程序员爱装B

写装A程序 做装C的事情

[搬家文] 第10课 碰撞检测 - part 3

void handleKeypress(unsigned char key, int x, int y) {
    switch (key) {
        case 27//Escape key
            cleanup();
            exit(0);

      当用户按下ESC键时调用cleanup()函数。

        case ' ':
            //Add 20 balls with a random position, velocity, radius, and color
            for(int i = 0; i < 20; i++) {
                Ball* ball = new Ball();
                ball->pos = Vec3f(8 * randomFloat() - 4,
                                  8 * randomFloat() - 4,
                                  8 * randomFloat() - 4);
                ball->v = Vec3f(8 * randomFloat() - 4,
                                8 * randomFloat() - 4,
                                8 * randomFloat() - 4);
                ball->r = 0.1f * randomFloat() + 0.1f;
                ball->color = Vec3f(0.6f * randomFloat() + 0.2f,
                                    0.6f * randomFloat() + 0.2f,
                                    0.6f * randomFloat() + 0.2f);
                _balls.push_back(ball);
                _octree->add(ball);
            }
    }
}

      当用户按空格键,我们创建20个随机大小、位置、速度的球,并加入8叉树和_balls vector。

      如果你看下drawScene,你会发现我们首先绘制盒子的上面和下面,然后绘制球,使用下的代码:

    //Draw the balls
    for(unsigned int i = 0; i < _balls.size(); i++) {
        Ball* ball = _balls[i];
        glPushMatrix();
        glTranslatef(ball->pos[0], ball->pos[1], ball->pos[2]);
        glColor3f(ball->color[0], ball->color[1], ball->color[2]);
        glutSolidSphere(ball->r, 1212); //Draw a sphere
        glPopMatrix();
    }

      我们有个新的函数,glutSolidSphere绘制一个实心球面。第一个参数是球面的半径,第二个和第三个参数表示我们绘制球面使用的多边形数目;数目越多球面的效果越明显。

//Called every TIMER_MS milliseconds
void update(int value) {
    advance(_balls, _octree, (float)TIMER_MS / 1000.0f, _timeUntilUpdate);
    _angle += (float)TIMER_MS / 100;
    if (_angle > 360) {
        _angle -= 360;
    }
    
    glutPostRedisplay();
    glutTimerFunc(TIMER_MS, update, 0);
}

      更新函数调用advance并旋转视角。

8叉树代码

      上面是基本的方法,下面是8叉树的实现:

const int MAX_OCTREE_DEPTH = 6;
const int MIN_BALLS_PER_OCTREE = 3;
const int MAX_BALLS_PER_OCTREE = 6;

      这是8叉数的参数。我们需要一个最大深度为6,每个立方体中超过6个球就将立方体细分,如果小于3个就不拆分。

//Our data structure for making collision detection faster
class Octree {
    private:
        Vec3f corner1; //(minX, minY, minZ)
        Vec3f corner2; //(maxX, maxY, maxZ)
        Vec3f center;//((minX + maxX) / 2, (minY + maxY) / 2, (minZ + maxZ) / 2)

      从我们8叉树类开始,corner1表示立方体中左下最远的角落,corner2表示是最右上方的角落,center表示立方体的正中。

        /* The children of this, if this has any.  children[0][*][*] are the
         * children with x coordinates ranging from minX to centerX.
         * children[1][*][*] are the children with x coordinates ranging from
         * centerX to maxX.  Similarly for the other two dimensions of the
         * children array.
         */
        Octree *children[2][2][2];

      这是8叉树一个节点的孩子。这些孩子自己也是一颗8叉树。请自己阅读注释。

        //Whether this has children
        bool hasChildren;
        //The balls in this, if this doesn't have any children
        set<Ball*> balls;
        //The depth of this in the tree
        int depth;
        //The number of balls in this, including those stored in its children
        int numBalls;

      这些值的意思都很明显了。balls是一个c++ set容器。要使用set,需要包含头文件#include <set>,向一个set中添加元素调用s.insert(element),删除调用s.erase(element),清除所有元素调用s.clear()。

//Adds a ball to or removes a from one to the children of this
void fileBall(Ball* ball, Vec3f pos, bool addBall) {
//Figure out in which child(ren) the ball belongs
     for(int x = 0; x < 2; x++) {
         if (x == 0) {
             if (pos[0] - ball->r > center[0]) {
                 continue;
             }
         }
         else if (pos[0] + ball->r < center[0]) {
              continue;
         }
         for(int y = 0; y < 2; y++) {
             if (y == 0) {
                 if (pos[1] - ball->r > center[1]) {
                     continue;
                 }
             }
             else if (pos[1] + ball->r < center[1]) {
                  continue;
             }
             for(int z = 0; z < 2; z++) {
                 if (z == 0) {
                     if (pos[2] - ball->r > center[2]) {
                         continue;
                     }
                 }
                 else if (pos[2] + ball->r < center[2]) {
                     continue;
                  }
                 //Add or remove the ball
                 if (addBall) {
                     children[x][y][z]->add(ball);
                 }
                 else {
                     children[x][y][z]->remove(ball, pos);
                 }
             }
        }
   }
}

      fileBall 基于pos的位置从这些孩子中添加或删除找出球所属的孩子,调用后面看到的add和remove函数。为了使事情简单,我们检测球的外接立方体是否相交而不使用球面是否相交来进行检测。

posted on 2010-07-19 20:32 camel 阅读(96) 评论(0)  编辑 收藏 引用


只有注册用户登录后才能发表评论。
网站导航: 博客园   IT新闻   BlogJava   知识库   博问   管理


导航

<2024年9月>
25262728293031
1234567
891011121314
15161718192021
22232425262728
293012345

统计

常用链接

留言簿

随笔档案

文章档案

搜索

最新评论

阅读排行榜

评论排行榜