dwscr/ss/TumblingLogos.cc
josh 126b75fb56 first wall of LightBox done
git-svn-id: svn://anubis/dwscr/trunk@92 5bef9df8-b654-44bb-925b-0ff18baa8f8c
2008-11-13 04:14:46 +00:00

245 lines
7.6 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]);
}