Lua logo

Preface

This is the reference manual of MoonODE, which is a Lua binding library for Open Dynamics Engine (ODE), the high performance library for simulating rigid body dynamics developed by Russ Smith. [1]

It is assumed that the reader is familiar with both ODE and the Lua programming language.

For convenience of reference, this document contains external (deep) links to the Lua Reference Manual and the ODE Wiki.

Getting and installing

For installation intructions, refer to the README file in the MoonODE official repository on GitHub.

Module organization

The MoonODE module is loaded using Lua’s require() and returns a table containing the functions it provides (as usual with Lua modules). This manual assumes that such table is named ode, i.e. that it is loaded with:

 ode = require("moonode")

but nothing forbids the use of a different name.

Examples

Complete examples can be found in the examples/ directory of the release package.

License

MoonODE is released under the MIT/X11 license (same as Lua, and with the same only requirement to give proper credits to the original author). The copyright notice is in the LICENSE file in the base directory of the official repository on GitHub.

See also

MoonODE is part of MoonLibs, a collection of Lua libraries for graphics and audio programming.

Introduction

MoonODE is an almost one-to-one Lua binding library to the Open Dynamic Engine (ODE). This means that, by and large, it is intended to be used as described in the ODE Manual (apart from coding in Lua, of course).

This section gives a brief overview of the binding library, while the details are given in the sections that follow.

MoonODE binds ODE objects (world, body, etc.) to Lua userdata, which are returned by the creating functions (ode.create_world( ), ode.create_body( ), etc) and are then used to refer to objects in Lua in the same way as one would use ODE handles in C.

As a general rule, ODE functions are bound to MoonODE functions or methods whose names are (more or less) snake_case versions of the original ones.

The functions related to an object type - with the exception of the creating function - are bounded to methods of that object. For example, the C function dBodySetPosition(body,…​) becomes the method body:set_position(…​) in Lua.

Objects are garbage collected at exit (which includes on error), and automatically deleted at the ODE, so there is no need to explicitly invoke their free( ) methods at exit for cleanup.

Apart from at exit, however, objects are not automatically garbage collected [2] and one must release them explicitly when needed, e.g. to release resources when the application is not exiting and some objects are no longer needed.

Releasing an object causes the automatic (pre) destruction of all its children objects, and the invalidation of any reference to the object and to its children. [3]

If not stated otherwise, on error all MoonODE functions raise a Lua error. If needed, this behaviour can be overridden by wrapping function calls in the standard Lua pcall( ).

Objects

Listed below are the Lua object types in MoonODE, together with the corresponding original types in ODE:

Objects:
world (dWorldID)
body (dBodyID)
joint (dJointID)
├─ joint_null
├─ joint_contact
├─ joint_ball
├─ joint_hinge
├─ joint_slider
├─ joint_hinge2
├─ joint_universal
├─ joint_pr
├─ joint_pu
├─ joint_piston
├─ joint_fixed
├─ joint_amotor
├─ joint_lmotor
├─ joint_plane2d
├─ joint_dball
├─ joint_dhinge
└─ joint_transmission
geom (dGeomID)
├─ geom_sphere
├─ geom_box
├─ geom_plane
├─ geom_capsule
├─ geom_cylinder
├─ geom_ray
├─ geom_convex
├─ geom_trimesh
└─ geom_heightfield
tmdata (dTriMeshDataID)
hfdata (dHeightfieldDataID)
space (dSpaceID)
├─ space_simple
├─ space_hash
├─ space_quadtree
└─ space_sap

Rigid bodies

Rfr: Background.

An object of the body type represents a rigid body with its properties relevant to the simulation, such as mass, position, orientation, velocity, etc. Rigid bodies may be connected by joints of different kinds, which are represented by objects of joint subtypes. A world, represented by the world type, is a container for rigid bodies and joints. Rigid bodies in different worlds cannot interact, e.g. they can not be joined together nor collide with each other. An application would usually only need one world.

world

