1 module nudge_ext;
2 
3 import core.memory;
4 import core.stdc.string : memset;
5 static import nudge;
6 
7 /// represents a context for nudge
8 class NudgeRealm {
9     public const(uint) max_bodies;
10     public const(uint) max_boxes;
11     public const(uint) max_spheres;
12 
13     public size_t arena_size = 64 * 1024 * 1024; // 64M
14 
15     public enum float[3] zero_vec = [0, 0, 0];
16     public enum nudge.Transform identity_transform = nudge.Transform([0, 0, 0],
17                 0, [0.0f, 0.0f, 0.0f, 1.0f]);
18     public enum nudge.BodyMomentum zero_momentum = nudge.BodyMomentum(zero_vec, 0, zero_vec, 0);
19 
20     public nudge.Arena arena;
21     public nudge.BodyData bodies;
22     public nudge.ColliderData colliders;
23     public nudge.ContactData contact_data;
24     public nudge.ContactCache contact_cache;
25     public nudge.ActiveBodies active_bodies;
26 
27     /// initializes a nudge realm with the given maximums
28     this(uint max_bodies, uint max_boxes, uint max_spheres) {
29         assert(max_boxes <= max_bodies, "boxes exceeds max body count");
30         assert(max_spheres <= max_bodies, "spheres exceeds max body count");
31 
32         this.max_bodies = max_bodies;
33         this.max_boxes = max_boxes;
34         this.max_spheres = max_spheres;
35     }
36 
37     /// allocate memory for nudge
38     public void allocate() {
39         // Allocate memory for simulation arena.
40         arena.size = arena_size;
41         arena.data = GC.malloc(arena.size);
42 
43         // Allocate memory for bodies, colliders, and contacts.
44         active_bodies.capacity = max_bodies;
45         active_bodies.indices = new ushort[max_bodies].ptr;
46 
47         bodies.idle_counters = new ubyte[max_bodies].ptr;
48         bodies.transforms = new nudge.Transform[max_bodies].ptr;
49         bodies.momentum = new nudge.BodyMomentum[max_bodies].ptr;
50         bodies.properties = new nudge.BodyProperties[max_bodies].ptr;
51 
52         colliders.boxes.data = new nudge.BoxCollider[max_boxes].ptr;
53         colliders.boxes.tags = new uint[max_boxes].ptr;
54         colliders.boxes.transforms = new nudge.Transform[max_boxes].ptr;
55 
56         colliders.spheres.data = new nudge.SphereCollider[max_spheres].ptr;
57         colliders.spheres.tags = new uint[max_spheres].ptr;
58         colliders.spheres.transforms = new nudge.Transform[max_spheres].ptr;
59 
60         contact_data.capacity = max_bodies * 64;
61         contact_data.bodies = new nudge.BodyPair[contact_data.capacity].ptr;
62         contact_data.data = new nudge.Contact[contact_data.capacity].ptr;
63         contact_data.tags = new ulong[contact_data.capacity].ptr;
64         contact_data.sleeping_pairs = new uint[contact_data.capacity].ptr;
65 
66         contact_cache.capacity = max_bodies * 64;
67         contact_cache.data = new nudge.CachedContactImpulse[contact_cache.capacity].ptr;
68         contact_cache.tags = new ulong[contact_cache.capacity].ptr;
69     }
70 
71     public void destroy() {
72         // free the arena data, because we used malloc()
73         GC.free(arena.data);
74     }
75 
76     pragma(inline) {
77         /// append a new body, return the id
78         public uint append_body(nudge.Transform transform,
79                 nudge.BodyProperties properties, nudge.BodyMomentum momentum) {
80             if (bodies.count >= max_bodies) {
81                 assert(0, "max body count exceeded");
82             }
83 
84             uint id = bodies.count++;
85 
86             bodies.transforms[id] = transform;
87             bodies.properties[id] = properties;
88             bodies.momentum[id] = momentum;
89             bodies.idle_counters[id] = 0;
90 
91             return id;
92         }
93 
94         /// pop the last body off
95         public void pop_last_body() {
96             bodies.count--;
97         }
98 
99         /// clear the data of a body with matching id
100         public void clear_body(uint id) {
101             // zero out the body
102             bodies.transforms[id] = identity_transform;
103             memset(&bodies.properties[id], 0, bodies.properties[id].sizeof);
104             memset(&bodies.momentum[id], 0, bodies.momentum[id].sizeof);
105             bodies.idle_counters[id] = 0;
106         }
107 
108         /// swap two bodies given their indices
109         public void swap_bodies(uint id_src, uint id_dst) {
110             auto tmp_transform = bodies.transforms[id_dst];
111             bodies.transforms[id_dst] = bodies.transforms[id_src];
112             bodies.transforms[id_src] = tmp_transform;
113 
114             auto tmp_props = bodies.properties[id_dst];
115             bodies.properties[id_dst] = bodies.properties[id_src];
116             bodies.properties[id_src] = tmp_props;
117 
118             auto tmp_momentum = bodies.momentum[id_dst];
119             bodies.momentum[id_dst] = bodies.momentum[id_src];
120             bodies.momentum[id_src] = tmp_momentum;
121 
122             auto tmp_idle = bodies.idle_counters[id_dst];
123             bodies.idle_counters[id_dst] = bodies.idle_counters[id_src];
124             bodies.idle_counters[id_src] = tmp_idle;
125         }
126 
127         /// append a box collider and get its index
128         public uint append_box_collider(uint body_id, nudge.BoxCollider collider,
129                 nudge.Transform transform, uint tag = 0) {
130             if (colliders.boxes.count >= max_boxes) {
131                 assert(0, "max body count exceeded (boxes)");
132             }
133 
134             uint box_id = colliders.boxes.count++;
135 
136             colliders.boxes.tags[box_id] = tag;
137             colliders.boxes.data[box_id] = collider;
138             colliders.boxes.transforms[box_id] = transform;
139             colliders.boxes.transforms[box_id].body = body_id;
140 
141             return box_id;
142         }
143 
144         /// zero the data of a box collider index
145         public void clear_box_collider(uint id) {
146             colliders.boxes.tags[id] = 0;
147             memset(&colliders.boxes.data[id], 0, colliders.boxes.data[id].sizeof);
148             memset(&colliders.boxes.transforms[id], 0, colliders.boxes.transforms[id].sizeof);
149         }
150 
151         /// swap two box colliders given their indices
152         public void swap_box_colliders(uint id_src, uint id_dst) {
153             auto tmp_transform = colliders.boxes.transforms[id_dst];
154             colliders.boxes.transforms[id_dst] = colliders.boxes.transforms[id_src];
155             colliders.boxes.transforms[id_src] = tmp_transform;
156 
157             auto tmp_data = colliders.boxes.data[id_dst];
158             colliders.boxes.data[id_dst] = colliders.boxes.data[id_src];
159             colliders.boxes.data[id_src] = tmp_data;
160 
161             auto tmp_tag = colliders.boxes.tags[id_dst];
162             colliders.boxes.tags[id_dst] = colliders.boxes.tags[id_src];
163             colliders.boxes.tags[id_src] = tmp_tag;
164         }
165 
166         /// pop the last box collider off
167         public void pop_last_box_collider() {
168             colliders.boxes.count--;
169         }
170 
171         /// append a sphere collider and get its index
172         public uint append_sphere_collider(uint body_id,
173                 nudge.SphereCollider collider, nudge.Transform transform, uint tag = 0) {
174             if (colliders.spheres.count >= max_spheres) {
175                 assert(0, "max body count exceeded (spheres)");
176             }
177 
178             uint sphere_id = colliders.spheres.count++;
179 
180             colliders.spheres.tags[sphere_id] = tag;
181             colliders.spheres.data[sphere_id] = collider;
182             colliders.spheres.transforms[sphere_id] = transform;
183             colliders.spheres.transforms[sphere_id].body = body_id;
184 
185             return sphere_id;
186         }
187 
188         /// zero the data of a sphere collider index
189         public void clear_sphere_collider(uint id) {
190             colliders.spheres.tags[id] = 0;
191             memset(&colliders.spheres.data[id], 0, colliders.spheres.data[id].sizeof);
192             memset(&colliders.spheres.transforms[id], 0, colliders.spheres.transforms[id].sizeof);
193         }
194 
195         /// swap two sphere colliders given their indices
196         public void swap_sphere_colliders(uint id_src, uint id_dst) {
197             auto tmp_transform = colliders.spheres.transforms[id_dst];
198             colliders.spheres.transforms[id_dst] = colliders.spheres.transforms[id_src];
199             colliders.spheres.transforms[id_src] = tmp_transform;
200 
201             auto tmp_data = colliders.spheres.data[id_dst];
202             colliders.spheres.data[id_dst] = colliders.spheres.data[id_src];
203             colliders.spheres.data[id_src] = tmp_data;
204 
205             auto tmp_tag = colliders.spheres.tags[id_dst];
206             colliders.spheres.tags[id_dst] = colliders.spheres.tags[id_src];
207             colliders.spheres.tags[id_src] = tmp_tag;
208         }
209 
210         /// pop the last sphere collider off
211         public void pop_last_sphere_collider() {
212             colliders.spheres.count--;
213         }
214     }
215 }