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.
19 #ifndef _SG_MODELREGISTRY_HXX
20 #define _SG_MODELREGISTRY_HXX 1
22 #include <OpenThreads/ReentrantMutex>
24 #include <osg/ref_ptr>
26 #include <osgDB/FileUtils>
27 #include <osgDB/FileNameUtils>
28 #include <osgDB/ReaderWriter>
29 #include <osgDB/Registry>
31 #include <simgear/compiler.h>
36 // Class to register per file extension read callbacks with the OSG
37 // registry, mostly to control caching and post load optimization /
38 // copying that happens above the level of the ReaderWriter.
42 // Different caching and optimization strategies are needed for
43 // different file types. Most loaded files should be optimized and the
44 // optimized version should be cached. When an .osg file is
45 // substituted for another, it is assumed to be optimized already but
46 // it should be cached too (under the name of the original?). .stg
47 // files should not be cached (that's the pager's job) but the files
48 // it causes to be loaded should be. .btg files are already optimized
49 // and shouldn't be cached.
51 // Complicating this is the effect that removing CACHE_NODES has from
52 // the ReaderWriter options: it switches the object cache with an
53 // empty one, so that's not an option for the files that could be
54 // loaded from a .stg file. So, we'll let
55 // Registry::readNodeImplementation cache a loaded file and then add
56 // the optimized version to the cache ourselves, replacing the
59 // To support all these options with a minimum of duplication, the
60 // readNode function is specified as a template with a bunch of
61 // pluggable (and predefined) policies.
62 template <typename ProcessPolicy, typename CachePolicy, typename OptimizePolicy,
63 typename CopyPolicy, typename SubstitutePolicy>
64 class ModelRegistryCallback : public osgDB::Registry::ReadFileCallback {
66 ModelRegistryCallback(const std::string& extension) :
67 _processPolicy(extension), _cachePolicy(extension),
68 _optimizePolicy(extension), _copyPolicy(extension),
69 _substitutePolicy(extension)
72 virtual osgDB::ReaderWriter::ReadResult
73 readNode(const std::string& fileName,
74 const osgDB::ReaderWriter::Options* opt)
77 using namespace osgDB;
78 using osgDB::ReaderWriter;
79 Registry* registry = Registry::instance();
80 ref_ptr<osg::Node> optimizedNode = _cachePolicy.find(fileName, opt);
81 if (!optimizedNode.valid()) {
82 std::string otherFileName = _substitutePolicy.substitute(fileName,
84 ReaderWriter::ReadResult res;
85 if (!otherFileName.empty()) {
86 res = loadUsingReaderWriter(otherFileName, opt);
88 optimizedNode = res.getNode();
90 if (!optimizedNode.valid()) {
91 res = loadUsingReaderWriter(fileName, opt);
94 ref_ptr<osg::Node> processedNode
95 = _processPolicy.process(res.getNode(), fileName, opt);
96 optimizedNode = _optimizePolicy.optimize(processedNode.get(),
99 _cachePolicy.addToCache(fileName, optimizedNode.get());
101 return ReaderWriter::ReadResult(_copyPolicy.copy(optimizedNode.get(),
106 static osgDB::ReaderWriter::ReadResult
107 loadUsingReaderWriter(const std::string& fileName,
108 const osgDB::ReaderWriter::Options* opt)
110 using namespace osgDB;
111 ReaderWriter* rw = Registry::instance()
112 ->getReaderWriterForExtension(osgDB::getFileExtension(fileName));
114 return ReaderWriter::ReadResult(); // FILE_NOT_HANDLED
115 return rw->readNode(fileName, opt);
118 ProcessPolicy _processPolicy;
119 CachePolicy _cachePolicy;
120 OptimizePolicy _optimizePolicy;
121 CopyPolicy _copyPolicy;
122 SubstitutePolicy _substitutePolicy;
123 virtual ~ModelRegistryCallback() {}
126 // Predefined policies
128 struct DefaultProcessPolicy {
129 DefaultProcessPolicy(const std::string& extension) {}
130 osg::Node* process(osg::Node* node, const std::string& filename,
131 const osgDB::ReaderWriter::Options* opt);
134 struct DefaultCachePolicy {
135 DefaultCachePolicy(const std::string& extension) {}
136 osg::Node* find(const std::string& fileName,
137 const osgDB::ReaderWriter::Options* opt);
138 void addToCache(const std::string& filename, osg::Node* node);
141 struct NoCachePolicy {
142 NoCachePolicy(const std::string& extension) {}
143 osg::Node* find(const std::string& fileName,
144 const osgDB::ReaderWriter::Options* opt)
148 void addToCache(const std::string& filename, osg::Node* node) {}
151 class OptimizeModelPolicy {
153 OptimizeModelPolicy(const std::string& extension);
154 osg::Node* optimize(osg::Node* node, const std::string& fileName,
155 const osgDB::ReaderWriter::Options* opt);
157 unsigned _osgOptions;
160 struct NoOptimizePolicy {
161 NoOptimizePolicy(const std::string& extension) {}
162 osg::Node* optimize(osg::Node* node, const std::string& fileName,
163 const osgDB::ReaderWriter::Options* opt)
169 struct DefaultCopyPolicy {
170 DefaultCopyPolicy(const std::string& extension) {}
171 osg::Node* copy(osg::Node* node, const std::string& fileName,
172 const osgDB::ReaderWriter::Options* opt);
175 struct NoCopyPolicy {
176 NoCopyPolicy(const std::string& extension) {}
177 osg::Node* copy(osg::Node* node, const std::string& fileName,
178 const osgDB::ReaderWriter::Options* opt)
184 struct OSGSubstitutePolicy {
185 OSGSubstitutePolicy(const std::string& extension) {}
186 std::string substitute(const std::string& name,
187 const osgDB::ReaderWriter::Options* opt);
190 struct NoSubstitutePolicy {
191 NoSubstitutePolicy(const std::string& extension) {}
192 std::string substitute(const std::string& name,
193 const osgDB::ReaderWriter::Options* opt)
195 return std::string();
198 typedef ModelRegistryCallback<DefaultProcessPolicy, DefaultCachePolicy,
199 OptimizeModelPolicy, DefaultCopyPolicy,
200 OSGSubstitutePolicy> DefaultCallback;
202 // The manager for the callbacks
203 class ModelRegistry : public osgDB::Registry::ReadFileCallback {
206 virtual osgDB::ReaderWriter::ReadResult
207 readImage(const std::string& fileName,
208 const osgDB::ReaderWriter::Options* opt);
209 virtual osgDB::ReaderWriter::ReadResult
210 readNode(const std::string& fileName,
211 const osgDB::ReaderWriter::Options* opt);
212 void addImageCallbackForExtension(const std::string& extension,
213 osgDB::Registry::ReadFileCallback*
215 void addNodeCallbackForExtension(const std::string& extension,
216 osgDB::Registry::ReadFileCallback*
218 static ModelRegistry* getInstance();
219 virtual ~ModelRegistry() {}
221 static osg::ref_ptr<ModelRegistry> instance;
222 typedef std::map<std::string, osg::ref_ptr<osgDB::Registry::ReadFileCallback> >
224 CallbackMap imageCallbackMap;
225 CallbackMap nodeCallbackMap;
226 osg::ref_ptr<DefaultCallback> _defaultCallback;
227 // Protect against simultaneous calls from main thread (MP models)
229 OpenThreads::ReentrantMutex readerMutex;
232 // Callback that only loads the file without any caching or
234 typedef ModelRegistryCallback<DefaultProcessPolicy, NoCachePolicy,
235 NoOptimizePolicy, NoCopyPolicy,
236 NoSubstitutePolicy> LoadOnlyCallback;
238 // Proxy for registering extension-based callbacks
241 class ModelRegistryCallbackProxy
244 ModelRegistryCallbackProxy(std::string extension)
246 ModelRegistry::getInstance()
247 ->addNodeCallbackForExtension(extension, new T(extension));
251 #endif // _SG_MODELREGISTRY_HXX