clodcreator.java

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

JAVA
876
字号
/*
 * 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.lod;

import java.nio.FloatBuffer;
import java.nio.IntBuffer;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.TreeSet;
import java.util.Map.Entry;

import com.jme.math.Vector3f;
import com.jme.util.geom.BufferUtils;

/**
 * <code>ClodCreator</code> originally ported from David Eberly's c++,
 * modifications and enhancements made from there.<br>
 * <br>
 * This class is used by ClodMesh to create automatically generated records. The
 * reason for lack of documentation is that it should have little use to someone
 * outside the API, unless they already know how to use it.
 * 
 * @author Joshua Slack
 * @version $Id: ClodCreator.java,v 1.21 2007/08/20 10:28:23 rherlitz Exp $
 */

public class ClodCreator extends VETMesh {
	private FloatBuffer vertices;

	private FloatBuffer normals;

	private FloatBuffer colors;

	private FloatBuffer textures;

	private IntBuffer indices;

	private int currentVertex, currentTriangle, numbTriangles, vertQuantity;

	private int[] orderedVertices;

	private int[] permuteVertices;

	private int[] newIndices;

	int heapSize;

	HeapRecord[] heapArray;

	boolean collapsing;

	// for reordering vertices and triangles
	TreeSet<Integer> deletedVertices;

	ArrayList<CollapseRecord> deletedEdges;

	CollapseRecord[] records;

	private static final Vector3f tempVa = new Vector3f();

	private static final Vector3f tempVb = new Vector3f();

	private static final Vector3f tempVc = new Vector3f();

	private static final Vector3f tempVd = new Vector3f();

	protected class HeapRecord {
		public HeapRecord() {
			m_kEdge = new Edge(-1, -1);
			m_iHIndex = -1;
			m_fMetric = -1.0f;
		}

		public Edge m_kEdge;

		public int m_iHIndex;

		public float m_fMetric;

		public boolean equals(Object obj) {
			HeapRecord rkH = (HeapRecord) obj;
			return m_kEdge.equals(rkH.m_kEdge);
		}
	};

