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, 12, 12); //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函数。为了使事情简单,我们检测球的外接立方体是否相交而不使用球面是否相交来进行检测。