Hey all, for a while I have been using code copied from the Bullet physics extension as part of my engine, and as the engine has progressed, I have noticed that the code does not hold up well. Here is the code I am currently using. I would like to note that I am not using the physics extension itself, just the code from it.
Code:
struct MeshData
{
unsigned int GeoResource;
unsigned int VertexOffset, IndexOffset;
unsigned int NumVertices, NumTriangleIndices, VertRStart;
float* VertexBase;
unsigned int* TriangleBase32;
unsigned short* TriangleBase16;
unsigned int TriangleMode;
MeshData()
: VertexOffset(0),
IndexOffset(0),
NumVertices(0),
NumTriangleIndices(0),
VertRStart(0),
GeoResource(0)
{
}
};
void COLSHAPE_GetMeshData(int node,MeshData* data)
{
switch(h3dGetNodeType(node))
{
case H3DNodeTypes::Mesh:
data->GeoResource = h3dGetNodeParamI(h3dGetNodeParent(node), H3DModel::GeoResI);
data->NumVertices = h3dGetNodeParamI(node, H3DMesh::VertREndI) - h3dGetNodeParamI(node, H3DMesh::VertRStartI) + 1;
data->NumTriangleIndices = h3dGetNodeParamI(node, H3DMesh::BatchCountI);
data->VertRStart = h3dGetNodeParamI(node, H3DMesh::VertRStartI);
data->VertexOffset = h3dGetNodeParamI(node, H3DMesh::VertRStartI) * 3;
data->IndexOffset = h3dGetNodeParamI(node, H3DMesh::BatchStartI);
break;
case H3DNodeTypes::Model:
data->GeoResource = h3dGetNodeParamI(node, H3DModel::GeoResI);
data->NumVertices = h3dGetResParamI(data->GeoResource, H3DGeoRes::GeometryElem, 0, H3DGeoRes::GeoVertexCountI);
data->NumTriangleIndices = h3dGetResParamI(data->GeoResource, H3DGeoRes::GeometryElem, 0, H3DGeoRes::GeoIndexCountI);
break;
/*case H3DEXT_NodeType_Terrain:
unloadTerrainGeoRes();
geoResource = h3dextCreateTerrainGeoRes(
node,
h3dGetNodeParamStr( node, H3DNodeParams::NameStr ),
h3dGetNodeParamF( node, H3DEXTTerrain::MeshQualityF, 0) );
data->NumVertices = h3dGetResParamI(geoResource, H3DGeoRes::GeometryElem, 0, H3DGeoRes::GeoVertexCountI);
data->NumTriangleIndices = h3dGetResParamI(geoResource, H3DGeoRes::GeometryElem, 0, H3DGeoRes::GeoIndexCountI);
m_terrainGeoRes = geoResource;
break;*/
}
float* vb = (float*)h3dMapResStream(data->GeoResource, H3DGeoRes::GeometryElem, 0, H3DGeoRes::GeoVertPosStream, true, false);
data->VertexBase = new float[(data->NumVertices*3)];
memcpy(data->VertexBase, vb+data->VertexOffset, sizeof(float)*data->NumVertices*3);
h3dUnmapResStream(data->GeoResource);
//Triangle indices, must cope with 16 bit and 32 bit
if (h3dGetResParamI(data->GeoResource, H3DGeoRes::GeometryElem, 0, H3DGeoRes::GeoIndices16I)) {
unsigned short* tb = (unsigned short*)h3dMapResStream(data->GeoResource, H3DGeoRes::GeometryElem, 0, H3DGeoRes::GeoIndexStream, true, false);
data->TriangleBase16 = new unsigned short[data->NumTriangleIndices];
memcpy(data->TriangleBase16, tb+data->IndexOffset, sizeof(unsigned short)*data->NumTriangleIndices);
h3dUnmapResStream(data->GeoResource);
} else {
unsigned int* tb = (unsigned int*)h3dMapResStream(data->GeoResource, H3DGeoRes::GeometryElem, 0, H3DGeoRes::GeoIndexStream, true, false);
data->TriangleBase32 = new unsigned int[data->NumTriangleIndices];
memcpy(data->TriangleBase32, tb+data->IndexOffset, sizeof(unsigned int)*data->NumTriangleIndices);
h3dUnmapResStream(data->GeoResource);
}
}
void CollisionShape::SetAsStaticMesh(int node)
{
string out = "Beginning generation of static mesh... ";
MeshData mesh_data;
COLSHAPE_GetMeshData(node,&mesh_data);
if( mesh_data.VertexBase && (mesh_data.TriangleBase32 || mesh_data.TriangleBase16))
{
m_smile->triangle_mesh = new btTriangleMesh();
int offset = 3;
// copy mesh from graphics to physics
bool index16 = false;
if (mesh_data.TriangleBase16)
index16 = true;
for (unsigned int i = 0; i < mesh_data.NumTriangleIndices - 2; i+=offset)
{
unsigned int index1 = index16 ?
(mesh_data.TriangleBase16[i]-mesh_data.VertRStart)*3 :
(mesh_data.TriangleBase32[i]-mesh_data.VertRStart)*3;
unsigned int index2 = index16 ?
(mesh_data.TriangleBase16[i+1]-mesh_data.VertRStart)*3 :
(mesh_data.TriangleBase32[i+1]-mesh_data.VertRStart)*3;
unsigned int index3 = index16 ?
(mesh_data.TriangleBase16[i+2]-mesh_data.VertRStart)*3 :
(mesh_data.TriangleBase32[i+2]-mesh_data.VertRStart)*3;
m_smile->triangle_mesh->addTriangle(
btVector3( mesh_data.VertexBase[index1], mesh_data.VertexBase[index1+1], mesh_data.VertexBase[index1+2] ),
btVector3( mesh_data.VertexBase[index2], mesh_data.VertexBase[index2+1], mesh_data.VertexBase[index2+2] ),
btVector3( mesh_data.VertexBase[index3], mesh_data.VertexBase[index3+1], mesh_data.VertexBase[index3+2] )
);
}
bool useQuantizedAabbCompression = true;
m_CollisionShape = new btBvhTriangleMeshShape(
m_smile->triangle_mesh,useQuantizedAabbCompression
);
m_smile->is_set = true;
}
float scal_x, scal_y, scal_z;
int child_mesh = 0;
out += "DING!!! Done!";
Log::Info(out);
}
void CollisionShape::SetAsDynamicMesh(int node)
{
string out = "Beginning generation of dynamic mesh... ";
// Since the node that is passed can be almost any node at all, we need
// to walk the scenegraph to find a model node.
MeshData mesh_data;
COLSHAPE_GetMeshData(node,&mesh_data);
if(mesh_data.VertexBase && (mesh_data.TriangleBase32 || mesh_data.TriangleBase16))
{
// Create new mesh in physics engine
m_smile->triangle_mesh = new btTriangleMesh();
int offset = 3;
//if (triangleMode == 5) // Triangle Strip
// offset = 1;
// copy mesh from graphics to physics
bool index16 = false;
if(mesh_data.TriangleBase16)
index16 = true;
for(unsigned int i = 0; i < mesh_data.NumTriangleIndices - 2; i+=offset)
{
unsigned int index1 = index16 ?
(mesh_data.TriangleBase16[i]-mesh_data.VertRStart)*3 :
(mesh_data.TriangleBase32[i]-mesh_data.VertRStart)*3;
unsigned int index2 = index16 ?
(mesh_data.TriangleBase16[i+1]-mesh_data.VertRStart)*3 :
(mesh_data.TriangleBase32[i+1]-mesh_data.VertRStart)*3;
unsigned int index3 = index16 ?
(mesh_data.TriangleBase16[i+2]-mesh_data.VertRStart)*3 :
(mesh_data.TriangleBase32[i+2]-mesh_data.VertRStart)*3;
m_smile->triangle_mesh->addTriangle(
btVector3(
mesh_data.VertexBase[index1], mesh_data.VertexBase[index1+1], mesh_data.VertexBase[index1+2]
),
btVector3(
mesh_data.VertexBase[index2], mesh_data.VertexBase[index2+1], mesh_data.VertexBase[index2+2]
),
btVector3(
mesh_data.VertexBase[index3], mesh_data.VertexBase[index3+1], mesh_data.VertexBase[index3+2]
)
);
}
// You can use GImpact or convex
// decomposition of bullet to handle more complex meshes
m_CollisionShape = new btConvexTriangleMeshShape(m_smile->triangle_mesh);
//m_CollisionShape = new btGImpactConvexDecompositionShape(m_smile->triangle_mesh,btVector3(1,1,1),btScalar(0.1f),true);
//m_CollisionShape->updateBound();
m_smile->is_set = true;
}
float scal_x, scal_y, scal_z;
int child_mesh = 0;
out += "DING!!! Done!";
Log::Info(out);
}
m_smile is a pointer to a struct that implements the 'pimpl' idiom. Private Implementation. Basically holds all of the member variables in the implementation as opposed to the declaration. Complete separation of the interface from the implementation is a godsend. That aside, are there new things about the resource mapping that should be taken into account with Beta 5?
My next problem is with the Bullet debug drawing. I am using their debug drawer, and no combination of state saves/changes/beatingswithahammer are allowing the debug geometry to work.
I do not believe there is anything wrong with the rendering class itself, but I'll post it anyway along with the actual invocation of the debug rendering.
Code:
// Rendering routine.
void Renderer::Render()
{
DumpHorde3DLog();
boost::mutex::scoped_lock sl(GetRenderMutex());
int r;
Window* w = Engine::I()->GetWindow();
Stage* s = Engine::I()->GetStage();
int win_w = w->GetWidth();
int win_h = w->GetHeight();
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glViewport(0,0,win_w,win_h);
int stage_camera = Engine::I()->GetStage()->GetActiveCamera();
Matrix4f camera_world_transform;
Matrix4f camera_projection_matrix;
glPushAttrib(GL_ALL_ATTRIB_BITS);
if(stage_camera)
{
h3dRender(stage_camera);
h3dGetCameraProjMat(stage_camera,camera_projection_matrix.x);
h3dGetNodeTransMats(stage_camera,NULL,(const float**)&camera_world_transform.x);
}
h3dFinalizeFrame();
glPopAttrib();
glActiveTexture(0);
glBindBuffer(GL_ARRAY_BUFFER,0);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER,0);
glBindBuffer(GL_PIXEL_PACK_BUFFER,0);
glBindBuffer(GL_PIXEL_UNPACK_BUFFER,0);
glUseProgram(0);
// */
/*glMatrixMode(GL_PROJECTION);
glLoadMatrixf(camera_projection_matrix.x);
glMatrixMode(GL_MODELVIEW);
glLoadMatrixf(camera_world_transform.inverted().x);
s->GetWorld()->DebugDraw();
// */
s->GetCanvas()->Activate();
s->PropagatePostRender();
s->GetCanvas()->Deactivate();
glPopAttrib();
}
Debug drawing class implementation.
Code:
void GLDebugDrawer::drawLine(const btVector3& from,const btVector3& to,const btVector3& fromColor, const btVector3& toColor)
{
btVector3 f(from); btVector3 t(to);
f.setX(from.x() / Irre::Physics_Scale_Factor);
f.setY(from.y() / Irre::Physics_Scale_Factor);
f.setZ(from.z() / Irre::Physics_Scale_Factor);
t.setX(to.x() / Irre::Physics_Scale_Factor);
t.setY(to.y() / Irre::Physics_Scale_Factor);
t.setZ(to.z() / Irre::Physics_Scale_Factor);
//glLineWidth(2.f);
glBegin(GL_LINES);
glColor3f(fromColor.getX(), fromColor.getY(), fromColor.getZ());
glVertex3d(f.getX(), f.getY(), f.getZ());
glColor3f(toColor.getX(), toColor.getY(), toColor.getZ());
glVertex3d(t.getX(), t.getY(), t.getZ());
glEnd();
}
void GLDebugDrawer::drawLine(const btVector3& from,const btVector3& to,const btVector3& color)
{
btVector3 f(from); btVector3 t(to);
f.setX(from.x() / Irre::Physics_Scale_Factor);
f.setY(from.y() / Irre::Physics_Scale_Factor);
f.setZ(from.z() / Irre::Physics_Scale_Factor);
t.setX(to.x() / Irre::Physics_Scale_Factor);
t.setY(to.y() / Irre::Physics_Scale_Factor);
t.setZ(to.z() / Irre::Physics_Scale_Factor);
//glLineWidth(2.f);
glBegin(GL_LINES);
glColor3f(color.getX(), color.getY(), color.getZ());
glVertex3d(f.getX(),f.getY(),f.getZ());
glColor3f(color.getX(), color.getY(), color.getZ());
glVertex3d(t.getX(), t.getY(), t.getZ());
glEnd();
}
void GLDebugDrawer::drawSphere (const btVector3& p, btScalar radius, const btVector3& color)
{
btVector3 pp(p);
pp.setX(p.x() / Irre::Physics_Scale_Factor);
pp.setY(p.y() / Irre::Physics_Scale_Factor);
pp.setZ(p.z() / Irre::Physics_Scale_Factor);
glColor4f (color.getX(), color.getY(), color.getZ(), btScalar(1.0f));
glPushMatrix ();
glTranslatef (pp.getX(),pp.getY(), pp.getZ());
int lats = 5;
int longs = 5;
int i, j;
for(i = 0; i <= lats; i++) {
btScalar lat0 = SIMD_PI * (-btScalar(0.5) + (btScalar) (i - 1) / lats);
btScalar z0 = radius*sin(lat0);
btScalar zr0 = radius*cos(lat0);
btScalar lat1 = SIMD_PI * (-btScalar(0.5) + (btScalar) i / lats);
btScalar z1 = radius*sin(lat1);
btScalar zr1 = radius*cos(lat1);
glBegin(GL_QUAD_STRIP);
for(j = 0; j <= longs; j++) {
btScalar lng = 2 * SIMD_PI * (btScalar) (j - 1) / longs;
btScalar x = cos(lng);
btScalar y = sin(lng);
glNormal3f(x * zr0, y * zr0, z0);
glVertex3f(x * zr0, y * zr0, z0);
glNormal3f(x * zr1, y * zr1, z1);
glVertex3f(x * zr1, y * zr1, z1);
}
glEnd();
}
glPopMatrix();
}
void GLDebugDrawer::drawBox (const btVector3& boxMin, const btVector3& boxMax, const btVector3& color, btScalar alpha)
{
btVector3 halfExtent = (boxMax - boxMin) / Irre::Physics_Scale_Factor;// * btScalar(0.5f);
btVector3 center = ((boxMax + boxMin) * btScalar(0.5f)) / Irre::Physics_Scale_Factor;
glColor4f (color.getX(), color.getY(), color.getZ(), alpha);
glPushMatrix();
//glTranslatef (center.getX(), center.getY(), center.getZ());
//glScaled(2*halfExtent[0], 2*halfExtent[1], 2*halfExtent[2]);
glBegin(GL_QUADS);
{
glColor4f (color.getX(), color.getY(), color.getZ(), POLY_ALPHA);
glVertex3f(boxMin.getX(),boxMin.getY(),boxMin.getZ());
glVertex3f(boxMin.getX(),boxMin.getY(),boxMax.getZ());
glVertex3f(boxMin.getX(),boxMax.getY(),boxMax.getZ());
glVertex3f(boxMax.getX(),boxMax.getY(),boxMax.getZ());
}
glEnd();
glPopMatrix ();
}
void GLDebugDrawer::drawTriangle(const btVector3& a,const btVector3& b,const btVector3& c,const btVector3& color,btScalar alpha)
{
btVector3 aa(a); btVector3 bb(b); btVector3 cc(c);
aa.setX(a.x() / Irre::Physics_Scale_Factor); aa.setY(a.y() / Irre::Physics_Scale_Factor); aa.setZ(a.z() / Irre::Physics_Scale_Factor);
bb.setX(b.x() / Irre::Physics_Scale_Factor); bb.setY(b.y() / Irre::Physics_Scale_Factor); bb.setZ(b.z() / Irre::Physics_Scale_Factor);
cc.setX(c.x() / Irre::Physics_Scale_Factor); cc.setY(c.y() / Irre::Physics_Scale_Factor); cc.setZ(c.z() / Irre::Physics_Scale_Factor);
// if (m_debugMode > 0)
{
const btVector3 n=btCross(bb-aa,cc-aa).normalized();
glBegin(GL_TRIANGLES);
glColor4f(color.getX(), color.getY(), color.getZ(),alpha);
glNormal3d(n.getX(),n.getY(),n.getZ());
glVertex3d(aa.getX(),aa.getY(),aa.getZ());
glVertex3d(bb.getX(),bb.getY(),bb.getZ());
glVertex3d(cc.getX(),cc.getY(),cc.getZ());
glEnd();
}
}
void GLDebugDrawer::setDebugMode(int debugMode)
{
m_debugMode = debugMode;
}
void GLDebugDrawer::draw3dText(const btVector3& location,const char* textString)
{
}
void GLDebugDrawer::reportErrorWarning(const char* warningString)
{
printf("%s\n",warningString);
}
void GLDebugDrawer::drawContactPoint(const btVector3& pointOnB,const btVector3& normalOnB,btScalar distance,int lifeTime,const btVector3& color)
{
{
btVector3 to=pointOnB+normalOnB*distance;
const btVector3&from = pointOnB;
glColor4f(color.getX(), color.getY(), color.getZ(),1.f);
//glColor4f(0,0,0,1.f);
glBegin(GL_LINES);
glVertex3d(from.getX(), from.getY(), from.getZ());
glVertex3d(to.getX(), to.getY(), to.getZ());
glEnd();
glRasterPos3f(from.x(), from.y(), from.z());
char buf[12];
sprintf(buf," %d",lifeTime);
//BMF_DrawString(BMF_GetFont(BMF_kHelvetica10),buf);
}
}
I guess I am asking for advice on what I currently have. The strange thing is that while the bullet debug renderer is broken, my canvas works fine. I am actually very tempted to just write a low level drawing extension that ties in with the Horde renderer just to solve this problem once and for all, unless somebody else has already. Or else, would anyone be willing to collaborate on such an extension? This would just be something that, for the time being, would allow us to draw geometry and lines at a lower level without having to worry about the OpenGL state outside of Horde3D for things such as Bullet debug drawing. This could also be expanded to a generic canvas similar to the one I have implemented for my game engine. I even have the AntiGrain Graphics library giving me these nice 2D vector graphics.