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
This commit is contained in:
cutealien 2020-01-03 11:13:57 +00:00
parent 2928a632a4
commit 473ab1ea58
16 changed files with 94 additions and 69 deletions

@ -1,5 +1,7 @@
-------------------------- --------------------------
Changes in 1.9 (not yet released) 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. - 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. 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. - 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.

@ -1317,7 +1317,7 @@ namespace video
the E_MATERIAL_TYPE enum or a value which was returned by the E_MATERIAL_TYPE enum or a value which was returned by
addMaterialRenderer(). addMaterialRenderer().
\return Pointer to material renderer or null if not existing. */ \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. //! Get amount of currently available material renderers.
/** \return 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 //! Check if the driver supports creating textures with the given color format
/** \return True if the format is available, false if not. */ /** \return True if the format is available, false if not. */
virtual bool queryTextureFormat(ECOLOR_FORMAT format) const = 0; 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 } // end namespace video

@ -475,7 +475,7 @@ namespace video
//! Store the blend factors //! Store the blend factors
/** textureBlendFunc/textureBlendFuncSeparate functions should be used to write /** textureBlendFunc/textureBlendFuncSeparate functions should be used to write
properly blending factors to this parameter. If you use EMT_ONETEXTURE_BLEND 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; f32 BlendFactor;
//! DEPRECATED. Will be removed after Irrlicht 1.9. Please use PolygonOffsetDepthBias instead. //! 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 inline bool operator==(const SMaterial& b) const
{ return !(b!=*this); } { 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) if (BlendOperation != EBO_NONE && BlendFactor != 0.f)
{ {
E_BLEND_FACTOR srcRGBFact = EBF_ZERO; E_BLEND_FACTOR srcRGBFact = EBF_ZERO;
@ -804,6 +799,19 @@ namespace video
return true; 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; return false;
} }

@ -149,7 +149,7 @@ void CAnimatedMeshSceneNode::buildFrameNr(u32 timeMs)
void CAnimatedMeshSceneNode::OnRegisterSceneNode() void CAnimatedMeshSceneNode::OnRegisterSceneNode()
{ {
if (IsVisible) if (IsVisible && Mesh)
{ {
// because this node supports rendering of mixed mode meshes consisting of // 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 // transparent and solid material at the same time, we need to go through all
@ -163,12 +163,12 @@ void CAnimatedMeshSceneNode::OnRegisterSceneNode()
int solidCount = 0; int solidCount = 0;
// count transparent and solid materials in this scene node // count transparent and solid materials in this scene node
for (u32 i=0; i<Materials.size(); ++i) const u32 numMaterials = ReadOnlyMaterials ? Mesh->getMeshBufferCount() : Materials.size();
for (u32 i=0; i<numMaterials; ++i)
{ {
video::IMaterialRenderer* rnd = const video::SMaterial& material = ReadOnlyMaterials ? Mesh->getMeshBuffer(i)->getMaterial() : Materials[i];
driver->getMaterialRenderer(Materials[i].MaterialType);
if ((rnd && rnd->isTransparent()) || Materials[i].isTransparent()) if ( driver->needsTransparentRenderPass(Materials[i]) )
++transparentCount; ++transparentCount;
else else
++solidCount; ++solidCount;
@ -274,7 +274,7 @@ void CAnimatedMeshSceneNode::render()
return; return;
bool isTransparentPass = const bool isTransparentPass =
SceneManager->getSceneNodeRenderPass() == scene::ESNRP_TRANSPARENT; SceneManager->getSceneNodeRenderPass() == scene::ESNRP_TRANSPARENT;
++PassCount; ++PassCount;
@ -329,8 +329,7 @@ void CAnimatedMeshSceneNode::render()
{ {
for (u32 i=0; i<m->getMeshBufferCount(); ++i) for (u32 i=0; i<m->getMeshBufferCount(); ++i)
{ {
video::IMaterialRenderer* rnd = driver->getMaterialRenderer(Materials[i].MaterialType); const bool transparent = driver->needsTransparentRenderPass(Materials[i]);
bool transparent = (rnd && rnd->isTransparent());
// only render transparent buffer if this is the transparent render pass // only render transparent buffer if this is the transparent render pass
// and solid only in solid pass // and solid only in solid pass

@ -3580,6 +3580,11 @@ bool CD3D9Driver::queryTextureFormat(ECOLOR_FORMAT format) const
return getD3DFormatFromColorFormat(format) != D3DFMT_UNKNOWN; 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 CD3D9Driver::getD3DBlend(E_BLEND_FACTOR factor) const
{ {
u32 r = 0; u32 r = 0;

@ -300,6 +300,9 @@ namespace video
//! Check if the driver supports creating textures with the given color format //! Check if the driver supports creating textures with the given color format
virtual bool queryTextureFormat(ECOLOR_FORMAT format) const _IRR_OVERRIDE_; 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 //! Get the current color format of the color buffer
/** \return Color format of the color buffer as D3D color value. */ /** \return Color format of the color buffer as D3D color value. */
D3DFORMAT getD3DColorFormat() const; D3DFORMAT getD3DColorFormat() const;

