Page 1 of 1

Setting up collision between OpenGL meshes

Posted: Tue Jan 31, 2017 8:18 pm
by jimjamjack
Hello, I'm pretty new to Bullet so I'm pretty confused about setting up collisions between meshes. I understand how to set it up for basic shapes, but I'm having issues giving three of my meshes a collision shape of its mesh (I want simple ray callbacks, and I have a ray casting method).

I have three meshes in the shape of targets, for which I want each to have their mesh as the collision shape. How would I go about this?
Can anyone provide some sort of code to help me through this? Thanks in advance :)

Re: Setting up collision between OpenGL meshes

Posted: Sun Feb 12, 2017 10:14 pm
by benelot
If you want performance, do not try to use your OpenGL meshes to detect collisions. You should use an approximation of it built from the primitives. So if your mesh looks a bit like and mainly behaves like a sphere, use a sphere. If a capsule is better, then use that. Things like characters are usually full of capsules :)

If you really need to go for the full meshes, perform a convex shape decomposition and compose the convex hulls into a compound shape.

Re: Setting up collision between OpenGL meshes

Posted: Sun Feb 12, 2017 11:14 pm
by jimjamjack
The meshes are shooting targets, I currently have a collision box shape for each one but it's inaccurate since I can shoot through the box but not the target and still 'hit' it. Would you recommend another primitive shape in particular? :)

I've looked into convex hulls and btvTriangleArrays and similar things, but honestly cannot figure out how to create the collision shape using the vertices/indices that I have. I know it's less efficient than using primitives, but accuracy is quite important for the ray calls :/

Appreciate the help though :)

Re: Setting up collision between OpenGL meshes

Posted: Mon Feb 13, 2017 2:55 am
by drleviathan
Here is some code that may work for you. I tried to make it clear and correct, however please keep in mind that I did not test: there may be syntax errors, incorrect API usage, and memory leaks. Use at your own risk:

Code: Select all

    std::vector<unsigned short> targetIndices;
    std::vector<glm::vec3> targetVertices;
    std::vector<glm::vec2> targetUvs;
    std::vector<glm::vec3> targetNormals;
    res = loadAssImp("target2.obj", targetIndices, targetVertices, targetUvs, targetNormals);

    // first create a btTriangleIndexVertexArray
    // NOTE: we must track this pointer and delete it when all shapes are done with it!
    btTriangleIndexVertexArray* data = new btTriangleIndexVertexArray;

    // add an empty mesh (data makes a copy)
    btIndexedMesh tempMesh;
    data->addIndexedMesh(tempMesh, PHY_FLOAT);

    // get a reference to internal copy of the empty mesh
    btIndexedMesh& mesh = data->getIndexedMeshArray()[0];

    // allocate memory for the mesh
    const int32_t VERTICES_PER_TRIANGLE = 3;
    size_t numIndices = targetIndices.size();
    mesh.m_numTriangles = numIndices / VERTICES_PER_TRIANGLE;
    if (numIndices < std::numeric_limits<int16_t>::max()) {
        // we can use 16-bit indices
        mesh.m_triangleIndexBase = new unsigned char[sizeof(int16_t) * (size_t)numIndices];
        mesh.m_indexType = PHY_SHORT;
        mesh.m_triangleIndexStride = VERTICES_PER_TRIANGLE * sizeof(int16_t);
    } else {
        // we need 32-bit indices
        mesh.m_triangleIndexBase = new unsigned char[sizeof(int32_t) * (size_t)numIndices];
        mesh.m_indexType = PHY_INTEGER;
        mesh.m_triangleIndexStride = VERTICES_PER_TRIANGLE * sizeof(int32_t);
    }
    mesh.m_numVertices = targetVertices.size();
    mesh.m_vertexBase = new unsigned char[VERTICES_PER_TRIANGLE * sizeof(btScalar) * (size_t)mesh.m_numVertices];
    mesh.m_vertexStride = VERTICES_PER_TRIANGLE * sizeof(btScalar);

    // copy vertices into mesh
    btScalar* vertexData = static_cast<btScalar*>((void*)(mesh.m_vertexBase));
    for (int32_t i = 0; i < mesh.m_numVertices; ++i) {
        int32_t j = i * VERTICES_PER_TRIANGLE;
        const glm::vec3& point = targetVertices[i];
        vertexData[j] = point.x;
        vertexData[j + 1] = point.y;
        vertexData[j + 2] = point.z;
    }
    // copy indices into mesh
    if (numIndices < std::numeric_limits<int16_t>::max()) {
        // 16-bit case
        int16_t* indices = static_cast<int16_t*>((void*)(mesh.m_triangleIndexBase));
        for (int32_t i = 0; i < numIndices; ++i) {
            indices[i] = (int16_t)targetIndices[i];
        }
    } else {
        // 32-bit case
        int32_t* indices = static_cast<int32_t*>((void*)(mesh.m_triangleIndexBase));
        for (int32_t i = 0; i < numIndices; ++i) {
            indices[i] = targetIndices[i];
        }
    }

    // create the shape
    // NOTE: we must track this pointer and delete it when all btCollisionObjects that use it are done with it!
    const bool USE_QUANTIZED_AABB_COMPRESSION = true;
    btBvhTriangleMeshShape* shape = new btBvhTriangleMeshShape(data, USE_QUANTIZED_AABB_COMPRESSION);

Re: Setting up collision between OpenGL meshes

Posted: Mon Feb 13, 2017 9:44 pm
by jimjamjack
@drleviathan

Wow, after scouring the web for about a week that is by far the most useful post I've come across! Thanks for the code and break down, it really was helpful. The code works perfectly, apart from the impact on performance and frame rate, but I'll take a look at that when I can :)

That is unless you have any suggestions for improving performance? Regardless, an extremely useful post, so thanks again :)

Re: Setting up collision between OpenGL meshes

Posted: Tue Feb 14, 2017 6:03 am
by drleviathan
If your graphics mesh data are in the right format I believe is is possible to create a btStridingMeshInterface that points into that memory: you can share the same data for rendering and collisions. I've never done it so I don't have any example code to offer.

Re: Setting up collision between OpenGL meshes

Posted: Tue Feb 14, 2017 7:21 pm
by jimjamjack
Ah, I've actually looked into btStridingMeshInterfaces before. I'll look into them again, and if I make any progress with it then I'll post it here. If anyone else is looking into them as well, the basic idea is to create a btStridingMeshInterface, call getLockedVertexIndexBase, fill the array it returns, and unlock.

Cheers again :)