Files
Edit2D/interface_joints.odin
T
2026-03-16 20:50:59 +05:45

293 lines
8.1 KiB
Odin

package ion
import "core:fmt"
import b2 "vendor:box2d"
import im "shared:odin-imgui"
/*
TODO:
Delete joints
Angles in degree
*/
/*
All joints have bodyIdA and bodyIdB
*/
joint_common :: struct
{
pivot : [2]f32,
entity_a, entity_b : static_index,
bodyIdA, bodyIdB : b2.BodyId,
}
revolt_joint_def :: struct
{
pivot : [2]f32,
entity_a, entity_b : static_index,
using def : b2.RevoluteJointDef,
}
distance_joint_def :: struct
{
pivot : [2]f32,
entity_a, entity_b : static_index,
using def : b2.DistanceJointDef,
}
interface_edit_joint_common :: proc(joint_def : ^joint_common, interface: ^interface_state) -> bool
{
level := interface.world
{
if joint_def.entity_a in level.static_indexes{
entity_a := interface.entity_defs[level.static_indexes[joint_def.entity_a]]
points_add(&interface.state.draw.points, entity_a.body_def.position, 20.0, b2.HexColor.Plum)
}
if joint_def.entity_b in level.static_indexes{
entity_b := interface.entity_defs[level.static_indexes[joint_def.entity_b]]
points_add(&interface.state.draw.points, entity_b.body_def.position, 20.0, b2.HexColor.Plum)
}
}
/*
Set body A and Body B on the basis of static index so that it can pesist after restart
*/
ret := false
if im.BeginCombo("Index A", fmt.ctprint(joint_def.entity_a))
{
for i in level.static_indexes
{
if im.Selectable(fmt.ctprint(i), i == joint_def.entity_a)
{
joint_def.entity_a = i
ret = true
}
}
im.EndCombo()
}
im.Separator()
if im.BeginCombo("Index B", fmt.ctprint(joint_def.entity_b))
{
for i in level.static_indexes
{
//Set pivot
if im.Selectable(fmt.ctprint(i), i == joint_def.entity_b)
{
joint_def.entity_b = i
//Get the body's position and set it as default pivot
joint_def.pivot = interface.entity_defs[level.static_indexes[i]].body_def.position
ret = true
}
}
im.EndCombo()
}
return ret
}
interface_edit_distance_joint :: proc( interface : ^interface_state ) -> bool
{
level := interface.world
interface.curr_joint_type = .distanceJoint
if len(level.distant_joint_defs) == 0 do im.Text("No distance joint created, Click add to create new")
if im.Button("Create new joint")
{
append(&level.distant_joint_defs, distance_joint_def{def = b2.DefaultDistanceJointDef()})
interface.curr_joint_index = i32(len(level.distant_joint_defs))
}
if im.Button("Delete current join")
{
unordered_remove(&level.distant_joint_defs, interface.curr_joint_index)
return true
}
//Select index
{
if im.BeginCombo("Select joint", fmt.ctprint(interface.curr_joint_index))
{
for i in 0..<len(level.distant_joint_defs){
if im.Selectable(fmt.ctprint(i), i32(i) == interface.curr_joint_index) do interface.curr_joint_index = i32(i)
}
im.EndCombo()
}
}
if interface.curr_joint_index >= i32(len(level.distant_joint_defs)) do return false
joint_def := &level.distant_joint_defs[interface.curr_joint_index]
old_def := joint_def^
if interface_edit_joint_common(cast(^joint_common)joint_def, interface) do return true
/*
Highlight the bodies here
*/
im.SliderFloat2("localAnchorA", &joint_def.localAnchorA, -5, 5)
im.SliderFloat2("localAnchorB", &joint_def.localAnchorB, -5, 5)
im.SliderFloat("Rest length", &joint_def.length, 0, 100)
im.Checkbox("Enable Spring", &joint_def.enableSpring)
im.InputFloat("Hertz ", &joint_def.hertz)
im.InputFloat("Damping Ratio", &joint_def.dampingRatio)
im.Checkbox("Enable Limit", &joint_def.enableLimit)
im.InputFloat("Min length", &joint_def.minLength)
im.InputFloat("Max length", &joint_def.maxLength)
im.Checkbox("Enable Motor", &joint_def.enableMotor)
im.InputFloat("Moror Torque", &joint_def.maxMotorForce)
im.InputFloat("Moror Speed", &joint_def.motorSpeed)
im.Checkbox("Collide Connected", &joint_def.collideConnected)
return old_def != joint_def^
}
interface_edit_rev_joint_minimal :: proc(joint_def: ^b2.RevoluteJointDef)
{
im.SliderFloat2("localAnchorA", &joint_def.localAnchorA, -5, 5)
im.SliderFloat2("localAnchorB", &joint_def.localAnchorB, -5, 5)
reference_angle := RAD2DEG * joint_def.referenceAngle
if im.SliderFloat("Reference Angle", &reference_angle, 0, 359)
{
joint_def.referenceAngle = DEG2RAD * reference_angle
}
target_angle := RAD2DEG * joint_def.targetAngle
if im.SliderFloat("Target Angle", &target_angle, 0, 359)
{
joint_def.targetAngle = DEG2RAD * target_angle
}
im.Checkbox("Enable Spring", &joint_def.enableSpring)
im.InputFloat("Hertz ", &joint_def.hertz)
im.InputFloat("Damping Ratio", &joint_def.dampingRatio)
lower_angle := RAD2DEG * joint_def.lowerAngle
if im.SliderFloat("Lower Angle", &lower_angle, 0, 359)
{
joint_def.lowerAngle = DEG2RAD * lower_angle
}
upper_angle := RAD2DEG * joint_def.upperAngle
if im.SliderFloat("Upper Angle", &upper_angle, 0, 359)
{
joint_def.upperAngle = DEG2RAD * upper_angle
}
im.InputFloat("Max Motor Limit", &joint_def.maxMotorTorque)
im.InputFloat("Motor Speed", &joint_def.motorSpeed)
im.InputFloat("Draw Size", &joint_def.drawSize)
im.Checkbox("Enable Motor", &joint_def.enableMotor)
im.Checkbox("Enable Limit", &joint_def.enableLimit)
im.Checkbox("Collide Connected", &joint_def.collideConnected)
}
interface_edit_revolute_joint :: proc( interface : ^interface_state ) -> bool
{
level := interface.world
interface.curr_joint_type = .revoluteJoint
if len(level.revolute_joint_defs) == 0 do im.Text("No revolute joint created, Click add to create new")
if im.Button("Create new joint")
{
append(&level.revolute_joint_defs, revolt_joint_def{def = b2.DefaultRevoluteJointDef()})
interface.curr_joint_index = i32(len(level.revolute_joint_defs))
}
if im.Button("Delete current join")
{
unordered_remove(&level.revolute_joint_defs, interface.curr_joint_index)
return true
}
//Select index
if im.BeginCombo("Select joint", fmt.ctprint(interface.curr_joint_index))
{
for i in 0..<len(level.revolute_joint_defs)
{
if im.Selectable(fmt.ctprint(i), i32(i) == interface.curr_joint_index) do interface.curr_joint_index = i32(i)
}
im.EndCombo()
}
if interface.curr_joint_index >= i32(len(level.revolute_joint_defs)) do return false
joint_def := &level.revolute_joint_defs[interface.curr_joint_index]
old_def := joint_def^
if interface_edit_joint_common(cast(^joint_common)joint_def, interface)
{
//Set
body_a := interface.entities[level.static_indexes[joint_def.entity_a]].body_id
body_b := interface.entities[level.static_indexes[joint_def.entity_b]].body_id
joint_def.localAnchorA = b2.Body_GetLocalPoint(body_a, joint_def.pivot)
joint_def.localAnchorB = b2.Body_GetLocalPoint(body_b, joint_def.pivot)
return true
}
/*
Highlight the bodies here
*/
//Assuming pivot is always body b's position
//Edit pivot
if im.SliderFloat2("Pivot", &joint_def.pivot, -500, 500)
{
body_a := interface.entities[level.static_indexes[joint_def.entity_a]].body_id
body_b := interface.entities[level.static_indexes[joint_def.entity_b]].body_id
joint_def.localAnchorA = b2.Body_GetLocalPoint(body_a, joint_def.pivot)
joint_def.localAnchorB = b2.Body_GetLocalPoint(body_b, joint_def.pivot)
}
interface_edit_rev_joint_minimal(joint_def)
return old_def != joint_def^
}
interface_joints :: proc(interface: ^interface_state) -> bool
{
ret := false
interface.edit_mode = .JOINT
if im.BeginCombo("Joint type", fmt.ctprint(interface.curr_joint_type))
{
for type in b2.JointType
{
if im.Selectable(fmt.ctprint(type), type == interface.curr_joint_type) do interface.curr_joint_type = type
}
im.EndCombo()
}
if interface.curr_joint_type == .distanceJoint
{
ret |= interface_edit_distance_joint(interface)
}
if interface.curr_joint_type == .revoluteJoint
{
ret |= interface_edit_revolute_joint(interface)
}
return ret
}