Anybody use PhysX and ETM?

kungfoomasta

13-02-2008 18:24:13

With some reference code I'm able to create a heightfield shape, however its not aligned with the Terrain. I've been trying to find out how to determine the offset of the shape so that it matches the terrain's position, but haven't been able to do it correctly. Here is the reference code, since I don't have access to mine. They're very similar:


//Load as image
String heightMapFile = config.getSetting("Heightmap.image");
unsigned int heightMapSize = StringConverter::parseInt(config.getSetting("Heightmap.raw.size"));
int heightMapBPP = StringConverter::parseInt(config.getSetting("Heightmap.raw.bpp"));

PixelFormat heightMapFormat;
if (heightMapBPP == 1) heightMapFormat = PF_L8;
else if (heightMapBPP == 2) heightMapFormat = PF_L16;

Image heightMap;
DataStreamPtr dat = ResourceGroupManager::getSingleton().openResource(heightMapFile);
/*NxU16 *heightMapPixels = new NxU16[dat->size()];
dat->read(heightMapPixels, dat->size());
dat->close();*/
heightMap.loadRawData(dat, heightMapSize, heightMapSize, heightMapFormat);
//heightMap.load(heightMapFile, ResourceGroupManager::getSingleton().getWorldResourceGroupName());
//heightMap.load("Lost_HF.png", ResourceGroupManager::getSingleton().getWorldResourceGroupName());

//Copy image data into PhysX as a heightfield
NxHeightFieldDesc heightFieldDesc;

heightFieldDesc.nbColumns = (NxU32)heightMapSize;//heightMap.getHeight();
heightFieldDesc.nbRows = (NxU32)heightMapSize;//heightMap.getWidth();
heightFieldDesc.verticalExtent = -1000;
heightFieldDesc.convexEdgeThreshold = 0;

//Allocate storage for data
heightFieldDesc.samples = new NxU32[heightFieldDesc.nbColumns * heightFieldDesc.nbRows];
heightFieldDesc.sampleStride = sizeof(NxU32);

NxU8* currentByte = (NxU8*)heightFieldDesc.samples;

//NxU16 *pixelDat = heightMapPixels;
for (NxU32 row = 0; row < heightFieldDesc.nbRows; row++)
{
for (NxU32 column = 0; column < heightFieldDesc.nbColumns; column++)
{
NxHeightFieldSample* currentSample = (NxHeightFieldSample*)currentByte;

NxI16 height = heightMap.getColourAt(row, column, 0).r * 0x1FFF;
//NxI16 height = 0xFFFF - (*pixelDat++);// * 0x1FFF;
//height = (0x1FFF * height / 0xFFFF);

currentSample->height = height;
currentSample->materialIndex0 = 0;
currentSample->materialIndex1 = 0;

currentSample->tessFlag = 0;

currentByte += heightFieldDesc.sampleStride;
}
}

//delete[] heightMapPixels;

heightField = Core::getSingleton().physicsSDK->createHeightField(heightFieldDesc);

//Data has been copied, buffer can be deleted
delete[] heightFieldDesc.samples;

//Set up shape descriptor
NxVec3 size;
size.x = StringConverter::parseReal(config.getSetting("PageWorldX"));
size.y = StringConverter::parseReal(config.getSetting("MaxHeight"));
size.z = StringConverter::parseReal(config.getSetting("PageWorldZ"));

NxHeightFieldShapeDesc heightFieldShapeDesc;
heightFieldShapeDesc.heightField = heightField;
heightFieldShapeDesc.shapeFlags = NX_SF_FEATURE_INDICES;
heightFieldShapeDesc.heightScale = size.y / 0x1FFF;
heightFieldShapeDesc.rowScale = size.x / NxReal(heightFieldDesc.nbRows-1);
heightFieldShapeDesc.columnScale = size.z / NxReal(heightFieldDesc.nbColumns-1);
//heightFieldShapeDesc.meshFlags = NX_MESH_SMOOTH_SPHERE_COLLISIONS;
heightFieldShapeDesc.materialIndexHighBits = 0;
heightFieldShapeDesc.holeMaterial = 2;

this->xScale = heightFieldShapeDesc.rowScale;
this->yScale = heightFieldShapeDesc.heightScale;
this->zScale = heightFieldShapeDesc.columnScale;
terrainBounds.left = 0; terrainBounds.right = size.x;
terrainBounds.top = 0; terrainBounds.bottom = size.z;

NxBodyDesc bodyDesc;
bodyDesc.angularDamping = 0.5f;
bodyDesc.flags |= NX_BF_KINEMATIC;

