]> git.mxchange.org Git - simgear.git/blob - simgear/scene/material/makeEffect.cxx
Merge branch 'zan/stencil'
[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/props/props_io.hxx>
35 #include <simgear/scene/util/SGSceneFeatures.hxx>
36 #include <simgear/scene/util/SplicingVisitor.hxx>
37 #include <simgear/structure/SGExpression.hxx>
38
39 namespace simgear
40 {
41 using namespace std;
42 using namespace osg;
43 using namespace effect;
44
45 typedef vector<const SGPropertyNode*> RawPropVector;
46 typedef map<const string, ref_ptr<Effect> > EffectMap;
47
48 namespace
49 {
50 EffectMap effectMap;
51 OpenThreads::ReentrantMutex effectMutex;
52 }
53
54 /** Merge two property trees, producing a new tree.
55  * If the nodes are both leaves, value comes from left leaf.
56  * Otherwise, The children are examined. If a left and right child are
57  * "identical," they are merged and the result placed in the children
58  * of the result. Otherwise the left children are placed after the
59  * right children in the result.
60  *
61  * Nodes are considered equal if their names and indexes are equal.
62  */
63
64 struct PropPredicate
65     : public unary_function<const SGPropertyNode*, bool>
66 {
67     PropPredicate(const SGPropertyNode* node_) : node(node_) {}
68     bool operator()(const SGPropertyNode* arg) const
69     {
70         if (strcmp(node->getName(), arg->getName()))
71             return false;
72         return node->getIndex() == arg->getIndex();
73     }
74     const SGPropertyNode* node;
75 };
76
77 namespace effect
78 {
79 void mergePropertyTrees(SGPropertyNode* resultNode,
80                         const SGPropertyNode* left, const SGPropertyNode* right)
81 {
82     if (left->nChildren() == 0) {
83         copyProperties(left, resultNode);
84         return;
85     }
86     resultNode->setAttributes(right->getAttributes());
87     RawPropVector leftChildren;
88     for (int i = 0; i < left->nChildren(); ++i)
89         leftChildren.push_back(left->getChild(i));
90     // Merge identical nodes
91     for (int i = 0; i < right->nChildren(); ++i) {
92         const SGPropertyNode* node = right->getChild(i);
93         RawPropVector::iterator litr
94             = find_if(leftChildren.begin(), leftChildren.end(),
95                       PropPredicate(node));
96         SGPropertyNode* newChild
97             = resultNode->getChild(node->getName(), node->getIndex(), true);
98         if (litr != leftChildren.end()) {
99             mergePropertyTrees(newChild, *litr, node);
100             leftChildren.erase(litr);
101         } else {
102             copyProperties(node, newChild);
103         }
104     }
105     // Now copy nodes remaining in the left tree
106     for (RawPropVector::iterator itr = leftChildren.begin(),
107              e = leftChildren.end();
108          itr != e;
109          ++itr) {
110         SGPropertyNode* newChild
111             = resultNode->getChild((*itr)->getName(), (*itr)->getIndex(), true);
112         copyProperties(*itr, newChild);
113     }
114 }
115 }
116
117 Effect* makeEffect(const string& name,
118                    bool realizeTechniques,
119                    const osgDB::ReaderWriter::Options* options)
120 {
121     {
122         OpenThreads::ScopedLock<OpenThreads::ReentrantMutex> lock(effectMutex);
123         EffectMap::iterator itr = effectMap.find(name);
124         if (itr != effectMap.end())
125             return itr->second.get();
126     }
127     string effectFileName(name);
128     effectFileName += ".eff";
129     string absFileName
130         = osgDB::findDataFile(effectFileName, options);
131     if (absFileName.empty()) {
132         SG_LOG(SG_INPUT, SG_ALERT, "can't find \"" << effectFileName << "\"");
133         return 0;
134     }
135     SGPropertyNode_ptr effectProps = new SGPropertyNode();
136     try {
137         readProperties(absFileName, effectProps.ptr(), 0, true);
138     }
139     catch (sg_io_exception& e) {
140         SG_LOG(SG_INPUT, SG_ALERT, "error reading \"" << absFileName << "\": "
141                << e.getFormattedMessage());
142         return 0;
143     }
144     ref_ptr<Effect> result = makeEffect(effectProps.ptr(), realizeTechniques,
145                                         options);
146     if (result.valid()) {
147         OpenThreads::ScopedLock<OpenThreads::ReentrantMutex> lock(effectMutex);
148         pair<EffectMap::iterator, bool> irslt
149             = effectMap.insert(make_pair(name, result));
150         if (!irslt.second) {
151             // Another thread beat us to it!. Discard our newly
152             // constructed Effect and use the one in the cache.
153             result = irslt.first->second;
154         }
155     }
156     return result.release();
157 }
158
159
160 Effect* makeEffect(SGPropertyNode* prop,
161                    bool realizeTechniques,
162                    const osgDB::ReaderWriter::Options* options)
163 {
164     // Give default names to techniques and passes
165     vector<SGPropertyNode_ptr> techniques = prop->getChildren("technique");
166     for (int i = 0; i < (int)techniques.size(); ++i) {
167         SGPropertyNode* tniqProp = techniques[i].ptr();
168         if (!tniqProp->hasChild("name"))
169             setValue(tniqProp->getChild("name", 0, true),
170                      boost::lexical_cast<string>(i));
171         vector<SGPropertyNode_ptr> passes = tniqProp->getChildren("pass");
172         for (int j = 0; j < (int)passes.size(); ++j) {
173             SGPropertyNode* passProp = passes[j].ptr();
174             if (!passProp->hasChild("name"))
175                 setValue(passProp->getChild("name", 0, true),
176                          boost::lexical_cast<string>(j));
177             vector<SGPropertyNode_ptr> texUnits
178                 = passProp->getChildren("texture-unit");
179             for (int k = 0; k < (int)texUnits.size(); ++k) {
180                 SGPropertyNode* texUnitProp = texUnits[k].ptr();
181                 if (!texUnitProp->hasChild("name"))
182                     setValue(texUnitProp->getChild("name", 0, true),
183                              boost::lexical_cast<string>(k));
184             }
185         }
186     }
187     ref_ptr<Effect> effect;
188     // Merge with the parent effect, if any
189     SGPropertyNode_ptr inheritProp = prop->getChild("inherits-from");
190     Effect* parent = 0;
191     if (inheritProp) {
192         //prop->removeChild("inherits-from");
193         parent = makeEffect(inheritProp->getStringValue(), false, options);
194         if (parent) {
195             Effect::Key key;
196             key.unmerged = prop;
197             if (options) {
198                 key.paths = options->getDatabasePathList();
199             }
200             Effect::Cache* cache = 0;
201             Effect::Cache::iterator itr;
202             {
203                 OpenThreads::ScopedLock<OpenThreads::ReentrantMutex>
204                     lock(effectMutex);
205                 cache = parent->getCache();
206                 itr = cache->find(key);
207                 if (itr != cache->end()) 
208                     effect = itr->second.get();
209             }
210             if (!effect.valid()) {
211                 effect = new Effect;
212                 effect->root = new SGPropertyNode;
213                 mergePropertyTrees(effect->root, prop, parent->root);
214                 effect->parametersProp = effect->root->getChild("parameters");
215                 OpenThreads::ScopedLock<OpenThreads::ReentrantMutex>
216                     lock(effectMutex);
217                 pair<Effect::Cache::iterator, bool> irslt
218                     = cache->insert(make_pair(key, effect));
219                 if (!irslt.second)
220                     effect = irslt.first->second;
221             }
222         } else {
223             SG_LOG(SG_INPUT, SG_ALERT, "can't find base effect " <<
224                    inheritProp->getStringValue());
225             return 0;
226         }
227     } else {
228         effect = new Effect;
229         effect->root = prop;
230         effect->parametersProp = effect->root->getChild("parameters");
231     }
232     if (realizeTechniques) {
233         try {
234             OpenThreads::ScopedLock<OpenThreads::ReentrantMutex>
235                 lock(effectMutex);
236             effect->realizeTechniques(options);
237         }
238         catch (BuilderException& e) {
239             SG_LOG(SG_INPUT, SG_ALERT, "Error building technique: "
240                    << e.getFormattedMessage());
241             return 0;
242         }
243     }
244     return effect.release();
245 }
246
247 }