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 }