NxActorDesc actorDesc;
actorDesc.shapes.pushBack(&heightFieldShapeDesc);
actorDesc.body = &bodyDesc;
actorDesc.density = 1.0f;
actor = WorldManager::getSingleton().physicsScene->createActor(actorDesc);


Basically you create a Heightfield object via the Heightfield descriptor object, and then use it to create a HeightfieldShape object, which ends up being used to create the Actor.

When rendering the debug geometry, PhysX only shows me a red box that outlines the heightfield space, I don't see any terrain wireframe shape. I've been able to get the box to closely match the terrain's position on the z/x planes, but the objects come to rest in air, telling me that the hieghtfield shape is not sync'd on the Y plane. (heightfield is higher than the terrain)

The shape's offset is set by:

heightFieldShapeDesc.localpose.t = NxVec(x,y,z);

For x and z, I have used (terrain width / 4) and (terrain depth / 4). I would have thought this to be ( x / 2) instead of 4, but it doesn't look accurate. I'm wondering if Terrain Scale has any part to play in determining the offset.

For my particular scenario, the Terrain dimensions span from (-3000,-400,-3000) to (3000,400,3000).

The reference code is based on TSM, which has a corner at 0,0,0, unlike ETM. (ETM's method is better IMO)

So my main questions are:
- Has anybody used ETM and PhysX? Can anybody shed light on how to derive the shape's offset?
- Does any of the code look incorrect? Maybe the heightfield is incorrectly produced, maybe ETM and TSM have different winding, which could be a reason why objects don't roll over the terrain as I would expect?

SongOfTheWeave

13-02-2008 22:48:12

Yes! This was such a headache when I was getting mine to work!

I used the terrainInfo and split it up into tiles the size of an ETM tile (so if tilesize is 32 and your terrain is 128x128 there are 16 heightfield actor tiles arranged 4x4.)

iirc the world position of my tiles is at 0,0 (and ( tilesize x tileindexX, tilesize x tileindexY) for subsequent tiles.)

columnScale is the ratio of worldcoords/vertex indicies, i.e. how far apart, in ogre coords each ETM vertex is from the next. NOT the width of your terrain.

Also, make sure you're itterating in the right order, I ended up with my physics heightfield 90 degrees off from my visual terrain until I spotted that oops.

----

Another gotcha is that PhysX takes it's height data as an array of signed 16 bit integers. So... you have to convert your ogre coord vertical data down to a value between 0.0 and 1.0 then expand it out over the range from 0 and 32767 (or -32767 and 32768) then your height scale or whatever it's called is VERTICAL_OGRE_EXTENT / 32767, in other words, it is how much world coord height that each unit of the unsigned int16 physX height value is worth.

Since your Y span is 800 this is going to be a small number.

----

In conclusion, good luck, this is a headache but it can be made to work!

kungfoomasta

14-02-2008 08:23:37

Arg, I'm still not getting the results I want. Are you able to render the Terrain debug shape? I can render spheres, capsules, and cubes, but I only get the Terrain bounding box. Not exactly helpful. :?

Is it possible you could email me or post up a link to a snippet of code you used to get PhysX working with ETM? Are you using 16bit .raw files also?

I know its asking a lot, but if you gave me the code to populate the PhysX description objects, along with your heightmap/texturemap, I would be able to figure everything out easily.

SongOfTheWeave

14-02-2008 09:19:19

I used the terrainInfo and split it up into tiles the size of an ETM tile (so if tilesize is 32 and your terrain is 128x128 there are 16 heightfield actor tiles arranged 4x4.)

My heightmap is dynamic.

SongOfTheWeave

14-02-2008 09:23:43

Arg, I'm still not getting the results I want. Are you able to render the Terrain debug shape? I can render spheres, capsules, and cubes, but I only get the Terrain bounding box. Not exactly helpful. :?

I'm not rendering anything from PhysX. I just did the math and read the PhysX docs (like, 7 times.)

There's a heightfield sample program in the SDK training programs.

----

Again, I didn't use the heightmap image. TerrainInfo will spit you out the heightmap as an array of floats.

----

Edit: Just checked and the PhysX training programs that I used when figuring out how to do this are:

Chapter 1 - Rigid Bodies
- Lesson 117 - Heightfield Shapes
Chapter 6 - Gameplay Physics
- Lesson 601 - Heightfields

Though iirc 117 covered just about all I needed to know.

----

Well, it's time for me to sleep some. Reply again if you haven't gotten it yet and I'll dig up my ETM to PhysX heightfield code for you tomorrow when I get to work.

The pay ain't great at the moment, but the nice thing about being self-employed is being able to work between noon and 5am. Not a fan of the 9-5...

kungfoomasta

14-02-2008 18:21:16

I'm reviewing the tutorial code, and the first thing to pop out at me is this line of code:

heightFieldShapeDesc.shapeFlags = NX_SF_FEATURE_INDICES | NX_SF_VISUALIZATION;

I bet that's why I can't see my terrain! :x

Hopefully this solved that issue, and I can more accurately solve my problem.

If you could paste your snippet that populated the heightfield data from ETM float array, that would be very helpful. I will have to do this also, since my gameplay requires terrain deformation in real time, and the shape needs to match it.

[edit]

Do you know what is meant by the following 2 lines?

NxI16 height = (NxI32)(0.5 * thirtyTwoKb * (NxMath::sin(5.0f*NxPiF32*s) + NxMath::cos(5.0f*NxPiF32*t)));


heightFieldShapeDesc.heightScale = size.y / sixtyFourKb;

I don't really know what 32kb and 64kb are used for, and its not explained in L118.

Thanks for your help so far.

[/edit]

SongOfTheWeave

14-02-2008 22:15:43

I'm reviewing the tutorial code, and the first thing to pop out at me is this line of code:

heightFieldShapeDesc.shapeFlags = NX_SF_FEATURE_INDICES | NX_SF_VISUALIZATION;

I bet that's why I can't see my terrain! :x


Yeah, that looks like it. I never used that code though, since I didn't have PhysX displaying anything.

If you could paste your snippet that populated the heightfield data from ETM float array, that would be very helpful. I will have to do this also, since my gameplay requires terrain deformation in real time, and the shape needs to match it.

[edit]

Do you know what is meant by the following 2 lines?

NxI16 height = (NxI32)(0.5 * thirtyTwoKb * (NxMath::sin(5.0f*NxPiF32*s) + NxMath::cos(5.0f*NxPiF32*t)));


heightFieldShapeDesc.heightScale = size.y / sixtyFourKb;

I don't really know what 32kb and 64kb are used for, and its not explained in L118.[/edit]


yeah, what they're doing here is procedurally generating the heightfield as the following function of x and z:

y = sin(x) + cos(z)

Where y is the height and omitting a few constants.

This function has a subdomain of -1.0 to 1.0, so they're expanding that out to fill the values available in a signed 16 bit int, -32768 to 32767, which is where those crypticly named variables come in.

"32kb" is the max value of the signed 16 bit int while "64kb" is the range of a signed 16bit int.

So if you have a height value that ranges from -1.0 to 1.0 you multiply it by MAX_VALUE (which the example calls "32kb") to transform it into the PhysX int height value.

Then to get how much world coord height each unit in the PhysX signed 16bit int height is worth, you divide your world coord height range (in your example it is 800) by the max signed 16bit int range (which the example calls "64kb"). This is your height scale.

TERRAIN_HEIGHT_SPAN_OGRECOORDS / 16BIT_INT_RANGE = height scale

Then PhysX internally checks each 16bit height value and multiplies that by height scale to get the world coord position of each vertex.

----

whew. That's a mouthful.

[edit]
I just looked at my code and remembered my reason for not just posting it in the first place... I think it might just confuse matters further because there are references to classes and constants defined elsewhere.

What it comes down to is this inside a (nested if you're doing tiles) for loop:

float fOgreSpaceHeight = terraininfo->getHeightAt((iZTile * TERRAIN_TILESIZE * TERRAIN_SCALE) + iZ * TERRAIN_SCALE,
(iXTile * TERRAIN_TILESIZE * TERRAIN_SCALE) + iX * TERRAIN_SCALE);

// Collapse ogrespace height to a value between 0.0 and 1.0 then expand the range to a value between 0 the max signed 16bit int
NxI16 i16Height = (NxI32) fOgreSpaceHeight / fSampleHeightToOgreSpace;
currentSample->height = i16Height;


where
fSampleHeightToOgreSpace is my ogre coord height extent expanded out over the int range I'm using
TERRAIN_TILESIZE is how many verticies there are per tile I'm splitting this into
TERRAIN_SCALE is the ogre coord width between two adjacent ETM verticies

These are my nested for loop counters:
iXTile, iZTile are the current X or Z tile I'm on
iX, iZ are the current vertex within the tile that I'm on

And yeah, I know my X and Z are in the wrong order, but they're in the wrong order in my loops too so it works out <.< I should just change the names of the variables.
[/edit]

kungfoomasta

15-02-2008 06:11:44

Ug, this is going to be painful. I was able to get the Terrain Shape rendering, however if my heightmap height/width is more than 100 x 100, the app crashes. (the FPS is less than 1)

I think I will have to go the Tile route also. If I don't try to render the huge heightmap shape the app doesn't crash. (NX_SF_VISUALIZATION) However the terrain does not match up and I have to find out what it looks like..

Somewhere in your code you must be calling

heightFieldShapeDesc.localPose.t = ...

or setting the position of the actor after creation. Can you find out what you're doing here?

Also, the TerrainManager::createTerrain function defaults TileSize to 33. What tile sizes are you using? Assuming a 1024 x 1024 heightmap, that's 1024 tiles, isn't it? I'm curious what kinds of row length and col length you use for your terrain, and how your performance is with this.

[edit]

fSampleHeightToOgreSpace is my ogre coord height extent expanded out over the int range I'm using

I'm not sure I understand. Does the height value need to be between 0 and 16 bit integer, assuming 16 bit raw?

SongOfTheWeave

15-02-2008 09:24:00

actorDesc.globalPose.t = NxVec3(iZTile * TERRAIN_TILESIZE * TERRAIN_SCALE, 1, iXTile * TERRAIN_TILESIZE * TERRAIN_SCALE);

Don't remember why my Y is 1 not 0... I think I get a bit of rounding error in the float to int transition and that compensates for it a little.

in essence my global pose is (0, 0, 0), or the bottom left for each tile.

----

If you don't know what I'm talking about with the 16bit signed int you need to seriously read the PhysX docs. The .doc for lesson 117 and the API entry on HeightFieldDesc and all the other zillion heightfield classes.

An NxI16 is a 16 bit signed int
An NxI32 is a 32 bit signed int

The ones with U's rather than I's are unsigned.

SongOfTheWeave

15-02-2008 09:38:29

Also, the TerrainManager::createTerrain function defaults TileSize to 33. What tile sizes are you using? Assuming a 1024 x 1024 heightmap, that's 1024 tiles, isn't it? I'm curious what kinds of row length and col length you use for your terrain, and how your performance is with this.

a 1024 x 1024 heightmap at a tilesize of 33 is 32x32 tiles, so yes, 1024 tiles total.

Why are we assuming a 1024x1024 heightmap? that's huuuuge.

----

Let me restate:
- Row scale and column scale is the ogre distance between verticies.
Again.
- Row scale and column scale is the ogre coordinate distance between each row or column of verticies.

If your terrain is 32x32 and your terrain extents are 320.0x320.0 your row and column scale is 10.0

NxHeightFieldDesc::nbColumns and nbRows is the NUMBER of rows and columns. if your terrain is 128x128 and you're doing it all in one object, they are 129 and 129 (Extra vertex on the edge)

If you're splitting it into tiles your nbColumns and nbRows is TILE_SIZE + 1. for my 32 vert tiles my nbColumns and rows is 33 (the nb means number.)

----

I'm beginning to think I may have misinterpreted some of what you said, if so, forgive my redundance.

My terrains are around 128x128, 256x256. I haven't done a really big one in a while but when I make super big ones (I made a 3200x3200 once) it chokes on lightmap generation so badly I didn't want to think about physics.

I imagine if you're planning to be working with terrains around 1024x1024 more often then not a tilesize of 64 or 128 might be more appropriate. I haven't tested whether more tiles has a large impact on performance on top of the performance hit from simply having more verticies.

kungfoomasta

15-02-2008 18:33:01

So it sounds like you generally work with 16 or 64 tiles in your scene. Its odd that toggling the visualization flag chokes the application, I guess its quite a lot of overhead to request the vertex/index information from each heightfield vertex every frame.

I'm going to try to avoid going the Tile route if possible, my next step is to create a 129 x 129 dummy heightmap, so I can see if my heightfield shape is oriented wrong.

Thanks for posting the code to set your global pose of your shapes. :)

Just to confirm, I believe currentSample->height needs to be a value between 0 and 65536, assuming use of a 16 bit png or raw file.

SongOfTheWeave

16-02-2008 05:57:48

Just to confirm, I believe currentSample->height needs to be a value between 0 and 65536, assuming use of a 16 bit png or raw file.

Between - 32768 and 32767 I believe.

It is signed: +- 2^15 since one bit is used as the sign bit.

kungfoomasta

16-02-2008 07:33:06

SongOfTheWeave, do you accept PayPal? This is ridiculous, I've been banging my head against this code for the last 3 days, and I can't figure it out! :x

This is the dummy image I am using for the texture and heightmap. .raw heightmap is 129 x 129:

http://www.petwars.net/downloads/images/OgreForum/heightfield_test_textures.zip



And this is the debug visualization of the heightfield shape:








As you can see, not only is the shape not aligned with the terrain, but only 1 corner gets the correct data.



Creation of the Heightfield:


void CTerrainShape::_createHeightfield()
{
Ogre::Image heightMap;
Ogre::DataStreamPtr dat = Ogre::ResourceGroupManager::getSingleton().openResource(mTerrainComponent->getHeightmapName());
heightMap.loadRawData(dat, mTerrainComponent->getHeightmapWidth(), mTerrainComponent->getHeightmapHeight(), Ogre::PF_L16);

//Copy image data into PhysX as a heightfield

mNxHeightFieldDesc.nbColumns = (NxU32)mTerrainComponent->getHeightmapHeight();
mNxHeightFieldDesc.nbRows = (NxU32)mTerrainComponent->getHeightmapWidth();
mNxHeightFieldDesc.verticalExtent = -1000;
mNxHeightFieldDesc.convexEdgeThreshold = 0;

//Allocate storage for data
mNxHeightFieldDesc.samples = new NxU32[mNxHeightFieldDesc.nbColumns * mNxHeightFieldDesc.nbRows];
mNxHeightFieldDesc.sampleStride = sizeof(NxU32);

NxU8* currentByte = (NxU8*)mNxHeightFieldDesc.samples;
ET::TerrainInfo* i = mTerrainComponent->mTerrainInfo;
float xScale = i->getScaling().x;
float zScale = i->getScaling().z;
float yScale = i->getScaling().y / 65536.0f;

for (NxU32 row = 0; row < mNxHeightFieldDesc.nbRows; row++)
{
for (NxU32 column = 0; column < mNxHeightFieldDesc.nbColumns; column++)
{
NxHeightFieldSample* currentSample = (NxHeightFieldSample*)currentByte;

float ogreSpaceHeight = i->getHeightAt(row * xScale,column * zScale);
NxI16 height = ogreSpaceHeight / yScale;

currentSample->height = height;
currentSample->materialIndex0 = 0;
currentSample->materialIndex1 = 0;

currentSample->tessFlag = 0;

currentByte += mNxHeightFieldDesc.sampleStride;
}
}

mHeightField = PhysicsManager::getSingleton().createHeightField(mNxHeightFieldDesc);

//Data has been copied, buffer can be deleted
delete mNxHeightFieldDesc.samples;
}


Creation of the Actor:


void CTerrainShape::_createActor()
{
Ogre::AxisAlignedBox aab = mTerrainComponent->getDimensions();
Ogre::Vector3 s = aab.getSize();

NxVec3 size;
size.x = s.x;
size.y = s.y;
size.z = s.z;

NxHeightFieldShapeDesc heightFieldShapeDesc;
heightFieldShapeDesc.heightField = mHeightField;
heightFieldShapeDesc.shapeFlags = NX_SF_FEATURE_INDICES | NX_SF_VISUALIZATION;
heightFieldShapeDesc.heightScale = size.y / 65536.0f;
heightFieldShapeDesc.rowScale = size.x / NxReal(mNxHeightFieldDesc.nbRows-1);
heightFieldShapeDesc.columnScale = size.z / NxReal(mNxHeightFieldDesc.nbColumns-1);
//heightFieldShapeDesc.meshFlags = NX_MESH_SMOOTH_SPHERE_COLLISIONS;
heightFieldShapeDesc.materialIndexHighBits = 0;
heightFieldShapeDesc.holeMaterial = 2;
// heightFieldShapeDesc.localPose.t = NxVec3(-1500,-100,-1500);
// heightFieldShapeDesc.localPose.t = NxVec3(-(s.x / 4.0),-(s.y * heightFieldShapeDesc.heightScale),-(s.z / 4.0));
// heightFieldShapeDesc.localPose.t = NxVec3(-(s.x / 4.0),-(s.y / 2.0),-(s.z / 4.0));

NxBodyDesc bodyDesc;
bodyDesc.angularDamping = 0.5f;
bodyDesc.flags |= NX_BF_KINEMATIC;

NxActorDesc actorDesc;
actorDesc.shapes.pushBack(&heightFieldShapeDesc);
actorDesc.body = &bodyDesc;
actorDesc.density = 1.0f;
mActor = mComponentManager->getWindow()->getNxScene()->createActor(actorDesc);
}


If you or anybody could look at this code and find out what I'm doing wrong, I'd be extremely grateful.

SongOfTheWeave

16-02-2008 08:16:47

Lemme answer the obvious bug here before I read your code and find the more subtle one.

Your global pose is set to the center of your ETM terrain. set it to 0,0,0.

[Edit1]
Editing as I look over this.

I've no idea what results you're going to get from this... though it looks like it's working in the portion of your map that erm, well, maps your terrain.

float yScale = i->getScaling().y / 65536.0f;

The comment for TerrainInfo::getScaling is "Retrieves the scaling of one vertex in Ogre units."

I'm not sure how that's relevant in the Y axis considering the vertex domain is only the x/z plane.
[/Edit1]

[Edit2]
bodyDesc.flags |= NX_BF_KINEMATIC;

You don't really want your terrain to be kinematic do you?

----

Where are you setting the global pose?

Add this line

actorDesc.globalPose.t = NxVec3(0, 0, 0)

syedhs

16-02-2008 15:38:41

It has been sometimes that I get PhysX to work with heightmap - the input to the function is the heightmap file eg heightmap.raw, nothing else. It works flawlessly with TSM, but with ETM you may need to adjust the offset, I *think*. Let me know if you need to see the code :wink:

And another, yeah sometimes the heightmap debug is not rendered properly maybe due to lots of triangles drawn. I however find using Ageia Remote Debugger far more intuitive :) so you may want to check it out.

