Compare commits
20
Commits
8694a11416
..
main
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
99d5a5eb0f | ||
|
|
698abbce4b | ||
|
|
b9789bd1e1 | ||
|
|
fd032c273b | ||
|
|
da431a3879 | ||
|
|
84fc49af30 | ||
|
|
7c7a765455 | ||
|
|
e3baa65c3b | ||
|
|
44087d7a7c | ||
|
|
e8af30d10c | ||
|
|
4d1990bea8 | ||
|
|
5e34aaf3db | ||
|
|
3fb2995b02 | ||
|
|
c267276be3 | ||
|
|
53c0bff7a6 | ||
|
|
91e226945c | ||
|
|
d95bc9fb9c | ||
|
|
2d5337ed1d | ||
|
|
9f9ac60f9c | ||
|
|
1741238a61 |
@@ -78,6 +78,7 @@ target_include_directories(
|
|||||||
|
|
||||||
target_sources(AnimTestbed PRIVATE
|
target_sources(AnimTestbed PRIVATE
|
||||||
src/main.cc
|
src/main.cc
|
||||||
|
src/embedded_fonts.h
|
||||||
src/SkinnedMeshRenderer.cc
|
src/SkinnedMeshRenderer.cc
|
||||||
src/AnimGraph/AnimGraphEditor.cc
|
src/AnimGraph/AnimGraphEditor.cc
|
||||||
src/AnimGraph/AnimGraphEditor.h
|
src/AnimGraph/AnimGraphEditor.h
|
||||||
@@ -124,6 +125,7 @@ set(ozz_offline_test_objs
|
|||||||
|
|
||||||
target_sources(runtests PRIVATE
|
target_sources(runtests PRIVATE
|
||||||
tests/AnimGraphResourceTests.cc
|
tests/AnimGraphResourceTests.cc
|
||||||
|
tests/AnimGraphEditorTests.cc
|
||||||
# tests/AnimGraphEvalTests.cc
|
# tests/AnimGraphEvalTests.cc
|
||||||
tests/NodeDescriptorTests.cc
|
tests/NodeDescriptorTests.cc
|
||||||
tests/SyncTrackTests.cc
|
tests/SyncTrackTests.cc
|
||||||
|
|||||||
@@ -161,7 +161,7 @@ struct Socket {
|
|||||||
SocketValue m_value = {0};
|
SocketValue m_value = {0};
|
||||||
std::string m_value_string;
|
std::string m_value_string;
|
||||||
union SocketReference {
|
union SocketReference {
|
||||||
void* ptr;
|
void* ptr = nullptr;
|
||||||
void** ptr_ptr;
|
void** ptr_ptr;
|
||||||
};
|
};
|
||||||
SocketReference m_reference = {0};
|
SocketReference m_reference = {0};
|
||||||
@@ -418,7 +418,7 @@ struct NodeDescriptorBase {
|
|||||||
return socket->GetValue<T>();
|
return socket->GetValue<T>();
|
||||||
}
|
}
|
||||||
|
|
||||||
virtual void UpdateFlags(){};
|
virtual void UpdateFlags() {};
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
Socket* FindSocket(const char* name, std::vector<Socket>& sockets) {
|
Socket* FindSocket(const char* name, std::vector<Socket>& sockets) {
|
||||||
|
|||||||
+420
-389
@@ -13,9 +13,17 @@
|
|||||||
#include "imnodes.h"
|
#include "imnodes.h"
|
||||||
#include "misc/cpp/imgui_stdlib.h"
|
#include "misc/cpp/imgui_stdlib.h"
|
||||||
|
|
||||||
static AnimGraphResource sGraphGresource =
|
struct EditorState {
|
||||||
AnimGraphResource();
|
AnimGraphResource* rootGraphResource = nullptr;
|
||||||
static bool sGraphLoadedThisFrame = false;
|
|
||||||
|
std::vector<AnimGraphResource*> hierarchyStack;
|
||||||
|
size_t hierarchyStackIndex = 0;
|
||||||
|
|
||||||
|
bool isGraphLoadedThisFrame = false;
|
||||||
|
ImVec2 mousePopupStart = {};
|
||||||
|
};
|
||||||
|
|
||||||
|
static EditorState sEditorState;
|
||||||
|
|
||||||
ImNodesPinShape sGetSocketShapeFromSocketType(const SocketType& socket_type) {
|
ImNodesPinShape sGetSocketShapeFromSocketType(const SocketType& socket_type) {
|
||||||
switch (socket_type) {
|
switch (socket_type) {
|
||||||
@@ -38,44 +46,41 @@ ImNodesPinShape sGetSocketShapeFromSocketType(const SocketType& socket_type) {
|
|||||||
return ImNodesPinShape_Quad;
|
return ImNodesPinShape_Quad;
|
||||||
}
|
}
|
||||||
|
|
||||||
int GetNodeInputSocketId(int node_index, int input_socket_index) {
|
bool NodeSocketEditor(Socket& socket) {
|
||||||
return node_index * 1000 + input_socket_index;
|
bool modified = false;
|
||||||
}
|
|
||||||
|
|
||||||
int GetNodeOutputSocketId(int node_index, int output_socket_index) {
|
|
||||||
return node_index * 1000 + 100 + output_socket_index;
|
|
||||||
}
|
|
||||||
|
|
||||||
void NodeSocketEditor(Socket& socket) {
|
|
||||||
int mode_current = static_cast<int>(socket.m_type);
|
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(
|
if (ImGui::Combo(
|
||||||
"Type",
|
"Type",
|
||||||
&mode_current,
|
&mode_current,
|
||||||
SocketTypeNames,
|
SocketTypeNames,
|
||||||
sizeof(SocketTypeNames) / sizeof(char*))) {
|
sizeof(SocketTypeNames) / sizeof(char*))) {
|
||||||
socket.m_type = static_cast<SocketType>(mode_current);
|
socket.m_type = static_cast<SocketType>(mode_current);
|
||||||
|
|
||||||
|
modified = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return modified;
|
||||||
}
|
}
|
||||||
|
|
||||||
void RemoveConnectionsForSocket(
|
void RemoveBlendTreeConnectionsForSocket(
|
||||||
AnimGraphResource& graph_resource,
|
BlendTreeResource& blend_tree_resource,
|
||||||
AnimNodeResource& node_resource,
|
AnimNodeResource* node_resource,
|
||||||
Socket& socket) {
|
Socket& socket) {
|
||||||
std::vector<BlendTreeConnectionResource>::iterator iter =
|
const BlendTreeConnectionResource* connection =
|
||||||
graph_resource.m_blend_tree_resource.m_connections.begin();
|
blend_tree_resource.FindConnectionForSocket(node_resource, socket.m_name);
|
||||||
|
while (connection != nullptr) {
|
||||||
|
blend_tree_resource.DisconnectSockets(
|
||||||
|
blend_tree_resource.GetNode(connection->source_node_index),
|
||||||
|
connection->source_socket_name,
|
||||||
|
blend_tree_resource.GetNode(connection->target_node_index),
|
||||||
|
connection->target_socket_name);
|
||||||
|
|
||||||
while (iter != graph_resource.m_blend_tree_resource.m_connections.end()) {
|
connection = blend_tree_resource.FindConnectionForSocket(
|
||||||
// TODO adjust for refactor
|
node_resource,
|
||||||
assert(false);
|
socket.m_name);
|
||||||
|
|
||||||
// AnimGraphConnectionResource& connection = *iter;
|
|
||||||
// if (connection.m_source_node == &node_resource
|
|
||||||
// && connection.m_source_socket == &socket) {
|
|
||||||
// iter = sGraphGresource.m_connections.erase(iter);
|
|
||||||
// } else {
|
|
||||||
// iter++;
|
|
||||||
// }
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -160,24 +165,31 @@ void SkinnedMeshWidget(SkinnedMesh* skinned_mesh) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void AnimGraphEditorRenderSidebar(
|
void AnimGraphEditorRenderSidebar(
|
||||||
AnimGraphBlendTreeResource& graph_resource,
|
BlendTreeResource& blend_tree_resource,
|
||||||
AnimNodeResource& node_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];
|
char node_name_buffer[256];
|
||||||
memset(node_name_buffer, 0, sizeof(node_name_buffer));
|
memset(node_name_buffer, 0, sizeof(node_name_buffer));
|
||||||
strncpy(
|
strncpy(
|
||||||
node_name_buffer,
|
node_name_buffer,
|
||||||
node_resource.m_name.c_str(),
|
node_resource->m_name.c_str(),
|
||||||
std::min(node_resource.m_name.size(), sizeof(node_name_buffer)));
|
std::min(node_resource->m_name.size(), sizeof(node_name_buffer)));
|
||||||
|
|
||||||
if (ImGui::InputText("Name", node_name_buffer, sizeof(node_name_buffer))) {
|
if (ImGui::InputText("Name", node_name_buffer, sizeof(node_name_buffer))) {
|
||||||
node_resource.m_name = node_name_buffer;
|
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++) {
|
for (int i = 0; i < num_properties; i++) {
|
||||||
Socket& property = node_resource.m_socket_accessor->m_properties[i];
|
Socket& property = node_resource->m_socket_accessor->m_properties[i];
|
||||||
if (property.m_type == SocketType::SocketTypeInt) {
|
if (property.m_type == SocketType::SocketTypeInt) {
|
||||||
ImGui::InputInt(
|
ImGui::InputInt(
|
||||||
property.m_name.c_str(),
|
property.m_name.c_str(),
|
||||||
@@ -212,223 +224,360 @@ void AnimGraphEditorRenderSidebar(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (&node_resource == &graph_resource.getGraphOutputNode()) {
|
if (node_resource == blend_tree_resource.GetGraphOutputNode()) {
|
||||||
ImGui::Text("Outputs");
|
ImGui::Text("Outputs");
|
||||||
|
|
||||||
// Graph outputs are the inputs of the output node!
|
// Graph outputs are the inputs of the output node!
|
||||||
std::vector<Socket>& outputs = node_resource.m_socket_accessor->m_inputs;
|
std::vector<Socket>& outputs = node_resource->m_socket_accessor->m_inputs;
|
||||||
|
|
||||||
std::vector<Socket>::iterator iter = outputs.begin();
|
std::vector<Socket>::iterator iter = outputs.begin();
|
||||||
while (iter != outputs.end()) {
|
while (iter != outputs.end()) {
|
||||||
Socket& output = *iter;
|
Socket& output = *iter;
|
||||||
ImGui::PushID(&output);
|
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")) {
|
if (ImGui::Button("X")) {
|
||||||
RemoveConnectionsForSocket(graph_resource, node_resource, output);
|
RemoveBlendTreeConnectionsForSocket(
|
||||||
|
blend_tree_resource,
|
||||||
|
node_resource,
|
||||||
|
output);
|
||||||
iter = outputs.erase(iter);
|
iter = outputs.erase(iter);
|
||||||
} else {
|
} else {
|
||||||
iter++;
|
iter++;
|
||||||
}
|
}
|
||||||
ImGui::PopID();
|
ImGui::PopID();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (ImGui::Button("+")) {
|
||||||
|
AnimGraphResource* current_graph_resource =
|
||||||
|
sEditorState.hierarchyStack[sEditorState.hierarchyStackIndex];
|
||||||
|
current_graph_resource->RegisterBlendTreeOutputSocket<float>(
|
||||||
|
"GraphFloatOutput");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (&node_resource == &graph_resource.getGraphInputNode()) {
|
if (node_resource == blend_tree_resource.GetGraphInputNode()) {
|
||||||
ImGui::Text("Inputs");
|
ImGui::Text("Inputs");
|
||||||
|
|
||||||
// Graph inputs are the outputs of the input node!
|
// Graph inputs are the outputs of the input node!
|
||||||
std::vector<Socket>& inputs = node_resource.m_socket_accessor->m_outputs;
|
std::vector<Socket>& inputs = node_resource->m_socket_accessor->m_outputs;
|
||||||
|
|
||||||
std::vector<Socket>::iterator iter = inputs.begin();
|
std::vector<Socket>::iterator iter = inputs.begin();
|
||||||
while (iter != inputs.end()) {
|
while (iter != inputs.end()) {
|
||||||
Socket& input = *iter;
|
Socket& input = *iter;
|
||||||
ImGui::PushID(&input);
|
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")) {
|
if (ImGui::Button("X")) {
|
||||||
RemoveConnectionsForSocket(graph_resource, node_resource, input);
|
RemoveBlendTreeConnectionsForSocket(
|
||||||
|
blend_tree_resource,
|
||||||
|
node_resource,
|
||||||
|
input);
|
||||||
iter = inputs.erase(iter);
|
iter = inputs.erase(iter);
|
||||||
} else {
|
} else {
|
||||||
iter++;
|
iter++;
|
||||||
}
|
}
|
||||||
ImGui::PopID();
|
ImGui::PopID();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (ImGui::Button("+")) {
|
||||||
|
AnimGraphResource* current_graph_resource =
|
||||||
|
sEditorState.hierarchyStack[sEditorState.hierarchyStackIndex];
|
||||||
|
current_graph_resource->RegisterBlendTreeInputSocket<float>(
|
||||||
|
"GraphFloatInput");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void AnimGraphEditorClear() {
|
||||||
|
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) {
|
void AnimGraphEditorUpdate(ax::NodeEditor::EditorContext* context) {
|
||||||
ImGui::BeginMenuBar();
|
|
||||||
if (ImGui::Button("Save")) {
|
|
||||||
sGraphGresource.saveToFile("editor_graph.json");
|
|
||||||
}
|
|
||||||
if (ImGui::Button("Load")) {
|
|
||||||
sGraphGresource.loadFromFile("editor_graph.json");
|
|
||||||
sGraphLoadedThisFrame = true;
|
|
||||||
|
|
||||||
// for (size_t i = 0, n = sGraphGresource.m_nodes.size(); i < n; i++) {
|
|
||||||
// const AnimNodeResource& node_resource = sGraphGresource.m_nodes[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();
|
|
||||||
|
|
||||||
//
|
|
||||||
// Node editor canvas
|
|
||||||
//
|
|
||||||
ax::NodeEditor::SetCurrentEditor(context);
|
ax::NodeEditor::SetCurrentEditor(context);
|
||||||
ax::NodeEditor::Begin("Graph Editor");
|
|
||||||
|
|
||||||
#if 1
|
//
|
||||||
for (size_t node_id = 0, n = sGraphGresource.m_nodes.size(); node_id < n;
|
// Menu bar
|
||||||
node_id++) {
|
//
|
||||||
AnimNodeResource& node_resource = sGraphGresource.m_nodes[node_id];
|
|
||||||
|
|
||||||
if (node_id == 0 || node_id == 1) {
|
|
||||||
// continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (sGraphLoadedThisFrame) {
|
|
||||||
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 =
|
|
||||||
node_resource.m_socket_accessor->m_inputs;
|
|
||||||
for (size_t j = 0, ni = node_inputs.size(); j < ni; j++) {
|
|
||||||
Socket& socket = node_inputs[j];
|
|
||||||
ax::NodeEditor::BeginPin(
|
|
||||||
GetNodeInputSocketId(static_cast<int>(node_id), 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 =
|
|
||||||
node_resource.m_socket_accessor->m_outputs;
|
|
||||||
for (size_t j = 0, ni = node_outputs.size(); j < ni; j++) {
|
|
||||||
Socket& socket = node_outputs[j];
|
|
||||||
ax::NodeEditor::BeginPin(
|
|
||||||
GetNodeOutputSocketId(static_cast<int>(node_id), static_cast<int>(j)),
|
|
||||||
ax::NodeEditor::PinKind::Output);
|
|
||||||
ImGui::Text("%s", socket.m_name.c_str());
|
|
||||||
ax::NodeEditor::EndPin();
|
|
||||||
}
|
|
||||||
|
|
||||||
ax::NodeEditor::EndNode();
|
|
||||||
}
|
|
||||||
|
|
||||||
int link_id = 0;
|
|
||||||
for (size_t connection_id = 0, n = sGraphGresource.m_connections.size(); connection_id < n;
|
|
||||||
connection_id++) {
|
|
||||||
const AnimGraphConnectionResource& connection_resource = sGraphGresource.m_connections[connection_id];
|
|
||||||
|
|
||||||
const AnimNodeResource& source_node_resource = sGraphGresource.m_nodes[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_nodes[connection_resource.target_node_index];
|
|
||||||
int target_socket_index = target_node_resource.m_socket_accessor->GetInputIndex(connection_resource.target_socket_name.c_str());
|
|
||||||
|
|
||||||
int source_socket_id = GetNodeOutputSocketId(static_cast<int>(connection_resource.source_node_index), source_socket_index);
|
|
||||||
int target_socket_id = GetNodeInputSocketId(static_cast<int>(connection_resource.target_node_index), target_socket_index);
|
|
||||||
|
|
||||||
ax::NodeEditor::Link(link_id++, source_socket_id, target_socket_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()) {
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
ax::NodeEditor::EndCreate();
|
|
||||||
#endif
|
|
||||||
|
|
||||||
ax::NodeEditor::End();
|
|
||||||
|
|
||||||
sGraphLoadedThisFrame = false;
|
|
||||||
|
|
||||||
ax::NodeEditor::SetCurrentEditor(nullptr);
|
|
||||||
}
|
|
||||||
|
|
||||||
void LegacyAnimGraphEditorUpdate() {
|
|
||||||
ImGui::BeginMenuBar();
|
ImGui::BeginMenuBar();
|
||||||
if (ImGui::Button("Save")) {
|
if (ImGui::Button("Save")) {
|
||||||
sGraphGresource.saveToFile("editor_graph.json");
|
sEditorState.rootGraphResource->SaveToFile("editor_graph.json");
|
||||||
}
|
}
|
||||||
if (ImGui::Button("Load")) {
|
if (ImGui::Button("Load")) {
|
||||||
sGraphGresource.loadFromFile("editor_graph.json");
|
AnimGraphEditorClear();
|
||||||
|
sEditorState.rootGraphResource->LoadFromFile("editor_graph.json");
|
||||||
for (size_t i = 0, n = sGraphGresource.m_nodes.size(); i < n; i++) {
|
sEditorState.isGraphLoadedThisFrame = true;
|
||||||
const AnimNodeResource& node_resource = sGraphGresource.m_nodes[i];
|
|
||||||
ImNodes::SetNodeGridSpacePos(
|
|
||||||
i,
|
|
||||||
ImVec2(node_resource.m_position[0], node_resource.m_position[1]));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
if (ImGui::Button("Clear")) {
|
if (ImGui::Button("Clear")) {
|
||||||
sGraphGresource.clear();
|
AnimGraphEditorClear();
|
||||||
|
}
|
||||||
|
if (ImGui::Button("Content")) {
|
||||||
|
ax::NodeEditor::NavigateToContent();
|
||||||
}
|
}
|
||||||
char graph_name_buffer[256];
|
char graph_name_buffer[256];
|
||||||
memset(graph_name_buffer, 0, sizeof(graph_name_buffer));
|
memset(graph_name_buffer, 0, sizeof(graph_name_buffer));
|
||||||
strncpy(
|
strncpy(
|
||||||
graph_name_buffer,
|
graph_name_buffer,
|
||||||
sGraphGresource.m_name.c_str(),
|
sEditorState.hierarchyStack[sEditorState.hierarchyStackIndex]
|
||||||
|
->m_name.c_str(),
|
||||||
sizeof(graph_name_buffer));
|
sizeof(graph_name_buffer));
|
||||||
if (ImGui::InputText("Name", graph_name_buffer, 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();
|
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);
|
ImGui::Columns(2);
|
||||||
|
|
||||||
//
|
//
|
||||||
// Node editor canvas
|
// Node editor canvas
|
||||||
//
|
//
|
||||||
ImNodes::BeginNodeEditor();
|
ax::NodeEditor::Begin("Graph Editor");
|
||||||
|
|
||||||
|
for (size_t node_index = 0,
|
||||||
|
n = sEditorState.hierarchyStack[sEditorState.hierarchyStackIndex]
|
||||||
|
->m_blend_tree_resource.GetNumNodes();
|
||||||
|
node_index < n;
|
||||||
|
node_index++) {
|
||||||
|
AnimNodeResource* node_resource =
|
||||||
|
sEditorState.hierarchyStack[sEditorState.hierarchyStackIndex]
|
||||||
|
->m_blend_tree_resource.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 =
|
||||||
|
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_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 =
|
||||||
|
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_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 = sEditorState.hierarchyStack[sEditorState.hierarchyStackIndex]
|
||||||
|
->m_blend_tree_resource.GetNumConnections();
|
||||||
|
connection_id < n;
|
||||||
|
connection_id++) {
|
||||||
|
const BlendTreeConnectionResource* connection_resource =
|
||||||
|
sEditorState.hierarchyStack[sEditorState.hierarchyStackIndex]
|
||||||
|
->m_blend_tree_resource.GetConnection(connection_id);
|
||||||
|
|
||||||
|
const AnimNodeResource* source_node_resource =
|
||||||
|
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 =
|
||||||
|
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(
|
||||||
|
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
|
||||||
|
if (ax::NodeEditor::BeginCreate()) {
|
||||||
|
ax::NodeEditor::PinId input_pin_id, output_pin_id;
|
||||||
|
if (ax::NodeEditor::QueryNewLink(&input_pin_id, &output_pin_id)) {
|
||||||
|
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);
|
||||||
|
|
||||||
|
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);
|
||||||
|
|
||||||
|
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;
|
||||||
|
} else {
|
||||||
|
target_socket =
|
||||||
|
sEditorState.hierarchyStack[sEditorState.hierarchyStackIndex]
|
||||||
|
->m_blend_tree_resource.GetNodeInputSocketByIndex(
|
||||||
|
target_node,
|
||||||
|
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();
|
||||||
|
|
||||||
// Popup menu
|
// Popup menu
|
||||||
{
|
{
|
||||||
const bool open_popup =
|
const bool open_popup = ImGui::IsMouseReleased(ImGuiMouseButton_Right);
|
||||||
ImGui::IsWindowFocused(ImGuiFocusedFlags_RootAndChildWindows)
|
|
||||||
&& ImNodes::IsEditorHovered()
|
|
||||||
&& ImGui::IsMouseReleased(ImGuiMouseButton_Right);
|
|
||||||
|
|
||||||
ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2(8.f, 8.f));
|
if (open_popup && ImGui::IsWindowHovered()) {
|
||||||
if (!ImGui::IsAnyItemHovered() && open_popup) {
|
ax::NodeEditor::Suspend();
|
||||||
ImGui::OpenPopup("add node");
|
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")) {
|
if (ImGui::BeginPopup("add node")) {
|
||||||
const ImVec2 click_pos = ImGui::GetMousePosOnOpeningCurrentPopup();
|
|
||||||
std::string node_type_name = "";
|
std::string node_type_name = "";
|
||||||
if (ImGui::MenuItem("AnimSampler")) {
|
if (ImGui::MenuItem("AnimSampler")) {
|
||||||
node_type_name = "AnimSampler";
|
node_type_name = "AnimSampler";
|
||||||
@@ -458,226 +607,108 @@ void LegacyAnimGraphEditorUpdate() {
|
|||||||
node_type_name = "ConstScalarNode";
|
node_type_name = "ConstScalarNode";
|
||||||
}
|
}
|
||||||
|
|
||||||
if (node_type_name != "") {
|
if (ImGui::MenuItem("BlendTree")) {
|
||||||
AnimNodeResource node_resource =
|
node_type_name = "BlendTree";
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!node_type_name.empty()) {
|
||||||
|
AnimNodeResource* node_resource =
|
||||||
AnimNodeResourceFactory(node_type_name);
|
AnimNodeResourceFactory(node_type_name);
|
||||||
size_t node_id = sGraphGresource.m_nodes.size();
|
ax::NodeEditor::SetNodePosition(
|
||||||
ImNodes::SetNodeScreenSpacePos(node_id, ImGui::GetMousePos());
|
ax::NodeEditor::NodeId(node_resource),
|
||||||
sGraphGresource.m_nodes.push_back(node_resource);
|
sEditorState.mousePopupStart);
|
||||||
|
sEditorState.hierarchyStack[sEditorState.hierarchyStackIndex]
|
||||||
|
->m_blend_tree_resource.AddNode(node_resource);
|
||||||
}
|
}
|
||||||
|
|
||||||
ImGui::EndPopup();
|
ImGui::EndPopup();
|
||||||
}
|
}
|
||||||
|
ImGui::PopStyleVar();
|
||||||
ImGui::PopStyleVar(ImGuiStyleVar_WindowPadding);
|
ax::NodeEditor::Resume();
|
||||||
}
|
}
|
||||||
|
|
||||||
for (size_t i = 0, n = sGraphGresource.m_nodes.size(); i < n; i++) {
|
ax::NodeEditor::End();
|
||||||
AnimNodeResource& node_resource = sGraphGresource.m_nodes[i];
|
|
||||||
|
|
||||||
ImNodes::BeginNode(i);
|
|
||||||
ImGui::PushItemWidth(110.0f);
|
|
||||||
|
|
||||||
// Header
|
|
||||||
ImNodes::BeginNodeTitleBar();
|
|
||||||
if (&node_resource == &sGraphGresource.getGraphOutputNode()) {
|
|
||||||
ImGui::TextUnformatted("Graph Outputs");
|
|
||||||
} else if (&node_resource == &sGraphGresource.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.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.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.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_connections.size(); i < n; i++) {
|
|
||||||
const AnimGraphConnectionResource& connection =
|
|
||||||
sGraphGresource.m_connections[i];
|
|
||||||
int start_attr, end_attr;
|
|
||||||
|
|
||||||
const AnimNodeResource& source_node =
|
|
||||||
sGraphGresource.m_nodes[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_nodes[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);
|
|
||||||
|
|
||||||
AnimGraphConnectionResource connection;
|
|
||||||
connection.source_node_index = node_start_id;
|
|
||||||
const AnimNodeResource& source_node =
|
|
||||||
sGraphGresource.m_nodes[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_nodes[node_end_id];
|
|
||||||
connection.target_socket_name =
|
|
||||||
target_node.m_socket_accessor->m_inputs[node_end_input_index].m_name;
|
|
||||||
|
|
||||||
sGraphGresource.m_connections.push_back(connection);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (ImGui::IsKeyPressed(ImGuiKey_Delete, false)) {
|
|
||||||
std::cerr << "Delete key!" << std::endl;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Handle link detachements.
|
|
||||||
int link_id = 0;
|
|
||||||
if (ImNodes::IsLinkDestroyed(&link_id)) {
|
|
||||||
sGraphGresource.m_connections.erase(
|
|
||||||
sGraphGresource.m_connections.begin() + link_id);
|
|
||||||
}
|
|
||||||
|
|
||||||
int selected_nodes[ImNodes::NumSelectedNodes()];
|
|
||||||
ImNodes::GetSelectedNodes(selected_nodes);
|
|
||||||
|
|
||||||
//
|
//
|
||||||
// Sidebar
|
// Sidebar
|
||||||
//
|
//
|
||||||
ImGui::NextColumn();
|
ImGui::NextColumn();
|
||||||
|
|
||||||
if (ImNodes::NumSelectedNodes() == 1) {
|
if (ax::NodeEditor::GetSelectedObjectCount() > 0) {
|
||||||
if (selected_nodes[0] < sGraphGresource.m_nodes.size()) {
|
ax::NodeEditor::NodeId selected_node_id = 0;
|
||||||
AnimNodeResource& selected_node =
|
|
||||||
sGraphGresource.m_nodes[selected_nodes[0]];
|
ax::NodeEditor::GetSelectedNodes(&selected_node_id, 1);
|
||||||
AnimGraphEditorRenderSidebar(sGraphGresource, selected_node);
|
|
||||||
|
if (selected_node_id.Get() != 0) {
|
||||||
|
AnimGraphEditorRenderSidebar(
|
||||||
|
sEditorState.hierarchyStack[sEditorState.hierarchyStackIndex]
|
||||||
|
->m_blend_tree_resource,
|
||||||
|
selected_node_id.AsPointer<AnimNodeResource>());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ImGui::Columns(1);
|
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);
|
||||||
}
|
}
|
||||||
|
|
||||||
void AnimGraphEditorGetRuntimeGraph(AnimGraph& anim_graph) {
|
void AnimGraphEditorGetRuntimeGraph(AnimGraphBlendTree& blend_tree) {
|
||||||
sGraphGresource.createInstance(anim_graph);
|
sEditorState.rootGraphResource->CreateBlendTreeInstance(blend_tree);
|
||||||
}
|
}
|
||||||
@@ -10,6 +10,8 @@ struct EditorContext;
|
|||||||
} // namespace ax::NodeEditor
|
} // namespace ax::NodeEditor
|
||||||
|
|
||||||
struct SkinnedMesh;
|
struct SkinnedMesh;
|
||||||
|
struct AnimGraphBlendTree;
|
||||||
|
struct SyncTrack;
|
||||||
|
|
||||||
inline int GenerateInputAttributeId(int node_id, int input_index) {
|
inline int GenerateInputAttributeId(int node_id, int input_index) {
|
||||||
return ((input_index + 1) << 14) + node_id;
|
return ((input_index + 1) << 14) + node_id;
|
||||||
@@ -31,14 +33,42 @@ SplitOutputAttributeId(int attribute_id, int* node_id, int* output_index) {
|
|||||||
*output_index = (attribute_id >> 23) - 1;
|
*output_index = (attribute_id >> 23) - 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
inline int NodeIndexAndSocketIndexToInputPinId(
|
||||||
|
int node_index,
|
||||||
|
int input_socket_index) {
|
||||||
|
return node_index * 1000 + input_socket_index;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline int NodeIndexAndSocketIndexToOutputPinId(
|
||||||
|
int node_index,
|
||||||
|
int output_socket_index) {
|
||||||
|
return node_index * 1000 + 500 + output_socket_index;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline void InputPinIdToNodeIndexAndSocketIndex(
|
||||||
|
unsigned long input_pin_id,
|
||||||
|
int* node_index,
|
||||||
|
int* socket_index) {
|
||||||
|
*socket_index = input_pin_id % 1000;
|
||||||
|
*node_index = (input_pin_id - *socket_index) / 1000;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline void OutputPinIdToNodeIndexAndSocketIndex(
|
||||||
|
unsigned long output_pin_id,
|
||||||
|
int* node_index,
|
||||||
|
int* socket_index) {
|
||||||
|
*socket_index = ((output_pin_id - 500) % 1000);
|
||||||
|
*node_index = (output_pin_id - *socket_index) / 1000;
|
||||||
|
}
|
||||||
|
|
||||||
void SyncTrackEditor(SyncTrack* sync_track);
|
void SyncTrackEditor(SyncTrack* sync_track);
|
||||||
|
|
||||||
void SkinnedMeshWidget(SkinnedMesh* skinned_mesh);
|
void SkinnedMeshWidget(SkinnedMesh* skinned_mesh);
|
||||||
|
|
||||||
|
void AnimGraphEditorClear();
|
||||||
|
|
||||||
void AnimGraphEditorUpdate(ax::NodeEditor::EditorContext* context);
|
void AnimGraphEditorUpdate(ax::NodeEditor::EditorContext* context);
|
||||||
|
|
||||||
void LegacyAnimGraphEditorUpdate();
|
void AnimGraphEditorGetRuntimeGraph(AnimGraphBlendTree& anim_graph);
|
||||||
|
|
||||||
void AnimGraphEditorGetRuntimeGraph(AnimGraph& anim_graph);
|
|
||||||
|
|
||||||
#endif //ANIMTESTBED_ANIMGRAPHEDITOR_H
|
#endif //ANIMTESTBED_ANIMGRAPHEDITOR_H
|
||||||
|
|||||||
@@ -115,8 +115,8 @@ struct AnimSamplerNode : public AnimNode {
|
|||||||
ozz::animation::SamplingJob::Context m_sampling_context;
|
ozz::animation::SamplingJob::Context m_sampling_context;
|
||||||
ozz::animation::Animation* m_animation = nullptr;
|
ozz::animation::Animation* m_animation = nullptr;
|
||||||
|
|
||||||
virtual ~AnimSamplerNode();
|
~AnimSamplerNode() noexcept override;
|
||||||
virtual bool Init(AnimGraphContext& context) override;
|
bool Init(AnimGraphContext& context) override;
|
||||||
void UpdateTime(float time_last, float time_now) override {
|
void UpdateTime(float time_last, float time_now) override {
|
||||||
m_time_last = time_last;
|
m_time_last = time_last;
|
||||||
m_time_now = time_now;
|
m_time_now = time_now;
|
||||||
|
|||||||
+474
-106
@@ -182,6 +182,7 @@ AnimNodeResource* sAnimGraphNodeFromJson(
|
|||||||
|
|
||||||
if (node_type == "BlendTree") {
|
if (node_type == "BlendTree") {
|
||||||
AnimGraphResource* result = new AnimGraphResource();
|
AnimGraphResource* result = new AnimGraphResource();
|
||||||
|
result->m_socket_accessor = new NodeDescriptorBase;
|
||||||
sAnimGraphResourceBlendTreeFromJson(json_node, result);
|
sAnimGraphResourceBlendTreeFromJson(json_node, result);
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
@@ -222,16 +223,16 @@ AnimNodeResource* sAnimGraphNodeFromJson(
|
|||||||
//
|
//
|
||||||
// AnimGraphConnectionResource <-> Json
|
// AnimGraphConnectionResource <-> Json
|
||||||
//
|
//
|
||||||
json sAnimGraphConnectionToJson(const BlendTreeConnectionResource& connection) {
|
json sAnimGraphConnectionToJson(const BlendTreeConnectionResource* connection) {
|
||||||
json result;
|
json result;
|
||||||
|
|
||||||
result["type"] = "AnimGraphConnectionResource";
|
result["type"] = "AnimGraphConnectionResource";
|
||||||
|
|
||||||
result["source_node_index"] = connection.source_node_index;
|
result["source_node_index"] = connection->source_node_index;
|
||||||
result["source_socket_name"] = connection.source_socket_name;
|
result["source_socket_name"] = connection->source_socket_name;
|
||||||
|
|
||||||
result["target_node_index"] = connection.target_node_index;
|
result["target_node_index"] = connection->target_node_index;
|
||||||
result["target_socket_name"] = connection.target_socket_name;
|
result["target_socket_name"] = connection->target_socket_name;
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
@@ -256,12 +257,14 @@ static json sAnimGraphResourceBlendTreeToJson(
|
|||||||
result["name"] = anim_graph_resource.m_name;
|
result["name"] = anim_graph_resource.m_name;
|
||||||
result["type"] = "AnimNodeResource";
|
result["type"] = "AnimNodeResource";
|
||||||
result["node_type"] = "BlendTree";
|
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 =
|
const BlendTreeResource& blend_tree_resource =
|
||||||
anim_graph_resource.m_blend_tree_resource;
|
anim_graph_resource.m_blend_tree_resource;
|
||||||
|
|
||||||
for (size_t i = 0; i < blend_tree_resource.m_nodes.size(); i++) {
|
for (size_t i = 0; i < blend_tree_resource.GetNumNodes(); i++) {
|
||||||
const AnimNodeResource* node = blend_tree_resource.m_nodes[i];
|
const AnimNodeResource* node = blend_tree_resource.GetNode(i);
|
||||||
|
|
||||||
if (node->m_node_type_name == "BlendTree") {
|
if (node->m_node_type_name == "BlendTree") {
|
||||||
const AnimGraphResource* graph_resource =
|
const AnimGraphResource* graph_resource =
|
||||||
@@ -269,26 +272,26 @@ static json sAnimGraphResourceBlendTreeToJson(
|
|||||||
result["nodes"][i] = sAnimGraphResourceBlendTreeToJson(*graph_resource);
|
result["nodes"][i] = sAnimGraphResourceBlendTreeToJson(*graph_resource);
|
||||||
} else {
|
} else {
|
||||||
result["nodes"][i] =
|
result["nodes"][i] =
|
||||||
sAnimGraphNodeToJson(node, i, blend_tree_resource.m_connections);
|
sAnimGraphNodeToJson(node, i, blend_tree_resource.GetConnections());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for (size_t i = 0; i < blend_tree_resource.m_connections.size(); i++) {
|
for (size_t i = 0; i < blend_tree_resource.GetNumConnections(); i++) {
|
||||||
const BlendTreeConnectionResource& connection =
|
const BlendTreeConnectionResource* connection =
|
||||||
blend_tree_resource.m_connections[i];
|
blend_tree_resource.GetConnection(i);
|
||||||
result["connections"][i] = sAnimGraphConnectionToJson(connection);
|
result["connections"][i] = sAnimGraphConnectionToJson(connection);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Graph inputs and outputs
|
// Graph inputs and outputs
|
||||||
{
|
{
|
||||||
const AnimNodeResource* graph_output_node = blend_tree_resource.m_nodes[0];
|
const AnimNodeResource* graph_output_node = blend_tree_resource.GetNode(0);
|
||||||
const std::vector<Socket> graph_inputs =
|
const std::vector<Socket> graph_inputs =
|
||||||
graph_output_node->m_socket_accessor->m_inputs;
|
graph_output_node->m_socket_accessor->m_inputs;
|
||||||
for (size_t i = 0; i < graph_inputs.size(); i++) {
|
for (size_t i = 0; i < graph_inputs.size(); i++) {
|
||||||
result["nodes"][0]["inputs"][i] = sSocketToJson(graph_inputs[i]);
|
result["nodes"][0]["inputs"][i] = sSocketToJson(graph_inputs[i]);
|
||||||
}
|
}
|
||||||
|
|
||||||
const AnimNodeResource* graph_input_node = blend_tree_resource.m_nodes[1];
|
const AnimNodeResource* graph_input_node = blend_tree_resource.GetNode(1);
|
||||||
const std::vector<Socket> graph_outputs =
|
const std::vector<Socket> graph_outputs =
|
||||||
graph_input_node->m_socket_accessor->m_outputs;
|
graph_input_node->m_socket_accessor->m_outputs;
|
||||||
for (size_t i = 0; i < graph_outputs.size(); i++) {
|
for (size_t i = 0; i < graph_outputs.size(); i++) {
|
||||||
@@ -307,6 +310,8 @@ static bool sAnimGraphResourceBlendTreeFromJson(
|
|||||||
result_graph_resource->m_graph_type_name = "BlendTree";
|
result_graph_resource->m_graph_type_name = "BlendTree";
|
||||||
result_graph_resource->m_node_type_name = "BlendTree";
|
result_graph_resource->m_node_type_name = "BlendTree";
|
||||||
result_graph_resource->m_name = json_data["name"];
|
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
|
// Load nodes
|
||||||
for (size_t i = 0, n = json_data["nodes"].size(); i < n; i++) {
|
for (size_t i = 0, n = json_data["nodes"].size(); i < n; i++) {
|
||||||
@@ -319,40 +324,47 @@ static bool sAnimGraphResourceBlendTreeFromJson(
|
|||||||
}
|
}
|
||||||
|
|
||||||
AnimNodeResource* node = sAnimGraphNodeFromJson(json_node, i);
|
AnimNodeResource* node = sAnimGraphNodeFromJson(json_node, i);
|
||||||
blend_tree_resource.m_nodes.push_back(node);
|
blend_tree_resource.AddNode(node);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Graph outputs
|
// Graph outputs
|
||||||
const json& graph_outputs = json_data["nodes"][0]["inputs"];
|
if (json_data["nodes"][0].contains("inputs")) {
|
||||||
for (const auto& graph_output : graph_outputs) {
|
const json& graph_outputs = json_data["nodes"][0]["inputs"];
|
||||||
AnimNodeResource* graph_node = blend_tree_resource.m_nodes[0];
|
for (const auto& graph_output : graph_outputs) {
|
||||||
graph_node->m_socket_accessor->m_inputs.push_back(
|
result_graph_resource->RegisterBlendTreeOutputSocket(
|
||||||
sJsonToSocket(graph_output));
|
sJsonToSocket(graph_output));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Graph inputs (optional)
|
// Graph inputs (optional)
|
||||||
if (json_data["nodes"][1].contains("outputs")) {
|
if (json_data["nodes"][1].contains("outputs")) {
|
||||||
const json& graph_inputs = json_data["nodes"][1]["outputs"];
|
const json& graph_inputs = json_data["nodes"][1]["outputs"];
|
||||||
for (const auto& graph_input : graph_inputs) {
|
for (const auto& graph_input : graph_inputs) {
|
||||||
AnimNodeResource* graph_node = blend_tree_resource.m_nodes[1];
|
result_graph_resource->RegisterBlendTreeInputSocket(
|
||||||
graph_node->m_socket_accessor->m_outputs.push_back(
|
|
||||||
sJsonToSocket(graph_input));
|
sJsonToSocket(graph_input));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Load connections
|
// Load connections
|
||||||
for (const auto& json_connection : json_data["connections"]) {
|
if (json_data.contains("connections")) {
|
||||||
if (json_connection["type"] != "AnimGraphConnectionResource") {
|
for (const auto& json_connection : json_data["connections"]) {
|
||||||
std::cerr << "Invalid json object. Expected type "
|
if (json_connection["type"] != "AnimGraphConnectionResource") {
|
||||||
"'AnimGraphConnectionResource' "
|
std::cerr << "Invalid json object. Expected type "
|
||||||
"but got '"
|
"'AnimGraphConnectionResource' "
|
||||||
<< json_connection["type"] << "'." << std::endl;
|
"but got '"
|
||||||
return false;
|
<< json_connection["type"] << "'." << std::endl;
|
||||||
}
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
BlendTreeConnectionResource connection =
|
BlendTreeConnectionResource connection =
|
||||||
sAnimGraphConnectionFromJson(json_connection);
|
sAnimGraphConnectionFromJson(json_connection);
|
||||||
blend_tree_resource.m_connections.push_back(connection);
|
|
||||||
|
blend_tree_resource.ConnectSockets(
|
||||||
|
blend_tree_resource.GetNode(connection.source_node_index),
|
||||||
|
connection.source_socket_name,
|
||||||
|
blend_tree_resource.GetNode(connection.target_node_index),
|
||||||
|
connection.target_socket_name);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
@@ -363,8 +375,16 @@ bool BlendTreeResource::ConnectSockets(
|
|||||||
const std::string& source_socket_name,
|
const std::string& source_socket_name,
|
||||||
const AnimNodeResource* target_node,
|
const AnimNodeResource* target_node,
|
||||||
const std::string& target_socket_name) {
|
const std::string& target_socket_name) {
|
||||||
size_t source_node_index = GetNodeIndex(source_node);
|
if (!IsConnectionValid(
|
||||||
size_t target_node_index = GetNodeIndex(target_node);
|
source_node,
|
||||||
|
source_socket_name,
|
||||||
|
target_node,
|
||||||
|
target_socket_name)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
int source_node_index = GetNodeIndex(source_node);
|
||||||
|
int target_node_index = GetNodeIndex(target_node);
|
||||||
|
|
||||||
if (source_node_index >= m_nodes.size()
|
if (source_node_index >= m_nodes.size()
|
||||||
|| target_node_index >= m_nodes.size()) {
|
|| target_node_index >= m_nodes.size()) {
|
||||||
@@ -372,32 +392,8 @@ bool BlendTreeResource::ConnectSockets(
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
Socket* source_socket;
|
Socket* source_socket = GetNodeOutputSocket(source_node, source_socket_name);
|
||||||
Socket* target_socket;
|
Socket* target_socket = GetNodeInputSocket(target_node, target_socket_name);
|
||||||
|
|
||||||
if (target_node->m_node_type_name == "BlendTree") {
|
|
||||||
const AnimGraphResource* target_graph_resource =
|
|
||||||
dynamic_cast<const AnimGraphResource*>(target_node);
|
|
||||||
AnimNodeResource* graph_output_node =
|
|
||||||
target_graph_resource->m_blend_tree_resource.GetGraphInputNode();
|
|
||||||
target_socket = graph_output_node->m_socket_accessor->GetOutputSocket(
|
|
||||||
target_socket_name.c_str());
|
|
||||||
} else {
|
|
||||||
target_socket = target_node->m_socket_accessor->GetInputSocket(
|
|
||||||
target_socket_name.c_str());
|
|
||||||
}
|
|
||||||
|
|
||||||
if (source_node->m_node_type_name == "BlendTree") {
|
|
||||||
const AnimGraphResource* source_graph_resource =
|
|
||||||
dynamic_cast<const AnimGraphResource*>(source_node);
|
|
||||||
AnimNodeResource* graph_output_node =
|
|
||||||
source_graph_resource->m_blend_tree_resource.GetGraphOutputNode();
|
|
||||||
source_socket = graph_output_node->m_socket_accessor->GetInputSocket(
|
|
||||||
source_socket_name.c_str());
|
|
||||||
} else {
|
|
||||||
source_socket = source_node->m_socket_accessor->GetOutputSocket(
|
|
||||||
source_socket_name.c_str());
|
|
||||||
}
|
|
||||||
|
|
||||||
if (source_socket == nullptr) {
|
if (source_socket == nullptr) {
|
||||||
std::cerr << "Cannot connect nodes: could not find source socket '"
|
std::cerr << "Cannot connect nodes: could not find source socket '"
|
||||||
@@ -420,10 +416,352 @@ bool BlendTreeResource::ConnectSockets(
|
|||||||
connection.target_socket_name = target_socket_name;
|
connection.target_socket_name = target_socket_name;
|
||||||
m_connections.push_back(connection);
|
m_connections.push_back(connection);
|
||||||
|
|
||||||
|
m_node_input_connection_indices[target_node_index].emplace_back(
|
||||||
|
m_connections.size() - 1);
|
||||||
|
|
||||||
|
UpdateTreeTopologyInfo();
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool BlendTreeResource::DisconnectSockets(
|
||||||
|
const AnimNodeResource* source_node,
|
||||||
|
const std::string& source_socket_name,
|
||||||
|
const AnimNodeResource* target_node,
|
||||||
|
const std::string& target_socket_name) {
|
||||||
|
int source_node_index = GetNodeIndex(source_node);
|
||||||
|
int target_node_index = GetNodeIndex(target_node);
|
||||||
|
|
||||||
|
if (source_node_index < 0 || target_node_index < 0) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
int connection_index = -1;
|
||||||
|
for (size_t i = 0, n = m_connections.size(); i < n; i++) {
|
||||||
|
if (m_connections[i]
|
||||||
|
== BlendTreeConnectionResource{
|
||||||
|
source_node_index,
|
||||||
|
source_socket_name,
|
||||||
|
target_node_index,
|
||||||
|
target_socket_name}) {
|
||||||
|
connection_index = i;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (connection_index == -1) {
|
||||||
|
std::cerr << "Error: cannot disconnect sockets as connection is not found!"
|
||||||
|
<< std::endl;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// remove connection
|
||||||
|
m_connections.erase(m_connections.begin() + connection_index);
|
||||||
|
|
||||||
|
// remove the input connection of the target node
|
||||||
|
std::vector<size_t>& target_input_connections =
|
||||||
|
m_node_input_connection_indices[target_node_index];
|
||||||
|
std::vector<size_t>::iterator end_iterator = std::remove(
|
||||||
|
target_input_connections.begin(),
|
||||||
|
target_input_connections.end(),
|
||||||
|
connection_index);
|
||||||
|
target_input_connections.erase(end_iterator);
|
||||||
|
|
||||||
|
// Decrement all node input connection indices that are after the connection
|
||||||
|
// we have removed above.
|
||||||
|
for (size_t node_index = 0, n = m_nodes.size(); node_index < n;
|
||||||
|
node_index++) {
|
||||||
|
std::vector<size_t>& node_input_connections =
|
||||||
|
m_node_input_connection_indices[node_index];
|
||||||
|
for (size_t& node_connection_index : node_input_connections) {
|
||||||
|
if (node_connection_index > connection_index) {
|
||||||
|
node_connection_index--;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
UpdateTreeTopologyInfo();
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
Socket* BlendTreeResource::GetNodeOutputSocket(
|
||||||
|
const AnimNodeResource* node,
|
||||||
|
const std::string& output_socket_name) const {
|
||||||
|
Socket* output_socket = nullptr;
|
||||||
|
|
||||||
|
if (node->m_socket_accessor) {
|
||||||
|
output_socket =
|
||||||
|
node->m_socket_accessor->GetOutputSocket(output_socket_name.c_str());
|
||||||
|
}
|
||||||
|
|
||||||
|
if (output_socket == nullptr && 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_socket =
|
||||||
|
blend_tree_resource.GetGraphOutputNode()
|
||||||
|
->m_socket_accessor->GetInputSocket(output_socket_name.c_str());
|
||||||
|
}
|
||||||
|
|
||||||
|
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 {
|
||||||
|
Socket* input_socket = nullptr;
|
||||||
|
|
||||||
|
if (node->m_socket_accessor) {
|
||||||
|
input_socket =
|
||||||
|
node->m_socket_accessor->GetInputSocket(input_socket_name.c_str());
|
||||||
|
}
|
||||||
|
|
||||||
|
if (input_socket == nullptr && 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;
|
||||||
|
input_socket =
|
||||||
|
blend_tree_resource.GetGraphInputNode()
|
||||||
|
->m_socket_accessor->GetOutputSocket(input_socket_name.c_str());
|
||||||
|
}
|
||||||
|
|
||||||
|
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,
|
||||||
|
const AnimNodeResource* target_node,
|
||||||
|
const std::string& target_socket_name) const {
|
||||||
|
// Check self connection
|
||||||
|
if (source_node == target_node) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check for loops
|
||||||
|
int source_node_index = GetNodeIndex(source_node);
|
||||||
|
int target_node_index = GetNodeIndex(target_node);
|
||||||
|
|
||||||
|
if (std::find(
|
||||||
|
m_node_inputs_subtree[source_node_index].cbegin(),
|
||||||
|
m_node_inputs_subtree[source_node_index].cend(),
|
||||||
|
target_node_index)
|
||||||
|
!= m_node_inputs_subtree[source_node_index].end()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
const Socket* source_socket =
|
||||||
|
GetNodeOutputSocket(source_node, source_socket_name);
|
||||||
|
const Socket* target_socket =
|
||||||
|
GetNodeInputSocket(target_node, target_socket_name);
|
||||||
|
|
||||||
|
if (source_socket == nullptr) {
|
||||||
|
std::cerr << "Cannot connect nodes: could not find source socket '"
|
||||||
|
<< source_socket_name << "'." << std::endl;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (target_socket == nullptr) {
|
||||||
|
std::cerr << "Cannot connect nodes: could not find target socket '"
|
||||||
|
<< target_socket_name << "'." << std::endl;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (target_socket == nullptr || source_socket == nullptr) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check socket types
|
||||||
|
if (source_socket->m_type != target_socket->m_type) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
const BlendTreeConnectionResource* BlendTreeResource::FindConnectionForSocket(
|
||||||
|
const AnimNodeResource* node,
|
||||||
|
const std::string& socket_name) const {
|
||||||
|
int node_index = GetNodeIndex(node);
|
||||||
|
|
||||||
|
std::vector<BlendTreeConnectionResource>::const_iterator connection_iter =
|
||||||
|
std::find_if(
|
||||||
|
m_connections.begin(),
|
||||||
|
m_connections.end(),
|
||||||
|
[node_index,
|
||||||
|
socket_name](const BlendTreeConnectionResource& connection) {
|
||||||
|
if ((connection.source_node_index == node_index
|
||||||
|
&& connection.source_socket_name == socket_name)
|
||||||
|
|| (connection.target_node_index == node_index
|
||||||
|
&& connection.target_socket_name == socket_name)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
});
|
||||||
|
|
||||||
|
if (connection_iter != m_connections.end()) {
|
||||||
|
return &*connection_iter;
|
||||||
|
}
|
||||||
|
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
void BlendTreeResource::UpdateTreeTopologyInfo() {
|
||||||
|
// TODO: Updating eval order and subtrees may get slow with many nodes. An
|
||||||
|
// iterative approach would scale better. But let's leave that optimization
|
||||||
|
// for a later time.
|
||||||
|
|
||||||
|
UpdateNodeEvalOrder();
|
||||||
|
UpdateNodeSubtrees();
|
||||||
|
}
|
||||||
|
|
||||||
|
void BlendTreeResource::UpdateNodeEvalOrderRecursive(const size_t node_index) {
|
||||||
|
const std::vector<size_t>& node_input_connection_indices =
|
||||||
|
m_node_input_connection_indices[node_index];
|
||||||
|
|
||||||
|
for (size_t i = 0, n = node_input_connection_indices.size(); i < n; i++) {
|
||||||
|
const BlendTreeConnectionResource& connection_resource =
|
||||||
|
m_connections[node_input_connection_indices[i]];
|
||||||
|
|
||||||
|
if (connection_resource.source_node_index == 1) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
UpdateNodeEvalOrderRecursive(connection_resource.source_node_index);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (node_index != 0 && node_index != 1) {
|
||||||
|
// In case we have multiple output connections from the node we here
|
||||||
|
// ensure that use the node evaluation that is the furthest away from
|
||||||
|
// the output.
|
||||||
|
std::vector<size_t>::iterator find_iter = std::find(
|
||||||
|
m_node_eval_order.begin(),
|
||||||
|
m_node_eval_order.end(),
|
||||||
|
node_index);
|
||||||
|
|
||||||
|
if (find_iter != m_node_eval_order.end()) {
|
||||||
|
m_node_eval_order.erase(find_iter);
|
||||||
|
}
|
||||||
|
|
||||||
|
m_node_eval_order.push_back(node_index);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void BlendTreeResource::UpdateNodeSubtrees() {
|
||||||
|
for (size_t eval_index = 0, num_eval_nodes = m_node_eval_order.size();
|
||||||
|
eval_index < num_eval_nodes;
|
||||||
|
eval_index++) {
|
||||||
|
size_t node_index = m_node_eval_order[eval_index];
|
||||||
|
m_node_inputs_subtree[node_index].clear();
|
||||||
|
|
||||||
|
const std::vector<size_t>& node_input_connection_indices =
|
||||||
|
m_node_input_connection_indices[node_index];
|
||||||
|
|
||||||
|
for (size_t i = 0, n = node_input_connection_indices.size(); i < n; i++) {
|
||||||
|
const BlendTreeConnectionResource& connection_resource =
|
||||||
|
m_connections[node_input_connection_indices[i]];
|
||||||
|
|
||||||
|
m_node_inputs_subtree[node_index].emplace_back(
|
||||||
|
connection_resource.source_node_index);
|
||||||
|
|
||||||
|
m_node_inputs_subtree[node_index].insert(
|
||||||
|
m_node_inputs_subtree[node_index].end(),
|
||||||
|
m_node_inputs_subtree[connection_resource.source_node_index].cbegin(),
|
||||||
|
m_node_inputs_subtree[connection_resource.source_node_index].cend());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
bool AnimGraphResource::LoadFromFile(const char* filename) {
|
bool AnimGraphResource::LoadFromFile(const char* filename) {
|
||||||
|
Clear();
|
||||||
|
|
||||||
std::ifstream input_file;
|
std::ifstream input_file;
|
||||||
input_file.open(filename);
|
input_file.open(filename);
|
||||||
std::stringstream buffer;
|
std::stringstream buffer;
|
||||||
@@ -496,10 +834,12 @@ void AnimGraphResource::CreateBlendTreeInstance(
|
|||||||
|
|
||||||
result.m_name = m_name;
|
result.m_name = m_name;
|
||||||
|
|
||||||
|
NodeSocketDataOffsetMap node_offset_map;
|
||||||
|
|
||||||
CreateBlendTreeRuntimeNodeInstances(result);
|
CreateBlendTreeRuntimeNodeInstances(result);
|
||||||
PrepareBlendTreeIOData(result);
|
PrepareBlendTreeIOData(result, node_offset_map);
|
||||||
SetRuntimeNodeProperties(result);
|
SetRuntimeNodeProperties(result);
|
||||||
CreateBlendTreeConnectionInstances(result);
|
CreateBlendTreeConnectionInstances(result, node_offset_map);
|
||||||
|
|
||||||
result.UpdateOrderedNodes();
|
result.UpdateOrderedNodes();
|
||||||
result.ResetNodeStates();
|
result.ResetNodeStates();
|
||||||
@@ -507,12 +847,13 @@ void AnimGraphResource::CreateBlendTreeInstance(
|
|||||||
|
|
||||||
void AnimGraphResource::CreateBlendTreeRuntimeNodeInstances(
|
void AnimGraphResource::CreateBlendTreeRuntimeNodeInstances(
|
||||||
AnimGraphBlendTree& result) const {
|
AnimGraphBlendTree& result) const {
|
||||||
for (auto node_resource : m_blend_tree_resource.m_nodes) {
|
for (const AnimNodeResource* node_resource :
|
||||||
|
m_blend_tree_resource.GetNodes()) {
|
||||||
AnimNode* node = AnimNodeFactory(node_resource->m_node_type_name);
|
AnimNode* node = AnimNodeFactory(node_resource->m_node_type_name);
|
||||||
|
|
||||||
if (node_resource->m_node_type_name == "BlendTree") {
|
if (node_resource->m_node_type_name == "BlendTree") {
|
||||||
AnimGraphResource* embedded_blend_tree_resource =
|
const AnimGraphResource* embedded_blend_tree_resource =
|
||||||
dynamic_cast<AnimGraphResource*>(node_resource);
|
dynamic_cast<const AnimGraphResource*>(node_resource);
|
||||||
assert(embedded_blend_tree_resource != nullptr);
|
assert(embedded_blend_tree_resource != nullptr);
|
||||||
AnimGraphBlendTree* embedded_blend_tree =
|
AnimGraphBlendTree* embedded_blend_tree =
|
||||||
dynamic_cast<AnimGraphBlendTree*>(node);
|
dynamic_cast<AnimGraphBlendTree*>(node);
|
||||||
@@ -537,14 +878,15 @@ void AnimGraphResource::CreateBlendTreeRuntimeNodeInstances(
|
|||||||
}
|
}
|
||||||
|
|
||||||
void AnimGraphResource::PrepareBlendTreeIOData(
|
void AnimGraphResource::PrepareBlendTreeIOData(
|
||||||
AnimGraphBlendTree& instance) const {
|
AnimGraphBlendTree& instance,
|
||||||
|
NodeSocketDataOffsetMap& node_offset_map) const {
|
||||||
instance.m_node_descriptor =
|
instance.m_node_descriptor =
|
||||||
AnimNodeDescriptorFactory("BlendTree", instance.m_nodes[0]);
|
AnimNodeDescriptorFactory("BlendTree", instance.m_nodes[0]);
|
||||||
|
|
||||||
instance.m_node_descriptor->m_outputs =
|
instance.m_node_descriptor->m_outputs =
|
||||||
m_blend_tree_resource.m_nodes[1]->m_socket_accessor->m_outputs;
|
m_blend_tree_resource.GetNode(1)->m_socket_accessor->m_outputs;
|
||||||
instance.m_node_descriptor->m_inputs =
|
instance.m_node_descriptor->m_inputs =
|
||||||
m_blend_tree_resource.m_nodes[0]->m_socket_accessor->m_inputs;
|
m_blend_tree_resource.GetNode(0)->m_socket_accessor->m_inputs;
|
||||||
|
|
||||||
//
|
//
|
||||||
// graph inputs
|
// graph inputs
|
||||||
@@ -590,15 +932,24 @@ void AnimGraphResource::PrepareBlendTreeIOData(
|
|||||||
output_block_offset += sizeof(void*);
|
output_block_offset += sizeof(void*);
|
||||||
}
|
}
|
||||||
|
|
||||||
// connections: make source and target sockets point to the same address in the connection data storage.
|
//
|
||||||
// TODO: instead of every connection, only create data blocks for the source sockets and make sure every source socket gets allocated once.
|
// connecton data storage
|
||||||
|
//
|
||||||
size_t connection_data_storage_size = 0;
|
size_t connection_data_storage_size = 0;
|
||||||
for (const auto& connection : m_blend_tree_resource.m_connections) {
|
for (const BlendTreeConnectionResource& connection :
|
||||||
|
m_blend_tree_resource.GetConnections()) {
|
||||||
const AnimNodeResource* source_node =
|
const AnimNodeResource* source_node =
|
||||||
m_blend_tree_resource.m_nodes[connection.source_node_index];
|
m_blend_tree_resource.GetNode(connection.source_node_index);
|
||||||
Socket* source_socket = source_node->m_socket_accessor->GetOutputSocket(
|
Socket* source_socket = source_node->m_socket_accessor->GetOutputSocket(
|
||||||
connection.source_socket_name.c_str());
|
connection.source_socket_name.c_str());
|
||||||
connection_data_storage_size += source_socket->m_type_size;
|
|
||||||
|
NodeSocketPair source_socket_pair{source_node, source_socket->m_name};
|
||||||
|
if (node_offset_map.find(source_socket_pair) == node_offset_map.end()) {
|
||||||
|
node_offset_map.insert(
|
||||||
|
{source_socket_pair, connection_data_storage_size});
|
||||||
|
|
||||||
|
connection_data_storage_size += source_socket->m_type_size;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (connection_data_storage_size > 0) {
|
if (connection_data_storage_size > 0) {
|
||||||
@@ -608,21 +959,22 @@ void AnimGraphResource::PrepareBlendTreeIOData(
|
|||||||
}
|
}
|
||||||
|
|
||||||
void AnimGraphResource::CreateBlendTreeConnectionInstances(
|
void AnimGraphResource::CreateBlendTreeConnectionInstances(
|
||||||
AnimGraphBlendTree& instance) const {
|
AnimGraphBlendTree& instance,
|
||||||
|
NodeSocketDataOffsetMap& node_offset_map) const {
|
||||||
std::vector<NodeDescriptorBase*> instance_node_descriptors(
|
std::vector<NodeDescriptorBase*> instance_node_descriptors(
|
||||||
m_blend_tree_resource.m_nodes.size(),
|
m_blend_tree_resource.GetNumNodes(),
|
||||||
nullptr);
|
nullptr);
|
||||||
for (int i = 0; i < m_blend_tree_resource.m_nodes.size(); i++) {
|
for (int i = 0; i < m_blend_tree_resource.GetNumNodes(); i++) {
|
||||||
instance_node_descriptors[i] = AnimNodeDescriptorFactory(
|
instance_node_descriptors[i] = AnimNodeDescriptorFactory(
|
||||||
m_blend_tree_resource.m_nodes[i]->m_node_type_name,
|
m_blend_tree_resource.GetNode(i)->m_node_type_name,
|
||||||
instance.m_nodes[i]);
|
instance.m_nodes[i]);
|
||||||
|
|
||||||
if (i > 1
|
if (i > 1
|
||||||
&& m_blend_tree_resource.m_nodes[i]->m_node_type_name == "BlendTree") {
|
&& m_blend_tree_resource.GetNode(i)->m_node_type_name == "BlendTree") {
|
||||||
instance_node_descriptors[i]->m_inputs =
|
instance_node_descriptors[i]->m_inputs =
|
||||||
m_blend_tree_resource.m_nodes[i]->m_socket_accessor->m_inputs;
|
m_blend_tree_resource.GetNode(i)->m_socket_accessor->m_inputs;
|
||||||
instance_node_descriptors[i]->m_outputs =
|
instance_node_descriptors[i]->m_outputs =
|
||||||
m_blend_tree_resource.m_nodes[i]->m_socket_accessor->m_outputs;
|
m_blend_tree_resource.GetNode(i)->m_socket_accessor->m_outputs;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -630,8 +982,8 @@ void AnimGraphResource::CreateBlendTreeConnectionInstances(
|
|||||||
instance_node_descriptors[1]->m_outputs =
|
instance_node_descriptors[1]->m_outputs =
|
||||||
instance.m_node_descriptor->m_outputs;
|
instance.m_node_descriptor->m_outputs;
|
||||||
|
|
||||||
size_t connection_data_offset = 0;
|
for (const BlendTreeConnectionResource& connection :
|
||||||
for (const auto& connection : m_blend_tree_resource.m_connections) {
|
m_blend_tree_resource.GetConnections()) {
|
||||||
NodeDescriptorBase* source_node_descriptor =
|
NodeDescriptorBase* source_node_descriptor =
|
||||||
instance_node_descriptors[connection.source_node_index];
|
instance_node_descriptors[connection.source_node_index];
|
||||||
NodeDescriptorBase* target_node_descriptor =
|
NodeDescriptorBase* target_node_descriptor =
|
||||||
@@ -673,9 +1025,9 @@ void AnimGraphResource::CreateBlendTreeConnectionInstances(
|
|||||||
instance.m_node_output_connections[connection.source_node_index]
|
instance.m_node_output_connections[connection.source_node_index]
|
||||||
.push_back(embedded_graph_activation_connection);
|
.push_back(embedded_graph_activation_connection);
|
||||||
|
|
||||||
AnimGraphResource* source_blend_tree_resource =
|
const AnimGraphResource* source_blend_tree_resource =
|
||||||
dynamic_cast<AnimGraphResource*>(
|
dynamic_cast<const AnimGraphResource*>(
|
||||||
m_blend_tree_resource.m_nodes[connection.source_node_index]);
|
m_blend_tree_resource.GetNode(connection.source_node_index));
|
||||||
AnimGraphBlendTree* source_blend_tree =
|
AnimGraphBlendTree* source_blend_tree =
|
||||||
dynamic_cast<AnimGraphBlendTree*>(source_node);
|
dynamic_cast<AnimGraphBlendTree*>(source_node);
|
||||||
|
|
||||||
@@ -690,9 +1042,9 @@ void AnimGraphResource::CreateBlendTreeConnectionInstances(
|
|||||||
// that the embedded node knows about its connection partner in the parent
|
// that the embedded node knows about its connection partner in the parent
|
||||||
// tree. This allows the embedded node to properly activate the node in
|
// tree. This allows the embedded node to properly activate the node in
|
||||||
// the parent graph.
|
// the parent graph.
|
||||||
AnimGraphResource* target_blend_tree_resource =
|
const AnimGraphResource* target_blend_tree_resource =
|
||||||
dynamic_cast<AnimGraphResource*>(
|
dynamic_cast<const AnimGraphResource*>(
|
||||||
m_blend_tree_resource.m_nodes[connection.target_node_index]);
|
m_blend_tree_resource.GetNode(connection.target_node_index));
|
||||||
AnimGraphBlendTree* target_blend_tree =
|
AnimGraphBlendTree* target_blend_tree =
|
||||||
dynamic_cast<AnimGraphBlendTree*>(target_node);
|
dynamic_cast<AnimGraphBlendTree*>(target_node);
|
||||||
|
|
||||||
@@ -729,32 +1081,48 @@ void AnimGraphResource::CreateBlendTreeConnectionInstances(
|
|||||||
instance_connection.m_target_socket_name = target_socket->m_name;
|
instance_connection.m_target_socket_name = target_socket->m_name;
|
||||||
instance_connection.m_socket = *target_socket;
|
instance_connection.m_socket = *target_socket;
|
||||||
|
|
||||||
|
size_t socket_data_offset = 0;
|
||||||
|
|
||||||
if (connection.source_node_index == 1) {
|
if (connection.source_node_index == 1) {
|
||||||
instance_connection.m_socket = *target_socket;
|
instance_connection.m_socket = *target_socket;
|
||||||
} else if (connection.target_node_index == 0) {
|
|
||||||
instance_connection.m_socket = *source_socket;
|
|
||||||
} else {
|
} else {
|
||||||
|
if (connection.target_node_index == 0) {
|
||||||
|
instance_connection.m_socket = *source_socket;
|
||||||
|
}
|
||||||
|
|
||||||
|
NodeSocketPair node_socket_pair{
|
||||||
|
m_blend_tree_resource.GetNode(connection.source_node_index),
|
||||||
|
source_socket->m_name};
|
||||||
|
|
||||||
|
NodeSocketDataOffsetMap::const_iterator socket_data_offset_iter =
|
||||||
|
node_offset_map.find(node_socket_pair);
|
||||||
|
|
||||||
|
if (socket_data_offset_iter != node_offset_map.end()) {
|
||||||
|
socket_data_offset = socket_data_offset_iter->second;
|
||||||
|
} else {
|
||||||
|
std::cerr << "Error: could not find data offset for node '"
|
||||||
|
<< source_node->m_name << "' and socket '"
|
||||||
|
<< source_socket->m_name << "'." << std::endl;
|
||||||
|
assert(false);
|
||||||
|
abort();
|
||||||
|
}
|
||||||
|
|
||||||
*target_socket->m_reference.ptr_ptr =
|
*target_socket->m_reference.ptr_ptr =
|
||||||
&instance.m_connection_data_storage[connection_data_offset];
|
&instance.m_connection_data_storage[socket_data_offset];
|
||||||
*source_socket->m_reference.ptr_ptr =
|
*source_socket->m_reference.ptr_ptr =
|
||||||
&instance.m_connection_data_storage[connection_data_offset];
|
&instance.m_connection_data_storage[socket_data_offset];
|
||||||
|
|
||||||
|
if (source_socket->m_type == SocketType::SocketTypeAnimation) {
|
||||||
|
instance.m_animdata_blocks.push_back(
|
||||||
|
(AnimData*)(&instance
|
||||||
|
.m_connection_data_storage[socket_data_offset]));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
instance.m_node_input_connections[connection.target_node_index].push_back(
|
instance.m_node_input_connections[connection.target_node_index].push_back(
|
||||||
instance_connection);
|
instance_connection);
|
||||||
instance.m_node_output_connections[connection.source_node_index].push_back(
|
instance.m_node_output_connections[connection.source_node_index].push_back(
|
||||||
instance_connection);
|
instance_connection);
|
||||||
|
|
||||||
*instance_connection.m_socket.m_reference.ptr_ptr =
|
|
||||||
&instance.m_connection_data_storage[connection_data_offset];
|
|
||||||
|
|
||||||
if (source_socket->m_type == SocketType::SocketTypeAnimation) {
|
|
||||||
instance.m_animdata_blocks.push_back(
|
|
||||||
(AnimData*)(&instance
|
|
||||||
.m_connection_data_storage[connection_data_offset]));
|
|
||||||
}
|
|
||||||
|
|
||||||
connection_data_offset += source_socket->m_type_size;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
//
|
//
|
||||||
@@ -795,15 +1163,15 @@ void AnimGraphResource::CreateBlendTreeConnectionInstances(
|
|||||||
const_input_buffer_offset += i->m_type_size;
|
const_input_buffer_offset += i->m_type_size;
|
||||||
}
|
}
|
||||||
|
|
||||||
for (int i = 0; i < m_blend_tree_resource.m_nodes.size(); i++) {
|
for (int i = 0; i < m_blend_tree_resource.GetNumNodes(); i++) {
|
||||||
delete instance_node_descriptors[i];
|
delete instance_node_descriptors[i];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void AnimGraphResource::SetRuntimeNodeProperties(
|
void AnimGraphResource::SetRuntimeNodeProperties(
|
||||||
AnimGraphBlendTree& result) const {
|
AnimGraphBlendTree& result) const {
|
||||||
for (int i = 2; i < m_blend_tree_resource.m_nodes.size(); i++) {
|
for (int i = 2; i < m_blend_tree_resource.GetNumNodes(); i++) {
|
||||||
const AnimNodeResource* node_resource = m_blend_tree_resource.m_nodes[i];
|
const AnimNodeResource* node_resource = m_blend_tree_resource.GetNode(i);
|
||||||
|
|
||||||
NodeDescriptorBase* node_instance_accessor = AnimNodeDescriptorFactory(
|
NodeDescriptorBase* node_instance_accessor = AnimNodeDescriptorFactory(
|
||||||
node_resource->m_node_type_name,
|
node_resource->m_node_type_name,
|
||||||
|
|||||||
@@ -25,15 +25,24 @@ static inline AnimNodeResource* AnimNodeResourceFactory(
|
|||||||
const std::string& node_type_name);
|
const std::string& node_type_name);
|
||||||
|
|
||||||
struct BlendTreeConnectionResource {
|
struct BlendTreeConnectionResource {
|
||||||
size_t source_node_index = -1;
|
int source_node_index = -1;
|
||||||
std::string source_socket_name;
|
std::string source_socket_name;
|
||||||
size_t target_node_index = -1;
|
int target_node_index = -1;
|
||||||
std::string target_socket_name;
|
std::string target_socket_name;
|
||||||
|
|
||||||
|
bool operator==(const BlendTreeConnectionResource& other) const {
|
||||||
|
return (
|
||||||
|
source_node_index == other.source_node_index
|
||||||
|
&& target_node_index == other.target_node_index
|
||||||
|
&& source_socket_name == other.source_socket_name
|
||||||
|
|
||||||
|
&& target_socket_name == other.target_socket_name);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
struct BlendTreeResource {
|
struct BlendTreeResource {
|
||||||
std::vector<AnimNodeResource*> m_nodes;
|
std::vector<std::vector<size_t> > m_node_input_connection_indices;
|
||||||
std::vector<BlendTreeConnectionResource> m_connections;
|
std::vector<std::vector<size_t> > m_node_inputs_subtree;
|
||||||
|
|
||||||
~BlendTreeResource() { CleanupNodes(); }
|
~BlendTreeResource() { CleanupNodes(); }
|
||||||
|
|
||||||
@@ -41,6 +50,9 @@ struct BlendTreeResource {
|
|||||||
CleanupNodes();
|
CleanupNodes();
|
||||||
|
|
||||||
m_connections.clear();
|
m_connections.clear();
|
||||||
|
|
||||||
|
m_node_input_connection_indices.clear();
|
||||||
|
m_node_inputs_subtree.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
void CleanupNodes() {
|
void CleanupNodes() {
|
||||||
@@ -54,10 +66,13 @@ struct BlendTreeResource {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void InitGraphConnectors() {
|
void InitGraphConnectors() {
|
||||||
m_nodes.push_back(AnimNodeResourceFactory("BlendTreeSockets"));
|
AddNode(AnimNodeResourceFactory("BlendTreeSockets"));
|
||||||
m_nodes[0]->m_name = "Outputs";
|
AnimNodeResource* output_node = GetGraphOutputNode();
|
||||||
m_nodes.push_back(AnimNodeResourceFactory("BlendTreeSockets"));
|
output_node->m_name = "Outputs";
|
||||||
m_nodes[1]->m_name = "Inputs";
|
|
||||||
|
AddNode(AnimNodeResourceFactory("BlendTreeSockets"));
|
||||||
|
AnimNodeResource* input_node = GetGraphInputNode();
|
||||||
|
output_node->m_name = "Inputs";
|
||||||
}
|
}
|
||||||
|
|
||||||
[[nodiscard]] AnimNodeResource* GetGraphOutputNode() const {
|
[[nodiscard]] AnimNodeResource* GetGraphOutputNode() const {
|
||||||
@@ -67,22 +82,101 @@ struct BlendTreeResource {
|
|||||||
return m_nodes[1];
|
return m_nodes[1];
|
||||||
}
|
}
|
||||||
|
|
||||||
size_t GetNodeIndex(const AnimNodeResource* node_resource) const {
|
int GetNodeIndex(const AnimNodeResource* node_resource) const {
|
||||||
for (size_t i = 0, n = m_nodes.size(); i < n; i++) {
|
for (size_t i = 0, n = m_nodes.size(); i < n; i++) {
|
||||||
if (m_nodes[i] == node_resource) {
|
if (m_nodes[i] == node_resource) {
|
||||||
return i;
|
return i;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::cerr << "Error: could not find node index for node resource "
|
||||||
|
<< node_resource << std::endl;
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[[maybe_unused]] size_t AddNode(AnimNodeResource* node_resource) {
|
||||||
|
m_nodes.push_back(node_resource);
|
||||||
|
m_node_input_connection_indices.emplace_back();
|
||||||
|
m_node_inputs_subtree.emplace_back();
|
||||||
|
return m_nodes.size() - 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] size_t GetNumNodes() const { return m_nodes.size(); }
|
||||||
|
[[nodiscard]] AnimNodeResource* GetNode(size_t i) { return m_nodes[i]; }
|
||||||
|
[[nodiscard]] const AnimNodeResource* GetNode(size_t i) const {
|
||||||
|
return m_nodes[i];
|
||||||
|
}
|
||||||
|
[[nodiscard]] const std::vector<AnimNodeResource*>& GetNodes() const {
|
||||||
|
return m_nodes;
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] size_t GetNumConnections() const {
|
||||||
|
return m_connections.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] BlendTreeConnectionResource* GetConnection(size_t i) {
|
||||||
|
return &m_connections[i];
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] const BlendTreeConnectionResource* GetConnection(
|
||||||
|
size_t i) const {
|
||||||
|
return &m_connections[i];
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] const std::vector<BlendTreeConnectionResource>& GetConnections()
|
||||||
|
const {
|
||||||
|
return m_connections;
|
||||||
|
}
|
||||||
|
|
||||||
|
Socket* GetNodeOutputSocket(
|
||||||
|
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(
|
bool ConnectSockets(
|
||||||
const AnimNodeResource* source_node,
|
const AnimNodeResource* source_node,
|
||||||
const std::string& source_socket_name,
|
const std::string& source_socket_name,
|
||||||
const AnimNodeResource* target_node,
|
const AnimNodeResource* target_node,
|
||||||
const std::string& target_socket_name);
|
const std::string& target_socket_name);
|
||||||
|
|
||||||
|
bool DisconnectSockets(
|
||||||
|
const AnimNodeResource* source_node,
|
||||||
|
const std::string& source_socket_name,
|
||||||
|
const AnimNodeResource* target_node,
|
||||||
|
const std::string& target_socket_name);
|
||||||
|
|
||||||
|
bool IsConnectionValid(
|
||||||
|
const AnimNodeResource* source_node,
|
||||||
|
const std::string& source_socket_name,
|
||||||
|
const AnimNodeResource* target_node,
|
||||||
|
const std::string& target_socket_name) const;
|
||||||
|
|
||||||
|
const BlendTreeConnectionResource* FindConnectionForSocket(
|
||||||
|
const AnimNodeResource* node,
|
||||||
|
const std::string& socket_name) const;
|
||||||
|
|
||||||
|
bool IsSocketConnected(
|
||||||
|
const AnimNodeResource* node,
|
||||||
|
const std::string& socket_name) const {
|
||||||
|
const BlendTreeConnectionResource* connection =
|
||||||
|
FindConnectionForSocket(node, socket_name);
|
||||||
|
return connection != nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
std::vector<Socket*> GetConstantNodeInputs(
|
std::vector<Socket*> GetConstantNodeInputs(
|
||||||
std::vector<NodeDescriptorBase*>& instance_node_descriptors) const {
|
std::vector<NodeDescriptorBase*>& instance_node_descriptors) const {
|
||||||
std::vector<Socket*> result;
|
std::vector<Socket*> result;
|
||||||
@@ -136,6 +230,24 @@ struct BlendTreeResource {
|
|||||||
|
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void UpdateTreeTopologyInfo();
|
||||||
|
|
||||||
|
[[nodiscard]] const std::vector<size_t>& GetNodeEvalOrder() const {
|
||||||
|
return m_node_eval_order;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
void UpdateNodeEvalOrder() {
|
||||||
|
m_node_eval_order.clear();
|
||||||
|
UpdateNodeEvalOrderRecursive(0);
|
||||||
|
}
|
||||||
|
void UpdateNodeEvalOrderRecursive(size_t node_index);
|
||||||
|
void UpdateNodeSubtrees();
|
||||||
|
|
||||||
|
std::vector<AnimNodeResource*> m_nodes;
|
||||||
|
std::vector<BlendTreeConnectionResource> m_connections;
|
||||||
|
std::vector<size_t> m_node_eval_order;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct StateMachineTransitionResources {
|
struct StateMachineTransitionResources {
|
||||||
@@ -154,20 +266,95 @@ struct AnimGraphResource : AnimNodeResource {
|
|||||||
std::string m_graph_type_name;
|
std::string m_graph_type_name;
|
||||||
|
|
||||||
BlendTreeResource m_blend_tree_resource;
|
BlendTreeResource m_blend_tree_resource;
|
||||||
|
typedef std::pair<const AnimNodeResource*, std::string> NodeSocketPair;
|
||||||
|
typedef std::map<NodeSocketPair, int> NodeSocketDataOffsetMap;
|
||||||
|
|
||||||
StateMachineResource m_state_machine_resource;
|
StateMachineResource m_state_machine_resource;
|
||||||
|
|
||||||
|
void Clear() { m_blend_tree_resource.Reset(); }
|
||||||
bool SaveToFile(const char* filename) const;
|
bool SaveToFile(const char* filename) const;
|
||||||
bool LoadFromFile(const char* filename);
|
bool LoadFromFile(const char* filename);
|
||||||
|
|
||||||
void CreateBlendTreeInstance(AnimGraphBlendTree& result) const;
|
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;
|
void CreateStateMachineInstance(AnimGraphStateMachine& result) const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
// BlendTree
|
// BlendTree
|
||||||
bool SaveBlendTreeResourceToFile(const char* filename) const;
|
bool SaveBlendTreeResourceToFile(const char* filename) const;
|
||||||
void CreateBlendTreeRuntimeNodeInstances(AnimGraphBlendTree& result) const;
|
void CreateBlendTreeRuntimeNodeInstances(AnimGraphBlendTree& result) const;
|
||||||
void PrepareBlendTreeIOData(AnimGraphBlendTree& instance) const;
|
void PrepareBlendTreeIOData(
|
||||||
void CreateBlendTreeConnectionInstances(AnimGraphBlendTree& instance) const;
|
AnimGraphBlendTree& instance,
|
||||||
|
NodeSocketDataOffsetMap& node_offset_map) const;
|
||||||
|
void CreateBlendTreeConnectionInstances(
|
||||||
|
AnimGraphBlendTree& instance,
|
||||||
|
NodeSocketDataOffsetMap& node_offset_map) const;
|
||||||
void SetRuntimeNodeProperties(AnimGraphBlendTree& result) const;
|
void SetRuntimeNodeProperties(AnimGraphBlendTree& result) const;
|
||||||
|
|
||||||
bool SaveStateMachineResourceToFile(const char* filename) const;
|
bool SaveStateMachineResourceToFile(const char* filename) const;
|
||||||
|
|||||||
+10441
File diff suppressed because it is too large
Load Diff
+66
-99
@@ -16,13 +16,17 @@
|
|||||||
#define GLFW_INCLUDE_NONE
|
#define GLFW_INCLUDE_NONE
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
|
|
||||||
|
#include "3rdparty/imgui-node-editor/imgui_node_editor.h"
|
||||||
#include "3rdparty/json/json.hpp"
|
#include "3rdparty/json/json.hpp"
|
||||||
|
#include "AnimGraph/AnimGraphBlendTree.h"
|
||||||
|
#include "AnimGraph/AnimGraphData.h"
|
||||||
#include "Camera.h"
|
#include "Camera.h"
|
||||||
#include "GLFW/glfw3.h"
|
#include "GLFW/glfw3.h"
|
||||||
#include "SkinnedMesh.h"
|
#include "SkinnedMesh.h"
|
||||||
|
#include "SkinnedMeshRenderer.h"
|
||||||
#include "SkinnedMeshResource.h"
|
#include "SkinnedMeshResource.h"
|
||||||
|
#include "embedded_fonts.h"
|
||||||
#include "src/AnimGraph/AnimGraphEditor.h"
|
#include "src/AnimGraph/AnimGraphEditor.h"
|
||||||
#include "3rdparty/imgui-node-editor/imgui_node_editor.h"
|
|
||||||
|
|
||||||
const int Width = 1024;
|
const int Width = 1024;
|
||||||
const int Height = 768;
|
const int Height = 768;
|
||||||
@@ -49,7 +53,6 @@ static void draw_imgui(ImDrawData*);
|
|||||||
#include <cmath> // fmodf
|
#include <cmath> // fmodf
|
||||||
#include <fstream>
|
#include <fstream>
|
||||||
|
|
||||||
#include "SkinnedMeshRenderer.h"
|
|
||||||
#include "ozz/animation/runtime/animation.h"
|
#include "ozz/animation/runtime/animation.h"
|
||||||
#include "ozz/animation/runtime/sampling_job.h"
|
#include "ozz/animation/runtime/sampling_job.h"
|
||||||
#include "ozz/animation/runtime/skeleton.h"
|
#include "ozz/animation/runtime/skeleton.h"
|
||||||
@@ -156,14 +159,10 @@ struct Viewport {
|
|||||||
this->pass = sg_make_pass(&offscreen_pass_desc);
|
this->pass = sg_make_pass(&offscreen_pass_desc);
|
||||||
|
|
||||||
sg_pipeline_desc gl_pipeline_desc = {
|
sg_pipeline_desc gl_pipeline_desc = {
|
||||||
.depth = {
|
.depth = {.compare = SG_COMPAREFUNC_LESS_EQUAL, .write_enabled = true},
|
||||||
.compare = SG_COMPAREFUNC_LESS_EQUAL,
|
|
||||||
.write_enabled = true
|
|
||||||
},
|
|
||||||
.cull_mode = SG_CULLMODE_BACK,
|
.cull_mode = SG_CULLMODE_BACK,
|
||||||
.sample_count = cMSAASampleCount
|
.sample_count = cMSAASampleCount};
|
||||||
};
|
// this->pip = sg_make_pipeline(gl_pipeline_desc);
|
||||||
// this->pip = sg_make_pipeline(gl_pipeline_desc);
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -171,13 +170,6 @@ struct ApplicationConfig {
|
|||||||
int window_position[2] = {100, 30};
|
int window_position[2] = {100, 30};
|
||||||
int window_size[2] = {1000, 600};
|
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 {
|
struct GraphEditor {
|
||||||
bool visible = false;
|
bool visible = false;
|
||||||
int position[2] = {20, 20};
|
int position[2] = {20, 20};
|
||||||
@@ -223,12 +215,6 @@ void to_json(nlohmann::json& j, const ApplicationConfig& config) {
|
|||||||
j["main_window"]["size"][0] = config.window_size[0];
|
j["main_window"]["size"][0] = config.window_size[0];
|
||||||
j["main_window"]["size"][1] = config.window_size[1];
|
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"]["visible"] = config.graph_editor.visible;
|
||||||
j["graph_editor"]["position"][0] = config.graph_editor.position[0];
|
j["graph_editor"]["position"][0] = config.graph_editor.position[0];
|
||||||
j["graph_editor"]["position"][1] = config.graph_editor.position[1];
|
j["graph_editor"]["position"][1] = config.graph_editor.position[1];
|
||||||
@@ -280,24 +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.contains("graph_editor")) {
|
||||||
if (j["graph_editor"].contains("visible")) {
|
if (j["graph_editor"].contains("visible")) {
|
||||||
config.graph_editor.visible = j["graph_editor"]["visible"];
|
config.graph_editor.visible = j["graph_editor"]["visible"];
|
||||||
@@ -529,7 +497,9 @@ int main() {
|
|||||||
|
|
||||||
// setup sokol_gfx and sokol_time
|
// setup sokol_gfx and sokol_time
|
||||||
stm_setup();
|
stm_setup();
|
||||||
sg_desc desc = {.logger = {.func = slog_func}, .context {.sample_count = cMSAASampleCount}};
|
sg_desc desc = {
|
||||||
|
.logger = {.func = slog_func},
|
||||||
|
.context{.sample_count = cMSAASampleCount}};
|
||||||
sg_setup(&desc);
|
sg_setup(&desc);
|
||||||
assert(sg_isvalid());
|
assert(sg_isvalid());
|
||||||
|
|
||||||
@@ -550,11 +520,12 @@ int main() {
|
|||||||
skinned_mesh_resource.createInstance(skinned_mesh);
|
skinned_mesh_resource.createInstance(skinned_mesh);
|
||||||
skinned_mesh.SetCurrentAnimation(0);
|
skinned_mesh.SetCurrentAnimation(0);
|
||||||
|
|
||||||
AnimGraph anim_graph;
|
AnimGraphBlendTree anim_graph;
|
||||||
AnimGraphContext anim_graph_context;
|
AnimGraphContext anim_graph_context;
|
||||||
AnimData anim_graph_output;
|
AnimData anim_graph_output;
|
||||||
anim_graph_output.m_local_matrices.resize(
|
anim_graph_output.m_local_matrices.resize(
|
||||||
skinned_mesh.m_skeleton.num_soa_joints());
|
skinned_mesh.m_skeleton.num_soa_joints());
|
||||||
|
AnimGraphEditorClear();
|
||||||
|
|
||||||
state.time.factor = 1.0f;
|
state.time.factor = 1.0f;
|
||||||
|
|
||||||
@@ -566,7 +537,20 @@ int main() {
|
|||||||
ImGuiIO& io = ImGui::GetIO();
|
ImGuiIO& io = ImGui::GetIO();
|
||||||
io.ConfigFlags |= ImGuiConfigFlags_DockingEnable;
|
io.ConfigFlags |= ImGuiConfigFlags_DockingEnable;
|
||||||
io.IniFilename = "ATPImgui.ini";
|
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_Tab] = GLFW_KEY_TAB;
|
||||||
io.KeyMap[ImGuiKey_LeftArrow] = GLFW_KEY_LEFT;
|
io.KeyMap[ImGuiKey_LeftArrow] = GLFW_KEY_LEFT;
|
||||||
io.KeyMap[ImGuiKey_RightArrow] = GLFW_KEY_RIGHT;
|
io.KeyMap[ImGuiKey_RightArrow] = GLFW_KEY_RIGHT;
|
||||||
@@ -673,7 +657,8 @@ int main() {
|
|||||||
// Graph Editor
|
// Graph Editor
|
||||||
gApplicationConfig.graph_editor.config.SettingsFile = "graph_editor.json";
|
gApplicationConfig.graph_editor.config.SettingsFile = "graph_editor.json";
|
||||||
gApplicationConfig.graph_editor.config.NavigateButtonIndex = 2;
|
gApplicationConfig.graph_editor.config.NavigateButtonIndex = 2;
|
||||||
gApplicationConfig.graph_editor.context = ax::NodeEditor::CreateEditor(&gApplicationConfig.graph_editor.config);
|
gApplicationConfig.graph_editor.context =
|
||||||
|
ax::NodeEditor::CreateEditor(&gApplicationConfig.graph_editor.config);
|
||||||
|
|
||||||
// draw loop
|
// draw loop
|
||||||
while (!glfwWindowShouldClose(w)) {
|
while (!glfwWindowShouldClose(w)) {
|
||||||
@@ -795,9 +780,6 @@ int main() {
|
|||||||
&gApplicationConfig.animation_player_widget.visible);
|
&gApplicationConfig.animation_player_widget.visible);
|
||||||
|
|
||||||
ImGui::Separator();
|
ImGui::Separator();
|
||||||
ImGui::Checkbox(
|
|
||||||
"Legacy Graph Editor",
|
|
||||||
&gApplicationConfig.legacy_graph_editor.visible);
|
|
||||||
|
|
||||||
ImGui::Checkbox(
|
ImGui::Checkbox(
|
||||||
"ImGui Demo",
|
"ImGui Demo",
|
||||||
@@ -811,12 +793,12 @@ int main() {
|
|||||||
|
|
||||||
AnimGraphEditorGetRuntimeGraph(anim_graph);
|
AnimGraphEditorGetRuntimeGraph(anim_graph);
|
||||||
anim_graph_context.m_skeleton = &skinned_mesh.m_skeleton;
|
anim_graph_context.m_skeleton = &skinned_mesh.m_skeleton;
|
||||||
anim_graph.init(anim_graph_context);
|
anim_graph.Init(anim_graph_context);
|
||||||
|
|
||||||
// For simplicity use first animation data output
|
// For simplicity use first animation data output
|
||||||
const std::vector<Socket>& graph_output_sockets =
|
const std::vector<Socket>& graph_output_sockets =
|
||||||
anim_graph.getGraphOutputs();
|
anim_graph.GetGraphOutputs();
|
||||||
for (const auto & output : graph_output_sockets) {
|
for (const auto& output : graph_output_sockets) {
|
||||||
if (output.m_type == SocketType::SocketTypeAnimation) {
|
if (output.m_type == SocketType::SocketTypeAnimation) {
|
||||||
anim_graph.SetOutput(output.m_name.c_str(), &anim_graph_output);
|
anim_graph.SetOutput(output.m_name.c_str(), &anim_graph_output);
|
||||||
}
|
}
|
||||||
@@ -845,7 +827,8 @@ int main() {
|
|||||||
|
|
||||||
ImGui::Begin("Viewport", &gApplicationConfig.viewport_widget.visible);
|
ImGui::Begin("Viewport", &gApplicationConfig.viewport_widget.visible);
|
||||||
|
|
||||||
if (ImGui::IsWindowHovered() && ImGui::IsMouseDown(ImGuiMouseButton_Right)) {
|
if (ImGui::IsWindowHovered()
|
||||||
|
&& ImGui::IsMouseDown(ImGuiMouseButton_Right)) {
|
||||||
if (gControlMode == ControlMode::ControlModeNone) {
|
if (gControlMode == ControlMode::ControlModeNone) {
|
||||||
gControlMode = ControlMode::ControlModeFPS;
|
gControlMode = ControlMode::ControlModeFPS;
|
||||||
Camera_CalcFromMatrix(&state.camera, &state.camera.mtxView[0]);
|
Camera_CalcFromMatrix(&state.camera, &state.camera.mtxView[0]);
|
||||||
@@ -854,8 +837,10 @@ int main() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
ImVec2 viewport_widget_size = ImGui::GetWindowSize();
|
ImVec2 viewport_widget_size = ImGui::GetWindowSize();
|
||||||
gApplicationConfig.viewport_widget.size[0] = static_cast<int>(viewport_widget_size.x);
|
gApplicationConfig.viewport_widget.size[0] =
|
||||||
gApplicationConfig.viewport_widget.size[1] = static_cast<int>(viewport_widget_size.y);
|
static_cast<int>(viewport_widget_size.x);
|
||||||
|
gApplicationConfig.viewport_widget.size[1] =
|
||||||
|
static_cast<int>(viewport_widget_size.y);
|
||||||
|
|
||||||
ImGui::Text(
|
ImGui::Text(
|
||||||
"Viewport size: %d, %d",
|
"Viewport size: %d, %d",
|
||||||
@@ -869,12 +854,16 @@ int main() {
|
|||||||
if (static_cast<float>(current_size[0]) != content_size[0]
|
if (static_cast<float>(current_size[0]) != content_size[0]
|
||||||
|| static_cast<float>(current_size[1]) != content_size[1]
|
|| static_cast<float>(current_size[1]) != content_size[1]
|
||||||
|| offscreen_viewport.pass.id == 0) {
|
|| offscreen_viewport.pass.id == 0) {
|
||||||
offscreen_viewport.Resize(static_cast<int>(content_size[0]), static_cast<int>(content_size[1]));
|
offscreen_viewport.Resize(
|
||||||
|
static_cast<int>(content_size[0]),
|
||||||
|
static_cast<int>(content_size[1]));
|
||||||
}
|
}
|
||||||
|
|
||||||
ImGui::Image(
|
ImGui::Image(
|
||||||
(ImTextureID)(uintptr_t)offscreen_viewport.color_image.id,
|
(ImTextureID)(uintptr_t)offscreen_viewport.color_image.id,
|
||||||
ImVec2(static_cast<float>(offscreen_viewport.size[0]), static_cast<float>(offscreen_viewport.size[1])),
|
ImVec2(
|
||||||
|
static_cast<float>(offscreen_viewport.size[0]),
|
||||||
|
static_cast<float>(offscreen_viewport.size[1])),
|
||||||
ImVec2(0.0f, 1.0f),
|
ImVec2(0.0f, 1.0f),
|
||||||
ImVec2(1.0f, 0.0f));
|
ImVec2(1.0f, 0.0f));
|
||||||
|
|
||||||
@@ -897,13 +886,17 @@ int main() {
|
|||||||
if (gApplicationConfig.skinned_mesh_widget.visible) {
|
if (gApplicationConfig.skinned_mesh_widget.visible) {
|
||||||
ImGui::SetNextWindowPos(
|
ImGui::SetNextWindowPos(
|
||||||
ImVec2(
|
ImVec2(
|
||||||
static_cast<float>(gApplicationConfig.skinned_mesh_widget.position[0]),
|
static_cast<float>(
|
||||||
static_cast<float>(gApplicationConfig.skinned_mesh_widget.position[1])),
|
gApplicationConfig.skinned_mesh_widget.position[0]),
|
||||||
|
static_cast<float>(
|
||||||
|
gApplicationConfig.skinned_mesh_widget.position[1])),
|
||||||
ImGuiCond_FirstUseEver);
|
ImGuiCond_FirstUseEver);
|
||||||
ImGui::SetNextWindowSize(
|
ImGui::SetNextWindowSize(
|
||||||
ImVec2(
|
ImVec2(
|
||||||
static_cast<float>(gApplicationConfig.skinned_mesh_widget.size[0]),
|
static_cast<float>(
|
||||||
static_cast<float>(gApplicationConfig.skinned_mesh_widget.size[1])),
|
gApplicationConfig.skinned_mesh_widget.size[0]),
|
||||||
|
static_cast<float>(
|
||||||
|
gApplicationConfig.skinned_mesh_widget.size[1])),
|
||||||
ImGuiCond_FirstUseEver);
|
ImGuiCond_FirstUseEver);
|
||||||
|
|
||||||
ImGui::Begin(
|
ImGui::Begin(
|
||||||
@@ -930,13 +923,17 @@ int main() {
|
|||||||
if (gApplicationConfig.animation_player_widget.visible) {
|
if (gApplicationConfig.animation_player_widget.visible) {
|
||||||
ImGui::SetNextWindowPos(
|
ImGui::SetNextWindowPos(
|
||||||
ImVec2(
|
ImVec2(
|
||||||
static_cast<float>(gApplicationConfig.animation_player_widget.position[0]),
|
static_cast<float>(
|
||||||
static_cast<float>(gApplicationConfig.animation_player_widget.position[1])),
|
gApplicationConfig.animation_player_widget.position[0]),
|
||||||
|
static_cast<float>(
|
||||||
|
gApplicationConfig.animation_player_widget.position[1])),
|
||||||
ImGuiCond_FirstUseEver);
|
ImGuiCond_FirstUseEver);
|
||||||
ImGui::SetNextWindowSize(
|
ImGui::SetNextWindowSize(
|
||||||
ImVec2(
|
ImVec2(
|
||||||
static_cast<float>(gApplicationConfig.animation_player_widget.size[0]),
|
static_cast<float>(
|
||||||
static_cast<float>(gApplicationConfig.animation_player_widget.size[1])),
|
gApplicationConfig.animation_player_widget.size[0]),
|
||||||
|
static_cast<float>(
|
||||||
|
gApplicationConfig.animation_player_widget.size[1])),
|
||||||
ImGuiCond_FirstUseEver);
|
ImGuiCond_FirstUseEver);
|
||||||
|
|
||||||
ImGui::Begin(
|
ImGui::Begin(
|
||||||
@@ -1015,8 +1012,8 @@ int main() {
|
|||||||
|
|
||||||
if (state.ozz.animation != nullptr) {
|
if (state.ozz.animation != nullptr) {
|
||||||
state.ozz.sampling_job.animation = state.ozz.animation;
|
state.ozz.sampling_job.animation = state.ozz.animation;
|
||||||
state.ozz.sampling_job.ratio =
|
state.ozz.sampling_job.ratio = static_cast<float>(state.time.absolute)
|
||||||
static_cast<float>(state.time.absolute) / state.ozz.animation->duration();
|
/ state.ozz.animation->duration();
|
||||||
state.ozz.sampling_job.context = &skinned_mesh.m_sampling_context;
|
state.ozz.sampling_job.context = &skinned_mesh.m_sampling_context;
|
||||||
state.ozz.sampling_job.output =
|
state.ozz.sampling_job.output =
|
||||||
ozz::make_span(skinned_mesh.m_local_matrices);
|
ozz::make_span(skinned_mesh.m_local_matrices);
|
||||||
@@ -1031,9 +1028,10 @@ int main() {
|
|||||||
|
|
||||||
if (state.time.use_graph && !anim_graph.m_nodes.empty()
|
if (state.time.use_graph && !anim_graph.m_nodes.empty()
|
||||||
&& state.time.anim_update_time > 0.) {
|
&& state.time.anim_update_time > 0.) {
|
||||||
anim_graph.markActiveNodes();
|
// TODO: update for new API after embedding refactor
|
||||||
anim_graph.updateTime(state.time.anim_update_time);
|
// anim_graph.MarkActiveNodes();
|
||||||
anim_graph.evaluate(anim_graph_context);
|
// anim_graph.UpdateTime()pdateTime(state.time.anim_update_time);
|
||||||
|
// anim_graph.evaluate(anim_graph_context);
|
||||||
skinned_mesh.m_local_matrices = anim_graph_output.m_local_matrices;
|
skinned_mesh.m_local_matrices = anim_graph_output.m_local_matrices;
|
||||||
|
|
||||||
skinned_mesh.CalcModelMatrices();
|
skinned_mesh.CalcModelMatrices();
|
||||||
@@ -1080,37 +1078,6 @@ int main() {
|
|||||||
ImGui::End();
|
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()
|
// 3. Show the ImGui test window. Most of the sample code is in ImGui::ShowDemoWindow()
|
||||||
if (gApplicationConfig.show_imgui_demo_window) {
|
if (gApplicationConfig.show_imgui_demo_window) {
|
||||||
ImGui::SetNextWindowPos(ImVec2(460, 20), ImGuiCond_FirstUseEver);
|
ImGui::SetNextWindowPos(ImVec2(460, 20), ImGuiCond_FirstUseEver);
|
||||||
|
|||||||
@@ -0,0 +1,40 @@
|
|||||||
|
#include "AnimGraph/AnimGraphEditor.h"
|
||||||
|
#include "catch.hpp"
|
||||||
|
|
||||||
|
TEST_CASE("Node Socket To InputPin Conversion", "[animGraphEditor]") {
|
||||||
|
int node_index = 321;
|
||||||
|
int socket_index = 221;
|
||||||
|
long socket_id;
|
||||||
|
|
||||||
|
socket_id = NodeIndexAndSocketIndexToInputPinId(node_index, socket_index);
|
||||||
|
|
||||||
|
int node_index_resolved;
|
||||||
|
int socket_index_resolved;
|
||||||
|
|
||||||
|
InputPinIdToNodeIndexAndSocketIndex(
|
||||||
|
socket_id,
|
||||||
|
&node_index_resolved,
|
||||||
|
&socket_index_resolved);
|
||||||
|
|
||||||
|
CHECK(node_index == node_index_resolved);
|
||||||
|
CHECK(socket_index == socket_index_resolved);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE("Node Socket To OutputPin Conversion", "[animGraphEditor]") {
|
||||||
|
int node_index = 321;
|
||||||
|
int socket_index = 221;
|
||||||
|
long socket_id;
|
||||||
|
|
||||||
|
socket_id = NodeIndexAndSocketIndexToOutputPinId(node_index, socket_index);
|
||||||
|
|
||||||
|
int node_index_resolved;
|
||||||
|
int socket_index_resolved;
|
||||||
|
|
||||||
|
OutputPinIdToNodeIndexAndSocketIndex(
|
||||||
|
socket_id,
|
||||||
|
&node_index_resolved,
|
||||||
|
&socket_index_resolved);
|
||||||
|
|
||||||
|
CHECK(node_index == node_index_resolved);
|
||||||
|
CHECK(socket_index == socket_index_resolved);
|
||||||
|
}
|
||||||
+236
-247
@@ -28,11 +28,9 @@ class SimpleAnimSamplerGraphResource {
|
|||||||
blend_tree_resource->InitGraphConnectors();
|
blend_tree_resource->InitGraphConnectors();
|
||||||
|
|
||||||
// Prepare graph inputs and outputs
|
// Prepare graph inputs and outputs
|
||||||
blend_tree_resource->m_nodes.push_back(
|
walk_node_index =
|
||||||
AnimNodeResourceFactory("AnimSampler"));
|
blend_tree_resource->AddNode(AnimNodeResourceFactory("AnimSampler"));
|
||||||
walk_node_index = blend_tree_resource->m_nodes.size() - 1;
|
walk_node = blend_tree_resource->GetNode(walk_node_index);
|
||||||
|
|
||||||
walk_node = blend_tree_resource->m_nodes[walk_node_index];
|
|
||||||
walk_node->m_name = "WalkAnim";
|
walk_node->m_name = "WalkAnim";
|
||||||
walk_node->m_socket_accessor->SetPropertyValue(
|
walk_node->m_socket_accessor->SetPropertyValue(
|
||||||
"Filename",
|
"Filename",
|
||||||
@@ -72,28 +70,26 @@ class Blend2GraphResource {
|
|||||||
blend_tree_resource->InitGraphConnectors();
|
blend_tree_resource->InitGraphConnectors();
|
||||||
|
|
||||||
// Prepare graph inputs and outputs
|
// Prepare graph inputs and outputs
|
||||||
blend_tree_resource->m_nodes.push_back(
|
walk_node_index =
|
||||||
AnimNodeResourceFactory("AnimSampler"));
|
blend_tree_resource->AddNode(AnimNodeResourceFactory("AnimSampler"));
|
||||||
walk_node_index = blend_tree_resource->m_nodes.size() - 1;
|
|
||||||
|
|
||||||
blend_tree_resource->m_nodes.push_back(
|
run_node_index =
|
||||||
AnimNodeResourceFactory("AnimSampler"));
|
blend_tree_resource->AddNode(AnimNodeResourceFactory("AnimSampler"));
|
||||||
run_node_index = blend_tree_resource->m_nodes.size() - 1;
|
|
||||||
|
|
||||||
blend_tree_resource->m_nodes.push_back(AnimNodeResourceFactory("Blend2"));
|
blend_node_index =
|
||||||
blend_node_index = blend_tree_resource->m_nodes.size() - 1;
|
blend_tree_resource->AddNode(AnimNodeResourceFactory("Blend2"));
|
||||||
|
|
||||||
walk_node = blend_tree_resource->m_nodes[walk_node_index];
|
walk_node = blend_tree_resource->GetNode(walk_node_index);
|
||||||
walk_node->m_name = "WalkAnim";
|
walk_node->m_name = "WalkAnim";
|
||||||
walk_node->m_socket_accessor->SetPropertyValue(
|
walk_node->m_socket_accessor->SetPropertyValue(
|
||||||
"Filename",
|
"Filename",
|
||||||
std::string("media/Walking-loop.ozz"));
|
std::string("media/Walking-loop.ozz"));
|
||||||
run_node = blend_tree_resource->m_nodes[run_node_index];
|
run_node = blend_tree_resource->GetNode(run_node_index);
|
||||||
run_node->m_socket_accessor->SetPropertyValue(
|
run_node->m_socket_accessor->SetPropertyValue(
|
||||||
"Filename",
|
"Filename",
|
||||||
std::string("media/Running0-loop.ozz"));
|
std::string("media/Running0-loop.ozz"));
|
||||||
run_node->m_name = "RunAnim";
|
run_node->m_name = "RunAnim";
|
||||||
blend_node = blend_tree_resource->m_nodes[blend_node_index];
|
blend_node = blend_tree_resource->GetNode(blend_node_index);
|
||||||
blend_node->m_name = "BlendWalkRun";
|
blend_node->m_name = "BlendWalkRun";
|
||||||
|
|
||||||
AnimNodeResource* graph_node = blend_tree_resource->GetGraphOutputNode();
|
AnimNodeResource* graph_node = blend_tree_resource->GetGraphOutputNode();
|
||||||
@@ -161,11 +157,10 @@ class EmbeddedBlendTreeGraphResource {
|
|||||||
nullptr);
|
nullptr);
|
||||||
|
|
||||||
// Parent AnimSampler
|
// Parent AnimSampler
|
||||||
parent_blend_tree_resource->m_nodes.push_back(
|
walk_node_index = parent_blend_tree_resource->AddNode(
|
||||||
AnimNodeResourceFactory("AnimSampler"));
|
AnimNodeResourceFactory("AnimSampler"));
|
||||||
walk_node_index = parent_blend_tree_resource->m_nodes.size() - 1;
|
|
||||||
|
|
||||||
walk_node_resource = parent_blend_tree_resource->m_nodes[walk_node_index];
|
walk_node_resource = parent_blend_tree_resource->GetNode(walk_node_index);
|
||||||
walk_node_resource->m_name = "WalkAnim";
|
walk_node_resource->m_name = "WalkAnim";
|
||||||
walk_node_resource->m_socket_accessor->SetPropertyValue(
|
walk_node_resource->m_socket_accessor->SetPropertyValue(
|
||||||
"Filename",
|
"Filename",
|
||||||
@@ -174,12 +169,10 @@ class EmbeddedBlendTreeGraphResource {
|
|||||||
//
|
//
|
||||||
// Embedded Tree
|
// Embedded Tree
|
||||||
//
|
//
|
||||||
parent_blend_tree_resource->m_nodes.push_back(
|
embedded_blend_tree_node_index = parent_blend_tree_resource->AddNode(
|
||||||
AnimNodeResourceFactory("BlendTree"));
|
AnimNodeResourceFactory("BlendTree"));
|
||||||
embedded_blend_tree_node_index =
|
|
||||||
parent_blend_tree_resource->m_nodes.size() - 1;
|
|
||||||
embedded_graph = dynamic_cast<AnimGraphResource*>(
|
embedded_graph = dynamic_cast<AnimGraphResource*>(
|
||||||
parent_blend_tree_resource->m_nodes.back());
|
parent_blend_tree_resource->GetNode(embedded_blend_tree_node_index));
|
||||||
embedded_graph->m_name = "EmbeddedBlendTree";
|
embedded_graph->m_name = "EmbeddedBlendTree";
|
||||||
embedded_graph->m_node_type_name = "BlendTree";
|
embedded_graph->m_node_type_name = "BlendTree";
|
||||||
embedded_graph->m_graph_type_name = "BlendTree";
|
embedded_graph->m_graph_type_name = "BlendTree";
|
||||||
@@ -200,12 +193,10 @@ class EmbeddedBlendTreeGraphResource {
|
|||||||
nullptr);
|
nullptr);
|
||||||
|
|
||||||
// Embedded: SpeedScale node
|
// Embedded: SpeedScale node
|
||||||
embedded_blend_tree_resource->m_nodes.push_back(
|
embedded_speed_scale_index = embedded_blend_tree_resource->AddNode(
|
||||||
AnimNodeResourceFactory("SpeedScale"));
|
AnimNodeResourceFactory("SpeedScale"));
|
||||||
embedded_speed_scale_index =
|
|
||||||
embedded_blend_tree_resource->m_nodes.size() - 1;
|
|
||||||
AnimNodeResource* embedded_speed_scale_resource =
|
AnimNodeResource* embedded_speed_scale_resource =
|
||||||
embedded_blend_tree_resource->m_nodes[embedded_speed_scale_index];
|
embedded_blend_tree_resource->GetNode(embedded_speed_scale_index);
|
||||||
embedded_speed_scale_resource->m_socket_accessor->SetInputValue(
|
embedded_speed_scale_resource->m_socket_accessor->SetInputValue(
|
||||||
"SpeedScale",
|
"SpeedScale",
|
||||||
0.1f);
|
0.1f);
|
||||||
@@ -291,11 +282,10 @@ class EmbeddedTreeBlend2GraphResource {
|
|||||||
nullptr);
|
nullptr);
|
||||||
|
|
||||||
// Parent AnimSampler
|
// Parent AnimSampler
|
||||||
parent_blend_tree_resource->m_nodes.push_back(
|
walk_node_index = parent_blend_tree_resource->AddNode(
|
||||||
AnimNodeResourceFactory("AnimSampler"));
|
AnimNodeResourceFactory("AnimSampler"));
|
||||||
walk_node_index = parent_blend_tree_resource->m_nodes.size() - 1;
|
|
||||||
|
|
||||||
walk_node_resource = parent_blend_tree_resource->m_nodes[walk_node_index];
|
walk_node_resource = parent_blend_tree_resource->GetNode(walk_node_index);
|
||||||
walk_node_resource->m_name = "WalkAnim";
|
walk_node_resource->m_name = "WalkAnim";
|
||||||
walk_node_resource->m_socket_accessor->SetPropertyValue(
|
walk_node_resource->m_socket_accessor->SetPropertyValue(
|
||||||
"Filename",
|
"Filename",
|
||||||
@@ -304,53 +294,38 @@ class EmbeddedTreeBlend2GraphResource {
|
|||||||
//
|
//
|
||||||
// Embedded Tree
|
// Embedded Tree
|
||||||
//
|
//
|
||||||
parent_blend_tree_resource->m_nodes.push_back(
|
embedded_blend_tree_node_index = parent_blend_tree_resource->AddNode(
|
||||||
AnimNodeResourceFactory("BlendTree"));
|
AnimNodeResourceFactory("BlendTree"));
|
||||||
embedded_blend_tree_node_index =
|
|
||||||
parent_blend_tree_resource->m_nodes.size() - 1;
|
|
||||||
embedded_graph = dynamic_cast<AnimGraphResource*>(
|
embedded_graph = dynamic_cast<AnimGraphResource*>(
|
||||||
parent_blend_tree_resource->m_nodes.back());
|
parent_blend_tree_resource->GetNode(embedded_blend_tree_node_index));
|
||||||
embedded_graph->m_name = "EmbeddedTreeBlend2GraphResource";
|
embedded_graph->m_name = "EmbeddedTreeBlend2GraphResource";
|
||||||
embedded_graph->m_node_type_name = "BlendTree";
|
embedded_graph->m_node_type_name = "BlendTree";
|
||||||
embedded_graph->m_graph_type_name = "BlendTree";
|
embedded_graph->m_graph_type_name = "BlendTree";
|
||||||
embedded_blend_tree_resource = &embedded_graph->m_blend_tree_resource;
|
embedded_blend_tree_resource = &embedded_graph->m_blend_tree_resource;
|
||||||
|
|
||||||
// Embedded: outputs
|
// Embedded: outputs
|
||||||
AnimNodeResource* embedded_outputs =
|
embedded_graph->RegisterBlendTreeOutputSocket<AnimData>("AnimOutput");
|
||||||
embedded_blend_tree_resource->GetGraphOutputNode();
|
|
||||||
embedded_outputs->m_socket_accessor->RegisterInput<AnimData>(
|
|
||||||
"AnimOutput",
|
|
||||||
nullptr);
|
|
||||||
|
|
||||||
// Embedded: inputs
|
// Embedded: inputs
|
||||||
AnimNodeResource* embedded_inputs =
|
embedded_graph->RegisterBlendTreeInputSocket<AnimData>("AnimInput");
|
||||||
embedded_blend_tree_resource->GetGraphInputNode();
|
embedded_graph->RegisterBlendTreeInputSocket<float>("BlendWeight");
|
||||||
embedded_inputs->m_socket_accessor->RegisterOutput<AnimData>(
|
|
||||||
"AnimInput",
|
|
||||||
nullptr);
|
|
||||||
embedded_inputs->m_socket_accessor->RegisterOutput<float>(
|
|
||||||
"BlendWeight",
|
|
||||||
nullptr);
|
|
||||||
|
|
||||||
// Embedded nodes
|
// Embedded nodes
|
||||||
embedded_blend_tree_resource->m_nodes.push_back(
|
embedded_blend2_node_index = embedded_blend_tree_resource->AddNode(
|
||||||
AnimNodeResourceFactory("Blend2"));
|
AnimNodeResourceFactory("Blend2"));
|
||||||
embedded_blend2_node_index =
|
|
||||||
embedded_blend_tree_resource->m_nodes.size() - 1;
|
|
||||||
|
|
||||||
embedded_blend_tree_resource->m_nodes.push_back(
|
embedded_run_node_index = embedded_blend_tree_resource->AddNode(
|
||||||
AnimNodeResourceFactory("AnimSampler"));
|
AnimNodeResourceFactory("AnimSampler"));
|
||||||
embedded_run_node_index = embedded_blend_tree_resource->m_nodes.size() - 1;
|
|
||||||
|
|
||||||
// Configure node resources
|
// Configure node resources
|
||||||
AnimNodeResource* embedded_blend2_node_resource =
|
embedded_blend2_node_resource =
|
||||||
embedded_blend_tree_resource->m_nodes[embedded_blend2_node_index];
|
embedded_blend_tree_resource->GetNode(embedded_blend2_node_index);
|
||||||
embedded_blend2_node_resource->m_socket_accessor->SetInputValue(
|
embedded_blend2_node_resource->m_socket_accessor->SetInputValue(
|
||||||
"Weight",
|
"Weight",
|
||||||
0.1f);
|
0.1f);
|
||||||
|
|
||||||
embedded_run_node_resource =
|
embedded_run_node_resource =
|
||||||
embedded_blend_tree_resource->m_nodes[embedded_run_node_index];
|
embedded_blend_tree_resource->GetNode(embedded_run_node_index);
|
||||||
embedded_run_node_resource->m_name = "RunAnim";
|
embedded_run_node_resource->m_name = "RunAnim";
|
||||||
embedded_run_node_resource->m_socket_accessor->SetPropertyValue(
|
embedded_run_node_resource->m_socket_accessor->SetPropertyValue(
|
||||||
"Filename",
|
"Filename",
|
||||||
@@ -358,7 +333,7 @@ class EmbeddedTreeBlend2GraphResource {
|
|||||||
|
|
||||||
// Embedded: setup connections
|
// Embedded: setup connections
|
||||||
REQUIRE(embedded_blend_tree_resource->ConnectSockets(
|
REQUIRE(embedded_blend_tree_resource->ConnectSockets(
|
||||||
embedded_inputs,
|
embedded_blend_tree_resource->GetGraphInputNode(),
|
||||||
"AnimInput",
|
"AnimInput",
|
||||||
embedded_blend2_node_resource,
|
embedded_blend2_node_resource,
|
||||||
"Input0"));
|
"Input0"));
|
||||||
@@ -373,7 +348,7 @@ class EmbeddedTreeBlend2GraphResource {
|
|||||||
embedded_blend_tree_resource->GetGraphOutputNode(),
|
embedded_blend_tree_resource->GetGraphOutputNode(),
|
||||||
"AnimOutput"));
|
"AnimOutput"));
|
||||||
REQUIRE(embedded_blend_tree_resource->ConnectSockets(
|
REQUIRE(embedded_blend_tree_resource->ConnectSockets(
|
||||||
embedded_inputs,
|
embedded_blend_tree_resource->GetGraphInputNode(),
|
||||||
"BlendWeight",
|
"BlendWeight",
|
||||||
embedded_blend2_node_resource,
|
embedded_blend2_node_resource,
|
||||||
"Weight"));
|
"Weight"));
|
||||||
@@ -418,6 +393,64 @@ bool load_skeleton(ozz::animation::Skeleton& skeleton, const char* filename) {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void CheckBlendTreeResourcesEqual(
|
||||||
|
const BlendTreeResource* blend_tree_resource_reference,
|
||||||
|
const BlendTreeResource* blend_tree_resource_rhs) {
|
||||||
|
REQUIRE(
|
||||||
|
blend_tree_resource_reference->GetNumNodes()
|
||||||
|
== blend_tree_resource_rhs->GetNumNodes());
|
||||||
|
for (size_t i = 0; i < blend_tree_resource_reference->GetNumNodes(); i++) {
|
||||||
|
const AnimNodeResource* node = blend_tree_resource_reference->GetNode(i);
|
||||||
|
const AnimNodeResource* node_loaded = blend_tree_resource_rhs->GetNode(i);
|
||||||
|
|
||||||
|
REQUIRE(node->m_name == node_loaded->m_name);
|
||||||
|
REQUIRE(node->m_node_type_name == node_loaded->m_node_type_name);
|
||||||
|
}
|
||||||
|
|
||||||
|
REQUIRE(
|
||||||
|
blend_tree_resource_reference->GetNumConnections()
|
||||||
|
== blend_tree_resource_rhs->GetNumConnections());
|
||||||
|
for (size_t i = 0; i < blend_tree_resource_reference->GetNumConnections();
|
||||||
|
i++) {
|
||||||
|
const BlendTreeConnectionResource* connection =
|
||||||
|
blend_tree_resource_reference->GetConnection(i);
|
||||||
|
const BlendTreeConnectionResource* connection_loaded =
|
||||||
|
blend_tree_resource_rhs->GetConnection(i);
|
||||||
|
|
||||||
|
REQUIRE(
|
||||||
|
connection->source_node_index == connection_loaded->source_node_index);
|
||||||
|
REQUIRE(
|
||||||
|
connection->source_socket_name
|
||||||
|
== connection_loaded->source_socket_name);
|
||||||
|
REQUIRE(
|
||||||
|
connection->target_node_index == connection_loaded->target_node_index);
|
||||||
|
REQUIRE(
|
||||||
|
connection->target_socket_name
|
||||||
|
== connection_loaded->target_socket_name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void CheckAnimGraphResourceEqual(
|
||||||
|
const AnimGraphResource& graph_resource_reference,
|
||||||
|
const AnimGraphResource& graph_resource_rhs) {
|
||||||
|
REQUIRE(
|
||||||
|
graph_resource_reference.m_graph_type_name
|
||||||
|
== graph_resource_rhs.m_graph_type_name);
|
||||||
|
REQUIRE(graph_resource_reference.m_name == graph_resource_rhs.m_name);
|
||||||
|
|
||||||
|
REQUIRE(graph_resource_reference.m_graph_type_name == "BlendTree");
|
||||||
|
|
||||||
|
const BlendTreeResource* blend_tree_resource_reference =
|
||||||
|
&graph_resource_reference.m_blend_tree_resource;
|
||||||
|
|
||||||
|
const BlendTreeResource* blend_tree_resource_rhs =
|
||||||
|
&graph_resource_rhs.m_blend_tree_resource;
|
||||||
|
|
||||||
|
CheckBlendTreeResourcesEqual(
|
||||||
|
blend_tree_resource_reference,
|
||||||
|
blend_tree_resource_rhs);
|
||||||
|
}
|
||||||
|
|
||||||
TEST_CASE("InputAttributeConversion", "[AnimGraphResource]") {
|
TEST_CASE("InputAttributeConversion", "[AnimGraphResource]") {
|
||||||
int node_id = 3321;
|
int node_id = 3321;
|
||||||
int input_index = 221;
|
int input_index = 221;
|
||||||
@@ -447,44 +480,7 @@ TEST_CASE_METHOD(
|
|||||||
AnimGraphResource graph_resource_loaded;
|
AnimGraphResource graph_resource_loaded;
|
||||||
graph_resource_loaded.LoadFromFile("TestGraphAnimSamplerBlendTree.json");
|
graph_resource_loaded.LoadFromFile("TestGraphAnimSamplerBlendTree.json");
|
||||||
|
|
||||||
REQUIRE(
|
CheckAnimGraphResourceEqual(graph_resource, graph_resource_loaded);
|
||||||
graph_resource.m_graph_type_name
|
|
||||||
== graph_resource_loaded.m_graph_type_name);
|
|
||||||
REQUIRE(graph_resource.m_name == graph_resource_loaded.m_name);
|
|
||||||
|
|
||||||
BlendTreeResource* blend_tree_resource_loaded =
|
|
||||||
&graph_resource_loaded.m_blend_tree_resource;
|
|
||||||
|
|
||||||
REQUIRE(
|
|
||||||
blend_tree_resource->m_nodes.size()
|
|
||||||
== blend_tree_resource_loaded->m_nodes.size());
|
|
||||||
for (size_t i = 0; i < blend_tree_resource->m_nodes.size(); i++) {
|
|
||||||
const AnimNodeResource* node = blend_tree_resource->m_nodes[i];
|
|
||||||
const AnimNodeResource* node_loaded =
|
|
||||||
blend_tree_resource_loaded->m_nodes[i];
|
|
||||||
|
|
||||||
REQUIRE(node->m_name == node_loaded->m_name);
|
|
||||||
REQUIRE(node->m_node_type_name == node_loaded->m_node_type_name);
|
|
||||||
}
|
|
||||||
|
|
||||||
REQUIRE(
|
|
||||||
blend_tree_resource->m_connections.size()
|
|
||||||
== blend_tree_resource_loaded->m_connections.size());
|
|
||||||
for (size_t i = 0; i < blend_tree_resource->m_connections.size(); i++) {
|
|
||||||
const BlendTreeConnectionResource& connection =
|
|
||||||
blend_tree_resource->m_connections[i];
|
|
||||||
const BlendTreeConnectionResource& connection_loaded =
|
|
||||||
blend_tree_resource_loaded->m_connections[i];
|
|
||||||
|
|
||||||
REQUIRE(
|
|
||||||
connection.source_node_index == connection_loaded.source_node_index);
|
|
||||||
REQUIRE(
|
|
||||||
connection.source_socket_name == connection_loaded.source_socket_name);
|
|
||||||
REQUIRE(
|
|
||||||
connection.target_node_index == connection_loaded.target_node_index);
|
|
||||||
REQUIRE(
|
|
||||||
connection.target_socket_name == connection_loaded.target_socket_name);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_CASE_METHOD(
|
TEST_CASE_METHOD(
|
||||||
@@ -558,20 +554,20 @@ TEST_CASE("AnimSamplerSpeedScaleGraph", "[AnimGraphResource]") {
|
|||||||
blend_tree_resource.InitGraphConnectors();
|
blend_tree_resource.InitGraphConnectors();
|
||||||
|
|
||||||
// Prepare graph inputs and outputs
|
// Prepare graph inputs and outputs
|
||||||
blend_tree_resource.m_nodes.push_back(AnimNodeResourceFactory("AnimSampler"));
|
size_t walk_node_index =
|
||||||
size_t walk_node_index = blend_tree_resource.m_nodes.size() - 1;
|
blend_tree_resource.AddNode(AnimNodeResourceFactory("AnimSampler"));
|
||||||
|
|
||||||
blend_tree_resource.m_nodes.push_back(AnimNodeResourceFactory("SpeedScale"));
|
size_t speed_scale_node_index =
|
||||||
size_t speed_scale_node_index = blend_tree_resource.m_nodes.size() - 1;
|
blend_tree_resource.AddNode(AnimNodeResourceFactory("SpeedScale"));
|
||||||
|
|
||||||
AnimNodeResource* walk_node = blend_tree_resource.m_nodes[walk_node_index];
|
AnimNodeResource* walk_node = blend_tree_resource.GetNode(walk_node_index);
|
||||||
walk_node->m_name = "WalkAnim";
|
walk_node->m_name = "WalkAnim";
|
||||||
walk_node->m_socket_accessor->SetPropertyValue(
|
walk_node->m_socket_accessor->SetPropertyValue(
|
||||||
"Filename",
|
"Filename",
|
||||||
std::string("media/Walking-loop.ozz"));
|
std::string("media/Walking-loop.ozz"));
|
||||||
|
|
||||||
AnimNodeResource* speed_scale_node =
|
AnimNodeResource* speed_scale_node =
|
||||||
blend_tree_resource.m_nodes[speed_scale_node_index];
|
blend_tree_resource.GetNode(speed_scale_node_index);
|
||||||
speed_scale_node->m_name = "SpeedScale";
|
speed_scale_node->m_name = "SpeedScale";
|
||||||
float speed_scale_value = 1.35f;
|
float speed_scale_value = 1.35f;
|
||||||
speed_scale_node->m_socket_accessor->SetInputValue(
|
speed_scale_node->m_socket_accessor->SetInputValue(
|
||||||
@@ -602,7 +598,7 @@ TEST_CASE("AnimSamplerSpeedScaleGraph", "[AnimGraphResource]") {
|
|||||||
graph_resource_loaded.m_blend_tree_resource;
|
graph_resource_loaded.m_blend_tree_resource;
|
||||||
|
|
||||||
Socket* speed_scale_resource_loaded_input =
|
Socket* speed_scale_resource_loaded_input =
|
||||||
blend_tree_resource_loaded.m_nodes[speed_scale_node_index]
|
blend_tree_resource_loaded.GetNode(speed_scale_node_index)
|
||||||
->m_socket_accessor->GetInputSocket("SpeedScale");
|
->m_socket_accessor->GetInputSocket("SpeedScale");
|
||||||
REQUIRE(speed_scale_resource_loaded_input != nullptr);
|
REQUIRE(speed_scale_resource_loaded_input != nullptr);
|
||||||
|
|
||||||
@@ -617,6 +613,97 @@ TEST_CASE("AnimSamplerSpeedScaleGraph", "[AnimGraphResource]") {
|
|||||||
*dynamic_cast<SpeedScaleNode*>(blend_tree.m_nodes[speed_scale_node_index])
|
*dynamic_cast<SpeedScaleNode*>(blend_tree.m_nodes[speed_scale_node_index])
|
||||||
->i_speed_scale,
|
->i_speed_scale,
|
||||||
Catch::Matchers::WithinAbs(speed_scale_value, 0.1));
|
Catch::Matchers::WithinAbs(speed_scale_value, 0.1));
|
||||||
|
|
||||||
|
WHEN("Checking node eval order and node subtrees") {
|
||||||
|
const std::vector<size_t>& eval_order =
|
||||||
|
graph_resource_loaded.m_blend_tree_resource.GetNodeEvalOrder();
|
||||||
|
|
||||||
|
THEN("Walk node gets evaluated before speed scale node") {
|
||||||
|
CHECK(eval_order.size() == 2);
|
||||||
|
CHECK(eval_order[0] == walk_node_index);
|
||||||
|
CHECK(eval_order[1] == speed_scale_node_index);
|
||||||
|
}
|
||||||
|
|
||||||
|
THEN("Subtree of the speed scale node contains only the walk node") {
|
||||||
|
CHECK(
|
||||||
|
graph_resource_loaded.m_blend_tree_resource
|
||||||
|
.m_node_inputs_subtree[speed_scale_node_index]
|
||||||
|
.size()
|
||||||
|
== 1);
|
||||||
|
CHECK(
|
||||||
|
graph_resource_loaded.m_blend_tree_resource
|
||||||
|
.m_node_inputs_subtree[speed_scale_node_index][0]
|
||||||
|
== walk_node_index);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// Checks that connections additions and removals are properly validated.
|
||||||
|
//
|
||||||
|
TEST_CASE_METHOD(
|
||||||
|
Blend2GraphResource,
|
||||||
|
"Connectivity Tests",
|
||||||
|
"[AnimGraphResource][Blend2GraphResource]") {
|
||||||
|
INFO("Removing Blend2 -> Output Connection")
|
||||||
|
CHECK(
|
||||||
|
blend_tree_resource->DisconnectSockets(
|
||||||
|
blend_node,
|
||||||
|
"Output",
|
||||||
|
blend_tree_resource->GetGraphOutputNode(),
|
||||||
|
"GraphOutput")
|
||||||
|
== true);
|
||||||
|
CHECK(blend_tree_resource->GetNodeEvalOrder().empty());
|
||||||
|
|
||||||
|
INFO("Adding speed scale node");
|
||||||
|
size_t speed_scale_node_index =
|
||||||
|
blend_tree_resource->AddNode(AnimNodeResourceFactory("SpeedScale"));
|
||||||
|
AnimNodeResource* speed_scale_node_resource =
|
||||||
|
blend_tree_resource->GetNode(speed_scale_node_index);
|
||||||
|
|
||||||
|
INFO("Connecting speed scale node");
|
||||||
|
CHECK(
|
||||||
|
blend_tree_resource->ConnectSockets(
|
||||||
|
speed_scale_node_resource,
|
||||||
|
"Output",
|
||||||
|
blend_tree_resource->GetGraphOutputNode(),
|
||||||
|
"GraphOutput")
|
||||||
|
== true);
|
||||||
|
|
||||||
|
const std::vector<size_t>& tree_eval_order =
|
||||||
|
blend_tree_resource->GetNodeEvalOrder();
|
||||||
|
CHECK(tree_eval_order.size() == 1);
|
||||||
|
CHECK(tree_eval_order[0] == speed_scale_node_index);
|
||||||
|
|
||||||
|
CHECK(
|
||||||
|
blend_tree_resource->ConnectSockets(
|
||||||
|
blend_node,
|
||||||
|
"Output",
|
||||||
|
speed_scale_node_resource,
|
||||||
|
"Input")
|
||||||
|
== true);
|
||||||
|
|
||||||
|
CHECK(tree_eval_order.size() == 4);
|
||||||
|
CHECK(tree_eval_order[3] == speed_scale_node_index);
|
||||||
|
CHECK(tree_eval_order[2] == blend_node_index);
|
||||||
|
CHECK(tree_eval_order[1] == run_node_index);
|
||||||
|
CHECK(tree_eval_order[0] == walk_node_index);
|
||||||
|
|
||||||
|
INFO("Creating loop");
|
||||||
|
CHECK(blend_tree_resource->DisconnectSockets(
|
||||||
|
speed_scale_node_resource,
|
||||||
|
"Output",
|
||||||
|
blend_tree_resource->GetGraphOutputNode(),
|
||||||
|
"GraphOutput"));
|
||||||
|
CHECK(blend_tree_resource
|
||||||
|
->DisconnectSockets(walk_node, "Output", blend_node, "Input0"));
|
||||||
|
CHECK(
|
||||||
|
blend_tree_resource->IsConnectionValid(
|
||||||
|
speed_scale_node_resource,
|
||||||
|
"Output",
|
||||||
|
blend_node,
|
||||||
|
"Input0")
|
||||||
|
== false);
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_CASE_METHOD(
|
TEST_CASE_METHOD(
|
||||||
@@ -628,50 +715,16 @@ TEST_CASE_METHOD(
|
|||||||
AnimGraphResource graph_resource_loaded;
|
AnimGraphResource graph_resource_loaded;
|
||||||
graph_resource_loaded.LoadFromFile("TestGraphBlend2Graph.animgraph.json");
|
graph_resource_loaded.LoadFromFile("TestGraphBlend2Graph.animgraph.json");
|
||||||
|
|
||||||
REQUIRE(
|
CheckAnimGraphResourceEqual(graph_resource, graph_resource_loaded);
|
||||||
graph_resource.m_graph_type_name
|
|
||||||
== graph_resource_loaded.m_graph_type_name);
|
|
||||||
REQUIRE(graph_resource.m_name == graph_resource_loaded.m_name);
|
|
||||||
|
|
||||||
BlendTreeResource* blend_tree_resource_loaded =
|
BlendTreeResource* blend_tree_resource_loaded =
|
||||||
&graph_resource_loaded.m_blend_tree_resource;
|
&graph_resource_loaded.m_blend_tree_resource;
|
||||||
|
|
||||||
REQUIRE(
|
|
||||||
blend_tree_resource->m_nodes.size()
|
|
||||||
== blend_tree_resource_loaded->m_nodes.size());
|
|
||||||
for (size_t i = 0; i < blend_tree_resource->m_nodes.size(); i++) {
|
|
||||||
const AnimNodeResource* node = blend_tree_resource->m_nodes[i];
|
|
||||||
const AnimNodeResource* node_loaded =
|
|
||||||
blend_tree_resource_loaded->m_nodes[i];
|
|
||||||
|
|
||||||
REQUIRE(node->m_name == node_loaded->m_name);
|
|
||||||
REQUIRE(node->m_node_type_name == node_loaded->m_node_type_name);
|
|
||||||
}
|
|
||||||
|
|
||||||
REQUIRE(
|
|
||||||
blend_tree_resource->m_connections.size()
|
|
||||||
== blend_tree_resource_loaded->m_connections.size());
|
|
||||||
for (size_t i = 0; i < blend_tree_resource->m_connections.size(); i++) {
|
|
||||||
const BlendTreeConnectionResource& connection =
|
|
||||||
blend_tree_resource->m_connections[i];
|
|
||||||
const BlendTreeConnectionResource& connection_loaded =
|
|
||||||
blend_tree_resource_loaded->m_connections[i];
|
|
||||||
|
|
||||||
REQUIRE(
|
|
||||||
connection.source_node_index == connection_loaded.source_node_index);
|
|
||||||
REQUIRE(
|
|
||||||
connection.source_socket_name == connection_loaded.source_socket_name);
|
|
||||||
REQUIRE(
|
|
||||||
connection.target_node_index == connection_loaded.target_node_index);
|
|
||||||
REQUIRE(
|
|
||||||
connection.target_socket_name == connection_loaded.target_socket_name);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check that the constant weight of the Blend2 node was properly applied when
|
// Check that the constant weight of the Blend2 node was properly applied when
|
||||||
// loading the resource.
|
// loading the resource.
|
||||||
const NodeDescriptor<Blend2Node>* blend2_node_descriptor_loaded =
|
const NodeDescriptor<Blend2Node>* blend2_node_descriptor_loaded =
|
||||||
dynamic_cast<NodeDescriptor<Blend2Node>*>(
|
dynamic_cast<NodeDescriptor<Blend2Node>*>(
|
||||||
blend_tree_resource_loaded->m_nodes[blend_node_index]
|
blend_tree_resource_loaded->GetNode(blend_node_index)
|
||||||
->m_socket_accessor);
|
->m_socket_accessor);
|
||||||
|
|
||||||
REQUIRE_THAT(
|
REQUIRE_THAT(
|
||||||
@@ -794,9 +847,8 @@ TEST_CASE("ResourceSaveLoadMathGraphInputs", "[AnimGraphResource]") {
|
|||||||
blend_tree_resource.InitGraphConnectors();
|
blend_tree_resource.InitGraphConnectors();
|
||||||
|
|
||||||
// Prepare graph inputs and outputs
|
// Prepare graph inputs and outputs
|
||||||
blend_tree_resource.m_nodes.push_back(
|
size_t float_to_vec3_node_index = blend_tree_resource.AddNode(
|
||||||
AnimNodeResourceFactory("MathFloatToVec3Node"));
|
AnimNodeResourceFactory("MathFloatToVec3Node"));
|
||||||
size_t float_to_vec3_node_index = blend_tree_resource.m_nodes.size() - 1;
|
|
||||||
|
|
||||||
AnimNodeResource* graph_output_node =
|
AnimNodeResource* graph_output_node =
|
||||||
blend_tree_resource.GetGraphOutputNode();
|
blend_tree_resource.GetGraphOutputNode();
|
||||||
@@ -815,7 +867,7 @@ TEST_CASE("ResourceSaveLoadMathGraphInputs", "[AnimGraphResource]") {
|
|||||||
|
|
||||||
// Prepare graph inputs and outputs
|
// Prepare graph inputs and outputs
|
||||||
AnimNodeResource* float_to_vec3_node_resource =
|
AnimNodeResource* float_to_vec3_node_resource =
|
||||||
blend_tree_resource.m_nodes[float_to_vec3_node_index];
|
blend_tree_resource.GetNode(float_to_vec3_node_index);
|
||||||
|
|
||||||
REQUIRE(blend_tree_resource.ConnectSockets(
|
REQUIRE(blend_tree_resource.ConnectSockets(
|
||||||
graph_input_node_resource,
|
graph_input_node_resource,
|
||||||
@@ -856,9 +908,9 @@ TEST_CASE("ResourceSaveLoadMathGraphInputs", "[AnimGraphResource]") {
|
|||||||
graph_resource_loaded.m_blend_tree_resource;
|
graph_resource_loaded.m_blend_tree_resource;
|
||||||
|
|
||||||
const AnimNodeResource* graph_loaded_output_node =
|
const AnimNodeResource* graph_loaded_output_node =
|
||||||
graph_blend_tree_loaded.m_nodes[0];
|
graph_blend_tree_loaded.GetGraphOutputNode();
|
||||||
const AnimNodeResource* graph_loaded_input_node =
|
const AnimNodeResource* graph_loaded_input_node =
|
||||||
graph_blend_tree_loaded.m_nodes[1];
|
graph_blend_tree_loaded.GetGraphInputNode();
|
||||||
|
|
||||||
THEN("Graph inputs and outputs must be in loaded resource as well.") {
|
THEN("Graph inputs and outputs must be in loaded resource as well.") {
|
||||||
REQUIRE(
|
REQUIRE(
|
||||||
@@ -952,11 +1004,10 @@ TEST_CASE("SimpleMathEvaluations", "[AnimGraphResource]") {
|
|||||||
blend_tree_resource.InitGraphConnectors();
|
blend_tree_resource.InitGraphConnectors();
|
||||||
|
|
||||||
// Prepare graph inputs and outputs
|
// Prepare graph inputs and outputs
|
||||||
blend_tree_resource.m_nodes.push_back(AnimNodeResourceFactory("MathAddNode"));
|
size_t math_add0_node_index =
|
||||||
size_t math_add0_node_index = blend_tree_resource.m_nodes.size() - 1;
|
blend_tree_resource.AddNode(AnimNodeResourceFactory("MathAddNode"));
|
||||||
|
size_t math_add1_node_index =
|
||||||
blend_tree_resource.m_nodes.push_back(AnimNodeResourceFactory("MathAddNode"));
|
blend_tree_resource.AddNode(AnimNodeResourceFactory("MathAddNode"));
|
||||||
size_t math_add1_node_index = blend_tree_resource.m_nodes.size() - 1;
|
|
||||||
|
|
||||||
AnimNodeResource* graph_output_node =
|
AnimNodeResource* graph_output_node =
|
||||||
blend_tree_resource.GetGraphOutputNode();
|
blend_tree_resource.GetGraphOutputNode();
|
||||||
@@ -978,9 +1029,9 @@ TEST_CASE("SimpleMathEvaluations", "[AnimGraphResource]") {
|
|||||||
|
|
||||||
// Prepare graph inputs and outputs
|
// Prepare graph inputs and outputs
|
||||||
AnimNodeResource* math_add0_node =
|
AnimNodeResource* math_add0_node =
|
||||||
blend_tree_resource.m_nodes[math_add0_node_index];
|
blend_tree_resource.GetNode(math_add0_node_index);
|
||||||
AnimNodeResource* math_add1_node =
|
AnimNodeResource* math_add1_node =
|
||||||
blend_tree_resource.m_nodes[math_add1_node_index];
|
blend_tree_resource.GetNode(math_add1_node_index);
|
||||||
|
|
||||||
// direct output
|
// direct output
|
||||||
REQUIRE(blend_tree_resource.ConnectSockets(
|
REQUIRE(blend_tree_resource.ConnectSockets(
|
||||||
@@ -1095,112 +1146,28 @@ TEST_CASE_METHOD(
|
|||||||
AnimGraphResource parent_graph_resource_loaded;
|
AnimGraphResource parent_graph_resource_loaded;
|
||||||
parent_graph_resource_loaded.LoadFromFile("TestGraphEmbeddedBlendTree.json");
|
parent_graph_resource_loaded.LoadFromFile("TestGraphEmbeddedBlendTree.json");
|
||||||
|
|
||||||
//
|
|
||||||
// Check the loaded parent graph
|
// Check the loaded parent graph
|
||||||
//
|
CheckAnimGraphResourceEqual(
|
||||||
CHECK(parent_graph_resource.m_name == parent_graph_resource_loaded.m_name);
|
parent_graph_resource,
|
||||||
CHECK(
|
parent_graph_resource_loaded);
|
||||||
parent_graph_resource.m_graph_type_name
|
|
||||||
== parent_graph_resource_loaded.m_graph_type_name);
|
|
||||||
CHECK(
|
|
||||||
parent_graph_resource.m_node_type_name
|
|
||||||
== parent_graph_resource_loaded.m_node_type_name);
|
|
||||||
|
|
||||||
const BlendTreeResource& parent_blend_tree_resource_loaded =
|
const BlendTreeResource& parent_blend_tree_resource_loaded =
|
||||||
parent_graph_resource_loaded.m_blend_tree_resource;
|
parent_graph_resource_loaded.m_blend_tree_resource;
|
||||||
|
|
||||||
CHECK(
|
|
||||||
parent_blend_tree_resource->m_nodes.size()
|
|
||||||
== parent_blend_tree_resource_loaded.m_nodes.size());
|
|
||||||
for (size_t i = 0; i < parent_blend_tree_resource->m_nodes.size(); i++) {
|
|
||||||
const AnimNodeResource* parent_node =
|
|
||||||
parent_blend_tree_resource->m_nodes[i];
|
|
||||||
const AnimNodeResource* parent_node_loaded =
|
|
||||||
parent_blend_tree_resource_loaded.m_nodes[i];
|
|
||||||
|
|
||||||
CHECK(parent_node->m_name == parent_node_loaded->m_name);
|
|
||||||
CHECK(
|
|
||||||
parent_node->m_node_type_name == parent_node_loaded->m_node_type_name);
|
|
||||||
}
|
|
||||||
|
|
||||||
CHECK(
|
|
||||||
parent_blend_tree_resource->m_connections.size()
|
|
||||||
== parent_blend_tree_resource_loaded.m_connections.size());
|
|
||||||
for (size_t i = 0; i < parent_blend_tree_resource->m_connections.size();
|
|
||||||
i++) {
|
|
||||||
const BlendTreeConnectionResource& parent_connection =
|
|
||||||
parent_blend_tree_resource->m_connections[i];
|
|
||||||
const BlendTreeConnectionResource& parent_connection_loaded =
|
|
||||||
parent_blend_tree_resource_loaded.m_connections[i];
|
|
||||||
|
|
||||||
CHECK(
|
|
||||||
parent_connection.source_node_index
|
|
||||||
== parent_connection_loaded.source_node_index);
|
|
||||||
CHECK(
|
|
||||||
parent_connection.source_socket_name
|
|
||||||
== parent_connection_loaded.source_socket_name);
|
|
||||||
CHECK(
|
|
||||||
parent_connection.target_node_index
|
|
||||||
== parent_connection_loaded.target_node_index);
|
|
||||||
CHECK(
|
|
||||||
parent_connection.target_socket_name
|
|
||||||
== parent_connection_loaded.target_socket_name);
|
|
||||||
}
|
|
||||||
|
|
||||||
//
|
|
||||||
// Check the loaded embedded graph
|
// Check the loaded embedded graph
|
||||||
//
|
|
||||||
REQUIRE(
|
REQUIRE(
|
||||||
parent_blend_tree_resource_loaded.m_nodes[3]->m_node_type_name
|
parent_blend_tree_resource_loaded.GetNode(3)->m_node_type_name
|
||||||
== "BlendTree");
|
== "BlendTree");
|
||||||
|
|
||||||
const AnimGraphResource* embedded_graph_loaded =
|
const AnimGraphResource* embedded_graph_loaded =
|
||||||
dynamic_cast<AnimGraphResource*>(
|
dynamic_cast<const AnimGraphResource*>(
|
||||||
parent_blend_tree_resource_loaded.m_nodes[3]);
|
parent_blend_tree_resource_loaded.GetNode(3));
|
||||||
const BlendTreeResource& embedded_blend_tree_resource_loaded =
|
const BlendTreeResource* embedded_blend_tree_resource_loaded =
|
||||||
embedded_graph_loaded->m_blend_tree_resource;
|
&embedded_graph_loaded->m_blend_tree_resource;
|
||||||
|
|
||||||
CHECK(
|
CheckBlendTreeResourcesEqual(
|
||||||
embedded_blend_tree_resource->m_nodes.size()
|
embedded_blend_tree_resource,
|
||||||
== embedded_blend_tree_resource_loaded.m_nodes.size());
|
embedded_blend_tree_resource_loaded);
|
||||||
CHECK(
|
|
||||||
embedded_blend_tree_resource->m_connections.size()
|
|
||||||
== embedded_blend_tree_resource_loaded.m_connections.size());
|
|
||||||
|
|
||||||
for (size_t i = 0; i < embedded_blend_tree_resource->m_nodes.size(); i++) {
|
|
||||||
const AnimNodeResource* parent_node =
|
|
||||||
embedded_blend_tree_resource->m_nodes[i];
|
|
||||||
const AnimNodeResource* parent_node_loaded =
|
|
||||||
embedded_blend_tree_resource_loaded.m_nodes[i];
|
|
||||||
|
|
||||||
CHECK(parent_node->m_name == parent_node_loaded->m_name);
|
|
||||||
CHECK(
|
|
||||||
parent_node->m_node_type_name == parent_node_loaded->m_node_type_name);
|
|
||||||
}
|
|
||||||
|
|
||||||
CHECK(
|
|
||||||
embedded_blend_tree_resource->m_connections.size()
|
|
||||||
== embedded_blend_tree_resource_loaded.m_connections.size());
|
|
||||||
for (size_t i = 0; i < embedded_blend_tree_resource->m_connections.size();
|
|
||||||
i++) {
|
|
||||||
const BlendTreeConnectionResource& embedded_connection =
|
|
||||||
embedded_blend_tree_resource->m_connections[i];
|
|
||||||
const BlendTreeConnectionResource& embedded_connection_loaded =
|
|
||||||
embedded_blend_tree_resource_loaded.m_connections[i];
|
|
||||||
|
|
||||||
CHECK(
|
|
||||||
embedded_connection.source_node_index
|
|
||||||
== embedded_connection_loaded.source_node_index);
|
|
||||||
CHECK(
|
|
||||||
embedded_connection.source_socket_name
|
|
||||||
== embedded_connection_loaded.source_socket_name);
|
|
||||||
CHECK(
|
|
||||||
embedded_connection.target_node_index
|
|
||||||
== embedded_connection_loaded.target_node_index);
|
|
||||||
CHECK(
|
|
||||||
embedded_connection.target_socket_name
|
|
||||||
== embedded_connection_loaded.target_socket_name);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_CASE_METHOD(
|
TEST_CASE_METHOD(
|
||||||
@@ -1333,7 +1300,8 @@ TEST_CASE_METHOD(
|
|||||||
blend_tree.StartUpdateTick();
|
blend_tree.StartUpdateTick();
|
||||||
blend_tree.MarkActiveInputs(std::vector<AnimGraphConnection>());
|
blend_tree.MarkActiveInputs(std::vector<AnimGraphConnection>());
|
||||||
THEN(
|
THEN(
|
||||||
"parent AnimSampler is active and embedded AnimSampler is inactive") {
|
"parent AnimSampler is active and embedded AnimSampler is "
|
||||||
|
"inactive") {
|
||||||
CHECK(walk_node->m_state == AnimNodeEvalState::Activated);
|
CHECK(walk_node->m_state == AnimNodeEvalState::Activated);
|
||||||
CHECK(embedded_run_node->m_state == AnimNodeEvalState::Deactivated);
|
CHECK(embedded_run_node->m_state == AnimNodeEvalState::Deactivated);
|
||||||
}
|
}
|
||||||
@@ -1344,7 +1312,8 @@ TEST_CASE_METHOD(
|
|||||||
blend_tree.StartUpdateTick();
|
blend_tree.StartUpdateTick();
|
||||||
blend_tree.MarkActiveInputs(std::vector<AnimGraphConnection>());
|
blend_tree.MarkActiveInputs(std::vector<AnimGraphConnection>());
|
||||||
THEN(
|
THEN(
|
||||||
"parent AnimSampler is inactive and embedded AnimSampler is active") {
|
"parent AnimSampler is inactive and embedded AnimSampler is "
|
||||||
|
"active") {
|
||||||
CHECK(walk_node->m_state == AnimNodeEvalState::Deactivated);
|
CHECK(walk_node->m_state == AnimNodeEvalState::Deactivated);
|
||||||
CHECK(embedded_run_node->m_state == AnimNodeEvalState::Activated);
|
CHECK(embedded_run_node->m_state == AnimNodeEvalState::Activated);
|
||||||
}
|
}
|
||||||
@@ -1363,3 +1332,23 @@ TEST_CASE_METHOD(
|
|||||||
|
|
||||||
graph_context.freeAnimations();
|
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