|
|
@@ -1,60 +1,220 @@
|
|
|
package main
|
|
|
|
|
|
import "base:runtime"
|
|
|
-
|
|
|
+import "core:fmt"
|
|
|
import mu "third-party:microui"
|
|
|
-
|
|
|
import sapp "third-party:sokol/app"
|
|
|
import sg "third-party:sokol/gfx"
|
|
|
import sgl "third-party:sokol/gl"
|
|
|
import sglue "third-party:sokol/glue"
|
|
|
import slog "third-party:sokol/log"
|
|
|
|
|
|
-state: struct {
|
|
|
+State :: struct {
|
|
|
+ pip: sg.Pipeline,
|
|
|
+ bind: sg.Bindings,
|
|
|
pass_action: sg.Pass_Action,
|
|
|
-} = {
|
|
|
- pass_action = {colors = {0 = {load_action = .CLEAR, clear_value = {1.0, 0.0, 1.0, 1.0}}}},
|
|
|
+ mu_ctx: mu.Context,
|
|
|
+
|
|
|
+ // Ресурсы
|
|
|
+ atlas_img: sg.Image,
|
|
|
+ atlas_view: sg.View,
|
|
|
+ atlas_smp: sg.Sampler,
|
|
|
+ ui_pip: sgl.Pipeline,
|
|
|
}
|
|
|
|
|
|
-mu_ctx: mu.Context
|
|
|
+state: State
|
|
|
+
|
|
|
+map_mouse_button :: proc(btn: sapp.Mousebutton) -> (mu.Mouse, bool) {
|
|
|
+ switch btn {
|
|
|
+ case .LEFT:
|
|
|
+ return .LEFT, true
|
|
|
+ case .RIGHT:
|
|
|
+ return .RIGHT, true
|
|
|
+ case .MIDDLE:
|
|
|
+ return .MIDDLE, true
|
|
|
+ case .INVALID:
|
|
|
+ return nil, false
|
|
|
+ }
|
|
|
+ return nil, false
|
|
|
+}
|
|
|
+
|
|
|
+map_key :: proc(key: sapp.Keycode) -> (mu.Key, bool) {
|
|
|
+ #partial switch key {
|
|
|
+ case .LEFT_SHIFT, .RIGHT_SHIFT:
|
|
|
+ return .SHIFT, true
|
|
|
+ case .LEFT_CONTROL, .RIGHT_CONTROL:
|
|
|
+ return .CTRL, true
|
|
|
+ case .LEFT_ALT, .RIGHT_ALT:
|
|
|
+ return .ALT, true
|
|
|
+ case .ENTER:
|
|
|
+ return .RETURN, true
|
|
|
+ case .BACKSPACE:
|
|
|
+ return .BACKSPACE, true
|
|
|
+ }
|
|
|
+ return nil, false
|
|
|
+}
|
|
|
+
|
|
|
+event :: proc "c" (ev: ^sapp.Event) {
|
|
|
+ context = runtime.default_context()
|
|
|
+
|
|
|
+ #partial switch ev.type {
|
|
|
+ case .MOUSE_DOWN:
|
|
|
+ if btn, ok := map_mouse_button(ev.mouse_button); ok {
|
|
|
+ mu.input_mouse_down(&state.mu_ctx, i32(ev.mouse_x), i32(ev.mouse_y), btn)
|
|
|
+ }
|
|
|
+
|
|
|
+ case .MOUSE_UP:
|
|
|
+ if btn, ok := map_mouse_button(ev.mouse_button); ok {
|
|
|
+ mu.input_mouse_up(&state.mu_ctx, i32(ev.mouse_x), i32(ev.mouse_y), btn)
|
|
|
+ }
|
|
|
+
|
|
|
+ case .MOUSE_MOVE:
|
|
|
+ mu.input_mouse_move(&state.mu_ctx, i32(ev.mouse_x), i32(ev.mouse_y))
|
|
|
+
|
|
|
+ case .MOUSE_SCROLL:
|
|
|
+ mu.input_scroll(&state.mu_ctx, 0, i32(ev.scroll_y))
|
|
|
+
|
|
|
+ case .KEY_DOWN:
|
|
|
+ if k, ok := map_key(ev.key_code); ok {
|
|
|
+ mu.input_key_down(&state.mu_ctx, k)
|
|
|
+ }
|
|
|
+
|
|
|
+ case .KEY_UP:
|
|
|
+ if k, ok := map_key(ev.key_code); ok {
|
|
|
+ mu.input_key_up(&state.mu_ctx, k)
|
|
|
+ }
|
|
|
+
|
|
|
+ case .CHAR:
|
|
|
+ if ev.char_code != 127 && ev.char_code >= 32 {
|
|
|
+ text := fmt.tprintf("%r", rune(ev.char_code))
|
|
|
+ mu.input_text(&state.mu_ctx, text)
|
|
|
+ }
|
|
|
+ }
|
|
|
+}
|
|
|
|
|
|
init :: proc "c" () {
|
|
|
context = runtime.default_context()
|
|
|
+
|
|
|
sg.setup({environment = sglue.environment(), logger = {func = slog.func}})
|
|
|
sgl.setup({logger = {func = slog.func}})
|
|
|
|
|
|
- mu.init(&mu_ctx)
|
|
|
- mu_ctx.text_width = mu.default_atlas_text_width
|
|
|
- mu_ctx.text_height = mu.default_atlas_text_height
|
|
|
-}
|
|
|
+ mu.init(&state.mu_ctx)
|
|
|
+ state.mu_ctx.text_width = mu.default_atlas_text_width
|
|
|
+ state.mu_ctx.text_height = mu.default_atlas_text_height
|
|
|
|
|
|
-frame :: proc "c" () {
|
|
|
- context = runtime.default_context()
|
|
|
+ width := mu.DEFAULT_ATLAS_WIDTH
|
|
|
+ height := mu.DEFAULT_ATLAS_HEIGHT
|
|
|
+ pixels := make([]u32, width * height)
|
|
|
+ defer delete(pixels)
|
|
|
+
|
|
|
+ for alpha, i in mu.default_atlas_alpha {
|
|
|
+ pixels[i] = 0x00FFFFFF | (u32(alpha) << 24)
|
|
|
+ }
|
|
|
+
|
|
|
+ img_desc: sg.Image_Desc
|
|
|
+ img_desc.width = i32(width)
|
|
|
+ img_desc.height = i32(height)
|
|
|
+ img_desc.pixel_format = .RGBA8
|
|
|
+ img_desc.data.mip_levels[0] = {
|
|
|
+ ptr = raw_data(pixels),
|
|
|
+ size = len(pixels) * 4,
|
|
|
+ }
|
|
|
+ state.atlas_img = sg.make_image(img_desc)
|
|
|
|
|
|
- t := f32(sapp.frame_duration() * 60.0)
|
|
|
+ view_desc: sg.View_Desc
|
|
|
+ view_desc.texture.image = state.atlas_img
|
|
|
+ state.atlas_view = sg.make_view(view_desc)
|
|
|
|
|
|
- mu.begin(&mu_ctx)
|
|
|
+ smp_desc: sg.Sampler_Desc
|
|
|
+ smp_desc.min_filter = .NEAREST
|
|
|
+ smp_desc.mag_filter = .NEAREST
|
|
|
+ state.atlas_smp = sg.make_sampler(smp_desc)
|
|
|
|
|
|
- @(static) opts := mu.Options{}
|
|
|
- if mu.begin_window(&mu_ctx, "Window", {0, 0, 400, 400}, opts) {
|
|
|
- mu.button(&mu_ctx, "Button")
|
|
|
+ pip_desc: sg.Pipeline_Desc
|
|
|
+ pip_desc.colors[0].blend.enabled = true
|
|
|
+ pip_desc.colors[0].blend.src_factor_rgb = .SRC_ALPHA
|
|
|
+ pip_desc.colors[0].blend.dst_factor_rgb = .ONE_MINUS_SRC_ALPHA
|
|
|
+ state.ui_pip = sgl.make_pipeline(pip_desc)
|
|
|
|
|
|
- mu.end_window(&mu_ctx)
|
|
|
+ vertices := [?]f32 {
|
|
|
+ 0.0,
|
|
|
+ 0.5,
|
|
|
+ 0.5,
|
|
|
+ 1.0,
|
|
|
+ 0.0,
|
|
|
+ 0.0,
|
|
|
+ 1.0,
|
|
|
+ 0.5,
|
|
|
+ -0.5,
|
|
|
+ 0.5,
|
|
|
+ 0.0,
|
|
|
+ 1.0,
|
|
|
+ 0.0,
|
|
|
+ 1.0,
|
|
|
+ -0.5,
|
|
|
+ -0.5,
|
|
|
+ 0.5,
|
|
|
+ 0.0,
|
|
|
+ 0.0,
|
|
|
+ 1.0,
|
|
|
+ 1.0,
|
|
|
}
|
|
|
+ state.bind.vertex_buffers[0] = sg.make_buffer(
|
|
|
+ {data = {ptr = &vertices, size = size_of(vertices)}},
|
|
|
+ )
|
|
|
+ state.pip = sg.make_pipeline(
|
|
|
+ {
|
|
|
+ shader = sg.make_shader(triangle_shader_desc(sg.query_backend())),
|
|
|
+ layout = {attrs = {0 = {format = .FLOAT3}, 1 = {format = .FLOAT4}}},
|
|
|
+ },
|
|
|
+ )
|
|
|
+ state.pass_action = {
|
|
|
+ colors = {0 = {load_action = .CLEAR, clear_value = {0.4, 0.4, 0.5, 1}}},
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+frame :: proc "c" () {
|
|
|
+ context = runtime.default_context()
|
|
|
+
|
|
|
+ mu.begin(&state.mu_ctx)
|
|
|
|
|
|
- mu.end(&mu_ctx)
|
|
|
+ if mu.begin_window(&state.mu_ctx, "Interactive Window", {40, 40, 300, 250}) {
|
|
|
+ mu.label(&state.mu_ctx, "Try interacting!")
|
|
|
+ mu.button(&state.mu_ctx, "Button")
|
|
|
+ mu.end_window(&state.mu_ctx)
|
|
|
+ }
|
|
|
+ mu.end(&state.mu_ctx)
|
|
|
|
|
|
sgl.defaults()
|
|
|
sgl.push_pipeline()
|
|
|
+ sgl.load_pipeline(state.ui_pip)
|
|
|
+ sgl.enable_texture()
|
|
|
+ sgl.texture(state.atlas_view, state.atlas_smp)
|
|
|
+
|
|
|
sgl.matrix_mode_projection()
|
|
|
sgl.push_matrix()
|
|
|
sgl.ortho(0.0, sapp.widthf(), sapp.heightf(), 0.0, -1.0, +1.0)
|
|
|
sgl.begin_quads()
|
|
|
+
|
|
|
current_command: ^mu.Command
|
|
|
- for cmd_variant in mu.next_command_iterator(&mu_ctx, ¤t_command) {
|
|
|
+ for cmd_variant in mu.next_command_iterator(&state.mu_ctx, ¤t_command) {
|
|
|
#partial switch cmd in cmd_variant {
|
|
|
case ^mu.Command_Rect:
|
|
|
draw_rect(cmd.rect, cmd.color)
|
|
|
+ case ^mu.Command_Text:
|
|
|
+ draw_text(cmd.str, cmd.pos, cmd.color)
|
|
|
+ case ^mu.Command_Icon:
|
|
|
+ draw_icon(cmd.id, cmd.rect, cmd.color)
|
|
|
+ case ^mu.Command_Clip:
|
|
|
+ sgl.end()
|
|
|
+ sgl.scissor_rect(
|
|
|
+ f32(cmd.rect.x),
|
|
|
+ f32(cmd.rect.y),
|
|
|
+ f32(cmd.rect.w),
|
|
|
+ f32(cmd.rect.h),
|
|
|
+ true,
|
|
|
+ )
|
|
|
+ sgl.begin_quads()
|
|
|
}
|
|
|
}
|
|
|
sgl.end()
|
|
|
@@ -62,11 +222,52 @@ frame :: proc "c" () {
|
|
|
sgl.pop_pipeline()
|
|
|
|
|
|
sg.begin_pass({action = state.pass_action, swapchain = sglue.swapchain()})
|
|
|
+ sg.apply_pipeline(state.pip)
|
|
|
+ sg.apply_bindings(state.bind)
|
|
|
+ sg.draw(0, 3, 1)
|
|
|
sgl.draw()
|
|
|
sg.end_pass()
|
|
|
sg.commit()
|
|
|
}
|
|
|
|
|
|
+draw_rect :: proc(rect: mu.Rect, color: mu.Color) {
|
|
|
+ push_quad(rect, mu.default_atlas[mu.DEFAULT_ATLAS_WHITE], color)
|
|
|
+}
|
|
|
+
|
|
|
+draw_text :: proc(str: string, pos: mu.Vec2, color: mu.Color) {
|
|
|
+ cur_pos := pos
|
|
|
+ for char in str {
|
|
|
+ idx := int(char)
|
|
|
+ if idx > 127 do idx = 0
|
|
|
+ rect := mu.default_atlas[mu.DEFAULT_ATLAS_FONT + idx]
|
|
|
+ dst := mu.Rect{cur_pos.x, cur_pos.y, rect.w, rect.h}
|
|
|
+ push_quad(dst, rect, color)
|
|
|
+ cur_pos.x += rect.w
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+draw_icon :: proc(id: mu.Icon, rect: mu.Rect, color: mu.Color) {
|
|
|
+ src := mu.default_atlas[int(id)]
|
|
|
+ x := rect.x + (rect.w - src.w) / 2
|
|
|
+ y := rect.y + (rect.h - src.h) / 2
|
|
|
+ push_quad({x, y, src.w, src.h}, src, color)
|
|
|
+}
|
|
|
+
|
|
|
+push_quad :: proc(dst: mu.Rect, src: mu.Rect, color: mu.Color) {
|
|
|
+ u0 := f32(src.x) / f32(mu.DEFAULT_ATLAS_WIDTH)
|
|
|
+ v0 := f32(src.y) / f32(mu.DEFAULT_ATLAS_HEIGHT)
|
|
|
+ u1 := f32(src.x + src.w) / f32(mu.DEFAULT_ATLAS_WIDTH)
|
|
|
+ v1 := f32(src.y + src.h) / f32(mu.DEFAULT_ATLAS_HEIGHT)
|
|
|
+ x0, y0 := f32(dst.x), f32(dst.y)
|
|
|
+ x1, y1 := f32(dst.x + dst.w), f32(dst.y + dst.h)
|
|
|
+
|
|
|
+ sgl.c4b(color.r, color.g, color.b, color.a)
|
|
|
+ sgl.v2f_t2f(x0, y0, u0, v0)
|
|
|
+ sgl.v2f_t2f(x1, y0, u1, v0)
|
|
|
+ sgl.v2f_t2f(x1, y1, u1, v1)
|
|
|
+ sgl.v2f_t2f(x0, y1, u0, v1)
|
|
|
+}
|
|
|
+
|
|
|
cleanup :: proc "c" () {
|
|
|
context = runtime.default_context()
|
|
|
sgl.shutdown()
|
|
|
@@ -79,34 +280,12 @@ main :: proc() {
|
|
|
init_cb = init,
|
|
|
frame_cb = frame,
|
|
|
cleanup_cb = cleanup,
|
|
|
+ event_cb = event,
|
|
|
width = 1280,
|
|
|
height = 720,
|
|
|
- sample_count = 4,
|
|
|
- window_title = "huginn",
|
|
|
+ window_title = "Sokol + MicroUI",
|
|
|
icon = {sokol_default = true},
|
|
|
logger = {func = slog.func},
|
|
|
},
|
|
|
)
|
|
|
}
|
|
|
-
|
|
|
-push_quad :: proc(dst: mu.Rect, src: mu.Rect, color: mu.Color) {
|
|
|
- u0 := f32(src.x) / f32(mu.DEFAULT_ATLAS_WIDTH)
|
|
|
- v0 := f32(src.y) / f32(mu.DEFAULT_ATLAS_HEIGHT)
|
|
|
- u1 := f32(src.x + src.w) / f32(mu.DEFAULT_ATLAS_WIDTH)
|
|
|
- v1 := f32(src.y + src.h) / f32(mu.DEFAULT_ATLAS_HEIGHT)
|
|
|
-
|
|
|
- x0 := f32(dst.x)
|
|
|
- y0 := f32(dst.y)
|
|
|
- x1 := f32(dst.x + dst.w)
|
|
|
- y1 := f32(dst.y + dst.h)
|
|
|
-
|
|
|
- sgl.c4b(color.r, color.g, color.b, color.a)
|
|
|
- sgl.v2f_t2f(x0, y0, u0, v0)
|
|
|
- sgl.v2f_t2f(x1, y0, u1, v0)
|
|
|
- sgl.v2f_t2f(x1, y1, u1, v1)
|
|
|
- sgl.v2f_t2f(x0, y1, u0, v1)
|
|
|
-}
|
|
|
-
|
|
|
-draw_rect :: proc(rect: mu.Rect, color: mu.Color) {
|
|
|
- push_quad(rect, mu.default_atlas[mu.DEFAULT_ATLAS_WHITE], color)
|
|
|
-}
|