kungfoomasta

16-02-2008 18:54:19

syedhs, that would help me out a lot if you could post your code. :)

I could set manually set the offset for the actor or shape to match this specific terrain, but I really want it to be automatically derived. I'm following a Component Based Design, and using this I can create mutliple terrains in multiple locations, even move the terrain around. Sounds odd but I'm keeping the terrain modular, so that its just another component, like a mesh, or light, etc.

SongOfTheWeave

16-02-2008 21:27:27

I could set manually set the offset for the actor or shape to match this specific terrain, but I really want it to be automatically derived. I'm following a Component Based Design, and using this I can create mutliple terrains in multiple locations, even move the terrain around. Sounds odd but I'm keeping the terrain modular, so that its just another component, like a mesh, or light, etc.

Then use ETMs getOffset function and set the globalpose to that.

I'm using a component based object system too. You still need to set the global pose of your actor. In the code you posted you didn't set the global pose at all and all the local pose lines were commented out.

kungfoomasta

17-02-2008 09:48:33

You're right, I realized that after reading your post. I added this in now, it seems that the x/z alignment is correct, but I'm not sure about the y component.

Shape AABB 1:



Shape AABB 2:





void CTerrainShape::_createActor()
{
Ogre::AxisAlignedBox aab = mTerrainComponent->getDimensions();
Ogre::Vector3 s = aab.getSize();

NxVec3 size;
size.x = s.x;
size.y = s.y;
size.z = s.z;

NxHeightFieldShapeDesc heightFieldShapeDesc;
heightFieldShapeDesc.heightField = mHeightField;
heightFieldShapeDesc.shapeFlags = NX_SF_FEATURE_INDICES | NX_SF_VISUALIZATION;
heightFieldShapeDesc.heightScale = size.y / 65536.0f;
heightFieldShapeDesc.rowScale = size.x / NxReal(mNxHeightFieldDesc.nbRows-1);
heightFieldShapeDesc.columnScale = size.z / NxReal(mNxHeightFieldDesc.nbColumns-1);
//heightFieldShapeDesc.meshFlags = NX_MESH_SMOOTH_SPHERE_COLLISIONS;
heightFieldShapeDesc.materialIndexHighBits = 0;
heightFieldShapeDesc.holeMaterial = 2;
heightFieldShapeDesc.localPose.t = NxVec3(0,0,0);
Ogre::Vector3 offset = mTerrainComponent->mTerrainInfo->getOffset();
// heightFieldShapeDesc.localPose.t = NxVec3(offset.x,offset.y,offset.z); // AABB1
heightFieldShapeDesc.localPose.t = NxVec3(offset.x,0,offset.z); // AABB2

// NxBodyDesc bodyDesc;
// bodyDesc.angularDamping = 0.5f;
// bodyDesc.flags |= NX_BF_KINEMATIC;

NxActorDesc actorDesc;
actorDesc.shapes.pushBack(&heightFieldShapeDesc);
// actorDesc.body = &bodyDesc;
actorDesc.density = 1.0f;

if(mOwner->hasComponent("CLocation"))
{
mLocation = dynamic_cast<CLocation*>(mOwner->getComponent("CLocation"));
Vector3 p = mLocation->getPosition();
actorDesc.globalPose.t = NxVec3(p.x,p.y,p.z);
}
else
actorDesc.globalPose.t = NxVec3(0,0,0);

mActor = mComponentManager->getWindow()->getNxScene()->createActor(actorDesc);
}