Rfr: World.

  • world = create_world( )
    world:destroy( )
    Create/delete a world.

  • world:set_gravity(vec3)
    vec3 = world:get_gravity( )

  • force = world:impulse_to_force(dt, impulse)
    dt: float.
    force, impulse: vec3.

  • world:step(dt)
    world:quick_step(dt)
    world:set_quick_step_num_iterations(integer)
    world:set_quick_step_w(float)
    integer = world:get_quick_step_num_iterations( )
    float = world:get_quick_step_w( )
    dt: float.
    Rfr: Stepping Functions.

  • world:set_auto_disable_flag(boolean)
    world:set_auto_disable_linear_threshold(float)
    world:set_auto_disable_angular_threshold(float)
    world:set_auto_disable_steps(integer)
    world:set_auto_disable_time(float)
    world:set_auto_disable_average_samples_count(integer)
    boolean = world:get_auto_disable_flag( )
    float = world:get_auto_disable_linear_threshold( )
    float = world:get_auto_disable_angular_threshold( )
    integer = world:get_auto_disable_steps( )
    float = world:get_auto_disable_time( )
    integer = world:get_auto_disable_average_samples_count( )
    Rfr: Automatic Enabling and Disabling.

  • world:set_linear_damping_threshold(float)
    world:set_angular_damping_threshold(float)
    world:set_linear_damping(float)
    world:set_angular_damping(float)
    world:set_damping(floatlin, floatang)
    world:set_max_angular_speed(float)
    float = world:get_linear_damping_threshold( )
    float = world:get_angular_damping_threshold( )
    float = world:get_linear_damping( )
    float = world:get_angular_damping( )
    floatlin, floatang = world:get_damping( )
    float = world:get_max_angular_speed( )
    Rfr: Damping.

  • world:set_contact_max_correcting_vel(float)
    world:set_contact_surface_layer(float)
    float = world:get_contact_max_correcting_vel( )
    float = world:get_contact_surface_layer( )
    Rfr: Contact Parameters.

body

  • body = create_body(world)
    body:destroy( )
    Create/delete a body.

  • body:set_dynamic( )
    body:set_kinematic( )
    boolean = body:is_kinematic( )
    Rfr: Kinematic State.

  • body:enable( )
    body:disable( )
    boolean = body:is_enabled( )
    body:set_auto_disable_defaults( )
    body:set_auto_disable_flag(boolean)
    body:set_auto_disable_linear_threshold(float)
    body:set_auto_disable_angular_threshold(float)
    body:set_auto_disable_steps(integer)
    body:set_auto_disable_time(float)
    body:set_auto_disable_average_samples_count(integer)
    boolean = body:get_auto_disable_flag( )
    float = body:get_auto_disable_linear_threshold( )
    float = body:get_auto_disable_angular_threshold( )
    integer = body:get_auto_disable_steps( )
    float = body:get_auto_disable_time( )
    integer = body:get_auto_disable_average_samples_count( )
    Rfr: Automatic Enabling and Disabling.

  • body:set_moved_callback([func])
    The callback is executed as func(body)
    Passing func=nil removes the callback.

  • body:set_damping_defaults( )
    body:set_linear_damping_threshold(float)
    body:set_angular_damping_threshold(float)
    body:set_linear_damping(float)
    body:set_angular_damping(float)
    body:set_damping(floatlin, floatang)
    body:set_max_angular_speed(float)
    float = body:get_linear_damping_threshold( )
    float = body:get_angular_damping_threshold( )
    float = body:get_linear_damping( )
    float = body:get_angular_damping( )
    floatlin, floatang = body:get_damping( )
    float = body:get_max_angular_speed( )
    Rfr: Damping.

  • body:set_finite_rotation_mode(boolean)
    body:set_finite_rotation_axis(vec3)
    body:set_gravity_mode(boolean)
    body:set_gyroscopic_mode(boolean)
    boolean = body:get_finite_rotation_mode( )
    vec3 = body:get_finite_rotation_axis( )
    boolean = body:get_gravity_mode( )
    boolean = body:get_gyroscopic_mode( )
    world = body:get_world( )
    {joint} = body:get_joints( )
    {geom} = body:get_geoms( )
    integer = body:get_num_joints( )
    Rfr: Miscellaneous Body Functions.

joint

