# How to load and render PhysX 3.3 heightfield

I'm trying to load a PhysX 3.3 heightmap and render it with OpenGL using glm and glfw, but so far it doesn't seem to be working. I'm basing my code on the PhysX samples, and am also using FreeImage to load the heightmap texture (which is a .TGA, the same one that comes with the PhysX Particles sample).

Here's the code where I load the texture into a PhysX heightmap, everything based on the PhysX Particles sample and the FreeImage manual:

PxVec3* heightfieldVertexes;
PxU32* heightfieldIndices;
heightfield:addHeightfield("particles_heightmap.tga", -10.0f, 2.0f, 2.0f, 0.3f, physicsSimulator->getInstance().createMaterial(0.9f, 0.9f, 0.001f), heightfieldVertexes, heightfieldIndices);

void PhysicsSimulator::addHeightfield(char* heightfieldTexturePath, PxReal thickness, PxReal xScale, PxReal yScale, PxReal zScale, PxMaterial *material, PxVec3*& vertices, PxU32*& indices)
{
FREE_IMAGE_FORMAT fif = FIF_UNKNOWN;

// check the file signature and deduce its format
fif = FreeImage_GetFileType(heightfieldTexturePath);

if (fif == FIF_UNKNOWN) // No signature? Try to guess the file format from the file extension
fif = FreeImage_GetFIFFromFilename(heightfieldTexturePath);

if ((fif == FIF_UNKNOWN) || !FreeImage_FIFSupportsReading(fif))
{
cerr << "Heightfield map texture could not be loaded!" << endl;

return;
}

FIBITMAP *heightfieldTexture = FreeImage_Load(fif, heightfieldTexturePath);

if(!heightfieldTexture)
{
cerr << "Heightfield map texture contains invalid data!" << endl;

return;
}

PxU16 nbColumns = PxU16(FreeImage_GetWidth(heightfieldTexture));
PxU16 nbRows = PxU16(FreeImage_GetHeight(heightfieldTexture));
PxHeightFieldDesc heightfieldDesc;
heightfieldDesc.nbColumns = nbColumns;
heightfieldDesc.nbRows = nbRows;
PxU32* samplesData = (PxU32*)malloc(sizeof(PxU32) * (nbColumns * nbRows));
heightfieldDesc.samples.data = samplesData;
heightfieldDesc.samples.stride = sizeof(PxU32);
heightfieldDesc.convexEdgeThreshold = 0;
PxU8* currentByte = (PxU8*)heightfieldDesc.samples.data;
PxU32 texturePitch = PxU32(FreeImage_GetPitch(heightfieldTexture));
PxU8* loaderPtr = (PxU8*)FreeImage_GetBits(heightfieldTexture);
vertices = (PxVec3*)malloc(sizeof(PxVec3) * (nbColumns * nbRows));
PxReal* UVs = (PxReal*)malloc(sizeof(PxReal) * ((nbColumns * nbRows * 2)));

for(PxU32 row = 0; row < nbRows; row++)
{
PxU8* pixel = (PxU8*)loaderPtr;
for(PxU32 column = 0; column < nbColumns; column++)
{
PxHeightFieldSample* currentSample = (PxHeightFieldSample*)currentByte;
currentSample->height = pixel[FI_RGBA_BLUE];
vertices[row * nbColumns + column] = PxVec3(PxReal(row) * xScale, PxReal(currentSample->height * zScale), PxReal(column) * yScale);
UVs[2 * (row * nbColumns + column)] = (PxReal(row) / PxReal(nbRows)) * 7.0f;
UVs[2 * (row * nbColumns + column) + 1] = (PxReal(column) / PxReal(nbColumns)) * 7.0f;
currentSample->materialIndex0 = 0;
currentSample->materialIndex1 = 0;
currentSample->clearTessFlag();
currentByte += heightfieldDesc.samples.stride;
pixel += 3;
}
}

// free alocated memory for heightfield texture

PxHeightField* heightfield = mPhysics->createHeightField(heightfieldDesc);
// free alocated memory for heightfield samples description
//free(samplesData);
// create shape for heightfield
PxTransform pose(PxVec3(-((PxReal)nbRows * yScale) / 2.0f, 0.0f, -((PxReal)nbColumns * xScale) / 2.0f), PxQuat(PxIdentity));
PxRigidActor* hf = mPhysics->createRigidStatic(pose);
PxShape* shape = hf->createShape(PxHeightFieldGeometry(heightfield, PxMeshGeometryFlags(), zScale, xScale, yScale), *material);
shape->setFlag(PxShapeFlag::ePARTICLE_DRAIN, false);
shape->setFlag(PxShapeFlag::eSIMULATION_SHAPE, true);
// add actor to the scene
mActors.push_back(hf);
// create indices and UVs
indices = (PxU32*)malloc(sizeof(PxU32) * (nbColumns - 1) * (nbRows - 1) * 3 * 2);
for(int i = 0; i < (nbColumns - 1); ++i)
{
for(int j = 0; j < (nbRows - 1); ++j)
{
// first triangle
indices[6 * (i * (nbRows - 1) + j) + 0] = (i + 1) * nbRows + j;
indices[6 * (i * (nbRows - 1) + j) + 1] = i * nbRows + j;
indices[6 * (i * (nbRows - 1) + j) + 2] = i * nbRows + j + 1;
// second triangle
indices[6 * (i * (nbRows - 1) + j) + 3] = (i + 1) * nbRows + j + 1;
indices[6 * (i * (nbRows - 1) + j) + 4] = (i + 1) * nbRows + j;
indices[6 * (i * (nbRows - 1) + j) + 5] = i * nbRows + j + 1;
}
}
}


