Olympe Engine 2.0
2D Game Engine with ECS Architecture
Loading...
Searching...
No Matches
AIEditorNodeRenderer.cpp
Go to the documentation of this file.
1/**
2 * @file AIEditorNodeRenderer.cpp
3 * @brief Implementation of AIEditorNodeRenderer
4 * @author Olympe Engine
5 * @date 2026-02-18
6 */
7
9#include "../../third_party/imgui/imgui.h"
10#include "../../third_party/imnodes/imnodes.h"
11#include <cmath>
12
13namespace Olympe {
14namespace AI {
15
16
17
18// ============================================================================
19// Node Rendering
20// ============================================================================
21
23 const NodeGraph::NodeData& nodeData,
24 bool isSelected,
25 bool isExecuting,
27{
29 const BTNodeTypeInfo* typeInfo = registry.GetNodeTypeInfo(nodeData.type);
30
31 if (typeInfo == nullptr) {
32 return;
33 }
34
35 // Begin node
36 int iNodeId = static_cast<int>(nodeData.id.value);
37
38 // Pulsed amber/yellow outline when the node is currently executing.
39 if (isExecuting) {
40 float t = 0.5f + 0.5f * std::sin(static_cast<float>(ImGui::GetTime()) * 4.0f);
41 float alpha = 0.6f + 0.4f * t;
43 static_cast<int>(180.0f + t * 75.0f),
44 static_cast<int>(140.0f + t * 115.0f),
45 10,
46 static_cast<int>(alpha * 255.0f));
47 ImNodes::PushColorStyle(ImNodesCol_NodeOutline, pulseColor);
48 }
49
50 ImNodes::BeginNode(iNodeId);
51
52 // Title bar with color
53 ImNodes::BeginNodeTitleBar();
54 uint32_t color = typeInfo->color;
55
56 // Highlight if executing
57 if (isExecuting) {
58 ImGui::PushStyleColor(ImGuiCol_Text, ImVec4(1.0f, 1.0f, 0.0f, 1.0f));
59 }
60
61 ImGui::TextColored(
62 ImVec4(
63 ((color >> 16) & 0xFF) / 255.0f,
64 ((color >> 8) & 0xFF) / 255.0f,
65 ((color >> 0) & 0xFF) / 255.0f,
66 ((color >> 24) & 0xFF) / 255.0f
67 ),
68 "%s %s", typeInfo->icon.c_str(), nodeData.name.c_str()
69 );
70
71 if (isExecuting) {
72 ImGui::PopStyleColor();
73 }
74
75 // Phase 2.0 - Breakpoint indicator (red dot) and comment badge
76 if (annotation != nullptr)
77 {
78 if (annotation->hasBreakpoint)
79 {
80 ImGui::SameLine();
81 ImGui::TextColored(ImVec4(1.0f, 0.15f, 0.15f, 1.0f), "[B]");
82 }
83 if (!annotation->comment.empty())
84 {
85 ImGui::SameLine();
86 ImGui::TextColored(ImVec4(1.0f, 1.0f, 0.4f, 1.0f), "[C]");
87 if (ImGui::IsItemHovered())
88 {
89 ImGui::SetTooltip("%s", annotation->comment.c_str());
90 }
91 }
92 }
93
94 ImNodes::EndNodeTitleBar();
95
96 // Use 2-column layout to align input pins (left) with output pins (right) on the same Y
97 ImGui::Columns(2, "ai_node_pins", false);
98 ImGui::SetColumnWidth(0, 80.0f);
99
100 // ---- LEFT COLUMN: Input Pins ----
101 // Input pin (for composites and decorators)
102 if (typeInfo->category == BTNodeCategory::Composite ||
103 typeInfo->category == BTNodeCategory::Decorator) {
104 int inputPinId = GetInputPinId(nodeData.id);
105 ImNodes::BeginInputAttribute(inputPinId);
106 ImGui::Text("In");
107 ImNodes::EndInputAttribute();
108 }
109
110 // Parameters
111 for (auto it = nodeData.parameters.begin(); it != nodeData.parameters.end(); ++it) {
112 ImGui::Text("%s: %s", it->first.c_str(), it->second.c_str());
113 }
114
115 // ---- RIGHT COLUMN: Output Pins ----
116 ImGui::NextColumn();
117
118 // Output pins (for nodes that can have children)
119 if (typeInfo->category == BTNodeCategory::Composite ||
120 typeInfo->category == BTNodeCategory::Decorator) {
121 int outputPinId = GetOutputPinId(nodeData.id);
122 ImNodes::BeginOutputAttribute(outputPinId);
123 ImGui::Text("Out");
124 ImNodes::EndOutputAttribute();
125 }
126
127 ImGui::Columns(1); // End columns
128
129 ImNodes::EndNode();
130
131 // Pop pulsed outline style pushed before BeginNode.
132 if (isExecuting) {
133 ImNodes::PopColorStyle();
134 }
135
136 // Set node position
137 ImNodes::SetNodeGridSpacePos(iNodeId, ImVec2(nodeData.position.x, nodeData.position.y));
138
139 // Tooltip on hover
140 int hoveredNode = -1;
141 if (ImNodes::IsNodeHovered(&hoveredNode) && hoveredNode == iNodeId) {
142 RenderNodeTooltip(nodeData);
143 }
144
145 (void)isSelected; // Unused for now
146}
147
149{
151 const BTNodeTypeInfo* typeInfo = registry.GetNodeTypeInfo(nodeData.type);
152
153 if (typeInfo != nullptr) {
154 ImGui::SetTooltip("%s", typeInfo->description.c_str());
155 }
156}
157
158// ============================================================================
159// Pin ID Helpers
160// ============================================================================
161
163{
164 return static_cast<int>(nodeId.value) * 1000;
165}
166
168{
169 return static_cast<int>(nodeId.value) * 1000 + 1;
170}
171
173{
174 return static_cast<int>(nodeId.value) * 1000 + 10 + childIndex;
175}
176
177} // namespace AI
178} // namespace Olympe
Node renderer for AI Editor with ImNodes.
ComponentTypeID GetComponentTypeID_Static()
Definition ECS_Entity.h:56
static int GetChildPinId(NodeGraph::NodeId nodeId, int childIndex)
Get pin ID for child connection.
static int GetInputPinId(NodeGraph::NodeId nodeId)
Get pin ID for node input.
static void RenderNode(const NodeGraph::NodeData &nodeData, bool isSelected=false, bool isExecuting=false, const NodeGraph::NodeAnnotation *annotation=nullptr)
Render a single node with ImNodes.
static void RenderNodeTooltip(const NodeGraph::NodeData &nodeData)
Render node tooltip.
static int GetOutputPinId(NodeGraph::NodeId nodeId)
Get pin ID for node output.
Singleton registry for all BT node types.
static BTNodeRegistry & Get()
Get singleton instance.
@ Composite
Flow control nodes (Selector, Sequence, Parallel)
@ Decorator
Modifiers (Inverter, Repeater, Cooldown, etc.)
< Provides AssetID and INVALID_ASSET_ID
Metadata for a behavior tree node type.
Holds annotation data for a single node.
std::map< std::string, std::string > parameters