Compare commits
10
Commits
4d1990bea8
..
main
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
99d5a5eb0f | ||
|
|
698abbce4b | ||
|
|
b9789bd1e1 | ||
|
|
fd032c273b | ||
|
|
da431a3879 | ||
|
|
84fc49af30 | ||
|
|
7c7a765455 | ||
|
|
e3baa65c3b | ||
|
|
44087d7a7c | ||
|
|
e8af30d10c |
@@ -78,6 +78,7 @@ target_include_directories(
|
||||
|
||||
target_sources(AnimTestbed PRIVATE
|
||||
src/main.cc
|
||||
src/embedded_fonts.h
|
||||
src/SkinnedMeshRenderer.cc
|
||||
src/AnimGraph/AnimGraphEditor.cc
|
||||
src/AnimGraph/AnimGraphEditor.h
|
||||
|
||||
@@ -161,7 +161,7 @@ struct Socket {
|
||||
SocketValue m_value = {0};
|
||||
std::string m_value_string;
|
||||
union SocketReference {
|
||||
void* ptr;
|
||||
void* ptr = nullptr;
|
||||
void** ptr_ptr;
|
||||
};
|
||||
SocketReference m_reference = {0};
|
||||
@@ -418,7 +418,7 @@ struct NodeDescriptorBase {
|
||||
return socket->GetValue<T>();
|
||||
}
|
||||
|
||||
virtual void UpdateFlags(){};
|
||||
virtual void UpdateFlags() {};
|
||||
|
||||
protected:
|
||||
Socket* FindSocket(const char* name, std::vector<Socket>& sockets) {
|
||||
|
||||
+259
-414
@@ -13,8 +13,17 @@
|
||||
#include "imnodes.h"
|
||||
#include "misc/cpp/imgui_stdlib.h"
|
||||
|
||||
static AnimGraphResource sGraphGresource = AnimGraphResource();
|
||||
static bool sGraphLoadedThisFrame = false;
|
||||
struct EditorState {
|
||||
AnimGraphResource* rootGraphResource = nullptr;
|
||||
|
||||
std::vector<AnimGraphResource*> hierarchyStack;
|
||||
size_t hierarchyStackIndex = 0;
|
||||
|
||||
bool isGraphLoadedThisFrame = false;
|
||||
ImVec2 mousePopupStart = {};
|
||||
};
|
||||
|
||||
static EditorState sEditorState;
|
||||
|
||||
ImNodesPinShape sGetSocketShapeFromSocketType(const SocketType& socket_type) {
|
||||
switch (socket_type) {
|
||||
@@ -37,16 +46,23 @@ ImNodesPinShape sGetSocketShapeFromSocketType(const SocketType& socket_type) {
|
||||
return ImNodesPinShape_Quad;
|
||||
}
|
||||
|
||||
void NodeSocketEditor(Socket& socket) {
|
||||
bool NodeSocketEditor(Socket& socket) {
|
||||
bool modified = false;
|
||||
int mode_current = static_cast<int>(socket.m_type);
|
||||
ImGui::InputText("Name", &socket.m_name);
|
||||
if (ImGui::InputText("Name", &socket.m_name)) {
|
||||
modified = true;
|
||||
}
|
||||
if (ImGui::Combo(
|
||||
"Type",
|
||||
&mode_current,
|
||||
SocketTypeNames,
|
||||
sizeof(SocketTypeNames) / sizeof(char*))) {
|
||||
socket.m_type = static_cast<SocketType>(mode_current);
|
||||
|
||||
modified = true;
|
||||
}
|
||||
|
||||
return modified;
|
||||
}
|
||||
|
||||
void RemoveBlendTreeConnectionsForSocket(
|
||||
@@ -151,7 +167,11 @@ void SkinnedMeshWidget(SkinnedMesh* skinned_mesh) {
|
||||
void AnimGraphEditorRenderSidebar(
|
||||
BlendTreeResource& blend_tree_resource,
|
||||
AnimNodeResource* node_resource) {
|
||||
ImGui::Text("[%s]", node_resource->m_node_type_name.c_str());
|
||||
ImGui::Text(
|
||||
"[%s (%2.2f, %2.2f)]",
|
||||
node_resource->m_node_type_name.c_str(),
|
||||
node_resource->m_position[0],
|
||||
node_resource->m_position[1]);
|
||||
|
||||
char node_name_buffer[256];
|
||||
memset(node_name_buffer, 0, sizeof(node_name_buffer));
|
||||
@@ -164,7 +184,10 @@ void AnimGraphEditorRenderSidebar(
|
||||
node_resource->m_name = node_name_buffer;
|
||||
}
|
||||
|
||||
int num_properties = node_resource->m_socket_accessor->m_properties.size();
|
||||
int num_properties = 0;
|
||||
if (node_resource->m_socket_accessor != nullptr) {
|
||||
num_properties = node_resource->m_socket_accessor->m_properties.size();
|
||||
}
|
||||
for (int i = 0; i < num_properties; i++) {
|
||||
Socket& property = node_resource->m_socket_accessor->m_properties[i];
|
||||
if (property.m_type == SocketType::SocketTypeInt) {
|
||||
@@ -211,7 +234,12 @@ void AnimGraphEditorRenderSidebar(
|
||||
while (iter != outputs.end()) {
|
||||
Socket& output = *iter;
|
||||
ImGui::PushID(&output);
|
||||
NodeSocketEditor(output);
|
||||
if (NodeSocketEditor(output)) {
|
||||
AnimGraphResource* current_graph_resource =
|
||||
sEditorState.hierarchyStack[sEditorState.hierarchyStackIndex];
|
||||
current_graph_resource->m_socket_accessor->m_inputs = outputs;
|
||||
}
|
||||
|
||||
if (ImGui::Button("X")) {
|
||||
RemoveBlendTreeConnectionsForSocket(
|
||||
blend_tree_resource,
|
||||
@@ -223,6 +251,13 @@ void AnimGraphEditorRenderSidebar(
|
||||
}
|
||||
ImGui::PopID();
|
||||
}
|
||||
|
||||
if (ImGui::Button("+")) {
|
||||
AnimGraphResource* current_graph_resource =
|
||||
sEditorState.hierarchyStack[sEditorState.hierarchyStackIndex];
|
||||
current_graph_resource->RegisterBlendTreeOutputSocket<float>(
|
||||
"GraphFloatOutput");
|
||||
}
|
||||
}
|
||||
|
||||
if (node_resource == blend_tree_resource.GetGraphInputNode()) {
|
||||
@@ -235,7 +270,11 @@ void AnimGraphEditorRenderSidebar(
|
||||
while (iter != inputs.end()) {
|
||||
Socket& input = *iter;
|
||||
ImGui::PushID(&input);
|
||||
NodeSocketEditor(input);
|
||||
if (NodeSocketEditor(input)) {
|
||||
AnimGraphResource* current_graph_resource =
|
||||
sEditorState.hierarchyStack[sEditorState.hierarchyStackIndex];
|
||||
current_graph_resource->m_socket_accessor->m_inputs = inputs;
|
||||
}
|
||||
if (ImGui::Button("X")) {
|
||||
RemoveBlendTreeConnectionsForSocket(
|
||||
blend_tree_resource,
|
||||
@@ -247,25 +286,53 @@ void AnimGraphEditorRenderSidebar(
|
||||
}
|
||||
ImGui::PopID();
|
||||
}
|
||||
|
||||
if (ImGui::Button("+")) {
|
||||
AnimGraphResource* current_graph_resource =
|
||||
sEditorState.hierarchyStack[sEditorState.hierarchyStackIndex];
|
||||
current_graph_resource->RegisterBlendTreeInputSocket<float>(
|
||||
"GraphFloatInput");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void AnimGraphEditorClear() {
|
||||
sGraphGresource.Clear();
|
||||
sGraphGresource.m_blend_tree_resource.InitGraphConnectors();
|
||||
sGraphGresource.m_graph_type_name = "BlendTree";
|
||||
if (ax::NodeEditor::GetCurrentEditor() != nullptr) {
|
||||
ax::NodeEditor::ClearSelection();
|
||||
}
|
||||
|
||||
if (sEditorState.rootGraphResource) {
|
||||
delete sEditorState.rootGraphResource->m_socket_accessor;
|
||||
}
|
||||
delete sEditorState.rootGraphResource;
|
||||
|
||||
sEditorState.rootGraphResource = new AnimGraphResource();
|
||||
sEditorState.rootGraphResource->m_name = "Root";
|
||||
sEditorState.rootGraphResource->m_graph_type_name = "BlendTree";
|
||||
sEditorState.rootGraphResource->m_blend_tree_resource.InitGraphConnectors();
|
||||
sEditorState.rootGraphResource->m_socket_accessor = new NodeDescriptorBase;
|
||||
|
||||
sEditorState.hierarchyStack.clear();
|
||||
sEditorState.hierarchyStack.push_back(sEditorState.rootGraphResource);
|
||||
sEditorState.hierarchyStack[sEditorState.hierarchyStackIndex] =
|
||||
sEditorState.hierarchyStack.back();
|
||||
sEditorState.hierarchyStackIndex = 0;
|
||||
}
|
||||
|
||||
void AnimGraphEditorUpdate(ax::NodeEditor::EditorContext* context) {
|
||||
ax::NodeEditor::SetCurrentEditor(context);
|
||||
|
||||
//
|
||||
// Menu bar
|
||||
//
|
||||
ImGui::BeginMenuBar();
|
||||
if (ImGui::Button("Save")) {
|
||||
sGraphGresource.SaveToFile("editor_graph.json");
|
||||
sEditorState.rootGraphResource->SaveToFile("editor_graph.json");
|
||||
}
|
||||
if (ImGui::Button("Load")) {
|
||||
sGraphGresource.LoadFromFile("editor_graph.json");
|
||||
sGraphLoadedThisFrame = true;
|
||||
AnimGraphEditorClear();
|
||||
sEditorState.rootGraphResource->LoadFromFile("editor_graph.json");
|
||||
sEditorState.isGraphLoadedThisFrame = true;
|
||||
}
|
||||
if (ImGui::Button("Clear")) {
|
||||
AnimGraphEditorClear();
|
||||
@@ -277,14 +344,47 @@ void AnimGraphEditorUpdate(ax::NodeEditor::EditorContext* context) {
|
||||
memset(graph_name_buffer, 0, sizeof(graph_name_buffer));
|
||||
strncpy(
|
||||
graph_name_buffer,
|
||||
sGraphGresource.m_name.c_str(),
|
||||
sEditorState.hierarchyStack[sEditorState.hierarchyStackIndex]
|
||||
->m_name.c_str(),
|
||||
sizeof(graph_name_buffer));
|
||||
if (ImGui::InputText("Name", graph_name_buffer, sizeof(graph_name_buffer))) {
|
||||
sGraphGresource.m_name = graph_name_buffer;
|
||||
sEditorState.hierarchyStack[sEditorState.hierarchyStackIndex]->m_name =
|
||||
graph_name_buffer;
|
||||
}
|
||||
|
||||
ImGui::EndMenuBar();
|
||||
|
||||
//
|
||||
// Breadcrumb navigation
|
||||
//
|
||||
for (size_t i = 0, n = sEditorState.hierarchyStack.size(); i < n; i++) {
|
||||
AnimGraphResource* graph_resource =
|
||||
dynamic_cast<AnimGraphResource*>(sEditorState.hierarchyStack[i]);
|
||||
ImGui::PushID(graph_resource);
|
||||
|
||||
bool highlight_button = i == sEditorState.hierarchyStackIndex;
|
||||
|
||||
if (highlight_button) {
|
||||
ImGui::PushStyleColor(
|
||||
ImGuiCol_Button,
|
||||
(ImVec4)ImColor::HSV(1. / 7.0f, 0.6f, 0.6f));
|
||||
}
|
||||
|
||||
if (ImGui::Button(graph_resource->m_name.c_str())) {
|
||||
sEditorState.hierarchyStackIndex = i;
|
||||
}
|
||||
|
||||
if (highlight_button) {
|
||||
ImGui::PopStyleColor(1);
|
||||
}
|
||||
|
||||
ImGui::PopID();
|
||||
|
||||
if (i < n - 1) {
|
||||
ImGui::SameLine();
|
||||
}
|
||||
}
|
||||
|
||||
ImGui::Columns(2);
|
||||
|
||||
//
|
||||
@@ -292,19 +392,17 @@ void AnimGraphEditorUpdate(ax::NodeEditor::EditorContext* context) {
|
||||
//
|
||||
ax::NodeEditor::Begin("Graph Editor");
|
||||
|
||||
#if 1
|
||||
for (size_t node_id = 0,
|
||||
n = sGraphGresource.m_blend_tree_resource.GetNumNodes();
|
||||
node_id < n;
|
||||
node_id++) {
|
||||
for (size_t node_index = 0,
|
||||
n = sEditorState.hierarchyStack[sEditorState.hierarchyStackIndex]
|
||||
->m_blend_tree_resource.GetNumNodes();
|
||||
node_index < n;
|
||||
node_index++) {
|
||||
AnimNodeResource* node_resource =
|
||||
sGraphGresource.m_blend_tree_resource.GetNode(node_id);
|
||||
sEditorState.hierarchyStack[sEditorState.hierarchyStackIndex]
|
||||
->m_blend_tree_resource.GetNode(node_index);
|
||||
ax::NodeEditor::NodeId node_id(node_resource);
|
||||
|
||||
if (node_id == 0 || node_id == 1) {
|
||||
// continue;
|
||||
}
|
||||
|
||||
if (sGraphLoadedThisFrame) {
|
||||
if (sEditorState.isGraphLoadedThisFrame) {
|
||||
ax::NodeEditor::SetNodePosition(
|
||||
node_id,
|
||||
ImVec2(node_resource->m_position[0], node_resource->m_position[1]));
|
||||
@@ -313,13 +411,14 @@ void AnimGraphEditorUpdate(ax::NodeEditor::EditorContext* context) {
|
||||
ImGui::Text("%s", node_resource->m_node_type_name.c_str());
|
||||
|
||||
// Inputs
|
||||
std::vector<Socket>& node_inputs =
|
||||
node_resource->m_socket_accessor->m_inputs;
|
||||
std::vector<Socket> node_inputs =
|
||||
sEditorState.hierarchyStack[sEditorState.hierarchyStackIndex]
|
||||
->m_blend_tree_resource.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_id),
|
||||
static_cast<int>(node_index),
|
||||
static_cast<int>(j)),
|
||||
ax::NodeEditor::PinKind::Input);
|
||||
ImGui::Text("%s", socket.m_name.c_str());
|
||||
@@ -327,13 +426,14 @@ void AnimGraphEditorUpdate(ax::NodeEditor::EditorContext* context) {
|
||||
}
|
||||
|
||||
// Outputs
|
||||
std::vector<Socket>& node_outputs =
|
||||
node_resource->m_socket_accessor->m_outputs;
|
||||
std::vector<Socket> node_outputs =
|
||||
sEditorState.hierarchyStack[sEditorState.hierarchyStackIndex]
|
||||
->m_blend_tree_resource.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_id),
|
||||
static_cast<int>(node_index),
|
||||
static_cast<int>(j)),
|
||||
ax::NodeEditor::PinKind::Output);
|
||||
ImGui::Text("%s", socket.m_name.c_str());
|
||||
@@ -349,21 +449,25 @@ void AnimGraphEditorUpdate(ax::NodeEditor::EditorContext* context) {
|
||||
|
||||
int link_id = 0;
|
||||
for (size_t connection_id = 0,
|
||||
n = sGraphGresource.m_blend_tree_resource.GetNumConnections();
|
||||
n = sEditorState.hierarchyStack[sEditorState.hierarchyStackIndex]
|
||||
->m_blend_tree_resource.GetNumConnections();
|
||||
connection_id < n;
|
||||
connection_id++) {
|
||||
const BlendTreeConnectionResource* connection_resource =
|
||||
sGraphGresource.m_blend_tree_resource.GetConnection(connection_id);
|
||||
sEditorState.hierarchyStack[sEditorState.hierarchyStackIndex]
|
||||
->m_blend_tree_resource.GetConnection(connection_id);
|
||||
|
||||
const AnimNodeResource* source_node_resource =
|
||||
sGraphGresource.m_blend_tree_resource.GetNode(
|
||||
sEditorState.hierarchyStack[sEditorState.hierarchyStackIndex]
|
||||
->m_blend_tree_resource.GetNode(
|
||||
connection_resource->source_node_index);
|
||||
int source_socket_index =
|
||||
source_node_resource->m_socket_accessor->GetOutputIndex(
|
||||
connection_resource->source_socket_name.c_str());
|
||||
|
||||
const AnimNodeResource* target_node_resource =
|
||||
sGraphGresource.m_blend_tree_resource.GetNode(
|
||||
sEditorState.hierarchyStack[sEditorState.hierarchyStackIndex]
|
||||
->m_blend_tree_resource.GetNode(
|
||||
connection_resource->target_node_index);
|
||||
int target_socket_index =
|
||||
target_node_resource->m_socket_accessor->GetInputIndex(
|
||||
@@ -376,86 +480,100 @@ void AnimGraphEditorUpdate(ax::NodeEditor::EditorContext* context) {
|
||||
static_cast<int>(connection_resource->target_node_index),
|
||||
target_socket_index);
|
||||
|
||||
ax::NodeEditor::Link(link_id++, source_socket_pin_id, target_socket_pin_id);
|
||||
ax::NodeEditor::Link(
|
||||
ax::NodeEditor::LinkId(connection_resource),
|
||||
source_socket_pin_id,
|
||||
target_socket_pin_id);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
#if 1
|
||||
// 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)) {
|
||||
if (input_pin_id && output_pin_id) {
|
||||
if (ax::NodeEditor::AcceptNewItem()) {
|
||||
int source_node_index;
|
||||
int source_node_socket_index;
|
||||
const AnimNodeResource* source_node = nullptr;
|
||||
const Socket* source_socket = nullptr;
|
||||
|
||||
if (input_pin_id) {
|
||||
OutputPinIdToNodeIndexAndSocketIndex(
|
||||
input_pin_id.Get(),
|
||||
&source_node_index,
|
||||
&source_node_socket_index);
|
||||
|
||||
const AnimNodeResource* source_node =
|
||||
sGraphGresource.m_blend_tree_resource.GetNode(source_node_index);
|
||||
source_node =
|
||||
sEditorState.hierarchyStack[sEditorState.hierarchyStackIndex]
|
||||
->m_blend_tree_resource.GetNode(source_node_index);
|
||||
if (source_node->m_socket_accessor->m_outputs.size()
|
||||
< source_node_socket_index) {
|
||||
source_node_socket_index = -1;
|
||||
} else {
|
||||
source_socket =
|
||||
sEditorState.hierarchyStack[sEditorState.hierarchyStackIndex]
|
||||
->m_blend_tree_resource.GetNodeOutputSocketByIndex(
|
||||
source_node,
|
||||
source_node_socket_index);
|
||||
}
|
||||
}
|
||||
|
||||
int target_node_index;
|
||||
int target_node_socket_index;
|
||||
const AnimNodeResource* target_node = nullptr;
|
||||
const Socket* target_socket = nullptr;
|
||||
|
||||
if (output_pin_id) {
|
||||
InputPinIdToNodeIndexAndSocketIndex(
|
||||
output_pin_id.Get(),
|
||||
&target_node_index,
|
||||
&target_node_socket_index);
|
||||
|
||||
const AnimNodeResource* target_node =
|
||||
sGraphGresource.m_blend_tree_resource.GetNode(target_node_index);
|
||||
target_node =
|
||||
sEditorState.hierarchyStack[sEditorState.hierarchyStackIndex]
|
||||
->m_blend_tree_resource.GetNode(target_node_index);
|
||||
if (target_node->m_socket_accessor->m_inputs.size()
|
||||
< target_node_socket_index) {
|
||||
target_node_socket_index = -1;
|
||||
}
|
||||
|
||||
if (source_node_socket_index == -1
|
||||
|| target_node_socket_index == -1) {
|
||||
ax::NodeEditor::RejectNewItem();
|
||||
} else {
|
||||
const std::string& source_socket_name =
|
||||
source_node->m_socket_accessor
|
||||
->m_outputs[source_node_socket_index]
|
||||
.m_name;
|
||||
|
||||
const std::string& target_socket_name =
|
||||
target_node->m_socket_accessor
|
||||
->m_inputs[target_node_socket_index]
|
||||
.m_name;
|
||||
|
||||
sGraphGresource.m_blend_tree_resource.ConnectSockets(
|
||||
source_node,
|
||||
source_socket_name,
|
||||
target_socket =
|
||||
sEditorState.hierarchyStack[sEditorState.hierarchyStackIndex]
|
||||
->m_blend_tree_resource.GetNodeInputSocketByIndex(
|
||||
target_node,
|
||||
target_socket_name);
|
||||
target_node_socket_index);
|
||||
}
|
||||
}
|
||||
|
||||
if (input_pin_id && output_pin_id) {
|
||||
if (source_socket == nullptr || target_socket == nullptr
|
||||
|| !sEditorState.hierarchyStack[sEditorState.hierarchyStackIndex]
|
||||
->m_blend_tree_resource.IsConnectionValid(
|
||||
source_node,
|
||||
source_socket->m_name,
|
||||
target_node,
|
||||
target_socket->m_name)) {
|
||||
ax::NodeEditor::RejectNewItem();
|
||||
} else if (ax::NodeEditor::AcceptNewItem()) {
|
||||
sEditorState.hierarchyStack[sEditorState.hierarchyStackIndex]
|
||||
->m_blend_tree_resource.ConnectSockets(
|
||||
source_node,
|
||||
source_socket->m_name,
|
||||
target_node,
|
||||
target_socket->m_name);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
ax::NodeEditor::EndCreate();
|
||||
#endif
|
||||
|
||||
// Popup menu
|
||||
{
|
||||
const bool open_popup = ImGui::IsMouseReleased(ImGuiMouseButton_Right);
|
||||
|
||||
ImVec2 popup_mouse_position = ImGui::GetMousePos();
|
||||
|
||||
if (open_popup && ImGui::IsWindowHovered()) {
|
||||
ax::NodeEditor::Suspend();
|
||||
if (open_popup) {
|
||||
ImGui::OpenPopup("add node");
|
||||
}
|
||||
ax::NodeEditor::Resume();
|
||||
sEditorState.mousePopupStart = ImGui::GetMousePos();
|
||||
}
|
||||
|
||||
ax::NodeEditor::Suspend();
|
||||
ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2(8.f, 8.f));
|
||||
@@ -489,12 +607,18 @@ void AnimGraphEditorUpdate(ax::NodeEditor::EditorContext* context) {
|
||||
node_type_name = "ConstScalarNode";
|
||||
}
|
||||
|
||||
if (ImGui::MenuItem("BlendTree")) {
|
||||
node_type_name = "BlendTree";
|
||||
}
|
||||
|
||||
if (!node_type_name.empty()) {
|
||||
AnimNodeResource* node_resource =
|
||||
AnimNodeResourceFactory(node_type_name);
|
||||
size_t node_id = sGraphGresource.m_blend_tree_resource.GetNumNodes();
|
||||
ax::NodeEditor::SetNodePosition(node_id, popup_mouse_position);
|
||||
sGraphGresource.m_blend_tree_resource.AddNode(node_resource);
|
||||
ax::NodeEditor::SetNodePosition(
|
||||
ax::NodeEditor::NodeId(node_resource),
|
||||
sEditorState.mousePopupStart);
|
||||
sEditorState.hierarchyStack[sEditorState.hierarchyStackIndex]
|
||||
->m_blend_tree_resource.AddNode(node_resource);
|
||||
}
|
||||
|
||||
ImGui::EndPopup();
|
||||
@@ -517,353 +641,74 @@ void AnimGraphEditorUpdate(ax::NodeEditor::EditorContext* context) {
|
||||
|
||||
if (selected_node_id.Get() != 0) {
|
||||
AnimGraphEditorRenderSidebar(
|
||||
sGraphGresource.m_blend_tree_resource,
|
||||
sGraphGresource.m_blend_tree_resource.GetNode(
|
||||
selected_node_id.Get()));
|
||||
sEditorState.hierarchyStack[sEditorState.hierarchyStackIndex]
|
||||
->m_blend_tree_resource,
|
||||
selected_node_id.AsPointer<AnimNodeResource>());
|
||||
}
|
||||
}
|
||||
|
||||
ImGui::Columns(1);
|
||||
|
||||
// Clear flag, however it may be re-set further down when handling double
|
||||
// clicking into subgraphs.
|
||||
sEditorState.isGraphLoadedThisFrame = false;
|
||||
|
||||
//
|
||||
// Handle double click into subgraphs
|
||||
//
|
||||
ax::NodeEditor::NodeId double_clicked_node_id =
|
||||
ax::NodeEditor::GetDoubleClickedNode();
|
||||
if (!double_clicked_node_id.Invalid) {
|
||||
AnimNodeResource* clicked_node_resource =
|
||||
double_clicked_node_id.AsPointer<AnimNodeResource>();
|
||||
|
||||
if (clicked_node_resource != nullptr
|
||||
&& clicked_node_resource->m_node_type_name == "BlendTree") {
|
||||
AnimGraphResource* clicked_graph_resource =
|
||||
dynamic_cast<AnimGraphResource*>(clicked_node_resource);
|
||||
|
||||
assert(clicked_graph_resource != nullptr);
|
||||
|
||||
if (sEditorState.hierarchyStack.size()
|
||||
> sEditorState.hierarchyStackIndex + 1
|
||||
&& sEditorState.hierarchyStack[sEditorState.hierarchyStackIndex + 1]
|
||||
== clicked_graph_resource) {
|
||||
sEditorState.hierarchyStackIndex++;
|
||||
} else {
|
||||
sEditorState.hierarchyStack.resize(
|
||||
sEditorState.hierarchyStackIndex + 1);
|
||||
sEditorState.hierarchyStack.push_back(clicked_graph_resource);
|
||||
sEditorState.hierarchyStackIndex++;
|
||||
}
|
||||
|
||||
sEditorState.isGraphLoadedThisFrame = true;
|
||||
ax::NodeEditor::ClearSelection();
|
||||
}
|
||||
}
|
||||
|
||||
ax::NodeEditor::LinkId hovered_link = ax::NodeEditor::GetHoveredLink();
|
||||
if (!hovered_link.Invalid) {
|
||||
BlendTreeConnectionResource* connection_resource =
|
||||
hovered_link.AsPointer<BlendTreeConnectionResource>();
|
||||
|
||||
if (connection_resource && ImGui::IsKeyPressed(ImGuiKey_Delete)) {
|
||||
BlendTreeResource* blend_tree_resource =
|
||||
&sEditorState.hierarchyStack[sEditorState.hierarchyStackIndex]
|
||||
->m_blend_tree_resource;
|
||||
|
||||
blend_tree_resource->DisconnectSockets(
|
||||
blend_tree_resource->GetNode(connection_resource->source_node_index),
|
||||
connection_resource->source_socket_name,
|
||||
blend_tree_resource->GetNode(connection_resource->target_node_index),
|
||||
connection_resource->target_socket_name);
|
||||
|
||||
ax::NodeEditor::DeleteLink(hovered_link);
|
||||
}
|
||||
}
|
||||
|
||||
ax::NodeEditor::SetCurrentEditor(nullptr);
|
||||
sGraphLoadedThisFrame = false;
|
||||
}
|
||||
|
||||
void LegacyAnimGraphEditorUpdate() {
|
||||
ImGui::BeginMenuBar();
|
||||
if (ImGui::Button("Save")) {
|
||||
sGraphGresource.SaveToFile("editor_graph.json");
|
||||
}
|
||||
if (ImGui::Button("Load")) {
|
||||
sGraphGresource.LoadFromFile("editor_graph.json");
|
||||
|
||||
for (size_t i = 0, n = sGraphGresource.m_blend_tree_resource.GetNumNodes();
|
||||
i < n;
|
||||
i++) {
|
||||
const AnimNodeResource* node_resource =
|
||||
sGraphGresource.m_blend_tree_resource.GetNode(i);
|
||||
ImNodes::SetNodeGridSpacePos(
|
||||
i,
|
||||
ImVec2(node_resource->m_position[0], node_resource->m_position[1]));
|
||||
}
|
||||
}
|
||||
if (ImGui::Button("Clear")) {
|
||||
sGraphGresource.Clear();
|
||||
}
|
||||
char graph_name_buffer[256];
|
||||
memset(graph_name_buffer, 0, sizeof(graph_name_buffer));
|
||||
strncpy(
|
||||
graph_name_buffer,
|
||||
sGraphGresource.m_name.c_str(),
|
||||
sizeof(graph_name_buffer));
|
||||
if (ImGui::InputText("Name", graph_name_buffer, sizeof(graph_name_buffer))) {
|
||||
sGraphGresource.m_name = graph_name_buffer;
|
||||
}
|
||||
|
||||
ImGui::EndMenuBar();
|
||||
|
||||
ImGui::Columns(2);
|
||||
|
||||
//
|
||||
// Node editor canvas
|
||||
//
|
||||
ImNodes::BeginNodeEditor();
|
||||
|
||||
// Popup menu
|
||||
{
|
||||
const bool open_popup =
|
||||
ImGui::IsWindowFocused(ImGuiFocusedFlags_RootAndChildWindows)
|
||||
&& ImNodes::IsEditorHovered()
|
||||
&& ImGui::IsMouseReleased(ImGuiMouseButton_Right);
|
||||
|
||||
ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2(8.f, 8.f));
|
||||
if (!ImGui::IsAnyItemHovered() && open_popup) {
|
||||
ImGui::OpenPopup("add node");
|
||||
}
|
||||
|
||||
if (ImGui::BeginPopup("add node")) {
|
||||
const ImVec2 click_pos = ImGui::GetMousePosOnOpeningCurrentPopup();
|
||||
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 (node_type_name != "") {
|
||||
AnimNodeResource* node_resource =
|
||||
AnimNodeResourceFactory(node_type_name);
|
||||
size_t node_id = sGraphGresource.m_blend_tree_resource.GetNumNodes();
|
||||
ImNodes::SetNodeScreenSpacePos(node_id, ImGui::GetMousePos());
|
||||
sGraphGresource.m_blend_tree_resource.AddNode(node_resource);
|
||||
}
|
||||
|
||||
ImGui::EndPopup();
|
||||
}
|
||||
|
||||
ImGui::PopStyleVar(ImGuiStyleVar_WindowPadding);
|
||||
}
|
||||
|
||||
for (size_t i = 0, n = sGraphGresource.m_blend_tree_resource.GetNumNodes();
|
||||
i < n;
|
||||
i++) {
|
||||
AnimNodeResource* node_resource =
|
||||
sGraphGresource.m_blend_tree_resource.GetNode(i);
|
||||
|
||||
ImNodes::BeginNode(i);
|
||||
ImGui::PushItemWidth(110.0f);
|
||||
|
||||
// Header
|
||||
ImNodes::BeginNodeTitleBar();
|
||||
if (node_resource
|
||||
== sGraphGresource.m_blend_tree_resource.GetGraphOutputNode()) {
|
||||
ImGui::TextUnformatted("Graph Outputs");
|
||||
} else if (
|
||||
node_resource
|
||||
== sGraphGresource.m_blend_tree_resource.GetGraphInputNode()) {
|
||||
ImGui::TextUnformatted("Graph Inputs");
|
||||
} else {
|
||||
ImGui::TextUnformatted(node_resource->m_node_type_name.c_str());
|
||||
}
|
||||
ImNodes::EndNodeTitleBar();
|
||||
|
||||
// Inputs
|
||||
std::vector<Socket>& node_inputs =
|
||||
node_resource->m_socket_accessor->m_inputs;
|
||||
for (size_t j = 0, ni = node_inputs.size(); j < ni; j++) {
|
||||
Socket& socket = node_inputs[j];
|
||||
|
||||
ImColor socket_color = ImColor(255, 255, 255, 255);
|
||||
if (socket.m_flags & SocketFlagAffectsTime) {
|
||||
socket_color = ImColor(255, 128, 128, 255);
|
||||
}
|
||||
|
||||
ImNodes::BeginInputAttribute(
|
||||
GenerateInputAttributeId(i, j),
|
||||
sGetSocketShapeFromSocketType(socket.m_type),
|
||||
socket_color);
|
||||
ImGui::TextUnformatted(socket.m_name.c_str());
|
||||
|
||||
bool socket_connected =
|
||||
sGraphGresource.m_blend_tree_resource.IsSocketConnected(
|
||||
node_resource,
|
||||
socket.m_name);
|
||||
if (!socket_connected && (socket.m_type == SocketType::SocketTypeFloat)) {
|
||||
ImGui::SameLine();
|
||||
float socket_value = socket.m_value.float_value;
|
||||
ImGui::PushItemWidth(
|
||||
130.0f - ImGui::CalcTextSize(socket.m_name.c_str()).x);
|
||||
if (ImGui::DragFloat("##hidelabel", &socket_value, 0.01f)) {
|
||||
socket.SetValue(socket_value);
|
||||
}
|
||||
ImGui::PopItemWidth();
|
||||
}
|
||||
|
||||
if (!socket_connected && (socket.m_type == SocketType::SocketTypeInt)) {
|
||||
ImGui::SameLine();
|
||||
int socket_value = socket.m_value.int_value;
|
||||
ImGui::PushItemWidth(
|
||||
130.0f - ImGui::CalcTextSize(socket.m_name.c_str()).x);
|
||||
if (ImGui::InputInt("##hidelabel", &socket_value, 1)) {
|
||||
socket.SetValue(socket_value);
|
||||
}
|
||||
ImGui::PopItemWidth();
|
||||
}
|
||||
|
||||
ImNodes::PushAttributeFlag(
|
||||
ImNodesAttributeFlags_EnableLinkDetachWithDragClick);
|
||||
ImNodes::EndInputAttribute();
|
||||
}
|
||||
|
||||
// Outputs
|
||||
const std::vector<Socket>& node_outputs =
|
||||
node_resource->m_socket_accessor->m_outputs;
|
||||
for (size_t j = 0, ni = node_outputs.size(); j < ni; j++) {
|
||||
const Socket& socket = node_outputs[j];
|
||||
ImNodes::BeginOutputAttribute(
|
||||
GenerateOutputAttributeId(i, j),
|
||||
sGetSocketShapeFromSocketType(socket.m_type),
|
||||
ImColor(255, 255, 255, 255));
|
||||
ImGui::TextUnformatted(socket.m_name.c_str());
|
||||
ImNodes::PushAttributeFlag(
|
||||
ImNodesAttributeFlags_EnableLinkDetachWithDragClick);
|
||||
ImNodes::EndInputAttribute();
|
||||
}
|
||||
|
||||
// Graph output node
|
||||
if (i == 0) {
|
||||
if (ImGui::Button("+Output")) {
|
||||
AnimNodeResource* graph_output_node =
|
||||
sGraphGresource.m_blend_tree_resource.GetGraphOutputNode();
|
||||
|
||||
static float bla = 0.f;
|
||||
std::string socket_name = "Output";
|
||||
socket_name += std::to_string(
|
||||
graph_output_node->m_socket_accessor->m_inputs.size());
|
||||
graph_output_node->m_socket_accessor->RegisterInput<float>(
|
||||
socket_name.c_str(),
|
||||
nullptr);
|
||||
}
|
||||
} else if (i == 1) {
|
||||
if (ImGui::Button("+Input")) {
|
||||
AnimNodeResource* graph_input_node =
|
||||
sGraphGresource.m_blend_tree_resource.GetGraphInputNode();
|
||||
|
||||
static float bla = 0.f;
|
||||
std::string socket_name = "Input";
|
||||
socket_name += std::to_string(
|
||||
graph_input_node->m_socket_accessor->m_outputs.size());
|
||||
graph_input_node->m_socket_accessor->RegisterOutput<float>(
|
||||
socket_name.c_str(),
|
||||
nullptr);
|
||||
}
|
||||
}
|
||||
|
||||
// Save state in node resource
|
||||
ImVec2 node_pos = ImNodes::GetNodeGridSpacePos(i);
|
||||
node_resource->m_position[0] = node_pos[0];
|
||||
node_resource->m_position[1] = node_pos[1];
|
||||
ImGui::PopItemWidth();
|
||||
|
||||
ImNodes::EndNode();
|
||||
|
||||
// Ensure flags such as SocketFlagAffectsTime are properly set.
|
||||
node_resource->m_socket_accessor->UpdateFlags();
|
||||
}
|
||||
|
||||
for (size_t i = 0,
|
||||
n = sGraphGresource.m_blend_tree_resource.GetNumConnections();
|
||||
i < n;
|
||||
i++) {
|
||||
const BlendTreeConnectionResource* connection =
|
||||
sGraphGresource.m_blend_tree_resource.GetConnection(i);
|
||||
int start_attr, end_attr;
|
||||
|
||||
const AnimNodeResource* source_node =
|
||||
sGraphGresource.m_blend_tree_resource.GetNode(
|
||||
connection->source_node_index);
|
||||
int source_socket_index = source_node->m_socket_accessor->GetOutputIndex(
|
||||
connection->source_socket_name.c_str());
|
||||
|
||||
const AnimNodeResource* target_node =
|
||||
sGraphGresource.m_blend_tree_resource.GetNode(
|
||||
connection->target_node_index);
|
||||
int target_socket_index = target_node->m_socket_accessor->GetInputIndex(
|
||||
connection->target_socket_name.c_str());
|
||||
|
||||
start_attr = GenerateOutputAttributeId(
|
||||
connection->source_node_index,
|
||||
source_socket_index);
|
||||
end_attr = GenerateInputAttributeId(
|
||||
connection->target_node_index,
|
||||
target_socket_index);
|
||||
|
||||
ImNodes::Link(i, start_attr, end_attr);
|
||||
}
|
||||
|
||||
ImNodes::EndNodeEditor();
|
||||
|
||||
// Handle newly created links.
|
||||
int start_attr, end_attr;
|
||||
if (ImNodes::IsLinkCreated(&start_attr, &end_attr)) {
|
||||
int node_start_id;
|
||||
int node_start_output_index;
|
||||
SplitOutputAttributeId(
|
||||
start_attr,
|
||||
&node_start_id,
|
||||
&node_start_output_index);
|
||||
|
||||
int node_end_id;
|
||||
int node_end_input_index;
|
||||
SplitInputAttributeId(end_attr, &node_end_id, &node_end_input_index);
|
||||
|
||||
BlendTreeConnectionResource connection;
|
||||
connection.source_node_index = node_start_id;
|
||||
const AnimNodeResource* source_node =
|
||||
sGraphGresource.m_blend_tree_resource.GetNode(node_start_id);
|
||||
connection.source_socket_name =
|
||||
source_node->m_socket_accessor->m_outputs[node_start_output_index]
|
||||
.m_name;
|
||||
|
||||
connection.target_node_index = node_end_id;
|
||||
const AnimNodeResource* target_node =
|
||||
sGraphGresource.m_blend_tree_resource.GetNode(node_end_id);
|
||||
connection.target_socket_name =
|
||||
target_node->m_socket_accessor->m_inputs[node_end_input_index].m_name;
|
||||
|
||||
sGraphGresource.m_blend_tree_resource.ConnectSockets(
|
||||
source_node,
|
||||
connection.source_socket_name,
|
||||
target_node,
|
||||
connection.target_socket_name);
|
||||
}
|
||||
|
||||
if (ImGui::IsKeyPressed(ImGuiKey_Delete, false)) {
|
||||
std::cerr << "Delete key!" << std::endl;
|
||||
}
|
||||
|
||||
// Handle link detachements.
|
||||
int link_id = 0;
|
||||
if (ImNodes::IsLinkDestroyed(&link_id)) {
|
||||
BlendTreeConnectionResource* connection =
|
||||
sGraphGresource.m_blend_tree_resource.GetConnection(link_id);
|
||||
AnimNodeResource* source_node =
|
||||
sGraphGresource.m_blend_tree_resource.GetNode(
|
||||
connection->source_node_index);
|
||||
AnimNodeResource* target_node =
|
||||
sGraphGresource.m_blend_tree_resource.GetNode(
|
||||
connection->target_node_index);
|
||||
sGraphGresource.m_blend_tree_resource.DisconnectSockets(
|
||||
source_node,
|
||||
connection->source_socket_name,
|
||||
target_node,
|
||||
connection->target_socket_name);
|
||||
}
|
||||
|
||||
int selected_nodes[ImNodes::NumSelectedNodes()];
|
||||
ImNodes::GetSelectedNodes(selected_nodes);
|
||||
|
||||
//
|
||||
// Sidebar
|
||||
//
|
||||
ImGui::NextColumn();
|
||||
|
||||
if (ImNodes::NumSelectedNodes() == 1) {
|
||||
if (selected_nodes[0]
|
||||
< sGraphGresource.m_blend_tree_resource.GetNumNodes()) {
|
||||
AnimNodeResource* selected_node =
|
||||
sGraphGresource.m_blend_tree_resource.GetNode(selected_nodes[0]);
|
||||
AnimGraphEditorRenderSidebar(
|
||||
sGraphGresource.m_blend_tree_resource,
|
||||
selected_node);
|
||||
}
|
||||
}
|
||||
|
||||
ImGui::Columns(1);
|
||||
}
|
||||
|
||||
void AnimGraphEditorGetRuntimeGraph(AnimGraphBlendTree& blend_tree) {
|
||||
sGraphGresource.CreateBlendTreeInstance(blend_tree);
|
||||
sEditorState.rootGraphResource->CreateBlendTreeInstance(blend_tree);
|
||||
}
|
||||
@@ -69,8 +69,6 @@ void AnimGraphEditorClear();
|
||||
|
||||
void AnimGraphEditorUpdate(ax::NodeEditor::EditorContext* context);
|
||||
|
||||
void LegacyAnimGraphEditorUpdate();
|
||||
|
||||
void AnimGraphEditorGetRuntimeGraph(AnimGraphBlendTree& anim_graph);
|
||||
|
||||
#endif //ANIMTESTBED_ANIMGRAPHEDITOR_H
|
||||
|
||||
@@ -182,6 +182,7 @@ AnimNodeResource* sAnimGraphNodeFromJson(
|
||||
|
||||
if (node_type == "BlendTree") {
|
||||
AnimGraphResource* result = new AnimGraphResource();
|
||||
result->m_socket_accessor = new NodeDescriptorBase;
|
||||
sAnimGraphResourceBlendTreeFromJson(json_node, result);
|
||||
return result;
|
||||
}
|
||||
@@ -256,6 +257,8 @@ static json sAnimGraphResourceBlendTreeToJson(
|
||||
result["name"] = anim_graph_resource.m_name;
|
||||
result["type"] = "AnimNodeResource";
|
||||
result["node_type"] = "BlendTree";
|
||||
result["position"][0] = anim_graph_resource.m_position[0];
|
||||
result["position"][1] = anim_graph_resource.m_position[1];
|
||||
|
||||
const BlendTreeResource& blend_tree_resource =
|
||||
anim_graph_resource.m_blend_tree_resource;
|
||||
@@ -307,6 +310,8 @@ static bool sAnimGraphResourceBlendTreeFromJson(
|
||||
result_graph_resource->m_graph_type_name = "BlendTree";
|
||||
result_graph_resource->m_node_type_name = "BlendTree";
|
||||
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];
|
||||
|
||||
// Load nodes
|
||||
for (size_t i = 0, n = json_data["nodes"].size(); i < n; i++) {
|
||||
@@ -326,8 +331,7 @@ static bool sAnimGraphResourceBlendTreeFromJson(
|
||||
if (json_data["nodes"][0].contains("inputs")) {
|
||||
const json& graph_outputs = json_data["nodes"][0]["inputs"];
|
||||
for (const auto& graph_output : graph_outputs) {
|
||||
AnimNodeResource* graph_node = blend_tree_resource.GetNode(0);
|
||||
graph_node->m_socket_accessor->m_inputs.push_back(
|
||||
result_graph_resource->RegisterBlendTreeOutputSocket(
|
||||
sJsonToSocket(graph_output));
|
||||
}
|
||||
}
|
||||
@@ -336,13 +340,13 @@ static bool sAnimGraphResourceBlendTreeFromJson(
|
||||
if (json_data["nodes"][1].contains("outputs")) {
|
||||
const json& graph_inputs = json_data["nodes"][1]["outputs"];
|
||||
for (const auto& graph_input : graph_inputs) {
|
||||
AnimNodeResource* graph_node = blend_tree_resource.GetNode(1);
|
||||
graph_node->m_socket_accessor->m_outputs.push_back(
|
||||
result_graph_resource->RegisterBlendTreeInputSocket(
|
||||
sJsonToSocket(graph_input));
|
||||
}
|
||||
}
|
||||
|
||||
// Load connections
|
||||
if (json_data.contains("connections")) {
|
||||
for (const auto& json_connection : json_data["connections"]) {
|
||||
if (json_connection["type"] != "AnimGraphConnectionResource") {
|
||||
std::cerr << "Invalid json object. Expected type "
|
||||
@@ -361,6 +365,7 @@ static bool sAnimGraphResourceBlendTreeFromJson(
|
||||
blend_tree_resource.GetNode(connection.target_node_index),
|
||||
connection.target_socket_name);
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
@@ -503,6 +508,31 @@ Socket* BlendTreeResource::GetNodeOutputSocket(
|
||||
return output_socket;
|
||||
}
|
||||
|
||||
const Socket* BlendTreeResource::GetNodeOutputSocketByIndex(
|
||||
const AnimNodeResource* node,
|
||||
const size_t socket_output_index) const {
|
||||
const std::vector<Socket>* output_sockets = nullptr;
|
||||
|
||||
if (node->m_socket_accessor) {
|
||||
output_sockets = &node->m_socket_accessor->m_outputs;
|
||||
} else if (node->m_node_type_name == "BlendTree") {
|
||||
const AnimGraphResource* graph_resource =
|
||||
dynamic_cast<const AnimGraphResource*>(node);
|
||||
const BlendTreeResource& blend_tree_resource =
|
||||
graph_resource->m_blend_tree_resource;
|
||||
|
||||
output_sockets =
|
||||
&blend_tree_resource.GetGraphOutputNode()->m_socket_accessor->m_outputs;
|
||||
}
|
||||
|
||||
if (output_sockets != nullptr
|
||||
&& output_sockets->size() > socket_output_index) {
|
||||
return &output_sockets->operator[](socket_output_index);
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
Socket* BlendTreeResource::GetNodeInputSocket(
|
||||
const AnimNodeResource* node,
|
||||
const std::string& input_socket_name) const {
|
||||
@@ -526,6 +556,67 @@ Socket* BlendTreeResource::GetNodeInputSocket(
|
||||
return input_socket;
|
||||
}
|
||||
|
||||
const Socket* BlendTreeResource::GetNodeInputSocketByIndex(
|
||||
const AnimNodeResource* node,
|
||||
const size_t socket_input_index) const {
|
||||
const std::vector<Socket>* output_sockets = nullptr;
|
||||
|
||||
if (node->m_socket_accessor) {
|
||||
output_sockets = &node->m_socket_accessor->m_inputs;
|
||||
} else if (node->m_node_type_name == "BlendTree") {
|
||||
const AnimGraphResource* graph_resource =
|
||||
dynamic_cast<const AnimGraphResource*>(node);
|
||||
const BlendTreeResource& blend_tree_resource =
|
||||
graph_resource->m_blend_tree_resource;
|
||||
|
||||
output_sockets =
|
||||
&blend_tree_resource.GetGraphOutputNode()->m_socket_accessor->m_outputs;
|
||||
}
|
||||
|
||||
if (output_sockets != nullptr
|
||||
&& output_sockets->size() > socket_input_index) {
|
||||
return &output_sockets->operator[](socket_input_index);
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
std::vector<Socket> BlendTreeResource::GetNodeOutputSockets(
|
||||
const AnimNodeResource* node) const {
|
||||
if (node->m_socket_accessor) {
|
||||
return node->m_socket_accessor->m_outputs;
|
||||
}
|
||||
|
||||
if (node->m_node_type_name == "BlendTree") {
|
||||
const AnimGraphResource* graph_resource =
|
||||
dynamic_cast<const AnimGraphResource*>(node);
|
||||
const BlendTreeResource& blend_tree_resource =
|
||||
graph_resource->m_blend_tree_resource;
|
||||
return blend_tree_resource.GetGraphOutputNode()
|
||||
->m_socket_accessor->m_inputs;
|
||||
}
|
||||
|
||||
return std::vector<Socket>();
|
||||
}
|
||||
|
||||
std::vector<Socket> BlendTreeResource::GetNodeInputSockets(
|
||||
const AnimNodeResource* node) const {
|
||||
if (node->m_socket_accessor) {
|
||||
return node->m_socket_accessor->m_inputs;
|
||||
}
|
||||
|
||||
if (node->m_node_type_name == "BlendTree") {
|
||||
const AnimGraphResource* graph_resource =
|
||||
dynamic_cast<const AnimGraphResource*>(node);
|
||||
const BlendTreeResource& blend_tree_resource =
|
||||
graph_resource->m_blend_tree_resource;
|
||||
return blend_tree_resource.GetGraphInputNode()
|
||||
->m_socket_accessor->m_outputs;
|
||||
}
|
||||
|
||||
return std::vector<Socket>();
|
||||
}
|
||||
|
||||
bool BlendTreeResource::IsConnectionValid(
|
||||
const AnimNodeResource* source_node,
|
||||
const std::string& source_socket_name,
|
||||
|
||||
@@ -132,10 +132,21 @@ struct BlendTreeResource {
|
||||
const AnimNodeResource* node,
|
||||
const std::string& output_socket_name) const;
|
||||
|
||||
const Socket* GetNodeOutputSocketByIndex(
|
||||
const AnimNodeResource* node,
|
||||
const size_t socket_output_index) const;
|
||||
|
||||
Socket* GetNodeInputSocket(
|
||||
const AnimNodeResource* node,
|
||||
const std::string& input_socket_name) const;
|
||||
|
||||
const Socket* GetNodeInputSocketByIndex(
|
||||
const AnimNodeResource* node,
|
||||
const size_t socket_input_index) const;
|
||||
|
||||
std::vector<Socket> GetNodeOutputSockets(const AnimNodeResource* node) const;
|
||||
std::vector<Socket> GetNodeInputSockets(const AnimNodeResource* node) const;
|
||||
|
||||
bool ConnectSockets(
|
||||
const AnimNodeResource* source_node,
|
||||
const std::string& source_socket_name,
|
||||
@@ -265,6 +276,73 @@ struct AnimGraphResource : AnimNodeResource {
|
||||
bool LoadFromFile(const char* filename);
|
||||
|
||||
void CreateBlendTreeInstance(AnimGraphBlendTree& result) const;
|
||||
|
||||
template <typename T>
|
||||
bool RegisterBlendTreeInputSocket(const std::string& socket_name) {
|
||||
Socket socket;
|
||||
socket.m_name = socket_name;
|
||||
socket.m_type = GetSocketType<T>();
|
||||
socket.m_type_size = sizeof(T);
|
||||
|
||||
return RegisterBlendTreeInputSocket(socket);
|
||||
}
|
||||
|
||||
bool RegisterBlendTreeInputSocket(const Socket& socket) {
|
||||
AnimNodeResource* input_node = m_blend_tree_resource.GetGraphInputNode();
|
||||
|
||||
std::vector<Socket> input_sockets =
|
||||
input_node->m_socket_accessor->m_outputs;
|
||||
std::vector<Socket>::const_iterator iter = std::find_if(
|
||||
input_sockets.begin(),
|
||||
input_sockets.end(),
|
||||
[&socket](const Socket& input_socket) {
|
||||
return socket.m_name == input_socket.m_name;
|
||||
});
|
||||
|
||||
if (iter != input_sockets.end()) {
|
||||
std::cerr << "Error: cannot register input socket as socket with name '"
|
||||
<< socket.m_name << "' already exists!" << std::endl;
|
||||
return false;
|
||||
}
|
||||
|
||||
input_node->m_socket_accessor->m_outputs.push_back(socket);
|
||||
m_socket_accessor->m_inputs = input_node->m_socket_accessor->m_outputs;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
bool RegisterBlendTreeOutputSocket(const std::string& socket_name) {
|
||||
Socket socket;
|
||||
socket.m_name = socket_name;
|
||||
socket.m_type = GetSocketType<T>();
|
||||
socket.m_type_size = sizeof(T);
|
||||
|
||||
return RegisterBlendTreeOutputSocket(socket);
|
||||
}
|
||||
|
||||
bool RegisterBlendTreeOutputSocket(const Socket& socket) {
|
||||
AnimNodeResource* output_node = m_blend_tree_resource.GetGraphOutputNode();
|
||||
|
||||
std::vector<Socket> output_sockets =
|
||||
output_node->m_socket_accessor->m_inputs;
|
||||
std::vector<Socket>::const_iterator iter = std::find_if(
|
||||
output_sockets.begin(),
|
||||
output_sockets.end(),
|
||||
[&socket](const Socket& input_socket) {
|
||||
return socket.m_name == input_socket.m_name;
|
||||
});
|
||||
|
||||
if (iter != output_sockets.end()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
output_node->m_socket_accessor->m_inputs.push_back(socket);
|
||||
m_socket_accessor->m_outputs = output_node->m_socket_accessor->m_inputs;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void CreateStateMachineInstance(AnimGraphStateMachine& result) const;
|
||||
|
||||
private:
|
||||
|
||||
+10441
File diff suppressed because it is too large
Load Diff
+15
-74
@@ -25,6 +25,7 @@
|
||||
#include "SkinnedMesh.h"
|
||||
#include "SkinnedMeshRenderer.h"
|
||||
#include "SkinnedMeshResource.h"
|
||||
#include "embedded_fonts.h"
|
||||
#include "src/AnimGraph/AnimGraphEditor.h"
|
||||
|
||||
const int Width = 1024;
|
||||
@@ -169,13 +170,6 @@ struct ApplicationConfig {
|
||||
int window_position[2] = {100, 30};
|
||||
int window_size[2] = {1000, 600};
|
||||
|
||||
struct LegacyGraphEditor {
|
||||
bool visible = false;
|
||||
int position[2] = {20, 20};
|
||||
int size[2] = {800, 500};
|
||||
};
|
||||
LegacyGraphEditor legacy_graph_editor;
|
||||
|
||||
struct GraphEditor {
|
||||
bool visible = false;
|
||||
int position[2] = {20, 20};
|
||||
@@ -221,14 +215,6 @@ void to_json(nlohmann::json& j, const ApplicationConfig& config) {
|
||||
j["main_window"]["size"][0] = config.window_size[0];
|
||||
j["main_window"]["size"][1] = config.window_size[1];
|
||||
|
||||
j["legacy_graph_editor"]["visible"] = config.legacy_graph_editor.visible;
|
||||
j["legacy_graph_editor"]["position"][0] =
|
||||
config.legacy_graph_editor.position[0];
|
||||
j["legacy_graph_editor"]["position"][1] =
|
||||
config.legacy_graph_editor.position[1];
|
||||
j["legacy_graph_editor"]["size"][0] = config.legacy_graph_editor.size[0];
|
||||
j["legacy_graph_editor"]["size"][1] = config.legacy_graph_editor.size[1];
|
||||
|
||||
j["graph_editor"]["visible"] = config.graph_editor.visible;
|
||||
j["graph_editor"]["position"][0] = config.graph_editor.position[0];
|
||||
j["graph_editor"]["position"][1] = config.graph_editor.position[1];
|
||||
@@ -280,28 +266,6 @@ void from_json(const nlohmann::json& j, ApplicationConfig& config) {
|
||||
}
|
||||
}
|
||||
|
||||
if (j.contains("legacy_graph_editor")) {
|
||||
if (j["legacy_graph_editor"].contains("visible")) {
|
||||
config.legacy_graph_editor.visible = j["legacy_graph_editor"]["visible"];
|
||||
}
|
||||
|
||||
if (j["legacy_graph_editor"].contains("position")
|
||||
and j["legacy_graph_editor"]["position"].size() == 2) {
|
||||
config.legacy_graph_editor.position[0] =
|
||||
j["legacy_graph_editor"]["position"].at(0);
|
||||
config.legacy_graph_editor.position[1] =
|
||||
j["legacy_graph_editor"]["position"].at(1);
|
||||
}
|
||||
|
||||
if (j["legacy_graph_editor"].contains("size")
|
||||
and j["legacy_graph_editor"]["size"].size() == 2) {
|
||||
config.legacy_graph_editor.size[0] =
|
||||
j["legacy_graph_editor"]["size"].at(0);
|
||||
config.legacy_graph_editor.size[1] =
|
||||
j["legacy_graph_editor"]["size"].at(1);
|
||||
}
|
||||
}
|
||||
|
||||
if (j.contains("graph_editor")) {
|
||||
if (j["graph_editor"].contains("visible")) {
|
||||
config.graph_editor.visible = j["graph_editor"]["visible"];
|
||||
@@ -573,7 +537,20 @@ int main() {
|
||||
ImGuiIO& io = ImGui::GetIO();
|
||||
io.ConfigFlags |= ImGuiConfigFlags_DockingEnable;
|
||||
io.IniFilename = "ATPImgui.ini";
|
||||
io.Fonts->AddFontDefault();
|
||||
|
||||
//io.Fonts->AddFontDefault();
|
||||
ImFontConfig font_config;
|
||||
font_config.OversampleH = 4;
|
||||
font_config.OversampleV = 4;
|
||||
font_config.GlyphExtraSpacing.x = 1.0f;
|
||||
io.Fonts->AddFontFromMemoryCompressedTTF(
|
||||
// roboto_medium_ttf_compressed_data,
|
||||
// roboto_medium_ttf_compressed_size,
|
||||
droid_sans_ttf_compressed_data,
|
||||
droid_sans_ttf_compressed_size,
|
||||
14,
|
||||
&font_config);
|
||||
|
||||
io.KeyMap[ImGuiKey_Tab] = GLFW_KEY_TAB;
|
||||
io.KeyMap[ImGuiKey_LeftArrow] = GLFW_KEY_LEFT;
|
||||
io.KeyMap[ImGuiKey_RightArrow] = GLFW_KEY_RIGHT;
|
||||
@@ -803,9 +780,6 @@ int main() {
|
||||
&gApplicationConfig.animation_player_widget.visible);
|
||||
|
||||
ImGui::Separator();
|
||||
ImGui::Checkbox(
|
||||
"Legacy Graph Editor",
|
||||
&gApplicationConfig.legacy_graph_editor.visible);
|
||||
|
||||
ImGui::Checkbox(
|
||||
"ImGui Demo",
|
||||
@@ -1104,39 +1078,6 @@ int main() {
|
||||
ImGui::End();
|
||||
}
|
||||
|
||||
// Legacy Animation Graph Editor
|
||||
if (gApplicationConfig.legacy_graph_editor.visible) {
|
||||
ImGui::SetNextWindowPos(
|
||||
ImVec2(
|
||||
gApplicationConfig.legacy_graph_editor.position[0],
|
||||
gApplicationConfig.legacy_graph_editor.position[1]),
|
||||
ImGuiCond_FirstUseEver);
|
||||
ImGui::SetNextWindowSize(
|
||||
ImVec2(
|
||||
gApplicationConfig.legacy_graph_editor.size[0],
|
||||
gApplicationConfig.legacy_graph_editor.size[1]),
|
||||
ImGuiCond_FirstUseEver);
|
||||
|
||||
ImGui::Begin(
|
||||
"Legacy Graph Editor",
|
||||
&gApplicationConfig.legacy_graph_editor.visible,
|
||||
ImGuiWindowFlags_MenuBar);
|
||||
|
||||
ImVec2 graph_editor_position = ImGui::GetWindowPos();
|
||||
gApplicationConfig.legacy_graph_editor.position[0] =
|
||||
graph_editor_position.x;
|
||||
gApplicationConfig.legacy_graph_editor.position[1] =
|
||||
graph_editor_position.y;
|
||||
|
||||
ImVec2 graph_editor_size = ImGui::GetWindowSize();
|
||||
gApplicationConfig.legacy_graph_editor.size[0] = graph_editor_size.x;
|
||||
gApplicationConfig.legacy_graph_editor.size[1] = graph_editor_size.y;
|
||||
|
||||
LegacyAnimGraphEditorUpdate();
|
||||
|
||||
ImGui::End();
|
||||
}
|
||||
|
||||
// 3. Show the ImGui test window. Most of the sample code is in ImGui::ShowDemoWindow()
|
||||
if (gApplicationConfig.show_imgui_demo_window) {
|
||||
ImGui::SetNextWindowPos(ImVec2(460, 20), ImGuiCond_FirstUseEver);
|
||||
|
||||
@@ -304,21 +304,11 @@ class EmbeddedTreeBlend2GraphResource {
|
||||
embedded_blend_tree_resource = &embedded_graph->m_blend_tree_resource;
|
||||
|
||||
// Embedded: outputs
|
||||
AnimNodeResource* embedded_outputs =
|
||||
embedded_blend_tree_resource->GetGraphOutputNode();
|
||||
embedded_outputs->m_socket_accessor->RegisterInput<AnimData>(
|
||||
"AnimOutput",
|
||||
nullptr);
|
||||
embedded_graph->RegisterBlendTreeOutputSocket<AnimData>("AnimOutput");
|
||||
|
||||
// Embedded: inputs
|
||||
AnimNodeResource* embedded_inputs =
|
||||
embedded_blend_tree_resource->GetGraphInputNode();
|
||||
embedded_inputs->m_socket_accessor->RegisterOutput<AnimData>(
|
||||
"AnimInput",
|
||||
nullptr);
|
||||
embedded_inputs->m_socket_accessor->RegisterOutput<float>(
|
||||
"BlendWeight",
|
||||
nullptr);
|
||||
embedded_graph->RegisterBlendTreeInputSocket<AnimData>("AnimInput");
|
||||
embedded_graph->RegisterBlendTreeInputSocket<float>("BlendWeight");
|
||||
|
||||
// Embedded nodes
|
||||
embedded_blend2_node_index = embedded_blend_tree_resource->AddNode(
|
||||
@@ -343,7 +333,7 @@ class EmbeddedTreeBlend2GraphResource {
|
||||
|
||||
// Embedded: setup connections
|
||||
REQUIRE(embedded_blend_tree_resource->ConnectSockets(
|
||||
embedded_inputs,
|
||||
embedded_blend_tree_resource->GetGraphInputNode(),
|
||||
"AnimInput",
|
||||
embedded_blend2_node_resource,
|
||||
"Input0"));
|
||||
@@ -358,7 +348,7 @@ class EmbeddedTreeBlend2GraphResource {
|
||||
embedded_blend_tree_resource->GetGraphOutputNode(),
|
||||
"AnimOutput"));
|
||||
REQUIRE(embedded_blend_tree_resource->ConnectSockets(
|
||||
embedded_inputs,
|
||||
embedded_blend_tree_resource->GetGraphInputNode(),
|
||||
"BlendWeight",
|
||||
embedded_blend2_node_resource,
|
||||
"Weight"));
|
||||
@@ -1342,3 +1332,23 @@ TEST_CASE_METHOD(
|
||||
|
||||
graph_context.freeAnimations();
|
||||
}
|
||||
|
||||
TEST_CASE(
|
||||
"Register AnimGraphResource Blendtree Sockets",
|
||||
"[AnimGraphResource]") {
|
||||
AnimNodeResource* blend_tree_anim_node_resource =
|
||||
AnimNodeResourceFactory("BlendTree");
|
||||
AnimGraphResource* blend_tree_graph_resource =
|
||||
dynamic_cast<AnimGraphResource*>(blend_tree_anim_node_resource);
|
||||
|
||||
Socket socket;
|
||||
socket.m_name = "FloatSocket";
|
||||
socket.m_type = SocketType::SocketTypeFloat;
|
||||
socket.m_reference.ptr = nullptr;
|
||||
|
||||
CHECK(blend_tree_graph_resource->RegisterBlendTreeInputSocket(socket));
|
||||
CHECK(!blend_tree_graph_resource->RegisterBlendTreeInputSocket(socket));
|
||||
|
||||
CHECK(blend_tree_graph_resource->RegisterBlendTreeOutputSocket(socket));
|
||||
CHECK(!blend_tree_graph_resource->RegisterBlendTreeOutputSocket(socket));
|
||||
}
|
||||
Reference in New Issue
Block a user