Efficiency Improvements

I’ll look into the shadowUtil thing, I don’t like the idea of having a static utility class made stateful because of temp vars. So some kind of pool will have to be used, maybe just have TempVars have a couple of BoundingBox instances…

@phr00t said: Good find. These solutions need to be worked into the engine quicker, so people like me don't need to solve the same problems again. I'll push my changes to the SVN, which are clean. Before anyone says "garbage collection is fast & this isn't a concern": it might not be for you, but apparently it is for many people:

Odd lag :: 3089 -- Futuristic Action RPG General Discussions

Just a note on this…

You’ve probably already done profiling to check your engine but I thought I’d mention that every time I’ve had “full GC” issues that affect performance and thought to blame the render loop, it always ended up coming back to something I was doing in my own code (or in adjunct libraries).

The temporary objects are generally reclaimed for free without requiring a full GC. So if full GC’s are being done every ‘x’ seconds then it’s a sign that harder-to-free garbage is building up.

That being said, keeping the engine as churn-free as possible is always a good thing.

Regarding the bounding volume changes, I’m not familiar with the algorithm. How many active bounding volumes are actually needed at once?

I’m wondering what the implications are of doing computeUnionBound() on the fly rather than creating a whole list of bounding volumes just to reduce them back to one again. I haven’t read the algorithm thoroughly so I don’t know exactly what I’m talking about.

…but the point is perhaps the first step is to reduce the need for so many throw-away bounding volume instances and then pick a cache strategy if still necessary.

Actually I think it could be reduced to one, but at the cost of some readability. So one Bounding box in tempVars would do the trick I guess.

I went ahead and fixed ShadowUtil using one BoundingBox in TempVars. This code tests fine in 3089, shadows work, no excessive memory usage, no static pool of BoundingBoxes etc. – also got rid of the ArrayList stuff, no need to make a list of BoundingVolumes if you process them as needed:

[java]/*

  • Copyright © 2009-2012 jMonkeyEngine
  • All rights reserved.
  • Redistribution and use in source and binary forms, with or without
  • modification, are permitted provided that the following conditions are
  • met:
    • Redistributions of source code must retain the above copyright
  • notice, this list of conditions and the following disclaimer.
    • Redistributions in binary form must reproduce the above copyright
  • notice, this list of conditions and the following disclaimer in the
  • documentation and/or other materials provided with the distribution.
    • Neither the name of ‘jMonkeyEngine’ nor the names of its contributors
  • may be used to endorse or promote products derived from this software
  • without specific prior written permission.
  • THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
  • “AS IS” AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
  • TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
  • PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
  • CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
  • EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
  • PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
  • PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
  • LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
  • 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;
import com.jme3.bounding.BoundingVolume;
import com.jme3.math.FastMath;
import com.jme3.math.Matrix4f;
import com.jme3.math.Transform;
import com.jme3.math.Vector2f;
import com.jme3.math.Vector3f;
import com.jme3.renderer.Camera;
import com.jme3.renderer.queue.GeometryList;
import com.jme3.scene.Geometry;
import com.jme3.util.TempVars;
import static java.lang.Math.max;
import static java.lang.Math.min;
import java.util.List;

/**

  • Includes various useful shadow mapping functions.
  • @see
    • <a
    • href=“http://appsrv.cse.cuhk.edu.hk/~fzhang/pssm_vrcia/”>http://appsrv.cse.cuhk.edu.hk/~fzhang/pssm_vrcia/
    • <a
    • href=“http://http.developer.nvidia.com/GPUGems3/gpugems3_ch10.html”>http://http.developer.nvidia.com/GPUGems3/gpugems3_ch10.html
    for more info.

*/
public class ShadowUtil {

/**
 * Updates a points arrays with the frustum corners of the provided camera.
 *
 * @param viewCam
 * @param points
 */
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));
}

/**
 * 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
 *
 * @param viewCam
 * @param nearOverride
 * @param farOverride
 */
