Updated CDTestFramework with the OPCODE Array SAP test.

Thanks Pierre Terdiman for the latest update.
This commit is contained in:
erwin.coumans
2008-09-01 18:46:57 +00:00
parent f655eff89f
commit 932de57d4c
41 changed files with 6385 additions and 410 deletions

View File

@@ -342,7 +342,8 @@ BulletSAPCompleteBoxPruningTest::BulletSAPCompleteBoxPruningTest(int numBoxes,in
mBoxPtrs (null),
mBoxTime (null),
mSpeed (0.005f),
mAmplitude (100.0f)
mAmplitude (100.0f),
m_method(method)
{
btVector3 aabbMin(-200,-200,-200);
btVector3 aabbMax(200,200,200);
@@ -412,7 +413,7 @@ BulletSAPCompleteBoxPruningTest::BulletSAPCompleteBoxPruningTest(int numBoxes,in
case 7:
m_broadphase = new btDbvtBroadphase();
m_isdbvt = true;
methodname = "btDbvtBroadphase";
methodname = "dynamic AABB tree, btDbvtBroadphase";
break;
default:
{
@@ -660,7 +661,7 @@ void BulletSAPCompleteBoxPruningTest::PerformTest()
}*/
char Buffer[4096];
sprintf(Buffer, "CompleteBoxPruning: %5.1f us (%d cycles) : %d pairs\n", mProfiler.mMsTime, mProfiler.mCycles,
sprintf(Buffer, "Bullet %s: %5.1f us (%d cycles) : %d pairs\n", methodname, mProfiler.mMsTime, mProfiler.mCycles,
m_broadphase->getOverlappingPairCache()->getNumOverlappingPairs());
// m_broadphase)->printStats();

View File

@@ -57,6 +57,7 @@ subject to the following restrictions:
float mSpeed;
float mAmplitude;
bool m_firstTime;
int m_method;
private:
bool UpdateBoxes(int numBoxes);

View File

@@ -22,13 +22,14 @@ subject to the following restrictions:
#include "CompleteBoxPruning.h"
#include "BulletSAPCompleteBoxPruningTest.h"
#include "BipartiteBoxPruning.h"
#include "OpcodeArraySAPTest.h"
#include "RenderingHelpers.h"
#include "Terrain.h"
#include "Camera.h"
#include "GLFontRenderer.h"
#include "BulletCollision/BroadphaseCollision/btDbvt.h"
#define NUM_SAP_BOXES 8192
//#define NUM_SAP_BOXES 1024
int percentUpdate = 10;
@@ -62,11 +63,12 @@ enum TestIndex
// TEST_COMPLETE_BOX_PRUNING=0,
TEST_COMPLETE_BOX_PRUNING_8192,
// TEST_BULLET_SAP_1024,
// TEST_BULLET_SAP_8192,
TEST_BULLET_SAP_8192,
// TEST_BULLET_SAP_SORTEDPAIRS_8192,
TEST_BULLET_MULTISAP_8192,
// TEST_BIPARTITE_BOX_PRUNING,
TEST_DBVT_8192,
TEST_OPCODE_ARRAY_SAP,
MAX_NB_TESTS
};
@@ -225,15 +227,6 @@ static void Terminate()
int main(int argc, char** argv)
{
{
::SetPriorityClass(::GetCurrentProcess(),HIGH_PRIORITY_CLASS);
::SetThreadPriority(::GetCurrentThread(),THREAD_PRIORITY_TIME_CRITICAL);
#if 0
btDbvt::benchmark();
exit(0);
#endif
}
// Initialize AntTweakBar
// (note that AntTweakBar could also be intialize after GLUT, no matter)
if(!TwInit(TW_OPENGL, NULL))
@@ -296,13 +289,14 @@ int main(int argc, char** argv)
// {TEST_OBB_MESH_QUERY, "OBB-mesh query"},
// {TEST_CAPSULE_MESH_QUERY, "Capsule-mesh query"},
// {TEST_COMPLETE_BOX_PRUNING, "OPCODE SAP 1024"},
{TEST_COMPLETE_BOX_PRUNING_8192, "OPCODE SAP 8192"},
{TEST_COMPLETE_BOX_PRUNING_8192, "OPCODE BOX PRUNING 8192"},
// {TEST_BULLET_SAP_1024, "Bullet SAP HASHPAIR 1024"},
// {TEST_BULLET_SAP_8192, "Bullet SAP HASHPAIR 8192"},
{TEST_BULLET_SAP_8192, "Bullet SAP HASHPAIR 8192"},
// {TEST_BULLET_SAP_SORTEDPAIRS_8192, "Bullet SAP SORTEDPAIR 8192"},
{TEST_BULLET_MULTISAP_8192, "Bullet MultiSAP 8192"},
// {TEST_BIPARTITE_BOX_PRUNING, "Bipartite box pruning"},
{TEST_DBVT_8192, "DBVT 8192"},
{TEST_DBVT_8192, "Bullet DBVT 8192"},
{TEST_OPCODE_ARRAY_SAP, "OPCODE ARRAY SAP"},
};
TwType testType = TwDefineEnum("CollisionTest", testEV, MAX_NB_TESTS);
TwAddVarRW(gMainBar, "CollisionTests", testType, &gSelectedTest, "");
@@ -318,11 +312,12 @@ int main(int argc, char** argv)
// gCollisionTests[TEST_COMPLETE_BOX_PRUNING_8192] = new CompleteBoxPruningTest(NUM_SAP_BOXES);
gCollisionTests[TEST_COMPLETE_BOX_PRUNING_8192] = new CompleteBoxPruningTest(NUM_SAP_BOXES);
// gCollisionTests[TEST_BULLET_SAP_1024] = new BulletSAPCompleteBoxPruningTest(NUM_SAP_BOXES,1);
// gCollisionTests[TEST_BULLET_SAP_8192] = new BulletSAPCompleteBoxPruningTest(NUM_SAP_BOXES,1);
gCollisionTests[TEST_BULLET_SAP_8192] = new BulletSAPCompleteBoxPruningTest(NUM_SAP_BOXES,1);
// gCollisionTests[TEST_BULLET_SAP_SORTEDPAIRS_8192] = new BulletSAPCompleteBoxPruningTest(NUM_SAP_BOXES,3);
gCollisionTests[TEST_BULLET_MULTISAP_8192] = new BulletSAPCompleteBoxPruningTest(NUM_SAP_BOXES,6);
// gCollisionTests[TEST_BIPARTITE_BOX_PRUNING] = new BipartiteBoxPruningTest;
gCollisionTests[TEST_DBVT_8192] = new BulletSAPCompleteBoxPruningTest(NUM_SAP_BOXES,7);
gCollisionTests[TEST_OPCODE_ARRAY_SAP] = new OpcodeArraySAPTest(NUM_SAP_BOXES);
for(int i=0;i<MAX_NB_TESTS;i++)
gCollisionTests[i]->Init();

View File

@@ -1,7 +1,7 @@
<?xml version="1.0" encoding="Windows-1252"?>
<VisualStudioProject
ProjectType="Visual C++"
Version="8.00"
Version="8,00"
Name="CDTestFramework"
ProjectGUID="{0565DB39-45CC-416E-B549-BFC24F2666D1}"
Keyword="Win32Proj"
@@ -120,10 +120,15 @@
/>
<Tool
Name="VCCLCompilerTool"
InlineFunctionExpansion="2"
EnableIntrinsicFunctions="true"
AdditionalIncludeDirectories=".\Opcode;.\AntTweakBar\include;.\GIMPACT\Include;../../src;../../Glut"
PreprocessorDefinitions="WIN32;NDEBUG;_CONSOLE"
ExceptionHandling="0"
RuntimeLibrary="0"
EnableEnhancedInstructionSet="0"
FloatingPointModel="2"
RuntimeTypeInfo="false"
UsePrecompiledHeader="2"
WarningLevel="3"
Detect64BitPortabilityProblems="true"
@@ -195,6 +200,10 @@
RelativePath=".\BulletSAPCompleteBoxPruningTest.cpp"
>
</File>
<File
RelativePath=".\BulletSAPCompleteBoxPruningTest.h"
>
</File>
<File
RelativePath=".\Camera.cpp"
>
@@ -263,6 +272,14 @@
RelativePath=".\OBBMeshQuery.h"
>
</File>
<File
RelativePath=".\OpcodeArraySAPTest.cpp"
>
</File>
<File
RelativePath=".\OpcodeArraySAPTest.h"
>
</File>
<File
RelativePath=".\Profiling.h"
>
@@ -303,6 +320,10 @@
/>
</FileConfiguration>
</File>
<File
RelativePath=".\stdafx.h"
>
</File>
<File
RelativePath=".\Terrain.cpp"
>
@@ -317,14 +338,6 @@
Filter="h;hpp;hxx;hm;inl;inc;xsd"
UniqueIdentifier="{93995380-89BD-4b04-88EB-625FBE52EBFB}"
>
<File
RelativePath=".\BulletSAPCompleteBoxPruningTest.h"
>
</File>
<File
RelativePath=".\stdafx.h"
>
</File>
</Filter>
<Filter
Name="Resource Files"

View File

@@ -105,9 +105,11 @@ bool CompleteBoxPruningTest::UpdateBoxes(int numBoxes)
return true;
}
extern int percentUpdate;
void CompleteBoxPruningTest::PerformTest()
{
int numBoxes = (mNbBoxes*10)/100;
int numBoxes = (mNbBoxes*percentUpdate)/100;
if (m_firstTime)
{
numBoxes = mNbBoxes;

View File

@@ -1,20 +1,3 @@
/*
* ICE / OPCODE - Optimized Collision Detection
* http://www.codercorner.com/Opcode.htm
*
* Copyright (c) 2001-2008 Pierre Terdiman, pierre@codercorner.com
This software is provided 'as-is', without any express or implied warranty.
In no event will the authors be held liable for any damages arising from the use of this software.
Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it freely,
subject to the following restrictions:
1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required.
2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software.
3. This notice may not be removed or altered from any source distribution.
*/
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/**
* Contains AABB-related code. (axis-aligned bounding box)
@@ -26,8 +9,20 @@ subject to the following restrictions:
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Include Guard
#ifndef __ICEAABB_H__
#define __ICEAABB_H__
#ifndef ICEAABB_H
#define ICEAABB_H
inline_ void ComputeMinMax(const Point& p, Point& min, Point& max)
{
if(p.x > max.x) max.x = p.x;
if(p.x < min.x) min.x = p.x;
if(p.y > max.y) max.y = p.y;
if(p.y < min.y) min.y = p.y;
if(p.z > max.z) max.z = p.z;
if(p.z < min.z) min.z = p.z;
}
// Forward declarations
class Sphere;
@@ -35,6 +30,7 @@ subject to the following restrictions:
//! Declarations of type-independent methods (most of them implemented in the .cpp)
#define AABB_COMMON_METHODS \
AABB& Add(const AABB& aabb); \
AABB& Sub(const AABB& aabb); \
float MakeCube(AABB& cube) const; \
void MakeSphere(Sphere& sphere) const; \
const sbyte* ComputeOutline(const Point& local_eye, sdword& num) const; \
@@ -69,7 +65,7 @@ subject to the following restrictions:
Point mMax;
};
class ICEMATHS_API AABB
class ICEMATHS_API AABB : public Allocateable
{
public:
//! Constructor
@@ -126,17 +122,8 @@ subject to the following restrictions:
* \param p [in] the next point
*/
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
void Extend(const Point& p)
{
if(p.x > mMax.x) mMax.x = p.x;
if(p.x < mMin.x) mMin.x = p.x;
inline_ void Extend(const Point& p) { ComputeMinMax(p, mMin, mMax); }
if(p.y > mMax.y) mMax.y = p.y;
if(p.y < mMin.y) mMin.y = p.y;
if(p.z > mMax.z) mMax.z = p.z;
if(p.z < mMin.z) mMin.z = p.z;
}
// Data access
//! Get min point of the box
@@ -492,18 +479,7 @@ subject to the following restrictions:
#endif
inline_ void ComputeMinMax(const Point& p, Point& min, Point& max)
{
if(p.x > max.x) max.x = p.x;
if(p.x < min.x) min.x = p.x;
if(p.y > max.y) max.y = p.y;
if(p.y < min.y) min.y = p.y;
if(p.z > max.z) max.z = p.z;
if(p.z < min.z) min.z = p.z;
}
//! Computes the AABB around a set of vertices
inline_ void ComputeAABB(AABB& aabb, const Point* list, udword nb_pts)
{
if(list)
@@ -519,4 +495,20 @@ subject to the following restrictions:
}
}
#endif // __ICEAABB_H__
//! Computes the AABB around a set of vertices after transform by a matrix
inline_ void ComputeAABB(AABB& aabb, const Point* list, udword nb_pts, const Matrix4x4& world)
{
if(list)
{
Point Maxi(MIN_FLOAT, MIN_FLOAT, MIN_FLOAT);
Point Mini(MAX_FLOAT, MAX_FLOAT, MAX_FLOAT);
while(nb_pts--)
{
// _prefetch(list+1); // off by one ?
ComputeMinMax((*list++)*world, Mini, Maxi);
}
aabb.SetMinMax(Mini, Maxi);
}
}
#endif // ICEAABB_H

View File

@@ -0,0 +1,805 @@
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/**
* Contains an allocator base class.
* \file IceAllocator.cpp
* \author Pierre Terdiman
* \date December, 19, 2003
*/
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Precompiled Header
#include "StdAfx.h"
#include <malloc.h>
using namespace IceCore;
//#define ZERO_OVERHEAD_RELEASE
#define NEW_CODE
// For some reason dmalloc seems a lot slower than the system malloc?
//#define USE_DMALLOC
#ifdef USE_DMALLOC
#include "dmalloc.h"
#define LOCAL_MALLOC dlmalloc
#define LOCAL_FREE dlfree
#else
#define LOCAL_MALLOC ::malloc
#define LOCAL_FREE ::free
#endif
// WARNING: this makes allocations a lot slower. Only use when tracking memory leaks.
//#define ALLOC_STRINGS
// ### doesn't seem that useful
//#define FAST_BUFFER_SIZE 256*1024
#define DEBUG_IDENTIFIER 0xBeefBabe
#define DEBUG_DEALLOCATED 0xDeadDead
#ifdef ALLOC_STRINGS
static const char* AllocString(const char* str)
{
if(!str) return null;
char* mem = (char*)LOCAL_MALLOC(strlen(str)+1);
strcpy(mem, str);
return mem;
}
static void FreeString(const char* str)
{
if(str) LOCAL_FREE((void*)str);
}
#endif
class DefaultAllocator : public Allocator
{
public:
DefaultAllocator();
virtual ~DefaultAllocator();
void reset();
override(Allocator) void* malloc(size_t size, MemoryType type);
override(Allocator) void* mallocDebug(size_t size, const char* filename, udword line, const char* class_name, MemoryType type);
override(Allocator) void* realloc(void* memory, size_t size);
override(Allocator) void* shrink(void* memory, size_t size);
override(Allocator) void free(void* memory);
void DumpCurrentMemoryState() const;
void** mMemBlockList;
udword mMemBlockListSize;
#ifdef NEW_CODE
udword mFirstFree;
#else
udword mMemBlockFirstFree;
#endif
udword mMemBlockUsed;
sdword mNbAllocatedBytes;
sdword mHighWaterMark;
sdword mTotalNbAllocs;
sdword mNbAllocs;
sdword mNbReallocs;
#ifdef FAST_BUFFER_SIZE
udword mNbFastBytes;
udword mFastBufferOffset;
ubyte* mFastBuffer;
#endif
};
#define MEMBLOCKSTART 64
struct DebugBlock
{
udword mCheckValue;
#ifdef FAST_BUFFER_SIZE
MemoryType mType;
#endif
udword mSize;
const char* mFilename;
udword mLine;
udword mSlotIndex;
const char* mClassName;
};
#ifndef FAST_BUFFER_SIZE
ICE_COMPILE_TIME_ASSERT(sizeof(DebugBlock)==24); // Prevents surprises.....
#endif
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
DefaultAllocator::DefaultAllocator() : mNbAllocatedBytes(0), mHighWaterMark(0), mTotalNbAllocs(0), mNbAllocs(0), mNbReallocs(0)
{
mMemBlockList = null;
#ifdef _DEBUG
// Initialize the Memory blocks list (DEBUG mode only)
mMemBlockList = (void**)LOCAL_MALLOC(MEMBLOCKSTART*sizeof(void*));
ZeroMemory(mMemBlockList, MEMBLOCKSTART*sizeof(void*));
mMemBlockListSize = MEMBLOCKSTART;
#ifdef NEW_CODE
mFirstFree = INVALID_ID;
#else
mMemBlockFirstFree = 0;
#endif
mMemBlockUsed = 0;
#endif
#ifdef FAST_BUFFER_SIZE
mNbFastBytes = 0;
mFastBufferOffset = 0;
mFastBuffer = (ubyte*)LOCAL_MALLOC(FAST_BUFFER_SIZE);
#endif
}
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
DefaultAllocator::~DefaultAllocator()
{
#ifdef FAST_BUFFER_SIZE
mNbFastBytes = 0;
mFastBufferOffset = 0;
if(mFastBuffer) LOCAL_FREE(mFastBuffer);
mFastBuffer = null;
#endif
#ifdef _DEBUG
// Ok, it is a bad idea to use _F() here, because it internally uses the allocator (for the log string). So let's use good old C style here.
char Buffer[4096];
if(mNbAllocatedBytes)
{
sprintf(Buffer, "Memory leak detected: %d bytes non released\n", mNbAllocatedBytes);
// IceTrace(Buffer);
// IceTrace(_F("Memory leak detected: %d bytes non released\n", mNbAllocatedBytes));
}
if(mNbAllocs)
{
sprintf(Buffer, "Remaining allocs: %d\n", mNbAllocs);
// IceTrace(Buffer);
// IceTrace(_F("Remaining allocs: %d\n", mNbAllocs));
}
// IceTrace(_F("Nb alloc: %d\n", mTotalNbAllocs));
sprintf(Buffer, "Total nb alloc: %d\n", mTotalNbAllocs);
// IceTrace(Buffer);
// IceTrace(_F("Nb realloc: %d\n", mNbReallocs));
sprintf(Buffer, "Nb realloc: %d\n", mNbReallocs);
// IceTrace(Buffer);
// IceTrace(_F("High water mark: %d Kb\n", mHighWaterMark/1024));
sprintf(Buffer, "High water mark: %d Kb\n", mHighWaterMark/1024);
// IceTrace(Buffer);
// Scanning for memory leaks
if(mMemBlockList && mNbAllocs)
{
udword NbLeaks = 0;
// IceTrace("\n\n ICE Message Memory leaks detected :\n\n");
#ifdef NEW_CODE
for(udword i=0; i<mMemBlockUsed; i++)
{
if(udword(mMemBlockList[i])&1)
continue;
const DebugBlock* DB = (const DebugBlock*)mMemBlockList[i];
// IceTrace(_F(" Address 0x%.8X, %d bytes (%s), allocated in: %s(%d):\n\n", cur+6, cur[1], (const char*)cur[5], (const char*)cur[2], cur[3]));
// IceTrace(_F(" Address 0x%.8X, %d bytes (%s), allocated in: %s(%d):\n\n", DB+1, DB->mSize, DB->mClassName, DB->mFilename, DB->mLine));
sprintf(Buffer, " Address 0x%.8X, %d bytes (%s), allocated in: %s(%d):\n\n", DB+1, DB->mSize, DB->mClassName, DB->mFilename, DB->mLine);
// IceTrace(Buffer);
NbLeaks++;
// Free(cur+4);
}
#else
for(udword i=0, j=0; i<mMemBlockUsed; i++, j++)
{
// Skip empty slots
while(!mMemBlockList[j]) j++;
const DebugBlock* DB = (const DebugBlock*)mMemBlockList[j];
// IceTrace(_F(" Address 0x%.8X, %d bytes (%s), allocated in: %s(%d):\n\n", cur+6, cur[1], (const char*)cur[5], (const char*)cur[2], cur[3]));
// IceTrace(_F(" Address 0x%.8X, %d bytes (%s), allocated in: %s(%d):\n\n", DB+1, DB->mSize, DB->mClassName, DB->mFilename, DB->mLine));
sprintf(Buffer, " Address 0x%.8X, %d bytes (%s), allocated in: %s(%d):\n\n", DB+1, DB->mSize, DB->mClassName, DB->mFilename, DB->mLine);
IceTrace(Buffer);
NbLeaks++;
// Free(cur+4);
}
#endif
// IceTrace(_F("\n Dump complete (%d leaks)\n\n", NbLeaks));
sprintf(Buffer, "\n Dump complete (%d leaks)\n\n", NbLeaks);
// IceTrace(Buffer);
}
// Free the Memory Block list
if(mMemBlockList) LOCAL_FREE(mMemBlockList);
mMemBlockList = null;
#endif
}
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
void DefaultAllocator::reset()
{
mNbAllocatedBytes = 0;
mHighWaterMark = 0;
mNbAllocs = 0;
}
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
void* DefaultAllocator::malloc(udword size, MemoryType type)
{
// return ::malloc(size);
#ifdef _DEBUG
return mallocDebug(size, null, 0, "Undefined", type);
#endif
if(!size)
{
#ifdef _DEBUG
// IceTrace("Warning: trying to allocate 0 bytes\n");
#endif
return null;
}
mTotalNbAllocs++;
mNbAllocs++;
mNbAllocatedBytes+=size;
if(mNbAllocatedBytes>mHighWaterMark) mHighWaterMark = mNbAllocatedBytes;
#ifdef ZERO_OVERHEAD_RELEASE
return LOCAL_MALLOC(size);
#else
void* ptr = (void*)LOCAL_MALLOC(size+8);
udword* blockStart = (udword*)ptr;
blockStart[0] = DEBUG_IDENTIFIER;
blockStart[1] = size;
return ((udword*)ptr)+2;
#endif
}
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
void* DefaultAllocator::mallocDebug(size_t size, const char* filename, udword line, const char* class_name, MemoryType type)
{
#ifdef _DEBUG
if(!size)
{
// IceTrace("Warning: trying to allocate 0 bytes\n");
return null;
}
// Catch improper use of alloc macro...
if(0 && class_name)
{
const char* c = class_name;
while(*c)
{
if(*c==']' || *c=='[')
{
int stop=0;
}
c++;
}
}
// Make sure size is even
if(size&1) size++;
#ifdef FAST_BUFFER_SIZE
// Allocate one debug block in front of each real allocation
void* ptr = null;
if(type==MEMORY_TEMP)
{
udword NeededSize = size + sizeof(DebugBlock);
if(mFastBufferOffset + NeededSize <= FAST_BUFFER_SIZE)
{
ptr = mFastBuffer + mFastBufferOffset;
mFastBufferOffset += NeededSize;
mNbFastBytes += NeededSize;
}
}
if(!ptr)
{
ptr = (void*)LOCAL_MALLOC(size + sizeof(DebugBlock));
type = MEMORY_PERSISTENT;
}
#else
// Allocate one debug block in front of each real allocation
void* ptr = (void*)LOCAL_MALLOC(size + sizeof(DebugBlock));
#endif
ASSERT(IS_ALIGNED_2(udword(ptr)));
// Fill debug block
DebugBlock* DB = (DebugBlock*)ptr;
DB->mCheckValue = DEBUG_IDENTIFIER;
#ifdef FAST_BUFFER_SIZE
DB->mType = type;
#endif
DB->mSize = size;
DB->mLine = line;
DB->mSlotIndex = INVALID_ID;
#ifdef ALLOC_STRINGS
DB->mFilename = AllocString(filename);
DB->mClassName = AllocString(class_name);
#else
DB->mFilename = filename;
DB->mClassName = class_name;
#endif
// Update global stats
mTotalNbAllocs++;
mNbAllocs++;
mNbAllocatedBytes += size;
if(mNbAllocatedBytes>mHighWaterMark)
mHighWaterMark = mNbAllocatedBytes;
// Insert the allocated block in the debug memory block list
if(mMemBlockList)
{
#ifdef NEW_CODE
if(mFirstFree!=INVALID_ID)
{
// Recycle old location
udword NextFree = udword(mMemBlockList[mFirstFree]);
if(NextFree!=INVALID_ID) NextFree>>=1;
mMemBlockList[mFirstFree] = ptr;
DB->mSlotIndex = mFirstFree;
mFirstFree = NextFree;
}
else
{
if(mMemBlockUsed==mMemBlockListSize)
{
// Allocate a bigger block
void** tps = (void**)LOCAL_MALLOC((mMemBlockListSize+MEMBLOCKSTART)*sizeof(void*));
// Copy already used part
CopyMemory(tps, mMemBlockList, mMemBlockListSize*sizeof(void*));
// Initialize remaining part
void* Next = tps + mMemBlockListSize;
ZeroMemory(Next, MEMBLOCKSTART*sizeof(void*));
// Free previous memory, setup new pointer
LOCAL_FREE(mMemBlockList);
mMemBlockList = tps;
// Setup new size
mMemBlockListSize += MEMBLOCKSTART;
}
mMemBlockList[mMemBlockUsed] = ptr;
DB->mSlotIndex = mMemBlockUsed++;
}
#else
// Store allocated pointer in first free slot
mMemBlockList[mMemBlockFirstFree] = ptr;
DB->mSlotIndex = mMemBlockFirstFree;
// Count number of used slots
mMemBlockUsed++;
// Resize if needed
if(mMemBlockUsed==mMemBlockListSize)
{
// Allocate a bigger block
void** tps = (void**)LOCAL_MALLOC((mMemBlockListSize+MEMBLOCKSTART)*sizeof(void*));
// Copy already used part
CopyMemory(tps, mMemBlockList, mMemBlockListSize*sizeof(void*));
// Initialize remaining part
void* Next = tps + mMemBlockListSize;
ZeroMemory(Next, MEMBLOCKSTART*sizeof(void*));
// Free previous memory, setup new pointer
LOCAL_FREE(mMemBlockList);
mMemBlockList = tps;
// -1 because we'll do a ++ just afterwards
mMemBlockFirstFree = mMemBlockListSize-1;
// Setup new size
mMemBlockListSize += MEMBLOCKSTART;
}
// Look for first free ### recode this ugly thing
while(mMemBlockList[++mMemBlockFirstFree] && (mMemBlockFirstFree<mMemBlockListSize));
if(mMemBlockFirstFree==mMemBlockListSize)
{
mMemBlockFirstFree = (udword)-1;
while(mMemBlockList[++mMemBlockFirstFree] && (mMemBlockFirstFree<mMemBlockListSize));
}
#endif
}
return ((ubyte*)ptr) + sizeof(DebugBlock);
#else
Log("Error: mallocDebug has been called in release!\n");
ASSERT(0);//Don't use debug malloc for release mode code!
return 0;
#endif
}
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
void* DefaultAllocator::shrink(void* memory, udword size)
{
return null; // #ifdef ZERO_OVERHEAD_RELEASE
if(!memory) return null;
#ifdef _DEBUG
// Debug codepath
ubyte* SystemPointer = ((ubyte*)memory) - sizeof(DebugBlock);
DebugBlock* DB = (DebugBlock*)SystemPointer;
if(DB->mCheckValue!=DEBUG_IDENTIFIER)
{
// Not a valid memory block
return null;
}
if(size>DB->mSize)
{
// New size should be smaller!
return null;
}
// Try to shrink the block
void* Reduced = _expand(SystemPointer, size + sizeof(DebugBlock));
if(!Reduced) return null;
if(Reduced!=SystemPointer)
{
// Should not be possible?!
}
// Update stats
mNbAllocatedBytes -= DB->mSize;
mNbAllocatedBytes += size;
// Setup new size
DB->mSize = size;
return memory; // The pointer should not have changed!
#else
// Release codepath
udword* SystemPointer = ((udword*)memory)-2;
if(SystemPointer[0]!=DEBUG_IDENTIFIER)
{
// Not a valid memory block
return null;
}
if(size>SystemPointer[1])
{
// New size should be smaller!
return null;
}
// Try to shrink the block
void* Reduced = _expand(SystemPointer, size+8);
if(!Reduced) return null;
if(Reduced!=SystemPointer)
{
// Should not be possible?!
}
// Update stats
mNbAllocatedBytes -= SystemPointer[1];
mNbAllocatedBytes += size;
// Setup new size
SystemPointer[1] = size;
return memory; // The pointer should not have changed!
#endif
}
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
void* DefaultAllocator::realloc(void* memory, udword size)
{
// return ::realloc(memory, size);
ASSERT(0);
return null;
}
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
void DefaultAllocator::free(void* memory)
{
if(!memory)
{
#ifdef _DEBUG
// IceTrace("Warning: trying to free null pointer\n");
#endif
return;
}
#ifdef _DEBUG
DebugBlock* DB = ((DebugBlock*)memory)-1;
// DebugBlock TmpDB = *DB; // Keep a local copy to have readable data when ::free() fails!
// Check we allocated it
if(DB->mCheckValue!=DEBUG_IDENTIFIER)
{
// IceTrace("Error: free unknown memory!!\n");
// ### should we really continue??
return;
}
// Update global stats
mNbAllocatedBytes -= DB->mSize;
mNbAllocs--;
#ifdef NEW_CODE
// Remove the block from the Memory block list
if(mMemBlockList)
{
udword FreeSlot = DB->mSlotIndex;
ASSERT(mMemBlockList[FreeSlot]==DB);
udword NextFree = mFirstFree;
if(NextFree!=INVALID_ID)
{
NextFree<<=1;
NextFree|=1;
}
mMemBlockList[FreeSlot] = (void*)NextFree;
mFirstFree = FreeSlot;
}
#else
udword MemBlockFirstFree = DB->mSlotIndex; // The slot we are in
udword Line = DB->mLine;
const char* File = DB->mFilename;
// Remove the block from the Memory block list
if(mMemBlockList)
{
ASSERT(mMemBlockList[MemBlockFirstFree]==DB);
mMemBlockList[MemBlockFirstFree] = null;
mMemBlockUsed--;
}
#endif
#ifdef ALLOC_STRINGS
FreeString(DB->mClassName);
FreeString(DB->mFilename);
#endif
#ifdef FAST_BUFFER_SIZE
if(DB->mType==MEMORY_TEMP)
{
mNbFastBytes -= DB->mSize + sizeof(DebugBlock);
if(mNbFastBytes==0)
{
mFastBufferOffset = 0;
}
return;
}
#endif
// ### should be useless since we'll release the memory just afterwards
DB->mCheckValue = DEBUG_DEALLOCATED;
DB->mSize = 0;
DB->mClassName = null;
DB->mFilename = null;
DB->mSlotIndex = INVALID_ID;
DB->mLine = INVALID_ID;
LOCAL_FREE(DB);
#else
// Release codepath
#ifdef ZERO_OVERHEAD_RELEASE
// mNbAllocatedBytes -= ptr[1]; // ### use _msize() ?
mNbAllocs--;
LOCAL_FREE(memory);
#else
udword* ptr = ((udword*)memory)-2;
if(ptr[0]!=DEBUG_IDENTIFIER)
{
#ifdef _DEBUG
IceTrace("Error: free unknown memory!!\n");
#endif
}
mNbAllocatedBytes -= ptr[1];
if(mNbAllocatedBytes<0)
{
#ifdef _DEBUG
IceTrace(_F("Oops (%d)\n", ptr[1]));
#endif
}
mNbAllocs--;
ptr[0]=DEBUG_DEALLOCATED;
ptr[1]=0;
LOCAL_FREE(ptr);
#endif
#endif
}
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
inline_ bool ValidAddress(const void* addy)
{
#ifdef NEW_CODE
return (addy && !(udword(addy)&1));
#else
return addy!=null;
#endif
}
void DefaultAllocator::DumpCurrentMemoryState() const
{
#ifdef _DEBUG
// Scanning for memory leaks
if(mMemBlockList && mMemBlockUsed)
{
// IceTrace("\n\n----ALLOCATOR MEMORY DUMP:\n\n");
// We can't just use the "const char*" stored in the debug blocks because they're not guaranteed to
// be unique for similar strings. For example if a Container is allocated from two different DLLs,
// the "Container" character string will be duplicated (one per DLL). So we need to group similar
// strings together using the actual characters, not just the string address. We also have to do this
// without allocating any new memory, since it would add new entries to the memory debug structure.
//
// The good news is that we don't care about speed too much in this function, since it's not supposed
// to be called all the time.
struct Local
{
struct TmpStruct
{
const char* mName;
udword mSize;
};
static int SortCB(const void* elem1, const void* elem2)
{
const TmpStruct* s1 = (const TmpStruct*)elem1;
const TmpStruct* s2 = (const TmpStruct*)elem2;
return strcmp(s1->mName, s2->mName);
}
};
Local::TmpStruct* SortedStrings = (Local::TmpStruct*)LOCAL_MALLOC(sizeof(Local::TmpStruct)*mMemBlockListSize);
udword NbStrings = 0;
udword TotalSize = 0;
for(udword i=0;i<mMemBlockListSize;i++)
{
if(ValidAddress(mMemBlockList[i]))
{
const DebugBlock* DB = (const DebugBlock*)mMemBlockList[i];
if(DB->mClassName)
{
SortedStrings[NbStrings].mName = DB->mClassName; // Memory by class
// SortedStrings[NbStrings].mName = DB->mFilename; // Memory by file
SortedStrings[NbStrings].mSize = DB->mSize;
TotalSize += DB->mSize;
NbStrings++;
}
}
}
qsort(SortedStrings, NbStrings, sizeof(Local::TmpStruct), Local::SortCB);
// Strings are now sorted. They might still be duplicated, i.e. we may have two strings for the same
// class. So now we parse the list and collect used memory for all classes. Then we sort this again,
// to report results in order of increasing memory.
udword NbClasses=0;
udword* Classes = (udword*)LOCAL_MALLOC(sizeof(udword)*NbStrings);
udword* Sizes = (udword*)LOCAL_MALLOC(sizeof(udword)*NbStrings);
udword CurrentSize = SortedStrings[0].mSize;
const char* CurrentClass = SortedStrings[0].mName;
for(udword i=1;i<=NbStrings;i++) // One more time on purpose
{
const char* Current = null;
if(i!=NbStrings)
{
Current = SortedStrings[i].mName;
}
if(Current && strcmp(Current, CurrentClass)==0)
{
// Same class
CurrentSize += SortedStrings[i].mSize;
}
else
{
// New class
// Store previous class
if(CurrentClass)
{
Classes[NbClasses] = (udword)CurrentClass; // We can store this pointer now because it's unique in our new array
Sizes[NbClasses++] = CurrentSize;
}
// Next one
if(Current)
{
CurrentClass = Current;
CurrentSize = SortedStrings[i].mSize;
}
}
}
udword* Ranks0 = (udword*)LOCAL_MALLOC(sizeof(udword)*NbClasses);
udword* Ranks1 = (udword*)LOCAL_MALLOC(sizeof(udword)*NbClasses);
StackRadixSort(RS, Ranks0, Ranks1);
const udword* Sorted = RS.Sort(Sizes, NbClasses).GetRanks();
for(udword i=0;i<NbClasses;i++)
{
udword Index = Sorted[i];
char Buffer[4096];
sprintf(Buffer, "%s : %d\n", (const char*)Classes[Index], Sizes[Index]);
// IceTrace(Buffer);
}
char Buffer[4096];
sprintf(Buffer, "Total size: %d\n", TotalSize);
// IceTrace(Buffer);
LOCAL_FREE(Ranks1);
LOCAL_FREE(Ranks0);
LOCAL_FREE(Sizes);
LOCAL_FREE(Classes);
LOCAL_FREE(SortedStrings);
}
#endif
}
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// ### probably a bad idea to have a static var. Are we sure the "const char*" for class names/etc
// are still valid when this gets deleted?
static Allocator* gAllocator = null;
static DefaultAllocator gDefault;
//static DefaultAllocator* gDefault = null;
Allocator* IceCore::GetAllocator()
{
if(!gAllocator) gAllocator = &gDefault;
// if(!gAllocator) gAllocator = gDefault;
return gAllocator;
}
bool IceCore::SetAllocator(Allocator& allocator)
{
// ### make sure nothing has been allocated from the default one
gAllocator = &allocator;
return true;
}
void IceCore::DumpMemory()
{
gDefault.DumpCurrentMemoryState();
// if(gDefault) gDefault->DumpCurrentMemoryState();
}
void InitDefaultAllocator()
{
// gDefault = ::new DefaultAllocator;
}
void ReleaseDefaultAllocator()
{
// if(gDefault) ::delete gDefault;
// gDefault = null;
}

View File

@@ -0,0 +1,52 @@
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/**
* Contains an allocator base class.
* \file IceAllocator.h
* \author Pierre Terdiman
* \date December, 19, 2003
*/
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Include Guard
#ifndef ICEALLOCATOR_H
#define ICEALLOCATOR_H
enum MemoryType
{
MEMORY_PERSISTENT,
MEMORY_TEMP,
};
class ICECORE_API Allocator
{
public:
virtual void* malloc(size_t size, MemoryType type) = 0;
virtual void* mallocDebug(size_t size, const char* filename, udword line, const char* class_name, MemoryType type) = 0;
virtual void* realloc(void* memory, size_t size) = 0;
virtual void* shrink(void* memory, size_t size) = 0;
virtual void free(void* memory) = 0;
};
FUNCTION ICECORE_API Allocator* GetAllocator();
FUNCTION ICECORE_API bool SetAllocator(Allocator& allocator);
FUNCTION ICECORE_API void DumpMemory();
class ICECORE_API Allocateable
{
public:
#ifdef DONT_TRACK_MEMORY_LEAKS
inline_ void* operator new (size_t size, MemoryType type) { return GetAllocator()->malloc(size, type); }
inline_ void* operator new (size_t size, const char * filename, int line, const char* class_name, MemoryType type) { return GetAllocator()->mallocDebug(size, filename, line, class_name, type); }
inline_ void* operator new[] (size_t size, MemoryType type) { return GetAllocator()->malloc(size, type); }
inline_ void* operator new[] (size_t size, const char * filename, int line, const char* class_name, MemoryType type) { return GetAllocator()->mallocDebug(size, filename, line, class_name, type); }
inline_ void operator delete (void* p) { GetAllocator()->free(p); }
inline_ void operator delete (void* p, MemoryType) { GetAllocator()->free(p); }
inline_ void operator delete (void* p, const char*, int, const char*, MemoryType) { GetAllocator()->free(p); }
inline_ void operator delete[] (void* p) { GetAllocator()->free(p); }
inline_ void operator delete[] (void* p, MemoryType) { GetAllocator()->free(p); }
inline_ void operator delete[] (void* p, const char*, int, const char*, MemoryType) { GetAllocator()->free(p); }
#endif
};
#endif // ICEALLOCATOR_H

View File

@@ -0,0 +1,48 @@
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/**
* Contains custom assertion code.
* \file IceAssert.h
* \author Pierre Terdiman
* \date January, 14, 2001
*/
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Include Guard
#ifndef ICEASSERT_H
#define ICEASSERT_H
// Leave the {} so that you can write this kind of things safely in release mode:
// if(condition) ASSERT()
#ifndef ASSERT
#if defined( _DEBUG )
FUNCTION ICECORE_API bool CustomAssertFunction(int, char*, int, char*, bool&);
//! Custom ASSERT function. Various usages:
//! ASSERT(condition)
//! ASSERT(!"Not implemented")
//! ASSERT(condition && "error text")
#define ASSERT(exp) \
{ \
static bool IgnoreAlways = false; \
if(!IgnoreAlways) \
{ \
if(CustomAssertFunction((int)(exp), #exp, __LINE__, __FILE__, IgnoreAlways)) \
{ \
_asm { int 3 } \
} \
} \
}
#else
#define ASSERT(exp) {}
#endif
#endif
#ifndef assert
#define assert ASSERT
#endif
#define ICE_COMPILE_TIME_ASSERT(exp) extern char ICE_Dummy[ (exp) ? 1 : -1 ]
#endif // ICEASSERT_H

View File

@@ -0,0 +1,73 @@
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/**
* Contains code for bit arrays.
* \file IceBitArray.cpp
* \author Pierre Terdiman
* \date February, 5, 2000
*/
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/**
* A simple array of bits stored as bytes.
*
* \class Container
* \author Pierre Terdiman
* \version 1.0
* \date February, 5, 2000
*/
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Precompiled Header
#include "StdAfx.h"
using namespace IceCore;
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/**
* Constructor.
*/
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
BitArray::BitArray() : mSize(0), mBits(null)
{
}
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/**
* Constructor.
*/
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
BitArray::BitArray(udword nb_bits) : mSize(0), mBits(null)
{
Init(nb_bits);
}
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/**
* Destructor.
*/
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
BitArray::~BitArray()
{
ICE_FREE(mBits);
mSize = 0;
}
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/**
* Initializes the bit array for a given number of entries
* \param nb_bits [in] max number of entries in the array
* \return true if success
*/
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
bool BitArray::Init(udword nb_bits)
{
mSize = BitsToDwords(nb_bits);
// Get ram for n bits
ICE_FREE(mBits);
mBits = (udword*)ICE_ALLOC(sizeof(udword)*mSize);
// Set all bits to 0
ClearAll();
return true;
}

View File

@@ -0,0 +1,82 @@
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/**
* Contains code for bit arrays.
* \file IceBitArray.h
* \author Pierre Terdiman
* \date February, 5, 2000
*/
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Include Guard
#ifndef ICEBITARRAY_H
#define ICEBITARRAY_H
inline_ udword BitsToBytes(udword nb_bits)
{
return (nb_bits>>3) + ((nb_bits&7) ? 1 : 0);
}
inline_ udword BitsToDwords(udword nb_bits)
{
return (nb_bits>>5) + ((nb_bits&31) ? 1 : 0);
}
// Use that one instead of an array of bools. Takes less ram, nearly as fast [no bounds checkings and so on].
class ICECORE_API BitArray
{
public:
//! Constructor
BitArray();
BitArray(udword nb_bits);
//! Destructor
~BitArray();
bool Init(udword nb_bits);
// Data management
inline_ void SetBit(udword bit_number) { mBits[bit_number>>5] |= 1<<(bit_number&31); }
inline_ void ClearBit(udword bit_number) { mBits[bit_number>>5] &= ~(1<<(bit_number&31)); }
inline_ void ToggleBit(udword bit_number) { mBits[bit_number>>5] ^= 1<<(bit_number&31); }
inline_ void ClearAll() { ZeroMemory(mBits, mSize*4); }
inline_ void SetAll() { FillMemory(mBits, mSize*4, 0xff); }
// Data access
inline_ BOOL IsSet(udword bit_number) const { return mBits[bit_number>>5] & (1<<(bit_number&31)); }
inline_ const udword* GetBits() const { return mBits; }
inline_ udword GetSize() const { return mSize; }
protected:
udword* mBits; //!< Array of bits
udword mSize; //!< Size of the array in dwords
};
// - We consider square symmetric N*N matrices
// - A N*N symmetric matrix has N(N+1)/2 elements
// - A boolean version needs N(N+1)/16 bytes
// N NbBits NbBytes
// 4 10 -
// 8 36 4.5
// 16 136 17 <= the one we select
// 32 528 66
static ubyte BitMasks[] = { 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80 };
static ubyte NegBitMasks[] = { 0xFE, 0xFD, 0xFB, 0xF7, 0xEF, 0xDF, 0xBF, 0x7F };
class ICECORE_API BoolSquareSymmetricMatrix16
{
public:
inline_ udword Index(udword x, udword y) const { if(x>y) Swap(x,y); return x + (y ? ((y-1)*y)>>1 : 0); }
inline_ void Set(udword x, udword y) { udword i = Index(x, y); mBits[i>>3] |= BitMasks[i&7]; }
inline_ void Clear(udword x, udword y) { udword i = Index(x, y); mBits[i>>3] &= NegBitMasks[i&7]; }
inline_ void Toggle(udword x, udword y) { udword i = Index(x, y); mBits[i>>3] ^= BitMasks[i&7]; }
inline_ bool IsSet(udword x, udword y) const { udword i = Index(x, y); return (mBits[i>>3] & BitMasks[i&7])!=0; }
inline_ void ClearAll() { ZeroMemory(mBits, 17); }
inline_ void SetAll() { FillMemory(mBits, 17, 0xff); }
ubyte mBits[17];
};
#endif // ICEBITARRAY_H

View File

@@ -1,19 +1,3 @@
/*
* ICE / OPCODE - Optimized Collision Detection
* http://www.codercorner.com/Opcode.htm
*
* Copyright (c) 2001-2008 Pierre Terdiman, pierre@codercorner.com
This software is provided 'as-is', without any express or implied warranty.
In no event will the authors be held liable for any damages arising from the use of this software.
Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it freely,
subject to the following restrictions:
1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required.
2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software.
3. This notice may not be removed or altered from any source distribution.
*/
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/**
* Contains a simple container class.
@@ -38,7 +22,7 @@ subject to the following restrictions:
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Precompiled Header
#include "Stdafx.h"
#include "StdAfx.h"
using namespace IceCore;
@@ -46,6 +30,7 @@ using namespace IceCore;
#ifdef CONTAINER_STATS
udword Container::mNbContainers = 0;
udword Container::mUsedRam = 0;
LinkedList Container::mContainers;
#endif
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
@@ -58,6 +43,7 @@ Container::Container() : mMaxNbEntries(0), mCurNbEntries(0), mEntries(null), mGr
#ifdef CONTAINER_STATS
mNbContainers++;
mUsedRam+=sizeof(Container);
mContainers.AddElem(this);
#endif
}
@@ -66,11 +52,13 @@ Container::Container() : mMaxNbEntries(0), mCurNbEntries(0), mEntries(null), mGr
* Constructor. Also allocates a given number of entries.
*/
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
Container::Container(udword size, float growth_factor) : mMaxNbEntries(0), mCurNbEntries(0), mEntries(null), mGrowthFactor(growth_factor)
Container::Container(udword size, float growth_factor) : mMaxNbEntries(0), mCurNbEntries(0), mEntries(null)
{
SetGrowthFactor(growth_factor);
#ifdef CONTAINER_STATS
mNbContainers++;
mUsedRam+=sizeof(Container);
mContainers.AddElem(this);
#endif
SetSize(size);
}
@@ -85,6 +73,7 @@ Container::Container(const Container& object) : mMaxNbEntries(0), mCurNbEntries(
#ifdef CONTAINER_STATS
mNbContainers++;
mUsedRam+=sizeof(Container);
mContainers.AddElem(this);
#endif
*this = object;
}
@@ -100,9 +89,25 @@ Container::~Container()
#ifdef CONTAINER_STATS
mNbContainers--;
mUsedRam-=GetUsedRam();
mContainers.RemElem(this);
#endif
}
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/**
* Initializes the container so that it uses an external memory buffer. The container doesn't own the memory, resizing is disabled.
* \param max_entries [in] max number of entries in the container
* \param entries [in] external memory buffer
*/
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
void Container::InitSharedBuffers(udword max_entries, udword* entries)
{
Empty(); // Make sure everything has been released
mMaxNbEntries = max_entries;
mEntries = entries;
mGrowthFactor = -1.0f; // Negative growth ==> resize is disabled
}
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/**
* Clears the container. All stored values are deleted, and it frees used ram.
@@ -115,7 +120,7 @@ Container& Container::Empty()
#ifdef CONTAINER_STATS
mUsedRam-=mMaxNbEntries*sizeof(udword);
#endif
DELETEARRAY(mEntries);
if(mGrowthFactor>=0.0f) ICE_FREE(mEntries); // Release memory if we own it
mCurNbEntries = mMaxNbEntries = 0;
return *this;
}
@@ -129,6 +134,13 @@ Container& Container::Empty()
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
bool Container::Resize(udword needed)
{
// Check growth is allowed
if(mGrowthFactor<=0.0f)
{
ASSERT(!"Invalid operation - trying to resize a static buffer!");
return false;
}
#ifdef CONTAINER_STATS
// Subtract previous amount of bytes
mUsedRam-=mMaxNbEntries*sizeof(udword);
@@ -139,7 +151,7 @@ bool Container::Resize(udword needed)
if(mMaxNbEntries<mCurNbEntries + needed) mMaxNbEntries = mCurNbEntries + needed;
// Get some bytes for new entries
udword* NewEntries = new udword[mMaxNbEntries];
udword* NewEntries = (udword*)ICE_ALLOC(sizeof(udword)*mMaxNbEntries);
CHECKALLOC(NewEntries);
#ifdef CONTAINER_STATS
@@ -151,7 +163,7 @@ bool Container::Resize(udword needed)
if(mCurNbEntries) CopyMemory(NewEntries, mEntries, mCurNbEntries*sizeof(udword));
// Delete old data
DELETEARRAY(mEntries);
ICE_FREE(mEntries);
// Assign new pointer
mEntries = NewEntries;
@@ -178,7 +190,7 @@ bool Container::SetSize(udword nb)
mMaxNbEntries = nb;
// Get some bytes for new entries
mEntries = new udword[mMaxNbEntries];
mEntries = (udword*)ICE_ALLOC(sizeof(udword)*mMaxNbEntries);
CHECKALLOC(mEntries);
#ifdef CONTAINER_STATS
@@ -196,6 +208,13 @@ bool Container::SetSize(udword nb)
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
bool Container::Refit()
{
// Check refit is allowed
if(mGrowthFactor<=0.0f)
{
ASSERT(!"Invalid operation - trying to refit a static buffer!");
return false;
}
#ifdef CONTAINER_STATS
// Subtract previous amount of bytes
mUsedRam-=mMaxNbEntries*sizeof(udword);
@@ -206,7 +225,7 @@ bool Container::Refit()
if(!mMaxNbEntries) return false;
// Get just enough bytes
udword* NewEntries = new udword[mMaxNbEntries];
udword* NewEntries = (udword*)ICE_ALLOC(sizeof(udword)*mMaxNbEntries);
CHECKALLOC(NewEntries);
#ifdef CONTAINER_STATS
@@ -218,7 +237,7 @@ bool Container::Refit()
CopyMemory(NewEntries, mEntries, mCurNbEntries*sizeof(udword));
// Delete old data
DELETEARRAY(mEntries);
ICE_FREE(mEntries);
// Assign new pointer
mEntries = NewEntries;
@@ -226,6 +245,36 @@ bool Container::Refit()
return true;
}
// Same as Refit but more efficient
bool Container::Shrink()
{
if(!mEntries) return false;
if(!mCurNbEntries)
{
Empty();
return true;
}
// Try to shrink the pointer
if(!GetAllocator()->shrink(mEntries, sizeof(udword)*mCurNbEntries))
return false;
#ifdef CONTAINER_STATS
// Subtract previous amount of bytes
mUsedRam-=mMaxNbEntries*sizeof(udword);
#endif
// Get just enough entries
mMaxNbEntries = mCurNbEntries;
#ifdef CONTAINER_STATS
// Add current amount of bytes
mUsedRam+=mMaxNbEntries*sizeof(udword);
#endif
return true;
}
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/**
* Checks whether the container already contains a given value.
@@ -353,6 +402,19 @@ udword Container::GetUsedRam() const
return sizeof(Container) + mMaxNbEntries * sizeof(udword);
}
float Container::GetGrowthFactor() const
{
return mGrowthFactor;
}
void Container::SetGrowthFactor(float growth)
{
// Negative growths are reserved for internal usages
if(growth<0.0f) growth = 0.0f;
mGrowthFactor = growth;
}
//! Operator for "Container A = Container B"
void Container::operator=(const Container& object)
{
SetSize(object.GetNbEntries());

View File

@@ -1,19 +1,3 @@
/*
* ICE / OPCODE - Optimized Collision Detection
* http://www.codercorner.com/Opcode.htm
*
* Copyright (c) 2001-2008 Pierre Terdiman, pierre@codercorner.com
This software is provided 'as-is', without any express or implied warranty.
In no event will the authors be held liable for any damages arising from the use of this software.
Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it freely,
subject to the following restrictions:
1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required.
2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software.
3. This notice may not be removed or altered from any source distribution.
*/
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/**
* Contains a simple container class.
@@ -25,10 +9,12 @@ subject to the following restrictions:
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Include Guard
#ifndef __ICECONTAINER_H__
#define __ICECONTAINER_H__
#ifndef ICECONTAINER_H
#define ICECONTAINER_H
#define CONTAINER_STATS
// #define CONTAINER_STATS // #### doesn't work with micro-threads!
class LinkedList;
enum FindMode
{
@@ -38,7 +24,10 @@ subject to the following restrictions:
FIND_FORCE_DWORD = 0x7fffffff
};
class ICECORE_API Container
class ICECORE_API Container : public Allocateable
#ifdef CONTAINER_STATS
, public ListElem
#endif
{
public:
// Constructor / Destructor
@@ -47,6 +36,15 @@ subject to the following restrictions:
Container(udword size, float growth_factor);
~Container();
// Management
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/**
* Initializes the container so that it uses an external memory buffer. The container doesn't own the memory, resizing is disabled.
* \param max_entries [in] max number of entries in the container
* \param entries [in] external memory buffer
*/
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
void InitSharedBuffers(udword max_entries, udword* entries);
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/**
* A O(1) method to add a value in the container. The container is automatically resized if needed.
@@ -71,16 +69,39 @@ subject to the following restrictions:
}
inline_ Container& Add(const udword* entries, udword nb)
{
if(entries && nb)
{
// Resize if needed
if(mCurNbEntries+nb>mMaxNbEntries) Resize(nb);
// Add new entry
CopyMemory(&mEntries[mCurNbEntries], entries, nb*sizeof(udword));
mCurNbEntries+=nb;
mCurNbEntries += nb;
}
return *this;
}
inline_ Container& Add(const Container& container)
{
return Add(container.GetEntries(), container.GetNbEntries());
}
inline_ udword* Reserve(udword nb)
{
// Resize if needed
if(mCurNbEntries+nb>mMaxNbEntries) Resize(nb);
// We expect the user to fill reserved memory with 'nb' udwords
udword* Reserved = &mEntries[mCurNbEntries];
// Meanwhile, we do as if it had been filled
mCurNbEntries += nb;
// This is mainly used to avoid the copy when possible
return Reserved;
}
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/**
* A O(1) method to add a value in the container. The container is automatically resized if needed.
@@ -145,7 +166,7 @@ subject to the following restrictions:
if(mCurNbEntries) mCurNbEntries = 0;
}
// HANDLE WITH CARE
// HANDLE WITH CARE - I hope you know what you're doing
inline_ void ForceSize(udword size)
{
mCurNbEntries = size;
@@ -168,6 +189,8 @@ subject to the following restrictions:
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
bool Refit();
bool Shrink();
// Checks whether the container already contains a given value.
bool Contains(udword entry, udword* location=null) const;
// Deletes an entry - doesn't preserve insertion order.
@@ -184,15 +207,16 @@ subject to the following restrictions:
Container& FindPrev(udword& entry, FindMode find_mode=FIND_CLAMP);
// Data access.
inline_ udword GetNbEntries() const { return mCurNbEntries; } //!< Returns the current number of entries.
inline_ udword GetEntry(udword i) const { return mEntries[i]; } //!< Returns ith entry
inline_ udword GetMaxNbEntries() const { return mMaxNbEntries; } //!< Returns max number of entries before resizing.
inline_ udword GetEntry(udword i) const { return mEntries[i]; } //!< Returns ith entry.
inline_ udword* GetEntries() const { return mEntries; } //!< Returns the list of entries.
inline_ udword GetFirst() const { return mEntries[0]; }
inline_ udword GetLast() const { return mEntries[mCurNbEntries-1]; }
// Growth control
inline_ float GetGrowthFactor() const { return mGrowthFactor; } //!< Returns the growth factor
inline_ void SetGrowthFactor(float growth) { mGrowthFactor = growth; } //!< Sets the growth factor
float GetGrowthFactor() const; //!< Returns the growth factor
void SetGrowthFactor(float growth); //!< Sets the growth factor
inline_ bool IsFull() const { return mCurNbEntries==mMaxNbEntries; } //!< Checks the container is full
inline_ BOOL IsNotEmpty() const { return mCurNbEntries; } //!< Checks the container is empty
@@ -208,12 +232,14 @@ subject to the following restrictions:
void operator = (const Container& object);
#ifdef CONTAINER_STATS
inline_ udword GetNbContainers() const { return mNbContainers; }
inline_ udword GetTotalBytes() const { return mUsedRam; }
static udword GetNbContainers() { return mNbContainers; }
static udword GetTotalBytes() { return mUsedRam; }
static LinkedList& GetContainers() { return mContainers; }
private:
static udword mNbContainers; //!< Number of containers around
static udword mUsedRam; //!< Amount of bytes used by containers in the system
static LinkedList mContainers;
#endif
private:
// Resizing
@@ -225,4 +251,4 @@ subject to the following restrictions:
float mGrowthFactor; //!< Resize: new number of entries = old number * mGrowthFactor
};
#endif // __ICECONTAINER_H__
#endif // ICECONTAINER_H

View File

@@ -1,19 +1,3 @@
/*
* ICE / OPCODE - Optimized Collision Detection
* http://www.codercorner.com/Opcode.htm
*
* Copyright (c) 2001-2008 Pierre Terdiman, pierre@codercorner.com
This software is provided 'as-is', without any express or implied warranty.
In no event will the authors be held liable for any damages arising from the use of this software.
Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it freely,
subject to the following restrictions:
1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required.
2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software.
3. This notice may not be removed or altered from any source distribution.
*/
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/**
* Contains FPU related code.
@@ -25,8 +9,8 @@ subject to the following restrictions:
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Include Guard
#ifndef __ICEFPU_H__
#define __ICEFPU_H__
#ifndef ICEFPU_H
#define ICEFPU_H
#define SIGN_BITMASK 0x80000000
@@ -46,6 +30,12 @@ subject to the following restrictions:
//! Don't use it blindly, it can be faster or slower than the FPU comparison, depends on the context.
#define IS_NEGATIVE_FLOAT(x) (IR(x)&0x80000000)
//! Checks 2 values have different signs
inline_ BOOL DifferentSign(float f0, float f1)
{
return (IR(f0)^IR(f1))&SIGN_BITMASK;
}
//! Fast fabs for floating-point values. It just clears the sign bit.
//! Don't use it blindy, it can be faster or slower than the FPU comparison, depends on the context.
inline_ float FastFabs(float x)
@@ -292,6 +282,46 @@ subject to the following restrictions:
return Res;
}
//! A global function to find MAX(a,b,c,d) using FCOMI/FCMOV
inline_ float FCMax4(float a, float b, float c, float d)
{
float Res;
_asm fld [a]
_asm fld [b]
_asm fld [c]
_asm fld [d]
FCOMI_ST1
FCMOVB_ST1
FCOMI_ST2
FCMOVB_ST2
FCOMI_ST3
FCMOVB_ST3
_asm fstp [Res]
_asm fcompp
_asm fcomp
return Res;
}
//! A global function to find MIN(a,b,c,d) using FCOMI/FCMOV
inline_ float FCMin4(float a, float b, float c, float d)
{
float Res;
_asm fld [a]
_asm fld [b]
_asm fld [c]
_asm fld [d]
FCOMI_ST1
FCMOVNB_ST1
FCOMI_ST2
FCMOVNB_ST2
FCOMI_ST3
FCMOVNB_ST3
_asm fstp [Res]
_asm fcompp
_asm fcomp
return Res;
}
inline_ int ConvertToSortable(float f)
{
int& Fi = (int&)f;
@@ -302,6 +332,32 @@ subject to the following restrictions:
return Fi;
}
inline_ udword EncodeFloat(const float val)
{
// We may need to check on -0 and 0
// But it should make no practical difference.
udword ir = IR(val);
if(ir & 0x80000000) //negative?
ir = ~ir;//reverse sequence of negative numbers
else
ir |= 0x80000000; // flip sign
return ir;
}
inline_ float DecodeFloat(udword ir)
{
udword rv;
if(ir & 0x80000000) //positive?
rv = ir & ~0x80000000; //flip sign
else
rv = ~ir; //undo reversal
return FR(rv);
}
enum FPUMode
{
FPU_FLOOR = 0,
@@ -330,4 +386,18 @@ subject to the following restrictions:
FUNCTION ICECORE_API int intFloor(const float& f);
FUNCTION ICECORE_API int intCeil(const float& f);
#endif // __ICEFPU_H__
inline_ sdword MyFloor(float f)
{
return (sdword)f - (IR(f)>>31);
}
class ICECORE_API FPUGuard
{
public:
FPUGuard();
~FPUGuard();
private:
uword mControlWord;
};
#endif // ICEFPU_H

View File

@@ -0,0 +1,78 @@
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/**
* Contains hashing code.
* \file IceHashing.h
* \author Pierre Terdiman
* \date May, 08, 1999
*/
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Include Guard
#ifndef ICEHASHING_H
#define ICEHASHING_H
#define HashSize(n) ((udword)1<<(n))
#define HashMask(n) (HashSize(n)-1)
ICECORE_API udword Hash(const char* str);
ICECORE_API udword Hash(ubyte* k, udword length, udword initval);
// Bob Jenkin's hash
inline_ unsigned int Hash32Bits_0(unsigned int key)
{
key += (key << 12);
key ^= (key >> 22);
key += (key << 4);
key ^= (key >> 9);
key += (key << 10);
key ^= (key >> 2);
key += (key << 7);
key ^= (key >> 12);
return key;
}
// Thomas Wang's hash
inline_ int Hash32Bits_1(int key)
{
key += ~(key << 15);
key ^= (key >> 10);
key += (key << 3);
key ^= (key >> 6);
key += ~(key << 11);
key ^= (key >> 16);
return key;
}
// Thomas Wang's hash
inline_ __int64 Hash64Bits_0(__int64 key)
{
key += ~(key << 32);
key ^= (key >> 22);
key += ~(key << 13);
key ^= (key >> 8);
key += (key << 3);
key ^= (key >> 15);
key += ~(key << 27);
key ^= (key >> 31);
return key;
}
inline_ __int64 Hash64Bits_1(__int64 key)
{
__int64 c1 = 0x6e5ea73858134343L;
__int64 c2 = 0xb34e8f99a2ec9ef5L;
key ^= ((c1 ^ key) >> 32);
key *= c1;
key ^= ((c2 ^ key) >> 31);
key *= c2;
key ^= ((c1 ^ key) >> 32);
return key;
}
inline_ udword Hash(udword id0, udword id1)
{
return Hash32Bits_1( (id0&0xffff)|(id1<<16) );
}
#endif // ICEHASHING_H

View File

@@ -1,19 +1,3 @@
/*
* ICE / OPCODE - Optimized Collision Detection
* http://www.codercorner.com/Opcode.htm
*
* Copyright (c) 2001-2008 Pierre Terdiman, pierre@codercorner.com
This software is provided 'as-is', without any express or implied warranty.
In no event will the authors be held liable for any damages arising from the use of this software.
Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it freely,
subject to the following restrictions:
1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required.
2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software.
3. This notice may not be removed or altered from any source distribution.
*/
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/**
* Contains all memory macros.
@@ -25,8 +9,8 @@ subject to the following restrictions:
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Include Guard
#ifndef __ICEMEMORYMACROS_H__
#define __ICEMEMORYMACROS_H__
#ifndef ICEMEMORYMACROS_H
#define ICEMEMORYMACROS_H
#undef ZeroMemory
#undef CopyMemory
@@ -40,7 +24,7 @@ subject to the following restrictions:
//! \see StoreDwords
//! \see CopyMemory
//! \see MoveMemory
inline_ void ZeroMemory(void* addr, udword size) { memset(addr, 0, size); }
inline_ void ZeroMemory(void* addr, regsize size) { memset(addr, 0, size); }
//! Fills a buffer with a given byte.
//! \param addr [in] buffer address
@@ -50,7 +34,7 @@ subject to the following restrictions:
//! \see ZeroMemory
//! \see CopyMemory
//! \see MoveMemory
inline_ void FillMemory(void* dest, udword size, ubyte val) { memset(dest, val, size); }
inline_ void FillMemory(void* dest, regsize size, ubyte val) { memset(dest, val, size); }
//! Fills a buffer with a given dword.
//! \param addr [in] buffer address
@@ -90,7 +74,7 @@ subject to the following restrictions:
//! \see FillMemory
//! \see StoreDwords
//! \see MoveMemory
inline_ void CopyMemory(void* dest, const void* src, udword size) { memcpy(dest, src, size); }
inline_ void CopyMemory(void* dest, const void* src, regsize size) { memcpy(dest, src, size); }
//! Moves a buffer.
//! \param addr [in] destination buffer address
@@ -100,22 +84,97 @@ subject to the following restrictions:
//! \see FillMemory
//! \see StoreDwords
//! \see CopyMemory
inline_ void MoveMemory(void* dest, const void* src, udword size) { memmove(dest, src, size); }
inline_ void MoveMemory(void* dest, const void* src, regsize size) { memmove(dest, src, size); }
#define SIZEOFOBJECT sizeof(*this) //!< Gives the size of current object. Avoid some mistakes (e.g. "sizeof(this)").
//#define CLEAROBJECT { memset(this, 0, SIZEOFOBJECT); } //!< Clears current object. Laziness is my business. HANDLE WITH CARE.
#define DELETESINGLE(x) if (x) { delete x; x = null; } //!< Deletes an instance of a class.
#define DELETEARRAY(x) if (x) { delete []x; x = null; } //!< Deletes an array.
#define SAFE_RELEASE(x) if (x) { (x)->Release(); (x) = null; } //!< Safe D3D-style release
#define SAFE_DESTRUCT(x) if (x) { (x)->SelfDestruct(); (x) = null; } //!< Safe ICE-style release
//! Flexible buffer copy
//! \param src [in] source buffer address
//! \param dst [in] destination buffer address
//! \param nb_elem [in] number of elements to copy
//! \param elem_size [in] size of an element
//! \param stride [in] stride in bytes, including size of element
inline_ void FlexiCopy(const void* src, void* dst, udword nb_elem, regsize elem_size, udword stride)
{
ubyte* d = (ubyte*)dst;
const ubyte* s = (const ubyte*)src;
const ubyte* Last = s + stride*nb_elem;
while(s!=Last)
{
CopyMemory(d, s, elem_size);
d += elem_size;
s += stride;
}
}
#ifdef __ICEERROR_H__
#define CHECKALLOC(x) if(!x) return SetIceError("Out of memory.", EC_OUT_OF_MEMORY); //!< Standard alloc checking. HANDLE WITH CARE.
//! Gives the size of current object. This avoids some mistakes (e.g. "sizeof(this)").
#define SIZEOFOBJECT sizeof(*this)
//! Clears current object. Laziness is my business! HANDLE WITH CARE. ### Removed, too dangerous, cleared too many v-tables
//#define CLEAROBJECT { memset(this, 0, SIZEOFOBJECT); }
// The two macros below are here for several reasons:
// - sometimes we write "delete x" instead of "delete []x" just because we don't pay attention. Using the macro forces you
// to think about what you're deleting, just because you have to write the macro's name (SINGLE or ARRAY).
// - always clearing the pointer afterwards prevents some double-deletion in various situations.
// - deleting null is a valid operation according to the standard, yet some lame memory managers don't like it. In sake of
// robustness, we avoid trying.
//! Deletes an instance of a class.
#define DELETESINGLE(x) if (x) { delete x; x = null; }
//! Deletes an array.
#define DELETEARRAY(x) if (x) { delete []x; x = null; }
//! Safe D3D-style release
#define SAFE_RELEASE(x) if (x) { (x)->Release(); (x) = null; }
//! Safe ICE-style release
#define SAFE_DESTRUCT(x) if (x) { (x)->SelfDestruct(); (x) = null; }
#ifdef ICEERROR_H
//! Standard alloc checking. HANDLE WITH CARE. Relies on strict coding rules. Probably shouldn't be used outside of ICE.
#define CHECKALLOC(x) if(!x) return SetIceError("Out of memory.", EC_OUT_OF_MEMORY);
#else
#define CHECKALLOC(x) if(!x) return false;
#endif
//! Standard allocation cycle
#define SAFE_ALLOC(ptr, type, count) DELETEARRAY(ptr); ptr = new type[count]; CHECKALLOC(ptr);
#define SAFE_ICE_ALLOC(ptr, type, count) DELETEARRAY(ptr); ptr = ICE_NEW(type)[count]; CHECKALLOC(ptr);
#endif // __ICEMEMORYMACROS_H__
//! Don't use inline for alloca !!!
#ifdef WIN32
#define StackAlloc(x) _alloca(x)
#elif LINUX
#define StackAlloc(x) alloca(x)
#elif defined(__APPLE__)
#define StackAlloc(x) alloca(x)
#elif defined(_XBOX)
#define StackAlloc(x) _alloca(x)
#endif
#ifdef _DEBUG
// #define ICE_ALLOC_TMP(x) GetAllocator()->mallocDebug(x, __FILE__, __LINE__, #x, MEMORY_TEMP)
// #define ICE_ALLOC(x) GetAllocator()->mallocDebug(x, __FILE__, __LINE__, #x, MEMORY_PERSISTENT)
#define ICE_ALLOC_TMP(x) GetAllocator()->mallocDebug(x, __FILE__, __LINE__, "(undefined)", MEMORY_TEMP)
#define ICE_ALLOC(x) GetAllocator()->mallocDebug(x, __FILE__, __LINE__, "(undefined)", MEMORY_PERSISTENT)
#define ICE_ALLOC_TMP2(x, y) GetAllocator()->mallocDebug(x, __FILE__, __LINE__, #y, MEMORY_TEMP)
#define ICE_ALLOC2(x, y) GetAllocator()->mallocDebug(x, __FILE__, __LINE__, #y, MEMORY_PERSISTENT)
#else
#define ICE_ALLOC_TMP(x) GetAllocator()->malloc(x, MEMORY_TEMP)
#define ICE_ALLOC(x) GetAllocator()->malloc(x, MEMORY_PERSISTENT)
#endif
#define ICE_FREE(x) if(x) { GetAllocator()->free(x); x = null; }
#ifdef DONT_TRACK_MEMORY_LEAKS
#ifdef _DEBUG
#define ICE_NEW_TMP(x) new(__FILE__, __LINE__, #x, MEMORY_TEMP) x
#define ICE_NEW(x) new(__FILE__, __LINE__, #x, MEMORY_PERSISTENT) x
#else
#define ICE_NEW_TMP(x) new(MEMORY_TEMP) x
#define ICE_NEW(x) new(MEMORY_PERSISTENT) x
#endif
#else
#define ICE_NEW_TMP(x) new x
#define ICE_NEW(x) new x
#endif
#endif // ICEMEMORYMACROS_H

View File

@@ -1,19 +1,3 @@
/*
* ICE / OPCODE - Optimized Collision Detection
* http://www.codercorner.com/Opcode.htm
*
* Copyright (c) 2001-2008 Pierre Terdiman, pierre@codercorner.com
This software is provided 'as-is', without any express or implied warranty.
In no event will the authors be held liable for any damages arising from the use of this software.
Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it freely,
subject to the following restrictions:
1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required.
2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software.
3. This notice may not be removed or altered from any source distribution.
*/
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/**
* Contains preprocessor stuff. This should be the first included header.
@@ -25,8 +9,8 @@ subject to the following restrictions:
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Include Guard
#ifndef __ICEPREPROCESSOR_H__
#define __ICEPREPROCESSOR_H__
#ifndef ICEPREPROCESSOR_H
#define ICEPREPROCESSOR_H
// Check platform
#if defined( _WIN32 ) || defined( WIN32 )
@@ -40,6 +24,14 @@ subject to the following restrictions:
#if defined(_MSC_VER)
#pragma message("Compiling with VC++...")
#define COMPILER_VISUAL_CPP
#if _MSC_VER > 1300
#pragma message("Compiling with VC7")
#define COMPILER_VC7
#else
#pragma message("Compiling with VC6")
#define COMPILER_VC6
#endif
#else
#pragma message("Compiling with unknown compiler...")
#endif
@@ -90,10 +82,6 @@ subject to the following restrictions:
#define ICECORE_API
#endif
// Don't override new/delete
// #define DEFAULT_NEWDELETE
#define DONT_TRACK_MEMORY_LEAKS
#define FUNCTION extern "C"
// Cosmetic stuff [mainly useful with multiple inheritance]
@@ -135,10 +123,36 @@ subject to the following restrictions:
// ANSI compliance
#ifdef _DEBUG
// Remove painful warning in debug
inline_ bool __False__(){ return false; }
#define for if(__False__()){} else for
inline_ bool ReturnsFalse(){ return false; }
#define for if(ReturnsFalse()){} else for
#else
#define for if(0){} else for
#endif
#endif // __ICEPREPROCESSOR_H__
// Don't override new/delete
#define DEFAULT_NEWDELETE
#define DONT_TRACK_MEMORY_LEAKS
//! Macro used to give me a clue when it crashes in release and only the assembly is available
#define INCLUDE_GUARDIANS
#ifdef INCLUDE_GUARDIANS
#define GUARD(x) \
{ \
static const char guard_text[] = x; \
_asm push eax \
_asm nop \
_asm nop \
_asm nop \
_asm nop \
_asm lea eax, guard_text \
_asm nop \
_asm nop \
_asm nop \
_asm nop \
_asm pop eax \
}
#else
#define GUARD(x)
#endif
#endif // ICEPREPROCESSOR_H

View File

@@ -1,19 +1,3 @@
/*
* ICE / OPCODE - Optimized Collision Detection
* http://www.codercorner.com/Opcode.htm
*
* Copyright (c) 2001-2008 Pierre Terdiman, pierre@codercorner.com
This software is provided 'as-is', without any express or implied warranty.
In no event will the authors be held liable for any damages arising from the use of this software.
Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it freely,
subject to the following restrictions:
1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required.
2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software.
3. This notice may not be removed or altered from any source distribution.
*/
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/**
* Contains source code from the article "Radix Sort Revisited".
@@ -44,12 +28,18 @@ subject to the following restrictions:
* - 01.20.02: bugfix! In very particular cases the last pass was skipped in the float code-path, leading to incorrect sorting......
* - 01.02.02: - "mIndices" renamed => "mRanks". That's a rank sorter after all.
* - ranks are not "reset" anymore, but implicit on first calls
* - 07.05.02: - offsets rewritten with one less indirection.
* - 11.03.02: - "bool" replaced with RadixHint enum
* - 07.05.02: offsets rewritten with one less indirection.
* - 11.03.02: "bool" replaced with RadixHint enum
* - 07.15.04: stack-based radix added
* - we want to use the radix sort but without making it static, and without allocating anything.
* - we internally allocate two arrays of ranks. Each of them has N udwords to sort N values.
* - 1Mb/2/sizeof(udword) = 131072 values max, at the same time.
* - 09.22.04: - adapted to MacOS by Chris Lamb
* - 01.12.06: - added optimizations suggested by Kyle Hubert
*
* \class RadixSort
* \author Pierre Terdiman
* \version 1.4
* \version 1.5
* \date August, 15, 1998
*/
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
@@ -58,14 +48,13 @@ subject to the following restrictions:
To do:
- add an offset parameter between two input values (avoid some data recopy sometimes)
- unroll ? asm ?
- 11 bits trick & 3 passes as Michael did
- prefetch stuff the day I have a P3
- make a version with 16-bits indices ?
*/
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Precompiled Header
#include "Stdafx.h"
#include "StdAfx.h"
using namespace IceCore;
@@ -82,17 +71,31 @@ using namespace IceCore;
mPreviousSize = n; \
}
#if defined(__APPLE__) || defined(_XBOX)
#define H0_OFFSET 768
#define H1_OFFSET 512
#define H2_OFFSET 256
#define H3_OFFSET 0
#define BYTES_INC (3-j)
#else
#define H0_OFFSET 0
#define H1_OFFSET 256
#define H2_OFFSET 512
#define H3_OFFSET 768
#define BYTES_INC j
#endif
#define CREATE_HISTOGRAMS(type, buffer) \
/* Clear counters/histograms */ \
ZeroMemory(mHistogram, 256*4*sizeof(udword)); \
\
/* Prepare to count */ \
ubyte* p = (ubyte*)input; \
ubyte* pe = &p[nb*4]; \
udword* h0= &mHistogram[0]; /* Histogram for first pass (LSB) */ \
udword* h1= &mHistogram[256]; /* Histogram for second pass */ \
udword* h2= &mHistogram[512]; /* Histogram for third pass */ \
udword* h3= &mHistogram[768]; /* Histogram for last pass (MSB) */ \
const ubyte* p = (const ubyte*)input; \
const ubyte* pe = &p[nb*4]; \
udword* h0= &mHistogram[H0_OFFSET]; /* Histogram for first pass (LSB) */ \
udword* h1= &mHistogram[H1_OFFSET]; /* Histogram for second pass */ \
udword* h2= &mHistogram[H2_OFFSET]; /* Histogram for third pass */ \
udword* h3= &mHistogram[H3_OFFSET]; /* Histogram for last pass (MSB) */ \
\
bool AlreadySorted = true; /* Optimism... */ \
\
@@ -128,7 +131,7 @@ using namespace IceCore;
else \
{ \
/* Prepare for temporal coherence */ \
udword* Indices = mRanks; \
const udword* Indices = mRanks; \
type PrevVal = (type)buffer[*Indices]; \
\
while(p!=pe) \
@@ -159,7 +162,7 @@ using namespace IceCore;
#define CHECK_PASS_VALIDITY(pass) \
/* Shortcut to current counters */ \
udword* CurCount = &mHistogram[pass<<8]; \
const udword* CurCount = &mHistogram[pass<<8]; \
\
/* Reset flag. The sorting pass is supposed to be performed. (default) */ \
bool PerformPass = true; \
@@ -183,12 +186,12 @@ using namespace IceCore;
* Constructor.
*/
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
RadixSort::RadixSort() : mRanks(null), mRanks2(null), mCurrentSize(0), mTotalCalls(0), mNbHits(0)
RadixSort::RadixSort() : mRanks(null), mRanks2(null), mCurrentSize(0), mTotalCalls(0), mNbHits(0), mDeleteRanks(true)
{
#ifndef RADIX_LOCAL_RAM
// Allocate input-independent ram
mHistogram = new udword[256*4];
mOffset = new udword[256];
mHistogram = ICE_ALLOC(sizeof(udword)*256*4);
mOffset = ICE_ALLOC(sizeof(udword)*256);
#endif
// Initialize indices
INVALIDATE_RANKS;
@@ -203,11 +206,14 @@ RadixSort::~RadixSort()
{
// Release everything
#ifndef RADIX_LOCAL_RAM
DELETEARRAY(mOffset);
DELETEARRAY(mHistogram);
ICE_FREE(mOffset);
ICE_FREE(mHistogram);
#endif
DELETEARRAY(mRanks2);
DELETEARRAY(mRanks);
if(mDeleteRanks)
{
ICE_FREE(mRanks2);
ICE_FREE(mRanks);
}
}
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
@@ -219,14 +225,16 @@ RadixSort::~RadixSort()
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
bool RadixSort::Resize(udword nb)
{
if(mDeleteRanks)
{
// Free previously used ram
DELETEARRAY(mRanks2);
DELETEARRAY(mRanks);
ICE_FREE(mRanks2);
ICE_FREE(mRanks);
// Get some fresh one
mRanks = new udword[nb]; CHECKALLOC(mRanks);
mRanks2 = new udword[nb]; CHECKALLOC(mRanks2);
mRanks = (udword*)ICE_ALLOC(sizeof(udword)*nb); CHECKALLOC(mRanks);
mRanks2 = (udword*)ICE_ALLOC(sizeof(udword)*nb); CHECKALLOC(mRanks2);
}
return true;
}
@@ -276,7 +284,7 @@ RadixSort& RadixSort::Sort(const udword* input, udword nb, RadixHint hint)
// have 2 code paths even if just a single opcode changes. Self-modifying code, someone?
if(hint==RADIX_UNSIGNED) { CREATE_HISTOGRAMS(udword, input); }
else { CREATE_HISTOGRAMS(sdword, input); }
/*
// Compute #negative values involved if needed
udword NbNegativeValues = 0;
if(hint==RADIX_SIGNED)
@@ -287,7 +295,7 @@ RadixSort& RadixSort::Sort(const udword* input, udword nb, RadixHint hint)
udword* h3= &mHistogram[768];
for(udword i=128;i<256;i++) NbNegativeValues += h3[i]; // 768 for last histogram, 128 for negative part
}
*/
// Radix sort, j is the pass number (0=LSB, 3=MSB)
for(udword j=0;j<4;j++)
{
@@ -311,23 +319,38 @@ RadixSort& RadixSort::Sort(const udword* input, udword nb, RadixHint hint)
else
{
// This is a special case to correctly handle negative integers. They're sorted in the right order but at the wrong place.
/*
// Create biased offsets, in order for negative numbers to be sorted as well
// mOffset[0] = NbNegativeValues; // First positive number takes place after the negative ones
mLink[0] = &mRanks2[NbNegativeValues]; // First positive number takes place after the negative ones
// for(udword i=1;i<128;i++) mOffset[i] = mOffset[i-1] + CurCount[i-1]; // 1 to 128 for positive numbers
mLink[0] = &mRanks2[NbNegativeValues]; // First positive number takes place after the negative ones
for(udword i=1;i<128;i++) mLink[i] = mLink[i-1] + CurCount[i-1]; // 1 to 128 for positive numbers
// Fixing the wrong place for negative values
// mOffset[128] = 0;
mLink[128] = mRanks2;
// for(i=129;i<256;i++) mOffset[i] = mOffset[i-1] + CurCount[i-1];
mLink[128] = mRanks2;
for(udword i=129;i<256;i++) mLink[i] = mLink[i-1] + CurCount[i-1];
*/
// From Kyle Hubert:
//mOffset[128] = 0;
mLink[128] = mRanks2;
//for(i=129;i<256;i++) mOffset[i] = mOffset[i-1] + CurCount[i-1];
for(udword i=129;i<256;i++) mLink[i] = mLink[i-1] + CurCount[i-1];
//mOffset[0] = mOffset[255] + CurCount[255];
mLink[0] = mLink[255] + CurCount[255];
//for(i=1;i<128;i++) mOffset[i] = mOffset[i-1] + CurCount[i-1];
for(udword i=1;i<128;i++) mLink[i] = mLink[i-1] + CurCount[i-1];
}
// Perform Radix Sort
ubyte* InputBytes = (ubyte*)input;
InputBytes += j;
const ubyte* InputBytes = (const ubyte*)input;
InputBytes += BYTES_INC;
if(INVALID_RANKS)
{
// for(udword i=0;i<nb;i++) mRanks2[mOffset[InputBytes[i<<2]]++] = i;
@@ -336,8 +359,8 @@ RadixSort& RadixSort::Sort(const udword* input, udword nb, RadixHint hint)
}
else
{
udword* Indices = mRanks;
udword* IndicesEnd = &mRanks[nb];
const udword* Indices = mRanks;
const udword* IndicesEnd = &mRanks[nb];
while(Indices!=IndicesEnd)
{
udword id = *Indices++;
@@ -347,7 +370,9 @@ RadixSort& RadixSort::Sort(const udword* input, udword nb, RadixHint hint)
}
// Swap pointers for next pass. Valid indices - the most recent ones - are in mRanks after the swap.
udword* Tmp = mRanks; mRanks = mRanks2; mRanks2 = Tmp;
udword* Tmp = mRanks;
mRanks = mRanks2;
mRanks2 = Tmp;
}
}
return *this;
@@ -371,7 +396,7 @@ RadixSort& RadixSort::Sort(const float* input2, udword nb)
// Stats
mTotalCalls++;
udword* input = (udword*)input2;
const udword* input = (const udword*)input2;
// Resize lists if needed
CheckResize(nb);
@@ -392,15 +417,16 @@ RadixSort& RadixSort::Sort(const float* input2, udword nb)
// generation Pentiums....We can't make comparison on integer representations because, as Chris said, it just
// wouldn't work with mixed positive/negative values....
{ CREATE_HISTOGRAMS(float, input2); }
/*
// Compute #negative values involved if needed
udword NbNegativeValues = 0;
// An efficient way to compute the number of negatives values we'll have to deal with is simply to sum the 128
// last values of the last histogram. Last histogram because that's the one for the Most Significant Byte,
// responsible for the sign. 128 last values because the 128 first ones are related to positive numbers.
// ### is that ok on Apple ?!
udword* h3= &mHistogram[768];
for(udword i=128;i<256;i++) NbNegativeValues += h3[i]; // 768 for last histogram, 128 for negative part
*/
// Radix sort, j is the pass number (0=LSB, 3=MSB)
for(udword j=0;j<4;j++)
{
@@ -419,8 +445,8 @@ RadixSort& RadixSort::Sort(const float* input2, udword nb)
for(udword i=1;i<256;i++) mLink[i] = mLink[i-1] + CurCount[i-1];
// Perform Radix Sort
ubyte* InputBytes = (ubyte*)input;
InputBytes += j;
const ubyte* InputBytes = (const ubyte*)input;
InputBytes += BYTES_INC;
if(INVALID_RANKS)
{
// for(i=0;i<nb;i++) mRanks2[mOffset[InputBytes[i<<2]]++] = i;
@@ -429,8 +455,8 @@ RadixSort& RadixSort::Sort(const float* input2, udword nb)
}
else
{
udword* Indices = mRanks;
udword* IndicesEnd = &mRanks[nb];
const udword* Indices = mRanks;
const udword* IndicesEnd = &mRanks[nb];
while(Indices!=IndicesEnd)
{
udword id = *Indices++;
@@ -440,7 +466,9 @@ RadixSort& RadixSort::Sort(const float* input2, udword nb)
}
// Swap pointers for next pass. Valid indices - the most recent ones - are in mRanks after the swap.
udword* Tmp = mRanks; mRanks = mRanks2; mRanks2 = Tmp;
udword* Tmp = mRanks;
mRanks = mRanks2;
mRanks2 = Tmp;
}
}
else
@@ -450,19 +478,32 @@ RadixSort& RadixSort::Sort(const float* input2, udword nb)
if(PerformPass)
{
/*
// Create biased offsets, in order for negative numbers to be sorted as well
// mOffset[0] = NbNegativeValues; // First positive number takes place after the negative ones
mLink[0] = &mRanks2[NbNegativeValues]; // First positive number takes place after the negative ones
// for(udword i=1;i<128;i++) mOffset[i] = mOffset[i-1] + CurCount[i-1]; // 1 to 128 for positive numbers
mLink[0] = &mRanks2[NbNegativeValues]; // First positive number takes place after the negative ones
for(udword i=1;i<128;i++) mLink[i] = mLink[i-1] + CurCount[i-1]; // 1 to 128 for positive numbers
// We must reverse the sorting order for negative numbers!
// mOffset[255] = 0;
mLink[255] = mRanks2;
// for(i=0;i<127;i++) mOffset[254-i] = mOffset[255-i] + CurCount[255-i]; // Fixing the wrong order for negative values
for(udword i=0;i<127;i++) mLink[254-i] = mLink[255-i] + CurCount[255-i]; // Fixing the wrong order for negative values
// for(i=128;i<256;i++) mOffset[i] += CurCount[i]; // Fixing the wrong place for negative values
mLink[255] = mRanks2;
for(udword i=0;i<127;i++) mLink[254-i] = mLink[255-i] + CurCount[255-i]; // Fixing the wrong order for negative values
for(udword i=128;i<256;i++) mLink[i] += CurCount[i]; // Fixing the wrong place for negative values
*/
// From Kyle Hubert:
//mOffset[255] = CurCount[255];
mLink[255] = mRanks2 + CurCount[255];
//for(udword i=254;i>127;i--) mOffset[i] = mOffset[i+1] + CurCount[i];
for(udword i=254;i>127;i--) mLink[i] = mLink[i+1] + CurCount[i];
//mOffset[0] = mOffset[128] + CurCount[128];
mLink[0] = mLink[128] + CurCount[128];
//for(udword i=1;i<128;i++) mOffset[i] = mOffset[i-1] + CurCount[i-1];
for(udword i=1;i<128;i++) mLink[i] = mLink[i-1] + CurCount[i-1];
// Perform Radix Sort
if(INVALID_RANKS)
@@ -491,7 +532,9 @@ RadixSort& RadixSort::Sort(const float* input2, udword nb)
}
}
// Swap pointers for next pass. Valid indices - the most recent ones - are in mRanks after the swap.
udword* Tmp = mRanks; mRanks = mRanks2; mRanks2 = Tmp;
udword* Tmp = mRanks;
mRanks = mRanks2;
mRanks2 = Tmp;
}
else
{
@@ -510,7 +553,9 @@ RadixSort& RadixSort::Sort(const float* input2, udword nb)
}
// Swap pointers for next pass. Valid indices - the most recent ones - are in mRanks after the swap.
udword* Tmp = mRanks; mRanks = mRanks2; mRanks2 = Tmp;
udword* Tmp = mRanks;
mRanks = mRanks2;
mRanks2 = Tmp;
}
}
}
@@ -534,3 +579,14 @@ udword RadixSort::GetUsedRam() const
UsedRam += 2*CURRENT_SIZE*sizeof(udword); // 2 lists of indices
return UsedRam;
}
bool RadixSort::SetRankBuffers(udword* ranks0, udword* ranks1)
{
if(!ranks0 || !ranks1) return false;
mRanks = ranks0;
mRanks2 = ranks1;
mDeleteRanks = false;
return true;
}

