Node refactor (#1406)
This commit is contained in:
parent
75adea2d01
commit
c39de5a66a
11 changed files with 543 additions and 3641 deletions
1
external/imgui/imgui_unity.cpp
vendored
1
external/imgui/imgui_unity.cpp
vendored
|
@ -6,4 +6,3 @@
|
|||
#include "imgui_draw.cpp"
|
||||
#include "imgui_widgets.cpp"
|
||||
#include "imgui_freetype.cpp"
|
||||
#include "imnodes.cpp"
|
||||
|
|
15
external/imgui/imgui_user.h
vendored
15
external/imgui/imgui_user.h
vendored
|
@ -24,6 +24,11 @@ enum class CurveEditorFlags
|
|||
RESET = 1 << 2
|
||||
};
|
||||
|
||||
enum class PinShape {
|
||||
CIRCLE,
|
||||
TRIANGLE
|
||||
};
|
||||
|
||||
IMGUI_API int CurveEditor(const char* label
|
||||
, float* values
|
||||
, int points_count
|
||||
|
@ -37,6 +42,16 @@ IMGUI_API void HSplitter(const char* str_id, ImVec2* size);
|
|||
IMGUI_API void VSplitter(const char* str_id, ImVec2* size);
|
||||
IMGUI_API void Rect(float w, float h, ImU32 color);
|
||||
|
||||
IMGUI_API void BeginNodeEditor(const char* title, ImVec2* offset);
|
||||
IMGUI_API void EndNodeEditor();
|
||||
IMGUI_API ImVec2 GetNodeEditorOffset();
|
||||
IMGUI_API void BeginNode(ImGuiID id, ImVec2& screen_pos);
|
||||
IMGUI_API void EndNode();
|
||||
IMGUI_API void Pin(ImGuiID id, bool is_input, PinShape shape = PinShape::CIRCLE);
|
||||
IMGUI_API bool GetNewLink(ImGuiID* from, ImGuiID* to);
|
||||
IMGUI_API void NodeLink(ImGuiID from, ImGuiID to);
|
||||
IMGUI_API bool IsLinkHovered();
|
||||
|
||||
IMGUI_API bool InputRotation(const char* label, float* euler);
|
||||
IMGUI_API void Label(const char* label);
|
||||
IMGUI_API void TextClipped(const char* text, float size);
|
||||
|
|
186
external/imgui/imgui_user.inl
vendored
186
external/imgui/imgui_user.inl
vendored
|
@ -16,9 +16,193 @@ ImVec2::operator Lumix::Vec2() const {
|
|||
return {x, y};
|
||||
}
|
||||
|
||||
|
||||
namespace ImGuiEx {
|
||||
|
||||
constexpr float NODE_PIN_RADIUS = 5.f;
|
||||
struct NodeEditorState {
|
||||
ImVec2* node_pos;
|
||||
float node_w = 120;
|
||||
ImGuiID last_node_id;
|
||||
ImVec2 node_editor_pos;
|
||||
ImGuiID new_link_from = 0;
|
||||
ImGuiID new_link_to = 0;
|
||||
bool new_link_from_input;
|
||||
bool link_hovered = false;
|
||||
ImDrawList* draw_list = nullptr;
|
||||
bool is_pin_hovered = false;
|
||||
ImVec2* canvas_offset = nullptr;
|
||||
} g_node_editor;
|
||||
|
||||
ImVec2 GetNodeEditorOffset() {
|
||||
return *g_node_editor.canvas_offset;
|
||||
}
|
||||
|
||||
void BeginNodeEditor(const char* title, ImVec2* offset) {
|
||||
g_node_editor.canvas_offset = offset;
|
||||
BeginChild(title, ImVec2(0, 0), false, ImGuiWindowFlags_NoScrollbar | ImGuiWindowFlags_NoScrollWithMouse);
|
||||
g_node_editor.node_editor_pos = GetCursorScreenPos() + *g_node_editor.canvas_offset;
|
||||
g_node_editor.link_hovered = false;
|
||||
g_node_editor.draw_list = GetWindowDrawList();
|
||||
g_node_editor.draw_list->ChannelsSplit(2);
|
||||
}
|
||||
|
||||
void EndNodeEditor() {
|
||||
g_node_editor.draw_list->ChannelsMerge();
|
||||
|
||||
if (g_node_editor.new_link_from != 0) {
|
||||
ImGuiStorage* storage = GetStateStorage();
|
||||
PushID(g_node_editor.new_link_from);
|
||||
const ImVec2 from(storage->GetFloat(GetID("pin-x"), 0), storage->GetFloat(GetID("pin-y"), 0));
|
||||
PopID();
|
||||
ImDrawList* dl = g_node_editor.draw_list;
|
||||
const ImVec2 to = GetMousePos();
|
||||
if (g_node_editor.new_link_from_input) {
|
||||
dl->AddBezierCubic(from, from - ImVec2(20, 0), to + ImVec2(20, 0), to, GetColorU32(ImGuiCol_Tab), 3.f);
|
||||
}
|
||||
else {
|
||||
dl->AddBezierCubic(from, from + ImVec2(20, 0), to - ImVec2(20, 0), to, GetColorU32(ImGuiCol_Tab), 3.f);
|
||||
}
|
||||
}
|
||||
EndChild();
|
||||
if (IsMouseReleased(0)) {
|
||||
g_node_editor.new_link_from = 0;
|
||||
g_node_editor.new_link_to = 0;
|
||||
}
|
||||
|
||||
if (IsMouseDragging(ImGuiMouseButton_Middle) && IsItemHovered()) {
|
||||
const ImVec2 delta = GetIO().MouseDelta;
|
||||
*g_node_editor.canvas_offset += delta;
|
||||
}
|
||||
}
|
||||
|
||||
bool GetNewLink(ImGuiID* from, ImGuiID* to) {
|
||||
if (g_node_editor.new_link_to) {
|
||||
*from = g_node_editor.new_link_from;
|
||||
*to = g_node_editor.new_link_to;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void Pin(ImGuiID id, bool is_input, PinShape shape) {
|
||||
PopID(); // pop node id, we want pin id to not include node id
|
||||
ImDrawList* draw_list = GetWindowDrawList();
|
||||
ImVec2 screen_pos = ImGui::GetCursorScreenPos();
|
||||
|
||||
const ImVec2 center = [&](){
|
||||
if (is_input) return screen_pos + ImVec2(-GetStyle().WindowPadding.x, GetTextLineHeightWithSpacing() * 0.5f);
|
||||
return ImVec2(g_node_editor.node_pos->x + g_node_editor.node_w + 2 * GetStyle().WindowPadding.x, screen_pos.y + GetTextLineHeightWithSpacing() * 0.5f);
|
||||
}();
|
||||
const ImVec2 half_extents(NODE_PIN_RADIUS, NODE_PIN_RADIUS);
|
||||
ItemAdd(ImRect(center - half_extents, center + half_extents), id);
|
||||
const bool hovered = IsItemHovered();
|
||||
ImGuiStyle& style = ImGui::GetStyle();
|
||||
const ImU32 color = GetColorU32(hovered ? ImGuiCol_TabHovered : ImGuiCol_Tab);
|
||||
switch(shape) {
|
||||
case PinShape::TRIANGLE:
|
||||
draw_list->AddTriangleFilled(center - ImVec2(NODE_PIN_RADIUS, -NODE_PIN_RADIUS), center - half_extents, center + ImVec2(NODE_PIN_RADIUS, 0), GetColorU32(ImGuiCol_Text));
|
||||
break;
|
||||
default:
|
||||
draw_list->AddCircleFilled(center, NODE_PIN_RADIUS, color);
|
||||
break;
|
||||
}
|
||||
|
||||
g_node_editor.is_pin_hovered = g_node_editor.is_pin_hovered || hovered;
|
||||
|
||||
ImGuiStorage* storage = GetStateStorage();
|
||||
PushID(id);
|
||||
storage->SetFloat(GetID("pin-x"), center.x);
|
||||
storage->SetFloat(GetID("pin-y"), center.y);
|
||||
PopID();
|
||||
|
||||
if (hovered && ImGui::IsMouseClicked(0)) {
|
||||
g_node_editor.new_link_from = id;
|
||||
g_node_editor.new_link_from_input = is_input;
|
||||
}
|
||||
|
||||
if (hovered && ImGui::IsMouseReleased(0) && g_node_editor.new_link_from != 0) {
|
||||
g_node_editor.new_link_to = id;
|
||||
if (!is_input) {
|
||||
ImSwap(g_node_editor.new_link_to, g_node_editor.new_link_from);
|
||||
}
|
||||
}
|
||||
PushID(g_node_editor.last_node_id);
|
||||
}
|
||||
|
||||
bool IsLinkHovered() {
|
||||
return g_node_editor.link_hovered;
|
||||
}
|
||||
|
||||
void NodeLink(ImGuiID from_id, ImGuiID to_id) {
|
||||
ImGuiStorage* storage = GetStateStorage();
|
||||
PushID(from_id);
|
||||
const ImVec2 from(storage->GetFloat(GetID("pin-x"), 0), storage->GetFloat(GetID("pin-y"), 0));
|
||||
PopID();
|
||||
|
||||
PushID(to_id);
|
||||
const ImVec2 to(storage->GetFloat(GetID("pin-x"), 0), storage->GetFloat(GetID("pin-y"), 0));
|
||||
PopID();
|
||||
|
||||
ImVec2 p1 = from;
|
||||
float d = ImMax(20.f, ImAbs(from.x - to.x)) * 0.75f;
|
||||
ImVec2 t1 = ImVec2(d, 0.0f);
|
||||
ImVec2 p2 = to;
|
||||
ImVec2 t2 = ImVec2(d, 0.0f);
|
||||
const int STEPS = 12;
|
||||
ImDrawList* draw_list = GetWindowDrawList();
|
||||
const ImGuiStyle& style = ImGui::GetStyle();
|
||||
const ImVec2 closest_point = ImBezierCubicClosestPointCasteljau(p1, p1 + t1, p2 - t2, p2, ImGui::GetMousePos(), style.CurveTessellationTol);
|
||||
const float dist_squared = ImFabs(ImLengthSqr(ImGui::GetMousePos() - closest_point));
|
||||
g_node_editor.link_hovered = dist_squared < 3 * 3 + 1;
|
||||
|
||||
draw_list->AddBezierCubic(p1, p1 + t1, p2 - t2, p2, GetColorU32(g_node_editor.link_hovered ? ImGuiCol_TabActive : ImGuiCol_Tab), 3.f);
|
||||
}
|
||||
|
||||
void BeginNode(ImGuiID id, ImVec2& pos) {
|
||||
g_node_editor.last_node_id = id;
|
||||
pos += g_node_editor.node_editor_pos;
|
||||
g_node_editor.node_pos = &pos;
|
||||
SetCursorScreenPos(pos + GetStyle().WindowPadding);
|
||||
g_node_editor.draw_list->ChannelsSetCurrent(1);
|
||||
BeginGroup();
|
||||
PushID(id);
|
||||
g_node_editor.node_w = GetStateStorage()->GetFloat(GetID("node-width"), 120);
|
||||
PushItemWidth(80);
|
||||
g_node_editor.is_pin_hovered = false;
|
||||
}
|
||||
|
||||
void EndNode()
|
||||
{
|
||||
PopItemWidth();
|
||||
EndGroup();
|
||||
const ImGuiStyle& style = GetStyle();
|
||||
const ImRect rect(GetItemRectMin() - style.WindowPadding, GetItemRectMax() + style.WindowPadding);
|
||||
const ImVec2 size = rect.GetSize();
|
||||
|
||||
GetStateStorage()->SetFloat(GetID("node-width"), size.x - style.WindowPadding.x * 2);
|
||||
|
||||
const ImGuiID dragger_id = GetID("##_node_dragger");
|
||||
ItemAdd(rect, dragger_id);
|
||||
const bool is_hovered = IsItemHovered();
|
||||
if (is_hovered && IsMouseClicked(0) && !g_node_editor.is_pin_hovered) {
|
||||
SetActiveID(dragger_id, GetCurrentWindow());
|
||||
}
|
||||
if (IsItemActive() && IsMouseReleased(0)) {
|
||||
ResetActiveID();
|
||||
}
|
||||
if (IsItemActive() && IsMouseDragging(0)) {
|
||||
*g_node_editor.node_pos += GetIO().MouseDelta;
|
||||
}
|
||||
|
||||
g_node_editor.draw_list->ChannelsSetCurrent(0);
|
||||
ImVec2 np = *g_node_editor.node_pos;
|
||||
g_node_editor.draw_list->AddRectFilled(np, np + size, ImColor(style.Colors[ImGuiCol_WindowBg]), 4.0f);
|
||||
g_node_editor.draw_list->AddRect(np, np + size, GetColorU32(is_hovered ? ImGuiCol_ButtonHovered : ImGuiCol_Border), 4.0f);
|
||||
|
||||
PopID();
|
||||
*g_node_editor.node_pos -= g_node_editor.node_editor_pos;
|
||||
}
|
||||
|
||||
bool ToolbarButton(ImFont* font, const char* font_icon, const ImVec4& bg_color, const char* tooltip)
|
||||
{
|
||||
auto frame_padding = GetStyle().FramePadding;
|
||||
|
|
3089
external/imgui/imnodes.cpp
vendored
3089
external/imgui/imnodes.cpp
vendored
File diff suppressed because it is too large
Load diff
335
external/imgui/imnodes.h
vendored
335
external/imgui/imnodes.h
vendored
|
@ -1,335 +0,0 @@
|
|||
#pragma once
|
||||
|
||||
#include <stddef.h>
|
||||
|
||||
struct ImGuiContext;
|
||||
struct ImVec2;
|
||||
|
||||
namespace imnodes
|
||||
{
|
||||
enum ColorStyle
|
||||
{
|
||||
ColorStyle_NodeBackground = 0,
|
||||
ColorStyle_NodeBackgroundHovered,
|
||||
ColorStyle_NodeBackgroundSelected,
|
||||
ColorStyle_NodeOutline,
|
||||
ColorStyle_TitleBar,
|
||||
ColorStyle_TitleBarHovered,
|
||||
ColorStyle_TitleBarSelected,
|
||||
ColorStyle_Link,
|
||||
ColorStyle_LinkHovered,
|
||||
ColorStyle_LinkSelected,
|
||||
ColorStyle_Pin,
|
||||
ColorStyle_PinHovered,
|
||||
ColorStyle_BoxSelector,
|
||||
ColorStyle_BoxSelectorOutline,
|
||||
ColorStyle_GridBackground,
|
||||
ColorStyle_GridLine,
|
||||
ColorStyle_Count
|
||||
};
|
||||
|
||||
enum StyleVar
|
||||
{
|
||||
StyleVar_GridSpacing = 0,
|
||||
StyleVar_NodeCornerRounding,
|
||||
StyleVar_NodePaddingHorizontal,
|
||||
StyleVar_NodePaddingVertical,
|
||||
StyleVar_NodeBorderThickness,
|
||||
StyleVar_LinkThickness,
|
||||
StyleVar_LinkLineSegmentsPerLength,
|
||||
StyleVar_LinkHoverDistance,
|
||||
StyleVar_PinCircleRadius,
|
||||
StyleVar_PinQuadSideLength,
|
||||
StyleVar_PinTriangleSideLength,
|
||||
StyleVar_PinLineThickness,
|
||||
StyleVar_PinHoverRadius,
|
||||
StyleVar_PinOffset
|
||||
};
|
||||
|
||||
enum StyleFlags
|
||||
{
|
||||
StyleFlags_None = 0,
|
||||
StyleFlags_NodeOutline = 1 << 0,
|
||||
StyleFlags_GridLines = 1 << 2
|
||||
};
|
||||
|
||||
// This enum controls the way attribute pins look.
|
||||
enum PinShape
|
||||
{
|
||||
PinShape_Circle,
|
||||
PinShape_CircleFilled,
|
||||
PinShape_Triangle,
|
||||
PinShape_TriangleFilled,
|
||||
PinShape_Quad,
|
||||
PinShape_QuadFilled
|
||||
};
|
||||
|
||||
// This enum controls the way the attribute pins behave.
|
||||
enum AttributeFlags
|
||||
{
|
||||
AttributeFlags_None = 0,
|
||||
// Allow detaching a link by left-clicking and dragging the link at a pin it is connected to.
|
||||
// NOTE: the user has to actually delete the link for this to work. A deleted link can be
|
||||
// detected by calling IsLinkDestroyed() after EndNodeEditor().
|
||||
AttributeFlags_EnableLinkDetachWithDragClick = 1 << 0,
|
||||
// Visual snapping of an in progress link will trigger IsLink Created/Destroyed events. Allows
|
||||
// for previewing the creation of a link while dragging it across attributes. See here for demo:
|
||||
// https://github.com/Nelarius/imnodes/issues/41#issuecomment-647132113 NOTE: the user has to
|
||||
// actually delete the link for this to work. A deleted link can be detected by calling
|
||||
// IsLinkDestroyed() after EndNodeEditor().
|
||||
AttributeFlags_EnableLinkCreationOnSnap = 1 << 1
|
||||
};
|
||||
|
||||
struct IO
|
||||
{
|
||||
struct EmulateThreeButtonMouse
|
||||
{
|
||||
EmulateThreeButtonMouse();
|
||||
|
||||
// The keyboard modifier to use in combination with mouse left click to pan the editor view.
|
||||
// Set to NULL by default. To enable this feature, set the modifier to point to a boolean
|
||||
// indicating the state of a modifier. For example,
|
||||
//
|
||||
// imnodes::GetIO().emulate_three_button_mouse.modifier = &ImGui::GetIO().KeyAlt;
|
||||
const bool* modifier;
|
||||
} emulate_three_button_mouse;
|
||||
|
||||
struct LinkDetachWithModifierClick
|
||||
{
|
||||
LinkDetachWithModifierClick();
|
||||
|
||||
// Pointer to a boolean value indicating when the desired modifier is pressed. Set to NULL
|
||||
// by default. To enable the feature, set the modifier to point to a boolean indicating the
|
||||
// state of a modifier. For example,
|
||||
//
|
||||
// imnodes::GetIO().link_detach_with_modifier_click.modifier = &ImGui::GetIO().KeyCtrl;
|
||||
//
|
||||
// Left-clicking a link with this modifier pressed will detach that link. NOTE: the user has
|
||||
// to actually delete the link for this to work. A deleted link can be detected by calling
|
||||
// IsLinkDestroyed() after EndNodeEditor().
|
||||
const bool* modifier;
|
||||
} link_detach_with_modifier_click;
|
||||
|
||||
IO();
|
||||
};
|
||||
|
||||
struct Style
|
||||
{
|
||||
float grid_spacing;
|
||||
|
||||
float node_corner_rounding;
|
||||
float node_padding_horizontal;
|
||||
float node_padding_vertical;
|
||||
float node_border_thickness;
|
||||
|
||||
float link_thickness;
|
||||
float link_line_segments_per_length;
|
||||
float link_hover_distance;
|
||||
|
||||
// The following variables control the look and behavior of the pins. The default size of each
|
||||
// pin shape is balanced to occupy approximately the same surface area on the screen.
|
||||
|
||||
// The circle radius used when the pin shape is either PinShape_Circle or PinShape_CircleFilled.
|
||||
float pin_circle_radius;
|
||||
// The quad side length used when the shape is either PinShape_Quad or PinShape_QuadFilled.
|
||||
float pin_quad_side_length;
|
||||
// The equilateral triangle side length used when the pin shape is either PinShape_Triangle or
|
||||
// PinShape_TriangleFilled.
|
||||
float pin_triangle_side_length;
|
||||
// The thickness of the line used when the pin shape is not filled.
|
||||
float pin_line_thickness;
|
||||
// The radius from the pin's center position inside of which it is detected as being hovered
|
||||
// over.
|
||||
float pin_hover_radius;
|
||||
// Offsets the pins' positions from the edge of the node to the outside of the node.
|
||||
float pin_offset;
|
||||
|
||||
// By default, StyleFlags_NodeOutline and StyleFlags_Gridlines are enabled.
|
||||
StyleFlags flags;
|
||||
// Set these mid-frame using Push/PopColorStyle. You can index this color array with with a
|
||||
// ColorStyle enum value.
|
||||
unsigned int colors[ColorStyle_Count];
|
||||
|
||||
Style();
|
||||
};
|
||||
|
||||
struct Context;
|
||||
|
||||
// Call this function if you are compiling imnodes in to a dll, separate from ImGui. Calling this
|
||||
// function sets the GImGui global variable, which is not shared across dll boundaries.
|
||||
void SetImGuiContext(ImGuiContext* ctx);
|
||||
|
||||
Context* CreateContext();
|
||||
void DestroyContext(Context* ctx = NULL); // NULL = destroy current context
|
||||
Context* GetCurrentContext();
|
||||
void SetCurrentContext(Context* ctx);
|
||||
|
||||
// An editor context corresponds to a set of nodes in a single workspace (created with a single
|
||||
// Begin/EndNodeEditor pair)
|
||||
//
|
||||
// By default, the library creates an editor context behind the scenes, so using any of the imnodes
|
||||
// functions doesn't require you to explicitly create a context.
|
||||
struct EditorContext;
|
||||
|
||||
EditorContext* EditorContextCreate();
|
||||
void EditorContextFree(EditorContext*);
|
||||
void EditorContextSet(EditorContext*);
|
||||
ImVec2 EditorContextGetPanning();
|
||||
void EditorContextResetPanning(const ImVec2& pos);
|
||||
void EditorContextMoveToNode(const int node_id);
|
||||
|
||||
IO& GetIO();
|
||||
|
||||
// Returns the global style struct. See the struct declaration for default values.
|
||||
Style& GetStyle();
|
||||
// Style presets matching the dear imgui styles of the same name.
|
||||
void StyleColorsDark(); // on by default
|
||||
void StyleColorsClassic();
|
||||
void StyleColorsLight();
|
||||
|
||||
// The top-level function call. Call this before calling BeginNode/EndNode. Calling this function
|
||||
// will result the node editor grid workspace being rendered.
|
||||
void BeginNodeEditor();
|
||||
void EndNodeEditor();
|
||||
|
||||
// Use PushColorStyle and PopColorStyle to modify Style::colors mid-frame.
|
||||
void PushColorStyle(ColorStyle item, unsigned int color);
|
||||
void PopColorStyle();
|
||||
void PushStyleVar(StyleVar style_item, float value);
|
||||
void PopStyleVar();
|
||||
|
||||
// id can be any positive or negative integer, but INT_MIN is currently reserved for internal use.
|
||||
void BeginNode(int id);
|
||||
void EndNode();
|
||||
|
||||
ImVec2 GetNodeDimensions(int id);
|
||||
|
||||
// Place your node title bar content (such as the node title, using ImGui::Text) between the
|
||||
// following function calls. These functions have to be called before adding any attributes, or the
|
||||
// layout of the node will be incorrect.
|
||||
void BeginNodeTitleBar();
|
||||
void EndNodeTitleBar();
|
||||
|
||||
// Attributes are ImGui UI elements embedded within the node. Attributes can have pin shapes
|
||||
// rendered next to them. Links are created between pins.
|
||||
//
|
||||
// The activity status of an attribute can be checked via the IsAttributeActive() and
|
||||
// IsAnyAttributeActive() function calls. This is one easy way of checking for any changes made to
|
||||
// an attribute's drag float UI, for instance.
|
||||
//
|
||||
// Each attribute id must be unique.
|
||||
|
||||
// Create an input attribute block. The pin is rendered on left side.
|
||||
void BeginInputAttribute(int id, PinShape shape = PinShape_CircleFilled);
|
||||
void EndInputAttribute();
|
||||
// Create an output attribute block. The pin is rendered on the right side.
|
||||
void BeginOutputAttribute(int id, PinShape shape = PinShape_CircleFilled);
|
||||
void EndOutputAttribute();
|
||||
// Create a static attribute block. A static attribute has no pin, and therefore can't be linked to
|
||||
// anything. However, you can still use IsAttributeActive() and IsAnyAttributeActive() to check for
|
||||
// attribute activity.
|
||||
void BeginStaticAttribute(int id);
|
||||
void EndStaticAttribute();
|
||||
|
||||
// Push a single AttributeFlags value. By default, only AttributeFlags_None is set.
|
||||
void PushAttributeFlag(AttributeFlags flag);
|
||||
void PopAttributeFlag();
|
||||
|
||||
// Render a link between attributes.
|
||||
// The attributes ids used here must match the ids used in Begin(Input|Output)Attribute function
|
||||
// calls. The order of start_attr and end_attr doesn't make a difference for rendering the link.
|
||||
void Link(int id, int start_attribute_id, int end_attribute_id);
|
||||
|
||||
// Enable or disable the ability to click and drag a specific node.
|
||||
void SetNodeDraggable(int node_id, const bool draggable);
|
||||
|
||||
// The node's position can be expressed in three coordinate systems:
|
||||
// * screen space coordinates, -- the origin is the upper left corner of the window.
|
||||
// * editor space coordinates -- the origin is the upper left corner of the node editor window
|
||||
// * grid space coordinates, -- the origin is the upper left corner of the node editor window,
|
||||
// translated by the current editor panning vector (see EditorContextGetPanning() and
|
||||
// EditorContextResetPanning())
|
||||
|
||||
// Use the following functions to get and set the node's coordinates in these coordinate systems.
|
||||
|
||||
void SetNodeScreenSpacePos(int node_id, const ImVec2& screen_space_pos);
|
||||
void SetNodeEditorSpacePos(int node_id, const ImVec2& editor_space_pos);
|
||||
void SetNodeGridSpacePos(int node_id, const ImVec2& grid_pos);
|
||||
|
||||
ImVec2 GetNodeScreenSpacePos(const int node_id);
|
||||
ImVec2 GetNodeEditorSpacePos(const int node_id);
|
||||
ImVec2 GetNodeGridSpacePos(const int node_id);
|
||||
|
||||
// Returns true if the current node editor canvas is being hovered over by the mouse, and is not
|
||||
// blocked by any other windows.
|
||||
bool IsEditorHovered();
|
||||
// The following functions return true if a UI element is being hovered over by the mouse cursor.
|
||||
// Assigns the id of the UI element being hovered over to the function argument. Use these functions
|
||||
// after EndNodeEditor() has been called.
|
||||
bool IsNodeHovered(int* node_id);
|
||||
bool IsLinkHovered(int* link_id);
|
||||
bool IsPinHovered(int* attribute_id);
|
||||
|
||||
// Use The following two functions to query the number of selected nodes or links in the current
|
||||
// editor. Use after calling EndNodeEditor().
|
||||
int NumSelectedNodes();
|
||||
int NumSelectedLinks();
|
||||
// Get the selected node/link ids. The pointer argument should point to an integer array with at
|
||||
// least as many elements as the respective NumSelectedNodes/NumSelectedLinks function call
|
||||
// returned.
|
||||
void GetSelectedNodes(int* node_ids);
|
||||
void GetSelectedLinks(int* link_ids);
|
||||
|
||||
// Clears the list of selected nodes/links. Useful if you want to delete a selected node or link.
|
||||
void ClearNodeSelection();
|
||||
void ClearLinkSelection();
|
||||
|
||||
// Was the previous attribute active? This will continuously return true while the left mouse button
|
||||
// is being pressed over the UI content of the attribute.
|
||||
bool IsAttributeActive();
|
||||
// Was any attribute active? If so, sets the active attribute id to the output function argument.
|
||||
bool IsAnyAttributeActive(int* attribute_id = NULL);
|
||||
|
||||
// Use the following functions to query a change of state for an existing link, or new link. Call
|
||||
// these after EndNodeEditor().
|
||||
|
||||
// Did the user start dragging a new link from a pin?
|
||||
bool IsLinkStarted(int* started_at_attribute_id);
|
||||
// Did the user drop the dragged link before attaching it to a pin?
|
||||
// There are two different kinds of situations to consider when handling this event:
|
||||
// 1) a link which is created at a pin and then dropped
|
||||
// 2) an existing link which is detached from a pin and then dropped
|
||||
// Use the including_detached_links flag to control whether this function triggers when the user
|
||||
// detaches a link and drops it.
|
||||
bool IsLinkDropped(int* started_at_attribute_id = NULL, bool including_detached_links = true);
|
||||
// Did the user finish creating a new link?
|
||||
bool IsLinkCreated(
|
||||
int* started_at_attribute_id,
|
||||
int* ended_at_attribute_id,
|
||||
bool* created_from_snap = NULL);
|
||||
bool IsLinkCreated(
|
||||
int* started_at_node_id,
|
||||
int* started_at_attribute_id,
|
||||
int* ended_at_node_id,
|
||||
int* ended_at_attribute_id,
|
||||
bool* created_from_snap = NULL);
|
||||
|
||||
// Was an existing link detached from a pin by the user? The detached link's id is assigned to the
|
||||
// output argument link_id.
|
||||
bool IsLinkDestroyed(int* link_id);
|
||||
|
||||
// Use the following functions to write the editor context's state to a string, or directly to a
|
||||
// file. The editor context is serialized in the INI file format.
|
||||
|
||||
const char* SaveCurrentEditorStateToIniString(size_t* data_size = NULL);
|
||||
const char* SaveEditorStateToIniString(const EditorContext* editor, size_t* data_size = NULL);
|
||||
|
||||
void LoadCurrentEditorStateFromIniString(const char* data, size_t data_size);
|
||||
void LoadEditorStateFromIniString(EditorContext* editor, const char* data, size_t data_size);
|
||||
|
||||
void SaveCurrentEditorStateToIniFile(const char* file_name);
|
||||
void SaveEditorStateToIniFile(const EditorContext* editor, const char* file_name);
|
||||
|
||||
void LoadCurrentEditorStateFromIniFile(const char* file_name);
|
||||
void LoadEditorStateFromIniFile(EditorContext* editor, const char* file_name);
|
||||
} // namespace imnodes
|
|
@ -1,6 +1,5 @@
|
|||
#include <imgui/imgui.h>
|
||||
#include <imgui/imgui_internal.h>
|
||||
#include <imgui/imnodes.h>
|
||||
|
||||
#include "audio/audio_scene.h"
|
||||
#include "editor/asset_browser.h"
|
||||
|
@ -441,7 +440,6 @@ struct StudioAppImpl final : StudioApp
|
|||
|
||||
ImGui::SetAllocatorFunctions(imguiAlloc, imguiFree, this);
|
||||
ImGui::CreateContext();
|
||||
imnodes::CreateContext();
|
||||
loadSettings();
|
||||
initIMGUI();
|
||||
|
||||
|
|
|
@ -136,6 +136,7 @@ LUMIX_FORCE_INLINE static bool trigger(Signal* signal)
|
|||
}
|
||||
else {
|
||||
--signal->counter;
|
||||
ASSERT(signal->counter >= 0);
|
||||
if (signal->counter > 0) return false;
|
||||
}
|
||||
|
||||
|
|
|
@ -12,11 +12,12 @@
|
|||
#include "engine/os.h"
|
||||
#include "engine/string.h"
|
||||
#include "engine/universe.h"
|
||||
#include "render_plugins.h"
|
||||
#include "renderer/material.h"
|
||||
#include "renderer/particle_system.h"
|
||||
#include "renderer/render_scene.h"
|
||||
#include "renderer/renderer.h"
|
||||
#include <imgui/imgui.h>
|
||||
#include <imgui/imnodes.h>
|
||||
|
||||
namespace Lumix {
|
||||
|
||||
|
@ -94,28 +95,24 @@ struct ParticleEditorResource {
|
|||
return {};
|
||||
}
|
||||
|
||||
void beginInput() {
|
||||
imnodes::BeginInputAttribute(m_id | (u32(m_input_counter) << 16));
|
||||
void inputSlot(ImGuiEx::PinShape shape = ImGuiEx::PinShape::CIRCLE) {
|
||||
ImGuiEx::Pin(m_id | (u32(m_input_counter) << 16), true, shape);
|
||||
++m_input_counter;
|
||||
}
|
||||
|
||||
static void endInput() { imnodes::EndInputAttribute(); }
|
||||
|
||||
void beginOutput() {
|
||||
imnodes::BeginOutputAttribute(m_id | (u32(m_output_counter) << 16) | OUTPUT_FLAG);
|
||||
void outputSlot(ImGuiEx::PinShape shape = ImGuiEx::PinShape::CIRCLE) {
|
||||
ImGuiEx::Pin(m_id | (u32(m_output_counter) << 16) | OUTPUT_FLAG, false, shape);
|
||||
++m_output_counter;
|
||||
}
|
||||
|
||||
static void endOutput() { imnodes::EndOutputAttribute(); }
|
||||
|
||||
bool onNodeGUI() {
|
||||
m_input_counter = 0;
|
||||
m_output_counter = 0;
|
||||
imnodes::SetNodeEditorSpacePos(m_id, m_pos);
|
||||
imnodes::BeginNode(m_id);
|
||||
const ImVec2 old_pos = m_pos;
|
||||
ImGuiEx::BeginNode(m_id, m_pos);
|
||||
bool res = onGUI();
|
||||
imnodes::EndNode();
|
||||
return res;
|
||||
ImGuiEx::EndNode();
|
||||
return res || old_pos.x != m_pos.x || old_pos.y != m_pos.y;
|
||||
}
|
||||
|
||||
u16 m_id;
|
||||
|
@ -157,12 +154,11 @@ struct ParticleEditorResource {
|
|||
}
|
||||
|
||||
bool onGUI() override {
|
||||
beginInput();
|
||||
endInput();
|
||||
beginOutput();
|
||||
inputSlot();
|
||||
ImGui::SetNextItemWidth(60);
|
||||
ImGui::Combo("##fn", (int*)&func, "cos\0sin\0");
|
||||
endOutput();
|
||||
ImGui::SameLine();
|
||||
outputSlot();
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -209,13 +205,11 @@ struct ParticleEditorResource {
|
|||
void deserialize(InputMemoryStream& blob) override { blob.read(count); blob.read(keys); blob.read(values); }
|
||||
|
||||
bool onGUI() override {
|
||||
beginOutput();
|
||||
endOutput();
|
||||
|
||||
beginInput();
|
||||
ImGui::SetNextItemWidth(120);
|
||||
inputSlot();
|
||||
bool changed = ImGuiEx::Gradient4("test", lengthOf(keys), (int*)&count, keys, &values[0].x);
|
||||
endInput();
|
||||
ImGui::SameLine();
|
||||
outputSlot();
|
||||
return changed;
|
||||
}
|
||||
|
||||
|
@ -256,11 +250,10 @@ struct ParticleEditorResource {
|
|||
void deserialize(InputMemoryStream& blob) override { blob.read(count); blob.read(keys); blob.read(values); }
|
||||
|
||||
bool onGUI() override {
|
||||
beginOutput();
|
||||
endOutput();
|
||||
|
||||
beginInput();
|
||||
ImGui::TextUnformatted("Gradient");
|
||||
ImGui::BeginGroup();
|
||||
inputSlot();
|
||||
|
||||
ImGui::PushItemWidth(60);
|
||||
bool changed = false;
|
||||
for (u32 i = 0; i < count; ++i) {
|
||||
|
@ -272,7 +265,6 @@ struct ParticleEditorResource {
|
|||
keys[i] = clamp(keys[i], 0.f, 1.f);
|
||||
}
|
||||
ImGui::PopItemWidth();
|
||||
endInput();
|
||||
if (ImGui::Button("Add")) {
|
||||
ASSERT(count < lengthOf(values));
|
||||
keys[count] = 0;
|
||||
|
@ -280,6 +272,10 @@ struct ParticleEditorResource {
|
|||
++count;
|
||||
changed = true;
|
||||
}
|
||||
ImGui::EndGroup();
|
||||
|
||||
ImGui::SameLine();
|
||||
outputSlot();
|
||||
return changed ;
|
||||
}
|
||||
|
||||
|
@ -304,9 +300,7 @@ struct ParticleEditorResource {
|
|||
}
|
||||
|
||||
bool onGUI() override {
|
||||
beginOutput();
|
||||
ImGui::TextUnformatted(m_resource.m_consts[idx].name);
|
||||
endOutput();
|
||||
outputSlot(); ImGui::TextUnformatted(m_resource.m_consts[idx].name);
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -339,15 +333,20 @@ struct ParticleEditorResource {
|
|||
}
|
||||
|
||||
bool onGUI() override {
|
||||
imnodes::BeginNodeTitleBar();
|
||||
ImGui::Text("Random");
|
||||
imnodes::EndNodeTitleBar();
|
||||
beginOutput();
|
||||
//imnodes::BeginNodeTitleBar();
|
||||
ImGui::Text(ICON_FA_DICE " Random");
|
||||
//imnodes::EndNodeTitleBar();
|
||||
|
||||
ImGui::BeginGroup();
|
||||
ImGui::PushItemWidth(60);
|
||||
ImGui::DragFloat("From", &from);
|
||||
ImGui::DragFloat("To", &to);
|
||||
ImGui::DragFloat("##from", &from);
|
||||
ImGui::SameLine(); ImGui::DragFloat("##to", &to);
|
||||
ImGui::PopItemWidth();
|
||||
endOutput();
|
||||
ImGui::EndGroup();
|
||||
|
||||
ImGui::SameLine();
|
||||
|
||||
outputSlot();
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -371,10 +370,9 @@ struct ParticleEditorResource {
|
|||
void deserialize(InputMemoryStream& blob) override { blob.read(value); }
|
||||
|
||||
bool onGUI() override {
|
||||
beginOutput();
|
||||
outputSlot();
|
||||
ImGui::SetNextItemWidth(120);
|
||||
bool changed = ImGui::DragFloat("##v", &value);
|
||||
endOutput();
|
||||
return changed;
|
||||
}
|
||||
|
||||
|
@ -402,39 +400,41 @@ struct ParticleEditorResource {
|
|||
void deserialize(InputMemoryStream& blob) override { blob.read(value); }
|
||||
|
||||
bool onGUI() override {
|
||||
beginOutput();
|
||||
endOutput();
|
||||
|
||||
ImGui::PushItemWidth(60);
|
||||
|
||||
bool changed = false;
|
||||
beginInput();
|
||||
ImGui::BeginGroup();
|
||||
inputSlot();
|
||||
if (getInput(0).node) {
|
||||
ImGui::TextUnformatted("X");
|
||||
}
|
||||
else {
|
||||
changed = ImGui::DragFloat("X", &value.x);
|
||||
}
|
||||
endInput();
|
||||
|
||||
beginInput();
|
||||
inputSlot();
|
||||
if (getInput(1).node) {
|
||||
ImGui::TextUnformatted("Y");
|
||||
}
|
||||
else {
|
||||
changed = ImGui::DragFloat("Y", &value.y) || changed;
|
||||
}
|
||||
endInput();
|
||||
|
||||
beginInput();
|
||||
|
||||
inputSlot();
|
||||
if (getInput(2).node) {
|
||||
ImGui::TextUnformatted("Z");
|
||||
}
|
||||
else {
|
||||
changed = ImGui::DragFloat("Z", &value.z) || changed;
|
||||
}
|
||||
endInput();
|
||||
ImGui::EndGroup();
|
||||
|
||||
ImGui::PopItemWidth();
|
||||
|
||||
ImGui::SameLine();
|
||||
outputSlot();
|
||||
|
||||
return changed;
|
||||
}
|
||||
|
||||
|
@ -457,14 +457,13 @@ struct ParticleEditorResource {
|
|||
void deserialize(InputMemoryStream& blob) override { blob.read(idx); }
|
||||
|
||||
bool onGUI() override {
|
||||
beginOutput();
|
||||
outputSlot();
|
||||
if (idx < m_resource.m_streams.size()) {
|
||||
ImGui::TextUnformatted(m_resource.m_streams[idx].name);
|
||||
}
|
||||
else {
|
||||
ImGui::TextUnformatted(ICON_FA_EXCLAMATION "Deleted input");
|
||||
}
|
||||
endOutput();
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -477,13 +476,11 @@ struct ParticleEditorResource {
|
|||
Type getType() const override { return Type::EMIT; }
|
||||
|
||||
bool onGUI() override {
|
||||
imnodes::BeginNodeTitleBar();
|
||||
//imnodes::BeginNodeTitleBar();
|
||||
ImGui::TextUnformatted(ICON_FA_PLUS " Emit");
|
||||
imnodes::EndNodeTitleBar();
|
||||
//imnodes::EndNodeTitleBar();
|
||||
for (const Stream& stream : m_resource.m_streams) {
|
||||
beginInput();
|
||||
ImGui::TextUnformatted(stream.name);
|
||||
endInput();
|
||||
inputSlot(); ImGui::TextUnformatted(stream.name);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
@ -558,18 +555,15 @@ struct ParticleEditorResource {
|
|||
Type getType() const override { return Type::UPDATE; }
|
||||
|
||||
bool onGUI() override {
|
||||
imnodes::BeginNodeTitleBar();
|
||||
|
||||
//imnodes::BeginNodeTitleBar();
|
||||
ImGui::TextUnformatted(ICON_FA_CLOCK " Update");
|
||||
imnodes::EndNodeTitleBar();
|
||||
//imnodes::EndNodeTitleBar();
|
||||
|
||||
beginInput();
|
||||
ImGui::TextUnformatted("Kill");
|
||||
endInput();
|
||||
inputSlot(ImGuiEx::PinShape::TRIANGLE); ImGui::TextUnformatted("Kill");
|
||||
|
||||
for (const Stream& stream : m_resource.m_streams) {
|
||||
beginInput();
|
||||
ImGui::TextUnformatted(stream.name);
|
||||
endInput();
|
||||
inputSlot(); ImGui::TextUnformatted(stream.name);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
@ -614,25 +608,24 @@ struct ParticleEditorResource {
|
|||
Type getType() const override { return Type::CMP; }
|
||||
|
||||
bool onGUI() override {
|
||||
beginInput();
|
||||
ImGui::TextUnformatted("A");
|
||||
endInput();
|
||||
ImGui::BeginGroup();
|
||||
inputSlot(); ImGui::NewLine();
|
||||
|
||||
beginOutput();
|
||||
ImGui::SetNextItemWidth(60);
|
||||
bool changed = ImGui::Combo("##op", (int*)&op, "A < B\0A > B\0");
|
||||
endOutput();
|
||||
ImGui::SetNextItemWidth(45);
|
||||
bool changed = ImGui::Combo("##op", (int*)&op, "<\0>\0");
|
||||
inputSlot();
|
||||
|
||||
beginInput();
|
||||
if (getInput(1).node) {
|
||||
ImGui::TextUnformatted("B");
|
||||
ImGui::NewLine();
|
||||
}
|
||||
else {
|
||||
ImGui::SetNextItemWidth(60);
|
||||
changed = ImGui::DragFloat("B", &value) || changed;
|
||||
changed = ImGui::DragFloat("##b", &value) || changed;
|
||||
}
|
||||
endInput();
|
||||
ImGui::EndGroup();
|
||||
|
||||
ImGui::SameLine();
|
||||
outputSlot(ImGuiEx::PinShape::TRIANGLE);
|
||||
return changed;
|
||||
}
|
||||
|
||||
|
@ -681,13 +674,11 @@ struct ParticleEditorResource {
|
|||
Type getType() const override { return Type::OUTPUT; }
|
||||
|
||||
bool onGUI() override {
|
||||
imnodes::BeginNodeTitleBar();
|
||||
//imnodes::BeginNodeTitleBar();
|
||||
ImGui::TextUnformatted(ICON_FA_EYE " Output");
|
||||
imnodes::EndNodeTitleBar();
|
||||
//imnodes::EndNodeTitleBar();
|
||||
for (const Output& stream : m_resource.m_outputs) {
|
||||
beginInput();
|
||||
ImGui::TextUnformatted(stream.name);
|
||||
endInput();
|
||||
inputSlot(); ImGui::TextUnformatted(stream.name);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
@ -767,15 +758,14 @@ struct ParticleEditorResource {
|
|||
void deserialize(InputMemoryStream& blob) override { blob.read(color0); blob.read(color1); }
|
||||
|
||||
bool onGUI() override {
|
||||
beginInput();
|
||||
ImGui::TextUnformatted("Weight");
|
||||
endInput();
|
||||
ImGui::BeginGroup();
|
||||
inputSlot(); ImGui::TextUnformatted("Weight");
|
||||
bool changed = ImGui::ColorEdit4("Color A", &color0.x, ImGuiColorEditFlags_NoInputs);
|
||||
changed = ImGui::ColorEdit4("Color B", &color1.x, ImGuiColorEditFlags_NoInputs) || changed;
|
||||
ImGui::EndGroup();
|
||||
|
||||
beginOutput();
|
||||
ImGui::TextUnformatted("RGBA");
|
||||
endOutput();
|
||||
ImGui::SameLine();
|
||||
outputSlot();
|
||||
|
||||
return changed;
|
||||
}
|
||||
|
@ -831,35 +821,34 @@ struct ParticleEditorResource {
|
|||
}
|
||||
|
||||
bool onGUI() override {
|
||||
imnodes::BeginNodeTitleBar();
|
||||
beginOutput();
|
||||
ImGui::TextUnformatted("A * B + C");
|
||||
endOutput();
|
||||
imnodes::EndNodeTitleBar();
|
||||
|
||||
beginInput();
|
||||
ImGui::TextUnformatted("A");
|
||||
endInput();
|
||||
|
||||
beginInput();
|
||||
ImGui::BeginGroup();
|
||||
inputSlot(); ImGui::NewLine();
|
||||
|
||||
ImGui::TextUnformatted("X");
|
||||
|
||||
inputSlot();
|
||||
if (getInput(1).node) {
|
||||
ImGui::TextUnformatted("B");
|
||||
ImGui::NewLine();
|
||||
}
|
||||
else {
|
||||
ImGui::SetNextItemWidth(60);
|
||||
ImGui::DragFloat("B", &value1);
|
||||
}
|
||||
endInput();
|
||||
|
||||
beginInput();
|
||||
ImGui::TextUnformatted(ICON_FA_PLUS);
|
||||
|
||||
inputSlot();
|
||||
if (getInput(2).node) {
|
||||
ImGui::TextUnformatted("C");
|
||||
ImGui::NewLine();
|
||||
}
|
||||
else {
|
||||
ImGui::SetNextItemWidth(60);
|
||||
ImGui::DragFloat("C", &value2);
|
||||
}
|
||||
endInput();
|
||||
ImGui::EndGroup();
|
||||
|
||||
ImGui::SameLine();
|
||||
outputSlot();
|
||||
|
||||
return false;
|
||||
}
|
||||
|
@ -913,30 +902,30 @@ struct ParticleEditorResource {
|
|||
}
|
||||
|
||||
bool onGUI() override {
|
||||
imnodes::BeginNodeTitleBar();
|
||||
beginOutput();
|
||||
switch(OP_TYPE) {
|
||||
case InstructionType::DIV: ImGui::TextUnformatted("Divide"); break;
|
||||
case InstructionType::MUL: ImGui::TextUnformatted("Multiply"); break;
|
||||
case InstructionType::ADD: ImGui::TextUnformatted("Add"); break;
|
||||
default: ASSERT(false); break;
|
||||
}
|
||||
endOutput();
|
||||
imnodes::EndNodeTitleBar();
|
||||
|
||||
ImGui::BeginGroup();
|
||||
inputSlot(); ImGui::NewLine();
|
||||
|
||||
beginInput();
|
||||
ImGui::TextUnformatted("A");
|
||||
endInput();
|
||||
|
||||
beginInput();
|
||||
inputSlot();
|
||||
if (getInput(1).node) {
|
||||
ImGui::TextUnformatted("B");
|
||||
ImGui::NewLine();
|
||||
}
|
||||
else {
|
||||
ImGui::SetNextItemWidth(60);
|
||||
ImGui::DragFloat("B", &value);
|
||||
ImGui::DragFloat("##b", &value);
|
||||
}
|
||||
endInput();
|
||||
ImGui::EndGroup();
|
||||
|
||||
ImGui::SameLine();
|
||||
switch(OP_TYPE) {
|
||||
case InstructionType::DIV: ImGui::TextUnformatted(ICON_FA_DIVIDE); break;
|
||||
case InstructionType::MUL: ImGui::TextUnformatted("X"); break;
|
||||
case InstructionType::ADD: ImGui::TextUnformatted(ICON_FA_PLUS); break;
|
||||
default: ASSERT(false); break;
|
||||
}
|
||||
|
||||
ImGui::SameLine();
|
||||
outputSlot();
|
||||
|
||||
return false;
|
||||
}
|
||||
|
@ -1110,7 +1099,9 @@ struct ParticleEditorResource {
|
|||
|
||||
m_nodes.push(UniquePtr<UpdateNode>::create(m_allocator, *this));
|
||||
m_nodes.push(UniquePtr<OutputNode>::create(m_allocator, *this));
|
||||
m_nodes.back()->m_pos = ImVec2(200, 100);
|
||||
m_nodes.push(UniquePtr<EmitNode>::create(m_allocator, *this));
|
||||
m_nodes.back()->m_pos = ImVec2(300, 100);
|
||||
}
|
||||
|
||||
void generate() {
|
||||
|
@ -1169,10 +1160,11 @@ struct ParticleEditorResource {
|
|||
};
|
||||
|
||||
struct ParticleEditorImpl : ParticleEditor {
|
||||
ParticleEditorImpl(StudioApp& app, IAllocator& allocator)
|
||||
ParticleEditorImpl(StudioApp& app, IImGuiRenderer& imgui_renderer, IAllocator& allocator)
|
||||
: m_allocator(allocator)
|
||||
, m_app(app)
|
||||
, m_undo_stack(allocator)
|
||||
, m_imgui_renderer(imgui_renderer)
|
||||
{
|
||||
m_toggle_ui.init("Particle editor", "Toggle particle editor", "particle_editor", "", true);
|
||||
m_toggle_ui.func.bind<&ParticleEditorImpl::toggleOpen>(this);
|
||||
|
@ -1198,6 +1190,7 @@ struct ParticleEditorImpl : ParticleEditor {
|
|||
}
|
||||
|
||||
~ParticleEditorImpl() {
|
||||
if (m_canvas_ctx) ImGui::DestroyContext(m_canvas_ctx);
|
||||
m_app.removeAction(&m_toggle_ui);
|
||||
m_app.removeAction(&m_undo_action);
|
||||
m_app.removeAction(&m_redo_action);
|
||||
|
@ -1400,22 +1393,86 @@ struct ParticleEditorImpl : ParticleEditor {
|
|||
ImGui::Columns(2);
|
||||
|
||||
leftColumnGUI();
|
||||
|
||||
ImGui::NextColumn();
|
||||
bool context_open = false;
|
||||
imnodes::BeginNodeEditor();
|
||||
|
||||
if (imnodes::IsEditorHovered() && ImGui::IsMouseClicked(1)) {
|
||||
ImGui::NextColumn();
|
||||
|
||||
ImVec2 canvas_size = ImGui::GetContentRegionAvail();
|
||||
const ImVec2 canvas_origin = ImGui::GetCursorScreenPos();
|
||||
|
||||
ImGuiContext* mainctx = ImGui::GetCurrentContext();
|
||||
if (!m_canvas_ctx) m_canvas_ctx = ImGui::CreateContext(ImGui::GetIO().Fonts);
|
||||
ImGui::SetCurrentContext(m_canvas_ctx);
|
||||
|
||||
const os::Event* events = m_app.getEvents();
|
||||
ImGuiIO& io = ImGui::GetIO();
|
||||
for (i32 i = 0, c = m_app.getEventsCount(); i < c; ++i) {
|
||||
switch(events[i].type) {
|
||||
case os::Event::Type::MOUSE_BUTTON: {
|
||||
// TODO check if we should handle input, see studioapp how it's done there
|
||||
io.AddMouseButtonEvent((int)events[i].mouse_button.button, events[i].mouse_button.down);
|
||||
break;
|
||||
}
|
||||
case os::Event::Type::MOUSE_MOVE: {
|
||||
const os::Point cp = os::getMouseScreenPos();
|
||||
io.AddMousePosEvent((cp.x - canvas_origin.x) / m_canvas_scale.x, (cp.y - canvas_origin.y) / m_canvas_scale.y);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ImGui::GetIO().DisplaySize = canvas_size / m_canvas_scale;
|
||||
ImGui::NewFrame();
|
||||
|
||||
ImGui::SetNextWindowPos(ImVec2(0, 0));
|
||||
ImGui::SetNextWindowSize(canvas_size / m_canvas_scale);
|
||||
ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2(0, 0));
|
||||
ImGui::Begin("particle_editor_canvas", nullptr, ImGuiWindowFlags_NoBackground | ImGuiWindowFlags_NoDecoration | ImGuiWindowFlags_NoInputs);
|
||||
ImGui::PopStyleVar();
|
||||
ImGuiEx::BeginNodeEditor("particle_editor", &m_canvas_offset);
|
||||
|
||||
i32 hovered_node = -1;
|
||||
i32 hovered_link = -1;
|
||||
for (UniquePtr<ParticleEditorResource::Node>& n : m_resource->m_nodes) {
|
||||
if (n->onNodeGUI()) {
|
||||
pushUndo(n->m_id);
|
||||
}
|
||||
if (ImGui::IsItemHovered()) {
|
||||
hovered_node = n->m_id;
|
||||
}
|
||||
}
|
||||
|
||||
for (const ParticleEditorResource::Link& link : m_resource->m_links) {
|
||||
ImGuiEx::NodeLink(link.from, link.to);
|
||||
if (ImGuiEx::IsLinkHovered()) {
|
||||
hovered_link = link.id;
|
||||
}
|
||||
}
|
||||
|
||||
ImGuiID nlf, nlt;
|
||||
if (ImGuiEx::GetNewLink(&nlf, &nlt)) {
|
||||
ParticleEditorResource::Link& link = m_resource->m_links.emplace();
|
||||
link.from = nlf;
|
||||
link.to = nlt;
|
||||
pushUndo(0xffFFffFF);
|
||||
}
|
||||
|
||||
ImGuiEx::EndNodeEditor();
|
||||
const ImVec2 editor_pos = ImGui::GetItemRectMin();
|
||||
bool context_open = false;
|
||||
|
||||
if (ImGui::IsItemHovered() && ImGui::IsMouseClicked(1)) {
|
||||
ImGui::OpenPopup("context_menu");
|
||||
context_open = true;
|
||||
}
|
||||
|
||||
|
||||
ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2(8.f, 8.f));
|
||||
if (ImGui::BeginPopup("context_menu")) {
|
||||
ImVec2 cp = ImGui::GetItemRectMin();
|
||||
if (ImGui::BeginMenu("Add")) {
|
||||
if (ImGui::Selectable("Add")) addNode(ParticleEditorResource::Node::ADD);
|
||||
if (ImGui::Selectable("Color mix")) addNode(ParticleEditorResource::Node::COLOR_MIX);
|
||||
if (ImGui::Selectable("Compare")) addNode(ParticleEditorResource::Node::CMP);
|
||||
ParticleEditorResource::Node* n = nullptr;
|
||||
if (ImGui::Selectable("Add")) n = addNode(ParticleEditorResource::Node::ADD);
|
||||
if (ImGui::Selectable("Color mix")) n = addNode(ParticleEditorResource::Node::COLOR_MIX);
|
||||
if (ImGui::Selectable("Compare")) n = addNode(ParticleEditorResource::Node::CMP);
|
||||
if (ImGui::BeginMenu("Constant")) {
|
||||
for (u8 i = 0; i < m_resource->m_consts.size(); ++i) {
|
||||
if (ImGui::Selectable(m_resource->m_consts[i].name)) {
|
||||
|
@ -1428,11 +1485,11 @@ struct ParticleEditorImpl : ParticleEditor {
|
|||
ImGui::EndMenu();
|
||||
}
|
||||
if (ImGui::Selectable("Cos")) {
|
||||
ParticleEditorResource::Node* n = addNode(ParticleEditorResource::Node::UNARY_FUNCTION);
|
||||
n = addNode(ParticleEditorResource::Node::UNARY_FUNCTION);
|
||||
((ParticleEditorResource::UnaryFunctionNode*)n)->func = ParticleEditorResource::UnaryFunctionNode::COS;
|
||||
}
|
||||
if (ImGui::Selectable("Gradient")) addNode(ParticleEditorResource::Node::GRADIENT);
|
||||
if (ImGui::Selectable("Gradient color")) addNode(ParticleEditorResource::Node::GRADIENT_COLOR);
|
||||
if (ImGui::Selectable("Gradient")) n = addNode(ParticleEditorResource::Node::GRADIENT);
|
||||
if (ImGui::Selectable("Gradient color")) n = addNode(ParticleEditorResource::Node::GRADIENT_COLOR);
|
||||
if (ImGui::BeginMenu("Input")) {
|
||||
for (u8 i = 0; i < m_resource->m_streams.size(); ++i) {
|
||||
if (ImGui::Selectable(m_resource->m_streams[i].name)) {
|
||||
|
@ -1444,16 +1501,19 @@ struct ParticleEditorImpl : ParticleEditor {
|
|||
}
|
||||
ImGui::EndMenu();
|
||||
}
|
||||
if (ImGui::Selectable("Literal")) addNode(ParticleEditorResource::Node::LITERAL);
|
||||
if (ImGui::Selectable("Divide")) addNode(ParticleEditorResource::Node::DIV);
|
||||
if (ImGui::Selectable("Multiply")) addNode(ParticleEditorResource::Node::MUL);
|
||||
if (ImGui::Selectable("Multiply add")) addNode(ParticleEditorResource::Node::MADD);
|
||||
if (ImGui::Selectable("Random")) addNode(ParticleEditorResource::Node::RANDOM);
|
||||
if (ImGui::Selectable("Literal")) n = addNode(ParticleEditorResource::Node::LITERAL);
|
||||
if (ImGui::Selectable("Divide")) n = addNode(ParticleEditorResource::Node::DIV);
|
||||
if (ImGui::Selectable("Multiply")) n = addNode(ParticleEditorResource::Node::MUL);
|
||||
if (ImGui::Selectable("Multiply add")) n = addNode(ParticleEditorResource::Node::MADD);
|
||||
if (ImGui::Selectable("Random")) n = addNode(ParticleEditorResource::Node::RANDOM);
|
||||
if (ImGui::Selectable("Sin")) {
|
||||
ParticleEditorResource::Node* n = addNode(ParticleEditorResource::Node::UNARY_FUNCTION);
|
||||
n = addNode(ParticleEditorResource::Node::UNARY_FUNCTION);
|
||||
((ParticleEditorResource::UnaryFunctionNode*)n)->func = ParticleEditorResource::UnaryFunctionNode::SIN;
|
||||
}
|
||||
if (ImGui::Selectable("Vec3")) addNode(ParticleEditorResource::Node::VEC3);
|
||||
if (ImGui::Selectable("Vec3")) n = addNode(ParticleEditorResource::Node::VEC3);
|
||||
if (n) {
|
||||
n->m_pos = cp - editor_pos - ImGuiEx::GetNodeEditorOffset();
|
||||
}
|
||||
ImGui::EndMenu();
|
||||
}
|
||||
|
||||
|
@ -1478,43 +1538,38 @@ struct ParticleEditorImpl : ParticleEditor {
|
|||
}
|
||||
ImGui::PopStyleVar();
|
||||
|
||||
for (UniquePtr<ParticleEditorResource::Node>& n : m_resource->m_nodes) {
|
||||
if (n->onNodeGUI()) {
|
||||
pushUndo(n->m_id);
|
||||
}
|
||||
}
|
||||
|
||||
for (const ParticleEditorResource::Link& link : m_resource->m_links) {
|
||||
imnodes::Link(link.id, link.from, link.to);
|
||||
}
|
||||
|
||||
imnodes::EndNodeEditor();
|
||||
|
||||
for (UniquePtr<ParticleEditorResource::Node>& n : m_resource->m_nodes) {
|
||||
ImVec2 p = imnodes::GetNodeEditorSpacePos(n->m_id);
|
||||
if (p.x != n->m_pos.x || p.y != n->m_pos.y) {
|
||||
n->m_pos = p;
|
||||
pushUndo(n->m_id);
|
||||
}
|
||||
}
|
||||
|
||||
if (context_open) {
|
||||
m_context_link = -1;
|
||||
imnodes::IsLinkHovered(&m_context_link);
|
||||
m_context_node = -1;
|
||||
imnodes::IsNodeHovered(&m_context_node);
|
||||
m_context_link = hovered_link;
|
||||
m_context_node = hovered_node;
|
||||
}
|
||||
|
||||
{
|
||||
int from, to;
|
||||
if (imnodes::IsLinkCreated(&from, &to)) {
|
||||
ParticleEditorResource::Link& link = m_resource->m_links.emplace();
|
||||
link.id = m_resource->genID();
|
||||
link.from = from;
|
||||
link.to = to;
|
||||
pushUndo(0xffFFffFF);
|
||||
}
|
||||
ImGui::End();
|
||||
ImGui::Render();
|
||||
|
||||
if (!m_canvas_rt || m_canvas_size.x != canvas_size.x || m_canvas_size.y != canvas_size.y) {
|
||||
Renderer* renderer = (Renderer*)m_app.getEngine().getPluginManager().getPlugin("renderer");
|
||||
Renderer::MemRef mem;
|
||||
if (m_canvas_rt) renderer->destroy(m_canvas_rt);
|
||||
m_canvas_size = canvas_size;
|
||||
m_canvas_rt = renderer->createTexture((u32)canvas_size.x, (u32)canvas_size.y, 1, gpu::TextureFormat::RGBA8, gpu::TextureFlags::RENDER_TARGET | gpu::TextureFlags::SRGB, mem, "particle_editor");
|
||||
}
|
||||
|
||||
m_imgui_renderer.render(m_canvas_rt, Vec2(canvas_size.x, canvas_size.y), ImGui::GetDrawData(), m_canvas_scale);
|
||||
|
||||
ImGui::SetCurrentContext(mainctx);
|
||||
ImGui::SetCursorScreenPos(canvas_origin);
|
||||
if (gpu::isOriginBottomLeft()) {
|
||||
ImGui::Image(m_canvas_rt, canvas_size, ImVec2(0, 1), ImVec2(1, 0));
|
||||
}
|
||||
else {
|
||||
ImGui::Image(m_canvas_rt, canvas_size);
|
||||
}
|
||||
if (ImGui::IsItemHovered() && ImGui::GetIO().MouseWheel) {
|
||||
m_canvas_scale.x += ImGui::GetIO().MouseWheel / 20;
|
||||
m_canvas_scale.x = clamp(m_canvas_scale.x, 0.1f, 10.f);
|
||||
m_canvas_scale.y = m_canvas_scale.x;
|
||||
}
|
||||
|
||||
ImGui::Columns();
|
||||
|
||||
ImGui::End();
|
||||
|
@ -1711,19 +1766,21 @@ struct ParticleEditorImpl : ParticleEditor {
|
|||
Action m_redo_action;
|
||||
Action m_apply_action;
|
||||
bool m_has_focus = false;
|
||||
IImGuiRenderer& m_imgui_renderer;
|
||||
ImVec2 m_canvas_size = ImVec2(0, 0);
|
||||
gpu::TextureHandle m_canvas_rt = gpu::INVALID_TEXTURE;
|
||||
ImVec2 m_canvas_scale = ImVec2(1, 1);
|
||||
ImVec2 m_canvas_offset = ImVec2(0, 0);
|
||||
ImGuiContext* m_canvas_ctx = nullptr;
|
||||
};
|
||||
|
||||
DataStream ParticleEditorResource::NodeInput::generate(OutputMemoryStream& instructions, DataStream output, u8 subindex) const {
|
||||
return node ? node->generate(instructions, output_idx, output, subindex) : DataStream();
|
||||
}
|
||||
|
||||
UniquePtr<StudioApp::GUIPlugin> createParticleEditor(StudioApp& app) {
|
||||
return UniquePtr<ParticleEditorImpl>::create(app.getAllocator(), app, app.getAllocator());
|
||||
}
|
||||
|
||||
UniquePtr<ParticleEditor> ParticleEditor::create(StudioApp& app) {
|
||||
UniquePtr<ParticleEditor> ParticleEditor::create(StudioApp& app, IImGuiRenderer& imgui_renderer) {
|
||||
IAllocator& allocator = app.getAllocator();
|
||||
return UniquePtr<ParticleEditorImpl>::create(allocator, app, allocator);
|
||||
return UniquePtr<ParticleEditorImpl>::create(allocator, app, imgui_renderer, allocator);
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -7,7 +7,7 @@ namespace Lumix {
|
|||
template <typename T> struct UniquePtr;
|
||||
|
||||
struct ParticleEditor : StudioApp::GUIPlugin {
|
||||
static UniquePtr<ParticleEditor> create(StudioApp& app);
|
||||
static UniquePtr<ParticleEditor> create(StudioApp& app, struct IImGuiRenderer& imgui_renderer);
|
||||
virtual void open(const char* path) = 0;
|
||||
virtual bool compile(struct InputMemoryStream& input, struct OutputMemoryStream& output, const char* path) = 0;
|
||||
};
|
||||
|
|
|
@ -4,7 +4,6 @@
|
|||
#endif
|
||||
|
||||
#include <imgui/imgui_freetype.h>
|
||||
#include <imgui/imnodes.h>
|
||||
|
||||
#include "animation/animation.h"
|
||||
#include "editor/asset_browser.h"
|
||||
|
@ -37,6 +36,7 @@
|
|||
#include "engine/universe.h"
|
||||
#include "fbx_importer.h"
|
||||
#include "game_view.h"
|
||||
#include "render_plugins.h"
|
||||
#include "renderer/culling_system.h"
|
||||
#include "renderer/editor/composite_texture.h"
|
||||
#include "renderer/font.h"
|
||||
|
@ -4357,7 +4357,7 @@ struct RenderInterfaceImpl final : RenderInterface
|
|||
};
|
||||
|
||||
|
||||
struct EditorUIRenderPlugin final : StudioApp::GUIPlugin
|
||||
struct EditorUIRenderPlugin final : StudioApp::GUIPlugin, IImGuiRenderer
|
||||
{
|
||||
struct RenderJob : Renderer::RenderJob
|
||||
{
|
||||
|
@ -4380,6 +4380,10 @@ struct EditorUIRenderPlugin final : StudioApp::GUIPlugin
|
|||
i32 x, y;
|
||||
bool new_program = false;
|
||||
Array<CmdList> cmd_lists;
|
||||
Renderer::TransientSlice ub;
|
||||
Vec2 scale;
|
||||
gpu::TextureHandle render_target = gpu::INVALID_TEXTURE;
|
||||
Vec4 clear_color = Vec4(0.2f, 0.2f, 0.2f, 1.f);
|
||||
};
|
||||
|
||||
RenderJob(LinearAllocator& allocator)
|
||||
|
@ -4405,6 +4409,8 @@ struct EditorUIRenderPlugin final : StudioApp::GUIPlugin
|
|||
PluginManager& plugin_manager = plugin->m_engine.getPluginManager();
|
||||
renderer = (Renderer*)plugin_manager.getPlugin("renderer");
|
||||
|
||||
if (!should_setup) return;
|
||||
|
||||
ImGuiPlatformIO& platform_io = ImGui::GetPlatformIO();
|
||||
window_draw_data.reserve(platform_io.Viewports.size());
|
||||
for (ImGuiViewport* vp : platform_io.Viewports) {
|
||||
|
@ -4414,9 +4420,17 @@ struct EditorUIRenderPlugin final : StudioApp::GUIPlugin
|
|||
dd.h = u32(vp->Size.y);
|
||||
dd.x = i32(draw_data->DisplayPos.x);
|
||||
dd.y = i32(draw_data->DisplayPos.y);
|
||||
dd.scale = Vec2(1);
|
||||
dd.window = vp->PlatformHandle;
|
||||
dd.program = getProgram(dd.window, dd.new_program);
|
||||
dd.cmd_lists.reserve(draw_data->CmdListsCount);
|
||||
dd.ub = renderer->allocUniform(sizeof(Vec4) * 2);
|
||||
|
||||
const Vec4 canvas_mtx[] = {
|
||||
Vec4(2.f / dd.w, 0, -1 + (float)-dd.x * 2.f / dd.w, 0),
|
||||
Vec4(0, -2.f / dd.h, 1 + (float)dd.y * 2.f / dd.h, 0)
|
||||
};
|
||||
memcpy(dd.ub.ptr, &canvas_mtx, sizeof(canvas_mtx));
|
||||
|
||||
for (int i = 0; i < draw_data->CmdListsCount; ++i) {
|
||||
ImDrawList* cmd_list = draw_data->CmdLists[i];
|
||||
|
@ -4428,9 +4442,14 @@ struct EditorUIRenderPlugin final : StudioApp::GUIPlugin
|
|||
out_cmd_list.vtx_buffer = renderer->allocTransient(cmd_list->VtxBuffer.size_in_bytes());
|
||||
memcpy(out_cmd_list.vtx_buffer.ptr, &cmd_list->VtxBuffer[0], cmd_list->VtxBuffer.size_in_bytes());
|
||||
|
||||
out_cmd_list.commands.resize(cmd_list->CmdBuffer.size());
|
||||
for (int i = 0, c = out_cmd_list.commands.size(); i < c; ++i) {
|
||||
out_cmd_list.commands[i] = cmd_list->CmdBuffer[i];
|
||||
out_cmd_list.commands.reserve(cmd_list->CmdBuffer.size());
|
||||
for (int i = 0, c = cmd_list->CmdBuffer.size(); i < c; ++i) {
|
||||
if (cmd_list->CmdBuffer[i].UserCallback) {
|
||||
cmd_list->CmdBuffer[i].UserCallback(cmd_list, &cmd_list->CmdBuffer[i]);
|
||||
}
|
||||
else {
|
||||
out_cmd_list.commands.push(cmd_list->CmdBuffer[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -4463,18 +4482,18 @@ struct EditorUIRenderPlugin final : StudioApp::GUIPlugin
|
|||
if (!tex) tex = *default_texture;
|
||||
gpu::bindTextures(&tex, 0, 1);
|
||||
|
||||
const u32 h = u32(clamp(pcmd->ClipRect.w - pcmd->ClipRect.y, 0.f, 65535.f));
|
||||
const u32 h = u32(clamp((pcmd->ClipRect.w - pcmd->ClipRect.y) * wdd.scale.y, 0.f, 65535.f));
|
||||
|
||||
if (gpu::isOriginBottomLeft()) {
|
||||
gpu::scissor(u32(maximum(pcmd->ClipRect.x - wdd.x, 0.0f)),
|
||||
wdd.h - u32(maximum(pcmd->ClipRect.y - wdd.y, 0.0f)) - h,
|
||||
u32(clamp(pcmd->ClipRect.z - pcmd->ClipRect.x, 0.f, 65535.f)),
|
||||
u32(clamp(pcmd->ClipRect.w - pcmd->ClipRect.y, 0.f, 65535.f)));
|
||||
gpu::scissor(u32(maximum((pcmd->ClipRect.x - wdd.x) * wdd.scale.x, 0.0f)),
|
||||
wdd.h - u32(maximum((pcmd->ClipRect.y - wdd.y) * wdd.scale.y, 0.0f)) - h,
|
||||
u32(clamp((pcmd->ClipRect.z - pcmd->ClipRect.x) * wdd.scale.x, 0.f, 65535.f)),
|
||||
u32(clamp((pcmd->ClipRect.w - pcmd->ClipRect.y) * wdd.scale.y, 0.f, 65535.f)));
|
||||
} else {
|
||||
gpu::scissor(u32(maximum(pcmd->ClipRect.x - wdd.x, 0.0f)),
|
||||
u32(maximum(pcmd->ClipRect.y - wdd.y, 0.0f)),
|
||||
u32(clamp(pcmd->ClipRect.z - pcmd->ClipRect.x, 0.f, 65535.f)),
|
||||
u32(clamp(pcmd->ClipRect.w - pcmd->ClipRect.y, 0.f, 65535.f)));
|
||||
gpu::scissor(u32(maximum((pcmd->ClipRect.x - wdd.x) * wdd.scale.x, 0.0f)),
|
||||
u32(maximum((pcmd->ClipRect.y - wdd.y) * wdd.scale.y, 0.0f)),
|
||||
u32(clamp((pcmd->ClipRect.z - pcmd->ClipRect.x) * wdd.scale.x, 0.f, 65535.f)),
|
||||
u32(clamp((pcmd->ClipRect.w - pcmd->ClipRect.y) * wdd.scale.y, 0.f, 65535.f)));
|
||||
}
|
||||
|
||||
gpu::drawElements(gpu::PrimitiveType::TRIANGLES, elem_offset * sizeof(u32) + cmd_list.idx_buffer.offset, pcmd->ElemCount, gpu::DataType::U32);
|
||||
|
@ -4495,19 +4514,18 @@ struct EditorUIRenderPlugin final : StudioApp::GUIPlugin
|
|||
vb_offset = 0;
|
||||
ib_offset = 0;
|
||||
for (WindowDrawData& dd : window_draw_data) {
|
||||
gpu::setCurrentWindow(dd.window);
|
||||
gpu::setFramebuffer(nullptr, 0, gpu::INVALID_TEXTURE, gpu::FramebufferFlags::NONE);
|
||||
if (dd.render_target) {
|
||||
gpu::setFramebuffer(&dd.render_target, 1, gpu::INVALID_TEXTURE, gpu::FramebufferFlags::NONE);
|
||||
}
|
||||
else {
|
||||
gpu::setCurrentWindow(dd.window);
|
||||
gpu::setFramebuffer(nullptr, 0, gpu::INVALID_TEXTURE, gpu::FramebufferFlags::NONE);
|
||||
}
|
||||
gpu::viewport(0, 0, dd.w, dd.h);
|
||||
|
||||
const Vec4 canvas_mtx[] = {
|
||||
Vec4(2.f / dd.w, 0, -1 + (float)-dd.x * 2.f / dd.w, 0),
|
||||
Vec4(0, -2.f / dd.h, 1 + (float)dd.y * 2.f / dd.h, 0)
|
||||
};
|
||||
gpu::update(ub, &canvas_mtx, sizeof(canvas_mtx));
|
||||
gpu::bindUniformBuffer(UniformBuffer::DRAWCALL, ub, 0, sizeof(canvas_mtx));
|
||||
gpu::bindUniformBuffer(UniformBuffer::DRAWCALL, dd.ub.buffer, dd.ub.offset, dd.ub.size);
|
||||
|
||||
const float clear_color[] = {0.2f, 0.2f, 0.2f, 1.f};
|
||||
gpu::clear(gpu::ClearFlags::COLOR | gpu::ClearFlags::DEPTH, clear_color, 1.0);
|
||||
gpu::clear(gpu::ClearFlags::COLOR | gpu::ClearFlags::DEPTH, &dd.clear_color.x, 1.0);
|
||||
if (dd.new_program) {
|
||||
const char* vs =
|
||||
R"#(
|
||||
|
@ -4557,10 +4575,10 @@ struct EditorUIRenderPlugin final : StudioApp::GUIPlugin
|
|||
Renderer* renderer;
|
||||
const gpu::TextureHandle* default_texture;
|
||||
Array<WindowDrawData> window_draw_data;
|
||||
gpu::BufferHandle ub;
|
||||
u32 ib_offset;
|
||||
u32 vb_offset;
|
||||
EditorUIRenderPlugin* plugin;
|
||||
bool should_setup = true;
|
||||
};
|
||||
|
||||
|
||||
|
@ -4585,10 +4603,6 @@ struct EditorUIRenderPlugin final : StudioApp::GUIPlugin
|
|||
m_texture = renderer->createTexture(width, height, 1, gpu::TextureFormat::RGBA8, gpu::TextureFlags::NO_MIPS, mem, "editor_font_atlas");
|
||||
ImGui::GetIO().Fonts->TexID = m_texture;
|
||||
|
||||
Renderer::MemRef ub_mem;
|
||||
ub_mem.size = sizeof(Vec4) * 2;
|
||||
m_ub = renderer->createBuffer(ub_mem, gpu::BufferFlags::UNIFORM_BUFFER);
|
||||
|
||||
m_render_interface.create(app, *renderer);
|
||||
app.setRenderInterface(m_render_interface.get());
|
||||
}
|
||||
|
@ -4600,13 +4614,64 @@ struct EditorUIRenderPlugin final : StudioApp::GUIPlugin
|
|||
shutdownImGui();
|
||||
PluginManager& plugin_manager = m_engine.getPluginManager();
|
||||
Renderer* renderer = (Renderer*)plugin_manager.getPlugin("renderer");
|
||||
renderer->destroy(m_ub);
|
||||
for (gpu::ProgramHandle program : m_programs) {
|
||||
renderer->destroy(program);
|
||||
}
|
||||
if (m_texture) renderer->destroy(m_texture);
|
||||
}
|
||||
|
||||
void render(gpu::TextureHandle rt, Vec2 rt_size, ImDrawData* draw_data, Vec2 scale) override {
|
||||
Renderer* renderer = static_cast<Renderer*>(m_engine.getPluginManager().getPlugin("renderer"));
|
||||
RenderJob& cmd = renderer->createJob<RenderJob>(renderer->getCurrentFrameAllocator());
|
||||
cmd.plugin = this;
|
||||
|
||||
LinearAllocator& allocator = renderer->getCurrentFrameAllocator();
|
||||
RenderJob::WindowDrawData& dd = cmd.window_draw_data.emplace(allocator);
|
||||
dd.w = u32(rt_size.x);
|
||||
dd.h = u32(rt_size.y);
|
||||
dd.x = i32(0);
|
||||
dd.y = i32(0);
|
||||
dd.window = nullptr;
|
||||
dd.program = cmd.getProgram(dd.window, dd.new_program);
|
||||
dd.cmd_lists.reserve(draw_data->CmdListsCount);
|
||||
dd.ub = renderer->allocUniform(sizeof(Vec4) * 2);
|
||||
dd.render_target = rt;
|
||||
dd.scale = scale;
|
||||
dd.clear_color = Vec4(0);
|
||||
|
||||
const Vec2 offset(0);
|
||||
const Vec4 canvas_mtx[] = {
|
||||
Vec4(2.f / dd.w * scale.x, 0, -1 + (float)-offset.x * 2.f / dd.w * scale.x, 0),
|
||||
Vec4(0, -2.f / dd.h * scale.y, 1 + (float)offset.y * 2.f / dd.h * scale.y, 0)
|
||||
};
|
||||
memcpy(dd.ub.ptr, &canvas_mtx, sizeof(canvas_mtx));
|
||||
|
||||
for (int i = 0; i < draw_data->CmdListsCount; ++i) {
|
||||
ImDrawList* cmd_list = draw_data->CmdLists[i];
|
||||
RenderJob::CmdList& out_cmd_list = dd.cmd_lists.emplace(allocator);
|
||||
|
||||
out_cmd_list.idx_buffer = renderer->allocTransient(cmd_list->IdxBuffer.size_in_bytes());
|
||||
memcpy(out_cmd_list.idx_buffer.ptr, &cmd_list->IdxBuffer[0], cmd_list->IdxBuffer.size_in_bytes());
|
||||
|
||||
out_cmd_list.vtx_buffer = renderer->allocTransient(cmd_list->VtxBuffer.size_in_bytes());
|
||||
memcpy(out_cmd_list.vtx_buffer.ptr, &cmd_list->VtxBuffer[0], cmd_list->VtxBuffer.size_in_bytes());
|
||||
|
||||
out_cmd_list.commands.reserve(cmd_list->CmdBuffer.size());
|
||||
for (int i = 0, c = cmd_list->CmdBuffer.size(); i < c; ++i) {
|
||||
if (cmd_list->CmdBuffer[i].UserCallback) {
|
||||
cmd_list->CmdBuffer[i].UserCallback(cmd_list, &cmd_list->CmdBuffer[i]);
|
||||
}
|
||||
else {
|
||||
out_cmd_list.commands.push(cmd_list->CmdBuffer[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
cmd.default_texture = &m_texture;
|
||||
cmd.should_setup = false;
|
||||
|
||||
renderer->queue(cmd, 0);
|
||||
}
|
||||
|
||||
void onWindowGUI() override {}
|
||||
|
||||
|
@ -4616,7 +4681,6 @@ struct EditorUIRenderPlugin final : StudioApp::GUIPlugin
|
|||
|
||||
void shutdownImGui()
|
||||
{
|
||||
imnodes::DestroyContext();
|
||||
ImGui::DestroyContext();
|
||||
}
|
||||
|
||||
|
@ -4626,7 +4690,6 @@ struct EditorUIRenderPlugin final : StudioApp::GUIPlugin
|
|||
Renderer* renderer = static_cast<Renderer*>(m_engine.getPluginManager().getPlugin("renderer"));
|
||||
RenderJob& cmd = renderer->createJob<RenderJob>(renderer->getCurrentFrameAllocator());
|
||||
cmd.plugin = this;
|
||||
cmd.ub = m_ub;
|
||||
|
||||
renderer->queue(cmd, 0);
|
||||
renderer->frame();
|
||||
|
@ -4637,7 +4700,6 @@ struct EditorUIRenderPlugin final : StudioApp::GUIPlugin
|
|||
Engine& m_engine;
|
||||
HashMap<void*, gpu::ProgramHandle> m_programs;
|
||||
gpu::TextureHandle m_texture;
|
||||
gpu::BufferHandle m_ub;
|
||||
Local<RenderInterfaceImpl> m_render_interface;
|
||||
};
|
||||
|
||||
|
@ -4935,7 +4997,7 @@ struct StudioAppPlugin : StudioApp::IPlugin
|
|||
m_env_probe_plugin.init();
|
||||
m_model_plugin.init();
|
||||
|
||||
m_particle_editor = ParticleEditor::create(m_app);
|
||||
m_particle_editor = ParticleEditor::create(m_app, m_editor_ui_render_plugin);
|
||||
m_app.addPlugin(*m_particle_editor.get());
|
||||
|
||||
m_particle_emitter_plugin.m_particle_editor = m_particle_editor.get();
|
||||
|
|
10
src/renderer/editor/render_plugins.h
Normal file
10
src/renderer/editor/render_plugins.h
Normal file
|
@ -0,0 +1,10 @@
|
|||
#pragma once
|
||||
|
||||
#include "../gpu/gpu.h"
|
||||
#include <imgui/imgui.h>
|
||||
|
||||
namespace Lumix {
|
||||
struct IImGuiRenderer {
|
||||
virtual void render(gpu::TextureHandle rt, Vec2 rt_size, ImDrawData* dd, Vec2 scale) = 0;
|
||||
};
|
||||
};
|
Loading…
Reference in a new issue