package edit2d import b2 "vendor:box2d" import "core:fmt" static_index :: i32 static_index_global :: struct { index : i32, level : string, offset : b2.Vec2, } //DropProc :: #type proc "c" (window: WindowHandle, count: c.int, paths: [^]cstring) /* This file contains code to handle box2d stuffs of the game code Don't put game's logic here */ engine_entity_flags_enum :: enum u64 { POLYGON_IS_BOX, MULTI_BODIES, CHAIN, CHAIN_CUSTOM, MULTI_SHAPES, } engine_entity_flags :: bit_set[engine_entity_flags_enum] engine_entity_def :: struct { body_def : b2.BodyDef, shape_def : b2.ShapeDef, shape_type : b2.ShapeType, radius, scale : f32, centers : [2]b2.Vec2, size : b2.Vec2, is_loop : bool, vertices : [dynamic; b2.MAX_POLYGON_VERTICES]b2.Vec2, name_buf : [255]u8 `fmt:"-" cbor:"-"`, entity_flags : engine_entity_flags, index : static_index, //For chain bodies //It will replicate itself to the body count and connect themselves with rev_joints body_count : i32, //If chain not custom then the dynamic list of link_length will be used to create the chain //Handle the link_length like vertices rev_joint : b2.RevoluteJointDef, link_length_array : [dynamic]b2.Vec2, } default_engine_entity_def :: proc() -> engine_entity_def { ret : engine_entity_def ret.body_def = b2.DefaultBodyDef() ret.shape_def = b2.DefaultShapeDef() ret.shape_type = .polygonShape ret.scale = 1 ret.centers = {{-10, 0}, {10, 0}} ret.size = {2, 2} ret.radius = 10 ret.body_count = 10 ret.rev_joint = b2.DefaultRevoluteJointDef() //for dynamic polygon vs : [4]b2.Vec2 = { {-1.0, -1.0}, {-1.0, 1.0}, {1.0, 1.0}, {1.0, -1.0}, } ret.is_loop = true for v in vs do append(&ret.vertices, v) return ret } compare_engine_entity_def :: proc(a, b : engine_entity_def) -> bool { ret := false ret &= a.body_def == b.body_def ret &= a.shape_def == b.shape_def ret &= a.shape_type == b.shape_type ret &= a.radius == b.radius ret &= a.scale == b.scale ret &= a.centers == b.centers ret &= a.size == b.size ret &= a.is_loop == b.is_loop //ret &= a.vertices[:] == b.vertices[:] ret &= a.name_buf == b.name_buf ret &= a.entity_flags == b.entity_flags ret &= a.index == b.index ret &= a.body_count == b.body_count ret &= a.rev_joint == b.rev_joint //ret &= a.link_length[:] == b.link_length[:] return ret } engine_entity :: struct { body_id : b2.BodyId, shape_id : b2.ShapeId, //This is if the entity has multiple bodies bodies : [dynamic]b2.BodyId, shapes : [dynamic]b2.ShapeId, joints : [dynamic]b2.JointId, entity_flags : engine_entity_flags, index : ^static_index, joint_id : b2.JointId, } engine_entity_single_body :: proc(def : ^engine_entity_def, world_id: b2.WorldId, index : i32) -> engine_entity { def := def new_entity : engine_entity if def.index != 0 { new_entity.index = new(static_index) new_entity.index^ = def.index } new_entity.body_id = b2.CreateBody(world_id, def.body_def) switch def.shape_type{ case .circleShape: { def.radius *= def.scale circle := b2.Circle{ radius = def.radius } def.scale = 1 new_entity.shape_id = b2.CreateCircleShape(new_entity.body_id, def.shape_def, circle) } case .capsuleShape: { def.radius *= def.scale def.centers[0] *= def.scale def.centers[1] *= def.scale def.scale = 1 capsule := b2.Capsule{ center1 = def.centers[0], center2 = def.centers[1], radius = def.radius } new_entity.shape_id = b2.CreateCapsuleShape( new_entity.body_id, def.shape_def, capsule ) } case .chainSegmentShape: { chain_def := b2.DefaultChainDef() verts :[dynamic]b2.Vec2 for &v in def.vertices[:]{ v *= def.scale } for v in def.vertices[:]{ //If it's not a looped chain then it needs two defination if !def.is_loop do append(&verts, v) append(&verts, v) } chain_def.points = &verts[0] chain_def.count = i32(len(verts)) chain_def.isLoop = def.is_loop c := b2.CreateChain(new_entity.body_id, chain_def) shapes_data :[10]b2.ShapeId shapes := b2.Body_GetShapes(new_entity.body_id, shapes_data[:]) for shape in shapes{ b2.Shape_SetUserData(shape, rawptr(uintptr(index))) } def.scale = 1 } case .segmentShape: { for &v in def.vertices[:]{ v *= def.scale } segment : b2.Segment = {point1 = def.vertices[0], point2 = def.vertices[2]} new_entity.shape_id = b2.CreateSegmentShape(new_entity.body_id, def.shape_def, segment) def.scale = 1 } case .polygonShape: { poly : b2.Polygon if .POLYGON_IS_BOX in def.entity_flags { def.size *= def.scale poly = b2.MakeBox(def.size.x, def.size.y) def.scale = 1 }else { //def.size *= def.scale for &v in def.vertices[:]{ v *= def.scale } points := make([dynamic]b2.Vec2, 0) for p, i in def.vertices[:]{ if i >= int(len(def.vertices)) do break append_elem(&points, p) } sort_points_ccw(points[:]) hull := b2.ComputeHull(points[:]) poly = b2.MakePolygon(hull, 0) delete(points) def.scale = 1 } new_entity.shape_id = b2.CreatePolygonShape(new_entity.body_id, def.shape_def, poly) } } if def.shape_type != .chainSegmentShape{ b2.Shape_SetUserData(new_entity.shape_id, rawptr(uintptr(index))) } return new_entity } engine_create_chain_shape :: proc( def : ^engine_entity_def, world_id : b2.WorldId, index : i32 ) -> engine_entity { joint_def := def.rev_joint orig_pos := def.body_def.position position := def.body_def.position prev_body_id : b2.BodyId engine_entity := engine_entity_single_body(def, world_id, index) for i in def.link_length_array[:] { rot := b2.ComputeRotationBetweenUnitVectors(def.body_def.position, position) def.body_def.rotation = rot def.body_def.position = position engine_entity = engine_entity_single_body(def, world_id, index) pivot := position - i if i != 0 { joint_def.bodyIdA = prev_body_id joint_def.bodyIdB = engine_entity.body_id joint_def.localAnchorA = b2.Body_GetLocalPoint(joint_def.bodyIdA, pivot) joint_def.localAnchorB = b2.Body_GetLocalPoint(joint_def.bodyIdB, pivot) joint_id := b2.CreateRevoluteJoint(world_id, joint_def) } position += 2 * i prev_body_id = engine_entity.body_id } def.body_def.position = orig_pos return engine_entity } engine_create_entity :: proc( def : ^engine_entity_def, world_id : b2.WorldId, index : i32 ) -> engine_entity { if .CHAIN not_in def.entity_flags { return engine_entity_single_body(def, world_id, index) } else { return engine_create_chain_shape(def, world_id, index) } }