* Adjustment: Update Bullet version to 3.24.

This commit is contained in:
Robert MacGregor 2022-06-27 10:01:08 -04:00
parent 35de012ee7
commit 4a3f31df2a
6148 changed files with 2112532 additions and 56873 deletions

View file

@ -0,0 +1,14 @@
Copyright (c) 2011 Khaled Mamou (kmamou at gmail dot com)
All rights reserved.
Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
3. The names of the contributors may not be used to endorse or promote products derived from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

View file

@ -0,0 +1,84 @@
/* Copyright (c) 2011 Khaled Mamou (kmamou at gmail dot com)
All rights reserved.
Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
3. The names of the contributors may not be used to endorse or promote products derived from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#pragma once
#ifndef VHACD_CIRCULAR_LIST_H
#define VHACD_CIRCULAR_LIST_H
#include <stdlib.h>
namespace VHACD
{
//! CircularListElement class.
template <typename T>
class CircularListElement
{
public:
T& GetData() { return m_data; }
const T& GetData() const { return m_data; }
CircularListElement<T>*& GetNext() { return m_next; }
CircularListElement<T>*& GetPrev() { return m_prev; }
const CircularListElement<T>*& GetNext() const { return m_next; }
const CircularListElement<T>*& GetPrev() const { return m_prev; }
//! Constructor
CircularListElement(const T& data) { m_data = data; }
CircularListElement(void) {}
//! Destructor
~CircularListElement(void) {}
private:
T m_data;
CircularListElement<T>* m_next;
CircularListElement<T>* m_prev;
CircularListElement(const CircularListElement& rhs);
};
//! CircularList class.
template <typename T>
class CircularList
{
public:
CircularListElement<T>*& GetHead() { return m_head; }
const CircularListElement<T>* GetHead() const { return m_head; }
bool IsEmpty() const { return (m_size == 0); }
size_t GetSize() const { return m_size; }
const T& GetData() const { return m_head->GetData(); }
T& GetData() { return m_head->GetData(); }
bool Delete();
bool Delete(CircularListElement<T>* element);
CircularListElement<T>* Add(const T* data = 0);
CircularListElement<T>* Add(const T& data);
bool Next();
bool Prev();
void Clear()
{
while (Delete())
;
};
const CircularList& operator=(const CircularList& rhs);
//! Constructor
CircularList()
{
m_head = 0;
m_size = 0;
}
CircularList(const CircularList& rhs);
//! Destructor
~CircularList(void) { Clear(); };
private:
CircularListElement<T>* m_head; //!< a pointer to the head of the circular list
size_t m_size; //!< number of element in the circular list
};
} // namespace VHACD
#include "vhacdCircularList.inl"
#endif // VHACD_CIRCULAR_LIST_H

View file

@ -0,0 +1,161 @@
#pragma once
#ifndef HACD_CIRCULAR_LIST_INL
#define HACD_CIRCULAR_LIST_INL
namespace VHACD
{
template < typename T >
inline bool CircularList<T>::Delete(CircularListElement<T> * element)
{
if (!element)
{
return false;
}
if (m_size > 1)
{
CircularListElement<T> * next = element->GetNext();
CircularListElement<T> * prev = element->GetPrev();
delete element;
m_size--;
if (element == m_head)
{
m_head = next;
}
next->GetPrev() = prev;
prev->GetNext() = next;
return true;
}
else if (m_size == 1)
{
delete m_head;
m_size--;
m_head = 0;
return true;
}
else
{
return false;
}
}
template < typename T >
inline bool CircularList<T>::Delete()
{
if (m_size > 1)
{
CircularListElement<T> * next = m_head->GetNext();
CircularListElement<T> * prev = m_head->GetPrev();
delete m_head;
m_size--;
m_head = next;
next->GetPrev() = prev;
prev->GetNext() = next;
return true;
}
else if (m_size == 1)
{
delete m_head;
m_size--;
m_head = 0;
return true;
}
else
{
return false;
}
}
template < typename T >
inline CircularListElement<T> * CircularList<T>::Add(const T * data)
{
if (m_size == 0)
{
if (data)
{
m_head = new CircularListElement<T>(*data);
}
else
{
m_head = new CircularListElement<T>();
}
m_head->GetNext() = m_head->GetPrev() = m_head;
}
else
{
CircularListElement<T> * next = m_head->GetNext();
CircularListElement<T> * element = m_head;
if (data)
{
m_head = new CircularListElement<T>(*data);
}
else
{
m_head = new CircularListElement<T>();
}
m_head->GetNext() = next;
m_head->GetPrev() = element;
element->GetNext() = m_head;
next->GetPrev() = m_head;
}
m_size++;
return m_head;
}
template < typename T >
inline CircularListElement<T> * CircularList<T>::Add(const T & data)
{
const T * pData = &data;
return Add(pData);
}
template < typename T >
inline bool CircularList<T>::Next()
{
if (m_size == 0)
{
return false;
}
m_head = m_head->GetNext();
return true;
}
template < typename T >
inline bool CircularList<T>::Prev()
{
if (m_size == 0)
{
return false;
}
m_head = m_head->GetPrev();
return true;
}
template < typename T >
inline CircularList<T>::CircularList(const CircularList& rhs)
{
if (rhs.m_size > 0)
{
CircularListElement<T> * current = rhs.m_head;
do
{
current = current->GetNext();
Add(current->GetData());
}
while ( current != rhs.m_head );
}
}
template < typename T >
inline const CircularList<T>& CircularList<T>::operator=(const CircularList& rhs)
{
if (&rhs != this)
{
Clear();
if (rhs.m_size > 0)
{
CircularListElement<T> * current = rhs.m_head;
do
{
current = current->GetNext();
Add(current->GetData());
}
while ( current != rhs.m_head );
}
}
return (*this);
}
}
#endif

View file

@ -0,0 +1,101 @@
/* Copyright (c) 2011 Khaled Mamou (kmamou at gmail dot com)
All rights reserved.
Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
3. The names of the contributors may not be used to endorse or promote products derived from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#pragma once
#ifndef VHACD_ICHULL_H
#define VHACD_ICHULL_H
#include "vhacdManifoldMesh.h"
#include "vhacdVector.h"
namespace VHACD
{
//! Incremental Convex Hull algorithm (cf. http://cs.smith.edu/~orourke/books/ftp.html ).
enum ICHullError
{
ICHullErrorOK = 0,
ICHullErrorCoplanarPoints,
ICHullErrorNoVolume,
ICHullErrorInconsistent,
ICHullErrorNotEnoughPoints
};
class ICHull
{
public:
static const double sc_eps;
//!
bool IsFlat() { return m_isFlat; }
//! Returns the computed mesh
TMMesh& GetMesh() { return m_mesh; }
//! Add one point to the convex-hull
bool AddPoint(const Vec3<double>& point) { return AddPoints(&point, 1); }
//! Add one point to the convex-hull
bool AddPoint(const Vec3<double>& point, int id);
//! Add points to the convex-hull
bool AddPoints(const Vec3<double>* points, size_t nPoints);
//!
ICHullError Process();
//!
ICHullError Process(const unsigned int nPointsCH, const double minVolume = 0.0);
//!
bool IsInside(const Vec3<double>& pt0, const double eps = 0.0);
//!
const ICHull& operator=(ICHull& rhs);
//! Constructor
ICHull();
//! Destructor
~ICHull(void){};
private:
//! DoubleTriangle builds the initial double triangle. It first finds 3 noncollinear points and makes two faces out of them, in opposite order. It then finds a fourth point that is not coplanar with that face. The vertices are stored in the face structure in counterclockwise order so that the volume between the face and the point is negative. Lastly, the 3 newfaces to the fourth point are constructed and the data structures are cleaned up.
ICHullError DoubleTriangle();
//! MakeFace creates a new face structure from three vertices (in ccw order). It returns a pointer to the face.
CircularListElement<TMMTriangle>* MakeFace(CircularListElement<TMMVertex>* v0,
CircularListElement<TMMVertex>* v1,
CircularListElement<TMMVertex>* v2,
CircularListElement<TMMTriangle>* fold);
//!
CircularListElement<TMMTriangle>* MakeConeFace(CircularListElement<TMMEdge>* e, CircularListElement<TMMVertex>* v);
//!
bool ProcessPoint();
//!
bool ComputePointVolume(double& totalVolume, bool markVisibleFaces);
//!
bool FindMaxVolumePoint(const double minVolume = 0.0);
//!
bool CleanEdges();
//!
bool CleanVertices(unsigned int& addedPoints);
//!
bool CleanTriangles();
//!
bool CleanUp(unsigned int& addedPoints);
//!
bool MakeCCW(CircularListElement<TMMTriangle>* f,
CircularListElement<TMMEdge>* e,
CircularListElement<TMMVertex>* v);
void Clear();
private:
static const int sc_dummyIndex;
TMMesh m_mesh;
SArray<CircularListElement<TMMEdge>*> m_edgesToDelete;
SArray<CircularListElement<TMMEdge>*> m_edgesToUpdate;
SArray<CircularListElement<TMMTriangle>*> m_trianglesToDelete;
Vec3<double> m_normal;
bool m_isFlat;
ICHull(const ICHull& rhs);
};
} // namespace VHACD
#endif // VHACD_ICHULL_H

View file

@ -0,0 +1,147 @@
/* Copyright (c) 2011 Khaled Mamou (kmamou at gmail dot com)
All rights reserved.
Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
3. The names of the contributors may not be used to endorse or promote products derived from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#pragma once
#ifndef VHACD_MANIFOLD_MESH_H
#define VHACD_MANIFOLD_MESH_H
#include "vhacdCircularList.h"
#include "vhacdSArray.h"
#include "vhacdVector.h"
namespace VHACD
{
class TMMTriangle;
class TMMEdge;
class TMMesh;
class ICHull;
//! Vertex data structure used in a triangular manifold mesh (TMM).
class TMMVertex
{
public:
void Initialize();
TMMVertex(void);
~TMMVertex(void);
private:
Vec3<double> m_pos;
int m_name;
size_t m_id;
CircularListElement<TMMEdge>* m_duplicate; // pointer to incident cone edge (or NULL)
bool m_onHull;
bool m_tag;
TMMVertex(const TMMVertex& rhs);
friend class ICHull;
friend class TMMesh;
friend class TMMTriangle;
friend class TMMEdge;
};
//! Edge data structure used in a triangular manifold mesh (TMM).
class TMMEdge
{
public:
void Initialize();
TMMEdge(void);
~TMMEdge(void);
private:
size_t m_id;
CircularListElement<TMMTriangle>* m_triangles[2];
CircularListElement<TMMVertex>* m_vertices[2];
CircularListElement<TMMTriangle>* m_newFace;
TMMEdge(const TMMEdge& rhs);
friend class ICHull;
friend class TMMTriangle;
friend class TMMVertex;
friend class TMMesh;
};
//! Triangle data structure used in a triangular manifold mesh (TMM).
class TMMTriangle
{
public:
void Initialize();
TMMTriangle(void);
~TMMTriangle(void);
private:
size_t m_id;
CircularListElement<TMMEdge>* m_edges[3];
CircularListElement<TMMVertex>* m_vertices[3];
bool m_visible;
TMMTriangle(const TMMTriangle& rhs);
friend class ICHull;
friend class TMMesh;
friend class TMMVertex;
friend class TMMEdge;
};
//! triangular manifold mesh data structure.
class TMMesh
{
public:
//! Returns the number of vertices>
inline size_t GetNVertices() const { return m_vertices.GetSize(); }
//! Returns the number of edges
inline size_t GetNEdges() const { return m_edges.GetSize(); }
//! Returns the number of triangles
inline size_t GetNTriangles() const { return m_triangles.GetSize(); }
//! Returns the vertices circular list
inline const CircularList<TMMVertex>& GetVertices() const { return m_vertices; }
//! Returns the edges circular list
inline const CircularList<TMMEdge>& GetEdges() const { return m_edges; }
//! Returns the triangles circular list
inline const CircularList<TMMTriangle>& GetTriangles() const { return m_triangles; }
//! Returns the vertices circular list
inline CircularList<TMMVertex>& GetVertices() { return m_vertices; }
//! Returns the edges circular list
inline CircularList<TMMEdge>& GetEdges() { return m_edges; }
//! Returns the triangles circular list
inline CircularList<TMMTriangle>& GetTriangles() { return m_triangles; }
//! Add vertex to the mesh
CircularListElement<TMMVertex>* AddVertex() { return m_vertices.Add(); }
//! Add vertex to the mesh
CircularListElement<TMMEdge>* AddEdge() { return m_edges.Add(); }
//! Add vertex to the mesh
CircularListElement<TMMTriangle>* AddTriangle() { return m_triangles.Add(); }
//! Print mesh information
void Print();
//!
void GetIFS(Vec3<double>* const points, Vec3<int>* const triangles);
//!
void Clear();
//!
void Copy(TMMesh& mesh);
//!
bool CheckConsistancy();
//!
bool Normalize();
//!
bool Denormalize();
//! Constructor
TMMesh();
//! Destructor
virtual ~TMMesh(void);
private:
CircularList<TMMVertex> m_vertices;
CircularList<TMMEdge> m_edges;
CircularList<TMMTriangle> m_triangles;
// not defined
TMMesh(const TMMesh& rhs);
friend class ICHull;
};
} // namespace VHACD
#endif // VHACD_MANIFOLD_MESH_H

View file

@ -0,0 +1,133 @@
/* Copyright (c) 2011 Khaled Mamou (kmamou at gmail dot com)
All rights reserved.
Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
3. The names of the contributors may not be used to endorse or promote products derived from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#pragma once
#ifndef VHACD_MESH_H
#define VHACD_MESH_H
#include "vhacdSArray.h"
#include "vhacdVector.h"
#define VHACD_DEBUG_MESH
namespace VHACD
{
enum AXIS
{
AXIS_X = 0,
AXIS_Y = 1,
AXIS_Z = 2
};
struct Plane
{
double m_a;
double m_b;
double m_c;
double m_d;
AXIS m_axis;
short m_index;
};
#ifdef VHACD_DEBUG_MESH
struct Material
{
Vec3<double> m_diffuseColor;
double m_ambientIntensity;
Vec3<double> m_specularColor;
Vec3<double> m_emissiveColor;
double m_shininess;
double m_transparency;
Material(void)
{
m_diffuseColor.X() = 0.5;
m_diffuseColor.Y() = 0.5;
m_diffuseColor.Z() = 0.5;
m_specularColor.X() = 0.5;
m_specularColor.Y() = 0.5;
m_specularColor.Z() = 0.5;
m_ambientIntensity = 0.4;
m_emissiveColor.X() = 0.0;
m_emissiveColor.Y() = 0.0;
m_emissiveColor.Z() = 0.0;
m_shininess = 0.4;
m_transparency = 0.0;
};
};
#endif // VHACD_DEBUG_MESH
//! Triangular mesh data structure
class Mesh
{
public:
void AddPoint(const Vec3<double>& pt) { m_points.PushBack(pt); };
void SetPoint(size_t index, const Vec3<double>& pt) { m_points[index] = pt; };
const Vec3<double>& GetPoint(size_t index) const { return m_points[index]; };
Vec3<double>& GetPoint(size_t index) { return m_points[index]; };
size_t GetNPoints() const { return m_points.Size(); };
double* GetPoints() { return (double*)m_points.Data(); } // ugly
const double* const GetPoints() const { return (double*)m_points.Data(); } // ugly
const Vec3<double>* const GetPointsBuffer() const { return m_points.Data(); } //
Vec3<double>* const GetPointsBuffer() { return m_points.Data(); } //
void AddTriangle(const Vec3<int>& tri) { m_triangles.PushBack(tri); };
void SetTriangle(size_t index, const Vec3<int>& tri) { m_triangles[index] = tri; };
const Vec3<int>& GetTriangle(size_t index) const { return m_triangles[index]; };
Vec3<int>& GetTriangle(size_t index) { return m_triangles[index]; };
size_t GetNTriangles() const { return m_triangles.Size(); };
int* GetTriangles() { return (int*)m_triangles.Data(); } // ugly
const int* const GetTriangles() const { return (int*)m_triangles.Data(); } // ugly
const Vec3<int>* const GetTrianglesBuffer() const { return m_triangles.Data(); }
Vec3<int>* const GetTrianglesBuffer() { return m_triangles.Data(); }
const Vec3<double>& GetCenter() const { return m_center; }
const Vec3<double>& GetMinBB() const { return m_minBB; }
const Vec3<double>& GetMaxBB() const { return m_maxBB; }
void ClearPoints() { m_points.Clear(); }
void ClearTriangles() { m_triangles.Clear(); }
void Clear()
{
ClearPoints();
ClearTriangles();
}
void ResizePoints(size_t nPts) { m_points.Resize(nPts); }
void ResizeTriangles(size_t nTri) { m_triangles.Resize(nTri); }
void CopyPoints(SArray<Vec3<double> >& points) const { points = m_points; }
double GetDiagBB() const { return m_diag; }
double ComputeVolume() const;
void ComputeConvexHull(const double* const pts,
const size_t nPts);
void Clip(const Plane& plane,
SArray<Vec3<double> >& positivePart,
SArray<Vec3<double> >& negativePart) const;
bool IsInside(const Vec3<double>& pt) const;
double ComputeDiagBB();
#ifdef VHACD_DEBUG_MESH
bool LoadOFF(const std::string& fileName, bool invert);
bool SaveVRML2(const std::string& fileName) const;
bool SaveVRML2(std::ofstream& fout, const Material& material) const;
bool SaveOFF(const std::string& fileName) const;
#endif // VHACD_DEBUG_MESH
//! Constructor.
Mesh();
//! Destructor.
~Mesh(void);
private:
SArray<Vec3<double> > m_points;
SArray<Vec3<int> > m_triangles;
Vec3<double> m_minBB;
Vec3<double> m_maxBB;
Vec3<double> m_center;
double m_diag;
};
} // namespace VHACD
#endif

View file

@ -0,0 +1,148 @@
/*!
**
** Copyright (c) 2009 by John W. Ratcliff mailto:jratcliffscarab@gmail.com
**
** Portions of this source has been released with the PhysXViewer application, as well as
** Rocket, CreateDynamics, ODF, and as a number of sample code snippets.
**
** If you find this code useful or you are feeling particularily generous I would
** ask that you please go to http://www.amillionpixels.us and make a donation
** to Troy DeMolay.
**
** DeMolay is a youth group for young men between the ages of 12 and 21.
** It teaches strong moral principles, as well as leadership skills and
** public speaking. The donations page uses the 'pay for pixels' paradigm
** where, in this case, a pixel is only a single penny. Donations can be
** made for as small as $4 or as high as a $100 block. Each person who donates
** will get a link to their own site as well as acknowledgement on the
** donations blog located here http://www.amillionpixels.blogspot.com/
**
** If you wish to contact me you can use the following methods:
**
** Skype ID: jratcliff63367
** Yahoo: jratcliff63367
** AOL: jratcliff1961
** email: jratcliffscarab@gmail.com
**
**
** The MIT license:
**
** Permission is hereby granted, free of charge, to any person obtaining a copy
** of this software and associated documentation files (the "Software"), to deal
** in the Software without restriction, including without limitation the rights
** to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
** copies of the Software, and to permit persons to whom the Software is furnished
** to do so, subject to the following conditions:
**
** The above copyright notice and this permission notice shall be included in all
** copies or substantial portions of the Software.
** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
** IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
** FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
** AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
** WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
** CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
#pragma once
#ifndef VHACD_MUTEX_H
#define VHACD_MUTEX_H
#if defined(WIN32)
//#define _WIN32_WINNT 0x400
#include <windows.h>
#pragma comment(lib, "winmm.lib")
#endif
#if defined(__linux__)
//#include <sys/time.h>
#include <errno.h>
#include <time.h>
#include <unistd.h>
#define __stdcall
#endif
#if defined(__APPLE__) || defined(__linux__)
#include <pthread.h>
#endif
#if defined(__APPLE__) || !defined(__GLIBC__)
#define PTHREAD_MUTEX_RECURSIVE_NP PTHREAD_MUTEX_RECURSIVE
#endif
#define VHACD_DEBUG
//#define VHACD_NDEBUG
#ifdef VHACD_NDEBUG
#define VHACD_VERIFY(x) (x)
#else
#define VHACD_VERIFY(x) assert((x))
#endif
namespace VHACD
{
class Mutex
{
public:
Mutex(void)
{
#if defined(WIN32) || defined(_XBOX)
InitializeCriticalSection(&m_mutex);
#elif defined(__APPLE__) || defined(__linux__)
pthread_mutexattr_t mutexAttr; // Mutex Attribute
VHACD_VERIFY(pthread_mutexattr_init(&mutexAttr) == 0);
VHACD_VERIFY(pthread_mutexattr_settype(&mutexAttr, PTHREAD_MUTEX_RECURSIVE_NP) == 0);
VHACD_VERIFY(pthread_mutex_init(&m_mutex, &mutexAttr) == 0);
VHACD_VERIFY(pthread_mutexattr_destroy(&mutexAttr) == 0);
#endif
}
~Mutex(void)
{
#if defined(WIN32) || defined(_XBOX)
DeleteCriticalSection(&m_mutex);
#elif defined(__APPLE__) || defined(__linux__)
VHACD_VERIFY(pthread_mutex_destroy(&m_mutex) == 0);
#endif
}
void Lock(void)
{
#if defined(WIN32) || defined(_XBOX)
EnterCriticalSection(&m_mutex);
#elif defined(__APPLE__) || defined(__linux__)
VHACD_VERIFY(pthread_mutex_lock(&m_mutex) == 0);
#endif
}
bool TryLock(void)
{
#if defined(WIN32) || defined(_XBOX)
bool bRet = false;
//assert(("TryEnterCriticalSection seems to not work on XP???", 0));
bRet = TryEnterCriticalSection(&m_mutex) ? true : false;
return bRet;
#elif defined(__APPLE__) || defined(__linux__)
int result = pthread_mutex_trylock(&m_mutex);
return (result == 0);
#endif
}
void Unlock(void)
{
#if defined(WIN32) || defined(_XBOX)
LeaveCriticalSection(&m_mutex);
#elif defined(__APPLE__) || defined(__linux__)
VHACD_VERIFY(pthread_mutex_unlock(&m_mutex) == 0);
#endif
}
private:
#if defined(WIN32) || defined(_XBOX)
CRITICAL_SECTION m_mutex;
#elif defined(__APPLE__) || defined(__linux__)
pthread_mutex_t m_mutex;
#endif
};
} // namespace VHACD
#endif // VHACD_MUTEX_H

View file

@ -0,0 +1,164 @@
/* Copyright (c) 2011 Khaled Mamou (kmamou at gmail dot com)
All rights reserved.
Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
3. The names of the contributors may not be used to endorse or promote products derived from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#pragma once
#ifndef VHACD_SARRAY_H
#define VHACD_SARRAY_H
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define SARRAY_DEFAULT_MIN_SIZE 16
namespace VHACD
{
//! SArray.
template <typename T, size_t N = 64>
class SArray
{
public:
T& operator[](size_t i)
{
T* const data = Data();
return data[i];
}
const T& operator[](size_t i) const
{
const T* const data = Data();
return data[i];
}
size_t Size() const
{
return m_size;
}
T* const Data()
{
return (m_maxSize == N) ? m_data0 : m_data;
}
const T* const Data() const
{
return (m_maxSize == N) ? m_data0 : m_data;
}
void Clear()
{
m_size = 0;
delete[] m_data;
m_data = 0;
m_maxSize = N;
}
void PopBack()
{
--m_size;
}
void Allocate(size_t size)
{
if (size > m_maxSize)
{
T* temp = new T[size];
memcpy(temp, Data(), m_size * sizeof(T));
delete[] m_data;
m_data = temp;
m_maxSize = size;
}
}
void Resize(size_t size)
{
Allocate(size);
m_size = size;
}
void PushBack(const T& value)
{
if (m_size == m_maxSize)
{
size_t maxSize = (m_maxSize << 1);
T* temp = new T[maxSize];
memcpy(temp, Data(), m_maxSize * sizeof(T));
delete[] m_data;
m_data = temp;
m_maxSize = maxSize;
}
T* const data = Data();
data[m_size++] = value;
}
bool Find(const T& value, size_t& pos)
{
T* const data = Data();
for (pos = 0; pos < m_size; ++pos)
if (value == data[pos])
return true;
return false;
}
bool Insert(const T& value)
{
size_t pos;
if (Find(value, pos))
return false;
PushBack(value);
return true;
}
bool Erase(const T& value)
{
size_t pos;
T* const data = Data();
if (Find(value, pos))
{
for (size_t j = pos + 1; j < m_size; ++j)
data[j - 1] = data[j];
--m_size;
return true;
}
return false;
}
void operator=(const SArray& rhs)
{
if (m_maxSize < rhs.m_size)
{
delete[] m_data;
m_maxSize = rhs.m_maxSize;
m_data = new T[m_maxSize];
}
m_size = rhs.m_size;
memcpy(Data(), rhs.Data(), m_size * sizeof(T));
}
void Initialize()
{
m_data = 0;
m_size = 0;
m_maxSize = N;
}
SArray(const SArray& rhs)
{
m_data = 0;
m_size = 0;
m_maxSize = N;
*this = rhs;
}
SArray()
{
Initialize();
}
~SArray()
{
delete[] m_data;
}
private:
T m_data0[N];
T* m_data;
size_t m_size;
size_t m_maxSize;
};
} // namespace VHACD
#endif

View file

@ -0,0 +1,125 @@
/* Copyright (c) 2011 Khaled Mamou (kmamou at gmail dot com)
All rights reserved.
Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
3. The names of the contributors may not be used to endorse or promote products derived from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#pragma once
#ifndef VHACD_TIMER_H
#define VHACD_TIMER_H
#ifdef _WIN32
#ifndef WIN32_LEAN_AND_MEAN
#define WIN32_LEAN_AND_MEAN // Exclude rarely-used stuff from Windows headers
#endif
#include <windows.h>
#elif __MACH__
#include <mach/clock.h>
#include <mach/mach.h>
#else
#include <sys/time.h>
#include <time.h>
#endif
namespace VHACD
{
#ifdef _WIN32
class Timer
{
public:
Timer(void)
{
m_start.QuadPart = 0;
m_stop.QuadPart = 0;
QueryPerformanceFrequency(&m_freq);
};
~Timer(void){};
void Tic()
{
QueryPerformanceCounter(&m_start);
}
void Toc()
{
QueryPerformanceCounter(&m_stop);
}
double GetElapsedTime() // in ms
{
LARGE_INTEGER delta;
delta.QuadPart = m_stop.QuadPart - m_start.QuadPart;
return (1000.0 * delta.QuadPart) / (double)m_freq.QuadPart;
}
private:
LARGE_INTEGER m_start;
LARGE_INTEGER m_stop;
LARGE_INTEGER m_freq;
};
#elif __MACH__
class Timer
{
public:
Timer(void)
{
memset(this, 0, sizeof(Timer));
host_get_clock_service(mach_host_self(), CALENDAR_CLOCK, &m_cclock);
};
~Timer(void)
{
mach_port_deallocate(mach_task_self(), m_cclock);
};
void Tic()
{
clock_get_time(m_cclock, &m_start);
}
void Toc()
{
clock_get_time(m_cclock, &m_stop);
}
double GetElapsedTime() // in ms
{
return 1000.0 * (m_stop.tv_sec - m_start.tv_sec + (1.0E-9) * (m_stop.tv_nsec - m_start.tv_nsec));
}
private:
clock_serv_t m_cclock;
mach_timespec_t m_start;
mach_timespec_t m_stop;
};
#else
class Timer
{
public:
Timer(void)
{
memset(this, 0, sizeof(Timer));
};
~Timer(void){};
void Tic()
{
clock_gettime(CLOCK_REALTIME, &m_start);
}
void Toc()
{
clock_gettime(CLOCK_REALTIME, &m_stop);
}
double GetElapsedTime() // in ms
{
return 1000.0 * (m_stop.tv_sec - m_start.tv_sec + (1.0E-9) * (m_stop.tv_nsec - m_start.tv_nsec));
}
private:
struct timespec m_start;
struct timespec m_stop;
};
#endif
} // namespace VHACD
#endif // VHACD_TIMER_H

View file

@ -0,0 +1,369 @@
/* Copyright (c) 2011 Khaled Mamou (kmamou at gmail dot com)
All rights reserved.
Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
3. The names of the contributors may not be used to endorse or promote products derived from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#pragma once
#ifndef VHACD_VHACD_H
#define VHACD_VHACD_H
#ifdef OPENCL_FOUND
#ifdef __MACH__
#include <OpenCL/cl.h>
#else
#include <CL/cl.h>
#endif
#endif //OPENCL_FOUND
#include "vhacdMutex.h"
#include "vhacdVolume.h"
#define USE_THREAD 1
#define OCL_MIN_NUM_PRIMITIVES 4096
#define CH_APP_MIN_NUM_PRIMITIVES 64000
namespace VHACD
{
class VHACD : public IVHACD
{
public:
//! Constructor.
VHACD()
{
#if USE_THREAD == 1 && _OPENMP
m_ompNumProcessors = 2 * omp_get_num_procs();
omp_set_num_threads(m_ompNumProcessors);
#else //USE_THREAD == 1 && _OPENMP
m_ompNumProcessors = 1;
#endif //USE_THREAD == 1 && _OPENMP
#ifdef CL_VERSION_1_1
m_oclWorkGroupSize = 0;
m_oclDevice = 0;
m_oclQueue = 0;
m_oclKernelComputePartialVolumes = 0;
m_oclKernelComputeSum = 0;
#endif //CL_VERSION_1_1
Init();
}
//! Destructor.
~VHACD(void) {}
unsigned int GetNConvexHulls() const
{
return (unsigned int)m_convexHulls.Size();
}
void Cancel()
{
SetCancel(true);
}
void GetConvexHull(const unsigned int index, ConvexHull& ch) const
{
Mesh* mesh = m_convexHulls[index];
ch.m_nPoints = (unsigned int)mesh->GetNPoints();
ch.m_nTriangles = (unsigned int)mesh->GetNTriangles();
ch.m_points = mesh->GetPoints();
ch.m_triangles = mesh->GetTriangles();
}
void Clean(void)
{
delete m_volume;
delete m_pset;
size_t nCH = m_convexHulls.Size();
for (size_t p = 0; p < nCH; ++p)
{
delete m_convexHulls[p];
}
m_convexHulls.Clear();
Init();
}
void Release(void)
{
delete this;
}
bool Compute(const float* const points,
const unsigned int stridePoints,
const unsigned int nPoints,
const int* const triangles,
const unsigned int strideTriangles,
const unsigned int nTriangles,
const Parameters& params);
bool Compute(const double* const points,
const unsigned int stridePoints,
const unsigned int nPoints,
const int* const triangles,
const unsigned int strideTriangles,
const unsigned int nTriangles,
const Parameters& params);
bool OCLInit(void* const oclDevice,
IUserLogger* const logger = 0);
bool OCLRelease(IUserLogger* const logger = 0);
private:
void SetCancel(bool cancel)
{
m_cancelMutex.Lock();
m_cancel = cancel;
m_cancelMutex.Unlock();
}
bool GetCancel()
{
m_cancelMutex.Lock();
bool cancel = m_cancel;
m_cancelMutex.Unlock();
return cancel;
}
void Update(const double stageProgress,
const double operationProgress,
const Parameters& params)
{
m_stageProgress = stageProgress;
m_operationProgress = operationProgress;
if (params.m_callback)
{
params.m_callback->Update(m_overallProgress,
m_stageProgress,
m_operationProgress,
m_stage.c_str(),
m_operation.c_str());
}
}
void Init()
{
memset(m_rot, 0, sizeof(double) * 9);
m_dim = 64;
m_volume = 0;
m_volumeCH0 = 0.0;
m_pset = 0;
m_overallProgress = 0.0;
m_stageProgress = 0.0;
m_operationProgress = 0.0;
m_stage = "";
m_operation = "";
m_barycenter[0] = m_barycenter[1] = m_barycenter[2] = 0.0;
m_rot[0][0] = m_rot[1][1] = m_rot[2][2] = 1.0;
SetCancel(false);
}
void ComputePrimitiveSet(const Parameters& params);
void ComputeACD(const Parameters& params);
void MergeConvexHulls(const Parameters& params);
void SimplifyConvexHulls(const Parameters& params);
void ComputeBestClippingPlane(const PrimitiveSet* inputPSet,
const double volume,
const SArray<Plane>& planes,
const Vec3<double>& preferredCuttingDirection,
const double w,
const double alpha,
const double beta,
const int convexhullDownsampling,
const double progress0,
const double progress1,
Plane& bestPlane,
double& minConcavity,
const Parameters& params);
template <class T>
void AlignMesh(const T* const points,
const unsigned int stridePoints,
const unsigned int nPoints,
const int* const triangles,
const unsigned int strideTriangles,
const unsigned int nTriangles,
const Parameters& params)
{
if (GetCancel() || !params.m_pca)
{
return;
}
m_timer.Tic();
m_stage = "Align mesh";
m_operation = "Voxelization";
std::ostringstream msg;
if (params.m_logger)
{
msg << "+ " << m_stage << std::endl;
params.m_logger->Log(msg.str().c_str());
}
Update(0.0, 0.0, params);
if (GetCancel())
{
return;
}
m_dim = (size_t)(pow((double)params.m_resolution, 1.0 / 3.0) + 0.5);
Volume volume;
volume.Voxelize(points, stridePoints, nPoints,
triangles, strideTriangles, nTriangles,
m_dim, m_barycenter, m_rot);
size_t n = volume.GetNPrimitivesOnSurf() + volume.GetNPrimitivesInsideSurf();
Update(50.0, 100.0, params);
if (params.m_logger)
{
msg.str("");
msg << "\t dim = " << m_dim << "\t-> " << n << " voxels" << std::endl;
params.m_logger->Log(msg.str().c_str());
}
if (GetCancel())
{
return;
}
m_operation = "PCA";
Update(50.0, 0.0, params);
volume.AlignToPrincipalAxes(m_rot);
m_overallProgress = 1.0;
Update(100.0, 100.0, params);
m_timer.Toc();
if (params.m_logger)
{
msg.str("");
msg << "\t time " << m_timer.GetElapsedTime() / 1000.0 << "s" << std::endl;
params.m_logger->Log(msg.str().c_str());
}
}
template <class T>
void VoxelizeMesh(const T* const points,
const unsigned int stridePoints,
const unsigned int nPoints,
const int* const triangles,
const unsigned int strideTriangles,
const unsigned int nTriangles,
const Parameters& params)
{
if (GetCancel())
{
return;
}
m_timer.Tic();
m_stage = "Voxelization";
std::ostringstream msg;
if (params.m_logger)
{
msg << "+ " << m_stage << std::endl;
params.m_logger->Log(msg.str().c_str());
}
delete m_volume;
m_volume = 0;
int iteration = 0;
const int maxIteration = 5;
double progress = 0.0;
while (iteration++ < maxIteration && !m_cancel)
{
msg.str("");
msg << "Iteration " << iteration;
m_operation = msg.str();
progress = iteration * 100.0 / maxIteration;
Update(progress, 0.0, params);
m_volume = new Volume;
m_volume->Voxelize(points, stridePoints, nPoints,
triangles, strideTriangles, nTriangles,
m_dim, m_barycenter, m_rot);
Update(progress, 100.0, params);
size_t n = m_volume->GetNPrimitivesOnSurf() + m_volume->GetNPrimitivesInsideSurf();
if (params.m_logger)
{
msg.str("");
msg << "\t dim = " << m_dim << "\t-> " << n << " voxels" << std::endl;
params.m_logger->Log(msg.str().c_str());
}
double a = pow((double)(params.m_resolution) / n, 0.33);
size_t dim_next = (size_t)(m_dim * a + 0.5);
if (n < params.m_resolution && iteration < maxIteration && m_volume->GetNPrimitivesOnSurf() < params.m_resolution / 8 && m_dim != dim_next)
{
delete m_volume;
m_volume = 0;
m_dim = dim_next;
}
else
{
break;
}
}
m_overallProgress = 10.0;
Update(100.0, 100.0, params);
m_timer.Toc();
if (params.m_logger)
{
msg.str("");
msg << "\t time " << m_timer.GetElapsedTime() / 1000.0 << "s" << std::endl;
params.m_logger->Log(msg.str().c_str());
}
}
template <class T>
bool ComputeACD(const T* const points,
const unsigned int stridePoints,
const unsigned int nPoints,
const int* const triangles,
const unsigned int strideTriangles,
const unsigned int nTriangles,
const Parameters& params)
{
Init();
if (params.m_oclAcceleration)
{
// build kernals
}
AlignMesh(points, stridePoints, nPoints, triangles, strideTriangles, nTriangles, params);
VoxelizeMesh(points, stridePoints, nPoints, triangles, strideTriangles, nTriangles, params);
ComputePrimitiveSet(params);
ComputeACD(params);
MergeConvexHulls(params);
SimplifyConvexHulls(params);
if (params.m_oclAcceleration)
{
// Release kernals
}
if (GetCancel())
{
Clean();
return false;
}
return true;
}
private:
SArray<Mesh*> m_convexHulls;
std::string m_stage;
std::string m_operation;
double m_overallProgress;
double m_stageProgress;
double m_operationProgress;
double m_rot[3][3];
double m_volumeCH0;
Vec3<double> m_barycenter;
Timer m_timer;
size_t m_dim;
Volume* m_volume;
PrimitiveSet* m_pset;
Mutex m_cancelMutex;
bool m_cancel;
int m_ompNumProcessors;
#ifdef CL_VERSION_1_1
cl_device_id* m_oclDevice;
cl_context m_oclContext;
cl_program m_oclProgram;
cl_command_queue* m_oclQueue;
cl_kernel* m_oclKernelComputePartialVolumes;
cl_kernel* m_oclKernelComputeSum;
size_t m_oclWorkGroupSize;
#endif //CL_VERSION_1_1
};
} // namespace VHACD
#endif // VHACD_VHACD_H

View file

@ -0,0 +1,106 @@
/* Copyright (c) 2011 Khaled Mamou (kmamou at gmail dot com)
All rights reserved.
Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
3. The names of the contributors may not be used to endorse or promote products derived from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#pragma once
#ifndef VHACD_VECTOR_H
#define VHACD_VECTOR_H
#include <iostream>
#include <math.h>
namespace VHACD
{
//! Vector dim 3.
template <typename T>
class Vec3
{
public:
T& operator[](size_t i) { return m_data[i]; }
const T& operator[](size_t i) const { return m_data[i]; }
T& X();
T& Y();
T& Z();
const T& X() const;
const T& Y() const;
const T& Z() const;
void Normalize();
T GetNorm() const;
void operator=(const Vec3& rhs);
void operator+=(const Vec3& rhs);
void operator-=(const Vec3& rhs);
void operator-=(T a);
void operator+=(T a);
void operator/=(T a);
void operator*=(T a);
Vec3 operator^(const Vec3& rhs) const;
T operator*(const Vec3& rhs) const;
Vec3 operator+(const Vec3& rhs) const;
Vec3 operator-(const Vec3& rhs) const;
Vec3 operator-() const;
Vec3 operator*(T rhs) const;
Vec3 operator/(T rhs) const;
bool operator<(const Vec3& rhs) const;
bool operator>(const Vec3& rhs) const;
Vec3();
Vec3(T a);
Vec3(T x, T y, T z);
Vec3(const Vec3& rhs);
/*virtual*/ ~Vec3(void);
private:
T m_data[3];
};
//! Vector dim 2.
template <typename T>
class Vec2
{
public:
T& operator[](size_t i) { return m_data[i]; }
const T& operator[](size_t i) const { return m_data[i]; }
T& X();
T& Y();
const T& X() const;
const T& Y() const;
void Normalize();
T GetNorm() const;
void operator=(const Vec2& rhs);
void operator+=(const Vec2& rhs);
void operator-=(const Vec2& rhs);
void operator-=(T a);
void operator+=(T a);
void operator/=(T a);
void operator*=(T a);
T operator^(const Vec2& rhs) const;
T operator*(const Vec2& rhs) const;
Vec2 operator+(const Vec2& rhs) const;
Vec2 operator-(const Vec2& rhs) const;
Vec2 operator-() const;
Vec2 operator*(T rhs) const;
Vec2 operator/(T rhs) const;
Vec2();
Vec2(T a);
Vec2(T x, T y);
Vec2(const Vec2& rhs);
/*virtual*/ ~Vec2(void);
private:
T m_data[2];
};
template <typename T>
const bool Colinear(const Vec3<T>& a, const Vec3<T>& b, const Vec3<T>& c);
template <typename T>
const T ComputeVolume4(const Vec3<T>& a, const Vec3<T>& b, const Vec3<T>& c, const Vec3<T>& d);
} // namespace VHACD
#include "vhacdVector.inl" // template implementation
#endif

