heightfieldShape crashing

imtrobin

06-12-2006 15:41:24

Hi,

Reporting two fixes that is crashing heightfieldShape. Perhaps it's is fixed in cvs but I can't connect to it currently.

In heightfieldShape ctor


currentSample->height = img.getColourAt(row, column,1).r * 255;

should be changed to

currentSample->height = img.getColourAt(row, column,0).r * 255;


The Z = 1 is crashing when I used a 8 bit png file.


delete heightFieldDesc.samples;

should be changed to

delete [] heightFieldDesc.samples;


since we allocated heightFieldDesc.samples = new NxU32[size * size];

Though I can get it running, the collision doesn't match the terrain rendered. It seems there is some hardcoding in there which I can't figure out yet.

1. Why is nbRows/nbCols hardcoded to 50 instead of image width?

2. Why is img.getColourAt(row, column,0).r using the red component? PhysX expects a 16bit file, is that the reason? I'm using a 8bit heightmap like the terrain.cfg

3. Shouldn't size be the parameter used in terrain.cfg worldscale instead of image size?

betajaen

06-12-2006 17:12:33

The height map isn't finished so your going to see little things like that and I can't remember anything about it, however:

1. No idea. I expect I was working with a file, so I had to hard code it like that.

2. Because 16 bit is more accurate than 8 bit. Why the red component, because blue or green can be used for material indexes.

3. Probably ;)

kungfoomasta

07-12-2006 21:44:23

Is the height map on the to-do list?

I'm new to nxOgre (downloaded everything, but haven't integrated into my app), so I'm curious how nxOgre deals with Terrains.

Also, I think PLSM2 splits the heightmap image into several smaller ones (mapsplitter).

I'm currently using TSM, which only has one heightmap.. maybe I should stick with that for use with nxOgre. I could use ray queries to place my character on the terrain, but that would be defying gravity, not to mention I would have to also check if I was over objects like bridges and stairs. Does the character controller support walking over terrain?

KungFooMasta

betajaen

07-12-2006 22:55:52

I doesn't split it up, it just turns the image into a heightmap meshShape, based on max height and width/length of the piece of terrain, and of course since it's a meshShape you can walk on it, you can have multiple terrains too. But the rendering is totally separate so you implement it by your own way; Ogre Terrain or PLSM2.

In fact NxTutorial 605 will be a full tutorial on that (think Oblivion).

But terrain is on the lower part of my TODO list.

imtrobin

10-12-2006 11:26:58

Ok, I manageed to get it working, I created another class heightmapShape instead of modify NxOgre. Would you want me to submit the source?

betajaen

10-12-2006 11:39:22

Sure go ahead.

imtrobin

10-12-2006 12:01:13

I didn't following your coding convention. I named it as HeightmapShape instead to avoid conflict

Header


#ifndef _HEIGHTMAPSHAPE_H_
#define _HEIGHTMAPSHAPE_H_

#include <nxOgre_includes.h>
#include <nxOgre_shape.h>

namespace nxOgre
{
/// A shape for normal heightmaps.
class heightmapShape : public nxOgre::shape
{
public:

// creates terrain from grayscale height map like terrain.cfg
// maxWorldX,Z are size of terrain in world
// maxWorldHeightY is max height of terrain

// heightFieldRow,Col split the terrain into polygon samples as in PhysX NxHeightFieldDesc
// note largr values slow down the PhysX considerably, small values do not represent terrain accurately

heightmapShape (Ogre::String _filename
,float maxWorldX
,float maxWorldZ
,float maxWorldHeightY
,unsigned int heightFieldRow = 40
,unsigned int heightFieldCol = 40);

NxHeightFieldShapeDesc mShapeDesc;
//NxTriangleMeshShapeDesc mShapeDesc;

Ogre::String toString ();
};

};


#endif // _HEIGHTMAPSHAPE_H_


source

#include "HeightmapShape.h"

#include <nxOgre_world.h>

#include <NxCooking.h>

#include <cassert>

