#include "../global.h" bool ui_nodes_show_menu = false; bool ui_nodes_show_menu_first = true; bool ui_nodes_hide_menu = false; i32 ui_nodes_menu_category = 0; f32 ui_nodes_popup_x = 0.0; f32 ui_nodes_popup_y = 0.0; i32 ui_nodes_node_search_x; i32 ui_nodes_node_search_y; bool ui_nodes_uichanged_last = false; bool ui_nodes_recompile_mat = false; // Mat preview bool ui_nodes_recompile_mat_final = false; ui_node_t *ui_nodes_node_search_spawn = NULL; i32 ui_nodes_node_search_offset = 0; ui_node_canvas_t *ui_nodes_last_canvas = NULL; i32 ui_nodes_last_node_selected_id = -1; bool ui_nodes_release_link = false; bool ui_nodes_is_node_menu_op = false; gpu_texture_t *ui_nodes_grid = NULL; bool ui_nodes_controls_down = false; slot_material_t_array_t *ui_nodes_tabs = NULL; ui_node_link_t *_ui_nodes_on_link_drag_link_drag; ui_node_t *_ui_nodes_on_link_drag_node; ui_node_socket_t *_ui_nodes_on_socket_released_socket; ui_node_t *_ui_nodes_on_socket_released_node; ui_node_t *_ui_nodes_on_canvas_released_selected; bool _ui_nodes_node_search_first; void (*_ui_nodes_node_search_done)(void); ui_node_t *ui_nodes_node_changed = NULL; void (*_ui_nodes_render_tmp)(i32); void ui_viewnodes_on_link_drag_on_node_search_done() { ui_nodes_t *ui_nodes = ui_nodes_get_nodes(); i32 node_selected_id = _ui_nodes_on_link_drag_node->id; if (ui_nodes->nodes_selected_id->length > 0) { node_selected_id = ui_nodes->nodes_selected_id->buffer[0]; } ui_node_link_t *link_drag = _ui_nodes_on_link_drag_link_drag; ui_node_t_array_t *nodes = ui_nodes_get_canvas(true)->nodes; ui_node_t *n = ui_get_node(nodes, node_selected_id); if (link_drag->to_id == -1 && n->inputs->length > 0) { link_drag->to_id = n->id; char *from_type = _ui_nodes_on_link_drag_node->outputs->buffer[link_drag->from_socket]->type; // Connect to the first socket link_drag->to_socket = 0; // Try to find the first type-matching socket and use it if present for (i32 i = 0; i < n->inputs->length; ++i) { ui_node_socket_t *socket = n->inputs->buffer[i]; if (string_equals(socket->type, from_type)) { link_drag->to_socket = array_index_of(n->inputs, socket); break; } } any_array_push(ui_nodes_get_canvas(true)->links, link_drag); } else if (link_drag->from_id == -1 && n->outputs->length > 0) { link_drag->from_id = n->id; link_drag->from_socket = 0; any_array_push(ui_nodes_get_canvas(true)->links, link_drag); } } void ui_nodes_node_search_menu() { ui_menu_h = UI_ELEMENT_H() * 8; ui_handle_t *search_handle = ui_handle(__ID__); char *search = to_lower_case(ui_text_input(search_handle, "", UI_ALIGN_LEFT, true, true)); g_ui->changed = false; if (_ui_nodes_node_search_first) { _ui_nodes_node_search_first = false; search_handle->text = ""; ui_start_text_edit(search_handle, UI_ALIGN_LEFT); // Focus search bar } if (search_handle->changed) { ui_nodes_node_search_offset = 0; } if (g_ui->is_key_pressed) { // Move selection if (g_ui->key_code == KEY_CODE_DOWN && ui_nodes_node_search_offset < 6) { ui_nodes_node_search_offset++; } if (g_ui->key_code == KEY_CODE_UP && ui_nodes_node_search_offset > 0) { ui_nodes_node_search_offset--; } } bool enter = keyboard_down("enter"); i32 count = 0; i32 _BUTTON_COL = g_theme->BUTTON_COL; bool _FILL_BUTTON_BG = g_theme->FILL_BUTTON_BG; g_theme->FILL_BUTTON_BG = true; bool _SHADOWS = g_theme->SHADOWS; g_theme->SHADOWS = false; node_list_t_array_t *node_list = ui_nodes_canvas_type == CANVAS_TYPE_MATERIAL ? nodes_material_list : nodes_brush_list; for (i32 i = 0; i < node_list->length; ++i) { ui_node_t_array_t *list = node_list->buffer[i]; for (i32 i = 0; i < list->length; ++i) { ui_node_t *n = list->buffer[i]; if (string_index_of(to_lower_case(tr(n->name)), search) >= 0) { g_theme->BUTTON_COL = count == ui_nodes_node_search_offset ? g_theme->HIGHLIGHT_COL : g_theme->SEPARATOR_COL; if (ui_button(tr(n->name), UI_ALIGN_LEFT, "") || (enter && count == ui_nodes_node_search_offset)) { ui_nodes_push_undo(NULL); ui_nodes_t *nodes = ui_nodes_get_nodes(); ui_node_canvas_t *canvas = ui_nodes_get_canvas(true); gc_unroot(ui_nodes_node_search_spawn); ui_nodes_node_search_spawn = ui_nodes_make_node(n, nodes, canvas); // Spawn selected node gc_root(ui_nodes_node_search_spawn); any_array_push(canvas->nodes, ui_nodes_node_search_spawn); nodes->nodes_selected_id = i32_array_create_from_raw( (i32[]){ ui_nodes_node_search_spawn->id, }, 1); nodes->nodes_drag = true; ui_nodes_hwnd->redraws = 2; if (enter) { g_ui->changed = true; count = 6; // Trigger break } if (_ui_nodes_node_search_done != NULL) { _ui_nodes_node_search_done(); } } if (++count > 6) { break; } } } if (count > 6) { break; } } if (enter && count == 0) { // Hide popup on enter when node is not found g_ui->changed = true; search_handle->text = ""; } g_theme->BUTTON_COL = _BUTTON_COL; g_theme->FILL_BUTTON_BG = _FILL_BUTTON_BG; g_theme->SHADOWS = _SHADOWS; } void ui_nodes_node_search(i32 x, i32 y, void (*done)(void)) { _ui_nodes_node_search_first = true; gc_unroot(_ui_nodes_node_search_done); _ui_nodes_node_search_done = done; gc_root(_ui_nodes_node_search_done); ui_menu_draw(&ui_nodes_node_search_menu, x, y); } void ui_viewnodes_on_link_drag(i32 link_drag_id, bool is_new_link) { if (!is_new_link) { return; } ui_node_link_t_array_t *links = ui_nodes_get_canvas(true)->links; ui_node_link_t *link_drag = ui_get_link(links, link_drag_id); ui_node_t_array_t *nodes = ui_nodes_get_canvas(true)->nodes; ui_node_t *node = ui_get_node(nodes, link_drag->from_id > -1 ? link_drag->from_id : link_drag->to_id); i32 link_x = g_ui->_window_x + UI_NODE_X(node); i32 link_y = g_ui->_window_y + UI_NODE_Y(node); if (link_drag->from_id > -1) { link_x += UI_NODE_W(node); link_y += UI_OUTPUT_Y(node, link_drag->from_socket); } else { link_y += ui_nodes_INPUT_Y(ui_nodes_get_canvas(true), node, link_drag->to_socket) + UI_OUTPUTS_H(node, -1) + UI_BUTTONS_H(node); } if (math_abs(mouse_x - link_x) > 5 || math_abs(mouse_y - link_y) > 5) { // Link length gc_unroot(_ui_nodes_on_link_drag_link_drag); _ui_nodes_on_link_drag_link_drag = link_drag; gc_root(_ui_nodes_on_link_drag_link_drag); gc_unroot(_ui_nodes_on_link_drag_node); _ui_nodes_on_link_drag_node = node; gc_root(_ui_nodes_on_link_drag_node); ui_nodes_node_search(-1, -1, &ui_viewnodes_on_link_drag_on_node_search_done); } // Selecting which node socket to preview else if (link_drag->from_id > -1) { i32_imap_set(g_context->node_preview_socket_map, node->id, link_drag->from_socket); gc_unroot(ui_nodes_node_changed); ui_nodes_node_changed = node; gc_root(ui_nodes_node_changed); } } void ui_viewnodes_on_socket_released_group_edit_box() { ui_node_socket_t *socket = _ui_nodes_on_socket_released_socket; ui_node_t *node = _ui_nodes_on_socket_released_node; string_array_t *type_combo = any_array_create_from_raw( (void *[]){ tr("Color"), tr("Vector"), tr("Value"), }, 3); i32 type = ui_combo(_ui_nodes_htype, type_combo, tr("Type"), true, UI_ALIGN_LEFT, true); if (_ui_nodes_htype->changed) { _ui_nodes_hname->text = type == 0 ? tr("Color") : type == 1 ? tr("Vector") : tr("Value"); } char *name = ui_text_input(_ui_nodes_hname, tr("Name"), UI_ALIGN_LEFT, true, false); f32 min = ui_float_input(_ui_nodes_hmin, tr("Min"), UI_ALIGN_LEFT, 1000.0); f32 max = ui_float_input(_ui_nodes_hmax, tr("Max"), UI_ALIGN_LEFT, 1000.0); f32_array_t *default_value = NULL; if (type == 0) { ui_row4(); ui_float_input(_ui_nodes_hval0, tr("R"), UI_ALIGN_LEFT, 1000.0); ui_float_input(_ui_nodes_hval1, tr("G"), UI_ALIGN_LEFT, 1000.0); ui_float_input(_ui_nodes_hval2, tr("B"), UI_ALIGN_LEFT, 1000.0); ui_float_input(_ui_nodes_hval3, tr("A"), UI_ALIGN_LEFT, 1000.0); default_value = f32_array_create_xyzw(_ui_nodes_hval0->f, _ui_nodes_hval1->f, _ui_nodes_hval2->f, _ui_nodes_hval3->f); } else if (type == 1) { ui_row3(); _ui_nodes_hval0->f = ui_float_input(_ui_nodes_hval0, tr("X"), UI_ALIGN_LEFT, 1000.0); _ui_nodes_hval1->f = ui_float_input(_ui_nodes_hval1, tr("Y"), UI_ALIGN_LEFT, 1000.0); _ui_nodes_hval2->f = ui_float_input(_ui_nodes_hval2, tr("Z"), UI_ALIGN_LEFT, 1000.0); default_value = f32_array_create_xyz(_ui_nodes_hval0->f, _ui_nodes_hval1->f, _ui_nodes_hval2->f); } else { f32 f = ui_float_input(_ui_nodes_hval0, tr("Value"), UI_ALIGN_LEFT, 1000.0); default_value = f32_array_create_x(f); } if (ui_icon_button(tr("OK"), ICON_CHECK, UI_ALIGN_CENTER)) { socket->name = string_copy(name); socket->type = type == 0 ? "RGBA" : type == 1 ? "VECTOR" : "VALUE"; socket->color = nodes_material_get_socket_color(socket->type); socket->min = min; socket->max = max; socket->default_value = default_value; ui_box_hide(); nodes_material_sync_sockets(node); ui_nodes_hwnd->redraws = 2; } } void ui_viewnodes_on_socket_released_group_edit(void *_) { ui_end_input(); ui_box_show_custom(&ui_viewnodes_on_socket_released_group_edit_box, 400, 250, NULL, true, tr("Socket")); } void ui_viewnodes_on_socket_released_group_menu_draw() { ui_node_socket_t *socket = _ui_nodes_on_socket_released_socket; ui_node_t *node = _ui_nodes_on_socket_released_node; if (ui_menu_button(tr("Edit"), "", ICON_EDIT)) { _ui_nodes_htype->i = string_equals(socket->type, "RGBA") ? 0 : string_equals(socket->type, "VECTOR") ? 1 : 2; _ui_nodes_hname->text = string_copy(socket->name); _ui_nodes_hmin->f = socket->min; _ui_nodes_hmax->f = socket->max; if (string_equals(socket->type, "RGBA") || string_equals(socket->type, "VECTOR")) { _ui_nodes_hval0->f = socket->default_value->buffer[0]; _ui_nodes_hval1->f = socket->default_value->buffer[1]; _ui_nodes_hval2->f = socket->default_value->buffer[2]; if (string_equals(socket->type, "RGBA")) { _ui_nodes_hval3->f = socket->default_value->buffer[3]; } } else { _ui_nodes_hval0->f = socket->default_value->buffer[0]; } sys_notify_on_next_frame(&ui_viewnodes_on_socket_released_group_edit, NULL); } if (ui_menu_button(tr("Delete"), "", ICON_DELETE)) { i32 i = 0; ui_node_canvas_t *canvas = ui_nodes_get_canvas(true); // Remove links connected to the socket while (i < canvas->links->length) { ui_node_link_t *l = canvas->links->buffer[i]; if ((l->from_id == node->id && l->from_socket == array_index_of(node->outputs, socket)) || (l->to_id == node->id && l->to_socket == array_index_of(node->inputs, socket))) { array_splice(canvas->links, i, 1); } else { i++; } } // Remove socket array_remove(node->inputs, socket); array_remove(node->outputs, socket); nodes_material_sync_sockets(node); } } void ui_viewnodes_on_socket_released_group_menu(void *_) { ui_menu_draw(&ui_viewnodes_on_socket_released_group_menu_draw, -1, -1); } void ui_viewnodes_on_canvas_context_menu() { ui_node_t *selected = _ui_nodes_on_canvas_released_selected; g_ui->_y += 1; bool is_protected = selected == NULL || string_equals(selected->type, "OUTPUT_MATERIAL_PBR") || string_equals(selected->type, "GROUP_INPUT") || string_equals(selected->type, "GROUP_OUTPUT") || string_equals(selected->type, "brush_output_node"); g_ui->enabled = !is_protected; if (ui_menu_button(tr("Cut"), "ctrl+x", ICON_CUT)) { sys_notify_on_next_frame(&ui_viewnodes_on_canvas_cut, NULL); } if (ui_menu_button(tr("Copy"), "ctrl+c", ICON_COPY)) { sys_notify_on_next_frame(&ui_viewnodes_on_canvas_copy, NULL); } g_ui->enabled = !string_equals(ui_clipboard, ""); if (ui_menu_button(tr("Paste"), "ctrl+v", ICON_PASTE)) { sys_notify_on_next_frame(&ui_viewnodes_on_canvas_paste, NULL); } g_ui->enabled = !is_protected; if (ui_menu_button(tr("Delete"), "delete", ICON_DELETE)) { sys_notify_on_next_frame(&ui_viewnodes_on_canvas_delete, NULL); } if (ui_menu_button(tr("Duplicate"), "ctrl+d", ICON_DUPLICATE)) { sys_notify_on_next_frame(&ui_viewnodes_on_canvas_released_duplicate, NULL); } if (selected != NULL && string_equals(selected->type, "RGB")) { if (ui_menu_button(tr("Add Swatch"), "", ICON_PALETTE)) { f32_array_t *color = selected->outputs->buffer[0]->default_value; swatch_color_t *new_swatch = project_make_swatch(color_from_floats(color->buffer[0], color->buffer[1], color->buffer[2], color->buffer[3])); g_context->swatch = new_swatch; any_array_push(g_project->swatches, new_swatch); ui_base_hwnds->buffer[TAB_AREA_STATUS]->redraws = 1; } } if (ui_menu_button(tr("Capture Output"), "", ICON_PHOTO)) { sys_notify_on_next_frame(&ui_viewnodes_on_canvas_capture_output, NULL); } if (ui_nodes_canvas_type == CANVAS_TYPE_MATERIAL) { ui_menu_separator(); if (ui_menu_button(tr("2D View"), "", ICON_NONE)) { ui_base_show_2d_view(VIEW_2D_TYPE_NODE); } } g_ui->enabled = true; } void ui_viewnodes_on_canvas_released() { if (g_ui->input_released_r && context_in_nodes() && math_abs(g_ui->input_x - g_ui->input_started_x) < 2 && math_abs(g_ui->input_y - g_ui->input_started_y) < 2) { // Node selection ui_nodes_t *nodes = ui_nodes_get_nodes(); ui_node_canvas_t *canvas = ui_nodes_get_canvas(true); ui_node_t *selected = NULL; for (i32 i = 0; i < canvas->nodes->length; ++i) { ui_node_t *node = canvas->nodes->buffer[i]; if (ui_input_in_rect(g_ui->_window_x + UI_NODE_X(node), g_ui->_window_y + UI_NODE_Y(node), UI_NODE_W(node), UI_NODE_H(canvas, node))) { selected = node; break; } } if (selected == NULL) { nodes->nodes_selected_id = i32_array_create_from_raw((i32[]){}, 0); } else if (i32_array_index_of(nodes->nodes_selected_id, selected->id) == -1) { nodes->nodes_selected_id = i32_array_create_from_raw( (i32[]){ selected->id, }, 1); } // Node context menu if (!ui_nodes_socket_released) { gc_unroot(_ui_nodes_on_canvas_released_selected); _ui_nodes_on_canvas_released_selected = selected; gc_root(_ui_nodes_on_canvas_released_selected); ui_menu_draw(&ui_viewnodes_on_canvas_context_menu, -1, -1); } } if (g_ui->input_released) { ui_nodes_t *nodes = ui_nodes_get_nodes(); ui_node_canvas_t *canvas = ui_nodes_get_canvas(true); for (i32 i = 0; i < canvas->nodes->length; ++i) { ui_node_t *node = canvas->nodes->buffer[i]; if (ui_input_in_rect(g_ui->_window_x + UI_NODE_X(node), g_ui->_window_y + UI_NODE_Y(node), UI_NODE_W(node), UI_NODE_H(canvas, node))) { if (nodes->nodes_selected_id->length > 0 && node->id == nodes->nodes_selected_id->buffer[0]) { ui_view2d_hwnd->redraws = 2; if (sys_time() - g_context->select_time < 0.2) ui_base_show_2d_view(VIEW_2D_TYPE_NODE); g_context->select_time = sys_time(); } break; } } } } void ui_viewnodes_on_socket_released(i32 socket_id) { ui_node_canvas_t *canvas = ui_nodes_get_canvas(true); ui_node_socket_t *socket = ui_get_socket(canvas->nodes, socket_id); ui_node_t *node = ui_get_node(canvas->nodes, socket->node_id); if (g_ui->input_released_r) { if (string_equals(node->type, "GROUP_INPUT") || string_equals(node->type, "GROUP_OUTPUT")) { gc_unroot(_ui_nodes_on_socket_released_socket); _ui_nodes_on_socket_released_socket = socket; gc_root(_ui_nodes_on_socket_released_socket); gc_unroot(_ui_nodes_on_socket_released_node); _ui_nodes_on_socket_released_node = node; gc_root(_ui_nodes_on_socket_released_node); sys_notify_on_next_frame(&ui_viewnodes_on_socket_released_group_menu, NULL); } else { ui_viewnodes_on_canvas_released(); } } // Selecting which node socket to preview // else { // let i: i32 = array_index_of(node.outputs, socket); // if (i > -1) { // i32_imap_set(g_context.node_preview_socket_map, node.id, i); // ui_nodes_node_changed = node; // } // } } ui_canvas_control_t *ui_viewnodes_on_canvas_control() { ui_canvas_control_t *control = ui_nodes_get_canvas_control(ui_nodes_controls_down, true); ui_nodes_controls_down = control->controls_down; return control; } f32 ui_nodes_get_zoom_delta() { return g_config->zoom_direction == ZOOM_DIRECTION_VERTICAL ? -g_ui->input_dy : g_config->zoom_direction == ZOOM_DIRECTION_VERTICAL_INVERTED ? -g_ui->input_dy : g_config->zoom_direction == ZOOM_DIRECTION_HORIZONTAL ? g_ui->input_dx : g_config->zoom_direction == ZOOM_DIRECTION_HORIZONTAL_INVERTED ? g_ui->input_dx : -(g_ui->input_dy - g_ui->input_dx); } ui_canvas_control_t *ui_nodes_get_canvas_control(bool controls_down, bool is_node_view) { if (g_config->wrap_mouse && controls_down) { if (g_ui->input_x < g_ui->_window_x) { g_ui->input_x = g_ui->_window_x + g_ui->_window_w; iron_mouse_set_position(math_floor(g_ui->input_x), math_floor(g_ui->input_y)); } else if (g_ui->input_x > g_ui->_window_x + g_ui->_window_w) { g_ui->input_x = g_ui->_window_x; iron_mouse_set_position(math_floor(g_ui->input_x), math_floor(g_ui->input_y)); } else if (g_ui->input_y < g_ui->_window_y) { g_ui->input_y = g_ui->_window_y + g_ui->_window_h; iron_mouse_set_position(math_floor(g_ui->input_x), math_floor(g_ui->input_y)); } else if (g_ui->input_y > g_ui->_window_y + g_ui->_window_h) { g_ui->input_y = g_ui->_window_y; iron_mouse_set_position(math_floor(g_ui->input_x), math_floor(g_ui->input_y)); } } if (operator_shortcut(any_map_get(g_keymap, "action_pan"), SHORTCUT_TYPE_STARTED) || operator_shortcut(any_map_get(g_keymap, "action_zoom"), SHORTCUT_TYPE_STARTED) || g_ui->input_started_r || g_ui->input_wheel_delta != 0.0) { controls_down = true; } else if (!operator_shortcut(any_map_get(g_keymap, "action_pan"), SHORTCUT_TYPE_DOWN) && !operator_shortcut(any_map_get(g_keymap, "action_zoom"), SHORTCUT_TYPE_DOWN) && !g_ui->input_down_r && g_ui->input_wheel_delta == 0.0) { controls_down = false; } if (!controls_down) { ui_canvas_control_t *cc = GC_ALLOC_INIT(ui_canvas_control_t, {.pan_x = 0, .pan_y = 0, .zoom = 0, .controls_down = controls_down}); return cc; } bool pan = g_ui->input_down_r || operator_shortcut(any_map_get(g_keymap, "action_pan"), SHORTCUT_TYPE_DOWN); f32 zoom_delta = operator_shortcut(any_map_get(g_keymap, "action_zoom"), SHORTCUT_TYPE_DOWN) ? ui_nodes_get_zoom_delta() / 100.0 : 0.0; ui_canvas_control_t *control = GC_ALLOC_INIT(ui_canvas_control_t, {.pan_x = pan ? g_ui->input_dx : 0.0, .pan_y = pan ? g_ui->input_dy : 0.0, .zoom = g_ui->input_wheel_delta != 0.0 ? -g_ui->input_wheel_delta / 10 : zoom_delta, .controls_down = controls_down}); if (is_node_view && g_ui->input_x < g_ui->_window_x) { control->pan_x = 0.0; control->pan_y = 0.0; control->zoom = 0.0; } if (g_ui->combo_selected_handle != NULL) { control->zoom = 0.0; } if (control->zoom != 0.0) { ui_nodes_grid_redraw = true; } return control; } gpu_texture_t *ui_nodes_draw_grid(f32 zoom) { i32 ww = g_config->layout->buffer[LAYOUT_SIZE_NODES_W]; if (!ui_base_show) { ww += g_config->layout->buffer[LAYOUT_SIZE_SIDEBAR_W] + ui_toolbar_w(true); } if (!base_view3d_show) { ww += base_view3d_w(); } i32 wh = sys_h(); f32 step = ui_nodes_grid_cell_w * zoom; i32 mult = 5 * UI_SCALE(); i32 w = math_floor(ww + step * mult); i32 h = math_floor(wh + step * mult); if (w < 1) { w = 1; } if (h < 1) { h = 1; } gpu_texture_t *grid = gpu_create_render_target(w, h, GPU_TEXTURE_FORMAT_RGBA32); draw_begin(grid, true, g_theme->SEPARATOR_COL); i32 sep_col = g_theme->SEPARATOR_COL; i32 line_primary = base_darker(sep_col, 0x00050505); if (line_primary < 0xff000000) { line_primary = sep_col + 0x00050505; } i32 line_secondary = base_darker(sep_col, 0x00090909); if (line_secondary < 0xff000000) { line_secondary = sep_col + 0x00090909; } draw_set_color(line_primary); step = ui_nodes_grid_small_cell_w * zoom; for (i32 i = 0; i < math_floor(h / (float)step) + 1; ++i) { draw_line(0, i * step, w, i * step, 1.0); } for (i32 i = 0; i < math_floor(w / (float)step) + 1; ++i) { draw_line(i * step, 0, i * step, h, 1.0); } draw_set_color(line_secondary); step = ui_nodes_grid_cell_w * zoom; for (i32 i = 0; i < math_floor(h / (float)step) + 1; ++i) { draw_line(0, i * step, w, i * step, 1.0); } for (i32 i = 0; i < math_floor(w / (float)step) + 1; ++i) { draw_line(i * step, 0, i * step, h, 1.0); } draw_end(); return grid; } void ui_nodes_render_color_picker_callback(swatch_color_t *color) { _ui_nodes_render_tmp(color->base); ui_nodes_hwnd->redraws = 2; bool material_live = g_config->material_live; if (material_live) { ui_nodes_canvas_changed(); } } void ui_nodes_draw_menubar_search(void *_) { ui_nodes_node_search(math_floor(ui_nodes_node_search_x), math_floor(ui_nodes_node_search_y), NULL); } void ui_nodes_draw_menubar() { ui_node_canvas_t *c = ui_nodes_get_canvas(true); i32 ew = math_floor(UI_ELEMENT_W() * 0.7); i32 top_y = ui_menu_top_y(); draw_set_color(g_theme->WINDOW_BG_COL); draw_filled_rect(0, top_y, ui_nodes_ww, UI_ELEMENT_H() + UI_ELEMENT_OFFSET() * 2); draw_set_color(0xffffffff); i32 start_y = top_y + UI_ELEMENT_OFFSET(); g_ui->_x = 0; g_ui->_y = 2 + start_y; if (g_config->touch_ui && !base_view3d_show && !ui_view2d_show) { g_ui->_w = math_floor(ew + 3); if (ui_icon_button("Back", ICON_ARROW_LEFT, UI_ALIGN_CENTER)) { g_ui->input_released = false; g_config->workspace = WORKSPACE_PAINT_3D; config_save(); base_update_workspace(); } g_ui->_x += ew + 3; g_ui->_y = 2 + start_y; } // Editable canvas name g_ui->_w = ew; ui_handle_t *h = ui_handle(__ID__); h->text = string_copy(c->name); g_ui->_w = math_floor(math_min(draw_string_width(g_font, g_ui->font_size, h->text) + 15 * UI_SCALE(), 100 * UI_SCALE())); char *new_name = ui_text_input(h, "", UI_ALIGN_LEFT, true, false); g_ui->_x += g_ui->_w + 3; g_ui->_y = 2 + start_y; g_ui->_w = ew; if (h->changed) { // Check whether renaming is possible and update group links if (ui_nodes_group_stack->length > 0) { bool can_rename = true; for (i32 i = 0; i < g_project->_->material_groups->length; ++i) { node_group_t *m = g_project->_->material_groups->buffer[i]; if (string_equals(m->canvas->name, new_name)) { can_rename = false; // Name already used } } if (can_rename) { char *old_name = c->name; c->name = string_copy(new_name); ui_node_canvas_t_array_t *canvases = any_array_create_from_raw((void *[]){}, 0); for (i32 i = 0; i < g_project->_->materials->length; ++i) { slot_material_t *m = g_project->_->materials->buffer[i]; any_array_push(canvases, m->canvas); } for (i32 i = 0; i < g_project->_->material_groups->length; ++i) { node_group_t *m = g_project->_->material_groups->buffer[i]; any_array_push(canvases, m->canvas); } for (i32 i = 0; i < canvases->length; ++i) { ui_node_canvas_t *canvas = canvases->buffer[i]; for (i32 i = 0; i < canvas->nodes->length; ++i) { ui_node_t *n = canvas->nodes->buffer[i]; if (string_equals(n->type, "GROUP") && string_equals(n->name, old_name)) { n->name = string_copy(c->name); } } } } } else { c->name = string_copy(new_name); } } i32 _BUTTON_COL = g_theme->BUTTON_COL; bool _SHADOWS = g_theme->SHADOWS; g_theme->BUTTON_COL = g_theme->WINDOW_BG_COL; g_theme->SHADOWS = false; string_array_t *cats = ui_nodes_canvas_type == CANVAS_TYPE_MATERIAL ? nodes_material_categories : nodes_brush_categories; for (i32 i = 0; i < cats->length; ++i) { if ((ui_menubar_button(tr(cats->buffer[i]))) || (g_ui->is_hovered && ui_nodes_show_menu)) { ui_nodes_show_menu = true; ui_nodes_menu_category = i; ui_nodes_popup_x = ui_nodes_wx + g_ui->_x; ui_nodes_popup_y = ui_nodes_wy + g_ui->_y; if (g_config->touch_ui) { ui_nodes_show_menu_first = true; i32 menuw = math_floor(ew * 2.3); ui_nodes_popup_x -= menuw / 2.0; ui_nodes_popup_x += g_ui->_w / 2.0; } } g_ui->_x += g_ui->_w + 3; g_ui->_y = 2 + start_y; } if (g_config->touch_ui) { i32 _w = g_ui->_w; g_ui->_w = math_floor(36 * UI_SCALE()); g_ui->_y = 4 * UI_SCALE() + start_y; if (ui_menubar_icon_button(ICON_SEARCH)) { ui_nodes_node_search(math_floor(g_ui->_window_x + g_ui->_x), math_floor(g_ui->_window_y + g_ui->_y), NULL); } g_ui->_w = _w; } else { if (ui_menubar_button(tr("Search"))) { ui_nodes_node_search_x = g_ui->_window_x + g_ui->_x; ui_nodes_node_search_y = g_ui->_window_y + g_ui->_y; // Allow for node menu to be closed first sys_notify_on_next_frame(&ui_nodes_draw_menubar_search, NULL); } } if (g_ui->is_hovered) { ui_tooltip(string("%s (%s)", tr("Search for nodes"), any_map_get(g_keymap, "node_search"))); } g_ui->_x += g_ui->_w + 3; g_ui->_y = 2 + start_y; g_theme->BUTTON_COL = _BUTTON_COL; g_theme->SHADOWS = _SHADOWS; // Close node group if (ui_nodes_group_stack->length > 0 && ui_menubar_button(tr("Close"))) { array_pop(ui_nodes_group_stack); } } void ui_nodes_update(void *_) { if (ui_nodes_show && base_ui_enabled) { ui_nodes_wx = math_floor(sys_w()) + ui_toolbar_w(true); ui_nodes_wy = ui_header_h * 2; if (ui_view2d_show && base_view3d_show) { ui_nodes_wy += sys_h() - g_config->layout->buffer[LAYOUT_SIZE_NODES_H]; } i32 ww = g_config->layout->buffer[LAYOUT_SIZE_NODES_W]; if (!ui_base_show) { ww += g_config->layout->buffer[LAYOUT_SIZE_SIDEBAR_W] + ui_toolbar_w(true); ui_nodes_wx -= ui_toolbar_w(true); ui_nodes_wy = 0; } if (!base_view3d_show) { ww += base_view3d_w(); } if (!base_view3d_show && ui_view2d_show) { ui_nodes_wx = base_view3d_w(); ui_nodes_wy = 0; ui_nodes_ww = g_config->layout->buffer[LAYOUT_SIZE_NODES_W]; ui_nodes_wh = sys_h(); if (g_config->layout->buffer[LAYOUT_SIZE_HEADER] == 1) { ui_nodes_wh += ui_header_h * 2; } } if (!base_view3d_show) { ui_nodes_wh -= ui_header_h * 4; } i32 mx = mouse_x; i32 my = mouse_y; bool enabled = true; if (mx < ui_nodes_wx || mx > ui_nodes_wx + ww || my < ui_nodes_wy) { enabled = false; } if (g_ui->is_typing || !g_ui->input_enabled) { enabled = false; } if (enabled) { ui_nodes_t *nodes = ui_nodes_get_nodes(); if (nodes->nodes_selected_id->length > 0 && g_ui->is_key_pressed) { if (g_ui->key_code == KEY_CODE_LEFT) { for (i32 i = 0; i < nodes->nodes_selected_id->length; ++i) { i32 n = nodes->nodes_selected_id->buffer[i]; ui_node_t_array_t *_nodes = ui_nodes_get_canvas(true)->nodes; ui_get_node(_nodes, n)->x -= 1; } } else if (g_ui->key_code == KEY_CODE_RIGHT) { for (i32 i = 0; i < nodes->nodes_selected_id->length; ++i) { i32 n = nodes->nodes_selected_id->buffer[i]; ui_node_t_array_t *_nodes = ui_nodes_get_canvas(true)->nodes; ui_get_node(_nodes, n)->x += 1; } } if (g_ui->key_code == KEY_CODE_UP) { for (i32 i = 0; i < nodes->nodes_selected_id->length; ++i) { i32 n = nodes->nodes_selected_id->buffer[i]; ui_node_t_array_t *_nodes = ui_nodes_get_canvas(true)->nodes; ui_get_node(_nodes, n)->y -= 1; } } else if (g_ui->key_code == KEY_CODE_DOWN) { for (i32 i = 0; i < nodes->nodes_selected_id->length; ++i) { i32 n = nodes->nodes_selected_id->buffer[i]; ui_node_t_array_t *_nodes = ui_nodes_get_canvas(true)->nodes; ui_get_node(_nodes, n)->y += 1; } } } // Node search popup if (operator_shortcut(any_map_get(g_keymap, "node_search"), SHORTCUT_TYPE_STARTED)) { ui_nodes_node_search(-1, -1, NULL); } if (ui_nodes_node_search_spawn != NULL) { g_ui->input_x = mouse_x; // Fix input_dx after popup removal g_ui->input_y = mouse_y; gc_unroot(ui_nodes_node_search_spawn); ui_nodes_node_search_spawn = NULL; } if (operator_shortcut(any_map_get(g_keymap, "view_reset"), SHORTCUT_TYPE_STARTED)) { nodes->pan_x = 0.0; nodes->pan_y = 0.0; nodes->zoom = 1.0; } if (operator_shortcut(any_map_get(g_keymap, "node_overview"), SHORTCUT_TYPE_STARTED)) { nodes->zoom = nodes->zoom == 1.0 ? 0.2 : 1.0; nodes->uiw = ui_nodes_ww; nodes->uih = ui_nodes_wh; } } } // Render ui_nodes_recompile(); ui_nodes_t *ui_nodes = ui_nodes_get_nodes(); // Remove dragged link when mouse is released out of the node viewport ui_node_canvas_t *c = ui_nodes_get_canvas(true); if (ui_nodes_release_link && ui_nodes->link_drag_id != -1) { array_remove(c->links, ui_get_link(c->links, ui_nodes->link_drag_id)); ui_nodes->link_drag_id = -1; } ui_nodes_release_link = g_ui->input_released; if (!ui_nodes_show) { return; } g_ui->input_enabled = base_ui_enabled; if (ui_nodes_last_zoom != ui_nodes->zoom) { ui_nodes_last_zoom = ui_nodes->zoom; ui_nodes_grid_redraw = true; } if (ui_nodes_grid_redraw) { if (ui_nodes_grid != NULL) { gpu_delete_texture(ui_nodes_grid); } gc_unroot(ui_nodes_grid); ui_nodes_grid = ui_nodes_draw_grid(ui_nodes->zoom); gc_root(ui_nodes_grid); ui_nodes_grid_redraw = false; } // Selected node preview if (ui_nodes->nodes_selected_id->length > 0 && ui_nodes->nodes_selected_id->buffer[0] != ui_nodes_last_node_selected_id) { ui_nodes_last_node_selected_id = ui_nodes->nodes_selected_id->buffer[0]; ui_node_t *sel = ui_get_node(c->nodes, ui_nodes->nodes_selected_id->buffer[0]); ui_nodes_make_node_preview(sel); // If script node is present, update selected node preview on every frame bool has_script_node = false; ui_node_canvas_t *canvas = g_context->material->canvas; for (i32 i = 0; i < canvas->nodes->length; ++i) { ui_node_t *n = canvas->nodes->buffer[i]; if (string_equals(n->type, "SCRIPT_CPU")) { has_script_node = true; break; } } if (has_script_node) { ui_nodes_last_node_selected_id = -1; ui_view2d_hwnd->redraws = 2; iron_delay_idle_sleep(); } } else if (ui_nodes->nodes_selected_id->length == 0) { ui_nodes_last_node_selected_id = -1; } // Update node previews if (ui_nodes_node_changed != NULL && !g_ui->input_down) { ui_node_t_array_t *linked_nodes = any_array_create_from_raw((void *[]){}, 0); ui_nodes_get_linked_nodes(linked_nodes, ui_nodes_node_changed, c); ui_node_t *sel = ui_nodes->nodes_selected_id->length > 0 ? ui_get_node(c->nodes, ui_nodes->nodes_selected_id->buffer[0]) : NULL; if (sel != NULL && array_index_of(linked_nodes, sel) == -1) { any_array_push(linked_nodes, sel); } for (i32 i = 0; i < linked_nodes->length; ++i) { ui_nodes_make_node_preview(linked_nodes->buffer[i]); } gc_unroot(ui_nodes_node_changed); ui_nodes_node_changed = NULL; } // Start with UI ui_begin(g_ui); // Make window ui_nodes_ww = g_config->layout->buffer[LAYOUT_SIZE_NODES_W]; ui_nodes_wx = math_floor(sys_w()) + ui_toolbar_w(true); ui_nodes_wy = 0; if (!ui_base_show) { ui_nodes_ww += g_config->layout->buffer[LAYOUT_SIZE_SIDEBAR_W] + ui_toolbar_w(true); ui_nodes_wx -= ui_toolbar_w(true); } if (!base_view3d_show) { ui_nodes_ww += base_view3d_w(); } i32 ew = math_floor(UI_ELEMENT_W() * 0.7); ui_nodes_wh = sys_h(); if (g_config->layout->buffer[LAYOUT_SIZE_HEADER] == 1) { ui_nodes_wh += ui_header_h * 2; } if (ui_view2d_show) { ui_nodes_wh = g_config->layout->buffer[LAYOUT_SIZE_NODES_H]; ui_nodes_wy = sys_h() - g_config->layout->buffer[LAYOUT_SIZE_NODES_H] + ui_header_h; if (g_config->layout->buffer[LAYOUT_SIZE_HEADER] == 1) { ui_nodes_wy += ui_header_h; } else { ui_nodes_wy -= ui_header_h; } if (!ui_base_show) { ui_nodes_wy -= ui_header_h * 2; } } if (!base_view3d_show && ui_view2d_show) { ui_nodes_wx = base_view3d_w(); ui_nodes_wy = 0; ui_nodes_ww = g_config->layout->buffer[LAYOUT_SIZE_NODES_W]; ui_nodes_wh = sys_h(); if (g_config->layout->buffer[LAYOUT_SIZE_HEADER] == 1) { ui_nodes_wh += ui_header_h * 2; } } if (!base_view3d_show) { ui_nodes_wh -= ui_header_h * 4; } if (ui_window(ui_nodes_hwnd, ui_nodes_wx, ui_nodes_wy, ui_nodes_ww, ui_nodes_wh, false)) { if (!g_config->touch_ui) { bool expand = !base_view3d_show && g_config->layout->buffer[LAYOUT_SIZE_SIDEBAR_W] == 0; ui_tab(ui_nodes_htab, expand ? string("%s ", tr("Nodes")) : tr("Nodes"), false, -1, !base_view3d_show); // Additional tabs if (ui_nodes_canvas_type == CANVAS_TYPE_MATERIAL) { if (ui_nodes_tabs == NULL) { gc_unroot(ui_nodes_tabs); ui_nodes_tabs = any_array_create_from_raw((void *[]){}, 0); gc_root(ui_nodes_tabs); } for (i32 i = 0; i < ui_nodes_tabs->length; ++i) { ui_tab(ui_nodes_htab, ui_nodes_tabs->buffer[i]->canvas->name, false, -1, false); if (ui_tab(ui_nodes_htab, tr("x"), false, -1, false)) { array_splice(ui_nodes_tabs, i, 1); ui_nodes_htab->i = 0; } } if (ui_tab(ui_nodes_htab, tr("+"), false, -1, false)) { any_array_push(ui_nodes_tabs, g_context->material); } } } // Grid draw_set_color(0xffffffff); f32 step = ui_nodes_grid_cell_w * ui_nodes->zoom; ui_canvas_control_t *pending_control = ui_nodes_on_canvas_control(); f32 x = math_fmod(UI_NODES_PAN_X() + pending_control->pan_x, step) - step; f32 y = math_fmod(UI_NODES_PAN_Y() + pending_control->pan_y, step) - step; draw_image(ui_nodes_grid, x, y); // Undo if (g_ui->input_started || g_ui->is_key_pressed) { gc_unroot(ui_nodes_last_canvas); ui_nodes_last_canvas = util_clone_canvas(ui_nodes_get_canvas(true)); gc_root(ui_nodes_last_canvas); } // Nodes bool _input_enabled = g_ui->input_enabled; i32 header_h = UI_ELEMENT_H() * 2 + UI_ELEMENT_OFFSET() * 2; bool header_hover = g_ui->input_y < g_ui->_window_y + header_h; g_ui->input_enabled = _input_enabled && !ui_nodes_show_menu && !header_hover; g_ui->window_border_right = g_config->layout->buffer[LAYOUT_SIZE_SIDEBAR_W]; g_ui->window_border_top = ui_header_h * 2; g_ui->window_border_bottom = g_config->layout->buffer[LAYOUT_SIZE_STATUS_H]; ui_node_canvas(ui_nodes, c); g_ui->input_enabled = _input_enabled; if (ui_nodes->color_picker_callback != NULL) { g_context->color_picker_previous_tool = g_context->tool; context_select_tool(TOOL_TYPE_PICKER); gc_unroot(_ui_nodes_render_tmp); _ui_nodes_render_tmp = ui_nodes->color_picker_callback; gc_root(_ui_nodes_render_tmp); g_context->color_picker_callback = &ui_nodes_render_color_picker_callback; ui_nodes->color_picker_callback = NULL; } // Remove nodes with unknown id for this canvas type if (ui_is_paste) { node_list_t_array_t *node_list = ui_nodes_canvas_type == CANVAS_TYPE_MATERIAL ? nodes_material_list : nodes_brush_list; i32 i = 0; while (i++ < c->nodes->length) { ui_node_t *canvas_node = c->nodes->buffer[i - 1]; if (string_array_index_of(ui_nodes_exclude_remove, canvas_node->type) >= 0) { continue; } bool found = false; for (i32 i = 0; i < node_list->length; ++i) { ui_node_t_array_t *list = node_list->buffer[i]; for (i32 i = 0; i < list->length; ++i) { ui_node_t *list_node = list->buffer[i]; if (string_equals(canvas_node->type, list_node->type)) { found = true; break; } } if (found) { break; } } if (string_equals(canvas_node->type, "GROUP") && !ui_nodes_can_place_group(canvas_node->name)) { found = false; } if (!found) { ui_remove_node(canvas_node, c); i32_array_remove(ui_nodes->nodes_selected_id, canvas_node->id); i--; } } } if (ui_nodes_is_node_menu_op) { ui_is_copy = false; ui_is_cut = false; ui_is_paste = false; g_ui->is_delete_down = false; } // Recompile material on change if (g_ui->changed) { ui_nodes_recompile_mat = (g_ui->input_dx != 0 || g_ui->input_dy != 0 || !ui_nodes_uichanged_last) && g_config->material_live; // Instant preview } else if (ui_nodes_uichanged_last) { ui_nodes_canvas_changed(); ui_nodes_push_undo(ui_nodes_last_canvas); } ui_nodes_uichanged_last = g_ui->changed; // Node previews if (g_context->selected_node_preview && ui_nodes->nodes_selected_id->length > 0) { ui_node_t *sel = ui_get_node(c->nodes, ui_nodes->nodes_selected_id->buffer[0]); gpu_texture_t *img = ui_nodes_get_node_preview_image(sel); if (img != NULL && !(sel->flags & UI_NODE_FLAG_PREVIEW)) { f32 tw = 128 * UI_SCALE(); f32 th = tw * (img->height / (float)img->width); f32 tx = ui_nodes_ww - tw - 8 * UI_SCALE(); f32 ty = ui_nodes_wh - th - 8 * UI_SCALE(); if (img == any_imap_get(g_context->node_preview_map, sel->id)) { ui_draw_shadow(tx, ty, tw, th); } bool single_channel = string_equals(sel->type, "LAYER_MASK"); if (single_channel) { draw_set_pipeline(ui_view2d_pipe); gpu_set_int(ui_view2d_channel_loc, 1); } draw_set_color(0xffffffff); draw_scaled_image(img, tx, ty, tw, th); if (single_channel) { draw_set_pipeline(NULL); } } } ui_nodes_draw_menubar(); g_ui->window_border_right = 0; g_ui->window_border_top = 0; g_ui->window_border_bottom = 0; } ui_end(); if (ui_nodes_show_menu) { node_list_t_array_t *list = ui_nodes_canvas_type == CANVAS_TYPE_MATERIAL ? nodes_material_list : nodes_brush_list; ui_node_t_array_t *category = list->buffer[ui_nodes_menu_category]; bool is_group_category = ui_nodes_canvas_type == CANVAS_TYPE_MATERIAL && string_equals(nodes_material_categories->buffer[ui_nodes_menu_category], "Group"); i32 py = ui_nodes_popup_y; i32 menuw = math_floor(ew * 2.3); draw_begin(NULL, false, 0); ui_begin_region(g_ui, math_floor(ui_nodes_popup_x), math_floor(py), menuw); i32 _FILL_BUTTON_BG = g_theme->FILL_BUTTON_BG; g_theme->FILL_BUTTON_BG = false; i32 _ELEMENT_OFFSET = g_theme->ELEMENT_OFFSET; g_theme->ELEMENT_OFFSET = 0; i32 _ELEMENT_H = g_theme->ELEMENT_H; g_theme->ELEMENT_H = g_config->touch_ui ? (28 + 2) : 28; ui_menu_h = category->length * UI_ELEMENT_H(); if (is_group_category) { ui_menu_h += g_project->_->material_groups->length * UI_ELEMENT_H(); } ui_menu_begin(); for (i32 i = 0; i < category->length; ++i) { ui_node_t *n = category->buffer[i]; if (ui_menu_button(tr(n->name), "", ICON_NONE)) { ui_nodes_push_undo(NULL); ui_node_canvas_t *canvas = ui_nodes_get_canvas(true); ui_nodes_t *nodes = ui_nodes_get_nodes(); ui_node_t *node = ui_nodes_make_node(n, nodes, canvas); any_array_push(canvas->nodes, node); nodes->nodes_selected_id = i32_array_create_from_raw( (i32[]){ node->id, }, 1); nodes->nodes_drag = true; } // Next column if (g_ui->_y - ui_nodes_wy + UI_ELEMENT_H() / 2.0 > ui_nodes_wh) { g_ui->_x += menuw; g_ui->_y = py; } } if (is_group_category) { for (i32 i = 0; i < g_project->_->material_groups->length; ++i) { node_group_t *g = g_project->_->material_groups->buffer[i]; g_ui->enabled = ui_nodes_can_place_group(g->canvas->name); f32_array_t *row = f32_array_create_from_raw( (f32[]){ 5 / 6.0, 1 / 6.0, }, 2); ui_row(row); if (ui_button(string("%s%s", config_button_spacing, g->canvas->name), UI_ALIGN_LEFT, "")) { ui_nodes_push_undo(NULL); ui_node_canvas_t *canvas = ui_nodes_get_canvas(true); ui_nodes_t *nodes = ui_nodes_get_nodes(); ui_node_t *node = ui_nodes_make_group_node(g->canvas, nodes, canvas); any_array_push(canvas->nodes, node); nodes->nodes_selected_id = i32_array_create_from_raw( (i32[]){ node->id, }, 1); nodes->nodes_drag = true; } g_ui->enabled = !project_is_material_group_in_use(g); if (ui_button("x", UI_ALIGN_CENTER, "")) { history_delete_material_group(g); array_remove(g_project->_->material_groups, g); } g_ui->enabled = true; } } ui_nodes_hide_menu = g_ui->combo_selected_handle == NULL && !ui_nodes_show_menu_first && (g_ui->changed || g_ui->input_released || g_ui->input_released_r || g_ui->is_escape_down); ui_nodes_show_menu_first = false; g_theme->FILL_BUTTON_BG = _FILL_BUTTON_BG; g_theme->ELEMENT_OFFSET = _ELEMENT_OFFSET; g_theme->ELEMENT_H = _ELEMENT_H; ui_menu_end(); ui_end_region(); draw_end(); } if (ui_nodes_hide_menu) { ui_nodes_show_menu = false; ui_nodes_show_menu_first = true; } g_ui->input_enabled = true; } void ui_nodes_accept_asset_drop(i32 index) { ui_nodes_push_undo(NULL); node_group_t *g = ui_nodes_group_stack->length > 0 ? ui_nodes_group_stack->buffer[ui_nodes_group_stack->length - 1] : NULL; ui_node_t *n = ui_nodes_canvas_type == CANVAS_TYPE_MATERIAL ? nodes_material_create_node("TEX_IMAGE", g) : nodes_brush_create_node("TEX_IMAGE"); n->buttons->buffer[0]->default_value->buffer[0] = index; ui_nodes_get_nodes()->nodes_selected_id = i32_array_create_from_raw( (i32[]){ n->id, }, 1); } void ui_nodes_accept_layer_drop(i32 index) { ui_nodes_push_undo(NULL); if (slot_layer_is_group(g_project->_->layers->buffer[index])) { return; } node_group_t *g = ui_nodes_group_stack->length > 0 ? ui_nodes_group_stack->buffer[ui_nodes_group_stack->length - 1] : NULL; ui_node_t *n = nodes_material_create_node(slot_layer_is_mask(g_context->layer) ? "LAYER_MASK" : "LAYER", g); n->buttons->buffer[0]->default_value->buffer[0] = index; ui_nodes_get_nodes()->nodes_selected_id = i32_array_create_from_raw( (i32[]){ n->id, }, 1); } void ui_nodes_accept_material_drop(i32 index) { ui_nodes_push_undo(NULL); node_group_t *g = ui_nodes_group_stack->length > 0 ? ui_nodes_group_stack->buffer[ui_nodes_group_stack->length - 1] : NULL; ui_node_t *n = nodes_material_create_node("MATERIAL", g); n->buttons->buffer[0]->default_value->buffer[0] = index; ui_nodes_get_nodes()->nodes_selected_id = i32_array_create_from_raw( (i32[]){ n->id, }, 1); } void ui_nodes_accept_swatch_drop(swatch_color_t *swatch) { ui_nodes_push_undo(NULL); node_group_t *g = ui_nodes_group_stack->length > 0 ? ui_nodes_group_stack->buffer[ui_nodes_group_stack->length - 1] : NULL; ui_node_t *n = nodes_material_create_node("RGB", g); n->outputs->buffer[0]->default_value = f32_array_create_xyzw(color_get_rb(swatch->base) / 255.0, color_get_gb(swatch->base) / 255.0, color_get_bb(swatch->base) / 255.0, color_get_ab(swatch->base) / 255.0); ui_nodes_get_nodes()->nodes_selected_id = i32_array_create_from_raw( (i32[]){ n->id, }, 1); } void ui_viewnodes_init() { ui_nodes_preview_image = ui_nodes_get_node_preview_image; gc_root(ui_nodes_preview_image); ui_nodes_on_link_drag = ui_viewnodes_on_link_drag; gc_root(ui_nodes_on_link_drag); ui_nodes_on_node_remove = ui_viewnodes_on_node_remove; gc_root(ui_nodes_on_node_remove); ui_nodes_on_node_changed = ui_viewnodes_on_node_changed; gc_root(ui_nodes_on_node_changed); ui_nodes_on_socket_released = ui_viewnodes_on_socket_released; gc_root(ui_nodes_on_socket_released); ui_nodes_on_canvas_released = ui_viewnodes_on_canvas_released; gc_root(ui_nodes_on_canvas_released); ui_nodes_on_canvas_control = ui_viewnodes_on_canvas_control; gc_root(ui_nodes_on_canvas_control); ui_nodes_grid_snap = g_config->grid_snap; nodes_material_init(); }