1 module game_core.collision;
2 
3 import bubel.ecs.attributes;
4 import bubel.ecs.block_allocator;
5 import bubel.ecs.core;
6 import bubel.ecs.std;
7 import bubel.ecs.vector;
8 
9 import ecs_utils.math.vector;
10 import ecs_utils.utils;
11 
12 import game_core.basic;
13 
14 import gui.attributes;
15 
16 void registerCollisionModule(EntityManager* manager)
17 {
18     manager.registerDependency(ShootGridDependency);
19     manager.registerDependency(BVHDependency);
20     manager.registerDependency(StaticBVHDependency);
21 
22     manager.registerComponent!CShootGrid;
23     manager.registerComponent!CShootGridMask;
24     manager.registerComponent!CColliderScale;
25     manager.registerComponent!CBVH;
26     manager.registerComponent!CAABB;
27     manager.registerComponent!CStatic;
28 
29     manager.registerSystem!ShootGridManager(-80);
30     manager.registerSystem!ShootGridCleaner(-101);
31     manager.registerSystem!BVHBuilder(-80);
32     manager.registerSystem!StaticBVHBuilder(-80);
33     //manager.registerSystem!BVHBuilder2(-79);
34     manager.registerSystem!AABBUpdater(-81);
35 }
36 
37 enum ShootGridDependency = "ShootGridDependency";
38 enum BVHDependency = "BVHDependency";
39 enum StaticBVHDependency = "StaticBVHDependency";
40 
41 struct CShootGrid
42 {
43     mixin ECS.Component;
44 }
45 
46 struct CBVH
47 {
48     mixin ECS.Component;
49 
50     @GUIDisabled uint index;
51 }
52 
53 struct CAABB
54 {
55     mixin ECS.Component;
56 
57     alias bounding this;
58 
59     AABB bounding;
60 }
61 
62 struct CShootGridMask
63 {
64     mixin ECS.Component;
65 
66     alias value this;
67 
68     @GUIDisabled ubyte value;
69 }
70 
71 struct CColliderScale
72 {
73     mixin ECS.Component;
74 
75     alias value this;
76 
77     vec2 value = vec2(16,16);
78 }
79 
80 
81 struct ShootGrid
82 {
83 
84     ~this() @nogc nothrow
85     {
86         if(nodes)Mallocator.dispose(nodes);
87         if(masks)Mallocator.dispose(masks);
88     }
89 
90     struct Node
91     {
92         alias entity this;
93 
94         EntityID entity;
95     }
96 
97     void create(ivec2 nodes_count, vec2 node_size)
98     {
99         this.size = nodes_count;
100         this.node_size = node_size;
101         inv_node_size = vec2(1.0/node_size.x, 1.0/node_size.y);
102         nodes = Mallocator.makeArray!Node(nodes_count.x * nodes_count.y);
103         masks = Mallocator.makeArray!ubyte(nodes.length);
104     }
105 
106     void mark(EntityID id, vec2 beg, vec2 end, ubyte mask)
107     {
108         ivec2 ibeg = cast(ivec2)(beg * inv_node_size);
109         ivec2 iend = cast(ivec2)(end * inv_node_size + 0.5);
110         if(ibeg.x < 0)ibeg.x = 0;
111         if(ibeg.y < 0)ibeg.y = 0;
112         if(iend.x > size.x)iend.x = size.x;
113         if(iend.y > size.y)iend.y = size.y;
114         foreach(i; ibeg.y .. iend.y)
115         {
116             foreach(j; ibeg.x .. iend.x)
117             {
118                 nodes[i * size.x + j] = id;
119                 masks[i * size.x + j] = mask;
120             }
121         }
122     }
123 
124     void clear()
125     {
126         size_t size = nodes.length * EntityID.sizeof;
127         memset(nodes.ptr, 0, size);
128         memset(masks.ptr, 0, masks.length);
129     }
130 
131     bool test(out EntityID id, vec2 beg, vec2 end, ubyte mask)
132     {
133         ivec2 ibeg = cast(ivec2)(beg * inv_node_size);
134         ivec2 iend = cast(ivec2)(end * inv_node_size + 0.5);
135         if(ibeg.x < 0)ibeg.x = 0;
136         if(ibeg.y < 0)ibeg.y = 0;
137         if(iend.x > size.x)iend.x = size.x;
138         if(iend.y > size.y)iend.y = size.y;
139         foreach(i; ibeg.y .. iend.y)
140         {
141             foreach(j; ibeg.x .. iend.x)
142             {
143                 uint index = i * size.x + j;
144                 if(nodes[index].id != 0)
145                 {
146                     if((masks[index] & mask) == 0)continue;
147                     id = nodes[index];
148                     return true;
149                 }
150             }
151         }
152         return false;
153     }
154 
155     bool test(out EntityID id, vec2 pos, ubyte mask)
156     {
157         ivec2 ipos = cast(ivec2)(pos * inv_node_size - 0.5);
158         if(ipos.x < 0)ipos.x = 0;
159         if(ipos.y < 0)ipos.y = 0;
160         if(ipos.x >= size.x)ipos.x = size.x - 1;
161         if(ipos.y >= size.y)ipos.y = size.y - 1;
162         size_t index = ipos.y * size.x + ipos.x;
163         if((masks[index] & mask) == 0)return false;
164         if(nodes[index].id != 0)
165         {
166             id = nodes[index];
167             return true;
168         }
169         return false;
170     }
171 
172     vec2 inv_node_size;
173     ivec2 size;
174     vec2 node_size;
175     Node[] nodes;
176     ubyte[] masks;
177 }
178 
179 struct ShootGridCleaner
180 {
181     mixin ECS.System!1;
182 
183     struct EntitiesData
184     {
185 
186     }
187 
188     ShootGrid* grid;
189 
190     bool onBegin()
191     {
192         grid = gEntityManager.getSystem!ShootGridManager().grid;
193         if(grid != null)return true;
194         else return false;
195     }
196 
197     void onUpdate(EntitiesData data)
198     {
199         if(grid)grid.clear();
200     }
201 }
202 
203 struct ShootGridManager
204 {
205     mixin ECS.System!128;
206 
207     mixin ECS.WritableDependencies!(ShootGridDependency);
208 
209     struct EntitiesData
210     {
211         uint length;
212         //uint thread_id;
213         const (Entity)[] entity;
214         @readonly CLocation[] locations;
215         @readonly CShootGrid[] grid_flag;
216         //@readonly CGuild[] guild;
217         @optional @readonly CShootGridMask[] mask;
218         @optional @readonly CScale[] scale;
219         @optional @readonly CColliderScale[] collider_scale;
220     }
221 
222     ShootGrid* grid;
223 
224     void onCreate()
225     {
226         //grid = space_invaders.shoot_grid;
227         grid = Mallocator.make!ShootGrid;
228         grid.create(ivec2(80,60), vec2(5,5));
229     }
230 
231     void onDestroy()
232     {
233         Mallocator.dispose(grid);
234     }
235 
236     // bool onBegin()
237     // {
238     //     //if(!grid)return false;
239     //     //grid.clear();
240     //     return true;
241     // }
242 
243     void onUpdate(EntitiesData data)
244     {
245         vec2[] scale;
246         if(data.collider_scale)scale = cast(vec2[])data.collider_scale;
247         else if(data.scale)scale = cast(vec2[])data.scale;
248         else return;
249         if(data.mask is null)
250         {
251             foreach(i; 0..data.length)
252             {
253                 vec2 half_scale = scale[i] * 0.5;
254                 grid.mark(data.entity[i].id, data.locations[i] - half_scale, data.locations[i] + half_scale, ubyte.max);//cast(ubyte)(1 << data.guild[i].guild));
255             }
256         }
257         else foreach(i; 0..data.length)
258         {
259             vec2 half_scale = scale[i] * 0.5;
260             grid.mark(data.entity[i].id, data.locations[i] - half_scale, data.locations[i] + half_scale, data.mask[i]);//cast(ubyte)(1 << data.guild[i].guild));
261         }
262 
263     }
264 }
265 
266 struct AABB
267 {
268     vec2 size() 
269     {
270         return max-min;
271     }
272 
273     vec2 center()
274     {
275         return (max+min) * 0.5;
276     }
277 
278     float area()
279     {
280         return size.x * size.y;
281     }
282 
283     void set(ref AABB base, vec2 position, float angle, vec2 scale)
284     {
285         import std.algorithm.comparison : max;
286 
287         float sr = sinf(angle);
288 		float cr = cosf(angle);
289 		/*mat2 m = mat2(cr,-sr,
290 					sr,cr);*/
291 
292         
293 		//vec2 pos = ;//m * ((base.max + base.min)*0.5*scale);
294 		vec2 size = (base.max - base.min)*scale;
295 		vec2[2] axis = [vec2(cr*size.x,sr*size.y),vec2(-sr*size.x,cr*size.y)];
296 		
297 	    this.max.x = max(fabs(axis[0].x),fabs(axis[1].x));
298 		this.max.y = max(fabs(axis[0].y),fabs(axis[1].y));
299 		
300 		this.min = -this.max;
301 		
302 		this.min += center + position;
303 		this.max += center + position;
304     }
305 
306     void set(ref AABB base, vec2 position, vec2 scale)
307     {
308         vec2 size = (base.max - base.min)*scale;
309 		
310 		this.min = -size;
311 		this.max = size;
312 		
313 		this.min += center + position;
314 		this.max += center + position;
315     }
316 
317     void set(ref AABB base, vec2 position, float angle)
318     {
319         import std.algorithm.comparison : max;
320 
321         float sr = sinf(angle);
322 		float cr = cosf(angle);
323 		/*mat2 m = mat2(cr,-sr,
324 					sr,cr);*/
325 
326         
327 		//vec2 pos = ;//m * ((base.max + base.min)*0.5*scale);
328 		vec2 size = (base.max - base.min);//*scale;
329 		vec2[2] axis = [vec2(cr*size.x,sr*size.y),vec2(-sr*size.x,cr*size.y)];
330 		
331 	    this.max.x = max(fabs(axis[0].x),fabs(axis[1].x));
332 		this.max.y = max(fabs(axis[0].y),fabs(axis[1].y));
333 		
334 		this.min = -this.max;
335 		
336 		this.min += center + position;
337 		this.max += center + position;
338     }
339 
340     void set(ref AABB base, vec2 position)
341     {
342 		min = base.min + position;
343 		max = base.max + position;
344     }
345 
346     vec2 min;
347     vec2 max;
348 }
349 
350 bool test(AABB a, AABB b)
351 {
352     if((a.max.x>b.min.x && a.max.y>b.min.y) &&
353 		(a.min.x<b.max.x && a.min.y<b.max.y))return true;
354 	else return false;
355 }
356 
357 byte intersectTest(AABB a, AABB b)
358 {
359 	if(a.min.x < b.min.x && a.min.y < b.min.y &&
360 	a.max.x > b.max.x && a.max.y > b.max.y)return 2;
361 	else if((a.max.x>b.min.x && a.max.y>b.min.y) &&
362 		(a.min.x<b.max.x && a.min.y<b.max.y))return 1;
363 	return 0;
364 }
365 
366 bool test(vec2 point, AABB b)
367 {
368 	if((point.x>b.min.x && point.y>b.min.y) &&
369 		(point.x<b.max.x && point.y<b.max.y))return true;
370 	else return false;
371 }
372 
373 AABB merge(AABB a, AABB b)
374 {
375     import std.algorithm.comparison: min, max;
376     return AABB(vec2(min(a.min.x,b.min.x),min(a.min.y,b.min.y)),vec2(max(a.max.x,b.max.x),max(a.max.y,b.max.y)));
377 }
378 
379 struct Quadtree
380 {
381 
382     Node* add(EntityID id, AABB aabb)
383     {   
384         ubyte depth = void;
385         vec2 ratio = aabb.size / this.aabb.size;
386         //if(ratio.x < ratio.y)depth = log2f();
387         //else depth = 0;
388         return null;
389         //2^x = size2/size;
390     }
391 
392     struct Node
393     {
394 
395         AABB aabb;
396         Node*[4] nodes;
397         MemoryBlock* block;
398         MemoryBlock* last_block;
399     }
400 
401     struct MemoryBlock
402     {
403         EntityID[10] entities;
404         MemoryBlock* next_block;
405         void* Node;
406     }
407  
408     struct MetaBlock
409     {
410         union 
411         {
412             MemoryBlock _alignment;
413             struct 
414             {
415                 Quadtree* quadtree;
416             }
417         }
418     }
419 
420     AABB aabb;
421     
422     Node main_node;
423     uint max_depth;
424 
425     BlockAllocator allocator;
426 }
427 
428 struct BVHTree
429 {
430 
431     void generateTopDown()
432     {
433 
434     }
435 
436     void generateBottomUp()
437     {
438         clearNodes();
439         uint index = 0;
440         while(index < nodes.length - 1)
441         {
442             Node* node = &nodes[index];
443             if(node.parent != uint.max)
444             {
445                 index++;
446                 continue;
447             }
448             uint best_index = 0;
449             float best_cost = float.max;
450             foreach(i;index+1 .. nodes.length)
451             {
452                 Node* test_node = &nodes[i];
453                 if(test_node.parent != uint.max)continue;
454                 // vec2 rel_pos = node.bounding.center - test_node.bounding.center;
455                 // float cost = fabs(rel_pos.x) + fabs(rel_pos.y);
456                 // float cost = rel_pos.length;
457                 // float cost = rel_pos.length2;
458                 float cost = merge(node.bounding, test_node.bounding).area();
459                 if(cost < best_cost)
460                 {
461                     best_cost = cost;
462                     best_index = cast(uint)i;
463                 }
464             }
465 
466             uint new_index = getNode();
467             Node* new_node = &nodes[new_index];
468             Node* best_node = &nodes[best_index];
469             
470             new_node.childs[0] = index;
471             new_node.childs[1] = best_index;
472             new_node.bounding = merge(best_node.bounding, node.bounding);
473 
474             best_node.parent = new_index;
475             node.parent = new_index;
476         }
477 
478         root = cast(uint)nodes.length - 1;
479     }
480 
481     uint addIncrementally(AABB bounding, EntityID id)
482     {
483         if(root == uint.max)
484         {
485             root = getNode();
486             Node* new_node = &nodes[root];
487             new_node.parent = uint.max;
488             new_node.entity = id;
489             new_node.bounding = bounding;
490             //new_node.childs = [uint.max, uint.max].staticArray;
491             new_node.childs[0] = uint.max;
492             new_node.childs[1] = uint.max;
493             return root;
494         }
495 
496         float cost = float.max;
497         uint best_index = 0;
498         findBest(bounding, root, best_index, cost, 0);
499 
500         uint new_index = getNode();
501         uint leaf_index = getNode();
502 
503         Node* new_node = &nodes[new_index];
504         Node* node = &nodes[best_index];
505         Node* parent = &nodes[node.parent];
506         Node* leaf_node = &nodes[leaf_index];
507 
508         leaf_node.entity = id;
509         leaf_node.bounding = bounding;
510         leaf_node.parent = new_index;
511 
512         new_node.parent = node.parent;
513         new_node.childs[0] = best_index;
514         new_node.childs[1] = leaf_index;
515         new_node.bounding = merge(bounding, node.bounding);
516 
517         if(node.parent != uint.max)
518         {
519             if(parent.childs[0] == best_index)
520             {
521                 parent.childs[0] = new_index;
522             }
523             else
524             {
525                 parent.childs[1] = new_index;
526             }
527         }
528         else
529         {
530             root = new_index;
531         }
532 
533         node.parent = new_index;
534 
535         uint index = new_node.parent;
536 
537         while(index != uint.max)
538         {
539             Node* lnode = &nodes[index];
540 
541             recalculate(lnode);
542 
543             rotate(lnode);
544 
545             index = lnode.parent;
546         }
547 
548         return leaf_index;
549     }
550 
551     void clearNodes()
552     {
553         root = uint.max;
554         uint i = 0;
555         while(i < nodes.length)
556         {
557             Node* node = &nodes[i];
558             if(node.childs[0] == uint.max)
559             {
560                 node.parent = uint.max;
561                 i++;
562             }
563             else 
564             {
565                 removeNode(i);
566             }
567         }
568     }
569 
570     void findBest(AABB bounding, uint node_index, ref uint best_index, ref float best_cost, float cost)
571     {
572         Node* node = &nodes[node_index];
573         //float area = nodes.bounding.area;
574         AABB new_bounding = merge(node.bounding, bounding);
575         float new_area = new_bounding.area;
576         if(new_area + cost < best_cost)
577         {
578             best_index = node_index;
579             best_cost = cost + new_area;
580         }
581 
582         if(node.childs[0] == uint.max)return;
583         float area_delta = new_area - node.bounding.area;
584 
585         if(bounding.area + area_delta + cost < best_cost)
586         {
587             findBest(bounding, node.childs[0], best_index, best_cost, cost + area_delta);
588             findBest(bounding, node.childs[1], best_index, best_cost, cost + area_delta);
589         }
590     }
591 
592     void add(AABB bounding, EntityID id)
593     {
594         Node* node = &nodes[getNode()];
595         node.entity = id;
596         node.bounding = bounding;
597         // node.childs = [uint.max,uint.max];
598         node.childs[0] = uint.max;
599         node.childs[1] = uint.max;
600     }
601 
602     void test(AABB bounding, bool delegate(EntityID id) callback)
603     {
604         bool traverse(Node* node)
605         {
606             if(.test(bounding, node.bounding))
607             {
608                 if(node.childs[0] == uint.max)
609                 {
610                     return callback(node.entity);
611                 }
612                 if(!traverse(&nodes[node.childs[0]]))return false;
613                 if(!traverse(&nodes[node.childs[1]]))return false;
614             }
615         /*node.bounding.max.x = max(nodes[node.childs[0]].bounding.max.x,nodes[node.childs[1]].bounding.max.x);
616         node.bounding.max.y = max(nodes[node.childs[0]].bounding.max.y,nodes[node.childs[1]].bounding.max.y);
617         node.bounding.min.x = min(nodes[node.childs[0]].bounding.min.x,nodes[node.childs[1]].bounding.min.x);
618         node.bounding.min.y = min(nodes[node.childs[0]].bounding.min.y,nodes[node.childs[1]].bounding.min.y);*/
619             return true;
620         }
621 
622         if(root < nodes.length)traverse(&nodes[root]);
623     }
624 
625     float computeCost()
626     {
627         float cost = 0;
628         foreach(ref Node node;nodes)
629         {
630             if(node.childs[0] != uint.max)
631             {
632                 cost += node.bounding.area();
633             }
634         }
635         return cost;
636     }
637 
638     void recalculate(Node* node)
639     {
640         import std.algorithm.comparison: min, max;
641         node.bounding = merge(nodes[node.childs[0]].bounding, nodes[node.childs[1]].bounding);
642     }
643 
644     void rotate(Node* node)
645     {
646         import std.algorithm.comparison: min, max;
647         if(node.parent == uint.max)return;
648         Node* parent = &nodes[node.parent];
649         Node* child1 = &nodes[node.childs[0]];
650         Node* child2 = &nodes[node.childs[1]];
651         
652         uint child_index = void;
653         if(parent.childs[0] == child1.parent)
654         {
655             child_index = 1;
656         }
657         else
658         {
659             child_index = 0;
660         }
661         Node* to_rotate = &nodes[parent.childs[child_index]];
662 
663         float cost = node.bounding.area();
664         AABB bounding1 = merge(child1.bounding, to_rotate.bounding);
665         AABB bounding2 = merge(child2.bounding, to_rotate.bounding);
666         float area1 = bounding1.area;
667         float area2 = bounding2.area;
668         if(area1 < area2)
669         {
670             if(area1 < cost)
671             {
672                 to_rotate.parent = child1.parent;
673                 child2.parent = node.parent;
674                 uint swap_index = node.childs[1];
675                 node.childs[1] = parent.childs[child_index];
676                 parent.childs[child_index] = swap_index;
677                 node.bounding = bounding1;
678             }
679         }
680         else
681         {
682             if(area2 < cost)
683             {
684                 to_rotate.parent = child1.parent;
685                 child1.parent = node.parent;
686                 uint swap_index = node.childs[0];
687                 node.childs[0] = parent.childs[child_index];
688                 parent.childs[child_index] = swap_index;
689                 node.bounding = bounding2;
690             }
691         }
692     }
693 
694     void remove(uint i)
695     {
696         //foreach(i, ref Node node; nodes)
697         //{
698         // if(node.entity == id)
699         // {
700         Node* node = &nodes[i];
701         if(node.parent != uint.max)
702         {
703             ///parent isn't root, most common beaviour
704             Node* parent = &nodes[node.parent];
705             if(parent.parent == uint.max)
706             {
707                 //delete leaf attached to root
708                 if(parent.childs[0] == i)
709                 {
710                     root = parent.childs[1];
711                     nodes[parent.childs[1]].parent = uint.max;
712                 }
713                 else 
714                 {
715                     root = parent.childs[0];
716                     nodes[parent.childs[0]].parent = uint.max;
717                 }
718             }
719             else 
720             {
721                 ///remove node from inside of tree
722                 Node* grand_parent = &nodes[parent.parent];
723                 uint remain_index = void;
724                 if(parent.childs[0] == i)remain_index = parent.childs[1];
725                 else remain_index = parent.childs[0];
726                 if(grand_parent.childs[0] == node.parent)
727                 {
728                     grand_parent.childs[0] = remain_index;
729                     nodes[remain_index].parent = parent.parent;
730                 }
731                 else 
732                 {
733                     grand_parent.childs[1] = remain_index;
734                     nodes[remain_index].parent = parent.parent;
735                 }
736 
737                 uint index = parent.parent;
738 
739                 while(index != uint.max)
740                 {
741                     Node* lnode = &nodes[index];
742 
743                     recalculate(lnode);
744 
745                     rotate(lnode);
746 
747                     index = lnode.parent;
748                 }
749             }
750             removeNode(node.parent);
751         }
752         else root = uint.max;
753         removeNode(cast(uint)i);
754                 //return;
755             //}
756         //}
757     }
758 
759     void clear()
760     {
761         last_node = uint.max;
762         nodes.clear();
763         root = uint.max;
764     }
765 
766     uint getNode()
767     {
768         if(last_node == uint.max)
769         {
770             //nodes.length = nodes.length + 1;
771             nodes.add(Node());
772             return cast(uint)nodes.length - 1;
773         }
774         else 
775         {
776             uint ret = last_node;
777             last_node = nodes[last_node].parent;
778             nodes[ret] = Node();
779             return ret;
780         }
781     }
782 
783     void removeNode(uint index)
784     {
785         Node* node = &nodes[index];
786         node.parent = last_node;
787         node.ptr = null;
788         last_node = index;
789     }
790 
791     /*void create()
792     {
793         root = getNode();
794     }*/
795 
796     struct Node
797     {
798         union 
799         {
800             EntityID entity;
801             void* ptr;
802         }   
803         AABB bounding;
804         uint parent = uint.max;
805         union 
806         {
807             struct //workaround betterC compilation issue with _memset
808             {
809                 uint _c1 = uint.max;
810                 uint _c2 = uint.max;
811             }
812             uint[2] childs;
813         }
814         
815     }
816 
817     Vector!Node nodes;
818     //uint nodes_count;
819     uint last_node = uint.max;
820     uint root = uint.max;
821 }
822 
823 struct StaticBVHBuilder
824 {
825     mixin ECS.System!1;
826 
827     mixin ECS.WritableDependencies!(StaticBVHDependency);
828 
829     struct EntitiesData
830     {
831         uint length;
832         //uint thread_id;
833         const (Entity)[] entity;
834         CBVH[] bvh;
835         @readonly CStatic[] static_flag;
836         @readonly CLocation[] locations;
837         @readonly CAABB[] bounding;
838     }
839 
840     BVHTree* tree;
841 
842     void onCreate()
843     {
844         tree = Mallocator.make!BVHTree;
845     }
846 
847     void onDestroy()
848     {
849         Mallocator.dispose(tree);
850     }
851 
852     // bool onBegin()
853     // {
854     //     tree.clear();
855     //     return true;
856     //     // return false;
857     // }
858 
859     void onAddEntity(EntitiesData data)
860     {
861         foreach(i;0..data.length)
862         {
863             data.bvh[i].index = tree.addIncrementally(data.bounding[i], data.entity[i].id);
864         }
865     }
866 
867     void onRemoveEntity(EntitiesData data)
868     {
869         foreach(i;0..data.length)
870         {
871             tree.remove(data.bvh[i].index);
872         }
873     }
874 
875     // void onUpdate(EntitiesData data)
876     // {
877     //     foreach(i; 0..data.length)
878     //     {
879     //         // tree.add(data.bounding[i], data.entity[i].id);
880     //         tree.addIncrementally(data.bounding[i], data.entity[i].id);
881     //     }
882 
883     //     import std.stdio;
884     //     writeln("Cost: ",tree.computeCost());
885     // }
886 }
887 
888 struct BVHBuilder
889 {
890     mixin ECS.System!1;
891 
892     mixin ECS.WritableDependencies!(BVHDependency);
893 
894     struct EntitiesData
895     {
896         uint length;
897         //uint thread_id;
898         const (Entity)[] entity;
899         CBVH[] bvh;
900         @readonly CLocation[] locations;
901         @readonly CAABB[] bounding;
902     }
903 
904     mixin ECS.ExcludedComponents!(CStatic);
905 
906     BVHTree* tree;
907 
908     void onCreate()
909     {
910         tree = Mallocator.make!BVHTree;
911     }
912 
913     void onDestroy()
914     {
915         Mallocator.dispose(tree);
916     }
917 
918     bool onBegin()
919     {
920         tree.clear();
921         return true;
922         // return false;
923     }
924 
925     // void onAddEntity(EntitiesData data)
926     // {
927     //     foreach(i;0..data.length)
928     //     {
929     //         tree.add(data.bounding[i], data.entity[i].id);
930     //     }
931     // }
932 
933     // void onRemoveEntity(EntitiesData data)
934     // {
935     //     foreach(i;0..data.length)
936     //     {
937     //         tree.remove(data.entity[i].id);
938     //     }
939     // }
940 
941     void onUpdate(EntitiesData data)
942     {
943         foreach(i; 0..data.length)
944         {
945             // tree.add(data.bounding[i], data.entity[i].id);
946             data.bvh[i].index = tree.addIncrementally(data.bounding[i], data.entity[i].id);
947         }
948 
949         // import std.stdio;
950         // writeln("Cost: ",tree.computeCost());
951     }
952 }
953 /*
954 struct BVHBuilder2
955 {
956     mixin ECS.System!1;
957 
958     mixin ECS.WritableDependencies!(BVHDependency);
959 
960     struct EntitiesData
961     {
962     }
963 
964     BVHTree* tree;
965 
966     void onCreate()
967     {
968         tree = gEntityManager.getSystem!BVHBuilder().tree;
969     }
970 
971     bool onBegin()
972     {
973         //if(tree.nodes.length-1 != tree.root)tree.clear();
974         return true;
975     }
976 
977     void onUpdate(EntitiesData data)
978     {
979         if(tree.nodes.length-1 != tree.root)
980         {
981             tree.generateBottomUp();
982             import std.stdio;
983             writeln("Cost: ",tree.computeCost());
984         }
985     }
986 }//*/
987 
988 struct AABBUpdater
989 {
990     mixin ECS.System!64;
991 
992     struct EntitiesData
993     {
994         uint length;
995         //uint thread_id;
996         const (Entity)[] entity;
997         CAABB[] bounding;
998 
999         @readonly CLocation[] location;
1000         @readonly CScale[] scale;
1001         @optional @readonly CRotation[] rotation;
1002         @optional @readonly CStatic[] static_flag;
1003     }
1004 
1005     void onAddEntity(EntitiesData data)
1006     {
1007         foreach(i; 0..data.length)
1008         {
1009             data.bounding[i] = AABB(data.location[i]-data.scale[i],data.location[i]+data.scale[i]);
1010         }
1011     }
1012 
1013     void onUpdate(EntitiesData data)
1014     {
1015         if(data.static_flag)return;
1016         
1017         foreach(i; 0..data.length)
1018         {
1019             data.bounding[i] = AABB(data.location[i]-data.scale[i],data.location[i]+data.scale[i]);
1020         }
1021     }
1022 }