working vhacd

renamed ThreadPool to TorqueThreadPool to avoid conflics
fixed data transmission between stages of convexDecome and trimesh creation
TODO: re-add our own functions for generating sphere/cylinder/box
This commit is contained in:
marauder2k7 2024-05-12 14:43:56 +01:00
parent 679f0ff065
commit eb33fe04af
15 changed files with 116 additions and 75 deletions

View file

@ -51,7 +51,7 @@ torqueAddSourceDirectories("app" "app/net")
# Handle console
torqueAddSourceDirectories("console")
torqueAddSourceDirectories("console/torquescript")
set(TORQUE_INCLUDE_DIRECTORIES ${TORQUE_INCLUDE_DIRECTORIES} "ts/vhacd")
# Handle Platform
torqueAddSourceDirectories("platform" "platform/threads" "platform/async"
"platform/input" "platform/output")
@ -86,7 +86,6 @@ torqueAddSourceDirectories("gfx" "gfx/Null" "gfx/test" "gfx/bitmap" "gfx/bitmap/
# add the stb headers
set(TORQUE_INCLUDE_DIRECTORIES ${TORQUE_INCLUDE_DIRECTORIES} "gfx/bitmap/loaders/stb")
set(TORQUE_INCLUDE_DIRECTORIES ${TORQUE_INCLUDE_DIRECTORIES} "ts/vhacd")
if (TORQUE_OPENGL)
torqueAddSourceDirectories("gfx/gl" "gfx/gl/sdl" "gfx/gl/tGL")

View file

@ -262,7 +262,7 @@ void StandardMainLoop::init()
RedBook::init();
Platform::initConsole();
ThreadPool::GlobalThreadPool::createSingleton();
TorqueThreadPool::GlobalThreadPool::createSingleton();
// Set engineAPI initialized to true
engineAPI::gIsInitialized = true;
@ -293,7 +293,7 @@ void StandardMainLoop::init()
Con::setVariable("TorqueScriptFileExtension", TORQUE_SCRIPT_EXTENSION);
Con::addVariable( "_forceAllMainThread", TypeBool, &ThreadPool::getForceAllMainThread(), "Force all work items to execute on main thread. turns this into a single-threaded system. Primarily useful to find whether malfunctions are caused by parallel execution or not.\n"
Con::addVariable( "_forceAllMainThread", TypeBool, &TorqueThreadPool::getForceAllMainThread(), "Force all work items to execute on main thread. turns this into a single-threaded system. Primarily useful to find whether malfunctions are caused by parallel execution or not.\n"
"@ingroup platform" );
#if defined( TORQUE_MINIDUMP ) && defined( TORQUE_RELEASE )
@ -351,7 +351,7 @@ void StandardMainLoop::shutdown()
EngineModuleManager::shutdownSystem();
ThreadPool::GlobalThreadPool::deleteSingleton();
TorqueThreadPool::GlobalThreadPool::deleteSingleton();
#ifdef TORQUE_ENABLE_VFS
closeEmbeddedVFSArchive();
@ -636,7 +636,7 @@ bool StandardMainLoop::doMainLoop()
if(!Process::processEvents())
keepRunning = false;
ThreadPool::processMainThreadWorkItems();
TorqueThreadPool::processMainThreadWorkItems();
Sampler::endFrame();
ConsoleValue::resetConversionBuffer();
PROFILE_END_NAMED(MainLoop);

View file

@ -65,7 +65,7 @@ namespace ImageUtil
}
//Thread work job for compression
struct CompressJob : public ThreadPool::WorkItem
struct CompressJob : public TorqueThreadPool::WorkItem
{
S32 width;
S32 height;
@ -124,7 +124,7 @@ namespace ImageUtil
srcDDS->mFlags.set(DDSFile::CompressedData);
//grab global thread pool
ThreadPool* pThreadPool = &ThreadPool::GLOBAL();
TorqueThreadPool* pThreadPool = &TorqueThreadPool::GLOBAL();
if (cubemap)
{

View file

@ -336,7 +336,7 @@ void TheoraTexture::_onTextureEvent( GFXTexCallbackCode code )
{
// Blast out work items and then release all texture locks.
ThreadPool::GLOBAL().flushWorkItems();
TorqueThreadPool::GLOBAL().flushWorkItems();
mAsyncState->getFrameStream()->releaseTextureLocks();
// The Theora decoder does not implement seeking at the moment,

View file

@ -177,7 +177,7 @@ class TheoraTexture : private IOutputStream< TheoraTextureFrame* >,
///
FrameReadItem( AsyncBufferedInputStream< TheoraTextureFrame*, IInputStream< OggTheoraFrame* >* >* stream,
ThreadPool::Context* context );
TorqueThreadPool::Context* context );
};
/// Stream filter that turns a stream of OggTheoraFrames into a buffered background stream of TheoraTextureFrame

View file

@ -107,7 +107,7 @@ class AsyncBufferedInputStream : public IInputStreamFilter< T, Stream >,
ElementList mBufferedElements;
/// The thread pool to which read items are queued.
ThreadPool* mThreadPool;
TorqueThreadPool* mThreadPool;
/// The thread context used for prioritizing read items in the pool.
ThreadContext* mThreadContext;
@ -132,7 +132,7 @@ class AsyncBufferedInputStream : public IInputStreamFilter< T, Stream >,
U32 numSourceElementsToRead = 0,
U32 numReadAhead = DEFAULT_STREAM_LOOKAHEAD,
bool isLooping = false,
ThreadPool* pool = &ThreadPool::GLOBAL(),
TorqueThreadPool* pool = &TorqueThreadPool::GLOBAL(),
ThreadContext* context = ThreadContext::ROOT_CONTEXT() );
virtual ~AsyncBufferedInputStream();
@ -162,7 +162,7 @@ AsyncBufferedInputStream< T, Stream >::AsyncBufferedInputStream
U32 numSourceElementsToRead,
U32 numReadAhead,
bool isLooping,
ThreadPool* threadPool,
TorqueThreadPool* threadPool,
ThreadContext* threadContext )
: Parent( stream ),
mIsLooping( isLooping ),
@ -321,7 +321,7 @@ class AsyncBufferedReadItem : public ThreadWorkItem
///
AsyncBufferedReadItem(
const AsyncStreamRef& asyncStream,
ThreadPool::Context* context = NULL
TorqueThreadPool::Context* context = NULL
)
: Parent( context ),
mAsyncStream( asyncStream ),
@ -376,7 +376,7 @@ class AsyncSingleBufferedInputStream : public AsyncBufferedInputStream< T, Strea
U32 numSourceElementsToRead = 0,
U32 numReadAhead = Parent::DEFAULT_STREAM_LOOKAHEAD,
bool isLooping = false,
ThreadPool* pool = &ThreadPool::GLOBAL(),
TorqueThreadPool* pool = &TorqueThreadPool::GLOBAL(),
ThreadContext* context = ThreadContext::ROOT_CONTEXT() )
: Parent( stream,
numSourceElementsToRead,

View file

@ -113,7 +113,7 @@ class AsyncPacketBufferedInputStream : public AsyncBufferedInputStream< Packet*,
PacketReadItem( const ThreadSafeRef< AsyncPacketBufferedInputStream< Stream, Packet > >& asyncStream,
PacketType* packet,
U32 numElements,
ThreadPool::Context* context = NULL )
TorqueThreadPool::Context* context = NULL )
: Parent( asyncStream->getSourceStream(), numElements, 0, *packet, false, 0, context ),
mAsyncStream( asyncStream ),
mPacket( packet ) {}
@ -227,7 +227,7 @@ class AsyncPacketBufferedInputStream : public AsyncBufferedInputStream< Packet*,
U32 numSourceElementsToRead = 0,
U32 numReadAhead = Parent::DEFAULT_STREAM_LOOKAHEAD,
bool isLooping = false,
ThreadPool* pool = &ThreadPool::GLOBAL(),
TorqueThreadPool* pool = &TorqueThreadPool::GLOBAL(),
ThreadContext* context = ThreadContext::ROOT_CONTEXT() );
/// @return the size of stream packets returned by this stream in number of elements.
@ -241,7 +241,7 @@ AsyncPacketBufferedInputStream< Stream, Packet >::AsyncPacketBufferedInputStream
U32 numSourceElementsToRead,
U32 numReadAhead,
bool isLooping,
ThreadPool* threadPool,
TorqueThreadPool* threadPool,
ThreadContext* threadContext )
: Parent( stream, numSourceElementsToRead, numReadAhead, isLooping, threadPool, threadContext ),
mPacketSize( packetSize ),