public static void updateFrustumPoints(Camera viewCam,
        float nearOverride,
        float farOverride,
        float scale,
        Vector3f[] points) {

    Vector3f pos = viewCam.getLocation();
    Vector3f dir = viewCam.getDirection();
    Vector3f up = viewCam.getUp();

    float depthHeightRatio = viewCam.getFrustumTop() / viewCam.getFrustumNear();
    float near = nearOverride;
    float far = farOverride;
    float ftop = viewCam.getFrustumTop();
    float fright = viewCam.getFrustumRight();
    float ratio = fright / ftop;

    float near_height;
    float near_width;
    float far_height;
    float far_width;

    if (viewCam.isParallelProjection()) {
        near_height = ftop;
        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;
    }

    Vector3f right = dir.cross(up).normalizeLocal();

    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();

    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();

    points[0].set(nearCenter).subtractLocal(nearUp).subtractLocal(nearRight);
    points[1].set(nearCenter).addLocal(nearUp).subtractLocal(nearRight);
    points[2].set(nearCenter).addLocal(nearUp).addLocal(nearRight);
    points[3].set(nearCenter).subtractLocal(nearUp).addLocal(nearRight);

    points[4].set(farCenter).subtractLocal(farUp).subtractLocal(farRight);
    points[5].set(farCenter).addLocal(farUp).subtractLocal(farRight);
    points[6].set(farCenter).addLocal(farUp).addLocal(farRight);
    points[7].set(farCenter).subtractLocal(farUp).addLocal(farRight);

    if (scale != 1.0f) {
        // find center of frustum
        Vector3f center = new Vector3f();
        for (int i = 0; i &lt; 8; i++) {
            center.addLocal(points[i]);
        }
        center.divideLocal(8f);

        Vector3f cDir = new Vector3f();
        for (int i = 0; i &lt; 8; i++) {
            cDir.set(points[i]).subtractLocal(center);
            cDir.multLocal(scale - 1.0f);
            points[i].addLocal(cDir);
        }
    }
}

/**
 * Compute bounds of a geomList
 * @param list
 * @param transform
 * @return
 */
public static BoundingBox computeUnionBound(GeometryList list, Transform transform) {
    BoundingBox bbox = new BoundingBox();
    TempVars tempv = TempVars.get();
    for (int i = 0; i &lt; list.size(); i++) {
        BoundingVolume vol = list.get(i).getWorldBound();
        BoundingVolume newVol = vol.transform(transform, tempv.bbox);
        //Nehon : prevent NaN and infinity values to screw the final bounding box
        if (!Float.isNaN(newVol.getCenter().x) &amp;&amp; !Float.isInfinite(newVol.getCenter().x)) {
            bbox.mergeLocal(newVol);
        }
    }
    tempv.release();
    return bbox;
}

/**
 * Compute bounds of a geomList
 * @param list
 * @param mat
 * @return
 */
public static BoundingBox computeUnionBound(GeometryList list, Matrix4f mat) {
    BoundingBox bbox = new BoundingBox();
    TempVars tempv = TempVars.get();
    for (int i = 0; i &lt; list.size(); i++) {
        BoundingVolume vol = list.get(i).getWorldBound();
        BoundingVolume store = vol.clone().transform(mat, tempv.bbox);
        //Nehon : prevent NaN and infinity values to screw the final bounding box
        if (!Float.isNaN(store.getCenter().x) &amp;&amp; !Float.isInfinite(store.getCenter().x)) {
            bbox.mergeLocal(store);
        }
    }
    tempv.release();
    return bbox;
}

/**
 * Computes the bounds of multiple bounding volumes
 *
 * @param bv
 * @return
 */
public static BoundingBox computeUnionBound(List&lt;BoundingVolume&gt; bv) {
    BoundingBox bbox = new BoundingBox();
    for (int i = 0; i &lt; bv.size(); i++) {
        BoundingVolume vol = bv.get(i);
        bbox.mergeLocal(vol);
    }
    return bbox;
}

/**
 * Compute bounds from an array of points
 *
 * @param pts
 * @param transform
 * @return
 */
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 &lt; pts.length; i++) {
        transform.transformVector(pts[i], 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);
}

