code clean and color picker
authorBrendan Hansen <brendan.f.hansen@gmail.com>
Sat, 5 Mar 2022 22:00:33 +0000 (16:00 -0600)
committerBrendan Hansen <brendan.f.hansen@gmail.com>
Sat, 5 Mar 2022 22:00:33 +0000 (16:00 -0600)
run_tree/scenes/level1.scene
src/entity/components/background.onyx
src/entity/editor.onyx
src/entity/items.onyx
src/entity/scene.onyx
src/gfx/ui.onyx
src/main.onyx
src/sfx/audio_manager.onyx
src/utils/colors.onyx [new file with mode: 0644]

index c2d9a19317a125afe347639b644ad22756f90e9c..d7050a5ad42b605bd9247ae2831704ef23542847 100644 (file)
@@ -498,7 +498,7 @@ max_timeout = 2.0000
 [Player]
 id = 152
 flags = 2
-pos.x = 276.7525
+pos.x = 277.7525
 pos.y = 229.4651
 size.x = 16.0000
 size.y = 32.0000
@@ -515,7 +515,7 @@ velocity.y = 0.0000
 :PlayerComponent
 holding = 155
 :RenderComponent
-layer = 0
+layer = 10
 color.r = 1.0000
 color.g = 0.0000
 color.b = 1.0000
index 9473564d4fc72b762c832ce5b8ae8b640be0ed4c..2336e17666e2005a286c15aa069e03bfd297f53e 100644 (file)
@@ -18,6 +18,8 @@ BackgroundComponent :: struct {
         texture_wrap(^background_texture, .Repeat);
 
         r := entity->get_rect();
-        immediate_subimage(^background_texture, r.x, r.y, r.w, r.h, 0, 0, r.w, r.h);
+        tw := math.ceil(r.w / ~~background_texture.width) * ~~background_texture.width;
+        th := math.ceil(r.h / ~~background_texture.height) * ~~background_texture.height;
+        immediate_subimage(^background_texture, r.x, r.y, tw, th, 0, 0, tw, th);
     }
-}
\ No newline at end of file
+}
index e6c26d2f63d507a4ad2d494b9f7fb802d3248ddb..e8e49b4da363b430c4d715228ebf0b1f4ff21431 100644 (file)
@@ -14,7 +14,7 @@ use package glfw3
 Editor_Range        :: struct {min, max: f32;}
 Editor_Disabled     :: struct {}
 Editor_Hidden       :: struct {}
