From 6355bc18b776cbec77043c70e3ce34371dcd1933 Mon Sep 17 00:00:00 2001 From: cutealien Date: Wed, 19 Apr 2017 16:28:24 +0000 Subject: [PATCH] Add IOctreeSceneNode interface to control parameters like VBO usage and polygon clipping checks for octree scene nodes. This was already possible, but needed users to set some defines and recompile Irrlicht. As before it's only implemented for the EVT_2TCOORDS vertex format (others will follow soon). git-svn-id: svn://svn.code.sf.net/p/irrlicht/code/trunk@5434 dfc29bdd-3216-0410-991c-e03cc46cb475 --- changes.txt | 1 + include/IOctreeSceneNode.h | 86 ++++++++++++++++++ include/ISceneManager.h | 25 ++---- include/irrlicht.h | 1 + source/Irrlicht/CDefaultSceneNodeFactory.cpp | 1 + source/Irrlicht/COctreeSceneNode.cpp | 94 ++++++++++++++------ source/Irrlicht/COctreeSceneNode.h | 31 +++++-- source/Irrlicht/CSceneManager.cpp | 4 +- source/Irrlicht/CSceneManager.h | 4 +- source/Irrlicht/Octree.h | 6 -- 10 files changed, 188 insertions(+), 65 deletions(-) create mode 100644 include/IOctreeSceneNode.h diff --git a/changes.txt b/changes.txt index ed3bdc0e..5fb33337 100644 --- a/changes.txt +++ b/changes.txt @@ -1,6 +1,7 @@ -------------------------- Changes in 1.9 (not yet released) +- Add IOctreeSceneNode interface to control parameters like VBO usage and polygon clipping checks for octree scene nodes. - Add support for different geometric primitivs to meshbuffers. Thanks @gerdb for patch proposal (http://irrlicht.sourceforge.net/forum/viewtopic.php?f=7&t=45999) - change SEvent::SUserEvent.UserData1 and SEvent::SUserEvent.UserData1 from s32 to size_t to avoid cutting numbers on some 64-bit platforms (SDL and Windows) - Improve speed of draw3DBox (OpenGL and D3D9). Thanks @zerochen for patch (https://sourceforge.net/p/irrlicht/patches/256) diff --git a/include/IOctreeSceneNode.h b/include/IOctreeSceneNode.h new file mode 100644 index 00000000..4e2b08fd --- /dev/null +++ b/include/IOctreeSceneNode.h @@ -0,0 +1,86 @@ +// Copyright (C) 2002-2012 Nikolaus Gebhardt +// This file is part of the "Irrlicht Engine". +// For conditions of distribution and use, see copyright notice in irrlicht.h + +#ifndef __I_OCTREE_SCENE_NODE_H_INCLUDED__ +#define __I_OCTREE_SCENE_NODE_H_INCLUDED__ + +#include "IMeshSceneNode.h" + +namespace irr +{ +namespace scene +{ + +//! Settings if/how octree scene nodes are using hardware mesh-buffers +/** VBO = Vertex buffer object = meshbuffers bound on the graphic-card instead of uploaded each frame. + It can not be generally said which mode is optimal for drawing as this depends + on the scene. So you have to try and experiment for your meshes which one works best. +*/ +enum EOCTREENODE_VBO +{ + //! No VBO's used. Vertices+indices send to graphic-card on each render. + EOV_NO_VBO, + + //! VBO's used. Draw the complete meshbuffers if any polygon in it is visible. + //! This allows VBO's for the meshbuffers to be completely static, but basically means + //! the octree is not used as an octree (not it still does do all the octree calculations) + //! Might work in very specific cases, but if this is gives you the best fastest results + //! you should probably compare as well to simply using a static mesh with no octree at all. + //! In most cases the other 2 options should work better with an octree. + EOV_USE_VBO, + + //! VBO's used. The index-buffer information is updated each frame + //! with only the visible parts of a tree-node. + //! So the vertex-buffer is static and the index-buffer is dynamic. + //! This is the default + EOV_USE_VBO_WITH_VISIBITLY +}; + +//! Kind of checks polygons of the octree scene nodes use against camera +enum EOCTREE_POLYGON_CHECKS +{ + //! Check against box of the camera frustum + //! This is the default + EOPC_BOX, + + //! against the camera frustum + EOPC_FRUSTUM + +}; + +//! A scene node displaying a static mesh +class IOctreeSceneNode : public irr::scene::IMeshSceneNode +{ +public: + + //! Constructor + /** Use setMesh() to set the mesh to display. + */ + IOctreeSceneNode(ISceneNode* parent, ISceneManager* mgr, s32 id, + const core::vector3df& position = core::vector3df(0,0,0), + const core::vector3df& rotation = core::vector3df(0,0,0), + const core::vector3df& scale = core::vector3df(1,1,1)) + : IMeshSceneNode(parent, mgr, id, position, rotation, scale) {} + + //! Set if/how vertex buffer object are used for the meshbuffers + /** NOTE: When there is already a mesh in the node this will rebuild + the octree. */ + virtual void setUseVBO(EOCTREENODE_VBO useVBO) = 0; + + //! Get if/how vertex buffer object are used for the meshbuffers + virtual EOCTREENODE_VBO getUseVBO() const = 0; + + //! Set the kind of tests polygons do for visibility against the camera + virtual void setPolygonChecks(EOCTREE_POLYGON_CHECKS checks) = 0; + + //! Get the kind of tests polygons do for visibility against the camera + virtual EOCTREE_POLYGON_CHECKS getPolygonChecks() const = 0; +}; + +} // end namespace scene +} // end namespace irr + + +#endif + diff --git a/include/ISceneManager.h b/include/ISceneManager.h index 2a105c55..2c244296 100644 --- a/include/ISceneManager.h +++ b/include/ISceneManager.h @@ -110,6 +110,7 @@ namespace scene class IMeshSceneNode; class IMeshWriter; class IMetaTriangleSelector; + class IOctreeSceneNode; class IParticleSystemSceneNode; class ISceneCollisionManager; class ISceneLoader; @@ -543,17 +544,9 @@ namespace scene \param alsoAddIfMeshPointerZero: Add the scene node even if a 0 pointer is passed. \return Pointer to the octree if successful, otherwise 0. This pointer should not be dropped. See IReferenceCounted::drop() for more information. */ - virtual IMeshSceneNode* addOctreeSceneNode(IAnimatedMesh* mesh, ISceneNode* parent=0, + virtual IOctreeSceneNode* addOctreeSceneNode(IAnimatedMesh* mesh, ISceneNode* parent=0, s32 id=-1, s32 minimalPolysPerNode=512, bool alsoAddIfMeshPointerZero=false) = 0; - //! Adds a scene node for rendering using a octree to the scene graph. - /** \deprecated Use addOctreeSceneNode instead. This method may be removed by Irrlicht 1.9. */ - _IRR_DEPRECATED_ IMeshSceneNode* addOctTreeSceneNode(IAnimatedMesh* mesh, ISceneNode* parent=0, - s32 id=-1, s32 minimalPolysPerNode=512, bool alsoAddIfMeshPointerZero=false) - { - return addOctreeSceneNode(mesh, parent, id, minimalPolysPerNode, alsoAddIfMeshPointerZero); - } - //! Adds a scene node for rendering using a octree to the scene graph. /** This a good method for rendering scenes with lots of geometry. The octree is built on the fly from the mesh, much @@ -567,17 +560,9 @@ namespace scene \param alsoAddIfMeshPointerZero: Add the scene node even if a 0 pointer is passed. \return Pointer to the octree if successful, otherwise 0. This pointer should not be dropped. See IReferenceCounted::drop() for more information. */ - virtual IMeshSceneNode* addOctreeSceneNode(IMesh* mesh, ISceneNode* parent=0, + virtual IOctreeSceneNode* addOctreeSceneNode(IMesh* mesh, ISceneNode* parent=0, s32 id=-1, s32 minimalPolysPerNode=256, bool alsoAddIfMeshPointerZero=false) = 0; - //! Adds a scene node for rendering using a octree to the scene graph. - /** \deprecated Use addOctreeSceneNode instead. This method may be removed by Irrlicht 1.9. */ - _IRR_DEPRECATED_ IMeshSceneNode* addOctTreeSceneNode(IMesh* mesh, ISceneNode* parent=0, - s32 id=-1, s32 minimalPolysPerNode=256, bool alsoAddIfMeshPointerZero=false) - { - return addOctreeSceneNode(mesh, parent, id, minimalPolysPerNode, alsoAddIfMeshPointerZero); - } - //! Adds a camera scene node to the scene graph and sets it as active camera. /** This camera does not react on user input like for example the one created with addCameraSceneNodeFPS(). If you want to move or animate it, use animators or the @@ -1272,7 +1257,7 @@ namespace scene \endcode \param mesh: Mesh of which the triangles are taken. \param node: Scene node of which transformation is used. - \param separateMeshbuffers: When true it's possible to get information which meshbuffer + \param separateMeshbuffers: When true it's possible to get information which meshbuffer got hit in collision tests. But has a slight speed cost. \return The selector, or null if not successful. If you no longer need the selector, you should call ITriangleSelector::drop(). @@ -1291,7 +1276,7 @@ namespace scene //! Creates a simple ITriangleSelector, based on an animated mesh scene node. /** Details of the mesh associated with the node will be extracted internally. \param node The animated mesh scene node from which to build the selector - \param separateMeshbuffers: When true it's possible to get information which meshbuffer + \param separateMeshbuffers: When true it's possible to get information which meshbuffer got hit in collision tests. But has a slight speed cost. */ virtual ITriangleSelector* createTriangleSelector(IAnimatedMeshSceneNode* node, bool separateMeshbuffers=false) = 0; diff --git a/include/irrlicht.h b/include/irrlicht.h index a813a490..705f6b2a 100644 --- a/include/irrlicht.h +++ b/include/irrlicht.h @@ -115,6 +115,7 @@ #include "IMeshManipulator.h" #include "IMeshSceneNode.h" #include "IMeshWriter.h" +#include "IOctreeSceneNode.h" #include "IColladaMeshWriter.h" #include "IMetaTriangleSelector.h" #include "IOSOperator.h" diff --git a/source/Irrlicht/CDefaultSceneNodeFactory.cpp b/source/Irrlicht/CDefaultSceneNodeFactory.cpp index 2902501a..0a82918f 100644 --- a/source/Irrlicht/CDefaultSceneNodeFactory.cpp +++ b/source/Irrlicht/CDefaultSceneNodeFactory.cpp @@ -14,6 +14,7 @@ #include "IParticleSystemSceneNode.h" #include "ILightSceneNode.h" #include "IMeshSceneNode.h" +#include "IOctreeSceneNode.h" namespace irr { diff --git a/source/Irrlicht/COctreeSceneNode.cpp b/source/Irrlicht/COctreeSceneNode.cpp index b77626c6..5fdb2cd4 100644 --- a/source/Irrlicht/COctreeSceneNode.cpp +++ b/source/Irrlicht/COctreeSceneNode.cpp @@ -24,11 +24,10 @@ namespace scene //! constructor COctreeSceneNode::COctreeSceneNode(ISceneNode* parent, ISceneManager* mgr, s32 id, s32 minimalPolysPerNode) - : IMeshSceneNode(parent, mgr, id), StdOctree(0), LightMapOctree(0), + : IOctreeSceneNode(parent, mgr, id), StdOctree(0), LightMapOctree(0), TangentsOctree(0), VertexType((video::E_VERTEX_TYPE)-1), MinimalPolysPerNode(minimalPolysPerNode), Mesh(0), Shadow(0), - UseVBOs(OCTREE_USE_HARDWARE), UseVisibilityAndVBOs(OCTREE_USE_VISIBILITY), - BoxBased(OCTREE_BOX_BASED) + UseVBOs(EOV_USE_VBO_WITH_VISIBITLY), PolygonChecks(EOPC_BOX) { #ifdef _DEBUG setDebugName("COctreeSceneNode"); @@ -136,10 +135,15 @@ void COctreeSceneNode::render() case video::EVT_STANDARD: { IRR_PROFILE(getProfiler().start(EPID_OC_CALCPOLYS)); - if (BoxBased) - StdOctree->calculatePolys(box); - else - StdOctree->calculatePolys(frust); + switch ( PolygonChecks ) + { + case EOPC_BOX: + StdOctree->calculatePolys(box); + break; + case EOPC_FRUSTUM: + StdOctree->calculatePolys(frust); + break; + } IRR_PROFILE(getProfiler().stop(EPID_OC_CALCPOLYS)); const Octree::SIndexData* d = StdOctree->getIndexData(); @@ -186,10 +190,15 @@ void COctreeSceneNode::render() case video::EVT_2TCOORDS: { IRR_PROFILE(getProfiler().start(EPID_OC_CALCPOLYS)); - if (BoxBased) - LightMapOctree->calculatePolys(box); - else - LightMapOctree->calculatePolys(frust); + switch ( PolygonChecks ) + { + case EOPC_BOX: + LightMapOctree->calculatePolys(box); + break; + case EOPC_FRUSTUM: + LightMapOctree->calculatePolys(frust); + break; + } IRR_PROFILE(getProfiler().stop(EPID_OC_CALCPOLYS)); const Octree::SIndexData* d = LightMapOctree->getIndexData(); @@ -207,9 +216,18 @@ void COctreeSceneNode::render() if (transparent == isTransparentPass) { driver->setMaterial(Materials[i]); - if (UseVBOs) + switch ( UseVBOs ) { - if (UseVisibilityAndVBOs) + case EOV_NO_VBO: + driver->drawIndexedTriangleList( + &LightMapMeshes[i].Vertices[0], + LightMapMeshes[i].Vertices.size(), + d[i].Indices, d[i].CurrentSize / 3); + break; + case EOV_USE_VBO: + driver->drawMeshBuffer ( &LightMapMeshes[i] ); + break; + case EOV_USE_VBO_WITH_VISIBITLY: { u16* oldPointer = LightMapMeshes[i].Indices.pointer(); const u32 oldSize = LightMapMeshes[i].Indices.size(); @@ -219,15 +237,9 @@ void COctreeSceneNode::render() driver->drawMeshBuffer ( &LightMapMeshes[i] ); LightMapMeshes[i].Indices.set_pointer(oldPointer, oldSize); LightMapMeshes[i].setDirty(scene::EBT_INDEX); + break; } - else - driver->drawMeshBuffer ( &LightMapMeshes[i] ); } - else - driver->drawIndexedTriangleList( - &LightMapMeshes[i].Vertices[0], - LightMapMeshes[i].Vertices.size(), - d[i].Indices, d[i].CurrentSize / 3); } } @@ -254,10 +266,15 @@ void COctreeSceneNode::render() case video::EVT_TANGENTS: { IRR_PROFILE(getProfiler().start(EPID_OC_CALCPOLYS)); - if (BoxBased) - TangentsOctree->calculatePolys(box); - else - TangentsOctree->calculatePolys(frust); + switch ( PolygonChecks ) + { + case EOPC_BOX: + TangentsOctree->calculatePolys(box); + break; + case EOPC_FRUSTUM: + TangentsOctree->calculatePolys(frust); + break; + } IRR_PROFILE(getProfiler().stop(EPID_OC_CALCPOLYS)); const Octree::SIndexData* d = TangentsOctree->getIndexData(); @@ -319,6 +336,27 @@ bool COctreeSceneNode::removeChild(ISceneNode* child) return ISceneNode::removeChild(child); } +void COctreeSceneNode::setUseVBO(EOCTREENODE_VBO useVBO) +{ + UseVBOs = useVBO; + if ( Mesh ) + createTree(Mesh); +} + +EOCTREENODE_VBO COctreeSceneNode::getUseVBO() const +{ + return UseVBOs; +} + +void COctreeSceneNode::setPolygonChecks(EOCTREE_POLYGON_CHECKS checks) +{ + PolygonChecks = checks; +} + +EOCTREE_POLYGON_CHECKS COctreeSceneNode::getPolygonChecks() const +{ + return PolygonChecks; +} //! Creates shadow volume scene node as child of this node //! and returns a pointer to it. @@ -371,7 +409,11 @@ bool COctreeSceneNode::createTree(IMesh* mesh) if (mesh->getMeshBufferCount()) { - // check for "larger" buffer types + // check for "largest" buffer types + // Also dropping buffers/materials for empty buffer + // (which looks like a horrible idea. If a user wanted that material without mesh he should still get it... + // but not going to change that now. Only documenting it after figuring out what happens here. + // It works at least as Materials are reset in deleteTree). VertexType = video::EVT_STANDARD; u32 meshReserve = 0; for (i=0; igetMeshBufferCount(); ++i) @@ -450,7 +492,7 @@ bool COctreeSceneNode::createTree(IMesh* mesh) Octree::SMeshChunk& nchunk = LightMapMeshes.getLast(); nchunk.MaterialId = Materials.size() - 1; - if (UseVisibilityAndVBOs) + if (UseVBOs == EOV_USE_VBO_WITH_VISIBITLY) { nchunk.setHardwareMappingHint(scene::EHM_STATIC, scene::EBT_VERTEX); nchunk.setHardwareMappingHint(scene::EHM_DYNAMIC, scene::EBT_INDEX); diff --git a/source/Irrlicht/COctreeSceneNode.h b/source/Irrlicht/COctreeSceneNode.h index 2ed2e2b8..3d1ff88d 100644 --- a/source/Irrlicht/COctreeSceneNode.h +++ b/source/Irrlicht/COctreeSceneNode.h @@ -5,15 +5,17 @@ #ifndef __C_OCTREE_SCENE_NODE_H_INCLUDED__ #define __C_OCTREE_SCENE_NODE_H_INCLUDED__ -#include "IMeshSceneNode.h" +#include "IOctreeSceneNode.h" #include "Octree.h" namespace irr { namespace scene { - //! implementation of the IBspTreeSceneNode - class COctreeSceneNode : public IMeshSceneNode + class COctreeSceneNode; + + //! implementation of the IOctreeSceneNode + class COctreeSceneNode : public IOctreeSceneNode { public: @@ -76,6 +78,20 @@ namespace scene //! or to remove attached childs. virtual bool removeChild(ISceneNode* child) _IRR_OVERRIDE_; + //! Set if/how vertex buffer object are used for the meshbuffers + /** NOTE: When there is already a mesh in the node this will rebuild + the octree. */ + virtual void setUseVBO(EOCTREENODE_VBO useVBO) _IRR_OVERRIDE_; + + //! Get if/how vertex buffer object are used for the meshbuffers + virtual EOCTREENODE_VBO getUseVBO() const _IRR_OVERRIDE_; + + //! Set the kind of tests polygons do for visibility against the camera + virtual void setPolygonChecks(EOCTREE_POLYGON_CHECKS checks) _IRR_OVERRIDE_; + + //! Get the kind of tests polygons do for visibility against the camera + virtual EOCTREE_POLYGON_CHECKS getPolygonChecks() const _IRR_OVERRIDE_; + private: void deleteTree(); @@ -100,12 +116,9 @@ namespace scene IMesh * Mesh; IShadowVolumeSceneNode* Shadow; - //! use VBOs for rendering where possible - bool UseVBOs; - //! use visibility information together with VBOs - bool UseVisibilityAndVBOs; - //! use bounding box or frustum for calculate polys - bool BoxBased; + + EOCTREENODE_VBO UseVBOs; + EOCTREE_POLYGON_CHECKS PolygonChecks; }; } // end namespace scene diff --git a/source/Irrlicht/CSceneManager.cpp b/source/Irrlicht/CSceneManager.cpp index 17977ec5..dfba5be7 100644 --- a/source/Irrlicht/CSceneManager.cpp +++ b/source/Irrlicht/CSceneManager.cpp @@ -678,7 +678,7 @@ IAnimatedMeshSceneNode* CSceneManager::addAnimatedMeshSceneNode(IAnimatedMesh* m //! Adds a scene node for rendering using a octree to the scene graph. This a good method for rendering //! scenes with lots of geometry. The Octree is built on the fly from the mesh, much //! faster then a bsp tree. -IMeshSceneNode* CSceneManager::addOctreeSceneNode(IAnimatedMesh* mesh, ISceneNode* parent, +IOctreeSceneNode* CSceneManager::addOctreeSceneNode(IAnimatedMesh* mesh, ISceneNode* parent, s32 id, s32 minimalPolysPerNode, bool alsoAddIfMeshPointerZero) { if (!alsoAddIfMeshPointerZero && (!mesh || !mesh->getFrameCount())) @@ -693,7 +693,7 @@ IMeshSceneNode* CSceneManager::addOctreeSceneNode(IAnimatedMesh* mesh, ISceneNod //! Adds a scene node for rendering using a octree. This a good method for rendering //! scenes with lots of geometry. The Octree is built on the fly from the mesh, much //! faster then a bsp tree. -IMeshSceneNode* CSceneManager::addOctreeSceneNode(IMesh* mesh, ISceneNode* parent, +IOctreeSceneNode* CSceneManager::addOctreeSceneNode(IMesh* mesh, ISceneNode* parent, s32 id, s32 minimalPolysPerNode, bool alsoAddIfMeshPointerZero) { if (!alsoAddIfMeshPointerZero && !mesh) diff --git a/source/Irrlicht/CSceneManager.h b/source/Irrlicht/CSceneManager.h index 4944894c..dd57b1c2 100644 --- a/source/Irrlicht/CSceneManager.h +++ b/source/Irrlicht/CSceneManager.h @@ -118,13 +118,13 @@ namespace scene //! Adds a scene node for rendering using a octree to the scene graph. This a good method for rendering //! scenes with lots of geometry. The Octree is built on the fly from the mesh, much //! faster then a bsp tree. - virtual IMeshSceneNode* addOctreeSceneNode(IAnimatedMesh* mesh, ISceneNode* parent=0, + virtual IOctreeSceneNode* addOctreeSceneNode(IAnimatedMesh* mesh, ISceneNode* parent=0, s32 id=-1, s32 minimalPolysPerNode=512, bool alsoAddIfMeshPointerZero=false) _IRR_OVERRIDE_; //! Adss a scene node for rendering using a octree. This a good method for rendering //! scenes with lots of geometry. The Octree is built on the fly from the mesh, much //! faster then a bsp tree. - virtual IMeshSceneNode* addOctreeSceneNode(IMesh* mesh, ISceneNode* parent=0, + virtual IOctreeSceneNode* addOctreeSceneNode(IMesh* mesh, ISceneNode* parent=0, s32 id=-1, s32 minimalPolysPerNode=128, bool alsoAddIfMeshPointerZero=false) _IRR_OVERRIDE_; //! Adds a camera scene node to the tree and sets it as active camera. diff --git a/source/Irrlicht/Octree.h b/source/Irrlicht/Octree.h index 2bb879d6..9746f05d 100644 --- a/source/Irrlicht/Octree.h +++ b/source/Irrlicht/Octree.h @@ -14,12 +14,6 @@ /** Flags for Octree */ -//! use meshbuffer for drawing, enables VBO usage -#define OCTREE_USE_HARDWARE false -//! use visibility information together with VBOs -#define OCTREE_USE_VISIBILITY true -//! use bounding box or frustum for calculate polys -#define OCTREE_BOX_BASED true //! bypass full invisible/visible test #define OCTREE_PARENTTEST