Using bits of the blueprints examples for the AnimGraphEditor.
This commit is contained in:
parent
a467715ce3
commit
5a6ac92a48
@ -80,8 +80,14 @@ target_sources(AnimTestbed PRIVATE
|
||||
src/main.cc
|
||||
src/embedded_fonts.h
|
||||
src/SkinnedMeshRenderer.cc
|
||||
src/AnimGraph/AnimGraphEditor.cc
|
||||
src/AnimGraph/AnimGraphEditor.h
|
||||
src/AnimGraphEditor/AnimGraphEditor.cc
|
||||
src/AnimGraphEditor/AnimGraphEditor.h
|
||||
src/AnimGraphEditor/utilities/builders.cpp
|
||||
src/AnimGraphEditor/utilities/builders.h
|
||||
src/AnimGraphEditor/utilities/drawing.cpp
|
||||
src/AnimGraphEditor/utilities/drawing.h
|
||||
src/AnimGraphEditor/utilities/widgets.cpp
|
||||
src/AnimGraphEditor/utilities/widgets.h
|
||||
src/Camera.c
|
||||
src/SkinnedMesh.cc
|
||||
src/SkinnedMesh.h
|
||||
@ -109,6 +115,10 @@ target_sources(AnimTestbed PRIVATE
|
||||
3rdparty/imgui-node-editor/imgui_extra_math.inl
|
||||
3rdparty/imgui-node-editor/crude_json.cpp
|
||||
3rdparty/imgui-node-editor/crude_json.h
|
||||
# ImGui Extension needed for blueprint nodes
|
||||
3rdparty/imgui/imgui_stacklayout.cpp
|
||||
3rdparty/imgui/imgui_stacklayout.h
|
||||
3rdparty/imgui/imgui_stacklayout_internal.h
|
||||
)
|
||||
|
||||
target_link_libraries(AnimTestbed AnimTestbedCode glfw ozz_base ozz_geometry ozz_animation ${OPENGL_LIBRARIES})
|
||||
|
0
media/fonts/Roboto-Medium.ttf
Normal file
0
media/fonts/Roboto-Medium.ttf
Normal file
@ -310,9 +310,6 @@ static bool sAnimGraphResourceBlendTreeFromJson(
|
||||
result_graph_resource->m_name = json_data["name"];
|
||||
result_graph_resource->m_position[0] = json_data["position"][0];
|
||||
result_graph_resource->m_position[1] = json_data["position"][1];
|
||||
assert(result_graph_resource->m_virtual_socket_accessor == nullptr);
|
||||
result_graph_resource->m_virtual_socket_accessor =
|
||||
AnimNodeDescriptorFactory("BlendTree", nullptr);
|
||||
|
||||
// Load nodes
|
||||
for (size_t i = 0, n = json_data["nodes"].size(); i < n; i++) {
|
||||
|
@ -56,8 +56,6 @@ struct BlendTreeResource {
|
||||
|
||||
void CleanupNodes() {
|
||||
for (AnimNodeResource* node_resource : m_nodes) {
|
||||
delete node_resource->m_virtual_socket_accessor;
|
||||
node_resource->m_virtual_socket_accessor = nullptr;
|
||||
delete node_resource;
|
||||
}
|
||||
|
||||
|
@ -7,11 +7,14 @@
|
||||
#include <sstream>
|
||||
|
||||
#include "3rdparty/imgui-node-editor/imgui_node_editor.h"
|
||||
#include "AnimGraphResource.h"
|
||||
#include "AnimGraphEditor/utilities/builders.h"
|
||||
#include "AnimGraphEditor/utilities/drawing.h"
|
||||
#include "AnimGraphEditor/utilities/widgets.h"
|
||||
#include "SkinnedMesh.h"
|
||||
#include "imgui.h"
|
||||
#include "imnodes.h"
|
||||
#include "misc/cpp/imgui_stdlib.h"
|
||||
#include "src/AnimGraph/AnimGraphResource.h"
|
||||
|
||||
struct EditorState {
|
||||
AnimGraphResource* rootGraphResource = nullptr;
|
||||
@ -25,6 +28,8 @@ struct EditorState {
|
||||
|
||||
static EditorState sEditorState;
|
||||
|
||||
constexpr int cPinIconSize = 24;
|
||||
|
||||
ImNodesPinShape sGetSocketShapeFromSocketType(const SocketType& socket_type) {
|
||||
switch (socket_type) {
|
||||
case SocketType::SocketTypeAnimation:
|
||||
@ -46,12 +51,47 @@ ImNodesPinShape sGetSocketShapeFromSocketType(const SocketType& socket_type) {
|
||||
return ImNodesPinShape_Quad;
|
||||
}
|
||||
|
||||
void DrawSocketIcon(const SocketType& socket_type, bool is_connected = false) {
|
||||
ax::Drawing::IconType iconType;
|
||||
ImColor color = ImColor(255, 255, 255);
|
||||
color.Value.w = 1;
|
||||
switch (socket_type) {
|
||||
case SocketType::SocketTypeAnimation:
|
||||
iconType = ax::Drawing::IconType::Flow;
|
||||
break;
|
||||
case SocketType::SocketTypeInt:
|
||||
iconType = ax::Drawing::IconType::Circle;
|
||||
break;
|
||||
case SocketType::SocketTypeFloat:
|
||||
iconType = ax::Drawing::IconType::Circle;
|
||||
break;
|
||||
case SocketType::SocketTypeVec3:
|
||||
iconType = ax::Drawing::IconType::Circle;
|
||||
break;
|
||||
case SocketType::SocketTypeQuat:
|
||||
iconType = ax::Drawing::IconType::Circle;
|
||||
break;
|
||||
case SocketType::SocketTypeBool:
|
||||
iconType = ax::Drawing::IconType::Circle;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
ax::Widgets::Icon(
|
||||
ImVec2(
|
||||
static_cast<float>(cPinIconSize),
|
||||
static_cast<float>(cPinIconSize)),
|
||||
iconType,
|
||||
is_connected,
|
||||
color,
|
||||
ImColor(32, 32, 32, 1));
|
||||
}
|
||||
|
||||
bool NodeSocketEditor(Socket& socket) {
|
||||
bool modified = false;
|
||||
int mode_current = static_cast<int>(socket.m_type);
|
||||
if (ImGui::InputText("Name", &socket.m_name)) {
|
||||
modified = true;
|
||||
}
|
||||
ImGui::Columns(3);
|
||||
if (ImGui::Combo(
|
||||
"Type",
|
||||
&mode_current,
|
||||
@ -62,6 +102,13 @@ bool NodeSocketEditor(Socket& socket) {
|
||||
modified = true;
|
||||
}
|
||||
|
||||
ImGui::Columns(2);
|
||||
if (ImGui::InputText("Name", &socket.m_name)) {
|
||||
modified = true;
|
||||
}
|
||||
|
||||
ImGui::Columns(1);
|
||||
|
||||
return modified;
|
||||
}
|
||||
|
||||
@ -234,26 +281,39 @@ void AnimGraphEditorRenderSidebar(
|
||||
node_resource->m_virtual_socket_accessor->m_inputs;
|
||||
|
||||
std::vector<Socket>::iterator iter = outputs.begin();
|
||||
ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2(2, 2));
|
||||
if (ImGui::BeginTable(
|
||||
"split",
|
||||
2,
|
||||
ImGuiTableFlags_BordersOuter | ImGuiTableFlags_Resizable)) {
|
||||
while (iter != outputs.end()) {
|
||||
Socket& output = *iter;
|
||||
ImGui::PushID(&output);
|
||||
if (NodeSocketEditor(output)) {
|
||||
AnimGraphResource* current_graph_resource =
|
||||
sEditorState.hierarchyStack[sEditorState.hierarchyStackIndex];
|
||||
current_graph_resource->m_socket_accessor->m_inputs = outputs;
|
||||
Socket& socket = *iter;
|
||||
ImGui::PushID(&socket);
|
||||
|
||||
ImGui::TableNextRow();
|
||||
ImGui::TableSetColumnIndex(0);
|
||||
ImGui::AlignTextToFramePadding();
|
||||
bool node_open = ImGui::TreeNode(socket.m_name.c_str());
|
||||
ImGui::TableSetColumnIndex(1);
|
||||
|
||||
if (node_open) {
|
||||
int mode_current = static_cast<int>(socket.m_type);
|
||||
if (ImGui::Combo(
|
||||
"Type",
|
||||
&mode_current,
|
||||
SocketTypeNames,
|
||||
sizeof(SocketTypeNames) / sizeof(char*))) {
|
||||
socket.m_type = static_cast<SocketType>(mode_current);
|
||||
}
|
||||
ImGui::TreePop();
|
||||
}
|
||||
|
||||
if (ImGui::Button("X")) {
|
||||
RemoveBlendTreeConnectionsForSocket(
|
||||
blend_tree_resource,
|
||||
node_resource,
|
||||
output);
|
||||
iter = outputs.erase(iter);
|
||||
} else {
|
||||
ImGui::PopID();
|
||||
iter++;
|
||||
}
|
||||
ImGui::PopID();
|
||||
ImGui::EndTable();
|
||||
}
|
||||
ImGui::PopStyleVar();
|
||||
|
||||
if (ImGui::Button("+")) {
|
||||
AnimGraphResource* current_graph_resource =
|
||||
@ -306,9 +366,8 @@ void AnimGraphEditorClear() {
|
||||
}
|
||||
|
||||
if (sEditorState.rootGraphResource) {
|
||||
delete sEditorState.rootGraphResource->m_virtual_socket_accessor;
|
||||
}
|
||||
delete sEditorState.rootGraphResource;
|
||||
}
|
||||
|
||||
sEditorState.rootGraphResource = new AnimGraphResource();
|
||||
sEditorState.rootGraphResource->m_name = "Root";
|
||||
@ -324,12 +383,68 @@ void AnimGraphEditorClear() {
|
||||
sEditorState.hierarchyStackIndex = 0;
|
||||
}
|
||||
|
||||
void AnimGraphEditorUpdate(ax::NodeEditor::EditorContext* context) {
|
||||
ax::NodeEditor::SetCurrentEditor(context);
|
||||
void BlendTreeEditorNodePopup() {
|
||||
const bool open_popup = ImGui::IsMouseReleased(ImGuiMouseButton_Right);
|
||||
|
||||
//
|
||||
// Menu bar
|
||||
//
|
||||
if (open_popup && ImGui::IsWindowHovered()) {
|
||||
ax::NodeEditor::Suspend();
|
||||
ImGui::OpenPopup("add node");
|
||||
ax::NodeEditor::Resume();
|
||||
sEditorState.mousePopupStart = ImGui::GetMousePos();
|
||||
}
|
||||
|
||||
ax::NodeEditor::Suspend();
|
||||
ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2(8.f, 8.f));
|
||||
if (ImGui::BeginPopup("add node")) {
|
||||
std::string node_type_name = "";
|
||||
if (ImGui::MenuItem("AnimSampler")) {
|
||||
node_type_name = "AnimSampler";
|
||||
}
|
||||
|
||||
if (ImGui::MenuItem("Blend2")) {
|
||||
node_type_name = "Blend2";
|
||||
}
|
||||
|
||||
if (ImGui::MenuItem("SpeedScale")) {
|
||||
node_type_name = "SpeedScale";
|
||||
}
|
||||
|
||||
if (ImGui::MenuItem("LockTranslationNode")) {
|
||||
node_type_name = "LockTranslationNode";
|
||||
}
|
||||
|
||||
if (ImGui::MenuItem("MathAddNode")) {
|
||||
node_type_name = "MathAddNode";
|
||||
}
|
||||
|
||||
if (ImGui::MenuItem("MathFloatToVec3Node")) {
|
||||
node_type_name = "MathFloatToVec3Node";
|
||||
}
|
||||
|
||||
if (ImGui::MenuItem("ConstScalarNode")) {
|
||||
node_type_name = "ConstScalarNode";
|
||||
}
|
||||
|
||||
if (ImGui::MenuItem("BlendTree")) {
|
||||
node_type_name = "BlendTree";
|
||||
}
|
||||
|
||||
if (!node_type_name.empty()) {
|
||||
AnimNodeResource* node_resource = AnimNodeResourceFactory(node_type_name);
|
||||
ax::NodeEditor::SetNodePosition(
|
||||
ax::NodeEditor::NodeId(node_resource),
|
||||
sEditorState.mousePopupStart);
|
||||
sEditorState.hierarchyStack[sEditorState.hierarchyStackIndex]
|
||||
->m_blend_tree_resource.AddNode(node_resource);
|
||||
}
|
||||
|
||||
ImGui::EndPopup();
|
||||
}
|
||||
ImGui::PopStyleVar();
|
||||
ax::NodeEditor::Resume();
|
||||
}
|
||||
|
||||
void AnimGraphEditorMenuBar() {
|
||||
ImGui::BeginMenuBar();
|
||||
if (ImGui::Button("Save")) {
|
||||
sEditorState.rootGraphResource->SaveToFile("editor_graph.json");
|
||||
@ -358,10 +473,9 @@ void AnimGraphEditorUpdate(ax::NodeEditor::EditorContext* context) {
|
||||
}
|
||||
|
||||
ImGui::EndMenuBar();
|
||||
}
|
||||
|
||||
//
|
||||
// Breadcrumb navigation
|
||||
//
|
||||
void AnimGraphEditorBreadcrumbNavigation() {
|
||||
for (size_t i = 0, n = sEditorState.hierarchyStack.size(); i < n; i++) {
|
||||
AnimGraphResource* graph_resource =
|
||||
dynamic_cast<AnimGraphResource*>(sEditorState.hierarchyStack[i]);
|
||||
@ -389,100 +503,10 @@ void AnimGraphEditorUpdate(ax::NodeEditor::EditorContext* context) {
|
||||
ImGui::SameLine();
|
||||
}
|
||||
}
|
||||
|
||||
ImGui::Columns(2);
|
||||
|
||||
//
|
||||
// Node editor canvas
|
||||
//
|
||||
ax::NodeEditor::Begin("Graph Editor");
|
||||
|
||||
AnimGraphResource* current_graph =
|
||||
sEditorState.hierarchyStack[sEditorState.hierarchyStackIndex];
|
||||
BlendTreeResource& current_blend_tree = current_graph->m_blend_tree_resource;
|
||||
|
||||
for (size_t node_index = 0, n = current_blend_tree.GetNumNodes();
|
||||
node_index < n;
|
||||
node_index++) {
|
||||
AnimNodeResource* node_resource = current_blend_tree.GetNode(node_index);
|
||||
ax::NodeEditor::NodeId node_id(node_resource);
|
||||
|
||||
if (sEditorState.isGraphLoadedThisFrame) {
|
||||
ax::NodeEditor::SetNodePosition(
|
||||
node_id,
|
||||
ImVec2(node_resource->m_position[0], node_resource->m_position[1]));
|
||||
}
|
||||
ax::NodeEditor::BeginNode(node_id);
|
||||
ImGui::Text("%s", node_resource->m_node_type_name.c_str());
|
||||
|
||||
// Inputs
|
||||
std::vector<Socket> node_inputs =
|
||||
current_blend_tree.GetNodeInputSockets(node_resource);
|
||||
for (size_t j = 0, ni = node_inputs.size(); j < ni; j++) {
|
||||
Socket& socket = node_inputs[j];
|
||||
ax::NodeEditor::BeginPin(
|
||||
NodeIndexAndSocketIndexToInputPinId(
|
||||
static_cast<int>(node_index),
|
||||
static_cast<int>(j)),
|
||||
ax::NodeEditor::PinKind::Input);
|
||||
ImGui::Text("%s", socket.m_name.c_str());
|
||||
ax::NodeEditor::EndPin();
|
||||
}
|
||||
|
||||
// Outputs
|
||||
std::vector<Socket> node_outputs =
|
||||
current_blend_tree.GetNodeOutputSockets(node_resource);
|
||||
for (size_t j = 0, ni = node_outputs.size(); j < ni; j++) {
|
||||
Socket& socket = node_outputs[j];
|
||||
ax::NodeEditor::BeginPin(
|
||||
NodeIndexAndSocketIndexToOutputPinId(
|
||||
static_cast<int>(node_index),
|
||||
static_cast<int>(j)),
|
||||
ax::NodeEditor::PinKind::Output);
|
||||
ImGui::Text("%s", socket.m_name.c_str());
|
||||
ax::NodeEditor::EndPin();
|
||||
}
|
||||
|
||||
ax::NodeEditor::EndNode();
|
||||
|
||||
ImVec2 node_position = ax::NodeEditor::GetNodePosition(node_id);
|
||||
node_resource->m_position[0] = node_position.x;
|
||||
node_resource->m_position[1] = node_position.y;
|
||||
}
|
||||
|
||||
int link_id = 0;
|
||||
for (size_t connection_id = 0, n = current_blend_tree.GetNumConnections();
|
||||
connection_id < n;
|
||||
connection_id++) {
|
||||
const BlendTreeConnectionResource* connection_resource =
|
||||
current_blend_tree.GetConnection(connection_id);
|
||||
|
||||
const AnimNodeResource* source_node_resource =
|
||||
current_blend_tree.GetNode(connection_resource->source_node_index);
|
||||
int source_socket_index =
|
||||
source_node_resource->m_virtual_socket_accessor->GetOutputIndex(
|
||||
connection_resource->source_socket_name.c_str());
|
||||
|
||||
const AnimNodeResource* target_node_resource =
|
||||
current_blend_tree.GetNode(connection_resource->target_node_index);
|
||||
int target_socket_index =
|
||||
target_node_resource->m_virtual_socket_accessor->GetInputIndex(
|
||||
connection_resource->target_socket_name.c_str());
|
||||
|
||||
int source_socket_pin_id = NodeIndexAndSocketIndexToOutputPinId(
|
||||
static_cast<int>(connection_resource->source_node_index),
|
||||
source_socket_index);
|
||||
int target_socket_pin_id = NodeIndexAndSocketIndexToInputPinId(
|
||||
static_cast<int>(connection_resource->target_node_index),
|
||||
target_socket_index);
|
||||
|
||||
ax::NodeEditor::Link(
|
||||
ax::NodeEditor::LinkId(connection_resource),
|
||||
source_socket_pin_id,
|
||||
target_socket_pin_id);
|
||||
}
|
||||
|
||||
// Create Connections
|
||||
void HandleConnectionCreation(
|
||||
BlendTreeResource& current_blend_tree) { // Create Connections
|
||||
if (ax::NodeEditor::BeginCreate()) {
|
||||
ax::NodeEditor::PinId input_pin_id, output_pin_id;
|
||||
if (ax::NodeEditor::QueryNewLink(&input_pin_id, &output_pin_id)) {
|
||||
@ -549,70 +573,138 @@ void AnimGraphEditorUpdate(ax::NodeEditor::EditorContext* context) {
|
||||
}
|
||||
}
|
||||
ax::NodeEditor::EndCreate();
|
||||
|
||||
// Popup menu
|
||||
{
|
||||
const bool open_popup = ImGui::IsMouseReleased(ImGuiMouseButton_Right);
|
||||
|
||||
if (open_popup && ImGui::IsWindowHovered()) {
|
||||
ax::NodeEditor::Suspend();
|
||||
ImGui::OpenPopup("add node");
|
||||
ax::NodeEditor::Resume();
|
||||
sEditorState.mousePopupStart = ImGui::GetMousePos();
|
||||
}
|
||||
|
||||
ax::NodeEditor::Suspend();
|
||||
ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2(8.f, 8.f));
|
||||
if (ImGui::BeginPopup("add node")) {
|
||||
std::string node_type_name = "";
|
||||
if (ImGui::MenuItem("AnimSampler")) {
|
||||
node_type_name = "AnimSampler";
|
||||
}
|
||||
void BlendTreeRenderNodes(
|
||||
BlendTreeResource& current_blend_tree,
|
||||
ax::NodeEditor::Utilities::BlueprintNodeBuilder& builder) {
|
||||
for (size_t node_index = 0, n = current_blend_tree.GetNumNodes();
|
||||
node_index < n;
|
||||
node_index++) {
|
||||
AnimNodeResource* node_resource = current_blend_tree.GetNode(node_index);
|
||||
|
||||
if (ImGui::MenuItem("Blend2")) {
|
||||
node_type_name = "Blend2";
|
||||
}
|
||||
ax::NodeEditor::NodeId node_id(node_resource);
|
||||
|
||||
if (ImGui::MenuItem("SpeedScale")) {
|
||||
node_type_name = "SpeedScale";
|
||||
}
|
||||
builder.Begin(node_id);
|
||||
|
||||
if (ImGui::MenuItem("LockTranslationNode")) {
|
||||
node_type_name = "LockTranslationNode";
|
||||
}
|
||||
|
||||
if (ImGui::MenuItem("MathAddNode")) {
|
||||
node_type_name = "MathAddNode";
|
||||
}
|
||||
|
||||
if (ImGui::MenuItem("MathFloatToVec3Node")) {
|
||||
node_type_name = "MathFloatToVec3Node";
|
||||
}
|
||||
|
||||
if (ImGui::MenuItem("ConstScalarNode")) {
|
||||
node_type_name = "ConstScalarNode";
|
||||
}
|
||||
|
||||
if (ImGui::MenuItem("BlendTree")) {
|
||||
node_type_name = "BlendTree";
|
||||
}
|
||||
|
||||
if (!node_type_name.empty()) {
|
||||
AnimNodeResource* node_resource =
|
||||
AnimNodeResourceFactory(node_type_name);
|
||||
if (sEditorState.isGraphLoadedThisFrame) {
|
||||
ax::NodeEditor::SetNodePosition(
|
||||
ax::NodeEditor::NodeId(node_resource),
|
||||
sEditorState.mousePopupStart);
|
||||
sEditorState.hierarchyStack[sEditorState.hierarchyStackIndex]
|
||||
->m_blend_tree_resource.AddNode(node_resource);
|
||||
node_id,
|
||||
ImVec2(node_resource->m_position[0], node_resource->m_position[1]));
|
||||
}
|
||||
|
||||
ImGui::EndPopup();
|
||||
builder.Header();
|
||||
ImGui::Text("%s", node_resource->m_node_type_name.c_str());
|
||||
ImGui::Spring(0);
|
||||
builder.EndHeader();
|
||||
|
||||
// Inputs
|
||||
std::vector<Socket> node_inputs =
|
||||
current_blend_tree.GetNodeInputSockets(node_resource);
|
||||
for (size_t j = 0, ni = node_inputs.size(); j < ni; j++) {
|
||||
Socket& socket = node_inputs[j];
|
||||
builder.Input(NodeIndexAndSocketIndexToInputPinId(
|
||||
static_cast<int>(node_index),
|
||||
static_cast<int>(j)));
|
||||
|
||||
DrawSocketIcon(
|
||||
socket.m_type,
|
||||
current_blend_tree.IsSocketConnected(node_resource, socket.m_name));
|
||||
ImGui::Spring(0);
|
||||
|
||||
//ImGui::PushItemWidth(100.0f);
|
||||
ImGui::Text("%s", socket.m_name.c_str());
|
||||
//ImGui::PopItemWidth();
|
||||
builder.EndInput();
|
||||
}
|
||||
ImGui::PopStyleVar();
|
||||
ax::NodeEditor::Resume();
|
||||
|
||||
// Outputs
|
||||
std::vector<Socket> node_outputs =
|
||||
current_blend_tree.GetNodeOutputSockets(node_resource);
|
||||
for (size_t j = 0, ni = node_outputs.size(); j < ni; j++) {
|
||||
Socket& socket = node_outputs[j];
|
||||
builder.Output(NodeIndexAndSocketIndexToOutputPinId(
|
||||
static_cast<int>(node_index),
|
||||
static_cast<int>(j)));
|
||||
|
||||
ImGui::Text("%s", socket.m_name.c_str());
|
||||
|
||||
ImGui::Spring(0);
|
||||
DrawSocketIcon(
|
||||
socket.m_type,
|
||||
current_blend_tree.IsSocketConnected(node_resource, socket.m_name));
|
||||
|
||||
builder.EndOutput();
|
||||
}
|
||||
|
||||
ImVec2 node_position = ax::NodeEditor::GetNodePosition(node_id);
|
||||
node_resource->m_position[0] = node_position.x;
|
||||
node_resource->m_position[1] = node_position.y;
|
||||
|
||||
builder.End();
|
||||
}
|
||||
}
|
||||
|
||||
void BlendTreeRenderConnections(BlendTreeResource& current_blend_tree) {
|
||||
for (size_t connection_id = 0, n = current_blend_tree.GetNumConnections();
|
||||
connection_id < n;
|
||||
connection_id++) {
|
||||
const BlendTreeConnectionResource* connection_resource =
|
||||
current_blend_tree.GetConnection(connection_id);
|
||||
|
||||
const AnimNodeResource* source_node_resource =
|
||||
current_blend_tree.GetNode(connection_resource->source_node_index);
|
||||
int source_socket_index =
|
||||
source_node_resource->m_virtual_socket_accessor->GetOutputIndex(
|
||||
connection_resource->source_socket_name.c_str());
|
||||
|
||||
const AnimNodeResource* target_node_resource =
|
||||
current_blend_tree.GetNode(connection_resource->target_node_index);
|
||||
int target_socket_index =
|
||||
target_node_resource->m_virtual_socket_accessor->GetInputIndex(
|
||||
connection_resource->target_socket_name.c_str());
|
||||
|
||||
int source_socket_pin_id = NodeIndexAndSocketIndexToOutputPinId(
|
||||
static_cast<int>(connection_resource->source_node_index),
|
||||
source_socket_index);
|
||||
int target_socket_pin_id = NodeIndexAndSocketIndexToInputPinId(
|
||||
static_cast<int>(connection_resource->target_node_index),
|
||||
target_socket_index);
|
||||
|
||||
ax::NodeEditor::Link(
|
||||
ax::NodeEditor::LinkId(connection_resource),
|
||||
source_socket_pin_id,
|
||||
target_socket_pin_id);
|
||||
}
|
||||
}
|
||||
void AnimGraphEditorUpdate(ax::NodeEditor::EditorContext* context) {
|
||||
ax::NodeEditor::SetCurrentEditor(context);
|
||||
|
||||
AnimGraphEditorMenuBar();
|
||||
|
||||
AnimGraphEditorBreadcrumbNavigation();
|
||||
|
||||
ImGui::Columns(2);
|
||||
|
||||
//
|
||||
// Node editor canvas
|
||||
//
|
||||
ax::NodeEditor::Begin("Graph Editor");
|
||||
|
||||
AnimGraphResource* current_graph =
|
||||
sEditorState.hierarchyStack[sEditorState.hierarchyStackIndex];
|
||||
BlendTreeResource& current_blend_tree = current_graph->m_blend_tree_resource;
|
||||
|
||||
ax::NodeEditor::Utilities::BlueprintNodeBuilder builder;
|
||||
|
||||
BlendTreeRenderNodes(current_blend_tree, builder);
|
||||
|
||||
BlendTreeRenderConnections(current_blend_tree);
|
||||
|
||||
HandleConnectionCreation(current_blend_tree);
|
||||
|
||||
BlendTreeEditorNodePopup();
|
||||
|
||||
ax::NodeEditor::End();
|
||||
|
||||
//
|
310
src/AnimGraphEditor/utilities/builders.cpp
Normal file
310
src/AnimGraphEditor/utilities/builders.cpp
Normal file
@ -0,0 +1,310 @@
|
||||
//------------------------------------------------------------------------------
|
||||
// LICENSE
|
||||
// This software is dual-licensed to the public domain and under the following
|
||||
// license: you are granted a perpetual, irrevocable license to copy, modify,
|
||||
// publish, and distribute this file as you see fit.
|
||||
//
|
||||
// CREDITS
|
||||
// Written by Michal Cichon
|
||||
//------------------------------------------------------------------------------
|
||||
# define IMGUI_DEFINE_MATH_OPERATORS
|
||||
# include "builders.h"
|
||||
# include <imgui_internal.h>
|
||||
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
namespace ed = ax::NodeEditor;
|
||||
namespace util = ax::NodeEditor::Utilities;
|
||||
|
||||
util::BlueprintNodeBuilder::BlueprintNodeBuilder(ImTextureID texture, int textureWidth, int textureHeight):
|
||||
HeaderTextureId(texture),
|
||||
HeaderTextureWidth(textureWidth),
|
||||
HeaderTextureHeight(textureHeight),
|
||||
CurrentNodeId(0),
|
||||
CurrentStage(Stage::Invalid),
|
||||
HasHeader(false)
|
||||
{
|
||||
}
|
||||
|
||||
void util::BlueprintNodeBuilder::Begin(ed::NodeId id)
|
||||
{
|
||||
HasHeader = false;
|
||||
HeaderMin = HeaderMax = ImVec2();
|
||||
|
||||
ed::PushStyleVar(StyleVar_NodePadding, ImVec4(8, 4, 8, 8));
|
||||
|
||||
ed::BeginNode(id);
|
||||
|
||||
ImGui::PushID(id.AsPointer());
|
||||
CurrentNodeId = id;
|
||||
|
||||
SetStage(Stage::Begin);
|
||||
}
|
||||
|
||||
void util::BlueprintNodeBuilder::End()
|
||||
{
|
||||
SetStage(Stage::End);
|
||||
|
||||
ed::EndNode();
|
||||
|
||||
if (ImGui::IsItemVisible())
|
||||
{
|
||||
auto alpha = static_cast<int>(255 * ImGui::GetStyle().Alpha);
|
||||
|
||||
auto drawList = ed::GetNodeBackgroundDrawList(CurrentNodeId);
|
||||
|
||||
const auto halfBorderWidth = ed::GetStyle().NodeBorderWidth * 0.5f;
|
||||
|
||||
auto headerColor = IM_COL32(0, 0, 0, alpha) | (HeaderColor & IM_COL32(255, 255, 255, 0));
|
||||
if ((HeaderMax.x > HeaderMin.x) && (HeaderMax.y > HeaderMin.y) && HeaderTextureId)
|
||||
{
|
||||
const auto uv = ImVec2(
|
||||
(HeaderMax.x - HeaderMin.x) / (float)(4.0f * HeaderTextureWidth),
|
||||
(HeaderMax.y - HeaderMin.y) / (float)(4.0f * HeaderTextureHeight));
|
||||
|
||||
drawList->AddImageRounded(HeaderTextureId,
|
||||
HeaderMin - ImVec2(8 - halfBorderWidth, 4 - halfBorderWidth),
|
||||
HeaderMax + ImVec2(8 - halfBorderWidth, 0),
|
||||
ImVec2(0.0f, 0.0f), uv,
|
||||
#if IMGUI_VERSION_NUM > 18101
|
||||
headerColor, GetStyle().NodeRounding, ImDrawFlags_RoundCornersTop);
|
||||
#else
|
||||
headerColor, GetStyle().NodeRounding, 1 | 2);
|
||||
#endif
|
||||
|
||||
if (ContentMin.y > HeaderMax.y)
|
||||
{
|
||||
drawList->AddLine(
|
||||
ImVec2(HeaderMin.x - (8 - halfBorderWidth), HeaderMax.y - 0.5f),
|
||||
ImVec2(HeaderMax.x + (8 - halfBorderWidth), HeaderMax.y - 0.5f),
|
||||
ImColor(255, 255, 255, 96 * alpha / (3 * 255)), 1.0f);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
CurrentNodeId = 0;
|
||||
|
||||
ImGui::PopID();
|
||||
|
||||
ed::PopStyleVar();
|
||||
|
||||
SetStage(Stage::Invalid);
|
||||
}
|
||||
|
||||
void util::BlueprintNodeBuilder::Header(const ImVec4& color)
|
||||
{
|
||||
HeaderColor = ImColor(color);
|
||||
SetStage(Stage::Header);
|
||||
}
|
||||
|
||||
void util::BlueprintNodeBuilder::EndHeader()
|
||||
{
|
||||
SetStage(Stage::Content);
|
||||
}
|
||||
|
||||
void util::BlueprintNodeBuilder::Input(ed::PinId id)
|
||||
{
|
||||
if (CurrentStage == Stage::Begin)
|
||||
SetStage(Stage::Content);
|
||||
|
||||
const auto applyPadding = (CurrentStage == Stage::Input);
|
||||
|
||||
SetStage(Stage::Input);
|
||||
|
||||
if (applyPadding)
|
||||
ImGui::Spring(0);
|
||||
|
||||
Pin(id, PinKind::Input);
|
||||
|
||||
ImGui::BeginHorizontal(id.AsPointer());
|
||||
}
|
||||
|
||||
void util::BlueprintNodeBuilder::EndInput()
|
||||
{
|
||||
ImGui::EndHorizontal();
|
||||
|
||||
EndPin();
|
||||
}
|
||||
|
||||
void util::BlueprintNodeBuilder::Middle()
|
||||
{
|
||||
if (CurrentStage == Stage::Begin)
|
||||
SetStage(Stage::Content);
|
||||
|
||||
SetStage(Stage::Middle);
|
||||
}
|
||||
|
||||
void util::BlueprintNodeBuilder::Output(ed::PinId id)
|
||||
{
|
||||
if (CurrentStage == Stage::Begin)
|
||||
SetStage(Stage::Content);
|
||||
|
||||
const auto applyPadding = (CurrentStage == Stage::Output);
|
||||
|
||||
SetStage(Stage::Output);
|
||||
|
||||
if (applyPadding)
|
||||
ImGui::Spring(0);
|
||||
|
||||
Pin(id, PinKind::Output);
|
||||
|
||||
ImGui::BeginHorizontal(id.AsPointer());
|
||||
}
|
||||
|
||||
void util::BlueprintNodeBuilder::EndOutput()
|
||||
{
|
||||
ImGui::EndHorizontal();
|
||||
|
||||
EndPin();
|
||||
}
|
||||
|
||||
bool util::BlueprintNodeBuilder::SetStage(Stage stage)
|
||||
{
|
||||
if (stage == CurrentStage)
|
||||
return false;
|
||||
|
||||
auto oldStage = CurrentStage;
|
||||
CurrentStage = stage;
|
||||
|
||||
ImVec2 cursor;
|
||||
switch (oldStage)
|
||||
{
|
||||
case Stage::Begin:
|
||||
break;
|
||||
|
||||
case Stage::Header:
|
||||
ImGui::EndHorizontal();
|
||||
HeaderMin = ImGui::GetItemRectMin();
|
||||
HeaderMax = ImGui::GetItemRectMax();
|
||||
|
||||
// spacing between header and content
|
||||
ImGui::Spring(0, ImGui::GetStyle().ItemSpacing.y * 2.0f);
|
||||
|
||||
break;
|
||||
|
||||
case Stage::Content:
|
||||
break;
|
||||
|
||||
case Stage::Input:
|
||||
ed::PopStyleVar(2);
|
||||
|
||||
ImGui::Spring(1, 0);
|
||||
ImGui::EndVertical();
|
||||
|
||||
// #debug
|
||||
// ImGui::GetWindowDrawList()->AddRect(
|
||||
// ImGui::GetItemRectMin(), ImGui::GetItemRectMax(), IM_COL32(255, 0, 0, 255));
|
||||
|
||||
break;
|
||||
|
||||
case Stage::Middle:
|
||||
ImGui::EndVertical();
|
||||
|
||||
// #debug
|
||||
// ImGui::GetWindowDrawList()->AddRect(
|
||||
// ImGui::GetItemRectMin(), ImGui::GetItemRectMax(), IM_COL32(255, 0, 0, 255));
|
||||
|
||||
break;
|
||||
|
||||
case Stage::Output:
|
||||
ed::PopStyleVar(2);
|
||||
|
||||
ImGui::Spring(1, 0);
|
||||
ImGui::EndVertical();
|
||||
|
||||
// #debug
|
||||
// ImGui::GetWindowDrawList()->AddRect(
|
||||
// ImGui::GetItemRectMin(), ImGui::GetItemRectMax(), IM_COL32(255, 0, 0, 255));
|
||||
|
||||
break;
|
||||
|
||||
case Stage::End:
|
||||
break;
|
||||
|
||||
case Stage::Invalid:
|
||||
break;
|
||||
}
|
||||
|
||||
switch (stage)
|
||||
{
|
||||
case Stage::Begin:
|
||||
ImGui::BeginVertical("node");
|
||||
break;
|
||||
|
||||
case Stage::Header:
|
||||
HasHeader = true;
|
||||
|
||||
ImGui::BeginHorizontal("header");
|
||||
break;
|
||||
|
||||
case Stage::Content:
|
||||
if (oldStage == Stage::Begin)
|
||||
ImGui::Spring(0);
|
||||
|
||||
ImGui::BeginHorizontal("content");
|
||||
ImGui::Spring(0, 0);
|
||||
break;
|
||||
|
||||
case Stage::Input:
|
||||
ImGui::BeginVertical("inputs", ImVec2(0, 0), 0.0f);
|
||||
|
||||
ed::PushStyleVar(ed::StyleVar_PivotAlignment, ImVec2(0, 0.5f));
|
||||
ed::PushStyleVar(ed::StyleVar_PivotSize, ImVec2(0, 0));
|
||||
|
||||
if (!HasHeader)
|
||||
ImGui::Spring(1, 0);
|
||||
break;
|
||||
|
||||
case Stage::Middle:
|
||||
ImGui::Spring(1);
|
||||
ImGui::BeginVertical("middle", ImVec2(0, 0), 1.0f);
|
||||
break;
|
||||
|
||||
case Stage::Output:
|
||||
if (oldStage == Stage::Middle || oldStage == Stage::Input)
|
||||
ImGui::Spring(1);
|
||||
else
|
||||
ImGui::Spring(1, 0);
|
||||
ImGui::BeginVertical("outputs", ImVec2(0, 0), 1.0f);
|
||||
|
||||
ed::PushStyleVar(ed::StyleVar_PivotAlignment, ImVec2(1.0f, 0.5f));
|
||||
ed::PushStyleVar(ed::StyleVar_PivotSize, ImVec2(0, 0));
|
||||
|
||||
if (!HasHeader)
|
||||
ImGui::Spring(1, 0);
|
||||
break;
|
||||
|
||||
case Stage::End:
|
||||
if (oldStage == Stage::Input)
|
||||
ImGui::Spring(1, 0);
|
||||
if (oldStage != Stage::Begin)
|
||||
ImGui::EndHorizontal();
|
||||
ContentMin = ImGui::GetItemRectMin();
|
||||
ContentMax = ImGui::GetItemRectMax();
|
||||
|
||||
//ImGui::Spring(0);
|
||||
ImGui::EndVertical();
|
||||
NodeMin = ImGui::GetItemRectMin();
|
||||
NodeMax = ImGui::GetItemRectMax();
|
||||
break;
|
||||
|
||||
case Stage::Invalid:
|
||||
break;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void util::BlueprintNodeBuilder::Pin(ed::PinId id, ed::PinKind kind)
|
||||
{
|
||||
ed::BeginPin(id, kind);
|
||||
}
|
||||
|
||||
void util::BlueprintNodeBuilder::EndPin()
|
||||
{
|
||||
ed::EndPin();
|
||||
|
||||
// #debug
|
||||
// ImGui::GetWindowDrawList()->AddRectFilled(
|
||||
// ImGui::GetItemRectMin(), ImGui::GetItemRectMax(), IM_COL32(255, 0, 0, 64));
|
||||
}
|
76
src/AnimGraphEditor/utilities/builders.h
Normal file
76
src/AnimGraphEditor/utilities/builders.h
Normal file
@ -0,0 +1,76 @@
|
||||
//------------------------------------------------------------------------------
|
||||
// LICENSE
|
||||
// This software is dual-licensed to the public domain and under the following
|
||||
// license: you are granted a perpetual, irrevocable license to copy, modify,
|
||||
// publish, and distribute this file as you see fit.
|
||||
//
|
||||
// CREDITS
|
||||
// Written by Michal Cichon
|
||||
//------------------------------------------------------------------------------
|
||||
#pragma once
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
#include "3rdparty/imgui-node-editor/imgui_node_editor.h"
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
namespace ax {
|
||||
namespace NodeEditor {
|
||||
namespace Utilities {
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
struct BlueprintNodeBuilder {
|
||||
BlueprintNodeBuilder(
|
||||
ImTextureID texture = 0,
|
||||
int textureWidth = 0,
|
||||
int textureHeight = 0);
|
||||
|
||||
void Begin(NodeId id);
|
||||
void End();
|
||||
|
||||
void Header(const ImVec4& color = ImVec4(1, 1, 1, 1));
|
||||
void EndHeader();
|
||||
|
||||
void Input(PinId id);
|
||||
void EndInput();
|
||||
|
||||
void Middle();
|
||||
|
||||
void Output(PinId id);
|
||||
void EndOutput();
|
||||
|
||||
private:
|
||||
enum class Stage {
|
||||
Invalid,
|
||||
Begin,
|
||||
Header,
|
||||
Content,
|
||||
Input,
|
||||
Output,
|
||||
Middle,
|
||||
End
|
||||
};
|
||||
|
||||
bool SetStage(Stage stage);
|
||||
|
||||
void Pin(PinId id, ax::NodeEditor::PinKind kind);
|
||||
void EndPin();
|
||||
|
||||
ImTextureID HeaderTextureId;
|
||||
int HeaderTextureWidth;
|
||||
int HeaderTextureHeight;
|
||||
NodeId CurrentNodeId;
|
||||
Stage CurrentStage;
|
||||
ImU32 HeaderColor;
|
||||
ImVec2 NodeMin;
|
||||
ImVec2 NodeMax;
|
||||
ImVec2 HeaderMin;
|
||||
ImVec2 HeaderMax;
|
||||
ImVec2 ContentMin;
|
||||
ImVec2 ContentMax;
|
||||
bool HasHeader;
|
||||
};
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
} // namespace Utilities
|
||||
} // namespace NodeEditor
|
||||
} // namespace ax
|
252
src/AnimGraphEditor/utilities/drawing.cpp
Normal file
252
src/AnimGraphEditor/utilities/drawing.cpp
Normal file
@ -0,0 +1,252 @@
|
||||
# define IMGUI_DEFINE_MATH_OPERATORS
|
||||
# include "drawing.h"
|
||||
# include <imgui_internal.h>
|
||||
|
||||
void ax::Drawing::DrawIcon(ImDrawList* drawList, const ImVec2& a, const ImVec2& b, IconType type, bool filled, ImU32 color, ImU32 innerColor)
|
||||
{
|
||||
auto rect = ImRect(a, b);
|
||||
auto rect_x = rect.Min.x;
|
||||
auto rect_y = rect.Min.y;
|
||||
auto rect_w = rect.Max.x - rect.Min.x;
|
||||
auto rect_h = rect.Max.y - rect.Min.y;
|
||||
auto rect_center_x = (rect.Min.x + rect.Max.x) * 0.5f;
|
||||
auto rect_center_y = (rect.Min.y + rect.Max.y) * 0.5f;
|
||||
auto rect_center = ImVec2(rect_center_x, rect_center_y);
|
||||
const auto outline_scale = rect_w / 24.0f;
|
||||
const auto extra_segments = static_cast<int>(2 * outline_scale); // for full circle
|
||||
|
||||
if (type == IconType::Flow)
|
||||
{
|
||||
const auto origin_scale = rect_w / 24.0f;
|
||||
|
||||
const auto offset_x = 1.0f * origin_scale;
|
||||
const auto offset_y = 0.0f * origin_scale;
|
||||
const auto margin = (filled ? 2.0f : 2.0f) * origin_scale;
|
||||
const auto rounding = 0.1f * origin_scale;
|
||||
const auto tip_round = 0.7f; // percentage of triangle edge (for tip)
|
||||
//const auto edge_round = 0.7f; // percentage of triangle edge (for corner)
|
||||
const auto canvas = ImRect(
|
||||
rect.Min.x + margin + offset_x,
|
||||
rect.Min.y + margin + offset_y,
|
||||
rect.Max.x - margin + offset_x,
|
||||
rect.Max.y - margin + offset_y);
|
||||
const auto canvas_x = canvas.Min.x;
|
||||
const auto canvas_y = canvas.Min.y;
|
||||
const auto canvas_w = canvas.Max.x - canvas.Min.x;
|
||||
const auto canvas_h = canvas.Max.y - canvas.Min.y;
|
||||
|
||||
const auto left = canvas_x + canvas_w * 0.5f * 0.3f;
|
||||
const auto right = canvas_x + canvas_w - canvas_w * 0.5f * 0.3f;
|
||||
const auto top = canvas_y + canvas_h * 0.5f * 0.2f;
|
||||
const auto bottom = canvas_y + canvas_h - canvas_h * 0.5f * 0.2f;
|
||||
const auto center_y = (top + bottom) * 0.5f;
|
||||
//const auto angle = AX_PI * 0.5f * 0.5f * 0.5f;
|
||||
|
||||
const auto tip_top = ImVec2(canvas_x + canvas_w * 0.5f, top);
|
||||
const auto tip_right = ImVec2(right, center_y);
|
||||
const auto tip_bottom = ImVec2(canvas_x + canvas_w * 0.5f, bottom);
|
||||
|
||||
drawList->PathLineTo(ImVec2(left, top) + ImVec2(0, rounding));
|
||||
drawList->PathBezierCubicCurveTo(
|
||||
ImVec2(left, top),
|
||||
ImVec2(left, top),
|
||||
ImVec2(left, top) + ImVec2(rounding, 0));
|
||||
drawList->PathLineTo(tip_top);
|
||||
drawList->PathLineTo(tip_top + (tip_right - tip_top) * tip_round);
|
||||
drawList->PathBezierCubicCurveTo(
|
||||
tip_right,
|
||||
tip_right,
|
||||
tip_bottom + (tip_right - tip_bottom) * tip_round);
|
||||
drawList->PathLineTo(tip_bottom);
|
||||
drawList->PathLineTo(ImVec2(left, bottom) + ImVec2(rounding, 0));
|
||||
drawList->PathBezierCubicCurveTo(
|
||||
ImVec2(left, bottom),
|
||||
ImVec2(left, bottom),
|
||||
ImVec2(left, bottom) - ImVec2(0, rounding));
|
||||
|
||||
if (!filled)
|
||||
{
|
||||
if (innerColor & 0xFF000000)
|
||||
drawList->AddConvexPolyFilled(drawList->_Path.Data, drawList->_Path.Size, innerColor);
|
||||
|
||||
drawList->PathStroke(color, true, 2.0f * outline_scale);
|
||||
}
|
||||
else
|
||||
drawList->PathFillConvex(color);
|
||||
}
|
||||
else
|
||||
{
|
||||
auto triangleStart = rect_center_x + 0.32f * rect_w;
|
||||
|
||||
auto rect_offset = -static_cast<int>(rect_w * 0.25f * 0.25f);
|
||||
|
||||
rect.Min.x += rect_offset;
|
||||
rect.Max.x += rect_offset;
|
||||
rect_x += rect_offset;
|
||||
rect_center_x += rect_offset * 0.5f;
|
||||
rect_center.x += rect_offset * 0.5f;
|
||||
|
||||
if (type == IconType::Circle)
|
||||
{
|
||||
const auto c = rect_center;
|
||||
|
||||
if (!filled)
|
||||
{
|
||||
const auto r = 0.5f * rect_w / 2.0f - 0.5f;
|
||||
|
||||
if (innerColor & 0xFF000000)
|
||||
drawList->AddCircleFilled(c, r, innerColor, 12 + extra_segments);
|
||||
drawList->AddCircle(c, r, color, 12 + extra_segments, 2.0f * outline_scale);
|
||||
}
|
||||
else
|
||||
{
|
||||
drawList->AddCircleFilled(c, 0.5f * rect_w / 2.0f, color, 12 + extra_segments);
|
||||
}
|
||||
}
|
||||
|
||||
if (type == IconType::Square)
|
||||
{
|
||||
if (filled)
|
||||
{
|
||||
const auto r = 0.5f * rect_w / 2.0f;
|
||||
const auto p0 = rect_center - ImVec2(r, r);
|
||||
const auto p1 = rect_center + ImVec2(r, r);
|
||||
|
||||
#if IMGUI_VERSION_NUM > 18101
|
||||
drawList->AddRectFilled(p0, p1, color, 0, ImDrawFlags_RoundCornersAll);
|
||||
#else
|
||||
drawList->AddRectFilled(p0, p1, color, 0, 15);
|
||||
#endif
|
||||
}
|
||||
else
|
||||
{
|
||||
const auto r = 0.5f * rect_w / 2.0f - 0.5f;
|
||||
const auto p0 = rect_center - ImVec2(r, r);
|
||||
const auto p1 = rect_center + ImVec2(r, r);
|
||||
|
||||
if (innerColor & 0xFF000000)
|
||||
{
|
||||
#if IMGUI_VERSION_NUM > 18101
|
||||
drawList->AddRectFilled(p0, p1, innerColor, 0, ImDrawFlags_RoundCornersAll);
|
||||
#else
|
||||
drawList->AddRectFilled(p0, p1, innerColor, 0, 15);
|
||||
#endif
|
||||
}
|
||||
|
||||
#if IMGUI_VERSION_NUM > 18101
|
||||
drawList->AddRect(p0, p1, color, 0, ImDrawFlags_RoundCornersAll, 2.0f * outline_scale);
|
||||
#else
|
||||
drawList->AddRect(p0, p1, color, 0, 15, 2.0f * outline_scale);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
if (type == IconType::Grid)
|
||||
{
|
||||
const auto r = 0.5f * rect_w / 2.0f;
|
||||
const auto w = ceilf(r / 3.0f);
|
||||
|
||||
const auto baseTl = ImVec2(floorf(rect_center_x - w * 2.5f), floorf(rect_center_y - w * 2.5f));
|
||||
const auto baseBr = ImVec2(floorf(baseTl.x + w), floorf(baseTl.y + w));
|
||||
|
||||
auto tl = baseTl;
|
||||
auto br = baseBr;
|
||||
for (int i = 0; i < 3; ++i)
|
||||
{
|
||||
tl.x = baseTl.x;
|
||||
br.x = baseBr.x;
|
||||
drawList->AddRectFilled(tl, br, color);
|
||||
tl.x += w * 2;
|
||||
br.x += w * 2;
|
||||
if (i != 1 || filled)
|
||||
drawList->AddRectFilled(tl, br, color);
|
||||
tl.x += w * 2;
|
||||
br.x += w * 2;
|
||||
drawList->AddRectFilled(tl, br, color);
|
||||
|
||||
tl.y += w * 2;
|
||||
br.y += w * 2;
|
||||
}
|
||||
|
||||
triangleStart = br.x + w + 1.0f / 24.0f * rect_w;
|
||||
}
|
||||
|
||||
if (type == IconType::RoundSquare)
|
||||
{
|
||||
if (filled)
|
||||
{
|
||||
const auto r = 0.5f * rect_w / 2.0f;
|
||||
const auto cr = r * 0.5f;
|
||||
const auto p0 = rect_center - ImVec2(r, r);
|
||||
const auto p1 = rect_center + ImVec2(r, r);
|
||||
|
||||
#if IMGUI_VERSION_NUM > 18101
|
||||
drawList->AddRectFilled(p0, p1, color, cr, ImDrawFlags_RoundCornersAll);
|
||||
#else
|
||||
drawList->AddRectFilled(p0, p1, color, cr, 15);
|
||||
#endif
|
||||
}
|
||||
else
|
||||
{
|
||||
const auto r = 0.5f * rect_w / 2.0f - 0.5f;
|
||||
const auto cr = r * 0.5f;
|
||||
const auto p0 = rect_center - ImVec2(r, r);
|
||||
const auto p1 = rect_center + ImVec2(r, r);
|
||||
|
||||
if (innerColor & 0xFF000000)
|
||||
{
|
||||
#if IMGUI_VERSION_NUM > 18101
|
||||
drawList->AddRectFilled(p0, p1, innerColor, cr, ImDrawFlags_RoundCornersAll);
|
||||
#else
|
||||
drawList->AddRectFilled(p0, p1, innerColor, cr, 15);
|
||||
#endif
|
||||
}
|
||||
|
||||
#if IMGUI_VERSION_NUM > 18101
|
||||
drawList->AddRect(p0, p1, color, cr, ImDrawFlags_RoundCornersAll, 2.0f * outline_scale);
|
||||
#else
|
||||
drawList->AddRect(p0, p1, color, cr, 15, 2.0f * outline_scale);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
else if (type == IconType::Diamond)
|
||||
{
|
||||
if (filled)
|
||||
{
|
||||
const auto r = 0.607f * rect_w / 2.0f;
|
||||
const auto c = rect_center;
|
||||
|
||||
drawList->PathLineTo(c + ImVec2( 0, -r));
|
||||
drawList->PathLineTo(c + ImVec2( r, 0));
|
||||
drawList->PathLineTo(c + ImVec2( 0, r));
|
||||
drawList->PathLineTo(c + ImVec2(-r, 0));
|
||||
drawList->PathFillConvex(color);
|
||||
}
|
||||
else
|
||||
{
|
||||
const auto r = 0.607f * rect_w / 2.0f - 0.5f;
|
||||
const auto c = rect_center;
|
||||
|
||||
drawList->PathLineTo(c + ImVec2( 0, -r));
|
||||
drawList->PathLineTo(c + ImVec2( r, 0));
|
||||
drawList->PathLineTo(c + ImVec2( 0, r));
|
||||
drawList->PathLineTo(c + ImVec2(-r, 0));
|
||||
|
||||
if (innerColor & 0xFF000000)
|
||||
drawList->AddConvexPolyFilled(drawList->_Path.Data, drawList->_Path.Size, innerColor);
|
||||
|
||||
drawList->PathStroke(color, true, 2.0f * outline_scale);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
const auto triangleTip = triangleStart + rect_w * (0.45f - 0.32f);
|
||||
|
||||
drawList->AddTriangleFilled(
|
||||
ImVec2(ceilf(triangleTip), rect_y + rect_h * 0.5f),
|
||||
ImVec2(triangleStart, rect_center_y + 0.15f * rect_h),
|
||||
ImVec2(triangleStart, rect_center_y - 0.15f * rect_h),
|
||||
color);
|
||||
}
|
||||
}
|
||||
}
|
12
src/AnimGraphEditor/utilities/drawing.h
Normal file
12
src/AnimGraphEditor/utilities/drawing.h
Normal file
@ -0,0 +1,12 @@
|
||||
# pragma once
|
||||
# include <imgui.h>
|
||||
|
||||
namespace ax {
|
||||
namespace Drawing {
|
||||
|
||||
enum class IconType: ImU32 { Flow, Circle, Square, Grid, RoundSquare, Diamond };
|
||||
|
||||
void DrawIcon(ImDrawList* drawList, const ImVec2& a, const ImVec2& b, IconType type, bool filled, ImU32 color, ImU32 innerColor);
|
||||
|
||||
} // namespace Drawing
|
||||
} // namespace ax
|
16
src/AnimGraphEditor/utilities/widgets.cpp
Normal file
16
src/AnimGraphEditor/utilities/widgets.cpp
Normal file
@ -0,0 +1,16 @@
|
||||
# define IMGUI_DEFINE_MATH_OPERATORS
|
||||
# include "widgets.h"
|
||||
# include <imgui_internal.h>
|
||||
|
||||
void ax::Widgets::Icon(const ImVec2& size, IconType type, bool filled, const ImVec4& color/* = ImVec4(1, 1, 1, 1)*/, const ImVec4& innerColor/* = ImVec4(0, 0, 0, 0)*/)
|
||||
{
|
||||
if (ImGui::IsRectVisible(size))
|
||||
{
|
||||
auto cursorPos = ImGui::GetCursorScreenPos();
|
||||
auto drawList = ImGui::GetWindowDrawList();
|
||||
ax::Drawing::DrawIcon(drawList, cursorPos, cursorPos + size, type, filled, ImColor(color), ImColor(innerColor));
|
||||
}
|
||||
|
||||
ImGui::Dummy(size);
|
||||
}
|
||||
|
13
src/AnimGraphEditor/utilities/widgets.h
Normal file
13
src/AnimGraphEditor/utilities/widgets.h
Normal file
@ -0,0 +1,13 @@
|
||||
#pragma once
|
||||
#include <imgui.h>
|
||||
#include "drawing.h"
|
||||
|
||||
namespace ax {
|
||||
namespace Widgets {
|
||||
|
||||
using Drawing::IconType;
|
||||
|
||||
void Icon(const ImVec2& size, IconType type, bool filled, const ImVec4& color = ImVec4(1, 1, 1, 1), const ImVec4& innerColor = ImVec4(0, 0, 0, 0));
|
||||
|
||||
} // namespace Widgets
|
||||
} // namespace ax
|
13
src/main.cc
13
src/main.cc
@ -22,13 +22,13 @@
|
||||
#include "3rdparty/json/json.hpp"
|
||||
#include "AnimGraph/AnimGraphBlendTree.h"
|
||||
#include "AnimGraph/AnimGraphData.h"
|
||||
#include "AnimGraphEditor/AnimGraphEditor.h"
|
||||
#include "Camera.h"
|
||||
#include "GLFW/glfw3.h"
|
||||
#include "SkinnedMesh.h"
|
||||
#include "SkinnedMeshRenderer.h"
|
||||
#include "SkinnedMeshResource.h"
|
||||
#include "embedded_fonts.h"
|
||||
#include "src/AnimGraph/AnimGraphEditor.h"
|
||||
|
||||
const int Width = 1024;
|
||||
const int Height = 768;
|
||||
@ -159,12 +159,6 @@ struct Viewport {
|
||||
offscreen_pass_desc.label = "offscreen-pass";
|
||||
|
||||
this->pass = sg_make_pass(&offscreen_pass_desc);
|
||||
|
||||
sg_pipeline_desc gl_pipeline_desc = {
|
||||
.depth = {.compare = SG_COMPAREFUNC_LESS_EQUAL, .write_enabled = true},
|
||||
.cull_mode = SG_CULLMODE_BACK,
|
||||
.sample_count = cMSAASampleCount};
|
||||
// this->pip = sg_make_pipeline(gl_pipeline_desc);
|
||||
}
|
||||
};
|
||||
|
||||
@ -1063,7 +1057,12 @@ int main() {
|
||||
/* cleanup */
|
||||
ax::NodeEditor::DestroyEditor(gApplicationConfig.graph_editor.context);
|
||||
ImNodes::DestroyContext();
|
||||
|
||||
ImGui_ImplOpenGL3_Shutdown();
|
||||
ImGui_ImplGlfw_Shutdown();
|
||||
|
||||
ImGui::DestroyContext();
|
||||
|
||||
sgl_shutdown();
|
||||
sg_shutdown();
|
||||
glfwTerminate();
|
||||
|
@ -1,4 +1,4 @@
|
||||
#include "AnimGraph/AnimGraphEditor.h"
|
||||
#include "AnimGraphEditor/AnimGraphEditor.h"
|
||||
#include "catch.hpp"
|
||||
|
||||
TEST_CASE("Node Socket To InputPin Conversion", "[animGraphEditor]") {
|
||||
|
@ -3,9 +3,9 @@
|
||||
//
|
||||
|
||||
#include "AnimGraph/AnimGraphBlendTree.h"
|
||||
#include "AnimGraph/AnimGraphEditor.h"
|
||||
#include "AnimGraph/AnimGraphNodes.h"
|
||||
#include "AnimGraph/AnimGraphResource.h"
|
||||
#include "AnimGraphEditor/AnimGraphEditor.h"
|
||||
#include "catch.hpp"
|
||||
#include "ozz/base/io/archive.h"
|
||||
#include "ozz/base/io/stream.h"
|
||||
|
Loading…
x
Reference in New Issue
Block a user