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;
}
loaderPtr += texturePitch;
}
// free alocated memory for heightfield texture
FreeImage_Unload(heightfieldTexture);
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
mScene->addActor(*hf);
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 program = InitShader("heightfieldVertexShader.vert", "heightfieldFragmentShader.frag");
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?
malloc
in C++ code - use C++new
and casts, it will help you find some hard to spot mistakes and bugs \$\endgroup\$