zoukankan      html  css  js  c++  java
  • 源代码实现

    源代码实现 - octrees(2)- 碰撞检测

    在前面相关的文章中介绍了,Octrees的一些概念,以及Octrees插入对象的实现

    下面需要实现以下八叉树内的对象碰撞检测的操作。基于八叉树的碰撞检测的基本原理为:遍历八叉树,将子节点的对象和所有祖先节点中的对象进行碰撞检测。

    球的碰撞检测

    球的碰撞检测很简单,只要两个球的球心之间的距离小于半径距离之和,说明两个球发生了碰撞。完整代码如下:

    #include <iostream>
    #include <vector>
    #include <map>
    #include <sstream>
    #include <cmath>
    
    const int MAX_DEPTH = 2;
    
    struct Point {
        Point() {}
        Point(float xi, float yi, float zi) : x(xi), y(yi), z(zi) {}
        float x{ 0.0f };
        float y{ 0.0f };
        float z{ 0.0f };
    
        float operator[](int i) {
            float b[3] = { x,y,z };
            return b[i];
        }
    };
    
    Point operator+(const Point& a, const Point& b) {
        return Point{ a.x + b.x, a.y + b.y, a.z + b.z };
    }
    
    std::ostream& operator<<(std::ostream& os, const Point& pt) {
        os << "{" << pt.x << ", " << pt.y << ", " << pt.z << "}";
        return os;
    }
    
    struct Object {
        Point center{ 0.0f, 0.0f, 0.0f };
        float radius{ 0.0f };
        Object* pNextObject{ nullptr };
    };
    
    std::ostream& operator<<(std::ostream& os, const Object& obj) {
        os << "Obj Center: " << obj.center << " radius: " << obj.radius;
        return os;
    }
    
    struct Node {
        Point center{ 0.0f, 0.0f, 0.0f };   //< The center of the node
        float halfWidth{ 0.0f };            //< The half width of the node
        Node* pChild[8]{ nullptr };         //< The child of the node
        Object* pObjList{ nullptr };        //< The objects in the node
    };
    
    std::ostream& operator<<(std::ostream& os, const Node& node) {
        os << "Node Center: " << node.center << " halfWidth: " << node.halfWidth;
        return os;
    }
    
    // Preallocates an octree down to a specific depth
    Node* BuildOctree(Point center, float halfWidth, int stopDepth)
    {
        if (stopDepth < 0) return NULL;
        else {
            // Construct and fill in 'root' of this subtree
            Node* pNode = new Node;
            pNode->center = center;
            pNode->halfWidth = halfWidth;
            pNode->pObjList = NULL;
    
            // Recursively construct the eight children of the subtree
            Point offset;
            float step = halfWidth * 0.5f;
            for (int i = 0; i < 8; ++i) {
                // 赞
                offset.x = ((i & 1) ? step : -step);
                offset.y = ((i & 2) ? step : -step);
                offset.z = ((i & 4) ? step : -step);
                pNode->pChild[i] = BuildOctree(center + offset, step, stopDepth - 1);
            }
            return pNode;
        }
    }
    
    // Insert object
    void InsertObject(Node* pTree, Object* pObject)
    {
        int index = 0, straddle = 0;
        // Compute the octant number [0..7] the object sphere center is in
        // If straddling any of the dividing x, y, or z planes, exit directly
        for (int i = 0; i < 3; ++i) {
            float delta = pObject->center[i] - pTree->center[i];
            if (std::abs((double)delta) < pTree->halfWidth + pObject->radius) { // TODO CHECK
                straddle = 1;
                break;
            }
            if (delta > 0.0f) index |= (1 << i);
        }
    
        if (!straddle && pTree->pChild[index]) {
            InsertObject(pTree->pChild[index], pObject);
        }
        else {
            pObject->pNextObject = pTree->pObjList;
            pTree->pObjList = pObject;
        }
    }
    
    std::string GenerateKey(Object* pA, Object* pB)
    {
        Object* lower = pA < pB ? pA : pB;
        Object* higher = pA > pB ? pA : pB;
    
        std::ostringstream address;
        address << lower << higher;
        return address.str();
    }
    
    bool TestCollision(Object* pA, Object* pB)
    {
        float x = pA->center[0] - pB->center[0];
        float y = pA->center[1] - pB->center[1];
        float z = pA->center[2] - pB->center[2];
    
        float length = std::sqrt(x * x + y * y + z * z);
        return length < pA->radius + pB->radius;
    }
    
    // Test all objects that could possibly overlap due to cell ancestry and coexistence
    // in the same cell. Assumes objects exist in a single cell only, and fully inside it
    std::map<std::string, std::pair<Object*, Object*>> TestAllCollisions(Node* pTree)
    {
        // Keep track of all ancestor object lists in a stack
        static Node* ancestorStack[MAX_DEPTH];
        static int depth = 0;
    
    
        // Check collision between all objects on this level and all
        // all ancestor objects. The current level is included as its
        // ancestor so all nessary pairwise tests are done
        std::map<std::string, std::pair<Object*, Object*>> result;
        ancestorStack[depth++] = pTree;
        std::string key;
        for (int n = 0; n < depth; n++) {
            Object* pA, * pB;
            for (pA = ancestorStack[n]->pObjList; pA; pA = pA->pNextObject) {
                for (pB = pTree->pObjList; pB; pB = pB->pNextObject) {
                    if (pA == pB) break;
                    key = GenerateKey(pA, pB);
                    if (result.count(key) == 0 && TestCollision(pA, pB)) {
                        result[key] = std::make_pair(pA, pB);
                    }
                }
            }
        }
    
        std::map<std::string, std::pair<Object*, Object*>> childResult;
        // Recursively visit all existing children
        for (int i = 0; i < 8; ++i) {
            if (pTree->pChild[i]) {
                childResult = TestAllCollisions(pTree->pChild[i]);
                result.insert(childResult.begin(), childResult.end());
            }
        }
    
        // Remove current node from ancestor stack before returning
        depth--;
        return result;
    }
    
    void PrintCollisionInfo(const std::map<std::string, std::pair<Object*, Object*>>& mapInterObjects)
    {
        for (auto iter = mapInterObjects.begin(); iter != mapInterObjects.end(); ++iter) {
            std::cout << "Object: " << *(iter->second.first) << " Intersects Object: " << *(iter->second.second) << std::endl;
        }
    }
    
    int main()
    {
        // Assuming the depth of octree is 1
        // The root center is (0.0, 0.0, 0.0)
        // The root width is 2.0
        Node* pRoot = BuildOctree(Point{ 0.0, 0.0, 0.0 }, 1.0, MAX_DEPTH-1);
    
        Object objs[3];
        objs[0].radius = 0.9;
        objs[0].center = Point{ 0.0, 0.0, 0.0 };
        objs[1].radius = 0.1;
        objs[1].center = Point{ -0.5, -0.5, -0.5 };
        objs[2].radius = 0.3;
        objs[2].center = Point{0.1, 0.2, 0.3};
    
        InsertObject(pRoot, &objs[0]);
        InsertObject(pRoot, &objs[1]);
        InsertObject(pRoot, &objs[2]);
    
        std::cout << "=================================" << std::endl;
        auto result = TestAllCollisions(pRoot);
        PrintCollisionInfo(result);
    
        return 0;
    }
    

    输出结果为:

    =================================
    Object: Obj Center: {0, 0, 0} radius: 0.9 Intersects Object: Obj Center: {-0.5, -0.5, -0.5} radius: 0.1
    Object: Obj Center: {0, 0, 0} radius: 0.9 Intersects Object: Obj Center: {0.1, 0.2, 0.3} radius: 0.3
    

    经过test确定,之前怀疑TestAllCollisions有问题的地方是没有问题的。

    三角面片之间的碰撞检测

    要实现三角形碰撞检测,最关键的地方就是需要判断两个三角形是不是相交。判断三角片面之间相交的实现可以参见:https://www.cnblogs.com/grass-and-moon/p/13300247.html

  • 相关阅读:
    Fixed Table Header jQuery Plugin
    jquery实现置顶和置底特效
    HTTP_REFERER头的使用方法
    为什么使用框架?
    javascript:history的一些用法
    揭秘BYOD五大隐藏成本
    PHP中冒号、endif、endwhile、endfor使用介绍
    面对业务开拓重任,CIO们普遍显得缺乏信心
    Asp.net中的页面传值
    DropDownList的用法
  • 原文地址:https://www.cnblogs.com/grass-and-moon/p/13303647.html
Copyright © 2011-2022 走看看