1 /* 2 * Copyright (c) 2009-2010 jMonkeyEngine 3 * All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions are 7 * met: 8 * 9 * * Redistributions of source code must retain the above copyright 10 * notice, this list of conditions and the following disclaimer. 11 * 12 * * Redistributions in binary form must reproduce the above copyright 13 * notice, this list of conditions and the following disclaimer in the 14 * documentation and/or other materials provided with the distribution. 15 * 16 * * Neither the name of 'jMonkeyEngine' nor the names of its contributors 17 * may be used to endorse or promote products derived from this software 18 * without specific prior written permission. 19 * 20 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 21 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 22 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 23 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR 24 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 25 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 26 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 27 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 28 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 29 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 30 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 31 */ 32 33 package jme3tools.converters.model.strip; 34 35 import java.util.HashSet; 36 import java.util.logging.Logger; 37 38 /** 39 * 40 */ 41 class Stripifier { 42 private static final Logger logger = Logger.getLogger(Stripifier.class 43 .getName()); 44 45 public static int CACHE_INEFFICIENCY = 6; 46 47 IntVec indices = new IntVec(); 48 49 int cacheSize; 50 51 int minStripLength; 52 53 float meshJump; 54 55 boolean bFirstTimeResetPoint; 56 57 Stripifier() { 58 super(); 59 } 60 61 /////////////////////////////////////////////////////////////////////////////////////////// 62 // FindEdgeInfo() 63 // 64 // find the edge info for these two indices 65 // 66 static EdgeInfo findEdgeInfo(EdgeInfoVec edgeInfos, int v0, int v1) { 67 68 // we can get to it through either array 69 // because the edge infos have a v0 and v1 70 // and there is no order except how it was 71 // first created. 72 EdgeInfo infoIter = edgeInfos.at(v0); 73 while (infoIter != null) { 74 if (infoIter.m_v0 == v0) { 75 if (infoIter.m_v1 == v1) 76 return infoIter; 77 78 infoIter = infoIter.m_nextV0; 79 } else { 80 if (infoIter.m_v0 == v1) 81 return infoIter; 82 83 infoIter = infoIter.m_nextV1; 84 } 85 } 86 return null; 87 } 88 89 /////////////////////////////////////////////////////////////////////////////////////////// 90 // FindOtherFace 91 // 92 // find the other face sharing these vertices 93 // exactly like the edge info above 94 // 95 static FaceInfo findOtherFace(EdgeInfoVec edgeInfos, int v0, int v1, 96 FaceInfo faceInfo) { 97 EdgeInfo edgeInfo = findEdgeInfo(edgeInfos, v0, v1); 98 99 if ((edgeInfo == null) || (v0 == v1)) { 100 //we've hit a degenerate 101 return null; 102 } 103 104 return (edgeInfo.m_face0 == faceInfo ? edgeInfo.m_face1 105 : edgeInfo.m_face0); 106 } 107 108 static boolean alreadyExists(FaceInfo faceInfo, FaceInfoVec faceInfos) { 109 for (int i = 0; i < faceInfos.size(); ++i) { 110 FaceInfo o = faceInfos.at(i); 111 if ((o.m_v0 == faceInfo.m_v0) && (o.m_v1 == faceInfo.m_v1) 112 && (o.m_v2 == faceInfo.m_v2)) 113 return true; 114 } 115 return false; 116 } 117 118 /////////////////////////////////////////////////////////////////////////////////////////// 119 // BuildStripifyInfo() 120 // 121 // Builds the list of all face and edge infos 122 // 123 void buildStripifyInfo(FaceInfoVec faceInfos, EdgeInfoVec edgeInfos, 124 int maxIndex) { 125 // reserve space for the face infos, but do not resize them. 126 int numIndices = indices.size(); 127 faceInfos.reserve(numIndices / 3); 128 129 // we actually resize the edge infos, so we must initialize to null 130 for (int i = 0; i < maxIndex + 1; i++) 131 edgeInfos.add(null); 132 133 // iterate through the triangles of the triangle list 134 int numTriangles = numIndices / 3; 135 int index = 0; 136 boolean[] bFaceUpdated = new boolean[3]; 137 138 for (int i = 0; i < numTriangles; i++) { 139 boolean bMightAlreadyExist = true; 140 bFaceUpdated[0] = false; 141 bFaceUpdated[1] = false; 142 bFaceUpdated[2] = false; 143 144 // grab the indices 145 int v0 = indices.get(index++); 146 int v1 = indices.get(index++); 147 int v2 = indices.get(index++); 148 149 //we disregard degenerates 150 if (isDegenerate(v0, v1, v2)) 151 continue; 152 153 // create the face info and add it to the list of faces, but only 154 // if this exact face doesn't already 155 // exist in the list 156 FaceInfo faceInfo = new FaceInfo(v0, v1, v2); 157 158 // grab the edge infos, creating them if they do not already exist 159 EdgeInfo edgeInfo01 = findEdgeInfo(edgeInfos, v0, v1); 160 if (edgeInfo01 == null) { 161 //since one of it's edges isn't in the edge data structure, it 162 // can't already exist in the face structure 163 bMightAlreadyExist = false; 164 165 // create the info 166 edgeInfo01 = new EdgeInfo(v0, v1); 167 168 // update the linked list on both 169 edgeInfo01.m_nextV0 = edgeInfos.at(v0); 170 edgeInfo01.m_nextV1 = edgeInfos.at(v1); 171 edgeInfos.set(v0, edgeInfo01); 172 edgeInfos.set(v1, edgeInfo01); 173 174 // set face 0 175 edgeInfo01.m_face0 = faceInfo; 176 } else { 177 if (edgeInfo01.m_face1 != null) { 178 logger.info("BuildStripifyInfo: > 2 triangles on an edge" 179 + v0 + "," + v1 + "... uncertain consequences\n"); 180 } else { 181 edgeInfo01.m_face1 = faceInfo; 182 bFaceUpdated[0] = true; 183 } 184 } 185 186 // grab the edge infos, creating them if they do not already exist 187 EdgeInfo edgeInfo12 = findEdgeInfo(edgeInfos, v1, v2); 188 if (edgeInfo12 == null) { 189 bMightAlreadyExist = false; 190 191 // create the info 192 edgeInfo12 = new EdgeInfo(v1, v2); 193 194 // update the linked list on both 195 edgeInfo12.m_nextV0 = edgeInfos.at(v1); 196 edgeInfo12.m_nextV1 = edgeInfos.at(v2); 197 edgeInfos.set(v1, edgeInfo12); 198 edgeInfos.set(v2, edgeInfo12); 199 200 // set face 0 201 edgeInfo12.m_face0 = faceInfo; 202 } else { 203 if (edgeInfo12.m_face1 != null) { 204 logger.info("BuildStripifyInfo: > 2 triangles on an edge" 205 + v1 206 + "," 207 + v2 208 + "... uncertain consequences\n"); 209 } else { 210 edgeInfo12.m_face1 = faceInfo; 211 bFaceUpdated[1] = true; 212 } 213 } 214 215 // grab the edge infos, creating them if they do not already exist 216 EdgeInfo edgeInfo20 = findEdgeInfo(edgeInfos, v2, v0); 217 if (edgeInfo20 == null) { 218 bMightAlreadyExist = false; 219 220 // create the info 221 edgeInfo20 = new EdgeInfo(v2, v0); 222 223 // update the linked list on both 224 edgeInfo20.m_nextV0 = edgeInfos.at(v2); 225 edgeInfo20.m_nextV1 = edgeInfos.at(v0); 226 edgeInfos.set(v2, edgeInfo20); 227 edgeInfos.set(v0, edgeInfo20); 228 229 // set face 0 230 edgeInfo20.m_face0 = faceInfo; 231 } else { 232 if (edgeInfo20.m_face1 != null) { 233 logger.info("BuildStripifyInfo: > 2 triangles on an edge" 234 + v2 235 + "," 236 + v0 237 + "... uncertain consequences\n"); 238 } else { 239 edgeInfo20.m_face1 = faceInfo; 240 bFaceUpdated[2] = true; 241 } 242 } 243 244 if (bMightAlreadyExist) { 245 if (!alreadyExists(faceInfo, faceInfos)) 246 faceInfos.add(faceInfo); 247 else { 248 249 //cleanup pointers that point to this deleted face 250 if (bFaceUpdated[0]) 251 edgeInfo01.m_face1 = null; 252 if (bFaceUpdated[1]) 253 edgeInfo12.m_face1 = null; 254 if (bFaceUpdated[2]) 255 edgeInfo20.m_face1 = null; 256 } 257 } else { 258 faceInfos.add(faceInfo); 259 } 260 261 } 262 } 263 264 static boolean isDegenerate(FaceInfo face) { 265 if (face.m_v0 == face.m_v1) 266 return true; 267 else if (face.m_v0 == face.m_v2) 268 return true; 269 else if (face.m_v1 == face.m_v2) 270 return true; 271 else 272 return false; 273 } 274 275 static boolean isDegenerate(int v0, int v1, int v2) { 276 if (v0 == v1) 277 return true; 278 else if (v0 == v2) 279 return true; 280 else if (v1 == v2) 281 return true; 282 else 283 return false; 284 } 285 286 /////////////////////////////////////////////////////////////////////////////////////////// 287 // GetNextIndex() 288 // 289 // Returns vertex of the input face which is "next" in the input index list 290 // 291 static int getNextIndex(IntVec indices, FaceInfo face) { 292 293 int numIndices = indices.size(); 294 295 int v0 = indices.get(numIndices - 2); 296 int v1 = indices.get(numIndices - 1); 297 298 int fv0 = face.m_v0; 299 int fv1 = face.m_v1; 300 int fv2 = face.m_v2; 301 302 if (fv0 != v0 && fv0 != v1) { 303 if ((fv1 != v0 && fv1 != v1) || (fv2 != v0 && fv2 != v1)) { 304 logger.info("GetNextIndex: Triangle doesn't have all of its vertices\n"); 305 logger.info("GetNextIndex: Duplicate triangle probably got us derailed\n"); 306 } 307 return fv0; 308 } 309 if (fv1 != v0 && fv1 != v1) { 310 if ((fv0 != v0 && fv0 != v1) || (fv2 != v0 && fv2 != v1)) { 311 logger.info("GetNextIndex: Triangle doesn't have all of its vertices\n"); 312 logger.info("GetNextIndex: Duplicate triangle probably got us derailed\n"); 313 } 314 return fv1; 315 } 316 if (fv2 != v0 && fv2 != v1) { 317 if ((fv0 != v0 && fv0 != v1) || (fv1 != v0 && fv1 != v1)) { 318 logger.info("GetNextIndex: Triangle doesn't have all of its vertices\n"); 319 logger.info("GetNextIndex: Duplicate triangle probably got us derailed\n"); 320 } 321 return fv2; 322 } 323 324 // shouldn't get here, but let's try and fail gracefully 325 if ((fv0 == fv1) || (fv0 == fv2)) 326 return fv0; 327 else if ((fv1 == fv0) || (fv1 == fv2)) 328 return fv1; 329 else if ((fv2 == fv0) || (fv2 == fv1)) 330 return fv2; 331 else 332 return -1; 333 } 334 335 /////////////////////////////////////////////////////////////////////////////////////////// 336 // FindStartPoint() 337 // 338 // Finds a good starting point, namely one which has only one neighbor 339 // 340 static int findStartPoint(FaceInfoVec faceInfos, EdgeInfoVec edgeInfos) { 341 int bestCtr = -1; 342 int bestIndex = -1; 343 344 for (int i = 0; i < faceInfos.size(); i++) { 345 int ctr = 0; 346 347 if (findOtherFace(edgeInfos, faceInfos.at(i).m_v0, 348 faceInfos.at(i).m_v1, faceInfos.at(i)) == null) 349 ctr++; 350 if (findOtherFace(edgeInfos, faceInfos.at(i).m_v1, 351 faceInfos.at(i).m_v2, faceInfos.at(i)) == null) 352 ctr++; 353 if (findOtherFace(edgeInfos, faceInfos.at(i).m_v2, 354 faceInfos.at(i).m_v0, faceInfos.at(i)) == null) 355 ctr++; 356 if (ctr > bestCtr) { 357 bestCtr = ctr; 358 bestIndex = i; 359 //return i; 360 } 361 } 362 //return -1; 363 364 if (bestCtr == 0) 365 return -1; 366 367 return bestIndex; 368 } 369 370 /////////////////////////////////////////////////////////////////////////////////////////// 371 // FindGoodResetPoint() 372 // 373 // A good reset point is one near other commited areas so that 374 // we know that when we've made the longest strips its because 375 // we're stripifying in the same general orientation. 376 // 377 FaceInfo findGoodResetPoint(FaceInfoVec faceInfos, EdgeInfoVec edgeInfos) { 378 // we hop into different areas of the mesh to try to get 379 // other large open spans done. Areas of small strips can 380 // just be left to triangle lists added at the end. 381 FaceInfo result = null; 382 383 if (result == null) { 384 int numFaces = faceInfos.size(); 385 int startPoint; 386 if (bFirstTimeResetPoint) { 387 //first time, find a face with few neighbors (look for an edge 388 // of the mesh) 389 startPoint = findStartPoint(faceInfos, edgeInfos); 390 bFirstTimeResetPoint = false; 391 } else 392 startPoint = (int) (((float) numFaces - 1) * meshJump); 393 394 if (startPoint == -1) { 395 startPoint = (int) (((float) numFaces - 1) * meshJump); 396 397 //meshJump += 0.1f; 398 //if (meshJump > 1.0f) 399 // meshJump = .05f; 400 } 401 402 int i = startPoint; 403 do { 404 405 // if this guy isn't visited, try him 406 if (faceInfos.at(i).m_stripId < 0) { 407 result = faceInfos.at(i); 408 break; 409 } 410 411 // update the index and clamp to 0-(numFaces-1) 412 if (++i >= numFaces) 413 i = 0; 414 415 } while (i != startPoint); 416 417 // update the meshJump 418 meshJump += 0.1f; 419 if (meshJump > 1.0f) 420 meshJump = .05f; 421 } 422 423 // return the best face we found 424 return result; 425 } 426 427 /////////////////////////////////////////////////////////////////////////////////////////// 428 // GetUniqueVertexInB() 429 // 430 // Returns the vertex unique to faceB 431 // 432 static int getUniqueVertexInB(FaceInfo faceA, FaceInfo faceB) { 433 434 int facev0 = faceB.m_v0; 435 if (facev0 != faceA.m_v0 && facev0 != faceA.m_v1 436 && facev0 != faceA.m_v2) 437 return facev0; 438 439 int facev1 = faceB.m_v1; 440 if (facev1 != faceA.m_v0 && facev1 != faceA.m_v1 441 && facev1 != faceA.m_v2) 442 return facev1; 443 444 int facev2 = faceB.m_v2; 445 if (facev2 != faceA.m_v0 && facev2 != faceA.m_v1 446 && facev2 != faceA.m_v2) 447 return facev2; 448 449 // nothing is different 450 return -1; 451 } 452 453 /////////////////////////////////////////////////////////////////////////////////////////// 454 // GetSharedVertices() 455 // 456 // Returns the (at most) two vertices shared between the two faces 457 // 458 static void getSharedVertices(FaceInfo faceA, FaceInfo faceB, int[] vertex) { 459 vertex[0] = -1; 460 vertex[1] = -1; 461 462 int facev0 = faceB.m_v0; 463 if (facev0 == faceA.m_v0 || facev0 == faceA.m_v1 464 || facev0 == faceA.m_v2) { 465 if (vertex[0] == -1) 466 vertex[0] = facev0; 467 else { 468 vertex[1] = facev0; 469 return; 470 } 471 } 472 473 int facev1 = faceB.m_v1; 474 if (facev1 == faceA.m_v0 || facev1 == faceA.m_v1 475 || facev1 == faceA.m_v2) { 476 if (vertex[0] == -1) 477 vertex[0] = facev1; 478 else { 479 vertex[1] = facev1; 480 return; 481 } 482 } 483 484 int facev2 = faceB.m_v2; 485 if (facev2 == faceA.m_v0 || facev2 == faceA.m_v1 486 || facev2 == faceA.m_v2) { 487 if (vertex[0] == -1) 488 vertex[0] = facev2; 489 else { 490 vertex[1] = facev2; 491 return; 492 } 493 } 494 495 } 496 497 /////////////////////////////////////////////////////////////////////////////////////////// 498 // CommitStrips() 499 // 500 // "Commits" the input strips by setting their m_experimentId to -1 and 501 // adding to the allStrips 502 // vector 503 // 504 static void commitStrips(StripInfoVec allStrips, StripInfoVec strips) { 505 // Iterate through strips 506 int numStrips = strips.size(); 507 for (int i = 0; i < numStrips; i++) { 508 509 // Tell the strip that it is now real 510 StripInfo strip = strips.at(i); 511 strip.m_experimentId = -1; 512 513 // add to the list of real strips 514 allStrips.add(strip); 515 516 // Iterate through the faces of the strip 517 // Tell the faces of the strip that they belong to a real strip now 518 FaceInfoVec faces = strips.at(i).m_faces; 519 int numFaces = faces.size(); 520 521 for (int j = 0; j < numFaces; j++) { 522 strip.markTriangle(faces.at(j)); 523 } 524 } 525 } 526 527 /////////////////////////////////////////////////////////////////////////////////////////// 528 // NextIsCW() 529 // 530 // Returns true if the next face should be ordered in CW fashion 531 // 532 static boolean nextIsCW(int numIndices) { 533 return ((numIndices % 2) == 0); 534 } 535 536 /////////////////////////////////////////////////////////////////////////////////////////// 537 // UpdateCacheFace() 538 // 539 // Updates the input vertex cache with this face's vertices 540 // 541 static void updateCacheFace(VertexCache vcache, FaceInfo face) { 542 if (!vcache.inCache(face.m_v0)) 543 vcache.addEntry(face.m_v0); 544 545 if (!vcache.inCache(face.m_v1)) 546 vcache.addEntry(face.m_v1); 547 548 if (!vcache.inCache(face.m_v2)) 549 vcache.addEntry(face.m_v2); 550 } 551 552 /////////////////////////////////////////////////////////////////////////////////////////// 553 // UpdateCacheStrip() 554 // 555 // Updates the input vertex cache with this strip's vertices 556 // 557 static void updateCacheStrip(VertexCache vcache, StripInfo strip) { 558 for (int i = 0; i < strip.m_faces.size(); ++i) { 559 if (!vcache.inCache(strip.m_faces.at(i).m_v0)) 560 vcache.addEntry(strip.m_faces.at(i).m_v0); 561 562 if (!vcache.inCache(strip.m_faces.at(i).m_v1)) 563 vcache.addEntry(strip.m_faces.at(i).m_v1); 564 565 if (!vcache.inCache(strip.m_faces.at(i).m_v2)) 566 vcache.addEntry(strip.m_faces.at(i).m_v2); 567 } 568 } 569 570 /////////////////////////////////////////////////////////////////////////////////////////// 571 // CalcNumHitsStrip() 572 // 573 // returns the number of cache hits per face in the strip 574 // 575 static float calcNumHitsStrip(VertexCache vcache, StripInfo strip) { 576 int numHits = 0; 577 int numFaces = 0; 578 579 for (int i = 0; i < strip.m_faces.size(); i++) { 580 if (vcache.inCache(strip.m_faces.at(i).m_v0)) 581 ++numHits; 582 583 if (vcache.inCache(strip.m_faces.at(i).m_v1)) 584 ++numHits; 585 586 if (vcache.inCache(strip.m_faces.at(i).m_v2)) 587 ++numHits; 588 589 numFaces++; 590 } 591 592 return ((float) numHits / (float) numFaces); 593 } 594 595 /////////////////////////////////////////////////////////////////////////////////////////// 596 // AvgStripSize() 597 // 598 // Finds the average strip size of the input vector of strips 599 // 600 static float avgStripSize(StripInfoVec strips) { 601 int sizeAccum = 0; 602 int numStrips = strips.size(); 603 for (int i = 0; i < numStrips; i++) { 604 StripInfo strip = strips.at(i); 605 sizeAccum += strip.m_faces.size(); 606 sizeAccum -= strip.m_numDegenerates; 607 } 608 return ((float) sizeAccum) / ((float) numStrips); 609 } 610 611 /////////////////////////////////////////////////////////////////////////////////////////// 612 // CalcNumHitsFace() 613 // 614 // returns the number of cache hits in the face 615 // 616 static int calcNumHitsFace(VertexCache vcache, FaceInfo face) { 617 int numHits = 0; 618 619 if (vcache.inCache(face.m_v0)) 620 numHits++; 621 622 if (vcache.inCache(face.m_v1)) 623 numHits++; 624 625 if (vcache.inCache(face.m_v2)) 626 numHits++; 627 628 return numHits; 629 } 630 631 /////////////////////////////////////////////////////////////////////////////////////////// 632 // NumNeighbors() 633 // 634 // Returns the number of neighbors that this face has 635 // 636 static int numNeighbors(FaceInfo face, EdgeInfoVec edgeInfoVec) { 637 int numNeighbors = 0; 638 639 if (findOtherFace(edgeInfoVec, face.m_v0, face.m_v1, face) != null) { 640 numNeighbors++; 641 } 642 643 if (findOtherFace(edgeInfoVec, face.m_v1, face.m_v2, face) != null) { 644 numNeighbors++; 645 } 646 647 if (findOtherFace(edgeInfoVec, face.m_v2, face.m_v0, face) != null) { 648 numNeighbors++; 649 } 650 651 return numNeighbors; 652 } 653 654 /////////////////////////////////////////////////////////////////////////////////////////// 655 // IsCW() 656 // 657 // Returns true if the face is ordered in CW fashion 658 // 659 static boolean isCW(FaceInfo faceInfo, int v0, int v1) { 660 if (faceInfo.m_v0 == v0) 661 return (faceInfo.m_v1 == v1); 662 else if (faceInfo.m_v1 == v0) 663 return (faceInfo.m_v2 == v1); 664 else 665 return (faceInfo.m_v0 == v1); 666 667 } 668 669 static boolean faceContainsIndex(FaceInfo face, int index) { 670 return ((face.m_v0 == index) || (face.m_v1 == index) || (face.m_v2 == index)); 671 } 672 673 /////////////////////////////////////////////////////////////////////////////////////////// 674 // FindTraversal() 675 // 676 // Finds the next face to start the next strip on. 677 // 678 static boolean findTraversal(FaceInfoVec faceInfos, EdgeInfoVec edgeInfos, 679 StripInfo strip, StripStartInfo startInfo) { 680 681 // if the strip was v0.v1 on the edge, then v1 will be a vertex in the 682 // next edge. 683 int v = (strip.m_startInfo.m_toV1 ? strip.m_startInfo.m_startEdge.m_v1 684 : strip.m_startInfo.m_startEdge.m_v0); 685 686 FaceInfo untouchedFace = null; 687 EdgeInfo edgeIter = edgeInfos.at(v); 688 while (edgeIter != null) { 689 FaceInfo face0 = edgeIter.m_face0; 690 FaceInfo face1 = edgeIter.m_face1; 691 if ((face0 != null && !strip.isInStrip(face0)) && face1 != null 692 && !strip.isMarked(face1)) { 693 untouchedFace = face1; 694 break; 695 } 696 if ((face1 != null && !strip.isInStrip(face1)) && face0 != null 697 && !strip.isMarked(face0)) { 698 untouchedFace = face0; 699 break; 700 } 701 702 // find the next edgeIter 703 edgeIter = (edgeIter.m_v0 == v ? edgeIter.m_nextV0 704 : edgeIter.m_nextV1); 705 } 706 707 startInfo.m_startFace = untouchedFace; 708 startInfo.m_startEdge = edgeIter; 709 if (edgeIter != null) { 710 if (strip.sharesEdge(startInfo.m_startFace, edgeInfos)) 711 startInfo.m_toV1 = (edgeIter.m_v0 == v); //note! used to be 712 // m_v1 713 else 714 startInfo.m_toV1 = (edgeIter.m_v1 == v); 715 } 716 return (startInfo.m_startFace != null); 717 } 718 719 //////////////////////////////////////////////////////////////////////////////////////// 720 // RemoveSmallStrips() 721 // 722 // allStrips is the whole strip vector...all small strips will be deleted 723 // from this list, to avoid leaking mem 724 // allBigStrips is an out parameter which will contain all strips above 725 // minStripLength 726 // faceList is an out parameter which will contain all faces which were 727 // removed from the striplist 728 // 729 void removeSmallStrips(StripInfoVec allStrips, StripInfoVec allBigStrips, 730 FaceInfoVec faceList) { 731 faceList.clear(); 732 allBigStrips.clear(); //make sure these are empty 733 FaceInfoVec tempFaceList = new FaceInfoVec(); 734 735 for (int i = 0; i < allStrips.size(); i++) { 736 if (allStrips.at(i).m_faces.size() < minStripLength) { 737 //strip is too small, add faces to faceList 738 for (int j = 0; j < allStrips.at(i).m_faces.size(); j++) 739 tempFaceList.add(allStrips.at(i).m_faces.at(j)); 740 741 } else { 742 allBigStrips.add(allStrips.at(i)); 743 } 744 } 745 746 boolean[] bVisitedList = new boolean[tempFaceList.size()]; 747 748 VertexCache vcache = new VertexCache(cacheSize); 749 750 int bestNumHits = -1; 751 int numHits; 752 int bestIndex = -9999; 753 754 while (true) { 755 bestNumHits = -1; 756 757 //find best face to add next, given the current cache 758 for (int i = 0; i < tempFaceList.size(); i++) { 759 if (bVisitedList[i]) 760 continue; 761 762 numHits = calcNumHitsFace(vcache, tempFaceList.at(i)); 763 if (numHits > bestNumHits) { 764 bestNumHits = numHits; 765 bestIndex = i; 766 } 767 } 768 769 if (bestNumHits == -1.0f) 770 break; 771 bVisitedList[bestIndex] = true; 772 updateCacheFace(vcache, tempFaceList.at(bestIndex)); 773 faceList.add(tempFaceList.at(bestIndex)); 774 } 775 } 776 777 //////////////////////////////////////////////////////////////////////////////////////// 778 // CreateStrips() 779 // 780 // Generates actual strips from the list-in-strip-order. 781 // 782 int createStrips(StripInfoVec allStrips, IntVec stripIndices, 783 boolean bStitchStrips) { 784 int numSeparateStrips = 0; 785 786 FaceInfo tLastFace = new FaceInfo(0, 0, 0); 787 int nStripCount = allStrips.size(); 788 789 //we infer the cw/ccw ordering depending on the number of indices 790 //this is screwed up by the fact that we insert -1s to denote changing 791 // strips 792 //this is to account for that 793 int accountForNegatives = 0; 794 795 for (int i = 0; i < nStripCount; i++) { 796 StripInfo strip = allStrips.at(i); 797 int nStripFaceCount = strip.m_faces.size(); 798 799 // Handle the first face in the strip 800 { 801 FaceInfo tFirstFace = new FaceInfo(strip.m_faces.at(0).m_v0, 802 strip.m_faces.at(0).m_v1, strip.m_faces.at(0).m_v2); 803 804 // If there is a second face, reorder vertices such that the 805 // unique vertex is first 806 if (nStripFaceCount > 1) { 807 int nUnique = getUniqueVertexInB(strip.m_faces.at(1), 808 tFirstFace); 809 if (nUnique == tFirstFace.m_v1) { 810 int tmp = tFirstFace.m_v0; 811 tFirstFace.m_v0 = tFirstFace.m_v1; 812 tFirstFace.m_v1 = tmp; 813 } else if (nUnique == tFirstFace.m_v2) { 814 int tmp = tFirstFace.m_v0; 815 tFirstFace.m_v0 = tFirstFace.m_v2; 816 tFirstFace.m_v2 = tmp; 817 } 818 819 // If there is a third face, reorder vertices such that the 820 // shared vertex is last 821 if (nStripFaceCount > 2) { 822 if (isDegenerate(strip.m_faces.at(1))) { 823 int pivot = strip.m_faces.at(1).m_v1; 824 if (tFirstFace.m_v1 == pivot) { 825 int tmp = tFirstFace.m_v1; 826 tFirstFace.m_v1 = tFirstFace.m_v2; 827 tFirstFace.m_v2 = tmp; 828 } 829 } else { 830 int[] nShared = new int[2]; 831 getSharedVertices(strip.m_faces.at(2), tFirstFace, 832 nShared); 833 if ((nShared[0] == tFirstFace.m_v1) 834 && (nShared[1] == -1)) { 835 int tmp = tFirstFace.m_v1; 836 tFirstFace.m_v1 = tFirstFace.m_v2; 837 tFirstFace.m_v2 = tmp; 838 } 839 } 840 } 841 } 842 843 if ((i == 0) || !bStitchStrips) { 844 if (!isCW(strip.m_faces.at(0), tFirstFace.m_v0, 845 tFirstFace.m_v1)) 846 stripIndices.add(tFirstFace.m_v0); 847 } else { 848 // Double tap the first in the new strip 849 stripIndices.add(tFirstFace.m_v0); 850 851 // Check CW/CCW ordering 852 if (nextIsCW(stripIndices.size() - accountForNegatives) != isCW( 853 strip.m_faces.at(0), tFirstFace.m_v0, 854 tFirstFace.m_v1)) { 855 stripIndices.add(tFirstFace.m_v0); 856 } 857 } 858 859 stripIndices.add(tFirstFace.m_v0); 860 stripIndices.add(tFirstFace.m_v1); 861 stripIndices.add(tFirstFace.m_v2); 862 863 // Update last face info 864 tLastFace.set(tFirstFace); 865 } 866 867 for (int j = 1; j < nStripFaceCount; j++) { 868 int nUnique = getUniqueVertexInB(tLastFace, strip.m_faces.at(j)); 869 if (nUnique != -1) { 870 stripIndices.add(nUnique); 871 872 // Update last face info 873 tLastFace.m_v0 = tLastFace.m_v1; 874 tLastFace.m_v1 = tLastFace.m_v2; 875 tLastFace.m_v2 = nUnique; 876 } else { 877 //we've hit a degenerate 878 stripIndices.add(strip.m_faces.at(j).m_v2); 879 tLastFace.m_v0 = strip.m_faces.at(j).m_v0; //tLastFace.m_v1; 880 tLastFace.m_v1 = strip.m_faces.at(j).m_v1; //tLastFace.m_v2; 881 tLastFace.m_v2 = strip.m_faces.at(j).m_v2; //tLastFace.m_v1; 882 883 } 884 } 885 886 // Double tap between strips. 887 if (bStitchStrips) { 888 if (i != nStripCount - 1) 889 stripIndices.add(tLastFace.m_v2); 890 } else { 891 //-1 index indicates next strip 892 stripIndices.add(-1); 893 accountForNegatives++; 894 numSeparateStrips++; 895 } 896 897 // Update last face info 898 tLastFace.m_v0 = tLastFace.m_v1; 899 tLastFace.m_v1 = tLastFace.m_v2; 900 tLastFace.m_v2 = tLastFace.m_v2; 901 } 902 903 if (bStitchStrips) 904 numSeparateStrips = 1; 905 return numSeparateStrips; 906 } 907 908 /////////////////////////////////////////////////////////////////////////////////////////// 909 // FindAllStrips() 910 // 911 // Does the stripification, puts output strips into vector allStrips 912 // 913 // Works by setting runnning a number of experiments in different areas of 914 // the mesh, and 915 // accepting the one which results in the longest strips. It then accepts 916 // this, and moves 917 // on to a different area of the mesh. We try to jump around the mesh some, 918 // to ensure that 919 // large open spans of strips get generated. 920 // 921 void findAllStrips(StripInfoVec allStrips, FaceInfoVec allFaceInfos, 922 EdgeInfoVec allEdgeInfos, int numSamples) { 923 // the experiments 924 int experimentId = 0; 925 int stripId = 0; 926 boolean done = false; 927 928 int loopCtr = 0; 929 930 while (!done) { 931 loopCtr++; 932 933 // 934 // PHASE 1: Set up numSamples * numEdges experiments 935 // 936 StripInfoVec[] experiments = new StripInfoVec[numSamples * 6]; 937 for (int i = 0; i < experiments.length; i++) 938 experiments[i] = new StripInfoVec(); 939 940 int experimentIndex = 0; 941 HashSet<FaceInfo> resetPoints = new HashSet<FaceInfo>(); /* NvFaceInfo */ 942 for (int i = 0; i < numSamples; i++) { 943 // Try to find another good reset point. 944 // If there are none to be found, we are done 945 FaceInfo nextFace = findGoodResetPoint(allFaceInfos, 946 allEdgeInfos); 947 if (nextFace == null) { 948 done = true; 949 break; 950 } 951 // If we have already evaluated starting at this face in this 952 // slew of experiments, then skip going any further 953 else if (resetPoints.contains(nextFace)) { 954 continue; 955 } 956 957 // trying it now... 958 resetPoints.add(nextFace); 959 960 // otherwise, we shall now try experiments for starting on the 961 // 01,12, and 20 edges 962 963 // build the strip off of this face's 0-1 edge 964 EdgeInfo edge01 = findEdgeInfo(allEdgeInfos, nextFace.m_v0, 965 nextFace.m_v1); 966 StripInfo strip01 = new StripInfo(new StripStartInfo(nextFace, 967 edge01, true), stripId++, experimentId++); 968 experiments[experimentIndex++].add(strip01); 969 970 // build the strip off of this face's 1-0 edge 971 EdgeInfo edge10 = findEdgeInfo(allEdgeInfos, nextFace.m_v0, 972 nextFace.m_v1); 973 StripInfo strip10 = new StripInfo(new StripStartInfo(nextFace, 974 edge10, false), stripId++, experimentId++); 975 experiments[experimentIndex++].add(strip10); 976 977 // build the strip off of this face's 1-2 edge 978 EdgeInfo edge12 = findEdgeInfo(allEdgeInfos, nextFace.m_v1, 979 nextFace.m_v2); 980 StripInfo strip12 = new StripInfo(new StripStartInfo(nextFace, 981 edge12, true), stripId++, experimentId++); 982 experiments[experimentIndex++].add(strip12); 983 984 // build the strip off of this face's 2-1 edge 985 EdgeInfo edge21 = findEdgeInfo(allEdgeInfos, nextFace.m_v1, 986 nextFace.m_v2); 987 StripInfo strip21 = new StripInfo(new StripStartInfo(nextFace, 988 edge21, false), stripId++, experimentId++); 989 experiments[experimentIndex++].add(strip21); 990 991 // build the strip off of this face's 2-0 edge 992 EdgeInfo edge20 = findEdgeInfo(allEdgeInfos, nextFace.m_v2, 993 nextFace.m_v0); 994 StripInfo strip20 = new StripInfo(new StripStartInfo(nextFace, 995 edge20, true), stripId++, experimentId++); 996 experiments[experimentIndex++].add(strip20); 997 998 // build the strip off of this face's 0-2 edge 999 EdgeInfo edge02 = findEdgeInfo(allEdgeInfos, nextFace.m_v2, 1000 nextFace.m_v0); 1001 StripInfo strip02 = new StripInfo(new StripStartInfo(nextFace, 1002 edge02, false), stripId++, experimentId++); 1003 experiments[experimentIndex++].add(strip02); 1004 } 1005 1006 // 1007 // PHASE 2: Iterate through that we setup in the last phase 1008 // and really build each of the strips and strips that follow to 1009 // see how 1010 // far we get 1011 // 1012 int numExperiments = experimentIndex; 1013 for (int i = 0; i < numExperiments; i++) { 1014 1015 // get the strip set 1016 1017 // build the first strip of the list 1018 experiments[i].at(0).build(allEdgeInfos, allFaceInfos); 1019 int experimentId2 = experiments[i].at(0).m_experimentId; 1020 1021 StripInfo stripIter = experiments[i].at(0); 1022 StripStartInfo startInfo = new StripStartInfo(null, null, false); 1023 while (findTraversal(allFaceInfos, allEdgeInfos, stripIter, 1024 startInfo)) { 1025 1026 // create the new strip info 1027 //TODO startInfo clone ? 1028 stripIter = new StripInfo(startInfo, stripId++, 1029 experimentId2); 1030 1031 // build the next strip 1032 stripIter.build(allEdgeInfos, allFaceInfos); 1033 1034 // add it to the list 1035 experiments[i].add(stripIter); 1036 } 1037 } 1038 1039 // 1040 // Phase 3: Find the experiment that has the most promise 1041 // 1042 int bestIndex = 0; 1043 double bestValue = 0; 1044 for (int i = 0; i < numExperiments; i++) { 1045 float avgStripSizeWeight = 1.0f; 1046 //float numTrisWeight = 0.0f; 1047 float numStripsWeight = 0.0f; 1048 float avgStripSize = avgStripSize(experiments[i]); 1049 float numStrips = experiments[i].size(); 1050 float value = avgStripSize * avgStripSizeWeight 1051 + (numStrips * numStripsWeight); 1052 //float value = 1.f / numStrips; 1053 //float value = numStrips * avgStripSize; 1054 1055 if (value > bestValue) { 1056 bestValue = value; 1057 bestIndex = i; 1058 } 1059 } 1060 1061 // 1062 // Phase 4: commit the best experiment of the bunch 1063 // 1064 commitStrips(allStrips, experiments[bestIndex]); 1065 } 1066 } 1067 1068 /////////////////////////////////////////////////////////////////////////////////////////// 1069 // SplitUpStripsAndOptimize() 1070 // 1071 // Splits the input vector of strips (allBigStrips) into smaller, cache 1072 // friendly pieces, then 1073 // reorders these pieces to maximize cache hits 1074 // The final strips are output through outStrips 1075 // 1076 void splitUpStripsAndOptimize(StripInfoVec allStrips, 1077 StripInfoVec outStrips, EdgeInfoVec edgeInfos, 1078 FaceInfoVec outFaceList) { 1079 int threshold = cacheSize; 1080 StripInfoVec tempStrips = new StripInfoVec(); 1081 int j; 1082 1083 //split up strips into threshold-sized pieces 1084 for (int i = 0; i < allStrips.size(); i++) { 1085 StripInfo currentStrip; 1086 StripStartInfo startInfo = new StripStartInfo(null, null, false); 1087 1088 int actualStripSize = 0; 1089 for (j = 0; j < allStrips.at(i).m_faces.size(); ++j) { 1090 if (!isDegenerate(allStrips.at(i).m_faces.at(j))) 1091 actualStripSize++; 1092 } 1093 1094 if (actualStripSize /* allStrips.at(i).m_faces.size() */ 1095 > threshold) { 1096 1097 int numTimes = actualStripSize /* allStrips.at(i).m_faces.size() */ 1098 / threshold; 1099 int numLeftover = actualStripSize /* allStrips.at(i).m_faces.size() */ 1100 % threshold; 1101 1102 int degenerateCount = 0; 1103 for (j = 0; j < numTimes; j++) { 1104 currentStrip = new StripInfo(startInfo, 0, -1); 1105 1106 int faceCtr = j * threshold + degenerateCount; 1107 boolean bFirstTime = true; 1108 while (faceCtr < threshold + (j * threshold) 1109 + degenerateCount) { 1110 if (isDegenerate(allStrips.at(i).m_faces.at(faceCtr))) { 1111 degenerateCount++; 1112 1113 //last time or first time through, no need for a 1114 // degenerate 1115 if ((((faceCtr + 1) != threshold + (j * threshold) 1116 + degenerateCount) || ((j == numTimes - 1) 1117 && (numLeftover < 4) && (numLeftover > 0))) 1118 && !bFirstTime) { 1119 currentStrip.m_faces 1120 .add(allStrips.at(i).m_faces 1121 .at(faceCtr++)); 1122 } else 1123 ++faceCtr; 1124 } else { 1125 currentStrip.m_faces.add(allStrips.at(i).m_faces 1126 .at(faceCtr++)); 1127 bFirstTime = false; 1128 } 1129 } 1130 /* 1131 * threshold; faceCtr < threshold+(j*threshold); faceCtr++) { 1132 * currentStrip.m_faces.add(allStrips.at(i).m_faces.at(faceCtr]); } 1133 */ 1134 ///* 1135 if (j == numTimes - 1) //last time through 1136 { 1137 if ((numLeftover < 4) && (numLeftover > 0)) //way too 1138 // small 1139 { 1140 //just add to last strip 1141 int ctr = 0; 1142 while (ctr < numLeftover) { 1143 if (!isDegenerate(allStrips.at(i).m_faces 1144 .at(faceCtr))) { 1145 currentStrip.m_faces 1146 .add(allStrips.at(i).m_faces 1147 .at(faceCtr++)); 1148 ++ctr; 1149 } else { 1150 currentStrip.m_faces 1151 .add(allStrips.at(i).m_faces 1152 .at(faceCtr++)); 1153 ++degenerateCount; 1154 } 1155 } 1156 numLeftover = 0; 1157 } 1158 } 1159 //*/ 1160 tempStrips.add(currentStrip); 1161 } 1162 1163 int leftOff = j * threshold + degenerateCount; 1164 1165 if (numLeftover != 0) { 1166 currentStrip = new StripInfo(startInfo, 0, -1); 1167 1168 int ctr = 0; 1169 boolean bFirstTime = true; 1170 while (ctr < numLeftover) { 1171 if (!isDegenerate(allStrips.at(i).m_faces.at(leftOff))) { 1172 ctr++; 1173 bFirstTime = false; 1174 currentStrip.m_faces.add(allStrips.at(i).m_faces 1175 .at(leftOff++)); 1176 } else if (!bFirstTime) 1177 currentStrip.m_faces.add(allStrips.at(i).m_faces 1178 .at(leftOff++)); 1179 else 1180 leftOff++; 1181 } 1182 /* 1183 * for(int k = 0; k < numLeftover; k++) { 1184 * currentStrip.m_faces.add(allStrips.at(i).m_faces[leftOff++]); } 1185 */ 1186 1187 tempStrips.add(currentStrip); 1188 } 1189 } else { 1190 //we're not just doing a tempStrips.add(allBigStrips[i]) 1191 // because 1192 // this way we can delete allBigStrips later to free the memory 1193 currentStrip = new StripInfo(startInfo, 0, -1); 1194 1195 for (j = 0; j < allStrips.at(i).m_faces.size(); j++) 1196 currentStrip.m_faces.add(allStrips.at(i).m_faces.at(j)); 1197 1198 tempStrips.add(currentStrip); 1199 } 1200 } 1201 1202 //add small strips to face list 1203 StripInfoVec tempStrips2 = new StripInfoVec(); 1204 removeSmallStrips(tempStrips, tempStrips2, outFaceList); 1205 1206 outStrips.clear(); 1207 //screw optimization for now 1208 // for(i = 0; i < tempStrips.size(); ++i) 1209 // outStrips.add(tempStrips[i]); 1210 1211 if (tempStrips2.size() != 0) { 1212 //Optimize for the vertex cache 1213 VertexCache vcache = new VertexCache(cacheSize); 1214 1215 float bestNumHits = -1.0f; 1216 float numHits; 1217 int bestIndex = -99999; 1218 1219 int firstIndex = 0; 1220 float minCost = 10000.0f; 1221 1222 for (int i = 0; i < tempStrips2.size(); i++) { 1223 int numNeighbors = 0; 1224 1225 //find strip with least number of neighbors per face 1226 for (j = 0; j < tempStrips2.at(i).m_faces.size(); j++) { 1227 numNeighbors += numNeighbors(tempStrips2.at(i).m_faces 1228 .at(j), edgeInfos); 1229 } 1230 1231 float currCost = (float) numNeighbors 1232 / (float) tempStrips2.at(i).m_faces.size(); 1233 if (currCost < minCost) { 1234 minCost = currCost; 1235 firstIndex = i; 1236 } 1237 } 1238 1239 updateCacheStrip(vcache, tempStrips2.at(firstIndex)); 1240 outStrips.add(tempStrips2.at(firstIndex)); 1241 1242 tempStrips2.at(firstIndex).visited = true; 1243 1244 boolean bWantsCW = (tempStrips2.at(firstIndex).m_faces.size() % 2) == 0; 1245 1246 //this n^2 algo is what slows down stripification so much.... 1247 // needs to be improved 1248 while (true) { 1249 bestNumHits = -1.0f; 1250 1251 //find best strip to add next, given the current cache 1252 for (int i = 0; i < tempStrips2.size(); i++) { 1253 if (tempStrips2.at(i).visited) 1254 continue; 1255 1256 numHits = calcNumHitsStrip(vcache, tempStrips2.at(i)); 1257 if (numHits > bestNumHits) { 1258 bestNumHits = numHits; 1259 bestIndex = i; 1260 } else if (numHits >= bestNumHits) { 1261 //check previous strip to see if this one requires it 1262 // to switch polarity 1263 StripInfo strip = tempStrips2.at(i); 1264 int nStripFaceCount = strip.m_faces.size(); 1265 1266 FaceInfo tFirstFace = new FaceInfo( 1267 strip.m_faces.at(0).m_v0, 1268 strip.m_faces.at(0).m_v1, 1269 strip.m_faces.at(0).m_v2); 1270 1271 // If there is a second face, reorder vertices such 1272 // that the 1273 // unique vertex is first 1274 if (nStripFaceCount > 1) { 1275 int nUnique = getUniqueVertexInB(strip.m_faces 1276 .at(1), tFirstFace); 1277 if (nUnique == tFirstFace.m_v1) { 1278 int tmp = tFirstFace.m_v0; 1279 tFirstFace.m_v0 = tFirstFace.m_v1; 1280 tFirstFace.m_v1 = tmp; 1281 } else if (nUnique == tFirstFace.m_v2) { 1282 int tmp = tFirstFace.m_v0; 1283 tFirstFace.m_v0 = tFirstFace.m_v2; 1284 tFirstFace.m_v2 = tmp; 1285 } 1286 1287 // If there is a third face, reorder vertices such 1288 // that the 1289 // shared vertex is last 1290 if (nStripFaceCount > 2) { 1291 int[] nShared = new int[2]; 1292 getSharedVertices(strip.m_faces.at(2), 1293 tFirstFace, nShared); 1294 if ((nShared[0] == tFirstFace.m_v1) 1295 && (nShared[1] == -1)) { 1296 int tmp = tFirstFace.m_v2; 1297 tFirstFace.m_v2 = tFirstFace.m_v1; 1298 tFirstFace.m_v1 = tmp; 1299 } 1300 } 1301 } 1302 1303 // Check CW/CCW ordering 1304 if (bWantsCW == isCW(strip.m_faces.at(0), 1305 tFirstFace.m_v0, tFirstFace.m_v1)) { 1306 //I like this one! 1307 bestIndex = i; 1308 } 1309 } 1310 } 1311 1312 if (bestNumHits == -1.0f) 1313 break; 1314 tempStrips2.at(bestIndex).visited = true; 1315 updateCacheStrip(vcache, tempStrips2.at(bestIndex)); 1316 outStrips.add(tempStrips2.at(bestIndex)); 1317 bWantsCW = (tempStrips2.at(bestIndex).m_faces.size() % 2 == 0) ? bWantsCW 1318 : !bWantsCW; 1319 } 1320 } 1321 } 1322 1323 /////////////////////////////////////////////////////////////////////////////////////////// 1324 // Stripify() 1325 // 1326 // 1327 // in_indices are the input indices of the mesh to stripify 1328 // in_cacheSize is the target cache size 1329 // 1330 void stripify(IntVec in_indices, int in_cacheSize, int in_minStripLength, 1331 int maxIndex, StripInfoVec outStrips, FaceInfoVec outFaceList) { 1332 meshJump = 0.0f; 1333 bFirstTimeResetPoint = true; //used in FindGoodResetPoint() 1334 1335 //the number of times to run the experiments 1336 int numSamples = 10; 1337 1338 //the cache size, clamped to one 1339 cacheSize = Math.max(1, in_cacheSize - CACHE_INEFFICIENCY); 1340 1341 minStripLength = in_minStripLength; 1342 //this is the strip size threshold below which we dump the strip into 1343 // a list 1344 1345 indices = in_indices; 1346 1347 // build the stripification info 1348 FaceInfoVec allFaceInfos = new FaceInfoVec(); 1349 EdgeInfoVec allEdgeInfos = new EdgeInfoVec(); 1350 1351 buildStripifyInfo(allFaceInfos, allEdgeInfos, maxIndex); 1352 1353 StripInfoVec allStrips = new StripInfoVec(); 1354 1355 // stripify 1356 findAllStrips(allStrips, allFaceInfos, allEdgeInfos, numSamples); 1357 1358 //split up the strips into cache friendly pieces, optimize them, then 1359 // dump these into outStrips 1360 splitUpStripsAndOptimize(allStrips, outStrips, allEdgeInfos, 1361 outFaceList); 1362 1363 } 1364 1365 }