Joints may be of the following subtypes: null, contact, ball, hinge, slider, hinge2, universal, pr, pu, piston, fixed, amotor, lmotor, plane2d, dball, dhinge, and transmission.

Each subtype has its specialized contructors and methods, in addition to the following methods that are common to all subtypes:

  • joint:set_feedback(boolean)
    force1, torque1, force2, torque2 = joint:get_feedback( )
    force1, torque1, force2, torque2: vec3.
    Rfr: Joint feedback.

  • joint:set_param(param, float, [paramgroup])
    float = joint:get_param(param, [paramgroup])
    paramgroup: 1, 2, or 3 (defaults to 1).
    Rfr: Parameter Functions.

joint_null

joint_contact

Rfr: Contact.

joint_ball

  • joint = create_ball_joint(world)
    joint:set_anchor1(vec3)
    joint:set_anchor2(vec3)
    vec3 = joint:get_anchor1( )
    vec3 = joint:get_anchor2( )

joint_hinge

Rfr: Hinge.

  • joint = create_hinge_joint(world)
    joint:add_torque(float)
    joint:set_anchor1(vec3)
    joint:set_axis(vec3)
    vec3 = joint:get_anchor1( )
    vec3 = joint:get_anchor2( )
    vec3 = joint:get_axis( )
    float = joint:get_angle( )
    float = joint:get_angle_rate( )

joint_slider

Rfr: Slider.

  • joint = create_slider_joint(world)
    joint:add_force(float)
    joint:set_axis(vec3)
    vec3 = joint:get_axis( )
    float = joint:get_position( )
    float = joint:get_position_rate( )

joint_hinge2

Rfr: Hinge-2.

  • joint = create_hinge2_joint(world)
    joint:add_torques(float1, float2)
    joint:set_anchor1(vec3)
    joint:set_axis1(vec3)
    joint:set_axis2(vec3)
    vec3 = joint:get_anchor1( )
    vec3 = joint:get_anchor2( )
    vec3 = joint:get_axis1( )
    vec3 = joint:get_axis2( )
    float = joint:get_angle1( )
    float = joint:get_angle2( )
    float = joint:get_angle1_rate( )
    float = joint:get_angle2_rate( )

joint_universal

Rfr: Universal.

  • joint = create_universal_joint(world)
    joint:add_torques(float1, float2)
    joint:set_anchor1(vec3)
    joint:set_axis1(vec3)
    joint:set_axis2(vec3)
    vec3 = joint:get_anchor1( )
    vec3 = joint:get_anchor2( )
    vec3 = joint:get_axis1( )
    vec3 = joint:get_axis2( )
    float = joint:get_angle1( )
    float = joint:get_angle2( )
    float = joint:get_angle1_rate( )
    float = joint:get_angle2_rate( )

joint_pr

  • joint = create_pr_joint(world)
    joint:add_torque(float)
    joint:set_anchor(vec3)
    joint:set_axis1(vec3)
    joint:set_axis2(vec3)
    vec3 = joint:get_anchor( )
    vec3 = joint:get_axis1( )
    vec3 = joint:get_axis2( )
    float = joint:get_angle( )
    float = joint:get_position( )
    float = joint:get_angle_rate( )
    float = joint:get_position_rate( )

joint_pu

  • joint = create_pu_joint(world)
    joint:set_anchor(vec3)
    joint:set_axis1(vec3)
    joint:set_axis2(vec3)
    joint:set_axis3(vec3)
    vec3 = joint:get_anchor( )
    vec3 = joint:get_axis1( )
    vec3 = joint:get_axis2( )
    vec3 = joint:get_axis3( )
    float = joint:get_angle1( )
    float = joint:get_angle2( )
    float = joint:get_position( )
    float = joint:get_angle1_rate( )
    float = joint:get_angle2_rate( )
    float = joint:get_position_rate( )

joint_piston

Rfr: Piston.

  • joint = create_piston_joint(world)
    joint:add_force(float)
    joint:set_anchor1(vec3)
    joint:set_axis(vec3)
    vec3 = joint:get_anchor1( )
    vec3 = joint:get_anchor2( )
    vec3 = joint:get_axis( )
    float = joint:get_angle( )
    float = joint:get_position( )
    float = joint:get_angle_rate( )
    float = joint:get_position_rate( )

