Nodes can now be deleted in the blend tree editor.

This commit is contained in:
Martin Felis
2025-03-02 19:30:26 +01:00
parent acbe3a4ed5
commit a1c4630ee7
5 changed files with 221 additions and 151 deletions
+88 -16
View File
@@ -192,9 +192,6 @@ AnimNodeResource* sAnimGraphNodeFromJson(
result->m_position[0] = json_node["position"][0];
result->m_position[1] = json_node["position"][1];
result->m_virtual_socket_accessor =
VirtualAnimNodeDescriptorFactory(result->m_node_type_name);
for (auto& property : result->m_virtual_socket_accessor->m_properties) {
property = sJsonToSocket(json_node["properties"][property.m_name]);
}
@@ -310,6 +307,9 @@ static bool sAnimGraphResourceBlendTreeFromJson(
result_graph_resource->m_position[0] = json_data["position"][0];
result_graph_resource->m_position[1] = json_data["position"][1];
// Clear all nodes as we overwrite them here anyway.
blend_tree_resource.ClearAllNodes();
// Load nodes
for (size_t i = 0, n = json_data["nodes"].size(); i < n; i++) {
const json& json_node = json_data["nodes"][i];
@@ -367,6 +367,76 @@ static bool sAnimGraphResourceBlendTreeFromJson(
return true;
}
static bool sAnimGraphResourceStateMachineFromJson(
const json& json_data,
AnimGraphResource* result_graph_resource) {
assert(false && !"Not yet implemented!");
return false;
}
void BlendTreeResource::RemoveConnectionsForSocket(
const AnimNodeResource* node_resource,
const Socket& socket) {
const BlendTreeConnectionResource* connection =
FindConnectionForSocket(node_resource, socket.m_name);
while (connection != nullptr) {
DisconnectSockets(
GetNode(connection->source_node_index),
connection->source_socket_name,
GetNode(connection->target_node_index),
connection->target_socket_name);
connection = FindConnectionForSocket(node_resource, socket.m_name);
}
}
void BlendTreeResource::RemoveNodeConnections(AnimNodeResource* node_resource) {
for (const Socket& socket :
node_resource->m_virtual_socket_accessor->m_inputs) {
RemoveConnectionsForSocket(node_resource, socket);
}
for (const Socket& socket :
node_resource->m_virtual_socket_accessor->m_outputs) {
RemoveConnectionsForSocket(node_resource, socket);
}
}
bool BlendTreeResource::RemoveNode(AnimNodeResource* node_resource) {
std::vector<AnimNodeResource*>::iterator node_iterator =
std::find(m_nodes.begin(), m_nodes.end(), node_resource);
if (node_iterator == m_nodes.end()) {
return true;
}
const size_t node_index = node_iterator - m_nodes.begin();
if (m_node_input_connection_indices[node_index].size() > 0) {
std::cerr << "Cannot remove node, node still has input connections!"
<< std::endl;
return false;
}
m_node_input_connection_indices.erase(
m_node_input_connection_indices.begin() + node_index);
m_nodes.erase(node_iterator);
for (BlendTreeConnectionResource& connection : m_connections) {
if (connection.source_node_index > node_index) {
connection.source_node_index--;
}
if (connection.target_node_index > node_index) {
connection.target_node_index--;
}
}
UpdateNodeEvalOrder();
UpdateTreeTopologyInfo();
return true;
}
bool BlendTreeResource::ConnectSockets(
const AnimNodeResource* source_node,
const std::string& source_socket_name,
@@ -760,7 +830,8 @@ AnimGraphResource::AnimGraphResource(AnimGraphType graph_type) {
m_virtual_socket_accessor = VirtualAnimNodeDescriptorFactory("BlendTree");
m_blend_tree_resource.InitGraphConnectors();
RegisterBlendTreeOutputSocket<AnimData>("Output");
RegisterBlendTreeOutputSocket<AnimData>(
AnimGraphResource::DefaultAnimOutput);
} else {
std::cerr
<< "Warning: construction of state machine graphs not yet implemented!"
@@ -768,9 +839,7 @@ AnimGraphResource::AnimGraphResource(AnimGraphType graph_type) {
}
}
bool AnimGraphResource::LoadFromFile(const char* filename) {
Clear();
AnimGraphResource* AnimGraphResource::CreateFromFile(const char* filename) {
std::ifstream input_file;
input_file.open(filename);
std::stringstream buffer;
@@ -781,7 +850,7 @@ bool AnimGraphResource::LoadFromFile(const char* filename) {
std::cerr << "Error parsing json of file '" << filename << "'."
<< std::endl;
return false;
return nullptr;
}
if (json_data["type"] != "AnimNodeResource") {
@@ -789,20 +858,23 @@ bool AnimGraphResource::LoadFromFile(const char* filename) {
<< "Invalid json object. Expected type 'AnimNodeResource' but got '"
<< json_data["type"] << "'." << std::endl;
return false;
return nullptr;
}
AnimGraphResource* result = nullptr;
if (json_data["node_type"] == "BlendTree") {
return sAnimGraphResourceBlendTreeFromJson(json_data, this);
result =
dynamic_cast<AnimGraphResource*>(AnimNodeResourceFactory("BlendTree"));
sAnimGraphResourceBlendTreeFromJson(json_data, result);
} else if (json_data["node_type"] == "StateMachine") {
return LoadStateMachineResourceFromJson(json_data);
sAnimGraphResourceStateMachineFromJson(json_data, result);
} else {
std::cerr << "Invalid node_type. Expected type 'BlendTree' or "
"'StateMachine' but got '"
<< json_data["node_type"] << "'." << std::endl;
}
std::cerr << "Invalid node_type. Expected type 'BlendTree' or "
"'StateMachine' but got '"
<< json_data["node_type"] << "'." << std::endl;
return false;
return result;
}
bool AnimGraphResource::SaveToFile(const char* filename) const {
+30 -18
View File
@@ -43,10 +43,10 @@ struct BlendTreeResource {
std::vector<std::vector<size_t> > m_node_input_connection_indices;
std::vector<std::vector<size_t> > m_node_inputs_subtree;
~BlendTreeResource() { CleanupNodes(); }
~BlendTreeResource() { ClearAllNodes(); }
void Reset() {
CleanupNodes();
ClearAllNodes();
m_connections.clear();
@@ -54,7 +54,7 @@ struct BlendTreeResource {
m_node_inputs_subtree.clear();
}
void CleanupNodes() {
void ClearAllNodes() {
for (AnimNodeResource* node_resource : m_nodes) {
delete node_resource;
}
@@ -62,18 +62,6 @@ struct BlendTreeResource {
m_nodes.clear();
}
void InitGraphConnectors() {
AddNode(AnimNodeResourceFactory("BlendTreeSockets"));
AnimNodeResource* output_node = GetGraphOutputNode();
output_node->m_name = "Outputs";
output_node->m_position[0] = 200;
AddNode(AnimNodeResourceFactory("BlendTreeSockets"));
AnimNodeResource* input_node = GetGraphInputNode();
input_node->m_name = "Inputs";
input_node->m_position[0] = -200;
}
[[nodiscard]] AnimNodeResource* GetGraphOutputNode() const {
return m_nodes[0];
}
@@ -134,6 +122,12 @@ struct BlendTreeResource {
return m_nodes.size() - 1;
}
void RemoveConnectionsForSocket(
const AnimNodeResource* node_resource,
const Socket& socket);
void RemoveNodeConnections(AnimNodeResource* node_resource);
[[maybe_unused]] bool RemoveNode(AnimNodeResource* node_resource);
[[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 {
@@ -271,6 +265,18 @@ struct BlendTreeResource {
}
private:
void InitGraphConnectors() {
AddNode(AnimNodeResourceFactory("BlendTreeSockets"));
AnimNodeResource* output_node = GetGraphOutputNode();
output_node->m_name = "Outputs";
output_node->m_position[0] = 200;
AddNode(AnimNodeResourceFactory("BlendTreeSockets"));
AnimNodeResource* input_node = GetGraphInputNode();
input_node->m_name = "Inputs";
input_node->m_position[0] = -200;
}
void UpdateNodeEvalOrder() {
m_node_eval_order.clear();
UpdateNodeEvalOrderRecursive(0);
@@ -281,6 +287,8 @@ struct BlendTreeResource {
std::vector<AnimNodeResource*> m_nodes;
std::vector<BlendTreeConnectionResource> m_connections;
std::vector<size_t> m_node_eval_order;
friend class AnimGraphResource;
};
struct StateMachineTransitionResources {
@@ -299,6 +307,8 @@ struct AnimGraphResource : AnimNodeResource {
explicit AnimGraphResource(AnimGraphType graph_type);
virtual ~AnimGraphResource() { Clear(); };
static constexpr char DefaultAnimOutput[] = "Output";
std::string m_graph_type_name;
BlendTreeResource m_blend_tree_resource;
@@ -308,8 +318,8 @@ struct AnimGraphResource : AnimNodeResource {
StateMachineResource m_state_machine_resource;
void Clear() { m_blend_tree_resource.Reset(); }
bool SaveToFile(const char* filename) const;
bool LoadFromFile(const char* filename);
[[maybe_unused]] bool SaveToFile(const char* filename) const;
static AnimGraphResource* CreateFromFile(const char* filename);
void CreateBlendTreeInstance(AnimGraphBlendTree& result) const;
@@ -389,7 +399,9 @@ struct AnimGraphResource : AnimNodeResource {
bool LoadStateMachineResourceFromJson(nlohmann::json const& json_data);
};
static inline AnimNodeResource* AnimNodeResourceFactory(
typedef std::unique_ptr<AnimGraphResource> AnimGraphResourcePtr;
inline AnimNodeResource* AnimNodeResourceFactory(
const std::string& node_type_name) {
AnimNodeResource* result;
+16 -23
View File
@@ -132,25 +132,6 @@ bool NodeSocketEditor(Socket& socket) {
return modified;
}
void RemoveBlendTreeConnectionsForSocket(
BlendTreeResource& blend_tree_resource,
AnimNodeResource* node_resource,
Socket& socket) {
const BlendTreeConnectionResource* connection =
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);
connection = blend_tree_resource.FindConnectionForSocket(
node_resource,
socket.m_name);
}
}
void SyncTrackEditor(SyncTrack* sync_track) {
ImGui::SliderFloat("duration", &sync_track->m_duration, 0.001f, 10.f);
@@ -363,10 +344,7 @@ void AnimGraphEditorRenderSidebar(
current_graph_resource->m_virtual_socket_accessor->m_inputs = inputs;
}
if (ImGui::Button("X")) {
RemoveBlendTreeConnectionsForSocket(
blend_tree_resource,
node_resource,
input);
blend_tree_resource.RemoveConnectionsForSocket(node_resource, input);
iter = inputs.erase(iter);
} else {
iter++;
@@ -947,6 +925,21 @@ void AnimGraphEditorUpdate(ax::NodeEditor::EditorContext* context) {
}
}
ax::NodeEditor::NodeId hovered_node = ax::NodeEditor::GetHoveredNode();
if (!hovered_node.Invalid) {
AnimNodeResource* node_resource =
hovered_node.AsPointer<AnimNodeResource>();
if (node_resource && ImGui::IsKeyPressed(ImGuiKey_Delete)) {
AnimGraphResource* current_graph_resource =
sEditorState.hierarchyStack[sEditorState.hierarchyStackIndex];
current_graph_resource->m_blend_tree_resource.RemoveNodeConnections(
node_resource);
current_graph_resource->m_blend_tree_resource.RemoveNode(node_resource);
}
}
ax::NodeEditor::SetCurrentEditor(nullptr);
}