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