/* Author: Josh Holtrop * DornerWorks screensaver * A slightly more interesting screensaver mode involving * towers of DW logos with randomly disappearing ones */ #include /* fmax(), sin() */ #include /* rand() */ #include #include #include "Towers.h" #include #include 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::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) ? (int) (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); }