Retour
Version Originale

./aip/1.8aipmod/source/Irrlicht/CTerrainSceneNode.cpp :


// Copyright (C) 2002-2011 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 GeoMipMapSceneNode

// developed by Spintz. 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.


#include "CTerrainSceneNode.h"
#include "CTerrainTriangleSelector.h"
#include "IVideoDriver.h"
#include "ISceneManager.h"
#include "ICameraSceneNode.h"
#include "SViewFrustum.h"
#include "irrMath.h"
#include "os.h"
#include "IGUIFont.h"
#include "IFileSystem.h"
#include "IReadFile.h"
#include "ITextSceneNode.h"
#include "IAnimatedMesh.h"
#include "SMesh.h"
#include "CDynamicMeshBuffer.h"

namespace irr
{
namespace scene
{

	//! constructor

	CTerrainSceneNode::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)
	: ITerrainSceneNode(parent, mgr, id, position, rotation, scale),
	TerrainData(patchSize, maxLOD, position, rotation, scale), RenderBuffer(0),
	VerticesToRender(0), IndicesToRender(0), DynamicSelectorUpdate(false),
	OverrideDistanceThreshold(false), UseDefaultRotationPivot(true), ForceRecalculation(false),
	OldCameraPosition(core::vector3df(-99999.9f, -99999.9f, -99999.9f)),
	OldCameraRotation(core::vector3df(-99999.9f, -99999.9f, -99999.9f)),
	OldCameraUp(core::vector3df(-99999.9f, -99999.9f, -99999.9f)),
	CameraMovementDelta(10.0f), CameraRotationDelta(1.0f),CameraFOVDelta(0.1f),
	TCoordScale1(1.0f), TCoordScale2(1.0f), FileSystem(fs)
	{
		#ifdef _DEBUG
		setDebugName("CTerrainSceneNode");
		#endif

		Mesh = new SMesh();
		RenderBuffer = new CDynamicMeshBuffer(video::EVT_2TCOORDS, video::EIT_16BIT);
		RenderBuffer->setHardwareMappingHint(scene::EHM_STATIC, scene::EBT_VERTEX);
		RenderBuffer->setHardwareMappingHint(scene::EHM_DYNAMIC, scene::EBT_INDEX);

		if (FileSystem)
			FileSystem->grab();

		setAutomaticCulling(scene::EAC_OFF);
	}


	//! destructor

	CTerrainSceneNode::~CTerrainSceneNode()
	{
		delete [] TerrainData.Patches;

		if (FileSystem)
			FileSystem->drop();

		if (Mesh)
			Mesh->drop();

		if (RenderBuffer)
			RenderBuffer->drop();
	}


	//! Initializes the terrain data. Loads the vertices from the heightMapFile

	bool CTerrainSceneNode::loadHeightMap(io::IReadFile* file, video::SColor vertexColor,
			s32 smoothFactor)
	{
		if (!file)
			return false;

		Mesh->MeshBuffers.clear();
		const u32 startTime = os::Timer::getRealTime();
		video::IImage* heightMap = SceneManager->getVideoDriver()->createImageFromFile(file);

		if (!heightMap)
		{
			os::Printer::log("Unable to load heightmap.");
			return false;
		}

		HeightmapFile = file->getFileName();

		// Get the dimension of the heightmap data

		TerrainData.Size = heightMap->getDimension().Width;

		switch (TerrainData.PatchSize)
		{
			case ETPS_9:
				if (TerrainData.MaxLOD > 3)
				{
					TerrainData.MaxLOD = 3;
				}
			break;
			case ETPS_17:
				if (TerrainData.MaxLOD > 4)
				{
					TerrainData.MaxLOD = 4;
				}
			break;
			case ETPS_33:
				if (TerrainData.MaxLOD > 5)
				{
					TerrainData.MaxLOD = 5;
				}
			break;
			case ETPS_65:
				if (TerrainData.MaxLOD > 6)
				{
					TerrainData.MaxLOD = 6;
				}
			break;
			case ETPS_129:
				if (TerrainData.MaxLOD > 7)
				{
					TerrainData.MaxLOD = 7;
				}
			break;
		}

		// --- Generate vertex data from heightmap ----

		// resize the vertex array for the mesh buffer one time (makes loading faster)

		scene::CDynamicMeshBuffer *mb=0;

		const u32 numVertices = TerrainData.Size * TerrainData.Size;
		if (numVertices <= 65536)
		{
			//small enough for 16bit buffers

			mb=new scene::CDynamicMeshBuffer(video::EVT_2TCOORDS, video::EIT_16BIT);
			RenderBuffer->getIndexBuffer().setType(video::EIT_16BIT);
		}
		else
		{
			//we need 32bit buffers

			mb=new scene::CDynamicMeshBuffer(video::EVT_2TCOORDS, video::EIT_32BIT);
			RenderBuffer->getIndexBuffer().setType(video::EIT_32BIT);
		}

		mb->getVertexBuffer().set_used(numVertices);

		// Read the heightmap to get the vertex data

		// Apply positions changes, scaling changes

		const f32 tdSize = 1.0f/(f32)(TerrainData.Size-1);
		s32 index = 0;
		float fx=0.f;
		float fx2=0.f;
		for (s32 x = 0; x < TerrainData.Size; ++x)
		{
			float fz=0.f;
			float fz2=0.f;
			for (s32 z = 0; z < TerrainData.Size; ++z)
			{
				video::S3DVertex2TCoords& vertex= static_cast<video::S3DVertex2TCoords*>(mb->getVertexBuffer().pointer())[index++];
				vertex.Normal.set(0.0f, 1.0f, 0.0f);
				vertex.Color = vertexColor;
				vertex.Pos.X = fx;
				vertex.Pos.Y = (f32) heightMap->getPixel(TerrainData.Size-x-1,z).getLuminance();
				vertex.Pos.Z = fz;

				vertex.TCoords.X = vertex.TCoords2.X = 1.f-fx2;
				vertex.TCoords.Y = vertex.TCoords2.Y = fz2;

				++fz;
				fz2 += tdSize;
			}
			++fx;
			fx2 += tdSize;
		}

		// drop heightMap, no longer needed

		heightMap->drop();

		smoothTerrain(mb, smoothFactor);

		// calculate smooth normals for the vertices

		calculateNormals(mb);

		// add the MeshBuffer to the mesh

		Mesh->addMeshBuffer(mb);

		// We copy the data to the renderBuffer, after the normals have been calculated.

		RenderBuffer->getVertexBuffer().set_used(numVertices);

		for (u32 i = 0; i < numVertices; ++i)
		{
			RenderBuffer->getVertexBuffer()[i] = mb->getVertexBuffer()[i];
			RenderBuffer->getVertexBuffer()[i].Pos *= TerrainData.Scale;
			RenderBuffer->getVertexBuffer()[i].Pos += TerrainData.Position;
		}

		// We no longer need the mb

		mb->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->getIndexBuffer().set_used(
				TerrainData.PatchCount * TerrainData.PatchCount *
				TerrainData.CalcPatchSize * TerrainData.CalcPatchSize * 6);

		RenderBuffer->setDirty();

		const u32 endTime = os::Timer::getRealTime();

		c8 tmp[255];
		snprintf(tmp, 255, "Generated terrain data (%dx%d) in %.4f seconds",
			TerrainData.Size, TerrainData.Size, (endTime - startTime) / 1000.0f );
		os::Printer::log(tmp);

		return true;
	}


