This commit is contained in:
SamratGhale
2026-03-12 12:54:13 +05:45
parent d23c934662
commit 8ed94fac32
4 changed files with 222 additions and 28 deletions
+16 -7
View File
@@ -17,20 +17,24 @@ static_index_global :: struct
Don't put game's logic here Don't put game's logic here
*/ */
joint_common :: struct
{
entity_a, entity_b : static_index,
bodyIdA, bodyIdB : b2.BodyId,
}
revolt_joint_def :: struct revolt_joint_def :: struct
{ {
using def : b2.RevoluteJointDef,
//Everything else can be stored in the def //Everything else can be stored in the def
entity_a, entity_b : static_index, entity_a, entity_b : static_index,
using def : b2.RevoluteJointDef,
} }
distance_joint_def :: struct distance_joint_def :: struct
{ {
using def : b2.DistanceJointDef,
//Everything else can be stored in the def
entity_a, entity_b : static_index, entity_a, entity_b : static_index,
using def : b2.DistanceJointDef,
} }
engine_world :: struct engine_world :: struct
@@ -42,10 +46,15 @@ engine_world :: struct
relations : map[^static_index][dynamic]static_index_global `cbor:"-"`, relations : map[^static_index][dynamic]static_index_global `cbor:"-"`,
relations_serializeable : map[ static_index][dynamic]static_index_global, 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, revolute_joint_defs : [dynamic]revolt_joint_def,
distant_joint_defs : [dynamic]distance_joint_def, distant_joint_defs : [dynamic]distance_joint_def,
joints : [dynamic]b2.JointId `cbor:"-"`,
revolute_joints : [dynamic]b2.JointId,
} }
engine_entity_flags_enum :: enum u64 { engine_entity_flags_enum :: enum u64 {
+40 -16
View File
@@ -1,16 +1,18 @@
package ion package ion
import "base:runtime"
import "core:slice" import "core:slice"
import "core:container/small_array" import "core:container/small_array"
import "core:fmt" import "core:fmt"
import im "shared:odin-imgui" import im "shared:odin-imgui"
import "vendor:glfw"
import b2 "vendor:box2d" import b2 "vendor:box2d"
/* /*
This library will only account for box2d's entities editing This library will only account for box2d's entities editing
It only deals with one world_id, which means typically one level It only deals with one world_id, which means typically one level
All the interface follows a pattern
i.e. It takes interface_state pointer and returns a boolean indicating weather the world needs to be reloaded
*/ */
EditMode :: enum EditMode :: enum
@@ -18,22 +20,22 @@ EditMode :: enum
ENTITY, ENTITY,
VERTICES, VERTICES,
OVERVIEW, OVERVIEW,
JOINT,
} }
interface_state :: struct interface_state :: struct
{ {
entity_defs: [dynamic]^engine_entity_def, entity_defs : [dynamic]^engine_entity_def,
entities: [dynamic]^engine_entity, entities : [dynamic]^engine_entity,
selected_entity: ^i32, selected_entity : ^i32,
world: ^engine_world, world : ^engine_world,
state: ^engine_state, state : ^engine_state,
vertex_index : ^i32, vertex_index : ^i32,
edit_mode : EditMode, edit_mode : EditMode,
curr_revolt_joint : revolt_joint_def, curr_joint_index : i32,
curr_joint_type : b2.JointType,
curr_joint_joint : distance_joint_def,
curr_static_index : static_index_global, curr_static_index : static_index_global,
} }
@@ -251,15 +253,15 @@ interface_edit_static_index :: proc(interface:^interface_state, def: ^engine_ent
return false return false
} }
/*
interface_edit_revolute_joint :: proc(interface: ^interface_state) -> bool interface_edit_revolute_joint :: proc(interface: ^interface_state) -> bool
{ {
//Select static index and then get bodyId from it //Select static index and then get bodyId from it
//If chain shapre then allow choosing index //If chain shapre then allow choosing index
level := interface.world level := interface.world
joint_def := &interface.curr_revolt_joint joint_def := interface.curr_revolt_joint
if im.BeginCombo("Index A", fmt.ctprint(joint_def.entity_a)) if im.BeginCombo("Index A", fmt.ctprint(joint_def.entity_a))
{ {
@@ -317,13 +319,14 @@ interface_edit_revolute_joint :: proc(interface: ^interface_state) -> bool
if im.Button("Add joint") if im.Button("Add joint")
{ {
append(&level.revolute_joint_defs, interface.curr_revolt_joint) //append(&level.revolute_joint_defs, interface.curr_revolt_joint)
return true return true
} }
return false return false
} }
*/
interface_entity :: proc(interface: ^interface_state) -> bool interface_entity :: proc(interface: ^interface_state) -> bool
@@ -340,7 +343,7 @@ interface_entity :: proc(interface: ^interface_state) -> bool
if im.BeginTabItem("Entity", nil, {.Leading}) if im.BeginTabItem("Entity", nil, {.Leading})
{ {
interface.edit_mode = .ENTITY
//Flags //Flags
for flag in engine_entity_flags_enum for flag in engine_entity_flags_enum
{ {
@@ -375,12 +378,26 @@ interface_entity :: proc(interface: ^interface_state) -> bool
if im.BeginTabItem("Joints", nil , {}) if im.BeginTabItem("Joints", nil , {})
{ {
interface.edit_mode = .JOINT
if im.CollapsingHeader("Revolute Joints") 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) ret |= interface_edit_revolute_joint(interface)
} }
im.EndTabItem() im.EndTabItem()
} }
@@ -407,3 +424,10 @@ interface_all :: proc(interface: ^interface_state) -> bool
im.End() im.End()
return ret return ret
} }
+162
View File
@@ -0,0 +1,162 @@
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
*/
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
*/
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) do joint_def.entity_a = i
}
im.EndCombo()
}
im.Separator()
if im.BeginCombo("Index B", fmt.ctprint(joint_def.entity_b))
{
for i in level.static_indexes
{
if im.Selectable(fmt.ctprint(i), i == joint_def.entity_b) do joint_def.entity_b = i
}
im.EndCombo()
}
return false
}
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))
}
//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_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))
}
//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) 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("Refresh angle", &joint_def.referenceAngle, 0, 100)
im.SliderFloat("Target angle", &joint_def.targetAngle, 0, 100)
im.Checkbox("Enable Spring", &joint_def.enableSpring)
im.InputFloat("Hertz ", &joint_def.hertz)
im.InputFloat("Damping Ratio", &joint_def.dampingRatio)
im.InputFloat("Lower Angle", &joint_def.lowerAngle)
im.InputFloat("Upper Angle", &joint_def.upperAngle)
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)
return old_def != joint_def^
}
-1
View File
@@ -40,7 +40,6 @@ input_state :: struct
/* /*
This will only be called once to initilize the engine This will only be called once to initilize the engine
initilize graphics library, glfw, callbacks initilize graphics library, glfw, callbacks
*/ */
engine_init :: proc(state: ^engine_state) engine_init :: proc(state: ^engine_state)