Engine directory for ticket #1

This commit is contained in:
DavidWyand-GG 2012-09-19 11:15:01 -04:00
parent 352279af7a
commit 7dbfe6994d
3795 changed files with 1363358 additions and 0 deletions

View file

@ -0,0 +1,206 @@
//-----------------------------------------------------------------------------
// Copyright (c) 2012 GarageGames, LLC
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to
// deal in the Software without restriction, including without limitation the
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
// sell copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
// IN THE SOFTWARE.
//-----------------------------------------------------------------------------
#include "math/mPoint2.h"
#include "gfx/bitmap/gBitmap.h"
#include "gfx/util/distanceField.h"
//-----------------------------------------------------------------------------
struct DistanceFieldSearchSpaceStruct
{
S32 xOffset;
S32 yOffset;
F32 distance;
};
int QSORT_CALLBACK cmpSortDistanceFieldSearchSpaceStruct(const void* p1, const void* p2)
{
const DistanceFieldSearchSpaceStruct* sp1 = (const DistanceFieldSearchSpaceStruct*)p1;
const DistanceFieldSearchSpaceStruct* sp2 = (const DistanceFieldSearchSpaceStruct*)p2;
if (sp2->distance > sp1->distance)
return -1;
else if (sp2->distance == sp1->distance)
return 0;
else
return 1;
}
//GBitmap * GFXUtil::DistanceField::makeDistanceField(GBitmap * sourceBmp, S32 targetSizeX, S32 targetSizeY, F32 rangePct)
//{
// AssertFatal(sourceBmp->getFormat() == GFXFormatA8,"Use an alpha-only texture to create distance fields");
//
// static Vector<DistanceFieldSearchSpaceStruct> searchSpace;
//
// S32 sourceSizeX = sourceBmp->getWidth();
// S32 sourceSizeY = sourceBmp->getHeight();
//
// S32 targetToSourceScalarX = sourceSizeX / targetSizeX;
// S32 targetToSourceScalarY = sourceSizeY / targetSizeY;
// S32 targetToSourcePixOffsetX = targetToSourceScalarX / 2;
// S32 targetToSourcePixOffsetY = targetToSourceScalarY / 2;
//
// F32 range = getMin(sourceSizeX,sourceSizeY) * rangePct;
// F32 range2 = range * 2.f;
//
// {
// S32 intRange = mCeil(range);
// for(S32 spaceY = -intRange; spaceY < intRange; spaceY++)
// {
// for(S32 spaceX = -intRange; spaceX < intRange; spaceX++)
// {
// if(spaceX == 0 && spaceY == 0)
// continue;
//
// F32 distance = Point2F(spaceX,spaceY).len();
// if(distance <= range)
// {
// searchSpace.increment();
// searchSpace.last().distance = distance;
// searchSpace.last().xOffset = spaceX;
// searchSpace.last().yOffset = spaceY;
// }
// }
// }
// }
// dQsort(searchSpace.address(), searchSpace.size(), sizeof(DistanceFieldSearchSpaceStruct), cmpSortDistanceFieldSearchSpaceStruct);
//
// GBitmap * targetBmp = new GBitmap(targetSizeX,targetSizeY,false,GFXFormatA8);
//
// U8 * targetPixel = targetBmp->getWritableBits();
// for(S32 y = 0; y < targetSizeY; y++)
// {
// for(S32 x = 0; x < targetSizeX; x++)
// {
// S32 sourceX = x * targetToSourceScalarX + targetToSourcePixOffsetX;
// S32 sourceY = y * targetToSourceScalarY + targetToSourcePixOffsetY;
//
// const U8 * thisPixel = sourceBmp->getAddress(sourceX,sourceY);
//
// bool thisPixelEmpty = *thisPixel == 0;
//
// F32 closestDist = F32_MAX;
//
// for(DistanceFieldSearchSpaceStruct * seachSpaceStructPtr = searchSpace.begin(); seachSpaceStructPtr <= searchSpace.end(); seachSpaceStructPtr++)
// {
// DistanceFieldSearchSpaceStruct & searchSpaceStruct = *seachSpaceStructPtr;
// S32 cx = sourceX + searchSpaceStruct.xOffset;
// if(cx < 0 || cx >= sourceSizeX)
// continue;
//
// S32 cy = sourceY + searchSpaceStruct.yOffset;
// if(cy < 0 || cy >= sourceSizeY)
// continue;
//
// const U8 * checkPixel = sourceBmp->getAddress(cx,cy);
// if((*checkPixel == 0) != thisPixelEmpty)
// {
// closestDist = searchSpaceStruct.distance;
// break;
// }
// }
//
// F32 diff = thisPixelEmpty ? getMax(-0.5f,-(closestDist / range2)) : getMin(0.5f,closestDist / range2);
// F32 targetValue = 0.5f + diff;
//
// *targetPixel = targetValue * 255;
// targetPixel++;
// }
// }
//
// searchSpace.clear();
//
// return targetBmp;
//}
void GFXUtil::DistanceField::makeDistanceField( const U8 * sourceData, S32 sourceSizeX, S32 sourceSizeY, U8 * targetData, S32 targetSizeX, S32 targetSizeY, F32 radius )
{
static Vector<DistanceFieldSearchSpaceStruct> searchSpace;
S32 targetToSourceScalarX = sourceSizeX / targetSizeX;
S32 targetToSourceScalarY = sourceSizeY / targetSizeY;
S32 targetToSourcePixOffsetX = targetToSourceScalarX / 2;
S32 targetToSourcePixOffsetY = targetToSourceScalarY / 2;
F32 radius2 = radius * 2.f;
{
S32 intRange = mCeil(radius);
for(S32 spaceY = -intRange; spaceY < intRange; spaceY++)
{
for(S32 spaceX = -intRange; spaceX < intRange; spaceX++)
{
if(spaceX == 0 && spaceY == 0)
continue;
F32 distance = Point2F(spaceX,spaceY).len();
if(distance <= radius)
{
searchSpace.increment();
searchSpace.last().distance = distance;
searchSpace.last().xOffset = spaceX;
searchSpace.last().yOffset = spaceY;
}
}
}
}
dQsort(searchSpace.address(), searchSpace.size(), sizeof(DistanceFieldSearchSpaceStruct), cmpSortDistanceFieldSearchSpaceStruct);
for(S32 y = 0; y < targetSizeY; y++)
{
for(S32 x = 0; x < targetSizeX; x++)
{
S32 sourceX = x * targetToSourceScalarX + targetToSourcePixOffsetX;
S32 sourceY = y * targetToSourceScalarY + targetToSourcePixOffsetY;
bool thisPixelEmpty = sourceData[sourceY * sourceSizeX + sourceX] < 127;
F32 closestDist = F32_MAX;
for(DistanceFieldSearchSpaceStruct * seachSpaceStructPtr = searchSpace.begin(); seachSpaceStructPtr <= searchSpace.end(); seachSpaceStructPtr++)
{
DistanceFieldSearchSpaceStruct & searchSpaceStruct = *seachSpaceStructPtr;
S32 cx = sourceX + searchSpaceStruct.xOffset;
if(cx < 0 || cx >= sourceSizeX)
continue;
S32 cy = sourceY + searchSpaceStruct.yOffset;
if(cy < 0 || cy >= sourceSizeY)
continue;
if((sourceData[cy * sourceSizeX + cx] < 127) != thisPixelEmpty)
{
closestDist = searchSpaceStruct.distance;
break;
}
}
F32 diff = thisPixelEmpty ? getMax(-0.5f,-(closestDist / radius2)) : getMin(0.5f,closestDist / radius2);
F32 targetValue = 0.5f + diff;
*targetData = targetValue * 255;
targetData++;
}
}
searchSpace.clear();
}

