From f8d51d654316fd5a22002506e46b9532d8539cd6 Mon Sep 17 00:00:00 2001 From: luboslenco Date: Thu, 13 Nov 2025 14:04:57 +0100 Subject: [PATCH] Neural nodes progress --- paint/sources/box_preferences.ts | 27 +++--- paint/sources/neural_nodes/edit_image_node.ts | 62 ++------------ .../neural_nodes/image_to_depth_node.ts | 66 +++++++-------- .../neural_nodes/image_to_normal_map_node.ts | 66 +++++++-------- .../sources/neural_nodes/image_to_pbr_node.ts | 42 ++-------- .../neural_nodes/inpaint_image_node.ts | 59 +++---------- paint/sources/neural_nodes/neural_node.ts | 77 +++++++++++++++++ .../neural_nodes/text_to_image_node.ts | 53 +++--------- paint/sources/neural_nodes/tile_image_node.ts | 55 ++---------- .../neural_nodes/upscale_image_node.ts | 52 ++---------- paint/sources/neural_nodes/vary_image_node.ts | 84 +++++++++++++++++++ paint/sources/nodes_material.ts | 1 + paint/sources/ui_nodes.ts | 16 +--- 13 files changed, 283 insertions(+), 377 deletions(-) create mode 100644 paint/sources/neural_nodes/neural_node.ts create mode 100644 paint/sources/neural_nodes/vary_image_node.ts diff --git a/paint/sources/box_preferences.ts b/paint/sources/box_preferences.ts index b5bec055..ed675d94 100644 --- a/paint/sources/box_preferences.ts +++ b/paint/sources/box_preferences.ts @@ -707,16 +707,17 @@ function box_preferences_neural_tab() { ui_text(tr("Models")); if (ui_panel(ui_handle(__ID__), "Stable Diffusion")) { // (downloaded) - // https://huggingface.co/stable-diffusion-v1-5/stable-diffusion-v1-5/resolve/main/v1-5-pruned-emaonly.safetensors - ui_text("license: openrail"); - ui_text("nodes: Text to Image"); + ui_text("source: https://huggingface.co/stable-diffusion-v1-5/stable-diffusion-v1-5 (openrail)"); + ui_text("gpu memory: 4GB"); + ui_text("nodes: Inpaint Image, Text to Image, Tile Image, Vary Image"); if (ui_button(tr("Download (4.0GB)"))) { + // https://huggingface.co/stable-diffusion-v1-5/stable-diffusion-v1-5/resolve/main/v1-5-pruned-emaonly.safetensors } } if (ui_panel(ui_handle(__ID__), "Qwen Image")) { // (downloaded) ui_text("source: https://huggingface.co/QuantStack/Qwen-Image-GGUF (apache-2.0)"); - ui_text("video ram: GB"); + ui_text("gpu memory: 13GB"); ui_text("nodes: Text to Image"); if (ui_button(tr("Download") + " (15.7GB)")) { // https://huggingface.co/QuantStack/Qwen-Image-GGUF/resolve/main/Qwen_Image-Q4_K_S.gguf @@ -728,8 +729,9 @@ function box_preferences_neural_tab() { } if (ui_panel(ui_handle(__ID__), "Qwen Image Edit")) { // (downloaded) - ui_text("license: apache-2.0"); - ui_text("nodes: Edit Image"); + ui_text("source: https://huggingface.co/QuantStack/Qwen-Image-Edit-2509-GGUF (apache-2.0)"); + ui_text("gpu memory: 13GB"); + ui_text("nodes: Edit Image, Inpaint Image, Tile Image, Vary Image"); if (ui_button(tr("Download (17.0GB)"))) { // https://huggingface.co/QuantStack/Qwen-Image-Edit-2509-GGUF/resolve/main/Qwen-Image-Edit-2509-Q4_K_S.gguf // https://huggingface.co/QuantStack/Qwen-Image-GGUF/resolve/main/VAE/Qwen_Image-VAE.safetensors @@ -739,7 +741,8 @@ function box_preferences_neural_tab() { } if (ui_panel(ui_handle(__ID__), "Wan")) { // (downloaded) - ui_text("license: apache-2.0"); + ui_text("source: https://huggingface.co/QuantStack/Wan2.2-T2V-A14B-GGUF (apache-2.0)"); + ui_text("gpu memory: 10GB"); ui_text("nodes: Text to Image"); if (ui_button(tr("Download (19.8GB)"))) { // https://huggingface.co/QuantStack/Wan2.2-T2V-A14B-GGUF/resolve/main/LowNoise/Wan2.2-T2V-A14B-LowNoise-Q4_K_S.gguf @@ -750,8 +753,9 @@ function box_preferences_neural_tab() { } if (ui_panel(ui_handle(__ID__), "Marigold")) { // (downloaded) - ui_text("license: openrail"); - ui_text("nodes: Image to PBR"); + ui_text("source: https://huggingface.co/armory3d/marigold-v1-1-gguf (openrail)"); + ui_text("gpu memory: 6GB"); + ui_text("nodes: Image to Depth, Image to Normal Map, Image to PBR"); if (ui_button(tr("Download (9.6GB)"))) { // https://huggingface.co/armory3d/marigold-v1-1-gguf/resolve/main/marigold-depth-v1-1.q8_0.gguf // https://huggingface.co/armory3d/marigold-v1-1-gguf/resolve/main/marigold-normals-v1-1.q8_0.gguf @@ -760,10 +764,11 @@ function box_preferences_neural_tab() { } if (ui_panel(ui_handle(__ID__), "RealESRGAN")) { // (downloaded) - // https://huggingface.co/armory3d/Real-ESRGAN/resolve/main/RealESRGAN_x4plus.pth - ui_text("license: bsd-3-clause"); + ui_text("source: https://huggingface.co/armory3d/Real-ESRGAN (bsd-3-clause)"); + ui_text("gpu memory: 1GB"); ui_text("nodes: Upscale Image"); if (ui_button(tr("Download (0.06GB)"))) { + // https://huggingface.co/armory3d/Real-ESRGAN/resolve/main/RealESRGAN_x4plus.pth } } } diff --git a/paint/sources/neural_nodes/edit_image_node.ts b/paint/sources/neural_nodes/edit_image_node.ts index 5abe7acd..5ea4e069 100644 --- a/paint/sources/neural_nodes/edit_image_node.ts +++ b/paint/sources/neural_nodes/edit_image_node.ts @@ -1,23 +1,10 @@ -let edit_image_node_result: gpu_texture_t = null; - function edit_image_node_init() { array_push(nodes_material_neural, edit_image_node_def); - map_set(parser_material_node_vectors, "NEURAL_EDIT_IMAGE", edit_image_node_vector); + map_set(parser_material_node_vectors, "NEURAL_EDIT_IMAGE", neural_node_vector); map_set(ui_nodes_custom_buttons, "edit_image_node_button", edit_image_node_button); } -function edit_image_node_vector(node: ui_node_t, socket: ui_node_socket_t): string { - if (edit_image_node_result == null) { - return "float3(0.0, 0.0, 0.0)"; - } - let tex_name: string = parser_material_node_name(node); - map_set(data_cached_images, tex_name, edit_image_node_result); - let tex: bind_tex_t = parser_material_make_bind_tex(tex_name, tex_name); - let texstore: string = parser_material_texture_store(node, tex, tex_name, color_space_t.AUTO); - return texstore + ".rgb"; -} - function edit_image_node_button(node_id: i32) { let canvas: ui_node_canvas_t = ui_nodes_get_canvas(true); let node: ui_node_t = ui_get_node(canvas.nodes, node_id); @@ -28,63 +15,28 @@ function edit_image_node_button(node_id: i32) { let prompt: string = ui_text_area(ui_handle(__ID__), ui_align_t.LEFT, true, tr("prompt"), true); node.buttons[0].height = string_split(prompt, "\n").length + 2; - // let found: bool = false; - let found: bool = true; - - if (iron_exec_async_done == 0) { - ui_button(tr("Processing...")); - } - else if (!found && ui_button(tr("Setup"))) { - box_preferences_htab.i = preference_tab_t.NEURAL; - box_preferences_show(); - } - else if (found && ui_button(tr("Run"))) { - let inp: ui_node_socket_t = node.inputs[0]; - let from_node: ui_node_t = null; - for (let i: i32 = 0; i < canvas.links.length; ++i) { - let l: ui_node_link_t = canvas.links[i]; - if (l.to_id == inp.node_id) { - from_node = ui_get_node(canvas.nodes, l.from_id); - break; - } - } - + if (neural_node_button()) { + let from_node: ui_node_t = neural_from_node(node.inputs[0]); let input: gpu_texture_t = ui_nodes_get_node_preview_image(from_node); if (input != null) { - let dir: string; - if (path_is_protected()) { - dir = iron_internal_save_path() + "models"; - } - else { - dir = iron_internal_files_location() + path_sep + "models"; - } + let dir: string = neural_node_dir(); iron_write_png(dir + path_sep + "input.png", gpu_get_texture_pixels(input), input.width, input.height, 0); let argv: string[] = [ dir + "/sd", "--diffusion-model", dir + "/Qwen-Image-Edit-2509-Q4_K_S.gguf", "--vae", dir + "/Qwen_Image-VAE.safetensors", "--qwen2vl", - dir + "/Qwen2.5-VL-7B-Instruct-Q4_K_S.gguf", "--qwen2vl_vision", dir + "/mmproj-F16.gguf", "--sampling-method", "euler", - "--offload-to-cpu", "--diffusion-fa", "--steps", "30", "-s", "-1", "-W", "512", "-H", "512", "-p", "'" + prompt + "'", + dir + "/Qwen2.5-VL-7B-Instruct-Q4_K_S.gguf", "--qwen2vl_vision", dir + "/mmproj-F16.gguf", "--sampling-method", "euler", "--offload-to-cpu", + "--diffusion-fa", "--steps", "30", "-s", "-1", "-W", "512", "-H", "512", "-p", "'" + prompt + "'", // "-n", // "'" + negative + "'", "-r", dir + "/input.png", "-o", dir + "/output.png", null ]; iron_exec_async(argv[0], argv.buffer); - sys_notify_on_update(edit_image_node_check_result, dir); + sys_notify_on_update(neural_node_check_result, node); } } } -function edit_image_node_check_result(dir: string) { - if (iron_exec_async_done == 1) { - let file: string = dir + path_sep + "output.png"; - if (iron_file_exists(file)) { - edit_image_node_result = iron_load_texture(file); - } - sys_remove_update(edit_image_node_check_result); - } -} - let edit_image_node_def: ui_node_t = { id : 0, name : _tr("Edit Image"), diff --git a/paint/sources/neural_nodes/image_to_depth_node.ts b/paint/sources/neural_nodes/image_to_depth_node.ts index 75d10503..82af75eb 100644 --- a/paint/sources/neural_nodes/image_to_depth_node.ts +++ b/paint/sources/neural_nodes/image_to_depth_node.ts @@ -1,23 +1,10 @@ -let image_to_depth_node_result: gpu_texture_t = null; - function image_to_depth_node_init() { array_push(nodes_material_neural, image_to_depth_node_def); - map_set(parser_material_node_values, "NEURAL_IMAGE_TO_DEPTH", image_to_depth_node_value); + map_set(parser_material_node_values, "NEURAL_IMAGE_TO_DEPTH", neural_node_value); map_set(ui_nodes_custom_buttons, "image_to_depth_node_button", image_to_depth_node_button); } -function image_to_depth_node_value(node: ui_node_t, socket: ui_node_socket_t): string { - if (image_to_depth_node_result == null) { - return "0.0"; - } - let tex_name: string = parser_material_node_name(node); - map_set(data_cached_images, tex_name, image_to_depth_node_result); - let tex: bind_tex_t = parser_material_make_bind_tex(tex_name, tex_name); - let texstore: string = parser_material_texture_store(node, tex, tex_name, color_space_t.AUTO); - return texstore + ".r"; -} - function image_to_depth_node_button(node_id: i32) { let canvas: ui_node_canvas_t = ui_nodes_get_canvas(true); let node: ui_node_t = ui_get_node(canvas.nodes, node_id); @@ -25,34 +12,37 @@ function image_to_depth_node_button(node_id: i32) { let models: string[] = [ "Marigold" ]; let model: i32 = ui_combo(ui_handle(__ID__), models, tr("Model")); - if (iron_exec_async_done == 0) { - ui_button("Processing..."); - } - else if (ui_button("Run")) { - let inp: ui_node_socket_t = node.inputs[0]; - let from_node: ui_node_t = null; - for (let i: i32 = 0; i < canvas.links.length; ++i) { - let l: ui_node_link_t = canvas.links[i]; - if (l.to_id == inp.node_id) { - from_node = ui_get_node(canvas.nodes, l.from_id); - break; - } - } - + if (neural_node_button()) { + let from_node: ui_node_t = neural_from_node(node.inputs[0]); let input: gpu_texture_t = ui_nodes_get_node_preview_image(from_node); if (input != null) { - let dir: string; - if (path_is_protected()) { - dir = iron_internal_save_path() + "models"; - } - else { - dir = iron_internal_files_location() + path_sep + "models"; - } + let dir: string = neural_node_dir(); iron_write_png(dir + path_sep + "input.png", gpu_get_texture_pixels(input), input.width, input.height, 0); - image_to_pbr_node_run_sd("marigold-depth-v1-1.q8_0.gguf", " ", function(tex: gpu_texture_t) { - image_to_depth_node_result = tex; - }); + let argv: string[] = [ + dir + "/sd", + "-m", + dir + "/marigold-depth-v1-1.q8_0.gguf", + "--sampling-method", + "ddim_trailing", + "--steps", + "10", + "-s", + "-1", + "-W", + "768", + "-H", + "768", + "-p", + "' '", + "-i", + dir + "/input.png", + "-o", + dir + "/output.png", + null + ]; + iron_exec_async(argv[0], argv.buffer); + sys_notify_on_update(neural_node_check_result, node); } } } diff --git a/paint/sources/neural_nodes/image_to_normal_map_node.ts b/paint/sources/neural_nodes/image_to_normal_map_node.ts index af9a4ad7..45c80e51 100644 --- a/paint/sources/neural_nodes/image_to_normal_map_node.ts +++ b/paint/sources/neural_nodes/image_to_normal_map_node.ts @@ -1,23 +1,10 @@ -let image_to_normal_map_node_result: gpu_texture_t = null; - function image_to_normal_map_node_init() { array_push(nodes_material_neural, image_to_normal_map_node_def); - map_set(parser_material_node_vectors, "NEURAL_IMAGE_TO_NORMAL_MAP", image_to_normal_map_node_vector); + map_set(parser_material_node_vectors, "NEURAL_IMAGE_TO_NORMAL_MAP", neural_node_vector); map_set(ui_nodes_custom_buttons, "image_to_normal_map_node_button", image_to_normal_map_node_button); } -function image_to_normal_map_node_vector(node: ui_node_t, socket: ui_node_socket_t): string { - if (image_to_normal_map_node_result == null) { - return "float3(0.0, 0.0, 0.0)"; - } - let tex_name: string = parser_material_node_name(node); - map_set(data_cached_images, tex_name, image_to_normal_map_node_result); - let tex: bind_tex_t = parser_material_make_bind_tex(tex_name, tex_name); - let texstore: string = parser_material_texture_store(node, tex, tex_name, color_space_t.AUTO); - return texstore + ".rgb"; -} - function image_to_normal_map_node_button(node_id: i32) { let canvas: ui_node_canvas_t = ui_nodes_get_canvas(true); let node: ui_node_t = ui_get_node(canvas.nodes, node_id); @@ -25,34 +12,37 @@ function image_to_normal_map_node_button(node_id: i32) { let models: string[] = [ "Marigold" ]; let model: i32 = ui_combo(ui_handle(__ID__), models, tr("Model")); - if (iron_exec_async_done == 0) { - ui_button("Processing..."); - } - else if (ui_button("Run")) { - let inp: ui_node_socket_t = node.inputs[0]; - let from_node: ui_node_t = null; - for (let i: i32 = 0; i < canvas.links.length; ++i) { - let l: ui_node_link_t = canvas.links[i]; - if (l.to_id == inp.node_id) { - from_node = ui_get_node(canvas.nodes, l.from_id); - break; - } - } - + if (neural_node_button()) { + let from_node: ui_node_t = neural_from_node(node.inputs[0]); let input: gpu_texture_t = ui_nodes_get_node_preview_image(from_node); if (input != null) { - let dir: string; - if (path_is_protected()) { - dir = iron_internal_save_path() + "models"; - } - else { - dir = iron_internal_files_location() + path_sep + "models"; - } + let dir: string = neural_node_dir(); iron_write_png(dir + path_sep + "input.png", gpu_get_texture_pixels(input), input.width, input.height, 0); - image_to_pbr_node_run_sd("marigold-normals-v1-1.q8_0.gguf", " ", function(tex: gpu_texture_t) { - image_to_normal_map_node_result = tex; - }); + let argv: string[] = [ + dir + "/sd", + "-m", + dir + "/marigold-normals-v1-1.q8_0.gguf", + "--sampling-method", + "ddim_trailing", + "--steps", + "10", + "-s", + "-1", + "-W", + "768", + "-H", + "768", + "-p", + "' '", + "-i", + dir + "/input.png", + "-o", + dir + "/output.png", + null + ]; + iron_exec_async(argv[0], argv.buffer); + sys_notify_on_update(neural_node_check_result, node); } } } diff --git a/paint/sources/neural_nodes/image_to_pbr_node.ts b/paint/sources/neural_nodes/image_to_pbr_node.ts index c424d273..3c55f628 100644 --- a/paint/sources/neural_nodes/image_to_pbr_node.ts +++ b/paint/sources/neural_nodes/image_to_pbr_node.ts @@ -59,13 +59,7 @@ function image_to_pbr_node_value(node: ui_node_t, socket: ui_node_socket_t): str } function image_to_pbr_node_run_sd(model: string, prompt: string, done: (tex: gpu_texture_t) => void) { - let dir: string; - if (path_is_protected()) { - dir = iron_internal_save_path() + "models"; - } - else { - dir = iron_internal_files_location() + path_sep + "models"; - } + let dir: string = neural_node_dir(); let argv: string[] = [ dir + "/sd", @@ -101,29 +95,11 @@ function image_to_pbr_node_button(node_id: i32) { let models: string[] = [ "Marigold" ]; let model: i32 = ui_combo(ui_handle(__ID__), models, tr("Model")); - if (iron_exec_async_done == 0) { - ui_button("Processing..."); - } - else if (ui_button("Run")) { - let inp: ui_node_socket_t = node.inputs[0]; - let from_node: ui_node_t = null; - for (let i: i32 = 0; i < canvas.links.length; ++i) { - let l: ui_node_link_t = canvas.links[i]; - if (l.to_id == inp.node_id) { - from_node = ui_get_node(canvas.nodes, l.from_id); - break; - } - } - + if (neural_node_button()) { + let from_node: ui_node_t = neural_from_node(node.inputs[0]); let input: gpu_texture_t = ui_nodes_get_node_preview_image(from_node); if (input != null) { - let dir: string; - if (path_is_protected()) { - dir = iron_internal_save_path() + "models"; - } - else { - dir = iron_internal_files_location() + path_sep + "models"; - } + let dir: string = neural_node_dir(); iron_write_png(dir + path_sep + "input.png", gpu_get_texture_pixels(input), input.width, input.height, 0); image_to_pbr_node_run_sd("marigold-normals-v1-1.q8_0.gguf", " ", function(tex: gpu_texture_t) { @@ -144,15 +120,7 @@ function image_to_pbr_node_button(node_id: i32) { function image_to_pbr_node_check_result(done: (tex: gpu_texture_t) => void) { if (iron_exec_async_done == 1) { - - let dir: string; - if (path_is_protected()) { - dir = iron_internal_save_path() + "models"; - } - else { - dir = iron_internal_files_location() + path_sep + "models"; - } - + let dir: string = neural_node_dir(); let file: string = dir + path_sep + "output.png"; if (iron_file_exists(file)) { let tex: gpu_texture_t = iron_load_texture(file); diff --git a/paint/sources/neural_nodes/inpaint_image_node.ts b/paint/sources/neural_nodes/inpaint_image_node.ts index 30678481..c39f9d2b 100644 --- a/paint/sources/neural_nodes/inpaint_image_node.ts +++ b/paint/sources/neural_nodes/inpaint_image_node.ts @@ -1,23 +1,10 @@ -let inpaint_image_node_result: gpu_texture_t = null; - function inpaint_image_node_init() { array_push(nodes_material_neural, inpaint_image_node_def); - map_set(parser_material_node_vectors, "NEURAL_INPAINT_IMAGE", inpaint_image_node_vector); + map_set(parser_material_node_vectors, "NEURAL_INPAINT_IMAGE", neural_node_vector); map_set(ui_nodes_custom_buttons, "inpaint_image_node_button", inpaint_image_node_button); } -function inpaint_image_node_vector(node: ui_node_t, socket: ui_node_socket_t): string { - if (inpaint_image_node_result == null) { - return "float3(0.0, 0.0, 0.0)"; - } - let tex_name: string = parser_material_node_name(node); - map_set(data_cached_images, tex_name, inpaint_image_node_result); - let tex: bind_tex_t = parser_material_make_bind_tex(tex_name, tex_name); - let texstore: string = parser_material_texture_store(node, tex, tex_name, color_space_t.AUTO); - return texstore + ".rgb"; -} - function inpaint_image_node_button(node_id: i32) { let canvas: ui_node_canvas_t = ui_nodes_get_canvas(true); let node: ui_node_t = ui_get_node(canvas.nodes, node_id); @@ -25,31 +12,14 @@ function inpaint_image_node_button(node_id: i32) { let models: string[] = [ "Qwen Image Edit" ]; let model: i32 = ui_combo(ui_handle(__ID__), models, tr("Model")); - if (iron_exec_async_done == 0) { - ui_button("Processing..."); - } - else if (ui_button("Run")) { - let inp: ui_node_socket_t = node.inputs[0]; - let from_node: ui_node_t = null; - for (let i: i32 = 0; i < canvas.links.length; ++i) { - let l: ui_node_link_t = canvas.links[i]; - if (l.to_id == inp.node_id) { - from_node = ui_get_node(canvas.nodes, l.from_id); - break; - } - } - + if (neural_node_button()) { + let from_node: ui_node_t = neural_from_node(node.inputs[0]); let input: gpu_texture_t = ui_nodes_get_node_preview_image(from_node); if (input != null) { - sys_notify_on_next_frame(function(input: gpu_texture_t) { - let dir: string; - if (path_is_protected()) { - dir = iron_internal_save_path() + "models"; - } - else { - dir = iron_internal_files_location() + path_sep + "models"; - } + // sys_notify_on_next_frame(function(input: gpu_texture_t) { + + let dir: string = neural_node_dir(); iron_write_png(dir + path_sep + "input.png", gpu_get_texture_pixels(input), input.width, input.height, 0); let prompt: string = "Remove red square"; @@ -69,20 +39,13 @@ function inpaint_image_node_button(node_id: i32) { dir + "/output.png", null ]; - iron_exec_async(argv[0], argv.buffer); - sys_notify_on_update(inpaint_image_node_check_result, dir); - }, input); - } - } -} + // ./sd -m ./v1-5-pruned-emaonly.safetensors -i ./inpaint.png -W 512 -H 512 -p " " --mask ./mask.png -s -1 + // white = inpaint -function inpaint_image_node_check_result(dir: string) { - if (iron_exec_async_done == 1) { - let file: string = dir + path_sep + "output.png"; - if (iron_file_exists(file)) { - inpaint_image_node_result = iron_load_texture(file); + iron_exec_async(argv[0], argv.buffer); + sys_notify_on_update(neural_node_check_result, node); + // }, input); } - sys_remove_update(inpaint_image_node_check_result); } } diff --git a/paint/sources/neural_nodes/neural_node.ts b/paint/sources/neural_nodes/neural_node.ts new file mode 100644 index 00000000..5b72b480 --- /dev/null +++ b/paint/sources/neural_nodes/neural_node.ts @@ -0,0 +1,77 @@ + +let neural_node_results: map_t = map_create(); + +function neural_node_vector(node: ui_node_t, socket: ui_node_socket_t): string { + let result: gpu_texture_t = map_get(neural_node_results, node.id); + if (result == null) { + return "float3(0.0, 0.0, 0.0)"; + } + let tex_name: string = parser_material_node_name(node); + map_set(data_cached_images, tex_name, result); + let tex: bind_tex_t = parser_material_make_bind_tex(tex_name, tex_name); + let texstore: string = parser_material_texture_store(node, tex, tex_name, color_space_t.AUTO); + return texstore + ".rgb"; +} + +function neural_node_value(node: ui_node_t, socket: ui_node_socket_t): string { + let result: gpu_texture_t = map_get(neural_node_results, node.id); + if (result == null) { + return "0.0"; + } + let tex_name: string = parser_material_node_name(node); + map_set(data_cached_images, tex_name, result); + let tex: bind_tex_t = parser_material_make_bind_tex(tex_name, tex_name); + let texstore: string = parser_material_texture_store(node, tex, tex_name, color_space_t.AUTO); + return texstore + ".r"; +} + +function neural_from_node(inp: ui_node_socket_t): ui_node_t { + let result: ui_node_t = null; + let canvas: ui_node_canvas_t = ui_nodes_get_canvas(true); + for (let i: i32 = 0; i < canvas.links.length; ++i) { + let l: ui_node_link_t = canvas.links[i]; + if (l.to_id == inp.node_id) { + result = ui_get_node(canvas.nodes, l.from_id); + break; + } + } + return result; +} + +function neural_node_button(): bool { + let found: bool = true; + + if (iron_exec_async_done == 0) { + ui_button(tr("Processing...")); + } + else if (!found && ui_button(tr("Setup"))) { + box_preferences_htab.i = preference_tab_t.NEURAL; + box_preferences_show(); + } + else if (found && ui_button(tr("Run"))) { + return true; + } + return false; +} + +function neural_node_check_result(node: ui_node_t) { + if (iron_exec_async_done == 1) { + let file: string = neural_node_dir() + path_sep + "output.png"; + if (iron_file_exists(file)) { + let result: gpu_texture_t = iron_load_texture(file); + map_set(neural_node_results, node.id, result); + } + sys_remove_update(neural_node_check_result); + } +} + +function neural_node_dir(): string { + let dir: string; + if (path_is_protected()) { + dir = iron_internal_save_path() + "models"; + } + else { + dir = iron_internal_files_location() + path_sep + "models"; + } + return dir; +} diff --git a/paint/sources/neural_nodes/text_to_image_node.ts b/paint/sources/neural_nodes/text_to_image_node.ts index 7d7bc621..1f51992a 100644 --- a/paint/sources/neural_nodes/text_to_image_node.ts +++ b/paint/sources/neural_nodes/text_to_image_node.ts @@ -1,25 +1,11 @@ - -let text_to_image_node_result: gpu_texture_t = null; - function text_to_image_node_init() { array_push(nodes_material_neural, text_to_image_node_def); - map_set(parser_material_node_vectors, "NEURAL_TEXT_TO_IMAGE", text_to_image_node_vector); + map_set(parser_material_node_vectors, "NEURAL_TEXT_TO_IMAGE", neural_node_vector); map_set(ui_nodes_custom_buttons, "text_to_image_node_button", text_to_image_node_button); } -function text_to_image_node_vector(node: ui_node_t, socket: ui_node_socket_t): string { - if (text_to_image_node_result == null) { - return "float3(0.0, 0.0, 0.0)"; - } - let tex_name: string = parser_material_node_name(node); - map_set(data_cached_images, tex_name, text_to_image_node_result); - let tex: bind_tex_t = parser_material_make_bind_tex(tex_name, tex_name); - let texstore: string = parser_material_texture_store(node, tex, tex_name, color_space_t.AUTO); - return texstore + ".rgb"; -} - -function text_to_image_node_run_sd(dir: string, prompt: string): string[] { +function text_to_image_node_sd_args(dir: string, prompt: string): string[] { let argv: string[] = [ dir + "/sd", "-m", @@ -42,7 +28,7 @@ function text_to_image_node_run_sd(dir: string, prompt: string): string[] { return argv; } -function text_to_image_node_run_qwen(dir: string, prompt: string): string[] { +function text_to_image_node_qwen_args(dir: string, prompt: string): string[] { let argv: string[] = [ dir + "/sd", "--diffusion-model", @@ -71,7 +57,7 @@ function text_to_image_node_run_qwen(dir: string, prompt: string): string[] { return argv; } -function text_to_image_node_run_wan(dir: string, prompt: string): string[] { +function text_to_image_node_wan_args(dir: string, prompt: string): string[] { let argv: string[] = [ dir + "/sd", "-M", @@ -117,40 +103,21 @@ function text_to_image_node_button(node_id: i32) { let prompt: string = ui_text_area(ui_handle(__ID__), ui_align_t.LEFT, true, tr("prompt"), true); node.buttons[0].height = string_split(prompt, "\n").length + 2; - if (iron_exec_async_done == 0) { - ui_button("Processing..."); - } - else if (ui_button("Run")) { - let dir: string; - if (path_is_protected()) { - dir = iron_internal_save_path() + "models"; - } - else { - dir = iron_internal_files_location() + path_sep + "models"; - } + if (neural_node_button()) { + let dir: string = neural_node_dir(); let argv: string[]; if (model == 0) { - argv = text_to_image_node_run_sd(dir, prompt); + argv = text_to_image_node_sd_args(dir, prompt); } else if (model == 1) { - argv = text_to_image_node_run_qwen(dir, prompt); + argv = text_to_image_node_qwen_args(dir, prompt); } else { - argv = text_to_image_node_run_wan(dir, prompt); + argv = text_to_image_node_wan_args(dir, prompt); } iron_exec_async(argv[0], argv.buffer); - sys_notify_on_update(text_to_image_node_check_result, dir); - } -} - -function text_to_image_node_check_result(dir: string) { - if (iron_exec_async_done == 1) { - let file: string = dir + path_sep + "output.png"; - if (iron_file_exists(file)) { - text_to_image_node_result = iron_load_texture(file); - } - sys_remove_update(text_to_image_node_check_result); + sys_notify_on_update(neural_node_check_result, node); } } diff --git a/paint/sources/neural_nodes/tile_image_node.ts b/paint/sources/neural_nodes/tile_image_node.ts index b2738d99..3ad7de03 100644 --- a/paint/sources/neural_nodes/tile_image_node.ts +++ b/paint/sources/neural_nodes/tile_image_node.ts @@ -1,23 +1,10 @@ -let tile_image_node_result: gpu_texture_t = null; - function tile_image_node_init() { array_push(nodes_material_neural, tile_image_node_def); - map_set(parser_material_node_vectors, "NEURAL_TILE_IMAGE", tile_image_node_vector); + map_set(parser_material_node_vectors, "NEURAL_TILE_IMAGE", neural_node_vector); map_set(ui_nodes_custom_buttons, "tile_image_node_button", tile_image_node_button); } -function tile_image_node_vector(node: ui_node_t, socket: ui_node_socket_t): string { - if (tile_image_node_result == null) { - return "float3(0.0, 0.0, 0.0)"; - } - let tex_name: string = parser_material_node_name(node); - map_set(data_cached_images, tex_name, tile_image_node_result); - let tex: bind_tex_t = parser_material_make_bind_tex(tex_name, tex_name); - let texstore: string = parser_material_texture_store(node, tex, tex_name, color_space_t.AUTO); - return texstore + ".rgb"; -} - function tile_image_node_button(node_id: i32) { let canvas: ui_node_canvas_t = ui_nodes_get_canvas(true); let node: ui_node_t = ui_get_node(canvas.nodes, node_id); @@ -25,24 +12,13 @@ function tile_image_node_button(node_id: i32) { let models: string[] = [ "Qwen Image Edit" ]; let model: i32 = ui_combo(ui_handle(__ID__), models, tr("Model")); - if (iron_exec_async_done == 0) { - ui_button("Processing..."); - } - else if (ui_button("Run")) { - let inp: ui_node_socket_t = node.inputs[0]; - let from_node: ui_node_t = null; - for (let i: i32 = 0; i < canvas.links.length; ++i) { - let l: ui_node_link_t = canvas.links[i]; - if (l.to_id == inp.node_id) { - from_node = ui_get_node(canvas.nodes, l.from_id); - break; - } - } - + if (neural_node_button()) { + let from_node: ui_node_t = neural_from_node(node.inputs[0]); let input: gpu_texture_t = ui_nodes_get_node_preview_image(from_node); if (input != null) { - sys_notify_on_next_frame(function(input: gpu_texture_t) { + // sys_notify_on_next_frame(function(input: gpu_texture_t) { + let tile: gpu_texture_t = gpu_create_render_target(512, 512); draw_begin(tile); draw_scaled_image(input, -256, -256, 512, 512); @@ -51,13 +27,7 @@ function tile_image_node_button(node_id: i32) { draw_scaled_image(input, 256, 256, 512, 512); draw_end(); - let dir: string; - if (path_is_protected()) { - dir = iron_internal_save_path() + "models"; - } - else { - dir = iron_internal_files_location() + path_sep + "models"; - } + let dir: string = neural_node_dir(); iron_write_png(dir + path_sep + "input.png", gpu_get_texture_pixels(tile), tile.width, tile.height, 0); let prompt: string = "Blend seams"; @@ -100,19 +70,10 @@ function tile_image_node_button(node_id: i32) { ]; iron_exec_async(argv[0], argv.buffer); - sys_notify_on_update(tile_image_node_check_result, dir); - }, input); - } - } -} + sys_notify_on_update(neural_node_check_result, node); -function tile_image_node_check_result(dir: string) { - if (iron_exec_async_done == 1) { - let file: string = dir + path_sep + "output.png"; - if (iron_file_exists(file)) { - tile_image_node_result = iron_load_texture(file); + // }, input); } - sys_remove_update(tile_image_node_check_result); } } diff --git a/paint/sources/neural_nodes/upscale_image_node.ts b/paint/sources/neural_nodes/upscale_image_node.ts index 6cb31a2a..70cbb179 100644 --- a/paint/sources/neural_nodes/upscale_image_node.ts +++ b/paint/sources/neural_nodes/upscale_image_node.ts @@ -1,23 +1,10 @@ -let upscale_image_node_result: gpu_texture_t = null; - function upscale_image_node_init() { array_push(nodes_material_neural, upscale_image_node_def); - map_set(parser_material_node_vectors, "NEURAL_UPSCALE_IMAGE", upscale_image_node_vector); + map_set(parser_material_node_vectors, "NEURAL_UPSCALE_IMAGE", neural_node_vector); map_set(ui_nodes_custom_buttons, "upscale_image_node_button", upscale_image_node_button); } -function upscale_image_node_vector(node: ui_node_t, socket: ui_node_socket_t): string { - if (upscale_image_node_result == null) { - return "float3(0.0, 0.0, 0.0)"; - } - let tex_name: string = parser_material_node_name(node); - map_set(data_cached_images, tex_name, upscale_image_node_result); - let tex: bind_tex_t = parser_material_make_bind_tex(tex_name, tex_name); - let texstore: string = parser_material_texture_store(node, tex, tex_name, color_space_t.AUTO); - return texstore + ".rgb"; -} - function upscale_image_node_button(node_id: i32) { let canvas: ui_node_canvas_t = ui_nodes_get_canvas(true); let node: ui_node_t = ui_get_node(canvas.nodes, node_id); @@ -25,48 +12,21 @@ function upscale_image_node_button(node_id: i32) { let models: string[] = [ "RealESRGAN_X4" ]; let model: i32 = ui_combo(ui_handle(__ID__), models, tr("Model")); - if (iron_exec_async_done == 0) { - ui_button("Processing..."); - } - else if (ui_button("Run")) { - let inp: ui_node_socket_t = node.inputs[0]; - let from_node: ui_node_t = null; - for (let i: i32 = 0; i < canvas.links.length; ++i) { - let l: ui_node_link_t = canvas.links[i]; - if (l.to_id == inp.node_id) { - from_node = ui_get_node(canvas.nodes, l.from_id); - break; - } - } - + if (neural_node_button()) { + let from_node: ui_node_t = neural_from_node(node.inputs[0]); let input: gpu_texture_t = ui_nodes_get_node_preview_image(from_node); if (input != null) { - let dir: string; - if (path_is_protected()) { - dir = iron_internal_save_path() + "models"; - } - else { - dir = iron_internal_files_location() + path_sep + "models"; - } + let dir: string = neural_node_dir(); iron_write_png(dir + path_sep + "input.png", gpu_get_texture_pixels(input), input.width, input.height, 0); + let argv: string[] = [ dir + "/sd", "-M", "upscale", "--upscale-model", dir + "/RealESRGAN_x4plus.pth", "-i", dir + "/input.png", "-o", dir + "/output.png", null ]; iron_exec_async(argv[0], argv.buffer); - sys_notify_on_update(upscale_image_node_check_result, dir); + sys_notify_on_update(neural_node_check_result, node); } } } -function upscale_image_node_check_result(dir: string) { - if (iron_exec_async_done == 1) { - let file: string = dir + path_sep + "output.png"; - if (iron_file_exists(file)) { - upscale_image_node_result = iron_load_texture(file); - } - sys_remove_update(upscale_image_node_check_result); - } -} - let upscale_image_node_def: ui_node_t = { id : 0, name : _tr("Upscale Image"), diff --git a/paint/sources/neural_nodes/vary_image_node.ts b/paint/sources/neural_nodes/vary_image_node.ts new file mode 100644 index 00000000..a79ac486 --- /dev/null +++ b/paint/sources/neural_nodes/vary_image_node.ts @@ -0,0 +1,84 @@ + +function vary_image_node_init() { + array_push(nodes_material_neural, vary_image_node_def); + map_set(parser_material_node_vectors, "NEURAL_VARY_IMAGE", neural_node_vector); + map_set(ui_nodes_custom_buttons, "vary_image_node_button", vary_image_node_button); +} + +function vary_image_node_button(node_id: i32) { + let canvas: ui_node_canvas_t = ui_nodes_get_canvas(true); + let node: ui_node_t = ui_get_node(canvas.nodes, node_id); + + let models: string[] = [ "Qwen Image Edit" ]; + let model: i32 = ui_combo(ui_handle(__ID__), models, tr("Model")); + + let prompt: string = ui_text_area(ui_handle(__ID__), ui_align_t.LEFT, true, tr("prompt"), true); + node.buttons[0].height = string_split(prompt, "\n").length + 2; + + if (neural_node_button()) { + let from_node: ui_node_t = neural_from_node(node.inputs[0]); + let input: gpu_texture_t = ui_nodes_get_node_preview_image(from_node); + if (input != null) { + let dir: string = neural_node_dir(); + iron_write_png(dir + path_sep + "input.png", gpu_get_texture_pixels(input), input.width, input.height, 0); + + let argv: string[] = [ + dir + "/sd", "--diffusion-model", dir + "/Qwen-Image-Edit-2509-Q4_K_S.gguf", "--vae", dir + "/Qwen_Image-VAE.safetensors", "--qwen2vl", + dir + "/Qwen2.5-VL-7B-Instruct-Q4_K_S.gguf", "--qwen2vl_vision", dir + "/mmproj-F16.gguf", "--sampling-method", "euler", + "--offload-to-cpu", "--diffusion-fa", "--steps", "30", "-s", "-1", "-W", "512", "-H", "512", "-p", "'" + prompt + "'", + // "-n", + // "'" + negative + "'", + "-r", dir + "/input.png", "-o", dir + "/output.png", null + ]; + + iron_exec_async(argv[0], argv.buffer); + sys_notify_on_update(neural_node_check_result, node); + } + } +} + +let vary_image_node_def: ui_node_t = { + id : 0, + name : _tr("Vary Image"), + type : "NEURAL_VARY_IMAGE", + x : 0, + y : 0, + color : 0xff4982a0, + inputs : [ { + id : 0, + node_id : 0, + name : _tr("Color"), + type : "RGBA", + color : 0xffc7c729, + default_value : f32_array_create_xyzw(1.0, 1.0, 1.0, 1.0), + min : 0.0, + max : 1.0, + precision : 100, + display : 0 + } ], + outputs : [ { + id : 0, + node_id : 0, + name : _tr("Color"), + type : "RGBA", + color : 0xffc7c729, + default_value : f32_array_create_xyzw(0.0, 0.0, 0.0, 1.0), + min : 0.0, + max : 1.0, + precision : 100, + display : 0 + } ], + buttons : [ { + name : "vary_image_node_button", + type : "CUSTOM", + output : -1, + default_value : f32_array_create_x(0), + data : null, + min : 0.0, + max : 1.0, + precision : 100, + height : 0 + } ], + width : 0, + flags : 0 +}; diff --git a/paint/sources/nodes_material.ts b/paint/sources/nodes_material.ts index 403de2b4..a006f28c 100644 --- a/paint/sources/nodes_material.ts +++ b/paint/sources/nodes_material.ts @@ -88,6 +88,7 @@ function nodes_material_init() { text_to_image_node_init(); tile_image_node_init(); upscale_image_node_init(); + vary_image_node_init(); } nodes_material_group = []; diff --git a/paint/sources/ui_nodes.ts b/paint/sources/ui_nodes.ts index b0b9ed50..eeab21cf 100644 --- a/paint/sources/ui_nodes.ts +++ b/paint/sources/ui_nodes.ts @@ -1164,20 +1164,8 @@ function ui_nodes_get_node_preview_image(n: ui_node_t): gpu_texture_t { img = project_get_image(project_assets[asset_index]); } } - else if (n.type == "NEURAL_TEXT_TO_IMAGE") { - img = text_to_image_node_result; - } - else if (n.type == "NEURAL_UPSCALE_IMAGE") { - img = upscale_image_node_result; - } - else if (n.type == "NEURAL_EDIT_IMAGE") { - img = edit_image_node_result; - } - else if (n.type == "NEURAL_TILE_IMAGE") { - img = tile_image_node_result; - } - else if (n.type == "NEURAL_INPAINT_IMAGE") { - img = inpaint_image_node_result; + else if (starts_with(n.type, "NEURAL_") && n.type != "NEURAL_IMAGE_TO_PBR") { + img = any_imap_get(neural_node_results, n.id); } else if (ui_nodes_canvas_type == canvas_type_t.MATERIAL) { img = any_imap_get(context_raw.node_preview_map, n.id);