Heightmap Terrain

gzmzhen

03-08-2006 22:28:55

I've seen a lot of posts about getting OgreNewt to work with terrains and so on... and I'm just curious if anyone ever got something to work with heightmaps.

If not, how can I take my height map and turn it into something that works with OgreNewt?

Thanks for the help,
Nate

gzmzhen

04-08-2006 19:30:09

In addition to collision detection, I want to lie road along certain paths (I have exact placements) but I am unsure of how to tell where I should place them in terms of the y-axis... Is there anyway to get height value back from the terrain?

Thanks,
Nate

mysterycoder

06-08-2006 18:21:25

Are you trying to get OgreNewt to work with Paging Lanscape Scene Manager 2(PLSM2), or Terrain Scene Manager(TSM)?

Markness

07-08-2006 13:51:51

I am also interested in hearing about this, as I will be attempting object collision with PLSM2 terrains in a few days.

Is there anything in particular i should know before I do this? :)

HexiDave

07-08-2006 15:49:48

You can look at PLSM2 stuff here: http://www.ogre3d.org/phpBB2addons/viewtopic.php?t=1445

You can ignore all that stuff about changing the SceneManager to a PagingLandscapeSceneManager - I've since found that it was just an error from the CVS version I was dodging - it works without re-casting mSceneMgr.

On a side note - I'm attempting to add paging and quick access to vertex/index information in the standard TerrainSceneManager, so getting OgreNewt to work with that should be easier in the near future (unless I hit a snag or something :D)

gzmzhen

07-08-2006 18:47:14

I currently am only using the Terrain Scene Manager, since I have only a raw file that defines the area.

I either need OgreNewt to work with it, or at least be able to find the height at any given x,z point. What I want to do is lie road ontop of the terrain in specific places, and then get a vehicle to drive along it. I was hoping of just laying a road mesh down along the specific routes, but it seems like it'll be fairly difficult.

Does anyone have any ideas for me?

HexiDave

07-08-2006 20:23:19

Well I'm taking a break at the moment, but I successfully put paging into the TSM and have direct access to index/vertex lists, so I can surely add OgreNewt to TSM with a few function calls. If you're able to wait a little while, I can most likely get it done tonight (a way to grab vertex/index information from the terrain) so you can feed it to a TreeCollision.

I'm fairly certain you can create a TreeCollision from a SceneNode - look into the OctreeSceneManager project that comes with Ogre - that's where the TSM comes from, look at the TerrainSceneManager.cpp/.h files for grabbing the TerrainRoot SceneNode - I know it attaches all pages to it, so you could probably just build the collision data that way (might need to create a function to give you that SceneNode...)

Or, you could skip that stuff all together and just create a terrain as a mesh, since it's only one page anyways, and go to town on that (create the TreeCollision directly from that SceneNode.)

gzmzhen

07-08-2006 20:46:18

I'd love to see that code, and maybe use it if you let me =). I'll try looking into the TerrainSceneManager and see where that takes me.

You may email me directly if you wish, nbauernfeind at cmu dot edu.

Thanks.

gzmzhen

07-08-2006 22:06:05

Do you mean OgreTerrainSceneManager.h/.cpp? Those appear to be the closest files to what you were talking about.

HexiDave

07-08-2006 22:10:23

Yep, those are the files - sorry, went to look at the file names when typing and forgot I closed em :D

I'll be releasing all the code I do once it all works - currently it loads the pages from Perlin noise (haven't messed with the standard heightmap stuff yet, but not much to change) and has a slight floating-point issue which screws the height by very small fraction. The only other thing is that only one page is picked up as terrain in some cases, so i'm tracking that down (I think I know why it's doing it.)

I'll be working on it again shortly, so I'll try to get some code to retrieve the index/vertex data from the page that the current TSM sets up.

gzmzhen

07-08-2006 22:27:49

There is a function called getTerrainRootNode() in the TerrainSceneManager class. I am currently trying to see if I can get something to work with that. I will post a block of code if it works with OgreNewt.

gzmzhen

07-08-2006 22:51:32

Ok so here is a quick question, is it possible to cast a SceneManager object to a TerrainSceneManager? I mean, as you said this is the default Scene manager, but TerrainSceneManager has the function call getTerrainRootNode() and not SceneManager. Any ideas?

HexiDave

07-08-2006 23:18:49

You could, but you're opening a can of worms there. You'll want to use the getOptions function, but you need to edit a few things - I haven't tried these myself, but there's no reason why they shouldn't work:

