mirror of
https://github.com/Ed94/VEFontCache-Odin.git
synced 2025-08-06 06:52:44 -07:00
Finished initial implementation of sokol demo
This commit is contained in:
@@ -5,6 +5,7 @@ import "core:path/filepath"
|
|||||||
file_name_from_path :: filepath.short_stem
|
file_name_from_path :: filepath.short_stem
|
||||||
import "core:fmt"
|
import "core:fmt"
|
||||||
import "core:math"
|
import "core:math"
|
||||||
|
import "core:math/rand"
|
||||||
import "core:mem"
|
import "core:mem"
|
||||||
import "core:os"
|
import "core:os"
|
||||||
import "core:strings"
|
import "core:strings"
|
||||||
@@ -33,22 +34,21 @@ normalize_rgba8 :: #force_inline proc( color : RGBA8 ) -> RGBAN {
|
|||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
Color_Blue :: RGBA8 { 90, 90, 230, 255 }
|
COLOR_BLUE :: RGBA8 { 90, 90, 230, 255 }
|
||||||
Color_Red :: RGBA8 { 230, 90, 90, 255 }
|
COLOR_RED :: RGBA8 { 230, 90, 90, 255 }
|
||||||
Color_White :: RGBA8 { 255, 255, 255, 255 }
|
COLOR_WHITE :: RGBA8 { 255, 255, 255, 255 }
|
||||||
|
|
||||||
Font_Provider_Use_Freetype :: false
|
FONT_LARGEST_PIXEL_SIZE :: 400
|
||||||
Font_Largest_Px_Size :: 154
|
FONT_SIZE_INTERVAL :: 2
|
||||||
Font_Size_Interval :: 2
|
|
||||||
|
|
||||||
Font_Default :: FontID { "" }
|
FONT_DEFAULT :: FontID { "" }
|
||||||
Font_Default_Size :: 12.0
|
FONT_DEFAULT_SIZEZ :: 12.0
|
||||||
|
|
||||||
Font_Load_Use_Default_Size :: -1
|
FONT_LOAD_USE_DEFAULT_SIZE :: -1
|
||||||
Font_Load_Gen_ID :: ""
|
FONT_LOAD_GEN_ID :: ""
|
||||||
|
|
||||||
// Working directory assumed to be the build folder
|
// Working directory assumed to be the build folder
|
||||||
Path_Fonts :: "../fonts/"
|
PATH_FONTS :: "../fonts/"
|
||||||
|
|
||||||
FontID :: struct {
|
FontID :: struct {
|
||||||
label : string,
|
label : string,
|
||||||
@@ -57,7 +57,7 @@ FontID :: struct {
|
|||||||
FontDef :: struct {
|
FontDef :: struct {
|
||||||
path_file : string,
|
path_file : string,
|
||||||
default_size : i32,
|
default_size : i32,
|
||||||
size_table : [Font_Largest_Px_Size / Font_Size_Interval] ve.FontID,
|
size_table : [FONT_LARGEST_PIXEL_SIZE / FONT_SIZE_INTERVAL] ve.FontID,
|
||||||
}
|
}
|
||||||
|
|
||||||
Demo_Context :: struct {
|
Demo_Context :: struct {
|
||||||
@@ -65,16 +65,38 @@ Demo_Context :: struct {
|
|||||||
render_ctx : ve_sokol.Context,
|
render_ctx : ve_sokol.Context,
|
||||||
font_ids : map[string]FontDef,
|
font_ids : map[string]FontDef,
|
||||||
|
|
||||||
font_firacode : FontID,
|
// Values between 1, & -1 on Y axis
|
||||||
|
mouse_scroll : Vec2,
|
||||||
|
|
||||||
screen_size : [2]f32
|
font_firacode : FontID,
|
||||||
|
font_logo : FontID,
|
||||||
|
font_title : FontID,
|
||||||
|
font_print : FontID,
|
||||||
|
font_mono : FontID,
|
||||||
|
font_small : FontID,
|
||||||
|
font_demo_sans : FontID,
|
||||||
|
font_demo_serif : FontID,
|
||||||
|
font_demo_script : FontID,
|
||||||
|
font_demo_mono : FontID,
|
||||||
|
font_demo_chinese : FontID,
|
||||||
|
font_demo_japanese : FontID,
|
||||||
|
font_demo_korean : FontID,
|
||||||
|
font_demo_thai : FontID,
|
||||||
|
font_demo_arabic : FontID,
|
||||||
|
font_demo_hebrew : FontID,
|
||||||
|
font_demo_raincode : FontID,
|
||||||
|
font_demo_grid2 : FontID,
|
||||||
|
font_demo_grid3 : FontID,
|
||||||
|
|
||||||
|
screen_size : [2]f32,
|
||||||
}
|
}
|
||||||
|
|
||||||
demo_ctx : Demo_Context
|
demo_ctx : Demo_Context
|
||||||
|
|
||||||
font_load :: proc(path_file : string,
|
font_load :: proc(path_file : string,
|
||||||
default_size : i32 = Font_Load_Use_Default_Size,
|
default_size : i32 = FONT_LOAD_USE_DEFAULT_SIZE,
|
||||||
desired_id : string = Font_Load_Gen_ID
|
desired_id : string = FONT_LOAD_GEN_ID,
|
||||||
|
curve_quality : u32 = 3,
|
||||||
) -> FontID
|
) -> FontID
|
||||||
{
|
{
|
||||||
msg := fmt.println("Loading font: %v", path_file)
|
msg := fmt.println("Loading font: %v", path_file)
|
||||||
@@ -96,17 +118,17 @@ font_firacode : FontID
|
|||||||
|
|
||||||
default_size := default_size
|
default_size := default_size
|
||||||
if default_size < 0 {
|
if default_size < 0 {
|
||||||
default_size = Font_Default_Size
|
default_size = FONT_DEFAULT_SIZEZ
|
||||||
}
|
}
|
||||||
|
|
||||||
def.path_file = path_file
|
def.path_file = path_file
|
||||||
def.default_size = default_size
|
def.default_size = default_size
|
||||||
|
|
||||||
for font_size : i32 = clamp( Font_Size_Interval, 2, Font_Size_Interval ); font_size <= Font_Largest_Px_Size; font_size += Font_Size_Interval
|
for font_size : i32 = clamp( FONT_SIZE_INTERVAL, 2, FONT_SIZE_INTERVAL ); font_size <= FONT_LARGEST_PIXEL_SIZE; font_size += FONT_SIZE_INTERVAL
|
||||||
{
|
{
|
||||||
id := (font_size / Font_Size_Interval) + (font_size % Font_Size_Interval)
|
id := (font_size / FONT_SIZE_INTERVAL) + (font_size % FONT_SIZE_INTERVAL)
|
||||||
ve_id := & def.size_table[id - 1]
|
ve_id := & def.size_table[id - 1]
|
||||||
ve_ret_id := ve.load_font( & demo_ctx.ve_ctx, desired_id, font_data, f32(font_size) )
|
ve_ret_id := ve.load_font( & demo_ctx.ve_ctx, desired_id, font_data, f32(font_size), curve_quality )
|
||||||
(ve_id^) = ve_ret_id
|
(ve_id^) = ve_ret_id
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -120,10 +142,10 @@ font_provider_resolve_draw_id :: proc( id : FontID, size := Font_Use_Default_Siz
|
|||||||
{
|
{
|
||||||
def := demo_ctx.font_ids[ id.label ]
|
def := demo_ctx.font_ids[ id.label ]
|
||||||
size := size == 0.0 ? f32(def.default_size) : size
|
size := size == 0.0 ? f32(def.default_size) : size
|
||||||
even_size := math.round(size * (1.0 / f32(Font_Size_Interval))) * f32(Font_Size_Interval)
|
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), 2, FONT_LARGEST_PIXEL_SIZE )
|
||||||
|
|
||||||
id := (resolved_size / Font_Size_Interval) + (resolved_size % Font_Size_Interval)
|
id := (resolved_size / FONT_SIZE_INTERVAL) + (resolved_size % FONT_SIZE_INTERVAL)
|
||||||
ve_id = def.size_table[ id - 1 ]
|
ve_id = def.size_table[ id - 1 ]
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@@ -143,7 +165,7 @@ get_font_vertical_metrics :: #force_inline proc ( font : FontID, font_size := Fo
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Draw text using a string and normalized render coordinates
|
// 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, scale : f32 = 1.0 )
|
draw_text_string_pos_norm :: proc( content : string, id : FontID, size : f32, pos : Vec2, color := COLOR_WHITE, scale : f32 = 1.0 )
|
||||||
{
|
{
|
||||||
width := demo_ctx.screen_size.x
|
width := demo_ctx.screen_size.x
|
||||||
height := demo_ctx.screen_size.y
|
height := demo_ctx.screen_size.y
|
||||||
@@ -157,12 +179,44 @@ 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 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 ) {
|
draw_text_string_pos_extent :: proc( content : string, id : FontID, size : f32, pos : Vec2, color := COLOR_WHITE ) {
|
||||||
render_pos := pos + demo_ctx.screen_size * 0.5
|
render_pos := pos + demo_ctx.screen_size * 0.5
|
||||||
normalized_pos := render_pos * (1.0 / demo_ctx.screen_size)
|
normalized_pos := render_pos * (1.0 / demo_ctx.screen_size)
|
||||||
draw_text_string_pos_norm( content, id, size, normalized_pos, color )
|
draw_text_string_pos_norm( content, id, size, normalized_pos, color )
|
||||||
}
|
}
|
||||||
|
|
||||||
|
OVER_SAMPLE_ZOOM : f32 : 2.0 // Adjust this value as needed
|
||||||
|
|
||||||
|
// Adapt the draw_text_string_pos_extent_zoomed procedure
|
||||||
|
draw_text_zoomed_norm :: proc(content : string, id : FontID, size : f32, pos : Vec2, zoom : f32, color := COLOR_WHITE)
|
||||||
|
{
|
||||||
|
screen_size := demo_ctx.screen_size
|
||||||
|
screen_scale := Vec2{1.0 / screen_size.x, 1.0 / screen_size.y}
|
||||||
|
zoom_adjust_size := size * zoom
|
||||||
|
|
||||||
|
// Over-sample font-size
|
||||||
|
|
||||||
|
zoom_adjust_size *= OVER_SAMPLE_ZOOM
|
||||||
|
|
||||||
|
ve_id, resolved_size := font_provider_resolve_draw_id(id, zoom_adjust_size)
|
||||||
|
|
||||||
|
text_scale := screen_scale
|
||||||
|
{
|
||||||
|
f32_resolved_size := f32(resolved_size)
|
||||||
|
diff_scalar := 1 + (zoom_adjust_size - f32_resolved_size) / f32_resolved_size
|
||||||
|
text_scale = diff_scalar * screen_scale
|
||||||
|
text_scale.x = clamp(text_scale.x, 0, 1)
|
||||||
|
text_scale.y = clamp(text_scale.y, 0, 1)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Down-sample back
|
||||||
|
text_scale /= OVER_SAMPLE_ZOOM
|
||||||
|
|
||||||
|
color_norm := normalize_rgba8(color)
|
||||||
|
ve.set_colour(&demo_ctx.ve_ctx, color_norm)
|
||||||
|
ve.draw_text(&demo_ctx.ve_ctx, ve_id, content, pos, text_scale)
|
||||||
|
}
|
||||||
|
|
||||||
sokol_app_alloc :: proc "c" ( size : u64, user_data : rawptr ) -> rawptr {
|
sokol_app_alloc :: proc "c" ( size : u64, user_data : rawptr ) -> rawptr {
|
||||||
context = runtime.default_context()
|
context = runtime.default_context()
|
||||||
block, error := mem.alloc( int(size), allocator = context.allocator )
|
block, error := mem.alloc( int(size), allocator = context.allocator )
|
||||||
@@ -217,15 +271,57 @@ init :: proc "c" ()
|
|||||||
case .DUMMY: fmt.println(">> using dummy backend")
|
case .DUMMY: fmt.println(">> using dummy backend")
|
||||||
}
|
}
|
||||||
|
|
||||||
ve.startup( & demo_ctx.ve_ctx, .STB_TrueType, allocator = context.allocator )
|
ve.startup( & demo_ctx.ve_ctx, .STB_TrueType, allocator = context.allocator, snap_shape_position = false )
|
||||||
ve_sokol.setup_gfx_objects( & demo_ctx.render_ctx, & demo_ctx.ve_ctx, vert_cap = 1024 * 1024, index_cap = 1024 * 1024 )
|
ve_sokol.setup_gfx_objects( & demo_ctx.render_ctx, & demo_ctx.ve_ctx, vert_cap = 1024 * 1024, index_cap = 1024 * 1024 )
|
||||||
|
|
||||||
error : mem.Allocator_Error
|
error : mem.Allocator_Error
|
||||||
demo_ctx.font_ids, error = make( map[string]FontDef, 256 )
|
demo_ctx.font_ids, error = make( map[string]FontDef, 256 )
|
||||||
assert( error == .None, "Failed to allocate demo_ctx.font_ids" )
|
assert( error == .None, "Failed to allocate demo_ctx.font_ids" )
|
||||||
|
|
||||||
path_firacode := strings.concatenate( { Path_Fonts, "FiraCode-Regular.ttf" } )
|
path_sawarabi_mincho := strings.concatenate({ PATH_FONTS, "SawarabiMincho-Regular.ttf" })
|
||||||
demo_ctx.font_firacode = font_load( path_firacode, 16.0, "FiraCode" )
|
path_open_sans := strings.concatenate({ PATH_FONTS, "OpenSans-Regular.ttf" })
|
||||||
|
path_noto_sans_jp := strings.concatenate({ PATH_FONTS, "NotoSansJP-Light.otf" })
|
||||||
|
path_ubuntu_mono := strings.concatenate({ PATH_FONTS, "UbuntuMono-Regular.ttf" })
|
||||||
|
path_roboto := strings.concatenate({ PATH_FONTS, "Roboto-Regular.ttf" })
|
||||||
|
path_bitter := strings.concatenate({ PATH_FONTS, "Bitter-Regular.ttf" })
|
||||||
|
path_dancing_script := strings.concatenate({ PATH_FONTS, "DancingScript-Regular.ttf" })
|
||||||
|
path_nova_mono := strings.concatenate({ PATH_FONTS, "NovaMono-Regular.ttf" })
|
||||||
|
path_noto_serif_sc := strings.concatenate({ PATH_FONTS, "NotoSerifSC-Regular.otf" })
|
||||||
|
path_nanum_pen_script := strings.concatenate({ PATH_FONTS, "NanumPenScript-Regular.ttf" })
|
||||||
|
path_krub := strings.concatenate({ PATH_FONTS, "Krub-Regular.ttf" })
|
||||||
|
path_tajawal := strings.concatenate({ PATH_FONTS, "Tajawal-Regular.ttf" })
|
||||||
|
path_david_libre := strings.concatenate({ PATH_FONTS, "DavidLibre-Regular.ttf" })
|
||||||
|
path_noto_sans_jp_reg := strings.concatenate({ PATH_FONTS, "NotoSansJP-Regular.otf" })
|
||||||
|
path_firacode := strings.concatenate({ PATH_FONTS, "FiraCode-Regular.ttf" })
|
||||||
|
|
||||||
|
using demo_ctx
|
||||||
|
font_logo = font_load(path_sawarabi_mincho, 330.0, "SawarabiMincho", 6 )
|
||||||
|
font_title = font_load(path_open_sans, 92.0, "OpenSans", 12 )
|
||||||
|
font_print = font_load(path_noto_sans_jp, 19.0, "NotoSansJP")
|
||||||
|
font_mono = font_load(path_ubuntu_mono, 21.0, "UbuntuMono")
|
||||||
|
font_small = font_load(path_roboto, 10.0, "Roboto")
|
||||||
|
font_demo_sans = font_load(path_open_sans, 18.0, "OpenSans")
|
||||||
|
font_demo_serif = font_load(path_bitter, 18.0, "Bitter")
|
||||||
|
font_demo_script = font_load(path_dancing_script, 22.0, "DancingScript")
|
||||||
|
font_demo_mono = font_load(path_nova_mono, 18.0, "NovaMono")
|
||||||
|
font_demo_chinese = font_load(path_noto_serif_sc, 24.0, "NotoSerifSC")
|
||||||
|
font_demo_japanese = font_load(path_sawarabi_mincho, 24.0, "SawarabiMincho")
|
||||||
|
font_demo_korean = font_load(path_nanum_pen_script, 36.0, "NanumPenScript")
|
||||||
|
font_demo_thai = font_load(path_krub, 24.0, "Krub")
|
||||||
|
font_demo_arabic = font_load(path_tajawal, 24.0, "Tajawal")
|
||||||
|
font_demo_hebrew = font_load(path_david_libre, 22.0, "DavidLibre")
|
||||||
|
font_demo_raincode = font_load(path_noto_sans_jp_reg, 20.0, "NotoSansJPRegular")
|
||||||
|
font_demo_grid2 = font_load(path_noto_serif_sc, 54.0, "NotoSerifSC")
|
||||||
|
font_demo_grid3 = font_load(path_bitter, 44.0, "Bitter")
|
||||||
|
font_firacode = font_load(path_firacode, 16.0, "FiraCode", 12 )
|
||||||
|
}
|
||||||
|
|
||||||
|
event :: proc "c" (sokol_event : ^app.Event)
|
||||||
|
{
|
||||||
|
#partial switch sokol_event.type {
|
||||||
|
case .MOUSE_SCROLL:
|
||||||
|
demo_ctx.mouse_scroll = clamp(sokol_event.scroll_y, -1, 1) * -1
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
frame :: proc "c" ()
|
frame :: proc "c" ()
|
||||||
@@ -235,43 +331,391 @@ frame :: proc "c" ()
|
|||||||
demo_ctx.screen_size = { app.widthf(), app.heightf() }
|
demo_ctx.screen_size = { app.widthf(), app.heightf() }
|
||||||
|
|
||||||
pass_action : gfx.Pass_Action;
|
pass_action : gfx.Pass_Action;
|
||||||
pass_action.colors[0] = { load_action = .CLEAR, clear_value = { 0.18 * 0.18, 0.204 * 0.204, 0.251 * 0.251, 1.0 } }
|
pass_action.colors[0] = { load_action = .CLEAR, clear_value = { 0.18, 0.204, 0.251, 1.0 } }
|
||||||
gfx.begin_pass({ action = pass_action, swapchain = glue.swapchain() })
|
gfx.begin_pass({ action = pass_action, swapchain = glue.swapchain() })
|
||||||
gfx.end_pass()
|
gfx.end_pass()
|
||||||
{
|
{
|
||||||
ve.configure_snap( & demo_ctx.ve_ctx, u32(demo_ctx.screen_size.x), u32(demo_ctx.screen_size.y) )
|
ve.configure_snap( & demo_ctx.ve_ctx, u32(demo_ctx.screen_size.x), u32(demo_ctx.screen_size.y) )
|
||||||
ve.set_colour( & demo_ctx.ve_ctx, ve.Colour { 1.0, 1.0, 1.0, 1.0 })
|
ve.set_colour( & demo_ctx.ve_ctx, ve.Colour { 1.0, 1.0, 1.0, 1.0 })
|
||||||
|
|
||||||
ve_id, size := font_provider_resolve_draw_id( demo_ctx.font_firacode, 100 )
|
using demo_ctx
|
||||||
ve.draw_text(
|
|
||||||
& demo_ctx.ve_ctx,
|
|
||||||
ve_id,
|
|
||||||
"Hello VE FONT CACHE???",
|
|
||||||
Vec2{0.1, 0.1},
|
|
||||||
Vec2{1 / demo_ctx.screen_size.x, 1 / demo_ctx.screen_size.y }
|
|
||||||
)
|
|
||||||
|
|
||||||
draw_text_string_pos_extent( "Hello VEFontCache!", demo_ctx.font_firacode, 48, {0, 0}, Color_White )
|
// Smooth scrolling implementation
|
||||||
draw_text_string_pos_norm( "Hello VEFontCache!", demo_ctx.font_firacode, 24, {0, 0}, Color_White )
|
@static demo_autoscroll := true
|
||||||
|
@static current_scroll : f32 = 0.0
|
||||||
|
@static mouse_down_pos : f32 = -1.0
|
||||||
|
@static mouse_down_scroll : f32 = -1.0
|
||||||
|
@static mouse_prev_pos : f32 = 0.0
|
||||||
|
@static scroll_velocity : f32 = 0.0
|
||||||
|
|
||||||
|
frame_duration := cast(f32) app.frame_duration()
|
||||||
|
|
||||||
|
scroll_velocity += mouse_scroll.y * 0.05
|
||||||
|
mouse_down_pos = -1.0
|
||||||
|
substep_dt := frame_duration / 4.0
|
||||||
|
for _ in 0 ..< 4 {
|
||||||
|
scroll_velocity *= math.exp(-3.5 * substep_dt)
|
||||||
|
current_scroll += scroll_velocity * substep_dt * 18.0
|
||||||
|
}
|
||||||
|
if demo_autoscroll {
|
||||||
|
current_scroll += 0.05 * frame_duration
|
||||||
|
}
|
||||||
|
mouse_scroll = {} // Reset mouse scroll
|
||||||
|
|
||||||
|
// Clamp scroll value if needed
|
||||||
|
current_scroll = clamp(current_scroll, 0, 5.2) // Adjust max value as needed
|
||||||
|
|
||||||
|
// Frametime display
|
||||||
|
frametime_text := fmt.tprintf("Frametime %v", frame_duration)
|
||||||
|
draw_text_string_pos_norm(frametime_text, font_title, 0, {0.0, 0.0}, COLOR_WHITE)
|
||||||
|
|
||||||
|
if current_scroll < 1.5 {
|
||||||
|
intro := `Ça va! Everything here is rendered using VE Font Cache, a single header-only library designed for game engines.
|
||||||
|
It aims to:
|
||||||
|
• Be fast and simple to integrate.
|
||||||
|
• Take advantage of modern GPU power.
|
||||||
|
• Be backend agnostic and easy to port to any API such as Vulkan, DirectX, OpenGL.
|
||||||
|
• Load TTF & OTF file formats directly.
|
||||||
|
• Use only runtime cache with no offline calculation.
|
||||||
|
• Render glyphs at reasonable quality at a wide range of hb_font sizes.
|
||||||
|
• Support a good amount of internationalisation. そうですね!
|
||||||
|
• Support cached text shaping with HarfBuzz with simple Latin-style fallback.
|
||||||
|
• Load and unload fonts at any time.`
|
||||||
|
|
||||||
|
draw_text_string_pos_norm("ゑ", font_logo, 330, {0.4, current_scroll}, COLOR_WHITE)
|
||||||
|
draw_text_string_pos_norm("VEFontCache Demo", font_title, 92, {0.2, current_scroll - 0.1}, COLOR_WHITE)
|
||||||
|
draw_text_string_pos_norm(intro, font_print, 19, {0.2, current_scroll - 0.14}, COLOR_WHITE)
|
||||||
|
}
|
||||||
|
|
||||||
|
section_start : f32 = 0.42
|
||||||
|
section_end : f32 = 2.32
|
||||||
|
if current_scroll > section_start && current_scroll < section_end {
|
||||||
|
how_it_works := `Glyphs are GPU rasterised with 16x supersampling. This method is a simplification of "Easy Scalable Text Rendering on the GPU",
|
||||||
|
by Evan Wallace, making use of XOR blending. Bézier curves are handled via brute force triangle tessellation; even 6 triangles per
|
||||||
|
curve only generates < 300 triangles, which is nothing for modern GPUs! This avoids complex frag shader for reasonable quality.
|
||||||
|
|
||||||
|
Texture atlas caching uses naïve grid placement; this wastes a lot of space but ensures interchangeable cache slots allowing for
|
||||||
|
LRU ( Least Recently Used ) caching scheme to be employed.
|
||||||
|
The hb_font atlas is a single 4k x 2k R8 texture divided into 4 regions:`
|
||||||
|
|
||||||
|
caching_strategy := ` 2k
|
||||||
|
--------------------
|
||||||
|
| | |
|
||||||
|
| A | |
|
||||||
|
| | | 2
|
||||||
|
|---------| C | k
|
||||||
|
| | |
|
||||||
|
1k | B | |
|
||||||
|
| | |
|
||||||
|
--------------------
|
||||||
|
| |
|
||||||
|
| |
|
||||||
|
| | 2
|
||||||
|
| D | k
|
||||||
|
| |
|
||||||
|
| |
|
||||||
|
| |
|
||||||
|
--------------------
|
||||||
|
|
||||||
|
Region A = 32x32 caches, 1024 glyphs
|
||||||
|
Region B = 32x64 caches, 512 glyphs
|
||||||
|
Region C = 64x64 caches, 512 glyphs
|
||||||
|
Region D = 128x128 caches, 256 glyphs`
|
||||||
|
|
||||||
|
how_it_works2 := `Region A is designed for small glyphs, Region B is for tall glyphs, Region C is for large glyphs, and Region D for huge glyphs.
|
||||||
|
Glyphs are first rendered to an intermediate 2k x 512px R8 texture. This allows for minimum 4 Region D glyphs supersampled at
|
||||||
|
4 x 4 = 16x supersampling, and 8 Region C glyphs similarly. A simple 16-tap box downsample shader is then used to blit from this
|
||||||
|
intermediate texture to the final atlas location.`
|
||||||
|
|
||||||
|
draw_text_string_pos_norm("How it works", font_title, 92, {0.2, current_scroll - (section_start + 0.06)}, COLOR_WHITE)
|
||||||
|
draw_text_string_pos_norm(how_it_works, font_print, 19, {0.2, current_scroll - (section_start + 0.1)}, COLOR_WHITE)
|
||||||
|
draw_text_string_pos_norm(caching_strategy, demo_ctx.font_mono, 21, {0.28, current_scroll - (section_start + 0.32)}, COLOR_WHITE)
|
||||||
|
draw_text_string_pos_norm(how_it_works2, font_print, 19, {0.2, current_scroll - (section_start + 0.82)}, COLOR_WHITE)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Showcase section
|
||||||
|
section_start, section_end = 1.2, 3.2
|
||||||
|
if current_scroll > section_start && current_scroll < section_end
|
||||||
|
{
|
||||||
|
font_family_test := `Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor
|
||||||
|
incididunt ut labore et dolore magna aliqua. Est ullamcorper eget nulla facilisi
|
||||||
|
etiam dignissim diam quis enim. Convallis convallis tellus id interdum.`
|
||||||
|
|
||||||
|
draw_text_string_pos_norm("Showcase", font_title, 92, {0.2, current_scroll - (section_start + 0.2)}, COLOR_WHITE)
|
||||||
|
draw_text_string_pos_norm("This is a showcase demonstrating different hb_font categories and languages.", font_print, 19, {0.2, current_scroll - (section_start + 0.24)}, COLOR_WHITE)
|
||||||
|
|
||||||
|
draw_text_string_pos_norm("Sans serif", font_print, 19, {0.2, current_scroll - (section_start + 0.28)}, COLOR_WHITE)
|
||||||
|
draw_text_string_pos_norm(font_family_test, font_demo_sans, 18, {0.3, current_scroll - (section_start + 0.28)}, COLOR_WHITE)
|
||||||
|
|
||||||
|
draw_text_string_pos_norm("Serif", font_print, 19, {0.2, current_scroll - (section_start + 0.36)}, COLOR_WHITE)
|
||||||
|
draw_text_string_pos_norm(font_family_test, font_demo_serif, 18, {0.3, current_scroll - (section_start + 0.36)}, COLOR_WHITE)
|
||||||
|
|
||||||
|
draw_text_string_pos_norm("Script", font_print, 19, {0.2, current_scroll - (section_start + 0.44)}, COLOR_WHITE)
|
||||||
|
draw_text_string_pos_norm(font_family_test, font_demo_script, 22, {0.3, current_scroll - (section_start + 0.44)}, COLOR_WHITE)
|
||||||
|
|
||||||
|
draw_text_string_pos_norm("Monospace", font_print, 19, {0.2, current_scroll - (section_start + 0.52)}, COLOR_WHITE)
|
||||||
|
draw_text_string_pos_norm(font_family_test, font_demo_mono, 18, {0.3, current_scroll - (section_start + 0.52)}, COLOR_WHITE)
|
||||||
|
|
||||||
|
draw_text_string_pos_norm("Small", font_print, 19, {0.2, current_scroll - (section_start + 0.60)}, COLOR_WHITE)
|
||||||
|
draw_text_string_pos_norm(font_family_test, font_small, 10, {0.3, current_scroll - (section_start + 0.60)}, COLOR_WHITE)
|
||||||
|
|
||||||
|
draw_text_string_pos_norm("Greek", font_print, 19, {0.2, current_scroll - (section_start + 0.72)}, COLOR_WHITE)
|
||||||
|
draw_text_string_pos_norm("Ήταν απλώς θέμα χρόνου.", font_demo_sans, 18, {0.3, current_scroll - (section_start + 0.72)}, COLOR_WHITE)
|
||||||
|
|
||||||
|
draw_text_string_pos_norm("Vietnamese", font_print, 19, {0.2, current_scroll - (section_start + 0.76)}, COLOR_WHITE)
|
||||||
|
draw_text_string_pos_norm("Bầu trời trong xanh thăm thẳm, không một gợn mây.", font_demo_sans, 18, {0.3, current_scroll - (section_start + 0.76)}, COLOR_WHITE)
|
||||||
|
|
||||||
|
draw_text_string_pos_norm("Thai", font_print, 19, {0.2, current_scroll - (section_start + 0.80)}, COLOR_WHITE)
|
||||||
|
draw_text_string_pos_norm("การเดินทางขากลับคงจะเหงา", font_demo_thai, 24, {0.3, current_scroll - (section_start + 0.80)}, COLOR_WHITE)
|
||||||
|
|
||||||
|
draw_text_string_pos_norm("Chinese", font_print, 19, {0.2, current_scroll - (section_start + 0.84)}, COLOR_WHITE)
|
||||||
|
draw_text_string_pos_norm("床前明月光 疑是地上霜 举头望明月 低头思故乡", font_demo_chinese, 24, {0.3, current_scroll - (section_start + 0.84)}, COLOR_WHITE)
|
||||||
|
|
||||||
|
draw_text_string_pos_norm("Japanese", font_print, 19, {0.2, current_scroll - (section_start + 0.88)}, COLOR_WHITE)
|
||||||
|
draw_text_string_pos_norm("ぎょしょうとナレズシの研究 モンスーン・アジアの食事文化", font_demo_japanese, 24, {0.3, current_scroll - (section_start + 0.88)}, COLOR_WHITE)
|
||||||
|
|
||||||
|
draw_text_string_pos_norm("Korean", font_print, 19, {0.2, current_scroll - (section_start + 0.92)}, COLOR_WHITE)
|
||||||
|
draw_text_string_pos_norm("그들의 장비와 기구는 모두 살아 있다.", font_demo_korean, 36, {0.3, current_scroll - (section_start + 0.92)}, COLOR_WHITE)
|
||||||
|
|
||||||
|
draw_text_string_pos_norm("Arabic", font_print, 19, {0.2, current_scroll - (section_start + 0.96)}, COLOR_WHITE)
|
||||||
|
draw_text_string_pos_norm("حب السماء لا تمطر غير الأحلام. This one needs HarfBuzz to work!", font_demo_arabic, 24, {0.3, current_scroll - (section_start + 0.96)}, COLOR_WHITE)
|
||||||
|
|
||||||
|
draw_text_string_pos_norm("Hebrew", font_print, 19, {0.2, current_scroll - (section_start + 1.0)}, COLOR_WHITE)
|
||||||
|
draw_text_string_pos_norm("אז הגיע הלילה של כוכב השביט הראשון. This one needs HarfBuzz to work!", font_demo_hebrew, 22, {0.3, current_scroll - (section_start + 1.0)}, COLOR_WHITE)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Raincode Demo
|
||||||
|
section_start = 2.1
|
||||||
|
section_end = section_start + 2.23
|
||||||
|
if current_scroll > section_start && current_scroll < section_end
|
||||||
|
{
|
||||||
|
GRID_W :: 80
|
||||||
|
GRID_H :: 50
|
||||||
|
NUM_RAINDROPS :: GRID_W / 3
|
||||||
|
|
||||||
|
@static init_grid := false
|
||||||
|
@static grid : [ GRID_W * GRID_H ]int
|
||||||
|
@static grid_age : [ GRID_W * GRID_H ]f32
|
||||||
|
@static raindropsX : [ NUM_RAINDROPS ]int
|
||||||
|
@static raindropsY : [ NUM_RAINDROPS ]int
|
||||||
|
@static code_colour : RGBA8
|
||||||
|
|
||||||
|
@static codes := [?]string {
|
||||||
|
" ", "0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "Z", "T", "H", "E", "|", "¦", "日",
|
||||||
|
"ハ", "ミ", "ヒ", "ー", "ウ", "シ", "ナ", "モ", "ニ", "サ", "ワ", "ツ", "オ", "リ", "ア", "ホ", "テ", "マ",
|
||||||
|
"ケ", "メ", "エ", "カ", "キ", "ム", "ユ", "ラ", "セ", "ネ", "ス", "ツ", "タ", "ヌ", "ヘ", ":", "・", ".",
|
||||||
|
"\"", "=", "*", "+", "-", "<", ">", "ç", "リ", "ク", "コ", "チ", "ヤ", "ル", "ン", "C", "O", "D"
|
||||||
|
}
|
||||||
|
|
||||||
|
if !init_grid {
|
||||||
|
for idx in 0..<NUM_RAINDROPS do raindropsY[idx] = GRID_H
|
||||||
|
init_grid = true
|
||||||
|
}
|
||||||
|
|
||||||
|
@static fixed_timestep_passed : f32 = 0.0
|
||||||
|
fixed_timestep : f32 = (1.0 / 60.0)
|
||||||
|
fixed_timestep_passed += frame_duration
|
||||||
|
for fixed_timestep_passed > fixed_timestep
|
||||||
|
{
|
||||||
|
for idx in 0 ..< (GRID_W * GRID_H) do grid_age[idx] += frame_duration
|
||||||
|
for idx in 0 ..< NUM_RAINDROPS {
|
||||||
|
raindropsY[idx] += 1
|
||||||
|
if raindropsY[idx] < 0 do continue
|
||||||
|
if raindropsY[idx] >= GRID_H {
|
||||||
|
raindropsY[idx] = -5 - rand.int_max(40)
|
||||||
|
raindropsX[idx] = rand.int_max(GRID_W)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
grid [ raindropsY[idx] * GRID_W + raindropsX[idx] ] = rand.int_max(len(codes))
|
||||||
|
grid_age[ raindropsY[idx] * GRID_W + raindropsX[idx] ] = 0.0
|
||||||
|
}
|
||||||
|
fixed_timestep_passed = 0
|
||||||
|
}
|
||||||
|
|
||||||
|
// Draw grid
|
||||||
|
draw_text_string_pos_norm("Raincode demo", font_title, 92, { 0.2, current_scroll - (section_start + 0.2) }, COLOR_WHITE)
|
||||||
|
for y in 0 ..< GRID_H do for x in 0 ..< GRID_W
|
||||||
|
{
|
||||||
|
pos_x := 0.2 + f32(x) * 0.007
|
||||||
|
pos_y := current_scroll - (section_start + 0.24 + f32(y) * 0.018)
|
||||||
|
age := grid_age[y * GRID_W + x]
|
||||||
|
|
||||||
|
code_colour = {255, 255, 255, 255}
|
||||||
|
if age > 0.0 {
|
||||||
|
code_colour = {
|
||||||
|
51 + 30,
|
||||||
|
77 + 30,
|
||||||
|
102 + 30,
|
||||||
|
u8(clamp((1.0 - age) * 255, 0, 255) ) }
|
||||||
|
if code_colour.a == 0 do continue
|
||||||
|
}
|
||||||
|
|
||||||
|
draw_text_string_pos_norm(codes[grid[y * GRID_W + x]], font_demo_raincode, 20, {pos_x, pos_y}, code_colour)
|
||||||
|
}
|
||||||
|
|
||||||
|
ve.set_colour(&ve_ctx, {1.0, 1.0, 1.0, 1.0})
|
||||||
|
}
|
||||||
|
|
||||||
|
// Zoom Test
|
||||||
|
section_start = 3.3
|
||||||
|
section_end = 4.8
|
||||||
|
if current_scroll > section_start && current_scroll < section_end
|
||||||
|
{
|
||||||
|
zoom_text := `Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor
|
||||||
|
incididunt ut labore et dolore magna aliqua. Est ullamcorper eget nulla facilisi
|
||||||
|
etiam dignissim diam quis enim. Convallis convallis tellus id interdum.`
|
||||||
|
|
||||||
|
@static zoom_time: f32 = 0
|
||||||
|
zoom_time += frame_duration
|
||||||
|
zoom_duration :: 10.0 // Time for one complete zoom cycle in seconds
|
||||||
|
|
||||||
|
modified_linear_zoom :: proc( delta : f32) -> f32
|
||||||
|
{
|
||||||
|
// Adjust these values to control the time spent at min/max zoom
|
||||||
|
min_threshold :: 0.05
|
||||||
|
max_threshold :: 0.95
|
||||||
|
|
||||||
|
if delta < min_threshold do return 0
|
||||||
|
else if delta > max_threshold do return 1
|
||||||
|
else do return (delta - min_threshold) / (max_threshold - min_threshold)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Calculate the current zoom
|
||||||
|
delta := (math.sin(2 * math.PI * zoom_time / zoom_duration) + 1) / 2 // Normalize sine wave to 0-1
|
||||||
|
zoom_t := modified_linear_zoom(delta)
|
||||||
|
current_zoom := math.lerp(f32(1.0), f32(20.0), zoom_t) // Zoom range from 0.5x to 50x
|
||||||
|
|
||||||
|
// Calculate positions with reduced gaps
|
||||||
|
scroll_offset := current_scroll - section_start
|
||||||
|
title_y := current_scroll - (section_start + 0.05)
|
||||||
|
zoom_info_y := current_scroll - (section_start + 0.10)
|
||||||
|
zoomed_text_y := current_scroll - (section_start + 0.30) + math.sin(zoom_time) * 0.02
|
||||||
|
|
||||||
|
draw_text_string_pos_norm("Zoom Test", font_title, 92, {0.2, title_y}, COLOR_WHITE)
|
||||||
|
|
||||||
|
zoomed_text_base_size : f32 = 12.0
|
||||||
|
zoom_adjust_size := zoomed_text_base_size * current_zoom
|
||||||
|
ve_id, resolved_size := font_provider_resolve_draw_id( font_firacode, zoom_adjust_size * OVER_SAMPLE_ZOOM )
|
||||||
|
current_zoom_text := fmt.tprintf("Current Zoom : %.2f x\nCurrent Resolved Size: %v px", current_zoom, resolved_size )
|
||||||
|
draw_text_string_pos_norm(current_zoom_text, font_firacode, 19, {0.2, zoom_info_y}, COLOR_WHITE)
|
||||||
|
|
||||||
|
ve.configure_snap( & demo_ctx.ve_ctx, u32(0), u32(0) )
|
||||||
|
|
||||||
|
size := measure_text_size( zoom_text, font_firacode, zoomed_text_base_size, 0 ) * current_zoom
|
||||||
|
x_offset := (size.x / demo_ctx.screen_size.x) * 0.5
|
||||||
|
zoomed_text_pos := Vec2 { 0.5 - x_offset, zoomed_text_y }
|
||||||
|
draw_text_zoomed_norm(zoom_text, font_firacode, zoomed_text_base_size, zoomed_text_pos, current_zoom, COLOR_WHITE)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Cache pressure test
|
||||||
|
section_start = 4.3
|
||||||
|
section_end = 5.3
|
||||||
|
if current_scroll > section_start && current_scroll < section_end && true
|
||||||
|
{
|
||||||
|
GRID_W :: 30
|
||||||
|
GRID_H :: 15
|
||||||
|
GRID2_W :: 8
|
||||||
|
GRID2_H :: 2
|
||||||
|
GRID3_W :: 16
|
||||||
|
GRID3_H :: 4
|
||||||
|
|
||||||
|
@static grid : [GRID_W * GRID_H ]int
|
||||||
|
@static grid2 : [GRID2_W * GRID2_H]int
|
||||||
|
@static grid3 : [GRID3_W * GRID3_H]int
|
||||||
|
|
||||||
|
@static rotate_current : int = 0
|
||||||
|
@static fixed_timestep_passed : f32 = 0.0
|
||||||
|
|
||||||
|
fixed_timestep_passed += frame_duration
|
||||||
|
fixed_timestep := f32(1.0 / 60.0)
|
||||||
|
for fixed_timestep_passed > fixed_timestep
|
||||||
|
{
|
||||||
|
rotate_current = (rotate_current + 1) % 4
|
||||||
|
rotate_idx := 0
|
||||||
|
for & g in grid
|
||||||
|
{
|
||||||
|
if (rotate_idx % 4) != rotate_current {
|
||||||
|
rotate_idx += 1
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
g = 0x4E00 + rand.int_max(0x9FFF - 0x4E00)
|
||||||
|
rotate_idx += 1
|
||||||
|
}
|
||||||
|
for & g in grid2 do g = 0x4E00 + rand.int_max(0x9FFF - 0x4E00)
|
||||||
|
for & g in grid3 do g = rand.int_max(128)
|
||||||
|
fixed_timestep_passed -= fixed_timestep
|
||||||
|
}
|
||||||
|
|
||||||
|
codepoint_to_utf8 :: proc(c: []u8, chr: int) {
|
||||||
|
if chr == 0 {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
else if (0xffffff80 & chr) == 0 {
|
||||||
|
c[0] = u8(chr)
|
||||||
|
}
|
||||||
|
else if (0xfffff800 & chr) == 0 {
|
||||||
|
c[0] = 0xc0 | u8(chr >> 6)
|
||||||
|
c[1] = 0x80 | u8(chr & 0x3f)
|
||||||
|
}
|
||||||
|
else if (0xffff0000 & chr) == 0 {
|
||||||
|
c[0] = 0xe0 | u8(chr >> 12)
|
||||||
|
c[1] = 0x80 | u8((chr >> 6) & 0x3f)
|
||||||
|
c[2] = 0x80 | u8(chr & 0x3f)
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
c[0] = 0xf0 | u8(chr >> 18)
|
||||||
|
c[1] = 0x80 | u8((chr >> 12) & 0x3f)
|
||||||
|
c[2] = 0x80 | u8((chr >> 6) & 0x3f)
|
||||||
|
c[3] = 0x80 | u8(chr & 0x3f)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Draw grid
|
||||||
|
draw_text_string_pos_norm("Cache pressure test", font_title, 92, {0.2, current_scroll - (section_start + 0.2)}, COLOR_WHITE)
|
||||||
|
for y in 0..< GRID_H do for x in 0 ..< GRID_W
|
||||||
|
{
|
||||||
|
posx := 0.2 + f32(x) * 0.02
|
||||||
|
posy := current_scroll - (section_start + 0.24 + f32(y) * 0.025)
|
||||||
|
c := [5]u8{}
|
||||||
|
codepoint_to_utf8(c[:], grid[ y * GRID_W + x ])
|
||||||
|
draw_text_string_pos_norm(string( c[:] ), font_demo_chinese, 24, {posx, posy}, COLOR_WHITE)
|
||||||
|
}
|
||||||
|
for y in 0 ..< GRID2_H do for x in 0 ..< GRID2_W {
|
||||||
|
posx := 0.2 + f32(x) * 0.03
|
||||||
|
posy := current_scroll - (section_start + 0.66 + f32(y) * 0.052)
|
||||||
|
c := [5]u8{}
|
||||||
|
codepoint_to_utf8(c[:], grid2[ y * GRID2_W + x ])
|
||||||
|
draw_text_string_pos_norm(string( c[:] ), font_demo_grid2, 54, {posx, posy}, COLOR_WHITE)
|
||||||
|
}
|
||||||
|
for y in 0 ..< GRID3_H do for x in 0 ..< GRID3_W {
|
||||||
|
posx := 0.45 + f32(x) * 0.02
|
||||||
|
posy := current_scroll - (section_start + 0.64 + f32(y) * 0.034)
|
||||||
|
c := [5]u8{}
|
||||||
|
codepoint_to_utf8( c[:], grid3[ y * GRID3_W + x ])
|
||||||
|
draw_text_string_pos_norm(string( c[:] ), font_demo_grid3, 44, {posx, posy}, COLOR_WHITE)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
ve_sokol.render_text_layer(demo_ctx.screen_size * 0.5, & demo_ctx.ve_ctx, demo_ctx.render_ctx)
|
ve_sokol.render_text_layer(demo_ctx.screen_size * 0.5, & demo_ctx.ve_ctx, demo_ctx.render_ctx)
|
||||||
}
|
}
|
||||||
|
|
||||||
gfx.commit()
|
gfx.commit()
|
||||||
ve.flush_draw_list( & demo_ctx.ve_ctx )
|
ve.flush_draw_list( & demo_ctx.ve_ctx )
|
||||||
}
|
}
|
||||||
|
|
||||||
cleanup :: proc "c" () {
|
cleanup :: proc "c" () {
|
||||||
context = runtime.default_context()
|
context = runtime.default_context()
|
||||||
ve.shutdown( & demo_ctx.ve_ctx )
|
// ve.shutdown( & demo_ctx.ve_ctx )
|
||||||
gfx.shutdown()
|
// gfx.shutdown()
|
||||||
}
|
}
|
||||||
|
|
||||||
main :: proc()
|
main :: proc()
|
||||||
{
|
{
|
||||||
demo_ctx.screen_size = Vec2 { 1600, 900 }
|
demo_ctx.screen_size = Vec2 { 1920, 1080 }
|
||||||
|
|
||||||
app.run({
|
app.run({
|
||||||
init_cb = init,
|
init_cb = init,
|
||||||
|
event_cb = event,
|
||||||
frame_cb = frame,
|
frame_cb = frame,
|
||||||
cleanup_cb = cleanup,
|
cleanup_cb = cleanup,
|
||||||
width = i32(demo_ctx.screen_size.x),
|
width = i32(demo_ctx.screen_size.x),
|
||||||
|
@@ -94,10 +94,10 @@ function build-SokolBackendDemo
|
|||||||
# $build_args += $flag_micro_architecture_native
|
# $build_args += $flag_micro_architecture_native
|
||||||
$build_args += $flag_use_separate_modules
|
$build_args += $flag_use_separate_modules
|
||||||
$build_args += $flag_thread_count + $CoreCount_Physical
|
$build_args += $flag_thread_count + $CoreCount_Physical
|
||||||
$build_args += $flag_optimize_none
|
# $build_args += $flag_optimize_none
|
||||||
# $build_args += $flag_optimize_minimal
|
# $build_args += $flag_optimize_minimal
|
||||||
# $build_args += $flag_optimize_speed
|
# $build_args += $flag_optimize_speed
|
||||||
# $build_args += $falg_optimize_aggressive
|
$build_args += $falg_optimize_aggressive
|
||||||
$build_args += $flag_debug
|
$build_args += $flag_debug
|
||||||
$build_args += $flag_pdb_name + $pdb
|
$build_args += $flag_pdb_name + $pdb
|
||||||
$build_args += $flag_subsystem + 'windows'
|
$build_args += $flag_subsystem + 'windows'
|
||||||
|
Reference in New Issue
Block a user