1 // Copyright (C) 2008 - 2012 Mathias Froehlich - Mathias.Froehlich@web.de
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.
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.
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.
19 # include <simgear_config.h>
22 #include "BVHPageNodeOSG.hxx"
24 #include "../../bvh/BVHPageRequest.hxx"
25 #include "../../bvh/BVHPager.hxx"
27 #include <osg/io_utils>
29 #include <osg/Drawable>
32 #include <osg/PagedLOD>
33 #include <osg/ProxyNode>
34 #include <osg/Transform>
35 #include <osgDB/ReadFile>
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>
44 #include <simgear/bvh/BVHStaticGeometryBuilder.hxx>
46 #include "PrimitiveCollector.hxx"
50 class BVHPageNodeOSG::_NodeVisitor : public osg::NodeVisitor {
52 struct _PrimitiveCollector : public PrimitiveCollector {
53 _PrimitiveCollector(_NodeVisitor& nodeVisitor) :
54 _nodeVisitor(nodeVisitor)
56 virtual ~_PrimitiveCollector()
58 virtual void addPoint(const osg::Vec3d& v1)
60 virtual void addLine(const osg::Vec3d& v1, const osg::Vec3d& v2)
62 virtual void addTriangle(const osg::Vec3d& v1, const osg::Vec3d& v2, const osg::Vec3d& v3)
63 { _nodeVisitor.addTriangle(v1, v2, v3); }
65 _NodeVisitor& _nodeVisitor;
69 SGSharedPtr<BVHNode> getNode(const osg::Matrix& matrix)
71 if (_nodeVector.empty())
72 return SGSharedPtr<BVHNode>();
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());
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());
96 void addNode(const SGSharedPtr<BVHNode>& node)
100 if (node->getBoundingSphere().empty())
102 _nodeVector.push_back(node);
106 typedef std::vector<SGSharedPtr<BVHNode> > _NodeVector;
108 // The current pending node vector.
109 _NodeVector _nodeVector;
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),
118 setTraversalMask(SG_NODEMASK_TERRAIN_BIT);
120 virtual ~_NodeVisitor()
124 void addTriangle(const osg::Vec3d& v1, const osg::Vec3d& v2, const osg::Vec3d& v3)
126 _geometryBuilder->addTriangle(toVec3f(toSG(_localToWorldMatrix.preMult(v1))),
127 toVec3f(toSG(_localToWorldMatrix.preMult(v2))),
128 toVec3f(toSG(_localToWorldMatrix.preMult(v3))));
131 void setCenter(const osg::Vec3& center)
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)));
143 virtual void apply(osg::Geode& geode)
145 const BVHMaterial* oldMaterial = _geometryBuilder->getCurrentMaterial();
146 if (const BVHMaterial* material = SGMaterialLib::findMaterial(&geode))
147 _geometryBuilder->setCurrentMaterial(material);
149 _PrimitiveCollector primitiveCollector(*this);
150 for(unsigned i = 0; i < geode.getNumDrawables(); ++i)
151 geode.getDrawable(i)->accept(primitiveCollector);
153 _geometryBuilder->setCurrentMaterial(oldMaterial);
156 virtual void apply(osg::Node& node)
161 _NodeVisitor nodeVisitor(_flatten, _localToWorldMatrix);
162 nodeVisitor.traverse(node);
163 _nodeBin.addNode(nodeVisitor.getNode(osg::Matrix::identity()));
167 virtual void apply(osg::Transform& transform)
169 if (transform.getReferenceFrame() != osg::Transform::RELATIVE_RF)
172 // FIXME identify and handle dynamic transforms
175 // propagate the matrix further down into the nodes and
176 // build a flat leaf tree as far as possible
178 // save away and accumulate the localToWorldMatrix
179 osg::Matrix localToWorldMatrix = _localToWorldMatrix;
180 if (!transform.computeLocalToWorldMatrix(_localToWorldMatrix, this))
185 _localToWorldMatrix = localToWorldMatrix;
187 // accumulate the localToWorldMatrix
188 osg::Matrix localToWorldMatrix = _localToWorldMatrix;
189 if (!transform.computeLocalToWorldMatrix(localToWorldMatrix, this))
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));
199 virtual void apply(osg::Camera& camera)
201 if (camera.getRenderOrder() != osg::Camera::NESTED_RENDER)
203 apply(static_cast<osg::Transform&>(camera));
206 virtual void apply(osg::PagedLOD& pagedLOD)
208 unsigned numFileNames = pagedLOD.getNumFileNames();
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))
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());
221 node = new osg::Group;
222 if (i < pagedLOD.getNumChildren())
223 pagedLOD.setChild(i, node);
225 pagedLOD.addChild(node);
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))
233 nameList.push_back(pagedLOD.getFileName(i));
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()));
243 _nodeBin.addNode(nodeBin.getNode(_localToWorldMatrix));
246 // For the rest that might be already there, traverse this as lod
247 apply(static_cast<osg::LOD&>(pagedLOD));
250 virtual void apply(osg::ProxyNode& proxyNode)
252 unsigned numFileNames = proxyNode.getNumFileNames();
253 for (unsigned i = 0; i < numFileNames; ++i) {
254 if (i < proxyNode.getNumChildren() && proxyNode.getChild(i))
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());
261 node = new osg::Group;
262 if (i < proxyNode.getNumChildren())
263 proxyNode.setChild(i, node);
265 proxyNode.addChild(node);
268 apply(static_cast<osg::Group&>(proxyNode));
271 static osg::ref_ptr<const osgDB::Options>
272 getOptions(const osg::Referenced* referenced, const std::string& databasePath)
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())
279 osg::ref_ptr<osgDB::Options> writable;
281 writable = static_cast<osgDB::Options*>(options->clone(osg::CopyOp()));
283 writable = new osgDB::Options;
284 writable->getDatabasePathList().push_front(databasePath);
288 SGSharedPtr<BVHNode> getNode(const osg::Matrix& matrix = osg::Matrix())
290 // Flush any pendig leaf nodes
291 if (_geometryBuilder.valid()) {
292 _nodeBin.addNode(_geometryBuilder->buildTree());
293 _geometryBuilder.clear();
296 return _nodeBin.getNode(matrix*_centerMatrix);
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;
306 // The current pending nodes.
309 SGSharedPtr<BVHStaticGeometryBuilder> _geometryBuilder;
314 class BVHPageNodeOSG::_Request : public BVHPageRequest {
316 _Request(BVHPageNodeOSG* pageNode) :
325 if (!_pageNode.valid())
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);
332 _nodeVector.push_back(node);
335 virtual void insert()
337 if (!_pageNode.valid())
339 for (unsigned i = 0; i < _nodeVector.size(); ++i)
340 _pageNode->addChild(_nodeVector[i].get());
342 virtual BVHPageNode* getPageNode()
344 return _pageNode.get();
348 SGSharedPtr<BVHPageNodeOSG> _pageNode;
349 std::vector<SGSharedPtr<BVHNode> > _nodeVector;
353 BVHPageNodeOSG::load(const std::string& name, const osg::ref_ptr<const osg::Referenced>& options)
355 osg::ref_ptr<osg::Node> node;
356 node = osgDB::readRefNodeFile(name, dynamic_cast<const osgDB::Options*>(options.get()));
358 return SGSharedPtr<BVHNode>();
360 bool flatten = (node->getBound()._radius < 30000);
361 _NodeVisitor nodeVisitor(flatten);
363 nodeVisitor.setCenter(node->getBound()._center);
364 node->accept(nodeVisitor);
365 return nodeVisitor.getNode();
368 BVHPageNodeOSG::BVHPageNodeOSG(const std::string& name,
369 const SGSphered& boundingSphere,
370 const osg::ref_ptr<const osg::Referenced>& options) :
371 _boundingSphere(boundingSphere),
374 _modelList.push_back(name);
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),
386 BVHPageNodeOSG::~BVHPageNodeOSG()
391 BVHPageNodeOSG::newRequest()
393 return new _Request(this);
397 BVHPageNodeOSG::setBoundingSphere(const SGSphered& sphere)
399 _boundingSphere = sphere;
400 invalidateParentBound();
404 BVHPageNodeOSG::computeBoundingSphere() const
406 return _boundingSphere;
410 BVHPageNodeOSG::invalidateBound()
412 // Don't propagate invalidate bound to its parent
413 // Just do this once we get a bounding sphere set