OgreTerrainRenderable.h
Under the constructor of the class TerrainOptions you'll see a list of variables - those are the options that are returned when you use getOptions(), which is a function that overrides SceneManagers defaults (so you don't need to recast to a TerrainSceneManager to use it.) Add a SceneNode* to the options list like this:

...
/// Whether vertex colours are enabled
bool coloured;
/// Pointer to the material to use to render the terrain
MaterialPtr terrainMaterial;
/// >>>>> This is the new option! <<<<<
SceneNode* terrainNode;


Now add to the constructor:
TerrainOptions()
{
pageSize = 0;
tileSize = 0;
tilesPerPage = 0;
maxGeoMipMapLevel = 0;
scale = Vector3::UNIT_SCALE;
maxPixelError = 4;
detailTile = 1;
lit = false;
coloured = false;
lodMorph = false;
lodMorphStart = 0.5;
useTriStrips = false;
primaryCamera = 0;
terrainMaterial.setNull();
/// Here is good!
terrainNode=0;
};


Now the option is ready, but you need to actually set the option...

OgreTerrainSceneManager.cpp
Head to the void TerrainSceneManager::setupTerrainPages(void) function and under where mTerrainRoot is created, add this:
//create a root terrain node.
if (!mTerrainRoot)
mTerrainRoot = getRootSceneNode() -> createChildSceneNode( "Terrain" );
/// NEW CODE
mOptions.terrainNode = mTerrainRoot;


That's better! Now you should be able to call getOptions correctly to get the SceneNode like this:
SceneNode* mTerrainNode = mSceneMgr->getOptions().terrainNode;

You should then be able to create a TreeCollision from that node like this:
OgreNewt::Collision* col = new OgreNewt::CollisionPrimitives::TreeCollision(mWorld, mTerrainNode, true);
OgreNewt::Body* body = new OgreNewt::Body(mWorld, col);
delete col;


I did most of this code in my head, so double check for "spelling" errors. It should work alright, though - I haven't tried it, so feel free to point out mistakes.

gzmzhen

07-08-2006 23:46:15

Currently, for my OGRE applications I am simply using the SDK. Will making these changes force me to compile the OGRE library and DLL files from the actual source? Or is it possible for me to take the two files you mentioned, make the changes, and have my project compile a local OgreTerrainSceneManager by including the cpp source in my project? I believe that this will cause linker errors by having the source redefined in the library files, but I am unsure.

Thanks for your great help.

HexiDave

08-08-2006 00:09:15

I'm fairly certain all you'd have to recompile would be the Plugin_OctreeSceneManager project. I use the source and don't have any experience with the SDK. However, a quick look at the creation code from what I posted, you see this:
//create a root terrain node.
if (!mTerrainRoot)
mTerrainRoot = getRootSceneNode() -> createChildSceneNode( "Terrain" );


Now you can do this:
SceneNode* mTerrainNode = mSceneMgr->getSceneNode("Terrain"); and continue as it was. Well that's a bit easier, isn't it?

That should work without needing any recompiling of DLLs and such.

gzmzhen

08-08-2006 00:23:33

You're right about only needing to compile the OctreeSceneManager. I was just about to compile the manager after only making a few modifications in .h files that would've done the same thing, but in a different place (at least for the definition of the node).

Anyways, that is a much easier way to do it. Thanks for your help. I'll let you know if it works =).

gzmzhen

08-08-2006 00:39:21

This is what I'm trying:


virtual void createScene(void)
{

// omitted code

mSceneMgr->setWorldGeometry("GMterrain.cfg");

// omitted code

SceneNode* terrainNode = mSceneMgr->getSceneNode("Terrain");
OgreNewt::Collision* col = new OgreNewt::CollisionPrimitives::TreeCollision(mWorld, terrainNode, true);
OgreNewt::Body* bod = new OgreNewt::Body(mWorld, col);
delete col;

// omitted code
}


When run, I get an error #4 where the SceneNode says that something was indexed out of bounds. In particular the ogre.log says:


Error #4:
Funciton: SceneNode::getAttachedObject
Description: Object index out of bounds...
File: ...ogremain\src\ogrescenenode.cpp
Line:149


Stepping through my program I see that it successfully gets a SceneNode, but fails when it tries to create the TreeCollision object. Any suggestions here?

gzmzhen

08-08-2006 00:43:05

To be even more precise, in the constructor of TreeCollision it uses the code:


Ogre::MovableObject* obj = node->getAttachedObject(0);


However, this scene node has no attached objects... I can only wonder how it makes the terrain in this SceneNode ...

Seems like I get to do more OGRE probing =).

gzmzhen

08-08-2006 01:36:46

