1 // Copyright (C) 2002-2012 Nikolaus Gebhardt
\r
2 // This file is part of the "Irrlicht Engine".
\r
3 // For conditions of distribution and use, see copyright notice in irrlicht.h
\r
5 #include "IrrCompileConfig.h"
\r
6 #ifdef _IRR_COMPILE_WITH_OCTREE_SCENENODE_
\r
8 #include "COctreeSceneNode.h"
\r
10 #include "ISceneManager.h"
\r
11 #include "IVideoDriver.h"
\r
12 #include "ICameraSceneNode.h"
\r
13 #include "IMeshCache.h"
\r
14 #include "IAnimatedMesh.h"
\r
15 #include "IMaterialRenderer.h"
\r
17 #ifdef _IRR_COMPILE_WITH_SHADOW_VOLUME_SCENENODE_
\r
18 #include "CShadowVolumeSceneNode.h"
\r
20 #include "IShadowVolumeSceneNode.h"
\r
21 #endif // _IRR_COMPILE_WITH_SHADOW_VOLUME_SCENENODE_
\r
22 #include "EProfileIDs.h"
\r
23 #include "IProfiler.h"
\r
32 COctreeSceneNode::COctreeSceneNode(ISceneNode* parent, ISceneManager* mgr,
\r
33 s32 id, s32 minimalPolysPerNode)
\r
34 : IOctreeSceneNode(parent, mgr, id), StdOctree(0), LightMapOctree(0),
\r
35 TangentsOctree(0), VertexType((video::E_VERTEX_TYPE)-1),
\r
36 MinimalPolysPerNode(minimalPolysPerNode), Mesh(0), Shadow(0),
\r
37 UseVBOs(EOV_NO_VBO), PolygonChecks(EOPC_BOX)
\r
40 setDebugName("COctreeSceneNode");
\r
44 static bool initProfile = false;
\r
48 getProfiler().add(EPID_OC_RENDER, L"render octnode", L"Irrlicht scene");
\r
49 getProfiler().add(EPID_OC_CALCPOLYS, L"calc octnode", L"Irrlicht scene");
\r
56 COctreeSceneNode::~COctreeSceneNode()
\r
64 void COctreeSceneNode::OnRegisterSceneNode()
\r
68 // because this node supports rendering of mixed mode meshes consisting of
\r
69 // transparent and solid material at the same time, we need to go through all
\r
70 // materials, check of what type they are and register this node for the right
\r
71 // render pass according to that.
\r
73 video::IVideoDriver* driver = SceneManager->getVideoDriver();
\r
76 u32 transparentCount = 0;
\r
79 // count transparent and solid materials in this scene node
\r
80 for (u32 i=0; i<Materials.size(); ++i)
\r
82 if (driver->needsTransparentRenderPass(Materials[i]))
\r
87 if (solidCount && transparentCount)
\r
91 // register according to material types counted
\r
94 SceneManager->registerNodeForRendering(this, scene::ESNRP_SOLID);
\r
96 if (transparentCount)
\r
97 SceneManager->registerNodeForRendering(this, scene::ESNRP_TRANSPARENT);
\r
99 ISceneNode::OnRegisterSceneNode();
\r
103 template <class VT>
\r
104 void renderMeshBuffer(video::IVideoDriver* driver, EOCTREENODE_VBO useVBO, typename Octree<VT>::SMeshChunk& meshChunk, const typename Octree<VT>::SIndexData& indexData)
\r
109 driver->drawIndexedTriangleList(
\r
110 &meshChunk.Vertices[0],
\r
111 meshChunk.Vertices.size(),
\r
112 indexData.Indices, indexData.CurrentSize / 3);
\r
115 driver->drawMeshBuffer ( &meshChunk );
\r
117 case EOV_USE_VBO_WITH_VISIBITLY:
\r
119 u16* oldPointer = meshChunk.Indices.pointer();
\r
120 const u32 oldSize = meshChunk.Indices.size();
\r
121 meshChunk.Indices.set_free_when_destroyed(false);
\r
122 meshChunk.Indices.set_pointer(indexData.Indices, indexData.CurrentSize, false, false);
\r
123 meshChunk.setDirty(scene::EBT_INDEX);
\r
124 driver->drawMeshBuffer ( &meshChunk );
\r
125 meshChunk.Indices.set_pointer(oldPointer, oldSize);
\r
126 meshChunk.setDirty(scene::EBT_INDEX);
\r
132 //! renders the node.
\r
133 void COctreeSceneNode::render()
\r
135 IRR_PROFILE(CProfileScope psRender(EPID_OC_RENDER);)
\r
136 video::IVideoDriver* driver = SceneManager->getVideoDriver();
\r
141 ICameraSceneNode* camera = SceneManager->getActiveCamera();
\r
145 const bool isTransparentPass =
\r
146 SceneManager->getSceneNodeRenderPass() == scene::ESNRP_TRANSPARENT;
\r
149 driver->setTransform(video::ETS_WORLD, AbsoluteTransformation);
\r
152 Shadow->updateShadowVolumes();
\r
154 SViewFrustum frust = *camera->getViewFrustum();
\r
156 //transform the frustum to the current absolute transformation
\r
157 if ( !AbsoluteTransformation.isIdentity() )
\r
159 core::matrix4 invTrans(AbsoluteTransformation, core::matrix4::EM4CONST_INVERSE);
\r
160 frust.transform(invTrans);
\r
163 const core::aabbox3d<float> &box = frust.getBoundingBox();
\r
165 switch (VertexType)
\r
167 case video::EVT_STANDARD:
\r
169 IRR_PROFILE(getProfiler().start(EPID_OC_CALCPOLYS));
\r
170 switch ( PolygonChecks )
\r
173 StdOctree->calculatePolys(box);
\r
176 StdOctree->calculatePolys(frust);
\r
179 IRR_PROFILE(getProfiler().stop(EPID_OC_CALCPOLYS));
\r
181 const Octree<video::S3DVertex>::SIndexData* d = StdOctree->getIndexData();
\r
183 for (u32 i=0; i<Materials.size(); ++i)
\r
185 if ( 0 == d[i].CurrentSize )
\r
188 const bool transparent = driver->needsTransparentRenderPass(Materials[i]);
\r
190 // only render transparent buffer if this is the transparent render pass
\r
191 // and solid only in solid pass
\r
192 if (transparent == isTransparentPass)
\r
194 driver->setMaterial(Materials[i]);
\r
195 renderMeshBuffer<video::S3DVertex>(driver, UseVBOs, StdMeshes[i], d[i]);
\r
200 case video::EVT_2TCOORDS:
\r
202 IRR_PROFILE(getProfiler().start(EPID_OC_CALCPOLYS));
\r
203 switch ( PolygonChecks )
\r
206 LightMapOctree->calculatePolys(box);
\r
209 LightMapOctree->calculatePolys(frust);
\r
212 IRR_PROFILE(getProfiler().stop(EPID_OC_CALCPOLYS));
\r
214 const Octree<video::S3DVertex2TCoords>::SIndexData* d = LightMapOctree->getIndexData();
\r
216 for (u32 i=0; i<Materials.size(); ++i)
\r
218 if ( 0 == d[i].CurrentSize )
\r
221 const video::IMaterialRenderer* const rnd = driver->getMaterialRenderer(Materials[i].MaterialType);
\r
222 const bool transparent = (rnd && rnd->isTransparent());
\r
224 // only render transparent buffer if this is the transparent render pass
\r
225 // and solid only in solid pass
\r
226 if (transparent == isTransparentPass)
\r
228 driver->setMaterial(Materials[i]);
\r
230 renderMeshBuffer<video::S3DVertex2TCoords>(driver, UseVBOs, LightMapMeshes[i], d[i]);
\r
235 case video::EVT_TANGENTS:
\r
237 IRR_PROFILE(getProfiler().start(EPID_OC_CALCPOLYS));
\r
238 switch ( PolygonChecks )
\r
241 TangentsOctree->calculatePolys(box);
\r
244 TangentsOctree->calculatePolys(frust);
\r
247 IRR_PROFILE(getProfiler().stop(EPID_OC_CALCPOLYS));
\r
249 const Octree<video::S3DVertexTangents>::SIndexData* d = TangentsOctree->getIndexData();
\r
251 for (u32 i=0; i<Materials.size(); ++i)
\r
253 if ( 0 == d[i].CurrentSize )
\r
256 const video::IMaterialRenderer* const rnd = driver->getMaterialRenderer(Materials[i].MaterialType);
\r
257 const bool transparent = (rnd && rnd->isTransparent());
\r
259 // only render transparent buffer if this is the transparent render pass
\r
260 // and solid only in solid pass
\r
261 if (transparent == isTransparentPass)
\r
263 driver->setMaterial(Materials[i]);
\r
264 renderMeshBuffer<video::S3DVertexTangents>(driver, UseVBOs, TangentsMeshes[i], d[i]);
\r
271 // for debug purposes only
\r
272 if (DebugDataVisible && !Materials.empty() && PassCount==1)
\r
274 core::array< const core::aabbox3d<f32>* > boxes;
\r
275 video::SMaterial m;
\r
276 m.Lighting = false;
\r
277 driver->setMaterial(m);
\r
278 if ( DebugDataVisible & scene::EDS_BBOX_BUFFERS )
\r
280 switch (VertexType)
\r
282 case video::EVT_STANDARD:
\r
283 StdOctree->getBoundingBoxes(box, boxes);
\r
285 case video::EVT_2TCOORDS:
\r
286 LightMapOctree->getBoundingBoxes(box, boxes);
\r
288 case video::EVT_TANGENTS:
\r
289 TangentsOctree->getBoundingBoxes(box, boxes);
\r
293 for (u32 b=0; b!=boxes.size(); ++b)
\r
294 driver->draw3DBox(*boxes[b]);
\r
297 if ( DebugDataVisible & scene::EDS_BBOX )
\r
298 driver->draw3DBox(Box,video::SColor(0,255,0,0));
\r
304 //! Removes a child from this scene node.
\r
305 //! Implemented here, to be able to remove the shadow properly, if there is one,
\r
306 //! or to remove attached childs.
\r
307 bool COctreeSceneNode::removeChild(ISceneNode* child)
\r
309 if (child && Shadow == child)
\r
315 return ISceneNode::removeChild(child);
\r
318 void COctreeSceneNode::setUseVBO(EOCTREENODE_VBO useVBO)
\r
325 EOCTREENODE_VBO COctreeSceneNode::getUseVBO() const
\r
330 void COctreeSceneNode::setPolygonChecks(EOCTREE_POLYGON_CHECKS checks)
\r
332 PolygonChecks = checks;
\r
335 EOCTREE_POLYGON_CHECKS COctreeSceneNode::getPolygonChecks() const
\r
337 return PolygonChecks;
\r
340 //! Creates shadow volume scene node as child of this node
\r
341 //! and returns a pointer to it.
\r
342 IShadowVolumeSceneNode* COctreeSceneNode::addShadowVolumeSceneNode(
\r
343 const IMesh* shadowMesh, s32 id, bool zfailmethod, f32 infinity)
\r
345 #ifdef _IRR_COMPILE_WITH_SHADOW_VOLUME_SCENENODE_
\r
346 if (!SceneManager->getVideoDriver()->queryFeature(video::EVDF_STENCIL_BUFFER))
\r
350 shadowMesh = Mesh; // if null is given, use the mesh of node
\r
355 Shadow = new CShadowVolumeSceneNode(shadowMesh, this, SceneManager, id, zfailmethod, infinity);
\r
363 //! returns the axis aligned bounding box of this node
\r
364 const core::aabbox3d<f32>& COctreeSceneNode::getBoundingBox() const
\r
370 //! creates the tree
\r
371 /* This method has a lot of duplication and overhead. Moreover, the tangents mesh conversion does not really work. I think we need a a proper mesh implementation for octrees, which handle all vertex types internally. Converting all structures to just one vertex type is always problematic.
\r
372 Thanks to Auria for fixing major parts of this method. */
\r
373 bool COctreeSceneNode::createTree(IMesh* mesh)
\r
378 MeshName = SceneManager->getMeshCache()->getMeshName(mesh);
\r
385 const u32 beginTime = os::Timer::getRealTime();
\r
391 Box = mesh->getBoundingBox();
\r
393 if (mesh->getMeshBufferCount())
\r
395 // check for "largest" buffer types
\r
396 // Also dropping buffers/materials for empty buffer
\r
397 // (which looks like a horrible idea. If a user wanted that material without mesh he should still get it...
\r
398 // but not going to change that now. Only documenting it after figuring out what happens here.
\r
399 // It works at least as Materials are reset in deleteTree).
\r
400 VertexType = video::EVT_STANDARD;
\r
401 u32 meshReserve = 0;
\r
402 for (i=0; i<mesh->getMeshBufferCount(); ++i)
\r
404 const IMeshBuffer* b = mesh->getMeshBuffer(i);
\r
405 if (b->getVertexCount() && b->getIndexCount())
\r
408 if (b->getVertexType() == video::EVT_2TCOORDS)
\r
409 VertexType = video::EVT_2TCOORDS;
\r
410 else if (b->getVertexType() == video::EVT_TANGENTS)
\r
411 VertexType = video::EVT_TANGENTS;
\r
414 Materials.reallocate(Materials.size()+meshReserve);
\r
418 case video::EVT_STANDARD:
\r
420 StdMeshes.reallocate(StdMeshes.size() + meshReserve);
\r
421 for (i=0; i<mesh->getMeshBufferCount(); ++i)
\r
423 IMeshBuffer* b = mesh->getMeshBuffer(i);
\r
425 if (b->getVertexCount() && b->getIndexCount())
\r
427 Materials.push_back(b->getMaterial());
\r
429 StdMeshes.push_back(Octree<video::S3DVertex>::SMeshChunk());
\r
430 Octree<video::S3DVertex>::SMeshChunk &nchunk = StdMeshes.getLast();
\r
431 nchunk.MaterialId = Materials.size() - 1;
\r
434 nchunk.Vertices.reallocate(b->getVertexCount());
\r
435 switch (b->getVertexType())
\r
437 case video::EVT_STANDARD:
\r
438 for (v=0; v<b->getVertexCount(); ++v)
\r
439 nchunk.Vertices.push_back(((video::S3DVertex*)b->getVertices())[v]);
\r
441 case video::EVT_2TCOORDS:
\r
442 for (v=0; v<b->getVertexCount(); ++v)
\r
443 nchunk.Vertices.push_back(((video::S3DVertex2TCoords*)b->getVertices())[v]);
\r
445 case video::EVT_TANGENTS:
\r
446 for (v=0; v<b->getVertexCount(); ++v)
\r
447 nchunk.Vertices.push_back(((video::S3DVertexTangents*)b->getVertices())[v]);
\r
451 polyCount += b->getIndexCount();
\r
453 nchunk.Indices.reallocate(b->getIndexCount());
\r
454 for (v=0; v<b->getIndexCount(); ++v)
\r
455 nchunk.Indices.push_back(b->getIndices()[v]);
\r
459 StdOctree = new Octree<video::S3DVertex>(StdMeshes, MinimalPolysPerNode);
\r
460 nodeCount = StdOctree->getNodeCount();
\r
463 case video::EVT_2TCOORDS:
\r
465 LightMapMeshes.reallocate(LightMapMeshes.size() + meshReserve);
\r
467 for ( i=0; i < mesh->getMeshBufferCount(); ++i)
\r
469 IMeshBuffer* b = mesh->getMeshBuffer(i);
\r
471 if (b->getVertexCount() && b->getIndexCount())
\r
473 Materials.push_back(b->getMaterial());
\r
474 LightMapMeshes.push_back(Octree<video::S3DVertex2TCoords>::SMeshChunk());
\r
475 Octree<video::S3DVertex2TCoords>::SMeshChunk& nchunk = LightMapMeshes.getLast();
\r
476 nchunk.MaterialId = Materials.size() - 1;
\r
478 if (UseVBOs == EOV_USE_VBO_WITH_VISIBITLY)
\r
480 nchunk.setHardwareMappingHint(scene::EHM_STATIC, scene::EBT_VERTEX);
\r
481 nchunk.setHardwareMappingHint(scene::EHM_DYNAMIC, scene::EBT_INDEX);
\r
484 nchunk.setHardwareMappingHint(scene::EHM_STATIC);
\r
487 nchunk.Vertices.reallocate(b->getVertexCount());
\r
488 switch (b->getVertexType())
\r
490 case video::EVT_STANDARD:
\r
491 for (v=0; v<b->getVertexCount(); ++v)
\r
492 nchunk.Vertices.push_back(((video::S3DVertex*)b->getVertices())[v]);
\r
494 case video::EVT_2TCOORDS:
\r
495 for (v=0; v<b->getVertexCount(); ++v)
\r
496 nchunk.Vertices.push_back(((video::S3DVertex2TCoords*)b->getVertices())[v]);
\r
498 case video::EVT_TANGENTS:
\r
499 for (v=0; v<b->getVertexCount(); ++v)
\r
500 nchunk.Vertices.push_back(((video::S3DVertexTangents*)b->getVertices())[v]);
\r
504 polyCount += b->getIndexCount();
\r
505 nchunk.Indices.reallocate(b->getIndexCount());
\r
506 for (v=0; v<b->getIndexCount(); ++v)
\r
507 nchunk.Indices.push_back(b->getIndices()[v]);
\r
511 LightMapOctree = new Octree<video::S3DVertex2TCoords>(LightMapMeshes, MinimalPolysPerNode);
\r
512 nodeCount = LightMapOctree->getNodeCount();
\r
515 case video::EVT_TANGENTS:
\r
517 TangentsMeshes.reallocate(TangentsMeshes.size() + meshReserve);
\r
519 for (u32 i=0; i<mesh->getMeshBufferCount(); ++i)
\r
521 IMeshBuffer* b = mesh->getMeshBuffer(i);
\r
523 if (b->getVertexCount() && b->getIndexCount())
\r
525 Materials.push_back(b->getMaterial());
\r
526 TangentsMeshes.push_back(Octree<video::S3DVertexTangents>::SMeshChunk());
\r
527 Octree<video::S3DVertexTangents>::SMeshChunk& nchunk = TangentsMeshes.getLast();
\r
528 nchunk.MaterialId = Materials.size() - 1;
\r
531 nchunk.Vertices.reallocate(b->getVertexCount());
\r
532 switch (b->getVertexType())
\r
534 case video::EVT_STANDARD:
\r
535 for (v=0; v<b->getVertexCount(); ++v)
\r
537 const video::S3DVertex& tmpV = ((video::S3DVertex*)b->getVertices())[v];
\r
538 nchunk.Vertices.push_back(video::S3DVertexTangents(tmpV.Pos, tmpV.Color, tmpV.TCoords));
\r
541 case video::EVT_2TCOORDS:
\r
542 for (v=0; v<b->getVertexCount(); ++v)
\r
544 const video::S3DVertex2TCoords& tmpV = ((video::S3DVertex2TCoords*)b->getVertices())[v];
\r
545 nchunk.Vertices.push_back(video::S3DVertexTangents(tmpV.Pos, tmpV.Color, tmpV.TCoords));
\r
548 case video::EVT_TANGENTS:
\r
549 for (v=0; v<b->getVertexCount(); ++v)
\r
550 nchunk.Vertices.push_back(((video::S3DVertexTangents*)b->getVertices())[v]);
\r
554 polyCount += b->getIndexCount();
\r
555 nchunk.Indices.reallocate(b->getIndexCount());
\r
556 for (v=0; v<b->getIndexCount(); ++v)
\r
557 nchunk.Indices.push_back(b->getIndices()[v]);
\r
561 TangentsOctree = new Octree<video::S3DVertexTangents>(TangentsMeshes, MinimalPolysPerNode);
\r
562 nodeCount = TangentsOctree->getNodeCount();
\r
568 const u32 endTime = os::Timer::getRealTime();
\r
570 sprintf(tmp, "Needed %ums to create Octree SceneNode.(%u nodes, %u polys)",
\r
571 endTime - beginTime, nodeCount, polyCount/3);
\r
572 os::Printer::log(tmp, ELL_INFORMATION);
\r
578 //! returns the material based on the zero based index i.
\r
579 video::SMaterial& COctreeSceneNode::getMaterial(u32 i)
\r
581 if ( i >= Materials.size() )
\r
582 return ISceneNode::getMaterial(i);
\r
584 return Materials[i];
\r
588 //! returns amount of materials used by this scene node.
\r
589 u32 COctreeSceneNode::getMaterialCount() const
\r
591 return Materials.size();
\r
595 //! Writes attributes of the scene node.
\r
596 void COctreeSceneNode::serializeAttributes(io::IAttributes* out, io::SAttributeReadWriteOptions* options) const
\r
598 ISceneNode::serializeAttributes(out, options);
\r
600 out->addInt("MinimalPolysPerNode", MinimalPolysPerNode);
\r
601 out->addString("Mesh", MeshName.c_str());
\r
605 //! Reads attributes of the scene node.
\r
606 void COctreeSceneNode::deserializeAttributes(io::IAttributes* in, io::SAttributeReadWriteOptions* options)
\r
608 const s32 oldMinimal = MinimalPolysPerNode;
\r
610 MinimalPolysPerNode = in->getAttributeAsInt("MinimalPolysPerNode");
\r
611 io::path newMeshStr = in->getAttributeAsString("Mesh");
\r
613 IMesh* newMesh = 0;
\r
615 if (newMeshStr == "")
\r
616 newMeshStr = MeshName;
\r
618 IAnimatedMesh* newAnimatedMesh = SceneManager->getMesh(newMeshStr.c_str());
\r
620 if (newAnimatedMesh)
\r
621 newMesh = newAnimatedMesh->getMesh(0);
\r
623 if (newMesh && ((MeshName != newMeshStr) || (MinimalPolysPerNode != oldMinimal)))
\r
625 // recalculate tree
\r
626 createTree(newMesh);
\r
629 ISceneNode::deserializeAttributes(in, options);
\r
633 void COctreeSceneNode::deleteTree()
\r
639 delete LightMapOctree;
\r
640 LightMapOctree = 0;
\r
641 LightMapMeshes.clear();
\r
643 delete TangentsOctree;
\r
644 TangentsOctree = 0;
\r
645 TangentsMeshes.clear();
\r
653 void COctreeSceneNode::setMesh(IMesh* mesh)
\r
658 IMesh* COctreeSceneNode::getMesh(void)
\r
663 void COctreeSceneNode::setReadOnlyMaterials(bool readonly)
\r
668 bool COctreeSceneNode::isReadOnlyMaterials() const
\r
674 } // end namespace scene
\r
675 } // end namespace irr
\r
677 #endif // _IRR_COMPILE_WITH_OCTREE_SCENENODE_
\r