Compare commits
8
Commits
44087d7a7c
..
main
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
99d5a5eb0f | ||
|
|
698abbce4b | ||
|
|
b9789bd1e1 | ||
|
|
fd032c273b | ||
|
|
da431a3879 | ||
|
|
84fc49af30 | ||
|
|
7c7a765455 | ||
|
|
e3baa65c3b |
@@ -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};
|
||||||
|
|||||||
@@ -15,8 +15,12 @@
|
|||||||
|
|
||||||
struct EditorState {
|
struct EditorState {
|
||||||
AnimGraphResource* rootGraphResource = nullptr;
|
AnimGraphResource* rootGraphResource = nullptr;
|
||||||
AnimGraphResource* currentGraphResource = nullptr;
|
|
||||||
|
std::vector<AnimGraphResource*> hierarchyStack;
|
||||||
|
size_t hierarchyStackIndex = 0;
|
||||||
|
|
||||||
bool isGraphLoadedThisFrame = false;
|
bool isGraphLoadedThisFrame = false;
|
||||||
|
ImVec2 mousePopupStart = {};
|
||||||
};
|
};
|
||||||
|
|
||||||
static EditorState sEditorState;
|
static EditorState sEditorState;
|
||||||
@@ -42,16 +46,23 @@ ImNodesPinShape sGetSocketShapeFromSocketType(const SocketType& socket_type) {
|
|||||||
return ImNodesPinShape_Quad;
|
return ImNodesPinShape_Quad;
|
||||||
}
|
}
|
||||||
|
|
||||||
void NodeSocketEditor(Socket& socket) {
|
bool NodeSocketEditor(Socket& socket) {
|
||||||
|
bool modified = false;
|
||||||
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 RemoveBlendTreeConnectionsForSocket(
|
void RemoveBlendTreeConnectionsForSocket(
|
||||||
@@ -156,7 +167,11 @@ void SkinnedMeshWidget(SkinnedMesh* skinned_mesh) {
|
|||||||
void AnimGraphEditorRenderSidebar(
|
void AnimGraphEditorRenderSidebar(
|
||||||
BlendTreeResource& blend_tree_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));
|
||||||
@@ -219,7 +234,12 @@ void AnimGraphEditorRenderSidebar(
|
|||||||
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")) {
|
||||||
RemoveBlendTreeConnectionsForSocket(
|
RemoveBlendTreeConnectionsForSocket(
|
||||||
blend_tree_resource,
|
blend_tree_resource,
|
||||||
@@ -231,6 +251,13 @@ void AnimGraphEditorRenderSidebar(
|
|||||||
}
|
}
|
||||||
ImGui::PopID();
|
ImGui::PopID();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (ImGui::Button("+")) {
|
||||||
|
AnimGraphResource* current_graph_resource =
|
||||||
|
sEditorState.hierarchyStack[sEditorState.hierarchyStackIndex];
|
||||||
|
current_graph_resource->RegisterBlendTreeOutputSocket<float>(
|
||||||
|
"GraphFloatOutput");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (node_resource == blend_tree_resource.GetGraphInputNode()) {
|
if (node_resource == blend_tree_resource.GetGraphInputNode()) {
|
||||||
@@ -243,7 +270,11 @@ void AnimGraphEditorRenderSidebar(
|
|||||||
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")) {
|
||||||
RemoveBlendTreeConnectionsForSocket(
|
RemoveBlendTreeConnectionsForSocket(
|
||||||
blend_tree_resource,
|
blend_tree_resource,
|
||||||
@@ -255,27 +286,51 @@ void AnimGraphEditorRenderSidebar(
|
|||||||
}
|
}
|
||||||
ImGui::PopID();
|
ImGui::PopID();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (ImGui::Button("+")) {
|
||||||
|
AnimGraphResource* current_graph_resource =
|
||||||
|
sEditorState.hierarchyStack[sEditorState.hierarchyStackIndex];
|
||||||
|
current_graph_resource->RegisterBlendTreeInputSocket<float>(
|
||||||
|
"GraphFloatInput");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void AnimGraphEditorClear() {
|
void AnimGraphEditorClear() {
|
||||||
sEditorState.currentGraphResource = nullptr;
|
if (ax::NodeEditor::GetCurrentEditor() != nullptr) {
|
||||||
|
ax::NodeEditor::ClearSelection();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (sEditorState.rootGraphResource) {
|
||||||
|
delete sEditorState.rootGraphResource->m_socket_accessor;
|
||||||
|
}
|
||||||
delete sEditorState.rootGraphResource;
|
delete sEditorState.rootGraphResource;
|
||||||
|
|
||||||
sEditorState.rootGraphResource = new AnimGraphResource();
|
sEditorState.rootGraphResource = new AnimGraphResource();
|
||||||
|
sEditorState.rootGraphResource->m_name = "Root";
|
||||||
sEditorState.rootGraphResource->m_graph_type_name = "BlendTree";
|
sEditorState.rootGraphResource->m_graph_type_name = "BlendTree";
|
||||||
sEditorState.rootGraphResource->m_blend_tree_resource.InitGraphConnectors();
|
sEditorState.rootGraphResource->m_blend_tree_resource.InitGraphConnectors();
|
||||||
sEditorState.currentGraphResource = sEditorState.rootGraphResource;
|
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) {
|
||||||
ax::NodeEditor::SetCurrentEditor(context);
|
ax::NodeEditor::SetCurrentEditor(context);
|
||||||
|
|
||||||
|
//
|
||||||
|
// Menu bar
|
||||||
|
//
|
||||||
ImGui::BeginMenuBar();
|
ImGui::BeginMenuBar();
|
||||||
if (ImGui::Button("Save")) {
|
if (ImGui::Button("Save")) {
|
||||||
sEditorState.rootGraphResource->SaveToFile("editor_graph.json");
|
sEditorState.rootGraphResource->SaveToFile("editor_graph.json");
|
||||||
}
|
}
|
||||||
if (ImGui::Button("Load")) {
|
if (ImGui::Button("Load")) {
|
||||||
|
AnimGraphEditorClear();
|
||||||
sEditorState.rootGraphResource->LoadFromFile("editor_graph.json");
|
sEditorState.rootGraphResource->LoadFromFile("editor_graph.json");
|
||||||
sEditorState.isGraphLoadedThisFrame = true;
|
sEditorState.isGraphLoadedThisFrame = true;
|
||||||
}
|
}
|
||||||
@@ -289,14 +344,47 @@ void AnimGraphEditorUpdate(ax::NodeEditor::EditorContext* context) {
|
|||||||
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,
|
||||||
sEditorState.currentGraphResource->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))) {
|
||||||
sEditorState.currentGraphResource->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);
|
||||||
|
|
||||||
//
|
//
|
||||||
@@ -304,18 +392,15 @@ void AnimGraphEditorUpdate(ax::NodeEditor::EditorContext* context) {
|
|||||||
//
|
//
|
||||||
ax::NodeEditor::Begin("Graph Editor");
|
ax::NodeEditor::Begin("Graph Editor");
|
||||||
|
|
||||||
for (size_t node_id = 0,
|
for (size_t node_index = 0,
|
||||||
n = sEditorState.currentGraphResource->m_blend_tree_resource
|
n = sEditorState.hierarchyStack[sEditorState.hierarchyStackIndex]
|
||||||
.GetNumNodes();
|
->m_blend_tree_resource.GetNumNodes();
|
||||||
node_id < n;
|
node_index < n;
|
||||||
node_id++) {
|
node_index++) {
|
||||||
AnimNodeResource* node_resource =
|
AnimNodeResource* node_resource =
|
||||||
sEditorState.currentGraphResource->m_blend_tree_resource.GetNode(
|
sEditorState.hierarchyStack[sEditorState.hierarchyStackIndex]
|
||||||
node_id);
|
->m_blend_tree_resource.GetNode(node_index);
|
||||||
|
ax::NodeEditor::NodeId node_id(node_resource);
|
||||||
if (node_id == 0 || node_id == 1) {
|
|
||||||
// continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (sEditorState.isGraphLoadedThisFrame) {
|
if (sEditorState.isGraphLoadedThisFrame) {
|
||||||
ax::NodeEditor::SetNodePosition(
|
ax::NodeEditor::SetNodePosition(
|
||||||
@@ -327,13 +412,13 @@ void AnimGraphEditorUpdate(ax::NodeEditor::EditorContext* context) {
|
|||||||
|
|
||||||
// Inputs
|
// Inputs
|
||||||
std::vector<Socket> node_inputs =
|
std::vector<Socket> node_inputs =
|
||||||
sEditorState.currentGraphResource->m_blend_tree_resource
|
sEditorState.hierarchyStack[sEditorState.hierarchyStackIndex]
|
||||||
.GetNodeInputSockets(node_resource);
|
->m_blend_tree_resource.GetNodeInputSockets(node_resource);
|
||||||
for (size_t j = 0, ni = node_inputs.size(); j < ni; j++) {
|
for (size_t j = 0, ni = node_inputs.size(); j < ni; j++) {
|
||||||
Socket& socket = node_inputs[j];
|
Socket& socket = node_inputs[j];
|
||||||
ax::NodeEditor::BeginPin(
|
ax::NodeEditor::BeginPin(
|
||||||
NodeIndexAndSocketIndexToInputPinId(
|
NodeIndexAndSocketIndexToInputPinId(
|
||||||
static_cast<int>(node_id),
|
static_cast<int>(node_index),
|
||||||
static_cast<int>(j)),
|
static_cast<int>(j)),
|
||||||
ax::NodeEditor::PinKind::Input);
|
ax::NodeEditor::PinKind::Input);
|
||||||
ImGui::Text("%s", socket.m_name.c_str());
|
ImGui::Text("%s", socket.m_name.c_str());
|
||||||
@@ -342,13 +427,13 @@ void AnimGraphEditorUpdate(ax::NodeEditor::EditorContext* context) {
|
|||||||
|
|
||||||
// Outputs
|
// Outputs
|
||||||
std::vector<Socket> node_outputs =
|
std::vector<Socket> node_outputs =
|
||||||
sEditorState.currentGraphResource->m_blend_tree_resource
|
sEditorState.hierarchyStack[sEditorState.hierarchyStackIndex]
|
||||||
.GetNodeOutputSockets(node_resource);
|
->m_blend_tree_resource.GetNodeOutputSockets(node_resource);
|
||||||
for (size_t j = 0, ni = node_outputs.size(); j < ni; j++) {
|
for (size_t j = 0, ni = node_outputs.size(); j < ni; j++) {
|
||||||
Socket& socket = node_outputs[j];
|
Socket& socket = node_outputs[j];
|
||||||
ax::NodeEditor::BeginPin(
|
ax::NodeEditor::BeginPin(
|
||||||
NodeIndexAndSocketIndexToOutputPinId(
|
NodeIndexAndSocketIndexToOutputPinId(
|
||||||
static_cast<int>(node_id),
|
static_cast<int>(node_index),
|
||||||
static_cast<int>(j)),
|
static_cast<int>(j)),
|
||||||
ax::NodeEditor::PinKind::Output);
|
ax::NodeEditor::PinKind::Output);
|
||||||
ImGui::Text("%s", socket.m_name.c_str());
|
ImGui::Text("%s", socket.m_name.c_str());
|
||||||
@@ -364,23 +449,25 @@ void AnimGraphEditorUpdate(ax::NodeEditor::EditorContext* context) {
|
|||||||
|
|
||||||
int link_id = 0;
|
int link_id = 0;
|
||||||
for (size_t connection_id = 0,
|
for (size_t connection_id = 0,
|
||||||
n = sEditorState.currentGraphResource->m_blend_tree_resource
|
n = sEditorState.hierarchyStack[sEditorState.hierarchyStackIndex]
|
||||||
.GetNumConnections();
|
->m_blend_tree_resource.GetNumConnections();
|
||||||
connection_id < n;
|
connection_id < n;
|
||||||
connection_id++) {
|
connection_id++) {
|
||||||
const BlendTreeConnectionResource* connection_resource =
|
const BlendTreeConnectionResource* connection_resource =
|
||||||
sEditorState.currentGraphResource->m_blend_tree_resource.GetConnection(
|
sEditorState.hierarchyStack[sEditorState.hierarchyStackIndex]
|
||||||
connection_id);
|
->m_blend_tree_resource.GetConnection(connection_id);
|
||||||
|
|
||||||
const AnimNodeResource* source_node_resource =
|
const AnimNodeResource* source_node_resource =
|
||||||
sEditorState.currentGraphResource->m_blend_tree_resource.GetNode(
|
sEditorState.hierarchyStack[sEditorState.hierarchyStackIndex]
|
||||||
|
->m_blend_tree_resource.GetNode(
|
||||||
connection_resource->source_node_index);
|
connection_resource->source_node_index);
|
||||||
int source_socket_index =
|
int source_socket_index =
|
||||||
source_node_resource->m_socket_accessor->GetOutputIndex(
|
source_node_resource->m_socket_accessor->GetOutputIndex(
|
||||||
connection_resource->source_socket_name.c_str());
|
connection_resource->source_socket_name.c_str());
|
||||||
|
|
||||||
const AnimNodeResource* target_node_resource =
|
const AnimNodeResource* target_node_resource =
|
||||||
sEditorState.currentGraphResource->m_blend_tree_resource.GetNode(
|
sEditorState.hierarchyStack[sEditorState.hierarchyStackIndex]
|
||||||
|
->m_blend_tree_resource.GetNode(
|
||||||
connection_resource->target_node_index);
|
connection_resource->target_node_index);
|
||||||
int target_socket_index =
|
int target_socket_index =
|
||||||
target_node_resource->m_socket_accessor->GetInputIndex(
|
target_node_resource->m_socket_accessor->GetInputIndex(
|
||||||
@@ -393,79 +480,85 @@ void AnimGraphEditorUpdate(ax::NodeEditor::EditorContext* context) {
|
|||||||
static_cast<int>(connection_resource->target_node_index),
|
static_cast<int>(connection_resource->target_node_index),
|
||||||
target_socket_index);
|
target_socket_index);
|
||||||
|
|
||||||
ax::NodeEditor::Link(link_id++, source_socket_pin_id, target_socket_pin_id);
|
ax::NodeEditor::Link(
|
||||||
|
ax::NodeEditor::LinkId(connection_resource),
|
||||||
|
source_socket_pin_id,
|
||||||
|
target_socket_pin_id);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create Connections
|
// Create Connections
|
||||||
if (ax::NodeEditor::BeginCreate()) {
|
if (ax::NodeEditor::BeginCreate()) {
|
||||||
ax::NodeEditor::PinId input_pin_id, output_pin_id;
|
ax::NodeEditor::PinId input_pin_id, output_pin_id;
|
||||||
if (ax::NodeEditor::QueryNewLink(&input_pin_id, &output_pin_id)) {
|
if (ax::NodeEditor::QueryNewLink(&input_pin_id, &output_pin_id)) {
|
||||||
if (input_pin_id && output_pin_id) {
|
|
||||||
if (ax::NodeEditor::AcceptNewItem()) {
|
|
||||||
int source_node_index;
|
int source_node_index;
|
||||||
int source_node_socket_index;
|
int source_node_socket_index;
|
||||||
|
const AnimNodeResource* source_node = nullptr;
|
||||||
|
const Socket* source_socket = nullptr;
|
||||||
|
|
||||||
|
if (input_pin_id) {
|
||||||
OutputPinIdToNodeIndexAndSocketIndex(
|
OutputPinIdToNodeIndexAndSocketIndex(
|
||||||
input_pin_id.Get(),
|
input_pin_id.Get(),
|
||||||
&source_node_index,
|
&source_node_index,
|
||||||
&source_node_socket_index);
|
&source_node_socket_index);
|
||||||
|
|
||||||
const AnimNodeResource* source_node =
|
source_node =
|
||||||
sEditorState.currentGraphResource->m_blend_tree_resource.GetNode(
|
sEditorState.hierarchyStack[sEditorState.hierarchyStackIndex]
|
||||||
source_node_index);
|
->m_blend_tree_resource.GetNode(source_node_index);
|
||||||
if (source_node->m_socket_accessor->m_outputs.size()
|
if (source_node->m_socket_accessor->m_outputs.size()
|
||||||
< source_node_socket_index) {
|
< source_node_socket_index) {
|
||||||
source_node_socket_index = -1;
|
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_index;
|
||||||
int target_node_socket_index;
|
int target_node_socket_index;
|
||||||
|
const AnimNodeResource* target_node = nullptr;
|
||||||
|
const Socket* target_socket = nullptr;
|
||||||
|
|
||||||
|
if (output_pin_id) {
|
||||||
InputPinIdToNodeIndexAndSocketIndex(
|
InputPinIdToNodeIndexAndSocketIndex(
|
||||||
output_pin_id.Get(),
|
output_pin_id.Get(),
|
||||||
&target_node_index,
|
&target_node_index,
|
||||||
&target_node_socket_index);
|
&target_node_socket_index);
|
||||||
|
|
||||||
const AnimNodeResource* target_node =
|
target_node =
|
||||||
sEditorState.currentGraphResource->m_blend_tree_resource.GetNode(
|
sEditorState.hierarchyStack[sEditorState.hierarchyStackIndex]
|
||||||
target_node_index);
|
->m_blend_tree_resource.GetNode(target_node_index);
|
||||||
if (target_node->m_socket_accessor->m_inputs.size()
|
if (target_node->m_socket_accessor->m_inputs.size()
|
||||||
< target_node_socket_index) {
|
< target_node_socket_index) {
|
||||||
target_node_socket_index = -1;
|
target_node_socket_index = -1;
|
||||||
}
|
} else {
|
||||||
|
target_socket =
|
||||||
const Socket* source_socket =
|
sEditorState.hierarchyStack[sEditorState.hierarchyStackIndex]
|
||||||
sEditorState.currentGraphResource->m_blend_tree_resource
|
->m_blend_tree_resource.GetNodeInputSocketByIndex(
|
||||||
.GetNodeOutputSocketByIndex(
|
|
||||||
source_node,
|
|
||||||
source_node_socket_index);
|
|
||||||
const Socket* target_socket =
|
|
||||||
sEditorState.currentGraphResource->m_blend_tree_resource
|
|
||||||
.GetNodeInputSocketByIndex(
|
|
||||||
target_node,
|
target_node,
|
||||||
target_node_socket_index);
|
target_node_socket_index);
|
||||||
|
|
||||||
if (source_socket == nullptr || target_socket == nullptr) {
|
|
||||||
ax::NodeEditor::RejectNewItem();
|
|
||||||
} else {
|
|
||||||
const std::string& source_socket_name =
|
|
||||||
source_node->m_socket_accessor
|
|
||||||
->m_outputs[source_node_socket_index]
|
|
||||||
.m_name;
|
|
||||||
|
|
||||||
const std::string& target_socket_name =
|
|
||||||
target_node->m_socket_accessor
|
|
||||||
->m_inputs[target_node_socket_index]
|
|
||||||
.m_name;
|
|
||||||
|
|
||||||
sEditorState.currentGraphResource->m_blend_tree_resource
|
|
||||||
.ConnectSockets(
|
|
||||||
source_node,
|
|
||||||
source_socket_name,
|
|
||||||
target_node,
|
|
||||||
target_socket_name);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -475,13 +568,12 @@ void AnimGraphEditorUpdate(ax::NodeEditor::EditorContext* context) {
|
|||||||
{
|
{
|
||||||
const bool open_popup = ImGui::IsMouseReleased(ImGuiMouseButton_Right);
|
const bool open_popup = ImGui::IsMouseReleased(ImGuiMouseButton_Right);
|
||||||
|
|
||||||
ImVec2 popup_mouse_position = ImGui::GetMousePos();
|
if (open_popup && ImGui::IsWindowHovered()) {
|
||||||
|
|
||||||
ax::NodeEditor::Suspend();
|
ax::NodeEditor::Suspend();
|
||||||
if (open_popup) {
|
|
||||||
ImGui::OpenPopup("add node");
|
ImGui::OpenPopup("add node");
|
||||||
}
|
|
||||||
ax::NodeEditor::Resume();
|
ax::NodeEditor::Resume();
|
||||||
|
sEditorState.mousePopupStart = ImGui::GetMousePos();
|
||||||
|
}
|
||||||
|
|
||||||
ax::NodeEditor::Suspend();
|
ax::NodeEditor::Suspend();
|
||||||
ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2(8.f, 8.f));
|
ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2(8.f, 8.f));
|
||||||
@@ -522,11 +614,11 @@ void AnimGraphEditorUpdate(ax::NodeEditor::EditorContext* context) {
|
|||||||
if (!node_type_name.empty()) {
|
if (!node_type_name.empty()) {
|
||||||
AnimNodeResource* node_resource =
|
AnimNodeResource* node_resource =
|
||||||
AnimNodeResourceFactory(node_type_name);
|
AnimNodeResourceFactory(node_type_name);
|
||||||
size_t node_id = sEditorState.currentGraphResource
|
ax::NodeEditor::SetNodePosition(
|
||||||
->m_blend_tree_resource.GetNumNodes();
|
ax::NodeEditor::NodeId(node_resource),
|
||||||
ax::NodeEditor::SetNodePosition(node_id, popup_mouse_position);
|
sEditorState.mousePopupStart);
|
||||||
sEditorState.currentGraphResource->m_blend_tree_resource.AddNode(
|
sEditorState.hierarchyStack[sEditorState.hierarchyStackIndex]
|
||||||
node_resource);
|
->m_blend_tree_resource.AddNode(node_resource);
|
||||||
}
|
}
|
||||||
|
|
||||||
ImGui::EndPopup();
|
ImGui::EndPopup();
|
||||||
@@ -549,29 +641,72 @@ void AnimGraphEditorUpdate(ax::NodeEditor::EditorContext* context) {
|
|||||||
|
|
||||||
if (selected_node_id.Get() != 0) {
|
if (selected_node_id.Get() != 0) {
|
||||||
AnimGraphEditorRenderSidebar(
|
AnimGraphEditorRenderSidebar(
|
||||||
sEditorState.currentGraphResource->m_blend_tree_resource,
|
sEditorState.hierarchyStack[sEditorState.hierarchyStackIndex]
|
||||||
sEditorState.currentGraphResource->m_blend_tree_resource.GetNode(
|
->m_blend_tree_resource,
|
||||||
selected_node_id.Get()));
|
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::NodeId double_clicked_node_id =
|
||||||
ax::NodeEditor::GetDoubleClickedNode();
|
ax::NodeEditor::GetDoubleClickedNode();
|
||||||
if (!double_clicked_node_id.Invalid) {
|
if (!double_clicked_node_id.Invalid) {
|
||||||
AnimNodeResource* clicked_node_resource =
|
AnimNodeResource* clicked_node_resource =
|
||||||
sEditorState.currentGraphResource->m_blend_tree_resource.GetNode(
|
double_clicked_node_id.AsPointer<AnimNodeResource>();
|
||||||
double_clicked_node_id.Get());
|
|
||||||
if (clicked_node_resource->m_node_type_name == "BlendTree") {
|
if (clicked_node_resource != nullptr
|
||||||
sEditorState.currentGraphResource =
|
&& clicked_node_resource->m_node_type_name == "BlendTree") {
|
||||||
|
AnimGraphResource* clicked_graph_resource =
|
||||||
dynamic_cast<AnimGraphResource*>(clicked_node_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::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);
|
ax::NodeEditor::SetCurrentEditor(nullptr);
|
||||||
sEditorState.isGraphLoadedThisFrame = false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void AnimGraphEditorGetRuntimeGraph(AnimGraphBlendTree& blend_tree) {
|
void AnimGraphEditorGetRuntimeGraph(AnimGraphBlendTree& blend_tree) {
|
||||||
|
|||||||
@@ -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;
|
||||||
}
|
}
|
||||||
@@ -256,6 +257,8 @@ 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;
|
||||||
@@ -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++) {
|
||||||
@@ -326,8 +331,7 @@ static bool sAnimGraphResourceBlendTreeFromJson(
|
|||||||
if (json_data["nodes"][0].contains("inputs")) {
|
if (json_data["nodes"][0].contains("inputs")) {
|
||||||
const json& graph_outputs = json_data["nodes"][0]["inputs"];
|
const json& graph_outputs = json_data["nodes"][0]["inputs"];
|
||||||
for (const auto& graph_output : graph_outputs) {
|
for (const auto& graph_output : graph_outputs) {
|
||||||
AnimNodeResource* graph_node = blend_tree_resource.GetNode(0);
|
result_graph_resource->RegisterBlendTreeOutputSocket(
|
||||||
graph_node->m_socket_accessor->m_inputs.push_back(
|
|
||||||
sJsonToSocket(graph_output));
|
sJsonToSocket(graph_output));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -336,8 +340,7 @@ static bool sAnimGraphResourceBlendTreeFromJson(
|
|||||||
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.GetNode(1);
|
result_graph_resource->RegisterBlendTreeInputSocket(
|
||||||
graph_node->m_socket_accessor->m_outputs.push_back(
|
|
||||||
sJsonToSocket(graph_input));
|
sJsonToSocket(graph_input));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -276,6 +276,73 @@ struct AnimGraphResource : AnimNodeResource {
|
|||||||
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:
|
||||||
|
|||||||
@@ -304,21 +304,11 @@ class EmbeddedTreeBlend2GraphResource {
|
|||||||
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_blend2_node_index = embedded_blend_tree_resource->AddNode(
|
embedded_blend2_node_index = embedded_blend_tree_resource->AddNode(
|
||||||
@@ -343,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"));
|
||||||
@@ -358,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"));
|
||||||
@@ -1342,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