Alright, in OgreTerrainPageSource.cpp there is a function called buildPage(...). It is the function that actually adds to the Terrain node. Well, not quite. It makes a pageSceneNode which it then creates child nodes from that represent the actual terrain.

So the idea of the terrain scene node looks somewhat like this:

Terrain Scene Node
- > Page Scene Node
- - > Children Scene Nodes / Actual Tiles of Terrain

I had hoped that the SceneNodes were attached objects... so I used the code

mSceneMgr->getSceneNode("page[1]"); instead of "Terrain" which gave me the node where the children were directly attached to it.

However, I still got the same error. So, I guess that these extra scene nodes are not the attached objects we need.

HexiDave

08-08-2006 01:40:14

Ah! Sorry, I thought it "trickled down" to encompass all other SceneNodes in the heirarchy on it's own, sorry about that. The terrain creates pages (1 page for the default setup) and tiles - it creates a SceneNode for each tile as well as each page and THEN attaches it to the "Terrain" SceneNode. That means the whole method won't work :(

I'll have to look at TreeCollision constructor that takes a SceneNode - I'm sure it's possible to change it to accept a vector<> of SceneNodes that you grab by name (you can pull each tile's SceneNode by name then push it to the back of the vector, then feed it to the TreeCollision.) I have to fix a problem in my code first, but I'll look in a little while on this method - you'd have to add stuff to OgreNewt, but you're compiling from source there anyway.

gzmzhen

08-08-2006 01:54:43

It seems like this is fairly hopeless in that sense, I mean ... possibly hopeless. I'll keep looking into it. Right now this is how the Tree Collision gets the geometry:


Ogre::MovableObject* obj = node->getAttachedObject(0);
obj->getMovableType();
Ogre::MeshPtr mesh = ((Ogre::Entity*)obj)->getMesh();


The way this is makes me feel like the only way to actually do it is to convert my heightmap to a mesh that can be loaded seperately. Which is fine, but I have no idea where to do this or how - I can probably program something on my own, but I don't know much about the .mesh file format, or any other format that would be easily converted to mesh.

Do you think that it would work the same if each tile has a mesh, and each mesh can be treated as a submesh of a "fake" TreeCollisionTerrain mesh?

gzmzhen

08-08-2006 02:11:23

I tried to see if one tile would work (cause then we could possible link them together somehow in the future, as you said), but it also fails.

It successfully gets the attachedObject and successfully gets the MeshPtr from the obj->getMesh() call. For some reason though this line seems to be where it fails the most:

unsigned short sub = mesh->getNumSubMeshes();

for (unsigned short cs = 0; cs < sub; cs++)
{
Ogre::SubMesh* sub_mesh = mesh->getSubMesh(cs);
// ...


sub is equal to 65532 (i.e. MAX_SHORT - 4 = 2^16-4), yet getSubMesh(0) returns an uninitialized pointer (0xcccccccc).

Actually, now that I look at the MeshPtr information, it seems like all of it was invalid. There are weird values set everywhere and one of them is mCreator which is NULL (or 0x00000000). Yep the pRep of the MeshPtr looks like crap. I guess I had my hopes up for nothing =).

Thanks for all your help so far.

HexiDave

08-08-2006 02:18:46

Well, if you don't mind testing this next bit of code out in your setup (I currently don't have a testbed for this setup), it compiled perfectly fine on my end in OgreNewt, so should work fine with Ogre SDK:

OgreNewt_CollisionPrimitives.h
//! constructor
/*!
Creates a TreeCollision object from a vector of SceneNodes.
\param world pointer to OgreNewt::World.
\param nodes vector of SceneNodes.
\param optimize bool whether you want to optimize the collision or not.
*/

TreeCollision( const World* world, std::vector<Ogre::SceneNode*>& nodes, bool optimize );


Add that constructor (and the comment code if you like - works well with VisualAssistX if you have it) to the TreeCollision class - it's closer to the bottom of the file.

