Attribute ?

Post Reply
katiequinn
Posts: 1
Joined: Thu Aug 15, 2019 3:27 am

Attribute ?

Post by katiequinn »

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.
User avatar
drleviathan
Posts: 849
Joined: Tue Sep 30, 2014 6:03 pm
Location: San Francisco

Re: Attribute ?

Post by drleviathan »

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:

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]
Post Reply