mirror of
https://github.com/TorqueGameEngines/Torque3D.git
synced 2026-04-29 00:05:40 +00:00
Engine directory for ticket #1
This commit is contained in:
parent
352279af7a
commit
7dbfe6994d
3795 changed files with 1363358 additions and 0 deletions
206
Engine/source/gfx/util/distanceField.cpp
Normal file
206
Engine/source/gfx/util/distanceField.cpp
Normal 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();
|
||||
}
|
||||
34
Engine/source/gfx/util/distanceField.h
Normal file
34
Engine/source/gfx/util/distanceField.h
Normal 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_
|
||||
34
Engine/source/gfx/util/gfxFrustumSaver.cpp
Normal file
34
Engine/source/gfx/util/gfxFrustumSaver.cpp
Normal 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 );
|
||||
}
|
||||
45
Engine/source/gfx/util/gfxFrustumSaver.h
Normal file
45
Engine/source/gfx/util/gfxFrustumSaver.h
Normal 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
|
||||
52
Engine/source/gfx/util/screenspace.cpp
Normal file
52
Engine/source/gfx/util/screenspace.cpp
Normal 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 );
|
||||
}
|
||||
35
Engine/source/gfx/util/screenspace.h
Normal file
35
Engine/source/gfx/util/screenspace.h
Normal 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
|
||||
398
Engine/source/gfx/util/triListOpt.cpp
Normal file
398
Engine/source/gfx/util/triListOpt.cpp
Normal 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
|
||||
100
Engine/source/gfx/util/triListOpt.h
Normal file
100
Engine/source/gfx/util/triListOpt.h
Normal 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
|
||||
Loading…
Add table
Add a link
Reference in a new issue