DanielChappuis / reactphysics3d

Open source C++ physics engine library in 3D
http://www.reactphysics3d.com
zlib License
1.54k stars 223 forks source link

Convex mesh creation and collisions #313

Open ZeunO8 opened 1 year ago

ZeunO8 commented 1 year ago

Here is my code for creating a convex mesh:

void Entity::createConvexMesh()
{
 reactphysics3d::Transform transform = reactphysics3d::Transform::identity();
 body = scene_p->physicsWorld->createRigidBody(transform);
 vec3 *evertices = (vec3 *)(*this).operator()<Floating>(EntityQuanta::Vertex);
 ivec3 *eindices = (ivec3 *)(*this).operator()<Floating>(EntityQuanta::Indice);
 const int nbVertices = QuantaSize(EntityQuanta::Vertex) / 3;
 const int nbTriangles = QuantaSize(EntityQuanta::Indice) / 3;
 float vertices[nbVertices * 3];
 for (Integer i = 0; i < nbVertices; i++)
 {
  vec3 &vertex = evertices[i];
  vertices[(i * 3)] = vertex.x;
  vertices[(i * 3) + 1] = vertex.y;
  vertices[(i * 3) + 2] = vertex.z;
 }
 int indices[nbTriangles * 3];
 for (Integer i = 0; i < nbTriangles; i++)
 {
  ivec3& indice = eindices[i];
  indices[(i * 3)] = indice.x;
  indices[(i * 3) + 1] = indice.y;
  indices[(i * 3) + 2] = indice.z;
 }
 reactphysics3d::PolygonVertexArray::PolygonFace *polygonFaces = new reactphysics3d::PolygonVertexArray::PolygonFace[nbTriangles];
 reactphysics3d::PolygonVertexArray::PolygonFace *face = polygonFaces;
 for (int f = 0; f < nbTriangles; f++)
 {
  face->indexBase = f * 3;
  face->nbVertices = 3;
  face++;
 }
 reactphysics3d::PolygonVertexArray *polygonVertexArray = new reactphysics3d::PolygonVertexArray(nbVertices, vertices, 3 * sizeof(float),
                                                                                                 indices, sizeof(int), nbTriangles, polygonFaces,
                                                                                                 reactphysics3d::PolygonVertexArray::VertexDataType::VERTEX_FLOAT_TYPE,
                                                                                                 reactphysics3d::PolygonVertexArray::IndexDataType::INDEX_INTEGER_TYPE);
 reactphysics3d::PolyhedronMesh *polyhedronMesh = scene_p->physicsCommon.createPolyhedronMesh(polygonVertexArray);
 reactphysics3d::ConvexMeshShape *convexMeshShape = scene_p->physicsCommon.createConvexMeshShape(polyhedronMesh);
 body->addCollider(convexMeshShape, transform);
 return;
};

I added a Quad to my scene, which should have two faces (two triangles making up the Quad), and now I'm getting an assertion error:

