1 // model.cxx - manage a 3D aircraft model.
2 // Written by David Megginson, started 2002.
3 //
4 // This file is in the Public Domain, and comes with no warranty.
5
6 #ifdef HAVE_CONFIG_H
7 #include <simgear_config.h>
8 #endif
9
10 #include <utility>
11
12 #include <osg/ref_ptr>
13 #include <osgDB/FileNameUtils>
14 #include <osgDB/FileUtils>
15 #include <osgDB/ReaderWriter>
16 #include <osgDB/ReadFile>
17 #include <osgDB/SharedStateManager>
18
19 #include <simgear/scene/material/Effect.hxx>
20 #include <simgear/scene/material/EffectGeode.hxx>
21 #include <simgear/scene/util/SGSceneFeatures.hxx>
22 #include <simgear/scene/util/SGSceneUserData.hxx>
23 #include <simgear/scene/util/CopyOp.hxx>
24 #include <simgear/scene/util/SplicingVisitor.hxx>
25 #include <simgear/scene/util/SGReaderWriterOptions.hxx>
26
27 #include <simgear/debug/ErrorReportingCallback.hxx>
28 #include <simgear/props/condition.hxx>
29 #include <simgear/props/props.hxx>
30 #include <simgear/props/props_io.hxx>
31 #include <simgear/structure/Singleton.hxx>
32 #include <simgear/structure/exception.hxx>
33
34 #include "model.hxx"
35
36 using std::vector;
37
38 osg::Texture2D*
SGLoadTexture2D(bool staticTexture,const std::string & path,const osgDB::Options * options,bool wrapu,bool wrapv,int)39 SGLoadTexture2D(bool staticTexture, const std::string& path,
40 const osgDB::Options* options,
41 bool wrapu, bool wrapv, int)
42 {
43 osg::ref_ptr<osg::Image> image;
44 if (options)
45 #if OSG_VERSION_LESS_THAN(3,4,0)
46 image = osgDB::readImageFile(path, options);
47 #else
48 image = osgDB::readRefImageFile(path, options);
49 #endif
50 else
51 #if OSG_VERSION_LESS_THAN(3,4,0)
52 image = osgDB::readImageFile(path);
53 #else
54 image = osgDB::readRefImageFile(path);
55 #endif
56
57 osg::ref_ptr<osg::Texture2D> texture = new osg::Texture2D;
58 texture->setImage(image);
59 texture->setMaxAnisotropy(SGSceneFeatures::instance()->getTextureFilter());
60
61 if (staticTexture)
62 texture->setDataVariance(osg::Object::STATIC);
63 if (wrapu)
64 texture->setWrap(osg::Texture::WRAP_S, osg::Texture::REPEAT);
65 else
66 texture->setWrap(osg::Texture::WRAP_S, osg::Texture::CLAMP);
67 if (wrapv)
68 texture->setWrap(osg::Texture::WRAP_T, osg::Texture::REPEAT);
69 else
70 texture->setWrap(osg::Texture::WRAP_T, osg::Texture::CLAMP);
71
72 if (image) {
73 int s = image->s();
74 int t = image->t();
75
76 if (s <= t && 32 <= s) {
77 SGSceneFeatures::instance()->applyTextureCompression(texture.get());
78 } else if (t < s && 32 <= t) {
79 SGSceneFeatures::instance()->applyTextureCompression(texture.get());
80 }
81 }
82
83 return texture.release();
84 }
85
86 namespace simgear
87 {
88 using namespace std;
89 using namespace osg;
90 using simgear::CopyOp;
91
copyModel(Node * model)92 Node* copyModel(Node* model)
93 {
94 const CopyOp::CopyFlags flags = (CopyOp::DEEP_COPY_ALL
95 & ~CopyOp::DEEP_COPY_TEXTURES
96 & ~CopyOp::DEEP_COPY_IMAGES
97 & ~CopyOp::DEEP_COPY_STATESETS
98 & ~CopyOp::DEEP_COPY_STATEATTRIBUTES
99 & ~CopyOp::DEEP_COPY_ARRAYS
100 & ~CopyOp::DEEP_COPY_PRIMITIVES
101 // This will preserve display lists ...
102 & ~CopyOp::DEEP_COPY_DRAWABLES
103 & ~CopyOp::DEEP_COPY_SHAPES);
104 return (CopyOp(flags))(model);
105 }
106
TextureUpdateVisitor(const osgDB::FilePathList & pathList)107 TextureUpdateVisitor::TextureUpdateVisitor(const osgDB::FilePathList& pathList) :
108 NodeAndDrawableVisitor(NodeVisitor::TRAVERSE_ALL_CHILDREN),
109 _pathList(pathList)
110 {
111 }
112
apply(osg::Node & node)113 void TextureUpdateVisitor::apply(osg::Node& node)
114 {
115 StateSet* stateSet = cloneStateSet(node.getStateSet());
116 if (stateSet)
117 node.setStateSet(stateSet);
118 traverse(node);
119 }
120
apply(Drawable & drawable)121 void TextureUpdateVisitor::apply(Drawable& drawable)
122 {
123 StateSet* stateSet = cloneStateSet(drawable.getStateSet());
124 if (stateSet)
125 drawable.setStateSet(stateSet);
126 }
127
textureReplace(int unit,const StateAttribute * attr)128 Texture2D* TextureUpdateVisitor::textureReplace(int unit, const StateAttribute* attr)
129 {
130 using namespace osgDB;
131 const Texture2D* texture = dynamic_cast<const Texture2D*>(attr);
132
133 if (!texture)
134 return 0;
135
136 const Image* image = texture->getImage();
137 const string* fullFilePath = 0;
138 if (image) {
139 // The currently loaded file name
140 fullFilePath = &image->getFileName();
141 } else {
142 fullFilePath = &texture->getName();
143 }
144
145 // The short name
146 string fileName = getSimpleFileName(*fullFilePath);
147 if (fileName.empty())
148 return 0;
149
150 // The name that should be found with the current database path
151 string fullLiveryFile = findFileInPath(fileName, _pathList);
152 // If it is empty or they are identical then there is nothing to do
153 if (fullLiveryFile.empty() || fullLiveryFile == *fullFilePath)
154 return 0;
155
156 #if OSG_VERSION_LESS_THAN(3,4,0)
157 Image* newImage = readImageFile(fullLiveryFile);
158 #else
159 osg::ref_ptr<Image> newImage = readRefImageFile(fullLiveryFile);
160 #endif
161 if (!newImage)
162 return 0;
163
164 CopyOp copyOp(CopyOp::DEEP_COPY_ALL & ~CopyOp::DEEP_COPY_IMAGES);
165 Texture2D* newTexture = static_cast<Texture2D*>(copyOp(texture));
166 if (!newTexture)
167 return 0;
168
169 newTexture->setImage(newImage);
170 #if OSG_VERSION_LESS_THAN(3,4,0)
171 if (newImage->valid())
172 #else
173 if (newImage.valid())
174 #endif
175 {
176 newTexture->setMaxAnisotropy(SGSceneFeatures::instance()->getTextureFilter());
177 }
178
179 return newTexture;
180 }
181
cloneStateSet(const StateSet * stateSet)182 StateSet* TextureUpdateVisitor::cloneStateSet(const StateSet* stateSet)
183 {
184 typedef std::pair<int, Texture2D*> Tex2D;
185 vector<Tex2D> newTextures;
186 StateSet* result = 0;
187
188 if (!stateSet)
189 return 0;
190 int numUnits = stateSet->getTextureAttributeList().size();
191 if (numUnits > 0) {
192 for (int i = 0; i < numUnits; ++i) {
193 const StateAttribute* attr
194 = stateSet->getTextureAttribute(i, StateAttribute::TEXTURE);
195 Texture2D* newTexture = textureReplace(i, attr);
196 if (newTexture)
197 newTextures.push_back(Tex2D(i, newTexture));
198 }
199 if (!newTextures.empty()) {
200 result = static_cast<StateSet*>(stateSet->clone(CopyOp()));
201 for (vector<Tex2D>::iterator i = newTextures.begin();
202 i != newTextures.end();
203 ++i) {
204 result->setTextureAttribute(i->first, i->second);
205 }
206 }
207 }
208 return result;
209 }
210
UserDataCopyVisitor()211 UserDataCopyVisitor::UserDataCopyVisitor() :
212 NodeVisitor(NodeVisitor::NODE_VISITOR,
213 NodeVisitor::TRAVERSE_ALL_CHILDREN)
214 {
215 }
216
apply(Node & node)217 void UserDataCopyVisitor::apply(Node& node)
218 {
219 ref_ptr<SGSceneUserData> userData;
220 userData = SGSceneUserData::getSceneUserData(&node);
221 if (userData.valid()) {
222 SGSceneUserData* newUserData = new SGSceneUserData(*userData);
223 newUserData->setVelocity(0);
224 node.setUserData(newUserData);
225 }
226 node.traverse(*this);
227 }
228
229 namespace
230 {
231 class MakeEffectVisitor : public SplicingVisitor
232 {
233 public:
234 typedef std::map<string, SGPropertyNode_ptr> EffectMap;
235 using SplicingVisitor::apply;
MakeEffectVisitor(const SGReaderWriterOptions * options=0)236 MakeEffectVisitor(const SGReaderWriterOptions* options = 0)
237 : _options(options)
238 {
239 }
240 virtual void apply(osg::Group& node);
241 virtual void apply(osg::Geode& geode);
getEffectMap()242 EffectMap& getEffectMap() { return _effectMap; }
getEffectMap() const243 const EffectMap& getEffectMap() const { return _effectMap; }
setDefaultEffect(SGPropertyNode * effect)244 void setDefaultEffect(SGPropertyNode* effect)
245 {
246 _currentEffectParent = effect;
247 }
getDefaultEffect()248 SGPropertyNode* getDefaultEffect() { return _currentEffectParent; }
249 protected:
250 EffectMap _effectMap;
251 SGPropertyNode_ptr _currentEffectParent;
252 osg::ref_ptr<const SGReaderWriterOptions> _options;
253 };
254
apply(osg::Group & node)255 void MakeEffectVisitor::apply(osg::Group& node)
256 {
257 SGPropertyNode_ptr savedEffectRoot;
258 const string& nodeName = node.getName();
259 bool restoreEffect = false;
260 if (!nodeName.empty()) {
261 EffectMap::iterator eitr = _effectMap.find(nodeName);
262 if (eitr != _effectMap.end()) {
263 savedEffectRoot = _currentEffectParent;
264 _currentEffectParent = eitr->second;
265 restoreEffect = true;
266 }
267 }
268 SplicingVisitor::apply(node);
269 // If a new node was created, copy the user data too.
270 ref_ptr<SGSceneUserData> userData = SGSceneUserData::getSceneUserData(&node);
271 if (userData.valid() && _childStack.back().back().get() != &node)
272 _childStack.back().back()->setUserData(new SGSceneUserData(*userData));
273 if (restoreEffect)
274 _currentEffectParent = savedEffectRoot;
275 }
276
apply(osg::Geode & geode)277 void MakeEffectVisitor::apply(osg::Geode& geode)
278 {
279 if (pushNode(getNewNode(geode)))
280 return;
281 osg::StateSet* ss = geode.getStateSet();
282 if (!ss) {
283 pushNode(&geode);
284 return;
285 }
286 SGPropertyNode_ptr ssRoot = new SGPropertyNode;
287 makeParametersFromStateSet(ssRoot, ss);
288 SGPropertyNode_ptr effectRoot = new SGPropertyNode;
289 effect::mergePropertyTrees(effectRoot, ssRoot, _currentEffectParent);
290 Effect* effect = makeEffect(effectRoot, true, _options.get());
291 EffectGeode* eg = dynamic_cast<EffectGeode*>(&geode);
292 if (eg) {
293 eg->setEffect(effect);
294 } else {
295 eg = new EffectGeode;
296 eg->setEffect(effect);
297 ref_ptr<SGSceneUserData> userData = SGSceneUserData::getSceneUserData(&geode);
298 if (userData.valid())
299 eg->setUserData(new SGSceneUserData(*userData));
300 for (unsigned i = 0; i < geode.getNumDrawables(); ++i) {
301 osg::Drawable *drawable = geode.getDrawable(i);
302 eg->addDrawable(drawable);
303
304 // Generate tangent vectors etc if needed
305 osg::Geometry *geom = dynamic_cast<osg::Geometry*>(drawable);
306 if(geom) eg->runGenerators(geom);
307 }
308 }
309 pushResultNode(&geode, eg);
310
311 }
312
313 }
314
315 namespace
316 {
317 class DefaultEffect : public simgear::Singleton<DefaultEffect>
318 {
319 public:
DefaultEffect()320 DefaultEffect()
321 {
322 _effect = new SGPropertyNode;
323 makeChild(_effect.ptr(), "inherits-from")
324 ->setStringValue("Effects/model-default");
325 }
~DefaultEffect()326 virtual ~DefaultEffect() {}
getEffect()327 SGPropertyNode* getEffect() { return _effect.ptr(); }
328 protected:
329 SGPropertyNode_ptr _effect;
330 };
331 }
332
instantiateEffects(osg::Node * modelGroup,PropertyList & effectProps,const SGReaderWriterOptions * options)333 ref_ptr<Node> instantiateEffects(osg::Node* modelGroup,
334 PropertyList& effectProps,
335 const SGReaderWriterOptions* options)
336 {
337 SGPropertyNode_ptr defaultEffectPropRoot;
338 MakeEffectVisitor visitor(options);
339 MakeEffectVisitor::EffectMap& emap = visitor.getEffectMap();
340 for (PropertyList::iterator itr = effectProps.begin(),
341 end = effectProps.end();
342 itr != end;
343 ++itr)
344 {
345 SGPropertyNode_ptr configNode = *itr;
346 std::vector<SGPropertyNode_ptr> objectNames =
347 configNode->getChildren("object-name");
348 SGPropertyNode* defaultNode = configNode->getChild("default");
349 if (defaultNode && defaultNode->getValue<bool>())
350 defaultEffectPropRoot = configNode;
351 for (auto objNameNode : objectNames) {
352 emap.insert(make_pair(objNameNode->getStringValue(), configNode));
353 }
354 configNode->removeChild("default");
355 configNode->removeChildren("object-name");
356 }
357 if (!defaultEffectPropRoot)
358 defaultEffectPropRoot = DefaultEffect::instance()->getEffect();
359 visitor.setDefaultEffect(defaultEffectPropRoot.ptr());
360 modelGroup->accept(visitor);
361 osg::NodeList& result = visitor.getResults();
362 return ref_ptr<Node>(result[0].get());
363 }
364
instantiateMaterialEffects(osg::Node * modelGroup,const SGReaderWriterOptions * options)365 ref_ptr<Node> instantiateMaterialEffects(osg::Node* modelGroup,
366 const SGReaderWriterOptions* options)
367 {
368
369 SGPropertyNode_ptr effect;
370 PropertyList effectProps;
371
372 if (options->getMaterialLib()) {
373 const SGGeod loc = SGGeod(options->getLocation());
374 SGMaterialCache* matcache = options->getMaterialLib()->generateMatCache(loc);
375 SGMaterial* mat = matcache->find(options->getMaterialName());
376 delete matcache;
377
378 if (mat) {
379 effect = new SGPropertyNode();
380 makeChild(effect, "inherits-from")->setStringValue(mat->get_effect_name());
381 } else {
382 effect = DefaultEffect::instance()->getEffect();
383 SG_LOG( SG_TERRAIN, SG_ALERT, "Unable to get effect for " << options->getMaterialName());
384 simgear::reportFailure(simgear::LoadFailure::NotFound, simgear::ErrorCode::LoadEffectsShaders,
385 "Unable to get effect for material:" + options->getMaterialName());
386 }
387 } else {
388 effect = DefaultEffect::instance()->getEffect();
389 }
390
391 effect->addChild("default")->setBoolValue(true);
392 effectProps.push_back(effect);
393 return instantiateEffects(modelGroup, effectProps, options);
394 }
395
396 }
397 // end of model.cxx
398