303 lines
9.1 KiB
C++
303 lines
9.1 KiB
C++
|
|
/* Author: Josh Holtrop
|
|
* DornerWorks screensaver
|
|
* A slightly more interesting screensaver mode involving
|
|
* towers of DW logos with randomly disappearing ones
|
|
*/
|
|
|
|
#include <math.h> /* fmax(), sin() */
|
|
#include <stdlib.h> /* rand() */
|
|
#include <GL/gl.h>
|
|
#include <GL/glu.h>
|
|
#include "Towers.h"
|
|
#include <ode/ode.h>
|
|
|
|
#include <iostream>
|
|
using namespace std;
|
|
|
|
#define WORLD_STEP 0.001
|
|
/* time at beginning of mode to not remove any logos (msec) */
|
|
#define WAIT_TIME 6000
|
|
/* amount of time to wait before removing another logo (msec) */
|
|
#define REMOVE_TIME 3000
|
|
#define GROUND_ALPHA 0.5
|
|
|
|
Towers::Towers(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 ground texture */
|
|
float tex[] = {1.0, 0.0, 0.0, GROUND_ALPHA,
|
|
1.0, 1.0, 1.0, GROUND_ALPHA,
|
|
1.0, 1.0, 1.0, GROUND_ALPHA,
|
|
1.0, 0.0, 0.0, GROUND_ALPHA};
|
|
glGenTextures(1, &m_groundTexture);
|
|
glBindTexture(GL_TEXTURE_2D, m_groundTexture);
|
|
glTexImage2D(GL_TEXTURE_2D, 0, 4, 2, 2, 0, GL_RGBA, GL_FLOAT, tex);
|
|
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
|
|
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
|
|
glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
|
|
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
|
|
|
|
/* create the ODE world */
|
|
createWorld();
|
|
|
|
m_lastElapsed = 0;
|
|
m_logoToRemove = -1;
|
|
m_logosList = glGenLists(1);
|
|
}
|
|
|
|
Towers::~Towers()
|
|
{
|
|
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);
|
|
glDeleteTextures(1, &m_groundTexture);
|
|
glDeleteLists(m_logosList, 1);
|
|
}
|
|
|
|
void Towers::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);
|
|
// dVector3 center = {0, 10, 0};
|
|
// dVector3 extents = {100, 40, 100};
|
|
// m_space = dQuadTreeSpaceCreate(0, center, extents, 5);
|
|
m_contactJointGroup = dJointGroupCreate(0);
|
|
|
|
double lHeight = 0.0;
|
|
const double towerBaseOffset = 0.0;
|
|
const int towerHeight = 6;
|
|
const double towerSpacing = 10.0;
|
|
const double logoTierSpacing = 3.75;
|
|
bool turn = false;
|
|
for (double xoffset = -towerSpacing;
|
|
xoffset < towerSpacing*1.1;
|
|
xoffset += towerSpacing*2.0)
|
|
{
|
|
for (double zoffset = -towerSpacing;
|
|
zoffset < towerSpacing*1.1;
|
|
zoffset += towerSpacing*2.0)
|
|
{
|
|
double towerOffset = 0.0;
|
|
for (int i = 0;
|
|
i < towerHeight;
|
|
i++, towerOffset += lHeight, turn = !turn)
|
|
{
|
|
double localXOffset = 0.0;
|
|
double localZOffset = 0.0;
|
|
if (turn)
|
|
localXOffset = -logoTierSpacing;
|
|
else
|
|
localZOffset = -logoTierSpacing;
|
|
for (int i = 0; i < 2; i++)
|
|
{
|
|
LogoBox * lb = new LogoBox(m_world, m_space);
|
|
if (lHeight == 0.0)
|
|
lHeight = lb->getDepth();
|
|
dMatrix3 rotation;
|
|
dRFromEulerAngles(rotation,
|
|
0,
|
|
turn ? M_PI_2 : 0,
|
|
0);
|
|
dBodySetRotation(lb->getBody(), rotation);
|
|
dBodySetPosition(lb->getBody(),
|
|
xoffset + localXOffset,
|
|
towerBaseOffset + towerOffset + lb->getDepth()/2.0,
|
|
zoffset + localZOffset);
|
|
m_logos.push_back(lb);
|
|
|
|
if (turn)
|
|
localXOffset += logoTierSpacing * 2;
|
|
else
|
|
localZOffset += logoTierSpacing * 2;
|
|
}
|
|
}
|
|
turn = !turn;
|
|
}
|
|
}
|
|
|
|
/* create ground plane */
|
|
dCreatePlane(m_space, 0, 1, 0, 0);
|
|
}
|
|
|
|
void Towers::update()
|
|
{
|
|
SSMode::update();
|
|
|
|
if (m_lastElapsed == 0)
|
|
m_lastElapsed = m_elapsed;
|
|
|
|
if (m_logoToRemove == -1 && m_elapsed > WAIT_TIME)
|
|
{
|
|
if (m_logos.size() > 0)
|
|
{
|
|
m_lastRemoveTime = m_elapsed;
|
|
m_logoToRemove = rand() % m_logos.size();
|
|
}
|
|
}
|
|
else if (m_elapsed - m_lastRemoveTime > REMOVE_TIME)
|
|
{
|
|
if (m_logoToRemove >= 0)
|
|
{
|
|
std::vector<LogoBox *>::iterator it = m_logos.begin();
|
|
for (int i = 0; i < m_logoToRemove; i++)
|
|
it++;
|
|
delete *it;
|
|
m_logos.erase(it);
|
|
m_logoToRemove = (m_logos.size() > 0) ? rand() % m_logos.size() : -1;
|
|
m_lastRemoveTime = 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();
|
|
bool drewLogos = false;
|
|
for (int i = 0; i < numMonitors; i++)
|
|
{
|
|
glViewport(i*width/numMonitors, 0, width/numMonitors, height);
|
|
glLoadIdentity();
|
|
gluLookAt(0, 30, 55, /* eye position */
|
|
0, 12, 0, /* focal point */
|
|
0, 1, 0); /* up vector */
|
|
glRotatef(m_elapsed/60.0f, 0, 1, 0); /* rotate view over time */
|
|
|
|
/* draw logo reflection */
|
|
glPushAttrib(GL_POLYGON_BIT);
|
|
glFrontFace(GL_CW); /* polygon vertex order reversed when
|
|
reflecting across a plane */
|
|
glPushMatrix();
|
|
glScalef(1.0, -1.0, 1.0);
|
|
if (drewLogos)
|
|
glCallList(m_logosList);
|
|
else
|
|
{
|
|
glNewList(m_logosList, GL_COMPILE_AND_EXECUTE);
|
|
drawLogos();
|
|
glEndList();
|
|
drewLogos = true;
|
|
}
|
|
glPopMatrix();
|
|
glPopAttrib();
|
|
|
|
drawGround();
|
|
|
|
/* draw logos normally */
|
|
// drawLogos();
|
|
glCallList(m_logosList);
|
|
}
|
|
SDL_GL_SwapBuffers();
|
|
|
|
m_lastElapsed = m_elapsed;
|
|
}
|
|
|
|
void Towers::drawLogos()
|
|
{
|
|
/* 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));
|
|
if (i == m_logoToRemove)
|
|
{
|
|
float amt = fabsf(sin(1.9*M_PI*
|
|
(m_elapsed-m_lastRemoveTime)/REMOVE_TIME));
|
|
GLfloat emission[] = {0.0, 0.0, amt, 1.0};
|
|
glPushAttrib(GL_LIGHTING_BIT);
|
|
glMaterialfv(GL_FRONT, GL_EMISSION, emission);
|
|
}
|
|
m_logos[i]->draw();
|
|
if (i == m_logoToRemove)
|
|
{
|
|
glPopAttrib();
|
|
}
|
|
glPopMatrix();
|
|
}
|
|
}
|
|
|
|
void Towers::drawGround()
|
|
{
|
|
const float gs = 120; /* ground size */
|
|
const float tt = 20; /* texture tiles */
|
|
glPushAttrib(GL_ENABLE_BIT);
|
|
glEnable(GL_TEXTURE_2D);
|
|
glEnable(GL_BLEND);
|
|
glBegin(GL_QUADS);
|
|
glNormal3f(0, 1, 0);
|
|
glTexCoord2f(0, 0);
|
|
glVertex3f(-gs, 0, gs);
|
|
glTexCoord2f(tt, 0);
|
|
glVertex3f(gs, 0, gs);
|
|
glTexCoord2f(tt, tt);
|
|
glVertex3f(gs, 0, -gs);
|
|
glTexCoord2f(0, tt);
|
|
glVertex3f(-gs, 0, -gs);
|
|
glEnd();
|
|
glPopAttrib();
|
|
}
|
|
|
|
/* used by ODE to perform collision detection */
|
|
void Towers_collide_callback(void * data, dGeomID o1, dGeomID o2)
|
|
{
|
|
const int maxNumContacts = 2;
|
|
Towers * t = (Towers *) data;
|
|
static dContact contact[maxNumContacts];
|
|
dBodyID b1 = dGeomGetBody(o1);
|
|
dBodyID b2 = dGeomGetBody(o2);
|
|
if (b1 && b2 && dAreConnected(b1, b2))
|
|
return;
|
|
int num = dCollide(o1, o2, maxNumContacts,
|
|
&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.0;
|
|
dJointID joint = dJointCreateContact(t->m_world,
|
|
t->m_contactJointGroup, contact + i);
|
|
dJointAttach(joint, b1, b2);
|
|
}
|
|
}
|
|
|
|
/* invokes ODE to do physics on our world */
|
|
void Towers::worldStep()
|
|
{
|
|
dSpaceCollide(m_space, this, Towers_collide_callback);
|
|
dWorldQuickStep(m_world, WORLD_STEP);
|
|
dJointGroupEmpty(m_contactJointGroup);
|
|
}
|
|
|