微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

AABB碰撞代码的三角形不起作用

如何解决AABB碰撞代码的三角形不起作用

我一直在调整和修改我的代码以针对一个三角形测试AABB,但我不确定自己做错了什么。我正在使用分离轴定理,因为我认为这是检测AABB和三角形之间碰撞的最好且唯一的方法(如果我错了或者有更好/更快的方法,请纠正我)。当前,当发生碰撞时,它什么也检测不到。

代码相对较小,每帧调用isAABBIntersectingTriangle,希望了解数学或算法的人可以对我有所帮助。该函数将边界框min和max作为两个3D向量(glm :: vec3)和三个三角形顶点(tri1,tri2,tri3)作为glm :: vec3s。这些坐标都在世界空间中(AABB坐标仅通过位置和比例转换,三角形通过位置旋转和比例转换)

bool SATTriangleAABBCheck(glm::vec3 axis,glm::vec3 bBoxMin,glm::vec3 bBoxMax,glm::vec3 tri1,glm::vec3 tri2,glm::vec3 tri3)
{
    //Dot triangle vertices
    float triVert1 = glm::dot(axis,tri1);
    float triVert2 = glm::dot(axis,tri2);
    float triVert3 = glm::dot(axis,tri3);

    float triMin = glm::min(glm::min(triVert1,triVert2),triVert3);
    float triMax = glm::max(glm::max(triVert1,triVert3);

    //Dot cube vertices
    float v1 = glm::dot(axis,glm::vec3(bBoxMin.x,bBoxMin.y,bBoxMin.z));
    float v2 = glm::dot(axis,glm::vec3(bBoxMax.x,bBoxMax.y,bBoxMax.z));
    float v3 = glm::dot(axis,bBoxMin.z));
    float v4 = glm::dot(axis,bBoxMax.z));
    float v5 = glm::dot(axis,bBoxMin.z));
    float v6 = glm::dot(axis,bBoxMax.z));
    float v7 = glm::dot(axis,bBoxMax.z));
    float v8 = glm::dot(axis,bBoxMin.z));

    float aabbMin = glm::min(glm::min(glm::min(glm::min(glm::min(glm::min(glm::min(v1,v2),v3),v4),v5),v6),v7),v8);
    float aabbMax = glm::max(glm::max(glm::max(glm::max(glm::max(glm::max(glm::max(v1,v8);

    if ((triMin < aabbMax && triMin > aabbMin) || (triMax < aabbMax && triMax > aabbMin))
        return true;
    if ((aabbMin < triMax && aabbMin > triMin) || (aabbMax < triMax && aabbMax > triMin))
        return true;

    return false;
}

glm::vec3 CalcSurfacenormal(glm::vec3 tri1,glm::vec3 tri3)
{
    glm::vec3 u = tri2 - tri1;
    glm::vec3 v = tri3 - tri1;
    glm::vec3 nrmcross = glm::normalize(glm::cross(u,v));
    return nrmcross;
}

bool isAABBIntersectingTriangle(glm::vec3 bBoxMin,glm::vec3 tri3)
{   
    //AABB face normals
    glm::vec3 axis1(1,0);
    glm::vec3 axis2(0,1,0);
    glm::vec3 axis3(0,1);

    //Triangle face normal
    glm::vec3 axis4 = CalcSurfacenormal(tri1,tri2,tri3);

    //Edge normals
    glm::vec3 e1 = tri2 - tri1;
    glm::vec3 e2 = tri3 - tri1;
    glm::vec3 e3 = tri3 - tri2;
    glm::vec3 e4 = glm::vec3(bBoxMax.x,bBoxMax.z) - glm::vec3(bBoxMin.x,bBoxMax.z);
    glm::vec3 e5 = glm::vec3(bBoxMax.x,bBoxMax.z) - glm::vec3(bBoxMax.x,bBoxMax.z);
    glm::vec3 e6 = glm::vec3(bBoxMax.x,bBoxMin.z);

    //Cross products of each edge
    glm::vec3 axis5 = glm::normalize(glm::cross(e1,e4));
    glm::vec3 axis6 = glm::normalize(glm::cross(e1,e5));
    glm::vec3 axis7 = glm::normalize(glm::cross(e1,e6));
    glm::vec3 axis8 = glm::normalize(glm::cross(e2,e4));
    glm::vec3 axis9 = glm::normalize(glm::cross(e2,e5));
    glm::vec3 axis10 = glm::normalize(glm::cross(e2,e6));
    glm::vec3 axis11 = glm::normalize(glm::cross(e3,e4));
    glm::vec3 axis12 = glm::normalize(glm::cross(e3,e5));
    glm::vec3 axis13 = glm::normalize(glm::cross(e3,e6));


    //If no overlap on all axes
    if (!SATTriangleAABBCheck(axis1,bBoxMin,bBoxMax,tri1,tri3)) return false;
    if (!SATTriangleAABBCheck(axis2,tri3)) return false;
    if (!SATTriangleAABBCheck(axis3,tri3)) return false;
    if (!SATTriangleAABBCheck(axis4,tri3)) return false;
    if (!SATTriangleAABBCheck(axis5,tri3)) return false;
    if (!SATTriangleAABBCheck(axis6,tri3)) return false;
    if (!SATTriangleAABBCheck(axis7,tri3)) return false;
    if (!SATTriangleAABBCheck(axis8,tri3)) return false;
    if (!SATTriangleAABBCheck(axis9,tri3)) return false;
    if (!SATTriangleAABBCheck(axis10,tri3)) return false;
    if (!SATTriangleAABBCheck(axis11,tri3)) return false;
    if (!SATTriangleAABBCheck(axis12,tri3)) return false;
    if (!SATTriangleAABBCheck(axis13,tri3)) return false;

    return true;
}

这也是主循环中的代码,该代码计算三角形的世界位置,然后调用函数,我认为这里没有什么不对的地方,因为它可能是最受关注的地方:

glm::vec3 tri1 = glm::vec3(entities[0]->model.meshes[0].vertices[entities[0]->model.meshes[0].indices[0]].position.x * entities[0]->scale,entities[0]->model.meshes[0].vertices[entities[0]->model.meshes[0].indices[0]].position.y * entities[0]->scale,entities[0]->model.meshes[0].vertices[entities[0]->model.meshes[0].indices[0]].position.z * entities[0]->scale);
    glm::vec3 tri2 = glm::vec3(entities[0]->model.meshes[0].vertices[entities[0]->model.meshes[0].indices[1]].position.x * entities[0]->scale,entities[0]->model.meshes[0].vertices[entities[0]->model.meshes[0].indices[1]].position.y * entities[0]->scale,entities[0]->model.meshes[0].vertices[entities[0]->model.meshes[0].indices[1]].position.z * entities[0]->scale);
    glm::vec3 tri3 = glm::vec3(entities[0]->model.meshes[0].vertices[entities[0]->model.meshes[0].indices[2]].position.x * entities[0]->scale,entities[0]->model.meshes[0].vertices[entities[0]->model.meshes[0].indices[2]].position.y * entities[0]->scale,entities[0]->model.meshes[0].vertices[entities[0]->model.meshes[0].indices[2]].position.z * entities[0]->scale);

    //Translate these tris by the model matrix
    glm::mat4 mat(1.0f);
    mat = glm::translate(mat,glm::vec3(entities[0]->xPos,entities[0]->yPos,entities[0]->zPos));
    mat = glm::rotate(mat,glm::radians(entities[0]->xRot),glm::vec3(1,0));
    mat = glm::rotate(mat,glm::radians(entities[0]->yRot),glm::vec3(0,glm::radians(entities[0]->zRot),1));
    mat = glm::scale(mat,glm::vec3(entities[0]->scale,entities[0]->scale,entities[0]->scale));
    glm::vec4 tri11 = mat * glm::vec4(tri1.x,tri1.y,tri1.z,1.0f);
    glm::vec4 tri22 = mat * glm::vec4(tri2.x,tri2.y,tri2.z,1.0f);
    glm::vec4 tri33 = mat * glm::vec4(tri3.x,tri3.y,tri3.z,1.0f);


    if (isAABBIntersectingTriangle(entities[3]->bBoxMin,entities[3]->bBoxMax,glm::vec3(tri11.x,tri11.y,tri11.z),glm::vec3(tri22.x,tri22.y,tri22.z),glm::vec3(tri33.x,tri33.y,tri33.z)))
    {
        std::cout << "AABB Tri collision\n";
    }

解决方法

测试中有一部分代码不够健壮。

glm::vec3 axis5 = glm::normalize(glm::cross(e1,e4));
glm::vec3 axis6 = glm::normalize(glm::cross(e1,e5));
glm::vec3 axis7 = glm::normalize(glm::cross(e1,e6));
glm::vec3 axis8 = glm::normalize(glm::cross(e2,e4));
glm::vec3 axis9 = glm::normalize(glm::cross(e2,e5));
glm::vec3 axis10 = glm::normalize(glm::cross(e2,e6));
glm::vec3 axis11 = glm::normalize(glm::cross(e3,e4));
glm::vec3 axis12 = glm::normalize(glm::cross(e3,e5));
glm::vec3 axis13 = glm::normalize(glm::cross(e3,e6));

这些计算是无条件进行的。但是,仅当向量的长度不为零时,才能对向量进行归一化。当因子平行时,叉积的长度为零。因此,如果e1,e2,e3中的任何一个与e4,e5,e6中的任何一个平行,则对应的叉积将为零长度矢量,并且归一化的坐标将为NaN

NaN的一个大问题是它会“毒化”所有计算。特别是,向量中的单个NaN坐标足以使涉及该向量的任何点积评估为NaN。这确实与SATTriangleAABBCheck的尝试混淆。 (零长度向量同样会干扰函数,因此跳过归一化不是解决方案。)

该解决方案取决于叉积为零的情况下应采取的措施。在这种情况下,该零表示计算无法生成分隔轴。检查的轴数少于最大数量,因为不同的平行线永不相交。在零轴(标准化为NaN)上跳过测试。

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。