diff --git a/demo/common/node_editor.c b/demo/common/node_editor.c index d4f34c3bb..fc3da7b05 100644 --- a/demo/common/node_editor.c +++ b/demo/common/node_editor.c @@ -1,34 +1,53 @@ -/* nuklear - v1.00 - public domain */ -/* This is a simple node editor just to show a simple implementation and that - * it is possible to achieve it with this library. While all nodes inside this - * example use a simple color modifier as content you could change them - * to have your custom content depending on the node time. - * Biggest difference to most usual implementation is that this example does - * not have connectors on the right position of the property that it links. - * This is mainly done out of laziness and could be implemented as well but - * requires calculating the position of all rows and add connectors. - * In addition adding and removing nodes is quite limited at the - * moment since it is based on a simple fixed array. If this is to be converted - * into something more serious it is probably best to extend it.*/ +/* +A basic node-based UI built with Nuklear. +Builds on the node editor example included in Nuklear v1.00, with the aim of +being used as a prototype for implementing a functioning node editor. + +Features: +- Nodes of different types. Currently their implementations are #included in + the main file, but they could easily be turned into eg. a plugin system. +- Connectors/slots of different types -- currently float values and colors. +- Adding and removing nodes. +- Linking nodes, with validation (one link per input, only link similar connectors). +- Detaching and moving links. +- Evaluation of output values of connected nodes. +- Memory management based on fixed size arrays for links and node pointers +*/ + +struct node_connector { + enum {fValue, fColor} type; + nk_bool is_connected; + struct node* connected_node; + int connected_slot; +}; + struct node { int ID; char name[32]; struct nk_rect bounds; - float value; - struct nk_color color; int input_count; int output_count; - struct node *next; - struct node *prev; + struct node_connector *inputs; + struct node_connector *outputs; + struct { + float in_top; + float in_space; + float out_top; + float out_space; + } slot_spacing; /* Maybe this should be called "node_layout" and include the bounds? */ + struct node *next; /* Z ordering only */ + struct node *prev; /* Z ordering only */ + + void* (*eval_func)(struct node*, int oIndex); + void (*display_func)(struct nk_context*, struct node*); }; struct node_link { - int input_id; + struct node* input_node; int input_slot; - int output_id; + struct node* output_node; int output_slot; - struct nk_vec2 in; - struct nk_vec2 out; + nk_bool is_active; }; struct node_linking { @@ -40,8 +59,9 @@ struct node_linking { struct node_editor { int initialized; - struct node node_buf[32]; + struct node *node_buf[32]; struct node_link links[64]; + struct node *output_node; struct node *begin; struct node *end; int node_count; @@ -54,6 +74,23 @@ struct node_editor { }; static struct node_editor nodeEditor; +/* === PROTOTYPES === */ +/* The node implementations need these two functions. */ +/* These could/should go in a header file along with the node and node_connector structs and be #included in the node implementations */ + +struct node* node_editor_add(struct node_editor *editor, size_t nodeSize, const char *name, struct nk_rect bounds, + int in_count, int out_count); +void* node_editor_eval_connected(struct node *node, int inputSlot); +/* ================== */ + +/* === NODE TYPE IMPLEMENTATIONS === */ + +#include "nodeeditor/node_type_float.c" +#include "nodeeditor/node_type_color.c" +#include "nodeeditor/node_type_blend.c" +#include "nodeeditor/node_type_output.c" +/* ================================= */ + static void node_editor_push(struct node_editor *editor, struct node *node) { @@ -98,49 +135,181 @@ node_editor_find(struct node_editor *editor, int ID) return NULL; } +static struct node_link* +node_editor_find_link_by_output(struct node_editor *editor, struct node *output_node, int node_input_connector) +{ + int i; + for (i = 0; i < editor->link_count; i++) + { + if (editor->links[i].output_node == output_node && + editor->links[i].output_slot == node_input_connector && + editor->links[i].is_active == nk_true) + { + return &editor->links[i]; + } + } + return NULL; +} + +static struct node_link* +node_editor_find_link_by_input(struct node_editor *editor, struct node *input_node, int node_output_connector) +{ + int i; + for (i = 0; i < editor->link_count; i++) + { + if (editor->links[i].input_node == input_node && + editor->links[i].input_slot == node_output_connector && + editor->links[i].is_active == nk_true) + { + return &editor->links[i]; + } + } + return NULL; +} + static void -node_editor_add(struct node_editor *editor, const char *name, struct nk_rect bounds, - struct nk_color col, int in_count, int out_count) +node_editor_delete_link(struct node_link *link) +{ + link->is_active = nk_false; + link->input_node->outputs[link->input_slot].is_connected = nk_false; + link->output_node->inputs[link->output_slot].is_connected = nk_false; +} + +struct node* +node_editor_add(struct node_editor *editor, size_t nodeSize, const char *name, struct nk_rect bounds, +int in_count, int out_count) { + int i; static int IDs = 0; - struct node *node; - NK_ASSERT((nk_size)editor->node_count < NK_LEN(editor->node_buf)); - node = &editor->node_buf[editor->node_count++]; - node->ID = IDs++; - node->value = 0; - node->color = nk_rgb(255, 0, 0); + struct node *node = NULL; + + if ((nk_size)editor->node_count < NK_LEN(editor->node_buf)) + { + /* node_buf has unused slots */ + node = malloc(nodeSize); + editor->node_buf[editor->node_count++] = node; + node->ID = IDs++; + } + else { + /* check for freed up slots in node_buf */ + for (i = 0; i < editor->node_count; i++) + { + if (editor->node_buf[i] == NULL) { + node = malloc(nodeSize); + editor->node_buf[i] = node; + node->ID = i; + break; + } + } + } + if (node == NULL) { + fprintf(stdout, "Node creation failed\n"); + return NULL; + } + + node->bounds = bounds; + node->input_count = in_count; node->output_count = out_count; - node->color = col; - node->bounds = bounds; + + node->inputs = malloc(node->input_count * sizeof(struct node_connector)); + node->outputs = malloc(node->output_count * sizeof(struct node_connector)); + + for (i = 0; i < node->input_count; i++) { + node->inputs[i].is_connected = nk_false; + node->inputs[i].type = fValue; /* default connector type */ + } + for (i = 0; i < node->output_count; i++) { + node->outputs[i].is_connected = nk_false; + node->outputs[i].type = fValue; /* default connector type */ + } + + /* default connector spacing */ + node->slot_spacing.in_top = node->slot_spacing.in_space = node->bounds.h / (float)((node->input_count) + 1); + node->slot_spacing.out_top = node->slot_spacing.out_space = node->bounds.h / (float)((node->output_count) + 1); + strcpy(node->name, name); node_editor_push(editor, node); + + return node; +} + +void * +node_editor_eval_connected(struct node* node, int inputSlot) +{ + NK_ASSERT(node->inputs[inputSlot].is_connected); + return node->inputs[inputSlot].connected_node->eval_func(node->inputs[inputSlot].connected_node, node->inputs[inputSlot].connected_slot); } static void -node_editor_link(struct node_editor *editor, int in_id, int in_slot, - int out_id, int out_slot) +node_editor_link(struct node_editor *editor, struct node *in_node, int in_slot, + struct node *out_node, int out_slot) { - struct node_link *link; - NK_ASSERT((nk_size)editor->link_count < NK_LEN(editor->links)); - link = &editor->links[editor->link_count++]; - link->input_id = in_id; - link->input_slot = in_slot; - link->output_id = out_id; - link->output_slot = out_slot; + /* Confusingly, in and out nodes/slots here refer to the inputs and outputs OF THE LINK ITSELF, not the nodes */ + struct node_link *link = NULL; + + if ((nk_size)editor->link_count < NK_LEN(editor->links)) + link = &editor->links[editor->link_count++]; + else { + int i; + for (i = 0; i < (int)NK_LEN(editor->links); i++) + { + if (editor->links[i].is_active == nk_false) { + link = &editor->links[i]; + break; + } + } + } + if (link) { + out_node->inputs[out_slot].is_connected = nk_true; + in_node->outputs[in_slot].is_connected = nk_true; + out_node->inputs[out_slot].connected_node = in_node; + out_node->inputs[out_slot].connected_slot = in_slot; + + link->input_node = in_node; + link->input_slot = in_slot; + link->output_node = out_node; + link->output_slot = out_slot; + link->is_active = nk_true; + } + else + fprintf(stdout, "Too many links\n"); } static void node_editor_init(struct node_editor *editor) { + struct nk_vec2 color_node_position = {40, 10}; + struct nk_vec2 color2_node_position = {40, 260}; + struct nk_vec2 blend_node_position = {300, 140}; + struct nk_vec2 output_node_position = {540, 154}; + struct node_type_color *color1; + struct node_type_color *color2; + struct node *blend; memset(editor, 0, sizeof(*editor)); editor->begin = NULL; editor->end = NULL; - node_editor_add(editor, "Source", nk_rect(40, 10, 180, 220), nk_rgb(255, 0, 0), 0, 1); - node_editor_add(editor, "Source", nk_rect(40, 260, 180, 220), nk_rgb(0, 255, 0), 0, 1); - node_editor_add(editor, "Combine", nk_rect(400, 100, 180, 220), nk_rgb(0,0,255), 2, 2); - node_editor_link(editor, 0, 0, 2, 0); - node_editor_link(editor, 1, 0, 2, 1); + + /* Create the nodes */ + editor->output_node = node_output_create(editor, output_node_position); + color1 = node_color_create(editor, color_node_position); + color2 = node_color_create(editor, color2_node_position); + blend = (struct node *)node_blend_create(editor, blend_node_position); + + /* Set the color values */ + color1->input_val[0] = 1.0f; + color1->input_val[1] = 0.0f; + color1->input_val[2] = 0.0f; + + color2->input_val[0] = 0.0f; + color2->input_val[1] = 0.0f; + color2->input_val[2] = 1.0f; + + /* Link the nodes */ + node_editor_link(editor, (struct node*)color1, 0, blend, 0); + node_editor_link(editor, (struct node*)color2, 0, blend, 1); + node_editor_link(editor, blend, 0, editor->output_node, 0); + editor->show_grid = nk_true; } @@ -152,7 +321,7 @@ node_editor(struct nk_context *ctx) const struct nk_input *in = &ctx->input; struct nk_command_buffer *canvas; struct node *updated = 0; - struct node_editor *nodedit = &nodeEditor; + struct node_editor *editor = &nodeEditor; if (!nodeEditor.initialized) { node_editor_init(&nodeEditor); @@ -165,82 +334,120 @@ node_editor(struct nk_context *ctx) /* allocate complete window space */ canvas = nk_window_get_canvas(ctx); total_space = nk_window_get_content_region(ctx); - nk_layout_space_begin(ctx, NK_STATIC, total_space.h, nodedit->node_count); + nk_layout_space_begin(ctx, NK_STATIC, total_space.h, editor->node_count); { - struct node *it = nodedit->begin; + struct node *it = editor->begin; struct nk_rect size = nk_layout_space_bounds(ctx); - struct nk_panel *node = 0; + struct nk_panel *nodePanel = 0; - if (nodedit->show_grid) { + if (editor->show_grid) { /* display grid */ float x, y; const float grid_size = 32.0f; const struct nk_color grid_color = nk_rgb(50, 50, 50); - for (x = (float)fmod(size.x - nodedit->scrolling.x, grid_size); x < size.w; x += grid_size) + for (x = (float)fmod(size.x - editor->scrolling.x, grid_size); x < size.w; x += grid_size) nk_stroke_line(canvas, x+size.x, size.y, x+size.x, size.y+size.h, 1.0f, grid_color); - for (y = (float)fmod(size.y - nodedit->scrolling.y, grid_size); y < size.h; y += grid_size) + for (y = (float)fmod(size.y - editor->scrolling.y, grid_size); y < size.h; y += grid_size) nk_stroke_line(canvas, size.x, y+size.y, size.x+size.w, y+size.y, 1.0f, grid_color); } /* execute each node as a movable group */ + /* (loop through nodes) */ while (it) { + /* Output node window should not have a close button */ + nk_flags nodePanel_flags = NK_WINDOW_MOVABLE|NK_WINDOW_NO_SCROLLBAR|NK_WINDOW_BORDER|NK_WINDOW_TITLE; + if (it != editor->output_node) + nodePanel_flags |= NK_WINDOW_CLOSABLE; + /* calculate scrolled node window position and size */ - nk_layout_space_push(ctx, nk_rect(it->bounds.x - nodedit->scrolling.x, - it->bounds.y - nodedit->scrolling.y, it->bounds.w, it->bounds.h)); + nk_layout_space_push(ctx, nk_rect(it->bounds.x - editor->scrolling.x, + it->bounds.y - editor->scrolling.y, it->bounds.w, it->bounds.h)); /* execute node window */ - if (nk_group_begin(ctx, it->name, NK_WINDOW_MOVABLE|NK_WINDOW_NO_SCROLLBAR|NK_WINDOW_BORDER|NK_WINDOW_TITLE)) + if (nk_group_begin(ctx, it->name, nodePanel_flags)) { /* always have last selected node on top */ - node = nk_window_get_panel(ctx); - if (nk_input_mouse_clicked(in, NK_BUTTON_LEFT, node->bounds) && + nodePanel = nk_window_get_panel(ctx); + if (nk_input_mouse_clicked(in, NK_BUTTON_LEFT, nodePanel->bounds) && (!(it->prev && nk_input_mouse_clicked(in, NK_BUTTON_LEFT, - nk_layout_space_rect_to_screen(ctx, node->bounds)))) && - nodedit->end != it) + nk_layout_space_rect_to_screen(ctx, nodePanel->bounds)))) && + editor->end != it) { updated = it; } - /* ================= NODE CONTENT =====================*/ - nk_layout_row_dynamic(ctx, 25, 1); - nk_button_color(ctx, it->color); - it->color.r = (nk_byte)nk_propertyi(ctx, "#R:", 0, it->color.r, 255, 1,1); - it->color.g = (nk_byte)nk_propertyi(ctx, "#G:", 0, it->color.g, 255, 1,1); - it->color.b = (nk_byte)nk_propertyi(ctx, "#B:", 0, it->color.b, 255, 1,1); - it->color.a = (nk_byte)nk_propertyi(ctx, "#A:", 0, it->color.a, 255, 1,1); - /* ====================================================*/ + if ((nodePanel->flags & NK_WINDOW_HIDDEN)) /* Node close button has been clicked */ + { + /* Delete node */ + struct node_link *link_remove; + node_editor_pop(editor, it); + for (n = 0; n < it->input_count; n++) + { + if ((link_remove = node_editor_find_link_by_output(editor, it, n))) + { + node_editor_delete_link(link_remove); + } + } + for (n = 0; n < it -> output_count; n++) + { + while((link_remove = node_editor_find_link_by_input(editor, it, n))) + { + node_editor_delete_link(link_remove); + } + } + NK_ASSERT(editor->node_buf[it->ID] == it); + editor->node_buf[it->ID] = NULL; + free(it->inputs); + free(it->outputs); + free(it); + } + else { + + /* ================= NODE CONTENT ===================== */ + + it->display_func(ctx, it); + + /* ==================================================== */ + + } nk_group_end(ctx); + } + if (!(nodePanel->flags & NK_WINDOW_HIDDEN)) { /* node connector and linking */ - float space; struct nk_rect bounds; - bounds = nk_layout_space_rect_to_local(ctx, node->bounds); - bounds.x += nodedit->scrolling.x; - bounds.y += nodedit->scrolling.y; + bounds = nk_layout_space_rect_to_local(ctx, nodePanel->bounds); + bounds.x += editor->scrolling.x; + bounds.y += editor->scrolling.y; it->bounds = bounds; - /* output connector */ - space = node->bounds.h / (float)((it->output_count) + 1); + /* output connectors */ for (n = 0; n < it->output_count; ++n) { struct nk_rect circle; - circle.x = node->bounds.x + node->bounds.w-4; - circle.y = node->bounds.y + space * (float)(n+1); + struct nk_color color; + circle.x = nodePanel->bounds.x + nodePanel->bounds.w-4; + circle.y = nodePanel->bounds.y + it->slot_spacing.out_top + it->slot_spacing.out_space * (float)n; circle.w = 8; circle.h = 8; - nk_fill_circle(canvas, circle, nk_rgb(100, 100, 100)); + if (it->outputs[n].type == fColor) + color = nk_rgb(200, 200, 0); + else color = nk_rgb(100, 100, 100); + nk_fill_circle(canvas, circle, color); /* start linking process */ + /* (set linking active) */ if (nk_input_has_mouse_click_down_in_rect(in, NK_BUTTON_LEFT, circle, nk_true)) { - nodedit->linking.active = nk_true; - nodedit->linking.node = it; - nodedit->linking.input_id = it->ID; - nodedit->linking.input_slot = n; + editor->linking.active = nk_true; + editor->linking.node = it; + editor->linking.input_id = it->ID; + editor->linking.input_slot = n; } /* draw curve from linked node slot to mouse position */ - if (nodedit->linking.active && nodedit->linking.node == it && - nodedit->linking.input_slot == n) { + /* (if linking active) */ + if (editor->linking.active && editor->linking.node == it && + editor->linking.input_slot == n) { struct nk_vec2 l0 = nk_vec2(circle.x + 3, circle.y + 3); struct nk_vec2 l1 = in->mouse.pos; nk_stroke_curve(canvas, l0.x, l0.y, l0.x + 50.0f, l0.y, @@ -248,20 +455,41 @@ node_editor(struct nk_context *ctx) } } - /* input connector */ - space = node->bounds.h / (float)((it->input_count) + 1); + /* input connectors */ for (n = 0; n < it->input_count; ++n) { struct nk_rect circle; - circle.x = node->bounds.x-4; - circle.y = node->bounds.y + space * (float)(n+1); + struct nk_color color; + circle.x = nodePanel->bounds.x-4; + circle.y = nodePanel->bounds.y + it->slot_spacing.in_top + it->slot_spacing.in_space * (float)n; circle.w = 8; circle.h = 8; - nk_fill_circle(canvas, circle, nk_rgb(100, 100, 100)); + if (it->inputs[n].type == fColor) + color = nk_rgb(200, 200, 0); + else color = nk_rgb(100, 100, 100); + nk_fill_circle(canvas, circle, color); + + /* Detach link */ + if (nk_input_has_mouse_click_down_in_rect(in, NK_BUTTON_LEFT, circle, nk_true) && + editor->linking.active == nk_false && + it->inputs[n].is_connected == nk_true) { + struct node_link *node_relink = node_editor_find_link_by_output(editor, it, n); + editor->linking.active = nk_true; + editor->linking.node = node_relink->input_node; + editor->linking.input_id = node_relink->input_node->ID; + editor->linking.input_slot = node_relink->input_slot; + node_editor_delete_link(node_relink); + } + + /* (Create link) */ if (nk_input_is_mouse_released(in, NK_BUTTON_LEFT) && nk_input_is_mouse_hovering_rect(in, circle) && - nodedit->linking.active && nodedit->linking.node != it) { - nodedit->linking.active = nk_false; - node_editor_link(nodedit, nodedit->linking.input_id, - nodedit->linking.input_slot, it->ID, n); + editor->linking.active && + editor->linking.node != it && + it->inputs[n].type == editor->linking.node->outputs[editor->linking.input_slot].type && + it->inputs[n].is_connected != nk_true) { + editor->linking.active = nk_false; + + node_editor_link(editor, editor->linking.node, + editor->linking.input_slot, it, n); } } } @@ -269,62 +497,65 @@ node_editor(struct nk_context *ctx) } /* reset linking connection */ - if (nodedit->linking.active && nk_input_is_mouse_released(in, NK_BUTTON_LEFT)) { - nodedit->linking.active = nk_false; - nodedit->linking.node = NULL; + if (editor->linking.active && nk_input_is_mouse_released(in, NK_BUTTON_LEFT)) { + editor->linking.active = nk_false; + editor->linking.node = NULL; fprintf(stdout, "linking failed\n"); } /* draw each link */ - for (n = 0; n < nodedit->link_count; ++n) { - struct node_link *link = &nodedit->links[n]; - struct node *ni = node_editor_find(nodedit, link->input_id); - struct node *no = node_editor_find(nodedit, link->output_id); - float spacei = node->bounds.h / (float)((ni->output_count) + 1); - float spaceo = node->bounds.h / (float)((no->input_count) + 1); - struct nk_vec2 l0 = nk_layout_space_to_screen(ctx, - nk_vec2(ni->bounds.x + ni->bounds.w, 3.0f + ni->bounds.y + spacei * (float)(link->input_slot+1))); - struct nk_vec2 l1 = nk_layout_space_to_screen(ctx, - nk_vec2(no->bounds.x, 3.0f + no->bounds.y + spaceo * (float)(link->output_slot+1))); - - l0.x -= nodedit->scrolling.x; - l0.y -= nodedit->scrolling.y; - l1.x -= nodedit->scrolling.x; - l1.y -= nodedit->scrolling.y; - nk_stroke_curve(canvas, l0.x, l0.y, l0.x + 50.0f, l0.y, - l1.x - 50.0f, l1.y, l1.x, l1.y, 1.0f, nk_rgb(100, 100, 100)); + for (n = 0; n < editor->link_count; ++n) { + struct node_link *link = &editor->links[n]; + if (link->is_active == nk_true){ + struct node *ni = link->input_node; + struct node *no = link->output_node; + struct nk_vec2 l0 = nk_layout_space_to_screen(ctx, + nk_vec2(ni->bounds.x + ni->bounds.w, 3.0f + ni->bounds.y + ni->slot_spacing.out_top + ni->slot_spacing.out_space * (float)(link->input_slot))); + struct nk_vec2 l1 = nk_layout_space_to_screen(ctx, + nk_vec2(no->bounds.x, 3.0f + no->bounds.y + no->slot_spacing.in_top + no->slot_spacing.in_space * (float)(link->output_slot))); + + l0.x -= editor->scrolling.x; + l0.y -= editor->scrolling.y; + l1.x -= editor->scrolling.x; + l1.y -= editor->scrolling.y; + nk_stroke_curve(canvas, l0.x, l0.y, l0.x + 50.0f, l0.y, + l1.x - 50.0f, l1.y, l1.x, l1.y, 1.0f, nk_rgb(100, 100, 100)); + } } if (updated) { /* reshuffle nodes to have least recently selected node on top */ - node_editor_pop(nodedit, updated); - node_editor_push(nodedit, updated); + node_editor_pop(editor, updated); + node_editor_push(editor, updated); } /* node selection */ if (nk_input_mouse_clicked(in, NK_BUTTON_LEFT, nk_layout_space_bounds(ctx))) { - it = nodedit->begin; - nodedit->selected = NULL; - nodedit->bounds = nk_rect(in->mouse.pos.x, in->mouse.pos.y, 100, 200); + it = editor->begin; + editor->selected = NULL; + editor->bounds = nk_rect(in->mouse.pos.x, in->mouse.pos.y, 100, 200); while (it) { struct nk_rect b = nk_layout_space_rect_to_screen(ctx, it->bounds); - b.x -= nodedit->scrolling.x; - b.y -= nodedit->scrolling.y; + b.x -= editor->scrolling.x; + b.y -= editor->scrolling.y; if (nk_input_is_mouse_hovering_rect(in, b)) - nodedit->selected = it; + editor->selected = it; it = it->next; } } /* contextual menu */ - if (nk_contextual_begin(ctx, 0, nk_vec2(100, 220), nk_window_get_bounds(ctx))) { + if (nk_contextual_begin(ctx, 0, nk_vec2(150, 220), nk_window_get_bounds(ctx))) { const char *grid_option[] = {"Show Grid", "Hide Grid"}; nk_layout_row_dynamic(ctx, 25, 1); - if (nk_contextual_item_label(ctx, "New", NK_TEXT_CENTERED)) - node_editor_add(nodedit, "New", nk_rect(400, 260, 180, 220), - nk_rgb(255, 255, 255), 1, 2); - if (nk_contextual_item_label(ctx, grid_option[nodedit->show_grid],NK_TEXT_CENTERED)) - nodedit->show_grid = !nodedit->show_grid; + if (nk_contextual_item_label(ctx, "Add Color node", NK_TEXT_CENTERED)) + node_color_create(editor, in->mouse.pos); + if (nk_contextual_item_label(ctx, "Add Float node", NK_TEXT_CENTERED)) + node_float_create(editor, in->mouse.pos); + if (nk_contextual_item_label(ctx, "Add Blend Node", NK_TEXT_CENTERED)) + node_blend_create(editor, in->mouse.pos); + if (nk_contextual_item_label(ctx, grid_option[editor->show_grid],NK_TEXT_CENTERED)) + editor->show_grid = !editor->show_grid; nk_contextual_end(ctx); } } @@ -333,8 +564,8 @@ node_editor(struct nk_context *ctx) /* window content scrolling */ if (nk_input_is_mouse_hovering_rect(in, nk_window_get_bounds(ctx)) && nk_input_is_mouse_down(in, NK_BUTTON_MIDDLE)) { - nodedit->scrolling.x += in->mouse.delta.x; - nodedit->scrolling.y += in->mouse.delta.y; + editor->scrolling.x += in->mouse.delta.x; + editor->scrolling.y += in->mouse.delta.y; } } nk_end(ctx); diff --git a/demo/common/nodeeditor/node_type_blend.c b/demo/common/nodeeditor/node_type_blend.c new file mode 100644 index 000000000..48d39a13a --- /dev/null +++ b/demo/common/nodeeditor/node_type_blend.c @@ -0,0 +1,75 @@ +struct node_type_blend { + struct node node; + struct nk_colorf input_val[2]; + struct nk_colorf output_val; + float blend_val; +}; + +static struct nk_colorf *node_blend_eval(struct node *node, int oIndex) { + struct node_type_blend* blend_node = (struct node_type_blend*)node; + NK_UNUSED(oIndex); + return &blend_node->output_val; +} + +static void node_blend_display(struct nk_context *ctx, struct node *node) { + struct node_type_blend *blend_node = (struct node_type_blend*)node; + const struct nk_colorf blank = {0.0f, 0.0f, 0.0f, 0.0f}; + float blend_amnt; + int i; + + nk_layout_row_dynamic(ctx, 25, 1); + for (i = 0; i < 2; i++){ + if(node->inputs[i].is_connected) { + blend_node->input_val[i] = *(struct nk_colorf*)node_editor_eval_connected(node, i); + } + else { + blend_node->input_val[i] = blank; + } + nk_button_color(ctx, nk_rgba_cf(blend_node->input_val[i])); + } + + if (node->inputs[2].is_connected) { + blend_amnt = *(float*)node_editor_eval_connected(node, 2); + blend_amnt = nk_propertyf(ctx, "#Blend", blend_amnt, blend_amnt, blend_amnt, 0.01f, 0.01f); + } + else { + blend_node->blend_val = nk_propertyf(ctx, "#Blend", 0.0f, blend_node->blend_val, 1.0f, 0.01f, 0.01f); + blend_amnt = blend_node->blend_val; + } + + + if(node->inputs[0].is_connected && node->inputs[1].is_connected) { + blend_node->output_val.r = blend_node->input_val[0].r * (1.0f-blend_amnt) + blend_node->input_val[1].r * blend_amnt; + blend_node->output_val.g = blend_node->input_val[0].g * (1.0f-blend_amnt) + blend_node->input_val[1].g * blend_amnt; + blend_node->output_val.b = blend_node->input_val[0].b * (1.0f-blend_amnt) + blend_node->input_val[1].b * blend_amnt; + blend_node->output_val.a = blend_node->input_val[0].a * (1.0f-blend_amnt) + blend_node->input_val[1].a * blend_amnt; + } + else { + blend_node->output_val = blank; + } +} + +struct node_type_blend* node_blend_create(struct node_editor *editor, struct nk_vec2 position) { + struct node_type_blend* blend_node = (struct node_type_blend*)node_editor_add(editor, sizeof(struct node_type_blend), "Blend", nk_rect(position.x, position.y, 180, 130), 3, 1); + if (blend_node) { + const struct nk_colorf blank = {0.0f, 0.0f, 0.0f, 0.0f}; + int i; + for (i = 0; i < (int)NK_LEN(blend_node->input_val); i++) + blend_node->node.inputs[i].type = fColor; + blend_node->node.outputs[0].type = fColor; + + blend_node->node.slot_spacing.in_top = 42.0f; + blend_node->node.slot_spacing.in_space = 29.0f; + + for (i = 0; i < (int)NK_LEN(blend_node->input_val); i++) + blend_node->input_val[i] = blank; + blend_node->output_val = blank; + + blend_node->blend_val = 0.5f; + + blend_node->node.display_func = node_blend_display; + blend_node->node.eval_func = (void*(*)(struct node*, int)) node_blend_eval; + } + + return blend_node; +} diff --git a/demo/common/nodeeditor/node_type_color.c b/demo/common/nodeeditor/node_type_color.c new file mode 100644 index 000000000..4daa1723c --- /dev/null +++ b/demo/common/nodeeditor/node_type_color.c @@ -0,0 +1,73 @@ +struct node_type_color { + struct node node; + float input_val[4]; + struct nk_colorf output_val; +}; + +static struct nk_colorf *node_color_eval(struct node* node, int oIndex) +{ + struct node_type_color *color_node = (struct node_type_color*)node; + NK_ASSERT(oIndex == 0); /* only one output connector */ + + return &color_node->output_val; +} + + +static void node_color_draw(struct nk_context *ctx, struct node *node) +{ + struct node_type_color *color_node = (struct node_type_color*)node; + float eval_result; /* Get the values from connected nodes into this so the inputs revert on disconnect */ + const char* labels[4] = {"#R:","#G:","#B:","#A:"}; + float color_val[4]; /* Because we can't just loop through the struct... */ + int i; + nk_layout_row_dynamic(ctx, 25, 1); + nk_button_color(ctx, nk_rgba_cf(color_node->output_val)); + + for (i = 0; i < 4; i++) + { + if (color_node->node.inputs[i].is_connected) { + eval_result = *(float*)node_editor_eval_connected(node, i); + eval_result = nk_propertyf(ctx, labels[i], eval_result, eval_result, eval_result, 0.01f, 0.01f); + color_val[i] = eval_result; + } + else { + color_node->input_val[i] = nk_propertyf(ctx, labels[i], 0.0f, color_node->input_val[i], 1.0f, 0.01f, 0.01f); + color_val[i] = color_node->input_val[i]; + } + } + + color_node->output_val.r = color_val[0]; + color_node->output_val.g = color_val[1]; + color_node->output_val.b = color_val[2]; + color_node->output_val.a = color_val[3]; +} + +struct node_type_color * node_color_create(struct node_editor *editor, struct nk_vec2 position) +{ + struct node_type_color *color_node = (struct node_type_color*)node_editor_add(editor, sizeof(struct node_type_color), "Color", nk_rect(position.x, position.y, 180, 190), 4, 1); + if (color_node) + { + int i; + const struct nk_colorf white = {1.0f, 1.0f, 1.0f, 1.0f}; + + color_node->node.slot_spacing.in_top = 72.0f; + color_node->node.slot_spacing.in_space = 29.0f; + color_node->node.slot_spacing.out_top = 42.0f; + color_node->node.slot_spacing.out_space = 0.0f; + + for (i = 0; i < color_node->node.input_count; i++) + color_node->node.inputs[i].type = fValue; + color_node->node.outputs[0].type = fColor; + + color_node->input_val[0] = + color_node->input_val[1] = + color_node->input_val[2] = + color_node->input_val[3] = 1.0f; + + color_node->output_val = white; + + color_node->node.display_func = node_color_draw; + color_node->node.eval_func = (void*(*)(struct node*, int)) node_color_eval; + } + return color_node; +} diff --git a/demo/common/nodeeditor/node_type_float.c b/demo/common/nodeeditor/node_type_float.c new file mode 100644 index 000000000..c3087e935 --- /dev/null +++ b/demo/common/nodeeditor/node_type_float.c @@ -0,0 +1,27 @@ +struct node_type_float { + struct node node; + float output_val; +}; + +static float *node_float_eval(struct node* node, int oIndex) { + struct node_type_float *float_node = (struct node_type_float*)node; + NK_ASSERT(oIndex == 0); + return &float_node->output_val; +} + +static void node_float_draw(struct nk_context *ctx, struct node *node) { + struct node_type_float *float_node = (struct node_type_float*)node; + nk_layout_row_dynamic(ctx, 25, 1); + float_node->output_val = nk_propertyf(ctx, "#Value:", 0.0f, float_node->output_val, 1.0f, 0.01f, 0.01f); +} + +struct node_type_float *node_float_create(struct node_editor *editor, struct nk_vec2 position) { + struct node_type_float *float_node = (struct node_type_float*)node_editor_add(editor, sizeof(struct node_type_float), "Float", nk_rect(position.x, position.y, 180, 75), 0, 1); + if (float_node) + { + float_node->output_val = 1.0f; + float_node->node.display_func = node_float_draw; + float_node->node.eval_func = (void*(*)(struct node*, int)) node_float_eval; + } + return float_node; +} diff --git a/demo/common/nodeeditor/node_type_output.c b/demo/common/nodeeditor/node_type_output.c new file mode 100644 index 000000000..aa135fa0a --- /dev/null +++ b/demo/common/nodeeditor/node_type_output.c @@ -0,0 +1,31 @@ +struct node_type_output { + struct node node; + struct nk_colorf input_val; +}; + +struct nk_colorf *node_output_get(struct node* node) { + struct node_type_output *output_node = (struct node_type_output*)node; + if (!node->inputs[0].is_connected) { + struct nk_colorf black = {0.0f, 0.0f, 0.0f, 0.0f}; + output_node->input_val = black; + } + return &output_node->input_val; +} + +static void node_output_display(struct nk_context *ctx, struct node *node) { + if (node->inputs[0].is_connected) { + struct node_type_output *output_node = (struct node_type_output*)node; + output_node->input_val = *(struct nk_colorf*)node_editor_eval_connected(node, 0); + nk_layout_row_dynamic(ctx, 60, 1); + nk_button_color(ctx, nk_rgba_cf(output_node->input_val)); + } +} + +struct node* node_output_create(struct node_editor *editor, struct nk_vec2 position) { + struct node_type_output *output_node = (struct node_type_output*)node_editor_add(editor, sizeof(struct node_type_output), "Output", nk_rect(position.x, position.y, 100, 100), 1, 0); + if (output_node){ + output_node->node.inputs[0].type = fColor; + output_node->node.display_func = node_output_display; + } + return (struct node*)output_node; +}