
Collision rescue at hand.
// new header
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 irr;
using 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;
irr::video::IVideoDriver* driver;
SMesh* mesh; // a mesh that stores the landscape mesh for a collosion proxy
SMeshBuffer buffer; // a buffer for fan vertecis
S3DVertex vertex; // a default vertex
// true iff we want to draw to the mesh and not to the screen
bool bDrawToMesh;
// adds the meshbuffer to the mesh when drawing to mesh is enabled
void addMeshBufferToMesh();
// 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 a mesh of the whole terrain for collision detection etc.
IMesh* getMesh();
// 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
new impl.
Code: Select all
#include "CLMTerrainSceneNode.h"
namespace irr
{
namespace scene
{
using namespace irr;
using 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),mesh(0),driver(0)
{
driver=SceneManager->getVideoDriver();
stub=0;
hfield=0;
AbsoluteTransformation.getInverse(invAbsoluteTransform);
AutomaticCullingEnabled=false;
}
CLMTerrainSceneNode::~CLMTerrainSceneNode()
{
if(stub) delete stub;
if(hfield) delete [] hfield;
if(mesh) mesh->drop();
}
//! 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];
}
//getMesh();
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(
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;
bDrawToMesh=false;
return true;
}
// adds the meshbuffer to the mesh
void CLMTerrainSceneNode::addMeshBufferToMesh()
{
if(buffer.Vertices.size()>0)
{
// copy the
SMeshBuffer* mb=new SMeshBuffer();
u32 n=buffer.Vertices.size();
for(u32 i=0; i<n; i++)
{
// add vertex
mb->Vertices.push_back(buffer.Vertices[i]);
if(i>1)
{
mb->Indices.push_back(0);
mb->Indices.push_back(i-1);
mb->Indices.push_back(i);
}
}
mb->Indices.push_back(0);
mb->Indices.push_back(n-1);
mb->Indices.push_back(1);
mesh->addMeshBuffer(mb);
mb->drop();
}
}
// 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)
{
if(driver )
{
if(bDrawToMesh)
addMeshBufferToMesh();
else
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)
{
vertex.Pos.set(dim*i-size/2*dim, y, size/2*dim-dim*j);
vertex.TCoords.set((float)i/(size-1),(float)j/(size-1));
buffer.Indices.push_back(buffer.Indices.size());
buffer.Vertices.push_back(vertex);
}
IMesh* CLMTerrainSceneNode::getMesh()
{
return mesh;
}
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,0,0);
mat.MaterialType=EMT_SOLID;
driver->setMaterial(mat);
driver->draw3DBox(Box, SColor(255,0,0,255));
if(mesh)
for(int i=0;i<mesh->getMeshBufferCount();i++)
driver->drawMeshBuffer(mesh->getMeshBuffer(i));
}
}
}
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;
}
}
}
terrain height following animator
Code: Select all
#ifndef CTERRAINHEIGHTANIMATOR_H_INCLUDED
#define CTERRAINHEIGHTANIMATOR_H_INCLUDED
#include <irrlicht.h>
#include "CLMTerrainSceneNode.h"
namespace irr
{
namespace scene
{
// animator that clamps the animated node to the terrain height
class CTerrainHeightAnimator : public ISceneNodeAnimator
{
protected:
// the terrian node
CLMTerrainSceneNode* d_terrain;
// minimal height over ground
f32 d_h;
// fit the height of the node or let it fly
bool d_fit;
// to speed things up a little some cached memory,
// so anim node doesn't have to allocate everytime
matrix4 Trans;
matrix4 invTrans;
vector3df pos;
public:
/*!
\brief constructor
\param terrain, the terrain we want to follow
\param h, min height over ground
\param fit, if true the clamp height to terrain height otherwise allow to fly
*/
CTerrainHeightAnimator(CLMTerrainSceneNode* terrain, f32 h, bool fit)
: d_terrain(terrain),d_h(h),d_fit(fit)
{ }
// destructor
virtual ~CTerrainHeightAnimator(){}
//! Animates a scene node.
//! \param node: Node to animate.
//! \param timeMs: Current time in milli seconds.
virtual void animateNode(ISceneNode* node, u32 timeMs)
{
pos.set(node->getPosition());
Trans=d_terrain->getAbsoluteTransformation();
Trans.getInverse(invTrans);
invTrans.transformVect(pos);
float h = d_terrain->getHeight(pos.X,pos.Z)+d_h;
pos.Y = d_fit ? h : (pos.Y > h ? pos.Y : h);
Trans.transformVect(pos);
node->setPosition(pos);
}
};
}
}
#endif
for collisions just get the whole terrain mesh after you created the terrain
and do what ever fancy things you'd like to do with the mesh but do not drop() it
The interface to create the terrain and the collision mesh is currently just one function "create(...)" this might turn out not to be enough because the collision mesh will have the same resolution as the terrain.
Cheers
Tom