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 // The code for the TerrainSceneNode is based on the GeoMipMapSceneNode
6 // developed by Spintz. He made it available for Irrlicht and allowed it to be
7 // distributed under this licence. I only modified some parts. A lot of thanks
8 // go to him.
9 
10 #include "CTerrainSceneNode.h"
11 #include "CTerrainTriangleSelector.h"
12 #include "IVideoDriver.h"
13 #include "ISceneManager.h"
14 #include "ICameraSceneNode.h"
15 #include "SViewFrustum.h"
16 #include "irrMath.h"
17 #include "os.h"
18 #include "IGUIFont.h"
19 #include "IFileSystem.h"
20 #include "IReadFile.h"
21 #include "ITextSceneNode.h"
22 #include "IAnimatedMesh.h"
23 #include "SMesh.h"
24 #include "CDynamicMeshBuffer.h"
25 
26 namespace irr
27 {
28 namespace scene
29 {
30 
31 	//! constructor
CTerrainSceneNode(ISceneNode * parent,ISceneManager * mgr,io::IFileSystem * fs,s32 id,s32 maxLOD,E_TERRAIN_PATCH_SIZE patchSize,const core::vector3df & position,const core::vector3df & rotation,const core::vector3df & scale)32 	CTerrainSceneNode::CTerrainSceneNode(ISceneNode* parent, ISceneManager* mgr,
33 			io::IFileSystem* fs, s32 id, s32 maxLOD, E_TERRAIN_PATCH_SIZE patchSize,
34 			const core::vector3df& position,
35 			const core::vector3df& rotation,
36 			const core::vector3df& scale)
37 	: ITerrainSceneNode(parent, mgr, id, position, rotation, scale),
38 	TerrainData(patchSize, maxLOD, position, rotation, scale), RenderBuffer(0),
39 	VerticesToRender(0), IndicesToRender(0), DynamicSelectorUpdate(false),
40 	OverrideDistanceThreshold(false), UseDefaultRotationPivot(true), ForceRecalculation(true),
41 	CameraMovementDelta(10.0f), CameraRotationDelta(1.0f),CameraFOVDelta(0.1f),
42 	TCoordScale1(1.0f), TCoordScale2(1.0f), SmoothFactor(0), FileSystem(fs)
43 	{
44 		#ifdef _DEBUG
45 		setDebugName("CTerrainSceneNode");
46 		#endif
47 
48 		Mesh = new SMesh();
49 		RenderBuffer = new CDynamicMeshBuffer(video::EVT_2TCOORDS, video::EIT_16BIT);
50 		RenderBuffer->setHardwareMappingHint(scene::EHM_STATIC, scene::EBT_VERTEX);
51 		RenderBuffer->setHardwareMappingHint(scene::EHM_DYNAMIC, scene::EBT_INDEX);
52 
53 		if (FileSystem)
54 			FileSystem->grab();
55 
56 		setAutomaticCulling(scene::EAC_OFF);
57 	}
58 
59 
60 	//! destructor
~CTerrainSceneNode()61 	CTerrainSceneNode::~CTerrainSceneNode()
62 	{
63 		delete [] TerrainData.Patches;
64 
65 		if (FileSystem)
66 			FileSystem->drop();
67 
68 		if (Mesh)
69 			Mesh->drop();
70 
71 		if (RenderBuffer)
72 			RenderBuffer->drop();
73 	}
74 
75 
76 	//! Initializes the terrain data. Loads the vertices from the heightMapFile
loadHeightMap(io::IReadFile * file,video::SColor vertexColor,s32 smoothFactor)77 	bool CTerrainSceneNode::loadHeightMap(io::IReadFile* file, video::SColor vertexColor,
78 			s32 smoothFactor)
79 	{
80 		if (!file)
81 			return false;
82 
83 		Mesh->MeshBuffers.clear();
84 		const u32 startTime = os::Timer::getRealTime();
85 		video::IImage* heightMap = SceneManager->getVideoDriver()->createImageFromFile(file);
86 
87 		if (!heightMap)
88 		{
89 			os::Printer::log("Unable to load heightmap.");
90 			return false;
91 		}
92 
93 		HeightmapFile = file->getFileName();
94 		SmoothFactor = smoothFactor;
95 
96 		// Get the dimension of the heightmap data
97 		TerrainData.Size = heightMap->getDimension().Width;
98 
99 		switch (TerrainData.PatchSize)
100 		{
101 			case ETPS_9:
102 				if (TerrainData.MaxLOD > 3)
103 				{
104 					TerrainData.MaxLOD = 3;
105 				}
106 			break;
107 			case ETPS_17:
108 				if (TerrainData.MaxLOD > 4)
109 				{
110 					TerrainData.MaxLOD = 4;
111 				}
112 			break;
113 			case ETPS_33:
114 				if (TerrainData.MaxLOD > 5)
115 				{
116 					TerrainData.MaxLOD = 5;
117 				}
118 			break;
119 			case ETPS_65:
120 				if (TerrainData.MaxLOD > 6)
121 				{
122 					TerrainData.MaxLOD = 6;
123 				}
124 			break;
125 			case ETPS_129:
126 				if (TerrainData.MaxLOD > 7)
127 				{
128 					TerrainData.MaxLOD = 7;
129 				}
130 			break;
131 		}
132 
133 		// --- Generate vertex data from heightmap ----
134 		// resize the vertex array for the mesh buffer one time (makes loading faster)
135 		scene::CDynamicMeshBuffer *mb=0;
136 
137 		const u32 numVertices = TerrainData.Size * TerrainData.Size;
138 		if (numVertices <= 65536)
139 		{
140 			//small enough for 16bit buffers
141 			mb=new scene::CDynamicMeshBuffer(video::EVT_2TCOORDS, video::EIT_16BIT);
142 			RenderBuffer->getIndexBuffer().setType(video::EIT_16BIT);
143 		}
144 		else
145 		{
146 			//we need 32bit buffers
147 			mb=new scene::CDynamicMeshBuffer(video::EVT_2TCOORDS, video::EIT_32BIT);
148 			RenderBuffer->getIndexBuffer().setType(video::EIT_32BIT);
149 		}
150 
151 		mb->getVertexBuffer().set_used(numVertices);
152 
153 		// Read the heightmap to get the vertex data
154 		// Apply positions changes, scaling changes
155 		const f32 tdSize = 1.0f/(f32)(TerrainData.Size-1);
156 		s32 index = 0;
157 		float fx=0.f;
158 		float fx2=0.f;
159 		for (s32 x = 0; x < TerrainData.Size; ++x)
160 		{
161 			float fz=0.f;
162 			float fz2=0.f;
163 			for (s32 z = 0; z < TerrainData.Size; ++z)
164 			{
165 				video::S3DVertex2TCoords& vertex= static_cast<video::S3DVertex2TCoords*>(mb->getVertexBuffer().pointer())[index++];
166 				vertex.Normal.set(0.0f, 1.0f, 0.0f);
167 				vertex.Color = vertexColor;
168 				vertex.Pos.X = fx;
169 				vertex.Pos.Y = (f32) heightMap->getPixel(TerrainData.Size-x-1,z).getLightness();
170 				vertex.Pos.Z = fz;
171 
172 				vertex.TCoords.X = vertex.TCoords2.X = 1.f-fx2;
173 				vertex.TCoords.Y = vertex.TCoords2.Y = fz2;
174 
175 				++fz;
176 				fz2 += tdSize;
177 			}
178 			++fx;
179 			fx2 += tdSize;
180 		}
181 
182 		// drop heightMap, no longer needed
183 		heightMap->drop();
184 
185 		smoothTerrain(mb, smoothFactor);
186 
187 		// calculate smooth normals for the vertices
188 		calculateNormals(mb);
189 
190 		// add the MeshBuffer to the mesh
191 		Mesh->addMeshBuffer(mb);
192 
193 		// We copy the data to the renderBuffer, after the normals have been calculated.
194 		RenderBuffer->getVertexBuffer().set_used(numVertices);
195 
196 		for (u32 i = 0; i < numVertices; ++i)
197 		{
198 			RenderBuffer->getVertexBuffer()[i] = mb->getVertexBuffer()[i];
199 			RenderBuffer->getVertexBuffer()[i].Pos *= TerrainData.Scale;
200 			RenderBuffer->getVertexBuffer()[i].Pos += TerrainData.Position;
201 		}
202 
203 		// We no longer need the mb
204 		mb->drop();
205 
206 		// calculate all the necessary data for the patches and the terrain
207 		calculateDistanceThresholds();
208 		createPatches();
209 		calculatePatchData();
210 
211 		// set the default rotation pivot point to the terrain nodes center
212 		TerrainData.RotationPivot = TerrainData.Center;
213 
214 		// Rotate the vertices of the terrain by the rotation
215 		// specified. Must be done after calculating the terrain data,
216 		// so we know what the current center of the terrain is.
217 		setRotation(TerrainData.Rotation);
218 
219 		// Pre-allocate memory for indices
220 
221 		RenderBuffer->getIndexBuffer().set_used(
222 				TerrainData.PatchCount * TerrainData.PatchCount *
223 				TerrainData.CalcPatchSize * TerrainData.CalcPatchSize * 6);
224 
225 		RenderBuffer->setDirty();
226 
227 		const u32 endTime = os::Timer::getRealTime();
228 
229 		c8 tmp[255];
230 		snprintf(tmp, 255, "Generated terrain data (%dx%d) in %.4f seconds",
231 			TerrainData.Size, TerrainData.Size, (endTime - startTime) / 1000.0f );
232 		os::Printer::log(tmp);
233 
234 		return true;
235 	}
236 
237 
238 	//! Initializes the terrain data. Loads the vertices from the heightMapFile
loadHeightMapRAW(io::IReadFile * file,s32 bitsPerPixel,bool signedData,bool floatVals,s32 width,video::SColor vertexColor,s32 smoothFactor)239 	bool CTerrainSceneNode::loadHeightMapRAW(io::IReadFile* file,
240 			s32 bitsPerPixel, bool signedData, bool floatVals,
241 			s32 width, video::SColor vertexColor, s32 smoothFactor)
242 	{
243 		if (!file)
244 			return false;
245 		if (floatVals && bitsPerPixel != 32)
246 			return false;
247 
248 		// start reading
249 		const u32 startTime = os::Timer::getTime();
250 
251 		Mesh->MeshBuffers.clear();
252 
253 		const s32 bytesPerPixel = bitsPerPixel / 8;
254 
255 		// Get the dimension of the heightmap data
256 		const s32 filesize = file->getSize();
257 		if (!width)
258 			TerrainData.Size = core::floor32(sqrtf((f32)(filesize / bytesPerPixel)));
259 		else
260 		{
261 			if ((filesize-file->getPos())/bytesPerPixel>width*width)
262 			{
263 				os::Printer::log("Error reading heightmap RAW file", "File is too small.");
264 				return false;
265 			}
266 			TerrainData.Size = width;
267 		}
268 
269 		switch (TerrainData.PatchSize)
270 		{
271 			case ETPS_9:
272 				if (TerrainData.MaxLOD > 3)
273 				{
274 					TerrainData.MaxLOD = 3;
275 				}
276 			break;
277 			case ETPS_17:
278 				if (TerrainData.MaxLOD > 4)
279 				{
280 					TerrainData.MaxLOD = 4;
281 				}
282 			break;
283 			case ETPS_33:
284 				if (TerrainData.MaxLOD > 5)
285 				{
286 					TerrainData.MaxLOD = 5;
287 				}
288 			break;
289 			case ETPS_65:
290 				if (TerrainData.MaxLOD > 6)
291 				{
292 					TerrainData.MaxLOD = 6;
293 				}
294 			break;
295 			case ETPS_129:
296 				if (TerrainData.MaxLOD > 7)
297 				{
298 					TerrainData.MaxLOD = 7;
299 				}
300 			break;
301 		}
302 
303 		// --- Generate vertex data from heightmap ----
304 		// resize the vertex array for the mesh buffer one time (makes loading faster)
305 		scene::CDynamicMeshBuffer *mb=0;
306 		const u32 numVertices = TerrainData.Size * TerrainData.Size;
307 		if (numVertices <= 65536)
308 		{
309 			//small enough for 16bit buffers
310 			mb=new scene::CDynamicMeshBuffer(video::EVT_2TCOORDS, video::EIT_16BIT);
311 			RenderBuffer->getIndexBuffer().setType(video::EIT_16BIT);
312 		}
313 		else
314 		{
315 			//we need 32bit buffers
316 			mb=new scene::CDynamicMeshBuffer(video::EVT_2TCOORDS, video::EIT_32BIT);
317 			RenderBuffer->getIndexBuffer().setType(video::EIT_32BIT);
318 		}
319 
320 		mb->getVertexBuffer().reallocate(numVertices);
321 
322 		video::S3DVertex2TCoords vertex;
323 		vertex.Normal.set(0.0f, 1.0f, 0.0f);
324 		vertex.Color = vertexColor;
325 
326 		// Read the heightmap to get the vertex data
327 		// Apply positions changes, scaling changes
328 		const f32 tdSize = 1.0f/(f32)(TerrainData.Size-1);
329 		float fx=0.f;
330 		float fx2=0.f;
331 		for (s32 x = 0; x < TerrainData.Size; ++x)
332 		{
333 			float fz=0.f;
334 			float fz2=0.f;
335 			for (s32 z = 0; z < TerrainData.Size; ++z)
336 			{
337 				bool failure=false;
338 				vertex.Pos.X = fx;
339 				if (floatVals)
340 				{
341 					if (file->read(&vertex.Pos.Y, bytesPerPixel) != bytesPerPixel)
342 						failure=true;
343 				}
344 				else if (signedData)
345 				{
346 					switch (bytesPerPixel)
347 					{
348 						case 1:
349 						{
350 							s8 val;
351 							if (file->read(&val, bytesPerPixel) != bytesPerPixel)
352 								failure=true;
353 							vertex.Pos.Y=val;
354 						}
355 						break;
356 						case 2:
357 						{
358 							s16 val;
359 							if (file->read(&val, bytesPerPixel) != bytesPerPixel)
360 								failure=true;
361 							vertex.Pos.Y=val/256.f;
362 						}
363 						break;
364 						case 4:
365 						{
366 							s32 val;
367 							if (file->read(&val, bytesPerPixel) != bytesPerPixel)
368 								failure=true;
369 							vertex.Pos.Y=val/16777216.f;
370 						}
371 						break;
372 					}
373 				}
374 				else
375 				{
376 					switch (bytesPerPixel)
377 					{
378 						case 1:
379 						{
380 							u8 val;
381 							if (file->read(&val, bytesPerPixel) != bytesPerPixel)
382 								failure=true;
383 							vertex.Pos.Y=val;
384 						}
385 						break;
386 						case 2:
387 						{
388 							u16 val;
389 							if (file->read(&val, bytesPerPixel) != bytesPerPixel)
390 								failure=true;
391 							vertex.Pos.Y=val/256.f;
392 						}
393 						break;
394 						case 4:
395 						{
396 							u32 val;
397 							if (file->read(&val, bytesPerPixel) != bytesPerPixel)
398 								failure=true;
399 							vertex.Pos.Y=val/16777216.f;
400 						}
401 						break;
402 					}
403 				}
404 				if (failure)
405 				{
406 					os::Printer::log("Error reading heightmap RAW file.");
407 					mb->drop();
408 					return false;
409 				}
410 				vertex.Pos.Z = fz;
411 
412 				vertex.TCoords.X = vertex.TCoords2.X = 1.f-fx2;
413 				vertex.TCoords.Y = vertex.TCoords2.Y = fz2;
414 
415 				mb->getVertexBuffer().push_back(vertex);
416 				++fz;
417 				fz2 += tdSize;
418 			}
419 			++fx;
420 			fx2 += tdSize;
421 		}
422 
423 		smoothTerrain(mb, smoothFactor);
424 
425 		// calculate smooth normals for the vertices
426 		calculateNormals(mb);
427 
428 		// add the MeshBuffer to the mesh
429 		Mesh->addMeshBuffer(mb);
430 		const u32 vertexCount = mb->getVertexCount();
431 
432 		// We copy the data to the renderBuffer, after the normals have been calculated.
433 		RenderBuffer->getVertexBuffer().set_used(vertexCount);
434 
435 		for (u32 i = 0; i < vertexCount; i++)
436 		{
437 			RenderBuffer->getVertexBuffer()[i] = mb->getVertexBuffer()[i];
438 			RenderBuffer->getVertexBuffer()[i].Pos *= TerrainData.Scale;
439 			RenderBuffer->getVertexBuffer()[i].Pos += TerrainData.Position;
440 		}
441 
442 		// We no longer need the mb
443 		mb->drop();
444 
445 		// calculate all the necessary data for the patches and the terrain
446 		calculateDistanceThresholds();
447 		createPatches();
448 		calculatePatchData();
449 
450 		// set the default rotation pivot point to the terrain nodes center
451 		TerrainData.RotationPivot = TerrainData.Center;
452 
453 		// Rotate the vertices of the terrain by the rotation specified. Must be done
454 		// after calculating the terrain data, so we know what the current center of the
455 		// terrain is.
456 		setRotation(TerrainData.Rotation);
457 
458 		// Pre-allocate memory for indices
459 		RenderBuffer->getIndexBuffer().set_used(
460 				TerrainData.PatchCount*TerrainData.PatchCount*
461 				TerrainData.CalcPatchSize*TerrainData.CalcPatchSize*6);
462 
463 		const u32 endTime = os::Timer::getTime();
464 
465 		c8 tmp[255];
466 		snprintf(tmp, 255, "Generated terrain data (%dx%d) in %.4f seconds",
467 			TerrainData.Size, TerrainData.Size, (endTime - startTime) / 1000.0f);
468 		os::Printer::log(tmp);
469 
470 		return true;
471 	}
472 
473 
474 	//! Returns the mesh
getMesh()475 	IMesh* CTerrainSceneNode::getMesh() { return Mesh; }
476 
477 
478 	//! Returns the material based on the zero based index i.
getMaterial(u32 i)479 	video::SMaterial& CTerrainSceneNode::getMaterial(u32 i)
480 	{
481 		return Mesh->getMeshBuffer(i)->getMaterial();
482 	}
483 
484 
485 	//! Returns amount of materials used by this scene node ( always 1 )
getMaterialCount() const486 	u32 CTerrainSceneNode::getMaterialCount() const
487 	{
488 		return Mesh->getMeshBufferCount();
489 	}
490 
491 
492 	//! Sets the scale of the scene node.
493 	//! \param scale: New scale of the node
setScale(const core::vector3df & scale)494 	void CTerrainSceneNode::setScale(const core::vector3df& scale)
495 	{
496 		TerrainData.Scale = scale;
497 		applyTransformation();
498 		calculateNormals(RenderBuffer);
499 		ForceRecalculation = true;
500 	}
501 
502 
503 	//! Sets the rotation of the node. This only modifies
504 	//! the relative rotation of the node.
505 	//! \param rotation: New rotation of the node in degrees.
setRotation(const core::vector3df & rotation)506 	void CTerrainSceneNode::setRotation(const core::vector3df& rotation)
507 	{
508 		TerrainData.Rotation = rotation;
509 		applyTransformation();
510 		ForceRecalculation = true;
511 	}
512 
513 
514 	//! Sets the pivot point for rotation of this node. This is useful for the TiledTerrainManager to
515 	//! rotate all terrain tiles around a global world point.
516 	//! NOTE: The default for the RotationPivot will be the center of the individual tile.
setRotationPivot(const core::vector3df & pivot)517 	void CTerrainSceneNode::setRotationPivot(const core::vector3df& pivot)
518 	{
519 		UseDefaultRotationPivot = false;
520 		TerrainData.RotationPivot = pivot;
521 	}
522 
523 
524 	//! Sets the position of the node.
525 	//! \param newpos: New postition of the scene node.
setPosition(const core::vector3df & newpos)526 	void CTerrainSceneNode::setPosition(const core::vector3df& newpos)
527 	{
528 		TerrainData.Position = newpos;
529 		applyTransformation();
530 		ForceRecalculation = true;
531 	}
532 
533 
534 	//! Apply transformation changes(scale, position, rotation)
applyTransformation()535 	void CTerrainSceneNode::applyTransformation()
536 	{
537 		if (!Mesh->getMeshBufferCount())
538 			return;
539 
540 		core::matrix4 rotMatrix;
541 		rotMatrix.setRotationDegrees(TerrainData.Rotation);
542 
543 		const s32 vtxCount = Mesh->getMeshBuffer(0)->getVertexCount();
544 		for (s32 i = 0; i < vtxCount; ++i)
545 		{
546 			RenderBuffer->getVertexBuffer()[i].Pos = Mesh->getMeshBuffer(0)->getPosition(i) * TerrainData.Scale + TerrainData.Position;
547 
548 			RenderBuffer->getVertexBuffer()[i].Pos -= TerrainData.RotationPivot;
549 			rotMatrix.inverseRotateVect(RenderBuffer->getVertexBuffer()[i].Pos);
550 			RenderBuffer->getVertexBuffer()[i].Pos += TerrainData.RotationPivot;
551 		}
552 
553 		calculateDistanceThresholds(true);
554 		calculatePatchData();
555 
556 		RenderBuffer->setDirty(EBT_VERTEX);
557 	}
558 
559 
560 	//! Updates the scene nodes indices if the camera has moved or rotated by a certain
561 	//! threshold, which can be changed using the SetCameraMovementDeltaThreshold and
562 	//! SetCameraRotationDeltaThreshold functions. This also determines if a given patch
563 	//! for the scene node is within the view frustum and if it's not the indices are not
564 	//! generated for that patch.
OnRegisterSceneNode()565 	void CTerrainSceneNode::OnRegisterSceneNode()
566 	{
567 		if (!IsVisible || !SceneManager->getActiveCamera())
568 			return;
569 
570 		SceneManager->registerNodeForRendering(this);
571 
572 		preRenderCalculationsIfNeeded();
573 
574 		// Do Not call ISceneNode::OnRegisterSceneNode(), this node should have no children (luke: is this comment still true, as ISceneNode::OnRegisterSceneNode() is called?)
575 
576 		ISceneNode::OnRegisterSceneNode();
577 		ForceRecalculation = false;
578 	}
579 
preRenderCalculationsIfNeeded()580 	void CTerrainSceneNode::preRenderCalculationsIfNeeded()
581 	{
582 		scene::ICameraSceneNode * camera = SceneManager->getActiveCamera();
583 		if (!camera)
584 			return;
585 
586 		// Determine the camera rotation, based on the camera direction.
587 		const core::vector3df cameraPosition = camera->getAbsolutePosition();
588 		const core::vector3df cameraRotation = core::line3d<f32>(cameraPosition, camera->getTarget()).getVector().getHorizontalAngle();
589 		core::vector3df cameraUp = camera->getUpVector();
590 		cameraUp.normalize();
591 		const f32 CameraFOV = SceneManager->getActiveCamera()->getFOV();
592 
593 		// Only check on the Camera's Y Rotation
594 		if (!ForceRecalculation)
595 		{
596 			if ((fabsf(cameraRotation.X - OldCameraRotation.X) < CameraRotationDelta) &&
597 				(fabsf(cameraRotation.Y - OldCameraRotation.Y) < CameraRotationDelta))
598 			{
599 				if ((fabs(cameraPosition.X - OldCameraPosition.X) < CameraMovementDelta) &&
600 					(fabs(cameraPosition.Y - OldCameraPosition.Y) < CameraMovementDelta) &&
601 					(fabs(cameraPosition.Z - OldCameraPosition.Z) < CameraMovementDelta))
602 				{
603 					if (fabs(CameraFOV-OldCameraFOV) < CameraFOVDelta &&
604 						cameraUp.dotProduct(OldCameraUp) > (1.f - (cos(core::DEGTORAD * CameraRotationDelta))))
605 					{
606 						return;
607 					}
608 				}
609 			}
610 		}
611 
612 		//we need to redo calculations...
613 
614 		OldCameraPosition = cameraPosition;
615 		OldCameraRotation = cameraRotation;
616 		OldCameraUp = cameraUp;
617 		OldCameraFOV = CameraFOV;
618 
619 		preRenderLODCalculations();
620 		preRenderIndicesCalculations();
621 	}
622 
preRenderLODCalculations()623 	void CTerrainSceneNode::preRenderLODCalculations()
624 	{
625 		scene::ICameraSceneNode * camera = SceneManager->getActiveCamera();
626 
627 		if (!camera)
628 			return;
629 
630 		const core::vector3df cameraPosition = camera->getAbsolutePosition();
631 
632 		const SViewFrustum* frustum = camera->getViewFrustum();
633 
634 		// Determine each patches LOD based on distance from camera (and whether or not they are in
635 		// the view frustum).
636 		const s32 count = TerrainData.PatchCount * TerrainData.PatchCount;
637 		for (s32 j = 0; j < count; ++j)
638 		{
639 			if (frustum->getBoundingBox().intersectsWithBox(TerrainData.Patches[j].BoundingBox))
640 			{
641 				const f32 distance = cameraPosition.getDistanceFromSQ(TerrainData.Patches[j].Center);
642 
643 				TerrainData.Patches[j].CurrentLOD = 0;
644 				for (s32 i = TerrainData.MaxLOD - 1; i>0; --i)
645 				{
646 					if (distance >= TerrainData.LODDistanceThreshold[i])
647 					{
648 						TerrainData.Patches[j].CurrentLOD = i;
649 						break;
650 					}
651 				}
652 			}
653 			else
654 			{
655 				TerrainData.Patches[j].CurrentLOD = -1;
656 			}
657 		}
658 	}
659 
660 
preRenderIndicesCalculations()661 	void CTerrainSceneNode::preRenderIndicesCalculations()
662 	{
663 		scene::IIndexBuffer& indexBuffer = RenderBuffer->getIndexBuffer();
664 		IndicesToRender = 0;
665 		indexBuffer.set_used(0);
666 
667 		s32 index = 0;
668 		// Then generate the indices for all patches that are visible.
669 		for (s32 i = 0; i < TerrainData.PatchCount; ++i)
670 		{
671 			for (s32 j = 0; j < TerrainData.PatchCount; ++j)
672 			{
673 				if (TerrainData.Patches[index].CurrentLOD >= 0)
674 				{
675 					s32 x = 0;
676 					s32 z = 0;
677 
678 					// calculate the step we take this patch, based on the patches current LOD
679 					const s32 step = 1 << TerrainData.Patches[index].CurrentLOD;
680 
681 					// Loop through patch and generate indices
682 					while (z < TerrainData.CalcPatchSize)
683 					{
684 						const s32 index11 = getIndex(j, i, index, x, z);
685 						const s32 index21 = getIndex(j, i, index, x + step, z);
686 						const s32 index12 = getIndex(j, i, index, x, z + step);
687 						const s32 index22 = getIndex(j, i, index, x + step, z + step);
688 
689 						indexBuffer.push_back(index12);
690 						indexBuffer.push_back(index11);
691 						indexBuffer.push_back(index22);
692 						indexBuffer.push_back(index22);
693 						indexBuffer.push_back(index11);
694 						indexBuffer.push_back(index21);
695 						IndicesToRender+=6;
696 
697 						// increment index position horizontally
698 						x += step;
699 
700 						// we've hit an edge
701 						if (x >= TerrainData.CalcPatchSize)
702 						{
703 							x = 0;
704 							z += step;
705 						}
706 					}
707 				}
708 				++index;
709 			}
710 		}
711 
712 		RenderBuffer->setDirty(EBT_INDEX);
713 
714 		if (DynamicSelectorUpdate && TriangleSelector)
715 		{
716 			CTerrainTriangleSelector* selector = (CTerrainTriangleSelector*)TriangleSelector;
717 			selector->setTriangleData(this, -1);
718 		}
719 	}
720 
721 
722 	//! Render the scene node
render()723 	void CTerrainSceneNode::render()
724 	{
725 		if (!IsVisible || !SceneManager->getActiveCamera())
726 			return;
727 
728 		if (!Mesh->getMeshBufferCount())
729 			return;
730 
731 		video::IVideoDriver* driver = SceneManager->getVideoDriver();
732 
733 		driver->setTransform (video::ETS_WORLD, core::IdentityMatrix);
734 		driver->setMaterial(Mesh->getMeshBuffer(0)->getMaterial());
735 
736 		RenderBuffer->getIndexBuffer().set_used(IndicesToRender);
737 
738 		// For use with geomorphing
739 		driver->drawMeshBuffer(RenderBuffer);
740 
741 		RenderBuffer->getIndexBuffer().set_used(RenderBuffer->getIndexBuffer().allocated_size());
742 
743 		// for debug purposes only:
744 		if (DebugDataVisible)
745 		{
746 			video::SMaterial m;
747 			m.Lighting = false;
748 			driver->setMaterial(m);
749 			if (DebugDataVisible & scene::EDS_BBOX)
750 				driver->draw3DBox(TerrainData.BoundingBox, video::SColor(255,255,255,255));
751 
752 			const s32 count = TerrainData.PatchCount * TerrainData.PatchCount;
753 			s32 visible = 0;
754 			if (DebugDataVisible & scene::EDS_BBOX_BUFFERS)
755 			{
756 				for (s32 j = 0; j < count; ++j)
757 				{
758 					driver->draw3DBox(TerrainData.Patches[j].BoundingBox, video::SColor(255,255,0,0));
759 					visible += (TerrainData.Patches[j].CurrentLOD >= 0);
760 				}
761 			}
762 
763 			if (DebugDataVisible & scene::EDS_NORMALS)
764 			{
765 				// draw normals
766 				const f32 debugNormalLength = SceneManager->getParameters()->getAttributeAsFloat(DEBUG_NORMAL_LENGTH);
767 				const video::SColor debugNormalColor = SceneManager->getParameters()->getAttributeAsColor(DEBUG_NORMAL_COLOR);
768 				driver->drawMeshBufferNormals(RenderBuffer, debugNormalLength, debugNormalColor);
769 			}
770 
771 			driver->setTransform(video::ETS_WORLD, AbsoluteTransformation);
772 
773 			static u32 lastTime = 0;
774 
775 			const u32 now = os::Timer::getRealTime();
776 			if (now - lastTime > 1000)
777 			{
778 				char buf[64];
779 				snprintf(buf, 64, "Count: %d, Visible: %d", count, visible);
780 				os::Printer::log(buf);
781 
782 				lastTime = now;
783 			}
784 		}
785 	}
786 
787 
788 	//! Return the bounding box of the entire terrain.
getBoundingBox() const789 	const core::aabbox3d<f32>& CTerrainSceneNode::getBoundingBox() const
790 	{
791 		return TerrainData.BoundingBox;
792 	}
793 
794 
795 	//! Return the bounding box of a patch
getBoundingBox(s32 patchX,s32 patchZ) const796 	const core::aabbox3d<f32>& CTerrainSceneNode::getBoundingBox(s32 patchX, s32 patchZ) const
797 	{
798 		return TerrainData.Patches[patchX * TerrainData.PatchCount + patchZ].BoundingBox;
799 	}
800 
801 
802 	//! Gets the meshbuffer data based on a specified Level of Detail.
803 	//! \param mb: A reference to an SMeshBuffer object
804 	//! \param LOD: The Level Of Detail you want the indices from.
getMeshBufferForLOD(IDynamicMeshBuffer & mb,s32 LOD) const805 	void CTerrainSceneNode::getMeshBufferForLOD(IDynamicMeshBuffer& mb, s32 LOD ) const
806 	{
807 		if (!Mesh->getMeshBufferCount())
808 			return;
809 
810 		LOD = core::clamp(LOD, 0, TerrainData.MaxLOD - 1);
811 
812 		const u32 numVertices = Mesh->getMeshBuffer(0)->getVertexCount();
813 		mb.getVertexBuffer().reallocate(numVertices);
814 		video::S3DVertex2TCoords* vertices = (video::S3DVertex2TCoords*)Mesh->getMeshBuffer(0)->getVertices();
815 
816 		for (u32 n=0; n<numVertices; ++n)
817 			mb.getVertexBuffer().push_back(vertices[n]);
818 
819 		mb.getIndexBuffer().setType(RenderBuffer->getIndexBuffer().getType());
820 
821 		// calculate the step we take for all patches, since LOD is the same
822 		const s32 step = 1 << LOD;
823 
824 		// Generate the indices for all patches at the specified LOD
825 		s32 index = 0;
826 		for (s32 i=0; i<TerrainData.PatchCount; ++i)
827 		{
828 			for (s32 j=0; j<TerrainData.PatchCount; ++j)
829 			{
830 				s32 x = 0;
831 				s32 z = 0;
832 
833 				// Loop through patch and generate indices
834 				while (z < TerrainData.CalcPatchSize)
835 				{
836 					const s32 index11 = getIndex(j, i, index, x, z);
837 					const s32 index21 = getIndex(j, i, index, x + step, z);
838 					const s32 index12 = getIndex(j, i, index, x, z + step);
839 					const s32 index22 = getIndex(j, i, index, x + step, z + step);
840 
841 					mb.getIndexBuffer().push_back(index12);
842 					mb.getIndexBuffer().push_back(index11);
843 					mb.getIndexBuffer().push_back(index22);
844 					mb.getIndexBuffer().push_back(index22);
845 					mb.getIndexBuffer().push_back(index11);
846 					mb.getIndexBuffer().push_back(index21);
847 
848 					// increment index position horizontally
849 					x += step;
850 
851 					if (x >= TerrainData.CalcPatchSize) // we've hit an edge
852 					{
853 						x = 0;
854 						z += step;
855 					}
856 				}
857 				++index;
858 			}
859 		}
860 	}
861 
862 
863 	//! Gets the indices for a specified patch at a specified Level of Detail.
864 	//! \param mb: A reference to an array of u32 indices.
865 	//! \param patchX: Patch x coordinate.
866 	//! \param patchZ: Patch z coordinate.
867 	//! \param LOD: The level of detail to get for that patch. If -1, then get
868 	//! the CurrentLOD. If the CurrentLOD is set to -1, meaning it's not shown,
869 	//! then it will retrieve the triangles at the highest LOD (0).
870 	//! \return: Number if indices put into the buffer.
getIndicesForPatch(core::array<u32> & indices,s32 patchX,s32 patchZ,s32 LOD)871 	s32 CTerrainSceneNode::getIndicesForPatch(core::array<u32>& indices, s32 patchX, s32 patchZ, s32 LOD)
872 	{
873 		if (patchX < 0 || patchX > TerrainData.PatchCount-1 ||
874 				patchZ < 0 || patchZ > TerrainData.PatchCount-1)
875 			return -1;
876 
877 		if (LOD < -1 || LOD > TerrainData.MaxLOD - 1)
878 			return -1;
879 
880 		core::array<s32> cLODs;
881 		bool setLODs = false;
882 
883 		// If LOD of -1 was passed in, use the CurrentLOD of the patch specified
884 		if (LOD == -1)
885 		{
886 			LOD = TerrainData.Patches[patchX * TerrainData.PatchCount + patchZ].CurrentLOD;
887 		}
888 		else
889 		{
890 			getCurrentLODOfPatches(cLODs);
891 			setCurrentLODOfPatches(LOD);
892 			setLODs = true;
893 		}
894 
895 		if (LOD < 0)
896 			return -2; // Patch not visible, don't generate indices.
897 
898 		// calculate the step we take for this LOD
899 		const s32 step = 1 << LOD;
900 
901 		// Generate the indices for the specified patch at the specified LOD
902 		const s32 index = patchX * TerrainData.PatchCount + patchZ;
903 
904 		s32 x = 0;
905 		s32 z = 0;
906 
907 		indices.set_used(TerrainData.PatchSize * TerrainData.PatchSize * 6);
908 
909 		// Loop through patch and generate indices
910 		s32 rv=0;
911 		while (z<TerrainData.CalcPatchSize)
912 		{
913 			const s32 index11 = getIndex(patchZ, patchX, index, x, z);
914 			const s32 index21 = getIndex(patchZ, patchX, index, x + step, z);
915 			const s32 index12 = getIndex(patchZ, patchX, index, x, z + step);
916 			const s32 index22 = getIndex(patchZ, patchX, index, x + step, z + step);
917 
918 			indices[rv++] = index12;
919 			indices[rv++] = index11;
920 			indices[rv++] = index22;
921 			indices[rv++] = index22;
922 			indices[rv++] = index11;
923 			indices[rv++] = index21;
924 
925 			// increment index position horizontally
926 			x += step;
927 
928 			if (x >= TerrainData.CalcPatchSize) // we've hit an edge
929 			{
930 				x = 0;
931 				z += step;
932 			}
933 		}
934 
935 		if (setLODs)
936 			setCurrentLODOfPatches(cLODs);
937 
938 		return rv;
939 	}
940 
941 
942 	//! Populates an array with the CurrentLOD of each patch.
943 	//! \param LODs: A reference to a core::array<s32> to hold the values
944 	//! \return Returns the number of elements in the array
getCurrentLODOfPatches(core::array<s32> & LODs) const945 	s32 CTerrainSceneNode::getCurrentLODOfPatches(core::array<s32>& LODs) const
946 	{
947 		s32 numLODs;
948 		LODs.clear();
949 
950 		const s32 count = TerrainData.PatchCount * TerrainData.PatchCount;
951 		for (numLODs = 0; numLODs < count; numLODs++)
952 			LODs.push_back(TerrainData.Patches[numLODs].CurrentLOD);
953 
954 		return LODs.size();
955 	}
956 
957 
958 	//! Manually sets the LOD of a patch
959 	//! \param patchX: Patch x coordinate.
960 	//! \param patchZ: Patch z coordinate.
961 	//! \param LOD: The level of detail to set the patch to.
setLODOfPatch(s32 patchX,s32 patchZ,s32 LOD)962 	void CTerrainSceneNode::setLODOfPatch(s32 patchX, s32 patchZ, s32 LOD)
963 	{
964 		TerrainData.Patches[patchX * TerrainData.PatchCount + patchZ].CurrentLOD = LOD;
965 	}
966 
967 
968 	//! Override the default generation of distance thresholds for determining the LOD a patch
969 	//! is rendered at.
overrideLODDistance(s32 LOD,f64 newDistance)970 	bool CTerrainSceneNode::overrideLODDistance(s32 LOD, f64 newDistance)
971 	{
972 		OverrideDistanceThreshold = true;
973 
974 		if (LOD < 0 || LOD > TerrainData.MaxLOD - 1)
975 			return false;
976 
977 		TerrainData.LODDistanceThreshold[LOD] = newDistance * newDistance;
978 
979 		return true;
980 	}
981 
982 
983 	//! Creates a planar texture mapping on the terrain
984 	//! \param resolution: resolution of the planar mapping. This is the value
985 	//! specifying the relation between world space and texture coordinate space.
scaleTexture(f32 resolution,f32 resolution2)986 	void CTerrainSceneNode::scaleTexture(f32 resolution, f32 resolution2)
987 	{
988 		TCoordScale1 = resolution;
989 		TCoordScale2 = resolution2;
990 
991 		const f32 resBySize = resolution / (f32)(TerrainData.Size-1);
992 		const f32 res2BySize = resolution2 / (f32)(TerrainData.Size-1);
993 		u32 index = 0;
994 		f32 xval = 0.f;
995 		f32 x2val = 0.f;
996 		for (s32 x=0; x<TerrainData.Size; ++x)
997 		{
998 			f32 zval=0.f;
999 			f32 z2val=0.f;
1000 			for (s32 z=0; z<TerrainData.Size; ++z)
1001 			{
1002 				RenderBuffer->getVertexBuffer()[index].TCoords.X = 1.f-xval;
1003 				RenderBuffer->getVertexBuffer()[index].TCoords.Y = zval;
1004 
1005 				if (RenderBuffer->getVertexType()==video::EVT_2TCOORDS)
1006 				{
1007 					if (resolution2 == 0)
1008 					{
1009 						((video::S3DVertex2TCoords&)RenderBuffer->getVertexBuffer()[index]).TCoords2 = RenderBuffer->getVertexBuffer()[index].TCoords;
1010 					}
1011 					else
1012 					{
1013 						((video::S3DVertex2TCoords&)RenderBuffer->getVertexBuffer()[index]).TCoords2.X = 1.f-x2val;
1014 						((video::S3DVertex2TCoords&)RenderBuffer->getVertexBuffer()[index]).TCoords2.Y = z2val;
1015 					}
1016 				}
1017 
1018 				++index;
1019 				zval += resBySize;
1020 				z2val += res2BySize;
1021 			}
1022 			xval += resBySize;
1023 			x2val += res2BySize;
1024 		}
1025 
1026 		RenderBuffer->setDirty(EBT_VERTEX);
1027 	}
1028 
1029 
1030 	//! used to get the indices when generating index data for patches at varying levels of detail.
getIndex(const s32 PatchX,const s32 PatchZ,const s32 PatchIndex,u32 vX,u32 vZ) const1031 	u32 CTerrainSceneNode::getIndex(const s32 PatchX, const s32 PatchZ,
1032 					const s32 PatchIndex, u32 vX, u32 vZ) const
1033 	{
1034 		// top border
1035 		if (vZ == 0)
1036 		{
1037 			if (TerrainData.Patches[PatchIndex].Top &&
1038 				TerrainData.Patches[PatchIndex].CurrentLOD < TerrainData.Patches[PatchIndex].Top->CurrentLOD &&
1039 				(vX % (1 << TerrainData.Patches[PatchIndex].Top->CurrentLOD)) != 0 )
1040 			{
1041 				vX -= vX % (1 << TerrainData.Patches[PatchIndex].Top->CurrentLOD);
1042 			}
1043 		}
1044 		else
1045 		if (vZ == (u32)TerrainData.CalcPatchSize) // bottom border
1046 		{
1047 			if (TerrainData.Patches[PatchIndex].Bottom &&
1048 				TerrainData.Patches[PatchIndex].CurrentLOD < TerrainData.Patches[PatchIndex].Bottom->CurrentLOD &&
1049 				(vX % (1 << TerrainData.Patches[PatchIndex].Bottom->CurrentLOD)) != 0)
1050 			{
1051 				vX -= vX % (1 << TerrainData.Patches[PatchIndex].Bottom->CurrentLOD);
1052 			}
1053 		}
1054 
1055 		// left border
1056 		if (vX == 0)
1057 		{
1058 			if (TerrainData.Patches[PatchIndex].Left &&
1059 				TerrainData.Patches[PatchIndex].CurrentLOD < TerrainData.Patches[PatchIndex].Left->CurrentLOD &&
1060 				(vZ % (1 << TerrainData.Patches[PatchIndex].Left->CurrentLOD)) != 0)
1061 			{
1062 				vZ -= vZ % (1 << TerrainData.Patches[PatchIndex].Left->CurrentLOD);
1063 			}
1064 		}
1065 		else
1066 		if (vX == (u32)TerrainData.CalcPatchSize) // right border
1067 		{
1068 			if (TerrainData.Patches[PatchIndex].Right &&
1069 				TerrainData.Patches[PatchIndex].CurrentLOD < TerrainData.Patches[PatchIndex].Right->CurrentLOD &&
1070 				(vZ % (1 << TerrainData.Patches[PatchIndex].Right->CurrentLOD)) != 0)
1071 			{
1072 				vZ -= vZ % (1 << TerrainData.Patches[PatchIndex].Right->CurrentLOD);
1073 			}
1074 		}
1075 
1076 		if (vZ >= (u32)TerrainData.PatchSize)
1077 			vZ = TerrainData.CalcPatchSize;
1078 
1079 		if (vX >= (u32)TerrainData.PatchSize)
1080 			vX = TerrainData.CalcPatchSize;
1081 
1082 		return (vZ + ((TerrainData.CalcPatchSize) * PatchZ)) * TerrainData.Size +
1083 			(vX + ((TerrainData.CalcPatchSize) * PatchX));
1084 	}
1085 
1086 
1087 	//! smooth the terrain
smoothTerrain(IDynamicMeshBuffer * mb,s32 smoothFactor)1088 	void CTerrainSceneNode::smoothTerrain(IDynamicMeshBuffer* mb, s32 smoothFactor)
1089 	{
1090 		for (s32 run = 0; run < smoothFactor; ++run)
1091 		{
1092 			s32 yd = TerrainData.Size;
1093 			for (s32 y = 1; y < TerrainData.Size - 1; ++y)
1094 			{
1095 				for (s32 x = 1; x < TerrainData.Size - 1; ++x)
1096 				{
1097 					mb->getVertexBuffer()[x + yd].Pos.Y =
1098 						(mb->getVertexBuffer()[x-1 + yd].Pos.Y + //left
1099 						mb->getVertexBuffer()[x+1 + yd].Pos.Y + //right
1100 						mb->getVertexBuffer()[x + yd - TerrainData.Size].Pos.Y + //above
1101 						mb->getVertexBuffer()[x + yd + TerrainData.Size].Pos.Y) * 0.25f; //below
1102 				}
1103 				yd += TerrainData.Size;
1104 			}
1105 		}
1106 	}
1107 
1108 
1109 	//! calculate smooth normals
calculateNormals(IDynamicMeshBuffer * mb)1110 	void CTerrainSceneNode::calculateNormals(IDynamicMeshBuffer* mb)
1111 	{
1112 		s32 count;
1113 		core::vector3df a, b, c, t;
1114 
1115 		for (s32 x=0; x<TerrainData.Size; ++x)
1116 		{
1117 			for (s32 z=0; z<TerrainData.Size; ++z)
1118 			{
1119 				count = 0;
1120 				core::vector3df normal;
1121 
1122 				// top left
1123 				if (x>0 && z>0)
1124 				{
1125 					a = mb->getVertexBuffer()[(x-1)*TerrainData.Size+z-1].Pos;
1126 					b = mb->getVertexBuffer()[(x-1)*TerrainData.Size+z].Pos;
1127 					c = mb->getVertexBuffer()[x*TerrainData.Size+z].Pos;
1128 					b -= a;
1129 					c -= a;
1130 					t = b.crossProduct(c);
1131 					t.normalize();
1132 					normal += t;
1133 
1134 					a = mb->getVertexBuffer()[(x-1)*TerrainData.Size+z-1].Pos;
1135 					b = mb->getVertexBuffer()[x*TerrainData.Size+z-1].Pos;
1136 					c = mb->getVertexBuffer()[x*TerrainData.Size+z].Pos;
1137 					b -= a;
1138 					c -= a;
1139 					t = b.crossProduct(c);
1140 					t.normalize();
1141 					normal += t;
1142 
1143 					count += 2;
1144 				}
1145 
1146 				// top right
1147 				if (x>0 && z<TerrainData.Size-1)
1148 				{
1149 					a = mb->getVertexBuffer()[(x-1)*TerrainData.Size+z].Pos;
1150 					b = mb->getVertexBuffer()[(x-1)*TerrainData.Size+z+1].Pos;
1151 					c = mb->getVertexBuffer()[x*TerrainData.Size+z+1].Pos;
1152 					b -= a;
1153 					c -= a;
1154 					t = b.crossProduct(c);
1155 					t.normalize();
1156 					normal += t;
1157 
1158 					a = mb->getVertexBuffer()[(x-1)*TerrainData.Size+z].Pos;
1159 					b = mb->getVertexBuffer()[x*TerrainData.Size+z+1].Pos;
1160 					c = mb->getVertexBuffer()[x*TerrainData.Size+z].Pos;
1161 					b -= a;
1162 					c -= a;
1163 					t = b.crossProduct(c);
1164 					t.normalize();
1165 					normal += t;
1166 
1167 					count += 2;
1168 				}
1169 
1170 				// bottom right
1171 				if (x<TerrainData.Size-1 && z<TerrainData.Size-1)
1172 				{
1173 					a = mb->getVertexBuffer()[x*TerrainData.Size+z+1].Pos;
1174 					b = mb->getVertexBuffer()[x*TerrainData.Size+z].Pos;
1175 					c = mb->getVertexBuffer()[(x+1)*TerrainData.Size+z+1].Pos;
1176 					b -= a;
1177 					c -= a;
1178 					t = b.crossProduct(c);
1179 					t.normalize();
1180 					normal += t;
1181 
1182 					a = mb->getVertexBuffer()[x*TerrainData.Size+z+1].Pos;
1183 					b = mb->getVertexBuffer()[(x+1)*TerrainData.Size+z+1].Pos;
1184 					c = mb->getVertexBuffer()[(x+1)*TerrainData.Size+z].Pos;
1185 					b -= a;
1186 					c -= a;
1187 					t = b.crossProduct(c);
1188 					t.normalize();
1189 					normal += t;
1190 
1191 					count += 2;
1192 				}
1193 
1194 				// bottom left
1195 				if (x<TerrainData.Size-1 && z>0)
1196 				{
1197 					a = mb->getVertexBuffer()[x*TerrainData.Size+z-1].Pos;
1198 					b = mb->getVertexBuffer()[x*TerrainData.Size+z].Pos;
1199 					c = mb->getVertexBuffer()[(x+1)*TerrainData.Size+z].Pos;
1200 					b -= a;
1201 					c -= a;
1202 					t = b.crossProduct(c);
1203 					t.normalize();
1204 					normal += t;
1205 
1206 					a = mb->getVertexBuffer()[x*TerrainData.Size+z-1].Pos;
1207 					b = mb->getVertexBuffer()[(x+1)*TerrainData.Size+z].Pos;
1208 					c = mb->getVertexBuffer()[(x+1)*TerrainData.Size+z-1].Pos;
1209 					b -= a;
1210 					c -= a;
1211 					t = b.crossProduct(c);
1212 					t.normalize();
1213 					normal += t;
1214 
1215 					count += 2;
1216 				}
1217 
1218 				if (count != 0)
1219 				{
1220 					normal.normalize();
1221 				}
1222 				else
1223 				{
1224 					normal.set(0.0f, 1.0f, 0.0f);
1225 				}
1226 
1227 				mb->getVertexBuffer()[x * TerrainData.Size + z].Normal = normal;
1228 			}
1229 		}
1230 	}
1231 
1232 
1233 	//! create patches, stuff that needs to be done only once for patches goes here.
createPatches()1234 	void CTerrainSceneNode::createPatches()
1235 	{
1236 		TerrainData.PatchCount = (TerrainData.Size - 1) / (TerrainData.CalcPatchSize);
1237 
1238 		if (TerrainData.Patches)
1239 			delete [] TerrainData.Patches;
1240 
1241 		TerrainData.Patches = new SPatch[TerrainData.PatchCount * TerrainData.PatchCount];
1242 	}
1243 
1244 
1245 	//! used to calculate the internal STerrainData structure both at creation and after scaling/position calls.
calculatePatchData()1246 	void CTerrainSceneNode::calculatePatchData()
1247 	{
1248 		// Reset the Terrains Bounding Box for re-calculation
1249 		TerrainData.BoundingBox.reset(RenderBuffer->getPosition(0));
1250 
1251 		for (s32 x = 0; x < TerrainData.PatchCount; ++x)
1252 		{
1253 			for (s32 z = 0; z < TerrainData.PatchCount; ++z)
1254 			{
1255 				const s32 index = x * TerrainData.PatchCount + z;
1256 				SPatch& patch = TerrainData.Patches[index];
1257 				patch.CurrentLOD = 0;
1258 
1259 				const s32 xstart = x*TerrainData.CalcPatchSize;
1260 				const s32 xend = xstart+TerrainData.CalcPatchSize;
1261 				const s32 zstart = z*TerrainData.CalcPatchSize;
1262 				const s32 zend = zstart+TerrainData.CalcPatchSize;
1263 				// For each patch, calculate the bounding box (mins and maxes)
1264 				patch.BoundingBox.reset(RenderBuffer->getPosition(xstart*TerrainData.Size + zstart));
1265 
1266 				for (s32 xx = xstart; xx <= xend; ++xx)
1267 					for (s32 zz = zstart; zz <= zend; ++zz)
1268 						patch.BoundingBox.addInternalPoint(RenderBuffer->getVertexBuffer()[xx * TerrainData.Size + zz].Pos);
1269 
1270 				// Reconfigure the bounding box of the terrain as a whole
1271 				TerrainData.BoundingBox.addInternalBox(patch.BoundingBox);
1272 
1273 				// get center of Patch
1274 				patch.Center = patch.BoundingBox.getCenter();
1275 
1276 				// Assign Neighbours
1277 				// Top
1278 				if (x > 0)
1279 					patch.Top = &TerrainData.Patches[(x-1) * TerrainData.PatchCount + z];
1280 				else
1281 					patch.Top = 0;
1282 
1283 				// Bottom
1284 				if (x < TerrainData.PatchCount - 1)
1285 					patch.Bottom = &TerrainData.Patches[(x+1) * TerrainData.PatchCount + z];
1286 				else
1287 					patch.Bottom = 0;
1288 
1289 				// Left
1290 				if (z > 0)
1291 					patch.Left = &TerrainData.Patches[x * TerrainData.PatchCount + z - 1];
1292 				else
1293 					patch.Left = 0;
1294 
1295 				// Right
1296 				if (z < TerrainData.PatchCount - 1)
1297 					patch.Right = &TerrainData.Patches[x * TerrainData.PatchCount + z + 1];
1298 				else
1299 					patch.Right = 0;
1300 			}
1301 		}
1302 
1303 		// get center of Terrain
1304 		TerrainData.Center = TerrainData.BoundingBox.getCenter();
1305 
1306 		// if the default rotation pivot is still being used, update it.
1307 		if (UseDefaultRotationPivot)
1308 		{
1309 			TerrainData.RotationPivot = TerrainData.Center;
1310 		}
1311 	}
1312 
1313 
1314 	//! used to calculate or recalculate the distance thresholds
calculateDistanceThresholds(bool scalechanged)1315 	void CTerrainSceneNode::calculateDistanceThresholds(bool scalechanged)
1316 	{
1317 		// Only update the LODDistanceThreshold if it's not manually changed
1318 		if (!OverrideDistanceThreshold)
1319 		{
1320 			TerrainData.LODDistanceThreshold.set_used(0);
1321 			// Determine new distance threshold for determining what LOD to draw patches at
1322 			TerrainData.LODDistanceThreshold.reallocate(TerrainData.MaxLOD);
1323 
1324 			const f64 size = TerrainData.PatchSize * TerrainData.PatchSize *
1325 					TerrainData.Scale.X * TerrainData.Scale.Z;
1326 			for (s32 i=0; i<TerrainData.MaxLOD; ++i)
1327 			{
1328 				TerrainData.LODDistanceThreshold.push_back(size * ((i+1+ i / 2) * (i+1+ i / 2)));
1329 			}
1330 		}
1331 	}
1332 
1333 
setCurrentLODOfPatches(s32 lod)1334 	void CTerrainSceneNode::setCurrentLODOfPatches(s32 lod)
1335 	{
1336 		const s32 count = TerrainData.PatchCount * TerrainData.PatchCount;
1337 		for (s32 i=0; i< count; ++i)
1338 			TerrainData.Patches[i].CurrentLOD = lod;
1339 	}
1340 
1341 
setCurrentLODOfPatches(const core::array<s32> & lodarray)1342 	void CTerrainSceneNode::setCurrentLODOfPatches(const core::array<s32>& lodarray)
1343 	{
1344 		const s32 count = TerrainData.PatchCount * TerrainData.PatchCount;
1345 		for (s32 i=0; i<count; ++i)
1346 			TerrainData.Patches[i].CurrentLOD = lodarray[i];
1347 	}
1348 
1349 
1350 	//! Gets the height
getHeight(f32 x,f32 z) const1351 	f32 CTerrainSceneNode::getHeight(f32 x, f32 z) const
1352 	{
1353 		if (!Mesh->getMeshBufferCount())
1354 			return 0;
1355 
1356 		core::matrix4 rotMatrix;
1357 		rotMatrix.setRotationDegrees(TerrainData.Rotation);
1358 		core::vector3df pos(x, 0.0f, z);
1359 		rotMatrix.rotateVect(pos);
1360 		pos -= TerrainData.Position;
1361 		pos /= TerrainData.Scale;
1362 
1363 		s32 X(core::floor32(pos.X));
1364 		s32 Z(core::floor32(pos.Z));
1365 
1366 		f32 height = -FLT_MAX;
1367 		if (X >= 0 && X < TerrainData.Size-1 &&
1368 				Z >= 0 && Z < TerrainData.Size-1)
1369 		{
1370 			const video::S3DVertex2TCoords* Vertices = (const video::S3DVertex2TCoords*)Mesh->getMeshBuffer(0)->getVertices();
1371 			const core::vector3df& a = Vertices[X * TerrainData.Size + Z].Pos;
1372 			const core::vector3df& b = Vertices[(X + 1) * TerrainData.Size + Z].Pos;
1373 			const core::vector3df& c = Vertices[X * TerrainData.Size + (Z + 1)].Pos;
1374 			const core::vector3df& d = Vertices[(X + 1) * TerrainData.Size + (Z + 1)].Pos;
1375 
1376 			// offset from integer position
1377 			const f32 dx = pos.X - X;
1378 			const f32 dz = pos.Z - Z;
1379 
1380 			if (dx > dz)
1381 				height = a.Y + (d.Y - b.Y)*dz + (b.Y - a.Y)*dx;
1382 			else
1383 				height = a.Y + (d.Y - c.Y)*dx + (c.Y - a.Y)*dz;
1384 
1385 			height *= TerrainData.Scale.Y;
1386 			height += TerrainData.Position.Y;
1387 		}
1388 
1389 		return height;
1390 	}
1391 
1392 
1393 	//! Writes attributes of the scene node.
serializeAttributes(io::IAttributes * out,io::SAttributeReadWriteOptions * options) const1394 	void CTerrainSceneNode::serializeAttributes(io::IAttributes* out,
1395 				io::SAttributeReadWriteOptions* options) const
1396 	{
1397 		ISceneNode::serializeAttributes(out, options);
1398 
1399 		out->addString("Heightmap", HeightmapFile.c_str());
1400 		out->addFloat("TextureScale1", TCoordScale1);
1401 		out->addFloat("TextureScale2", TCoordScale2);
1402 		out->addInt("SmoothFactor", SmoothFactor);
1403 	}
1404 
1405 
1406 	//! Reads attributes of the scene node.
deserializeAttributes(io::IAttributes * in,io::SAttributeReadWriteOptions * options)1407 	void CTerrainSceneNode::deserializeAttributes(io::IAttributes* in,
1408 			io::SAttributeReadWriteOptions* options)
1409 	{
1410 		io::path newHeightmap = in->getAttributeAsString("Heightmap");
1411 		f32 tcoordScale1 = in->getAttributeAsFloat("TextureScale1");
1412 		f32 tcoordScale2 = in->getAttributeAsFloat("TextureScale2");
1413 		s32 smoothFactor = in->getAttributeAsInt("SmoothFactor");
1414 
1415 		// set possible new heightmap
1416 
1417 		if (newHeightmap.size() != 0 && newHeightmap != HeightmapFile)
1418 		{
1419 			io::IReadFile* file = FileSystem->createAndOpenFile(newHeightmap.c_str());
1420 			if (file)
1421 			{
1422 				loadHeightMap(file, video::SColor(255,255,255,255), smoothFactor);
1423 				file->drop();
1424 			}
1425 			else
1426 				os::Printer::log("could not open heightmap", newHeightmap.c_str());
1427 		}
1428 
1429 		// set possible new scale
1430 
1431 		if (core::equals(tcoordScale1, 0.f))
1432 			tcoordScale1 = 1.0f;
1433 
1434 		if (core::equals(tcoordScale2, 0.f))
1435 			tcoordScale2 = 1.0f;
1436 
1437 		if (!core::equals(tcoordScale1, TCoordScale1) ||
1438 			!core::equals(tcoordScale2, TCoordScale2))
1439 		{
1440 			scaleTexture(tcoordScale1, tcoordScale2);
1441 		}
1442 
1443 		ISceneNode::deserializeAttributes(in, options);
1444 	}
1445 
1446 
1447 	//! Creates a clone of this scene node and its children.
clone(ISceneNode * newParent,ISceneManager * newManager)1448 	ISceneNode* CTerrainSceneNode::clone(ISceneNode* newParent, ISceneManager* newManager)
1449 	{
1450 		if (!newParent)
1451 			newParent = Parent;
1452 		if (!newManager)
1453 			newManager = SceneManager;
1454 
1455 		CTerrainSceneNode* nb = new CTerrainSceneNode(
1456 			newParent, newManager, FileSystem, ID,
1457 			4, ETPS_17, getPosition(), getRotation(), getScale());
1458 
1459 		nb->cloneMembers(this, newManager);
1460 
1461 		// instead of cloning the data structures, recreate the terrain.
1462 		// (temporary solution)
1463 
1464 		// load file
1465 
1466 		io::IReadFile* file = FileSystem->createAndOpenFile(HeightmapFile.c_str());
1467 		if (file)
1468 		{
1469 			nb->loadHeightMap(file, video::SColor(255,255,255,255), 0);
1470 			file->drop();
1471 		}
1472 
1473 		// scale textures
1474 
1475 		nb->scaleTexture(TCoordScale1, TCoordScale2);
1476 
1477 		// copy materials
1478 
1479 		for (unsigned int m = 0; m<Mesh->getMeshBufferCount(); ++m)
1480 		{
1481 			if (nb->Mesh->getMeshBufferCount()>m &&
1482 				nb->Mesh->getMeshBuffer(m) &&
1483 				Mesh->getMeshBuffer(m))
1484 			{
1485 				nb->Mesh->getMeshBuffer(m)->getMaterial() =
1486 					Mesh->getMeshBuffer(m)->getMaterial();
1487 			}
1488 		}
1489 
1490 		nb->RenderBuffer->getMaterial() = RenderBuffer->getMaterial();
1491 
1492 		// finish
1493 
1494 		if ( newParent )
1495 			nb->drop();
1496 		return nb;
1497 	}
1498 
1499 } // end namespace scene
1500 } // end namespace irr
1501 
1502 
1503