@ -52,7 +52,7 @@ CMeshSceneNode::~CMeshSceneNode()
//! frame //! frame
void CMeshSceneNode::OnRegisterSceneNode() void CMeshSceneNode::OnRegisterSceneNode()
{ {
if (IsVisible) if (IsVisible && Mesh)
{ {
// because this node supports rendering of mixed mode meshes consisting of // 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 // transparent and solid material at the same time, we need to go through all
@ -66,41 +66,18 @@ void CMeshSceneNode::OnRegisterSceneNode()
int solidCount = 0; int solidCount = 0;
// count transparent and solid materials in this scene node // count transparent and solid materials in this scene node
if (ReadOnlyMaterials && Mesh) const u32 numMaterials = ReadOnlyMaterials ? Mesh->getMeshBufferCount() : Materials.size();
for (u32 i=0; i<numMaterials; ++i)
{ {
// count mesh materials const video::SMaterial& material = ReadOnlyMaterials ? Mesh->getMeshBuffer(i)->getMaterial() : Materials[i];
for (u32 i=0; i<Mesh->getMeshBufferCount(); ++i) if ( driver->needsTransparentRenderPass(Materials[i]) )
{ ++transparentCount;
scene::IMeshBuffer* mb = Mesh->getMeshBuffer(i); else
video::IMaterialRenderer* rnd = mb ? driver->getMaterialRenderer(mb->getMaterial().MaterialType) : 0; ++solidCount;
if (rnd && rnd->isTransparent()) if (solidCount && transparentCount)
++transparentCount; break;
else
++solidCount;
if (solidCount && transparentCount)
break;
}
}
else
{
// count copied materials
for (u32 i=0; i<Materials.size(); ++i)
{
video::IMaterialRenderer* rnd =
driver->getMaterialRenderer(Materials[i].MaterialType);
if ((rnd && rnd->isTransparent()) || Materials[i].isTransparent())
++transparentCount;
else
++solidCount;
if (solidCount && transparentCount)
break;
}
} }
// register according to material types counted // register according to material types counted
@ -124,7 +101,7 @@ void CMeshSceneNode::render()
if (!Mesh || !driver) if (!Mesh || !driver)
return; return;
bool isTransparentPass = const bool isTransparentPass =
SceneManager->getSceneNodeRenderPass() == scene::ESNRP_TRANSPARENT; SceneManager->getSceneNodeRenderPass() == scene::ESNRP_TRANSPARENT;
++PassCount; ++PassCount;
@ -165,8 +142,7 @@ void CMeshSceneNode::render()
{ {
const video::SMaterial& material = ReadOnlyMaterials ? mb->getMaterial() : Materials[i]; const video::SMaterial& material = ReadOnlyMaterials ? mb->getMaterial() : Materials[i];
video::IMaterialRenderer* rnd = driver->getMaterialRenderer(material.MaterialType); const bool transparent = driver->needsTransparentRenderPass(material);
bool transparent = (rnd && rnd->isTransparent());
// only render transparent buffer if this is the transparent render pass // only render transparent buffer if this is the transparent render pass
// and solid only in solid pass // and solid only in solid pass

@ -2325,7 +2325,7 @@ void CNullDriver::deleteMaterialRenders()
//! Returns pointer to material renderer or null //! Returns pointer to material renderer or null
IMaterialRenderer* CNullDriver::getMaterialRenderer(u32 idx) IMaterialRenderer* CNullDriver::getMaterialRenderer(u32 idx) const
{ {
if ( idx < MaterialRenderers.size() ) if ( idx < MaterialRenderers.size() )
return MaterialRenderers[idx].Renderer; return MaterialRenderers[idx].Renderer;
@ -2749,6 +2749,22 @@ core::dimension2du CNullDriver::getMaxTextureSize() const
return core::dimension2du(0x10000,0x10000); // maybe large enough 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 //! Color conversion convenience function
/** Convert an image (as array of pixels) from source to destination /** Convert an image (as array of pixels) from source to destination

@ -533,7 +533,7 @@ namespace video
s32 userData=0) _IRR_OVERRIDE_; s32 userData=0) _IRR_OVERRIDE_;
//! Returns pointer to material renderer or null //! 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. //! Returns amount of currently available material renderers.
virtual u32 getMaterialRendererCount() const _IRR_OVERRIDE_; virtual u32 getMaterialRendererCount() const _IRR_OVERRIDE_;
@ -667,6 +667,9 @@ namespace video
//! Returns the maximum texture size supported. //! Returns the maximum texture size supported.
virtual core::dimension2du getMaxTextureSize() const _IRR_OVERRIDE_; 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 //! Color conversion convenience function
/** Convert an image (as array of pixels) from source to destination /** Convert an image (as array of pixels) from source to destination
array, thereby converting the color format. The pixel size is array, thereby converting the color format. The pixel size is
@ -743,14 +746,14 @@ namespace video
return (f32) getAverage ( p[(y * pitch) + x] ); return (f32) getAverage ( p[(y * pitch) + x] );
} }
inline bool getWriteZBuffer(const SMaterial&material) const inline bool getWriteZBuffer(const SMaterial& material) const
{ {
switch ( material.ZWriteEnable ) switch ( material.ZWriteEnable )
{ {
case video::EZW_OFF: case video::EZW_OFF:
return false; return false;
case video::EZW_AUTO: case video::EZW_AUTO:
return AllowZWriteOnTransparent || !material.isTransparent(); return AllowZWriteOnTransparent || ! needsTransparentRenderPass(material);
case video::EZW_ON: case video::EZW_ON:
return true; return true;
} }

@ -79,10 +79,7 @@ void COctreeSceneNode::OnRegisterSceneNode()
// count transparent and solid materials in this scene node // count transparent and solid materials in this scene node
for (u32 i=0; i<Materials.size(); ++i) for (u32 i=0; i<Materials.size(); ++i)
{ {
const video::IMaterialRenderer* const rnd = if (driver->needsTransparentRenderPass(Materials[i]))
driver->getMaterialRenderer(Materials[i].MaterialType);
if ((rnd && rnd->isTransparent()) || Materials[i].isTransparent())
++transparentCount; ++transparentCount;
else else
++solidCount; ++solidCount;
@ -145,7 +142,7 @@ void COctreeSceneNode::render()
if (!camera) if (!camera)
return; return;
bool isTransparentPass = const bool isTransparentPass =
SceneManager->getSceneNodeRenderPass() == scene::ESNRP_TRANSPARENT; SceneManager->getSceneNodeRenderPass() == scene::ESNRP_TRANSPARENT;
++PassCount; ++PassCount;
@ -188,8 +185,7 @@ void COctreeSceneNode::render()
if ( 0 == d[i].CurrentSize ) if ( 0 == d[i].CurrentSize )
continue; continue;
const video::IMaterialRenderer* const rnd = driver->getMaterialRenderer(Materials[i].MaterialType); const bool transparent = driver->needsTransparentRenderPass(Materials[i]);
const bool transparent = (rnd && rnd->isTransparent());
// only render transparent buffer if this is the transparent render pass // only render transparent buffer if this is the transparent render pass
// and solid only in solid pass // and solid only in solid pass

@ -3617,6 +3617,11 @@ bool COpenGLDriver::queryTextureFormat(ECOLOR_FORMAT format) const
return getColorFormatParameters(format, dummyInternalFormat, dummyPixelFormat, dummyPixelType, &dummyConverter); 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 //! Only used by the internal engine. Used to notify the driver that
//! the window was resized. //! the window was resized.
void COpenGLDriver::OnResize(const core::dimension2d<u32>& size) void COpenGLDriver::OnResize(const core::dimension2d<u32>& size)

@ -382,6 +382,9 @@ namespace video
//! Check if the driver supports creating textures with the given color format //! Check if the driver supports creating textures with the given color format
virtual bool queryTextureFormat(ECOLOR_FORMAT format) const _IRR_OVERRIDE_; 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 //! Convert E_PRIMITIVE_TYPE to OpenGL equivalent
GLenum primitiveTypeToGL(scene::E_PRIMITIVE_TYPE type) const; GLenum primitiveTypeToGL(scene::E_PRIMITIVE_TYPE type) const;

@ -1377,9 +1377,7 @@ u32 CSceneManager::registerNodeForRendering(ISceneNode* node, E_SCENE_NODE_RENDE
taken = 0; taken = 0;
for (u32 i=0; i<count; ++i) for (u32 i=0; i<count; ++i)
{ {
video::IMaterialRenderer* rnd = if (Driver->needsTransparentRenderPass(node->getMaterial(i)))
Driver->getMaterialRenderer(node->getMaterial(i).MaterialType);
if ((rnd && rnd->isTransparent()) || node->getMaterial(i).isTransparent())
{ {
// register as transparent node // register as transparent node
TransparentNodeEntry e(node, camWorldPos); TransparentNodeEntry e(node, camWorldPos);

@ -178,7 +178,7 @@ void CBurningVideoDriver::setCurrentShader()
bool zMaterialTest = Material.org.ZBuffer != ECFN_DISABLED && bool zMaterialTest = Material.org.ZBuffer != ECFN_DISABLED &&
Material.org.ZWriteEnable != video::EZW_OFF && Material.org.ZWriteEnable != video::EZW_OFF &&
getWriteZBuffer(Material.org); ( AllowZWriteOnTransparent || !Material.org.isTransparent() );
EBurningFFShader shader = zMaterialTest ? ETR_TEXTURE_GOURAUD : ETR_TEXTURE_GOURAUD_NOZ; 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; 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 video
} // end namespace irr } // end namespace irr

@ -164,6 +164,9 @@ namespace video
//! Check if the driver supports creating textures with the given color format //! Check if the driver supports creating textures with the given color format
virtual bool queryTextureFormat(ECOLOR_FORMAT format) const _IRR_OVERRIDE_; 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; } IDepthBuffer * getDepthBuffer () { return DepthBuffer; }
IStencilBuffer * getStencilBuffer () { return StencilBuffer; } IStencilBuffer * getStencilBuffer () { return StencilBuffer; }

@ -1,4 +1,4 @@
Tests finished. 72 tests of 72 passed. Tests finished. 72 tests of 72 passed.
Compiled as DEBUG 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