ConvexMesh cylinder creation problem

I am trying to create a cylinder PxConvexMesh from 64 vertices (32 side faces and 2 faces top/bottom) “from hand” meaning the PxConvexMeshDesc.flags is 0. I’m providing vertices, polygons and indices to the cooking.

Somewhere in validateConvexMesh it checks for vertex-polygon plane distance (which must be near zero) which fails (“Gu::ConvexMesh::checkHullPolygons: Some hull vertices seems to be too far from hull planes” is printed). It looks like it checks every vertex against every polygon. But why? Shouldn’t only the vertices which belong to the polygon lie on it’s plane?

Anyhow, I think I am misunderstanding something, here’s the pretty simple code which just creates the cylinder geometry and passes it to the cooking

(The cylinders “height axis” is aligned with the x axis, so the “circle” of it is on the y-z plane.

const int VertexCount = 64;
PxVec3 verts[VertexCount];
const int PolyCount = 34; // 32 circle points + lids
PxHullPolygon polys[PolyCount]; 
const int IndexCount = 192; // 4 verts * 32 faces + 32 verts * 2 faces (lids)
uint32_t indices[IndexCount];

const int NumCirclePoints = 32;
const float xLeft = wheelWidth / 2; // x coord of left lid
const float xRight = -xLeft; // x coord of right lid

float angle = 0;
for (int i = 0; i < NumCirclePoints; ++i)
{
	float z = cos(angle) * wheelRadius;
	float y = sin(angle) * wheelRadius;
	
	PxVec3 normal{ 0, sin(angle + Float::Pi / NumCirclePoints), cos(angle + Float::Pi / NumCirclePoints) };
	polys[i].mPlane[0] = normal.x;
	polys[i].mPlane[1] = normal.y;
	polys[i].mPlane[2] = normal.z;
	polys[i].mPlane[3] = normal.y * y + normal.z * z;
	polys[i].mNbVerts = 4;
	polys[i].mIndexBase = i * 4;
	verts[i * 2].x = xLeft;
	verts[i * 2].y = y;
	verts[i * 2].z = z;
	verts[i * 2 + 1].x = xRight;
	verts[i * 2 + 1].y = y;
	verts[i * 2 + 1].z = z;
	uint32_t index = i * 2;
	indices[i * 4] = index;
	indices[i * 4 + 1] = index + 1;
	indices[i * 4 + 2] = (index + 3) % (NumCirclePoints * 2);
	indices[i * 4 + 3] = (index + 2) % (NumCirclePoints * 2);

	angle += 2 * Float::Pi / NumCirclePoints;
}
//left lid
for (int i = 0; i < NumCirclePoints; ++i)
	indices[NumCirclePoints * 4 + i] = i * 2;

//right lid
for (int i = 0; i < NumCirclePoints; ++i)
        // indices in reverse order so that the winding order matches the other polys
	indices[NumCirclePoints * 5 + i] = (NumCirclePoints - i - 1) * 2 + 1;

polys[NumCirclePoints].mPlane[0] = 1;
polys[NumCirclePoints].mPlane[1] = 0;
polys[NumCirclePoints].mPlane[2] = 0;
polys[NumCirclePoints].mPlane[3] = xLeft;
polys[NumCirclePoints].mNbVerts = 32;
polys[NumCirclePoints].mIndexBase = IndexCount - 2 * NumCirclePoints;

polys[NumCirclePoints + 1].mPlane[0] = -1;
polys[NumCirclePoints + 1].mPlane[1] = 0;
polys[NumCirclePoints + 1].mPlane[2] = 0;
polys[NumCirclePoints + 1].mPlane[3] = -xRight;
polys[NumCirclePoints + 1].mNbVerts = 32;
polys[NumCirclePoints + 1].mIndexBase = IndexCount - NumCirclePoints;

PxConvexMeshDesc convexMeshDesc;
convexMeshDesc.points.count = VertexCount;
convexMeshDesc.points.data = verts;
convexMeshDesc.points.stride = sizeof(PxVec3);
convexMeshDesc.polygons.count = PolyCount;
convexMeshDesc.polygons.data = polys;
convexMeshDesc.polygons.stride = sizeof(PxHullPolygon);
convexMeshDesc.indices.count = IndexCount;
convexMeshDesc.indices.data = indices;
convexMeshDesc.indices.stride = sizeof(uint32_t);

bool res = physics->cooking->validateConvexMesh(convexMeshDesc); // false

Hi,
the validation code does check if all vertices are inside the hull, so if a vertex is too far from a plane equation, the validation fails. In your case it means that the normal you provide is in incorrect direction.
Please rotate the normal:
polys[i].mPlane[0] = -normal.x;
polys[i].mPlane[1] = -normal.y;
polys[i].mPlane[2] = -normal.z;
polys[i].mPlane[3] = -normal.y * y - normal.z * z;

Regards,
Ales

Ah, yes, you gave me the hint, thanks. I didn’t have to flip the normal though. The problem was that I used the wrong plane equation. (ax + by + cz = d instead of ax + by + cz + d = 0 like physx wants it)

Therefore, I just had to negate mPlane[3]

I see, glad it works now!