OgreNewt_CollisionPrimitives.cpp
TreeCollision::TreeCollision( const World* world, std::vector<Ogre::SceneNode*>& nodes, bool optimize ) : Collision( world )
{
Ogre::Vector3 scale;

m_col = NewtonCreateTreeCollision( m_world->getNewtonWorld(), NULL );
NewtonTreeCollisionBeginBuild( m_col );

for(int iNodes=0;iNodes < nodes.size();iNodes++ )
{

//now get the mesh!
Ogre::MovableObject* obj = nodes[iNodes]->getAttachedObject(0);
Ogre::MeshPtr mesh = ((Ogre::Entity*)obj)->getMesh();

//get scale
scale = nodes[iNodes]->getScale();

//find number of sub-meshes
unsigned short sub = mesh->getNumSubMeshes();

for (unsigned short cs=0;cs<sub;cs++)
{
Ogre::SubMesh* sub_mesh = mesh->getSubMesh(cs);

//vertex data!
Ogre::VertexData* v_data;

if (sub_mesh->useSharedVertices)
{
v_data = mesh->sharedVertexData;
}
else
{
v_data = sub_mesh->vertexData;
}

//let's find more information about the Vertices...
Ogre::VertexDeclaration* v_decl = v_data->vertexDeclaration;
const Ogre::VertexElement* p_elem = v_decl->findElementBySemantic( Ogre::VES_POSITION );

// get pointer!
Ogre::HardwareVertexBufferSharedPtr v_sptr = v_data->vertexBufferBinding->getBuffer( p_elem->getSource() );
unsigned char* v_ptr = static_cast<unsigned char*>(v_sptr->lock( Ogre::HardwareBuffer::HBL_READ_ONLY ));

//now find more about the index!!
Ogre::IndexData* i_data = sub_mesh->indexData;
size_t index_count = i_data->indexCount;
size_t poly_count = index_count / 3;

// get pointer!
Ogre::HardwareIndexBufferSharedPtr i_sptr = i_data->indexBuffer;

// 16 or 32 bit indices?
bool uses32bit = ( i_sptr->getType() == Ogre::HardwareIndexBuffer::IT_32BIT );
unsigned long* i_Longptr;
unsigned short* i_Shortptr;


if ( uses32bit)
{
i_Longptr = static_cast<unsigned long*>(i_sptr->lock( Ogre::HardwareBuffer::HBL_READ_ONLY ));

}
else
{
i_Shortptr = static_cast<unsigned short*>(i_sptr->lock( Ogre::HardwareBuffer::HBL_READ_ONLY ));
}


//now loop through the indices, getting polygon info!
int i_offset = 0;

for (size_t i=0; i<poly_count; i++)
{
Ogre::Vector3 poly_verts[3];
unsigned char* v_offset;
float* v_Posptr;
int idx;

if (uses32bit)
{
for (int j=0;j<3;j++)
{
idx = i_Longptr[i_offset+j]; // index to first vertex!
v_offset = v_ptr + (idx * v_sptr->getVertexSize());
p_elem->baseVertexPointerToElement( v_offset, &v_Posptr );
//now get vertex position from v_Posptr!
poly_verts[j].x = *v_Posptr; v_Posptr++;
poly_verts[j].y = *v_Posptr; v_Posptr++;
poly_verts[j].z = *v_Posptr; v_Posptr++;

poly_verts[j] *= scale;
}
}
else
{
for (int j=0;j<3;j++)
{
idx = i_Shortptr[i_offset+j]; // index to first vertex!
v_offset = v_ptr + (idx * v_sptr->getVertexSize());
p_elem->baseVertexPointerToElement( v_offset, &v_Posptr );
//now get vertex position from v_Posptr!

// switch poly winding.
poly_verts[j].x = *v_Posptr; v_Posptr++;
poly_verts[j].y = *v_Posptr; v_Posptr++;
poly_verts[j].z = *v_Posptr; v_Posptr++;

poly_verts[j] *= scale;
}
}

NewtonTreeCollisionAddFace( m_col, 3, (float*)&poly_verts[0].x, sizeof(Ogre::Vector3), cs );
i_offset += 3;
}

//unlock the buffers!
v_sptr->unlock();
i_sptr->unlock();

}
}
//done!
NewtonTreeCollisionEndBuild( m_col, optimize );
}


Add the function with the other TreeCollision constructors (again, near the bottom - in the .cpp file this time) and recompile.

Now, I haven't tested this, but i'm fairly sure it works:
vector<SceneNode*> nodes;
ConstChildNodeIterator it = mSceneMgr->getSceneNode("page[0]")->getChildIterator();
while (it.hasMoreElements())
{
SceneNode* node = static_cast<SceneNode*>(it.getNext());
nodes.push_back(node);
}
OgreNewt::Collision* col = new OgreNewt::CollisionPrimitives::TreeCollision(mWorld,nodes,true);


That should iterate through all the SceneNodes under the "Terrain" node's page node ("page[0]") and feed it to the TreeCollision as a vector of all the tiles.

I really hope that works, but my appologies if it doesn't - I'm trying to get a very annoying bug squashed at the same time as writing all of this :D If it still goes all screwy, let me know and I'll see what help I can offer - I can always create a small utility to chew up a heightfield with the correct paramaters and spit it out as a .mesh that you can do as you please with.
Good luck!

