layer text rendering works, ui has issues, there is perf problems
* Added todos for VEFontCache * Going be remaking the direct box traversal rendering again, I'll keep both layer based and it as a option to switch between * Need to implement a quad tree for the ui boxes to help with collision test traversal
This commit is contained in:
parent
dee8fc7ef9
commit
0d9623c340
@ -1,10 +1,30 @@
|
||||
# VE Font Cache : Odin Port
|
||||
|
||||
This is a port of the library base on my [fork](https://github.com/Ed94/VEFontCache)
|
||||
This is a port of the library base on [fork](https://github.com/hypernewbie/VEFontCache)
|
||||
|
||||
TODO (Making it a more idiomatic library):
|
||||
|
||||
* Use Odin's builtin dynamic arrays
|
||||
* Use Odin's builtin map type
|
||||
* Setup freetype, harfbuzz, depedency management within the library
|
||||
|
||||
TODO Documentation:
|
||||
|
||||
* Pureref outline of draw_text exectuion
|
||||
* Markdown general documentation
|
||||
|
||||
TODO Content:
|
||||
|
||||
* Port over the original demo utilizing sokol libraries instead
|
||||
* Provide a sokol_gfx backend package
|
||||
|
||||
TODO Additional Features:
|
||||
|
||||
* Support for freetype
|
||||
* Support for harfbuzz
|
||||
* Ability to set a draw transform, viewport and projection
|
||||
* By default the library's position is in unsigned normalized render space
|
||||
|
||||
TODO Optimizations:
|
||||
|
||||
* Support more granular handling of shapes by chunking any text from draw_text into visible and whitespace/formatting
|
||||
|
@ -4,10 +4,7 @@ A port of (https://github.com/hypernewbie/VEFontCache) to Odin.
|
||||
Status:
|
||||
This port is heavily tied to the grime package in SectrPrototype.
|
||||
|
||||
TODO(Ed): Make an idiomatic port of this for Odin (or just dupe the data structures...)
|
||||
|
||||
Changes:
|
||||
- Support for freetype(WIP)
|
||||
- Font Parser & Glyph Shaper are abstracted to their own interface
|
||||
- Font Face parser info stored separately from entries
|
||||
- ve_fontcache_loadfile not ported (just use odin's core:os or os2), then call load_font
|
||||
@ -759,9 +756,15 @@ reset_batch_codepoint_state :: proc( ctx : ^Context ) {
|
||||
|
||||
shape_text_cached :: proc( ctx : ^Context, font : FontID, text_utf8 : string ) -> ^ShapedText
|
||||
{
|
||||
@static buffer : [64 * Kilobyte]byte
|
||||
|
||||
font := font
|
||||
hash := cast(u64) crc64( transmute([]u8) text_utf8 )
|
||||
// hash := label_hash( text_utf8 )
|
||||
|
||||
font_bytes := slice_ptr( transmute(^byte) & font, size_of(FontID) )
|
||||
copy( buffer[:], font_bytes )
|
||||
copy( buffer[: size_of(FontID) + len(text_utf8) ], transmute( []byte) text_utf8 )
|
||||
|
||||
hash := label_hash( transmute(string) buffer[: size_of(FontID) + len(text_utf8)] )
|
||||
|
||||
shape_cache := & ctx.shape_cache
|
||||
state := & ctx.shape_cache.state
|
||||
|
@ -219,15 +219,6 @@ draw_cached_glyph :: proc( ctx : ^Context, entry : ^Entry, glyph_index : Glyph,
|
||||
end_index = cast(u32) ctx.draw_list.indices.num
|
||||
}
|
||||
append( & ctx.draw_list.calls, call )
|
||||
|
||||
// NOTE(Ed): Never done in the original
|
||||
// Clear glyph_update_FBO
|
||||
// call.pass = .Glyph
|
||||
// call.start_index = 0
|
||||
// call.end_index = 0
|
||||
// call.clear_before_draw = true
|
||||
// append( & ctx.draw_list.calls, call )
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
@ -291,17 +282,125 @@ draw_text :: proc( ctx : ^Context, font : FontID, text_utf8 : string, position :
|
||||
|
||||
context.allocator = ctx.backing
|
||||
|
||||
shaped := shape_text_cached( ctx, font, text_utf8 )
|
||||
|
||||
position := position
|
||||
snap_width := f32(ctx.snap_width)
|
||||
snap_height := f32(ctx.snap_height)
|
||||
|
||||
position := position
|
||||
if ctx.snap_width > 0 do position.x = cast(f32) cast(u32) (position.x * snap_width + 0.5) / snap_width
|
||||
if ctx.snap_height > 0 do position.y = cast(f32) cast(u32) (position.y * snap_height + 0.5) / snap_height
|
||||
|
||||
entry := & ctx.entries.data[ font ]
|
||||
entry := & ctx.entries.data[ font ]
|
||||
|
||||
post_shapes_draw_cursor_pos : Vec2
|
||||
last_shaped : ^ShapedText
|
||||
|
||||
ChunkType :: enum u32 { Visible, Formatting }
|
||||
chunk_kind : ChunkType
|
||||
chunk_start : int = 0
|
||||
chunk_end : int = 0
|
||||
|
||||
text_utf8_bytes := transmute([]u8) text_utf8
|
||||
text_chunk : string
|
||||
|
||||
when true {
|
||||
text_chunk = transmute(string) text_utf8_bytes[ : ]
|
||||
if len(text_chunk) > 0 {
|
||||
shaped := shape_text_cached( ctx, font, text_chunk )
|
||||
post_shapes_draw_cursor_pos += draw_text_shape( ctx, font, entry, shaped, position, scale, snap_width, snap_height )
|
||||
ctx.cursor_pos = post_shapes_draw_cursor_pos
|
||||
position += shaped.end_cursor_pos
|
||||
last_shaped = shaped
|
||||
}
|
||||
}
|
||||
else {
|
||||
last_byte_offset : int = 0
|
||||
byte_offset : int = 0
|
||||
for codepoint, offset in text_utf8
|
||||
{
|
||||
Rune_Space :: ' '
|
||||
Rune_Tab :: '\t'
|
||||
Rune_Carriage_Return :: '\r'
|
||||
Rune_Line_Feed :: '\n'
|
||||
// Rune_Tab_Vertical :: '\v'
|
||||
|
||||
byte_offset = offset
|
||||
|
||||
switch codepoint
|
||||
{
|
||||
case Rune_Space: fallthrough
|
||||
case Rune_Tab: fallthrough
|
||||
case Rune_Line_Feed: fallthrough
|
||||
case Rune_Carriage_Return:
|
||||
if chunk_kind == .Formatting {
|
||||
chunk_end = byte_offset
|
||||
last_byte_offset = byte_offset
|
||||
}
|
||||
else
|
||||
{
|
||||
text_chunk = transmute(string) text_utf8_bytes[ chunk_start : byte_offset]
|
||||
if len(text_chunk) > 0 {
|
||||
shaped := shape_text_cached( ctx, font, text_chunk )
|
||||
post_shapes_draw_cursor_pos += draw_text_shape( ctx, font, entry, shaped, position, scale, snap_width, snap_height )
|
||||
ctx.cursor_pos = post_shapes_draw_cursor_pos
|
||||
position += shaped.end_cursor_pos
|
||||
last_shaped = shaped
|
||||
}
|
||||
|
||||
chunk_start = byte_offset
|
||||
chunk_end = chunk_start
|
||||
chunk_kind = .Formatting
|
||||
|
||||
last_byte_offset = byte_offset
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
// Visible Chunk
|
||||
if chunk_kind == .Visible {
|
||||
chunk_end = byte_offset
|
||||
last_byte_offset = byte_offset
|
||||
}
|
||||
else
|
||||
{
|
||||
text_chunk = transmute(string) text_utf8_bytes[ chunk_start : byte_offset ]
|
||||
if len(text_chunk) > 0 {
|
||||
shaped := shape_text_cached( ctx, font, text_chunk )
|
||||
post_shapes_draw_cursor_pos += draw_text_shape( ctx, font, entry, shaped, position, scale, snap_width, snap_height )
|
||||
ctx.cursor_pos = post_shapes_draw_cursor_pos
|
||||
position += shaped.end_cursor_pos
|
||||
last_shaped = shaped
|
||||
}
|
||||
|
||||
chunk_start = byte_offset
|
||||
chunk_end = chunk_start
|
||||
chunk_kind = .Visible
|
||||
|
||||
last_byte_offset = byte_offset
|
||||
}
|
||||
}
|
||||
|
||||
text_chunk = transmute(string) text_utf8_bytes[ chunk_start : byte_offset ]
|
||||
if len(text_chunk) > 0 {
|
||||
shaped := shape_text_cached( ctx, font, text_chunk )
|
||||
post_shapes_draw_cursor_pos += draw_text_shape( ctx, font, entry, shaped, position, scale, snap_width, snap_height )
|
||||
ctx.cursor_pos = post_shapes_draw_cursor_pos
|
||||
position += shaped.end_cursor_pos
|
||||
last_shaped = shaped
|
||||
}
|
||||
|
||||
chunk_start = byte_offset
|
||||
chunk_end = chunk_start
|
||||
chunk_kind = .Visible
|
||||
|
||||
last_byte_offset = byte_offset
|
||||
|
||||
ctx.cursor_pos = post_shapes_draw_cursor_pos
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// Helper for draw_text, all raw text content should be confirmed to be either formatting or visible shapes before getting cached.
|
||||
draw_text_shape :: proc( ctx : ^Context, font : FontID, entry : ^Entry, shaped : ^ShapedText, position, scale : Vec2, snap_width, snap_height : f32 ) -> (cursor_pos : Vec2)
|
||||
{
|
||||
batch_start_idx : i32 = 0
|
||||
for index : i32 = 0; index < i32(shaped.glyphs.num); index += 1
|
||||
{
|
||||
@ -309,6 +408,7 @@ draw_text :: proc( ctx : ^Context, font : FontID, text_utf8 : string, position :
|
||||
if is_empty( ctx, entry, glyph_index ) do continue
|
||||
if can_batch_glyph( ctx, font, entry, glyph_index ) do continue
|
||||
|
||||
// Glyph has not been catched, needs to be directly drawn.
|
||||
draw_text_batch( ctx, entry, shaped, batch_start_idx, index, position, scale )
|
||||
reset_batch_codepoint_state( ctx )
|
||||
|
||||
@ -323,9 +423,8 @@ draw_text :: proc( ctx : ^Context, font : FontID, text_utf8 : string, position :
|
||||
|
||||
draw_text_batch( ctx, entry, shaped, batch_start_idx, i32(shaped.glyphs.num), position, scale )
|
||||
reset_batch_codepoint_state( ctx )
|
||||
ctx.cursor_pos = position + shaped.end_cursor_pos * scale
|
||||
|
||||
return true
|
||||
cursor_pos = position + shaped.end_cursor_pos * scale
|
||||
return
|
||||
}
|
||||
|
||||
draw_text_batch :: proc( ctx : ^Context, entry : ^Entry, shaped : ^ShapedText, batch_start_idx, batch_end_idx : i32, position, scale : Vec2 )
|
||||
@ -375,6 +474,11 @@ get_draw_list :: proc( ctx : ^Context ) -> ^DrawList {
|
||||
return & ctx.draw_list
|
||||
}
|
||||
|
||||
// TODO(Ed): See render.odin's render_text_layer, should provide the ability to get a slice of the draw list to render the latest layer
|
||||
DrawListLayer :: struct {}
|
||||
get_draw_list_layer :: proc() -> DrawListLayer { return {} }
|
||||
flush_layer :: proc( draw_list : ^DrawList ) {}
|
||||
|
||||
// ve_fontcache_merge_drawlist
|
||||
merge_draw_list :: proc( dst, src : ^DrawList )
|
||||
{
|
||||
@ -403,19 +507,18 @@ merge_draw_list :: proc( dst, src : ^DrawList )
|
||||
}
|
||||
}
|
||||
|
||||
// Call during a full depth layer pass to optimize the final draw list when necessary
|
||||
optimize_draw_list :: proc( ctx : ^Context )
|
||||
optimize_draw_list :: proc( draw_list : ^DrawList, call_offset : u64 )
|
||||
{
|
||||
assert( ctx != nil )
|
||||
assert( draw_list != nil )
|
||||
|
||||
calls := array_to_slice(ctx.draw_list.calls)
|
||||
calls := array_to_slice(draw_list.calls)
|
||||
|
||||
write_index : i32 = 0
|
||||
for index : i32 = 1; index < i32(ctx.draw_list.calls.num); index += 1
|
||||
write_index : u64 = call_offset
|
||||
for index : u64 = 1 + call_offset; index < u64(draw_list.calls.num); index += 1
|
||||
{
|
||||
assert( write_index <= index )
|
||||
draw_0 := & ctx.draw_list.calls.data[ write_index ]
|
||||
draw_1 := & ctx.draw_list.calls.data[ index ]
|
||||
draw_0 := & draw_list.calls.data[ write_index ]
|
||||
draw_1 := & draw_list.calls.data[ index ]
|
||||
|
||||
merge : b32 = true
|
||||
if draw_0.pass != draw_1.pass do merge = false
|
||||
@ -436,11 +539,11 @@ optimize_draw_list :: proc( ctx : ^Context )
|
||||
// logf("can't merge %v : %v %v", draw_0.pass, write_index, index )
|
||||
write_index += 1
|
||||
if write_index != index {
|
||||
draw_2 := & ctx.draw_list.calls.data[ write_index ]
|
||||
draw_2 := & draw_list.calls.data[ write_index ]
|
||||
draw_2^ = draw_1^
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
resize( & ctx.draw_list.calls, u64(write_index + 1) )
|
||||
resize( & draw_list.calls, u64(write_index + 1) )
|
||||
}
|
||||
|
@ -25,7 +25,8 @@ ParserFontInfo :: struct {
|
||||
using _ : struct #raw_union {
|
||||
stbtt_info : stbtt.fontinfo,
|
||||
freetype_info : freetype.Face
|
||||
}
|
||||
},
|
||||
data : []byte,
|
||||
}
|
||||
|
||||
GlyphVertType :: enum u8 {
|
||||
@ -96,6 +97,7 @@ parser_load_font :: proc( ctx : ^ParserContext, label : string, data : []byte )
|
||||
}
|
||||
|
||||
font.label = label
|
||||
font.data = data
|
||||
return
|
||||
}
|
||||
|
||||
@ -209,7 +211,7 @@ 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 := stbtt.InitFont( & font.stbtt_info, raw_data(font.data), 0 )
|
||||
// }
|
||||
success := cast(bool) stbtt.GetGlyphBox( & font.stbtt_info, i32(glyph_index), & x0, & y0, & x1, & y1 )
|
||||
assert( success )
|
||||
@ -394,7 +396,16 @@ parser_is_glyph_empty :: proc( font : ^ParserFontInfo, glyph_index : Glyph ) ->
|
||||
return false
|
||||
}
|
||||
|
||||
parser_scale_for_pixel_height :: #force_inline proc( font : ^ParserFontInfo, size : f32 ) -> f32
|
||||
parser_scale :: #force_inline proc( font : ^ParserFontInfo, size : f32 ) -> f32
|
||||
{
|
||||
size_scale := size < 0.0 ? \
|
||||
parser_scale_for_pixel_height( font, -size ) \
|
||||
: parser_scale_for_mapping_em_to_pixels( font, size )
|
||||
// size_scale = 1.0
|
||||
return size_scale
|
||||
}
|
||||
|
||||
parser_scale_for_pixel_height :: proc( font : ^ParserFontInfo, size : f32 ) -> f32
|
||||
{
|
||||
switch font.kind {
|
||||
case .Freetype:
|
||||
|
@ -503,8 +503,8 @@ tick_work_frame :: #force_inline proc( host_delta_time_ms : f64 ) -> b32
|
||||
|
||||
// config.engine_refresh_hz = 165
|
||||
|
||||
config.color_theme = App_Thm_Light
|
||||
// config.color_theme = App_Thm_Dusk
|
||||
// config.color_theme = App_Thm_Light
|
||||
config.color_theme = App_Thm_Dusk
|
||||
// config.color_theme = App_Thm_Dark
|
||||
|
||||
sokol_width := sokol_app.widthf()
|
||||
@ -551,7 +551,7 @@ tick_frametime :: #force_inline proc( client_tick : ^time.Tick, host_delta_time_
|
||||
pre_sleep_tick := time.tick_now()
|
||||
|
||||
if can_sleep && sleep_ms > 0 {
|
||||
thread_sleep( cast(Duration) sleep_ms * MS_To_NS )
|
||||
// thread_sleep( cast(Duration) sleep_ms * MS_To_NS )
|
||||
// thread__highres_wait( sleep_ms )
|
||||
}
|
||||
|
||||
|
@ -19,12 +19,7 @@ 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 )
|
||||
}
|
||||
#region("Helpers")
|
||||
|
||||
draw_filled_circle :: proc(x, y, radius: f32, edges: int)
|
||||
{
|
||||
@ -53,6 +48,15 @@ draw_filled_circle :: proc(x, y, radius: f32, edges: int)
|
||||
gp.draw_filled_triangles(raw_data(triangles), u32(len(triangles)))
|
||||
}
|
||||
|
||||
draw_rect :: proc( rect : Range2, color : RGBA8 ) {
|
||||
using rect
|
||||
render_set_color( color )
|
||||
|
||||
size := max - min
|
||||
position := min
|
||||
gp.draw_filled_rect( position.x, position.y, size.x, size.y )
|
||||
}
|
||||
|
||||
draw_rect_border :: proc( rect : Range2, border_width: f32)
|
||||
{
|
||||
rect_size := rect.max - rect.min
|
||||
@ -68,7 +72,7 @@ draw_rect_border :: proc( rect : Range2, border_width: f32)
|
||||
}
|
||||
|
||||
// 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 )
|
||||
draw_text_string_pos_norm :: proc( content : string, id : FontID, size : f32, pos : Vec2, color := Color_White, scale : f32 = 1.0 )
|
||||
{
|
||||
state := get_state(); using state
|
||||
width := app_window.extent.x * 2
|
||||
@ -78,14 +82,14 @@ draw_text_string_pos_norm :: proc( content : string, id : FontID, size : f32, po
|
||||
color_norm := normalize_rgba8(color)
|
||||
|
||||
ve.set_colour( & font_provider_data.ve_font_cache, color_norm )
|
||||
ve.draw_text( & font_provider_data.ve_font_cache, ve_id, content, pos, Vec2{1 / width, 1 / height} )
|
||||
ve.draw_text( & font_provider_data.ve_font_cache, ve_id, content, pos, Vec2{1 / width, 1 / height} * scale )
|
||||
return
|
||||
}
|
||||
|
||||
// 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 )
|
||||
{
|
||||
profile(#procedure)
|
||||
// profile(#procedure)
|
||||
state := get_state(); using state
|
||||
screen_size := app_window.extent * 2
|
||||
render_pos := screen_to_render_pos(pos)
|
||||
@ -93,37 +97,80 @@ draw_text_string_pos_extent :: proc( content : string, id : FontID, size : f32,
|
||||
draw_text_string_pos_norm( content, id, size, normalized_pos, color )
|
||||
}
|
||||
|
||||
#endregion("Draw Helpers")
|
||||
draw_text_string_pos_extent_zoomed :: proc( content : string, id : FontID, size : f32, pos : Vec2, cam : Camera, color := Color_White )
|
||||
{
|
||||
// profile(#procedure)
|
||||
cam_offset := Vec2 {
|
||||
cam.position.x,
|
||||
cam.position.y,
|
||||
}
|
||||
|
||||
pos_offset := pos + cam_offset * cam.zoom
|
||||
|
||||
cam_zoom_ratio := 1 / cam.zoom
|
||||
|
||||
state := get_state(); using state
|
||||
screen_size := app_window.extent * 2
|
||||
render_pos := screen_to_render_pos(pos_offset)
|
||||
normalized_pos := render_pos * (1.0 / screen_size)
|
||||
draw_text_string_pos_norm( content, id, size, normalized_pos, color, cam.zoom )
|
||||
}
|
||||
|
||||
// TODO(Ed): Eventually the workspace will need a viewport for drawing text
|
||||
|
||||
render_flush_gp :: #force_inline proc()
|
||||
{
|
||||
gfx.begin_pass( gfx.Pass { action = get_state().render_data.pass_actions.empty_action, swapchain = sokol_glue.swapchain() })
|
||||
gp.flush()
|
||||
gfx.end_pass()
|
||||
}
|
||||
|
||||
@(deferred_none=gp.reset_transform)
|
||||
render_set_camera :: #force_inline proc( cam : Camera )
|
||||
{
|
||||
gp.translate( cam.position.x * cam.zoom, cam.position.y * cam.zoom )
|
||||
gp.scale( cam.zoom, cam.zoom )
|
||||
}
|
||||
|
||||
render_set_color :: #force_inline proc( color : RGBA8 ) {
|
||||
color := normalize_rgba8(color);
|
||||
gp.set_color( color.r, color.g, color.b, color.a )
|
||||
}
|
||||
|
||||
render_set_view_space :: #force_inline proc( extent : Extents2 )
|
||||
{
|
||||
size := extent * 2
|
||||
gp.viewport(0, 0, i32(size.x), i32(size.y))
|
||||
gp.project( -extent.x, extent.x, extent.y, -extent.y )
|
||||
}
|
||||
|
||||
#endregion("Helpers")
|
||||
|
||||
render :: proc()
|
||||
{
|
||||
profile(#procedure)
|
||||
state := get_state(); using state // TODO(Ed): Prefer passing static context to through the callstack
|
||||
|
||||
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 )
|
||||
|
||||
screen_extent := app_window.extent
|
||||
screen_size := app_window.extent * 2
|
||||
screen_ratio := screen_size.x * ( 1.0 / screen_size.y )
|
||||
|
||||
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.set_blend_mode( .BLEND )
|
||||
gp_set_color(config.color_theme.bg)
|
||||
gp.clear()
|
||||
|
||||
gfx.begin_pass(clear_pass)
|
||||
gp.flush()
|
||||
gfx.end_pass();
|
||||
|
||||
render_mode_2d_workspace()
|
||||
render_mode_screenspace()
|
||||
|
||||
// Clear the gp surface
|
||||
{
|
||||
render_set_view_space(screen_extent)
|
||||
render_set_color(config.color_theme.bg)
|
||||
gp.clear()
|
||||
render_flush_gp()
|
||||
}
|
||||
// Workspace and screen rendering passes
|
||||
{
|
||||
render_mode_2d_workspace()
|
||||
render_mode_screenspace()
|
||||
}
|
||||
gp.end()
|
||||
|
||||
gfx.commit()
|
||||
ve.flush_draw_list( & font_provider_data.ve_font_cache )
|
||||
font_provider_data.vbuf_layer_offset = 0
|
||||
@ -137,7 +184,7 @@ render_mode_2d_workspace :: proc()
|
||||
{
|
||||
profile(#procedure)
|
||||
state := get_state(); using state // TODO(Ed): Prefer passing static context to through the callstack
|
||||
cam := & project.workspace.cam
|
||||
cam := project.workspace.cam
|
||||
|
||||
screen_extent := app_window.extent
|
||||
screen_size := app_window.extent * 2
|
||||
@ -145,16 +192,16 @@ render_mode_2d_workspace :: proc()
|
||||
|
||||
cam_zoom_ratio := 1.0 / cam.zoom
|
||||
|
||||
Render_Reference_Dots:
|
||||
// TODO(Ed): Eventually will be the viewport extents
|
||||
ve.configure_snap( & font_provider_data.ve_font_cache, u32(state.app_window.extent.x * 2.0), u32(state.app_window.extent.y * 2.0) )
|
||||
|
||||
Render_Debug:
|
||||
{
|
||||
profile("render_reference_dots (workspace)")
|
||||
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_set_view_space(screen_extent)
|
||||
render_set_camera(cam)
|
||||
|
||||
gp.translate( cam.position.x * cam.zoom, cam.position.y * cam.zoom )
|
||||
gp.scale( cam.zoom, cam.zoom )
|
||||
|
||||
gp_set_color(Color_White)
|
||||
render_set_color(Color_White)
|
||||
draw_filled_circle(0, 0, 2 * cam_zoom_ratio, 24)
|
||||
|
||||
if false
|
||||
@ -166,19 +213,70 @@ render_mode_2d_workspace :: proc()
|
||||
gp.draw_filled_rect(50, 50, 100, 100 )
|
||||
}
|
||||
|
||||
Mouse_Position:
|
||||
mouse_pos := input.mouse.pos * cam_zoom_ratio - cam.position
|
||||
render_set_color( Color_GreyRed )
|
||||
draw_filled_circle( mouse_pos.x, mouse_pos.y, 4, 24 )
|
||||
|
||||
render_flush_gp()
|
||||
}
|
||||
|
||||
render_set_view_space(screen_extent)
|
||||
render_set_camera(cam)
|
||||
|
||||
ui := & project.workspace.ui
|
||||
render_list := array_to_slice( ui.render_list )
|
||||
|
||||
text_enqueued : b32 = false
|
||||
shape_enqueued : b32 = false
|
||||
|
||||
for entry, id in render_list
|
||||
{
|
||||
already_passed_signal := id > 0 && render_list[ id - 1 ].layer_signal
|
||||
if !already_passed_signal && entry.layer_signal
|
||||
{
|
||||
mouse_pos := (input.mouse.pos) * cam_zoom_ratio - cam.position
|
||||
gp_set_color( Color_GreyRed )
|
||||
draw_filled_circle( mouse_pos.x, mouse_pos.y, 4, 24 )
|
||||
profile("render ui layer")
|
||||
render_flush_gp()
|
||||
if text_enqueued do render_text_layer()
|
||||
continue
|
||||
}
|
||||
using entry
|
||||
|
||||
profile("enqueue box")
|
||||
|
||||
GP_Render:
|
||||
{
|
||||
// profile("draw_shapes")
|
||||
if style.bg_color.a != 0
|
||||
{
|
||||
draw_rect( bounds, style.bg_color )
|
||||
shape_enqueued = true
|
||||
}
|
||||
|
||||
if style.border_color.a != 0 && border_width > 0 {
|
||||
render_set_color( style.border_color )
|
||||
draw_rect_border( bounds, border_width )
|
||||
shape_enqueued = true
|
||||
}
|
||||
|
||||
if debug.draw_ui_box_bounds_points
|
||||
{
|
||||
render_set_color(Color_Red)
|
||||
draw_filled_circle(bounds.min.x, bounds.min.y, 3, 24)
|
||||
|
||||
render_set_color(Color_Blue)
|
||||
draw_filled_circle(bounds.max.x, bounds.max.y, 3, 24)
|
||||
shape_enqueued = true
|
||||
}
|
||||
}
|
||||
|
||||
gp.reset_transform()
|
||||
|
||||
gfx.begin_pass( gfx.Pass { action = render_data.pass_actions.empty_action, swapchain = sokol_glue.swapchain() })
|
||||
gp.flush()
|
||||
gfx.end_pass()
|
||||
if len(text.str) > 0 && style.font.key != 0 {
|
||||
draw_text_string_pos_extent_zoomed( text.str, default_font, font_size, computed.text_pos, cam, style.text_color )
|
||||
text_enqueued = true
|
||||
}
|
||||
}
|
||||
|
||||
if shape_enqueued do render_flush_gp()
|
||||
if text_enqueued do render_text_layer()
|
||||
}
|
||||
|
||||
render_mode_screenspace :: proc()
|
||||
@ -200,67 +298,51 @@ render_mode_screenspace :: proc()
|
||||
Render_Reference_Dots:
|
||||
{
|
||||
profile("render_reference_dots (screen)")
|
||||
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_set_view_space(screen_extent)
|
||||
|
||||
gp_set_color(Color_Screen_Center_Dot)
|
||||
render_set_color(Color_Screen_Center_Dot)
|
||||
draw_filled_circle(0, 0, 2, 24)
|
||||
|
||||
Mouse_Position:
|
||||
{
|
||||
mouse_pos := input.mouse.pos
|
||||
gp_set_color( Color_White_A125 )
|
||||
render_set_color( Color_White_A125 )
|
||||
draw_filled_circle( mouse_pos.x, mouse_pos.y, 4, 24 )
|
||||
}
|
||||
|
||||
gfx.begin_pass( gfx.Pass { action = render_data.pass_actions.empty_action, swapchain = sokol_glue.swapchain() })
|
||||
gp.flush()
|
||||
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
|
||||
|
||||
if len( content ) == 0 {
|
||||
return
|
||||
}
|
||||
|
||||
font := font
|
||||
if font.key == Font_Default.key {
|
||||
font = default_font
|
||||
}
|
||||
// pos := screen_to_render_pos(pos)
|
||||
|
||||
draw_text_string_pos_extent( content, font, size, pos, color )
|
||||
}
|
||||
|
||||
debug_text :: proc( format : string, args : ..any )
|
||||
{
|
||||
@static draw_text_scratch : [Kilobyte * 8]u8
|
||||
|
||||
state := get_state(); using state
|
||||
if debug.draw_debug_text_y > 800 {
|
||||
debug.draw_debug_text_y = 0
|
||||
}
|
||||
|
||||
cam := & project.workspace.cam
|
||||
screen_corners := screen_get_corners()
|
||||
|
||||
position := screen_corners.top_left
|
||||
position.y -= debug.draw_debug_text_y
|
||||
|
||||
content := str_fmt_buffer( draw_text_scratch[:], format, ..args )
|
||||
|
||||
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
|
||||
render_flush_gp()
|
||||
}
|
||||
|
||||
debug.debug_text_vis = true
|
||||
if debug.debug_text_vis
|
||||
{
|
||||
debug_draw_text :: proc( content : string, pos : Vec2, size : f32, color := Color_White, font : FontID = Font_Default )
|
||||
{
|
||||
state := get_state(); using state
|
||||
if len( content ) == 0 do return
|
||||
|
||||
font := font
|
||||
if font.key == Font_Default.key do font = default_font
|
||||
draw_text_string_pos_extent( content, font, size, pos, color )
|
||||
}
|
||||
|
||||
debug_text :: proc( format : string, args : ..any )
|
||||
{
|
||||
state := get_state(); using state
|
||||
if debug.draw_debug_text_y > 800 do debug.draw_debug_text_y = 0
|
||||
|
||||
cam := & project.workspace.cam
|
||||
screen_corners := screen_get_corners()
|
||||
|
||||
position := screen_corners.top_left
|
||||
position.y -= debug.draw_debug_text_y
|
||||
|
||||
content := str_fmt( format, ..args )
|
||||
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
|
||||
}
|
||||
|
||||
profile("debug_text_vis")
|
||||
fps_size : f32 = 14.0
|
||||
fps_msg := str_fmt( "FPS: %0.2f", fps_avg)
|
||||
@ -281,7 +363,7 @@ render_mode_screenspace :: proc()
|
||||
}
|
||||
debug_text("Zoom Target: %v", project.workspace.zoom_target)
|
||||
|
||||
if false
|
||||
if true
|
||||
{
|
||||
using input_events
|
||||
|
||||
@ -302,11 +384,9 @@ render_mode_screenspace :: proc()
|
||||
debug_text("Mouse Position (Render) : %v", input.mouse.raw_pos )
|
||||
debug_text("Mouse Position (Screen) : %v", input.mouse.pos )
|
||||
debug_text("Mouse Position (Workspace View): %v", screen_to_ws_view_pos(input.mouse.pos) )
|
||||
// rl.DrawCircleV( input.mouse.raw_pos, 10, Color_White_A125 )
|
||||
// rl.DrawCircleV( screen_to_render_pos(input.mouse.pos), 2, Color_BG )
|
||||
}
|
||||
|
||||
if false
|
||||
if true
|
||||
{
|
||||
ui := & project.workspace.ui
|
||||
|
||||
@ -323,7 +403,7 @@ render_mode_screenspace :: proc()
|
||||
}
|
||||
}
|
||||
|
||||
if false
|
||||
if true
|
||||
{
|
||||
ui := & screen_ui
|
||||
|
||||
@ -354,29 +434,21 @@ render_screen_ui :: proc()
|
||||
screen_extent := app_window.extent
|
||||
screen_size := app_window.extent * 2
|
||||
screen_ratio := screen_size.x * ( 1.0 / screen_size.y )
|
||||
render_set_view_space(screen_extent)
|
||||
|
||||
ui := & screen_ui
|
||||
|
||||
render_list := array_to_slice( ui.render_list )
|
||||
|
||||
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
|
||||
|
||||
render_list := array_to_slice( ui.render_list )
|
||||
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()
|
||||
gfx.end_pass()
|
||||
|
||||
if text_enqueued {
|
||||
render_text_layer()
|
||||
}
|
||||
render_flush_gp()
|
||||
if text_enqueued do render_text_layer()
|
||||
continue
|
||||
}
|
||||
using entry
|
||||
@ -389,7 +461,7 @@ render_screen_ui :: proc()
|
||||
|
||||
draw_rect :: proc( rect : Range2, color : RGBA8 ) {
|
||||
using rect
|
||||
gp_set_color( color )
|
||||
render_set_color( color )
|
||||
|
||||
size := max - min
|
||||
position := min
|
||||
@ -403,17 +475,17 @@ render_screen_ui :: proc()
|
||||
}
|
||||
|
||||
if style.border_color.a != 0 && border_width > 0 {
|
||||
gp_set_color( style.border_color )
|
||||
render_set_color( style.border_color )
|
||||
draw_rect_border( bounds, border_width )
|
||||
shape_enqueued = true
|
||||
}
|
||||
|
||||
if debug.draw_ui_box_bounds_points
|
||||
{
|
||||
gp_set_color(Color_Red)
|
||||
render_set_color(Color_Red)
|
||||
draw_filled_circle(bounds.min.x, bounds.min.y, 3, 24)
|
||||
|
||||
gp_set_color(Color_Blue)
|
||||
render_set_color(Color_Blue)
|
||||
draw_filled_circle(bounds.max.x, bounds.max.y, 3, 24)
|
||||
shape_enqueued = true
|
||||
}
|
||||
@ -425,15 +497,8 @@ render_screen_ui :: proc()
|
||||
}
|
||||
}
|
||||
|
||||
if shape_enqueued {
|
||||
gfx.begin_pass( gfx.Pass { action = render_data.pass_actions.empty_action, swapchain = sokol_glue.swapchain() })
|
||||
gp.flush()
|
||||
gfx.end_pass()
|
||||
}
|
||||
|
||||
if text_enqueued {
|
||||
render_text_layer()
|
||||
}
|
||||
if shape_enqueued do render_flush_gp()
|
||||
if text_enqueued do render_text_layer()
|
||||
}
|
||||
|
||||
render_text_layer :: proc()
|
||||
@ -448,7 +513,9 @@ render_text_layer :: proc()
|
||||
font_provider := & state.font_provider_data
|
||||
using font_provider
|
||||
|
||||
// ve.optimize_draw_list( & ve_font_cache )
|
||||
// TODO(Ed): All this functionality for being able to segregate rendering of the drawlist incrementally should be lifted to the library itself (VEFontCache)
|
||||
|
||||
ve.optimize_draw_list( & ve_font_cache.draw_list, calls_layer_offset )
|
||||
draw_list := ve.get_draw_list( & ve_font_cache )
|
||||
|
||||
draw_list_vert_slice := array_to_slice(draw_list.vertices)
|
||||
|
@ -244,7 +244,7 @@ update :: proc( delta_time : f64 ) -> b32
|
||||
scope( frame_style )
|
||||
|
||||
config.ui_resize_border_width = 2.5
|
||||
test_hover_n_click()
|
||||
// test_hover_n_click()
|
||||
// test_draggable()
|
||||
// test_text_box()
|
||||
// test_parenting( & default_layout, & frame_style_default )
|
||||
|
@ -202,7 +202,7 @@ render_to_ws_view_pos :: #force_inline proc "contextless" (pos : Vec2) -> Vec2 {
|
||||
screen_to_ws_view_pos :: #force_inline proc "contextless" (pos: Vec2) -> Vec2 {
|
||||
state := get_state(); using state
|
||||
cam := & project.workspace.cam
|
||||
result := Vec2 { cam.position.x, -cam.position.y} + Vec2 { pos.x, pos.y } * (1 / cam.zoom)
|
||||
result := Vec2 { cam.position.x, -cam.position.y} + Vec2 { pos.x, pos.y } * (1 / cam.zoom)
|
||||
return result
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user