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