joint_fixed

Rfr: Fixed.

joint_amotor

joint_lmotor

Rfr: Linear Motor.

joint_plane2d

Rfr: Plane 2D.

  • joint = create_plane2d_joint(world)
    joint:set_x_param(param, float, [paramgroup])
    joint:set_y_param(param, float, [paramgroup])
    joint:set_angle_param(param, float, [paramgroup])
    See set/get_param.

joint_dball

  • joint = create_dball_joint(world)
    joint:set_anchor1(vec3)
    joint:set_anchor2(vec3)
    joint:set_distance(float)
    vec3 = joint:get_anchor1( )
    vec3 = joint:get_anchor2( )
    float = joint:get_distance( )

joint_dhinge

Rfr: Double Hinge.

  • joint = create_dhinge_joint(world)
    joint:set_axis(vec3)
    joint:set_anchor1(vec3)
    joint:set_anchor2(vec3)
    joint:set_distance(float)
    vec3 = joint:get_axis( )
    vec3 = joint:get_anchor1( )
    vec3 = joint:get_anchor2( )
    float = joint:get_distance( )

joint_transmission

Rfr: Transmission.

  • joint = create_transmission_joint(world)
    joint:set_mode(transmissionmode)
    joint:set_axis(vec3)
    joint:set_axis1(vec3)
    joint:set_axis2(vec3)
    joint:set_anchor1(vec3)
    joint:set_anchor2(vec3)
    joint:set_radius1(float)
    joint:set_radius2(float)
    joint:set_ratio(float)
    joint:set_backlash(float)
    transmissionmode = joint:get_mode( )
    vec3 = joint:get_axis( )
    vec3 = joint:get_axis1( )
    vec3 = joint:get_axis2( )
    vec3 = joint:get_anchor1( )
    vec3 = joint:get_anchor2( )
    float = joint:get_radius1( )
    float = joint:get_radius2( )
    float = joint:get_ratio( )
    float = joint:get_backlash( )
    float = joint:get_angle1( )
    float = joint:get_angle2( )
    vec3 = joint:get_contact_point1( )
    vec3 = joint:get_contact_point2( )

Collision detection

Objects of geom subtypes represent collision primitives, i.e. geometric objects of various shapes and sizes that may collide and interpenetrate during the simulation. Objects of space subtypes are containers for geoms, whose purpose is to speed up the collision detection process.

Collision detection between a pair of geoms is achieved by calling collide( ), which in case of collision returns a list of contact points, each represented by a contactpoint struct). The application should call this function for each pair of geoms that may potentially collide.

To speed up collision detection when several geoms are used, the best approach is to insert them in one or more collision spaces, set the near callback, anf finally call space_collide( ) or space_collide2( ). These functions will then invoke the callback only for those pairs of geoms and/or spaces that are likely to collide with each other, thus limiting the required number of collide( ) calls (that with this approach are to be made within the callback).

  • boolean, {contactpoint} = collide(geom1, geom2, maxcontacts, [collideflags])
    Returns true and the list of contact points (up to maxcontacts) if the two geom objects intersect. Otherwise returns false.

  • space_collide(space)
    Determines potential intersections between pairs and executes the near callback accordingly.
    The callback is executed only for pairs of potentially intersecting geoms (internally, the same logic as in the nearCallback( ) example from the ODE manual is performed for intersecting spaces with other spaces or geoms) and would typically call collide( ) to generate contact points between the two objects, and possibly create contact joints between the associated bodies.

  • space_collide2(space)
    space_collide2(obj1, obj2)
    obj1, obj2: geom or space.
    Determines potential intersections between pairs and executes the near callback accordingly.
    The callback is executed even when one or both the objects is a space (no internal logic is performed), and would typically call collide( ) to generate contact points between the two objects (if they are both geoms), or recursively call space_collide2( ) on them.

  • set_near_callback([func])
    Sets the callback to be invoked by calls of space_collide( ) or space_collide2( ).
    The callback is executed as func(obj1, obj2), where the arguments are two potentially colliding geom or space objects. If the callback is invoked by space_collide( ) calls, then obj1 and obj2 are both geoms, otherwise they may be either geoms or spaces.
    Any previously set callback is overridden. Passing func=nil unregisters the callback.