View file

@ -0,0 +1,34 @@
//-----------------------------------------------------------------------------
// Copyright (c) 2012 GarageGames, LLC
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to
// deal in the Software without restriction, including without limitation the
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
// sell copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
// IN THE SOFTWARE.
//-----------------------------------------------------------------------------
#ifndef _DISTANCE_FIELD_H_
#define _DISTANCE_FIELD_H_
namespace GFXUtil
{
namespace DistanceField
{
//GBitmap * makeDistanceField(const GBitmap * sourceBmp, S32 targetSizeX, S32 targetSizeY, F32 rangePct);
void makeDistanceField(const U8 * sourceData, S32 sourceSizeX, S32 sourceSizeY, U8 * targetData, S32 targetSizeX, S32 targetSizeY, F32 radius);
}
}
#endif // _DISTANCE_FIELD_H_

View file

@ -0,0 +1,34 @@
//-----------------------------------------------------------------------------
// Copyright (c) 2012 GarageGames, LLC
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to
// deal in the Software without restriction, including without limitation the
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
// sell copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
// IN THE SOFTWARE.
//-----------------------------------------------------------------------------
#include "gfx/util/gfxFrustumSaver.h"
#include "gfx/gfxDevice.h"
GFXFrustumSaver::GFXFrustumSaver()
{
mFrustum = GFX->getFrustum();
}
GFXFrustumSaver::~GFXFrustumSaver()
{
GFX->setFrustum( mFrustum );
}

