]> git.mxchange.org Git - simgear.git/blob - simgear/scene/material/makeEffect.cxx
Construct effects from property lists
[simgear.git] / simgear / scene / material / makeEffect.cxx
1 #include "Effect.hxx"
2 #include "Technique.hxx"
3 #include "Pass.hxx"
4
5 #include <algorithm>
6 #include <cstring>
7 #include <map>
8 #include <sstream>
9
10 #include <boost/lexical_cast.hpp>
11 #include <boost/tuple/tuple.hpp>
12 #include <boost/tuple/tuple_comparison.hpp>
13
14 #include <OpenThreads/ReentrantMutex>
15 #include <OpenThreads/ScopedLock>
16
17 #include <osg/Material>
18 #include <osg/Program>
19 #include <osg/Referenced>
20 #include <osg/Texture2D>
21 #include <osg/Vec4d>
22
23 #include <osgDB/FileUtils>
24 #include <osgDB/ReadFile>
25 #include <osgDB/Registry>
26
27 #include <simgear/debug/logstream.hxx>
28 #include <simgear/props/props_io.hxx>
29 #include <simgear/scene/util/SGSceneFeatures.hxx>
30 #include <simgear/structure/SGExpression.hxx>
31
32 namespace simgear
33 {
34 using namespace std;
35 using namespace osg;
36
37 typedef vector<const SGPropertyNode*> RawPropVector;
38 typedef map<const string, ref_ptr<Effect> > EffectMap;
39
40 namespace
41 {
42 EffectMap effectMap;
43 OpenThreads::ReentrantMutex effectMutex;
44 }
45
46 /** Merge two property trees, producing a new tree.
47  * If the nodes are both leaves, value comes from left leaf.
48  * Otherwise, The children are examined. If a left and right child are
49  * "identical," they are merged and the result placed in the children
50  * of the result. Otherwise the left children are placed after the
51  * right children in the result.
52  *
53  * Nodes are considered identical if:
54  * Their names are equal;
55  * Either they both have "name" children and their values are equal;
56  * or their indexes are equal.
57  */
58
59 struct PropPredicate
60     : public unary_function<const SGPropertyNode*, bool>
61 {
62     PropPredicate(const SGPropertyNode* node_) : node(node_) {}
63     bool operator()(const SGPropertyNode* arg) const
64     {
65         if (strcmp(node->getName(), arg->getName()))
66             return false;
67         const SGPropertyNode* nodeName = node->getChild("name");
68         const SGPropertyNode* argName = arg->getChild("name");
69         if (nodeName && argName)
70             return !strcmp(nodeName->getStringValue(),
71                            argName->getStringValue());
72         else if (!(nodeName || argName))
73             return node->getIndex() == arg->getIndex();
74         else
75             return false;
76     }
77     const SGPropertyNode* node;
78 };
79
80 void mergePropertyTrees(SGPropertyNode* resultNode,
81                         const SGPropertyNode* left, const SGPropertyNode* right)
82 {
83     if (left->nChildren() == 0) {
84         copyProperties(left, resultNode);
85         return;
86     }
87     resultNode->setAttributes(right->getAttributes());
88     RawPropVector leftChildren;
89     for (int i = 0; i < left->nChildren(); ++i)
90         leftChildren.push_back(left->getChild(i));
91     // Maximum index of nodes (with same names) we've created.
92     map<string, int> nodeIndex;
93     // Merge identical nodes
94     for (int i = 0; i < right->nChildren(); ++i) {
95         const SGPropertyNode* node = right->getChild(i);
96         RawPropVector::iterator litr
97             = find_if(leftChildren.begin(), leftChildren.end(),
98                       PropPredicate(node));
99         SGPropertyNode* newChild
100             = resultNode->getChild(node->getName(),
101                                    nodeIndex[node->getName()]++, true);
102         if (litr != leftChildren.end()) {
103             mergePropertyTrees(newChild, *litr, node);
104             leftChildren.erase(litr);
105         } else {
106             copyProperties(node, newChild);
107         }
108     }
109     for (RawPropVector::iterator itr = leftChildren.begin(),
110              e = leftChildren.end();
111          itr != e;
112          ++itr) {
113         SGPropertyNode* newChild
114             = resultNode->getChild((*itr)->getName(),
115                                    nodeIndex[(*itr)->getName()]++, true);
116         copyProperties(*itr, newChild);
117     }
118 }
119
120 Effect* makeEffect(const string& name,
121                    bool realizeTechniques,
122                    const osgDB::ReaderWriter::Options* options)
123 {
124     OpenThreads::ScopedLock<OpenThreads::ReentrantMutex> lock(effectMutex);
125     EffectMap::iterator itr = effectMap.find(name);
126     if (itr != effectMap.end())
127         return itr->second.get();
128     string effectFileName(name);
129     effectFileName += ".eff";
130     string absFileName
131         = osgDB::Registry::instance()->findDataFile(effectFileName, options,
132                                                     osgDB::CASE_SENSITIVE);
133     if (absFileName.empty())
134         return 0;
135     SGPropertyNode_ptr effectProps = new SGPropertyNode();
136     readProperties(absFileName, effectProps.ptr(), 0, true);
137     Effect* result = makeEffect(effectProps.ptr(), realizeTechniques, options);
138     if (result)
139         effectMap.insert(make_pair(name, result));
140     return result;
141 }
142
143
144 Effect* makeEffect(SGPropertyNode* prop,
145                    bool realizeTechniques,
146                    const osgDB::ReaderWriter::Options* options)
147 {
148     // Give default names to techniques and passes
149     vector<SGPropertyNode_ptr> techniques = prop->getChildren("technique");
150     for (int i = 0; i < techniques.size(); ++i) {
151         SGPropertyNode* tniqProp = techniques[i].ptr();
152         if (!tniqProp->hasChild("name"))
153             setValue(tniqProp->getChild("name", 0, true),
154                      boost::lexical_cast<string>(i));
155         vector<SGPropertyNode_ptr> passes = tniqProp->getChildren("pass");
156         for (int j = 0; j < passes.size(); ++j) {
157             SGPropertyNode* passProp = passes[j].ptr();
158             if (!passProp->hasChild("name"))
159                 setValue(passProp->getChild("name", 0, true),
160                          boost::lexical_cast<string>(j));
161             vector<SGPropertyNode_ptr> texUnits
162                 = passProp->getChildren("texture-unit");
163             for (int k = 0; k < texUnits.size(); ++k) {
164                 SGPropertyNode* texUnitProp = texUnits[k].ptr();
165                 if (!texUnitProp->hasChild("name"))
166                     setValue(texUnitProp->getChild("name", 0, true),
167                              boost::lexical_cast<string>(k));
168             }
169         }
170     }
171     Effect* effect = new Effect;
172     // Merge with the parent effect, if any
173     const SGPropertyNode* inheritProp = prop->getChild("inherits-from");
174     Effect* parent = 0;
175     if (inheritProp) {
176         parent = makeEffect(inheritProp->getStringValue(), realizeTechniques,
177                             options);
178         effect->root = new SGPropertyNode;
179         mergePropertyTrees(effect->root, prop, parent->root);
180         effect->root->removeChild("inherits-from");
181     } else {
182         effect->root = prop;
183     }
184     effect->parametersProp = effect->root->getChild("parameters");
185     if (realizeTechniques)
186         effect->realizeTechniques(options);
187     return effect;
188 }
189
190 }