gzmzhen

08-08-2006 02:29:36

Given the testing/probing that I've done I actually won't have a list of SceneNodes to spit at the function. It appears that each tile doesn't really have a mesh. I mean, the functions work but only so far. It says that there are 65k submeshes and querying the first returns an invalid pointer. So, as much as I appreciate your help writing such a function (which I'm sure makes a great addition to the TreeCollision class and you should try to add that to the CVS) it won't quite help us out on this aspect.

I will look into making a mesh from the raw file. Thanks for your help. If I fail miserably (again), I'll let you know.

HexiDave

08-08-2006 02:42:39

If it makes you feel any better I just kicked my monitor :D

Anyways, give me a little bit to break and I'll look through some code I have - when I first thought about redesigning the TerrainSceneManager to work with Perlin Noise I looked at TerraBuild, which was (I believe) finally incorporated into the PLSM2 map editor. There was a Heightmap -> Mesh function there, so I'll go dig it up and put it into a program you can edit.

I'll take another look at how to "extract" the terrain without modifying the TSM - sounds like something funky is going on.

mysterycoder

08-08-2006 06:28:14

I was trying to get this to work also, I used:
pageConstructed(Ogre::TerrainSceneManager* manager, size_t pagex, size_t pagez, Ogre::Real* heightData)
{
//Get the scale
Ogre::Vector3 scale = manager->getScale();

//Get the terrain's Size
int terrainSize = manager->getPageSize();

numOfVertices = terrainSize * terrainSize;
vertices = new Ogre::Vector3[numOfVertices];

for(int x=0; x < terrainSize; ++x)
{
for(int z=0; z < terrainSize; ++z)
{
//Calculate the points
vertices[x] = Ogre::Vector3(x, heightData[z] * 100, z) * scale;
}
}

Ogre::LogManager::getSingletonPtr()->logMessage("Page was Constructed");
}


with:

//Add terrListener as a TerrainPageSourceListener
terrListener = new TerrainListener();
Ogre::TerrainPageSourceListenerManager::getSingletonPtr()->addListener(terrListener);


this let me grab the vertices, but not the indicies. Maybe it'll help you guys?

gzmzhen

08-08-2006 17:46:37

Does that even work? I thought that heightData would have size terrainSize^2 meaning for each x,z coordinate could potentially have a different height. It looks like you're just getting the verticies along one particular slice of the plane and extending it all the way to the other side.

I understand your idea - I just thought I'd give you a heads-up.

walaber

08-08-2006 18:37:01

sorry, I haven't followed this completely... but have we found a way to get the vertex and index data from the terrain manager?

if so, we should be able to hook this up into the new SceneParser class I created a while back, which can be used to parse through a tree of scene nodes, adding Entity's as it goes...

HexiDave

08-08-2006 18:46:50

I see why the TreeCollision-from-SceneNode code doesn't work - the terrain renderables aren't actually meshes, they're just a pile of polygons.

I'm gonna go ahead and suggest that you put the heightmap through a modeler, like Blender, and export as a mesh - that way you can really give it a nice look without any problems. You'll still have access to ray-queries using OgreNewt, so you can place things directly on the mesh if you need to - plus you'll be able to build the TreeCollision with the original SceneNode* feeder code.

Here's a tutorial I googled that shows how to take a heightmap and put it through blender - I don't know what 3D modeler you're using, but they all can do this - then you just export it as an Ogre .mesh file like you would any model: http://members.tripod.com/~funky_munky/tuts/blender/heightmaps.htm

Otherwise you're most likely going to need to lock vertex buffers and all sorts of overkill. If you were to switch to the source version instead of SDK, it wouldn't be nearly as big of a hassle because you could just redo the TerrainSceneManager to your likings and pull the index/vertex buffers when you need to.

I'll be creating code that does that for my project, so I'll probably submit patches or something.

HexiDave

08-08-2006 18:49:00

Walaber: with some simple modifications to TSM, I could submit a patch that returns vertex/index data from the getOptions() code - that way you can still use TSM without re-casting anything. I'll work on it this afternoon.

gzmzhen

08-08-2006 18:56:39

My heightmap is a .raw file of real data (altitude data in Michigan). Is it possible to turn this into a black/white image, or use that for the noise in blender?

Any ideas?

mysterycoder

08-08-2006 22:35:38

@HexiDave: That'd be great, please post it when you're done :)

HexiDave

09-08-2006 09:02:53

