245 lines
7.5 KiB
C++
245 lines
7.5 KiB
C++
|
|
/* Author: Josh Holtrop
|
|
* DornerWorks screensaver
|
|
* This module implements a slightly more interesting screensaver
|
|
* mode that contains many DornerWorks logos that "tumble" around
|
|
* inside a cube-shaped container as gravity randomly shifts every
|
|
* few seconds
|
|
*/
|
|
|
|
#include <math.h> /* fmax() */
|
|
#include <stdlib.h> /* rand() */
|
|
#include <GL/gl.h>
|
|
#include <GL/glu.h>
|
|
#include "TumblingLogos.h"
|
|
#include <ode/ode.h>
|
|
|
|
#include <iostream>
|
|
using namespace std;
|
|
|
|
#define WORLD_STEP 0.001
|
|
/* number of milliseconds after which to switch gravity */
|
|
#define GRAVITY_CHANGE_MSEC 4000
|
|
|
|
static double randDbl()
|
|
{
|
|
return ((double) rand() / (double) RAND_MAX);
|
|
}
|
|
|
|
TumblingLogos::TumblingLogos(SSMain * _SSMain) : SSMode(_SSMain)
|
|
{
|
|
/* set up our specific openGL settings */
|
|
glMatrixMode(GL_PROJECTION);
|
|
glLoadIdentity();
|
|
gluPerspective(60.0,
|
|
(double)m_SSMain->getWidth()
|
|
/ (double)m_SSMain->getHeight()
|
|
/ (double)m_SSMain->getNumMonitors(),
|
|
0.01,
|
|
1000.01);
|
|
glMatrixMode(GL_MODELVIEW);
|
|
|
|
/* create the ODE world */
|
|
createWorld();
|
|
|
|
m_lastGravityChangeTime = m_lastElapsed = 0;
|
|
}
|
|
|
|
TumblingLogos::~TumblingLogos()
|
|
{
|
|
int sz = m_logos.size();
|
|
for (int i = 0; i < sz; i++)
|
|
{
|
|
delete m_logos[i];
|
|
}
|
|
dJointGroupDestroy(m_contactJointGroup);
|
|
dWorldDestroy(m_world);
|
|
dSpaceDestroy(m_space);
|
|
}
|
|
|
|
void TumblingLogos::createWorld()
|
|
{
|
|
/* set up the ODE world and collision space */
|
|
m_world = dWorldCreate();
|
|
// dWorldSetCFM(m_world, 1e-5);
|
|
dWorldSetGravity(m_world, 0, -9.81, 0);
|
|
dWorldSetERP(m_world, 0.8);
|
|
m_space = dHashSpaceCreate(0);
|
|
m_contactJointGroup = dJointGroupCreate(0);
|
|
|
|
m_logoSize = 0.0;
|
|
m_boxSize = 0.0;
|
|
double logo_offset = 0.0;
|
|
double logoCellSize = 0.0;
|
|
|
|
/* create the logos to fill the tumble box */
|
|
for (int x = 0; x < TUMBLE_BOX_CELLS; x++)
|
|
{
|
|
for (int y = 0; y < TUMBLE_BOX_CELLS; y++)
|
|
{
|
|
for (int z = 0; z < TUMBLE_BOX_CELLS; z++)
|
|
{
|
|
LogoBox * lb = new LogoBox(m_world, m_space);
|
|
|
|
if (m_boxSize == 0.0)
|
|
{
|
|
m_logoSize = fmax(fmax(lb->getWidth(), lb->getDepth()),
|
|
lb->getHeight());
|
|
logoCellSize = m_logoSize * 1.3;
|
|
m_boxSize = logoCellSize * TUMBLE_BOX_CELLS;
|
|
logo_offset = logoCellSize/2.0 - m_boxSize/2.0;
|
|
}
|
|
|
|
dMatrix3 rotation;
|
|
dRFromEulerAngles(rotation,
|
|
randDbl() * M_PI * 2.0,
|
|
randDbl() * M_PI * 2.0,
|
|
randDbl() * M_PI * 2.0);
|
|
dBodySetPosition(lb->getBody(),
|
|
x * logoCellSize + logo_offset +
|
|
(randDbl() - 0.5) * 0.1 * m_logoSize,
|
|
y * logoCellSize + logo_offset +
|
|
(randDbl() - 0.5) * 0.1 * m_logoSize,
|
|
z * logoCellSize + logo_offset +
|
|
(randDbl() - 0.5) * 0.1 * m_logoSize);
|
|
dBodySetRotation(lb->getBody(), rotation);
|
|
m_logos.push_back(lb);
|
|
}
|
|
}
|
|
}
|
|
|
|
/* tumble box will be size m_boxSize X m_boxSize X m_boxSize
|
|
* centered at the origin */
|
|
/* create planes surrounding the tumble box */
|
|
dCreatePlane(m_space, -1, 0, 0, -m_boxSize/2.0);
|
|
dCreatePlane(m_space, 1, 0, 0, -m_boxSize/2.0);
|
|
dCreatePlane(m_space, 0, -1, 0, -m_boxSize/2.0);
|
|
dCreatePlane(m_space, 0, 1, 0, -m_boxSize/2.0);
|
|
dCreatePlane(m_space, 0, 0, -1, -m_boxSize/2.0);
|
|
dCreatePlane(m_space, 0, 0, 1, -m_boxSize/2.0);
|
|
}
|
|
|
|
void TumblingLogos::update()
|
|
{
|
|
SSMode::update();
|
|
|
|
if (m_lastElapsed == 0)
|
|
m_lastGravityChangeTime = m_lastElapsed = m_elapsed;
|
|
|
|
/* change gravity if it has been GRAVITY_CHANGE_MSEC milliseconds */
|
|
if (m_elapsed - m_lastGravityChangeTime > GRAVITY_CHANGE_MSEC)
|
|
{
|
|
changeGravity();
|
|
m_lastGravityChangeTime = m_elapsed;
|
|
}
|
|
|
|
Uint32 delta = m_elapsed - m_lastElapsed;
|
|
for (unsigned int i = 0; i < delta / 2; i++)
|
|
worldStep();
|
|
|
|
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
|
|
int numMonitors = m_SSMain->getNumMonitors();
|
|
int width = m_SSMain->getWidth();
|
|
int height = m_SSMain->getHeight();
|
|
for (int i = 0; i < numMonitors; i++)
|
|
{
|
|
glViewport(i*width/numMonitors, 0, width/numMonitors, height);
|
|
glLoadIdentity();
|
|
gluLookAt(0, 0, m_boxSize * 1.25, 0, 0, 0, 0, 1, 0);
|
|
glRotatef(m_elapsed/60.0f, 0, 1, 0);
|
|
|
|
/* draw the logos */
|
|
int sz = m_logos.size();
|
|
for (int i = 0; i < sz; i++)
|
|
{
|
|
dBodyID body = m_logos[i]->getBody();
|
|
pushTransform(dBodyGetPosition(body),
|
|
dBodyGetRotation(body));
|
|
m_logos[i]->draw();
|
|
glPopMatrix();
|
|
}
|
|
|
|
/* draw the wireframe bounding box */
|
|
glColor3f(1, 1, 1);
|
|
double halfBox = m_boxSize/2.0;
|
|
glPushAttrib(GL_LIGHTING_BIT);
|
|
glDisable(GL_LIGHTING);
|
|
for (int i = -1; i <= 1; i += 2)
|
|
{
|
|
glBegin(GL_LINE_LOOP);
|
|
glVertex3f(-halfBox, -halfBox, m_boxSize*i/2.0);
|
|
glVertex3f(-halfBox, halfBox, m_boxSize*i/2.0);
|
|
glVertex3f(halfBox, halfBox, m_boxSize*i/2.0);
|
|
glVertex3f(halfBox, -halfBox, m_boxSize*i/2.0);
|
|
glEnd();
|
|
glBegin(GL_LINES);
|
|
glVertex3f(m_boxSize*i/2.0, -halfBox, -halfBox);
|
|
glVertex3f(m_boxSize*i/2.0, -halfBox, halfBox);
|
|
glVertex3f(m_boxSize*i/2.0, halfBox, -halfBox);
|
|
glVertex3f(m_boxSize*i/2.0, halfBox, halfBox);
|
|
glEnd();
|
|
}
|
|
glPopAttrib();
|
|
}
|
|
SDL_GL_SwapBuffers();
|
|
|
|
m_lastElapsed = m_elapsed;
|
|
}
|
|
|
|
/* used by ODE to perform collision detection */
|
|
void TumblingLogos_collide_callback(void * data, dGeomID o1, dGeomID o2)
|
|
{
|
|
TumblingLogos * tl = (TumblingLogos *) data;
|
|
static dContact contact[4];
|
|
dBodyID b1 = dGeomGetBody(o1);
|
|
dBodyID b2 = dGeomGetBody(o2);
|
|
if (b1 && b2 && dAreConnected(b1, b2))
|
|
return;
|
|
int num = dCollide(o1, o2, 4, &contact[0].geom, sizeof(dContact));
|
|
int i;
|
|
for (i = 0; i < num; i++)
|
|
{
|
|
contact[i].surface.mode =
|
|
dContactSlip1 | dContactSlip2 | dContactBounce |
|
|
dContactSoftERP | dContactSoftCFM | dContactApprox1;
|
|
contact[i].surface.mu = 0.5;
|
|
contact[i].surface.slip1 = 0.0;
|
|
contact[i].surface.slip2 = 0.0;
|
|
contact[i].surface.soft_erp = 0.8;
|
|
contact[i].surface.soft_cfm = 0.01;
|
|
contact[i].surface.bounce = 0.4;
|
|
dJointID joint = dJointCreateContact(tl->m_world,
|
|
tl->m_contactJointGroup, contact + i);
|
|
dJointAttach(joint, b1, b2);
|
|
}
|
|
}
|
|
|
|
/* invokes ODE to do physics on our world */
|
|
void TumblingLogos::worldStep()
|
|
{
|
|
dSpaceCollide(m_space, this, TumblingLogos_collide_callback);
|
|
dWorldQuickStep(m_world, WORLD_STEP);
|
|
dJointGroupEmpty(m_contactJointGroup);
|
|
}
|
|
|
|
/* randomly chooses a gravity direction */
|
|
void TumblingLogos::changeGravity()
|
|
{
|
|
double g[3] = {0.0, 0.0, 0.0};
|
|
double r = randDbl();
|
|
if (r < 0.7)
|
|
{
|
|
/* gravitate towards one of the six sides */
|
|
g[(int) (randDbl() * 3)] = ((randDbl() < 0.5) ? -1 : 1) * 9.81;
|
|
}
|
|
else
|
|
{
|
|
/* gravitate towards one of the eight corners */
|
|
for (int i = 0; i < 3; i++)
|
|
{
|
|
g[i] = ((randDbl() < 0.5) ? -1 : 1) * 9.81;
|
|
}
|
|
}
|
|
dWorldSetGravity(m_world, g[0], g[1], g[2]);
|
|
}
|