From 3c4ac201ce94d6f78abeb50625f6bb8a7366d23a Mon Sep 17 00:00:00 2001 From: cutealien Date: Wed, 22 Nov 2023 14:52:19 +0000 Subject: [PATCH] Add options for transparency node sorting algorithm Until last summer we sorted by object origin to camera distance Since then we used nearest transformed bbox-extent to camera. I've now added an enum to allow switching those plus 2 new: - none (so sorting based on scenegraph instead) - object center to camera. Which I made the new default as it worked the best in my tests. I already experimented with a few more ones like different sphere sizes (bbox radius, minimal inbound radius, maximal inbound radius) around center or origin to handle objects with different sizes, but that just gave worse results for all my test cases. Likely algorithms we should still try: - Collision point with bounding-box in line between camera and object center (sounds a bit slow, but maybe worth it) - Distance to camera plane (instead of camera position). But needs additional parameter to distance functions first (maybe normalized view vector will do). That should be useful when working with planar objects. git-svn-id: svn://svn.code.sf.net/p/irrlicht/code/trunk@6572 dfc29bdd-3216-0410-991c-e03cc46cb475 --- changes.txt | 1 + include/ISceneManager.h | 31 ++++++++++++ source/Irrlicht/CSceneManager.cpp | 78 +++++++++++++++++++++++++++++-- source/Irrlicht/CSceneManager.h | 50 +++++++++----------- tests/tests-last-passed-at.txt | 2 +- 5 files changed, 127 insertions(+), 35 deletions(-) diff --git a/changes.txt b/changes.txt index 1e862545..62be9e55 100644 --- a/changes.txt +++ b/changes.txt @@ -1,6 +1,7 @@ -------------------------- Changes in 1.9 (not yet released) +- Add options for transparency node sorting algorithm - CImageWriterPNG now also supports the writeImageToFile param to allow setting compressing level. 0 stays default, 1-10 for range increasing compression level. - Add io::IUserData which can be set in SMaterial to make it easer passing additional material values to shaders - Add lens shift support for the camera and the perspective projection functions diff --git a/include/ISceneManager.h b/include/ISceneManager.h index fab9a7ed..60c325d5 100644 --- a/include/ISceneManager.h +++ b/include/ISceneManager.h @@ -97,7 +97,32 @@ namespace scene //! Drawn after transparent effect nodes. For custom gui's. Unsorted (in order nodes registered themselves). ESNRP_GUI = 128 + }; + //! Enumeration for sorting transparent nodes + /** Sorting used for nodes with ESNRP_TRANSPARENT or ESNRP_AUTOMATIC+transparency. + Also used for ESNRP_TRANSPARENT_EFFECT nodes (in an independent array) + Transparent nodes are always drawn back to front based on distance to camera. + This enum controls which points are used for the distance. */ + enum E_TRANSPARENT_NODE_SORTING + { + //! Don't sort, but draw in the order in which nodes registered themselves for rendering + //! By default this is the order in which nodes are in the scenegraph + //! Which can be used to do some custom sorting via scene-graph (mainly useful if you only have to that once) + ETNS_NONE, + + //! Distance from node origin to camera + ETNS_ORIGIN, + + //! Distance from node center to camera + ETNS_CENTER, + + //! Distance from the nearest of the 2 transformed bounding-box extend corners to camera + ETNS_BBOX_EXTENTS, + + //! Default sorting Irrlicht uses currently + //! This may change in the future + ETNS_DEFAULT = ETNS_CENTER }; class IAnimatedMesh; @@ -1676,6 +1701,12 @@ namespace scene //! Set current render pass. virtual void setCurrentRenderPass(E_SCENE_NODE_RENDER_PASS nextPass) =0; + //! Get current node sorting algorithm used for transparent nodes + virtual E_TRANSPARENT_NODE_SORTING getTransparentNodeSorting() const = 0; + + //! Set the node sorting algorithm used for transparent nodes + virtual void setTransparentNodeSorting(E_TRANSPARENT_NODE_SORTING sorting) = 0; + //! Get an instance of a geometry creator. /** The geometry creator provides some helper methods to create various types of basic geometry. This can be useful for custom scene nodes. */ diff --git a/source/Irrlicht/CSceneManager.cpp b/source/Irrlicht/CSceneManager.cpp index 93dd9199..ef789190 100644 --- a/source/Irrlicht/CSceneManager.cpp +++ b/source/Irrlicht/CSceneManager.cpp @@ -230,6 +230,8 @@ CSceneManager::CSceneManager(video::IVideoDriver* driver, io::IFileSystem* fs, // root node's scene manager SceneManager = this; + setTransparentNodeSorting(ETNS_DEFAULT); + if (Driver) Driver->grab(); @@ -1362,14 +1364,14 @@ u32 CSceneManager::registerNodeForRendering(ISceneNode* node, E_SCENE_NODE_RENDE case ESNRP_TRANSPARENT: if (!isCulled(node)) { - TransparentNodeList.push_back(TransparentNodeEntry(node, camWorldPos)); + TransparentNodeList.push_back(TransparentNodeEntry(node, funcTransparentNodeDistance(node, camWorldPos))); taken = 1; } break; case ESNRP_TRANSPARENT_EFFECT: if (!isCulled(node)) { - TransparentEffectNodeList.push_back(TransparentNodeEntry(node, camWorldPos)); + TransparentEffectNodeList.push_back(TransparentNodeEntry(node, funcTransparentNodeDistance(node, camWorldPos))); taken = 1; } break; @@ -1384,7 +1386,7 @@ u32 CSceneManager::registerNodeForRendering(ISceneNode* node, E_SCENE_NODE_RENDE if (Driver->needsTransparentRenderPass(node->getMaterial(i))) { // register as transparent node - TransparentNodeEntry e(node, camWorldPos); + TransparentNodeEntry e(node, funcTransparentNodeDistance(node, camWorldPos)); TransparentNodeList.push_back(e); taken = 1; break; @@ -1664,7 +1666,8 @@ void CSceneManager::drawAll() CurrentRenderPass = ESNRP_TRANSPARENT; Driver->getOverrideMaterial().Enabled = ((Driver->getOverrideMaterial().EnablePasses & CurrentRenderPass) != 0); - TransparentNodeList.sort(); // sort by distance from camera + if ( TransparentNodeSorting != ETNS_NONE ) + TransparentNodeList.sort(); // sort by distance from camera if (LightManager) { LightManager->OnRenderPassPreRender(CurrentRenderPass); @@ -1698,7 +1701,8 @@ void CSceneManager::drawAll() CurrentRenderPass = ESNRP_TRANSPARENT_EFFECT; Driver->getOverrideMaterial().Enabled = ((Driver->getOverrideMaterial().EnablePasses & CurrentRenderPass) != 0); - TransparentEffectNodeList.sort(); // sort by distance from camera + if ( TransparentNodeSorting != ETNS_NONE ) + TransparentEffectNodeList.sort(); // sort by distance from camera if (LightManager) { @@ -2191,6 +2195,70 @@ E_SCENE_NODE_RENDER_PASS CSceneManager::getSceneNodeRenderPass() const return CurrentRenderPass; } +// Not sorting this later +static f32 transparentSortingNone(const ISceneNode* node, const core::vector3df& camera) +{ + return 0.f; +} + +// Distance from node origin to camera +static f32 transparentSortingByOrigin(const ISceneNode* node, const core::vector3df& camera) +{ + return node->getAbsolutePosition().getDistanceFromSQ(camera); +} + +// Distance from node center to camera +static f32 transparentSortingByCenter(const ISceneNode* node, const core::vector3df& camera) +{ + core::vector3df center = node->getBoundingBox().getCenter(); + const core::matrix4& absMat = node->getAbsoluteTransformation(); + absMat.rotateVect(center); + return (absMat.getTranslation()+center).getDistanceFromSQ(camera); +} + +/* +const core::aabbox3d box = Node->getTransformedBoundingBox(); +Distance = core::min_(camera.getDistanceFromSQ(box.MinEdge), camera.getDistanceFromSQ(box.MaxEdge)); +*/ +static f32 transparentSortingBBoxExtents(const ISceneNode* node, const core::vector3df& camera) +{ + const core::aabbox3d& box = node->getBoundingBox(); + const f32* m = node->getAbsoluteTransformation().pointer(); + + f32 p[4]; + p[0] = camera.X - (box.MinEdge.X * m[0] + box.MinEdge.Y * m[4] + box.MinEdge.Z * m[8] + m[12]); + p[1] = camera.Y - (box.MinEdge.X * m[1] + box.MinEdge.Y * m[5] + box.MinEdge.Z * m[9] + m[13]); + p[2] = camera.Z - (box.MinEdge.X * m[2] + box.MinEdge.Y * m[6] + box.MinEdge.Z * m[10] + m[14]); + f32 l0 = (p[0] * p[0]) + (p[1] * p[1]) + (p[2] * p[2]); + + p[0] = camera.X - (box.MaxEdge.X * m[0] + box.MaxEdge.Y * m[4] + box.MaxEdge.Z * m[8] + m[12]); + p[1] = camera.Y - (box.MaxEdge.X * m[1] + box.MaxEdge.Y * m[5] + box.MaxEdge.Z * m[9] + m[13]); + p[2] = camera.Z - (box.MaxEdge.X * m[2] + box.MaxEdge.Y * m[6] + box.MaxEdge.Z * m[10] + m[14]); + f32 l1 = (p[0] * p[0]) + (p[1] * p[1]) + (p[2] * p[2]); + return core::min_(l0, l1); +} + +void CSceneManager::setTransparentNodeSorting(E_TRANSPARENT_NODE_SORTING sorting) +{ + TransparentNodeSorting = sorting; + switch ( TransparentNodeSorting ) + { + case ETNS_NONE: + funcTransparentNodeDistance = transparentSortingNone; + break; + case ETNS_ORIGIN: + funcTransparentNodeDistance = transparentSortingByOrigin; + break; + case ETNS_CENTER: + funcTransparentNodeDistance = transparentSortingByCenter; + break; + case ETNS_BBOX_EXTENTS: + funcTransparentNodeDistance = transparentSortingBBoxExtents; + break; + default: + break; + } +} //! Returns an interface to the mesh cache which is shared between all existing scene managers. IMeshCache* CSceneManager::getMeshCache() diff --git a/source/Irrlicht/CSceneManager.h b/source/Irrlicht/CSceneManager.h index 8d3e83be..3bf83be2 100644 --- a/source/Irrlicht/CSceneManager.h +++ b/source/Irrlicht/CSceneManager.h @@ -445,6 +445,16 @@ namespace scene //! Returns current render pass. virtual E_SCENE_NODE_RENDER_PASS getSceneNodeRenderPass() const IRR_OVERRIDE; + //! Get current node sorting algorithm used for transparent nodes + virtual E_TRANSPARENT_NODE_SORTING getTransparentNodeSorting() const IRR_OVERRIDE + { + return TransparentNodeSorting; + } + + //! Set the node sorting algorithm used for transparent nodes + virtual void setTransparentNodeSorting(E_TRANSPARENT_NODE_SORTING sorting) IRR_OVERRIDE; + + //! Creates a new scene manager. virtual ISceneManager* createNewSceneManager(bool cloneContent) IRR_OVERRIDE; @@ -567,37 +577,13 @@ namespace scene void* TextureValue; }; - /* - const core::aabbox3d box = Node->getTransformedBoundingBox(); - Distance = core::min_(camera.getDistanceFromSQ(box.MinEdge), camera.getDistanceFromSQ(box.MaxEdge)); - */ - static inline f32 estimatedSphereDistance(const ISceneNode* node, const core::vector3df& camera) - { - const core::aabbox3d& box = node->getBoundingBox(); - const f32* m = node->getAbsoluteTransformation().pointer(); - - f32 p[4]; - p[0] = camera.X - (box.MinEdge.X * m[0] + box.MinEdge.Y * m[4] + box.MinEdge.Z * m[8] + m[12]); - p[1] = camera.Y - (box.MinEdge.X * m[1] + box.MinEdge.Y * m[5] + box.MinEdge.Z * m[9] + m[13]); - p[2] = camera.Z - (box.MinEdge.X * m[2] + box.MinEdge.Y * m[6] + box.MinEdge.Z * m[10] + m[14]); - f32 l0 = (p[0] * p[0]) + (p[1] * p[1]) + (p[2] * p[2]); - - p[0] = camera.X - (box.MaxEdge.X * m[0] + box.MaxEdge.Y * m[4] + box.MaxEdge.Z * m[8] + m[12]); - p[1] = camera.Y - (box.MaxEdge.X * m[1] + box.MaxEdge.Y * m[5] + box.MaxEdge.Z * m[9] + m[13]); - p[2] = camera.Z - (box.MaxEdge.X * m[2] + box.MaxEdge.Y * m[6] + box.MaxEdge.Z * m[10] + m[14]); - f32 l1 = (p[0] * p[0]) + (p[1] * p[1]) + (p[2] * p[2]); - return core::min_(l0, l1); - } - - //! sort on distance (center) to camera + //! Sort on distance to camera + //! Larger distances drawn first struct TransparentNodeEntry { - TransparentNodeEntry(ISceneNode* n, const core::vector3df& camera) - : Node(n) - { - //Distance = Node->getAbsoluteTransformation().getTranslation().getDistanceFromSQ(camera); - Distance = estimatedSphereDistance(n, camera); - } + TransparentNodeEntry(ISceneNode* n, const f32 distance) + : Node(n), Distance(distance) + {} bool operator < (const TransparentNodeEntry& other) const { @@ -682,6 +668,12 @@ namespace scene E_SCENE_NODE_RENDER_PASS CurrentRenderPass; + //! Algorithm used to sort transparent nodes + E_TRANSPARENT_NODE_SORTING TransparentNodeSorting; + //! Pointer to the actual algorithm to get the distance + // (Could be we have to pass more parameters for better results, like view normal) + f32 (*funcTransparentNodeDistance)(const ISceneNode* node, const core::vector3df& camera); + //! An optional callbacks manager to allow the user app finer control //! over the scene lighting and rendering. ILightManager* LightManager; diff --git a/tests/tests-last-passed-at.txt b/tests/tests-last-passed-at.txt index 7a936a53..61976c19 100644 --- a/tests/tests-last-passed-at.txt +++ b/tests/tests-last-passed-at.txt @@ -1,4 +1,4 @@ Tests finished. 72 tests of 72 passed. Compiled as DEBUG -Test suite pass at GMT Mon Nov 13 14:43:47 2023 +Test suite pass at GMT Wed Nov 22 14:39:17 2023