1 /* 2 Copyright (c) 2019-2020 Timur Gafarov 3 Boost Software License - Version 1.0 - August 17th, 2003 4 Permission is hereby granted, free of charge, to any person or organization 5 obtaining a copy of the software and accompanying documentation covered by 6 this license (the "Software") to use, reproduce, display, distribute, 7 execute, and transmit the Software, and to prepare derivative works of the 8 Software, and to permit third-parties to whom the Software is furnished to 9 do so, all subject to the following: 10 The copyright notices in the Software and this entire statement, including 11 the above license grant, this restriction and the following disclaimer, 12 must be included in all copies of the Software, in whole or in part, and 13 all derivative works of the Software, unless such copies or derivative 14 works are solely in the form of machine-executable object code generated by 15 a source language processor. 16 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT 19 SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE 20 FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, 21 ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 22 DEALINGS IN THE SOFTWARE. 23 */ 24 module dagon.ext.newton; 25 26 import std.stdio; 27 import std..string; 28 import std.conv; 29 import dlib.core.ownership; 30 import dlib.core.memory; 31 import dlib.math.vector; 32 import dlib.math.matrix; 33 import dlib.math.transformation; 34 import dlib.math.quaternion; 35 import dlib.math.utils; 36 import dagon.core.event; 37 import dagon.core.time; 38 import dagon.graphics.entity; 39 public import bindbc.newton; 40 41 extern(C) nothrow @nogc void newtonBodyForceCallback( 42 const NewtonBody* nbody, 43 dFloat timestep, 44 int threadIndex) 45 { 46 NewtonRigidBody b = cast(NewtonRigidBody)NewtonBodyGetUserData(nbody); 47 if (b) 48 { 49 Vector3f gravityForce = b.gravity * b.mass; 50 NewtonBodyAddForce(nbody, gravityForce.arrayof.ptr); 51 NewtonBodyAddForce(nbody, b.force.arrayof.ptr); 52 NewtonBodyAddTorque(nbody, b.torque.arrayof.ptr); 53 b.force = Vector3f(0.0f, 0.0f, 0.0f); 54 b.torque = Vector3f(0.0f, 0.0f, 0.0f); 55 } 56 } 57 58 extern(C) dFloat newtonWorldRayFilterCallback( 59 const NewtonBody* nbody, 60 const NewtonCollision* shapeHit, 61 const dFloat* hitContact, 62 const dFloat* hitNormal, 63 dLong collisionID, 64 void* userData, 65 dFloat intersectParam) 66 { 67 NewtonRaycaster raycaster = cast(NewtonRaycaster)userData; 68 NewtonRigidBody b = cast(NewtonRigidBody)NewtonBodyGetUserData(nbody); 69 if (raycaster && b) 70 { 71 Vector3f p = Vector3f(hitContact[0], hitContact[1], hitContact[2]); 72 Vector3f n = Vector3f(hitNormal[0], hitNormal[1], hitNormal[2]); 73 raycaster.onRayHit(b, p, n); 74 } 75 return 1.0f; 76 } 77 78 extern(C) uint newtonWorldRayPrefilterCallback( 79 const NewtonBody* nbody, 80 const NewtonCollision* collision, 81 void* userData) 82 { 83 return 1; 84 } 85 86 interface NewtonRaycaster 87 { 88 void onRayHit(NewtonRigidBody nbody, Vector3f hitPoint, Vector3f hitNormal); 89 } 90 91 class NewtonPhysicsWorld: Owner 92 { 93 NewtonWorld* newtonWorld; 94 int defaultGroupId; 95 int kinematicGroupId; 96 97 this(Owner o) 98 { 99 super(o); 100 newtonWorld = NewtonCreate(); 101 defaultGroupId = NewtonMaterialGetDefaultGroupID(newtonWorld); 102 kinematicGroupId = createGroupId(); 103 NewtonMaterialSetDefaultElasticity(newtonWorld, defaultGroupId, kinematicGroupId, 0.0f); 104 } 105 106 int createGroupId() 107 { 108 return NewtonMaterialCreateGroupID(newtonWorld); 109 } 110 111 void loadPlugins(string dir) 112 { 113 NewtonLoadPlugins(newtonWorld, dir.toStringz); 114 void* p = NewtonGetPreferedPlugin(newtonWorld); 115 writeln("Selected plugin: ", NewtonGetPluginString(newtonWorld, p).to!string); 116 } 117 118 void update(double dt) 119 { 120 NewtonUpdate(newtonWorld, dt); 121 } 122 123 NewtonRigidBody createDynamicBody(NewtonCollisionShape shape, float mass) 124 { 125 NewtonRigidBody b = New!NewtonRigidBody(shape, mass, this, this); 126 // TODO: store a list of bodies 127 return b; 128 } 129 130 NewtonRigidBody createStaticBody(NewtonCollisionShape shape) 131 { 132 return createDynamicBody(shape, 0.0f); 133 } 134 135 void raycast(Vector3f pstart, Vector3f pend, NewtonRaycaster raycaster) 136 { 137 NewtonWorldRayCast(newtonWorld, pstart.arrayof.ptr, pend.arrayof.ptr, &newtonWorldRayFilterCallback, cast(void*)raycaster, &newtonWorldRayPrefilterCallback, 0); 138 } 139 140 ~this() 141 { 142 NewtonMaterialDestroyAllGroupID(newtonWorld); 143 NewtonDestroyAllBodies(newtonWorld); 144 NewtonDestroy(newtonWorld); 145 } 146 } 147 148 abstract class NewtonCollisionShape: Owner 149 { 150 NewtonPhysicsWorld world; 151 NewtonCollision* newtonCollision; 152 153 this(NewtonPhysicsWorld world) 154 { 155 super(world); 156 this.world = world; 157 } 158 159 ~this() 160 { 161 if (newtonCollision) 162 NewtonDestroyCollision(newtonCollision); 163 } 164 165 // Override me 166 Vector3f inertia(float mass) 167 { 168 return Vector3f(mass, mass, mass); 169 } 170 } 171 172 class NewtonBoxShape: NewtonCollisionShape 173 { 174 Vector3f halfSize; 175 176 this(Vector3f extents, NewtonPhysicsWorld world) 177 { 178 super(world); 179 newtonCollision = NewtonCreateBox(world.newtonWorld, extents.x, extents.y, extents.z, 0, null); 180 halfSize = extents * 0.5f; 181 } 182 183 override Vector3f inertia(float mass) 184 { 185 float x2 = halfSize.x * halfSize.x; 186 float y2 = halfSize.y * halfSize.y; 187 float z2 = halfSize.z * halfSize.z; 188 return Vector3f( 189 (y2 + z2)/3 * mass, 190 (x2 + z2)/3 * mass, 191 (x2 + y2)/3 * mass 192 ); 193 } 194 } 195 196 class NewtonSphereShape: NewtonCollisionShape 197 { 198 float radius; 199 200 this(float radius, NewtonPhysicsWorld world) 201 { 202 super(world); 203 this.radius = radius; 204 newtonCollision = NewtonCreateSphere(world.newtonWorld, radius, 0, null); 205 } 206 207 override Vector3f inertia(float mass) 208 { 209 float v = 0.4f * mass * radius * radius; 210 return Vector3f(v, v, v); 211 } 212 } 213 214 class NewtonRigidBody: Owner 215 { 216 NewtonPhysicsWorld world; 217 NewtonBody* newtonBody; 218 int materialGroupId; 219 float mass; 220 Vector3f inertia; 221 Vector3f gravity = Vector3f(0.0f, -9.8f, 0.0f); 222 Vector3f force = Vector3f(0.0f, 0.0f, 0.0f); 223 Vector3f torque = Vector3f(0.0f, 0.0f, 0.0f); 224 Vector4f position = Vector4f(0.0f, 0.0f, 0.0f, 1.0f); 225 Quaternionf rotation = Quaternionf.identity; 226 Matrix4x4f transformation = Matrix4x4f.identity; 227 bool enableRotation = true; 228 bool raycastable = true; 229 230 bool isRaycastable() { return raycastable; } 231 232 this(NewtonCollisionShape shape, float mass, NewtonPhysicsWorld world, Owner o) 233 { 234 super(o); 235 236 this.world = world; 237 238 newtonBody = NewtonCreateDynamicBody(world.newtonWorld, shape.newtonCollision, transformation.arrayof.ptr); 239 NewtonBodySetUserData(newtonBody, cast(void*)this); 240 this.groupId = world.defaultGroupId; 241 this.mass = mass; 242 this.inertia = shape.inertia(mass); 243 NewtonBodySetMassMatrix(newtonBody, mass, inertia.x, inertia.y, inertia.z); 244 NewtonBodySetForceAndTorqueCallback(newtonBody, &newtonBodyForceCallback); 245 } 246 247 void update(double dt) 248 { 249 NewtonBodyGetPosition(newtonBody, position.arrayof.ptr); 250 NewtonBodyGetMatrix(newtonBody, transformation.arrayof.ptr); 251 if (enableRotation) 252 { 253 rotation = Quaternionf.fromMatrix(transformation); 254 } 255 else 256 { 257 rotation = Quaternionf.identity; 258 transformation = translationMatrix(position.xyz); 259 NewtonBodySetMatrix(newtonBody, transformation.arrayof.ptr); 260 } 261 } 262 263 void groupId(int id) @property 264 { 265 NewtonBodySetMaterialGroupID(newtonBody, id); 266 materialGroupId = id; 267 } 268 269 int groupId() @property 270 { 271 return materialGroupId; 272 } 273 274 void addForce(Vector3f f) 275 { 276 force += f; 277 } 278 279 void addTorque(Vector3f t) 280 { 281 torque += t; 282 } 283 284 void createUpVectorConstraint(Vector3f up) 285 { 286 NewtonJoint* joint = NewtonConstraintCreateUpVector(world.newtonWorld, up.arrayof.ptr, newtonBody); 287 } 288 289 void velocity(Vector3f v) @property 290 { 291 NewtonBodySetVelocity(newtonBody, v.arrayof.ptr); 292 } 293 294 Vector3f velocity() @property 295 { 296 Vector3f v; 297 NewtonBodyGetVelocity(newtonBody, v.arrayof.ptr); 298 return v; 299 } 300 } 301 302 class NewtonBodyComponent: EntityComponent 303 { 304 NewtonRigidBody rbody; 305 Matrix4x4f prevTransformation; 306 307 this(EventManager em, Entity e, NewtonRigidBody b) 308 { 309 super(em, e); 310 rbody = b; 311 312 Quaternionf rot = e.rotation; 313 rbody.transformation = 314 translationMatrix(e.position) * 315 rot.toMatrix4x4; 316 317 NewtonBodySetMatrix(rbody.newtonBody, rbody.transformation.arrayof.ptr); 318 319 prevTransformation = Matrix4x4f.identity; 320 } 321 322 override void update(Time t) 323 { 324 rbody.update(t.delta); 325 326 entity.prevTransformation = prevTransformation; 327 328 entity.position = rbody.position.xyz; 329 entity.transformation = rbody.transformation * scaleMatrix(entity.scaling); 330 entity.invTransformation = entity.transformation.inverse; 331 entity.rotation = rbody.rotation; 332 333 entity.absoluteTransformation = entity.transformation; 334 entity.invAbsoluteTransformation = entity.invTransformation; 335 entity.prevAbsoluteTransformation = entity.prevTransformation; 336 337 prevTransformation = entity.transformation; 338 } 339 }