Skip to content

Add vertex normal visualization (Show Normals)#173

Merged
fernandotonon merged 3 commits intomasterfrom
feature/display-normals
Mar 5, 2026
Merged

Add vertex normal visualization (Show Normals)#173
fernandotonon merged 3 commits intomasterfrom
feature/display-normals

Conversation

@fernandotonon
Copy link
Owner

@fernandotonon fernandotonon commented Mar 5, 2026

Summary

  • Adds NormalVisualizer class that draws colored lines from each vertex along its normal direction, color-coded by axis (|X|→Red, |Y|→Green, |Z|→Blue)
  • Normals follow skeletal animations in real-time via addSoftwareAnimationRequest(true) and per-frame ManualObject updates
  • New Options → Show Normals checkable menu action, placed right below Show Grid
  • New MCP toggle_normals tool for programmatic control (global toggle)
  • Sentry breadcrumb tracking for Show Normals toggle
  • Overlays auto-update when entities are added/removed via Manager signals
  • ManualObjects use dedicated child scene nodes to avoid crashes from unsafe static_cast<Entity*> in ObjectItemModel and Manager::getEntities()
  • Version bumped to 2.10.0
  • Updated CLAUDE.md documentation

Test plan

  • Build on all platforms (Windows, macOS, Linux)
  • Run app, create a sphere primitive, toggle Options → Show Normals → colored lines appear
  • Toggle off → lines disappear
  • Enable Show Normals, then load/create entities → overlays appear automatically
  • Remove entities while normals visible → no crash
  • Load animated mesh, play animation → normals follow the deformation
  • MCP: call toggle_normals tool → normals toggle on/off
  • Run unit tests (NormalVisualizerIntegrationTest and MCPServerTest.ToggleNormals* suites)

🤖 Generated with Claude Code

Summary by CodeRabbit

  • New Features

    • Toggleable "Show Normals" overlay in View menu to render colored per-vertex normal lines.
    • Remote toggle: server tool to toggle the normals overlay remotely.
  • Tests

    • Integration tests validating material creation, visibility toggling, and skeletal/animated mesh handling.
  • Documentation

    • Added docs for the normal visualizer and a bone-weight overlay.
  • Chores

    • Project version bumped to 2.10.0.

Add NormalVisualizer class that draws colored lines from each vertex along
its normal direction using ManualObject with OT_LINE_LIST. Normals are
color-coded by direction (|X|->Red, |Y|->Green, |Z|->Blue) like standard
normal maps. Toggled via Options -> Show Normals menu action.

Overlays auto-update when entities are added/removed via Manager signals.
ManualObjects are attached to dedicated child scene nodes to avoid crashes
from code that does unsafe static_cast<Entity*> on all attached objects.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@coderabbitai
Copy link

coderabbitai bot commented Mar 5, 2026

📝 Walkthrough

Walkthrough

Adds a NormalVisualizer component that renders per-entity normals as Ogre ManualObject overlays, integrates a "Show Normals" UI toggle and MCPServer tool to toggle it remotely, includes integration tests, updates build files and project version, and documents the feature.

Changes

Cohort / File(s) Summary
Build / Project
CMakeLists.txt, src/CMakeLists.txt, tests/CMakeLists.txt
Project version bumped to 2.10.0 and NormalVisualizer source/header added to main and test builds.
NormalVisualizer Implementation
src/NormalVisualizer.h, src/NormalVisualizer.cpp
New NormalVisualizer class: creates unlit material, builds per-entity ManualObject overlays from mesh vertex/normal data, supports animated/skeletal entities via software animation requests, manages overlay lifecycle, visibility toggling, and per-entity state.
UI Integration
ui_files/mainwindow.ui, src/mainwindow.h, src/mainwindow.cpp
Adds checkable "Show Normals" action, forwards NormalVisualizer declaration, instantiates/destroys NormalVisualizer in MainWindow, and connects action to setVisible().
Remote Tooling / Server
src/MCPServer.h, src/MCPServer.cpp, src/MCPServer_test.cpp
Adds "toggle_normals" tool and handler (toolToggleNormals), exposes it in tools list and tests; bumps SERVER_VERSION to 1.1.0.
Tests
src/NormalVisualizer_test.cpp
New integration tests covering material creation, visibility toggling, overlays for in-memory and skeletal meshes, destructor cleanup, and edge-case behavior.
Docs
CLAUDE.md
Documentation entries added describing NormalVisualizer and a BoneWeightOverlay debug component.

Sequence Diagram

