// // Copyright 2019 Pixar // // Licensed under the Apache License, Version 2.0 (the "Apache License") // with the following modification; you may not use this file except in // compliance with the Apache License and the following modification to it: // Section 6. Trademarks. is deleted and replaced with: // // 6. Trademarks. This License does not grant permission to use the trade // names, trademarks, service marks, or product names of the Licensor // and its affiliates, except as required to comply with Section 4(c) of // the License and to reproduce the content of the NOTICE file. // // You may obtain a copy of the Apache License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the Apache License with the above modification is // distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY // KIND, either express or implied. See the Apache License for the specific // language governing permissions and limitations under the Apache License. // %define api.pure %error-verbose %locations %parse-param { _VSCGParserData * data } %lex-param { void * MACRO_YYSCANNER } %{ #include #include #include #include #include #include #include #include "hdPrman/debugCodes.h" #include "hdPrman/matfiltResolveVstructs.h" #include "pxr/pxr.h" #include "pxr/usd/sdr/shaderNode.h" #include "pxr/usd/sdr/shaderProperty.h" #include "pxr/usd/sdr/registry.h" PXR_NAMESPACE_USING_DIRECTIVE // --------------------------------------------------------------------------- struct _VSCGConditionalBase { virtual ~_VSCGConditionalBase() = default; typedef std::shared_ptr<_VSCGConditionalBase> Ptr; virtual bool Eval(const MatfiltNode & node, const NdrTokenVec & shaderTypePriority) = 0; }; // --------------------------------------------------------------------------- namespace // anonymous { struct ConditionalParamBase : public _VSCGConditionalBase { ConditionalParamBase(const TfToken & name) : paramName(name) {} TfToken paramName; }; struct ConditionalParamIsConnected : public ConditionalParamBase { ConditionalParamIsConnected(const TfToken & name) : ConditionalParamBase(name) {} bool Eval(const MatfiltNode & node, const NdrTokenVec & shaderTypePriority) override { auto I = node.inputConnections.find(paramName); if (I == node.inputConnections.end()) { return false; } return !(*I).second.empty(); } }; struct ConditionalParamIsNotConnected : public ConditionalParamBase { ConditionalParamIsNotConnected(const TfToken & name) : ConditionalParamBase(name) {} bool Eval(const MatfiltNode & node, const NdrTokenVec & shaderTypePriority) override { auto I = node.inputConnections.find(paramName); if (I == node.inputConnections.end()) { return true; } return (*I).second.empty(); } }; struct ConditionalParamIsSet : public ConditionalParamBase { ConditionalParamIsSet(const TfToken & name) : ConditionalParamBase(name) {} bool Eval(const MatfiltNode & node, const NdrTokenVec & shaderTypePriority) override { return (node.parameters.find(paramName) != node.parameters.end()); } }; struct ConditionalParamIsNotSet : public ConditionalParamBase { ConditionalParamIsNotSet(const TfToken & name) : ConditionalParamBase(name) {} bool Eval(const MatfiltNode & node, const NdrTokenVec & shaderTypePriority) override { return (node.parameters.find(paramName) == node.parameters.end()); } }; struct ConditionalParamCmpBase : public ConditionalParamBase { ConditionalParamCmpBase(const TfToken & name, const VtValue & v) : ConditionalParamBase(name) , value(v) {} VtValue value; static bool ValueAsNumber(const VtValue v, double * result) { if (v.IsHolding()) { *result = v.UncheckedGet(); return true; } if (v.IsHolding()) { *result = v.UncheckedGet(); return true; } if (v.IsHolding()) { *result = v.UncheckedGet(); return true; } if (v.IsHolding()) { *result = v.UncheckedGet(); return true; } // TODO check color? return false; } static bool GetParameterValue( const MatfiltNode & node, const TfToken & paramName, const NdrTokenVec & shaderTypePriority, VtValue * result) { auto I = node.parameters.find(paramName); if (I != node.parameters.end()) { *result = (*I).second; return true; } // check for a default auto& reg = SdrRegistry::GetInstance(); if (SdrShaderNodeConstPtr sdrNode = reg.GetShaderNodeByIdentifier( node.nodeTypeId, shaderTypePriority)) { if (NdrPropertyConstPtr ndrProp = sdrNode->GetInput(paramName)) { *result = ndrProp->GetDefaultValue(); return true; } } return false; } virtual bool CompareNumber(double v1, double v2) = 0; virtual bool CompareString( const std::string & v1, const std::string & v2) = 0; bool Eval(const MatfiltNode & node, const NdrTokenVec & shaderTypePriority) override { VtValue paramValue; if (!GetParameterValue( node, paramName, shaderTypePriority, ¶mValue)) { return false; } if (value.IsHolding()) { if (paramValue.IsHolding()) { return CompareString(value.UncheckedGet(), paramValue.UncheckedGet()); } return false; } double d1 = 0, d2 = 0; if (ValueAsNumber(paramValue, &d1) && ValueAsNumber(value, &d2)) { //std::cerr << d1 << " " << d2 << std::endl; return CompareNumber(d1, d2); } return false; } }; struct ConditionalParamCmpEqualTo : public ConditionalParamCmpBase { ConditionalParamCmpEqualTo(const TfToken & name, const VtValue & v) : ConditionalParamCmpBase(name, v) {} bool CompareNumber(double v1, double v2) override { return v1 == v2; } bool CompareString(const std::string & v1, const std::string & v2) override { return v1 == v2; } }; struct ConditionalParamCmpNotEqualTo : public ConditionalParamCmpBase { ConditionalParamCmpNotEqualTo(const TfToken & name, const VtValue & v) : ConditionalParamCmpBase(name, v) {} bool CompareNumber(double v1, double v2) override { return v1 != v2; } bool CompareString(const std::string & v1, const std::string & v2) override { return v1 != v2; } }; struct ConditionalParamCmpGreaterThan : public ConditionalParamCmpBase { ConditionalParamCmpGreaterThan(const TfToken & name, const VtValue & v) : ConditionalParamCmpBase(name, v) { } bool CompareNumber(double v1, double v2) override { return v1 > v2; } bool CompareString(const std::string & v1, const std::string & v2) override { return false; } }; struct ConditionalParamCmpLessThan : public ConditionalParamCmpBase { ConditionalParamCmpLessThan(const TfToken & name, const VtValue & v) : ConditionalParamCmpBase(name, v) {} bool CompareNumber(double v1, double v2) override { return v1 < v2; } bool CompareString(const std::string & v1, const std::string & v2) override { return false; } }; struct ConditionalParamCmpGreaterThanOrEqualTo : public ConditionalParamCmpBase { ConditionalParamCmpGreaterThanOrEqualTo( const TfToken & name, const VtValue & v) : ConditionalParamCmpBase(name, v) {} bool CompareNumber(double v1, double v2) override { return v1 >= v2; } bool CompareString(const std::string & v1, const std::string & v2) override { return false; } }; struct ConditionalParamCmpLessThanOrEqualTo : public ConditionalParamCmpBase { ConditionalParamCmpLessThanOrEqualTo( const TfToken & name, const VtValue & v) : ConditionalParamCmpBase(name, v) {} bool CompareNumber(double v1, double v2) override { return v1 <= v2; } bool CompareString(const std::string & v1, const std::string & v2) override { return false; } }; struct ConditionalAnd : _VSCGConditionalBase { ConditionalAnd(_VSCGConditionalBase * left, _VSCGConditionalBase * right) : _VSCGConditionalBase(), left(left), right(right) {} bool Eval(const MatfiltNode & node, const NdrTokenVec & shaderTypePriority) override { return (left && left->Eval(node, shaderTypePriority)) && (right && right->Eval(node, shaderTypePriority)); } ~ConditionalAnd() { delete left; delete right; } _VSCGConditionalBase * left; _VSCGConditionalBase * right; }; struct ConditionalOr : _VSCGConditionalBase { ConditionalOr(_VSCGConditionalBase * left, _VSCGConditionalBase * right) : _VSCGConditionalBase(), left(left), right(right) {} ~ConditionalOr() { delete left; delete right; } bool Eval(const MatfiltNode & node, const NdrTokenVec & shaderTypePriority) override { return (left && left->Eval(node, shaderTypePriority)) || (right && right->Eval(node, shaderTypePriority)); } _VSCGConditionalBase * left; _VSCGConditionalBase * right; }; //---------------------------------------------------------------------------- } // namespace anonymous struct _VSCGValueContainer { _VSCGValueContainer(const VtValue & v) : value(v) {} VtValue value; }; struct _VSCGAction { enum Action { Connect, Ignore, SetConstant, CopyParam, }; _VSCGAction(Action action) : action(action) {} _VSCGAction(Action action, const VtValue & value) : action(action), value(value) {} Action action; VtValue value; }; struct _VSCGParserData { // flex scanner void * yyscanner; std::string parseError; _VSCGAction * action = nullptr; _VSCGAction * fallbackAction = nullptr; std::set<_VSCGConditionalBase *> _intermediateConditions; _VSCGConditionalBase * NewCondition(_VSCGConditionalBase * condition) { _intermediateConditions.insert(condition); return condition; } _VSCGConditionalBase * ClaimCondition(_VSCGConditionalBase * condition) { _intermediateConditions.erase(condition); return condition; } _VSCGConditionalBase * rootCondition = nullptr; ~_VSCGParserData() { delete action; delete fallbackAction; for (auto c : _intermediateConditions) { delete c; } _intermediateConditions.clear(); } }; PXR_NAMESPACE_OPEN_SCOPE class MatfiltVstructConditionalEvaluatorImpl { public: ~MatfiltVstructConditionalEvaluatorImpl() { delete condition; delete action; delete fallbackAction; } _VSCGConditionalBase * condition = nullptr; _VSCGAction * action = nullptr; _VSCGAction * fallbackAction = nullptr; }; PXR_NAMESPACE_CLOSE_SCOPE #include "virtualStructConditionalGrammar.tab.h" %} %union { double number; char * string; struct _VSCGConditionalBase * condition; struct _VSCGAction * action; struct _VSCGValueContainer * value; }; %destructor { if ($$) { free($$); } $$ = 0; } %destructor { if ($$) { delete $$; } $$ = 0; } %destructor { if ($$) { delete $$; } $$ = 0; } %{ int virtualStructConditionalGrammarYylex( YYSTYPE * lvalp, YYLTYPE *llocp, void * yyscanner); void virtualStructConditionalGrammarYyerror( YYLTYPE *llocp, _VSCGParserData * data, const char *s); /*This macro is to hack around the fact that %lex-param ends up getting cut off, for example: %lex-param { void * data->yyscanner} Really ends up passing in yyscanner in the YYLEX macro */ #define MACRO_YYSCANNER data->yyscanner %} %start statement %token NUMBER %token STRING %token PARAM %token LPAR RPAR %token OP_EQ OP_NOTEQ %token OP_GT OP_LT OP_GTEQ OP_LTEQ %token OP_IS OP_ISNOT %token OP_AND OP_OR %token KW_IF KW_ELSE KW_CONNECTED KW_CONNECT KW_IGNORE KW_COPY KW_SET %token UNRECOGNIZED_TOKEN %type value %type expr statement %type action %left OP_OR %left OP_AND %% value : STRING { $$ = new _VSCGValueContainer(VtValue($1)); free($1); } | NUMBER { $$ = new _VSCGValueContainer(VtValue(atof($1))); free($1); } // op : OP_EQ {data->curEq = "paramEqualTo";} // | OP_NOTEQ {data->curEq = "paramNotEqualTo";} // | OP_GT {data->curEq = "paramGreaterThan";} // | OP_LT {data->curEq = "paramLessThan";} // | OP_GTEQ {data->curEq = "paramGreaterThanOrEqualTo";} // | OP_LTEQ {data->curEq = "paramLessThanOrEqualTo";} // TODO, operators expr: PARAM OP_EQ value { $$ = data->NewCondition( new ConditionalParamCmpEqualTo(TfToken($1), $3->value)); free($1); delete $3; } | OP_AND OP_EQ value { /* PARAM named 'and', special case */ $$ = data->NewCondition( new ConditionalParamCmpEqualTo(TfToken("and"), $3->value)); delete $3; } | OP_OR OP_EQ value { /* PARAM named 'or', special case */ $$ = data->NewCondition( new ConditionalParamCmpEqualTo(TfToken("or"), $3->value)); delete $3; } | OP_IS OP_EQ value { /* PARAM named 'is', special case */ $$ = data->NewCondition( new ConditionalParamCmpEqualTo(TfToken("is"), $3->value)); delete $3; } | KW_IF OP_EQ value { /* PARAM named 'if', special case */ $$ = data->NewCondition( new ConditionalParamCmpEqualTo(TfToken("if"), $3->value)); delete $3; } | KW_ELSE OP_EQ value { /* PARAM named 'else', special case */ $$ = data->NewCondition( new ConditionalParamCmpEqualTo(TfToken("else"), $3->value)); delete $3; } | KW_CONNECTED OP_EQ value { /* PARAM named 'connected', special case */ $$ = data->NewCondition( new ConditionalParamCmpEqualTo(TfToken("connected"), $3->value)); delete $3; } | KW_CONNECT OP_EQ value { /* PARAM named 'connect', special case */ $$ = data->NewCondition( new ConditionalParamCmpEqualTo(TfToken("connect"), $3->value)); delete $3; } | KW_IGNORE OP_EQ value { /* PARAM named 'ignore', special case */ $$ = data->NewCondition( new ConditionalParamCmpEqualTo(TfToken("ignore"), $3->value)); delete $3; } | KW_COPY OP_EQ value { /* PARAM named 'copy', special case */ $$ = data->NewCondition( new ConditionalParamCmpEqualTo(TfToken("copy"), $3->value)); delete $3; } | KW_SET OP_EQ value { /* PARAM named 'set', special case */ $$ = data->NewCondition( new ConditionalParamCmpEqualTo(TfToken("set"), $3->value)); delete $3; } //--- NOTEQ | PARAM OP_NOTEQ value { $$ = data->NewCondition( new ConditionalParamCmpNotEqualTo(TfToken($1), $3->value)); free($1); delete $3; } | OP_AND OP_NOTEQ value { /* PARAM named 'and', special case */ $$ = data->NewCondition( new ConditionalParamCmpNotEqualTo(TfToken("and"), $3->value)); delete $3; } | OP_OR OP_NOTEQ value { /* PARAM named 'or', special case */ $$ = data->NewCondition( new ConditionalParamCmpNotEqualTo(TfToken("or"), $3->value)); delete $3; } | OP_IS OP_NOTEQ value { /* PARAM named 'is', special case */ $$ = data->NewCondition( new ConditionalParamCmpNotEqualTo(TfToken("is"), $3->value)); delete $3; } | KW_IF OP_NOTEQ value { /* PARAM named 'if', special case */ $$ = data->NewCondition( new ConditionalParamCmpNotEqualTo(TfToken("if"), $3->value)); delete $3; } | KW_ELSE OP_NOTEQ value { /* PARAM named 'else', special case */ $$ = data->NewCondition( new ConditionalParamCmpNotEqualTo(TfToken("else"), $3->value)); delete $3; } | KW_CONNECTED OP_NOTEQ value { /* PARAM named 'connected', special case */ $$ = data->NewCondition( new ConditionalParamCmpNotEqualTo( TfToken("connected"), $3->value)); delete $3; } | KW_CONNECT OP_NOTEQ value { /* PARAM named 'connect', special case */ $$ = data->NewCondition( new ConditionalParamCmpNotEqualTo(TfToken("connect"), $3->value)); delete $3; } | KW_IGNORE OP_NOTEQ value { /* PARAM named 'ignore', special case */ $$ = data->NewCondition( new ConditionalParamCmpNotEqualTo(TfToken("ignore"), $3->value)); delete $3; } | KW_COPY OP_NOTEQ value { /* PARAM named 'copy', special case */ $$ = data->NewCondition( new ConditionalParamCmpNotEqualTo(TfToken("copy"), $3->value)); delete $3; } | KW_SET OP_NOTEQ value { /* PARAM named 'set', special case */ $$ = data->NewCondition( new ConditionalParamCmpNotEqualTo(TfToken("set"), $3->value)); delete $3; } //--- GT | PARAM OP_GT value { $$ = data->NewCondition( new ConditionalParamCmpGreaterThan(TfToken($1), $3->value)); free($1); delete $3; } | OP_AND OP_GT value { /* PARAM named 'and', special case */ $$ = data->NewCondition( new ConditionalParamCmpGreaterThan(TfToken("and"), $3->value)); delete $3; } | OP_OR OP_GT value { /* PARAM named 'or', special case */ $$ = data->NewCondition( new ConditionalParamCmpGreaterThan(TfToken("or"), $3->value)); delete $3; } | OP_IS OP_GT value { /* PARAM named 'is', special case */ $$ = data->NewCondition( new ConditionalParamCmpGreaterThan(TfToken("is"), $3->value)); delete $3; } | KW_IF OP_GT value { /* PARAM named 'if', special case */ $$ = data->NewCondition( new ConditionalParamCmpGreaterThan(TfToken("if"), $3->value)); delete $3; } | KW_ELSE OP_GT value { /* PARAM named 'else', special case */ $$ = data->NewCondition( new ConditionalParamCmpGreaterThan(TfToken("else"), $3->value)); delete $3; } | KW_CONNECTED OP_GT value { /* PARAM named 'connected', special case */ $$ = data->NewCondition( new ConditionalParamCmpGreaterThan( TfToken("connected"), $3->value)); delete $3; } | KW_CONNECT OP_GT value { /* PARAM named 'connect', special case */ $$ = data->NewCondition( new ConditionalParamCmpGreaterThan(TfToken("connect"), $3->value)); delete $3; } | KW_IGNORE OP_GT value { /* PARAM named 'ignore', special case */ $$ = data->NewCondition( new ConditionalParamCmpGreaterThan(TfToken("ignore"), $3->value)); delete $3; } | KW_COPY OP_GT value { /* PARAM named 'copy', special case */ $$ = data->NewCondition( new ConditionalParamCmpGreaterThan(TfToken("copy"), $3->value)); delete $3; } | KW_SET OP_GT value { /* PARAM named 'set', special case */ $$ = data->NewCondition( new ConditionalParamCmpGreaterThan(TfToken("set"), $3->value)); delete $3; } //--- LT | PARAM OP_LT value { $$ = data->NewCondition( new ConditionalParamCmpLessThan(TfToken($1), $3->value)); free($1); delete $3; } | OP_AND OP_LT value { /* PARAM named 'and', special case */ $$ = data->NewCondition( new ConditionalParamCmpLessThan(TfToken("and"), $3->value)); delete $3; } | OP_OR OP_LT value { /* PARAM named 'or', special case */ $$ = data->NewCondition( new ConditionalParamCmpLessThan(TfToken("or"), $3->value)); delete $3; } | OP_IS OP_LT value { /* PARAM named 'is', special case */ $$ = data->NewCondition( new ConditionalParamCmpLessThan(TfToken("is"), $3->value)); delete $3; } | KW_IF OP_LT value { /* PARAM named 'if', special case */ $$ = data->NewCondition( new ConditionalParamCmpLessThan(TfToken("if"), $3->value)); delete $3; } | KW_ELSE OP_LT value { /* PARAM named 'else', special case */ $$ = data->NewCondition( new ConditionalParamCmpLessThan(TfToken("else"), $3->value)); delete $3; } | KW_CONNECTED OP_LT value { /* PARAM named 'connected', special case */ $$ = data->NewCondition( new ConditionalParamCmpLessThan(TfToken("connected"), $3->value)); delete $3; } | KW_CONNECT OP_LT value { /* PARAM named 'connect', special case */ $$ = data->NewCondition( new ConditionalParamCmpLessThan(TfToken("connect"), $3->value)); delete $3; } | KW_IGNORE OP_LT value { /* PARAM named 'ignore', special case */ $$ = data->NewCondition( new ConditionalParamCmpLessThan(TfToken("ignore"), $3->value)); delete $3; } | KW_COPY OP_LT value { /* PARAM named 'copy', special case */ $$ = data->NewCondition( new ConditionalParamCmpLessThan(TfToken("copy"), $3->value)); delete $3; } | KW_SET OP_LT value { /* PARAM named 'set', special case */ $$ = data->NewCondition( new ConditionalParamCmpLessThan(TfToken("set"), $3->value)); delete $3; } //--- GTEQ | PARAM OP_GTEQ value { $$ = data->NewCondition( new ConditionalParamCmpGreaterThanOrEqualTo(TfToken($1), $3->value)); free($1); delete $3; } | OP_AND OP_GTEQ value { /* PARAM named 'and', special case */ $$ = data->NewCondition( new ConditionalParamCmpGreaterThanOrEqualTo(TfToken("and"), $3->value)); delete $3; } | OP_OR OP_GTEQ value { /* PARAM named 'or', special case */ $$ = data->NewCondition( new ConditionalParamCmpGreaterThanOrEqualTo(TfToken("or"), $3->value)); delete $3; } | OP_IS OP_GTEQ value { /* PARAM named 'is', special case */ $$ = data->NewCondition( new ConditionalParamCmpGreaterThanOrEqualTo(TfToken("is"), $3->value)); delete $3; } | KW_IF OP_GTEQ value { /* PARAM named 'if', special case */ $$ = data->NewCondition( new ConditionalParamCmpGreaterThanOrEqualTo(TfToken("if"), $3->value)); delete $3; } | KW_ELSE OP_GTEQ value { /* PARAM named 'else', special case */ $$ = data->NewCondition( new ConditionalParamCmpGreaterThanOrEqualTo(TfToken("else"), $3->value)); delete $3; } | KW_CONNECTED OP_GTEQ value { /* PARAM named 'connected', special case */ $$ = data->NewCondition( new ConditionalParamCmpGreaterThanOrEqualTo(TfToken("connected"), $3->value)); delete $3; } | KW_CONNECT OP_GTEQ value { /* PARAM named 'connect', special case */ $$ = data->NewCondition( new ConditionalParamCmpGreaterThanOrEqualTo(TfToken("connect"), $3->value)); delete $3; } | KW_IGNORE OP_GTEQ value { /* PARAM named 'ignore', special case */ $$ = data->NewCondition( new ConditionalParamCmpGreaterThanOrEqualTo(TfToken("ignore"), $3->value)); delete $3; } | KW_COPY OP_GTEQ value { /* PARAM named 'copy', special case */ $$ = data->NewCondition( new ConditionalParamCmpGreaterThanOrEqualTo(TfToken("copy"), $3->value)); delete $3; } | KW_SET OP_GTEQ value { /* PARAM named 'set', special case */ $$ = data->NewCondition( new ConditionalParamCmpGreaterThanOrEqualTo(TfToken("set"), $3->value)); delete $3; } //--- LTEQ | PARAM OP_LTEQ value { $$ = data->NewCondition( new ConditionalParamCmpLessThanOrEqualTo(TfToken($1), $3->value)); free($1); delete $3; } | OP_AND OP_LTEQ value { /* PARAM named 'and', special case */ $$ = data->NewCondition( new ConditionalParamCmpLessThanOrEqualTo(TfToken("and"), $3->value)); delete $3; } | OP_OR OP_LTEQ value { /* PARAM named 'or', special case */ $$ = data->NewCondition( new ConditionalParamCmpLessThanOrEqualTo(TfToken("or"), $3->value)); delete $3; } | OP_IS OP_LTEQ value { /* PARAM named 'is', special case */ $$ = data->NewCondition( new ConditionalParamCmpLessThanOrEqualTo(TfToken("is"), $3->value)); delete $3; } | KW_IF OP_LTEQ value { /* PARAM named 'if', special case */ $$ = data->NewCondition( new ConditionalParamCmpLessThanOrEqualTo(TfToken("if"), $3->value)); delete $3; } | KW_ELSE OP_LTEQ value { /* PARAM named 'else', special case */ $$ = data->NewCondition( new ConditionalParamCmpLessThanOrEqualTo(TfToken("else"), $3->value)); delete $3; } | KW_CONNECTED OP_LTEQ value { /* PARAM named 'connected', special case */ $$ = data->NewCondition( new ConditionalParamCmpLessThanOrEqualTo(TfToken("connected"), $3->value)); delete $3; } | KW_CONNECT OP_LTEQ value { /* PARAM named 'connect', special case */ $$ = data->NewCondition( new ConditionalParamCmpLessThanOrEqualTo(TfToken("connect"), $3->value)); delete $3; } | KW_IGNORE OP_LTEQ value { /* PARAM named 'ignore', special case */ $$ = data->NewCondition( new ConditionalParamCmpLessThanOrEqualTo(TfToken("ignore"), $3->value)); delete $3; } | KW_COPY OP_LTEQ value { /* PARAM named 'copy', special case */ $$ = data->NewCondition( new ConditionalParamCmpLessThanOrEqualTo(TfToken("copy"), $3->value)); delete $3; } | KW_SET OP_LTEQ value { /* PARAM named 'set', special case */ $$ = data->NewCondition( new ConditionalParamCmpLessThanOrEqualTo(TfToken("set"), $3->value)); delete $3; } //--- ISCONNECTED | PARAM OP_IS KW_CONNECTED { $$ = data->NewCondition(new ConditionalParamIsConnected(TfToken($1))); free($1); } | OP_AND OP_IS KW_CONNECTED { /* PARAM named 'and', special case */ $$ = data->NewCondition(new ConditionalParamIsConnected(TfToken("and"))); } | OP_OR OP_IS KW_CONNECTED { /* PARAM named 'or', special case */ $$ = data->NewCondition(new ConditionalParamIsConnected(TfToken("or"))); } | OP_IS OP_IS KW_CONNECTED { /* PARAM named 'is', special case */ $$ = data->NewCondition(new ConditionalParamIsConnected(TfToken("is"))); } | KW_IF OP_IS KW_CONNECTED { /* PARAM named 'if', special case */ $$ = data->NewCondition(new ConditionalParamIsConnected(TfToken("if"))); } | KW_ELSE OP_IS KW_CONNECTED { /* PARAM named 'else', special case */ $$ = data->NewCondition(new ConditionalParamIsConnected(TfToken("else"))); } | KW_CONNECTED OP_IS KW_CONNECTED { /* PARAM named 'connected', special case */ $$ = data->NewCondition(new ConditionalParamIsConnected( TfToken("connected"))); } | KW_CONNECT OP_IS KW_CONNECTED { /* PARAM named 'connect', special case */ $$ = data->NewCondition(new ConditionalParamIsConnected( TfToken("connect"))); } | KW_IGNORE OP_IS KW_CONNECTED { /* PARAM named 'ignore', special case */ $$ = data->NewCondition(new ConditionalParamIsConnected( TfToken("ignore"))); } | KW_COPY OP_IS KW_CONNECTED { /* PARAM named 'copy', special case */ $$ = data->NewCondition(new ConditionalParamIsConnected(TfToken("copy"))); } | KW_SET OP_IS KW_CONNECTED { /* PARAM named 'set', special case */ $$ = data->NewCondition(new ConditionalParamIsConnected(TfToken("set"))); } //--- IS NOT CONNECTED | PARAM OP_ISNOT KW_CONNECTED { $$ = data->NewCondition(new ConditionalParamIsNotConnected(TfToken($1))); free($1); } | OP_AND OP_ISNOT KW_CONNECTED { /* PARAM named 'and', special case */ $$ = data->NewCondition(new ConditionalParamIsNotConnected( TfToken("and"))); } | OP_OR OP_ISNOT KW_CONNECTED { /* PARAM named 'or', special case */ $$ = data->NewCondition(new ConditionalParamIsNotConnected( TfToken("or"))); } | OP_IS OP_ISNOT KW_CONNECTED { /* PARAM named 'is', special case */ $$ = data->NewCondition(new ConditionalParamIsNotConnected( TfToken("is"))); } | KW_IF OP_ISNOT KW_CONNECTED { /* PARAM named 'if', special case */ $$ = data->NewCondition(new ConditionalParamIsNotConnected( TfToken("if"))); } | KW_ELSE OP_ISNOT KW_CONNECTED { /* PARAM named 'else', special case */ $$ = data->NewCondition(new ConditionalParamIsNotConnected( TfToken("else"))); } | KW_CONNECTED OP_ISNOT KW_CONNECTED { /* PARAM named 'connected', special case */ $$ = data->NewCondition(new ConditionalParamIsNotConnected( TfToken("connected"))); } | KW_CONNECT OP_ISNOT KW_CONNECTED { /* PARAM named 'connect', special case */ $$ = data->NewCondition(new ConditionalParamIsNotConnected( TfToken("connect"))); } | KW_IGNORE OP_ISNOT KW_CONNECTED { /* PARAM named 'ignore', special case */ $$ = data->NewCondition(new ConditionalParamIsNotConnected( TfToken("ignore"))); } | KW_COPY OP_ISNOT KW_CONNECTED { /* PARAM named 'copy', special case */ $$ = data->NewCondition(new ConditionalParamIsNotConnected( TfToken("copy"))); } | KW_SET OP_ISNOT KW_CONNECTED { /* PARAM named 'set', special case */ $$ = data->NewCondition(new ConditionalParamIsNotConnected( TfToken("set"))); } //--- IS SET | PARAM OP_IS KW_SET { $$ = data->NewCondition(new ConditionalParamIsSet(TfToken($1))); free($1); } | OP_AND OP_IS KW_SET { /* PARAM named 'and', special case */ $$ = data->NewCondition(new ConditionalParamIsSet(TfToken("and"))); } | OP_OR OP_IS KW_SET { /* PARAM named 'or', special case */ $$ = data->NewCondition(new ConditionalParamIsSet(TfToken("or"))); } | OP_IS OP_IS KW_SET { /* PARAM named 'is', special case */ $$ = data->NewCondition(new ConditionalParamIsSet(TfToken("is"))); } | KW_IF OP_IS KW_SET { /* PARAM named 'if', special case */ $$ = data->NewCondition(new ConditionalParamIsSet(TfToken("if"))); } | KW_ELSE OP_IS KW_SET { /* PARAM named 'else', special case */ $$ = data->NewCondition(new ConditionalParamIsSet(TfToken("else"))); } | KW_CONNECTED OP_IS KW_SET { /* PARAM named 'connected', special case */ $$ = data->NewCondition(new ConditionalParamIsSet(TfToken("connected"))); } | KW_CONNECT OP_IS KW_SET { /* PARAM named 'connect', special case */ $$ = data->NewCondition(new ConditionalParamIsSet(TfToken("connect"))); } | KW_IGNORE OP_IS KW_SET { /* PARAM named 'ignore', special case */ $$ = data->NewCondition(new ConditionalParamIsSet(TfToken("ignore"))); } | KW_COPY OP_IS KW_SET { /* PARAM named 'copy', special case */ $$ = data->NewCondition(new ConditionalParamIsSet(TfToken("copy"))); } | KW_SET OP_IS KW_SET { /* PARAM named 'set', special case */ $$ = data->NewCondition(new ConditionalParamIsSet(TfToken("set"))); } //--- IS NOT SET | PARAM OP_ISNOT KW_SET { $$ = data->NewCondition(new ConditionalParamIsNotSet(TfToken($1))); free($1); } | OP_AND OP_ISNOT KW_SET { /* PARAM named 'and', special case */ $$ = data->NewCondition(new ConditionalParamIsNotSet(TfToken("and"))); } | OP_OR OP_ISNOT KW_SET { /* PARAM named 'or', special case */ $$ = data->NewCondition(new ConditionalParamIsNotSet(TfToken("or"))); } | OP_IS OP_ISNOT KW_SET { /* PARAM named 'is', special case */ $$ = data->NewCondition(new ConditionalParamIsNotSet(TfToken("is"))); } | KW_IF OP_ISNOT KW_SET { /* PARAM named 'if', special case */ $$ = data->NewCondition(new ConditionalParamIsNotSet(TfToken("if"))); } | KW_ELSE OP_ISNOT KW_SET { /* PARAM named 'else', special case */ $$ = data->NewCondition(new ConditionalParamIsNotSet(TfToken("else"))); } | KW_CONNECTED OP_ISNOT KW_SET { /* PARAM named 'connected', special case */ $$ = data->NewCondition(new ConditionalParamIsNotSet( TfToken("connected"))); } | KW_CONNECT OP_ISNOT KW_SET { /* PARAM named 'connect', special case */ $$ = data->NewCondition(new ConditionalParamIsNotSet(TfToken("connect"))); } | KW_IGNORE OP_ISNOT KW_SET { /* PARAM named 'ignore', special case */ $$ = data->NewCondition(new ConditionalParamIsNotSet(TfToken("ignore"))); } | KW_COPY OP_ISNOT KW_SET { /* PARAM named 'copy', special case */ $$ = data->NewCondition(new ConditionalParamIsNotSet(TfToken("copy"))); } | KW_SET OP_ISNOT KW_SET { /* PARAM named 'set', special case */ $$ = data->NewCondition(new ConditionalParamIsNotSet(TfToken("set"))); } | LPAR expr RPAR { $$ = $2; } | expr OP_AND expr { $$ = data->NewCondition(new ConditionalAnd( data->ClaimCondition($1), data->ClaimCondition($3))); } | expr OP_OR expr { $$ = data->NewCondition(new ConditionalOr( data->ClaimCondition($1), data->ClaimCondition($3))); } action : KW_COPY PARAM { TfToken paramName($2); free($2); $$ = new _VSCGAction(_VSCGAction::CopyParam, VtValue(paramName)); } | KW_CONNECT { $$ = new _VSCGAction(_VSCGAction::Connect); } | KW_IGNORE { $$ = new _VSCGAction(_VSCGAction::Ignore); } | KW_SET STRING { std::string value($2); free($2); $$ = new _VSCGAction(_VSCGAction::SetConstant, VtValue(value)); } | KW_SET NUMBER { $$ = new _VSCGAction(_VSCGAction::SetConstant, VtValue(atof($2))); free($2); } statement : action KW_IF expr KW_ELSE action { data->action = $1; data->rootCondition = data->ClaimCondition($3); data->fallbackAction = $5; } | action KW_IF expr { data->action = $1; data->rootCondition = data->ClaimCondition($3); if ($1->action == _VSCGAction::Ignore) { data->fallbackAction = new _VSCGAction(_VSCGAction::Connect); } else { data->fallbackAction = new _VSCGAction(_VSCGAction::Ignore); } } | KW_IF expr KW_ELSE action { data->action = new _VSCGAction(_VSCGAction::Connect); data->rootCondition = data->ClaimCondition($2); data->fallbackAction = $4; } | action { data->action = $1; } | expr { data->action = new _VSCGAction(_VSCGAction::Connect); data->rootCondition = data->ClaimCondition($1); data->fallbackAction = new _VSCGAction(_VSCGAction::Ignore); } %% /* Called by yyparse on error */ void virtualStructConditionalGrammarYyerror( YYLTYPE *llocp, _VSCGParserData * data, const char *s) { data->parseError = s; } typedef struct yy_buffer_state * YY_BUFFER_STATE; YY_BUFFER_STATE virtualStructConditionalGrammarYy_scan_string( const char *, void * ); void virtualStructConditionalGrammarYy_delete_buffer( YY_BUFFER_STATE, void * ); int virtualStructConditionalGrammarYylex_init (void ** yyscanner); int virtualStructConditionalGrammarYylex_destroy (void * yyscanner); //---------------------------------------------------------------------------- MatfiltVstructConditionalEvaluator::Ptr MatfiltVstructConditionalEvaluator::Parse(const std::string & inputExpr) { _VSCGParserData data; if (virtualStructConditionalGrammarYylex_init(&data.yyscanner)) { TF_CODING_ERROR("_VSCGParser: error initializing scanner"); return nullptr; } YY_BUFFER_STATE bufferstate = virtualStructConditionalGrammarYy_scan_string( inputExpr.c_str(), data.yyscanner); MatfiltVstructConditionalEvaluator::Ptr result( new MatfiltVstructConditionalEvaluator); if (virtualStructConditionalGrammarYyparse(&data) == 0) { //hintsAttr = data.getHints(prefix, secondaryPrefix); result->_impl = new MatfiltVstructConditionalEvaluatorImpl; result->_impl->condition = data.rootCondition; result->_impl->action = data.action; result->_impl->fallbackAction = data.fallbackAction; data.rootCondition = nullptr; data.action = nullptr; data.fallbackAction = nullptr; } else { TF_CODING_ERROR("_VSCGParser: Error parsing '%s'", inputExpr.c_str()); } if (!data.parseError.empty()) { TF_CODING_ERROR("_VSCGParser: Error parsing '%s': %s", inputExpr.c_str(), data.parseError.c_str()); } virtualStructConditionalGrammarYy_delete_buffer( bufferstate, data.yyscanner); virtualStructConditionalGrammarYylex_destroy(data.yyscanner); // TODO return result; } void MatfiltVstructConditionalEvaluator::Evaluate( const SdfPath & nodeId, const TfToken & nodeInput, const SdfPath & upstreamNodeId, const TfToken & upstreamNodeOutput, const NdrTokenVec & shaderTypePriority, MatfiltNetwork & network) const { if (!_impl) { TF_CODING_ERROR("MatfiltVstructConditionalEvaluator: No impl"); return; } // Find node auto I = network.nodes.find(nodeId); if (I == network.nodes.end()) { TF_CODING_ERROR("MatfiltVstructConditionalEvaluator: Cannot eval " "for node %s; not found in network", nodeId.GetText()); return; } MatfiltNode & node = (*I).second; // if it's already connected explicitly, don't do anything if (node.inputConnections.find(nodeInput) != node.inputConnections.end()) { return; } _VSCGAction * chosenAction = nullptr; // Get upstream node. auto I2 = network.nodes.find(upstreamNodeId); if (I2 == network.nodes.end()) { // No upstream node; silently ignore return; } const MatfiltNode & upstreamNode = (*I2).second; // Decide action to perform. if (_impl->condition) { if (_impl->condition->Eval(upstreamNode, shaderTypePriority)) { chosenAction = _impl->action; } else { chosenAction = _impl->fallbackAction; } } else { chosenAction = _impl->action; } if (!chosenAction) { TF_CODING_ERROR("MatfiltVstructConditionalEvaluator: NULL action"); return; } // Execute action. switch (chosenAction->action) { case _VSCGAction::Ignore: break; case _VSCGAction::Connect: node.inputConnections[nodeInput] = {{upstreamNodeId, upstreamNodeOutput}}; break; case _VSCGAction::SetConstant: { // convert the constant to the expected type auto& reg = SdrRegistry::GetInstance(); SdrShaderNodeConstPtr sdrNode = reg.GetShaderNodeByIdentifier( node.nodeTypeId, shaderTypePriority); if (!sdrNode) { // TODO, warn break; } NdrPropertyConstPtr ndrProp = sdrNode->GetInput(nodeInput); if (!ndrProp) { // TODO, warn break; } VtValue value = chosenAction->value; TfToken inputType = ndrProp->GetType(); if (value.IsHolding()) { if (inputType == SdrPropertyTypes->String) { node.parameters[nodeInput] = value; } else { TF_CODING_ERROR("MatfiltVstructConditionalEvaluator: " "Expected string but found %s\n", inputType.GetText()); break; } } else if (value.IsHolding()) { // parser always stores numbers as double. double doubleValue = value.UncheckedGet(); VtValue resultValue; if (inputType == SdrPropertyTypes->Int) { resultValue = VtValue(static_cast(doubleValue)); } else if (inputType == SdrPropertyTypes->Float) { resultValue = VtValue(static_cast(doubleValue)); } if (!resultValue.IsEmpty()) { node.parameters[nodeInput] = resultValue; } else { TF_CODING_ERROR("MatfiltVstructConditionalEvaluator: " "Empty result"); } break; } else { TF_CODING_ERROR("MatfiltVstructConditionalEvaluator: " "Unhandled type %s\n", value.GetTypeName().c_str()); break; } break; } case _VSCGAction::CopyParam: { if (chosenAction->value.IsHolding()) { const TfToken & copyParamName = chosenAction->value.UncheckedGet(); // confirm that parameter types are equivalent // via Sdr auto& reg = SdrRegistry::GetInstance(); SdrShaderNodeConstPtr sdrNode = reg.GetShaderNodeByIdentifier( node.nodeTypeId, shaderTypePriority); SdrShaderNodeConstPtr sdrUpstreamNode = reg.GetShaderNodeByIdentifier( upstreamNode.nodeTypeId, shaderTypePriority); if (!sdrNode || !sdrUpstreamNode) { // TODO warn? break; } NdrPropertyConstPtr ndrProp = sdrNode->GetInput(nodeInput); NdrPropertyConstPtr ndrUpstreamProp = sdrUpstreamNode->GetInput(copyParamName); if (!ndrProp || !ndrUpstreamProp) { // TODO warn? break; } // TODO, convert between int and float if (ndrProp->GetType() == ndrUpstreamProp->GetType()) { auto I = upstreamNode.parameters.find(copyParamName); if (I != upstreamNode.parameters.end()) { // authored value node.parameters[nodeInput] = (*I).second; } else { // use default node.parameters[nodeInput] = ndrUpstreamProp->GetDefaultValue(); } } else { // TODO warn? } } break; } } // TODO } MatfiltVstructConditionalEvaluator::~MatfiltVstructConditionalEvaluator() { delete _impl; }