	//! Initializes the terrain data. Loads the vertices from the heightMapFile

	bool CTerrainSceneNode::loadHeightMapRAW(io::IReadFile* file,
			s32 bitsPerPixel, bool signedData, bool floatVals,
			s32 width, video::SColor vertexColor, s32 smoothFactor)
	{
		if (!file)
			return false;
		if (floatVals && bitsPerPixel != 32)
			return false;

		// start reading

		const u32 startTime = os::Timer::getTime();

		Mesh->MeshBuffers.clear();

		const s32 bytesPerPixel = bitsPerPixel / 8;

		// Get the dimension of the heightmap data

		const s32 filesize = file->getSize();
		if (!width)
			TerrainData.Size = core::floor32(sqrtf((f32)(filesize / bytesPerPixel)));
		else
		{
			if ((filesize-file->getPos())/bytesPerPixel>width*width)
			{
				os::Printer::log("Error reading heightmap RAW file", "File is too small.");
				return false;
			}
			TerrainData.Size = width;
		}

		switch (TerrainData.PatchSize)
		{
			case ETPS_9:
				if (TerrainData.MaxLOD > 3)
				{
					TerrainData.MaxLOD = 3;
				}
			break;
			case ETPS_17:
				if (TerrainData.MaxLOD > 4)
				{
					TerrainData.MaxLOD = 4;
				}
			break;
			case ETPS_33:
				if (TerrainData.MaxLOD > 5)
				{
					TerrainData.MaxLOD = 5;
				}
			break;
			case ETPS_65:
				if (TerrainData.MaxLOD > 6)
				{
					TerrainData.MaxLOD = 6;
				}
			break;
			case ETPS_129:
				if (TerrainData.MaxLOD > 7)
				{
					TerrainData.MaxLOD = 7;
				}
			break;
		}

		// --- Generate vertex data from heightmap ----

		// resize the vertex array for the mesh buffer one time (makes loading faster)

		scene::CDynamicMeshBuffer *mb=0;
		const u32 numVertices = TerrainData.Size * TerrainData.Size;
		if (numVertices <= 65536)
		{
			//small enough for 16bit buffers

			mb=new scene::CDynamicMeshBuffer(video::EVT_2TCOORDS, video::EIT_16BIT);
			RenderBuffer->getIndexBuffer().setType(video::EIT_16BIT);
		}
		else
		{
			//we need 32bit buffers

			mb=new scene::CDynamicMeshBuffer(video::EVT_2TCOORDS, video::EIT_32BIT);
			RenderBuffer->getIndexBuffer().setType(video::EIT_32BIT);
		}

		mb->getVertexBuffer().reallocate(numVertices);

		video::S3DVertex2TCoords vertex;
		vertex.Normal.set(0.0f, 1.0f, 0.0f);
		vertex.Color = vertexColor;

		// Read the heightmap to get the vertex data

		// Apply positions changes, scaling changes

		const f32 tdSize = 1.0f/(f32)(TerrainData.Size-1);
		float fx=0.f;
		float fx2=0.f;
		for (s32 x = 0; x < TerrainData.Size; ++x)
		{
			float fz=0.f;
			float fz2=0.f;
			for (s32 z = 0; z < TerrainData.Size; ++z)
			{
				bool failure=false;
				vertex.Pos.X = fx;
				if (floatVals)
				{
					if (file->read(&vertex.Pos.Y, bytesPerPixel) != bytesPerPixel)
						failure=true;
				}
				else if (signedData)
				{
					switch (bytesPerPixel)
					{
						case 1:
						{
							s8 val;
							if (file->read(&val, bytesPerPixel) != bytesPerPixel)
								failure=true;
							vertex.Pos.Y=val;
						}
						break;
						case 2:
						{
							s16 val;
							if (file->read(&val, bytesPerPixel) != bytesPerPixel)
								failure=true;
							vertex.Pos.Y=val/256.f;
						}
						break;
						case 4:
						{
							s32 val;
							if (file->read(&val, bytesPerPixel) != bytesPerPixel)
								failure=true;
							vertex.Pos.Y=val/16777216.f;
						}
						break;
					}
				}
				else
				{
					switch (bytesPerPixel)
					{
						case 1:
						{
							u8 val;
							if (file->read(&val, bytesPerPixel) != bytesPerPixel)
								failure=true;
							vertex.Pos.Y=val;
						}
						break;
						case 2:
						{
							u16 val;
							if (file->read(&val, bytesPerPixel) != bytesPerPixel)
								failure=true;
							vertex.Pos.Y=val/256.f;
						}
						break;
						case 4:
						{
							u32 val;
							if (file->read(&val, bytesPerPixel) != bytesPerPixel)
								failure=true;
							vertex.Pos.Y=val/16777216.f;
						}
						break;
					}
				}
				if (failure)
				{
					os::Printer::log("Error reading heightmap RAW file.");
					mb->drop();
					return false;
				}
				vertex.Pos.Z = fz;

				vertex.TCoords.X = vertex.TCoords2.X = 1.f-fx2;
				vertex.TCoords.Y = vertex.TCoords2.Y = fz2;

				mb->getVertexBuffer().push_back(vertex);
				++fz;
				fz2 += tdSize;
			}
			++fx;
			fx2 += tdSize;
		}

		smoothTerrain(mb, smoothFactor);

		// calculate smooth normals for the vertices

		calculateNormals(mb);

		// add the MeshBuffer to the mesh

		Mesh->addMeshBuffer(mb);
		const u32 vertexCount = mb->getVertexCount();

		// We copy the data to the renderBuffer, after the normals have been calculated.

		RenderBuffer->getVertexBuffer().set_used(vertexCount);

		for (u32 i = 0; i < vertexCount; i++)
		{
			RenderBuffer->getVertexBuffer()[i] = mb->getVertexBuffer()[i];
			RenderBuffer->getVertexBuffer()[i].Pos *= TerrainData.Scale;
			RenderBuffer->getVertexBuffer()[i].Pos += TerrainData.Position;
		}

		// We no longer need the mb

		mb->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->getIndexBuffer().set_used(
				TerrainData.PatchCount*TerrainData.PatchCount*
				TerrainData.CalcPatchSize*TerrainData.CalcPatchSize*6);

		const u32 endTime = os::Timer::getTime();

		c8 tmp[255];
		snprintf(tmp, 255, "Generated terrain data (%dx%d) in %.4f seconds",
			TerrainData.Size, TerrainData.Size, (endTime - startTime) / 1000.0f);
		os::Printer::log(tmp);

		return true;
	}