Alright - a few things about this code: first, I didn't do most of it - it's mostly from TSM's loader code and TerraBuild's code. Next, there is something that's happening that I don't have time to check out (it might be from my adjustments to TSM's core, but I dunno) - the terrain mesh that's rebuilt is slightly larger by about 2.9 units, but I compensate for that in the code for a 513x513 heightfield. The code is setup here for a 513x513 PNG heightmap, but the code is also there for RAW files and all the other stuff the TSM uses (same code) so you can see how it's used - you could even use getOptions() to retrieve what exactly TSM has loaded and do it automatically, but I didn't have time tonight.

Ok, the first two files are in this zip file - they're added to your project - they create the "new" mesh from heightfield info:
TerrainMesh.zip

Make sure you put
#include "TerrainMesh.h"
at the top of your program.

And this giant mess is the code:

size_t mPageSize=512;
size_t imgSize=513;
size_t scaleX=1500;
size_t scaleY=100;
size_t scaleZ=1500;
bool mIsRaw = false;
uchar mRawBpp;
String mSource="terrain.png";

MemoryDataStreamPtr mRawData;
Image mImage;

// Special-case RAW format
if (mIsRaw)
{
// Image size comes from setting (since RAW is not self-describing)


// Load data
mRawData.setNull();
DataStreamPtr stream = ResourceGroupManager::getSingleton().openResource(mSource, ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME);
mRawData = MemoryDataStreamPtr(new MemoryDataStream(mSource, stream));

// Validate size
size_t numBytes = imgSize * imgSize * mRawBpp;
if (mRawData->size() != numBytes)
{
//shutdown();
OGRE_EXCEPT(Exception::ERR_INVALIDPARAMS,
"RAW size (" + StringConverter::toString(mRawData->size()) +
") does not agree with configuration settings.",
"HeightmapTerrainPageSource::loadHeightmap");
}
}
else
{

mImage.load(mSource, ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME);
// Must be square (dimensions checked later)
if ( mImage.getWidth() != mImage.getHeight())
{
//shutdown();
OGRE_EXCEPT(Exception::ERR_INVALIDPARAMS,
"Heightmap must be square",
"HeightmapTerrainPageSource::loadHeightmap");
}
imgSize = mImage.getWidth();
}


// Convert the image data to unscaled floats
ulong totalPageSize = (imgSize) * (imgSize);
Real *heightData = new Real[totalPageSize];
const uchar* pOrigSrc, *pSrc;
Real* pDest = heightData;
Real invScale;
bool is16bit = false;

if (mIsRaw)
{
pOrigSrc = mRawData->getPtr();
is16bit = (mRawBpp == 2);
}
else
{
PixelFormat pf = mImage.getFormat();
if (pf != PF_L8 && pf != PF_L16)
{
OGRE_EXCEPT( Exception::ERR_INVALIDPARAMS,
"Error: Image is not a grayscale image.",
"HeightmapTerrainPageSource::requestPage" );
}

pOrigSrc = mImage.getData();
is16bit = (pf == PF_L16);
}
// Determine mapping from fixed to floating
ulong rowSize;
if ( is16bit )
{
invScale = 1.0f / 65535.0f;
rowSize = imgSize * 2;
}
else
{
invScale = 1.0f / 255.0f;
rowSize = imgSize;
}
// Read the data
pSrc = pOrigSrc;

for (ulong j = 0; j < imgSize; ++j)
{
for (ulong i = 0; i < imgSize; ++i)
{
if (is16bit)
{
#if OGRE_ENDIAN == OGRE_ENDIAN_BIG
ushort val = *pSrc++ << 8;
val += *pSrc++;
#else
ushort val = *pSrc++;
val += *pSrc++ << 8;
#endif
*pDest++ = Real(val) * invScale*scaleY;
}
else
{
*pDest++ = Real(*pSrc++) * invScale*scaleY;
}
}
}
#define TERRAIN_MESH_NAME "TerrainMesh"
#define TERRAIN_ENT_NAME "TerrainEntity"
#define TERRAIN_MAT_NAME "Examples/GrassFloor"

TerrainMesh* tMesh = new TerrainMesh(TERRAIN_MESH_NAME,TERRAIN_MAT_NAME);
tMesh->init(mPageSize,mPageSize,scaleX-2.9,scaleZ-2.9);

Entity* mEnt = mSceneMgr->createEntity("MyTerrain",TERRAIN_MESH_NAME);
//mEnt->setMaterialName (TERRAIN_MAT_NAME);
mEnt->setCastShadows(false);



tMesh->update(heightData);

SceneNode* mNode = mSceneMgr->getRootSceneNode()->createChildSceneNode("TerrainNode");
mNode->attachObject(mEnt);

delete[] heightData;
delete tMesh;
OgreNewt::Collision* col = new OgreNewt::CollisionPrimitives::TreeCollision(mWorld,mNode,false);
OgreNewt::Body* bod = new OgreNewt::Body(mWorld,col);
mNode->detachAllObjects();
delete col;


As you can see, it's nothing pretty - but it works! Just use mNode to feed as a SceneNode to a new TreeCollision. Comment out the line at the bottom where you detatch everything from the node if you want to see how close the new mesh is from what you normally use. The scaleX-2.9 is the adjustment for it's scale problem. I really don't know why it does it just yet, but it was my roommates birthday, so I didn't have any time to get much done :D

I did test it and it works just fine, but I warn you now: look into TreeCollisionSerializer! It takes quite a while to build that many polys into a TreeCollision (expect at least a minute to load a new one) - you're talking 512x512x2 polygons... It'll probably be a few megs if you want to serialize it, but I think it's worth it. Here's what I used in PLSM2 with OgreNewt - look at the PhysicsManager::loadTerrainGeometry function for some serializer use. Loading a serialized file is a LOT faster.

Anyways, hope this helps - works pretty well on my end, but it really needs a serializer for most maps. Good luck!

gzmzhen

09-08-2006 17:04:05

Thanks a lot. I have a lot to do for now, so I will test this within a week or so. I really appreciate it.

When I tried to download the zip I got a 404 and a Forbidden access error:

Forbidden

You don't have permission to access /files/2006/7/10/122469/TerrainMesh.zip on this server.

Additionally, a 403 Forbidden error was encountered while trying to use an ErrorDocument to handle the request.

HexiDave

09-08-2006 18:43:56

Jeez, is every free file hosting service dying lately? In the last month I've had 3 of my fileshare sites just die for no reason.

Here's a rapidshare link: click

Someone mentioned a TerrainPageSourceListener in the other forum, but I think this still gets the job done.

gzmzhen

09-08-2006 20:20:11

Thanks, I got it this time =).