/**
 * Compute bounds from an array of points
 * @param pts
 * @param mat
 * @return
 */
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 &lt; pts.length; i++) {
        float w = mat.multProj(pts[i], temp);

        temp.x /= w;
        temp.y /= w;
        // Why was this commented out?
        temp.z /= w;

        min.minLocal(temp);
        max.maxLocal(temp);
    }
    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 frustum are aligned
    return new BoundingBox(center, extent.x + 2.0f, extent.y + 2.0f, extent.z + 2.5f);
}

/**
 * Updates the shadow camera to properly contain the given points (which
 * contain the eye camera frustum corners)
 *
 * @param shadowCam
 * @param points
 */
public static void updateShadowCamera(Camera shadowCam, Vector3f[] points) {
    boolean ortho = shadowCam.isParallelProjection();
    shadowCam.setProjectionMatrix(null);

    if (ortho) {
        shadowCam.setFrustum(-1, 1, -1, 1, 1, -1);
    } else {
        shadowCam.setFrustumPerspective(45, 1, 1, 150);
    }

    Matrix4f viewProjMatrix = shadowCam.getViewProjectionMatrix();
    Matrix4f projMatrix = shadowCam.getProjectionMatrix();

    BoundingBox splitBB = computeBoundForPoints(points, viewProjMatrix);

    TempVars vars = TempVars.get();

    Vector3f splitMin = splitBB.getMin(vars.vect1);
    Vector3f splitMax = splitBB.getMax(vars.vect2);

// splitMin.z = 0;

    // Create the crop matrix.
    float scaleX, scaleY, scaleZ;
    float offsetX, offsetY, offsetZ;

    scaleX = 2.0f / (splitMax.x - splitMin.x);
    scaleY = 2.0f / (splitMax.y - splitMin.y);
    offsetX = -0.5f * (splitMax.x + splitMin.x) * scaleX;
    offsetY = -0.5f * (splitMax.y + splitMin.y) * scaleY;
    scaleZ = 1.0f / (splitMax.z - splitMin.z);
    offsetZ = -splitMin.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();
    result.set(cropMatrix);
    result.multLocal(projMatrix);

    vars.release();
    shadowCam.setProjectionMatrix(result);
}

/**
 * Updates the shadow camera to properly contain the given points (which
 * contain the eye camera frustum corners) and the shadow occluder objects.
 *
 * @param occluders
 * @param receivers
 * @param shadowCam
 * @param points
 */
public static void updateShadowCamera(GeometryList occluders,
        GeometryList receivers,
        Camera shadowCam,
        Vector3f[] points,
        float shadowMapSize) {
    updateShadowCamera(occluders, receivers, shadowCam, points, null, shadowMapSize);
}

/**
 * Updates the shadow camera to properly contain the given points (which
 * contain the eye camera frustum corners) and the shadow occluder objects.
 *
 * @param occluders
 * @param shadowCam
 * @param points
 */
