]> git.mxchange.org Git - simgear.git/blob - simgear/scene/material/makeEffect.cxx
Random buildings - initial commit.
[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     // Merge with the parent effect, if any
191     SGPropertyNode_ptr inheritProp = prop->getChild("inherits-from");
192     Effect* parent = 0;
193     if (inheritProp) {
194         //prop->removeChild("inherits-from");
195         parent = makeEffect(inheritProp->getStringValue(), false, options);
196         if (parent) {
197             Effect::Key key;
198             key.unmerged = prop;
199             if (options) {
200                 key.paths = options->getDatabasePathList();
201             }
202             Effect::Cache* cache = 0;
203             Effect::Cache::iterator itr;
204             {
205                 OpenThreads::ScopedLock<OpenThreads::ReentrantMutex>
206                     lock(effectMutex);
207                 cache = parent->getCache();
208                 itr = cache->find(key);
209                 if ((itr != cache->end())&&
210                     itr->second.lock(effect))
211                 {
212                     effect->generator = parent->generator;  // Copy the generators
213                 }
214             }
215             if (!effect.valid()) {
216                 effect = new Effect;
217                 effect->root = new SGPropertyNode;
218                 mergePropertyTrees(effect->root, prop, parent->root);
219                 effect->parametersProp = effect->root->getChild("parameters");
220                 OpenThreads::ScopedLock<OpenThreads::ReentrantMutex>
221                     lock(effectMutex);
222                 pair<Effect::Cache::iterator, bool> irslt
223                     = cache->insert(make_pair(key, effect));
224                 if (!irslt.second) {
225                     ref_ptr<Effect> old;
226                     if (irslt.first->second.lock(old))
227                         effect = old; // Another thread beat us in creating it! Discard our own...
228                     else
229                         irslt.first->second = effect; // update existing, but empty observer
230                 }
231                 effect->generator = parent->generator;  // Copy the generators
232             }
233         } else {
234             SG_LOG(SG_INPUT, SG_ALERT, "can't find base effect " <<
235                    inheritProp->getStringValue());
236             return 0;
237         }
238     } else {
239         effect = new Effect;
240         effect->root = prop;
241         effect->parametersProp = effect->root->getChild("parameters");
242     }
243     const SGPropertyNode *generateProp = prop->getChild("generate");
244     if(generateProp)
245     {
246         effect->generator.clear();
247
248         // Effect needs some generated properties, like tangent vectors
249         const SGPropertyNode *parameter = generateProp->getChild("normal");
250         if(parameter) effect->setGenerator(Effect::NORMAL, parameter->getIntValue());
251
252         parameter = generateProp->getChild("tangent");
253         if(parameter) effect->setGenerator(Effect::TANGENT, parameter->getIntValue());
254
255         parameter = generateProp->getChild("binormal");
256         if(parameter) effect->setGenerator(Effect::BINORMAL, parameter->getIntValue());
257     }
258     if (realizeTechniques) {
259         try {
260             OpenThreads::ScopedLock<OpenThreads::ReentrantMutex>
261                 lock(effectMutex);
262             effect->realizeTechniques(options);
263         }
264         catch (BuilderException& e) {
265             SG_LOG(SG_INPUT, SG_ALERT, "Error building technique: "
266                    << e.getFormattedMessage());
267             return 0;
268         }
269     }
270     return effect.release();
271 }
272
273 }