diff -Nauwr Irrlicht/CSceneManager.cpp Irrlicht/CSceneManager.cpp --- Irrlicht/CSceneManager.cpp 2005-08-24 18:01:14.000000000 +0200 +++ Irrlicht/CSceneManager.cpp 2005-09-27 15:50:08.000000000 +0200 @@ -36,6 +36,7 @@ #include "CDummyTransformationSceneNode.h" #include "CWaterSurfaceSceneNode.h" #include "CTerrainSceneNode.h" +#include "CTiledTerrainSceneNodeManager.h" #include "CEmptySceneNode.h" #include "CTextSceneNode.h" @@ -502,6 +502,75 @@ } +//! Adds a terrain scene node to the scene graph. +ITerrainSceneNode* CSceneManager::addTerrainSceneNodeRAW( + const char* heightMapFileName, s32 bitsPerPixel, + ISceneNode* parent, s32 id, + const core::vector3df& position, + const core::vector3df& rotation, + const core::vector3df& scale, + video::SColor vertexColor, + s32 maxLOD, E_TERRAIN_PATCH_SIZE patchSize) +{ + io::IReadFile* file = FileSystem->createAndOpenFile(heightMapFileName); + if (!file) + { + os::Printer::log("Could not load terrain, because file could not be opened.", + heightMapFileName, ELL_ERROR); + return 0; + } + + ITerrainSceneNode* terrain = addTerrainSceneNodeRAW(file, bitsPerPixel, parent, id, + position, rotation, scale, vertexColor, maxLOD, patchSize); + file->drop(); + + return terrain; +} + +//! Adds a terrain scene node to the scene graph. +ITerrainSceneNode* CSceneManager::addTerrainSceneNodeRAW( + io::IReadFile* heightMapFile, s32 bitsPerPixel, + ISceneNode* parent, s32 id, + const core::vector3df& position, + const core::vector3df& rotation, + const core::vector3df& scale, + video::SColor vertexColor, + s32 maxLOD, E_TERRAIN_PATCH_SIZE patchSize) +{ + if (!parent) + parent = this; + + CTerrainSceneNode* node = new CTerrainSceneNode(parent, this, id, + maxLOD, patchSize, position, rotation, scale); + + if (!node->loadHeightMapRAW(heightMapFile, bitsPerPixel, vertexColor)) + { + node->remove(); + node->drop(); + return 0; + } + + node->drop(); + return node; +} + + +//! Adds a tiled terrain scene node manager to the scene graph. +ITiledTerrainSceneNodeManager* CSceneManager::addTiledTerrainSceneNodeManager( + s32 tileWidth, s32 xWidth, s32 zWidth, s32 viewableTileDistance, ISceneNode* parent, s32 id, + const core::vector3df& position, const core::vector3df& rotation, + const core::vector3df& scale, video::SColor vertexColor ) +{ + if( !parent ) + parent = this; + + CTiledTerrainSceneNodeManager* manager = new CTiledTerrainSceneNodeManager( parent, this, id, + tileWidth, xWidth, zWidth, viewableTileDistance, position, rotation, scale ); + + return manager; +} + + //! Adds an empty scene node. ISceneNode* CSceneManager::addEmptySceneNode(ISceneNode* parent, s32 id) { diff -Nauwr Irrlicht/CSceneManager.h Irrlicht/CSceneManager.h --- Irrlicht/CSceneManager.h 2005-08-20 22:17:50.000000000 +0200 +++ Irrlicht/CSceneManager.h 2005-09-27 15:46:54.000000000 +0200 @@ -179,6 +177,35 @@ video::SColor vertexColor = video::SColor(255,255,255,255), s32 maxLOD=5, E_TERRAIN_PATCH_SIZE patchSize=ETPS_17); + //! Adds a terrain scene node to the scene graph. + virtual ITerrainSceneNode* addTerrainSceneNodeRAW( + const char* heightMapFileName, s32 bitsPerPixel = 16, + ISceneNode* parent=0, s32 id=-1, + const core::vector3df& position = core::vector3df(0.0f,0.0f,0.0f), + const core::vector3df& rotation = core::vector3df(0.0f,0.0f,0.0f), + const core::vector3df& scale = core::vector3df(1.0f,1.0f,1.0f), + video::SColor vertexColor = video::SColor(255,255,255,255), + s32 maxLOD=5, E_TERRAIN_PATCH_SIZE patchSize=ETPS_17); + + //! Adds a terrain scene node to the scene graph. + virtual ITerrainSceneNode* addTerrainSceneNodeRAW( + io::IReadFile* heightMap, s32 bitsPerPixel = 16, + ISceneNode* parent=0, s32 id=-1, + const core::vector3df& position = core::vector3df(0.0f,0.0f,0.0f), + const core::vector3df& rotation = core::vector3df(0.0f,0.0f,0.0f), + const core::vector3df& scale = core::vector3df(1.0f,1.0f,1.0f), + video::SColor vertexColor = video::SColor(255,255,255,255), + s32 maxLOD=5, E_TERRAIN_PATCH_SIZE patchSize=ETPS_17); + + //! Adds a tiled terrain scene node manager to the scene graph. + virtual ITiledTerrainSceneNodeManager* addTiledTerrainSceneNodeManager( + s32 tileWidth, s32 xWidth, s32 zWidth, s32 viewableTileDistance = 1, + ISceneNode* parent = 0, s32 id = -1, + const core::vector3df& position = core::vector3df(0.0f,0.0f,0.0f), + const core::vector3df& rotation = core::vector3df(0.0f,0.0f,0.0f), + const core::vector3df& scale = core::vector3df(1.0f,1.0f,1.0f), + video::SColor vertexColor = video::SColor(255,255,255,255) ); + //! Adds a dummy transformation scene node to the scene graph. virtual IDummyTransformationSceneNode* addDummyTransformationSceneNode( ISceneNode* parent=0, s32 id=-1); diff -Nauwr Irrlicht/CTerrainSceneNode.cpp Irrlicht/CTerrainSceneNode.cpp --- Irrlicht/CTerrainSceneNode.cpp 2005-08-20 22:17:52.000000000 +0200 +++ Irrlicht/CTerrainSceneNode.cpp 2005-09-22 11:34:16.000000000 +0200 @@ -25,8 +25,10 @@ CTerrainSceneNode::CTerrainSceneNode(ISceneNode* parent, ISceneManager* mgr, s32 id, s32 maxLOD, E_TERRAIN_PATCH_SIZE patchSize, const core::vector3df& position, const core::vector3df& rotation, const core::vector3df& scale) -: ITerrainSceneNode(parent, mgr, id, position, rotation, scale), - OverrideDistanceThreshold(false), DynamicSelectorUpdate(false) + : ITerrainSceneNode(parent, mgr, id, position, rotation, scale) + , OverrideDistanceThreshold(false) + , DynamicSelectorUpdate(false) + , UseDefaultRotationPivot(true) { #ifdef _DEBUG setDebugName("CTerrainSceneNode"); @@ -58,15 +60,13 @@ } //! Initializes the terrain data. Loads the vertices from the heightMapFile -bool CTerrainSceneNode::loadHeightMap(io::IReadFile* file, - video::SColor vertexColor) + bool CTerrainSceneNode::loadHeightMap( io::IReadFile* file, video::SColor vertexColor ) { if (!file) return false; - u32 startTime = os::Timer::getRealTime(); - video::IImage* heightMap = SceneManager->getVideoDriver()->createImageFromFile( - file); + u32 startTime = os::Timer::getTime(); + video::IImage* heightMap = SceneManager->getVideoDriver()->createImageFromFile( file ); if (!heightMap) { @@ -74,14 +74,51 @@ return false; } - SMeshBufferLightMap* pMeshBuffer = new SMeshBufferLightMap(); - // Get the dimension of the heightmap data TerrainData.Size = heightMap->getDimension().Width; + // Make sure the maximum level of detail is compatible with the heightmap size + if( TerrainData.Size <= 17 && TerrainData.MaxLOD > 1 ) + { + TerrainData.MaxLOD = 1; + os::Printer::print( "WARNING! Terrain Size is less than or equal to 17 and MaxLOD was greater than 1! Forcing MaxLOD to 1!" ); + } + else if( TerrainData.Size <= 33 && TerrainData.MaxLOD > 2 ) + { + TerrainData.MaxLOD = 2; + os::Printer::print( "WARNING! Terrain Size is less than or equal to 33 and MaxLOD was greater than 2! Forcing MaxLOD to 2!" ); + } + else if( TerrainData.Size <= 65 && TerrainData.MaxLOD > 3 ) + { + TerrainData.MaxLOD = 3; + os::Printer::print( "WARNING! Terrain Size is less than or equal to 65 and MaxLOD was greater than 3! Forcing MaxLOD to 3!" ); + } + else if( TerrainData.Size <= 129 && TerrainData.MaxLOD > 4 ) + { + TerrainData.MaxLOD = 4; + os::Printer::print( "WARNING! Terrain Size is less than or equal to 129 and MaxLOD was greater than 4! Forcing MaxLOD to 4!" ); + } + else if( TerrainData.Size <= 257 && TerrainData.MaxLOD > 4 ) + { + TerrainData.MaxLOD = 5; + os::Printer::print( "WARNING! Terrain Size is less than or equal to 257 and MaxLOD was greater than 5! Forcing MaxLOD to 5!" ); + } + else if( TerrainData.Size <= 513 && TerrainData.MaxLOD > 6 ) + { + TerrainData.MaxLOD = 6; + os::Printer::print( "WARNING! Terrain Size is less than or equal to 513 and MaxLOD was greater than 6! Forcing MaxLOD to 6!" ); + } + else if( TerrainData.Size <= 1025 && TerrainData.MaxLOD > 7 ) + { + TerrainData.MaxLOD = 7; + os::Printer::print( "WARNING! Terrain Size is less than or equal to 1025 and MaxLOD was greater than 7! Forcing MaxLOD to 7!" ); + } + // --- Generate vertex data from heightmap ---- // resize the vertex array for the mesh buffer one time ( makes loading faster ) + SMeshBufferLightMap* pMeshBuffer = new SMeshBufferLightMap(); pMeshBuffer->Vertices.reallocate(TerrainData.Size * TerrainData.Size); + pMeshBuffer->Vertices.set_used( TerrainData.Size * TerrainData.Size ); video::S3DVertex2TCoords vertex; core::vector3df normal = core::vector3df (0.0f, 1.0f, 0.0f); @@ -89,22 +126,16 @@ // Read the heightmap to get the vertex data // Apply positions changes, scaling changes - for (int x=0; xgetPixel(x,z); + for( s32 z = 0; z < TerrainData.Size; ++z ) + { + s32 index = x * TerrainData.Size + z; - vertex.Pos.Y = (f32)(c.getRed() + c.getGreen() + c.getBlue()) / 3.0f; - vertex.Pos.Y *= TerrainData.Scale.Y; - vertex.Pos.Y += TerrainData.Position.Y; + vertex.Pos.X = (f32)x; + vertex.Pos.Y = (f32)( heightMap->getPixel(x,z).getRed() + heightMap->getPixel(x,z).getGreen() + heightMap->getPixel(x,z).getBlue() ) / 3.0f; + vertex.Pos.Z = (f32)z; - vertex.Pos.Z = (f32)z * TerrainData.Scale.Z; - vertex.Pos.Z += TerrainData.Position.Z; vertex.Normal = normal; vertex.TCoords.X = x / (f32)TerrainData.Size; @@ -113,7 +144,8 @@ vertex.TCoords2.X = x / (f32)TerrainData.Size; vertex.TCoords2.Y = z / (f32)TerrainData.Size; - pMeshBuffer->Vertices.push_back ( vertex ); + pMeshBuffer->Vertices[index] = vertex; + } } // drop heightMap, no longer needed @@ -124,20 +156,42 @@ // add the MeshBuffer to the mesh Mesh.addMeshBuffer(pMeshBuffer); + s32 vertexCount = pMeshBuffer->getVertexCount(); + + // We copy the data to the renderBuffer, after the normals have been calculated. + RenderBuffer.Vertices.reallocate( vertexCount ); + RenderBuffer.Vertices.set_used( vertexCount ); + + for( s32 i = 0; i < vertexCount; i++ ) + { + RenderBuffer.Vertices[i] = pMeshBuffer->Vertices[i]; + RenderBuffer.Vertices[i].Pos *= TerrainData.Scale; + RenderBuffer.Vertices[i].Pos += TerrainData.Position; + } + + // We no longer need the pMeshBuffer pMeshBuffer->drop(); // calculate all the necessary data for the patches and the terrain calculateDistanceThresholds(); createPatches(); calculatePatchData(); - initRenderBuffers(); + + // set the default rotation pivot point to the terrain nodes center + TerrainData.RotationPivot = TerrainData.Center; // Rotate the vertices of the terrain by the rotation specified. Must be done // after calculating the terrain data, so we know what the current center of the // terrain is. setRotation(TerrainData.Rotation); - u32 endTime = os::Timer::getRealTime(); + // Pre-allocate memory for indices + RenderBuffer.Indices.reallocate( TerrainData.PatchCount * TerrainData.PatchCount * + TerrainData.CalcPatchSize * TerrainData.CalcPatchSize * 6 ); + RenderBuffer.Indices.set_used( TerrainData.PatchCount * TerrainData.PatchCount * + TerrainData.CalcPatchSize * TerrainData.CalcPatchSize * 6 ); + + u32 endTime = os::Timer::getTime(); c8 tmp[255]; sprintf(tmp, "Generated terrain data (%dx%d) in %.4f seconds", @@ -147,67 +201,205 @@ return true; } -//! Sets the scale of the scene node. -//! \param scale: New scale of the node -void CTerrainSceneNode::setScale(const core::vector3df& scale) + //! Initializes the terrain data. Loads the vertices from the heightMapFile + bool CTerrainSceneNode::loadHeightMapRAW( io::IReadFile* file, s32 bitsPerPixel, video::SColor vertexColor ) { - if (!Mesh.getMeshBufferCount()) - return; + if( !file ) + return false; - TerrainData.Scale = scale; - video::S3DVertex2TCoords* vertices = (video::S3DVertex2TCoords*)Mesh.getMeshBuffer ( 0 )->getVertices ( ); + // start reading + u32 startTime = os::Timer::getTime(); - int vtxCnt = Mesh.getMeshBuffer(0)->getVertexCount(); - for (int i=0; igetSize(); + s32 bytesPerPixel = bitsPerPixel / 8; + s32 heightMapSize = (s32)sqrt( (f32)( fileSize / bytesPerPixel ) ); - calculateDistanceThresholds ( true ); - calculatePatchData(); + // Get the dimension of the heightmap data + TerrainData.Size = heightMapSize; + + // Make sure the maximum level of detail is compatible with the heightmap size + if( TerrainData.Size <= 17 && TerrainData.MaxLOD > 1 ) + { + TerrainData.MaxLOD = 1; + os::Printer::print( "WARNING! Terrain Size is less than or equal to 17 and MaxLOD was greater than 1! Forcing MaxLOD to 1!" ); + } + else if( TerrainData.Size <= 33 && TerrainData.MaxLOD > 2 ) + { + TerrainData.MaxLOD = 2; + os::Printer::print( "WARNING! Terrain Size is less than or equal to 33 and MaxLOD was greater than 2! Forcing MaxLOD to 2!" ); + } + else if( TerrainData.Size <= 65 && TerrainData.MaxLOD > 3 ) + { + TerrainData.MaxLOD = 3; + os::Printer::print( "WARNING! Terrain Size is less than or equal to 65 and MaxLOD was greater than 3! Forcing MaxLOD to 3!" ); + } + else if( TerrainData.Size <= 129 && TerrainData.MaxLOD > 4 ) + { + TerrainData.MaxLOD = 4; + os::Printer::print( "WARNING! Terrain Size is less than or equal to 129 and MaxLOD was greater than 4! Forcing MaxLOD to 4!" ); + } + else if( TerrainData.Size <= 257 && TerrainData.MaxLOD > 4 ) + { + TerrainData.MaxLOD = 5; + os::Printer::print( "WARNING! Terrain Size is less than or equal to 257 and MaxLOD was greater than 5! Forcing MaxLOD to 5!" ); + } + else if( TerrainData.Size <= 513 && TerrainData.MaxLOD > 6 ) + { + TerrainData.MaxLOD = 6; + os::Printer::print( "WARNING! Terrain Size is less than or equal to 513 and MaxLOD was greater than 6! Forcing MaxLOD to 6!" ); + } + else if( TerrainData.Size <= 1025 && TerrainData.MaxLOD > 7 ) + { + TerrainData.MaxLOD = 7; + os::Printer::print( "WARNING! Terrain Size is less than or equal to 1025 and MaxLOD was greater than 7! Forcing MaxLOD to 7!" ); } -//! Sets the rotation of the node. This only modifies -//! the relative rotation of the node. -//! \param rotation: New rotation of the node in degrees. -void CTerrainSceneNode::setRotation(const core::vector3df& rotation) + // --- Generate vertex data from heightmap ---- + // resize the vertex array for the mesh buffer one time ( makes loading faster ) + SMeshBufferLightMap* pMeshBuffer = new SMeshBufferLightMap(); + pMeshBuffer->Vertices.reallocate( TerrainData.Size * TerrainData.Size ); + pMeshBuffer->Vertices.set_used( TerrainData.Size * TerrainData.Size ); + + video::S3DVertex2TCoords vertex; + core::vector3df normal = core::vector3df( 0.0f, 1.0f, 0.0f ); + vertex.Color = vertexColor; + + // Read the heightmap to get the vertex data + // Apply positions changes, scaling changes + for( s32 x = 0; x < TerrainData.Size; ++x ) { - if (!Mesh.getMeshBufferCount()) - return; + for( s32 z = 0; z < TerrainData.Size; ++z ) + { + vertex.Pos.X = (f32)x; - TerrainData.Rotation = rotation; + if( file->read( &vertex.Pos.Y, bytesPerPixel ) != bytesPerPixel ) + { + os::Printer::print("Error reading heightmap RAW file."); + pMeshBuffer->drop(); + return false; + } - core::matrix4 rotMatrix; - core::vector3df tempVector; - rotMatrix.setRotationDegrees(rotation); + vertex.Pos.Z = (f32)z; + + vertex.Normal = normal; + + vertex.TCoords.X = x / (f32)TerrainData.Size; + vertex.TCoords.Y = z / (f32)TerrainData.Size; + + vertex.TCoords2.X = x / (f32)TerrainData.Size; + vertex.TCoords2.Y = z / (f32)TerrainData.Size; + + pMeshBuffer->Vertices.push_back( vertex ); + } + } - video::S3DVertex2TCoords* vertices = - (video::S3DVertex2TCoords*)Mesh.getMeshBuffer(0)->getVertices(); + // calculate smooth normals for the vertices + calculateNormals( pMeshBuffer ); - for (int i=0; igetVertexCount(); ++i) + // add the MeshBuffer to the mesh + Mesh.addMeshBuffer( pMeshBuffer ); + s32 vertexCount = pMeshBuffer->getVertexCount(); + + // We copy the data to the renderBuffer, after the normals have been calculated. + RenderBuffer.Vertices.reallocate( vertexCount ); + RenderBuffer.Vertices.set_used( vertexCount ); + + for( s32 i = 0; i < vertexCount; i++ ) { - tempVector = vertices[i].Pos; - tempVector -= TerrainData.Center; - rotMatrix.inverseRotateVect(tempVector); - tempVector += TerrainData.Center; - vertices[i].Pos = tempVector; + RenderBuffer.Vertices[i] = pMeshBuffer->Vertices[i]; + RenderBuffer.Vertices[i].Pos *= TerrainData.Scale; + RenderBuffer.Vertices[i].Pos += TerrainData.Position; } + // We no longer need the pMeshBuffer + pMeshBuffer->drop(); + + // calculate all the necessary data for the patches and the terrain + calculateDistanceThresholds(); + createPatches(); calculatePatchData(); + + // set the default rotation pivot point to the terrain nodes center + TerrainData.RotationPivot = TerrainData.Center; + + // Rotate the vertices of the terrain by the rotation specified. Must be done + // after calculating the terrain data, so we know what the current center of the + // terrain is. + setRotation( TerrainData.Rotation ); + + // Pre-allocate memory for indices + RenderBuffer.Indices.reallocate( TerrainData.PatchCount * TerrainData.PatchCount * + TerrainData.CalcPatchSize * TerrainData.CalcPatchSize * 6 ); + RenderBuffer.Indices.set_used( TerrainData.PatchCount * TerrainData.PatchCount * + TerrainData.CalcPatchSize * TerrainData.CalcPatchSize * 6 ); + + u32 endTime = os::Timer::getTime(); + + c8 tmp[255]; + sprintf( tmp, "Generated terrain data (%dx%d) in %.4f seconds", + TerrainData.Size, TerrainData.Size, (endTime - startTime) / 1000.0f ); + os::Printer::print( tmp ); + + return true; + } + + //! Sets the scale of the scene node. + //! \param scale: New scale of the node + void CTerrainSceneNode::setScale(const core::vector3df& scale) + { + TerrainData.Scale = scale; + applyTransformation(); + } + + //! Sets the rotation of the node. This only modifies + //! the relative rotation of the node. + //! \param rotation: New rotation of the node in degrees. + void CTerrainSceneNode::setRotation(const core::vector3df& rotation) + { + TerrainData.Rotation = rotation; + applyTransformation(); + } + + //! Sets the pivot point for rotation of this node. This is useful for the TiledTerrainManager to + //! rotate all terrain tiles around a global world point. + //! NOTE: The default for the RotationPivot will be the center of the individual tile. + void CTerrainSceneNode::setRotationPivot( const core::vector3df& pivot ) + { + UseDefaultRotationPivot = false; + TerrainData.RotationPivot = pivot; } //! Sets the position of the node. //! \param newpos: New postition of the scene node. void CTerrainSceneNode::setPosition ( const core::vector3df& newpos ) { + TerrainData.Position = newpos; + applyTransformation(); + } + + //! Apply transformation changes( scale, position, rotation ) + void CTerrainSceneNode::applyTransformation() + { if (!Mesh.getMeshBufferCount()) return; - TerrainData.Position = newpos; - video::S3DVertex2TCoords* vertices = (video::S3DVertex2TCoords*) - Mesh.getMeshBuffer(0)->getVertices(); + TerrainData.Position = TerrainData.Position; + video::S3DVertex2TCoords* meshVertices = (video::S3DVertex2TCoords*)Mesh.getMeshBuffer( 0 )->getVertices(); + s32 vtxCount = Mesh.getMeshBuffer( 0 )->getVertexCount(); + core::matrix4 rotMatrix; + rotMatrix.setRotationDegrees( TerrainData.Rotation ); - for (int i=0; igetVertexCount(); ++i) - vertices[i].Pos += TerrainData.Position; + for( s32 i = 0; i < vtxCount; ++i ) + { + RenderBuffer.Vertices[i].Pos = meshVertices[i].Pos * TerrainData.Scale + TerrainData.Position; + RenderBuffer.Vertices[i].Pos -= TerrainData.RotationPivot; + rotMatrix.inverseRotateVect( RenderBuffer.Vertices[i].Pos ); + RenderBuffer.Vertices[i].Pos += TerrainData.RotationPivot; + } + + calculateDistanceThresholds( true ); calculatePatchData(); } @@ -218,15 +410,21 @@ //! generated for that patch. void CTerrainSceneNode::OnPreRender() { - if (!IsVisible || Mesh.getMeshBufferCount()< 1 || !SceneManager->getActiveCamera()) + if (!IsVisible || !SceneManager->getActiveCamera()) return; + preRenderLODCalculations(); + preRenderIndicesCalculations(); + } + + void CTerrainSceneNode::preRenderLODCalculations() + { SceneManager->registerNodeForRendering(this); // Do Not call ISceneNode::OnPreRender ( ), this node should have no children // Determine the camera rotation, based on the camera direction. core::line3d line; - line.start = SceneManager->getActiveCamera()->getPosition(); + line.start = SceneManager->getActiveCamera()->getAbsolutePosition(); line.end = SceneManager->getActiveCamera()->getTarget(); core::vector3df cameraRotation = line.getVector().getHorizontalAngle(); core::vector3df cameraPosition = SceneManager->getActiveCamera()->getPosition ( ); @@ -249,69 +447,72 @@ // Determine each patches LOD based on distance from camera ( and whether or not they are in // the view frustrum ). - for (int j=0; jgetBoundingBox().intersectsWithBox( - TerrainData.Patches[j].BoundingBox)) + if( frustrum->getBoundingBox().intersectsWithBox( TerrainData.Patches[j].BoundingBox ) ) { f32 distance = (cameraPosition.X - TerrainData.Patches[j].Center.X) * (cameraPosition.X - TerrainData.Patches[j].Center.X) + (cameraPosition.Y - TerrainData.Patches[j].Center.Y) * (cameraPosition.Y - TerrainData.Patches[j].Center.Y) + (cameraPosition.Z - TerrainData.Patches[j].Center.Z) * (cameraPosition.Z - TerrainData.Patches[j].Center.Z); - for (int i=TerrainData.MaxLOD-1; i>=0; --i) + for( s32 i = TerrainData.MaxLOD - 1; i >= 0; --i ) { if (distance >= TerrainData.LODDistanceThreshold[i]) { TerrainData.Patches[j].CurrentLOD = i; break; } - + //else if( i == 0 ) + { // If we've turned off a patch from viewing, because of the frustrum, and now we turn around and it's // too close, we need to turn it back on, at the highest LOD. The if above doesn't catch this. TerrainData.Patches[j].CurrentLOD = 0; } } + } else { TerrainData.Patches[j].CurrentLOD = -1; } } + } - video::S3DVertex2TCoords* vertices = - (video::S3DVertex2TCoords*)Mesh.getMeshBuffer(0)->getVertices(); - - VerticesToRender = 0; + void CTerrainSceneNode::preRenderIndicesCalculations() + { IndicesToRender = 0; + s32 index11; + s32 index21; + s32 index12; + s32 index22; // Then generate the indices for all patches that are visible. - for (int i=0; i= 0) { - int x = 0; - int z = 0; + s32 x = 0; + s32 z = 0; // calculate the step we take this patch, based on the patches current LOD - int step = 1 << TerrainData.Patches[index].CurrentLOD; + s32 step = 1 << TerrainData.Patches[index].CurrentLOD; // Loop through patch and generate indices while ( z < TerrainData.CalcPatchSize ) { - int index1 = getIndex(j, i, index, x + step, z); - int index2 = getIndex(j, i, index, x, z + step); - int index3 = getIndex(j, i, index, x, z); - int index4 = getIndex(j, i, index, x + step, z + step); - - RenderBuffer.Vertices[VerticesToRender++] = vertices[index1]; - RenderBuffer.Vertices[VerticesToRender++] = vertices[index2]; - RenderBuffer.Vertices[VerticesToRender++] = vertices[index3]; - RenderBuffer.Vertices[VerticesToRender++] = vertices[index4]; - RenderBuffer.Vertices[VerticesToRender++] = vertices[index2]; - RenderBuffer.Vertices[VerticesToRender++] = vertices[index1]; - - IndicesToRender += 6; + index11 = getIndex( j, i, index, x, z ); + index21 = getIndex( j, i, index, x + step, z ); + index12 = getIndex( j, i, index, x, z + step ); + index22 = getIndex( j, i, index, x + step, z + step ); + + RenderBuffer.Indices[IndicesToRender++] = index12; + RenderBuffer.Indices[IndicesToRender++] = index11; + RenderBuffer.Indices[IndicesToRender++] = index22; + RenderBuffer.Indices[IndicesToRender++] = index22; + RenderBuffer.Indices[IndicesToRender++] = index11; + RenderBuffer.Indices[IndicesToRender++] = index21; // increment index position horizontally x += step; @@ -324,6 +525,7 @@ } } } + } if ( DynamicSelectorUpdate && TriangleSelector ) { @@ -332,10 +534,11 @@ } } + //! Render the scene node void CTerrainSceneNode::render() { - if (!Mesh.getMeshBufferCount()) + if (!IsVisible || !SceneManager->getActiveCamera()) return; core::matrix4 identity; @@ -343,11 +546,13 @@ SceneManager->getVideoDriver()->setMaterial(Mesh.getMeshBuffer(0)->getMaterial()); + // For use with geomorphing SceneManager->getVideoDriver()->drawIndexedTriangleList( (video::S3DVertex2TCoords*)RenderBuffer.getVertices(), - VerticesToRender, + RenderBuffer.getVertexCount(), RenderBuffer.getIndices(), - IndicesToRender / 3 ); + IndicesToRender / 3 + ); } //! Return the bounding box of the entire terrain. @@ -376,31 +581,40 @@ mb.Vertices.reallocate ( numVertices ); video::S3DVertex2TCoords* vertices = (video::S3DVertex2TCoords*)Mesh.getMeshBuffer ( 0 )->getVertices ( ); - int i; + s32 i; for (i=0; igetVertices ( ); - - for (int x=0; xgetVertices(); - - for (int x= 0; x 0) - TerrainData.Patches[index].Top = &TerrainData.Patches[(x-1) * - TerrainData.PatchCount + z]; + TerrainData.Patches[index].Top = &TerrainData.Patches[(x-1) * TerrainData.PatchCount + z]; else TerrainData.Patches[index].Top = 0; // Bottom if (x < TerrainData.PatchCount-1) - TerrainData.Patches[index].Bottom = &TerrainData.Patches[(x+1) * - TerrainData.PatchCount + z]; + TerrainData.Patches[index].Bottom = &TerrainData.Patches[(x+1) * TerrainData.PatchCount + z]; else TerrainData.Patches[index].Bottom = 0; @@ -788,27 +1017,18 @@ else TerrainData.Patches[index].Right = 0; } + } // get center of Terrain TerrainData.Center = TerrainData.BoundingBox.getCenter(); -} -//! initialize render buffer data -void CTerrainSceneNode::initRenderBuffers() + // if the default rotation pivot is still being used, update it. + if( UseDefaultRotationPivot ) { - //! The new RenderBuffer always displays Indices by { 0,1,2,3,4,5,6,7,8.... } - //! Set it up here, one time only, preRender only needs to determine the - //! number of indices to send to the GPU. - - u32 MaxIndices = TerrainData.PatchCount * TerrainData.PatchCount * TerrainData.CalcPatchSize * - TerrainData.CalcPatchSize * 6 + 1; - u32 i; - RenderBuffer.Vertices.set_used ( TerrainData.Size * TerrainData.Size * 4 ); - RenderBuffer.Indices.set_used ( MaxIndices ); - - for ( i = 0; i < MaxIndices; i++ ) - RenderBuffer.Indices[i] = i; + TerrainData.RotationPivot = TerrainData.Center; } + } + //! used to calculate or recalculate the distance thresholds void CTerrainSceneNode::calculateDistanceThresholds(bool scalechanged) @@ -819,14 +1039,12 @@ // Only update the LODDistanceThreshold if it's not manually changed if (!OverrideDistanceThreshold) { - // Determine new distance threshold for determining what LOD to draw patches at - if ( TerrainData.LODDistanceThreshold && scalechanged ) + if( TerrainData.LODDistanceThreshold ) { - for (i=0; i& lodarray) { - for (int i=0; i& LODs); + //! Manually sets the LOD of a patch + //! \param patchX: Patch x coordinate. + //! \param patchZ: Patch z coordinate. + //! \param LOD: The level of detail to set the patch to. + virtual void setLODOfPatch( s32 patchX, s32 patchZ, s32 LOD ); + //! Returns center of terrain. virtual core::vector3df getTerrainCenter() { @@ -189,19 +204,26 @@ private: friend class CTerrainTriangleSelector; + friend class CTiledTerrainSceneNodeManager; struct SPatch { s32 CurrentLOD; core::aabbox3df BoundingBox; core::vector3df Center; - SMeshBufferLightMap RenderBuffer; - s32 VerticesToRender; - s32 IndicesToRender; SPatch* Top; SPatch* Bottom; SPatch* Right; SPatch* Left; + + SPatch() + : CurrentLOD( -1 ) + , Top( 0 ) + , Bottom( 0 ) + , Right( 0 ) + , Left( 0 ) + { + } }; struct STerrainData @@ -216,6 +238,7 @@ s32 Size; core::vector3df Position; core::vector3df Rotation; + core::vector3df RotationPivot; core::vector3df Scale; core::vector3df Center; s32 PatchSize; @@ -227,6 +250,9 @@ SPatch* Patches; }; + virtual void preRenderLODCalculations(); + virtual void preRenderIndicesCalculations(); + //! get indices when generating index data for patches at varying levels of detail. u32 getIndex(const s32& PatchX, const s32& PatchZ, const s32& PatchIndex, u32 vX, u32 vZ); @@ -239,9 +265,6 @@ //! calculate the internal STerrainData structure void calculatePatchData(); - //! initialize render buffer data - void initRenderBuffers(); - //! calculate or recalculate the distance thresholds void calculateDistanceThresholds(bool scalechanged = false); @@ -251,6 +274,9 @@ //! sets the CurrentLOD of TerrainData patches to the LODs specified in the array void setCurrentLODOfPatches(core::array& lodarray); + //! Apply transformation changes( scale, position, rotation ) + void applyTransformation(); + STerrainData TerrainData; SMesh Mesh; SMeshBufferLightMap RenderBuffer; @@ -259,6 +285,7 @@ bool DynamicSelectorUpdate; bool OverrideDistanceThreshold; + bool UseDefaultRotationPivot; core::vector3df OldCameraPosition; core::vector3df OldCameraRotation; diff -Nauwr Irrlicht/CTerrainTriangleSelector.cpp Irrlicht/CTerrainTriangleSelector.cpp --- Irrlicht/CTerrainTriangleSelector.cpp 2005-08-20 22:21:28.000000000 +0200 +++ Irrlicht/CTerrainTriangleSelector.cpp 2005-09-15 14:37:10.000000000 +0200 @@ -37,12 +37,10 @@ s32 x, z, indexCount, i; core::triangle3df tri; core::array indices; - CTerrainSceneNode* terrainNode = (CTerrainSceneNode*)node; // Get pointer to the GeoMipMaps vertices - video::S3DVertex2TCoords* vertices = - (video::S3DVertex2TCoords*)terrainNode->Mesh.getMeshBuffer(0)->getVertices ( ); + video::S3DVertex2TCoords* vertices = (video::S3DVertex2TCoords*)terrainNode->RenderBuffer.getVertices(); // Clear current data TrianglePatches.TotalTriangles = 0; @@ -164,8 +159,10 @@ s32 i, j; for (i=0; i +#include +#include "os.h" +#include + +namespace irr +{ +namespace scene +{ + + //! constructor + CTiledTerrainSceneNodeManager::CTiledTerrainSceneNodeManager( ISceneNode* parent, ISceneManager* mgr, s32 id, + s32 tileWidth, s32 xWidth, s32 zWidth, s32 viewableTileDistance, const core::vector3df& position, + const core::vector3df& rotation, const core::vector3df& scale ) + : ITiledTerrainSceneNodeManager( parent, mgr, id, position, rotation, scale ) + , m_tileWidth( tileWidth ) + , m_xWidth( xWidth ) + , m_zWidth( zWidth ) + , m_viewableTileDistance( viewableTileDistance ) + { + m_allocatedTiles = xWidth * zWidth; + m_terrainTiles = (ITerrainSceneNode**)malloc( sizeof( ITerrainSceneNode* ) * m_allocatedTiles ); + + for( s32 i = 0; i < m_allocatedTiles; i++ ) + m_terrainTiles[i] = 0; + } + + //! destructor + CTiledTerrainSceneNodeManager::~CTiledTerrainSceneNodeManager ( ) + { + } + + //! Resizes the allocated memory for tiles. At this point in time, you can only increase + //! the size, not decrease. + s32 CTiledTerrainSceneNodeManager::Resize( s32 xWidth, s32 zWidth ) + { + s32 rtnValue = 0; + s32 temp = xWidth * zWidth; + + if( temp < m_allocatedTiles ) + { + rtnValue = -1; + } + else if( temp == m_allocatedTiles ) + { + rtnValue = -2; + } + else + { + m_terrainTiles = (ITerrainSceneNode**)realloc( m_terrainTiles, sizeof( ITerrainSceneNode* ) * m_allocatedTiles ); + + for( s32 i = m_allocatedTiles; i < temp; i++ ) + m_terrainTiles[i] = 0; + + m_allocatedTiles = temp; + + if( !m_terrainTiles ) + rtnValue = 1; + } + + return rtnValue; + } + + //! Loads a tile from a heightmap + bool CTiledTerrainSceneNodeManager::LoadTile( s32 X, s32 Z, const c8* heightmap, + s32 maxLOD, E_TERRAIN_PATCH_SIZE patchSize, s32 id ) + { + if( !SceneManager ) + return false; + + s32 index = X * m_xWidth + Z; + + if( index > m_allocatedTiles ) + { + return false; + } + + core::vector3df position( RelativeTranslation ); + position.X += (float)X * m_tileWidth; + position.Z += (float)Z * m_tileWidth; + position *= RelativeScale; + + m_terrainTiles[index] = SceneManager->addTerrainSceneNode( heightmap, this, id, + position, RelativeRotation, RelativeScale, + video::SColor( 255,255,255,255 ), maxLOD, patchSize + ); + + return true; + } + + //! Loads a tile from a heightmap + bool CTiledTerrainSceneNodeManager::LoadTileRAW( s32 X, s32 Z, const c8* heightmap, + s32 bitsPerPixel, s32 maxLOD, E_TERRAIN_PATCH_SIZE patchSize, s32 id ) + { + if( !SceneManager ) + return false; + + s32 index = X * m_xWidth + Z; + + if( index > m_allocatedTiles ) + { + return false; + } + + core::vector3df position( RelativeTranslation ); + position.X += (float)X * m_tileWidth; + position.Z += (float)Z * m_tileWidth; + + m_terrainTiles[index] = SceneManager->addTerrainSceneNodeRAW( heightmap, + bitsPerPixel, this, id, position, core::vector3df( 0,0,0 ), + core::vector3df( 1,1,1 ), video::SColor( 255,255,255,255 ), maxLOD, + patchSize + ); + + return true; + } + + //! Gets the pointer to tile terrainSceneNode data + ITerrainSceneNode* CTiledTerrainSceneNodeManager::GetTileNode( s32 X, s32 Z ) + { + s32 index = X * m_xWidth + Z; + + if( index > m_allocatedTiles ) + return 0; + + return m_terrainTiles[index]; + } + + void CTiledTerrainSceneNodeManager::OnPreRender() + { + core::list::Iterator it; + core::list::Iterator endIt; + + it = Children.begin(); + endIt = Children.end(); + for( ; it != endIt; ++it ) + { + ((CTerrainSceneNode*)(*it))->preRenderLODCalculations(); + } + + // Once all tiledterrainnodes have their 1st sweep of LODs determined, we can check the LOD of patches adjacent to + // each other on adjacent tiles. + + // Get the tile that the camera is on. + core::vector3df camPos = SceneManager->getActiveCamera()->getPosition(); + ITerrainSceneNode* activeTile = 0; + s32 activeTileX = -1; + s32 activeTileZ = -1; + + for( s32 x = 0; x < m_xWidth; x++ ) + { + for( s32 z = 0; z < m_zWidth; z++ ) + { + s32 index = x * m_xWidth + z; + + // Need to only check the X and Z edges, not the Y of the bbox + if( m_terrainTiles[index]->isVisible() ) + { + core::aabbox3df bbox = m_terrainTiles[index]->getBoundingBox(); + if( camPos.X >= bbox.MinEdge.X && + camPos.X <= bbox.MaxEdge.X && + camPos.Z >= bbox.MinEdge.Z && + camPos.Z <= bbox.MaxEdge.Z ) + { + activeTile = m_terrainTiles[index]; + activeTileX = x; + activeTileZ = z; + + // breaks both for loops + x = m_xWidth; + z = m_zWidth; + continue; + } + } + } + } + + // We've found the tile that the camera is in. Check seams with all neighbor tiles of this tile only. + //if( activeTile && activeTile->isVisible() ) + if( activeTile ) + { + // Make only my tile, and surrounding tiles visible. + for( s32 x = 0; x < m_xWidth; x++ ) + { + for( s32 z = 0; z < m_zWidth; z++ ) + { + s32 index = x * m_xWidth + z; + + if( m_viewableTileDistance == 0 ) + { + if( x == activeTileX && z == activeTileZ ) + m_terrainTiles[index]->setVisible( true ); + else + m_terrainTiles[index]->setVisible( false ); + } + else + { + if( x >= activeTileX - m_viewableTileDistance && x <= activeTileX + m_viewableTileDistance && + z >= activeTileZ - m_viewableTileDistance && z <= activeTileZ + m_viewableTileDistance ) + { + m_terrainTiles[index]->setVisible( true ); + } + else + { + m_terrainTiles[index]->setVisible( false ); + } + } + } + } + + if( m_viewableTileDistance > 0 ) + { + core::array myLODs; + core::array neighborLODs; + s32 totalPatches = activeTile->getCurrentLODOfPatches( myLODs ); + s32 tilePatchWidth = (s32)sqrt( (f32)totalPatches ); + s32 neighborIndex = 0; + ITerrainSceneNode* neighbor = 0; + + // does the activeTile have a left neighbor? + neighborIndex = (activeTileX - 1) * m_xWidth + activeTileZ; + if( neighborIndex > -1 && neighborIndex < m_allocatedTiles ) + { + neighbor = m_terrainTiles[neighborIndex]; + if( neighbor && neighbor->isVisible() ) + { + // Check the right patches of the neighbor with the left patches of my tile. + neighbor->getCurrentLODOfPatches( neighborLODs ); + + s32 myX = 0; + s32 neighborX = tilePatchWidth - 1; + s32 Z = 0; + for( Z = 0; Z < tilePatchWidth - 1; Z++ ) + { + s32 myIndex = myX * tilePatchWidth + Z; + s32 neighborIndex = neighborX * tilePatchWidth + Z; + if( neighborLODs[neighborIndex] != myLODs[myIndex] ) + neighbor->setLODOfPatch( neighborX, Z, myLODs[myIndex] ); + } + + if( m_viewableTileDistance > 1 ) + { + // Check the left neighbor's top patches with the left neighbor's top neighbor's bottom patches + // Check the left neighbor's bottom patches with the left neighbor's bottom neighor's top patches + } + } + } + + // does the activeTile have a top neighbor? + neighbor = 0; + neighborIndex = activeTileX * m_xWidth + (activeTileZ + 1); + if( neighborIndex > -1 && neighborIndex < m_allocatedTiles ) + { + neighbor = m_terrainTiles[neighborIndex]; + if( neighbor && neighbor->isVisible() ) + { + // check the bottom patches of the neighbor with the top patches of my tile. + neighborLODs.clear(); + neighbor->getCurrentLODOfPatches( neighborLODs ); + + s32 myZ = tilePatchWidth - 1; + s32 neighborZ = 0; + s32 X = 0; + for( X = 0; X < tilePatchWidth - 1; X++ ) + { + s32 myIndex = X * tilePatchWidth + myZ; + s32 neighborIndex = X * tilePatchWidth + neighborZ; + if( neighborLODs[neighborIndex] != myLODs[myIndex] ) + neighbor->setLODOfPatch( X, neighborZ, myLODs[myIndex] ); + } + + if( m_viewableTileDistance > 1 ) + { + // Check the top neighbor's left patches with the top neighbor's left neighbor's right patches + // Check the top neighbor's right patches with the top neighbor's right neighbor's left patches + } + } + } + + // does the activeTile have a bottom neighbor? + neighbor = 0; + neighborIndex = activeTileX * m_xWidth + (activeTileZ - 1); + if( neighborIndex > -1 && neighborIndex < m_allocatedTiles ) + { + neighbor = m_terrainTiles[neighborIndex]; + if( neighbor ) + { + // check the top patches of the neighbor with the bottom patches of my tile. + neighborLODs.clear(); + neighbor->getCurrentLODOfPatches( neighborLODs ); + + s32 myZ = 0; + s32 neighborZ = tilePatchWidth - 1; + s32 X = 0; + for( X = 0; X < tilePatchWidth - 1; X++ ) + { + s32 myIndex = X * tilePatchWidth + myZ; + s32 neighborIndex = X * tilePatchWidth + neighborZ; + if( neighborLODs[neighborIndex] != myLODs[myIndex] ) + neighbor->setLODOfPatch( X, neighborZ, myLODs[myIndex] ); + } + + if( m_viewableTileDistance > 1 ) + { + // Check the bottom neighbor's left patches with the bottom neighbor's left neighbor's right patches + // Check the bottom neighbor's right patches with the bottom neighbor's right neighbor's left patches + } + } + } + + // does the activeTile have a right neighbor? + neighbor = 0; + neighborIndex = (activeTileX + 1 ) * m_xWidth + activeTileZ; + if( neighborIndex > -1 && neighborIndex < m_allocatedTiles ) + { + neighbor = m_terrainTiles[neighborIndex]; + if( neighbor ) + { + // Check the left patches of the neighbor with the right patches of my tile. + neighbor->getCurrentLODOfPatches( neighborLODs ); + + s32 myX = tilePatchWidth - 1; + s32 neighborX = 0; + s32 Z = 0; + for( Z = 0; Z < tilePatchWidth - 1; Z++ ) + { + s32 myIndex = myX * tilePatchWidth + Z; + s32 neighborIndex = neighborX * tilePatchWidth + Z; + if( neighborLODs[neighborIndex] != myLODs[myIndex] ) + neighbor->setLODOfPatch( neighborX, Z, myLODs[myIndex] ); + } + + if( m_viewableTileDistance > 1 ) + { + // Check the right neighbor's top patches with the right neighbor's top neighbor's bottom patches + // Check the right neighbor's bottom patches with the right neighbor's bottom neighbor's top patches + } + } + } + } + } + + it = Children.begin(); + for( ; it != endIt; ++it ) + { + ((CTerrainSceneNode*)(*it))->preRenderIndicesCalculations(); + } + } + + //! Sets the scale for all terrain tiles the terrain manager is managing. + //! \param scale: New scale of all terrain nodes. + void CTiledTerrainSceneNodeManager::setScale( const core::vector3df& scale ) + { + ISceneNode::setScale( scale ); + + for( s32 X = 0; X < m_xWidth; ++X ) + { + for( s32 Z = 0; Z < m_zWidth; ++Z ) + { + s32 index = X * m_xWidth + Z; + + if( index > m_allocatedTiles || !m_terrainTiles[index] ) + { + return; + } + + core::vector3df position( RelativeTranslation ); + position.X += (float)X * m_tileWidth * RelativeScale.X; + position.Z += (float)Z * m_tileWidth * RelativeScale.Z; + + m_terrainTiles[index]->setPosition( position ); + m_terrainTiles[index]->setScale( RelativeScale ); + } + } + } + + //! Sets the rotation for all terrain tiles the terrain manager is managing. + //! \param scale: New rotation of all terrain nodes. + void CTiledTerrainSceneNodeManager::setRotation( const core::vector3df& rotation ) + { + ISceneNode::setRotation( rotation ); + + for( s32 X = 0; X < m_xWidth; ++X ) + { + for( s32 Z = 0; Z < m_zWidth; ++Z ) + { + s32 index = X * m_xWidth + Z; + + if( index > m_allocatedTiles || !m_terrainTiles[index] ) + { + return; + } + + m_terrainTiles[index]->setRotationPivot( RelativeTranslation ); + m_terrainTiles[index]->setRotation( RelativeRotation ); + } + } + } + + //! Sets the position for the first terrain tile the terrain manager is managing. + //! All other tiles will react to this change in position, if any. + //! \param scale: New position of the terrain manager. + void CTiledTerrainSceneNodeManager::setPosition( const core::vector3df& position ) + { + ISceneNode::setPosition( position ); + + for( s32 X = 0; X < m_xWidth; ++X ) + { + for( s32 Z = 0; Z < m_zWidth; ++Z ) + { + s32 index = X * m_xWidth + Z; + + if( index > m_allocatedTiles || !m_terrainTiles[index] ) + { + return; + } + + core::vector3df position( RelativeTranslation ); + position.X += (float)X * m_tileWidth * RelativeScale.X; + position.Z += (float)Z * m_tileWidth * RelativeScale.Z; + + m_terrainTiles[index]->setPosition( position ); + } + } + } + +} // end namespace scene +} // end namespace irr + diff -Nauwr Irrlicht/CTiledTerrainSceneNodeManager.h Irrlicht/CTiledTerrainSceneNodeManager.h --- Irrlicht/CTiledTerrainSceneNodeManager.h 1970-01-01 01:00:00.000000000 +0100 +++ Irrlicht/CTiledTerrainSceneNodeManager.h 2005-09-16 10:43:26.000000000 +0200 @@ -0,0 +1,105 @@ +// Copyright (C) 2002-2005 Spintz( Tom Ince ) +// This file is part of the "Irrlicht Engine". +// For conditions of distribution and use, see copyright notice in Irrlicht.h + +// The code for the TerrainSceneNode is based on the GeoMipMapSceneNode +// developed by Spinz. He made it available for Irrlicht and allowed it to be +// distributed under this licence. I only modified some parts. A lot of thanks go to him. + +// TODO : Match the LOD of seams of tiles for all visible tiles, not just the active tiles, +// immediate x and z neighbors. ( should it only be the active tiles neighbors +// including x, z and diagonal or should it be all visible tiles, maybe make a flag? ) +// TODO : If the camera is not above a tile, for activeTile determination, select the tile +// nearest to the camera as the activeTile. + +#ifndef __C_TILED_TERRAIN_SCENE_NODE_MANAGER_H__ +#define __C_TILED_TERRAIN_SCENE_NODE_MANAGER_H__ + +#include "ITiledTerrainSceneNodeManager.h" +#include "ITerrainSceneNode.h" + +namespace irr +{ +namespace scene +{ + //! A scene node for displaying tiled terrain, with each tile being a ITerrainSceneNode. + /** You cannot, or rather should not, modify the position, scale or rotation of the terrain tiles + * directly. If you wish to change the position, scale or rotation of the terrain tiles that are + * used by the tile manager, use the set functions for the tile manager. It will apply the proper + * changes to all tiles it is managing. + **/ + + class CTiledTerrainSceneNodeManager : public ITiledTerrainSceneNodeManager + { + public: + + //! constructor + CTiledTerrainSceneNodeManager( ISceneNode* parent, ISceneManager* mgr, s32 id, + s32 tileWidth = 0, s32 xWidth = 0, s32 zWidth = 0, s32 viewableTileDistance = 1, + const core::vector3df& position = core::vector3df(0.0f, 0.0f, 0.0f), + const core::vector3df& rotation = core::vector3df(0.0f, 0.0f, 0.0f), + const core::vector3df& scale = core::vector3df(1.0f, 1.0f, 1.0f) ); + + //! destructor + virtual ~CTiledTerrainSceneNodeManager(); + + //! Sets the viewable tile distance, or the depth of tiles to set as viewable based on the + //! active tile( the tile the camera is hovering in. + virtual void SetViewableTileDistance( s32 viewableTileDistance ) { m_viewableTileDistance = viewableTileDistance; } + + //! Resizes the allocated memory for tiles. At this point in time, you can only increase + //! the size, not decrease. + virtual s32 Resize( s32 xWidth, s32 zWidth ); + + //! Loads a tile from a heightmap + virtual bool LoadTile( s32 X, s32 Z, const c8* heightmap, + s32 maxLOD = 5, E_TERRAIN_PATCH_SIZE patchSize = ETPS_33, s32 id = -1 ); + + //! Loads a tile from a heightmap + virtual bool LoadTileRAW( s32 X, s32 Z, const c8* heightmap, s32 bitsPerPixel = 16, + s32 maxLOD = 5, E_TERRAIN_PATCH_SIZE patchSize = ETPS_33, s32 id = -1 ); + + //! Gets the pointer to tile terrainSceneNode data + virtual ITerrainSceneNode* GetTileNode( s32 X, s32 Z ); + + virtual void OnPreRender(); + + //! Returns the axis aligned, not transformed bounding box of this node. + //! This means that if this node is a animated 3d character, moving in a room, + //! the bounding box will always be around the origin. To get the box in + //! real world coordinates, just transform it with the matrix you receive with + //! getAbsoluteTransformation() or simply use getTransformedBoundingBox(), + //! which does the same. + virtual const core::aabbox3d& getBoundingBox() const { return m_bbox; } + + //! Sets the scale for all terrain tiles the terrain manager is managing. + //! \param scale: New scale of all terrain nodes. + virtual void setScale( const core::vector3df& scale ); + + //! Sets the rotation for all terrain tiles the terrain manager is managing. + //! \param scale: New rotation of all terrain nodes. + virtual void setRotation( const core::vector3df& scale ); + + //! Sets the position for the first terrain tile the terrain manager is managing. + //! All other tiles will react to this change in position, if any. + //! \param scale: New position of the terrain manager. + virtual void setPosition( const core::vector3df& scale ); + + protected: + + ITerrainSceneNode** m_terrainTiles; + core::aabbox3d m_bbox; + + s32 m_allocatedTiles; + s32 m_xWidth; + s32 m_zWidth; + s32 m_tileWidth; + s32 m_viewableTileDistance; + }; + +} // end namespace scene +} // end namespace irr + + +#endif // __C_TILED_TERRAIN_SCENE_NODE_MANAGER_H__ + diff -Nauwr Irrlicht/include/irrlicht.h Irrlicht/include/irrlicht.h --- Irrlicht/include/irrlicht.h 2005-08-20 22:46:48.000000000 +0200 +++ Irrlicht/include/irrlicht.h 2005-09-28 10:14:26.000000000 +0200 @@ -87,6 +87,7 @@ #include "IShaderConstantSetCallBack.h" #include "IParticleSystemSceneNode.h" #include "ITerrainSceneNode.h" +#include "ITiledTerrainSceneNodeManager.h" #include "ITextSceneNode.h" #include "IParticleEmitter.h" #include "IParticleAffector.h" diff -Nauwr Irrlicht/include/ISceneManager.h Irrlicht/include/ISceneManager.h --- Irrlicht/include/ISceneManager.h 2005-08-23 18:51:58.000000000 +0200 +++ Irrlicht/include/ISceneManager.h 2005-09-27 15:46:10.000000000 +0200 @@ -162,6 +168,7 @@ class IMetaTriangleSelector; class IMeshManipulator; class ITextSceneNode; + class ITiledTerrainSceneNodeManager; //! The Scene Manager manages scene nodes, mesh recources, cameras and all the other stuff. /** All Scene nodes can be created only here. There is a always growing list of scene @@ -638,6 +644,39 @@ video::SColor vertexColor = video::SColor(255,255,255,255), s32 maxLOD=5, E_TERRAIN_PATCH_SIZE patchSize=ETPS_17) = 0; + //! Just like the addTerrainSceneNode() method, but takes a RAW file as + //! input parameter for the heightmap. For more informations take a look + //! add the other overload. + virtual ITerrainSceneNode* addTerrainSceneNodeRAW( + const char* heightMapFileName, s32 bitsPerPixel = 16, + ISceneNode* parent=0, s32 id=-1, + const core::vector3df& position = core::vector3df(0.0f,0.0f,0.0f), + const core::vector3df& rotation = core::vector3df(0.0f,0.0f,0.0f), + const core::vector3df& scale = core::vector3df(1.0f,1.0f,1.0f), + video::SColor vertexColor = video::SColor(255,255,255,255), + s32 maxLOD=5, E_TERRAIN_PATCH_SIZE patchSize=ETPS_17) = 0; + + //! Just like the other addTerrainSceneNodeRAW() method, but takes an IReadFile + //! pointer as parameter for the heightmap. For more informations take a look + //! add the other overload. + virtual ITerrainSceneNode* addTerrainSceneNodeRAW( + io::IReadFile* heightMapFile, s32 bitsPerPixel = 16, + ISceneNode* parent=0, s32 id=-1, + const core::vector3df& position = core::vector3df(0.0f,0.0f,0.0f), + const core::vector3df& rotation = core::vector3df(0.0f,0.0f,0.0f), + const core::vector3df& scale = core::vector3df(1.0f,1.0f,1.0f), + video::SColor vertexColor = video::SColor(255,255,255,255), + s32 maxLOD=5, E_TERRAIN_PATCH_SIZE patchSize=ETPS_17) = 0; + + //! Adds a tiled terrain scene node manager to the scene graph. + virtual ITiledTerrainSceneNodeManager* addTiledTerrainSceneNodeManager( + s32 tileWidth, s32 xWidth, s32 zWidth, s32 viewableTileDistance = 1, + ISceneNode* parent = 0, s32 id = -1, + const core::vector3df& position = core::vector3df(0.0f,0.0f,0.0f), + const core::vector3df& rotation = core::vector3df(0.0f,0.0f,0.0f), + const core::vector3df& scale = core::vector3df(1.0f,1.0f,1.0f), + video::SColor vertexColor = video::SColor(255,255,255,255) ) = 0; + //! Adds an empty scene node to the scene graph. /** Can be used for doing advanced transformations or structuring the scene graph. diff -Nauwr Irrlicht/include/ITerrainSceneNode.h Irrlicht/include/ITerrainSceneNode.h --- Irrlicht/include/ITerrainSceneNode.h 2005-08-20 22:17:46.000000000 +0200 +++ Irrlicht/include/ITerrainSceneNode.h 2005-09-16 10:33:02.000000000 +0200 @@ -108,6 +108,12 @@ \return: Returns the number of elements in the array */ virtual s32 getCurrentLODOfPatches(core::array& LODs) = 0; + //! Manually sets the LOD of a patch + //! \param patchX: Patch x coordinate. + //! \param patchZ: Patch z coordinate. + //! \param LOD: The level of detail to set the patch to. + virtual void setLODOfPatch( s32 patchX, s32 patchZ, s32 LOD ) = 0; + //! Returns center of terrain. virtual core::vector3df getTerrainCenter() = 0; @@ -142,6 +148,11 @@ to the same values as in the first set. If this is another value than zero, it will scale the second texture coordinate set by this value. */ virtual void scaleTexture(f32 scale = 1.0f, f32 scale2 = 0.0f) = 0; + + //! Sets the pivot point for rotation of this node. This is useful for the TiledTerrainManager to + //! rotate all terrain tiles around a global world point. + //! NOTE: The default for the RotationPivot will be the center of the individual tile. + virtual void setRotationPivot( const core::vector3df& pivot ) = 0; }; } // end namespace scene diff -Nauwr Irrlicht/include/ITiledTerrainSceneNodeManager.h Irrlicht/include/ITiledTerrainSceneNodeManager.h --- Irrlicht/include/ITiledTerrainSceneNodeManager.h 1970-01-01 01:00:00.000000000 +0100 +++ Irrlicht/include/ITiledTerrainSceneNodeManager.h 2005-09-16 10:43:36.000000000 +0200 @@ -0,0 +1,77 @@ +// Copyright (C) 2002-2005 Nikolaus Gebhardt +// This file is part of the "Irrlicht Engine". +// For conditions of distribution and use, see copyright notice in Irrlicht.h + +// The code for the TerrainSceneNode is based on the terrain renderer by Soconne and +// the GeoMipMapSceneNode developed by Spinz. They made their code available for Irrlicht +// and allowed it to be distributed under this licence. I only modified some parts. +// A lot of thanks go to them. + +#ifndef __I_TILED_TERRAIN_SCENE_NODE_MANAGER_H__ +#define __I_TILED_TERRAIN_SCENE_NODE_MANAGER_H__ + +#include "ISceneNode.h" +#include "ITerrainSceneNode.h" + +namespace irr +{ +namespace scene +{ + class ISceneManager; + + //! A scene node for displaying tiled terrain, with each tile being a ITerrainSceneNode. + /** You cannot, or rather should not, modify the position, scale or rotation of the terrain tiles + * directly. If you wish to change the position, scale or rotation of the terrain tiles that are + * used by the tile manager, use the set functions for the tile manager. It will apply the proper + * changes to all tiles it is managing. + **/ + class ITiledTerrainSceneNodeManager : public ISceneNode + { + public: + + //! constructor + ITiledTerrainSceneNodeManager( ISceneNode* parent, ISceneManager* mgr, s32 id, + const core::vector3df& position = core::vector3df(0.0f, 0.0f, 0.0f), + const core::vector3df& rotation = core::vector3df(0.0f, 0.0f, 0.0f), + const core::vector3df& scale = core::vector3df(1.0f, 1.0f, 1.0f) ) + : ISceneNode (parent, mgr, id, position, rotation, scale) {} + + //! destructor + virtual ~ITiledTerrainSceneNodeManager() {}; + + //! Sets the viewable tile distance, or the depth of tiles to set as viewable based on the + //! active tile( the tile the camera is hovering in. + virtual void SetViewableTileDistance( s32 viewableTileDistance ) = 0; + + //! Resizes the allocated memory for tiles. At this point in time, you can only increase + //! the size, not decrease. + virtual s32 Resize( s32 xWidth, s32 zWidth ) = 0; + + //! Loads a tile from a heightmap + virtual bool LoadTile( s32 X, s32 Z, const c8* heightmap, s32 maxLOD = 5, + E_TERRAIN_PATCH_SIZE patchSize = ETPS_33, s32 id = -1 ) = 0; + + //! Loads a tile from a heightmap + virtual bool LoadTileRAW( s32 X, s32 Z, const c8* heightmap, s32 bitsPerPixel = 16, + s32 maxLOD = 5, E_TERRAIN_PATCH_SIZE patchSize = ETPS_33, s32 id = -1 ) = 0; + + //! Gets the pointer to tile terrainSceneNode data + virtual ITerrainSceneNode* GetTileNode( s32 X, s32 Z ) = 0; + + //! Renders the node. + virtual void render() {} + + //! Returns the axis aligned, not transformed bounding box of this node. + //! This means that if this node is a animated 3d character, moving in a room, + //! the bounding box will always be around the origin. To get the box in + //! real world coordinates, just transform it with the matrix you receive with + //! getAbsoluteTransformation() or simply use getTransformedBoundingBox(), + //! which does the same. + virtual const core::aabbox3d& getBoundingBox() const = 0; + }; + +} // end namespace scene +} // end namespace irr + + +#endif // __I_TILED_TERRAIN_SCENE_NODE_MANAGER_H__