meshshadows.java

来自「java 3d game jme 工程开发源代码」· Java 代码 · 共 599 行 · 第 1/2 页

JAVA
599
字号
/*
 * Copyright (c) 2003-2009 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.jme.scene.shadow;

import java.nio.FloatBuffer;
import java.nio.IntBuffer;
import java.util.ArrayList;
import java.util.BitSet;

import com.jme.light.DirectionalLight;
import com.jme.light.Light;
import com.jme.light.PointLight;
import com.jme.math.Plane;
import com.jme.math.Quaternion;
import com.jme.math.Vector3f;
import com.jme.scene.Spatial;
import com.jme.scene.TriMesh;
import com.jme.scene.state.LightState;
import com.jme.util.geom.BufferUtils;

/**
 * <code>MeshShadows</code> A grouping of the ShadowVolumes for a single
 * TriMesh.
 * 
 * @author Mike Talbot (some code from a shadow implementation written Jan 2005)
 * @author Joshua Slack
 * @version $Id: MeshShadows.java,v 1.15 2006/09/29 22:37:32 nca Exp $
 */
public class MeshShadows {
    private static final long serialVersionUID = 1L;

    /** the distance to which shadow volumes will be projected */
    protected float projectionLength = 1000;

    /** The triangles of our occluding mesh (one per triangle in the mesh) */
    protected ArrayList<ShadowTriangle> faces;

    /** A bitset used for storing directional flags. */
    protected BitSet facing;

    /** The mesh that is the target of this shadow volume */
    protected TriMesh target = null;

    /** The arraylist of shadowvolumes in this grouping */
    protected ArrayList<ShadowVolume> volumes = new ArrayList<ShadowVolume>();

    /** The world rotation of the target at the last mesh construction */
    protected Quaternion oldWorldRotation = new Quaternion();

    /** The world translation of the trimesh at the last mesh construction */
    protected Vector3f oldWorldTranslation = new Vector3f();

    /** The world scale of the trimesh at the last mesh construction */
    protected Vector3f oldWorldScale = new Vector3f();

    private int maxIndex;

    private int vertCount;

    public static long throttle = 1000 / 50; // 50 x a sec
    private long lastTime;
    private boolean nextTime = true;

    /** Static computation field */
    protected static Vector3f compVect = new Vector3f();

    /**
     * Constructor for <code>MeshShadows</code>
     * 
     * @param target
     *            the mesh that will be the target of the shadow volumes held in
     *            this grouping
     */
    public MeshShadows(TriMesh target) {
        this.target = target;
        recreateFaces();
    }

    /**
     * <code>createGeometry</code> creates or updates the ShadowVolume
     * geometries for the target TriMesh - one for each applicable Light in the
     * given LightState. Only Directional and Point lights are currently
     * supported. ShadowVolume geometry is only regen'd when light or occluder
     * aspects change.
     * 
     * @param lightState
     *            is the current lighting state
     */
    public void createGeometry(LightState lightState) {
        if (target.getTriangleCount() != maxIndex
                || target.getVertexCount() != vertCount) {
            recreateFaces();
        }

        // Holds a copy of the mesh vertices transformed to world coordinates
        FloatBuffer vertex = null;

        // Ensure that we have some potential lights to cast shadows!
        if (lightState.getQuantity() != 0) {
            LightState lights = lightState;

            // Update the cache of lights - if still sane, return
            if (updateCache(lights))
                return;

            // Now scan through each light and create the shadow volume
            for (int l = 0; l < lights.getQuantity(); l++) {
                Light light = lights.get(l);

                // Make sure we can (or want to) handle this light
                if (!light.isShadowCaster()
                        || (!(light.getType() == Light.Type.Directional) && !(light
                                .getType() == Light.Type.Point)))
                    continue;

                // Get the volume assoicated with this light
                ShadowVolume lv = getShadowVolume(light);

                // See if this light has not been seen before!
                if (lv == null) {
                    // Create a new light volume
                    lv = new ShadowVolume(light);
                    volumes.add(lv);
                    lv.setUpdate(true);
                }

                // See if the volume requires updating
                if (lv.isUpdate()) {
                    lv.setUpdate(false);

                    if (!target.isCastsShadows()) {
                        lv.setCullHint(Spatial.CullHint.Always);
                        continue;
                    }

                    lv.setCullHint(Spatial.CullHint.Dynamic);

                    // Translate the vertex information from the mesh to
                    // world
                    // coordinates if
                    // we are going to do any work
                    if (vertex == null) {
                        vertex = target.getWorldCoords(null);
                    }

                    // Find out which triangles are facing the light
                    // triangle will be set true for faces towards the light
                    processFaces(vertex, light, target);

                    // Get the edges that are in shadow
                    ShadowEdge[] edges = getShadowEdges();

                    // Now we need to develop a mesh based on projecting
                    // these
                    // edges
                    // to infinity in the direction of the light
                    int length = edges.length;

                    // Create arrays to hold the shadow mesh
                    FloatBuffer shadowVertex = lv.getVertexBuffer();
                    if (shadowVertex == null
                            || shadowVertex.capacity() < length * 12)
                        shadowVertex = BufferUtils
                                .createVector3Buffer(length * 4);
                    FloatBuffer shadowNormal = lv.getNormalBuffer();
                    if (shadowNormal == null
                            || shadowNormal.capacity() < length * 12)
                        shadowNormal = BufferUtils
                                .createVector3Buffer(length * 4);
                    IntBuffer shadowIndex = lv.getIndexBuffer();
                    if (shadowIndex == null
                            || shadowIndex.capacity() < length * 6)
                        shadowIndex = BufferUtils.createIntBuffer(length * 6);

                    shadowVertex.limit(length * 12);
                    shadowNormal.limit(length * 12);
                    shadowIndex.limit(length * 6);

                    // Create quads out of the edge vertices
                    createShadowQuads(vertex, edges, shadowVertex,
                            shadowNormal, shadowIndex, light);

                    // Rebuild the TriMesh
                    lv.reconstruct(shadowVertex, shadowNormal, null, null,
                            shadowIndex);
                    shadowVertex.rewind();
                    lv.setVertexCount(shadowVertex.remaining() / 3);
                    shadowIndex.rewind();
                    lv.setTriangleQuantity(
                            shadowIndex.remaining() / 3);
                    lv.updateModelBound();
                    if ((target.getLocks() & Spatial.LOCKED_SHADOWS) != 0)
                        lv.lock();
                }

            }

        } else {
            // There are no volumes
            volumes.clear();
        }

    }

