1 // Copyright (C) 2002-2012 Nikolaus Gebhardt
2 // This file is part of the "Irrlicht Engine".
3 // For conditions of distribution and use, see copyright notice in irrlicht.h
4 
5 #include "CShadowVolumeSceneNode.h"
6 #include "ISceneManager.h"
7 #include "IMesh.h"
8 #include "IVideoDriver.h"
9 #include "ICameraSceneNode.h"
10 #include "SViewFrustum.h"
11 #include "SLight.h"
12 #include "os.h"
13 
14 namespace irr
15 {
16 namespace scene
17 {
18 
19 
20 //! constructor
CShadowVolumeSceneNode(const IMesh * shadowMesh,ISceneNode * parent,ISceneManager * mgr,s32 id,bool zfailmethod,f32 infinity)21 CShadowVolumeSceneNode::CShadowVolumeSceneNode(const IMesh* shadowMesh, ISceneNode* parent,
22 		ISceneManager* mgr, s32 id, bool zfailmethod, f32 infinity)
23 : IShadowVolumeSceneNode(parent, mgr, id),
24 	ShadowMesh(0), IndexCount(0), VertexCount(0), ShadowVolumesUsed(0),
25 	Infinity(infinity), UseZFailMethod(zfailmethod)
26 {
27 	#ifdef _DEBUG
28 	setDebugName("CShadowVolumeSceneNode");
29 	#endif
30 	setShadowMesh(shadowMesh);
31 	setAutomaticCulling(scene::EAC_OFF);
32 }
33 
34 
35 //! destructor
~CShadowVolumeSceneNode()36 CShadowVolumeSceneNode::~CShadowVolumeSceneNode()
37 {
38 	if (ShadowMesh)
39 		ShadowMesh->drop();
40 }
41 
42 
createShadowVolume(const core::vector3df & light,bool isDirectional)43 void CShadowVolumeSceneNode::createShadowVolume(const core::vector3df& light, bool isDirectional)
44 {
45 	SShadowVolume* svp = 0;
46 	core::aabbox3d<f32>* bb = 0;
47 
48 	// builds the shadow volume and adds it to the shadow volume list.
49 
50 	if (ShadowVolumes.size() > ShadowVolumesUsed)
51 	{
52 		// get the next unused buffer
53 
54 		svp = &ShadowVolumes[ShadowVolumesUsed];
55 		svp->set_used(0);
56 
57 		bb = &ShadowBBox[ShadowVolumesUsed];
58 	}
59 	else
60 	{
61 		ShadowVolumes.push_back(SShadowVolume());
62 		svp = &ShadowVolumes.getLast();
63 
64 		ShadowBBox.push_back(core::aabbox3d<f32>());
65 		bb = &ShadowBBox.getLast();
66 	}
67 	svp->reallocate(IndexCount*5);
68 	++ShadowVolumesUsed;
69 
70 	// We use triangle lists
71 	Edges.set_used(IndexCount*2);
72 	u32 numEdges = 0;
73 
74 	numEdges=createEdgesAndCaps(light, svp, bb);
75 
76 	// for all edges add the near->far quads
77 	for (u32 i=0; i<numEdges; ++i)
78 	{
79 		const core::vector3df &v1 = Vertices[Edges[2*i+0]];
80 		const core::vector3df &v2 = Vertices[Edges[2*i+1]];
81 		const core::vector3df v3(v1+(v1 - light).normalize()*Infinity);
82 		const core::vector3df v4(v2+(v2 - light).normalize()*Infinity);
83 
84 		// Add a quad (two triangles) to the vertex list
85 #ifdef _DEBUG
86 		if (svp->size() >= svp->allocated_size()-5)
87 			os::Printer::log("Allocation too small.", ELL_DEBUG);
88 #endif
89 		svp->push_back(v1);
90 		svp->push_back(v2);
91 		svp->push_back(v3);
92 
93 		svp->push_back(v2);
94 		svp->push_back(v4);
95 		svp->push_back(v3);
96 	}
97 }
98 
99 
100 #define IRR_USE_ADJACENCY
101 #define IRR_USE_REVERSE_EXTRUDED
102 
createEdgesAndCaps(const core::vector3df & light,SShadowVolume * svp,core::aabbox3d<f32> * bb)103 u32 CShadowVolumeSceneNode::createEdgesAndCaps(const core::vector3df& light,
104 					SShadowVolume* svp, core::aabbox3d<f32>* bb)
105 {
106 	u32 numEdges=0;
107 	const u32 faceCount = IndexCount / 3;
108 
109 	if(faceCount >= 1)
110 		bb->reset(Vertices[Indices[0]]);
111 	else
112 		bb->reset(0,0,0);
113 
114 	// Check every face if it is front or back facing the light.
115 	for (u32 i=0; i<faceCount; ++i)
116 	{
117 		const core::vector3df v0 = Vertices[Indices[3*i+0]];
118 		const core::vector3df v1 = Vertices[Indices[3*i+1]];
119 		const core::vector3df v2 = Vertices[Indices[3*i+2]];
120 
121 #ifdef IRR_USE_REVERSE_EXTRUDED
122 		FaceData[i]=core::triangle3df(v0,v1,v2).isFrontFacing(light);
123 #else
124 		FaceData[i]=core::triangle3df(v2,v1,v0).isFrontFacing(light);
125 #endif
126 
127 		if (UseZFailMethod && FaceData[i])
128 		{
129 #ifdef _DEBUG
130 			if (svp->size() >= svp->allocated_size()-5)
131 				os::Printer::log("Allocation too small.", ELL_DEBUG);
132 #endif
133 			// add front cap from light-facing faces
134 			svp->push_back(v2);
135 			svp->push_back(v1);
136 			svp->push_back(v0);
137 
138 			// add back cap
139 			const core::vector3df i0 = v0+(v0-light).normalize()*Infinity;
140 			const core::vector3df i1 = v1+(v1-light).normalize()*Infinity;
141 			const core::vector3df i2 = v2+(v2-light).normalize()*Infinity;
142 
143 			svp->push_back(i0);
144 			svp->push_back(i1);
145 			svp->push_back(i2);
146 
147 			bb->addInternalPoint(i0);
148 			bb->addInternalPoint(i1);
149 			bb->addInternalPoint(i2);
150 		}
151 	}
152 
153 	// Create edges
154 	for (u32 i=0; i<faceCount; ++i)
155 	{
156 		// check all front facing faces
157 		if (FaceData[i] == true)
158 		{
159 			const u16 wFace0 = Indices[3*i+0];
160 			const u16 wFace1 = Indices[3*i+1];
161 			const u16 wFace2 = Indices[3*i+2];
162 
163 			const u16 adj0 = Adjacency[3*i+0];
164 			const u16 adj1 = Adjacency[3*i+1];
165 			const u16 adj2 = Adjacency[3*i+2];
166 
167 			// add edges if face is adjacent to back-facing face
168 			// or if no adjacent face was found
169 #ifdef IRR_USE_ADJACENCY
170 			if (adj0 == i || FaceData[adj0] == false)
171 #endif
172 			{
173 				// add edge v0-v1
174 				Edges[2*numEdges+0] = wFace0;
175 				Edges[2*numEdges+1] = wFace1;
176 				++numEdges;
177 			}
178 
179 #ifdef IRR_USE_ADJACENCY
180 			if (adj1 == i || FaceData[adj1] == false)
181 #endif
182 			{
183 				// add edge v1-v2
184 				Edges[2*numEdges+0] = wFace1;
185 				Edges[2*numEdges+1] = wFace2;
186 				++numEdges;
187 			}
188 
189 #ifdef IRR_USE_ADJACENCY
190 			if (adj2 == i || FaceData[adj2] == false)
191 #endif
192 			{
193 				// add edge v2-v0
194 				Edges[2*numEdges+0] = wFace2;
195 				Edges[2*numEdges+1] = wFace0;
196 				++numEdges;
197 			}
198 		}
199 	}
200 	return numEdges;
201 }
202 
203 
setShadowMesh(const IMesh * mesh)204 void CShadowVolumeSceneNode::setShadowMesh(const IMesh* mesh)
205 {
206 	if (ShadowMesh == mesh)
207 		return;
208 	if (ShadowMesh)
209 		ShadowMesh->drop();
210 	ShadowMesh = mesh;
211 	if (ShadowMesh)
212 	{
213 		ShadowMesh->grab();
214 		Box = ShadowMesh->getBoundingBox();
215 	}
216 }
217 
218 
updateShadowVolumes()219 void CShadowVolumeSceneNode::updateShadowVolumes()
220 {
221 	const u32 oldIndexCount = IndexCount;
222 	const u32 oldVertexCount = VertexCount;
223 
224 	const IMesh* const mesh = ShadowMesh;
225 	if (!mesh)
226 		return;
227 
228 	// create as much shadow volumes as there are lights but
229 	// do not ignore the max light settings.
230 	const u32 lightCount = SceneManager->getVideoDriver()->getDynamicLightCount();
231 	if (!lightCount)
232 		return;
233 
234 	// calculate total amount of vertices and indices
235 
236 	VertexCount = 0;
237 	IndexCount = 0;
238 	ShadowVolumesUsed = 0;
239 
240 	u32 i;
241 	u32 totalVertices = 0;
242 	u32 totalIndices = 0;
243 	const u32 bufcnt = mesh->getMeshBufferCount();
244 
245 	for (i=0; i<bufcnt; ++i)
246 	{
247 		const IMeshBuffer* buf = mesh->getMeshBuffer(i);
248 		totalIndices += buf->getIndexCount();
249 		totalVertices += buf->getVertexCount();
250 	}
251 
252 	// allocate memory if necessary
253 
254 	Vertices.set_used(totalVertices);
255 	Indices.set_used(totalIndices);
256 	FaceData.set_used(totalIndices / 3);
257 
258 	// copy mesh
259 	for (i=0; i<bufcnt; ++i)
260 	{
261 		const IMeshBuffer* buf = mesh->getMeshBuffer(i);
262 
263 		const u16* idxp = buf->getIndices();
264 		const u16* idxpend = idxp + buf->getIndexCount();
265 		for (; idxp!=idxpend; ++idxp)
266 			Indices[IndexCount++] = *idxp + VertexCount;
267 
268 		const u32 vtxcnt = buf->getVertexCount();
269 		for (u32 j=0; j<vtxcnt; ++j)
270 			Vertices[VertexCount++] = buf->getPosition(j);
271 	}
272 
273 	// recalculate adjacency if necessary
274 	if (oldVertexCount != VertexCount || oldIndexCount != IndexCount)
275 		calculateAdjacency();
276 
277 	core::matrix4 mat = Parent->getAbsoluteTransformation();
278 	mat.makeInverse();
279 	const core::vector3df parentpos = Parent->getAbsolutePosition();
280 
281 	// TODO: Only correct for point lights.
282 	for (i=0; i<lightCount; ++i)
283 	{
284 		const video::SLight& dl = SceneManager->getVideoDriver()->getDynamicLight(i);
285 		core::vector3df lpos = dl.Position;
286 		if (dl.CastShadows &&
287 			fabs((lpos - parentpos).getLengthSQ()) <= (dl.Radius*dl.Radius*4.0f))
288 		{
289 			mat.transformVect(lpos);
290 			createShadowVolume(lpos);
291 		}
292 	}
293 }
294 
295 
296 //! pre render method
OnRegisterSceneNode()297 void CShadowVolumeSceneNode::OnRegisterSceneNode()
298 {
299 	if (IsVisible)
300 	{
301 		SceneManager->registerNodeForRendering(this, scene::ESNRP_SHADOW);
302 		ISceneNode::OnRegisterSceneNode();
303 	}
304 }
305 
306 //! renders the node.
render()307 void CShadowVolumeSceneNode::render()
308 {
309 	video::IVideoDriver* driver = SceneManager->getVideoDriver();
310 
311 	if (!ShadowVolumesUsed || !driver)
312 		return;
313 
314 	driver->setTransform(video::ETS_WORLD, Parent->getAbsoluteTransformation());
315 
316 	for (u32 i=0; i<ShadowVolumesUsed; ++i)
317 	{
318 		bool drawShadow = true;
319 
320 		if (UseZFailMethod && SceneManager->getActiveCamera())
321 		{
322 			// Disable shadows drawing, when back cap is behind of ZFar plane.
323 
324 			SViewFrustum frust = *SceneManager->getActiveCamera()->getViewFrustum();
325 
326 			core::matrix4 invTrans(Parent->getAbsoluteTransformation(), core::matrix4::EM4CONST_INVERSE);
327 			frust.transform(invTrans);
328 
329 			core::vector3df edges[8];
330 			ShadowBBox[i].getEdges(edges);
331 
332 			core::vector3df largestEdge = edges[0];
333 			f32 maxDistance = core::vector3df(SceneManager->getActiveCamera()->getPosition() - edges[0]).getLength();
334 			f32 curDistance = 0.f;
335 
336 			for(int j = 1; j < 8; ++j)
337 			{
338 				curDistance = core::vector3df(SceneManager->getActiveCamera()->getPosition() - edges[j]).getLength();
339 
340 				if(curDistance > maxDistance)
341 				{
342 					maxDistance = curDistance;
343 					largestEdge = edges[j];
344 				}
345 			}
346 
347 			if (!(frust.planes[scene::SViewFrustum::VF_FAR_PLANE].classifyPointRelation(largestEdge) != core::ISREL3D_FRONT))
348 				drawShadow = false;
349 		}
350 
351 		if(drawShadow)
352 			driver->drawStencilShadowVolume(ShadowVolumes[i], UseZFailMethod, DebugDataVisible);
353 		else
354 		{
355 			core::array<core::vector3df> triangles;
356 			driver->drawStencilShadowVolume(triangles, UseZFailMethod, DebugDataVisible);
357 		}
358 	}
359 }
360 
361 
362 //! returns the axis aligned bounding box of this node
getBoundingBox() const363 const core::aabbox3d<f32>& CShadowVolumeSceneNode::getBoundingBox() const
364 {
365 	return Box;
366 }
367 
368 
369 //! Generates adjacency information based on mesh indices.
calculateAdjacency()370 void CShadowVolumeSceneNode::calculateAdjacency()
371 {
372 	Adjacency.set_used(IndexCount);
373 
374 	// go through all faces and fetch their three neighbours
375 	for (u32 f=0; f<IndexCount; f+=3)
376 	{
377 		for (u32 edge = 0; edge<3; ++edge)
378 		{
379 			const core::vector3df& v1 = Vertices[Indices[f+edge]];
380 			const core::vector3df& v2 = Vertices[Indices[f+((edge+1)%3)]];
381 
382 			// now we search an_O_ther _F_ace with these two
383 			// vertices, which is not the current face.
384 			u32 of;
385 
386 			for (of=0; of<IndexCount; of+=3)
387 			{
388 				// only other faces
389 				if (of != f)
390 				{
391 					bool cnt1 = false;
392 					bool cnt2 = false;
393 
394 					for (s32 e=0; e<3; ++e)
395 					{
396 						if (v1.equals(Vertices[Indices[of+e]]))
397 							cnt1=true;
398 
399 						if (v2.equals(Vertices[Indices[of+e]]))
400 							cnt2=true;
401 					}
402 					// one match for each vertex, i.e. edge is the same
403 					if (cnt1 && cnt2)
404 						break;
405 				}
406 			}
407 
408 			// no adjacent edges -> store face number, else store adjacent face
409 			if (of >= IndexCount)
410 				Adjacency[f + edge] = f/3;
411 			else
412 				Adjacency[f + edge] = of/3;
413 		}
414 	}
415 }
416 
417 
418 } // end namespace scene
419 } // end namespace irr
420