// Copyright (C) 2002-2009 Nikolaus Gebhardt // This file is part of the "Irrlicht Engine". // For conditions of distribution and use, see copyright notice in irrlicht.h #include "CShadowVolumeSceneNode.h" #include "ISceneManager.h" #include "IMesh.h" #include "IVideoDriver.h" #include "SLight.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), Vertices(0), Indices(0), Adjacency(0), Edges(0), FaceData(0), ShadowMesh(0), IndexCountAllocated(0), VertexCountAllocated(0), IndexCount(0), VertexCount(0), EdgeCount(0), ShadowVolumesUsed(0), Infinity(infinity), UseZFailMethod(zfailmethod) { #ifdef _DEBUG setDebugName("CShadowVolumeSceneNode"); #endif setShadowMesh(shadowMesh); setAutomaticCulling(scene::EAC_OFF); } //! destructor CShadowVolumeSceneNode::~CShadowVolumeSceneNode() { if (ShadowMesh) ShadowMesh->drop(); delete [] Edges; for (u32 i=0; i (u32)ShadowVolumesUsed) { // get the next unused buffer svp = &ShadowVolumes[ShadowVolumesUsed]; if (svp->size >= IndexCount*5) svp->count = 0; else { svp->size = IndexCount*5; svp->count = 0; delete [] svp->vertices; svp->vertices = new core::vector3df[svp->size]; } ++ShadowVolumesUsed; } else { // add a buffer SShadowVolume tmp; // lets make a rather large shadowbuffer tmp.size = IndexCount*5; tmp.count = 0; tmp.vertices = new core::vector3df[tmp.size]; ShadowVolumes.push_back(tmp); svp = &ShadowVolumes[ShadowVolumes.size()-1]; ++ShadowVolumesUsed; } const s32 faceCount = (s32)(IndexCount / 3); if (!Edges || faceCount * 6 > EdgeCount) { delete [] Edges; EdgeCount = faceCount * 6; Edges = new u16[EdgeCount]; } s32 numEdges = 0; const core::vector3df ls = light * Infinity; // light scaled //if (UseZFailMethod) // createZFailVolume(faceCount, numEdges, light, svp); //else // createZPassVolume(faceCount, numEdges, light, svp, false); // the createZFailVolume does currently not work 100% correctly, // so we create createZPassVolume with caps if the zfail method // is used createZPassVolume(faceCount, numEdges, light, svp, UseZFailMethod); for (s32 i=0; ivertices && svp->count < svp->size-5) { svp->vertices[svp->count++] = v1; svp->vertices[svp->count++] = v2; svp->vertices[svp->count++] = v3; svp->vertices[svp->count++] = v2; svp->vertices[svp->count++] = v4; svp->vertices[svp->count++] = v3; } } } void CShadowVolumeSceneNode::createZFailVolume(s32 faceCount, s32& numEdges, const core::vector3df& light, SShadowVolume* svp) { s32 i; const core::vector3df ls = light * Infinity; // Check every face if it is front or back facing the light. for (i=0; ivertices && svp->count < svp->size-5) { // add front cap svp->vertices[svp->count++] = v0; svp->vertices[svp->count++] = v2; svp->vertices[svp->count++] = v1; // add back cap svp->vertices[svp->count++] = v0 - ls; svp->vertices[svp->count++] = v1 - ls; svp->vertices[svp->count++] = v2 - ls; } } else FaceData[i] = true; // it's a front facing face } for(i=0; ivertices && svp->count < svp->size-5) { svp->vertices[svp->count++] = Vertices[wFace0]; svp->vertices[svp->count++] = Vertices[wFace2]; svp->vertices[svp->count++] = Vertices[wFace1]; svp->vertices[svp->count++] = Vertices[wFace0] - light; svp->vertices[svp->count++] = Vertices[wFace1] - light; svp->vertices[svp->count++] = Vertices[wFace2] - light; } } } } void CShadowVolumeSceneNode::setShadowMesh(const IMesh* mesh) { if (ShadowMesh) ShadowMesh->drop(); ShadowMesh = mesh; if (ShadowMesh) ShadowMesh->grab(); } void CShadowVolumeSceneNode::updateShadowVolumes() { const s32 oldIndexCount = IndexCount; const s32 oldVertexCount = VertexCount; VertexCount = 0; IndexCount = 0; ShadowVolumesUsed = 0; const IMesh* const mesh = ShadowMesh; if (!mesh) return; // calculate total amount of vertices and indices u32 i; s32 totalVertices = 0; s32 totalIndices = 0; const u32 bufcnt = mesh->getMeshBufferCount(); for (i=0; igetMeshBuffer(i); totalIndices += buf->getIndexCount(); totalVertices += buf->getVertexCount(); } // allocate memory if necessary if (totalVertices > VertexCountAllocated) { delete [] Vertices; Vertices = new core::vector3df[totalVertices]; VertexCountAllocated = totalVertices; } if (totalIndices > IndexCountAllocated) { delete [] Indices; Indices = new u16[totalIndices]; IndexCountAllocated = totalIndices; if (UseZFailMethod) { delete [] FaceData; FaceData = new bool[totalIndices / 3]; } } // copy mesh 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 && UseZFailMethod) calculateAdjacency(); // create as much shadow volumes as there are lights but // do not ignore the max light settings. const u32 lights = SceneManager->getVideoDriver()->getDynamicLightCount(); core::matrix4 mat = Parent->getAbsoluteTransformation(); const core::vector3df parentpos = Parent->getAbsolutePosition(); core::vector3df lpos; mat.makeInverse(); // TODO: Only correct for point lights. for (i=0; igetVideoDriver()->getDynamicLight(i); lpos = dl.Position; if (dl.CastShadows && fabs((lpos - parentpos).getLengthSQ()) <= (dl.Radius*dl.Radius*4.0f)) { mat.transformVect(lpos); createShadowVolume(lpos); } } } //! 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()); for (s32 i=0; idrawStencilShadowVolume(ShadowVolumes[i].vertices, ShadowVolumes[i].count, UseZFailMethod); /* if ( DebugDataVisible & scene::EDS_MESH_WIRE_OVERLAY ) { video::SMaterial mat; mat.Lighting = false; mat.Wireframe = true; mat.ZBuffer = true; driver->setMaterial(mat); for (s32 i=0; idrawVertexPrimitiveList(ShadowVolumes[i].vertices, ShadowVolumes[i].count,0,0,video::EVT_STANDARD,scene::EPT_LINES); } */ } //! 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(f32 epsilon) { delete [] Adjacency; Adjacency = new u16[IndexCount]; epsilon *= epsilon; f32 t = 0; // go through all faces and fetch their three neighbours for (s32 f=0; f