Skip to content

Fix flipFaceCull corrupting shared RenderState for backward-normal geometry#2915

Open
DieterDePaepe wants to merge 1 commit into
jMonkeyEngine:masterfrom
DieterDePaepe:fix-facecull-flip-shared-renderstate
Open

Fix flipFaceCull corrupting shared RenderState for backward-normal geometry#2915
DieterDePaepe wants to merge 1 commit into
jMonkeyEngine:masterfrom
DieterDePaepe:fix-facecull-flip-shared-renderstate

Conversation

@DieterDePaepe

Copy link
Copy Markdown

Material.updateRenderState() flips the face cull mode of geometry whose world scale reverses the winding order (an odd number of negative scale components), so mirrored geometry culls the correct face. It performed the flip on the RenderState returned by RenderState.copyMergedTo(), which begins "if (additionalState == null) return this;". A Material's additionalState is null until getAdditionalRenderState() is first called, so for any material that never set an additional render state, copyMergedTo() returned the shared receiver -- the technique's RenderState, or the RenderState.DEFAULT singleton -- rather than a per-material copy. flipFaceCull() then mutated that shared object in place.

Because materials sharing a technique (or falling back to RenderState.DEFAULT) share that RenderState, rendering a single mirrored geometry permanently flipped the cull mode for every other geometry using it: their front faces were culled, so they vanished or rendered inside-out, and the corruption persisted across frames until another mirrored draw flipped it back.

Only flip a RenderState the material owns: if copyMergedTo() handed back a shared alias, copy it into the material's private mergedRenderState first.

(Created with help from AI)

…ometry

Material.updateRenderState() flips the face cull mode of geometry whose
world scale reverses the winding order (an odd number of negative scale
components), so mirrored geometry culls the correct face. It performed the
flip on the RenderState returned by RenderState.copyMergedTo(), which begins
"if (additionalState == null) return this;". A Material's additionalState is
null until getAdditionalRenderState() is first called, so for any material
that never set an additional render state, copyMergedTo() returned the shared
receiver -- the technique's RenderState, or the RenderState.DEFAULT singleton
-- rather than a per-material copy. flipFaceCull() then mutated that shared
object in place.

Because materials sharing a technique (or falling back to RenderState.DEFAULT)
share that RenderState, rendering a single mirrored geometry permanently
flipped the cull mode for every other geometry using it: their front faces
were culled, so they vanished or rendered inside-out, and the corruption
persisted across frames until another mirrored draw flipped it back.

Only flip a RenderState the material owns: if copyMergedTo() handed back a
shared alias, copy it into the material's private mergedRenderState first.
@gemini-code-assist

Copy link
Copy Markdown
Contributor

Caution

The consumer version of Gemini Code Assist on GitHub has been sunset. All code review activity has officially ceased.

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.

1 participant