package sokol_demo import "base:runtime" import "core:path/filepath" file_name_from_path :: filepath.short_stem import "core:fmt" import "core:math" import "core:math/rand" import "core:mem" import "core:os" import "core:strings" import ve "../../vefontcache" import ve_sokol "backend:sokol" import app "thirdparty:sokol/app" import gfx "thirdparty:sokol/gfx" import glue "thirdparty:sokol/glue" import slog "thirdparty:sokol/log" Vec2 :: ve.Vec2 RGBA8 :: struct { r, g, b, a : u8 } RGBAN :: [4]f32 normalize_rgba8 :: #force_inline proc( color : RGBA8 ) -> RGBAN { quotient : f32 = 1.0 / 255 result := RGBAN { f32(color.r) * quotient, f32(color.g) * quotient, f32(color.b) * quotient, f32(color.a) * quotient, } return result } COLOR_BLUE :: RGBA8 { 90, 90, 230, 255 } COLOR_RED :: RGBA8 { 230, 90, 90, 255 } COLOR_WHITE :: RGBA8 { 255, 255, 255, 255 } FONT_LARGEST_PIXEL_SIZE :: 400 FONT_SIZE_INTERVAL :: 2 FONT_DEFAULT :: Font_ID { "" } FONT_DEFAULT_SIZEZ :: 12.0 FONT_LOAD_USE_DEFAULT_SIZE :: -1 FONT_LOAD_GEN_ID :: "" // Working directory assumed to be the build folder PATH_FONTS :: "../fonts/" OVER_SAMPLE_ZOOM : f32 : 2.0 // Adjust this value as needed, used by draw_text_zoomed_norm Font_ID :: struct { label : string, } FontDef :: struct { path_file : string, default_size : i32, size_table : [FONT_LARGEST_PIXEL_SIZE / FONT_SIZE_INTERVAL] ve.Font_ID, } Demo_Context :: struct { ve_ctx : ve.Context, render_ctx : ve_sokol.Context, font_ids : map[string]FontDef, // Values between 1, & -1 on Y axis mouse_scroll : Vec2, font_firacode : Font_ID, font_logo : Font_ID, font_title : Font_ID, font_print : Font_ID, font_mono : Font_ID, font_small : Font_ID, font_demo_sans : Font_ID, font_demo_serif : Font_ID, font_demo_script : Font_ID, font_demo_mono : Font_ID, font_demo_chinese : Font_ID, font_demo_japanese : Font_ID, font_demo_korean : Font_ID, font_demo_thai : Font_ID, font_demo_arabic : Font_ID, font_demo_hebrew : Font_ID, font_demo_raincode : Font_ID, font_demo_grid2 : Font_ID, font_demo_grid3 : Font_ID, screen_size : [2]f32, } demo_ctx : Demo_Context font_load :: proc(path_file : string, default_size : i32 = FONT_LOAD_USE_DEFAULT_SIZE, desired_id : string = FONT_LOAD_GEN_ID, curve_quality : u32 = 3, ) -> Font_ID { msg := fmt.println("Loading font: %v", path_file) font_data, read_succeded : = os.read_entire_file( path_file ) assert( bool(read_succeded), fmt.tprintf("Failed to read font file for: %v", path_file) ) font_data_size := cast(i32) len(font_data) font_firacode : Font_ID desired_id := desired_id if len(desired_id) == 0 { fmt.println("desired_key not provided, using file name. Give it a proper name!") desired_id = file_name_from_path(path_file) } demo_ctx.font_ids[desired_id] = FontDef {} def := & demo_ctx.font_ids[desired_id] default_size := default_size if default_size < 0 { default_size = FONT_DEFAULT_SIZEZ } def.path_file = path_file def.default_size = default_size 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) ve_id := & def.size_table[id - 1] ve_ret_id := ve.load_font( & demo_ctx.ve_ctx, desired_id, font_data, f32(font_size), curve_quality ) (ve_id^) = ve_ret_id } fid := Font_ID { desired_id } return fid } Font_Use_Default_Size :: f32(0.0) font_resolve_draw_id :: proc( id : Font_ID, size := Font_Use_Default_Size ) -> ( ve_id : ve.Font_ID, resolved_size : i32 ) { def := demo_ctx.font_ids[ id.label ] size := size == 0.0 ? f32(def.default_size) : size even_size := math.round(size * (1.0 / f32(FONT_SIZE_INTERVAL))) * f32(FONT_SIZE_INTERVAL) resolved_size = clamp( i32( even_size), 2, FONT_LARGEST_PIXEL_SIZE ) id := (resolved_size / FONT_SIZE_INTERVAL) + (resolved_size % FONT_SIZE_INTERVAL) ve_id = def.size_table[ id - 1 ] return } measure_text_size :: proc( text : string, font : Font_ID, font_size := Font_Use_Default_Size, spacing : f32 ) -> Vec2 { ve_id, size := font_resolve_draw_id( font, font_size ) measured := ve.measure_text_size( & demo_ctx.ve_ctx, ve_id, text ) return measured } get_font_vertical_metrics :: #force_inline proc ( font : Font_ID, font_size := Font_Use_Default_Size ) -> ( ascent, descent, line_gap : f32 ) { ve_id, size := font_resolve_draw_id( font, font_size ) ascent, descent, line_gap = ve.get_font_vertical_metrics( & demo_ctx.ve_ctx, ve_id ) return } // Draw text using a string and normalized render coordinates draw_text_string_pos_norm :: proc( content : string, id : Font_ID, size : f32, pos : Vec2, color := COLOR_WHITE, scale : f32 = 1.0 ) { width := demo_ctx.screen_size.x height := demo_ctx.screen_size.y ve_id, resolved_size := font_resolve_draw_id( id, size ) 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, 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 : Font_ID, size : f32, pos : Vec2, color := COLOR_WHITE ) { render_pos := pos + demo_ctx.screen_size * 0.5 normalized_pos := render_pos * (1.0 / demo_ctx.screen_size) draw_text_string_pos_norm( content, id, size, normalized_pos, color ) } // Adapt the draw_text_string_pos_extent_zoomed procedure draw_text_zoomed_norm :: proc(content : string, id : Font_ID, 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_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 : uint, user_data : rawptr ) -> rawptr { context = runtime.default_context() block, error := mem.alloc( int(size), allocator = context.allocator ) assert(error == .None, "sokol_app allocation failed") return block } sokol_app_free :: proc "c" ( data : rawptr, user_data : rawptr ) { context = runtime.default_context() free(data, allocator = context.allocator) } sokol_gfx_alloc :: proc "c" ( size : uint, user_data : rawptr ) -> rawptr { context = runtime.default_context() block, error := mem.alloc( int(size), allocator = context.allocator ) assert(error == .None, "sokol_gfx allocation failed") return block } sokol_gfx_free :: proc "c" ( data : rawptr, user_data : rawptr ) { context = runtime.default_context() free(data, allocator = context.allocator ) } init :: proc "c" () { context = runtime.default_context() desc := gfx.Desc { buffer_pool_size = 128, image_pool_size = 128, sampler_pool_size = 64, shader_pool_size = 32, pipeline_pool_size = 64, attachments_pool_size = 16, uniform_buffer_size = 4 * mem.Megabyte, max_commit_listeners = 1024, allocator = { sokol_gfx_alloc, sokol_gfx_free, nil }, logger = { func = slog.func }, environment = glue.environment(), } gfx.setup(desc) // just some debug output what backend we're running on switch gfx.query_backend() { case .D3D11 : fmt.println(">> using D3D11 backend") case .GLCORE, .GLES3: fmt.println(">> using GL backend") case .METAL_MACOS, .METAL_IOS, .METAL_SIMULATOR: fmt.println(">> using Metal backend") case .WGPU : fmt.println(">> using WebGPU backend") case .DUMMY: fmt.println(">> using dummy backend") } ve.startup( & demo_ctx.ve_ctx, .STB_TrueType, allocator = context.allocator, snap_shape_position = false, use_advanced_text_shaper = true ) 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 demo_ctx.font_ids, error = make( map[string]FontDef, 256 ) assert( error == .None, "Failed to allocate demo_ctx.font_ids" ) path_sawarabi_mincho := strings.concatenate({ PATH_FONTS, "SawarabiMincho-Regular.ttf" }) 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" () { context = runtime.default_context() demo_ctx.screen_size = { app.widthf(), app.heightf() } pass_action : gfx.Pass_Action; 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.end_pass() { 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 }) using demo_ctx // Smooth scrolling implementation @static demo_autoscroll := false @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.0 * substep_dt) current_scroll += scroll_velocity * substep_dt * 10.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, 6.1) // 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("Needs harfbuzz to work:", font_print, 14, {0.2, current_scroll - (section_start + 0.96)}, COLOR_WHITE) draw_text_string_pos_norm("Arabic", font_print, 19, {0.2, current_scroll - (section_start + 1.00)}, COLOR_WHITE) draw_text_string_pos_norm("حب السماء لا تمطر غير الأحلام.", font_demo_arabic, 24, {0.3, current_scroll - (section_start + 1.00)}, COLOR_WHITE) draw_text_string_pos_norm("Hebrew", font_print, 19, {0.2, current_scroll - (section_start + 1.04)}, COLOR_WHITE) draw_text_string_pos_norm("אז הגיע הלילה של כוכב השביט הראשון.", font_demo_hebrew, 22, {0.3, current_scroll - (section_start + 1.04)}, COLOR_WHITE) } // Zoom Test section_start = 2.3 section_end = section_start + 2.23 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_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) } // Raincode Demo section_start = 3.6 section_end = 5.4 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.. 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}) } // Cache pressure test section_start = 5.3 section_end = 6.2 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 / 30.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) } gfx.commit() ve.flush_draw_list( & demo_ctx.ve_ctx ) } cleanup :: proc "c" () { context = runtime.default_context() ve.shutdown( & demo_ctx.ve_ctx ) gfx.shutdown() } main :: proc() { demo_ctx.screen_size = Vec2 { 1920, 1080 } app.run({ init_cb = init, event_cb = event, frame_cb = frame, cleanup_cb = cleanup, width = i32(demo_ctx.screen_size.x), height = i32(demo_ctx.screen_size.y), window_title = "VEFonCache: Sokol Backend Demo", icon = { sokol_default = true }, logger = { func = slog.func }, allocator = { sokol_app_alloc, sokol_app_free, nil }, }) }