Package com.ardor3d.extension.shadow.map

Source Code of com.ardor3d.extension.shadow.map.PSSMCamera

/**
* Copyright (c) 2008-2012 Ardor Labs, Inc.
*
* This file is part of Ardor3D.
*
* Ardor3D is free software: you can redistribute it and/or modify it
* under the terms of its license which may be found in the accompanying
* LICENSE file or at <http://www.ardor3d.com/LICENSE>.
*/

package com.ardor3d.extension.shadow.map;

import com.ardor3d.bounding.BoundingBox;
import com.ardor3d.bounding.BoundingSphere;
import com.ardor3d.bounding.BoundingVolume;
import com.ardor3d.math.Vector3;
import com.ardor3d.math.Vector4;
import com.ardor3d.math.type.ReadOnlyMatrix4;
import com.ardor3d.math.type.ReadOnlyVector3;
import com.ardor3d.renderer.Camera;

/**
* Camera with additional pssm related functionality.
*/
public class PSSMCamera extends Camera {

    /** The storage place for calculated split distances. */
    protected double _splitDistances[] = new double[2];

    /** The lambda value used in split distance calculations. */
    protected double _lambda = 0.5;

    /** The corners of the camera frustum. */
    protected final Vector3[] _corners = new Vector3[8];

    /** The center of the camera frustum. */
    protected final Vector3 _center = new Vector3();

    /** Temporary vector used for storing extents during corner calculations. */
    protected final Vector3 _extents = new Vector3();

    /** The maximum far plane distance used when packing the frustum. */
    protected double _maxFarPlaneDistance = 2000.0;

    /**
     * Instantiates a new PSSM camera.
     */
    public PSSMCamera() {
        super(0, 0); // copy later
        init();
    }

    /**
     * Instantiates a new PSSM camera.
     *
     * @param width
     *            the width
     * @param height
     *            the height
     */
    public PSSMCamera(final int width, final int height) {
        super(width, height);
        init();
    }

    /**
     * Instantiates a new PSSM camera.
     *
     * @param source
     *            the source
     */
    public PSSMCamera(final Camera source) {
        super(source);
        init();
    }

    /**
     * Initialize structures.
     */
    private void init() {
        for (int i = 0; i < _corners.length; i++) {
            _corners[i] = new Vector3();
        }
    }

    /**
     * Calculates the distances from view location for view frustum splits using the "practical split scheme".
     *
     * @param splitCount
     *            the split count
     *
     * @see <a href="http://appsrv.cse.cuhk.edu.hk/~fzhang/pssm_vrcia/keypoint1.htm">technique paper</a>
     */
    public void calculateSplitDistances(final int splitCount) {
        // ensure correct size.
        if (_splitDistances.length != splitCount + 1) {
            _splitDistances = new double[splitCount + 1];
        }

        final double nearPlane = getFrustumNear();
        final double farPlane = getFrustumFar();

        // setup intermediate splits
        for (int i = 1; i < splitCount; i++) {
            final double part = i / (double) splitCount;
            final double logsplit = nearPlane * Math.pow((farPlane / nearPlane), part);
            final double uniformSplit = nearPlane + (farPlane - nearPlane) * part;
            _splitDistances[i] = logsplit * _lambda + uniformSplit * (1 - _lambda);
        }

        // setup first and last split (near/far planes)
        _splitDistances[0] = nearPlane;
        _splitDistances[splitCount] = farPlane;
    }

    /**
     * Compress this camera's near and far frustum planes to be smaller if possible, using the given bounds as a
     * measure.
     *
     * @param sceneBounds
     *            the scene bounds
     */
    public void pack(final BoundingVolume sceneBounds) {
        final ReadOnlyVector3 center = sceneBounds.getCenter();
        for (int i = 0; i < _corners.length; i++) {
            _corners[i].set(center);
        }

        if (sceneBounds instanceof BoundingBox) {
            final BoundingBox bbox = (BoundingBox) sceneBounds;
            bbox.getExtent(_extents);
        } else if (sceneBounds instanceof BoundingSphere) {
            final BoundingSphere bsphere = (BoundingSphere) sceneBounds;
            _extents.set(bsphere.getRadius(), bsphere.getRadius(), bsphere.getRadius());
        }

        _corners[0].addLocal(_extents.getX(), _extents.getY(), _extents.getZ());
        _corners[1].addLocal(_extents.getX(), -_extents.getY(), _extents.getZ());
        _corners[2].addLocal(_extents.getX(), _extents.getY(), -_extents.getZ());
        _corners[3].addLocal(_extents.getX(), -_extents.getY(), -_extents.getZ());
        _corners[4].addLocal(-_extents.getX(), _extents.getY(), _extents.getZ());
        _corners[5].addLocal(-_extents.getX(), -_extents.getY(), _extents.getZ());
        _corners[6].addLocal(-_extents.getX(), _extents.getY(), -_extents.getZ());
        _corners[7].addLocal(-_extents.getX(), -_extents.getY(), -_extents.getZ());

        final ReadOnlyMatrix4 mvMatrix = getModelViewMatrix();
        double optimalCameraNear = Double.MAX_VALUE;
        double optimalCameraFar = -Double.MAX_VALUE;
        final Vector4 position = Vector4.fetchTempInstance();
        for (int i = 0; i < _corners.length; i++) {
            position.set(_corners[i].getX(), _corners[i].getY(), _corners[i].getZ(), 1);
            mvMatrix.applyPre(position, position);

            optimalCameraNear = Math.min(-position.getZ(), optimalCameraNear);
            optimalCameraFar = Math.max(-position.getZ(), optimalCameraFar);
        }
        Vector4.releaseTempInstance(position);

        // XXX: use of getFrustumNear and getFrustumFar seems suspicious...
        // XXX: It depends on the frustum being reset each update
        optimalCameraNear = Math.min(Math.max(getFrustumNear(), optimalCameraNear), getFrustumFar());
        optimalCameraFar = Math.max(optimalCameraNear, Math.min(_maxFarPlaneDistance, optimalCameraFar));

        final double change = optimalCameraNear / _frustumNear;
        setFrustumLeft(getFrustumLeft() * change);
        setFrustumRight(getFrustumRight() * change);
        setFrustumTop(getFrustumTop() * change);
        setFrustumBottom(getFrustumBottom() * change);

        setFrustumNear(optimalCameraNear);
        setFrustumFar(optimalCameraFar);
    }

