I ported some code from Bullet in an effort to make Q3BSP work well in Newton, because right now the only available examples use the mesh structure instead of the underlying true collision structure in the BSP. The difference between the two is pretty evident where one is simplified and composed of convex brushes while the other one is arranged in a way that is efficient for rendering rather than collision and also may contain structures that the scene does not necessarily want to collide against, with no real way to detect and strip them out.
Quake 3 BSP's describe the convex collision shapes as a series of planes and distances, where the interior of the planes is the collision shape. By taking the intersections of these planes you can generate point data that Newton can take to create a convex hull.
This code works for all the brushes in my scene, with the exception of 3. During my debugging process, I decided to try building only one convex hull, this time for brush #69, which is one of the problem brushes. When I ONLY built that one brush, collision worked against that one brush. However, when I continue to build the rest of the brushes as convex hulls, it fails to detect collisions against that brush.
Any ideas? Is there a problem with Newton's implementation of convex hull collision?
- Code: Select all
static bool isPointInsidePlanes(std::vector<glm::vec4> equations, glm::vec3 point, float margin)
{
int numbrushes = equations.size();
for (int i=0;i<numbrushes;i++)
{
const glm::vec3 n1 = glm::vec3(equations[i]);
float dist = glm::dot(n1, point) + equations[i].w - margin;
if (dist > 0.f)
return false;
}
return true;
}
static std::vector<glm::vec3> createPointCloudFromPlaneEquations(std::vector<glm::vec4> equations)
{
std::vector<glm::vec3> vertOut;
const int numbrushes = equations.size();
for (int i=0;i<numbrushes;i++)
{
const glm::vec3& n1 = glm::vec3(equations[i]);
for (int j=i+1;j<numbrushes;j++)
{
const glm::vec3& n2 = glm::vec3(equations[j]);
for (int k=j+1;k<numbrushes;k++)
{
const glm::vec3& n3 = glm::vec3(equations[k]);
glm::vec3 n2n3; n2n3 = glm::cross(n2, n3);
glm::vec3 n3n1; n3n1 = glm::cross(n3, n1);
glm::vec3 n1n2; n1n2 = glm::cross(n1, n2);
if ( (n2n3.length() * n2n3.length() > 0.0001) &&
(n3n1.length() * n3n1.length() > 0.0001) &&
(n1n2.length() * n1n2.length() > 0.0001) )
{
float quotient = glm::dot(n1, n2n3);
if (fabs(quotient) > 0.000001)
{
quotient = -1.f / quotient;
n2n3 *= equations[i].w;
n3n1 *= equations[j].w;
n1n2 *= equations[k].w;
glm::vec3 potentialVertex = n2n3;
potentialVertex += n3n1;
potentialVertex += n1n2;
potentialVertex *= quotient;
if (isPointInsidePlanes(equations, potentialVertex, 0.01f))
vertOut.push_back(potentialVertex);
}
}
}
}
}
return vertOut;
}
void CPhysicsWorld::addBrush(CWorld* world, q3bsp_lump_brushes& brush)
{
std::vector<glm::vec4> planeEquations;
bool isValidBrush = false;
for (int i=0;i<brush.n_brushsides;i++)
{
q3bsp_lump_brushsides& brushside = world->brushsides[brush.brushside + i];
q3bsp_lump_planes& plane = world->planes[brushside.plane];
glm::vec4 planeEq(plane.normal[0], plane.normal[1], plane.normal[2], -plane.dist);
planeEquations.push_back(planeEq);
isValidBrush = true;
}
if (isValidBrush)
{
std::vector<glm::vec3> pointCloud = createPointCloudFromPlaneEquations(planeEquations);
dMatrix identity = GetIdentityMatrix();
NewtonCollision* hull = NewtonCreateConvexHull(_physWorld, pointCloud.size(), &pointCloud[0][0], sizeof(float) * 3, 0.0f, NULL, NULL);
NewtonCreateKinematicBody(_physWorld, hull, &identity[0][0]);
}
}