QT向量类实现GJK碰撞检测算法3d版本

用QT实现GJK算法(吉尔伯特-约翰逊-基尔蒂距离算法)。这里使用QVector3D作为向量类。

cpp 复制代码
#ifndef GJKALGORITHM_H
#define GJKALGORITHM_H

#include <QVector3D>
#include <QList>
#include <cmath>

class GJKAlgorithm
{
public:
    // 凸体接口(需要实现支持函数)
    class ConvexShape {
    public:
        virtual ~ConvexShape() {}
        virtual QVector3D getFarthestPointInDirection(const QVector3D& direction) const = 0;
    };

    // 测试两个凸体是否相交
    static bool checkCollision(const ConvexShape& shapeA, const ConvexShape& shapeB);
    
    // 计算两个凸体之间的距离
    static float getDistance(const ConvexShape& shapeA, const ConvexShape& shapeB, 
                            QVector3D* closestPointA = nullptr, 
                            QVector3D* closestPointB = nullptr);

private:
    // 辅助函数
    static QVector3D support(const ConvexShape& shapeA, const ConvexShape& shapeB, 
                            const QVector3D& direction);
    static bool containsOrigin(QList<QVector3D>& simplex);
    static bool updateSimplex(QList<QVector3D>& simplex, QVector3D& direction);
    static bool lineCase(QList<QVector3D>& simplex, QVector3D& direction);
    static bool triangleCase(QList<QVector3D>& simplex, QVector3D& direction);
    static bool tetrahedronCase(QList<QVector3D>& simplex, QVector3D& direction);
    static float signedVolume(const QVector3D& a, const QVector3D& b, 
                             const QVector3D& c, const QVector3D& d);
};

#endif // GJKALGORITHM_H
cpp 复制代码
#include "GJKAlgorithm.h"
#include <QDebug>

// 实现部分
QVector3D GJKAlgorithm::support(const ConvexShape& shapeA, const ConvexShape& shapeB, 
                               const QVector3D& direction)
{
    // 计算Minkowski差的支持点
    QVector3D pointA = shapeA.getFarthestPointInDirection(direction);
    QVector3D pointB = shapeB.getFarthestPointInDirection(-direction);
    return pointA - pointB;
}

bool GJKAlgorithm::checkCollision(const ConvexShape& shapeA, const ConvexShape& shapeB)
{
    // 初始化方向向量(任意方向)
    QVector3D direction(1.0f, 0.0f, 0.0f);
    
    // 获取初始支持点
    QVector3D simplexPoint = support(shapeA, shapeB, direction);
    
    // 构建初始单形(1个点)
    QList<QVector3D> simplex;
    simplex.append(simplexPoint);
    
    // 更新搜索方向(指向原点)
    direction = -simplexPoint;
    
    const int maxIterations = 32;
    int iteration = 0;
    
    while (iteration++ < maxIterations) {
        // 获取新的支持点
        QVector3D newPoint = support(shapeA, shapeB, direction);
        
        // 如果新点在方向上的投影小于等于0,则没有碰撞
        if (QVector3D::dotProduct(newPoint, direction) <= 0.0f) {
            return false;
        }
        
        // 添加到单形
        simplex.append(newPoint);
        
        // 检查单形是否包含原点
        if (containsOrigin(simplex)) {
            return true;
        }
    }
    
    return false;
}

bool GJKAlgorithm::containsOrigin(QList<QVector3D>& simplex)
{
    QVector3D direction;
    
    switch (simplex.size()) {
    case 2:  // 线
        return lineCase(simplex, direction);
    case 3:  // 三角形
        return triangleCase(simplex, direction);
    case 4:  // 四面体
        return tetrahedronCase(simplex, direction);
    default:
        return false;
    }
}

bool GJKAlgorithm::updateSimplex(QList<QVector3D>& simplex, QVector3D& direction)
{
    switch (simplex.size()) {
    case 2:
        return lineCase(simplex, direction);
    case 3:
        return triangleCase(simplex, direction);
    case 4:
        return tetrahedronCase(simplex, direction);
    default:
        return false;
    }
}