geom

Geometric objects can be of the following subtypes: sphere, box, plane, capsule, cylinder, ray, convex, trimesh, and heightfield.

Each subtype has its specialized contructors and methods, in addition to the following methods that are common to all subtypes:

  • geom:destroy( )
    geom:enable( )
    geom:disable( )
    boolean = geom:is_enabled( )
    boolean = space:is_space( )
    geom:set_body([body])
    geom:set_position(vec3)
    geom:set_rotation(mat3)
    geom:set_quaternion(quat)
    geom:set_offset_position(vec3)
    geom:set_offset_rotation(mat3)
    geom:set_offset_quaternion(quat)
    geom:set_offset_world_position(vec3)
    geom:set_offset_world_rotation(mat3)
    geom:set_offset_world_quaternion(quat)
    geom:set_category_bits(integer)
    geom:set_collide_bits(integer)
    geom:clear_offset(vec3)
    boolean = geom:is_offset( )
    geomtype = geom:get_type( )
    [space] = geom:get_space( )
    [body] = geom:get_body( )
    vec3 = geom:get_position( )
    mat3 = geom:get_rotation( )
    quat = geom:get_quaternion( )
    vec3 = geom:get_offset_position( )
    mat3 = geom:get_offset_rotation( )
    quat = geom:get_offset_quaternion( )
    integer = geom:get_category_bits( )
    integer = geom:get_collide_bits( )
    box3 = geom:get_aabb( )
    vec3 = geom:get_rel_point_pos(vec3)
    vec3 = geom:get_pos_rel_point(vec3)
    vec3 = geom:vector_to_world(vec3)
    vec3 = geom:vector_from_world(vec3)

geom_sphere

  • geom = create_sphere([space], radius)
    geom:set_radius(radius)
    radius = geom:get_radius( )
    float = geom:point_depth(vec3)

geom_box

Rfr: Box Class

  • geom = create_box([space], lengthx, lengthy, lengthz)
    geom:set_lengths(lengthx, lengthy, lengthz)
    lengthx, lengthy, lengthz = geom:get_lengths( )
    float = geom:point_depth(vec3)

geom_plane

  • geom = create_plane([space], a, b, c, d)
    geom:set(a, b, c, d)
    a, b, c, d = geom:get( )
    float = geom:point_depth(vec3)
    n, p, q = geom:basis( )
    The plane equation is ax + by + cz = d.
    n, p, q: vec3 (orthonormal basis where n is the plane normal (a, b, c) and q=n x p).

geom_capsule

  • geom = create_capsule([space], radius, length)
    geom:set(radius, length)
    radius, length = geom:get( )
    float = geom:point_depth(vec3)
    The capsule is composed of a z-aligned cylinder of the given radius and length, capped with two hemispheres of the same radius.

geom_cylinder

  • geom = create_cylinder([space], radius, length)
    geom:set(radius, length)
    radius, length = geom:get( )
    The cylinder is z-aligned.

geom_ray

Rfr: Ray Class

  • geom = create_ray([space], length)
    geom:set(vec3O, vec3dir)
    geom:set_length(length)
    geom:set_first_contact(boolean)
    geom:set_backface_cull(boolean)
    geom:set_closest_hit(boolean)
    vec3O, vec3dir = geom:get( )
    length = geom:get_length( )
    boolean = geom:get_first_contact( )
    boolean = geom:get_backface_cull( )
    boolean = geom:get_closest_hit( )
    O is the origin of the ray, and dir its direction.

geom_convex

  • geom = create_convex([space], planes, points, polygons)
    geom:set(planes, points, polygons)
    planes: {vec4}.
    points: {vec3}.
    polygons: {{integer}} (each polygon is a list of 0-based integer indices).