sequenceDiagram
    participant User
    participant UI as MainWindow UI
    participant NV as NormalVisualizer
    participant SceneMgr as Ogre::SceneManager
    participant Entity as Ogre::Entity

    User->>UI: toggle "Show Normals"
    UI->>NV: setVisible(on/off)

    alt Enable
        NV->>SceneMgr: enumerate scene nodes / entities
        SceneMgr->>Entity: list entities
        loop per entity
            Entity-->>NV: mesh vertex & normal data (or animated data)
            NV->>NV: buildOverlayForEntity()
            NV->>SceneMgr: create ManualObject & attach overlay node
        end
        NV->>NV: start update timer for animated entities
    else Disable
        NV->>SceneMgr: destroyAllOverlays()
        NV->>NV: stop update timer
    end
Loading

Estimated Code Review Effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Poem

🐰 I hop through vertices, whiskers keen and bright,
I sketch tiny arrows that point to surface light,
A click and lines appear, each normal finds its place,
I tidy up the scene with a quiet, nimble pace,
Hooray — debug carrots for every face! 🥕

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 14.29% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Title check ✅ Passed The pull request title clearly summarizes the main change: adding a new feature for vertex normal visualization with a menu option to toggle it.
Description check ✅ Passed The pull request description is comprehensive, covering summary, technical details with features, test plan, and implementation notes. It aligns well with the repository's template structure.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
  • 📝 Generate docstrings (stacked PR)
  • 📝 Generate docstrings (commit on current branch)
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch feature/display-normals

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link

@chatgpt-codex-connector chatgpt-codex-connector bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: a7d4d445d7

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

return;

Ogre::String moName = "NormalVisualizer_" + entity->getName();
Ogre::ManualObject* mo = mSceneMgr->createManualObject(moName);

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Clean up stale entity overlays before rebuilding

This call can throw when an entity is recreated with the same name while normals are visible. PrimitiveObject::updatePrimitive destroys the old entity and recreates a new one on the same node/name (src/PrimitiveObject.cpp:457-465), but NormalVisualizer never removes the old overlay entry keyed by the destroyed entity pointer, so createManualObject("NormalVisualizer_<name>") is invoked again for an existing ManualObject and Ogre raises a duplicate-name exception.

Useful? React with 👍 / 👎.

connect(ui->actionShow_Grid, SIGNAL(toggled(bool)),Manager::getSingleton()->getViewportGrid(),SLOT(setVisible(bool)));

// show normals
m_normalVisualizer = new NormalVisualizer(Manager::getSingleton()->getSceneMgr(), this);

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Destroy normal visualizer before Manager shutdown

Constructing NormalVisualizer with MainWindow as parent defers its destruction until after MainWindow::~MainWindow finishes, but that destructor calls Manager::kill() first (src/mainwindow.cpp:209-213). If normals are enabled at exit, NormalVisualizer::~NormalVisualizer runs later and calls destroyAllOverlays() using a freed Ogre::SceneManager*, which can crash during shutdown.

Useful? React with 👍 / 👎.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (1)
src/NormalVisualizer_test.cpp (1)

82-106: Test intent is misleading; this currently verifies only “no crash.”

BuildsOverlayForInMemoryMesh does not assert overlay creation and always passes with SUCCEED(). Please either rename it as a crash-smoke test or add a concrete overlay assertion.

✏️ Minimal rename to match current behavior
-TEST_F(NormalVisualizerIntegrationTest, BuildsOverlayForInMemoryMesh)
+TEST_F(NormalVisualizerIntegrationTest, NoCrashWhenTogglingVisibilityForInMemoryMesh)
@@
-    // Manually call buildOverlayForEntity since the entity wasn't created via Manager
-    // (Manager::entityCreated signal was not emitted for direct SceneManager creation)
-    // Instead, just verify the toggle works without crash
+    // Smoke test only: toggling visibility should not crash for direct SceneManager entities.
     visualizer.setVisible(false);
     SUCCEED();
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/NormalVisualizer_test.cpp` around lines 82 - 106, The test
BuildsOverlayForInMemoryMesh only asserts no crash (ends with SUCCEED()) but its
name implies it verifies overlay creation; either rename the test to reflect a
crash-smoke (e.g., DoesNotCrashWhenTogglingVisibilityForInMemoryMesh) or add a
concrete assertion: call NormalVisualizer::buildOverlayForEntity(entity) (or the
appropriate method on NormalVisualizer that triggers overlay creation), then
assert the overlay was created by querying the visualizer (e.g.,
NormalVisualizer::getOverlayForEntity or checking Ogre::OverlayManager for the
expected overlay name) and validate it is non-null/has expected contents; update
the test name if you choose the rename option (reference: test
BuildsOverlayForInMemoryMesh, class NormalVisualizer, methods setVisible and
buildOverlayForEntity/getOverlayForEntity).
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@src/mainwindow.cpp`:
- Around line 341-343: m_normalVisualizer is a child QObject whose destructor
(NormalVisualizer::~NormalVisualizer -> destroyAllOverlays()) uses the scene
manager, but Manager::kill() is called earlier in MainWindow::~MainWindow(),
invalidating the scene and causing a UAF; fix by explicitly destroying
m_normalVisualizer before calling Manager::kill(): in MainWindow::~MainWindow()
delete (or deleteLater/safely delete synchronously) m_normalVisualizer (or
remove its parent and delete) and set m_normalVisualizer = nullptr prior to
invoking Manager::kill(), ensuring NormalVisualizer::~NormalVisualizer() runs
while the scene manager is still valid.

