1 /* Newtonian physics simulation using the Open Dynamics Engine
2 Copyright (C) 2005-2008, Jonathan Bachrach, Jacob Beal, and contributors
3 listed in the AUTHORS file in the MIT Proto distribution's top directory.
5 This file is part of MIT Proto, and is distributed under the terms of
6 the GNU General Public License, with a linking exception, as described
7 in the file LICENSE in the MIT Proto distribution's top directory. */
10 #include "odedynamics.h"
11 #include "visualizer.h"
13 /*****************************************************************************
15 *****************************************************************************/
16 // Note: this largely duplicates some of the code in drawing_primitives.cpp
18 static void set_transform(const float pos
[3], const float R
[12]) {
21 matrix
[0]=R
[0]; matrix
[1]=R
[4]; matrix
[2]=R
[8]; matrix
[3]=0;
22 matrix
[4]=R
[1]; matrix
[5]=R
[5]; matrix
[6]=R
[9]; matrix
[7]=0;
23 matrix
[8]=R
[2]; matrix
[9]=R
[6]; matrix
[10]=R
[10]; matrix
[11]=0;
24 matrix
[12]=pos
[0]; matrix
[13]=pos
[1]; matrix
[14]=pos
[2]; matrix
[15]=1;
26 glMultMatrixf(matrix
);
30 static void do_draw_wire_box(const float sides
[3]) {
32 float w
= sides
[0]/2, h
= sides
[1]/2, d
= sides
[2]/2;
33 glBegin( GL_LINE_STRIP
);
35 glVertex3d(-w
, -h
, -d
);
36 glVertex3d(-w
, h
, -d
);
37 glVertex3d( w
, h
, -d
);
38 glVertex3d( w
, -h
, -d
);
39 glVertex3d(-w
, -h
, -d
);
41 glVertex3d(-w
, -h
, d
);
44 glVertex3d( w
, -h
, d
);
45 glVertex3d(-w
, -h
, d
);
47 glVertex3d( w
, -h
, d
);
48 glVertex3d( w
, -h
, -d
);
49 glVertex3d( w
, h
, -d
);
52 glVertex3d(-w
, h
, -d
);
58 static void do_draw_box(const float sides
[3]) {
60 float lx
= sides
[0]*0.5f
, ly
= sides
[1]*0.5f
, lz
= sides
[2]*0.5f
;
62 glBegin (GL_TRIANGLE_STRIP
);
64 glVertex3f (-lx
,-ly
,-lz
);
65 glVertex3f (-lx
,-ly
,lz
);
66 glVertex3f (-lx
,ly
,-lz
);
67 glVertex3f (-lx
,ly
,lz
);
69 glVertex3f (lx
,ly
,-lz
);
70 glVertex3f (lx
,ly
,lz
);
72 glVertex3f (lx
,-ly
,-lz
);
73 glVertex3f (lx
,-ly
,lz
);
75 glVertex3f (-lx
,-ly
,-lz
);
76 glVertex3f (-lx
,-ly
,lz
);
79 glBegin (GL_TRIANGLE_FAN
);
81 glVertex3f (-lx
,-ly
,lz
);
82 glVertex3f (lx
,-ly
,lz
);
83 glVertex3f (lx
,ly
,lz
);
84 glVertex3f (-lx
,ly
,lz
);
87 glBegin (GL_TRIANGLE_FAN
);
89 glVertex3f (-lx
,-ly
,-lz
);
90 glVertex3f (-lx
,ly
,-lz
);
91 glVertex3f (lx
,ly
,-lz
);
92 glVertex3f (lx
,-ly
,-lz
);
97 void draw_box(const float *pos
, const float *R
, const float *sides
) {
99 set_transform(pos
, R
);
105 void draw_wire_box(const float *pos
, const float *R
, const float *sides
) {
107 set_transform(pos
, R
);
108 do_draw_wire_box(sides
);
113 /*****************************************************************************
115 *****************************************************************************/
117 ODEBody::ODEBody(ODEDynamics
*parent
, Device
* container
, flo x
, flo y
, flo z
,
118 flo r
) : Body(container
) {
119 this->parent
=parent
; moved
=FALSE
;
120 for(int i
=0;i
<3;i
++) desired_v
[i
]=0;
122 // create and attach body, shape, and mass
123 body
= dBodyCreate(parent
->world
);
124 geom
= dCreateBox(parent
->space
,r
*2,r
*2,r
*2);
125 dMass m
; dMassSetBox(&m
,parent
->density
,r
*2,r
*2,r
*2);
126 dGeomSetBody(geom
,body
); dBodySetMass(body
,&m
);
127 // set position and orientation
128 dBodySetPosition(body
, x
, y
, z
);
129 dQuaternion Q
; dQFromAxisAndAngle (Q
,0,0,1,0);
130 dBodySetQuaternion (body
,Q
);
131 // set up back-pointer
132 dBodySetData(body
,(void*)this);
133 dGeomSetData(geom
,(void*)this);
136 ODEBody::~ODEBody() {
137 dBodyDestroy(body
); dGeomDestroy(geom
);
138 parent
->bodies
.remove(parloc
);
141 void ODEBody::visualize() {
143 if(!parent
->is_show_bot
) return; // don't display unless should be shown
145 // get location/size information
146 const flo pos
[3] = {0,0,0};
147 const dReal
*R
= dGeomGetRotation(geom
);
148 dVector3 sides
; dGeomBoxGetLengths(geom
,sides
);
152 if(container
->is_selected
) { palette
->use_color(ODE_SELECTED
);
153 } else if(!dBodyIsEnabled(body
)) { palette
->use_color(ODE_DISABLED
);
155 if(parent
->is_multicolored_bots
) {
156 flo h
= (360.0*parloc
)/parent
->bodies
.max_id();
157 flo r
, g
, b
; hsv_to_rgb(h
, 1, 1, &r
, &g
, &b
);
158 palette
->push_color(ODE_BOT
,r
/255,g
/255,b
/255,0.7);
161 palette
->use_color(ODE_BOT
);
163 draw_box(pos
,R
,sides
);
164 palette
->use_color(ODE_EDGES
); // draw edges in separate color
165 draw_wire_box(pos
,R
,sides
);
166 if(pushed
) palette
->pop_color(ODE_BOT
);
170 void ODEBody::render_selection() {
171 const flo pos
[3] = {0,0,0};
172 const dReal
*R
= dGeomGetRotation(geom
);
173 dVector3 sides
; dGeomBoxGetLengths(geom
,sides
);
174 draw_box(pos
,R
,sides
);
177 void ODEBody::dump_state(FILE* out
, int verbosity
) {
179 const flo
*v
= position(); fprintf(out
," %.2f %.2f %.2f",v
[0],v
[1],v
[2]);
180 v
= velocity(); fprintf(out
," %.2f %.2f %.2f",v
[0],v
[1],v
[2]);
181 v
= orientation(); fprintf(out
," %.2f %.2f %.2f %.2f",v
[0],v
[1],v
[2],v
[3]);
182 v
= ang_velocity(); fprintf(out
," %.2f %.2f %.2f",v
[0],v
[1],v
[2]);
184 const flo
*v
= position();
185 fprintf(out
,"Position=[%.2f %.2f %.2f], ",v
[0],v
[1],v
[2]);
187 fprintf(out
,"Velocity=[%.2f %.2f %.2f] (Speed=%.2f)\n",v
[0],v
[1],v
[2],
188 sqrt(v
[0]*v
[0] + v
[1]*v
[1] + v
[2]*v
[2]));
190 fprintf(out
,"Orientation=[%.2f %.2f %.2f %.2f], ",v
[0],v
[1],v
[2],v
[3]);
192 fprintf(out
,"Angular Velocity=[%.2f %.2f %.2f]\n",v
[0],v
[1],v
[2]);
197 void ODEBody::drive() {
198 const dReal
*cur
= velocity();
200 force
[0] = K_MOVE
* (desired_v
[0] - cur
[0]);
201 force
[1] = K_MOVE
* (desired_v
[1] - cur
[1]);
202 force
[2] = (parent
->parent
->is_3d())? K_MOVE
* (desired_v
[2] - cur
[2]) : 0;
203 dBodyAddForce(body
, force
[0], force
[1], force
[2]);
206 /*****************************************************************************
207 * COLLISION HANDLING *
208 *****************************************************************************/
210 #define WALL_DATA -1 // identifier for walls
211 #define MAX_CONTACTS 8 // maximum number of contact points per body
213 static int isWall (dGeomID g
) { return dGeomGetData(g
)==((void*)WALL_DATA
); }
215 static void nearCallback (void *data
, dGeomID o1
, dGeomID o2
) {
217 ODEDynamics
*dyn
= (ODEDynamics
*)data
;
219 // exit without doing anything if the two bodies are connected by a joint
220 dBodyID b1
= dGeomGetBody(o1
); dBodyID b2
= dGeomGetBody(o2
);
221 if (b1
&& b2
&& dAreConnectedExcluding(b1
,b2
,dJointTypeContact
)) return;
222 // exit without doing anything if the walls are off and one object is a wall
223 if(!dyn
->is_walls
&& (isWall(o1
) || isWall(o2
))) { return; }
225 dContact contact
[MAX_CONTACTS
]; // up to MAX_CONTACTS contacts per pair
226 if (int numc
= dCollide (o1
,o2
,MAX_CONTACTS
,&contact
[0].geom
,
228 for (i
=0; i
<numc
; i
++) {
229 contact
[i
].surface
.mode
= dContactBounce
| dContactSoftCFM
;
230 contact
[i
].surface
.mu
= 0;
231 // contact[i].surface.mu = dInfinity;
232 contact
[i
].surface
.mu2
= 0;
233 // contact[i].surface.bounce = 0.1;
234 contact
[i
].surface
.bounce
= 0.5;
235 contact
[i
].surface
.bounce_vel
= 0.1;
236 contact
[i
].surface
.soft_cfm
= 0.0; // give
237 dJointID c
=dJointCreateContact(dyn
->world
,dyn
->contactgroup
,&contact
[i
]);
238 dJointAttach(c
,b1
,b2
);
241 if(b1
&& (b2
|| isWall(o2
))) ((ODEBody
*)dBodyGetData(b1
))->did_bump
=TRUE
;
242 if(b2
&& (b1
|| isWall(o1
))) ((ODEBody
*)dBodyGetData(b2
))->did_bump
=TRUE
;
246 /*****************************************************************************
248 *****************************************************************************/
250 #define DENSITY (0.125/2) // default density
251 #define MAX_V 100 // default ceiling on velocity
252 #define SUBSTEP 0.001 // default sub-step size
253 #define K_BODY_RAD 0.0870 // constant matched against previous visualization
255 void ODEDynamics::make_walls() {
256 flo pen_w
=parent
->volume
->r
- parent
->volume
->l
;
257 flo pen_h
=parent
->volume
->t
- parent
->volume
->b
;
261 pen
= dCreateBox(0,pen_w
+2*wall_width
,pen_h
+2*wall_width
,10*pen_h
);
264 dQFromAxisAndAngle (Q
,0,0,1,M_PI
/2);
265 walls
[0] = dCreateBox(space
, pen_h
+2*wall_width
, wall_width
, 10*pen_h
);
266 dGeomSetQuaternion (walls
[0],Q
);
267 dGeomSetPosition (walls
[0], pen_w
/2+wall_width
/2, 0, 0);
268 dGeomSetData(walls
[0], (void*)WALL_DATA
);
270 walls
[1] = dCreateBox(space
, pen_h
+2*wall_width
, wall_width
, 10*pen_h
);
271 dGeomSetQuaternion (walls
[1],Q
);
272 dGeomSetPosition (walls
[1], -pen_w
/2-wall_width
/2, 0, 0);
273 dGeomSetData(walls
[1], (void*)WALL_DATA
);
275 dQFromAxisAndAngle (Q
,0,0,1,0);
276 walls
[2] = dCreateBox(space
, pen_w
+2*wall_width
, wall_width
, 10*pen_h
);
277 dGeomSetQuaternion (walls
[2],Q
);
278 dGeomSetPosition (walls
[2], 0, pen_h
/2+wall_width
/2, 0);
279 dGeomSetData(walls
[2], (void*)WALL_DATA
);
281 walls
[3] = dCreateBox(space
, pen_w
+2*wall_width
, wall_width
, 10*pen_h
);
282 dGeomSetQuaternion (walls
[3],Q
);
283 dGeomSetPosition (walls
[3], 0, -pen_h
/2-wall_width
/2, 0);
284 dGeomSetData(walls
[3], (void*)WALL_DATA
);
287 dQFromAxisAndAngle (Q
,0,0,1,M_PI
/4);
289 walls
[4] = dCreateBox(space
, 2*wall_width
, 2*wall_width
, 10*pen_h
);
290 dGeomSetQuaternion (walls
[4],Q
);
291 dGeomSetPosition (walls
[4], -pen_w
/2, -pen_h
/2, 0);
292 dGeomSetData(walls
[4], (void*)WALL_DATA
);
294 walls
[5] = dCreateBox(space
, 2*wall_width
, 2*wall_width
, 10*pen_h
);
295 dGeomSetQuaternion (walls
[5],Q
);
296 dGeomSetPosition (walls
[5], pen_w
/2, -pen_h
/2, 0);
297 dGeomSetData(walls
[5], (void*)WALL_DATA
);
299 walls
[6] = dCreateBox(space
, 2*wall_width
, 2*wall_width
, 10*pen_h
);
300 dGeomSetQuaternion (walls
[6],Q
);
301 dGeomSetPosition (walls
[6], pen_w
/2, pen_h
/2, 0);
302 dGeomSetData(walls
[6], (void*)WALL_DATA
);
304 walls
[7] = dCreateBox(space
, 2*wall_width
, 2*wall_width
, 10*pen_h
);
305 dGeomSetQuaternion (walls
[7],Q
);
306 dGeomSetPosition (walls
[7], -pen_w
/2, pen_h
/2, 0);
307 dGeomSetData(walls
[7], (void*)WALL_DATA
);
310 // Note: ODE's manual claims that dCloseODE is optional
311 BOOL
ODEDynamics::inited
= FALSE
;
312 ODEDynamics::ODEDynamics(Args
* args
, SpatialComputer
* p
, int n
)
314 if(!inited
) { dInitODE(); inited
=TRUE
; } // initialize if not yet done so
316 flo width
=parent
->volume
->r
- parent
->volume
->l
;
317 flo height
=parent
->volume
->t
- parent
->volume
->b
;
319 body_radius
= (args
->extract_switch("-rad")) ? args
->pop_number()
320 : K_BODY_RAD
*sqrt((width
*height
)/(flo
)n
);
321 density
= (args
->extract_switch("-density")) ? args
->pop_number() : DENSITY
;
322 speed_lim
= (args
->extract_switch("-S"))?args
->pop_number():MAX_V
;
323 substep
= (args
->extract_switch("-substep"))?args
->pop_number() : SUBSTEP
;
325 post("-substep must be greater than zero; using default %f",SUBSTEP
);
328 is_mobile
= args
->extract_switch("-m");
329 is_show_bot
= !args
->extract_switch("-hide-body");
330 is_walls
= ((args
->extract_switch("-w") & !args
->extract_switch("-nw")))
332 is_draw_walls
= args
->extract_switch("-draw-walls");
333 is_inescapable
= args
->extract_switch("-inescapable");
334 is_multicolored_bots
= args
->extract_switch("-rainbow-bots");
335 args
->undefault(&can_dump
,"-Ddynamics","-NDdynamics");
336 // register to simulate hardware
337 parent
->hardware
.patch(this,MOV_FN
);
338 parent
->hardware
.patch(this,READ_BUMP_FN
);
339 // Initialize ODE and make the walls
340 world
= dWorldCreate();
341 space
= dHashSpaceCreate(0);
342 contactgroup
= dJointGroupCreate(0);
343 if(p
->volume
->dimensions()==2) dWorldSetGravity(world
,0,0,-9.81);
344 dWorldSetCFM(world
,1e-5);
345 dWorldSetAutoDisableFlag(world
,0); // nothing ever disables
346 dWorldSetAutoDisableAverageSamplesCount(world
, 10);
347 dWorldSetContactMaxCorrectingVel(world
,0.1);
348 dWorldSetContactSurfaceLayer(world
,0.001);
349 if(p
->volume
->dimensions()==2) dCreatePlane(space
,0,0,1,-body_radius
);
353 ODEDynamics::~ODEDynamics() {
354 dJointGroupDestroy(contactgroup
);
355 for(int i
=0;i
<ODE_N_WALLS
;i
++) dGeomDestroy(walls
[i
]);
357 dSpaceDestroy(space
);
358 dWorldDestroy(world
);
361 BOOL
ODEDynamics::handle_key(KeyEvent
* key
) {
362 if(key
->normal
&& !key
->ctrl
) {
364 case 'w': is_walls
= !is_walls
; return TRUE
;
365 case 'b': is_show_bot
= !is_show_bot
; return TRUE
;
366 case 'm': is_mobile
= !is_mobile
; return TRUE
;
372 void ODEDynamics::visualize() {
374 if(is_walls
&& is_draw_walls
) {
375 for(int i
=0; i
<ODE_N_WALLS
; i
++) {
376 const dReal
*pos
= dGeomGetPosition(walls
[i
]);
377 const dReal
*R
= dGeomGetRotation(walls
[i
]);
378 dVector3 sides
; dGeomBoxGetLengths(walls
[i
],sides
);
379 palette
->use_color(ODE_WALL
);
380 draw_box(pos
,R
,sides
);
381 palette
->use_color(ODE_EDGES
);
382 draw_wire_box(pos
,R
,sides
);
388 Body
* ODEDynamics::new_body(Device
* d
, flo x
, flo y
, flo z
) {
389 ODEBody
* b
= new ODEBody(this,d
,x
,y
,z
,body_radius
);
390 b
->parloc
= bodies
.add(b
);
394 void ODEDynamics::dump_header(FILE* out
) {
396 fprintf(out
," \"X\" \"Y\" \"Z\"");
397 fprintf(out
," \"V_X\" \"V_Y\" \"V_Z\"");
398 fprintf(out
," \"Q_X\" \"Q_Y\" \"Q_Z\" \"Q_W\"");
399 fprintf(out
," \"W_X\" \"W_Y\" \"W_Z\"");
403 BOOL
ODEDynamics::evolve(SECONDS dt
) {
404 if(!is_mobile
) return FALSE
;
407 dSpaceCollide(space
,this,&nearCallback
);
409 for(int i
=0;i
<bodies
.max_id();i
++)
410 { ODEBody
* b
= (ODEBody
*)bodies
.get(i
); if(b
) b
->drive(); }
411 dWorldQuickStep(world
,substep
);
412 dJointGroupEmpty(contactgroup
);
413 if(is_walls
&& is_inescapable
) reset_escapes();
414 time_slop
-= substep
;
416 for(int i
=0;i
<bodies
.max_id();i
++) // mark everything as moved
417 { Body
* b
= (Body
*)bodies
.get(i
); if(b
) b
->moved
=TRUE
; }
421 // return escaped bots to a new starting position
422 void ODEDynamics::reset_escapes() {
423 for(int i
=0;i
<bodies
.max_id();i
++) {
424 ODEBody
* b
= (ODEBody
*)bodies
.get(i
);
427 if(dCollide(pen
,b
->geom
,1,&contact
.geom
,sizeof(dContact
))) continue;
428 // if not in pen, reset
429 const flo
*p
= b
->position();
430 //post("You cannot escape! [%.2f %.2f %.2f]\n",p[0],p[1],p[2]);
432 if(parent
->distribution
->next_location(loc
)) {
433 b
->set_position(loc
[0],loc
[1],loc
[2]);
434 b
->set_velocity(0,0,0); b
->set_ang_velocity(0,0,0); // start still
435 } else { // if there's no starting position available, die instead
436 hardware
->set_vm_context(b
->container
);
443 // hardware emulation
444 void ODEDynamics::mov(VEC_VAL
*v
) {
445 ODEBody
* b
= (ODEBody
*)device
->body
;
446 b
->desired_v
[0] = NUM_GET(&v
->elts
[0]);
447 b
->desired_v
[1] = NUM_GET(&v
->elts
[1]);
448 b
->desired_v
[2] = v
->n
> 2 ? NUM_GET(&v
->elts
[2]) : 0.0;
449 dReal len
= sqrt(b
->desired_v
[0]*b
->desired_v
[0] +
450 b
->desired_v
[1]*b
->desired_v
[1] +
451 b
->desired_v
[2]*b
->desired_v
[2]);
453 for(int i
=0;i
<3;i
++) b
->desired_v
[i
] *= speed_lim
/len
;
457 NUM_VAL
ODEDynamics::read_bump (VOID
) {
458 return (float)((ODEBody
*)device
->body
)->did_bump
;