HexiDave

09-08-2006 21:10:58

I'm gonna continue my project a bit, but if you need help getting the serializer to work from the thread I linked, lemme know and I'll try to package this whole thing up into a function or something. And as for the TerrainPageSourceListener thing - you're not losing out on a ton of speed - loading a terrain map into a heightfield regularly only takes a moment, so building the TreeCollision is still, by far, the most time consuming step - which you'd be doing with the exact same information, just a lot more steps (per tile instead of one big page.)

mysterycoder

09-08-2006 21:51:13

@HexiDave: What license is this thing?
no license? :)

HexiDave

10-08-2006 02:08:46

Well from reading the header, tuan did the code and it says "This code is absolutely free to use and modify."

The other code was right from Ogre's TSM, so Ogre's license applies there.

Mr. Awesome

14-08-2006 00:56:40

I tried using this code in the example framework to see if it works, but when I display the mesh, only a quarter of the terrain is mapped along the z-axis. Also, there are a lot of stray triangles running all over the place, but constrained to only the one quarter of terrain. Any ideas?

turkeypotpie

18-08-2006 03:33:19

This solution seems to work fine:


class MyTerrainPageSourceListener:
public Ogre::TerrainPageSourceListener
{
public:
MyTerrainPageSourceListener(OgreNewt::World* world) : world_(world)
{
Ogre::TerrainPageSourceListenerManager::getSingleton().addListener(this);
}
~MyTerrainPageSourceListener()
{
Ogre::TerrainPageSourceListenerManager::getSingleton().removeListener(this);
}

virtual void pageConstructed(Ogre::TerrainSceneManager* manager, size_t pagex, size_t pagez, Real* heightData);

OgreNewt::World* world_;
};

