diff options
author | David Walter Seikel | 2016-03-28 22:28:34 +1000 |
---|---|---|
committer | David Walter Seikel | 2016-03-28 22:28:34 +1000 |
commit | 7028cbe09c688437910a25623098762bf0fa592d (patch) | |
tree | 10b5af58277d9880380c2251f109325542c4e6eb /src/others/irrlicht-1.8.1/source/Irrlicht/CTerrainSceneNode.cpp | |
parent | Move lemon to the src/others directory. (diff) | |
download | SledjHamr-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.cpp | 1502 |
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 | |||
26 | namespace irr | ||
27 | { | ||
28 | namespace 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 | |||