Hi,
I would like to be able to set an attribute to a polygon when I add one to a btTriangleShape. I need to be able to access this attribute in the btManifoldPoint. Is anyone who knows if there a way to do this ?
Should I develop myself a way to do this? For instance can I add a attribute tab in the btTriangleShape and modify the collision callback to be able to access this attribute list?
Thanks.
Attribute ?
-
- Posts: 849
- Joined: Tue Sep 30, 2014 6:03 pm
- Location: San Francisco
Re: Attribute ?
The statement: "when I add a polygon to a btTriangleShape" does not quite make sense. A btTriangleShape is just a triangle with three vertices and doesn't have polygons. Perhaps you meant a btTriangleMeshShape?
btTriangleMeshShape is a pure virtual base class. The first non-virtual derived class is btBvhTriangleMeshShape and derived from that is btMultimaterialTriangleMeshShape. It sounds like the multimaterial shape allows you to set per-triangle btMaterial which has friction and restitution data members. But if you look closely at the btMaterial class you can see that it has a third data member: int pad[2], which is not used for anything but padding to make that class 16-byte aligned. You could use a btMultimaterialTriangleMeshShape and store your per-triangle attribute in those 64-bits.
There is another way to do it. The btTriangleMeshShape API uses a btTriangleMeshInterface which can point to "arbitrary" mesh data as long as its vertices and triangle indices are properly strided. This means you could interleave your own mesh data with per-triangle attributes and write custom code for extracting the attribute for each triangle.
Below is a callback implementation that flips back-fracing triangles, slightly simplified for presentation (so I'm not certain it actually compiles). It was designed to be used with the gContactAddedCallback and the btCollisionObject::CF_CUSTOM_MATERIAL_CALLBACK flag. I post it here because it provides an example of how to access the triangle through the btTriangleMeshInterface:
btTriangleMeshShape is a pure virtual base class. The first non-virtual derived class is btBvhTriangleMeshShape and derived from that is btMultimaterialTriangleMeshShape. It sounds like the multimaterial shape allows you to set per-triangle btMaterial which has friction and restitution data members. But if you look closely at the btMaterial class you can see that it has a third data member: int pad[2], which is not used for anything but padding to make that class 16-byte aligned. You could use a btMultimaterialTriangleMeshShape and store your per-triangle attribute in those 64-bits.
There is another way to do it. The btTriangleMeshShape API uses a btTriangleMeshInterface which can point to "arbitrary" mesh data as long as its vertices and triangle indices are properly strided. This means you could interleave your own mesh data with per-triangle attributes and write custom code for extracting the attribute for each triangle.
Below is a callback implementation that flips back-fracing triangles, slightly simplified for presentation (so I'm not certain it actually compiles). It was designed to be used with the gContactAddedCallback and the btCollisionObject::CF_CUSTOM_MATERIAL_CALLBACK flag. I post it here because it provides an example of how to access the triangle through the btTriangleMeshInterface:
Code: Select all
bool flipBackfaceTriangleNormals(btManifoldPoint& cp,
const btCollisionObjectWrapper* colObj0Wrap, int partId0, int index0,
const btCollisionObjectWrapper* colObj1Wrap, int partId1, int index1) {
// This callback is ONLY called on objects with btCollisionObject::CF_CUSTOM_MATERIAL_CALLBACK flag
// and the flagged object will always be sorted to Obj0. Hence the "other" is always Obj1.
const btCollisionObject* other = colObj1Wrap->m_collisionObject;
if (other->getCollisionShape()->getShapeType() == TRIANGLE_MESH_SHAPE_PROXYTYPE) {
// access the meshInterface
auto meshShape = static_cast<const btBvhTriangleMeshShape*>(other->getCollisionShape());
const btStridingMeshInterface* meshInterface = meshShape->getMeshInterface();
// figure out about how to navigate meshInterface
const uint8_t* vertexBase;
int32_t numverts;
PHY_ScalarType vertexType;
int32_t vertexStride;
const uint8_t* indexBase;
int32_t indexStride;
int32_t numFaces;
PHY_ScalarType indicesType;
int32_t subPart = colObj1Wrap->m_partId;
// NOTE: all arguments are being passed by reference except the bases (passed by pointer) and subPart (true input)
meshInterface->getLockedReadOnlyVertexIndexBase(&vertexBase, numverts, vertexType, vertexStride, &indexBase, indexStride, numFaces, indicesType, subPart);
// fetch the triangle vertices
int32_t triangleIndex = colObj1Wrap->m_index;
assert(vertexType == PHY_FLOAT); // we expect our mesh vertex data to be float...
// ...but indicesType can vary
btVector3 triangleVertex[3];
switch (indicesType) {
case PHY_INTEGER: {
uint32_t* triangleIndices = (uint32_t*)(indexBase + triangleIndex * indexStride);
float* triangleBase;
triangleBase = (float*)(vertexBase + triangleIndices[0] * vertexStride);
triangleVertex[0].setValue(triangleBase[0], triangleBase[1], triangleBase[2]);
triangleBase = (float*)(vertexBase + triangleIndices[1] * vertexStride);
triangleVertex[1].setValue(triangleBase[0], triangleBase[1], triangleBase[2]);
triangleBase = (float*)(vertexBase + triangleIndices[2] * vertexStride);
triangleVertex[2].setValue(triangleBase[0], triangleBase[1], triangleBase[2]);
}
break;
case PHY_SHORT: {
uint16_t* triangleIndices = (uint16_t*)(indexBase + triangleIndex * indexStride);
float* triangleBase;
triangleBase = (float*)(vertexBase + triangleIndices[0] * vertexStride);
triangleVertex[0].setValue(triangleBase[0], triangleBase[1], triangleBase[2]);
triangleBase = (float*)(vertexBase + triangleIndices[1] * vertexStride);
triangleVertex[1].setValue(triangleBase[0], triangleBase[1], triangleBase[2]);
triangleBase = (float*)(vertexBase + triangleIndices[2] * vertexStride);
triangleVertex[2].setValue(triangleBase[0], triangleBase[1], triangleBase[2]);
}
break;
case PHY_UCHAR: {
uint8_t* triangleIndices = (uint8_t*)(indexBase + triangleIndex * indexStride);
float* triangleBase;
triangleBase = (float*)(vertexBase + triangleIndices[0] * vertexStride);
triangleVertex[0].setValue(triangleBase[0], triangleBase[1], triangleBase[2]);
triangleBase = (float*)(vertexBase + triangleIndices[1] * vertexStride);
triangleVertex[1].setValue(triangleBase[0], triangleBase[1], triangleBase[2]);
triangleBase = (float*)(vertexBase + triangleIndices[2] * vertexStride);
triangleVertex[2].setValue(triangleBase[0], triangleBase[1], triangleBase[2]);
}
break;
default:
return false;
}
// compute faceNormal
btVector3 meshScaling = meshInterface->getScaling();
triangleVertex[0] *= meshScaling;
triangleVertex[1] *= meshScaling;
triangleVertex[2] *= meshScaling;
btVector3 faceNormal = other->getWorldTransform().getBasis() * btCross(triangleVertex[1] - triangleVertex[0], triangleVertex[2] - triangleVertex[0]);
btScalar nDotF = btDot(faceNormal, cp.m_normalWorldOnB);
if (nDotF <= 0.0 && faceNormal.length2() > EPSILON) {
faceNormal.normalize();
// flip the contact normal to be aligned with the face normal...
// ...but only if old normal does NOT point along obj0's UP
// (because we're "stuck" and UP is the likely path out)
btVector3 up = colObj0Wrap->m_collisionObject->getWorldTransform().getBasis() * LOCAL_UP_AXIS;
if (cp.m_normalWorldOnB.dot(up) <= 0.0) {
nDotF = btDot(faceNormal, cp.m_normalWorldOnB);
cp.m_normalWorldOnB -= 2.0 * nDotF * faceNormal;
}
}
} else if (other->getCollisionShape()->getShapeType() == TRIANGLE_SHAPE_PROXYTYPE) {
auto triShape = static_cast<const btTriangleShape*>(other->getCollisionShape());
const btVector3* v = triShape->m_vertices1;
btVector3 faceNormal = other->getWorldTransform().getBasis() * btCross(v[1] - v[0], v[2] - v[0]);
btScalar nDotF = btDot(faceNormal, cp.m_normalWorldOnB);
if (nDotF <= 0.0 && faceNormal.length2() > EPSILON) {
faceNormal.normalize();
// flip the contact normal to be aligned with the face normal
cp.m_normalWorldOnB += -2.0 * nDotF * faceNormal;
}
}
// NOTE: this ManifoldPoint is a candidate and hasn't been accepted yet into the final ContactManifold yet
// when we modify its parameters Bullet may decide to discard it when assembling the manifold
//
// by Bullet convention for its gContactAddedCallback feature:
// the return value is currently ignored but to be future-proof:
// return true when friction has been modified
return false;
}
[code]