diff --git a/clay2.cm b/clay2.cm index f0bea02e..0bdfc92b 100644 --- a/clay2.cm +++ b/clay2.cm @@ -7,7 +7,6 @@ var layout = use('layout') var graphics = use('graphics') -var prosperon = use('prosperon') var clay = {} diff --git a/compositor.cm b/compositor.cm index 6429dfd7..75b76d4b 100644 --- a/compositor.cm +++ b/compositor.cm @@ -61,7 +61,8 @@ compositor.compile = function(comp, renderers, backend) { backend: backend, passes: [], targets: {}, - target_counter: 0 + target_counter: 0, + imgui_node: null } // Determine window/screen size from backend @@ -132,6 +133,19 @@ function compile_node(node, ctx, parent_target, parent_size) { } else if (is_renderer_type(node_type)) { // Renderer layer (film2d, forward3d, imgui, etc.) return compile_renderer_layer(node, ctx, target, target_size, parent_target, parent_size) + } else if (node_type == 'imgui') { + if (ctx.imgui_node) { + throw new Error("Only one imgui node is allowed in a composition") + } + ctx.imgui_node = node + ctx.passes.push({ + type: 'imgui', + target: target, + target_size: target_size, + draw: node.draw, + rect: node.rect + }) + return {output: target} } return {output: target} @@ -723,6 +737,15 @@ function execute_pass(pass, renderers, backend, resolve_target) { }) break + case 'imgui': + commands.push({ + cmd: 'imgui', + draw: pass.draw, + rect: pass.rect, + target: resolve_target(pass.target) + }) + break + case 'render_mask_source': // This would render the mask source node // For now, delegate to film2d renderer diff --git a/core.cm b/core.cm index e2297b2b..482b4b44 100644 --- a/core.cm +++ b/core.cm @@ -11,7 +11,7 @@ // }) var video = use('sdl3/video') -var events = use('sdl3/events') +var events = use('sdl3/input') var time_mod = use('time') var core = {} @@ -24,6 +24,8 @@ var _window = null var _last_time = 0 var _framerate = 60 +var imgui = use('imgui') + // Start the application core.start = function(config) { _config = config @@ -45,6 +47,11 @@ core.start = function(config) { } _window = _backend.get_window() + + if (config.imgui && imgui.init) { + imgui.init(_window, _backend.get_device()) + } + _running = true _last_time = time_mod.number() @@ -78,29 +85,30 @@ function _main_loop() { _last_time = now // Process events - var evts = [] - var ev - while ((ev = events.poll()) != null) { - evts.push(ev) - } + var evts = events.get_events() var win_size = _backend.get_window_size() for (var ev of evts) { - if (ev.type == 'quit') { + if (_config.imgui) { + imgui.process_event(ev) + } + + var ev_obj = events.objectify(ev) + if (ev_obj.type == 'quit') { _running = false $stop() return } - if (ev.type == 'window_pixel_size_changed') { - win_size.width = ev.data1 - win_size.height = ev.data2 + if (ev_obj.type == 'window_pixel_size_changed') { + win_size.width = ev_obj.width + win_size.height = ev_obj.height if (_backend.set_window_size) { - _backend.set_window_size(ev.width, ev.height) + _backend.set_window_size(ev_obj.width, ev_obj.height) } } if (_config.input) { - _config.input(ev) + _config.input(ev_obj) } } @@ -109,6 +117,11 @@ function _main_loop() { _config.update(dt) } + // ImGui Frame + if (_config.imgui) { + imgui.newframe() + } + // Render if (_config.render) { var render_result = _config.render() @@ -122,6 +135,13 @@ function _main_loop() { // Handle both compositor result ({commands: [...]}) and fx_graph (graph object) if (render_result.commands) { + if (_config.imgui) { + render_result.commands.push({ + cmd: 'imgui', + draw: _config.imgui, + target: 'screen' + }) + } // Compositor result - execute commands directly _backend.execute_commands(render_result.commands, win_size, dbg) } else { diff --git a/imgui.cpp b/imgui.cpp index ef179a04..b4e81952 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -27,10 +27,9 @@ extern "C" { SDL_Window *js2SDL_Window(JSContext *js, JSValue v); SDL_GPUDevice *js2SDL_GPUDevice(JSContext *js, JSValue v); -SDL_Texture *js2SDL_Texture(JSContext *js, JSValue v); -//SDL_GPUCommandBuffer *js2SDL_GPUCommandBuffer(JSContext *js, JSValue v); +SDL_GPUCommandBuffer *js2SDL_GPUCommandBuffer(JSContext *js, JSValue v); SDL_GPURenderPass *js2SDL_GPURenderPass(JSContext *js, JSValue v); -double js2number(JSContext *js, JSValue v); +SDL_Event *js2SDL_Event(JSContext *js, JSValue v); } // ==================== UTILITY FUNCTIONS ==================== @@ -239,8 +238,10 @@ JSC_SCALL(imgui_combo, JSC_SCALL(imgui_text, ImGui::Text("%s", str) ) JSC_SCALL(imgui_button, - if (ImGui::Button(str)) + bool gogo = ImGui::Button(str); + if (gogo && JS_IsFunction(js, argv[1])) JS_Call(js, argv[1], JS_NULL, 0, NULL); + ret = JS_NewBool(js, gogo); ) JSC_SCALL(imgui_slider, @@ -378,6 +379,12 @@ JSC_SCALL(imgui_tab, } ) +JSC_CCALL(imgui_process_event, + SDL_Event *e = js2SDL_Event(js, argv[0]); + if (!e) return JS_ThrowTypeError(js, "Expected SDL_Event object"); + return JS_NewBool(js, ImGui_ImplSDL3_ProcessEvent(e)); +) + JSC_SCALL(imgui_listbox, /* char **arr = js2strarr(argv[1]); int n = JS_ArrayLength(js, argv[1]); @@ -580,12 +587,6 @@ JSC_SCALL(imgui_setclipboard, ImGui::SetClipboardText(str); ) -// Global variable to store logical presentation state -static int stored_logical_w = 0; -static int stored_logical_h = 0; -static SDL_RendererLogicalPresentation stored_logical_mode = SDL_LOGICAL_PRESENTATION_DISABLED; -static SDL_ScaleMode stored_scale_mode = SDL_SCALEMODE_NEAREST; - JSC_CCALL(imgui_newframe, ImGui_ImplSDL3_NewFrame(); ImGui_ImplSDLGPU3_NewFrame(); @@ -594,11 +595,11 @@ JSC_CCALL(imgui_newframe, JSC_CCALL(imgui_prepare, ImGui::Render(); -// ImGui_ImplSDLGPU3_PrepareDrawData(ImGui::GetDrawData(), js2SDL_GPUCommandBuffer(js,argv[0])); + ImGui_ImplSDLGPU3_PrepareDrawData(ImGui::GetDrawData(), js2SDL_GPUCommandBuffer(js,argv[0])); ) JSC_CCALL(imgui_endframe, -// ImGui_ImplSDLGPU3_RenderDrawData(ImGui::GetDrawData(), js2SDL_GPUCommandBuffer(js,argv[0]), js2SDL_GPURenderPass(js,argv[1])); + ImGui_ImplSDLGPU3_RenderDrawData(ImGui::GetDrawData(), js2SDL_GPUCommandBuffer(js,argv[0]), js2SDL_GPURenderPass(js,argv[1])); ) JSC_CCALL(imgui_wantmouse, @@ -610,7 +611,7 @@ JSC_CCALL(imgui_wantkeys, ) JSC_CCALL(imgui_init, -/* ImGui::CreateContext(); + ImGui::CreateContext(); ImGuiIO& io = ImGui::GetIO(); io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard; io.ConfigFlags |= ImGuiConfigFlags_NavEnableGamepad; @@ -626,10 +627,6 @@ JSC_CCALL(imgui_init, info.Device = js2SDL_GPUDevice(js,argv[1]); info.ColorTargetFormat = SDL_GetGPUSwapchainTextureFormat(js2SDL_GPUDevice(js,argv[1]),window); bool gpu3_init = ImGui_ImplSDLGPU3_Init(&info); - - io.IniFilename = ".prosperon/imgui.ini"; - ImGui::LoadIniSettingsFromDisk(".prosperon/imgui.ini"); - */ ) // ==================== IMPLOT FUNCTIONS ==================== @@ -879,6 +876,8 @@ JSC_CCALL(imgui_nodelink, JSC_CCALL(imgui_nodemini, ImNodes::MiniMap(js2number(js, argv[0]))) + + #endif // ENABLE_IMNODES // ==================== FUNCTION LISTS ==================== @@ -986,6 +985,7 @@ const JSCFunctionListEntry js_imgui_funcs[] = { MIST_FUNC_DEF(imgui, newframe, 0), MIST_FUNC_DEF(imgui, endframe, 2), MIST_FUNC_DEF(imgui, prepare, 1), + MIST_FUNC_DEF(imgui, process_event, 1), }; #ifdef ENABLE_IMPLOT @@ -1026,11 +1026,6 @@ const JSCFunctionListEntry js_imnodes_funcs[] = { }; #endif -void gui_input(SDL_Event *e) -{ - ImGui_ImplSDL3_ProcessEvent(e); -} - CELL_USE_INIT( JSValue imgui = JS_NewObject(js); diff --git a/sdl_gpu.cm b/sdl_gpu.cm index 6ca91481..fcd99103 100644 --- a/sdl_gpu.cm +++ b/sdl_gpu.cm @@ -66,7 +66,8 @@ sdl_gpu.init = function(opts) { _window = new video.window({ title: opts.title || "Prosperon", width: _window_width, - height: _window_height + height: _window_height, + resizable: true }) _gpu = new gpu_mod.gpu({debug: true, shaders_msl: true, lowpower: true}) @@ -110,6 +111,10 @@ sdl_gpu.get_window = function() { return _window } +sdl_gpu.get_device = function() { + return _gpu +} + sdl_gpu.set_window_size = function(w, h) { _window_width = w _window_height = h @@ -1183,6 +1188,56 @@ function _execute_commands(commands, window_size) { current_pass = null } break + + case 'imgui': + // Flush pending draws first + if (current_pass && pending_draws.length > 0) { + _flush_draws(cmd_buffer, current_pass, pending_draws, current_camera, current_target) + pending_draws = [] + } + + // ImGui needs to be outside a render pass for prepare, but inside for endframe + if (current_pass) { + current_pass.end() + current_pass = null + } + + var imgui_mod = use('imgui') + if (cmd.draw) { + cmd.draw(imgui_mod) + } + imgui_mod.prepare(cmd_buffer) + + // Restart pass to the same target for rendering + var target = cmd.target + var swap_tex = null + if (target == 'screen') { + swap_tex = get_swapchain_tex() + if (swap_tex) { + current_pass = cmd_buffer.render_pass({ + color_targets: [{ + texture: swap_tex, + load: "load", + store: "store" + }] + }) + } + } else if (target && target.texture) { + current_pass = cmd_buffer.render_pass({ + color_targets: [{ + texture: target.texture, + load: "load", + store: "store" + }] + }) + } + + if (current_pass) { + imgui_mod.endframe(cmd_buffer, current_pass) + current_pass.end() + current_pass = null + } + break case 'present': // Submit command buffer