aboutsummaryrefslogtreecommitdiffstatshomepage
path: root/src/others/irrlicht-1.8.1/source/Irrlicht/CTerrainSceneNode.cpp
diff options
context:
space:
mode:
authorDavid Walter Seikel2016-03-28 22:28:34 +1000
committerDavid Walter Seikel2016-03-28 22:28:34 +1000
commit7028cbe09c688437910a25623098762bf0fa592d (patch)
tree10b5af58277d9880380c2251f109325542c4e6eb /src/others/irrlicht-1.8.1/source/Irrlicht/CTerrainSceneNode.cpp
parentMove lemon to the src/others directory. (diff)
downloadSledjHamr-7028cbe09c688437910a25623098762bf0fa592d.zip
SledjHamr-7028cbe09c688437910a25623098762bf0fa592d.tar.gz
SledjHamr-7028cbe09c688437910a25623098762bf0fa592d.tar.bz2
SledjHamr-7028cbe09c688437910a25623098762bf0fa592d.tar.xz
Move Irrlicht to src/others.
Diffstat (limited to 'src/others/irrlicht-1.8.1/source/Irrlicht/CTerrainSceneNode.cpp')
-rw-r--r--src/others/irrlicht-1.8.1/source/Irrlicht/CTerrainSceneNode.cpp1502
1 files changed, 1502 insertions, 0 deletions
diff --git a/src/others/irrlicht-1.8.1/source/Irrlicht/CTerrainSceneNode.cpp b/src/others/irrlicht-1.8.1/source/Irrlicht/CTerrainSceneNode.cpp
new file mode 100644
index 0000000..35e9211
--- /dev/null
+++ b/src/others/irrlicht-1.8.1/source/Irrlicht/CTerrainSceneNode.cpp
@@ -0,0 +1,1502 @@
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
26namespace irr
27{
28namespace scene
29{
30
31 //! constructor
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
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
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
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
475 IMesh* CTerrainSceneNode::getMesh() { return Mesh; }
476
477
478 //! Returns the material based on the zero based index 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 )
486 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
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.
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.
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.
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)
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.
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
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
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
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
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.
789 const core::aabbox3d<f32>& CTerrainSceneNode::getBoundingBox() const
790 {
791 return TerrainData.BoundingBox;
792 }
793
794
795 //! Return the bounding box of a patch
796 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.
805 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.
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
945 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.
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.
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.
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.
1031 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
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
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.
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.
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
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
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
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
1351 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.
1394 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.
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.
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