I think setting the y offset to 0 puts the terrain into the correct region, but I can't be sure until the heightfield generation is correct. Not sure why that isn't being populated correctly... :?

kungfoomasta

17-02-2008 20:03:28




void CTerrainShape::_createHeightfield()
{
Ogre::MemoryDataStreamPtr mRawData;
mRawData.setNull();

Ogre::DataStreamPtr dat = Ogre::ResourceGroupManager::getSingleton().openResource(mTerrainComponent->getHeightmapName());
mRawData = Ogre::MemoryDataStreamPtr(new Ogre::MemoryDataStream(mTerrainComponent->getHeightmapName(), dat));

// Ogre::Image heightMap;
// Ogre::DataStreamPtr dat = Ogre::ResourceGroupManager::getSingleton().openResource(mTerrainComponent->getHeightmapName());
// heightMap.loadRawData(dat, mTerrainComponent->getHeightmapWidth(), mTerrainComponent->getHeightmapHeight(), Ogre::PF_L16);

//Copy image data into PhysX as a heightfield

mNxHeightFieldDesc.nbColumns = (NxU32)mTerrainComponent->getHeightmapHeight();
mNxHeightFieldDesc.nbRows = (NxU32)mTerrainComponent->getHeightmapWidth();
mNxHeightFieldDesc.verticalExtent = -1000;
mNxHeightFieldDesc.convexEdgeThreshold = 0;

//Allocate storage for data
mNxHeightFieldDesc.samples = new NxU32[mNxHeightFieldDesc.nbColumns * mNxHeightFieldDesc.nbRows];
mNxHeightFieldDesc.sampleStride = sizeof(NxU32);

NxU8* currentByte = (NxU8*)mNxHeightFieldDesc.samples;
ET::TerrainInfo* i = mTerrainComponent->mTerrainInfo;
std::vector<float> heightData = i->getHeightmapData();

NxU32 index = 0;
NxI16 height = 0;
const Ogre::uchar* pSrc = mRawData->getPtr();

for (NxU32 row = 0; row < mNxHeightFieldDesc.nbRows; row++)
{
for (NxU32 column = 0; column < mNxHeightFieldDesc.nbColumns; column++)
{
NxHeightFieldSample* currentSample = (NxHeightFieldSample*)currentByte;

index = row + (column * mNxHeightFieldDesc.nbRows);
index *= 2;
height = pSrc[index];
height += pSrc[index + 1] * 256.0;
height -= 32768;

currentSample->height = height;
currentSample->materialIndex0 = 0;
currentSample->materialIndex1 = 0;

currentSample->tessFlag = 0;

currentByte += mNxHeightFieldDesc.sampleStride;
}
}

mHeightField = PhysicsManager::getSingleton().createHeightField(mNxHeightFieldDesc);

//Data has been copied, buffer can be deleted
delete[] mNxHeightFieldDesc.samples;
}


:D

Now I need to figure out how to correctly populate the heightmap using the Terrain's vector of floats, instead of the raw image. The above code was taken from NxOgre, so I have no idea what its actually doing.

SongOfTheWeave

18-02-2008 04:23:32

Replace all the height lines in the for loop in your last post with:


currentSample->height = ((terrainInfo->getHeightAt(ogreSpaceX, ogreSpaceZ) / OgreSpaceYExtent) * (2^16)) - (2^15);

Obviously pseudocode, you'll have to fill in the values for 2^16 and such.

The reason for it is you're spreading out your subdomain (in colloquial terms, the "range" of your function) over a span of 0 - 2^16 then ofsetting it to fit into the signed 16 bit int, which ranges from -2^15 to 2^15 (which is the ~32000 number you keep seeing.)

kungfoomasta

18-02-2008 19:49:41

Not sure I follow that code. I'm guess ogreSpaceX and ogreSpaceZ change for each iteration within the double for loop? I don't understand how the getHeightAt function works, you supply x and z ogre space coordinates? That doesn't seem very helpful..

I would imagine using the TerrainInfo::at function, or TerrainInfo::getHeightmapData and manually iterating through the heightmap values.

Am I right in assuming grayscale images don't contain the RBGA channels? Is it just one channel that goes from 0 - 2^16? (65536)

So basically I want to process values within range 0 - 65536, and convert these into accurate NxI16 values, to assign to currentSample->height. Aren't the height values supposed to be between 0 - 65536 anyway? Why do these values need to be modified? If you had to summarize what currentSample->height represents, what would you say?

SongOfTheWeave

19-02-2008 00:28:40

Not sure I follow that code. I'm guess ogreSpaceX and ogreSpaceZ change for each iteration within the double for loop? I don't understand how the getHeightAt function works, you supply x and z ogre space coordinates? That doesn't seem very helpful..

to get ogreSpaceX and ogreSpaceZ just do the following in your for loop, where x and z are your counters that describe what vertex you're at.

ogreSpaceX = x * COLUMN_SCALE;
ogreSpaceZ = z * ROW_SCALE;


Or the other way around depending on if you've called X row and Z column. though It probably doesn't matter since your row and column scales are probably the same.

I would imagine using the TerrainInfo::at function, or TerrainInfo::getHeightmapData and manually iterating through the heightmap values.

Am I right in assuming grayscale images don't contain the RBGA channels? Is it just one channel that goes from 0 - 2^16? (65536)

So basically I want to process values within range 0 - 65536, and convert these into accurate NxI16 values, to assign to currentSample->height. Aren't the height values supposed to be between 0 - 65536 anyway? Why do these values need to be modified?


