1 /*
2 Copyright (c) 2013-2014 Timur Gafarov 
3 
4 Boost Software License - Version 1.0 - August 17th, 2003
5 
6 Permission is hereby granted, free of charge, to any person or organization
7 obtaining a copy of the software and accompanying documentation covered by
8 this license (the "Software") to use, reproduce, display, distribute,
9 execute, and transmit the Software, and to prepare derivative works of the
10 Software, and to permit third-parties to whom the Software is furnished to
11 do so, all subject to the following:
12 
13 The copyright notices in the Software and this entire statement, including
14 the above license grant, this restriction and the following disclaimer,
15 must be included in all copies of the Software, in whole or in part, and
16 all derivative works of the Software, unless such copies or derivative
17 works are solely in the form of machine-executable object code generated by
18 a source language processor.
19 
20 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
21 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
22 FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
23 SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
24 FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
25 ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
26 DEALINGS IN THE SOFTWARE.
27 */
28 
29 module dmech.rigidbody;
30 
31 import std.math;
32 
33 import dlib.math.vector;
34 import dlib.math.matrix;
35 import dlib.math.quaternion;
36 import dlib.math.affine;
37 import dlib.math.utils;
38 
39 import dmech.shape;
40 import dmech.contact;
41 
42 class RigidBody
43 {
44     Vector3f position;
45     Quaternionf orientation;
46 
47     Vector3f linearVelocity;
48     Vector3f angularVelocity;
49 
50     Vector3f pseudoLinearVelocity;
51     Vector3f pseudoAngularVelocity;
52     
53     Vector3f linearAcceleration;
54     Vector3f angularAcceleration;
55 
56     float mass;
57     float invMass;
58 
59     Matrix3x3f inertiaTensor;
60     Matrix3x3f invInertiaTensor; //TODO: rename to invInertiaWorld
61 
62     Vector3f centerOfMass;
63 
64     Vector3f forceAccumulator;
65     Vector3f torqueAccumulator;
66 
67     float bounce;
68     float friction;
69 
70     bool useGravity = true;
71     bool enableRotation = true;
72 
73     ShapeComponent[] shapes;
74 
75     bool dynamic;
76 
77     float damping = 0.5f;
78     float stopThreshold = 0.15f; //0.15f
79     float stopThresholdPV = 0.0f; //0.01f
80 
81     bool useOwnGravity = false;
82     Vector3f gravity = Vector3f(0, 0, 0);
83 
84     alias void delegate(RigidBody, Contact) contactFunc;
85     contactFunc[] onNewContact;
86 
87     void contactEvent(Contact c)
88     {
89         foreach(f; onNewContact)
90         {
91             f(this, c);
92         }
93     }
94 
95     bool raycastable = true;
96 
97     this()
98     {
99         position = Vector3f(0.0f, 0.0f, 0.0f);
100         orientation = Quaternionf(0.0f, 0.0f, 0.0f, 1.0f);
101 
102         linearVelocity = Vector3f(0.0f, 0.0f, 0.0f);
103         angularVelocity = Vector3f(0.0f, 0.0f, 0.0f);
104 
105         pseudoLinearVelocity = Vector3f(0.0f, 0.0f, 0.0f);
106         pseudoAngularVelocity = Vector3f(0.0f, 0.0f, 0.0f);
107     
108         linearAcceleration = Vector3f(0.0f, 0.0f, 0.0f);
109         angularAcceleration = Vector3f(0.0f, 0.0f, 0.0f);
110 
111         mass = 1.0f;
112         invMass = 1.0f;
113 
114         inertiaTensor = matrixf(
115             mass, 0, 0,
116             0, mass, 0,
117             0, 0, mass
118         );
119         invInertiaTensor = matrixf(
120             0, 0, 0,
121             0, 0, 0,
122             0, 0, 0
123         );
124 
125         centerOfMass = Vector3f(0.0f, 0.0f, 0.0f);
126 
127         forceAccumulator = Vector3f(0.0f, 0.0f, 0.0f);
128         torqueAccumulator = Vector3f(0.0f, 0.0f, 0.0f);
129 
130         bounce = 0.0f;
131         friction = 0.9f;
132 
133         dynamic = true;
134     }
135 
136     Matrix4x4f transformation()
137     {
138         Matrix4x4f t;
139         t = translationMatrix(position);
140         t *= orientation.toMatrix4x4();
141         return t;
142     }
143 
144     void addShapeComponent(ShapeComponent shape)
145     {
146         shape.transformation = transformation() * translationMatrix(shape.centroid);
147         shapes ~= shape;
148 
149         if (!dynamic)
150             return;
151 
152         centerOfMass = Vector3f(0, 0, 0);
153         float m = 0.0f;
154 
155         foreach (sh; shapes)
156         {
157             m += sh.mass;
158             centerOfMass += sh.mass * sh.centroid;
159         }
160 
161         float invm = 1.0f / m;
162         centerOfMass *= invm;
163 
164         mass += shape.mass;
165         invMass = 1.0f / mass;
166 
167         // Compute inertia tensor using Huygens-Steiner theorem
168         inertiaTensor = Matrix3x3f.zero;
169         foreach (sh; shapes)
170         {
171             Vector3f r = centerOfMass - sh.centroid;
172             inertiaTensor += 
173                 sh.inertiaTensor(sh.mass) + 
174                 (Matrix3x3f.identity * dot(r, r) - tensorProduct(r, r)) * sh.mass;
175         }
176 
177         invInertiaTensor = inertiaTensor.inverse;
178     }
179 
180     void integrateForces(float dt)
181     {
182         if (!dynamic)
183             return;
184 
185         linearAcceleration = forceAccumulator * invMass;
186         if (enableRotation)
187             angularAcceleration = torqueAccumulator * invInertiaTensor;
188 
189         linearVelocity += linearAcceleration * dt;
190         if (enableRotation)
191             angularVelocity += angularAcceleration * dt;
192     }
193 
194     void integrateVelocities(float dt)
195     {
196         if (!dynamic)
197             return;
198 
199         float d = clamp(1.0f - dt * damping, 0.0f, 1.0f);
200         linearVelocity *= d;
201         angularVelocity *= d;
202         
203         if (linearVelocity.length > stopThreshold)
204         {
205             position += linearVelocity * dt;
206         }
207 
208         if (enableRotation)
209         if (angularVelocity.length > stopThreshold)
210         {
211             orientation += 0.5f * Quaternionf(angularVelocity, 0.0f) * orientation * dt;
212             orientation.normalize();
213         }
214     }
215 
216     void integratePseudoVelocities(float dt)
217     {
218         if (!dynamic)
219             return;
220 
221         float d = clamp(1.0f - dt * damping, 0.0f, 1.0f);
222 
223         pseudoLinearVelocity *= d;
224         pseudoAngularVelocity *= d;
225 
226         if (pseudoLinearVelocity.length > stopThresholdPV)
227         {
228             position += pseudoLinearVelocity;
229         }
230 
231         if (enableRotation)
232         if (pseudoAngularVelocity.length > stopThresholdPV)
233         {
234             orientation += 0.5f * Quaternionf(pseudoAngularVelocity, 0.0f) * orientation;
235             orientation.normalize();
236         }
237 
238         pseudoLinearVelocity = Vector3f(0.0f, 0.0f, 0.0f);
239         pseudoAngularVelocity = Vector3f(0.0f, 0.0f, 0.0f);
240     }
241 
242     @property Vector3f worldCenterOfMass()
243     {
244         return position + orientation.rotate(centerOfMass);
245     }
246 
247     void updateInertia()
248     {
249         if (dynamic)
250         {
251             auto rot = orientation.toMatrix3x3;
252             invInertiaTensor = (rot * inertiaTensor * rot.transposed).inverse;
253         }
254     }
255 
256     void updateShapeComponents()
257     {
258         foreach (sh; shapes)
259         {
260             sh.transformation = transformation() * translationMatrix(sh.centroid);
261         }
262     }
263 
264     void resetForces()
265     {
266         forceAccumulator = Vector3f(0, 0, 0);
267         torqueAccumulator = Vector3f(0, 0, 0);
268     }
269 
270     void applyForce(Vector3f force)
271     {
272         if (!dynamic)
273             return;
274 
275         forceAccumulator += force;
276     }
277 
278     void applyTorque(Vector3f torque)
279     {
280         if (!dynamic)
281             return;
282 
283         torqueAccumulator += torque;
284     }
285 
286     void applyImpulse(Vector3f impulse, Vector3f point)
287     {
288         if (!dynamic)
289             return;
290 
291         linearVelocity += impulse * invMass;
292         Vector3f angularImpulse = cross(point - worldCenterOfMass, impulse);
293         angularVelocity += angularImpulse * invInertiaTensor;
294     }
295 
296     void applyPseudoImpulse(Vector3f impulse, Vector3f point)
297     {
298         if (!dynamic)
299             return;
300 
301         pseudoLinearVelocity += impulse * invMass;
302         Vector3f angularImpulse = cross(point - worldCenterOfMass, impulse);
303         pseudoAngularVelocity += angularImpulse * invInertiaTensor;
304     }
305     
306     @property float linearKineticEnergy()
307     {
308         if (!dynamic)
309             return float.infinity;
310 
311         // 0.5 * m * v^2
312         float v = linearVelocity.length;
313         return 0.5f * mass * v * v;
314     }
315     
316     @property float angularKineticEnergy()
317     {
318         if (!dynamic)
319             return float.infinity;
320 
321         // 0.5 * w * I * w
322         Vector3f w = angularVelocity;
323         return 0.5f * dot(w * inertiaTensor, w);
324     }
325 }
326