1 /* 2 Copyright (c) 2019-2022 Timur Gafarov 3 4 Boost Software License - Version 1.0 - August 17th, 2003 5 Permission is hereby granted, free of charge, to any person or organization 6 obtaining a copy of the software and accompanying documentation covered by 7 this license (the "Software") to use, reproduce, display, distribute, 8 execute, and transmit the Software, and to prepare derivative works of the 9 Software, and to permit third-parties to whom the Software is furnished to 10 do so, all subject to the following: 11 12 The copyright notices in the Software and this entire statement, including 13 the above license grant, this restriction and the following disclaimer, 14 must be included in all copies of the Software, in whole or in part, and 15 all derivative works of the Software, unless such copies or derivative 16 works are solely in the form of machine-executable object code generated by 17 a source language processor. 18 19 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 20 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 21 FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT 22 SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE 23 FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, 24 ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 25 DEALINGS IN THE SOFTWARE. 26 */ 27 28 module dagon.ext.newton.world; 29 30 import std.stdio; 31 import std.string; 32 import std.conv; 33 import dlib.core.ownership; 34 import dlib.core.memory; 35 import dlib.math.vector; 36 import bindbc.newton; 37 import dagon.core.event; 38 import dagon.ext.newton.shape; 39 import dagon.ext.newton.rigidbody; 40 41 extern(C) 42 { 43 dFloat newtonWorldRayFilterCallback( 44 const NewtonBody* nbody, 45 const NewtonCollision* shapeHit, 46 const dFloat* hitContact, 47 const dFloat* hitNormal, 48 dLong collisionID, 49 void* userData, 50 dFloat intersectParam) 51 { 52 NewtonRaycaster raycaster = cast(NewtonRaycaster)userData; 53 NewtonRigidBody b = cast(NewtonRigidBody)NewtonBodyGetUserData(nbody); 54 if (raycaster) 55 { 56 if (b && b.raycastable) 57 { 58 Vector3f p = Vector3f(hitContact[0], hitContact[1], hitContact[2]); 59 Vector3f n = Vector3f(hitNormal[0], hitNormal[1], hitNormal[2]); 60 return raycaster.onRayHit(b, p, n, intersectParam); 61 } 62 else 63 { 64 return 1.0f; 65 } 66 } 67 68 return 0.0f; 69 } 70 71 uint newtonWorldRayPrefilterCallback( 72 const NewtonBody* nbody, 73 const NewtonCollision* collision, 74 void* userData) 75 { 76 return 1; 77 } 78 79 void newtonSensorContactsProcess( 80 const NewtonJoint* contactJoint, 81 dFloat timestep, 82 int threadIndex) 83 { 84 void* nextContact; 85 uint numContacts = 0; 86 for (void* contact = NewtonContactJointGetFirstContact(contactJoint); contact; contact = nextContact) 87 { 88 nextContact = NewtonContactJointGetNextContact(contactJoint, contact); 89 NewtonContactJointRemoveContact(contactJoint, contact); 90 numContacts++; 91 } 92 93 if (numContacts) 94 { 95 NewtonBody* b0 = NewtonJointGetBody0(contactJoint); 96 NewtonBody* b1 = NewtonJointGetBody1(contactJoint); 97 NewtonRigidBody body0 = cast(NewtonRigidBody)NewtonBodyGetUserData(b0); 98 NewtonRigidBody body1 = cast(NewtonRigidBody)NewtonBodyGetUserData(b1); 99 100 if (body0 && body0.isSensor) 101 { 102 body0.onCollision(body1); 103 } 104 else if (body1 && body1.isSensor) 105 { 106 body1.onCollision(body0); 107 } 108 } 109 } 110 111 int newtonSensorOnAABBOverlapCallback( 112 const NewtonJoint* contact, 113 dFloat timestep, 114 int threadIndex) 115 { 116 return 1; 117 } 118 119 int newtonSensorOnAABBOverlapCancelCallback( 120 const NewtonJoint* contact, 121 dFloat timestep, 122 int threadIndex) 123 { 124 return 0; 125 } 126 } 127 128 interface NewtonRaycaster 129 { 130 /* 131 Callback should return parametric distance (0.0 to 1.0) above which Newton won't search anymore 132 for intersection points. For example, if t is returned, the engine immediately stops searching for new hits. 133 */ 134 float onRayHit(NewtonRigidBody nbody, Vector3f hitPoint, Vector3f hitNormal, float t); 135 } 136 137 class NewtonPhysicsWorld: Owner 138 { 139 EventManager eventManager; 140 NewtonWorld* newtonWorld; 141 int defaultGroupId; 142 int kinematicGroupId; 143 int sensorGroupId; 144 145 this(EventManager eventManager, Owner o) 146 { 147 super(o); 148 eventManager = eventManager; 149 newtonWorld = NewtonCreate(); 150 defaultGroupId = NewtonMaterialGetDefaultGroupID(newtonWorld); 151 kinematicGroupId = createGroupId(); 152 sensorGroupId = createGroupId(); 153 NewtonMaterialSetDefaultElasticity(newtonWorld, defaultGroupId, kinematicGroupId, 0.0f); 154 NewtonMaterialSetDefaultFriction(newtonWorld, defaultGroupId, kinematicGroupId, 0.5f, 0.0f); 155 NewtonMaterialSetCollisionCallback(newtonWorld, sensorGroupId, defaultGroupId, null, &newtonSensorContactsProcess); 156 NewtonMaterialSetCollisionCallback(newtonWorld, kinematicGroupId, sensorGroupId, &newtonSensorOnAABBOverlapCancelCallback, null); 157 } 158 159 int createGroupId() 160 { 161 return NewtonMaterialCreateGroupID(newtonWorld); 162 } 163 164 void loadPlugins(string dir) 165 { 166 NewtonLoadPlugins(newtonWorld, dir.toStringz); 167 void* p = NewtonGetPreferedPlugin(newtonWorld); 168 writeln("Selected plugin: ", NewtonGetPluginString(newtonWorld, p).to!string); 169 } 170 171 void update(double dt) 172 { 173 NewtonUpdate(newtonWorld, dt); 174 } 175 176 NewtonRigidBody createDynamicBody(NewtonCollisionShape shape, float mass) 177 { 178 NewtonRigidBody b = New!NewtonRigidBody(shape, mass, this, this); 179 b.dynamic = true; 180 // TODO: store a list of bodies 181 return b; 182 } 183 184 NewtonRigidBody createStaticBody(NewtonCollisionShape shape) 185 { 186 auto b = createDynamicBody(shape, 0.0f); 187 b.dynamic = false; 188 return b; 189 } 190 191 void raycast(Vector3f pstart, Vector3f pend, NewtonRaycaster raycaster) 192 { 193 NewtonWorldRayCast(newtonWorld, pstart.arrayof.ptr, pend.arrayof.ptr, &newtonWorldRayFilterCallback, cast(void*)raycaster, &newtonWorldRayPrefilterCallback, 0); 194 } 195 196 ~this() 197 { 198 NewtonDestroyAllBodies(newtonWorld); 199 NewtonMaterialDestroyAllGroupID(newtonWorld); 200 NewtonDestroy(newtonWorld); 201 } 202 }