package ion import b2 "vendor:box2d" import "core:fmt" static_index :: i32 static_index_global :: struct { index : i32, level : string, offset : b2.Vec2, } /* This file contains code to handle box2d stuffs of the game code Don't put game's logic here */ engine_world :: struct { world_id : b2.WorldId, //This in engine code? static_indexes : map[static_index]int `cbor:"-"`, relations : map[^static_index][dynamic]static_index_global `cbor:"-"`, relations_serializeable : map[ static_index][dynamic]static_index_global, /* Seems okay to put the joint defs in engine rather than in game because we don't add more attributes in joints in the game Can be changed later without requireing refactor */ revolute_joint_defs : [dynamic]revolt_joint_def, distant_joint_defs : [dynamic]distance_joint_def, joints : [dynamic]b2.JointId `cbor:"-"`, } 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, } 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 if a.entity_flags != b.entity_flags{ fmt.println("Hello world") } 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, } 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 }