demo_tracks.cpp 13 KB

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