Init
This commit is contained in:
@@ -0,0 +1,19 @@
|
||||
# Edit2d
|
||||
|
||||
Edit2d is my attempt to make a box2d editor, where i can create, simulate, edit and save them using a single tool
|
||||
My main goal is to create game using it
|
||||
It's created using folloing tools
|
||||
|
||||
1. Odin programming language
|
||||
2. GLFW for input handling and graphics
|
||||
3. Dear ImGui for user interfaces
|
||||
4. Box2d
|
||||
|
||||
## Current Progress
|
||||
|
||||
Currently it can create, edit and save all kinds of bodies and shape
|
||||
|
||||
## Working on
|
||||
|
||||
Create edit and save different kinds of joints
|
||||
|
||||
+237
@@ -0,0 +1,237 @@
|
||||
package ion
|
||||
|
||||
import b2 "vendor:box2d"
|
||||
import array "core:container/small_array"
|
||||
|
||||
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
|
||||
*/
|
||||
|
||||
revolt_joint_def :: struct
|
||||
{
|
||||
using def : b2.RevoluteJointDef,
|
||||
|
||||
//Everything else can be stored in the def
|
||||
entity_a, entity_b : static_index,
|
||||
}
|
||||
|
||||
distance_joint_def :: struct
|
||||
{
|
||||
using def : b2.DistanceJointDef,
|
||||
|
||||
//Everything else can be stored in the def
|
||||
entity_a, entity_b : static_index,
|
||||
}
|
||||
|
||||
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,
|
||||
|
||||
revolute_joint_defs : [dynamic]revolt_joint_def,
|
||||
distant_joint_defs : [dynamic]distance_joint_def,
|
||||
|
||||
revolute_joints : [dynamic]b2.JointId,
|
||||
}
|
||||
|
||||
engine_entity_flags_enum :: enum u64 {
|
||||
POLYGON_IS_BOX,
|
||||
MULTI_BODIES,
|
||||
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 : array.Small_Array(b2.MAX_POLYGON_VERTICES, b2.Vec2),
|
||||
name_buf : [255]u8 `fmt:"-" cbor:"-"`,
|
||||
|
||||
entity_flags : engine_entity_flags,
|
||||
|
||||
index : static_index,
|
||||
|
||||
body_count : int,
|
||||
}
|
||||
|
||||
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 array.slice(&def.vertices){
|
||||
v *= def.scale
|
||||
}
|
||||
|
||||
|
||||
for v in array.slice(&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)
|
||||
}
|
||||
|
||||
slice := array.slice(&def.vertices)
|
||||
|
||||
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 array.slice(&def.vertices){
|
||||
v *= def.scale
|
||||
}
|
||||
segment : b2.Segment = {point1 = array.get(def.vertices, 0), point2 = array.get(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 array.slice(&def.vertices){
|
||||
v *= def.scale
|
||||
}
|
||||
|
||||
points := make([dynamic]b2.Vec2, 0)
|
||||
|
||||
for p, i in array.slice(&def.vertices){
|
||||
if i >= int(def.vertices.len) 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
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
+409
@@ -0,0 +1,409 @@
|
||||
package ion
|
||||
import "base:runtime"
|
||||
import "core:slice"
|
||||
import "core:container/small_array"
|
||||
import "core:fmt"
|
||||
import im "shared:odin-imgui"
|
||||
import "vendor:glfw"
|
||||
import b2 "vendor:box2d"
|
||||
|
||||
/*
|
||||
This library will only account for box2d's entities editing
|
||||
|
||||
It only deals with one world_id, which means typically one level
|
||||
*/
|
||||
|
||||
EditMode :: enum
|
||||
{
|
||||
ENTITY,
|
||||
VERTICES,
|
||||
OVERVIEW,
|
||||
}
|
||||
|
||||
interface_state :: struct
|
||||
{
|
||||
entity_defs: [dynamic]^engine_entity_def,
|
||||
entities: [dynamic]^engine_entity,
|
||||
selected_entity: ^i32,
|
||||
world: ^engine_world,
|
||||
state: ^engine_state,
|
||||
|
||||
vertex_index : ^i32,
|
||||
edit_mode : EditMode,
|
||||
|
||||
curr_revolt_joint : revolt_joint_def,
|
||||
|
||||
curr_joint_joint : distance_joint_def,
|
||||
|
||||
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
|
||||
|
||||
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()
|
||||
}
|
||||
}
|
||||
|
||||
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})
|
||||
{
|
||||
|
||||
//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 , {})
|
||||
{
|
||||
|
||||
if im.CollapsingHeader("Revolute Joints")
|
||||
{
|
||||
ret |= interface_edit_revolute_joint(interface)
|
||||
}
|
||||
|
||||
im.EndTabItem()
|
||||
}
|
||||
|
||||
|
||||
return def^ != def_old || ret
|
||||
}else{
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
interface_all :: proc(interface: ^interface_state) -> bool
|
||||
{
|
||||
ret := false
|
||||
if im.Begin("Box2d interface")
|
||||
{
|
||||
if im.BeginTabBar("Tabs")
|
||||
{
|
||||
if interface_entity(interface) do ret = true
|
||||
|
||||
interface_draw_options(interface.state)
|
||||
im.EndTabBar()
|
||||
}
|
||||
}
|
||||
im.End()
|
||||
return ret
|
||||
}
|
||||
@@ -0,0 +1,186 @@
|
||||
package ion
|
||||
|
||||
|
||||
import "core:encoding/cbor"
|
||||
import im "shared:odin-imgui"
|
||||
import "shared:odin-imgui/imgui_impl_glfw"
|
||||
import "shared:odin-imgui/imgui_impl_opengl3"
|
||||
import gl "vendor:OpenGL"
|
||||
import "vendor:glfw"
|
||||
|
||||
|
||||
|
||||
engine_state :: struct
|
||||
{
|
||||
window : glfw.WindowHandle,
|
||||
draw : Draw,
|
||||
restart, pause : bool,
|
||||
substep_count : u32,
|
||||
|
||||
//Must be set before calling ion_init
|
||||
width, height : i32,
|
||||
title : cstring,
|
||||
time : f32,
|
||||
tex_line : u32,
|
||||
drop_callback : glfw.DropProc,
|
||||
|
||||
input : input_state,
|
||||
}
|
||||
|
||||
MAX_KEYS :: 512
|
||||
|
||||
input_state :: struct
|
||||
{
|
||||
mouse_wheel : [2]f64,
|
||||
mouse : [2]f64,
|
||||
mouse_prev : [2]f64,
|
||||
|
||||
curr, prev : [MAX_KEYS]bool,
|
||||
}
|
||||
|
||||
/*
|
||||
This will only be called once to initilize the engine
|
||||
|
||||
initilize graphics library, glfw, callbacks
|
||||
*/
|
||||
engine_init :: proc(state: ^engine_state)
|
||||
{
|
||||
|
||||
assert(glfw.Init() == true)
|
||||
|
||||
glfw.WindowHint(glfw.SCALE_TO_MONITOR, 1)
|
||||
|
||||
state.window = glfw.CreateWindow(state.width, state.height, state.title, nil, nil)
|
||||
|
||||
assert(state.window != nil)
|
||||
|
||||
glfw.MakeContextCurrent(state.window)
|
||||
glfw.SwapInterval(1)
|
||||
gl.load_up_to(4, 5, glfw.gl_set_proc_address)
|
||||
|
||||
im.CHECKVERSION()
|
||||
im.CreateContext()
|
||||
|
||||
io := im.GetIO()
|
||||
|
||||
io.ConfigFlags += {
|
||||
.NavEnableKeyboard,
|
||||
.NavEnableGamepad,
|
||||
.DpiEnableScaleFonts,
|
||||
}
|
||||
|
||||
|
||||
im.StyleColorsClassic()
|
||||
|
||||
style := im.GetStyle()
|
||||
style.ChildBorderSize = 0.
|
||||
style.ChildRounding = 6
|
||||
style.TabRounding = 6
|
||||
style.FrameRounding = 6
|
||||
style.GrabRounding = 6
|
||||
style.WindowRounding = 6
|
||||
style.PopupRounding = 6
|
||||
|
||||
imgui_impl_glfw.InitForOpenGL(state.window, true)
|
||||
imgui_impl_opengl3.Init("#version 150")
|
||||
|
||||
state.draw.cam = camera_init()
|
||||
|
||||
display_w, display_h := glfw.GetFramebufferSize(state.window)
|
||||
state.draw.cam.width = display_w
|
||||
state.draw.cam.height = display_h
|
||||
state.draw.cam.zoom = 15
|
||||
state.draw.show_ui = true
|
||||
|
||||
draw_create(&state.draw, &state.draw.cam)
|
||||
|
||||
cbor.tag_register_type({
|
||||
marshal = proc(_: ^cbor.Tag_Implementation, e: cbor.Encoder, v: any) -> cbor.Marshal_Error {
|
||||
cbor._encode_u8(e.writer, 201, .Tag) or_return
|
||||
return nil;
|
||||
},
|
||||
unmarshal = proc(_: ^cbor.Tag_Implementation, d: cbor.Decoder, _: cbor.Tag_Number, v: any) -> (cbor.Unmarshal_Error) {
|
||||
return nil
|
||||
},
|
||||
}, 201, rawptr)
|
||||
}
|
||||
|
||||
update_frame :: proc(state: ^engine_state)
|
||||
{
|
||||
state.input.mouse_wheel = {}
|
||||
glfw.PollEvents()
|
||||
|
||||
keyboard_update(state)
|
||||
|
||||
gl.ClearColor(0.4, 0.5, 0.6, 1.0)
|
||||
gl.Clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT)
|
||||
|
||||
cam := &state.draw.cam
|
||||
cam.width, cam.height = glfw.GetWindowSize(state.window)
|
||||
|
||||
state.width , state.height = glfw.GetFramebufferSize(state.window)
|
||||
gl.Viewport(0, 0, state.width, state.height)
|
||||
|
||||
imgui_impl_opengl3.NewFrame()
|
||||
imgui_impl_glfw.NewFrame()
|
||||
im.NewFrame()
|
||||
}
|
||||
|
||||
end_frame :: proc(state: ^engine_state)
|
||||
{
|
||||
im.Render()
|
||||
imgui_impl_opengl3.RenderDrawData(im.GetDrawData())
|
||||
glfw.SwapBuffers(state.window)
|
||||
}
|
||||
|
||||
cleanup :: proc(state: ^engine_state)
|
||||
{
|
||||
imgui_impl_opengl3.Shutdown()
|
||||
imgui_impl_glfw.Shutdown()
|
||||
}
|
||||
|
||||
engine_should_close :: proc(state : ^engine_state) -> b32
|
||||
{
|
||||
return glfw.WindowShouldClose(state.window)
|
||||
}
|
||||
|
||||
|
||||
keyboard_update :: proc(state: ^engine_state)
|
||||
{
|
||||
state.input.mouse_prev = state.input.mouse
|
||||
|
||||
state.input.mouse.x, state.input.mouse.y = glfw.GetCursorPos(state.window)
|
||||
|
||||
state.input.prev = state.input.curr
|
||||
|
||||
//Update current states
|
||||
|
||||
for key in glfw.KEY_SPACE ..< MAX_KEYS
|
||||
{
|
||||
state.input.curr[key] = glfw.GetKey(state.window, i32(key)) == glfw.PRESS
|
||||
}
|
||||
|
||||
for key in 0..<glfw.KEY_SPACE
|
||||
{
|
||||
state.input.curr[key] = glfw.GetMouseButton(state.window, i32(key)) == glfw.PRESS
|
||||
}
|
||||
}
|
||||
|
||||
is_key_down :: #force_inline proc(state: ^engine_state, key : i32) -> bool{
|
||||
return state.input.curr[key]
|
||||
}
|
||||
|
||||
is_key_pressed :: #force_inline proc(state: ^engine_state, key : i32) -> bool{
|
||||
return state.input.curr[key] && !state.input.prev[key]
|
||||
}
|
||||
|
||||
is_key_released :: #force_inline proc(state: ^engine_state, key : i32) -> bool{
|
||||
return !state.input.curr[key] && state.input.prev[key]
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,90 @@
|
||||
package ion
|
||||
|
||||
import "core:slice"
|
||||
import b2 "vendor:box2d"
|
||||
|
||||
|
||||
saturate :: proc(f : f32) -> f32 {
|
||||
return (f < 0.0) ? 0.0 : (f > 1.0) ? 1.0 : f
|
||||
}
|
||||
|
||||
f32_to_u8_sat :: proc(val : f32) -> u8 {
|
||||
|
||||
sat := saturate(val)
|
||||
sat *= 255
|
||||
sat += 0.5
|
||||
|
||||
ret := cast(u8)sat
|
||||
return ret
|
||||
}
|
||||
|
||||
|
||||
float4_to_u32 :: proc(color : [4]f32) -> u32 {
|
||||
out : u32
|
||||
out = u32(f32_to_u8_sat(color.a)) << 24
|
||||
out |= u32(f32_to_u8_sat(color.r)) << 16
|
||||
out |= u32(f32_to_u8_sat(color.g)) << 8
|
||||
out |= u32(f32_to_u8_sat(color.b))
|
||||
return out
|
||||
}
|
||||
|
||||
|
||||
u32_to_float4 :: proc(color : u32) -> [4]f32 {
|
||||
ret : [4]f32
|
||||
ret.a = f32((color >> 24) & 0xFF) / 255.0
|
||||
ret.r = f32((color >> 16) & 0xFF) / 255.0
|
||||
ret.g = f32((color >> 8) & 0xFF) / 255.0
|
||||
ret.b = f32((color) & 0xFF) / 255.0
|
||||
return ret
|
||||
}
|
||||
|
||||
|
||||
|
||||
centroid :: proc(points: []b2.Vec2) -> b2.Vec2{
|
||||
center := b2.Vec2{0,0}
|
||||
for p in points do center += p
|
||||
center /= f32(len(points))
|
||||
return center
|
||||
}
|
||||
|
||||
|
||||
cross :: proc(o, a, b : b2.Vec2) -> f32{
|
||||
return (a.x - o.x) * (b.y - o.y) - (a.y - o.y) * (b.x - o.x)
|
||||
}
|
||||
|
||||
//For sorting
|
||||
curr_center : b2.Vec2
|
||||
|
||||
sort_points_ccw :: proc(points : []b2.Vec2){
|
||||
if len(points) == 0 do return
|
||||
|
||||
curr_center = centroid(points)
|
||||
slice.sort_by(points , proc(a, b: b2.Vec2) -> bool{
|
||||
c := cross(curr_center, a, b)
|
||||
|
||||
if abs(c) < 1e-7{
|
||||
return b2.Distance(curr_center, a) < b2.Distance(curr_center, b)
|
||||
}
|
||||
return c > 0
|
||||
})
|
||||
}
|
||||
|
||||
FlipDirection :: enum {
|
||||
Horizontal,
|
||||
Vertical,
|
||||
Both, // Flip both horizontally and vertically
|
||||
}
|
||||
|
||||
|
||||
flip_points :: proc(points: []b2.Vec2, direction : FlipDirection){
|
||||
for &vertex, i in points{
|
||||
switch direction {
|
||||
case .Horizontal:
|
||||
points[i] = b2.Vec2{-vertex.x, vertex.y}
|
||||
case .Vertical:
|
||||
points[i] = b2.Vec2{vertex.x, -vertex.y}
|
||||
case .Both:
|
||||
points[i] = b2.Vec2{-vertex.x, -vertex.y}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
#version 330
|
||||
|
||||
out vec4 FragColor;
|
||||
|
||||
uniform float time;
|
||||
uniform vec2 resolution;
|
||||
uniform vec3 baseColor;
|
||||
|
||||
|
||||
|
||||
//A simple pseudo-random function
|
||||
float random(vec2 st){
|
||||
return fract(sin(dot(st.xy, vec2(12.9898, 78.233))) * 43758.5853123);
|
||||
}
|
||||
|
||||
void main(){
|
||||
vec2 uv = gl_FragCoord.xy / resolution.xy;
|
||||
|
||||
//Create some noise
|
||||
float noise = random(uv + time * 0.5);
|
||||
|
||||
//Adjust these values to control the intensity and color of the grain
|
||||
float grainIntensity = 0.01;
|
||||
|
||||
//Mix the base color with the noise
|
||||
vec3 color = baseColor + vec3(noise * grainIntensity);
|
||||
|
||||
FragColor = vec4(color, 1.0);
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
#version 330
|
||||
|
||||
layout(location = 0) in vec2 v_position;
|
||||
|
||||
void main(void){
|
||||
gl_Position = vec4(v_position, 0.0f, 1.0f);
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
#version 330
|
||||
|
||||
in vec2 f_position;
|
||||
in vec4 f_color;
|
||||
in float f_thickness;
|
||||
|
||||
|
||||
out vec4 fragColor;
|
||||
|
||||
void main(){
|
||||
float radius = 1.0;
|
||||
|
||||
//distance to circle
|
||||
vec2 w = f_position;
|
||||
float dw = length(w);
|
||||
float d = abs(dw - radius);
|
||||
|
||||
fragColor = vec4(f_color.rgb, smoothstep(f_thickness, 0.0, d));
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
#version 330
|
||||
|
||||
uniform mat4 projectionMatrix;
|
||||
uniform float pixelScale;
|
||||
|
||||
layout(location = 0) in vec2 v_localPosition;
|
||||
layout(location = 1) in vec2 v_instancePosition;
|
||||
layout(location = 2) in float v_instanceRadius;
|
||||
layout(location = 3) in vec4 v_instanceColor;
|
||||
|
||||
|
||||
out vec2 f_position;
|
||||
out vec4 f_color;
|
||||
out float f_thickness;
|
||||
|
||||
|
||||
void main(){
|
||||
f_position = v_localPosition;
|
||||
f_color = v_instanceColor;
|
||||
float radius = v_instanceRadius;
|
||||
|
||||
//resolution.y = pixelScale * radius
|
||||
|
||||
f_thickness = 3.0f / (pixelScale * radius);
|
||||
|
||||
vec2 p = vec2(radius * v_localPosition.x, radius * v_localPosition.y) + v_instancePosition;
|
||||
|
||||
gl_Position = projectionMatrix * vec4(p, 0.0f, 1.0f);
|
||||
}
|
||||
@@ -0,0 +1,61 @@
|
||||
// SPDX-FileCopyrightText: 2024 Erin Catto
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
#version 330
|
||||
|
||||
in vec2 f_position;
|
||||
in vec4 f_color;
|
||||
in float f_length;
|
||||
in float f_thickness;
|
||||
|
||||
out vec4 color;
|
||||
|
||||
// Thanks to baz and kolyan3040 for help on this shader
|
||||
// todo this can be optimized a bit, keeping some terms for clarity
|
||||
|
||||
// https://en.wikipedia.org/wiki/Alpha_compositing
|
||||
vec4 blend_colors(vec4 front,vec4 back)
|
||||
{
|
||||
vec3 cSrc = front.rgb;
|
||||
float alphaSrc = front.a;
|
||||
vec3 cDst = back.rgb;
|
||||
float alphaDst = back.a;
|
||||
|
||||
vec3 cOut = cSrc * alphaSrc + cDst * alphaDst * (1.0 - alphaSrc);
|
||||
float alphaOut = alphaSrc + alphaDst * (1.0 - alphaSrc);
|
||||
|
||||
// remove alpha from rgb
|
||||
cOut = cOut / alphaOut;
|
||||
|
||||
return vec4(cOut, alphaOut);
|
||||
}
|
||||
|
||||
void main()
|
||||
{
|
||||
// radius in unit quad
|
||||
float radius = 0.5 * (2.0 - f_length);
|
||||
|
||||
vec4 borderColor = f_color;
|
||||
vec4 fillColor = 0.6f * borderColor;
|
||||
|
||||
vec2 v1 = vec2(-0.5 * f_length, 0);
|
||||
vec2 v2 = vec2(0.5 * f_length, 0);
|
||||
|
||||
// distance to line segment
|
||||
vec2 e = v2 - v1;
|
||||
vec2 w = f_position - v1;
|
||||
float we = dot(w, e);
|
||||
vec2 b = w - e * clamp(we / dot(e, e), 0.0, 1.0);
|
||||
float dw = length(b);
|
||||
|
||||
// SDF union of capsule and line segment
|
||||
float d = min(dw, abs(dw - radius));
|
||||
|
||||
// roll the fill alpha down at the border
|
||||
vec4 back = vec4(fillColor.rgb, fillColor.a * smoothstep(radius + f_thickness, radius, dw));
|
||||
|
||||
// roll the border alpha down from 1 to 0 across the border thickness
|
||||
vec4 front = vec4(borderColor.rgb, smoothstep(f_thickness, 0.0f, d));
|
||||
|
||||
color = blend_colors(front, back);
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
// SPDX-FileCopyrightText: 2024 Erin Catto
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
#version 330
|
||||
|
||||
uniform mat4 projectionMatrix;
|
||||
uniform float pixelScale;
|
||||
|
||||
layout(location=0) in vec2 v_localPosition;
|
||||
layout(location=1) in vec4 v_instanceTransform;
|
||||
layout(location=2) in float v_instanceRadius;
|
||||
layout(location=3) in float v_instanceLength;
|
||||
layout(location=4) in vec4 v_instanceColor;
|
||||
|
||||
out vec2 f_position;
|
||||
out vec4 f_color;
|
||||
out float f_length;
|
||||
out float f_thickness;
|
||||
|
||||
void main()
|
||||
{
|
||||
f_position = v_localPosition;
|
||||
f_color = v_instanceColor;
|
||||
|
||||
float radius = v_instanceRadius;
|
||||
float length = v_instanceLength;
|
||||
|
||||
// scale quad large enough to hold capsule
|
||||
float scale = radius + 0.5 * length;
|
||||
|
||||
// quad range of [-1, 1] implies normalize radius and length
|
||||
f_length = length / scale;
|
||||
|
||||
// resolution.y = pixelScale * scale
|
||||
f_thickness = 3.0f / (pixelScale * scale);
|
||||
|
||||
float x = v_instanceTransform.x;
|
||||
float y = v_instanceTransform.y;
|
||||
float c = v_instanceTransform.z;
|
||||
float s = v_instanceTransform.w;
|
||||
vec2 p = vec2(scale * v_localPosition.x, scale * v_localPosition.y);
|
||||
p = vec2((c * p.x - s * p.y) + x, (s * p.x + c * p.y) + y);
|
||||
gl_Position = projectionMatrix * vec4(p, 0.0, 1.0);
|
||||
}
|
||||
@@ -0,0 +1,56 @@
|
||||
// SPDX-FileCopyrightText: 2024 Erin Catto
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
#version 330
|
||||
|
||||
in vec2 f_position;
|
||||
in vec4 f_color;
|
||||
in float f_thickness;
|
||||
|
||||
out vec4 fragColor;
|
||||
|
||||
// https://en.wikipedia.org/wiki/Alpha_compositing
|
||||
vec4 blend_colors(vec4 front, vec4 back)
|
||||
{
|
||||
vec3 cSrc = front.rgb;
|
||||
float alphaSrc = front.a;
|
||||
vec3 cDst = back.rgb;
|
||||
float alphaDst = back.a;
|
||||
|
||||
vec3 cOut = cSrc * alphaSrc + cDst * alphaDst * (1.0 - alphaSrc);
|
||||
float alphaOut = alphaSrc + alphaDst * (1.0 - alphaSrc);
|
||||
cOut = cOut / alphaOut;
|
||||
|
||||
return vec4(cOut, alphaOut);
|
||||
}
|
||||
|
||||
void main()
|
||||
{
|
||||
// radius in unit quad
|
||||
float radius = 1.0;
|
||||
|
||||
// distance to axis line segment
|
||||
vec2 e = vec2(radius, 0);
|
||||
vec2 w = f_position;
|
||||
float we = dot(w, e);
|
||||
vec2 b = w - e * clamp(we / dot(e, e), 0.0, 1.0);
|
||||
float da = length(b);
|
||||
|
||||
// distance to circle
|
||||
float dw = length(w);
|
||||
float dc = abs(dw - radius);
|
||||
|
||||
// union of circle and axis
|
||||
float d = min(da, dc);
|
||||
|
||||
vec4 borderColor = f_color;
|
||||
vec4 fillColor = 0.6f * borderColor;
|
||||
|
||||
// roll the fill alpha down at the border
|
||||
vec4 back = vec4(fillColor.rgb, fillColor.a * smoothstep(radius + f_thickness, radius, dw));
|
||||
|
||||
// roll the border alpha down from 1 to 0 across the border thickness
|
||||
vec4 front = vec4(borderColor.rgb, smoothstep(f_thickness, 0.0f, d));
|
||||
|
||||
fragColor = blend_colors(front, back);
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
// SPDX-FileCopyrightText: 2024 Erin Catto
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
#version 330
|
||||
|
||||
uniform mat4 projectionMatrix;
|
||||
uniform float pixelScale;
|
||||
|
||||
layout(location = 0) in vec2 v_localPosition;
|
||||
layout(location = 1) in vec4 v_instanceTransform;
|
||||
layout(location = 2) in float v_instanceRadius;
|
||||
layout(location = 3) in vec4 v_instanceColor;
|
||||
|
||||
out vec2 f_position;
|
||||
out vec4 f_color;
|
||||
out float f_thickness;
|
||||
|
||||
void main()
|
||||
{
|
||||
f_position = v_localPosition;
|
||||
f_color = v_instanceColor;
|
||||
float radius = v_instanceRadius;
|
||||
|
||||
// resolution.y = pixelScale * radius
|
||||
f_thickness = 3.0f / (pixelScale * radius);
|
||||
|
||||
float x = v_instanceTransform.x;
|
||||
float y = v_instanceTransform.y;
|
||||
float c = v_instanceTransform.z;
|
||||
float s = v_instanceTransform.w;
|
||||
vec2 p = vec2(radius * v_localPosition.x, radius * v_localPosition.y);
|
||||
p = vec2((c * p.x - s * p.y) + x, (s * p.x + c * p.y) + y);
|
||||
gl_Position = projectionMatrix * vec4(p, 0.0f, 1.0f);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,106 @@
|
||||
// SPDX-FileCopyrightText: 2024 Erin Catto
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
#version 330
|
||||
|
||||
in vec2 f_position;
|
||||
in vec2 f_points[8];
|
||||
flat in int f_count;
|
||||
in float f_radius;
|
||||
in vec4 f_color;
|
||||
in float f_thickness;
|
||||
|
||||
out vec4 fragColor;
|
||||
|
||||
// https://en.wikipedia.org/wiki/Alpha_compositing
|
||||
vec4 blend_colors(vec4 front, vec4 back)
|
||||
{
|
||||
vec3 cSrc = front.rgb;
|
||||
float alphaSrc = front.a;
|
||||
vec3 cDst = back.rgb;
|
||||
float alphaDst = back.a;
|
||||
|
||||
vec3 cOut = cSrc * alphaSrc + cDst * alphaDst * (1.0 - alphaSrc);
|
||||
float alphaOut = alphaSrc + alphaDst * (1.0 - alphaSrc);
|
||||
|
||||
// remove alpha from rgb
|
||||
cOut = cOut / alphaOut;
|
||||
|
||||
return vec4(cOut, alphaOut);
|
||||
}
|
||||
|
||||
float cross2d(in vec2 v1, in vec2 v2)
|
||||
{
|
||||
return v1.x * v2.y - v1.y * v2.x;
|
||||
}
|
||||
|
||||
// Signed distance function for convex polygon
|
||||
float sdConvexPolygon(in vec2 p, in vec2[8] v, in int count)
|
||||
{
|
||||
// Initial squared distance
|
||||
float d = dot(p - v[0], p - v[0]);
|
||||
|
||||
// Consider query point inside to start
|
||||
float side = -1.0;
|
||||
int j = count - 1;
|
||||
for (int i = 0; i < count; ++i)
|
||||
{
|
||||
// Distance to a polygon edge
|
||||
vec2 e = v[i] - v[j];
|
||||
vec2 w = p - v[j];
|
||||
float we = dot(w, e);
|
||||
vec2 b = w - e * clamp(we / dot(e, e), 0.0, 1.0);
|
||||
float bb = dot(b, b);
|
||||
|
||||
// Track smallest distance
|
||||
if (bb < d)
|
||||
{
|
||||
d = bb;
|
||||
}
|
||||
|
||||
// If the query point is outside any edge then it is outside the entire polygon.
|
||||
// This depends on the CCW winding order of points.
|
||||
float s = cross2d(w, e);
|
||||
if (s >= 0.0)
|
||||
{
|
||||
side = 1.0;
|
||||
}
|
||||
|
||||
j = i;
|
||||
}
|
||||
|
||||
return side * sqrt(d);
|
||||
}
|
||||
|
||||
void main()
|
||||
{
|
||||
vec4 borderColor = f_color;
|
||||
vec4 fillColor = 0.6f * borderColor;
|
||||
|
||||
float dw = sdConvexPolygon(f_position, f_points, f_count);
|
||||
float d = abs(dw - f_radius);
|
||||
|
||||
// roll the fill alpha down at the border
|
||||
vec4 back = vec4(fillColor.rgb, fillColor.a * smoothstep(f_radius + f_thickness, f_radius, dw));
|
||||
|
||||
// roll the border alpha down from 1 to 0 across the border thickness
|
||||
vec4 front = vec4(borderColor.rgb, smoothstep(f_thickness, 0.0f, d));
|
||||
|
||||
fragColor = blend_colors(front, back);
|
||||
|
||||
// todo debugging
|
||||
// float resy = 3.0f / f_thickness;
|
||||
|
||||
// if (resy < 539.9f)
|
||||
// {
|
||||
// fragColor = vec4(1, 0, 0, 1);
|
||||
// }
|
||||
// else if (resy > 540.1f)
|
||||
// {
|
||||
// fragColor = vec4(0, 1, 0, 1);
|
||||
// }
|
||||
// else
|
||||
// {
|
||||
// fragColor = vec4(0, 0, 1, 1);
|
||||
// }
|
||||
}
|
||||
@@ -0,0 +1,79 @@
|
||||
// SPDX-FileCopyrightText: 2024 Erin Catto
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
#version 330
|
||||
|
||||
uniform mat4 projectionMatrix;
|
||||
uniform float pixelScale;
|
||||
|
||||
layout(location = 0) in vec2 v_localPosition;
|
||||
layout(location = 1) in vec4 v_instanceTransform;
|
||||
layout(location = 2) in vec4 v_instancePoints12;
|
||||
layout(location = 3) in vec4 v_instancePoints34;
|
||||
layout(location = 4) in vec4 v_instancePoints56;
|
||||
layout(location = 5) in vec4 v_instancePoints78;
|
||||
layout(location = 6) in int v_instanceCount;
|
||||
layout(location = 7) in float v_instanceRadius;
|
||||
layout(location = 8) in vec4 v_instanceColor;
|
||||
|
||||
out vec2 f_position;
|
||||
out vec4 f_color;
|
||||
out vec2 f_points[8];
|
||||
flat out int f_count;
|
||||
out float f_radius;
|
||||
out float f_thickness;
|
||||
|
||||
void main()
|
||||
{
|
||||
f_position = v_localPosition;
|
||||
f_color = v_instanceColor;
|
||||
|
||||
f_radius = v_instanceRadius;
|
||||
f_count = v_instanceCount;
|
||||
|
||||
f_points[0] = v_instancePoints12.xy;
|
||||
f_points[1] = v_instancePoints12.zw;
|
||||
f_points[2] = v_instancePoints34.xy;
|
||||
f_points[3] = v_instancePoints34.zw;
|
||||
f_points[4] = v_instancePoints56.xy;
|
||||
f_points[5] = v_instancePoints56.zw;
|
||||
f_points[6] = v_instancePoints78.xy;
|
||||
f_points[7] = v_instancePoints78.zw;
|
||||
|
||||
// Compute polygon AABB
|
||||
vec2 lower = f_points[0];
|
||||
vec2 upper = f_points[0];
|
||||
for (int i = 1; i < v_instanceCount; ++i)
|
||||
{
|
||||
lower = min(lower, f_points[i]);
|
||||
upper = max(upper, f_points[i]);
|
||||
}
|
||||
|
||||
vec2 center = 0.5 * (lower + upper);
|
||||
vec2 width = upper - lower;
|
||||
float maxWidth = max(width.x, width.y);
|
||||
|
||||
float scale = f_radius + 0.5 * maxWidth;
|
||||
float invScale = 1.0 / scale;
|
||||
|
||||
// Shift and scale polygon points so they fit in 2x2 quad
|
||||
for (int i = 0; i < f_count; ++i)
|
||||
{
|
||||
f_points[i] = invScale * (f_points[i] - center);
|
||||
}
|
||||
|
||||
// Scale radius as well
|
||||
f_radius = invScale * f_radius;
|
||||
|
||||
// resolution.y = pixelScale * scale
|
||||
f_thickness = 3.0f / (pixelScale * scale);
|
||||
|
||||
// scale up and transform quad to fit polygon
|
||||
float x = v_instanceTransform.x;
|
||||
float y = v_instanceTransform.y;
|
||||
float c = v_instanceTransform.z;
|
||||
float s = v_instanceTransform.w;
|
||||
vec2 p = vec2(scale * v_localPosition.x, scale * v_localPosition.y) + center;
|
||||
p = vec2((c * p.x - s * p.y) + x, (s * p.x + c * p.y) + y);
|
||||
gl_Position = projectionMatrix * vec4(p, 0.0f, 1.0f);
|
||||
}
|
||||
Reference in New Issue
Block a user