View File

@@ -1,19 +1,3 @@
/*
* ICE / OPCODE - Optimized Collision Detection
* http://www.codercorner.com/Opcode.htm
*
* Copyright (c) 2001-2008 Pierre Terdiman, pierre@codercorner.com
This software is provided 'as-is', without any express or implied warranty.
In no event will the authors be held liable for any damages arising from the use of this software.
Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it freely,
subject to the following restrictions:
1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required.
2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software.
3. This notice may not be removed or altered from any source distribution.
*/
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/**
* Contains source code from the article "Radix Sort Revisited".
@@ -25,8 +9,8 @@ subject to the following restrictions:
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Include Guard
#ifndef __ICERADIXSORT_H__
#define __ICERADIXSORT_H__
#ifndef ICERADIXSORT_H
#define ICERADIXSORT_H
//! Allocate histograms & offsets locally
#define RADIX_LOCAL_RAM
@@ -39,7 +23,7 @@ subject to the following restrictions:
RADIX_FORCE_DWORD = 0x7fffffff
};
class ICECORE_API RadixSort
class ICECORE_API RadixSort : public Allocateable
{
public:
// Constructor/Destructor
@@ -62,6 +46,9 @@ subject to the following restrictions:
//! Returns the number of eraly exits due to temporal coherence.
inline_ udword GetNbHits() const { return mNbHits; }
bool SetRankBuffers(udword* ranks0, udword* ranks1);
PREVENT_COPY(RadixSort)
private:
#ifndef RADIX_LOCAL_RAM
udword* mHistogram; //!< Counters for each byte
@@ -73,9 +60,15 @@ subject to the following restrictions:
// Stats
udword mTotalCalls; //!< Total number of calls to the sort routine
udword mNbHits; //!< Number of early exits due to coherence
// Stack-radix
bool mDeleteRanks; //!<
// Internal methods
void CheckResize(udword nb);
bool Resize(udword nb);
};
#endif // __ICERADIXSORT_H__
#define StackRadixSort(name, ranks0, ranks1) \
RadixSort name; \
name.SetRankBuffers(ranks0, ranks1);
#endif // ICERADIXSORT_H

View File

@@ -1,19 +1,3 @@
/*
* ICE / OPCODE - Optimized Collision Detection
* http://www.codercorner.com/Opcode.htm
*
* Copyright (c) 2001-2008 Pierre Terdiman, pierre@codercorner.com
This software is provided 'as-is', without any express or implied warranty.
In no event will the authors be held liable for any damages arising from the use of this software.
Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it freely,
subject to the following restrictions:
1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required.
2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software.
3. This notice may not be removed or altered from any source distribution.
*/
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/**
* Contains custom types.
@@ -25,13 +9,15 @@ subject to the following restrictions:
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Include Guard
#ifndef __ICETYPES_H__
#define __ICETYPES_H__
#ifndef ICETYPES_H
#define ICETYPES_H
#define USE_HANDLE_MANAGER
// Constants
#ifndef PI
#define PI 3.1415926535897932384626433832795028841971693993751f //!< PI
#endif
#define HALFPI 1.57079632679489661923f //!< 0.5 * PI
#define TWOPI 6.28318530717958647692f //!< 2.0 * PI
#define INVPI 0.31830988618379067154f //!< 1.0 / PI
@@ -65,10 +51,26 @@ subject to the following restrictions:
typedef unsigned short uword; //!< sizeof(uword) must be 2
typedef signed int sdword; //!< sizeof(sdword) must be 4
typedef unsigned int udword; //!< sizeof(udword) must be 4
#ifdef WIN32
typedef signed __int64 sqword; //!< sizeof(sqword) must be 8
typedef unsigned __int64 uqword; //!< sizeof(uqword) must be 8
#elif LINUX
typedef signed long long sqword; //!< sizeof(sqword) must be 8
typedef unsigned long long uqword; //!< sizeof(uqword) must be 8
#elif defined(__APPLE__)
typedef signed long long sqword; //!< sizeof(sqword) must be 8
typedef unsigned long long uqword; //!< sizeof(uqword) must be 8
#elif defined(_XBOX)
typedef signed __int64 sqword; //!< sizeof(sqword) must be 8
typedef unsigned __int64 uqword; //!< sizeof(uqword) must be 8
#endif
typedef float float32; //!< sizeof(float32) must be 4
typedef double float64; //!< sizeof(float64) must be 4
typedef double float64; //!< sizeof(float64) must be 8
typedef size_t regsize; //!< sizeof(regsize) must be sizeof(void*)
// For test purpose you can force one of those:
// typedef udword regsize;
// typedef uqword regsize;
ICE_COMPILE_TIME_ASSERT(sizeof(bool)==1); // ...otherwise things might fail with VC++ 4.2 !
ICE_COMPILE_TIME_ASSERT(sizeof(ubyte)==1);
@@ -79,6 +81,9 @@ subject to the following restrictions:
ICE_COMPILE_TIME_ASSERT(sizeof(sdword)==4);
ICE_COMPILE_TIME_ASSERT(sizeof(uqword)==8);
ICE_COMPILE_TIME_ASSERT(sizeof(sqword)==8);
ICE_COMPILE_TIME_ASSERT(sizeof(float32)==4);
ICE_COMPILE_TIME_ASSERT(sizeof(float64)==8);
ICE_COMPILE_TIME_ASSERT(sizeof(regsize)==sizeof(void*));
//! TO BE DOCUMENTED
#define DECLARE_ICE_HANDLE(name) struct name##__ { int unused; }; typedef struct name##__ *name
@@ -90,7 +95,6 @@ subject to the following restrictions:
#else
typedef uword KID; //!< Kernel ID
#endif
typedef udword RTYPE; //!< Relationship-type (!) between owners and references
#define INVALID_ID 0xffffffff //!< Invalid dword ID (counterpart of null pointers)
#ifdef USE_HANDLE_MANAGER
#define INVALID_KID 0xffffffff //!< Invalid Kernel ID
@@ -139,8 +143,6 @@ subject to the following restrictions:
#define ONE_OVER_RAND_MAX (1.0f / float(RAND_MAX)) //!< Inverse of the max possible value returned by rand()
typedef int (__stdcall* PROC)(); //!< A standard procedure call.
typedef bool (*ENUMERATION)(udword value, udword param, udword context); //!< ICE standard enumeration call
typedef void** VTABLE; //!< A V-Table.
#undef MIN
@@ -154,6 +156,7 @@ subject to the following restrictions:
template<class T> inline_ void TSetMin (T& a, const T& b) { if(a>b) a = b; }
template<class T> inline_ void TSetMax (T& a, const T& b) { if(a<b) a = b; }
/* Obsolete stuff - if needed, move to dedicated header and include in few files using this.
#define SQR(x) ((x)*(x)) //!< Returns x square
#define CUBE(x) ((x)*(x)*(x)) //!< Returns x cube
@@ -163,11 +166,16 @@ subject to the following restrictions:
#define QUADRAT(x) ((x)*(x)) //!< Returns x square
#ifdef _WIN32
# define srand48(x) srand((unsigned int) (x))
# define srandom(x) srand((unsigned int) (x))
# define random() ((double) rand())
# define drand48() ((double) (((double) rand()) / ((double) RAND_MAX)))
#endif
typedef int (__stdcall* PROC)(); //!< A standard procedure call.
typedef bool (*ENUMERATION)(udword value, udword param, udword context); //!< ICE standard enumeration call
#endif // __ICETYPES_H__
#ifdef _WIN32
// Used to compile legacy code
__forceinline void srand48(udword x) { srand(x); }
__forceinline void srandom(udword x) { srand(x); }
__forceinline double random() { return (double)rand(); }
__forceinline double drand48() { return (double) ( ((double)rand()) / ((double)RAND_MAX) ); }
#endif
*/
#endif // ICETYPES_H

