/* World.cpp * * Definition of the World class. */ #include "World.h" #include "Path.h" #include #include using namespace std; #define PI 3.14159 const double World::MAX_DT = 0.01; const double World::DEF_GRAVITY = -60; const double World::METER_INC = 1.0; const double World::METER_MULT = 100.0; World::World(const Tuple& gravity, bool light) : gravity_(gravity), ball_(), club_(), meter_(), meterActive_(false), meterIncreasing_(true), gameOver_(false), light_(light), ballDebug_(false), strokes_(0), obstructions_(), camera() { // nothing else to do } World::~World() { destroyWorld(); } void World::destroyWorld() { clearObstructions(); ball_.clearAll(); ball_.accelTo(Tuple()); strokes_ = 0; Triangle::setShrinkFactor(); Triangle::unsetCrazyTris(); gameOver_ = false; } /* clearObstructions * * Iterates through the obstruction vector and deletes each element before * clearing the vector. */ void World::clearObstructions() { for (vector::const_iterator i = obstructions_.begin(); i != obstructions_.end(); ++i) { delete *i; } obstructions_.clear(); } void World::init() { camera.init(); camera.track(pBallPosition()); // default: Camera follow Ball setupLight(); club_.initializeGraphics(); } /* draw * * Draw the ball and all the triangles. */ void World::draw() { camera.display(); // must be called first! ball_.draw(light_); // Only draw club if ball is stopped // and we are focused on the ball. if (ball_.stopped() && camera.tracking()) { glPushMatrix(); Tuple dP = ball_.position(); glTranslatef(dP.x(), dP.y(), dP.z()); glRotatef(-180.0 * camera.phi() / PI, 0.0, 1.0, 0.0); dP = Tuple(-2.5 * ball_.radius(), -1 * ball_.radius(), 2 * ball_.radius()); glTranslatef(dP.x(), dP.y(), dP.z()); club_.draw(); glPopMatrix(); } for (vector::const_iterator i = obstructions_.begin(); i != obstructions_.end(); ++i) { (*i)->draw(light_); } meter_.draw(light_); } /* timeStep * * Do the whole movement and collision shebang. */ void World::timeStep(double dt) { if (meterActive_) { if (meterIncreasing_) { meter_.incValue(dt * METER_INC); if (meter_.value() > 1.0) { meter_.setValue(1.0); meterIncreasing_ = false; } } else { meter_.decValue(dt * METER_INC); if (meter_.value() < 0.0) { meter_.setValue(0.0); meterIncreasing_ = true; } } } // Prevent crazy performance on slow computers if (dt > MAX_DT) dt = MAX_DT; // Check whether the ball is stopped if (!ball_.checkStopped()) { // Check the ball's rolling status ball_.checkRolling(); // Add half the effect of gravity and rolling ball_.impulse(dt / 2, gravity_); ball_.doFriction(dt / 2); // Loop until dt has elapsed while (dt > 0.0) { // Calculate the ball's expected path Path path = ball_.calcPath(dt); // Test the ball's path on all obstructions, which return beta values // indicating where/whether there are any collisions bool hole = false; double minBeta = 1.0; vector collisions; for (vector::const_iterator i = obstructions_.begin(); i != obstructions_.end(); ++i) { double beta = (*i)->pathIntersect(path, ball_.radius()); if (beta < minBeta) { // We've spotted a new closest collision, so clear the others minBeta = beta; collisions.clear(); hole = (*i)->isHole(); collisions.push_back(*i); } else if (beta == minBeta) { // We've spotted a rare simultaneous collision; add it hole = hole || (*i)->isHole(); collisions.push_back(*i); } } // Move the ball to the nearest collision (or end of path) ball_.moveTo(path.begPos() + minBeta * (path.endPos() - path.begPos())); // Check if we've gotten to the hole if (!hole) { // Respond to the collisions, if there are any ball_.doCollision(collisions); // Update time dt *= 1.0 - minBeta; } else { // Print the score cout << "Kudos! You finished the course in " << strokes() << endl; cout << "GAME OVER! ... now get a life." << endl; // Prep the grand finale Triangle::setCrazyTris(); // End the game gameOver_ = true; dt = 0.0; } // Report Ball's position and velocity at each time step if (ballDebug_) cout << "P:" << ball_.position() << " V:" << ball_.velocity() << endl; } // Add the rest of the effect of gravity and rolling ball_.impulse(dt / 2, gravity_); ball_.doFriction(dt / 2); } // Make sure we haven't fallen off of the course if (ball_.checkBottomedOut()) ++strokes_; } void World::swing() { if (ball_.stopped() && camera.tracking()) { if (meterActive_) { // Stop the ball from rolling and erase it's history // (so it doesn't just stop immediately) ball_.accelTo(Tuple()); ball_.clearAll(); // Find the initial direction of the ball's trajectory Tuple camDir = camera.direction(); Tuple strokeDir = (Tuple(camDir.x(), 0.0, camDir.z()).norm() + Tuple(0.0, club_.slope(), 0.0)).norm(); // Hit the ball ball_.accelBy(strokeDir * meter_.value() * METER_MULT); meterActive_ = false; meter_.setValue(0.0); // Player hit the ball, increment strokes ++strokes_; cout << "Strokes so far: " << strokes << endl; } else meterActive_ = true; } } void World::ballDebugToggle() { ballDebug_ = !ballDebug_; cout << "Ball Reporting: " << (ballDebug_ ? "On" : "Off") << endl; } void World::rollDebugToggle() { ball_.rollDebugToggle(); cout << "Collision Reporting: " << (ball_.rollDebug() ? "On" : "Off") << endl; } void World::lightOn() { light_ = true; glEnable(GL_LIGHTING); } void World::lightOff() { light_ = false; glDisable(GL_LIGHTING); } void World::setupLight() { // Function should only be run once. static int sentinel = 0; if (sentinel++ > 0) { cerr << "***ERROR: Ran setupLight() twice!\n"; return; } // Define light RGBA intensity parameters. GLfloat glfLightAmbient[] = {0.5f,0.5f,0.5f,1.0f}; GLfloat glfLightDiffuse[] = {1.2f,1.2f,1.2f,1.0f}; GLfloat glfLightSpecular[] = {0.9f,0.9f,0.9f,1.0f}; // Define a directional light source (infinite distance away from the objects in the scene). GLfloat glfLightPosition[] = {0.0f,0.0f,2.0f,0.0f}; // Set light source parameters. glLightfv(GL_LIGHT0,GL_AMBIENT,glfLightAmbient); //glLightfv(GL_LIGHT0,GL_DIFFUSE,glfLightDiffuse); //glLightfv(GL_LIGHT0,GL_SPECULAR,glfLightSpecular); glLightfv(GL_LIGHT0,GL_POSITION,glfLightPosition); // Enable the defined light source. glEnable(GL_LIGHT0); // Enable back face culling of polygons based upon their window coordinates. //glEnable(GL_CULL_FACE); // turns off front face draw // Enable depth comparisons. Enabled in initOpenGL(). //glEnable(GL_DEPTH_TEST); // Select smooth shading. glShadeModel(GL_SMOOTH); // Turn on/off lights as required light_ ? lightOn() : lightOff(); } /* addTriangle * * Adds a triangle. */ Triangle* World::addTriangle(Vertex* v1, Vertex* v2, Vertex* v3) { assert(v1 != NULL && v2 != NULL && v3 != NULL); Triangle* temp = new Triangle(v1, v2, v3); obstructions_.push_back(temp); return temp; } /* addEdge * * Adds an edge. */ Edge* World::addEdge(Vertex* v1, Vertex* v2) { assert(v1 != NULL && v2 != NULL); Edge* temp = new Edge(v1, v2); obstructions_.push_back(temp); return temp; } /* addVertex * * Adds a vertex. */ Vertex* World::addVertex(Tuple v1, Tuple color) { Vertex* temp = new Vertex(v1, color); obstructions_.push_back(temp); return temp; }