---

Nitpick comments:
In `@src/NormalVisualizer_test.cpp`:
- Around line 82-106: The test BuildsOverlayForInMemoryMesh only asserts no
crash (ends with SUCCEED()) but its name implies it verifies overlay creation;
either rename the test to reflect a crash-smoke (e.g.,
DoesNotCrashWhenTogglingVisibilityForInMemoryMesh) or add a concrete assertion:
call NormalVisualizer::buildOverlayForEntity(entity) (or the appropriate method
on NormalVisualizer that triggers overlay creation), then assert the overlay was
created by querying the visualizer (e.g., NormalVisualizer::getOverlayForEntity
or checking Ogre::OverlayManager for the expected overlay name) and validate it
is non-null/has expected contents; update the test name if you choose the rename
option (reference: test BuildsOverlayForInMemoryMesh, class NormalVisualizer,
methods setVisible and buildOverlayForEntity/getOverlayForEntity).

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 9666fa34-9595-4d1a-8976-a3981561449e

📥 Commits

Reviewing files that changed from the base of the PR and between 850af21 and a7d4d44.

📒 Files selected for processing (7)
  • src/CMakeLists.txt
  • src/NormalVisualizer.cpp
  • src/NormalVisualizer.h
  • src/NormalVisualizer_test.cpp
  • src/mainwindow.cpp
  • src/mainwindow.h
  • ui_files/mainwindow.ui

- Normals now follow skeletal animations in real-time by requesting
  software-skinned normals via addSoftwareAnimationRequest(true) and
  updating ManualObject geometry each frame via QTimer
- Add toggle_normals MCP tool for enabling/disabling normal visualization
  programmatically (global toggle, no entity parameter required)
- Add Sentry breadcrumb for Show Normals toggle
- Expand test coverage: 9 NormalVisualizer tests + 2 MCP toggle_normals tests
- Update CLAUDE.md with NormalVisualizer documentation
- Bump version to 2.10.0

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@src/MCPServer.cpp`:
- Around line 2432-2448: New public tool "toggle_normals" was added to the MCP
interface so you must update the MCP protocol version; open the MCPServer.h
symbol SERVER_VERSION and increment its semantic version (e.g., 1.0.0 -> 1.1.0)
to reflect the new capability, commit the change, and ensure any code that
references SERVER_VERSION continues to compile against the new string constant.

In `@src/NormalVisualizer.cpp`:
- Line 68: The timer is started with mUpdateTimer.start(0) which can spin the
CPU; change the call so the update loop is capped (e.g., mUpdateTimer.start(16))
to limit frequency to ~60 FPS consistent with MCPServer, ensuring
updateAnimatedOverlays() is not invoked unboundedly and preventing CPU pegging
when it locks GPU vertex buffers and iterates skeletal vertices.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: c25a67c1-326f-45cc-a508-86553fa4a1c1

📥 Commits

Reviewing files that changed from the base of the PR and between a7d4d44 and 00db440.

📒 Files selected for processing (8)
  • CLAUDE.md
  • CMakeLists.txt
  • src/MCPServer.cpp
  • src/MCPServer.h
  • src/MCPServer_test.cpp
  • src/NormalVisualizer.cpp
  • src/NormalVisualizer.h
  • src/NormalVisualizer_test.cpp
✅ Files skipped from review due to trivial changes (1)
  • CLAUDE.md

- Add NormalVisualizer to tests/CMakeLists.txt (fixes linker errors)
- Destroy NormalVisualizer before Manager::kill() to prevent UAF
- Clean up stale overlays when entities are recreated with same name
- Cap animation timer at ~60 FPS (start(16) instead of start(0))
- Bump MCP SERVER_VERSION to 1.1.0 for toggle_normals addition

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@src/NormalVisualizer.cpp`:
- Around line 375-389: In destroyAllOverlays(), guard entries in
mSoftwareAnimRequested before calling removeSoftwareAnimationRequest: iterate
the container and for each Ogre::Entity* (from mSoftwareAnimRequested) check the
pointer is non-null (and optionally verify it's still attached via
entity->getParentSceneNode() or similar) before calling
entity->removeSoftwareAnimationRequest(true); this prevents dereferencing
invalid/deleted Entity pointers that may have been destroyed (e.g., by
PrimitiveObject::updatePrimitive) without onSceneNodeDestroyed() being invoked;
update the loop in destroyAllOverlays() to skip null/invalid entities and then
clear mSoftwareAnimRequested as before.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 5ffa6cc5-657d-47ad-b05a-8033f97366bc

