diff --git a/jme3-core/src/main/java/com/jme3/shadow/SpotLightShadowRenderer.java b/jme3-core/src/main/java/com/jme3/shadow/SpotLightShadowRenderer.java
index 448857408b..2d5ea1a867 100644
--- a/jme3-core/src/main/java/com/jme3/shadow/SpotLightShadowRenderer.java
+++ b/jme3-core/src/main/java/com/jme3/shadow/SpotLightShadowRenderer.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
@@ -52,46 +52,51 @@
import java.io.IOException;
/**
- * SpotLightShadowRenderer renderer use Parallel Split Shadow Mapping technique
- * (pssm)
It splits the view frustum in several parts and compute a shadow
- * map for each one.
splits are distributed so that the closer they are from
- * the camera, the smaller they are to maximize the resolution used of the
- * shadow map.
This results in a better quality shadow than standard shadow
- * mapping.
for more information on this read http://http.developer.nvidia.com/GPUGems3/gpugems3_ch10.html
+ * Implements a shadow renderer specifically for {@link SpotLight SpotLights}
+ * using the **Parallel Split Shadow Mapping (PSSM)** technique.
*
- * @author Rémy Bouquet aka Nehon
+ *
PSSM divides the camera's view frustum into multiple sections, + * generating a separate shadow map for each. These splits are + * intelligently distributed, with smaller, higher-resolution maps for areas + * closer to the camera and larger, lower-resolution maps for distant areas. + * This approach optimizes shadow map usage, leading to superior shadow quality + * compared to standard shadow mapping techniques. + * + *
For a detailed explanation of PSSM, refer to: + * GPU Gems 3, Chapter 10: Parallel-Split Shadow Maps on Programmable GPUs + * + * @author Nehon */ public class SpotLightShadowRenderer extends AbstractShadowRenderer { - protected Camera shadowCam; + protected Camera shadowCam; protected SpotLight light; - protected Vector3f[] points = new Vector3f[8]; - //Holding the info for fading shadows in the far distance - + protected final Camera[] cameras = new Camera[1]; + protected final Vector3f[] points = new Vector3f[8]; + protected final Vector3f tempVec = new Vector3f(); /** - * Used for serialization use SpotLightShadowRenderer#SpotLightShadowRenderer(AssetManager assetManager, int shadowMapSize) + * For serialization only. Do not use. */ protected SpotLightShadowRenderer() { super(); } /** - * Create a SpotLightShadowRenderer This use standard shadow mapping + * Creates a new {@code SpotLightShadowRenderer} instance. * - * @param assetManager the application asset manager - * @param shadowMapSize the size of the rendered shadowmaps (512,1024,2048, - * etc...) The more quality, the fewer fps. + * @param assetManager The application's asset manager. + * @param shadowMapSize The size of the rendered shadow maps (e.g., 512, 1024, 2048). + * Higher values produce better quality shadows but may impact performance. */ public SpotLightShadowRenderer(AssetManager assetManager, int shadowMapSize) { super(assetManager, shadowMapSize, 1); init(shadowMapSize); } - private void init(int shadowMapSize) { shadowCam = new Camera(shadowMapSize, shadowMapSize); + cameras[0] = shadowCam; for (int i = 0; i < points.length; i++) { points[i] = new Vector3f(); } @@ -101,7 +106,8 @@ private void init(int shadowMapSize) { protected void initFrustumCam() { Camera viewCam = viewPort.getCamera(); frustumCam = viewCam.clone(); - frustumCam.setFrustum(viewCam.getFrustumNear(), zFarOverride, viewCam.getFrustumLeft(), viewCam.getFrustumRight(), viewCam.getFrustumTop(), viewCam.getFrustumBottom()); + frustumCam.setFrustum(viewCam.getFrustumNear(), zFarOverride, + viewCam.getFrustumLeft(), viewCam.getFrustumRight(), viewCam.getFrustumTop(), viewCam.getFrustumBottom()); } /** @@ -138,10 +144,9 @@ protected void updateShadowCams(Camera viewCam) { //We prevent computing the frustum points and splits with zeroed or negative near clip value float frustumNear = Math.max(viewCam.getFrustumNear(), 0.001f); ShadowUtil.updateFrustumPoints(viewCam, frustumNear, zFar, 1.0f, points); - //shadowCam.setDirection(direction); shadowCam.setFrustumPerspective(light.getSpotOuterAngle() * FastMath.RAD_TO_DEG * 2.0f, 1, 1f, light.getSpotRange()); - shadowCam.getRotation().lookAt(light.getDirection(), shadowCam.getUp()); + shadowCam.getRotation().lookAt(light.getDirection(), shadowCam.getUp(tempVec)); shadowCam.setLocation(light.getPosition()); shadowCam.update(); @@ -159,8 +164,6 @@ protected GeometryList getOccludersToRender(int shadowMapIndex, GeometryList sha @Override protected void getReceivers(GeometryList lightReceivers) { lightReceivers.clear(); - Camera[] cameras = new Camera[1]; - cameras[0] = shadowCam; for (Spatial scene : viewPort.getScenes()) { ShadowUtil.getLitGeometriesInViewPort(scene, viewPort.getCamera(), cameras, RenderQueue.ShadowMode.Receive, lightReceivers); } @@ -208,7 +211,6 @@ public void read(JmeImporter im) throws IOException { fadeInfo = (Vector2f) ic.readSavable("fadeInfo", null); fadeLength = ic.readFloat("fadeLength", 0f); init((int) shadowMapSize); - } @Override @@ -227,18 +229,17 @@ public void write(JmeExporter ex) throws IOException { * @return true if intersects, otherwise false */ @Override - protected boolean checkCulling(Camera viewCam) { + protected boolean checkCulling(Camera viewCam) { Camera cam = viewCam; - if(frustumCam != null){ - cam = frustumCam; + if (frustumCam != null) { + cam = frustumCam; cam.setLocation(viewCam.getLocation()); cam.setRotation(viewCam.getRotation()); } TempVars vars = TempVars.get(); - boolean intersects = light.intersectsFrustum(cam,vars); + boolean intersects = light.intersectsFrustum(cam, vars); vars.release(); return intersects; - } }