module game_core.collision; import bubel.ecs.attributes; import bubel.ecs.block_allocator; import bubel.ecs.core; import bubel.ecs.std; import bubel.ecs.vector; import ecs_utils.math.vector; import ecs_utils.utils; import game_core.basic; void registerCollisionModule(EntityManager* manager) { manager.registerDependency(ShootGridDependency); manager.registerDependency(BVHDependency); manager.registerDependency(StaticBVHDependency); manager.registerComponent!CShootGrid; manager.registerComponent!CShootGridMask; manager.registerComponent!CColliderScale; manager.registerComponent!CBVH; manager.registerComponent!CAABB; manager.registerComponent!CStatic; manager.registerSystem!ShootGridManager(-80); manager.registerSystem!ShootGridCleaner(-101); manager.registerSystem!BVHBuilder(-80); manager.registerSystem!StaticBVHBuilder(-80); //manager.registerSystem!BVHBuilder2(-79); manager.registerSystem!AABBUpdater(-81); } enum ShootGridDependency = "ShootGridDependency"; enum BVHDependency = "BVHDependency"; enum StaticBVHDependency = "StaticBVHDependency"; struct CShootGrid { mixin ECS.Component; } struct CBVH { mixin ECS.Component; uint index; } struct CStatic { mixin ECS.Component; } struct CAABB { mixin ECS.Component; alias bounding this; AABB bounding; } struct CShootGridMask { mixin ECS.Component; alias value this; ubyte value; } struct CColliderScale { mixin ECS.Component; alias value this; vec2 value = vec2(16,16); } struct ShootGrid { ~this() @nogc nothrow { if(nodes)Mallocator.dispose(nodes); if(masks)Mallocator.dispose(masks); } struct Node { alias entity this; EntityID entity; } void create(ivec2 nodes_count, vec2 node_size) { this.size = nodes_count; this.node_size = node_size; inv_node_size = vec2(1.0/node_size.x, 1.0/node_size.y); nodes = Mallocator.makeArray!Node(nodes_count.x * nodes_count.y); masks = Mallocator.makeArray!ubyte(nodes.length); } void mark(EntityID id, vec2 beg, vec2 end, ubyte mask) { ivec2 ibeg = cast(ivec2)(beg * inv_node_size); ivec2 iend = cast(ivec2)(end * inv_node_size + 0.5); if(ibeg.x < 0)ibeg.x = 0; if(ibeg.y < 0)ibeg.y = 0; if(iend.x > size.x)iend.x = size.x; if(iend.y > size.y)iend.y = size.y; foreach(i; ibeg.y .. iend.y) { foreach(j; ibeg.x .. iend.x) { nodes[i * size.x + j] = id; masks[i * size.x + j] = mask; } } } void clear() { size_t size = nodes.length * EntityID.sizeof; memset(nodes.ptr, 0, size); memset(masks.ptr, 0, masks.length); } bool test(out EntityID id, vec2 beg, vec2 end, ubyte mask) { ivec2 ibeg = cast(ivec2)(beg * inv_node_size); ivec2 iend = cast(ivec2)(end * inv_node_size + 0.5); if(ibeg.x < 0)ibeg.x = 0; if(ibeg.y < 0)ibeg.y = 0; if(iend.x > size.x)iend.x = size.x; if(iend.y > size.y)iend.y = size.y; foreach(i; ibeg.y .. iend.y) { foreach(j; ibeg.x .. iend.x) { uint index = i * size.x + j; if(nodes[index].id != 0) { if((masks[index] & mask) == 0)continue; id = nodes[index]; return true; } } } return false; } bool test(out EntityID id, vec2 pos, ubyte mask) { ivec2 ipos = cast(ivec2)(pos * inv_node_size - 0.5); if(ipos.x < 0)ipos.x = 0; if(ipos.y < 0)ipos.y = 0; if(ipos.x >= size.x)ipos.x = size.x - 1; if(ipos.y >= size.y)ipos.y = size.y - 1; size_t index = ipos.y * size.x + ipos.x; if((masks[index] & mask) == 0)return false; if(nodes[index].id != 0) { id = nodes[index]; return true; } return false; } vec2 inv_node_size; ivec2 size; vec2 node_size; Node[] nodes; ubyte[] masks; } struct ShootGridCleaner { mixin ECS.System!1; struct EntitiesData { } ShootGrid* grid; bool onBegin() { grid = gEM.getSystem!ShootGridManager().grid; if(grid != null)return true; else return false; } void onUpdate(EntitiesData data) { if(grid)grid.clear(); } } struct ShootGridManager { mixin ECS.System!128; mixin ECS.WritableDependencies!(ShootGridDependency); struct EntitiesData { uint length; //uint thread_id; const (Entity)[] entity; @readonly CLocation[] locations; @readonly CShootGrid[] grid_flag; //@readonly CGuild[] guild; @optional @readonly CShootGridMask[] mask; @optional @readonly CScale[] scale; @optional @readonly CColliderScale[] collider_scale; } ShootGrid* grid; void onCreate() { //grid = space_invaders.shoot_grid; grid = Mallocator.make!ShootGrid; grid.create(ivec2(80,60), vec2(5,5)); } void onDestroy() { Mallocator.dispose(grid); } // bool onBegin() // { // //if(!grid)return false; // //grid.clear(); // return true; // } void onUpdate(EntitiesData data) { vec2[] scale; if(data.collider_scale)scale = cast(vec2[])data.collider_scale; else if(data.scale)scale = cast(vec2[])data.scale; else return; if(data.mask is null) { foreach(i; 0..data.length) { vec2 half_scale = scale[i] * 0.5; 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)); } } else foreach(i; 0..data.length) { vec2 half_scale = scale[i] * 0.5; 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)); } } } struct AABB { vec2 size() { return max-min; } vec2 center() { return (max+min) * 0.5; } float area() { return size.x * size.y; } void set(ref AABB base, vec2 position, float angle, vec2 scale) { import std.algorithm.comparison : max; float sr = sinf(angle); float cr = cosf(angle); /*mat2 m = mat2(cr,-sr, sr,cr);*/ //vec2 pos = ;//m * ((base.max + base.min)*0.5*scale); vec2 size = (base.max - base.min)*scale; vec2[2] axis = [vec2(cr*size.x,sr*size.y),vec2(-sr*size.x,cr*size.y)]; this.max.x = max(fabs(axis[0].x),fabs(axis[1].x)); this.max.y = max(fabs(axis[0].y),fabs(axis[1].y)); this.min = -this.max; this.min += center + position; this.max += center + position; } void set(ref AABB base, vec2 position, vec2 scale) { vec2 size = (base.max - base.min)*scale; this.min = -size; this.max = size; this.min += center + position; this.max += center + position; } void set(ref AABB base, vec2 position, float angle) { import std.algorithm.comparison : max; float sr = sinf(angle); float cr = cosf(angle); /*mat2 m = mat2(cr,-sr, sr,cr);*/ //vec2 pos = ;//m * ((base.max + base.min)*0.5*scale); vec2 size = (base.max - base.min);//*scale; vec2[2] axis = [vec2(cr*size.x,sr*size.y),vec2(-sr*size.x,cr*size.y)]; this.max.x = max(fabs(axis[0].x),fabs(axis[1].x)); this.max.y = max(fabs(axis[0].y),fabs(axis[1].y)); this.min = -this.max; this.min += center + position; this.max += center + position; } void set(ref AABB base, vec2 position) { min = base.min + position; max = base.max + position; } vec2 min; vec2 max; } bool test(AABB a, AABB b) { if((a.max.x>b.min.x && a.max.y>b.min.y) && (a.min.x b.max.x && a.max.y > b.max.y)return 2; else if((a.max.x>b.min.x && a.max.y>b.min.y) && (a.min.xb.min.x && point.y>b.min.y) && (point.x