	public ClodCreator(FloatBuffer vertexArray, FloatBuffer normalArray,
			FloatBuffer colorArray, FloatBuffer textureArray,
			IntBuffer indiceArray) {
		// Hang onto these to avoid having to pass them through member function
		// calls.
		this.vertices = vertexArray;
		this.normals = normalArray;
		this.colors = colorArray;
		this.textures = textureArray;
		this.indices = indiceArray;
		vertQuantity = vertexArray.capacity() / 3;
		numbTriangles = indiceArray.capacity() / 3;

		// for reordering vertices and triangles
		currentVertex = vertQuantity - 1;
		currentTriangle = numbTriangles - 1;
		orderedVertices = new int[vertQuantity];
		permuteVertices = new int[vertQuantity];
		newIndices = new int[indices.capacity()];

		deletedEdges = new ArrayList<CollapseRecord>();
		deletedVertices = new TreeSet<Integer>();

		// Insert the triangles into the mesh.  The triangle indices are attached
		// as extra data.
		collapsing = false;
		for (int i = 0; i < numbTriangles; i++) {
			//      int iV0 = m_aiConnect[3 * i];
			//      int iV1 = m_aiConnect[3 * i + 1];
			//      int iV2 = m_aiConnect[3 * i + 2];
			//      if (!(iV0 != iV1 && iV0 != iV2 && iV1 != iV2)) throw new AssertionError();
			Triangle tri = new Triangle(indices.get(3 * i), indices
					.get(3 * i + 1), indices.get(3 * i + 2));
			insertTriangle(tri);
			setData(tri, new Integer(i));
		}

		if (triangleMap.size() != numbTriangles) {
			// We must have duplicates...  lets weed them out and make a new Clod.
			IntBuffer redoneIndices = BufferUtils.createIntBuffer(triangleMap
					.size() * 3);
			Iterator<Triangle> it = triangleMap.keySet().iterator();
			while (it.hasNext()) {
				Triangle t = it.next();
				redoneIndices.put(t.vert[0]);
				redoneIndices.put(t.vert[1]);
				redoneIndices.put(t.vert[2]);
			}
			ClodCreator creator = new ClodCreator(vertexArray, normalArray,
					colorArray, textureArray, redoneIndices);
			records = creator.getRecords();
			creator = null;
			// Copy the reduced indices back to the original indice array.  There will
			// be some bogus ones on the end, but thats ok because they will never be shown thanks to
			// the number of triangles field.
			indices.rewind();
			indices.put(redoneIndices);

			// clear the triangle map so a call to remove triangles doesn't bomb.
			triangleMap.clear();
			return;
		}

		//    if (m_kVMap.size() != m_akVertex.length)throw new AssertionError();
		//    if (m_kTMap.size() != m_iTQuantity)throw new AssertionError(
		//        "triangle map size: " + m_kTMap.size() + " != m_iTQuantity: " +
		//        m_iTQuantity);

		initializeHeap();

		collapsing = true;
		while (heapSize > 0) {
			if (heapArray[0].m_fMetric == Float.MAX_VALUE) {
				// all remaining heap elements have infinite weight
				flushVertices();
				flushTriangles();
				break;
			}

			doCollapse();

			//      if (! ( (m_kVMap.size()) == m_iVCurrent + 1))throw new AssertionError();
			//      if (! ( (m_kTMap.size()) == m_iTCurrent + 1))throw new AssertionError(
			//          "triangle map size: " + m_kTMap.size() + " != m_iTCurrent+1: " +
			//          (m_iTCurrent + 1));
		}

		collapsing = false;

		// Permute the vertices and triangle connectivity so that the last
		// vertex/triangle in the array is the first vertex/triangle to be
		// removed.
		reorder();

		// The collapse records store the incremental changes that are used for
		// dynamic LOD changes in the caller of this constructor.
		records = computeRecords();
	}

	public CollapseRecord[] getRecords() {
		return records;
	}

	public void doCollapse() {
		// Define a 2-edge to be an edge that has exactly two triangles sharing
		// it.  An edge is collapsible if it is a 2-edge and has at least one end
		// point whose sharing edges are all 2-edges.  In this case, such an end
		// point will be the 'throw' vertex.  This keeps the boundary and junction
		// edges from changing geometry and helps preserve the shape of the mesh.
		// The topology is always guaranteed not to change.

		// When this function is called, the metric has already been calculated
		// and is finite (so exactly two triangles must be sharing this edge).
		//    if (!(m_apkHeap[0].m_fMetric < Float.MAX_VALUE)) throw new AssertionError();
		Edge kEdge = heapArray[0].m_kEdge;

		// test end points to see if either has only 2-edges sharing it
		int i;
		for (i = 0; i < 2; i++) {
			ExVector pkESet = (ExVector) getEdges(kEdge.vert[i]).clone();
			int j;
			for (j = 0; j < pkESet.size(); j++) {
				EdgeAttribute pkEM = edgeMap.get(pkESet
						.toArray()[j]);
				//        if (!(pkEM != null)) throw new AssertionError();
				if (pkEM.triangleSet.size() != 2)
					break;
			}

			if (j == pkESet.size()) {
				// all edges sharing this end point are 2-edges
				break;
			}
		}

		if (i < 2) {
			int iVThrow = kEdge.vert[i];
			int iVKeep = kEdge.vert[1 - i];
			if (!collapseCausesFolding(iVKeep, iVThrow)) {
				remove();
				collapseEdge(iVKeep, iVThrow);
				return;
			}
		}

		// edge not collapsible, assign it infinite weight and update the heap
		update(0, Float.MAX_VALUE);
	}