122     if (2 + nbEdges - mPolygonVertexArray->getNbFaces() != mPolygonVertexArray->getNbVertices()) {
(gdb) 
124         RP3D_LOG("PhysicsCommon", Logger::Level::Error, Logger::Category::PhysicCommon,
(gdb) print nbEdges
$37 = 3
(gdb) u
127         assert(false);
(gdb) 
Cojarexp: /home/saeun/Projects/coje/venjor/reactphysics3d/src/collision/PolyhedronMesh.cpp:127: bool reactphysics3d::PolyhedronMesh::createHalfEdgeStructure(): Assertion `false' failed.

NbFaces = 2, NbVertices = 4.

What is going wrong here, what can I do to fix the assertion?

DanielChappuis commented 1 year ago

The assert here is because a quad is not a valid convex mesh. A convex mesh should be a closed shape that has an interior like a cube for instance. A plane (a quad) is therefore not a valid convex mesh.

Secondly, as described in the documentation here, when you create a convex mesh, you should really avoid coplanar faces. Note that you can have faces with more than three vertices in a convex mesh. Therefore, if you want to have a quad face in your convex mesh, do not use two triangles but directly create a quad face with four vertices. The collision detection will be more stable this way.

I hope this helps.

ZeunO8 commented 1 year ago

I extended the mesh so that it is 3d (box rather than plane) and after fiddling with the order of indices, got createPolyhedronMesh working. Now I'm having the issue where my triangle shape (with depth) does not collide correctly with my box shape. I'm assuming this is because I used triangles for the faces of the triangle (on its sides) and box. So now to work on createConvexMesh to create 4 vertice faces.

ZeunO8 commented 1 year ago

As you can see in the following gif, the rigidbodies are not colliding. This is after I've updated the sides of the triangle and boxes to be 4 vertices.

2023-01-18 22-33-32

Any help would be much appreciated!!

ZeunO8 commented 1 year ago

Turns out I was using the Transform twice in this code rather than Transform::identity() in the second use.

void Entity::createConvexMeshFromData(float *vertices, const int &nbVertices, int *indices, const int &nbFaces, reactphysics3d::PolygonVertexArray::PolygonFace *polygonFaces)
{
 reactphysics3d::Transform transform = reactphysics3d::Transform(reactphysics3d::Vector3(position.x, position.y, position.z), reactphysics3d::Quaternion::identity());
 body = scene_p->physicsWorld->createRigidBody(transform);
 reactphysics3d::PolygonVertexArray *polygonVertexArray = new reactphysics3d::PolygonVertexArray(nbVertices, vertices, 3 * sizeof(float),
                                                                                                 indices, sizeof(int), nbFaces, polygonFaces,
                                                                                                 reactphysics3d::PolygonVertexArray::VertexDataType::VERTEX_FLOAT_TYPE,
                                                                                                 reactphysics3d::PolygonVertexArray::IndexDataType::INDEX_INTEGER_TYPE);
 reactphysics3d::PolyhedronMesh *polyhedronMesh = scene_p->physicsCommon.createPolyhedronMesh(polygonVertexArray);
 reactphysics3d::ConvexMeshShape *convexMeshShape = scene_p->physicsCommon.createConvexMeshShape(polyhedronMesh);
 body->addCollider(convexMeshShape, rp3d::Transform::identity()); // was using transform again here
 return;
};

Now I'm getting a new assertion failure:

Cojarexp: /home/saeun/Projects/coje/venjor/reactphysics3d/src/collision/shapes/ConvexMeshShape.cpp:76: virtual reactphysics3d::Vector3 reactphysics3d::ConvexMeshShape::getLocalSupportPointWithoutMargin(const reactphysics3d::Vector3&) const: Assertion `maxDotProduct >= decimal(0.0)' failed.

maxDotProduct was -4.83096852e-13 in the case with triangle colliding with box

This is as soon as one rigidbody touches another

DanielChappuis commented 1 year ago

Can you post the code you use to create your PolygonVertexArray for your triangular prism?

ZeunO8 commented 1 year ago
Triangle::Triangle(const vec2 &size, const vec3 &position)
{
 (*this).position = position;
 ivec3 *indices = (ivec3 *)(*this).operator()<uint32_t>(EntityQuanta::Indice, 8);
 vec3 *vertices = (vec3 *)(*this).operator()<Floating>(EntityQuanta::Vertex, 6);
 vec4 *colors = (vec4 *)(*this).operator()<Floating>(EntityQuanta::Color, 6);
 indices[0] = {0, 2, 1};
 indices[1] = {1, 2, 5};
 indices[2] = {5, 4, 1};
 indices[3] = {3, 4, 5};
 indices[4] = {3, 1, 0};
 indices[5] = {3, 4, 1};
 indices[6] = {0, 3, 5};
 indices[7] = {5, 2, 0};
 vertices[0] = {-size.x / 2, -size.x / 2, size.y / 2};
 vertices[1] = {0, size.x / 2, size.y / 2};
 vertices[2] = {size.x / 2, -size.x / 2, size.y / 2};
 vertices[3] = {-size.x / 2, -size.x / 2, -size.y / 2};
 vertices[4] = {0, size.x / 2, -size.y / 2};
 vertices[5] = {size.x / 2, -size.x / 2, -size.y / 2};
 colors[0] = {1, 1, 1, 0.8f};
 colors[1] = {0, 0, 1, 1};
 colors[2] = {1, 1, 1, 0.8f};
 colors[3] = {1, 1, 1, 0.8f};
 colors[4] = {0, 0, 1, 1};
 colors[5] = {1, 1, 1, 0.8f};
 return;
};
/*
 */
void Triangle::createConvexMesh()
{
 vec3 *evertices = (vec3 *)(*this).operator()<Floating>(EntityQuanta::Vertex);
 const int nbVertices = QuantaSize(EntityQuanta::Vertex) / 3;
 const int nbFaces = 5; // (2 front and back, 3 on sides)
 float vertices[nbVertices * 3];
 for (Integer i = 0; i < nbVertices; i++)
 {
  vec3 &vertex = evertices[i];
  vertices[(i * 3)] = vertex.x;
  vertices[(i * 3) + 1] = vertex.y;
  vertices[(i * 3) + 2] = vertex.z;
 }
 int indices[(2 * 3) + (3 * 4)];
 ivec3 &indice1 = (ivec3&)indices[0];
 ivec3 &indice2 = (ivec3&)indices[1 * 3];
 ivec4 &indice3 = (ivec4&)indices[2 * 3];
 ivec4 &indice4 = (ivec4&)indices[(2 * 3) + (1 * 4)];
 ivec4 &indice5 = (ivec4&)indices[(2 * 3) + (2 * 4)];
 indice1 = {0, 1, 2};
 indice2 = {5, 4, 3};
 indice3 = {1, 0, 3, 4};
 indice4 = {4, 5, 2, 1};
 indice5 = {0, 2, 5, 3};
 reactphysics3d::PolygonVertexArray::PolygonFace *polygonFaces = new reactphysics3d::PolygonVertexArray::PolygonFace[nbFaces];
 polygonFaces[0].indexBase = 0;
 polygonFaces[0].nbVertices = 3;
 polygonFaces[1].indexBase = 1 * 3;
 polygonFaces[1].nbVertices = 3;
 polygonFaces[2].indexBase = 2 * 3;
 polygonFaces[2].nbVertices = 4;
 polygonFaces[3].indexBase = (2 * 3) + (1 * 4);
 polygonFaces[3].nbVertices = 4;
 polygonFaces[4].indexBase = (2 * 3) + (2 * 4);
 polygonFaces[4].nbVertices = 4;
 createConvexMeshFromData(vertices, nbVertices, indices, nbFaces, polygonFaces);
 return;
};
ZeunO8 commented 1 year ago

Below is Quad creation code for reference

Quad::Quad(const vec3 &size, const vec3 &position)
{
 (*this).position = position;
 ivec3 *indices = (ivec3 *)(*this).operator()<uint32_t>(EntityQuanta::Indice, 12);
 resize(size);
 indices[0] = {0, 3, 1};
 indices[1] = {2, 1, 3};
 indices[2] = {0, 1, 4};
 indices[3] = {1, 5, 4};
 indices[4] = {7, 4, 5};
 indices[5] = {5, 6, 7};
 indices[6] = {7, 6, 2};
 indices[7] = {2, 3, 7};
 indices[8] = {2, 6, 5};
 indices[9] = {5, 1, 2};
 indices[10] = {4, 7, 3};
 indices[11] = {4, 3, 0};
 return;
};
/*
 */
const Boolean Quad::resize(const vec3 &_size)
{
 size = _size;
 vec3 *vertices = (vec3*)(*this).operator()<Floating>(EntityQuanta::Vertex, 8);
 vertices[0] = {size.x / 2, size.y / 2, size.z / 2};
 vertices[1] = {size.x / 2, -(size.y / 2), size.z / 2};
 vertices[2] = {-(size.x / 2), -(size.y / 2), size.z / 2};
 vertices[3] = {-(size.x / 2), size.y / 2, size.z / 2};
 vertices[4] = {size.x / 2, size.y / 2, -size.z / 2};
 vertices[5] = {size.x / 2, -(size.y / 2), -size.z / 2};
 vertices[6] = {-(size.x / 2), -(size.y / 2), -size.z / 2};
 vertices[7] = {-(size.x / 2), size.y / 2, -size.z / 2};
 return true;
};
/*
 */
void Quad::createConvexMesh()
{
 vec3 *evertices = (vec3 *)(*this).operator()<Floating>(EntityQuanta::Vertex);
 const int nbVertices = QuantaSize(EntityQuanta::Vertex) / 3;
 const int nbFaces = 6; // (2 front and back, 2 on sides, 2 on top and bottom)
 float vertices[nbVertices * 3];
 for (Integer i = 0; i < nbVertices; i++)
 {
  vec3 &vertex = evertices[i];
  vertices[(i * 3)] = vertex.x;
  vertices[(i * 3) + 1] = vertex.y;
  vertices[(i * 3) + 2] = vertex.z;
 }
 int indices[(6 * 4)];
 ivec4 &indice1 = (ivec4&)indices[0];
 ivec4 &indice2 = (ivec4&)indices[1 * 4];
 ivec4 &indice3 = (ivec4&)indices[2 * 4];
 ivec4 &indice4 = (ivec4&)indices[3 * 4];
 ivec4 &indice5 = (ivec4&)indices[4 * 4];
 ivec4 &indice6 = (ivec4&)indices[5 * 4];
 indice1 = {0, 1, 2, 3};
 indice2 = {4, 5, 1, 0};
 indice3 = {5, 4, 7, 6};
 indice4 = {3, 2, 6, 7};
 indice5 = {1, 5, 6, 2};
 indice6 = {4, 0, 3, 7};
 reactphysics3d::PolygonVertexArray::PolygonFace *polygonFaces = new reactphysics3d::PolygonVertexArray::PolygonFace[nbFaces];
 polygonFaces[0].indexBase = 0;
 polygonFaces[0].nbVertices = 4;
 polygonFaces[1].indexBase = 1 * 4;
 polygonFaces[1].nbVertices = 4;
 polygonFaces[2].indexBase = 2 * 4;
 polygonFaces[2].nbVertices = 4;
 polygonFaces[3].indexBase = 3 * 4;
 polygonFaces[3].nbVertices = 4;
 polygonFaces[4].indexBase = 4 * 4;
 polygonFaces[4].nbVertices = 4;
 polygonFaces[5].indexBase = 5 * 4;
 polygonFaces[5].nbVertices = 4;
 createConvexMeshFromData(vertices, nbVertices, indices, nbFaces, polygonFaces);
};
ZeunO8 commented 1 year ago

I read in the usermanual:

Note that the vertex coordinates and indices array are not copied and therefore you need to make sure that they exist until the collision shape exists. This is also true for the all the PolygonFace, the PolygonVertexArray and the PolyhedronMesh objects.

Does this mean I need to store vertices and indices in my Entity struct for the lifecycle of the object. Or is it just until I've called physicsCommon.createConvexMeshShape

ZeunO8 commented 1 year ago

No longer have the assertion error after persisting vertices and indices. However the bodies still don't collide

DanielChappuis commented 1 year ago

Does this mean I need to store vertices and indices in my Entity struct for the lifecycle of the object. Or is it just until I've called physicsCommon.createConvexMeshShape

Currently, yes you need to keep the vertices array, the indices array and the PolygonVertexArray around during the whole simulation (until you destroy the collision shape). This will change in the next release of the library.

However the bodies still don't collide

How are the bodies moving (it's not very clear in the GIF)? Using gravity?

ZeunO8 commented 1 year ago

Gravity is off

I'm applying a force like so:

 case KEY_w:
 case KEY_W:
 {
  triangle2_p->body->applyLocalForceAtCenterOfMass({ 0, 50, 0 });
  break;
 };
 case KEY_a:
 case KEY_A:
 {
  triangle2_p->body->applyLocalTorque({ 0, 0, 50 });
  break;
 };
 case KEY_s:
 case KEY_S:
 {
  triangle2_p->body->applyLocalForceAtCenterOfMass({ 0, -50, 0 });
  break;
 };
 case KEY_d:
 case KEY_D:
 {
  triangle2_p->body->applyLocalTorque({ 0, 0, -50 });
  break;
 };

I think it could be due to this:

When you specify the vertices for each face of your convex mesh, be careful with their order. The vertices of a face must be specified in counter clockwise order as seen from the outside of your convex mesh.
ZeunO8 commented 1 year ago

The issue with them not colliding was due to the indices not in ccw order.

Here is a GIF of my currently working collisions with gravity set to { 0, -1, 0 }. As you can see the collision isn't exact and the rendering looks off (they overlap by a fair amount at one point). What could be the issue here?

Untitled

DanielChappuis commented 1 year ago

It's difficult to say what could be wrong here but if you want to check that the collision shapes for the physics are perfectly aligned with the rendered shapes, you can use the DebugRenderer of the library. It will allow you to render the collision shape in your simulation. This way, you can check that they match your rendered shapes.

ZeunO8 commented 1 year ago

I am creating a TrianglesDebugRenderer Entity like so:

struct TrianglesDebugRenderer : Entity
{
 rp3d::DebugRenderer &debugRenderer;
 TrianglesDebugRenderer(rp3d::DebugRenderer &debugRenderer);
 void updateFromDebugRenderer();
 void preEnsureBuffersAndCopyData() override;
 void createMesh() override;
};
/*
 */
#include <coje/Entitys/TrianglesDebugRenderer.hpp>
/*
 */
TrianglesDebugRenderer::TrianglesDebugRenderer(rp3d::DebugRenderer &debugRenderer) : debugRenderer(debugRenderer){};
/*
 */
void TrianglesDebugRenderer::updateFromDebugRenderer()
{
 const rp3d::Array<rp3d::DebugRenderer::DebugTriangle> &triangles = debugRenderer.getTriangles();
 auto trianglesSize = triangles.size();
 vec3 *evertices = (vec3 *)(*this).operator()<Floating>(Entity::Quanta::Vertex, trianglesSize * 3);
 vec4 *ecolors = (vec4 *)(*this).operator()<Floating>(Entity::Quanta::Color, trianglesSize * 3);
 ivec3 *eindices = (ivec3*)(*this).operator()<uint32_t>(Entity::Quanta::Indice, trianglesSize);
 for (Integer index = 0; index < trianglesSize; index++)
 {
  auto &triangle = triangles[index];
  auto &vertex1 = evertices[index * 3];
  auto &vertex2 = evertices[(index * 3) + 1];
  auto &vertex3 = evertices[(index * 3) + 2];
  vertex1.x = triangle.point1.x;
  vertex1.y = triangle.point1.y;
  vertex1.z = triangle.point1.z;
  vertex2.x = triangle.point2.x;
  vertex2.y = triangle.point2.y;
  vertex2.z = triangle.point2.z;
  vertex3.x = triangle.point3.x;
  vertex3.y = triangle.point3.y;
  vertex3.z = triangle.point3.z;
  auto &color1 = ecolors[index * 3];
  auto &color2 = ecolors[(index * 3) + 1];
  auto &color3 = ecolors[(index * 3) + 2];
  color1.x = getColorComponent('R', triangle.color1) / 255.f;
  color1.y = getColorComponent('G', triangle.color1) / 255.f;
  color1.z = getColorComponent('B', triangle.color1) / 255.f;
  color1.w = 1;
  color2.x = getColorComponent('R', triangle.color2) / 255.f;
  color2.y = getColorComponent('G', triangle.color2) / 255.f;
  color2.z = getColorComponent('B', triangle.color2) / 255.f;
  color2.w = 1;
  color3.x = getColorComponent('R', triangle.color3) / 255.f;
  color3.y = getColorComponent('G', triangle.color3) / 255.f;
  color3.z = getColorComponent('B', triangle.color3) / 255.f;
  color3.w = 1;
  auto &indice = eindices[index];
  indice.x = index * 3;
  indice.y = (index * 3) + 1;
  indice.z = (index * 3) + 2;
 }
};
/*
 */
void TrianglesDebugRenderer::preEnsureBuffersAndCopyData()
{
 updateFromDebugRenderer();
};
/*
 */
void TrianglesDebugRenderer::createMesh(){

};
/*
 */

and am now getting the following results: Untitled (1)

ZeunO8 commented 1 year ago

After doing:

  const reactphysics3d::Transform &transform = body->getTransform();
  mat4 matrix;
  transform.getOpenGLMatrix(&matrix[0][0]);
  shader.setMat4("model", matrix);

The collisions don't overlap in my renderer.

Still unsure why the triangles aren't renderer correctly in my TrianglesDebugRenderer

ZeunO8 commented 1 year ago

Here is a gif of working collisions: Untitled (2)

DanielChappuis commented 1 year ago

Great. It seems to be much better. Can I close the issue?

ZeunO8 commented 1 year ago

You can, though I have another issue to open regarding memory alignment of your HeapAllocator on Android

ZeunO8 commented 1 year ago

Would be nice knowing what is going on with the TrianglesDebugRenderer. It's like the triangles use the indices (0,1,2) and (1,2,3) instead of (0,1,2) and (2,3,0).

DanielChappuis commented 1 year ago

Would be nice knowing what is going on with the TrianglesDebugRenderer. It's like the triangles use the indices (0,1,2) and (1,2,3) instead of (0,1,2) and (2,3,0).

What do you mean exactly? What code are you talking about exactly?