package main import rl "libraries/raylib" import fixed_pool "libraries/pool" import "base:runtime" import "core:fmt" WINDOW_WIDTH :: 1280 WINDOW_HEIGHT :: 720 WINDOW_FRAMERATE :: 30 WINDOW_CAPTION :: "Transformation Visualizer" MATRIX_FIELD_COUNT :: 1024 MATRIX_FIELD_BYTES :: 8 program: Program FontStyle :: struct { font: rl.Font, size, spacing: f32 } ProgramState :: enum { Normal, Message, } ProgramMessageType :: enum { ChangeMatrixTypeClearData, } @rodata program_messages := [ProgramMessageType]cstring { .ChangeMatrixTypeClearData = "Changing the matrix type will delete all current matrices.\nAre you sure you want to do this?", } Program :: struct { ui_state: UiContext, matrix_field_pool: FieldPool(f32), matrix_mode_perceived: MatrixType, matrix_mode_actual: MatrixType, grid: Grid, font_style: FontStyle, state: ProgramState, message_type: ProgramMessageType, } Grid :: struct { area: rect2, inner_area: rect2, zoom: f32, } FIELD_BYTE_COUNT :: 8 FieldInput :: [FIELD_BYTE_COUNT]byte FieldPool :: struct($T: typeid) { values: fixed_pool.Pool(T), inputs: fixed_pool.Pool(FieldInput), } field_pool_get :: proc(pool: ^FieldPool($T), count: int) -> (values: []T, inputs: []FieldInput) { return fixed_pool.get(&pool.values, count), fixed_pool.get(&pool.inputs, count) } field_pool_clear :: proc(pool: ^FieldPool($T)) { fixed_pool.clear(&pool.values) fixed_pool.clear(&pool.inputs) } field_pool_init :: proc(pool: ^FieldPool($T), capacity: int, allocator := context.allocator) { fixed_pool.init(&pool.values, capacity, allocator) fixed_pool.init(&pool.inputs, capacity, allocator) } field_pool_free_range :: proc(pool: ^FieldPool($T), from: int, count: int) { fixed_pool.free_range(&pool.values, from, count) fixed_pool.free_range(&pool.inputs, from, count) } Field :: struct($T: typeid) { value: f32, input: FieldInput, } field_pool_get_allocated_elems :: proc(pool: ^FieldPool($T)) -> #soa[]Field(T) { values := fixed_pool.get_allocated_elems(pool.values) fields := fixed_pool.get_allocated_elems(pool.inputs) return soa_zip(value = values, input = fields) } draw_text :: proc(text: cstring, position: v2, style: FontStyle, color: rl.Color) { rl.DrawTextEx(style.font, text, position, style.size, style.spacing, color) } TextHalign :: enum { Left, Middle, Right, } TextValign :: enum { Top, Center, Bottom, } draw_text_aligned :: proc(text: cstring, position: v2, style: FontStyle, halign: TextHalign, valign: TextValign, color: rl.Color) { size := measure_text(text, style) position := position switch halign { case .Left: case .Middle: position.x -= size.x / 2.0 case .Right: position.x -= size.x } switch valign { case .Top: case .Center: position.y -= size.y / 2.0 case .Bottom: position.y -= size.y } draw_text(text, position, style, color) } UiButton_AddMat2CallbackData :: struct { program: ^Program, mat: mat2, } program_add_mat2 :: proc(data: rawptr) { data := cast(^UiButton_AddMat2CallbackData)data field_pool_add_mat2(&data.program.matrix_field_pool, data.mat) } field_pool_add_mat2 :: proc(pool: ^FieldPool(f32), m: mat2) { values, _ := field_pool_get(pool, 4) m := m floats := cast([^]f32)&m[0, 0] #unroll for i in 0..<4 {values[i] = floats[i]} } UiButton_AddMat3CallbackData :: struct { program: ^Program, mat: mat3, } program_add_mat3 :: proc(data: rawptr) { data := cast(^UiButton_AddMat3CallbackData)data field_pool_add_mat3(&data.program.matrix_field_pool, data.mat) } field_pool_add_mat3 :: proc(pool: ^FieldPool(f32), m: mat3) { values, _ := field_pool_get(pool, 9) m := m floats := cast([^]f32)&m[0, 0] #unroll for i in 0..<9 {values[i] = floats[i]} } UiButton_AddMat4CallbackData :: struct { program: ^Program, mat: mat4, } program_add_mat4 :: proc(data: rawptr) { data := cast(^UiButton_AddMat4CallbackData)data field_pool_add_mat4(&data.program.matrix_field_pool, data.mat) } field_pool_add_mat4 :: proc(pool: ^FieldPool(f32), m: mat4) { values, _ := field_pool_get(pool, 16) m := m floats := cast([^]f32)&m[0, 0] #unroll for i in 0..<16 {values[i] = floats[i]} } measure_text :: proc(text: cstring, style: FontStyle) -> v2 { return rl.MeasureTextEx(style.font, text, style.size, style.spacing) } MatrixType :: enum { Mat2, Mat3, Mat4, } @rodata matrix_type_table := [MatrixType]typeid { .Mat2 = mat2, .Mat3 = mat3, .Mat4 = mat4, } init :: proc() { rl.SetTraceLogLevel(.WARNING) rl.SetConfigFlags({.WINDOW_RESIZABLE, .VSYNC_HINT}) rl.InitWindow(WINDOW_WIDTH, WINDOW_HEIGHT, WINDOW_CAPTION) rl.SetTargetFPS(WINDOW_FRAMERATE) field_pool_init(&program.matrix_field_pool, 4096, context.allocator) program.font_style = { font = rl.GetFontDefault(), size = 16.0, spacing = 1.0 } program.grid = { area = {0, 0, WINDOW_WIDTH, WINDOW_HEIGHT}, inner_area = {-1, -1, 2, 2}, zoom = 1.0, } ui_style := UiStyle{ margin = 4.0, padding = 4.0, font_style = program.font_style, } ui_init(&program.ui_state, ui_style, {0, 0}, context.temp_allocator) } program_set_message_state :: proc(program: ^Program, message: ProgramMessageType) { program.state = .Message program.message_type = message } program_matrix_mode_dropdown_callback :: proc(previous, current: i32, data: rawptr) { program := cast(^Program)data program_set_message_state(program, .ChangeMatrixTypeClearData) } program_add_matrix_button_data :: proc(label: cstring, matrix_type: MatrixType, allocator := context.temp_allocator) -> UiElementData_Button { callback: UiElementCallback_Button callback_data: rawptr switch program.matrix_mode_actual { case .Mat2: callback = program_add_mat2 data := new(UiButton_AddMat2CallbackData, context.temp_allocator) data^ = {program = &program, mat = mat2{0, 1, 2, 3}} callback_data = cast(rawptr)data case .Mat3: callback = program_add_mat3 data := new(UiButton_AddMat3CallbackData, context.temp_allocator) data^ = {program = &program, mat = mat3{0, 1, 2, 3, 4, 5, 6, 7, 8}} callback_data = cast(rawptr)data case .Mat4: callback = program_add_mat4 data := new(UiButton_AddMat4CallbackData, context.temp_allocator) data^ = {program = &program, mat = mat4{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15}} callback_data = cast(rawptr)data } return {label = label, callback = callback, callback_data = callback_data} } program_build_ui :: proc() -> []UiElement { ui_button_size_top_bar := v2{32.0, 32.0} ui_dropdown_size_top_bar := v2{96.0, 32.0} ui_start(&program.ui_state) ui_container_begin({label = ""}) matrix_add_data := program_add_matrix_button_data("#10#", program.matrix_mode_actual, context.temp_allocator) ui_button(ui_button_size_top_bar, matrix_add_data) ui_same_line() ui_button(ui_button_size_top_bar, {label = "#11#"}) ui_same_line() ui_button(ui_button_size_top_bar, {label = "#12#"}) ui_same_line() matrix_options := enum_to_raygui_options_string(MatrixType, context.temp_allocator) matrix_mode_ref := cast(^i32)&program.matrix_mode_perceived ui_dropdown(ui_dropdown_size_top_bar, { label = matrix_options, active = matrix_mode_ref, callback = program_matrix_mode_dropdown_callback, callback_data = cast(rawptr)&program}) ui_container_end() ui_container_begin({label = "Matrices"}) field_pool := &program.matrix_field_pool if fixed_pool.usage(field_pool.values) == 0 { ui_text({text = "Add matrices", color = rl.WHITE}) } else { inputs := fixed_pool.get_allocated_elements(program.matrix_field_pool.inputs) matrix_count := len(inputs) / 4 field_size := v2{48.0, 32.0} for i in 0.. bool { when ODIN_OS != .JS { if rl.WindowShouldClose() { return false } } return true }