    public void updateMaxCameraFar() {
        double optimalCameraFar = getFrustumFar();
        optimalCameraFar = Math.max(getFrustumNear() + 1.0, Math.min(_maxFarPlaneDistance, optimalCameraFar));
        setFrustumFar(optimalCameraFar);
    }

    /**
     * Calculate frustum corners and center.
     *
     * @param fNear
     *            the near distance
     * @param fFar
     *            the far distance
     */
    public void calculateFrustum(final double fNear, final double fFar) {
        final double fNearPlaneHeight, fNearPlaneWidth, fFarPlaneHeight, fFarPlaneWidth;
        if (getProjectionMode() == ProjectionMode.Parallel) {
            fNearPlaneHeight = (_frustumTop - _frustumBottom) * 0.5;
            fNearPlaneWidth = (_frustumRight - _frustumLeft) * 0.5;

            fFarPlaneHeight = (_frustumTop - _frustumBottom) * 0.5;
            fFarPlaneWidth = (_frustumRight - _frustumLeft) * 0.5;
        } else {
            fNearPlaneHeight = (_frustumTop - _frustumBottom) * fNear * 0.5 / _frustumNear;
            fNearPlaneWidth = (_frustumRight - _frustumLeft) * fNear * 0.5 / _frustumNear;

            fFarPlaneHeight = (_frustumTop - _frustumBottom) * fFar * 0.5 / _frustumNear;
            fFarPlaneWidth = (_frustumRight - _frustumLeft) * fFar * 0.5 / _frustumNear;
        }

        final Vector3 vNearPlaneCenter = Vector3.fetchTempInstance();
        final Vector3 vFarPlaneCenter = Vector3.fetchTempInstance();
        final Vector3 direction = Vector3.fetchTempInstance();
        final Vector3 left = Vector3.fetchTempInstance();
        final Vector3 up = Vector3.fetchTempInstance();

        direction.set(getDirection()).multiplyLocal(fNear);
        vNearPlaneCenter.set(getLocation()).addLocal(direction);
        direction.set(getDirection()).multiplyLocal(fFar);
        vFarPlaneCenter.set(getLocation()).addLocal(direction);

        left.set(getLeft()).multiplyLocal(fNearPlaneWidth);
        up.set(getUp()).multiplyLocal(fNearPlaneHeight);
        _corners[0].set(vNearPlaneCenter).subtractLocal(left).subtractLocal(up);
        _corners[1].set(vNearPlaneCenter).subtractLocal(left).addLocal(up);
        _corners[2].set(vNearPlaneCenter).addLocal(left).addLocal(up);
        _corners[3].set(vNearPlaneCenter).addLocal(left).subtractLocal(up);

        left.set(getLeft()).multiplyLocal(fFarPlaneWidth);
        up.set(getUp()).multiplyLocal(fFarPlaneHeight);
        _corners[4].set(vFarPlaneCenter).subtractLocal(left).subtractLocal(up);
        _corners[5].set(vFarPlaneCenter).subtractLocal(left).addLocal(up);
        _corners[6].set(vFarPlaneCenter).addLocal(left).addLocal(up);
        _corners[7].set(vFarPlaneCenter).addLocal(left).subtractLocal(up);

        direction.set(getDirection()).multiplyLocal((fFar + fNear) * 0.5);
        _center.set(getLocation()).addLocal(direction);

        Vector3.releaseTempInstance(vNearPlaneCenter);
        Vector3.releaseTempInstance(vFarPlaneCenter);
        Vector3.releaseTempInstance(direction);
        Vector3.releaseTempInstance(left);
        Vector3.releaseTempInstance(up);
    }

    /**
     * Gets the lambda.
     *
     * @return the lambda
     */
    public double getLambda() {
        return _lambda;
    }

    /**
     * Sets the lambda.
     *
     * @param lambda
     *            the new lambda
     */
    public void setLambda(final double lambda) {
        _lambda = lambda;
    }

    /**
     * Gets the split distances.
     *
     * @return the split distances
     */
    public double[] getSplitDistances() {
        return _splitDistances;
    }

    /**
     * Gets the max far plane distance.
     *
     * @return the max far plane distance
     */
    public double getMaxFarPlaneDistance() {
        return _maxFarPlaneDistance;
    }

    /**
     * Sets the max far plane distance.
     *
     * @param maxFarPlaneDistance
     *            the new max far plane distance
     */
    public void setMaxFarPlaneDistance(final double maxFarPlaneDistance) {
        _maxFarPlaneDistance = maxFarPlaneDistance;
    }
}
TOP

Related Classes of com.ardor3d.extension.shadow.map.PSSMCamera

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.