Joints and Body Collisions
For the past few days I've been working on writing and testing an object oriented encapsulation of the open source Chipmunk2D physics library. This compliments my existing SDL and NanoVG bindings for Free Pascal. Below is a short video clip of my program to test precise detection of body collision points as well as the use of spring joints
Video
The program below tests the precise detection of collision points against a class of physics bodies. Receding pink circles are drawn at points where the bodies collide. Dynamic bodies (those that are not anchored) can be moved with the mouse and a green arrow indicates the force being applied to the body being grabbed. All the graphics are drawn in real time using OpenGLESv2 with the extremely fast NanoVG vector graphics library.
Video: Collision Test
Source Code
The following is a listing of the Pascal unit responsible for the test program above. The SDL, NanoVG, and Chipmunk2D code were built by me as static libraries and link inside of programs like this test. I have written well formatted Pascal units handling both the C interface to these projects. Additionally, I am in the process of writing quite sensible object orient encapsulations of each of these units, making it easier to discover and use these libraries in Pascal.
If you are interested in helping me write or test the Pascal encapsulations, message me and we can use Discord to share ideas.
unit JointScene;
{$i options.inc}
interface
uses
NanoVG, Chipmunk2D, ChipmunkObjs, PhysicsTools;
{ TJointScene }
type
TJointScene = class(TPhysicsScene)
private
FHits: array[0..50] of TVect;
FHitTimes: array[0..50] of Double;
FHitIndex: Integer;
procedure HandleHits(arb: cpArbiter);
protected
procedure Load; override;
procedure Render(Width, Height: Integer; const Time: Double); override;
end;
implementation
const
ballType = TCollisionType(1);
weightType = TCollisionType(2);
boxType = TCollisionType(3);
procedure TJointScene.HandleHits(arb: cpArbiter);
var
Points: cpContactPointSetStruct;
begin
Points := cpArbiterGetContactPointSet(arb);
FHits[FHitIndex] := Points.points[0].pointA;
FHitTimes[FHitIndex] := Time;
FHitIndex := (FHitIndex + 1) mod (High(FHits) + 1);
end;
procedure WeightsCollide(arb: cpArbiter; space: cpSpace; userData: cpDataPointer); cdecl;
var
Scene: TJointScene absolute userData;
begin
if cpArbiterIsFirstContact(arb) <> cpFalse then
Scene.HandleHits(arb);
end;
procedure TJointScene.Load;
var
Handler: TCollisionHandler;
B, C, K: TBody;
J: TJoint;
M: TFloat;
I: Integer;
begin
inherited Load;
Space.Gravity := Vect(0, 1000);
Space.SleepTimeThreshold := 0.25;
Space.IdleSpeedThreshold := 8;
Space.Damping := 0.8;
Handler := Space.AddCollisionHandler(weightType, weightType);
Handler.postSolveFunc := WeightsCollide;
Handler.userData := Self;
Handler := Space.AddCollisionHandler(weightType, ballType);
Handler.postSolveFunc := WeightsCollide;
Handler.userData := Self;
for I := 0 to 20 do
begin
C := Space.NewBody;
with C.NewCircle(30) do
begin
Friction := 0.7;
Elasticity := 0.75;
Density := 2;
CollisionType := ballType;
end;
C.Position := Vect(Random * 1800 + 100, Random * -500 - 200);
end;
for I := 1 to 7 do
begin
B := Space.NewBody;
with B.NewBox(80, I * 25) do
begin
Friction := 0.7;
Elasticity := 1;
Density := 5;
CollisionType := weightType;
end;
B.Position := Vect(I * 200 + 150, 600);
K := Space.NewKinematicBody;
with K.NewBox(40, 40) do
begin
Friction := 0;
Elasticity := 0;
CollisionType := boxType;
end;
K.Position := Vect(I * 200 + 150, 400);
M := cpBodyGetMass(B) / 100000;
J := Space.NewDampedSpring(K, B, Vect(0, 0), Vect(0, -I * 25 / 2), 100, 1000000 * M, 0.1);
J.CollideBodies := True;
end;
GenerateStudioWalls;
AllowGrab := True;
end;
procedure TJointScene.Render(Width, Height: Integer; const Time: Double);
const
Title = 'Joint & Collision Test'#10 +
'This program tests spring joints and precise collision detection of contact points';
RingTime = 3;
var
Fade: Double;
I, J: Integer;
begin
inherited Render(Width, Height, Time);
DrawPhysics;
nvgStrokeWidth(Ctx, 3);
for I := Low(FHits) to High(FHits) do
if Time - FHitTimes[I] < RingTime then
begin
Fade := 1 - (Time - FHitTimes[I]) / RingTime;
Fade := Fade * Fade * Fade * Fade * Fade;
for J := 0 to 2 do
begin
nvgStrokeColor(Ctx, nvgRGBAf(1, 0.5, 1, Fade));
nvgBeginPath(Ctx);
nvgCircle(Ctx, FHits[I].x, FHits[I].y, 50 * Fade);
nvgStroke(Ctx);
Fade := Fade * Fade;
end;
end;
nvgResetTransform(Ctx);
DrawSceneInfo(Title);
end;
end.