|
@@ -1,1365 +0,0 @@
|
|
|
-/*
|
|
|
- * Copyright (c) 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 jme3tools.converters.model.strip;
|
|
|
-
|
|
|
-import java.util.HashSet;
|
|
|
-import java.util.logging.Logger;
|
|
|
-
|
|
|
-/**
|
|
|
- *
|
|
|
- */
|
|
|
-class Stripifier {
|
|
|
- private static final Logger logger = Logger.getLogger(Stripifier.class
|
|
|
- .getName());
|
|
|
-
|
|
|
- public static int CACHE_INEFFICIENCY = 6;
|
|
|
-
|
|
|
- IntVec indices = new IntVec();
|
|
|
-
|
|
|
- int cacheSize;
|
|
|
-
|
|
|
- int minStripLength;
|
|
|
-
|
|
|
- float meshJump;
|
|
|
-
|
|
|
- boolean bFirstTimeResetPoint;
|
|
|
-
|
|
|
- Stripifier() {
|
|
|
- super();
|
|
|
- }
|
|
|
-
|
|
|
- ///////////////////////////////////////////////////////////////////////////////////////////
|
|
|
- // FindEdgeInfo()
|
|
|
- //
|
|
|
- // find the edge info for these two indices
|
|
|
- //
|
|
|
- static EdgeInfo findEdgeInfo(EdgeInfoVec edgeInfos, int v0, int v1) {
|
|
|
-
|
|
|
- // we can get to it through either array
|
|
|
- // because the edge infos have a v0 and v1
|
|
|
- // and there is no order except how it was
|
|
|
- // first created.
|
|
|
- EdgeInfo infoIter = edgeInfos.at(v0);
|
|
|
- while (infoIter != null) {
|
|
|
- if (infoIter.m_v0 == v0) {
|
|
|
- if (infoIter.m_v1 == v1)
|
|
|
- return infoIter;
|
|
|
-
|
|
|
- infoIter = infoIter.m_nextV0;
|
|
|
- } else {
|
|
|
- if (infoIter.m_v0 == v1)
|
|
|
- return infoIter;
|
|
|
-
|
|
|
- infoIter = infoIter.m_nextV1;
|
|
|
- }
|
|
|
- }
|
|
|
- return null;
|
|
|
- }
|
|
|
-
|
|
|
- ///////////////////////////////////////////////////////////////////////////////////////////
|
|
|
- // FindOtherFace
|
|
|
- //
|
|
|
- // find the other face sharing these vertices
|
|
|
- // exactly like the edge info above
|
|
|
- //
|
|
|
- static FaceInfo findOtherFace(EdgeInfoVec edgeInfos, int v0, int v1,
|
|
|
- FaceInfo faceInfo) {
|
|
|
- EdgeInfo edgeInfo = findEdgeInfo(edgeInfos, v0, v1);
|
|
|
-
|
|
|
- if ((edgeInfo == null) || (v0 == v1)) {
|
|
|
- //we've hit a degenerate
|
|
|
- return null;
|
|
|
- }
|
|
|
-
|
|
|
- return (edgeInfo.m_face0 == faceInfo ? edgeInfo.m_face1
|
|
|
- : edgeInfo.m_face0);
|
|
|
- }
|
|
|
-
|
|
|
- static boolean alreadyExists(FaceInfo faceInfo, FaceInfoVec faceInfos) {
|
|
|
- for (int i = 0; i < faceInfos.size(); ++i) {
|
|
|
- FaceInfo o = faceInfos.at(i);
|
|
|
- if ((o.m_v0 == faceInfo.m_v0) && (o.m_v1 == faceInfo.m_v1)
|
|
|
- && (o.m_v2 == faceInfo.m_v2))
|
|
|
- return true;
|
|
|
- }
|
|
|
- return false;
|
|
|
- }
|
|
|
-
|
|
|
- ///////////////////////////////////////////////////////////////////////////////////////////
|
|
|
- // BuildStripifyInfo()
|
|
|
- //
|
|
|
- // Builds the list of all face and edge infos
|
|
|
- //
|
|
|
- void buildStripifyInfo(FaceInfoVec faceInfos, EdgeInfoVec edgeInfos,
|
|
|
- int maxIndex) {
|
|
|
- // reserve space for the face infos, but do not resize them.
|
|
|
- int numIndices = indices.size();
|
|
|
- faceInfos.reserve(numIndices / 3);
|
|
|
-
|
|
|
- // we actually resize the edge infos, so we must initialize to null
|
|
|
- for (int i = 0; i < maxIndex + 1; i++)
|
|
|
- edgeInfos.add(null);
|
|
|
-
|
|
|
- // iterate through the triangles of the triangle list
|
|
|
- int numTriangles = numIndices / 3;
|
|
|
- int index = 0;
|
|
|
- boolean[] bFaceUpdated = new boolean[3];
|
|
|
-
|
|
|
- for (int i = 0; i < numTriangles; i++) {
|
|
|
- boolean bMightAlreadyExist = true;
|
|
|
- bFaceUpdated[0] = false;
|
|
|
- bFaceUpdated[1] = false;
|
|
|
- bFaceUpdated[2] = false;
|
|
|
-
|
|
|
- // grab the indices
|
|
|
- int v0 = indices.get(index++);
|
|
|
- int v1 = indices.get(index++);
|
|
|
- int v2 = indices.get(index++);
|
|
|
-
|
|
|
- //we disregard degenerates
|
|
|
- if (isDegenerate(v0, v1, v2))
|
|
|
- continue;
|
|
|
-
|
|
|
- // create the face info and add it to the list of faces, but only
|
|
|
- // if this exact face doesn't already
|
|
|
- // exist in the list
|
|
|
- FaceInfo faceInfo = new FaceInfo(v0, v1, v2);
|
|
|
-
|
|
|
- // grab the edge infos, creating them if they do not already exist
|
|
|
- EdgeInfo edgeInfo01 = findEdgeInfo(edgeInfos, v0, v1);
|
|
|
- if (edgeInfo01 == null) {
|
|
|
- //since one of it's edges isn't in the edge data structure, it
|
|
|
- // can't already exist in the face structure
|
|
|
- bMightAlreadyExist = false;
|
|
|
-
|
|
|
- // create the info
|
|
|
- edgeInfo01 = new EdgeInfo(v0, v1);
|
|
|
-
|
|
|
- // update the linked list on both
|
|
|
- edgeInfo01.m_nextV0 = edgeInfos.at(v0);
|
|
|
- edgeInfo01.m_nextV1 = edgeInfos.at(v1);
|
|
|
- edgeInfos.set(v0, edgeInfo01);
|
|
|
- edgeInfos.set(v1, edgeInfo01);
|
|
|
-
|
|
|
- // set face 0
|
|
|
- edgeInfo01.m_face0 = faceInfo;
|
|
|
- } else {
|
|
|
- if (edgeInfo01.m_face1 != null) {
|
|
|
- logger.fine("BuildStripifyInfo: > 2 triangles on an edge"
|
|
|
- + v0 + "," + v1 + "... uncertain consequences\n");
|
|
|
- } else {
|
|
|
- edgeInfo01.m_face1 = faceInfo;
|
|
|
- bFaceUpdated[0] = true;
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- // grab the edge infos, creating them if they do not already exist
|
|
|
- EdgeInfo edgeInfo12 = findEdgeInfo(edgeInfos, v1, v2);
|
|
|
- if (edgeInfo12 == null) {
|
|
|
- bMightAlreadyExist = false;
|
|
|
-
|
|
|
- // create the info
|
|
|
- edgeInfo12 = new EdgeInfo(v1, v2);
|
|
|
-
|
|
|
- // update the linked list on both
|
|
|
- edgeInfo12.m_nextV0 = edgeInfos.at(v1);
|
|
|
- edgeInfo12.m_nextV1 = edgeInfos.at(v2);
|
|
|
- edgeInfos.set(v1, edgeInfo12);
|
|
|
- edgeInfos.set(v2, edgeInfo12);
|
|
|
-
|
|
|
- // set face 0
|
|
|
- edgeInfo12.m_face0 = faceInfo;
|
|
|
- } else {
|
|
|
- if (edgeInfo12.m_face1 != null) {
|
|
|
- logger.fine("BuildStripifyInfo: > 2 triangles on an edge"
|
|
|
- + v1
|
|
|
- + ","
|
|
|
- + v2
|
|
|
- + "... uncertain consequences\n");
|
|
|
- } else {
|
|
|
- edgeInfo12.m_face1 = faceInfo;
|
|
|
- bFaceUpdated[1] = true;
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- // grab the edge infos, creating them if they do not already exist
|
|
|
- EdgeInfo edgeInfo20 = findEdgeInfo(edgeInfos, v2, v0);
|
|
|
- if (edgeInfo20 == null) {
|
|
|
- bMightAlreadyExist = false;
|
|
|
-
|
|
|
- // create the info
|
|
|
- edgeInfo20 = new EdgeInfo(v2, v0);
|
|
|
-
|
|
|
- // update the linked list on both
|
|
|
- edgeInfo20.m_nextV0 = edgeInfos.at(v2);
|
|
|
- edgeInfo20.m_nextV1 = edgeInfos.at(v0);
|
|
|
- edgeInfos.set(v2, edgeInfo20);
|
|
|
- edgeInfos.set(v0, edgeInfo20);
|
|
|
-
|
|
|
- // set face 0
|
|
|
- edgeInfo20.m_face0 = faceInfo;
|
|
|
- } else {
|
|
|
- if (edgeInfo20.m_face1 != null) {
|
|
|
- logger.fine("BuildStripifyInfo: > 2 triangles on an edge"
|
|
|
- + v2
|
|
|
- + ","
|
|
|
- + v0
|
|
|
- + "... uncertain consequences\n");
|
|
|
- } else {
|
|
|
- edgeInfo20.m_face1 = faceInfo;
|
|
|
- bFaceUpdated[2] = true;
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- if (bMightAlreadyExist) {
|
|
|
- if (!alreadyExists(faceInfo, faceInfos))
|
|
|
- faceInfos.add(faceInfo);
|
|
|
- else {
|
|
|
-
|
|
|
- //cleanup pointers that point to this deleted face
|
|
|
- if (bFaceUpdated[0])
|
|
|
- edgeInfo01.m_face1 = null;
|
|
|
- if (bFaceUpdated[1])
|
|
|
- edgeInfo12.m_face1 = null;
|
|
|
- if (bFaceUpdated[2])
|
|
|
- edgeInfo20.m_face1 = null;
|
|
|
- }
|
|
|
- } else {
|
|
|
- faceInfos.add(faceInfo);
|
|
|
- }
|
|
|
-
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- static boolean isDegenerate(FaceInfo face) {
|
|
|
- if (face.m_v0 == face.m_v1)
|
|
|
- return true;
|
|
|
- else if (face.m_v0 == face.m_v2)
|
|
|
- return true;
|
|
|
- else if (face.m_v1 == face.m_v2)
|
|
|
- return true;
|
|
|
- else
|
|
|
- return false;
|
|
|
- }
|
|
|
-
|
|
|
- static boolean isDegenerate(int v0, int v1, int v2) {
|
|
|
- if (v0 == v1)
|
|
|
- return true;
|
|
|
- else if (v0 == v2)
|
|
|
- return true;
|
|
|
- else if (v1 == v2)
|
|
|
- return true;
|
|
|
- else
|
|
|
- return false;
|
|
|
- }
|
|
|
-
|
|
|
- ///////////////////////////////////////////////////////////////////////////////////////////
|
|
|
- // GetNextIndex()
|
|
|
- //
|
|
|
- // Returns vertex of the input face which is "next" in the input index list
|
|
|
- //
|
|
|
- static int getNextIndex(IntVec indices, FaceInfo face) {
|
|
|
-
|
|
|
- int numIndices = indices.size();
|
|
|
-
|
|
|
- int v0 = indices.get(numIndices - 2);
|
|
|
- int v1 = indices.get(numIndices - 1);
|
|
|
-
|
|
|
- int fv0 = face.m_v0;
|
|
|
- int fv1 = face.m_v1;
|
|
|
- int fv2 = face.m_v2;
|
|
|
-
|
|
|
- if (fv0 != v0 && fv0 != v1) {
|
|
|
- if ((fv1 != v0 && fv1 != v1) || (fv2 != v0 && fv2 != v1)) {
|
|
|
- logger.fine("GetNextIndex: Triangle doesn't have all of its vertices\n");
|
|
|
- logger.fine("GetNextIndex: Duplicate triangle probably got us derailed\n");
|
|
|
- }
|
|
|
- return fv0;
|
|
|
- }
|
|
|
- if (fv1 != v0 && fv1 != v1) {
|
|
|
- if ((fv0 != v0 && fv0 != v1) || (fv2 != v0 && fv2 != v1)) {
|
|
|
- logger.fine("GetNextIndex: Triangle doesn't have all of its vertices\n");
|
|
|
- logger.fine("GetNextIndex: Duplicate triangle probably got us derailed\n");
|
|
|
- }
|
|
|
- return fv1;
|
|
|
- }
|
|
|
- if (fv2 != v0 && fv2 != v1) {
|
|
|
- if ((fv0 != v0 && fv0 != v1) || (fv1 != v0 && fv1 != v1)) {
|
|
|
- logger.fine("GetNextIndex: Triangle doesn't have all of its vertices\n");
|
|
|
- logger.fine("GetNextIndex: Duplicate triangle probably got us derailed\n");
|
|
|
- }
|
|
|
- return fv2;
|
|
|
- }
|
|
|
-
|
|
|
- // shouldn't get here, but let's try and fail gracefully
|
|
|
- if ((fv0 == fv1) || (fv0 == fv2))
|
|
|
- return fv0;
|
|
|
- else if ((fv1 == fv0) || (fv1 == fv2))
|
|
|
- return fv1;
|
|
|
- else if ((fv2 == fv0) || (fv2 == fv1))
|
|
|
- return fv2;
|
|
|
- else
|
|
|
- return -1;
|
|
|
- }
|
|
|
-
|
|
|
- ///////////////////////////////////////////////////////////////////////////////////////////
|
|
|
- // FindStartPoint()
|
|
|
- //
|
|
|
- // Finds a good starting point, namely one which has only one neighbor
|
|
|
- //
|
|
|
- static int findStartPoint(FaceInfoVec faceInfos, EdgeInfoVec edgeInfos) {
|
|
|
- int bestCtr = -1;
|
|
|
- int bestIndex = -1;
|
|
|
-
|
|
|
- for (int i = 0; i < faceInfos.size(); i++) {
|
|
|
- int ctr = 0;
|
|
|
-
|
|
|
- if (findOtherFace(edgeInfos, faceInfos.at(i).m_v0,
|
|
|
- faceInfos.at(i).m_v1, faceInfos.at(i)) == null)
|
|
|
- ctr++;
|
|
|
- if (findOtherFace(edgeInfos, faceInfos.at(i).m_v1,
|
|
|
- faceInfos.at(i).m_v2, faceInfos.at(i)) == null)
|
|
|
- ctr++;
|
|
|
- if (findOtherFace(edgeInfos, faceInfos.at(i).m_v2,
|
|
|
- faceInfos.at(i).m_v0, faceInfos.at(i)) == null)
|
|
|
- ctr++;
|
|
|
- if (ctr > bestCtr) {
|
|
|
- bestCtr = ctr;
|
|
|
- bestIndex = i;
|
|
|
- //return i;
|
|
|
- }
|
|
|
- }
|
|
|
- //return -1;
|
|
|
-
|
|
|
- if (bestCtr == 0)
|
|
|
- return -1;
|
|
|
-
|
|
|
- return bestIndex;
|
|
|
- }
|
|
|
-
|
|
|
- ///////////////////////////////////////////////////////////////////////////////////////////
|
|
|
- // FindGoodResetPoint()
|
|
|
- //
|
|
|
- // A good reset point is one near other commited areas so that
|
|
|
- // we know that when we've made the longest strips its because
|
|
|
- // we're stripifying in the same general orientation.
|
|
|
- //
|
|
|
- FaceInfo findGoodResetPoint(FaceInfoVec faceInfos, EdgeInfoVec edgeInfos) {
|
|
|
- // we hop into different areas of the mesh to try to get
|
|
|
- // other large open spans done. Areas of small strips can
|
|
|
- // just be left to triangle lists added at the end.
|
|
|
- FaceInfo result = null;
|
|
|
-
|
|
|
- if (result == null) {
|
|
|
- int numFaces = faceInfos.size();
|
|
|
- int startPoint;
|
|
|
- if (bFirstTimeResetPoint) {
|
|
|
- //first time, find a face with few neighbors (look for an edge
|
|
|
- // of the mesh)
|
|
|
- startPoint = findStartPoint(faceInfos, edgeInfos);
|
|
|
- bFirstTimeResetPoint = false;
|
|
|
- } else
|
|
|
- startPoint = (int) (((float) numFaces - 1) * meshJump);
|
|
|
-
|
|
|
- if (startPoint == -1) {
|
|
|
- startPoint = (int) (((float) numFaces - 1) * meshJump);
|
|
|
-
|
|
|
- //meshJump += 0.1f;
|
|
|
- //if (meshJump > 1.0f)
|
|
|
- // meshJump = .05f;
|
|
|
- }
|
|
|
-
|
|
|
- int i = startPoint;
|
|
|
- do {
|
|
|
-
|
|
|
- // if this guy isn't visited, try him
|
|
|
- if (faceInfos.at(i).m_stripId < 0) {
|
|
|
- result = faceInfos.at(i);
|
|
|
- break;
|
|
|
- }
|
|
|
-
|
|
|
- // update the index and clamp to 0-(numFaces-1)
|
|
|
- if (++i >= numFaces)
|
|
|
- i = 0;
|
|
|
-
|
|
|
- } while (i != startPoint);
|
|
|
-
|
|
|
- // update the meshJump
|
|
|
- meshJump += 0.1f;
|
|
|
- if (meshJump > 1.0f)
|
|
|
- meshJump = .05f;
|
|
|
- }
|
|
|
-
|
|
|
- // return the best face we found
|
|
|
- return result;
|
|
|
- }
|
|
|
-
|
|
|
- ///////////////////////////////////////////////////////////////////////////////////////////
|
|
|
- // GetUniqueVertexInB()
|
|
|
- //
|
|
|
- // Returns the vertex unique to faceB
|
|
|
- //
|
|
|
- static int getUniqueVertexInB(FaceInfo faceA, FaceInfo faceB) {
|
|
|
-
|
|
|
- int facev0 = faceB.m_v0;
|
|
|
- if (facev0 != faceA.m_v0 && facev0 != faceA.m_v1
|
|
|
- && facev0 != faceA.m_v2)
|
|
|
- return facev0;
|
|
|
-
|
|
|
- int facev1 = faceB.m_v1;
|
|
|
- if (facev1 != faceA.m_v0 && facev1 != faceA.m_v1
|
|
|
- && facev1 != faceA.m_v2)
|
|
|
- return facev1;
|
|
|
-
|
|
|
- int facev2 = faceB.m_v2;
|
|
|
- if (facev2 != faceA.m_v0 && facev2 != faceA.m_v1
|
|
|
- && facev2 != faceA.m_v2)
|
|
|
- return facev2;
|
|
|
-
|
|
|
- // nothing is different
|
|
|
- return -1;
|
|
|
- }
|
|
|
-
|
|
|
- ///////////////////////////////////////////////////////////////////////////////////////////
|
|
|
- // GetSharedVertices()
|
|
|
- //
|
|
|
- // Returns the (at most) two vertices shared between the two faces
|
|
|
- //
|
|
|
- static void getSharedVertices(FaceInfo faceA, FaceInfo faceB, int[] vertex) {
|
|
|
- vertex[0] = -1;
|
|
|
- vertex[1] = -1;
|
|
|
-
|
|
|
- int facev0 = faceB.m_v0;
|
|
|
- if (facev0 == faceA.m_v0 || facev0 == faceA.m_v1
|
|
|
- || facev0 == faceA.m_v2) {
|
|
|
- if (vertex[0] == -1)
|
|
|
- vertex[0] = facev0;
|
|
|
- else {
|
|
|
- vertex[1] = facev0;
|
|
|
- return;
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- int facev1 = faceB.m_v1;
|
|
|
- if (facev1 == faceA.m_v0 || facev1 == faceA.m_v1
|
|
|
- || facev1 == faceA.m_v2) {
|
|
|
- if (vertex[0] == -1)
|
|
|
- vertex[0] = facev1;
|
|
|
- else {
|
|
|
- vertex[1] = facev1;
|
|
|
- return;
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- int facev2 = faceB.m_v2;
|
|
|
- if (facev2 == faceA.m_v0 || facev2 == faceA.m_v1
|
|
|
- || facev2 == faceA.m_v2) {
|
|
|
- if (vertex[0] == -1)
|
|
|
- vertex[0] = facev2;
|
|
|
- else {
|
|
|
- vertex[1] = facev2;
|
|
|
- return;
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- }
|
|
|
-
|
|
|
- ///////////////////////////////////////////////////////////////////////////////////////////
|
|
|
- // CommitStrips()
|
|
|
- //
|
|
|
- // "Commits" the input strips by setting their m_experimentId to -1 and
|
|
|
- // adding to the allStrips
|
|
|
- // vector
|
|
|
- //
|
|
|
- static void commitStrips(StripInfoVec allStrips, StripInfoVec strips) {
|
|
|
- // Iterate through strips
|
|
|
- int numStrips = strips.size();
|
|
|
- for (int i = 0; i < numStrips; i++) {
|
|
|
-
|
|
|
- // Tell the strip that it is now real
|
|
|
- StripInfo strip = strips.at(i);
|
|
|
- strip.m_experimentId = -1;
|
|
|
-
|
|
|
- // add to the list of real strips
|
|
|
- allStrips.add(strip);
|
|
|
-
|
|
|
- // Iterate through the faces of the strip
|
|
|
- // Tell the faces of the strip that they belong to a real strip now
|
|
|
- FaceInfoVec faces = strips.at(i).m_faces;
|
|
|
- int numFaces = faces.size();
|
|
|
-
|
|
|
- for (int j = 0; j < numFaces; j++) {
|
|
|
- strip.markTriangle(faces.at(j));
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- ///////////////////////////////////////////////////////////////////////////////////////////
|
|
|
- // NextIsCW()
|
|
|
- //
|
|
|
- // Returns true if the next face should be ordered in CW fashion
|
|
|
- //
|
|
|
- static boolean nextIsCW(int numIndices) {
|
|
|
- return ((numIndices % 2) == 0);
|
|
|
- }
|
|
|
-
|
|
|
- ///////////////////////////////////////////////////////////////////////////////////////////
|
|
|
- // UpdateCacheFace()
|
|
|
- //
|
|
|
- // Updates the input vertex cache with this face's vertices
|
|
|
- //
|
|
|
- static void updateCacheFace(VertexCache vcache, FaceInfo face) {
|
|
|
- if (!vcache.inCache(face.m_v0))
|
|
|
- vcache.addEntry(face.m_v0);
|
|
|
-
|
|
|
- if (!vcache.inCache(face.m_v1))
|
|
|
- vcache.addEntry(face.m_v1);
|
|
|
-
|
|
|
- if (!vcache.inCache(face.m_v2))
|
|
|
- vcache.addEntry(face.m_v2);
|
|
|
- }
|
|
|
-
|
|
|
- ///////////////////////////////////////////////////////////////////////////////////////////
|
|
|
- // UpdateCacheStrip()
|
|
|
- //
|
|
|
- // Updates the input vertex cache with this strip's vertices
|
|
|
- //
|
|
|
- static void updateCacheStrip(VertexCache vcache, StripInfo strip) {
|
|
|
- for (int i = 0; i < strip.m_faces.size(); ++i) {
|
|
|
- if (!vcache.inCache(strip.m_faces.at(i).m_v0))
|
|
|
- vcache.addEntry(strip.m_faces.at(i).m_v0);
|
|
|
-
|
|
|
- if (!vcache.inCache(strip.m_faces.at(i).m_v1))
|
|
|
- vcache.addEntry(strip.m_faces.at(i).m_v1);
|
|
|
-
|
|
|
- if (!vcache.inCache(strip.m_faces.at(i).m_v2))
|
|
|
- vcache.addEntry(strip.m_faces.at(i).m_v2);
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- ///////////////////////////////////////////////////////////////////////////////////////////
|
|
|
- // CalcNumHitsStrip()
|
|
|
- //
|
|
|
- // returns the number of cache hits per face in the strip
|
|
|
- //
|
|
|
- static float calcNumHitsStrip(VertexCache vcache, StripInfo strip) {
|
|
|
- int numHits = 0;
|
|
|
- int numFaces = 0;
|
|
|
-
|
|
|
- for (int i = 0; i < strip.m_faces.size(); i++) {
|
|
|
- if (vcache.inCache(strip.m_faces.at(i).m_v0))
|
|
|
- ++numHits;
|
|
|
-
|
|
|
- if (vcache.inCache(strip.m_faces.at(i).m_v1))
|
|
|
- ++numHits;
|
|
|
-
|
|
|
- if (vcache.inCache(strip.m_faces.at(i).m_v2))
|
|
|
- ++numHits;
|
|
|
-
|
|
|
- numFaces++;
|
|
|
- }
|
|
|
-
|
|
|
- return ((float) numHits / (float) numFaces);
|
|
|
- }
|
|
|
-
|
|
|
- ///////////////////////////////////////////////////////////////////////////////////////////
|
|
|
- // AvgStripSize()
|
|
|
- //
|
|
|
- // Finds the average strip size of the input vector of strips
|
|
|
- //
|
|
|
- static float avgStripSize(StripInfoVec strips) {
|
|
|
- int sizeAccum = 0;
|
|
|
- int numStrips = strips.size();
|
|
|
- for (int i = 0; i < numStrips; i++) {
|
|
|
- StripInfo strip = strips.at(i);
|
|
|
- sizeAccum += strip.m_faces.size();
|
|
|
- sizeAccum -= strip.m_numDegenerates;
|
|
|
- }
|
|
|
- return ((float) sizeAccum) / ((float) numStrips);
|
|
|
- }
|
|
|
-
|
|
|
- ///////////////////////////////////////////////////////////////////////////////////////////
|
|
|
- // CalcNumHitsFace()
|
|
|
- //
|
|
|
- // returns the number of cache hits in the face
|
|
|
- //
|
|
|
- static int calcNumHitsFace(VertexCache vcache, FaceInfo face) {
|
|
|
- int numHits = 0;
|
|
|
-
|
|
|
- if (vcache.inCache(face.m_v0))
|
|
|
- numHits++;
|
|
|
-
|
|
|
- if (vcache.inCache(face.m_v1))
|
|
|
- numHits++;
|
|
|
-
|
|
|
- if (vcache.inCache(face.m_v2))
|
|
|
- numHits++;
|
|
|
-
|
|
|
- return numHits;
|
|
|
- }
|
|
|
-
|
|
|
- ///////////////////////////////////////////////////////////////////////////////////////////
|
|
|
- // NumNeighbors()
|
|
|
- //
|
|
|
- // Returns the number of neighbors that this face has
|
|
|
- //
|
|
|
- static int numNeighbors(FaceInfo face, EdgeInfoVec edgeInfoVec) {
|
|
|
- int numNeighbors = 0;
|
|
|
-
|
|
|
- if (findOtherFace(edgeInfoVec, face.m_v0, face.m_v1, face) != null) {
|
|
|
- numNeighbors++;
|
|
|
- }
|
|
|
-
|
|
|
- if (findOtherFace(edgeInfoVec, face.m_v1, face.m_v2, face) != null) {
|
|
|
- numNeighbors++;
|
|
|
- }
|
|
|
-
|
|
|
- if (findOtherFace(edgeInfoVec, face.m_v2, face.m_v0, face) != null) {
|
|
|
- numNeighbors++;
|
|
|
- }
|
|
|
-
|
|
|
- return numNeighbors;
|
|
|
- }
|
|
|
-
|
|
|
- ///////////////////////////////////////////////////////////////////////////////////////////
|
|
|
- // IsCW()
|
|
|
- //
|
|
|
- // Returns true if the face is ordered in CW fashion
|
|
|
- //
|
|
|
- static boolean isCW(FaceInfo faceInfo, int v0, int v1) {
|
|
|
- if (faceInfo.m_v0 == v0)
|
|
|
- return (faceInfo.m_v1 == v1);
|
|
|
- else if (faceInfo.m_v1 == v0)
|
|
|
- return (faceInfo.m_v2 == v1);
|
|
|
- else
|
|
|
- return (faceInfo.m_v0 == v1);
|
|
|
-
|
|
|
- }
|
|
|
-
|
|
|
- static boolean faceContainsIndex(FaceInfo face, int index) {
|
|
|
- return ((face.m_v0 == index) || (face.m_v1 == index) || (face.m_v2 == index));
|
|
|
- }
|
|
|
-
|
|
|
- ///////////////////////////////////////////////////////////////////////////////////////////
|
|
|
- // FindTraversal()
|
|
|
- //
|
|
|
- // Finds the next face to start the next strip on.
|
|
|
- //
|
|
|
- static boolean findTraversal(FaceInfoVec faceInfos, EdgeInfoVec edgeInfos,
|
|
|
- StripInfo strip, StripStartInfo startInfo) {
|
|
|
-
|
|
|
- // if the strip was v0.v1 on the edge, then v1 will be a vertex in the
|
|
|
- // next edge.
|
|
|
- int v = (strip.m_startInfo.m_toV1 ? strip.m_startInfo.m_startEdge.m_v1
|
|
|
- : strip.m_startInfo.m_startEdge.m_v0);
|
|
|
-
|
|
|
- FaceInfo untouchedFace = null;
|
|
|
- EdgeInfo edgeIter = edgeInfos.at(v);
|
|
|
- while (edgeIter != null) {
|
|
|
- FaceInfo face0 = edgeIter.m_face0;
|
|
|
- FaceInfo face1 = edgeIter.m_face1;
|
|
|
- if ((face0 != null && !strip.isInStrip(face0)) && face1 != null
|
|
|
- && !strip.isMarked(face1)) {
|
|
|
- untouchedFace = face1;
|
|
|
- break;
|
|
|
- }
|
|
|
- if ((face1 != null && !strip.isInStrip(face1)) && face0 != null
|
|
|
- && !strip.isMarked(face0)) {
|
|
|
- untouchedFace = face0;
|
|
|
- break;
|
|
|
- }
|
|
|
-
|
|
|
- // find the next edgeIter
|
|
|
- edgeIter = (edgeIter.m_v0 == v ? edgeIter.m_nextV0
|
|
|
- : edgeIter.m_nextV1);
|
|
|
- }
|
|
|
-
|
|
|
- startInfo.m_startFace = untouchedFace;
|
|
|
- startInfo.m_startEdge = edgeIter;
|
|
|
- if (edgeIter != null) {
|
|
|
- if (strip.sharesEdge(startInfo.m_startFace, edgeInfos))
|
|
|
- startInfo.m_toV1 = (edgeIter.m_v0 == v); //note! used to be
|
|
|
- // m_v1
|
|
|
- else
|
|
|
- startInfo.m_toV1 = (edgeIter.m_v1 == v);
|
|
|
- }
|
|
|
- return (startInfo.m_startFace != null);
|
|
|
- }
|
|
|
-
|
|
|
- ////////////////////////////////////////////////////////////////////////////////////////
|
|
|
- // RemoveSmallStrips()
|
|
|
- //
|
|
|
- // allStrips is the whole strip vector...all small strips will be deleted
|
|
|
- // from this list, to avoid leaking mem
|
|
|
- // allBigStrips is an out parameter which will contain all strips above
|
|
|
- // minStripLength
|
|
|
- // faceList is an out parameter which will contain all faces which were
|
|
|
- // removed from the striplist
|
|
|
- //
|
|
|
- void removeSmallStrips(StripInfoVec allStrips, StripInfoVec allBigStrips,
|
|
|
- FaceInfoVec faceList) {
|
|
|
- faceList.clear();
|
|
|
- allBigStrips.clear(); //make sure these are empty
|
|
|
- FaceInfoVec tempFaceList = new FaceInfoVec();
|
|
|
-
|
|
|
- for (int i = 0; i < allStrips.size(); i++) {
|
|
|
- if (allStrips.at(i).m_faces.size() < minStripLength) {
|
|
|
- //strip is too small, add faces to faceList
|
|
|
- for (int j = 0; j < allStrips.at(i).m_faces.size(); j++)
|
|
|
- tempFaceList.add(allStrips.at(i).m_faces.at(j));
|
|
|
-
|
|
|
- } else {
|
|
|
- allBigStrips.add(allStrips.at(i));
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- boolean[] bVisitedList = new boolean[tempFaceList.size()];
|
|
|
-
|
|
|
- VertexCache vcache = new VertexCache(cacheSize);
|
|
|
-
|
|
|
- int bestNumHits = -1;
|
|
|
- int numHits;
|
|
|
- int bestIndex = -9999;
|
|
|
-
|
|
|
- while (true) {
|
|
|
- bestNumHits = -1;
|
|
|
-
|
|
|
- //find best face to add next, given the current cache
|
|
|
- for (int i = 0; i < tempFaceList.size(); i++) {
|
|
|
- if (bVisitedList[i])
|
|
|
- continue;
|
|
|
-
|
|
|
- numHits = calcNumHitsFace(vcache, tempFaceList.at(i));
|
|
|
- if (numHits > bestNumHits) {
|
|
|
- bestNumHits = numHits;
|
|
|
- bestIndex = i;
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- if (bestNumHits == -1.0f)
|
|
|
- break;
|
|
|
- bVisitedList[bestIndex] = true;
|
|
|
- updateCacheFace(vcache, tempFaceList.at(bestIndex));
|
|
|
- faceList.add(tempFaceList.at(bestIndex));
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- ////////////////////////////////////////////////////////////////////////////////////////
|
|
|
- // CreateStrips()
|
|
|
- //
|
|
|
- // Generates actual strips from the list-in-strip-order.
|
|
|
- //
|
|
|
- int createStrips(StripInfoVec allStrips, IntVec stripIndices,
|
|
|
- boolean bStitchStrips) {
|
|
|
- int numSeparateStrips = 0;
|
|
|
-
|
|
|
- FaceInfo tLastFace = new FaceInfo(0, 0, 0);
|
|
|
- int nStripCount = allStrips.size();
|
|
|
-
|
|
|
- //we infer the cw/ccw ordering depending on the number of indices
|
|
|
- //this is screwed up by the fact that we insert -1s to denote changing
|
|
|
- // strips
|
|
|
- //this is to account for that
|
|
|
- int accountForNegatives = 0;
|
|
|
-
|
|
|
- for (int i = 0; i < nStripCount; i++) {
|
|
|
- StripInfo strip = allStrips.at(i);
|
|
|
- int nStripFaceCount = strip.m_faces.size();
|
|
|
-
|
|
|
- // Handle the first face in the strip
|
|
|
- {
|
|
|
- FaceInfo tFirstFace = new FaceInfo(strip.m_faces.at(0).m_v0,
|
|
|
- strip.m_faces.at(0).m_v1, strip.m_faces.at(0).m_v2);
|
|
|
-
|
|
|
- // If there is a second face, reorder vertices such that the
|
|
|
- // unique vertex is first
|
|
|
- if (nStripFaceCount > 1) {
|
|
|
- int nUnique = getUniqueVertexInB(strip.m_faces.at(1),
|
|
|
- tFirstFace);
|
|
|
- if (nUnique == tFirstFace.m_v1) {
|
|
|
- int tmp = tFirstFace.m_v0;
|
|
|
- tFirstFace.m_v0 = tFirstFace.m_v1;
|
|
|
- tFirstFace.m_v1 = tmp;
|
|
|
- } else if (nUnique == tFirstFace.m_v2) {
|
|
|
- int tmp = tFirstFace.m_v0;
|
|
|
- tFirstFace.m_v0 = tFirstFace.m_v2;
|
|
|
- tFirstFace.m_v2 = tmp;
|
|
|
- }
|
|
|
-
|
|
|
- // If there is a third face, reorder vertices such that the
|
|
|
- // shared vertex is last
|
|
|
- if (nStripFaceCount > 2) {
|
|
|
- if (isDegenerate(strip.m_faces.at(1))) {
|
|
|
- int pivot = strip.m_faces.at(1).m_v1;
|
|
|
- if (tFirstFace.m_v1 == pivot) {
|
|
|
- int tmp = tFirstFace.m_v1;
|
|
|
- tFirstFace.m_v1 = tFirstFace.m_v2;
|
|
|
- tFirstFace.m_v2 = tmp;
|
|
|
- }
|
|
|
- } else {
|
|
|
- int[] nShared = new int[2];
|
|
|
- getSharedVertices(strip.m_faces.at(2), tFirstFace,
|
|
|
- nShared);
|
|
|
- if ((nShared[0] == tFirstFace.m_v1)
|
|
|
- && (nShared[1] == -1)) {
|
|
|
- int tmp = tFirstFace.m_v1;
|
|
|
- tFirstFace.m_v1 = tFirstFace.m_v2;
|
|
|
- tFirstFace.m_v2 = tmp;
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- if ((i == 0) || !bStitchStrips) {
|
|
|
- if (!isCW(strip.m_faces.at(0), tFirstFace.m_v0,
|
|
|
- tFirstFace.m_v1))
|
|
|
- stripIndices.add(tFirstFace.m_v0);
|
|
|
- } else {
|
|
|
- // Double tap the first in the new strip
|
|
|
- stripIndices.add(tFirstFace.m_v0);
|
|
|
-
|
|
|
- // Check CW/CCW ordering
|
|
|
- if (nextIsCW(stripIndices.size() - accountForNegatives) != isCW(
|
|
|
- strip.m_faces.at(0), tFirstFace.m_v0,
|
|
|
- tFirstFace.m_v1)) {
|
|
|
- stripIndices.add(tFirstFace.m_v0);
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- stripIndices.add(tFirstFace.m_v0);
|
|
|
- stripIndices.add(tFirstFace.m_v1);
|
|
|
- stripIndices.add(tFirstFace.m_v2);
|
|
|
-
|
|
|
- // Update last face info
|
|
|
- tLastFace.set(tFirstFace);
|
|
|
- }
|
|
|
-
|
|
|
- for (int j = 1; j < nStripFaceCount; j++) {
|
|
|
- int nUnique = getUniqueVertexInB(tLastFace, strip.m_faces.at(j));
|
|
|
- if (nUnique != -1) {
|
|
|
- stripIndices.add(nUnique);
|
|
|
-
|
|
|
- // Update last face info
|
|
|
- tLastFace.m_v0 = tLastFace.m_v1;
|
|
|
- tLastFace.m_v1 = tLastFace.m_v2;
|
|
|
- tLastFace.m_v2 = nUnique;
|
|
|
- } else {
|
|
|
- //we've hit a degenerate
|
|
|
- stripIndices.add(strip.m_faces.at(j).m_v2);
|
|
|
- tLastFace.m_v0 = strip.m_faces.at(j).m_v0; //tLastFace.m_v1;
|
|
|
- tLastFace.m_v1 = strip.m_faces.at(j).m_v1; //tLastFace.m_v2;
|
|
|
- tLastFace.m_v2 = strip.m_faces.at(j).m_v2; //tLastFace.m_v1;
|
|
|
-
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- // Double tap between strips.
|
|
|
- if (bStitchStrips) {
|
|
|
- if (i != nStripCount - 1)
|
|
|
- stripIndices.add(tLastFace.m_v2);
|
|
|
- } else {
|
|
|
- //-1 index indicates next strip
|
|
|
- stripIndices.add(-1);
|
|
|
- accountForNegatives++;
|
|
|
- numSeparateStrips++;
|
|
|
- }
|
|
|
-
|
|
|
- // Update last face info
|
|
|
- tLastFace.m_v0 = tLastFace.m_v1;
|
|
|
- tLastFace.m_v1 = tLastFace.m_v2;
|
|
|
- tLastFace.m_v2 = tLastFace.m_v2;
|
|
|
- }
|
|
|
-
|
|
|
- if (bStitchStrips)
|
|
|
- numSeparateStrips = 1;
|
|
|
- return numSeparateStrips;
|
|
|
- }
|
|
|
-
|
|
|
- ///////////////////////////////////////////////////////////////////////////////////////////
|
|
|
- // FindAllStrips()
|
|
|
- //
|
|
|
- // Does the stripification, puts output strips into vector allStrips
|
|
|
- //
|
|
|
- // Works by setting runnning a number of experiments in different areas of
|
|
|
- // the mesh, and
|
|
|
- // accepting the one which results in the longest strips. It then accepts
|
|
|
- // this, and moves
|
|
|
- // on to a different area of the mesh. We try to jump around the mesh some,
|
|
|
- // to ensure that
|
|
|
- // large open spans of strips get generated.
|
|
|
- //
|
|
|
- void findAllStrips(StripInfoVec allStrips, FaceInfoVec allFaceInfos,
|
|
|
- EdgeInfoVec allEdgeInfos, int numSamples) {
|
|
|
- // the experiments
|
|
|
- int experimentId = 0;
|
|
|
- int stripId = 0;
|
|
|
- boolean done = false;
|
|
|
-
|
|
|
- int loopCtr = 0;
|
|
|
-
|
|
|
- while (!done) {
|
|
|
- loopCtr++;
|
|
|
-
|
|
|
- //
|
|
|
- // PHASE 1: Set up numSamples * numEdges experiments
|
|
|
- //
|
|
|
- StripInfoVec[] experiments = new StripInfoVec[numSamples * 6];
|
|
|
- for (int i = 0; i < experiments.length; i++)
|
|
|
- experiments[i] = new StripInfoVec();
|
|
|
-
|
|
|
- int experimentIndex = 0;
|
|
|
- HashSet<FaceInfo> resetPoints = new HashSet<FaceInfo>(); /* NvFaceInfo */
|
|
|
- for (int i = 0; i < numSamples; i++) {
|
|
|
- // Try to find another good reset point.
|
|
|
- // If there are none to be found, we are done
|
|
|
- FaceInfo nextFace = findGoodResetPoint(allFaceInfos,
|
|
|
- allEdgeInfos);
|
|
|
- if (nextFace == null) {
|
|
|
- done = true;
|
|
|
- break;
|
|
|
- }
|
|
|
- // If we have already evaluated starting at this face in this
|
|
|
- // slew of experiments, then skip going any further
|
|
|
- else if (resetPoints.contains(nextFace)) {
|
|
|
- continue;
|
|
|
- }
|
|
|
-
|
|
|
- // trying it now...
|
|
|
- resetPoints.add(nextFace);
|
|
|
-
|
|
|
- // otherwise, we shall now try experiments for starting on the
|
|
|
- // 01,12, and 20 edges
|
|
|
-
|
|
|
- // build the strip off of this face's 0-1 edge
|
|
|
- EdgeInfo edge01 = findEdgeInfo(allEdgeInfos, nextFace.m_v0,
|
|
|
- nextFace.m_v1);
|
|
|
- StripInfo strip01 = new StripInfo(new StripStartInfo(nextFace,
|
|
|
- edge01, true), stripId++, experimentId++);
|
|
|
- experiments[experimentIndex++].add(strip01);
|
|
|
-
|
|
|
- // build the strip off of this face's 1-0 edge
|
|
|
- EdgeInfo edge10 = findEdgeInfo(allEdgeInfos, nextFace.m_v0,
|
|
|
- nextFace.m_v1);
|
|
|
- StripInfo strip10 = new StripInfo(new StripStartInfo(nextFace,
|
|
|
- edge10, false), stripId++, experimentId++);
|
|
|
- experiments[experimentIndex++].add(strip10);
|
|
|
-
|
|
|
- // build the strip off of this face's 1-2 edge
|
|
|
- EdgeInfo edge12 = findEdgeInfo(allEdgeInfos, nextFace.m_v1,
|
|
|
- nextFace.m_v2);
|
|
|
- StripInfo strip12 = new StripInfo(new StripStartInfo(nextFace,
|
|
|
- edge12, true), stripId++, experimentId++);
|
|
|
- experiments[experimentIndex++].add(strip12);
|
|
|
-
|
|
|
- // build the strip off of this face's 2-1 edge
|
|
|
- EdgeInfo edge21 = findEdgeInfo(allEdgeInfos, nextFace.m_v1,
|
|
|
- nextFace.m_v2);
|
|
|
- StripInfo strip21 = new StripInfo(new StripStartInfo(nextFace,
|
|
|
- edge21, false), stripId++, experimentId++);
|
|
|
- experiments[experimentIndex++].add(strip21);
|
|
|
-
|
|
|
- // build the strip off of this face's 2-0 edge
|
|
|
- EdgeInfo edge20 = findEdgeInfo(allEdgeInfos, nextFace.m_v2,
|
|
|
- nextFace.m_v0);
|
|
|
- StripInfo strip20 = new StripInfo(new StripStartInfo(nextFace,
|
|
|
- edge20, true), stripId++, experimentId++);
|
|
|
- experiments[experimentIndex++].add(strip20);
|
|
|
-
|
|
|
- // build the strip off of this face's 0-2 edge
|
|
|
- EdgeInfo edge02 = findEdgeInfo(allEdgeInfos, nextFace.m_v2,
|
|
|
- nextFace.m_v0);
|
|
|
- StripInfo strip02 = new StripInfo(new StripStartInfo(nextFace,
|
|
|
- edge02, false), stripId++, experimentId++);
|
|
|
- experiments[experimentIndex++].add(strip02);
|
|
|
- }
|
|
|
-
|
|
|
- //
|
|
|
- // PHASE 2: Iterate through that we setup in the last phase
|
|
|
- // and really build each of the strips and strips that follow to
|
|
|
- // see how
|
|
|
- // far we get
|
|
|
- //
|
|
|
- int numExperiments = experimentIndex;
|
|
|
- for (int i = 0; i < numExperiments; i++) {
|
|
|
-
|
|
|
- // get the strip set
|
|
|
-
|
|
|
- // build the first strip of the list
|
|
|
- experiments[i].at(0).build(allEdgeInfos, allFaceInfos);
|
|
|
- int experimentId2 = experiments[i].at(0).m_experimentId;
|
|
|
-
|
|
|
- StripInfo stripIter = experiments[i].at(0);
|
|
|
- StripStartInfo startInfo = new StripStartInfo(null, null, false);
|
|
|
- while (findTraversal(allFaceInfos, allEdgeInfos, stripIter,
|
|
|
- startInfo)) {
|
|
|
-
|
|
|
- // create the new strip info
|
|
|
- //TODO startInfo clone ?
|
|
|
- stripIter = new StripInfo(startInfo, stripId++,
|
|
|
- experimentId2);
|
|
|
-
|
|
|
- // build the next strip
|
|
|
- stripIter.build(allEdgeInfos, allFaceInfos);
|
|
|
-
|
|
|
- // add it to the list
|
|
|
- experiments[i].add(stripIter);
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- //
|
|
|
- // Phase 3: Find the experiment that has the most promise
|
|
|
- //
|
|
|
- int bestIndex = 0;
|
|
|
- double bestValue = 0;
|
|
|
- for (int i = 0; i < numExperiments; i++) {
|
|
|
- float avgStripSizeWeight = 1.0f;
|
|
|
- //float numTrisWeight = 0.0f;
|
|
|
- float numStripsWeight = 0.0f;
|
|
|
- float avgStripSize = avgStripSize(experiments[i]);
|
|
|
- float numStrips = experiments[i].size();
|
|
|
- float value = avgStripSize * avgStripSizeWeight
|
|
|
- + (numStrips * numStripsWeight);
|
|
|
- //float value = 1.f / numStrips;
|
|
|
- //float value = numStrips * avgStripSize;
|
|
|
-
|
|
|
- if (value > bestValue) {
|
|
|
- bestValue = value;
|
|
|
- bestIndex = i;
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- //
|
|
|
- // Phase 4: commit the best experiment of the bunch
|
|
|
- //
|
|
|
- commitStrips(allStrips, experiments[bestIndex]);
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- ///////////////////////////////////////////////////////////////////////////////////////////
|
|
|
- // SplitUpStripsAndOptimize()
|
|
|
- //
|
|
|
- // Splits the input vector of strips (allBigStrips) into smaller, cache
|
|
|
- // friendly pieces, then
|
|
|
- // reorders these pieces to maximize cache hits
|
|
|
- // The final strips are output through outStrips
|
|
|
- //
|
|
|
- void splitUpStripsAndOptimize(StripInfoVec allStrips,
|
|
|
- StripInfoVec outStrips, EdgeInfoVec edgeInfos,
|
|
|
- FaceInfoVec outFaceList) {
|
|
|
- int threshold = cacheSize;
|
|
|
- StripInfoVec tempStrips = new StripInfoVec();
|
|
|
- int j;
|
|
|
-
|
|
|
- //split up strips into threshold-sized pieces
|
|
|
- for (int i = 0; i < allStrips.size(); i++) {
|
|
|
- StripInfo currentStrip;
|
|
|
- StripStartInfo startInfo = new StripStartInfo(null, null, false);
|
|
|
-
|
|
|
- int actualStripSize = 0;
|
|
|
- for (j = 0; j < allStrips.at(i).m_faces.size(); ++j) {
|
|
|
- if (!isDegenerate(allStrips.at(i).m_faces.at(j)))
|
|
|
- actualStripSize++;
|
|
|
- }
|
|
|
-
|
|
|
- if (actualStripSize /* allStrips.at(i).m_faces.size() */
|
|
|
- > threshold) {
|
|
|
-
|
|
|
- int numTimes = actualStripSize /* allStrips.at(i).m_faces.size() */
|
|
|
- / threshold;
|
|
|
- int numLeftover = actualStripSize /* allStrips.at(i).m_faces.size() */
|
|
|
- % threshold;
|
|
|
-
|
|
|
- int degenerateCount = 0;
|
|
|
- for (j = 0; j < numTimes; j++) {
|
|
|
- currentStrip = new StripInfo(startInfo, 0, -1);
|
|
|
-
|
|
|
- int faceCtr = j * threshold + degenerateCount;
|
|
|
- boolean bFirstTime = true;
|
|
|
- while (faceCtr < threshold + (j * threshold)
|
|
|
- + degenerateCount) {
|
|
|
- if (isDegenerate(allStrips.at(i).m_faces.at(faceCtr))) {
|
|
|
- degenerateCount++;
|
|
|
-
|
|
|
- //last time or first time through, no need for a
|
|
|
- // degenerate
|
|
|
- if ((((faceCtr + 1) != threshold + (j * threshold)
|
|
|
- + degenerateCount) || ((j == numTimes - 1)
|
|
|
- && (numLeftover < 4) && (numLeftover > 0)))
|
|
|
- && !bFirstTime) {
|
|
|
- currentStrip.m_faces
|
|
|
- .add(allStrips.at(i).m_faces
|
|
|
- .at(faceCtr++));
|
|
|
- } else
|
|
|
- ++faceCtr;
|
|
|
- } else {
|
|
|
- currentStrip.m_faces.add(allStrips.at(i).m_faces
|
|
|
- .at(faceCtr++));
|
|
|
- bFirstTime = false;
|
|
|
- }
|
|
|
- }
|
|
|
- /*
|
|
|
- * threshold; faceCtr < threshold+(j*threshold); faceCtr++) {
|
|
|
- * currentStrip.m_faces.add(allStrips.at(i).m_faces.at(faceCtr]); }
|
|
|
- */
|
|
|
- ///*
|
|
|
- if (j == numTimes - 1) //last time through
|
|
|
- {
|
|
|
- if ((numLeftover < 4) && (numLeftover > 0)) //way too
|
|
|
- // small
|
|
|
- {
|
|
|
- //just add to last strip
|
|
|
- int ctr = 0;
|
|
|
- while (ctr < numLeftover) {
|
|
|
- if (!isDegenerate(allStrips.at(i).m_faces
|
|
|
- .at(faceCtr))) {
|
|
|
- currentStrip.m_faces
|
|
|
- .add(allStrips.at(i).m_faces
|
|
|
- .at(faceCtr++));
|
|
|
- ++ctr;
|
|
|
- } else {
|
|
|
- currentStrip.m_faces
|
|
|
- .add(allStrips.at(i).m_faces
|
|
|
- .at(faceCtr++));
|
|
|
- ++degenerateCount;
|
|
|
- }
|
|
|
- }
|
|
|
- numLeftover = 0;
|
|
|
- }
|
|
|
- }
|
|
|
- //*/
|
|
|
- tempStrips.add(currentStrip);
|
|
|
- }
|
|
|
-
|
|
|
- int leftOff = j * threshold + degenerateCount;
|
|
|
-
|
|
|
- if (numLeftover != 0) {
|
|
|
- currentStrip = new StripInfo(startInfo, 0, -1);
|
|
|
-
|
|
|
- int ctr = 0;
|
|
|
- boolean bFirstTime = true;
|
|
|
- while (ctr < numLeftover) {
|
|
|
- if (!isDegenerate(allStrips.at(i).m_faces.at(leftOff))) {
|
|
|
- ctr++;
|
|
|
- bFirstTime = false;
|
|
|
- currentStrip.m_faces.add(allStrips.at(i).m_faces
|
|
|
- .at(leftOff++));
|
|
|
- } else if (!bFirstTime)
|
|
|
- currentStrip.m_faces.add(allStrips.at(i).m_faces
|
|
|
- .at(leftOff++));
|
|
|
- else
|
|
|
- leftOff++;
|
|
|
- }
|
|
|
- /*
|
|
|
- * for(int k = 0; k < numLeftover; k++) {
|
|
|
- * currentStrip.m_faces.add(allStrips.at(i).m_faces[leftOff++]); }
|
|
|
- */
|
|
|
-
|
|
|
- tempStrips.add(currentStrip);
|
|
|
- }
|
|
|
- } else {
|
|
|
- //we're not just doing a tempStrips.add(allBigStrips[i])
|
|
|
- // because
|
|
|
- // this way we can delete allBigStrips later to free the memory
|
|
|
- currentStrip = new StripInfo(startInfo, 0, -1);
|
|
|
-
|
|
|
- for (j = 0; j < allStrips.at(i).m_faces.size(); j++)
|
|
|
- currentStrip.m_faces.add(allStrips.at(i).m_faces.at(j));
|
|
|
-
|
|
|
- tempStrips.add(currentStrip);
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- //add small strips to face list
|
|
|
- StripInfoVec tempStrips2 = new StripInfoVec();
|
|
|
- removeSmallStrips(tempStrips, tempStrips2, outFaceList);
|
|
|
-
|
|
|
- outStrips.clear();
|
|
|
- //screw optimization for now
|
|
|
- // for(i = 0; i < tempStrips.size(); ++i)
|
|
|
- // outStrips.add(tempStrips[i]);
|
|
|
-
|
|
|
- if (tempStrips2.size() != 0) {
|
|
|
- //Optimize for the vertex cache
|
|
|
- VertexCache vcache = new VertexCache(cacheSize);
|
|
|
-
|
|
|
- float bestNumHits = -1.0f;
|
|
|
- float numHits;
|
|
|
- int bestIndex = -99999;
|
|
|
-
|
|
|
- int firstIndex = 0;
|
|
|
- float minCost = 10000.0f;
|
|
|
-
|
|
|
- for (int i = 0; i < tempStrips2.size(); i++) {
|
|
|
- int numNeighbors = 0;
|
|
|
-
|
|
|
- //find strip with least number of neighbors per face
|
|
|
- for (j = 0; j < tempStrips2.at(i).m_faces.size(); j++) {
|
|
|
- numNeighbors += numNeighbors(tempStrips2.at(i).m_faces
|
|
|
- .at(j), edgeInfos);
|
|
|
- }
|
|
|
-
|
|
|
- float currCost = (float) numNeighbors
|
|
|
- / (float) tempStrips2.at(i).m_faces.size();
|
|
|
- if (currCost < minCost) {
|
|
|
- minCost = currCost;
|
|
|
- firstIndex = i;
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- updateCacheStrip(vcache, tempStrips2.at(firstIndex));
|
|
|
- outStrips.add(tempStrips2.at(firstIndex));
|
|
|
-
|
|
|
- tempStrips2.at(firstIndex).visited = true;
|
|
|
-
|
|
|
- boolean bWantsCW = (tempStrips2.at(firstIndex).m_faces.size() % 2) == 0;
|
|
|
-
|
|
|
- //this n^2 algo is what slows down stripification so much....
|
|
|
- // needs to be improved
|
|
|
- while (true) {
|
|
|
- bestNumHits = -1.0f;
|
|
|
-
|
|
|
- //find best strip to add next, given the current cache
|
|
|
- for (int i = 0; i < tempStrips2.size(); i++) {
|
|
|
- if (tempStrips2.at(i).visited)
|
|
|
- continue;
|
|
|
-
|
|
|
- numHits = calcNumHitsStrip(vcache, tempStrips2.at(i));
|
|
|
- if (numHits > bestNumHits) {
|
|
|
- bestNumHits = numHits;
|
|
|
- bestIndex = i;
|
|
|
- } else if (numHits >= bestNumHits) {
|
|
|
- //check previous strip to see if this one requires it
|
|
|
- // to switch polarity
|
|
|
- StripInfo strip = tempStrips2.at(i);
|
|
|
- int nStripFaceCount = strip.m_faces.size();
|
|
|
-
|
|
|
- FaceInfo tFirstFace = new FaceInfo(
|
|
|
- strip.m_faces.at(0).m_v0,
|
|
|
- strip.m_faces.at(0).m_v1,
|
|
|
- strip.m_faces.at(0).m_v2);
|
|
|
-
|
|
|
- // If there is a second face, reorder vertices such
|
|
|
- // that the
|
|
|
- // unique vertex is first
|
|
|
- if (nStripFaceCount > 1) {
|
|
|
- int nUnique = getUniqueVertexInB(strip.m_faces
|
|
|
- .at(1), tFirstFace);
|
|
|
- if (nUnique == tFirstFace.m_v1) {
|
|
|
- int tmp = tFirstFace.m_v0;
|
|
|
- tFirstFace.m_v0 = tFirstFace.m_v1;
|
|
|
- tFirstFace.m_v1 = tmp;
|
|
|
- } else if (nUnique == tFirstFace.m_v2) {
|
|
|
- int tmp = tFirstFace.m_v0;
|
|
|
- tFirstFace.m_v0 = tFirstFace.m_v2;
|
|
|
- tFirstFace.m_v2 = tmp;
|
|
|
- }
|
|
|
-
|
|
|
- // If there is a third face, reorder vertices such
|
|
|
- // that the
|
|
|
- // shared vertex is last
|
|
|
- if (nStripFaceCount > 2) {
|
|
|
- int[] nShared = new int[2];
|
|
|
- getSharedVertices(strip.m_faces.at(2),
|
|
|
- tFirstFace, nShared);
|
|
|
- if ((nShared[0] == tFirstFace.m_v1)
|
|
|
- && (nShared[1] == -1)) {
|
|
|
- int tmp = tFirstFace.m_v2;
|
|
|
- tFirstFace.m_v2 = tFirstFace.m_v1;
|
|
|
- tFirstFace.m_v1 = tmp;
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- // Check CW/CCW ordering
|
|
|
- if (bWantsCW == isCW(strip.m_faces.at(0),
|
|
|
- tFirstFace.m_v0, tFirstFace.m_v1)) {
|
|
|
- //I like this one!
|
|
|
- bestIndex = i;
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- if (bestNumHits == -1.0f)
|
|
|
- break;
|
|
|
- tempStrips2.at(bestIndex).visited = true;
|
|
|
- updateCacheStrip(vcache, tempStrips2.at(bestIndex));
|
|
|
- outStrips.add(tempStrips2.at(bestIndex));
|
|
|
- bWantsCW = (tempStrips2.at(bestIndex).m_faces.size() % 2 == 0) ? bWantsCW
|
|
|
- : !bWantsCW;
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- ///////////////////////////////////////////////////////////////////////////////////////////
|
|
|
- // Stripify()
|
|
|
- //
|
|
|
- //
|
|
|
- // in_indices are the input indices of the mesh to stripify
|
|
|
- // in_cacheSize is the target cache size
|
|
|
- //
|
|
|
- void stripify(IntVec in_indices, int in_cacheSize, int in_minStripLength,
|
|
|
- int maxIndex, StripInfoVec outStrips, FaceInfoVec outFaceList) {
|
|
|
- meshJump = 0.0f;
|
|
|
- bFirstTimeResetPoint = true; //used in FindGoodResetPoint()
|
|
|
-
|
|
|
- //the number of times to run the experiments
|
|
|
- int numSamples = 10;
|
|
|
-
|
|
|
- //the cache size, clamped to one
|
|
|
- cacheSize = Math.max(1, in_cacheSize - CACHE_INEFFICIENCY);
|
|
|
-
|
|
|
- minStripLength = in_minStripLength;
|
|
|
- //this is the strip size threshold below which we dump the strip into
|
|
|
- // a list
|
|
|
-
|
|
|
- indices = in_indices;
|
|
|
-
|
|
|
- // build the stripification info
|
|
|
- FaceInfoVec allFaceInfos = new FaceInfoVec();
|
|
|
- EdgeInfoVec allEdgeInfos = new EdgeInfoVec();
|
|
|
-
|
|
|
- buildStripifyInfo(allFaceInfos, allEdgeInfos, maxIndex);
|
|
|
-
|
|
|
- StripInfoVec allStrips = new StripInfoVec();
|
|
|
-
|
|
|
- // stripify
|
|
|
- findAllStrips(allStrips, allFaceInfos, allEdgeInfos, numSamples);
|
|
|
-
|
|
|
- //split up the strips into cache friendly pieces, optimize them, then
|
|
|
- // dump these into outStrips
|
|
|
- splitUpStripsAndOptimize(allStrips, outStrips, allEdgeInfos,
|
|
|
- outFaceList);
|
|
|
-
|
|
|
- }
|
|
|
-
|
|
|
-}
|