    /**
     * void <code>createShadowQuad</code> Creates projected quads from a
     * series of edges and vertices and stores them in the output shadowXXXX
     * arrays
     * 
     * @param vertex
     *            array of world coordinate vertices for the target TriMesh
     * @param edges
     *            a collection of edges that will be projected
     * @param shadowVertex
     * @param shadowNormal
     * @param shadowIndex
     * @param light
     *            light casting shadow
     */
    private void createShadowQuads(FloatBuffer vertex, ShadowEdge[] edges,
            FloatBuffer shadowVertex, FloatBuffer shadowNormal,
            IntBuffer shadowIndex, Light light) {
        Vector3f p0 = new Vector3f(), p1 = new Vector3f(), p2 = new Vector3f(), p3 = new Vector3f();

        // Setup a flag to indicate which type of light this is
        boolean directional = (light.getType() == Light.Type.Directional);

        Vector3f direction = new Vector3f();
        Vector3f location = new Vector3f();
        if (directional) {
            direction = ((DirectionalLight) light).getDirection();
        } else {
            location = ((PointLight) light).getLocation();
        }

        // Loop for each edge
        for (int e = 0; e < edges.length; e++) {
            // get the two known vertices
            BufferUtils.populateFromBuffer(p0, vertex, edges[e].p0);
            BufferUtils.populateFromBuffer(p3, vertex, edges[e].p1);

            // Calculate the projection of p0
            if (!directional) {
                direction = p0.subtract(location, direction).normalizeLocal();
            }
            // Project the other edges to infinity
            p1 = direction.mult(projectionLength, p1).addLocal(p0);
            if (!directional) {
                direction = p3.subtract(location, direction).normalizeLocal();
            }
            p2 = direction.mult(projectionLength).addLocal(p3);

            // Now we need to add a quad to the model
            int vertexOffset = e * 4;
            BufferUtils.setInBuffer(p0, shadowVertex, vertexOffset);
            BufferUtils.setInBuffer(p1, shadowVertex, vertexOffset + 1);
            BufferUtils.setInBuffer(p2, shadowVertex, vertexOffset + 2);
            BufferUtils.setInBuffer(p3, shadowVertex, vertexOffset + 3);

            // Calculate the normal
            Vector3f n = p1.subtract(p0).normalizeLocal().crossLocal(
                    p3.subtract(p0).normalizeLocal()).normalizeLocal();
            BufferUtils.setInBuffer(n, shadowNormal, vertexOffset);
            BufferUtils.setInBuffer(n, shadowNormal, vertexOffset + 1);
            BufferUtils.setInBuffer(n, shadowNormal, vertexOffset + 2);
            BufferUtils.setInBuffer(n, shadowNormal, vertexOffset + 3);

            // Add the indices
            int indexOffset = e * 6;
            shadowIndex.put(indexOffset + 0, vertexOffset + 0);

⌨️ 快捷键说明

复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?