diff options
author | Lizzy Fleckenstein <eliasfleckenstein@web.de> | 2023-04-13 08:40:18 +0200 |
---|---|---|
committer | Lizzy Fleckenstein <eliasfleckenstein@web.de> | 2023-04-13 18:01:09 +0200 |
commit | bf90df100e120e272c14c7975a22ed01bf3ad215 (patch) | |
tree | 073f4cf268a7f9407e5ad4a313ae76a619ff1dcc /source/Irrlicht/CShadowVolumeSceneNode.cpp | |
parent | 7a3fc62ada4001d5bb6c97ed26ec19a4e7c9d9ac (diff) | |
download | irrlicht-bf90df100e120e272c14c7975a22ed01bf3ad215.tar.xz |
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).
Diffstat (limited to 'source/Irrlicht/CShadowVolumeSceneNode.cpp')
-rw-r--r-- | source/Irrlicht/CShadowVolumeSceneNode.cpp | 531 |
1 files changed, 531 insertions, 0 deletions
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<f32>* 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<f32>());
+ 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; i<numEdges; ++i)
+ {
+ const core::vector3df &v1 = Vertices[Edges[2*i+0]];
+ const core::vector3df &v2 = Vertices[Edges[2*i+1]];
+ if ( !isDirectional )
+ {
+ lightDir1 = (v1 - light).normalize()*Infinity;
+ lightDir2 = (v2 - light).normalize()*Infinity;
+ }
+ const core::vector3df v3(v1+lightDir1);
+ const core::vector3df v4(v2+lightDir2);
+
+ // Add a quad (two triangles) to the vertex list
+#ifdef _DEBUG
+ if (svp->size() >= 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<f32>* 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; i<faceCount; ++i)
+ {
+ const core::vector3df v0 = Vertices[Indices[3*i+0]];
+ const core::vector3df v1 = Vertices[Indices[3*i+1]];
+ const core::vector3df v2 = Vertices[Indices[3*i+2]];
+
+ if ( !isDirectional )
+ {
+ lightDir0 = (v0-light).normalize();
+ }
+#ifdef IRR_USE_REVERSE_EXTRUDED
+ FaceData[i]=core::triangle3df(v2,v1,v0).isFrontFacing(lightDir0); // actually the back-facing polygons
+#else
+ FaceData[i]=core::triangle3df(v0,v1,v2).isFrontFacing(lightDir0);
+#endif
+
+#if 0 // Useful for internal debugging & testing. Show all the faces in the light.
+ if ( FaceData[i] )
+ {
+ video::SMaterial m;
+ m.Lighting = false;
+ SceneManager->getVideoDriver()->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; i<faceCount; ++i)
+ {
+ // check all front facing faces
+ if (FaceData[i] == true)
+ {
+ const u16 wFace0 = Indices[3*i+0];
+ const u16 wFace1 = Indices[3*i+1];
+ const u16 wFace2 = Indices[3*i+2];
+
+ if ( Optimization == ESV_NONE )
+ {
+ // add edge v0-v1
+ Edges[2*numEdges+0] = wFace0;
+ Edges[2*numEdges+1] = wFace1;
+ ++numEdges;
+
+ // add edge v1-v2
+ Edges[2*numEdges+0] = wFace1;
+ Edges[2*numEdges+1] = wFace2;
+ ++numEdges;
+
+ // add edge v2-v0
+ Edges[2*numEdges+0] = wFace2;
+ Edges[2*numEdges+1] = wFace0;
+ ++numEdges;
+ }
+ else
+ {
+ const u16 adj0 = Adjacency[3*i+0];
+ const u16 adj1 = Adjacency[3*i+1];
+ const u16 adj2 = Adjacency[3*i+2];
+
+ // add edges if face is adjacent to back-facing face
+ // or if no adjacent face was found
+ if (adj0 == i || FaceData[adj0] == false)
+ {
+ // add edge v0-v1
+ Edges[2*numEdges+0] = wFace0;
+ Edges[2*numEdges+1] = wFace1;
+ ++numEdges;
+ }
+
+ if (adj1 == i || FaceData[adj1] == false)
+ {
+ // add edge v1-v2
+ Edges[2*numEdges+0] = wFace1;
+ Edges[2*numEdges+1] = wFace2;
+ ++numEdges;
+ }
+
+ if (adj2 == i || FaceData[adj2] == false)
+ {
+ // add edge v2-v0
+ Edges[2*numEdges+0] = wFace2;
+ Edges[2*numEdges+1] = wFace0;
+ ++numEdges;
+ }
+ }
+ }
+ }
+ return numEdges;
+}
+
+
+void CShadowVolumeSceneNode::setShadowMesh(const IMesh* mesh)
+{
+ if (ShadowMesh == mesh)
+ return;
+ if (ShadowMesh)
+ ShadowMesh->drop();
+ 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; i<bufcnt; ++i)
+ {
+ const IMeshBuffer* buf = mesh->getMeshBuffer(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; i<bufcnt; ++i)
+ {
+ const IMeshBuffer* buf = mesh->getMeshBuffer(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; j<vtxcnt; ++j)
+ Vertices[VertexCount++] = buf->getPosition(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; i<lightCount; ++i)
+ {
+ const video::SLight& dl = SceneManager->getVideoDriver()->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; i<ShadowVolumesUsed; ++i)
+ {
+ bool drawShadow = true;
+
+ if (checkFarPlaneClipping)
+ {
+ // Disable shadows drawing, when back cap is behind of ZFar plane.
+ // TODO: Using infinite projection matrices instead is said to work better
+ // as then we wouldn't fail when the shadow clip the far plane.
+ // I couldn't get it working (and neither anyone before me it seems).
+ // Anyone who can figure it out is welcome to provide a patch.
+
+ core::vector3df edges[8];
+ ShadowBBox[i].getEdges(edges);
+
+ for(int j = 0; j < 8; ++j)
+ {
+ if (frust.planes[scene::SViewFrustum::VF_FAR_PLANE].classifyPointRelation(edges[j]) == core::ISREL3D_FRONT)
+ {
+ drawShadow = false;
+ break;
+ }
+ }
+ }
+
+ if(drawShadow)
+ driver->drawStencilShadowVolume(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<core::vector3df> triangles;
+ driver->drawStencilShadowVolume(triangles, UseZFailMethod, DebugDataVisible);
+ }
+ }
+}
+
+
+//! returns the axis aligned bounding box of this node
+const core::aabbox3d<f32>& 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<IndexCount; f+=3)
+ {
+ for (u32 edge = 0; edge<3; ++edge)
+ {
+ const core::vector3df& v1 = Vertices[Indices[f+edge]];
+ const core::vector3df& v2 = Vertices[Indices[f+((edge+1)%3)]];
+
+ // now we search an_O_ther _F_ace with these two
+ // vertices, which is not the current face.
+ u32 of;
+
+ for (of=0; of<IndexCount; of+=3)
+ {
+ // only other faces
+ if (of != f)
+ {
+ bool cnt1 = false;
+ bool cnt2 = false;
+
+ for (s32 e=0; e<3; ++e)
+ {
+ if (v1.equals(Vertices[Indices[of+e]]))
+ cnt1=true;
+
+ if (v2.equals(Vertices[Indices[of+e]]))
+ cnt2=true;
+ }
+ // one match for each vertex, i.e. edge is the same
+ if (cnt1 && cnt2)
+ break;
+ }
+ }
+
+ // no adjacent edges -> 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_
|