bool GJKAlgorithm::lineCase(QList<QVector3D>& simplex, QVector3D& direction)
{
    QVector3D a = simplex.last();
    QVector3D b = simplex.first();
    
    QVector3D ab = b - a;
    QVector3D ao = -a;
    
    // 如果AB指向A->B,AO在AB方向上
    if (QVector3D::dotProduct(ab, ao) > 0) {
        // 原点在AB的垂直方向上
        direction = QVector3D::crossProduct(QVector3D::crossProduct(ab, ao), ab);
        if (direction.lengthSquared() == 0) {
            // 共线情况
            direction = QVector3D::crossProduct(ab, QVector3D(1, 0, 0));
            if (direction.lengthSquared() == 0) {
                direction = QVector3D::crossProduct(ab, QVector3D(0, 1, 0));
            }
        }
    } else {
        // 原点靠近A点
        simplex.clear();
        simplex.append(a);
        direction = ao;
    }
    
    return false;
}

bool GJKAlgorithm::triangleCase(QList<QVector3D>& simplex, QVector3D& direction)
{
    QVector3D a = simplex[2]; // 最新点
    QVector3D b = simplex[1];
    QVector3D c = simplex[0];
    
    QVector3D ab = b - a;
    QVector3D ac = c - a;
    QVector3D ao = -a;
    
    // 计算三角形法线
    QVector3D abc = QVector3D::crossProduct(ab, ac);
    
    // 检查原点在三角形的哪一侧
    if (QVector3D::dotProduct(QVector3D::crossProduct(abc, ac), ao) > 0) {
        // 原点在AC边外
        if (QVector3D::dotProduct(ac, ao) > 0) {
            simplex.clear();
            simplex.append(c);
            simplex.append(a);
            direction = QVector3D::crossProduct(QVector3D::crossProduct(ac, ao), ac);
        } else {
            return lineCase(simplex, direction);
        }
    } else {
        if (QVector3D::dotProduct(QVector3D::crossProduct(ab, abc), ao) > 0) {
            // 原点在AB边外
            return lineCase(simplex, direction);
        } else {
            // 原点在三角形上方或下方
            if (QVector3D::dotProduct(abc, ao) > 0) {
                // 原点在上方
                simplex.clear();
                simplex.append(a);
                simplex.append(b);
                simplex.append(c);
                direction = abc;
            } else {
                // 原点在下方
                simplex.clear();
                simplex.append(a);
                simplex.append(c);
                simplex.append(b);
                direction = -abc;
            }
        }
    }
    
    return false;
}

bool GJKAlgorithm::tetrahedronCase(QList<QVector3D>& simplex, QVector3D& direction)
{
    QVector3D a = simplex[3];
    QVector3D b = simplex[2];
    QVector3D c = simplex[1];
    QVector3D d = simplex[0];
    
    // 检查四面体是否包含原点
    float volume1 = signedVolume(a, b, c, d);
    float volume2 = signedVolume(a, b, c, QVector3D(0, 0, 0));
    
    // 根据有向体积判断原点位置
    // 这里简化处理,实际需要检查所有面
    
    // 如果四面体包含原点,则发生碰撞
    if ((volume1 > 0 && volume2 > 0) || (volume1 < 0 && volume2 < 0)) {
        return true;
    }
    
    // 简化处理:返回最近的面
    // 实际实现需要更精确的最近点计算
    simplex.removeFirst(); // 移除最旧的点
    return triangleCase(simplex, direction);
}

float GJKAlgorithm::signedVolume(const QVector3D& a, const QVector3D& b,
                                const QVector3D& c, const QVector3D& d)
{
    QVector3D ab = b - a;
    QVector3D ac = c - a;
    QVector3D ad = d - a;
    
    return QVector3D::dotProduct(QVector3D::crossProduct(ab, ac), ad);
}

float GJKAlgorithm::getDistance(const ConvexShape& shapeA, const ConvexShape& shapeB,
                               QVector3D* closestPointA, QVector3D* closestPointB)
{
    // 扩展GJK实现,计算最近距离(EPA算法)
    // 这里简化实现,返回碰撞检测结果
    bool collision = checkCollision(shapeA, shapeB);
    
    if (collision) {
        // 如果碰撞,距离为0
        return 0.0f;
    }
    
    // 这里应该实现EPA算法来获取最近距离
    // 简化实现返回一个估计值
    return 1.0f; // 需要完整实现
}
cpp 复制代码
// 示例:实现一个凸体类(AABB包围盒)
#ifndef AABBSHAPE_H
#define AABBSHAPE_H

#include "GJKAlgorithm.h"
#include <QVector3D>

