Skip to content

Commit 735132f

Browse files
authored
Update PointLightShadowFilter.java
1 parent aab9e9b commit 735132f

File tree

1 file changed

+168
-29
lines changed

1 file changed

+168
-29
lines changed

jme3-core/src/main/java/com/jme3/shadow/PointLightShadowFilter.java

Lines changed: 168 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (c) 2009-2024 jMonkeyEngine
2+
* Copyright (c) 2009-2025 jMonkeyEngine
33
* All rights reserved.
44
*
55
* Redistribution and use in source and binary forms, with or without
@@ -37,71 +37,210 @@
3737
import com.jme3.export.JmeImporter;
3838
import com.jme3.export.OutputCapsule;
3939
import com.jme3.light.PointLight;
40+
import com.jme3.material.Material;
41+
import com.jme3.math.Vector3f;
42+
import com.jme3.renderer.Camera;
43+
import com.jme3.renderer.queue.GeometryList;
44+
import com.jme3.renderer.queue.RenderQueue;
45+
import com.jme3.scene.Geometry;
46+
import com.jme3.scene.Spatial;
47+
import com.jme3.util.TempVars;
48+
import com.jme3.util.clone.Cloner;
49+
4050
import java.io.IOException;
4151

4252
/**
43-
* This Filter does basically the same as a PointLightShadowRenderer except it
44-
* renders the post shadow pass as a fullscreen quad pass instead of a geometry
45-
* pass. It's mostly faster than PointLightShadowRenderer as long as you have
46-
* more than about ten shadow receiving objects. The expense is the drawback
47-
* that the shadow Receive mode set on spatial is ignored. So basically all and
48-
* only objects that render depth in the scene receive shadows.
49-
*
50-
* API is basically the same as the PssmShadowRenderer.
53+
* Renders shadows for a {@link PointLight}. This renderer uses six cameras,
54+
* one for each face of a cube map, to capture shadows from the point light's
55+
* perspective.
5156
*
5257
* @author Rémy Bouquet aka Nehon
5358
*/
54-
public class PointLightShadowFilter extends AbstractShadowFilter<PointLightShadowRenderer> {
59+
public class PointLightShadowRenderer extends AbstractShadowRenderer {
60+
61+
/**
62+
* The fixed number of cameras used for rendering point light shadows (6 for a cube map).
63+
*/
64+
public static final int CAM_NUMBER = 6;
65+
66+
protected PointLight light;
67+
protected Camera[] shadowCams;
68+
protected Geometry[] frustums = null;
69+
protected final Vector3f X_NEG = Vector3f.UNIT_X.mult(-1f);
70+
protected final Vector3f Y_NEG = Vector3f.UNIT_Y.mult(-1f);
71+
protected final Vector3f Z_NEG = Vector3f.UNIT_Z.mult(-1f);
5572

5673
/**
5774
* For serialization only. Do not use.
58-
*
59-
* @see #PointLightShadowFilter(AssetManager assetManager, int shadowMapSize)
6075
*/
61-
protected PointLightShadowFilter() {
76+
protected PointLightShadowRenderer() {
6277
super();
6378
}
6479

6580
/**
66-
* Creates a PointLightShadowFilter.
81+
* Creates a new {@code PointLightShadowRenderer} instance.
6782
*
68-
* @param assetManager the application's asset manager
69-
* @param shadowMapSize the size of the rendered shadow maps (512, 1024, 2048, etc...)
83+
* @param assetManager The application's asset manager.
84+
* @param shadowMapSize The size of the rendered shadow maps (e.g., 512, 1024, 2048).
85+
* Higher values produce better quality shadows but may impact performance.
7086
*/
71-
public PointLightShadowFilter(AssetManager assetManager, int shadowMapSize) {
72-
super(assetManager, shadowMapSize, new PointLightShadowRenderer(assetManager, shadowMapSize));
87+
public PointLightShadowRenderer(AssetManager assetManager, int shadowMapSize) {
88+
super(assetManager, shadowMapSize, CAM_NUMBER);
89+
init(shadowMapSize);
90+
}
91+
92+
private void init(int shadowMapSize) {
93+
shadowCams = new Camera[CAM_NUMBER];
94+
for (int i = 0; i < shadowCams.length; i++) {
95+
shadowCams[i] = new Camera(shadowMapSize, shadowMapSize);
96+
}
97+
}
98+
99+
@Override
100+
protected void initFrustumCam() {
101+
Camera viewCam = viewPort.getCamera();
102+
frustumCam = viewCam.clone();
103+
frustumCam.setFrustum(viewCam.getFrustumNear(), zFarOverride,
104+
viewCam.getFrustumLeft(), viewCam.getFrustumRight(), viewCam.getFrustumTop(), viewCam.getFrustumBottom());
105+
}
106+
107+
@Override
108+
protected void updateShadowCams(Camera viewCam) {
109+
110+
if (light == null) {
111+
logger.warning("The light can't be null for a " + getClass().getName());
112+
return;
113+
}
114+
115+
// Configure axes for each of the six cube map cameras (positive/negative X, Y, Z)
116+
shadowCams[0].setAxes(X_NEG, Z_NEG, Y_NEG); // -Y (bottom)
117+
shadowCams[1].setAxes(X_NEG, Vector3f.UNIT_Z, Vector3f.UNIT_Y); // +Y (top)
118+
shadowCams[2].setAxes(X_NEG, Vector3f.UNIT_Y, Z_NEG); // +Z (forward)
119+
shadowCams[3].setAxes(Vector3f.UNIT_X, Vector3f.UNIT_Y, Vector3f.UNIT_Z); // -Z (backward)
120+
shadowCams[4].setAxes(Vector3f.UNIT_Z, Vector3f.UNIT_Y, X_NEG); // -X (left)
121+
shadowCams[5].setAxes(Z_NEG, Vector3f.UNIT_Y, Vector3f.UNIT_X); // +X (right)
122+
123+
// Set perspective and location for all shadow cameras
124+
for (Camera shadowCam : shadowCams) {
125+
shadowCam.setFrustumPerspective(90f, 1f, 0.1f, light.getRadius());
126+
shadowCam.setLocation(light.getPosition());
127+
shadowCam.update();
128+
shadowCam.updateViewProjection();
129+
}
130+
}
131+
132+
@Override
133+
protected GeometryList getOccludersToRender(int shadowMapIndex, GeometryList shadowMapOccluders) {
134+
for (Spatial scene : viewPort.getScenes()) {
135+
ShadowUtil.getGeometriesInCamFrustum(scene, shadowCams[shadowMapIndex], RenderQueue.ShadowMode.Cast, shadowMapOccluders);
136+
}
137+
return shadowMapOccluders;
138+
}
139+
140+
@Override
141+
protected void getReceivers(GeometryList lightReceivers) {
142+
lightReceivers.clear();
143+
for (Spatial scene : viewPort.getScenes()) {
144+
ShadowUtil.getLitGeometriesInViewPort(scene, viewPort.getCamera(), shadowCams, RenderQueue.ShadowMode.Receive, lightReceivers);
145+
}
146+
}
147+
148+
@Override
149+
protected Camera getShadowCam(int shadowMapIndex) {
150+
return shadowCams[shadowMapIndex];
73151
}
74152

153+
@Override
154+
protected void doDisplayFrustumDebug(int shadowMapIndex) {
155+
if (frustums == null) {
156+
frustums = new Geometry[CAM_NUMBER];
157+
Vector3f[] points = new Vector3f[8];
158+
for (int i = 0; i < points.length; i++) {
159+
points[i] = new Vector3f();
160+
}
161+
for (int i = 0; i < CAM_NUMBER; i++) {
162+
ShadowUtil.updateFrustumPoints2(shadowCams[i], points);
163+
frustums[i] = createFrustum(points, i);
164+
}
165+
}
166+
Geometry geo = frustums[shadowMapIndex];
167+
if (geo.getParent() == null) {
168+
getMainScene().attachChild(geo);
169+
}
170+
}
171+
172+
@Override
173+
protected void setMaterialParameters(Material material) {
174+
material.setVector3("LightPos", light == null ? new Vector3f() : light.getPosition());
175+
}
176+
177+
@Override
178+
protected void clearMaterialParameters(Material material) {
179+
material.clearParam("LightPos");
180+
}
181+
75182
/**
76-
* Returns the light used to cast shadows.
183+
* gets the point light used to cast shadows with this processor
77184
*
78-
* @return the PointLight
185+
* @return the point light
79186
*/
80187
public PointLight getLight() {
81-
return shadowRenderer.getLight();
188+
return light;
82189
}
83190

84191
/**
85-
* Sets the light to use to cast shadows.
192+
* sets the light to use for casting shadows with this processor
86193
*
87-
* @param light the PointLight
194+
* @param light the point light
88195
*/
89196
public void setLight(PointLight light) {
90-
shadowRenderer.setLight(light);
197+
this.light = light;
91198
}
92199

93200
@Override
94-
public void write(JmeExporter ex) throws IOException {
95-
super.write(ex);
96-
OutputCapsule oc = ex.getCapsule(this);
97-
oc.write(shadowRenderer, "shadowRenderer", null);
201+
public void cloneFields(final Cloner cloner, final Object original) {
202+
light = cloner.clone(light);
203+
init((int) shadowMapSize);
204+
frustums = null;
205+
super.cloneFields(cloner, original);
98206
}
99207

100208
@Override
101209
public void read(JmeImporter im) throws IOException {
102210
super.read(im);
103211
InputCapsule ic = im.getCapsule(this);
104-
shadowRenderer = (PointLightShadowRenderer) ic.readSavable("shadowRenderer", null);
212+
light = (PointLight) ic.readSavable("light", null);
213+
init((int) shadowMapSize);
214+
}
215+
216+
@Override
217+
public void write(JmeExporter ex) throws IOException {
218+
super.write(ex);
219+
OutputCapsule oc = ex.getCapsule(this);
220+
oc.write(light, "light", null);
105221
}
106222

223+
/**
224+
*
225+
* @param viewCam a Camera to define the view frustum
226+
* @return true if intersects
227+
*/
228+
@Override
229+
protected boolean checkCulling(Camera viewCam) {
230+
231+
if (light == null) {
232+
return false;
233+
}
234+
235+
Camera cam = viewCam;
236+
if (frustumCam != null) {
237+
cam = frustumCam;
238+
cam.setLocation(viewCam.getLocation());
239+
cam.setRotation(viewCam.getRotation());
240+
}
241+
TempVars vars = TempVars.get();
242+
boolean intersects = light.intersectsFrustum(cam, vars);
243+
vars.release();
244+
return intersects;
245+
}
107246
}

0 commit comments

Comments
 (0)