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

View File

@ -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.

View File

@ -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

View File

@ -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;
}

View File

@ -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; i<Materials.size(); ++i)
const u32 numMaterials = ReadOnlyMaterials ? Mesh->getMeshBufferCount() : Materials.size();
for (u32 i=0; i<numMaterials; ++i)
{
video::IMaterialRenderer* rnd =
driver->getMaterialRenderer(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; i<m->getMeshBufferCount(); ++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

View File

@ -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;

View File

@ -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;

View File

@ -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; 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)
{
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; 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;
}
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

View File

@ -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

View File

@ -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;
}

View File

@ -79,10 +79,7 @@ void COctreeSceneNode::OnRegisterSceneNode()
// count transparent and solid materials in this scene node
for (u32 i=0; i<Materials.size(); ++i)
{
const video::IMaterialRenderer* const rnd =
driver->getMaterialRenderer(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

View File

@ -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<u32>& size)

View File

@ -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;

View File

@ -1377,9 +1377,7 @@ u32 CSceneManager::registerNodeForRendering(ISceneNode* node, E_SCENE_NODE_RENDE
taken = 0;
for (u32 i=0; i<count; ++i)
{
video::IMaterialRenderer* rnd =
Driver->getMaterialRenderer(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);

View File

@ -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

View File

@ -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; }

View File

@ -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