[ODE] dBodyAddForce / dBodyAddTorque don't do anything!

Richard Jones rich at annexia.org
Tue Jun 21 15:49:13 MST 2005


Hi:

I'm doing some bindings for ODE to the Objective CAML language.

As part of these bindings, I'm doing a simple wireframe-only
"roll-em-up" game (think Katamari Damashii for those familiar with
that game), which will just form an example for how to use the
library.

So far it's going quite well - the ball falls down, bounces off
scenery and so on.  However I cannot get dBodyAddForce,
dBodyAddTorque, dBodyAddRelForce or dBodyAddRelTorque to do anything
at all.  Even when I set the fx/fy/fz to wild numbers, the ball isn't
affected.  I've added debugging print statements to the low level
bindings which prove that the C function actually gets called and with
the right parameters.

It seems unlikely that dBodyAddForce, etc. are actually broken.  So
can anyone suggest what I'm doing wrong?

Rich.

----------------------------------------------------------------------
(* Toy wireframe version of Katamari Damashii.
 * Copyright (C) 2005 Richard W.M. Jones <rich at annexia.org>
 * $Id$
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 *)

open Printf

open ExtList

open Ode.LowLevel

let width, height = 640, 480

(* Stepsize for physics (in secs). *)
let stepsize = 0.005

(* Real time / Physics time.  If set to 1., then physics proceeds at the
 * same pace as real time.  If > 1, then physics is slowed down.
 *)
let timescale = 4.

let initial = 0., 0., 1.		(* Initial position for katamari. *)

let eye = ref (0., -2., 1.)		(* Camera location. *)
let up = ref (0., 1., 2.)		(* Up vector. *)
let center = ref (0., 0., 0.5)		(* Center of camera. *)

let init_gl () =
  GlDraw.viewport ~x:0 ~y:0 ~w:width ~h:height;
  GlClear.color (0.9375, 0., 1.);
  GlClear.depth 1.0;
  GlFunc.depth_func `less;
  Gl.enable `depth_test;
  GlDraw.shade_model `smooth;
  GlMat.mode `projection;
  GlMat.load_identity ();
  GluMat.perspective ~fovy:45. ~aspect:(float width /. float height)
    ~z:(0.1, 100.);
  GlMat.mode `modelview

let draw_scene kata boxes =
  GlClear.clear [ `color; `depth ];

  (* Camera. *)
  GlMat.load_identity ();
  GluMat.look_at ~eye:!eye ~center:!center ~up:!up;

  (* Katamari. *)
  GlMat.push ();
  let x, y, z = dGeomGetPosition kata in
  let r = dGeomGetRotation kata in
  let matrix = [|
    [| r.r11; r.r21; r.r31; 0. |];
    [| r.r12; r.r22; r.r32; 0. |];
    [| r.r13; r.r23; r.r33; 0. |];
    [| x; y; z; 1. |]
  |] in
  GlMat.mult (GlMat.of_array matrix);
  let radius = dGeomSphereGetRadius kata in
  Glut.wireSphere ~radius ~slices:10 ~stacks:10;
  GlMat.pop ();

  (* Boxes. *)
  let draw_box box =
    GlMat.push ();
    let x, y, z = dGeomGetPosition box in
    GlMat.translate ~x ~y ~z ();
    let lx, ly, lz = dGeomBoxGetLengths box in
    GlMat.scale ~x:lx ~y:ly ~z:lz ();
    Glut.wireCube ~size:1.;
    GlMat.pop ();
  in
  Array.iter draw_box boxes;

  Sdlgl.swap_buffers ()

(* Surface parameters used for all contact points. *)
let surface_params = {
  sp_mode = [`dContactBounce];
  sp_mu = dInfinity; sp_mu2 = 0.;
  sp_bounce = 0.7; sp_bounce_vel = 0.1;
  sp_soft_erp = 0.; sp_soft_cfm = 0.;
  sp_motion1 = 0.; sp_motion2 = 0.;
  sp_slip1 = 0.; sp_slip2 = 0.;
}

