/CarDemo/src/Car.cpp

https://bitbucket.org/juniormitac/session12018 · C++ · 569 lines · 370 code · 82 blank · 117 comment · 15 complexity · 76eddcfd4e23e79c2bff008455cae2af MD5 · raw file

  1. // Car Demo Program
  2. // Author: Len Hamey
  3. // Date: March 2015
  4. // Based on: Lecture notes
  5. // A car is displayed in three dimensions as a wire frame.
  6. // The wheels of the car are cylinders.
  7. // Each wheel has bolts drawn on it, except for the spare wheel.
  8. // The body of the car is a wire frame intended to resemble the model used in lectures.
  9. // a: Animate.
  10. // s: Slow animation speed
  11. // f: Fast animation speed
  12. // r: Reset animation position
  13. // +: Zoom in
  14. // -: Zoom out
  15. # include <ctype.h>
  16. # include <GL/glut.h>
  17. # include <iostream>
  18. # define PI 3.1415926535897932
  19. # define ANIMATION_STEP (1000/60)
  20. // Motion in world units per second
  21. # define SLOW_MOTION 0.5
  22. # define FAST_MOTION 2.5
  23. # define ASPECT_RATIO 2.0
  24. struct Point {
  25. double x, y, z;
  26. // Empty constructor is needed to build Point into other objects.
  27. Point() { x = y = z = 0.0; }
  28. // Constructor with parameters.
  29. Point(double _x, double _y, double _z) { x = _x; y = _y; z = _z; }
  30. };
  31. Point add(Point p1, Point p2)
  32. {
  33. return Point(p1.x+p2.x, p1.y+p2.y, p1.z+p2.z);
  34. }
  35. void vertex(Point p)
  36. {
  37. glVertex3d(p.x, p.y, p.z);
  38. }
  39. void translate(Point p)
  40. {
  41. glTranslated(p.x, p.y, p.z);
  42. }
  43. struct Car
  44. {
  45. // The points of the left and right panels.
  46. Point left[7];
  47. Point right[7];
  48. // Points where the axles attach to the body
  49. Point frontLeft, frontRight, rearLeft, rearRight;
  50. // Location of the spare wheel mounting point
  51. Point spareWheel;
  52. // Parameters for wheels
  53. double wheelRadius, wheelThickness;
  54. // Parameters for bolts on wheels
  55. double boltPosRadius, boltRadius, boltLength;
  56. // Floor height of the car body
  57. double floorHeight;
  58. };
  59. static Car car;
  60. static bool animating = false;
  61. static double xpos = 0.0;
  62. static double xposStep = FAST_MOTION;
  63. static double zoomFactor = 1.0;
  64. static double rotation_factor = 1.0; // For correct rotation, use 1.0
  65. // Initialise the car model data
  66. void initCar(void)
  67. {
  68. // First draw the wire frame of the car body.
  69. // The origin of the drawing is the centre of back of the car.
  70. // Various measurements are recorded in mm and then scaled
  71. // so that the length of the car is 1 unit. (i.e. it extends from 0 to 1 in X)
  72. // The longitudinal axis is aligned with X.
  73. double length = 288.0;
  74. double rearAxle = 60.0 / length; // Distance from rear bumper to rear axle.
  75. double frontAxle = 68.0 / length; // Distance from front bumper to front axle.
  76. double axleY = 20.0 / length; // Height of axle above the vehicle floor.
  77. double rearSlopeX = 13.0 / length; // X distance from rear bumper to top of sloped rear panel.
  78. double rearSlopeY = 55.0 / length; // Y distance from vehicle floor to bottom of sloped rear panel.
  79. double hoodY = 82.0 / length; // Y distance from vehicle floor to hood (bonnet, lid over the engine).
  80. double hoodX = 62.0 / length; // X length of the hood
  81. double frontSlopeX = 8.0 / length; // X distance from bottom of windscreen to top.
  82. double bodyHeight = 135.0 / length; // Y height of the body
  83. double spareY = 57.0 / length; // Height of the spare wheel mounting point
  84. double wheelDiameter = 100.0 / length; // Diameter of wheel
  85. double wheelThickness = 15.0 / length; // Thickness of the wheel itself
  86. double wheelClearance = 3.0 / length; // Z gap between wheel and body
  87. double boltDiameter = 4.0 / length; // Diameter of the bolt
  88. double boltLength = 3.0 / length; // Length of the bolt
  89. double boltPosRadius = 17.0 / length; // Distance of centre of bolt from centre of wheel
  90. double bodyWidth = 90.0 / length; // Z distance from left panel to right panel of the body
  91. double halfWidth = bodyWidth / 2.0;
  92. // Compute the profile points that define the shape of the body.
  93. // The points are numbered as follows:
  94. // 2---------------------------3
  95. // / \ .
  96. // / \ .
  97. // 1 4---------5
  98. // | |
  99. // | |
  100. // 0-----------------------------------------6
  101. // There is a left and a right point for each one.
  102. car.left[0] = Point(0.0, 0.0, -halfWidth);
  103. car.left[1] = add(car.left[0], Point(0.0, rearSlopeY, 0.0));
  104. car.left[2] = add(car.left[0], Point(rearSlopeX, bodyHeight, 0.0));
  105. car.left[6] = Point(1.0, 0.0, -halfWidth);
  106. car.left[5] = add(car.left[6], Point(0.0, hoodY, 0.0));
  107. car.left[4] = add(car.left[5], Point(-hoodX, 0.0, 0.0));
  108. car.left[3] = add(car.left[4], Point(-frontSlopeX, bodyHeight - hoodY, 0.0));
  109. // The right points are Z translations of the left points. Each one is explicitly computed here.
  110. Point w(0.0, 0.0, bodyWidth);
  111. for (int i = 0; i < 7; i++)
  112. car.right[i] = add(car.left[i], w);
  113. // The axle attachment points with wheel clearance from the body added
  114. car.frontLeft = add(car.left[6], Point(-frontAxle, axleY, -wheelClearance));
  115. car.frontRight = add(car.right[6], Point(-frontAxle, axleY, wheelClearance));
  116. car.rearLeft = add(car.left[0], Point(rearAxle, axleY, -wheelClearance));
  117. car.rearRight = add(car.right[0], Point(rearAxle, axleY, wheelClearance));
  118. // Attachment of the spare wheel
  119. car.spareWheel = add(car.left[0], Point(0.0, spareY, halfWidth));
  120. // Wheel parameters. Radius, thickness.
  121. car.wheelRadius = wheelDiameter / 2.0;
  122. car.wheelThickness = wheelThickness;
  123. // Bolt parameters. Positional radius, length and radius of the bolt.
  124. car.boltPosRadius = boltPosRadius;
  125. car.boltLength = boltLength;
  126. car.boltRadius = boltDiameter / 2.0;
  127. // Floor height of the body
  128. car.floorHeight = car.wheelRadius - axleY;
  129. return;
  130. }
  131. // Draw a cylinder using gluCylinder.
  132. // The cylinder is aligned with the Z axis
  133. void drawCylinder(double radius, double length)
  134. {
  135. // Reference: HK page 248
  136. GLUquadricObj *qobj = gluNewQuadric();
  137. gluQuadricDrawStyle(qobj, GLU_LINE);
  138. gluCylinder(qobj, radius, radius, length, 16, 1);
  139. gluDeleteQuadric(qobj);
  140. return;
  141. }
  142. // Draw a bolt as a cylinder
  143. void drawBolt()
  144. {
  145. // *******************************************
  146. // Draw a bolt at the current origin aligned with the Z axis
  147. drawCylinder(car.boltRadius, car.boltLength);
  148. return;
  149. }
  150. // Draw a wheel as a cylinder with bolts also drawn as cylinders
  151. // The bolts are on the outside surface of the cylinder.
  152. void drawWheel(bool bolts)
  153. {
  154. // *******************************************
  155. // The wheel is a cylinder
  156. drawCylinder(car.wheelRadius, car.wheelThickness);
  157. if (! bolts)
  158. return;
  159. // *******************************************
  160. // Draw the bolts on the surface of the wheel
  161. glPushMatrix();
  162. // Translate to the outside surface of the cylinder
  163. glTranslated(0.0, 0.0, car.wheelThickness);
  164. // ***************************************
  165. // Each bolt is drawn at a location in wheel coordinates
  166. glPushMatrix();
  167. glTranslated(car.boltPosRadius, 0.0, 0.0);
  168. drawBolt();
  169. glPopMatrix();
  170. glPushMatrix();
  171. glTranslated(-car.boltPosRadius, 0.0, 0.0);
  172. drawBolt();
  173. glPopMatrix();
  174. glPushMatrix();
  175. glTranslated(0.0, car.boltPosRadius, 0.0);
  176. drawBolt();
  177. glPopMatrix();
  178. glPushMatrix();
  179. glTranslated(0.0, -car.boltPosRadius, 0.0);
  180. drawBolt();
  181. glPopMatrix();
  182. glPopMatrix();
  183. return;
  184. }
  185. // Draw the car body
  186. void drawBody(void)
  187. {
  188. // Draw the left panel.
  189. glBegin(GL_LINE_LOOP);
  190. for (int i = 0; i < 7; i++)
  191. vertex(car.left[i]);
  192. glEnd();
  193. // Draw the right panel.
  194. glBegin(GL_LINE_LOOP);
  195. for (int i = 0; i < 7; i++)
  196. vertex(car.right[i]);
  197. glEnd();
  198. // Draw the Z-lines between the panels.
  199. glBegin(GL_LINES);
  200. for (int i = 0; i < 7; i++) {
  201. vertex(car.left[i]);
  202. vertex(car.right[i]);
  203. }
  204. glEnd();
  205. }
  206. // Draw the car
  207. void drawCar(double scale, double distance)
  208. {
  209. double angle, circumference;
  210. // Compute rotation angle for wheels when the car has driven distance.
  211. // A positive distance travelled in X means a negative wheel rotation on the right side.
  212. // Note: The scale of the vehicle affects the circumference of the wheel.
  213. circumference = 2.0 * PI * car.wheelRadius * scale;
  214. // rotation_factor is 1.0 for correct rotation. For demonstrations of errors, use 0.0, 2.0, -1.0
  215. angle = -distance / circumference * 360.0 * rotation_factor;
  216. glPushMatrix();
  217. // Translate the car for the distance travelled
  218. glTranslated(distance, 0.0, 0.0);
  219. // Scale the car to the required scale factor.
  220. glScaled(scale, scale, scale);
  221. // Lift the car body off the ground by the required floor height
  222. // Note that the wheels are defined relative to the body position
  223. glTranslated(0.0, car.floorHeight, 0.0);
  224. // *****************************************
  225. // Draw the car body
  226. drawBody();
  227. // *****************************************
  228. // Draw each wheel, one at a time.
  229. // Save and restore the modelview matrix each time.
  230. // Wheels on the left side are rotated 180 degrees about Y axis.
  231. // Wheels are rotated about Z axis to simulate rolling on the road;
  232. // wheels on the left ride rotate in the opposite direction to those on the right side.
  233. glPushMatrix();
  234. // Draw the front left wheel
  235. // It is on the other side of the car, so rotate about Y 180 degrees
  236. translate(car.frontLeft);
  237. glRotated(180.0, 0,1,0);
  238. glRotated(-angle, 0,0,1);
  239. drawWheel(true);
  240. glPopMatrix();
  241. glPushMatrix();
  242. // Draw the front right wheel
  243. translate(car.frontRight);
  244. glRotated(angle, 0,0,1);
  245. drawWheel(true);
  246. glPopMatrix();
  247. glPushMatrix();
  248. // Draw the rear left wheel
  249. translate(car.rearLeft);
  250. glRotated(180.0, 0,1,0);
  251. glRotated(-angle, 0,0,1);
  252. drawWheel(true);
  253. glPopMatrix();
  254. glPushMatrix();
  255. // Draw the rear right wheel
  256. translate(car.rearRight);
  257. glRotated(angle, 0,0,1);
  258. drawWheel(true);
  259. glPopMatrix();
  260. glPushMatrix();
  261. // Draw the spare wheel.
  262. // It does not roll on the road.
  263. // It is rotated -90 about Y to orient it for the rear of the vehicle
  264. // It has no bolts on it!
  265. translate(car.spareWheel);
  266. glRotated(-90.0, 0,1,0);
  267. drawWheel(false);
  268. glPopMatrix();
  269. glPopMatrix();
  270. }
  271. // Draw car with its (line) shadow.
  272. void drawShadowCar(double scale, double distance, float r, float g, float b)
  273. {
  274. // Draw a shadow of the car - it is a line shadow.
  275. // It is drawn first so that the car itself overwrites the shadow.
  276. glPushMatrix();
  277. // Colour * 0.3 + 0.7 is a pale version of the colour
  278. glColor3f(r*0.3 + 0.7, g*0.3 + 0.7, b*0.3 + 0.7);
  279. glScaled(1.0, 0.0, 1.0);
  280. drawCar(scale, distance);
  281. glPopMatrix();
  282. // Draw the car itself
  283. glColor3f(r, g, b);
  284. drawCar(scale, distance);
  285. }
  286. // Draw a ground-plane grid extending from -5 to +5 in X and Z.
  287. void drawGrid(void)
  288. {
  289. glBegin(GL_LINES);
  290. for (int x = -5; x <= 5; x++) {
  291. glVertex3f(x, 0.0, -5.0);
  292. glVertex3f(x, 0.0, 5.0);
  293. }
  294. for (int z = -5; z <= 5; z++) {
  295. glVertex3f(-5.0, 0.0, z);
  296. glVertex3f(5.0, 0.0, z);
  297. }
  298. glEnd();
  299. }
  300. // Draw the X Y and Z positive axes as lines from the origin.
  301. void drawAxes(void)
  302. {
  303. glBegin(GL_LINES);
  304. glVertex3f(0.0, 0.0, 0.0);
  305. glVertex3f(5.0, 0.0, 0.0);
  306. glVertex3f(0.0, 0.0, 0.0);
  307. glVertex3f(0.0, 5.0, 0.0);
  308. glVertex3f(0.0, 0.0, 0.0);
  309. glVertex3f(0.0, 0.0, 5.0);
  310. glEnd();
  311. }
  312. void init(void)
  313. {
  314. initCar();
  315. glClearColor(1.0, 1.0, 1.0, 1.0);
  316. }
  317. void display(void)
  318. // clears the screen and draws the car
  319. {
  320. glMatrixMode (GL_PROJECTION);
  321. glLoadIdentity ();
  322. glOrtho(-5.0/zoomFactor, 5.0/zoomFactor, -5.0/ASPECT_RATIO/zoomFactor, 5.0/ASPECT_RATIO/zoomFactor, 0.0, 15.0);
  323. glClear (GL_COLOR_BUFFER_BIT);
  324. glMatrixMode (GL_MODELVIEW);
  325. glLoadIdentity ();
  326. // Set up a viewpoint for looking at the car.
  327. gluLookAt(3.0,2.0,7.0, 0.0,0.0,0.0, 0.0,1.0,0.0);
  328. // Draw axes and a ground plane grid
  329. // Axes are drawn after the grid so that they overwrite it
  330. glColor3f(0.7, 1.0, 0.7); // Pale green
  331. drawGrid();
  332. glColor3f(0.0, 0.0, 0.0); // Black.
  333. drawAxes();
  334. // *******************************************
  335. // Draw the lead car with specified scale and road position
  336. // First save the modelview matrix
  337. glPushMatrix();
  338. // Position the car
  339. glTranslated(0.0, 0.0, 1.0);
  340. drawShadowCar(2.0, xpos, 1.0, 0.0, 0.0);
  341. glPopMatrix();
  342. //save the original modelview matrix
  343. // Draw the following car
  344. glPushMatrix();
  345. // Position the car
  346. glTranslated(-2.0, 0.0, 1.0);
  347. drawShadowCar(1.2, xpos, 0.0, 0.0, 1.0);
  348. glPopMatrix();
  349. /* glutSwapBuffers must be called for any drawing to
  350. * appear on the screen when in double buffered mode
  351. */
  352. glutSwapBuffers();
  353. }
  354. # define ANIMATING 1
  355. # define ANIMATION_FAST 2
  356. # define ANIMATION_SLOW 3
  357. # define RESET 4
  358. # define ZOOM_IN 5
  359. # define ZOOM_OUT 6
  360. # define ROTATION_NEGATIVE 7
  361. # define ROTATION_ZERO 8
  362. # define ROTATION_NORMAL 9
  363. # define ROTATION_DOUBLE 10
  364. void menu (int selected)
  365. {
  366. switch (selected) {
  367. case ANIMATING:
  368. animating = ! animating;
  369. glutPostRedisplay();
  370. break;
  371. case ANIMATION_FAST:
  372. xposStep = FAST_MOTION;
  373. glutPostRedisplay();
  374. break;
  375. case ANIMATION_SLOW:
  376. xposStep = SLOW_MOTION;
  377. glutPostRedisplay();
  378. break;
  379. case RESET: // reset
  380. xpos = 0.0;
  381. animating = 0;
  382. glutPostRedisplay();
  383. break;
  384. case ZOOM_IN:
  385. zoomFactor = zoomFactor * 1.4142135;
  386. if (zoomFactor > 4.0) zoomFactor = 4.0;
  387. glutPostRedisplay();
  388. break;
  389. case ZOOM_OUT:
  390. zoomFactor = zoomFactor / 1.4142135;
  391. if (zoomFactor < 1.01) zoomFactor = 1.0;
  392. glutPostRedisplay();
  393. break;
  394. case ROTATION_NEGATIVE:
  395. case ROTATION_ZERO:
  396. case ROTATION_NORMAL:
  397. case ROTATION_DOUBLE:
  398. rotation_factor = selected - ROTATION_ZERO;
  399. glutPostRedisplay();
  400. break;
  401. }
  402. }
  403. void keyboard (unsigned char key, int x, int y)
  404. {
  405. switch (key) {
  406. case 'a':
  407. menu(ANIMATING);
  408. break;
  409. case 'f':
  410. menu(ANIMATION_FAST);
  411. break;
  412. case 's':
  413. menu(ANIMATION_SLOW);
  414. break;
  415. case 'r': // reset
  416. menu(RESET);
  417. break;
  418. case '+':
  419. menu(ZOOM_IN);
  420. break;
  421. case '-':
  422. menu(ZOOM_OUT);
  423. break;
  424. case 'n':
  425. menu(ROTATION_NEGATIVE);
  426. break;
  427. case '0':
  428. case '1':
  429. case '2':
  430. menu(ROTATION_ZERO + key - '0');
  431. break;
  432. }
  433. }
  434. void timer(int v)
  435. {
  436. // Computing elapsed time for smooth animation.
  437. int time = glutGet(GLUT_ELAPSED_TIME); // In msec
  438. // Set up next timer event
  439. glutTimerFunc(ANIMATION_STEP, timer, time);
  440. if (animating) {
  441. xpos += xposStep * (time - v) / 1000.0;
  442. // When the cars disappear from the right
  443. // draw them entering from the left
  444. if (xpos > 8.1)
  445. xpos = -7.1;
  446. glutPostRedisplay();
  447. }
  448. }
  449. void reshape(int width, int height)
  450. {
  451. // A very minimal reshape function to ensure that the viewport has a fixed aspect ratio.
  452. if (width > height*ASPECT_RATIO) {
  453. glViewport((width-height*ASPECT_RATIO)/2, 0, height*ASPECT_RATIO, height);
  454. }
  455. else {
  456. glViewport(0, (height-width/ASPECT_RATIO)/2, width, width/ASPECT_RATIO);
  457. }
  458. return;
  459. }
  460. int main(int argc, char** argv)
  461. {
  462. glutInit(&argc, argv);
  463. //specify double buffering
  464. glutInitDisplayMode (GLUT_DOUBLE | GLUT_RGB);
  465. glutInitWindowSize (600*ASPECT_RATIO, 600);
  466. glutInitWindowPosition (0, 0);
  467. glutCreateWindow ("Model Car");
  468. init ();
  469. glutDisplayFunc(display);
  470. glutKeyboardFunc(keyboard);
  471. glutTimerFunc(1000/60, timer, 0);
  472. glutReshapeFunc(reshape);
  473. glutCreateMenu(menu);
  474. glutAddMenuEntry("a: Animating on/off", ANIMATING);
  475. glutAddMenuEntry("f: Fast animation", ANIMATION_FAST);
  476. glutAddMenuEntry("s: Slow animation", ANIMATION_SLOW);
  477. glutAddMenuEntry("r: Reset", RESET);
  478. glutAddMenuEntry("+: Zoom in", ZOOM_IN);
  479. glutAddMenuEntry("-: Zoom out", ZOOM_OUT);
  480. glutAddMenuEntry("n: Negative wheel rotation", ROTATION_NEGATIVE);
  481. glutAddMenuEntry("0: No wheel rotation", ROTATION_ZERO);
  482. glutAddMenuEntry("1: Normal wheel rotation", ROTATION_NORMAL);
  483. glutAddMenuEntry("2: Double wheel rotation", ROTATION_DOUBLE);
  484. glutAttachMenu(GLUT_RIGHT_BUTTON);
  485. glutMainLoop();
  486. return 0;
  487. }