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 }