summaryrefslogtreecommitdiff
path: root/Fuse/demos
diff options
context:
space:
mode:
authorVasco <[email protected]>2026-08-23 02:25:33 +0100
committerVasco <[email protected]>2026-08-23 02:25:33 +0100
commitdc96fcd771bea09d726a760266739906e95c09a6 (patch)
tree5af4d4d7660a90a04016fae4defec3c2a422477d /Fuse/demos
Peak 0.1.1
Diffstat (limited to 'Fuse/demos')
-rw-r--r--Fuse/demos/fuse.slang31
-rw-r--r--Fuse/demos/fuse_demo.c280
2 files changed, 311 insertions, 0 deletions
diff --git a/Fuse/demos/fuse.slang b/Fuse/demos/fuse.slang
new file mode 100644
index 0000000..837a3dc
--- /dev/null
+++ b/Fuse/demos/fuse.slang
@@ -0,0 +1,31 @@
+struct VertexInput {
+ [[vk::location(0)]] float2 pos : POSITION;
+ [[vk::location(1)]] float4 col : COLOR;
+};
+
+struct VertexOutput {
+ float4 col : COLOR;
+ float4 pos : SV_Position;
+};
+
+struct UBO {
+ float2 viewport;
+};
+
+[[vk::binding(0, 0)]]
+ConstantBuffer<UBO> ubo;
+
+[shader("vertex")]
+VertexOutput vertMain(VertexInput input)
+{
+ VertexOutput output;
+ output.col = input.col;
+ output.pos = float4(input.pos*2.0/ubo.viewport - 1.0, 0.0, 1.0);
+ return output;
+}
+
+[shader("fragment")]
+float4 fragMain(VertexOutput input) : SV_Target
+{
+ return input.col;
+}
diff --git a/Fuse/demos/fuse_demo.c b/Fuse/demos/fuse_demo.c
new file mode 100644
index 0000000..60128bd
--- /dev/null
+++ b/Fuse/demos/fuse_demo.c
@@ -0,0 +1,280 @@
+#include "rend.h"
+#include "rend.c"
+#include "fuse.h"
+#include "fuse.c"
+#include <stddef.h>
+#include <stdio.h>
+
+/* has to be included last because rend will ask for certain modules */
+#define PODIUM_IMPLEMENTATION
+#include "podium.h"
+
+typedef struct { float x, y; } FuseVec2;
+typedef struct { float x, y, z, w; } FuseVec4;
+
+#pragma pack(push, 1)
+typedef struct {
+ FuseVec2 pos;
+ FuseVec4 color;
+} FuseVertex;
+#pragma pack(pop)
+
+static FuseVertex fuse_renderer_data[1024];
+static size_t fuse_renderer_idx = 0;
+
+struct data_t { float offset_x; float padding = 0 };
+struct data_t button_data = { .offset_x = 0, .padding = 0};
+
+void stretch_button_op(void *data) {
+ struct data_t* ptr = (struct data_t*) data;
+ data->offset_x += 10;
+ data->padding += 10;
+}
+
+
+static inline FuseVec4
+fuse__unpack_color(uint32_t c)
+{
+ FuseVec4 v;
+ v.x = (float) ((c >> 24) & 0xFF) / 255.0f; // Red
+ v.y = (float) ((c >> 16) & 0xFF) / 255.0f; // Green
+ v.z = (float) ((c >> 8) & 0xFF) / 255.0f; // Blue
+ v.w = (float) ( c & 0xFF) / 255.0f; // Alpha
+ return v;
+}
+
+void fuse_handler(FuseCanvas canvas, FuseCmd *cmd, void *data) {
+ switch (cmd->type) {
+ case FUSE_CMD_RECT:
+ FuseRect rect = cmd->rect;
+ FuseVec4 color = fuse__unpack_color(rect.color);
+ FuseVertex c0 = {
+ .pos = { rect.x, rect.y },
+ .color = color
+ };
+
+ FuseVertex c1 = {
+ .pos = { rect.x + rect.w, rect.y },
+ .color = color
+ };
+
+ FuseVertex c2 = {
+ .pos = { rect.x + rect.w, rect.y + rect.h },
+ .color = color
+ };
+
+ FuseVertex c3 = {
+ .pos = { rect.x, rect.y + rect.h },
+ .color = color
+ };
+
+ fuse_renderer_data[fuse_renderer_idx++] = c0;
+ fuse_renderer_data[fuse_renderer_idx++] = c1;
+ fuse_renderer_data[fuse_renderer_idx++] = c2;
+
+ fuse_renderer_data[fuse_renderer_idx++] = c2;
+ fuse_renderer_data[fuse_renderer_idx++] = c3;
+ fuse_renderer_data[fuse_renderer_idx++] = c0;
+ break;
+ }
+}
+
+#define SCREEN_WIDTH 800
+#define SCREEN_HEIGHT 600
+
+int main() {
+
+ P_Window window;
+ if (!p_window_open(&window, 800, 600, "fuse_demo")) {
+ PFATAL("Failed to open window!");
+ return 1;
+ }
+
+ if (!rend_init(REND_BACKEND_AUTO)) {
+ PFATAL("Failed to initialize backend!");
+ return 1;
+ }
+
+ RendRenderer renderer = rend_renderer_create(&window, true);
+ RendShader vert = rend_shader_from_spv_path(REND_SHADER_VERTEX, "fuse.vert.spv");
+ RendShader frag = rend_shader_from_spv_path(REND_SHADER_FRAG, "fuse.frag.spv");
+
+ RendPipelineConfig ui_cfg = rend_pipeline_config_create(
+ REND_TOPOLOGY_TRIANGLE_LIST,
+ REND_POLYGON_MODE_FILL,
+ REND_CULL_MODE_BACK);
+ rend_pipeline_config_depth_test(ui_cfg, false);
+
+ /* configure vertex input */
+ int b0 = rend_pipeline_config_vertex_binding_add(ui_cfg, sizeof (FuseVertex), REND_INPUT_RATE_VERTEX);
+ rend_pipeline_config_vertex_attribute_add(ui_cfg, b0, 0, REND_FORMAT_R32G32_SFLOAT, offsetof(FuseVertex, pos));
+ rend_pipeline_config_vertex_attribute_add(ui_cfg, b0, 1, REND_FORMAT_R32G32B32A32_SFLOAT, offsetof(FuseVertex, color));
+ rend_pipeline_config_resource_binding_add(ui_cfg, 0, 0, REND_BUFFER_UNIFORM, REND_SHADER_VERTEX, 1);
+
+ /* add the shaders */
+ rend_pipeline_config_shader_add(ui_cfg, vert);
+ rend_pipeline_config_shader_add(ui_cfg, frag);
+
+ RendPipeline ui_pipeline = rend_pipeline_create(renderer, ui_cfg);
+
+ RendResourceSet ui_resources = rend_resource_set_create(renderer, ui_pipeline, 0);
+
+ FuseCanvas canvas = fuse_canvas_create(1024, NULL);
+
+ u64 frame_t = NANOS_PER_SEC / 60;
+
+ bool running = true;
+ P_Event ev;
+ float pev = 0, px = 0, py = 0;
+
+ float nob = 0.3;
+
+ float target_screen[2] = { SCREEN_WIDTH, SCREEN_HEIGHT };
+ rend_resource_set_write_buffer(ui_resources, 0, REND_BUFFER_UNIFORM, target_screen, 2 * sizeof *target_screen, 0);
+
+ float real_screen_width = 0;
+ float real_screen_heigth = 0;
+
+ enum UI_State {
+ UI_START_MENU,
+ UI_OPTIONS,
+ };
+
+ int ui_state = UI_START_MENU;
+
+ while (running) {
+
+ u64 start = p_get_time();
+
+ pev = 0;
+ /* event loop */
+ while (p_window_poll_event(&window, &ev)) {
+ if (ev.type == P_EVENT_WINDOW_CLOSE) {
+ running = false;
+ break;
+ }
+
+ switch (ev.type) {
+ case P_EVENT_WINDOW_RESIZE:
+ real_screen_width = ev.resize.width;
+ real_screen_heigth = ev.resize.height;
+ break;
+ case P_EVENT_POINTER:
+ px = ev.pointer.x;
+ py = ev.pointer.y;
+ switch (ev.pointer.state) {
+ case P_POINTER_PRESSED:
+ if (ev.pointer.button == 1 || ev.pointer.button == 0)
+ pev = FUSE_POINTER_PRESSED;
+ break;
+ case P_POINTER_RELEASED:
+ if (ev.pointer.button == 1 || ev.pointer.button == 0)
+ pev = FUSE_POINTER_RELEASED;
+ break;
+ default:
+ case P_POINTER_MOVED:
+ break;
+ }
+
+ fuse_canvas_pointer(pev, px * SCREEN_WIDTH / real_screen_width, py * SCREEN_HEIGHT / real_screen_heigth);
+ break;
+ }
+ }
+
+ /* Clears the Screen */
+ fuse_canvas_clear(canvas, 0x000000);
+
+ /* Inside a div, children no longer have fixed positions,
+ * instead x, y represent an offset so that they can still be animated!
+ * Width and height represent min width and min height instead! */
+ if (ui_state == UI_START_MENU) {
+ // NOTE: brackets for legibility only
+ FUSE_LAYOUT_DIRECTION(FUSE_DIRECTION_COLUMN);
+ fuse_div_begin(0, 0, SCREEN_WIDTH / 4, SCREEN_HEIGHT, 0xFFFFFFaa);
+ {
+
+ fuse_hovered(stretch_button_op, data);
+
+ if (fuse_button_text("Start New Game", button_data.offset_x, 0, 100, 0, 0xFF0000FF, 0x000000FF)) {
+ printf("start new game");
+ }
+
+
+ if (fuse_button_text("Options", 0, 0, 100, 0, 0xFF0000FF, 0x000000FF)) {
+ ui_state = UI_OPTIONS;
+ }
+
+
+ if (fuse_button_text("Credits", 0, 0, 100, 0, 0xFF0000FF, 0x000000FF)) {
+ printf("awesome credits");
+ }
+
+ if (fuse_button_text("Quit Game")) {
+ running = false;
+ break;
+ }
+
+ fuse_hovered_clear();
+
+ }
+ fuse_div_end();
+
+ /* back to global layouting */
+ fuse_3d_object();
+ fuse_3d_point_at();
+
+
+ } else if (ui_state == UI_OPTIONS) {
+
+ /* Inside a flex the same x,y as offsets and width and height as minimums rules apply
+ * The difference is that a flex is flexible, by default it does not have a fixed size
+ * but you may specify minimums and maximus for it to work around.
+ */
+
+ FUSE(
+ .max_width = 300,
+ .max_height = 600,
+ .direction = FUSE_DIRECTION_COLUMN,
+ .gap = 32
+ );
+
+ fuse_flex_begin(0, 0, SCREEN_WIDTH / 4, SCREEN_HEIGHT, 0xFFFFFFaa);
+ {
+ if (fuse_checkbox_text("VSYNC", &vsync)) {
+ vsync = ~vsync;
+ }
+ }
+ }
+
+ fuse_slider(100, 200, 300, 600, 0xFFFF00FF, 0xFF0000FF, &nob);
+
+ fuse_canvas_draw(canvas, fuse_handler, NULL);
+
+ /* call renderer */
+ if (rend_renderer_frame_begin(renderer)) {
+ rend_pipeline_bind(ui_pipeline);
+ rend_resource_set_bind(ui_pipeline, 0, ui_resources);
+
+ rend_pipeline_push_data(ui_pipeline, fuse_renderer_data, fuse_renderer_idx, sizeof *fuse_renderer_data);
+ rend_pipeline_draw(ui_pipeline);
+
+ rend_renderer_frame_end(renderer, NULL);
+ }
+
+ fuse_renderer_idx = 0;
+
+ u64 elapsed = p_get_time() - start;
+
+ /* the renderer should already be forcing vsync */
+ if (elapsed < frame_t)
+ p_sleep_ns(frame_t - elapsed);
+ }
+
+
+ fuse_canvas_destroy(canvas);
+
+ rend_quit();
+
+ p_window_close(&window);
+ return 0;
+}