View file

@ -0,0 +1,362 @@
#pragma once
#ifndef VHACD_VECTOR_INL
#define VHACD_VECTOR_INL
namespace VHACD
{
template <typename T>
inline Vec3<T> operator*(T lhs, const Vec3<T> & rhs)
{
return Vec3<T>(lhs * rhs.X(), lhs * rhs.Y(), lhs * rhs.Z());
}
template <typename T>
inline T & Vec3<T>::X()
{
return m_data[0];
}
template <typename T>
inline T & Vec3<T>::Y()
{
return m_data[1];
}
template <typename T>
inline T & Vec3<T>::Z()
{
return m_data[2];
}
template <typename T>
inline const T & Vec3<T>::X() const
{
return m_data[0];
}
template <typename T>
inline const T & Vec3<T>::Y() const
{
return m_data[1];
}
template <typename T>
inline const T & Vec3<T>::Z() const
{
return m_data[2];
}
template <typename T>
inline void Vec3<T>::Normalize()
{
T n = sqrt(m_data[0]*m_data[0]+m_data[1]*m_data[1]+m_data[2]*m_data[2]);
if (n != 0.0) (*this) /= n;
}
template <typename T>
inline T Vec3<T>::GetNorm() const
{
return sqrt(m_data[0]*m_data[0]+m_data[1]*m_data[1]+m_data[2]*m_data[2]);
}
template <typename T>
inline void Vec3<T>::operator= (const Vec3 & rhs)
{
this->m_data[0] = rhs.m_data[0];
this->m_data[1] = rhs.m_data[1];
this->m_data[2] = rhs.m_data[2];
}
template <typename T>
inline void Vec3<T>::operator+=(const Vec3 & rhs)
{
this->m_data[0] += rhs.m_data[0];
this->m_data[1] += rhs.m_data[1];
this->m_data[2] += rhs.m_data[2];
}
template <typename T>
inline void Vec3<T>::operator-=(const Vec3 & rhs)
{
this->m_data[0] -= rhs.m_data[0];
this->m_data[1] -= rhs.m_data[1];
this->m_data[2] -= rhs.m_data[2];
}
template <typename T>
inline void Vec3<T>::operator-=(T a)
{
this->m_data[0] -= a;
this->m_data[1] -= a;
this->m_data[2] -= a;
}
template <typename T>
inline void Vec3<T>::operator+=(T a)
{
this->m_data[0] += a;
this->m_data[1] += a;
this->m_data[2] += a;
}
template <typename T>
inline void Vec3<T>::operator/=(T a)
{
this->m_data[0] /= a;
this->m_data[1] /= a;
this->m_data[2] /= a;
}
template <typename T>
inline void Vec3<T>::operator*=(T a)
{
this->m_data[0] *= a;
this->m_data[1] *= a;
this->m_data[2] *= a;
}
template <typename T>
inline Vec3<T> Vec3<T>::operator^ (const Vec3<T> & rhs) const
{
return Vec3<T>(m_data[1] * rhs.m_data[2] - m_data[2] * rhs.m_data[1],
m_data[2] * rhs.m_data[0] - m_data[0] * rhs.m_data[2],
m_data[0] * rhs.m_data[1] - m_data[1] * rhs.m_data[0]);
}
template <typename T>
inline T Vec3<T>::operator*(const Vec3<T> & rhs) const
{
return (m_data[0] * rhs.m_data[0] + m_data[1] * rhs.m_data[1] + m_data[2] * rhs.m_data[2]);
}
template <typename T>
inline Vec3<T> Vec3<T>::operator+(const Vec3<T> & rhs) const
{
return Vec3<T>(m_data[0] + rhs.m_data[0],m_data[1] + rhs.m_data[1],m_data[2] + rhs.m_data[2]);
}
template <typename T>
inline Vec3<T> Vec3<T>::operator-(const Vec3<T> & rhs) const
{
return Vec3<T>(m_data[0] - rhs.m_data[0],m_data[1] - rhs.m_data[1],m_data[2] - rhs.m_data[2]) ;
}
template <typename T>
inline Vec3<T> Vec3<T>::operator-() const
{
return Vec3<T>(-m_data[0],-m_data[1],-m_data[2]) ;
}
template <typename T>
inline Vec3<T> Vec3<T>::operator*(T rhs) const
{
return Vec3<T>(rhs * this->m_data[0], rhs * this->m_data[1], rhs * this->m_data[2]);
}
template <typename T>
inline Vec3<T> Vec3<T>::operator/ (T rhs) const
{
return Vec3<T>(m_data[0] / rhs, m_data[1] / rhs, m_data[2] / rhs);
}
template <typename T>
inline Vec3<T>::Vec3(T a)
{
m_data[0] = m_data[1] = m_data[2] = a;
}
template <typename T>
inline Vec3<T>::Vec3(T x, T y, T z)
{
m_data[0] = x;
m_data[1] = y;
m_data[2] = z;
}
template <typename T>
inline Vec3<T>::Vec3(const Vec3 & rhs)
{
m_data[0] = rhs.m_data[0];
m_data[1] = rhs.m_data[1];
m_data[2] = rhs.m_data[2];
}
template <typename T>
inline Vec3<T>::~Vec3(void){};
template <typename T>
inline Vec3<T>::Vec3() {}
template<typename T>
inline const bool Colinear(const Vec3<T> & a, const Vec3<T> & b, const Vec3<T> & c)
{
return ((c.Z() - a.Z()) * (b.Y() - a.Y()) - (b.Z() - a.Z()) * (c.Y() - a.Y()) == 0.0 /*EPS*/) &&
((b.Z() - a.Z()) * (c.X() - a.X()) - (b.X() - a.X()) * (c.Z() - a.Z()) == 0.0 /*EPS*/) &&
((b.X() - a.X()) * (c.Y() - a.Y()) - (b.Y() - a.Y()) * (c.X() - a.X()) == 0.0 /*EPS*/);
}
template<typename T>
inline const T ComputeVolume4(const Vec3<T> & a, const Vec3<T> & b, const Vec3<T> & c, const Vec3<T> & d)
{
return (a-d) * ((b-d) ^ (c-d));
}
template <typename T>
inline bool Vec3<T>::operator<(const Vec3 & rhs) const
{
if (X() == rhs[0])
{
if (Y() == rhs[1])
{
return (Z()<rhs[2]);
}
return (Y()<rhs[1]);
}
return (X()<rhs[0]);
}
template <typename T>
inline bool Vec3<T>::operator>(const Vec3 & rhs) const
{
if (X() == rhs[0])
{
if (Y() == rhs[1])
{
return (Z()>rhs[2]);
}
return (Y()>rhs[1]);
}
return (X()>rhs[0]);
}
template <typename T>
inline Vec2<T> operator*(T lhs, const Vec2<T> & rhs)
{
return Vec2<T>(lhs * rhs.X(), lhs * rhs.Y());
}
template <typename T>
inline T & Vec2<T>::X()
{
return m_data[0];
}
template <typename T>
inline T & Vec2<T>::Y()
{
return m_data[1];
}
template <typename T>
inline const T & Vec2<T>::X() const
{
return m_data[0];
}
template <typename T>
inline const T & Vec2<T>::Y() const
{
return m_data[1];
}
template <typename T>
inline void Vec2<T>::Normalize()
{
T n = sqrt(m_data[0]*m_data[0]+m_data[1]*m_data[1]);
if (n != 0.0) (*this) /= n;
}
template <typename T>
inline T Vec2<T>::GetNorm() const
{
return sqrt(m_data[0]*m_data[0]+m_data[1]*m_data[1]);
}
template <typename T>
inline void Vec2<T>::operator= (const Vec2 & rhs)
{
this->m_data[0] = rhs.m_data[0];
this->m_data[1] = rhs.m_data[1];
}
template <typename T>
inline void Vec2<T>::operator+=(const Vec2 & rhs)
{
this->m_data[0] += rhs.m_data[0];
this->m_data[1] += rhs.m_data[1];
}
template <typename T>
inline void Vec2<T>::operator-=(const Vec2 & rhs)
{
this->m_data[0] -= rhs.m_data[0];
this->m_data[1] -= rhs.m_data[1];
}
template <typename T>
inline void Vec2<T>::operator-=(T a)
{
this->m_data[0] -= a;
this->m_data[1] -= a;
}
template <typename T>
inline void Vec2<T>::operator+=(T a)
{
this->m_data[0] += a;
this->m_data[1] += a;
}
template <typename T>
inline void Vec2<T>::operator/=(T a)
{
this->m_data[0] /= a;
this->m_data[1] /= a;
}
template <typename T>
inline void Vec2<T>::operator*=(T a)
{
this->m_data[0] *= a;
this->m_data[1] *= a;
}
template <typename T>
inline T Vec2<T>::operator^ (const Vec2<T> & rhs) const
{
return m_data[0] * rhs.m_data[1] - m_data[1] * rhs.m_data[0];
}
template <typename T>
inline T Vec2<T>::operator*(const Vec2<T> & rhs) const
{
return (m_data[0] * rhs.m_data[0] + m_data[1] * rhs.m_data[1]);
}
template <typename T>
inline Vec2<T> Vec2<T>::operator+(const Vec2<T> & rhs) const
{
return Vec2<T>(m_data[0] + rhs.m_data[0],m_data[1] + rhs.m_data[1]);
}
template <typename T>
inline Vec2<T> Vec2<T>::operator-(const Vec2<T> & rhs) const
{
return Vec2<T>(m_data[0] - rhs.m_data[0],m_data[1] - rhs.m_data[1]);
}
template <typename T>
inline Vec2<T> Vec2<T>::operator-() const
{
return Vec2<T>(-m_data[0],-m_data[1]) ;
}
template <typename T>
inline Vec2<T> Vec2<T>::operator*(T rhs) const
{
return Vec2<T>(rhs * this->m_data[0], rhs * this->m_data[1]);
}
template <typename T>
inline Vec2<T> Vec2<T>::operator/ (T rhs) const
{
return Vec2<T>(m_data[0] / rhs, m_data[1] / rhs);
}
template <typename T>
inline Vec2<T>::Vec2(T a)
{
m_data[0] = m_data[1] = a;
}
template <typename T>
inline Vec2<T>::Vec2(T x, T y)
{
m_data[0] = x;
m_data[1] = y;
}
template <typename T>
inline Vec2<T>::Vec2(const Vec2 & rhs)
{
m_data[0] = rhs.m_data[0];
m_data[1] = rhs.m_data[1];
}
template <typename T>
inline Vec2<T>::~Vec2(void){};
template <typename T>
inline Vec2<T>::Vec2() {}
/*
InsideTriangle decides if a point P is Inside of the triangle
defined by A, B, C.
*/
template<typename T>
inline const bool InsideTriangle(const Vec2<T> & a, const Vec2<T> & b, const Vec2<T> & c, const Vec2<T> & p)
{
T ax, ay, bx, by, cx, cy, apx, apy, bpx, bpy, cpx, cpy;
T cCROSSap, bCROSScp, aCROSSbp;
ax = c.X() - b.X(); ay = c.Y() - b.Y();
bx = a.X() - c.X(); by = a.Y() - c.Y();
cx = b.X() - a.X(); cy = b.Y() - a.Y();
apx= p.X() - a.X(); apy= p.Y() - a.Y();
bpx= p.X() - b.X(); bpy= p.Y() - b.Y();
cpx= p.X() - c.X(); cpy= p.Y() - c.Y();
aCROSSbp = ax*bpy - ay*bpx;
cCROSSap = cx*apy - cy*apx;
bCROSScp = bx*cpy - by*cpx;
return ((aCROSSbp >= 0.0) && (bCROSScp >= 0.0) && (cCROSSap >= 0.0));
}
}
#endif //VHACD_VECTOR_INL