-Editor_Custom_Field :: struct { func: () -> void; }
+Editor_Custom_Field :: struct { func: (v: any, x: f32, y: f32, w: f32, h: f32, field_name: str) -> void; }
 
 editor_init :: () {
     editor_font = font_lookup(.{"./assets/fonts/calibri.ttf", 18});
@@ -22,9 +22,9 @@ editor_init :: () {
     selected_entity_id = Entity_Nothing;
 
     array.ensure_capacity(^save_path, 512);
-    save_path.count = 512;
-    s := conv.format(save_path, "./scenes/level1.scene");
-    save_path.count = s.count;
+    conv.format(^save_path, "./scenes/level1.scene");
+
+    custom_editors[Color] = render_color_picker;
 }
 
 editor_shown :: () => editor_openness != 0.0f || editor_target_openness != 0.0f;
@@ -231,6 +231,22 @@ editor_draw :: () {
                 if draw_button(.{ x + w / 2, y + 40, w / 2, 40 }, "Load") {
                     scene->load_from_file(save_path);
                 }
+
+                @Cleanup // This is rather expensive to do every frame, I think?
+                dir, success := os.dir_open("./scenes");
+                entry: os.DirectoryEntry;
+                y += 80;
+                while os.dir_read(dir, ^entry) {
+                    if string.ends_with(entry->name(), ".scene") {
+                        if draw_button(.{ x, y, w, 40 }, entry->name(), increment=entry.identifier) {
+                            buf: [1024] u8;
+                            path := conv.format(buf, "./scenes/{}", entry->name());
+                            scene->load_from_file(path);
+                        }
+                        y += 40;
+                    }
+                }
+                os.dir_close(dir);
             }
             
             case .Create {
@@ -250,11 +266,19 @@ editor_draw :: () {
 }
 
 #local render_create_sidebar :: (x, y, w, h: f32) {
+    #persist search_value: [..] u8;
+    draw_textbox(.{x, y, w, 40}, ^search_value, placeholder="Search...");
+    h -= 40;
+    y += 40;
+
     i := 0;
     for^ scene.schematics.entries {
         defer i += 1;
 
         name := it.key;
+        if search_value.count != 0 {
+            if !string.contains(name, search_value) do continue;
+        }
 
         y += 40.0f;
         theme := Button_Theme.{active = active_index == i};
@@ -266,7 +290,7 @@ editor_draw :: () {
 
 #local render_entity_list :: (x, y, w, h: f32) {
     #persist search_value: [..] u8;
-    draw_textbox(.{x, y, w, 40}, ^search_value);
+    draw_textbox(.{x, y, w, 40}, ^search_value, placeholder="Search...");
     h -= 40;
     y += 40;
 
@@ -360,11 +384,28 @@ editor_draw :: () {
             }
         }
 
+        member_info := type_info.get_type_info(it.type);
+
         if active_index + 1 == i {
             immediate_set_color(.{.4,.4,.2});
             immediate_rectangle(x, y + 2, w, Field_Height + 2);
 
-            render_field_editor(member_any, y, it.name);
+            w := sidebar_width / 2;
+            h := 200.0f;
+            x := ~~ window_width - sidebar_width;
+            render := render_field_editor;
+
+            if custom_editors[it.type] != null_proc {
+                render = custom_editors[it.type];
+            }
+
+            for it.tags {
+                if it.type == Editor_Custom_Field {
+                    render = (cast(^Editor_Custom_Field) it.data).func;
+                }
+            }
+
+            render(member_any, x, y, w, h, it.name);
 
         } elseif i % 2 == 0 {
             immediate_set_color(.{.3,.3,.3});
@@ -380,7 +421,7 @@ editor_draw :: () {
             Color.{0.4, 0.4, 0.2}
         ];
 
-        if type_info.get_type_info(it.type).kind == .Struct {
+        if member_info.kind == .Struct {
             new_y, new_i := render_struct_fields(member_any, i, x + Field_Height, y, w - Field_Height, h, depth + 1);
             immediate_set_color(depth_colors[depth % 4]);
             immediate_rectangle(x, y + Field_Height + 4, Field_Height / 2, new_y - y);
@@ -399,15 +440,10 @@ editor_draw :: () {
 
 #local prepare_field_editor :: (v: any) {
     array.ensure_capacity(^field_buffer, 256);
-    field_buffer.count = 256;
-    s := conv.format_va(field_buffer, "{\"d}", .[v]);
-    field_buffer.count = s.count;
+    conv.format_va(^field_buffer, "{\"d}", .[v]);
 }
 
-#local render_field_editor :: (v: any, y: f32, field_name: str) {
-    w := sidebar_width / 2;
-    h := 200.0f;
-    x := ~~ window_width - sidebar_width;
+#local render_field_editor :: (v: any, x, y, w, h: f32, field_name: str) {
     immediate_set_color(.{.3,.3,.3});
     immediate_rectangle(x, y, w, h);
 
@@ -459,6 +495,28 @@ editor_draw :: () {
     }
 }
 
+#local render_color_picker :: (v: any, x, y, w, h: f32, field_name: str) {
+    color := cast(^Color) v.data;
+    
+    immediate_set_color(.{.3, .3, .3});
+    immediate_rectangle(x, y, w, h);
+
+    draw_slider(.{x, y+0,  w, 40}, ^color.r, 0, 1);
+    draw_slider(.{x, y+40, w, 40}, ^color.g, 0, 1);
+    draw_slider(.{x, y+80, w, 40}, ^color.b, 0, 1);
+
+    buf: [64] u8;
+    msg := conv.format(buf, "Red: {}", color.r);
+    font_draw_centered(editor_font, x, y+24, w, msg);
+    msg = conv.format(buf, "Green: {}", color.g);
+    font_draw_centered(editor_font, x, y+64, w, msg);
+    msg = conv.format(buf, "Blue: {}", color.b);
+    font_draw_centered(editor_font, x, y+104, w, msg);
+
+    immediate_set_color(*color);
+    immediate_rectangle(x, y+120, w, h - 120);
+}
+
 #local {
     background_color :: Color.{0.15, 0.15, 0.15, 1};
 
@@ -493,4 +551,6 @@ editor_draw :: () {
     editor_grid_size := 16.0f; @TODO // This should be configurable
 
     save_path: [..] u8;
+
+    custom_editors : Map(type_expr, (any, f32, f32, f32, f32, str) -> void);
 }
index dfa5d99f93f9442dc4bd15c7b739240196db5c96..d75fa4692ae6c0b851bf785a147c88ddc0471c70 100644 (file)
@@ -122,7 +122,7 @@ ItemComponent :: struct {
 
 
 #local {
-    render_item_picker :: () {
+    render_item_picker :: (v: any, x, y, w, h: f32, field_name: str) {
         
     }
 }
\ No newline at end of file
index 7ee3beb87ef6a91ac9cbdc020cd9e70fe77c0b17..a0a8414c1baa35b784603b6a0f2a609f03ede7b5 100644 (file)
@@ -283,6 +283,8 @@ scene_modify_component :: macro (this: ^Scene, entity: ^Entity, $component_type:
         // This 'modify_component' function is more often used by the programmer, so initializing
         // the component here before returning is a good idea.
         __initialize(comp);
+    } else {
+        comp = ~~ entity->get(component_type);
     }
 
     #insert init_block;
index a58f30b7649a60b8a5ffe71a95d613e8ab2b2cd5..8765ac4bd22117d9bf5a35c34237b3f2c6853c76 100644 (file)
@@ -26,7 +26,10 @@ ui_end_frame :: () {
 
     if active_countdown > 0 {
         active_countdown -= 1;
-        if active_countdown == 0 do set_active_item(0);
+        if active_countdown == 0 {
+            set_active_item(0);
+            input_release_keys();
+        }
     }
 }
 
@@ -105,6 +108,86 @@ draw_button  :: (use r: Rect, text: str, theme := ^default_button_theme, site :=
 }
 
 
+//
+// Sliders
+//
+Slider_Theme :: struct {
+    use text_theme := Text_Theme.{};
+    use animation_theme := Animation_Theme.{};
+
+    box_color        := Color.{ 0.1, 0.1, 0.1 };
+    box_border_color := Color.{ 0.2, 0.2, 0.2 };
+    box_border_width := 4.0f;   @InPixels
+
+    bar_color                := Color.{ 0.4, 0.4, 0.4 };
+    bar_hover_color          := Color.{ 0, 0, 1 };
+    bar_hover_negative_color := Color.{ 1, 0, 0 }; // The color when value is less than 0
+}
+
+#local default_slider_theme := Slider_Theme.{};
+
+draw_slider :: (use r: Rect, value: ^$T, min_value: T, max_value: T, theme := ^default_slider_theme, site := #callsite, increment := 0) -> bool {
+    result := false;
+
+    hash := get_site_hash(site, increment);
+    state := get_animation(hash);
+    mx, my := mouse_get_position();
+
+    if is_hot_item(hash) {
+        if is_button_down(GLFW_MOUSE_BUTTON_LEFT) {
+            set_active_item(hash);
+            result = true;
+
+            // Animate this?
+            sx := ~~mx - x;
+
+            if T == i32 || T == i64 || T == u32 || T == u64 {
+                step_width := w / ~~math.abs(max_value - min_value);
+                percent := (sx + step_width / 2) / w;
+                *value = math.lerp(percent, min_value, max_value);
+                *value = math.clamp(*value, min_value, max_value);
+
+            } else {
+                percent := sx / w;
+                *value = math.lerp(percent, min_value, max_value);
+                *value = math.clamp(*value, min_value, max_value);
+            }
+        } else {
+            set_active_item(0);
+        }
+    }
+
+    if Rect.contains(r, .{ ~~mx, ~~my }) {
+        set_hot_item(hash);
+    }
+
+    if is_hot_item(hash) {
+        move_towards(^state.hover_time, 1.0f, theme.hover_speed);
+    } else {
+        move_towards(^state.hover_time, 0.0f, theme.hover_speed);
+    }
+
+    box_border_width := theme.box_border_width;
+
+    bar_color := theme.bar_color;
+    if *value < 0 do bar_color = color_lerp(state.hover_time, bar_color, theme.bar_hover_negative_color);
+    else          do bar_color = color_lerp(state.hover_time, bar_color, theme.bar_hover_color);
+
+    immediate_set_color(theme.box_border_color);
+    immediate_rectangle(x, y, w, h);
+
+    immediate_set_color(theme.box_border_color);
+    immediate_rectangle(x + box_border_width, y + box_border_width, w - box_border_width * 2, h - box_border_width * 2);
+
+    box_width := cast(f32) (*value - min_value) / ~~(max_value - min_value);
+    box_width *= w - box_border_width * 2;
+    box_width = math.clamp(box_width, 0, w - box_border_width * 2);
+    immediate_set_color(bar_color);
+    immediate_rectangle(x + box_border_width, y + box_border_width, box_width, h - box_border_width * 2);
+
+    return result;
+}
+
 //
 // Textbox
 //
index 882d3a23c2a0348171248014c04b2383511aac62..6dcc89fdc4421735a6bec15c3adc2d1f6f1e585e 100644 (file)
@@ -155,4 +155,5 @@ main :: (args) => {
 
     init();
     run();
+    deinit();
 }
index 7a57bfbe20e931fc09b2b91746ef7eb29b5482fc..7e7bdd0ad8fc34d33dccf70db88ef68ae33e99b3 100644 (file)
@@ -96,4 +96,4 @@ Audio_Manager :: struct {
 
     loaded_sounds: Map(str, Sound);
     playing_sounds: [..] u32; // List of sources
-}
\ No newline at end of file
+}
diff --git a/src/utils/colors.onyx b/src/utils/colors.onyx
new file mode 100644 (file)
index 0000000..886387d
--- /dev/null
@@ -0,0 +1,50 @@
+
+use package core
+
+color_to_hsl :: (c: Color) -> (h: f32, s: f32, l: f32) {
+    r := c.r / 255;
+    g := c.g / 255;
+    b := c.b / 255;
+    cmax := math.max(math.max(r, g), b);
+    cmin := math.min(math.min(r, g), b);
+    delta := cmax - cmin;
+
+    h := 0.0f;
+    if cmax == r {
+        m := (g - b) / delta;
+        while m >= 6 do m -= 6;
+        while m < 0  do m += 6;
+        h = 60 * m;
+    }
+    if cmax == g do h = 60 * (b - r) / delta + 2;
+    if cmax == b do h = 60 * (r - g) / delta + 4;
+
+    l := (cmax + cmin) / 2;
+    s := delta / (1 - math.abs(2 * l - 1));
+
+    return h, s, l;
+}
+
+hsl_to_color :: (h, s, l: f32) -> Color {
+    c := (1 - math.abs(2 * l - 1)) * s;
+
+    h_term := h / 60;
+    while h_term < 0  do h_term += 2;
+    while h_term >= 0 do h_term -= 2;
+
+    x := c * (1 - math.abs(h_term - 1));
+    m := l - c / 2;
+
+    r, g, b: f32;
+    if 0   <= h && h < 60  { r = c; g = x; b = 0; }
+    if 60  <= h && h < 120 { r = x; g = c; b = 0; }
+    if 120 <= h && h < 180 { r = 0; g = c; b = x; }
+    if 180 <= h && h < 240 { r = 0; g = x; b = c; }
+    if 240 <= h && h < 300 { r = x; g = 0; b = c; }
+    if 300 <= h && h < 360 { r = c; g = 0; b = x; }
+
+    r = (r + m) * 255;
+    g = (g + m) * 255;
+    b = (b + m) * 255;
+    return .{ r, g, b, 1 };
+}
\ No newline at end of file