diff --git a/entity.odin b/entity.odin index 262c71e..9e1f6d0 100644 --- a/entity.odin +++ b/entity.odin @@ -17,26 +17,6 @@ static_index_global :: struct Don't put game's logic here */ -joint_common :: struct -{ - entity_a, entity_b : static_index, - bodyIdA, bodyIdB : b2.BodyId, -} - -revolt_joint_def :: struct -{ - //Everything else can be stored in the def - entity_a, entity_b : static_index, - - using def : b2.RevoluteJointDef, -} - -distance_joint_def :: struct -{ - entity_a, entity_b : static_index, - using def : b2.DistanceJointDef, -} - engine_world :: struct { world_id : b2.WorldId, @@ -60,6 +40,7 @@ engine_world :: struct engine_entity_flags_enum :: enum u64 { POLYGON_IS_BOX, MULTI_BODIES, + CHAIN, MULTI_SHAPES, } @@ -82,7 +63,11 @@ engine_entity_def :: struct { index : static_index, - body_count : int, + //For chain bodies + //It will replicate itself to the body count and connect themselves with rev_joints + body_count : i32, + rev_joint : b2.RevoluteJointDef, + half_link_length : f32 } engine_entity :: struct { diff --git a/interface.odin b/interface.odin index 4bc75f4..5458d3a 100644 --- a/interface.odin +++ b/interface.odin @@ -40,373 +40,25 @@ interface_state :: struct curr_static_index : static_index_global, } -interface_body_def_editor :: proc(def: ^engine_entity_def) -{ - if im.BeginCombo("Body Type", fmt.ctprint(def.body_def.type)) - { - for type in b2.BodyType - { - if im.Selectable(fmt.ctprint(type), def.body_def.type == type) do def.body_def.type = type - } - im.EndCombo() - } - - im.SliderFloat2("Position", &def.body_def.position, -50, 50) - - angle := RAD2DEG * b2.Rot_GetAngle(def.body_def.rotation) - - if im.SliderFloat("Rotation", &angle, 0, 359) - { - rad := DEG2RAD * angle - def.body_def.rotation = b2.MakeRot(rad) - } - - im.SliderFloat2("Linear velocity", &def.body_def.linearVelocity, 0, 500) - im.SliderFloat("Angular velocity", &def.body_def.angularVelocity, 0, 500) - im.SliderFloat("Linear Damping", &def.body_def.linearDamping, 0, 500) - im.SliderFloat("Angular Damping", &def.body_def.angularDamping, 0, 500) - im.SliderFloat("Gravity Scale", &def.body_def.gravityScale, 0, 100) - - im.Checkbox("Fixed rotation", &def.body_def.fixedRotation) - - if im.InputText("Body Name", cstring(&def.name_buf[0]), 255) { - def.body_def.name = cstring(&def.name_buf[0]) - } -} - -interface_shape_def_editor :: proc(def: ^engine_entity_def) -> bool -{ - shape_def := &def.shape_def - - - if im.BeginCombo("Shape Type", fmt.ctprint(def.shape_type)) { - - for type in b2.ShapeType - { - if im.Selectable(fmt.ctprint(type), def.shape_type == type) - { - def.shape_type = type - } - } - - im.EndCombo() - } - - switch def.shape_type { - - case .circleShape: - { - im.SliderFloat("radius", &def.radius, 0, 40) - } - - case .polygonShape: - { - im.SliderFloat2("Size", &def.size, -500, 500) - } - - case .capsuleShape: - { - im.SliderFloat2("Center 1", &def.centers[0], -100, 100) - im.SliderFloat2("Center 2", &def.centers[0], -100, 100) - im.SliderFloat("Radius", &def.radius, 0, 40) - } - - case .chainSegmentShape: - { - im.Checkbox("is loop", &def.is_loop) - } - - case .segmentShape: - { - //TODO - } - - } - - im.SliderFloat("Density", &def.shape_def.density, 0, 100) - - if im.Button("Flip horizontally") do flip_points(small_array.slice(&def.vertices), .Horizontal) - if im.Button("Flip Vertically ") do flip_points(small_array.slice(&def.vertices), .Vertical) - - if im.TreeNode("Events and contacts") { - im.Checkbox("Is sensor", &def.shape_def.isSensor) - im.Checkbox("Enable Sensor Events", &def.shape_def.enableSensorEvents) - im.Checkbox("Enable Contact Events", &def.shape_def.enableContactEvents) - im.Checkbox("Enable Hit Events", &def.shape_def.enableHitEvents) - im.Checkbox("Enable Presolve Events", &def.shape_def.enablePreSolveEvents) - im.Checkbox("Invoke contact Creation", &def.shape_def.invokeContactCreation) - im.Checkbox("Update body mass ", &def.shape_def.updateBodyMass) - im.TreePop() - } - - if im.TreeNode("Material") { - im.Separator() - - im.SliderFloat("Friction", &def.shape_def.material.friction, 0, 1) - im.SliderFloat("Restitution", &def.shape_def.material.restitution, 0, 1) - im.SliderFloat("Rolling Resistance", &def.shape_def.material.rollingResistance, 0, 1) - im.SliderFloat("Tangent Speed", &def.shape_def.material.tangentSpeed, 0, 1) - im.InputInt("User material id", &def.shape_def.material.userMaterialId) - - //Colorpicker - - if im.TreeNode("Color") { - color_f32 := u32_to_float4(def.shape_def.material.customColor) - - if im.ColorPicker4("Custom Color", &color_f32, {.Uint8, .InputRGB}) { - def.shape_def.material.customColor = float4_to_u32(color_f32) - } - im.TreePop() - } - - im.Separator() - im.TreePop() - } - - return false -} - interface_draw_options :: proc(state: ^engine_state) { - if im.BeginTabItem("Controls") { - debug_draw := &state.draw.debug_draw + debug_draw := &state.draw.debug_draw - im.Checkbox("Shapes", &debug_draw.drawShapes) - im.Checkbox("Joints", &debug_draw.drawJoints) - im.Checkbox("Joint Extras", &debug_draw.drawJointExtras) - im.Checkbox("Bounds", &debug_draw.drawBounds) - im.Checkbox("Contact Points", &debug_draw.drawContacts) - im.Checkbox("Contact Normals", &debug_draw.drawContactNormals) - im.Checkbox("Contact Inpulses", &debug_draw.drawContactImpulses) - im.Checkbox("Contact Features", &debug_draw.drawContactFeatures) - im.Checkbox("Friction Inpulses", &debug_draw.drawFrictionImpulses) - im.Checkbox("Mass ", &debug_draw.drawMass) - im.Checkbox("Body Names", &debug_draw.drawBodyNames) - im.Checkbox("Graph Colors", &debug_draw.drawGraphColors) - im.Checkbox("Islands ", &debug_draw.drawIslands) - - im.SliderFloat("Rotation", &state.draw.cam.rotation, 0, 360) - - im.EndTabItem() - } + im.Checkbox("Shapes", &debug_draw.drawShapes) + im.Checkbox("Joints", &debug_draw.drawJoints) + im.Checkbox("Joint Extras", &debug_draw.drawJointExtras) + im.Checkbox("Bounds", &debug_draw.drawBounds) + im.Checkbox("Contact Points", &debug_draw.drawContacts) + im.Checkbox("Contact Normals", &debug_draw.drawContactNormals) + im.Checkbox("Contact Inpulses", &debug_draw.drawContactImpulses) + im.Checkbox("Contact Features", &debug_draw.drawContactFeatures) + im.Checkbox("Friction Inpulses", &debug_draw.drawFrictionImpulses) + im.Checkbox("Mass ", &debug_draw.drawMass) + im.Checkbox("Body Names", &debug_draw.drawBodyNames) + im.Checkbox("Graph Colors", &debug_draw.drawGraphColors) + im.Checkbox("Islands ", &debug_draw.drawIslands) + im.SliderFloat("Rotation", &state.draw.cam.rotation, 0, 360) } -interface_edit_static_index :: proc(interface:^interface_state, def: ^engine_entity_def) -> bool -{ - - curr_index := &interface.curr_static_index - entity := interface.entities[interface.selected_entity^] - - level := interface.world - - - if level.relations[entity.index] == nil - { - level.relations[entity.index] = {} - } - - indexes := &level.relations[entity.index] - - if im.InputInt("Index Value", &def.index) do return true - - ret := false - - if def.index != 0 - { - //For now only select from current room - - if im.BeginCombo("Edit Select index", fmt.ctprint(curr_index.index)) - { - for index in level.static_indexes - { - if im.Selectable(fmt.ctprint(index), curr_index.index == index) - { - curr_index.index = index - } - } - im.EndCombo() - } - if curr_index.index != 0 - { - if indexes != nil - { - if im.Button("Add relation") - { - if !slice.contains(indexes[:], interface.curr_static_index) - { - append(indexes, interface.curr_static_index) - } - } - } - } - - if indexes != nil{ - for val, i in indexes - { - im.Text("%d", val.index) - im.SameLine() - if im.Button("Delete") { - ordered_remove(indexes, i) - } - } - } - } - return false -} - -/* -interface_edit_revolute_joint :: proc(interface: ^interface_state) -> bool -{ - //Select static index and then get bodyId from it - //If chain shapre then allow choosing index - - level := interface.world - - joint_def := interface.curr_revolt_joint - - 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 - } - } - 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) - { - joint_def.entity_b = i - } - } - im.EndCombo() - } - - //Now box2d - - im.SliderFloat2("localAnchorA", &joint_def.localAnchorA, -5, 5) - im.SliderFloat2("localAnchorB", &joint_def.localAnchorB, -5, 5) - - //Convert to degree to radian - im.SliderFloat("Reference 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.Checkbox("Enable Limit", &joint_def.enableLimit) - - im.InputFloat("Lower Angle", &joint_def.lowerAngle) - im.InputFloat("Upper Angle", &joint_def.upperAngle) - - im.Checkbox("Enable Motor", &joint_def.enableMotor) - im.InputFloat("Moror Torque", &joint_def.maxMotorTorque) - im.InputFloat("Moror Speed", &joint_def.motorSpeed) - im.InputFloat("Draw Size", &joint_def.drawSize) - im.Checkbox("Collide Connected", &joint_def.collideConnected) - - if im.Button("Add joint") - { - //append(&level.revolute_joint_defs, interface.curr_revolt_joint) - return true - } - - - return false -} -*/ - - -interface_entity :: proc(interface: ^interface_state) -> bool -{ - - entity_selected := interface.selected_entity^ != -1 - - if entity_selected - { - def := interface.entity_defs[interface.selected_entity^] - def_old := def^ - - ret := false - - if im.BeginTabItem("Entity", nil, {.Leading}) - { - interface.edit_mode = .ENTITY - //Flags - for flag in engine_entity_flags_enum - { - contains := flag in def.entity_flags - if im.Checkbox(fmt.ctprint(flag), &contains) - { - def.entity_flags ~= {flag} - } - } - - im.Separator() - - if im.CollapsingHeader("Shape Edit") - { - interface_shape_def_editor(def) - } - - im.Separator() - - if im.CollapsingHeader("Body Edit") - { - interface_body_def_editor(def) - } - - if im.CollapsingHeader("Static Index") - { - ret |= interface_edit_static_index(interface, def) - } - - im.EndTabItem() - } - - if im.BeginTabItem("Joints", nil , {}) - { - 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) - } - im.EndTabItem() - } - - - return def^ != def_old || ret - }else{ - return false - } -} interface_all :: proc(interface: ^interface_state) -> bool { @@ -415,9 +67,26 @@ interface_all :: proc(interface: ^interface_state) -> bool { if im.BeginTabBar("Tabs") { - if interface_entity(interface) do ret = true - - interface_draw_options(interface.state) + + if im.BeginTabItem("Entity", nil, {.Leading}) + { + if interface_entity(interface) do ret = true + im.EndTabItem() + } + + + if im.BeginTabItem("Joints", nil, {}) + { + if interface_joints(interface) do ret = true + im.EndTabItem() + } + + if im.BeginTabItem("Draw Options", nil, {}) + { + interface_draw_options(interface.state) + im.EndTabItem() + } + im.EndTabBar() } } diff --git a/interface_joints.odin b/interface_joints.odin index 150dc9b..6749454 100644 --- a/interface_joints.odin +++ b/interface_joints.odin @@ -5,6 +5,7 @@ import b2 "vendor:box2d" import im "shared:odin-imgui" /* + TODO: Delete joints Angles in degree @@ -13,6 +14,27 @@ import im "shared:odin-imgui" /* 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 @@ -31,11 +53,17 @@ interface_edit_joint_common :: proc(joint_def : ^joint_common, interface: ^inter 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) do joint_def.entity_a = i + if im.Selectable(fmt.ctprint(i), i == joint_def.entity_a) + { + joint_def.entity_a = i + ret = true + } } im.EndCombo() } @@ -46,11 +74,19 @@ interface_edit_joint_common :: proc(joint_def : ^joint_common, interface: ^inter { for i in level.static_indexes { - if im.Selectable(fmt.ctprint(i), i == joint_def.entity_b) do joint_def.entity_b = i + //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 false + return ret } @@ -67,6 +103,11 @@ interface_edit_distance_joint :: proc( interface : ^interface_state ) -> bool 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 { @@ -107,6 +148,51 @@ interface_edit_distance_joint :: proc( interface : ^interface_state ) -> bool 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 { @@ -122,10 +208,17 @@ interface_edit_revolute_joint :: proc( interface : ^interface_state ) -> bool 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.. bool 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 + 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 */ - 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) + //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^ -} \ No newline at end of file +} + + +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 +} +