Skip to content

Fix controls for shadow-only casters - #3087

Open
adrian-andrzjewski wants to merge 2 commits into
jMonkeyEngine:masterfrom
adrian-andrzjewski:codex/issue-1875-animation-shadow-update
Open

adrian-andrzjewski wants to merge 2 commits into
jMonkeyEngine:masterfrom
adrian-andrzjewski:codex/issue-1875-animation-shadow-update

Conversation

@adrian-andrzjewski

@adrian-andrzjewski adrian-andrzjewski commented Nov 14, 2026 •

Copy link
Copy Markdown

Fixes #1875

What changed

  • Run render controls for queued shadow casters and their ancestor chain after the shadow camera is active and before rendering each shadow queue.
  • Deduplicate spatials by identity within each shadow-map pass; controls can run again on the next pass. Keep passing the owning ViewPort unchanged while RenderManager uses the shadow camera.
  • Restore the forced material and technique in a finally block, including when a control throws.

Compatibility

controlRender is render-pass behavior, not a once-per-frame behavior. Shadow-only casters now receive callbacks for each shadow-map pass in which they are queued, so multiple shadow maps can add callbacks within one frame. Controls that assume main-view-only or once-per-frame invocation should account for the additional render-pass calls.

Regression coverage

Tests cover shadow-only casters, shared-ancestor deduplication, callbacks across separate shadow passes, the ViewPort and shadow-camera context, and forced-state restoration after successful and throwing callbacks.

Validation

  • Focused :jme3-core:test --tests com.jme3.shadow.ShadowControlRenderTest: passed.
  • :jme3-core:test: 1,218 tests, 0 failures, 0 errors, 1 skipped.
  • :jme3-core:check: passed.
  • Animated Jaime / SpotLightShadowRenderer reproduction: 89 shadow-control callbacks in 90 frames, 0 main-view callbacks, 0 ViewPort mismatches (RTX 2060 SUPER).

@jaime-jmebot jaime-jmebot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Hi Adrian, thanks for putting this fix together. The change addresses the shadow-only caster path directly: it runs render controls for queued casters and their ancestors with the shadow camera active, while deduplicating shared ancestors within a pass, and restores the render manager’s forced material and technique even if a control throws.

I reviewed the helper and its call site alongside the regression tests. The identity-based set is a good fit for scene-graph spatials, and creating it per invocation ensures controls can run again on the next shadow pass. The tests cover both shared-parent deduplication and repeated callbacks across separate passes, and the material/technique restoration is protected by finally, which is important for leaving subsequent rendering state intact.

Looks good to me. Thanks for including both regression coverage and a runtime reproduction!

@riccardobl

Copy link
Copy Markdown
Member

Is this something you need in your use case?
I suspect this can be a breaking change for some applications.

@adrian-andrzjewski

Copy link
Copy Markdown
Author

@riccardobl Yes. This is needed for animated casters whose render state is prepared by a control, where the caster is outside the main camera frustum but still queued by a shadow map. The Jaime reproduction exercises that case: the mesh stays out of view while its floor shadow updates with the animation.

controlRender runs after the shadow camera is active and before the shadow queue is rendered. It receives the owning ViewPort unchanged while RenderManager uses the shadow camera. Spatials are identity-deduplicated within each shadow-map pass and can run again on the next pass. Multiple shadow maps can therefore add callbacks within one frame. This is render-pass behavior, not a once-per-frame callback; controls with per-frame side effects should account for the extra calls.

The regression coverage includes shadow-only casters, shared ancestors, separate passes, camera/ViewPort context, and restoration after successful and throwing callbacks. The focused test, full core tests, and core checks pass.

htanks

@8Keep

8Keep commented Nov 14, 2026 •

Copy link
Copy Markdown
Contributor

cc: @zzuegg who authored that issue

@zzuegg

zzuegg commented Nov 14, 2026

Copy link
Copy Markdown
Member

I remember the issue.

I was never sure what a good solution is.

-The viewport camera and and selected camera do not match in this case.
-Multiple Shadowpasses still call the control multiple times

My original idea was to create a overloaded controlRender with more information, do the once per frame check there, and call the regular control render there for backwards compability. Obviously also not the best solution since the control would get called multiple times still.

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.

Animation state gets only updated when inside main camera

5 participants