]> git.mxchange.org Git - simgear.git/blob - simgear/scene/material/makeEffect.cxx
Use of copy-constructors
[simgear.git] / simgear / scene / material / makeEffect.cxx
1
2 #ifdef HAVE_CONFIG_H
3 #  include <simgear_config.h>
4 #endif
5
6 #include "Effect.hxx"
7 #include "EffectBuilder.hxx"
8 #include "Technique.hxx"
9 #include "Pass.hxx"
10
11 #include <algorithm>
12 #include <cstring>
13 #include <map>
14 #include <sstream>
15
16 #include <boost/lexical_cast.hpp>
17 #include <boost/tuple/tuple.hpp>
18 #include <boost/tuple/tuple_comparison.hpp>
19
20 #include <OpenThreads/ReentrantMutex>
21 #include <OpenThreads/ScopedLock>
22
23 #include <osg/Material>
24 #include <osg/Program>
25 #include <osg/Referenced>
26 #include <osg/Texture2D>
27 #include <osg/Vec4d>
28
29 #include <osgDB/FileUtils>
30 #include <osgDB/ReadFile>
31 #include <osgDB/Registry>
32
33 #include <simgear/debug/logstream.hxx>
34 #include <simgear/scene/util/SGReaderWriterOptions.hxx>
35 #include <simgear/props/props_io.hxx>
36 #include <simgear/scene/util/SGSceneFeatures.hxx>
37 #include <simgear/scene/util/SplicingVisitor.hxx>
38 #include <simgear/structure/SGExpression.hxx>
39
40 namespace simgear
41 {
42 using namespace std;
43 using namespace osg;
44 using namespace effect;
45
46 typedef vector<const SGPropertyNode*> RawPropVector;
47 typedef map<const string, ref_ptr<Effect> > EffectMap;
48
49 namespace
50 {
51 EffectMap effectMap;
52 OpenThreads::ReentrantMutex effectMutex;
53 }
54
55 /** Merge two property trees, producing a new tree.
56  * If the nodes are both leaves, value comes from left leaf.
57  * Otherwise, The children are examined. If a left and right child are
58  * "identical," they are merged and the result placed in the children
59  * of the result. Otherwise the left children are placed after the
60  * right children in the result.
61  *
62  * Nodes are considered equal if their names and indexes are equal.
63  */
64
65 struct PropPredicate
66     : public unary_function<const SGPropertyNode*, bool>
67 {
68     PropPredicate(const SGPropertyNode* node_) : node(node_) {}
69     bool operator()(const SGPropertyNode* arg) const
70     {
71         if (strcmp(node->getName(), arg->getName()))
72             return false;
73         return node->getIndex() == arg->getIndex();
74     }
75     const SGPropertyNode* node;
76 };
77
78 namespace effect
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     // Merge identical nodes
92     for (int i = 0; i < right->nChildren(); ++i) {
93         const SGPropertyNode* node = right->getChild(i);
94         RawPropVector::iterator litr
95             = find_if(leftChildren.begin(), leftChildren.end(),
96                       PropPredicate(node));
97         SGPropertyNode* newChild
98             = resultNode->getChild(node->getName(), node->getIndex(), true);
99         if (litr != leftChildren.end()) {
100             mergePropertyTrees(newChild, *litr, node);
101             leftChildren.erase(litr);
102         } else {
103             copyProperties(node, newChild);
104         }
105     }
106     // Now copy nodes remaining in the left tree
107     for (RawPropVector::iterator itr = leftChildren.begin(),
108              e = leftChildren.end();
109          itr != e;
110          ++itr) {
111         SGPropertyNode* newChild
112             = resultNode->getChild((*itr)->getName(), (*itr)->getIndex(), true);
113         copyProperties(*itr, newChild);
114     }
115 }
116 }
117
118 Effect* makeEffect(const string& name,
119                    bool realizeTechniques,
120                    const SGReaderWriterOptions* options)
121 {
122     {
123         OpenThreads::ScopedLock<OpenThreads::ReentrantMutex> lock(effectMutex);
124         EffectMap::iterator itr = effectMap.find(name);
125         if (itr != effectMap.end())
126             return itr->second.get();
127     }
128     string effectFileName(name);
129     effectFileName += ".eff";
130     string absFileName
131         = SGModelLib::findDataFile(effectFileName, options);
132     if (absFileName.empty()) {
133         SG_LOG(SG_INPUT, SG_ALERT, "can't find \"" << effectFileName << "\"");
134         return 0;
135     }
136     SGPropertyNode_ptr effectProps = new SGPropertyNode();
137     try {
138         readProperties(absFileName, effectProps.ptr(), 0, true);
139     }
140     catch (sg_io_exception& e) {
141         SG_LOG(SG_INPUT, SG_ALERT, "error reading \"" << absFileName << "\": "
142                << e.getFormattedMessage());
143         return 0;
144     }
145     ref_ptr<Effect> result = makeEffect(effectProps.ptr(), realizeTechniques,
146                                         options);
147     if (result.valid()) {
148         OpenThreads::ScopedLock<OpenThreads::ReentrantMutex> lock(effectMutex);
149         pair<EffectMap::iterator, bool> irslt
150             = effectMap.insert(make_pair(name, result));
151         if (!irslt.second) {
152             // Another thread beat us to it!. Discard our newly
153             // constructed Effect and use the one in the cache.
154             result = irslt.first->second;
155         }
156     }
157     return result.release();
158 }
159
160
161 Effect* makeEffect(SGPropertyNode* prop,
162                    bool realizeTechniques,
163                    const SGReaderWriterOptions* options)
164 {
165     // Give default names to techniques and passes
166     vector<SGPropertyNode_ptr> techniques = prop->getChildren("technique");
167     for (int i = 0; i < (int)techniques.size(); ++i) {
168         SGPropertyNode* tniqProp = techniques[i].ptr();
169         if (!tniqProp->hasChild("name"))
170             setValue(tniqProp->getChild("name", 0, true),
171                      boost::lexical_cast<string>(i));
172         vector<SGPropertyNode_ptr> passes = tniqProp->getChildren("pass");
173         for (int j = 0; j < (int)passes.size(); ++j) {
174             SGPropertyNode* passProp = passes[j].ptr();
175             if (!passProp->hasChild("name"))
176                 setValue(passProp->getChild("name", 0, true),
177                          boost::lexical_cast<string>(j));
178             vector<SGPropertyNode_ptr> texUnits
179                 = passProp->getChildren("texture-unit");
180             for (int k = 0; k < (int)texUnits.size(); ++k) {
181                 SGPropertyNode* texUnitProp = texUnits[k].ptr();
182                 if (!texUnitProp->hasChild("name"))
183                     setValue(texUnitProp->getChild("name", 0, true),
184                              boost::lexical_cast<string>(k));
185             }
186         }
187     }
188     ref_ptr<Effect> effect;
189     // Merge with the parent effect, if any
190     SGPropertyNode_ptr inheritProp = prop->getChild("inherits-from");
191     Effect* parent = 0;
192     if (inheritProp) {
193         //prop->removeChild("inherits-from");
194         parent = makeEffect(inheritProp->getStringValue(), false, options);
195         if (parent) {
196             Effect::Key key;
197             key.unmerged = prop;
198             if (options) {
199                 key.paths = options->getDatabasePathList();
200             }
201             Effect::Cache* cache = 0;
202             Effect::Cache::iterator itr;
203             {
204                 OpenThreads::ScopedLock<OpenThreads::ReentrantMutex>
205                     lock(effectMutex);
206                 cache = parent->getCache();
207                 itr = cache->find(key);
208                 if (itr != cache->end()) {
209                     effect = itr->second.get();
210                     effect->generator = parent->generator;  // Copy the generators
211                 }
212             }
213             if (!effect.valid()) {
214                 effect = new Effect;
215                 effect->root = new SGPropertyNode;
216                 mergePropertyTrees(effect->root, prop, parent->root);
217                 effect->parametersProp = effect->root->getChild("parameters");
218                 OpenThreads::ScopedLock<OpenThreads::ReentrantMutex>
219                     lock(effectMutex);
220                 pair<Effect::Cache::iterator, bool> irslt
221                     = cache->insert(make_pair(key, effect));
222                 if (!irslt.second)
223                     effect = irslt.first->second;
224                 effect->generator = parent->generator;  // Copy the generators
225             }
226         } else {
227             SG_LOG(SG_INPUT, SG_ALERT, "can't find base effect " <<
228                    inheritProp->getStringValue());
229             return 0;
230         }
231     } else {
232         effect = new Effect;
233         effect->root = prop;
234         effect->parametersProp = effect->root->getChild("parameters");
235     }
236     const SGPropertyNode *generateProp = prop->getChild("generate");
237     if(generateProp)
238     {
239         effect->generator.clear();
240
241         // Effect needs some generated properties, like tangent vectors
242         const SGPropertyNode *parameter = generateProp->getChild("normal");
243         if(parameter) effect->setGenerator(Effect::NORMAL, parameter->getIntValue());
244
245         parameter = generateProp->getChild("tangent");
246         if(parameter) effect->setGenerator(Effect::TANGENT, parameter->getIntValue());
247
248         parameter = generateProp->getChild("binormal");
249         if(parameter) effect->setGenerator(Effect::BINORMAL, parameter->getIntValue());
250     }
251     if (realizeTechniques) {
252         try {
253             OpenThreads::ScopedLock<OpenThreads::ReentrantMutex>
254                 lock(effectMutex);
255             effect->realizeTechniques(options);
256         }
257         catch (BuilderException& e) {
258             SG_LOG(SG_INPUT, SG_ALERT, "Error building technique: "
259                    << e.getFormattedMessage());
260             return 0;
261         }
262     }
263     return effect.release();
264 }
265
266 }