void MyTerrainPageSourceListener::pageConstructed
(Ogre::TerrainSceneManager* manager, size_t pagex, size_t pagez, Real* heightData) /*= 0*/
{
assert(world_);

class TerrainVectorCalculator
{
public:
size_t pagex_;
size_t pagez_;
int pageSize_;
Vector3 scale_;
Real* heightData_;

Vector3 v(int x, int z)
{
return scale_ * Vector3(
pagex_ * pageSize_ + x,
heightData_[pageSize_ * z + x],
pagez_ * pageSize_ + z
);
}
};

TerrainVectorCalculator terrainCalc;
terrainCalc.pagex_ = pagex;
terrainCalc.pagez_ = pagez;
terrainCalc.pageSize_ = manager->getPageSize();
terrainCalc.scale_ = manager->getScale();
terrainCalc.heightData_ = heightData;

OgreNewt::CollisionPrimitives::TreeCollision terrainCollision(world_);
terrainCollision.start();

int polyId = 0;
for (int z = 0; z < manager->getPageSize()-1; z++)
{
for (int x = 0; x < manager->getPageSize()-1; x++)
{
Vector3 va[3];

va[0] = terrainCalc.v(x , z );
va[1] = terrainCalc.v(x , z+1);
va[2] = terrainCalc.v(x+1, z );
terrainCollision.addPoly(va, polyId++);

va[0] = terrainCalc.v(x+1, z );
va[1] = terrainCalc.v(x , z+1);
va[2] = terrainCalc.v(x+1, z+1);
terrainCollision.addPoly(va, polyId++);
}
}

const bool optimize = false;
terrainCollision.finish(optimize);

new OgreNewt::Body(world_, &terrainCollision);
}



Load up some terrain, and you're good to go.

This looks ok with OgreNewt::Debugger's lines ... but I haven't tested it with bodies yet. Nor have I tested it with multiple terrain pages.

gzmzhen

20-08-2006 18:59:00

I seem to be misunderstanding something, cause with the code HexiDave has provided I can't get it to load the RAW file properly (it actually tries to access heightData out of bounds). It doesn't work with the settings I've saved in the config file. I can get it to run with specific settings, but then can't get it to show.

So, since my application requires practically free queries to terrain height I've decided to write something myself that generates the mesh for me, which will clearly work with the Newton physics. I'm not done yet, but I'm getting there.

HexiDave

21-08-2006 02:49:29

Well, as I said - it was a quick rip of stuff from different functions, so I wasn't sure if it would work or just be an entry point into helping you make your own function. The stuff above for the PageSourceListener is probably a good way to go.

gzmzhen

21-08-2006 17:04:09

=) No worries, I'm definitely using your code as a guide to get the heightdata, which I had no idea how to do at first. I'm using the OgreTerrainSceneManager to figure out how to load all the data from a config file, and then using mesh code to regenerate the meshes.

I'm currently stuck on how to calculate normals for each vertex (since, in my opinion, it's the triangles that should have the normals and not the vertecies...).

But much thanks for your help! I'd still be lost without you.

gzmzhen

21-08-2006 21:57:28

Hey HexiDave,

About that serializer. It takes quite some time to create the collisions for the terrain. If I serialize them once, it's basically an instant upload right? Is it easy to serialize them?

Do you have a quick example? (even a cut-paste job will be fine)

Thanks!

P.S. My terrain mesh maker is awesome... I'm still working on the part where I put a texture on the terrain (u-v coords in the mesh buffer and such). I'm betting that the mesh serializer works a lot like the OgreNewt serialier - so I'll probably make these two ouput to file pairs so that one can't be updated without the other. I'll have to have a #define set to either create and save them or to load them.

My project is starting to feel really good. Thanks so much for your help.

HexiDave

22-08-2006 02:39:30

OgreNewt::TreeCollisionSerializer* serializer=new OgreNewt::TreeCollisionSerializer();
OgreNewt::CollisionPrimitives::TreeCollision* collision;

if(!ResourceGroupManager::getSingleton().resourceExists("General","FILENAMEHERE.col")){

collision = new OgreNewt::CollisionPrimitives::TreeCollision(mWorld,numVertices,vertices,indexData,false);
serializer->exportTreeCollision(collision,"coldata//" + "FILENAMEHERE.col");
}else{
collision=new OgreNewt::CollisionPrimitives::TreeCollision(mWorld);
DataStreamPtr ptr = ResourceGroupManager::getSingleton().openResource("FILENAMEHERE.col");
serializer->importTreeCollision(ptr,collision);
ptr.getPointer()->close();
}



OgreNewt::Body* body=new OgreNewt::Body(mWorld,collision);
SceneNode* bodyNode = mSceneMgr->getRootSceneNode()->createChildSceneNode();
body->attachToNode(bodyNode);


You'll see "FILENAMEHERE.col", but you can change that to whatever you want or a string you make up with the tile numbers. I also have it exporting to ./coldata folder, this is to keep clutter out - if you use it like above, you'll need to add a folder to the resources.cfg file like this:
FileSystem=./coldata

There might be a few issues with the code - I changed a few things so they were generic (I just ripped it out of my code, which was adapted from the OgreNewt + PLSM2 wiki), but it should work. I also was using indexData and vertices to create my TreeCollision, but any method you use will work the same.