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.
001
unit
JointScene;
002
003
{$i options.inc}
004
005
interface
006
007
uses
008
NanoVG, Chipmunk2D, ChipmunkObjs, PhysicsTools;
009
010
{ TJointScene }
011
012
type
013
TJointScene =
class
(TPhysicsScene)
014
private
015
FHits:
array
[
0..50
]
of
TVect;
016
FHitTimes:
array
[
0..50
]
of
Double;
017
FHitIndex: Integer;
018
procedure
HandleHits(arb: cpArbiter);
019
protected
020
procedure
Load;
override
;
021
procedure
Render(Width, Height: Integer;
const
Time: Double);
override
;
022
end
;
023
024
implementation
025
026
const
027
ballType = TCollisionType(
1
);
028
weightType = TCollisionType(
2
);
029
boxType = TCollisionType(
3
);
030
031
procedure
TJointScene
.
HandleHits(arb: cpArbiter);
032
var
033
Points: cpContactPointSetStruct;
034
begin
035
Points := cpArbiterGetContactPointSet(arb);
036
FHits[FHitIndex] := Points
.
points[
0
].pointA;
037
FHitTimes[FHitIndex] := Time;
038
FHitIndex := (FHitIndex +
1
)
mod
(High(FHits) +
1
);
039
end
;
040
041
procedure
WeightsCollide(arb: cpArbiter; space: cpSpace; userData: cpDataPointer); cdecl;
042
var
043
Scene: TJointScene absolute userData;
044
begin
045
if
cpArbiterIsFirstContact(arb) <> cpFalse
then
046
Scene
.
HandleHits(arb);
047
end
;
048
049
procedure
TJointScene
.
Load;
050
var
051
Handler: TCollisionHandler;
052
B, C, K: TBody;
053
J: TJoint;
054
M: TFloat;
055
I: Integer;
056
begin
057
inherited
Load;
058
Space
.
Gravity := Vect(
0
,
1000
);
059
Space
.
SleepTimeThreshold :=
0.25
;
060
Space
.
IdleSpeedThreshold :=
8
;
061
Space
.
Damping :=
0.8
;
062
063
Handler := Space
.
AddCollisionHandler(weightType, weightType);
064
Handler
.
postSolveFunc := WeightsCollide;
065
Handler
.
userData := Self;
066
067
Handler := Space
.
AddCollisionHandler(weightType, ballType);
068
Handler
.
postSolveFunc := WeightsCollide;
069
Handler
.
userData := Self;
070
071
for
I :=
0
to
20
do
072
begin
073
C := Space
.
NewBody;
074
with
C
.
NewCircle(
30
)
do
075
begin
076
Friction :=
0.7
;
077
Elasticity :=
0.75
;
078
Density :=
2
;
079
CollisionType := ballType;
080
end
;
081
C
.
Position := Vect(Random *
1800
+
100
, Random * -
500
-
200
);
082
end
;
083
084
for
I :=
1
to
7
do
085
begin
086
B := Space
.
NewBody;
087
with
B
.
NewBox(
80
, I *
25
)
do
088
begin
089
Friction :=
0.7
;
090
Elasticity :=
1
;
091
Density :=
5
;
092
CollisionType := weightType;
093
end
;
094
B
.
Position := Vect(I *
200
+
150
,
600
);
095
K := Space
.
NewKinematicBody;
096
with
K
.
NewBox(
40
,
40
)
do
097
begin
098
Friction :=
0
;
099
Elasticity :=
0
;
100
CollisionType := boxType;
101
end
;
102
K
.
Position := Vect(I *
200
+
150
,
400
);
103
M := cpBodyGetMass(B) /
100000
;
104
J := Space
.
NewDampedSpring(K, B, Vect(
0
,
0
), Vect(
0
, -I *
25
/
2
),
100
,
1000000
* M,
0.1
);
105
J
.
CollideBodies := True;
106
end
;
107
108
GenerateStudioWalls;
109
AllowGrab := True;
110
end
;
111
112
procedure
TJointScene
.
Render(Width, Height: Integer;
const
Time: Double);
113
const
114
Title =
'Joint & Collision Test'
#
10
+
115
'This program tests spring joints and precise collision detection of contact points'
;
116
RingTime =
3
;
117
var
118
Fade: Double;
119
I, J: Integer;
120
begin
121
inherited
Render(Width, Height, Time);
122
DrawPhysics;
123
nvgStrokeWidth(Ctx,
3
);
124
for
I := Low(FHits)
to
High(FHits)
do
125
if
Time - FHitTimes[I] < RingTime
then
126
begin
127
Fade :=
1
- (Time - FHitTimes[I]) / RingTime;
128
Fade := Fade * Fade * Fade * Fade * Fade;
129
for
J :=
0
to
2
do
130
begin
131
nvgStrokeColor(Ctx, nvgRGBAf(
1
,
0.5
,
1
, Fade));
132
nvgBeginPath(Ctx);
133
nvgCircle(Ctx, FHits[I].x, FHits[I].y,
50
* Fade);
134
nvgStroke(Ctx);
135
Fade := Fade * Fade;
136
end
;
137
end
;
138
nvgResetTransform(Ctx);
139
DrawSceneInfo(Title);
140
end
;
141
142
end
.