opengl中的有界盒碰撞检测

时间:2012-11-25 15:37:20

标签: c++ opengl collision-detection

这是该计划的重叠。我在visual studio 10,c ++与opengl / glut工作。我正在尝试创建一个迷宫(我从输入文件生成)。然后我使用obj加载器(glm)从3dstudio max导入两个模型。其中一个模型(mo)将在迷宫中移动。所以我在他周围和迷宫的墙壁周围添加了一个边界框。我还在mo周围绘制了一个框,它似乎与角色一起移动/旋转。但由于某种原因(也许我不是在正确的地方做)碰撞测试从未检测到任何东西。我会让代码进行讨论,如果你有任何问题,我会很乐意理顺它们。现在准备好代码。

//bounding boxes
struct BoundingBox
{
    Vector3D max;
    Vector3D min;
};
BoundingBox *box_mo;
BoundingBox *static_box[400];

void initbox(BoundingBox *b){
    b->min.x=100000;
    b->min.y=100000;
    b->min.z=100000;
    b->max.x=-100000;
    b->max.y=-100000;
    b->max.z=-100000;
}

BoundingBox *CreateCollisionBox(GLMmodel *model,GLMgroup *object){
/* GLM doesn't store each vertex together with the object that owns it. It doesn't have that notion. In GLM object don't have vertex, they have triangles. And each triangle is actually an index in the triangle list of the object.*/
    BoundingBox *boxx=(BoundingBox*)malloc(sizeof(BoundingBox));
    initbox(boxx);
    for(int i=0;i<object->numtriangles;i++){
// for each vertex of the triangle pmodel1->triangles[object->triangles[i]]
// calculate min and max
        for(int j=0;j<3;j++){
            GLuint index=model->triangles[object->triangles[i]].vindices[j];
            GLfloat x = model->vertices[index*3 +0];
            GLfloat y = model->vertices[index*3 +1];
            GLfloat z = model->vertices[index*3 +2];
            if(boxx->min.x>x) boxx->min.x =x;
            if(boxx->min.y>y) boxx->min.y =y;
            if(boxx->min.z>z) boxx->min.z =z;

            if(boxx->max.x<x) boxx->max.x =x;
            if(boxx->max.y<y) boxx->max.y =y;
            if(boxx->max.z<z) boxx->max.z =z;
        }
    }
    return boxx;
}

void AddCollisionBox(GLMmodel *model,GLMgroup *object){
    //box[boxindex]=CreateCollisionBox(model,object);
    box_mo=CreateCollisionBox(model,object);
    //boxindex++;
}
// A GLMmodel has a chained list of groups, each group representing an object. 
// Each object has a name (the name you gave it in 3D Studio Max or Gmax).
// Let's you have 10 walls in your scene a few other objects as well and you want to 
// create collision boxes just for the walls and you do not want to make a collision box 
// for one of your objects. You could name all your walls
// like this: Wall1, Wall2, ..., Wall10. If you wanted to add collision boxes just to them 
// you could go through all objects in the scene and if their name contains "Wall" add them.
// with this one: strstr
// Basicly this function does just that: if you want to add boxes for the walls you would call it like this: DefineCollisionBoxes(model,"Wall"); 
void DefineCollisionBoxes(GLMmodel *model,char *name){
    GLMgroup *group = model->groups;
    while(group){
        if(strstr(group->name,name))
            AddCollisionBox(model,group);
        group=group->next;
    }
}

bool Collision(BoundingBox *b,GLfloat x,GLfloat y,GLfloat z){
    return x <= b->max.x && x>= b->min.x && y<= b->max.y && y>= b->min.y && z<= b->max.z && z >= b->min.z;
}

bool CollisionTest(BoundingBox *a,BoundingBox *b){
    /*bool collision=false;
    for(int i=0;i<static_boxes;i++){
        for(float x=static_box[i]->min.x, y=static_box[i]->min.y,z=static_box[i]->min.z ;x<=static_box[i]->max.x && y<=static_box[i]->max.y && z<=static_box[i]->max.z;x+=0.1,y+=0.1,z+=0.1){
            if(Collision(a,x,y,z) == true)
                collision=true;
        }
    }
    return collision;*/
    if(a->min.x <= b->max.x && a->max.x >= b->min.x && a->min.z <= b->max.z && a->max.z >= b->min.z && a->min.y <= b->max.y && a->max.y >= b->min.y)
        return true;
    return false;
}

void drawBox(BoundingBox *b){
    glColor3f(1,1,1);
    glBegin(GL_LINE_LOOP);
    glVertex3f(b->max.x,b->max.y,b->min.z);
    glVertex3f(b->min.x,b->max.y,b->min.z);
    glVertex3f(b->min.x,b->min.y,b->min.z);
    glVertex3f(b->max.x,b->min.y,b->min.z);
    glEnd();

    glBegin(GL_LINE_LOOP);
    glVertex3f(b->max.x,b->min.y,b->max.z);
    glVertex3f(b->max.x,b->max.y,b->max.z);
    glVertex3f(b->min.x,b->max.y,b->max.z);
    glVertex3f(b->min.x,b->min.y,b->max.z);
    glEnd();

    glBegin(GL_LINE_LOOP);
    glVertex3f(b->max.x,b->max.y,b->min.z);
    glVertex3f(b->max.x,b->max.y,b->max.z);
    glVertex3f(b->min.x,b->max.y,b->max.z);
    glVertex3f(b->min.x,b->max.y,b->min.z);
    glEnd();

    glBegin(GL_LINE_LOOP);
    glVertex3f(b->max.x,b->min.y,b->max.z);
    glVertex3f(b->min.x,b->min.y,b->max.z);
    glVertex3f(b->min.x,b->min.y,b->min.z);
    glVertex3f(b->max.x,b->min.y,b->min.z);
    glEnd();
}