public static void updateShadowCamera(GeometryList occluders,
        GeometryList receivers,
        Camera shadowCam,
        Vector3f[] points,
        GeometryList splitOccluders,
        float shadowMapSize) {
    
    boolean ortho = shadowCam.isParallelProjection();

    shadowCam.setProjectionMatrix(null);

    if (ortho) {
        shadowCam.setFrustum(-1, 1, -1, 1, 1, -1);
    }

    // create transform to rotate points to viewspace        
    Matrix4f viewProjMatrix = shadowCam.getViewProjectionMatrix();

    BoundingBox splitBB = computeBoundForPoints(points, viewProjMatrix);

    TempVars vars = TempVars.get();
    
    BoundingBox casterBB = new BoundingBox();
    BoundingBox receiverBB = new BoundingBox();
    
    int casterCount = 0, receiverCount = 0;
    
    //ArrayList&lt;BoundingVolume&gt; visRecvList = new ArrayList&lt;BoundingVolume&gt;();
    for (int i = 0; i &lt; receivers.size(); i++) {
        // convert bounding box to light's viewproj space
        Geometry receiver = receivers.get(i);
        BoundingVolume bv = receiver.getWorldBound();
        BoundingVolume recvBox = bv.transform(viewProjMatrix, vars.bbox);

        if (splitBB.intersects(recvBox)) {
            receiverBB.mergeLocal(recvBox);
            receiverCount++;
        }
    }

    //ArrayList&lt;BoundingVolume&gt; visOccList = new ArrayList&lt;BoundingVolume&gt;();
    for (int i = 0; i &lt; occluders.size(); i++) {
        // convert bounding box to light's viewproj space
        Geometry occluder = occluders.get(i);
        BoundingVolume bv = occluder.getWorldBound();
        BoundingVolume occBox = bv.transform(viewProjMatrix, vars.bbox);

        boolean intersects = splitBB.intersects(occBox);
        if (!intersects &amp;&amp; occBox instanceof BoundingBox) {
            BoundingBox occBB = (BoundingBox) occBox;
            //Kirill 01/10/2011
            // Extend the occluder further into the frustum
            // This fixes shadow dissapearing issues when
            // the caster itself is not in the view camera
            // but its shadow is in the camera
            //      The number is in world units
            occBB.setZExtent(occBB.getZExtent() + 50);
            occBB.setCenter(occBB.getCenter().addLocal(0, 0, 25));
            if (splitBB.intersects(occBB)) {
                // To prevent extending the depth range too much
                // We return the bound to its former shape
                // Before adding it
                occBB.setZExtent(occBB.getZExtent() - 50);
                occBB.setCenter(occBB.getCenter().subtractLocal(0, 0, 25));
                casterBB.mergeLocal(occBox);
                casterCount++;
                if (splitOccluders != null) {
                    splitOccluders.add(occluder);
                }
            }
        } else if (intersects) {
            casterBB.mergeLocal(occBox);
            casterCount++;
            if (splitOccluders != null) {
                splitOccluders.add(occluder);
            }
        }
    }

    //Nehon 08/18/2010 this is to avoid shadow bleeding when the ground is set to only receive shadows
    if (casterCount != receiverCount) {
        casterBB.setXExtent(casterBB.getXExtent() + 2.0f);
        casterBB.setYExtent(casterBB.getYExtent() + 2.0f);
        casterBB.setZExtent(casterBB.getZExtent() + 2.0f);
    }

    Vector3f casterMin = casterBB.getMin(vars.vect1);
    Vector3f casterMax = casterBB.getMax(vars.vect2);

    Vector3f receiverMin = receiverBB.getMin(vars.vect3);
    Vector3f receiverMax = receiverBB.getMax(vars.vect4);

    Vector3f splitMin = splitBB.getMin(vars.vect5);
    Vector3f splitMax = splitBB.getMax(vars.vect6);

    splitMin.z = 0;

// if (!ortho) {
// shadowCam.setFrustumPerspective(45, 1, 1, splitMax.z);
// }

    Matrix4f projMatrix = shadowCam.getProjectionMatrix();

    Vector3f cropMin = vars.vect7;
    Vector3f cropMax = vars.vect8;

    // IMPORTANT: Special handling for Z values
    cropMin.x = max(max(casterMin.x, receiverMin.x), splitMin.x);
    cropMax.x = min(min(casterMax.x, receiverMax.x), splitMax.x);

    cropMin.y = max(max(casterMin.y, receiverMin.y), splitMin.y);
    cropMax.y = min(min(casterMax.y, receiverMax.y), splitMax.y);

    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;

    scaleX = (2.0f) / (cropMax.x - cropMin.x);
    scaleY = (2.0f) / (cropMax.y - cropMin.y);

    //Shadow map stabilization approximation from shaderX 7
    //from Practical Cascaded Shadow maps adapted to PSSM
    //scale stabilization
    float halfTextureSize = shadowMapSize * 0.5f;

    if (halfTextureSize != 0 &amp;&amp; scaleX &gt;0 &amp;&amp; scaleY&gt;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;
    }

    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  &amp;&amp; scaleX &gt;0 &amp;&amp; scaleY&gt;0) {
        offsetX = FastMath.ceil(offsetX * halfTextureSize) / halfTextureSize;
        offsetY = FastMath.ceil(offsetY * halfTextureSize) / halfTextureSize;
    }

    scaleZ = 1.0f / (cropMax.z - cropMin.z);
    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();
    result.set(cropMatrix);
    result.multLocal(projMatrix);
    vars.release();

    shadowCam.setProjectionMatrix(result);

}