I think I used getHeightAt because I wasn't entirely sure what TerrainInfo::at returned. I think it's a float, so I don't know what you're talking about when you went on to talk about image pixel formats.

If you had to summarize what currentSample->height represents, what would you say?

It represents the height of the vertex represented by currentSample.

kungfoomasta

19-02-2008 01:07:43

The float values are in range [0.0 - 1.0].

I think I've tried this before, don't know why it didn't work, but I will try it again. Since I'm not yet using Tiles, I will just iterate through the heightmap data and multiply it by (2^16 - 1)

std::vector<float> heightmapData = mTerrainInfo.getHeightmapData();
for(std::vector<float>::iterator it = heightmapData.begin(); it != heightmapData.end(); ++it)
{
...
currentSample->height = static_cast<NxI16>( ((*it) * 65535) - 32768);
...
}

I think my problem earlier was that I was generating all positive values, when the height values should be in range [-32767 - 32767]

kungfoomasta

20-02-2008 18:52:42

Just to make it clear to anybody reading the thread, I was able to create the heightfield shape using data directly from ETM. :D

@SongOfTheWeave:

Is each tile its own PhysX actor, or did you make a multi-shape actor? A multi-shape actor is much easier to organize, but I'm wondering which method is more efficient.

SongOfTheWeave

