]> git.mxchange.org Git - simgear.git/blob - simgear/scene/material/makeEffect.cxx
Fix removal of directories.
[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             itr->second.valid())
127             return itr->second.get();
128     }
129     string effectFileName(name);
130     effectFileName += ".eff";
131     string absFileName
132         = SGModelLib::findDataFile(effectFileName, options);
133     if (absFileName.empty()) {
134         SG_LOG(SG_INPUT, SG_ALERT, "can't find \"" << effectFileName << "\"");
135         return 0;
136     }
137     SGPropertyNode_ptr effectProps = new SGPropertyNode();
138     try {
139         readProperties(absFileName, effectProps.ptr(), 0, true);
140     }
141     catch (sg_io_exception& e) {
142         SG_LOG(SG_INPUT, SG_ALERT, "error reading \"" << absFileName << "\": "
143                << e.getFormattedMessage());
144         return 0;
145     }
146     ref_ptr<Effect> result = makeEffect(effectProps.ptr(), realizeTechniques,
147                                         options);
148     if (result.valid()) {
149         OpenThreads::ScopedLock<OpenThreads::ReentrantMutex> lock(effectMutex);
150         pair<EffectMap::iterator, bool> irslt
151             = effectMap.insert(make_pair(name, result));
152         if (!irslt.second) {
153             // Another thread beat us to it!. Discard our newly
154             // constructed Effect and use the one in the cache.
155             result = irslt.first->second;
156         }
157     }
158     return result.release();
159 }
160
161
162 Effect* makeEffect(SGPropertyNode* prop,
163                    bool realizeTechniques,
164                    const SGReaderWriterOptions* options)
165 {
166     // Give default names to techniques and passes
167     vector<SGPropertyNode_ptr> techniques = prop->getChildren("technique");
168     for (int i = 0; i < (int)techniques.size(); ++i) {
169         SGPropertyNode* tniqProp = techniques[i].ptr();
170         if (!tniqProp->hasChild("name"))
171             setValue(tniqProp->getChild("name", 0, true),
172                      boost::lexical_cast<string>(i));
173         vector<SGPropertyNode_ptr> passes = tniqProp->getChildren("pass");
174         for (int j = 0; j < (int)passes.size(); ++j) {
175             SGPropertyNode* passProp = passes[j].ptr();
176             if (!passProp->hasChild("name"))
177                 setValue(passProp->getChild("name", 0, true),
178                          boost::lexical_cast<string>(j));
179             vector<SGPropertyNode_ptr> texUnits
180                 = passProp->getChildren("texture-unit");
181             for (int k = 0; k < (int)texUnits.size(); ++k) {
182                 SGPropertyNode* texUnitProp = texUnits[k].ptr();
183                 if (!texUnitProp->hasChild("name"))
184                     setValue(texUnitProp->getChild("name", 0, true),
185                              boost::lexical_cast<string>(k));
186             }
187         }
188     }
189     ref_ptr<Effect> effect;
190     if(!prop->hasChild("name")){
191         setValue(prop->getChild("name", 0, true), "noname");
192     }
193     SGPropertyNode_ptr nameProp = prop->getChild("name");
194     // Merge with the parent effect, if any
195     SGPropertyNode_ptr inheritProp = prop->getChild("inherits-from");
196     Effect* parent = 0;
197     if (inheritProp) {
198         //prop->removeChild("inherits-from");
199         parent = makeEffect(inheritProp->getStringValue(), false, options);
200         if (parent) {
201             Effect::Key key;
202             key.unmerged = prop;
203             if (options) {
204                 key.paths = options->getDatabasePathList();
205             }
206             Effect::Cache* cache = 0;
207             Effect::Cache::iterator itr;
208             {
209                 OpenThreads::ScopedLock<OpenThreads::ReentrantMutex>
210                     lock(effectMutex);
211                 cache = parent->getCache();
212                 itr = cache->find(key);
213                 if ((itr != cache->end())&&
214                     itr->second.lock(effect))
215                 {
216                     effect->generator = parent->generator;  // Copy the generators
217                 }
218             }
219             if (!effect.valid()) {
220                 effect = new Effect;
221                 effect->setName(nameProp->getStringValue());
222                 effect->root = new SGPropertyNode;
223                 mergePropertyTrees(effect->root, prop, parent->root);
224                 effect->parametersProp = effect->root->getChild("parameters");
225                 OpenThreads::ScopedLock<OpenThreads::ReentrantMutex>
226                     lock(effectMutex);
227                 pair<Effect::Cache::iterator, bool> irslt
228                     = cache->insert(make_pair(key, effect));
229                 if (!irslt.second) {
230                     ref_ptr<Effect> old;
231                     if (irslt.first->second.lock(old))
232                         effect = old; // Another thread beat us in creating it! Discard our own...
233                     else
234                         irslt.first->second = effect; // update existing, but empty observer
235                 }
236                 effect->generator = parent->generator;  // Copy the generators
237             }
238         } else {
239             SG_LOG(SG_INPUT, SG_ALERT, "can't find base effect " <<
240                    inheritProp->getStringValue());
241             return 0;
242         }
243     } else {
244         effect = new Effect;
245         effect->setName(nameProp->getStringValue());
246         effect->root = prop;
247         effect->parametersProp = effect->root->getChild("parameters");
248     }
249     const SGPropertyNode *generateProp = prop->getChild("generate");
250     if(generateProp)
251     {
252         effect->generator.clear();
253
254         // Effect needs some generated properties, like tangent vectors
255         const SGPropertyNode *parameter = generateProp->getChild("normal");
256         if(parameter) effect->setGenerator(Effect::NORMAL, parameter->getIntValue());
257
258         parameter = generateProp->getChild("tangent");
259         if(parameter) effect->setGenerator(Effect::TANGENT, parameter->getIntValue());
260
261         parameter = generateProp->getChild("binormal");
262         if(parameter) effect->setGenerator(Effect::BINORMAL, parameter->getIntValue());
263     }
264     if (realizeTechniques) {
265         try {
266             OpenThreads::ScopedLock<OpenThreads::ReentrantMutex>
267                 lock(effectMutex);
268             effect->realizeTechniques(options);
269         }
270         catch (BuilderException& e) {
271             SG_LOG(SG_INPUT, SG_ALERT, "Error building technique: "
272                    << e.getFormattedMessage());
273             return 0;
274         }
275     }
276     return effect.release();
277 }
278
279 void clearEffectCache()
280 {
281     OpenThreads::ScopedLock<OpenThreads::ReentrantMutex> lock(effectMutex);
282     effectMap.clear();
283 }
284
285 }