wip : trying to get layered text rendering working
This commit is contained in:
parent
55b80da8e5
commit
7d41fcc335
@ -413,7 +413,7 @@ configure_snap :: proc( ctx : ^Context, snap_width, snap_height : u32 ) {
|
||||
}
|
||||
|
||||
// ve_fontcache_load
|
||||
load_font :: proc( ctx : ^Context, label : string, data : []byte, size_px : f32 ) -> FontID
|
||||
load_font :: proc( ctx : ^Context, label : string, data : []byte, size_px : f32 ) -> (font_id : FontID)
|
||||
{
|
||||
assert( ctx != nil )
|
||||
assert( len(data) > 0 )
|
||||
@ -421,6 +421,7 @@ load_font :: proc( ctx : ^Context, label : string, data : []byte, size_px : f32
|
||||
context.allocator = backing
|
||||
|
||||
id : i32 = -1
|
||||
|
||||
for index : i32 = 0; index < i32(entries.num); index += 1 {
|
||||
if entries.data[index].used do continue
|
||||
id = index
|
||||
@ -435,6 +436,7 @@ load_font :: proc( ctx : ^Context, label : string, data : []byte, size_px : f32
|
||||
entry := & entries.data[ id ]
|
||||
{
|
||||
using entry
|
||||
|
||||
parser_info = parser_load_font( & parser_ctx, label, data )
|
||||
// assert( parser_info != nil, "VEFontCache.load_font: Failed to load font info from parser" )
|
||||
|
||||
@ -448,9 +450,12 @@ load_font :: proc( ctx : ^Context, label : string, data : []byte, size_px : f32
|
||||
|
||||
shaper_info = shaper_load_font( & shaper_ctx, label, data, transmute(rawptr) id )
|
||||
// assert( shaper_info != nil, "VEFontCache.load_font: Failed to load font from shaper")
|
||||
|
||||
return id
|
||||
}
|
||||
entry.id = FontID(id)
|
||||
ctx.entries.data[ id ].id = FontID(id)
|
||||
|
||||
font_id = FontID(id)
|
||||
return
|
||||
}
|
||||
|
||||
// ve_fontcache_unload
|
||||
@ -740,7 +745,8 @@ measure_text_size :: proc( ctx : ^Context, font : FontID, text_utf8 : string ) -
|
||||
cast(f32) cast(u32) (f32(bounds_width) * entry.size_scale - f32(atlas.glyph_padding)),
|
||||
cast(f32) cast(u32) (f32(bounds_height) * entry.size_scale - f32(atlas.glyph_padding)),
|
||||
}
|
||||
measured += bounds
|
||||
measured.x += bounds.x
|
||||
measured.y = max(measured.y, bounds.y)
|
||||
}
|
||||
|
||||
return measured
|
||||
|
@ -208,6 +208,9 @@ parser_get_glyph_box :: proc( font : ^ParserFontInfo, glyph_index : Glyph ) -> (
|
||||
|
||||
case .STB_TrueType:
|
||||
x0, y0, x1, y1 : i32
|
||||
// {
|
||||
// success := stbtt.InitFont( & font.stbtt_info, raw_data(data), 0 )
|
||||
// }
|
||||
success := cast(bool) stbtt.GetGlyphBox( & font.stbtt_info, i32(glyph_index), & x0, & y0, & x1, & y1 )
|
||||
assert( success )
|
||||
|
||||
|
@ -19,6 +19,22 @@ intersects_range2 :: #force_inline proc "contextless" ( a, b: Range2 ) -> bool
|
||||
return true;
|
||||
}
|
||||
|
||||
// AABB: Separating Axis Theorem
|
||||
overlap_range2 :: #force_inline proc "contextless" ( a, b: Range2 ) -> bool
|
||||
{
|
||||
// Check if there's no overlap on the x-axis
|
||||
if a.max.x <= b.min.x || b.max.x <= a.min.x {
|
||||
return false; // No overlap on x-axis means no intersection
|
||||
}
|
||||
// Check if there's no overlap on the y-axis
|
||||
if a.max.y <= b.min.y || b.max.y <= a.min.y {
|
||||
return false; // No overlap on y-axis means no intersection
|
||||
}
|
||||
// If neither of the above conditions are true, there's at least a partial overlap
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
// TODO(Ed): Do we need this? Also does it even work (looks unfinished)?
|
||||
is_within_screenspace :: #force_inline proc "contextless" ( pos : Vec2 ) -> b32 {
|
||||
state := get_state(); using state
|
||||
|
@ -276,13 +276,15 @@ startup :: proc( prof : ^SpallProfiler, persistent_mem, frame_mem, transient_mem
|
||||
path_arial_unicode_ms := strings.concatenate( { Path_Assets, "Arial Unicode MS.ttf" } )
|
||||
font_arial_unicode_ms = font_load( path_arial_unicode_ms, 24.0, "Arial_Unicode_MS" )
|
||||
|
||||
default_font = font_firacode
|
||||
default_font = font_rec_mono_semicasual_reg
|
||||
log( "Default font loaded" )
|
||||
}
|
||||
|
||||
// Setup the screen ui state
|
||||
if true
|
||||
{
|
||||
profile("screen ui")
|
||||
|
||||
ui_startup( & screen_ui.base, cache_allocator = persistent_slab_allocator() )
|
||||
ui_floating_startup( & screen_ui.floating, 1 * Kilobyte, 1 * Kilobyte, persistent_slab_allocator(), "screen ui floating manager" )
|
||||
|
||||
@ -298,6 +300,7 @@ startup :: proc( prof : ^SpallProfiler, persistent_mem, frame_mem, transient_mem
|
||||
// TODO(Ed): This will eventually have to occur when the user either creates or loads a workspace. I don't know
|
||||
if true
|
||||
{
|
||||
profile("project setup")
|
||||
using project
|
||||
path = str_intern("./")
|
||||
name = str_intern( "First Project" )
|
||||
@ -437,7 +440,7 @@ hot_reload :: proc( prof : ^SpallProfiler, persistent_mem, frame_mem, transient_
|
||||
slab_reload( frame_slab, frame_allocator())
|
||||
slab_reload( transient_slab, transient_allocator())
|
||||
|
||||
// ui_reload( & get_state().project.workspace.ui, cache_allocator = persistent_slab_allocator() )
|
||||
ui_reload( & get_state().project.workspace.ui, cache_allocator = persistent_slab_allocator() )
|
||||
|
||||
log("Module reloaded")
|
||||
}
|
||||
|
@ -1,6 +1,7 @@
|
||||
package sectr
|
||||
|
||||
import "core:math"
|
||||
import lalg "core:math/linalg"
|
||||
import "core:time"
|
||||
|
||||
import ve "codebase:font/VEFontCache"
|
||||
@ -18,6 +19,8 @@ RenderState :: struct {
|
||||
pass_actions : PassActions,
|
||||
}
|
||||
|
||||
#region("Draw Helpers")
|
||||
|
||||
gp_set_color :: #force_inline proc( color : RGBA8 ) {
|
||||
color := normalize_rgba8(color);
|
||||
gp.set_color( color.r, color.g, color.b, color.a )
|
||||
@ -26,14 +29,14 @@ gp_set_color :: #force_inline proc( color : RGBA8 ) {
|
||||
draw_filled_circle :: proc(x, y, radius: f32, edges: int) {
|
||||
if edges < 3 do return // Need at least 3 edges to form a shape
|
||||
|
||||
triangles := make([]gp.Triangle, edges)
|
||||
// defer delete(triangles)
|
||||
|
||||
center := gp.Point{x, y}
|
||||
|
||||
for i in 0..<edges {
|
||||
angle1 := 2 * math.PI * f32(i) / f32(edges)
|
||||
angle2 := 2 * math.PI * f32(i+1) / f32(edges)
|
||||
triangles := make([]gp.Triangle, edges)
|
||||
center := gp.Point{x, y}
|
||||
edge_quotient := 1 / f32(edges)
|
||||
angle_factor := 2 * math.PI * edge_quotient
|
||||
for edge_id in 0..< edges
|
||||
{
|
||||
angle1 := f32(edge_id ) * angle_factor
|
||||
angle2 := f32(edge_id +1) * angle_factor
|
||||
|
||||
p1 := gp.Point{
|
||||
x + radius * math.cos(angle1),
|
||||
@ -43,21 +46,34 @@ draw_filled_circle :: proc(x, y, radius: f32, edges: int) {
|
||||
x + radius * math.cos(angle2),
|
||||
y + radius * math.sin(angle2),
|
||||
}
|
||||
|
||||
triangles[i] = gp.Triangle{center, p1, p2}
|
||||
triangles[edge_id] = gp.Triangle{center, p1, p2}
|
||||
}
|
||||
|
||||
gp.draw_filled_triangles(raw_data(triangles), u32(len(triangles)))
|
||||
}
|
||||
|
||||
// Draw text using a string and normalized screen coordinates
|
||||
draw_rect_border :: proc( rect : Range2, border_width: f32)
|
||||
{
|
||||
rect_size := rect.max - rect.min
|
||||
border_width := lalg.min(border_width, min(rect_size.x, rect_size.y) * 0.5)
|
||||
|
||||
top := gp.Rect{ rect.min.x, rect.min.y, rect_size.x, border_width }
|
||||
bottom := gp.Rect{ rect.min.x, rect.max.y - border_width, rect_size.x, border_width }
|
||||
left := gp.Rect{ rect.min.x, rect.min.y + border_width, border_width, rect_size.y - 2 * border_width }
|
||||
right := gp.Rect{ rect.max.x - border_width, rect.min.y + border_width, border_width, rect_size.y - 2 * border_width }
|
||||
|
||||
borders := []gp.Rect{ top, bottom, left, right }
|
||||
gp.draw_filled_rects( raw_data(borders), u32(len(borders)) )
|
||||
}
|
||||
|
||||
// Draw text using a string and normalized render coordinates
|
||||
draw_text_string_pos_norm :: proc( content : string, id : FontID, size : f32, pos : Vec2, color := Color_White )
|
||||
{
|
||||
state := get_state(); using state
|
||||
width := app_window.extent.x * 2
|
||||
height := app_window.extent.y * 2
|
||||
|
||||
ve_id := font_provider_resolve_draw_id( id )
|
||||
ve_id := font_provider_resolve_draw_id( id, size )
|
||||
color_norm := normalize_rgba8(color)
|
||||
|
||||
ve.set_colour( & font_provider_data.ve_font_cache, color_norm )
|
||||
@ -68,18 +84,26 @@ draw_text_string_pos_norm :: proc( content : string, id : FontID, size : f32, po
|
||||
// Draw text using a string and extent-based screen coordinates
|
||||
draw_text_string_pos_extent :: proc( content : string, id : FontID, size : f32, pos : Vec2, color := Color_White )
|
||||
{
|
||||
state := get_state(); using state
|
||||
extent := app_window.extent
|
||||
|
||||
normalized_pos := pos / extent
|
||||
profile(#procedure)
|
||||
state := get_state(); using state
|
||||
screen_size := app_window.extent * 2
|
||||
render_pos := screen_to_render_pos(pos)
|
||||
normalized_pos := render_pos * (1.0 / screen_size)
|
||||
draw_text_string_pos_norm( content, id, size, normalized_pos, color )
|
||||
}
|
||||
|
||||
#endregion("Draw Helpers")
|
||||
|
||||
render :: proc()
|
||||
{
|
||||
profile(#procedure)
|
||||
state := get_state(); using state // TODO(Ed): Prefer passing static context to through the callstack
|
||||
|
||||
ve.flush_draw_list( & font_provider_data.ve_font_cache )
|
||||
font_provider_data.vbuf_layer_offset = 0
|
||||
font_provider_data.ibuf_layer_offset = 0
|
||||
font_provider_data.calls_layer_offset = 0
|
||||
|
||||
clear_pass := gfx.Pass { action = render_data.pass_actions.bg_clear_black, swapchain = sokol_glue.swapchain() }
|
||||
clear_pass.action.colors[0].clear_value = transmute(gfx.Color) normalize_rgba8( config.color_theme.bg )
|
||||
|
||||
@ -91,7 +115,6 @@ render :: proc()
|
||||
render_mode_screenspace()
|
||||
|
||||
gfx.commit()
|
||||
ve.flush_draw_list( & font_provider_data.ve_font_cache )
|
||||
}
|
||||
|
||||
// TODO(Ed): Eventually this needs to become a 'viewport within a UI'
|
||||
@ -108,21 +131,25 @@ render_mode_2d_workspace :: proc()
|
||||
|
||||
cam_zoom_ratio := 1.0 / cam.zoom
|
||||
|
||||
gp.begin( i32(screen_size.x), i32(screen_size.y) )
|
||||
gp.viewport(0, 0, i32(screen_size.x), i32(screen_size.y))
|
||||
gp.project( -screen_extent.x, screen_extent.x, screen_extent.y, -screen_extent.y )
|
||||
Render_Reference_Dots:
|
||||
{
|
||||
profile("render_reference_dots (workspace)")
|
||||
gp.begin( i32(screen_size.x), i32(screen_size.y) )
|
||||
gp.viewport(0, 0, i32(screen_size.x), i32(screen_size.y))
|
||||
gp.project( -screen_extent.x, screen_extent.x, screen_extent.y, -screen_extent.y )
|
||||
|
||||
gp.translate( cam.position.x * cam.zoom, cam.position.y * cam.zoom )
|
||||
gp.scale( cam.zoom, cam.zoom )
|
||||
gp.translate( cam.position.x * cam.zoom, cam.position.y * cam.zoom )
|
||||
gp.scale( cam.zoom, cam.zoom )
|
||||
|
||||
gp_set_color(Color_White)
|
||||
draw_filled_circle(0, 0, 4, 24)
|
||||
gp_set_color(Color_White)
|
||||
draw_filled_circle(0, 0, 4, 24)
|
||||
|
||||
// Enqueue gp pass to sokol gfx
|
||||
gfx.begin_pass( gfx.Pass { action = render_data.pass_actions.empty_action, swapchain = sokol_glue.swapchain() })
|
||||
gp.flush()
|
||||
gp.end()
|
||||
gfx.end_pass()
|
||||
// Enqueue gp pass to sokol gfx
|
||||
gfx.begin_pass( gfx.Pass { action = render_data.pass_actions.empty_action, swapchain = sokol_glue.swapchain() })
|
||||
gp.flush()
|
||||
gp.end()
|
||||
gfx.end_pass()
|
||||
}
|
||||
}
|
||||
|
||||
render_mode_screenspace :: proc()
|
||||
@ -143,6 +170,7 @@ render_mode_screenspace :: proc()
|
||||
|
||||
Render_Reference_Dots:
|
||||
{
|
||||
profile("render_reference_dots (screen)")
|
||||
gp.begin( i32(screen_size.x), i32(screen_size.y) )
|
||||
gp.viewport(0, 0, i32(screen_size.x), i32(screen_size.y))
|
||||
gp.project( -screen_extent.x, screen_extent.x, screen_extent.y, -screen_extent.y )
|
||||
@ -163,7 +191,6 @@ render_mode_screenspace :: proc()
|
||||
gfx.end_pass()
|
||||
}
|
||||
|
||||
|
||||
debug_draw_text :: proc( content : string, pos : Vec2, size : f32, color := Color_White, font : FontID = Font_Default )
|
||||
{
|
||||
state := get_state(); using state
|
||||
@ -194,27 +221,30 @@ render_mode_screenspace :: proc()
|
||||
screen_corners := screen_get_corners()
|
||||
|
||||
position := screen_corners.top_left
|
||||
position.x = 0
|
||||
position.y -= debug.draw_debug_text_y
|
||||
|
||||
content := str_fmt_buffer( draw_text_scratch[:], format, ..args )
|
||||
debug_draw_text( content, position, 12.0 )
|
||||
|
||||
debug.draw_debug_text_y += 12
|
||||
text_size := measure_text_size( content, default_font, 14.0, 0.0 )
|
||||
debug_draw_text( content, position, 14.0 )
|
||||
|
||||
debug.draw_debug_text_y += text_size.y + 4
|
||||
}
|
||||
|
||||
debug.debug_text_vis = true
|
||||
if debug.debug_text_vis
|
||||
{
|
||||
profile("debug_text_vis")
|
||||
fps_size : f32 = 14.0
|
||||
fps_msg := str_fmt( "FPS: %0.2f", fps_avg)
|
||||
fps_msg_width := measure_text_size( fps_msg, default_font, 12.0, 0.0 ).x
|
||||
fps_msg_pos := screen_get_corners().top_right - { fps_msg_width, 0 } - { 5, 5 }
|
||||
debug_draw_text( fps_msg, fps_msg_pos, 38.0, color = Color_Red )
|
||||
fps_msg_size := measure_text_size( fps_msg, default_font, fps_size, 0.0 )
|
||||
fps_msg_pos := screen_get_corners().top_right - { fps_msg_size.x * 2, fps_msg_size.y }
|
||||
debug_draw_text( fps_msg, fps_msg_pos, fps_size, color = Color_Red )
|
||||
|
||||
debug_text( "Screen Width : %v", screen_size.x )
|
||||
debug_text( "Screen Height: %v", screen_size.y )
|
||||
debug_text( "frametime_target_ms : %f ms", frametime_target_ms )
|
||||
debug_text( "frametime : %0.3f ms", frametime_delta32() )
|
||||
debug_text( "frametime (work) : %0.3f ms", frametime_delta_ms )
|
||||
debug_text( "frametime_last_elapsed_ms : %f ms", frametime_elapsed_ms )
|
||||
if replay.mode == ReplayMode.Record {
|
||||
debug_text( "Recording Input")
|
||||
@ -224,7 +254,7 @@ render_mode_screenspace :: proc()
|
||||
}
|
||||
debug_text("Zoom Target: %v", project.workspace.zoom_target)
|
||||
|
||||
if true
|
||||
if false
|
||||
{
|
||||
using input_events
|
||||
|
||||
@ -294,12 +324,99 @@ render_screen_ui :: proc()
|
||||
profile(#procedure)
|
||||
state := get_state(); using state // TODO(Ed): Prefer passing static context to through the callstack
|
||||
|
||||
screen_extent := app_window.extent
|
||||
screen_size := app_window.extent * 2
|
||||
screen_ratio := screen_size.x * ( 1.0 / screen_size.y )
|
||||
|
||||
ui := & screen_ui
|
||||
|
||||
render_list := array_to_slice( ui.render_list )
|
||||
|
||||
gp.begin( i32(screen_size.x), i32(screen_size.y) )
|
||||
gp.viewport(0, 0, i32(screen_size.x), i32(screen_size.y))
|
||||
gp.project( -screen_extent.x, screen_extent.x, screen_extent.y, -screen_extent.y )
|
||||
|
||||
text_enqueued : b32 = false
|
||||
shape_enqueued : b32 = false
|
||||
|
||||
for entry, id in render_list
|
||||
{
|
||||
if entry.layer_signal
|
||||
{
|
||||
profile("render ui layer")
|
||||
gfx.begin_pass( gfx.Pass { action = render_data.pass_actions.empty_action, swapchain = sokol_glue.swapchain() })
|
||||
gp.flush()
|
||||
gp.end()
|
||||
gfx.end_pass()
|
||||
|
||||
if text_enqueued {
|
||||
render_text_layer()
|
||||
}
|
||||
|
||||
gp.begin( i32(screen_size.x), i32(screen_size.y) )
|
||||
gp.viewport(0, 0, i32(screen_size.x), i32(screen_size.y))
|
||||
gp.project( -screen_extent.x, screen_extent.x, screen_extent.y, -screen_extent.y )
|
||||
continue
|
||||
}
|
||||
using entry
|
||||
|
||||
profile("enqueue box")
|
||||
|
||||
GP_Render:
|
||||
{
|
||||
profile("draw_shapes")
|
||||
|
||||
draw_rect :: proc( rect : Range2, color : RGBA8 ) {
|
||||
using rect
|
||||
gp_set_color( color )
|
||||
|
||||
size := max - min
|
||||
position := min
|
||||
gp.draw_filled_rect( position.x, position.y, size.x, size.y )
|
||||
}
|
||||
|
||||
if style.bg_color.a != 0
|
||||
{
|
||||
draw_rect( bounds, style.bg_color )
|
||||
shape_enqueued = true
|
||||
}
|
||||
|
||||
if style.border_color.a != 0 && border_width > 0 {
|
||||
gp_set_color( style.border_color )
|
||||
draw_rect_border( bounds, border_width )
|
||||
shape_enqueued = true
|
||||
}
|
||||
|
||||
gp_set_color(Color_Red)
|
||||
draw_filled_circle(bounds.min.x, bounds.min.y, 3, 24)
|
||||
shape_enqueued = true
|
||||
|
||||
gp_set_color(Color_Blue)
|
||||
draw_filled_circle(bounds.max.x, bounds.max.y, 3, 24)
|
||||
shape_enqueued = true
|
||||
}
|
||||
|
||||
if len(text.str) > 0 && style.font.key != 0 {
|
||||
draw_text_string_pos_extent( text.str, default_font, font_size, computed.text_pos, style.text_color )
|
||||
text_enqueued = true
|
||||
}
|
||||
}
|
||||
|
||||
if shape_enqueued {
|
||||
gfx.begin_pass( gfx.Pass { action = render_data.pass_actions.empty_action, swapchain = sokol_glue.swapchain() })
|
||||
gp.flush()
|
||||
gp.end()
|
||||
gfx.end_pass()
|
||||
}
|
||||
|
||||
if text_enqueued {
|
||||
render_text_layer()
|
||||
}
|
||||
}
|
||||
|
||||
render_text_layer :: proc()
|
||||
{
|
||||
profile("VEFontCache: render frame")
|
||||
profile("VEFontCache: render text layer")
|
||||
|
||||
Bindings :: gfx.Bindings
|
||||
Range :: gfx.Range
|
||||
@ -309,28 +426,38 @@ render_text_layer :: proc()
|
||||
font_provider := state.font_provider_data
|
||||
using font_provider
|
||||
|
||||
ve.optimize_draw_list( & ve_font_cache )
|
||||
// ve.optimize_draw_list( & ve_font_cache )
|
||||
draw_list := ve.get_draw_list( & ve_font_cache )
|
||||
|
||||
draw_list_vert_slice := array_to_slice(draw_list.vertices)
|
||||
draw_list_index_slice := array_to_slice(draw_list.indices)
|
||||
draw_list_calls_slice := array_to_slice(draw_list.calls)
|
||||
|
||||
gfx.update_buffer( draw_list_vbuf, Range{ draw_list.vertices.data, draw_list.vertices.num * size_of(ve.Vertex) })
|
||||
gfx.update_buffer( draw_list_ibuf, Range{ draw_list.indices.data, draw_list.indices.num * size_of(u32) })
|
||||
vbuf_layer_slice := draw_list_vert_slice [ vbuf_layer_offset : ]
|
||||
ibuf_layer_slice := draw_list_index_slice[ ibuf_layer_offset : ]
|
||||
calls_layer_slice := draw_list_calls_slice[ calls_layer_offset : ]
|
||||
|
||||
draw_list_call_slice := array_to_slice(draw_list.calls)
|
||||
for & draw_call in array_to_slice(draw_list.calls)
|
||||
gfx.append_buffer( draw_list_vbuf, Range{ raw_data(vbuf_layer_slice), cast(u64) len(vbuf_layer_slice) * size_of(ve.Vertex) })
|
||||
gfx.append_buffer( draw_list_ibuf, Range{ raw_data(ibuf_layer_slice), cast(u64) len(ibuf_layer_slice) * size_of(u32) })
|
||||
|
||||
vbuf_layer_offset = cast(u64) len(draw_list_vert_slice)
|
||||
ibuf_layer_offset = cast(u64) len(draw_list_index_slice)
|
||||
calls_layer_offset = cast(u64) len(draw_list_calls_slice)
|
||||
|
||||
for & draw_call in calls_layer_slice
|
||||
{
|
||||
watch := draw_call
|
||||
// profile("VEFontCache: draw call")
|
||||
|
||||
num_indices := draw_call.end_index - draw_call.start_index
|
||||
|
||||
switch draw_call.pass
|
||||
{
|
||||
// 1. Do the glyph rendering pass
|
||||
// Glyphs are first rendered to an intermediate 2k x 512px R8 texture
|
||||
case .Glyph:
|
||||
// profile("VEFontCache: draw call: glyph")
|
||||
if (draw_call.end_index - draw_call.start_index) == 0 && ! draw_call.clear_before_draw {
|
||||
if num_indices == 0 && ! draw_call.clear_before_draw {
|
||||
continue
|
||||
}
|
||||
|
||||
@ -366,7 +493,7 @@ render_text_layer :: proc()
|
||||
// A simple 16-tap box downsample shader is then used to blit from this intermediate texture to the final atlas location
|
||||
case .Atlas:
|
||||
// profile("VEFontCache: draw call: atlas")
|
||||
if (draw_call.end_index - draw_call.start_index) == 0 && ! draw_call.clear_before_draw {
|
||||
if num_indices == 0 && ! draw_call.clear_before_draw {
|
||||
continue
|
||||
}
|
||||
|
||||
@ -407,7 +534,7 @@ render_text_layer :: proc()
|
||||
case .None: fallthrough
|
||||
case .Target: fallthrough
|
||||
case .Target_Uncached:
|
||||
if (draw_call.end_index - draw_call.start_index) == 0 && ! draw_call.clear_before_draw {
|
||||
if num_indices == 0 && ! draw_call.clear_before_draw {
|
||||
continue
|
||||
}
|
||||
|
||||
@ -455,8 +582,7 @@ render_text_layer :: proc()
|
||||
})
|
||||
}
|
||||
|
||||
if (draw_call.end_index - draw_call.start_index) != 0 {
|
||||
num_indices := draw_call.end_index - draw_call.start_index
|
||||
if num_indices != 0 {
|
||||
gfx.draw( draw_call.start_index, num_indices, 1 )
|
||||
}
|
||||
|
||||
|
@ -195,7 +195,7 @@ update :: proc( delta_time : f64 ) -> b32
|
||||
if debug_actions.cam_mouse_pan
|
||||
{
|
||||
if is_within_screenspace(input.mouse.pos) {
|
||||
pan_velocity := input.mouse.delta * vec2(1, -1) * ( 1 / cam.zoom )
|
||||
pan_velocity := input.mouse.delta * ( 1 / cam.zoom )
|
||||
cam.position += pan_velocity
|
||||
}
|
||||
}
|
||||
@ -204,7 +204,7 @@ update :: proc( delta_time : f64 ) -> b32
|
||||
|
||||
// TODO(Ed): We need input buffer so that we can consume input actions based on the UI with priority
|
||||
|
||||
// ui_screen_tick()
|
||||
ui_screen_tick()
|
||||
|
||||
//region WorkspaceImgui Tick
|
||||
if false
|
||||
|
@ -37,6 +37,10 @@ FontProviderData :: struct
|
||||
draw_list_vbuf : sokol_gfx.Buffer,
|
||||
draw_list_ibuf : sokol_gfx.Buffer,
|
||||
|
||||
vbuf_layer_offset : u64,
|
||||
ibuf_layer_offset : u64,
|
||||
calls_layer_offset : u64,
|
||||
|
||||
glyph_shader : sokol_gfx.Shader,
|
||||
atlas_shader : sokol_gfx.Shader,
|
||||
screen_shader : sokol_gfx.Shader,
|
||||
@ -547,6 +551,11 @@ font_load :: proc(path_file : string,
|
||||
def, set_error := hmap_chained_set(font_cache, key, FontDef{})
|
||||
verify( set_error == AllocatorError.None, "Failed to add new font entry to cache" )
|
||||
|
||||
default_size := default_size
|
||||
if default_size < 0 {
|
||||
default_size = Font_Default_Point_Size
|
||||
}
|
||||
|
||||
def.path_file = path_file
|
||||
def.default_size = default_size
|
||||
|
||||
@ -555,7 +564,8 @@ font_load :: proc(path_file : string,
|
||||
// logf("Loading at size %v", font_size)
|
||||
id := (font_size / Font_Size_Interval) + (font_size % Font_Size_Interval)
|
||||
ve_id := & def.size_table[id - 1]
|
||||
ve_id^ = ve.load_font( & provider_data.ve_font_cache, desired_id, font_data, 14.0 )
|
||||
ve_ret_id := ve.load_font( & provider_data.ve_font_cache, desired_id, font_data, f32(font_size) )
|
||||
(ve_id^) = ve_ret_id
|
||||
}
|
||||
|
||||
fid := FontID { key, desired_id }
|
||||
@ -570,8 +580,9 @@ font_provider_resolve_draw_id :: proc( id : FontID, size := Font_Use_Default_Siz
|
||||
|
||||
def := hmap_chained_get( font_provider_data.font_cache, id.key )
|
||||
size := size == 0.0 ? f32(def.default_size) : size
|
||||
// size :f32 = 14.0
|
||||
even_size := math.round(size * (1.0 / f32(Font_Size_Interval))) * f32(Font_Size_Interval)
|
||||
resolved_size := clamp( i32( even_size), 2, Font_Largest_Px_Size )
|
||||
resolved_size := clamp( i32( even_size), 14, Font_Largest_Px_Size )
|
||||
|
||||
id := (resolved_size / Font_Size_Interval) + (resolved_size % Font_Size_Interval)
|
||||
ve_id := def.size_table[ id - 1 ]
|
||||
|
@ -164,6 +164,7 @@ import "codebase:grime"
|
||||
array_to_slice :: grime.array_to_slice
|
||||
array_init :: grime.array_init
|
||||
array_append :: grime.array_append
|
||||
array_append_value :: grime.array_append_value
|
||||
array_append_array :: grime.array_append_array
|
||||
array_append_at :: grime.array_append_at
|
||||
array_clear :: grime.array_clear
|
||||
|
@ -279,7 +279,7 @@ poll_input_events :: proc( input, prev_input : ^InputState, input_events : Input
|
||||
|
||||
input.mouse.raw_pos = event.pos
|
||||
input.mouse.pos = render_to_screen_pos( event.pos )
|
||||
input.mouse.delta = event.delta
|
||||
input.mouse.delta = event.delta * { 1, -1 }
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -189,7 +189,7 @@ view_get_corners :: #force_inline proc "contextless"() -> BoundsCorners2 {
|
||||
render_to_screen_pos :: #force_inline proc "contextless" (pos : Vec2) -> Vec2 {
|
||||
extent := & get_state().app_window.extent
|
||||
result := Vec2 {
|
||||
pos.x - extent.x,
|
||||
pos.x - extent.x,
|
||||
pos.y * -1 + extent.y
|
||||
}
|
||||
return result
|
||||
@ -209,7 +209,7 @@ screen_to_ws_view_pos :: #force_inline proc "contextless" (pos: Vec2) -> Vec2 {
|
||||
// Centered screen space to conventional screen space used for rendering
|
||||
screen_to_render_pos :: #force_inline proc "contextless" (pos : Vec2) -> Vec2 {
|
||||
screen_extent := transmute(Vec2) get_state().app_window.extent
|
||||
return pos * {1, -1} + { screen_extent.x, screen_extent.y }
|
||||
return pos * {1, 1} + { screen_extent.x, screen_extent.y }
|
||||
}
|
||||
|
||||
// TODO(Ed): These should assume a cam_context or have the ability to provide it in params
|
||||
|
@ -72,7 +72,17 @@ UI_RenderEntry :: struct {
|
||||
layer_id : i32,
|
||||
}
|
||||
|
||||
UI_RenderLayer :: DLL_NodeFL(UI_RenderEntry)
|
||||
UI_RenderLayer :: DLL_NodeFL(UI_RenderEntry)\
|
||||
|
||||
UI_RenderBoxInfo :: struct {
|
||||
using computed : UI_Computed,
|
||||
using style : UI_Style,
|
||||
text : StrRunesPair,
|
||||
font_size : UI_Scalar,
|
||||
border_width : UI_Scalar,
|
||||
label : StrRunesPair,
|
||||
layer_signal : b32,
|
||||
}
|
||||
|
||||
// TODO(Ed): Rename to UI_Context
|
||||
UI_State :: struct {
|
||||
@ -131,8 +141,12 @@ ui_startup :: proc( ui : ^ UI_State, cache_allocator : Allocator /* , cache_rese
|
||||
ui.prev_cache = (& ui.caches[0])
|
||||
|
||||
allocation_error : AllocatorError
|
||||
|
||||
ui.render_queue, allocation_error = make( Array(UI_RenderLayer), 32, cache_allocator )
|
||||
verify( allocation_error == AllocatorError.None, "Failed to allcate render_queue")
|
||||
|
||||
ui.render_list, allocation_error = make( Array(UI_RenderBoxInfo), UI_Built_Boxes_Array_Size, cache_allocator, fixed_cap = true )
|
||||
verify( allocation_error == AllocatorError.None, "Failed to allocate render queue" )
|
||||
verify( allocation_error == AllocatorError.None, "Failed to allocate rener_list" )
|
||||
|
||||
log("ui_startup completed")
|
||||
}
|
||||
@ -143,7 +157,8 @@ ui_reload :: proc( ui : ^ UI_State, cache_allocator : Allocator )
|
||||
for & cache in ui.caches {
|
||||
hmap_zpl_reload( & cache, cache_allocator)
|
||||
}
|
||||
ui.render_list.backing = cache_allocator
|
||||
ui.render_queue.backing = cache_allocator
|
||||
ui.render_list.backing = cache_allocator
|
||||
}
|
||||
|
||||
// TODO(Ed) : Is this even needed?
|
||||
@ -160,6 +175,7 @@ ui_graph_build_begin :: proc( ui : ^ UI_State, bounds : Vec2 = {} )
|
||||
|
||||
stack_clear( & layout_combo_stack )
|
||||
stack_clear( & style_combo_stack )
|
||||
array_clear( render_queue )
|
||||
array_clear( render_list )
|
||||
|
||||
curr_cache, prev_cache = swap( curr_cache, prev_cache )
|
||||
@ -215,6 +231,8 @@ ui_graph_build_end :: proc( ui : ^UI_State )
|
||||
current.text,
|
||||
current.layout.font_size,
|
||||
current.layout.border_width,
|
||||
current.label,
|
||||
false,
|
||||
},
|
||||
layer_id = current.ancestors -1,
|
||||
}
|
||||
@ -224,15 +242,17 @@ ui_graph_build_end :: proc( ui : ^UI_State )
|
||||
render_queue = array_to_slice(ui.render_queue)
|
||||
}
|
||||
|
||||
// else if layer.last == nil {
|
||||
// layer.first.next = entry
|
||||
// entry.prev = layer.first
|
||||
// layer.last = entry
|
||||
// }
|
||||
|
||||
// push_back to next layer
|
||||
layer := & render_queue[entry.layer_id]
|
||||
if layer.first == nil {
|
||||
layer.first = entry
|
||||
}
|
||||
else if layer.last == nil {
|
||||
layer.first.next = entry
|
||||
entry.prev = layer.first
|
||||
layer.last = entry
|
||||
layer.last = entry
|
||||
}
|
||||
else {
|
||||
layer.last.next = entry
|
||||
@ -248,34 +268,48 @@ ui_graph_build_end :: proc( ui : ^UI_State )
|
||||
parent_entry = parent_layer.last
|
||||
entry.parent = parent_entry
|
||||
|
||||
// dll_fl_append( parent_entry, entry )
|
||||
|
||||
if parent_entry.first == nil {
|
||||
parent_entry.first = entry
|
||||
parent_entry.last = entry
|
||||
}
|
||||
else {
|
||||
parent_entry.last = entry
|
||||
}
|
||||
// dll_fl_append( parent_entry, entry )
|
||||
}
|
||||
}
|
||||
|
||||
// render_queue overlap corrections & render_list generation
|
||||
render_queue = array_to_slice(ui.render_queue)
|
||||
for & layer in render_queue
|
||||
for layer_id : i32 = 0; layer_id < i32(ui.render_queue.num); layer_id += 1
|
||||
{
|
||||
layer := & ui.render_queue.data[ layer_id ]
|
||||
append( & ui.render_list, UI_RenderBoxInfo { layer_signal = true })
|
||||
|
||||
to_increment, error := make( Array(^UI_RenderEntry), 4 * Kilo )
|
||||
verify( error == .None, "Faied to make to_increment array.")
|
||||
|
||||
to_inc_last_iterated : i32 = 0
|
||||
for entry := layer.first; entry != nil; entry = entry.next
|
||||
{
|
||||
for neighbor := entry.next; neighbor != nil; neighbor = neighbor.next
|
||||
{
|
||||
if ! intersects_range2( entry.info.computed.bounds, neighbor.info.computed.bounds) do continue
|
||||
if ! overlap_range2( entry.info.computed.bounds, neighbor.info.computed.bounds) do continue
|
||||
|
||||
|
||||
append( & to_increment, neighbor )
|
||||
} // for neighbor := entry.next; neighbor != nil; neighbor = neighbor.next
|
||||
|
||||
if entry == to_increment.data[ to_inc_last_iterated ] {
|
||||
to_inc_last_iterated += 1
|
||||
}
|
||||
else {
|
||||
// This entry stayed in this layer, we can append the value
|
||||
array_append_value( & ui.render_list, entry.info )
|
||||
}
|
||||
} // for entry := layer.first; entry != nil; entry = entry.next
|
||||
|
||||
// Move overlaping entries & their children's by 1 layer
|
||||
to_inc_slice := array_to_slice(to_increment)
|
||||
for entry in to_inc_slice
|
||||
{
|
||||
@ -287,7 +321,6 @@ ui_graph_build_end :: proc( ui : ^UI_State )
|
||||
}
|
||||
push_layer := render_queue[entry.layer_id]
|
||||
|
||||
|
||||
// pop entry from layer
|
||||
prev := entry.prev
|
||||
prev.next = entry.next
|
||||
@ -298,12 +331,7 @@ ui_graph_build_end :: proc( ui : ^UI_State )
|
||||
// push entry to next layer
|
||||
if push_layer.first == nil {
|
||||
push_layer.first = entry
|
||||
}
|
||||
else if push_layer.last == nil {
|
||||
push_layer.last = entry
|
||||
entry.prev = push_layer.first
|
||||
push_layer.first.next = entry
|
||||
entry.next = nil
|
||||
push_layer.last = entry
|
||||
}
|
||||
else {
|
||||
push_layer.last.next = entry
|
||||
@ -311,6 +339,12 @@ ui_graph_build_end :: proc( ui : ^UI_State )
|
||||
push_layer.last = entry
|
||||
entry.next = nil
|
||||
}
|
||||
// else if push_layer.last == nil {
|
||||
// push_layer.last = entry
|
||||
// entry.prev = push_layer.first
|
||||
// push_layer.first.next = entry
|
||||
// entry.next = nil
|
||||
// }
|
||||
|
||||
// increment children's layers
|
||||
if entry.first != nil
|
||||
@ -337,23 +371,29 @@ ui_graph_build_end :: proc( ui : ^UI_State )
|
||||
// push_back to next layer
|
||||
if push_layer.first == nil {
|
||||
push_layer.first = child
|
||||
}
|
||||
else if push_layer.last == nil {
|
||||
push_layer.first.next = child
|
||||
child.prev = push_layer.first
|
||||
push_layer.last = child
|
||||
push_layer.last = child
|
||||
}
|
||||
else {
|
||||
push_layer.last.next = child
|
||||
child.prev = push_layer.last
|
||||
push_layer.last = child
|
||||
}
|
||||
|
||||
// else if push_layer.last == nil {
|
||||
// push_layer.first.next = child
|
||||
// child.prev = push_layer.first
|
||||
// push_layer.last = child
|
||||
// }
|
||||
|
||||
} // for child := neighbor.first; child != nil; child = ui_render_entry_traverse_depth( child )
|
||||
} // if entry.first != nil
|
||||
} // for entry in to_inc_slice
|
||||
} // for & layer in render_queue
|
||||
}
|
||||
|
||||
render_queue := array_to_slice(ui.render_queue)
|
||||
render_list := array_to_slice(ui.render_list)
|
||||
|
||||
get_state().ui_context = nil
|
||||
}
|
||||
|
||||
|
@ -19,14 +19,6 @@ UI_NavLinks :: struct {
|
||||
left, right, up, down : ^UI_Box,
|
||||
}
|
||||
|
||||
UI_RenderBoxInfo :: struct {
|
||||
using computed : UI_Computed,
|
||||
using style : UI_Style,
|
||||
text : StrRunesPair,
|
||||
font_size : UI_Scalar,
|
||||
border_width : UI_Scalar,
|
||||
}
|
||||
|
||||
UI_Box :: struct {
|
||||
// Cache ID
|
||||
key : UI_Key,
|
||||
|
@ -70,17 +70,17 @@ ui_box_compute_layout :: proc( box : ^UI_Box,
|
||||
adjusted_size.x = max( adjusted_max_size_x, layout.size.min.x)
|
||||
adjusted_size.y = max( adjusted_max_size_y, layout.size.min.y)
|
||||
|
||||
// text_size : Vec2
|
||||
// if layout.font_size == computed.text_size.y {
|
||||
// text_size = computed.text_size
|
||||
// }
|
||||
// else {
|
||||
// text_size = cast(Vec2) measure_text_size( box.text.str, style.font, layout.font_size, 0 )
|
||||
// }
|
||||
text_size : Vec2
|
||||
if layout.font_size == computed.text_size.y {
|
||||
text_size = computed.text_size
|
||||
}
|
||||
else {
|
||||
text_size = cast(Vec2) measure_text_size( box.text.str, style.font, layout.font_size, 0 )
|
||||
}
|
||||
|
||||
// if size_to_text {
|
||||
// adjusted_size = text_size
|
||||
// }
|
||||
if size_to_text {
|
||||
adjusted_size = text_size
|
||||
}
|
||||
|
||||
if .Scale_Width_By_Height_Ratio in layout.flags {
|
||||
adjusted_size.x = adjusted_size.y * layout.size.min.x
|
||||
@ -161,16 +161,16 @@ ui_box_compute_layout :: proc( box : ^UI_Box,
|
||||
computed.content = content_bounds
|
||||
|
||||
// 8. Text position & size
|
||||
// if len(box.text.str) > 0
|
||||
// {
|
||||
// content_size := content_bounds.max - content_bounds.min
|
||||
// text_pos : Vec2
|
||||
// text_pos = content_bounds.min + { 0, text_size.y }
|
||||
// text_pos += (content_size - text_size) * layout.text_alignment
|
||||
if len(box.text.str) > 0
|
||||
{
|
||||
content_size := content_bounds.max - content_bounds.min
|
||||
text_pos : Vec2
|
||||
text_pos = content_bounds.min + { 0, text_size.y }
|
||||
text_pos += (content_size - text_size) * layout.text_alignment
|
||||
|
||||
// computed.text_size = text_size
|
||||
// computed.text_pos = text_pos
|
||||
// }
|
||||
computed.text_size = text_size
|
||||
computed.text_pos = text_pos
|
||||
}
|
||||
computed.fresh = true && !dont_mark_fresh
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user