	public boolean collapseCausesFolding(int iVKeep, int iVThrow) {
		VertexAttribute pkVT = vertexMap.get(new Integer(
				iVThrow));
		//    if (!(pkVT != null)) throw new AssertionError();

		Edge kCollapse = new Edge(iVKeep, iVThrow);
		for (int j = 0; j < pkVT.triangleSet.size(); j++) {
			Triangle kT = (Triangle) pkVT.triangleSet.toArray()[j];
			if (kCollapse.equals(new Edge(kT.vert[0], kT.vert[1]))
					|| kCollapse.equals(new Edge(kT.vert[1], kT.vert[2]))
					|| kCollapse.equals(new Edge(kT.vert[2], kT.vert[0]))) {
				// This triangle would be removed in a collapse, so it does not
				// contribute to any folding.
				continue;
			}

			for (int i = 0; i < 3; i++) {
				if (kT.vert[i] == iVThrow) {
					// Test if potential replacement triangle (either ordering)
					// is in the mesh.
					int iV0 = iVKeep;
					int iV1 = kT.vert[(i + 1) % 3];
					int iV2 = kT.vert[(i + 2) % 3];

					if (triangleMap.get(new Triangle(iV0, iV1, iV2)) != null
							|| triangleMap.get(new Triangle(iV0, iV2, iV1)) != null) {
						return true;
					}
				}
			}
		}

		return false;
	}

	public float getMetric(Edge pkE, EdgeAttribute pkEA) {
		float fLengthWeight = 10.0f;
		float fAngleWeight = 1.0f;

		// Compute the metric for the edge.  Only manifold edges (exactly two
		// triangles sharing the edge) are allowed to collapse.
		if (pkEA.triangleSet.size() == 2) {
			// length contribution
			BufferUtils.populateFromBuffer(tempVa, vertices, pkE.vert[0]);
			BufferUtils.populateFromBuffer(tempVb, vertices, pkE.vert[1]);
			Vector3f kDiff = tempVa.subtractLocal(tempVb);
			float fMetric = fLengthWeight * kDiff.length();

			// angle/area contribution
			Triangle kT = (Triangle) pkEA.triangleSet.toArray()[0];
			BufferUtils.populateFromBuffer(tempVc, vertices, kT.vert[0]);
			BufferUtils.populateFromBuffer(tempVa, vertices, kT.vert[1]);
			BufferUtils.populateFromBuffer(tempVb, vertices, kT.vert[2]);
			Vector3f kE0 = tempVa.subtractLocal(tempVc);
			Vector3f kE1 = tempVb.subtractLocal(tempVc);
			Vector3f kN0 = kE0.cross(kE1, tempVc);

			kT = (Triangle) pkEA.triangleSet.toArray()[1];
			BufferUtils.populateFromBuffer(tempVd, vertices, kT.vert[0]);
			BufferUtils.populateFromBuffer(tempVa, vertices, kT.vert[1]);
			BufferUtils.populateFromBuffer(tempVb, vertices, kT.vert[2]);
			kE0 = tempVa.subtractLocal(tempVd);
			kE1 = tempVb.subtractLocal(tempVd);
			Vector3f kN1 = kE0.crossLocal(kE1);

			Vector3f kCross = kN0.cross(kN1, tempVa);
			fMetric += fAngleWeight * kCross.length();

			return fMetric;
		}

		// Boundary edges (one triangle containing edge) and junction edges
		// (3 or more triangles sharing edge) are not allowed to collapse.
		return Float.MAX_VALUE;
	}

	public void removeTriangle(Triangle rkT) {
		// If the triangle is an original one, reorder the connectivity array so
		// that the triangle occurs at the end.
		int iTIndex = ((Integer) getData(rkT)).intValue();
		if (iTIndex >= 0) {
			//      if (!(m_iTCurrent >= 0)) throw new AssertionError();
			newIndices[3 * currentTriangle] = indices.get(3 * iTIndex);
			newIndices[3 * currentTriangle + 1] = indices.get(3 * iTIndex + 1);
			newIndices[3 * currentTriangle + 2] = indices.get(3 * iTIndex + 2);
			currentTriangle--;
		}

		super.removeTriangle(rkT);
	}

