Files
VEFontCache-Odin/examples/sokol_demo/sokol_demo.odin
2025-02-13 19:44:25 -05:00

693 lines
27 KiB
Odin
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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_SIZE :: 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,
}
Font_Entry :: struct {
path_file : string,
data : []byte,
default_size : i32,
ve_id : ve.Font_ID,
}
Demo_Context :: struct {
ve_ctx : ve.Context,
render_ctx : ve_sokol.Context,
font_ids : map[string]Font_Entry,
// 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] = Font_Entry {}
def := & demo_ctx.font_ids[desired_id]
default_size := default_size
if default_size < 0 {
default_size = FONT_DEFAULT_SIZE
}
error : ve.Load_Font_Error
def.path_file = path_file
def.default_size = default_size
def.data = font_data
def.ve_id, error = ve.load_font( & demo_ctx.ve_ctx, desired_id, font_data, curve_quality )
assert(error == .None)
fid := Font_ID { desired_id }
return fid
}
Font_Use_Default_Size :: f32(0.0)
measure_text_size :: proc( text : string, font : Font_ID, font_size := Font_Use_Default_Size, spacing : f32 ) -> Vec2
{
def := demo_ctx.font_ids[ font.label ]
measured := ve.measure_text_size( & demo_ctx.ve_ctx, def.ve_id, font_size, text )
return measured
}
get_font_vertical_metrics :: #force_inline proc ( font : Font_ID, font_size := Font_Use_Default_Size ) -> ( ascent, descent, line_gap : f32 )
{
def := demo_ctx.font_ids[ font.label ]
ascent, descent, line_gap = ve.get_font_vertical_metrics( demo_ctx.ve_ctx, def.ve_id, font_size )
return
}
// Draw text using a string and normalized render coordinates
draw_text :: proc( content : string, font : Font_ID, pos : Vec2, size : f32 = 0.0, color := COLOR_WHITE, scale : f32 = 1.0, zoom : f32 = 1.0 )
{
color_norm := normalize_rgba8(color)
def := demo_ctx.font_ids[ font.label ]
size := size >= 2.0 ? size : f32(def.default_size)
resolved_size, zoom_scale := ve.resolve_zoom_size_scale( zoom, size, scale, 2, 2, 999.0, demo_ctx.screen_size )
snapped_pos := ve.snap_normalized_position_to_view( pos, demo_ctx.screen_size )
norm_scale := zoom_scale * (1 / demo_ctx.screen_size)
ve.draw_text_normalized_space( & demo_ctx.ve_ctx,
def.ve_id,
resolved_size,
color_norm,
snapped_pos,
norm_scale,
content
)
return
}
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")
}
glyph_draw_opts := ve.Init_Glyph_Draw_Params_Default
glyph_draw_opts.snap_glyph_width = false
glyph_draw_opts.snap_glyph_height = false
shaper_opts := ve.Init_Shaper_Params_Default
shaper_opts.snap_glyph_position = true
ve.startup( & demo_ctx.ve_ctx, .STB_TrueType, allocator = context.allocator,
glyph_draw_params = glyph_draw_opts,
shaper_params = shaper_opts,
px_scalar = 1.4,
alpha_sharpen = 0.1,
)
ve_sokol.setup_gfx_objects( & demo_ctx.render_ctx, & demo_ctx.ve_ctx, vert_cap = 256 * 1024, index_cap = 512 * 1024 )
error : mem.Allocator_Error
demo_ctx.font_ids, error = make( map[string]Font_Entry, 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" })
demo_ctx.font_logo = font_load(path_sawarabi_mincho, 150.0, "SawarabiMincho", 18 )
demo_ctx.font_print = font_load(path_noto_sans_jp, 19.0, "NotoSansJP")
demo_ctx.font_mono = font_load(path_ubuntu_mono, 21.0, "UbuntuMono")
demo_ctx.font_small = font_load(path_roboto, 10.0, "Roboto")
demo_ctx.font_demo_sans = font_load(path_open_sans, 18.0, "OpenSans", 6)
demo_ctx.font_demo_serif = font_load(path_bitter, 18.0, "Bitter")
demo_ctx.font_demo_script = font_load(path_dancing_script, 22.0, "DancingScript")
demo_ctx.font_demo_mono = font_load(path_nova_mono, 18.0, "NovaMono")
demo_ctx.font_demo_chinese = font_load(path_noto_serif_sc, 24.0, "NotoSerifSC")
demo_ctx.font_demo_japanese = font_load(path_sawarabi_mincho, 24.0, "SawarabiMincho")
demo_ctx.font_demo_korean = font_load(path_nanum_pen_script, 36.0, "NanumPenScript")
demo_ctx.font_demo_thai = font_load(path_krub, 24.0, "Krub")
demo_ctx.font_demo_arabic = font_load(path_tajawal, 24.0, "Tajawal")
demo_ctx.font_demo_hebrew = font_load(path_david_libre, 22.0, "DavidLibre")
demo_ctx.font_demo_raincode = font_load(path_noto_sans_jp_reg, 20.0, "NotoSansJPRegular")
demo_ctx.font_title = demo_ctx.font_demo_sans
demo_ctx.font_demo_grid2 = demo_ctx.font_demo_chinese
demo_ctx.font_demo_grid3 = demo_ctx.font_demo_serif
demo_ctx.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 })
// 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 += demo_ctx.mouse_scroll.y * 0.035
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
}
demo_ctx.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(frametime_text, demo_ctx.font_title, {0.0, 0.0}, size = 30)
// Below is content based on the original demo from the C++ library.
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("ゑ", demo_ctx.font_logo, { 0.4, current_scroll }, size = 200, scale = 2.0)
draw_text("VEFontCache Demo", demo_ctx.font_title, { 0.2, current_scroll - 0.1 }, size = 92)
draw_text(intro, demo_ctx.font_print, { 0.2, current_scroll - 0.14 }, size = 19)
}
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("How it works", demo_ctx.font_title, { 0.2, current_scroll - (section_start + 0.06) }, size = 92)
draw_text(how_it_works, demo_ctx.font_print, { 0.2, current_scroll - (section_start + 0.1) }, size = 19)
draw_text(caching_strategy, demo_ctx.font_mono, { 0.28, current_scroll - (section_start + 0.32) }, size = 21)
draw_text(how_it_works2, demo_ctx.font_print, { 0.2, current_scroll - (section_start + 0.82) }, size = 19)
}
// 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("Showcase", demo_ctx.font_title, { 0.2, current_scroll - (section_start + 0.2) }, size = 92)
draw_text("This is a showcase demonstrating different hb_font categories and languages.", demo_ctx.font_print, { 0.2, current_scroll - (section_start + 0.24) }, size = 19)
draw_text("Sans serif", demo_ctx.font_print, { 0.2, current_scroll - (section_start + 0.28) }, size = 19)
draw_text(font_family_test, demo_ctx.font_demo_sans, { 0.3, current_scroll - (section_start + 0.28) }, size = 18)
draw_text("Serif", demo_ctx.font_print, { 0.2, current_scroll - (section_start + 0.36) }, size = 19)
draw_text(font_family_test, demo_ctx.font_demo_serif, { 0.3, current_scroll - (section_start + 0.36) }, size = 18)
draw_text("Script", demo_ctx.font_print, { 0.2, current_scroll - (section_start + 0.44) }, size = 19)
draw_text(font_family_test, demo_ctx.font_demo_script, { 0.3, current_scroll - (section_start + 0.44) }, size = 22)
draw_text("Monospace", demo_ctx.font_print, { 0.2, current_scroll - (section_start + 0.52) }, size = 19)
draw_text(font_family_test, demo_ctx.font_demo_mono, { 0.3, current_scroll - (section_start + 0.52) }, size = 22)
draw_text("Small", demo_ctx.font_print, { 0.2, current_scroll - (section_start + 0.60) }, size = 19)
draw_text(font_family_test, demo_ctx.font_small, { 0.3, current_scroll - (section_start + 0.60) }, size = 10)
draw_text("Greek", demo_ctx.font_print, { 0.2, current_scroll - (section_start + 0.72) }, size = 19)
draw_text("Ήταν απλώς θέμα χρόνου.", demo_ctx.font_demo_sans, { 0.3, current_scroll - (section_start + 0.72) }, size = 18)
draw_text("Vietnamese", demo_ctx.font_print, { 0.2, current_scroll - (section_start + 0.76) }, size = 19)
draw_text("Bầu trời trong xanh thăm thẳm, không một gợn mây.", demo_ctx.font_demo_sans, { 0.3, current_scroll - (section_start + 0.76) }, size = 18)
draw_text("Thai", demo_ctx.font_print, { 0.2, current_scroll - (section_start + 0.80) }, size = 19)
draw_text("การเดินทางขากลับคงจะเหงา", demo_ctx.font_demo_thai, { 0.3, current_scroll - (section_start + 0.80) }, size = 24)
draw_text("Chinese", demo_ctx.font_print, { 0.2, current_scroll - (section_start + 0.84) }, size = 19)
draw_text("床前明月光 疑是地上霜 举头望明月 低头思故乡", demo_ctx.font_demo_chinese, {0.3, current_scroll - (section_start + 0.84) }, size = 24)
draw_text("Japanese", demo_ctx.font_print, { 0.2, current_scroll - (section_start + 0.88) }, size = 19)
draw_text("ぎょしょうとナレズシの研究 モンスーン・アジアの食事文化", demo_ctx.font_demo_japanese, { 0.3, current_scroll - (section_start + 0.88) }, size = 24)
draw_text("Korean", demo_ctx.font_print, { 0.2, current_scroll - (section_start + 0.92) }, size = 19)
draw_text("그들의 장비와 기구는 모두 살아 있다.", demo_ctx.font_demo_korean, { 0.3, current_scroll - (section_start + 0.92) }, size = 36)
draw_text("Needs harfbuzz to work:", demo_ctx.font_print, { 0.2, current_scroll - (section_start + 0.96)}, size = 14)
draw_text("Arabic", demo_ctx.font_print, { 0.2, current_scroll - (section_start + 1.00) }, size = 19)
draw_text("حب السماء لا تمطر غير الأحلام.", demo_ctx.font_demo_arabic, { 0.3, current_scroll - (section_start + 1.00) }, size = 24)
draw_text("Hebrew", demo_ctx.font_print, { 0.2, current_scroll - (section_start + 1.04) }, size = 19)
draw_text("אז הגיע הלילה של כוכב השביט הראשון.", demo_ctx.font_demo_hebrew, { 0.3, current_scroll - (section_start + 1.04) }, size = 22)
}
// 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("Zoom Test", demo_ctx.font_title, { 0.2, title_y }, size = 92)
zoomed_text_base_size : f32 = 12.0
zoom_adjust_size := zoomed_text_base_size * current_zoom
resolved_size, _ := ve.resolve_zoom_size_scale(current_zoom, zoomed_text_base_size, 1.0, 2, 2, 999.0, demo_ctx.screen_size)
current_zoom_text := fmt.tprintf("Current Zoom : %.2f x\nCurrent Resolved Size: %v px", current_zoom, resolved_size )
draw_text(current_zoom_text, demo_ctx.font_firacode, { 0.2, zoom_info_y })
size := measure_text_size( zoom_text, demo_ctx.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(zoom_text, demo_ctx.font_firacode, zoomed_text_pos, size = zoomed_text_base_size, zoom = current_zoom)
}
// Raincode Demo
section_start = 3.6
section_end = 5.4
if current_scroll > section_start && current_scroll < section_end
{
GRID_W :: 80
GRID_H :: 45
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 / 30.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("Raincode demo", demo_ctx.font_title, { 0.2, current_scroll - (section_start + 0.2) }, size = 92)
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(codes[grid[y * GRID_W + x]], demo_ctx.font_demo_raincode, { pos_x, pos_y }, size = 20, color = code_colour)
}
}
// Cache pressure test
section_start = 5.3
section_end = 6.2
if current_scroll > section_start && current_scroll < section_end
{
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 / 120.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("Cache pressure test (throttled to 120 hz)", demo_ctx.font_title, { 0.2, current_scroll - (section_start + 0.2) }, size = 72)
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( c[:] ), demo_ctx.font_demo_chinese, { posx, posy }, size = 24)
}
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( c[:] ), demo_ctx.font_demo_grid2, { posx, posy }, size = 54)
}
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.04)
c := [5]u8{}
codepoint_to_utf8( c[:], grid3[ y * GRID3_W + x ])
draw_text(string( c[:] ), demo_ctx.font_demo_grid3, { posx, posy }, size = 44)
}
}
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 },
})
}