Files
Edit2D/entity.odin
T
2026-03-17 09:34:15 +05:45

307 lines
6.9 KiB
Odin

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
}