3 # include <simgear_config.h>
7 #include "EffectBuilder.hxx"
8 #include "Technique.hxx"
16 #include <boost/lexical_cast.hpp>
17 #include <boost/tuple/tuple.hpp>
18 #include <boost/tuple/tuple_comparison.hpp>
20 #include <OpenThreads/ReentrantMutex>
21 #include <OpenThreads/ScopedLock>
23 #include <osg/Material>
24 #include <osg/Program>
25 #include <osg/Referenced>
26 #include <osg/Texture2D>
29 #include <osgDB/FileUtils>
30 #include <osgDB/ReadFile>
31 #include <osgDB/Registry>
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>
43 using namespace effect;
45 typedef vector<const SGPropertyNode*> RawPropVector;
46 typedef map<const string, ref_ptr<Effect> > EffectMap;
51 OpenThreads::ReentrantMutex effectMutex;
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.
61 * Nodes are considered equal if their names and indexes are equal.
65 : public unary_function<const SGPropertyNode*, bool>
67 PropPredicate(const SGPropertyNode* node_) : node(node_) {}
68 bool operator()(const SGPropertyNode* arg) const
70 if (strcmp(node->getName(), arg->getName()))
72 return node->getIndex() == arg->getIndex();
74 const SGPropertyNode* node;
79 void mergePropertyTrees(SGPropertyNode* resultNode,
80 const SGPropertyNode* left, const SGPropertyNode* right)
82 if (left->nChildren() == 0) {
83 copyProperties(left, resultNode);
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(),
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);
102 copyProperties(node, newChild);
105 // Now copy nodes remaining in the left tree
106 for (RawPropVector::iterator itr = leftChildren.begin(),
107 e = leftChildren.end();
110 SGPropertyNode* newChild
111 = resultNode->getChild((*itr)->getName(), (*itr)->getIndex(), true);
112 copyProperties(*itr, newChild);
117 Effect* makeEffect(const string& name,
118 bool realizeTechniques,
119 const osgDB::ReaderWriter::Options* options)
122 OpenThreads::ScopedLock<OpenThreads::ReentrantMutex> lock(effectMutex);
123 EffectMap::iterator itr = effectMap.find(name);
124 if (itr != effectMap.end())
125 return itr->second.get();
127 string effectFileName(name);
128 effectFileName += ".eff";
130 = osgDB::findDataFile(effectFileName, options);
131 if (absFileName.empty()) {
132 SG_LOG(SG_INPUT, SG_ALERT, "can't find \"" << effectFileName << "\"");
135 SGPropertyNode_ptr effectProps = new SGPropertyNode();
137 readProperties(absFileName, effectProps.ptr(), 0, true);
139 catch (sg_io_exception& e) {
140 SG_LOG(SG_INPUT, SG_ALERT, "error reading \"" << absFileName << "\": "
141 << e.getFormattedMessage());
144 ref_ptr<Effect> result = makeEffect(effectProps.ptr(), realizeTechniques,
146 if (result.valid()) {
147 OpenThreads::ScopedLock<OpenThreads::ReentrantMutex> lock(effectMutex);
148 pair<EffectMap::iterator, bool> irslt
149 = effectMap.insert(make_pair(name, result));
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;
156 return result.release();
160 Effect* makeEffect(SGPropertyNode* prop,
161 bool realizeTechniques,
162 const osgDB::ReaderWriter::Options* options)
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));
187 ref_ptr<Effect> effect;
188 // Merge with the parent effect, if any
189 SGPropertyNode_ptr inheritProp = prop->getChild("inherits-from");
192 //prop->removeChild("inherits-from");
193 parent = makeEffect(inheritProp->getStringValue(), false, options);
198 key.paths = options->getDatabasePathList();
200 Effect::Cache* cache = 0;
201 Effect::Cache::iterator itr;
203 OpenThreads::ScopedLock<OpenThreads::ReentrantMutex>
205 cache = parent->getCache();
206 itr = cache->find(key);
207 if (itr != cache->end())
208 effect = itr->second.get();
210 if (!effect.valid()) {
212 effect->root = new SGPropertyNode;
213 mergePropertyTrees(effect->root, prop, parent->root);
214 effect->parametersProp = effect->root->getChild("parameters");
215 OpenThreads::ScopedLock<OpenThreads::ReentrantMutex>
217 pair<Effect::Cache::iterator, bool> irslt
218 = cache->insert(make_pair(key, effect));
220 effect = irslt.first->second;
223 SG_LOG(SG_INPUT, SG_ALERT, "can't find base effect " <<
224 inheritProp->getStringValue());
230 effect->parametersProp = effect->root->getChild("parameters");
232 if (realizeTechniques) {
234 OpenThreads::ScopedLock<OpenThreads::ReentrantMutex>
236 effect->realizeTechniques(options);
238 catch (BuilderException& e) {
239 SG_LOG(SG_INPUT, SG_ALERT, "Error building technique: "
240 << e.getFormattedMessage());
244 return effect.release();