namespace nxOgre
{
heightmapShape::heightmapShape (Ogre::String _filename
,float maxWorldX
,float maxWorldZ
,float maxWorldHeightY
,unsigned int heightFieldRow ,unsigned int heightFieldCol)
: shape(shape::SHAPE_HEIGHTFIELD)
{
assert (0 < maxWorldX);
assert (0 < maxWorldZ);
assert (0 <= maxWorldHeightY);
assert (0 < heightFieldRow);
assert (0 < heightFieldCol);

Ogre::Image img;

img.load(_filename, Ogre::ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME);

// TODO: Moan about non square sizes. (Although it doesn't matter to much in PhysX, but Ogre
// doesn't like 'em)

if (img.getWidth() != img.getHeight()) {
assert (0);
return;
}

//if (img.getFormat() != Ogre::PF_L8) {
// return;
//}

unsigned int size = img.getWidth();

unsigned int nbColumns = heightFieldCol;
unsigned int nbRows = heightFieldRow;
NxHeightFieldDesc heightFieldDesc;
heightFieldDesc.nbColumns = nbColumns;
heightFieldDesc.nbRows = nbRows;
heightFieldDesc.verticalExtent = -1000;
heightFieldDesc.convexEdgeThreshold = 0;

// Allocate storage for heightfield samples
heightFieldDesc.samples = new NxU32[size * size];
heightFieldDesc.sampleStride = sizeof(NxU32);

char* currentByte = (char*)heightFieldDesc.samples;
int gMatrixSize = 3;
NxReal sixtyFourKb = 65536.0;
NxReal thirtyTwoKb = 32767.5;

NxMaterialIndex gMatrix[9][3] = {
// {tesselation, material0, material1}
{0,1,1}, {0,1,1}, {0,1,1},
{0,1,1}, {0,1,1}, {0,1,1},
{0,1,1}, {0,1,1}, {0,1,1}
};
const Ogre::uchar* pSrc = img.getData();

// bloody, row and column is bad naming, should be X,Y like Ogre Image
// note this "row" goes left right, while "column" goes top down

for (NxU32 row = 0; row < nbRows; row++) {
for (NxU32 column = 0; column < nbColumns; column++) {

NxU32 matrixOffset = (row % gMatrixSize) * gMatrixSize + (column % gMatrixSize);

NxReal s = NxReal(row) / NxReal(nbRows);
NxReal t = NxReal(column) / NxReal(nbColumns);
// NxI16 height = (NxI32)(0.5 * thirtyTwoKb * (NxMath::sin(5.0f*NxPiF32*s) + NxMath::cos(5.0f*NxPiF32*t)));

NxHeightFieldSample* currentSample = (NxHeightFieldSample*)currentByte;
//currentSample->height = *pSrc++; // height; // img.getColourAt(nbRows - row,nbColumns - column,0).r;

// in greyscale, rgb of colourvalue returns same value so use .r is fine
float colorValue = img.getColourAt (s * size, t * size,0).r;
// float colorValue = img.getColourAt (row, column,0).r;

// height is expected in signed 16 bits for PhysX
currentSample->height = colorValue * 32768.0f;


currentSample->materialIndex0 = gMatrix[matrixOffset][1];
currentSample->materialIndex1 = gMatrix[matrixOffset][2];
currentSample->tessFlag = gMatrix[matrixOffset][0];


currentByte += heightFieldDesc.sampleStride;
}
}

NxHeightField *hf = world::getSingleton().mPhysicsSDK->createHeightField(heightFieldDesc);

// Data has been copied, we can free our buffer
delete [] heightFieldDesc.samples;

mShapeDesc.heightField = hf;
mShapeDesc.shapeFlags = NX_SF_FEATURE_INDICES | NX_SF_VISUALIZATION;

mShapeDesc.heightScale = maxWorldHeightY / 32768.0f;

mShapeDesc.rowScale = maxWorldX / NxReal(nbRows-1);
mShapeDesc.columnScale = maxWorldZ / NxReal(nbColumns-1);
// mShapeDesc.rowScale = size / NxReal(nbRows-1);
// mShapeDesc.columnScale = size / NxReal(nbColumns-1);
// heightFieldShapeDesc.meshFlags = NX_MESH_SMOOTH_SPHERE_COLLISIONS;
mShapeDesc.materialIndexHighBits = 0;
mShapeDesc.holeMaterial = 2;


}

Ogre::String heightmapShape::toString ()
{
return "Heightmap";
}
};

betajaen

10-12-2006 12:10:50

Excellent. I'll put it into NxOgre if you like.

imtrobin

10-12-2006 12:21:25

Yes please

syedhs

11-12-2006 04:38:25

Cool as I am going to tackle this feature (heightmap) about a month from now :) Need to wait for smokes to clear and dust to settle. 8)

imtrobin

11-12-2006 05:04:31

Cool syedhs, you are from malaysia. So am I but in JB.

syedhs

11-12-2006 09:12:43

JB? Are you from UTM? Are you in the same faculty as public_enemy?

imtrobin

11-12-2006 10:28:22

UTM? No I graduated years ago in Singapore. Is there a school in JB teaching gamedev? I don't know public_enemy, there is only 1 game developer in JB I only know who is Jason.

luis

21-01-2007 11:30:38

@imtrobin thanks for sharing the code!

the class works perfectly well :) the only thing i modified was this line:

mShapeDesc.shapeFlags = NX_SF_FEATURE_INDICES | NX_SF_VISUALIZATION;

to:

mShapeDesc.shapeFlags = NX_SF_FEATURE_INDICES;


with the original version i get: 47FPS with a 257x257 pixels heightmap and 275FPS without the NX_SF_VISUALIZATION.

imtrobin

21-01-2007 12:57:47

I think the code is already integrated into the the NxOgre 0.4 RC3.

Nudel

21-01-2007 14:11:37

@luis
Holy shit, thanks! That increased the fps from 100 to 600. :D
@imtrobin
Im using your code too, because the heightfield shape in rc3 had weird constructor parameters.

luis

25-01-2007 13:38:02

Hi!

i'm trying to delete the class and unload the heightmap in Physx (in order to load another one as a different tracks/levels in my game) but it crashes

i'm doing something like this (i dont have the original code here, but it looks like this):


heightmapShape::~heightmapShape()
{
world::getSingleton().mPhysicsSDK->destroyHeightField( *(mShapeDesc.heightField) );
// not sure if the method is destroyHeightField or releaseHeightField
}



I'm also destroying the static body in the main application but it still crashes...

does someone know how to unload and delete the heightmap in a proper way ?

betajaen

25-01-2007 13:52:16

I had a quick look, and for some reason the heightfield pointer isn't being stored anywhere. So it should go:

class _nxOgreExport heightmapShape : public shape {
...
NxHeightField *mHeightField;
...
};


heightmapShape::heightmapShape(Ogre::String _filename, Ogre::Real _maxHeight) : shape(shape::SHAPE_HEIGHTFIELD) {
...
mHeightField = world::getSingleton().mPhysicsSDK->createHeightField(heightFieldDesc);
...
}


Then finally.

heightmapShape::~heightmapShape() {
world::getSingleton().mPhysicsSDK->releaseHeightField(mHeightField);
}


See if that works for you, as I don't know if getting the heightmap from the Shape Description is the correct way.

luis

25-01-2007 14:25:17

Thanks for replying :)
I had a quick look, and for some reason the heightfield pointer isn't being stored anywhere. So it should go:
it is beeing stored here (from the original @imtrobin's code):
NxHeightField *hf = world::getSingleton().mPhysicsSDK->createHeightField(heightFieldDesc);

// Data has been copied, we can free our buffer
delete [] heightFieldDesc.samples;

mShapeDesc.heightField = hf;


so i belive the problem could be in the order i'm destroing the heightmap or that there is something else still holding the old reference (a dangling pointer in that case)
:(
another idea ?

imtrobin

25-01-2007 15:17:37

Hmm, are you able to quit the program without crashing? I haven't tried deleting without quitting, If so, there could be a dependency which needs fixing.

luis

25-01-2007 15:30:06

well, right now i'm just trying to quit the app but deleting the heightmap manually before deleting the world... :)
If i'm could do that i'll able to switch between heightmaps also....


I took an nxOgre example (105) and in the stop() method i have something like this:

stop()
{
mScene->destroyBody( "heightmapNameHere" )
delete mHeightmapShape; // its destructor will call releaseHeightField (crash is here)
delete mWorld;
}


is it right ?
in wich order are you doing this ?

betajaen

25-01-2007 16:44:37

I think the reason why it crashes, because body "thinks" it's a heightmap shape, and casting it as so. Which would cause a crash.

The trick is add "SHAPE_HEIGHTFIELD" to the enum in shape, and add some code in to the body _addShape, deleteShape and so on to handle it.

luis

25-01-2007 21:10:12

I think the reason why it crashes, because body "thinks" it's a heightmap shape, and casting it as so. Which would cause a crash.

you're right !! thanks :)
in fact i had two problems, the one you pointed out and that i was redeclaring the same pointer arghhh :(

The trick is add "SHAPE_HEIGHTFIELD" to the enum in shape, and add some code in to the body _addShape, deleteShape and so on to handle it.
it is really a big hack in the library..... are you going to add this class in the next version ?

I've implemented another solution: inherit from heightfieldShape, but i had a lot of troubles, neither heightfieldShape nor shape have default constructors so i had to add them....
Is there another way to add new shapes ? something like "user defined shape" ?

thanks again!
BTW: Betajaen i think you dont sleep... even with your eyes open :)

betajaen

25-01-2007 21:24:18

I suppose the easiest way would be to remove the old heightMapShape code (my code) and replace it with the newer heightFieldShape, then rename it as a heightmapShape. NxOgre would be none the wiser.

If imtrobin is willing, I'll add replace it with the older code. But stand by for a big "I want your patches" thread soon, where it can be submitted.

Also I don't sleep often, and if I do it's almost definitely with my eyes open ;)

luis

25-01-2007 22:26:28

I suppose the easiest way would be to remove the old heightMapShape code (my code) and replace it with the newer heightFieldShape, then rename it as a heightmapShape. NxOgre would be none the wiser.
great :)
but what do you think about letting the user inherit from those classes also ?

betajaen

25-01-2007 22:47:42

I wouldn't. Not only NxOgre treats its at it's inherited shape, you can encounter some problems with member variables and such things of sub-classes when it's treated as it's inherited class.

imtrobin

26-01-2007 04:32:33

If imtrobin is willing, I'll add replace it with the older code. But stand by for a big "I want your patches" thread soon, where it can be submitted.


Sure please do. I haven't checked out RC3, I assumed you have integrated the code in.

luis

26-01-2007 08:09:31

ok, i understand, but would be good to be able for the users to add shapes without hacking the library.....

In my case for example i would like to have control of the flags NX_SF_FEATURE_INDICES & NX_SF_VISUALIZATION and the color used to get the height from the image and things like that...

I dont know well the architecture of nxOgre but, is there a way to make 'custom' shapes ?

betajaen

26-01-2007 11:47:50

It's the same process as adding an official shape. Make sure the class is set up like the others, and the body shape handling code (there are 3-4 methods) recognises the shape.