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