2
0

demo_tracks.cpp 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498
  1. /*************************************************************************
  2. * *
  3. * Open Dynamics Engine, Copyright (C) 2001,2002 Russell L. Smith. *
  4. * All rights reserved. Email: [email protected] Web: www.q12.org *
  5. * *
  6. * This library is free software; you can redistribute it and/or *
  7. * modify it under the terms of EITHER: *
  8. * (1) The GNU Lesser General Public License as published by the Free *
  9. * Software Foundation; either version 2.1 of the License, or (at *
  10. * your option) any later version. The text of the GNU Lesser *
  11. * General Public License is included with this library in the *
  12. * file LICENSE.TXT. *
  13. * (2) The BSD-style license that is included with this library in *
  14. * the file LICENSE-BSD.TXT. *
  15. * *
  16. * This library is distributed in the hope that it will be useful, *
  17. * but WITHOUT ANY WARRANTY; without even the implied warranty of *
  18. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the files *
  19. * LICENSE.TXT and LICENSE-BSD.TXT for more details. *
  20. * *
  21. *************************************************************************/
  22. //#include <iostream>
  23. #include <ode/ode.h>
  24. #include <drawstuff/drawstuff.h>
  25. #include "texturepath.h"
  26. #ifdef dDOUBLE
  27. #define dsDrawSphere dsDrawSphereD
  28. #define dsDrawBox dsDrawBoxD
  29. #define dsDrawTriangle dsDrawTriangleD
  30. #define dsDrawLine dsDrawLineD
  31. #endif
  32. const dReal ball_radius = 0.4;
  33. const dReal balls_sep = 2; // separation between the balls
  34. /* Choose one test case
  35. */
  36. #define TEST_CASE 0
  37. #if TEST_CASE == 0
  38. const dReal track_len = 10;
  39. const dReal track_height = 1;
  40. const dReal track_width = 0.1;
  41. const dReal track_gauge = 1;
  42. const dReal track_elevation = 2;
  43. const dReal track_angle = 80 * M_PI/180.;
  44. const dReal track_incl = 10 * M_PI/180.;
  45. #elif TEST_CASE == 1
  46. const dReal track_len = 10;
  47. const dReal track_height = 1;
  48. const dReal track_width = 0.1;
  49. const dReal track_gauge = 1.9*ball_radius;
  50. const dReal track_elevation = 2;
  51. const dReal track_angle = 0 * M_PI/180.;
  52. const dReal track_incl = 10 * M_PI/180.;
  53. #elif TEST_CASE == 2
  54. const dReal track_len = 10;
  55. const dReal track_height = 1;
  56. const dReal track_width = 0.1;
  57. const dReal track_gauge = 1.9*ball_radius;
  58. const dReal track_elevation = 2;
  59. const dReal track_angle = 15 * M_PI/180.;
  60. const dReal track_incl = 10 * M_PI/180.;
  61. #elif TEST_CASE == 3
  62. const dReal track_len = 10;
  63. const dReal track_height = .7;
  64. const dReal track_width = 0.1;
  65. const dReal track_gauge = track_height*1.1;
  66. const dReal track_elevation = 2;
  67. const dReal track_angle = 90 * M_PI/180.;
  68. const dReal track_incl = 10 * M_PI/180.;
  69. #else
  70. #error "TEST_CAST to a valid value!"
  71. #endif
  72. dWorldID world;
  73. dSpaceID space;
  74. dJointGroupID contact_group;
  75. dGeomID ground;
  76. dGeomID ball1_geom, ball2_geom;
  77. dTriMeshDataID mesh_data;
  78. dGeomID mesh_geom;
  79. dBodyID ball1_body, ball2_body;
  80. const unsigned n_box_verts = 8;
  81. dVector3 box_verts[n_box_verts] = {
  82. {-track_len/2, -track_width/2, track_height/2}, // 0
  83. { track_len/2, -track_width/2, track_height/2}, // 1
  84. { track_len/2, track_width/2, track_height/2}, // 2
  85. {-track_len/2, track_width/2, track_height/2}, // 3
  86. { track_len/2, -track_width/2, -track_height/2}, // 4
  87. {-track_len/2, -track_width/2, -track_height/2}, // 5
  88. {-track_len/2, track_width/2, -track_height/2}, // 6
  89. { track_len/2, track_width/2, -track_height/2} // 7
  90. };
  91. const unsigned n_box_faces = 12;
  92. dTriIndex box_faces[n_box_faces * 3] = {
  93. 0, 1, 2,
  94. 0, 2, 3,
  95. 1, 4, 7,
  96. 1, 7, 2,
  97. 4, 5, 6,
  98. 4, 6, 7,
  99. 5, 0, 3,
  100. 5, 3, 6,
  101. 3, 2, 7,
  102. 3, 7, 6,
  103. 0, 5, 4,
  104. 0, 4, 1
  105. };
  106. const unsigned n_track_verts = n_box_verts * 2;
  107. const unsigned n_track_faces = n_box_faces * 2;
  108. dVector3 track_verts[n_track_verts];
  109. dTriIndex track_faces[n_track_faces * 3];
  110. void resetBall(dBodyID b, unsigned idx)
  111. {
  112. dBodySetPosition(b,
  113. 0.5*track_len*cos(track_incl) // Z
  114. - 0.5*track_height*sin(track_incl)
  115. - ball_radius, // X
  116. balls_sep*idx, // Y
  117. track_elevation + ball_radius// Z
  118. + 0.5*track_len*sin(track_incl)
  119. + 0.5*track_height*cos(track_incl));
  120. dMatrix3 r = {1, 0, 0, 0,
  121. 0, 1, 0, 0,
  122. 0, 0, 1, 0};
  123. dBodySetRotation(b, r);
  124. dBodySetLinearVel(b, 0, 0, 0);
  125. dBodySetAngularVel(b, 0, 0, 0);
  126. }
  127. void resetSim()
  128. {
  129. resetBall(ball1_body, 0);
  130. resetBall(ball2_body, 1);
  131. }
  132. void start()
  133. {
  134. dAllocateODEDataForThread(dAllocateMaskAll);
  135. world = dWorldCreate();
  136. dWorldSetGravity (world,0,0,-9.8);
  137. contact_group = dJointGroupCreate(0);
  138. space = dSimpleSpaceCreate (0);
  139. // first, the ground plane
  140. // it has to coincide with the plane we have in drawstuff
  141. ground = dCreatePlane(space, 0, 0, 1, 0);
  142. // now a ball
  143. dMass m;
  144. dMassSetSphere(&m, 0.1, ball_radius);
  145. ball1_geom = dCreateSphere(space, ball_radius);
  146. ball1_body = dBodyCreate(world);
  147. dGeomSetBody(ball1_geom, ball1_body);
  148. dBodySetMass(ball1_body, &m);
  149. ball2_geom = dCreateSphere(space, ball_radius);
  150. ball2_body = dBodyCreate(world);
  151. dGeomSetBody(ball2_geom, ball2_body);
  152. dBodySetMass(ball2_body, &m);
  153. // tracks made out of boxes
  154. dGeomID trk;
  155. dMatrix3 r1, r2, r3;
  156. dVector3 ro = {0, -(0.5*track_gauge + 0.5*track_width), track_elevation};
  157. dMatrix3 s1, s2, s3;
  158. dVector3 so = {0, 0.5*track_gauge + 0.5*track_width, track_elevation};
  159. dRFromAxisAndAngle(r1, 1, 0, 0, track_angle);
  160. dRFromAxisAndAngle(r2, 0, 1, 0, -track_incl);
  161. dMultiply0_333(r3, r2, r1);
  162. dRFromAxisAndAngle(s1, 1, 0, 0, -track_angle);
  163. dRFromAxisAndAngle(s2, 0, 1, 0, -track_incl);
  164. dMultiply0_333(s3, s2, s1);
  165. trk = dCreateBox(space, track_len, track_width, track_height);
  166. dGeomSetPosition(trk, ro[0], ro[1] + balls_sep, ro[2]);
  167. dGeomSetRotation(trk, r3);
  168. trk = dCreateBox(space, track_len, track_width, track_height);
  169. dGeomSetPosition(trk, so[0], so[1] + balls_sep, so[2]);
  170. dGeomSetRotation(trk, s3);
  171. // tracks made out of trimesh
  172. for (unsigned i=0; i<n_box_verts; ++i) {
  173. dVector3 p;
  174. dMultiply0_331(p, s3, box_verts[i]);
  175. dAddVectors3(p, p, so);
  176. dCopyVector3(track_verts[i], p);
  177. }
  178. // trimesh tracks 2, transform all vertices by s3
  179. for (unsigned i=0; i<n_box_verts; ++i) {
  180. dVector3 p;
  181. dMultiply0_331(p, r3, box_verts[i]);
  182. dAddVectors3(p, p, ro);
  183. dCopyVector3(track_verts[n_box_verts + i], p);
  184. }
  185. // copy face indices
  186. for (unsigned i=0; i<n_box_faces; ++i)
  187. for (unsigned j=0; j<3; ++j) // each face index
  188. track_faces[3*i+j] = box_faces[3*i+j];
  189. for (unsigned i=0; i<n_box_faces; ++i)
  190. for (unsigned j=0; j<3; ++j) // each face index
  191. track_faces[3*(i + n_box_faces)+j] = box_faces[3*i+j] + n_box_verts;
  192. mesh_data = dGeomTriMeshDataCreate();
  193. dGeomTriMeshDataBuildSimple(mesh_data,
  194. track_verts[0], n_track_verts,
  195. track_faces, 3*n_track_faces);
  196. mesh_geom = dCreateTriMesh(space, mesh_data, 0, 0, 0);
  197. resetSim();
  198. // initial camera position
  199. static float xyz[3] = {-5.9414,-0.4804,2.9800};
  200. static float hpr[3] = {32.5000,-10.0000,0.0000};
  201. dsSetViewpoint (xyz,hpr);
  202. dsSetSphereQuality(3);
  203. }
  204. void nearCallback(void *, dGeomID a, dGeomID b)
  205. {
  206. const unsigned max_contacts = 8;
  207. dContact contacts[max_contacts];
  208. if (!dGeomGetBody(a) && !dGeomGetBody(b))
  209. return; // don't handle static geom collisions
  210. int n = dCollide(a, b, max_contacts, &contacts[0].geom, sizeof(dContact));
  211. //clog << "got " << n << " contacts" << endl;
  212. /* Simple contact merging:
  213. * If we have contacts that are too close with the same normal, keep only
  214. * the one with maximum depth.
  215. * The epsilon that defines what "too close" means can be a heuristic.
  216. */
  217. int new_n = 0;
  218. dReal epsilon = 1e-1; // default
  219. /* If we know one of the geoms is a sphere, we can base the epsilon on the
  220. * sphere's radius.
  221. */
  222. dGeomID s = 0;
  223. if ((dGeomGetClass(a) == dSphereClass && (s = a)) ||
  224. (dGeomGetClass(b) == dSphereClass && (s = b))) {
  225. epsilon = dGeomSphereGetRadius(s) * 0.3;
  226. }
  227. for (int i=0; i<n; ++i) {
  228. // this block draws the contact points before merging, in red
  229. dMatrix3 r;
  230. dRSetIdentity(r);
  231. dsSetColor(1, 0, 0);
  232. dsSetTexture(DS_NONE);
  233. dsDrawSphere(contacts[i].geom.pos, r, 0.008);
  234. // let's offset the line a bit to avoid drawing overlap issues
  235. float xyzf[3], hprf[3];
  236. dsGetViewpoint(xyzf, hprf);
  237. dVector3 xyz = {dReal(xyzf[0]), dReal(xyzf[1]), dReal(xyzf[2])};
  238. dVector3 v;
  239. dSubtractVectors3(v, contacts[i].geom.pos, xyz);
  240. dVector3 c;
  241. dCalcVectorCross3(c, v, contacts[i].geom.pos);
  242. dSafeNormalize3(c);
  243. dVector3 pos1;
  244. dAddScaledVectors3(pos1, contacts[i].geom.pos, c, 1, 0.005);
  245. dVector3 pos2;
  246. dAddScaledVectors3(pos2, pos1, contacts[i].geom.normal, 1, 0.05);
  247. dsDrawLine(pos1, pos2);
  248. // end of contacts drawing code
  249. int closest_point = i;
  250. for (int j=0; j<new_n; ++j) {
  251. dReal alignment = dCalcVectorDot3(contacts[i].geom.normal, contacts[j].geom.normal);
  252. if (alignment > 0.99 // about 8 degrees of difference
  253. &&
  254. dCalcPointsDistance3(contacts[i].geom.pos, contacts[j].geom.pos) < epsilon) {
  255. // they are too close
  256. closest_point = j;
  257. //clog << "found close points: " << j << " and " << i << endl;
  258. break;
  259. }
  260. }
  261. if (closest_point != i) {
  262. // we discard one of the points
  263. if (contacts[i].geom.depth > contacts[closest_point].geom.depth)
  264. // the new point is deeper, copy it over closest_point
  265. contacts[closest_point] = contacts[i];
  266. } else
  267. contacts[new_n++] = contacts[i]; // the point is preserved
  268. }
  269. //clog << "reduced from " << n << " to " << new_n << endl;
  270. n = new_n;
  271. for (int i=0; i<n; ++i) {
  272. contacts[i].surface.mode = dContactBounce | dContactApprox1 | dContactSoftERP;
  273. contacts[i].surface.mu = 10;
  274. contacts[i].surface.bounce = 0.2;
  275. contacts[i].surface.bounce_vel = 0;
  276. contacts[i].surface.soft_erp = 1e-3;
  277. //clog << "depth: " << contacts[i].geom.depth << endl;
  278. dJointID contact = dJointCreateContact(world, contact_group, &contacts[i]);
  279. dJointAttach(contact, dGeomGetBody(a), dGeomGetBody(b));
  280. dMatrix3 r;
  281. dRSetIdentity(r);
  282. dsSetColor(0, 0, 1);
  283. dsSetTexture(DS_NONE);
  284. dsDrawSphere(contacts[i].geom.pos, r, 0.01);
  285. dsSetColor(0, 1, 0);
  286. dVector3 pos2;
  287. dAddScaledVectors3(pos2, contacts[i].geom.pos, contacts[i].geom.normal, 1, 0.1);
  288. dsDrawLine(contacts[i].geom.pos, pos2);
  289. }
  290. //clog << "----" << endl;
  291. }
  292. void stop()
  293. {
  294. dGeomDestroy(mesh_geom);
  295. dGeomTriMeshDataDestroy(mesh_data);
  296. dBodyDestroy(ball1_body);
  297. dBodyDestroy(ball2_body);
  298. dGeomDestroy(ground);
  299. dJointGroupDestroy(contact_group);
  300. dSpaceDestroy(space); // will destroy all geoms
  301. dWorldDestroy(world);
  302. }
  303. static void command (int cmd)
  304. {
  305. switch (cmd) {
  306. case ' ':
  307. resetSim();
  308. break;
  309. }
  310. }
  311. void drawGeom(dGeomID g)
  312. {
  313. int gclass = dGeomGetClass(g);
  314. const dReal *pos = dGeomGetPosition(g);
  315. const dReal *rot = dGeomGetRotation(g);
  316. switch (gclass) {
  317. case dSphereClass:
  318. dsSetColorAlpha(0, 0.75, 0.5, 0.5);
  319. dsSetTexture (DS_CHECKERED);
  320. dsDrawSphere(pos, rot, dGeomSphereGetRadius(g));
  321. break;
  322. case dBoxClass:
  323. {
  324. dVector3 lengths;
  325. dsSetColorAlpha(1, 1, 0, 0.5);
  326. dsSetTexture (DS_WOOD);
  327. dGeomBoxGetLengths(g, lengths);
  328. dsDrawBox(pos, rot, lengths);
  329. break;
  330. }
  331. case dTriMeshClass:
  332. {
  333. int numi = dGeomTriMeshGetTriangleCount(g);
  334. for (int i=0; i<numi; ++i) {
  335. dVector3 v0, v1, v2;
  336. dGeomTriMeshGetTriangle(g, i, &v0, &v1, &v2);
  337. dsSetTexture (DS_WOOD);
  338. dsSetDrawMode(DS_WIREFRAME);
  339. dsSetColorAlpha(0, 0, 0, 1.0);
  340. dsDrawTriangle(pos, rot, v0, v1, v2, true);
  341. dsSetDrawMode(DS_POLYFILL);
  342. dsSetColorAlpha(1, 1, 0, 0.5);
  343. dsDrawTriangle(pos, rot, v0, v1, v2, true);
  344. }
  345. break;
  346. }
  347. default:
  348. {}
  349. }
  350. }
  351. void simLoop (int pause)
  352. {
  353. if (!pause) {
  354. const dReal step = 0.02;
  355. const unsigned nsteps = 1;
  356. for (unsigned i=0; i<nsteps; ++i) {
  357. dSpaceCollide(space, 0, nearCallback);
  358. dWorldQuickStep(world, step);
  359. dJointGroupEmpty(contact_group);
  360. }
  361. } else {
  362. dSpaceCollide(space, 0, nearCallback);
  363. dJointGroupEmpty(contact_group);
  364. }
  365. // now we draw everything
  366. unsigned ngeoms = dSpaceGetNumGeoms(space);
  367. for (unsigned i=0; i<ngeoms; ++i) {
  368. dGeomID g = dSpaceGetGeom(space, i);
  369. if (g == ground)
  370. continue; // drawstuff is already drawing it for us
  371. drawGeom(g);
  372. }
  373. if (dBodyGetPosition(ball1_body)[0] < -track_len)
  374. resetSim();
  375. }
  376. int main (int argc, char **argv)
  377. {
  378. // setup pointers to drawstuff callback functions
  379. dsFunctions fn;
  380. fn.version = DS_VERSION;
  381. fn.start = &start;
  382. fn.step = &simLoop;
  383. fn.command = &command;
  384. fn.stop = stop;
  385. fn.path_to_textures = DRAWSTUFF_TEXTURE_PATH;
  386. // create world
  387. dInitODE();
  388. // run demo
  389. dsSimulationLoop (argc, argv, 800, 600, &fn);
  390. dCloseODE();
  391. return 0;
  392. }