/* 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 /* fmax() */ #include /* rand() */ #include #include #include "TumblingLogos.h" #include #include 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]); }