How to use the terrain engine libmini with Irrlicht
@UzMaN, the node is work in progress and the detailtexture parameter in the create method is currently not used. Feel free to extend the node as You like I'm sure every one will appreciate it. You might want to check out the method "BeginFan" and "FanVertex"
@brcolow, sorry I can't help You with this. From what I see I think You're having also a problem with linking debug with nondebug code.
@brcolow, sorry I can't help You with this. From what I see I think You're having also a problem with linking debug with nondebug code.
-
- Posts: 199
- Joined: Sun Aug 24, 2003 5:47 pm
- Location: Germany
Hi community:
with zolas permission, i put his code from this thread for download to
http://zenprogramming.tripod.com in downloadsection
Hi UzMaN and Guest,
yes sure please show us this new version!
with zolas permission, i put his code from this thread for download to
http://zenprogramming.tripod.com in downloadsection
Hi UzMaN and Guest,
yes sure please show us this new version!
sorry the Guest above was me,so final code is just like this
CLMTerrainSceneNode.h
only SMeshBuffer and S3DVertex have been changed;
CLMTerrainSceneNode.cpp
changes in fanvertex and create
CLMTerrainSceneNode.h
only SMeshBuffer and S3DVertex have been changed;
Code: Select all
#ifndef __CLMTerrainSceneNode_h__
#define __CLMTerrainSceneNode_h__
#include <irrlicht.h>
#include "ministub.hpp"
#pragma comment(lib, "libMini.lib")
namespace irr
{
namespace scene
{
using namespace core;
using namespace video;
class CLMTerrainSceneNode : public ISceneNode
{
protected:
//////////////////////////////////////////////////////////////////
// globals for libMini callbacks
//////////////////////////////////////////////////////////////////
// prepares a buffer if the buffer is nonempty draws and then empties the buffer
// this function needs to be called after stub->draw one more time
static void _BeginFan();
// fills the buffer with vertecis
static void _FanVertex(float i,float y,float j);
// pointer to the scenenode
static CLMTerrainSceneNode* _self;
//////////////////////////////////////////////////////////////////
ministub *stub;
IVideoDriver* driver;
SMeshBufferLightMap buffer; // a buffer for fan vertecis
S3DVertex2TCoords vertex; // a default vertex
// draw the meshbuffer
void BeginFan();
// add point (i,y,j) to the meshbuffer
void FanVertex(float i,float y,float j);
// point spacing
float dim;
// height scaling factor
float scale;
// pixel width of the heightmap
s32 size;
// resolution
float res;
// field of view
float fovy;
// aspect ratio
float aspect;
// near value
float nearp;
// far value
float farp;
// the height field
float *hfield;
// material
SMaterial Material;
// invere of absolute transform to calculate hf coordinates from world coordinates
matrix4 invAbsoluteTransform;
// current camera pos in heightfield coordinate
vector3df pos;
// current camera target in heightfield coordinate
vector3df tgt;
// current camer up in heightfield coordinate
vector3df up;
// Bounding box of the heightfield
mutable aabbox3df Box;
public:
// constructor
CLMTerrainSceneNode(ISceneNode* parent, ISceneManager* mgr, s32 id);
// destructor
virtual ~CLMTerrainSceneNode();
/*!
\brief create the terrain
\param heightmap, image with height values
\param texture, texture used for texturing the terrain
\param detailmap, not used
\param gridPointSpacing, spacing between the gridpoints of the heightfield
\param heightScale, scaling value for image height values
\param resolution, 1000.0f ... 1000000.0f
*/
bool create(IImage* heightmap, ITexture* texture, ITexture* detailmap,
f32 gridPointSpacing, f32 heightScale, f32 resolution);
// prerender register node for rendering and call child nodes for registering
virtual void OnPreRender();
// render the node
virtual void render() ;
// post render, animation etc.
virtual void OnPostRender(u32 timeMs);
// get the material with given number
virtual SMaterial& getMaterial(s32 i)
{
return Material;
}
// get the boundingbox of the node
const aabbox3df& getBoundingBox() const
{
AbsoluteTransformation.transformBox(Box);
return Box;
}
// get the inverse of the absolute transformation
matrix4& getInvAbsoluteTransformation(){ return invAbsoluteTransform; }
// get the heightfield data
f32* getHeightField() { return hfield; }
// get the length of the square size of the heightfield
s32 getHeightFieldSize() { return size; }
// spacing between the sample points
f32 getPointSpacing() { return dim; }
// scaling factor to modify the height values
f32 getHeightScale() { return scale; }
// get height at grid point (i,j)
f32 getHeight(int i,int j);
// get height at heightfield coordinate (x,z)
// transformation is inv=AbsoluteTransform.getInverse();
// inv.transformVect(pos);
f32 getHeight(float x,float z);
// get fog height at heightfield coordinate (x,z)
// transformation is inv=AbsoluteTransform.getInverse();
// inv.transformVect(pos);
f32 getFogHeight(float x,float z);
// get normal at heightfield coordinate (x,z)
// transformation is inv=AbsoluteTransform.getInverse();
// inv.transformVect(pos);
vector3df getNormal(float x,float z);
};
}
}
#endif
changes in fanvertex and create
Code: Select all
#include "CLMTerrainSceneNode.h"
#include <stdio.h>
namespace irr
{
namespace scene
{
using namespace core;
using namespace video;
// self pointer for callback
CLMTerrainSceneNode* CLMTerrainSceneNode::_self=0;
// prepares a buffer if the buffer is nonempty draws and then empties the buffer
// this function needs to be called after stub->draw one more time
void CLMTerrainSceneNode::_BeginFan()
{
if(_self) _self->BeginFan();
}
// fills the buffer with vertecis
void CLMTerrainSceneNode::_FanVertex(float i,float y,float j)
{
if(_self) _self->FanVertex(i,y,j);
}
//////////////////////////////////////////////////////////////////////////
// constructor
CLMTerrainSceneNode::CLMTerrainSceneNode(ISceneNode* parent, ISceneManager* mgr, s32 id):
ISceneNode(parent,mgr,id)
{
driver=SceneManager->getVideoDriver();
stub=0;
hfield=0;
AbsoluteTransformation.getInverse(invAbsoluteTransform);
}
CLMTerrainSceneNode::~CLMTerrainSceneNode()
{
if(stub) delete stub;
if(hfield) delete [] hfield;
}
//! loads the terrain
bool CLMTerrainSceneNode::create(IImage* heightmap, ITexture* texture, ITexture* detailmap,
f32 gridPointSpacing, f32 heightScale, f32 resolution)
{
if(stub) delete stub;
if(hfield) delete [] hfield;
res=resolution;
dim=gridPointSpacing;
scale=heightScale;
size = heightmap->getDimension().Width;
hfield = new float[size*size];
vector3df ext(size*dim,0,size*dim);
f32 hmin=10000000;
f32 hmax=-10000000;
for (int i=0; i<size; i++)
for (int j=0; j<size; j++)
{
hfield[i+j*size] = heightmap->getPixel(i,size-1-j).getRed()*scale;
if(hmin>hfield[i+j*size]) hmin=hfield[i+j*size];
if(hmax<hfield[i+j*size]) hmax=hfield[i+j*size];
}
Box.MinEdge.set(-ext.X/2.0f,hmin,-ext.Z/2.0f);
Box.MaxEdge.set(ext.X/2.0f,hmax,ext.Z/2.0f);
/*
stub=new ministub(hfield,
&size,&dim,scale,1.0f,
CLMTerrainSceneNode::_BeginFan,
CLMTerrainSceneNode::_FanVertex,
0,0,0);
*/
stub=new ministub(hfield,
&size,&dim,1.0f,1.0f,
CLMTerrainSceneNode::_BeginFan,
CLMTerrainSceneNode::_FanVertex,
0,0,0);
vertex.Normal.set(0,1,0);
vertex.Color.set(128,128,128,128);
Material.Texture1=texture;
Material.Texture2=detailmap;
// try also other material types
Material.MaterialType=EMT_LIGHTMAP_M2;
return true;
}
// prepares a buffer if the buffer is nonempty draws and then empties the buffer
// this function needs to be called after stub->draw one more time
void CLMTerrainSceneNode::BeginFan()
{
if(buffer.Vertices.size()>0 && driver)
{
driver->drawIndexedTriangleFan(buffer.Vertices.const_pointer(),buffer.Vertices.size(),buffer.Indices.const_pointer(),buffer.Indices.size()-2);
buffer.Vertices.set_used(0);
buffer.Indices.set_used(0);
}
}
// fills the buffer with vertecis
void CLMTerrainSceneNode::FanVertex(float i,float y,float j)
{
int detail=256;
//vertex.Pos.set(dim*i-size/2*dim, y*scale, size/2*dim-dim*j);
vertex.Pos.set(dim*i-size/2*dim, y, size/2*dim-dim*j);
//vertex.TCoords.set(1.0f-(float)i/(size-1),1.0f-(float)j/(size-1));
vertex.TCoords.set(((float)i/(size-1)),(float)j/(size-1));
float t2i=((int)i%((size)/detail))*
((float)detail/(size-1));
float t2j=((int)j%((size)/detail))*
((float)detail/(size-1));
int offi=(int)i/(size/detail);
int offj=(int)j/(size/detail);
vertex.TCoords2.set(offi+t2i,offj+t2j);
buffer.Indices.push_back(buffer.Indices.size());
buffer.Vertices.push_back(vertex);
}
void CLMTerrainSceneNode::OnPreRender()
{
if(IsVisible)
SceneManager->registerNodeForRendering(this,SNRT_DEFAULT);
ISceneNode::OnPreRender();
AbsoluteTransformation.getInverse(invAbsoluteTransform);
}
void CLMTerrainSceneNode::render()
{
ICameraSceneNode* camera=SceneManager->getActiveCamera();
driver->setTransform(video::ETS_WORLD, AbsoluteTransformation);
if(stub && camera)
{
aspect = camera->getAspectRatio();
fovy = f32(GRAD_PI)*camera->getFOV();
nearp = camera->getNearValue();
farp = camera->getFarValue();
pos.set(camera->getPosition());
tgt.set(camera->getTarget());
up.set(camera->getUpVector());
invAbsoluteTransform.transformVect(pos);
invAbsoluteTransform.transformVect(tgt);
invAbsoluteTransform.transformVect(up);
vector3df dx=tgt-pos;
if(dx.getLengthSQ()==0.0)
{
dx.Z=-1.0f;
}
// make callbacks to this scenenode :)
_self=this;
driver->setMaterial(Material);
stub->draw(res,
pos.X,pos.Y,pos.Z,
dx.X,dx.Y,dx.Z,
up.X,up.Y,up.Z,
2.0f*fovy,
aspect,
nearp,
farp);
BeginFan();
if(DebugDataVisible)
{
SMaterial mat;
mat.Lighting=false;
mat.Wireframe=true;
mat.FogEnable=false;
mat.EmissiveColor=SColor(255,0,255,0);
mat.AmbientColor=SColor(255,0,255,0);
mat.DiffuseColor=SColor(255,0,255,0);
mat.Texture1=0;
mat.Texture2=0;
driver->setMaterial(mat);
driver->draw3DBox(Box, SColor(255,0,0,255));
stub->draw(res,
pos.X,pos.Y,pos.Z,
dx.X,dx.Y,dx.Z,
up.X,up.Y,up.Z,
2.0f*fovy,
aspect,
nearp,
farp);
BeginFan();
}
}
}
void CLMTerrainSceneNode::OnPostRender(u32 timeMs)
{
ISceneNode::OnPostRender(timeMs);
}
f32 CLMTerrainSceneNode::getHeight(int i,int j)
{
if(stub)
return stub->getheight(i,j);
else
return 0;
}
f32 CLMTerrainSceneNode::getHeight(float x,float z)
{
if(stub)
return stub->getheight(x,z);
else
return 0;
}
f32 CLMTerrainSceneNode::getFogHeight(float x,float z)
{
if(stub)
return stub->getfogheight(x,z);
else
return 0;
}
vector3df CLMTerrainSceneNode::getNormal(float x,float z)
{
vector3df n(0,1,0);
if(stub)
{
f32 nx,ny,nz;
stub->getnormal(x,z,&nx,&ny,&nz);
n.set(nx,ny,nz);
}
return n;
}
}
}
-
- Posts: 199
- Joined: Sun Aug 24, 2003 5:47 pm
- Location: Germany
You can did it by hand, like this:
You must calculate values (minPos, maxPos)
(this you can do with easily in function AddMeshBuffer)
Code: Select all
// global declaration
core::vector2df minPos, maxPos;
//! loads the terrain
bool CLMTerrainSceneNode::create(IImage* heightmap, ITexture* texture, ITexture* detailmap,
f32 gridPointSpacing, f32 heightScale, f32 resolution)
{
...
maxPos.X =-1000000;
maxPos.Y =-1000000;
minPos.X = 1000000;
minPos.Y = 1000000;
bDrawToMesh=true;
mesh = new SMesh();
_self=this;
stub->draw(
res, // resolution
0,2.0f*hmax,0, // pos
0,-1,0, // dir
0,0,1, // up
-ext.X, // OTHO fovy
1, // aspect
1, // near
ext.X);
BeginFan();
bDrawToMesh=false;
...
f32 width, length;
width = fabs(maxPos.X - minPos.X);
length = fabs(maxPos.Y - minPos.Y);
s32 collis_buf_size=128;
f32 currX, currZ;
vid::S3DVertex vert;
// this mesh buffer we are using for collision response
SMeshBuffer *collisionBuffer=new SMeshBuffer();
dX = width / collis_buf_size;
dZ = length / collis_buf_size;
for (s32 x=0; x<collis_buf_size; x++)
{
for (s32 z=0; z<collis_buf_size; z++)
{
currX = minPos.X+x*dX;
currZ = minPos.Y+z*dZ;
vert.Pos = core::vector3df(currX , getHeight(currX,currZ), currZ);
vert.Normal = core::vector3df(0,1,0);
vert.Color.set(255,255,255,255);
collisionBuffer->Vertices.push_back(vert);
}
}
s32 indA, indB, indC;
for (x=0; x<collis_buf_size-1; x++)
{
for (s32 z=0; z<collis_buf_size_1; z++)
{
indA = x*collis_buf_size+z;
indB = (x+1)*collis_buf_size+z+1;
indC = (x+1)*collis_buf_size+z;
collisionBuffer->Indices.push_back(indA);
collisionBuffer->Indices.push_back(indB);
collisionBuffer->Indices.push_back(indC);
indA = x*collis_buf_size+z;
indB = x*collis_buf_size+z+1;
indC = (x+1)*collis_buf_size+z+1;
collisionBuffer->Indices.push_back(indA);
collisionBuffer->Indices.push_back(indB);
collisionBuffer->Indices.push_back(indC);
}
}
mesh->addMeshBuffer(collisionBuffer);
collisionBuffer->drop();
....
(this you can do with easily in function AddMeshBuffer)
Code: Select all
// adds the meshbuffer to the mesh
void CLMTerrainSceneNode::addMeshBufferToMesh()
{
if(buffer.Vertices.size()>0)
{
//////////////////////////////////////////////
////////// you don't need this any more///////
/*
// copy the
SMeshBuffer* mb=new SMeshBuffer();
*/
////////// you don't need this any more///////
//////////////////////////////////////////////
u32 n=buffer.Vertices.size();
for(u32 i=0; i<n; i++)
{
//////////////////////////////////////////////
////////// you don't need this any more///////
/*
// add vertex
mb->Vertices.push_back(buffer.Vertices[i]);
*/
////////// you don't need this any more///////
//////////////////////////////////////////////
////////////////////////////////////////
////////// add this ////////////////////
if (buffer.Vertices[i].Pos.X < minPos.X)
minPos.X = buffer.Vertices[i].Pos.X;
if (buffer.Vertices[i].Pos.Z < minPos.Y)
minPos.Y = buffer.Vertices[i].Pos.Z;
if (buffer.Vertices[i].Pos.X > maxPos.X)
maxPos.X = buffer.Vertices[i].Pos.X;
if (buffer.Vertices[i].Pos.Z > maxPos.Y)
maxPos.Y = buffer.Vertices[i].Pos.Z;
////////// add this ////////////////////
////////////////////////////////////////
//////////////////////////////////////////////
////////// you don't need this any more///////
/*
if(i>1)
{
mb->Indices.push_back(0);
mb->Indices.push_back(i-1);
mb->Indices.push_back(i);
}
*/
////////// you don't need this any more///////
//////////////////////////////////////////////
}
//////////////////////////////////////////////
////////// you don't need this any more///////
/*
mb->Indices.push_back(0);
mb->Indices.push_back(n-1);
mb->Indices.push_back(1);
mesh->addMeshBuffer(mb);
mb->drop();
*/
////////// you don't need this any more///////
//////////////////////////////////////////////
}
Another method to get the right resolution for the mesh reternud by getMesh() would be to give the create method an additional parameter float staticRes which you use for drawing the mesh.
change the method signature in the header file accordingly.
You might keep in mind that a finer collision mesh will result in longer collision detection time, obviously. So, You might want to play around with the staticRes parameter. Btw. the factor must be really high if you want a finer mesh 100000000.0f or something like that, just to give an impression of the magnitude.
to get the bounding box you can also call getBoundingBox() on the mesh (maybe you also need to call recalculateBoundingBox() first )
Cheers
Tom
Code: Select all
//! loads the terrain
bool CLMTerrainSceneNode::create(IImage* heightmap, ITexture* texture, ITexture* detailmap,
f32 gridPointSpacing, f32 heightScale, f32 resolution, f32 staticRes)
{
if(stub) delete stub;
if(hfield) delete [] hfield;
res=resolution;
dim=gridPointSpacing;
scale=heightScale;
size = heightmap->getDimension().Width;
hfield = new float[size*size];
vector3df ext(size*dim,0,size*dim);
f32 hmin=10000000;
f32 hmax=-10000000;
for (int i=0; i<size; i++)
for (int j=0; j<size; j++)
{
hfield[i+j*size] = heightmap->getPixel(i,size-1-j).getRed()*scale;
if(hmin>hfield[i+j*size]) hmin=hfield[i+j*size];
if(hmax<hfield[i+j*size]) hmax=hfield[i+j*size];
}
Box.MinEdge.set(-ext.X/2.0f,hmin,-ext.Z/2.0f);
Box.MaxEdge.set(ext.X/2.0f,hmax,ext.Z/2.0f);
stub=new ministub(hfield,
&size,&dim,1.0f,1.0f,
CLMTerrainSceneNode::_BeginFan,
CLMTerrainSceneNode::_FanVertex,
0,0,0);
vertex.Normal.set(0,1,0);
vertex.Color.set(255,255,255,255);
Material.Texture1=texture;
Material.Texture2=detailmap;
bDrawToMesh=true;
mesh = new SMesh();
_self=this;
stub->draw(
staticRes, // resolution ATTENTION THIS CHANGED!!!!!!
0,2.0f*hmax,0, // pos
0,-1,0, // dir
0,0,1, // up
-ext.X, // OTHO fovy
1, // aspect
1, // near
ext.X);
BeginFan();
bDrawToMesh=false;
return true;
}
You might keep in mind that a finer collision mesh will result in longer collision detection time, obviously. So, You might want to play around with the staticRes parameter. Btw. the factor must be really high if you want a finer mesh 100000000.0f or something like that, just to give an impression of the magnitude.
to get the bounding box you can also call getBoundingBox() on the mesh (maybe you also need to call recalculateBoundingBox() first )
Cheers
Tom
Yeah, thats true. My FPS drops from 80 to 20 now, and time to create OctTree is much more time consumption.You might keep in mind that a finer collision mesh will result in longer collision detection time, obviously.
I'll try to keep my camera moving on terrain without collision with SMesh, but base on getHeight.
Thank you ZDimitor and zola for such quick response.
[EDIT]
I've just try to use TerrainHeightAnimator on Camera node (with attached CollisionResponceAnimator) and seems to me it works OK. The only difficult thing is when one animator changes with another (when camera exits from the house with it's own flor to terrain).
Hittom wrote:Hi all:
This lib is realy great. I only download it and compiler it. It work great.
Also thanks to zola,ZDimitor,UzMaN... u really great to do thsi job. I think if i have a big detailmap (may be 1Gbyte),how to solve it.have any idea.
ttom
One solution would be to devide the terrain and the detailmap into smaller parts and then only display the terrain that is visible.
For a way how to do this You could take a look at Saigumis Seamless world tutorial. It's rather old but I guess You will see what the idea behind it is.
Btw, You forgot the mention KnightOfLight who started all this and Stefan who wrote libMini
Regards
Tom
Still alive?
Hello,
I read this thread and became quite interested in this but seeing that the last post was more then a year ago i am wondering if it's still viable as a terrainnode or that it is replaced by the next big thing(tm)
regards,
Deg
I read this thread and became quite interested in this but seeing that the last post was more then a year ago i am wondering if it's still viable as a terrainnode or that it is replaced by the next big thing(tm)
regards,
Deg