17#include "spdlog/spdlog.h"
36 eng->on(
"annotation:add", [
this](
Annotation* annotation) {
37 registerAnnotation(annotation);
39 eng->on(
"annotation:remove", [
this](
Annotation* annotation) {
40 unregisterAnnotation(annotation);
43 spdlog::info(
"AnnotationManager initialized");
46 void AnnotationManager::registerAnnotation(
Annotation* annotation)
49 auto it = std::find(_annotations.begin(), _annotations.end(), annotation);
50 if (it != _annotations.end())
return;
52 _annotations.push_back(annotation);
53 spdlog::info(
"Registered annotation: label='{}' title='{}'", annotation->
label(), annotation->
title());
56 void AnnotationManager::unregisterAnnotation(Annotation* annotation)
58 auto it = std::find(_annotations.begin(), _annotations.end(), annotation);
59 if (it == _annotations.end())
return;
61 if (_activeAnnotation == annotation) {
62 _activeAnnotation =
nullptr;
64 if (_hoveredAnnotation == annotation) {
65 _hoveredAnnotation =
nullptr;
68 _annotations.erase(it);
71 bool AnnotationManager::worldToScreen(
const Vector3& worldPos,
float& screenX,
float& screenY)
const
73 if (!_camera)
return false;
75 auto* cameraComp = _camera->findComponent<CameraComponent>();
76 if (!cameraComp || !cameraComp->camera())
return false;
79 const Matrix4 viewMatrix = _camera->worldTransform().inverse();
80 const Matrix4& projMatrix = cameraComp->camera()->projectionMatrix();
83 Vector3 viewPos = viewMatrix.transformPoint(worldPos);
87 if (viewPos.getZ() >= 0.0f) {
92 Vector4 clipPos = projMatrix * Vector4(viewPos.getX(), viewPos.getY(), viewPos.getZ(), 1.0f);
94 if (std::abs(clipPos.getW()) < 1e-6f)
return false;
97 float ndcX = clipPos.getX() / clipPos.getW();
98 float ndcY = clipPos.getY() / clipPos.getW();
101 int windowW = 0, windowH = 0;
103 if (eng && eng->sdlWindow()) {
104 SDL_GetWindowSize(eng->sdlWindow(), &windowW, &windowH);
106 if (windowW <= 0 || windowH <= 0)
return false;
111 screenX = (ndcX * 0.5f + 0.5f) *
static_cast<float>(windowW);
112 screenY = (1.0f - (ndcY * 0.5f + 0.5f)) *
static_cast<float>(windowH);
117 void AnnotationManager::findCameraEntity()
125 auto root = eng->
root();
128 std::function<Entity*(Entity*)> search = [&](Entity* e) -> Entity* {
129 if (e->findComponent<CameraComponent>())
return e;
130 for (
auto* child : e->children()) {
131 auto* ent =
dynamic_cast<Entity*
>(child);
133 auto* found = search(ent);
134 if (found)
return found;
138 _camera = search(root.get());
147 if (!_camera)
return;
150 _screenInfos.clear();
151 _screenInfos.reserve(_annotations.size());
153 for (
auto* annotation : _annotations) {
154 if (!annotation->enabled())
continue;
159 const Vector3 worldPos = annotation->entity()->position();
162 _screenInfos.push_back(info);
168 float closestDist = _hotspotSize + 5.0f;
171 for (
const auto& info : _screenInfos) {
172 if (!info.visible)
continue;
174 float dx = mouseX - info.screenX;
175 float dy = mouseY - info.screenY;
176 float dist = std::sqrt(dx * dx + dy * dy);
178 if (dist < closestDist) {
180 closest = info.annotation;
184 _hoveredAnnotation = closest;
189 float closestDist = _hotspotSize + 5.0f;
192 for (
const auto& info : _screenInfos) {
193 if (!info.visible)
continue;
195 float dx = screenX - info.screenX;
196 float dy = screenY - info.screenY;
197 float dist = std::sqrt(dx * dx + dy * dy);
199 if (dist < closestDist) {
201 closestAnnotation = info.annotation;
205 if (closestAnnotation) {
206 if (_activeAnnotation == closestAnnotation) {
208 _activeAnnotation =
nullptr;
209 spdlog::info(
"Annotation hidden: '{}'", closestAnnotation->
title());
211 _activeAnnotation = closestAnnotation;
212 spdlog::info(
"Annotation selected: '{}' -- {}", closestAnnotation->
title(), closestAnnotation->
text());
215 _activeAnnotation =
nullptr;
const std::string & text() const
const std::string & label() const
const std::string & title() const
void handleClick(float screenX, float screenY)
Handle mouse click at screen coordinates — selects/deselects the nearest annotation.
void update(float dt) override
void updateHover(float mouseX, float mouseY)
Update hover state based on mouse position (call each frame with current mouse pos).
void initialize() override
std::shared_ptr< Entity > root()
3D vector for positions, directions, and normals with multi-backend SIMD acceleration.