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 + -
显示快捷键?