]> git.mxchange.org Git - simgear.git/blob - simgear/scene/model/BVHPageNodeOSG.cxx
Fixed a crash: the singleton needs to be instantiated the first time SGCommandMgr...
[simgear.git] / simgear / scene / model / BVHPageNodeOSG.cxx
1 // Copyright (C) 2008 - 2012  Mathias Froehlich - Mathias.Froehlich@web.de
2 //
3 // This library is free software; you can redistribute it and/or
4 // modify it under the terms of the GNU Library General Public
5 // License as published by the Free Software Foundation; either
6 // version 2 of the License, or (at your option) any later version.
7 //
8 // This library is distributed in the hope that it will be useful,
9 // but WITHOUT ANY WARRANTY; without even the implied warranty of
10 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
11 // Library General Public License for more details.
12 //
13 // You should have received a copy of the GNU General Public License
14 // along with this program; if not, write to the Free Software
15 // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
16 //
17
18 #ifdef HAVE_CONFIG_H
19 #  include <simgear_config.h>
20 #endif
21
22 #include "BVHPageNodeOSG.hxx"
23
24 #include "../../bvh/BVHPageRequest.hxx"
25 #include "../../bvh/BVHPager.hxx"
26
27 #include <osg/io_utils>
28 #include <osg/Camera>
29 #include <osg/Drawable>
30 #include <osg/Geode>
31 #include <osg/Group>
32 #include <osg/PagedLOD>
33 #include <osg/ProxyNode>
34 #include <osg/Transform>
35 #include <osgDB/ReadFile>
36
37 #include <simgear/scene/material/mat.hxx>
38 #include <simgear/scene/material/matlib.hxx>
39 #include <simgear/scene/util/SGNodeMasks.hxx>
40 #include <simgear/scene/util/OsgMath.hxx>
41 #include <simgear/scene/util/SGSceneUserData.hxx>
42 #include <simgear/math/SGGeometry.hxx>
43
44 #include <simgear/bvh/BVHStaticGeometryBuilder.hxx>
45
46 #include "PrimitiveCollector.hxx"
47
48 namespace simgear {
49
50 class BVHPageNodeOSG::_NodeVisitor : public osg::NodeVisitor {
51 public:
52     struct _PrimitiveCollector : public PrimitiveCollector {
53         _PrimitiveCollector(_NodeVisitor& nodeVisitor) :
54             _nodeVisitor(nodeVisitor)
55         { }
56         virtual ~_PrimitiveCollector()
57         { }
58         virtual void addPoint(const osg::Vec3d& v1)
59         { }
60         virtual void addLine(const osg::Vec3d& v1, const osg::Vec3d& v2)
61         { }
62         virtual void addTriangle(const osg::Vec3d& v1, const osg::Vec3d& v2, const osg::Vec3d& v3)
63         { _nodeVisitor.addTriangle(v1, v2, v3); }
64     private:
65         _NodeVisitor& _nodeVisitor;
66     };
67
68     struct _NodeBin {
69         SGSharedPtr<BVHNode> getNode(const osg::Matrix& matrix)
70         {
71             if (_nodeVector.empty())
72                 return SGSharedPtr<BVHNode>();
73             
74             if (!matrix.isIdentity()) {
75                 // If we have a non trivial matrix we need a
76                 // transform node in any case.
77                 SGSharedPtr<BVHTransform> transform = new BVHTransform;
78                 transform->setToWorldTransform(SGMatrixd(matrix.ptr()));
79                 for (_NodeVector::iterator i = _nodeVector.begin();
80                      i != _nodeVector.end(); ++i)
81                     transform->addChild(i->get());
82                 return transform;
83             } else {
84                 // If the matrix is an identity, return the
85                 // smallest possible subtree.
86                 if (_nodeVector.size() == 1)
87                     return _nodeVector.front();
88                 SGSharedPtr<BVHGroup> group = new BVHGroup;
89                 for (_NodeVector::iterator i = _nodeVector.begin();
90                      i != _nodeVector.end(); ++i)
91                     group->addChild(i->get());
92                 return group;
93             }
94         }
95         
96         void addNode(const SGSharedPtr<BVHNode>& node)
97         {
98             if (!node.valid())
99                 return;
100             if (node->getBoundingSphere().empty())
101                 return;
102             _nodeVector.push_back(node);
103         }
104         
105     private:
106         typedef std::vector<SGSharedPtr<BVHNode> > _NodeVector;
107         
108         // The current pending node vector.
109         _NodeVector _nodeVector;
110     };
111     
112     _NodeVisitor(bool flatten, const osg::Matrix& localToWorldMatrix = osg::Matrix()) :
113         osg::NodeVisitor(osg::NodeVisitor::TRAVERSE_ACTIVE_CHILDREN),
114         _localToWorldMatrix(localToWorldMatrix),
115         _geometryBuilder(new BVHStaticGeometryBuilder),
116         _flatten(flatten)
117     {
118         setTraversalMask(SG_NODEMASK_TERRAIN_BIT);
119     }
120     virtual ~_NodeVisitor()
121     {
122     }
123
124     void addTriangle(const osg::Vec3d& v1, const osg::Vec3d& v2, const osg::Vec3d& v3)
125     {
126         _geometryBuilder->addTriangle(toVec3f(toSG(_localToWorldMatrix.preMult(v1))),
127                                       toVec3f(toSG(_localToWorldMatrix.preMult(v2))),
128                                       toVec3f(toSG(_localToWorldMatrix.preMult(v3))));
129     }
130
131     void setCenter(const osg::Vec3& center)
132     {
133         _centerMatrix.preMultTranslate(center);
134         _localToWorldMatrix.postMultTranslate(-center);
135         if (1e6 < center.length()) {
136             SGGeod geod = SGGeod::fromCart(toVec3d(toSG(center)));
137             SGQuatd orientation = SGQuatd::fromLonLat(geod);
138             _centerMatrix.preMultRotate(toOsg(orientation));
139             _localToWorldMatrix.postMultRotate(toOsg(inverse(orientation)));
140         }
141     }
142
143     virtual void apply(osg::Geode& geode)
144     {
145         const BVHMaterial* oldMaterial = _geometryBuilder->getCurrentMaterial();
146         if (const BVHMaterial* material = SGMaterialLib::findMaterial(&geode))
147             _geometryBuilder->setCurrentMaterial(material);
148
149         _PrimitiveCollector primitiveCollector(*this);
150         for(unsigned i = 0; i < geode.getNumDrawables(); ++i)
151             geode.getDrawable(i)->accept(primitiveCollector);
152
153         _geometryBuilder->setCurrentMaterial(oldMaterial);
154     }
155
156     virtual void apply(osg::Node& node)
157     {
158         if (_flatten) {
159             traverse(node);
160         } else {
161             _NodeVisitor nodeVisitor(_flatten, _localToWorldMatrix);
162             nodeVisitor.traverse(node);
163             _nodeBin.addNode(nodeVisitor.getNode(osg::Matrix::identity()));
164         }
165     }
166
167     virtual void apply(osg::Transform& transform)
168     {
169         if (transform.getReferenceFrame() != osg::Transform::RELATIVE_RF)
170             return;
171
172         // FIXME identify and handle dynamic transforms
173
174         if (_flatten) {
175             // propagate the matrix further down into the nodes and
176             // build a flat leaf tree as far as possible
177
178             // save away and accumulate the localToWorldMatrix
179             osg::Matrix localToWorldMatrix = _localToWorldMatrix;
180             if (!transform.computeLocalToWorldMatrix(_localToWorldMatrix, this))
181                 return;
182
183             traverse(transform);
184
185             _localToWorldMatrix = localToWorldMatrix;
186         } else {
187             // accumulate the localToWorldMatrix
188             osg::Matrix localToWorldMatrix = _localToWorldMatrix;
189             if (!transform.computeLocalToWorldMatrix(localToWorldMatrix, this))
190                 return;
191
192             // evaluate the loca to world matrix here in this group node.
193             _NodeVisitor nodeVisitor(_flatten);
194             nodeVisitor.traverse(transform);
195             _nodeBin.addNode(nodeVisitor.getNode(localToWorldMatrix));
196         }
197     }
198
199     virtual void apply(osg::Camera& camera)
200     {
201         if (camera.getRenderOrder() != osg::Camera::NESTED_RENDER)
202             return;
203         apply(static_cast<osg::Transform&>(camera));
204     }
205
206     virtual void apply(osg::PagedLOD& pagedLOD)
207     {
208         unsigned numFileNames = pagedLOD.getNumFileNames();
209         if (_flatten) {
210             // In flattening mode treat lod nodes as proxy nodes
211             for (unsigned i = 0; i < numFileNames; ++i) {
212                 if (i < pagedLOD.getNumChildren() && pagedLOD.getChild(i))
213                     continue;
214                 osg::ref_ptr<osg::Node> node;
215                 if (pagedLOD.getMinRange(i) <= 0) {
216                     osg::ref_ptr<const osgDB::Options> options;
217                     options = getOptions(pagedLOD.getDatabaseOptions(), pagedLOD.getDatabasePath());
218                     node = osgDB::readRefNodeFile(pagedLOD.getFileName(i), options.get());
219                 }
220                 if (!node.valid())
221                     node = new osg::Group;
222                 if (i < pagedLOD.getNumChildren())
223                     pagedLOD.setChild(i, node);
224                 else
225                     pagedLOD.addChild(node);
226             }
227         } else {
228             // in non flattening mode translate to bvh page nodes
229             std::vector<std::string> nameList;
230             for (unsigned i = pagedLOD.getNumChildren(); i < numFileNames; ++i) {
231                 if (0 < pagedLOD.getMinRange(i))
232                     continue;
233                 nameList.push_back(pagedLOD.getFileName(i));
234             }
235
236             _NodeBin nodeBin;
237             if (!nameList.empty()) {
238                 osg::ref_ptr<const osgDB::Options> options;
239                 options = getOptions(pagedLOD.getDatabaseOptions(), pagedLOD.getDatabasePath());
240                 SGSphered boundingSphere(toVec3d(toSG(pagedLOD.getCenter())), pagedLOD.getRadius());
241                 nodeBin.addNode(new BVHPageNodeOSG(nameList, boundingSphere, options.get()));
242             }
243             _nodeBin.addNode(nodeBin.getNode(_localToWorldMatrix));
244         }
245         
246         // For the rest that might be already there, traverse this as lod
247         apply(static_cast<osg::LOD&>(pagedLOD));
248     }
249
250     virtual void apply(osg::ProxyNode& proxyNode)
251     {
252         unsigned numFileNames = proxyNode.getNumFileNames();
253         for (unsigned i = 0; i < numFileNames; ++i) {
254             if (i < proxyNode.getNumChildren() && proxyNode.getChild(i))
255                 continue;
256             osg::ref_ptr<const osgDB::Options> options;
257             options = getOptions(proxyNode.getDatabaseOptions(), proxyNode.getDatabasePath());
258             osg::ref_ptr<osg::Node> node;
259             node = osgDB::readRefNodeFile(proxyNode.getFileName(i), options.get());
260             if (!node.valid())
261                 node = new osg::Group;
262             if (i < proxyNode.getNumChildren())
263                 proxyNode.setChild(i, node);
264             else
265                 proxyNode.addChild(node);
266         }
267
268         apply(static_cast<osg::Group&>(proxyNode));
269     }
270
271     static osg::ref_ptr<const osgDB::Options>
272     getOptions(const osg::Referenced* referenced, const std::string& databasePath)
273     {
274         osg::ref_ptr<const osgDB::Options> options = dynamic_cast<const osgDB::Options*>(referenced);
275         if (!options.valid())
276             options = osgDB::Registry::instance()->getOptions();
277         if (databasePath.empty())
278             return options;
279         osg::ref_ptr<osgDB::Options> writable;
280         if (options.valid())
281             writable = static_cast<osgDB::Options*>(options->clone(osg::CopyOp()));
282         else
283             writable = new osgDB::Options;
284         writable->getDatabasePathList().push_front(databasePath);
285         return writable;
286     }
287
288     SGSharedPtr<BVHNode> getNode(const osg::Matrix& matrix = osg::Matrix())
289     {
290         // Flush any pendig leaf nodes
291         if (_geometryBuilder.valid()) {
292             _nodeBin.addNode(_geometryBuilder->buildTree());
293             _geometryBuilder.clear();
294         }
295
296         return _nodeBin.getNode(matrix*_centerMatrix);
297     }
298
299 private:
300     // The part of the accumulated model view matrix that
301     // is put into a BVHTransform node.
302     osg::Matrix _localToWorldMatrix;
303     // The matrix that centers and aligns the leaf.
304     osg::Matrix _centerMatrix;
305
306     // The current pending nodes.
307     _NodeBin _nodeBin;
308
309     SGSharedPtr<BVHStaticGeometryBuilder> _geometryBuilder;
310
311     bool _flatten;
312 };
313
314 class BVHPageNodeOSG::_Request : public BVHPageRequest {
315 public:
316     _Request(BVHPageNodeOSG* pageNode) :
317         _pageNode(pageNode)
318     {
319     }
320     virtual ~_Request()
321     {
322     }
323     virtual void load()
324     {
325         if (!_pageNode.valid())
326             return;
327         for (std::vector<std::string>::const_iterator i = _pageNode->_modelList.begin();
328              i != _pageNode->_modelList.end(); ++i) {
329             SGSharedPtr<BVHNode> node = BVHPageNodeOSG::load(*i, _pageNode->_options);
330             if (!node.valid())
331                 continue;
332             _nodeVector.push_back(node);
333         }
334     }
335     virtual void insert()
336     {
337         if (!_pageNode.valid())
338             return;
339         for (unsigned i = 0; i < _nodeVector.size(); ++i)
340             _pageNode->addChild(_nodeVector[i].get());
341     }
342     virtual BVHPageNode* getPageNode()
343     {
344         return _pageNode.get();
345     }
346
347 private:
348     SGSharedPtr<BVHPageNodeOSG> _pageNode;
349     std::vector<SGSharedPtr<BVHNode> > _nodeVector;
350 };
351
352 SGSharedPtr<BVHNode>
353 BVHPageNodeOSG::load(const std::string& name, const osg::ref_ptr<const osg::Referenced>& options)
354 {
355     osg::ref_ptr<osg::Node> node;
356     node = osgDB::readRefNodeFile(name, dynamic_cast<const osgDB::Options*>(options.get()));
357     if (!node.valid())
358         return SGSharedPtr<BVHNode>();
359
360     bool flatten = (node->getBound()._radius < 30000);
361     _NodeVisitor nodeVisitor(flatten);
362     if (flatten)
363         nodeVisitor.setCenter(node->getBound()._center);
364     node->accept(nodeVisitor);
365     return nodeVisitor.getNode();
366 }
367
368 BVHPageNodeOSG::BVHPageNodeOSG(const std::string& name,
369                                const SGSphered& boundingSphere,
370                                const osg::ref_ptr<const osg::Referenced>& options) :
371     _boundingSphere(boundingSphere),
372     _options(options)
373 {
374     _modelList.push_back(name);
375 }
376
377 BVHPageNodeOSG::BVHPageNodeOSG(const std::vector<std::string>& nameList,
378                                const SGSphered& boundingSphere,
379                                const osg::ref_ptr<const osg::Referenced>& options) :
380     _modelList(nameList),
381     _boundingSphere(boundingSphere),
382     _options(options)
383 {
384 }
385
386 BVHPageNodeOSG::~BVHPageNodeOSG()
387 {
388 }
389     
390 BVHPageRequest*
391 BVHPageNodeOSG::newRequest()
392 {
393     return new _Request(this);
394 }
395
396 void
397 BVHPageNodeOSG::setBoundingSphere(const SGSphered& sphere)
398 {
399     _boundingSphere = sphere;
400     invalidateParentBound();
401 }
402
403 SGSphered
404 BVHPageNodeOSG::computeBoundingSphere() const
405 {
406     return _boundingSphere;
407 }
408
409 void
410 BVHPageNodeOSG::invalidateBound()
411 {
412     // Don't propagate invalidate bound to its parent
413     // Just do this once we get a bounding sphere set
414 }
415
416 }