001/** 002 * Copyright (c) 2008-2014 Ardor Labs, Inc. 003 * 004 * This file is part of Ardor3D. 005 * 006 * Ardor3D is free software: you can redistribute it and/or modify it 007 * under the terms of its license which may be found in the accompanying 008 * LICENSE file or at <http://www.ardor3d.com/LICENSE>. 009 */ 010 011package com.ardor3d.extension.model.util.nvtristrip; 012 013import java.util.ArrayList; 014import java.util.HashSet; 015import java.util.List; 016import java.util.Set; 017import java.util.logging.Logger; 018 019/** 020 * Ported from <a href="http://developer.nvidia.com/object/nvtristrip_library.html">NVIDIA's NvTriStrip Library</a> 021 */ 022final class NvStripifier { 023 private static final Logger logger = Logger.getLogger(NvStripifier.class.getName()); 024 025 public static int CACHE_INEFFICIENCY = 6; 026 027 protected List<Integer> _indices = new ArrayList<>(); 028 protected int _cacheSize; 029 protected int _minStripLength; 030 protected float _meshJump; 031 protected boolean _firstTimeResetPoint; 032 033 /** 034 * 035 * @param in_indices 036 * the input indices of the mesh to stripify 037 * @param in_cacheSize 038 * the target cache size 039 * @param in_minStripLength 040 * the minimum strip length 041 * @param maxIndex 042 * the maximum index 043 * @param outStrips 044 * the strips 045 * @param outFaceList 046 * the face list 047 */ 048 void stripify(final List<Integer> in_indices, final int in_cacheSize, final int in_minStripLength, 049 final int maxIndex, final List<NvStripInfo> outStrips, final List<NvFaceInfo> outFaceList) { 050 _meshJump = 0.0f; 051 _firstTimeResetPoint = true; // used in FindGoodResetPoint() 052 053 // the number of times to run the experiments 054 final int numSamples = 10; 055 056 // the cache size, clamped to one 057 _cacheSize = Math.max(1, in_cacheSize - NvStripifier.CACHE_INEFFICIENCY); 058 059 // this is the strip size threshold below which we dump the strip into a list 060 _minStripLength = in_minStripLength; 061 062 _indices = in_indices; 063 064 // build the stripification info 065 final List<NvFaceInfo> allFaceInfos = new ArrayList<>(); 066 final List<NvEdgeInfo> allEdgeInfos = new ArrayList<>(); 067 068 buildStripifyInfo(allFaceInfos, allEdgeInfos, maxIndex); 069 070 final List<NvStripInfo> allStrips = new ArrayList<>(); 071 072 // stripify 073 findAllStrips(allStrips, allFaceInfos, allEdgeInfos, numSamples); 074 075 // split up the strips into cache friendly pieces, optimize them, then dump these into outStrips 076 splitUpStripsAndOptimize(allStrips, outStrips, allEdgeInfos, outFaceList); 077 } 078 079 /** 080 * Generates actual strips from the list-in-strip-order. 081 * 082 * @param allStrips 083 * all strips 084 * @param stripIndices 085 * the strip indices 086 * @param bStitchStrips 087 * <code>true</code> if the strips are stitched 088 * @param bRestart 089 * <code>true</code> if it restarts 090 * @param restartVal 091 * restart value 092 * @return the number of separate strips 093 */ 094 int createStrips(final List<NvStripInfo> allStrips, final List<Integer> stripIndices, final boolean bStitchStrips, 095 final boolean bRestart, final int restartVal) { 096 int numSeparateStrips = 0; 097 098 NvFaceInfo tLastFace = new NvFaceInfo(0, 0, 0); 099 final int nStripCount = allStrips.size(); 100 assert nStripCount > 0; 101 102 // we infer the cw/ccw ordering depending on the number of indices 103 // this is screwed up by the fact that we insert -1s to denote changing strips 104 // this is to account for that 105 int accountForNegatives = 0; 106 107 for (int i = 0; i < nStripCount; i++) { 108 final NvStripInfo strip = allStrips.get(i); 109 final int nStripFaceCount = strip._faces.size(); 110 assert nStripFaceCount > 0; 111 112 // Handle the first face in the strip 113 { 114 final NvFaceInfo tFirstFace = new NvFaceInfo(strip._faces.get(0)._v0, strip._faces.get(0)._v1, 115 strip._faces.get(0)._v2); 116 117 // If there is a second face, reorder vertices such that the 118 // unique vertex is first 119 if (nStripFaceCount > 1) { 120 final int nUnique = NvStripifier.getUniqueVertexInB(strip._faces.get(1), tFirstFace); 121 if (nUnique == tFirstFace._v1) { 122 final int store = tFirstFace._v1; 123 tFirstFace._v1 = tFirstFace._v0; 124 tFirstFace._v0 = store; 125 } else if (nUnique == tFirstFace._v2) { 126 final int store = tFirstFace._v2; 127 tFirstFace._v2 = tFirstFace._v0; 128 tFirstFace._v0 = store; 129 } 130 131 // If there is a third face, reorder vertices such that the 132 // shared vertex is last 133 if (nStripFaceCount > 2) { 134 if (NvStripifier.isDegenerate(strip._faces.get(1))) { 135 final int pivot = strip._faces.get(1)._v1; 136 if (tFirstFace._v1 == pivot) { 137 final int store = tFirstFace._v2; 138 tFirstFace._v2 = tFirstFace._v1; 139 tFirstFace._v1 = store; 140 } 141 } else { 142 final int[] nShared = NvStripifier.getSharedVertices(strip._faces.get(2), tFirstFace); 143 if (nShared[0] == tFirstFace._v1 && nShared[1] == -1) { 144 final int store = tFirstFace._v2; 145 tFirstFace._v2 = tFirstFace._v1; 146 tFirstFace._v1 = store; 147 } 148 } 149 } 150 } 151 152 if (i == 0 || !bStitchStrips || bRestart) { 153 if (!NvStripifier.isCW(strip._faces.get(0), tFirstFace._v0, tFirstFace._v1)) { 154 stripIndices.add(tFirstFace._v0); 155 } 156 } else { 157 // Double tap the first in the new strip 158 stripIndices.add(tFirstFace._v0); 159 160 // Check CW/CCW ordering 161 if (NvStripifier.nextIsCW(stripIndices.size() - accountForNegatives) != NvStripifier 162 .isCW(strip._faces.get(0), tFirstFace._v0, tFirstFace._v1)) { 163 stripIndices.add(tFirstFace._v0); 164 } 165 } 166 167 stripIndices.add(tFirstFace._v0); 168 stripIndices.add(tFirstFace._v1); 169 stripIndices.add(tFirstFace._v2); 170 171 // Update last face info 172 tLastFace = tFirstFace; 173 } 174 175 for (int j = 1; j < nStripFaceCount; j++) { 176 final int nUnique = NvStripifier.getUniqueVertexInB(tLastFace, strip._faces.get(j)); 177 if (nUnique != -1) { 178 stripIndices.add(nUnique); 179 180 // Update last face info 181 tLastFace._v0 = tLastFace._v1; 182 tLastFace._v1 = tLastFace._v2; 183 tLastFace._v2 = nUnique; 184 } else { 185 // we've hit a degenerate 186 stripIndices.add(strip._faces.get(j)._v2); 187 tLastFace._v0 = strip._faces.get(j)._v0;// tLastFace.m_v1; 188 tLastFace._v1 = strip._faces.get(j)._v1;// tLastFace.m_v2; 189 tLastFace._v2 = strip._faces.get(j)._v2;// tLastFace.m_v1; 190 191 } 192 } 193 194 // Double tap between strips. 195 if (bStitchStrips && !bRestart) { 196 if (i != nStripCount - 1) { 197 stripIndices.add(tLastFace._v2); 198 } 199 } else if (bRestart) { 200 stripIndices.add(restartVal); 201 } else { 202 // -1 index indicates next strip 203 stripIndices.add(-1); 204 accountForNegatives++; 205 numSeparateStrips++; 206 } 207 208 // Update last face info 209 tLastFace._v0 = tLastFace._v1; 210 tLastFace._v1 = tLastFace._v2; 211 // tLastFace._v2 = tLastFace._v2; // for info purposes. 212 } 213 214 if (bStitchStrips || bRestart) { 215 numSeparateStrips = 1; 216 } 217 218 return numSeparateStrips; 219 } 220 221 /** 222 * @param faceA 223 * the face A 224 * @param faceB 225 * the face B 226 * @return the first vertex unique to faceB 227 */ 228 static int getUniqueVertexInB(final NvFaceInfo faceA, final NvFaceInfo faceB) { 229 final int facev0 = faceB._v0; 230 if (facev0 != faceA._v0 && facev0 != faceA._v1 && facev0 != faceA._v2) { 231 return facev0; 232 } 233 234 final int facev1 = faceB._v1; 235 if (facev1 != faceA._v0 && facev1 != faceA._v1 && facev1 != faceA._v2) { 236 return facev1; 237 } 238 239 final int facev2 = faceB._v2; 240 if (facev2 != faceA._v0 && facev2 != faceA._v1 && facev2 != faceA._v2) { 241 return facev2; 242 } 243 244 // nothing is different 245 return -1; 246 } 247 248 /** 249 * @param faceA 250 * the face A 251 * @param faceB 252 * the face B 253 * @return the (at most) two vertices shared between the two faces 254 */ 255 static int[] getSharedVertices(final NvFaceInfo faceA, final NvFaceInfo faceB) { 256 final int[] vertexStore = new int[2]; 257 vertexStore[0] = vertexStore[1] = -1; 258 259 final int facev0 = faceB._v0; 260 if (facev0 == faceA._v0 || facev0 == faceA._v1 || facev0 == faceA._v2) { 261 if (vertexStore[0] == -1) { 262 vertexStore[0] = facev0; 263 } else { 264 vertexStore[1] = facev0; 265 return vertexStore; 266 } 267 } 268 269 final int facev1 = faceB._v1; 270 if (facev1 == faceA._v0 || facev1 == faceA._v1 || facev1 == faceA._v2) { 271 if (vertexStore[0] == -1) { 272 vertexStore[0] = facev1; 273 } else { 274 vertexStore[1] = facev1; 275 return vertexStore; 276 } 277 } 278 279 final int facev2 = faceB._v2; 280 if (facev2 == faceA._v0 || facev2 == faceA._v1 || facev2 == faceA._v2) { 281 if (vertexStore[0] == -1) { 282 vertexStore[0] = facev2; 283 } else { 284 vertexStore[1] = facev2; 285 return vertexStore; 286 } 287 } 288 289 return vertexStore; 290 } 291 292 static boolean isDegenerate(final NvFaceInfo face) { 293 if (face._v0 == face._v1) { 294 return true; 295 } else if (face._v0 == face._v2) { 296 return true; 297 } else if (face._v1 == face._v2) { 298 return true; 299 } else { 300 return false; 301 } 302 } 303 304 static boolean isDegenerate(final int v0, final int v1, final int v2) { 305 if (v0 == v1) { 306 return true; 307 } else if (v0 == v2) { 308 return true; 309 } else if (v1 == v2) { 310 return true; 311 } else { 312 return false; 313 } 314 } 315 316 // /////////////////////////////////////////////////////////////////////////////// 317 // 318 // Big mess of functions called during stripification 319 // Note: I removed some that were orphans - JES 320 // 321 // /////////////////////////////////////////////////////////////////////////////// 322 323 /** 324 * @param faceInfo 325 * the face info 326 * @param v0 327 * the vertex 0 328 * @param v1 329 * the vertex 1 330 * @return <code>true</code> if the face is ordered in CW fashion 331 */ 332 static boolean isCW(final NvFaceInfo faceInfo, final int v0, final int v1) { 333 if (faceInfo._v0 == v0) { 334 return faceInfo._v1 == v1; 335 } else if (faceInfo._v1 == v0) { 336 return faceInfo._v2 == v1; 337 } else { 338 return faceInfo._v0 == v1; 339 } 340 } 341 342 /** 343 * 344 * @param numIndices 345 * the number of indices 346 * @return <code>true</code> if the next face should be ordered in CW fashion 347 */ 348 static boolean nextIsCW(final int numIndices) { 349 return numIndices % 2 == 0; 350 } 351 352 /** 353 * @param indices 354 * the indices 355 * @param face 356 * the face 357 * @return vertex of the input face which is "next" in the input index list 358 */ 359 static int getNextIndex(final List<Integer> indices, final NvFaceInfo face) { 360 final int numIndices = indices.size(); 361 assert numIndices >= 2; 362 363 final int v0 = indices.get(numIndices - 2); 364 final int v1 = indices.get(numIndices - 1); 365 366 final int fv0 = face._v0; 367 final int fv1 = face._v1; 368 final int fv2 = face._v2; 369 370 if (fv0 != v0 && fv0 != v1) { 371 if (fv1 != v0 && fv1 != v1 || fv2 != v0 && fv2 != v1) { 372 NvStripifier.logger.warning("getNextIndex: Triangle doesn't have all of its vertices\n"); 373 NvStripifier.logger.warning("getNextIndex: Duplicate triangle probably got us derailed\n"); 374 } 375 return fv0; 376 } 377 if (fv1 != v0 && fv1 != v1) { 378 if (fv0 != v0 && fv0 != v1 || fv2 != v0 && fv2 != v1) { 379 NvStripifier.logger.warning("getNextIndex: Triangle doesn't have all of its vertices\n"); 380 NvStripifier.logger.warning("getNextIndex: Duplicate triangle probably got us derailed\n"); 381 } 382 return fv1; 383 } 384 if (fv2 != v0 && fv2 != v1) { 385 if (fv0 != v0 && fv0 != v1 || fv1 != v0 && fv1 != v1) { 386 NvStripifier.logger.warning("getNextIndex: Triangle doesn't have all of its vertices\n"); 387 NvStripifier.logger.warning("getNextIndex: Duplicate triangle probably got us derailed\n"); 388 } 389 return fv2; 390 } 391 392 // shouldn't get here, but let's try and fail gracefully 393 if (fv0 == fv1 || fv0 == fv2) { 394 return fv0; 395 } else if (fv1 == fv0 || fv1 == fv2) { 396 return fv1; 397 } else if (fv2 == fv0 || fv2 == fv1) { 398 return fv2; 399 } else { 400 return -1; 401 } 402 } 403 404 /** 405 * find the edge info for these two indices 406 * 407 * @param edgeInfos 408 * the edge infos 409 * @param v0 410 * the vertex 0 411 * @param v1 412 * the vertex 1 413 * @return the edge info 414 */ 415 static NvEdgeInfo findEdgeInfo(final List<NvEdgeInfo> edgeInfos, final int v0, final int v1) { 416 // we can get to it through either array because the edge infos have a v0 and v1 and there is no order except 417 // how it was first created. 418 NvEdgeInfo infoIter = edgeInfos.get(v0); 419 while (infoIter != null) { 420 if (infoIter._v0 == v0) { 421 if (infoIter._v1 == v1) { 422 return infoIter; 423 } else { 424 infoIter = infoIter._nextV0; 425 } 426 } else { 427 assert infoIter._v1 == v0; 428 if (infoIter._v0 == v1) { 429 return infoIter; 430 } else { 431 infoIter = infoIter._nextV1; 432 } 433 } 434 } 435 return null; 436 } 437 438 /** 439 * find the other face sharing these vertices 440 * 441 * @param edgeInfos 442 * the edge infos 443 * @param v0 444 * the vertex 0 445 * @param v1 446 * the vertex 1 447 * @param faceInfo 448 * the face info 449 * @return the other face sharing these vertices 450 */ 451 @SuppressWarnings("null") 452 static NvFaceInfo findOtherFace(final List<NvEdgeInfo> edgeInfos, final int v0, final int v1, 453 final NvFaceInfo faceInfo) { 454 final NvEdgeInfo edgeInfo = NvStripifier.findEdgeInfo(edgeInfos, v0, v1); 455 456 if (edgeInfo == null && v0 == v1) { 457 // we've hit a degenerate 458 return null; 459 } 460 461 assert edgeInfo != null; 462 return edgeInfo._face0 == faceInfo ? edgeInfo._face1 : edgeInfo._face0; 463 } 464 465 /** 466 * A good reset point is one near other committed areas so that we know that when we've made the longest strips its 467 * because we're stripifying in the same general orientation. 468 * 469 * @param faceInfos 470 * the face infos 471 * @param edgeInfos 472 * the edge infos 473 * @return the good reset point 474 */ 475 NvFaceInfo findGoodResetPoint(final List<NvFaceInfo> faceInfos, final List<NvEdgeInfo> edgeInfos) { 476 // we hop into different areas of the mesh to try to get other large open spans done. Areas of small strips can 477 // just be left to triangle lists added at the end. 478 NvFaceInfo result = null; 479 final int numFaces = faceInfos.size(); 480 int startPoint; 481 if (_firstTimeResetPoint) { 482 // first time, find a face with few neighbors (look for an edge of the mesh) 483 startPoint = findStartPoint(faceInfos, edgeInfos); 484 _firstTimeResetPoint = false; 485 } else { 486 startPoint = (int) (((float) numFaces - 1) * _meshJump); 487 } 488 489 if (startPoint == -1) { 490 startPoint = (int) (((float) numFaces - 1) * _meshJump); 491 492 // meshJump += 0.1f; 493 // if (meshJump > 1.0f) 494 // meshJump = .05f; 495 } 496 497 int i = startPoint; 498 do { 499 500 // if this guy isn't visited, try him 501 if (faceInfos.get(i)._stripId < 0) { 502 result = faceInfos.get(i); 503 break; 504 } 505 506 // update the index and clamp to 0-(numFaces-1) 507 if (++i >= numFaces) { 508 i = 0; 509 } 510 511 } while (i != startPoint); 512 513 // update the meshJump 514 _meshJump += 0.1f; 515 if (_meshJump > 1.0f) { 516 _meshJump = .05f; 517 } 518 519 // return the best face we found 520 return result; 521 } 522 523 /** 524 * Does the stripification, puts output strips into vector allStrips 525 * 526 * Works by setting running a number of experiments in different areas of the mesh, and accepting the one which 527 * results in the longest strips. It then accepts this, and moves on to a different area of the mesh. We try to jump 528 * around the mesh some, to ensure that large open spans of strips get generated. 529 * 530 * @param allStrips 531 * all strips 532 * @param allFaceInfos 533 * all face infos 534 * @param allEdgeInfos 535 * all edge infos 536 * @param numSamples 537 * the number of samples 538 */ 539 @SuppressWarnings("unchecked") 540 void findAllStrips(final List<NvStripInfo> allStrips, final List<NvFaceInfo> allFaceInfos, 541 final List<NvEdgeInfo> allEdgeInfos, final int numSamples) { 542 // the experiments 543 int experimentId = 0; 544 int stripId = 0; 545 boolean done = false; 546 547 while (!done) { 548 549 // 550 // PHASE 1: Set up numSamples * numEdges experiments 551 // 552 final List<NvStripInfo>[] experiments = new List[numSamples * 6]; 553 for (int i = 0; i < experiments.length; i++) { 554 experiments[i] = new ArrayList<>(); 555 } 556 557 int experimentIndex = 0; 558 final Set<NvFaceInfo> resetPoints = new HashSet<>(); 559 for (int i = 0; i < numSamples; i++) { 560 561 // Try to find another good reset point. 562 // If there are none to be found, we are done 563 final NvFaceInfo nextFace = findGoodResetPoint(allFaceInfos, allEdgeInfos); 564 if (nextFace == null) { 565 done = true; 566 break; 567 } 568 // If we have already evaluated starting at this face in this slew 569 // of experiments, then skip going any further 570 else if (resetPoints.contains(nextFace)) { 571 continue; 572 } 573 574 // trying it now... 575 resetPoints.add(nextFace); 576 577 // otherwise, we shall now try experiments for starting on the 01,12, and 20 edges 578 assert nextFace._stripId < 0; 579 580 // build the strip off of this face's 0-1 edge 581 final NvEdgeInfo edge01 = NvStripifier.findEdgeInfo(allEdgeInfos, nextFace._v0, nextFace._v1); 582 final NvStripInfo strip01 = new NvStripInfo(new NvStripStartInfo(nextFace, edge01, true), stripId++, 583 experimentId++); 584 experiments[experimentIndex++].add(strip01); 585 586 // build the strip off of this face's 1-0 edge 587 final NvEdgeInfo edge10 = NvStripifier.findEdgeInfo(allEdgeInfos, nextFace._v0, nextFace._v1); 588 final NvStripInfo strip10 = new NvStripInfo(new NvStripStartInfo(nextFace, edge10, false), stripId++, 589 experimentId++); 590 experiments[experimentIndex++].add(strip10); 591 592 // build the strip off of this face's 1-2 edge 593 final NvEdgeInfo edge12 = NvStripifier.findEdgeInfo(allEdgeInfos, nextFace._v1, nextFace._v2); 594 final NvStripInfo strip12 = new NvStripInfo(new NvStripStartInfo(nextFace, edge12, true), stripId++, 595 experimentId++); 596 experiments[experimentIndex++].add(strip12); 597 598 // build the strip off of this face's 2-1 edge 599 final NvEdgeInfo edge21 = NvStripifier.findEdgeInfo(allEdgeInfos, nextFace._v1, nextFace._v2); 600 final NvStripInfo strip21 = new NvStripInfo(new NvStripStartInfo(nextFace, edge21, false), stripId++, 601 experimentId++); 602 experiments[experimentIndex++].add(strip21); 603 604 // build the strip off of this face's 2-0 edge 605 final NvEdgeInfo edge20 = NvStripifier.findEdgeInfo(allEdgeInfos, nextFace._v2, nextFace._v0); 606 final NvStripInfo strip20 = new NvStripInfo(new NvStripStartInfo(nextFace, edge20, true), stripId++, 607 experimentId++); 608 experiments[experimentIndex++].add(strip20); 609 610 // build the strip off of this face's 0-2 edge 611 final NvEdgeInfo edge02 = NvStripifier.findEdgeInfo(allEdgeInfos, nextFace._v2, nextFace._v0); 612 final NvStripInfo strip02 = new NvStripInfo(new NvStripStartInfo(nextFace, edge02, false), stripId++, 613 experimentId++); 614 experiments[experimentIndex++].add(strip02); 615 } 616 617 // 618 // PHASE 2: Iterate through that we setup in the last phase 619 // and really build each of the strips and strips that follow to see how 620 // far we get 621 // 622 final int numExperiments = experimentIndex; 623 for (int i = 0; i < numExperiments; i++) { 624 // get the strip set 625 NvStripInfo stripIter = experiments[i].get(0); 626 627 // build the first strip of the list 628 stripIter.build(allEdgeInfos, allFaceInfos); 629 final int currExperimentId = stripIter._experimentId; 630 631 final NvStripStartInfo startInfo = new NvStripStartInfo(null, null, false); 632 while (findTraversal(allFaceInfos, allEdgeInfos, stripIter, startInfo)) { 633 634 // create the new strip info 635 stripIter = new NvStripInfo(startInfo, stripId++, currExperimentId); 636 637 // build the next strip 638 stripIter.build(allEdgeInfos, allFaceInfos); 639 640 // add it to the list 641 experiments[i].add(stripIter); 642 } 643 } 644 645 // 646 // Phase 3: Find the experiment that has the most promise 647 // 648 int bestIndex = 0; 649 double bestValue = 0; 650 for (int i = 0; i < numExperiments; i++) { 651 final float avgStripSizeWeight = 1.0f; 652 // final float numTrisWeight = 0.0f; // unused 653 final float numStripsWeight = 0.0f; 654 final float avgStripSize = avgStripSize(experiments[i]); 655 final float numStrips = experiments[i].size(); 656 final float value = avgStripSize * avgStripSizeWeight + numStrips * numStripsWeight; 657 // float value = 1.f / numStrips; 658 // float value = numStrips * avgStripSize; 659 660 if (value > bestValue) { 661 bestValue = value; 662 bestIndex = i; 663 } 664 } 665 666 // 667 // Phase 4: commit the best experiment of the bunch 668 // 669 commitStrips(allStrips, experiments[bestIndex]); 670 } 671 } 672 673 /** 674 * Splits the input vector of strips (allBigStrips) into smaller, cache friendly pieces, then reorders these pieces 675 * to maximize cache hits. The final strips are stored in outStrips 676 * 677 * @param allStrips 678 * all strips 679 * @param outStrips 680 * the resulting strips 681 * @param edgeInfos 682 * the edge infos 683 * @param outFaceList 684 * the resulting face list 685 */ 686 void splitUpStripsAndOptimize(final List<NvStripInfo> allStrips, final List<NvStripInfo> outStrips, 687 final List<NvEdgeInfo> edgeInfos, final List<NvFaceInfo> outFaceList) { 688 final int threshold = _cacheSize; 689 final List<NvStripInfo> tempStrips = new ArrayList<>(); 690 691 // split up strips into threshold-sized pieces 692 for (int i = 0; i < allStrips.size(); i++) { 693 final NvStripInfo allStripI = allStrips.get(i); 694 NvStripInfo currentStrip; 695 final NvStripStartInfo startInfo = new NvStripStartInfo(null, null, false); 696 697 int actualStripSize = 0; 698 for (final NvFaceInfo face : allStripI._faces) { 699 if (!NvStripifier.isDegenerate(face)) { 700 actualStripSize++; 701 } 702 } 703 704 if (actualStripSize > threshold) { 705 706 final int numTimes = actualStripSize / threshold; 707 int numLeftover = actualStripSize % threshold; 708 709 int degenerateCount = 0, j = 0; 710 for (; j < numTimes; j++) { 711 currentStrip = new NvStripInfo(startInfo, 0, -1); 712 713 int faceCtr = j * threshold + degenerateCount; 714 boolean bFirstTime = true; 715 while (faceCtr < threshold + j * threshold + degenerateCount) { 716 if (NvStripifier.isDegenerate(allStripI._faces.get(faceCtr))) { 717 degenerateCount++; 718 719 // last time or first time through, no need for a degenerate 720 if ((faceCtr + 1 != threshold + j * threshold + degenerateCount 721 || j == numTimes - 1 && numLeftover < 4 && numLeftover > 0) && !bFirstTime) { 722 currentStrip._faces.add(allStripI._faces.get(faceCtr++)); 723 } else { 724 ++faceCtr; 725 } 726 } else { 727 currentStrip._faces.add(allStripI._faces.get(faceCtr++)); 728 bFirstTime = false; 729 } 730 } 731 if (j == numTimes - 1) // last time through 732 { 733 if (numLeftover < 4 && numLeftover > 0) // way too small 734 { 735 // just add to last strip 736 int ctr = 0; 737 while (ctr < numLeftover) { 738 if (NvStripifier.isDegenerate(allStripI._faces.get(faceCtr))) { 739 ++degenerateCount; 740 } else { 741 ++ctr; 742 } 743 currentStrip._faces.add(allStripI._faces.get(faceCtr++)); 744 } 745 numLeftover = 0; 746 } 747 } 748 tempStrips.add(currentStrip); 749 } 750 int leftOff = j * threshold + degenerateCount; 751 752 if (numLeftover != 0) { 753 currentStrip = new NvStripInfo(startInfo, 0, -1); 754 755 int ctr = 0; 756 boolean bFirstTime = true; 757 while (ctr < numLeftover) { 758 if (!NvStripifier.isDegenerate(allStripI._faces.get(leftOff))) { 759 ctr++; 760 bFirstTime = false; 761 currentStrip._faces.add(allStripI._faces.get(leftOff++)); 762 } else if (!bFirstTime) { 763 currentStrip._faces.add(allStripI._faces.get(leftOff++)); 764 } else { 765 leftOff++; 766 } 767 } 768 769 tempStrips.add(currentStrip); 770 } 771 } else { 772 // we're not just doing a tempStrips.add(allBigStrips.get(i)) because 773 // this way we can delete allBigStrips later to free the memory 774 currentStrip = new NvStripInfo(startInfo, 0, -1); 775 776 for (int j = 0; j < allStripI._faces.size(); j++) { 777 currentStrip._faces.add(allStripI._faces.get(j)); 778 } 779 780 tempStrips.add(currentStrip); 781 } 782 } 783 784 // add small strips to face list 785 final List<NvStripInfo> tempStrips2 = new ArrayList<>(); 786 removeSmallStrips(tempStrips, tempStrips2, outFaceList); 787 788 outStrips.clear(); 789 // screw optimization for now 790 // for(i = 0; i < tempStrips.size(); ++i) 791 // outStrips.add(tempStrips.get(i)); 792 793 if (tempStrips2.size() != 0) { 794 // Optimize for the vertex cache 795 final VertexCache vcache = new VertexCache(_cacheSize); 796 797 float bestNumHits = -1.0f; 798 float numHits; 799 int bestIndex = 0; 800 int firstIndex = 0; 801 float minCost = 10000.0f; 802 803 for (int i = 0; i < tempStrips2.size(); i++) { 804 final NvStripInfo tempStrips2I = tempStrips2.get(i); 805 int numNeighbors = 0; 806 807 // find strip with least number of neighbors per face 808 for (int j = 0; j < tempStrips2I._faces.size(); j++) { 809 numNeighbors += numNeighbors(tempStrips2I._faces.get(j), edgeInfos); 810 } 811 812 final float currCost = numNeighbors / (float) tempStrips2I._faces.size(); 813 if (currCost < minCost) { 814 minCost = currCost; 815 firstIndex = i; 816 } 817 } 818 819 final NvStripInfo tempStrips2FirstIndex = tempStrips2.get(firstIndex); 820 updateCacheStrip(vcache, tempStrips2FirstIndex); 821 outStrips.add(tempStrips2FirstIndex); 822 823 tempStrips2FirstIndex._visited = true; 824 825 boolean bWantsCW = tempStrips2FirstIndex._faces.size() % 2 == 0; 826 827 // XXX: this n^2 algo is what slows down stripification so much.... needs to be improved 828 while (true) { 829 bestNumHits = -1.0f; 830 831 // find best strip to add next, given the current cache 832 for (int i = 0; i < tempStrips2.size(); i++) { 833 final NvStripInfo tempStrips2I = tempStrips2.get(i); 834 if (tempStrips2I._visited) { 835 continue; 836 } 837 838 numHits = calcNumHitsStrip(vcache, tempStrips2I); 839 if (numHits > bestNumHits) { 840 bestNumHits = numHits; 841 bestIndex = i; 842 } else if (numHits >= bestNumHits) { 843 // check previous strip to see if this one requires it to switch polarity 844 final NvStripInfo strip = tempStrips2I; 845 final int nStripFaceCount = strip._faces.size(); 846 847 final NvFaceInfo tFirstFace = new NvFaceInfo(strip._faces.get(0)); 848 849 // If there is a second face, reorder vertices such that the 850 // unique vertex is first 851 if (nStripFaceCount > 1) { 852 final int nUnique = NvStripifier.getUniqueVertexInB(strip._faces.get(1), tFirstFace); 853 if (nUnique == tFirstFace._v1) { 854 final int store = tFirstFace._v1; 855 tFirstFace._v1 = tFirstFace._v0; 856 tFirstFace._v0 = store; 857 } else if (nUnique == tFirstFace._v2) { 858 final int store = tFirstFace._v2; 859 tFirstFace._v2 = tFirstFace._v0; 860 tFirstFace._v0 = store; 861 } 862 863 // If there is a third face, reorder vertices such that the 864 // shared vertex is last 865 if (nStripFaceCount > 2) { 866 final int[] nShared = NvStripifier.getSharedVertices(strip._faces.get(2), tFirstFace); 867 if (nShared[0] == tFirstFace._v1 && nShared[1] == -1) { 868 final int store = tFirstFace._v2; 869 tFirstFace._v2 = tFirstFace._v1; 870 tFirstFace._v1 = store; 871 } 872 } 873 } 874 875 // Check CW/CCW ordering 876 if (bWantsCW == NvStripifier.isCW(strip._faces.get(0), tFirstFace._v0, tFirstFace._v1)) { 877 // I like this one! 878 bestIndex = i; 879 } 880 } 881 } 882 883 if (bestNumHits == -1.0f) { 884 break; 885 } 886 tempStrips2.get(bestIndex)._visited = true; 887 updateCacheStrip(vcache, tempStrips2.get(bestIndex)); 888 outStrips.add(tempStrips2.get(bestIndex)); 889 bWantsCW = tempStrips2.get(bestIndex)._faces.size() % 2 == 0 ? bWantsCW : !bWantsCW; 890 } 891 } 892 } 893 894 /** 895 * @param allStrips 896 * the whole strip vector...all small strips will be deleted from this list, to avoid leaking mem 897 * @param allBigStrips 898 * an out parameter which will contain all strips above minStripLength 899 * @param faceList 900 * an out parameter which will contain all faces which were removed from the striplist 901 */ 902 void removeSmallStrips(final List<NvStripInfo> allStrips, final List<NvStripInfo> allBigStrips, 903 final List<NvFaceInfo> faceList) { 904 faceList.clear(); 905 allBigStrips.clear(); // make sure these are empty 906 final List<NvFaceInfo> tempFaceList = new ArrayList<>(); 907 908 for (int i = 0; i < allStrips.size(); i++) { 909 final NvStripInfo allStripI = allStrips.get(i); 910 if (allStripI._faces.size() < _minStripLength) { 911 // strip is too small, add faces to faceList 912 for (int j = 0; j < allStripI._faces.size(); j++) { 913 tempFaceList.add(allStripI._faces.get(j)); 914 } 915 } else { 916 allBigStrips.add(allStripI); 917 } 918 } 919 920 if (!tempFaceList.isEmpty()) { 921 final boolean[] bVisitedList = new boolean[tempFaceList.size()]; 922 final VertexCache vcache = new VertexCache(_cacheSize); 923 924 int bestNumHits = -1; 925 int numHits = 0; 926 int bestIndex = 0; 927 928 while (true) { 929 bestNumHits = -1; 930 931 // find best face to add next, given the current cache 932 for (int i = 0; i < tempFaceList.size(); i++) { 933 if (bVisitedList[i]) { 934 continue; 935 } 936 937 numHits = calcNumHitsFace(vcache, tempFaceList.get(i)); 938 if (numHits > bestNumHits) { 939 bestNumHits = numHits; 940 bestIndex = i; 941 } 942 } 943 944 if (bestNumHits == -1.0f) { 945 break; 946 } 947 bVisitedList[bestIndex] = true; 948 updateCacheFace(vcache, tempFaceList.get(bestIndex)); 949 faceList.add(tempFaceList.get(bestIndex)); 950 } 951 } 952 } 953 954 /** 955 * Finds the next face to start the next strip on. 956 * 957 * @param faceInfos 958 * the face infos 959 * @param edgeInfos 960 * the edge infos 961 * @param strip 962 * the strip 963 * @param startInfo 964 * the start info 965 * @return <code>true</code> if the next face to start the next strip on is found 966 */ 967 boolean findTraversal(final List<NvFaceInfo> faceInfos, final List<NvEdgeInfo> edgeInfos, final NvStripInfo strip, 968 final NvStripStartInfo startInfo) { 969 // if the strip was v0.v1 on the edge, then v1 will be a vertex in the next edge. 970 final int v = strip._startInfo._toV1 ? strip._startInfo._startEdge._v1 : strip._startInfo._startEdge._v0; 971 972 NvFaceInfo untouchedFace = null; 973 NvEdgeInfo edgeIter = edgeInfos.get(v); 974 while (edgeIter != null) { 975 final NvFaceInfo face0 = edgeIter._face0; 976 final NvFaceInfo face1 = edgeIter._face1; 977 if (face0 != null && !strip.IsInStrip(face0) && face1 != null && !strip.isMarked(face1)) { 978 untouchedFace = face1; 979 break; 980 } 981 if (face1 != null && !strip.IsInStrip(face1) && face0 != null && !strip.isMarked(face0)) { 982 untouchedFace = face0; 983 break; 984 } 985 986 // find the next edgeIter 987 edgeIter = edgeIter._v0 == v ? edgeIter._nextV0 : edgeIter._nextV1; 988 } 989 990 startInfo._startFace = untouchedFace; 991 startInfo._startEdge = edgeIter; 992 if (edgeIter != null) { 993 if (strip.sharesEdge(startInfo._startFace, edgeInfos)) { 994 startInfo._toV1 = edgeIter._v0 == v; // note! used to be m_v1 995 } else { 996 startInfo._toV1 = edgeIter._v1 == v; 997 } 998 } 999 return startInfo._startFace != null; 1000 } 1001 1002 /** 1003 * "Commits" the input strips by setting their m_experimentId to -1 and adding to the allStrips vector 1004 * 1005 * @param allStrips 1006 * all strips 1007 * @param strips 1008 * the strips 1009 */ 1010 void commitStrips(final List<NvStripInfo> allStrips, final List<NvStripInfo> strips) { 1011 // Iterate through strips 1012 for (final NvStripInfo strip : strips) { 1013 // Tell the strip that it is now real 1014 strip._experimentId = -1; 1015 1016 // add to the list of real strips 1017 allStrips.add(strip); 1018 1019 // Iterate through the faces of the strip 1020 // Tell the faces of the strip that they belong to a real strip now 1021 final List<NvFaceInfo> faces = strip._faces; 1022 for (final NvFaceInfo face : faces) { 1023 strip.markTriangle(face); 1024 } 1025 } 1026 } 1027 1028 /** 1029 * 1030 * @param strips 1031 * the strips 1032 * @return the average strip size of the input vector of strips 1033 */ 1034 float avgStripSize(final List<NvStripInfo> strips) { 1035 int sizeAccum = 0; 1036 for (final NvStripInfo strip : strips) { 1037 sizeAccum += strip._faces.size(); 1038 sizeAccum -= strip._numDegenerates; 1039 } 1040 return (float) sizeAccum / (float) strips.size(); 1041 } 1042 1043 /** 1044 * Finds a good starting point, namely one which has only one neighbor 1045 * 1046 * @param faceInfos 1047 * the face infos 1048 * @param edgeInfos 1049 * the edge infos 1050 * @return the good starting point or -1 1051 */ 1052 int findStartPoint(final List<NvFaceInfo> faceInfos, final List<NvEdgeInfo> edgeInfos) { 1053 int bestCtr = -1; 1054 int bestIndex = -1; 1055 1056 int i = 0; 1057 for (final NvFaceInfo faceInfo : faceInfos) { 1058 int ctr = 0; 1059 1060 if (NvStripifier.findOtherFace(edgeInfos, faceInfo._v0, faceInfo._v1, faceInfo) == null) { 1061 ctr++; 1062 } 1063 if (NvStripifier.findOtherFace(edgeInfos, faceInfo._v1, faceInfo._v2, faceInfo) == null) { 1064 ctr++; 1065 } 1066 if (NvStripifier.findOtherFace(edgeInfos, faceInfo._v2, faceInfo._v0, faceInfo) == null) { 1067 ctr++; 1068 } 1069 if (ctr > bestCtr) { 1070 bestCtr = ctr; 1071 bestIndex = i; 1072 } 1073 i++; 1074 } 1075 1076 if (bestCtr == 0) { 1077 return -1; 1078 } else { 1079 return bestIndex; 1080 } 1081 } 1082 1083 /** 1084 * Updates the input vertex cache with this strip's vertices 1085 * 1086 * @param vcache 1087 * the vertex cache 1088 * @param strip 1089 * the strip 1090 */ 1091 void updateCacheStrip(final VertexCache vcache, final NvStripInfo strip) { 1092 for (final NvFaceInfo face : strip._faces) { 1093 updateCacheFace(vcache, face); 1094 } 1095 } 1096 1097 /** 1098 * Updates the input vertex cache with this face's vertices 1099 * 1100 * @param vcache 1101 * the vertex cache 1102 * @param face 1103 * the face 1104 */ 1105 void updateCacheFace(final VertexCache vcache, final NvFaceInfo face) { 1106 if (!vcache.inCache(face._v0)) { 1107 vcache.addEntry(face._v0); 1108 } 1109 1110 if (!vcache.inCache(face._v1)) { 1111 vcache.addEntry(face._v1); 1112 } 1113 1114 if (!vcache.inCache(face._v2)) { 1115 vcache.addEntry(face._v2); 1116 } 1117 } 1118 1119 /** 1120 * @param vcache 1121 * the vertex cache 1122 * @param strip 1123 * the strip 1124 * @return the number of cache hits per face in the strip 1125 */ 1126 float calcNumHitsStrip(final VertexCache vcache, final NvStripInfo strip) { 1127 int numHits = 0; 1128 int numFaces = 0; 1129 1130 for (final NvFaceInfo face : strip._faces) { 1131 if (vcache.inCache(face._v0)) { 1132 ++numHits; 1133 } 1134 1135 if (vcache.inCache(face._v1)) { 1136 ++numHits; 1137 } 1138 1139 if (vcache.inCache(face._v2)) { 1140 ++numHits; 1141 } 1142 1143 numFaces++; 1144 } 1145 1146 return (float) numHits / (float) numFaces; 1147 } 1148 1149 /** 1150 * @param vcache 1151 * the vertex cache 1152 * @param face 1153 * the face 1154 * @return the number of cache hits in the face 1155 */ 1156 int calcNumHitsFace(final VertexCache vcache, final NvFaceInfo face) { 1157 int numHits = 0; 1158 1159 if (vcache.inCache(face._v0)) { 1160 numHits++; 1161 } 1162 1163 if (vcache.inCache(face._v1)) { 1164 numHits++; 1165 } 1166 1167 if (vcache.inCache(face._v2)) { 1168 numHits++; 1169 } 1170 1171 return numHits; 1172 } 1173 1174 /** 1175 * 1176 * @param face 1177 * the face 1178 * @param edgeInfoVec 1179 * the edge info list 1180 * @return the number of neighbors that this face has 1181 */ 1182 int numNeighbors(final NvFaceInfo face, final List<NvEdgeInfo> edgeInfoVec) { 1183 int numNeighbors = 0; 1184 1185 if (NvStripifier.findOtherFace(edgeInfoVec, face._v0, face._v1, face) != null) { 1186 numNeighbors++; 1187 } 1188 1189 if (NvStripifier.findOtherFace(edgeInfoVec, face._v1, face._v2, face) != null) { 1190 numNeighbors++; 1191 } 1192 1193 if (NvStripifier.findOtherFace(edgeInfoVec, face._v2, face._v0, face) != null) { 1194 numNeighbors++; 1195 } 1196 1197 return numNeighbors; 1198 } 1199 1200 /** 1201 * Builds the list of all face and edge infos 1202 * 1203 * @param faceInfos 1204 * the face infos 1205 * @param edgeInfos 1206 * the edge infos 1207 * @param maxIndex 1208 * the maximum index 1209 */ 1210 void buildStripifyInfo(final List<NvFaceInfo> faceInfos, final List<NvEdgeInfo> edgeInfos, final int maxIndex) { 1211 // reserve space for the face infos, but do not resize them. 1212 final int numIndices = _indices.size(); 1213 1214 // we actually resize the edge infos, so we must initialize to null 1215 for (int i = 0; i <= maxIndex; i++) { 1216 edgeInfos.add(null); 1217 } 1218 1219 // iterate through the triangles of the triangle list 1220 final int numTriangles = numIndices / 3; 1221 int index = 0; 1222 final boolean[] bFaceUpdated = new boolean[3]; 1223 1224 for (int i = 0; i < numTriangles; i++) { 1225 boolean bMightAlreadyExist = true; 1226 bFaceUpdated[0] = false; 1227 bFaceUpdated[1] = false; 1228 bFaceUpdated[2] = false; 1229 1230 // grab the indices 1231 final int v0 = _indices.get(index++); 1232 final int v1 = _indices.get(index++); 1233 final int v2 = _indices.get(index++); 1234 1235 // we disregard degenerates 1236 if (NvStripifier.isDegenerate(v0, v1, v2)) { 1237 continue; 1238 } 1239 1240 // create the face info and add it to the list of faces, but only if this exact face doesn't already 1241 // exist in the list 1242 final NvFaceInfo faceInfo = new NvFaceInfo(v0, v1, v2); 1243 1244 // grab the edge infos, creating them if they do not already exist 1245 NvEdgeInfo edgeInfo01 = NvStripifier.findEdgeInfo(edgeInfos, v0, v1); 1246 if (edgeInfo01 == null) { 1247 // since one of it's edges isn't in the edge data structure, it can't already exist in the face 1248 // structure 1249 bMightAlreadyExist = false; 1250 1251 // create the info 1252 edgeInfo01 = new NvEdgeInfo(v0, v1); 1253 1254 // update the linked list on both 1255 edgeInfo01._nextV0 = edgeInfos.get(v0); 1256 edgeInfo01._nextV1 = edgeInfos.get(v1); 1257 edgeInfos.set(v0, edgeInfo01); 1258 edgeInfos.set(v1, edgeInfo01); 1259 1260 // set face 0 1261 edgeInfo01._face0 = faceInfo; 1262 } else { 1263 if (edgeInfo01._face1 != null) { 1264 NvStripifier.logger 1265 .warning("BuildStripifyInfo: > 2 triangles on an edge... uncertain consequences\n"); 1266 } else { 1267 edgeInfo01._face1 = faceInfo; 1268 bFaceUpdated[0] = true; 1269 } 1270 } 1271 1272 // grab the edge infos, creating them if they do not already exist 1273 NvEdgeInfo edgeInfo12 = NvStripifier.findEdgeInfo(edgeInfos, v1, v2); 1274 if (edgeInfo12 == null) { 1275 bMightAlreadyExist = false; 1276 1277 // create the info 1278 edgeInfo12 = new NvEdgeInfo(v1, v2); 1279 1280 // update the linked list on both 1281 edgeInfo12._nextV0 = edgeInfos.get(v1); 1282 edgeInfo12._nextV1 = edgeInfos.get(v2); 1283 edgeInfos.set(v1, edgeInfo12); 1284 edgeInfos.set(v2, edgeInfo12); 1285 1286 // set face 0 1287 edgeInfo12._face0 = faceInfo; 1288 } else { 1289 if (edgeInfo12._face1 != null) { 1290 NvStripifier.logger 1291 .warning("BuildStripifyInfo: > 2 triangles on an edge... uncertain consequences\n"); 1292 } else { 1293 edgeInfo12._face1 = faceInfo; 1294 bFaceUpdated[1] = true; 1295 } 1296 } 1297 1298 // grab the edge infos, creating them if they do not already exist 1299 NvEdgeInfo edgeInfo20 = NvStripifier.findEdgeInfo(edgeInfos, v2, v0); 1300 if (edgeInfo20 == null) { 1301 bMightAlreadyExist = false; 1302 1303 // create the info 1304 edgeInfo20 = new NvEdgeInfo(v2, v0); 1305 1306 // update the linked list on both 1307 edgeInfo20._nextV0 = edgeInfos.get(v2); 1308 edgeInfo20._nextV1 = edgeInfos.get(v0); 1309 edgeInfos.set(v2, edgeInfo20); 1310 edgeInfos.set(v0, edgeInfo20); 1311 1312 // set face 0 1313 edgeInfo20._face0 = faceInfo; 1314 } else { 1315 if (edgeInfo20._face1 != null) { 1316 NvStripifier.logger 1317 .warning("BuildStripifyInfo: > 2 triangles on an edge... uncertain consequences\n"); 1318 } else { 1319 edgeInfo20._face1 = faceInfo; 1320 bFaceUpdated[2] = true; 1321 } 1322 } 1323 1324 if (bMightAlreadyExist) { 1325 if (!alreadyExists(faceInfo, faceInfos)) { 1326 faceInfos.add(faceInfo); 1327 } else { 1328 // cleanup pointers that point to this deleted face 1329 if (bFaceUpdated[0]) { 1330 edgeInfo01._face1 = null; 1331 } 1332 if (bFaceUpdated[1]) { 1333 edgeInfo12._face1 = null; 1334 } 1335 if (bFaceUpdated[2]) { 1336 edgeInfo20._face1 = null; 1337 } 1338 } 1339 } else { 1340 faceInfos.add(faceInfo); 1341 } 1342 } 1343 } 1344 1345 boolean alreadyExists(final NvFaceInfo toFind, final List<NvFaceInfo> faceInfos) { 1346 for (final NvFaceInfo face : faceInfos) { 1347 if (face._v0 == toFind._v0 && face._v1 == toFind._v1 && face._v2 == toFind._v2) { 1348 return true; 1349 } 1350 } 1351 1352 return false; 1353 } 1354}