5823b5d77d
Modified both MeshInstance tools as well as importer to use it instead of QuickHull.
334 lines
8.6 KiB
C++
334 lines
8.6 KiB
C++
#include "../public/VHACD.h"
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <stdarg.h>
|
|
#include <thread>
|
|
#include <atomic>
|
|
#include <mutex>
|
|
#include <string>
|
|
#include <float.h>
|
|
|
|
#define ENABLE_ASYNC 1
|
|
|
|
#define HACD_ALLOC(x) malloc(x)
|
|
#define HACD_FREE(x) free(x)
|
|
#define HACD_ASSERT(x) assert(x)
|
|
|
|
namespace VHACD
|
|
{
|
|
|
|
class MyHACD_API : public VHACD::IVHACD, public VHACD::IVHACD::IUserCallback, VHACD::IVHACD::IUserLogger
|
|
{
|
|
public:
|
|
MyHACD_API(void)
|
|
{
|
|
mVHACD = VHACD::CreateVHACD();
|
|
}
|
|
|
|
virtual ~MyHACD_API(void)
|
|
{
|
|
releaseHACD();
|
|
Cancel();
|
|
mVHACD->Release();
|
|
}
|
|
|
|
|
|
virtual bool Compute(const double* const _points,
|
|
const uint32_t countPoints,
|
|
const uint32_t* const _triangles,
|
|
const uint32_t countTriangles,
|
|
const Parameters& _desc) final
|
|
{
|
|
#if ENABLE_ASYNC
|
|
Cancel(); // if we previously had a solution running; cancel it.
|
|
releaseHACD();
|
|
|
|
// We need to copy the input vertices and triangles into our own buffers so we can operate
|
|
// on them safely from the background thread.
|
|
mVertices = (double *)HACD_ALLOC(sizeof(double)*countPoints * 3);
|
|
mIndices = (uint32_t *)HACD_ALLOC(sizeof(uint32_t)*countTriangles * 3);
|
|
memcpy(mVertices, _points, sizeof(double)*countPoints * 3);
|
|
memcpy(mIndices, _triangles, sizeof(uint32_t)*countTriangles * 3);
|
|
mRunning = true;
|
|
mThread = new std::thread([this, countPoints, countTriangles, _desc]()
|
|
{
|
|
ComputeNow(mVertices, countPoints, mIndices, countTriangles, _desc);
|
|
mRunning = false;
|
|
});
|
|
#else
|
|
releaseHACD();
|
|
ComputeNow(_points, countPoints, _triangles, countTriangles, _desc);
|
|
#endif
|
|
return true;
|
|
}
|
|
|
|
bool ComputeNow(const double* const points,
|
|
const uint32_t countPoints,
|
|
const uint32_t* const triangles,
|
|
const uint32_t countTriangles,
|
|
const Parameters& _desc)
|
|
{
|
|
uint32_t ret = 0;
|
|
|
|
mHullCount = 0;
|
|
mCallback = _desc.m_callback;
|
|
mLogger = _desc.m_logger;
|
|
|
|
IVHACD::Parameters desc = _desc;
|
|
// Set our intercepting callback interfaces if non-null
|
|
desc.m_callback = desc.m_callback ? this : nullptr;
|
|
desc.m_logger = desc.m_logger ? this : nullptr;
|
|
|
|
if ( countPoints )
|
|
{
|
|
bool ok = mVHACD->Compute(points, countPoints, triangles, countTriangles, desc);
|
|
if (ok)
|
|
{
|
|
ret = mVHACD->GetNConvexHulls();
|
|
mHulls = new IVHACD::ConvexHull[ret];
|
|
for (uint32_t i = 0; i < ret; i++)
|
|
{
|
|
VHACD::IVHACD::ConvexHull vhull;
|
|
mVHACD->GetConvexHull(i, vhull);
|
|
VHACD::IVHACD::ConvexHull h;
|
|
h.m_nPoints = vhull.m_nPoints;
|
|
h.m_points = (double *)HACD_ALLOC(sizeof(double) * 3 * h.m_nPoints);
|
|
memcpy(h.m_points, vhull.m_points, sizeof(double) * 3 * h.m_nPoints);
|
|
h.m_nTriangles = vhull.m_nTriangles;
|
|
h.m_triangles = (uint32_t *)HACD_ALLOC(sizeof(uint32_t) * 3 * h.m_nTriangles);
|
|
memcpy(h.m_triangles, vhull.m_triangles, sizeof(uint32_t) * 3 * h.m_nTriangles);
|
|
h.m_volume = vhull.m_volume;
|
|
h.m_center[0] = vhull.m_center[0];
|
|
h.m_center[1] = vhull.m_center[1];
|
|
h.m_center[2] = vhull.m_center[2];
|
|
mHulls[i] = h;
|
|
if (mCancel)
|
|
{
|
|
ret = 0;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
mHullCount = ret;
|
|
return ret ? true : false;
|
|
}
|
|
|
|
void releaseHull(VHACD::IVHACD::ConvexHull &h)
|
|
{
|
|
HACD_FREE((void *)h.m_triangles);
|
|
HACD_FREE((void *)h.m_points);
|
|
h.m_triangles = nullptr;
|
|
h.m_points = nullptr;
|
|
}
|
|
|
|
virtual void GetConvexHull(const uint32_t index, VHACD::IVHACD::ConvexHull& ch) const final
|
|
{
|
|
if ( index < mHullCount )
|
|
{
|
|
ch = mHulls[index];
|
|
}
|
|
}
|
|
|
|
void releaseHACD(void) // release memory associated with the last HACD request
|
|
{
|
|
for (uint32_t i=0; i<mHullCount; i++)
|
|
{
|
|
releaseHull(mHulls[i]);
|
|
}
|
|
delete[]mHulls;
|
|
mHulls = nullptr;
|
|
mHullCount = 0;
|
|
HACD_FREE(mVertices);
|
|
mVertices = nullptr;
|
|
HACD_FREE(mIndices);
|
|
mIndices = nullptr;
|
|
}
|
|
|
|
|
|
virtual void release(void) // release the HACD_API interface
|
|
{
|
|
delete this;
|
|
}
|
|
|
|
virtual uint32_t getHullCount(void)
|
|
{
|
|
return mHullCount;
|
|
}
|
|
|
|
virtual void Cancel() final
|
|
{
|
|
if (mRunning)
|
|
{
|
|
mVHACD->Cancel(); // Set the cancel signal to the base VHACD
|
|
}
|
|
if (mThread)
|
|
{
|
|
mThread->join(); // Wait for the thread to fully exit before we delete the instance
|
|
delete mThread;
|
|
mThread = nullptr;
|
|
Log("Convex Decomposition thread canceled\n");
|
|
}
|
|
mCancel = false; // clear the cancel semaphore
|
|
}
|
|
|
|
virtual bool Compute(const float* const points,
|
|
const uint32_t countPoints,
|
|
const uint32_t* const triangles,
|
|
const uint32_t countTriangles,
|
|
const Parameters& params) final
|
|
{
|
|
|
|
double *vertices = (double *)HACD_ALLOC(sizeof(double)*countPoints * 3);
|
|
const float *source = points;
|
|
double *dest = vertices;
|
|
for (uint32_t i = 0; i < countPoints; i++)
|
|
{
|
|
dest[0] = source[0];
|
|
dest[1] = source[1];
|
|
dest[2] = source[2];
|
|
dest += 3;
|
|
source += 3;
|
|
}
|
|
|
|
bool ret = Compute(vertices, countPoints, triangles, countTriangles, params);
|
|
HACD_FREE(vertices);
|
|
return ret;
|
|
}
|
|
|
|
virtual uint32_t GetNConvexHulls() const final
|
|
{
|
|
processPendingMessages();
|
|
return mHullCount;
|
|
}
|
|
|
|
virtual void Clean(void) final // release internally allocated memory
|
|
{
|
|
Cancel();
|
|
releaseHACD();
|
|
mVHACD->Clean();
|
|
}
|
|
|
|
virtual void Release(void) final // release IVHACD
|
|
{
|
|
delete this;
|
|
}
|
|
|
|
virtual bool OCLInit(void* const oclDevice,
|
|
IVHACD::IUserLogger* const logger = 0) final
|
|
{
|
|
return mVHACD->OCLInit(oclDevice, logger);
|
|
}
|
|
|
|
virtual bool OCLRelease(IVHACD::IUserLogger* const logger = 0) final
|
|
{
|
|
return mVHACD->OCLRelease(logger);
|
|
}
|
|
|
|
virtual void Update(const double overallProgress,
|
|
const double stageProgress,
|
|
const double operationProgress,
|
|
const char* const stage,
|
|
const char* const operation) final
|
|
{
|
|
mMessageMutex.lock();
|
|
mHaveUpdateMessage = true;
|
|
mOverallProgress = overallProgress;
|
|
mStageProgress = stageProgress;
|
|
mOperationProgress = operationProgress;
|
|
mStage = std::string(stage);
|
|
mOperation = std::string(operation);
|
|
mMessageMutex.unlock();
|
|
}
|
|
|
|
virtual void Log(const char* const msg) final
|
|
{
|
|
mMessageMutex.lock();
|
|
mHaveLogMessage = true;
|
|
mMessage = std::string(msg);
|
|
mMessageMutex.unlock();
|
|
}
|
|
|
|
virtual bool IsReady(void) const final
|
|
{
|
|
processPendingMessages();
|
|
return !mRunning;
|
|
}
|
|
|
|
// As a convenience for the calling application we only send it update and log messages from it's own main
|
|
// thread. This reduces the complexity burden on the caller by making sure it only has to deal with log
|
|
// messages in it's main application thread.
|
|
void processPendingMessages(void) const
|
|
{
|
|
// If we have a new update message and the user has specified a callback we send the message and clear the semaphore
|
|
if (mHaveUpdateMessage && mCallback)
|
|
{
|
|
mMessageMutex.lock();
|
|
mCallback->Update(mOverallProgress, mStageProgress, mOperationProgress, mStage.c_str(), mOperation.c_str());
|
|
mHaveUpdateMessage = false;
|
|
mMessageMutex.unlock();
|
|
}
|
|
// If we have a new log message and the user has specified a callback we send the message and clear the semaphore
|
|
if (mHaveLogMessage && mLogger)
|
|
{
|
|
mMessageMutex.lock();
|
|
mLogger->Log(mMessage.c_str());
|
|
mHaveLogMessage = false;
|
|
mMessageMutex.unlock();
|
|
}
|
|
}
|
|
|
|
// Will compute the center of mass of the convex hull decomposition results and return it
|
|
// in 'centerOfMass'. Returns false if the center of mass could not be computed.
|
|
virtual bool ComputeCenterOfMass(double centerOfMass[3]) const
|
|
{
|
|
bool ret = false;
|
|
|
|
centerOfMass[0] = 0;
|
|
centerOfMass[1] = 0;
|
|
centerOfMass[2] = 0;
|
|
|
|
if (mVHACD && IsReady() )
|
|
{
|
|
ret = mVHACD->ComputeCenterOfMass(centerOfMass);
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
private:
|
|
double *mVertices{ nullptr };
|
|
uint32_t *mIndices{ nullptr };
|
|
std::atomic< uint32_t> mHullCount{ 0 };
|
|
VHACD::IVHACD::ConvexHull *mHulls{ nullptr };
|
|
VHACD::IVHACD::IUserCallback *mCallback{ nullptr };
|
|
VHACD::IVHACD::IUserLogger *mLogger{ nullptr };
|
|
VHACD::IVHACD *mVHACD{ nullptr };
|
|
std::thread *mThread{ nullptr };
|
|
std::atomic< bool > mRunning{ false };
|
|
std::atomic<bool> mCancel{ false };
|
|
|
|
// Thread safe caching mechanism for messages and update status.
|
|
// This is so that caller always gets messages in his own thread
|
|
// Member variables are marked as 'mutable' since the message dispatch function
|
|
// is called from const query methods.
|
|
mutable std::mutex mMessageMutex;
|
|
mutable std::atomic< bool > mHaveUpdateMessage{ false };
|
|
mutable std::atomic< bool > mHaveLogMessage{ false };
|
|
mutable double mOverallProgress{ 0 };
|
|
mutable double mStageProgress{ 0 };
|
|
mutable double mOperationProgress{ 0 };
|
|
mutable std::string mStage;
|
|
mutable std::string mOperation;
|
|
mutable std::string mMessage;
|
|
};
|
|
|
|
IVHACD* CreateVHACD_ASYNC(void)
|
|
{
|
|
MyHACD_API *m = new MyHACD_API;
|
|
return static_cast<IVHACD *>(m);
|
|
}
|
|
|
|
|
|
}; // end of VHACD namespace
|
|
|