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