📥 Commits

Reviewing files that changed from the base of the PR and between 00db440 and 39b532b.

📒 Files selected for processing (4)
  • src/MCPServer.h
  • src/NormalVisualizer.cpp
  • src/mainwindow.cpp
  • tests/CMakeLists.txt

Comment on lines +375 to +389
void NormalVisualizer::destroyAllOverlays()
{
for (auto it = mOverlays.begin(); it != mOverlays.end(); ++it)
{
OverlayData& data = it.value();
data.node->detachObject(data.manualObject);
mSceneMgr->destroyManualObject(data.manualObject);
mSceneMgr->destroySceneNode(data.node);
}
mOverlays.clear();

for (Ogre::Entity* entity : mSoftwareAnimRequested)
entity->removeSoftwareAnimationRequest(true);
mSoftwareAnimRequested.clear();
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# First, let's find the NormalVisualizer class and understand mSoftwareAnimRequested usage
rg -n "mSoftwareAnimRequested" --type cpp src/

Repository: fernandotonon/QtMeshEditor

Length of output: 979


🏁 Script executed:

#!/bin/bash
# Look for the onSceneNodeDestroyed callback implementation
rg -n "onSceneNodeDestroyed" --type cpp src/

Repository: fernandotonon/QtMeshEditor

Length of output: 346


🏁 Script executed:

#!/bin/bash
# Check for entity destruction paths
rg -n "destroyEntity|removeAllEntities|destroySceneNode" --type cpp src/ -A 2

Repository: fernandotonon/QtMeshEditor

Length of output: 24735


🏁 Script executed:

#!/bin/bash
# Find the NormalVisualizer class definition
fd -e "cpp" -e "hpp" -e "h" | xargs grep -l "class NormalVisualizer" | head -5

Repository: fernandotonon/QtMeshEditor

Length of output: 139


🏁 Script executed:

cat -n src/NormalVisualizer.cpp | sed -n '75,100p'

Repository: fernandotonon/QtMeshEditor

Length of output: 923


🏁 Script executed:

cat -n src/NormalVisualizer.cpp | sed -n '375,395p'

Repository: fernandotonon/QtMeshEditor

Length of output: 670


🏁 Script executed:

cat -n src/NormalVisualizer.cpp | sed -n '125,145p'

Repository: fernandotonon/QtMeshEditor

Length of output: 927


🏁 Script executed:

cat -n src/NormalVisualizer.cpp | sed -n '360,390p'

Repository: fernandotonon/QtMeshEditor

Length of output: 1166


🏁 Script executed:

rg -n "destroyEntity" --type cpp src/NormalVisualizer.cpp

Repository: fernandotonon/QtMeshEditor

Length of output: 52


🏁 Script executed:

# Check if there's an entity destroyed signal or callback
rg -n "onEntity" --type cpp src/NormalVisualizer.cpp -A 2

Repository: fernandotonon/QtMeshEditor

Length of output: 360


🏁 Script executed:

# Look at the full NormalVisualizer.h to see all callbacks
cat -n src/NormalVisualizer.h | head -50

Repository: fernandotonon/QtMeshEditor

Length of output: 1641


🏁 Script executed:

# Check how entities are being destroyed in tests to understand risk scenarios
rg -B 2 -A 2 "destroyEntity.*destroySceneNode|destroySceneNode.*destroyEntity" --type cpp src/ | head -40

Repository: fernandotonon/QtMeshEditor

Length of output: 52


🏁 Script executed:

# Check if there's an onEntityDestroyed or similar signal
rg -n "entityDestroyed|EntityDestroyed" --type cpp src/ | head -20

Repository: fernandotonon/QtMeshEditor

Length of output: 52


🏁 Script executed:

# Check if Manager has entity destruction signals
cat -n src/Manager.h | grep -A 5 -B 5 "signal\|Q_SIGNAL"

Repository: fernandotonon/QtMeshEditor

Length of output: 496


🏁 Script executed:

# Look at the destructor to understand cleanup
cat -n src/NormalVisualizer.cpp | sed -n '38,75p'

Repository: fernandotonon/QtMeshEditor

Length of output: 1578


🏁 Script executed:

# Let me check the destructor to see how cleanup is done
rg -n "~NormalVisualizer" --type cpp src/ -A 10

Repository: fernandotonon/QtMeshEditor

Length of output: 1156


🏁 Script executed:

# Look for any entity destruction in the codebase that doesn't destroy node
rg -B 3 -A 3 "mSceneMgr->destroyEntity" --type cpp src/ | head -50

Repository: fernandotonon/QtMeshEditor

Length of output: 52


🏁 Script executed:

# Check if there are actual cases where entities are destroyed without node destruction
rg -B 5 "destroyEntity" --type cpp src/ | grep -A 5 "destroyEntity" | head -60

Repository: fernandotonon/QtMeshEditor

Length of output: 2382


🏁 Script executed:

# Check how PrimitiveObject destroys entities - this seems to be the main creation path
cat -n src/PrimitiveObject.cpp | sed -n '450,465p'

Repository: fernandotonon/QtMeshEditor

Length of output: 820


🏁 Script executed:

# Check how Manager::createEntity works and how it registers signal
rg -n "createEntity|entityCreated" --type cpp src/Manager.cpp | head -20

Repository: fernandotonon/QtMeshEditor

Length of output: 270


🏁 Script executed:

# Verify the concern is real by looking at test patterns more carefully
cat -n src/NormalVisualizer_test.cpp | sed -n '130,145p'

Repository: fernandotonon/QtMeshEditor

Length of output: 778


🏁 Script executed:

# Look at where destroyOverlayForEntity is called to understand cleanup paths
rg -n "destroyOverlayForEntity" --type cpp src/

Repository: fernandotonon/QtMeshEditor

Length of output: 318


🏁 Script executed:

# Check if there are any other paths that remove entities from scene without cleanup
rg -n "detachObject|detachAllObjects" --type cpp src/NormalVisualizer.cpp

Repository: fernandotonon/QtMeshEditor

Length of output: 243


🏁 Script executed:

# Let me check if getParentSceneNode() is the right method to check validity
rg -n "getParentSceneNode" --type cpp src/ | head -10

Repository: fernandotonon/QtMeshEditor

Length of output: 1023


🏁 Script executed:

# Check if entity pointers can actually become invalid between addition and use
cat -n src/NormalVisualizer.cpp | sed -n '99,145p'

Repository: fernandotonon/QtMeshEditor

Length of output: 2098


Guard entity pointers in destroyAllOverlays() before calling removeSoftwareAnimationRequest().

Entities in mSoftwareAnimRequested can become invalid if destroyed directly (e.g., via PrimitiveObject::updatePrimitive()) without triggering onSceneNodeDestroyed(). The code already acknowledges this risk at lines 112–114. Add a validity check before accessing the entity:

Suggested fix
-    for (Ogre::Entity* entity : mSoftwareAnimRequested)
-        entity->removeSoftwareAnimationRequest(true);
+    for (Ogre::Entity* entity : mSoftwareAnimRequested)
+    {
+        if (entity && entity->getParentSceneNode())
+            entity->removeSoftwareAnimationRequest(true);
+    }
     mSoftwareAnimRequested.clear();
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/NormalVisualizer.cpp` around lines 375 - 389, In destroyAllOverlays(),
guard entries in mSoftwareAnimRequested before calling
removeSoftwareAnimationRequest: iterate the container and for each Ogre::Entity*
(from mSoftwareAnimRequested) check the pointer is non-null (and optionally
verify it's still attached via entity->getParentSceneNode() or similar) before
calling entity->removeSoftwareAnimationRequest(true); this prevents
dereferencing invalid/deleted Entity pointers that may have been destroyed
(e.g., by PrimitiveObject::updatePrimitive) without onSceneNodeDestroyed() being
invoked; update the loop in destroyAllOverlays() to skip null/invalid entities
and then clear mSoftwareAnimRequested as before.

@sonarqubecloud
Copy link

sonarqubecloud bot commented Mar 5, 2026

@fernandotonon fernandotonon merged commit a9fda71 into master Mar 5, 2026
15 checks passed
@fernandotonon fernandotonon deleted the feature/display-normals branch March 5, 2026 16:56
@fernandotonon fernandotonon linked an issue Mar 8, 2026 that may be closed by this pull request
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Display normal map

1 participant