1 /*
2 Copyright (c) 2019-2020 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 && b)
55         {
56             if (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         }
63         
64         return 1.0f;
65     }
66 
67     uint newtonWorldRayPrefilterCallback(
68         const NewtonBody* nbody,
69         const NewtonCollision* collision,
70         void* userData)
71     {
72         return 1;
73     }
74     
75     void newtonSensorContactsProcess(
76         const NewtonJoint* contactJoint,
77         dFloat timestep,
78         int threadIndex)
79     {
80         void* nextContact;
81         uint numContacts = 0;
82         for (void* contact = NewtonContactJointGetFirstContact(contactJoint); contact; contact = nextContact)
83         {
84             nextContact = NewtonContactJointGetNextContact(contactJoint, contact);
85             NewtonContactJointRemoveContact(contactJoint, contact);
86             numContacts++;
87         }
88         
89         if (numContacts)
90         {
91             NewtonBody* b0 = NewtonJointGetBody0(contactJoint);
92             NewtonBody* b1 = NewtonJointGetBody1(contactJoint);
93             NewtonRigidBody body0 = cast(NewtonRigidBody)NewtonBodyGetUserData(b0);
94             NewtonRigidBody body1 = cast(NewtonRigidBody)NewtonBodyGetUserData(b1);
95             
96             if (body0 && body0.isSensor)
97             {
98                 body0.onCollision(body1);
99             }
100             else if (body1 && body1.isSensor)
101             {
102                 body1.onCollision(body0);
103             }
104         }
105     }
106     
107     int newtonSensorOnAABBOverlapCallback(
108         const NewtonJoint* contact, 
109         dFloat timestep,
110         int threadIndex)
111     {
112         return 1;
113     }
114     
115     int newtonSensorOnAABBOverlapCancelCallback(
116         const NewtonJoint* contact, 
117         dFloat timestep,
118         int threadIndex)
119     {
120         return 0;
121     }
122 }
123 
124 interface NewtonRaycaster
125 {
126     float onRayHit(NewtonRigidBody nbody, Vector3f hitPoint, Vector3f hitNormal, float t);
127 }
128 
129 class NewtonPhysicsWorld: Owner
130 {
131     EventManager eventManager;
132     NewtonWorld* newtonWorld;
133     int defaultGroupId;
134     int kinematicGroupId;
135     int sensorGroupId;
136 
137     this(EventManager eventManager, Owner o)
138     {
139         super(o);
140         eventManager = eventManager;
141         newtonWorld = NewtonCreate();
142         defaultGroupId = NewtonMaterialGetDefaultGroupID(newtonWorld);
143         kinematicGroupId = createGroupId();
144         sensorGroupId = createGroupId();
145         NewtonMaterialSetDefaultElasticity(newtonWorld, defaultGroupId, kinematicGroupId, 0.0f);
146         NewtonMaterialSetDefaultFriction(newtonWorld, defaultGroupId, kinematicGroupId, 0.5f, 0.0f);
147         NewtonMaterialSetCollisionCallback(newtonWorld, sensorGroupId, defaultGroupId, null, &newtonSensorContactsProcess);
148         NewtonMaterialSetCollisionCallback(newtonWorld, kinematicGroupId, sensorGroupId, &newtonSensorOnAABBOverlapCancelCallback, null);
149     }
150     
151     int createGroupId()
152     {
153         return NewtonMaterialCreateGroupID(newtonWorld);
154     }
155 
156     void loadPlugins(string dir)
157     {
158         NewtonLoadPlugins(newtonWorld, dir.toStringz);
159         void* p = NewtonGetPreferedPlugin(newtonWorld);
160         writeln("Selected plugin: ", NewtonGetPluginString(newtonWorld, p).to!string);
161     }
162 
163     void update(double dt)
164     {
165         NewtonUpdate(newtonWorld, dt);
166     }
167 
168     NewtonRigidBody createDynamicBody(NewtonCollisionShape shape, float mass)
169     {
170         NewtonRigidBody b = New!NewtonRigidBody(shape, mass, this, this);
171         b.dynamic = true;
172         // TODO: store a list of bodies
173         return b;
174     }
175 
176     NewtonRigidBody createStaticBody(NewtonCollisionShape shape)
177     {
178         auto b = createDynamicBody(shape, 0.0f);
179         b.dynamic = false;
180         return b;
181     }
182 
183     void raycast(Vector3f pstart, Vector3f pend, NewtonRaycaster raycaster)
184     {
185         NewtonWorldRayCast(newtonWorld, pstart.arrayof.ptr, pend.arrayof.ptr, &newtonWorldRayFilterCallback, cast(void*)raycaster, &newtonWorldRayPrefilterCallback, 0);
186     }
187 
188     ~this()
189     {
190         NewtonDestroyAllBodies(newtonWorld);
191         NewtonMaterialDestroyAllGroupID(newtonWorld);
192         NewtonDestroy(newtonWorld);
193     }
194 }