View file

@ -0,0 +1,45 @@
//-----------------------------------------------------------------------------
// Copyright (c) 2012 GarageGames, LLC
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to
// deal in the Software without restriction, including without limitation the
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
// sell copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
// IN THE SOFTWARE.
//-----------------------------------------------------------------------------
#ifndef TORQUE_GFX_UTIL_GFXFRUSTUMSAVER_H_
#define TORQUE_GFX_UTIL_GFXFRUSTUMSAVER_H_
#include "platform/types.h"
#ifndef _MATHUTIL_FRUSTUM_H_
#include "math/util/frustum.h"
#endif
class GFXFrustumSaver
{
public:
/// Saves the current frustum state.
GFXFrustumSaver();
/// Restores the saved frustum state.
~GFXFrustumSaver();
private:
Frustum mFrustum;
};
#endif

View file

@ -0,0 +1,52 @@
//-----------------------------------------------------------------------------
// Copyright (c) 2012 GarageGames, LLC
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to
// deal in the Software without restriction, including without limitation the
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
// sell copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
// IN THE SOFTWARE.
//-----------------------------------------------------------------------------
#include "gfx/util/screenspace.h"
// The conversion from screen space to the render target
// is made more complex because screen space is relative
// to the viewport.
void ScreenSpace::RenderTargetParameters(const Point3I &targetSize, const RectI &targetViewport, Point4F &rtParams)
{
// Top->Down
Point2F targetOffset( (F32)targetViewport.point.x / (F32)targetSize.x,
(F32)targetViewport.point.y / (F32)targetSize.y );
// Bottom->Up
//Point2F targetOffset( (F32)targetViewport.point.x / (F32)targetSize.x,
// ( (F32)targetSize.y - (F32)(targetViewport.point.y + targetViewport.extent.y ) ) / (F32)targetSize.y );
// Get the scale to convert from the
// screen space to the target size.
Point2F targetScale( (F32)targetViewport.extent.x / (F32)targetSize.x,
(F32)targetViewport.extent.y / (F32)targetSize.y );
// Get the target half pixel size.
const Point2F halfPixel( 0.5f / targetSize.x,
0.5f / targetSize.y );
rtParams.set( targetOffset.x + halfPixel.x,
targetOffset.y + halfPixel.y,
targetScale.x,
targetScale.y );
}

View file

@ -0,0 +1,35 @@
//-----------------------------------------------------------------------------
// Copyright (c) 2012 GarageGames, LLC
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to
// deal in the Software without restriction, including without limitation the
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
// sell copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
// IN THE SOFTWARE.
//-----------------------------------------------------------------------------
#ifndef _SCREENSPACE_UTILS_H_
#define _SCREENSPACE_UTILS_H_
#include "math/mPoint3.h"
#include "math/mPoint4.h"
#include "math/mRect.h"
namespace ScreenSpace
{
void RenderTargetParameters(const Point3I &targetSize, const RectI &targetViewport, Point4F &rtParams);
};
#endif

View file