View File

@@ -1,32 +1,16 @@
/*
* ICE / OPCODE - Optimized Collision Detection
* http://www.codercorner.com/Opcode.htm
*
* Copyright (c) 2001-2008 Pierre Terdiman, pierre@codercorner.com
This software is provided 'as-is', without any express or implied warranty.
In no event will the authors be held liable for any damages arising from the use of this software.
Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it freely,
subject to the following restrictions:
1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required.
2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software.
3. This notice may not be removed or altered from any source distribution.
*/
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/**
* Contains misc. useful macros & defines.
* \file IceUtils.h
* \author Pierre Terdiman (collected from various sources)
* \author Pierre Terdiman (personal code + collected from various sources)
* \date April, 4, 2000
*/
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Include Guard
#ifndef __ICEUTILS_H__
#define __ICEUTILS_H__
#ifndef ICEUTILS_H
#define ICEUTILS_H
#define START_RUNONCE { static bool __RunOnce__ = false; if(!__RunOnce__){
#define END_RUNONCE __RunOnce__ = true;}}
@@ -40,7 +24,7 @@ subject to the following restrictions:
n = ((n >> 4) & 0x0f0f0f0f) | ((n << 4) & 0xf0f0f0f0);
n = ((n >> 8) & 0x00ff00ff) | ((n << 8) & 0xff00ff00);
n = ((n >> 16) & 0x0000ffff) | ((n << 16) & 0xffff0000);
// Etc for larger intergers (64 bits in Java)
// Etc for larger integers (64 bits in Java)
// NOTE: the >> operation must be unsigned! (>>> in java)
}
@@ -48,15 +32,15 @@ subject to the following restrictions:
inline_ udword CountBits(udword n)
{
// This relies of the fact that the count of n bits can NOT overflow
// an n bit interger. EG: 1 bit count takes a 1 bit interger, 2 bit counts
// 2 bit interger, 3 bit count requires only a 2 bit interger.
// an n bit integer. EG: 1 bit count takes a 1 bit integer, 2 bit counts
// 2 bit integer, 3 bit count requires only a 2 bit integer.
// So we add all bit pairs, then each nible, then each byte etc...
n = (n & 0x55555555) + ((n & 0xaaaaaaaa) >> 1);
n = (n & 0x33333333) + ((n & 0xcccccccc) >> 2);
n = (n & 0x0f0f0f0f) + ((n & 0xf0f0f0f0) >> 4);
n = (n & 0x00ff00ff) + ((n & 0xff00ff00) >> 8);
n = (n & 0x0000ffff) + ((n & 0xffff0000) >> 16);
// Etc for larger intergers (64 bits in Java)
// Etc for larger integers (64 bits in Java)
// NOTE: the >> operation must be unsigned! (>>> in java)
return n;
}
@@ -70,9 +54,44 @@ subject to the following restrictions:
return (bits * 0x01010101) >> 24;
}
// "Population Count (Ones Count)
// The population count of a binary integer value x is the number of one bits in the value. Although many machines have
// single instructions for this, the single instructions are usually microcoded loops that test a bit per cycle; a log-time
// algorithm coded in C is often faster. The following code uses a variable-precision SWAR algorithm to perform a tree
// reduction adding the bits in a 32-bit value:"
inline_ udword ones32(udword x)
{
/* 32-bit recursive reduction using SWAR...
but first step is mapping 2-bit values
into sum of 2 1-bit values in sneaky way
*/
x -= ((x >> 1) & 0x55555555);
x = (((x >> 2) & 0x33333333) + (x & 0x33333333));
x = (((x >> 4) + x) & 0x0f0f0f0f);
x += (x >> 8);
x += (x >> 16);
return (x & 0x0000003f);
// "It is worthwhile noting that the SWAR population count algorithm given above can be improved upon for the case of
// counting the population of multi-word bit sets. How? The last few steps in the reduction are using only a portion
// of the SWAR width to produce their results; thus, it would be possible to combine these steps across multiple words
// being reduced. One additional note: the AMD Athlon optimization guidelines suggest a very similar algorithm that
// replaces the last three lines with return((x * 0x01010101) >> 24);. For the Athlon (which has a very fast integer
// multiply), I would have expected AMD's code to be faster... but it is actually 6% slower according to my benchmarks
// using a 1.2GHz Athlon (a Thunderbird). Why? Well, it so happens that GCC doesn't use a multiply instruction - it
// writes out the equivalent shift and add sequence!"
}
// "Trailing Zero Count
// Given the Least Significant 1 Bit and Population Count (Ones Count) algorithms, it is trivial to combine them to
// construct a trailing zero count (as pointed-out by Joe Bowbeer):"
inline_ udword tzc(sdword x)
{
return(ones32((x & -x) - 1));
}
//! Spread out bits. EG 00001111 -> 0101010101
//! 00001010 -> 0100010000
//! This is used to interleve to intergers to produce a `Morten Key'
//! This is used to interleave two integers to produce a `Morton Key'
//! used in Space Filling Curves (See DrDobbs Journal, July 1999)
//! Order is important.
inline_ void SpreadBits(udword& n)
@@ -84,12 +103,12 @@ subject to the following restrictions:
n = ( n & 0x11111111) | (( n & 0x22222222) << 1);
}
// Next Largest Power of 2
// "Next Largest Power of 2
// Given a binary integer value x, the next largest power of 2 can be computed by a SWAR algorithm
// that recursively "folds" the upper bits into the lower bits. This process yields a bit vector with
// the same most significant 1 as x, but all 1's below it. Adding 1 to that value yields the next
// largest power of 2. For a 32-bit value:
inline_ udword nlpo2(udword x)
// largest power of 2. For a 32-bit value:"
inline_ udword NextPowerOfTwo(udword x)
{
x |= (x >> 1);
x |= (x >> 2);
@@ -133,8 +152,29 @@ subject to the following restrictions:
//!< Alternative abs function
inline_ udword abs_(sdword x) { sdword y= x >> 31; return (x^y)-y; }
// "Integer Minimum or Maximum
// Given 2's complement integer values x and y, the minimum can be computed without any branches as
// x+(((y-x)>>(WORDBITS-1))&(y-x)).
// Logically, this works because the shift by (WORDBITS-1) replicates the sign bit to create a mask
// -- be aware, however, that the C language does not require that shifts are signed even if their
// operands are signed, so there is a potential portability problem. Additionally, one might think
// that a shift by any number greater than or equal to WORDBITS would have the same effect, but many
// instruction sets have shifts that behave strangely when such shift distances are specified.
// Of course, maximum can be computed using the same trick:
// x-(((x-y)>>(WORDBITS-1))&(x-y))."
//!< Alternative min function
inline_ sdword min_(sdword a, sdword b) { sdword delta = b-a; return a + (delta&(delta>>31)); }
//!< Alternative max function
inline_ sdword max_(sdword a, sdword b) { sdword delta = a-b; return a - (delta&(delta>>31)); }
// "Integer Selection
// A branchless, lookup-free, alternative to code like if (a<b) x=c; else x=d; is ((((a-b) >> (WORDBITS-1)) & (c^d)) ^ d).
// This code assumes that the shift is signed, which, of course, C does not promise."
inline_ sdword IntegerSelection(sdword a, sdword b, sdword c, sdword d)
{
return ((((a-b)>>31) & (c^d)) ^ d);
}
// Determine if one of the bytes in a 4 byte word is zero
inline_ BOOL HasNullByte(udword x) { return ((x + 0xfefefeff) & (~x) & 0x80808080); }
@@ -143,12 +183,12 @@ subject to the following restrictions:
inline_ udword LowestOneBit(udword w) { return ((w) & (~(w)+1)); }
// inline_ udword LowestOneBit_(udword w) { return ((w) & (-(w))); }
// Most Significant 1 Bit
// "Most Significant 1 Bit
// Given a binary integer value x, the most significant 1 bit (highest numbered element of a bit set)
// can be computed using a SWAR algorithm that recursively "folds" the upper bits into the lower bits.
// This process yields a bit vector with the same most significant 1 as x, but all 1's below it.
// Bitwise AND of the original value with the complement of the "folded" value shifted down by one
// yields the most significant bit. For a 32-bit value:
// yields the most significant bit. For a 32-bit value:"
inline_ udword msb32(udword x)
{
x |= (x >> 1);
@@ -159,6 +199,23 @@ subject to the following restrictions:
return (x & ~(x >> 1));
}
// "Gray Code Conversion
// A Gray code is any binary coding sequence in which only a single bit position changes as we move from one value to the next.
// There are many such codes, but the traditional one is computed such that the Kth Gray code is K^(K>>1).
//
// The well-known algorithm for conversion from Gray to binary is a linear sequence of XORs that makes it seem each bit must be
// dealt with separately. Fortunately, that is equivalent to a parallel prefix XOR that can be computed using SWAR techniques
// in log time. For 32-bit Gray code values produced as described above, the conversion from Gray code back to unsigned binary is:"
inline_ udword g2b(udword gray)
{
gray ^= (gray >> 16);
gray ^= (gray >> 8);
gray ^= (gray >> 4);
gray ^= (gray >> 2);
gray ^= (gray >> 1);
return gray;
}
/*
"Just call it repeatedly with various input values and always with the same variable as "memory".
The sharpness determines the degree of filtering, where 0 completely filters out the input, and 1
@@ -181,9 +238,9 @@ subject to the following restrictions:
return memory = val * sharpness + memory * (1.0f - sharpness);
}
//! If you can guarantee that your input domain (i.e. value of x) is slightly
//! "If you can guarantee that your input domain (i.e. value of x) is slightly
//! limited (abs(x) must be < ((1<<31u)-32767)), then you can use the
//! following code to clamp the resulting value into [-32768,+32767] range:
//! following code to clamp the resulting value into [-32768,+32767] range:"
inline_ int ClampToInt16(int x)
{
// ASSERT(abs(x) < (int)((1<<31u)-32767));
@@ -219,8 +276,12 @@ subject to the following restrictions:
//! TO BE DOCUMENTED
#define OFFSET_OF(Class, Member) (size_t)&(((Class*)0)->Member)
//! TO BE DOCUMENTED
#if !defined(_XBOX)
// Already defined on Xbox.
#define ARRAYSIZE(p) (sizeof(p)/sizeof(p[0]))
#endif
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/**
@@ -236,6 +297,10 @@ subject to the following restrictions:
#define IS_ALIGNED_4(x) ((x&3)==0)
#define IS_ALIGNED_8(x) ((x&7)==0)
// Updates a pointer with "stride" bytes
inline_ void UpdatePtr(void*& ptr, udword stride) { ptr = ((ubyte*)ptr) + stride; }
// From Jon Watte IIRC
inline_ void _prefetch(void const* ptr) { (void)*(char const volatile *)ptr; }
// Compute implicit coords from an index:
@@ -269,4 +334,44 @@ subject to the following restrictions:
Compute2DCoords(u, v, i - (w * nbu_nbv), nbu);
}
#endif // __ICEUTILS_H__
// Calling fsincos instead of fsin+fcos. Twice faster.
inline_ void FSinCos(float& c, float& s, float f)
{
float LocalCos, LocalSin;
float Local = f;
#ifdef WIN32
_asm fld Local
_asm fsincos
_asm fstp LocalCos
_asm fstp LocalSin
#elif LINUX
asm("fld Local\n\t"
"fsincos\n\t"
"fstp LocalCos\n\t"
"fstp LocalSin\n\t"
);
#endif
c = LocalCos;
s = LocalSin;
}
// Modulo3 macros. See http://www.codercorner.com/Modulo3.htm
#define GET_NEXT_INDICES(i, j, k) \
k = 0x01000201; \
k>>=(i<<3); \
j = k & 0xff; \
k>>=8; \
k&=0xff;
#define GET_NEXT_INDICES2(i, j, k) \
j = ( 9 >> (i<<1)) & 3; \
k = (18 >> (i<<1)) & 3;
// 0=>1, 1=>2, 2=>0
inline_ udword Modulo3(udword i)
{
ASSERT(i==0 || i==1 || i==2);
return (9 >> (i << 1)) & 3;
}
#endif // ICEUTILS_H

View File

@@ -0,0 +1,522 @@
/*
* ICE / OPCODE - Optimized Collision Detection
* http://www.codercorner.com/Opcode.htm
*
* Copyright (c) 2001-2008 Pierre Terdiman, pierre@codercorner.com
This software is provided 'as-is', without any express or implied warranty.
In no event will the authors be held liable for any damages arising from the use of this software.
Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it freely,
subject to the following restrictions:
1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required.
2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software.
3. This notice may not be removed or altered from any source distribution.
*/
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/**
* Contains AABB-related code. (axis-aligned bounding box)
* \file IceAABB.h
* \author Pierre Terdiman
* \date January, 13, 2000
*/
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Include Guard
#ifndef __ICEAABB_H__
#define __ICEAABB_H__
// Forward declarations
class Sphere;
//! Declarations of type-independent methods (most of them implemented in the .cpp)
#define AABB_COMMON_METHODS \
AABB& Add(const AABB& aabb); \
float MakeCube(AABB& cube) const; \
void MakeSphere(Sphere& sphere) const; \
const sbyte* ComputeOutline(const Point& local_eye, sdword& num) const; \
float ComputeBoxArea(const Point& eye, const Matrix4x4& mat, float width, float height, sdword& num) const; \
bool IsInside(const AABB& box) const; \
bool ComputePlanes(Plane* planes) const; \
bool ComputePoints(Point* pts) const; \
const Point* GetVertexNormals() const; \
const udword* GetEdges() const; \
const Point* GetEdgeNormals() const; \
inline_ BOOL ContainsPoint(const Point& p) const \
{ \
if(p.x > GetMax(0) || p.x < GetMin(0)) return FALSE; \
if(p.y > GetMax(1) || p.y < GetMin(1)) return FALSE; \
if(p.z > GetMax(2) || p.z < GetMin(2)) return FALSE; \
return TRUE; \
}
enum AABBType
{
AABB_RENDER = 0, //!< AABB used for rendering. Not visible == not rendered.
AABB_UPDATE = 1, //!< AABB used for dynamic updates. Not visible == not updated.
AABB_FORCE_DWORD = 0x7fffffff,
};
#ifdef USE_MINMAX
struct ICEMATHS_API ShadowAABB
{
Point mMin;
Point mMax;
};
class ICEMATHS_API AABB
{
public:
//! Constructor
inline_ AABB() {}
//! Destructor
inline_ ~AABB() {}
//! Type-independent methods
AABB_COMMON_METHODS;
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/**
* Setups an AABB from min & max vectors.
* \param min [in] the min point
* \param max [in] the max point
*/
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
void SetMinMax(const Point& min, const Point& max) { mMin = min; mMax = max; }
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/**
* Setups an AABB from center & extents vectors.
* \param c [in] the center point
* \param e [in] the extents vector
*/
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
void SetCenterExtents(const Point& c, const Point& e) { mMin = c - e; mMax = c + e; }
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/**
* Setups an empty AABB.
*/
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
void SetEmpty() { Point p(MIN_FLOAT, MIN_FLOAT, MIN_FLOAT); mMin = -p; mMax = p;}
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/**
* Setups a point AABB.
*/
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
void SetPoint(const Point& pt) { mMin = mMax = pt; }
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/**
* Gets the size of the AABB. The size is defined as the longest extent.
* \return the size of the AABB
*/
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
float GetSize() const { Point e; GetExtents(e); return e.Max(); }
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/**
* Extends the AABB.
* \param p [in] the next point
*/
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
void Extend(const Point& p)
{
if(p.x > mMax.x) mMax.x = p.x;
if(p.x < mMin.x) mMin.x = p.x;
if(p.y > mMax.y) mMax.y = p.y;
if(p.y < mMin.y) mMin.y = p.y;
if(p.z > mMax.z) mMax.z = p.z;
if(p.z < mMin.z) mMin.z = p.z;
}
// Data access
//! Get min point of the box
inline_ void GetMin(Point& min) const { min = mMin; }
//! Get max point of the box
inline_ void GetMax(Point& max) const { max = mMax; }
//! Get component of the box's min point along a given axis
inline_ float GetMin(udword axis) const { return mMin[axis]; }
//! Get component of the box's max point along a given axis
inline_ float GetMax(udword axis) const { return mMax[axis]; }
//! Get box center
inline_ void GetCenter(Point& center) const { center = (mMax + mMin)*0.5f; }
//! Get box extents
inline_ void GetExtents(Point& extents) const { extents = (mMax - mMin)*0.5f; }
//! Get component of the box's center along a given axis
inline_ float GetCenter(udword axis) const { return (mMax[axis] + mMin[axis])*0.5f; }
//! Get component of the box's extents along a given axis
inline_ float GetExtents(udword axis) const { return (mMax[axis] - mMin[axis])*0.5f; }
//! Get box diagonal
inline_ void GetDiagonal(Point& diagonal) const { diagonal = mMax - mMin; }
inline_ float GetWidth() const { return mMax.x - mMin.x; }
inline_ float GetHeight() const { return mMax.y - mMin.y; }
inline_ float GetDepth() const { return mMax.z - mMin.z; }
//! Volume
inline_ float GetVolume() const { return GetWidth() * GetHeight() * GetDepth(); }
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/**
* Computes the intersection between two AABBs.
* \param a [in] the other AABB
* \return true on intersection
*/
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
inline_ BOOL Intersect(const AABB& a) const
{
if(mMax.x < a.mMin.x
|| a.mMax.x < mMin.x
|| mMax.y < a.mMin.y
|| a.mMax.y < mMin.y
|| mMax.z < a.mMin.z
|| a.mMax.z < mMin.z) return FALSE;
return TRUE;
}
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/**
* Computes the 1D-intersection between two AABBs, on a given axis.
* \param a [in] the other AABB
* \param axis [in] the axis (0, 1, 2)
* \return true on intersection
*/
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
inline_ BOOL Intersect(const AABB& a, udword axis) const
{
if(mMax[axis] < a.mMin[axis] || a.mMax[axis] < mMin[axis]) return FALSE;
return TRUE;
}
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/**
* Recomputes the AABB after an arbitrary transform by a 4x4 matrix.
* Original code by Charles Bloom on the GD-Algorithm list. (I slightly modified it)
* \param mtx [in] the transform matrix
* \param aabb [out] the transformed AABB [can be *this]
*/
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
inline_ void Rotate(const Matrix4x4& mtx, AABB& aabb) const
{
// The three edges transformed: you can efficiently transform an X-only vector
// by just getting the "X" column of the matrix
Point vx,vy,vz;
mtx.GetRow(0, vx); vx *= (mMax.x - mMin.x);
mtx.GetRow(1, vy); vy *= (mMax.y - mMin.y);
mtx.GetRow(2, vz); vz *= (mMax.z - mMin.z);
// Transform the min point
aabb.mMin = aabb.mMax = mMin * mtx;
// Take the transformed min & axes and find new extents
// Using CPU code in the right place is faster...
if(IS_NEGATIVE_FLOAT(vx.x)) aabb.mMin.x += vx.x; else aabb.mMax.x += vx.x;
if(IS_NEGATIVE_FLOAT(vx.y)) aabb.mMin.y += vx.y; else aabb.mMax.y += vx.y;
if(IS_NEGATIVE_FLOAT(vx.z)) aabb.mMin.z += vx.z; else aabb.mMax.z += vx.z;
if(IS_NEGATIVE_FLOAT(vy.x)) aabb.mMin.x += vy.x; else aabb.mMax.x += vy.x;
if(IS_NEGATIVE_FLOAT(vy.y)) aabb.mMin.y += vy.y; else aabb.mMax.y += vy.y;
if(IS_NEGATIVE_FLOAT(vy.z)) aabb.mMin.z += vy.z; else aabb.mMax.z += vy.z;
if(IS_NEGATIVE_FLOAT(vz.x)) aabb.mMin.x += vz.x; else aabb.mMax.x += vz.x;
if(IS_NEGATIVE_FLOAT(vz.y)) aabb.mMin.y += vz.y; else aabb.mMax.y += vz.y;
if(IS_NEGATIVE_FLOAT(vz.z)) aabb.mMin.z += vz.z; else aabb.mMax.z += vz.z;
}
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/**
* Checks the AABB is valid.
* \return true if the box is valid
*/
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
inline_ BOOL IsValid() const
{
// Consistency condition for (Min, Max) boxes: min < max
if(mMin.x > mMax.x) return FALSE;
if(mMin.y > mMax.y) return FALSE;
if(mMin.z > mMax.z) return FALSE;
return TRUE;
}
//! Operator for AABB *= float. Scales the extents, keeps same center.
inline_ AABB& operator*=(float s)
{
Point Center; GetCenter(Center);
Point Extents; GetExtents(Extents);
SetCenterExtents(Center, Extents * s);
return *this;
}
//! Operator for AABB /= float. Scales the extents, keeps same center.
inline_ AABB& operator/=(float s)
{
Point Center; GetCenter(Center);
Point Extents; GetExtents(Extents);
SetCenterExtents(Center, Extents / s);
return *this;
}
//! Operator for AABB += Point. Translates the box.
inline_ AABB& operator+=(const Point& trans)
{
mMin+=trans;
mMax+=trans;
return *this;
}
private:
Point mMin; //!< Min point
Point mMax; //!< Max point
};
#else
class ICEMATHS_API AABB
{
public:
//! Constructor
inline_ AABB() {}
//! Destructor
inline_ ~AABB() {}
//! Type-independent methods
AABB_COMMON_METHODS;
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/**
* Setups an AABB from min & max vectors.
* \param min [in] the min point
* \param max [in] the max point
*/
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
void SetMinMax(const Point& min, const Point& max) { mCenter = (max + min)*0.5f; mExtents = (max - min)*0.5f; }
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/**
* Setups an AABB from center & extents vectors.
* \param c [in] the center point
* \param e [in] the extents vector
*/
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
void SetCenterExtents(const Point& c, const Point& e) { mCenter = c; mExtents = e; }
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/**
* Setups an empty AABB.
*/
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
void SetEmpty() { mCenter.Zero(); mExtents.Set(MIN_FLOAT, MIN_FLOAT, MIN_FLOAT);}
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/**
* Setups a point AABB.
*/
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
void SetPoint(const Point& pt) { mCenter = pt; mExtents.Zero(); }
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/**
* Gets the size of the AABB. The size is defined as the longest extent.
* \return the size of the AABB
*/
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
float GetSize() const { return mExtents.Max(); }
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/**
* Extends the AABB.
* \param p [in] the next point
*/
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
void Extend(const Point& p)
{
Point Max = mCenter + mExtents;
Point Min = mCenter - mExtents;
if(p.x > Max.x) Max.x = p.x;
if(p.x < Min.x) Min.x = p.x;
if(p.y > Max.y) Max.y = p.y;
if(p.y < Min.y) Min.y = p.y;
if(p.z > Max.z) Max.z = p.z;
if(p.z < Min.z) Min.z = p.z;
SetMinMax(Min, Max);
}
// Data access
//! Get min point of the box
inline_ void GetMin(Point& min) const { min = mCenter - mExtents; }
//! Get max point of the box
inline_ void GetMax(Point& max) const { max = mCenter + mExtents; }
//! Get component of the box's min point along a given axis
inline_ float GetMin(udword axis) const { return mCenter[axis] - mExtents[axis]; }
//! Get component of the box's max point along a given axis
inline_ float GetMax(udword axis) const { return mCenter[axis] + mExtents[axis]; }
//! Get box center
inline_ void GetCenter(Point& center) const { center = mCenter; }
//! Get box extents
inline_ void GetExtents(Point& extents) const { extents = mExtents; }
//! Get component of the box's center along a given axis
inline_ float GetCenter(udword axis) const { return mCenter[axis]; }
//! Get component of the box's extents along a given axis
inline_ float GetExtents(udword axis) const { return mExtents[axis]; }
//! Get box diagonal
inline_ void GetDiagonal(Point& diagonal) const { diagonal = mExtents * 2.0f; }
inline_ float GetWidth() const { return mExtents.x * 2.0f; }
inline_ float GetHeight() const { return mExtents.y * 2.0f; }
inline_ float GetDepth() const { return mExtents.z * 2.0f; }
//! Volume
inline_ float GetVolume() const { return mExtents.x * mExtents.y * mExtents.z * 8.0f; }
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/**
* Computes the intersection between two AABBs.
* \param a [in] the other AABB
* \return true on intersection
*/
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
inline_ BOOL Intersect(const AABB& a) const
{
float tx = mCenter.x - a.mCenter.x; float ex = a.mExtents.x + mExtents.x; if(AIR(tx) > IR(ex)) return FALSE;
float ty = mCenter.y - a.mCenter.y; float ey = a.mExtents.y + mExtents.y; if(AIR(ty) > IR(ey)) return FALSE;
float tz = mCenter.z - a.mCenter.z; float ez = a.mExtents.z + mExtents.z; if(AIR(tz) > IR(ez)) return FALSE;
return TRUE;
}
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/**
* The standard intersection method from Gamasutra. Just here to check its speed against the one above.
* \param a [in] the other AABB
* \return true on intersection
*/
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
inline_ bool GomezIntersect(const AABB& a)
{
Point T = mCenter - a.mCenter; // Vector from A to B
return ((fabsf(T.x) <= (a.mExtents.x + mExtents.x))
&& (fabsf(T.y) <= (a.mExtents.y + mExtents.y))
&& (fabsf(T.z) <= (a.mExtents.z + mExtents.z)));
}
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/**
* Computes the 1D-intersection between two AABBs, on a given axis.
* \param a [in] the other AABB
* \param axis [in] the axis (0, 1, 2)
* \return true on intersection
*/
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
inline_ BOOL Intersect(const AABB& a, udword axis) const
{
float t = mCenter[axis] - a.mCenter[axis];
float e = a.mExtents[axis] + mExtents[axis];
if(AIR(t) > IR(e)) return FALSE;
return TRUE;
}
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/**
* Recomputes the AABB after an arbitrary transform by a 4x4 matrix.
* \param mtx [in] the transform matrix
* \param aabb [out] the transformed AABB [can be *this]
*/
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
inline_ void Rotate(const Matrix4x4& mtx, AABB& aabb) const
{
// Compute new center
aabb.mCenter = mCenter * mtx;
// Compute new extents. FPU code & CPU code have been interleaved for improved performance.
Point Ex(mtx.m[0][0] * mExtents.x, mtx.m[0][1] * mExtents.x, mtx.m[0][2] * mExtents.x);
IR(Ex.x)&=0x7fffffff; IR(Ex.y)&=0x7fffffff; IR(Ex.z)&=0x7fffffff;
Point Ey(mtx.m[1][0] * mExtents.y, mtx.m[1][1] * mExtents.y, mtx.m[1][2] * mExtents.y);
IR(Ey.x)&=0x7fffffff; IR(Ey.y)&=0x7fffffff; IR(Ey.z)&=0x7fffffff;
Point Ez(mtx.m[2][0] * mExtents.z, mtx.m[2][1] * mExtents.z, mtx.m[2][2] * mExtents.z);
IR(Ez.x)&=0x7fffffff; IR(Ez.y)&=0x7fffffff; IR(Ez.z)&=0x7fffffff;
aabb.mExtents.x = Ex.x + Ey.x + Ez.x;
aabb.mExtents.y = Ex.y + Ey.y + Ez.y;
aabb.mExtents.z = Ex.z + Ey.z + Ez.z;
}
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/**
* Checks the AABB is valid.
* \return true if the box is valid
*/
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
inline_ BOOL IsValid() const
{
// Consistency condition for (Center, Extents) boxes: Extents >= 0
if(IS_NEGATIVE_FLOAT(mExtents.x)) return FALSE;
if(IS_NEGATIVE_FLOAT(mExtents.y)) return FALSE;
if(IS_NEGATIVE_FLOAT(mExtents.z)) return FALSE;
return TRUE;
}
//! Operator for AABB *= float. Scales the extents, keeps same center.
inline_ AABB& operator*=(float s) { mExtents*=s; return *this; }
//! Operator for AABB /= float. Scales the extents, keeps same center.
inline_ AABB& operator/=(float s) { mExtents/=s; return *this; }
//! Operator for AABB += Point. Translates the box.
inline_ AABB& operator+=(const Point& trans)
{
mCenter+=trans;
return *this;
}
private:
Point mCenter; //!< AABB Center
Point mExtents; //!< x, y and z extents
};
#endif
inline_ void ComputeMinMax(const Point& p, Point& min, Point& max)
{
if(p.x > max.x) max.x = p.x;
if(p.x < min.x) min.x = p.x;
if(p.y > max.y) max.y = p.y;
if(p.y < min.y) min.y = p.y;
if(p.z > max.z) max.z = p.z;
if(p.z < min.z) min.z = p.z;
}
inline_ void ComputeAABB(AABB& aabb, const Point* list, udword nb_pts)
{
if(list)
{
Point Maxi(MIN_FLOAT, MIN_FLOAT, MIN_FLOAT);
Point Mini(MAX_FLOAT, MAX_FLOAT, MAX_FLOAT);
while(nb_pts--)
{
// _prefetch(list+1); // off by one ?
ComputeMinMax(*list++, Mini, Maxi);
}
aabb.SetMinMax(Mini, Maxi);
}
}
#endif // __ICEAABB_H__

View File

@@ -0,0 +1,361 @@
/*
* ICE / OPCODE - Optimized Collision Detection
* http://www.codercorner.com/Opcode.htm
*
* Copyright (c) 2001-2008 Pierre Terdiman, pierre@codercorner.com
This software is provided 'as-is', without any express or implied warranty.
In no event will the authors be held liable for any damages arising from the use of this software.
Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it freely,
subject to the following restrictions:
1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required.
2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software.
3. This notice may not be removed or altered from any source distribution.
*/
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/**
* Contains a simple container class.
* \file IceContainer.cpp
* \author Pierre Terdiman
* \date February, 5, 2000
*/
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/**
* Contains a list of 32-bits values.
* Use this class when you need to store an unknown number of values. The list is automatically
* resized and can contains 32-bits entities (dwords or floats)
*
* \class Container
* \author Pierre Terdiman
* \version 1.0
* \date 08.15.98
*/
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Precompiled Header
#include "Stdafx.h"
using namespace IceCore;
// Static members
#ifdef CONTAINER_STATS
udword Container::mNbContainers = 0;
udword Container::mUsedRam = 0;
#endif
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/**
* Constructor. No entries allocated there.
*/
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
Container::Container() : mMaxNbEntries(0), mCurNbEntries(0), mEntries(null), mGrowthFactor(2.0f)
{
#ifdef CONTAINER_STATS
mNbContainers++;
mUsedRam+=sizeof(Container);
#endif
}
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/**
* Constructor. Also allocates a given number of entries.
*/
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
Container::Container(udword size, float growth_factor) : mMaxNbEntries(0), mCurNbEntries(0), mEntries(null), mGrowthFactor(growth_factor)
{
#ifdef CONTAINER_STATS
mNbContainers++;
mUsedRam+=sizeof(Container);
#endif
SetSize(size);
}
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/**
* Copy constructor.
*/
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
Container::Container(const Container& object) : mMaxNbEntries(0), mCurNbEntries(0), mEntries(null), mGrowthFactor(2.0f)
{
#ifdef CONTAINER_STATS
mNbContainers++;
mUsedRam+=sizeof(Container);
#endif
*this = object;
}
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/**
* Destructor. Frees everything and leaves.
*/
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
Container::~Container()
{
Empty();
#ifdef CONTAINER_STATS
mNbContainers--;
mUsedRam-=GetUsedRam();
#endif
}
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/**
* Clears the container. All stored values are deleted, and it frees used ram.
* \see Reset()
* \return Self-Reference
*/
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
Container& Container::Empty()
{
#ifdef CONTAINER_STATS
mUsedRam-=mMaxNbEntries*sizeof(udword);
#endif
DELETEARRAY(mEntries);
mCurNbEntries = mMaxNbEntries = 0;
return *this;
}
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/**
* Resizes the container.
* \param needed [in] assume the container can be added at least "needed" values
* \return true if success.
*/
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
bool Container::Resize(udword needed)
{
#ifdef CONTAINER_STATS
// Subtract previous amount of bytes
mUsedRam-=mMaxNbEntries*sizeof(udword);
#endif
// Get more entries
mMaxNbEntries = mMaxNbEntries ? udword(float(mMaxNbEntries)*mGrowthFactor) : 2; // Default nb Entries = 2
if(mMaxNbEntries<mCurNbEntries + needed) mMaxNbEntries = mCurNbEntries + needed;
// Get some bytes for new entries
udword* NewEntries = new udword[mMaxNbEntries];
CHECKALLOC(NewEntries);
#ifdef CONTAINER_STATS
// Add current amount of bytes
mUsedRam+=mMaxNbEntries*sizeof(udword);
#endif
// Copy old data if needed
if(mCurNbEntries) CopyMemory(NewEntries, mEntries, mCurNbEntries*sizeof(udword));
// Delete old data
DELETEARRAY(mEntries);
// Assign new pointer
mEntries = NewEntries;
return true;
}
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/**
* Sets the initial size of the container. If it already contains something, it's discarded.
* \param nb [in] Number of entries
* \return true if success
*/
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
bool Container::SetSize(udword nb)
{
// Make sure it's empty
Empty();
// Checkings
if(!nb) return false;
// Initialize for nb entries
mMaxNbEntries = nb;
// Get some bytes for new entries
mEntries = new udword[mMaxNbEntries];
CHECKALLOC(mEntries);
#ifdef CONTAINER_STATS
// Add current amount of bytes
mUsedRam+=mMaxNbEntries*sizeof(udword);
#endif
return true;
}
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/**
* Refits the container and get rid of unused bytes.
* \return true if success
*/
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
bool Container::Refit()
{
#ifdef CONTAINER_STATS
// Subtract previous amount of bytes
mUsedRam-=mMaxNbEntries*sizeof(udword);
#endif
// Get just enough entries
mMaxNbEntries = mCurNbEntries;
if(!mMaxNbEntries) return false;
// Get just enough bytes
udword* NewEntries = new udword[mMaxNbEntries];
CHECKALLOC(NewEntries);
#ifdef CONTAINER_STATS
// Add current amount of bytes
mUsedRam+=mMaxNbEntries*sizeof(udword);
#endif
// Copy old data
CopyMemory(NewEntries, mEntries, mCurNbEntries*sizeof(udword));
// Delete old data
DELETEARRAY(mEntries);
// Assign new pointer
mEntries = NewEntries;
return true;
}
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/**
* Checks whether the container already contains a given value.
* \param entry [in] the value to look for in the container
* \param location [out] a possible pointer to store the entry location
* \see Add(udword entry)
* \see Add(float entry)
* \see Empty()
* \return true if the value has been found in the container, else false.
*/
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
bool Container::Contains(udword entry, udword* location) const
{
// Look for the entry
for(udword i=0;i<mCurNbEntries;i++)
{
if(mEntries[i]==entry)
{
if(location) *location = i;
return true;
}
}
return false;
}
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/**
* Deletes an entry. If the container contains such an entry, it's removed.
* \param entry [in] the value to delete.
* \return true if the value has been found in the container, else false.
* \warning This method is arbitrary slow (O(n)) and should be used carefully. Insertion order is not preserved.
*/
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
bool Container::Delete(udword entry)
{
// Look for the entry
for(udword i=0;i<mCurNbEntries;i++)
{
if(mEntries[i]==entry)
{
// Entry has been found at index i. The strategy is to copy the last current entry at index i, and decrement the current number of entries.
DeleteIndex(i);
return true;
}
}
return false;
}
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/**
* Deletes an entry, preserving the insertion order. If the container contains such an entry, it's removed.
* \param entry [in] the value to delete.
* \return true if the value has been found in the container, else false.
* \warning This method is arbitrary slow (O(n)) and should be used carefully.
*/
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
bool Container::DeleteKeepingOrder(udword entry)
{
// Look for the entry
for(udword i=0;i<mCurNbEntries;i++)
{
if(mEntries[i]==entry)
{
// Entry has been found at index i.
// Shift entries to preserve order. You really should use a linked list instead.
mCurNbEntries--;
for(udword j=i;j<mCurNbEntries;j++)
{
mEntries[j] = mEntries[j+1];
}
return true;
}
}
return false;
}
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/**
* Gets the next entry, starting from input one.
* \param entry [in/out] On input, the entry to look for. On output, the next entry
* \param find_mode [in] wrap/clamp
* \return Self-Reference
*/
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
Container& Container::FindNext(udword& entry, FindMode find_mode)
{
udword Location;
if(Contains(entry, &Location))
{
Location++;
if(Location==mCurNbEntries) Location = find_mode==FIND_WRAP ? 0 : mCurNbEntries-1;
entry = mEntries[Location];
}
return *this;
}
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/**
* Gets the previous entry, starting from input one.
* \param entry [in/out] On input, the entry to look for. On output, the previous entry
* \param find_mode [in] wrap/clamp
* \return Self-Reference
*/
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
Container& Container::FindPrev(udword& entry, FindMode find_mode)
{
udword Location;
if(Contains(entry, &Location))
{
Location--;
if(Location==0xffffffff) Location = find_mode==FIND_WRAP ? mCurNbEntries-1 : 0;
entry = mEntries[Location];
}
return *this;
}
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/**
* Gets the ram used by the container.
* \return the ram used in bytes.
*/
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
udword Container::GetUsedRam() const
{
return sizeof(Container) + mMaxNbEntries * sizeof(udword);
}
void Container::operator=(const Container& object)
{
SetSize(object.GetNbEntries());
CopyMemory(mEntries, object.GetEntries(), mMaxNbEntries*sizeof(udword));
mCurNbEntries = mMaxNbEntries;
}

View File

@@ -0,0 +1,228 @@
/*
* ICE / OPCODE - Optimized Collision Detection
* http://www.codercorner.com/Opcode.htm
*
* Copyright (c) 2001-2008 Pierre Terdiman, pierre@codercorner.com
This software is provided 'as-is', without any express or implied warranty.
In no event will the authors be held liable for any damages arising from the use of this software.
Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it freely,
subject to the following restrictions:
1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required.
2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software.
3. This notice may not be removed or altered from any source distribution.
*/
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/**
* Contains a simple container class.
* \file IceContainer.h
* \author Pierre Terdiman
* \date February, 5, 2000
*/
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Include Guard
#ifndef __ICECONTAINER_H__
#define __ICECONTAINER_H__
#define CONTAINER_STATS
enum FindMode
{
FIND_CLAMP,
FIND_WRAP,
FIND_FORCE_DWORD = 0x7fffffff
};
class ICECORE_API Container
{
public:
// Constructor / Destructor
Container();
Container(const Container& object);
Container(udword size, float growth_factor);
~Container();
// Management
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/**
* A O(1) method to add a value in the container. The container is automatically resized if needed.
* The method is inline, not the resize. The call overhead happens on resizes only, which is not a problem since the resizing operation
* costs a lot more than the call overhead...
*
* \param entry [in] a udword to store in the container
* \see Add(float entry)
* \see Empty()
* \see Contains(udword entry)
* \return Self-Reference
*/
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
inline_ Container& Add(udword entry)
{
// Resize if needed
if(mCurNbEntries==mMaxNbEntries) Resize();
// Add new entry
mEntries[mCurNbEntries++] = entry;
return *this;
}
inline_ Container& Add(const udword* entries, udword nb)
{
// Resize if needed
if(mCurNbEntries+nb>mMaxNbEntries) Resize(nb);
// Add new entry
CopyMemory(&mEntries[mCurNbEntries], entries, nb*sizeof(udword));
mCurNbEntries+=nb;
return *this;
}
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/**
* A O(1) method to add a value in the container. The container is automatically resized if needed.
* The method is inline, not the resize. The call overhead happens on resizes only, which is not a problem since the resizing operation
* costs a lot more than the call overhead...
*
* \param entry [in] a float to store in the container
* \see Add(udword entry)
* \see Empty()
* \see Contains(udword entry)
* \return Self-Reference
*/
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
inline_ Container& Add(float entry)
{
// Resize if needed
if(mCurNbEntries==mMaxNbEntries) Resize();
// Add new entry
mEntries[mCurNbEntries++] = IR(entry);
return *this;
}
inline_ Container& Add(const float* entries, udword nb)
{
// Resize if needed
if(mCurNbEntries+nb>mMaxNbEntries) Resize(nb);
// Add new entry
CopyMemory(&mEntries[mCurNbEntries], entries, nb*sizeof(float));
mCurNbEntries+=nb;
return *this;
}
//! Add unique [slow]
inline_ Container& AddUnique(udword entry)
{
if(!Contains(entry)) Add(entry);
return *this;
}
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/**
* Clears the container. All stored values are deleted, and it frees used ram.
* \see Reset()
* \return Self-Reference
*/
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
Container& Empty();
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/**
* Resets the container. Stored values are discarded but the buffer is kept so that further calls don't need resizing again.
* That's a kind of temporal coherence.
* \see Empty()
*/
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
inline_ void Reset()
{
// Avoid the write if possible
// ### CMOV
if(mCurNbEntries) mCurNbEntries = 0;
}
// HANDLE WITH CARE
inline_ void ForceSize(udword size)
{
mCurNbEntries = size;
}
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/**
* Sets the initial size of the container. If it already contains something, it's discarded.
* \param nb [in] Number of entries
* \return true if success
*/
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
bool SetSize(udword nb);
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/**
* Refits the container and get rid of unused bytes.
* \return true if success
*/
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
bool Refit();
// Checks whether the container already contains a given value.
bool Contains(udword entry, udword* location=null) const;
// Deletes an entry - doesn't preserve insertion order.
bool Delete(udword entry);
// Deletes an entry - does preserve insertion order.
bool DeleteKeepingOrder(udword entry);
//! Deletes the very last entry.
inline_ void DeleteLastEntry() { if(mCurNbEntries) mCurNbEntries--; }
//! Deletes the entry whose index is given
inline_ void DeleteIndex(udword index) { mEntries[index] = mEntries[--mCurNbEntries]; }
// Helpers
Container& FindNext(udword& entry, FindMode find_mode=FIND_CLAMP);
Container& FindPrev(udword& entry, FindMode find_mode=FIND_CLAMP);
// Data access.
inline_ udword GetNbEntries() const { return mCurNbEntries; } //!< Returns the current number of entries.
inline_ udword GetEntry(udword i) const { return mEntries[i]; } //!< Returns ith entry
inline_ udword* GetEntries() const { return mEntries; } //!< Returns the list of entries.
inline_ udword GetFirst() const { return mEntries[0]; }
inline_ udword GetLast() const { return mEntries[mCurNbEntries-1]; }
// Growth control
inline_ float GetGrowthFactor() const { return mGrowthFactor; } //!< Returns the growth factor
inline_ void SetGrowthFactor(float growth) { mGrowthFactor = growth; } //!< Sets the growth factor
inline_ bool IsFull() const { return mCurNbEntries==mMaxNbEntries; } //!< Checks the container is full
inline_ BOOL IsNotEmpty() const { return mCurNbEntries; } //!< Checks the container is empty
//! Read-access as an array
inline_ udword operator[](udword i) const { ASSERT(i>=0 && i<mCurNbEntries); return mEntries[i]; }
//! Write-access as an array
inline_ udword& operator[](udword i) { ASSERT(i>=0 && i<mCurNbEntries); return mEntries[i]; }
// Stats
udword GetUsedRam() const;
//! Operator for "Container A = Container B"
void operator = (const Container& object);
#ifdef CONTAINER_STATS
inline_ udword GetNbContainers() const { return mNbContainers; }
inline_ udword GetTotalBytes() const { return mUsedRam; }
private:
static udword mNbContainers; //!< Number of containers around
static udword mUsedRam; //!< Amount of bytes used by containers in the system
#endif
private:
// Resizing
bool Resize(udword needed=1);
// Data
udword mMaxNbEntries; //!< Maximum possible number of entries
udword mCurNbEntries; //!< Current number of entries
udword* mEntries; //!< List of entries
float mGrowthFactor; //!< Resize: new number of entries = old number * mGrowthFactor
};
#endif // __ICECONTAINER_H__

View File

@@ -0,0 +1,333 @@
/*
* ICE / OPCODE - Optimized Collision Detection
* http://www.codercorner.com/Opcode.htm
*
* Copyright (c) 2001-2008 Pierre Terdiman, pierre@codercorner.com
This software is provided 'as-is', without any express or implied warranty.
In no event will the authors be held liable for any damages arising from the use of this software.
Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it freely,
subject to the following restrictions:
1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required.
2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software.
3. This notice may not be removed or altered from any source distribution.
*/
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/**
* Contains FPU related code.
* \file IceFPU.h
* \author Pierre Terdiman
* \date April, 4, 2000
*/
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Include Guard
#ifndef __ICEFPU_H__
#define __ICEFPU_H__
#define SIGN_BITMASK 0x80000000
//! Integer representation of a floating-point value.
#define IR(x) ((udword&)(x))
//! Signed integer representation of a floating-point value.
#define SIR(x) ((sdword&)(x))
//! Absolute integer representation of a floating-point value
#define AIR(x) (IR(x)&0x7fffffff)
//! Floating-point representation of an integer value.
#define FR(x) ((float&)(x))
//! Integer-based comparison of a floating point value.
//! Don't use it blindly, it can be faster or slower than the FPU comparison, depends on the context.
#define IS_NEGATIVE_FLOAT(x) (IR(x)&0x80000000)
//! Fast fabs for floating-point values. It just clears the sign bit.
//! Don't use it blindy, it can be faster or slower than the FPU comparison, depends on the context.
inline_ float FastFabs(float x)
{
udword FloatBits = IR(x)&0x7fffffff;
return FR(FloatBits);
}
//! Fast square root for floating-point values.
inline_ float FastSqrt(float square)
{
float retval;
__asm {
mov eax, square
sub eax, 0x3F800000
sar eax, 1
add eax, 0x3F800000
mov [retval], eax
}
return retval;
}
//! Saturates positive to zero.
inline_ float fsat(float f)
{
udword y = (udword&)f & ~((sdword&)f >>31);
return (float&)y;
}
//! Computes 1.0f / sqrtf(x).
inline_ float frsqrt(float f)
{
float x = f * 0.5f;
udword y = 0x5f3759df - ((udword&)f >> 1);
// Iteration...
(float&)y = (float&)y * ( 1.5f - ( x * (float&)y * (float&)y ) );
// Result
return (float&)y;
}
//! Computes 1.0f / sqrtf(x). Comes from NVIDIA.
inline_ float InvSqrt(const float& x)
{
udword tmp = (udword(IEEE_1_0 << 1) + IEEE_1_0 - *(udword*)&x) >> 1;
float y = *(float*)&tmp;
return y * (1.47f - 0.47f * x * y * y);
}
//! Computes 1.0f / sqrtf(x). Comes from Quake3. Looks like the first one I had above.
//! See http://www.magic-software.com/3DGEDInvSqrt.html
inline_ float RSqrt(float number)
{
long i;
float x2, y;
const float threehalfs = 1.5f;
x2 = number * 0.5f;
y = number;
i = * (long *) &y;
i = 0x5f3759df - (i >> 1);
y = * (float *) &i;
y = y * (threehalfs - (x2 * y * y));
return y;
}
//! TO BE DOCUMENTED
inline_ float fsqrt(float f)
{
udword y = ( ( (sdword&)f - 0x3f800000 ) >> 1 ) + 0x3f800000;
// Iteration...?
// (float&)y = (3.0f - ((float&)y * (float&)y) / f) * (float&)y * 0.5f;
// Result
return (float&)y;
}
//! Returns the float ranged espilon value.
inline_ float fepsilon(float f)
{
udword b = (udword&)f & 0xff800000;
udword a = b | 0x00000001;
(float&)a -= (float&)b;
// Result
return (float&)a;
}
//! Is the float valid ?
inline_ bool IsNAN(float value) { return (IR(value)&0x7f800000) == 0x7f800000; }
inline_ bool IsIndeterminate(float value) { return IR(value) == 0xffc00000; }
inline_ bool IsPlusInf(float value) { return IR(value) == 0x7f800000; }
inline_ bool IsMinusInf(float value) { return IR(value) == 0xff800000; }
inline_ bool IsValidFloat(float value)
{
if(IsNAN(value)) return false;
if(IsIndeterminate(value)) return false;
if(IsPlusInf(value)) return false;
if(IsMinusInf(value)) return false;
return true;
}
#define CHECK_VALID_FLOAT(x) ASSERT(IsValidFloat(x));
/*
//! FPU precision setting function.
inline_ void SetFPU()
{
// This function evaluates whether the floating-point
// control word is set to single precision/round to nearest/
// exceptions disabled. If these conditions don't hold, the
// function changes the control word to set them and returns
// TRUE, putting the old control word value in the passback
// location pointed to by pwOldCW.
{
uword wTemp, wSave;
__asm fstcw wSave
if (wSave & 0x300 || // Not single mode
0x3f != (wSave & 0x3f) || // Exceptions enabled
wSave & 0xC00) // Not round to nearest mode
{
__asm
{
mov ax, wSave
and ax, not 300h ;; single mode
or ax, 3fh ;; disable all exceptions
and ax, not 0xC00 ;; round to nearest mode
mov wTemp, ax
fldcw wTemp
}
}
}
}
*/
//! This function computes the slowest possible floating-point value (you can also directly use FLT_EPSILON)
inline_ float ComputeFloatEpsilon()
{
float f = 1.0f;
((udword&)f)^=1;
return f - 1.0f; // You can check it's the same as FLT_EPSILON
}
inline_ bool IsFloatZero(float x, float epsilon=1e-6f)
{
return x*x < epsilon;
}
#define FCOMI_ST0 _asm _emit 0xdb _asm _emit 0xf0
#define FCOMIP_ST0 _asm _emit 0xdf _asm _emit 0xf0
#define FCMOVB_ST0 _asm _emit 0xda _asm _emit 0xc0
#define FCMOVNB_ST0 _asm _emit 0xdb _asm _emit 0xc0
#define FCOMI_ST1 _asm _emit 0xdb _asm _emit 0xf1
#define FCOMIP_ST1 _asm _emit 0xdf _asm _emit 0xf1
#define FCMOVB_ST1 _asm _emit 0xda _asm _emit 0xc1
#define FCMOVNB_ST1 _asm _emit 0xdb _asm _emit 0xc1
#define FCOMI_ST2 _asm _emit 0xdb _asm _emit 0xf2
#define FCOMIP_ST2 _asm _emit 0xdf _asm _emit 0xf2
#define FCMOVB_ST2 _asm _emit 0xda _asm _emit 0xc2
#define FCMOVNB_ST2 _asm _emit 0xdb _asm _emit 0xc2
#define FCOMI_ST3 _asm _emit 0xdb _asm _emit 0xf3
#define FCOMIP_ST3 _asm _emit 0xdf _asm _emit 0xf3
#define FCMOVB_ST3 _asm _emit 0xda _asm _emit 0xc3
#define FCMOVNB_ST3 _asm _emit 0xdb _asm _emit 0xc3
#define FCOMI_ST4 _asm _emit 0xdb _asm _emit 0xf4
#define FCOMIP_ST4 _asm _emit 0xdf _asm _emit 0xf4
#define FCMOVB_ST4 _asm _emit 0xda _asm _emit 0xc4
#define FCMOVNB_ST4 _asm _emit 0xdb _asm _emit 0xc4
#define FCOMI_ST5 _asm _emit 0xdb _asm _emit 0xf5
#define FCOMIP_ST5 _asm _emit 0xdf _asm _emit 0xf5
#define FCMOVB_ST5 _asm _emit 0xda _asm _emit 0xc5
#define FCMOVNB_ST5 _asm _emit 0xdb _asm _emit 0xc5
#define FCOMI_ST6 _asm _emit 0xdb _asm _emit 0xf6
#define FCOMIP_ST6 _asm _emit 0xdf _asm _emit 0xf6
#define FCMOVB_ST6 _asm _emit 0xda _asm _emit 0xc6
#define FCMOVNB_ST6 _asm _emit 0xdb _asm _emit 0xc6
#define FCOMI_ST7 _asm _emit 0xdb _asm _emit 0xf7
#define FCOMIP_ST7 _asm _emit 0xdf _asm _emit 0xf7
#define FCMOVB_ST7 _asm _emit 0xda _asm _emit 0xc7
#define FCMOVNB_ST7 _asm _emit 0xdb _asm _emit 0xc7
//! A global function to find MAX(a,b) using FCOMI/FCMOV
inline_ float FCMax2(float a, float b)
{
float Res;
_asm fld [a]
_asm fld [b]
FCOMI_ST1
FCMOVB_ST1
_asm fstp [Res]
_asm fcomp
return Res;
}
//! A global function to find MIN(a,b) using FCOMI/FCMOV
inline_ float FCMin2(float a, float b)
{
float Res;
_asm fld [a]
_asm fld [b]
FCOMI_ST1
FCMOVNB_ST1
_asm fstp [Res]
_asm fcomp
return Res;
}
//! A global function to find MAX(a,b,c) using FCOMI/FCMOV
inline_ float FCMax3(float a, float b, float c)
{
float Res;
_asm fld [a]
_asm fld [b]
_asm fld [c]
FCOMI_ST1
FCMOVB_ST1
FCOMI_ST2
FCMOVB_ST2
_asm fstp [Res]
_asm fcompp
return Res;
}
//! A global function to find MIN(a,b,c) using FCOMI/FCMOV
inline_ float FCMin3(float a, float b, float c)
{
float Res;
_asm fld [a]
_asm fld [b]
_asm fld [c]
FCOMI_ST1
FCMOVNB_ST1
FCOMI_ST2
FCMOVNB_ST2
_asm fstp [Res]
_asm fcompp
return Res;
}
inline_ int ConvertToSortable(float f)
{
int& Fi = (int&)f;
int Fmask = (Fi>>31);
Fi ^= Fmask;
Fmask &= ~(1<<31);
Fi -= Fmask;
return Fi;
}
enum FPUMode
{
FPU_FLOOR = 0,
FPU_CEIL = 1,
FPU_BEST = 2,
FPU_FORCE_DWORD = 0x7fffffff
};
FUNCTION ICECORE_API FPUMode GetFPUMode();
FUNCTION ICECORE_API void SaveFPU();
FUNCTION ICECORE_API void RestoreFPU();
FUNCTION ICECORE_API void SetFPUFloorMode();
FUNCTION ICECORE_API void SetFPUCeilMode();
FUNCTION ICECORE_API void SetFPUBestMode();
FUNCTION ICECORE_API void SetFPUPrecision24();
FUNCTION ICECORE_API void SetFPUPrecision53();
FUNCTION ICECORE_API void SetFPUPrecision64();
FUNCTION ICECORE_API void SetFPURoundingChop();
FUNCTION ICECORE_API void SetFPURoundingUp();
FUNCTION ICECORE_API void SetFPURoundingDown();
FUNCTION ICECORE_API void SetFPURoundingNear();
FUNCTION ICECORE_API int intChop(const float& f);
FUNCTION ICECORE_API int intFloor(const float& f);
FUNCTION ICECORE_API int intCeil(const float& f);
#endif // __ICEFPU_H__

View File

@@ -0,0 +1,121 @@
/*
* ICE / OPCODE - Optimized Collision Detection
* http://www.codercorner.com/Opcode.htm
*
* Copyright (c) 2001-2008 Pierre Terdiman, pierre@codercorner.com
This software is provided 'as-is', without any express or implied warranty.
In no event will the authors be held liable for any damages arising from the use of this software.
Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it freely,
subject to the following restrictions:
1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required.
2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software.
3. This notice may not be removed or altered from any source distribution.
*/
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/**
* Contains all memory macros.
* \file IceMemoryMacros.h
* \author Pierre Terdiman
* \date April, 4, 2000
*/
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Include Guard
#ifndef __ICEMEMORYMACROS_H__
#define __ICEMEMORYMACROS_H__
#undef ZeroMemory
#undef CopyMemory
#undef MoveMemory
#undef FillMemory
//! Clears a buffer.
//! \param addr [in] buffer address
//! \param size [in] buffer length
//! \see FillMemory
//! \see StoreDwords
//! \see CopyMemory
//! \see MoveMemory
inline_ void ZeroMemory(void* addr, udword size) { memset(addr, 0, size); }
//! Fills a buffer with a given byte.
//! \param addr [in] buffer address
//! \param size [in] buffer length
//! \param val [in] the byte value
//! \see StoreDwords
//! \see ZeroMemory
//! \see CopyMemory
//! \see MoveMemory
inline_ void FillMemory(void* dest, udword size, ubyte val) { memset(dest, val, size); }
//! Fills a buffer with a given dword.
//! \param addr [in] buffer address
//! \param nb [in] number of dwords to write
//! \param value [in] the dword value
//! \see FillMemory
//! \see ZeroMemory
//! \see CopyMemory
//! \see MoveMemory
//! \warning writes nb*4 bytes !
inline_ void StoreDwords(udword* dest, udword nb, udword value)
{
// The asm code below **SHOULD** be equivalent to one of those C versions
// or the other if your compiled is good: (checked on VC++ 6.0)
//
// 1) while(nb--) *dest++ = value;
//
// 2) for(udword i=0;i<nb;i++) dest[i] = value;
//
_asm push eax
_asm push ecx
_asm push edi
_asm mov edi, dest
_asm mov ecx, nb
_asm mov eax, value
_asm rep stosd
_asm pop edi
_asm pop ecx
_asm pop eax
}
//! Copies a buffer.
//! \param addr [in] destination buffer address
//! \param addr [in] source buffer address
//! \param size [in] buffer length
//! \see ZeroMemory
//! \see FillMemory
//! \see StoreDwords
//! \see MoveMemory
inline_ void CopyMemory(void* dest, const void* src, udword size) { memcpy(dest, src, size); }
//! Moves a buffer.
//! \param addr [in] destination buffer address
//! \param addr [in] source buffer address
//! \param size [in] buffer length
//! \see ZeroMemory
//! \see FillMemory
//! \see StoreDwords
//! \see CopyMemory
inline_ void MoveMemory(void* dest, const void* src, udword size) { memmove(dest, src, size); }
#define SIZEOFOBJECT sizeof(*this) //!< Gives the size of current object. Avoid some mistakes (e.g. "sizeof(this)").
//#define CLEAROBJECT { memset(this, 0, SIZEOFOBJECT); } //!< Clears current object. Laziness is my business. HANDLE WITH CARE.
#define DELETESINGLE(x) if (x) { delete x; x = null; } //!< Deletes an instance of a class.
#define DELETEARRAY(x) if (x) { delete []x; x = null; } //!< Deletes an array.
#define SAFE_RELEASE(x) if (x) { (x)->Release(); (x) = null; } //!< Safe D3D-style release
#define SAFE_DESTRUCT(x) if (x) { (x)->SelfDestruct(); (x) = null; } //!< Safe ICE-style release
#ifdef __ICEERROR_H__
#define CHECKALLOC(x) if(!x) return SetIceError("Out of memory.", EC_OUT_OF_MEMORY); //!< Standard alloc checking. HANDLE WITH CARE.
#else
#define CHECKALLOC(x) if(!x) return false;
#endif
//! Standard allocation cycle
#define SAFE_ALLOC(ptr, type, count) DELETEARRAY(ptr); ptr = new type[count]; CHECKALLOC(ptr);
#endif // __ICEMEMORYMACROS_H__

View File

@@ -0,0 +1,144 @@
/*
* ICE / OPCODE - Optimized Collision Detection
* http://www.codercorner.com/Opcode.htm
*
* Copyright (c) 2001-2008 Pierre Terdiman, pierre@codercorner.com
This software is provided 'as-is', without any express or implied warranty.
In no event will the authors be held liable for any damages arising from the use of this software.
Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it freely,
subject to the following restrictions:
1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required.
2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software.
3. This notice may not be removed or altered from any source distribution.
*/
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/**
* Contains preprocessor stuff. This should be the first included header.
* \file IcePreprocessor.h
* \author Pierre Terdiman
* \date April, 4, 2000
*/
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Include Guard
#ifndef __ICEPREPROCESSOR_H__
#define __ICEPREPROCESSOR_H__
// Check platform
#if defined( _WIN32 ) || defined( WIN32 )
#pragma message("Compiling on Windows...")
#define PLATFORM_WINDOWS
#else
#pragma message("Compiling on unknown platform...")
#endif
// Check compiler
#if defined(_MSC_VER)
#pragma message("Compiling with VC++...")
#define COMPILER_VISUAL_CPP
#else
#pragma message("Compiling with unknown compiler...")
#endif
// Check compiler options. If this file is included in user-apps, this
// shouldn't be needed, so that they can use what they like best.
#ifndef ICE_DONT_CHECK_COMPILER_OPTIONS
#ifdef COMPILER_VISUAL_CPP
#if defined(_CHAR_UNSIGNED)
#endif
#if defined(_CPPRTTI)
#error Please disable RTTI...
#endif
#if defined(_CPPUNWIND)
#error Please disable exceptions...
#endif
#if defined(_MT)
// Multithreading
#endif
#endif
#endif
// Check debug mode
#ifdef DEBUG // May be defined instead of _DEBUG. Let's fix it.
#ifndef _DEBUG
#define _DEBUG
#endif
#endif
#ifdef _DEBUG
// Here you may define items for debug builds
#endif
#ifndef THIS_FILE
#define THIS_FILE __FILE__
#endif
#ifndef ICE_NO_DLL
#ifdef ICECORE_EXPORTS
#define ICECORE_API __declspec(dllexport)
#else
#define ICECORE_API __declspec(dllimport)
#endif
#else
#define ICECORE_API
#endif
// Don't override new/delete
// #define DEFAULT_NEWDELETE
#define DONT_TRACK_MEMORY_LEAKS
#define FUNCTION extern "C"
// Cosmetic stuff [mainly useful with multiple inheritance]
#define override(base_class) virtual
// Our own inline keyword, so that:
// - we can switch to __forceinline to check it's really better or not
// - we can remove __forceinline if the compiler doesn't support it
// #define inline_ __forceinline
// #define inline_ inline
// Contributed by Bruce Mitchener
#if defined(COMPILER_VISUAL_CPP)
#define inline_ __forceinline
// #define inline_ inline
#elif defined(__GNUC__) && __GNUC__ < 3
#define inline_ inline
#elif defined(__GNUC__)
#define inline_ inline __attribute__ ((always_inline))
#else
#define inline_ inline
#endif
// Down the hatch
#pragma inline_depth( 255 )
#ifdef COMPILER_VISUAL_CPP
#pragma intrinsic(memcmp)
#pragma intrinsic(memcpy)
#pragma intrinsic(memset)
#pragma intrinsic(strcat)
#pragma intrinsic(strcmp)
#pragma intrinsic(strcpy)
#pragma intrinsic(strlen)
#pragma intrinsic(abs)
#pragma intrinsic(labs)
#endif
// ANSI compliance
#ifdef _DEBUG
// Remove painful warning in debug
inline_ bool __False__(){ return false; }
#define for if(__False__()){} else for
#else
#define for if(0){} else for
#endif
#endif // __ICEPREPROCESSOR_H__

View File

@@ -0,0 +1,536 @@
/*
* ICE / OPCODE - Optimized Collision Detection
* http://www.codercorner.com/Opcode.htm
*
* Copyright (c) 2001-2008 Pierre Terdiman, pierre@codercorner.com
This software is provided 'as-is', without any express or implied warranty.
In no event will the authors be held liable for any damages arising from the use of this software.
Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it freely,
subject to the following restrictions:
1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required.
2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software.
3. This notice may not be removed or altered from any source distribution.
*/
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/**
* Contains source code from the article "Radix Sort Revisited".
* \file IceRevisitedRadix.cpp
* \author Pierre Terdiman
* \date April, 4, 2000
*/
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/**
* Revisited Radix Sort.
* This is my new radix routine:
* - it uses indices and doesn't recopy the values anymore, hence wasting less ram
* - it creates all the histograms in one run instead of four
* - it sorts words faster than dwords and bytes faster than words
* - it correctly sorts negative floating-point values by patching the offsets
* - it automatically takes advantage of temporal coherence
* - multiple keys support is a side effect of temporal coherence
* - it may be worth recoding in asm... (mainly to use FCOMI, FCMOV, etc) [it's probably memory-bound anyway]
*
* History:
* - 08.15.98: very first version
* - 04.04.00: recoded for the radix article
* - 12.xx.00: code lifting
* - 09.18.01: faster CHECK_PASS_VALIDITY thanks to Mark D. Shattuck (who provided other tips, not included here)
* - 10.11.01: added local ram support
* - 01.20.02: bugfix! In very particular cases the last pass was skipped in the float code-path, leading to incorrect sorting......
* - 01.02.02: - "mIndices" renamed => "mRanks". That's a rank sorter after all.
* - ranks are not "reset" anymore, but implicit on first calls
* - 07.05.02: - offsets rewritten with one less indirection.
* - 11.03.02: - "bool" replaced with RadixHint enum
*
* \class RadixSort
* \author Pierre Terdiman
* \version 1.4
* \date August, 15, 1998
*/
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/*
To do:
- add an offset parameter between two input values (avoid some data recopy sometimes)
- unroll ? asm ?
- 11 bits trick & 3 passes as Michael did
- prefetch stuff the day I have a P3
- make a version with 16-bits indices ?
*/
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Precompiled Header
#include "Stdafx.h"
using namespace IceCore;
#define INVALIDATE_RANKS mCurrentSize|=0x80000000
#define VALIDATE_RANKS mCurrentSize&=0x7fffffff
#define CURRENT_SIZE (mCurrentSize&0x7fffffff)
#define INVALID_RANKS (mCurrentSize&0x80000000)
#define CHECK_RESIZE(n) \
if(n!=mPreviousSize) \
{ \
if(n>mCurrentSize) Resize(n); \
else ResetRanks(); \
mPreviousSize = n; \
}
#define CREATE_HISTOGRAMS(type, buffer) \
/* Clear counters/histograms */ \
ZeroMemory(mHistogram, 256*4*sizeof(udword)); \
\
/* Prepare to count */ \
ubyte* p = (ubyte*)input; \
ubyte* pe = &p[nb*4]; \
udword* h0= &mHistogram[0]; /* Histogram for first pass (LSB) */ \
udword* h1= &mHistogram[256]; /* Histogram for second pass */ \
udword* h2= &mHistogram[512]; /* Histogram for third pass */ \
udword* h3= &mHistogram[768]; /* Histogram for last pass (MSB) */ \
\
bool AlreadySorted = true; /* Optimism... */ \
\
if(INVALID_RANKS) \
{ \
/* Prepare for temporal coherence */ \
type* Running = (type*)buffer; \
type PrevVal = *Running; \
\
while(p!=pe) \
{ \
/* Read input buffer in previous sorted order */ \
type Val = *Running++; \
/* Check whether already sorted or not */ \
if(Val<PrevVal) { AlreadySorted = false; break; } /* Early out */ \
/* Update for next iteration */ \
PrevVal = Val; \
\
/* Create histograms */ \
h0[*p++]++; h1[*p++]++; h2[*p++]++; h3[*p++]++; \
} \
\
/* If all input values are already sorted, we just have to return and leave the */ \
/* previous list unchanged. That way the routine may take advantage of temporal */ \
/* coherence, for example when used to sort transparent faces. */ \
if(AlreadySorted) \
{ \
mNbHits++; \
for(udword i=0;i<nb;i++) mRanks[i] = i; \
return *this; \
} \
} \
else \
{ \
/* Prepare for temporal coherence */ \
udword* Indices = mRanks; \
type PrevVal = (type)buffer[*Indices]; \
\
while(p!=pe) \
{ \
/* Read input buffer in previous sorted order */ \
type Val = (type)buffer[*Indices++]; \
/* Check whether already sorted or not */ \
if(Val<PrevVal) { AlreadySorted = false; break; } /* Early out */ \
/* Update for next iteration */ \
PrevVal = Val; \
\
/* Create histograms */ \
h0[*p++]++; h1[*p++]++; h2[*p++]++; h3[*p++]++; \
} \
\
/* If all input values are already sorted, we just have to return and leave the */ \
/* previous list unchanged. That way the routine may take advantage of temporal */ \
/* coherence, for example when used to sort transparent faces. */ \
if(AlreadySorted) { mNbHits++; return *this; } \
} \
\
/* Else there has been an early out and we must finish computing the histograms */ \
while(p!=pe) \
{ \
/* Create histograms without the previous overhead */ \
h0[*p++]++; h1[*p++]++; h2[*p++]++; h3[*p++]++; \
}
#define CHECK_PASS_VALIDITY(pass) \
/* Shortcut to current counters */ \
udword* CurCount = &mHistogram[pass<<8]; \
\
/* Reset flag. The sorting pass is supposed to be performed. (default) */ \
bool PerformPass = true; \
\
/* Check pass validity */ \
\
/* If all values have the same byte, sorting is useless. */ \
/* It may happen when sorting bytes or words instead of dwords. */ \
/* This routine actually sorts words faster than dwords, and bytes */ \
/* faster than words. Standard running time (O(4*n))is reduced to O(2*n) */ \
/* for words and O(n) for bytes. Running time for floats depends on actual values... */ \
\
/* Get first byte */ \
ubyte UniqueVal = *(((ubyte*)input)+pass); \
\
/* Check that byte's counter */ \
if(CurCount[UniqueVal]==nb) PerformPass=false;
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/**
* Constructor.
*/
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
RadixSort::RadixSort() : mRanks(null), mRanks2(null), mCurrentSize(0), mTotalCalls(0), mNbHits(0)
{
#ifndef RADIX_LOCAL_RAM
// Allocate input-independent ram
mHistogram = new udword[256*4];
mOffset = new udword[256];
#endif
// Initialize indices
INVALIDATE_RANKS;
}
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/**
* Destructor.
*/
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
RadixSort::~RadixSort()
{
// Release everything
#ifndef RADIX_LOCAL_RAM
DELETEARRAY(mOffset);
DELETEARRAY(mHistogram);
#endif
DELETEARRAY(mRanks2);
DELETEARRAY(mRanks);
}
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/**
* Resizes the inner lists.
* \param nb [in] new size (number of dwords)
* \return true if success
*/
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
bool RadixSort::Resize(udword nb)
{
// Free previously used ram
DELETEARRAY(mRanks2);
DELETEARRAY(mRanks);
// Get some fresh one
mRanks = new udword[nb]; CHECKALLOC(mRanks);
mRanks2 = new udword[nb]; CHECKALLOC(mRanks2);
return true;
}
inline_ void RadixSort::CheckResize(udword nb)
{
udword CurSize = CURRENT_SIZE;
if(nb!=CurSize)
{
if(nb>CurSize) Resize(nb);
mCurrentSize = nb;
INVALIDATE_RANKS;
}
}
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/**
* Main sort routine.
* This one is for integer values. After the call, mRanks contains a list of indices in sorted order, i.e. in the order you may process your data.
* \param input [in] a list of integer values to sort
* \param nb [in] number of values to sort, must be < 2^31
* \param hint [in] RADIX_SIGNED to handle negative values, RADIX_UNSIGNED if you know your input buffer only contains positive values
* \return Self-Reference
*/
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
RadixSort& RadixSort::Sort(const udword* input, udword nb, RadixHint hint)
{
// Checkings
if(!input || !nb || nb&0x80000000) return *this;
// Stats
mTotalCalls++;
// Resize lists if needed
CheckResize(nb);
#ifdef RADIX_LOCAL_RAM
// Allocate histograms & offsets on the stack
udword mHistogram[256*4];
// udword mOffset[256];
udword* mLink[256];
#endif
// Create histograms (counters). Counters for all passes are created in one run.
// Pros: read input buffer once instead of four times
// Cons: mHistogram is 4Kb instead of 1Kb
// We must take care of signed/unsigned values for temporal coherence.... I just
// have 2 code paths even if just a single opcode changes. Self-modifying code, someone?
if(hint==RADIX_UNSIGNED) { CREATE_HISTOGRAMS(udword, input); }
else { CREATE_HISTOGRAMS(sdword, input); }
// Compute #negative values involved if needed
udword NbNegativeValues = 0;
if(hint==RADIX_SIGNED)
{
// An efficient way to compute the number of negatives values we'll have to deal with is simply to sum the 128
// last values of the last histogram. Last histogram because that's the one for the Most Significant Byte,
// responsible for the sign. 128 last values because the 128 first ones are related to positive numbers.
udword* h3= &mHistogram[768];
for(udword i=128;i<256;i++) NbNegativeValues += h3[i]; // 768 for last histogram, 128 for negative part
}
// Radix sort, j is the pass number (0=LSB, 3=MSB)
for(udword j=0;j<4;j++)
{
CHECK_PASS_VALIDITY(j);
// Sometimes the fourth (negative) pass is skipped because all numbers are negative and the MSB is 0xFF (for example). This is
// not a problem, numbers are correctly sorted anyway.
if(PerformPass)
{
// Should we care about negative values?
if(j!=3 || hint==RADIX_UNSIGNED)
{
// Here we deal with positive values only
// Create offsets
// mOffset[0] = 0;
// for(udword i=1;i<256;i++) mOffset[i] = mOffset[i-1] + CurCount[i-1];
mLink[0] = mRanks2;
for(udword i=1;i<256;i++) mLink[i] = mLink[i-1] + CurCount[i-1];
}
else
{
// This is a special case to correctly handle negative integers. They're sorted in the right order but at the wrong place.
// Create biased offsets, in order for negative numbers to be sorted as well
// mOffset[0] = NbNegativeValues; // First positive number takes place after the negative ones
mLink[0] = &mRanks2[NbNegativeValues]; // First positive number takes place after the negative ones
// for(udword i=1;i<128;i++) mOffset[i] = mOffset[i-1] + CurCount[i-1]; // 1 to 128 for positive numbers
for(udword i=1;i<128;i++) mLink[i] = mLink[i-1] + CurCount[i-1]; // 1 to 128 for positive numbers
// Fixing the wrong place for negative values
// mOffset[128] = 0;
mLink[128] = mRanks2;
// for(i=129;i<256;i++) mOffset[i] = mOffset[i-1] + CurCount[i-1];
for(udword i=129;i<256;i++) mLink[i] = mLink[i-1] + CurCount[i-1];
}
// Perform Radix Sort
ubyte* InputBytes = (ubyte*)input;
InputBytes += j;
if(INVALID_RANKS)
{
// for(udword i=0;i<nb;i++) mRanks2[mOffset[InputBytes[i<<2]]++] = i;
for(udword i=0;i<nb;i++) *mLink[InputBytes[i<<2]]++ = i;
VALIDATE_RANKS;
}
else
{
udword* Indices = mRanks;
udword* IndicesEnd = &mRanks[nb];
while(Indices!=IndicesEnd)
{
udword id = *Indices++;
// mRanks2[mOffset[InputBytes[id<<2]]++] = id;
*mLink[InputBytes[id<<2]]++ = id;
}
}
// Swap pointers for next pass. Valid indices - the most recent ones - are in mRanks after the swap.
udword* Tmp = mRanks; mRanks = mRanks2; mRanks2 = Tmp;
}
}
return *this;
}
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/**
* Main sort routine.
* This one is for floating-point values. After the call, mRanks contains a list of indices in sorted order, i.e. in the order you may process your data.
* \param input [in] a list of floating-point values to sort
* \param nb [in] number of values to sort, must be < 2^31
* \return Self-Reference
* \warning only sorts IEEE floating-point values
*/
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
RadixSort& RadixSort::Sort(const float* input2, udword nb)
{
// Checkings
if(!input2 || !nb || nb&0x80000000) return *this;
// Stats
mTotalCalls++;
udword* input = (udword*)input2;
// Resize lists if needed
CheckResize(nb);
#ifdef RADIX_LOCAL_RAM
// Allocate histograms & offsets on the stack
udword mHistogram[256*4];
// udword mOffset[256];
udword* mLink[256];
#endif
// Create histograms (counters). Counters for all passes are created in one run.
// Pros: read input buffer once instead of four times
// Cons: mHistogram is 4Kb instead of 1Kb
// Floating-point values are always supposed to be signed values, so there's only one code path there.
// Please note the floating point comparison needed for temporal coherence! Although the resulting asm code
// is dreadful, this is surprisingly not such a performance hit - well, I suppose that's a big one on first
// generation Pentiums....We can't make comparison on integer representations because, as Chris said, it just
// wouldn't work with mixed positive/negative values....
{ CREATE_HISTOGRAMS(float, input2); }
// Compute #negative values involved if needed
udword NbNegativeValues = 0;
// An efficient way to compute the number of negatives values we'll have to deal with is simply to sum the 128
// last values of the last histogram. Last histogram because that's the one for the Most Significant Byte,
// responsible for the sign. 128 last values because the 128 first ones are related to positive numbers.
udword* h3= &mHistogram[768];
for(udword i=128;i<256;i++) NbNegativeValues += h3[i]; // 768 for last histogram, 128 for negative part
// Radix sort, j is the pass number (0=LSB, 3=MSB)
for(udword j=0;j<4;j++)
{
// Should we care about negative values?
if(j!=3)
{
// Here we deal with positive values only
CHECK_PASS_VALIDITY(j);
if(PerformPass)
{
// Create offsets
// mOffset[0] = 0;
mLink[0] = mRanks2;
// for(udword i=1;i<256;i++) mOffset[i] = mOffset[i-1] + CurCount[i-1];
for(udword i=1;i<256;i++) mLink[i] = mLink[i-1] + CurCount[i-1];
// Perform Radix Sort
ubyte* InputBytes = (ubyte*)input;
InputBytes += j;
if(INVALID_RANKS)
{
// for(i=0;i<nb;i++) mRanks2[mOffset[InputBytes[i<<2]]++] = i;
for(udword i=0;i<nb;i++) *mLink[InputBytes[i<<2]]++ = i;
VALIDATE_RANKS;
}
else
{
udword* Indices = mRanks;
udword* IndicesEnd = &mRanks[nb];
while(Indices!=IndicesEnd)
{
udword id = *Indices++;
// mRanks2[mOffset[InputBytes[id<<2]]++] = id;
*mLink[InputBytes[id<<2]]++ = id;
}
}
// Swap pointers for next pass. Valid indices - the most recent ones - are in mRanks after the swap.
udword* Tmp = mRanks; mRanks = mRanks2; mRanks2 = Tmp;
}
}
else
{
// This is a special case to correctly handle negative values
CHECK_PASS_VALIDITY(j);
if(PerformPass)
{
// Create biased offsets, in order for negative numbers to be sorted as well
// mOffset[0] = NbNegativeValues; // First positive number takes place after the negative ones
mLink[0] = &mRanks2[NbNegativeValues]; // First positive number takes place after the negative ones
// for(udword i=1;i<128;i++) mOffset[i] = mOffset[i-1] + CurCount[i-1]; // 1 to 128 for positive numbers
for(udword i=1;i<128;i++) mLink[i] = mLink[i-1] + CurCount[i-1]; // 1 to 128 for positive numbers
// We must reverse the sorting order for negative numbers!
// mOffset[255] = 0;
mLink[255] = mRanks2;
// for(i=0;i<127;i++) mOffset[254-i] = mOffset[255-i] + CurCount[255-i]; // Fixing the wrong order for negative values
for(udword i=0;i<127;i++) mLink[254-i] = mLink[255-i] + CurCount[255-i]; // Fixing the wrong order for negative values
// for(i=128;i<256;i++) mOffset[i] += CurCount[i]; // Fixing the wrong place for negative values
for(udword i=128;i<256;i++) mLink[i] += CurCount[i]; // Fixing the wrong place for negative values
// Perform Radix Sort
if(INVALID_RANKS)
{
for(udword i=0;i<nb;i++)
{
udword Radix = input[i]>>24; // Radix byte, same as above. AND is useless here (udword).
// ### cmp to be killed. Not good. Later.
// if(Radix<128) mRanks2[mOffset[Radix]++] = i; // Number is positive, same as above
// else mRanks2[--mOffset[Radix]] = i; // Number is negative, flip the sorting order
if(Radix<128) *mLink[Radix]++ = i; // Number is positive, same as above
else *(--mLink[Radix]) = i; // Number is negative, flip the sorting order
}
VALIDATE_RANKS;
}
else
{
for(udword i=0;i<nb;i++)
{
udword Radix = input[mRanks[i]]>>24; // Radix byte, same as above. AND is useless here (udword).
// ### cmp to be killed. Not good. Later.
// if(Radix<128) mRanks2[mOffset[Radix]++] = mRanks[i]; // Number is positive, same as above
// else mRanks2[--mOffset[Radix]] = mRanks[i]; // Number is negative, flip the sorting order
if(Radix<128) *mLink[Radix]++ = mRanks[i]; // Number is positive, same as above
else *(--mLink[Radix]) = mRanks[i]; // Number is negative, flip the sorting order
}
}
// Swap pointers for next pass. Valid indices - the most recent ones - are in mRanks after the swap.
udword* Tmp = mRanks; mRanks = mRanks2; mRanks2 = Tmp;
}
else
{
// The pass is useless, yet we still have to reverse the order of current list if all values are negative.
if(UniqueVal>=128)
{
if(INVALID_RANKS)
{
// ###Possible?
for(udword i=0;i<nb;i++) mRanks2[i] = nb-i-1;
VALIDATE_RANKS;
}
else
{
for(udword i=0;i<nb;i++) mRanks2[i] = mRanks[nb-i-1];
}
// Swap pointers for next pass. Valid indices - the most recent ones - are in mRanks after the swap.
udword* Tmp = mRanks; mRanks = mRanks2; mRanks2 = Tmp;
}
}
}
}
return *this;
}
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/**
* Gets the ram used.
* \return memory used in bytes
*/
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
udword RadixSort::GetUsedRam() const
{
udword UsedRam = sizeof(RadixSort);
#ifndef RADIX_LOCAL_RAM
UsedRam += 256*4*sizeof(udword); // Histograms
UsedRam += 256*sizeof(udword); // Offsets
#endif
UsedRam += 2*CURRENT_SIZE*sizeof(udword); // 2 lists of indices
return UsedRam;
}

View File

@@ -0,0 +1,81 @@
/*
* ICE / OPCODE - Optimized Collision Detection
* http://www.codercorner.com/Opcode.htm
*
* Copyright (c) 2001-2008 Pierre Terdiman, pierre@codercorner.com
This software is provided 'as-is', without any express or implied warranty.
In no event will the authors be held liable for any damages arising from the use of this software.
Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it freely,
subject to the following restrictions:
1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required.
2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software.
3. This notice may not be removed or altered from any source distribution.
*/
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/**
* Contains source code from the article "Radix Sort Revisited".
* \file IceRevisitedRadix.h
* \author Pierre Terdiman
* \date April, 4, 2000
*/
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Include Guard
#ifndef __ICERADIXSORT_H__
#define __ICERADIXSORT_H__
//! Allocate histograms & offsets locally
#define RADIX_LOCAL_RAM
enum RadixHint
{
RADIX_SIGNED, //!< Input values are signed
RADIX_UNSIGNED, //!< Input values are unsigned
RADIX_FORCE_DWORD = 0x7fffffff
};
class ICECORE_API RadixSort
{
public:
// Constructor/Destructor
RadixSort();
~RadixSort();
// Sorting methods
RadixSort& Sort(const udword* input, udword nb, RadixHint hint=RADIX_SIGNED);
RadixSort& Sort(const float* input, udword nb);
//! Access to results. mRanks is a list of indices in sorted order, i.e. in the order you may further process your data
inline_ const udword* GetRanks() const { return mRanks; }
//! mIndices2 gets trashed on calling the sort routine, but otherwise you can recycle it the way you want.
inline_ udword* GetRecyclable() const { return mRanks2; }
// Stats
udword GetUsedRam() const;
//! Returns the total number of calls to the radix sorter.
inline_ udword GetNbTotalCalls() const { return mTotalCalls; }
//! Returns the number of eraly exits due to temporal coherence.
inline_ udword GetNbHits() const { return mNbHits; }
private:
#ifndef RADIX_LOCAL_RAM
udword* mHistogram; //!< Counters for each byte
udword* mOffset; //!< Offsets (nearly a cumulative distribution function)
#endif
udword mCurrentSize; //!< Current size of the indices list
udword* mRanks; //!< Two lists, swapped each pass
udword* mRanks2;
// Stats
udword mTotalCalls; //!< Total number of calls to the sort routine
udword mNbHits; //!< Number of early exits due to coherence
// Internal methods
void CheckResize(udword nb);
bool Resize(udword nb);
};
#endif // __ICERADIXSORT_H__

View File

@@ -0,0 +1,173 @@
/*
* ICE / OPCODE - Optimized Collision Detection
* http://www.codercorner.com/Opcode.htm
*
* Copyright (c) 2001-2008 Pierre Terdiman, pierre@codercorner.com
This software is provided 'as-is', without any express or implied warranty.
In no event will the authors be held liable for any damages arising from the use of this software.
Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it freely,
subject to the following restrictions:
1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required.
2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software.
3. This notice may not be removed or altered from any source distribution.
*/
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/**
* Contains custom types.
* \file IceTypes.h
* \author Pierre Terdiman
* \date April, 4, 2000
*/
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Include Guard
#ifndef __ICETYPES_H__
#define __ICETYPES_H__
#define USE_HANDLE_MANAGER
// Constants
#define PI 3.1415926535897932384626433832795028841971693993751f //!< PI
#define HALFPI 1.57079632679489661923f //!< 0.5 * PI
#define TWOPI 6.28318530717958647692f //!< 2.0 * PI
#define INVPI 0.31830988618379067154f //!< 1.0 / PI
#define RADTODEG 57.2957795130823208768f //!< 180.0 / PI, convert radians to degrees
#define DEGTORAD 0.01745329251994329577f //!< PI / 180.0, convert degrees to radians
#define EXP 2.71828182845904523536f //!< e
#define INVLOG2 3.32192809488736234787f //!< 1.0 / log10(2)
#define LN2 0.693147180559945f //!< ln(2)
#define INVLN2 1.44269504089f //!< 1.0f / ln(2)
#define INV3 0.33333333333333333333f //!< 1/3
#define INV6 0.16666666666666666666f //!< 1/6
#define INV7 0.14285714285714285714f //!< 1/7
#define INV9 0.11111111111111111111f //!< 1/9
#define INV255 0.00392156862745098039f //!< 1/255
#define SQRT2 1.41421356237f //!< sqrt(2)
#define INVSQRT2 0.707106781188f //!< 1 / sqrt(2)
#define SQRT3 1.73205080757f //!< sqrt(3)
#define INVSQRT3 0.577350269189f //!< 1 / sqrt(3)
#define null 0 //!< our own NULL pointer
// Custom types used in ICE
typedef signed char sbyte; //!< sizeof(sbyte) must be 1
typedef unsigned char ubyte; //!< sizeof(ubyte) must be 1
typedef signed short sword; //!< sizeof(sword) must be 2
typedef unsigned short uword; //!< sizeof(uword) must be 2
typedef signed int sdword; //!< sizeof(sdword) must be 4
typedef unsigned int udword; //!< sizeof(udword) must be 4
typedef signed __int64 sqword; //!< sizeof(sqword) must be 8
typedef unsigned __int64 uqword; //!< sizeof(uqword) must be 8
typedef float float32; //!< sizeof(float32) must be 4
typedef double float64; //!< sizeof(float64) must be 4
ICE_COMPILE_TIME_ASSERT(sizeof(bool)==1); // ...otherwise things might fail with VC++ 4.2 !
ICE_COMPILE_TIME_ASSERT(sizeof(ubyte)==1);
ICE_COMPILE_TIME_ASSERT(sizeof(sbyte)==1);
ICE_COMPILE_TIME_ASSERT(sizeof(sword)==2);
ICE_COMPILE_TIME_ASSERT(sizeof(uword)==2);
ICE_COMPILE_TIME_ASSERT(sizeof(udword)==4);
ICE_COMPILE_TIME_ASSERT(sizeof(sdword)==4);
ICE_COMPILE_TIME_ASSERT(sizeof(uqword)==8);
ICE_COMPILE_TIME_ASSERT(sizeof(sqword)==8);
//! TO BE DOCUMENTED
#define DECLARE_ICE_HANDLE(name) struct name##__ { int unused; }; typedef struct name##__ *name
typedef udword DynID; //!< Dynamic identifier
#ifdef USE_HANDLE_MANAGER
typedef udword KID; //!< Kernel ID
// DECLARE_ICE_HANDLE(KID);
#else
typedef uword KID; //!< Kernel ID
#endif
typedef udword RTYPE; //!< Relationship-type (!) between owners and references
#define INVALID_ID 0xffffffff //!< Invalid dword ID (counterpart of null pointers)
#ifdef USE_HANDLE_MANAGER
#define INVALID_KID 0xffffffff //!< Invalid Kernel ID
#else
#define INVALID_KID 0xffff //!< Invalid Kernel ID
#endif
#define INVALID_NUMBER 0xDEADBEEF //!< Standard junk value
// Define BOOL if needed
#ifndef BOOL
typedef int BOOL; //!< Another boolean type.
#endif
//! Union of a float and a sdword
typedef union {
float f; //!< The float
sdword d; //!< The integer
}scell;
//! Union of a float and a udword
typedef union {
float f; //!< The float
udword d; //!< The integer
}ucell;
// Type ranges
#define MAX_SBYTE 0x7f //!< max possible sbyte value
#define MIN_SBYTE 0x80 //!< min possible sbyte value
#define MAX_UBYTE 0xff //!< max possible ubyte value
#define MIN_UBYTE 0x00 //!< min possible ubyte value
#define MAX_SWORD 0x7fff //!< max possible sword value
#define MIN_SWORD 0x8000 //!< min possible sword value
#define MAX_UWORD 0xffff //!< max possible uword value
#define MIN_UWORD 0x0000 //!< min possible uword value
#define MAX_SDWORD 0x7fffffff //!< max possible sdword value
#define MIN_SDWORD 0x80000000 //!< min possible sdword value
#define MAX_UDWORD 0xffffffff //!< max possible udword value
#define MIN_UDWORD 0x00000000 //!< min possible udword value
#define MAX_FLOAT FLT_MAX //!< max possible float value
#define MIN_FLOAT (-FLT_MAX) //!< min possible loat value
#define IEEE_1_0 0x3f800000 //!< integer representation of 1.0
#define IEEE_255_0 0x437f0000 //!< integer representation of 255.0
#define IEEE_MAX_FLOAT 0x7f7fffff //!< integer representation of MAX_FLOAT
#define IEEE_MIN_FLOAT 0xff7fffff //!< integer representation of MIN_FLOAT
#define IEEE_UNDERFLOW_LIMIT 0x1a000000
#define ONE_OVER_RAND_MAX (1.0f / float(RAND_MAX)) //!< Inverse of the max possible value returned by rand()
typedef int (__stdcall* PROC)(); //!< A standard procedure call.
typedef bool (*ENUMERATION)(udword value, udword param, udword context); //!< ICE standard enumeration call
typedef void** VTABLE; //!< A V-Table.
#undef MIN
#undef MAX
#define MIN(a, b) ((a) < (b) ? (a) : (b)) //!< Returns the min value between a and b
#define MAX(a, b) ((a) > (b) ? (a) : (b)) //!< Returns the max value between a and b
#define MAXMAX(a,b,c) ((a) > (b) ? MAX (a,c) : MAX (b,c)) //!< Returns the max value between a, b and c
template<class T> inline_ const T& TMin (const T& a, const T& b) { return b < a ? b : a; }
template<class T> inline_ const T& TMax (const T& a, const T& b) { return a < b ? b : a; }
template<class T> inline_ void TSetMin (T& a, const T& b) { if(a>b) a = b; }
template<class T> inline_ void TSetMax (T& a, const T& b) { if(a<b) a = b; }
#define SQR(x) ((x)*(x)) //!< Returns x square
#define CUBE(x) ((x)*(x)*(x)) //!< Returns x cube
#define AND & //!< ...
#define OR | //!< ...
#define XOR ^ //!< ...
#define QUADRAT(x) ((x)*(x)) //!< Returns x square
#ifdef _WIN32
# define srand48(x) srand((unsigned int) (x))
# define srandom(x) srand((unsigned int) (x))
# define random() ((double) rand())
# define drand48() ((double) (((double) rand()) / ((double) RAND_MAX)))
#endif
#endif // __ICETYPES_H__

View File

@@ -0,0 +1,272 @@
/*
* ICE / OPCODE - Optimized Collision Detection
* http://www.codercorner.com/Opcode.htm
*
* Copyright (c) 2001-2008 Pierre Terdiman, pierre@codercorner.com
This software is provided 'as-is', without any express or implied warranty.
In no event will the authors be held liable for any damages arising from the use of this software.
Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it freely,
subject to the following restrictions:
1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required.
2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software.
3. This notice may not be removed or altered from any source distribution.
*/
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/**
* Contains misc. useful macros & defines.
* \file IceUtils.h
* \author Pierre Terdiman (collected from various sources)
* \date April, 4, 2000
*/
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Include Guard
#ifndef __ICEUTILS_H__
#define __ICEUTILS_H__
#define START_RUNONCE { static bool __RunOnce__ = false; if(!__RunOnce__){
#define END_RUNONCE __RunOnce__ = true;}}
//! Reverse all the bits in a 32 bit word (from Steve Baker's Cute Code Collection)
//! (each line can be done in any order.
inline_ void ReverseBits(udword& n)
{
n = ((n >> 1) & 0x55555555) | ((n << 1) & 0xaaaaaaaa);
n = ((n >> 2) & 0x33333333) | ((n << 2) & 0xcccccccc);
n = ((n >> 4) & 0x0f0f0f0f) | ((n << 4) & 0xf0f0f0f0);
n = ((n >> 8) & 0x00ff00ff) | ((n << 8) & 0xff00ff00);
n = ((n >> 16) & 0x0000ffff) | ((n << 16) & 0xffff0000);
// Etc for larger intergers (64 bits in Java)
// NOTE: the >> operation must be unsigned! (>>> in java)
}
//! Count the number of '1' bits in a 32 bit word (from Steve Baker's Cute Code Collection)
inline_ udword CountBits(udword n)
{
// This relies of the fact that the count of n bits can NOT overflow
// an n bit interger. EG: 1 bit count takes a 1 bit interger, 2 bit counts
// 2 bit interger, 3 bit count requires only a 2 bit interger.
// So we add all bit pairs, then each nible, then each byte etc...
n = (n & 0x55555555) + ((n & 0xaaaaaaaa) >> 1);
n = (n & 0x33333333) + ((n & 0xcccccccc) >> 2);
n = (n & 0x0f0f0f0f) + ((n & 0xf0f0f0f0) >> 4);
n = (n & 0x00ff00ff) + ((n & 0xff00ff00) >> 8);
n = (n & 0x0000ffff) + ((n & 0xffff0000) >> 16);
// Etc for larger intergers (64 bits in Java)
// NOTE: the >> operation must be unsigned! (>>> in java)
return n;
}
//! Even faster?
inline_ udword CountBits2(udword bits)
{
bits = bits - ((bits >> 1) & 0x55555555);
bits = ((bits >> 2) & 0x33333333) + (bits & 0x33333333);
bits = ((bits >> 4) + bits) & 0x0F0F0F0F;
return (bits * 0x01010101) >> 24;
}
//! Spread out bits. EG 00001111 -> 0101010101
//! 00001010 -> 0100010000
//! This is used to interleve to intergers to produce a `Morten Key'
//! used in Space Filling Curves (See DrDobbs Journal, July 1999)
//! Order is important.
inline_ void SpreadBits(udword& n)
{
n = ( n & 0x0000ffff) | (( n & 0xffff0000) << 16);
n = ( n & 0x000000ff) | (( n & 0x0000ff00) << 8);
n = ( n & 0x000f000f) | (( n & 0x00f000f0) << 4);
n = ( n & 0x03030303) | (( n & 0x0c0c0c0c) << 2);
n = ( n & 0x11111111) | (( n & 0x22222222) << 1);
}
// Next Largest Power of 2
// Given a binary integer value x, the next largest power of 2 can be computed by a SWAR algorithm
// that recursively "folds" the upper bits into the lower bits. This process yields a bit vector with
// the same most significant 1 as x, but all 1's below it. Adding 1 to that value yields the next
// largest power of 2. For a 32-bit value:
inline_ udword nlpo2(udword x)
{
x |= (x >> 1);
x |= (x >> 2);
x |= (x >> 4);
x |= (x >> 8);
x |= (x >> 16);
return x+1;
}
//! Test to see if a number is an exact power of two (from Steve Baker's Cute Code Collection)
inline_ bool IsPowerOfTwo(udword n) { return ((n&(n-1))==0); }
//! Zero the least significant '1' bit in a word. (from Steve Baker's Cute Code Collection)
inline_ void ZeroLeastSetBit(udword& n) { n&=(n-1); }
//! Set the least significant N bits in a word. (from Steve Baker's Cute Code Collection)
inline_ void SetLeastNBits(udword& x, udword n) { x|=~(~0<<n); }
//! Classic XOR swap (from Steve Baker's Cute Code Collection)
//! x ^= y; /* x' = (x^y) */
//! y ^= x; /* y' = (y^(x^y)) = x */
//! x ^= y; /* x' = (x^y)^x = y */
inline_ void Swap(udword& x, udword& y) { x ^= y; y ^= x; x ^= y; }
//! Little/Big endian (from Steve Baker's Cute Code Collection)
//!
//! Extra comments by Kenny Hoff:
//! Determines the byte-ordering of the current machine (little or big endian)
//! by setting an integer value to 1 (so least significant bit is now 1); take
//! the address of the int and cast to a byte pointer (treat integer as an
//! array of four bytes); check the value of the first byte (must be 0 or 1).
//! If the value is 1, then the first byte least significant byte and this
//! implies LITTLE endian. If the value is 0, the first byte is the most
//! significant byte, BIG endian. Examples:
//! integer 1 on BIG endian: 00000000 00000000 00000000 00000001
//! integer 1 on LITTLE endian: 00000001 00000000 00000000 00000000
//!---------------------------------------------------------------------------
//! int IsLittleEndian() { int x=1; return ( ((char*)(&x))[0] ); }
inline_ char LittleEndian() { int i = 1; return *((char*)&i); }
//!< Alternative abs function
inline_ udword abs_(sdword x) { sdword y= x >> 31; return (x^y)-y; }
//!< Alternative min function
inline_ sdword min_(sdword a, sdword b) { sdword delta = b-a; return a + (delta&(delta>>31)); }
// Determine if one of the bytes in a 4 byte word is zero
inline_ BOOL HasNullByte(udword x) { return ((x + 0xfefefeff) & (~x) & 0x80808080); }
// To find the smallest 1 bit in a word EG: ~~~~~~10---0 => 0----010---0
inline_ udword LowestOneBit(udword w) { return ((w) & (~(w)+1)); }
// inline_ udword LowestOneBit_(udword w) { return ((w) & (-(w))); }
// Most Significant 1 Bit
// Given a binary integer value x, the most significant 1 bit (highest numbered element of a bit set)
// can be computed using a SWAR algorithm that recursively "folds" the upper bits into the lower bits.
// This process yields a bit vector with the same most significant 1 as x, but all 1's below it.
// Bitwise AND of the original value with the complement of the "folded" value shifted down by one
// yields the most significant bit. For a 32-bit value:
inline_ udword msb32(udword x)
{
x |= (x >> 1);
x |= (x >> 2);
x |= (x >> 4);
x |= (x >> 8);
x |= (x >> 16);
return (x & ~(x >> 1));
}
/*
"Just call it repeatedly with various input values and always with the same variable as "memory".
The sharpness determines the degree of filtering, where 0 completely filters out the input, and 1
does no filtering at all.
I seem to recall from college that this is called an IIR (Infinite Impulse Response) filter. As opposed
to the more typical FIR (Finite Impulse Response).
Also, I'd say that you can make more intelligent and interesting filters than this, for example filters
that remove wrong responses from the mouse because it's being moved too fast. You'd want such a filter
to be applied before this one, of course."
(JCAB on Flipcode)
*/
inline_ float FeedbackFilter(float val, float& memory, float sharpness)
{
ASSERT(sharpness>=0.0f && sharpness<=1.0f && "Invalid sharpness value in feedback filter");
if(sharpness<0.0f) sharpness = 0.0f;
else if(sharpness>1.0f) sharpness = 1.0f;
return memory = val * sharpness + memory * (1.0f - sharpness);
}
//! If you can guarantee that your input domain (i.e. value of x) is slightly
//! limited (abs(x) must be < ((1<<31u)-32767)), then you can use the
//! following code to clamp the resulting value into [-32768,+32767] range:
inline_ int ClampToInt16(int x)
{
// ASSERT(abs(x) < (int)((1<<31u)-32767));
int delta = 32767 - x;
x += (delta>>31) & delta;
delta = x + 32768;
x -= (delta>>31) & delta;
return x;
}
// Generic functions
template<class Type> inline_ void TSwap(Type& a, Type& b) { const Type c = a; a = b; b = c; }
template<class Type> inline_ Type TClamp(const Type& x, const Type& lo, const Type& hi) { return ((x<lo) ? lo : (x>hi) ? hi : x); }
template<class Type> inline_ void TSort(Type& a, Type& b)
{
if(a>b) TSwap(a, b);
}
template<class Type> inline_ void TSort(Type& a, Type& b, Type& c)
{
if(a>b) TSwap(a, b);
if(b>c) TSwap(b, c);
if(a>b) TSwap(a, b);
if(b>c) TSwap(b, c);
}
// Prevent nasty user-manipulations (strategy borrowed from Charles Bloom)
// #define PREVENT_COPY(curclass) void operator = (const curclass& object) { ASSERT(!"Bad use of operator ="); }
// ... actually this is better !
#define PREVENT_COPY(cur_class) private: cur_class(const cur_class& object); cur_class& operator=(const cur_class& object);
//! TO BE DOCUMENTED
#define OFFSET_OF(Class, Member) (size_t)&(((Class*)0)->Member)
//! TO BE DOCUMENTED
#define ARRAYSIZE(p) (sizeof(p)/sizeof(p[0]))
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/**
* Returns the alignment of the input address.
* \fn Alignment()
* \param address [in] address to check
* \return the best alignment (e.g. 1 for odd addresses, etc)
*/
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
FUNCTION ICECORE_API udword Alignment(udword address);
#define IS_ALIGNED_2(x) ((x&1)==0)
#define IS_ALIGNED_4(x) ((x&3)==0)
#define IS_ALIGNED_8(x) ((x&7)==0)
inline_ void _prefetch(void const* ptr) { (void)*(char const volatile *)ptr; }
// Compute implicit coords from an index:
// The idea is to get back 2D coords from a 1D index.
// For example:
//
// 0 1 2 ... nbu-1
// nbu nbu+1 i ...
//
// We have i, we're looking for the equivalent (u=2, v=1) location.
// i = u + v*nbu
// <=> i/nbu = u/nbu + v
// Since 0 <= u < nbu, u/nbu = 0 (integer)
// Hence: v = i/nbu
// Then we simply put it back in the original equation to compute u = i - v*nbu
inline_ void Compute2DCoords(udword& u, udword& v, udword i, udword nbu)
{
v = i / nbu;
u = i - (v * nbu);
}
// In 3D: i = u + v*nbu + w*nbu*nbv
// <=> i/(nbu*nbv) = u/(nbu*nbv) + v/nbv + w
// u/(nbu*nbv) is null since u/nbu was null already.
// v/nbv is null as well for the same reason.
// Hence w = i/(nbu*nbv)
// Then we're left with a 2D problem: i' = i - w*nbu*nbv = u + v*nbu
inline_ void Compute3DCoords(udword& u, udword& v, udword& w, udword i, udword nbu, udword nbu_nbv)
{
w = i / (nbu_nbv);
Compute2DCoords(u, v, i - (w * nbu_nbv), nbu);
}
#endif // __ICEUTILS_H__

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,159 @@
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/*
* OPCODE - Optimized Collision Detection
* Copyright (C) 2001 Pierre Terdiman
* Homepage: http://www.codercorner.com/Opcode.htm
*/
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/**
* Contains an array-based version of the sweep-and-prune algorithm
* \file OPC_ArraySAP.h
* \author Pierre Terdiman
* \date December, 2, 2007
*/
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Include Guard
#ifndef OPC_ARRAYSAP_H
#define OPC_ARRAYSAP_H
#pragma pack(1)
struct OPCODE_API ASAP_Pair
{
uword id0;
uword id1;
const void* object0;
const void* object1;
//#ifdef PAIR_USER_DATA
void* userData;
//#endif
inline_ const void* GetObject0() const { return (const void*)(size_t(object0) & ~3); }
inline_ const void* GetObject1() const { return (const void*)(size_t(object1) & ~3); }
inline_ size_t IsInArray() const { return size_t(object0) & 1; }
inline_ size_t IsRemoved() const { return size_t(object1) & 1; }
inline_ size_t IsNew() const { return size_t(object0) & 2; }
private:
inline_ void SetInArray() { object0 = (const void*)(size_t(object0) | 1); }
inline_ void SetRemoved() { object1 = (const void*)(size_t(object1) | 1); }
inline_ void SetNew() { object0 = (const void*)(size_t(object0) | 2); }
inline_ void ClearInArray() { object0 = (const void*)(size_t(object0) & ~1); }
inline_ void ClearRemoved() { object1 = (const void*)(size_t(object1) & ~1); }
inline_ void ClearNew() { object0 = (const void*)(size_t(object0) & ~2); }
friend class ArraySAP;
};
#pragma pack()
class OPCODE_API ASAP_PairManager
{
public:
ASAP_PairManager();
~ASAP_PairManager();
void Purge();
void ShrinkMemory();
const ASAP_Pair* AddPair (uword id0, uword id1, const void* object0, const void* object1);
bool RemovePair (uword id0, uword id1);
bool RemovePairs (const BitArray& array);
const ASAP_Pair* FindPair (uword id0, uword id1) const;
inline_ udword GetPairIndex(const ASAP_Pair* pair) const
{
return ((udword)((size_t(pair) - size_t(mActivePairs)))/sizeof(ASAP_Pair));
}
udword mHashSize;
udword mMask;
udword mNbActivePairs;
udword* mHashTable;
udword* mNext;
ASAP_Pair* mActivePairs;
inline_ ASAP_Pair* FindPair(uword id0, uword id1, udword hash_value) const;
void RemovePair(uword id0, uword id1, udword hash_value, udword pair_index);
void ReallocPairs();
};
typedef void* (*SAP_CreatePair)(const void* object0, const void* object1, void* user_data);
typedef void (*SAP_DeletePair)(const void* object0, const void* object1, void* user_data, void* pair_user_data);
// Forward declarations
class ASAP_EndPoint;
class ASAP_Box;
struct IAABB;
struct CreateData;
class OPCODE_API ArraySAP : public Allocateable
{
public:
ArraySAP();
~ArraySAP();
udword AddObject(void* object, uword guid, const AABB& box);
bool RemoveObject(udword handle);
bool UpdateObject(udword handle, const AABB& box);
udword DumpPairs(SAP_CreatePair create_cb, SAP_DeletePair delete_cb, void* cb_user_data, ASAP_Pair** pairs=null);
private:
Container mData;
ASAP_PairManager mPairs;
inline_ void AddPair(const void* object0, const void* object1, uword id0, uword id1)
{
ASSERT(object0);
ASAP_Pair* UP = (ASAP_Pair*)mPairs.AddPair(id0, id1, null, null);
ASSERT(UP);
if(UP->object0)
{
// Persistent pair
}
else
{
// New pair
ASSERT(!(int(object0)&1));
ASSERT(!(int(object1)&1));
UP->object0 = object0;
UP->object1 = object1;
UP->SetInArray();
mData.Add(mPairs.GetPairIndex(UP));
UP->SetNew();
}
UP->ClearRemoved();
}
inline_ void RemovePair(const void* object0, const void* object1, uword id0, uword id1)
{
ASAP_Pair* UP = (ASAP_Pair*)mPairs.FindPair(id0, id1);
if(UP)
{
if(!UP->IsInArray())
{
UP->SetInArray();
mData.Add(mPairs.GetPairIndex(UP));
}
UP->SetRemoved();
}
}
udword mNbBoxes;
udword mMaxNbBoxes;
ASAP_Box* mBoxes;
ASAP_EndPoint* mEndPoints[3];
udword mFirstFree;
void ResizeBoxArray();
// For batch creation
Container mCreated;
void BatchCreate();
void InsertEndPoints(udword axis, const ASAP_EndPoint* end_points, udword nb_endpoints);
bool CompleteBoxPruning2(udword nb, const IAABB* array, const Axes& axes, const CreateData* batched);
bool BipartiteBoxPruning2(udword nb0, const IAABB* array0, udword nb1, const IAABB* array1, const Axes& axes, const CreateData* batched, const udword* box_indices);
// For batch removal
Container mRemoved;
void BatchRemove();
};
#endif // OPC_ARRAYSAP_H

View File

@@ -67,17 +67,17 @@ static PRUNING_SORTER* gBipartitePruningSorter0 = null;
static PRUNING_SORTER* gBipartitePruningSorter1 = null;
inline_ PRUNING_SORTER* GetCompletePruningSorter()
{
if(!gCompletePruningSorter) gCompletePruningSorter = new PRUNING_SORTER;
if(!gCompletePruningSorter) gCompletePruningSorter = ICE_NEW(PRUNING_SORTER);
return gCompletePruningSorter;
}
inline_ PRUNING_SORTER* GetBipartitePruningSorter0()
{
if(!gBipartitePruningSorter0) gBipartitePruningSorter0 = new PRUNING_SORTER;
if(!gBipartitePruningSorter0) gBipartitePruningSorter0 = ICE_NEW(PRUNING_SORTER);
return gBipartitePruningSorter0;
}
inline_ PRUNING_SORTER* GetBipartitePruningSorter1()
{
if(!gBipartitePruningSorter1) gBipartitePruningSorter1 = new PRUNING_SORTER;
if(!gBipartitePruningSorter1) gBipartitePruningSorter1 = ICE_NEW(PRUNING_SORTER);
return gBipartitePruningSorter1;
}
void ReleasePruningSorters()

View File

@@ -56,11 +56,14 @@ subject to the following restrictions:
namespace IceCore
{
#include ".\Ice\IceAllocator.h"
#include ".\Ice\IceUtils.h"
#include ".\Ice\IceBitArray.h"
#include ".\Ice\IceContainer.h"
#include ".\Ice\IcePairs.h"
#include ".\Ice\IceRevisitedRadix.h"
#include ".\Ice\IceRandom.h"
#include ".\Ice\IceHashing.h"
}
using namespace IceCore;

View File

@@ -90,6 +90,7 @@ subject to the following restrictions:
// Sweep-and-prune
#include "OPC_BoxPruning.h"
#include "OPC_SweepAndPrune.h"
#include "OPC_ArraySAP.h"
FUNCTION OPCODE_API bool InitOpcode();
FUNCTION OPCODE_API bool CloseOpcode();

View File

@@ -1,7 +1,7 @@
<?xml version="1.0" encoding="Windows-1252"?>
<VisualStudioProject
ProjectType="Visual C++"
Version="8.00"
Version="8,00"
Name="Opcode"
ProjectGUID="{DBE44CA3-2912-4441-8D99-AA2242688AD2}"
>
@@ -825,6 +825,14 @@
<Filter
Name="SweepAndPrune"
>
<File
RelativePath=".\OPC_ArraySAP.cpp"
>
</File>
<File
RelativePath=".\OPC_ArraySAP.h"
>
</File>
<File
RelativePath="OPC_BoxPruning.cpp"
>
@@ -945,10 +953,30 @@
RelativePath="Ice\IceAABB.h"
>
</File>
<File
RelativePath=".\Ice\IceAllocator.cpp"
>
</File>
<File
RelativePath=".\Ice\IceAllocator.h"
>
</File>
<File
RelativePath=".\Ice\IceAssert.h"
>
</File>
<File
RelativePath="Ice\IceAxes.h"
>
</File>
<File
RelativePath=".\Ice\IceBitArray.cpp"
>
</File>
<File
RelativePath=".\Ice\IceBitArray.h"
>
</File>
<File
RelativePath="Ice\IceBoundingSphere.h"
>
@@ -985,6 +1013,10 @@
RelativePath="Ice\IceFPU.h"
>
</File>
<File
RelativePath=".\Ice\IceHashing.h"
>
</File>
<File
RelativePath="Ice\IceHPoint.cpp"
>

View File

@@ -0,0 +1,210 @@
/*
CDTestFramework http://codercorner.com
Copyright (c) 2007-2008 Pierre Terdiman, pierre@codercorner.com
This software is provided 'as-is', without any express or implied warranty.
In no event will the authors be held liable for any damages arising from the use of this software.
Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it freely,
subject to the following restrictions:
1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required.
2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software.
3. This notice may not be removed or altered from any source distribution.
*/
#include "stdafx.h"
#include "OpcodeArraySAPTest.h"
#include "RenderingHelpers.h"
#include "GLFontRenderer.h"
static udword gNbCreatedPairs;
static udword gNbDeletedPairs;
static udword gTotalNbPairs;
static void* CBData = (void*)0x12345678;
static void* PairUserData = (void*)0xDeadDead;
static void* CreatePairCB(const void* object0, const void* object1, void* user_data)
{
assert(user_data==CBData);
gNbCreatedPairs++;
return PairUserData;
}
static void DeletePairCB(const void* object0, const void* object1, void* user_data, void* pair_user_data)
{
assert(user_data==CBData);
assert(pair_user_data==PairUserData);
gNbDeletedPairs++;
}
OpcodeArraySAPTest::OpcodeArraySAPTest(int numBoxes) :
mBar (null),
mNbBoxes (numBoxes),
mBoxes (null),
mHandles (null),
mBoxTime (null),
mSpeed (0.005f),
mAmplitude (100.0f)
{
}
OpcodeArraySAPTest::~OpcodeArraySAPTest()
{
Release();
}
void OpcodeArraySAPTest::Init()
{
m_firstTime = true;
SRand(0);
mBoxes = new AABB[mNbBoxes];
mBoxTime = new float[mNbBoxes];
mHandles = new void*[mNbBoxes];
for(udword i=0;i<mNbBoxes;i++)
{
Point Center, Extents;
Center.x = (UnitRandomFloat()-0.5f) * 100.0f;
Center.y = (UnitRandomFloat()-0.5f) * 10.0f;
Center.z = (UnitRandomFloat()-0.5f) * 100.0f;
Extents.x = 2.0f + UnitRandomFloat() * 2.0f;
Extents.y = 2.0f + UnitRandomFloat() * 2.0f;
Extents.z = 2.0f + UnitRandomFloat() * 2.0f;
mBoxes[i].SetCenterExtents(Center, Extents);
mBoxTime[i] = 2000.0f*UnitRandomFloat();
}
UpdateBoxes(mNbBoxes);
for(udword i=0;i<mNbBoxes;i++)
{
// It is mandatory to pass a valid pointer as a first parameter. This is supposed to be the game object
// associated with the AABB. In this small example I just pass a pointer to the SAP itself.
mHandles[i] = (void*)mASAP.AddObject(&mASAP, i, mBoxes[i]);
}
udword MyNbInitialPairs = mASAP.DumpPairs(CreatePairCB, DeletePairCB, CBData);
gTotalNbPairs = MyNbInitialPairs;
}
void OpcodeArraySAPTest::Release()
{
DELETEARRAY(mHandles);
DELETEARRAY(mBoxTime);
DELETEARRAY(mBoxes);
}
void OpcodeArraySAPTest::Select()
{
// Create a tweak bar
{
mBar = TwNewBar("OpcodeArraySAPTest");
TwAddVarRW(mBar, "Speed", TW_TYPE_FLOAT, &mSpeed, " min=0.0 max=0.01 step=0.00001");
TwAddVarRW(mBar, "Amplitude", TW_TYPE_FLOAT, &mAmplitude, " min=10.0 max=200.0 step=0.1");
}
}
void OpcodeArraySAPTest::Deselect()
{
if(mBar)
{
TwDeleteBar(mBar);
mBar = null;
}
}
bool OpcodeArraySAPTest::UpdateBoxes(int numBoxes)
{
for(int i=0;i<numBoxes;i++)
{
mBoxTime[i] += mSpeed;
Point Center,Extents;
mBoxes[i].GetExtents(Extents);
Center.x = cosf(mBoxTime[i]*2.17f)*mAmplitude + sinf(mBoxTime[i])*mAmplitude*0.5f;
Center.y = cosf(mBoxTime[i]*1.38f)*mAmplitude + sinf(mBoxTime[i]*mAmplitude);
Center.z = sinf(mBoxTime[i]*0.777f)*mAmplitude;
mBoxes[i].SetCenterExtents(Center, Extents);
}
return true;
}
extern int percentUpdate;
void OpcodeArraySAPTest::PerformTest()
{
int numBoxes = (mNbBoxes*percentUpdate)/100;
if (m_firstTime)
{
numBoxes = mNbBoxes;
m_firstTime = false;
}
mProfiler.Start();
UpdateBoxes(numBoxes);
gNbCreatedPairs = 0;
gNbDeletedPairs = 0;
for(int i=0;i<numBoxes;i++)
{
mASAP.UpdateObject((udword)mHandles[i], mBoxes[i]);
}
ASAP_Pair* Pairs;
udword NbPairs = mASAP.DumpPairs(CreatePairCB, DeletePairCB, CBData, &Pairs);
gTotalNbPairs += gNbCreatedPairs;
gTotalNbPairs -= gNbDeletedPairs;
mProfiler.End();
mProfiler.Accum();
// printf("%d pairs colliding\r ", mPairs.GetNbPairs());
bool* Flags = (bool*)_alloca(sizeof(bool)*mNbBoxes);
ZeroMemory(Flags, sizeof(bool)*mNbBoxes);
for(udword i=0;i<NbPairs;i++)
{
Flags[Pairs[i].id0] = true;
Flags[Pairs[i].id1] = true;
}
// Render boxes
OBB CurrentBox;
CurrentBox.mRot.Identity();
for(udword i=0;i<mNbBoxes;i++)
{
if(Flags[i]) glColor3f(1.0f, 0.0f, 0.0f);
else glColor3f(0.0f, 1.0f, 0.0f);
mBoxes[i].GetCenter(CurrentBox.mCenter);
mBoxes[i].GetExtents(CurrentBox.mExtents);
DrawOBB(CurrentBox);
}
char Buffer[4096];
sprintf(Buffer, "OpcodeArraySAPTest: %5.1f us (%d cycles) : %d pairs\n", mProfiler.mMsTime, mProfiler.mCycles, NbPairs);
GLFontRenderer::print(10.0f, 10.0f, 0.02f, Buffer);
}
void OpcodeArraySAPTest::KeyboardCallback(unsigned char key, int x, int y)
{
}
void OpcodeArraySAPTest::MouseCallback(int button, int state, int x, int y)
{
}
void OpcodeArraySAPTest::MotionCallback(int x, int y)
{
}

View File

@@ -0,0 +1,53 @@
/*
CDTestFramework http://codercorner.com
Copyright (c) 2007-2008 Pierre Terdiman, pierre@codercorner.com
This software is provided 'as-is', without any express or implied warranty.
In no event will the authors be held liable for any damages arising from the use of this software.
Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it freely,
subject to the following restrictions:
1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required.
2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software.
3. This notice may not be removed or altered from any source distribution.
*/
#ifndef OPCODEARRAYSAPTEST_H
#define OPCODEARRAYSAPTEST_H
#include "CollisionTest.h"
#include "Profiling.h"
class OpcodeArraySAPTest : public CollisionTest
{
public:
OpcodeArraySAPTest(int numBoxes);
virtual ~OpcodeArraySAPTest();
virtual void Init();
virtual void Release();
virtual void PerformTest();
virtual void Select();
virtual void Deselect();
virtual void KeyboardCallback(unsigned char key, int x, int y);
virtual void MouseCallback(int button, int state, int x, int y);
virtual void MotionCallback(int x, int y);
TwBar* mBar; //!< AntTweakBar
Profiler mProfiler;
ArraySAP mASAP;
udword mNbBoxes;
AABB* mBoxes;
void** mHandles;
float* mBoxTime;
float mSpeed;
float mAmplitude;
bool m_firstTime;
bool UpdateBoxes(int numBoxes);
};
#endif // OPCODEARRAYSAPTEST_H

View File

@@ -1,32 +1,22 @@
========================================================================
CONSOLE APPLICATION : CDTestFramework Project Overview
========================================================================
AppWizard has created this CDTestFramework application for you.
This file contains a summary of what you will find in each of the files that
make up your CDTestFramework application.
This is a modified CDTestFramework to test Bullet's dynamic AABB tree against a SAP. It's the same demo as here: http://bulletphysics.com/ftp/pub/test/physics/demos/CDTestFramework2.70.zip
But I added an extra challenger: http://www.codercorner.com/Code/SweepAndPrune2.rar. This is the code described in this document: http://www.codercorner.com/SAP.pdf
So there are 4 tests:
- OPCODE's "box pruning"
- Bullet's Multi SAP
- Bullet's dbvt (dynamic AABB tree)
- OPCODE's array-based SAP
For 8192 boxes and 10% of them moving, OPCODE's SAP is roughly as fast as dbvt (and twice faster than Bullet's SAP on my machine). For less boxes (say 1024 or 2048), OPCODE's SAP is faster than dbvt. Figures and "winner" vary a lot depending on the number of objects and how many of them are moving each frame.
If you're interested you can see for yourself:
- download Bullet 2.70
- replace the content of this directory with the new files: \bullet-2.70\bullet-2.70\Extras\CDTestFramework
CDTestFramework.vcproj
This is the main project file for VC++ projects generated using an Application Wizard.
It contains information about the version of Visual C++ that generated the file, and
information about the platforms, configurations, and project features selected with the
Application Wizard.
Cheers,
CDTestFramework.cpp
This is the main application source file.
/////////////////////////////////////////////////////////////////////////////
Other standard files:
StdAfx.h, StdAfx.cpp
These files are used to build a precompiled header (PCH) file
named CDTestFramework.pch and a precompiled types file named StdAfx.obj.
/////////////////////////////////////////////////////////////////////////////
Other notes:
AppWizard uses "TODO:" comments to indicate parts of the source code you
should add to or customize.
/////////////////////////////////////////////////////////////////////////////
- Pierre Terdiman
August 31, 2008