I have been trying to find an easy to use terrain engine but couldn't find one so i made one.
It works wit 256x256 heightmaps. (You can adapt it to 128x128... by using the ratio 240/256 for the size but we will get to it later).
Basically the engine is going to make a virtual grid :
And is going to get the distance between your camera and the map coordinates. If the distance is shorter than the distance you have defined, it will load the grid, if the distance is bigger it will remove the map. If you go back it will reload the map.
You will only have to rename your heightmaps to gridX.X.png (X.X being the virtual coordinates cf : picture above) and same for the color that you will rename colorX.X.png.
Changelog :
v0.1 : Added physics
v0.2 working : adding PhysX wrapper.
Here is the code :
CTerrain.cpp
Code: Select all
/* Author : Y
Date : 08/12/2008
Copyright Naruto : Tatakai 2008 */
#include "CTerrain.h"
#define DISTANCE 8000 /// Distance from which the terrain will be loaded or removed if bigger
#define SCALEXZ 20
#define SCALEY 2
#define RATIO 240 /// for 256 I think that it would be 120 for 128
#define SIZE 4800 /// multiply SCALEXZ by RATIO
CTerrain::CTerrain()
{
smgr = Graphic::GetSingleton()->GetSmgrDevice();
driver = Graphic::GetSingleton()->GetDriverDevice();
m_count = 16;
for(int i = 0;i < 4; i++)
{
for(int j = 0;j < 4; j++)
{
char* tmp = new char[100];
//sprintf(tmp,"Media2/grid%ld.%ld.png",i,j);
char* tmp2 = new char[100];
//sprintf(tmp2,"Media2/color%ld.%ld.png",i,j);
sprintf(tmp2,"Media2/color0.0.png");
sprintf(tmp,"Media2/grid0.0.png");
TerrainInfo* Tmp = new TerrainInfo;
Tmp->loaded = false;
Tmp->X = i*SIZE;
Tmp->Z = j*SIZE;
Tmp->height = tmp;
Tmp->color = tmp2;
m_terrainInfo.push_back(Tmp);
}
}
}
CTerrain::~CTerrain()
{
int i = 0;
while(i < m_count)
{
delete m_terrainInfo.at(i)->height;
delete m_terrainInfo.at(i)->color;
delete m_terrainInfo.at(i);
i++;
}
}
void CTerrain::render()
{
int distance,X,Z = 0;
int id = 0;
vector3df camPos = Graphic::GetSingleton()->GetCameraSceneNode()->getAbsolutePosition();
vector3df nodePos = Graphic::GetSingleton()->GetMainSceneNode()->getPosition();
while(id < m_count)
{
X = camPos.X - m_terrainInfo.at(id)->X;
Z = camPos.Z - m_terrainInfo.at(id)->Z;
distance = sqrt(pow(X,2)+pow(Z,2));
if(distance < DISTANCE && m_terrainInfo.at(id)->loaded == false)
loadGrid(id);
if(distance > DISTANCE && m_terrainInfo.at(id)->loaded == true)
deleteGrid(id);
if(nodePos.X < m_terrainInfo.at(id)->X+4800 && nodePos.X > m_terrainInfo.at(id)->X &&
nodePos.Z < m_terrainInfo.at(id)->Z+4800 && nodePos.Z > m_terrainInfo.at(id)->Z)
{
setAnimator(id);
}
else if(m_terrainInfo.at(id)->anim == true)
m_terrainInfo.at(id)->anim = false;
id++;
}
}
void CTerrain::loadGrid(int id)
{
scene::ITerrainSceneNode* terrain = smgr->addTerrainSceneNode(
m_terrainInfo.at(id)->height,
0,
-1,
core::vector3df(m_terrainInfo.at(id)->X, 0.f, m_terrainInfo.at(id)->Z),
core::vector3df(0.f, 0.f, 0.f),
core::vector3df(SCALEXZ, SCALEY, SCALEXZ),
video::SColor ( 255, 255, 255, 255 ),
50,
scene::ETPS_17,
4
);
if(terrain)
{
terrain->setMaterialFlag(video::EMF_FOG_ENABLE, true);
terrain->setMaterialFlag(video::EMF_LIGHTING, false);
m_terrainInfo.at(id)->colorTexture = driver->getTexture(m_terrainInfo.at(id)->color);
terrain->setMaterialTexture(0, m_terrainInfo.at(id)->colorTexture);
terrain->setMaterialTexture(1, driver->getTexture("Media2/detail.jpg"));
terrain->setMaterialType(video::EMT_DETAIL_MAP);
terrain->scaleTexture(1.0f, 20.0f);
m_terrainInfo.at(id)->loaded = true;
m_terrain[id] = terrain;
}
}
void CTerrain::deleteGrid(int id)
{
m_terrain[id]->remove();
m_terrainInfo.at(id)->loaded = false;
}
void CTerrain::setAnimator(int id)
{
vector3df nodePos = YOURNODE>getPosition();
nodePos.Y = (m_terrain[id]->getHeight(nodePos.X,nodePos.Z) + 25);
YOURNODE->setPosition(nodePos);
}
Code: Select all
#include <irrlicht.h>
#include <iostream>
struct TerrainInfo
{
int X;
int Z;
c8* height;
c8* color;
bool loaded;
bool anim;
ITexture* colorTexture;
};
class CTerrain {
public:
CTerrain();
~CTerrain();
void render();
void loadGrid(int);
void deleteGrid(int);
void setAnimator(int);
private:
vector<TerrainInfo*> m_terrainInfo;
std::map<int,ITerrainSceneNode*> m_terrain;
IVideoDriver* driver;
ISceneManager* smgr;
int m_count;
};
Pretty simple :
You just have to find a way to send your node and camera to the class ( I use a singleton so I don't have that problem ) You have to replace YOURCAMERA by your camera's pointer and YOURNODE by your node's pointer.
Then it's easy just add the line :
CTerrain* myTerrain = new CTerrain;
somewhere in your code.
And
myTerrain->render()
in your render loop.
And
delete myTerrain;
after your drop function.
But I repeat that you have to send a pointer to the class you can modify the constructor and make a pointer...
If you want to modify it do as you wish !
If you need help or If you have a suggestion feel free to reply to this post.
PS : The terrain is removed but 5mo of memory is still allocated after removing the terrain. It's not a big deal since those 5mo are deleted when you close your app.
If you want to change region just clean the smgr to free the memory at the same time