float FindBestScore ( int iCountdown, float fCurrentScore, float fInputBestSoFar, //WORD *pwIndices, ScoreTri **ppstCurTris, // Pointer to a list of pointers to ScoreTris int iNumTris, WORD *pwResult ) { int iLookahead = iCountdown; if ( iLookahead > LOOKAHEAD ) { iLookahead = LOOKAHEAD; } //VERIFY ( fCurrentScore < 1e9 ); // At the start, limit the lookahead, or it takes ages. if ( ( iNumTris > 100 ) && ( fCurrentScore < 50.0f ) && ( iLookahead > 2 ) ) { iLookahead = 2; } float fBestSoFar = fInputBestSoFar; // Given the BestSoFar score, what is the average cost of each lookahead tri? float fAverageCost = ( fBestSoFar - fCurrentScore ) / (int)iLookahead; // And now allow tris that are a bit worse. float fExpensiveCutoff = fAverageCost * fExpensiveFactor; VERIFY ( iCountdown > 0 ); VERIFY ( iNumTris > 0 ); int j; if ( iNumTris == 1 ) { // Well, that's an easy decision then. // Find score after removal. //float fTriScore = CacheAddTri ( pwIndices[0], pwIndices[1], pwIndices[2], TRUE ); ScoreTri *pstCur = ppstCurTris[0]; float fTriScore = CacheAddTri ( pstCur->wIndex[0], pstCur->wIndex[1], pstCur->wIndex[2], TRUE ); for ( j = 0; j < 3; j++ ) { // Use the valence score after the tri has been removed. //int iValence = (*(iValenceCounts.item(pwIndices[j]))) - 1; int iValence = (svVertex.item(pstCur->wIndex[j])->iCurrentValence) - 1; VERIFY ( iValence >= 0 ); if ( iValence < c_iMaxValenceBoost ) { fTriScore += fValenceBoost[iValence]; } else { fTriScore += fValenceBoost[c_iMaxValenceBoost-1]; } } fTriScore += fCurrentScore; //pwResult[0] = pwIndices[0]; //pwResult[1] = pwIndices[1]; //pwResult[2] = pwIndices[2]; pwResult[0] = pstCur->wIndex[0]; pwResult[1] = pstCur->wIndex[1]; pwResult[2] = pstCur->wIndex[2]; if ( fTriScore > fBestSoFar ) { // Oh well. // (actually, not sure this ever happens). return fInputBestSoFar; } else { return fTriScore; } } #ifdef _DEBUG for ( int k = 0; k < iNumTris; k++ ) { VERIFY ( !ppstCurTris[k]->bAllowed ); VERIFY ( !ppstCurTris[k]->bUsed ); } #endif // Should we limit ourselves to tris that share at least one vert with the previous one? bool bNoMadJumps = FALSE; WORD wPrevIndices[3]; //if ( ( fBestSoFar - fCurrentScore ) < (float)iCountdown ) // The lookahead score is lower than the countdown, so doing mad jumps // is not going to do sensible things. if ( iCountdown == iLookahead ) { // Ah - this is probably a lookahead, not assembling the real list. // Find the previous indices from the cache (a bit of a roundabout method). int iNumAllowed = 0; if ( CacheGetLastTri ( wPrevIndices ) ) { bNoMadJumps = TRUE; // And mark all the tris that these used as "allowed" for ( int i = 0; i < 3; i++ ) { ScoreVertex *psv = svVertex.item(wPrevIndices[i]); for ( int j = 0; j < psv->stri.size(); j++ ) { ScoreTri *pst = (*(psv->stri.item(j))); if ( ( !pst->bUsed ) && ( !pst->bAllowed ) ) { pst->bAllowed = TRUE; iNumAllowed++; } } } if ( iNumAllowed < 1 ) { // Oops - we've probably gone into a dead-end, so that very few tris // are allowed. So open the selection up again. bNoMadJumps = FALSE; for ( int i = 0; i < 3; i++ ) { ScoreVertex *psv = svVertex.item(wPrevIndices[i]); for ( int j = 0; j < psv->stri.size(); j++ ) { ScoreTri *pst = (*(psv->stri.item(j))); if ( !pst->bUsed ) { pst->bAllowed = FALSE; } } } } } } // Add the tris to a new priority queue, sorting by score. //BinaryHeap<WORD, float> NewHeap; BinaryHeap<ScoreTri*, float> NewHeap; int i; //WORD *pwCurIndex = pwIndices; for ( i = 0; i < iNumTris; i++ ) { ScoreTri *pstCurTri = ppstCurTris[i]; float fTriScore = 0.0f; if ( bNoMadJumps ) { if ( !pstCurTri->bAllowed ) { continue; } #if IGNORE_VALENCE_FOR_LOOKAHEADS // Only use the valence stuff on non-lookaheads. // To make the scores comparable, add the maximum valence boost all the time. fTriScore += 3.0f * fValenceBoost[c_iMaxValenceBoost-1]; #endif } #if IGNORE_VALENCE_FOR_LOOKAHEADS else #endif { // Only use the valence stuff on non-lookaheads. for ( j = 0; j < 3; j++ ) { // Use the valence score after the tri has been removed. //int iValence = (*(iValenceCounts.item(pwCurIndex[j]))) - 1; int iValence = (svVertex.item(pstCurTri->wIndex[j])->iCurrentValence) - 1; VERIFY ( iValence >= 0 ); if ( iValence < c_iMaxValenceBoost ) { fTriScore += fValenceBoost[iValence]; } else { fTriScore += fValenceBoost[c_iMaxValenceBoost-1]; } } } if ( fTriScore > fExpensiveCutoff ) { // This tri is a lot more expensive than the average cost of the BestSoFar tris. // It's very unlikely to give us a good result. continue; } if ( fCurrentScore + fTriScore >= fBestSoFar ) { // We've already gone more than the best score. //pwCurIndex += 3; continue; } // And the vertex cost. fTriScore += CacheAddTri ( pstCurTri->wIndex[0], pstCurTri->wIndex[1], pstCurTri->wIndex[2], TRUE ); if ( fTriScore > fExpensiveCutoff ) { // This tri is a lot more expensive than the average cost of the BestSoFar tris. // It's very unlikely to give us a good result. continue; } if ( fCurrentScore + fTriScore >= fBestSoFar ) { // We've already gone more than the best score. //pwCurIndex += 3; continue; } // And bung it in the heap. // We -ve the score so that the first in the heap is the one with the _least_ score. NewHeap.Add ( &(ppstCurTris[i]), -fTriScore ); //pwCurIndex += 3; } // Undo the "allowed" flags. if ( bNoMadJumps ) { for ( int i = 0; i < 3; i++ ) { ScoreVertex *psv = svVertex.item(wPrevIndices[i]); for ( int j = 0; j < psv->stri.size(); j++ ) { (*(psv->stri.item(j)))->bAllowed = FALSE; } } } #ifdef _DEBUG for ( k = 0; k < iNumTris; k++ ) { VERIFY ( !ppstCurTris[k]->bAllowed ); VERIFY ( !ppstCurTris[k]->bUsed ); } #endif // Now extract from the heap, best score to worst. // This attempts to get early-outs very quickly and prevent too much recursion. //WORD *pwBest = NULL; //WORD *pwCur = NewHeap.FindFirst(); ScoreTri **ppstBest = NULL; ScoreTri **ppstCur = NewHeap.FindFirst(); //if ( pwCur == NULL ) if ( ppstCur == NULL ) { // Found nothing that was better. return fBestSoFar; } // Above this score, just don't bother. float fCutoffScore = fCutoffFactor * NewHeap.GetCurrentSort(); #ifdef _DEBUG float fPrevScore = 1.0f; #endif int iTried = 0; //while ( pwCur != NULL ) while ( ppstCur != NULL ) { ScoreTri *pstCur = *ppstCur; float fThisTriNegScore = NewHeap.GetCurrentSort(); NewHeap.RemoveFirst(); #ifdef _DEBUG // Check this heap actually works! VERIFY ( fThisTriNegScore <= fPrevScore ); fPrevScore = fThisTriNegScore; #endif if ( fThisTriNegScore < fCutoffScore ) { // Reached the cutoff for this tri - don't bother continuing. break; } float fScore = fCurrentScore - fThisTriNegScore; if ( fScore >= fBestSoFar ) { // We've already gone more than the best score. // The reast of the heap is bound to be greater, so don't bother continuing. break; } if ( bNoMadJumps ) { iTried++; if ( iTried > iLookaheadCutoff ) { // Tried enough tris - don't want to cause a combinatorial explosion. break; } } // Do the valencies. #ifdef _DEBUG float fValenceScore = 0.0f; #endif #if IGNORE_VALENCE_FOR_LOOKAHEADS if ( bNoMadJumps ) { // Only use the valence stuff on non-lookaheads. // To make the scores comparable, add the maximum valence boost all the time. fValenceScore = 3.0f * fValenceBoost[c_iMaxValenceBoost-1]; } else #endif { for ( j = 0; j < 3; j++ ) { //int iValence = --(*(iValenceCounts.item(pwCur[j]))); int iValence = --(svVertex.item(pstCur->wIndex[j])->iCurrentValence); #ifdef _DEBUG VERIFY ( iValence >= 0 ); if ( iValence < c_iMaxValenceBoost ) { fValenceScore += fValenceBoost[iValence]; } else { fValenceScore += fValenceBoost[c_iMaxValenceBoost-1]; } #endif } } // Add it to the cache. float fScoreTemp = CacheAddTri ( pstCur->wIndex[0], pstCur->wIndex[1], pstCur->wIndex[2] ); VERIFY ( !pstCur->bUsed ); pstCur->bUsed = TRUE; #ifdef _DEBUG fScoreTemp += fValenceScore; VERIFY ( fabs ( fScoreTemp + fThisTriNegScore ) < 0.0001f ); #endif if ( iLookahead > 1 ) { // Swap pwCur to the start of the list. #if 0 WORD wTemp0 = pwCur[0]; WORD wTemp1 = pwCur[1]; WORD wTemp2 = pwCur[2]; pwCur[0] = pwIndices[0]; pwCur[1] = pwIndices[1]; pwCur[2] = pwIndices[2]; //pwIndices[0] = wTemp0; //pwIndices[1] = wTemp1; //pwIndices[2] = wTemp2; #else ScoreTri *pstTemp = *ppstCur; *ppstCur = *ppstCurTris; //*ppstCurTris = pstTemp; #endif //VERIFY ( fScore < 1e9 ); // And look ahead a bit more. //fScore = FindBestScoreLookahead ( iLookahead - 1, fScore, fBestSoFar, pwIndices + 3, iNumTris - 1, pwResult + 3 ); float fNewScore = FindBestScoreLookahead ( iLookahead - 1, fScore, fBestSoFar, iNumTris - 1, pwResult + 3 ); //VERIFY ( fNewScore < 1e9 ); fScore = fNewScore; #if 0 // And swap it back. //wTemp0 = pwIndices[0]; //wTemp1 = pwIndices[1]; //wTemp2 = pwIndices[2]; pwIndices[0] = pwCur[0]; pwIndices[1] = pwCur[1]; pwIndices[2] = pwCur[2]; pwCur[0] = wTemp0; pwCur[1] = wTemp1; pwCur[2] = wTemp2; #else //pstTemp = *ppstCurTris; *ppstCurTris = *ppstCur; *ppstCur = pstTemp; #endif } //VERIFY ( fScore < 1e9 ); if ( fScore < fBestSoFar ) { fBestSoFar = fScore; //pwBest = pwCur; ppstBest = ppstCur; } CacheRemoveTri(); VERIFY ( pstCur->bUsed ); pstCur->bUsed = FALSE; #if IGNORE_VALENCE_FOR_LOOKAHEADS if ( !bNoMadJumps ) #endif { // Restore the valencies. for ( j = 0; j < 3; j++ ) { //++(*(iValenceCounts.item(pwCur[j]))); ++(svVertex.item(pstCur->wIndex[j])->iCurrentValence); } } //pwCur = NewHeap.FindFirst(); ppstCur = NewHeap.FindFirst(); } VERIFY ( ppstBest != NULL ); { // Found a better solution. // Swap the best tri to the start of the list. float fNewBestScore = fBestSoFar; // Dump the tri to the result buffer. ScoreTri *pstBest = *ppstBest; //pwResult[0] = pwBest[0]; //pwResult[1] = pwBest[1]; //pwResult[2] = pwBest[2]; pwResult[0] = pstBest->wIndex[0]; pwResult[1] = pstBest->wIndex[1]; pwResult[2] = pstBest->wIndex[2]; if ( iCountdown > 1 ) { #if 0 WORD wTemp[3]; wTemp[0] = pwBest[0]; wTemp[1] = pwBest[1]; wTemp[2] = pwBest[2]; pwBest[0] = pwIndices[0]; pwBest[1] = pwIndices[1]; pwBest[2] = pwIndices[2]; #else ScoreTri *pTemp; pTemp = *ppstBest; *ppstBest = *ppstCurTris; #endif //float fScore = CacheAddTri ( wTemp[0], wTemp[1], wTemp[2] ); float fScore = CacheAddTri ( pstBest->wIndex[0], pstBest->wIndex[1], pstBest->wIndex[2] ); VERIFY ( !pstBest->bUsed ); pstBest->bUsed = TRUE; // And the valence. float fValenceScore = 0.0f; #if IGNORE_VALENCE_FOR_LOOKAHEADS //if ( !bNoMadJumps ) #endif { for ( j = 0; j < 3; j++ ) { //int iValence = --(*(iValenceCounts.item(wTemp[j]))); int iValence = --(svVertex.item(pstBest->wIndex[j])->iCurrentValence); VERIFY ( iValence >= 0 ); if ( iValence < c_iMaxValenceBoost ) { fValenceScore += fValenceBoost[iValence]; } else { fValenceScore += fValenceBoost[c_iMaxValenceBoost-1]; } } } fScore += fValenceScore + fCurrentScore; #ifdef DEBUG //. TRACE ( "Countdown %i\n", iCountdown ); #endif #if ALLOW_PROGRESS_BARS if ( g_hProgress2 != NULL ) { // Step one. SendMessage ( g_hProgress2, PBM_STEPIT, 0, 0 ); } #endif //VERIFY ( fScore < 1e9 ); //fNewBestScore = FindBestScore ( iCountdown - 1, fScore, 1e10, pwIndices + 3, iNumTris - 1, pwResult + 3 ); fNewBestScore = FindBestScore ( iCountdown - 1, fScore, 1e10, ppstCurTris + 1, iNumTris - 1, pwResult + 3 ); //VERIFY ( fNewBestScore < 1e9 ); CacheRemoveTri(); VERIFY ( pstBest->bUsed ); pstBest->bUsed = FALSE; // Restore the valencies. #if IGNORE_VALENCE_FOR_LOOKAHEADS //if ( !bNoMadJumps ) #endif { for ( j = 0; j < 3; j++ ) { //++(*(iValenceCounts.item(wTemp[j]))); ++(svVertex.item(pstBest->wIndex[j])->iCurrentValence); } } // And swap it back. #if 0 pwIndices[0] = pwBest[0]; pwIndices[1] = pwBest[1]; pwIndices[2] = pwBest[2]; pwBest[0] = wTemp[0]; pwBest[1] = wTemp[1]; pwBest[2] = wTemp[2]; #else *ppstCurTris = *ppstBest; *ppstBest = pTemp; #endif } return fNewBestScore; } }