View file

@ -65,11 +65,11 @@ struct NetAsync::NameLookupRequest
/// Work item issued to the thread pool for each lookup request.
struct NetAsync::NameLookupWorkItem : public ThreadPool::WorkItem
struct NetAsync::NameLookupWorkItem : public TorqueThreadPool::WorkItem
{
typedef ThreadPool::WorkItem Parent;
typedef TorqueThreadPool::WorkItem Parent;
NameLookupWorkItem( NameLookupRequest& request, ThreadPool::Context* context = 0 )
NameLookupWorkItem( NameLookupRequest& request, TorqueThreadPool::Context* context = 0 )
: Parent( context ),
mRequest( request )
{
@ -133,7 +133,7 @@ void NetAsync::queueLookup(const char* remoteAddr, NetSocket socket)
dStrncpy(lookupRequest.remoteAddr, remoteAddr, sizeof(lookupRequest.remoteAddr));
ThreadSafeRef< NameLookupWorkItem > workItem( new NameLookupWorkItem( lookupRequest ) );
ThreadPool::GLOBAL().queueWorkItem( workItem );
TorqueThreadPool::GLOBAL().queueWorkItem( workItem );
}
bool NetAsync::checkLookup(NetSocket socket, void* out_h_addr,

View file

@ -34,11 +34,11 @@
// ThreadPool::Context.
//=============================================================================
ThreadPool::Context ThreadPool::Context::smRootContext( "ROOT", NULL, 1.0 );
TorqueThreadPool::Context TorqueThreadPool::Context::smRootContext( "ROOT", NULL, 1.0 );
//--------------------------------------------------------------------------
ThreadPool::Context::Context( const char* name, ThreadPool::Context* parent, F32 priorityBias )
TorqueThreadPool::Context::Context( const char* name, TorqueThreadPool::Context* parent, F32 priorityBias )
: mParent( parent ),
mName( name ),
mChildren( 0 ),
@ -55,7 +55,7 @@ ThreadPool::Context::Context( const char* name, ThreadPool::Context* parent, F32
//--------------------------------------------------------------------------
ThreadPool::Context::~Context()
TorqueThreadPool::Context::~Context()
{
if( mParent )
for( Context* context = mParent->mChildren, *prev = 0; context != 0; prev = context, context = context->mSibling )
@ -70,7 +70,7 @@ ThreadPool::Context::~Context()
//--------------------------------------------------------------------------
ThreadPool::Context* ThreadPool::Context::getChild( const char* name )
TorqueThreadPool::Context* TorqueThreadPool::Context::getChild( const char* name )
{
for( Context* child = getChildren(); child != 0; child = child->getSibling() )
if( dStricmp( child->getName(), name ) == 0 )
@ -80,7 +80,7 @@ ThreadPool::Context* ThreadPool::Context::getChild( const char* name )
//--------------------------------------------------------------------------
F32 ThreadPool::Context::getAccumulatedPriorityBias()
F32 TorqueThreadPool::Context::getAccumulatedPriorityBias()
{
if( !mAccumulatedPriorityBias )
updateAccumulatedPriorityBiases();
@ -89,7 +89,7 @@ F32 ThreadPool::Context::getAccumulatedPriorityBias()
//--------------------------------------------------------------------------
void ThreadPool::Context::setPriorityBias( F32 value )
void TorqueThreadPool::Context::setPriorityBias( F32 value )
{
mPriorityBias = value;
mAccumulatedPriorityBias = 0.0;
@ -97,7 +97,7 @@ void ThreadPool::Context::setPriorityBias( F32 value )
//--------------------------------------------------------------------------
void ThreadPool::Context::updateAccumulatedPriorityBiases()
void TorqueThreadPool::Context::updateAccumulatedPriorityBiases()
{
// Update our own priority bias.
@ -117,7 +117,7 @@ void ThreadPool::Context::updateAccumulatedPriorityBiases()
//--------------------------------------------------------------------------
void ThreadPool::WorkItem::process()
void TorqueThreadPool::WorkItem::process()
{
execute();
mExecuted = true;
@ -125,14 +125,14 @@ void ThreadPool::WorkItem::process()
//--------------------------------------------------------------------------
bool ThreadPool::WorkItem::isCancellationRequested()
bool TorqueThreadPool::WorkItem::isCancellationRequested()
{
return false;
}
//--------------------------------------------------------------------------
bool ThreadPool::WorkItem::cancellationPoint()
bool TorqueThreadPool::WorkItem::cancellationPoint()
{
if( isCancellationRequested() )
{
@ -145,7 +145,7 @@ bool ThreadPool::WorkItem::cancellationPoint()
//--------------------------------------------------------------------------
F32 ThreadPool::WorkItem::getPriority()
F32 TorqueThreadPool::WorkItem::getPriority()
{
return 1.0;
}
@ -160,7 +160,7 @@ F32 ThreadPool::WorkItem::getPriority()
/// @see ThreadSafePriorityQueueWithUpdate
/// @see ThreadPool::WorkItem
///
struct ThreadPool::WorkItemWrapper : public ThreadSafeRef< WorkItem >
struct TorqueThreadPool::WorkItemWrapper : public ThreadSafeRef< WorkItem >
{
typedef ThreadSafeRef< WorkItem > Parent;
@ -172,7 +172,7 @@ struct ThreadPool::WorkItemWrapper : public ThreadSafeRef< WorkItem >
F32 getPriority();
};
inline bool ThreadPool::WorkItemWrapper::isAlive()
inline bool TorqueThreadPool::WorkItemWrapper::isAlive()
{
WorkItem* item = ptr();
if( !item )
@ -186,7 +186,7 @@ inline bool ThreadPool::WorkItemWrapper::isAlive()
return true;
}
inline F32 ThreadPool::WorkItemWrapper::getPriority()
inline F32 TorqueThreadPool::WorkItemWrapper::getPriority()
{
WorkItem* item = ptr();
AssertFatal( item != 0, "ThreadPool::WorkItemWrapper::getPriority - called on dead item" );
@ -201,20 +201,20 @@ inline F32 ThreadPool::WorkItemWrapper::getPriority()
///
///
struct ThreadPool::WorkerThread : public Thread
struct TorqueThreadPool::WorkerThread : public Thread
{
WorkerThread( ThreadPool* pool, U32 index );
WorkerThread( TorqueThreadPool* pool, U32 index );
WorkerThread* getNext();
void run( void* arg = 0 ) override;
private:
U32 mIndex;
ThreadPool* mPool;
TorqueThreadPool* mPool;
WorkerThread* mNext;
};
ThreadPool::WorkerThread::WorkerThread( ThreadPool* pool, U32 index )
TorqueThreadPool::WorkerThread::WorkerThread( TorqueThreadPool* pool, U32 index )
: mIndex( index ),
mPool( pool )
{
@ -224,12 +224,12 @@ ThreadPool::WorkerThread::WorkerThread( ThreadPool* pool, U32 index )
pool->mThreads = this;
}
inline ThreadPool::WorkerThread* ThreadPool::WorkerThread::getNext()
inline TorqueThreadPool::WorkerThread* TorqueThreadPool::WorkerThread::getNext()
{
return mNext;
}
void ThreadPool::WorkerThread::run( void* arg )
void TorqueThreadPool::WorkerThread::run( void* arg )
{
#ifdef TORQUE_DEBUG
{
@ -300,13 +300,13 @@ void ThreadPool::WorkerThread::run( void* arg )
// ThreadPool.
//=============================================================================
bool ThreadPool::smForceAllMainThread;
U32 ThreadPool::smMainThreadTimeMS;
ThreadPool::QueueType ThreadPool::smMainThreadQueue;
bool TorqueThreadPool::smForceAllMainThread;
U32 TorqueThreadPool::smMainThreadTimeMS;
TorqueThreadPool::QueueType TorqueThreadPool::smMainThreadQueue;
//--------------------------------------------------------------------------
ThreadPool::ThreadPool( const char* name, U32 numThreads )
TorqueThreadPool::TorqueThreadPool( const char* name, U32 numThreads )
: mName( name ),
mNumThreads( numThreads ),
mNumThreadsAwake( 0 ),
@ -347,14 +347,14 @@ ThreadPool::ThreadPool( const char* name, U32 numThreads )
//--------------------------------------------------------------------------
ThreadPool::~ThreadPool()
TorqueThreadPool::~TorqueThreadPool()
{
shutdown();
}
//--------------------------------------------------------------------------
void ThreadPool::shutdown()
void TorqueThreadPool::shutdown()
{
const U32 numThreads = mNumThreads;
@ -387,7 +387,7 @@ void ThreadPool::shutdown()
//--------------------------------------------------------------------------
void ThreadPool::queueWorkItem( WorkItem* item )
void TorqueThreadPool::queueWorkItem( WorkItem* item )
{
bool executeRightAway = ( getForceAllMainThread() );
#ifdef DEBUG_SPEW
@ -410,7 +410,7 @@ void ThreadPool::queueWorkItem( WorkItem* item )
//--------------------------------------------------------------------------
void ThreadPool::flushWorkItems( S32 timeOut )
void TorqueThreadPool::flushWorkItems( S32 timeOut )
{
AssertFatal( mNumThreads, "ThreadPool::flushWorkItems() - no worker threads in pool" );
@ -432,7 +432,7 @@ void ThreadPool::flushWorkItems( S32 timeOut )
}
}
void ThreadPool::waitForAllItems( S32 timeOut )
void TorqueThreadPool::waitForAllItems( S32 timeOut )
{
U32 endTime = 0;
if( timeOut != -1 )
@ -454,14 +454,14 @@ void ThreadPool::waitForAllItems( S32 timeOut )
//--------------------------------------------------------------------------
void ThreadPool::queueWorkItemOnMainThread( WorkItem* item )
void TorqueThreadPool::queueWorkItemOnMainThread( WorkItem* item )
{
smMainThreadQueue.insert( item->getPriority(), item );
}
//--------------------------------------------------------------------------
void ThreadPool::processMainThreadWorkItems()
void TorqueThreadPool::processMainThreadWorkItems()
{
AssertFatal( ThreadManager::isMainThread(),
"ThreadPool::processMainThreadWorkItems - this function must only be called on the main thread" );

View file

@ -70,7 +70,7 @@
/// automatically being released once the last concurrent work item has been
/// processed or discarded.
///
class ThreadPool
class TorqueThreadPool
{
public:
@ -298,9 +298,9 @@ class ThreadPool
/// will be based on the number of CPU cores available.
///
/// @param numThreads Number of threads to create or zero for default.
ThreadPool( const char* name, U32 numThreads = 0 );
TorqueThreadPool( const char* name, U32 numThreads = 0 );
~ThreadPool();
~TorqueThreadPool();
/// Manually shutdown threads outside of static destructors.
void shutdown();
@ -397,16 +397,16 @@ class ThreadPool
}
/// Return the global thread pool singleton.
static ThreadPool& GLOBAL();
static TorqueThreadPool& GLOBAL();
};
typedef ThreadPool::Context ThreadContext;
typedef ThreadPool::WorkItem ThreadWorkItem;
typedef TorqueThreadPool::Context ThreadContext;
typedef TorqueThreadPool::WorkItem ThreadWorkItem;
struct ThreadPool::GlobalThreadPool : public ThreadPool, public ManagedSingleton< GlobalThreadPool >
struct TorqueThreadPool::GlobalThreadPool : public TorqueThreadPool, public ManagedSingleton< GlobalThreadPool >
{
typedef ThreadPool Parent;
typedef TorqueThreadPool Parent;
GlobalThreadPool()
: Parent( "GLOBAL" ) {}
@ -415,7 +415,7 @@ struct ThreadPool::GlobalThreadPool : public ThreadPool, public ManagedSingleton
static const char* getSingletonName() { return "GlobalThreadPool"; }
};
inline ThreadPool& ThreadPool::GLOBAL()
inline TorqueThreadPool& TorqueThreadPool::GLOBAL()
{
return *( GlobalThreadPool::instance() );
}

View file

@ -54,7 +54,7 @@
///
/// @param T Type of elements being streamed.
template< typename T, class Stream >
class AsyncIOItem : public ThreadPool::WorkItem
class AsyncIOItem : public TorqueThreadPool::WorkItem
{
public:

View file

@ -24,6 +24,8 @@
#include "platform/platformRedBook.h"
#include "core/strings/unicode.h"
#include "core/strings/stringFunctions.h"
#include <windows.h>
#include <mmsystem.h>
class Win32RedBookDevice : public RedBookDevice
{

View file

@ -358,11 +358,11 @@ enum
/// @note Don't use this directly but rather use THREAD_POOL() instead.
/// This way, the sound code may be easily switched to using a common
/// pool later on.
class SFXThreadPool : public ThreadPool, public ManagedSingleton< SFXThreadPool >
class SFXThreadPool : public TorqueThreadPool, public ManagedSingleton< SFXThreadPool >
{
public:
typedef ThreadPool Parent;
typedef TorqueThreadPool Parent;
/// Create a ThreadPool called "SFX" with two threads.
SFXThreadPool()
@ -399,7 +399,7 @@ extern ThreadSafeRef< SFXBufferProcessList > gBufferUpdateList;
extern ThreadSafeDeque< SFXBuffer* > gDeadBufferList;
/// Return the thread pool used for SFX work.
inline ThreadPool& THREAD_POOL()
inline TorqueThreadPool& THREAD_POOL()
{
return *( SFXThreadPool::instance() );
}

View file

@ -26,12 +26,12 @@
#include "ts/tsShapeConstruct.h"
#include "console/engineAPI.h"
//-----------------------------------------------------------------------------
#define ENABLE_VHACD_IMPLEMENTATION 1
#define VHACD_DISABLE_THREADING 0
#include <VHACD.H>
//-----------------------------------------------------------------------------
static const Point3F sFacePlanes[] = {
Point3F( -1.0f, 0.0f, 0.0f ),
Point3F( 1.0f, 0.0f, 0.0f ),
@ -613,7 +613,7 @@ void MeshFit::fitK_DOP( const Vector<Point3F>& planes )
VHACD::IVHACD* iface = VHACD::CreateVHACD();
iface->Compute((F32*)points.address(), points.size(), (U32*)pointIndices.address(), pointIndices.size(), p);
iface->Compute((F32*)points.address(), points.size(), (U32*)pointIndices.address(), pointIndices.size() / 3, p);
// safety loop.
while (!iface->IsReady())
@ -630,11 +630,31 @@ void MeshFit::fitK_DOP( const Vector<Point3F>& planes )
MeshFit::Mesh& lastMesh = mMeshes.last();
lastMesh.type = MeshFit::Hull;
lastMesh.transform.identity();
lastMesh.tsmesh = createTriMesh((F32*)&ch.m_points, ch.m_points.size(),
(U32*)&ch.m_triangles, ch.m_triangles.size());
U32* indices = new U32[ch.m_triangles.size() * 3];
for (U32 i = 0; i < ch.m_triangles.size(); i++)
{
indices[i * 3 + 0] = ch.m_triangles[i].mI0;
indices[i * 3 + 1] = ch.m_triangles[i].mI1;
indices[i * 3 + 2] = ch.m_triangles[i].mI2;
}
F32* resultPts = new F32[ch.m_points.size() * 3];
for (U32 i = 0; i < ch.m_points.size(); i++)
{
resultPts[i * 3 + 0] = ch.m_points[i].mX;
resultPts[i * 3 + 1] = ch.m_points[i].mY;
resultPts[i * 3 + 2] = ch.m_points[i].mZ;
}
lastMesh.tsmesh = createTriMesh(resultPts, ch.m_points.size(),
indices, ch.m_triangles.size());
lastMesh.tsmesh->computeBounds();
iface->Release();
delete[] resultPts;
delete[] indices;
}
//---------------------------
@ -654,9 +674,9 @@ void MeshFit::fitConvexHulls( U32 depth, F32 mergeThreshold, F32 concavityThresh
p.m_resolution = 10000;
p.m_maxConvexHulls = depth;
VHACD::IVHACD* iface = VHACD::CreateVHACD();
VHACD::IVHACD* iface = VHACD::CreateVHACD_ASYNC();
iface->Compute((F32*)mVerts.address(), mVerts.size(), (U32*)mIndices.address(), mIndices.size(), p);
iface->Compute((F32*)mVerts.address(), mVerts.size(), mIndices.address(), mIndices.size() / 3, p);
// safety loop.
while (!iface->IsReady())
@ -731,8 +751,28 @@ void MeshFit::fitConvexHulls( U32 depth, F32 mergeThreshold, F32 concavityThresh
MeshFit::Mesh& lastMesh = mMeshes.last();
lastMesh.type = MeshFit::Hull;
lastMesh.transform.identity();
lastMesh.tsmesh = createTriMesh((F32*)&ch.m_points, ch.m_points.size(), (U32*)&ch.m_triangles, ch.m_triangles.size());
U32* indices = new U32[ch.m_triangles.size() * 3];
for (U32 i = 0; i < ch.m_triangles.size(); i++)
{
indices[i * 3 + 0] = ch.m_triangles[i].mI0;
indices[i * 3 + 1] = ch.m_triangles[i].mI1;
indices[i * 3 + 2] = ch.m_triangles[i].mI2;
}
F32* points = new F32[ch.m_points.size() * 3];
for (U32 i = 0; i < ch.m_points.size(); i++)
{
points[i * 3 + 0] = ch.m_points[i].mX;
points[i * 3 + 1] = ch.m_points[i].mY;
points[i * 3 + 2] = ch.m_points[i].mZ;
}
lastMesh.tsmesh = createTriMesh(points, ch.m_points.size(), indices, ch.m_triangles.size());
lastMesh.tsmesh->computeBounds();
delete[] points;
delete[] indices;
}
}