	//! Returns the mesh

	IMesh* CTerrainSceneNode::getMesh() { return Mesh; }


	//! Returns the material based on the zero based index i.

	video::SMaterial& CTerrainSceneNode::getMaterial(u32 i)
	{
		return Mesh->getMeshBuffer(i)->getMaterial();
	}


	//! Returns amount of materials used by this scene node ( always 1 )

	u32 CTerrainSceneNode::getMaterialCount() const
	{
		return Mesh->getMeshBufferCount();
	}


	//! 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();
		calculateNormals(RenderBuffer);
		ForceRecalculation = true;
	}


	//! 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();
		ForceRecalculation = true;
	}


	//! 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();
		ForceRecalculation = true;
	}


	//! Apply transformation changes(scale, position, rotation)

	void CTerrainSceneNode::applyTransformation()
	{
		if (!Mesh->getMeshBufferCount())
			return;

		TerrainData.Position = TerrainData.Position;
		s32 vtxCount = Mesh->getMeshBuffer(0)->getVertexCount();
		core::matrix4 rotMatrix;
		rotMatrix.setRotationDegrees(TerrainData.Rotation);

		for (s32 i = 0; i < vtxCount; ++i)
		{
			RenderBuffer->getVertexBuffer()[i].Pos = Mesh->getMeshBuffer(0)->getPosition(i) * TerrainData.Scale + TerrainData.Position;

			RenderBuffer->getVertexBuffer()[i].Pos -= TerrainData.RotationPivot;
			rotMatrix.inverseRotateVect(RenderBuffer->getVertexBuffer()[i].Pos);
			RenderBuffer->getVertexBuffer()[i].Pos += TerrainData.RotationPivot;
		}

		calculateDistanceThresholds(true);
		calculatePatchData();

		RenderBuffer->setDirty(EBT_VERTEX);
	}


	//! Updates the scene nodes indices if the camera has moved or rotated by a certain

	//! threshold, which can be changed using the SetCameraMovementDeltaThreshold and

	//! SetCameraRotationDeltaThreshold functions. This also determines if a given patch

	//! for the scene node is within the view frustum and if it's not the indices are not

	//! generated for that patch.

	void CTerrainSceneNode::OnRegisterSceneNode()
	{
		if (!IsVisible || !SceneManager->getActiveCamera())
			return;

		preRenderLODCalculations();
		preRenderIndicesCalculations();
		ISceneNode::OnRegisterSceneNode();
		ForceRecalculation = false;
	}


	void CTerrainSceneNode::preRenderLODCalculations()
	{
		scene::ICameraSceneNode * camera = SceneManager->getActiveCamera();
		if(!camera)
			return;

		SceneManager->registerNodeForRendering(this);
		// Do Not call ISceneNode::OnRegisterSceneNode(), this node should have no children


		// Determine the camera rotation, based on the camera direction.

		const core::vector3df cameraPosition = camera->getAbsolutePosition();
		const core::vector3df cameraRotation = core::line3d<f32>(cameraPosition, camera->getTarget()).getVector().getHorizontalAngle();
		core::vector3df cameraUp = camera->getUpVector();
		cameraUp.normalize();
		const f32 CameraFOV = SceneManager->getActiveCamera()->getFOV();

		// Only check on the Camera's Y Rotation

		if (!ForceRecalculation)
		{
			if ((fabsf(cameraRotation.X - OldCameraRotation.X) < CameraRotationDelta) &&
				(fabsf(cameraRotation.Y - OldCameraRotation.Y) < CameraRotationDelta))
			{
				if ((fabs(cameraPosition.X - OldCameraPosition.X) < CameraMovementDelta) &&
					(fabs(cameraPosition.Y - OldCameraPosition.Y) < CameraMovementDelta) &&
					(fabs(cameraPosition.Z - OldCameraPosition.Z) < CameraMovementDelta))
				{
					if (fabs(CameraFOV-OldCameraFOV) < CameraFOVDelta &&
						cameraUp.dotProduct(OldCameraUp) > (1.f - (cos(core::DEGTORAD * CameraRotationDelta))))
					{
						return;
					}
				}
			}
		}

		OldCameraPosition = cameraPosition;
		OldCameraRotation = cameraRotation;
		OldCameraUp = cameraUp;
		OldCameraFOV = CameraFOV;

		const SViewFrustum* frustum = SceneManager->getActiveCamera()->getViewFrustum();

		// Determine each patches LOD based on distance from camera (and whether or not they are in

		// the view frustum).

		const s32 count = TerrainData.PatchCount * TerrainData.PatchCount;
		for (s32 j = 0; j < count; ++j)
		{
			if (frustum->getBoundingBox().intersectsWithBox(TerrainData.Patches[j].BoundingBox))
			{
				const 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 (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 frustum, 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;
			}
		}
	}


	void CTerrainSceneNode::preRenderIndicesCalculations()
	{
		scene::IIndexBuffer& indexBuffer = RenderBuffer->getIndexBuffer();
		IndicesToRender = 0;
		indexBuffer.set_used(0);

		s32 index = 0;
		// Then generate the indices for all patches that are visible.

		for (s32 i = 0; i < TerrainData.PatchCount; ++i)
		{
			for (s32 j = 0; j < TerrainData.PatchCount; ++j)
			{
				if (TerrainData.Patches[index].CurrentLOD >= 0)
				{
					s32 x = 0;
					s32 z = 0;

					// calculate the step we take this patch, based on the patches current LOD

					const s32 step = 1 << TerrainData.Patches[index].CurrentLOD;

					// Loop through patch and generate indices

					while (z < TerrainData.CalcPatchSize)
					{
						const s32 index11 = getIndex(j, i, index, x, z);
						const s32 index21 = getIndex(j, i, index, x + step, z);
						const s32 index12 = getIndex(j, i, index, x, z + step);
						const s32 index22 = getIndex(j, i, index, x + step, z + step);

						indexBuffer.push_back(index12);
						indexBuffer.push_back(index11);
						indexBuffer.push_back(index22);
						indexBuffer.push_back(index22);
						indexBuffer.push_back(index11);
						indexBuffer.push_back(index21);
						IndicesToRender+=6;

						// increment index position horizontally

						x += step;

						// we've hit an edge

						if (x >= TerrainData.CalcPatchSize)
						{
							x = 0;
							z += step;
						}
					}
				}
				++index;
			}
		}

		RenderBuffer->setDirty(EBT_INDEX);

		if (DynamicSelectorUpdate && TriangleSelector)
		{
			CTerrainTriangleSelector* selector = (CTerrainTriangleSelector*)TriangleSelector;
			selector->setTriangleData(this, -1);
		}
	}


	//! Render the scene node

	void CTerrainSceneNode::render()
	{
		if (!IsVisible || !SceneManager->getActiveCamera())
			return;

		if (!Mesh->getMeshBufferCount())
			return;

		video::IVideoDriver* driver = SceneManager->getVideoDriver();

		driver->setTransform (video::ETS_WORLD, core::IdentityMatrix);
		driver->setMaterial(Mesh->getMeshBuffer(0)->getMaterial());

		RenderBuffer->getIndexBuffer().set_used(IndicesToRender);

		// For use with geomorphing

		driver->drawMeshBuffer(RenderBuffer);

		RenderBuffer->getIndexBuffer().set_used(RenderBuffer->getIndexBuffer().allocated_size());

		// for debug purposes only:

		if (DebugDataVisible)
		{
			video::SMaterial m;
			m.Lighting = false;
			driver->setMaterial(m);
			if (DebugDataVisible & scene::EDS_BBOX)
				driver->draw3DBox(TerrainData.BoundingBox, video::SColor(255,255,255,255));

			const s32 count = TerrainData.PatchCount * TerrainData.PatchCount;
			s32 visible = 0;
			if (DebugDataVisible & scene::EDS_BBOX_BUFFERS)
			{
				for (s32 j = 0; j < count; ++j)
				{
					driver->draw3DBox(TerrainData.Patches[j].BoundingBox, video::SColor(255,255,0,0));
					visible += (TerrainData.Patches[j].CurrentLOD >= 0);
				}
			}

			if (DebugDataVisible & scene::EDS_NORMALS)
			{
				IAnimatedMesh * arrow = SceneManager->addArrowMesh(
						"__debugnormal", 0xFFECEC00,
						0xFF999900, 4, 8, 1.f, 0.6f, 0.05f,
						0.3f);
				if (0 == arrow)
				{
					arrow = SceneManager->getMesh( "__debugnormal");
				}
				IMesh *mesh = arrow->getMesh(0);

				// find a good scaling factor


				core::matrix4 m2;

				// draw normals

				driver->setTransform(video::ETS_WORLD, core::IdentityMatrix);
				for (u32 i=0; i != RenderBuffer->getVertexCount(); ++i)
				{
					const core::vector3df& v = RenderBuffer->getNormal(i);
					// align to v->Normal

					if (core::vector3df(0,-1,0)==v)
					{
						m2.makeIdentity();
						m2[5]=-m2[5];
					}
					else
					{
						core::quaternion quatRot;
						m2=quatRot.rotationFromTo(v,core::vector3df(0,1,0)).getMatrix();
					}

					m2.setTranslation(RenderBuffer->getPosition(i));
					m2=AbsoluteTransformation*m2;

					driver->setTransform(video::ETS_WORLD, m2 );
					for (u32 a = 0; a != mesh->getMeshBufferCount(); ++a)
						driver->drawMeshBuffer(mesh->getMeshBuffer(a));
				}
				driver->setTransform(video::ETS_WORLD, AbsoluteTransformation);
			}

			static u32 lastTime = 0;

			const u32 now = os::Timer::getRealTime();
			if (now - lastTime > 1000)
			{
				char buf[64];
				snprintf(buf, 64, "Count: %d, Visible: %d", count, visible);
				os::Printer::log(buf);

				lastTime = now;
			}
		}
	}


	//! Return the bounding box of the entire terrain.

	const core::aabbox3d<f32>& CTerrainSceneNode::getBoundingBox() const
	{
		return TerrainData.BoundingBox;
	}


	//! Return the bounding box of a patch

	const core::aabbox3d<f32>& CTerrainSceneNode::getBoundingBox(s32 patchX, s32 patchZ) const
	{
		return TerrainData.Patches[patchX * TerrainData.PatchCount + patchZ].BoundingBox;
	}


	//! Gets the meshbuffer data based on a specified Level of Detail.

	//! \param mb: A reference to an SMeshBuffer object

	//! \param LOD: The Level Of Detail you want the indices from.

	void CTerrainSceneNode::getMeshBufferForLOD(IDynamicMeshBuffer& mb, s32 LOD ) const
	{
		if (!Mesh->getMeshBufferCount())
			return;

		LOD = core::clamp(LOD, 0, TerrainData.MaxLOD - 1);

		const u32 numVertices = Mesh->getMeshBuffer(0)->getVertexCount();
		mb.getVertexBuffer().reallocate(numVertices);
		video::S3DVertex2TCoords* vertices = (video::S3DVertex2TCoords*)Mesh->getMeshBuffer(0)->getVertices();

		for (u32 n=0; n<numVertices; ++n)
			mb.getVertexBuffer().push_back(vertices[n]);

		mb.getIndexBuffer().setType(RenderBuffer->getIndexBuffer().getType());

		// calculate the step we take for all patches, since LOD is the same

		const s32 step = 1 << LOD;

		// Generate the indices for all patches at the specified LOD

		s32 index = 0;
		for (s32 i=0; i<TerrainData.PatchCount; ++i)
		{
			for (s32 j=0; j<TerrainData.PatchCount; ++j)
			{
				s32 x = 0;
				s32 z = 0;

				// Loop through patch and generate indices

				while (z < TerrainData.CalcPatchSize)
				{
					const s32 index11 = getIndex(j, i, index, x, z);
					const s32 index21 = getIndex(j, i, index, x + step, z);
					const s32 index12 = getIndex(j, i, index, x, z + step);
					const s32 index22 = getIndex(j, i, index, x + step, z + step);

					mb.getIndexBuffer().push_back(index12);
					mb.getIndexBuffer().push_back(index11);
					mb.getIndexBuffer().push_back(index22);
					mb.getIndexBuffer().push_back(index22);
					mb.getIndexBuffer().push_back(index11);
					mb.getIndexBuffer().push_back(index21);

					// increment index position horizontally

					x += step;

					if (x >= TerrainData.CalcPatchSize) // we've hit an edge

					{
						x = 0;
						z += step;
					}
				}
				++index;
			}
		}
	}


	//! Gets the indices for a specified patch at a specified Level of Detail.

	//! \param mb: A reference to an array of u32 indices.

	//! \param patchX: Patch x coordinate.

	//! \param patchZ: Patch z coordinate.

	//! \param LOD: The level of detail to get for that patch. If -1, then get

	//! the CurrentLOD. If the CurrentLOD is set to -1, meaning it's not shown,

	//! then it will retrieve the triangles at the highest LOD (0).

	//! \return: Number if indices put into the buffer.

	s32 CTerrainSceneNode::getIndicesForPatch(core::array<u32>& indices, s32 patchX, s32 patchZ, s32 LOD)
	{
		if (patchX < 0 || patchX > TerrainData.PatchCount-1 ||
				patchZ < 0 || patchZ > TerrainData.PatchCount-1)
			return -1;

		if (LOD < -1 || LOD > TerrainData.MaxLOD - 1)
			return -1;

		core::array<s32> cLODs;
		bool setLODs = false;

		// If LOD of -1 was passed in, use the CurrentLOD of the patch specified

		if (LOD == -1)
		{
			LOD = TerrainData.Patches[patchX * TerrainData.PatchCount + patchZ].CurrentLOD;
		}
		else
		{
			getCurrentLODOfPatches(cLODs);
			setCurrentLODOfPatches(LOD);
			setLODs = true;
		}

		if (LOD < 0)
			return -2; // Patch not visible, don't generate indices.


		// calculate the step we take for this LOD

		const s32 step = 1 << LOD;

		// Generate the indices for the specified patch at the specified LOD

		const s32 index = patchX * TerrainData.PatchCount + patchZ;

		s32 x = 0;
		s32 z = 0;

		indices.set_used(TerrainData.PatchSize * TerrainData.PatchSize * 6);

		// Loop through patch and generate indices

		s32 rv=0;
		while (z<TerrainData.CalcPatchSize)
		{
			const s32 index11 = getIndex(patchZ, patchX, index, x, z);
			const s32 index21 = getIndex(patchZ, patchX, index, x + step, z);
			const s32 index12 = getIndex(patchZ, patchX, index, x, z + step);
			const s32 index22 = getIndex(patchZ, patchX, index, x + step, z + step);

			indices[rv++] = index12;
			indices[rv++] = index11;
			indices[rv++] = index22;
			indices[rv++] = index22;
			indices[rv++] = index11;
			indices[rv++] = index21;

			// increment index position horizontally

			x += step;

			if (x >= TerrainData.CalcPatchSize) // we've hit an edge

			{
				x = 0;
				z += step;
			}
		}

		if (setLODs)
			setCurrentLODOfPatches(cLODs);

		return rv;
	}


	//! Populates an array with the CurrentLOD of each patch.

	//! \param LODs: A reference to a core::array<s32> to hold the values

	//! \return Returns the number of elements in the array

	s32 CTerrainSceneNode::getCurrentLODOfPatches(core::array<s32>& LODs) const
	{
		s32 numLODs;
		LODs.clear();

		const s32 count = TerrainData.PatchCount * TerrainData.PatchCount;
		for (numLODs = 0; numLODs < count; numLODs++)
			LODs.push_back(TerrainData.Patches[numLODs].CurrentLOD);

		return LODs.size();
	}


	//! 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.

	void CTerrainSceneNode::setLODOfPatch(s32 patchX, s32 patchZ, s32 LOD)
	{
		TerrainData.Patches[patchX * TerrainData.PatchCount + patchZ].CurrentLOD = LOD;
	}


	//! Override the default generation of distance thresholds for determining the LOD a patch

	//! is rendered at.

	bool CTerrainSceneNode::overrideLODDistance(s32 LOD, f64 newDistance)
	{
		OverrideDistanceThreshold = true;

		if (LOD < 0 || LOD > TerrainData.MaxLOD - 1)
			return false;

		TerrainData.LODDistanceThreshold[LOD] = newDistance * newDistance;

		return true;
	}


	//! Creates a planar texture mapping on the terrain

	//! \param resolution: resolution of the planar mapping. This is the value

	//! specifying the relation between world space and texture coordinate space.

	void CTerrainSceneNode::scaleTexture(f32 resolution, f32 resolution2)
	{
		TCoordScale1 = resolution;
		TCoordScale2 = resolution2;

		const f32 resBySize = resolution / (f32)(TerrainData.Size-1);
		const f32 res2BySize = resolution2 / (f32)(TerrainData.Size-1);
		u32 index = 0;
		f32 xval = 0.f;
		f32 x2val = 0.f;
		for (s32 x=0; x<TerrainData.Size; ++x)
		{
			f32 zval=0.f;
			f32 z2val=0.f;
			for (s32 z=0; z<TerrainData.Size; ++z)
			{
				RenderBuffer->getVertexBuffer()[index].TCoords.X = 1.f-xval;
				RenderBuffer->getVertexBuffer()[index].TCoords.Y = zval;

				if (RenderBuffer->getVertexType()==video::EVT_2TCOORDS)
				{
					if (resolution2 == 0)
					{
						((video::S3DVertex2TCoords&)RenderBuffer->getVertexBuffer()[index]).TCoords2 = RenderBuffer->getVertexBuffer()[index].TCoords;
					}
					else
					{
						((video::S3DVertex2TCoords&)RenderBuffer->getVertexBuffer()[index]).TCoords2.X = 1.f-x2val;
						((video::S3DVertex2TCoords&)RenderBuffer->getVertexBuffer()[index]).TCoords2.Y = z2val;
					}
				}

				++index;
				zval += resBySize;
				z2val += res2BySize;
			}
			xval += resBySize;
			x2val += res2BySize;
		}

		RenderBuffer->setDirty(EBT_VERTEX);
	}


	//! used to get the indices when generating index data for patches at varying levels of detail.

	u32 CTerrainSceneNode::getIndex(const s32 PatchX, const s32 PatchZ,
					const s32 PatchIndex, u32 vX, u32 vZ) const
	{
		// top border

		if (vZ == 0)
		{
			if (TerrainData.Patches[PatchIndex].Top &&
				TerrainData.Patches[PatchIndex].CurrentLOD < TerrainData.Patches[PatchIndex].Top->CurrentLOD &&
				(vX % (1 << TerrainData.Patches[PatchIndex].Top->CurrentLOD)) != 0 )
			{
				vX -= vX % (1 << TerrainData.Patches[PatchIndex].Top->CurrentLOD);
			}
		}
		else
		if (vZ == (u32)TerrainData.CalcPatchSize) // bottom border

		{
			if (TerrainData.Patches[PatchIndex].Bottom &&
				TerrainData.Patches[PatchIndex].CurrentLOD < TerrainData.Patches[PatchIndex].Bottom->CurrentLOD &&
				(vX % (1 << TerrainData.Patches[PatchIndex].Bottom->CurrentLOD)) != 0)
			{
				vX -= vX % (1 << TerrainData.Patches[PatchIndex].Bottom->CurrentLOD);
			}
		}

		// left border

		if (vX == 0)
		{
			if (TerrainData.Patches[PatchIndex].Left &&
				TerrainData.Patches[PatchIndex].CurrentLOD < TerrainData.Patches[PatchIndex].Left->CurrentLOD &&
				(vZ % (1 << TerrainData.Patches[PatchIndex].Left->CurrentLOD)) != 0)
			{
				vZ -= vZ % (1 << TerrainData.Patches[PatchIndex].Left->CurrentLOD);
			}
		}
		else
		if (vX == (u32)TerrainData.CalcPatchSize) // right border

		{
			if (TerrainData.Patches[PatchIndex].Right &&
				TerrainData.Patches[PatchIndex].CurrentLOD < TerrainData.Patches[PatchIndex].Right->CurrentLOD &&
				(vZ % (1 << TerrainData.Patches[PatchIndex].Right->CurrentLOD)) != 0)
			{
				vZ -= vZ % (1 << TerrainData.Patches[PatchIndex].Right->CurrentLOD);
			}
		}

		if (vZ >= (u32)TerrainData.PatchSize)
			vZ = TerrainData.CalcPatchSize;

		if (vX >= (u32)TerrainData.PatchSize)
			vX = TerrainData.CalcPatchSize;

		return (vZ + ((TerrainData.CalcPatchSize) * PatchZ)) * TerrainData.Size +
			(vX + ((TerrainData.CalcPatchSize) * PatchX));
	}


	//! smooth the terrain

	void CTerrainSceneNode::smoothTerrain(IDynamicMeshBuffer* mb, s32 smoothFactor)
	{
		for (s32 run = 0; run < smoothFactor; ++run)
		{
			s32 yd = TerrainData.Size;
			for (s32 y = 1; y < TerrainData.Size - 1; ++y)
			{
				for (s32 x = 1; x < TerrainData.Size - 1; ++x)
				{
					mb->getVertexBuffer()[x + yd].Pos.Y =
						(mb->getVertexBuffer()[x-1 + yd].Pos.Y + //left

						mb->getVertexBuffer()[x+1 + yd].Pos.Y + //right

						mb->getVertexBuffer()[x   + yd - TerrainData.Size].Pos.Y + //above

						mb->getVertexBuffer()[x   + yd + TerrainData.Size].Pos.Y) * 0.25f; //below

				}
				yd += TerrainData.Size;
			}
		}
	}


	//! calculate smooth normals

	void CTerrainSceneNode::calculateNormals(IDynamicMeshBuffer* mb)
	{
		s32 count;
		core::vector3df a, b, c, t;

		for (s32 x=0; x<TerrainData.Size; ++x)
		{
			for (s32 z=0; z<TerrainData.Size; ++z)
			{
				count = 0;
				core::vector3df normal;

				// top left

				if (x>0 && z>0)
				{
					a = mb->getVertexBuffer()[(x-1)*TerrainData.Size+z-1].Pos;
					b = mb->getVertexBuffer()[(x-1)*TerrainData.Size+z].Pos;
					c = mb->getVertexBuffer()[x*TerrainData.Size+z].Pos;
					b -= a;
					c -= a;
					t = b.crossProduct(c);
					t.normalize();
					normal += t;

					a = mb->getVertexBuffer()[(x-1)*TerrainData.Size+z-1].Pos;
					b = mb->getVertexBuffer()[x*TerrainData.Size+z-1].Pos;
					c = mb->getVertexBuffer()[x*TerrainData.Size+z].Pos;
					b -= a;
					c -= a;
					t = b.crossProduct(c);
					t.normalize();
					normal += t;

					count += 2;
				}

				// top right

				if (x>0 && z<TerrainData.Size-1)
				{
					a = mb->getVertexBuffer()[(x-1)*TerrainData.Size+z].Pos;
					b = mb->getVertexBuffer()[(x-1)*TerrainData.Size+z+1].Pos;
					c = mb->getVertexBuffer()[x*TerrainData.Size+z+1].Pos;
					b -= a;
					c -= a;
					t = b.crossProduct(c);
					t.normalize();
					normal += t;

					a = mb->getVertexBuffer()[(x-1)*TerrainData.Size+z].Pos;
					b = mb->getVertexBuffer()[x*TerrainData.Size+z+1].Pos;
					c = mb->getVertexBuffer()[x*TerrainData.Size+z].Pos;
					b -= a;
					c -= a;
					t = b.crossProduct(c);
					t.normalize();
					normal += t;

					count += 2;
				}

				// bottom right

				if (x<TerrainData.Size-1 && z<TerrainData.Size-1)
				{
					a = mb->getVertexBuffer()[x*TerrainData.Size+z+1].Pos;
					b = mb->getVertexBuffer()[x*TerrainData.Size+z].Pos;
					c = mb->getVertexBuffer()[(x+1)*TerrainData.Size+z+1].Pos;
					b -= a;
					c -= a;
					t = b.crossProduct(c);
					t.normalize();
					normal += t;

					a = mb->getVertexBuffer()[x*TerrainData.Size+z+1].Pos;
					b = mb->getVertexBuffer()[(x+1)*TerrainData.Size+z+1].Pos;
					c = mb->getVertexBuffer()[(x+1)*TerrainData.Size+z].Pos;
					b -= a;
					c -= a;
					t = b.crossProduct(c);
					t.normalize();
					normal += t;

					count += 2;
				}

				// bottom left

				if (x<TerrainData.Size-1 && z>0)
				{
					a = mb->getVertexBuffer()[x*TerrainData.Size+z-1].Pos;
					b = mb->getVertexBuffer()[x*TerrainData.Size+z].Pos;
					c = mb->getVertexBuffer()[(x+1)*TerrainData.Size+z].Pos;
					b -= a;
					c -= a;
					t = b.crossProduct(c);
					t.normalize();
					normal += t;

					a = mb->getVertexBuffer()[x*TerrainData.Size+z-1].Pos;
					b = mb->getVertexBuffer()[(x+1)*TerrainData.Size+z].Pos;
					c = mb->getVertexBuffer()[(x+1)*TerrainData.Size+z-1].Pos;
					b -= a;
					c -= a;
					t = b.crossProduct(c);
					t.normalize();
					normal += t;

					count += 2;
				}

				if (count != 0)
				{
					normal.normalize();
				}
				else
				{
					normal.set(0.0f, 1.0f, 0.0f);
				}

				mb->getVertexBuffer()[x * TerrainData.Size + z].Normal = normal;
			}
		}
	}


	//! create patches, stuff that needs to be done only once for patches goes here.

	void CTerrainSceneNode::createPatches()
	{
		TerrainData.PatchCount = (TerrainData.Size - 1) / (TerrainData.CalcPatchSize);

		if (TerrainData.Patches)
			delete [] TerrainData.Patches;

		TerrainData.Patches = new SPatch[TerrainData.PatchCount * TerrainData.PatchCount];
	}


	//! used to calculate the internal STerrainData structure both at creation and after scaling/position calls.

	void CTerrainSceneNode::calculatePatchData()
	{
		// Reset the Terrains Bounding Box for re-calculation

		TerrainData.BoundingBox = core::aabbox3df(999999.9f, 999999.9f, 999999.9f, -999999.9f, -999999.9f, -999999.9f);

		for (s32 x = 0; x < TerrainData.PatchCount; ++x)
		{
			for (s32 z = 0; z < TerrainData.PatchCount; ++z)
			{
				const s32 index = x * TerrainData.PatchCount + z;
				TerrainData.Patches[index].CurrentLOD = 0;

				// For each patch, calculate the bounding box (mins and maxes)

				TerrainData.Patches[index].BoundingBox = core::aabbox3df(999999.9f, 999999.9f, 999999.9f,
					-999999.9f, -999999.9f, -999999.9f);

				for (s32 xx = x*(TerrainData.CalcPatchSize); xx <= (x + 1) * TerrainData.CalcPatchSize; ++xx)
					for (s32 zz = z*(TerrainData.CalcPatchSize); zz <= (z + 1) * TerrainData.CalcPatchSize; ++zz)
						TerrainData.Patches[index].BoundingBox.addInternalPoint(RenderBuffer->getVertexBuffer()[xx * TerrainData.Size + zz].Pos);


				// Reconfigure the bounding box of the terrain as a whole

				TerrainData.BoundingBox.addInternalBox(TerrainData.Patches[index].BoundingBox);

				// get center of Patch

				TerrainData.Patches[index].Center = TerrainData.Patches[index].BoundingBox.getCenter();

				// Assign Neighbours

				// Top

				if (x > 0)
					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];
				else
					TerrainData.Patches[index].Bottom = 0;

				// Left

				if (z > 0)
					TerrainData.Patches[index].Left = &TerrainData.Patches[x * TerrainData.PatchCount + z - 1];
				else
					TerrainData.Patches[index].Left = 0;

				// Right

				if (z < TerrainData.PatchCount - 1)
					TerrainData.Patches[index].Right = &TerrainData.Patches[x * TerrainData.PatchCount + z + 1];
				else
					TerrainData.Patches[index].Right = 0;
			}
		}

		// get center of Terrain

		TerrainData.Center = TerrainData.BoundingBox.getCenter();

		// if the default rotation pivot is still being used, update it.

		if (UseDefaultRotationPivot)
		{
			TerrainData.RotationPivot = TerrainData.Center;
		}
	}


	//! used to calculate or recalculate the distance thresholds

	void CTerrainSceneNode::calculateDistanceThresholds(bool scalechanged)
	{
		// Only update the LODDistanceThreshold if it's not manually changed

		if (!OverrideDistanceThreshold)
		{
			TerrainData.LODDistanceThreshold.set_used(0);
			// Determine new distance threshold for determining what LOD to draw patches at

			TerrainData.LODDistanceThreshold.reallocate(TerrainData.MaxLOD);

			const f64 size = TerrainData.PatchSize * TerrainData.PatchSize *
					TerrainData.Scale.X * TerrainData.Scale.Z;
			for (s32 i=0; i<TerrainData.MaxLOD; ++i)
			{
				TerrainData.LODDistanceThreshold.push_back(size * ((i+1+ i / 2) * (i+1+ i / 2)));
			}
		}
	}


	void CTerrainSceneNode::setCurrentLODOfPatches(s32 lod)
	{
		const s32 count = TerrainData.PatchCount * TerrainData.PatchCount;
		for (s32 i=0; i< count; ++i)
			TerrainData.Patches[i].CurrentLOD = lod;
	}


	void CTerrainSceneNode::setCurrentLODOfPatches(const core::array<s32>& lodarray)
	{
		const s32 count = TerrainData.PatchCount * TerrainData.PatchCount;
		for (s32 i=0; i<count; ++i)
			TerrainData.Patches[i].CurrentLOD = lodarray[i];
	}


	//! Gets the height

	f32 CTerrainSceneNode::getHeight(f32 x, f32 z) const
	{
		if (!Mesh->getMeshBufferCount())
			return 0;

		f32 height = -999999.9f;

		core::matrix4 rotMatrix;
		rotMatrix.setRotationDegrees(TerrainData.Rotation);
		core::vector3df pos(x, 0.0f, z);
		rotMatrix.rotateVect(pos);
		pos -= TerrainData.Position;
		pos /= TerrainData.Scale;

		s32 X(core::floor32(pos.X));
		s32 Z(core::floor32(pos.Z));

		if (X >= 0 && X < TerrainData.Size-1 &&
				Z >= 0 && Z < TerrainData.Size-1)
		{
			const video::S3DVertex2TCoords* Vertices = (const video::S3DVertex2TCoords*)Mesh->getMeshBuffer(0)->getVertices();
			const core::vector3df& a = Vertices[X * TerrainData.Size + Z].Pos;
			const core::vector3df& b = Vertices[(X + 1) * TerrainData.Size + Z].Pos;
			const core::vector3df& c = Vertices[X * TerrainData.Size + (Z + 1)].Pos;
			const core::vector3df& d = Vertices[(X + 1) * TerrainData.Size + (Z + 1)].Pos;

			// offset from integer position

			const f32 dx = pos.X - X;
			const f32 dz = pos.Z - Z;

			if (dx > dz)
				height = a.Y + (d.Y - b.Y)*dz + (b.Y - a.Y)*dx;
			else
				height = a.Y + (d.Y - c.Y)*dx + (c.Y - a.Y)*dz;

			height *= TerrainData.Scale.Y;
			height += TerrainData.Position.Y;
		}

		return height;
	}


	//! Writes attributes of the scene node.

	void CTerrainSceneNode::serializeAttributes(io::IAttributes* out,
				io::SAttributeReadWriteOptions* options) const
	{
		ISceneNode::serializeAttributes(out, options);

		out->addString("Heightmap", HeightmapFile.c_str());
		out->addFloat("TextureScale1", TCoordScale1);
		out->addFloat("TextureScale2", TCoordScale2);
	}


	//! Reads attributes of the scene node.

	void CTerrainSceneNode::deserializeAttributes(io::IAttributes* in,
			io::SAttributeReadWriteOptions* options)
	{
		io::path newHeightmap = in->getAttributeAsString("Heightmap");
		f32 tcoordScale1 = in->getAttributeAsFloat("TextureScale1");
		f32 tcoordScale2 = in->getAttributeAsFloat("TextureScale2");

		// set possible new heightmap


		if (newHeightmap.size() != 0 && newHeightmap != HeightmapFile)
		{
			io::IReadFile* file = FileSystem->createAndOpenFile(newHeightmap.c_str());
			if (file)
			{
				loadHeightMap(file, video::SColor(255,255,255,255), 0);
				file->drop();
			}
			else
				os::Printer::log("could not open heightmap", newHeightmap.c_str());
		}

		// set possible new scale


		if (core::equals(tcoordScale1, 0.f))
			tcoordScale1 = 1.0f;

		if (core::equals(tcoordScale2, 0.f))
			tcoordScale2 = 1.0f;

		if (!core::equals(tcoordScale1, TCoordScale1) ||
			!core::equals(tcoordScale2, TCoordScale2))
		{
			scaleTexture(tcoordScale1, tcoordScale2);
		}

		ISceneNode::deserializeAttributes(in, options);
	}


	//! Creates a clone of this scene node and its children.

	ISceneNode* CTerrainSceneNode::clone(ISceneNode* newParent, ISceneManager* newManager)
	{
		if (!newParent)
			newParent = Parent;
		if (!newManager)
			newManager = SceneManager;

		CTerrainSceneNode* nb = new CTerrainSceneNode(
			newParent, newManager, FileSystem, ID,
			4, ETPS_17, getPosition(), getRotation(), getScale());

		nb->cloneMembers(this, newManager);

		// instead of cloning the data structures, recreate the terrain.

		// (temporary solution)


		// load file


		io::IReadFile* file = FileSystem->createAndOpenFile(HeightmapFile.c_str());
		if (file)
		{
			nb->loadHeightMap(file, video::SColor(255,255,255,255), 0);
			file->drop();
		}

		// scale textures


		nb->scaleTexture(TCoordScale1, TCoordScale2);

		// copy materials


		for (unsigned int m = 0; m<Mesh->getMeshBufferCount(); ++m)
		{
			if (nb->Mesh->getMeshBufferCount()>m &&
				nb->Mesh->getMeshBuffer(m) &&
				Mesh->getMeshBuffer(m))
			{
				nb->Mesh->getMeshBuffer(m)->getMaterial() =
					Mesh->getMeshBuffer(m)->getMaterial();
			}
		}

		nb->RenderBuffer->getMaterial() = RenderBuffer->getMaterial();

		// finish


		if ( newParent )
			nb->drop();
		return nb;
	}

} // end namespace scene

} // end namespace irr



Options Liens officiels Caractéristiques Statistiques Communauté
Corrections
irrlicht
irrklang
irredit
irrxml
xhtml 1.0
css 2.1
Propulsé par FluxBB
Traduit par FluxBB.fr
Analysé par
880 membres
1424 sujets
11113 messages
Dernier membre inscrit: mandrifidy
36 invités en ligne
Aucun membre connecté
RSS Feed