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/scene/model/SGReaderWriterXMLOptions.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>
44 using namespace effect;
46 typedef vector<const SGPropertyNode*> RawPropVector;
47 typedef map<const string, ref_ptr<Effect> > EffectMap;
52 OpenThreads::ReentrantMutex effectMutex;
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.
62 * Nodes are considered equal if their names and indexes are equal.
66 : public unary_function<const SGPropertyNode*, bool>
68 PropPredicate(const SGPropertyNode* node_) : node(node_) {}
69 bool operator()(const SGPropertyNode* arg) const
71 if (strcmp(node->getName(), arg->getName()))
73 return node->getIndex() == arg->getIndex();
75 const SGPropertyNode* node;
80 void mergePropertyTrees(SGPropertyNode* resultNode,
81 const SGPropertyNode* left, const SGPropertyNode* right)
83 if (left->nChildren() == 0) {
84 copyProperties(left, resultNode);
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(),
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);
103 copyProperties(node, newChild);
106 // Now copy nodes remaining in the left tree
107 for (RawPropVector::iterator itr = leftChildren.begin(),
108 e = leftChildren.end();
111 SGPropertyNode* newChild
112 = resultNode->getChild((*itr)->getName(), (*itr)->getIndex(), true);
113 copyProperties(*itr, newChild);
118 Effect* makeEffect(const string& name,
119 bool realizeTechniques,
120 const SGReaderWriterXMLOptions* options)
123 OpenThreads::ScopedLock<OpenThreads::ReentrantMutex> lock(effectMutex);
124 EffectMap::iterator itr = effectMap.find(name);
125 if (itr != effectMap.end())
126 return itr->second.get();
128 string effectFileName(name);
129 effectFileName += ".eff";
131 = osgDB::findDataFile(effectFileName, options);
132 if (absFileName.empty()) {
133 SG_LOG(SG_INPUT, SG_ALERT, "can't find \"" << effectFileName << "\"");
136 SGPropertyNode_ptr effectProps = new SGPropertyNode();
138 readProperties(absFileName, effectProps.ptr(), 0, true);
140 catch (sg_io_exception& e) {
141 SG_LOG(SG_INPUT, SG_ALERT, "error reading \"" << absFileName << "\": "
142 << e.getFormattedMessage());
145 ref_ptr<Effect> result = makeEffect(effectProps.ptr(), realizeTechniques,
147 if (result.valid()) {
148 OpenThreads::ScopedLock<OpenThreads::ReentrantMutex> lock(effectMutex);
149 pair<EffectMap::iterator, bool> irslt
150 = effectMap.insert(make_pair(name, result));
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;
157 return result.release();
161 Effect* makeEffect(SGPropertyNode* prop,
162 bool realizeTechniques,
163 const SGReaderWriterXMLOptions* options)
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));
188 ref_ptr<Effect> effect;
189 // Merge with the parent effect, if any
190 SGPropertyNode_ptr inheritProp = prop->getChild("inherits-from");
193 //prop->removeChild("inherits-from");
194 parent = makeEffect(inheritProp->getStringValue(), false, options);
199 key.paths = options->getDatabasePathList();
201 Effect::Cache* cache = 0;
202 Effect::Cache::iterator itr;
204 OpenThreads::ScopedLock<OpenThreads::ReentrantMutex>
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
213 if (!effect.valid()) {
215 effect->root = new SGPropertyNode;
216 mergePropertyTrees(effect->root, prop, parent->root);
217 effect->parametersProp = effect->root->getChild("parameters");
218 OpenThreads::ScopedLock<OpenThreads::ReentrantMutex>
220 pair<Effect::Cache::iterator, bool> irslt
221 = cache->insert(make_pair(key, effect));
223 effect = irslt.first->second;
224 effect->generator = parent->generator; // Copy the generators
227 SG_LOG(SG_INPUT, SG_ALERT, "can't find base effect " <<
228 inheritProp->getStringValue());
234 effect->parametersProp = effect->root->getChild("parameters");
236 const SGPropertyNode *generateProp = prop->getChild("generate");
239 effect->generator.clear();
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());
245 parameter = generateProp->getChild("tangent");
246 if(parameter) effect->setGenerator(Effect::TANGENT, parameter->getIntValue());
248 parameter = generateProp->getChild("binormal");
249 if(parameter) effect->setGenerator(Effect::BINORMAL, parameter->getIntValue());
251 if (realizeTechniques) {
253 OpenThreads::ScopedLock<OpenThreads::ReentrantMutex>
255 effect->realizeTechniques(options);
257 catch (BuilderException& e) {
258 SG_LOG(SG_INPUT, SG_ALERT, "Error building technique: "
259 << e.getFormattedMessage());
263 return effect.release();