From 473ab1ea58e728cf8ddf2dc598d617d84af8e965 Mon Sep 17 00:00:00 2001 From: cutealien Date: Fri, 3 Jan 2020 11:13:57 +0000 Subject: [PATCH] Unify checks if materials should use transparent render pass with new IVideoDriver::needsTransparentRenderPass function. Fix bug that AnimatedMeshSceneNode ignored ReadOnlyMaterials flag when checking materials for transparent render passes. Make IVideoDriver::getMaterialRenderer const. Fix bugs in COctreeSceneNode, CMeshSceneNode and CAnimatedMeshSceneNode where check for transparency in OnRegisterSceneNode() and in render() where no longer identical (those got added after Irrlicht 1.8). Some notes for future: - Maybe we should have a getRenderPass instead of just needsTransparentRenderPass, but this way the code didn't need so much changes and behaves (aside from fixes) pretty much as before. - Still wondering if the default implementation in CNullDriver::needsTransparentRenderPass should always return false when SMaterial.ZWriteEnable is set to EZW_ON. This might be nicer with another material flag. Thought then we might want a material enum to choose the renderpass and that's more work. And we get some recursion as needsTransparentRenderPass might want to check result of getWriteZBuffer which calls needsTransparentRenderPass, so we might need a second function or an additional flag there. But return false when SMaterial.ZWriteEnable == EZW_ON could still be done as EZW_ON is a new flag so existing behavior shouldn't break. I just don't know right now if having an extra render pass for transparent nodes might still make sense even when zbuffer is not written or if that's really the only reason to do that. Any feedback anyone? git-svn-id: svn://svn.code.sf.net/p/irrlicht/code/trunk@6033 dfc29bdd-3216-0410-991c-e03cc46cb475 --- changes.txt | 2 + include/IVideoDriver.h | 5 ++- include/SMaterial.h | 24 +++++++---- source/Irrlicht/CAnimatedMeshSceneNode.cpp | 15 ++++--- source/Irrlicht/CD3D9Driver.cpp | 5 +++ source/Irrlicht/CD3D9Driver.h | 3 ++ source/Irrlicht/CMeshSceneNode.cpp | 48 ++++++---------------- source/Irrlicht/CNullDriver.cpp | 18 +++++++- source/Irrlicht/CNullDriver.h | 9 ++-- source/Irrlicht/COctreeSceneNode.cpp | 10 ++--- source/Irrlicht/COpenGLDriver.cpp | 5 +++ source/Irrlicht/COpenGLDriver.h | 3 ++ source/Irrlicht/CSceneManager.cpp | 4 +- source/Irrlicht/CSoftwareDriver2.cpp | 7 +++- source/Irrlicht/CSoftwareDriver2.h | 3 ++ tests/tests-last-passed-at.txt | 2 +- 16 files changed, 94 insertions(+), 69 deletions(-) diff --git a/changes.txt b/changes.txt index 7b1d0dbe..2dd72643 100644 --- a/changes.txt +++ b/changes.txt @@ -1,5 +1,7 @@ -------------------------- Changes in 1.9 (not yet released) +- Fix bug that AnimatedMeshSceneNode ignored ReadOnlyMaterials flag when checking materials for transparent render passes. +- Unify checks if materials should use transparent render pass with new IVideoDriver::needsTransparentRenderPass function. - Material.ZWriteEnable is now of type E_ZWRITE instead of bool. This allows now setting materials to always "on" independent of material type and transparency. This breaks compiling. To have old values replace false with EZW_OFF and true with EWZ_AUTO. - Materials EMT_REFLECTION_2_LAYER and EMT_TRANSPARENT_REFLECTION_2_LAYER on OpenGL are now same as in D3D9. Before GL used a sphere-map for those instead of reflection. diff --git a/include/IVideoDriver.h b/include/IVideoDriver.h index c49c7428..dea34a3a 100644 --- a/include/IVideoDriver.h +++ b/include/IVideoDriver.h @@ -1317,7 +1317,7 @@ namespace video the E_MATERIAL_TYPE enum or a value which was returned by addMaterialRenderer(). \return Pointer to material renderer or null if not existing. */ - virtual IMaterialRenderer* getMaterialRenderer(u32 idx) =0; + virtual IMaterialRenderer* getMaterialRenderer(u32 idx) const = 0; //! Get amount of currently available material renderers. /** \return Amount of currently available material renderers. */ @@ -1523,6 +1523,9 @@ namespace video //! Check if the driver supports creating textures with the given color format /** \return True if the format is available, false if not. */ virtual bool queryTextureFormat(ECOLOR_FORMAT format) const = 0; + + //! Used by some SceneNodes to check if a material should be rendered in the transparent render pass + virtual bool needsTransparentRenderPass(const irr::video::SMaterial& material) const = 0; }; } // end namespace video diff --git a/include/SMaterial.h b/include/SMaterial.h index 90f5c671..3661f7ee 100644 --- a/include/SMaterial.h +++ b/include/SMaterial.h @@ -475,7 +475,7 @@ namespace video //! Store the blend factors /** textureBlendFunc/textureBlendFuncSeparate functions should be used to write properly blending factors to this parameter. If you use EMT_ONETEXTURE_BLEND - type for this material, this field should be equal to MaterialTypeParam. */ + type for this material, this field should be equal to MaterialTypeParams. */ f32 BlendFactor; //! DEPRECATED. Will be removed after Irrlicht 1.9. Please use PolygonOffsetDepthBias instead. @@ -779,14 +779,9 @@ namespace video inline bool operator==(const SMaterial& b) const { return !(b!=*this); } - bool isTransparent() const + //! Check if material needs alpha blending + bool isAlphaBlendOperation() const { - if ( MaterialType==EMT_TRANSPARENT_ADD_COLOR || - MaterialType==EMT_TRANSPARENT_ALPHA_CHANNEL || - MaterialType==EMT_TRANSPARENT_VERTEX_ALPHA || - MaterialType==EMT_TRANSPARENT_REFLECTION_2_LAYER ) - return true; - if (BlendOperation != EBO_NONE && BlendFactor != 0.f) { E_BLEND_FACTOR srcRGBFact = EBF_ZERO; @@ -804,6 +799,19 @@ namespace video return true; } } + return false; + } + + //! Check for some fixed-function transparent types. Still used internally, but might be deprecated soon. + //! You probably should not use this anymore, IVideoDriver::needsTransparentRenderPass is more useful in most situations + //! as it asks the material renders directly what they do with the material. + bool isTransparent() const + { + if ( MaterialType==EMT_TRANSPARENT_ADD_COLOR || + MaterialType==EMT_TRANSPARENT_ALPHA_CHANNEL || + MaterialType==EMT_TRANSPARENT_VERTEX_ALPHA || + MaterialType==EMT_TRANSPARENT_REFLECTION_2_LAYER ) + return true; return false; } diff --git a/source/Irrlicht/CAnimatedMeshSceneNode.cpp b/source/Irrlicht/CAnimatedMeshSceneNode.cpp index d8c8491d..fb992b49 100644 --- a/source/Irrlicht/CAnimatedMeshSceneNode.cpp +++ b/source/Irrlicht/CAnimatedMeshSceneNode.cpp @@ -149,7 +149,7 @@ void CAnimatedMeshSceneNode::buildFrameNr(u32 timeMs) void CAnimatedMeshSceneNode::OnRegisterSceneNode() { - if (IsVisible) + if (IsVisible && Mesh) { // because this node supports rendering of mixed mode meshes consisting of // transparent and solid material at the same time, we need to go through all @@ -163,12 +163,12 @@ void CAnimatedMeshSceneNode::OnRegisterSceneNode() int solidCount = 0; // count transparent and solid materials in this scene node - for (u32 i=0; igetMeshBufferCount() : Materials.size(); + for (u32 i=0; igetMaterialRenderer(Materials[i].MaterialType); + const video::SMaterial& material = ReadOnlyMaterials ? Mesh->getMeshBuffer(i)->getMaterial() : Materials[i]; - if ((rnd && rnd->isTransparent()) || Materials[i].isTransparent()) + if ( driver->needsTransparentRenderPass(Materials[i]) ) ++transparentCount; else ++solidCount; @@ -274,7 +274,7 @@ void CAnimatedMeshSceneNode::render() return; - bool isTransparentPass = + const bool isTransparentPass = SceneManager->getSceneNodeRenderPass() == scene::ESNRP_TRANSPARENT; ++PassCount; @@ -329,8 +329,7 @@ void CAnimatedMeshSceneNode::render() { for (u32 i=0; igetMeshBufferCount(); ++i) { - video::IMaterialRenderer* rnd = driver->getMaterialRenderer(Materials[i].MaterialType); - bool transparent = (rnd && rnd->isTransparent()); + const bool transparent = driver->needsTransparentRenderPass(Materials[i]); // only render transparent buffer if this is the transparent render pass // and solid only in solid pass diff --git a/source/Irrlicht/CD3D9Driver.cpp b/source/Irrlicht/CD3D9Driver.cpp index e337c23a..03a45864 100644 --- a/source/Irrlicht/CD3D9Driver.cpp +++ b/source/Irrlicht/CD3D9Driver.cpp @@ -3580,6 +3580,11 @@ bool CD3D9Driver::queryTextureFormat(ECOLOR_FORMAT format) const return getD3DFormatFromColorFormat(format) != D3DFMT_UNKNOWN; } +bool CD3D9Driver::needsTransparentRenderPass(const irr::video::SMaterial& material) const +{ + return CNullDriver::needsTransparentRenderPass(material) || material.isAlphaBlendOperation(); +} + u32 CD3D9Driver::getD3DBlend(E_BLEND_FACTOR factor) const { u32 r = 0; diff --git a/source/Irrlicht/CD3D9Driver.h b/source/Irrlicht/CD3D9Driver.h index b42d39e4..2ceed2ea 100644 --- a/source/Irrlicht/CD3D9Driver.h +++ b/source/Irrlicht/CD3D9Driver.h @@ -300,6 +300,9 @@ namespace video //! Check if the driver supports creating textures with the given color format virtual bool queryTextureFormat(ECOLOR_FORMAT format) const _IRR_OVERRIDE_; + //! Used by some SceneNodes to check if a material should be rendered in the transparent render pass + virtual bool needsTransparentRenderPass(const irr::video::SMaterial& material) const _IRR_OVERRIDE_; + //! Get the current color format of the color buffer /** \return Color format of the color buffer as D3D color value. */ D3DFORMAT getD3DColorFormat() const; diff --git a/source/Irrlicht/CMeshSceneNode.cpp b/source/Irrlicht/CMeshSceneNode.cpp index def77d43..108711a4 100644 --- a/source/Irrlicht/CMeshSceneNode.cpp +++ b/source/Irrlicht/CMeshSceneNode.cpp @@ -52,7 +52,7 @@ CMeshSceneNode::~CMeshSceneNode() //! frame void CMeshSceneNode::OnRegisterSceneNode() { - if (IsVisible) + if (IsVisible && Mesh) { // because this node supports rendering of mixed mode meshes consisting of // transparent and solid material at the same time, we need to go through all @@ -66,41 +66,18 @@ void CMeshSceneNode::OnRegisterSceneNode() int solidCount = 0; // count transparent and solid materials in this scene node - if (ReadOnlyMaterials && Mesh) + const u32 numMaterials = ReadOnlyMaterials ? Mesh->getMeshBufferCount() : Materials.size(); + for (u32 i=0; igetMeshBuffer(i)->getMaterial() : Materials[i]; - for (u32 i=0; igetMeshBufferCount(); ++i) - { - scene::IMeshBuffer* mb = Mesh->getMeshBuffer(i); - video::IMaterialRenderer* rnd = mb ? driver->getMaterialRenderer(mb->getMaterial().MaterialType) : 0; + if ( driver->needsTransparentRenderPass(Materials[i]) ) + ++transparentCount; + else + ++solidCount; - if (rnd && rnd->isTransparent()) - ++transparentCount; - else - ++solidCount; - - if (solidCount && transparentCount) - break; - } - } - else - { - // count copied materials - - for (u32 i=0; igetMaterialRenderer(Materials[i].MaterialType); - - if ((rnd && rnd->isTransparent()) || Materials[i].isTransparent()) - ++transparentCount; - else - ++solidCount; - - if (solidCount && transparentCount) - break; - } + if (solidCount && transparentCount) + break; } // register according to material types counted @@ -124,7 +101,7 @@ void CMeshSceneNode::render() if (!Mesh || !driver) return; - bool isTransparentPass = + const bool isTransparentPass = SceneManager->getSceneNodeRenderPass() == scene::ESNRP_TRANSPARENT; ++PassCount; @@ -165,8 +142,7 @@ void CMeshSceneNode::render() { const video::SMaterial& material = ReadOnlyMaterials ? mb->getMaterial() : Materials[i]; - video::IMaterialRenderer* rnd = driver->getMaterialRenderer(material.MaterialType); - bool transparent = (rnd && rnd->isTransparent()); + const bool transparent = driver->needsTransparentRenderPass(material); // only render transparent buffer if this is the transparent render pass // and solid only in solid pass diff --git a/source/Irrlicht/CNullDriver.cpp b/source/Irrlicht/CNullDriver.cpp index 06a7f97c..3c4d745c 100644 --- a/source/Irrlicht/CNullDriver.cpp +++ b/source/Irrlicht/CNullDriver.cpp @@ -2325,7 +2325,7 @@ void CNullDriver::deleteMaterialRenders() //! Returns pointer to material renderer or null -IMaterialRenderer* CNullDriver::getMaterialRenderer(u32 idx) +IMaterialRenderer* CNullDriver::getMaterialRenderer(u32 idx) const { if ( idx < MaterialRenderers.size() ) return MaterialRenderers[idx].Renderer; @@ -2749,6 +2749,22 @@ core::dimension2du CNullDriver::getMaxTextureSize() const return core::dimension2du(0x10000,0x10000); // maybe large enough } +bool CNullDriver::needsTransparentRenderPass(const irr::video::SMaterial& material) const +{ + // TODO: I suspect it would be nice if the material had an enum for further control. + // Especially it probably makes sense to allow disabling transparent render pass as soon as material.ZWriteEnable is on. + // But then we might want an enum for the renderpass in material instead of just a transparency flag in material - and that's more work. + // Or we could at least set return false when material.ZWriteEnable is EZW_ON? Still considering that... + // Be careful - this function is deeply connected to getWriteZBuffer as transparent render passes are usually about rendering with + // zwrite disabled and getWriteZBuffer calls this function. + + video::IMaterialRenderer* rnd = getMaterialRenderer(material.MaterialType); + if (rnd && rnd->isTransparent()) + return true; + + return false; +} + //! Color conversion convenience function /** Convert an image (as array of pixels) from source to destination diff --git a/source/Irrlicht/CNullDriver.h b/source/Irrlicht/CNullDriver.h index ec2e095f..fa4d7279 100644 --- a/source/Irrlicht/CNullDriver.h +++ b/source/Irrlicht/CNullDriver.h @@ -533,7 +533,7 @@ namespace video s32 userData=0) _IRR_OVERRIDE_; //! Returns pointer to material renderer or null - virtual IMaterialRenderer* getMaterialRenderer(u32 idx) _IRR_OVERRIDE_; + virtual IMaterialRenderer* getMaterialRenderer(u32 idx) const _IRR_OVERRIDE_; //! Returns amount of currently available material renderers. virtual u32 getMaterialRendererCount() const _IRR_OVERRIDE_; @@ -667,6 +667,9 @@ namespace video //! Returns the maximum texture size supported. virtual core::dimension2du getMaxTextureSize() const _IRR_OVERRIDE_; + //! Used by some SceneNodes to check if a material should be rendered in the transparent render pass + virtual bool needsTransparentRenderPass(const irr::video::SMaterial& material) const _IRR_OVERRIDE_; + //! Color conversion convenience function /** Convert an image (as array of pixels) from source to destination array, thereby converting the color format. The pixel size is @@ -743,14 +746,14 @@ namespace video return (f32) getAverage ( p[(y * pitch) + x] ); } - inline bool getWriteZBuffer(const SMaterial&material) const + inline bool getWriteZBuffer(const SMaterial& material) const { switch ( material.ZWriteEnable ) { case video::EZW_OFF: return false; case video::EZW_AUTO: - return AllowZWriteOnTransparent || !material.isTransparent(); + return AllowZWriteOnTransparent || ! needsTransparentRenderPass(material); case video::EZW_ON: return true; } diff --git a/source/Irrlicht/COctreeSceneNode.cpp b/source/Irrlicht/COctreeSceneNode.cpp index c58fe390..16df1199 100644 --- a/source/Irrlicht/COctreeSceneNode.cpp +++ b/source/Irrlicht/COctreeSceneNode.cpp @@ -79,10 +79,7 @@ void COctreeSceneNode::OnRegisterSceneNode() // count transparent and solid materials in this scene node for (u32 i=0; igetMaterialRenderer(Materials[i].MaterialType); - - if ((rnd && rnd->isTransparent()) || Materials[i].isTransparent()) + if (driver->needsTransparentRenderPass(Materials[i])) ++transparentCount; else ++solidCount; @@ -145,7 +142,7 @@ void COctreeSceneNode::render() if (!camera) return; - bool isTransparentPass = + const bool isTransparentPass = SceneManager->getSceneNodeRenderPass() == scene::ESNRP_TRANSPARENT; ++PassCount; @@ -188,8 +185,7 @@ void COctreeSceneNode::render() if ( 0 == d[i].CurrentSize ) continue; - const video::IMaterialRenderer* const rnd = driver->getMaterialRenderer(Materials[i].MaterialType); - const bool transparent = (rnd && rnd->isTransparent()); + const bool transparent = driver->needsTransparentRenderPass(Materials[i]); // only render transparent buffer if this is the transparent render pass // and solid only in solid pass diff --git a/source/Irrlicht/COpenGLDriver.cpp b/source/Irrlicht/COpenGLDriver.cpp index b69a832f..8d233c2c 100644 --- a/source/Irrlicht/COpenGLDriver.cpp +++ b/source/Irrlicht/COpenGLDriver.cpp @@ -3617,6 +3617,11 @@ bool COpenGLDriver::queryTextureFormat(ECOLOR_FORMAT format) const return getColorFormatParameters(format, dummyInternalFormat, dummyPixelFormat, dummyPixelType, &dummyConverter); } +bool COpenGLDriver::needsTransparentRenderPass(const irr::video::SMaterial& material) const +{ + return CNullDriver::needsTransparentRenderPass(material) || material.isAlphaBlendOperation(); +} + //! Only used by the internal engine. Used to notify the driver that //! the window was resized. void COpenGLDriver::OnResize(const core::dimension2d& size) diff --git a/source/Irrlicht/COpenGLDriver.h b/source/Irrlicht/COpenGLDriver.h index 071d188d..46a6ebbc 100644 --- a/source/Irrlicht/COpenGLDriver.h +++ b/source/Irrlicht/COpenGLDriver.h @@ -382,6 +382,9 @@ namespace video //! Check if the driver supports creating textures with the given color format virtual bool queryTextureFormat(ECOLOR_FORMAT format) const _IRR_OVERRIDE_; + //! Used by some SceneNodes to check if a material should be rendered in the transparent render pass + virtual bool needsTransparentRenderPass(const irr::video::SMaterial& material) const _IRR_OVERRIDE_; + //! Convert E_PRIMITIVE_TYPE to OpenGL equivalent GLenum primitiveTypeToGL(scene::E_PRIMITIVE_TYPE type) const; diff --git a/source/Irrlicht/CSceneManager.cpp b/source/Irrlicht/CSceneManager.cpp index 4e42634f..0d1f7c9d 100644 --- a/source/Irrlicht/CSceneManager.cpp +++ b/source/Irrlicht/CSceneManager.cpp @@ -1377,9 +1377,7 @@ u32 CSceneManager::registerNodeForRendering(ISceneNode* node, E_SCENE_NODE_RENDE taken = 0; for (u32 i=0; igetMaterialRenderer(node->getMaterial(i).MaterialType); - if ((rnd && rnd->isTransparent()) || node->getMaterial(i).isTransparent()) + if (Driver->needsTransparentRenderPass(node->getMaterial(i))) { // register as transparent node TransparentNodeEntry e(node, camWorldPos); diff --git a/source/Irrlicht/CSoftwareDriver2.cpp b/source/Irrlicht/CSoftwareDriver2.cpp index 10a85e2e..43548107 100644 --- a/source/Irrlicht/CSoftwareDriver2.cpp +++ b/source/Irrlicht/CSoftwareDriver2.cpp @@ -178,7 +178,7 @@ void CBurningVideoDriver::setCurrentShader() bool zMaterialTest = Material.org.ZBuffer != ECFN_DISABLED && Material.org.ZWriteEnable != video::EZW_OFF && - getWriteZBuffer(Material.org); + ( AllowZWriteOnTransparent || !Material.org.isTransparent() ); EBurningFFShader shader = zMaterialTest ? ETR_TEXTURE_GOURAUD : ETR_TEXTURE_GOURAUD_NOZ; @@ -2381,6 +2381,11 @@ bool CBurningVideoDriver::queryTextureFormat(ECOLOR_FORMAT format) const return format == BURNINGSHADER_COLOR_FORMAT; } +bool CBurningVideoDriver::needsTransparentRenderPass(const irr::video::SMaterial& material) const +{ + return CNullDriver::needsTransparentRenderPass(material) || material.isTransparent(); +} + } // end namespace video } // end namespace irr diff --git a/source/Irrlicht/CSoftwareDriver2.h b/source/Irrlicht/CSoftwareDriver2.h index c9017306..3e9d2aaf 100644 --- a/source/Irrlicht/CSoftwareDriver2.h +++ b/source/Irrlicht/CSoftwareDriver2.h @@ -164,6 +164,9 @@ namespace video //! Check if the driver supports creating textures with the given color format virtual bool queryTextureFormat(ECOLOR_FORMAT format) const _IRR_OVERRIDE_; + //! Used by some SceneNodes to check if a material should be rendered in the transparent render pass + virtual bool needsTransparentRenderPass(const irr::video::SMaterial& material) const _IRR_OVERRIDE_; + IDepthBuffer * getDepthBuffer () { return DepthBuffer; } IStencilBuffer * getStencilBuffer () { return StencilBuffer; } diff --git a/tests/tests-last-passed-at.txt b/tests/tests-last-passed-at.txt index a62cf4c8..d47378ad 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 Thu Jan 02 15:17:55 2020 +Test suite pass at GMT Fri Jan 03 10:49:15 2020