geom_trimesh

  • geom = create_trimesh([space], tmdata)
    geom:set_data(tmdata)
    geom:set_last_transform(mat4)
    tmdata = geom:get_data( )
    mat4 = geom:get_last_transform( )
    integer = geom:get_triangle_count( )
    vec3 = geom:get_triangle(index)
    vec3 = geom:get_point(index, floatu, floatv)
    index: integer, 0-based index.

The purpose of tmdata objects, described here, is to hold vertex data for triangle meshes.

  • tmdata = create_tmdata(ptype, positions, indices)
    Create a new tmdata object.
    ptype: 'float'|'double'.
    positions: binary string (packed vertex positions, length = 3 * sizeof(ptype) * numvertices).
    indices: binary string (packed element indices, length = 3 * sizeof('uint') * numtriangles).
    (Rfr: dTriMeshDataID)

  • tmdata:destroy( )
    tmdata:update( )
    tmdata:preprocess([build_concave_edges], [build_face_angles])
    build_concave_edges, build_face_angles: boolean.

geom_heightfield

The purpose of hfdata objects, described here, is to hold or generate heightfield data.

  • hfdata = create_hfdata(datatype, data, width, depth, nw, nd, scale, offset, thickness, wrap)
    hfdata = create_hfdata_with_callback(func, width, depth, nw, nd, scale, offset, thickness, wrap)
    Create a new hfdata object.
    datatype: 'uchar' | 'short' | 'float' | 'double'.
    data: binary string (packed height samples, length = nw * nd * sizeof(datatype)).
    width, height: float (dimensions of the heightfield, in world units).
    nw, nd: integer (dimensions of the heightfield, in samples).
    scale, offset: float (vertical scale and offset to apply to the raw height data).
    thickness: float (thickness of an AABB added below the lowest point).
    wrap: boolean (true to tile the heightfield infinitely).
    The second version, instead of passing predefined data, specifies a callback (func) which is invoked during the simulation as double=func(hfdata, x, z) and is expected to compute and return the height value for the point at integer coordinates (x, z), where x is along the width and z is along the height.
    (Rfr: dGeomHeightfieldDataBuild, dGeomHeightfieldDataBuildCallback).

  • hfdata:destroy( )
    hfdata:set_bounds(minheight, maxheight)
    minheight, maxheight: double.

space

Spaces can be of the following subtypes: simple, hash, quadtree, and sap.

Each subtype has its specialized contructors and methods, in addition to the following methods that are common to all subtypes:

  • space:destroy( )
    boolean = space:is_space( )
    space:set_cleanup(boolean)
    boolean = space:get_cleanup( )
    space:set_sublevel(integer)
    integer = space:set_sublevel( ) space:add(geom)
    space:remove(geom)
    boolean = space:query(geom)
    integer = space:get_num_geoms( )
    {geom} = space:get_geoms( )
    geomtype = space:get_type( )

space_simple

space_hash

  • space = create_hash_space([parentspace])
    space:set_levels(min, max)
    min, max = space:get_levels( )
    min, max: integer.

space_quadtree

  • space = create_quadtree_space([parentspace], center, extents, depth)
    center, extents: vec3.
    depth: integer.

space_sap

Mass functions

  • mass = mass([total], [center], [inertia])
    mass = mass_sphere(density, radius, [total])
    mass = mass_capsule(density, direction, radius, length, [total])
    mass = mass_cylinder(density, direction, radius, length, [total])
    mass = mass_box(density, lengthx, lengthy, lengthz, [total])
    mass = mass_trimesh(density, geom, [total])
    If total=true, the density parameter is interpreted as the total mass of the object.

  • boolean = mass_check(mass)
    boolean = mass:check( )
    mass = mass_adjust(mass, total)
    mass = mass:adjust(total)
    mass = mass_translate(mass, vec3)
    mass = mass:translate(vec3)
    mass = mass_rotate(mass, mat3)
    mass = mass:rotate(mat3)
    mass = mass_add(massa, massb)
    mass = massa:add(massb)

Math functions

Random functions