/**
 * Populates the outputGeometryList with the geometry of the
 * inputGeomtryList that are in the frustum of the given camera
 *
 * @param inputGeometryList The list containing all geometry 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
 */
public static void getGeometriesInCamFrustum(GeometryList inputGeometryList,
        Camera camera,
        GeometryList outputGeometryList) {
    for (int i = 0; i &lt; 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);
    }

}

/**
 * Populates the outputGeometryList with the geometry of the
 * inputGeomtryList that are in the radius of a light.
 * The array of camera must be an array of 6 cameara initialized so they represent the light viewspace of a pointlight
 *
 * @param inputGeometryList The list containing all geometry 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
 */
public static void getGeometriesInLightRadius(GeometryList inputGeometryList,
        Camera[] cameras,
        GeometryList outputGeometryList) {
    for (int i = 0; i &lt; inputGeometryList.size(); i++) {
        Geometry g = inputGeometryList.get(i);
        boolean inFrustum = false;
        for (int j = 0; j &lt; cameras.length &amp;&amp; inFrustum == false; j++) {
            Camera camera = cameras[j];
            int planeState = camera.getPlaneState();
            camera.setPlaneState(0);
            inFrustum = camera.contains(g.getWorldBound()) != Camera.FrustumIntersect.Outside;
            camera.setPlaneState(planeState);
        }
        if (inFrustum) {
            outputGeometryList.add(g);
        }
    }

}

}[/java]

The diff:

[java]Index: ShadowUtil.java

— ShadowUtil.java (revision 11004)
+++ ShadowUtil.java (working copy)
@@ -44,7 +44,6 @@
import com.jme3.util.TempVars;
import static java.lang.Math.max;
import static java.lang.Math.min;
-import java.util.ArrayList;
import java.util.List;

/**
@@ -172,14 +171,16 @@
*/
public static BoundingBox computeUnionBound(GeometryList list, Transform transform) {
BoundingBox bbox = new BoundingBox();

  •    TempVars tempv = TempVars.get();
       for (int i = 0; i &lt; list.size(); i++) {
           BoundingVolume vol = list.get(i).getWorldBound();
    
  •        BoundingVolume newVol = vol.transform(transform);
    
  •        BoundingVolume newVol = vol.transform(transform, tempv.bbox);
           //Nehon : prevent NaN and infinity values to screw the final bounding box
           if (!Float.isNaN(newVol.getCenter().x) &amp;&amp; !Float.isInfinite(newVol.getCenter().x)) {
               bbox.mergeLocal(newVol);
           }
       }
    
  •    tempv.release();
       return bbox;
    
    }

@@ -191,15 +192,16 @@
*/
public static BoundingBox computeUnionBound(GeometryList list, Matrix4f mat) {
BoundingBox bbox = new BoundingBox();

  •    BoundingVolume store = null;
    
  •    TempVars tempv = TempVars.get();
       for (int i = 0; i &lt; list.size(); i++) {
           BoundingVolume vol = list.get(i).getWorldBound();
    
  •        store = vol.clone().transform(mat, null);
    
  •        BoundingVolume store = vol.clone().transform(mat, tempv.bbox);
           //Nehon : prevent NaN and infinity values to screw the final bounding box
           if (!Float.isNaN(store.getCenter().x) &amp;&amp; !Float.isInfinite(store.getCenter().x)) {
               bbox.mergeLocal(store);
           }
       }
    
  •    tempv.release();
       return bbox;
    
    }

