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?
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.
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.
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.
@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.
@imtrobin
Im using your code too, because the heightfield shape in rc3 had weird constructor parameters.
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.
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.
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.
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
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.
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.