//display function
void display(){
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
    //setup view
    glMatrixMode(GL_MODELVIEW);
    glLoadIdentity();
    camera.render
    //read from file, create walls and characters 
    //place model mo in front of the third person camera always
    Vector3D positionn;
    positionn = camera.position + camera.forward*5;

    glPushMatrix();
    glColor3f(0.88f,0.75f,0.49f);
    //here i move my character and calculate bounding box at each frame for him
    glTranslatef(positionn.x,-0.42f,positionn.z);
    glScalef(0.7f,0.7f,0.7f);
    glRotatef(angle,0,1,0);
    drawMo();
    DefineCollisionBoxes(pmodel1,"body"); // actual function call
    drawBox(box_mo); //draw bounding box
    glPopMatrix();

    //test if the character collides with any of the walls - not working
    for(int i=0;i<static_boxes;i++){
        if(CollisionTest(box_mo,static_box[i]) == true){
            printf("collision");
        }
    }


    //swap buffers
    glutSwapBuffers();
}

1 个答案:

答案 0 :(得分:2)

我将尝试概述如何解决碰撞问题。为简单起见,我将做出以下假设:

  1. 我们有一个迷宫内存在的坐标系。
  2. 相机和模型都转换为此坐标系。
  3. 为了让它们进入那个坐标系,在墙上进行的唯一转换是纯粹的平移,没有任何旋转或缩放。
  4. 角色将被翻译,旋转和缩放。
  5. 您不想检查是否会发生碰撞,但如果您发生碰撞。
  6. 您已经或者您将编写或获取矢量和矩阵库
  7. 如果我们使用这些假设,我们可以使用3d中的矩形来表示每个墙。因为我们假设迷宫既没有旋转也没有缩放,我们可以做一次并将其存储在数组或B树中或者任何地方。 (请注意,选择正确的数据结构来存储您的世界以进行计算可以大大提高性能)

    角色在迷宫周围移动,我们认为它可以旋转和缩放。确定角色是否与任何物体碰撞的最简单方法是计算物体的边界球体。一旦你有了你可以为碰撞检测商店只有两点。球体的中心点和球体上的任意点。

    将角色带到迷宫中的正确位置后,您可以将完全相同的变换应用于上述两个点。您可以使用矩阵库执行此操作,也可以在转换字符后查询MODELVIEW矩阵。你可以通过这样调用'glGetFloatv`来实现这个目的:

    GLfloat model[16]; 
    glGetFloatv(GL_MODELVIEW_MATRIX, model); 
    

    一旦你改变了两个点,你就可以找到它们之间的距离 - 这将是一个物体必须远离变形中心点以避免碰撞的距离。

    现在要做的最后一件事是越过相关的平面并使用变换的中心点和计算的距离检查指向平面距离。然后,您需要确保平面的投影中心位于矩形内。

    这里是程序的概要(这里没有真正的代码只是注释和函数抽象)

    void drawCharacter(Matrix4f const & charMatrix);
    void drawMaze(Matrix4f const & mazeMatrix);
    //Will return MAX_FLT to indicate out of rectangle condition
    float distanceToRectangle(Vector3f const & point, Rectangle3df const & plane);
    vector<Rectangle3df> wallPlanes;
    Vector3f centerOfCharSphere;
    Vector3f pointOnCharSphere;
    //Initialization
    /*
    1. Go Over maze create planes for all walls
    2. Calculate character sphere and extract two points: The center of the sphere and a point on the sphere
    */
    
    //Render
    /*
    Calculate the charMatrix - The matrix that takes the character from object CRS to maze CRS
    Calculate the translation matrix for the maze (mazeMatrix) - the matrix that translates the maze into place
    */
    
    Vector3f transformedSphereCenter = charMatrix.transform(centerOfCharSphere);
    Vector3f transformedPointOnSphere = charMatrix.transform(pointOnCharSphere);
    Vector3f R = transformedPointOnSphere - transformedSphereCenter;
    float safeDistance = R.length();
    for(vector<Plane3f>::const_iterator it = wallPlanes.begin(); it != wallPlanes.end(); it++)
    {
        if(distanceToRectangle(transformedSphereCenter, *it) <= safeDistance)
           printf("collided\n");
    }
    
    /*
    Setup projection matrix
    Setup the modelview matrix for the camera
    */
    drawMaze(mazeMatrix);
    drawCharacter(charMatrix);
    

    我希望这会让你走上正轨