let main () =
  Random.self_init ();

  (* Initialise SDL. *)
  Sdl.init [`VIDEO];
  Sdlgl.set_attr [];
  let surface = Sdlvideo.set_video_mode ~w:width ~h:height ~bpp:32 [`OPENGL] in

  (* Create the ODE world. *)
  let ode = dWorldCreate () in
  dWorldSetGravity ode ~x:0. ~y:0. ~z:(-9.81);

  (* Create the objects in the world. *)
  let space = dHashSpaceCreate None in
  dHashSpaceSetLevels space (-4) 4; (* 1/16 .. 16 units. *)

  (* The ground plane goes through the world origin, with the normal
   * facing upwards towards +z.
   *)
  let plane = dCreatePlane (Some space) ~a:0. ~b:0. ~c:1. ~d:0. in

  (* Create the katamari. *)
  let kata, kata_body =
    let radius = 0.5 in
    let body = dBodyCreate ode in
    dBodySetAutoDisableFlag body false;
    let mass = dMassCreate () in
    dMassSetZero mass;
    dMassSetSphereTotal mass ~total_mass:1. ~radius;
    dBodySetMass body mass;
    dMassDestroy mass;
    let kata = dCreateSphere (Some space) ~radius in
    dGeomSetBody kata body;
    let x, y, z = initial in
    dGeomSetPosition kata ~x ~y ~z;
    kata, body in

  (* Scatter boxes around. *)
  let boxes =
    let create_box i =
      let lx, ly, lz = Random.float 0.5, Random.float 0.5, Random.float 0.5 in
      let box = dCreateBox (Some space) ~lx ~ly ~lz in
      let x, y, z = Random.float 10. -. 5., Random.float 10. -. 5., lz/.2. in
      dGeomSetPosition box ~x ~y ~z;
      box
    in
    Array.init 20 (fun i -> create_box i) in

  (* A group to hold the contact joints. *)
  let contact_joint_group = dJointGroupCreate () in

  (* Initialise GL state. *)
  init_gl ();

  (* Current key state. *)
  let key_forward = ref false in
  let key_backward = ref false in
  let key_left = ref false in
  let key_right = ref false in

  (* Start the clocks counting. *)
  let current_time =
    let base = Unix.gettimeofday () in
    fun () ->
      Unix.gettimeofday () -. base
  in

  (* "Physics time" starts off coincident with real time. *)
  let physics_time = ref 0. in

  let rec main_loop () =
    (* Draw it. *)
    draw_scene kata boxes;

    (* Act on events. *)
    let quit = ref false in
    let rec read_events () =
      match Sdlevent.poll () with
	| None -> ()
	| Some event -> do_event event; read_events ()
    and do_event = function
      | Sdlevent.QUIT -> quit := true (* window closed: quit *)
      | Sdlevent.KEYDOWN ke ->
	  (match ke.Sdlevent.keysym with
	     | Sdlkey.KEY_ESCAPE -> quit := true (* escape key: quit *)
	     | Sdlkey.KEY_UP -> key_forward := true
	     | Sdlkey.KEY_DOWN -> key_backward := true
	     | Sdlkey.KEY_LEFT -> key_left := true
	     | Sdlkey.KEY_RIGHT -> key_right := true
	     | _ -> read_events () (* ignore this key *)
	  )
      | Sdlevent.KEYUP ke ->
	  (match ke.Sdlevent.keysym with
	     | Sdlkey.KEY_UP -> key_forward := false
	     | Sdlkey.KEY_DOWN -> key_backward := false
	     | Sdlkey.KEY_LEFT -> key_left := false
	     | Sdlkey.KEY_RIGHT -> key_right := false
	     | _ -> read_events () (* ignore this key *)
	  )
      | _ -> () (* ignore this event *)
    in
    read_events ();
    let quit = !quit in

    (* Run the physics loop until we catch up with real time. *)
    let rec physics_loop to_time =
      if timescale *. !physics_time < to_time then (
	let nr_contacts = ref 0 in

	(* Accumulate forces on the katamari according to the current
	 * key states.
	 *)
	if !key_forward then
	  dBodyAddTorque kata_body ~fx:0. ~fy:1. ~fz:0.;
	if !key_backward then
	  dBodyAddTorque kata_body ~fx:0. ~fy:(-0.7) ~fz:0.;

	(* Collision detection. *)
	let near geom1 geom2 =
	  (* geom1 and geom2 are close.  Test if they collide. *)
	  let contacts = dCollide geom1 geom2 ~max:4 in

	  if Array.length contacts > 0 then (
	    (* Get the bodies, if any. *)
	    let body1 = dGeomGetBody geom1 in
	    let body2 = dGeomGetBody geom2 in

	    (* For each collision, create a contact joint. *)
	    Array.iter (
	      fun contact_geom ->
		incr nr_contacts;

		(* Create the contact joint. *)
		let contact = {
		  c_surface = surface_params;
		  c_geom = contact_geom;
		  c_fdir1 = { x = 0.; y = 0.; z = 0. }
		} in
		let joint =
		  dJointCreateContact ode (Some contact_joint_group) contact in

		(* Attach that joint to the two bodies.  The bodies may be
		 * 'None' indicating a collision with the static world, but
		 * that's OK.
		 *)
		dJointAttach joint body1 body2
	    ) contacts
	  )
	in
	dSpaceCollide space near;

	(* Take a simulation step. *)
	dWorldQuickStep ode stepsize;
	physics_time := !physics_time +. stepsize;

	(* Remove and destroy the contact joints. *)
	dJointGroupEmpty contact_joint_group;

	physics_loop to_time
      )
    in
    physics_loop (current_time ());

    (* Run the garbage collector at a predictable point. *)
    Gc.full_major ();

    (* Loop unless user has quit. *)
    if not quit then main_loop ()
  in
  main_loop ();

  (* Clean up the world. *)
  dGeomDestroy plane;
  dSpaceDestroy space;

  (* Destroy the ODE world and clean up. *)
  dWorldDestroy ode;
  dCloseODE ();

  (* Quit SDL. *)
  Sdl.quit ();

  (* Find any memory allocation bugs. *)
  Gc.full_major ()

let () =
  main ()


-- 
Richard Jones, CTO Merjis Ltd.
Merjis - web marketing and technology - http://merjis.com
Team Notepad - intranets and extranets for business - http://team-notepad.com


More information about the ODE mailing list