From bf90df100e120e272c14c7975a22ed01bf3ad215 Mon Sep 17 00:00:00 2001 From: Lizzy Fleckenstein Date: Thu, 13 Apr 2023 08:40:18 +0200 Subject: Add back lighting system Code is taken from latest irrlicht trunk; this is relevant because there have been fixes to stencil shadows since 1.8.5 (irrlicht SVN revision 5933). --- source/Irrlicht/CShadowVolumeSceneNode.cpp | 531 +++++++++++++++++++++++++++++ 1 file changed, 531 insertions(+) create mode 100644 source/Irrlicht/CShadowVolumeSceneNode.cpp (limited to 'source/Irrlicht/CShadowVolumeSceneNode.cpp') diff --git a/source/Irrlicht/CShadowVolumeSceneNode.cpp b/source/Irrlicht/CShadowVolumeSceneNode.cpp new file mode 100644 index 0000000..da38c22 --- /dev/null +++ b/source/Irrlicht/CShadowVolumeSceneNode.cpp @@ -0,0 +1,531 @@ +// 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 + +#include "IrrCompileConfig.h" + +#ifdef _IRR_COMPILE_WITH_SHADOW_VOLUME_SCENENODE_ + +#include "CShadowVolumeSceneNode.h" +#include "ISceneManager.h" +#include "IMesh.h" +#include "IVideoDriver.h" +#include "ICameraSceneNode.h" +#include "SViewFrustum.h" +#include "SLight.h" +#include "os.h" + +namespace irr +{ +namespace scene +{ + + +//! constructor +CShadowVolumeSceneNode::CShadowVolumeSceneNode(const IMesh* shadowMesh, ISceneNode* parent, + ISceneManager* mgr, s32 id, bool zfailmethod, f32 infinity) +: IShadowVolumeSceneNode(parent, mgr, id), + AdjacencyDirtyFlag(true), + ShadowMesh(0), IndexCount(0), VertexCount(0), ShadowVolumesUsed(0), + Infinity(infinity), UseZFailMethod(zfailmethod), Optimization(ESV_SILHOUETTE_BY_POS) +{ + #ifdef _DEBUG + setDebugName("CShadowVolumeSceneNode"); + #endif + setShadowMesh(shadowMesh); + setAutomaticCulling(scene::EAC_OFF); +} + + +//! destructor +CShadowVolumeSceneNode::~CShadowVolumeSceneNode() +{ + if (ShadowMesh) + ShadowMesh->drop(); +} + + +void CShadowVolumeSceneNode::createShadowVolume(const core::vector3df& light, bool isDirectional) +{ + SShadowVolume* svp = 0; + core::aabbox3d* bb = 0; + + // builds the shadow volume and adds it to the shadow volume list. + + if (ShadowVolumes.size() > ShadowVolumesUsed) + { + // get the next unused buffer + + svp = &ShadowVolumes[ShadowVolumesUsed]; + svp->set_used(0); + + bb = &ShadowBBox[ShadowVolumesUsed]; + } + else + { + ShadowVolumes.push_back(SShadowVolume()); + svp = &ShadowVolumes.getLast(); + + ShadowBBox.push_back(core::aabbox3d()); + bb = &ShadowBBox.getLast(); + } + svp->reallocate(IndexCount*5); + ++ShadowVolumesUsed; + + // We use triangle lists + Edges.set_used(IndexCount*2); + u32 numEdges = 0; + + numEdges=createEdgesAndCaps(light, isDirectional, svp, bb); + + // for all edges add the near->far quads + core::vector3df lightDir1(light*Infinity); + core::vector3df lightDir2(light*Infinity); + for (u32 i=0; isize() >= svp->allocated_size()-5) + os::Printer::log("Allocation too small.", ELL_DEBUG); +#endif + svp->push_back(v1); + svp->push_back(v2); + svp->push_back(v3); + + svp->push_back(v2); + svp->push_back(v4); + svp->push_back(v3); + } +} + +// TODO. +// Not sure what's going on. Either FaceData should mean the opposite and true should mean facing away from light +// or I'm missing something else. Anyway - when not setting this then Shadows will look wrong on Burnings driver +// while they seem to look OK on first view either way on other drivers. Only tested with z-fail so far. +// Maybe errors only show up close to near/far plane on other drivers as otherwise the stencil-buffer-count +// is probably ending up with same value anyway +#define IRR_USE_REVERSE_EXTRUDED + +u32 CShadowVolumeSceneNode::createEdgesAndCaps(const core::vector3df& light, bool isDirectional, + SShadowVolume* svp, core::aabbox3d* bb) +{ + u32 numEdges=0; + const u32 faceCount = IndexCount / 3; + + if(faceCount >= 1) + bb->reset(Vertices[Indices[0]]); + else + bb->reset(0,0,0); + + // Check every face if it is front or back facing the light. + core::vector3df lightDir0(light); + core::vector3df lightDir1(light); + core::vector3df lightDir2(light); + for (u32 i=0; igetVideoDriver()->setMaterial(m); +#ifdef IRR_USE_REVERSE_EXTRUDED + SceneManager->getVideoDriver()->draw3DTriangle(core::triangle3df(v0+lightDir0,v1+lightDir0,v2+lightDir0), irr::video::SColor(255,255, 0, 0)); +#else + SceneManager->getVideoDriver()->draw3DTriangle(core::triangle3df(v0-lightDir0,v1-lightDir0,v2-lightDir0), irr::video::SColor(255,255, 0, 0)); +#endif + } +#endif + + if (UseZFailMethod && FaceData[i]) + { +#ifdef _DEBUG + if (svp->size() >= svp->allocated_size()-5) + os::Printer::log("Allocation too small.", ELL_DEBUG); +#endif + // add front cap from light-facing faces + svp->push_back(v2); + svp->push_back(v1); + svp->push_back(v0); + + // add back cap + if ( !isDirectional ) + { + lightDir1 = (v1-light).normalize(); + lightDir2 = (v2-light).normalize(); + } + const core::vector3df i0 = v0+lightDir0*Infinity; + const core::vector3df i1 = v1+lightDir1*Infinity; + const core::vector3df i2 = v2+lightDir2*Infinity; + + svp->push_back(i0); + svp->push_back(i1); + svp->push_back(i2); + + bb->addInternalPoint(i0); + bb->addInternalPoint(i1); + bb->addInternalPoint(i2); + } + } + + // Create edges + for (u32 i=0; idrop(); + ShadowMesh = mesh; + if (ShadowMesh) + { + ShadowMesh->grab(); + Box = ShadowMesh->getBoundingBox(); + } +} + + +void CShadowVolumeSceneNode::updateShadowVolumes() +{ + const u32 oldIndexCount = IndexCount; + const u32 oldVertexCount = VertexCount; + + VertexCount = 0; + IndexCount = 0; + ShadowVolumesUsed = 0; + + const IMesh* const mesh = ShadowMesh; + if (!mesh) + return; + + // create as much shadow volumes as there are lights but + // do not ignore the max light settings. + const u32 lightCount = SceneManager->getVideoDriver()->getDynamicLightCount(); + if (!lightCount) + return; + + // calculate total amount of vertices and indices + + u32 i; + u32 totalVertices = 0; + u32 totalIndices = 0; + const u32 bufcnt = mesh->getMeshBufferCount(); + + for (i=0; igetMeshBuffer(i); + if ( buf->getIndexType() == video::EIT_16BIT + && buf->getPrimitiveType() == scene::EPT_TRIANGLES ) + { + totalIndices += buf->getIndexCount(); + totalVertices += buf->getVertexCount(); + } + else + { + os::Printer::log("ShadowVolumeSceneNode only supports meshbuffers with 16 bit indices and triangles", ELL_WARNING); + return; + } + } + if ( totalIndices != (u32)(u16)totalIndices) + { + // We could switch to 32-bit indices, not much work and just bit of extra memory (< 192k) per shadow volume. + // If anyone ever complains and really needs that just switch it. But huge shadows are usually a bad idea as they will be slow. + os::Printer::log("ShadowVolumeSceneNode does not yet support shadowvolumes which need more than 16 bit indices", ELL_WARNING); + return; + } + + // allocate memory if necessary + + Vertices.set_used(totalVertices); + Indices.set_used(totalIndices); + FaceData.resize(totalIndices / 3); + + // copy mesh + // (could speed this up for static meshes by adding some user flag to prevents copying) + for (i=0; igetMeshBuffer(i); + + const u16* idxp = buf->getIndices(); + const u16* idxpend = idxp + buf->getIndexCount(); + for (; idxp!=idxpend; ++idxp) + Indices[IndexCount++] = *idxp + VertexCount; + + const u32 vtxcnt = buf->getVertexCount(); + for (u32 j=0; jgetPosition(j); + } + + // recalculate adjacency if necessary + if (oldVertexCount != VertexCount || oldIndexCount != IndexCount || AdjacencyDirtyFlag) + calculateAdjacency(); + + core::matrix4 matInv(Parent->getAbsoluteTransformation()); + matInv.makeInverse(); + core::matrix4 matTransp(Parent->getAbsoluteTransformation(), core::matrix4::EM4CONST_TRANSPOSED); + const core::vector3df parentpos = Parent->getAbsolutePosition(); + + for (i=0; igetVideoDriver()->getDynamicLight(i); + + if ( dl.Type == video::ELT_DIRECTIONAL ) + { + core::vector3df ldir(dl.Direction); + matTransp.transformVect(ldir); + createShadowVolume(ldir, true); + } + else + { + core::vector3df lpos(dl.Position); + if (dl.CastShadows && + fabs((lpos - parentpos).getLengthSQ()) <= (dl.Radius*dl.Radius*4.0f)) + { + matInv.transformVect(lpos); + createShadowVolume(lpos, false); + } + } + } +} + +void CShadowVolumeSceneNode::setOptimization(ESHADOWVOLUME_OPTIMIZATION optimization) +{ + if ( Optimization != optimization ) + { + Optimization = optimization; + AdjacencyDirtyFlag = true; + } +} + +//! pre render method +void CShadowVolumeSceneNode::OnRegisterSceneNode() +{ + if (IsVisible) + { + SceneManager->registerNodeForRendering(this, scene::ESNRP_SHADOW); + ISceneNode::OnRegisterSceneNode(); + } +} + +//! renders the node. +void CShadowVolumeSceneNode::render() +{ + video::IVideoDriver* driver = SceneManager->getVideoDriver(); + + if (!ShadowVolumesUsed || !driver) + return; + + driver->setTransform(video::ETS_WORLD, Parent->getAbsoluteTransformation()); + + bool checkFarPlaneClipping = UseZFailMethod && !driver->queryFeature(video::EVDF_DEPTH_CLAMP); + + // get camera frustum converted to local coordinates when we have to check for far plane clipping + SViewFrustum frust; + if ( checkFarPlaneClipping ) + { + const irr::scene::ICameraSceneNode* camera = SceneManager->getActiveCamera(); + if ( camera ) + { + frust = *camera->getViewFrustum(); + core::matrix4 invTrans(Parent->getAbsoluteTransformation(), core::matrix4::EM4CONST_INVERSE); + frust.transform(invTrans); + } + else + checkFarPlaneClipping = false; + } + + for (u32 i=0; idrawStencilShadowVolume(ShadowVolumes[i], UseZFailMethod, DebugDataVisible); + else + { + // TODO: For some reason (not yet further investigated), Direct3D needs a call to drawStencilShadowVolume + // even if we have nothing to draw here to set the renderstate into a StencilShadowMode. + // If that's not done it has effect on further render calls. + core::array triangles; + driver->drawStencilShadowVolume(triangles, UseZFailMethod, DebugDataVisible); + } + } +} + + +//! returns the axis aligned bounding box of this node +const core::aabbox3d& CShadowVolumeSceneNode::getBoundingBox() const +{ + return Box; +} + + +//! Generates adjacency information based on mesh indices. +void CShadowVolumeSceneNode::calculateAdjacency() +{ + AdjacencyDirtyFlag = false; + + if ( Optimization == ESV_NONE ) + { + Adjacency.clear(); + } + else if ( Optimization == ESV_SILHOUETTE_BY_POS ) + { + Adjacency.set_used(IndexCount); + + // go through all faces and fetch their three neighbours + for (u32 f=0; f store face number, else store adjacent face + if (of >= IndexCount) + Adjacency[f + edge] = f/3; + else + Adjacency[f + edge] = of/3; + } + } + } +} + + +} // end namespace scene +} // end namespace irr + +#endif // _IRR_COMPILE_WITH_SHADOW_VOLUME_SCENENODE_ -- cgit v1.2.3