@ -0,0 +1,398 @@
//-----------------------------------------------------------------------------
// Copyright (c) 2012 GarageGames, LLC
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to
// deal in the Software without restriction, including without limitation the
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
// sell copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
// IN THE SOFTWARE.
//-----------------------------------------------------------------------------
#include "gfx/util/triListOpt.h"
#include "core/frameAllocator.h"
#include "platform/profiler.h"
#include "math/mMathFn.h"
namespace TriListOpt
{
void OptimizeTriangleOrdering(const dsize_t numVerts, const dsize_t numIndices, const U32 *indices, IndexType *outIndices)
{
PROFILE_SCOPE(TriListOpt_OptimizeTriangleOrdering);
if(numVerts == 0 || numIndices == 0)
{
dCopyArray(outIndices, indices, numIndices);
return;
}
const U32 NumPrimitives = numIndices / 3;
AssertFatal(NumPrimitives == U32(mFloor(numIndices / 3.0f)), "Number of indicies not divisible by 3, not a good triangle list.");
//
// Step 1: Run through the data, and initialize
//
FrameTemp<VertData> vertexData(numVerts);
FrameTemp<TriData> triangleData(NumPrimitives);
U32 curIdx = 0;
for(int tri = 0; tri < NumPrimitives; tri++)
{
TriData &curTri = triangleData[tri];
for(int c = 0; c < 3; c++)
{
const U32 &curVIdx = indices[curIdx];
AssertFatal(curVIdx < numVerts, "Out of range index.");
// Add this vert to the list of verts that define the triangle
curTri.vertIdx[c] = curVIdx;
VertData &curVert = vertexData[curVIdx];
// Increment the number of triangles that reference this vertex
curVert.numUnaddedReferences++;
curIdx++;
}
}
// Allocate per-vertex triangle lists, and calculate the starting score of
// each of the verts
for(int v = 0; v < numVerts; v++)
{
VertData &curVert = vertexData[v];
curVert.triIndex = new S32[curVert.numUnaddedReferences];
curVert.score = FindVertexScore::score(curVert);
}
// These variables will be used later, but need to be declared now
S32 nextNextBestTriIdx = -1, nextBestTriIdx = -1;
F32 nextNextBestTriScore = -1.0f, nextBestTriScore = -1.0f;
#define _VALIDATE_TRI_IDX(idx) if(idx > -1) { AssertFatal(idx < NumPrimitives, "Out of range triangle index."); AssertFatal(!triangleData[idx].isInList, "Triangle already in list, bad."); }
#define _CHECK_NEXT_NEXT_BEST(score, idx) { if(score > nextNextBestTriScore) { nextNextBestTriIdx = idx; nextNextBestTriScore = score; } }
#define _CHECK_NEXT_BEST(score, idx) { if(score > nextBestTriScore) { _CHECK_NEXT_NEXT_BEST(nextBestTriScore, nextBestTriIdx); nextBestTriIdx = idx; nextBestTriScore = score; } _VALIDATE_TRI_IDX(nextBestTriIdx); }
// Fill-in per-vertex triangle lists, and sum the scores of each vertex used
// per-triangle, to get the starting triangle score
curIdx = 0;
for(int tri = 0; tri < NumPrimitives; tri++)
{
TriData &curTri = triangleData[tri];
for(int c = 0; c < 3; c++)
{
const U32 &curVIdx = indices[curIdx];
AssertFatal(curVIdx < numVerts, "Out of range index.");
VertData &curVert = vertexData[curVIdx];
// Add triangle to triangle list
curVert.triIndex[curVert.numReferences++] = tri;
// Add vertex score to triangle score
curTri.score += curVert.score;
curIdx++;
}
// This will pick the first triangle to add to the list in 'Step 2'
_CHECK_NEXT_BEST(curTri.score, tri);
_CHECK_NEXT_NEXT_BEST(curTri.score, tri);
}
//
// Step 2: Start emitting triangles...this is the emit loop
//
LRUCacheModel lruCache;
for(int outIdx = 0; outIdx < numIndices; /* this space intentionally left blank */ )
{
// If there is no next best triangle, than search for the next highest
// scored triangle that isn't in the list already
if(nextBestTriIdx < 0)
{
// TODO: Something better than linear performance here...
nextBestTriScore = nextNextBestTriScore = -1.0f;
nextBestTriIdx = nextNextBestTriIdx = -1;
for(int tri = 0; tri < NumPrimitives; tri++)
{
TriData &curTri = triangleData[tri];
if(!curTri.isInList)
{
_CHECK_NEXT_BEST(curTri.score, tri);
_CHECK_NEXT_NEXT_BEST(curTri.score, tri);
}
}
}
AssertFatal(nextBestTriIdx > -1, "Ran out of 'nextBestTriangle' before I ran out of indices...not good.");
// Emit the next best triangle
TriData &nextBestTri = triangleData[nextBestTriIdx];
AssertFatal(!nextBestTri.isInList, "Next best triangle already in list, this is no good.");
for(int i = 0; i < 3; i++)
{
// Emit index
outIndices[outIdx++] = IndexType(nextBestTri.vertIdx[i]);
// Update the list of triangles on the vert
VertData &curVert = vertexData[nextBestTri.vertIdx[i]];
curVert.numUnaddedReferences--;
for(int t = 0; t < curVert.numReferences; t++)
{
if(curVert.triIndex[t] == nextBestTriIdx)
{
curVert.triIndex[t] = -1;
break;
}
}
// Update cache
lruCache.useVertex(nextBestTri.vertIdx[i], &curVert);
}
nextBestTri.isInList = true;
// Enforce cache size, this will update the cache position of all verts
// still in the cache. It will also update the score of the verts in the
// cache, and give back a list of triangle indicies that need updating.
Vector<U32> trisToUpdate;
lruCache.enforceSize(MaxSizeVertexCache, trisToUpdate);
// Now update scores for triangles that need updates, and find the new best
// triangle score/index
nextBestTriIdx = -1;
nextBestTriScore = -1.0f;
for(Vector<U32>::iterator itr = trisToUpdate.begin(); itr != trisToUpdate.end(); itr++)
{
TriData &tri = triangleData[*itr];
// If this triangle isn't already emitted, re-score it
if(!tri.isInList)
{
tri.score = 0.0f;
for(int i = 0; i < 3; i++)
tri.score += vertexData[tri.vertIdx[i]].score;
_CHECK_NEXT_BEST(tri.score, *itr);
_CHECK_NEXT_NEXT_BEST(tri.score, *itr);
}
}
// If there was no love finding a good triangle, than see if there is a
// next-next-best triangle, and if there isn't one of those...well than
// I guess we have to find one next time
if(nextBestTriIdx < 0 && nextNextBestTriIdx > -1)
{
if(!triangleData[nextNextBestTriIdx].isInList)
{
nextBestTriIdx = nextNextBestTriIdx;
nextBestTriScore = nextNextBestTriScore;
_VALIDATE_TRI_IDX(nextNextBestTriIdx);
}
// Nuke the next-next best
nextNextBestTriIdx = -1;
nextNextBestTriScore = -1.0f;
}
// Validate triangle we are marking as next-best
_VALIDATE_TRI_IDX(nextBestTriIdx);
}
#undef _CHECK_NEXT_BEST
#undef _CHECK_NEXT_NEXT_BEST
#undef _VALIDATE_TRI_IDX
// FrameTemp will call destructInPlace to clean up vertex lists
}
//------------------------------------------------------------------------------
//------------------------------------------------------------------------------
LRUCacheModel::~LRUCacheModel()
{
for( LRUCacheEntry* entry = mCacheHead; entry != NULL; )
{
LRUCacheEntry* next = entry->next;
delete entry;
entry = next;
}
}
//------------------------------------------------------------------------------
void LRUCacheModel::useVertex(const U32 vIdx, VertData *vData)
{
LRUCacheEntry *search = mCacheHead;
LRUCacheEntry *last = NULL;
while(search != NULL)
{
if(search->vIdx == vIdx)
break;
last = search;
search = search->next;
}
// If this vertex wasn't found in the cache, create a new entry
if(search == NULL)
{
search = new LRUCacheEntry;
search->vIdx = vIdx;
search->vData = vData;
}
if(search != mCacheHead)
{
// Unlink the entry from the linked list
if(last)
last->next = search->next;
// Vertex that got passed in is now at the head of the cache
search->next = mCacheHead;
mCacheHead = search;
}
}
//------------------------------------------------------------------------------
void LRUCacheModel::enforceSize(const dsize_t maxSize, Vector<U32> &outTrisToUpdate)
{
// Clear list of triangles to update scores for
outTrisToUpdate.clear();
dsize_t length = 0;
LRUCacheEntry *next = mCacheHead;
LRUCacheEntry *last = NULL;
// Run through list, up to the max size
while(next != NULL && length < MaxSizeVertexCache)
{
VertData &vData = *next->vData;
// Update cache position on verts still in cache
vData.cachePosition = length++;
for(int i = 0; i < vData.numReferences; i++)
{
const S32 &triIdx = vData.triIndex[i];
if(triIdx > -1)
{
int j = 0;
for(; j < outTrisToUpdate.size(); j++)
if(outTrisToUpdate[j] == triIdx)
break;
if(j == outTrisToUpdate.size())
outTrisToUpdate.push_back(triIdx);
}
}
// Update score
vData.score = FindVertexScore::score(vData);
last = next;
next = next->next;
}
// NULL out the pointer to the next entry on the last valid entry
last->next = NULL;
// If next != NULL, than we need to prune entries from the tail of the cache
while(next != NULL)
{
// Update cache position on verts which are going to get tossed from cache
next->vData->cachePosition = -1;
LRUCacheEntry *curEntry = next;
next = next->next;
delete curEntry;
}
}
//------------------------------------------------------------------------------
S32 LRUCacheModel::getCachePosition(const U32 vIdx)
{
dsize_t length = 0;
LRUCacheEntry *next = mCacheHead;
while(next != NULL)
{
if(next->vIdx == vIdx)
return length;
length++;
next = next->next;
}
return -1;
}
//------------------------------------------------------------------------------
//------------------------------------------------------------------------------
// http://home.comcast.net/~tom_forsyth/papers/fast_vert_cache_opt.html
namespace FindVertexScore
{
F32 score(const VertData &vertexData)
{
// If nobody needs this vertex, return -1.0
if(vertexData.numUnaddedReferences < 1)
return -1.0f;
F32 Score = 0.0f;
if(vertexData.cachePosition < 0)
{
// Vertex is not in FIFO cache - no score.
}
else
{
if(vertexData.cachePosition < 3)
{
// This vertex was used in the last triangle,
// so it has a fixed score, whichever of the three
// it's in. Otherwise, you can get very different
// answers depending on whether you add
// the triangle 1,2,3 or 3,1,2 - which is silly.
Score = FindVertexScore::LastTriScore;
}
else
{
AssertFatal(vertexData.cachePosition < MaxSizeVertexCache, "Out of range cache position for vertex");
// Points for being high in the cache.
const float Scaler = 1.0f / (MaxSizeVertexCache - 3);
Score = 1.0f - (vertexData.cachePosition - 3) * Scaler;
Score = mPow(Score, FindVertexScore::CacheDecayPower);
}
}
// Bonus points for having a low number of tris still to
// use the vert, so we get rid of lone verts quickly.
float ValenceBoost = mPow(vertexData.numUnaddedReferences, -FindVertexScore::ValenceBoostPower);
Score += FindVertexScore::ValenceBoostScale * ValenceBoost;
return Score;
}
} // namspace FindVertexScore
} // namespace TriListOpt

