1 module re.phys.newton3d;
2 
3 version (physics) {
4     import std.math;
5     import std.format;
6     import std.typecons;
7     import std.container.array;
8 
9     import re.ecs.component;
10     import re.ecs.updatable;
11     import re.math;
12     import re.math.raytypes;
13     import re.time;
14     import re.core;
15     import re.ng.manager;
16     import re.ng.scene;
17     import re.phys.collider;
18     import re.util.dual_map;
19     import re.util.newtonphys;
20     import bindbc.newton;
21 
22     @nogc nothrow pragma(inline, true) {
23         float[3] arrayof(Vector3 v) {
24             return raymath.Vector3ToFloat(v);
25         }
26 
27         float[4] arrayof(Quaternion q) {
28             return [q.x, q.y, q.z, q.w];
29         }
30 
31         float[16] arrayof(Matrix4 m) {
32             return raymath.MatrixToFloat(m);
33         }
34 
35         Vector3 xyz(Vector4 v) {
36             return Vector3(v.x, v.y, v.z);
37         }
38     }
39 
40     extern (C) {
41         nothrow @nogc void newtonBodyForceCallback(
42             const NewtonBody* nbody,
43             dFloat timestep,
44             int threadIndex) {
45             NewtonRigidBody b = cast(NewtonRigidBody) NewtonBodyGetUserData(nbody);
46             if (b) {
47                 Vector3 gravityForce = b.gravity * b.mass;
48                 NewtonBodyAddForce(nbody, gravityForce.arrayof.ptr);
49                 NewtonBodyAddForce(nbody, b.force.arrayof.ptr);
50                 NewtonBodyAddTorque(nbody, b.torque.arrayof.ptr);
51                 b.force = Vector3(0.0f, 0.0f, 0.0f);
52                 b.torque = Vector3(0.0f, 0.0f, 0.0f);
53             }
54         }
55     }
56 
57     class NewtonWorldManager {
58         public NewtonWorld* newtonWorld;
59         int defaultGroupId;
60 
61         this(NewtonWorld* world) {
62             this.newtonWorld = world;
63             defaultGroupId = NewtonMaterialGetDefaultGroupID(newtonWorld);
64         }
65     }
66 
67     class NewtonRigidBody {
68         NewtonWorldManager world;
69         NewtonBody* newtonBody;
70         int materialGroupId;
71         bool dynamic = false;
72         float mass;
73         Vector3 gravity = Vector3(0.0f, -9.8f, 0.0f);
74         Vector3 force = Vector3(0.0f, 0.0f, 0.0f);
75         Vector3 torque = Vector3(0.0f, 0.0f, 0.0f);
76         Vector4 position = Vector4(0.0f, 0.0f, 0.0f, 1.0f);
77         Quaternion rotation = raymath.QuaternionIdentity();
78         Matrix4 transformation = Matrix4Identity;
79         bool enableRotation = true;
80         bool raycastable = true;
81         bool sensor = false;
82         void delegate(NewtonRigidBody, NewtonRigidBody) collisionCallback;
83 
84         bool isRaycastable() {
85             return raycastable;
86         }
87 
88         bool isSensor() {
89             return sensor;
90         }
91 
92         this(NewtonCollisionShape shape, float mass, NewtonWorldManager world) {
93             this.world = world;
94 
95             newtonBody = NewtonCreateDynamicBody(world.newtonWorld, shape.newtonCollision, transformation
96                     .arrayof.ptr);
97             NewtonBodySetUserData(newtonBody, cast(void*) this);
98             this.groupId = world.defaultGroupId;
99             this.mass = mass;
100             NewtonBodySetMassProperties(newtonBody, mass, shape.newtonCollision);
101             NewtonBodySetForceAndTorqueCallback(newtonBody, &newtonBodyForceCallback);
102 
103             collisionCallback = &defaultCollisionCallback;
104         }
105 
106         void defaultCollisionCallback(NewtonRigidBody, NewtonRigidBody) {
107         }
108 
109         void update(double dt) {
110             NewtonBodyGetPosition(newtonBody, position.arrayof.ptr);
111             NewtonBodyGetMatrix(newtonBody, transformation.arrayof.ptr);
112             if (enableRotation) {
113                 // rotation = Quaternion.fromMatrix(transformation);
114                 // raymath.QuaternionFromMatrix
115                 rotation = raymath.QuaternionFromMatrix(transformation);
116             } else {
117                 rotation = raymath.QuaternionIdentity;
118                 // transformation = translationMatrix(position.xyz);
119                 transformation = raymath.MatrixTranslate(position.x, position.y, position.z);
120                 NewtonBodySetMatrix(newtonBody, transformation.arrayof.ptr);
121             }
122             // TODO: enableTranslation
123         }
124 
125         void groupId(int id) @property {
126             NewtonBodySetMaterialGroupID(newtonBody, id);
127             materialGroupId = id;
128         }
129 
130         int groupId() @property {
131             return materialGroupId;
132         }
133 
134         Vector3 worldCenterOfMass() {
135             Vector3 centerOfMass;
136             NewtonBodyGetCentreOfMass(newtonBody, centerOfMass.arrayof.ptr);
137             // return position.xyz + rotation.rotate(centerOfMass);
138             return Vector3(position.x, position.y, position.z) + raymath.Vector3RotateByQuaternion(centerOfMass, rotation);
139         }
140 
141         void addForce(Vector3 f) {
142             // force += f;
143             this.force = this.force + f;
144         }
145 
146         void addForceAtPos(Vector3 f, Vector3 pos) {
147             this.force = this.force + f;
148             // torque += cross(position.xyz - worldCenterOfMass(), force);
149             this.torque = this.torque + raymath.Vector3CrossProduct(
150                 (position.xyz - worldCenterOfMass()), force);
151         }
152 
153         void addTorque(Vector3 t) {
154             // torque += t;
155             this.torque = this.torque + t;
156         }
157 
158         void createUpVectorConstraint(Vector3 up) {
159             NewtonJoint* joint = NewtonConstraintCreateUpVector(world.newtonWorld, up.arrayof.ptr, newtonBody);
160         }
161 
162         void velocity(Vector3 v) @property {
163             NewtonBodySetVelocity(newtonBody, v.arrayof.ptr);
164         }
165 
166         Vector3 velocity() @property {
167             Vector3 v;
168             NewtonBodyGetVelocity(newtonBody, v.arrayof.ptr);
169             return v;
170         }
171 
172         void onCollision(NewtonRigidBody otherBody) {
173             collisionCallback(this, otherBody);
174         }
175     }
176 
177     abstract class NewtonCollisionShape {
178         NewtonWorldManager world;
179         NewtonCollision* newtonCollision;
180 
181         this(NewtonWorldManager world) {
182             // super(world);
183             this.world = world;
184         }
185 
186         ~this() {
187             //if (newtonCollision)
188             //    NewtonDestroyCollision(newtonCollision);
189         }
190 
191         void setTransformation(Matrix4 m) {
192             if (newtonCollision)
193                 NewtonCollisionSetMatrix(newtonCollision, m.arrayof.ptr);
194         }
195     }
196 
197     class NewtonBoxShape : NewtonCollisionShape {
198         Vector3 halfSize;
199 
200         this(Vector3 extents, NewtonWorldManager world) {
201             super(world);
202             newtonCollision = NewtonCreateBox(world.newtonWorld, extents.x, extents.y, extents.z, 0, null);
203             NewtonCollisionSetUserData(newtonCollision, cast(void*) this);
204             halfSize = extents * 0.5f;
205         }
206     }
207 
208     class NewtonSphereShape : NewtonCollisionShape {
209         float radius;
210 
211         this(float radius, NewtonWorldManager world) {
212             super(world);
213             this.radius = radius;
214             newtonCollision = NewtonCreateSphere(world.newtonWorld, radius, 0, null);
215             NewtonCollisionSetUserData(newtonCollision, cast(void*) this);
216         }
217     }
218 
219     class NewtonCylinderShape : NewtonCollisionShape {
220         float radius1;
221         float radius2;
222         float height;
223 
224         this(float radius1, float radius2, float height, NewtonWorldManager world) {
225             super(world);
226             this.radius1 = radius1;
227             this.radius2 = radius2;
228             this.height = height;
229             newtonCollision = NewtonCreateCylinder(world.newtonWorld, radius1, radius2, height, 0, null);
230             NewtonCollisionSetUserData(newtonCollision, cast(void*) this);
231         }
232     }
233 
234     class NewtonChamferCylinderShape : NewtonCollisionShape {
235         float radius;
236         float height;
237 
238         this(float radius, float height, NewtonWorldManager world) {
239             super(world);
240             this.radius = radius;
241             this.height = height;
242             newtonCollision = NewtonCreateChamferCylinder(world.newtonWorld, radius, height, 0, null);
243             NewtonCollisionSetUserData(newtonCollision, cast(void*) this);
244         }
245     }
246 
247     class NewtonCapsuleShape : NewtonCollisionShape {
248         float radius1;
249         float radius2;
250         float height;
251 
252         this(float radius, float height, NewtonWorldManager world) {
253             super(world);
254             this.radius1 = radius1;
255             this.radius2 = radius2;
256             this.height = height;
257             newtonCollision = NewtonCreateCapsule(world.newtonWorld, radius1, radius2, height, 0, null);
258             NewtonCollisionSetUserData(newtonCollision, cast(void*) this);
259         }
260     }
261 
262     class NewtonConeShape : NewtonCollisionShape {
263         float radius;
264         float height;
265 
266         this(float radius, float height, NewtonWorldManager world) {
267             super(world);
268             this.radius = radius;
269             this.height = height;
270             newtonCollision = NewtonCreateCone(world.newtonWorld, radius, height, 0, null);
271             NewtonCollisionSetUserData(newtonCollision, cast(void*) this);
272         }
273     }
274 
275     // class NewtonMeshShape : NewtonCollisionShape {
276     //     this(TriangleSet triangleSet, NewtonWorldManager world) {
277     //         super(world);
278     //         NewtonMesh* nmesh = NewtonMeshCreate(world.newtonWorld);
279     //         NewtonMeshBeginBuild(nmesh);
280     //         foreach (triangle; triangleSet)
281     //             foreach (i, p; triangle.v) {
282     //                 Vector3 n = triangle.n[i];
283     //                 NewtonMeshAddPoint(nmesh, p.x, p.y, p.z);
284     //                 NewtonMeshAddNormal(nmesh, n.x, n.y, n.z);
285     //             }
286     //         NewtonMeshEndBuild(nmesh);
287 
288     //         newtonCollision = NewtonCreateTreeCollisionFromMesh(world.newtonWorld, nmesh, 0);
289     //         NewtonCollisionSetUserData(newtonCollision, cast(void*) this);
290 
291     //         NewtonMeshDestroy(nmesh);
292     //     }
293     // }
294 
295     // class NewtonConvexHullShape : NewtonCollisionShape {
296     //     this(Mesh mesh, float tolerance, NewtonWorldManager world) {
297     //         super(world);
298     //         NewtonMesh* nmesh = NewtonMeshCreate(world.newtonWorld);
299     //         NewtonMeshBeginBuild(nmesh);
300     //         foreach (face; mesh.indices)
301     //             foreach (i; face) {
302     //                 Vector3 p = mesh.vertices[i];
303     //                 Vector3 n = mesh.normals[i];
304     //                 NewtonMeshAddPoint(nmesh, p.x, p.y, p.z);
305     //                 NewtonMeshAddNormal(nmesh, n.x, n.y, n.z);
306     //             }
307     //         NewtonMeshEndBuild(nmesh);
308 
309     //         newtonCollision = NewtonCreateConvexHullFromMesh(world.newtonWorld, nmesh, tolerance, 0);
310     //         NewtonCollisionSetUserData(newtonCollision, cast(void*) this);
311 
312     //         NewtonMeshDestroy(nmesh);
313     //     }
314     // }
315 
316     class NewtonCompoundShape : NewtonCollisionShape {
317         this(NewtonCollisionShape[] shapes, NewtonWorldManager world) {
318             super(world);
319             newtonCollision = NewtonCreateCompoundCollision(world.newtonWorld, 0);
320             NewtonCompoundCollisionBeginAddRemove(newtonCollision);
321             foreach (shape; shapes) {
322                 NewtonCompoundCollisionAddSubCollision(newtonCollision, shape.newtonCollision);
323             }
324             NewtonCompoundCollisionEndAddRemove(newtonCollision);
325         }
326     }
327 
328     // class NewtonHeightmapShape : NewtonCollisionShape {
329     //     uint width;
330     //     uint height;
331     //     float[] elevationMap;
332     //     ubyte[] attributeMap;
333 
334     //     this(Heightmap heightmap, uint w, uint h, Vector3 scale, NewtonWorldManager world) {
335     //         super(world);
336 
337     //         width = w;
338     //         height = h;
339 
340     //         elevationMap = New!(float[])(width * height);
341     //         attributeMap = New!(ubyte[])(width * height);
342 
343     //         foreach (x; 0 .. width)
344     //             foreach (z; 0 .. height) {
345     //                 float y = heightmap.getHeight(
346     //                     cast(float) x / cast(float)(width - 1),
347     //                     cast(float) z / cast(float)(height - 1));
348     //                 elevationMap[z * width + x] = y;
349     //                 attributeMap[z * width + x] = 0; // TODO
350     //             }
351 
352     //         newtonCollision = NewtonCreateHeightFieldCollision(world.newtonWorld,
353     //             width, height, 1, 0, elevationMap.ptr, cast(char*) attributeMap.ptr, scale.y, scale.x, scale.z, 0);
354     //     }
355 
356     //     ~this() {
357     //         Delete(elevationMap);
358     //         Delete(attributeMap);
359     //     }
360     // }
361 }