From 607b89a8ce6104cbbf6d47370a016e4828567011 Mon Sep 17 00:00:00 2001 From: Wyatt Gillette Date: Sat, 7 Jun 2025 16:39:09 +0200 Subject: [PATCH 1/6] Update ShadowUtil.java --- .../main/java/com/jme3/shadow/ShadowUtil.java | 561 ++++++++++-------- 1 file changed, 303 insertions(+), 258 deletions(-) diff --git a/jme3-core/src/main/java/com/jme3/shadow/ShadowUtil.java b/jme3-core/src/main/java/com/jme3/shadow/ShadowUtil.java index 388310a920..cfbf884efd 100644 --- a/jme3-core/src/main/java/com/jme3/shadow/ShadowUtil.java +++ b/jme3-core/src/main/java/com/jme3/shadow/ShadowUtil.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2009-2021 jMonkeyEngine + * Copyright (c) 2009-2025 jMonkeyEngine * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -71,37 +71,39 @@ private ShadowUtil() { * Updates a points arrays with the frustum corners of the provided camera. * * @param viewCam the viewing Camera (not null, unaffected) - * @param points storage for the corner coordinates (not null, length≥8, + * @param points storage for the corner coordinates (not null, length ≥8, * modified) */ public static void updateFrustumPoints2(Camera viewCam, Vector3f[] points) { int w = viewCam.getWidth(); int h = viewCam.getHeight(); - points[0].set(viewCam.getWorldCoordinates(new Vector2f(0, 0), 0)); - points[1].set(viewCam.getWorldCoordinates(new Vector2f(0, h), 0)); - points[2].set(viewCam.getWorldCoordinates(new Vector2f(w, h), 0)); - points[3].set(viewCam.getWorldCoordinates(new Vector2f(w, 0), 0)); - - points[4].set(viewCam.getWorldCoordinates(new Vector2f(0, 0), 1)); - points[5].set(viewCam.getWorldCoordinates(new Vector2f(0, h), 1)); - points[6].set(viewCam.getWorldCoordinates(new Vector2f(w, h), 1)); - points[7].set(viewCam.getWorldCoordinates(new Vector2f(w, 0), 1)); + TempVars vars = TempVars.get(); + Vector2f tempVec2D = vars.vect2d; + Vector3f tempStore = vars.vect1; + + points[0].set(viewCam.getWorldCoordinates(tempVec2D.set(0, 0), 0, tempStore)); + points[1].set(viewCam.getWorldCoordinates(tempVec2D.set(0, h), 0, tempStore)); + points[2].set(viewCam.getWorldCoordinates(tempVec2D.set(w, h), 0, tempStore)); + points[3].set(viewCam.getWorldCoordinates(tempVec2D.set(w, 0), 0, tempStore)); + + points[4].set(viewCam.getWorldCoordinates(tempVec2D.set(0, 0), 1, tempStore)); + points[5].set(viewCam.getWorldCoordinates(tempVec2D.set(0, h), 1, tempStore)); + points[6].set(viewCam.getWorldCoordinates(tempVec2D.set(w, h), 1, tempStore)); + points[7].set(viewCam.getWorldCoordinates(tempVec2D.set(w, 0), 1, tempStore)); + vars.release(); } /** - * Updates the points array to contain the frustum corners of the given - * camera. The nearOverride and farOverride variables can be used to - * override the camera's near/far values with own values. - * - * TODO: Reduce creation of new vectors + * Updates the provided array of {@code Vector3f} to contain the frustum corners + * of the given camera, with optional overrides for near/far distances and a scale factor. + * The array must have a length of at least 8. * - * @param viewCam the viewing Camera (not null, unaffected) + * @param viewCam the viewing Camera (not null, unaffected) * @param nearOverride distance to the near plane (in world units) - * @param farOverride distance to the far plane (in world units) - * @param scale scale factor - * @param points storage for the corner coordinates (not null, length≥8, - * modified) + * @param farOverride distance to the far plane (in world units) + * @param scale a factor to scale the frustum points around their center (1.0 for no scaling) + * @param points storage for the corner coordinates (not null, length ≥ 8, modified) */ public static void updateFrustumPoints(Camera viewCam, float nearOverride, @@ -109,9 +111,12 @@ public static void updateFrustumPoints(Camera viewCam, float scale, Vector3f[] points) { - Vector3f pos = viewCam.getLocation(); - Vector3f dir = viewCam.getDirection(); - Vector3f up = viewCam.getUp(); + TempVars vars = TempVars.get(); + + Vector3f camPos = viewCam.getLocation(); + Vector3f camDir = viewCam.getDirection(); + Vector3f camUp = viewCam.getUp(); + Vector3f camRight = vars.vect1.set(camDir).crossLocal(camUp).normalizeLocal(); float depthHeightRatio = viewCam.getFrustumTop() / viewCam.getFrustumNear(); float near = nearOverride; @@ -127,29 +132,30 @@ public static void updateFrustumPoints(Camera viewCam, if (viewCam.isParallelProjection()) { near_height = ftop; - near_width = near_height * ratio; - far_height = ftop; - far_width = far_height * ratio; + near_width = near_height * ratio; + far_height = ftop; + far_width = far_height * ratio; } else { near_height = depthHeightRatio * near; - near_width = near_height * ratio; - far_height = depthHeightRatio * far; - far_width = far_height * ratio; + near_width = near_height * ratio; + far_height = depthHeightRatio * far; + far_width = far_height * ratio; } - Vector3f right = dir.cross(up).normalizeLocal(); + Vector3f tempVec = vars.vect2; // Reusing tempVec for calculations + Vector3f nearCenter = vars.vect3; + Vector3f farCenter = vars.vect4; - Vector3f temp = new Vector3f(); - temp.set(dir).multLocal(far).addLocal(pos); - Vector3f farCenter = temp.clone(); - temp.set(dir).multLocal(near).addLocal(pos); - Vector3f nearCenter = temp.clone(); + // Calculate near and far center points + nearCenter.set(camDir).multLocal(near).addLocal(camPos); + farCenter.set(camDir).multLocal(far).addLocal(camPos); - Vector3f nearUp = temp.set(up).multLocal(near_height).clone(); - Vector3f farUp = temp.set(up).multLocal(far_height).clone(); - Vector3f nearRight = temp.set(right).multLocal(near_width).clone(); - Vector3f farRight = temp.set(right).multLocal(far_width).clone(); + Vector3f nearUp = vars.vect5.set(camUp).multLocal(near_height).clone(); + Vector3f farUp = vars.vect6.set(camUp).multLocal(far_height).clone(); + Vector3f nearRight = vars.vect7.set(camRight).multLocal(near_width).clone(); + Vector3f farRight = vars.vect8.set(camRight).multLocal(far_width).clone(); + // Populate frustum points points[0].set(nearCenter).subtractLocal(nearUp).subtractLocal(nearRight); points[1].set(nearCenter).addLocal(nearUp).subtractLocal(nearRight); points[2].set(nearCenter).addLocal(nearUp).addLocal(nearRight); @@ -162,27 +168,29 @@ public static void updateFrustumPoints(Camera viewCam, if (scale != 1.0f) { // find center of frustum - Vector3f center = new Vector3f(); + Vector3f center = vars.vect9.zero(); for (int i = 0; i < 8; i++) { center.addLocal(points[i]); } center.divideLocal(8f); - Vector3f cDir = new Vector3f(); for (int i = 0; i < 8; i++) { - cDir.set(points[i]).subtractLocal(center); - cDir.multLocal(scale - 1.0f); - points[i].addLocal(cDir); + tempVec.set(points[i]).subtractLocal(center); + tempVec.multLocal(scale - 1.0f); + points[i].addLocal(tempVec); } } + + vars.release(); } /** - * Compute bounds of a geomList + * Computes the union bounding box of all geometries in the given list, + * after transforming their world bounds by the provided {@link Transform}. * - * @param list a list of geometries (not null) - * @param transform a coordinate transform - * @return a new instance + * @param list a list of geometries (not null) + * @param transform a coordinate transform to apply to each geometry's world bound (not null, unaffected) + * @return a new {@link BoundingBox} representing the union of transformed bounds. */ public static BoundingBox computeUnionBound(GeometryList list, Transform transform) { BoundingBox bbox = new BoundingBox(); @@ -200,11 +208,12 @@ public static BoundingBox computeUnionBound(GeometryList list, Transform transfo } /** - * Compute bounds of a geomList + * Computes the union bounding box of all geometries in the given list, + * after transforming their world bounds by the provided {@link Matrix4f}. * * @param list a list of geometries (not null) - * @param mat a coordinate-transform matrix - * @return a new instance + * @param mat a coordinate-transform matrix to apply to each geometry's world bound (not null, unaffected) + * @return a new {@link BoundingBox} representing the union of transformed bounds. */ public static BoundingBox computeUnionBound(GeometryList list, Matrix4f mat) { BoundingBox bbox = new BoundingBox(); @@ -222,10 +231,10 @@ public static BoundingBox computeUnionBound(GeometryList list, Matrix4f mat) { } /** - * Computes the bounds of multiple bounding volumes + * Computes the union bounding box of a list of {@link BoundingVolume}s. * - * @param bv a list of bounding volumes (not null) - * @return a new instance + * @param bv a list of bounding volumes (not null, elements can be null) + * @return a new {@link BoundingBox} representing the union of the provided volumes. */ public static BoundingBox computeUnionBound(List bv) { BoundingBox bbox = new BoundingBox(); @@ -237,64 +246,75 @@ public static BoundingBox computeUnionBound(List bv) { } /** - * Compute bounds from an array of points + * Computes a {@link BoundingBox} that encloses an array of 3D points, + * after transforming them by the provided {@link Transform}. * - * @param pts an array of location vectors (not null, unaffected) - * @param transform a coordinate transform - * @return a new instance + * @param pts an array of location vectors (not null, unaffected) + * @param transform a coordinate transform to apply to each point (not null, unaffected) + * @return a new {@link BoundingBox} enclosing the transformed points. */ public static BoundingBox computeBoundForPoints(Vector3f[] pts, Transform transform) { - Vector3f min = new Vector3f(Vector3f.POSITIVE_INFINITY); - Vector3f max = new Vector3f(Vector3f.NEGATIVE_INFINITY); - Vector3f temp = new Vector3f(); - for (int i = 0; i < pts.length; i++) { - transform.transformVector(pts[i], temp); + TempVars vars = TempVars.get(); + Vector3f min = vars.vect1.set(Vector3f.POSITIVE_INFINITY); + Vector3f max = vars.vect2.set(Vector3f.NEGATIVE_INFINITY); + Vector3f temp = vars.vect3; + for (Vector3f pt : pts) { + transform.transformVector(pt, temp); min.minLocal(temp); max.maxLocal(temp); } - Vector3f center = min.add(max).multLocal(0.5f); - Vector3f extent = max.subtract(min).multLocal(0.5f); - return new BoundingBox(center, extent.x, extent.y, extent.z); + Vector3f center = vars.vect4.set(min).addLocal(max).multLocal(0.5f); + Vector3f extent = vars.vect5.set(max).subtractLocal(min).multLocal(0.5f); + + BoundingBox bbox = new BoundingBox(center, extent.x, extent.y, extent.z); + vars.release(); + return bbox; } /** - * Compute bounds from an array of points + * Computes a {@link BoundingBox} that encloses an array of 3D points, + * after transforming them by the provided {@link Matrix4f}. + *

+ * Note: An offset is added to the extent to help avoid banding artifacts + * when frustums are aligned. * * @param pts an array of location vectors (not null, unaffected) * @param mat a coordinate-transform matrix (not null, unaffected) - * @return a new BoundingBox + * @return a new {@link BoundingBox} enclosing the transformed points. */ public static BoundingBox computeBoundForPoints(Vector3f[] pts, Matrix4f mat) { - Vector3f min = new Vector3f(Vector3f.POSITIVE_INFINITY); - Vector3f max = new Vector3f(Vector3f.NEGATIVE_INFINITY); TempVars vars = TempVars.get(); - Vector3f temp = vars.vect1; - - for (int i = 0; i < pts.length; i++) { - float w = mat.multProj(pts[i], temp); + Vector3f min = vars.vect1.set(Vector3f.POSITIVE_INFINITY); + Vector3f max = vars.vect2.set(Vector3f.NEGATIVE_INFINITY); + Vector3f temp = vars.vect3; + for (Vector3f pt : pts) { + float w = mat.multProj(pt, temp); temp.x /= w; temp.y /= w; - // Why was this commented out? - temp.z /= w; + temp.z /= w; // Z component correction min.minLocal(temp); max.maxLocal(temp); } + Vector3f center = vars.vect4.set(min).addLocal(max).multLocal(0.5f); + Vector3f extent = vars.vect5.set(max).subtractLocal(min).multLocal(0.5f); + + //Nehon 08/18/2010 : Added an offset to the extent, to avoid banding artifacts when the frustums are aligned. + BoundingBox bbox = new BoundingBox(center, extent.x + 2.0f, extent.y + 2.0f, extent.z + 2.5f); vars.release(); - Vector3f center = min.add(max).multLocal(0.5f); - Vector3f extent = max.subtract(min).multLocal(0.5f); - //Nehon 08/18/2010 : Added an offset to the extend, to avoid banding artifacts when the frustums are aligned. - return new BoundingBox(center, extent.x + 2.0f, extent.y + 2.0f, extent.z + 2.5f); + return bbox; } /** - * Updates the shadow camera to properly contain the given points (which - * contain the eye camera frustum corners) + * Updates the projection matrix of the shadow camera to properly contain + * the given points (which typically represent the eye camera frustum corners). + * This method is suitable for simple shadow camera setups where only the + * frustum points determine the shadow camera's projection. * * @param shadowCam the shadow camera (not null, modified) - * @param points an array of location vectors (not null, unaffected) + * @param points an array of location vectors representing the bounds to contain (not null, unaffected) */ public static void updateShadowCamera(Camera shadowCam, Vector3f[] points) { boolean ortho = shadowCam.isParallelProjection(); @@ -316,9 +336,7 @@ public static void updateShadowCamera(Camera shadowCam, Vector3f[] points) { Vector3f splitMin = splitBB.getMin(vars.vect1); Vector3f splitMax = splitBB.getMax(vars.vect2); -// splitMin.z = 0; - - // Create the crop matrix. + // Create the crop matrix based on the contained bounding box. float scaleX, scaleY, scaleZ; float offsetX, offsetY, offsetZ; @@ -335,13 +353,12 @@ public static void updateShadowCamera(Camera shadowCam, Vector3f[] points) { 0f, 0f, scaleZ, offsetZ, 0f, 0f, 0f, 1f); - - Matrix4f result = new Matrix4f(); + Matrix4f result = vars.tempMat42; result.set(cropMatrix); result.multLocal(projMatrix); - vars.release(); shadowCam.setProjectionMatrix(result); + vars.release(); } /** @@ -353,68 +370,92 @@ public static void updateShadowCamera(Camera shadowCam, Vector3f[] points) { */ public static class OccludersExtractor { // global variables set in order not to have recursive process method with too many parameters - Matrix4f viewProjMatrix; - public Integer casterCount; - BoundingBox splitBB, casterBB; - GeometryList splitOccluders; - TempVars vars; - - public OccludersExtractor() {} - - // initialize the global OccludersExtractor variables - public OccludersExtractor(Matrix4f vpm, int cc, BoundingBox sBB, BoundingBox cBB, - GeometryList sOCC, TempVars v) { - viewProjMatrix = vpm; - casterCount = cc; - splitBB = sBB; - casterBB = cBB; - splitOccluders = sOCC; - vars = v; + private final Matrix4f viewProjMatrix; + private int casterCount; + private final BoundingBox splitBB, casterBB; + private final GeometryList splitOccluders; + private final TempVars vars; + + /** + * Creates a new {@code OccludersExtractor}. + * + * @param viewProjMatrix the view-projection matrix of the shadow camera (not null, unaffected) + * @param splitBB the bounding box of the viewer camera's frustum in shadow camera's view-projection space (not null, unaffected) + * @param casterBB a bounding box to merge found caster bounds into (not null, modified) + * @param splitOccluders a list to add found caster geometries to (may be null if only counting casters) + * @param vars a {@link TempVars} instance for temporary object pooling (not null, managed by caller) + */ + public OccludersExtractor(Matrix4f viewProjMatrix, BoundingBox splitBB, BoundingBox casterBB, + GeometryList splitOccluders, TempVars vars) { + this.viewProjMatrix = viewProjMatrix; + this.casterCount = 0; + this.splitBB = splitBB; + this.casterBB = casterBB; + this.splitOccluders = splitOccluders; + this.vars = vars; } /** - * Check the rootScene against camera frustum and if intersects process it recursively. - * The global OccludersExtractor variables need to be initialized first. - * Variables are updated and used in {@link ShadowUtil#updateShadowCamera} at last. + * Recursively checks the provided scene graph for shadow casters (occluders) + * and adds them to the internal list if they intersect the specified frustum. + * The {@code casterCount} and {@code casterBB} will be updated during this process. * * @param scene the root of the scene to check (may be null) - * @return the number of shadow casters found + * @return this {@code OccludersExtractor} instance for chaining. */ - public int addOccluders(Spatial scene) { - if (scene != null) process(scene); + public OccludersExtractor addOccluders(Spatial scene) { + if (scene != null) { + process(scene); + } + return this; + } + + /** + * Returns the number of shadow casters found by this extractor. + * + * @return the current count of shadow casters. + */ + public int getCasterCount() { return casterCount; } + /** + * Internal recursive method to process the scene graph and identify shadow casters. + * + * @param scene the current spatial to process (not null) + */ private void process(Spatial scene) { - if (scene.getCullHint() == Spatial.CullHint.Always) return; + if (scene.getCullHint() == Spatial.CullHint.Always) { + return; + } RenderQueue.ShadowMode shadowMode = scene.getShadowMode(); if (scene instanceof Geometry) { // convert bounding box to light's viewproj space - Geometry occluder = (Geometry)scene; - if (shadowMode != RenderQueue.ShadowMode.Off && shadowMode != RenderQueue.ShadowMode.Receive - && !occluder.isGrouped() && occluder.getWorldBound()!=null) { + Geometry occluder = (Geometry) scene; + if (shadowMode != RenderQueue.ShadowMode.Off + && shadowMode != RenderQueue.ShadowMode.Receive + && !occluder.isGrouped() + && occluder.getWorldBound() != null) { + BoundingVolume bv = occluder.getWorldBound(); BoundingVolume occBox = bv.transform(viewProjMatrix, vars.bbox); boolean intersects = splitBB.intersects(occBox); if (!intersects && occBox instanceof BoundingBox) { BoundingBox occBB = (BoundingBox) occBox; - // Kirill 01/10/2011 - // Extend the occluder further into the frustum - // This fixes shadow disappearing issues when - // the caster itself is not in the view camera - // but its shadow is in the camera + float originalZExtent = occBB.getZExtent(); + + // Attempt to extend the occluder further into the frustum for better shadow coverage. + // This fixes issues where the caster itself is outside the view camera, but its shadow is visible. // The number is in world units - occBB.setZExtent(occBB.getZExtent() + 50); + occBB.setZExtent(originalZExtent + 50); occBB.setCenter(occBB.getCenter().addLocal(0, 0, 25)); if (splitBB.intersects(occBB)) { - //Nehon : prevent NaN and infinity values to screw the final bounding box + // Prevent NaN and infinity values from screwing the final bounding box if (!Float.isNaN(occBox.getCenter().x) && !Float.isInfinite(occBox.getCenter().x)) { - // To prevent extending the depth range too much - // We return the bound to its former shape - // Before adding it - occBB.setZExtent(occBB.getZExtent() - 50); + // Restore original bound before merging to prevent depth range extension + occBB.setZExtent(originalZExtent - 50); occBB.setCenter(occBB.getCenter().subtractLocal(0, 0, 25)); casterBB.mergeLocal(occBox); casterCount++; @@ -431,30 +472,27 @@ private void process(Spatial scene) { } } } - } else if (scene instanceof Node && ((Node)scene).getWorldBound() != null) { - Node nodeOcc = (Node)scene; - boolean intersects = false; - // some + } else if (scene instanceof Node && scene.getWorldBound() != null) { + Node nodeOcc = (Node) scene; BoundingVolume bv = nodeOcc.getWorldBound(); BoundingVolume occBox = bv.transform(viewProjMatrix, vars.bbox); - intersects = splitBB.intersects(occBox); + boolean intersects = splitBB.intersects(occBox); if (!intersects && occBox instanceof BoundingBox) { BoundingBox occBB = (BoundingBox) occBox; - //Kirill 01/10/2011 - // Extend the occluder further into the frustum - // This fixes shadow disappearing issues when - // the caster itself is not in the view camera - // but its shadow is in the camera + float originalZExtent = occBB.getZExtent(); + + // Attempt to extend the occluder further into the frustum for better shadow coverage. + // This fixes issues where the caster itself is outside the view camera, but its shadow is visible. // The number is in world units - occBB.setZExtent(occBB.getZExtent() + 50); + occBB.setZExtent(originalZExtent + 50); occBB.setCenter(occBB.getCenter().addLocal(0, 0, 25)); intersects = splitBB.intersects(occBB); } if (intersects) { - for (Spatial child : ((Node)scene).getChildren()) { - process(child); + for (Spatial child : ((Node) scene).getChildren()) { + process(child); // Recursively process children } } } @@ -462,16 +500,17 @@ private void process(Spatial scene) { } /** - * Updates the shadow camera to properly contain the given points (which - * contain the eye camera frustum corners) and the shadow occluder objects - * collected through the traverse of the scene hierarchy + * Updates the shadow camera's projection matrix to encompass the viewer camera's + * frustum corners and the identified shadow occluder and receiver objects. + * This method calculates a tight bounding box in the shadow camera's view-projection + * space and uses it to derive a suitable orthographic projection. * - * @param viewPort the ViewPort - * @param receivers a list of receiving geometries - * @param shadowCam the shadow camera (not null, modified) - * @param points an array of location vectors (not null, unaffected) - * @param splitOccluders a list of occluding geometries - * @param shadowMapSize the size of each edge of the shadow map (in pixels) + * @param viewPort the current view port (not null) + * @param receivers a list of geometries acting as shadow receivers (not null, unaffected) + * @param shadowCam the shadow camera to be updated (not null, modified) + * @param points an array of 8 {@code Vector3f} representing the viewer camera's frustum corners in world space (not null, unaffected) + * @param splitOccluders a {@link GeometryList} to populate with geometries that act as shadow casters (may be empty, modified) + * @param shadowMapSize the size of each edge of the shadow map texture (in pixels), used for stabilization */ public static void updateShadowCamera(ViewPort viewPort, GeometryList receivers, @@ -498,7 +537,7 @@ public static void updateShadowCamera(ViewPort viewPort, BoundingBox casterBB = new BoundingBox(); BoundingBox receiverBB = new BoundingBox(); - int casterCount = 0, receiverCount = 0; + int receiverCount = 0; for (int i = 0; i < receivers.size(); i++) { // convert bounding box to light's viewproj space @@ -516,11 +555,11 @@ public static void updateShadowCamera(ViewPort viewPort, } // collect splitOccluders through scene recursive traverse - OccludersExtractor occExt = new OccludersExtractor(viewProjMatrix, casterCount, splitBB, casterBB, splitOccluders, vars); + OccludersExtractor occExt = new OccludersExtractor(viewProjMatrix, splitBB, casterBB, splitOccluders, vars); for (Spatial scene : viewPort.getScenes()) { occExt.addOccluders(scene); } - casterCount = occExt.casterCount; + int casterCount = occExt.getCasterCount(); if (casterCount == 0) { vars.release(); @@ -564,7 +603,6 @@ public static void updateShadowCamera(ViewPort viewPort, cropMin.z = min(casterMin.z, splitMin.z); cropMax.z = min(receiverMax.z, splitMax.z); - // Create the crop matrix. float scaleX, scaleY, scaleZ; float offsetX, offsetY, offsetZ; @@ -574,12 +612,11 @@ public static void updateShadowCamera(ViewPort viewPort, scaleX = deltaCropX == 0 ? 0 : 2.0f / deltaCropX; scaleY = deltaCropY == 0 ? 0 : 2.0f / deltaCropY; - //Shadow map stabilization approximation from shaderX 7 - //from Practical Cascaded Shadow maps adapted to PSSM - //scale stabilization + // Shadow map stabilization approximation from ShaderX 7 (Practical Cascaded Shadow Maps adapted to PSSM) + // Scale stabilization: Quantizes the scale to prevent shimmering artifacts during camera movement. float halfTextureSize = shadowMapSize * 0.5f; - if (halfTextureSize != 0 && scaleX >0 && scaleY>0) { + if (halfTextureSize != 0 && scaleX > 0 && scaleY > 0) { float scaleQuantizer = 0.1f; scaleX = 1.0f / FastMath.ceil(1.0f / scaleX * scaleQuantizer) * scaleQuantizer; scaleY = 1.0f / FastMath.ceil(1.0f / scaleY * scaleQuantizer) * scaleQuantizer; @@ -588,11 +625,8 @@ public static void updateShadowCamera(ViewPort viewPort, offsetX = -0.5f * (cropMax.x + cropMin.x) * scaleX; offsetY = -0.5f * (cropMax.y + cropMin.y) * scaleY; - - //Shadow map stabilization approximation from shaderX 7 - //from Practical Cascaded Shadow maps adapted to PSSM - //offset stabilization - if (halfTextureSize != 0 && scaleX >0 && scaleY>0) { + // Offset stabilization: Quantizes the offset to align pixel boundaries + if (halfTextureSize != 0 && scaleX > 0 && scaleY > 0) { offsetX = FastMath.ceil(offsetX * halfTextureSize) / halfTextureSize; offsetY = FastMath.ceil(offsetY * halfTextureSize) / halfTextureSize; } @@ -601,111 +635,114 @@ public static void updateShadowCamera(ViewPort viewPort, scaleZ = deltaCropZ == 0 ? 0 : 1.0f / deltaCropZ; offsetZ = -cropMin.z * scaleZ; - - - Matrix4f cropMatrix = vars.tempMat4; cropMatrix.set(scaleX, 0f, 0f, offsetX, 0f, scaleY, 0f, offsetY, 0f, 0f, scaleZ, offsetZ, 0f, 0f, 0f, 1f); - - Matrix4f result = new Matrix4f(); + Matrix4f result = vars.tempMat42; result.set(cropMatrix); result.multLocal(projMatrix); - vars.release(); shadowCam.setProjectionMatrix(result); + vars.release(); } /** - * Populates the outputGeometryList with the geometry of the - * inputGeometryList that are in the frustum of the given camera + * Populates the {@code outputGeometryList} with geometries from the + * {@code inputGeometryList} that are within the frustum of the given camera. + * This method iterates through each geometry and checks its world bound + * against the camera's frustum. * - * @param inputGeometryList The list containing all geometries to check - * against the camera frustum - * @param camera the camera to check geometries against - * @param outputGeometryList the list of all geometries that are in the - * camera frustum + * @param inputGeometryList The list containing all geometries to check against the camera frustum (not null, unaffected) + * @param camera The camera to check geometries against (not null, unaffected) + * @param outputGeometryList The list to which geometries within the camera frustum will be added (not null, modified) */ public static void getGeometriesInCamFrustum(GeometryList inputGeometryList, - Camera camera, - GeometryList outputGeometryList) { + Camera camera, GeometryList outputGeometryList) { + int planeState = camera.getPlaneState(); for (int i = 0; i < inputGeometryList.size(); i++) { Geometry g = inputGeometryList.get(i); - int planeState = camera.getPlaneState(); camera.setPlaneState(0); if (camera.contains(g.getWorldBound()) != Camera.FrustumIntersect.Outside) { outputGeometryList.add(g); } - camera.setPlaneState(planeState); } - + camera.setPlaneState(planeState); } /** - * Populates the outputGeometryList with the rootScene children geometries - * that are in the frustum of the given camera + * Populates the {@code outputGeometryList} with geometries from the + * provided scene graph (starting from {@code rootScene}) that are + * within the frustum of the given camera and match the specified shadow mode. + * This method traverses the scene hierarchy recursively. * - * @param rootScene the rootNode of the scene to traverse - * @param camera the camera to check geometries against - * @param mode the ShadowMode to test for - * @param outputGeometryList the list of all geometries that are in the - * camera frustum + * @param rootScene The root of the scene to traverse (may be null) + * @param camera The camera to check geometries against (not null, unaffected) + * @param mode The {@link RenderQueue.ShadowMode} to filter geometries by + * @param outputGeometryList The list to which matching geometries within the camera frustum will be added (not null, modified) */ - public static void getGeometriesInCamFrustum(Spatial rootScene, Camera camera, RenderQueue.ShadowMode mode, GeometryList outputGeometryList) { - if (rootScene != null && rootScene instanceof Node) { + public static void getGeometriesInCamFrustum(Spatial rootScene, Camera camera, + RenderQueue.ShadowMode mode, GeometryList outputGeometryList) { + if (rootScene instanceof Node) { int planeState = camera.getPlaneState(); - addGeometriesInCamFrustumFromNode(camera, (Node)rootScene, mode, outputGeometryList); + addGeometriesInCamFrustumFromNode(camera, (Node) rootScene, mode, outputGeometryList); camera.setPlaneState(planeState); } } /** - * Helper function to distinguish between Occluders and Receivers + * Helper function to determine if a spatial's shadow mode matches the desired mode. + * This is useful for distinguishing between shadow casters and receivers. * - * @param shadowMode the ShadowMode tested - * @param desired the desired ShadowMode - * @return true if tested ShadowMode matches the desired one + * @param shadowMode The actual {@link RenderQueue.ShadowMode} of a spatial. + * @param desired The desired {@link RenderQueue.ShadowMode} to check against. + * @return true if the {@code shadowMode} allows for the {@code desired} shadow behavior, false otherwise. */ - static private boolean checkShadowMode(RenderQueue.ShadowMode shadowMode, RenderQueue.ShadowMode desired) - { - if (shadowMode != RenderQueue.ShadowMode.Off) - { - switch (desired) { - case Cast : - return shadowMode==RenderQueue.ShadowMode.Cast || shadowMode==RenderQueue.ShadowMode.CastAndReceive; - case Receive: - return shadowMode==RenderQueue.ShadowMode.Receive || shadowMode==RenderQueue.ShadowMode.CastAndReceive; - case CastAndReceive: - return true; - } + private static boolean checkShadowMode(RenderQueue.ShadowMode shadowMode, RenderQueue.ShadowMode desired) { + if (shadowMode == RenderQueue.ShadowMode.Off) { + return false; + } + switch (desired) { + case Cast: + return shadowMode == RenderQueue.ShadowMode.Cast || shadowMode == RenderQueue.ShadowMode.CastAndReceive; + case Receive: + return shadowMode == RenderQueue.ShadowMode.Receive || shadowMode == RenderQueue.ShadowMode.CastAndReceive; + case CastAndReceive: + return true; // Any non-Off mode implies CastAndReceive if that's desired + default: + return false; } - return false; } /** - * Helper function used to recursively populate the outputGeometryList - * with geometry children of scene node + * Recursive helper function to populate the {@code outputGeometryList} with geometries + * from a given node and its children that are within the camera frustum and match the + * specified shadow mode. * - * @param camera - * @param scene the root of the scene to traverse (may be null) - * @param mode the ShadowMode to test for - * @param outputGeometryList + * @param camera The camera to check against (not null, unaffected) + * @param scene The current node in the scene graph to traverse (not null) + * @param mode The {@link RenderQueue.ShadowMode} to filter geometries by. + * @param outputGeometryList The list to add matching geometries to (not null, modified) */ - private static void addGeometriesInCamFrustumFromNode(Camera camera, Node scene, RenderQueue.ShadowMode mode, GeometryList outputGeometryList) { - if (scene.getCullHint() == Spatial.CullHint.Always) return; + private static void addGeometriesInCamFrustumFromNode(Camera camera, Node scene, + RenderQueue.ShadowMode mode, GeometryList outputGeometryList) { + if (scene.getCullHint() == Spatial.CullHint.Always) { + return; + } + camera.setPlaneState(0); if (camera.contains(scene.getWorldBound()) != Camera.FrustumIntersect.Outside) { - for (Spatial child: scene.getChildren()) { - if (child instanceof Node) addGeometriesInCamFrustumFromNode(camera, (Node)child, mode, outputGeometryList); + for (Spatial child : scene.getChildren()) { + if (child instanceof Node) + addGeometriesInCamFrustumFromNode(camera, (Node) child, mode, outputGeometryList); else if (child instanceof Geometry && child.getCullHint() != Spatial.CullHint.Always) { camera.setPlaneState(0); if (checkShadowMode(child.getShadowMode(), mode) && - !((Geometry)child).isGrouped() && + !((Geometry) child).isGrouped() && camera.contains(child.getWorldBound()) != Camera.FrustumIntersect.Outside) { - outputGeometryList.add((Geometry)child); + outputGeometryList.add((Geometry) child); } } } @@ -713,23 +750,24 @@ else if (child instanceof Geometry && child.getCullHint() != Spatial.CullHint.Al } /** - * Populates the outputGeometryList with the geometry of the - * inputGeometryList that are in the radius of a light. - * The array must contain 6 cameras, initialized to represent the viewspace of a point light. + * Populates the {@code outputGeometryList} with geometries from the + * {@code inputGeometryList} that are within the light's effective radius, + * represented by an array of cameras (e.g., 6 cameras for a point light's cubemap faces). + * A geometry is considered "in light radius" if its world bound intersects + * the frustum of at least one of the provided cameras. * - * @param inputGeometryList The list containing all geometries to check - * against the camera frustum - * @param cameras the camera array to check geometries against - * @param outputGeometryList the list of all geometries that are in the - * camera frustum + * @param inputGeometryList The list containing all geometries to check (not null, unaffected) + * @param cameras An array of cameras representing the light's view frustums (not null, unaffected) + * @param outputGeometryList The list to which geometries within the light's radius will be added (not null, modified) */ public static void getGeometriesInLightRadius(GeometryList inputGeometryList, - Camera[] cameras, - GeometryList outputGeometryList) { + Camera[] cameras, GeometryList outputGeometryList) { for (int i = 0; i < inputGeometryList.size(); i++) { Geometry g = inputGeometryList.get(i); boolean inFrustum = false; - for (int j = 0; j < cameras.length && inFrustum == false; j++) { + + // Iterate through all light cameras, stop if found in any + for (int j = 0; j < cameras.length && !inFrustum; j++) { Camera camera = cameras[j]; int planeState = camera.getPlaneState(); camera.setPlaneState(0); @@ -740,38 +778,47 @@ public static void getGeometriesInLightRadius(GeometryList inputGeometryList, outputGeometryList.add(g); } } - } /** - * Populates the outputGeometryList with the geometries of the children - * of OccludersExtractor.rootScene node that are both in the frustum of the given vpCamera and some camera inside cameras array. - * The array of cameras must be initialized to represent the light viewspace of some light like pointLight or spotLight + * Populates the {@code outputGeometryList} with geometries from the children + * of {@code rootScene} that are both in the frustum of the main viewport camera + * ({@code vpCamera}) and within the view frustum of at least one camera + * from the provided {@code cameras} array (representing a light's view space, + * like for point or spot lights). The geometries are also filtered by their + * {@link RenderQueue.ShadowMode}. * - * @param rootScene the root of the scene to traverse (may be null) - * @param vpCamera the viewPort camera - * @param cameras the camera array to check geometries against, representing the light viewspace - * @param mode the ShadowMode to test for - * @param outputGeometryList the output list of all geometries that are in the camera frustum + * @param rootScene The root of the scene to traverse (may be null) + * @param vpCamera The main viewport camera (not null, unaffected) + * @param cameras An array of cameras representing the light's view frustums (not null, unaffected) + * @param mode The {@link RenderQueue.ShadowMode} to filter geometries by. + * @param outputGeometryList The list to which matching geometries will be added (not null, modified) */ - public static void getLitGeometriesInViewPort(Spatial rootScene, Camera vpCamera, Camera[] cameras, RenderQueue.ShadowMode mode, GeometryList outputGeometryList) { - if (rootScene != null && rootScene instanceof Node) { + public static void getLitGeometriesInViewPort(Spatial rootScene, Camera vpCamera, Camera[] cameras, + RenderQueue.ShadowMode mode, GeometryList outputGeometryList) { + if (rootScene instanceof Node) { addGeometriesInCamFrustumAndViewPortFromNode(vpCamera, cameras, rootScene, mode, outputGeometryList); } } + /** - * Helper function to recursively collect the geometries for getLitGeometriesInViewPort function. + * Recursive helper function to collect geometries that are visible in both + * the main viewport camera and at least one light view camera. * - * @param vpCamera the viewPort camera - * @param cameras the camera array to check geometries against, representing the light viewspace - * @param scene the Node to traverse or geometry to possibly add - * @param outputGeometryList the output list of all geometries that are in the camera frustum + * @param vpCamera The main viewport camera (not null, unaffected) + * @param cameras An array of cameras representing the light's view frustums (not null, unaffected) + * @param scene The current spatial (Node or Geometry) to process (not null) + * @param mode The {@link RenderQueue.ShadowMode} to filter geometries by. + * @param outputGeometryList The list to add matching geometries to (not null, modified) */ - private static void addGeometriesInCamFrustumAndViewPortFromNode(Camera vpCamera, Camera[] cameras, Spatial scene, RenderQueue.ShadowMode mode, GeometryList outputGeometryList) { - if (scene.getCullHint() == Spatial.CullHint.Always) return; + private static void addGeometriesInCamFrustumAndViewPortFromNode(Camera vpCamera, Camera[] cameras, + Spatial scene, RenderQueue.ShadowMode mode, GeometryList outputGeometryList) { + if (scene.getCullHint() == Spatial.CullHint.Always) { + return; + } boolean inFrustum = false; - for (int j = 0; j < cameras.length && inFrustum == false; j++) { + for (int j = 0; j < cameras.length && !inFrustum; j++) { Camera camera = cameras[j]; int planeState = camera.getPlaneState(); camera.setPlaneState(0); @@ -779,16 +826,14 @@ private static void addGeometriesInCamFrustumAndViewPortFromNode(Camera vpCamera camera.setPlaneState(planeState); } if (inFrustum) { - if (scene instanceof Node) - { - Node node = (Node)scene; - for (Spatial child: node.getChildren()) { + if (scene instanceof Node) { + Node node = (Node) scene; + for (Spatial child : node.getChildren()) { addGeometriesInCamFrustumAndViewPortFromNode(vpCamera, cameras, child, mode, outputGeometryList); } - } - else if (scene instanceof Geometry) { - if (checkShadowMode(scene.getShadowMode(), mode) && !((Geometry)scene).isGrouped()) { - outputGeometryList.add((Geometry)scene); + } else if (scene instanceof Geometry) { + if (checkShadowMode(scene.getShadowMode(), mode) && !((Geometry) scene).isGrouped()) { + outputGeometryList.add((Geometry) scene); } } } From 91b422357d8a7f86dd56e29633f8bd1e1edb9da8 Mon Sep 17 00:00:00 2001 From: Wyatt Gillette Date: Sat, 7 Jun 2025 17:05:09 +0200 Subject: [PATCH 2/6] Update ShadowUtil: comments --- jme3-core/src/main/java/com/jme3/shadow/ShadowUtil.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/jme3-core/src/main/java/com/jme3/shadow/ShadowUtil.java b/jme3-core/src/main/java/com/jme3/shadow/ShadowUtil.java index cfbf884efd..66aec13404 100644 --- a/jme3-core/src/main/java/com/jme3/shadow/ShadowUtil.java +++ b/jme3-core/src/main/java/com/jme3/shadow/ShadowUtil.java @@ -437,7 +437,7 @@ private void process(Spatial scene) { && shadowMode != RenderQueue.ShadowMode.Receive && !occluder.isGrouped() && occluder.getWorldBound() != null) { - + BoundingVolume bv = occluder.getWorldBound(); BoundingVolume occBox = bv.transform(viewProjMatrix, vars.bbox); @@ -625,6 +625,7 @@ public static void updateShadowCamera(ViewPort viewPort, offsetX = -0.5f * (cropMax.x + cropMin.x) * scaleX; offsetY = -0.5f * (cropMax.y + cropMin.y) * scaleY; + // Shadow map stabilization approximation from ShaderX 7 (Practical Cascaded Shadow Maps adapted to PSSM) // Offset stabilization: Quantizes the offset to align pixel boundaries if (halfTextureSize != 0 && scaleX > 0 && scaleY > 0) { offsetX = FastMath.ceil(offsetX * halfTextureSize) / halfTextureSize; From eb78164f618fdb0f992ee57f80c48441f956c296 Mon Sep 17 00:00:00 2001 From: Wyatt Gillette Date: Sat, 7 Jun 2025 20:06:33 +0200 Subject: [PATCH 3/6] Update ShadowUtil: rename tempVec2D to tempVec2 --- .../main/java/com/jme3/shadow/ShadowUtil.java | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/jme3-core/src/main/java/com/jme3/shadow/ShadowUtil.java b/jme3-core/src/main/java/com/jme3/shadow/ShadowUtil.java index 66aec13404..1a3ebde041 100644 --- a/jme3-core/src/main/java/com/jme3/shadow/ShadowUtil.java +++ b/jme3-core/src/main/java/com/jme3/shadow/ShadowUtil.java @@ -79,18 +79,18 @@ public static void updateFrustumPoints2(Camera viewCam, Vector3f[] points) { int h = viewCam.getHeight(); TempVars vars = TempVars.get(); - Vector2f tempVec2D = vars.vect2d; + Vector2f tempVec2 = vars.vect2d; Vector3f tempStore = vars.vect1; - points[0].set(viewCam.getWorldCoordinates(tempVec2D.set(0, 0), 0, tempStore)); - points[1].set(viewCam.getWorldCoordinates(tempVec2D.set(0, h), 0, tempStore)); - points[2].set(viewCam.getWorldCoordinates(tempVec2D.set(w, h), 0, tempStore)); - points[3].set(viewCam.getWorldCoordinates(tempVec2D.set(w, 0), 0, tempStore)); + points[0].set(viewCam.getWorldCoordinates(tempVec2.set(0, 0), 0, tempStore)); + points[1].set(viewCam.getWorldCoordinates(tempVec2.set(0, h), 0, tempStore)); + points[2].set(viewCam.getWorldCoordinates(tempVec2.set(w, h), 0, tempStore)); + points[3].set(viewCam.getWorldCoordinates(tempVec2.set(w, 0), 0, tempStore)); - points[4].set(viewCam.getWorldCoordinates(tempVec2D.set(0, 0), 1, tempStore)); - points[5].set(viewCam.getWorldCoordinates(tempVec2D.set(0, h), 1, tempStore)); - points[6].set(viewCam.getWorldCoordinates(tempVec2D.set(w, h), 1, tempStore)); - points[7].set(viewCam.getWorldCoordinates(tempVec2D.set(w, 0), 1, tempStore)); + points[4].set(viewCam.getWorldCoordinates(tempVec2.set(0, 0), 1, tempStore)); + points[5].set(viewCam.getWorldCoordinates(tempVec2.set(0, h), 1, tempStore)); + points[6].set(viewCam.getWorldCoordinates(tempVec2.set(w, h), 1, tempStore)); + points[7].set(viewCam.getWorldCoordinates(tempVec2.set(w, 0), 1, tempStore)); vars.release(); } From 9b0aeae6ac640f11889e286a26a2709a92dc2372 Mon Sep 17 00:00:00 2001 From: Wyatt Gillette Date: Sat, 7 Jun 2025 21:46:55 +0200 Subject: [PATCH 4/6] ShadowUtil: updateFrustumPoints() vector cloning not needed --- .../src/main/java/com/jme3/shadow/ShadowUtil.java | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/jme3-core/src/main/java/com/jme3/shadow/ShadowUtil.java b/jme3-core/src/main/java/com/jme3/shadow/ShadowUtil.java index 1a3ebde041..46db38454e 100644 --- a/jme3-core/src/main/java/com/jme3/shadow/ShadowUtil.java +++ b/jme3-core/src/main/java/com/jme3/shadow/ShadowUtil.java @@ -142,18 +142,18 @@ public static void updateFrustumPoints(Camera viewCam, far_width = far_height * ratio; } - Vector3f tempVec = vars.vect2; // Reusing tempVec for calculations + Vector3f tempVec = vars.vect2; // Reusing tempVec for calculations Vector3f nearCenter = vars.vect3; - Vector3f farCenter = vars.vect4; + Vector3f farCenter = vars.vect4; // Calculate near and far center points nearCenter.set(camDir).multLocal(near).addLocal(camPos); farCenter.set(camDir).multLocal(far).addLocal(camPos); - Vector3f nearUp = vars.vect5.set(camUp).multLocal(near_height).clone(); - Vector3f farUp = vars.vect6.set(camUp).multLocal(far_height).clone(); - Vector3f nearRight = vars.vect7.set(camRight).multLocal(near_width).clone(); - Vector3f farRight = vars.vect8.set(camRight).multLocal(far_width).clone(); + Vector3f nearUp = vars.vect5.set(camUp).multLocal(near_height); + Vector3f farUp = vars.vect6.set(camUp).multLocal(far_height); + Vector3f nearRight = vars.vect7.set(camRight).multLocal(near_width); + Vector3f farRight = vars.vect8.set(camRight).multLocal(far_width); // Populate frustum points points[0].set(nearCenter).subtractLocal(nearUp).subtractLocal(nearRight); From fc6828c3ddcb30be2d3dd2a79918afe3479f6539 Mon Sep 17 00:00:00 2001 From: Wyatt Gillette Date: Sun, 8 Jun 2025 11:37:37 +0200 Subject: [PATCH 5/6] Update ShadowUtil: code cleanup --- .../main/java/com/jme3/shadow/ShadowUtil.java | 51 +++++++++---------- 1 file changed, 25 insertions(+), 26 deletions(-) diff --git a/jme3-core/src/main/java/com/jme3/shadow/ShadowUtil.java b/jme3-core/src/main/java/com/jme3/shadow/ShadowUtil.java index 46db38454e..c0056d9caa 100644 --- a/jme3-core/src/main/java/com/jme3/shadow/ShadowUtil.java +++ b/jme3-core/src/main/java/com/jme3/shadow/ShadowUtil.java @@ -71,8 +71,7 @@ private ShadowUtil() { * Updates a points arrays with the frustum corners of the provided camera. * * @param viewCam the viewing Camera (not null, unaffected) - * @param points storage for the corner coordinates (not null, length ≥8, - * modified) + * @param points storage for the corner coordinates (not null, length ≥8, modified) */ public static void updateFrustumPoints2(Camera viewCam, Vector3f[] points) { int w = viewCam.getWidth(); @@ -80,17 +79,17 @@ public static void updateFrustumPoints2(Camera viewCam, Vector3f[] points) { TempVars vars = TempVars.get(); Vector2f tempVec2 = vars.vect2d; - Vector3f tempStore = vars.vect1; - points[0].set(viewCam.getWorldCoordinates(tempVec2.set(0, 0), 0, tempStore)); - points[1].set(viewCam.getWorldCoordinates(tempVec2.set(0, h), 0, tempStore)); - points[2].set(viewCam.getWorldCoordinates(tempVec2.set(w, h), 0, tempStore)); - points[3].set(viewCam.getWorldCoordinates(tempVec2.set(w, 0), 0, tempStore)); + viewCam.getWorldCoordinates(tempVec2.set(0, 0), 0, points[0]); + viewCam.getWorldCoordinates(tempVec2.set(0, h), 0, points[1]); + viewCam.getWorldCoordinates(tempVec2.set(w, h), 0, points[2]); + viewCam.getWorldCoordinates(tempVec2.set(w, 0), 0, points[3]); - points[4].set(viewCam.getWorldCoordinates(tempVec2.set(0, 0), 1, tempStore)); - points[5].set(viewCam.getWorldCoordinates(tempVec2.set(0, h), 1, tempStore)); - points[6].set(viewCam.getWorldCoordinates(tempVec2.set(w, h), 1, tempStore)); - points[7].set(viewCam.getWorldCoordinates(tempVec2.set(w, 0), 1, tempStore)); + viewCam.getWorldCoordinates(tempVec2.set(0, 0), 1, points[4]); + viewCam.getWorldCoordinates(tempVec2.set(0, h), 1, points[5]); + viewCam.getWorldCoordinates(tempVec2.set(w, h), 1, points[6]); + viewCam.getWorldCoordinates(tempVec2.set(w, 0), 1, points[7]); + vars.release(); } @@ -106,10 +105,10 @@ public static void updateFrustumPoints2(Camera viewCam, Vector3f[] points) { * @param points storage for the corner coordinates (not null, length ≥ 8, modified) */ public static void updateFrustumPoints(Camera viewCam, - float nearOverride, - float farOverride, - float scale, - Vector3f[] points) { + float nearOverride, + float farOverride, + float scale, + Vector3f[] points) { TempVars vars = TempVars.get(); @@ -513,11 +512,11 @@ private void process(Spatial scene) { * @param shadowMapSize the size of each edge of the shadow map texture (in pixels), used for stabilization */ public static void updateShadowCamera(ViewPort viewPort, - GeometryList receivers, - Camera shadowCam, - Vector3f[] points, - GeometryList splitOccluders, - float shadowMapSize) { + GeometryList receivers, + Camera shadowCam, + Vector3f[] points, + GeometryList splitOccluders, + float shadowMapSize) { boolean ortho = shadowCam.isParallelProjection(); @@ -661,7 +660,7 @@ public static void updateShadowCamera(ViewPort viewPort, * @param outputGeometryList The list to which geometries within the camera frustum will be added (not null, modified) */ public static void getGeometriesInCamFrustum(GeometryList inputGeometryList, - Camera camera, GeometryList outputGeometryList) { + Camera camera, GeometryList outputGeometryList) { int planeState = camera.getPlaneState(); for (int i = 0; i < inputGeometryList.size(); i++) { Geometry g = inputGeometryList.get(i); @@ -685,7 +684,7 @@ public static void getGeometriesInCamFrustum(GeometryList inputGeometryList, * @param outputGeometryList The list to which matching geometries within the camera frustum will be added (not null, modified) */ public static void getGeometriesInCamFrustum(Spatial rootScene, Camera camera, - RenderQueue.ShadowMode mode, GeometryList outputGeometryList) { + RenderQueue.ShadowMode mode, GeometryList outputGeometryList) { if (rootScene instanceof Node) { int planeState = camera.getPlaneState(); addGeometriesInCamFrustumFromNode(camera, (Node) rootScene, mode, outputGeometryList); @@ -728,7 +727,7 @@ private static boolean checkShadowMode(RenderQueue.ShadowMode shadowMode, Render * @param outputGeometryList The list to add matching geometries to (not null, modified) */ private static void addGeometriesInCamFrustumFromNode(Camera camera, Node scene, - RenderQueue.ShadowMode mode, GeometryList outputGeometryList) { + RenderQueue.ShadowMode mode, GeometryList outputGeometryList) { if (scene.getCullHint() == Spatial.CullHint.Always) { return; } @@ -762,7 +761,7 @@ else if (child instanceof Geometry && child.getCullHint() != Spatial.CullHint.Al * @param outputGeometryList The list to which geometries within the light's radius will be added (not null, modified) */ public static void getGeometriesInLightRadius(GeometryList inputGeometryList, - Camera[] cameras, GeometryList outputGeometryList) { + Camera[] cameras, GeometryList outputGeometryList) { for (int i = 0; i < inputGeometryList.size(); i++) { Geometry g = inputGeometryList.get(i); boolean inFrustum = false; @@ -796,7 +795,7 @@ public static void getGeometriesInLightRadius(GeometryList inputGeometryList, * @param outputGeometryList The list to which matching geometries will be added (not null, modified) */ public static void getLitGeometriesInViewPort(Spatial rootScene, Camera vpCamera, Camera[] cameras, - RenderQueue.ShadowMode mode, GeometryList outputGeometryList) { + RenderQueue.ShadowMode mode, GeometryList outputGeometryList) { if (rootScene instanceof Node) { addGeometriesInCamFrustumAndViewPortFromNode(vpCamera, cameras, rootScene, mode, outputGeometryList); } @@ -813,7 +812,7 @@ public static void getLitGeometriesInViewPort(Spatial rootScene, Camera vpCamera * @param outputGeometryList The list to add matching geometries to (not null, modified) */ private static void addGeometriesInCamFrustumAndViewPortFromNode(Camera vpCamera, Camera[] cameras, - Spatial scene, RenderQueue.ShadowMode mode, GeometryList outputGeometryList) { + Spatial scene, RenderQueue.ShadowMode mode, GeometryList outputGeometryList) { if (scene.getCullHint() == Spatial.CullHint.Always) { return; } From 6b5b7b37d6cb0793f1e4ddeb1b1176bae271dac4 Mon Sep 17 00:00:00 2001 From: Wyatt Gillette Date: Fri, 20 Jun 2025 12:24:37 +0200 Subject: [PATCH 6/6] Update ShadowUtil.java --- .../main/java/com/jme3/shadow/ShadowUtil.java | 107 ++++++++---------- 1 file changed, 50 insertions(+), 57 deletions(-) diff --git a/jme3-core/src/main/java/com/jme3/shadow/ShadowUtil.java b/jme3-core/src/main/java/com/jme3/shadow/ShadowUtil.java index c0056d9caa..b1e49f9d65 100644 --- a/jme3-core/src/main/java/com/jme3/shadow/ShadowUtil.java +++ b/jme3-core/src/main/java/com/jme3/shadow/ShadowUtil.java @@ -89,7 +89,7 @@ public static void updateFrustumPoints2(Camera viewCam, Vector3f[] points) { viewCam.getWorldCoordinates(tempVec2.set(0, h), 1, points[5]); viewCam.getWorldCoordinates(tempVec2.set(w, h), 1, points[6]); viewCam.getWorldCoordinates(tempVec2.set(w, 0), 1, points[7]); - + vars.release(); } @@ -98,61 +98,54 @@ public static void updateFrustumPoints2(Camera viewCam, Vector3f[] points) { * of the given camera, with optional overrides for near/far distances and a scale factor. * The array must have a length of at least 8. * - * @param viewCam the viewing Camera (not null, unaffected) - * @param nearOverride distance to the near plane (in world units) - * @param farOverride distance to the far plane (in world units) - * @param scale a factor to scale the frustum points around their center (1.0 for no scaling) - * @param points storage for the corner coordinates (not null, length ≥ 8, modified) + * @param viewCam the viewing Camera (not null, unaffected) + * @param near distance to the near plane (in world units) + * @param far distance to the far plane (in world units) + * @param scale a factor to scale the frustum points around their center (1.0 for no scaling) + * @param points storage for the corner coordinates (not null, length ≥ 8, modified) */ - public static void updateFrustumPoints(Camera viewCam, - float nearOverride, - float farOverride, - float scale, - Vector3f[] points) { + public static void updateFrustumPoints(Camera viewCam, float near, float far, float scale, Vector3f[] points) { TempVars vars = TempVars.get(); Vector3f camPos = viewCam.getLocation(); - Vector3f camDir = viewCam.getDirection(); - Vector3f camUp = viewCam.getUp(); - Vector3f camRight = vars.vect1.set(camDir).crossLocal(camUp).normalizeLocal(); + Vector3f camDir = viewCam.getDirection(vars.vect1); + Vector3f camUp = viewCam.getUp(vars.vect2); + Vector3f camRight = vars.vect3.set(camDir).crossLocal(camUp).normalizeLocal(); float depthHeightRatio = viewCam.getFrustumTop() / viewCam.getFrustumNear(); - float near = nearOverride; - float far = farOverride; - float ftop = viewCam.getFrustumTop(); + float ftop = viewCam.getFrustumTop(); float fright = viewCam.getFrustumRight(); - float ratio = fright / ftop; + float ratio = fright / ftop; - float near_height; - float near_width; - float far_height; - float far_width; + float nearHeight; + float nearWidth; + float farHeight; + float farWidth; if (viewCam.isParallelProjection()) { - near_height = ftop; - near_width = near_height * ratio; - far_height = ftop; - far_width = far_height * ratio; + nearHeight = ftop; + nearWidth = nearHeight * ratio; + farHeight = ftop; + farWidth = farHeight * ratio; } else { - near_height = depthHeightRatio * near; - near_width = near_height * ratio; - far_height = depthHeightRatio * far; - far_width = far_height * ratio; + nearHeight = depthHeightRatio * near; + nearWidth = nearHeight * ratio; + farHeight = depthHeightRatio * far; + farWidth = farHeight * ratio; } - Vector3f tempVec = vars.vect2; // Reusing tempVec for calculations - Vector3f nearCenter = vars.vect3; - Vector3f farCenter = vars.vect4; + Vector3f nearCenter = vars.vect4; + Vector3f farCenter = vars.vect5; // Calculate near and far center points nearCenter.set(camDir).multLocal(near).addLocal(camPos); farCenter.set(camDir).multLocal(far).addLocal(camPos); - Vector3f nearUp = vars.vect5.set(camUp).multLocal(near_height); - Vector3f farUp = vars.vect6.set(camUp).multLocal(far_height); - Vector3f nearRight = vars.vect7.set(camRight).multLocal(near_width); - Vector3f farRight = vars.vect8.set(camRight).multLocal(far_width); + Vector3f nearUp = vars.vect6.set(camUp).multLocal(nearHeight); + Vector3f farUp = vars.vect7.set(camUp).multLocal(farHeight); + Vector3f nearRight = vars.vect8.set(camRight).multLocal(nearWidth); + Vector3f farRight = vars.vect9.set(camRight).multLocal(farWidth); // Populate frustum points points[0].set(nearCenter).subtractLocal(nearUp).subtractLocal(nearRight); @@ -167,12 +160,13 @@ public static void updateFrustumPoints(Camera viewCam, if (scale != 1.0f) { // find center of frustum - Vector3f center = vars.vect9.zero(); + Vector3f center = new Vector3f(); for (int i = 0; i < 8; i++) { center.addLocal(points[i]); } center.divideLocal(8f); + Vector3f tempVec = vars.vect10; // Reusing tempVec for calculations for (int i = 0; i < 8; i++) { tempVec.set(points[i]).subtractLocal(center); tempVec.multLocal(scale - 1.0f); @@ -193,16 +187,16 @@ public static void updateFrustumPoints(Camera viewCam, */ public static BoundingBox computeUnionBound(GeometryList list, Transform transform) { BoundingBox bbox = new BoundingBox(); - TempVars tempVars = TempVars.get(); + TempVars vars = TempVars.get(); for (int i = 0; i < list.size(); i++) { BoundingVolume vol = list.get(i).getWorldBound(); - BoundingVolume newVol = vol.transform(transform, tempVars.bbox); + BoundingVolume newVol = vol.transform(transform, vars.bbox); //Nehon : prevent NaN and infinity values to screw the final bounding box if (!Float.isNaN(newVol.getCenter().x) && !Float.isInfinite(newVol.getCenter().x)) { bbox.mergeLocal(newVol); } } - tempVars.release(); + vars.release(); return bbox; } @@ -216,16 +210,16 @@ public static BoundingBox computeUnionBound(GeometryList list, Transform transfo */ public static BoundingBox computeUnionBound(GeometryList list, Matrix4f mat) { BoundingBox bbox = new BoundingBox(); - TempVars tempv = TempVars.get(); + TempVars vars = TempVars.get(); for (int i = 0; i < list.size(); i++) { BoundingVolume vol = list.get(i).getWorldBound(); - BoundingVolume store = vol.transform(mat, tempv.bbox); + BoundingVolume store = vol.transform(mat, vars.bbox); //Nehon : prevent NaN and infinity values to screw the final bounding box if (!Float.isNaN(store.getCenter().x) && !Float.isInfinite(store.getCenter().x)) { bbox.mergeLocal(store); } } - tempv.release(); + vars.release(); return bbox; } @@ -237,8 +231,7 @@ public static BoundingBox computeUnionBound(GeometryList list, Matrix4f mat) { */ public static BoundingBox computeUnionBound(List bv) { BoundingBox bbox = new BoundingBox(); - for (int i = 0; i < bv.size(); i++) { - BoundingVolume vol = bv.get(i); + for (BoundingVolume vol : bv) { bbox.mergeLocal(vol); } return bbox; @@ -256,12 +249,12 @@ public static BoundingBox computeBoundForPoints(Vector3f[] pts, Transform transf TempVars vars = TempVars.get(); Vector3f min = vars.vect1.set(Vector3f.POSITIVE_INFINITY); Vector3f max = vars.vect2.set(Vector3f.NEGATIVE_INFINITY); - Vector3f temp = vars.vect3; + Vector3f tempVec = vars.vect3; for (Vector3f pt : pts) { - transform.transformVector(pt, temp); - min.minLocal(temp); - max.maxLocal(temp); + transform.transformVector(pt, tempVec); + min.minLocal(tempVec); + max.maxLocal(tempVec); } Vector3f center = vars.vect4.set(min).addLocal(max).multLocal(0.5f); Vector3f extent = vars.vect5.set(max).subtractLocal(min).multLocal(0.5f); @@ -286,16 +279,16 @@ public static BoundingBox computeBoundForPoints(Vector3f[] pts, Matrix4f mat) { TempVars vars = TempVars.get(); Vector3f min = vars.vect1.set(Vector3f.POSITIVE_INFINITY); Vector3f max = vars.vect2.set(Vector3f.NEGATIVE_INFINITY); - Vector3f temp = vars.vect3; + Vector3f tempVec = vars.vect3; for (Vector3f pt : pts) { - float w = mat.multProj(pt, temp); - temp.x /= w; - temp.y /= w; - temp.z /= w; // Z component correction + float w = mat.multProj(pt, tempVec); + tempVec.x /= w; + tempVec.y /= w; + tempVec.z /= w; // Z component correction - min.minLocal(temp); - max.maxLocal(temp); + min.minLocal(tempVec); + max.maxLocal(tempVec); } Vector3f center = vars.vect4.set(min).addLocal(max).multLocal(0.5f); Vector3f extent = vars.vect5.set(max).subtractLocal(min).multLocal(0.5f);