View file

@ -0,0 +1,100 @@
//-----------------------------------------------------------------------------
// Copyright (c) 2012 GarageGames, LLC
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to
// deal in the Software without restriction, including without limitation the
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
// sell copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
// IN THE SOFTWARE.
//-----------------------------------------------------------------------------
#ifndef _TRI_LIST_OPT_H_
#define _TRI_LIST_OPT_H_
#include "core/util/tVector.h"
namespace TriListOpt
{
typedef U32 IndexType;
const U32 MaxSizeVertexCache = 32;
struct VertData
{
S32 cachePosition;
F32 score;
U32 numReferences;
U32 numUnaddedReferences;
S32 *triIndex;
VertData() : cachePosition(-1), score(0.0f), numReferences(0), numUnaddedReferences(0), triIndex(NULL) {}
~VertData() { if(triIndex != NULL) delete [] triIndex; triIndex = NULL; }
};
struct TriData
{
bool isInList;
F32 score;
U32 vertIdx[3];
TriData() : isInList(false), score(0.0f) { dMemset(vertIdx, 0, sizeof(vertIdx)); }
};
class LRUCacheModel
{
struct LRUCacheEntry
{
LRUCacheEntry *next;
U32 vIdx;
VertData *vData;
LRUCacheEntry() : next(NULL), vIdx(0), vData(NULL) {}
};
LRUCacheEntry *mCacheHead;
public:
LRUCacheModel() : mCacheHead(NULL) {}
~LRUCacheModel();
void enforceSize(const dsize_t maxSize, Vector<U32> &outTrisToUpdate);
void useVertex(const U32 vIdx, VertData *vData);
S32 getCachePosition(const U32 vIdx);
};
/// This method will look at the index buffer for a triangle list, and generate
/// a new index buffer which is optimized using Tom Forsyth's paper:
/// "Linear-Speed Vertex Cache Optimization"
/// http://home.comcast.net/~tom_forsyth/papers/fast_vert_cache_opt.html
/// @param numVerts Number of vertices indexed by the 'indices'
/// @param numIndices Number of elements in both 'indices' and 'outIndices'
/// @param indices Input index buffer
/// @param outIndices Output index buffer
///
/// @note Both 'indices' and 'outIndices' can point to the same memory.
void OptimizeTriangleOrdering(const dsize_t numVerts, const dsize_t numIndices, const U32 *indices, IndexType *outIndices);
namespace FindVertexScore
{
const F32 CacheDecayPower = 1.5f;
const F32 LastTriScore = 0.75f;
const F32 ValenceBoostScale = 2.0f;
const F32 ValenceBoostPower = 0.5f;
F32 score(const VertData &vertexData);
};
};
#endif