Added multi-threaded collision detection. Original code is written for Cell SPU, but wrappers are provided to run on multi-core using Win32 Threads.
SpuLibspe2Support is on the todo list, so it can run on Cell Blade & PS3 Linux.
This commit is contained in:
41
Extras/BulletMultiThreaded/PlatformDefinitions.h
Normal file
41
Extras/BulletMultiThreaded/PlatformDefinitions.h
Normal file
@@ -0,0 +1,41 @@
|
|||||||
|
#ifndef TYPE_DEFINITIONS_H
|
||||||
|
#define TYPE_DEFINITIONS_H
|
||||||
|
|
||||||
|
#include <LinearMath/btScalar.h>
|
||||||
|
|
||||||
|
///This file provides some platform/compiler checks for common definitions
|
||||||
|
|
||||||
|
#ifdef WIN32
|
||||||
|
#if defined(__MINGW32__) || defined(__CYGWIN__) || (defined (_MSC_VER) && _MSC_VER < 1300)
|
||||||
|
#else
|
||||||
|
#endif //__MINGW32__
|
||||||
|
|
||||||
|
typedef unsigned char uint8_t;
|
||||||
|
typedef unsigned long int uint64_t;
|
||||||
|
typedef unsigned int uint32_t;
|
||||||
|
typedef unsigned short uint16_t;
|
||||||
|
|
||||||
|
#include <malloc.h>
|
||||||
|
#define memalign(alignment, size) malloc(size);
|
||||||
|
|
||||||
|
#include <string.h> //memcpy
|
||||||
|
|
||||||
|
#define USE_WIN32_THREADING 1
|
||||||
|
|
||||||
|
#else
|
||||||
|
#if defined (__CELLOS_LV2__)
|
||||||
|
///Playstation 3 Cell SDK
|
||||||
|
#else
|
||||||
|
//non-windows systems
|
||||||
|
|
||||||
|
#define USE_PTHREADS 1
|
||||||
|
|
||||||
|
#endif //__CELLOS_LV2__
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
#endif //TYPE_DEFINITIONS_H
|
||||||
309
Extras/BulletMultiThreaded/SpuCollisionTaskProcess.cpp
Normal file
309
Extras/BulletMultiThreaded/SpuCollisionTaskProcess.cpp
Normal file
@@ -0,0 +1,309 @@
|
|||||||
|
/*
|
||||||
|
Bullet Continuous Collision Detection and Physics Library
|
||||||
|
Copyright (c) 2003-2007 Erwin Coumans http://bulletphysics.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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
//#define __CELLOS_LV2__ 1
|
||||||
|
|
||||||
|
//#define DEBUG_SPU_TASK_SCHEDULING 1
|
||||||
|
|
||||||
|
#include "SpuLibspe2Support.h"
|
||||||
|
#include "Win32ThreadSupport.h"
|
||||||
|
|
||||||
|
//#include "SPUAssert.h"
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
//class OptimizedBvhNode;
|
||||||
|
|
||||||
|
#include "SpuCollisionTaskProcess.h"
|
||||||
|
#include "BulletCollision/CollisionDispatch/btCollisionObject.h"
|
||||||
|
#include "BulletCollision/CollisionShapes/btCollisionShape.h"
|
||||||
|
#include "BulletCollision/CollisionShapes/btConvexShape.h"
|
||||||
|
#include "SpuLibspe2Support.h"
|
||||||
|
#include "SpuNarrowPhaseCollisionTask/SpuGatheringCollisionTask.h" // for definitions processCollisionTask and createCollisionLocalStoreMemory
|
||||||
|
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
|
||||||
|
|
||||||
|
//#ifdef __CELLOS_LV2__???
|
||||||
|
#ifdef USE_WIN32_THREADING
|
||||||
|
Win32ThreadSupport gMidphaseSPU(Win32ThreadSupport::Win32ThreadConstructionInfo("collision",
|
||||||
|
processCollisionTask,
|
||||||
|
createCollisionLocalStoreMemory,
|
||||||
|
MIDPHASE_NUM_WORKUNIT_TASKS));
|
||||||
|
#elif defined(USE_LIBSPE2)
|
||||||
|
SpuLibspe2Support gMidphaseSPU(SPU_ELF_COLLISION_DETECTION,MIDPHASE_NUM_WORKUNIT_TASKS);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef __CELLOS_LV2__
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
SpuGatherAndProcessPairsTaskDesc g_spuGatherTaskDesc[MIDPHASE_NUM_WORKUNIT_TASKS];
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
SpuCollisionTaskProcess::SpuCollisionTaskProcess()
|
||||||
|
{
|
||||||
|
m_workUnitTaskBuffers = (unsigned char *)0;
|
||||||
|
|
||||||
|
|
||||||
|
for (int i = 0; i < MIDPHASE_NUM_WORKUNIT_TASKS; i++)
|
||||||
|
{
|
||||||
|
m_taskBusy[i] = false;
|
||||||
|
}
|
||||||
|
m_numBusyTasks = 0;
|
||||||
|
m_currentTask = 0;
|
||||||
|
m_currentPage = 0;
|
||||||
|
m_currentPageEntry = 0;
|
||||||
|
|
||||||
|
#ifdef DEBUG_SpuCollisionTaskProcess
|
||||||
|
m_initialized = false;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef __CELLOS_LV2__
|
||||||
|
// adding SPURS support.
|
||||||
|
gMidphaseSPU.startSPU();
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
//printf("sizeof vec_float4: %d\n", sizeof(vec_float4));
|
||||||
|
printf("sizeof SpuGatherAndProcessWorkUnitInput: %d\n", sizeof(SpuGatherAndProcessWorkUnitInput));
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
SpuCollisionTaskProcess::~SpuCollisionTaskProcess()
|
||||||
|
{
|
||||||
|
if (m_workUnitTaskBuffers != 0)
|
||||||
|
{
|
||||||
|
free(m_workUnitTaskBuffers);
|
||||||
|
m_workUnitTaskBuffers = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef __CELLOS_LV2__
|
||||||
|
|
||||||
|
// Consolidating SPU code
|
||||||
|
gMidphaseSPU.stopSPU();
|
||||||
|
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
void
|
||||||
|
SpuCollisionTaskProcess::initialize2()
|
||||||
|
{
|
||||||
|
#ifdef DEBUG_SPU_TASK_SCHEDULING
|
||||||
|
printf("SpuCollisionTaskProcess::initialize()\n");
|
||||||
|
#endif //DEBUG_SPU_TASK_SCHEDULING
|
||||||
|
if (!m_workUnitTaskBuffers)
|
||||||
|
{
|
||||||
|
m_workUnitTaskBuffers = (unsigned char *)memalign(128, MIDPHASE_WORKUNIT_TASK_SIZE*MIDPHASE_NUM_WORKUNIT_TASKS);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int i = 0; i < MIDPHASE_NUM_WORKUNIT_TASKS; i++)
|
||||||
|
{
|
||||||
|
m_taskBusy[i] = false;
|
||||||
|
}
|
||||||
|
m_numBusyTasks = 0;
|
||||||
|
m_currentTask = 0;
|
||||||
|
m_currentPage = 0;
|
||||||
|
m_currentPageEntry = 0;
|
||||||
|
|
||||||
|
#ifdef DEBUG_SpuCollisionTaskProcess
|
||||||
|
m_initialized = true;
|
||||||
|
assert(MIDPHASE_NUM_WORKUNITS_PER_TASK*sizeof(SpuGatherAndProcessWorkUnitInput) <= MIDPHASE_WORKUNIT_TASK_SIZE);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void SpuCollisionTaskProcess::issueTask2()
|
||||||
|
{
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#ifdef DEBUG_SPU_TASK_SCHEDULING
|
||||||
|
printf("SpuCollisionTaskProcess::issueTask (m_currentTask= %d\)n", m_currentTask);
|
||||||
|
#endif //DEBUG_SPU_TASK_SCHEDULING
|
||||||
|
|
||||||
|
m_taskBusy[m_currentTask] = true;
|
||||||
|
m_numBusyTasks++;
|
||||||
|
|
||||||
|
|
||||||
|
SpuGatherAndProcessPairsTaskDesc& taskDesc = g_spuGatherTaskDesc[m_currentTask];
|
||||||
|
{
|
||||||
|
// send task description in event message
|
||||||
|
// no error checking here...
|
||||||
|
// but, currently, event queue can be no larger than NUM_WORKUNIT_TASKS.
|
||||||
|
|
||||||
|
taskDesc.inPtr = reinterpret_cast<uint64_t>(MIDPHASE_TASK_PTR(m_currentTask));
|
||||||
|
|
||||||
|
taskDesc.taskId = m_currentTask;
|
||||||
|
taskDesc.numPages = m_currentPage+1;
|
||||||
|
taskDesc.numOnLastPage = m_currentPageEntry;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
gMidphaseSPU.sendRequest(CMD_GATHER_AND_PROCESS_PAIRLIST, (uint32_t) &taskDesc,m_currentTask);
|
||||||
|
|
||||||
|
// if all tasks busy, wait for spu event to clear the task.
|
||||||
|
|
||||||
|
|
||||||
|
if (m_numBusyTasks >= MIDPHASE_NUM_WORKUNIT_TASKS)
|
||||||
|
{
|
||||||
|
unsigned int taskId;
|
||||||
|
unsigned int outputSize;
|
||||||
|
|
||||||
|
gMidphaseSPU.waitForResponse(&taskId, &outputSize);
|
||||||
|
|
||||||
|
//printf("PPU: after issue, received event: %u %d\n", taskId, outputSize);
|
||||||
|
|
||||||
|
//postProcess(taskId, outputSize);
|
||||||
|
|
||||||
|
m_taskBusy[taskId] = false;
|
||||||
|
|
||||||
|
m_numBusyTasks--;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void SpuCollisionTaskProcess::addWorkToTask(void* pairArrayPtr,int startIndex,int endIndex)
|
||||||
|
{
|
||||||
|
#ifdef DEBUG_SPU_TASK_SCHEDULING
|
||||||
|
printf("#");
|
||||||
|
#endif //DEBUG_SPU_TASK_SCHEDULING
|
||||||
|
|
||||||
|
#ifdef DEBUG_SpuCollisionTaskProcess
|
||||||
|
assert(m_initialized);
|
||||||
|
assert(m_workUnitTaskBuffers);
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
bool batch = true;
|
||||||
|
|
||||||
|
if (batch)
|
||||||
|
{
|
||||||
|
if (m_currentPageEntry == MIDPHASE_NUM_WORKUNITS_PER_PAGE)
|
||||||
|
{
|
||||||
|
if (m_currentPage == MIDPHASE_NUM_WORKUNIT_PAGES-1)
|
||||||
|
{
|
||||||
|
// task buffer is full, issue current task.
|
||||||
|
// if all task buffers busy, this waits until SPU is done.
|
||||||
|
issueTask2();
|
||||||
|
|
||||||
|
// find new task buffer
|
||||||
|
for (unsigned int i = 0; i < MIDPHASE_NUM_WORKUNIT_TASKS; i++)
|
||||||
|
{
|
||||||
|
if (!m_taskBusy[i])
|
||||||
|
{
|
||||||
|
m_currentTask = i;
|
||||||
|
//init the task data
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
m_currentPage = 0;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
m_currentPage++;
|
||||||
|
}
|
||||||
|
|
||||||
|
m_currentPageEntry = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
SpuGatherAndProcessWorkUnitInput &wuInput =
|
||||||
|
*(reinterpret_cast<SpuGatherAndProcessWorkUnitInput*>
|
||||||
|
(MIDPHASE_ENTRY_PTR(m_currentTask, m_currentPage, m_currentPageEntry)));
|
||||||
|
|
||||||
|
wuInput.m_pairArrayPtr = reinterpret_cast<uint64_t>(pairArrayPtr);
|
||||||
|
wuInput.m_startIndex = startIndex;
|
||||||
|
wuInput.m_endIndex = endIndex;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
m_currentPageEntry++;
|
||||||
|
|
||||||
|
if (!batch)
|
||||||
|
{
|
||||||
|
issueTask2();
|
||||||
|
|
||||||
|
// find new task buffer
|
||||||
|
for (unsigned int i = 0; i < MIDPHASE_NUM_WORKUNIT_TASKS; i++)
|
||||||
|
{
|
||||||
|
if (!m_taskBusy[i])
|
||||||
|
{
|
||||||
|
m_currentTask = i;
|
||||||
|
//init the task data
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
m_currentPage = 0;
|
||||||
|
m_currentPageEntry =0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void
|
||||||
|
SpuCollisionTaskProcess::flush2()
|
||||||
|
{
|
||||||
|
#ifdef DEBUG_SPU_TASK_SCHEDULING
|
||||||
|
printf("\nSpuCollisionTaskProcess::flush()\n");
|
||||||
|
#endif //DEBUG_SPU_TASK_SCHEDULING
|
||||||
|
|
||||||
|
// if there's a partially filled task buffer, submit that task
|
||||||
|
if (m_currentPage > 0 || m_currentPageEntry > 0)
|
||||||
|
{
|
||||||
|
issueTask2();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// all tasks are issued, wait for all tasks to be complete
|
||||||
|
while(m_numBusyTasks > 0)
|
||||||
|
{
|
||||||
|
// Consolidating SPU code
|
||||||
|
unsigned int taskId;
|
||||||
|
unsigned int outputSize;
|
||||||
|
|
||||||
|
{
|
||||||
|
|
||||||
|
// SPURS support.
|
||||||
|
gMidphaseSPU.waitForResponse(&taskId, &outputSize);
|
||||||
|
}
|
||||||
|
|
||||||
|
//printf("PPU: flushing, received event: %u %d\n", taskId, outputSize);
|
||||||
|
|
||||||
|
//postProcess(taskId, outputSize);
|
||||||
|
|
||||||
|
m_taskBusy[taskId] = false;
|
||||||
|
|
||||||
|
m_numBusyTasks--;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
131
Extras/BulletMultiThreaded/SpuCollisionTaskProcess.h
Normal file
131
Extras/BulletMultiThreaded/SpuCollisionTaskProcess.h
Normal file
@@ -0,0 +1,131 @@
|
|||||||
|
/*
|
||||||
|
Bullet Continuous Collision Detection and Physics Library
|
||||||
|
Copyright (c) 2003-2007 Erwin Coumans http://bulletphysics.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 SPU_COLLISION_TASK_PROCESS_H
|
||||||
|
#define SPU_COLLISION_TASK_PROCESS_H
|
||||||
|
|
||||||
|
#include <assert.h>
|
||||||
|
|
||||||
|
#include <LinearMath/btScalar.h>
|
||||||
|
|
||||||
|
#include "PlatformDefinitions.h"
|
||||||
|
|
||||||
|
|
||||||
|
#define DEBUG_SpuCollisionTaskProcess 1
|
||||||
|
|
||||||
|
//#define MIDPHASE_NUM_WORKUNIT_TASKS 4 //attempt for multi-core
|
||||||
|
#define MIDPHASE_NUM_WORKUNIT_TASKS 12 //attempt for multi-core
|
||||||
|
//#define MIDPHASE_NUM_WORKUNIT_TASKS 6 //for SPUs
|
||||||
|
|
||||||
|
#define CMD_GATHER_AND_PROCESS_PAIRLIST 1
|
||||||
|
|
||||||
|
class btCollisionObject;
|
||||||
|
class btPersistentManifold;
|
||||||
|
class btDispatcher;
|
||||||
|
|
||||||
|
|
||||||
|
///Task Description for SPU collision detection
|
||||||
|
struct SpuGatherAndProcessPairsTaskDesc
|
||||||
|
{
|
||||||
|
uint64_t inPtr;//m_pairArrayPtr;
|
||||||
|
//mutex variable
|
||||||
|
uint32_t m_someMutexVariableInMainMemory;
|
||||||
|
|
||||||
|
uint64_t m_dispatcher;
|
||||||
|
|
||||||
|
uint32_t numOnLastPage;
|
||||||
|
|
||||||
|
uint16_t numPages;
|
||||||
|
uint16_t taskId;
|
||||||
|
|
||||||
|
struct CollisionTask_LocalStoreMemory* m_lsMemory;
|
||||||
|
}
|
||||||
|
#ifdef __CELLOS_LV2__
|
||||||
|
__attribute__ ((aligned (16)))
|
||||||
|
#endif
|
||||||
|
;
|
||||||
|
|
||||||
|
|
||||||
|
///MidphaseWorkUnitInput stores individual primitive versus mesh collision detection input, to be processed by the SPU.
|
||||||
|
struct SpuGatherAndProcessWorkUnitInput
|
||||||
|
{
|
||||||
|
uint64_t m_pairArrayPtr;
|
||||||
|
int m_startIndex;
|
||||||
|
int m_endIndex;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/// SpuCollisionTaskProcess handles SPU processing of collision pairs.
|
||||||
|
/// Maintains a set of task buffers.
|
||||||
|
/// When the task is full, the task is issued for SPUs to process. Contact output goes into btPersistentManifold
|
||||||
|
/// associated with each task.
|
||||||
|
/// When PPU issues a task, it will look for completed task buffers
|
||||||
|
/// PPU will do postprocessing, dependent on workunit output (not likely)
|
||||||
|
class SpuCollisionTaskProcess
|
||||||
|
{
|
||||||
|
|
||||||
|
unsigned char *m_workUnitTaskBuffers;
|
||||||
|
|
||||||
|
|
||||||
|
// track task buffers that are being used, and total busy tasks
|
||||||
|
bool m_taskBusy[MIDPHASE_NUM_WORKUNIT_TASKS];
|
||||||
|
unsigned int m_numBusyTasks;
|
||||||
|
|
||||||
|
// the current task and the current entry to insert a new work unit
|
||||||
|
unsigned int m_currentTask;
|
||||||
|
unsigned int m_currentPage;
|
||||||
|
unsigned int m_currentPageEntry;
|
||||||
|
|
||||||
|
#ifdef DEBUG_SpuCollisionTaskProcess
|
||||||
|
bool m_initialized;
|
||||||
|
#endif
|
||||||
|
void issueTask2();
|
||||||
|
//void postProcess(unsigned int taskId, int outputSize);
|
||||||
|
|
||||||
|
public:
|
||||||
|
SpuCollisionTaskProcess();
|
||||||
|
|
||||||
|
~SpuCollisionTaskProcess();
|
||||||
|
|
||||||
|
///call initialize in the beginning of the frame, before addCollisionPairToTask
|
||||||
|
void initialize2();
|
||||||
|
|
||||||
|
///batch up additional work to a current task for SPU processing. When batch is full, it issues the task.
|
||||||
|
void addWorkToTask(void* pairArrayPtr,int startIndex,int endIndex);
|
||||||
|
|
||||||
|
///call flush to submit potential outstanding work to SPUs and wait for all involved SPUs to be finished
|
||||||
|
void flush2();
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#define MIDPHASE_TASK_PTR(task) (&m_workUnitTaskBuffers[0] + MIDPHASE_WORKUNIT_TASK_SIZE*task)
|
||||||
|
#define MIDPHASE_ENTRY_PTR(task,page,entry) (MIDPHASE_TASK_PTR(task) + MIDPHASE_WORKUNIT_PAGE_SIZE*page + sizeof(SpuGatherAndProcessWorkUnitInput)*entry)
|
||||||
|
#define MIDPHASE_OUTPUT_PTR(task) (&m_contactOutputBuffers[0] + MIDPHASE_MAX_CONTACT_BUFFER_SIZE*task)
|
||||||
|
#define MIDPHASE_TREENODES_PTR(task) (&m_complexShapeBuffers[0] + MIDPHASE_COMPLEX_SHAPE_BUFFER_SIZE*task)
|
||||||
|
|
||||||
|
|
||||||
|
#define MIDPHASE_WORKUNIT_PAGE_SIZE (16)
|
||||||
|
|
||||||
|
#define MIDPHASE_NUM_WORKUNIT_PAGES 1
|
||||||
|
#define MIDPHASE_WORKUNIT_TASK_SIZE (MIDPHASE_WORKUNIT_PAGE_SIZE*MIDPHASE_NUM_WORKUNIT_PAGES)
|
||||||
|
#define MIDPHASE_NUM_WORKUNITS_PER_PAGE (MIDPHASE_WORKUNIT_PAGE_SIZE / sizeof(SpuGatherAndProcessWorkUnitInput))
|
||||||
|
#define MIDPHASE_NUM_WORKUNITS_PER_TASK (MIDPHASE_NUM_WORKUNITS_PER_PAGE*MIDPHASE_NUM_WORKUNIT_PAGES)
|
||||||
|
|
||||||
|
|
||||||
|
#endif // SPU_COLLISION_TASK_PROCESS_H
|
||||||
|
|
||||||
@@ -0,0 +1,58 @@
|
|||||||
|
/*
|
||||||
|
Bullet Continuous Collision Detection and Physics Library
|
||||||
|
Copyright (c) 2003-2007 Erwin Coumans http://bulletphysics.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 "SpuContactManifoldCollisionAlgorithm.h"
|
||||||
|
#include "BulletCollision/CollisionDispatch/btCollisionDispatcher.h"
|
||||||
|
#include "BulletCollision/CollisionDispatch/btCollisionObject.h"
|
||||||
|
#include "BulletCollision/CollisionShapes/btCollisionShape.h"
|
||||||
|
|
||||||
|
|
||||||
|
SpuContactManifoldCollisionAlgorithm::SpuContactManifoldCollisionAlgorithm()
|
||||||
|
:m_manifoldPtr(0)
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void SpuContactManifoldCollisionAlgorithm::processCollision (btCollisionObject* body0,btCollisionObject* body1,const btDispatcherInfo& dispatchInfo,btManifoldResult* resultOut)
|
||||||
|
{
|
||||||
|
btAssert(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
float SpuContactManifoldCollisionAlgorithm::calculateTimeOfImpact(btCollisionObject* body0,btCollisionObject* body1,const btDispatcherInfo& dispatchInfo,btManifoldResult* resultOut)
|
||||||
|
{
|
||||||
|
btAssert(0);
|
||||||
|
return 1.f;
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifndef __SPU__
|
||||||
|
SpuContactManifoldCollisionAlgorithm::SpuContactManifoldCollisionAlgorithm(const btCollisionAlgorithmConstructionInfo& ci,btCollisionObject* body0,btCollisionObject* body1)
|
||||||
|
:btCollisionAlgorithm(ci)
|
||||||
|
{
|
||||||
|
m_manifoldPtr = m_dispatcher->getNewManifold(body0,body1);
|
||||||
|
m_shapeType0 = body0->getCollisionShape()->getShapeType();
|
||||||
|
m_shapeType1 = body1->getCollisionShape()->getShapeType();
|
||||||
|
m_collisionMargin0 = body0->getCollisionShape()->getMargin();
|
||||||
|
m_collisionMargin1 = body1->getCollisionShape()->getMargin();
|
||||||
|
|
||||||
|
}
|
||||||
|
#endif //__SPU__
|
||||||
|
|
||||||
|
|
||||||
|
SpuContactManifoldCollisionAlgorithm::~SpuContactManifoldCollisionAlgorithm()
|
||||||
|
{
|
||||||
|
if (m_manifoldPtr)
|
||||||
|
m_dispatcher->releaseManifold(m_manifoldPtr);
|
||||||
|
}
|
||||||
@@ -0,0 +1,80 @@
|
|||||||
|
/*
|
||||||
|
Bullet Continuous Collision Detection and Physics Library
|
||||||
|
Copyright (c) 2003-2007 Erwin Coumans http://bulletphysics.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 SPU_CONTACTMANIFOLD_COLLISION_ALGORITHM_H
|
||||||
|
#define SPU_CONTACTMANIFOLD_COLLISION_ALGORITHM_H
|
||||||
|
|
||||||
|
#include "BulletCollision/BroadphaseCollision/btCollisionAlgorithm.h"
|
||||||
|
#include "BulletCollision/BroadphaseCollision/btBroadphaseProxy.h"
|
||||||
|
#include "BulletCollision/CollisionDispatch/btCollisionCreateFunc.h"
|
||||||
|
class btPersistentManifold;
|
||||||
|
|
||||||
|
/// SpuContactManifoldCollisionAlgorithm provides contact manifold and should be processed on SPU.
|
||||||
|
ATTRIBUTE_ALIGNED16(class) SpuContactManifoldCollisionAlgorithm : public btCollisionAlgorithm
|
||||||
|
{
|
||||||
|
|
||||||
|
btPersistentManifold* m_manifoldPtr;
|
||||||
|
int m_shapeType0;
|
||||||
|
int m_shapeType1;
|
||||||
|
float m_collisionMargin0;
|
||||||
|
float m_collisionMargin1;
|
||||||
|
|
||||||
|
|
||||||
|
public:
|
||||||
|
|
||||||
|
virtual void processCollision (btCollisionObject* body0,btCollisionObject* body1,const btDispatcherInfo& dispatchInfo,btManifoldResult* resultOut);
|
||||||
|
|
||||||
|
virtual float calculateTimeOfImpact(btCollisionObject* body0,btCollisionObject* body1,const btDispatcherInfo& dispatchInfo,btManifoldResult* resultOut);
|
||||||
|
|
||||||
|
SpuContactManifoldCollisionAlgorithm();
|
||||||
|
|
||||||
|
SpuContactManifoldCollisionAlgorithm(const btCollisionAlgorithmConstructionInfo& ci,btCollisionObject* body0,btCollisionObject* body1);
|
||||||
|
|
||||||
|
virtual ~SpuContactManifoldCollisionAlgorithm();
|
||||||
|
|
||||||
|
btPersistentManifold* getContactManifoldPtr()
|
||||||
|
{
|
||||||
|
return m_manifoldPtr;
|
||||||
|
}
|
||||||
|
|
||||||
|
int getShapeType0() const
|
||||||
|
{
|
||||||
|
return m_shapeType0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int getShapeType1() const
|
||||||
|
{
|
||||||
|
return m_shapeType1;
|
||||||
|
}
|
||||||
|
float getCollisionMargin0() const
|
||||||
|
{
|
||||||
|
return m_collisionMargin0;
|
||||||
|
}
|
||||||
|
float getCollisionMargin1() const
|
||||||
|
{
|
||||||
|
return m_collisionMargin1;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct CreateFunc :public btCollisionAlgorithmCreateFunc
|
||||||
|
{
|
||||||
|
virtual btCollisionAlgorithm* CreateCollisionAlgorithm(btCollisionAlgorithmConstructionInfo& ci, btCollisionObject* body0,btCollisionObject* body1)
|
||||||
|
{
|
||||||
|
return new SpuContactManifoldCollisionAlgorithm(ci,body0,body1);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif //SPU_CONTACTMANIFOLD_COLLISION_ALGORITHM_H
|
||||||
105
Extras/BulletMultiThreaded/SpuDoubleBuffer.h
Normal file
105
Extras/BulletMultiThreaded/SpuDoubleBuffer.h
Normal file
@@ -0,0 +1,105 @@
|
|||||||
|
#ifndef DOUBLE_BUFFER_H
|
||||||
|
#define DOUBLE_BUFFER_H
|
||||||
|
|
||||||
|
#ifdef __CELLOS_LV2__
|
||||||
|
|
||||||
|
#include <cell/dma.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
#else
|
||||||
|
#include "SpuFakeDma.h"
|
||||||
|
#endif //
|
||||||
|
|
||||||
|
|
||||||
|
///DoubleBuffer
|
||||||
|
template<class T, int size>
|
||||||
|
class DoubleBuffer
|
||||||
|
{
|
||||||
|
#ifdef __CELLOS_LV2__
|
||||||
|
T m_buffer0[size] __attribute__ ((aligned (128)));
|
||||||
|
T m_buffer1[size] __attribute__ ((aligned (128)));
|
||||||
|
#else
|
||||||
|
T m_buffer0[size];
|
||||||
|
T m_buffer1[size];
|
||||||
|
#endif
|
||||||
|
|
||||||
|
T *m_frontBuffer;
|
||||||
|
T *m_backBuffer;
|
||||||
|
|
||||||
|
unsigned int m_dmaTag;
|
||||||
|
bool m_dmaPending;
|
||||||
|
public:
|
||||||
|
bool isPending() const { return m_dmaPending;}
|
||||||
|
DoubleBuffer();
|
||||||
|
|
||||||
|
// dma get and put commands
|
||||||
|
void backBufferDmaGet(uint64_t ea, unsigned int numBytes, unsigned int tag);
|
||||||
|
void backBufferDmaPut(uint64_t ea, unsigned int numBytes, unsigned int tag);
|
||||||
|
|
||||||
|
// gets pointer to a buffer
|
||||||
|
T *getFront();
|
||||||
|
T *getBack();
|
||||||
|
|
||||||
|
// if back buffer dma was started, wait for it to complete
|
||||||
|
// then move back to front and vice versa
|
||||||
|
T *swapBuffers();
|
||||||
|
};
|
||||||
|
|
||||||
|
template<class T, int size>
|
||||||
|
DoubleBuffer<T,size>::DoubleBuffer()
|
||||||
|
{
|
||||||
|
m_dmaPending = false;
|
||||||
|
m_frontBuffer = &m_buffer0[0];
|
||||||
|
m_backBuffer = &m_buffer1[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
template<class T, int size>
|
||||||
|
void
|
||||||
|
DoubleBuffer<T,size>::backBufferDmaGet(uint64_t ea, unsigned int numBytes, unsigned int tag)
|
||||||
|
{
|
||||||
|
m_dmaPending = true;
|
||||||
|
m_dmaTag = tag;
|
||||||
|
cellDmaLargeGet(m_backBuffer, ea, numBytes, tag, 0, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
template<class T, int size>
|
||||||
|
void
|
||||||
|
DoubleBuffer<T,size>::backBufferDmaPut(uint64_t ea, unsigned int numBytes, unsigned int tag)
|
||||||
|
{
|
||||||
|
m_dmaPending = true;
|
||||||
|
m_dmaTag = tag;
|
||||||
|
cellDmaLargePut(m_backBuffer, ea, numBytes, tag, 0, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
template<class T, int size>
|
||||||
|
T *
|
||||||
|
DoubleBuffer<T,size>::getFront()
|
||||||
|
{
|
||||||
|
return m_frontBuffer;
|
||||||
|
}
|
||||||
|
|
||||||
|
template<class T, int size>
|
||||||
|
T *
|
||||||
|
DoubleBuffer<T,size>::getBack()
|
||||||
|
{
|
||||||
|
return m_backBuffer;
|
||||||
|
}
|
||||||
|
|
||||||
|
template<class T, int size>
|
||||||
|
T *
|
||||||
|
DoubleBuffer<T,size>::swapBuffers()
|
||||||
|
{
|
||||||
|
if (m_dmaPending)
|
||||||
|
{
|
||||||
|
cellDmaWaitTagStatusAll(1<<m_dmaTag);
|
||||||
|
m_dmaPending = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
T *tmp = m_backBuffer;
|
||||||
|
m_backBuffer = m_frontBuffer;
|
||||||
|
m_frontBuffer = tmp;
|
||||||
|
|
||||||
|
return m_frontBuffer;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
30
Extras/BulletMultiThreaded/SpuFakeDma.cpp
Normal file
30
Extras/BulletMultiThreaded/SpuFakeDma.cpp
Normal file
@@ -0,0 +1,30 @@
|
|||||||
|
|
||||||
|
#include "SpuFakeDma.h"
|
||||||
|
|
||||||
|
int cellDmaLargeGet(void *ls, uint64_t ea, uint32_t size, uint32_t tag, uint32_t tid, uint32_t rid)
|
||||||
|
{
|
||||||
|
void* targetMainMem = (void*)ea;
|
||||||
|
memcpy(ls,targetMainMem,size);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int cellDmaGet(void *ls, uint64_t ea, uint32_t size, uint32_t tag, uint32_t tid, uint32_t rid)
|
||||||
|
{
|
||||||
|
void* targetMainMem = (void*)ea;
|
||||||
|
memcpy(ls,targetMainMem,size);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int cellDmaLargePut(const void *ls, uint64_t ea, uint32_t size, uint32_t tag, uint32_t tid, uint32_t rid)
|
||||||
|
{
|
||||||
|
void* targetMainMem = (void*)ea;
|
||||||
|
memcpy(targetMainMem,ls,size);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
void cellDmaWaitTagStatusAll(int ignore)
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
21
Extras/BulletMultiThreaded/SpuFakeDma.h
Normal file
21
Extras/BulletMultiThreaded/SpuFakeDma.h
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
|
||||||
|
#ifndef FAKE_DMA_H
|
||||||
|
#define FAKE_DMA_H
|
||||||
|
|
||||||
|
|
||||||
|
#include "PlatformDefinitions.h"
|
||||||
|
|
||||||
|
#include <LinearMath/btScalar.h> //for definition of uint64_t,uint32_t
|
||||||
|
#define DMA_TAG(a) (a)
|
||||||
|
#define DMA_MASK(a) (a)
|
||||||
|
|
||||||
|
/// cellDmaLargeGet Win32 replacements for Cell DMA to allow simulating most of the SPU code (just memcpy)
|
||||||
|
int cellDmaLargeGet(void *ls, uint64_t ea, uint32_t size, uint32_t tag, uint32_t tid, uint32_t rid);
|
||||||
|
int cellDmaGet(void *ls, uint64_t ea, uint32_t size, uint32_t tag, uint32_t tid, uint32_t rid);
|
||||||
|
/// cellDmaLargePut Win32 replacements for Cell DMA to allow simulating most of the SPU code (just memcpy)
|
||||||
|
int cellDmaLargePut(const void *ls, uint64_t ea, uint32_t size, uint32_t tag, uint32_t tid, uint32_t rid);
|
||||||
|
/// cellDmaWaitTagStatusAll Win32 replacements for Cell DMA to allow simulating most of the SPU code (just memcpy)
|
||||||
|
void cellDmaWaitTagStatusAll(int ignore);
|
||||||
|
|
||||||
|
|
||||||
|
#endif //FAKE_DMA_H
|
||||||
210
Extras/BulletMultiThreaded/SpuGatheringCollisionDispatcher.cpp
Normal file
210
Extras/BulletMultiThreaded/SpuGatheringCollisionDispatcher.cpp
Normal file
@@ -0,0 +1,210 @@
|
|||||||
|
/*
|
||||||
|
Bullet Continuous Collision Detection and Physics Library
|
||||||
|
Copyright (c) 2003-2007 Erwin Coumans http://bulletphysics.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 "SpuGatheringCollisionDispatcher.h"
|
||||||
|
#include "SpuCollisionTaskProcess.h"
|
||||||
|
|
||||||
|
|
||||||
|
#include "BulletCollision/BroadphaseCollision/btOverlappingPairCache.h"
|
||||||
|
#include "BulletCollision/CollisionDispatch/btEmptyCollisionAlgorithm.h"
|
||||||
|
#include "SpuContactManifoldCollisionAlgorithm.h"
|
||||||
|
#include "BulletCollision/CollisionDispatch/btCollisionObject.h"
|
||||||
|
#include "BulletCollision/CollisionShapes/btCollisionShape.h"
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
SpuGatheringCollisionDispatcher::SpuGatheringCollisionDispatcher()
|
||||||
|
:m_spuCollisionTaskProcess(0)
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
bool SpuGatheringCollisionDispatcher::supportsDispatchPairOnSpu(int proxyType0,int proxyType1)
|
||||||
|
{
|
||||||
|
bool supported0 = (
|
||||||
|
(proxyType0 == BOX_SHAPE_PROXYTYPE) ||
|
||||||
|
(proxyType0 == TRIANGLE_SHAPE_PROXYTYPE) ||
|
||||||
|
(proxyType0 == SPHERE_SHAPE_PROXYTYPE) ||
|
||||||
|
(proxyType0 == CAPSULE_SHAPE_PROXYTYPE) ||
|
||||||
|
(proxyType0 == CYLINDER_SHAPE_PROXYTYPE) ||
|
||||||
|
// (proxyType0 == CONE_SHAPE_PROXYTYPE) ||
|
||||||
|
(proxyType0 == TRIANGLE_MESH_SHAPE_PROXYTYPE) ||
|
||||||
|
(proxyType0 == CONVEX_HULL_SHAPE_PROXYTYPE)
|
||||||
|
);
|
||||||
|
|
||||||
|
bool supported1 = (
|
||||||
|
(proxyType1 == BOX_SHAPE_PROXYTYPE) ||
|
||||||
|
(proxyType1 == TRIANGLE_SHAPE_PROXYTYPE) ||
|
||||||
|
(proxyType1 == SPHERE_SHAPE_PROXYTYPE) ||
|
||||||
|
(proxyType1 == CAPSULE_SHAPE_PROXYTYPE) ||
|
||||||
|
(proxyType1 == CYLINDER_SHAPE_PROXYTYPE) ||
|
||||||
|
// (proxyType1 == CONE_SHAPE_PROXYTYPE) ||
|
||||||
|
(proxyType1 == TRIANGLE_MESH_SHAPE_PROXYTYPE) ||
|
||||||
|
(proxyType1 == CONVEX_HULL_SHAPE_PROXYTYPE)
|
||||||
|
);
|
||||||
|
|
||||||
|
return supported0 && supported1;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
SpuGatheringCollisionDispatcher::~SpuGatheringCollisionDispatcher()
|
||||||
|
{
|
||||||
|
if (m_spuCollisionTaskProcess)
|
||||||
|
delete m_spuCollisionTaskProcess;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#include "stdio.h"
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
///interface for iterating all overlapping collision pairs, no matter how those pairs are stored (array, set, map etc)
|
||||||
|
///this is useful for the collision dispatcher.
|
||||||
|
class btSpuCollisionPairCallback : public btOverlapCallback
|
||||||
|
{
|
||||||
|
btDispatcherInfo& m_dispatchInfo;
|
||||||
|
SpuGatheringCollisionDispatcher* m_dispatcher;
|
||||||
|
|
||||||
|
public:
|
||||||
|
|
||||||
|
btSpuCollisionPairCallback(btDispatcherInfo& dispatchInfo,SpuGatheringCollisionDispatcher* dispatcher)
|
||||||
|
:m_dispatchInfo(dispatchInfo),
|
||||||
|
m_dispatcher(dispatcher)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual bool processOverlap(btBroadphasePair& collisionPair)
|
||||||
|
{
|
||||||
|
|
||||||
|
|
||||||
|
//PPU version
|
||||||
|
//(*m_dispatcher->getNearCallback())(collisionPair,*m_dispatcher,m_dispatchInfo);
|
||||||
|
|
||||||
|
//only support discrete collision detection for now, we could fallback on PPU/unoptimized version for TOI/CCD
|
||||||
|
btAssert(m_dispatchInfo.m_dispatchFunc == btDispatcherInfo::DISPATCH_DISCRETE);
|
||||||
|
|
||||||
|
//by default, Bullet will use this near callback
|
||||||
|
{
|
||||||
|
///userInfo is used to determine if the SPU has to handle this case or not (skip PPU tasks)
|
||||||
|
if (!collisionPair.m_userInfo)
|
||||||
|
{
|
||||||
|
collisionPair.m_userInfo = (void*) 1;
|
||||||
|
}
|
||||||
|
if (!collisionPair.m_algorithm)
|
||||||
|
{
|
||||||
|
btCollisionObject* colObj0 = (btCollisionObject*)collisionPair.m_pProxy0->m_clientObject;
|
||||||
|
btCollisionObject* colObj1 = (btCollisionObject*)collisionPair.m_pProxy1->m_clientObject;
|
||||||
|
|
||||||
|
btCollisionAlgorithmConstructionInfo ci;
|
||||||
|
ci.m_dispatcher = m_dispatcher;
|
||||||
|
ci.m_manifold = 0;
|
||||||
|
|
||||||
|
if (m_dispatcher->needsCollision(colObj0,colObj1))
|
||||||
|
{
|
||||||
|
int proxyType0 = colObj0->getCollisionShape()->getShapeType();
|
||||||
|
int proxyType1 = colObj1->getCollisionShape()->getShapeType();
|
||||||
|
if (m_dispatcher->supportsDispatchPairOnSpu(proxyType0,proxyType1))
|
||||||
|
{
|
||||||
|
collisionPair.m_algorithm = new SpuContactManifoldCollisionAlgorithm(ci,colObj0,colObj1);
|
||||||
|
collisionPair.m_userInfo = (void*) 2;
|
||||||
|
} else
|
||||||
|
{
|
||||||
|
collisionPair.m_algorithm = m_dispatcher->findAlgorithm(colObj0,colObj1);
|
||||||
|
collisionPair.m_userInfo = (void*)3;
|
||||||
|
}
|
||||||
|
} else
|
||||||
|
{
|
||||||
|
//create an empty algorithm
|
||||||
|
collisionPair.m_algorithm = new btEmptyAlgorithm(ci);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
void SpuGatheringCollisionDispatcher::dispatchAllCollisionPairs(btOverlappingPairCache* pairCache,btDispatcherInfo& dispatchInfo)
|
||||||
|
{
|
||||||
|
//cellPerfInsertCBEpmBookmark(1);
|
||||||
|
|
||||||
|
if (dispatchInfo.m_enableSPU)
|
||||||
|
{
|
||||||
|
if (!m_spuCollisionTaskProcess)
|
||||||
|
m_spuCollisionTaskProcess = new SpuCollisionTaskProcess();
|
||||||
|
|
||||||
|
m_spuCollisionTaskProcess->initialize2();
|
||||||
|
|
||||||
|
///modified version of btCollisionDispatcher::dispatchAllCollisionPairs:
|
||||||
|
{
|
||||||
|
btSpuCollisionPairCallback collisionCallback(dispatchInfo,this);
|
||||||
|
|
||||||
|
pairCache->processAllOverlappingPairs(&collisionCallback);
|
||||||
|
}
|
||||||
|
|
||||||
|
//send one big batch
|
||||||
|
int numTotalPairs = pairCache->getNumOverlappingPairs();
|
||||||
|
btBroadphasePair* pairPtr = pairCache->getOverlappingPairArrayPtr();
|
||||||
|
int i;
|
||||||
|
for (i=0;i<numTotalPairs;)
|
||||||
|
{
|
||||||
|
//Performance Hint: tweak this number during benchmarking
|
||||||
|
static const int pairRange = SPU_BATCHSIZE_BROADPHASE_PAIRS;
|
||||||
|
int endIndex = (i+pairRange) < numTotalPairs ? i+pairRange : numTotalPairs;
|
||||||
|
m_spuCollisionTaskProcess->addWorkToTask(pairPtr,i,endIndex);
|
||||||
|
i = endIndex;
|
||||||
|
}
|
||||||
|
|
||||||
|
//handle PPU fallback pairs
|
||||||
|
for (i=0;i<numTotalPairs;i++)
|
||||||
|
{
|
||||||
|
btBroadphasePair& collisionPair = pairPtr[i];
|
||||||
|
if (collisionPair.m_userInfo == (void*)3)
|
||||||
|
{
|
||||||
|
if (collisionPair.m_algorithm)
|
||||||
|
{
|
||||||
|
btCollisionObject* colObj0 = (btCollisionObject*)collisionPair.m_pProxy0->m_clientObject;
|
||||||
|
btCollisionObject* colObj1 = (btCollisionObject*)collisionPair.m_pProxy1->m_clientObject;
|
||||||
|
btManifoldResult contactPointResult(colObj0,colObj1);
|
||||||
|
|
||||||
|
if (dispatchInfo.m_dispatchFunc == btDispatcherInfo::DISPATCH_DISCRETE)
|
||||||
|
{
|
||||||
|
//discrete collision detection query
|
||||||
|
collisionPair.m_algorithm->processCollision(colObj0,colObj1,dispatchInfo,&contactPointResult);
|
||||||
|
} else
|
||||||
|
{
|
||||||
|
//continuous collision detection query, time of impact (toi)
|
||||||
|
btScalar toi = collisionPair.m_algorithm->calculateTimeOfImpact(colObj0,colObj1,dispatchInfo,&contactPointResult);
|
||||||
|
if (dispatchInfo.m_timeOfImpact > toi)
|
||||||
|
dispatchInfo.m_timeOfImpact = toi;
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//make sure all SPU work is done
|
||||||
|
m_spuCollisionTaskProcess->flush2();
|
||||||
|
|
||||||
|
} else
|
||||||
|
{
|
||||||
|
///PPU fallback
|
||||||
|
///!Need to make sure to clear all 'algorithms' when switching between SPU and PPU
|
||||||
|
btCollisionDispatcher::dispatchAllCollisionPairs(pairCache,dispatchInfo);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
58
Extras/BulletMultiThreaded/SpuGatheringCollisionDispatcher.h
Normal file
58
Extras/BulletMultiThreaded/SpuGatheringCollisionDispatcher.h
Normal file
@@ -0,0 +1,58 @@
|
|||||||
|
/*
|
||||||
|
Bullet Continuous Collision Detection and Physics Library
|
||||||
|
Copyright (c) 2003-2007 Erwin Coumans http://bulletphysics.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 SPU_GATHERING_COLLISION__DISPATCHER_H
|
||||||
|
#define SPU_GATHERING_COLLISION__DISPATCHER_H
|
||||||
|
|
||||||
|
#include "BulletCollision/CollisionDispatch/btCollisionDispatcher.h"
|
||||||
|
|
||||||
|
|
||||||
|
///Tuning value to optimized SPU utilization
|
||||||
|
///Too small value means Task overhead is large compared to computation (too fine granularity)
|
||||||
|
///Too big value might render some SPUs are idle, while a few other SPUs are doing all work.
|
||||||
|
//#define SPU_BATCHSIZE_BROADPHASE_PAIRS 16
|
||||||
|
#define SPU_BATCHSIZE_BROADPHASE_PAIRS 256
|
||||||
|
|
||||||
|
|
||||||
|
class SpuCollisionTaskProcess;
|
||||||
|
|
||||||
|
///SpuGatheringCollisionDispatcher can use SPU to gather and calculate collision detection
|
||||||
|
///Time of Impact, Closest Points and Penetration Depth.
|
||||||
|
class SpuGatheringCollisionDispatcher : public btCollisionDispatcher
|
||||||
|
{
|
||||||
|
|
||||||
|
SpuCollisionTaskProcess* m_spuCollisionTaskProcess;
|
||||||
|
|
||||||
|
|
||||||
|
public:
|
||||||
|
|
||||||
|
//can be used by SPU collision algorithms
|
||||||
|
SpuCollisionTaskProcess* getSpuCollisionTaskProcess()
|
||||||
|
{
|
||||||
|
return m_spuCollisionTaskProcess;
|
||||||
|
}
|
||||||
|
|
||||||
|
SpuGatheringCollisionDispatcher ();
|
||||||
|
|
||||||
|
virtual ~SpuGatheringCollisionDispatcher();
|
||||||
|
|
||||||
|
bool supportsDispatchPairOnSpu(int proxyType0,int proxyType1);
|
||||||
|
|
||||||
|
virtual void dispatchAllCollisionPairs(btOverlappingPairCache* pairCache,btDispatcherInfo& dispatchInfo);
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#endif //SPU_GATHERING_COLLISION__DISPATCHER_H
|
||||||
1
Extras/BulletMultiThreaded/SpuIntegrationTask/readme.txt
Normal file
1
Extras/BulletMultiThreaded/SpuIntegrationTask/readme.txt
Normal file
@@ -0,0 +1 @@
|
|||||||
|
Empty placeholder for future Libspe2 SPU task
|
||||||
224
Extras/BulletMultiThreaded/SpuLibspe2Support.cpp
Normal file
224
Extras/BulletMultiThreaded/SpuLibspe2Support.cpp
Normal file
@@ -0,0 +1,224 @@
|
|||||||
|
/*
|
||||||
|
Bullet Continuous Collision Detection and Physics Library
|
||||||
|
Copyright (c) 2003-2007 Erwin Coumans http://bulletphysics.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.
|
||||||
|
*/
|
||||||
|
#ifdef USE_LIBSPE2
|
||||||
|
|
||||||
|
#include "SpuLibspe2Support.h"
|
||||||
|
|
||||||
|
#include "SpuCollisionTaskProcess.h"
|
||||||
|
#include "SpuNarrowPhaseCollisionTask/SpuGatheringCollisionTask.h"
|
||||||
|
|
||||||
|
#include <Windows.h>
|
||||||
|
|
||||||
|
///SpuLibspe2Support helps to initialize/shutdown libspe2, start/stop SPU tasks and communication
|
||||||
|
///Setup and initialize SPU/CELL/Libspe2
|
||||||
|
SpuLibspe2Support::SpuLibspe2Support(SpuLibspe2ElfId_t elfId,int numThreads)
|
||||||
|
{
|
||||||
|
startSPUs(numThreads);
|
||||||
|
}
|
||||||
|
|
||||||
|
///cleanup/shutdown Libspe2
|
||||||
|
SpuLibspe2Support::~SpuLibspe2Support()
|
||||||
|
{
|
||||||
|
stopSPUs();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
|
||||||
|
DWORD WINAPI Thread_no_1( LPVOID lpParam )
|
||||||
|
{
|
||||||
|
|
||||||
|
btSpuStatus* status = (btSpuStatus*)lpParam;
|
||||||
|
|
||||||
|
|
||||||
|
while (1)
|
||||||
|
{
|
||||||
|
WaitForSingleObject(status->m_eventStartHandle,INFINITE);
|
||||||
|
btAssert(status->m_status);
|
||||||
|
|
||||||
|
SpuGatherAndProcessPairsTaskDesc* taskDesc = status->m_taskDesc;
|
||||||
|
|
||||||
|
if (taskDesc)
|
||||||
|
{
|
||||||
|
processCollisionTask(*taskDesc);
|
||||||
|
SetEvent(status->m_eventCompletetHandle);
|
||||||
|
} else
|
||||||
|
{
|
||||||
|
//exit Thread
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
///send messages to SPUs
|
||||||
|
void SpuLibspe2Support::sendRequest(uint32_t uiCommand, uint32_t uiArgument0, uint32_t uiArgument1)
|
||||||
|
{
|
||||||
|
/// gMidphaseSPU.sendRequest(CMD_GATHER_AND_PROCESS_PAIRLIST, (uint32_t) &taskDesc);
|
||||||
|
|
||||||
|
///we should spawn an SPU task here, and in 'waitForResponse' it should wait for response of the (one of) the first tasks that finished
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
switch (uiCommand)
|
||||||
|
{
|
||||||
|
case CMD_GATHER_AND_PROCESS_PAIRLIST:
|
||||||
|
{
|
||||||
|
|
||||||
|
SpuGatherAndProcessPairsTaskDesc* taskDesc = (SpuGatherAndProcessPairsTaskDesc*) uiArgument0 ;
|
||||||
|
|
||||||
|
//#define SINGLE_THREADED 1
|
||||||
|
#ifdef SINGLE_THREADED
|
||||||
|
|
||||||
|
btSpuStatus& spuStatus = m_activeSpuStatus[0];
|
||||||
|
taskDesc->m_lsMemory = (CollisionTask_LocalStoreMemory*)spuStatus.m_lsMemory;
|
||||||
|
processCollisionTask(*taskDesc);
|
||||||
|
HANDLE handle =0;
|
||||||
|
#else
|
||||||
|
|
||||||
|
btAssert(taskDesc->taskId>=0);
|
||||||
|
btAssert(taskDesc->taskId<m_activeSpuStatus.size());
|
||||||
|
|
||||||
|
btSpuStatus& spuStatus = m_activeSpuStatus[taskDesc->taskId];
|
||||||
|
|
||||||
|
spuStatus.m_commandId = uiCommand;
|
||||||
|
spuStatus.m_status = 1;
|
||||||
|
spuStatus.m_taskDesc = taskDesc;
|
||||||
|
taskDesc->m_lsMemory = (CollisionTask_LocalStoreMemory*)spuStatus.m_lsMemory;
|
||||||
|
///fire event to start new task
|
||||||
|
SetEvent(spuStatus.m_eventStartHandle);
|
||||||
|
|
||||||
|
#endif //CollisionTask_LocalStoreMemory
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
{
|
||||||
|
///not implemented
|
||||||
|
btAssert(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
///check for messages from SPUs
|
||||||
|
void SpuLibspe2Support::waitForResponse(unsigned int *puiArgument0, unsigned int *puiArgument1)
|
||||||
|
{
|
||||||
|
///We should wait for (one of) the first tasks to finish (or other SPU messages), and report its response
|
||||||
|
|
||||||
|
///A possible response can be 'yes, SPU handled it', or 'no, please do a PPU fallback'
|
||||||
|
|
||||||
|
|
||||||
|
btAssert(m_activeSpuStatus.size());
|
||||||
|
|
||||||
|
int last = -1;
|
||||||
|
|
||||||
|
//find an active spu/thread
|
||||||
|
for (int i=0;i<m_activeSpuStatus.size();i++)
|
||||||
|
{
|
||||||
|
if (m_activeSpuStatus[i].m_status)
|
||||||
|
{
|
||||||
|
last = i;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#ifndef SINGLE_THREADED
|
||||||
|
btAssert(spuStatus.m_threadHandle);
|
||||||
|
btAssert(spuStatus.m_eventCompletetHandle);
|
||||||
|
btSpuStatus& spuStatus = m_activeSpuStatus[last];
|
||||||
|
WaitForSingleObject(spuStatus.m_eventCompletetHandle, INFINITE);
|
||||||
|
spuStatus.m_status = 0;
|
||||||
|
|
||||||
|
///need to find an active spu
|
||||||
|
btAssert(last>=0);
|
||||||
|
|
||||||
|
#else
|
||||||
|
last=0;
|
||||||
|
btSpuStatus& spuStatus = m_activeSpuStatus[last];
|
||||||
|
#endif //SINGLE_THREADED
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
*puiArgument0 = spuStatus.m_taskId;
|
||||||
|
*puiArgument1 = spuStatus.m_status;
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
///start the spus group (can be called at the beginning of each frame, to make sure that the right SPU program is loaded)
|
||||||
|
void SpuLibspe2Support::startSPUs(int numThreads)
|
||||||
|
{
|
||||||
|
|
||||||
|
m_activeSpuStatus.resize(numThreads);
|
||||||
|
|
||||||
|
for (int i=0;i<numThreads;i++)
|
||||||
|
{
|
||||||
|
printf("starting thread %d\n",i);
|
||||||
|
|
||||||
|
btSpuStatus& spuStatus = m_activeSpuStatus[i];
|
||||||
|
|
||||||
|
LPSECURITY_ATTRIBUTES lpThreadAttributes=NULL;
|
||||||
|
SIZE_T dwStackSize=65535;
|
||||||
|
LPTHREAD_START_ROUTINE lpStartAddress=&Thread_no_1;
|
||||||
|
LPVOID lpParameter=&spuStatus;
|
||||||
|
DWORD dwCreationFlags=0;
|
||||||
|
LPDWORD lpThreadId=0;
|
||||||
|
|
||||||
|
spuStatus.m_taskDesc = 0;
|
||||||
|
|
||||||
|
sprintf(spuStatus.m_eventStartHandleName,"eventStart%d",i);
|
||||||
|
spuStatus.m_eventStartHandle = CreateEvent(0,false,false,spuStatus.m_eventStartHandleName);
|
||||||
|
|
||||||
|
sprintf(spuStatus.m_eventCompletetHandleName,"eventComplete%d",i);
|
||||||
|
spuStatus.m_eventCompletetHandle = CreateEvent(0,false,false,spuStatus.m_eventCompletetHandleName);
|
||||||
|
|
||||||
|
|
||||||
|
HANDLE handle = CreateThread(lpThreadAttributes,dwStackSize,lpStartAddress,lpParameter, dwCreationFlags,lpThreadId);
|
||||||
|
SetThreadPriority(handle,THREAD_PRIORITY_TIME_CRITICAL);
|
||||||
|
|
||||||
|
spuStatus.m_taskId = i;
|
||||||
|
spuStatus.m_commandId = 0;
|
||||||
|
spuStatus.m_status = 0;
|
||||||
|
spuStatus.m_threadHandle = handle;
|
||||||
|
spuStatus.m_lsMemory = createLocalStoreMemory();
|
||||||
|
|
||||||
|
printf("started thread %d with threadHandle %d\n",i,handle);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
///tell the task scheduler we are done with the SPU tasks
|
||||||
|
void SpuLibspe2Support::stopSPUs()
|
||||||
|
{
|
||||||
|
// m_activeSpuStatus.pop_back();
|
||||||
|
// WaitForSingleObject(spuStatus.bla, INFINITE);
|
||||||
|
// CloseHandle(spuStatus.m_threadHandle);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif// USE_LIBSPE2
|
||||||
|
|
||||||
102
Extras/BulletMultiThreaded/SpuLibspe2Support.h
Normal file
102
Extras/BulletMultiThreaded/SpuLibspe2Support.h
Normal file
@@ -0,0 +1,102 @@
|
|||||||
|
/*
|
||||||
|
Bullet Continuous Collision Detection and Physics Library
|
||||||
|
Copyright (c) 2003-2007 Erwin Coumans http://bulletphysics.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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
///USE_LIBSPE2: this define should be in the build system, or in <LinearMath/btScalar.h>
|
||||||
|
//#define USE_LIBSPE2 1
|
||||||
|
#ifdef USE_LIBSPE2
|
||||||
|
|
||||||
|
#ifndef SPU_LIBSPE2_SUPPORT_H
|
||||||
|
#define SPU_LIBSPE2_SUPPORT_H
|
||||||
|
|
||||||
|
#include "LinearMath/btAlignedObjectArray.h"
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Note:
|
||||||
|
* The order of elements in this enum are important, that's why each one is explicitly
|
||||||
|
* given a value. They will correspond to the .elf names/addresses that will be
|
||||||
|
* loaded into Libspe2.
|
||||||
|
* Mixing up these values will cause the wrong code to execute, for instance, the
|
||||||
|
* solver may be asked to do a collision detection job.
|
||||||
|
*/
|
||||||
|
typedef enum {
|
||||||
|
SPU_ELF_COLLISION_DETECTION=0,
|
||||||
|
SPU_ELF_SAMPLE,
|
||||||
|
//SPU_ELF_INTEGRATION,
|
||||||
|
//SPU_ELF_SOLVER,
|
||||||
|
SPU_ELF_LAST,
|
||||||
|
} SpuLibspe2ElfId_t;
|
||||||
|
|
||||||
|
#ifdef WIN32
|
||||||
|
#include <malloc.h>
|
||||||
|
#define memalign(alignment, size) malloc(size);
|
||||||
|
#else
|
||||||
|
#include <stdlib.h>
|
||||||
|
#endif // WIN32
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#include <LinearMath/btScalar.h> //for uint32_t etc.
|
||||||
|
|
||||||
|
///placeholder, until libspe2 support is there
|
||||||
|
struct btSpuStatus
|
||||||
|
{
|
||||||
|
uint32_t m_taskId;
|
||||||
|
uint32_t m_commandId;
|
||||||
|
uint32_t m_status;
|
||||||
|
|
||||||
|
struct SpuGatherAndProcessPairsTaskDesc* m_taskDesc;
|
||||||
|
|
||||||
|
void* m_threadHandle;
|
||||||
|
void* m_lsMemory;
|
||||||
|
|
||||||
|
void* m_eventStartHandle;
|
||||||
|
char m_eventStartHandleName[32];
|
||||||
|
|
||||||
|
void* m_eventCompletetHandle;
|
||||||
|
char m_eventCompletetHandleName[32];
|
||||||
|
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
///SpuLibspe2Support helps to initialize/shutdown libspe2, start/stop SPU tasks and communication
|
||||||
|
class SpuLibspe2Support {
|
||||||
|
|
||||||
|
btAlignedObjectArray<btSpuStatus> m_activeSpuStatus;
|
||||||
|
|
||||||
|
public:
|
||||||
|
///Setup and initialize SPU/CELL/Libspe2
|
||||||
|
SpuLibspe2Support(SpuLibspe2ElfId_t elfId,int numThreads);
|
||||||
|
|
||||||
|
///cleanup/shutdown Libspe2
|
||||||
|
~SpuLibspe2Support();
|
||||||
|
|
||||||
|
///send messages to SPUs
|
||||||
|
void sendRequest(uint32_t uiCommand, uint32_t uiArgument0, uint32_t uiArgument1=0);
|
||||||
|
|
||||||
|
///check for messages from SPUs
|
||||||
|
void waitForResponse(unsigned int *puiArgument0, unsigned int *puiArgument1);
|
||||||
|
|
||||||
|
///start the spus (can be called at the beginning of each frame, to make sure that the right SPU program is loaded)
|
||||||
|
void startSPUs(int numThreads);
|
||||||
|
|
||||||
|
///tell the task scheduler we are done with the SPU tasks
|
||||||
|
void stopSPUs();
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif //SPU_LIBSPE2_SUPPORT_H
|
||||||
|
|
||||||
|
#endif //USE_LIBSPE2
|
||||||
@@ -0,0 +1,162 @@
|
|||||||
|
/* [SCE CONFIDENTIAL DOCUMENT]
|
||||||
|
* PLAYSTATION(R)3 SPU Optimized Bullet Physics Library (http://bulletphysics.com)
|
||||||
|
* Copyright (C) 2007 Sony Computer Entertainment Inc.
|
||||||
|
* All Rights Reserved.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "SpuContactResult.h"
|
||||||
|
|
||||||
|
|
||||||
|
//#define DEBUG_SPU_COLLISION_DETECTION 1
|
||||||
|
|
||||||
|
|
||||||
|
#include "SpuContactResult.h"
|
||||||
|
|
||||||
|
|
||||||
|
SpuContactResult::SpuContactResult()
|
||||||
|
{
|
||||||
|
m_manifoldAddress = 0;
|
||||||
|
m_spuManifold = NULL;
|
||||||
|
m_RequiresWriteBack = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
SpuContactResult::~SpuContactResult()
|
||||||
|
{
|
||||||
|
g_manifoldDmaExport.swapBuffers();
|
||||||
|
}
|
||||||
|
|
||||||
|
void SpuContactResult::setContactInfo(btPersistentManifold* spuManifold, uint64_t manifoldAddress,const btTransform& worldTrans0,const btTransform& worldTrans1)
|
||||||
|
{
|
||||||
|
// spu_printf("SpuContactResult::setContactInfo\n");
|
||||||
|
m_rootWorldTransform0 = worldTrans0;
|
||||||
|
m_rootWorldTransform1 = worldTrans1;
|
||||||
|
m_manifoldAddress = manifoldAddress;
|
||||||
|
m_spuManifold = spuManifold;
|
||||||
|
}
|
||||||
|
|
||||||
|
void SpuContactResult::setShapeIdentifiers(int partId0,int index0, int partId1,int index1)
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
///return true if it requires a dma transfer back
|
||||||
|
bool ManifoldResultAddContactPoint(const btVector3& normalOnBInWorld,
|
||||||
|
const btVector3& pointInWorld,
|
||||||
|
float depth,
|
||||||
|
btPersistentManifold* manifoldPtr,
|
||||||
|
btTransform& transA,
|
||||||
|
btTransform& transB
|
||||||
|
)
|
||||||
|
{
|
||||||
|
|
||||||
|
float contactTreshold = manifoldPtr->getContactBreakingThreshold();
|
||||||
|
|
||||||
|
//spu_printf("SPU: add contactpoint, depth:%f, contactTreshold %f, manifoldPtr %llx\n",depth,contactTreshold,manifoldPtr);
|
||||||
|
|
||||||
|
#ifdef DEBUG_SPU_COLLISION_DETECTION
|
||||||
|
spu_printf("SPU: contactTreshold %f\n",contactTreshold);
|
||||||
|
#endif //DEBUG_SPU_COLLISION_DETECTION
|
||||||
|
if (depth > manifoldPtr->getContactBreakingThreshold())
|
||||||
|
return false;
|
||||||
|
|
||||||
|
//provide inverses or just calculate?
|
||||||
|
btTransform transAInv = transA.inverse();//m_body0->m_cachedInvertedWorldTransform;
|
||||||
|
btTransform transBInv= transB.inverse();//m_body1->m_cachedInvertedWorldTransform;
|
||||||
|
|
||||||
|
btVector3 pointA = pointInWorld + normalOnBInWorld * depth;
|
||||||
|
btVector3 localA = transAInv(pointA );
|
||||||
|
btVector3 localB = transBInv(pointInWorld);
|
||||||
|
btManifoldPoint newPt(localA,localB,normalOnBInWorld,depth);
|
||||||
|
|
||||||
|
int insertIndex = manifoldPtr->getCacheEntry(newPt);
|
||||||
|
if (insertIndex >= 0)
|
||||||
|
{
|
||||||
|
// manifoldPtr->replaceContactPoint(newPt,insertIndex);
|
||||||
|
// return true;
|
||||||
|
|
||||||
|
#ifdef DEBUG_SPU_COLLISION_DETECTION
|
||||||
|
spu_printf("SPU: same contact detected, nothing done\n");
|
||||||
|
#endif //DEBUG_SPU_COLLISION_DETECTION
|
||||||
|
// This is not needed, just use the old info! saves a DMA transfer as well
|
||||||
|
} else
|
||||||
|
{
|
||||||
|
|
||||||
|
newPt.m_combinedFriction = 0.25f;//calculateCombinedFriction(m_body0,m_body1);
|
||||||
|
newPt.m_combinedRestitution = 0.0f;//calculateCombinedRestitution(m_body0,m_body1);
|
||||||
|
|
||||||
|
/*
|
||||||
|
//potential TODO: SPU callbacks, either immediate (local on the SPU), or deferred
|
||||||
|
//User can override friction and/or restitution
|
||||||
|
if (gContactAddedCallback &&
|
||||||
|
//and if either of the two bodies requires custom material
|
||||||
|
((m_body0->m_collisionFlags & btCollisionObject::customMaterialCallback) ||
|
||||||
|
(m_body1->m_collisionFlags & btCollisionObject::customMaterialCallback)))
|
||||||
|
{
|
||||||
|
//experimental feature info, for per-triangle material etc.
|
||||||
|
(*gContactAddedCallback)(newPt,m_body0,m_partId0,m_index0,m_body1,m_partId1,m_index1);
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
manifoldPtr->AddManifoldPoint(newPt);
|
||||||
|
return true;
|
||||||
|
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void SpuContactResult::writeDoubleBufferedManifold(btPersistentManifold* lsManifold, btPersistentManifold* mmManifold)
|
||||||
|
{
|
||||||
|
memcpy(g_manifoldDmaExport.getFront(),lsManifold,sizeof(btPersistentManifold));
|
||||||
|
|
||||||
|
g_manifoldDmaExport.swapBuffers();
|
||||||
|
g_manifoldDmaExport.backBufferDmaPut((uint64_t)mmManifold, sizeof(btPersistentManifold), DMA_TAG(9));
|
||||||
|
// Should there be any kind of wait here? What if somebody tries to use this tag again? What if we call this function again really soon?
|
||||||
|
//no, the swapBuffers does the wait
|
||||||
|
}
|
||||||
|
|
||||||
|
void SpuContactResult::addContactPoint(const btVector3& normalOnBInWorld,const btPoint3& pointInWorld,float depth)
|
||||||
|
{
|
||||||
|
// spu_printf("*** SpuContactResult::addContactPoint: depth = %f\n",depth);
|
||||||
|
|
||||||
|
#ifdef DEBUG_SPU_COLLISION_DETECTION
|
||||||
|
// int sman = sizeof(rage::phManifold);
|
||||||
|
// spu_printf("sizeof_manifold = %i\n",sman);
|
||||||
|
#endif //DEBUG_SPU_COLLISION_DETECTION
|
||||||
|
|
||||||
|
btPersistentManifold* localManifold = m_spuManifold;
|
||||||
|
|
||||||
|
btVector3 normalB(normalOnBInWorld.getX(),normalOnBInWorld.getY(),normalOnBInWorld.getZ());
|
||||||
|
btVector3 pointWrld(pointInWorld.getX(),pointInWorld.getY(),pointInWorld.getZ());
|
||||||
|
|
||||||
|
//process the contact point
|
||||||
|
const bool retVal = ManifoldResultAddContactPoint(normalB,
|
||||||
|
pointWrld,
|
||||||
|
depth,
|
||||||
|
localManifold,
|
||||||
|
m_rootWorldTransform0,
|
||||||
|
m_rootWorldTransform1
|
||||||
|
);
|
||||||
|
m_RequiresWriteBack = m_RequiresWriteBack || retVal;
|
||||||
|
}
|
||||||
|
|
||||||
|
void SpuContactResult::flush()
|
||||||
|
{
|
||||||
|
if (m_RequiresWriteBack)
|
||||||
|
{
|
||||||
|
#ifdef DEBUG_SPU_COLLISION_DETECTION
|
||||||
|
spu_printf("SPU: Start rage::phManifold Write (Put) DMA\n");
|
||||||
|
#endif //DEBUG_SPU_COLLISION_DETECTION
|
||||||
|
// spu_printf("writeDoubleBufferedManifold\n");
|
||||||
|
writeDoubleBufferedManifold(m_spuManifold, (btPersistentManifold*)m_manifoldAddress);
|
||||||
|
#ifdef DEBUG_SPU_COLLISION_DETECTION
|
||||||
|
spu_printf("SPU: Finished (Put) DMA\n");
|
||||||
|
#endif //DEBUG_SPU_COLLISION_DETECTION
|
||||||
|
}
|
||||||
|
m_spuManifold = NULL;
|
||||||
|
m_RequiresWriteBack = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -0,0 +1,101 @@
|
|||||||
|
#ifndef SPU_CONTACT_RESULT2_H
|
||||||
|
#define SPU_CONTACT_RESULT2_H
|
||||||
|
|
||||||
|
|
||||||
|
#ifndef WIN32
|
||||||
|
#include <stdint.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#ifdef WIN32
|
||||||
|
#include "SpuDoubleBuffer.h"
|
||||||
|
#else
|
||||||
|
#include "SPU_Common/SpuDefines.h"
|
||||||
|
#include "SPU_Common/SpuDoubleBuffer.h"
|
||||||
|
#include <spu_printf.h>
|
||||||
|
#endif //WIN32
|
||||||
|
|
||||||
|
|
||||||
|
#include "LinearMath/btTransform.h"
|
||||||
|
#include "LinearMath/btPoint3.h"
|
||||||
|
|
||||||
|
|
||||||
|
#include "BulletCollision/NarrowPhaseCollision/btPersistentManifold.h"
|
||||||
|
|
||||||
|
|
||||||
|
struct SpuCollisionPairInput
|
||||||
|
{
|
||||||
|
uint64_t m_collisionShapes[2];
|
||||||
|
void* m_spuCollisionShapes[2];
|
||||||
|
|
||||||
|
uint64_t m_persistentManifoldPtr;
|
||||||
|
btVector3 m_primitiveDimensions0;
|
||||||
|
btVector3 m_primitiveDimensions1;
|
||||||
|
int m_shapeType0;
|
||||||
|
int m_shapeType1;
|
||||||
|
float m_collisionMargin0;
|
||||||
|
float m_collisionMargin1;
|
||||||
|
|
||||||
|
btTransform m_worldTransform0;
|
||||||
|
btTransform m_worldTransform1;
|
||||||
|
|
||||||
|
bool m_isSwapped;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
struct SpuClosestPointInput
|
||||||
|
{
|
||||||
|
SpuClosestPointInput()
|
||||||
|
:m_maximumDistanceSquared(float(1e30)),
|
||||||
|
m_stackAlloc(0)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
btTransform m_transformA;
|
||||||
|
btTransform m_transformB;
|
||||||
|
float m_maximumDistanceSquared;
|
||||||
|
class btStackAlloc* m_stackAlloc;
|
||||||
|
struct SpuConvexPolyhedronVertexData* m_convexVertexData;
|
||||||
|
};
|
||||||
|
|
||||||
|
///SpuContactResult exports the contact points using double-buffered DMA transfers, only when needed
|
||||||
|
///So when an existing contact point is duplicated, no transfer/refresh is performed.
|
||||||
|
class SpuContactResult
|
||||||
|
{
|
||||||
|
btTransform m_rootWorldTransform0;
|
||||||
|
btTransform m_rootWorldTransform1;
|
||||||
|
uint64_t m_manifoldAddress;
|
||||||
|
|
||||||
|
btPersistentManifold* m_spuManifold;
|
||||||
|
bool m_RequiresWriteBack;
|
||||||
|
|
||||||
|
DoubleBuffer<btPersistentManifold, 1> g_manifoldDmaExport;
|
||||||
|
|
||||||
|
public:
|
||||||
|
SpuContactResult();
|
||||||
|
virtual ~SpuContactResult();
|
||||||
|
|
||||||
|
btPersistentManifold* GetSpuManifold() const
|
||||||
|
{
|
||||||
|
return m_spuManifold;
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual void setShapeIdentifiers(int partId0,int index0, int partId1,int index1);
|
||||||
|
|
||||||
|
void setContactInfo(btPersistentManifold* spuManifold, uint64_t manifoldAddress,const btTransform& worldTrans0,const btTransform& worldTrans1);
|
||||||
|
|
||||||
|
void writeDoubleBufferedManifold(btPersistentManifold* lsManifold, btPersistentManifold* mmManifold);
|
||||||
|
|
||||||
|
virtual void addContactPoint(const btVector3& normalOnBInWorld,const btPoint3& pointInWorld,float depth);
|
||||||
|
|
||||||
|
void flush();
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#endif //SPU_CONTACT_RESULT2_H
|
||||||
|
|
||||||
@@ -0,0 +1,56 @@
|
|||||||
|
/* [SCE CONFIDENTIAL DOCUMENT]
|
||||||
|
* PLAYSTATION(R)3 SPU Optimized Bullet Physics Library (http://bulletphysics.com)
|
||||||
|
* Copyright (C) 2007 Sony Computer Entertainment Inc.
|
||||||
|
* All Rights Reserved.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
Bullet Continuous Collision Detection and Physics Library
|
||||||
|
Copyright (c) 2003-2006 Erwin Coumans http://continuousphysics.com/Bullet/
|
||||||
|
|
||||||
|
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 CONVEX_PENETRATION_DEPTH_H
|
||||||
|
#define CONVEX_PENETRATION_DEPTH_H
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
class btStackAlloc;
|
||||||
|
class btIDebugDraw;
|
||||||
|
class SpuVoronoiSimplexSolver;
|
||||||
|
|
||||||
|
#include <LinearMath/btTransform.h>
|
||||||
|
#include <LinearMath/btPoint3.h>
|
||||||
|
|
||||||
|
|
||||||
|
///ConvexPenetrationDepthSolver provides an interface for penetration depth calculation.
|
||||||
|
class SpuConvexPenetrationDepthSolver
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
|
||||||
|
virtual ~SpuConvexPenetrationDepthSolver() {};
|
||||||
|
virtual bool calcPenDepth( SpuVoronoiSimplexSolver& simplexSolver,
|
||||||
|
void* convexA,void* convexB,int shapeTypeA, int shapeTypeB, float marginA, float marginB,
|
||||||
|
btTransform& transA,const btTransform& transB,
|
||||||
|
btVector3& v, btPoint3& pa, btPoint3& pb,
|
||||||
|
class btIDebugDraw* debugDraw,btStackAlloc* stackAlloc,
|
||||||
|
struct SpuConvexPolyhedronVertexData* convexVertexData
|
||||||
|
) const = 0;
|
||||||
|
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#endif //CONVEX_PENETRATION_DEPTH_H
|
||||||
|
|
||||||
@@ -0,0 +1,399 @@
|
|||||||
|
|
||||||
|
#include "SpuGatheringCollisionTask.h"
|
||||||
|
|
||||||
|
#include "SpuDoubleBuffer.h"
|
||||||
|
|
||||||
|
#include "../SpuCollisionTaskProcess.h"
|
||||||
|
#include "../SpuGatheringCollisionDispatcher.h" //for SPU_BATCHSIZE_BROADPHASE_PAIRS
|
||||||
|
|
||||||
|
#include "BulletCollision/BroadphaseCollision/btBroadphaseProxy.h"
|
||||||
|
#include "SpuContactManifoldCollisionAlgorithm.h"
|
||||||
|
#include "BulletCollision/CollisionDispatch/btCollisionObject.h"
|
||||||
|
#include "SpuContactResult.h"
|
||||||
|
#include "BulletCollision/CollisionShapes/btOptimizedBvh.h"
|
||||||
|
#include "BulletCollision/CollisionShapes/btTriangleIndexVertexArray.h"
|
||||||
|
#include "BulletCollision/CollisionShapes/btConvexShape.h"
|
||||||
|
#include "BulletCollision/CollisionShapes/btBvhTriangleMeshShape.h"
|
||||||
|
#include "SpuMinkowskiPenetrationDepthSolver.h"
|
||||||
|
#include "SpuGjkPairDetector.h"
|
||||||
|
#include "SpuVoronoiSimplexSolver.h"
|
||||||
|
|
||||||
|
#include "SpuLocalSupport.h" //definition of SpuConvexPolyhedronVertexData
|
||||||
|
|
||||||
|
|
||||||
|
#ifdef WIN32
|
||||||
|
#define spu_printf printf
|
||||||
|
#include <stdio.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
//int gNumConvexPoints0=0;
|
||||||
|
|
||||||
|
///Make sure no destructors are called on this memory
|
||||||
|
struct CollisionTask_LocalStoreMemory
|
||||||
|
{
|
||||||
|
|
||||||
|
DoubleBuffer<unsigned char, MIDPHASE_WORKUNIT_PAGE_SIZE> g_workUnitTaskBuffers;
|
||||||
|
btBroadphasePair gBroadphasePairs[SPU_BATCHSIZE_BROADPHASE_PAIRS];
|
||||||
|
//SpuContactManifoldCollisionAlgorithm gSpuContactManifoldAlgo;
|
||||||
|
ATTRIBUTE_ALIGNED16(char gSpuContactManifoldAlgo[sizeof(SpuContactManifoldCollisionAlgorithm)+128]);
|
||||||
|
SpuContactManifoldCollisionAlgorithm* getlocalCollisionAlgorithm()
|
||||||
|
{
|
||||||
|
return (SpuContactManifoldCollisionAlgorithm*)&gSpuContactManifoldAlgo;
|
||||||
|
|
||||||
|
}
|
||||||
|
btPersistentManifold gPersistentManifold;
|
||||||
|
btBroadphaseProxy gProxy0;
|
||||||
|
btBroadphaseProxy gProxy1;
|
||||||
|
btCollisionObject gColObj0;
|
||||||
|
btCollisionObject gColObj1;
|
||||||
|
|
||||||
|
static const int maxShapeSize = 256;//todo: make some compile-time assert that this is value is larger then sizeof(btCollisionShape)
|
||||||
|
|
||||||
|
ATTRIBUTE_ALIGNED16(char gCollisionShape0[maxShapeSize]);
|
||||||
|
ATTRIBUTE_ALIGNED16(char gCollisionShape1[maxShapeSize]);
|
||||||
|
|
||||||
|
ATTRIBUTE_ALIGNED16(btScalar spuUnscaledVertex[4]);
|
||||||
|
ATTRIBUTE_ALIGNED16(int spuIndices[16]);
|
||||||
|
|
||||||
|
ATTRIBUTE_ALIGNED16(btOptimizedBvh gOptimizedBvh);
|
||||||
|
ATTRIBUTE_ALIGNED16(btTriangleIndexVertexArray gTriangleMeshInterface);
|
||||||
|
///only a single mesh part for now, we can add support for multiple parts, but quantized trees don't support this at the moment
|
||||||
|
ATTRIBUTE_ALIGNED16(btIndexedMesh gIndexMesh);
|
||||||
|
|
||||||
|
#define MAX_SPU_SUBTREE_HEADERS 32
|
||||||
|
//1024
|
||||||
|
ATTRIBUTE_ALIGNED16(btBvhSubtreeInfo gSubtreeHeaders[MAX_SPU_SUBTREE_HEADERS]);
|
||||||
|
ATTRIBUTE_ALIGNED16(btQuantizedBvhNode gSubtreeNodes[MAX_SUBTREE_SIZE_IN_BYTES/sizeof(btQuantizedBvhNode)]);
|
||||||
|
|
||||||
|
SpuConvexPolyhedronVertexData* convexVertexData;
|
||||||
|
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
void* createCollisionLocalStoreMemory()
|
||||||
|
{
|
||||||
|
return new CollisionTask_LocalStoreMemory;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
void ProcessSpuConvexConvexCollision(SpuCollisionPairInput* wuInput, CollisionTask_LocalStoreMemory* lsMemPtr, SpuContactResult& spuContacts)
|
||||||
|
{
|
||||||
|
|
||||||
|
#ifdef DEBUG_SPU_COLLISION_DETECTION
|
||||||
|
//spu_printf("SPU: ProcessSpuConvexConvexCollision\n");
|
||||||
|
#endif //DEBUG_SPU_COLLISION_DETECTION
|
||||||
|
//CollisionShape* shape0 = (CollisionShape*)wuInput->m_collisionShapes[0];
|
||||||
|
//CollisionShape* shape1 = (CollisionShape*)wuInput->m_collisionShapes[1];
|
||||||
|
btPersistentManifold* manifold = (btPersistentManifold*)wuInput->m_persistentManifoldPtr;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
bool genericGjk = true;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
if (genericGjk)
|
||||||
|
{
|
||||||
|
//try generic GJK
|
||||||
|
|
||||||
|
SpuVoronoiSimplexSolver vsSolver;
|
||||||
|
SpuMinkowskiPenetrationDepthSolver penetrationSolver;
|
||||||
|
|
||||||
|
void* shape0Ptr = wuInput->m_spuCollisionShapes[0];
|
||||||
|
void* shape1Ptr = wuInput->m_spuCollisionShapes[1];
|
||||||
|
int shapeType0 = wuInput->m_shapeType0;
|
||||||
|
int shapeType1 = wuInput->m_shapeType1;
|
||||||
|
float marginA = wuInput->m_collisionMargin0;
|
||||||
|
float marginB = wuInput->m_collisionMargin1;
|
||||||
|
|
||||||
|
SpuClosestPointInput cpInput;
|
||||||
|
cpInput.m_convexVertexData = lsMemPtr->convexVertexData;
|
||||||
|
cpInput.m_transformA = wuInput->m_worldTransform0;
|
||||||
|
cpInput.m_transformB = wuInput->m_worldTransform1;
|
||||||
|
float sumMargin = (marginA+marginB);
|
||||||
|
cpInput.m_maximumDistanceSquared = sumMargin * sumMargin;
|
||||||
|
|
||||||
|
uint64_t manifoldAddress = (uint64_t)manifold;
|
||||||
|
btPersistentManifold* spuManifold=&lsMemPtr->gPersistentManifold;
|
||||||
|
spuContacts.setContactInfo(spuManifold,manifoldAddress,wuInput->m_worldTransform0,wuInput->m_worldTransform1);
|
||||||
|
|
||||||
|
|
||||||
|
SpuGjkPairDetector gjk(shape0Ptr,shape1Ptr,shapeType0,shapeType1,marginA,marginB,&vsSolver,&penetrationSolver);
|
||||||
|
gjk.getClosestPoints(cpInput,spuContacts);//,debugDraw);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
void processCollisionTask(void* userPtr, void* lsMemPtr)
|
||||||
|
{
|
||||||
|
|
||||||
|
SpuGatherAndProcessPairsTaskDesc* taskDescPtr = (SpuGatherAndProcessPairsTaskDesc*)userPtr;
|
||||||
|
SpuGatherAndProcessPairsTaskDesc& taskDesc = *taskDescPtr;
|
||||||
|
CollisionTask_LocalStoreMemory* colMemPtr = (CollisionTask_LocalStoreMemory*)lsMemPtr;
|
||||||
|
CollisionTask_LocalStoreMemory& lsMem = *(colMemPtr);
|
||||||
|
|
||||||
|
SpuContactResult spuContacts;
|
||||||
|
|
||||||
|
uint64_t dmaInPtr = taskDesc.inPtr;
|
||||||
|
unsigned int numPages = taskDesc.numPages;
|
||||||
|
unsigned int numOnLastPage = taskDesc.numOnLastPage;
|
||||||
|
|
||||||
|
// prefetch first set of inputs and wait
|
||||||
|
unsigned int nextNumOnPage = (numPages > 1)? MIDPHASE_NUM_WORKUNITS_PER_PAGE : numOnLastPage;
|
||||||
|
lsMem.g_workUnitTaskBuffers.backBufferDmaGet(dmaInPtr, nextNumOnPage*sizeof(SpuGatherAndProcessWorkUnitInput), DMA_TAG(3));
|
||||||
|
dmaInPtr += MIDPHASE_WORKUNIT_PAGE_SIZE;
|
||||||
|
|
||||||
|
for (unsigned int i = 0; i < numPages; i++)
|
||||||
|
{
|
||||||
|
// wait for back buffer dma and swap buffers
|
||||||
|
unsigned char *inputPtr = lsMem.g_workUnitTaskBuffers.swapBuffers();
|
||||||
|
|
||||||
|
// number on current page is number prefetched last iteration
|
||||||
|
unsigned int numOnPage = nextNumOnPage;
|
||||||
|
|
||||||
|
unsigned int j;
|
||||||
|
|
||||||
|
|
||||||
|
// prefetch next set of inputs
|
||||||
|
if (i < numPages-1)
|
||||||
|
{
|
||||||
|
nextNumOnPage = (i == numPages-2)? numOnLastPage : MIDPHASE_NUM_WORKUNITS_PER_PAGE;
|
||||||
|
lsMem.g_workUnitTaskBuffers.backBufferDmaGet(dmaInPtr, nextNumOnPage*sizeof(SpuGatherAndProcessWorkUnitInput), DMA_TAG(3));
|
||||||
|
dmaInPtr += MIDPHASE_WORKUNIT_PAGE_SIZE;
|
||||||
|
}
|
||||||
|
|
||||||
|
SpuGatherAndProcessWorkUnitInput* wuInputs = reinterpret_cast<SpuGatherAndProcessWorkUnitInput *>(inputPtr);
|
||||||
|
|
||||||
|
for (j = 0; j < numOnPage; j++)
|
||||||
|
{
|
||||||
|
#ifdef DEBUG_SPU_COLLISION_DETECTION
|
||||||
|
printMidphaseInput(&wuInputs[j]);
|
||||||
|
#endif //DEBUG_SPU_COLLISION_DETECTION
|
||||||
|
|
||||||
|
|
||||||
|
int numPairs = wuInputs[j].m_endIndex - wuInputs[j].m_startIndex;
|
||||||
|
|
||||||
|
// printf("startIndex=%d, endIndex = %d\n",wuInputs[j].m_startIndex,wuInputs[j].m_endIndex);
|
||||||
|
|
||||||
|
|
||||||
|
if (numPairs)
|
||||||
|
{
|
||||||
|
|
||||||
|
{
|
||||||
|
int dmaSize = numPairs*sizeof(SpuGatherAndProcessPairsTaskDesc);
|
||||||
|
uint64_t dmaPpuAddress = wuInputs[j].m_pairArrayPtr+wuInputs[j].m_startIndex * sizeof(btBroadphasePair);
|
||||||
|
cellDmaGet(&lsMem.gBroadphasePairs, dmaPpuAddress , dmaSize, DMA_TAG(1), 0, 0);
|
||||||
|
cellDmaWaitTagStatusAll(DMA_MASK(1));
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int p=0;p<numPairs;p++)
|
||||||
|
{
|
||||||
|
//for each broadphase pair, do something
|
||||||
|
|
||||||
|
btBroadphasePair& pair = lsMem.gBroadphasePairs[p];
|
||||||
|
int userInfo = int(pair.m_userInfo);
|
||||||
|
|
||||||
|
if (userInfo == 2 && pair.m_algorithm && pair.m_pProxy0 && pair.m_pProxy1)
|
||||||
|
{
|
||||||
|
|
||||||
|
{
|
||||||
|
int dmaSize = sizeof(SpuContactManifoldCollisionAlgorithm);
|
||||||
|
uint64_t dmaPpuAddress2 = (uint64_t)pair.m_algorithm;
|
||||||
|
cellDmaGet(&lsMem.gSpuContactManifoldAlgo, dmaPpuAddress2 , dmaSize, DMA_TAG(1), 0, 0);
|
||||||
|
cellDmaWaitTagStatusAll(DMA_MASK(1));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
SpuCollisionPairInput collisionPairInput;
|
||||||
|
collisionPairInput.m_persistentManifoldPtr = (uint64_t) lsMem.getlocalCollisionAlgorithm()->getContactManifoldPtr();
|
||||||
|
|
||||||
|
#ifdef DEBUG_SPU_COLLISION_DETECTION
|
||||||
|
spu_printf("SPU: manifoldPtr: %llx",collisionPairInput->m_persistentManifoldPtr);
|
||||||
|
#endif //DEBUG_SPU_COLLISION_DETECTION
|
||||||
|
|
||||||
|
{
|
||||||
|
int dmaSize = sizeof(btBroadphaseProxy);
|
||||||
|
uint64_t dmaPpuAddress2 = (uint64_t)pair.m_pProxy0;
|
||||||
|
cellDmaGet(&lsMem.gProxy0, dmaPpuAddress2 , dmaSize, DMA_TAG(1), 0, 0);
|
||||||
|
cellDmaWaitTagStatusAll(DMA_MASK(1));
|
||||||
|
}
|
||||||
|
{
|
||||||
|
int dmaSize = sizeof(btBroadphaseProxy);
|
||||||
|
uint64_t dmaPpuAddress2 = (uint64_t)pair.m_pProxy1;
|
||||||
|
cellDmaGet(&lsMem.gProxy1, dmaPpuAddress2 , dmaSize, DMA_TAG(2), 0, 0);
|
||||||
|
cellDmaWaitTagStatusAll(DMA_MASK(2));
|
||||||
|
}
|
||||||
|
|
||||||
|
//btCollisionObject* colObj0 = (btCollisionObject*)gProxy0.m_clientObject;
|
||||||
|
//btCollisionObject* colObj1 = (btCollisionObject*)gProxy1.m_clientObject;
|
||||||
|
|
||||||
|
{
|
||||||
|
int dmaSize = sizeof(btCollisionObject);
|
||||||
|
uint64_t dmaPpuAddress2 = (uint64_t)lsMem.gProxy0.m_clientObject;
|
||||||
|
cellDmaGet(&lsMem.gColObj0, dmaPpuAddress2 , dmaSize, DMA_TAG(1), 0, 0);
|
||||||
|
cellDmaWaitTagStatusAll(DMA_MASK(1));
|
||||||
|
}
|
||||||
|
{
|
||||||
|
int dmaSize = sizeof(btCollisionObject);
|
||||||
|
uint64_t dmaPpuAddress2 = (uint64_t)lsMem.gProxy1.m_clientObject;
|
||||||
|
cellDmaGet(&lsMem.gColObj1, dmaPpuAddress2 , dmaSize, DMA_TAG(2), 0, 0);
|
||||||
|
cellDmaWaitTagStatusAll(DMA_MASK(2));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
///can wait on the combined DMA_MASK, or dma on the same tag
|
||||||
|
|
||||||
|
collisionPairInput.m_shapeType0 = lsMem.getlocalCollisionAlgorithm()->getShapeType0();
|
||||||
|
collisionPairInput.m_shapeType1 = lsMem.getlocalCollisionAlgorithm()->getShapeType1();
|
||||||
|
collisionPairInput.m_collisionMargin0 = lsMem.getlocalCollisionAlgorithm()->getCollisionMargin0();
|
||||||
|
collisionPairInput.m_collisionMargin1 = lsMem.getlocalCollisionAlgorithm()->getCollisionMargin1();
|
||||||
|
|
||||||
|
#ifdef DEBUG_SPU_COLLISION_DETECTION
|
||||||
|
spu_printf("SPU collisionPairInput->m_shapeType0 = %d\n",collisionPairInput->m_shapeType0);
|
||||||
|
spu_printf("SPU collisionPairInput->m_shapeType1 = %d\n",collisionPairInput->m_shapeType1);
|
||||||
|
#endif //DEBUG_SPU_COLLISION_DETECTION
|
||||||
|
|
||||||
|
if (1)
|
||||||
|
{
|
||||||
|
|
||||||
|
collisionPairInput.m_worldTransform0 = lsMem.gColObj0.getWorldTransform();
|
||||||
|
collisionPairInput.m_worldTransform1 = lsMem.gColObj1.getWorldTransform();
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#ifdef DEBUG_SPU_COLLISION_DETECTION
|
||||||
|
spu_printf("SPU worldTrans0.origin = (%f,%f,%f)\n",
|
||||||
|
collisionPairInput->m_worldTransform0.getOrigin().getX(),
|
||||||
|
collisionPairInput->m_worldTransform0.getOrigin().getY(),
|
||||||
|
collisionPairInput->m_worldTransform0.getOrigin().getZ());
|
||||||
|
|
||||||
|
spu_printf("SPU worldTrans1.origin = (%f,%f,%f)\n",
|
||||||
|
collisionPairInput->m_worldTransform1.getOrigin().getX(),
|
||||||
|
collisionPairInput->m_worldTransform1.getOrigin().getY(),
|
||||||
|
collisionPairInput->m_worldTransform1.getOrigin().getZ());
|
||||||
|
#endif //DEBUG_SPU_COLLISION_DETECTION
|
||||||
|
|
||||||
|
|
||||||
|
{
|
||||||
|
int dmaSize = sizeof(btPersistentManifold);
|
||||||
|
uint64_t dmaPpuAddress2 = collisionPairInput.m_persistentManifoldPtr;
|
||||||
|
cellDmaGet(&lsMem.gPersistentManifold, dmaPpuAddress2 , dmaSize, DMA_TAG(1), 0, 0);
|
||||||
|
cellDmaWaitTagStatusAll(DMA_MASK(1));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (btBroadphaseProxy::isConvex(collisionPairInput.m_shapeType0)
|
||||||
|
&& btBroadphaseProxy::isConvex(collisionPairInput.m_shapeType1))
|
||||||
|
{
|
||||||
|
|
||||||
|
{
|
||||||
|
int dmaSize = lsMem.maxShapeSize;
|
||||||
|
uint64_t dmaPpuAddress2 = (uint64_t)lsMem.gColObj0.getCollisionShape();
|
||||||
|
cellDmaGet(lsMem.gCollisionShape0, dmaPpuAddress2 , dmaSize, DMA_TAG(1), 0, 0);
|
||||||
|
cellDmaWaitTagStatusAll(DMA_MASK(1));
|
||||||
|
}
|
||||||
|
{
|
||||||
|
int dmaSize = lsMem.maxShapeSize;
|
||||||
|
uint64_t dmaPpuAddress2 = (uint64_t)lsMem.gColObj1.getCollisionShape();
|
||||||
|
cellDmaGet(lsMem.gCollisionShape1, dmaPpuAddress2 , dmaSize, DMA_TAG(2), 0, 0);
|
||||||
|
cellDmaWaitTagStatusAll(DMA_MASK(2));
|
||||||
|
}
|
||||||
|
|
||||||
|
btConvexShape* spuConvexShape0 = (btConvexShape*)lsMem.gCollisionShape0;
|
||||||
|
btConvexShape* spuConvexShape1 = (btConvexShape*)lsMem.gCollisionShape1;
|
||||||
|
|
||||||
|
btVector3 dim0 = spuConvexShape0->getImplicitShapeDimensions();
|
||||||
|
btVector3 dim1 = spuConvexShape1->getImplicitShapeDimensions();
|
||||||
|
|
||||||
|
collisionPairInput.m_primitiveDimensions0 = dim0;
|
||||||
|
collisionPairInput.m_primitiveDimensions1 = dim1;
|
||||||
|
collisionPairInput.m_collisionShapes[0] = (uint64_t)lsMem.gColObj0.getCollisionShape();
|
||||||
|
collisionPairInput.m_collisionShapes[1] = (uint64_t)lsMem.gColObj1.getCollisionShape();
|
||||||
|
collisionPairInput.m_spuCollisionShapes[0] = spuConvexShape0;
|
||||||
|
collisionPairInput.m_spuCollisionShapes[1] = spuConvexShape1;
|
||||||
|
ProcessSpuConvexConvexCollision(&collisionPairInput,&lsMem, spuContacts);
|
||||||
|
} else
|
||||||
|
{
|
||||||
|
//a non-convex shape is involved
|
||||||
|
|
||||||
|
bool isSwapped = false;
|
||||||
|
bool handleConvexConcave = false;
|
||||||
|
|
||||||
|
if (btBroadphaseProxy::isConcave(collisionPairInput.m_shapeType0) &&
|
||||||
|
btBroadphaseProxy::isConvex(collisionPairInput.m_shapeType1))
|
||||||
|
{
|
||||||
|
isSwapped = true;
|
||||||
|
spu_printf("SPU convex/concave swapped, unsupported!\n");
|
||||||
|
handleConvexConcave = true;
|
||||||
|
}
|
||||||
|
if (btBroadphaseProxy::isConvex(collisionPairInput.m_shapeType0)&&
|
||||||
|
btBroadphaseProxy::isConcave(collisionPairInput.m_shapeType1))
|
||||||
|
{
|
||||||
|
handleConvexConcave = true;
|
||||||
|
}
|
||||||
|
if (handleConvexConcave && !isSwapped)
|
||||||
|
{
|
||||||
|
// spu_printf("SPU: non-convex detected\n");
|
||||||
|
|
||||||
|
{
|
||||||
|
// uint64_t dmaPpuAddress2 = (uint64_t)gProxy1.m_clientObject;
|
||||||
|
// spu_printf("SPU: gColObj1 trimesh = %llx\n",dmaPpuAddress2);
|
||||||
|
}
|
||||||
|
|
||||||
|
///dma and initialize the convex object
|
||||||
|
{
|
||||||
|
int dmaSize = lsMem.maxShapeSize;
|
||||||
|
uint64_t dmaPpuAddress2 = (uint64_t)lsMem.gColObj0.getCollisionShape();
|
||||||
|
cellDmaGet(lsMem.gCollisionShape0, dmaPpuAddress2 , dmaSize, DMA_TAG(1), 0, 0);
|
||||||
|
cellDmaWaitTagStatusAll(DMA_MASK(1));
|
||||||
|
}
|
||||||
|
///dma and initialize the convex object
|
||||||
|
{
|
||||||
|
int dmaSize = lsMem.maxShapeSize;
|
||||||
|
uint64_t dmaPpuAddress2 = (uint64_t)lsMem.gColObj1.getCollisionShape();
|
||||||
|
// spu_printf("SPU: trimesh = %llx\n",dmaPpuAddress2);
|
||||||
|
cellDmaGet(lsMem.gCollisionShape1, dmaPpuAddress2 , dmaSize, DMA_TAG(2), 0, 0);
|
||||||
|
cellDmaWaitTagStatusAll(DMA_MASK(2));
|
||||||
|
}
|
||||||
|
btConvexShape* spuConvexShape0 = (btConvexShape*)lsMem.gCollisionShape0;
|
||||||
|
btBvhTriangleMeshShape* trimeshShape = (btBvhTriangleMeshShape*)lsMem.gCollisionShape1;
|
||||||
|
|
||||||
|
btVector3 dim0 = spuConvexShape0->getImplicitShapeDimensions();
|
||||||
|
collisionPairInput.m_primitiveDimensions0 = dim0;
|
||||||
|
collisionPairInput.m_collisionShapes[0] = (uint64_t)lsMem.gColObj0.getCollisionShape();
|
||||||
|
collisionPairInput.m_collisionShapes[1] = (uint64_t)lsMem.gColObj1.getCollisionShape();
|
||||||
|
collisionPairInput.m_spuCollisionShapes[0] = spuConvexShape0;
|
||||||
|
collisionPairInput.m_spuCollisionShapes[1] = trimeshShape;
|
||||||
|
|
||||||
|
btAssert(0);
|
||||||
|
//ProcessConvexConcaveSpuCollision(&collisionPairInput,spuContacts);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
spuContacts.flush();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
|
||||||
|
#ifndef SPU_GATHERING_COLLISION_TASK_H
|
||||||
|
#define SPU_GATHERING_COLLISION_TASK_H
|
||||||
|
|
||||||
|
struct SpuGatherAndProcessPairsTaskDesc;
|
||||||
|
|
||||||
|
void processCollisionTask(void* userPtr, void* lsMemory);
|
||||||
|
|
||||||
|
void* createCollisionLocalStoreMemory();
|
||||||
|
|
||||||
|
#endif //SPU_GATHERING_COLLISION_TASK_H
|
||||||
@@ -0,0 +1,310 @@
|
|||||||
|
/*
|
||||||
|
Bullet Continuous Collision Detection and Physics Library
|
||||||
|
Copyright (c) 2003-2006 Erwin Coumans http://continuousphysics.com/Bullet/
|
||||||
|
|
||||||
|
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 "SpuGjkPairDetector.h"
|
||||||
|
#include "SpuConvexPenetrationDepthSolver.h"
|
||||||
|
#include "SpuLocalSupport.h"
|
||||||
|
|
||||||
|
|
||||||
|
#if defined(DEBUG) || defined (_DEBUG)
|
||||||
|
#include <stdio.h> //for debug printf
|
||||||
|
#ifdef __SPU__
|
||||||
|
#include <spu_printf.h>
|
||||||
|
#define printf spu_printf
|
||||||
|
#endif //__SPU__
|
||||||
|
#endif
|
||||||
|
|
||||||
|
//must be above the machine epsilon
|
||||||
|
#define REL_ERROR2 btScalar(1.0e-6)
|
||||||
|
|
||||||
|
//temp globals, to improve GJK/EPA/penetration calculations
|
||||||
|
int gSpuNumDeepPenetrationChecks = 0;
|
||||||
|
int gSpuNumGjkChecks = 0;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
SpuGjkPairDetector::SpuGjkPairDetector(void* objectA,void* objectB,int shapeTypeA, int shapeTypeB, float marginA,float marginB,SpuVoronoiSimplexSolver* simplexSolver, const SpuConvexPenetrationDepthSolver* penetrationDepthSolver)
|
||||||
|
:m_cachedSeparatingAxis(float(0.),float(0.),float(1.)),
|
||||||
|
m_penetrationDepthSolver(penetrationDepthSolver),
|
||||||
|
m_simplexSolver(simplexSolver),
|
||||||
|
m_minkowskiA(objectA),
|
||||||
|
m_minkowskiB(objectB),
|
||||||
|
m_shapeTypeA(shapeTypeA),
|
||||||
|
m_shapeTypeB(shapeTypeB),
|
||||||
|
m_marginA(marginA),
|
||||||
|
m_marginB(marginB),
|
||||||
|
m_ignoreMargin(false),
|
||||||
|
m_lastUsedMethod(-1),
|
||||||
|
m_catchDegeneracies(1)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void SpuGjkPairDetector::getClosestPoints(const SpuClosestPointInput& input,SpuContactResult& output)
|
||||||
|
{
|
||||||
|
btScalar distance=btScalar(0.);
|
||||||
|
btVector3 normalInB(btScalar(0.),btScalar(0.),btScalar(0.));
|
||||||
|
btVector3 pointOnA,pointOnB;
|
||||||
|
btTransform localTransA = input.m_transformA;
|
||||||
|
btTransform localTransB = input.m_transformB;
|
||||||
|
btVector3 positionOffset = (localTransA.getOrigin() + localTransB.getOrigin()) * btScalar(0.5);
|
||||||
|
localTransA.getOrigin() -= positionOffset;
|
||||||
|
localTransB.getOrigin() -= positionOffset;
|
||||||
|
|
||||||
|
btScalar marginA = m_marginA;
|
||||||
|
btScalar marginB = m_marginB;
|
||||||
|
|
||||||
|
gSpuNumGjkChecks++;
|
||||||
|
|
||||||
|
//for CCD we don't use margins
|
||||||
|
if (m_ignoreMargin)
|
||||||
|
{
|
||||||
|
marginA = btScalar(0.);
|
||||||
|
marginB = btScalar(0.);
|
||||||
|
}
|
||||||
|
|
||||||
|
m_curIter = 0;
|
||||||
|
int gGjkMaxIter = 1000;//this is to catch invalid input, perhaps check for #NaN?
|
||||||
|
m_cachedSeparatingAxis.setValue(0,1,0);
|
||||||
|
|
||||||
|
bool isValid = false;
|
||||||
|
bool checkSimplex = false;
|
||||||
|
bool checkPenetration = true;
|
||||||
|
m_degenerateSimplex = 0;
|
||||||
|
|
||||||
|
m_lastUsedMethod = -1;
|
||||||
|
|
||||||
|
{
|
||||||
|
btScalar squaredDistance = SIMD_INFINITY;
|
||||||
|
btScalar delta = btScalar(0.);
|
||||||
|
|
||||||
|
btScalar margin = marginA + marginB;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
m_simplexSolver->reset();
|
||||||
|
|
||||||
|
for ( ; ; )
|
||||||
|
//while (true)
|
||||||
|
{
|
||||||
|
|
||||||
|
btVector3 seperatingAxisInA = (-m_cachedSeparatingAxis)* input.m_transformA.getBasis();
|
||||||
|
btVector3 seperatingAxisInB = m_cachedSeparatingAxis* input.m_transformB.getBasis();
|
||||||
|
|
||||||
|
// btVector3 pInA = m_minkowskiA->localGetSupportingVertexWithoutMargin(seperatingAxisInA);
|
||||||
|
// btVector3 qInB = m_minkowskiB->localGetSupportingVertexWithoutMargin(seperatingAxisInB);
|
||||||
|
|
||||||
|
btVector3 pInA = localGetSupportingVertexWithoutMargin(m_shapeTypeA, m_minkowskiA, seperatingAxisInA,input.m_convexVertexData);//, &featureIndexA);
|
||||||
|
btVector3 qInB = localGetSupportingVertexWithoutMargin(m_shapeTypeB, m_minkowskiB, seperatingAxisInB,input.m_convexVertexData);//, &featureIndexB);
|
||||||
|
|
||||||
|
|
||||||
|
btPoint3 pWorld = localTransA(pInA);
|
||||||
|
btPoint3 qWorld = localTransB(qInB);
|
||||||
|
|
||||||
|
btVector3 w = pWorld - qWorld;
|
||||||
|
delta = m_cachedSeparatingAxis.dot(w);
|
||||||
|
|
||||||
|
// potential exit, they don't overlap
|
||||||
|
if ((delta > btScalar(0.0)) && (delta * delta > squaredDistance * input.m_maximumDistanceSquared))
|
||||||
|
{
|
||||||
|
checkPenetration = false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
//exit 0: the new point is already in the simplex, or we didn't come any closer
|
||||||
|
if (m_simplexSolver->inSimplex(w))
|
||||||
|
{
|
||||||
|
m_degenerateSimplex = 1;
|
||||||
|
checkSimplex = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
// are we getting any closer ?
|
||||||
|
btScalar f0 = squaredDistance - delta;
|
||||||
|
btScalar f1 = squaredDistance * REL_ERROR2;
|
||||||
|
|
||||||
|
if (f0 <= f1)
|
||||||
|
{
|
||||||
|
if (f0 <= btScalar(0.))
|
||||||
|
{
|
||||||
|
m_degenerateSimplex = 2;
|
||||||
|
}
|
||||||
|
checkSimplex = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
//add current vertex to simplex
|
||||||
|
m_simplexSolver->addVertex(w, pWorld, qWorld);
|
||||||
|
|
||||||
|
//calculate the closest point to the origin (update vector v)
|
||||||
|
if (!m_simplexSolver->closest(m_cachedSeparatingAxis))
|
||||||
|
{
|
||||||
|
m_degenerateSimplex = 3;
|
||||||
|
checkSimplex = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
btScalar previousSquaredDistance = squaredDistance;
|
||||||
|
squaredDistance = m_cachedSeparatingAxis.length2();
|
||||||
|
|
||||||
|
//redundant m_simplexSolver->compute_points(pointOnA, pointOnB);
|
||||||
|
|
||||||
|
//are we getting any closer ?
|
||||||
|
if (previousSquaredDistance - squaredDistance <= SIMD_EPSILON * previousSquaredDistance)
|
||||||
|
{
|
||||||
|
m_simplexSolver->backup_closest(m_cachedSeparatingAxis);
|
||||||
|
checkSimplex = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
//degeneracy, this is typically due to invalid/uninitialized worldtransforms for a btCollisionObject
|
||||||
|
if (m_curIter++ > gGjkMaxIter)
|
||||||
|
{
|
||||||
|
#if defined(DEBUG) || defined (_DEBUG)
|
||||||
|
|
||||||
|
printf("SpuGjkPairDetector maxIter exceeded:%i\n",m_curIter);
|
||||||
|
printf("sepAxis=(%f,%f,%f), squaredDistance = %f, shapeTypeA=%i,shapeTypeB=%i\n",
|
||||||
|
m_cachedSeparatingAxis.getX(),
|
||||||
|
m_cachedSeparatingAxis.getY(),
|
||||||
|
m_cachedSeparatingAxis.getZ(),
|
||||||
|
squaredDistance,
|
||||||
|
m_shapeTypeA,
|
||||||
|
m_shapeTypeB);
|
||||||
|
|
||||||
|
#endif
|
||||||
|
break;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
bool check = (!m_simplexSolver->fullSimplex());
|
||||||
|
//bool check = (!m_simplexSolver->fullSimplex() && squaredDistance > SIMD_EPSILON * m_simplexSolver->maxVertex());
|
||||||
|
|
||||||
|
if (!check)
|
||||||
|
{
|
||||||
|
//do we need this backup_closest here ?
|
||||||
|
m_simplexSolver->backup_closest(m_cachedSeparatingAxis);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (checkSimplex)
|
||||||
|
{
|
||||||
|
m_simplexSolver->compute_points(pointOnA, pointOnB);
|
||||||
|
normalInB = pointOnA-pointOnB;
|
||||||
|
btScalar lenSqr = m_cachedSeparatingAxis.length2();
|
||||||
|
//valid normal
|
||||||
|
if (lenSqr < 0.0001)
|
||||||
|
{
|
||||||
|
m_degenerateSimplex = 5;
|
||||||
|
}
|
||||||
|
if (lenSqr > SIMD_EPSILON*SIMD_EPSILON)
|
||||||
|
{
|
||||||
|
btScalar rlen = btScalar(1.) / btSqrt(lenSqr );
|
||||||
|
normalInB *= rlen; //normalize
|
||||||
|
btScalar s = btSqrt(squaredDistance);
|
||||||
|
|
||||||
|
btAssert(s > btScalar(0.0));
|
||||||
|
pointOnA -= m_cachedSeparatingAxis * (marginA / s);
|
||||||
|
pointOnB += m_cachedSeparatingAxis * (marginB / s);
|
||||||
|
distance = ((btScalar(1.)/rlen) - margin);
|
||||||
|
isValid = true;
|
||||||
|
|
||||||
|
m_lastUsedMethod = 1;
|
||||||
|
} else
|
||||||
|
{
|
||||||
|
m_lastUsedMethod = 2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool catchDegeneratePenetrationCase =
|
||||||
|
(m_catchDegeneracies && m_penetrationDepthSolver && m_degenerateSimplex && ((distance+margin) < 0.01));
|
||||||
|
|
||||||
|
//if (checkPenetration && !isValid)
|
||||||
|
if (checkPenetration && (!isValid || catchDegeneratePenetrationCase ))
|
||||||
|
{
|
||||||
|
//penetration case
|
||||||
|
|
||||||
|
//if there is no way to handle penetrations, bail out
|
||||||
|
if (m_penetrationDepthSolver)
|
||||||
|
{
|
||||||
|
// Penetration depth case.
|
||||||
|
btVector3 tmpPointOnA,tmpPointOnB;
|
||||||
|
|
||||||
|
gSpuNumDeepPenetrationChecks++;
|
||||||
|
|
||||||
|
bool isValid2 = m_penetrationDepthSolver->calcPenDepth(
|
||||||
|
*m_simplexSolver,
|
||||||
|
m_minkowskiA,m_minkowskiB,
|
||||||
|
m_shapeTypeA, m_shapeTypeB,
|
||||||
|
marginA, marginB,
|
||||||
|
localTransA,localTransB,
|
||||||
|
m_cachedSeparatingAxis, tmpPointOnA, tmpPointOnB,
|
||||||
|
0,input.m_stackAlloc,input.m_convexVertexData
|
||||||
|
);
|
||||||
|
|
||||||
|
if (isValid2)
|
||||||
|
{
|
||||||
|
btVector3 tmpNormalInB = tmpPointOnB-tmpPointOnA;
|
||||||
|
btScalar lenSqr = tmpNormalInB.length2();
|
||||||
|
if (lenSqr > (SIMD_EPSILON*SIMD_EPSILON))
|
||||||
|
{
|
||||||
|
tmpNormalInB /= btSqrt(lenSqr);
|
||||||
|
btScalar distance2 = -(tmpPointOnA-tmpPointOnB).length();
|
||||||
|
//only replace valid penetrations when the result is deeper (check)
|
||||||
|
if (!isValid || (distance2 < distance))
|
||||||
|
{
|
||||||
|
distance = distance2;
|
||||||
|
pointOnA = tmpPointOnA;
|
||||||
|
pointOnB = tmpPointOnB;
|
||||||
|
normalInB = tmpNormalInB;
|
||||||
|
isValid = true;
|
||||||
|
m_lastUsedMethod = 3;
|
||||||
|
} else
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
} else
|
||||||
|
{
|
||||||
|
//isValid = false;
|
||||||
|
m_lastUsedMethod = 4;
|
||||||
|
}
|
||||||
|
} else
|
||||||
|
{
|
||||||
|
m_lastUsedMethod = 5;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isValid)
|
||||||
|
{
|
||||||
|
#ifdef __SPU__
|
||||||
|
//spu_printf("distance\n");
|
||||||
|
#endif //__CELLOS_LV2__
|
||||||
|
|
||||||
|
|
||||||
|
output.addContactPoint(
|
||||||
|
normalInB,
|
||||||
|
pointOnB+positionOffset,
|
||||||
|
distance);
|
||||||
|
//printf("gjk add:%f",distance);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -0,0 +1,98 @@
|
|||||||
|
/* [SCE CONFIDENTIAL DOCUMENT]
|
||||||
|
* PLAYSTATION(R)3 SPU Optimized Bullet Physics Library (http://bulletphysics.com)
|
||||||
|
* Copyright (C) 2007 Sony Computer Entertainment Inc.
|
||||||
|
* All Rights Reserved.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
Bullet Continuous Collision Detection and Physics Library
|
||||||
|
Copyright (c) 2003-2006 Erwin Coumans http://continuousphysics.com/Bullet/
|
||||||
|
|
||||||
|
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 SPU_GJK_PAIR_DETECTOR_H
|
||||||
|
#define SPU_GJK_PAIR_DETECTOR_H
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#include "SpuContactResult.h"
|
||||||
|
|
||||||
|
|
||||||
|
#include "SpuVoronoiSimplexSolver.h"
|
||||||
|
class SpuConvexPenetrationDepthSolver;
|
||||||
|
|
||||||
|
/// btGjkPairDetector uses GJK to implement the btDiscreteCollisionDetectorInterface
|
||||||
|
class SpuGjkPairDetector
|
||||||
|
{
|
||||||
|
|
||||||
|
|
||||||
|
btVector3 m_cachedSeparatingAxis;
|
||||||
|
const SpuConvexPenetrationDepthSolver* m_penetrationDepthSolver;
|
||||||
|
SpuVoronoiSimplexSolver* m_simplexSolver;
|
||||||
|
void* m_minkowskiA;
|
||||||
|
void* m_minkowskiB;
|
||||||
|
int m_shapeTypeA;
|
||||||
|
int m_shapeTypeB;
|
||||||
|
float m_marginA;
|
||||||
|
float m_marginB;
|
||||||
|
bool m_ignoreMargin;
|
||||||
|
|
||||||
|
|
||||||
|
public:
|
||||||
|
|
||||||
|
//some debugging to fix degeneracy problems
|
||||||
|
int m_lastUsedMethod;
|
||||||
|
int m_curIter;
|
||||||
|
int m_degenerateSimplex;
|
||||||
|
int m_catchDegeneracies;
|
||||||
|
|
||||||
|
|
||||||
|
SpuGjkPairDetector(void* objectA,void* objectB,int m_shapeTypeA, int m_shapeTypeB, float marginA, float marginB, SpuVoronoiSimplexSolver* simplexSolver, const SpuConvexPenetrationDepthSolver* penetrationDepthSolver);
|
||||||
|
virtual ~SpuGjkPairDetector() {};
|
||||||
|
|
||||||
|
virtual void getClosestPoints(const SpuClosestPointInput& input,SpuContactResult& output);
|
||||||
|
|
||||||
|
void setMinkowskiA(void* minkA)
|
||||||
|
{
|
||||||
|
m_minkowskiA = minkA;
|
||||||
|
}
|
||||||
|
|
||||||
|
void setMinkowskiB(void* minkB)
|
||||||
|
{
|
||||||
|
m_minkowskiB = minkB;
|
||||||
|
}
|
||||||
|
|
||||||
|
void setCachedSeperatingAxis(const btVector3& seperatingAxis)
|
||||||
|
{
|
||||||
|
m_cachedSeparatingAxis = seperatingAxis;
|
||||||
|
}
|
||||||
|
|
||||||
|
void setPenetrationDepthSolver(SpuConvexPenetrationDepthSolver* penetrationDepthSolver)
|
||||||
|
{
|
||||||
|
m_penetrationDepthSolver = penetrationDepthSolver;
|
||||||
|
}
|
||||||
|
|
||||||
|
///don't use setIgnoreMargin, it's for Bullet's internal use
|
||||||
|
void setIgnoreMargin(bool ignoreMargin)
|
||||||
|
{
|
||||||
|
m_ignoreMargin = ignoreMargin;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#endif //SPU_GJK_PAIR_DETECTOR_H
|
||||||
@@ -0,0 +1,332 @@
|
|||||||
|
/*
|
||||||
|
|
||||||
|
Bullet Continuous Collision Detection and Physics Library
|
||||||
|
Copyright (c) 2003-2006 Erwin Coumans http://continuousphysics.com/Bullet/
|
||||||
|
|
||||||
|
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 "SpuGjkPairDetector.h"
|
||||||
|
#include "SpuConvexPenetrationDepthSolver.h"
|
||||||
|
#include "SpuLocalSupport.h"
|
||||||
|
|
||||||
|
//#include "BulletCollision/CollisionShapes/btConvexShape.h"
|
||||||
|
|
||||||
|
#if defined(DEBUG) || defined (_DEBUG)
|
||||||
|
#include <stdio.h> //for debug printf
|
||||||
|
#ifdef __SPU__
|
||||||
|
#include <spu_printf.h>
|
||||||
|
#define printf spu_printf
|
||||||
|
#endif //__SPU__
|
||||||
|
#endif
|
||||||
|
|
||||||
|
//must be above the machine epsilon
|
||||||
|
#define REL_ERROR2 float(1.0e-6)
|
||||||
|
|
||||||
|
//temp globals, to improve GJK/EPA/penetration calculations
|
||||||
|
int gSpuNumDeepPenetrationChecks = 0;
|
||||||
|
int gSpuNumGjkChecks = 0;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
SpuGjkPairDetector::SpuGjkPairDetector(void* objectA,void* objectB,int shapeTypeA, int shapeTypeB, float marginA,float marginB,SpuVoronoiSimplexSolver* simplexSolver, const SpuConvexPenetrationDepthSolver* penetrationDepthSolver)
|
||||||
|
:m_cachedSeparatingAxis(float(0.),float(0.),float(1.)),
|
||||||
|
m_penetrationDepthSolver(penetrationDepthSolver),
|
||||||
|
m_simplexSolver(simplexSolver),
|
||||||
|
m_minkowskiA(objectA),
|
||||||
|
m_minkowskiB(objectB),
|
||||||
|
m_shapeTypeA(shapeTypeA),
|
||||||
|
m_shapeTypeB(shapeTypeB),
|
||||||
|
m_marginA(marginA),
|
||||||
|
m_marginB(marginB),
|
||||||
|
m_ignoreMargin(false),
|
||||||
|
m_lastUsedMethod(-1),
|
||||||
|
m_catchDegeneracies(1)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void SpuGjkPairDetector::getClosestPoints(const SpuClosestPointInput& input,SpuContactResult& output)
|
||||||
|
{
|
||||||
|
float distance=float(0.);
|
||||||
|
Vectormath::Aos::Vector3 normalInB(float(0.),float(0.),float(0.));
|
||||||
|
Vectormath::Aos::Point3 pointOnA,pointOnB;
|
||||||
|
Vectormath::Aos::Transform3 localTransA = input.m_transformA;
|
||||||
|
Vectormath::Aos::Transform3 localTransB = input.m_transformB;
|
||||||
|
|
||||||
|
// World space coordinate
|
||||||
|
Vectormath::Aos::Vector3 localOriginA = localTransA.getTranslation();
|
||||||
|
Vectormath::Aos::Vector3 localOriginB = localTransB.getTranslation();
|
||||||
|
|
||||||
|
// Average instance position.
|
||||||
|
Vectormath::Aos::Vector3 positionOffset = (localOriginA + localOriginB) * float(0.5);
|
||||||
|
|
||||||
|
// Adjust the instance positions so that they're equidistant from the origin.
|
||||||
|
localTransA.setTranslation(localOriginA - positionOffset);
|
||||||
|
localTransB.setTranslation(localOriginB - positionOffset);
|
||||||
|
|
||||||
|
float marginA = m_marginA;
|
||||||
|
float marginB = m_marginB;
|
||||||
|
|
||||||
|
gSpuNumGjkChecks++;
|
||||||
|
|
||||||
|
//for CCD we don't use margins
|
||||||
|
if (m_ignoreMargin)
|
||||||
|
{
|
||||||
|
marginA = float(0.);
|
||||||
|
marginB = float(0.);
|
||||||
|
}
|
||||||
|
|
||||||
|
m_curIter = 0;
|
||||||
|
int gGjkMaxIter = 1000;//this is to catch invalid input, perhaps check for #NaN?
|
||||||
|
m_cachedSeparatingAxis = Vectormath::Aos::Vector3(0.f,1.f,0.f);
|
||||||
|
|
||||||
|
bool isValid = false;
|
||||||
|
bool checkSimplex = false;
|
||||||
|
bool checkPenetration = true;
|
||||||
|
m_degenerateSimplex = 0;
|
||||||
|
|
||||||
|
m_lastUsedMethod = -1;
|
||||||
|
|
||||||
|
{
|
||||||
|
float squaredDistance = 1e30f;
|
||||||
|
// There's no reason to have this delta declared out here, it's set in the loop before it's used and it's not used outside of the loop.
|
||||||
|
float delta = float(0.);
|
||||||
|
|
||||||
|
float margin = marginA + marginB;
|
||||||
|
|
||||||
|
m_simplexSolver->reset();
|
||||||
|
|
||||||
|
while (true)
|
||||||
|
{
|
||||||
|
// Get the separating axes into each bound's local space.
|
||||||
|
Vectormath::Aos::Vector3 seperatingAxisInA = orthoInverse(input.m_transformA) * (-m_cachedSeparatingAxis);
|
||||||
|
Vectormath::Aos::Vector3 seperatingAxisInB = orthoInverse(input.m_transformB) * m_cachedSeparatingAxis;
|
||||||
|
|
||||||
|
int shapeTypeA = m_shapeTypeA;
|
||||||
|
int shapeTypeB = m_shapeTypeB;
|
||||||
|
// int featureIndexA = 0, featureIndexB = 0; // Feature index basically means vertex index.
|
||||||
|
Vectormath::Aos::Point3 pInA = localGetSupportingVertexWithoutMargin(shapeTypeA, m_minkowskiA, seperatingAxisInA);//, &featureIndexA);
|
||||||
|
Vectormath::Aos::Point3 qInB = localGetSupportingVertexWithoutMargin(shapeTypeB, m_minkowskiB, seperatingAxisInB);//, &featureIndexB);
|
||||||
|
|
||||||
|
// These are in a 'translated' world space where the origin of that world space corresponds to 'positionOffset' in the 'real' world space.
|
||||||
|
Vectormath::Aos::Point3 pWorld = localTransA * pInA;
|
||||||
|
Vectormath::Aos::Point3 qWorld = localTransB * qInB;
|
||||||
|
|
||||||
|
//spu_printf("support point A: %f %f %f\n", pWorld.getX(), pWorld.getY(), pWorld.getZ());
|
||||||
|
//spu_printf("support point B: %f %f %f\n", qWorld.getX(), qWorld.getY(), qWorld.getZ());
|
||||||
|
|
||||||
|
// delta is sort of the distance between the current 'closest points' ...
|
||||||
|
// This seems kind of weird to me since m_cachedSeparatingAxis isn't a unit vector, so I'm not really sure what this signifies.
|
||||||
|
Vectormath::Aos::Vector3 w = pWorld - qWorld;
|
||||||
|
delta = dot(m_cachedSeparatingAxis, w);
|
||||||
|
|
||||||
|
// potential exit, they don't overlap
|
||||||
|
if ((delta > float(0.0)) && (delta * delta > squaredDistance * input.m_maximumDistanceSquared))
|
||||||
|
{
|
||||||
|
checkPenetration = false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
//exit 0: the new point is already in the simplex, or we didn't come any closer
|
||||||
|
if (m_simplexSolver->inSimplex(w))
|
||||||
|
{
|
||||||
|
m_degenerateSimplex = 1;
|
||||||
|
checkSimplex = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// are we getting any closer ?
|
||||||
|
float f0 = squaredDistance - delta;
|
||||||
|
float f1 = squaredDistance * REL_ERROR2;
|
||||||
|
|
||||||
|
// Are we close enough
|
||||||
|
if (f0 <= f1)
|
||||||
|
{
|
||||||
|
if (f0 <= float(0.))
|
||||||
|
{
|
||||||
|
m_degenerateSimplex = 2;
|
||||||
|
}
|
||||||
|
checkSimplex = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
//add current vertex to simplex
|
||||||
|
m_simplexSolver->addVertex(w, pWorld, qWorld);//, featureIndexA, featureIndexB);
|
||||||
|
|
||||||
|
//calculate the closest point to the origin (update vector v)
|
||||||
|
if (!m_simplexSolver->closest(m_cachedSeparatingAxis))
|
||||||
|
{
|
||||||
|
m_degenerateSimplex = 3;
|
||||||
|
checkSimplex = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
float previousSquaredDistance = squaredDistance;
|
||||||
|
squaredDistance = lengthSqr(m_cachedSeparatingAxis);
|
||||||
|
|
||||||
|
//redundant m_simplexSolver->compute_points(pointOnA, pointOnB);
|
||||||
|
|
||||||
|
//are we getting any closer ?
|
||||||
|
if (previousSquaredDistance - squaredDistance <= FLT_EPSILON * previousSquaredDistance)
|
||||||
|
{
|
||||||
|
m_simplexSolver->backup_closest(m_cachedSeparatingAxis);
|
||||||
|
checkSimplex = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
//degeneracy, this is typically due to invalid/uninitialized worldtransforms for a btCollisionObject
|
||||||
|
if (m_curIter++ > gGjkMaxIter)
|
||||||
|
{
|
||||||
|
#if defined(DEBUG) || defined (_DEBUG)
|
||||||
|
|
||||||
|
printf("SpuGjkPairDetector maxIter exceeded:%i\n",m_curIter);
|
||||||
|
printf("sepAxis=(%f,%f,%f), squaredDistance = %f, shapeTypeA=%i,shapeTypeB=%i\n",
|
||||||
|
m_cachedSeparatingAxis.getX(),
|
||||||
|
m_cachedSeparatingAxis.getY(),
|
||||||
|
m_cachedSeparatingAxis.getZ(),
|
||||||
|
squaredDistance,
|
||||||
|
shapeTypeA,
|
||||||
|
shapeTypeB);
|
||||||
|
|
||||||
|
#endif
|
||||||
|
break;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
bool check = (!m_simplexSolver->fullSimplex());
|
||||||
|
//bool check = (!m_simplexSolver->fullSimplex() && squaredDistance > FLT_EPSILON * m_simplexSolver->maxVertex());
|
||||||
|
|
||||||
|
if (!check)
|
||||||
|
{
|
||||||
|
//do we need this backup_closest here ?
|
||||||
|
m_simplexSolver->backup_closest(m_cachedSeparatingAxis);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (checkSimplex)
|
||||||
|
{
|
||||||
|
m_simplexSolver->compute_points(pointOnA, pointOnB);
|
||||||
|
normalInB = pointOnA-pointOnB;
|
||||||
|
float lenSqr = lengthSqr(m_cachedSeparatingAxis);
|
||||||
|
//valid normal
|
||||||
|
if (lenSqr < 0.0001)
|
||||||
|
{
|
||||||
|
m_degenerateSimplex = 5;
|
||||||
|
}
|
||||||
|
if (lenSqr > FLT_EPSILON*FLT_EPSILON)
|
||||||
|
{
|
||||||
|
float rlen = float(1.) / sqrtf(lenSqr );
|
||||||
|
normalInB *= rlen; //normalize
|
||||||
|
float s = sqrtf(squaredDistance);
|
||||||
|
|
||||||
|
btAssert(s > float(0.0));
|
||||||
|
pointOnA -= m_cachedSeparatingAxis * (marginA / s);
|
||||||
|
pointOnB += m_cachedSeparatingAxis * (marginB / s);
|
||||||
|
distance = ((float(1.)/rlen) - margin);
|
||||||
|
isValid = true;
|
||||||
|
|
||||||
|
m_lastUsedMethod = 1;
|
||||||
|
} else
|
||||||
|
{
|
||||||
|
m_lastUsedMethod = 2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool catchDegeneratePenetrationCase =
|
||||||
|
(m_catchDegeneracies && m_penetrationDepthSolver && m_degenerateSimplex && ((distance+margin) < 0.01));
|
||||||
|
|
||||||
|
//if (checkPenetration && !isValid)
|
||||||
|
if (checkPenetration && (!isValid || catchDegeneratePenetrationCase ))
|
||||||
|
{
|
||||||
|
//penetration case
|
||||||
|
|
||||||
|
//if there is no way to handle penetrations, bail out
|
||||||
|
if (m_penetrationDepthSolver)
|
||||||
|
{
|
||||||
|
// Penetration depth case.
|
||||||
|
Vectormath::Aos::Point3 tmpPointOnA,tmpPointOnB;
|
||||||
|
|
||||||
|
// spu_printf("SPU: deep penetration check\n");
|
||||||
|
gSpuNumDeepPenetrationChecks++;
|
||||||
|
|
||||||
|
bool isValid2 = m_penetrationDepthSolver->calcPenDepth(
|
||||||
|
*m_simplexSolver,
|
||||||
|
m_minkowskiA,m_minkowskiB,
|
||||||
|
m_shapeTypeA, m_shapeTypeB,
|
||||||
|
marginA, marginB,
|
||||||
|
localTransA,localTransB,
|
||||||
|
m_cachedSeparatingAxis, tmpPointOnA, tmpPointOnB,
|
||||||
|
0,input.m_stackAlloc
|
||||||
|
);
|
||||||
|
|
||||||
|
|
||||||
|
if (isValid2)
|
||||||
|
{
|
||||||
|
Vectormath::Aos::Vector3 tmpNormalInB = tmpPointOnB-tmpPointOnA;
|
||||||
|
float lenSqr = lengthSqr(tmpNormalInB);
|
||||||
|
if (lenSqr > (FLT_EPSILON*FLT_EPSILON))
|
||||||
|
{
|
||||||
|
tmpNormalInB /= sqrtf(lenSqr);
|
||||||
|
float distance2 = -dist(tmpPointOnA,tmpPointOnB);
|
||||||
|
//only replace valid penetrations when the result is deeper (check)
|
||||||
|
if (!isValid || (distance2 < distance))
|
||||||
|
{
|
||||||
|
distance = distance2;
|
||||||
|
pointOnA = tmpPointOnA;
|
||||||
|
pointOnB = tmpPointOnB;
|
||||||
|
normalInB = tmpNormalInB;
|
||||||
|
isValid = true;
|
||||||
|
m_lastUsedMethod = 3;
|
||||||
|
} else
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
} else
|
||||||
|
{
|
||||||
|
//isValid = false;
|
||||||
|
m_lastUsedMethod = 4;
|
||||||
|
}
|
||||||
|
} else
|
||||||
|
{
|
||||||
|
m_lastUsedMethod = 5;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isValid)
|
||||||
|
{
|
||||||
|
#ifdef __SPU__
|
||||||
|
//spu_printf("distance\n");
|
||||||
|
#endif //__CELLOS_LV2__
|
||||||
|
|
||||||
|
|
||||||
|
Vectormath::Aos::Point3 tmpPtOnB=pointOnB+positionOffset;
|
||||||
|
Vectormath::Aos::Point3 vmPtOnB(tmpPtOnB.getX(),tmpPtOnB.getY(),tmpPtOnB.getZ());
|
||||||
|
Vectormath::Aos::Vector3 vmNormalOnB(normalInB.getX(),normalInB.getY(),normalInB.getZ());
|
||||||
|
|
||||||
|
output.addContactPoint(
|
||||||
|
vmNormalOnB,
|
||||||
|
vmPtOnB,
|
||||||
|
distance
|
||||||
|
);
|
||||||
|
|
||||||
|
|
||||||
|
//printf("gjk add:%f",distance);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -0,0 +1,236 @@
|
|||||||
|
/* [SCE CONFIDENTIAL DOCUMENT]
|
||||||
|
* PLAYSTATION(R)3 SPU Optimized Bullet Physics Library (http://bulletphysics.com)
|
||||||
|
* Copyright (C) 2007 Sony Computer Entertainment Inc.
|
||||||
|
* All Rights Reserved.
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#include "BulletCollision/BroadphaseCollision/btBroadphaseProxy.h"
|
||||||
|
#include "BulletCollision/CollisionShapes/btConvexShape.h"
|
||||||
|
#include "BulletCollision/CollisionShapes/btCylinderShape.h"
|
||||||
|
|
||||||
|
|
||||||
|
struct SpuConvexPolyhedronVertexData
|
||||||
|
{
|
||||||
|
void* gSpuConvexShapePtr0;
|
||||||
|
void* gSpuConvexShapePtr1;
|
||||||
|
btPoint3* gConvexPoints0;
|
||||||
|
btPoint3* gConvexPoints1;
|
||||||
|
int gNumConvexPoints0;
|
||||||
|
int gNumConvexPoints1;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
inline btPoint3 localGetSupportingVertexWithoutMargin(int shapeType, void* shape, btVector3 localDir,struct SpuConvexPolyhedronVertexData* convexVertexData)//, int *featureIndex)
|
||||||
|
{
|
||||||
|
switch (shapeType)
|
||||||
|
{
|
||||||
|
case SPHERE_SHAPE_PROXYTYPE:
|
||||||
|
{
|
||||||
|
return btPoint3(0,0,0);
|
||||||
|
}
|
||||||
|
case BOX_SHAPE_PROXYTYPE:
|
||||||
|
{
|
||||||
|
// spu_printf("SPU: getSupport BOX_SHAPE_PROXYTYPE\n");
|
||||||
|
btConvexShape* convexShape = (btConvexShape*)shape;
|
||||||
|
btVector3 halfExtents = convexShape->getImplicitShapeDimensions();
|
||||||
|
float margin = convexShape->getMarginNV();
|
||||||
|
halfExtents -= btVector3(margin,margin,margin);
|
||||||
|
return btPoint3(
|
||||||
|
localDir.getX() < 0.0f ? -halfExtents.x() : halfExtents.x(),
|
||||||
|
localDir.getY() < 0.0f ? -halfExtents.y() : halfExtents.y(),
|
||||||
|
localDir.getZ() < 0.0f ? -halfExtents.z() : halfExtents.z());
|
||||||
|
}
|
||||||
|
|
||||||
|
case TRIANGLE_SHAPE_PROXYTYPE:
|
||||||
|
{
|
||||||
|
|
||||||
|
btVector3 dir(localDir.getX(),localDir.getY(),localDir.getZ());
|
||||||
|
btVector3* vertices = (btVector3*)shape;
|
||||||
|
btVector3 dots(dir.dot(vertices[0]), dir.dot(vertices[1]), dir.dot(vertices[2]));
|
||||||
|
btVector3 sup = vertices[dots.maxAxis()];
|
||||||
|
return btPoint3(sup.getX(),sup.getY(),sup.getZ());
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case CYLINDER_SHAPE_PROXYTYPE:
|
||||||
|
{
|
||||||
|
btCylinderShape* cylShape = (btCylinderShape*)shape;
|
||||||
|
|
||||||
|
//mapping of halfextents/dimension onto radius/height depends on how cylinder local orientation is (upAxis)
|
||||||
|
|
||||||
|
btVector3 halfExtents = cylShape->getImplicitShapeDimensions();
|
||||||
|
btVector3 v(localDir.getX(),localDir.getY(),localDir.getZ());
|
||||||
|
|
||||||
|
int cylinderUpAxis = cylShape->getUpAxis();
|
||||||
|
int XX(1),YY(0),ZZ(2);
|
||||||
|
|
||||||
|
switch (cylinderUpAxis)
|
||||||
|
{
|
||||||
|
case 0:
|
||||||
|
{
|
||||||
|
XX = 1;
|
||||||
|
YY = 0;
|
||||||
|
ZZ = 2;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case 1:
|
||||||
|
{
|
||||||
|
XX = 0;
|
||||||
|
YY = 1;
|
||||||
|
ZZ = 2;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case 2:
|
||||||
|
{
|
||||||
|
XX = 0;
|
||||||
|
YY = 2;
|
||||||
|
ZZ = 1;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
btAssert(0);
|
||||||
|
//printf("SPU:localGetSupportingVertexWithoutMargin unknown Cylinder up-axis\n");
|
||||||
|
};
|
||||||
|
|
||||||
|
btScalar radius = halfExtents[XX];
|
||||||
|
btScalar halfHeight = halfExtents[cylinderUpAxis];
|
||||||
|
|
||||||
|
btVector3 tmp;
|
||||||
|
btScalar d ;
|
||||||
|
|
||||||
|
btScalar s = btSqrt(v[XX] * v[XX] + v[ZZ] * v[ZZ]);
|
||||||
|
if (s != btScalar(0.0))
|
||||||
|
{
|
||||||
|
d = radius / s;
|
||||||
|
tmp[XX] = v[XX] * d;
|
||||||
|
tmp[YY] = v[YY] < 0.0 ? -halfHeight : halfHeight;
|
||||||
|
tmp[ZZ] = v[ZZ] * d;
|
||||||
|
return btPoint3(tmp.getX(),tmp.getY(),tmp.getZ());
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
tmp[XX] = radius;
|
||||||
|
tmp[YY] = v[YY] < 0.0 ? -halfHeight : halfHeight;
|
||||||
|
tmp[ZZ] = btScalar(0.0);
|
||||||
|
return btPoint3(tmp.getX(),tmp.getY(),tmp.getZ());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
case CAPSULE_SHAPE_PROXYTYPE:
|
||||||
|
{
|
||||||
|
//spu_printf("SPU: todo: getSupport CAPSULE_SHAPE_PROXYTYPE\n");
|
||||||
|
btVector3 vec0(localDir.getX(),localDir.getY(),localDir.getZ());
|
||||||
|
|
||||||
|
btConvexShape* cnvxShape = (btConvexShape*)shape;
|
||||||
|
btVector3 halfExtents = cnvxShape->getImplicitShapeDimensions();
|
||||||
|
btScalar halfHeight = halfExtents.getY();
|
||||||
|
btScalar radius = halfExtents.getX();
|
||||||
|
btVector3 supVec(0,0,0);
|
||||||
|
|
||||||
|
btScalar maxDot(btScalar(-1e30));
|
||||||
|
|
||||||
|
btVector3 vec = vec0;
|
||||||
|
btScalar lenSqr = vec.length2();
|
||||||
|
if (lenSqr < btScalar(0.0001))
|
||||||
|
{
|
||||||
|
vec.setValue(1,0,0);
|
||||||
|
} else
|
||||||
|
{
|
||||||
|
btScalar rlen = btScalar(1.) / btSqrt(lenSqr );
|
||||||
|
vec *= rlen;
|
||||||
|
}
|
||||||
|
btVector3 vtx;
|
||||||
|
btScalar newDot;
|
||||||
|
{
|
||||||
|
btVector3 pos(0,halfHeight,0);
|
||||||
|
vtx = pos +vec*(radius);
|
||||||
|
newDot = vec.dot(vtx);
|
||||||
|
if (newDot > maxDot)
|
||||||
|
{
|
||||||
|
maxDot = newDot;
|
||||||
|
supVec = vtx;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
{
|
||||||
|
btVector3 pos(0,-halfHeight,0);
|
||||||
|
vtx = pos +vec*(radius);
|
||||||
|
newDot = vec.dot(vtx);
|
||||||
|
if (newDot > maxDot)
|
||||||
|
{
|
||||||
|
maxDot = newDot;
|
||||||
|
supVec = vtx;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return btPoint3(supVec.getX(),supVec.getY(),supVec.getZ());
|
||||||
|
break;
|
||||||
|
};
|
||||||
|
|
||||||
|
case CONVEX_HULL_SHAPE_PROXYTYPE:
|
||||||
|
{
|
||||||
|
//spu_printf("SPU: todo: getSupport CONVEX_HULL_SHAPE_PROXYTYPE\n");
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
btPoint3* points = 0;
|
||||||
|
int numPoints = 0;
|
||||||
|
if (shape==convexVertexData->gSpuConvexShapePtr0)
|
||||||
|
{
|
||||||
|
points = convexVertexData->gConvexPoints0;
|
||||||
|
numPoints = convexVertexData->gNumConvexPoints0;
|
||||||
|
}
|
||||||
|
if (shape == convexVertexData->gSpuConvexShapePtr1)
|
||||||
|
{
|
||||||
|
points = convexVertexData->gConvexPoints1;
|
||||||
|
numPoints = convexVertexData->gNumConvexPoints1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// spu_printf("numPoints = %d\n",numPoints);
|
||||||
|
|
||||||
|
btVector3 supVec(btScalar(0.),btScalar(0.),btScalar(0.));
|
||||||
|
btScalar newDot,maxDot = btScalar(-1e30);
|
||||||
|
|
||||||
|
btVector3 vec0(localDir.getX(),localDir.getY(),localDir.getZ());
|
||||||
|
btVector3 vec = vec0;
|
||||||
|
btScalar lenSqr = vec.length2();
|
||||||
|
if (lenSqr < btScalar(0.0001))
|
||||||
|
{
|
||||||
|
vec.setValue(1,0,0);
|
||||||
|
} else
|
||||||
|
{
|
||||||
|
btScalar rlen = btScalar(1.) / btSqrt(lenSqr );
|
||||||
|
vec *= rlen;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
for (int i=0;i<numPoints;i++)
|
||||||
|
{
|
||||||
|
btPoint3 vtx = points[i];// * m_localScaling;
|
||||||
|
|
||||||
|
newDot = vec.dot(vtx);
|
||||||
|
if (newDot > maxDot)
|
||||||
|
{
|
||||||
|
maxDot = newDot;
|
||||||
|
supVec = vtx;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return btPoint3(supVec.getX(),supVec.getY(),supVec.getZ());
|
||||||
|
|
||||||
|
break;
|
||||||
|
};
|
||||||
|
|
||||||
|
default:
|
||||||
|
|
||||||
|
//spu_printf("SPU:(type %i) missing support function\n",shapeType);
|
||||||
|
|
||||||
|
|
||||||
|
#if __ASSERT
|
||||||
|
spu_printf("localGetSupportingVertexWithoutMargin() - Unsupported bound type: %d.\n", shapeType);
|
||||||
|
#endif // __ASSERT
|
||||||
|
return btPoint3(0.f, 0.f, 0.f);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -0,0 +1,339 @@
|
|||||||
|
/*
|
||||||
|
Bullet Continuous Collision Detection and Physics Library
|
||||||
|
Copyright (c) 2003-2006 Erwin Coumans http://continuousphysics.com/Bullet/
|
||||||
|
|
||||||
|
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 "SpuMinkowskiPenetrationDepthSolver.h"
|
||||||
|
#include "SpuVoronoiSimplexSolver.h"
|
||||||
|
#include "SpuGjkPairDetector.h"
|
||||||
|
#include "SpuContactResult.h"
|
||||||
|
|
||||||
|
|
||||||
|
#include "SpuLocalSupport.h"
|
||||||
|
|
||||||
|
#define NUM_UNITSPHERE_POINTS 42
|
||||||
|
static btVector3 sPenetrationDirections[NUM_UNITSPHERE_POINTS+MAX_PREFERRED_PENETRATION_DIRECTIONS*2] =
|
||||||
|
{
|
||||||
|
btVector3(btScalar(0.000000) , btScalar(-0.000000),btScalar(-1.000000)),
|
||||||
|
btVector3(btScalar(0.723608) , btScalar(-0.525725),btScalar(-0.447219)),
|
||||||
|
btVector3(btScalar(-0.276388) , btScalar(-0.850649),btScalar(-0.447219)),
|
||||||
|
btVector3(btScalar(-0.894426) , btScalar(-0.000000),btScalar(-0.447216)),
|
||||||
|
btVector3(btScalar(-0.276388) , btScalar(0.850649),btScalar(-0.447220)),
|
||||||
|
btVector3(btScalar(0.723608) , btScalar(0.525725),btScalar(-0.447219)),
|
||||||
|
btVector3(btScalar(0.276388) , btScalar(-0.850649),btScalar(0.447220)),
|
||||||
|
btVector3(btScalar(-0.723608) , btScalar(-0.525725),btScalar(0.447219)),
|
||||||
|
btVector3(btScalar(-0.723608) , btScalar(0.525725),btScalar(0.447219)),
|
||||||
|
btVector3(btScalar(0.276388) , btScalar(0.850649),btScalar(0.447219)),
|
||||||
|
btVector3(btScalar(0.894426) , btScalar(0.000000),btScalar(0.447216)),
|
||||||
|
btVector3(btScalar(-0.000000) , btScalar(0.000000),btScalar(1.000000)),
|
||||||
|
btVector3(btScalar(0.425323) , btScalar(-0.309011),btScalar(-0.850654)),
|
||||||
|
btVector3(btScalar(-0.162456) , btScalar(-0.499995),btScalar(-0.850654)),
|
||||||
|
btVector3(btScalar(0.262869) , btScalar(-0.809012),btScalar(-0.525738)),
|
||||||
|
btVector3(btScalar(0.425323) , btScalar(0.309011),btScalar(-0.850654)),
|
||||||
|
btVector3(btScalar(0.850648) , btScalar(-0.000000),btScalar(-0.525736)),
|
||||||
|
btVector3(btScalar(-0.525730) , btScalar(-0.000000),btScalar(-0.850652)),
|
||||||
|
btVector3(btScalar(-0.688190) , btScalar(-0.499997),btScalar(-0.525736)),
|
||||||
|
btVector3(btScalar(-0.162456) , btScalar(0.499995),btScalar(-0.850654)),
|
||||||
|
btVector3(btScalar(-0.688190) , btScalar(0.499997),btScalar(-0.525736)),
|
||||||
|
btVector3(btScalar(0.262869) , btScalar(0.809012),btScalar(-0.525738)),
|
||||||
|
btVector3(btScalar(0.951058) , btScalar(0.309013),btScalar(0.000000)),
|
||||||
|
btVector3(btScalar(0.951058) , btScalar(-0.309013),btScalar(0.000000)),
|
||||||
|
btVector3(btScalar(0.587786) , btScalar(-0.809017),btScalar(0.000000)),
|
||||||
|
btVector3(btScalar(0.000000) , btScalar(-1.000000),btScalar(0.000000)),
|
||||||
|
btVector3(btScalar(-0.587786) , btScalar(-0.809017),btScalar(0.000000)),
|
||||||
|
btVector3(btScalar(-0.951058) , btScalar(-0.309013),btScalar(-0.000000)),
|
||||||
|
btVector3(btScalar(-0.951058) , btScalar(0.309013),btScalar(-0.000000)),
|
||||||
|
btVector3(btScalar(-0.587786) , btScalar(0.809017),btScalar(-0.000000)),
|
||||||
|
btVector3(btScalar(-0.000000) , btScalar(1.000000),btScalar(-0.000000)),
|
||||||
|
btVector3(btScalar(0.587786) , btScalar(0.809017),btScalar(-0.000000)),
|
||||||
|
btVector3(btScalar(0.688190) , btScalar(-0.499997),btScalar(0.525736)),
|
||||||
|
btVector3(btScalar(-0.262869) , btScalar(-0.809012),btScalar(0.525738)),
|
||||||
|
btVector3(btScalar(-0.850648) , btScalar(0.000000),btScalar(0.525736)),
|
||||||
|
btVector3(btScalar(-0.262869) , btScalar(0.809012),btScalar(0.525738)),
|
||||||
|
btVector3(btScalar(0.688190) , btScalar(0.499997),btScalar(0.525736)),
|
||||||
|
btVector3(btScalar(0.525730) , btScalar(0.000000),btScalar(0.850652)),
|
||||||
|
btVector3(btScalar(0.162456) , btScalar(-0.499995),btScalar(0.850654)),
|
||||||
|
btVector3(btScalar(-0.425323) , btScalar(-0.309011),btScalar(0.850654)),
|
||||||
|
btVector3(btScalar(-0.425323) , btScalar(0.309011),btScalar(0.850654)),
|
||||||
|
btVector3(btScalar(0.162456) , btScalar(0.499995),btScalar(0.850654))
|
||||||
|
};
|
||||||
|
|
||||||
|
bool SpuMinkowskiPenetrationDepthSolver::calcPenDepth( SpuVoronoiSimplexSolver& simplexSolver,
|
||||||
|
void* convexA,void* convexB,int shapeTypeA, int shapeTypeB, float marginA, float marginB,
|
||||||
|
btTransform& transA,const btTransform& transB,
|
||||||
|
btVector3& v, btPoint3& pa, btPoint3& pb,
|
||||||
|
class btIDebugDraw* debugDraw,btStackAlloc* stackAlloc,
|
||||||
|
struct SpuConvexPolyhedronVertexData* convexVertexData
|
||||||
|
) const
|
||||||
|
{
|
||||||
|
|
||||||
|
(void)stackAlloc;
|
||||||
|
(void)v;
|
||||||
|
|
||||||
|
|
||||||
|
struct btIntermediateResult : public SpuContactResult
|
||||||
|
{
|
||||||
|
|
||||||
|
btIntermediateResult():m_hasResult(false)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
btVector3 m_normalOnBInWorld;
|
||||||
|
btVector3 m_pointInWorld;
|
||||||
|
btScalar m_depth;
|
||||||
|
bool m_hasResult;
|
||||||
|
|
||||||
|
virtual void setShapeIdentifiers(int partId0,int index0, int partId1,int index1)
|
||||||
|
{
|
||||||
|
(void)partId0;
|
||||||
|
(void)index0;
|
||||||
|
(void)partId1;
|
||||||
|
(void)index1;
|
||||||
|
}
|
||||||
|
void addContactPoint(const btVector3& normalOnBInWorld,const btVector3& pointInWorld,btScalar depth)
|
||||||
|
{
|
||||||
|
m_normalOnBInWorld = normalOnBInWorld;
|
||||||
|
m_pointInWorld = pointInWorld;
|
||||||
|
m_depth = depth;
|
||||||
|
m_hasResult = true;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
//just take fixed number of orientation, and sample the penetration depth in that direction
|
||||||
|
btScalar minProj = btScalar(1e30);
|
||||||
|
btVector3 minNorm;
|
||||||
|
btVector3 minVertex;
|
||||||
|
btVector3 minA,minB;
|
||||||
|
btVector3 seperatingAxisInA,seperatingAxisInB;
|
||||||
|
btVector3 pInA,qInB,pWorld,qWorld,w;
|
||||||
|
|
||||||
|
//#define USE_BATCHED_SUPPORT 1
|
||||||
|
#ifdef USE_BATCHED_SUPPORT
|
||||||
|
|
||||||
|
btVector3 supportVerticesABatch[NUM_UNITSPHERE_POINTS+MAX_PREFERRED_PENETRATION_DIRECTIONS*2];
|
||||||
|
btVector3 supportVerticesBBatch[NUM_UNITSPHERE_POINTS+MAX_PREFERRED_PENETRATION_DIRECTIONS*2];
|
||||||
|
btVector3 seperatingAxisInABatch[NUM_UNITSPHERE_POINTS+MAX_PREFERRED_PENETRATION_DIRECTIONS*2];
|
||||||
|
btVector3 seperatingAxisInBBatch[NUM_UNITSPHERE_POINTS+MAX_PREFERRED_PENETRATION_DIRECTIONS*2];
|
||||||
|
int i;
|
||||||
|
|
||||||
|
int numSampleDirections = NUM_UNITSPHERE_POINTS;
|
||||||
|
|
||||||
|
for (i=0;i<numSampleDirections;i++)
|
||||||
|
{
|
||||||
|
const btVector3& norm = sPenetrationDirections[i];
|
||||||
|
seperatingAxisInABatch[i] = (-norm) * transA.getBasis() ;
|
||||||
|
seperatingAxisInBBatch[i] = norm * transB.getBasis() ;
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
int numPDA = convexA->getNumPreferredPenetrationDirections();
|
||||||
|
if (numPDA)
|
||||||
|
{
|
||||||
|
for (int i=0;i<numPDA;i++)
|
||||||
|
{
|
||||||
|
btVector3 norm;
|
||||||
|
convexA->getPreferredPenetrationDirection(i,norm);
|
||||||
|
norm = transA.getBasis() * norm;
|
||||||
|
sPenetrationDirections[numSampleDirections] = norm;
|
||||||
|
seperatingAxisInABatch[numSampleDirections] = (-norm) * transA.getBasis();
|
||||||
|
seperatingAxisInBBatch[numSampleDirections] = norm * transB.getBasis();
|
||||||
|
numSampleDirections++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
int numPDB = convexB->getNumPreferredPenetrationDirections();
|
||||||
|
if (numPDB)
|
||||||
|
{
|
||||||
|
for (int i=0;i<numPDB;i++)
|
||||||
|
{
|
||||||
|
btVector3 norm;
|
||||||
|
convexB->getPreferredPenetrationDirection(i,norm);
|
||||||
|
norm = transB.getBasis() * norm;
|
||||||
|
sPenetrationDirections[numSampleDirections] = norm;
|
||||||
|
seperatingAxisInABatch[numSampleDirections] = (-norm) * transA.getBasis();
|
||||||
|
seperatingAxisInBBatch[numSampleDirections] = norm * transB.getBasis();
|
||||||
|
numSampleDirections++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
convexA->batchedUnitVectorGetSupportingVertexWithoutMargin(seperatingAxisInABatch,supportVerticesABatch,numSampleDirections);
|
||||||
|
convexB->batchedUnitVectorGetSupportingVertexWithoutMargin(seperatingAxisInBBatch,supportVerticesBBatch,numSampleDirections);
|
||||||
|
|
||||||
|
for (i=0;i<numSampleDirections;i++)
|
||||||
|
{
|
||||||
|
const btVector3& norm = sPenetrationDirections[i];
|
||||||
|
seperatingAxisInA = seperatingAxisInABatch[i];
|
||||||
|
seperatingAxisInB = seperatingAxisInBBatch[i];
|
||||||
|
|
||||||
|
pInA = supportVerticesABatch[i];
|
||||||
|
qInB = supportVerticesBBatch[i];
|
||||||
|
|
||||||
|
pWorld = transA(pInA);
|
||||||
|
qWorld = transB(qInB);
|
||||||
|
w = qWorld - pWorld;
|
||||||
|
btScalar delta = norm.dot(w);
|
||||||
|
//find smallest delta
|
||||||
|
if (delta < minProj)
|
||||||
|
{
|
||||||
|
minProj = delta;
|
||||||
|
minNorm = norm;
|
||||||
|
minA = pWorld;
|
||||||
|
minB = qWorld;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
|
||||||
|
int numSampleDirections = NUM_UNITSPHERE_POINTS;
|
||||||
|
|
||||||
|
#ifdef DO_PREFERRED_DIRECTIONS
|
||||||
|
{
|
||||||
|
int numPDA = convexA->getNumPreferredPenetrationDirections();
|
||||||
|
if (numPDA)
|
||||||
|
{
|
||||||
|
for (int i=0;i<numPDA;i++)
|
||||||
|
{
|
||||||
|
btVector3 norm;
|
||||||
|
convexA->getPreferredPenetrationDirection(i,norm);
|
||||||
|
norm = transA.getBasis() * norm;
|
||||||
|
sPenetrationDirections[numSampleDirections] = norm;
|
||||||
|
numSampleDirections++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
int numPDB = convexB->getNumPreferredPenetrationDirections();
|
||||||
|
if (numPDB)
|
||||||
|
{
|
||||||
|
for (int i=0;i<numPDB;i++)
|
||||||
|
{
|
||||||
|
btVector3 norm;
|
||||||
|
convexB->getPreferredPenetrationDirection(i,norm);
|
||||||
|
norm = transB.getBasis() * norm;
|
||||||
|
sPenetrationDirections[numSampleDirections] = norm;
|
||||||
|
numSampleDirections++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif //DO_PREFERRED_DIRECTIONS
|
||||||
|
|
||||||
|
for (int i=0;i<numSampleDirections;i++)
|
||||||
|
{
|
||||||
|
const btVector3& norm = sPenetrationDirections[i];
|
||||||
|
seperatingAxisInA = (-norm)* transA.getBasis();
|
||||||
|
seperatingAxisInB = norm* transB.getBasis();
|
||||||
|
|
||||||
|
pInA = localGetSupportingVertexWithoutMargin(shapeTypeA, convexA, seperatingAxisInA,convexVertexData);//, NULL);
|
||||||
|
qInB = localGetSupportingVertexWithoutMargin(shapeTypeB, convexB, seperatingAxisInB,convexVertexData);//, NULL);
|
||||||
|
|
||||||
|
// pInA = convexA->localGetSupportingVertexWithoutMargin(seperatingAxisInA);
|
||||||
|
// qInB = convexB->localGetSupportingVertexWithoutMargin(seperatingAxisInB);
|
||||||
|
|
||||||
|
pWorld = transA(pInA);
|
||||||
|
qWorld = transB(qInB);
|
||||||
|
w = qWorld - pWorld;
|
||||||
|
btScalar delta = norm.dot(w);
|
||||||
|
//find smallest delta
|
||||||
|
if (delta < minProj)
|
||||||
|
{
|
||||||
|
minProj = delta;
|
||||||
|
minNorm = norm;
|
||||||
|
minA = pWorld;
|
||||||
|
minB = qWorld;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif //USE_BATCHED_SUPPORT
|
||||||
|
|
||||||
|
//add the margins
|
||||||
|
|
||||||
|
minA += minNorm*marginA;
|
||||||
|
minB -= minNorm*marginB;
|
||||||
|
//no penetration
|
||||||
|
if (minProj < btScalar(0.))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
minProj += (marginA + marginB);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
//#define DEBUG_DRAW 1
|
||||||
|
#ifdef DEBUG_DRAW
|
||||||
|
if (debugDraw)
|
||||||
|
{
|
||||||
|
btVector3 color(0,1,0);
|
||||||
|
debugDraw->drawLine(minA,minB,color);
|
||||||
|
color = btVector3 (1,1,1);
|
||||||
|
btVector3 vec = minB-minA;
|
||||||
|
btScalar prj2 = minNorm.dot(vec);
|
||||||
|
debugDraw->drawLine(minA,minA+(minNorm*minProj),color);
|
||||||
|
|
||||||
|
}
|
||||||
|
#endif //DEBUG_DRAW
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
SpuGjkPairDetector gjkdet(convexA,convexB,shapeTypeA,shapeTypeB,marginA,marginB,&simplexSolver,0);
|
||||||
|
|
||||||
|
btScalar offsetDist = minProj;
|
||||||
|
btVector3 offset = minNorm * offsetDist;
|
||||||
|
|
||||||
|
|
||||||
|
SpuClosestPointInput input;
|
||||||
|
|
||||||
|
btVector3 newOrg = transA.getOrigin() + offset;
|
||||||
|
|
||||||
|
btTransform displacedTrans = transA;
|
||||||
|
displacedTrans.setOrigin(newOrg);
|
||||||
|
|
||||||
|
input.m_transformA = displacedTrans;
|
||||||
|
input.m_transformB = transB;
|
||||||
|
input.m_maximumDistanceSquared = btScalar(1e30);//minProj;
|
||||||
|
|
||||||
|
btIntermediateResult res;
|
||||||
|
gjkdet.getClosestPoints(input,res);
|
||||||
|
|
||||||
|
btScalar correctedMinNorm = minProj - res.m_depth;
|
||||||
|
|
||||||
|
|
||||||
|
//the penetration depth is over-estimated, relax it
|
||||||
|
btScalar penetration_relaxation= btScalar(1.);
|
||||||
|
minNorm*=penetration_relaxation;
|
||||||
|
|
||||||
|
if (res.m_hasResult)
|
||||||
|
{
|
||||||
|
|
||||||
|
pa = res.m_pointInWorld - minNorm * correctedMinNorm;
|
||||||
|
pb = res.m_pointInWorld;
|
||||||
|
|
||||||
|
#ifdef DEBUG_DRAW
|
||||||
|
if (debugDraw)
|
||||||
|
{
|
||||||
|
btVector3 color(1,0,0);
|
||||||
|
debugDraw->drawLine(pa,pb,color);
|
||||||
|
}
|
||||||
|
#endif//DEBUG_DRAW
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
return res.m_hasResult;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -0,0 +1,51 @@
|
|||||||
|
/* [SCE CONFIDENTIAL DOCUMENT]
|
||||||
|
* PLAYSTATION(R)3 SPU Optimized Bullet Physics Library (http://bulletphysics.com)
|
||||||
|
* Copyright (C) 2007 Sony Computer Entertainment Inc.
|
||||||
|
* All Rights Reserved.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
Bullet Continuous Collision Detection and Physics Library
|
||||||
|
Copyright (c) 2003-2006 Erwin Coumans http://continuousphysics.com/Bullet/
|
||||||
|
|
||||||
|
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 MINKOWSKI_PENETRATION_DEPTH_SOLVER_H
|
||||||
|
#define MINKOWSKI_PENETRATION_DEPTH_SOLVER_H
|
||||||
|
|
||||||
|
|
||||||
|
#include "SpuConvexPenetrationDepthSolver.h"
|
||||||
|
|
||||||
|
class btStackAlloc;
|
||||||
|
class btIDebugDraw;
|
||||||
|
class SpuVoronoiSimplexSolver;
|
||||||
|
|
||||||
|
///MinkowskiPenetrationDepthSolver implements bruteforce penetration depth estimation.
|
||||||
|
///Implementation is based on sampling the depth using support mapping, and using GJK step to get the witness points.
|
||||||
|
class SpuMinkowskiPenetrationDepthSolver : public SpuConvexPenetrationDepthSolver
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
|
||||||
|
virtual bool calcPenDepth( SpuVoronoiSimplexSolver& simplexSolver,
|
||||||
|
void* convexA,void* convexB,int shapeTypeA, int shapeTypeB, float marginA, float marginB,
|
||||||
|
btTransform& transA,const btTransform& transB,
|
||||||
|
btVector3& v, btPoint3& pa, btPoint3& pb,
|
||||||
|
class btIDebugDraw* debugDraw,btStackAlloc* stackAlloc,
|
||||||
|
struct SpuConvexPolyhedronVertexData* convexVertexData
|
||||||
|
) const;
|
||||||
|
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
#endif //MINKOWSKI_PENETRATION_DEPTH_SOLVER_H
|
||||||
|
|
||||||
@@ -0,0 +1,606 @@
|
|||||||
|
|
||||||
|
/*
|
||||||
|
Bullet Continuous Collision Detection and Physics Library
|
||||||
|
Copyright (c) 2003-2006 Erwin Coumans http://continuousphysics.com/Bullet/
|
||||||
|
|
||||||
|
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.
|
||||||
|
|
||||||
|
Elsevier CDROM license agreements grants nonexclusive license to use the software
|
||||||
|
for any purpose, commercial or non-commercial as long as the following credit is included
|
||||||
|
identifying the original source of the software:
|
||||||
|
|
||||||
|
Parts of the source are "from the book Real-Time Collision Detection by
|
||||||
|
Christer Ericson, published by Morgan Kaufmann Publishers,
|
||||||
|
(c) 2005 Elsevier Inc."
|
||||||
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
#include "SpuVoronoiSimplexSolver.h"
|
||||||
|
#include <assert.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
|
||||||
|
#define VERTA 0
|
||||||
|
#define VERTB 1
|
||||||
|
#define VERTC 2
|
||||||
|
#define VERTD 3
|
||||||
|
|
||||||
|
#define CATCH_DEGENERATE_TETRAHEDRON 1
|
||||||
|
void SpuVoronoiSimplexSolver::removeVertex(int index)
|
||||||
|
{
|
||||||
|
|
||||||
|
assert(m_numVertices>0);
|
||||||
|
m_numVertices--;
|
||||||
|
m_simplexVectorW[index] = m_simplexVectorW[m_numVertices];
|
||||||
|
m_simplexPointsP[index] = m_simplexPointsP[m_numVertices];
|
||||||
|
m_simplexPointsQ[index] = m_simplexPointsQ[m_numVertices];
|
||||||
|
}
|
||||||
|
|
||||||
|
void SpuVoronoiSimplexSolver::reduceVertices (const SpuUsageBitfield& usedVerts)
|
||||||
|
{
|
||||||
|
if ((numVertices() >= 4) && (!usedVerts.usedVertexD))
|
||||||
|
removeVertex(3);
|
||||||
|
|
||||||
|
if ((numVertices() >= 3) && (!usedVerts.usedVertexC))
|
||||||
|
removeVertex(2);
|
||||||
|
|
||||||
|
if ((numVertices() >= 2) && (!usedVerts.usedVertexB))
|
||||||
|
removeVertex(1);
|
||||||
|
|
||||||
|
if ((numVertices() >= 1) && (!usedVerts.usedVertexA))
|
||||||
|
removeVertex(0);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
//clear the simplex, remove all the vertices
|
||||||
|
void SpuVoronoiSimplexSolver::reset()
|
||||||
|
{
|
||||||
|
m_cachedValidClosest = false;
|
||||||
|
m_numVertices = 0;
|
||||||
|
m_needsUpdate = true;
|
||||||
|
m_lastW = btVector3(btScalar(1e30),btScalar(1e30),btScalar(1e30));
|
||||||
|
m_cachedBC.reset();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
//add a vertex
|
||||||
|
void SpuVoronoiSimplexSolver::addVertex(const btVector3& w, const btPoint3& p, const btPoint3& q)
|
||||||
|
{
|
||||||
|
m_lastW = w;
|
||||||
|
m_needsUpdate = true;
|
||||||
|
|
||||||
|
m_simplexVectorW[m_numVertices] = w;
|
||||||
|
m_simplexPointsP[m_numVertices] = p;
|
||||||
|
m_simplexPointsQ[m_numVertices] = q;
|
||||||
|
|
||||||
|
m_numVertices++;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool SpuVoronoiSimplexSolver::updateClosestVectorAndPoints()
|
||||||
|
{
|
||||||
|
|
||||||
|
if (m_needsUpdate)
|
||||||
|
{
|
||||||
|
m_cachedBC.reset();
|
||||||
|
|
||||||
|
m_needsUpdate = false;
|
||||||
|
|
||||||
|
switch (numVertices())
|
||||||
|
{
|
||||||
|
case 0:
|
||||||
|
m_cachedValidClosest = false;
|
||||||
|
break;
|
||||||
|
case 1:
|
||||||
|
{
|
||||||
|
m_cachedP1 = m_simplexPointsP[0];
|
||||||
|
m_cachedP2 = m_simplexPointsQ[0];
|
||||||
|
m_cachedV = m_cachedP1-m_cachedP2; //== m_simplexVectorW[0]
|
||||||
|
m_cachedBC.reset();
|
||||||
|
m_cachedBC.setBarycentricCoordinates(btScalar(1.),btScalar(0.),btScalar(0.),btScalar(0.));
|
||||||
|
m_cachedValidClosest = m_cachedBC.isValid();
|
||||||
|
break;
|
||||||
|
};
|
||||||
|
case 2:
|
||||||
|
{
|
||||||
|
//closest point origin from line segment
|
||||||
|
const btVector3& from = m_simplexVectorW[0];
|
||||||
|
const btVector3& to = m_simplexVectorW[1];
|
||||||
|
btVector3 nearest;
|
||||||
|
|
||||||
|
btVector3 p (btScalar(0.),btScalar(0.),btScalar(0.));
|
||||||
|
btVector3 diff = p - from;
|
||||||
|
btVector3 v = to - from;
|
||||||
|
btScalar t = v.dot(diff);
|
||||||
|
|
||||||
|
if (t > 0) {
|
||||||
|
btScalar dotVV = v.dot(v);
|
||||||
|
if (t < dotVV) {
|
||||||
|
t /= dotVV;
|
||||||
|
diff -= t*v;
|
||||||
|
m_cachedBC.m_usedVertices.usedVertexA = true;
|
||||||
|
m_cachedBC.m_usedVertices.usedVertexB = true;
|
||||||
|
} else {
|
||||||
|
t = 1;
|
||||||
|
diff -= v;
|
||||||
|
//reduce to 1 point
|
||||||
|
m_cachedBC.m_usedVertices.usedVertexB = true;
|
||||||
|
}
|
||||||
|
} else
|
||||||
|
{
|
||||||
|
t = 0;
|
||||||
|
//reduce to 1 point
|
||||||
|
m_cachedBC.m_usedVertices.usedVertexA = true;
|
||||||
|
}
|
||||||
|
m_cachedBC.setBarycentricCoordinates(1-t,t);
|
||||||
|
nearest = from + t*v;
|
||||||
|
|
||||||
|
m_cachedP1 = m_simplexPointsP[0] + t * (m_simplexPointsP[1] - m_simplexPointsP[0]);
|
||||||
|
m_cachedP2 = m_simplexPointsQ[0] + t * (m_simplexPointsQ[1] - m_simplexPointsQ[0]);
|
||||||
|
m_cachedV = m_cachedP1 - m_cachedP2;
|
||||||
|
|
||||||
|
reduceVertices(m_cachedBC.m_usedVertices);
|
||||||
|
|
||||||
|
m_cachedValidClosest = m_cachedBC.isValid();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case 3:
|
||||||
|
{
|
||||||
|
//closest point origin from triangle
|
||||||
|
btVector3 p (btScalar(0.),btScalar(0.),btScalar(0.));
|
||||||
|
|
||||||
|
const btVector3& a = m_simplexVectorW[0];
|
||||||
|
const btVector3& b = m_simplexVectorW[1];
|
||||||
|
const btVector3& c = m_simplexVectorW[2];
|
||||||
|
|
||||||
|
closestPtPointTriangle(p,a,b,c,m_cachedBC);
|
||||||
|
m_cachedP1 = m_simplexPointsP[0] * m_cachedBC.m_barycentricCoords[0] +
|
||||||
|
m_simplexPointsP[1] * m_cachedBC.m_barycentricCoords[1] +
|
||||||
|
m_simplexPointsP[2] * m_cachedBC.m_barycentricCoords[2] +
|
||||||
|
m_simplexPointsP[3] * m_cachedBC.m_barycentricCoords[3];
|
||||||
|
|
||||||
|
m_cachedP2 = m_simplexPointsQ[0] * m_cachedBC.m_barycentricCoords[0] +
|
||||||
|
m_simplexPointsQ[1] * m_cachedBC.m_barycentricCoords[1] +
|
||||||
|
m_simplexPointsQ[2] * m_cachedBC.m_barycentricCoords[2] +
|
||||||
|
m_simplexPointsQ[3] * m_cachedBC.m_barycentricCoords[3];
|
||||||
|
|
||||||
|
m_cachedV = m_cachedP1-m_cachedP2;
|
||||||
|
|
||||||
|
reduceVertices (m_cachedBC.m_usedVertices);
|
||||||
|
m_cachedValidClosest = m_cachedBC.isValid();
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case 4:
|
||||||
|
{
|
||||||
|
|
||||||
|
|
||||||
|
btVector3 p (btScalar(0.),btScalar(0.),btScalar(0.));
|
||||||
|
|
||||||
|
const btVector3& a = m_simplexVectorW[0];
|
||||||
|
const btVector3& b = m_simplexVectorW[1];
|
||||||
|
const btVector3& c = m_simplexVectorW[2];
|
||||||
|
const btVector3& d = m_simplexVectorW[3];
|
||||||
|
|
||||||
|
bool hasSeperation = closestPtPointTetrahedron(p,a,b,c,d,m_cachedBC);
|
||||||
|
|
||||||
|
if (hasSeperation)
|
||||||
|
{
|
||||||
|
|
||||||
|
m_cachedP1 = m_simplexPointsP[0] * m_cachedBC.m_barycentricCoords[0] +
|
||||||
|
m_simplexPointsP[1] * m_cachedBC.m_barycentricCoords[1] +
|
||||||
|
m_simplexPointsP[2] * m_cachedBC.m_barycentricCoords[2] +
|
||||||
|
m_simplexPointsP[3] * m_cachedBC.m_barycentricCoords[3];
|
||||||
|
|
||||||
|
m_cachedP2 = m_simplexPointsQ[0] * m_cachedBC.m_barycentricCoords[0] +
|
||||||
|
m_simplexPointsQ[1] * m_cachedBC.m_barycentricCoords[1] +
|
||||||
|
m_simplexPointsQ[2] * m_cachedBC.m_barycentricCoords[2] +
|
||||||
|
m_simplexPointsQ[3] * m_cachedBC.m_barycentricCoords[3];
|
||||||
|
|
||||||
|
m_cachedV = m_cachedP1-m_cachedP2;
|
||||||
|
reduceVertices (m_cachedBC.m_usedVertices);
|
||||||
|
} else
|
||||||
|
{
|
||||||
|
// printf("sub distance got penetration\n");
|
||||||
|
|
||||||
|
if (m_cachedBC.m_degenerate)
|
||||||
|
{
|
||||||
|
m_cachedValidClosest = false;
|
||||||
|
} else
|
||||||
|
{
|
||||||
|
m_cachedValidClosest = true;
|
||||||
|
//degenerate case == false, penetration = true + zero
|
||||||
|
m_cachedV.setValue(btScalar(0.),btScalar(0.),btScalar(0.));
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
m_cachedValidClosest = m_cachedBC.isValid();
|
||||||
|
|
||||||
|
//closest point origin from tetrahedron
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
{
|
||||||
|
m_cachedValidClosest = false;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
return m_cachedValidClosest;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
//return/calculate the closest vertex
|
||||||
|
bool SpuVoronoiSimplexSolver::closest(btVector3& v)
|
||||||
|
{
|
||||||
|
bool succes = updateClosestVectorAndPoints();
|
||||||
|
v = m_cachedV;
|
||||||
|
return succes;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
btScalar SpuVoronoiSimplexSolver::maxVertex()
|
||||||
|
{
|
||||||
|
int i, numverts = numVertices();
|
||||||
|
btScalar maxV = btScalar(0.);
|
||||||
|
for (i=0;i<numverts;i++)
|
||||||
|
{
|
||||||
|
btScalar curLen2 = m_simplexVectorW[i].length2();
|
||||||
|
if (maxV < curLen2)
|
||||||
|
maxV = curLen2;
|
||||||
|
}
|
||||||
|
return maxV;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
//return the current simplex
|
||||||
|
int SpuVoronoiSimplexSolver::getSimplex(btPoint3 *pBuf, btPoint3 *qBuf, btVector3 *yBuf) const
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
for (i=0;i<numVertices();i++)
|
||||||
|
{
|
||||||
|
yBuf[i] = m_simplexVectorW[i];
|
||||||
|
pBuf[i] = m_simplexPointsP[i];
|
||||||
|
qBuf[i] = m_simplexPointsQ[i];
|
||||||
|
}
|
||||||
|
return numVertices();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
bool SpuVoronoiSimplexSolver::inSimplex(const btVector3& w)
|
||||||
|
{
|
||||||
|
bool found = false;
|
||||||
|
int i, numverts = numVertices();
|
||||||
|
//btScalar maxV = btScalar(0.);
|
||||||
|
|
||||||
|
//w is in the current (reduced) simplex
|
||||||
|
for (i=0;i<numverts;i++)
|
||||||
|
{
|
||||||
|
if (m_simplexVectorW[i] == w)
|
||||||
|
found = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
//check in case lastW is already removed
|
||||||
|
if (w == m_lastW)
|
||||||
|
return true;
|
||||||
|
|
||||||
|
return found;
|
||||||
|
}
|
||||||
|
|
||||||
|
void SpuVoronoiSimplexSolver::backup_closest(btVector3& v)
|
||||||
|
{
|
||||||
|
v = m_cachedV;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
bool SpuVoronoiSimplexSolver::emptySimplex() const
|
||||||
|
{
|
||||||
|
return (numVertices() == 0);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void SpuVoronoiSimplexSolver::compute_points(btPoint3& p1, btPoint3& p2)
|
||||||
|
{
|
||||||
|
updateClosestVectorAndPoints();
|
||||||
|
p1 = m_cachedP1;
|
||||||
|
p2 = m_cachedP2;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
bool SpuVoronoiSimplexSolver::closestPtPointTriangle(const btPoint3& p, const btPoint3& a, const btPoint3& b, const btPoint3& c,SpuSubSimplexClosestResult& result)
|
||||||
|
{
|
||||||
|
result.m_usedVertices.reset();
|
||||||
|
|
||||||
|
// Check if P in vertex region outside A
|
||||||
|
btVector3 ab = b - a;
|
||||||
|
btVector3 ac = c - a;
|
||||||
|
btVector3 ap = p - a;
|
||||||
|
btScalar d1 = ab.dot(ap);
|
||||||
|
btScalar d2 = ac.dot(ap);
|
||||||
|
if (d1 <= btScalar(0.0) && d2 <= btScalar(0.0))
|
||||||
|
{
|
||||||
|
result.m_closestPointOnSimplex = a;
|
||||||
|
result.m_usedVertices.usedVertexA = true;
|
||||||
|
result.setBarycentricCoordinates(1,0,0);
|
||||||
|
return true;// a; // barycentric coordinates (1,0,0)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if P in vertex region outside B
|
||||||
|
btVector3 bp = p - b;
|
||||||
|
btScalar d3 = ab.dot(bp);
|
||||||
|
btScalar d4 = ac.dot(bp);
|
||||||
|
if (d3 >= btScalar(0.0) && d4 <= d3)
|
||||||
|
{
|
||||||
|
result.m_closestPointOnSimplex = b;
|
||||||
|
result.m_usedVertices.usedVertexB = true;
|
||||||
|
result.setBarycentricCoordinates(0,1,0);
|
||||||
|
|
||||||
|
return true; // b; // barycentric coordinates (0,1,0)
|
||||||
|
}
|
||||||
|
// Check if P in edge region of AB, if so return projection of P onto AB
|
||||||
|
btScalar vc = d1*d4 - d3*d2;
|
||||||
|
if (vc <= btScalar(0.0) && d1 >= btScalar(0.0) && d3 <= btScalar(0.0)) {
|
||||||
|
btScalar v = d1 / (d1 - d3);
|
||||||
|
result.m_closestPointOnSimplex = a + v * ab;
|
||||||
|
result.m_usedVertices.usedVertexA = true;
|
||||||
|
result.m_usedVertices.usedVertexB = true;
|
||||||
|
result.setBarycentricCoordinates(1-v,v,0);
|
||||||
|
return true;
|
||||||
|
//return a + v * ab; // barycentric coordinates (1-v,v,0)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if P in vertex region outside C
|
||||||
|
btVector3 cp = p - c;
|
||||||
|
btScalar d5 = ab.dot(cp);
|
||||||
|
btScalar d6 = ac.dot(cp);
|
||||||
|
if (d6 >= btScalar(0.0) && d5 <= d6)
|
||||||
|
{
|
||||||
|
result.m_closestPointOnSimplex = c;
|
||||||
|
result.m_usedVertices.usedVertexC = true;
|
||||||
|
result.setBarycentricCoordinates(0,0,1);
|
||||||
|
return true;//c; // barycentric coordinates (0,0,1)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if P in edge region of AC, if so return projection of P onto AC
|
||||||
|
btScalar vb = d5*d2 - d1*d6;
|
||||||
|
if (vb <= btScalar(0.0) && d2 >= btScalar(0.0) && d6 <= btScalar(0.0)) {
|
||||||
|
btScalar w = d2 / (d2 - d6);
|
||||||
|
result.m_closestPointOnSimplex = a + w * ac;
|
||||||
|
result.m_usedVertices.usedVertexA = true;
|
||||||
|
result.m_usedVertices.usedVertexC = true;
|
||||||
|
result.setBarycentricCoordinates(1-w,0,w);
|
||||||
|
return true;
|
||||||
|
//return a + w * ac; // barycentric coordinates (1-w,0,w)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if P in edge region of BC, if so return projection of P onto BC
|
||||||
|
btScalar va = d3*d6 - d5*d4;
|
||||||
|
if (va <= btScalar(0.0) && (d4 - d3) >= btScalar(0.0) && (d5 - d6) >= btScalar(0.0)) {
|
||||||
|
btScalar w = (d4 - d3) / ((d4 - d3) + (d5 - d6));
|
||||||
|
|
||||||
|
result.m_closestPointOnSimplex = b + w * (c - b);
|
||||||
|
result.m_usedVertices.usedVertexB = true;
|
||||||
|
result.m_usedVertices.usedVertexC = true;
|
||||||
|
result.setBarycentricCoordinates(0,1-w,w);
|
||||||
|
return true;
|
||||||
|
// return b + w * (c - b); // barycentric coordinates (0,1-w,w)
|
||||||
|
}
|
||||||
|
|
||||||
|
// P inside face region. Compute Q through its barycentric coordinates (u,v,w)
|
||||||
|
btScalar denom = btScalar(1.0) / (va + vb + vc);
|
||||||
|
btScalar v = vb * denom;
|
||||||
|
btScalar w = vc * denom;
|
||||||
|
|
||||||
|
result.m_closestPointOnSimplex = a + ab * v + ac * w;
|
||||||
|
result.m_usedVertices.usedVertexA = true;
|
||||||
|
result.m_usedVertices.usedVertexB = true;
|
||||||
|
result.m_usedVertices.usedVertexC = true;
|
||||||
|
result.setBarycentricCoordinates(1-v-w,v,w);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
// return a + ab * v + ac * w; // = u*a + v*b + w*c, u = va * denom = btScalar(1.0) - v - w
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/// Test if point p and d lie on opposite sides of plane through abc
|
||||||
|
int SpuVoronoiSimplexSolver::pointOutsideOfPlane(const btPoint3& p, const btPoint3& a, const btPoint3& b, const btPoint3& c, const btPoint3& d)
|
||||||
|
{
|
||||||
|
btVector3 normal = (b-a).cross(c-a);
|
||||||
|
|
||||||
|
btScalar signp = (p - a).dot(normal); // [AP AB AC]
|
||||||
|
btScalar signd = (d - a).dot( normal); // [AD AB AC]
|
||||||
|
|
||||||
|
#ifdef CATCH_DEGENERATE_TETRAHEDRON
|
||||||
|
#ifdef BT_USE_DOUBLE_PRECISION
|
||||||
|
if (signd * signd < (btScalar(1e-8) * btScalar(1e-8)))
|
||||||
|
{
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
if (signd * signd < (btScalar(1e-4) * btScalar(1e-4)))
|
||||||
|
{
|
||||||
|
// printf("affine dependent/degenerate\n");//
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif
|
||||||
|
// Points on opposite sides if expression signs are opposite
|
||||||
|
return signp * signd < btScalar(0.);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
bool SpuVoronoiSimplexSolver::closestPtPointTetrahedron(const btPoint3& p, const btPoint3& a, const btPoint3& b, const btPoint3& c, const btPoint3& d, SpuSubSimplexClosestResult& finalResult)
|
||||||
|
{
|
||||||
|
SpuSubSimplexClosestResult tempResult;
|
||||||
|
|
||||||
|
// Start out assuming point inside all halfspaces, so closest to itself
|
||||||
|
finalResult.m_closestPointOnSimplex = p;
|
||||||
|
finalResult.m_usedVertices.reset();
|
||||||
|
finalResult.m_usedVertices.usedVertexA = true;
|
||||||
|
finalResult.m_usedVertices.usedVertexB = true;
|
||||||
|
finalResult.m_usedVertices.usedVertexC = true;
|
||||||
|
finalResult.m_usedVertices.usedVertexD = true;
|
||||||
|
|
||||||
|
int pointOutsideABC = pointOutsideOfPlane(p, a, b, c, d);
|
||||||
|
int pointOutsideACD = pointOutsideOfPlane(p, a, c, d, b);
|
||||||
|
int pointOutsideADB = pointOutsideOfPlane(p, a, d, b, c);
|
||||||
|
int pointOutsideBDC = pointOutsideOfPlane(p, b, d, c, a);
|
||||||
|
|
||||||
|
if (pointOutsideABC < 0 || pointOutsideACD < 0 || pointOutsideADB < 0 || pointOutsideBDC < 0)
|
||||||
|
{
|
||||||
|
finalResult.m_degenerate = true;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!pointOutsideABC && !pointOutsideACD && !pointOutsideADB && !pointOutsideBDC)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
btScalar bestSqDist = FLT_MAX;
|
||||||
|
// If point outside face abc then compute closest point on abc
|
||||||
|
if (pointOutsideABC)
|
||||||
|
{
|
||||||
|
closestPtPointTriangle(p, a, b, c,tempResult);
|
||||||
|
btPoint3 q = tempResult.m_closestPointOnSimplex;
|
||||||
|
|
||||||
|
btScalar sqDist = (q - p).dot( q - p);
|
||||||
|
// Update best closest point if (squared) distance is less than current best
|
||||||
|
if (sqDist < bestSqDist) {
|
||||||
|
bestSqDist = sqDist;
|
||||||
|
finalResult.m_closestPointOnSimplex = q;
|
||||||
|
//convert result bitmask!
|
||||||
|
finalResult.m_usedVertices.reset();
|
||||||
|
finalResult.m_usedVertices.usedVertexA = tempResult.m_usedVertices.usedVertexA;
|
||||||
|
finalResult.m_usedVertices.usedVertexB = tempResult.m_usedVertices.usedVertexB;
|
||||||
|
finalResult.m_usedVertices.usedVertexC = tempResult.m_usedVertices.usedVertexC;
|
||||||
|
finalResult.setBarycentricCoordinates(
|
||||||
|
tempResult.m_barycentricCoords[VERTA],
|
||||||
|
tempResult.m_barycentricCoords[VERTB],
|
||||||
|
tempResult.m_barycentricCoords[VERTC],
|
||||||
|
0
|
||||||
|
);
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Repeat test for face acd
|
||||||
|
if (pointOutsideACD)
|
||||||
|
{
|
||||||
|
closestPtPointTriangle(p, a, c, d,tempResult);
|
||||||
|
btPoint3 q = tempResult.m_closestPointOnSimplex;
|
||||||
|
//convert result bitmask!
|
||||||
|
|
||||||
|
btScalar sqDist = (q - p).dot( q - p);
|
||||||
|
if (sqDist < bestSqDist)
|
||||||
|
{
|
||||||
|
bestSqDist = sqDist;
|
||||||
|
finalResult.m_closestPointOnSimplex = q;
|
||||||
|
finalResult.m_usedVertices.reset();
|
||||||
|
finalResult.m_usedVertices.usedVertexA = tempResult.m_usedVertices.usedVertexA;
|
||||||
|
finalResult.m_usedVertices.usedVertexC = tempResult.m_usedVertices.usedVertexB;
|
||||||
|
finalResult.m_usedVertices.usedVertexD = tempResult.m_usedVertices.usedVertexC;
|
||||||
|
finalResult.setBarycentricCoordinates(
|
||||||
|
tempResult.m_barycentricCoords[VERTA],
|
||||||
|
0,
|
||||||
|
tempResult.m_barycentricCoords[VERTB],
|
||||||
|
tempResult.m_barycentricCoords[VERTC]
|
||||||
|
);
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Repeat test for face adb
|
||||||
|
|
||||||
|
|
||||||
|
if (pointOutsideADB)
|
||||||
|
{
|
||||||
|
closestPtPointTriangle(p, a, d, b,tempResult);
|
||||||
|
btPoint3 q = tempResult.m_closestPointOnSimplex;
|
||||||
|
//convert result bitmask!
|
||||||
|
|
||||||
|
btScalar sqDist = (q - p).dot( q - p);
|
||||||
|
if (sqDist < bestSqDist)
|
||||||
|
{
|
||||||
|
bestSqDist = sqDist;
|
||||||
|
finalResult.m_closestPointOnSimplex = q;
|
||||||
|
finalResult.m_usedVertices.reset();
|
||||||
|
finalResult.m_usedVertices.usedVertexA = tempResult.m_usedVertices.usedVertexA;
|
||||||
|
finalResult.m_usedVertices.usedVertexD = tempResult.m_usedVertices.usedVertexB;
|
||||||
|
finalResult.m_usedVertices.usedVertexB = tempResult.m_usedVertices.usedVertexC;
|
||||||
|
finalResult.setBarycentricCoordinates(
|
||||||
|
tempResult.m_barycentricCoords[VERTA],
|
||||||
|
tempResult.m_barycentricCoords[VERTC],
|
||||||
|
0,
|
||||||
|
tempResult.m_barycentricCoords[VERTB]
|
||||||
|
);
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Repeat test for face bdc
|
||||||
|
|
||||||
|
|
||||||
|
if (pointOutsideBDC)
|
||||||
|
{
|
||||||
|
closestPtPointTriangle(p, b, d, c,tempResult);
|
||||||
|
btPoint3 q = tempResult.m_closestPointOnSimplex;
|
||||||
|
//convert result bitmask!
|
||||||
|
btScalar sqDist = (q - p).dot( q - p);
|
||||||
|
if (sqDist < bestSqDist)
|
||||||
|
{
|
||||||
|
bestSqDist = sqDist;
|
||||||
|
finalResult.m_closestPointOnSimplex = q;
|
||||||
|
finalResult.m_usedVertices.reset();
|
||||||
|
finalResult.m_usedVertices.usedVertexB = tempResult.m_usedVertices.usedVertexA;
|
||||||
|
finalResult.m_usedVertices.usedVertexD = tempResult.m_usedVertices.usedVertexB;
|
||||||
|
finalResult.m_usedVertices.usedVertexC = tempResult.m_usedVertices.usedVertexC;
|
||||||
|
|
||||||
|
finalResult.setBarycentricCoordinates(
|
||||||
|
0,
|
||||||
|
tempResult.m_barycentricCoords[VERTA],
|
||||||
|
tempResult.m_barycentricCoords[VERTC],
|
||||||
|
tempResult.m_barycentricCoords[VERTB]
|
||||||
|
);
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//help! we ended up full !
|
||||||
|
|
||||||
|
if (finalResult.m_usedVertices.usedVertexA &&
|
||||||
|
finalResult.m_usedVertices.usedVertexB &&
|
||||||
|
finalResult.m_usedVertices.usedVertexC &&
|
||||||
|
finalResult.m_usedVertices.usedVertexD)
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
@@ -0,0 +1,161 @@
|
|||||||
|
/* [SCE CONFIDENTIAL DOCUMENT]
|
||||||
|
* PLAYSTATION(R)3 SPU Optimized Bullet Physics Library (http://bulletphysics.com)
|
||||||
|
* Copyright (C) 2007 Sony Computer Entertainment Inc.
|
||||||
|
* All Rights Reserved.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
Bullet Continuous Collision Detection and Physics Library
|
||||||
|
Copyright (c) 2003-2006 Erwin Coumans http://continuousphysics.com/Bullet/
|
||||||
|
|
||||||
|
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 SPUVoronoiSimplexSolver_H
|
||||||
|
#define SPUVoronoiSimplexSolver_H
|
||||||
|
|
||||||
|
#include <LinearMath/btTransform.h>
|
||||||
|
#include <LinearMath/btPoint3.h>
|
||||||
|
|
||||||
|
#define VORONOI_SIMPLEX_MAX_VERTS 5
|
||||||
|
|
||||||
|
struct SpuUsageBitfield{
|
||||||
|
SpuUsageBitfield()
|
||||||
|
{
|
||||||
|
reset();
|
||||||
|
}
|
||||||
|
|
||||||
|
void reset()
|
||||||
|
{
|
||||||
|
usedVertexA = false;
|
||||||
|
usedVertexB = false;
|
||||||
|
usedVertexC = false;
|
||||||
|
usedVertexD = false;
|
||||||
|
}
|
||||||
|
unsigned short usedVertexA : 1;
|
||||||
|
unsigned short usedVertexB : 1;
|
||||||
|
unsigned short usedVertexC : 1;
|
||||||
|
unsigned short usedVertexD : 1;
|
||||||
|
unsigned short unused1 : 1;
|
||||||
|
unsigned short unused2 : 1;
|
||||||
|
unsigned short unused3 : 1;
|
||||||
|
unsigned short unused4 : 1;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
struct SpuSubSimplexClosestResult
|
||||||
|
{
|
||||||
|
btVector3 m_closestPointOnSimplex;
|
||||||
|
//MASK for m_usedVertices
|
||||||
|
//stores the simplex vertex-usage, using the MASK,
|
||||||
|
// if m_usedVertices & MASK then the related vertex is used
|
||||||
|
SpuUsageBitfield m_usedVertices;
|
||||||
|
float m_barycentricCoords[4];
|
||||||
|
bool m_degenerate;
|
||||||
|
|
||||||
|
void reset()
|
||||||
|
{
|
||||||
|
m_degenerate = false;
|
||||||
|
setBarycentricCoordinates();
|
||||||
|
m_usedVertices.reset();
|
||||||
|
}
|
||||||
|
bool isValid()
|
||||||
|
{
|
||||||
|
bool valid = (m_barycentricCoords[0] >= float(0.)) &&
|
||||||
|
(m_barycentricCoords[1] >= float(0.)) &&
|
||||||
|
(m_barycentricCoords[2] >= float(0.)) &&
|
||||||
|
(m_barycentricCoords[3] >= float(0.));
|
||||||
|
|
||||||
|
|
||||||
|
return valid;
|
||||||
|
}
|
||||||
|
void setBarycentricCoordinates(float a=float(0.),float b=float(0.),float c=float(0.),float d=float(0.))
|
||||||
|
{
|
||||||
|
m_barycentricCoords[0] = a;
|
||||||
|
m_barycentricCoords[1] = b;
|
||||||
|
m_barycentricCoords[2] = c;
|
||||||
|
m_barycentricCoords[3] = d;
|
||||||
|
}
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
/// SpuVoronoiSimplexSolver is an implementation of the closest point distance algorithm from a 1-4 points simplex to the origin.
|
||||||
|
/// Can be used with GJK, as an alternative to Johnson distance algorithm.
|
||||||
|
class SpuVoronoiSimplexSolver
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
|
||||||
|
int m_numVertices;
|
||||||
|
|
||||||
|
btVector3 m_simplexVectorW[VORONOI_SIMPLEX_MAX_VERTS];
|
||||||
|
btVector3 m_simplexPointsP[VORONOI_SIMPLEX_MAX_VERTS];
|
||||||
|
btVector3 m_simplexPointsQ[VORONOI_SIMPLEX_MAX_VERTS];
|
||||||
|
|
||||||
|
int m_VertexIndexA[VORONOI_SIMPLEX_MAX_VERTS];
|
||||||
|
int m_VertexIndexB[VORONOI_SIMPLEX_MAX_VERTS];
|
||||||
|
|
||||||
|
btVector3 m_cachedP1;
|
||||||
|
btVector3 m_cachedP2;
|
||||||
|
btVector3 m_cachedV;
|
||||||
|
btVector3 m_lastW;
|
||||||
|
bool m_cachedValidClosest;
|
||||||
|
|
||||||
|
SpuSubSimplexClosestResult m_cachedBC;
|
||||||
|
|
||||||
|
bool m_needsUpdate;
|
||||||
|
|
||||||
|
void removeVertex(int index);
|
||||||
|
void reduceVertices (const SpuUsageBitfield& usedVerts);
|
||||||
|
bool updateClosestVectorAndPoints();
|
||||||
|
|
||||||
|
bool closestPtPointTetrahedron(const btVector3& p, const btVector3& a, const btVector3& b, const btVector3& c, const btVector3& d, SpuSubSimplexClosestResult& finalResult);
|
||||||
|
int pointOutsideOfPlane(const btVector3& p, const btVector3& a, const btVector3& b, const btVector3& c, const btVector3& d);
|
||||||
|
bool closestPtPointTriangle(const btVector3& p, const btVector3& a, const btVector3& b, const btVector3& c,SpuSubSimplexClosestResult& result);
|
||||||
|
|
||||||
|
int RemoveDegenerateIndices (const int *inArray, int numIndices, int *outArray) const;
|
||||||
|
|
||||||
|
public:
|
||||||
|
|
||||||
|
void reset();
|
||||||
|
|
||||||
|
void addVertex(const btVector3& w, const btPoint3& p, const btPoint3& q);
|
||||||
|
|
||||||
|
|
||||||
|
bool closest(btVector3& v);
|
||||||
|
|
||||||
|
float maxVertex();
|
||||||
|
|
||||||
|
bool fullSimplex() const
|
||||||
|
{
|
||||||
|
return (m_numVertices == 4);
|
||||||
|
}
|
||||||
|
|
||||||
|
int getSimplex(btVector3 *pBuf, btVector3 *qBuf, btVector3 *yBuf) const;
|
||||||
|
|
||||||
|
bool inSimplex(const btVector3& w);
|
||||||
|
|
||||||
|
void backup_closest(btVector3& v) ;
|
||||||
|
|
||||||
|
bool emptySimplex() const ;
|
||||||
|
|
||||||
|
void compute_points(btVector3& p1, btVector3& p2) ;
|
||||||
|
|
||||||
|
int numVertices() const
|
||||||
|
{
|
||||||
|
return m_numVertices;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#endif //SpuVoronoiSimplexSolver
|
||||||
@@ -0,0 +1,649 @@
|
|||||||
|
|
||||||
|
/*
|
||||||
|
Bullet Continuous Collision Detection and Physics Library
|
||||||
|
Copyright (c) 2003-2006 Erwin Coumans http://continuousphysics.com/Bullet/
|
||||||
|
|
||||||
|
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.
|
||||||
|
|
||||||
|
Elsevier CDROM license agreements grants nonexclusive license to use the software
|
||||||
|
for any purpose, commercial or non-commercial as long as the following credit is included
|
||||||
|
identifying the original source of the software:
|
||||||
|
|
||||||
|
Parts of the source are "from the book Real-Time Collision Detection by
|
||||||
|
Christer Ericson, published by Morgan Kaufmann Publishers,
|
||||||
|
(c) 2005 Elsevier Inc."
|
||||||
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
// Needed to be able to DMA.
|
||||||
|
#ifdef WIN32
|
||||||
|
#include "SpuFakeDma.h"
|
||||||
|
#else
|
||||||
|
#include "SPU_Common/SpuDefines.h"
|
||||||
|
#include <cell/spurs/common.h>
|
||||||
|
#include <cell/dma.h>
|
||||||
|
#endif //WIN32
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#include "SpuVoronoiSimplexSolver.h"
|
||||||
|
#include "LinearMath/btScalar.h"
|
||||||
|
|
||||||
|
#include <assert.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
|
||||||
|
|
||||||
|
#define VERTA 0
|
||||||
|
#define VERTB 1
|
||||||
|
#define VERTC 2
|
||||||
|
#define VERTD 3
|
||||||
|
|
||||||
|
#define CATCH_DEGENERATE_TETRAHEDRON 1
|
||||||
|
void SpuVoronoiSimplexSolver::removeVertex(int index)
|
||||||
|
{
|
||||||
|
assert(m_numVertices>0);
|
||||||
|
m_numVertices--;
|
||||||
|
m_simplexVectorW[index] = m_simplexVectorW[m_numVertices];
|
||||||
|
m_simplexPointsP[index] = m_simplexPointsP[m_numVertices];
|
||||||
|
m_simplexPointsQ[index] = m_simplexPointsQ[m_numVertices];
|
||||||
|
// m_VertexIndexA[index] = m_VertexIndexA[m_numVertices];
|
||||||
|
// m_VertexIndexB[index] = m_VertexIndexB[m_numVertices];
|
||||||
|
}
|
||||||
|
|
||||||
|
void SpuVoronoiSimplexSolver::reduceVertices (const SpuUsageBitfield& usedVerts)
|
||||||
|
{
|
||||||
|
if ((numVertices() >= 4) && (!usedVerts.usedVertexD))
|
||||||
|
removeVertex(3);
|
||||||
|
|
||||||
|
if ((numVertices() >= 3) && (!usedVerts.usedVertexC))
|
||||||
|
removeVertex(2);
|
||||||
|
|
||||||
|
if ((numVertices() >= 2) && (!usedVerts.usedVertexB))
|
||||||
|
removeVertex(1);
|
||||||
|
|
||||||
|
if ((numVertices() >= 1) && (!usedVerts.usedVertexA))
|
||||||
|
removeVertex(0);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
//clear the simplex, remove all the vertices
|
||||||
|
void SpuVoronoiSimplexSolver::reset()
|
||||||
|
{
|
||||||
|
m_cachedValidClosest = false;
|
||||||
|
m_numVertices = 0;
|
||||||
|
m_needsUpdate = true;
|
||||||
|
m_lastW = Vectormath::Aos::Vector3(float(1e30),float(1e30),float(1e30));
|
||||||
|
m_cachedBC.reset();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
//add a vertex
|
||||||
|
void SpuVoronoiSimplexSolver::addVertex(const Vectormath::Aos::Vector3& w, const Vectormath::Aos::Point3& p, const Vectormath::Aos::Point3& q)//, int vertexIndexA, int vertexIndexB)
|
||||||
|
{
|
||||||
|
m_lastW = w;
|
||||||
|
m_needsUpdate = true;
|
||||||
|
|
||||||
|
m_simplexVectorW[m_numVertices] = w;
|
||||||
|
m_simplexPointsP[m_numVertices] = Vectormath::Aos::Vector3(p);
|
||||||
|
m_simplexPointsQ[m_numVertices] = Vectormath::Aos::Vector3(q);
|
||||||
|
|
||||||
|
//m_VertexIndexA[m_numVertices] = vertexIndexA;
|
||||||
|
//m_VertexIndexB[m_numVertices] = vertexIndexB;
|
||||||
|
|
||||||
|
m_numVertices++;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool SpuVoronoiSimplexSolver::updateClosestVectorAndPoints()
|
||||||
|
{
|
||||||
|
|
||||||
|
if (m_needsUpdate)
|
||||||
|
{
|
||||||
|
m_cachedBC.reset();
|
||||||
|
|
||||||
|
m_needsUpdate = false;
|
||||||
|
|
||||||
|
switch (numVertices())
|
||||||
|
{
|
||||||
|
case 0:
|
||||||
|
m_cachedValidClosest = false;
|
||||||
|
break;
|
||||||
|
case 1:
|
||||||
|
{
|
||||||
|
m_cachedP1 = m_simplexPointsP[0];
|
||||||
|
m_cachedP2 = m_simplexPointsQ[0];
|
||||||
|
m_cachedV = m_cachedP1-m_cachedP2; //== m_simplexVectorW[0]
|
||||||
|
m_cachedBC.reset();
|
||||||
|
m_cachedBC.setBarycentricCoordinates(float(1.),float(0.),float(0.),float(0.));
|
||||||
|
m_cachedValidClosest = m_cachedBC.isValid();
|
||||||
|
break;
|
||||||
|
};
|
||||||
|
case 2:
|
||||||
|
{
|
||||||
|
//closest point origin from line segment
|
||||||
|
const Vectormath::Aos::Vector3& from = m_simplexVectorW[0];
|
||||||
|
const Vectormath::Aos::Vector3& to = m_simplexVectorW[1];
|
||||||
|
Vectormath::Aos::Vector3 nearest;
|
||||||
|
|
||||||
|
Vectormath::Aos::Vector3 p (float(0.),float(0.),float(0.));
|
||||||
|
Vectormath::Aos::Vector3 diff = p - from;
|
||||||
|
Vectormath::Aos::Vector3 v = to - from;
|
||||||
|
float t = dot(v, diff);
|
||||||
|
|
||||||
|
if (t > 0) {
|
||||||
|
float dotVV = dot(v, v);
|
||||||
|
if (t < dotVV) {
|
||||||
|
t /= dotVV;
|
||||||
|
diff -= t*v;
|
||||||
|
m_cachedBC.m_usedVertices.usedVertexA = true;
|
||||||
|
m_cachedBC.m_usedVertices.usedVertexB = true;
|
||||||
|
} else {
|
||||||
|
t = 1;
|
||||||
|
diff -= v;
|
||||||
|
//reduce to 1 point
|
||||||
|
m_cachedBC.m_usedVertices.usedVertexB = true;
|
||||||
|
}
|
||||||
|
} else
|
||||||
|
{
|
||||||
|
t = 0;
|
||||||
|
//reduce to 1 point
|
||||||
|
m_cachedBC.m_usedVertices.usedVertexA = true;
|
||||||
|
}
|
||||||
|
m_cachedBC.setBarycentricCoordinates(1-t,t);
|
||||||
|
nearest = from + t*v;
|
||||||
|
|
||||||
|
m_cachedP1 = m_simplexPointsP[0] + t * (m_simplexPointsP[1] - m_simplexPointsP[0]);
|
||||||
|
m_cachedP2 = m_simplexPointsQ[0] + t * (m_simplexPointsQ[1] - m_simplexPointsQ[0]);
|
||||||
|
m_cachedV = m_cachedP1 - m_cachedP2;
|
||||||
|
|
||||||
|
reduceVertices(m_cachedBC.m_usedVertices);
|
||||||
|
|
||||||
|
m_cachedValidClosest = m_cachedBC.isValid();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case 3:
|
||||||
|
{
|
||||||
|
//closest point origin from triangle
|
||||||
|
Vectormath::Aos::Vector3 p (float(0.),float(0.),float(0.));
|
||||||
|
|
||||||
|
const Vectormath::Aos::Vector3& a = m_simplexVectorW[0];
|
||||||
|
const Vectormath::Aos::Vector3& b = m_simplexVectorW[1];
|
||||||
|
const Vectormath::Aos::Vector3& c = m_simplexVectorW[2];
|
||||||
|
|
||||||
|
closestPtPointTriangle(p,a,b,c,m_cachedBC);
|
||||||
|
m_cachedP1 = m_simplexPointsP[0] * m_cachedBC.m_barycentricCoords[0] +
|
||||||
|
m_simplexPointsP[1] * m_cachedBC.m_barycentricCoords[1] +
|
||||||
|
m_simplexPointsP[2] * m_cachedBC.m_barycentricCoords[2];
|
||||||
|
|
||||||
|
m_cachedP2 = m_simplexPointsQ[0] * m_cachedBC.m_barycentricCoords[0] +
|
||||||
|
m_simplexPointsQ[1] * m_cachedBC.m_barycentricCoords[1] +
|
||||||
|
m_simplexPointsQ[2] * m_cachedBC.m_barycentricCoords[2];
|
||||||
|
|
||||||
|
m_cachedV = m_cachedP1-m_cachedP2;
|
||||||
|
|
||||||
|
reduceVertices (m_cachedBC.m_usedVertices);
|
||||||
|
m_cachedValidClosest = m_cachedBC.isValid();
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case 4:
|
||||||
|
{
|
||||||
|
|
||||||
|
|
||||||
|
Vectormath::Aos::Vector3 p (float(0.),float(0.),float(0.));
|
||||||
|
|
||||||
|
const Vectormath::Aos::Vector3& a = m_simplexVectorW[0];
|
||||||
|
const Vectormath::Aos::Vector3& b = m_simplexVectorW[1];
|
||||||
|
const Vectormath::Aos::Vector3& c = m_simplexVectorW[2];
|
||||||
|
const Vectormath::Aos::Vector3& d = m_simplexVectorW[3];
|
||||||
|
|
||||||
|
bool hasSeperation = closestPtPointTetrahedron(p,a,b,c,d,m_cachedBC);
|
||||||
|
|
||||||
|
if (hasSeperation)
|
||||||
|
{
|
||||||
|
|
||||||
|
m_cachedP1 = m_simplexPointsP[0] * m_cachedBC.m_barycentricCoords[0] +
|
||||||
|
m_simplexPointsP[1] * m_cachedBC.m_barycentricCoords[1] +
|
||||||
|
m_simplexPointsP[2] * m_cachedBC.m_barycentricCoords[2] +
|
||||||
|
m_simplexPointsP[3] * m_cachedBC.m_barycentricCoords[3];
|
||||||
|
|
||||||
|
m_cachedP2 = m_simplexPointsQ[0] * m_cachedBC.m_barycentricCoords[0] +
|
||||||
|
m_simplexPointsQ[1] * m_cachedBC.m_barycentricCoords[1] +
|
||||||
|
m_simplexPointsQ[2] * m_cachedBC.m_barycentricCoords[2] +
|
||||||
|
m_simplexPointsQ[3] * m_cachedBC.m_barycentricCoords[3];
|
||||||
|
|
||||||
|
m_cachedV = m_cachedP1-m_cachedP2;
|
||||||
|
reduceVertices (m_cachedBC.m_usedVertices);
|
||||||
|
} else
|
||||||
|
{
|
||||||
|
// printf("sub distance got penetration\n");
|
||||||
|
|
||||||
|
if (m_cachedBC.m_degenerate)
|
||||||
|
{
|
||||||
|
m_cachedValidClosest = false;
|
||||||
|
} else
|
||||||
|
{
|
||||||
|
m_cachedValidClosest = true;
|
||||||
|
//degenerate case == false, penetration = true + zero
|
||||||
|
m_cachedV = Vectormath::Aos::Vector3(float(0.),float(0.),float(0.));
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
m_cachedValidClosest = m_cachedBC.isValid();
|
||||||
|
|
||||||
|
//closest point origin from tetrahedron
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
{
|
||||||
|
m_cachedValidClosest = false;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
return m_cachedValidClosest;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
//return/calculate the closest vertex
|
||||||
|
bool SpuVoronoiSimplexSolver::closest(Vectormath::Aos::Vector3& v)
|
||||||
|
{
|
||||||
|
bool succes = updateClosestVectorAndPoints();
|
||||||
|
v = m_cachedV;
|
||||||
|
return succes;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
float SpuVoronoiSimplexSolver::maxVertex()
|
||||||
|
{
|
||||||
|
int i, numverts = numVertices();
|
||||||
|
float maxV = float(0.);
|
||||||
|
for (i=0;i<numverts;i++)
|
||||||
|
{
|
||||||
|
float curLen2 = lengthSqr(m_simplexVectorW[i]);
|
||||||
|
if (maxV < curLen2)
|
||||||
|
maxV = curLen2;
|
||||||
|
}
|
||||||
|
return maxV;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
//return the current simplex
|
||||||
|
int SpuVoronoiSimplexSolver::getSimplex(Vectormath::Aos::Vector3 *pBuf, Vectormath::Aos::Vector3 *qBuf, Vectormath::Aos::Vector3 *yBuf) const
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
for (i=0;i<numVertices();i++)
|
||||||
|
{
|
||||||
|
yBuf[i] = m_simplexVectorW[i];
|
||||||
|
pBuf[i] = m_simplexPointsP[i];
|
||||||
|
qBuf[i] = m_simplexPointsQ[i];
|
||||||
|
}
|
||||||
|
return numVertices();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
bool SpuVoronoiSimplexSolver::inSimplex(const Vectormath::Aos::Vector3& w)
|
||||||
|
{
|
||||||
|
bool found = false;
|
||||||
|
int i, numverts = numVertices();
|
||||||
|
//float maxV = float(0.);
|
||||||
|
|
||||||
|
//w is in the current (reduced) simplex
|
||||||
|
for (i=0;i<numverts;i++)
|
||||||
|
{
|
||||||
|
// TODO: find a better way to determine equality
|
||||||
|
if (m_simplexVectorW[i].getX() == w.getX() &&
|
||||||
|
m_simplexVectorW[i].getY() == w.getY() &&
|
||||||
|
m_simplexVectorW[i].getZ() == w.getZ())
|
||||||
|
found = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
//check in case lastW is already removed
|
||||||
|
// TODO: find a better way to determine equality
|
||||||
|
if (w.getX() == m_lastW.getX() &&
|
||||||
|
w.getY() == m_lastW.getY() &&
|
||||||
|
w.getZ() == m_lastW.getZ())
|
||||||
|
return true;
|
||||||
|
|
||||||
|
return found;
|
||||||
|
}
|
||||||
|
|
||||||
|
void SpuVoronoiSimplexSolver::backup_closest(Vectormath::Aos::Vector3& v)
|
||||||
|
{
|
||||||
|
v = m_cachedV;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
bool SpuVoronoiSimplexSolver::emptySimplex() const
|
||||||
|
{
|
||||||
|
return (numVertices() == 0);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void SpuVoronoiSimplexSolver::compute_points(Vectormath::Aos::Point3& p1, Vectormath::Aos::Point3& p2)
|
||||||
|
{
|
||||||
|
updateClosestVectorAndPoints();
|
||||||
|
p1 = Vectormath::Aos::Point3(m_cachedP1);
|
||||||
|
p2 = Vectormath::Aos::Point3(m_cachedP2);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
bool SpuVoronoiSimplexSolver::closestPtPointTriangle(const Vectormath::Aos::Vector3& p, const Vectormath::Aos::Vector3& a, const Vectormath::Aos::Vector3& b, const Vectormath::Aos::Vector3& c,SpuSubSimplexClosestResult& result)
|
||||||
|
{
|
||||||
|
result.m_usedVertices.reset();
|
||||||
|
|
||||||
|
// Check if P in vertex region outside A
|
||||||
|
Vectormath::Aos::Vector3 ab = b - a;
|
||||||
|
Vectormath::Aos::Vector3 ac = c - a;
|
||||||
|
Vectormath::Aos::Vector3 ap = p - a;
|
||||||
|
float d1 = dot(ab,ap);
|
||||||
|
float d2 = dot(ac,ap);
|
||||||
|
if (d1 <= float(0.0) && d2 <= float(0.0))
|
||||||
|
{
|
||||||
|
result.m_closestPointOnSimplex = Vectormath::Aos::Point3(a);
|
||||||
|
result.m_usedVertices.usedVertexA = true;
|
||||||
|
result.setBarycentricCoordinates(1,0,0);
|
||||||
|
return true;// a; // barycentric coordinates (1,0,0)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if P in vertex region outside B
|
||||||
|
Vectormath::Aos::Vector3 bp = p - b;
|
||||||
|
float d3 = dot(ab,bp);
|
||||||
|
float d4 = dot(ac,bp);
|
||||||
|
if (d3 >= float(0.0) && d4 <= d3)
|
||||||
|
{
|
||||||
|
result.m_closestPointOnSimplex = Vectormath::Aos::Point3(b);
|
||||||
|
result.m_usedVertices.usedVertexB = true;
|
||||||
|
result.setBarycentricCoordinates(0,1,0);
|
||||||
|
|
||||||
|
return true; // b; // barycentric coordinates (0,1,0)
|
||||||
|
}
|
||||||
|
// Check if P in edge region of AB, if so return projection of P onto AB
|
||||||
|
float vc = d1*d4 - d3*d2;
|
||||||
|
if (vc <= float(0.0) && d1 >= float(0.0) && d3 <= float(0.0)) {
|
||||||
|
float v = d1 / (d1 - d3);
|
||||||
|
result.m_closestPointOnSimplex = Vectormath::Aos::Point3(a + v * ab);
|
||||||
|
result.m_usedVertices.usedVertexA = true;
|
||||||
|
result.m_usedVertices.usedVertexB = true;
|
||||||
|
result.setBarycentricCoordinates(1-v,v,0);
|
||||||
|
return true;
|
||||||
|
//return a + v * ab; // barycentric coordinates (1-v,v,0)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if P in vertex region outside C
|
||||||
|
Vectormath::Aos::Vector3 cp = p - c;
|
||||||
|
float d5 = dot(ab,cp);
|
||||||
|
float d6 = dot(ac,cp);
|
||||||
|
if (d6 >= float(0.0) && d5 <= d6)
|
||||||
|
{
|
||||||
|
result.m_closestPointOnSimplex = Vectormath::Aos::Point3(c);
|
||||||
|
result.m_usedVertices.usedVertexC = true;
|
||||||
|
result.setBarycentricCoordinates(0,0,1);
|
||||||
|
return true;//c; // barycentric coordinates (0,0,1)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if P in edge region of AC, if so return projection of P onto AC
|
||||||
|
float vb = d5*d2 - d1*d6;
|
||||||
|
if (vb <= float(0.0) && d2 >= float(0.0) && d6 <= float(0.0)) {
|
||||||
|
float w = d2 / (d2 - d6);
|
||||||
|
result.m_closestPointOnSimplex = Vectormath::Aos::Point3(a + w * ac);
|
||||||
|
result.m_usedVertices.usedVertexA = true;
|
||||||
|
result.m_usedVertices.usedVertexC = true;
|
||||||
|
result.setBarycentricCoordinates(1-w,0,w);
|
||||||
|
return true;
|
||||||
|
//return a + w * ac; // barycentric coordinates (1-w,0,w)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if P in edge region of BC, if so return projection of P onto BC
|
||||||
|
float va = d3*d6 - d5*d4;
|
||||||
|
if (va <= float(0.0) && (d4 - d3) >= float(0.0) && (d5 - d6) >= float(0.0)) {
|
||||||
|
float w = (d4 - d3) / ((d4 - d3) + (d5 - d6));
|
||||||
|
|
||||||
|
result.m_closestPointOnSimplex = Vectormath::Aos::Point3(b + w * (c - b));
|
||||||
|
result.m_usedVertices.usedVertexB = true;
|
||||||
|
result.m_usedVertices.usedVertexC = true;
|
||||||
|
result.setBarycentricCoordinates(0,1-w,w);
|
||||||
|
return true;
|
||||||
|
// return b + w * (c - b); // barycentric coordinates (0,1-w,w)
|
||||||
|
}
|
||||||
|
|
||||||
|
// P inside face region. Compute Q through its barycentric coordinates (u,v,w)
|
||||||
|
float denom = float(1.0) / (va + vb + vc);
|
||||||
|
float v = vb * denom;
|
||||||
|
float w = vc * denom;
|
||||||
|
|
||||||
|
result.m_closestPointOnSimplex = Vectormath::Aos::Point3(a + ab * v + ac * w);
|
||||||
|
result.m_usedVertices.usedVertexA = true;
|
||||||
|
result.m_usedVertices.usedVertexB = true;
|
||||||
|
result.m_usedVertices.usedVertexC = true;
|
||||||
|
result.setBarycentricCoordinates(1-v-w,v,w);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
// return a + ab * v + ac * w; // = u*a + v*b + w*c, u = va * denom = float(1.0) - v - w
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// This is specifically just removing duplicate indices.
|
||||||
|
int SpuVoronoiSimplexSolver::RemoveDegenerateIndices (const int* inArray, int numIndices, int* outArray) const
|
||||||
|
{
|
||||||
|
int outIndex = 0;
|
||||||
|
for (int firstIndex=0; firstIndex<numIndices; firstIndex++)
|
||||||
|
{
|
||||||
|
bool duplicate = false;
|
||||||
|
for (int secondIndex=0; secondIndex<firstIndex; secondIndex++)
|
||||||
|
{
|
||||||
|
if (inArray[secondIndex]==inArray[firstIndex])
|
||||||
|
{
|
||||||
|
duplicate = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!duplicate)
|
||||||
|
{
|
||||||
|
outArray[outIndex++] = inArray[firstIndex];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return outIndex;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/// Test if point p and d lie on opposite sides of plane through abc
|
||||||
|
int SpuVoronoiSimplexSolver::pointOutsideOfPlane(const Vectormath::Aos::Vector3& p, const Vectormath::Aos::Vector3& a, const Vectormath::Aos::Vector3& b, const Vectormath::Aos::Vector3& c, const Vectormath::Aos::Vector3& d)
|
||||||
|
{
|
||||||
|
Vectormath::Aos::Vector3 normal = cross(b-a,c-a);
|
||||||
|
|
||||||
|
float signp = dot(p - a, normal); // [AP AB AC]
|
||||||
|
float signd = dot(d - a, normal); // [AD AB AC]
|
||||||
|
|
||||||
|
#ifdef CATCH_DEGENERATE_TETRAHEDRON
|
||||||
|
if (signd * signd < (float(1e-4) * float(1e-4)))
|
||||||
|
{
|
||||||
|
// printf("affine dependent/degenerate\n");//
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
// Points on opposite sides if expression signs are opposite
|
||||||
|
return signp * signd < float(0.);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
bool SpuVoronoiSimplexSolver::closestPtPointTetrahedron(const Vectormath::Aos::Vector3& p, const Vectormath::Aos::Vector3& a, const Vectormath::Aos::Vector3& b, const Vectormath::Aos::Vector3& c, const Vectormath::Aos::Vector3& d, SpuSubSimplexClosestResult& finalResult)
|
||||||
|
{
|
||||||
|
SpuSubSimplexClosestResult tempResult;
|
||||||
|
|
||||||
|
// Start out assuming point inside all halfspaces, so closest to itself
|
||||||
|
finalResult.m_closestPointOnSimplex = Vectormath::Aos::Point3(p);
|
||||||
|
finalResult.m_usedVertices.reset();
|
||||||
|
finalResult.m_usedVertices.usedVertexA = true;
|
||||||
|
finalResult.m_usedVertices.usedVertexB = true;
|
||||||
|
finalResult.m_usedVertices.usedVertexC = true;
|
||||||
|
finalResult.m_usedVertices.usedVertexD = true;
|
||||||
|
|
||||||
|
// Check only the tetrahedron faces that are closest to p. We do that by checking each face (which itself is a triangle) to see if the excluded
|
||||||
|
// vertex (the one vertex that isn't part of that face) is on the other side of that face from the point p.
|
||||||
|
int pointOutsideABC = pointOutsideOfPlane(p, a, b, c, d);
|
||||||
|
int pointOutsideACD = pointOutsideOfPlane(p, a, c, d, b);
|
||||||
|
int pointOutsideADB = pointOutsideOfPlane(p, a, d, b, c);
|
||||||
|
int pointOutsideBDC = pointOutsideOfPlane(p, b, d, c, a);
|
||||||
|
|
||||||
|
if (pointOutsideABC < 0 || pointOutsideACD < 0 || pointOutsideADB < 0 || pointOutsideBDC < 0)
|
||||||
|
{
|
||||||
|
finalResult.m_degenerate = true;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!pointOutsideABC && !pointOutsideACD && !pointOutsideADB && !pointOutsideBDC)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
float bestSqDist = 1e30f;//FLT_MAX;
|
||||||
|
// If point outside face abc then compute closest point on abc
|
||||||
|
if (pointOutsideABC)
|
||||||
|
{
|
||||||
|
closestPtPointTriangle(p, a, b, c,tempResult);
|
||||||
|
Vectormath::Aos::Vector3 q = Vectormath::Aos::Vector3(tempResult.m_closestPointOnSimplex);
|
||||||
|
|
||||||
|
float sqDist = dot(q - p, q - p);
|
||||||
|
// Update best closest point if (squared) distance is less than current best
|
||||||
|
//if (sqDist < bestSqDist)
|
||||||
|
btAssert(sqDist < bestSqDist); // This has to be true; we haven't actually tested any other combinations.
|
||||||
|
{
|
||||||
|
bestSqDist = sqDist;
|
||||||
|
finalResult.m_closestPointOnSimplex = Vectormath::Aos::Point3(q);
|
||||||
|
//convert result bitmask!
|
||||||
|
finalResult.m_usedVertices.reset();
|
||||||
|
finalResult.m_usedVertices.usedVertexA = tempResult.m_usedVertices.usedVertexA;
|
||||||
|
finalResult.m_usedVertices.usedVertexB = tempResult.m_usedVertices.usedVertexB;
|
||||||
|
finalResult.m_usedVertices.usedVertexC = tempResult.m_usedVertices.usedVertexC;
|
||||||
|
finalResult.setBarycentricCoordinates(
|
||||||
|
tempResult.m_barycentricCoords[VERTA],
|
||||||
|
tempResult.m_barycentricCoords[VERTB],
|
||||||
|
tempResult.m_barycentricCoords[VERTC],
|
||||||
|
0
|
||||||
|
);
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Repeat test for face acd
|
||||||
|
if (pointOutsideACD)
|
||||||
|
{
|
||||||
|
closestPtPointTriangle(p, a, c, d,tempResult);
|
||||||
|
Vectormath::Aos::Vector3 q = Vectormath::Aos::Vector3(tempResult.m_closestPointOnSimplex);
|
||||||
|
//convert result bitmask!
|
||||||
|
|
||||||
|
float sqDist = dot(q - p, q - p);
|
||||||
|
if (sqDist < bestSqDist)
|
||||||
|
{
|
||||||
|
bestSqDist = sqDist;
|
||||||
|
finalResult.m_closestPointOnSimplex = Vectormath::Aos::Point3(q);
|
||||||
|
finalResult.m_usedVertices.reset();
|
||||||
|
finalResult.m_usedVertices.usedVertexA = tempResult.m_usedVertices.usedVertexA;
|
||||||
|
finalResult.m_usedVertices.usedVertexC = tempResult.m_usedVertices.usedVertexB;
|
||||||
|
finalResult.m_usedVertices.usedVertexD = tempResult.m_usedVertices.usedVertexC;
|
||||||
|
finalResult.setBarycentricCoordinates(
|
||||||
|
tempResult.m_barycentricCoords[VERTA],
|
||||||
|
0,
|
||||||
|
tempResult.m_barycentricCoords[VERTB],
|
||||||
|
tempResult.m_barycentricCoords[VERTC]
|
||||||
|
);
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Repeat test for face adb
|
||||||
|
|
||||||
|
|
||||||
|
if (pointOutsideADB)
|
||||||
|
{
|
||||||
|
closestPtPointTriangle(p, a, d, b,tempResult);
|
||||||
|
Vectormath::Aos::Vector3 q = Vectormath::Aos::Vector3(tempResult.m_closestPointOnSimplex);
|
||||||
|
//convert result bitmask!
|
||||||
|
|
||||||
|
float sqDist = dot(q - p, q - p);
|
||||||
|
if (sqDist < bestSqDist)
|
||||||
|
{
|
||||||
|
bestSqDist = sqDist;
|
||||||
|
finalResult.m_closestPointOnSimplex = Vectormath::Aos::Point3(q);
|
||||||
|
finalResult.m_usedVertices.reset();
|
||||||
|
finalResult.m_usedVertices.usedVertexA = tempResult.m_usedVertices.usedVertexA;
|
||||||
|
finalResult.m_usedVertices.usedVertexD = tempResult.m_usedVertices.usedVertexB;
|
||||||
|
finalResult.m_usedVertices.usedVertexB = tempResult.m_usedVertices.usedVertexC;
|
||||||
|
finalResult.setBarycentricCoordinates(
|
||||||
|
tempResult.m_barycentricCoords[VERTA],
|
||||||
|
tempResult.m_barycentricCoords[VERTC],
|
||||||
|
0,
|
||||||
|
tempResult.m_barycentricCoords[VERTB]
|
||||||
|
);
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Repeat test for face bdc
|
||||||
|
|
||||||
|
|
||||||
|
if (pointOutsideBDC)
|
||||||
|
{
|
||||||
|
closestPtPointTriangle(p, b, d, c,tempResult);
|
||||||
|
Vectormath::Aos::Vector3 q = Vectormath::Aos::Vector3(tempResult.m_closestPointOnSimplex);
|
||||||
|
//convert result bitmask!
|
||||||
|
float sqDist = dot(q - p, q - p);
|
||||||
|
if (sqDist < bestSqDist)
|
||||||
|
{
|
||||||
|
bestSqDist = sqDist;
|
||||||
|
finalResult.m_closestPointOnSimplex = Vectormath::Aos::Point3(q);
|
||||||
|
finalResult.m_usedVertices.reset();
|
||||||
|
finalResult.m_usedVertices.usedVertexB = tempResult.m_usedVertices.usedVertexA;
|
||||||
|
finalResult.m_usedVertices.usedVertexD = tempResult.m_usedVertices.usedVertexB;
|
||||||
|
finalResult.m_usedVertices.usedVertexC = tempResult.m_usedVertices.usedVertexC;
|
||||||
|
|
||||||
|
finalResult.setBarycentricCoordinates(
|
||||||
|
0,
|
||||||
|
tempResult.m_barycentricCoords[VERTA],
|
||||||
|
tempResult.m_barycentricCoords[VERTC],
|
||||||
|
tempResult.m_barycentricCoords[VERTB]
|
||||||
|
);
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//help! we ended up full !
|
||||||
|
|
||||||
|
if (finalResult.m_usedVertices.usedVertexA &&
|
||||||
|
finalResult.m_usedVertices.usedVertexB &&
|
||||||
|
finalResult.m_usedVertices.usedVertexC &&
|
||||||
|
finalResult.m_usedVertices.usedVertexD)
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
Empty placeholder for future Libspe2 SPU task
|
||||||
1
Extras/BulletMultiThreaded/SpuSampleTask/readme.txt
Normal file
1
Extras/BulletMultiThreaded/SpuSampleTask/readme.txt
Normal file
@@ -0,0 +1 @@
|
|||||||
|
Empty placeholder for future Libspe2 SPU task
|
||||||
199
Extras/BulletMultiThreaded/SpuSampleTaskProcess.cpp
Normal file
199
Extras/BulletMultiThreaded/SpuSampleTaskProcess.cpp
Normal file
@@ -0,0 +1,199 @@
|
|||||||
|
/*
|
||||||
|
Bullet Continuous Collision Detection and Physics Library
|
||||||
|
Copyright (c) 2003-2007 Erwin Coumans http://bulletphysics.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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
//#define __CELLOS_LV2__ 1
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#include "SpuLibspe2Support.h"
|
||||||
|
#include "Win32ThreadSupport.h"
|
||||||
|
|
||||||
|
//#include "SPUAssert.h"
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
|
||||||
|
#include "SpuSampleTaskProcess.h"
|
||||||
|
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
|
||||||
|
|
||||||
|
void SampleThreadFunc(void* userPtr,void* lsMemory)
|
||||||
|
{
|
||||||
|
//do nothing
|
||||||
|
printf("hello world\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
void* SamplelsMemoryFunc()
|
||||||
|
{
|
||||||
|
//don't create local store memory, just return 0
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//SpuLibspe2Support gSampleSPU(SPU_ELF_SAMPLE,SAMPLE_NUM_WORKUNIT_TASKS);
|
||||||
|
Win32ThreadSupport gSampleSPU(Win32ThreadSupport::Win32ThreadConstructionInfo("sample",
|
||||||
|
SampleThreadFunc,
|
||||||
|
SamplelsMemoryFunc,
|
||||||
|
SAMPLE_NUM_WORKUNIT_TASKS));
|
||||||
|
|
||||||
|
|
||||||
|
extern "C" {
|
||||||
|
extern char SPU_SAMPLE_ELF_SYMBOL[];
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
SpuSampleTaskDesc g_spuSampleTaskDesc[SAMPLE_NUM_WORKUNIT_TASKS];
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
SpuSampleTaskProcess::SpuSampleTaskProcess(Win32ThreadSupport::Win32ThreadConstructionInfo& threadConstructionInfo)
|
||||||
|
{
|
||||||
|
|
||||||
|
for (int i = 0; i < threadConstructionInfo.m_numThreads; i++)
|
||||||
|
{
|
||||||
|
m_taskBusy[i] = false;
|
||||||
|
}
|
||||||
|
m_numBusyTasks = 0;
|
||||||
|
m_currentTask = 0;
|
||||||
|
|
||||||
|
m_initialized = false;
|
||||||
|
|
||||||
|
gSampleSPU.startSPUs(threadConstructionInfo);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
SpuSampleTaskProcess::~SpuSampleTaskProcess()
|
||||||
|
{
|
||||||
|
|
||||||
|
gSampleSPU.stopSPUs();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
void SpuSampleTaskProcess::initialize()
|
||||||
|
{
|
||||||
|
#ifdef DEBUG_SPU_TASK_SCHEDULING
|
||||||
|
printf("SpuSampleTaskProcess::initialize()\n");
|
||||||
|
#endif //DEBUG_SPU_TASK_SCHEDULING
|
||||||
|
|
||||||
|
for (int i = 0; i < SAMPLE_NUM_WORKUNIT_TASKS; i++)
|
||||||
|
{
|
||||||
|
m_taskBusy[i] = false;
|
||||||
|
}
|
||||||
|
m_numBusyTasks = 0;
|
||||||
|
m_currentTask = 0;
|
||||||
|
m_initialized = true;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void SpuSampleTaskProcess::issueTask(void* sampleMainMemPtr,int sampleValue)
|
||||||
|
{
|
||||||
|
|
||||||
|
#ifdef DEBUG_SPU_TASK_SCHEDULING
|
||||||
|
printf("SpuSampleTaskProcess::issueTask (m_currentTask= %d\)n", m_currentTask);
|
||||||
|
#endif //DEBUG_SPU_TASK_SCHEDULING
|
||||||
|
|
||||||
|
m_taskBusy[m_currentTask] = true;
|
||||||
|
m_numBusyTasks++;
|
||||||
|
|
||||||
|
SpuSampleTaskDesc& taskDesc = g_spuSampleTaskDesc[m_currentTask];
|
||||||
|
{
|
||||||
|
// send task description in event message
|
||||||
|
// no error checking here...
|
||||||
|
// but, currently, event queue can be no larger than NUM_WORKUNIT_TASKS.
|
||||||
|
|
||||||
|
taskDesc.m_mainMemoryPtr = reinterpret_cast<uint64_t>(sampleMainMemPtr);
|
||||||
|
taskDesc.m_sampleValue = sampleValue;
|
||||||
|
|
||||||
|
//some bookkeeping to recognize finished tasks
|
||||||
|
taskDesc.m_taskId = m_currentTask;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
gSampleSPU.sendRequest(CMD_SAMPLE_TASK_COMMAND, (uint32_t) &taskDesc, m_currentTask);
|
||||||
|
|
||||||
|
// if all tasks busy, wait for spu event to clear the task.
|
||||||
|
|
||||||
|
if (m_numBusyTasks >= SAMPLE_NUM_WORKUNIT_TASKS)
|
||||||
|
{
|
||||||
|
unsigned int taskId;
|
||||||
|
unsigned int outputSize;
|
||||||
|
|
||||||
|
gSampleSPU.waitForResponse(&taskId, &outputSize);
|
||||||
|
|
||||||
|
//printf("PPU: after issue, received event: %u %d\n", taskId, outputSize);
|
||||||
|
|
||||||
|
postProcess(taskId, outputSize);
|
||||||
|
|
||||||
|
m_taskBusy[taskId] = false;
|
||||||
|
|
||||||
|
m_numBusyTasks--;
|
||||||
|
}
|
||||||
|
|
||||||
|
// find new task buffer
|
||||||
|
for (unsigned int i = 0; i < SAMPLE_NUM_WORKUNIT_TASKS; i++)
|
||||||
|
{
|
||||||
|
if (!m_taskBusy[i])
|
||||||
|
{
|
||||||
|
m_currentTask = i;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
///Optional PPU-size post processing for each task
|
||||||
|
void SpuSampleTaskProcess::postProcess(int taskId, int outputSize)
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void SpuSampleTaskProcess::flush()
|
||||||
|
{
|
||||||
|
#ifdef DEBUG_SPU_TASK_SCHEDULING
|
||||||
|
printf("\nSpuCollisionTaskProcess::flush()\n");
|
||||||
|
#endif //DEBUG_SPU_TASK_SCHEDULING
|
||||||
|
|
||||||
|
|
||||||
|
// all tasks are issued, wait for all tasks to be complete
|
||||||
|
while(m_numBusyTasks > 0)
|
||||||
|
{
|
||||||
|
// Consolidating SPU code
|
||||||
|
unsigned int taskId;
|
||||||
|
unsigned int outputSize;
|
||||||
|
|
||||||
|
{
|
||||||
|
|
||||||
|
gSampleSPU.waitForResponse(&taskId, &outputSize);
|
||||||
|
}
|
||||||
|
|
||||||
|
//printf("PPU: flushing, received event: %u %d\n", taskId, outputSize);
|
||||||
|
|
||||||
|
postProcess(taskId, outputSize);
|
||||||
|
|
||||||
|
m_taskBusy[taskId] = false;
|
||||||
|
|
||||||
|
m_numBusyTasks--;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
83
Extras/BulletMultiThreaded/SpuSampleTaskProcess.h
Normal file
83
Extras/BulletMultiThreaded/SpuSampleTaskProcess.h
Normal file
@@ -0,0 +1,83 @@
|
|||||||
|
/*
|
||||||
|
Bullet Continuous Collision Detection and Physics Library
|
||||||
|
Copyright (c) 2003-2007 Erwin Coumans http://bulletphysics.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 SPU_SAMPLE_TASK_PROCESS_H
|
||||||
|
#define SPU_SAMPLE_TASK_PROCESS_H
|
||||||
|
|
||||||
|
#include <assert.h>
|
||||||
|
|
||||||
|
#include <LinearMath/btScalar.h>
|
||||||
|
|
||||||
|
#include "PlatformDefinitions.h"
|
||||||
|
|
||||||
|
#include <stdlib.h>
|
||||||
|
|
||||||
|
///maximum outstanding tasks
|
||||||
|
#define SAMPLE_NUM_WORKUNIT_TASKS 4
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
///SpuSampleTaskDesc
|
||||||
|
struct SpuSampleTaskDesc
|
||||||
|
{
|
||||||
|
uint64_t m_mainMemoryPtr;
|
||||||
|
int m_sampleValue;
|
||||||
|
|
||||||
|
uint16_t m_taskId;
|
||||||
|
}
|
||||||
|
#ifdef __CELLOS_LV2__
|
||||||
|
__attribute__ ((aligned (16)))
|
||||||
|
#endif
|
||||||
|
;
|
||||||
|
|
||||||
|
//just add your commands here, try to keep them globally unique for debugging purposes
|
||||||
|
#define CMD_SAMPLE_TASK_COMMAND 10
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/// SpuSampleTaskProcess handles SPU processing of collision pairs.
|
||||||
|
/// When PPU issues a task, it will look for completed task buffers
|
||||||
|
/// PPU will do postprocessing, dependent on workunit output (not likely)
|
||||||
|
class SpuSampleTaskProcess
|
||||||
|
{
|
||||||
|
// track task buffers that are being used, and total busy tasks
|
||||||
|
bool m_taskBusy[SAMPLE_NUM_WORKUNIT_TASKS];
|
||||||
|
unsigned int m_numBusyTasks;
|
||||||
|
|
||||||
|
// the current task and the current entry to insert a new work unit
|
||||||
|
unsigned int m_currentTask;
|
||||||
|
|
||||||
|
bool m_initialized;
|
||||||
|
|
||||||
|
void postProcess(int taskId, int outputSize);
|
||||||
|
|
||||||
|
|
||||||
|
public:
|
||||||
|
SpuSampleTaskProcess(Win32ThreadSupport::Win32ThreadConstructionInfo& ci);
|
||||||
|
|
||||||
|
~SpuSampleTaskProcess();
|
||||||
|
|
||||||
|
///call initialize in the beginning of the frame, before addCollisionPairToTask
|
||||||
|
void initialize();
|
||||||
|
|
||||||
|
void issueTask(void* sampleMainMemPtr,int sampleValue);
|
||||||
|
|
||||||
|
///call flush to submit potential outstanding work to SPUs and wait for all involved SPUs to be finished
|
||||||
|
void flush();
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
#endif // SPU_SAMPLE_TASK_PROCESS_H
|
||||||
|
|
||||||
1
Extras/BulletMultiThreaded/SpuSolverTask/readme.txt
Normal file
1
Extras/BulletMultiThreaded/SpuSolverTask/readme.txt
Normal file
@@ -0,0 +1 @@
|
|||||||
|
Empty placeholder for future Libspe2 SPU task
|
||||||
226
Extras/BulletMultiThreaded/Win32ThreadSupport.cpp
Normal file
226
Extras/BulletMultiThreaded/Win32ThreadSupport.cpp
Normal file
@@ -0,0 +1,226 @@
|
|||||||
|
/*
|
||||||
|
Bullet Continuous Collision Detection and Physics Library
|
||||||
|
Copyright (c) 2003-2007 Erwin Coumans http://bulletphysics.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 "Win32ThreadSupport.h"
|
||||||
|
|
||||||
|
#ifdef USE_WIN32_THREADING
|
||||||
|
|
||||||
|
#include "SpuCollisionTaskProcess.h"
|
||||||
|
#include "SpuNarrowPhaseCollisionTask/SpuGatheringCollisionTask.h"
|
||||||
|
|
||||||
|
#include <Windows.h>
|
||||||
|
|
||||||
|
///Win32ThreadSupport helps to initialize/shutdown libspe2, start/stop SPU tasks and communication
|
||||||
|
///Setup and initialize SPU/CELL/Libspe2
|
||||||
|
Win32ThreadSupport::Win32ThreadSupport(Win32ThreadConstructionInfo& threadConstructionInfo)
|
||||||
|
{
|
||||||
|
startSPUs(threadConstructionInfo);
|
||||||
|
}
|
||||||
|
|
||||||
|
///cleanup/shutdown Libspe2
|
||||||
|
Win32ThreadSupport::~Win32ThreadSupport()
|
||||||
|
{
|
||||||
|
stopSPUs();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
|
||||||
|
DWORD WINAPI Thread_no_1( LPVOID lpParam )
|
||||||
|
{
|
||||||
|
|
||||||
|
btSpuStatus* status = (btSpuStatus*)lpParam;
|
||||||
|
|
||||||
|
|
||||||
|
while (1)
|
||||||
|
{
|
||||||
|
WaitForSingleObject(status->m_eventStartHandle,INFINITE);
|
||||||
|
btAssert(status->m_status);
|
||||||
|
|
||||||
|
void* userPtr = status->m_userPtr;
|
||||||
|
|
||||||
|
if (userPtr)
|
||||||
|
{
|
||||||
|
status->m_userThreadFunc(userPtr,status->m_lsMemory);
|
||||||
|
SetEvent(status->m_eventCompletetHandle);
|
||||||
|
} else
|
||||||
|
{
|
||||||
|
//exit Thread
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
///send messages to SPUs
|
||||||
|
void Win32ThreadSupport::sendRequest(uint32_t uiCommand, uint32_t uiArgument0, uint32_t taskId)
|
||||||
|
{
|
||||||
|
/// gMidphaseSPU.sendRequest(CMD_GATHER_AND_PROCESS_PAIRLIST, (uint32_t) &taskDesc);
|
||||||
|
|
||||||
|
///we should spawn an SPU task here, and in 'waitForResponse' it should wait for response of the (one of) the first tasks that finished
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
switch (uiCommand)
|
||||||
|
{
|
||||||
|
case CMD_GATHER_AND_PROCESS_PAIRLIST:
|
||||||
|
{
|
||||||
|
|
||||||
|
|
||||||
|
//#define SINGLE_THREADED 1
|
||||||
|
#ifdef SINGLE_THREADED
|
||||||
|
|
||||||
|
btSpuStatus& spuStatus = m_activeSpuStatus[0];
|
||||||
|
spuStatus.m_userPtr=(void*)uiArgument0;
|
||||||
|
spuStatus.m_userThreadFunc(spuStatus.m_userPtr,spuStatus.m_lsMemory);
|
||||||
|
HANDLE handle =0;
|
||||||
|
#else
|
||||||
|
|
||||||
|
|
||||||
|
btSpuStatus& spuStatus = m_activeSpuStatus[taskId];
|
||||||
|
btAssert(taskId>=0);
|
||||||
|
btAssert(taskId<m_activeSpuStatus.size());
|
||||||
|
|
||||||
|
spuStatus.m_commandId = uiCommand;
|
||||||
|
spuStatus.m_status = 1;
|
||||||
|
spuStatus.m_userPtr = (void*)uiArgument0;
|
||||||
|
|
||||||
|
///fire event to start new task
|
||||||
|
SetEvent(spuStatus.m_eventStartHandle);
|
||||||
|
|
||||||
|
#endif //CollisionTask_LocalStoreMemory
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
{
|
||||||
|
///not implemented
|
||||||
|
btAssert(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
///check for messages from SPUs
|
||||||
|
void Win32ThreadSupport::waitForResponse(unsigned int *puiArgument0, unsigned int *puiArgument1)
|
||||||
|
{
|
||||||
|
///We should wait for (one of) the first tasks to finish (or other SPU messages), and report its response
|
||||||
|
|
||||||
|
///A possible response can be 'yes, SPU handled it', or 'no, please do a PPU fallback'
|
||||||
|
|
||||||
|
|
||||||
|
btAssert(m_activeSpuStatus.size());
|
||||||
|
|
||||||
|
int last = -1;
|
||||||
|
|
||||||
|
//find an active spu/thread
|
||||||
|
for (int i=0;i<m_activeSpuStatus.size();i++)
|
||||||
|
{
|
||||||
|
if (m_activeSpuStatus[i].m_status)
|
||||||
|
{
|
||||||
|
last = i;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#ifndef SINGLE_THREADED
|
||||||
|
btSpuStatus& spuStatus = m_activeSpuStatus[last];
|
||||||
|
btAssert(spuStatus.m_threadHandle);
|
||||||
|
btAssert(spuStatus.m_eventCompletetHandle);
|
||||||
|
|
||||||
|
WaitForSingleObject(spuStatus.m_eventCompletetHandle, INFINITE);
|
||||||
|
spuStatus.m_status = 0;
|
||||||
|
|
||||||
|
///need to find an active spu
|
||||||
|
btAssert(last>=0);
|
||||||
|
|
||||||
|
#else
|
||||||
|
last=0;
|
||||||
|
btSpuStatus& spuStatus = m_activeSpuStatus[last];
|
||||||
|
#endif //SINGLE_THREADED
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
*puiArgument0 = spuStatus.m_taskId;
|
||||||
|
*puiArgument1 = spuStatus.m_status;
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
///start the spus group (can be called at the beginning of each frame, to make sure that the right SPU program is loaded)
|
||||||
|
void Win32ThreadSupport::startSPUs(Win32ThreadConstructionInfo& threadConstructionInfo)
|
||||||
|
{
|
||||||
|
|
||||||
|
m_activeSpuStatus.resize(threadConstructionInfo.m_numThreads);
|
||||||
|
|
||||||
|
for (int i=0;i<threadConstructionInfo.m_numThreads;i++)
|
||||||
|
{
|
||||||
|
printf("starting thread %d\n",i);
|
||||||
|
|
||||||
|
btSpuStatus& spuStatus = m_activeSpuStatus[i];
|
||||||
|
|
||||||
|
LPSECURITY_ATTRIBUTES lpThreadAttributes=NULL;
|
||||||
|
SIZE_T dwStackSize=threadConstructionInfo.m_threadStackSize;
|
||||||
|
LPTHREAD_START_ROUTINE lpStartAddress=&Thread_no_1;
|
||||||
|
LPVOID lpParameter=&spuStatus;
|
||||||
|
DWORD dwCreationFlags=0;
|
||||||
|
LPDWORD lpThreadId=0;
|
||||||
|
|
||||||
|
spuStatus.m_userPtr=0;
|
||||||
|
|
||||||
|
sprintf(spuStatus.m_eventStartHandleName,"eventStart%s%d",threadConstructionInfo.m_uniqueName,i);
|
||||||
|
spuStatus.m_eventStartHandle = CreateEvent(0,false,false,spuStatus.m_eventStartHandleName);
|
||||||
|
|
||||||
|
sprintf(spuStatus.m_eventCompletetHandleName,"eventComplete%s%d",threadConstructionInfo.m_uniqueName,i);
|
||||||
|
spuStatus.m_eventCompletetHandle = CreateEvent(0,false,false,spuStatus.m_eventCompletetHandleName);
|
||||||
|
|
||||||
|
|
||||||
|
HANDLE handle = CreateThread(lpThreadAttributes,dwStackSize,lpStartAddress,lpParameter, dwCreationFlags,lpThreadId);
|
||||||
|
SetThreadPriority(handle,THREAD_PRIORITY_TIME_CRITICAL);
|
||||||
|
|
||||||
|
spuStatus.m_taskId = i;
|
||||||
|
spuStatus.m_commandId = 0;
|
||||||
|
spuStatus.m_status = 0;
|
||||||
|
spuStatus.m_threadHandle = handle;
|
||||||
|
spuStatus.m_lsMemory = threadConstructionInfo.m_lsMemoryFunc();
|
||||||
|
spuStatus.m_userThreadFunc = threadConstructionInfo.m_userThreadFunc;
|
||||||
|
|
||||||
|
printf("started thread %d with threadHandle %d\n",i,handle);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
///tell the task scheduler we are done with the SPU tasks
|
||||||
|
void Win32ThreadSupport::stopSPUs()
|
||||||
|
{
|
||||||
|
// m_activeSpuStatus.pop_back();
|
||||||
|
// WaitForSingleObject(spuStatus.bla, INFINITE);
|
||||||
|
// CloseHandle(spuStatus.m_threadHandle);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif //USE_WIN32_THREADING
|
||||||
116
Extras/BulletMultiThreaded/Win32ThreadSupport.h
Normal file
116
Extras/BulletMultiThreaded/Win32ThreadSupport.h
Normal file
@@ -0,0 +1,116 @@
|
|||||||
|
/*
|
||||||
|
Bullet Continuous Collision Detection and Physics Library
|
||||||
|
Copyright (c) 2003-2007 Erwin Coumans http://bulletphysics.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 "LinearMath/btScalar.h"
|
||||||
|
#include "PlatformDefinitions.h"
|
||||||
|
|
||||||
|
#ifdef USE_WIN32_THREADING //platform specific defines are defined in PlatformDefinitions.h
|
||||||
|
|
||||||
|
#ifndef WIN32_THREAD_SUPPORT_H
|
||||||
|
#define WIN32_THREAD_SUPPORT_H
|
||||||
|
|
||||||
|
#include "LinearMath/btAlignedObjectArray.h"
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#include <LinearMath/btScalar.h> //for uint32_t etc.
|
||||||
|
|
||||||
|
|
||||||
|
typedef void (*Win32ThreadFunc)(void* userPtr,void* lsMemory);
|
||||||
|
typedef void* (*Win32lsMemorySetupFunc)();
|
||||||
|
|
||||||
|
|
||||||
|
///placeholder, until libspe2 support is there
|
||||||
|
struct btSpuStatus
|
||||||
|
{
|
||||||
|
uint32_t m_taskId;
|
||||||
|
uint32_t m_commandId;
|
||||||
|
uint32_t m_status;
|
||||||
|
|
||||||
|
Win32ThreadFunc m_userThreadFunc;
|
||||||
|
void* m_userPtr; //for taskDesc etc
|
||||||
|
void* m_lsMemory; //initialized using Win32LocalStoreMemorySetupFunc
|
||||||
|
|
||||||
|
void* m_threadHandle; //this one is calling 'Win32ThreadFunc'
|
||||||
|
|
||||||
|
void* m_eventStartHandle;
|
||||||
|
char m_eventStartHandleName[32];
|
||||||
|
|
||||||
|
void* m_eventCompletetHandle;
|
||||||
|
char m_eventCompletetHandleName[32];
|
||||||
|
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
///Win32ThreadSupport helps to initialize/shutdown libspe2, start/stop SPU tasks and communication
|
||||||
|
class Win32ThreadSupport {
|
||||||
|
|
||||||
|
btAlignedObjectArray<btSpuStatus> m_activeSpuStatus;
|
||||||
|
|
||||||
|
public:
|
||||||
|
///Setup and initialize SPU/CELL/Libspe2
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
struct Win32ThreadConstructionInfo
|
||||||
|
{
|
||||||
|
Win32ThreadConstructionInfo(char* uniqueName,
|
||||||
|
Win32ThreadFunc userThreadFunc,
|
||||||
|
Win32lsMemorySetupFunc lsMemoryFunc,
|
||||||
|
int numThreads=1,
|
||||||
|
int threadStackSize=65535
|
||||||
|
)
|
||||||
|
:m_uniqueName(uniqueName),
|
||||||
|
m_userThreadFunc(userThreadFunc),
|
||||||
|
m_lsMemoryFunc(lsMemoryFunc),
|
||||||
|
m_numThreads(numThreads),
|
||||||
|
m_threadStackSize(threadStackSize)
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
char* m_uniqueName;
|
||||||
|
Win32ThreadFunc m_userThreadFunc;
|
||||||
|
Win32lsMemorySetupFunc m_lsMemoryFunc;
|
||||||
|
int m_numThreads;
|
||||||
|
int m_threadStackSize;
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
Win32ThreadSupport(Win32ThreadConstructionInfo& threadConstructionInfo);
|
||||||
|
|
||||||
|
///cleanup/shutdown Libspe2
|
||||||
|
~Win32ThreadSupport();
|
||||||
|
|
||||||
|
///send messages to SPUs
|
||||||
|
void sendRequest(uint32_t uiCommand, uint32_t uiArgument0, uint32_t uiArgument1);
|
||||||
|
|
||||||
|
///check for messages from SPUs
|
||||||
|
void waitForResponse(unsigned int *puiArgument0, unsigned int *puiArgument1);
|
||||||
|
|
||||||
|
///start the spus (can be called at the beginning of each frame, to make sure that the right SPU program is loaded)
|
||||||
|
void startSPUs(Win32ThreadConstructionInfo& threadConstructionInfo);
|
||||||
|
|
||||||
|
///tell the task scheduler we are done with the SPU tasks
|
||||||
|
void stopSPUs();
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif //WIN32_THREAD_SUPPORT_H
|
||||||
|
|
||||||
|
#endif //USE_WIN32_THREADING
|
||||||
Reference in New Issue
Block a user