]> git.mxchange.org Git - simgear.git/blob - simgear/scene/model/BVHPageNodeOSG.cxx
scene: Factor out a common primitive functor.
[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/Camera>
28 #include <osg/Drawable>
29 #include <osg/Geode>
30 #include <osg/Group>
31 #include <osg/PagedLOD>
32 #include <osg/ProxyNode>
33 #include <osg/Transform>
34 #include <osgDB/ReadFile>
35
36 #include <simgear/scene/material/mat.hxx>
37 #include <simgear/scene/material/matlib.hxx>
38 #include <simgear/scene/util/SGNodeMasks.hxx>
39 #include <simgear/scene/util/OsgMath.hxx>
40 #include <simgear/scene/util/SGSceneUserData.hxx>
41 #include <simgear/math/SGGeometry.hxx>
42
43 #include <simgear/bvh/BVHStaticGeometryBuilder.hxx>
44
45 #include "PrimitiveCollector.hxx"
46
47 namespace simgear {
48
49 class BVHPageNodeOSG::_NodeVisitor : public osg::NodeVisitor {
50 public:
51     class _PrimitiveCollector : public PrimitiveCollector {
52     public:
53         _PrimitiveCollector() :
54             _geometryBuilder(new BVHStaticGeometryBuilder)
55         { }
56         virtual ~_PrimitiveCollector()
57         { }
58
59         virtual void addPoint(const osg::Vec3d& v1)
60         { }
61         virtual void addLine(const osg::Vec3d& v1, const osg::Vec3d& v2)
62         { }
63         virtual void addTriangle(const osg::Vec3d& v1, const osg::Vec3d& v2, const osg::Vec3d& v3)
64         {
65             _geometryBuilder->addTriangle(toVec3f(toSG(v1)), toVec3f(toSG(v2)), toVec3f(toSG(v3)));
66         }
67
68         BVHNode* buildTreeAndClear()
69         {
70             BVHNode* bvNode = _geometryBuilder->buildTree();
71             _geometryBuilder = new BVHStaticGeometryBuilder;
72             return bvNode;
73         }
74
75         void swap(_PrimitiveCollector& primitiveCollector)
76         {
77             PrimitiveCollector::swap(primitiveCollector);
78             std::swap(_geometryBuilder, primitiveCollector._geometryBuilder);
79         }
80
81         void setCurrentMaterial(const BVHMaterial* material)
82         {
83             _geometryBuilder->setCurrentMaterial(material);
84         }
85         const BVHMaterial* getCurrentMaterial() const
86         {
87             return _geometryBuilder->getCurrentMaterial();
88         }
89
90         SGSharedPtr<BVHStaticGeometryBuilder> _geometryBuilder;
91     };
92
93     _NodeVisitor() :
94         osg::NodeVisitor(osg::NodeVisitor::TRAVERSE_ACTIVE_CHILDREN)
95     {
96         setTraversalMask(SG_NODEMASK_TERRAIN_BIT);
97     }
98     virtual ~_NodeVisitor()
99     {
100     }
101
102     const BVHMaterial* pushMaterial(osg::Geode* geode)
103     {
104         const BVHMaterial* oldMaterial = _primitiveCollector.getCurrentMaterial();
105         const BVHMaterial* material = SGMaterialLib::findMaterial(geode);
106         if (material)
107             _primitiveCollector.setCurrentMaterial(material);
108         return oldMaterial;
109     }
110
111     virtual void apply(osg::Geode& geode)
112     {
113         const BVHMaterial* oldMaterial = pushMaterial(&geode);
114
115         for(unsigned i = 0; i < geode.getNumDrawables(); ++i)
116             geode.getDrawable(i)->accept(_primitiveCollector);
117
118         _primitiveCollector.setCurrentMaterial(oldMaterial);
119     }
120
121     virtual void apply(osg::Group& group)
122     {
123         // FIXME optimize this to collapse more leafs
124
125         // push the current active primitive list
126         _PrimitiveCollector previousPrimitives;
127         _primitiveCollector.swap(previousPrimitives);
128
129         const BVHMaterial* mat = previousPrimitives.getCurrentMaterial();
130         _primitiveCollector.setCurrentMaterial(mat);
131
132         NodeVector nodeVector;
133         _nodeVector.swap(nodeVector);
134
135         // walk the children
136         traverse(group);
137
138         // We know whenever we see a transform, we need to flush the
139         // collected bounding volume tree since these transforms are not
140         // handled by the plain leafs.
141         addBoundingVolumeTreeToNode();
142
143         _nodeVector.swap(nodeVector);
144
145         if (!nodeVector.empty()) {
146             if (nodeVector.size() == 1) {
147                 _nodeVector.push_back(nodeVector.front());
148             } else {
149                 SGSharedPtr<BVHGroup> group = new BVHGroup;
150                 for (NodeVector::iterator i = nodeVector.begin();
151                      i != nodeVector.end(); ++i)
152                     group->addChild(i->get());
153                 _nodeVector.push_back(group);
154             }
155         }
156
157         // pop the current active primitive list
158         _primitiveCollector.swap(previousPrimitives);
159     }
160
161     virtual void apply(osg::Transform& transform)
162     {
163         if (transform.getReferenceFrame() != osg::Transform::RELATIVE_RF)
164             return;
165
166         osg::Matrix matrix;
167         if (!transform.computeLocalToWorldMatrix(matrix, this))
168             return;
169
170         // push the current active primitive list
171         _PrimitiveCollector previousPrimitives;
172         _primitiveCollector.swap(previousPrimitives);
173
174         const BVHMaterial* mat = previousPrimitives.getCurrentMaterial();
175         _primitiveCollector.setCurrentMaterial(mat);
176
177         NodeVector nodeVector;
178         _nodeVector.swap(nodeVector);
179
180         // walk the children
181         traverse(transform);
182
183         // We know whenever we see a transform, we need to flush the
184         // collected bounding volume tree since these transforms are not
185         // handled by the plain leafs.
186         addBoundingVolumeTreeToNode();
187
188         _nodeVector.swap(nodeVector);
189
190         // pop the current active primitive list
191         _primitiveCollector.swap(previousPrimitives);
192
193         if (!nodeVector.empty()) {
194             SGSharedPtr<BVHTransform> bvhTransform = new BVHTransform;
195             bvhTransform->setToWorldTransform(SGMatrixd(matrix.ptr()));
196
197             for (NodeVector::iterator i = nodeVector.begin();
198                  i != nodeVector.end(); ++i)
199                 bvhTransform->addChild(i->get());
200
201             _nodeVector.push_back(bvhTransform);
202         }
203     }
204
205     virtual void apply(osg::Camera& camera)
206     {
207         if (camera.getRenderOrder() != osg::Camera::NESTED_RENDER)
208             return;
209         apply(static_cast<osg::Transform&>(camera));
210     }
211
212     void addBoundingVolumeTreeToNode()
213     {
214         // Build the flat tree.
215         BVHNode* bvNode = _primitiveCollector.buildTreeAndClear();
216
217         // Nothing in there?
218         if (!bvNode)
219             return;
220         if (bvNode->getBoundingSphere().empty())
221             return;
222
223         _nodeVector.push_back(bvNode);
224     }
225
226     virtual void apply(osg::PagedLOD& pagedLOD)
227     {
228         float range = std::numeric_limits<float>::max();
229         unsigned numFileNames = pagedLOD.getNumFileNames();
230         for (unsigned i = 0; i < numFileNames; ++i) {
231             if (range < pagedLOD.getMaxRange(i))
232                 continue;
233             range = pagedLOD.getMaxRange(i);
234         }
235
236         std::vector<std::string> nameList;
237         for (unsigned i = pagedLOD.getNumChildren(); i < pagedLOD.getNumFileNames(); ++i) {
238             if (pagedLOD.getMaxRange(i) <= range) {
239                 nameList.push_back(pagedLOD.getFileName(i));
240             }
241         }
242
243         if (!nameList.empty()) {
244             SGSphered boundingSphere(toVec3d(toSG(pagedLOD.getCenter())), pagedLOD.getRadius());
245             _nodeVector.push_back(new BVHPageNodeOSG(nameList, boundingSphere, pagedLOD.getDatabaseOptions()));
246         }
247
248         // For the rest that might be already there, traverse this as lod
249         apply(static_cast<osg::LOD&>(pagedLOD));
250     }
251
252     virtual void apply(osg::ProxyNode& proxyNode)
253     {
254         unsigned numFileNames = proxyNode.getNumFileNames();
255         for (unsigned i = 0; i < numFileNames; ++i) {
256             if (i < proxyNode.getNumChildren() && proxyNode.getChild(i))
257                 continue;
258             // FIXME evaluate proxyNode.getDatabasePath()
259             osg::ref_ptr<osg::Node> node;
260             node = osgDB::readNodeFile(proxyNode.getFileName(i),
261               dynamic_cast<const osgDB::Options*>(proxyNode.getDatabaseOptions()));
262             if (!node.valid())
263                 node = new osg::Group;
264             if (i < proxyNode.getNumChildren())
265                 proxyNode.setChild(i, node);
266             else
267                 proxyNode.addChild(node);
268         }
269
270         apply(static_cast<osg::Group&>(proxyNode));
271     }
272
273     SGSharedPtr<BVHNode> getBVHNode()
274     {
275         addBoundingVolumeTreeToNode();
276
277         if (_nodeVector.empty())
278             return SGSharedPtr<BVHNode>();
279         if (_nodeVector.size() == 1)
280             return _nodeVector.front();
281         SGSharedPtr<BVHGroup> group = new BVHGroup;
282         for (NodeVector::iterator i = _nodeVector.begin();
283              i != _nodeVector.end(); ++i)
284             group->addChild(i->get());
285         return group;
286     }
287
288 private:
289     _PrimitiveCollector _primitiveCollector;
290     typedef std::vector<SGSharedPtr<BVHNode> > NodeVector;
291     NodeVector _nodeVector;
292 };
293
294 class BVHPageNodeOSG::_Request : public BVHPageRequest {
295 public:
296     _Request(BVHPageNodeOSG* pageNode) :
297         _pageNode(pageNode)
298     {
299     }
300     virtual ~_Request()
301     {
302     }
303     virtual void load()
304     {
305         if (!_pageNode.valid())
306             return;
307         for (std::vector<std::string>::const_iterator i = _pageNode->_modelList.begin();
308              i != _pageNode->_modelList.end(); ++i) {
309             SGSharedPtr<BVHNode> node = BVHPageNodeOSG::load(*i, _pageNode->_options);
310             if (!node.valid())
311                 continue;
312             _nodeVector.push_back(node);
313         }
314     }
315     virtual void insert()
316     {
317         if (!_pageNode.valid())
318             return;
319         for (unsigned i = 0; i < _nodeVector.size(); ++i)
320             _pageNode->addChild(_nodeVector[i].get());
321     }
322     virtual BVHPageNode* getPageNode()
323     {
324         return _pageNode.get();
325     }
326
327 private:
328     SGSharedPtr<BVHPageNodeOSG> _pageNode;
329     std::vector<SGSharedPtr<BVHNode> > _nodeVector;
330 };
331
332 SGSharedPtr<BVHNode>
333 BVHPageNodeOSG::load(const std::string& name, const osg::ref_ptr<osg::Referenced>& options)
334 {
335     osg::ref_ptr<osg::Node> node;
336     node = osgDB::readNodeFile(name, dynamic_cast<const osgDB::Options*>(options.get()));
337     if (!node.valid())
338         return SGSharedPtr<BVHNode>();
339
340     _NodeVisitor nodeVisitor;
341     node->accept(nodeVisitor);
342     return nodeVisitor.getBVHNode();
343 }
344
345 BVHPageNodeOSG::BVHPageNodeOSG(const std::string& name,
346                                const SGSphered& boundingSphere,
347                                const osg::ref_ptr<osg::Referenced>& options) :
348     _boundingSphere(boundingSphere),
349     _options(options)
350 {
351     _modelList.push_back(name);
352 }
353
354 BVHPageNodeOSG::BVHPageNodeOSG(const std::vector<std::string>& nameList,
355                                const SGSphered& boundingSphere,
356                                const osg::ref_ptr<osg::Referenced>& options) :
357     _modelList(nameList),
358     _boundingSphere(boundingSphere),
359     _options(options)
360 {
361 }
362
363 BVHPageNodeOSG::~BVHPageNodeOSG()
364 {
365 }
366     
367 BVHPageRequest*
368 BVHPageNodeOSG::newRequest()
369 {
370     return new _Request(this);
371 }
372
373 void
374 BVHPageNodeOSG::setBoundingSphere(const SGSphered& sphere)
375 {
376     _boundingSphere = sphere;
377     invalidateParentBound();
378 }
379
380 SGSphered
381 BVHPageNodeOSG::computeBoundingSphere() const
382 {
383     return _boundingSphere;
384 }
385
386 void
387 BVHPageNodeOSG::invalidateBound()
388 {
389     // Don't propagate invalidate bound to its parent
390     // Just do this once we get a bounding sphere set
391 }
392
393 }