1 // ModelRegistry.hxx -- interface to the OSG model registry
3 // Copyright (C) 2005-2007 Mathias Froehlich
4 // Copyright (C) 2007 Tim Moore <timoore@redhat.com>
6 // This program is free software; you can redistribute it and/or
7 // modify it under the terms of the GNU General Public License as
8 // published by the Free Software Foundation; either version 2 of the
9 // License, or (at your option) any later version.
11 // This program is distributed in the hope that it will be useful, but
12 // WITHOUT ANY WARRANTY; without even the implied warranty of
13 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 // General Public License for more details.
16 // You should have received a copy of the GNU General Public License
17 // along with this program; if not, write to the Free Software
18 // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
21 # include <simgear_config.h>
24 #include "ModelRegistry.hxx"
30 #include <OpenThreads/ScopedLock>
32 #include <osg/observer_ptr>
33 #include <osg/ref_ptr>
35 #include <osg/NodeCallback>
37 #include <osg/Material>
38 #include <osg/MatrixTransform>
39 #include <osgDB/Archive>
40 #include <osgDB/FileNameUtils>
41 #include <osgDB/FileUtils>
42 #include <osgDB/ReadFile>
43 #include <osgDB/WriteFile>
44 #include <osgDB/Registry>
45 #include <osgDB/SharedStateManager>
46 #include <osgUtil/Optimizer>
48 #include <simgear/scene/util/SGSceneFeatures.hxx>
49 #include <simgear/scene/util/SGStateAttributeVisitor.hxx>
50 #include <simgear/scene/util/SGTextureStateAttributeVisitor.hxx>
51 #include <simgear/scene/util/NodeAndDrawableVisitor.hxx>
53 #include <simgear/structure/exception.hxx>
54 #include <simgear/props/props.hxx>
55 #include <simgear/props/props_io.hxx>
56 #include <simgear/props/condition.hxx>
58 #include "BoundingVolumeBuildVisitor.hxx"
62 using namespace osgUtil;
63 using namespace osgDB;
64 using namespace simgear;
66 using OpenThreads::ReentrantMutex;
67 using OpenThreads::ScopedLock;
69 // Little helper class that holds an extra reference to a
71 // Since we clone all structural nodes from our 3d models,
72 // the database pager will only see one single reference to
73 // top node of the model and expire it relatively fast.
74 // We attach that extra reference to every model cloned from
75 // a base model in the pager. When that cloned model is deleted
76 // this extra reference is deleted too. So if there are no
77 // cloned models left the model will expire.
79 class SGDatabaseReference : public Observer {
81 SGDatabaseReference(Referenced* referenced) :
82 mReferenced(referenced)
84 virtual void objectDeleted(void*)
89 ref_ptr<Referenced> mReferenced;
92 // Set the name of a Texture to the simple name of its image
93 // file. This can be used to do livery substitution after the image
94 // has been deallocated.
95 class TextureNameVisitor : public NodeAndDrawableVisitor {
97 TextureNameVisitor(NodeVisitor::TraversalMode tm = NodeVisitor::TRAVERSE_ALL_CHILDREN) :
98 NodeAndDrawableVisitor(tm)
102 virtual void apply(Node& node)
104 nameTextures(node.getStateSet());
108 virtual void apply(Drawable& drawable)
110 nameTextures(drawable.getStateSet());
113 void nameTextures(StateSet* stateSet)
117 int numUnits = stateSet->getTextureAttributeList().size();
118 for (int i = 0; i < numUnits; ++i) {
120 = stateSet->getTextureAttribute(i, StateAttribute::TEXTURE);
121 Texture2D* texture = dynamic_cast<Texture2D*>(attr);
122 if (!texture || !texture->getName().empty())
124 const Image *image = texture->getImage();
127 texture->setName(image->getFileName());
132 // Change the StateSets of a model to hold different textures based on
135 class TextureUpdateVisitor : public NodeAndDrawableVisitor {
137 TextureUpdateVisitor(const FilePathList& pathList) :
138 NodeAndDrawableVisitor(NodeVisitor::TRAVERSE_ALL_CHILDREN),
143 virtual void apply(Node& node)
145 StateSet* stateSet = cloneStateSet(node.getStateSet());
147 node.setStateSet(stateSet);
151 virtual void apply(Drawable& drawable)
153 StateSet* stateSet = cloneStateSet(drawable.getStateSet());
155 drawable.setStateSet(stateSet);
157 // Copied from Mathias' earlier SGTextureUpdateVisitor
159 Texture2D* textureReplace(int unit, const StateAttribute* attr)
161 const Texture2D* texture = dynamic_cast<const Texture2D*>(attr);
166 const Image* image = texture->getImage();
167 const string* fullFilePath = 0;
169 // The currently loaded file name
170 fullFilePath = &image->getFileName();
173 fullFilePath = &texture->getName();
176 string fileName = getSimpleFileName(*fullFilePath);
177 if (fileName.empty())
179 // The name that should be found with the current database path
180 string fullLiveryFile = findFileInPath(fileName, _pathList);
181 // If it is empty or they are identical then there is nothing to do
182 if (fullLiveryFile.empty() || fullLiveryFile == *fullFilePath)
184 Image* newImage = readImageFile(fullLiveryFile);
187 CopyOp copyOp(CopyOp::DEEP_COPY_ALL & ~CopyOp::DEEP_COPY_IMAGES);
188 Texture2D* newTexture = static_cast<Texture2D*>(copyOp(texture));
192 newTexture->setImage(newImage);
197 StateSet* cloneStateSet(const StateSet* stateSet)
199 typedef pair<int, Texture2D*> Tex2D;
200 vector<Tex2D> newTextures;
201 StateSet* result = 0;
205 int numUnits = stateSet->getTextureAttributeList().size();
207 for (int i = 0; i < numUnits; ++i) {
208 const StateAttribute* attr
209 = stateSet->getTextureAttribute(i, StateAttribute::TEXTURE);
210 Texture2D* newTexture = textureReplace(i, attr);
212 newTextures.push_back(Tex2D(i, newTexture));
214 if (!newTextures.empty()) {
215 result = static_cast<StateSet*>(stateSet->clone(CopyOp()));
216 for (vector<Tex2D>::iterator i = newTextures.begin();
217 i != newTextures.end();
219 result->setTextureAttribute(i->first, i->second);
226 FilePathList _pathList;
229 // Create new userdata structs in a copied model.
230 // The BVH trees are shared with the original model, but the velocity fields
231 // should usually be distinct fields for distinct models.
232 class UserDataCopyVisitor : public osg::NodeVisitor {
234 UserDataCopyVisitor() :
235 osg::NodeVisitor(osg::NodeVisitor::NODE_VISITOR,
236 osg::NodeVisitor::TRAVERSE_ALL_CHILDREN)
239 virtual void apply(osg::Node& node)
241 osg::ref_ptr<SGSceneUserData> userData;
242 userData = SGSceneUserData::getSceneUserData(&node);
243 if (userData.valid()) {
244 SGSceneUserData* newUserData = new SGSceneUserData(*userData);
245 newUserData->setVelocity(0);
246 node.setUserData(newUserData);
248 node.traverse(*this);
252 class SGTexCompressionVisitor : public SGTextureStateAttributeVisitor {
254 virtual void apply(int, StateSet::RefAttributePair& refAttr)
257 texture = dynamic_cast<Texture2D*>(refAttr.first.get());
261 // Do not touch dynamically generated textures.
262 if (texture->getReadPBuffer())
264 if (texture->getDataVariance() == osg::Object::DYNAMIC)
267 // If no image attached, we assume this one is dynamically generated
268 Image* image = texture->getImage(0);
275 if (s <= t && 32 <= s) {
276 SGSceneFeatures::instance()->setTextureCompression(texture);
277 } else if (t < s && 32 <= t) {
278 SGSceneFeatures::instance()->setTextureCompression(texture);
283 class SGTexDataVarianceVisitor : public SGTextureStateAttributeVisitor {
285 virtual void apply(int, StateSet::RefAttributePair& refAttr)
288 texture = dynamic_cast<Texture*>(refAttr.first.get());
292 // Cannot be static if this is a render to texture thing
293 if (texture->getReadPBuffer())
295 if (texture->getDataVariance() == osg::Object::DYNAMIC)
297 // If no image attached, we assume this one is dynamically generated
298 Image* image = texture->getImage(0);
302 texture->setDataVariance(Object::STATIC);
305 virtual void apply(StateSet* stateSet)
309 stateSet->setDataVariance(Object::STATIC);
310 SGTextureStateAttributeVisitor::apply(stateSet);
316 Node* DefaultProcessPolicy::process(Node* node, const string& filename,
317 const ReaderWriter::Options* opt)
319 TextureNameVisitor nameVisitor;
320 node->accept(nameVisitor);
324 ReaderWriter::ReadResult
325 ModelRegistry::readImage(const string& fileName,
326 const ReaderWriter::Options* opt)
328 ScopedLock<ReentrantMutex> lock(readerMutex);
329 CallbackMap::iterator iter
330 = imageCallbackMap.find(getFileExtension(fileName));
331 // XXX Workaround for OSG plugin bug
333 if (iter != imageCallbackMap.end() && iter->second.valid())
334 return iter->second->readImage(fileName, opt);
335 string absFileName = findDataFile(fileName, opt);
336 if (!fileExists(absFileName)) {
337 SG_LOG(SG_IO, SG_ALERT, "Cannot find image file \""
338 << fileName << "\"");
339 return ReaderWriter::ReadResult::FILE_NOT_FOUND;
342 Registry* registry = Registry::instance();
343 ReaderWriter::ReadResult res;
344 res = registry->readImageImplementation(absFileName, opt);
345 if (!res.success()) {
346 SG_LOG(SG_IO, SG_WARN, "Image loading failed:" << res.message());
350 if (res.loadedFromCache())
351 SG_LOG(SG_IO, SG_INFO, "Returning cached image \""
352 << res.getImage()->getFileName() << "\"");
354 SG_LOG(SG_IO, SG_INFO, "Reading image \""
355 << res.getImage()->getFileName() << "\"");
362 osg::Node* DefaultCachePolicy::find(const string& fileName,
363 const ReaderWriter::Options* opt)
365 Registry* registry = Registry::instance();
367 = dynamic_cast<Node*>(registry->getFromObjectCache(fileName));
369 SG_LOG(SG_IO, SG_INFO, "Got cached model \""
370 << fileName << "\"");
372 SG_LOG(SG_IO, SG_INFO, "Reading model \""
373 << fileName << "\"");
377 void DefaultCachePolicy::addToCache(const string& fileName,
380 Registry::instance()->addEntryToObjectCache(fileName, node);
383 // Optimizations we don't use:
384 // Don't use this one. It will break animation names ...
385 // opts |= osgUtil::Optimizer::REMOVE_REDUNDANT_NODES;
387 // opts |= osgUtil::Optimizer::REMOVE_LOADED_PROXY_NODES;
388 // opts |= osgUtil::Optimizer::COMBINE_ADJACENT_LODS;
389 // opts |= osgUtil::Optimizer::CHECK_GEOMETRY;
390 // opts |= osgUtil::Optimizer::SPATIALIZE_GROUPS;
391 // opts |= osgUtil::Optimizer::COPY_SHARED_NODES;
392 // opts |= osgUtil::Optimizer::TESSELATE_GEOMETRY;
393 // opts |= osgUtil::Optimizer::OPTIMIZE_TEXTURE_SETTINGS;
395 OptimizeModelPolicy::OptimizeModelPolicy(const string& extension) :
396 _osgOptions(Optimizer::SHARE_DUPLICATE_STATE
397 | Optimizer::MERGE_GEOMETRY
398 | Optimizer::FLATTEN_STATIC_TRANSFORMS
399 | Optimizer::TRISTRIP_GEOMETRY)
403 osg::Node* OptimizeModelPolicy::optimize(osg::Node* node,
404 const string& fileName,
405 const osgDB::ReaderWriter::Options* opt)
407 osgUtil::Optimizer optimizer;
408 optimizer.optimize(node, _osgOptions);
410 // Make sure the data variance of sharable objects is set to
411 // STATIC so that textures will be globally shared.
412 SGTexDataVarianceVisitor dataVarianceVisitor;
413 node->accept(dataVarianceVisitor);
415 SGTexCompressionVisitor texComp;
416 node->accept(texComp);
420 osg::Node* DefaultCopyPolicy::copy(osg::Node* model, const string& fileName,
421 const osgDB::ReaderWriter::Options* opt)
423 // Add an extra reference to the model stored in the database.
424 // That is to avoid expiring the object from the cache even if it is still
425 // in use. Note that the object cache will think that a model is unused
426 // if the reference count is 1. If we clone all structural nodes here
427 // we need that extra reference to the original object
428 SGDatabaseReference* databaseReference;
429 databaseReference = new SGDatabaseReference(model);
430 CopyOp::CopyFlags flags = CopyOp::DEEP_COPY_ALL;
431 flags &= ~CopyOp::DEEP_COPY_TEXTURES;
432 flags &= ~CopyOp::DEEP_COPY_IMAGES;
433 flags &= ~CopyOp::DEEP_COPY_STATESETS;
434 flags &= ~CopyOp::DEEP_COPY_STATEATTRIBUTES;
435 flags &= ~CopyOp::DEEP_COPY_ARRAYS;
436 flags &= ~CopyOp::DEEP_COPY_PRIMITIVES;
437 // This will safe display lists ...
438 flags &= ~CopyOp::DEEP_COPY_DRAWABLES;
439 flags &= ~CopyOp::DEEP_COPY_SHAPES;
440 osg::Node* res = CopyOp(flags)(model);
441 res->addObserver(databaseReference);
444 TextureUpdateVisitor liveryUpdate(opt->getDatabasePathList());
445 res->accept(liveryUpdate);
447 // Copy the userdata fields, still sharing the boundingvolumes,
448 // but introducing new data for velocities.
449 UserDataCopyVisitor userDataCopyVisitor;
450 res->accept(userDataCopyVisitor);
455 string OSGSubstitutePolicy::substitute(const string& name,
456 const ReaderWriter::Options* opt)
458 string fileSansExtension = getNameLessExtension(name);
459 string osgFileName = fileSansExtension + ".osg";
460 string absFileName = findDataFile(osgFileName, opt);
466 BuildLeafBVHPolicy::buildBVH(const std::string& fileName, osg::Node* node)
468 SG_LOG(SG_IO, SG_INFO, "Building leaf attached boundingvolume tree for \""
469 << fileName << "\".");
470 BoundingVolumeBuildVisitor bvBuilder(true);
471 node->accept(bvBuilder);
475 BuildGroupBVHPolicy::buildBVH(const std::string& fileName, osg::Node* node)
477 SG_LOG(SG_IO, SG_INFO, "Building group attached boundingvolume tree for \""
478 << fileName << "\".");
479 BoundingVolumeBuildVisitor bvBuilder(false);
480 node->accept(bvBuilder);
484 NoBuildBVHPolicy::buildBVH(const std::string& fileName, osg::Node*)
486 SG_LOG(SG_IO, SG_INFO, "Omitting boundingvolume tree for \""
487 << fileName << "\".");
490 ModelRegistry::ModelRegistry() :
491 _defaultCallback(new DefaultCallback(""))
496 ModelRegistry::addImageCallbackForExtension(const string& extension,
497 Registry::ReadFileCallback* callback)
499 imageCallbackMap.insert(CallbackMap::value_type(extension, callback));
503 ModelRegistry::addNodeCallbackForExtension(const string& extension,
504 Registry::ReadFileCallback* callback)
506 nodeCallbackMap.insert(CallbackMap::value_type(extension, callback));
509 ReaderWriter::ReadResult
510 ModelRegistry::readNode(const string& fileName,
511 const ReaderWriter::Options* opt)
513 ScopedLock<ReentrantMutex> lock(readerMutex);
515 // XXX Workaround for OSG plugin bug.
516 Registry* registry = Registry::instance();
517 ReaderWriter::ReadResult res;
518 CallbackMap::iterator iter
519 = nodeCallbackMap.find(getFileExtension(fileName));
520 ReaderWriter::ReadResult result;
521 if (iter != nodeCallbackMap.end() && iter->second.valid())
522 result = iter->second->readNode(fileName, opt);
524 result = _defaultCallback->readNode(fileName, opt);
529 class SGReadCallbackInstaller {
531 SGReadCallbackInstaller()
533 // XXX I understand why we want this, but this seems like a weird
534 // place to set this option.
535 Referenced::setThreadSafeReferenceCounting(true);
537 Registry* registry = Registry::instance();
538 ReaderWriter::Options* options = new ReaderWriter::Options;
539 int cacheOptions = ReaderWriter::Options::CACHE_ALL;
541 setObjectCacheHint((ReaderWriter::Options::CacheHintOptions)cacheOptions);
542 registry->setOptions(options);
543 registry->getOrCreateSharedStateManager()->
544 setShareMode(SharedStateManager::SHARE_STATESETS);
545 registry->setReadFileCallback(ModelRegistry::instance());
549 static SGReadCallbackInstaller readCallbackInstaller;
551 // we get optimal geometry from the loader.
552 struct ACOptimizePolicy : public OptimizeModelPolicy {
553 ACOptimizePolicy(const string& extension) :
554 OptimizeModelPolicy(extension)
556 _osgOptions &= ~Optimizer::TRISTRIP_GEOMETRY;
558 Node* optimize(Node* node, const string& fileName,
559 const ReaderWriter::Options* opt)
561 ref_ptr<Node> optimized
562 = OptimizeModelPolicy::optimize(node, fileName, opt);
563 Group* group = dynamic_cast<Group*>(optimized.get());
564 MatrixTransform* transform
565 = dynamic_cast<MatrixTransform*>(optimized.get());
566 if (((transform && transform->getMatrix().isIdentity()) || group)
567 && group->getName().empty()
568 && group->getNumChildren() == 1) {
569 optimized = static_cast<Node*>(group->getChild(0));
570 group = dynamic_cast<Group*>(optimized.get());
571 if (group && group->getName().empty()
572 && group->getNumChildren() == 1)
573 optimized = static_cast<Node*>(group->getChild(0));
575 return optimized.release();
579 struct ACProcessPolicy {
580 ACProcessPolicy(const string& extension) {}
581 Node* process(Node* node, const string& filename,
582 const ReaderWriter::Options* opt)
588 // XXX Does there need to be a Group node here to trick the
589 // optimizer into optimizing the static transform?
590 osg::Group* root = new Group;
591 MatrixTransform* transform = new MatrixTransform;
592 root->addChild(transform);
594 transform->setDataVariance(Object::STATIC);
595 transform->setMatrix(m);
596 transform->addChild(node);
602 typedef ModelRegistryCallback<ACProcessPolicy, DefaultCachePolicy,
603 ACOptimizePolicy, DefaultCopyPolicy,
604 OSGSubstitutePolicy, BuildLeafBVHPolicy>
609 ModelRegistryCallbackProxy<ACCallback> g_acRegister("ac");