	public void modifyTriangle(Triangle rkT, int iVKeep, int iVThrow) {
		// Get the index of the pre-modified triangle, then remove the triangle
		// from the mesh.
		int iTIndex = ((Integer) getData(rkT)).intValue();
		super.removeTriangle(rkT);

		// replace 'throw' by 'keep'
		for (int i = 0; i < 3; i++) {
			if (rkT.vert[i] == iVThrow) {
				rkT.vert[i] = iVKeep;
				break;
			}
		}

		// Indices on modified triangles are the same as the indices on the
		// pre-modified triangles.
		insertTriangle(rkT);
		setData(rkT, new Integer(iTIndex));
	}

	public void collapseEdge(int iVKeep, int iVThrow) {
		// find the edge to collapse
		Edge kCollapse = new Edge(iVKeep, iVThrow);
		EdgeAttribute pkEM = edgeMap.get(kCollapse);
		//    if (pkEM == null) throw new AssertionError("Edge unexpectedly missing from EdgeMap!");

		// keep track of vertices that are deleted in the collapse
		deletedVertices.clear();

		// Remove the collapse-edge-shared triangles.  Using a copy of the
		// triangle set from the collapse edge is required since removal of the
		// last triangle sharing the collapse edge will remove that edge from
		// the edge map, thereby invalidating any iterator that points to data
		// in the collapse edge.
		ExVector kTSet = (ExVector) pkEM.triangleSet.clone(); // <Triangle>
		int iTDeletions = kTSet.size();
		//    if (!(iTDeletions > 0)) throw new AssertionError();
		for (int j = 0; j < kTSet.size(); j++)
			removeTriangle((Triangle) kTSet.toArray()[j]);

		// Replace 'throw' vertices by 'keep' vertices in the remaining triangles
		// at the 'throw' vertex.  The old triangles are removed and the modified
		// triangles are inserted.
		Triangle kT;
		VertexAttribute pkVM = vertexMap.get(new Integer(
				iVThrow));
		if (pkVM != null) {
			kTSet = (ExVector) pkVM.triangleSet.clone();
			for (int j = 0; j < kTSet.size(); j++) {
				kT = (Triangle) kTSet.toArray()[j];
				modifyTriangle(kT, iVKeep, iVThrow);
			}
		}

		// The set of potentially modified edges consists of all those edges that
		// are shared by the triangles containing the 'keep' vertex.  Modify these
		// metrics and update the heap.
		TreeSet<Edge> kModified = new TreeSet<Edge>();
		ExVector pkTSet = (ExVector) getTriangles(iVKeep).clone(); // <Triangle>
		if (pkTSet != null) {
			kTSet = (ExVector) pkTSet.clone();
			for (int j = 0; j < kTSet.size(); j++) {
				kT = (Triangle) kTSet.toArray()[j];
				kModified.add(new Edge(kT.vert[0], kT.vert[1]));
				kModified.add(new Edge(kT.vert[1], kT.vert[2]));
				kModified.add(new Edge(kT.vert[2], kT.vert[0]));
			}

			Iterator<Edge> it = kModified.iterator();
			while (it.hasNext()) {
				Edge pkES = it.next();
				pkEM = edgeMap.get(pkES);
				HeapRecord pkRecord = (HeapRecord) pkEM.data;
				float fMetric = getMetric(pkES, pkEM);
				if (pkRecord.m_iHIndex >= 0)
					update(pkRecord.m_iHIndex, fMetric);
			}
		}

		// save vertex reordering information
		Iterator it = deletedVertices.iterator();
		int iV;
		while (it.hasNext()) {

⌨️ 快捷键说明

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