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 }