Added breadcrumb navigation for embedded graphs.

RefactorUnifiedBlendTreeStateMachineHandling
Martin Felis 2024-05-01 10:58:33 +02:00
parent 44087d7a7c
commit e3baa65c3b
1 changed files with 100 additions and 41 deletions

View File

@ -15,7 +15,10 @@
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;
}; };
@ -259,18 +262,25 @@ void AnimGraphEditorRenderSidebar(
} }
void AnimGraphEditorClear() { void AnimGraphEditorClear() {
sEditorState.currentGraphResource = nullptr; sEditorState.hierarchyStack.clear();
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.hierarchyStack.push_back(sEditorState.rootGraphResource);
sEditorState.hierarchyStack[sEditorState.hierarchyStackIndex] =
sEditorState.hierarchyStack.back();
} }
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");
@ -289,14 +299,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);
// //
@ -305,13 +348,13 @@ 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_id = 0,
n = sEditorState.currentGraphResource->m_blend_tree_resource n = sEditorState.hierarchyStack[sEditorState.hierarchyStackIndex]
.GetNumNodes(); ->m_blend_tree_resource.GetNumNodes();
node_id < n; node_id < n;
node_id++) { node_id++) {
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_id);
if (node_id == 0 || node_id == 1) { if (node_id == 0 || node_id == 1) {
// continue; // continue;
@ -327,8 +370,8 @@ 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(
@ -342,8 +385,8 @@ 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(
@ -364,23 +407,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(
@ -411,8 +456,8 @@ void AnimGraphEditorUpdate(ax::NodeEditor::EditorContext* context) {
&source_node_socket_index); &source_node_socket_index);
const AnimNodeResource* source_node = const AnimNodeResource* 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;
@ -427,21 +472,21 @@ void AnimGraphEditorUpdate(ax::NodeEditor::EditorContext* context) {
&target_node_socket_index); &target_node_socket_index);
const AnimNodeResource* target_node = const AnimNodeResource* 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;
} }
const Socket* source_socket = const Socket* source_socket =
sEditorState.currentGraphResource->m_blend_tree_resource sEditorState.hierarchyStack[sEditorState.hierarchyStackIndex]
.GetNodeOutputSocketByIndex( ->m_blend_tree_resource.GetNodeOutputSocketByIndex(
source_node, source_node,
source_node_socket_index); source_node_socket_index);
const Socket* target_socket = const Socket* target_socket =
sEditorState.currentGraphResource->m_blend_tree_resource sEditorState.hierarchyStack[sEditorState.hierarchyStackIndex]
.GetNodeInputSocketByIndex( ->m_blend_tree_resource.GetNodeInputSocketByIndex(
target_node, target_node,
target_node_socket_index); target_node_socket_index);
@ -458,8 +503,8 @@ void AnimGraphEditorUpdate(ax::NodeEditor::EditorContext* context) {
->m_inputs[target_node_socket_index] ->m_inputs[target_node_socket_index]
.m_name; .m_name;
sEditorState.currentGraphResource->m_blend_tree_resource sEditorState.hierarchyStack[sEditorState.hierarchyStackIndex]
.ConnectSockets( ->m_blend_tree_resource.ConnectSockets(
source_node, source_node,
source_socket_name, source_socket_name,
target_node, target_node,
@ -522,11 +567,12 @@ 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 size_t node_id =
sEditorState.hierarchyStack[sEditorState.hierarchyStackIndex]
->m_blend_tree_resource.GetNumNodes(); ->m_blend_tree_resource.GetNumNodes();
ax::NodeEditor::SetNodePosition(node_id, popup_mouse_position); ax::NodeEditor::SetNodePosition(node_id, popup_mouse_position);
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,9 +595,10 @@ 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())); sEditorState.hierarchyStack[sEditorState.hierarchyStackIndex]
->m_blend_tree_resource.GetNode(selected_node_id.Get()));
} }
} }
@ -561,11 +608,23 @@ void AnimGraphEditorUpdate(ax::NodeEditor::EditorContext* context) {
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( sEditorState.hierarchyStack[sEditorState.hierarchyStackIndex]
double_clicked_node_id.Get()); ->m_blend_tree_resource.GetNode(double_clicked_node_id.Get());
if (clicked_node_resource->m_node_type_name == "BlendTree") { if (clicked_node_resource->m_node_type_name == "BlendTree") {
sEditorState.currentGraphResource = AnimGraphResource* clicked_graph_resource =
dynamic_cast<AnimGraphResource*>(clicked_node_resource); dynamic_cast<AnimGraphResource*>(clicked_node_resource);
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++;
}
ax::NodeEditor::ClearSelection(); ax::NodeEditor::ClearSelection();
} }
} }