View file

@ -0,0 +1,441 @@
/* Copyright (c) 2011 Khaled Mamou (kmamou at gmail dot com)
All rights reserved.
Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
3. The names of the contributors may not be used to endorse or promote products derived from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#pragma once
#ifndef VHACD_VOLUME_H
#define VHACD_VOLUME_H
#include "vhacdMesh.h"
#include "vhacdVector.h"
#include <assert.h>
namespace VHACD
{
enum VOXEL_VALUE
{
PRIMITIVE_UNDEFINED = 0,
PRIMITIVE_OUTSIDE_SURFACE = 1,
PRIMITIVE_INSIDE_SURFACE = 2,
PRIMITIVE_ON_SURFACE = 3
};
struct Voxel
{
public:
short m_coord[3];
short m_data;
};
class PrimitiveSet
{
public:
virtual ~PrimitiveSet(){};
virtual PrimitiveSet* Create() const = 0;
virtual const size_t GetNPrimitives() const = 0;
virtual const size_t GetNPrimitivesOnSurf() const = 0;
virtual const size_t GetNPrimitivesInsideSurf() const = 0;
virtual const double GetEigenValue(AXIS axis) const = 0;
virtual const double ComputeMaxVolumeError() const = 0;
virtual const double ComputeVolume() const = 0;
virtual void Clip(const Plane& plane, PrimitiveSet* const positivePart,
PrimitiveSet* const negativePart) const = 0;
virtual void Intersect(const Plane& plane, SArray<Vec3<double> >* const positivePts,
SArray<Vec3<double> >* const negativePts, const size_t sampling) const = 0;
virtual void ComputeExteriorPoints(const Plane& plane, const Mesh& mesh,
SArray<Vec3<double> >* const exteriorPts) const = 0;
virtual void ComputeClippedVolumes(const Plane& plane, double& positiveVolume,
double& negativeVolume) const = 0;
virtual void SelectOnSurface(PrimitiveSet* const onSurfP) const = 0;
virtual void ComputeConvexHull(Mesh& meshCH, const size_t sampling = 1) const = 0;
virtual void ComputeBB() = 0;
virtual void ComputePrincipalAxes() = 0;
virtual void AlignToPrincipalAxes() = 0;
virtual void RevertAlignToPrincipalAxes() = 0;
virtual void Convert(Mesh& mesh, const VOXEL_VALUE value) const = 0;
const Mesh& GetConvexHull() const { return m_convexHull; };
Mesh& GetConvexHull() { return m_convexHull; };
private:
Mesh m_convexHull;
};
//!
class VoxelSet : public PrimitiveSet
{
friend class Volume;
public:
//! Destructor.
~VoxelSet(void);
//! Constructor.
VoxelSet();
const size_t GetNPrimitives() const { return m_voxels.Size(); }
const size_t GetNPrimitivesOnSurf() const { return m_numVoxelsOnSurface; }
const size_t GetNPrimitivesInsideSurf() const { return m_numVoxelsInsideSurface; }
const double GetEigenValue(AXIS axis) const { return m_D[axis][axis]; }
const double ComputeVolume() const { return m_unitVolume * m_voxels.Size(); }
const double ComputeMaxVolumeError() const { return m_unitVolume * m_numVoxelsOnSurface; }
const Vec3<short>& GetMinBBVoxels() const { return m_minBBVoxels; }
const Vec3<short>& GetMaxBBVoxels() const { return m_maxBBVoxels; }
const Vec3<double>& GetMinBB() const { return m_minBB; }
const double& GetScale() const { return m_scale; }
const double& GetUnitVolume() const { return m_unitVolume; }
Vec3<double> GetPoint(Vec3<short> voxel) const
{
return Vec3<double>(voxel[0] * m_scale + m_minBB[0],
voxel[1] * m_scale + m_minBB[1],
voxel[2] * m_scale + m_minBB[2]);
}
Vec3<double> GetPoint(const Voxel& voxel) const
{
return Vec3<double>(voxel.m_coord[0] * m_scale + m_minBB[0],
voxel.m_coord[1] * m_scale + m_minBB[1],
voxel.m_coord[2] * m_scale + m_minBB[2]);
}
Vec3<double> GetPoint(Vec3<double> voxel) const
{
return Vec3<double>(voxel[0] * m_scale + m_minBB[0],
voxel[1] * m_scale + m_minBB[1],
voxel[2] * m_scale + m_minBB[2]);
}
void GetPoints(const Voxel& voxel, Vec3<double>* const pts) const;
void ComputeConvexHull(Mesh& meshCH, const size_t sampling = 1) const;
void Clip(const Plane& plane, PrimitiveSet* const positivePart, PrimitiveSet* const negativePart) const;
void Intersect(const Plane& plane, SArray<Vec3<double> >* const positivePts,
SArray<Vec3<double> >* const negativePts, const size_t sampling) const;
void ComputeExteriorPoints(const Plane& plane, const Mesh& mesh,
SArray<Vec3<double> >* const exteriorPts) const;
void ComputeClippedVolumes(const Plane& plane, double& positiveVolume, double& negativeVolume) const;
void SelectOnSurface(PrimitiveSet* const onSurfP) const;
void ComputeBB();
void Convert(Mesh& mesh, const VOXEL_VALUE value) const;
void ComputePrincipalAxes();
PrimitiveSet* Create() const
{
return new VoxelSet();
}
void AlignToPrincipalAxes(){};
void RevertAlignToPrincipalAxes(){};
Voxel* const GetVoxels() { return m_voxels.Data(); }
const Voxel* const GetVoxels() const { return m_voxels.Data(); }
private:
size_t m_numVoxelsOnSurface;
size_t m_numVoxelsInsideSurface;
Vec3<double> m_minBB;
double m_scale;
SArray<Voxel, 8> m_voxels;
double m_unitVolume;
Vec3<double> m_minBBPts;
Vec3<double> m_maxBBPts;
Vec3<short> m_minBBVoxels;
Vec3<short> m_maxBBVoxels;
Vec3<short> m_barycenter;
double m_Q[3][3];
double m_D[3][3];
Vec3<double> m_barycenterPCA;
};
struct Tetrahedron
{
public:
Vec3<double> m_pts[4];
unsigned char m_data;
};
//!
class TetrahedronSet : public PrimitiveSet
{
friend class Volume;
public:
//! Destructor.
~TetrahedronSet(void);
//! Constructor.
TetrahedronSet();
const size_t GetNPrimitives() const { return m_tetrahedra.Size(); }
const size_t GetNPrimitivesOnSurf() const { return m_numTetrahedraOnSurface; }
const size_t GetNPrimitivesInsideSurf() const { return m_numTetrahedraInsideSurface; }
const Vec3<double>& GetMinBB() const { return m_minBB; }
const Vec3<double>& GetMaxBB() const { return m_maxBB; }
const Vec3<double>& GetBarycenter() const { return m_barycenter; }
const double GetEigenValue(AXIS axis) const { return m_D[axis][axis]; }
const double GetSacle() const { return m_scale; }
const double ComputeVolume() const;
const double ComputeMaxVolumeError() const;
void ComputeConvexHull(Mesh& meshCH, const size_t sampling = 1) const;
void ComputePrincipalAxes();
void AlignToPrincipalAxes();
void RevertAlignToPrincipalAxes();
void Clip(const Plane& plane, PrimitiveSet* const positivePart, PrimitiveSet* const negativePart) const;
void Intersect(const Plane& plane, SArray<Vec3<double> >* const positivePts,
SArray<Vec3<double> >* const negativePts, const size_t sampling) const;
void ComputeExteriorPoints(const Plane& plane, const Mesh& mesh,
SArray<Vec3<double> >* const exteriorPts) const;
void ComputeClippedVolumes(const Plane& plane, double& positiveVolume, double& negativeVolume) const;
void SelectOnSurface(PrimitiveSet* const onSurfP) const;
void ComputeBB();
void Convert(Mesh& mesh, const VOXEL_VALUE value) const;
inline bool Add(Tetrahedron& tetrahedron);
PrimitiveSet* Create() const
{
return new TetrahedronSet();
}
static const double EPS;
private:
void AddClippedTetrahedra(const Vec3<double> (&pts)[10], const int nPts);
size_t m_numTetrahedraOnSurface;
size_t m_numTetrahedraInsideSurface;
double m_scale;
Vec3<double> m_minBB;
Vec3<double> m_maxBB;
Vec3<double> m_barycenter;
SArray<Tetrahedron, 8> m_tetrahedra;
double m_Q[3][3];
double m_D[3][3];
};
//!
class Volume
{
public:
//! Destructor.
~Volume(void);
//! Constructor.
Volume();
//! Voxelize
template <class T>
void Voxelize(const T* const points, const unsigned int stridePoints, const unsigned int nPoints,
const int* const triangles, const unsigned int strideTriangles, const unsigned int nTriangles,
const size_t dim, const Vec3<double>& barycenter, const double (&rot)[3][3]);
unsigned char& GetVoxel(const size_t i, const size_t j, const size_t k)
{
assert(i < m_dim[0] || i >= 0);
assert(j < m_dim[0] || j >= 0);
assert(k < m_dim[0] || k >= 0);
return m_data[i + j * m_dim[0] + k * m_dim[0] * m_dim[1]];
}
const unsigned char& GetVoxel(const size_t i, const size_t j, const size_t k) const
{
assert(i < m_dim[0] || i >= 0);
assert(j < m_dim[0] || j >= 0);
assert(k < m_dim[0] || k >= 0);
return m_data[i + j * m_dim[0] + k * m_dim[0] * m_dim[1]];
}
const size_t GetNPrimitivesOnSurf() const { return m_numVoxelsOnSurface; }
const size_t GetNPrimitivesInsideSurf() const { return m_numVoxelsInsideSurface; }
void Convert(Mesh& mesh, const VOXEL_VALUE value) const;
void Convert(VoxelSet& vset) const;
void Convert(TetrahedronSet& tset) const;
void AlignToPrincipalAxes(double (&rot)[3][3]) const;
private:
void FillOutsideSurface(const size_t i0, const size_t j0, const size_t k0, const size_t i1,
const size_t j1, const size_t k1);
void FillInsideSurface();
template <class T>
void ComputeBB(const T* const points, const unsigned int stridePoints, const unsigned int nPoints,
const Vec3<double>& barycenter, const double (&rot)[3][3]);
void Allocate();
void Free();
Vec3<double> m_minBB;
Vec3<double> m_maxBB;
double m_scale;
size_t m_dim[3]; //>! dim
size_t m_numVoxelsOnSurface;
size_t m_numVoxelsInsideSurface;
size_t m_numVoxelsOutsideSurface;
unsigned char* m_data;
};
int TriBoxOverlap(const Vec3<double>& boxcenter, const Vec3<double>& boxhalfsize, const Vec3<double>& triver0,
const Vec3<double>& triver1, const Vec3<double>& triver2);
template <class T>
inline void ComputeAlignedPoint(const T* const points, const unsigned int idx, const Vec3<double>& barycenter,
const double (&rot)[3][3], Vec3<double>& pt){};
template <>
inline void ComputeAlignedPoint<float>(const float* const points, const unsigned int idx, const Vec3<double>& barycenter, const double (&rot)[3][3], Vec3<double>& pt)
{
double x = points[idx + 0] - barycenter[0];
double y = points[idx + 1] - barycenter[1];
double z = points[idx + 2] - barycenter[2];
pt[0] = rot[0][0] * x + rot[1][0] * y + rot[2][0] * z;
pt[1] = rot[0][1] * x + rot[1][1] * y + rot[2][1] * z;
pt[2] = rot[0][2] * x + rot[1][2] * y + rot[2][2] * z;
}
template <>
inline void ComputeAlignedPoint<double>(const double* const points, const unsigned int idx, const Vec3<double>& barycenter, const double (&rot)[3][3], Vec3<double>& pt)
{
double x = points[idx + 0] - barycenter[0];
double y = points[idx + 1] - barycenter[1];
double z = points[idx + 2] - barycenter[2];
pt[0] = rot[0][0] * x + rot[1][0] * y + rot[2][0] * z;
pt[1] = rot[0][1] * x + rot[1][1] * y + rot[2][1] * z;
pt[2] = rot[0][2] * x + rot[1][2] * y + rot[2][2] * z;
}
template <class T>
void Volume::ComputeBB(const T* const points, const unsigned int stridePoints, const unsigned int nPoints,
const Vec3<double>& barycenter, const double (&rot)[3][3])
{
Vec3<double> pt;
ComputeAlignedPoint(points, 0, barycenter, rot, pt);
m_maxBB = pt;
m_minBB = pt;
for (unsigned int v = 1; v < nPoints; ++v)
{
ComputeAlignedPoint(points, v * stridePoints, barycenter, rot, pt);
for (int i = 0; i < 3; ++i)
{
if (pt[i] < m_minBB[i])
m_minBB[i] = pt[i];
else if (pt[i] > m_maxBB[i])
m_maxBB[i] = pt[i];
}
}
}
template <class T>
void Volume::Voxelize(const T* const points, const unsigned int stridePoints, const unsigned int nPoints,
const int* const triangles, const unsigned int strideTriangles, const unsigned int nTriangles,
const size_t dim, const Vec3<double>& barycenter, const double (&rot)[3][3])
{
if (nPoints == 0)
{
return;
}
ComputeBB(points, stridePoints, nPoints, barycenter, rot);
double d[3] = {m_maxBB[0] - m_minBB[0], m_maxBB[1] - m_minBB[1], m_maxBB[2] - m_minBB[2]};
double r;
if (d[0] > d[1] && d[0] > d[2])
{
r = d[0];
m_dim[0] = dim;
m_dim[1] = 2 + static_cast<size_t>(dim * d[1] / d[0]);
m_dim[2] = 2 + static_cast<size_t>(dim * d[2] / d[0]);
}
else if (d[1] > d[0] && d[1] > d[2])
{
r = d[1];
m_dim[1] = dim;
m_dim[0] = 2 + static_cast<size_t>(dim * d[0] / d[1]);
m_dim[2] = 2 + static_cast<size_t>(dim * d[2] / d[1]);
}
else
{
r = d[2];
m_dim[2] = dim;
m_dim[0] = 2 + static_cast<size_t>(dim * d[0] / d[2]);
m_dim[1] = 2 + static_cast<size_t>(dim * d[1] / d[2]);
}
m_scale = r / (dim - 1);
double invScale = (dim - 1) / r;
Allocate();
m_numVoxelsOnSurface = 0;
m_numVoxelsInsideSurface = 0;
m_numVoxelsOutsideSurface = 0;
Vec3<double> p[3];
size_t i, j, k;
size_t i0, j0, k0;
size_t i1, j1, k1;
Vec3<double> boxcenter;
Vec3<double> pt;
const Vec3<double> boxhalfsize(0.5, 0.5, 0.5);
for (size_t t = 0, ti = 0; t < nTriangles; ++t, ti += strideTriangles)
{
Vec3<int> tri(triangles[ti + 0],
triangles[ti + 1],
triangles[ti + 2]);
for (int c = 0; c < 3; ++c)
{
ComputeAlignedPoint(points, tri[c] * stridePoints, barycenter, rot, pt);
p[c][0] = (pt[0] - m_minBB[0]) * invScale;
p[c][1] = (pt[1] - m_minBB[1]) * invScale;
p[c][2] = (pt[2] - m_minBB[2]) * invScale;
i = static_cast<size_t>(p[c][0] + 0.5);
j = static_cast<size_t>(p[c][1] + 0.5);
k = static_cast<size_t>(p[c][2] + 0.5);
assert(i < m_dim[0] && i >= 0 && j < m_dim[1] && j >= 0 && k < m_dim[2] && k >= 0);
if (c == 0)
{
i0 = i1 = i;
j0 = j1 = j;
k0 = k1 = k;
}
else
{
if (i < i0)
i0 = i;
if (j < j0)
j0 = j;
if (k < k0)
k0 = k;
if (i > i1)
i1 = i;
if (j > j1)
j1 = j;
if (k > k1)
k1 = k;
}
}
if (i0 > 0)
--i0;
if (j0 > 0)
--j0;
if (k0 > 0)
--k0;
if (i1 < m_dim[0])
++i1;
if (j1 < m_dim[1])
++j1;
if (k1 < m_dim[2])
++k1;
for (size_t i = i0; i < i1; ++i)
{
boxcenter[0] = (double)i;
for (size_t j = j0; j < j1; ++j)
{
boxcenter[1] = (double)j;
for (size_t k = k0; k < k1; ++k)
{
boxcenter[2] = (double)k;
int res = TriBoxOverlap(boxcenter, boxhalfsize, p[0], p[1], p[2]);
unsigned char& value = GetVoxel(i, j, k);
if (res == 1 && value == PRIMITIVE_UNDEFINED)
{
value = PRIMITIVE_ON_SURFACE;
++m_numVoxelsOnSurface;
}
}
}
}
}
FillOutsideSurface(0, 0, 0, m_dim[0], m_dim[1], 1);
FillOutsideSurface(0, 0, m_dim[2] - 1, m_dim[0], m_dim[1], m_dim[2]);
FillOutsideSurface(0, 0, 0, m_dim[0], 1, m_dim[2]);
FillOutsideSurface(0, m_dim[1] - 1, 0, m_dim[0], m_dim[1], m_dim[2]);
FillOutsideSurface(0, 0, 0, 1, m_dim[1], m_dim[2]);
FillOutsideSurface(m_dim[0] - 1, 0, 0, m_dim[0], m_dim[1], m_dim[2]);
FillInsideSurface();
}
} // namespace VHACD
#endif // VHACD_VOLUME_H

View file

@ -0,0 +1,4 @@
include "src"
include "test/src"

View file

@ -0,0 +1,123 @@
/* Copyright (c) 2011 Khaled Mamou (kmamou at gmail dot com)
All rights reserved.
Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
3. The names of the contributors may not be used to endorse or promote products derived from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#pragma once
#ifndef VHACD_H
#define VHACD_H
#define VHACD_VERSION_MAJOR 2
#define VHACD_VERSION_MINOR 2
namespace VHACD
{
class IVHACD
{
public:
class IUserCallback
{
public:
virtual ~IUserCallback(){};
virtual void Update(const double overallProgress,
const double stageProgress,
const double operationProgress,
const char* const stage,
const char* const operation) = 0;
};
class IUserLogger
{
public:
virtual ~IUserLogger(){};
virtual void Log(const char* const msg) = 0;
};
class ConvexHull
{
public:
double* m_points;
int* m_triangles;
unsigned int m_nPoints;
unsigned int m_nTriangles;
};
class Parameters
{
public:
Parameters(void) { Init(); }
void Init(void)
{
m_resolution = 1000000;
m_depth = 20;
m_concavity = 0.001;
m_planeDownsampling = 4;
m_convexhullDownsampling = 4;
m_alpha = 0.05;
m_beta = 0.05;
m_gamma = 0.0005;
m_pca = 0;
m_mode = 0; // 0: voxel-based (recommended), 1: tetrahedron-based
m_maxNumVerticesPerCH = 64;
m_minVolumePerCH = 0.0001;
m_callback = 0;
m_logger = 0;
m_convexhullApproximation = true;
m_oclAcceleration = true;
}
double m_concavity;
double m_alpha;
double m_beta;
double m_gamma;
double m_minVolumePerCH;
IUserCallback* m_callback;
IUserLogger* m_logger;
unsigned int m_resolution;
unsigned int m_maxNumVerticesPerCH;
int m_depth;
int m_planeDownsampling;
int m_convexhullDownsampling;
int m_pca;
int m_mode;
int m_convexhullApproximation;
int m_oclAcceleration;
};
virtual void Cancel() = 0;
virtual bool Compute(const float* const points,
const unsigned int stridePoints,
const unsigned int countPoints,
const int* const triangles,
const unsigned int strideTriangles,
const unsigned int countTriangles,
const Parameters& params) = 0;
virtual bool Compute(const double* const points,
const unsigned int stridePoints,
const unsigned int countPoints,
const int* const triangles,
const unsigned int strideTriangles,
const unsigned int countTriangles,
const Parameters& params) = 0;
virtual unsigned int GetNConvexHulls() const = 0;
virtual void GetConvexHull(const unsigned int index, ConvexHull& ch) const = 0;
virtual void Clean(void) = 0; // release internally allocated memory
virtual void Release(void) = 0; // release IVHACD
virtual bool OCLInit(void* const oclDevice,
IUserLogger* const logger = 0) = 0;
virtual bool OCLRelease(IUserLogger* const logger = 0) = 0;
protected:
virtual ~IVHACD(void) {}
};
IVHACD* CreateVHACD(void);
} // namespace VHACD
#endif // VHACD_H

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,14 @@
project "vhacd"
kind "StaticLib"
if os.is("Linux") then
buildoptions{"-fPIC"}
end
includedirs {
"../inc","../public",
"../../../src"
}
files {
"*.cpp",
"*.h"
}

View file

@ -0,0 +1,824 @@
/* Copyright (c) 2011 Khaled Mamou (kmamou at gmail dot com)
All rights reserved.
Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
3. The names of the contributors may not be used to endorse or promote products derived from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include "vhacdICHull.h"
#include <limits>
namespace VHACD
{
const double ICHull::sc_eps = 1.0e-15;
const int ICHull::sc_dummyIndex = std::numeric_limits<int>::max();
ICHull::ICHull()
{
m_isFlat = false;
}
bool ICHull::AddPoints(const Vec3<double>* points, size_t nPoints)
{
if (!points)
{
return false;
}
CircularListElement<TMMVertex>* vertex = NULL;
for (size_t i = 0; i < nPoints; i++)
{
vertex = m_mesh.AddVertex();
vertex->GetData().m_pos.X() = points[i].X();
vertex->GetData().m_pos.Y() = points[i].Y();
vertex->GetData().m_pos.Z() = points[i].Z();
vertex->GetData().m_name = static_cast<int>(i);
}
return true;
}
bool ICHull::AddPoint(const Vec3<double>& point, int id)
{
if (AddPoints(&point, 1))
{
m_mesh.m_vertices.GetData().m_name = id;
return true;
}
return false;
}
ICHullError ICHull::Process()
{
unsigned int addedPoints = 0;
if (m_mesh.GetNVertices() < 3)
{
return ICHullErrorNotEnoughPoints;
}
if (m_mesh.GetNVertices() == 3)
{
m_isFlat = true;
CircularListElement<TMMTriangle>* t1 = m_mesh.AddTriangle();
CircularListElement<TMMTriangle>* t2 = m_mesh.AddTriangle();
CircularListElement<TMMVertex>* v0 = m_mesh.m_vertices.GetHead();
CircularListElement<TMMVertex>* v1 = v0->GetNext();
CircularListElement<TMMVertex>* v2 = v1->GetNext();
// Compute the normal to the plane
Vec3<double> p0 = v0->GetData().m_pos;
Vec3<double> p1 = v1->GetData().m_pos;
Vec3<double> p2 = v2->GetData().m_pos;
m_normal = (p1 - p0) ^ (p2 - p0);
m_normal.Normalize();
t1->GetData().m_vertices[0] = v0;
t1->GetData().m_vertices[1] = v1;
t1->GetData().m_vertices[2] = v2;
t2->GetData().m_vertices[0] = v1;
t2->GetData().m_vertices[1] = v2;
t2->GetData().m_vertices[2] = v2;
return ICHullErrorOK;
}
if (m_isFlat)
{
m_mesh.m_edges.Clear();
m_mesh.m_triangles.Clear();
m_isFlat = false;
}
if (m_mesh.GetNTriangles() == 0) // we have to create the first polyhedron
{
ICHullError res = DoubleTriangle();
if (res != ICHullErrorOK)
{
return res;
}
else
{
addedPoints += 3;
}
}
CircularList<TMMVertex>& vertices = m_mesh.GetVertices();
// go to the first added and not processed vertex
while (!(vertices.GetHead()->GetPrev()->GetData().m_tag))
{
vertices.Prev();
}
while (!vertices.GetData().m_tag) // not processed
{
vertices.GetData().m_tag = true;
if (ProcessPoint())
{
addedPoints++;
CleanUp(addedPoints);
vertices.Next();
if (!GetMesh().CheckConsistancy())
{
size_t nV = m_mesh.GetNVertices();
CircularList<TMMVertex>& vertices = m_mesh.GetVertices();
for (size_t v = 0; v < nV; ++v)
{
if (vertices.GetData().m_name == sc_dummyIndex)
{
vertices.Delete();
break;
}
vertices.Next();
}
return ICHullErrorInconsistent;
}
}
}
if (m_isFlat)
{
SArray<CircularListElement<TMMTriangle>*> trianglesToDuplicate;
size_t nT = m_mesh.GetNTriangles();
for (size_t f = 0; f < nT; f++)
{
TMMTriangle& currentTriangle = m_mesh.m_triangles.GetHead()->GetData();
if (currentTriangle.m_vertices[0]->GetData().m_name == sc_dummyIndex || currentTriangle.m_vertices[1]->GetData().m_name == sc_dummyIndex || currentTriangle.m_vertices[2]->GetData().m_name == sc_dummyIndex)
{
m_trianglesToDelete.PushBack(m_mesh.m_triangles.GetHead());
for (int k = 0; k < 3; k++)
{
for (int h = 0; h < 2; h++)
{
if (currentTriangle.m_edges[k]->GetData().m_triangles[h] == m_mesh.m_triangles.GetHead())
{
currentTriangle.m_edges[k]->GetData().m_triangles[h] = 0;
break;
}
}
}
}
else
{
trianglesToDuplicate.PushBack(m_mesh.m_triangles.GetHead());
}
m_mesh.m_triangles.Next();
}
size_t nE = m_mesh.GetNEdges();
for (size_t e = 0; e < nE; e++)
{
TMMEdge& currentEdge = m_mesh.m_edges.GetHead()->GetData();
if (currentEdge.m_triangles[0] == 0 && currentEdge.m_triangles[1] == 0)
{
m_edgesToDelete.PushBack(m_mesh.m_edges.GetHead());
}
m_mesh.m_edges.Next();
}
size_t nV = m_mesh.GetNVertices();
CircularList<TMMVertex>& vertices = m_mesh.GetVertices();
for (size_t v = 0; v < nV; ++v)
{
if (vertices.GetData().m_name == sc_dummyIndex)
{
vertices.Delete();
}
else
{
vertices.GetData().m_tag = false;
vertices.Next();
}
}
CleanEdges();
CleanTriangles();
CircularListElement<TMMTriangle>* newTriangle;
for (size_t t = 0; t < trianglesToDuplicate.Size(); t++)
{
newTriangle = m_mesh.AddTriangle();
newTriangle->GetData().m_vertices[0] = trianglesToDuplicate[t]->GetData().m_vertices[1];
newTriangle->GetData().m_vertices[1] = trianglesToDuplicate[t]->GetData().m_vertices[0];
newTriangle->GetData().m_vertices[2] = trianglesToDuplicate[t]->GetData().m_vertices[2];
}
}
return ICHullErrorOK;
}
ICHullError ICHull::Process(const unsigned int nPointsCH,
const double minVolume)
{
unsigned int addedPoints = 0;
if (nPointsCH < 3 || m_mesh.GetNVertices() < 3)
{
return ICHullErrorNotEnoughPoints;
}
if (m_mesh.GetNVertices() == 3)
{
m_isFlat = true;
CircularListElement<TMMTriangle>* t1 = m_mesh.AddTriangle();
CircularListElement<TMMTriangle>* t2 = m_mesh.AddTriangle();
CircularListElement<TMMVertex>* v0 = m_mesh.m_vertices.GetHead();
CircularListElement<TMMVertex>* v1 = v0->GetNext();
CircularListElement<TMMVertex>* v2 = v1->GetNext();
// Compute the normal to the plane
Vec3<double> p0 = v0->GetData().m_pos;
Vec3<double> p1 = v1->GetData().m_pos;
Vec3<double> p2 = v2->GetData().m_pos;
m_normal = (p1 - p0) ^ (p2 - p0);
m_normal.Normalize();
t1->GetData().m_vertices[0] = v0;
t1->GetData().m_vertices[1] = v1;
t1->GetData().m_vertices[2] = v2;
t2->GetData().m_vertices[0] = v1;
t2->GetData().m_vertices[1] = v0;
t2->GetData().m_vertices[2] = v2;
return ICHullErrorOK;
}
if (m_isFlat)
{
m_mesh.m_triangles.Clear();
m_mesh.m_edges.Clear();
m_isFlat = false;
}
if (m_mesh.GetNTriangles() == 0) // we have to create the first polyhedron
{
ICHullError res = DoubleTriangle();
if (res != ICHullErrorOK)
{
return res;
}
else
{
addedPoints += 3;
}
}
CircularList<TMMVertex>& vertices = m_mesh.GetVertices();
while (!vertices.GetData().m_tag && addedPoints < nPointsCH) // not processed
{
if (!FindMaxVolumePoint((addedPoints > 4) ? minVolume : 0.0))
{
break;
}
vertices.GetData().m_tag = true;
if (ProcessPoint())
{
addedPoints++;
CleanUp(addedPoints);
if (!GetMesh().CheckConsistancy())
{
size_t nV = m_mesh.GetNVertices();
CircularList<TMMVertex>& vertices = m_mesh.GetVertices();
for (size_t v = 0; v < nV; ++v)
{
if (vertices.GetData().m_name == sc_dummyIndex)
{
vertices.Delete();
break;
}
vertices.Next();
}
return ICHullErrorInconsistent;
}
vertices.Next();
}
}
// delete remaining points
while (!vertices.GetData().m_tag)
{
vertices.Delete();
}
if (m_isFlat)
{
SArray<CircularListElement<TMMTriangle>*> trianglesToDuplicate;
size_t nT = m_mesh.GetNTriangles();
for (size_t f = 0; f < nT; f++)
{
TMMTriangle& currentTriangle = m_mesh.m_triangles.GetHead()->GetData();
if (currentTriangle.m_vertices[0]->GetData().m_name == sc_dummyIndex || currentTriangle.m_vertices[1]->GetData().m_name == sc_dummyIndex || currentTriangle.m_vertices[2]->GetData().m_name == sc_dummyIndex)
{
m_trianglesToDelete.PushBack(m_mesh.m_triangles.GetHead());
for (int k = 0; k < 3; k++)
{
for (int h = 0; h < 2; h++)
{
if (currentTriangle.m_edges[k]->GetData().m_triangles[h] == m_mesh.m_triangles.GetHead())
{
currentTriangle.m_edges[k]->GetData().m_triangles[h] = 0;
break;
}
}
}
}
else
{
trianglesToDuplicate.PushBack(m_mesh.m_triangles.GetHead());
}
m_mesh.m_triangles.Next();
}
size_t nE = m_mesh.GetNEdges();
for (size_t e = 0; e < nE; e++)
{
TMMEdge& currentEdge = m_mesh.m_edges.GetHead()->GetData();
if (currentEdge.m_triangles[0] == 0 && currentEdge.m_triangles[1] == 0)
{
m_edgesToDelete.PushBack(m_mesh.m_edges.GetHead());
}
m_mesh.m_edges.Next();
}
size_t nV = m_mesh.GetNVertices();
CircularList<TMMVertex>& vertices = m_mesh.GetVertices();
for (size_t v = 0; v < nV; ++v)
{
if (vertices.GetData().m_name == sc_dummyIndex)
{
vertices.Delete();
}
else
{
vertices.GetData().m_tag = false;
vertices.Next();
}
}
CleanEdges();
CleanTriangles();
CircularListElement<TMMTriangle>* newTriangle;
for (size_t t = 0; t < trianglesToDuplicate.Size(); t++)
{
newTriangle = m_mesh.AddTriangle();
newTriangle->GetData().m_vertices[0] = trianglesToDuplicate[t]->GetData().m_vertices[1];
newTriangle->GetData().m_vertices[1] = trianglesToDuplicate[t]->GetData().m_vertices[0];
newTriangle->GetData().m_vertices[2] = trianglesToDuplicate[t]->GetData().m_vertices[2];
}
}
return ICHullErrorOK;
}
bool ICHull::FindMaxVolumePoint(const double minVolume)
{
CircularList<TMMVertex>& vertices = m_mesh.GetVertices();
CircularListElement<TMMVertex>* vMaxVolume = 0;
CircularListElement<TMMVertex>* vHeadPrev = vertices.GetHead()->GetPrev();
double maxVolume = minVolume;
double volume = 0.0;
while (!vertices.GetData().m_tag) // not processed
{
if (ComputePointVolume(volume, false))
{
if (maxVolume < volume)
{
maxVolume = volume;
vMaxVolume = vertices.GetHead();
}
vertices.Next();
}
}
CircularListElement<TMMVertex>* vHead = vHeadPrev->GetNext();
vertices.GetHead() = vHead;
if (!vMaxVolume)
{
return false;
}
if (vMaxVolume != vHead)
{
Vec3<double> pos = vHead->GetData().m_pos;
int id = vHead->GetData().m_name;
vHead->GetData().m_pos = vMaxVolume->GetData().m_pos;
vHead->GetData().m_name = vMaxVolume->GetData().m_name;
vMaxVolume->GetData().m_pos = pos;
vHead->GetData().m_name = id;
}
return true;
}
ICHullError ICHull::DoubleTriangle()
{
// find three non colinear points
m_isFlat = false;
CircularList<TMMVertex>& vertices = m_mesh.GetVertices();
CircularListElement<TMMVertex>* v0 = vertices.GetHead();
while (Colinear(v0->GetData().m_pos,
v0->GetNext()->GetData().m_pos,
v0->GetNext()->GetNext()->GetData().m_pos))
{
if ((v0 = v0->GetNext()) == vertices.GetHead())
{
return ICHullErrorCoplanarPoints;
}
}
CircularListElement<TMMVertex>* v1 = v0->GetNext();
CircularListElement<TMMVertex>* v2 = v1->GetNext();
// mark points as processed
v0->GetData().m_tag = v1->GetData().m_tag = v2->GetData().m_tag = true;
// create two triangles
CircularListElement<TMMTriangle>* f0 = MakeFace(v0, v1, v2, 0);
MakeFace(v2, v1, v0, f0);
// find a fourth non-coplanar point to form tetrahedron
CircularListElement<TMMVertex>* v3 = v2->GetNext();
vertices.GetHead() = v3;
double vol = ComputeVolume4(v0->GetData().m_pos, v1->GetData().m_pos, v2->GetData().m_pos, v3->GetData().m_pos);
while (fabs(vol) < sc_eps && !v3->GetNext()->GetData().m_tag)
{
v3 = v3->GetNext();
vol = ComputeVolume4(v0->GetData().m_pos, v1->GetData().m_pos, v2->GetData().m_pos, v3->GetData().m_pos);
}
if (fabs(vol) < sc_eps)
{
// compute the barycenter
Vec3<double> bary(0.0, 0.0, 0.0);
CircularListElement<TMMVertex>* vBary = v0;
do
{
bary += vBary->GetData().m_pos;
} while ((vBary = vBary->GetNext()) != v0);
bary /= static_cast<double>(vertices.GetSize());
// Compute the normal to the plane
Vec3<double> p0 = v0->GetData().m_pos;
Vec3<double> p1 = v1->GetData().m_pos;
Vec3<double> p2 = v2->GetData().m_pos;
m_normal = (p1 - p0) ^ (p2 - p0);
m_normal.Normalize();
// add dummy vertex placed at (bary + normal)
vertices.GetHead() = v2;
Vec3<double> newPt = bary + m_normal;
AddPoint(newPt, sc_dummyIndex);
m_isFlat = true;
return ICHullErrorOK;
}
else if (v3 != vertices.GetHead())
{
TMMVertex temp;
temp.m_name = v3->GetData().m_name;
temp.m_pos = v3->GetData().m_pos;
v3->GetData().m_name = vertices.GetHead()->GetData().m_name;
v3->GetData().m_pos = vertices.GetHead()->GetData().m_pos;
vertices.GetHead()->GetData().m_name = temp.m_name;
vertices.GetHead()->GetData().m_pos = temp.m_pos;
}
return ICHullErrorOK;
}
CircularListElement<TMMTriangle>* ICHull::MakeFace(CircularListElement<TMMVertex>* v0,
CircularListElement<TMMVertex>* v1,
CircularListElement<TMMVertex>* v2,
CircularListElement<TMMTriangle>* fold)
{
CircularListElement<TMMEdge>* e0;
CircularListElement<TMMEdge>* e1;
CircularListElement<TMMEdge>* e2;
int index = 0;
if (!fold) // if first face to be created
{
e0 = m_mesh.AddEdge(); // create the three edges
e1 = m_mesh.AddEdge();
e2 = m_mesh.AddEdge();
}
else // otherwise re-use existing edges (in reverse order)
{
e0 = fold->GetData().m_edges[2];
e1 = fold->GetData().m_edges[1];
e2 = fold->GetData().m_edges[0];
index = 1;
}
e0->GetData().m_vertices[0] = v0;
e0->GetData().m_vertices[1] = v1;
e1->GetData().m_vertices[0] = v1;
e1->GetData().m_vertices[1] = v2;
e2->GetData().m_vertices[0] = v2;
e2->GetData().m_vertices[1] = v0;
// create the new face
CircularListElement<TMMTriangle>* f = m_mesh.AddTriangle();
f->GetData().m_edges[0] = e0;
f->GetData().m_edges[1] = e1;
f->GetData().m_edges[2] = e2;
f->GetData().m_vertices[0] = v0;
f->GetData().m_vertices[1] = v1;
f->GetData().m_vertices[2] = v2;
// link edges to face f
e0->GetData().m_triangles[index] = e1->GetData().m_triangles[index] = e2->GetData().m_triangles[index] = f;
return f;
}
CircularListElement<TMMTriangle>* ICHull::MakeConeFace(CircularListElement<TMMEdge>* e, CircularListElement<TMMVertex>* p)
{
// create two new edges if they don't already exist
CircularListElement<TMMEdge>* newEdges[2];
for (int i = 0; i < 2; ++i)
{
if (!(newEdges[i] = e->GetData().m_vertices[i]->GetData().m_duplicate))
{ // if the edge doesn't exits add it and mark the vertex as duplicated
newEdges[i] = m_mesh.AddEdge();
newEdges[i]->GetData().m_vertices[0] = e->GetData().m_vertices[i];
newEdges[i]->GetData().m_vertices[1] = p;
e->GetData().m_vertices[i]->GetData().m_duplicate = newEdges[i];
}
}
// make the new face
CircularListElement<TMMTriangle>* newFace = m_mesh.AddTriangle();
newFace->GetData().m_edges[0] = e;
newFace->GetData().m_edges[1] = newEdges[0];
newFace->GetData().m_edges[2] = newEdges[1];
MakeCCW(newFace, e, p);
for (int i = 0; i < 2; ++i)
{
for (int j = 0; j < 2; ++j)
{
if (!newEdges[i]->GetData().m_triangles[j])
{
newEdges[i]->GetData().m_triangles[j] = newFace;
break;
}
}
}
return newFace;
}
bool ICHull::ComputePointVolume(double& totalVolume, bool markVisibleFaces)
{
// mark visible faces
CircularListElement<TMMTriangle>* fHead = m_mesh.GetTriangles().GetHead();
CircularListElement<TMMTriangle>* f = fHead;
CircularList<TMMVertex>& vertices = m_mesh.GetVertices();
CircularListElement<TMMVertex>* vertex0 = vertices.GetHead();
bool visible = false;
Vec3<double> pos0 = Vec3<double>(vertex0->GetData().m_pos.X(),
vertex0->GetData().m_pos.Y(),
vertex0->GetData().m_pos.Z());
double vol = 0.0;
totalVolume = 0.0;
Vec3<double> ver0, ver1, ver2;
do
{
ver0.X() = f->GetData().m_vertices[0]->GetData().m_pos.X();
ver0.Y() = f->GetData().m_vertices[0]->GetData().m_pos.Y();
ver0.Z() = f->GetData().m_vertices[0]->GetData().m_pos.Z();
ver1.X() = f->GetData().m_vertices[1]->GetData().m_pos.X();
ver1.Y() = f->GetData().m_vertices[1]->GetData().m_pos.Y();
ver1.Z() = f->GetData().m_vertices[1]->GetData().m_pos.Z();
ver2.X() = f->GetData().m_vertices[2]->GetData().m_pos.X();
ver2.Y() = f->GetData().m_vertices[2]->GetData().m_pos.Y();
ver2.Z() = f->GetData().m_vertices[2]->GetData().m_pos.Z();
vol = ComputeVolume4(ver0, ver1, ver2, pos0);
if (vol < -sc_eps)
{
vol = fabs(vol);
totalVolume += vol;
if (markVisibleFaces)
{
f->GetData().m_visible = true;
m_trianglesToDelete.PushBack(f);
}
visible = true;
}
f = f->GetNext();
} while (f != fHead);
if (m_trianglesToDelete.Size() == m_mesh.m_triangles.GetSize())
{
for (size_t i = 0; i < m_trianglesToDelete.Size(); i++)
{
m_trianglesToDelete[i]->GetData().m_visible = false;
}
visible = false;
}
// if no faces visible from p then p is inside the hull
if (!visible && markVisibleFaces)
{
vertices.Delete();
m_trianglesToDelete.Resize(0);
return false;
}
return true;
}
bool ICHull::ProcessPoint()
{
double totalVolume = 0.0;
if (!ComputePointVolume(totalVolume, true))
{
return false;
}
// Mark edges in interior of visible region for deletion.
// Create a new face based on each border edge
CircularListElement<TMMVertex>* v0 = m_mesh.GetVertices().GetHead();
CircularListElement<TMMEdge>* eHead = m_mesh.GetEdges().GetHead();
CircularListElement<TMMEdge>* e = eHead;
CircularListElement<TMMEdge>* tmp = 0;
int nvisible = 0;
m_edgesToDelete.Resize(0);
m_edgesToUpdate.Resize(0);
do
{
tmp = e->GetNext();
nvisible = 0;
for (int k = 0; k < 2; k++)
{
if (e->GetData().m_triangles[k]->GetData().m_visible)
{
nvisible++;
}
}
if (nvisible == 2)
{
m_edgesToDelete.PushBack(e);
}
else if (nvisible == 1)
{
e->GetData().m_newFace = MakeConeFace(e, v0);
m_edgesToUpdate.PushBack(e);
}
e = tmp;
} while (e != eHead);
return true;
}
bool ICHull::MakeCCW(CircularListElement<TMMTriangle>* f,
CircularListElement<TMMEdge>* e,
CircularListElement<TMMVertex>* v)
{
// the visible face adjacent to e
CircularListElement<TMMTriangle>* fv;
if (e->GetData().m_triangles[0]->GetData().m_visible)
{
fv = e->GetData().m_triangles[0];
}
else
{
fv = e->GetData().m_triangles[1];
}
// set vertex[0] and vertex[1] to have the same orientation as the corresponding vertices of fv.
int i; // index of e->m_vertices[0] in fv
CircularListElement<TMMVertex>* v0 = e->GetData().m_vertices[0];
CircularListElement<TMMVertex>* v1 = e->GetData().m_vertices[1];
for (i = 0; fv->GetData().m_vertices[i] != v0; i++)
;
if (fv->GetData().m_vertices[(i + 1) % 3] != e->GetData().m_vertices[1])
{
f->GetData().m_vertices[0] = v1;
f->GetData().m_vertices[1] = v0;
}
else
{
f->GetData().m_vertices[0] = v0;
f->GetData().m_vertices[1] = v1;
// swap edges
CircularListElement<TMMEdge>* tmp = f->GetData().m_edges[0];
f->GetData().m_edges[0] = f->GetData().m_edges[1];
f->GetData().m_edges[1] = tmp;
}
f->GetData().m_vertices[2] = v;
return true;
}
bool ICHull::CleanUp(unsigned int& addedPoints)
{
bool r0 = CleanEdges();
bool r1 = CleanTriangles();
bool r2 = CleanVertices(addedPoints);
return r0 && r1 && r2;
}
bool ICHull::CleanEdges()
{
// integrate the new faces into the data structure
CircularListElement<TMMEdge>* e;
const size_t ne_update = m_edgesToUpdate.Size();
for (size_t i = 0; i < ne_update; ++i)
{
e = m_edgesToUpdate[i];
if (e->GetData().m_newFace)
{
if (e->GetData().m_triangles[0]->GetData().m_visible)
{
e->GetData().m_triangles[0] = e->GetData().m_newFace;
}
else
{
e->GetData().m_triangles[1] = e->GetData().m_newFace;
}
e->GetData().m_newFace = 0;
}
}
// delete edges maked for deletion
CircularList<TMMEdge>& edges = m_mesh.GetEdges();
const size_t ne_delete = m_edgesToDelete.Size();
for (size_t i = 0; i < ne_delete; ++i)
{
edges.Delete(m_edgesToDelete[i]);
}
m_edgesToDelete.Resize(0);
m_edgesToUpdate.Resize(0);
return true;
}
bool ICHull::CleanTriangles()
{
CircularList<TMMTriangle>& triangles = m_mesh.GetTriangles();
const size_t nt_delete = m_trianglesToDelete.Size();
for (size_t i = 0; i < nt_delete; ++i)
{
triangles.Delete(m_trianglesToDelete[i]);
}
m_trianglesToDelete.Resize(0);
return true;
}
bool ICHull::CleanVertices(unsigned int& addedPoints)
{
// mark all vertices incident to some undeleted edge as on the hull
CircularList<TMMEdge>& edges = m_mesh.GetEdges();
CircularListElement<TMMEdge>* e = edges.GetHead();
size_t nE = edges.GetSize();
for (size_t i = 0; i < nE; i++)
{
e->GetData().m_vertices[0]->GetData().m_onHull = true;
e->GetData().m_vertices[1]->GetData().m_onHull = true;
e = e->GetNext();
}
// delete all the vertices that have been processed but are not on the hull
CircularList<TMMVertex>& vertices = m_mesh.GetVertices();
CircularListElement<TMMVertex>* vHead = vertices.GetHead();
CircularListElement<TMMVertex>* v = vHead;
v = v->GetPrev();
do
{
if (v->GetData().m_tag && !v->GetData().m_onHull)
{
CircularListElement<TMMVertex>* tmp = v->GetPrev();
vertices.Delete(v);
v = tmp;
addedPoints--;
}
else
{
v->GetData().m_duplicate = 0;
v->GetData().m_onHull = false;
v = v->GetPrev();
}
} while (v->GetData().m_tag && v != vHead);
return true;
}
void ICHull::Clear()
{
m_mesh.Clear();
m_edgesToDelete.Resize(0);
m_edgesToUpdate.Resize(0);
m_trianglesToDelete.Resize(0);
m_isFlat = false;
}
const ICHull& ICHull::operator=(ICHull& rhs)
{
if (&rhs != this)
{
m_mesh.Copy(rhs.m_mesh);
m_edgesToDelete = rhs.m_edgesToDelete;
m_edgesToUpdate = rhs.m_edgesToUpdate;
m_trianglesToDelete = rhs.m_trianglesToDelete;
m_isFlat = rhs.m_isFlat;
}
return (*this);
}
bool ICHull::IsInside(const Vec3<double>& pt0, const double eps)
{
const Vec3<double> pt(pt0.X(), pt0.Y(), pt0.Z());
if (m_isFlat)
{
size_t nT = m_mesh.m_triangles.GetSize();
Vec3<double> ver0, ver1, ver2, a, b, c;
double u, v;
for (size_t t = 0; t < nT; t++)
{
ver0.X() = m_mesh.m_triangles.GetHead()->GetData().m_vertices[0]->GetData().m_pos.X();
ver0.Y() = m_mesh.m_triangles.GetHead()->GetData().m_vertices[0]->GetData().m_pos.Y();
ver0.Z() = m_mesh.m_triangles.GetHead()->GetData().m_vertices[0]->GetData().m_pos.Z();
ver1.X() = m_mesh.m_triangles.GetHead()->GetData().m_vertices[1]->GetData().m_pos.X();
ver1.Y() = m_mesh.m_triangles.GetHead()->GetData().m_vertices[1]->GetData().m_pos.Y();
ver1.Z() = m_mesh.m_triangles.GetHead()->GetData().m_vertices[1]->GetData().m_pos.Z();
ver2.X() = m_mesh.m_triangles.GetHead()->GetData().m_vertices[2]->GetData().m_pos.X();
ver2.Y() = m_mesh.m_triangles.GetHead()->GetData().m_vertices[2]->GetData().m_pos.Y();
ver2.Z() = m_mesh.m_triangles.GetHead()->GetData().m_vertices[2]->GetData().m_pos.Z();
a = ver1 - ver0;
b = ver2 - ver0;
c = pt - ver0;
u = c * a;
v = c * b;
if (u >= 0.0 && u <= 1.0 && v >= 0.0 && u + v <= 1.0)
{
return true;
}
m_mesh.m_triangles.Next();
}
return false;
}
else
{
size_t nT = m_mesh.m_triangles.GetSize();
Vec3<double> ver0, ver1, ver2;
double vol;
for (size_t t = 0; t < nT; t++)
{
ver0.X() = m_mesh.m_triangles.GetHead()->GetData().m_vertices[0]->GetData().m_pos.X();
ver0.Y() = m_mesh.m_triangles.GetHead()->GetData().m_vertices[0]->GetData().m_pos.Y();
ver0.Z() = m_mesh.m_triangles.GetHead()->GetData().m_vertices[0]->GetData().m_pos.Z();
ver1.X() = m_mesh.m_triangles.GetHead()->GetData().m_vertices[1]->GetData().m_pos.X();
ver1.Y() = m_mesh.m_triangles.GetHead()->GetData().m_vertices[1]->GetData().m_pos.Y();
ver1.Z() = m_mesh.m_triangles.GetHead()->GetData().m_vertices[1]->GetData().m_pos.Z();
ver2.X() = m_mesh.m_triangles.GetHead()->GetData().m_vertices[2]->GetData().m_pos.X();
ver2.Y() = m_mesh.m_triangles.GetHead()->GetData().m_vertices[2]->GetData().m_pos.Y();
ver2.Z() = m_mesh.m_triangles.GetHead()->GetData().m_vertices[2]->GetData().m_pos.Z();
vol = ComputeVolume4(ver0, ver1, ver2, pt);
if (vol < eps)
{
return false;
}
m_mesh.m_triangles.Next();
}
return true;
}
}
} // namespace VHACD

View file

@ -0,0 +1,235 @@
/* Copyright (c) 2011 Khaled Mamou (kmamou at gmail dot com)
All rights reserved.
Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
3. The names of the contributors may not be used to endorse or promote products derived from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include "vhacdManifoldMesh.h"
namespace VHACD
{
TMMVertex::TMMVertex(void)
{
Initialize();
}
void TMMVertex::Initialize()
{
m_name = 0;
m_id = 0;
m_duplicate = 0;
m_onHull = false;
m_tag = false;
}
TMMVertex::~TMMVertex(void)
{
}
TMMEdge::TMMEdge(void)
{
Initialize();
}
void TMMEdge::Initialize()
{
m_id = 0;
m_triangles[0] = m_triangles[1] = m_newFace = 0;
m_vertices[0] = m_vertices[1] = 0;
}
TMMEdge::~TMMEdge(void)
{
}
void TMMTriangle::Initialize()
{
m_id = 0;
for (int i = 0; i < 3; i++)
{
m_edges[i] = 0;
m_vertices[0] = 0;
}
m_visible = false;
}
TMMTriangle::TMMTriangle(void)
{
Initialize();
}
TMMTriangle::~TMMTriangle(void)
{
}
TMMesh::TMMesh()
{
}
TMMesh::~TMMesh(void)
{
}
void TMMesh::GetIFS(Vec3<double>* const points, Vec3<int>* const triangles)
{
size_t nV = m_vertices.GetSize();
size_t nT = m_triangles.GetSize();
for (size_t v = 0; v < nV; v++)
{
points[v] = m_vertices.GetData().m_pos;
m_vertices.GetData().m_id = v;
m_vertices.Next();
}
for (size_t f = 0; f < nT; f++)
{
TMMTriangle& currentTriangle = m_triangles.GetData();
triangles[f].X() = static_cast<int>(currentTriangle.m_vertices[0]->GetData().m_id);
triangles[f].Y() = static_cast<int>(currentTriangle.m_vertices[1]->GetData().m_id);
triangles[f].Z() = static_cast<int>(currentTriangle.m_vertices[2]->GetData().m_id);
m_triangles.Next();
}
}
void TMMesh::Clear()
{
m_vertices.Clear();
m_edges.Clear();
m_triangles.Clear();
}
void TMMesh::Copy(TMMesh& mesh)
{
Clear();
// updating the id's
size_t nV = mesh.m_vertices.GetSize();
size_t nE = mesh.m_edges.GetSize();
size_t nT = mesh.m_triangles.GetSize();
for (size_t v = 0; v < nV; v++)
{
mesh.m_vertices.GetData().m_id = v;
mesh.m_vertices.Next();
}
for (size_t e = 0; e < nE; e++)
{
mesh.m_edges.GetData().m_id = e;
mesh.m_edges.Next();
}
for (size_t f = 0; f < nT; f++)
{
mesh.m_triangles.GetData().m_id = f;
mesh.m_triangles.Next();
}
// copying data
m_vertices = mesh.m_vertices;
m_edges = mesh.m_edges;
m_triangles = mesh.m_triangles;
// generate mapping
CircularListElement<TMMVertex>** vertexMap = new CircularListElement<TMMVertex>*[nV];
CircularListElement<TMMEdge>** edgeMap = new CircularListElement<TMMEdge>*[nE];
CircularListElement<TMMTriangle>** triangleMap = new CircularListElement<TMMTriangle>*[nT];
for (size_t v = 0; v < nV; v++)
{
vertexMap[v] = m_vertices.GetHead();
m_vertices.Next();
}
for (size_t e = 0; e < nE; e++)
{
edgeMap[e] = m_edges.GetHead();
m_edges.Next();
}
for (size_t f = 0; f < nT; f++)
{
triangleMap[f] = m_triangles.GetHead();
m_triangles.Next();
}
// updating pointers
for (size_t v = 0; v < nV; v++)
{
if (vertexMap[v]->GetData().m_duplicate)
{
vertexMap[v]->GetData().m_duplicate = edgeMap[vertexMap[v]->GetData().m_duplicate->GetData().m_id];
}
}
for (size_t e = 0; e < nE; e++)
{
if (edgeMap[e]->GetData().m_newFace)
{
edgeMap[e]->GetData().m_newFace = triangleMap[edgeMap[e]->GetData().m_newFace->GetData().m_id];
}
if (nT > 0)
{
for (int f = 0; f < 2; f++)
{
if (edgeMap[e]->GetData().m_triangles[f])
{
edgeMap[e]->GetData().m_triangles[f] = triangleMap[edgeMap[e]->GetData().m_triangles[f]->GetData().m_id];
}
}
}
for (int v = 0; v < 2; v++)
{
if (edgeMap[e]->GetData().m_vertices[v])
{
edgeMap[e]->GetData().m_vertices[v] = vertexMap[edgeMap[e]->GetData().m_vertices[v]->GetData().m_id];
}
}
}
for (size_t f = 0; f < nT; f++)
{
if (nE > 0)
{
for (int e = 0; e < 3; e++)
{
if (triangleMap[f]->GetData().m_edges[e])
{
triangleMap[f]->GetData().m_edges[e] = edgeMap[triangleMap[f]->GetData().m_edges[e]->GetData().m_id];
}
}
}
for (int v = 0; v < 3; v++)
{
if (triangleMap[f]->GetData().m_vertices[v])
{
triangleMap[f]->GetData().m_vertices[v] = vertexMap[triangleMap[f]->GetData().m_vertices[v]->GetData().m_id];
}
}
}
delete[] vertexMap;
delete[] edgeMap;
delete[] triangleMap;
}
bool TMMesh::CheckConsistancy()
{
size_t nE = m_edges.GetSize();
size_t nT = m_triangles.GetSize();
for (size_t e = 0; e < nE; e++)
{
for (int f = 0; f < 2; f++)
{
if (!m_edges.GetHead()->GetData().m_triangles[f])
{
return false;
}
}
m_edges.Next();
}
for (size_t f = 0; f < nT; f++)
{
for (int e = 0; e < 3; e++)
{
int found = 0;
for (int k = 0; k < 2; k++)
{
if (m_triangles.GetHead()->GetData().m_edges[e]->GetData().m_triangles[k] == m_triangles.GetHead())
{
found++;
}
}
if (found != 1)
{
return false;
}
}
m_triangles.Next();
}
return true;
}
} // namespace VHACD

View file

@ -0,0 +1,358 @@
/* Copyright (c) 2011 Khaled Mamou (kmamou at gmail dot com)
All rights reserved.
Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
3. The names of the contributors may not be used to endorse or promote products derived from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#define _CRT_SECURE_NO_WARNINGS
#include "LinearMath/btConvexHullComputer.h"
#include "vhacdMesh.h"
#include <fstream>
#include <iosfwd>
#include <iostream>
#include <stdio.h>
#include <stdlib.h>
#include <string>
namespace VHACD
{
Mesh::Mesh()
{
m_diag = 1.0;
}
Mesh::~Mesh()
{
}
double Mesh::ComputeVolume() const
{
const size_t nV = GetNPoints();
const size_t nT = GetNTriangles();
if (nV == 0 || nT == 0)
{
return 0.0;
}
Vec3<double> bary(0.0, 0.0, 0.0);
for (size_t v = 0; v < nV; v++)
{
bary += GetPoint(v);
}
bary /= static_cast<double>(nV);
Vec3<double> ver0, ver1, ver2;
double totalVolume = 0.0;
for (int t = 0; t < nT; t++)
{
const Vec3<int>& tri = GetTriangle(t);
ver0 = GetPoint(tri[0]);
ver1 = GetPoint(tri[1]);
ver2 = GetPoint(tri[2]);
totalVolume += ComputeVolume4(ver0, ver1, ver2, bary);
}
return totalVolume / 6.0;
}
void Mesh::ComputeConvexHull(const double* const pts,
const size_t nPts)
{
ResizePoints(0);
ResizeTriangles(0);
btConvexHullComputer ch;
ch.compute(pts, 3 * sizeof(double), (int)nPts, -1.0, -1.0);
for (int v = 0; v < ch.vertices.size(); v++)
{
AddPoint(Vec3<double>(ch.vertices[v].getX(), ch.vertices[v].getY(), ch.vertices[v].getZ()));
}
const int nt = ch.faces.size();
for (int t = 0; t < nt; ++t)
{
const btConvexHullComputer::Edge* sourceEdge = &(ch.edges[ch.faces[t]]);
int a = sourceEdge->getSourceVertex();
int b = sourceEdge->getTargetVertex();
const btConvexHullComputer::Edge* edge = sourceEdge->getNextEdgeOfFace();
int c = edge->getTargetVertex();
while (c != a)
{
AddTriangle(Vec3<int>(a, b, c));
edge = edge->getNextEdgeOfFace();
b = c;
c = edge->getTargetVertex();
}
}
}
void Mesh::Clip(const Plane& plane,
SArray<Vec3<double> >& positivePart,
SArray<Vec3<double> >& negativePart) const
{
const size_t nV = GetNPoints();
if (nV == 0)
{
return;
}
double d;
for (size_t v = 0; v < nV; v++)
{
const Vec3<double>& pt = GetPoint(v);
d = plane.m_a * pt[0] + plane.m_b * pt[1] + plane.m_c * pt[2] + plane.m_d;
if (d > 0.0)
{
positivePart.PushBack(pt);
}
else if (d < 0.0)
{
negativePart.PushBack(pt);
}
else
{
positivePart.PushBack(pt);
negativePart.PushBack(pt);
}
}
}
bool Mesh::IsInside(const Vec3<double>& pt) const
{
const size_t nV = GetNPoints();
const size_t nT = GetNTriangles();
if (nV == 0 || nT == 0)
{
return false;
}
Vec3<double> ver0, ver1, ver2;
double volume;
for (int t = 0; t < nT; t++)
{
const Vec3<int>& tri = GetTriangle(t);
ver0 = GetPoint(tri[0]);
ver1 = GetPoint(tri[1]);
ver2 = GetPoint(tri[2]);
volume = ComputeVolume4(ver0, ver1, ver2, pt);
if (volume < 0.0)
{
return false;
}
}
return true;
}
double Mesh::ComputeDiagBB()
{
const size_t nPoints = GetNPoints();
if (nPoints == 0)
return 0.0;
Vec3<double> minBB = m_points[0];
Vec3<double> maxBB = m_points[0];
double x, y, z;
for (size_t v = 1; v < nPoints; v++)
{
x = m_points[v][0];
y = m_points[v][1];
z = m_points[v][2];
if (x < minBB[0])
minBB[0] = x;
else if (x > maxBB[0])
maxBB[0] = x;
if (y < minBB[1])
minBB[1] = y;
else if (y > maxBB[1])
maxBB[1] = y;
if (z < minBB[2])
minBB[2] = z;
else if (z > maxBB[2])
maxBB[2] = z;
}
return (m_diag = (maxBB - minBB).GetNorm());
}
#ifdef VHACD_DEBUG_MESH
bool Mesh::SaveVRML2(const std::string& fileName) const
{
std::ofstream fout(fileName.c_str());
if (fout.is_open())
{
const Material material;
if (SaveVRML2(fout, material))
{
fout.close();
return true;
}
return false;
}
return false;
}
bool Mesh::SaveVRML2(std::ofstream& fout, const Material& material) const
{
if (fout.is_open())
{
fout.setf(std::ios::fixed, std::ios::floatfield);
fout.setf(std::ios::showpoint);
fout.precision(6);
size_t nV = m_points.Size();
size_t nT = m_triangles.Size();
fout << "#VRML V2.0 utf8" << std::endl;
fout << "" << std::endl;
fout << "# Vertices: " << nV << std::endl;
fout << "# Triangles: " << nT << std::endl;
fout << "" << std::endl;
fout << "Group {" << std::endl;
fout << " children [" << std::endl;
fout << " Shape {" << std::endl;
fout << " appearance Appearance {" << std::endl;
fout << " material Material {" << std::endl;
fout << " diffuseColor " << material.m_diffuseColor[0] << " "
<< material.m_diffuseColor[1] << " "
<< material.m_diffuseColor[2] << std::endl;
fout << " ambientIntensity " << material.m_ambientIntensity << std::endl;
fout << " specularColor " << material.m_specularColor[0] << " "
<< material.m_specularColor[1] << " "
<< material.m_specularColor[2] << std::endl;
fout << " emissiveColor " << material.m_emissiveColor[0] << " "
<< material.m_emissiveColor[1] << " "
<< material.m_emissiveColor[2] << std::endl;
fout << " shininess " << material.m_shininess << std::endl;
fout << " transparency " << material.m_transparency << std::endl;
fout << " }" << std::endl;
fout << " }" << std::endl;
fout << " geometry IndexedFaceSet {" << std::endl;
fout << " ccw TRUE" << std::endl;
fout << " solid TRUE" << std::endl;
fout << " convex TRUE" << std::endl;
if (nV > 0)
{
fout << " coord DEF co Coordinate {" << std::endl;
fout << " point [" << std::endl;
for (size_t v = 0; v < nV; v++)
{
fout << " " << m_points[v][0] << " "
<< m_points[v][1] << " "
<< m_points[v][2] << "," << std::endl;
}
fout << " ]" << std::endl;
fout << " }" << std::endl;
}
if (nT > 0)
{
fout << " coordIndex [ " << std::endl;
for (size_t f = 0; f < nT; f++)
{
fout << " " << m_triangles[f][0] << ", "
<< m_triangles[f][1] << ", "
<< m_triangles[f][2] << ", -1," << std::endl;
}
fout << " ]" << std::endl;
}
fout << " }" << std::endl;
fout << " }" << std::endl;
fout << " ]" << std::endl;
fout << "}" << std::endl;
return true;
}
return false;
}
bool Mesh::SaveOFF(const std::string& fileName) const
{
std::ofstream fout(fileName.c_str());
if (fout.is_open())
{
size_t nV = m_points.Size();
size_t nT = m_triangles.Size();
fout << "OFF" << std::endl;
fout << nV << " " << nT << " " << 0 << std::endl;
for (size_t v = 0; v < nV; v++)
{
fout << m_points[v][0] << " "
<< m_points[v][1] << " "
<< m_points[v][2] << std::endl;
}
for (size_t f = 0; f < nT; f++)
{
fout << "3 " << m_triangles[f][0] << " "
<< m_triangles[f][1] << " "
<< m_triangles[f][2] << std::endl;
}
fout.close();
return true;
}
return false;
}
bool Mesh::LoadOFF(const std::string& fileName, bool invert)
{
FILE* fid = fopen(fileName.c_str(), "r");
if (fid)
{
const std::string strOFF("OFF");
char temp[1024];
fscanf(fid, "%s", temp);
if (std::string(temp) != strOFF)
{
fclose(fid);
return false;
}
else
{
int nv = 0;
int nf = 0;
int ne = 0;
fscanf(fid, "%i", &nv);
fscanf(fid, "%i", &nf);
fscanf(fid, "%i", &ne);
m_points.Resize(nv);
m_triangles.Resize(nf);
Vec3<double> coord;
float x, y, z;
for (int p = 0; p < nv; p++)
{
fscanf(fid, "%f", &x);
fscanf(fid, "%f", &y);
fscanf(fid, "%f", &z);
m_points[p][0] = x;
m_points[p][1] = y;
m_points[p][2] = z;
}
int i, j, k, s;
for (int t = 0; t < nf; ++t)
{
fscanf(fid, "%i", &s);
if (s == 3)
{
fscanf(fid, "%i", &i);
fscanf(fid, "%i", &j);
fscanf(fid, "%i", &k);
m_triangles[t][0] = i;
if (invert)
{
m_triangles[t][1] = k;
m_triangles[t][2] = j;
}
else
{
m_triangles[t][1] = j;
m_triangles[t][2] = k;
}
}
else // Fix me: support only triangular meshes
{
for (int h = 0; h < s; ++h)
fscanf(fid, "%i", &s);
}
}
fclose(fid);
}
}
else
{
return false;
}
return true;
}
#endif // VHACD_DEBUG_MESH
} // namespace VHACD

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,52 @@
/* Copyright (c) 2011 Khaled Mamou (kmamou at gmail dot com)
All rights reserved.
Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
3. The names of the contributors may not be used to endorse or promote products derived from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#ifdef OPENCL_FOUND
#pragma once
#ifndef OCL_HELPER_H
#define OCL_HELPER_H
#include <string>
#include <vector>
#ifdef __MACH__
#include <OpenCL/cl.h>
#else
#include <CL/cl.h>
#endif
class OCLHelper
{
public:
OCLHelper(void){};
~OCLHelper(void){};
bool InitPlatform(const unsigned int platformIndex = 0);
bool InitDevice(const unsigned int deviceIndex);
bool GetPlatformsInfo(std::vector<std::string>& info, const std::string& indentation);
bool GetDevicesInfo(std::vector<std::string>& info, const std::string& indentation);
cl_platform_id* GetPlatform() { return &m_platform; }
const cl_platform_id* GetPlatform() const { return &m_platform; }
cl_device_id* GetDevice() { return &m_device; }
const cl_device_id* GetDevice() const { return &m_device; }
private:
cl_platform_id m_platform;
cl_device_id m_device;
cl_int m_lastError;
};
#endif // OCL_HELPER_H
#endif //OPENCL_FOUND

View file

@ -0,0 +1,7 @@
int main_vhacd(int argc, char* argv[]);
int main(int argc, char* argv[])
{
main_vhacd(argc, argv);
}

View file

@ -0,0 +1,754 @@
/* Copyright (c) 2011 Khaled Mamou (kmamou at gmail dot com)
All rights reserved.
Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
3. The names of the contributors may not be used to endorse or promote products derived from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#define _CRT_SECURE_NO_WARNINGS
#include <algorithm>
#include <assert.h>
#include <fstream>
#include <iomanip>
#include <iostream>
#include <sstream>
#include <stdio.h>
#include <string.h>
#include <string>
#include <vector>
//#define _CRTDBG_MAP_ALLOC
#ifdef _CRTDBG_MAP_ALLOC
#include <crtdbg.h>
#include <stdlib.h>
#endif // _CRTDBG_MAP_ALLOC
#include "VHACD.h"
using namespace VHACD;
using namespace std;
bool replace(std::string& str, const std::string& from, const std::string& to)
{
size_t start_pos = str.find(from);
if (start_pos == std::string::npos)
return false;
str.replace(start_pos, from.length(), to);
return true;
}
class MyCallback : public IVHACD::IUserCallback
{
public:
MyCallback(void) {}
~MyCallback(){};
void Update(const double overallProgress, const double stageProgress, const double operationProgress,
const char* const stage, const char* const operation)
{
cout << setfill(' ') << setw(3) << (int)(overallProgress + 0.5) << "% "
<< "[ " << stage << " " << setfill(' ') << setw(3) << (int)(stageProgress + 0.5) << "% ] "
<< operation << " " << setfill(' ') << setw(3) << (int)(operationProgress + 0.5) << "%" << endl;
};
};
class MyLogger : public IVHACD::IUserLogger
{
public:
MyLogger(void) {}
MyLogger(const string& fileName) { OpenFile(fileName); }
~MyLogger(){};
void Log(const char* const msg)
{
if (m_file.is_open())
{
m_file << msg;
m_file.flush();
}
}
void OpenFile(const string& fileName)
{
m_file.open(fileName.c_str());
}
private:
ofstream m_file;
};
struct Material
{
float m_diffuseColor[3];
float m_ambientIntensity;
float m_specularColor[3];
float m_emissiveColor[3];
float m_shininess;
float m_transparency;
Material(void)
{
m_diffuseColor[0] = 0.5f;
m_diffuseColor[1] = 0.5f;
m_diffuseColor[2] = 0.5f;
m_specularColor[0] = 0.5f;
m_specularColor[1] = 0.5f;
m_specularColor[2] = 0.5f;
m_ambientIntensity = 0.4f;
m_emissiveColor[0] = 0.0f;
m_emissiveColor[1] = 0.0f;
m_emissiveColor[2] = 0.0f;
m_shininess = 0.4f;
m_transparency = 0.5f;
};
};
struct Parameters
{
unsigned int m_oclPlatformID;
unsigned int m_oclDeviceID;
string m_fileNameIn;
string m_fileNameOut;
string m_fileNameLog;
bool m_run;
IVHACD::Parameters m_paramsVHACD;
Parameters(void)
{
m_run = true;
m_oclPlatformID = 0;
m_oclDeviceID = 0;
m_fileNameIn = "";
m_fileNameOut = "output.obj";
m_fileNameLog = "log.txt";
}
};
bool LoadOFF(const string& fileName, vector<float>& points, vector<int>& triangles, IVHACD::IUserLogger& logger);
bool LoadOBJ(const string& fileName, vector<float>& points, vector<int>& triangles, IVHACD::IUserLogger& logger);
bool SaveOFF(const string& fileName, const float* const& points, const int* const& triangles, const unsigned int& nPoints,
const unsigned int& nTriangles, IVHACD::IUserLogger& logger);
bool SaveOBJ(ofstream& fout, const double* const& points, const int* const& triangles, const unsigned int& nPoints,
const unsigned int& nTriangles, const Material& material, IVHACD::IUserLogger& logger, int convexPart, int vertexOffset);
bool SaveVRML2(ofstream& fout, const double* const& points, const int* const& triangles, const unsigned int& nPoints,
const unsigned int& nTriangles, const Material& material, IVHACD::IUserLogger& logger);
void GetFileExtension(const string& fileName, string& fileExtension);
void ComputeRandomColor(Material& mat);
void Usage(const Parameters& params);
void ParseParameters(int argc, char* argv[], Parameters& params);
int main_vhacd2(Parameters& params)
{
MyCallback myCallback;
MyLogger myLogger(params.m_fileNameLog);
params.m_paramsVHACD.m_logger = &myLogger;
params.m_paramsVHACD.m_callback = &myCallback;
Usage(params);
if (!params.m_run)
{
return 0;
}
std::ostringstream msg;
msg << "+ OpenCL (OFF)" << std::endl;
msg << "+ Parameters" << std::endl;
msg << "\t input " << params.m_fileNameIn << endl;
msg << "\t resolution " << params.m_paramsVHACD.m_resolution << endl;
msg << "\t max. depth " << params.m_paramsVHACD.m_depth << endl;
msg << "\t max. concavity " << params.m_paramsVHACD.m_concavity << endl;
msg << "\t plane down-sampling " << params.m_paramsVHACD.m_planeDownsampling << endl;
msg << "\t convex-hull down-sampling " << params.m_paramsVHACD.m_convexhullDownsampling << endl;
msg << "\t alpha " << params.m_paramsVHACD.m_alpha << endl;
msg << "\t beta " << params.m_paramsVHACD.m_beta << endl;
msg << "\t gamma " << params.m_paramsVHACD.m_gamma << endl;
msg << "\t pca " << params.m_paramsVHACD.m_pca << endl;
msg << "\t mode " << params.m_paramsVHACD.m_mode << endl;
msg << "\t max. vertices per convex-hull " << params.m_paramsVHACD.m_maxNumVerticesPerCH << endl;
msg << "\t min. volume to add vertices to convex-hulls " << params.m_paramsVHACD.m_minVolumePerCH << endl;
msg << "\t convex-hull approximation " << params.m_paramsVHACD.m_convexhullApproximation << endl;
msg << "\t OpenCL acceleration " << params.m_paramsVHACD.m_oclAcceleration << endl;
msg << "\t OpenCL platform ID " << params.m_oclPlatformID << endl;
msg << "\t OpenCL device ID " << params.m_oclDeviceID << endl;
msg << "\t output " << params.m_fileNameOut << endl;
msg << "\t log " << params.m_fileNameLog << endl;
msg << "+ Load mesh" << std::endl;
myLogger.Log(msg.str().c_str());
cout << msg.str().c_str();
// load mesh
vector<float> points;
vector<int> triangles;
string fileExtension;
GetFileExtension(params.m_fileNameIn, fileExtension);
if (fileExtension == ".OFF")
{
if (!LoadOFF(params.m_fileNameIn, points, triangles, myLogger))
{
return -1;
}
}
else if (fileExtension == ".OBJ")
{
if (!LoadOBJ(params.m_fileNameIn, points, triangles, myLogger))
{
return -1;
}
}
else
{
myLogger.Log("Format not supported!\n");
return -1;
}
// run V-HACD
IVHACD* interfaceVHACD = CreateVHACD();
#ifdef CL_VERSION_1_1
if (params.m_paramsVHACD.m_oclAcceleration)
{
bool res = interfaceVHACD->OCLInit(oclHelper.GetDevice(), &myLogger);
if (!res)
{
params.m_paramsVHACD.m_oclAcceleration = false;
}
}
#endif //CL_VERSION_1_1
bool res = interfaceVHACD->Compute(&points[0], 3, (unsigned int)points.size() / 3,
&triangles[0], 3, (unsigned int)triangles.size() / 3, params.m_paramsVHACD);
if (res)
{
// save output
unsigned int nConvexHulls = interfaceVHACD->GetNConvexHulls();
msg.str("");
msg << "+ Generate output: " << nConvexHulls << " convex-hulls " << endl;
myLogger.Log(msg.str().c_str());
ofstream foutCH(params.m_fileNameOut.c_str());
IVHACD::ConvexHull ch;
if (foutCH.is_open())
{
Material mat;
int vertexOffset = 1; //obj wavefront starts counting at 1...
for (unsigned int p = 0; p < nConvexHulls; ++p)
{
interfaceVHACD->GetConvexHull(p, ch);
SaveOBJ(foutCH, ch.m_points, ch.m_triangles, ch.m_nPoints, ch.m_nTriangles, mat, myLogger, p, vertexOffset);
vertexOffset += ch.m_nPoints;
msg.str("");
msg << "\t CH[" << setfill('0') << setw(5) << p << "] " << ch.m_nPoints << " V, " << ch.m_nTriangles << " T" << endl;
myLogger.Log(msg.str().c_str());
}
foutCH.close();
}
}
else
{
myLogger.Log("Decomposition cancelled by user!\n");
}
#ifdef CL_VERSION_1_1
if (params.m_paramsVHACD.m_oclAcceleration)
{
bool res = interfaceVHACD->OCLRelease(&myLogger);
if (!res)
{
assert(-1);
}
}
#endif //CL_VERSION_1_1
interfaceVHACD->Clean();
interfaceVHACD->Release();
#ifdef _CRTDBG_MAP_ALLOC
_CrtDumpMemoryLeaks();
#endif // _CRTDBG_MAP_ALLOC
return 0;
}
int main_vhacd_ext(const std::string& fileNameIn, const std::string& fileNameOut, const std::string& fileNameLog, VHACD::IVHACD::Parameters& paramsVHACD)
{
Parameters params;
params.m_fileNameIn = fileNameIn;
params.m_fileNameOut = fileNameOut;
params.m_fileNameLog = fileNameLog;
params.m_paramsVHACD = paramsVHACD;
return main_vhacd2(params);
}
int main_vhacd(int argc, char* argv[])
{
// --input camel.off --output camel_acd.obj --log log.txt --resolution 1000000 --depth 20 --concavity 0.0025 --planeDownsampling 4 --convexhullDownsampling 4 --alpha 0.05 --beta 0.05 --gamma 0.00125 --pca 0 --mode 0 --maxNumVerticesPerCH 256 --minVolumePerCH 0.0001 --convexhullApproximation 1 --oclDeviceID 2
// set parameters
Parameters params;
ParseParameters(argc, argv, params);
main_vhacd2(params);
return 0;
}
void Usage(const Parameters& params)
{
std::ostringstream msg;
msg << "V-HACD V" << VHACD_VERSION_MAJOR << "." << VHACD_VERSION_MINOR << endl;
msg << "Syntax: testVHACD [options] --input infile.obj --output outfile.obj --log logfile.txt" << endl
<< endl;
msg << "Options:" << endl;
msg << " --input Wavefront .obj input file name" << endl;
msg << " --output VRML 2.0 output file name" << endl;
msg << " --log Log file name" << endl;
msg << " --resolution Maximum number of voxels generated during the voxelization stage (default=100,000, range=10,000-16,000,000)" << endl;
msg << " --depth Maximum number of clipping stages. During each split stage, parts with a concavity higher than the user defined threshold are clipped according the \"best\" clipping plane (default=20, range=1-32)" << endl;
msg << " --concavity Maximum allowed concavity (default=0.0025, range=0.0-1.0)" << endl;
msg << " --planeDownsampling Controls the granularity of the search for the \"best\" clipping plane (default=4, range=1-16)" << endl;
msg << " --convexhullDownsampling Controls the precision of the convex-hull generation process during the clipping plane selection stage (default=4, range=1-16)" << endl;
msg << " --alpha Controls the bias toward clipping along symmetry planes (default=0.05, range=0.0-1.0)" << endl;
msg << " --beta Controls the bias toward clipping along revolution axes (default=0.05, range=0.0-1.0)" << endl;
msg << " --gamma Controls the maximum allowed concavity during the merge stage (default=0.00125, range=0.0-1.0)" << endl;
msg << " --delta Controls the bias toward maximaxing local concavity (default=0.05, range=0.0-1.0)" << endl;
msg << " --pca Enable/disable normalizing the mesh before applying the convex decomposition (default=0, range={0,1})" << endl;
msg << " --mode 0: voxel-based approximate convex decomposition, 1: tetrahedron-based approximate convex decomposition (default=0, range={0,1})" << endl;
msg << " --maxNumVerticesPerCH Controls the maximum number of triangles per convex-hull (default=64, range=4-1024)" << endl;
msg << " --minVolumePerCH Controls the adaptive sampling of the generated convex-hulls (default=0.0001, range=0.0-0.01)" << endl;
msg << " --convexhullApproximation Enable/disable approximation when computing convex-hulls (default=1, range={0,1})" << endl;
msg << " --oclAcceleration Enable/disable OpenCL acceleration (default=0, range={0,1})" << endl;
msg << " --oclPlatformID OpenCL platform id (default=0, range=0-# OCL platforms)" << endl;
msg << " --oclDeviceID OpenCL device id (default=0, range=0-# OCL devices)" << endl;
msg << " --help Print usage" << endl
<< endl;
msg << "Examples:" << endl;
msg << " testVHACD.exe --input bunny.obj --output bunny_acd.obj --log log.txt" << endl
<< endl;
cout << msg.str();
if (params.m_paramsVHACD.m_logger)
{
params.m_paramsVHACD.m_logger->Log(msg.str().c_str());
}
}
void ParseParameters(int argc, char* argv[], Parameters& params)
{
for (int i = 1; i < argc; ++i)
{
if (!strcmp(argv[i], "--input"))
{
if (++i < argc)
{
params.m_fileNameIn = argv[i];
//come up with some default output name, if not provided
if (params.m_fileNameOut.length() == 0)
{
string tmp = argv[i];
replace(tmp, ".obj", ".vhacd.obj");
params.m_fileNameOut = tmp;
}
}
}
else if (!strcmp(argv[i], "--output"))
{
if (++i < argc)
params.m_fileNameOut = argv[i];
}
else if (!strcmp(argv[i], "--log"))
{
if (++i < argc)
params.m_fileNameLog = argv[i];
}
else if (!strcmp(argv[i], "--resolution"))
{
if (++i < argc)
params.m_paramsVHACD.m_resolution = atoi(argv[i]);
}
else if (!strcmp(argv[i], "--depth"))
{
if (++i < argc)
params.m_paramsVHACD.m_depth = atoi(argv[i]);
}
else if (!strcmp(argv[i], "--concavity"))
{
if (++i < argc)
params.m_paramsVHACD.m_concavity = atof(argv[i]);
}
else if (!strcmp(argv[i], "--planeDownsampling"))
{
if (++i < argc)
params.m_paramsVHACD.m_planeDownsampling = atoi(argv[i]);
}
else if (!strcmp(argv[i], "--convexhullDownsampling"))
{
if (++i < argc)
params.m_paramsVHACD.m_convexhullDownsampling = atoi(argv[i]);
}
else if (!strcmp(argv[i], "--alpha"))
{
if (++i < argc)
params.m_paramsVHACD.m_alpha = atof(argv[i]);
}
else if (!strcmp(argv[i], "--beta"))
{
if (++i < argc)
params.m_paramsVHACD.m_beta = atof(argv[i]);
}
else if (!strcmp(argv[i], "--gamma"))
{
if (++i < argc)
params.m_paramsVHACD.m_gamma = atof(argv[i]);
}
else if (!strcmp(argv[i], "--pca"))
{
if (++i < argc)
params.m_paramsVHACD.m_pca = atoi(argv[i]);
}
else if (!strcmp(argv[i], "--mode"))
{
if (++i < argc)
params.m_paramsVHACD.m_mode = atoi(argv[i]);
}
else if (!strcmp(argv[i], "--maxNumVerticesPerCH"))
{
if (++i < argc)
params.m_paramsVHACD.m_maxNumVerticesPerCH = atoi(argv[i]);
}
else if (!strcmp(argv[i], "--minVolumePerCH"))
{
if (++i < argc)
params.m_paramsVHACD.m_minVolumePerCH = atof(argv[i]);
}
else if (!strcmp(argv[i], "--convexhullApproximation"))
{
if (++i < argc)
params.m_paramsVHACD.m_convexhullApproximation = atoi(argv[i]);
}
else if (!strcmp(argv[i], "--oclAcceleration"))
{
if (++i < argc)
params.m_paramsVHACD.m_oclAcceleration = atoi(argv[i]);
}
else if (!strcmp(argv[i], "--oclPlatformID"))
{
if (++i < argc)
params.m_oclPlatformID = atoi(argv[i]);
}
else if (!strcmp(argv[i], "--oclDeviceID"))
{
if (++i < argc)
params.m_oclDeviceID = atoi(argv[i]);
}
else if (!strcmp(argv[i], "--help"))
{
params.m_run = false;
}
}
params.m_paramsVHACD.m_resolution = (params.m_paramsVHACD.m_resolution < 64) ? 0 : params.m_paramsVHACD.m_resolution;
params.m_paramsVHACD.m_planeDownsampling = (params.m_paramsVHACD.m_planeDownsampling < 1) ? 1 : params.m_paramsVHACD.m_planeDownsampling;
params.m_paramsVHACD.m_convexhullDownsampling = (params.m_paramsVHACD.m_convexhullDownsampling < 1) ? 1 : params.m_paramsVHACD.m_convexhullDownsampling;
}
void GetFileExtension(const string& fileName, string& fileExtension)
{
size_t lastDotPosition = fileName.find_last_of(".");
if (lastDotPosition == string::npos)
{
fileExtension = "";
}
else
{
fileExtension = fileName.substr(lastDotPosition, fileName.size());
transform(fileExtension.begin(), fileExtension.end(), fileExtension.begin(), ::toupper);
}
}
void ComputeRandomColor(Material& mat)
{
mat.m_diffuseColor[0] = mat.m_diffuseColor[1] = mat.m_diffuseColor[2] = 0.0f;
while (mat.m_diffuseColor[0] == mat.m_diffuseColor[1] || mat.m_diffuseColor[2] == mat.m_diffuseColor[1] || mat.m_diffuseColor[2] == mat.m_diffuseColor[0])
{
mat.m_diffuseColor[0] = (rand() % 100) / 100.0f;
mat.m_diffuseColor[1] = (rand() % 100) / 100.0f;
mat.m_diffuseColor[2] = (rand() % 100) / 100.0f;
}
}
bool LoadOFF(const string& fileName, vector<float>& points, vector<int>& triangles, IVHACD::IUserLogger& logger)
{
FILE* fid = fopen(fileName.c_str(), "r");
if (fid)
{
const string strOFF("OFF");
char temp[1024];
fscanf(fid, "%s", temp);
if (string(temp) != strOFF)
{
logger.Log("Loading error: format not recognized \n");
fclose(fid);
return false;
}
else
{
int nv = 0;
int nf = 0;
int ne = 0;
fscanf(fid, "%i", &nv);
fscanf(fid, "%i", &nf);
fscanf(fid, "%i", &ne);
points.resize(nv * 3);
triangles.resize(nf * 3);
const int np = nv * 3;
for (int p = 0; p < np; p++)
{
fscanf(fid, "%f", &(points[p]));
}
int s;
for (int t = 0, r = 0; t < nf; ++t)
{
fscanf(fid, "%i", &s);
if (s == 3)
{
fscanf(fid, "%i", &(triangles[r++]));
fscanf(fid, "%i", &(triangles[r++]));
fscanf(fid, "%i", &(triangles[r++]));
}
else // Fix me: support only triangular meshes
{
for (int h = 0; h < s; ++h)
fscanf(fid, "%i", &s);
}
}
fclose(fid);
}
}
else
{
logger.Log("Loading error: file not found \n");
return false;
}
return true;
}
bool LoadOBJ(const string& fileName, vector<float>& points, vector<int>& triangles, IVHACD::IUserLogger& logger)
{
const unsigned int BufferSize = 1024;
FILE* fid = fopen(fileName.c_str(), "r");
if (fid)
{
char buffer[BufferSize];
int ip[4];
float x[3];
char* pch;
char* str;
while (!feof(fid))
{
if (!fgets(buffer, BufferSize, fid))
{
break;
}
else if (buffer[0] == 'v')
{
if (buffer[1] == ' ')
{
str = buffer + 2;
for (int k = 0; k < 3; ++k)
{
pch = strtok(str, " ");
if (pch)
x[k] = (float)atof(pch);
else
{
return false;
}
str = NULL;
}
points.push_back(x[0]);
points.push_back(x[1]);
points.push_back(x[2]);
}
}
else if (buffer[0] == 'f')
{
pch = str = buffer + 2;
int k = 0;
while (pch)
{
pch = strtok(str, " ");
if (pch)
{
ip[k++] = atoi(pch) - 1;
}
else
{
break;
}
str = NULL;
}
if (k == 3)
{
triangles.push_back(ip[0]);
triangles.push_back(ip[1]);
triangles.push_back(ip[2]);
}
else if (k == 4)
{
triangles.push_back(ip[0]);
triangles.push_back(ip[1]);
triangles.push_back(ip[2]);
triangles.push_back(ip[0]);
triangles.push_back(ip[2]);
triangles.push_back(ip[3]);
}
}
}
fclose(fid);
}
else
{
logger.Log("File not found\n");
return false;
}
return true;
}
bool SaveOFF(const string& fileName, const float* const& points, const int* const& triangles, const unsigned int& nPoints,
const unsigned int& nTriangles, IVHACD::IUserLogger& logger)
{
ofstream fout(fileName.c_str());
if (fout.is_open())
{
size_t nV = nPoints * 3;
size_t nT = nTriangles * 3;
fout << "OFF" << std::endl;
fout << nPoints << " " << nTriangles << " " << 0 << std::endl;
for (size_t v = 0; v < nV; v += 3)
{
fout << points[v + 0] << " "
<< points[v + 1] << " "
<< points[v + 2] << std::endl;
}
for (size_t f = 0; f < nT; f += 3)
{
fout << "3 " << triangles[f + 0] << " "
<< triangles[f + 1] << " "
<< triangles[f + 2] << std::endl;
}
fout.close();
return true;
}
else
{
logger.Log("Can't open file\n");
return false;
}
}
bool SaveOBJ(ofstream& fout, const double* const& points, const int* const& triangles, const unsigned int& nPoints,
const unsigned int& nTriangles, const Material& material, IVHACD::IUserLogger& logger, int convexPart, int vertexOffset)
{
if (fout.is_open())
{
fout.setf(std::ios::fixed, std::ios::floatfield);
fout.setf(std::ios::showpoint);
fout.precision(6);
size_t nV = nPoints * 3;
size_t nT = nTriangles * 3;
fout << "o convex_" << convexPart << std::endl;
if (nV > 0)
{
for (size_t v = 0; v < nV; v += 3)
{
fout << "v " << points[v + 0] << " " << points[v + 1] << " " << points[v + 2] << std::endl;
}
}
if (nT > 0)
{
for (size_t f = 0; f < nT; f += 3)
{
fout << "f "
<< triangles[f + 0] + vertexOffset << " "
<< triangles[f + 1] + vertexOffset << " "
<< triangles[f + 2] + vertexOffset << " " << std::endl;
}
}
return true;
}
else
{
logger.Log("Can't open file\n");
return false;
}
}
bool SaveVRML2(ofstream& fout, const double* const& points, const int* const& triangles, const unsigned int& nPoints,
const unsigned int& nTriangles, const Material& material, IVHACD::IUserLogger& logger)
{
if (fout.is_open())
{
fout.setf(std::ios::fixed, std::ios::floatfield);
fout.setf(std::ios::showpoint);
fout.precision(6);
size_t nV = nPoints * 3;
size_t nT = nTriangles * 3;
fout << "#VRML V2.0 utf8" << std::endl;
fout << "" << std::endl;
fout << "# Vertices: " << nPoints << std::endl;
fout << "# Triangles: " << nTriangles << std::endl;
fout << "" << std::endl;
fout << "Group {" << std::endl;
fout << " children [" << std::endl;
fout << " Shape {" << std::endl;
fout << " appearance Appearance {" << std::endl;
fout << " material Material {" << std::endl;
fout << " diffuseColor " << material.m_diffuseColor[0] << " "
<< material.m_diffuseColor[1] << " "
<< material.m_diffuseColor[2] << std::endl;
fout << " ambientIntensity " << material.m_ambientIntensity << std::endl;
fout << " specularColor " << material.m_specularColor[0] << " "
<< material.m_specularColor[1] << " "
<< material.m_specularColor[2] << std::endl;
fout << " emissiveColor " << material.m_emissiveColor[0] << " "
<< material.m_emissiveColor[1] << " "
<< material.m_emissiveColor[2] << std::endl;
fout << " shininess " << material.m_shininess << std::endl;
fout << " transparency " << material.m_transparency << std::endl;
fout << " }" << std::endl;
fout << " }" << std::endl;
fout << " geometry IndexedFaceSet {" << std::endl;
fout << " ccw TRUE" << std::endl;
fout << " solid TRUE" << std::endl;
fout << " convex TRUE" << std::endl;
if (nV > 0)
{
fout << " coord DEF co Coordinate {" << std::endl;
fout << " point [" << std::endl;
for (size_t v = 0; v < nV; v += 3)
{
fout << " " << points[v + 0] << " "
<< points[v + 1] << " "
<< points[v + 2] << "," << std::endl;
}
fout << " ]" << std::endl;
fout << " }" << std::endl;
}
if (nT > 0)
{
fout << " coordIndex [ " << std::endl;
for (size_t f = 0; f < nT; f += 3)
{
fout << " " << triangles[f + 0] << ", "
<< triangles[f + 1] << ", "
<< triangles[f + 2] << ", -1," << std::endl;
}
fout << " ]" << std::endl;
}
fout << " }" << std::endl;
fout << " }" << std::endl;
fout << " ]" << std::endl;
fout << "}" << std::endl;
return true;
}
else
{
logger.Log("Can't open file\n");
return false;
}
}

View file

@ -0,0 +1,368 @@
/* Copyright (c) 2011 Khaled Mamou (kmamou at gmail dot com)
All rights reserved.
Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
3. The names of the contributors may not be used to endorse or promote products derived from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#ifdef OPENCL_FOUND
#include "oclHelper.h"
#include <assert.h>
#include <sstream>
bool OCLHelper::InitPlatform(const unsigned int platformIndex)
{
cl_uint numPlatforms;
m_lastError = clGetPlatformIDs(1, NULL, &numPlatforms);
if (m_lastError != CL_SUCCESS || platformIndex >= numPlatforms)
return false;
cl_platform_id* platforms = new cl_platform_id[numPlatforms];
m_lastError = clGetPlatformIDs(numPlatforms, platforms, NULL);
if (m_lastError != CL_SUCCESS)
{
delete[] platforms;
return false;
}
m_platform = platforms[platformIndex];
delete[] platforms;
return true;
}
bool OCLHelper::GetPlatformsInfo(std::vector<std::string>& info, const std::string& indentation)
{
const char* platformInfoParameters[] = {"CL_PLATFORM_NAME",
"CL_PLATFORM_VENDOR",
"CL_PLATFORM_VERSION",
"CL_PLATFORM_PROFILE",
"CL_PLATFORM_EXTENSIONS"};
cl_uint numPlatforms;
m_lastError = clGetPlatformIDs(1, NULL, &numPlatforms);
if (m_lastError != CL_SUCCESS)
return false;
cl_platform_id* platforms = new cl_platform_id[numPlatforms];
m_lastError = clGetPlatformIDs(numPlatforms, platforms, NULL);
if (m_lastError != CL_SUCCESS)
{
delete[] platforms;
return false;
}
size_t bufferSize = 4096;
char* buffer = new char[bufferSize];
size_t size;
info.resize(numPlatforms);
for (cl_uint i = 0; i < numPlatforms; ++i)
{
for (int j = CL_PLATFORM_PROFILE; j <= CL_PLATFORM_EXTENSIONS; ++j)
{
info[i] += indentation + platformInfoParameters[j - CL_PLATFORM_PROFILE] + std::string(": ");
m_lastError = clGetPlatformInfo(platforms[i], j, 0, NULL, &size);
if (m_lastError != CL_SUCCESS)
{
delete[] buffer;
delete[] platforms;
return false;
}
if (bufferSize < size)
{
delete[] buffer;
bufferSize = size;
buffer = new char[bufferSize];
}
m_lastError = clGetPlatformInfo(platforms[i], j, size, buffer, NULL);
if (m_lastError != CL_SUCCESS)
{
delete[] buffer;
delete[] platforms;
return false;
}
info[i] += buffer + std::string("\n");
}
}
delete[] platforms;
delete[] buffer;
return true;
}
bool OCLHelper::InitDevice(const unsigned int deviceIndex)
{
cl_uint numDevices;
m_lastError = clGetDeviceIDs(m_platform, CL_DEVICE_TYPE_ALL, 0, NULL, &numDevices);
if (m_lastError != CL_SUCCESS || deviceIndex >= numDevices)
return false;
cl_device_id* devices = new cl_device_id[numDevices];
m_lastError = clGetDeviceIDs(m_platform, CL_DEVICE_TYPE_ALL, numDevices, devices, NULL);
if (m_lastError != CL_SUCCESS)
{
delete[] devices;
return false;
}
m_device = devices[deviceIndex];
delete[] devices;
return true;
}
bool OCLHelper::GetDevicesInfo(std::vector<std::string>& info, const std::string& indentation)
{
enum
{
DATA_TYPE_CL_UINT,
DATA_TYPE_CL_BOOL,
DATA_TYPE_STRING,
DATA_TYPE_CL_ULONG,
DATA_TYPE_CL_DEVICE_FP_CONFIG,
DATA_TYPE_CL_DEVICE_EXEC_CAPABILITIES,
DATA_TYPE_CL_DEVICE_MEM_CACHE_TYPE,
DATA_TYPE_CL_DEVICE_MEM_LOCAL_TYPE,
DATA_TYPE_CL_DEVICE_CMD_QUEUE_PROP,
DATA_TYPE_CL_DEVICE_TYPE,
DATA_TYPE_SIZE_T,
DATA_TYPE_SIZE_T_3,
};
typedef struct
{
cl_device_info id;
const char* name;
int type;
} DeviceInfoParam;
const int numDeviceInfoParameters = 49;
const DeviceInfoParam deviceInfoParameters[numDeviceInfoParameters] = {
{CL_DEVICE_NAME, "CL_DEVICE_NAME", DATA_TYPE_STRING},
{CL_DEVICE_PROFILE, "CL_DEVICE_PROFILE", DATA_TYPE_STRING},
{CL_DEVICE_VENDOR, "CL_DEVICE_VENDOR", DATA_TYPE_STRING},
{CL_DEVICE_VERSION, "CL_DEVICE_VERSION", DATA_TYPE_STRING},
{CL_DRIVER_VERSION, "CL_DRIVER_VERSION", DATA_TYPE_STRING},
{CL_DEVICE_EXTENSIONS, "CL_DEVICE_EXTENSIONS", DATA_TYPE_STRING},
{CL_DEVICE_VERSION, "CL_DEVICE_VERSION", DATA_TYPE_STRING},
{CL_DEVICE_ADDRESS_BITS, "CL_DEVICE_ADDRESS_BITS", DATA_TYPE_CL_UINT},
{CL_DEVICE_GLOBAL_MEM_CACHELINE_SIZE, "CL_DEVICE_GLOBAL_MEM_CACHELINE_SIZE", DATA_TYPE_CL_UINT},
{CL_DEVICE_MAX_CLOCK_FREQUENCY, "CL_DEVICE_MAX_CLOCK_FREQUENCY", DATA_TYPE_CL_UINT},
{CL_DEVICE_MAX_COMPUTE_UNITS, "CL_DEVICE_MAX_COMPUTE_UNITS", DATA_TYPE_CL_UINT},
{CL_DEVICE_MAX_CONSTANT_ARGS, "CL_DEVICE_MAX_CONSTANT_ARGS", DATA_TYPE_CL_UINT},
{CL_DEVICE_MAX_READ_IMAGE_ARGS, "CL_DEVICE_MAX_READ_IMAGE_ARGS", DATA_TYPE_CL_UINT},
{CL_DEVICE_MAX_SAMPLERS, "CL_DEVICE_MAX_SAMPLERS", DATA_TYPE_CL_UINT},
{CL_DEVICE_MAX_WORK_ITEM_DIMENSIONS, "CL_DEVICE_MAX_WORK_ITEM_DIMENSIONS", DATA_TYPE_CL_UINT},
{CL_DEVICE_MAX_WRITE_IMAGE_ARGS, "CL_DEVICE_MAX_WRITE_IMAGE_ARGS", DATA_TYPE_CL_UINT},
{CL_DEVICE_MEM_BASE_ADDR_ALIGN, "CL_DEVICE_MEM_BASE_ADDR_ALIGN", DATA_TYPE_CL_UINT},
{CL_DEVICE_MIN_DATA_TYPE_ALIGN_SIZE, "CL_DEVICE_MIN_DATA_TYPE_ALIGN_SIZE", DATA_TYPE_CL_UINT},
{CL_DEVICE_PREFERRED_VECTOR_WIDTH_CHAR, "CL_DEVICE_PREFERRED_VECTOR_WIDTH_CHAR", DATA_TYPE_CL_UINT},
{CL_DEVICE_PREFERRED_VECTOR_WIDTH_SHORT, "CL_DEVICE_PREFERRED_VECTOR_WIDTH_SHORT", DATA_TYPE_CL_UINT},
{CL_DEVICE_PREFERRED_VECTOR_WIDTH_INT, "CL_DEVICE_PREFERRED_VECTOR_WIDTH_INT", DATA_TYPE_CL_UINT},
{CL_DEVICE_PREFERRED_VECTOR_WIDTH_LONG, "CL_DEVICE_PREFERRED_VECTOR_WIDTH_LONG", DATA_TYPE_CL_UINT},
{CL_DEVICE_PREFERRED_VECTOR_WIDTH_FLOAT, "CL_DEVICE_PREFERRED_VECTOR_WIDTH_FLOAT", DATA_TYPE_CL_UINT},
{CL_DEVICE_PREFERRED_VECTOR_WIDTH_DOUBLE, "CL_DEVICE_PREFERRED_VECTOR_WIDTH_DOUBLE", DATA_TYPE_CL_UINT},
{CL_DEVICE_VENDOR_ID, "CL_DEVICE_VENDOR_ID", DATA_TYPE_CL_UINT},
{CL_DEVICE_AVAILABLE, "CL_DEVICE_AVAILABLE", DATA_TYPE_CL_BOOL},
{CL_DEVICE_COMPILER_AVAILABLE, "CL_DEVICE_COMPILER_AVAILABLE", DATA_TYPE_CL_BOOL},
{CL_DEVICE_ENDIAN_LITTLE, "CL_DEVICE_ENDIAN_LITTLE", DATA_TYPE_CL_BOOL},
{CL_DEVICE_ERROR_CORRECTION_SUPPORT, "CL_DEVICE_ERROR_CORRECTION_SUPPORT", DATA_TYPE_CL_BOOL},
{CL_DEVICE_IMAGE_SUPPORT, "CL_DEVICE_IMAGE_SUPPORT", DATA_TYPE_CL_BOOL},
{CL_DEVICE_EXECUTION_CAPABILITIES, "CL_DEVICE_EXECUTION_CAPABILITIES", DATA_TYPE_CL_DEVICE_EXEC_CAPABILITIES},
{CL_DEVICE_GLOBAL_MEM_CACHE_SIZE, "CL_DEVICE_GLOBAL_MEM_CACHE_SIZE", DATA_TYPE_CL_ULONG},
{CL_DEVICE_GLOBAL_MEM_SIZE, "CL_DEVICE_GLOBAL_MEM_SIZE", DATA_TYPE_CL_ULONG},
{CL_DEVICE_LOCAL_MEM_SIZE, "CL_DEVICE_LOCAL_MEM_SIZE", DATA_TYPE_CL_ULONG},
{CL_DEVICE_MAX_CONSTANT_BUFFER_SIZE, "CL_DEVICE_MAX_CONSTANT_BUFFER_SIZE", DATA_TYPE_CL_ULONG},
{CL_DEVICE_MAX_MEM_ALLOC_SIZE, "CL_DEVICE_MAX_MEM_ALLOC_SIZE", DATA_TYPE_CL_ULONG},
{CL_DEVICE_GLOBAL_MEM_CACHE_TYPE, "CL_DEVICE_GLOBAL_MEM_CACHE_TYPE", DATA_TYPE_CL_DEVICE_MEM_CACHE_TYPE},
{CL_DEVICE_IMAGE2D_MAX_HEIGHT, "CL_DEVICE_IMAGE2D_MAX_HEIGHT", DATA_TYPE_SIZE_T},
{CL_DEVICE_IMAGE2D_MAX_WIDTH, "CL_DEVICE_IMAGE2D_MAX_WIDTH", DATA_TYPE_SIZE_T},
{CL_DEVICE_IMAGE3D_MAX_DEPTH, "CL_DEVICE_IMAGE3D_MAX_DEPTH", DATA_TYPE_SIZE_T},
{CL_DEVICE_IMAGE3D_MAX_HEIGHT, "CL_DEVICE_IMAGE3D_MAX_HEIGHT", DATA_TYPE_SIZE_T},
{CL_DEVICE_IMAGE3D_MAX_WIDTH, "CL_DEVICE_IMAGE3D_MAX_WIDTH", DATA_TYPE_SIZE_T},
{CL_DEVICE_MAX_PARAMETER_SIZE, "CL_DEVICE_MAX_PARAMETER_SIZE", DATA_TYPE_SIZE_T},
{CL_DEVICE_MAX_WORK_GROUP_SIZE, "CL_DEVICE_MAX_WORK_GROUP_SIZE", DATA_TYPE_SIZE_T},
{CL_DEVICE_PROFILING_TIMER_RESOLUTION, "CL_DEVICE_PROFILING_TIMER_RESOLUTION", DATA_TYPE_SIZE_T},
{CL_DEVICE_QUEUE_PROPERTIES, "CL_DEVICE_QUEUE_PROPERTIES", DATA_TYPE_CL_DEVICE_CMD_QUEUE_PROP},
{CL_DEVICE_TYPE, "CL_DEVICE_TYPE", DATA_TYPE_CL_DEVICE_TYPE},
{CL_DEVICE_LOCAL_MEM_TYPE, "CL_DEVICE_LOCAL_MEM_TYPE", DATA_TYPE_CL_DEVICE_MEM_LOCAL_TYPE},
{CL_DEVICE_MAX_WORK_ITEM_SIZES, "CL_DEVICE_MAX_WORK_ITEM_SIZES", DATA_TYPE_SIZE_T_3}
// { CL_DEVICE_DOUBLE_FP_CONFIG, "CL_DEVICE_DOUBLE_FP_CONFIG", DATA_TYPE_CL_DEVICE_FP_CONFIG },
//
};
cl_uint numDevices;
m_lastError = clGetDeviceIDs(m_platform, CL_DEVICE_TYPE_ALL, 0, NULL, &numDevices);
if (m_lastError != CL_SUCCESS)
return false;
cl_device_id* devices = new cl_device_id[numDevices];
m_lastError = clGetDeviceIDs(m_platform, CL_DEVICE_TYPE_ALL, numDevices, devices, NULL);
if (m_lastError != CL_SUCCESS)
{
delete[] devices;
return false;
}
size_t bufferSize = 4096;
char* buffer = new char[bufferSize];
size_t size;
info.resize(numDevices);
for (cl_uint i = 0; i < numDevices; ++i)
{
for (int j = 0; j < numDeviceInfoParameters; ++j)
{
const DeviceInfoParam& infoParam = deviceInfoParameters[j];
info[i] += indentation + infoParam.name + std::string(": ");
if (infoParam.type == DATA_TYPE_STRING)
{
m_lastError = clGetDeviceInfo(devices[i], infoParam.id, 0, NULL, &size);
if (m_lastError == CL_SUCCESS)
{
if (bufferSize < size)
{
delete[] buffer;
bufferSize = size;
buffer = new char[bufferSize];
}
m_lastError = clGetDeviceInfo(devices[i], infoParam.id, size, buffer, NULL);
if (m_lastError != CL_SUCCESS)
{
delete[] devices;
delete[] buffer;
return false;
}
info[i] += buffer + std::string("\n");
}
}
else if (infoParam.type == DATA_TYPE_CL_UINT)
{
cl_uint value;
m_lastError = clGetDeviceInfo(devices[i], infoParam.id, sizeof(cl_uint), &value, &size);
if (m_lastError == CL_SUCCESS)
{
std::ostringstream svalue;
svalue << value;
info[i] += svalue.str() + "\n";
}
}
else if (infoParam.type == DATA_TYPE_CL_BOOL)
{
cl_bool value;
m_lastError = clGetDeviceInfo(devices[i], infoParam.id, sizeof(cl_bool), &value, &size);
if (m_lastError == CL_SUCCESS)
{
std::ostringstream svalue;
svalue << value;
info[i] += svalue.str() + "\n";
}
}
else if (infoParam.type == DATA_TYPE_CL_ULONG)
{
cl_ulong value;
m_lastError = clGetDeviceInfo(devices[i], infoParam.id, sizeof(cl_ulong), &value, &size);
if (m_lastError == CL_SUCCESS)
{
std::ostringstream svalue;
svalue << value;
info[i] += svalue.str() + "\n";
}
}
else if (infoParam.type == DATA_TYPE_CL_DEVICE_FP_CONFIG)
{
cl_device_fp_config value;
m_lastError = clGetDeviceInfo(devices[i], infoParam.id, sizeof(cl_device_fp_config), &value, &size);
if (m_lastError == CL_SUCCESS)
{
std::ostringstream svalue;
svalue << value;
info[i] += svalue.str() + "\n";
}
}
else if (infoParam.type == DATA_TYPE_CL_DEVICE_EXEC_CAPABILITIES)
{
cl_device_exec_capabilities value;
m_lastError = clGetDeviceInfo(devices[i], infoParam.id, sizeof(cl_device_exec_capabilities), &value, &size);
if (m_lastError == CL_SUCCESS)
{
std::ostringstream svalue;
svalue << value;
info[i] += svalue.str() + "\n";
}
}
else if (infoParam.type == DATA_TYPE_CL_DEVICE_MEM_CACHE_TYPE)
{
cl_device_mem_cache_type value;
m_lastError = clGetDeviceInfo(devices[i], infoParam.id, sizeof(cl_device_mem_cache_type), &value, &size);
if (m_lastError == CL_SUCCESS)
{
std::ostringstream svalue;
svalue << value;
info[i] += svalue.str() + "\n";
}
}
else if (infoParam.type == DATA_TYPE_CL_DEVICE_MEM_LOCAL_TYPE)
{
cl_device_local_mem_type value;
m_lastError = clGetDeviceInfo(devices[i], infoParam.id, sizeof(cl_device_local_mem_type), &value, &size);
if (m_lastError == CL_SUCCESS)
{
std::ostringstream svalue;
svalue << value;
info[i] += svalue.str() + "\n";
}
}
else if (infoParam.type == DATA_TYPE_CL_DEVICE_CMD_QUEUE_PROP)
{
cl_command_queue_properties value;
m_lastError = clGetDeviceInfo(devices[i], infoParam.id, sizeof(cl_command_queue_properties), &value, &size);
if (m_lastError == CL_SUCCESS)
{
std::ostringstream svalue;
svalue << value;
info[i] += svalue.str() + "\n";
}
}
else if (infoParam.type == DATA_TYPE_CL_DEVICE_TYPE)
{
cl_device_type value;
m_lastError = clGetDeviceInfo(devices[i], infoParam.id, sizeof(cl_device_type), &value, &size);
if (m_lastError == CL_SUCCESS)
{
std::ostringstream svalue;
svalue << value;
info[i] += svalue.str() + "\n";
}
}
else if (infoParam.type == DATA_TYPE_SIZE_T)
{
size_t value;
m_lastError = clGetDeviceInfo(devices[i], infoParam.id, sizeof(size_t), &value, &size);
if (m_lastError == CL_SUCCESS)
{
std::ostringstream svalue;
svalue << value;
info[i] += svalue.str() + "\n";
}
}
else if (infoParam.type == DATA_TYPE_SIZE_T_3)
{
size_t value[3];
m_lastError = clGetDeviceInfo(devices[i], infoParam.id, 3 * sizeof(size_t), &value, &size);
if (m_lastError == CL_SUCCESS)
{
std::ostringstream svalue;
svalue << "(" << value[0] << ", " << value[1] << ", " << value[2] << ")";
info[i] += svalue.str() + "\n";
}
}
else
{
assert(0);
}
}
}
delete[] devices;
delete[] buffer;
return true;
}
#endif // OPENCL_FOUND

View file

@ -0,0 +1,27 @@
project "test_vhacd"
if _OPTIONS["ios"] then
kind "WindowedApp"
else
kind "ConsoleApp"
end
includedirs {"../../public",
"../../../src"}
links {
"vhacd", "LinearMath"
}
language "C++"
files {
"main.cpp",
"main_vhacd.cpp",
}
if os.is("Linux") then
links {"pthread"}
end