The following utilities are all based on Lua’s native math.random( ). To seed them, use math.randomseed( ).

  • number = random([m], [n])
    Same as math.random.

  • integer = randomi([n, m])
    Return a random integer in the range [n, m] (default: [0, 1]).

  • float = randomf([a, b])
    Return a random float in the range [a, b) (default: [0.0, 1.0)).

  • vec3 = randomvec3([a])
    vec4 = randomvec4([a])
    mat3 = randommat3([a])
    Return a vector or matrix with random components in the range [-a, a) (default: [-1.0, 1.0)).

  • vec3 = randomdir( )
    Returns a random unit direction vector.

  • mat3 = randomrot( )
    quat = randomquat( )
    Returns a random rotation.

  • float = randomsign( )
    Returns -1.0 or 1.0 with equal probability.

Time and tracing functions

  • trace_objects(boolean)
    Enable/disable tracing of objects creation and destruction (which by default is disabled).
    If enabled, a printf is generated whenever an object is created or deleted, indicating the object type and the value of its raw handle.

  • t = now( )
    Returns the current time in seconds.
    This is implemented with monotonic clock_gettime(3), if available, or with gettimeofday(3) otherwise.

  • dt = since(t)
    Returns the time in seconds elapsed since the time t, previously obtained with the now( ) function.

Data types

In this document, the float and integer types denote Lua numbers, while other types such as boolean, string, etc denote standard Lua types. The type number denotes a Lua number that may be either a float or an integer. Note that float here just means 'floating point' as opposed to 'integer', and does not imply single-precision (all numbers are actually represented internally using double-precision).

Angles are always expressed in radians.

Vectors, matrices, quaternions, and bounding boxes are by default represented as Lua arrays containing float elements (or arrays of arrays in the case of matrices). The meaning of the elements thus depends on their position, as detailed below, and there is no syntactic sugar such as v.x and v.y to access them (unless one enables GLMATH compatibility).

A few other ODE structs are represented as string-indexed Lua tables, also described below.

  • vec3 = {x, y, z}

  • vec4 = {x, y, z, w}

  • mat3 = {{a11, a12, a13}, {a21, a22, a23}, {a31, a32, a33}}

  • mat4 = {{a11, a12, a13, a14}, {a21, a22, a23, a24}, {a31, a32, a33, a34}, {a41, a42, a43, a44}}

  • quat = {w, x, y, z}
    w is the scalar part and (x, y, z) is the vector part.

  • box3 = {xmin, xmax, ymin, ymax, zmin, zmax}


Additional data structures:

  • mass = {
    total: float,
    center: vec3,
    inertia: mat3,
    } (Rfr: dMass)

  • contactpoint = {
    position: vec3,
    normal: vec3,
    depth: float,
    geom1: geom,
    geom2: geom,
    side1: integer,
    side2: integer,
    } (Rfr: dContactGeom)

  • surfaceparameters = {
    mu: float,
    mu2: float (opt.),
    rho: float (opt.),
    rho2: float (opt.),
    rhoN: float (opt.),
    bounce: float (opt.),
    bounce_vel: float (opt.),
    soft_erp: float (opt.),
    soft_cfm: float (opt.),
    motion1: float (opt.),
    motion2: float (opt.),
    motionN: float (opt.),
    slip1: float (opt.),
    slip2: float (opt.),
    approx1: boolean (opt., defaults to false),
    approx1_1: boolean (opt., defaults to false),
    approx1_2: boolean (opt., defaults to false),
    approx1_N: boolean (opt., defaults to false),
    } (Rfr: dSurfaceParameters)

GLMATH compatibility

As an option, it is possible to instruct MoonODE to return vectors, matrices, quaternions, and boxes as MoonGLMATH types, instead of returning them as plain tables (which is the default). [4]

More precisely, when this functionality is enabled functions and methods will return values of the MoonGLMATH types vec3, vec4, mat3, etc. instead of the MoonODE types with the same names.

(Notice that for function arguments nothing changes, since the above MoonGLMATH types are compatible with the corresponding plain tables used by default, thus they can be used as function arguments in any case).

Use the following functions to control GLMATH compatibility:

  • glmath_compat(boolean)
    Enables/disables GLMATH compatibility (which by default is disabled).
    Enabling this functionality requires MoonGLMATH to be installed.

  • boolean = is_glmath_compat( )
    Returns true if GLMATH compatibility is enabled.

