glutで自由落下

glutで実装してみた。
drawstuffが無いとなんとも味気ない画面に。
あれは、他にもマウス操作とかテクスチャ貼ったりと意外と機能あるしな。


z-upを止めてy-upに変更。
状態更新は、タイマーで定期的に呼ぶようにしてみた(ちゃんとFPSを維持するようにはしていない)。

// g++ -DdDOUBLE -lode -lglut main.cpp
#include <ode/ode.h>
#include <GL/glut.h>

const int INTERVAL=1000/30;
const dReal STEP=1.0/INTERVAL;
const dReal GRAVITY=4.9;

dWorldID g_world;

class Ball
{
  dBodyID id;
  dReal radius;
  dReal mass;
  dReal pos[3];
  dReal rot[4];

public:
  Ball(dReal _radius, dReal _mass)
    : radius(_radius), mass(_mass)
    {
      pos[0]=0.0; pos[1]=0.0; pos[2]=0.0;
      rot[0]=0.0; rot[1]=0.0; rot[2]=0.0; rot[3]=0.0;
    }
  void create(dBodyID _id, dReal x, dReal y, dReal z)
  {
    id=_id;
    pos[0]=x;
    pos[1]=y;
    pos[2]=z;
    // mass
    dMass _mass;
    dMassSetZero(&_mass);
    dMassSetSphereTotal(&_mass, mass, radius);
    dBodySetMass(id, &_mass);
    // position
    dBodySetPosition(id, x, y, z);
  }
  void update(){
    setPos(dBodyGetPosition(id));
    setRot(dBodyGetRotation(id));
  }
  void draw()
  {
    glPushMatrix();
    glTranslatef(pos[0], pos[1], pos[2]);
    glRotatef(rot[0], rot[1], rot[2], rot[3]);
    glutSolidSphere(radius, 16, 16);
    glPopMatrix();
  }
  void print(){
    printf("%f %f %f\n", pos[0], pos[1], pos[2]);
  }
  void setPos(const dReal *_pos){
    pos[0]=_pos[0]; pos[1]=_pos[1]; pos[2]=_pos[2];
  }
  void setRot(const dReal *_rot){
    rot[0]=_rot[0]; rot[1]=_rot[1]; rot[2]=_rot[2]; rot[3]=_rot[3];
  }
};
Ball g_ball(0.2, 1.0);

// camera
float g_xyz[]={0.0, 1.0, 6.0};
float g_hpr[]={180.0, 0.0, 0.0};

////////////////////////////////////////////////////////////////////////////////
// ODE
////////////////////////////////////////////////////////////////////////////////
// create ball
void setupScene()
{
  g_ball.create(dBodyCreate(g_world), 0.0, 2.0, 0.0);
}

// update
void onFrame(dReal step)
{
  dWorldStep(g_world, step);
  g_ball.update();
}

////////////////////////////////////////////////////////////////////////////////
// OpenGL
////////////////////////////////////////////////////////////////////////////////
// OpenGL Context生成後に一度だけ呼ぶ
void init(void)
{
  glClearColor(0.4, 0.4, 1.0, 1.0);
  glEnable(GL_DEPTH_TEST);
  glEnable(GL_LIGHTING);
  glEnable(GL_LIGHT0);
}

void drawPlane()
{
  glBegin(GL_QUADS);
  glVertex3f(-10.0,  0.0, -10.0);
  glNormal3f(0.0, 10.0, 0.0);
  glVertex3f(-10.0,  0.0,  10.0);
  glNormal3f(0.0, 10.0, 0.0);
  glVertex3f( 10.0,  0.0,  10.0);
  glNormal3f(0.0, 10.0, 0.0);
  glVertex3f( 10.0,  0.0, -10.0);
  glNormal3f(0.0, 10.0, 0.0);
  glEnd();
}

////////////////////////////////////////////////////////////////////////////////
// glut callback functions
////////////////////////////////////////////////////////////////////////////////
void display()
{
  glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
  // setup view
  glMatrixMode(GL_MODELVIEW);
  glLoadIdentity();
  glTranslatef(-g_xyz[0], -g_xyz[1], -g_xyz[2]);
  glRotatef(g_hpr[2], 0.0, 0.0, 1.0);
  glRotatef(g_hpr[1], 1.0, 0.0, 0.0);
  glRotatef(g_hpr[0], 0.0, 1.0, 0.0);
  // draw
  drawPlane();
  g_ball.draw();
  glutSwapBuffers();
}

void resize(int w, int h)
{
  glViewport(0, 0, w, h);
  glMatrixMode(GL_PROJECTION);
  glLoadIdentity();
  gluPerspective(30.0, (double)w / (double)h, 0.1, 1000.0);
}

void timer(int value)
{
  onFrame(STEP);
  glutTimerFunc(INTERVAL, &timer, 0);
  // debug
  g_ball.print();
  glutPostRedisplay();
}

////////////////////////////////////////////////////////////////////////////////
// entry point
////////////////////////////////////////////////////////////////////////////////
int main(int argc, char **argv)
{
  // setup ODE
  dInitODE();
  g_world=dWorldCreate();
  dWorldSetGravity(g_world, 0, -GRAVITY, 0);
  // setup scene
  setupScene();
  // glut
  glutInit(&argc, argv);
  glutInitDisplayMode(GLUT_RGBA | GLUT_DOUBLE | GLUT_DEPTH);
  glutCreateWindow(argv[0]);
  // initialize OpenGL
  init();
  // setup callback
  glutReshapeFunc(resize);
  glutDisplayFunc(display);
  glutTimerFunc(INTERVAL, &timer, 0);
  glutMainLoop();
  // cleanup ODE
  dWorldDestroy(g_world);
  dCloseODE();
  return 0;
}