@@ -356,7 +358,7 @@
Vector3f[] points,
GeometryList splitOccluders,
float shadowMapSize) {

  •    boolean ortho = shadowCam.isParallelProjection();
    
       shadowCam.setProjectionMatrix(null);
    

@@ -370,24 +372,32 @@

     BoundingBox splitBB = computeBoundForPoints(points, viewProjMatrix);
  •    ArrayList&lt;BoundingVolume&gt; visRecvList = new ArrayList&lt;BoundingVolume&gt;();
    
  •    TempVars vars = TempVars.get();
    
  •    BoundingBox casterBB = new BoundingBox();
    
  •    BoundingBox receiverBB = new BoundingBox();
    
  •    int casterCount = 0, receiverCount = 0;
    
  •    //ArrayList&lt;BoundingVolume&gt; visRecvList = new ArrayList&lt;BoundingVolume&gt;();
       for (int i = 0; i &lt; receivers.size(); i++) {
           // convert bounding box to light's viewproj space
           Geometry receiver = receivers.get(i);
           BoundingVolume bv = receiver.getWorldBound();
    
  •        BoundingVolume recvBox = bv.transform(viewProjMatrix, null);
    
  •        BoundingVolume recvBox = bv.transform(viewProjMatrix, vars.bbox);
    
           if (splitBB.intersects(recvBox)) {
    
  •            visRecvList.add(recvBox);
    
  •            receiverBB.mergeLocal(recvBox);
    
  •            receiverCount++;
           }
       }
    
  •    ArrayList&lt;BoundingVolume&gt; visOccList = new ArrayList&lt;BoundingVolume&gt;();
    
  •    //ArrayList&lt;BoundingVolume&gt; visOccList = new ArrayList&lt;BoundingVolume&gt;();
       for (int i = 0; i &lt; occluders.size(); i++) {
           // convert bounding box to light's viewproj space
           Geometry occluder = occluders.get(i);
           BoundingVolume bv = occluder.getWorldBound();
    
  •        BoundingVolume occBox = bv.transform(viewProjMatrix, null);
    
  •        BoundingVolume occBox = bv.transform(viewProjMatrix, vars.bbox);
    
           boolean intersects = splitBB.intersects(occBox);
           if (!intersects &amp;&amp; occBox instanceof BoundingBox) {
    

@@ -406,31 +416,28 @@
// Before adding it
occBB.setZExtent(occBB.getZExtent() - 50);
occBB.setCenter(occBB.getCenter().subtractLocal(0, 0, 25));

  •                visOccList.add(occBox);
    
  •                casterBB.mergeLocal(occBox);
    
  •                casterCount++;
                   if (splitOccluders != null) {
                       splitOccluders.add(occluder);
                   }
               }
           } else if (intersects) {
    
  •            visOccList.add(occBox);
    
  •            casterBB.mergeLocal(occBox);
    
  •            casterCount++;
               if (splitOccluders != null) {
                   splitOccluders.add(occluder);
               }
           }
       }
    
  •    BoundingBox casterBB = computeUnionBound(visOccList);
    
  •    BoundingBox receiverBB = computeUnionBound(visRecvList);
    
  •    //Nehon 08/18/2010 this is to avoid shadow bleeding when the ground is set to only receive shadows
    
  •    if (visOccList.size() != visRecvList.size()) {
    
  •    if (casterCount != receiverCount) {
           casterBB.setXExtent(casterBB.getXExtent() + 2.0f);
           casterBB.setYExtent(casterBB.getYExtent() + 2.0f);
           casterBB.setZExtent(casterBB.getZExtent() + 2.0f);
       }
    
  •    TempVars vars = TempVars.get();
    
  •    Vector3f casterMin = casterBB.getMin(vars.vect1);
       Vector3f casterMax = casterBB.getMax(vars.vect2);
    

[/java]

Care if you guys look it over so I can submit or offer suggestions?

I just want to say… thanks for providing the full version and the diff. Really makes it easy to review.

Nothing jumped out at me on a quick read but I’ll defer to nehon since he’s more familiar with what the code is doing. Looks like good work.

I committed the changes. Looks like the will need to be moved the to “gradle restructure” version (whatever that is all about) :stuck_out_tongue:

Well its basically about removing the ant build stuff and using a superior gradle build :slight_smile:

@phr00t said: I committed the changes. Looks like the will need to be moved the to "gradle restructure" version (whatever that is all about) :P
@Empire Phoenix said: Well its basically about removing the ant build stuff and using a superior gradle build :)

Yes. :smiley:

And so far we’ve been manually merging over the changes done on regular trunk… which gives a little bit of a safety buffer so we don’t freak out when strange commits pop-up in the message log. :slight_smile: