//////////////////////////////////////////////////////////// // car.cpp // // Car related classes for EDrive, a multiplatform driving simulator. // Those classes are: // wheel - one wheel of the car // car - the whole car (currently just 4 wheels and a sound) // // Copyright 2004 by Evan Alexander Weaver // // Despite the copyright, this is free software. See edrive.cpp for details. // // Date last modified: 31 May 2004 #include #include "platform.h" #include "error.h" #include "edmath.h" #include "graphics.h" #include "sound.h" #include "terrain.h" #include "car.h" ////////////////////////////////////////////////////////////////////// // wheel class // // One wheel of a car // Initially, the wheel is nothing // wheel::wheel():thing() { angle = lean = orientation = x = y = z = 0; moved = false; } // Add a pie slice (angle "a", with Z coordinate "z") to make part of // the wheel. // void wheel::addpie(float a, float z) { add(0.1*cos(a), 0.1*sin(a), z, 0.1 * cos(a), 0.1 * sin(a), z, 0.5+0.5*cos(a), 0.5+0.5*sin(a)); } // Create a wheel using a texture from image file "bmp" // There are 3 pieces: the front side, the back side and the tread. // bool wheel::create(graphics &g, const char *bmp) { bool rc = thing::create(g, mkcolor(0.2, 0.2, 0.2), TRIANGLEFAN, bmp, 50, 26 + 26 + 50); if (rc) { int i; // 0 - 24 is front side add(0, 0, Z(-0.022), 0, 0, Z(-1), 0.5, 0.5); for (i = 0; i < 24; i++) addpie(i*PI/12.0, Z(-0.025)); // 25 - 49 is back side add(0, 0, Z(0.022), 0, 0, Z(1), 0.5, 0.5); for (i = 0; i < 24; i++) addpie(i*PI/12.0, Z(0.025)); // front side addind(0); addind(1); for (i = 24; i > 0; i--) addind(i, true); // back side newpiece(TRIANGLEFAN); addind(25); addind(26); for (i = 0; i < 23; i++) addind(i+27, true); addind(26, true); // tread newpiece(TRIANGLESTRIP); addind(26); addind(1); for (i = 0; i < 23; i++) { addind(i + 27, true); addind(i + 2, true); } addind(26, true); addind(1, true); } return rc; } // rotate the wheel (e.g. as it rolls along) // void wheel::spin(float rad) { angle += rad; moved = true; } // move the wheel to a new location // void wheel::moveto(float x1, float y1, float z1) { x = x1; y = y1; z = z1; moved = true; } void wheel::moveto(const vector &v) { x = v.x; y = v.y; z = v.z; moved = true; } // turn the wheel (e.g. as it is steered) // void wheel::turnto(float rad) { orientation = rad; moved = true; } // tilt the wheel around axis "v" (e.g. as terrain tilts) // void wheel::tiltto(float rad, const vector &v) { lean = rad; leandir = v; moved = true; } // draw the wheel // void wheel::draw(graphics &g) { if (moved) { // We need to compute a new world matrix if it has been moved // since the last time we computed it. // #if ED_DIRECTX_9_GRAPHICS //**************************************** reposition(matrotationz(angle) * matrotationy(orientation) * matrotaxis(lean, leandir) * mattranslate(x, y, z)); #else //************************************************************ reposition(mattranslate(x, y, z) * matrotaxis(lean, leandir) * matrotationy(orientation) * matrotationz(angle)); #endif //*********************************************************** moved = false; } thing::draw(g); } //////////////////////////////////////////////////////////////////////// // car class // // Currently this is 4 wheels, and engine noise and some positional // information. // Initially empty // car::car() { direction = vector(0, 0, 1); up = vector(0, 1, 0); location = vector(0, 0, 0); pov = 0; } car::~car() { destroy(); } // Unload graphics // void car::destroygraphics() { leftf.destroy(); rightf.destroy(); leftr.destroy(); rightr.destroy(); } // Unload all resources consumed // void car::destroy() { destroygraphics(); engine.destroy(); } // Create the car to be drawn on "g", using texture "wheelbmp" and engine // sound "enginewav", pointing in direction "initdir" at point "initloc", // with point of view "initpov". // bool car::create(graphics &g, const char *wheelbmp, const char *enginewav, const vector &initdir, const vector &initloc, int initpov) { bool rc; // load graphics parts rc = loadgraphics(g, wheelbmp); // load sound parts and start them playing #if ED_DIRECTX_9_AUDIO //*********************************************** engine.create(g.gethwnd(), enginewav, true); #else //**************************************************************** engine.create(0, 0, enginewav, true); #endif //*************************************************************** freq = engine.frequency()/2; engine.frequency(freq); engine.play(); speed = 1; oldspeed = -1; direction = initdir; up = vector(0, 1, 0); location = initloc; pov = initpov; return rc; } // Load the graphic parts of the car, which are currently the wheels // bool car::loadgraphics(graphics &g, const char *wheelbmp) { bool rc; rc = leftf.create(g, wheelbmp) && rightf.create(g, wheelbmp) && leftr.create(g, wheelbmp) && rightr.create(g, wheelbmp); return rc; } // Move the car based on elapsed time (since last move), user input, // and the terrain. // void car::move(unsigned int delta, float accel, float brake, float turn, terrain &t, graphics &g) { bool reposition = false; if (delta == 0) reposition = true; else { // accelerate if (accel > 0.1) { speed += (accel * 0.0005 * delta); if (speed > 2) speed = 2; } else if (speed > 0.3) { speed -= 0.00015 * delta; if (speed < 0.3) speed = 0.3; } // slow down if (brake > 0.1) { speed -= (brake * 0.0006 * delta); if (speed < 0) speed = 0; } // recompute engine frequency if speed has changed // if (speed != oldspeed) { engine.frequency((unsigned int)(freq * (speed > 0.3 ? speed : 0.3))); oldspeed = speed; } // if moving or turning, recompute loactions of all wheels // if (turn != 0 || speed != 0) { reposition = true; float angle; float slip; vector leftw, rightw, backw; // fake slip! slip = (turn > 0 ? turn : -turn) + speed - 1; if (slip < 0) slip = 1; else slip = 1 + (brake - accel - 0.5) * slip / 3; // turn the car's direction // rotateaxis(direction, 0.002 * delta * turn * slip * speed, up); // try to move forward // normalize(rightdir = right(up, direction)); location = location + (speed * 0.005 * delta) * direction; // now raise/lower wheels as appropriate // leftw = location - 0.2 * rightdir; leftw.y = t.height(leftw.x, leftw.z); rightw = location + 0.2 * rightdir; rightw.y = t.height(rightw.x, rightw.z); location = 0.5 * (rightw + leftw); backw = location - direction; backw.y = t.height(backw.x, backw.z); normalize(rightdir = rightw - leftw); normalize(up = right(location - backw, rightdir)); normalize(direction = right(rightdir, up)); leftw = leftw + 0.1 * up; rightw = rightw + 0.1 * up; // spin the wheels as they move leftf.spin(angle = delta * speed * -0.022); leftr.spin(angle); rightf.spin(-angle); rightr.spin(-angle); // turn the wheels vector temp; normalize(temp = vector(direction.x, 0, direction.z)); float dp = temp.x; // temp.x is dot(temp, vector(1, 0, 0)); if (dp > 1.0) angle = 0; else if (dp < -1.0) angle = PI; else angle = acos(dp); if (Z(direction.z) > 0) angle = -angle; leftr.turnto(angle); rightr.turnto(PI + angle); // turn the fronts even more angle += Z(turn * 0.3); leftf.turnto(angle); rightf.turnto(PI + angle); // tilt the wheels normalize(temp = right(direction, vector(direction.z, 0, -direction.x))); dp = dot(up, temp); if (dp > 1.0) angle = 0; else if (dp < -1.0) angle = PI; else angle = acos(dp); if (Z(leftw.y) > Z(rightw.y)) angle = -angle; leftf.tiltto(angle, direction); rightf.tiltto(angle, direction); leftr.tiltto(angle, direction); rightr.tiltto(angle, direction); // finally move the wheels to the new spot // leftf.moveto(Z(leftw)); leftr.moveto(Z(leftw - 0.7*direction)); rightf.moveto(Z(rightw)); rightr.moveto(Z(rightw - 0.7*direction)); } } // recompute viewpoint if necessary if (reposition) { if (pov == 1) g.viewpoint(location + -0.5*direction + 0.25*up, direction, up); else if (pov == 2) g.viewpoint(location + -1.5*direction + 0.4*up, direction, up); else if (pov == 3) g.viewpoint(location + -0.5*direction + 0.7*up + 2*rightdir, -rightdir, up); else if (pov == 4) { vector temp(direction.x, 0, direction.z); normalize(temp); g.viewpoint(location + vector(0, 10, 0), vector(0, -1, 0), temp); } } } // Draw the car (but only the visible two front wheels in point of view 1 // void car::draw(graphics &g) { leftf.draw(g); rightf.draw(g); if (pov != 1) { leftr.draw(g); rightr.draw(g); } } // Change the camera (0: none, 1: behind close, 2: behind far, 3: from the // right, 4: from overhead), where -1 means switch to next camera. // void car::camera(int newcam) { if (newcam == -1) // switch to next cam pov = (pov==1 ? 2 : pov==2 ? 3 : pov==3 ? 4 : pov==4 ? 1 : pov); else pov = newcam; } // Stop making noise // void car::hush() { engine.stop(); } // Resume making noise // void car::saywhat() { engine.play(); } // Return interesting data in a (temporary) string - use it right away, // e.g. to display to the user. // const char *car::info() { sprintf(infostr, "dir(%.1f,%.1f,%.1f) " "pos(%.1f,%.1f,%.1f) spd(%.1f)", direction.x, direction.y, direction.z, location.x, location.y, location.z, speed); return infostr; }