#include "aliases.h" #include "mem_arena.h" #include "nodes.h" #include "ops.h" #include "ui.h" #include "window.h" #include #include #include #include #include #include #define MAX_WINDOWS 2 #define WINDOW_WIDTH 1280 #define WINDOW_HEIGHT 720 #define ARENA_CAPACITY 1 * 1024 * 1024 typedef struct compositor compositor; struct compositor { Arena *arena; window windows[MAX_WINDOWS]; u32 active_window; SDL_Event event; bool running; u64 count; node *nodes; node *back_nodes; ui_ctx ctx; }; internal void add_node(compositor *comp, node_type type, node_data data, u64 inputs, i32 x, i32 y, ui_elem_colours colours); internal void update_node_graph(compositor *comp, const window *wnd); internal void draw_node_graph(compositor *comp, const window *wnd); i32 run_main_loop(void) { if (SDL_Init(SDL_INIT_EVERYTHING) != 0) { return EXIT_FAILURE; } compositor comp = {0}; init_ui_ctx(&(comp.ctx)); mem_arena_init(&comp.arena, ARENA_CAPACITY); comp.nodes = (node *)mem_arena_alloc(comp.arena, sizeof(node) * MAX_NODES); comp.back_nodes = (node *)mem_arena_alloc(comp.arena, sizeof(node) * MAX_NODES); window *main_window = &(comp.windows[0]); window *toolbox = &(comp.windows[1]); if (!init_window(main_window, "Compositor", WINDOW_WIDTH, WINDOW_HEIGHT, -1, -1)) { SDL_Quit(); return EXIT_FAILURE; } u32 toolbox_window_width = WINDOW_WIDTH / 7; init_window(toolbox, "Toolbox", toolbox_window_width, WINDOW_HEIGHT, main_window->x - toolbox_window_width, -1); comp.running = true; SDL_EventState(SDL_DROPFILE, SDL_ENABLE); colour bg_colour = {.abgr = 0xffffffff}; ui_elem_colours button_colours = (ui_elem_colours){ .fill = (colour){.abgr = 0xff89a83c}, .border = (colour){.abgr = 0xff768432}, }; ui_elem_colours io_node_colours = (ui_elem_colours){ .fill = (colour){.abgr = 0xff2c84b7}, .border = (colour){.abgr = 0xff315c89}, }; ui_elem_colours op_node_colours = (ui_elem_colours){ .fill = (colour){.abgr = 0xffad6c3a}, .border = (colour){.abgr = 0xff8e4a33}, }; i32 toolbox_button_x = (toolbox->width - BUTTON_WIDTH) / 2; while (comp.running) { while (SDL_PollEvent(&(comp.event))) { handle_ui_events(&(comp.windows[comp.active_window - 1]), &(comp.ctx), &(comp.event)); if (comp.event.type == SDL_QUIT) { comp.running = false; } if (comp.event.type == SDL_WINDOWEVENT) { if (comp.event.window.event == SDL_WINDOWEVENT_CLOSE) { comp.running = false; } if (comp.event.window.event == SDL_WINDOWEVENT_ENTER) { u32 id = comp.event.window.windowID; window *wnd = NULL; for (u64 i = 0; i < MAX_WINDOWS; ++i) { window *window = &(comp.windows[i]); if (id == window->id) { comp.active_window = id; wnd = window; break; } } if (wnd) { SDL_RaiseWindow(wnd->window); } } } if (comp.event.type == SDL_DROPFILE) { if (comp.event.drop.windowID == main_window->id) { node_data data = (node_data){.path = comp.event.drop.file}; add_node(&comp, NODE_TYPE_IO, data, IO_INPUT_COUNT, comp.ctx.mouse_x, comp.ctx.mouse_y, io_node_colours); } } } update_node_graph(&comp, main_window); for (u64 i = 0; i < MAX_WINDOWS; ++i) { clear_window(&(comp.windows[i]), bg_colour); } for (u64 i = 0; i < COUNT_COMP_OPS; ++i) { rect rect = { .topleft.x = toolbox_button_x, .topleft.y = i * (BUTTON_HEIGHT + 20) + 30, .w = BUTTON_WIDTH, .h = BUTTON_HEIGHT, }; if (ui_button(toolbox, &(comp.ctx), rect, button_colours)) { node_data data = (node_data){.func = ops[i]}; add_node(&comp, NODE_TYPE_OP, data, OP_INPUT_COUNT, comp.ctx.mouse_x, comp.ctx.mouse_y, op_node_colours); } } draw_node_graph(&comp, main_window); for (u64 i = 0; i < MAX_WINDOWS; ++i) { swap_buffers(&(comp.windows[i])); } reset_ui_ctx(&(comp.ctx)); } for (u64 i = 0; i < MAX_WINDOWS; ++i) { cleanup_window(&(comp.windows[i])); } SDL_Quit(); mem_arena_free(&comp.arena); return EXIT_SUCCESS; } internal void add_node(compositor *comp, node_type type, node_data data, u64 inputs, i32 x, i32 y, ui_elem_colours colours) { if (comp->count + 1 >= MAX_NODES) { return; } u64 alloc_size = inputs * sizeof(noodle); noodle *noodles = mem_arena_alloc(comp->arena, alloc_size); noodle *back_noodles = mem_arena_alloc(comp->arena, alloc_size); if (!noodles || !back_noodles) { return; } rect rec = (rect){ .topleft.x = x, .topleft.y = y, .w = NODE_WIDTH, .h = NODE_HEIGHT, }; u64 idx = ++(comp->count); comp->nodes[idx] = comp->back_nodes[idx] = (node){ .rec = rec, .colours = colours, .type = type, .data.path = data.path, .inputs = inputs, .noodles = noodles, }; comp->back_nodes[idx].noodles = back_noodles; } internal void update_node_graph(compositor *comp, const window *wnd) { for (u64 i = NODE_START; i <= comp->count; ++i) { node *node_elem = &(comp->nodes[i]); const node *back_node = &(comp->back_nodes[i]); node_elem->rec = back_node->rec; for (u64 j = 0; j < node_elem->inputs; ++j) { noodle *ndl = &(node_elem->noodles[j]); const noodle *back_ndl = &(back_node->noodles[j]); ndl->connected_node = back_ndl->connected_node; ndl->ln = back_ndl->ln; } } } internal void draw_node_graph(compositor *comp, const window *wnd) { for (u64 i = NODE_START; i <= comp->count; ++i) { node *node_elem = &(comp->nodes[i]); node *back_node = &(comp->back_nodes[i]); f64 angle = 90.0; f64 angle_delta = 25.0; i64 delta_multiplier = node_elem->inputs % 2 == 0 ? -1 : 0; for (u64 j = 0; j < node_elem->inputs; ++j) { f64 new_angle = angle + angle_delta * delta_multiplier; noodle *ndl = &(node_elem->noodles[j]); noodle *back_ndl = &(back_node->noodles[j]); if (ndl->ln.p0.x == ndl->ln.p1.x && ndl->ln.p0.y == ndl->ln.p1.y) { point origin = {node_elem->rec.topleft.x + node_elem->rec.w / 2, node_elem->rec.topleft.y + node_elem->rec.h / 2}; back_ndl->ln = line_from_origin(origin, new_angle, DEFAULT_NOODLE_LENGTH); } switch (ui_noodle(wnd, &(comp->ctx), ndl->ln, node_elem->colours, node_elem->rec)) { case NOODLE_ACTION_DRAGGING: back_ndl->ln.p0.x += comp->ctx.rel_x; back_ndl->ln.p0.y += comp->ctx.rel_y; break; case NOODLE_ACTION_RELEASED: { bool connected = false; for (u64 k = NODE_START; k <= comp->count; ++k) { if (k == i) { continue; } const node *nd = &(comp->nodes[k]); if (aabb(nd->rec, comp->ctx.mouse_x, comp->ctx.mouse_y)) { point p0 = {nd->rec.topleft.x + nd->rec.w / 2, nd->rec.topleft.y + nd->rec.h / 2}; back_ndl->ln.p0 = p0; back_ndl->connected_node = k; connected = true; break; } } if (!connected) { back_ndl->ln.p0 = ndl->ln.p1; back_ndl->connected_node = EMPTY_NODE; } break; } default: break; } if (delta_multiplier > 0) { angle = new_angle; } if (delta_multiplier == 0) { delta_multiplier = -1; } else { delta_multiplier *= -1; } } if (ui_node(wnd, &(comp->ctx), node_elem->rec, node_elem->colours)) { back_node->rec.topleft.x += comp->ctx.rel_x; back_node->rec.topleft.y += comp->ctx.rel_y; for (u64 j = 0; j < node_elem->inputs; ++j) { noodle *ndl = &(node_elem->noodles[j]); noodle *back_ndl = &(back_node->noodles[j]); if (ndl->connected_node == EMPTY_NODE) { back_ndl->ln.p0.x += comp->ctx.rel_x; back_ndl->ln.p0.y += comp->ctx.rel_y; } back_ndl->ln.p1.x += comp->ctx.rel_x; back_ndl->ln.p1.y += comp->ctx.rel_y; } for (u64 j = NODE_START; j <= comp->count; ++j) { if (j == i) { continue; } node *nd = &(comp->back_nodes[j]); if (nd->inputs == 0) { continue; } for (u64 k = 0; k < nd->inputs; ++k) { noodle *ndl = &(nd->noodles[k]); if (ndl->connected_node == i) { ndl->ln.p0.x += comp->ctx.rel_x; ndl->ln.p0.y += comp->ctx.rel_y; } } } } } }