// definitions are below void AddDebugDisplayShape(hkpRigidBody* body, hkGeometry* geom); void DeleteDebugDisplayShape(hkpRigidBody* body); void AddHeightfieldDebugDisplayShape(hkpRigidBody* body); // sample usage class MyHeightfield { hkpRigidBody* m_fixedRigidBody; public: hkpRigidBody* CreateHeightfieldFixedRigidBody(); // not shown MyHeightfield(hkpWorld* world) { m_fixedRigidBody = CreateHeightfieldFixedRigidBody(); AddHeightfieldDebugDisplayShape(m_fixedRigidBody); world->addEntity(m_fixedRigidBody); } ~MyHeightfield() { m_fixedRigidBody->getWorld()->removeEntity(m_fixedRigidBody); DeleteDebugDisplayShape(m_fixedRigidBody); m_fixedRigidBody->removeReference(); } }; void AddDebugDisplayShape(hkpRigidBody* body, hkGeometry* geom) { #ifdef HK_DEBUG body->addProperty(HK_PROPERTY_OVERRIDE_DEBUG_DISPLAY_GEOMETRY_NO_DELETE, geom); geom->addReference(); #endif } void DeleteDebugDisplayShape(hkpRigidBody* body) { #ifdef HK_DEBUG if (body->hasProperty( HK_PROPERTY_OVERRIDE_DEBUG_DISPLAY_GEOMETRY_NO_DELETE)) { hkpPropertyValue prop = body->removeProperty( HK_PROPERTY_OVERRIDE_DEBUG_DISPLAY_GEOMETRY_NO_DELETE); if (hkGeometry* geom = static_cast<hkGeometry*>(prop.getPtr())) { geom->removeReference(); } } #endif } void AddHeightfieldDebugDisplayShape(hkpRigidBody* body) { #ifdef HK_DEBUG // extract sampled heightfield shape from rigid body const hkpShape* shape = body->getCollidable()->getShape(); hkcdShapeType::ShapeTypeEnum type = shape->getType(); const hkpSampledHeightFieldShape* heightfield = 0; if (type == hkcdShapeType::SAMPLED_HEIGHT_FIELD) { heightfield = static_cast<const hkpSampledHeightFieldShape*>(shape); } else if (type == hkcdShapeType::TRI_SAMPLED_HEIGHT_FIELD_COLLECTION) { heightfield = static_cast<const hkpTriSampledHeightFieldCollection*>( shape)->getHeightFieldShape(); } else if (type == hkcdShapeType::TRI_SAMPLED_HEIGHT_FIELD_BV_TREE) { heightfield = static_cast<const hkpTriSampledHeightFieldBvTreeShape*>( shape)->getShapeCollection()->getHeightFieldShape(); } else { return; // not a heightfield } // max vertex count, a compromise between VDB framerate and visual fidelity int maxVertexCount = 128 * 1024; if (heightfield->m_zRes * heightfield->m_xRes <= maxVertexCount) { // heightfield is small enough to display as-is return; } // compute downsample factor and new, smaller grid dimensions int smallXRes; int smallZRes; int downsampleFactor = 2; while(1) { smallZRes = (heightfield->m_zRes - 2) / downsampleFactor + 2; smallXRes = (heightfield->m_xRes - 2) / downsampleFactor + 2; if (smallXRes * smallZRes <= maxVertexCount) { break; } downsampleFactor++; } // allocate an hkGeometry (basically an indexed triangle list) hkGeometry* displayGeom = new hkGeometry(); // add vertices const hkVector4& scale = heightfield->m_intToFloatScale; hkVector4 vert; for (int smallZ = 0; smallZ < smallZRes; smallZ++) { int z = (smallZ == smallZRes - 1) ? heightfield->m_zRes - 1 : smallZ * downsampleFactor; for (int smallX = 0; smallX < smallXRes; smallX++) { int x = (smallX == smallXRes - 1) ? heightfield->m_xRes - 1 : smallX * downsampleFactor; vert.set( float(x) * scale(0), heightfield->getHeightAt(x, z) * scale(1), float(z) * scale(2)); displayGeom->m_vertices.pushBack(vert); } } // add triangle indices (two per grid cell) hkGeometry::Triangle tri0; hkGeometry::Triangle tri1; int rowOffset = smallXRes; for (int smallZ = 0; smallZ < smallZRes - 1; smallZ++) { int baseIndex = smallZ * rowOffset; for (int smallX = 0; smallX < smallXRes - 1; smallX++) { tri0.set( baseIndex + 0, baseIndex + rowOffset, baseIndex + 1); tri1.set( baseIndex + rowOffset, baseIndex + rowOffset + 1, baseIndex + 1); displayGeom->m_triangles.pushBack(tri0); displayGeom->m_triangles.pushBack(tri1); baseIndex++; } } // assign to rigid body AddDebugDisplayShape(body, displayGeom); displayGeom->removeReference(); #endif }