Editor improvements and some refactoring

This commit is contained in:
sergeypdev 2025-05-10 19:01:48 +04:00
parent f8b73786aa
commit 890ac2494a
7 changed files with 307 additions and 247 deletions

View File

@ -1,6 +1,9 @@
package game
import "core:fmt"
import lg "core:math/linalg"
import "game:physics/collision"
import "game:ui"
import rl "libs:raylib"
update_editor :: proc(es: ^Editor_State, dt: f32) {
@ -40,11 +43,21 @@ update_editor :: proc(es: ^Editor_State, dt: f32) {
clear(&es.point_selection)
}
if rl.IsKeyPressed(.G) {
es.track_edit_state = .Move
es.move_axis = .None
es.total_movement_world = {}
world_stack_push(&es.world_stack)
// es.initial_point_pos = g_mem.track.points[es.selected_track_point]
track := &world_stack_current(&es.world_stack).track
selected_count := 0
es.move_initial_pos = 0
for k in es.point_selection {
es.move_initial_pos += track.points[k]
selected_count += 1
}
if selected_count > 0 {
es.move_initial_pos /= f32(selected_count)
es.track_edit_state = .Move
es.move_axis = .None
es.total_movement_world = {}
world_stack_push(&es.world_stack)
}
}
}
}
@ -89,24 +102,53 @@ update_editor :: proc(es: ^Editor_State, dt: f32) {
camera := game_camera_3d()
mouse_delta := rl.GetMouseDelta() * 0.05
view_rotation := lg.transpose(rl.GetCameraMatrix(camera))
view_rotation[3].xyz = 0
view_proj := view_rotation * rl.MatrixOrtho(-1, 1, 1, -1, -1, 1)
mouse_pos := rl.GetMousePosition()
prev_mouse_pos := es.prev_mouse_pos
prev_ray := rl.GetScreenToWorldRay(prev_mouse_pos, camera)
ray := rl.GetScreenToWorldRay(mouse_pos, camera)
axes_buf: [2]rl.Vector3
colors_buf: [2]rl.Color
axes, _ := get_movement_axes(es.move_axis, &axes_buf, &colors_buf)
movement_world: rl.Vector3
for axis in axes {
axis_screen := (rl.Vector4{axis.x, axis.y, axis.z, 1} * view_proj).xy
axis_screen = lg.normalize0(axis_screen)
if len(axes) == 2 {
normal := lg.normalize0(lg.cross(axes[0], axes[1]))
plane := collision.plane_from_point_normal(es.move_initial_pos, normal)
movement_screen := lg.dot(axis_screen, mouse_delta) * axis_screen
movement_world +=
(rl.Vector4{movement_screen.x, movement_screen.y, 0, 1} * rl.MatrixInvert(view_proj)).xyz
_, pos1 := collision.intersect_ray_plane(
prev_ray.position,
prev_ray.direction,
plane,
)
_, pos2 := collision.intersect_ray_plane(
ray.position,
ray.direction,
plane,
)
world_delta := pos2 - pos1
world_delta -= lg.dot(world_delta, plane.normal) * plane.normal
movement_world += world_delta
} else if len(axes) == 1 {
for axis in axes {
tangent := lg.cross(es.move_initial_pos - camera.position, axis)
normal := lg.normalize0(lg.cross(axis, tangent))
plane := collision.plane_from_point_normal(es.move_initial_pos, normal)
_, pos1 := collision.intersect_ray_plane(
prev_ray.position,
prev_ray.direction,
plane,
)
_, pos2 := collision.intersect_ray_plane(
ray.position,
ray.direction,
plane,
)
world_delta := pos2 - pos1
movement_world += lg.dot(axis, world_delta) * axis
}
}
for k in es.point_selection {
@ -117,8 +159,26 @@ update_editor :: proc(es: ^Editor_State, dt: f32) {
}
}
}
}
// world := world_stack_current(&es.world_stack)
// update_world(world, dt, false)
es.prev_mouse_pos = rl.GetMousePosition()
ui_ctx := &g_mem.ui_context
if ui.window(ui_ctx, "Editor", ui.Rect{x = 500, y = 0, w = 500, h = 600}) {
cnt := ui.get_current_container(ui_ctx)
cnt.rect.w = max(cnt.rect.w, 500)
cnt.rect.h = max(cnt.rect.h, 600)
if .ACTIVE in ui.header(ui_ctx, "Details", {.EXPANDED}) {
ui.layout_row(ui_ctx, {100, -1})
ui.label(ui_ctx, "mov")
ui.label(ui_ctx, fmt.tprintf("%v", es.move_initial_pos))
}
}
world := world_stack_current(&es.world_stack)
config := World_Update_Config {
commit_physics = false,
}
update_world(world, dt, config)
}

