* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS\r
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\r
*/\r
-\r
package com.jme3.shadow;\r
\r
import com.jme3.material.Material;\r
import com.jme3.texture.Texture2D;\r
import com.jme3.ui.Picture;\r
\r
+/**\r
+ * BasicShadowRenderer uses standard shadow mapping with one map\r
+ * it's useful to render shadows in a small scene, but edges might look a bit jagged.\r
+ * \r
+ * @author Kirill Vainer\r
+ */\r
public class BasicShadowRenderer implements SceneProcessor {\r
\r
private RenderManager renderManager;\r
private ViewPort viewPort;\r
-\r
private FrameBuffer shadowFB;\r
private Texture2D shadowMap;\r
private Camera shadowCam;\r
-\r
private Material preshadowMat;\r
private Material postshadowMat;\r
-\r
private Picture dispPic = new Picture("Picture");\r
private boolean noOccluders = false;\r
-\r
private Vector3f[] points = new Vector3f[8];\r
private Vector3f direction = new Vector3f();\r
\r
- public BasicShadowRenderer(AssetManager manager, int size){\r
- shadowFB = new FrameBuffer(size,size,1);\r
- shadowMap = new Texture2D(size,size,Format.Depth);\r
+ /**\r
+ * Creates a BasicShadowRenderer\r
+ * @param manager the asset manager\r
+ * @param size the size of the shadow map (the map is square)\r
+ */\r
+ public BasicShadowRenderer(AssetManager manager, int size) {\r
+ shadowFB = new FrameBuffer(size, size, 1);\r
+ shadowMap = new Texture2D(size, size, Format.Depth);\r
shadowFB.setDepthTexture(shadowMap);\r
- shadowCam = new Camera(size,size);\r
- \r
- preshadowMat = new Material(manager, "Common/MatDefs/Shadow/PreShadow.j3md");\r
+ shadowCam = new Camera(size, size);\r
+\r
+ preshadowMat = new Material(manager, "Common/MatDefs/Shadow/PreShadow.j3md");\r
postshadowMat = new Material(manager, "Common/MatDefs/Shadow/PostShadow.j3md");\r
postshadowMat.setTexture("ShadowMap", shadowMap);\r
\r
dispPic.setTexture(manager, shadowMap, false);\r
\r
- for (int i = 0; i < points.length; i++){\r
+ for (int i = 0; i < points.length; i++) {\r
points[i] = new Vector3f();\r
}\r
}\r
\r
- public void initialize(RenderManager rm, ViewPort vp){\r
+ public void initialize(RenderManager rm, ViewPort vp) {\r
renderManager = rm;\r
viewPort = vp;\r
\r
reshape(vp, vp.getCamera().getWidth(), vp.getCamera().getHeight());\r
}\r
\r
- public boolean isInitialized(){\r
+ public boolean isInitialized() {\r
return viewPort != null;\r
}\r
\r
+ /**\r
+ * returns the light direction used for this processor\r
+ * @return \r
+ */\r
public Vector3f getDirection() {\r
return direction;\r
}\r
\r
+ /**\r
+ * sets the light direction to use to computs shadows\r
+ * @param direction \r
+ */\r
public void setDirection(Vector3f direction) {\r
this.direction.set(direction).normalizeLocal();\r
}\r
\r
+ /**\r
+ * debug only\r
+ * @return \r
+ */\r
public Vector3f[] getPoints() {\r
return points;\r
}\r
\r
- public Camera getShadowCamera(){\r
+ /**\r
+ * debug only\r
+ * returns the shadow camera \r
+ * @return \r
+ */\r
+ public Camera getShadowCamera() {\r
return shadowCam;\r
}\r
\r
- public void postQueue(RenderQueue rq){\r
+ public void postQueue(RenderQueue rq) {\r
GeometryList occluders = rq.getShadowQueueContent(ShadowMode.Cast);\r
- if (occluders.size() == 0){\r
+ if (occluders.size() == 0) {\r
noOccluders = true;\r
return;\r
- }else{\r
+ } else {\r
noOccluders = false;\r
}\r
\r
// update frustum points based on current camera\r
Camera viewCam = viewPort.getCamera();\r
ShadowUtil.updateFrustumPoints(viewCam,\r
- viewCam.getFrustumNear(),\r
- viewCam.getFrustumFar(), \r
- 1.0f,\r
- points);\r
+ viewCam.getFrustumNear(),\r
+ viewCam.getFrustumFar(),\r
+ 1.0f,\r
+ points);\r
\r
Vector3f frustaCenter = new Vector3f();\r
- for (Vector3f point : points){\r
+ for (Vector3f point : points) {\r
frustaCenter.addLocal(point);\r
}\r
frustaCenter.multLocal(1f / 8f);\r
shadowCam.setProjectionMatrix(null);\r
shadowCam.setParallelProjection(true);\r
// shadowCam.setFrustumPerspective(45, 1, 1, 20);\r
- \r
+\r
shadowCam.lookAtDirection(direction, Vector3f.UNIT_Y);\r
shadowCam.update();\r
shadowCam.setLocation(frustaCenter);\r
renderManager.setForcedMaterial(preshadowMat);\r
\r
r.setFrameBuffer(shadowFB);\r
- r.clearBuffers(false,true,false);\r
+ r.clearBuffers(false, true, false);\r
viewPort.getQueue().renderShadowQueue(ShadowMode.Cast, renderManager, shadowCam, true);\r
r.setFrameBuffer(viewPort.getOutputFrameBuffer());\r
\r
renderManager.setCamera(viewCam, false);\r
}\r
\r
- public Picture getDisplayPicture(){\r
+ /**\r
+ * debug only\r
+ * @return \r
+ */\r
+ public Picture getDisplayPicture() {\r
return dispPic;\r
}\r
\r
- public void postFrame(FrameBuffer out){\r
- if (!noOccluders){\r
+ public void postFrame(FrameBuffer out) {\r
+ if (!noOccluders) {\r
postshadowMat.setMatrix4("LightViewProjectionMatrix", shadowCam.getViewProjectionMatrix());\r
renderManager.setForcedMaterial(postshadowMat);\r
viewPort.getQueue().renderShadowQueue(ShadowMode.Receive, renderManager, viewPort.getCamera(), true);\r
dispPic.setWidth(w / 5f);\r
dispPic.setHeight(h / 5f);\r
}\r
-\r
}\r
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
-
package com.jme3.shadow;
import com.jme3.material.Material;
import com.jme3.texture.Texture2D;
import com.jme3.ui.Picture;
+/**
+ * PssmShadow renderer use Parrallel Split Shadow Mapping technique (pssm)<br>
+ * It splits the view frustum in several parts and compute a shadow map for each one.<br>
+ * 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.<br>
+ * This result in a better quality shadow than standard shadow mapping.<br>
+ * for more informations on this read this http://http.developer.nvidia.com/GPUGems3/gpugems3_ch10.html<br>
+ *
+ * @author Rémy Bouquet aka Nehon
+ */
public class PssmShadowRenderer implements SceneProcessor {
/**
* <code>FilterMode</code> specifies how shadows are filtered
*/
public enum FilterMode {
+
/**
* Shadows are not filtered. Nearest sample is used, causing in blocky
* shadows.
*/
Nearest,
-
/**
* Bilinear filtering is used. Has the potential of being hardware
* accelerated on some GPUs
*/
Bilinear,
-
/**
* Dither-based sampling is used, very cheap but can look bad
* at low resolutions.
*/
Dither,
-
/**
* 4x4 percentage-closer filtering is used. Shadows will be smoother
* at the cost of performance
*/
PCF4,
-
/**
* 8x8 percentage-closer filtering is used. Shadows will be smoother
* at the cost of performance
PCF8
}
+ /**
+ * Specifies the shadow comparison mode
+ */
public enum CompareMode {
+
/**
* Shadow depth comparisons are done by using shader code
*/
Software,
-
/**
* Shadow depth comparisons are done by using the GPU's dedicated
* shadowing pipeline.
*/
Hardware;
}
-
private int nbSplits = 3;
private float lambda = 0.65f;
private float shadowIntensity = 0.7f;
private Camera shadowCam;
private Material preshadowMat;
private Material postshadowMat;
-
private GeometryList splitOccluders = new GeometryList(new OpaqueComparator());
private Matrix4f[] lightViewProjectionsMatrices;
private ColorRGBA splits;
private float[] splitsArray;
private boolean noOccluders = false;
-
private Vector3f direction = new Vector3f();
private AssetManager assetManager;
private boolean debug = false;
private float edgesThickness = 1.0f;
-
private FilterMode filterMode;
private CompareMode compareMode;
-
private Picture[] dispPic;
private Vector3f[] points = new Vector3f[8];
dispPic = new Picture[nbSplits];
lightViewProjectionsMatrices = new Matrix4f[nbSplits];
splits = new ColorRGBA();
- splitsArray = new float[nbSplits+1];
+ splitsArray = new float[nbSplits + 1];
//DO NOT COMMENT THIS (it prevent the OSX incomplete read buffer crash)
- dummyTex= new Texture2D(size, size, Format.RGBA8);
+ dummyTex = new Texture2D(size, size, Format.RGBA8);
- preshadowMat = new Material(manager, "Common/MatDefs/Shadow/PreShadow.j3md");
+ preshadowMat = new Material(manager, "Common/MatDefs/Shadow/PreShadow.j3md");
postshadowMat = new Material(manager, "Common/MatDefs/Shadow/PostShadowPSSM.j3md");
for (int i = 0; i < nbSplits; i++) {
lightViewProjectionsMatrices[i] = new Matrix4f();
shadowFB[i] = new FrameBuffer(size, size, 1);
shadowMaps[i] = new Texture2D(size, size, Format.Depth);
-
+
shadowFB[i].setDepthTexture(shadowMaps[i]);
//DO NOT COMMENT THIS (it prevent the OSX incomplete read buffer crash)
shadowFB[i].setColorTexture(dummyTex);
postshadowMat.setTexture("ShadowMap" + i, shadowMaps[i]);
-
+
//quads for debuging purpose
dispPic[i] = new Picture("Picture" + i);
dispPic[i].setTexture(manager, shadowMaps[i], false);
}
}
- public void setFilterMode(FilterMode filterMode){
- if (filterMode == null)
+ /**
+ * Sets the filtering mode for shadow edges see {@link FilterMode} for more info
+ * @param filterMode
+ */
+ public void setFilterMode(FilterMode filterMode) {
+ if (filterMode == null) {
throw new NullPointerException();
+ }
- if (this.filterMode == filterMode)
+ if (this.filterMode == filterMode) {
return;
+ }
this.filterMode = filterMode;
postshadowMat.setInt("FilterMode", filterMode.ordinal());
postshadowMat.setFloat("PCFEdge", edgesThickness);
- if (compareMode == CompareMode.Hardware){
- for (Texture2D shadowMap : shadowMaps){
- if (filterMode == FilterMode.Bilinear){
+ if (compareMode == CompareMode.Hardware) {
+ for (Texture2D shadowMap : shadowMaps) {
+ if (filterMode == FilterMode.Bilinear) {
shadowMap.setMagFilter(MagFilter.Bilinear);
shadowMap.setMinFilter(MinFilter.BilinearNoMipMaps);
- }else{
+ } else {
shadowMap.setMagFilter(MagFilter.Nearest);
shadowMap.setMinFilter(MinFilter.NearestNoMipMaps);
}
}
}
+ /**
+ * sets the shadow compare mode see {@link CompareMode} for more info
+ * @param compareMode
+ */
public void setCompareMode(CompareMode compareMode) {
- if (compareMode == null)
+ if (compareMode == null) {
throw new NullPointerException();
+ }
- if (this.compareMode == compareMode)
+ if (this.compareMode == compareMode) {
return;
+ }
this.compareMode = compareMode;
- for (Texture2D shadowMap : shadowMaps){
- if (compareMode == CompareMode.Hardware){
+ for (Texture2D shadowMap : shadowMaps) {
+ if (compareMode == CompareMode.Hardware) {
shadowMap.setShadowCompareMode(ShadowCompareMode.LessOrEqual);
- if (filterMode == FilterMode.Bilinear){
+ if (filterMode == FilterMode.Bilinear) {
shadowMap.setMagFilter(MagFilter.Bilinear);
shadowMap.setMinFilter(MinFilter.BilinearNoMipMaps);
- }else{
+ } else {
shadowMap.setMagFilter(MagFilter.Nearest);
shadowMap.setMinFilter(MinFilter.NearestNoMipMaps);
}
- } else{
+ } else {
shadowMap.setShadowCompareMode(ShadowCompareMode.Off);
shadowMap.setMagFilter(MagFilter.Nearest);
shadowMap.setMinFilter(MinFilter.NearestNoMipMaps);
return viewPort != null;
}
+ /**
+ * returns the light direction used by the processor
+ * @return
+ */
public Vector3f getDirection() {
return direction;
}
+ /**
+ * Sets the light direction to use to compute shadows
+ * @param direction
+ */
public void setDirection(Vector3f direction) {
this.direction.set(direction).normalizeLocal();
}
- @SuppressWarnings("fallthrough")
+ @SuppressWarnings("fallthrough")
public void postQueue(RenderQueue rq) {
- GeometryList occluders = rq.getShadowQueueContent(ShadowMode.Cast);
- if (occluders.size() == 0)
+ GeometryList occluders = rq.getShadowQueueContent(ShadowMode.Cast);
+ if (occluders.size() == 0) {
return;
+ }
GeometryList receivers = rq.getShadowQueueContent(ShadowMode.Receive);
- if (receivers.size() == 0)
+ if (receivers.size() == 0) {
return;
-
+ }
+
Camera viewCam = viewPort.getCamera();
float zFar = zFarOverride;
if (zFar == 0) {
- zFar=viewCam.getFrustumFar();
- // zFar = PssmShadowUtil.computeZFar(occluders, receivers, viewCam);
+ zFar = viewCam.getFrustumFar();
+ // zFar = PssmShadowUtil.computeZFar(occluders, receivers, viewCam);
}
// System.out.println("Zfar : "+zFar);
ShadowUtil.updateFrustumPoints(viewCam, viewCam.getFrustumNear(), zFar, 1.0f, points);
PssmShadowUtil.updateFrustumSplits(splitsArray, viewCam.getFrustumNear(), zFar, lambda);
- switch (splitsArray.length){
+ switch (splitsArray.length) {
case 5:
splits.a = splitsArray[4];
case 4:
ShadowUtil.updateFrustumPoints(viewCam, splitsArray[i], splitsArray[i + 1], 1.0f, points);
//Updating shadow cam with curent split frustra
- // if(cropShadows){
- ShadowUtil.updateShadowCamera(occluders, receivers, shadowCam, points, splitOccluders);
-// }else{
-// ShadowUtil.updateShadowCamera(shadowCam, points);
-// }
- //displaying the current splitted frustrum and the associated cropped light frustrums in wireframe.
- //only for debuging purpose
-// if (debug) {
-// viewPort.attachScene(createFrustum(points, i));
-// Vector3f[] pts = new Vector3f[8];
-// for (int j = 0; j < pts.length; j++) {
-// pts[j] = new Vector3f();
-// }
-// ShadowUtil.updateFrustumPoints2(shadowCam, pts);
-// viewPort.attachScene(createFrustum(pts, i));
-// if (i == nbSplits-1) {
-// debug = false;
-// }
-// }
+ ShadowUtil.updateShadowCamera(occluders, receivers, shadowCam, points, splitOccluders);
//saving light view projection matrix for this split
lightViewProjectionsMatrices[i] = shadowCam.getViewProjectionMatrix().clone();
r.clearBuffers(false, true, false);
// render shadow casters to shadow map
- viewPort.getQueue().renderShadowQueue(splitOccluders, renderManager, shadowCam, true);
- //viewPort.getQueue().renderShadowQueue(ShadowMode.Cast, renderManager, shadowCam, i == nbSplits - 1);
-
+ viewPort.getQueue().renderShadowQueue(splitOccluders, renderManager, shadowCam, true);
}
occluders.clear();
//restore setting for future rendering
renderManager.setForcedMaterial(null);
renderManager.setForcedTechnique(null);
renderManager.setCamera(viewCam, false);
-
+
}
//debug only : displays depth shadow maps
- public void displayShadowMap(Renderer r) {
+ private void displayShadowMap(Renderer r) {
Camera cam = viewPort.getCamera();
renderManager.setCamera(cam, true);
int h = cam.getHeight();
renderManager.setCamera(cam, false);
}
- if (debug)
+ if (debug) {
displayShadowMap(renderManager.getRenderer());
+ }
}
public void preFrame(float tpf) {
public void reshape(ViewPort vp, int w, int h) {
}
+ /**
+ * returns the labda parameter<br>
+ * see {@link setLambda(float lambda)}
+ * @return
+ */
public float getLambda() {
return lambda;
}
this.lambda = lambda;
}
+ /**
+ * How far the shadows are rendered in the view
+ * see {@link setShadowZExtend(float zFar)}
+ * @return
+ */
public float getShadowZExtend() {
return zFarOverride;
}
this.zFarOverride = zFar;
}
+ /**
+ * returns the shdaow intensity<br>
+ * see {@link setShadowIntensity(float shadowIntensity)}
+ * @return
+ */
public float getShadowIntensity() {
return shadowIntensity;
}
postshadowMat.setFloat("ShadowIntensity", shadowIntensity);
}
+ /**
+ * returns the edges thickness <br>
+ * see {@link setEdgesThickness(int edgesThickness)}
+ * @return
+ */
public int getEdgesThickness() {
return (int) (edgesThickness * 10);
}
+ /**
+ * Stes the shadow edges thickness. default is 1, setting it to lower values can help to reduce the jagged effect of the shadow edges
+ * @param edgesThickness
+ */
public void setEdgesThickness(int edgesThickness) {
- this.edgesThickness = Math.max(1, Math.min(edgesThickness,10));
+ this.edgesThickness = Math.max(1, Math.min(edgesThickness, 10));
this.edgesThickness *= 0.1f;
postshadowMat.setFloat("PCFEdge", edgesThickness);
}
}
-
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
-
package com.jme3.shadow;
import com.jme3.bounding.BoundingBox;
*/
public final class PssmShadowUtil {
- public static void main(String[] args){
- float[] splits = new float[5];
- float[] splitsShader = new float[3];
- updateFrustumSplits(splits, 1, 1000, 0.5f);
- System.arraycopy(splits, 1, splitsShader, 0, splitsShader.length);
- System.out.println(Arrays.toString(splitsShader));
-
- for (int i = 0; i < splits.length-1; i++){
- System.out.println(splits[i] + " - " + splits[i+1]);
- }
- }
-
/**
* Updates the frustum splits stores in <code>splits</code> using PSSM.
*/
*/
public static float computeZFar(GeometryList occ, GeometryList recv, Camera cam) {
Matrix4f mat = cam.getViewMatrix();
- BoundingBox bbOcc = ShadowUtil.computeUnionBound(occ, mat);
+ BoundingBox bbOcc = ShadowUtil.computeUnionBound(occ, mat);
BoundingBox bbRecv = ShadowUtil.computeUnionBound(recv, mat);
return min(max(bbOcc.getZExtent() - bbOcc.getCenter().z, bbRecv.getZExtent() - bbRecv.getCenter().z), cam.getFrustumFar());
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS\r
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\r
*/\r
-\r
package com.jme3.shadow;\r
\r
import com.jme3.light.DirectionalLight;\r
import com.jme3.math.Vector3f;\r
import com.jme3.renderer.Camera;\r
\r
+/**\r
+ * Creates a camera according to a light\r
+ * Handy to compute projection matrix of a light\r
+ * @author Kirill Vainer\r
+ */\r
public class ShadowCamera {\r
\r
private Vector3f[] points = new Vector3f[8];\r
private Light target;\r
\r
- public ShadowCamera(Light target){\r
+ public ShadowCamera(Light target) {\r
this.target = target;\r
- for (int i = 0; i < points.length; i++){\r
+ for (int i = 0; i < points.length; i++) {\r
points[i] = new Vector3f();\r
}\r
}\r
/**\r
* Updates the camera view direction and position based on the light\r
*/\r
- private void updateLightCamera(Camera lightCam){\r
- if (target.getType() == Light.Type.Directional){\r
+ public void updateLightCamera(Camera lightCam) {\r
+ if (target.getType() == Light.Type.Directional) {\r
DirectionalLight dl = (DirectionalLight) target;\r
lightCam.setParallelProjection(true);\r
lightCam.setLocation(Vector3f.ZERO);\r
- lightCam.lookAtDirection(dl.getDirection(), Vector3f.UNIT_Y );\r
+ lightCam.lookAtDirection(dl.getDirection(), Vector3f.UNIT_Y);\r
lightCam.setFrustum(-1, 1, -1, 1, 1, -1);\r
- }else{\r
+ } else {\r
PointLight pl = (PointLight) target;\r
lightCam.setParallelProjection(false);\r
lightCam.setLocation(pl.getPosition());\r
lightCam.setFrustumPerspective(45, 1, 1, 300);\r
}\r
}\r
-\r
}\r
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS\r
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\r
*/\r
-\r
package com.jme3.shadow;\r
\r
import com.jme3.bounding.BoundingBox;\r
import com.jme3.bounding.BoundingVolume;\r
-import com.jme3.math.FastMath;\r
import com.jme3.math.Matrix4f;\r
import com.jme3.math.Transform;\r
import com.jme3.math.Vector2f;\r
*/\r
public class ShadowUtil {\r
\r
- public static void updateFrustumPoints2(Camera viewCam, Vector3f[] points){\r
+ /**\r
+ * Updates a points arrays with the frustum corners of the provided camera.\r
+ * @param viewCam\r
+ * @param points \r
+ */\r
+ public static void updateFrustumPoints2(Camera viewCam, Vector3f[] points) {\r
int w = viewCam.getWidth();\r
int h = viewCam.getHeight();\r
float n = viewCam.getFrustumNear();\r
points[1].set(viewCam.getWorldCoordinates(new Vector2f(0, h), n));\r
points[2].set(viewCam.getWorldCoordinates(new Vector2f(w, h), n));\r
points[3].set(viewCam.getWorldCoordinates(new Vector2f(w, 0), n));\r
- \r
+\r
points[4].set(viewCam.getWorldCoordinates(new Vector2f(0, 0), f));\r
points[5].set(viewCam.getWorldCoordinates(new Vector2f(0, h), f));\r
points[6].set(viewCam.getWorldCoordinates(new Vector2f(w, h), f));\r
* @param farOverride\r
*/\r
public static void updateFrustumPoints(Camera viewCam,\r
- float nearOverride,\r
- float farOverride,\r
- float scale,\r
- Vector3f[] points) {\r
+ float nearOverride,\r
+ float farOverride,\r
+ float scale,\r
+ Vector3f[] points) {\r
\r
Vector3f pos = viewCam.getLocation();\r
Vector3f dir = viewCam.getDirection();\r
}\r
}\r
\r
+ /**\r
+ * Compute bounds of a geomList\r
+ * @param list\r
+ * @param transform\r
+ * @return \r
+ */\r
public static BoundingBox computeUnionBound(GeometryList list, Transform transform) {\r
BoundingBox bbox = new BoundingBox();\r
for (int i = 0; i < list.size(); i++) {\r
return bbox;\r
}\r
\r
+ /**\r
+ * Compute bounds of a geomList\r
+ * @param list\r
+ * @param mat\r
+ * @return \r
+ */\r
public static BoundingBox computeUnionBound(GeometryList list, Matrix4f mat) {\r
BoundingBox bbox = new BoundingBox();\r
BoundingVolume store = null;\r
return bbox;\r
}\r
\r
+ /**\r
+ * Computes the bounds of multiple bounding volumes\r
+ * @param bv\r
+ * @return \r
+ */\r
public static BoundingBox computeUnionBound(List<BoundingVolume> bv) {\r
BoundingBox bbox = new BoundingBox();\r
for (int i = 0; i < bv.size(); i++) {\r
return bbox;\r
}\r
\r
+ /**\r
+ * Compute bounds from an array of points\r
+ * @param pts\r
+ * @param transform\r
+ * @return \r
+ */\r
public static BoundingBox computeBoundForPoints(Vector3f[] pts, Transform transform) {\r
Vector3f min = new Vector3f(Vector3f.POSITIVE_INFINITY);\r
Vector3f max = new Vector3f(Vector3f.NEGATIVE_INFINITY);\r
return new BoundingBox(center, extent.x, extent.y, extent.z);\r
}\r
\r
+ /**\r
+ * Compute bounds from an array of points\r
+ * @param pts\r
+ * @param mat\r
+ * @return \r
+ */\r
public static BoundingBox computeBoundForPoints(Vector3f[] pts, Matrix4f mat) {\r
Vector3f min = new Vector3f(Vector3f.POSITIVE_INFINITY);\r
Vector3f max = new Vector3f(Vector3f.NEGATIVE_INFINITY);\r
max.maxLocal(temp);\r
}\r
\r
-// min.x = FastMath.clamp(min.x, -1f, 1f);\r
-// max.y = FastMath.clamp(max.y, -1f, 1f);\r
-// min.x = FastMath.clamp(min.x, -1f, 1f);\r
-// max.y = FastMath.clamp(max.y, -1f, 1f);\r
-\r
Vector3f center = min.add(max).multLocal(0.5f);\r
Vector3f extent = max.subtract(min).multLocal(0.5f);\r
//Nehon 08/18/2010 : Added an offset to the extend to avoid banding artifacts when the frustum are aligned\r
- return new BoundingBox(center, extent.x + 2.0f, extent.y + 2.0f, extent.z +2.5f);\r
- //return new BoundingBox(center, extent.x, extent.y, extent.z);\r
+ return new BoundingBox(center, extent.x + 2.0f, extent.y + 2.0f, extent.z + 2.5f);\r
}\r
\r
/**\r
* @param lightCam\r
* @param points\r
*/\r
- public static void updateShadowCamera(Camera shadowCam, Vector3f[] points){\r
+ public static void updateShadowCamera(Camera shadowCam, Vector3f[] points) {\r
boolean ortho = shadowCam.isParallelProjection();\r
shadowCam.setProjectionMatrix(null);\r
\r
\r
Vector3f splitMin = splitBB.getMin(null);\r
Vector3f splitMax = splitBB.getMax(null);\r
- \r
+\r
// splitMin.z = 0;\r
\r
// Create the crop matrix.\r
offsetZ = -splitMin.z * scaleZ;\r
\r
Matrix4f cropMatrix = new Matrix4f(scaleX, 0f, 0f, offsetX,\r
- 0f, scaleY, 0f, offsetY,\r
- 0f, 0f, scaleZ, offsetZ,\r
- 0f, 0f, 0f, 1f);\r
+ 0f, scaleY, 0f, offsetY,\r
+ 0f, 0f, scaleZ, offsetZ,\r
+ 0f, 0f, 0f, 1f);\r
\r
\r
Matrix4f result = new Matrix4f();\r
* @param points\r
*/\r
public static void updateShadowCamera(GeometryList occluders,\r
- GeometryList receivers,\r
- Camera shadowCam,\r
- Vector3f[] points){\r
+ GeometryList receivers,\r
+ Camera shadowCam,\r
+ Vector3f[] points) {\r
updateShadowCamera(occluders, receivers, shadowCam, points, null);\r
}\r
\r
* @param points\r
*/\r
public static void updateShadowCamera(GeometryList occluders,\r
- GeometryList receivers,\r
- Camera shadowCam,\r
- Vector3f[] points,\r
- GeometryList splitOccluders){\r
+ GeometryList receivers,\r
+ Camera shadowCam,\r
+ Vector3f[] points,\r
+ GeometryList splitOccluders) {\r
\r
boolean ortho = shadowCam.isParallelProjection();\r
\r
shadowCam.setProjectionMatrix(null);\r
\r
- if (ortho){\r
+ if (ortho) {\r
shadowCam.setFrustum(-1, 1, -1, 1, 1, -1);\r
- }else{\r
+ } else {\r
shadowCam.setFrustumPerspective(45, 1, 1, 150);\r
}\r
\r
- // create transform to rotate points to viewspace\r
- //Transform t = new Transform(shadowCam.getRotation());\r
+ // create transform to rotate points to viewspace \r
Matrix4f viewProjMatrix = shadowCam.getViewProjectionMatrix();\r
\r
-// BoundingBox casterBB = computeUnionBound(occluders, viewProjMatrix);\r
-// BoundingBox receiverBB = computeUnionBound(receivers, viewProjMatrix);\r
- BoundingBox splitBB = computeBoundForPoints(points, viewProjMatrix);\r
+ BoundingBox splitBB = computeBoundForPoints(points, viewProjMatrix);\r
\r
ArrayList<BoundingVolume> visRecvList = new ArrayList<BoundingVolume>();\r
- for (int i = 0; i < receivers.size(); i++){\r
+ for (int i = 0; i < receivers.size(); i++) {\r
// convert bounding box to light's viewproj space\r
Geometry receiver = receivers.get(i);\r
BoundingVolume bv = receiver.getWorldBound();\r
BoundingVolume recvBox = bv.transform(viewProjMatrix, null);\r
- \r
- if (splitBB.intersects(recvBox)){\r
+\r
+ if (splitBB.intersects(recvBox)) {\r
visRecvList.add(recvBox);\r
}\r
}\r
- \r
+\r
ArrayList<BoundingVolume> visOccList = new ArrayList<BoundingVolume>();\r
- for (int i = 0; i < occluders.size(); i++){\r
+ for (int i = 0; i < occluders.size(); i++) {\r
// convert bounding box to light's viewproj space\r
Geometry occluder = occluders.get(i);\r
BoundingVolume bv = occluder.getWorldBound();\r
BoundingVolume occBox = bv.transform(viewProjMatrix, null);\r
\r
boolean intersects = splitBB.intersects(occBox);\r
- if (!intersects && occBox instanceof BoundingBox){\r
- BoundingBox occBB = (BoundingBox)occBox;\r
+ if (!intersects && occBox instanceof BoundingBox) {\r
+ BoundingBox occBB = (BoundingBox) occBox;\r
//Kirill 01/10/2011\r
// Extend the occluder further into the frustum\r
// This fixes shadow dissapearing issues when\r
// The number is in world units\r
occBB.setZExtent(occBB.getZExtent() + 50);\r
occBB.setCenter(occBB.getCenter().addLocal(0, 0, 25));\r
- if (splitBB.intersects(occBB)){\r
+ if (splitBB.intersects(occBB)) {\r
// To prevent extending the depth range too much\r
// We return the bound to its former shape\r
// Before adding it\r
occBB.setZExtent(occBB.getZExtent() - 50);\r
occBB.setCenter(occBB.getCenter().subtractLocal(0, 0, 25));\r
visOccList.add(occBox);\r
- if(splitOccluders != null){\r
+ if (splitOccluders != null) {\r
splitOccluders.add(occluder);\r
}\r
}\r
- }else if (intersects){\r
+ } else if (intersects) {\r
visOccList.add(occBox);\r
- if(splitOccluders != null){\r
+ if (splitOccluders != null) {\r
splitOccluders.add(occluder);\r
}\r
}\r
}\r
\r
- BoundingBox casterBB = computeUnionBound(visOccList);\r
+ BoundingBox casterBB = computeUnionBound(visOccList);\r
BoundingBox receiverBB = computeUnionBound(visRecvList);\r
\r
//Nehon 08/18/2010 this is to avoid shadow bleeding when the ground is set to only receive shadows\r
Vector3f splitMin = splitBB.getMin(null);\r
Vector3f splitMax = splitBB.getMax(null);\r
\r
-// actualMin.x = FastMath.clamp(actualMin.x, -1, 1);\r
-// actualMin.y = FastMath.clamp(actualMin.y, -1, 1);\r
-// actualMax.x = FastMath.clamp(actualMax.x, -1, 1);\r
-// actualMax.y = FastMath.clamp(actualMax.y, -1, 1);\r
-// float far = actualMin.z + actualMax.z * 4 + 1.0f + 1.5f;\r
splitMin.z = 0;\r
\r
- if (!ortho)\r
+ if (!ortho) {\r
shadowCam.setFrustumPerspective(45, 1, 1, splitMax.z);\r
- \r
+ }\r
+\r
Matrix4f projMatrix = shadowCam.getProjectionMatrix();\r
\r
Vector3f cropMin = new Vector3f();\r
cropMin.z = min(casterMin.z, splitMin.z);\r
cropMax.z = min(receiverMax.z, splitMax.z);\r
\r
-// cropMin.set(splitMin);\r
-// cropMax.set(splitMax);\r
-\r
-// cropMin.z = Math.min(cropMin.z, cropMax.z - cropMin.z - 1000);\r
-// cropMin.z = Math.max(10f, cropMin.z);\r
\r
// Create the crop matrix.\r
float scaleX, scaleY, scaleZ;\r
\r
scaleX = (2.0f) / (cropMax.x - cropMin.x);\r
scaleY = (2.0f) / (cropMax.y - cropMin.y);\r
- \r
+\r
offsetX = -0.5f * (cropMax.x + cropMin.x) * scaleX;\r
offsetY = -0.5f * (cropMax.y + cropMin.y) * scaleY;\r
\r
scaleZ = 1.0f / (cropMax.z - cropMin.z);\r
offsetZ = -cropMin.z * scaleZ;\r
\r
-// scaleZ = 2.0f / (cropMax.z - cropMin.z);\r
-// offsetZ = -0.5f * (cropMax.z + cropMin.z) * scaleZ;\r
\r
- Matrix4f cropMatrix = new Matrix4f(scaleX, 0f, 0f, offsetX,\r
- 0f, scaleY, 0f, offsetY,\r
- 0f, 0f, scaleZ, offsetZ,\r
- 0f, 0f, 0f, 1f);\r
\r
-// cropMatrix.transposeLocal();\r
-// Matrix4f cropMatrix = new Matrix4f();\r
-// cropMatrix.setScale(new Vector3f(scaleX, scaleY, 1f));\r
-// cropMatrix.setTranslation(offsetX, offsetY, 0);\r
+ Matrix4f cropMatrix = new Matrix4f(scaleX, 0f, 0f, offsetX,\r
+ 0f, scaleY, 0f, offsetY,\r
+ 0f, 0f, scaleZ, offsetZ,\r
+ 0f, 0f, 0f, 1f);\r
+\r
\r
Matrix4f result = new Matrix4f();\r
result.set(cropMatrix);\r
result.multLocal(projMatrix);\r
-// result.set(projMatrix);\r
-// result.multLocal(cropMatrix);\r
- shadowCam.setProjectionMatrix(result);\r
-\r
-// shadowCam.setFrustum(cropMin.z, cropMax.z, // near, far\r
-// cropMin.x, cropMax.x, // left, right\r
-// cropMax.y, cropMin.y); // top, bottom\r
\r
- // compute size and center of final frustum\r
- //float sizeX = (max.x - min.x) / 2f;\r
- //float sizeY = (max.y - min.y) / 2f;\r
- //float offsetX = (max.x + min.x) / -2f;\r
- //float offsetY = (max.y + min.y) / -2f;\r
-\r
- // compute center for frustum\r
- //temp.set(offsetX, offsetY, 0);\r
- //invRot.mult(temp, temp);\r
+ shadowCam.setProjectionMatrix(result);\r
\r
- //shadowCam.setLocation(temp);\r
- //shadowCam.setFrustum(min.z, max.z, -sizeX, sizeX, sizeY, -sizeY);\r
}\r
}\r