Data handling

This section describes additional utilities that can be used to encode data from Lua variables to binary strings and viceversa.

  • val1, …​, valN = flatten(table)
    Flattens out the given table and returns the terminal elements in the order they are found.
    Similar to Lua’s table.unpack( ), but it also unpacks any nested table. Only the array part of the table and of nested tables is considered.

  • {val1, …​, valN} = flatten_table(table)
    Same as flatten( ), but returns the values in a flat table. Unlike flatten( ), this function can be used also with very large tables.

  • size = sizeof(type)
    Returns the size in bytes of the given type.

  • data = pack(type, val1, …​, valN)
    data = pack(type, table)
    Packs the numbers val1, …​, valN, encoding them according to the given type, and returns the resulting binary string.
    The values may also be passed in a (possibly nested) table. Only the array part of the table (and of nested tables) is considered.

  • {val1, …​, valN} = unpack(type, data)
    Unpacks the binary string data, interpreting it as a sequence of values of the given type, and returns the extracted values in a flat table.
    The length of data must be a multiple of sizeof(type).

type: data types (and their corresponding C99 types)
Values: 'char' (int8_t), 'uchar' (uint8_t), 'short' (int16_t), 'ushort' (uint16_t), 'int' (int32_t), 'uint' (uint32_t), 'long' (int64_t), 'ulong' (uint64_t), 'float' (float), 'double' (double).

Enums

ODE enums are mapped in MoonODE to sets of string literals (as is customary in Lua).

Below are listed the enum types that are used in different places, together with the values they admit. Other enum types are described together with the functions that use them, either as parameters or as return values.

amotormode: 'user', 'euler'.

axesorder: 'xyz', 'xzy', 'yxz', 'yzx', 'zxy', 'zyx'.

direction: 'x', 'y', 'z'.

geomtype: 'sphere', 'box', 'capsule', 'cylinder', 'plane', 'ray', 'convex', 'geom transform', 'trimesh', 'heightfield', 'simple space', 'hash space', 'sap space', 'quad tree space', 'user1', 'user2', 'user3', 'user4'.

jointtype: 'none', 'ball', 'hinge', 'slider', 'contact', 'universal', 'hinge2', 'fixed', 'null', 'amotor', 'lmotor', 'plane2d', 'pr', 'pu', 'piston', 'dball', 'dhinge', 'transmission'.

param: 'lo stop', 'hi stop', 'vel', 'lo vel', 'hi vel', 'fmax', 'fudge factor', 'bounce', 'cfm', 'stop erp', 'stop cfm', 'suspension erp', 'suspension cfm', 'erp'.

relativeorientation: 'global frame', 'first body', 'second body'.

transmissionmode: 'parallel axes', 'intersecting axes', 'chain drive'.

Flags

Flags in MoonODE functions and structs are always represented as plain integers, and encoded in the same way as the corresponding flags in the ODE C API.

The ode table contains the XXX values, renamed as ode.XXX (e.g. ode.CONTACTS_UNIMPORTANT, etc.).

For each flags type (see the list below), a utility function is also available to map an integer code to a list of string literals, each corresponding to an individual bit set in the code, and viceversa to encode an integer value from the individual bits given as a list of string literals. The generic definition of such functions is the following, where xxxflags stands for collideflags, etc:

  • code = xxxflags(s1, s2, …​)
    s1, s2, …​ = xxxflags(code)
    Maps the integer code to/from the list of string values s1, s2, …​.

collideflags: Values: 'contacts unimportant'.


1. This manual is written in AsciiDoc, rendered with AsciiDoctor and a CSS from the AsciiDoctor Stylesheet Factory.
2. Objects are anchored to the Lua registry at their creation, so even if the script does not have references to an object, a reference always exists on the registry and this prevents the GC to collect it.
3. It is good practice to not leave invalid references to objects around, because they prevent the GC to collect the memory associated with the userdata.
4. MoonGLMATH types are convenient because they support operators and synctatic sugar, but I chose not to impose their use because one may want to use an alternative math library, or none at all.