View File

@ -222,13 +222,18 @@ Editor_State :: struct {
point_selection: map[int]bool,
track_edit_state: Track_Edit_State,
move_axis: Move_Axis,
prev_mouse_pos: rl.Vector2,
move_initial_pos: rl.Vector3,
total_movement_world: rl.Vector3,
initial_point_pos: rl.Vector3,
camera: Free_Camera,
}
editor_state_init :: proc(es: ^Editor_State, num_snapshots: int) {
world_stack_init(&es.world_stack, num_snapshots)
world_stack_current(&es.world_stack).debug_state = {
show_menu = true,
draw_physics_scene = true,
}
}
editor_state_destroy :: proc(es: ^Editor_State) {
@ -348,7 +353,12 @@ get_movement_axes :: proc(
return out_axes[0:0], out_colors[0:0]
}
update_world :: proc(world: ^World, dt: f32, single_step_physics: bool) {
World_Update_Config :: struct {
single_step_physics: bool,
commit_physics: bool,
}
update_world :: proc(world: ^World, dt: f32, config: World_Update_Config) {
if !world.pause {
car_model := assets.get_model(&g_mem.assetman, "assets/toyota_corolla_ae86_trueno.glb")
car_bounds := rl.GetModelBoundingBox(car_model)
@ -608,8 +618,8 @@ update_world :: proc(world: ^World, dt: f32, single_step_physics: bool) {
&world.physics_scene,
SOLVER_CONFIG,
dt,
commit = true,
step_mode = single_step_physics ? physics.Step_Mode.Single : physics.Step_Mode.Accumulated_Time,
commit = config.commit_physics,
step_mode = config.single_step_physics ? physics.Step_Mode.Single : physics.Step_Mode.Accumulated_Time,
)
}
}
@ -626,7 +636,11 @@ update_runtime_world :: proc(runtime_world: ^Runtime_World, dt: f32) {
if !runtime_world.rewind_simulation {
next_world := runtime_world_next_world(runtime_world)
copy_world(next_world, cur_world)
update_world(next_world, dt, should_single_step)
config := World_Update_Config {
commit_physics = true,
single_step_physics = should_single_step,
}
update_world(next_world, dt, config)
if runtime_world.commit_simulation {
runtime_world.current_world_index =
@ -676,7 +690,7 @@ collect_camera_input :: proc() -> (delta: rl.Vector2, sense: f32) {
mouse_delta := g_mem.mouse_captured ? rl.GetMouseDelta() : 0
MOUSE_SENSE :: 0.01
MOUSE_SENSE :: 0.005
GAMEPAD_SENSE :: 1
if linalg.length2(mouse_delta) > linalg.length2(gamepad_delta) {
@ -786,6 +800,14 @@ update :: proc() {
if rl.IsKeyPressed(.TAB) {
g_mem.editor = !g_mem.editor
// When switching from editor to game, copy editor world into game world
if !g_mem.editor {
es := &g_mem.es
editor_world := world_stack_current(&es.world_stack)
copy_world(runtime_world_current_world(&g_mem.runtime_world), editor_world)
}
}
if rl.IsKeyPressed(.F1) {
@ -921,8 +943,8 @@ draw_world :: proc(world: ^World) {
if world.debug_state.show_menu {
ui_ctx := &g_mem.ui_context
ui.push_font_size_style(ui_ctx, 20)
defer ui.pop_style(ui_ctx)
//ui.push_font_size_style(ui_ctx, 20)
// defer ui.pop_style(ui_ctx)
if ui.window(ui_ctx, "Debug Menu", {x = 0, y = 0, w = 200, h = 300}) {
cnt := ui.get_current_container(ui_ctx)
@ -961,7 +983,6 @@ draw_world :: proc(world: ^World) {
}
}
}
}
car_matrix := rl.QuaternionToMatrix(car_body.q)
@ -1050,9 +1071,9 @@ draw :: proc() {
for v in soa_zip(axis = axes, color = colors) {
rlgl.Color4ub(v.color.r, v.color.g, v.color.b, v.color.a)
start, end :=
es.initial_point_pos -
es.move_initial_pos -
v.axis * 100000,
es.initial_point_pos +
es.move_initial_pos +
v.axis * 100000
rlgl.Vertex3f(start.x, start.y, start.z)
@ -1251,9 +1272,8 @@ game_init :: proc() {
ui.init(&g_mem.ui_context)
g_mem.ui_context.default_style.font = ui.Font(&g_mem.default_font)
g_mem.ui_context.default_style.font_size = 32
g_mem.ui_context.text_width = ui.rl_measure_text_width
g_mem.ui_context.text_height = ui.rl_measure_text_height
g_mem.ui_context.default_style.font_size = 20
g_mem.ui_context.text_size = ui.rl_measure_text_2d
game_hot_reloaded(g_mem)
}

View File

@ -620,6 +620,13 @@ intersect_ray_triangle :: proc(
return t, normal, barycentric, true
}
intersect_ray_plane :: proc(origin, dir: Vec3, plane: Plane) -> (t: f32, pos: Vec3) {
t = (dot(plane.normal, (plane.normal * plane.dist - origin))) / dot(plane.normal, dir)
pos = origin + dir * t
return
}
intersect_segment_plane :: proc(
segment: [2]Vec3,
plane: Plane,

View File

@ -10,26 +10,115 @@ Body_Config_Inertia_Mode :: enum {
Explicit,
}
Immedate_State :: struct($T: typeid) {
handle: T,
// When was this referenced last time (frame number)
last_ref: u32,
}
Immediate_Container :: struct($T: typeid) {
items: map[u32]Immedate_State(T),
num_items: i32,
}
// When ok = true, it means we found an existing item
immediate_container_find_or_add :: proc(
c: $T/^Immediate_Container($E),
simulation_frame: u32,
id: u32,
) -> (
handle: ^E,
ok: bool,
) {
if id in c.items {
item := &c.items[id]
if item.last_ref != simulation_frame {
item.last_ref = simulation_frame
c.num_items += 1
}
handle = &item.handle
ok = true
} else {
c.num_items += 1
c.items[id] = {
handle = {},
last_ref = simulation_frame,
}
item := &c.items[id]
handle = &item.handle
ok = false
}
return
}
// Returns removed handles, allocated using allocator
immediate_container_prune :: proc(
c: $T/^Immediate_Container($E),
simulation_frame: u32,
allocator := context.temp_allocator,
) -> (
removed_handles: []E,
) {
if int(c.num_items) == len(c.items) {
return
}
num_unreferenced := len(c.items) - int(c.num_items)
assert(num_unreferenced >= 0)
removed_handles = make([]E, num_unreferenced, allocator)
items_to_remove := make([]u32, num_unreferenced, context.temp_allocator)
i := 0
for k, &v in c.items {
if v.last_ref != simulation_frame {
items_to_remove[i] = k
removed_handles[i] = v.handle
i += 1
}
}
assert(i == len(items_to_remove))
for k in items_to_remove {
delete_key(&c.items, k)
}
return
}
copy_map :: proc(dst, src: $T/^map[$K]$V) {
clear(dst)
reserve_map(dst, len(src))
for k, v in src {
dst[k] = v
}
}
immediate_container_copy :: proc(dst, src: $T/^Immediate_Container($E)) {
copy_map(&dst.items, &src.items)
dst.num_items = src.num_items
}
immediate_container_destroy :: proc(c: $T/^Immediate_Container($E)) {
delete_map(c.items)
}
immediate_body :: proc(scene: ^Scene, id: u32, config: Body_Config) -> (handle: Body_Handle) {
state := &scene.solver_state
sim_state := get_sim_state(scene)
if id in state.immedate_bodies {
body := &state.immedate_bodies[id]
if body.last_ref != state.simulation_frame {
body.last_ref = state.simulation_frame
state.num_referenced_bodies += 1
}
handle = body.handle
update_body_from_config(sim_state, get_body(sim_state, handle), config)
h, ok := immediate_container_find_or_add(&state.immediate_bodies, state.simulation_frame, id)
if ok {
update_body_from_config(sim_state, get_body(sim_state, h^), config)
} else {
state.num_referenced_bodies += 1
handle = add_body(sim_state, config)
state.immedate_bodies[id] = {
handle = handle,
last_ref = state.simulation_frame,
}
h^ = add_body(sim_state, config)
}
handle = h^
return
}
@ -41,22 +130,17 @@ immediate_suspension_constraint :: proc(
handle: Suspension_Constraint_Handle,
) {
state := &scene.solver_state
if id in state.immediate_suspension_constraints {
constraint := &state.immediate_suspension_constraints[id]
if constraint.last_ref != state.simulation_frame {
constraint.last_ref = state.simulation_frame
state.num_referenced_suspension_constraints += 1
}
handle = constraint.handle
} else {
state.num_referenced_suspension_constraints += 1
handle = add_suspension_constraint(get_sim_state(scene), {})
state.immediate_suspension_constraints[id] = {
handle = handle,
last_ref = state.simulation_frame,
}
h, ok := immediate_container_find_or_add(
&state.immediate_suspension_constraints,
state.simulation_frame,
id,
)
if !ok {
h^ = add_suspension_constraint(get_sim_state(scene), {})
}
handle = h^
update_suspension_constraint_from_config(
get_suspension_constraint(get_sim_state(scene), handle),
config,
@ -74,117 +158,36 @@ immediate_engine :: proc(
) {
state := &scene.solver_state
sim_state := get_sim_state(scene)
if id in state.immediate_engines {
engine := &state.immediate_engines[id]
if engine.last_ref != state.simulation_frame {
engine.last_ref = state.simulation_frame
state.num_referenced_engines += 1
}
handle = engine.handle
update_engine_from_config(sim_state, get_engine(sim_state, handle), config)
h, ok := immediate_container_find_or_add(&state.immediate_engines, state.simulation_frame, id)
if ok {
update_engine_from_config(sim_state, get_engine(sim_state, h^), config)
} else {
state.num_referenced_engines += 1
handle = add_engine(sim_state, config)
state.immediate_engines[id] = {
handle = handle,
last_ref = state.simulation_frame,
}
h^ = add_engine(sim_state, config)
}
handle = h^
return
}
prune_immediate :: proc(scene: ^Scene) {
tracy.Zone()
prune_immediate_bodies(scene)
prune_immediate_suspension_constraints(scene)
prune_immediate_engines(scene)
}
// TODO: Generic version
prune_immediate_bodies :: proc(scene: ^Scene) {
sim_state := get_sim_state(scene)
state := &scene.solver_state
if int(state.num_referenced_bodies) == len(state.immedate_bodies) {
return
removed_bodies := immediate_container_prune(&state.immediate_bodies, state.simulation_frame)
removed_wheels := immediate_container_prune(
&state.immediate_suspension_constraints,
state.simulation_frame,
)
removed_engines := immediate_container_prune(&state.immediate_engines, state.simulation_frame)
for handle in removed_bodies {
remove_body(sim_state, handle)
}
num_unreferenced_bodies := len(state.immedate_bodies) - int(state.num_referenced_bodies)
assert(num_unreferenced_bodies >= 0)
bodies_to_remove := make([]u32, num_unreferenced_bodies, context.temp_allocator)
i := 0
for k, &v in state.immedate_bodies {
if v.last_ref != state.simulation_frame {
bodies_to_remove[i] = k
i += 1
}
for handle in removed_wheels {
remove_suspension_constraint(sim_state, handle)
}
assert(i == len(bodies_to_remove))
for k in bodies_to_remove {
handle := state.immedate_bodies[k].handle
delete_key(&state.immedate_bodies, k)
remove_body(get_sim_state(scene), handle)
}
}
prune_immediate_suspension_constraints :: proc(scene: ^Scene) {
state := &scene.solver_state
if int(state.num_referenced_suspension_constraints) ==
len(state.immediate_suspension_constraints) {
return
}
num_unreferenced_constraints :=
len(state.immediate_suspension_constraints) -
int(state.num_referenced_suspension_constraints)
assert(num_unreferenced_constraints >= 0)
constraints_to_remove := make([]u32, num_unreferenced_constraints, context.temp_allocator)
i := 0
for k, &v in state.immediate_suspension_constraints {
if v.last_ref != state.simulation_frame {
constraints_to_remove[i] = k
i += 1
}
}
assert(i == len(constraints_to_remove))
for k in constraints_to_remove {
handle := state.immediate_suspension_constraints[k].handle
delete_key(&state.immediate_suspension_constraints, k)
remove_suspension_constraint(get_sim_state(scene), handle)
}
}
prune_immediate_engines :: proc(scene: ^Scene) {
state := &scene.solver_state
if int(state.num_referenced_engines) == len(state.immediate_engines) {
return
}
num_unreferenced_engines := len(state.immediate_engines) - int(state.num_referenced_engines)
assert(num_unreferenced_engines >= 0)
engines_to_remove := make([]u32, num_unreferenced_engines, context.temp_allocator)
i := 0
for k, &v in state.immediate_engines {
if v.last_ref != state.simulation_frame {
engines_to_remove[i] = k
i += 1
}
}
assert(i == len(engines_to_remove))
for k in engines_to_remove {
handle := state.immediate_engines[k].handle
delete_key(&state.immediate_engines, k)
remove_engine(get_sim_state(scene), handle)
for handle in removed_engines {
remove_engine(sim_state, handle)
}
}

View File

@ -30,51 +30,32 @@ Solver_Config :: struct {
}
Solver_State :: struct {
accumulated_time: f32,
accumulated_time: f32,
// Incremented when simulate is called (not simulate_step)
simulation_frame: u32,
simulation_frame: u32,
// Number of immediate bodies referenced this frame
num_referenced_bodies: i32,
num_referenced_suspension_constraints: i32,
num_referenced_engines: i32,
immedate_bodies: map[u32]Immedate_State(Body_Handle),
immediate_suspension_constraints: map[u32]Immedate_State(Suspension_Constraint_Handle),
immediate_engines: map[u32]Immedate_State(Engine_Handle),
}
copy_map :: proc(dst, src: $T/^map[$K]$V) {
clear(dst)
reserve_map(dst, len(src))
for k, v in src {
dst[k] = v
}
immediate_bodies: Immediate_Container(Body_Handle),
immediate_suspension_constraints: Immediate_Container(Suspension_Constraint_Handle),
immediate_engines: Immediate_Container(Engine_Handle),
}
copy_solver_state :: proc(dst, src: ^Solver_State) {
dst.accumulated_time = src.accumulated_time
dst.simulation_frame = src.simulation_frame
dst.num_referenced_bodies = src.num_referenced_bodies
dst.num_referenced_suspension_constraints = src.num_referenced_suspension_constraints
dst.num_referenced_engines = src.num_referenced_engines
copy_map(&dst.immedate_bodies, &src.immedate_bodies)
copy_map(&dst.immediate_suspension_constraints, &src.immediate_suspension_constraints)
copy_map(&dst.immediate_engines, &src.immediate_engines)
immediate_container_copy(&dst.immediate_bodies, &src.immediate_bodies)
immediate_container_copy(
&dst.immediate_suspension_constraints,
&src.immediate_suspension_constraints,
)
immediate_container_copy(&dst.immediate_engines, &src.immediate_engines)
}
destroy_solver_state :: proc(state: ^Solver_State) {
delete(state.immedate_bodies)
delete(state.immediate_suspension_constraints)
delete(state.immediate_engines)
}
Immedate_State :: struct($T: typeid) {
handle: T,
// When was this referenced last time (frame number)
last_ref: u32,
immediate_container_destroy(&state.immediate_bodies)
immediate_container_destroy(&state.immediate_suspension_constraints)
immediate_container_destroy(&state.immediate_engines)
}
MAX_STEPS :: 10
@ -253,9 +234,9 @@ simulate :: proc(
}
state.simulation_frame += 1
state.num_referenced_bodies = 0
state.num_referenced_suspension_constraints = 0
state.num_referenced_engines = 0
state.immediate_bodies.num_items = 0
state.immediate_suspension_constraints.num_items = 0
state.immediate_engines.num_items = 0
}
GLOBAL_PLANE :: collision.Plane {

View File

@ -241,8 +241,7 @@ Style :: struct {
Context :: struct {
/* callbacks */
text_width: proc(font: Font, font_size: i32, str: string) -> i32,
text_height: proc(font: Font, font_size: i32) -> i32,
text_size: proc(font: Font, font_size: i32, str: string) -> [2]i32,
draw_frame: proc(ctx: ^Context, rect: Rect, colorid: Color_Type),
/* core state */
default_style: Style,
@ -304,6 +303,7 @@ unclipped_rect := Rect{0, 0, 0x1000000, 0x1000000}
default_style := Style {
font = nil,
size = {68, 10},
font_size = 16,
padding = 5,
spacing = 4,
indent = 24,
@ -402,8 +402,7 @@ init :: proc(
}
begin :: proc(ctx: ^Context) {
assert(ctx.text_width != nil, "ctx.text_width is not set")
assert(ctx.text_height != nil, "ctx.text_height is not set")
assert(ctx.text_size != nil, "ctx.text_size is not set")
ctx.command_list.idx = 0
ctx.root_list.idx = 0
ctx.line_segments_num = 0
@ -792,12 +791,8 @@ draw_text :: proc(
pos: Vec2,
color: Color,
) {
rect := Rect {
pos.x,
pos.y,
ctx.text_width(font, font_size, str),
ctx.text_height(font, font_size),
}
text_size := ctx.text_size(font, font_size, str)
rect := Rect{pos.x, pos.y, text_size.x, text_size.y}
clipped := check_clip(ctx, rect)
switch clipped {
case .NONE: // okay
@ -1049,13 +1044,13 @@ draw_control_text :: proc(
pos: Vec2
font := get_style(ctx).font
font_size := get_style(ctx).font_size
tw := ctx.text_width(font, font_size, str)
ts := ctx.text_size(font, font_size, str)
push_clip_rect(ctx, rect)
pos.y = rect.y + (rect.h - ctx.text_height(font, font_size)) / 2
pos.y = rect.y + (rect.h - ts.y) / 2
if .ALIGN_CENTER in opt {
pos.x = rect.x + (rect.w - tw) / 2
pos.x = rect.x + (rect.w - ts.x) / 2
} else if .ALIGN_RIGHT in opt {
pos.x = rect.x + rect.w - tw - get_style(ctx).padding
pos.x = rect.x + rect.w - ts.x - get_style(ctx).padding
} else {
pos.x = rect.x + get_style(ctx).padding
}
@ -1109,7 +1104,7 @@ text :: proc(ctx: ^Context, text: string) {
font_size := style.font_size
color := style.colors[.TEXT]
layout_begin_column(ctx)
layout_row(ctx, {-1}, ctx.text_height(font, font_size))
layout_row(ctx, {-1}, ctx.text_size(font, font_size, text).y)
for len(text) > 0 {
w: i32
start: int
@ -1118,12 +1113,12 @@ text :: proc(ctx: ^Context, text: string) {
for ch, i in text {
if ch == ' ' || ch == '\n' {
word := text[start:i]
w += ctx.text_width(font, font_size, word)
w += ctx.text_size(font, font_size, word).x
if w > r.w && start != 0 {
end = start
break
}
w += ctx.text_width(font, font_size, text[i:i + 1])
w += ctx.text_size(font, font_size, text[i:i + 1]).x
if ch == '\n' {
end = i + 1
break
@ -1317,7 +1312,7 @@ textbox_raw :: proc(
if ctx.mouse_pos.x <
r.x +
ctx.textbox_offset +
ctx.text_width(font, font_size, string(textbuf[:i])) {
ctx.text_size(font, font_size, string(textbuf[:i])).x {
idx = i
break
}
@ -1336,10 +1331,11 @@ textbox_raw :: proc(
if ctx.focus_id == id {
text_color := style.colors[.TEXT]
sel_color := style.colors[.SELECTION_BG]
textw := ctx.text_width(font, font_size, textstr)
texth := ctx.text_height(font, font_size)
headx := ctx.text_width(font, font_size, textstr[:ctx.textbox_state.selection[0]])
tailx := ctx.text_width(font, font_size, textstr[:ctx.textbox_state.selection[1]])
text_size := ctx.text_size(font, font_size, textstr)
textw := text_size.x
texth := text_size.y
headx := ctx.text_size(font, font_size, textstr[:ctx.textbox_state.selection[0]]).x
tailx := ctx.text_size(font, font_size, textstr[:ctx.textbox_state.selection[1]]).x
ofmin := max(get_style(ctx).padding - headx, r.w - textw - get_style(ctx).padding)
ofmax := min(r.w - headx - get_style(ctx).padding, get_style(ctx).padding)
ctx.textbox_offset = clamp(ctx.textbox_offset, ofmin, ofmax)

View File

@ -3,6 +3,7 @@
package ui
import "core:log"
import "core:math"
import "core:strings"
import rl "libs:raylib"
import "libs:raylib/rlgl"
@ -13,23 +14,23 @@ _ :: log
default_atlas_texture: rl.Texture2D
rl_init :: proc() {
rl.UnloadTexture(default_atlas_texture)
default_atlas_texture = {}
rl.UnloadTexture(default_atlas_texture)
default_atlas_texture = {}
image := rl.Image{
data = &default_atlas_alpha,
width = DEFAULT_ATLAS_WIDTH,
height = DEFAULT_ATLAS_HEIGHT,
mipmaps = 1,
format = .UNCOMPRESSED_GRAYSCALE,
}
image := rl.Image {
data = &default_atlas_alpha,
width = DEFAULT_ATLAS_WIDTH,
height = DEFAULT_ATLAS_HEIGHT,
mipmaps = 1,
format = .UNCOMPRESSED_GRAYSCALE,
}
default_atlas_texture = rl.LoadTextureFromImage(image)
rl.SetTextureFilter(default_atlas_texture, .POINT)
default_atlas_texture = rl.LoadTextureFromImage(image)
rl.SetTextureFilter(default_atlas_texture, .POINT)
gl.BindTexture(gl.TEXTURE_2D, default_atlas_texture.id)
gl.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_SWIZZLE_A, gl.RED)
gl.BindTexture(gl.TEXTURE_2D, 0)
gl.BindTexture(gl.TEXTURE_2D, default_atlas_texture.id)
gl.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_SWIZZLE_A, gl.RED)
gl.BindTexture(gl.TEXTURE_2D, 0)
}
to_rl_color :: proc(c: Color) -> rl.Color {
@ -40,7 +41,7 @@ to_rl_rect :: proc(r: Rect) -> rl.Rectangle {
return rl.Rectangle{x = f32(r.x), y = f32(r.y), width = f32(r.w), height = f32(r.h)}
}
rl_measure_text_2d :: #force_inline proc(font: Font, font_size: i32, text: string) -> rl.Vector2 {
rl_measure_text_2d :: #force_inline proc(font: Font, font_size: i32, text: string) -> [2]i32 {
font := (cast(^rl.Font)font)
size := rl.MeasureTextEx(
font^ if font != nil else rl.GetFontDefault(),
@ -49,15 +50,7 @@ rl_measure_text_2d :: #force_inline proc(font: Font, font_size: i32, text: strin
f32(font_size / (font.baseSize if font != nil else 10)),
)
return size
}
rl_measure_text_width :: proc(font: Font, font_size: i32, text: string) -> i32 {
return i32(rl_measure_text_2d(font, font_size, text).x)
}
rl_measure_text_height :: proc(font: Font, font_size: i32) -> i32 {
return i32(rl_measure_text_2d(font, font_size, "A").y)
return {i32(math.ceil(size.x)), i32(math.ceil(size.y))}
}
rl_draw :: proc(ctx: ^Context) {
@ -88,13 +81,13 @@ rl_draw :: proc(ctx: ^Context) {
rl.DrawLineStrip(&segments[0], i32(len(segments)), to_rl_color(c.color))
case ^Command_Jump:
case ^Command_Icon:
src_rect := default_atlas[int(c.id)]
x := f32(c.rect.x + (c.rect.w - src_rect.w) / 2)
y := f32(c.rect.y + (c.rect.h - src_rect.h) / 2)
src_rect := default_atlas[int(c.id)]
x := f32(c.rect.x + (c.rect.w - src_rect.w) / 2)
y := f32(c.rect.y + (c.rect.h - src_rect.h) / 2)
rl.DrawTextureRec(
default_atlas_texture,
to_rl_rect(src_rect),
{x, y},
{x, y},
to_rl_color(c.color),
)
}