And here is the code where I try to render it, which is located on glfw main loop (some parts of the code where omitted for simplicity):

GLFWwindow* window;

int width, height;

mat4 viewMatrix;
mat4 projectionMatrix;

glEnable(GL_DEPTH_TEST);
glDepthFunc(GL_LESS);
glEnable(GL_CULL_FACE);

glfwGetFramebufferSize(window, &width, &height);
glViewport(0, 0, width, height);
glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT );
viewMatrix = lookAt(eye, center, up);;
projectionMatrix = perspective(fovy, width/(float)height, near, far);

GLuint vao;
glGenVertexArrays( 1, &vao );
glBindVertexArray( vao );

GLuint buffer;
glGenBuffers( 1, &buffer );
glBindBuffer( GL_ARRAY_BUFFER, buffer );
glBufferData( GL_ARRAY_BUFFER, sizeof(heightfieldVertexes), NULL, GL_STATIC_DRAW );
glBufferSubData( GL_ARRAY_BUFFER, 0, sizeof(heightfieldVertexes), heightfieldVertexes);

GLuint vPosition = glGetAttribLocation( program, "vPosition" );
glEnableVertexAttribArray( vPosition );
glVertexAttribPointer( vPosition, 4, GL_FLOAT, GL_FALSE, 0, BUFFER_OFFSET(0) );

GLuint Model = glGetUniformLocation( program, "Model" );
glUniformMatrix4fv( Model, 1, GL_FALSE, modelMatrix.front());

GLuint View = glGetUniformLocation( program, "View" );
glUniformMatrix4fv( View, 1, GL_FALSE, value_ptr(viewMatrix) );

GLuint Projection = glGetUniformLocation( program, "Projection" );
glUniformMatrix4fv( Projection, 1, GL_FALSE, value_ptr(projectionMatrix) );

glDrawElements( GL_TRIANGLE_STRIP, sizeof(heightfieldIndices)/sizeof(PxU32), GL_UNSIGNED_INT, heightfieldIndices );

glfwSwapBuffers(window);
glfwPollEvents();


Lastly, here are the GLSL shader codes. First for the vertex shader:

#version 430

in vec3 vPosition;

out vec4 color;

uniform mat4 Model;
uniform mat4 View;
uniform mat4 Projection;

void main()
{
mat4 ModelViewProjection = Projection * View * Model;

gl_Position = ModelViewProjection * vec4(vPosition, 1.0);

color = vec4(1.0, 0.0, 0.0, 1.0);
}


EDIT: I'm trying to render the heightmap in red just to check if it is ok.

And the fragment shader:

#version 430

in vec4 color;

out vec4 fColor; // fragment's final color

void main()
{
fColor = color;
}


The results of the rendering don't show anything from the heightmap.

As it is today it's far from optimized, but the initial idea is to make it work, and after that make it fast. I think that the view and projection matrix are correct, since I can create and render a box and a plane that interact with each other with PhysX, and they seem to be right. The model matrixes are created by PhysX itself and so I'm assuming that it is correct (and it seems correct in the other actors on the scene).

Unfortunatelly my current computer is not compatible with the more recent versions of OpenGL, so I'm programming this project while I'm on college, but came home today and thought that it may be that I'm trying to pass pointers to a PxVec3 and PxU32 arrays to GLSL shaders, but I don't really know if they are meant to this... As I said, I'm also using glm, and in the case of the vec3 and vec4 array types (from glm) everything works fine. Can this be the error or I'm wrong and the PhysX types are compatible with GLSL?

Other thing that I though is that maybe it's being rendered, but on some place that my camera isn't looking at. If this is the case, is there any way that I can figure where the heightmap is being drawn?

• Have you checked if the height map is being loaded correctly (using PVD)? Oct 21, 2014 at 9:37
• Also on a side note I would advise you to stop using C-style casts and malloc in C++ code - use C++ new and casts, it will help you find some hard to spot mistakes and bugs Oct 21, 2014 at 9:39
• @UnholySheep no, I'm not using PVD... thanks for pointing it out, I'll check it the next time I go to college and will report back. And also thanks for the C++ advise. Oct 21, 2014 at 12:40
• @UnholySheep I just tried to use PVD, made the code to connect my application to it, but when I open PVD it shows the "Failed to start Pvd console" message on the log, along with "The system cannot find the file specified. (Exception from HRESULT: 0x80070002)", and there's nothing on the log. Couldn't find anything online about this error specifically on PVD, but tried to deactivate the Windows firewall and reinstall PVD, which didn't solve it. Do you know what it can be? Thanks! Oct 22, 2014 at 23:52
• Tried it again in a different computer and got the same errors. Oct 23, 2014 at 14:49