bodge-chipmunk

2018-07-11

Build Status Build status

BODGE-CHIPMUNK

Thin wrapper over chipmunk 2d physics library.

Requirements

  • ASDF/Quicklisp
  • x86_64/i686 GNU/Linux, macOS or Windows

Loading

(ql:quickload '(chipmunk-blob bodge-chipmunk))

Usage

Loading compiled foreign library

Unlike many other wrappers, this one doesn't load shared library into the image upon system loading. That what chipmunk-blob system is for. It contains required compiled foreign library and loads it for you, that's why you need to load it before using bodge-chipmunk functions. You, of course, can load wrapper without chipmunk-blob by invoking

(ql:quickload :bodge-chipmunk)

Then let cffi know where to find your own version of compiled foreign library and then call (cffi:load-foreign-library 'cp:libchipmunk) manually yourself.

Interface

All wrapped functions can be found in %chipmunk package. On the other hand, chipmunk package contains minimally lispified wrappers and utility functions.

Example

Here's how chipmunk example from the manual translates to Common Lisp using this library.

C variant

C source

#include <stdio.h>
#include <chipmunk.h>

int main(void){
  // cpVect is a 2D vector and cpv() is a shortcut for initializing them.
  cpVect gravity = cpv(0, -100);

  // Create an empty space.
  cpSpace *space = cpSpaceNew();
  cpSpaceSetGravity(space, gravity);

  // Add a static line segment shape for the ground.
  // We'll make it slightly tilted so the ball will roll off.
  // We attach it to a static body to tell Chipmunk it shouldn't be movable.
  cpShape *ground = cpSegmentShapeNew(cpSpaceGetStaticBody(space), cpv(-20, 5), cpv(20, -5), 0);
  cpShapeSetFriction(ground, 1);
  cpSpaceAddShape(space, ground);

  // Now let's make a ball that falls onto the line and rolls off.
  // First we need to make a cpBody to hold the physical properties of the object.
  // These include the mass, position, velocity, angle, etc. of the object.
  // Then we attach collision shapes to the cpBody to give it a size and shape.

  cpFloat radius = 5;
  cpFloat mass = 1;

  // The moment of inertia is like mass for rotation
  // Use the cpMomentFor*() functions to help you approximate it.
  cpFloat moment = cpMomentForCircle(mass, 0, radius, cpvzero);

  // The cpSpaceAdd*() functions return the thing that you are adding.
  // It's convenient to create and add an object in one line.
  cpBody *ballBody = cpSpaceAddBody(space, cpBodyNew(mass, moment));
  cpBodySetPos(ballBody, cpv(0, 15));

  // Now we create the collision shape for the ball.
  // You can create multiple collision shapes that point to the same body.
  // They will all be attached to the body and move around to follow it.
  cpShape *ballShape = cpSpaceAddShape(space, cpCircleShapeNew(ballBody, radius, cpvzero));
  cpShapeSetFriction(ballShape, 0.7);

  // Now that it's all set up, we simulate all the objects in the space by
  // stepping forward through time in small increments called steps.
  // It is *highly* recommended to use a fixed size time step.
  cpFloat timeStep = 1.0/60.0;
  for(cpFloat time = 0; time < 2; time += timeStep){
    cpVect pos = cpBodyGetPosition(ballBody);
    cpVect vel = cpBodyGetVelocity(ballBody);
    printf(
      "Time is %5.2f. ballBody is at (%5.2f, %5.2f). It's velocity is (%5.2f, %5.2f)\n",
      time, pos.x, pos.y, vel.x, vel.y
    );

    cpSpaceStep(space, timeStep);
  }

  // Clean up our objects and exit!
  cpShapeFree(ballShape);
  cpBodyFree(ballBody);
  cpShapeFree(ground);
  cpSpaceFree(space);

  return 0;
}

Common Lisp translation

Note: I tried to translate C into CL as close as possible to the original, so what you see below is totally not how idimoatic CL is written.

(cl:defpackage :chipmunk-example
  (:use :cl :claw)
  (:export main))
(cl:in-package :chipmunk-example)

(defun main ()
  (with-float-traps-masked ()
    ;; cpVect is a 2D vector and cpv() is a shortcut for initializing them.
    (with-free (gravity (cp:v 0d0 -100d0))
      ;; Create an empty space.
      (let ((space (%cp:space-new)))
        (%cp:space-set-gravity space gravity)
        ;; Add a static line segment shape for the ground.
        ;; We'll make it slightly tilted so the ball will roll off.
        ;; We attach it to a static body to tell Chipmunk it shouldn't be movable.
        (with-many-free ((a (cp:v -20d0 5d0))
                         (b (cp:v 20d0 -5d0)))
          (let ((ground (%cp:segment-shape-new (%cp:space-get-static-body space) a b 0d0)))
            (%cp:shape-set-friction ground 1d0)
            (%cp:space-add-shape space ground)
            ;; Now let's make a ball that falls onto the line and rolls off.
            ;; First we need to make a cpBody to hold the physical properties of the object.
            ;; These include the mass, position, velocity, angle, etc. of the object.
            ;; Then we attach collision shapes to the cpBody to give it a size and shape.
            (let* ((radius 5d0)
                   (mass 1d0)
                   ;; The moment of inertia is like mass for rotation
                   ;; Use the cpMomentFor*() functions to help you approximate it.
                   (moment (%cp:moment-for-circle mass 0d0 radius cp:+vzero+))
                   ;; The cpSpaceAdd*() functions return the thing that you are adding.
                   ;; It's convenient to create and add an object in one line.
                   (ball-body (%cp:space-add-body space (%cp:body-new mass moment))))
              (with-free (position (cp:v 0d0 15d0))
                (%cp:body-set-position ball-body position))
              ;; Now we create the collision shape for the ball.
              ;; You can create multiple collision shapes that point to the same body.
              ;; They will all be attached to the body and move around to follow it.
              (let ((ball-shape (%cp:space-add-shape space
                                                     (%cp:circle-shape-new ball-body radius cp:+vzero+))))
                (%cp:shape-set-friction ball-shape 0.7d0)
                ;; Now that it's all set up, we simulate all the objects in the space by
                ;; stepping forward through time in small increments called steps.
                ;; It is *highly* recommended to use a fixed size time step.
                (let ((time-step (float 1/60 0d0)))
                  (loop for time = 0 then (incf time time-step) while (< time 2)
                        do (c-with ((pos %cp:vect)
                                   (vel %cp:vect))
                             (%cp:body-get-position pos ball-body)
                             (%cp:body-get-velocity vel ball-body)
                             (format t "Time is ~5,2F. ball-body is at (~5,2F ~5,2F)). It's velocity is (~5,2F, ~5,2F)~&"
                                     time (pos :x) (pos :y) (vel :x) (vel :y))
                             (%cp:space-step space time-step))))
                ;; Clean up our objects and exit!
                (%cp:shape-free ball-shape)
                (%cp:body-free ball-body)
                (%cp:shape-free ground)
                (%cp:space-free space)))))))))

Copy and paste code above into your favorite REPL after loading bodge-chipmunk and invoke

(chipmunk-example:main)
Author
Pavel Korolev
License
MIT