21-02-2008 05:55:19

@SongOfTheWeave:

Is each tile its own PhysX actor, or did you make a multi-shape actor? A multi-shape actor is much easier to organize, but I'm wondering which method is more efficient.


I made multiple actors and just slapped them into a vector for safekeeping (and so I could delete them later.)

I don't know which is more efficient though.

syedhs

04-03-2008 19:38:37

Hey Kungfoomasta, seems like you have long be finished your Ageia ETM heightmap thingy - I really wanted to paste the code I had promised earlier but seeing you are progressing quite fast, I think you will be better off with your own way. Mine is very crude and the only good thing out of it is it works :wink:

kungfoomasta

04-03-2008 19:46:13

Such fast response time! :wink: It took a while to sink in, but thankfully SongOfTheWeave put up with me and helped me get to a solution. My FPS sank down to about 60 FPS on my laptop, I hope using a multishape actor will increase the performance some. I cap my FPS to 60, so its not a very accurate analysis of the performance loss due to the terrain shape, but I'd still like to create the terrain shape on a tile by tile basis.

NoodlesOnMyBack

07-04-2008 05:39:54

Sorry to pop up this thread but i had to say thank you kungfoomasta, i found the solution to this problem with your code and of course to SongOfTheWeave too :)

leonardoaraujo.santos

28-07-2008 21:26:46

Sorry guys could you post some working code with ETM and NxOgre?
I'm having some problems with this :roll:

SongOfTheWeave

29-07-2008 22:16:52

Sorry guys could you post some working code with ETM and NxOgre?
I'm having some problems with this :roll:


To get an answer you really need to ask a question. It's very unlikely that someone is going to just post any and all code having to do with ETM and NxOgre and it's even more unlikely that if they did, it would be helpful to you.

If your question is specifically about ETM and NxOgre, please start a new thread concerning NxOgre specifically. This thread is about using PhysX directly and has no information about specific wrappers (such as NxOgre.)