class AABBShape : public GJKAlgorithm::ConvexShape
{
public:
    AABBShape(const QVector3D& min, const QVector3D& max) 
        : m_min(min), m_max(max) {}
    
    QVector3D getFarthestPointInDirection(const QVector3D& direction) const override
    {
        QVector3D point;
        point.setX((direction.x() >= 0) ? m_max.x() : m_min.x());
        point.setY((direction.y() >= 0) ? m_max.y() : m_min.y());
        point.setZ((direction.z() >= 0) ? m_max.z() : m_min.z());
        return point;
    }
    
    QVector3D getCenter() const {
        return (m_min + m_max) * 0.5f;
    }
    
    QVector3D getExtents() const {
        return (m_max - m_min) * 0.5f;
    }

private:
    QVector3D m_min;
    QVector3D m_max;
};

#endif // AABBSHAPE_H
cpp 复制代码
// 示例:实现一个球体
#ifndef SPHERESHAPE_H
#define SPHERESHAPE_H

#include "GJKAlgorithm.h"
#include <QVector3D>
#include <cmath>

class SphereShape : public GJKAlgorithm::ConvexShape
{
public:
    SphereShape(const QVector3D& center, float radius)
        : m_center(center), m_radius(radius) {}
    
    QVector3D getFarthestPointInDirection(const QVector3D& direction) const override
    {
        if (direction.lengthSquared() == 0) {
            return m_center + QVector3D(m_radius, 0, 0);
        }
        QVector3D normalizedDir = direction.normalized();
        return m_center + normalizedDir * m_radius;
    }
    
    float getRadius() const { return m_radius; }
    QVector3D getCenter() const { return m_center; }

private:
    QVector3D m_center;
    float m_radius;
};

#endif // SPHERESHAPE_H
cpp 复制代码
// 使用示例
#include "GJKAlgorithm.h"
#include "AABBShape.h"
#include "SphereShape.h"

void testGJK()
{
    // 创建两个AABB
    AABBShape box1(QVector3D(0, 0, 0), QVector3D(2, 2, 2));
    AABBShape box2(QVector3D(1, 1, 1), QVector3D(3, 3, 3));
    
    // 检测碰撞
    bool collision = GJKAlgorithm::checkCollision(box1, box2);
    qDebug() << "AABB-AABB Collision:" << collision;
    
    // 创建球体
    SphereShape sphere1(QVector3D(0, 0, 0), 1.0f);
    SphereShape sphere2(QVector3D(1.5f, 0, 0), 1.0f);
    
    // 检测碰撞
    collision = GJKAlgorithm::checkCollision(sphere1, sphere2);
    qDebug() << "Sphere-Sphere Collision:" << collision;
    
    // 混合检测
    collision = GJKAlgorithm::checkCollision(box1, sphere1);
    qDebug() << "AABB-Sphere Collision:" << collision;
}

这个实现提供了:

  1. GJK算法核心:使用Minkowski差和单形演算检测碰撞
  2. 凸体接口:定义了支持函数接口
  3. 示例形状:实现了AABB和球体的支持函数
  4. 碰撞检测:返回布尔值表示是否碰撞

要使用这个实现:

  1. 继承GJKAlgorithm::ConvexShape实现你的形状
  2. 实现getFarthestPointInDirection方法
  3. 调用GJKAlgorithm::checkCollision检测碰撞

注意:这个实现包含了基本的GJK碰撞检测。对于完整的距离计算和最近点信息,需要实现EPA(扩展多边形算法)。

相关推荐
煤球王子2 小时前
学而时习之:C++ 中的文件处理
c++
天赐学c语言2 小时前
12.10 - 合并两个有序链表 && 对字节对齐的理解
数据结构·c++·leetcode·链表
仰泳的熊猫2 小时前
1092 To Buy or Not to Buy
数据结构·c++·算法·pat考试
CSDN_RTKLIB2 小时前
解除vcpkg对VS的全局配置注入
c++
罗湖老棍子2 小时前
【深基16.例3】二叉树深度(洛谷P4913)
数据结构·算法·二叉树
Charlo2 小时前
Open-AutoGLM Windows 安装部署教程
算法·设计模式·github
君义_noip2 小时前
信息学奥赛一本通 4017:【GESP2309三级】小杨的储蓄 | 洛谷 B3867 [GESP202309 三级] 小杨的储蓄
c++·算法·gesp·信息学奥赛
高山上有一只小老虎3 小时前
判断是否为数独数组
java·算法