SeExpr
seop.cpp
Go to the documentation of this file.
1/*
2 Copyright Disney Enterprises, Inc. All rights reserved.
3
4 Licensed under the Apache License, Version 2.0 (the "License");
5 you may not use this file except in compliance with the License
6 and the following modification to it: Section 6 Trademarks.
7 deleted and replaced with:
8
9 6. Trademarks. This License does not grant permission to use the
10 trade names, trademarks, service marks, or product names of the
11 Licensor and its affiliates, except as required for reproducing
12 the content of the NOTICE file.
13
14 You may obtain a copy of the License at
15 http://www.apache.org/licenses/LICENSE-2.0
16*/
17#include <RslPlugin.h>
18#include <rx.h>
19#include <map>
20#include <list>
21#include <pthread.h>
22#include <SeVec.h>
23#include <SeExpression.h>
24#include <SeExprFunc.h>
25#include <RixInterfaces.h>
26#include <cstdlib>
27#include <cstring>
28#include "immutable_hash_map.h"
29
30namespace {
31RixTokenStorage* tokenizer;
32inline const char* tokenize(const char* str) {
33 const char* result = tokenizer->GetToken(str);
34 return result;
35}
36
37int SeTokenize(RslContext* ctx, int argc, const RslArg* argv[]) {
38 RslStringIter result(argv[0]);
39 const char* str = *RslStringIter(argv[1]);
40 *result = (RtString)tokenize(str);
41 return 0;
42}
43
44struct SeRmanVarMap {
45 immutable_hash_map<const char*, int> indexMap;
46 std::vector<int> groupStarts;
47 std::vector<int> groupCounts;
48 std::vector<int> groupIndices;
49
50 SeRmanVarMap(int nvars, const char** varnames, int* varindices, int* groupstarts)
51 : indexMap(varnames, varindices, nvars), groupStarts(groupstarts, groupstarts + nvars), groupCounts(nvars),
52 groupIndices(nvars) {
53 int groupIndex = 0, i, j;
54 for (i = 0; i < nvars;) {
55 // determine size of current group
56 for (j = i + 1; j < nvars; j++) {
57 if (groupStarts[j] != groupStarts[i]) break;
58 }
59 int groupCount = j - i;
60 // assign group size and index to group members
61 for (; i < j; i++) {
62 groupCounts[i] = groupCount;
63 groupIndices[i] = groupIndex;
64 }
65 groupIndex++;
66 }
67
68 // for(size_t i=0;i<indexMap._keys.size();i++){
69 // const char* name=indexMap._keys[i]?indexMap._keys[i]:"";
70 // }
71 }
72};
73typedef std::map<const char*, SeRmanVarMap*> SeRmanVarMapMap;
74typedef std::map<const char*, int> SeRmanExprMap;
75typedef std::vector<int> SeVarBinding;
76
77class SeRmanExpr;
78
80struct ThreadData {
81
82 // rix message interface
83 RixMessages* msgs;
84
85 // all variable maps accessed by this thread
86 SeRmanVarMapMap varmaps;
87
88 SeRmanVarMap& getVarMap(const char* varMapHandle) {
89 SeRmanVarMap*& varmap = varmaps[varMapHandle];
90 if (!varmap) {
91 // parse var list and make a new varmap
92 char* varlist = strdup(varMapHandle);
93 std::vector<const char*> varnames;
94 std::vector<int> groupStarts;
95
96 // parse each var group (separated by spaces)
97 char* varlist_end = 0;
98 char* vargroup = strtok_r(varlist, " ", &varlist_end);
99 do {
100 // parse vars within var group (separated by commas)
101 int groupStart = varnames.size();
102 char* vargroup_end = 0;
103 char* var = strtok_r(vargroup, ",", &vargroup_end);
104 do {
105 varnames.push_back(tokenize(var));
106 groupStarts.push_back(groupStart);
107 } while ((var = strtok_r(0, ",", &vargroup_end)));
108 } while ((vargroup = strtok_r(0, " ", &varlist_end)));
109
110 // build new varmap
111 int nvars = varnames.size();
112 int* varindices = (int*)alloca(sizeof(int) * nvars);
113 for (int i = 0; i < nvars; i++) {
114 varindices[i] = i;
115 }
116 varmap = new SeRmanVarMap(nvars, &varnames[0], &varindices[0], &groupStarts[0]);
117 free(varlist);
118 }
119 return *varmap;
120 }
121
122 void ptError(char* fmt, ...) {
123 static char strbuf[1024];
124 va_list ap;
125 va_start(ap, fmt);
126 vsprintf(strbuf, fmt, ap); // TODO: this should use vsnprintf
127 msgs->Error("%s", strbuf);
128 va_end(ap);
129 }
130
131 void ptWarn(char* fmt, ...) {
132 static char strbuf[1024];
133 va_list ap;
134 va_start(ap, fmt);
135 vsprintf(strbuf, fmt, ap); // TODO: this should use vsnprintf
136 msgs->Warning("%s", strbuf);
137 va_end(ap);
138 }
139
140 SeRmanExprMap exprmap;
141 std::vector<SeRmanExpr*> exprs;
142
143 RtColor* varValues; // value of every var at current grid point
144 float* Ci; // current value of Ci
145
146 ThreadData() {
147 msgs = (RixMessages*)RxGetRixContext()->GetRixInterface(k_RixMessages);
148 exprs.push_back(0); // dummy entry; index 0 means uninitialized
149 }
150};
151
153ThreadData& getThreadData(RslContext* ctx) {
154 ThreadData* td = (ThreadData*)ctx->GetThreadData();
155 if (!td) {
156 td = new ThreadData;
157 ctx->SetThreadData(td);
158 }
159 return *td;
160}
161
163class SeRmanVar : public SeExprVarRef {
164 public:
165 SeRmanVar(ThreadData& td) : SeExprVarRef(SeExprType().FP(3).Varying()), td(td), index(0) {}
166 // SeRmanVar(ThreadData& td) : td(td), index(0) {}
167 virtual bool isVec() { return 1; } // treat all vars as vectors
168 void setIndex(int i) { index = i; }
169 virtual void eval(const SeExprVarNode* node, SeVec3d& result) {
170 RtColor& c = td.varValues[index];
171 result = c;
172 }
173
174 private:
175 ThreadData& td;
176 int index;
177};
178
180//$user::foo
181class AttrVar : public SeExprVectorVarRef {
182 std::string name;
183 SeVec3d value;
184
185 public:
186 AttrVar() : name(""), value(0.) {}
187
188 AttrVar(const std::string& nameIn) : value(0.) {
189 // change "::" to ":" (needed :: for parsing SE).
190 size_t pos = nameIn.find("::");
191 name = nameIn.substr(0, pos) + nameIn.substr(pos + 1, nameIn.size() - (pos - 1));
192 }
193
194 std::string getName() { return name; }
195
196 void doLookup() {
197 // make sure have enough space to hold result
198 float fbuf16[16];
199
200 RxInfoType_t rxType; // = RxInfoColor;
201 int count;
202 int statusOpt, statusAttr;
203
204 // try option first then attribute
205 statusOpt = RxOption(name.c_str(), fbuf16, sizeof(fbuf16), &rxType, &count);
206 statusAttr = RxAttribute(name.c_str(), fbuf16, sizeof(fbuf16), &rxType, &count);
207
208 if (statusAttr != 0 && statusOpt != 0) {
209 // no matches, go with default of 0
210 value.setValue(0.0, 0.0, 0.0);
211 return;
212 }
213
214 // found something
215 switch (rxType) {
216 case RxInfoFloat:
217 // promote float to color grey scale
218 value.setValue(fbuf16[0], fbuf16[0], fbuf16[0]);
219 break;
220 case RxInfoColor:
221 case RxInfoNormal:
222 case RxInfoVector:
223 case RxInfoPoint:
224 // any of the tuples will do for color
225 value.setValue(fbuf16[0], fbuf16[1], fbuf16[2]);
226 break;
227 default:
228 // not an expected match
229 // stderr << "SeRmanExpr: Unexpected type for Option/Attribute" << name.c_str() << rxType <<". Only
230 // Float or Color allowed.";
231 break;
232 }
233 }
234
235 // Implement the interface of SeExprVarRef
236 virtual void eval(const SeExprVarNode* node, SeVec3d& result) { result = value; }
237};
238
240class SeRmanExpr : public SeExpression {
241 public:
242 SeRmanExpr(const std::string& expr, ThreadData& td) : SeExpression(expr), _td(td), _boundVarMap(-1) {}
243
244 virtual SeExprVarRef* resolveVar(const std::string& name) const {
245 if (name.find("::") != std::string::npos) {
246 int i, size;
247 for (i = 0, size = _attrrefs.size(); i < size; i++)
248 // AttrVar attr = _attrrefs[i];
249 if (name == _attrrefs[i]->getName()) return _attrrefs[i];
250
251 // didn't match so make new
252 AttrVar* attrVar = new AttrVar(/*td,*/ name);
253 _attrrefs.push_back(attrVar);
254 return attrVar;
255 }
256
257 const char* token = tokenize(name.c_str());
258 for (int i = 0, size = _varrefs.size(); i < size; i++)
259 if (_varnames[i] == token) return _varrefs[i];
260 SeRmanVar* var = new SeRmanVar(_td);
261 _varnames.push_back(token);
262 _varrefs.push_back(var);
263 return var;
264 }
265
266 SeVarBinding* bindVars(const char* varMapHandle) {
267 SeVarBinding*& binding = _bindings[varMapHandle];
268 if (!binding) {
269 binding = new SeVarBinding;
270
271 // find varmap
272 SeRmanVarMap& varmap = _td.getVarMap(varMapHandle);
273 // bind varmap to expression
274 int nvars = _varnames.size();
275 binding->resize(nvars);
276 for (int i = 0; i < nvars; i++) {
277 const char* name = _varnames[i];
278 int index = varmap.indexMap[name];
279 if (!index) {
280 // for(int i=0;i<varmap.indexMap._keys.size();i++){
281 // std::cerr<<"key "<<i<<std::endl;
282 // const char* name=indexMap._keys[i]?indexMap._keys[i]:"";
283 // std::cerr<<" we have key "<<name<<" and val "<<indexMap._values[i]<<std::endl;
284 //}
285 char msg[] = "SeRmanExpr error: undefined variable \"$%s\"";
286 _td.ptError(msg, name);
287 }
288 (*binding)[i] = index;
289 }
290 }
291 _bindstack.push_back(binding);
292 return binding;
293 }
294
295 void lookupAttrs() {
296 int nattrs = _attrrefs.size();
297
298 // fill in attrs
299 for (int i = 0; i < nattrs; i++) {
300 _attrrefs[i]->doLookup();
301 }
302 }
303
304 void setVarIndices() {
305 // set the varref indices to the currently bound varmap
306 // note: we can't do this during bind because expression evals may be nested
307 SeVarBinding* binding = _bindstack.back();
308 if (binding) {
309 for (int i = 0, size = binding->size(); i < size; i++) _varrefs[i]->setIndex((*binding)[i]);
310 }
311 }
312
313 void unbindVars() { _bindstack.pop_back(); }
314
315 private:
316 mutable std::vector<const char*> _varnames; // ordered, unique list of var names
317 mutable std::vector<SeRmanVar*> _varrefs; // var refs corresponding to _varnames
318 mutable std::vector<AttrVar*> _attrrefs;
319 mutable std::map<const char*, SeVarBinding*> _bindings; // bindings for each varmap
320 mutable std::vector<SeVarBinding*> _bindstack; // stack of active bindings
321 ThreadData& _td;
322 int _boundVarMap;
323};
324
325void init(RixContext* ctx) {
326 tokenizer = (RixTokenStorage*)ctx->GetRixInterface(k_RixGlobalTokenData);
327
328 // temporarily unset the expr plugins path
329 char* plugins_ptr = getenv("SE_EXPR_PLUGINS");
330 std::string plugins;
331 if (plugins_ptr) {
332 plugins = plugins_ptr;
333 unsetenv("SE_EXPR_PLUGINS");
334 }
335
336 // and init the plugins explicitly (they're statically linked)
337 SeExprFunc::init();
338 // DefineSeExprPlugin (SeExprFunc::define);
339
340 // restore environment
341 if (plugins_ptr) setenv("SE_EXPR_PLUGINS", plugins.c_str(), 1);
342}
343
344int SeExprBind(RslContext* ctx, int argc, const RslArg* argv[]) {
345 RslFloatIter result(argv[0]);
346 const char* exprstr = *RslStringIter(argv[1]);
347 const char* varmapHandle = *RslStringIter(argv[2]);
348
349 if (!exprstr[0]) {
350 // expression is blank - just return null handle
351 *result = 0;
352 argv[3]->GetResizer()->Resize(0);
353 return 0;
354 }
355
356 // see if we have this expr already
357 ThreadData& td = getThreadData(ctx);
358 int& index = td.exprmap[exprstr];
359 if (!index) {
360 // parse expr (parser is not reentrant - use mutex)
361 static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
362 pthread_mutex_lock(&mutex);
363 SeRmanExpr* expr = new SeRmanExpr(exprstr, td);
364 bool valid = expr->isValid(); // triggers parse
365 pthread_mutex_unlock(&mutex);
366 if (!valid) {
367 char msg[] = "SeRmanExpr error: %s";
368 td.ptError(msg, expr->parseError().c_str());
369 index = 0;
370 } else
371 index = td.exprs.size();
372 // store expr object whether parse succeeded or not (so we don't parse again)
373 td.exprs.push_back(expr);
374 }
375
376 *result = index;
377 if (index) {
378 // bind vars only if we have a valid expr
379 SeRmanExpr* expr = td.exprs[index];
380 expr->lookupAttrs();
381 SeVarBinding& binding = *expr->bindVars(varmapHandle);
382 int nvars = binding.size();
383 argv[3]->GetResizer()->Resize(nvars);
384 float* varIndices = *RslFloatArrayIter(argv[3]);
385 for (int i = 0; i < nvars; i++) {
386 varIndices[i] = binding[i];
387 }
388 } else {
389 argv[3]->GetResizer()->Resize(0);
390 }
391 return 0;
392}
393
394int SeExprEval(RslContext* ctx, int argc, const RslArg* argv[]) {
395 int index = int(*RslFloatIter(argv[1]));
396 RslColorArrayIter varValuesIter = argv[2];
397 RslColorIter CiIter = argv[3];
398
399 int numVals = argv[3]->NumValues();
400
401 if (!index) {
402 for (int i = 0; i < numVals; i++, CiIter++, varValuesIter++) {
403 float* Ci = *CiIter;
404 Ci[0] = Ci[1] = Ci[2] = 0;
405 }
406 return 0;
407 }
408
409 ThreadData& td = getThreadData(ctx);
410
411 SeRmanExpr& expr = *td.exprs[index];
412 expr.setVarIndices();
413
414 bool isThreadSafe = expr.isThreadSafe();
415
416 for (int i = 0; i < numVals; i++, CiIter++, varValuesIter++) {
417 td.varValues = &varValuesIter[0];
418 float* Ci = td.Ci = *CiIter;
419
420 // expression evaluator is reentrant but functions may not be
421 static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
422 if (!isThreadSafe) pthread_mutex_lock(&mutex);
423
424 SeVec3d v = expr.evaluate();
425
426 if (!isThreadSafe) pthread_mutex_unlock(&mutex);
427
428 if (!isfinite(v[0]) || !isfinite(v[1]) || !isfinite(v[2])) {
429 char msg[] = "Shader Expression: %s: resulted in NAN. Setting val to 1";
430 td.ptWarn(msg, expr.getExpr().c_str());
431 v[0] = v[1] = v[2] = 1;
432 }
433
434 Ci[0] = v[0];
435 Ci[1] = v[1];
436 Ci[2] = v[2];
437 }
438
439 expr.unbindVars();
440 return 0;
441}
442}
443
444extern "C" {
445static RslFunction funcs[] = {
446 // SeTokenize(inputStr) -> tokenized string
447 {"string SeTokenize(string)", SeTokenize, NULL, NULL},
448
449 // SeExprBind(exprStr, varmap, varIndices[]) -> exprHandle
450 {"float SeExprBind(string, string, output uniform float[])", SeExprBind, NULL, NULL},
451
452 // SeExprEval(exprHandle, varValues, CsVal)
453 {"void SeExprEval(uniform float, color[], output color)", SeExprEval, NULL, NULL},
454 {NULL}};
455
456RslFunctionTable RslPublicFunctions(funcs, init);
457}
virtual void eval(ArgHandle args)
RslFunctionTable RslPublicFunctions(funcs, init)
static RslFunction funcs[]
Definition: seop.cpp:445
</pre >< h3 > Binding our variable reference</h3 > If we now tried to use the variable would still not be found by our expressions To make it bindable we need to override the resolveVar() function as follows</pre >< h3 > Variable setting</h3 > Next we need to make a way of setting the variable As the controlling code will use the expression it will repeatedly alternate between setting the independent variables that are used and calling evaluate(). What it has to do depends very much on the application. In this case we only need to set the independent variable x as</pre >< h2 > Evaluating expressions</h2 > Evaluating an expression is pretty easy But before we can do that we need to make an instance< pre > GrapherExpr expr("x+x^2")
</pre > Once we have this we need an instance to store our variable and provide a reference to that We make it because resolveVar() is const . One does not need to store a variable reference in a given expression. In fact
The result is computed int int< br >< div style="margin-left: 40px;"> Picks values randomly between loRange and hiRange based on supplied index(which is automatically hashed). &nbsp
For any rgb or hsl value(except for negative s values)