From 5f3ca7fda02526e633b4244639c577c0afbc0eea Mon Sep 17 00:00:00 2001 From: Siana Gearz Date: Sun, 13 Jan 2013 03:23:56 +0100 Subject: [PATCH] Linux64 support, integrated physics/decomposition --- indra/CMakeLists.txt | 3 + indra/cmake/LLPhysicsExtensions.cmake | 5 +- indra/cmake/LLPrimitive.cmake | 3 + indra/libhacd/CMakeLists.txt | 9 + indra/libhacd/hacdCircularList.h | 85 ++ indra/libhacd/hacdCircularList.inl | 230 ++++ indra/libhacd/hacdGraph.cpp | 278 ++++ indra/libhacd/hacdGraph.h | 118 ++ indra/libhacd/hacdHACD.cpp | 1140 +++++++++++++++++ indra/libhacd/hacdHACD.h | 328 +++++ indra/libhacd/hacdICHull.cpp | 1004 +++++++++++++++ indra/libhacd/hacdICHull.h | 133 ++ indra/libhacd/hacdManifoldMesh.cpp | 602 +++++++++ indra/libhacd/hacdManifoldMesh.h | 267 ++++ indra/libhacd/hacdMeshDecimator.cpp | 684 ++++++++++ indra/libhacd/hacdMeshDecimator.h | 116 ++ indra/libhacd/hacdMicroAllocator.cpp | 993 ++++++++++++++ indra/libhacd/hacdMicroAllocator.h | 204 +++ indra/libhacd/hacdRaycastMesh.cpp | 280 ++++ indra/libhacd/hacdRaycastMesh.h | 97 ++ indra/libhacd/hacdSArray.h | 154 +++ indra/libhacd/hacdVector.h | 69 + indra/libhacd/hacdVector.inl | 178 +++ indra/libhacd/hacdVersion.h | 20 + indra/libndhacd/CMakeLists.txt | 9 + indra/libndhacd/LLConvexDecomposition.cpp | 80 ++ indra/libndhacd/LLConvexDecomposition.h | 231 ++++ .../LLConvexDecompositionStubImpl.cpp | 148 +++ .../libndhacd/LLConvexDecompositionStubImpl.h | 97 ++ indra/libndhacd/ndConvexDecomposition.h | 58 + indra/libndhacd/nd_EnterExitTracer.cpp | 51 + indra/libndhacd/nd_EnterExitTracer.h | 43 + indra/libndhacd/nd_StructTracer.cpp | 188 +++ indra/libndhacd/nd_StructTracer.h | 34 + .../libndhacd/nd_hacdConvexDecomposition.cpp | 393 ++++++ indra/libndhacd/nd_hacdConvexDecomposition.h | 103 ++ indra/libndhacd/nd_hacdDefines.h | 46 + indra/libndhacd/nd_hacdStructs.cpp | 107 ++ indra/libndhacd/nd_hacdStructs.h | 77 ++ indra/libndhacd/nd_hacdUtils.cpp | 183 +++ indra/libndhacd/nd_hacdUtils.h | 34 + indra/libndhacd/windowsincludes.h | 59 + indra/libpathing/CMakeLists.txt | 13 + indra/libpathing/llpathinglib.cpp | 89 ++ indra/libpathing/llpathinglib.h | 181 +++ indra/libpathing/llphysicsextensions.cpp | 10 + indra/libpathing/llphysicsextensions.h | 12 + indra/llprimitive/llmodel.cpp | 2 +- indra/newview/llmeshrepository.h | 2 +- indra/newview/viewer_manifest.py | 3 + install.xml | 52 +- 51 files changed, 9268 insertions(+), 37 deletions(-) create mode 100644 indra/libhacd/CMakeLists.txt create mode 100644 indra/libhacd/hacdCircularList.h create mode 100644 indra/libhacd/hacdCircularList.inl create mode 100644 indra/libhacd/hacdGraph.cpp create mode 100644 indra/libhacd/hacdGraph.h create mode 100644 indra/libhacd/hacdHACD.cpp create mode 100644 indra/libhacd/hacdHACD.h create mode 100644 indra/libhacd/hacdICHull.cpp create mode 100644 indra/libhacd/hacdICHull.h create mode 100644 indra/libhacd/hacdManifoldMesh.cpp create mode 100644 indra/libhacd/hacdManifoldMesh.h create mode 100644 indra/libhacd/hacdMeshDecimator.cpp create mode 100644 indra/libhacd/hacdMeshDecimator.h create mode 100644 indra/libhacd/hacdMicroAllocator.cpp create mode 100644 indra/libhacd/hacdMicroAllocator.h create mode 100644 indra/libhacd/hacdRaycastMesh.cpp create mode 100644 indra/libhacd/hacdRaycastMesh.h create mode 100644 indra/libhacd/hacdSArray.h create mode 100644 indra/libhacd/hacdVector.h create mode 100644 indra/libhacd/hacdVector.inl create mode 100644 indra/libhacd/hacdVersion.h create mode 100644 indra/libndhacd/CMakeLists.txt create mode 100644 indra/libndhacd/LLConvexDecomposition.cpp create mode 100644 indra/libndhacd/LLConvexDecomposition.h create mode 100644 indra/libndhacd/LLConvexDecompositionStubImpl.cpp create mode 100644 indra/libndhacd/LLConvexDecompositionStubImpl.h create mode 100644 indra/libndhacd/ndConvexDecomposition.h create mode 100644 indra/libndhacd/nd_EnterExitTracer.cpp create mode 100644 indra/libndhacd/nd_EnterExitTracer.h create mode 100644 indra/libndhacd/nd_StructTracer.cpp create mode 100644 indra/libndhacd/nd_StructTracer.h create mode 100644 indra/libndhacd/nd_hacdConvexDecomposition.cpp create mode 100644 indra/libndhacd/nd_hacdConvexDecomposition.h create mode 100644 indra/libndhacd/nd_hacdDefines.h create mode 100644 indra/libndhacd/nd_hacdStructs.cpp create mode 100644 indra/libndhacd/nd_hacdStructs.h create mode 100644 indra/libndhacd/nd_hacdUtils.cpp create mode 100644 indra/libndhacd/nd_hacdUtils.h create mode 100644 indra/libndhacd/windowsincludes.h create mode 100644 indra/libpathing/CMakeLists.txt create mode 100644 indra/libpathing/llpathinglib.cpp create mode 100644 indra/libpathing/llpathinglib.h create mode 100644 indra/libpathing/llphysicsextensions.cpp create mode 100644 indra/libpathing/llphysicsextensions.h diff --git a/indra/CMakeLists.txt b/indra/CMakeLists.txt index 43d185b4f..7977fca40 100644 --- a/indra/CMakeLists.txt +++ b/indra/CMakeLists.txt @@ -52,6 +52,9 @@ add_subdirectory(${LIBS_OPEN_PREFIX}llcharacter) add_subdirectory(${LIBS_OPEN_PREFIX}llcommon) add_subdirectory(${LIBS_OPEN_PREFIX}llimage) add_subdirectory(${LIBS_OPEN_PREFIX}libopenjpeg) +add_subdirectory(${LIBS_OPEN_PREFIX}libhacd) +add_subdirectory(${LIBS_OPEN_PREFIX}libndhacd) +add_subdirectory(${LIBS_OPEN_PREFIX}libpathing) add_subdirectory(${LIBS_OPEN_PREFIX}llimagej2coj) add_subdirectory(${LIBS_OPEN_PREFIX}llinventory) add_subdirectory(${LIBS_OPEN_PREFIX}llmath) diff --git a/indra/cmake/LLPhysicsExtensions.cmake b/indra/cmake/LLPhysicsExtensions.cmake index 21d20a3d3..0e2d94316 100644 --- a/indra/cmake/LLPhysicsExtensions.cmake +++ b/indra/cmake/LLPhysicsExtensions.cmake @@ -1,10 +1,7 @@ # -*- cmake -*- -include(Prebuilt) - -use_prebuilt_binary(ndPhysicsStub) set(LLPHYSICSEXTENSIONS_LIBRARIES nd_hacdConvexDecomposition hacd nd_Pathing ) -set(LLPHYSICSEXTENSIONS_INCLUDE_DIRS ${LIBS_PREBUILT_DIR}/${LL_ARCH_DIR}/include/ ) +set(LLPHYSICSEXTENSIONS_INCLUDE_DIRS ${LIBS_OPEN_DIR}/libndhacd ) diff --git a/indra/cmake/LLPrimitive.cmake b/indra/cmake/LLPrimitive.cmake index 4c8fec191..a49e0a802 100644 --- a/indra/cmake/LLPrimitive.cmake +++ b/indra/cmake/LLPrimitive.cmake @@ -3,7 +3,10 @@ # these should be moved to their own cmake file include(Prebuilt) use_prebuilt_binary(colladadom) + +if (NOT WINDOWS) use_prebuilt_binary(pcre) +endif (NOT WINDOWS) if (NOT DARWIN AND NOT WINDOWS) use_prebuilt_binary(libxml) diff --git a/indra/libhacd/CMakeLists.txt b/indra/libhacd/CMakeLists.txt new file mode 100644 index 000000000..fe35e823c --- /dev/null +++ b/indra/libhacd/CMakeLists.txt @@ -0,0 +1,9 @@ +cmake_minimum_required(VERSION 2.6.4) + +project(libhacd CXX C) + +file (GLOB SOURCE_FILES *.cpp ) +file (GLOB INCLUDE_FILES *.h ) + +add_library(hacd STATIC ${SOURCE_FILES} ${INCLUDE_FILES} ) + diff --git a/indra/libhacd/hacdCircularList.h b/indra/libhacd/hacdCircularList.h new file mode 100644 index 000000000..4ec965a5f --- /dev/null +++ b/indra/libhacd/hacdCircularList.h @@ -0,0 +1,85 @@ +/* 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 HACD_CIRCULAR_LIST_H +#define HACD_CIRCULAR_LIST_H +#include +#include "hacdVersion.h" +#include "hacdMicroAllocator.h" +namespace HACD +{ + //! CircularListElement class. + template < typename T > class CircularListElement + { + public: + T & GetData() { return m_data; } + const T & GetData() const { return m_data; } + CircularListElement * & GetNext() { return m_next; } + CircularListElement * & GetPrev() { return m_prev; } + const CircularListElement * & GetNext() const { return m_next; } + const CircularListElement * & GetPrev() const { return m_prev; } + //! Constructor + CircularListElement(const T & data) {m_data = data;} + CircularListElement(void){} + //! Destructor + ~CircularListElement(void){} + private: + T m_data; + CircularListElement * m_next; + CircularListElement * m_prev; + + CircularListElement(const CircularListElement & rhs); + }; + + + //! CircularList class. + template < typename T > class CircularList + { + public: + HeapManager * const GetHeapManager() const { return m_heapManager;} + void SetHeapManager(HeapManager * const heapManager) { m_heapManager = heapManager;} + CircularListElement * & GetHead() { return m_head;} + const CircularListElement * 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 * element); + CircularListElement * Add(const T * data = 0); + CircularListElement * Add(const T & data); + bool Next(); + bool Prev(); + void Clear() { while(Delete());}; + const CircularList& operator=(const CircularList& rhs); + //! Constructor + CircularList(HeapManager * heapManager) + { + m_head = 0; + m_size = 0; + m_heapManager = heapManager; + } + CircularList(const CircularList& rhs); + //! Destructor + virtual ~CircularList(void) {Clear();}; + private: + CircularListElement * m_head; //!< a pointer to the head of the circular list + size_t m_size; //!< number of element in the circular list + HeapManager * m_heapManager; + + }; +} +#include "hacdCircularList.inl" +#endif diff --git a/indra/libhacd/hacdCircularList.inl b/indra/libhacd/hacdCircularList.inl new file mode 100644 index 000000000..fa57ac752 --- /dev/null +++ b/indra/libhacd/hacdCircularList.inl @@ -0,0 +1,230 @@ +#pragma once +#ifndef HACD_CIRCULAR_LIST_INL +#define HACD_CIRCULAR_LIST_INL +#include +#include "hacdVersion.h" +namespace HACD +{ + template < typename T > + inline bool CircularList::Delete(CircularListElement * element) + { + if (!element) + { + return false; + } + if (m_size > 1) + { + CircularListElement * next = element->GetNext(); + CircularListElement * prev = element->GetPrev(); + if (m_heapManager) + { + element->~CircularListElement(); + heap_free(m_heapManager, element); + } + else + { + delete element; + } + m_size--; + if (element == m_head) + { + m_head = next; + } + next->GetPrev() = prev; + prev->GetNext() = next; + return true; + } + else if (m_size == 1) + { + if (m_heapManager) + { + element->~CircularListElement(); + heap_free(m_heapManager, m_head); + } + else + { + delete m_head; + } + m_size--; + m_head = 0; + return true; + } + else + { + return false; + } + } + + template < typename T > + inline bool CircularList::Delete() + { + if (m_size > 1) + { + CircularListElement * next = m_head->GetNext(); + CircularListElement * prev = m_head->GetPrev(); + if (m_heapManager) + { + m_head->~CircularListElement(); + heap_free(m_heapManager, m_head); + } + else + { + delete m_head; + } + m_size--; + m_head = next; + next->GetPrev() = prev; + prev->GetNext() = next; + return true; + } + else if (m_size == 1) + { + if (m_heapManager) + { + m_head->~CircularListElement(); + heap_free(m_heapManager, m_head); + } + else + { + delete m_head; + } + m_size--; + m_head = 0; + return true; + } + else + { + return false; + } + } + template < typename T > + inline CircularListElement * CircularList::Add(const T * data) + { + if (m_size == 0) + { + if (data) + { + if (m_heapManager) + { + m_head = static_cast< CircularListElement * > (heap_malloc(m_heapManager, sizeof(CircularListElement))); + m_head->GetData().Initialize(); + m_head->GetData() = (*data); + } + else + { + m_head = new CircularListElement(*data); + } + } + else + { + if (m_heapManager) + { + m_head = static_cast< CircularListElement * > (heap_malloc(m_heapManager, sizeof(CircularListElement))); + m_head->GetData().Initialize(); + } + else + { + m_head = new CircularListElement(); + } + } + m_head->GetNext() = m_head->GetPrev() = m_head; + } + else + { + CircularListElement * next = m_head->GetNext(); + CircularListElement * element = m_head; + if (data) + { + if (m_heapManager) + { + m_head = static_cast< CircularListElement * > (heap_malloc(m_heapManager, sizeof(CircularListElement))); + m_head->GetData().Initialize(); + m_head->GetData() = (*data); + } + else + { + m_head = new CircularListElement(*data); + } + } + else + { + if (m_heapManager) + { + m_head = static_cast< CircularListElement * > (heap_malloc(m_heapManager, sizeof(CircularListElement))); + m_head->GetData().Initialize(); + } + else + { + m_head = new CircularListElement(); + } + } + 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 * CircularList::Add(const T & data) + { + const T * pData = &data; + return Add(pData); + } + template < typename T > + inline bool CircularList::Next() + { + if (m_size == 0) + { + return false; + } + m_head = m_head->GetNext(); + return true; + } + template < typename T > + inline bool CircularList::Prev() + { + if (m_size == 0) + { + return false; + } + m_head = m_head->GetPrev(); + return true; + } + template < typename T > + inline CircularList::CircularList(const CircularList& rhs) + { + if (rhs.m_size > 0) + { + CircularListElement * current = rhs.m_head; + do + { + current = current->GetNext(); + Add(current->GetData()); + } + while ( current != rhs.m_head ); + } + } + template < typename T > + inline const CircularList& CircularList::operator=(const CircularList& rhs) + { + if (&rhs != this) + { + Clear(); + m_heapManager = rhs.m_heapManager; + if (rhs.m_size > 0) + { + CircularListElement * current = rhs.m_head; + do + { + current = current->GetNext(); + Add(current->GetData()); + } + while ( current != rhs.m_head ); + } + } + return (*this); + } +} +#endif diff --git a/indra/libhacd/hacdGraph.cpp b/indra/libhacd/hacdGraph.cpp new file mode 100644 index 000000000..cef2565ec --- /dev/null +++ b/indra/libhacd/hacdGraph.cpp @@ -0,0 +1,278 @@ +/* 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 "hacdGraph.h" +namespace HACD +{ + + GraphEdge::GraphEdge() + { + m_v1 = -1; + m_v2 = -1; + m_name = -1; + m_error = 0; + m_concavity = 0; + m_deleted = false; +#ifdef HACD_PRECOMPUTE_CHULLS + m_convexHull = 0; +#endif + } + + GraphVertex::GraphVertex() + { + m_convexHull = 0; + m_name = -1; + m_cc = -1; + m_concavity = 0; + m_surf = 0; + m_deleted = false; + } + + bool GraphVertex::DeleteEdge(long name) + { + return m_edges.Erase(name); + } + + Graph::Graph() + { + m_nV = 0; + m_nE = 0; + m_nCCs = 0; + } + + Graph::~Graph() + { + } + + void Graph::Allocate(size_t nV, size_t nE) + { + m_nV = nV; + m_edges.reserve(nE); + m_vertices.resize(nV); + for(size_t i = 0; i < nV; i++) + { + m_vertices[i].m_name = static_cast(i); + } + } + + long Graph::AddVertex() + { + size_t name = m_vertices.size(); + m_vertices.resize(name+1); + m_vertices[name].m_name = static_cast(name); + m_nV++; + return static_cast(name); + } + + long Graph::AddEdge(long v1, long v2) + { + size_t name = m_edges.size(); + m_edges.push_back(GraphEdge()); + m_edges[name].m_name = static_cast(name); + m_edges[name].m_v1 = v1; + m_edges[name].m_v2 = v2; + m_vertices[v1].AddEdge(static_cast(name)); + m_vertices[v2].AddEdge(static_cast(name)); + m_nE++; + return static_cast(name); + } + + bool Graph::DeleteEdge(long name) + { + if (name < static_cast(m_edges.size())) + { + long v1 = m_edges[name].m_v1; + long v2 = m_edges[name].m_v2; + m_edges[name].m_deleted = true; + m_vertices[v1].DeleteEdge(name); + m_vertices[v2].DeleteEdge(name); + m_nE--; + return true; + } + return false; + } + bool Graph::DeleteVertex(long name) + { + if (name < static_cast(m_vertices.size())) + { + m_vertices[name].m_deleted = true; + m_vertices[name].m_edges.Clear(); + m_vertices[name].m_ancestors = std::vector(); + delete m_vertices[name].m_convexHull; + m_vertices[name].m_distPoints.Clear(); + m_vertices[name].m_boudaryEdges.Clear(); + m_vertices[name].m_convexHull = 0; + m_nV--; + return true; + } + return false; + } + bool Graph::EdgeCollapse(long v1, long v2) + { + long edgeToDelete = GetEdgeID(v1, v2); + if (edgeToDelete >= 0) + { + // delete the edge (v1, v2) + DeleteEdge(edgeToDelete); + // add v2 to v1 ancestors + m_vertices[v1].m_ancestors.push_back(v2); + // add v2's ancestors to v1's ancestors + m_vertices[v1].m_ancestors.insert(m_vertices[v1].m_ancestors.begin(), + m_vertices[v2].m_ancestors.begin(), + m_vertices[v2].m_ancestors.end()); + // update adjacency information + SArray & v1Edges = m_vertices[v1].m_edges; + long b = -1; + long idEdge; + for(size_t ed = 0; ed < m_vertices[v2].m_edges.Size(); ++ed) + { + idEdge = m_vertices[v2].m_edges[ed]; + if (m_edges[idEdge].m_v1 == v2) + { + b = m_edges[idEdge].m_v2; + } + else + { + b = m_edges[idEdge].m_v1; + } + if (GetEdgeID(v1, b) >= 0) + { + m_edges[idEdge].m_deleted = true; + m_vertices[b].DeleteEdge(idEdge); + m_nE--; + } + else + { + m_edges[idEdge].m_v1 = v1; + m_edges[idEdge].m_v2 = b; + v1Edges.Insert(idEdge); + } + } + // delete the vertex v2 + DeleteVertex(v2); + return true; + } + return false; + } + + long Graph::GetEdgeID(long v1, long v2) const + { + if (v1 < static_cast(m_vertices.size()) && !m_vertices[v1].m_deleted) + { + long idEdge; + for(size_t ed = 0; ed < m_vertices[v1].m_edges.Size(); ++ed) + { + idEdge = m_vertices[v1].m_edges[ed]; + if ( (m_edges[idEdge].m_v1 == v2) || + (m_edges[idEdge].m_v2 == v2) ) + { + return m_edges[idEdge].m_name; + } + } + } + return -1; + } + + void Graph::Print() const + { + std::cout << "-----------------------------" << std::endl; + std::cout << "vertices (" << m_nV << ")" << std::endl; + for (size_t v = 0; v < m_vertices.size(); ++v) + { + const GraphVertex & currentVertex = m_vertices[v]; + if (!m_vertices[v].m_deleted) + { + + std::cout << currentVertex.m_name << "\t"; + long idEdge; + for(size_t ed = 0; ed < currentVertex.m_edges.Size(); ++ed) + { + idEdge = currentVertex.m_edges[ed]; + std::cout << "(" << m_edges[idEdge].m_v1 << "," << m_edges[idEdge].m_v2 << ") "; + } + std::cout << std::endl; + } + } + + std::cout << "vertices (" << m_nE << ")" << std::endl; + for (size_t e = 0; e < m_edges.size(); ++e) + { + const GraphEdge & currentEdge = m_edges[e]; + if (!m_edges[e].m_deleted) + { + std::cout << currentEdge.m_name << "\t(" + << m_edges[e].m_v1 << "," + << m_edges[e].m_v2 << ") "<< std::endl; + } + } + } + void Graph::Clear() + { + m_vertices.clear(); + m_edges.clear(); + m_nV = 0; + m_nE = 0; + } + + long Graph::ExtractCCs() + { + // all CCs to -1 + for (size_t v = 0; v < m_vertices.size(); ++v) + { + if (!m_vertices[v].m_deleted) + { + m_vertices[v].m_cc = -1; + } + } + + // we get the CCs + m_nCCs = 0; + long v2 = -1; + long idEdge; + std::vector temp; + for (size_t v = 0; v < m_vertices.size(); ++v) + { + if (!m_vertices[v].m_deleted && m_vertices[v].m_cc == -1) + { + m_vertices[v].m_cc = static_cast(m_nCCs); + temp.clear(); + temp.push_back(m_vertices[v].m_name); + while (temp.size()) + { + long vertex = temp[temp.size()-1]; + temp.pop_back(); + for(size_t ed = 0; ed < m_vertices[vertex].m_edges.Size(); ++ed) + { + idEdge = m_vertices[vertex].m_edges[ed]; + if (m_edges[idEdge].m_v1 == vertex) + { + v2 = m_edges[idEdge].m_v2; + } + else + { + v2 = m_edges[idEdge].m_v1; + } + if ( !m_vertices[v2].m_deleted && m_vertices[v2].m_cc == -1) + { + m_vertices[v2].m_cc = static_cast(m_nCCs); + temp.push_back(v2); + } + } + } + m_nCCs++; + } + } + return static_cast(m_nCCs); + } +} diff --git a/indra/libhacd/hacdGraph.h b/indra/libhacd/hacdGraph.h new file mode 100644 index 000000000..9d79bcf62 --- /dev/null +++ b/indra/libhacd/hacdGraph.h @@ -0,0 +1,118 @@ +/* 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 HACD_GRAPH_H +#define HACD_GRAPH_H +#include "hacdVersion.h" +#include "hacdVector.h" +#include "hacdICHull.h" +#include +#include +#include "hacdSArray.h" +//#define HACD_PRECOMPUTE_CHULLS + +namespace HACD +{ + class GraphVertex; + class GraphEdge; + class Graph; + class HACD; + + class GraphVertex + { + public: + bool AddEdge(long name) + { + m_edges.Insert(name); + return true; + } + bool DeleteEdge(long name); + GraphVertex(); + ~GraphVertex(){ delete m_convexHull;}; + private: + long m_name; + long m_cc; + SArray m_edges; + bool m_deleted; + std::vector m_ancestors; + SArray m_distPoints; + + Real m_concavity; + double m_surf; + ICHull * m_convexHull; + SArray m_boudaryEdges; + + + friend class GraphEdge; + friend class Graph; + friend class HACD; + }; + + class GraphEdge + { + public: + GraphEdge(); + ~GraphEdge() + { +#ifdef HACD_PRECOMPUTE_CHULLS + delete m_convexHull; +#endif + }; + private: + long m_name; + long m_v1; + long m_v2; + double m_concavity; + Real m_error; +#ifdef HACD_PRECOMPUTE_CHULLS + ICHull * m_convexHull; +#endif + bool m_deleted; + + friend class GraphVertex; + friend class Graph; + friend class HACD; + }; + + class Graph + { + public: + size_t GetNEdges() const { return m_nE;} + size_t GetNVertices() const { return m_nV;} + bool EdgeCollapse(long v1, long v2); + long AddVertex(); + long AddEdge(long v1, long v2); + bool DeleteEdge(long name); + bool DeleteVertex(long name); + long GetEdgeID(long v1, long v2) const; + void Clear(); + void Print() const; + long ExtractCCs(); + + Graph(); + virtual ~Graph(); + void Allocate(size_t nV, size_t nE); + + private: + size_t m_nCCs; + size_t m_nV; + size_t m_nE; + std::vector m_edges; + std::vector m_vertices; + + friend class HACD; + }; +} +#endif diff --git a/indra/libhacd/hacdHACD.cpp b/indra/libhacd/hacdHACD.cpp new file mode 100644 index 000000000..afc04033c --- /dev/null +++ b/indra/libhacd/hacdHACD.cpp @@ -0,0 +1,1140 @@ +/* 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 +#include "hacdGraph.h" +#include "hacdHACD.h" +#include "hacdICHull.h" +#include +#include +#include +#include +#include "hacdMeshDecimator.h" +#include "hacdRaycastMesh.h" +//#define HACD_DEBUG +namespace HACD +{ + double HACD::Concavity(ICHull & ch, std::map & distPoints) + { + double concavity = 0.0; + double distance = 0.0; + std::map::iterator itDP(distPoints.begin()); + std::map::iterator itDPEnd(distPoints.end()); + long pt; + const long nPoints = static_cast(m_nPoints); + const double eps = -0.001; + for(; itDP != itDPEnd; ++itDP) + { + if (!(itDP->second).m_computed) + { + if (itDP->first >= nPoints) + { + pt = itDP->first - nPoints; + if ( ch.IsInside(m_extraDistPoints[pt], eps)) + { + distance = ch.ComputeDistance(itDP->first, m_extraDistPoints[pt], m_extraDistNormals[pt], (itDP->second).m_computed, true); + } + else + { + distance = 0.0; + } + } + else if (itDP->first >= 0) + { + pt = itDP->first; + distance = ch.ComputeDistance(itDP->first, m_points[pt], m_normals[pt], (itDP->second).m_computed, true); + } + else + { + pt = -itDP->first-1; + distance = ch.ComputeDistance(itDP->first, m_facePoints[pt], m_faceNormals[pt], (itDP->second).m_computed, true); + } + } + else + { + distance = (itDP->second).m_dist; + } + + if (concavity < distance) + { + concavity = distance; + } + } + return concavity; + } + + void HACD::CreateGraph() + { + // vertex to triangle adjacency information + std::vector< std::set > vertexToTriangles; + vertexToTriangles.resize(m_nPoints); + for(size_t t = 0; t < m_nTriangles; ++t) + { + vertexToTriangles[m_triangles[t].X()].insert(static_cast(t)); + vertexToTriangles[m_triangles[t].Y()].insert(static_cast(t)); + vertexToTriangles[m_triangles[t].Z()].insert(static_cast(t)); + } + + m_graph.Clear(); + m_graph.Allocate(m_nTriangles, 5 * m_nTriangles); + unsigned long long tr1[3]; + unsigned long long tr2[3]; + long i1, j1, k1, i2, j2, k2; + long t1, t2; + for (size_t v = 0; v < m_nPoints; v++) + { + std::set::const_iterator it1(vertexToTriangles[v].begin()), itEnd(vertexToTriangles[v].end()); + for(; it1 != itEnd; ++it1) + { + t1 = *it1; + i1 = m_triangles[t1].X(); + j1 = m_triangles[t1].Y(); + k1 = m_triangles[t1].Z(); + tr1[0] = GetEdgeIndex(i1, j1); + tr1[1] = GetEdgeIndex(j1, k1); + tr1[2] = GetEdgeIndex(k1, i1); + std::set::const_iterator it2(it1); + for(++it2; it2 != itEnd; ++it2) + { + t2 = *it2; + i2 = m_triangles[t2].X(); + j2 = m_triangles[t2].Y(); + k2 = m_triangles[t2].Z(); + tr2[0] = GetEdgeIndex(i2, j2); + tr2[1] = GetEdgeIndex(j2, k2); + tr2[2] = GetEdgeIndex(k2, i2); + int shared = 0; + for(int i = 0; i < 3; ++i) + { + for(int j = 0; j < 3; ++j) + { + if (tr1[i] == tr2[j]) + { + shared++; + } + } + } + if (shared == 1) // two triangles are connected if they share exactly one edge + { + m_graph.AddEdge(t1, t2); + } + } + } + } + if (m_ccConnectDist >= 0.0) + { + m_graph.ExtractCCs(); + if (m_callBack) + { + char msg[1024]; + sprintf(msg, "nCC %lu\n", m_graph.m_nCCs); + (*m_callBack)(msg, 0.0, 0.0, m_graph.GetNVertices()); + + } + + if (m_graph.m_nCCs > 1) + { + std::vector< std::set > cc2V; + cc2V.resize(m_graph.m_nCCs); + long cc; + for(size_t t = 0; t < m_nTriangles; ++t) + { + cc = m_graph.m_vertices[t].m_cc; + cc2V[cc].insert(m_triangles[t].X()); + cc2V[cc].insert(m_triangles[t].Y()); + cc2V[cc].insert(m_triangles[t].Z()); + } + + for(size_t cc1 = 0; cc1 < m_graph.m_nCCs; ++cc1) + { + for(size_t cc2 = cc1+1; cc2 < m_graph.m_nCCs; ++cc2) + { + std::set::const_iterator itV1(cc2V[cc1].begin()), itVEnd1(cc2V[cc1].end()); + for(; itV1 != itVEnd1; ++itV1) + { + double distC1C2 = std::numeric_limits::max(); + double dist; + t1 = -1; + t2 = -1; + std::set::const_iterator itV2(cc2V[cc2].begin()), itVEnd2(cc2V[cc2].end()); + for(; itV2 != itVEnd2; ++itV2) + { + dist = (m_points[*itV1] - m_points[*itV2]).GetNorm(); + if (dist < distC1C2) + { + distC1C2 = dist; + t1 = *vertexToTriangles[*itV1].begin(); + + std::set::const_iterator it2(vertexToTriangles[*itV2].begin()), + it2End(vertexToTriangles[*itV2].end()); + t2 = -1; + for(; it2 != it2End; ++it2) + { + if (*it2 != t1) + { + t2 = *it2; + break; + } + } + } + } + if (distC1C2 <= m_ccConnectDist && t1 >= 0 && t2 >= 0) + { + m_graph.AddEdge(t1, t2); + } + } + } + } + } + } + } + void HACD::InitializeDualGraph() + { + long i, j, k; + Vec3 u, v, w, normal; + delete [] m_normals; + m_normals = new Vec3[m_nPoints]; + if (m_addFacesPoints) + { + delete [] m_facePoints; + delete [] m_faceNormals; + + m_facePoints = new Vec3[m_nTriangles]; + m_faceNormals = new Vec3[m_nTriangles]; +/* + m_facePoints = new Vec3[4*m_nTriangles]; + m_faceNormals = new Vec3[4*m_nTriangles]; +*/ + } + memset(m_normals, 0, sizeof(Vec3) * m_nPoints); + + RaycastMesh rm; + if (m_addExtraDistPoints) + { + rm.Initialize(m_nPoints, m_nTriangles, m_points, m_triangles, 15); + m_extraDistPoints = new Vec3[m_nTriangles]; + m_extraDistNormals = new Vec3[m_nTriangles]; + } + double progressOld = -1.0; + double progress = 0.0; + char msg[1024]; + double ptgStep = 1.0; + m_area = 0.0; + for(unsigned long f = 0; f < m_nTriangles; f++) + { + progress = f * 100.0 / m_nTriangles; + if (fabs(progress-progressOld) > ptgStep && m_callBack) + { + sprintf(msg, "%3.2f %% \t \t \r", progress); + (*m_callBack)(msg, progress, 0.0, m_nTriangles); + progressOld = progress; + } + + i = m_triangles[f].X(); + j = m_triangles[f].Y(); + k = m_triangles[f].Z(); + m_graph.m_vertices[f].m_distPoints.PushBack(DPoint(i, 0, false, false)); + m_graph.m_vertices[f].m_distPoints.PushBack(DPoint(j, 0, false, false)); + m_graph.m_vertices[f].m_distPoints.PushBack(DPoint(k, 0, false, false)); + + ICHull * ch = new ICHull(m_heapManager); + m_graph.m_vertices[f].m_convexHull = ch; + ch->AddPoint(m_points[i], i); + ch->AddPoint(m_points[j], j); + ch->AddPoint(m_points[k], k); + ch->SetDistPoints(0); + + u = m_points[j] - m_points[i]; + v = m_points[k] - m_points[i]; + w = m_points[k] - m_points[j]; + normal = u ^ v; + + m_normals[i] += normal; + m_normals[j] += normal; + m_normals[k] += normal; + + m_graph.m_vertices[f].m_surf = normal.GetNorm(); + m_area += m_graph.m_vertices[f].m_surf; + normal.Normalize(); + m_graph.m_vertices[f].m_boudaryEdges.Insert(GetEdgeIndex(i,j)); + m_graph.m_vertices[f].m_boudaryEdges.Insert(GetEdgeIndex(j,k)); + m_graph.m_vertices[f].m_boudaryEdges.Insert(GetEdgeIndex(k,i)); + if(m_addFacesPoints) + { + m_faceNormals[f] = normal; + m_facePoints[f] = (m_points[i] + m_points[j] + m_points[k]) / 3.0; + m_graph.m_vertices[f].m_distPoints.PushBack(DPoint(-static_cast(f)-1, 0, false, true)); +/* + m_facePoints[m_nTriangles + f] = (m_points[i] + m_points[j]) / 2.0; + m_graph.m_vertices[f].m_distPoints.PushBack(DPoint(-static_cast(m_nTriangles + f)-1, 0, false, true)); + m_facePoints[2*m_nTriangles + f] = (m_points[i] + m_points[k]) / 2.0; + m_graph.m_vertices[f].m_distPoints.PushBack(DPoint(-static_cast(2*m_nTriangles + f)-1, 0, false, true)); + m_facePoints[3*m_nTriangles + f] = (m_points[j] + m_points[k]) / 2.0; + m_graph.m_vertices[f].m_distPoints.PushBack(DPoint(-static_cast(3*m_nTriangles + f)-1, 0, false, true)); +*/ + } + if (m_addExtraDistPoints) + { + Vec3 seedPoint((m_points[i] + m_points[j] + m_points[k]) / 3.0); + Vec3 hitPoint; + Vec3 hitNormal; + normal = -normal; + size_t faceIndex = m_nTriangles; + + Float dist; + long hitTriangle; + if (rm.Raycast(seedPoint,normal,hitTriangle,dist, hitPoint, hitNormal)) + { + faceIndex = hitTriangle; + } +/* + Vec3 u1, v1, normal1; + double distance = 0.0; + double distMin = 0.0; + + long nhit = 0; + for(size_t f1 = 0; f1 < m_nTriangles; f1++) + { + i1 = m_triangles[f1].X(); + j1 = m_triangles[f1].Y(); + k1 = m_triangles[f1].Z(); + + u1 = m_points[j1] - m_points[i1]; + v1 = m_points[k1] - m_points[i1]; + normal1 = (u1 ^ v1); + if (normal * normal1 > 0.0) + { + nhit = IntersectRayTriangle(Vec3(seedPoint.X(), seedPoint.Y(), seedPoint.Z()), + Vec3(normal.X(), normal.Y(), normal.Z()), + Vec3(m_points[i1].X(), m_points[i1].Y(), m_points[i1].Z()), + Vec3(m_points[j1].X(), m_points[j1].Y(), m_points[j1].Z()), + Vec3(m_points[k1].X(), m_points[k1].Y(), m_points[k1].Z()), + distance); + if ((nhit==1) && ((distMin > distance) || (faceIndex == m_nTriangles))) + { + distMin = distance; + faceIndex = f1; + } + } + } + + if ( faceIndex != hitTriangle) + { + printf("%i %i %i\n", f, faceIndex, hitTriangle); + } +*/ + if (faceIndex < m_nTriangles ) + { + m_extraDistPoints[f] = hitPoint; + m_extraDistNormals[f] = hitNormal; + m_graph.m_vertices[f].m_distPoints.PushBack(DPoint(m_nPoints+f, 0, false, true)); + } + } + } + for (size_t v = 0; v < m_nPoints; v++) + { + m_normals[v].Normalize(); + } + } + + void HACD::NormalizeData() + { + if (m_nPoints == 0) + { + return; + } + m_barycenter = m_points[0]; + Vec3 min = m_points[0]; + Vec3 max = m_points[0]; + Real x, y, z; + for (size_t v = 1; v < m_nPoints ; v++) + { + m_barycenter += m_points[v]; + x = m_points[v].X(); + y = m_points[v].Y(); + z = m_points[v].Z(); + if ( x < min.X()) min.X() = x; + else if ( x > max.X()) max.X() = x; + if ( y < min.Y()) min.Y() = y; + else if ( y > max.Y()) max.Y() = y; + if ( z < min.Z()) min.Z() = z; + else if ( z > max.Z()) max.Z() = z; + } + m_barycenter /= static_cast(m_nPoints); + m_diag = (max-min).GetNorm(); + const Real invDiag = static_cast(2.0 * m_scale / m_diag); + if (m_diag != 0.0) + { + for (size_t v = 0; v < m_nPoints ; v++) + { + m_points[v] = (m_points[v] - m_barycenter) * invDiag; + } + } + } + void HACD::DenormalizeData() + { + if (m_nPoints == 0) + { + return; + } + if (m_diag != 0.0) + { + const Real diag = static_cast(m_diag / (2.0 * m_scale)); + for (size_t v = 0; v < m_nPoints ; v++) + { + m_points[v] = m_points[v] * diag + m_barycenter; + } + } + } + HACD::HACD(HeapManager * heapManager):m_heapManager(heapManager) + { + m_extraDistPoints = 0; + m_extraDistNormals = 0;; + m_convexHulls = 0; + m_trianglesDecimated = 0; + m_pointsDecimated = 0; + m_nTrianglesDecimated = 0; + m_nPointsDecimated = 0; + m_triangles = 0; + m_points = 0; + m_normals = 0; + m_nTriangles = 0; + m_nPoints = 0; + m_nClusters = 0; + m_concavity = 0.0; + m_diag = 1.0; + m_barycenter = Vec3(0.0, 0.0,0.0); + m_alpha = 0.0; + m_beta = 0.0; + m_gamma = 0.01; + m_nVerticesPerCH = 30; + m_callBack = 0; + m_addExtraDistPoints = false; + m_scale = 1000.0; + m_partition = 0; + m_nMinClusters = 3; + m_facePoints = 0; + m_faceNormals = 0; + m_ccConnectDist = 30; + m_targetNTrianglesDecimatedMesh = 1000; + m_flatRegionThreshold = 1.0; + m_smallClusterThreshold = 0.25; + m_area = 0.0; + } + HACD::~HACD(void) + { + delete [] m_normals; + delete [] m_convexHulls; + delete [] m_partition; + delete [] m_facePoints; + delete [] m_faceNormals; + delete [] m_trianglesDecimated; + delete [] m_pointsDecimated; + delete [] m_extraDistPoints; + delete [] m_extraDistNormals; + } + + void HACD::ComputeEdgeCost(size_t e) + { + GraphEdge & gE = m_graph.m_edges[e]; + long v1 = gE.m_v1; + long v2 = gE.m_v2; + + if (m_graph.m_vertices[v2].m_ancestors.size()>m_graph.m_vertices[v1].m_ancestors.size()) + { + gE.m_v1 = v2; + gE.m_v2 = v1; + std::swap(v1, v2); + } + GraphVertex & gV1 = m_graph.m_vertices[v1]; + GraphVertex & gV2 = m_graph.m_vertices[v2]; +#ifdef HACD_DEBUG + if (v1 == 308 && v2==276) + { + gV1.m_convexHull->m_mesh.Save("debug1.wrl"); + gV2.m_convexHull->m_mesh.Save("debug2.wrl"); + } + +#endif + + // create the edge's convex-hull + ICHull * ch = new ICHull(m_heapManager); + (*ch) = (*gV1.m_convexHull); + // update distPoints +#ifdef HACD_PRECOMPUTE_CHULLS + delete gE.m_convexHull; + gE.m_convexHull = 0; +#endif + std::map distPoints; + for(size_t p = 0; p < gV1.m_distPoints.Size(); ++p) + { + distPoints[gV1.m_distPoints[p].m_name] = gV1.m_distPoints[p]; + } + + std::map::iterator itDP1; + for(size_t p = 0; p < gV2.m_distPoints.Size(); ++p) + { + const DPoint & point = gV2.m_distPoints[p]; + itDP1 = distPoints.find(point.m_name); + if (itDP1 == distPoints.end()) + { + DPoint newPoint(point.m_name, 0, false, point.m_distOnly); + distPoints.insert(std::pair(point.m_name, newPoint)); + if ( !point.m_distOnly ) + { + ch->AddPoint(m_points[point.m_name], point.m_name); + } + } + else + { + if ( (itDP1->second).m_distOnly && !point.m_distOnly) + { + (itDP1->second).m_distOnly = false; + ch->AddPoint(m_points[point.m_name], point.m_name); + } + } + } + + ch->SetDistPoints(&distPoints); + // create the convex-hull + while (ch->Process() == ICHullErrorInconsistent) // if we face problems when constructing the visual-hull. really ugly!!!! + { +// if (m_callBack) (*m_callBack)("\t Problem with convex-hull construction [HACD::ComputeEdgeCost]\n", 0.0, 0.0, 0); + ICHull * chOld = ch; + ch = new ICHull(m_heapManager); + CircularList & verticesCH = chOld->GetMesh().m_vertices; + size_t nV = verticesCH.GetSize(); + long ptIndex = 0; + verticesCH.Next(); + // add noise to avoid the problem + ptIndex = verticesCH.GetHead()->GetData().m_name; + ch->AddPoint(m_points[ptIndex]+ m_scale * 0.0001 * Vec3(rand() % 10 - 5, rand() % 10 - 5, rand() % 10 - 5), ptIndex); + for(size_t v = 1; v < nV; ++v) + { + ptIndex = verticesCH.GetHead()->GetData().m_name; + ch->AddPoint(m_points[ptIndex], ptIndex); + verticesCH.Next(); + } + delete chOld; + } +#ifdef HACD_DEBUG + if (v1 == 438 && v2==468) + { + const long nPoints = static_cast(m_nPoints); + std::map::iterator itDP(distPoints.begin()); + std::map::iterator itDPEnd(distPoints.end()); + for(; itDP != itDPEnd; ++itDP) + { + + if (itDP->first >= nPoints) + { + long pt = itDP->first - nPoints; + ch->AddPoint(m_extraDistPoints[pt], itDP->first); + } + else if (itDP->first >= 0) + { + long pt = itDP->first; + ch->AddPoint(m_points[pt], itDP->first); + } + else + { + long pt = -itDP->first-1; + ch->AddPoint(m_facePoints[pt], itDP->first); + ch->AddPoint(m_facePoints[pt] + 10.0 * m_faceNormals[pt] , itDP->first); + } + } + printf("-***->\n"); + + ch->m_mesh.Save("debug.wrl"); + } +#endif + double surf = gV1.m_surf + gV2.m_surf; + double concavity = 0.0; + double surfCH = ch->ComputeArea() / 2.0; + double volumeCH = ch->ComputeVolume(); + double vol2Surf = volumeCH / surfCH; + double concavity_flat = sqrt(fabs(surfCH-surf)); + double weightFlat = std::max(0.0, 1.0 - pow(- vol2Surf * 100.0 / (m_scale * m_flatRegionThreshold), 2.0)); + +// concavity_flat *= std::max(exp(- vol2Surf * 100.0 / (m_scale * m_flatRegionThreshold)) - exp(-1.0), 0.0); + concavity_flat *= weightFlat; + if(!ch->IsFlat()) + { + concavity = Concavity(*ch, distPoints); + } + concavity += concavity_flat; +#ifdef HACD_PRECOMPUTE_CHULLS + gE.m_convexHull = ch; +#else + delete ch; +#endif + + // compute boudary edges + double perimeter = 0.0; + if (m_alpha > 0.0) + { + std::set boudaryEdges1; + for(size_t edV1 = 0; edV1 < gV1.m_boudaryEdges.Size(); ++edV1) + { + boudaryEdges1.insert(gV1.m_boudaryEdges[edV1]); + } + std::set boudaryEdges2; + for(size_t edV2 = 0; edV2 < gV2.m_boudaryEdges.Size(); ++edV2) + { + boudaryEdges2.insert(gV2.m_boudaryEdges[edV2]); + } + std::set boudaryEdges; + std::set_symmetric_difference (boudaryEdges1.begin(), + boudaryEdges1.end(), + boudaryEdges2.begin(), + boudaryEdges2.end(), + std::inserter( boudaryEdges, boudaryEdges.begin() ) ); + + std::set::const_iterator itBE(boudaryEdges.begin()); + std::set::const_iterator itBEEnd(boudaryEdges.end()); + for(; itBE != itBEEnd; ++itBE) + { + perimeter += (m_points[static_cast((*itBE) >> 32)] - + m_points[static_cast((*itBE) & 0xFFFFFFFFULL)]).GetNorm(); + } + } + double ratio = perimeter * perimeter / (4.0 * sc_pi * surf); + gE.m_concavity = concavity; // cluster's concavity + double volume = volumeCH/pow(m_scale, 3.0); // cluster's volume + gE.m_error = static_cast(concavity + m_alpha * (1.0 - weightFlat) * ratio + m_beta * volume + m_gamma * static_cast(distPoints.size()) / m_nPoints); // cluster's priority + } + bool HACD::InitializePriorityQueue() + { + m_pqueue.reserve(m_graph.m_nE + 100); + for (size_t e=0; e < m_graph.m_nE; ++e) + { + ComputeEdgeCost(static_cast(e)); + m_pqueue.push(GraphEdgePriorityQueue(static_cast(e), m_graph.m_edges[e].m_error)); + } + return true; + } + void HACD::Simplify() + { + double areaThreshold = m_area * m_smallClusterThreshold / 100.0; + long v1 = -1; + long v2 = -1; + double progressOld = -1.0; + double progress = 0.0; + double globalConcavity = 0.0; + char msg[1024]; + double ptgStep = 1.0; + while ( !m_pqueue.empty() ) + { + + progress = 100.0-m_graph.GetNVertices() * 100.0 / m_nTriangles; + if (fabs(progress-progressOld) > ptgStep && m_callBack) + { + sprintf(msg, "%3.2f %% V = %lu \t C = %f \t \t \r", progress, static_cast(m_graph.GetNVertices()), globalConcavity); + (*m_callBack)(msg, progress, globalConcavity, m_graph.GetNVertices()); + progressOld = progress; + if (progress > 99.0) + { + ptgStep = 0.01; + } + else if (progress > 90.0) + { + ptgStep = 0.1; + } + } + + GraphEdgePriorityQueue currentEdge(0,0.0); + bool done = false; + do + { + done = false; + if (m_pqueue.size() == 0) + { + done = true; + break; + } + currentEdge = m_pqueue.top(); + m_pqueue.pop(); + } + while ( m_graph.m_edges[currentEdge.m_name].m_deleted || + m_graph.m_edges[currentEdge.m_name].m_error != currentEdge.m_priority); + + if (!done) + { + v1 = m_graph.m_edges[currentEdge.m_name].m_v1; + v2 = m_graph.m_edges[currentEdge.m_name].m_v2; + bool condition1 = (m_graph.m_edges[currentEdge.m_name].m_concavity < m_concavity) && (globalConcavity < m_concavity) && (m_graph.GetNVertices() > m_nMinClusters) && (m_graph.GetNEdges() > 0); + bool condition2 = (m_graph.m_vertices[v1].m_surf < areaThreshold || m_graph.m_vertices[v2].m_surf < areaThreshold); + if (condition1 || condition2) + { + if ((!condition1) && m_callBack) + { + sprintf(msg, "\n-> %lu\t%f\t%f\t%f\n", m_pqueue.size(), m_graph.m_vertices[v1].m_surf*100.0/m_area, m_graph.m_vertices[v2].m_surf*100.0/m_area, m_graph.m_edges[currentEdge.m_name].m_concavity); + (*m_callBack)(msg, progress, globalConcavity, m_graph.GetNVertices()); + } + globalConcavity = std::max(globalConcavity ,m_graph.m_edges[currentEdge.m_name].m_concavity); + GraphEdge & gE = m_graph.m_edges[currentEdge.m_name]; + GraphVertex & gV1 = m_graph.m_vertices[v1]; + GraphVertex & gV2 = m_graph.m_vertices[v2]; + // update vertex info + gV1.m_concavity = gE.m_concavity; + #ifdef HACD_PRECOMPUTE_CHULLS + (*gV1.m_convexHull) = (*gE.m_convexHull); + (gV1.m_convexHull)->SetDistPoints(0); + // update distPoints + std::map distPoints; + for(size_t p = 0; p < gV1.m_distPoints.Size(); ++p) + { + distPoints[gV1.m_distPoints[p].m_name] = gV1.m_distPoints[p]; + } + + std::map::iterator itDP1; + for(size_t p = 0; p < gV2.m_distPoints.Size(); ++p) + { + const DPoint & point = gV2.m_distPoints[p]; + itDP1 = distPoints.find(point.m_name); + if (itDP1 == distPoints.end()) + { + DPoint newPoint(point.m_name, 0, false, point.m_distOnly); + distPoints.insert(std::pair(point.m_name, newPoint)); + } + else + { + if ( (itDP1->second).m_distOnly && !point.m_distOnly) + { + (itDP1->second).m_distOnly = false; + } + } + } + gV1.m_distPoints.Clear(); + gV1.m_distPoints.Resize(distPoints.size()); + std::map::iterator itDP(distPoints.begin()); + std::map::iterator itDPEnd(distPoints.end()); + for(; itDP != itDPEnd; ++itDP) + { + const DPoint & point = itDP->second; + gV1.m_distPoints.PushBack(itDP->second); + } + #else + ICHull * ch = gV1.m_convexHull; + + ch->SetDistPoints(0); + // update distPoints + std::map distPoints; + for(size_t p = 0; p < gV1.m_distPoints.Size(); ++p) + { + distPoints[gV1.m_distPoints[p].m_name] = gV1.m_distPoints[p]; + } + + std::map::iterator itDP1; + for(size_t p = 0; p < gV2.m_distPoints.Size(); ++p) + { + const DPoint & point = gV2.m_distPoints[p]; + itDP1 = distPoints.find(point.m_name); + if (itDP1 == distPoints.end()) + { + DPoint newPoint(point.m_name, 0, false, point.m_distOnly); + distPoints.insert(std::pair(point.m_name, newPoint)); + if ( !point.m_distOnly ) + { + ch->AddPoint(m_points[point.m_name], point.m_name); + } + } + else + { + if ( (itDP1->second).m_distOnly && !point.m_distOnly) + { + (itDP1->second).m_distOnly = false; + ch->AddPoint(m_points[point.m_name], point.m_name); + } + } + } + gV1.m_distPoints.Clear(); + gV1.m_distPoints.Resize(distPoints.size()); + std::map::iterator itDP(distPoints.begin()); + std::map::iterator itDPEnd(distPoints.end()); + for(; itDP != itDPEnd; ++itDP) + { + gV1.m_distPoints.PushBack(itDP->second); + } + ch->SetDistPoints(0); + while (ch->Process() == ICHullErrorInconsistent) // if we face problems when constructing the visual-hull. really ugly!!!! + { + // if (m_callBack) (*m_callBack)("\t Problem with convex-hull construction [HACD::ComputeEdgeCost]\n", 0.0, 0.0, 0); + ICHull * chOld = ch; + ch = new ICHull(m_heapManager); + CircularList & verticesCH = chOld->GetMesh().m_vertices; + size_t nV = verticesCH.GetSize(); + long ptIndex = 0; + verticesCH.Next(); + // add noise to avoid the problem + ptIndex = verticesCH.GetHead()->GetData().m_name; + ch->AddPoint(m_points[ptIndex]+ m_scale * 0.0001 * Vec3(rand() % 10 - 5, rand() % 10 - 5, rand() % 10 - 5), ptIndex); + for(size_t v = 1; v < nV; ++v) + { + ptIndex = verticesCH.GetHead()->GetData().m_name; + ch->AddPoint(m_points[ptIndex], ptIndex); + verticesCH.Next(); + } + gV1.m_convexHull = ch; + delete chOld; + } + + #ifdef HACD_DEBUG + if (v1 == 90 && v2==98) + { + const long nPoints = static_cast(m_nPoints); + std::map::iterator itDP(distPoints.begin()); + std::map::iterator itDPEnd(distPoints.end()); + for(; itDP != itDPEnd; ++itDP) + { + + if (itDP->first >= nPoints) + { + long pt = itDP->first - nPoints; + ch->AddPoint(m_extraDistPoints[pt], itDP->first); + } + else if (itDP->first >= 0) + { + long pt = itDP->first; + ch->AddPoint(m_points[pt], itDP->first); + } + else + { + long pt = -itDP->first-1; + ch->AddPoint(m_facePoints[pt], itDP->first); + ch->AddPoint(m_facePoints[pt] + 10.0 * m_faceNormals[pt] , itDP->first); + } + } + printf("-***->\n"); + + ch->m_mesh.Save("debug.wrl"); + } + #endif + + #endif + if (m_alpha > 0.0) + { + std::set boudaryEdges1; + for(size_t edV1 = 0; edV1 < gV1.m_boudaryEdges.Size(); ++edV1) + { + boudaryEdges1.insert(gV1.m_boudaryEdges[edV1]); + } + std::set boudaryEdges2; + for(size_t edV2 = 0; edV2 < gV2.m_boudaryEdges.Size(); ++edV2) + { + boudaryEdges2.insert(gV2.m_boudaryEdges[edV2]); + } + std::set boudaryEdges; + std::set_symmetric_difference (boudaryEdges1.begin(), + boudaryEdges1.end(), + boudaryEdges2.begin(), + boudaryEdges2.end(), + std::inserter( boudaryEdges, boudaryEdges.begin() ) ); + gV1.m_boudaryEdges.Clear(); + std::set::const_iterator itBE(boudaryEdges.begin()); + std::set::const_iterator itBEEnd(boudaryEdges.end()); + for(; itBE != itBEEnd; ++itBE) + { + gV1.m_boudaryEdges.Insert(*itBE); + } + } + gV1.m_surf += gV2.m_surf; + + #ifdef HACD_DEBUG + printf("v1 %i v2 %i \n", v1, v2); + #endif + m_graph.EdgeCollapse(v1, v2); + long idEdge; + for(size_t itE = 0; itE < m_graph.m_vertices[v1].m_edges.Size(); ++itE) + { + idEdge = m_graph.m_vertices[v1].m_edges[itE]; + ComputeEdgeCost(idEdge); + m_pqueue.push(GraphEdgePriorityQueue(idEdge, m_graph.m_edges[idEdge].m_error)); + } + } + } + else + { + break; + } + } + m_cVertices.clear(); + m_nClusters = m_graph.GetNVertices(); + m_cVertices.reserve(m_nClusters); + for (size_t p=0, v = 0; v != m_graph.m_vertices.size(); ++v) + { + if (!m_graph.m_vertices[v].m_deleted) + { + if (m_callBack) + { + char msg[1024]; + sprintf(msg, "\t CH(%lu) \t %lu \t %lf \t %lu \t %f \t %lu\n", v, static_cast(p), m_graph.m_vertices[v].m_concavity, m_graph.m_vertices[v].m_distPoints.Size(), m_graph.m_vertices[v].m_surf*100.0/m_area, m_graph.m_vertices[v].m_ancestors.size()); + (*m_callBack)(msg, 0.0, 0.0, m_nClusters); + p++; + } + m_cVertices.push_back(static_cast(v)); + } + } + if (m_callBack) + { + sprintf(msg, "# clusters = %lu \t C = %f\n", static_cast(m_nClusters), globalConcavity); + (*m_callBack)(msg, progress, globalConcavity, m_graph.GetNVertices()); + } + + } + + bool HACD::Compute(bool fullCH, bool exportDistPoints) + { + if ( !m_points || !m_triangles || !m_nPoints || !m_nTriangles) + { + return false; + } + + Vec3 * pointsOld = m_points; + Vec3 * triangles = m_triangles; + size_t nTrianglesOld = m_nTriangles; + size_t PointsOld = m_nPoints; + bool decimatedMeshComputed = false; + if (m_targetNTrianglesDecimatedMesh > 0 && m_targetNTrianglesDecimatedMesh < m_nTriangles) + { + decimatedMeshComputed = true; + MeshDecimator myMDecimator; + myMDecimator.SetCallBack(m_callBack); + myMDecimator.Initialize(m_nPoints, m_nTriangles,m_points, m_triangles); + myMDecimator.Decimate(0, m_targetNTrianglesDecimatedMesh); + m_nTriangles = myMDecimator.GetNTriangles(); + m_nPoints = myMDecimator.GetNVertices(); + m_points = new Vec3[m_nPoints]; + m_triangles = new Vec3[m_nTriangles]; + myMDecimator.GetMeshData(m_points, m_triangles); + } + size_t nV = m_nTriangles; + + if (m_callBack) + { + std::ostringstream msg; + msg << "+ Mesh" << std::endl; + msg << "\t # vertices \t" << m_nPoints << std::endl; + msg << "\t # triangles \t" << m_nTriangles << std::endl; + msg << "+ Parameters" << std::endl; + msg << "\t min # of clusters \t" << m_nMinClusters << std::endl; + msg << "\t max concavity \t" << m_concavity << std::endl; + msg << "\t compacity weigth \t" << m_alpha << std::endl; + msg << "\t volume weigth \t" << m_beta << std::endl; + msg << "\t # vertices per convex-hull \t" << m_nVerticesPerCH << std::endl; + msg << "\t scale \t" << m_scale << std::endl; + msg << "\t add extra distance points \t" << m_addExtraDistPoints << std::endl; + msg << "\t add face distance points \t" << m_addFacesPoints << std::endl; + msg << "\t produce full convex-hulls \t" << fullCH << std::endl; + msg << "\t max. distance to connect CCs \t" << m_ccConnectDist << std::endl; + msg << "\t threshold for small clusters \t" << m_smallClusterThreshold << std::endl; + (*m_callBack)(msg.str().c_str(), 0.0, 0.0, nV); + } + if (m_callBack) (*m_callBack)("+ Normalizing Data\n", 0.0, 0.0, nV); + NormalizeData(); + if (m_callBack) (*m_callBack)("+ Creating Graph\n", 0.0, 0.0, nV); + CreateGraph(); + // Compute the surfaces and perimeters of all the faces + if (m_callBack) (*m_callBack)("+ Initializing Dual Graph\n", 0.0, 0.0, nV); + InitializeDualGraph(); + if (m_callBack) (*m_callBack)("+ Initializing Priority Queue\n", 0.0, 0.0, nV); + InitializePriorityQueue(); + // we simplify the graph + if (m_callBack) (*m_callBack)("+ Simplification ...\n", 0.0, 0.0, m_nTriangles); + Simplify(); + if (m_callBack) (*m_callBack)("+ Denormalizing Data\n", 0.0, 0.0, m_nClusters); + DenormalizeData(); + if (m_callBack) (*m_callBack)("+ Computing final convex-hulls\n", 0.0, 0.0, m_nClusters); + delete [] m_convexHulls; + m_convexHulls = new ICHull[m_nClusters]; + delete [] m_partition; + m_partition = new long [m_nTriangles]; + for (size_t p = 0; p != m_cVertices.size(); ++p) + { + size_t v = m_cVertices[p]; + m_partition[v] = static_cast(p); + for(size_t a = 0; a < m_graph.m_vertices[v].m_ancestors.size(); a++) + { + m_partition[m_graph.m_vertices[v].m_ancestors[a]] = static_cast(p); + } + // compute the convex-hull + for(size_t itCH = 0; itCH < m_graph.m_vertices[v].m_distPoints.Size(); ++itCH) + { + const DPoint & point = m_graph.m_vertices[v].m_distPoints[itCH]; + if (!point.m_distOnly) + { + m_convexHulls[p].AddPoint(m_points[point.m_name], point.m_name); + } + } + m_convexHulls[p].SetDistPoints(0); //&m_graph.m_vertices[v].m_distPoints + if (fullCH) + { + while (m_convexHulls[p].Process() == ICHullErrorInconsistent) // if we face problems when constructing the visual-hull. really ugly!!!! + { + ICHull * ch = new ICHull(m_heapManager); + CircularList & verticesCH = m_convexHulls[p].GetMesh().m_vertices; + size_t nV = verticesCH.GetSize(); + long ptIndex = 0; + verticesCH.Next(); + // add noise to avoid the problem + ptIndex = verticesCH.GetHead()->GetData().m_name; + ch->AddPoint(m_points[ptIndex]+ m_diag * 0.0001 * Vec3(rand() % 10 - 5, rand() % 10 - 5, rand() % 10 - 5), ptIndex); + for(size_t v = 1; v < nV; ++v) + { + ptIndex = verticesCH.GetHead()->GetData().m_name; + ch->AddPoint(m_points[ptIndex], ptIndex); + verticesCH.Next(); + } + m_convexHulls[p] = (*ch); + delete ch; + } + } + else + { + while ( m_convexHulls[p].Process(static_cast(m_nVerticesPerCH)) == ICHullErrorInconsistent) // if we face problems when constructing the visual-hull. really ugly!!!! + { + ICHull * ch = new ICHull(m_heapManager); + CircularList & verticesCH = m_convexHulls[p].GetMesh().m_vertices; + size_t nV = verticesCH.GetSize(); + long ptIndex = 0; + verticesCH.Next(); + // add noise to avoid the problem + ptIndex = verticesCH.GetHead()->GetData().m_name; + ch->AddPoint(m_points[ptIndex]+ m_diag * 0.0001 * Vec3(rand() % 10 - 5, rand() % 10 - 5, rand() % 10 - 5), ptIndex); + for(size_t v = 1; v < nV; ++v) + { + ptIndex = verticesCH.GetHead()->GetData().m_name; + ch->AddPoint(m_points[ptIndex], ptIndex); + verticesCH.Next(); + } + m_convexHulls[p] = (*ch); + delete ch; + } + } +#ifdef HACD_DEBUG + if (v==90) + { + m_convexHulls[p].m_mesh.Save("debug.wrl"); + } +#endif + if (exportDistPoints) + { + for(size_t itCH = 0; itCH < m_graph.m_vertices[v].m_distPoints.Size(); ++itCH) + { + const DPoint & point = m_graph.m_vertices[v].m_distPoints[itCH]; + if (point.m_distOnly) + { + if (point.m_name >= 0) + { + m_convexHulls[p].AddPoint(m_points[point.m_name], point.m_name); + } + else + { + m_convexHulls[p].AddPoint(m_facePoints[-point.m_name-1], point.m_name); + } + } + } + } + } + if (decimatedMeshComputed) + { + m_trianglesDecimated = m_triangles; + m_pointsDecimated = m_points; + m_nTrianglesDecimated = m_nTriangles; + m_nPointsDecimated = m_nPoints; + m_points = pointsOld; + m_triangles = triangles; + m_nTriangles = nTrianglesOld; + m_nPoints = PointsOld; + } + return true; + } + + size_t HACD::GetNTrianglesCH(size_t numCH) const + { + if (numCH >= m_nClusters) + { + return 0; + } + return m_convexHulls[numCH].GetMesh().GetNTriangles(); + } + size_t HACD::GetNPointsCH(size_t numCH) const + { + if (numCH >= m_nClusters) + { + return 0; + } + return m_convexHulls[numCH].GetMesh().GetNVertices(); + } + + bool HACD::GetCH(size_t numCH, Vec3 * const points, Vec3 * const triangles) + { + if (numCH >= m_nClusters) + { + return false; + } + m_convexHulls[numCH].GetMesh().GetIFS(points, triangles); + return true; + } + + bool HACD::Save(const char * fileName, bool uniColor, long numCluster) const + { + std::ofstream fout(fileName); + if (fout.is_open()) + { + if (m_callBack) + { + char msg[1024]; + sprintf(msg, "Saving %s\n", fileName); + (*m_callBack)(msg, 0.0, 0.0, m_graph.GetNVertices()); + } + Material mat; + if (numCluster < 0) + { + for (size_t p = 0; p != m_nClusters; ++p) + { + if (!uniColor) + { + mat.m_diffuseColor.X() = mat.m_diffuseColor.Y() = mat.m_diffuseColor.Z() = 0.0; + while (mat.m_diffuseColor.X() == mat.m_diffuseColor.Y() || + mat.m_diffuseColor.Z() == mat.m_diffuseColor.Y() || + mat.m_diffuseColor.Z() == mat.m_diffuseColor.X() ) + { + mat.m_diffuseColor.X() = (rand()%100) / 100.0; + mat.m_diffuseColor.Y() = (rand()%100) / 100.0; + mat.m_diffuseColor.Z() = (rand()%100) / 100.0; + } + } + m_convexHulls[p].GetMesh().SaveVRML2(fout, mat); + } + } + else if (numCluster < static_cast(m_cVertices.size())) + { + m_convexHulls[numCluster].GetMesh().SaveVRML2(fout, mat); + } + fout.close(); + return true; + } + else + { + if (m_callBack) + { + char msg[1024]; + sprintf(msg, "Error saving %s\n", fileName); + (*m_callBack)(msg, 0.0, 0.0, m_graph.GetNVertices()); + } + return false; + } + } +} diff --git a/indra/libhacd/hacdHACD.h b/indra/libhacd/hacdHACD.h new file mode 100644 index 000000000..5753e45ce --- /dev/null +++ b/indra/libhacd/hacdHACD.h @@ -0,0 +1,328 @@ +/* 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 HACD_HACD_H +#define HACD_HACD_H +#include "hacdVersion.h" +#include "hacdVector.h" +#include "hacdGraph.h" +#include "hacdICHull.h" +#include +#include +#include + +namespace HACD +{ + const double sc_pi = 3.14159265; + class HACD; + + // just to be able to set the capcity of the container + + template, class _Pr = std::less > + class reservable_priority_queue: public std::priority_queue<_Ty, _Container, _Pr> + { + typedef typename std::priority_queue<_Ty, _Container, _Pr>::size_type size_type; + public: + reservable_priority_queue(size_type capacity = 0) { reserve(capacity); }; + void reserve(size_type capacity) { this->c.reserve(capacity); } + size_type capacity() const { return this->c.capacity(); } + }; + + //! priority queque element + class HACD; + class GraphEdgePriorityQueue + { + public: + //! Constructor + //! @param name edge's id + //! @param priority edge's priority + GraphEdgePriorityQueue(long name, Real priority) + { + m_name = name; + m_priority = priority; + } + //! Destructor + ~GraphEdgePriorityQueue(void){} + private: + long m_name; //!< edge name + Real m_priority; //!< priority + //! Operator < for GraphEdgePQ + friend bool operator<(const GraphEdgePriorityQueue & lhs, const GraphEdgePriorityQueue & rhs); + //! Operator > for GraphEdgePQ + friend bool operator>(const GraphEdgePriorityQueue & lhs, const GraphEdgePriorityQueue & rhs); + friend class HACD; + }; + inline bool operator<(const GraphEdgePriorityQueue & lhs, const GraphEdgePriorityQueue & rhs) + { + return (lhs.m_priority(const GraphEdgePriorityQueue & lhs, const GraphEdgePriorityQueue & rhs) + { + return lhs.m_priority>rhs.m_priority; + } + class ICallback + { + public: + virtual void operator()( char const *aMsg, double aProgress, double aConcavity, size_t aVertices) = 0; + }; + + typedef ICallback* CallBackFunction; + + //! Provides an implementation of the Hierarchical Approximate Convex Decomposition (HACD) technique described in "A Simple and Efficient Approach for 3D Mesh Approximate Convex Decomposition" Game Programming Gems 8 - Chapter 2.8, p.202. A short version of the chapter was published in ICIP09 and is available at ftp://ftp.elet.polimi.it/users/Stefano.Tubaro/ICIP_USB_Proceedings_v2/pdfs/0003501.pdf + class HACD + { + public: + //! Gives the targeted number of triangles of the decimated mesh + //! @return targeted number of triangles of the decimated mesh + size_t GetTargetNTrianglesDecimatedMesh() const { return m_targetNTrianglesDecimatedMesh;} + //! Sets the targeted number of triangles of the decimated mesh + //! @param targeted number of triangles of the decimated mesh + void SetNTargetTrianglesDecimatedMesh(size_t targetNTrianglesDecimatedMesh) { m_targetNTrianglesDecimatedMesh = targetNTrianglesDecimatedMesh;} + //! Gives the triangles partitionas an array of size m_nTriangles where the i-th element specifies the cluster to which belong the i-th triangle + //! @return triangles partition + const long * const GetPartition() const { return m_partition;} + //! Sets the scale factor + //! @param scale scale factor + void SetScaleFactor(double scale) { m_scale = scale;} + //! Gives the scale factor + //! @return scale factor + const double GetScaleFactor() const { return m_scale;} + //! Sets the threshold to detect small clusters. The threshold is expressed as a percentage of the total mesh surface (default 0.25%) + //! @param smallClusterThreshold threshold to detect small clusters + void SetSmallClusterThreshold(double smallClusterThreshold) { m_smallClusterThreshold = smallClusterThreshold;} + //! Gives the threshold to detect small clusters. The threshold is expressed as a percentage of the total mesh surface (default 0.25%) + //! @return threshold to detect small clusters + const double GetSmallClusterThreshold() const { return m_smallClusterThreshold;} + //! Sets the call-back function + //! @param callBack pointer to the call-back function + void SetCallBack(CallBackFunction callBack) { m_callBack = callBack;} + //! Gives the call-back function + //! @return pointer to the call-back function + const CallBackFunction GetCallBack() const { return m_callBack;} + + //! Specifies whether faces points should be added when computing the concavity + //! @param addFacesPoints true = faces points should be added + void SetAddFacesPoints(bool addFacesPoints) { m_addFacesPoints = addFacesPoints;} + //! Specifies wheter faces points should be added when computing the concavity + //! @return true = faces points should be added + const bool GetAddFacesPoints() const { return m_addFacesPoints;} + //! Specifies whether extra points should be added when computing the concavity + //! @param addExteraDistPoints true = extra points should be added + void SetAddExtraDistPoints(bool addExtraDistPoints) { m_addExtraDistPoints = addExtraDistPoints;} + //! Specifies wheter extra points should be added when computing the concavity + //! @return true = extra points should be added + const bool GetAddExtraDistPoints() const { return m_addExtraDistPoints;} + //! Sets the points of the input mesh (Remark: the input points will be scaled and shifted. Use DenormalizeData() to invert those operations) + //! @param points pointer to the input points + void SetPoints(Vec3 * points) { m_points = points;} + //! Gives the points of the input mesh (Remark: the input points will be scaled and shifted. Use DenormalizeData() to invert those operations) + //! @return pointer to the input points + const Vec3 * GetPoints() const { return m_points;} + //! Gives the points of the decimated mesh + //! @return pointer to the decimated mesh points + const Vec3 * GetDecimatedPoints() const { return m_pointsDecimated;} + //! Gives the triangles in the decimated mesh + //! @return pointer to the decimated mesh triangles + const Vec3 * GetDecimatedTriangles() const { return m_trianglesDecimated;} + //! Gives the number of points in the decimated mesh. + //! @return number of points the decimated mesh mesh + const size_t GetNDecimatedPoints() const { return m_nPointsDecimated;} + //! Gives the number of triangles in the decimated mesh. + //! @return number of triangles the decimated mesh + const size_t GetNDecimatedTriangles() const { return m_nTrianglesDecimated;} + //! Sets the triangles of the input mesh. + //! @param triangles points pointer to the input points + void SetTriangles(Vec3 * triangles) { m_triangles = triangles;} + //! Gives the triangles in the input mesh + //! @return pointer to the input triangles + const Vec3 * GetTriangles() const { return m_triangles;} + //! Sets the number of points in the input mesh. + //! @param nPoints number of points the input mesh + void SetNPoints(size_t nPoints) { m_nPoints = nPoints;} + //! Gives the number of points in the input mesh. + //! @return number of points the input mesh + const size_t GetNPoints() const { return m_nPoints;} + //! Sets the number of triangles in the input mesh. + //! @param nTriangles number of triangles in the input mesh + void SetNTriangles(size_t nTriangles) { m_nTriangles = nTriangles;} + //! Gives the number of triangles in the input mesh. + //! @return number of triangles the input mesh + const size_t GetNTriangles() const { return m_nTriangles;} + //! Sets the minimum number of clusters to be generated. + //! @param nClusters minimum number of clusters + void SetNClusters(size_t nClusters) { m_nMinClusters = nClusters;} + //! Gives the number of generated clusters. + //! @return number of generated clusters + const size_t GetNClusters() const { return m_nClusters;} + //! Sets the maximum allowed concavity. + //! @param concavity maximum concavity + void SetConcavity(double concavity) { m_concavity = concavity;} + //! Gives the maximum allowed concavity. + //! @return maximum concavity + double GetConcavity() const { return m_concavity;} + //! Sets the maximum allowed distance to get CCs connected. + //! @param concavity maximum distance to get CCs connected + void SetConnectDist(double ccConnectDist) { m_ccConnectDist = ccConnectDist;} + //! Gives the maximum allowed distance to get CCs connected. + //! @return maximum distance to get CCs connected + double GetConnectDist() const { return m_ccConnectDist;} + //! Sets the volume weight. + //! @param beta volume weight + void SetVolumeWeight(double beta) { m_beta = beta;} + //! Gives the volume weight. + //! @return volume weight + double GetVolumeWeight() const { return m_beta;} + //! Sets the compacity weight (i.e. parameter alpha in ftp://ftp.elet.polimi.it/users/Stefano.Tubaro/ICIP_USB_Proceedings_v2/pdfs/0003501.pdf). + //! @param alpha compacity weight + void SetCompacityWeight(double alpha) { m_alpha = alpha;} + //! Gives the compacity weight (i.e. parameter alpha in ftp://ftp.elet.polimi.it/users/Stefano.Tubaro/ICIP_USB_Proceedings_v2/pdfs/0003501.pdf). + //! @return compacity weight + double GetCompacityWeight() const { return m_alpha;} + //! Sets the maximum number of vertices for each generated convex-hull. + //! @param nVerticesPerCH maximum # vertices per CH + void SetNVerticesPerCH(size_t nVerticesPerCH) { m_nVerticesPerCH = nVerticesPerCH;} + //! Gives the maximum number of vertices for each generated convex-hull. + //! @return maximum # vertices per CH + const size_t GetNVerticesPerCH() const { return m_nVerticesPerCH;} + //! Gives the number of vertices for the cluster number numCH. + //! @return number of vertices + size_t GetNPointsCH(size_t numCH) const; + //! Gives the number of triangles for the cluster number numCH. + //! @param numCH cluster's number + //! @return number of triangles + size_t GetNTrianglesCH(size_t numCH) const; + //! Gives the vertices and the triangles of the cluster number numCH. + //! @param numCH cluster's number + //! @param points pointer to the vector of points to be filled + //! @param triangles pointer to the vector of triangles to be filled + //! @return true if sucess + bool GetCH(size_t numCH, Vec3 * const points, Vec3 * const triangles); + //! Computes the HACD decomposition. + //! @param fullCH specifies whether to generate convex-hulls with a full or limited (i.e. < m_nVerticesPerCH) number of vertices + //! @param exportDistPoints specifies wheter distance points should ne exported or not (used only for debugging). + //! @return true if sucess + bool Compute(bool fullCH=false, bool exportDistPoints=false); + //! Saves the generated convex-hulls in a VRML 2.0 file. + //! @param fileName the output file name + //! @param uniColor specifies whether the different convex-hulls should have the same color or not + //! @param numCluster specifies the cluster to be saved, if numCluster < 0 export all clusters + //! @return true if sucess + bool Save(const char * fileName, bool uniColor, long numCluster=-1) const; + //! Shifts and scales to the data to have all the coordinates between 0.0 and 1000.0. + void NormalizeData(); + //! Inverse the operations applied by NormalizeData(). + void DenormalizeData(); + //! Destructor. + ~HACD(void); + + private: + //! Constructor. + HACD(HeapManager * heapManager = 0); + + //! Gives the edge index. + //! @param a first vertex id + //! @param b second vertex id + //! @return edge's index + static unsigned long long GetEdgeIndex(unsigned long long a, unsigned long long b) + { + if (a > b) return (a << 32) + b; + else return (b << 32) + a; + } + //! Computes the concavity of a cluster. + //! @param ch the cluster's convex-hull + //! @param distPoints the cluster's points + //! @return cluster's concavity + double Concavity(ICHull & ch, std::map & distPoints); + //! Computes the perimeter of a cluster. + //! @param triIndices the cluster's triangles + //! @param distPoints the cluster's points + //! @return cluster's perimeter + double ComputePerimeter(const std::vector & triIndices) const; + //! Creates the Graph by associating to each mesh triangle a vertex in the graph and to each couple of adjacent triangles an edge in the graph. + void CreateGraph(); + //! Initializes the graph costs and computes the vertices normals + void InitializeDualGraph(); + //! Computes the cost of an edge + //! @param e edge's id + void ComputeEdgeCost(size_t e); + //! Initializes the priority queue + //! @param fast specifies whether fast mode is used + //! @return true if success + bool InitializePriorityQueue(); + //! Cleans the intersection between convex-hulls + void CleanClusters(); + //! Computes convex-hulls from partition information + //! @param fullCH specifies whether to generate convex-hulls with a full or limited (i.e. < m_nVerticesPerCH) number of vertices + void ComputeConvexHulls(bool fullCH); + //! Simplifies the graph + //! @param fast specifies whether fast mode is used + void Simplify(); + + private: + Vec3 * m_trianglesDecimated; //>! pointer the triangles array + Vec3 * m_pointsDecimated; //>! pointer the points array + Vec3 * m_triangles; //>! pointer the triangles array + Vec3 * m_points; //>! pointer the points array + Vec3 * m_facePoints; //>! pointer to the faces points array + Vec3 * m_faceNormals; //>! pointer to the faces normals array + Vec3 * m_normals; //>! pointer the normals array + Vec3 * m_extraDistPoints; //>! pointer to the faces points array + Vec3 * m_extraDistNormals; //>! pointer to the faces normals array + size_t m_nTrianglesDecimated; //>! number of triangles in the original mesh + size_t m_nPointsDecimated; //>! number of vertices in the original mesh + size_t m_nTriangles; //>! number of triangles in the original mesh + size_t m_nPoints; //>! number of vertices in the original mesh + size_t m_nClusters; //>! number of clusters + size_t m_nMinClusters; //>! minimum number of clusters + double m_ccConnectDist; //>! maximum allowed distance to connect CCs + double m_concavity; //>! maximum concavity + double m_alpha; //>! compacity weigth + double m_beta; //>! volume weigth + double m_gamma; //>! computation cost + double m_diag; //>! length of the BB diagonal + double m_scale; //>! scale factor used for NormalizeData() and DenormalizeData() + double m_flatRegionThreshold; //>! threshhold to control the contirbution of flat regions concavity (default 1% of m_scale) + double m_smallClusterThreshold; //>! threshhold to detect small clusters (default 0.25% of the total mesh surface) + double m_area; //>! surface area + Vec3 m_barycenter; //>! barycenter of the mesh + std::vector< long > m_cVertices; //>! array of vertices each belonging to a different cluster + ICHull * m_convexHulls; //>! convex-hulls associated with the final HACD clusters + Graph m_graph; //>! simplification graph + size_t m_nVerticesPerCH; //>! maximum number of vertices per convex-hull + reservable_priority_queue, + std::greater::value_type> > m_pqueue; //!> priority queue + HACD(const HACD & rhs); + CallBackFunction m_callBack; //>! call-back function + long * m_partition; //>! array of size m_nTriangles where the i-th element specifies the cluster to which belong the i-th triangle + size_t m_targetNTrianglesDecimatedMesh; //>! specifies the target number of triangles in the decimated mesh. If set to 0 no decimation is applied. + HeapManager * m_heapManager; //>! Heap Manager + bool m_addFacesPoints; //>! specifies whether to add faces points or not + bool m_addExtraDistPoints; //>! specifies whether to add extra points for concave shapes or not + + friend HACD * const CreateHACD(HeapManager * heapManager = 0); + friend void DestroyHACD(HACD * const hacd); + }; + inline HACD * const CreateHACD(HeapManager * heapManager) + { + return new HACD(heapManager); + } + inline void DestroyHACD(HACD * const hacd) + { + delete hacd; + } +} +#endif + diff --git a/indra/libhacd/hacdICHull.cpp b/indra/libhacd/hacdICHull.cpp new file mode 100644 index 000000000..d2b3355fc --- /dev/null +++ b/indra/libhacd/hacdICHull.cpp @@ -0,0 +1,1004 @@ +/* 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 "hacdICHull.h" +#include +namespace HACD +{ + const double ICHull::sc_eps = 0.000000001; + const long ICHull::sc_dummyIndex = std::numeric_limits::max(); + ICHull::ICHull(HeapManager * const heapManager): m_mesh(heapManager) + { + m_distPoints = 0; + m_isFlat = false; + m_heapManager = heapManager; + } + bool ICHull::AddPoints(const Vec3 * points, size_t nPoints) + { + if (!points) + { + return false; + } + CircularListElement * 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(i); + } + return true; + } + bool ICHull::AddPoints(std::vector< Vec3 > points) + { + CircularListElement * vertex = NULL; + for (size_t i = 0; i < points.size(); 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(); + } + return true; + } + + bool ICHull::AddPoint(const Vec3 & point, long id) + { + if (AddPoints(&point, 1)) + { + m_mesh.m_vertices.GetData().m_name = id; + return true; + } + return false; + } + + ICHullError ICHull::Process() + { + unsigned long addedPoints = 0; + if (m_mesh.GetNVertices() < 3) + { + return ICHullErrorNotEnoughPoints; + } + if (m_mesh.GetNVertices() == 3) + { + m_isFlat = true; + CircularListElement * t1 = m_mesh.AddTriangle(); + CircularListElement * t2 = m_mesh.AddTriangle(); + CircularListElement * v0 = m_mesh.m_vertices.GetHead(); + CircularListElement * v1 = v0->GetNext(); + CircularListElement * v2 = v1->GetNext(); + // Compute the normal to the plane + Vec3 p0 = v0->GetData().m_pos; + Vec3 p1 = v1->GetData().m_pos; + Vec3 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 & 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 & 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) + { + std::vector< CircularListElement * > 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.push_back(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.push_back(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.push_back(m_mesh.m_edges.GetHead()); + } + m_mesh.m_edges.Next(); + } + size_t nV = m_mesh.GetNVertices(); + CircularList & 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 * 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(unsigned long nPointsCH) + { + unsigned long addedPoints = 0; + if (nPointsCH < 3 || m_mesh.GetNVertices() < 3) + { + return ICHullErrorNotEnoughPoints; + } + if (m_mesh.GetNVertices() == 3) + { + m_isFlat = true; + CircularListElement * t1 = m_mesh.AddTriangle(); + CircularListElement * t2 = m_mesh.AddTriangle(); + CircularListElement * v0 = m_mesh.m_vertices.GetHead(); + CircularListElement * v1 = v0->GetNext(); + CircularListElement * v2 = v1->GetNext(); + // Compute the normal to the plane + Vec3 p0 = v0->GetData().m_pos; + Vec3 p1 = v1->GetData().m_pos; + Vec3 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 & vertices = m_mesh.GetVertices(); + while (!vertices.GetData().m_tag && addedPoints < nPointsCH) // not processed + { + if (!FindMaxVolumePoint()) + { + break; + } + vertices.GetData().m_tag = true; + if (ProcessPoint()) + { + addedPoints++; + CleanUp(addedPoints); + if (!GetMesh().CheckConsistancy()) + { + size_t nV = m_mesh.GetNVertices(); + CircularList & 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) + { + std::vector< CircularListElement * > 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.push_back(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.push_back(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.push_back(m_mesh.m_edges.GetHead()); + } + m_mesh.m_edges.Next(); + } + size_t nV = m_mesh.GetNVertices(); + CircularList & 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 * 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() + { + CircularList & vertices = m_mesh.GetVertices(); + CircularListElement * vMaxVolume = 0; + CircularListElement * vHeadPrev = vertices.GetHead()->GetPrev(); + + double maxVolume = 0.0; + 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 * vHead = vHeadPrev->GetNext(); + vertices.GetHead() = vHead; + + if (!vMaxVolume) + { + return false; + } + + if (vMaxVolume != vHead) + { + Vec3 pos = vHead->GetData().m_pos; + long 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 & vertices = m_mesh.GetVertices(); + CircularListElement * 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 * v1 = v0->GetNext(); + CircularListElement * v2 = v1->GetNext(); + // mark points as processed + v0->GetData().m_tag = v1->GetData().m_tag = v2->GetData().m_tag = true; + + // create two triangles + CircularListElement * f0 = MakeFace(v0, v1, v2, 0); + MakeFace(v2, v1, v0, f0); + + // find a fourth non-coplanar point to form tetrahedron + CircularListElement * v3 = v2->GetNext(); + vertices.GetHead() = v3; + + double vol = Volume(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 = Volume(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 bary(0.0,0.0,0.0); + CircularListElement * vBary = v0; + do + { + bary += vBary->GetData().m_pos; + } + while ( (vBary = vBary->GetNext()) != v0); + bary /= static_cast(vertices.GetSize()); + + // Compute the normal to the plane + Vec3 p0 = v0->GetData().m_pos; + Vec3 p1 = v1->GetData().m_pos; + Vec3 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 newPt = bary + m_normal; + AddPoint(newPt, sc_dummyIndex); + m_isFlat = true; + v3 = v2->GetNext(); + 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 * ICHull::MakeFace(CircularListElement * v0, + CircularListElement * v1, + CircularListElement * v2, + CircularListElement * fold) + { + CircularListElement * e0; + CircularListElement * e1; + CircularListElement * e2; + long 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 * 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 * ICHull::MakeConeFace(CircularListElement * e, CircularListElement * p) + { + // create two new edges if they don't already exist + CircularListElement * 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 * 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 * fHead = m_mesh.GetTriangles().GetHead(); + CircularListElement * f = fHead; + CircularList & vertices = m_mesh.GetVertices(); + CircularListElement * vertex0 = vertices.GetHead(); + bool visible = false; + Vec3 pos0 = Vec3(vertex0->GetData().m_pos.X(), + vertex0->GetData().m_pos.Y(), + vertex0->GetData().m_pos.Z()); + double vol = 0.0; + totalVolume = 0.0; + Vec3 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 = Volume(ver0, ver1, ver2, pos0); + if ( vol < -sc_eps) + { + vol = fabs(vol); + totalVolume += vol; + if (markVisibleFaces) + { + f->GetData().m_visible = true; + m_trianglesToDelete.push_back(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.clear(); + 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 * v0 = m_mesh.GetVertices().GetHead(); + CircularListElement * eHead = m_mesh.GetEdges().GetHead(); + CircularListElement * e = eHead; + CircularListElement * tmp = 0; + long nvisible = 0; + m_edgesToDelete.clear(); + m_edgesToUpdate.clear(); + 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.push_back(e); + } + else if ( nvisible == 1) + { + e->GetData().m_newFace = MakeConeFace(e, v0); + m_edgesToUpdate.push_back(e); + } + e = tmp; + } + while (e != eHead); + return true; + } + bool ICHull::MakeCCW(CircularListElement * f, + CircularListElement * e, + CircularListElement * v) + { + // the visible face adjacent to e + CircularListElement * 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. + long i; // index of e->m_vertices[0] in fv + CircularListElement * v0 = e->GetData().m_vertices[0]; + CircularListElement * 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 * 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 long & 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 * e; + const std::vector *>::iterator itEndUpdate = m_edgesToUpdate.end(); + for(std::vector *>::iterator it = m_edgesToUpdate.begin(); it != itEndUpdate; ++it) + { + e = *it; + 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 & edges = m_mesh.GetEdges(); + const std::vector *>::iterator itEndDelete = m_edgesToDelete.end(); + for(std::vector *>::iterator it = m_edgesToDelete.begin(); it != itEndDelete; ++it) + { + edges.Delete(*it); + } + m_edgesToDelete.clear(); + m_edgesToUpdate.clear(); + return true; + } + bool ICHull::CleanTriangles() + { + CircularList & triangles = m_mesh.GetTriangles(); + const std::vector *>::iterator itEndDelete = m_trianglesToDelete.end(); + for(std::vector *>::iterator it = m_trianglesToDelete.begin(); it != itEndDelete; ++it) + { + if (m_distPoints) + { + if (m_isFlat) + { + // to be updated + } + else + { + const SArray & incidentPoints = (*it)->GetData().m_incidentPoints; + std::map::iterator itPoint; + for(size_t itP = 0; itP < (*it)->GetData().m_incidentPoints.Size(); ++itP) + { + itPoint = m_distPoints->find(incidentPoints[itP]); + if (itPoint != m_distPoints->end()) + { + itPoint->second.m_computed = false; + } + } + } + } + triangles.Delete(*it); + } + m_trianglesToDelete.clear(); + return true; + } + bool ICHull::CleanVertices(unsigned long & addedPoints) + { + // mark all vertices incident to some undeleted edge as on the hull + CircularList & edges = m_mesh.GetEdges(); + CircularListElement * 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 & vertices = m_mesh.GetVertices(); + CircularListElement * vHead = vertices.GetHead(); + CircularListElement * v = vHead; + v = v->GetPrev(); + do + { + if (v->GetData().m_tag && !v->GetData().m_onHull) + { + CircularListElement * 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 = std::vector *>(); + m_edgesToUpdate = std::vector *>(); + m_trianglesToDelete= std::vector *>(); + 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; + m_heapManager = rhs.m_heapManager; + } + return (*this); + } + double ICHull::ComputeArea() + { + size_t nT = m_mesh.GetNTriangles(); + Vec3 ver0, ver1, ver2, normal; + double surfCH = 0.0; + for(size_t f = 0; f < nT; f++) + { + TMMTriangle & currentTriangle = m_mesh.m_triangles.GetHead()->GetData(); + ver0.X() = currentTriangle.m_vertices[0]->GetData().m_pos.X(); + ver0.Y() = currentTriangle.m_vertices[0]->GetData().m_pos.Y(); + ver0.Z() = currentTriangle.m_vertices[0]->GetData().m_pos.Z(); + ver1.X() = currentTriangle.m_vertices[1]->GetData().m_pos.X(); + ver1.Y() = currentTriangle.m_vertices[1]->GetData().m_pos.Y(); + ver1.Z() = currentTriangle.m_vertices[1]->GetData().m_pos.Z(); + ver2.X() = currentTriangle.m_vertices[2]->GetData().m_pos.X(); + ver2.Y() = currentTriangle.m_vertices[2]->GetData().m_pos.Y(); + ver2.Z() = currentTriangle.m_vertices[2]->GetData().m_pos.Z(); + normal = (ver1-ver0) ^ (ver2-ver0); + surfCH += normal.GetNorm(); + m_mesh.m_triangles.Next(); + } + return surfCH; + } + double ICHull::ComputeVolume() + { + size_t nV = m_mesh.m_vertices.GetSize(); + if (nV == 0 || m_isFlat) + { + return 0.0; + } + Vec3 bary(0.0, 0.0, 0.0); + for(size_t v = 0; v < nV; v++) + { + bary.X() += m_mesh.m_vertices.GetHead()->GetData().m_pos.X(); + bary.Y() += m_mesh.m_vertices.GetHead()->GetData().m_pos.Y(); + bary.Z() += m_mesh.m_vertices.GetHead()->GetData().m_pos.Z(); + m_mesh.m_vertices.Next(); + } + bary /= static_cast(nV); + + size_t nT = m_mesh.m_triangles.GetSize(); + Vec3 ver0, ver1, ver2; + double totalVolume = 0.0; + 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(); + totalVolume += Volume(ver0, ver1, ver2, bary); + m_mesh.m_triangles.Next(); + } + return totalVolume; + } + bool ICHull::IsInside(const Vec3 & pt0, const double eps) + { + const Vec3 pt(pt0.X(), pt0.Y(), pt0.Z()); + if (m_isFlat) + { + size_t nT = m_mesh.m_triangles.GetSize(); + Vec3 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 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 = Volume(ver0, ver1, ver2, pt); + if ( vol < eps) + { + return false; + } + m_mesh.m_triangles.Next(); + } + return true; + } + } + double ICHull::ComputeDistance(long name, const Vec3 & pt, const Vec3 & normal, bool & insideHull, bool updateIncidentPoints) + { + if (m_isFlat) + { + return 0.0; + } + else + { + Vec3 p0( static_cast(pt.X()), + static_cast(pt.Y()), + static_cast(pt.Z())); + + Vec3 ptNormal(static_cast(normal.X()), + static_cast(normal.Y()), + static_cast(normal.Z())); + Vec3 impact; + long nhit; + double dist; + double distance = 0.0; + size_t nT = m_mesh.GetNTriangles(); + insideHull = false; + CircularListElement * face = 0; + Vec3 ver0, ver1, ver2; + for(size_t f = 0; f < nT; f++) + { + TMMTriangle & currentTriangle = m_mesh.m_triangles.GetHead()->GetData(); + nhit = 0; + if (currentTriangle.m_vertices[0]->GetData().m_name != currentTriangle.m_vertices[1]->GetData().m_name && + currentTriangle.m_vertices[1]->GetData().m_name != currentTriangle.m_vertices[2]->GetData().m_name && + currentTriangle.m_vertices[2]->GetData().m_name != currentTriangle.m_vertices[0]->GetData().m_name) + { + if (currentTriangle.m_vertices[0]->GetData().m_name == name || + currentTriangle.m_vertices[1]->GetData().m_name == name || + currentTriangle.m_vertices[2]->GetData().m_name == name) + { + nhit = 1; + dist = 0.0; + } + else + { + ver0.X() = currentTriangle.m_vertices[0]->GetData().m_pos.X(); + ver0.Y() = currentTriangle.m_vertices[0]->GetData().m_pos.Y(); + ver0.Z() = currentTriangle.m_vertices[0]->GetData().m_pos.Z(); + ver1.X() = currentTriangle.m_vertices[1]->GetData().m_pos.X(); + ver1.Y() = currentTriangle.m_vertices[1]->GetData().m_pos.Y(); + ver1.Z() = currentTriangle.m_vertices[1]->GetData().m_pos.Z(); + ver2.X() = currentTriangle.m_vertices[2]->GetData().m_pos.X(); + ver2.Y() = currentTriangle.m_vertices[2]->GetData().m_pos.Y(); + ver2.Z() = currentTriangle.m_vertices[2]->GetData().m_pos.Z(); + Vec3 faceNormal = (ver1-ver0) ^ (ver2-ver0); + faceNormal.Normalize(); + if (ptNormal*normal > 0.0) + { + nhit = IntersectRayTriangle(p0, ptNormal, ver0, ver1, ver2, dist); + } + } +#ifdef HACD_DEBUG + std::cout << "T " << currentTriangle.m_vertices[0]->GetData().m_name << " " + << currentTriangle.m_vertices[1]->GetData().m_name << " " + << currentTriangle.m_vertices[2]->GetData().m_name << " " + << nhit << " " << dist << std::endl; +#endif + if (nhit == 1 && (!insideHull || dist > distance) ) + { + distance = dist; + insideHull = true; + face = m_mesh.m_triangles.GetHead(); + } + } + m_mesh.m_triangles.Next(); + } + if (updateIncidentPoints && face && m_distPoints) + { + (*m_distPoints)[name].m_dist = static_cast(distance); + face->GetData().m_incidentPoints.Insert(name); + } + return distance; + } + } +} + diff --git a/indra/libhacd/hacdICHull.h b/indra/libhacd/hacdICHull.h new file mode 100644 index 000000000..97bb7f8e1 --- /dev/null +++ b/indra/libhacd/hacdICHull.h @@ -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 HACD_ICHULL_H +#define HACD_ICHULL_H +#include "hacdVersion.h" +#include "hacdManifoldMesh.h" +#include "hacdVector.h" +#include +#include +#include "hacdMicroAllocator.h" + +namespace HACD +{ + class DPoint; + class HACD; + //! Incremental Convex Hull algorithm (cf. http://maven.smith.edu/~orourke/books/ftp.html ). + enum ICHullError + { + ICHullErrorOK = 0, + ICHullErrorCoplanarPoints, + ICHullErrorNoVolume, + ICHullErrorInconsistent, + ICHullErrorNotEnoughPoints + }; + class ICHull + { + public: + static const double sc_eps; + //! + HeapManager * const GetHeapManager() const { return m_heapManager;} + //! + void SetHeapManager(HeapManager * const heapManager) + { + m_heapManager = heapManager; + m_mesh.SetHeapManager(m_heapManager); + } + //! + bool IsFlat() { return m_isFlat;} + //! + std::map * GetDistPoints() const { return m_distPoints;} + //! + void SetDistPoints(std::map * distPoints) { m_distPoints = distPoints;} + //! Returns the computed mesh + TMMesh & GetMesh() { return m_mesh;} + //! Add one point to the convex-hull + bool AddPoint(const Vec3 & point) {return AddPoints(&point, 1);} + //! Add one point to the convex-hull + bool AddPoint(const Vec3 & point, long id); + //! Add points to the convex-hull + bool AddPoints(const Vec3 * points, size_t nPoints); + bool AddPoints(std::vector< Vec3 > points); + //! + ICHullError Process(); + //! + ICHullError Process(unsigned long nPointsCH); + //! + double ComputeVolume(); + //! + double ComputeArea(); + //! + bool IsInside(const Vec3 & pt0, const double eps = 0.0); + //! + double ComputeDistance(long name, const Vec3 & pt, const Vec3 & normal, bool & insideHull, bool updateIncidentPoints); + //! + const ICHull & operator=(ICHull & rhs); + + //! Constructor + ICHull(HeapManager * const heapManager=0); + //! Destructor + virtual ~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 * MakeFace(CircularListElement * v0, + CircularListElement * v1, + CircularListElement * v2, + CircularListElement * fold); + //! + CircularListElement * MakeConeFace(CircularListElement * e, CircularListElement * v); + //! + bool ProcessPoint(); + //! + bool ComputePointVolume(double &totalVolume, bool markVisibleFaces); + //! + bool FindMaxVolumePoint(); + //! + bool CleanEdges(); + //! + bool CleanVertices(unsigned long & addedPoints); + //! + bool CleanTriangles(); + //! + bool CleanUp(unsigned long & addedPoints); + //! + bool MakeCCW(CircularListElement * f, + CircularListElement * e, + CircularListElement * v); + void Clear(); + private: + static const long sc_dummyIndex; + static const double sc_distMin; + TMMesh m_mesh; + std::vector *> m_edgesToDelete; + std::vector *> m_edgesToUpdate; + std::vector *> m_trianglesToDelete; + std::map * m_distPoints; +// CircularListElement * m_dummyVertex; + Vec3 m_normal; + bool m_isFlat; + HeapManager * m_heapManager; + + ICHull(const ICHull & rhs); + + friend class HACD; + }; + +} +#endif diff --git a/indra/libhacd/hacdManifoldMesh.cpp b/indra/libhacd/hacdManifoldMesh.cpp new file mode 100644 index 000000000..98f5408ef --- /dev/null +++ b/indra/libhacd/hacdManifoldMesh.cpp @@ -0,0 +1,602 @@ +/* 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 "hacdManifoldMesh.h" +using namespace std; + + +namespace HACD +{ + Material::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; + } + + 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; + m_incidentPoints.Initialize(); + } + TMMTriangle::TMMTriangle(void) + { + Initialize(); + } + TMMTriangle::~TMMTriangle(void) + { + } + TMMesh::TMMesh(HeapManager * const heapManager): + m_vertices(heapManager), + m_edges(heapManager), + m_triangles(heapManager) + { + m_barycenter = Vec3(0,0,0); + m_diag = 1; + m_heapManager = heapManager; + } + TMMesh::~TMMesh(void) + { + } + + void TMMesh::Print() + { + size_t nV = m_vertices.GetSize(); + std::cout << "-----------------------------" << std::endl; + std::cout << "vertices (" << nV << ")" << std::endl; + for(size_t v = 0; v < nV; v++) + { + const TMMVertex & currentVertex = m_vertices.GetData(); + std::cout << currentVertex.m_id << ", " + << currentVertex.m_pos.X() << ", " + << currentVertex.m_pos.Y() << ", " + << currentVertex.m_pos.Z() << std::endl; + m_vertices.Next(); + } + + + size_t nE = m_edges.GetSize(); + std::cout << "edges (" << nE << ")" << std::endl; + for(size_t e = 0; e < nE; e++) + { + const TMMEdge & currentEdge = m_edges.GetData(); + const CircularListElement * v0 = currentEdge.m_vertices[0]; + const CircularListElement * v1 = currentEdge.m_vertices[1]; + const CircularListElement * f0 = currentEdge.m_triangles[0]; + const CircularListElement * f1 = currentEdge.m_triangles[1]; + + std::cout << "-> (" << v0->GetData().m_name << ", " << v1->GetData().m_name << ")" << std::endl; + std::cout << "-> F0 (" << f0->GetData().m_vertices[0]->GetData().m_name << ", " + << f0->GetData().m_vertices[1]->GetData().m_name << ", " + << f0->GetData().m_vertices[2]->GetData().m_name <<")" << std::endl; + std::cout << "-> F1 (" << f1->GetData().m_vertices[0]->GetData().m_name << ", " + << f1->GetData().m_vertices[1]->GetData().m_name << ", " + << f1->GetData().m_vertices[2]->GetData().m_name << ")" << std::endl; + m_edges.Next(); + } + size_t nT = m_triangles.GetSize(); + std::cout << "triangles (" << nT << ")" << std::endl; + for(size_t t = 0; t < nT; t++) + { + const TMMTriangle & currentTriangle = m_triangles.GetData(); + const CircularListElement * v0 = currentTriangle.m_vertices[0]; + const CircularListElement * v1 = currentTriangle.m_vertices[1]; + const CircularListElement * v2 = currentTriangle.m_vertices[2]; + const CircularListElement * e0 = currentTriangle.m_edges[0]; + const CircularListElement * e1 = currentTriangle.m_edges[1]; + const CircularListElement * e2 = currentTriangle.m_edges[2]; + + std::cout << "-> (" << v0->GetData().m_name << ", " << v1->GetData().m_name << ", "<< v2->GetData().m_name << ")" << std::endl; + + std::cout << "-> E0 (" << e0->GetData().m_vertices[0]->GetData().m_name << ", " + << e0->GetData().m_vertices[1]->GetData().m_name << ")" << std::endl; + std::cout << "-> E1 (" << e1->GetData().m_vertices[0]->GetData().m_name << ", " + << e1->GetData().m_vertices[1]->GetData().m_name << ")" << std::endl; + std::cout << "-> E2 (" << e2->GetData().m_vertices[0]->GetData().m_name << ", " + << e2->GetData().m_vertices[1]->GetData().m_name << ")" << std::endl; + m_triangles.Next(); + } + } + bool TMMesh::Save(const char *fileName) + { + std::ofstream fout(fileName); + std::cout << "Saving " << fileName << std::endl; + if (SaveVRML2(fout)) + { + fout.close(); + return true; + } + return false; + } + bool TMMesh::SaveVRML2(std::ofstream &fout) + { + return SaveVRML2(fout, Material()); + } + bool TMMesh::SaveVRML2(std::ofstream &fout, const Material & material) + { + if (fout.is_open()) + { + size_t nV = m_vertices.GetSize(); + size_t nT = m_triangles.GetSize(); + 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.X() << " " + << material.m_diffuseColor.Y() << " " + << material.m_diffuseColor.Z() << std::endl; + fout <<" ambientIntensity " << material.m_ambientIntensity << std::endl; + fout <<" specularColor " << material.m_specularColor.X() << " " + << material.m_specularColor.Y() << " " + << material.m_specularColor.Z() << std::endl; + fout <<" emissiveColor " << material.m_emissiveColor.X() << " " + << material.m_emissiveColor.Y() << " " + << material.m_emissiveColor.Z() << 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 (GetNVertices() > 0) { + fout <<" coord DEF co Coordinate {" << std::endl; + fout <<" point [" << std::endl; + for(size_t v = 0; v < nV; v++) + { + TMMVertex & currentVertex = m_vertices.GetData(); + fout <<" " << currentVertex.m_pos.X() << " " + << currentVertex.m_pos.Y() << " " + << currentVertex.m_pos.Z() << "," << std::endl; + currentVertex.m_id = v; + m_vertices.Next(); + } + fout <<" ]" << std::endl; + fout <<" }" << std::endl; + } + if (GetNTriangles() > 0) { + fout <<" coordIndex [ " << std::endl; + for(size_t f = 0; f < nT; f++) + { + TMMTriangle & currentTriangle = m_triangles.GetData(); + fout <<" " << currentTriangle.m_vertices[0]->GetData().m_id << ", " + << currentTriangle.m_vertices[1]->GetData().m_id << ", " + << currentTriangle.m_vertices[2]->GetData().m_id << ", -1," << std::endl; + m_triangles.Next(); + } + fout <<" ]" << std::endl; + } + fout <<" }" << std::endl; + fout <<" }" << std::endl; + fout <<" ]" << std::endl; + fout <<"}" << std::endl; + } + return true; + } + void TMMesh::GetIFS(Vec3 * const points, Vec3 * 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(currentTriangle.m_vertices[0]->GetData().m_id); + triangles[f].Y() = static_cast(currentTriangle.m_vertices[1]->GetData().m_id); + triangles[f].Z() = static_cast(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; + m_heapManager = mesh.m_heapManager; + + // generating mapping + CircularListElement ** vertexMap = new CircularListElement * [nV]; + CircularListElement ** edgeMap = new CircularListElement * [nE]; + CircularListElement ** triangleMap = new CircularListElement * [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; + + } + long IntersectRayTriangle(const Vec3 & P0, const Vec3 & dir, + const Vec3 & V0, const Vec3 & V1, + const Vec3 & V2, double &t) + { + const double EPS = 1e-9; + const double EPS1 = 1e-6; + t = 0.0; + Vec3 edge1, edge2, edge3; + double det; + edge1 = V1 - V2; + edge2 = V2 - V0; + Vec3 pvec = dir ^ edge2; + det = edge1 * pvec; + if (det < EPS && det > -EPS) + return 0; + Vec3 tvec = P0 - V0; + Vec3 qvec = tvec ^ edge1; + t = (edge2 * qvec) / det; + if (t < 0.0) + { + return 0; + } + edge3 = V0 - V1; + Vec3 I(P0 + t * dir); + Vec3 s0 = (I-V0) ^ edge3; + Vec3 s1 = (I-V1) ^ edge1; + Vec3 s2 = (I-V2) ^ edge2; + + Vec3 normal = edge1 ^ edge2; + double diff = normal.GetNorm() - s0.GetNorm() - s1.GetNorm() - s2.GetNorm(); + + if (diff < EPS1 && diff > -EPS1) + { + return 1; + } + return 0; + } + + bool IntersectLineLine(const Vec3 & p1, const Vec3 & p2, + const Vec3 & p3, const Vec3 & p4, + Vec3 & pa, Vec3 & pb, + double & mua, double & mub) + { + Vec3 p13,p43,p21; + double d1343,d4321,d1321,d4343,d2121; + double numer,denom; + + p13.X() = p1.X() - p3.X(); + p13.Y() = p1.Y() - p3.Y(); + p13.Z() = p1.Z() - p3.Z(); + p43.X() = p4.X() - p3.X(); + p43.Y() = p4.Y() - p3.Y(); + p43.Z() = p4.Z() - p3.Z(); + if (p43.X()==0.0 && p43.Y()==0.0 && p43.Z()==0.0) + return false; + p21.X() = p2.X() - p1.X(); + p21.Y() = p2.Y() - p1.Y(); + p21.Z() = p2.Z() - p1.Z(); + if (p21.X()==0.0 && p21.Y()==0.0 && p21.Z()==0.0) + return false; + + d1343 = p13.X() * p43.X() + p13.Y() * p43.Y() + p13.Z() * p43.Z(); + d4321 = p43.X() * p21.X() + p43.Y() * p21.Y() + p43.Z() * p21.Z(); + d1321 = p13.X() * p21.X() + p13.Y() * p21.Y() + p13.Z() * p21.Z(); + d4343 = p43.X() * p43.X() + p43.Y() * p43.Y() + p43.Z() * p43.Z(); + d2121 = p21.X() * p21.X() + p21.Y() * p21.Y() + p21.Z() * p21.Z(); + + denom = d2121 * d4343 - d4321 * d4321; + if (denom==0.0) + return false; + numer = d1343 * d4321 - d1321 * d4343; + + mua = numer / denom; + mub = (d1343 + d4321 * (mua)) / d4343; + + pa.X() = p1.X() + mua * p21.X(); + pa.Y() = p1.Y() + mua * p21.Y(); + pa.Z() = p1.Z() + mua * p21.Z(); + pb.X() = p3.X() + mub * p43.X(); + pb.Y() = p3.Y() + mub * p43.Y(); + pb.Z() = p3.Z() + mub * p43.Z(); + + return true; + } + + long IntersectRayTriangle2(const Vec3 & P0, const Vec3 & dir, + const Vec3 & V0, const Vec3 & V1, + const Vec3 & V2, double &r) + { + Vec3 u, v, n; // triangle vectors + Vec3 w0, w; // ray vectors + double a, b; // params to calc ray-plane intersect + + // get triangle edge vectors and plane normal + u = V1 - V0; + v = V2 - V0; + n = u ^ v; // cross product + if (n.GetNorm() == 0.0) // triangle is degenerate + return -1; // do not deal with this case + + w0 = P0 - V0; + a = - n * w0; + b = n * dir; + if (fabs(b) <= 0.0) { // ray is parallel to triangle plane + if (a == 0.0) // ray lies in triangle plane + return 2; + else return 0; // ray disjoint from plane + } + + // get intersect point of ray with triangle plane + r = a / b; + if (r < 0.0) // ray goes away from triangle + return 0; // => no intersect + // for a segment, also test if (r > 1.0) => no intersect + + Vec3 I = P0 + r * dir; // intersect point of ray and plane + + // is I inside T? + double uu, uv, vv, wu, wv, D; + uu = u * u; + uv = u * v; + vv = v * v; + w = I - V0; + wu = w * u; + wv = w * v; + D = uv * uv - uu * vv; + + // get and test parametric coords + double s, t; + s = (uv * wv - vv * wu) / D; + if (s < 0.0 || s > 1.0) // I is outside T + return 0; + t = (uv * wu - uu * wv) / D; + if (t < 0.0 || (s + t) > 1.0) // I is outside T + return 0; + return 1; // I is in T + } + + + 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; + } + bool TMMesh::Normalize() + { + size_t nV = m_vertices.GetSize(); + if (nV == 0) + { + return false; + } + m_barycenter = m_vertices.GetHead()->GetData().m_pos; + Vec3 min = m_barycenter; + Vec3 max = m_barycenter; + Real x, y, z; + for(size_t v = 1; v < nV; v++) + { + m_barycenter += m_vertices.GetHead()->GetData().m_pos; + x = m_vertices.GetHead()->GetData().m_pos.X(); + y = m_vertices.GetHead()->GetData().m_pos.Y(); + z = m_vertices.GetHead()->GetData().m_pos.Z(); + if ( x < min.X()) min.X() = x; + else if ( x > max.X()) max.X() = x; + if ( y < min.Y()) min.Y() = y; + else if ( y > max.Y()) max.Y() = y; + if ( z < min.Z()) min.Z() = z; + else if ( z > max.Z()) max.Z() = z; + m_vertices.Next(); + } + m_barycenter /= static_cast(nV); + m_diag = static_cast(0.001 * (max-min).GetNorm()); + const Real invDiag = static_cast(1.0 / m_diag); + if (m_diag != 0.0) + { + for(size_t v = 0; v < nV; v++) + { + m_vertices.GetHead()->GetData().m_pos = (m_vertices.GetHead()->GetData().m_pos - m_barycenter) * invDiag; + m_vertices.Next(); + } + } + return true; + } + bool TMMesh::Denormalize() + { + size_t nV = m_vertices.GetSize(); + if (nV == 0) + { + return false; + } + if (m_diag != 0.0) + { + for(size_t v = 0; v < nV; v++) + { + m_vertices.GetHead()->GetData().m_pos = m_vertices.GetHead()->GetData().m_pos * m_diag + m_barycenter; + m_vertices.Next(); + } + } + return false; + } +} diff --git a/indra/libhacd/hacdManifoldMesh.h b/indra/libhacd/hacdManifoldMesh.h new file mode 100644 index 000000000..c5c20caa1 --- /dev/null +++ b/indra/libhacd/hacdManifoldMesh.h @@ -0,0 +1,267 @@ +/* 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. +*/ + +/* 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 HACD_MANIFOLD_MESH_H +#define HACD_MANIFOLD_MESH_H +#include +#include +#include "hacdVersion.h" +#include "hacdCircularList.h" +#include "hacdVector.h" +#include "hacdSArray.h" +#include +#include "hacdMicroAllocator.h" +namespace HACD +{ + class TMMTriangle; + class TMMEdge; + class TMMesh; + class ICHull; + class HACD; + + class DPoint + { + public: + DPoint(long name=0, Real dist=0, bool computed=false, bool distOnly=false) + :m_name(name), + m_dist(dist), + m_computed(computed), + m_distOnly(distOnly){}; + ~DPoint(){}; + private: + long m_name; + Real m_dist; + bool m_computed; + bool m_distOnly; + friend class TMMTriangle; + friend class TMMesh; + friend class GraphVertex; + friend class GraphEdge; + friend class Graph; + friend class ICHull; + friend class HACD; + }; + + //! Vertex data structure used in a triangular manifold mesh (TMM). + class TMMVertex + { + public: + void Initialize(); + TMMVertex(void); + ~TMMVertex(void); + + private: + Vec3 m_pos; + long m_name; + size_t m_id; + CircularListElement * m_duplicate; // pointer to incident cone edge (or NULL) + bool m_onHull; + bool m_tag; + TMMVertex(const TMMVertex & rhs); + + friend class HACD; + 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 * m_triangles[2]; + CircularListElement * m_vertices[2]; + CircularListElement * m_newFace; + + + TMMEdge(const TMMEdge & rhs); + + friend class HACD; + 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 * m_edges[3]; + CircularListElement * m_vertices[3]; + SArray m_incidentPoints; + bool m_visible; + + TMMTriangle(const TMMTriangle & rhs); + + friend class HACD; + friend class ICHull; + friend class TMMesh; + friend class TMMVertex; + friend class TMMEdge; + }; + + class Material + { + public: + Material(void); + ~Material(void){} +// private: + Vec3 m_diffuseColor; + double m_ambientIntensity; + Vec3 m_specularColor; + Vec3 m_emissiveColor; + double m_shininess; + double m_transparency; + + friend class TMMesh; + friend class HACD; + }; + + //! triangular manifold mesh data structure. + class TMMesh + { + public: + //! + HeapManager * const GetHeapManager() const { return m_heapManager;} + //! + void SetHeapManager(HeapManager * const heapManager) + { + m_heapManager = heapManager; + m_vertices.SetHeapManager(m_heapManager); + m_edges.SetHeapManager(m_heapManager); + m_triangles.SetHeapManager(m_heapManager); + } + //! 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 & GetVertices() const { return m_vertices;} + //! Returns the edges circular list + inline const CircularList & GetEdges() const { return m_edges;} + //! Returns the triangles circular list + inline const CircularList & GetTriangles() const { return m_triangles;} + //! Returns the vertices circular list + inline CircularList & GetVertices() { return m_vertices;} + //! Returns the edges circular list + inline CircularList & GetEdges() { return m_edges;} + //! Returns the triangles circular list + inline CircularList & GetTriangles() { return m_triangles;} + //! Add vertex to the mesh + CircularListElement * AddVertex() {return m_vertices.Add();} + //! Add vertex to the mesh + CircularListElement * AddEdge() {return m_edges.Add();} + //! Add vertex to the mesh + CircularListElement * AddTriangle() {return m_triangles.Add();} + //! Print mesh information + void Print(); + //! + void GetIFS(Vec3 * const points, Vec3 * const triangles); + //! Save mesh + bool Save(const char *fileName); + //! Save mesh to VRML 2.0 format + bool SaveVRML2(std::ofstream &fout); + //! Save mesh to VRML 2.0 format + bool SaveVRML2(std::ofstream &fout, const Material & material); + //! + void Clear(); + //! + void Copy(TMMesh & mesh); + //! + bool CheckConsistancy(); + //! + bool Normalize(); + //! + bool Denormalize(); + //! Constructor + TMMesh(HeapManager * const heapManager); + //! Destructor + virtual ~TMMesh(void); + + private: + CircularList m_vertices; + CircularList m_edges; + CircularList m_triangles; + Real m_diag; //>! length of the BB diagonal + Vec3 m_barycenter; //>! barycenter of the mesh + HeapManager * m_heapManager; + + // not defined + TMMesh(const TMMesh & rhs); + friend class ICHull; + friend class HACD; + }; + //! IntersectRayTriangle(): intersect a ray with a 3D triangle + //! Input: a ray R, and a triangle T + //! Output: *I = intersection point (when it exists) + //! 0 = disjoint (no intersect) + //! 1 = intersect in unique point I1 + long IntersectRayTriangle( const Vec3 & P0, const Vec3 & dir, + const Vec3 & V0, const Vec3 & V1, + const Vec3 & V2, double &t); + + // intersect_RayTriangle(): intersect a ray with a 3D triangle + // Input: a ray R, and a triangle T + // Output: *I = intersection point (when it exists) + // Return: -1 = triangle is degenerate (a segment or point) + // 0 = disjoint (no intersect) + // 1 = intersect in unique point I1 + // 2 = are in the same plane + long IntersectRayTriangle2(const Vec3 & P0, const Vec3 & dir, + const Vec3 & V0, const Vec3 & V1, + const Vec3 & V2, double &r); + + /* + Calculate the line segment PaPb that is the shortest route between + two lines P1P2 and P3P4. Calculate also the values of mua and mub where + Pa = P1 + mua (P2 - P1) + Pb = P3 + mub (P4 - P3) + Return FALSE if no solution exists. + */ + bool IntersectLineLine(const Vec3 & p1, const Vec3 & p2, + const Vec3 & p3, const Vec3 & p4, + Vec3 & pa, Vec3 & pb, + double & mua, double &mub); +} +#endif diff --git a/indra/libhacd/hacdMeshDecimator.cpp b/indra/libhacd/hacdMeshDecimator.cpp new file mode 100644 index 000000000..3ca9ba87e --- /dev/null +++ b/indra/libhacd/hacdMeshDecimator.cpp @@ -0,0 +1,684 @@ +#include +#include +#include +#include +#include +#include "hacdMeshDecimator.h" + +namespace HACD +{ + MeshDecimator::MeshDecimator(void) + { + m_triangles = 0; + m_points = 0; + m_nPoints = 0; + m_nInitialTriangles = 0; + m_nVertices = 0; + m_nTriangles = 0; + m_nEdges = 0; + m_trianglesTags = 0; + m_ecolManifoldConstraint = true; + m_callBack = 0; + } + + MeshDecimator::~MeshDecimator(void) + { + ReleaseMemory(); + } + void MeshDecimator::ReleaseMemory() + { + delete [] m_trianglesTags; + std::vector< MDVertex > emptyVertices(0); + m_vertices.swap(emptyVertices); + std::vector emptyEdges(0); + m_edges.swap(emptyEdges); + m_pqueue = std::priority_queue< + MDEdgePriorityQueue, + std::vector, + std::less >(); + m_triangles = 0; + m_points = 0; + m_nPoints = 0; + m_nInitialTriangles = 0; + m_nVertices = 0; + m_nTriangles = 0; + m_nEdges = 0; + m_trianglesTags = 0; + } + void MeshDecimator::Initialize(size_t nVertices, size_t nTriangles, Vec3 * points, Vec3 * triangles) + { + m_nVertices = nVertices; + m_nTriangles = nTriangles; + m_nInitialTriangles = nTriangles; + m_points = points; + m_nPoints = nVertices; + m_triangles = triangles; + m_trianglesTags = new bool[m_nTriangles]; + m_edges.reserve(3*m_nTriangles); + m_vertices.resize(m_nVertices); + for(size_t v = 0; v < m_nVertices; ++v) + { + m_vertices[v].m_tag = true; + } + long tri[3]; + MDEdge edge; + edge.m_tag = true; + edge.m_onBoundary = true; + long nEdges = 0; + long idEdge; + long nTris = static_cast(m_nTriangles); + for(long t = 0; t < nTris; ++t) + { + tri[0] = m_triangles[t].X(); + tri[1] = m_triangles[t].Y(); + tri[2] = m_triangles[t].Z(); + m_trianglesTags[t] = true; + for(int k = 0; k < 3; ++k) + { + edge.m_v1 = tri[k]; + edge.m_v2 = tri[(k+1)%3]; + m_vertices[edge.m_v1].m_triangles.Insert(t); + idEdge = GetEdge(edge.m_v1, edge.m_v2); + if (idEdge == -1) + { + m_edges.push_back(edge); + m_vertices[edge.m_v1].m_edges.Insert(nEdges); + m_vertices[edge.m_v2].m_edges.Insert(nEdges); + ++nEdges; + } + else + { + m_edges[idEdge].m_onBoundary = false; + } + } + } + m_nEdges = static_cast(nEdges); + for(size_t v = 0; v < m_nVertices; ++v) + { + m_vertices[v].m_onBoundary = false; + for(size_t itE = 0; itE < m_vertices[v].m_edges.Size(); ++itE) + { + idEdge = m_vertices[v].m_edges[itE]; + if (m_edges[idEdge].m_onBoundary) + { + m_vertices[v].m_onBoundary = true; + break; + } + } + } + } + long MeshDecimator::GetTriangle(long v1, long v2, long v3) const + { + long i, j, k; + long idTriangle; + for(size_t it = 0; it < m_vertices[v1].m_triangles.Size(); ++it) + { + idTriangle = m_vertices[v1].m_triangles[it]; + i = m_triangles[idTriangle].X(); + j = m_triangles[idTriangle].Y(); + k = m_triangles[idTriangle].Z(); + if ( (i==v1 && j==v2 && k==v3) || (i==v1 && j==v3 && k==v2) || + (i==v2 && j==v1 && k==v3) || (i==v2 && j==v3 && k==v1) || + (i==v3 && j==v2 && k==v1) || (i==v3 && j==v1 && k==v2) ) + { + return idTriangle; + } + } + return -1; + } + long MeshDecimator::GetEdge(long v1, long v2) const + { + long idEdge; + for(size_t it = 0; it < m_vertices[v1].m_edges.Size(); ++it) + { + idEdge = m_vertices[v1].m_edges[it]; + if ( (m_edges[idEdge].m_v1==v1 && m_edges[idEdge].m_v2==v2) || + (m_edges[idEdge].m_v1==v2 && m_edges[idEdge].m_v2==v1) ) + { + return idEdge; + } + } + return -1; + } + void MeshDecimator::EdgeCollapse(long v1, long v2) + { + long u, w; + int shift; + long idTriangle; + for(size_t itT = 0; itT < m_vertices[v2].m_triangles.Size(); ++itT) + { + idTriangle = m_vertices[v2].m_triangles[itT]; + if (m_triangles[idTriangle].X() == v2) + { + shift = 0; + u = m_triangles[idTriangle].Y(); + w = m_triangles[idTriangle].Z(); + } + else if (m_triangles[idTriangle].Y() == v2) + { + shift = 1; + u = m_triangles[idTriangle].X(); + w = m_triangles[idTriangle].Z(); + } + else + { + shift = 2; + u = m_triangles[idTriangle].X(); + w = m_triangles[idTriangle].Y(); + } + + if ((u == v1) || (w == v1)) + { + m_trianglesTags[idTriangle] = false; + m_vertices[u].m_triangles.Erase(idTriangle); + m_vertices[w].m_triangles.Erase(idTriangle); + m_nTriangles--; + } + else if (GetTriangle(v1, u, w) == -1) + { + m_vertices[v1].m_triangles.Insert(idTriangle); + m_triangles[idTriangle][shift] = v1; + } + else + { + m_trianglesTags[idTriangle] = false; + m_vertices[u].m_triangles.Erase(idTriangle); + m_vertices[w].m_triangles.Erase(idTriangle); + m_nTriangles--; + } + } + long idEdge; + for(size_t itE = 0; itE < m_vertices[v2].m_edges.Size(); ++itE) + { + idEdge = m_vertices[v2].m_edges[itE]; + w = (m_edges[idEdge].m_v1 == v2)? m_edges[idEdge].m_v2 : m_edges[idEdge].m_v1; + if (w==v1) + { + m_edges[idEdge].m_tag = false; + m_vertices[w].m_edges.Erase(idEdge); + m_nEdges--; + } + else if ( GetEdge(v1, w) == -1) + { + if (m_edges[idEdge].m_v1 == v2) m_edges[idEdge].m_v1 = v1; + else m_edges[idEdge].m_v2 = v1; + m_vertices[v1].m_edges.Insert(idEdge); + } + else + { + m_edges[idEdge].m_tag = false; + m_vertices[w].m_edges.Erase(idEdge); + m_nEdges--; + } + } + m_vertices[v2].m_tag = false; + m_nVertices--; + // update boundary edges + SArray incidentVertices; + incidentVertices.PushBack(v1); + for(size_t itE = 0; itE < m_vertices[v1].m_edges.Size(); ++itE) + { + incidentVertices.PushBack((m_edges[idEdge].m_v1!= v1)?m_edges[idEdge].m_v1:m_edges[idEdge].m_v2); + idEdge = m_vertices[v1].m_edges[itE]; + m_edges[idEdge].m_onBoundary = (IsBoundaryEdge(m_edges[idEdge].m_v1, m_edges[idEdge].m_v2) != -1); + } + // update boundary vertices + long idVertex; + for(size_t itV = 0; itV < incidentVertices.Size(); ++itV) + { + idVertex = incidentVertices[itV]; + m_vertices[idVertex].m_onBoundary = false; + for(size_t itE = 0; itE < m_vertices[idVertex].m_edges.Size(); ++itE) + { + idEdge = m_vertices[idVertex].m_edges[itE]; + if (m_edges[idEdge].m_onBoundary) + { + m_vertices[idVertex].m_onBoundary = true; + break; + } + } + } + } + long MeshDecimator::IsBoundaryEdge(long v1, long v2) const + { + long commonTri = -1; + long itTriangle1, itTriangle2; + for(size_t itT1 = 0; itT1 < m_vertices[v1].m_triangles.Size(); ++itT1) + { + itTriangle1 = m_vertices[v1].m_triangles[itT1]; + for(size_t itT2 = 0; itT2 < m_vertices[v2].m_triangles.Size(); ++itT2) + { + itTriangle2 = m_vertices[v2].m_triangles[itT2]; + if (itTriangle1 == itTriangle2) + { + if (commonTri == -1) + { + commonTri = itTriangle1; + } + else + { + return -1; + } + } + } + } + return commonTri; + } + bool MeshDecimator::IsBoundaryVertex(long v) const + { + long idEdge; + for(size_t itE = 0; itE < m_vertices[v].m_edges.Size(); ++itE) + { + idEdge = m_vertices[v].m_edges[itE]; + if ( IsBoundaryEdge(m_edges[idEdge].m_v1, m_edges[idEdge].m_v2) != -1) return true; + } + return false; + } + + void MeshDecimator::GetMeshData(Vec3 * points, Vec3 * triangles) const + { + long * map = new long [m_nPoints]; + long counter = 0; + for (size_t v = 0; v < m_nPoints; ++v) + { + if ( m_vertices[v].m_tag ) + { + points[counter] = m_points[v]; + map[v] = counter++; + } + } + counter = 0; + for (size_t t = 0; t < m_nInitialTriangles; ++t) + { + if ( m_trianglesTags[t] ) + { + triangles[counter].X() = map[m_triangles[t].X()]; + triangles[counter].Y() = map[m_triangles[t].Y()]; + triangles[counter].Z() = map[m_triangles[t].Z()]; + counter++; + } + } + delete [] map; + } + + void MeshDecimator::InitializeQEM() + { + Vec3 coordMin = m_points[0]; + Vec3 coordMax = m_points[0]; + Vec3 coord; + for (size_t p = 1; p < m_nPoints ; ++p) + { + coord = m_points[p]; + if (coordMin.X() > coord.X()) coordMin.X() = coord.X(); + if (coordMin.Y() > coord.Y()) coordMin.Y() = coord.Y(); + if (coordMin.Z() > coord.Z()) coordMin.Z() = coord.Z(); + if (coordMax.X() < coord.X()) coordMax.X() = coord.X(); + if (coordMax.Y() < coord.Y()) coordMax.Y() = coord.Y(); + if (coordMax.Z() < coord.Z()) coordMax.Z() = coord.Z(); + } + coordMax -= coordMin; + m_diagBB = coordMax.GetNorm(); + + long i, j, k; + Vec3 n; + Float d = 0; + Float area = 0; + for(size_t v = 0; v < m_nPoints; ++v) + { + memset(m_vertices[v].m_Q, 0, 10 * sizeof(Float)); + long idTriangle; + for(size_t itT = 0; itT < m_vertices[v].m_triangles.Size(); ++itT) + { + idTriangle = m_vertices[v].m_triangles[itT]; + i = m_triangles[idTriangle].X(); + j = m_triangles[idTriangle].Y(); + k = m_triangles[idTriangle].Z(); + n = (m_points[j] - m_points[i])^(m_points[k] - m_points[i]); + area = n.GetNorm(); + n.Normalize(); + d = - (m_points[v] * n); + m_vertices[v].m_Q[0] += area * (n.X() * n.X()); + m_vertices[v].m_Q[1] += area * (n.X() * n.Y()); + m_vertices[v].m_Q[2] += area * (n.X() * n.Z()); + m_vertices[v].m_Q[3] += area * (n.X() * d); + m_vertices[v].m_Q[4] += area * (n.Y() * n.Y()); + m_vertices[v].m_Q[5] += area * (n.Y() * n.Z()); + m_vertices[v].m_Q[6] += area * (n.Y() * d); + m_vertices[v].m_Q[7] += area * (n.Z() * n.Z()); + m_vertices[v].m_Q[8] += area * (n.Z() * d); + m_vertices[v].m_Q[9] += area * (d * d); + } + } + Vec3 u1, u2; + const Float w = static_cast(1000); + long t, v1, v2, v3; + for(size_t e = 0; e < m_edges.size(); ++e) + { + v1 = m_edges[e].m_v1; + v2 = m_edges[e].m_v2; + t = IsBoundaryEdge(v1, v2); + if (t != -1) + { + if (m_triangles[t].X() != v1 && m_triangles[t].X() != v2) v3 = m_triangles[t].X(); + else if (m_triangles[t].Y() != v1 && m_triangles[t].Y() != v2) v3 = m_triangles[t].Y(); + else v3 = m_triangles[t].Z(); + u1 = m_points[v2] - m_points[v1]; + u2 = m_points[v3] - m_points[v1]; + area = w * (u1^u2).GetNorm(); + u1.Normalize(); + n = u2 - (u2 * u1) * u1; + n.Normalize(); + + d = - (m_points[v1] * n); + m_vertices[v1].m_Q[0] += area * (n.X() * n.X()); + m_vertices[v1].m_Q[1] += area * (n.X() * n.Y()); + m_vertices[v1].m_Q[2] += area * (n.X() * n.Z()); + m_vertices[v1].m_Q[3] += area * (n.X() * d); + m_vertices[v1].m_Q[4] += area * (n.Y() * n.Y()); + m_vertices[v1].m_Q[5] += area * (n.Y() * n.Z()); + m_vertices[v1].m_Q[6] += area * (n.Y() * d); + m_vertices[v1].m_Q[7] += area * (n.Z() * n.Z()); + m_vertices[v1].m_Q[8] += area * (n.Z() * d); + m_vertices[v1].m_Q[9] += area * (d * d); + + d = - (m_points[v2] * n); + m_vertices[v2].m_Q[0] += area * (n.X() * n.X()); + m_vertices[v2].m_Q[1] += area * (n.X() * n.Y()); + m_vertices[v2].m_Q[2] += area * (n.X() * n.Z()); + m_vertices[v2].m_Q[3] += area * (n.X() * d); + m_vertices[v2].m_Q[4] += area * (n.Y() * n.Y()); + m_vertices[v2].m_Q[5] += area * (n.Y() * n.Z()); + m_vertices[v2].m_Q[6] += area * (n.Y() * d); + m_vertices[v2].m_Q[7] += area * (n.Z() * n.Z()); + m_vertices[v2].m_Q[8] += area * (n.Z() * d); + m_vertices[v2].m_Q[9] += area * (d * d); + } + } + } + void MeshDecimator::InitializePriorityQueue() + { + double progressOld = -1.0; + double progress = 0.0; + char msg[1024]; + double ptgStep = 1.0; + long v1, v2; + MDEdgePriorityQueue pqEdge; + size_t nE = m_edges.size(); + for(size_t e = 0; e < nE; ++e) + { + progress = e * 100.0 / nE; + if (fabs(progress-progressOld) > ptgStep && m_callBack) + { + sprintf(msg, "%3.2f %% \t \t \r", progress); + (*m_callBack)(msg, progress, 0.0, m_nVertices); + progressOld = progress; + } + + if (m_edges[e].m_tag) + { + v1 = m_edges[e].m_v1; + v2 = m_edges[e].m_v2; + if ( (!m_ecolManifoldConstraint) || (ManifoldConstraint(v1, v2))) + { + pqEdge.m_qem = m_edges[e].m_qem = ComputeEdgeCost(v1, v2, m_edges[e].m_pos); + pqEdge.m_name = static_cast(e); + m_pqueue.push(pqEdge); + } + } + } + } + double MeshDecimator::ComputeEdgeCost(long v1, long v2, Vec3 & newPos) const + { + double Q[10]; + double M[12]; + Vec3 pos; + for(int i = 0; i < 10; ++i) Q[i] = m_vertices[v1].m_Q[i] + m_vertices[v2].m_Q[i]; + M[0] = Q[0]; // (0, 0) + M[1] = Q[1]; // (0, 1) + M[2] = Q[2]; // (0, 2) + M[3] = Q[3]; // (0, 3) + M[4] = Q[1]; // (1, 0) + M[5] = Q[4]; // (1, 1) + M[6] = Q[5]; // (1, 2) + M[7] = Q[6]; // (1, 3) + M[8] = Q[2]; // (2, 0) + M[9] = Q[5]; // (2, 1) + M[10] = Q[7]; // (2, 2); + M[11] = Q[8]; // (2, 3); + double det = M[0] * M[5] * M[10] + M[1] * M[6] * M[8] + M[2] * M[4] * M[9] + - M[0] * M[6] * M[9] - M[1] * M[4] * M[10]- M[2] * M[5] * M[8]; + if (det != 0.0) + { + double d = 1.0 / det; + pos.X() = d * (M[1]*M[7]*M[10] + M[2]*M[5]*M[11] + M[3]*M[6]*M[9] + -M[1]*M[6]*M[11] - M[2]*M[7]*M[9] - M[3]*M[5]*M[10]); + pos.Y() = d * (M[0]*M[6]*M[11] + M[2]*M[7]*M[8] + M[3]*M[4]*M[10] + -M[0]*M[7]*M[10] - M[2]*M[4]*M[11] - M[3]*M[6]*M[8]); + pos.Z() = d * (M[0]*M[7]*M[9] + M[1]*M[4]*M[11] + M[3]*M[5]*M[8] + -M[0]*M[5]*M[11] - M[1]*M[7]*M[8] - M[3]*M[4]*M[9]); + newPos.X() = static_cast(pos.X()); + newPos.Y() = static_cast(pos.Y()); + newPos.Z() = static_cast(pos.Z()); + } + else + { + const Float w = static_cast(0.5f); + newPos = w * m_points[v1] + w * m_points[v2]; + pos.X() = static_cast(newPos.X()); + pos.Y() = static_cast(newPos.Y()); + pos.Z() = static_cast(newPos.Z()); + } + + double qem = pos.X() * (Q[0] * pos.X() + Q[1] * pos.Y() + Q[2] * pos.Z() + Q[3]) + + pos.Y() * (Q[1] * pos.X() + Q[4] * pos.Y() + Q[5] * pos.Z() + Q[6]) + + pos.Z() * (Q[2] * pos.X() + Q[5] * pos.Y() + Q[7] * pos.Z() + Q[8]) + + (Q[3] * pos.X() + Q[6] * pos.Y() + Q[8] * pos.Z() + Q[9]) ; + + Vec3 d1; + Vec3 d2; + Vec3 n1; + Vec3 n2; + Vec3 oldPosV1 = m_points[v1]; + Vec3 oldPosV2 = m_points[v2]; + + SArray triangles = m_vertices[v1].m_triangles; + long idTriangle; + for(size_t itT = 0; itT < m_vertices[v2].m_triangles.Size(); ++itT) + { + idTriangle = m_vertices[v2].m_triangles[itT]; + triangles.Insert(idTriangle); + } + long a[3]; + for(size_t itT = 0; itT != triangles.Size(); ++itT) + { + idTriangle = triangles[itT]; + a[0] = m_triangles[idTriangle].X(); + a[1] = m_triangles[idTriangle].Y(); + a[2] = m_triangles[idTriangle].Z(); + + d1 = m_points[a[1]] - m_points[a[0]]; + d2 = m_points[a[2]] - m_points[a[0]]; + n1 = d1^d2; + + m_points[v1] = newPos; + m_points[v2] = newPos; + + d1 = m_points[a[1]] - m_points[a[0]]; + d2 = m_points[a[2]] - m_points[a[0]]; + n2 = d1^d2; + + m_points[v1] = oldPosV1; + m_points[v2] = oldPosV2; + + n1.Normalize(); + n2.Normalize(); + if (n1*n2 < 0.0) + { + return std::numeric_limits::max(); + } + } + if ( m_ecolManifoldConstraint && !ManifoldConstraint(v1, v2)) + { + return std::numeric_limits::max(); + } + return qem; + } + bool MeshDecimator::ManifoldConstraint(long v1, long v2) const + { + std::set vertices; + long a, b; + long idEdge1; + long idEdge2; + long idEdgeV1V2; + for(size_t itE1 = 0; itE1 < m_vertices[v1].m_edges.Size(); ++itE1) + { + idEdge1 = m_vertices[v1].m_edges[itE1]; + a = (m_edges[idEdge1].m_v1 == v1) ? m_edges[idEdge1].m_v2 : m_edges[idEdge1].m_v1; + vertices.insert(a); + if (a != v2) + { + for(size_t itE2 = 0; itE2 < m_vertices[v2].m_edges.Size(); ++itE2) + { + idEdge2 = m_vertices[v2].m_edges[itE2]; + b = (m_edges[idEdge2].m_v1 == v2) ? m_edges[idEdge2].m_v2 : m_edges[idEdge2].m_v1; + vertices.insert(b); + if ( a==b ) + { + if (GetTriangle(v1, v2, a) == -1) + { + return false; + } + } + } + } + else + { + idEdgeV1V2 = idEdge1; + } + } + if (vertices.size() <= 4 || ( m_vertices[v1].m_onBoundary && m_vertices[v2].m_onBoundary && !m_edges[idEdgeV1V2].m_onBoundary)) + { + return false; + } + return true; + } + bool MeshDecimator::EdgeCollapse(double & qem) + { + MDEdgePriorityQueue currentEdge; + long v1, v2; + bool done = false; + do + { + done = false; + if (m_pqueue.size() == 0) + { + done = true; + break; + } + else + { + currentEdge = m_pqueue.top(); + m_pqueue.pop(); + } + } + while ( (!m_edges[currentEdge.m_name].m_tag) || (m_edges[currentEdge.m_name].m_qem != currentEdge.m_qem)); + + if (done) return false; + v1 = m_edges[currentEdge.m_name].m_v1; + v2 = m_edges[currentEdge.m_name].m_v2; + + qem = currentEdge.m_qem; + EdgeCollapse(v1, v2); + m_points[v1] = m_edges[currentEdge.m_name].m_pos ; + for(int k = 0; k < 10; k++) m_vertices[v1].m_Q[k] += m_vertices[v2].m_Q[k]; + + // Update priority queue + long idEdge; + long a, b; + SArray incidentVertices; + for(size_t itE = 0; itE < m_vertices[v1].m_edges.Size(); ++itE) + { + idEdge = m_vertices[v1].m_edges[itE]; + a = m_edges[idEdge].m_v1; + b = m_edges[idEdge].m_v2; + incidentVertices.PushBack((a != v1)?a:b); + MDEdgePriorityQueue pqEdge; + pqEdge.m_qem = m_edges[idEdge].m_qem = ComputeEdgeCost(a, b, m_edges[idEdge].m_pos); + pqEdge.m_name = idEdge; + m_pqueue.push(pqEdge); + } + long idVertex; + for(size_t itV = 0; itV< incidentVertices.Size(); ++itV) + { + idVertex = incidentVertices[itV]; + for(size_t itE = 0; itE < m_vertices[idVertex].m_edges.Size(); ++itE) + { + idEdge = m_vertices[idVertex].m_edges[itE]; + a = m_edges[idEdge].m_v1; + b = m_edges[idEdge].m_v2; + if ( a!=v1 && b!=v1) + { + MDEdgePriorityQueue pqEdge; + pqEdge.m_qem = m_edges[idEdge].m_qem = ComputeEdgeCost(a, b, m_edges[idEdge].m_pos); + pqEdge.m_name = idEdge; + m_pqueue.push(pqEdge); + } + } + } + return true; + } + bool MeshDecimator::Decimate(size_t targetNVertices, size_t targetNTriangles, double targetError) + { + double qem = 0.0; + double progressOld = -1.0; + double progress = 0.0; + char msg[1024]; + double ptgStep = 1.0; + + if (m_callBack) + { + std::ostringstream msg; + msg << "+ Mesh" << std::endl; + msg << "\t # vertices \t" << m_nPoints << std::endl; + msg << "\t # triangles \t" << m_nTriangles << std::endl; + msg << "+ Parameters" << std::endl; + msg << "\t target # of vertices \t" << targetNVertices << std::endl; + msg << "\t target # of triangles \t" << targetNTriangles << std::endl; + msg << "\t QEM \t" << targetError << std::endl; + (*m_callBack)(msg.str().c_str(), 0.0, 0.0, m_nPoints); + } + + if (m_callBack) (*m_callBack)("+ Initialize QEM \n", 0.0, 0.0, m_nPoints); + InitializeQEM(); + if (m_callBack) (*m_callBack)("+ Initialize priority queue \n", 0.0, 0.0, m_nPoints); + InitializePriorityQueue(); + if (m_callBack) (*m_callBack)("+ Simplification \n", 0.0, 0.0, m_nPoints); + double invDiag2 = 1.0 / (m_diagBB * m_diagBB); + while((m_pqueue.size() > 0) && + (m_nEdges > 0) && + (m_nVertices > targetNVertices) && + (m_nTriangles > targetNTriangles) && + (qem < targetError)) + { + progress = 100.0 - m_nVertices * 100.0 / m_nPoints; + if (fabs(progress-progressOld) > ptgStep && m_callBack) + { + sprintf(msg, "%3.2f %% V = %lu \t QEM = %f \t \t \r", progress, static_cast(m_nVertices), sqrt(qem)); + (*m_callBack)(msg, progress, qem, m_nVertices); + progressOld = progress; + } + if (!EdgeCollapse(qem)) break; + qem *= invDiag2; + } + if (m_callBack) + { + std::ostringstream msg; + msg << "+ Simplification output" << std::endl; + msg << "\t # vertices \t" << m_nVertices << std::endl; + msg << "\t # triangles \t" << m_nTriangles << std::endl; + msg << "\t QEM \t" << qem << std::endl; + (*m_callBack)(msg.str().c_str(), 100.0, qem, m_nVertices); + } + return true; + } +} diff --git a/indra/libhacd/hacdMeshDecimator.h b/indra/libhacd/hacdMeshDecimator.h new file mode 100644 index 000000000..ae3019a0b --- /dev/null +++ b/indra/libhacd/hacdMeshDecimator.h @@ -0,0 +1,116 @@ +/* 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 HACD_MESH_DECEMATOR_H +#define HACD_MESH_DECEMATOR_H +#include +#include +#include +#include +#include "hacdVersion.h" +#include "hacdVector.h" +#include "hacdSArray.h" +#include "hacdHACD.h" + +namespace HACD +{ + typedef double Float; + struct MDVertex + { + SArray m_edges; + SArray m_triangles; + Float m_Q[10]; + // 0 1 2 3 + // 4 5 6 + // 7 8 + // 9 + bool m_tag; + bool m_onBoundary; + }; + + struct MDEdge + { + long m_v1; + long m_v2; + double m_qem; + Vec3 m_pos; + bool m_onBoundary; + bool m_tag; + }; + struct MDEdgePriorityQueue + { + long m_name; + double m_qem; + inline friend bool operator<(const MDEdgePriorityQueue & lhs, const MDEdgePriorityQueue & rhs) { return (lhs.m_qem > rhs.m_qem);} + inline friend bool operator>(const MDEdgePriorityQueue & lhs, const MDEdgePriorityQueue & rhs) { return (lhs.m_qem < rhs.m_qem);} + }; + + class MeshDecimator + { + public: + //! Sets the call-back function + //! @param callBack pointer to the call-back function + void SetCallBack(CallBackFunction callBack) { m_callBack = callBack;} + //! Gives the call-back function + //! @return pointer to the call-back function + const CallBackFunction GetCallBack() const { return m_callBack;} + + inline void SetEColManifoldConstraint(bool ecolManifoldConstraint) { m_ecolManifoldConstraint = ecolManifoldConstraint; } + inline size_t GetNVertices()const {return m_nVertices;}; + inline size_t GetNTriangles() const {return m_nTriangles;}; + inline size_t GetNEdges() const {return m_nEdges;}; + void GetMeshData(Vec3 * points, Vec3 * triangles) const; + void ReleaseMemory(); + void Initialize(size_t nVertices, size_t nTriangles, + Vec3 * points, + Vec3 * triangles); + bool Decimate(size_t targetNVertices = 100, + size_t targetNTriangles = 0, + double targetError = std::numeric_limits::max()); + + MeshDecimator(void); + ~MeshDecimator(void); + private : + void EdgeCollapse(long v1, long v2); + long GetTriangle(long v1, long v2, long v3) const; + long GetEdge(long v1, long v2) const; + long IsBoundaryEdge(long v1, long v2) const; + bool IsBoundaryVertex(long v) const; + void InitializePriorityQueue(); + void InitializeQEM(); + bool ManifoldConstraint(long v1, long v2) const; + double ComputeEdgeCost(long v1, long v2, Vec3 & pos) const; + bool EdgeCollapse(double & error); + private: + Vec3 * m_triangles; + Vec3 * m_points; + size_t m_nPoints; + size_t m_nInitialTriangles; + size_t m_nVertices; + size_t m_nTriangles; + size_t m_nEdges; + double m_diagBB; + std::vector m_vertices; + std::vector m_edges; + std::priority_queue< + MDEdgePriorityQueue, + std::vector, + std::less > m_pqueue; + CallBackFunction m_callBack; //>! call-back function + bool * m_trianglesTags; + bool m_ecolManifoldConstraint; + }; +} +#endif diff --git a/indra/libhacd/hacdMicroAllocator.cpp b/indra/libhacd/hacdMicroAllocator.cpp new file mode 100644 index 000000000..c49789842 --- /dev/null +++ b/indra/libhacd/hacdMicroAllocator.cpp @@ -0,0 +1,993 @@ +#include "hacdMicroAllocator.h" + +/*! +** +** Copyright (c) 2009 by John W. Ratcliff mailto:jratcliffscarab@gmail.com +** +** 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. +** +** +** If you wish to contact me you can use the following methods: +** +** Skype ID: jratcliff63367 +** 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. + +*/ + + +#include +#include +#include +#include + +#ifdef WIN32 +#include +#endif + +#if defined(__APPLE__) || defined(LINUX) +#include +#endif + + +#pragma warning(disable:4100) + +namespace HACD +{ + +//================================================================================== +class MemMutex +{ + public: + MemMutex(void); + ~MemMutex(void); + + public: + // Blocking Lock. + void Lock(void); + + // Unlock. + void Unlock(void); + +private: + #if defined(_WIN32) || defined(_XBOX) + CRITICAL_SECTION m_Mutex; + #elif defined(__APPLE__) || defined(LINUX) + pthread_mutex_t m_Mutex; + #endif +}; + +//================================================================================== +MemMutex::MemMutex(void) +{ +#if defined(_WIN32) || defined(_XBOX) + InitializeCriticalSection(&m_Mutex); +#elif defined(__APPLE__) || defined(LINUX) + pthread_mutexattr_t mta; + pthread_mutexattr_init(&mta); + pthread_mutexattr_settype(&mta, PTHREAD_MUTEX_RECURSIVE); + pthread_mutex_init(&m_Mutex, &mta); + pthread_mutexattr_destroy(&mta); +#endif +} + +//================================================================================== +MemMutex::~MemMutex(void) +{ +#if defined(_WIN32) || defined(_XBOX) + DeleteCriticalSection(&m_Mutex); +#elif defined(__APPLE__) || defined(LINUX) + pthread_mutex_destroy(&m_Mutex); +#endif +} + +//================================================================================== +// Blocking Lock. +//================================================================================== +void MemMutex::Lock(void) +{ +#if defined(_WIN32) || defined(_XBOX) + EnterCriticalSection(&m_Mutex); +#elif defined(__APPLE__) || defined(LINUX) + pthread_mutex_lock(&m_Mutex); +#endif +} + +//================================================================================== +// Unlock. +//================================================================================== +void MemMutex::Unlock(void) +{ +#if defined(_WIN32) || defined(_XBOX) + LeaveCriticalSection(&m_Mutex); +#elif defined(__APPLE__) || defined(LINUX) + pthread_mutex_unlock(&m_Mutex); +#endif +} + + + +struct ChunkHeader +{ + ChunkHeader *mNextChunk; +}; + +// interface to add and remove new chunks to the master list. +class MicroChunkUpdate +{ +public: + virtual void addMicroChunk(NxU8 *memStart,NxU8 *memEnd,MemoryChunk *chunk) = 0; + virtual void removeMicroChunk(MemoryChunk *chunk) = 0; +}; + +class MemoryHeader +{ +public: + MemoryHeader *mNext; +}; + +// a single fixed size chunk for micro-allocations. +class MemoryChunk +{ +public: + MemoryChunk(void) + { + mData = 0; + mDataEnd = 0; + mUsedCount = 0; + mFreeList = 0; + mMyHeap = false; + mChunkSize = 0; + } + + NxU8 * init(NxU8 *chunkBase,NxU32 chunkSize,NxU32 maxChunks) + { + mChunkSize = chunkSize; + mData = chunkBase; + mDataEnd = mData+(chunkSize*maxChunks); + mFreeList = (MemoryHeader *) mData; + MemoryHeader *scan = mFreeList; + NxU8 *data = mData; + data+=chunkSize; + for (NxU32 i=0; i<(maxChunks-1); i++) + { + MemoryHeader *next = (MemoryHeader *)data; + scan->mNext = next; + data+=chunkSize; + scan = next; + } + scan->mNext = 0; + return mDataEnd; + } + + inline void * allocate(MicroHeap *heap,NxU32 chunkSize,NxU32 maxChunks,MicroChunkUpdate *update) + { + void *ret = 0; + + if ( mData == 0 ) + { + mMyHeap = true; + mData = (NxU8 *)heap->micro_malloc( chunkSize * maxChunks ); + init(mData,chunkSize,maxChunks); + update->addMicroChunk(mData,mDataEnd,this); + } + if ( mFreeList ) + { + mUsedCount++; + ret = mFreeList; + mFreeList = mFreeList->mNext; + } + return ret; + } + + inline void deallocate(void *p,MicroHeap *heap,MicroChunkUpdate *update) + { +#ifdef _DEBUG + assert(mUsedCount); + NxU8 *s = (NxU8 *)p; + assert( s >= mData && s < mDataEnd ); +#endif + MemoryHeader *mh = mFreeList; + mFreeList = (MemoryHeader *)p; + mFreeList->mNext = mh; + mUsedCount--; + if ( mUsedCount == 0 && mMyHeap ) // free the heap back to the application if we are done with this. + { + update->removeMicroChunk(this); + heap->micro_free(mData); + mMyHeap = false; + mData = 0; + mDataEnd = 0; + mFreeList = 0; + } + } + + NxU32 getChunkSize(void) const { return mChunkSize; }; + + bool isInside(const NxU8 *p) const + { + return p>=mData && p < mDataEnd; + } + +private: + bool mMyHeap; + NxU8 *mData; + NxU8 *mDataEnd; + NxU32 mUsedCount; + MemoryHeader *mFreeList; + NxU32 mChunkSize; +}; + +#define DEFAULT_CHUNKS 32 + +class MemoryChunkChunk +{ +public: + MemoryChunkChunk(void) + { + mNext = 0; + mChunkSize = 0; + mMaxChunks = 0; + } + + ~MemoryChunkChunk(void) + { + } + + inline void * allocate(MemoryChunk *¤t,MicroChunkUpdate *update) + { + void *ret = 0; + + MemoryChunkChunk *scan = this; + while ( scan && ret == 0 ) + { + for (NxU32 i=0; imChunks[i].allocate(mHeap,mChunkSize,mMaxChunks,update); + if ( ret ) + { + current = &scan->mChunks[i]; + scan = 0; + break; + } + } + if ( scan ) + scan = scan->mNext; + } + + if ( !ret ) + { + MemoryChunkChunk *mcc = (MemoryChunkChunk *)mHeap->micro_malloc( sizeof(MemoryChunkChunk) ); + new ( mcc ) MemoryChunkChunk; + MemoryChunkChunk *onext = mNext; + mNext = mcc; + mcc->mNext = onext; + ret = mcc->mChunks[0].allocate(mHeap,mChunkSize,mMaxChunks,update); + current = &mcc->mChunks[0]; + } + + return ret; + } + + NxU8 * init(NxU8 *chunkBase,NxU32 fixedSize,NxU32 chunkSize,MemoryChunk *¤t,MicroHeap *heap) + { + mHeap = heap; + mChunkSize = chunkSize; + mMaxChunks = fixedSize/chunkSize; + current = &mChunks[0]; + chunkBase = mChunks[0].init(chunkBase,chunkSize,mMaxChunks); + return chunkBase; + } + + MicroHeap *mHeap; + NxU32 mChunkSize; + NxU32 mMaxChunks; + MemoryChunkChunk *mNext; + MemoryChunk mChunks[DEFAULT_CHUNKS]; +}; + +class FixedMemory +{ +public: + FixedMemory(void) + { + mCurrent = 0; + } + + void * allocate(MicroChunkUpdate *update) + { + void *ret = mCurrent->allocate(mChunks.mHeap,mChunks.mChunkSize,mChunks.mMaxChunks,update); + if ( ret == 0 ) + { + ret = mChunks.allocate(mCurrent,update); + } + return ret; + } + + NxU8 * init(NxU8 *chunkBase,NxU32 chunkSize,NxU32 fixedSize,MicroHeap *heap) + { + mMemBegin = chunkBase; + mMemEnd = chunkBase+fixedSize; + mChunks.init(chunkBase,fixedSize,chunkSize,mCurrent,heap); + return mMemEnd; + } + + NxU8 *mMemBegin; + NxU8 *mMemEnd; + MemoryChunk *mCurrent; // the current memory chunk we are operating in. + MemoryChunkChunk mChunks; // the collection of all memory chunks used. +}; + +class MicroChunk +{ +public: + + void set(NxU8 *memStart,NxU8 *memEnd,MemoryChunk *mc) + { + mMemStart = memStart; + mMemEnd = memEnd; + mChunk = mc; + mPad = 0; + } + + inline bool inside(const NxU8 *p) const + { + return p >= mMemStart && p < mMemEnd; + } + + NxU8 *mMemStart; + NxU8 *mMemEnd; + MemoryChunk *mChunk; + NxU8 *mPad; // padding to make it 16 byte aligned. +}; + +class MyMicroAllocator : public MicroAllocator, public MicroChunkUpdate, public MemMutex +{ +public: + MyMicroAllocator(MicroHeap *heap,void *baseMem,NxU32 initialSize,NxU32 chunkSize) + { + mLastMicroChunk = 0; + mMicroChunks = 0; + mMicroChunkCount = 0; + mMaxMicroChunks = 0; + mHeap = heap; + mChunkSize = chunkSize; + + // 0 through 8 bytes + for (NxU32 i=0; i<=8; i++) + { + mFixedAllocators[i] = &mAlloc[0]; + } + + // 9 through 16 bytes + for (NxU32 i=9; i<=16; i++) + { + mFixedAllocators[i] = &mAlloc[1]; + } + + // 17 through 32 bytes + for (NxU32 i=17; i<=32; i++) + { + mFixedAllocators[i] = &mAlloc[2]; + } + + // 33 through 64 + for (NxU32 i=33; i<=64; i++) + { + mFixedAllocators[i] = &mAlloc[3]; + } + + // 65 through 128 + for (NxU32 i=65; i<=128; i++) + { + mFixedAllocators[i] = &mAlloc[4]; + } + + // 129 through 255 + for (NxU32 i=129; i<257; i++) + { + mFixedAllocators[i] = &mAlloc[5]; + } + + mBaseMem = (NxU8 *)baseMem; + mBaseMemEnd = mBaseMem+initialSize; + + NxU8 *chunkBase = (NxU8 *)baseMem+sizeof(MyMicroAllocator); + + + chunkBase+=32; + NxU64 ptr = (NxU64)chunkBase; + ptr = ptr>>4; + ptr = ptr<<4; // make sure it is 16 byte aligned. + chunkBase = (NxU8 *)ptr; + + mChunkStart = chunkBase; + chunkBase = mAlloc[0].init(chunkBase,8,chunkSize,heap); + chunkBase = mAlloc[1].init(chunkBase,16,chunkSize,heap); + chunkBase = mAlloc[2].init(chunkBase,32,chunkSize,heap); + chunkBase = mAlloc[3].init(chunkBase,64,chunkSize,heap); + chunkBase = mAlloc[4].init(chunkBase,128,chunkSize,heap); + chunkBase = mAlloc[5].init(chunkBase,256,chunkSize,heap); + mChunkEnd = chunkBase; + + assert(chunkBase <= mBaseMemEnd ); + + } + + ~MyMicroAllocator(void) + { + if ( mMicroChunks ) + { + mHeap->micro_free(mMicroChunks); + } + } + + virtual NxU32 getChunkSize(MemoryChunk *chunk) + { + return chunk ? chunk->getChunkSize() : 0; + } + + // we have to steal one byte out of every allocation to record the size, so we can efficiently de-allocate it later. + virtual void * malloc(size_t size) + { + void *ret = 0; + Lock(); + assert( size <= 256 ); + if ( size <= 256 ) + { + ret = mFixedAllocators[size]->allocate(this); + } + Unlock(); + return ret; + } + + virtual void free(void *p,MemoryChunk *chunk) + { + Lock(); + chunk->deallocate(p,mHeap,this); + Unlock(); + } + + // perform a binary search on the sorted list of chunks. + MemoryChunk * binarySearchMicroChunks(const NxU8 *p) + { + MemoryChunk *ret = 0; + + NxU32 low = 0; + NxU32 high = mMicroChunkCount; + + while ( low != high ) + { + NxU32 mid = (high-low)/2+low; + MicroChunk &chunk = mMicroChunks[mid]; + if ( chunk.inside(p)) + { + mLastMicroChunk = &chunk; + ret = chunk.mChunk; + break; + } + else + { + if ( p > chunk.mMemEnd ) + { + low = mid+1; + } + else + { + high = mid; + } + } + } + + return ret; + } + + virtual MemoryChunk * isMicroAlloc(const void *p) // returns true if this pointer is handled by the micro-allocator. + { + MemoryChunk *ret = 0; + + Lock(); + + const NxU8 *s = (const NxU8 *)p; + + if ( s >= mChunkStart && s < mChunkEnd ) + { + NxU32 index = (NxU32)(s-mChunkStart)/mChunkSize; + assert(index>=0 && index < 6 ); + ret = &mAlloc[index].mChunks.mChunks[0]; + assert( ret->isInside(s) ); + } + else if ( mMicroChunkCount ) + { + if ( mLastMicroChunk && mLastMicroChunk->inside(s) ) + { + ret = mLastMicroChunk->mChunk; + } + else + { + if ( mMicroChunkCount >= 4 ) + { + ret = binarySearchMicroChunks(s); +#ifdef _DEBUG + if (ret ) + { + assert( ret->isInside(s) ); + } + else + { + for (NxU32 i=0; iisInside(s) ); + mLastMicroChunk = &mMicroChunks[i]; + break; + } + } + } + } + } +#ifdef _DEBUG + if ( ret ) + assert( ret->isInside(s) ); +#endif + Unlock(); + return ret; + } + + MicroHeap * getMicroHeap(void) const { return mHeap; }; + + void allocateMicroChunks(void) + { + if ( mMaxMicroChunks == 0 ) + { + mMaxMicroChunks = 64; // initial reserve. + mMicroChunks = (MicroChunk *)mHeap->micro_malloc( sizeof(MicroChunk)*mMaxMicroChunks ); + } + else + { + mMaxMicroChunks*=2; + mMicroChunks = (MicroChunk *)mHeap->micro_realloc( mMicroChunks, sizeof(MicroChunk)*mMaxMicroChunks); + } + } + + // perform an insertion sort of the new chunk. + virtual void addMicroChunk(NxU8 *memStart,NxU8 *memEnd,MemoryChunk *chunk) + { + if ( mMicroChunkCount >= mMaxMicroChunks ) + { + allocateMicroChunks(); + } + + bool inserted = false; + for (NxU32 i=0; ii; j--) + { + mMicroChunks[j] = mMicroChunks[j-1]; + } + mMicroChunks[i].set( memStart, memEnd, chunk ); + mLastMicroChunk = &mMicroChunks[i]; + mMicroChunkCount++; + inserted = true; + break; + } + } + if ( !inserted ) + { + mMicroChunks[mMicroChunkCount].set(memStart,memEnd,chunk); + mLastMicroChunk = &mMicroChunks[mMicroChunkCount]; + mMicroChunkCount++; + } + } + + virtual void removeMicroChunk(MemoryChunk *chunk) + { + mLastMicroChunk = 0; + #ifdef _DEBUG + bool removed = false; + #endif + for (NxU32 i=0; iallocate(this); + Unlock(); + return ret; + } + + inline void inline_free(void *p,MemoryChunk *chunk) // free relative to previously located MemoryChunk + { + Lock(); + chunk->deallocate(p,mHeap,this); + Unlock(); + } + + inline MemoryChunk * inline_isMicroAlloc(const void *p) // returns pointer to the chunk this memory belongs to, or null if not a micro-allocated block. + { + MemoryChunk *ret = 0; + + Lock(); + + const NxU8 *s = (const NxU8 *)p; + + if ( s >= mChunkStart && s < mChunkEnd ) + { + NxU32 index = (NxU32)(s-mChunkStart)/mChunkSize; + assert(index>=0 && index < 6 ); + ret = &mAlloc[index].mChunks.mChunks[0]; + } + else if ( mMicroChunkCount ) + { + if ( mLastMicroChunk && mLastMicroChunk->inside(s) ) + { + ret = mLastMicroChunk->mChunk; + } + else + { + if ( mMicroChunkCount >= 4 ) + { + ret = binarySearchMicroChunks(s); + } + else + { + for (NxU32 i=0; imicro_malloc(initialSize); + MyMicroAllocator *mc = (MyMicroAllocator *)baseMem; + new ( mc ) MyMicroAllocator(heap,baseMem,initialSize,chunkSize); + return static_cast< MicroAllocator *>(mc); +} + +void releaseMicroAllocator(MicroAllocator *m) +{ + MyMicroAllocator *mc = static_cast< MyMicroAllocator *>(m); + MicroHeap *mh = mc->getMicroHeap(); + mc->~MyMicroAllocator(); + mh->micro_free(mc); +} + +class MyHeapManager : public MicroHeap, public HeapManager +{ +public: + MyHeapManager(NxU32 defaultChunkSize) + { + mMicro = createMicroAllocator(this,defaultChunkSize); + } + + ~MyHeapManager(void) + { + releaseMicroAllocator(mMicro); + } + + // heap allocations used by the micro allocator. + virtual void * micro_malloc(size_t size) + { + return ::malloc(size); + } + + virtual void micro_free(void *p) + { + return ::free(p); + } + + virtual void * micro_realloc(void *oldMem,size_t newSize) + { + return ::realloc(oldMem,newSize); + } + + virtual void * heap_malloc(size_t size) + { + void *ret; + + if ( size <= 256 ) // micro allocator only handles allocations between 0 and 256 bytes in length. + { + ret = mMicro->malloc(size); + } + else + { + ret = ::malloc(size); + } + return ret; + } + + virtual void heap_free(void *p) + { + MemoryChunk *chunk = mMicro->isMicroAlloc(p); + if ( chunk ) + { + mMicro->free(p,chunk); + } + else + { + ::free(p); + } + } + + virtual void * heap_realloc(void *oldMem,size_t newSize) + { + void *ret = 0; + + MemoryChunk *chunk = mMicro->isMicroAlloc(oldMem); + if ( chunk ) + { + ret = heap_malloc(newSize); + NxU32 oldSize = chunk->getChunkSize(); + if ( oldSize < newSize ) + { + memcpy(ret,oldMem,oldSize); + } + else + { + memcpy(ret,oldMem,newSize); + } + mMicro->free(oldMem,chunk); + } + else + { + ret = ::realloc(oldMem,newSize); + } + + return ret; + } + + inline void * inline_heap_malloc(size_t size) + { + return size<=256 ? ((MyMicroAllocator *)mMicro)->inline_malloc(size) : ::malloc(size); + } + + inline void inline_heap_free(void *p) + { + MemoryChunk *chunk = ((MyMicroAllocator *)mMicro)->inline_isMicroAlloc(p); + if ( chunk ) + { + ((MyMicroAllocator *)mMicro)->inline_free(p,chunk); + } + else + { + ::free(p); + } + + } + +private: + MicroAllocator *mMicro; +}; + + +HeapManager * createHeapManager(NxU32 defaultChunkSize) +{ + MyHeapManager *m = (MyHeapManager *)::malloc(sizeof(MyHeapManager)); + new ( m ) MyHeapManager(defaultChunkSize); + return static_cast< HeapManager *>(m); +} + +void releaseHeapManager(HeapManager *heap) +{ + MyHeapManager *m = static_cast< MyHeapManager *>(heap); + m->~MyHeapManager(); + free(m); +} + + +#define TEST_SIZE 63 +#define TEST_ALLOC_COUNT 8192 +#define TEST_RUN 40000000 +#define TEST_INLINE 1 + +#ifdef WIN32 +#include +#pragma comment(lib,"winmm.lib") +#else +static NxU32 timeGetTime(void) +{ + return 0; +} +#endif +#include + +void performUnitTests(void) +{ + void *allocs[TEST_ALLOC_COUNT]; + for (NxU32 i=0; iheap_free( allocs[index] ); +#endif + allocs[index] = 0; + } + else + { + NxU32 asize = (rand()&TEST_SIZE); + if ( (rand()&127)==0) asize+=256; // one out of every 15 allocs is larger than 256 bytes. +#if TEST_INLINE + allocs[index] = heap_malloc(hm,asize); +#else + allocs[index] = hm->heap_malloc(asize); +#endif + } + } + + for (NxU32 i=0; iheap_free(allocs[i] ); +#endif + allocs[i] = 0; + } + } + + NxU32 etime = timeGetTime(); + printf("Micro allocation test took %d milliseconds.\r\n", etime - stime ); + + } + + { + + NxU32 stime = timeGetTime(); + srand(0); + + + for (NxU32 i=0; iinline_heap_malloc(size); +} + +void heap_free(HeapManager *hm,void *p) +{ + ((MyHeapManager *)hm)->inline_heap_free(p); +} + +void * heap_realloc(HeapManager *hm,void *oldMem,size_t newSize) +{ + return hm->heap_realloc(oldMem,newSize); +} + +}; // end of namespace diff --git a/indra/libhacd/hacdMicroAllocator.h b/indra/libhacd/hacdMicroAllocator.h new file mode 100644 index 000000000..cf2e80d27 --- /dev/null +++ b/indra/libhacd/hacdMicroAllocator.h @@ -0,0 +1,204 @@ +#ifndef MICRO_ALLOCATOR_H + +#define MICRO_ALLOCATOR_H + +/*! +** +** Copyright (c) 2009 by John W. Ratcliff mailto:jratcliffscarab@gmail.com +** +** 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. +** +** If you wish to contact me you can use the following methods: +** +** Skype ID: jratcliff63367 +** 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. + +*/ + + +// This code snippet provides a high speed micro-allocator. +// +// The concept is that you reserve an initial bank of memory for small allocations. Ideally a megabyte or two. +// The amount of memory reserved is equal to chunkSize*6 +// +// All micro-allocations are split into 6 seperate pools. +// They are: 0-8 bytes +// 9-32 bytes +// 33-64 bytes +// 65-128 bytes +// 129-256 bytes +// +// On creation of the micro-allocation system you preserve a certiain amount of memory for each of these banks. +// +// The user provides a heap interface to callback for additional memory as needed. +// +// In most cases allocations are order-N and frees are order-N as well. +// +// The larger a buffer you provide, the closer to 'order-N' the allocator behaves. +// +// This kind of a micro-allocator is ideal for use with STL as it does many tiny allocations. +// All allocations are 16 byte aligned (with the exception of the 8 byte allocations, which are 8 byte aligned every other one). +// +#include + +#ifdef WIN32 + typedef __int64 NxI64; + typedef signed int NxI32; + typedef signed short NxI16; + typedef signed char NxI8; + + typedef unsigned __int64 NxU64; + typedef unsigned int NxU32; + typedef unsigned short NxU16; + typedef unsigned char NxU8; + + typedef float NxF32; + typedef double NxF64; + +#elif __gnu_linux__ + typedef long long NxI64; + typedef signed int NxI32; + typedef signed short NxI16; + typedef signed char NxI8; + + typedef unsigned long long NxU64; + typedef unsigned int NxU32; + typedef unsigned short NxU16; + typedef unsigned char NxU8; + + typedef float NxF32; + typedef double NxF64; + +#elif __APPLE__ + typedef long long NxI64; + typedef signed int NxI32; + typedef signed short NxI16; + typedef signed char NxI8; + + typedef unsigned long long NxU64; + typedef unsigned int NxU32; + typedef unsigned short NxU16; + typedef unsigned char NxU8; + + typedef float NxF32; + typedef double NxF64; + +#elif __CELLOS_LV2__ + typedef long long NxI64; + typedef signed int NxI32; + typedef signed short NxI16; + typedef signed char NxI8; + + typedef unsigned long long NxU64; + typedef unsigned int NxU32; + typedef unsigned short NxU16; + typedef unsigned char NxU8; + + typedef float NxF32; + typedef double NxF64; + +#elif _XBOX + typedef __int64 NxI64; + typedef signed int NxI32; + typedef signed short NxI16; + typedef signed char NxI8; + + typedef unsigned __int64 NxU64; + typedef unsigned int NxU32; + typedef unsigned short NxU16; + typedef unsigned char NxU8; + + typedef float NxF32; + typedef double NxF64; + +#elif defined(__PPCGEKKO__) + typedef long long NxI64; + typedef signed int NxI32; + typedef signed short NxI16; + typedef signed char NxI8; + + typedef unsigned long long NxU64; + typedef unsigned int NxU32; + typedef unsigned short NxU16; + + typedef float NxF32; + typedef double NxF64; + +#else + #error Unknown platform! +#endif + + +namespace HACD +{ + +// user provided heap allocator +class MicroHeap +{ +public: + virtual void * micro_malloc(size_t size) = 0; + virtual void micro_free(void *p) = 0; + virtual void * micro_realloc(void *oldMen,size_t newSize) = 0; +}; + +class MemoryChunk; + +class MicroAllocator +{ +public: + virtual void * malloc(size_t size) = 0; + virtual void free(void *p,MemoryChunk *chunk) = 0; // free relative to previously located MemoryChunk + virtual MemoryChunk * isMicroAlloc(const void *p) = 0; // returns pointer to the chunk this memory belongs to, or null if not a micro-allocated block. + virtual NxU32 getChunkSize(MemoryChunk *chunk) = 0; +}; + +MicroAllocator *createMicroAllocator(MicroHeap *heap,NxU32 chunkSize=32768); // initial chunk size 32k per block. +void releaseMicroAllocator(MicroAllocator *m); + +class HeapManager +{ +public: + virtual void * heap_malloc(size_t size) = 0; + virtual void heap_free(void *p) = 0; + virtual void * heap_realloc(void *oldMem,size_t newSize) = 0; +}; + + +// creates a heap manager that uses micro-allocations for all allocations < 256 bytes and standard malloc/free for anything larger. +HeapManager * createHeapManager(NxU32 defaultChunkSize=32768); +void releaseHeapManager(HeapManager *heap); + +// about 10% faster than using the virtual interface, inlines the functions as much as possible. +void * heap_malloc(HeapManager *hm,size_t size); +void heap_free(HeapManager *hm,void *p); +void * heap_realloc(HeapManager *hm,void *oldMem,size_t newSize); + +void performUnitTests(void); + +// + +}; // end of namespace + +#endif diff --git a/indra/libhacd/hacdRaycastMesh.cpp b/indra/libhacd/hacdRaycastMesh.cpp new file mode 100644 index 000000000..5edd9adc3 --- /dev/null +++ b/indra/libhacd/hacdRaycastMesh.cpp @@ -0,0 +1,280 @@ +/* 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 "hacdRaycastMesh.h" +#include +#include +#include +#include "hacdManifoldMesh.h" +namespace HACD +{ + bool BBox::Raycast(const Vec3 & origin, const Vec3 & dir, Float & distMin) const + { + Vec3 d = m_max - m_min; + Float r2 = 0.25 * (d.X()*d.X()+d.Y()*d.Y()+d.Z()*d.Z()); + Vec3 c = 0.5 * m_max + 0.5 * m_min; + Vec3 u = (c-origin); + Vec3 a = u - (u * dir) * dir; + Float dist2 = (a.X()*a.X()+a.Y()*a.Y()+a.Z()*a.Z()); + distMin = (u.X()*u.X()+u.Y()*u.Y()+u.Z()*u.Z()); + if ( distMin > r2 ) // origin outside the sphere of center c and radius r + { + distMin = sqrt(distMin) - sqrt(r2); // the distance to any point inside the sphere is higher than |origin - c| - r + } + else + { + distMin = 0.0; + } + if (dist2 > r2) return false; + return true; + } + void RMNode::ComputeBB() + { + if (m_triIDs.Size() == 0) + { + return; + } + Vec3 * const triangles = m_rm->m_triangles; + Vec3 * const vertices = m_rm->m_vertices; + const Float minFloat = std::numeric_limits::min(); + const Float maxFloat = std::numeric_limits::max(); + m_bBox.m_max = Vec3(minFloat, minFloat, minFloat); + m_bBox.m_min = Vec3(maxFloat, maxFloat, maxFloat); + Float x, y, z; + long v, f; + for(size_t id = 0; id < m_triIDs.Size(); ++id) + { + f = m_triIDs[id]; + for(size_t k = 0; k < 3; ++k) + { + v = triangles[f][k]; + x = vertices[v].X(); + y = vertices[v].Y(); + z = vertices[v].Z(); + if ( x < m_bBox.m_min.X()) m_bBox.m_min.X() = x; + if ( x > m_bBox.m_max.X()) m_bBox.m_max.X() = x; + if ( y < m_bBox.m_min.Y()) m_bBox.m_min.Y() = y; + if ( y > m_bBox.m_max.Y()) m_bBox.m_max.Y() = y; + if ( z < m_bBox.m_min.Z()) m_bBox.m_min.Z() = z; + if ( z > m_bBox.m_max.Z()) m_bBox.m_max.Z() = z; + } + } + } + void RaycastMesh::ComputeBB() + { + if (m_nVertices == 0) + { + return; + } + m_bBox.m_min = m_vertices[0]; + m_bBox.m_max = m_vertices[0]; + Float x, y, z; + for (size_t v = 1; v < m_nVertices ; v++) + { + x = m_vertices[v].X(); + y = m_vertices[v].Y(); + z = m_vertices[v].Z(); + if ( x < m_bBox.m_min.X()) m_bBox.m_min.X() = x; + else if ( x > m_bBox.m_max.X()) m_bBox.m_max.X() = x; + if ( y < m_bBox.m_min.Y()) m_bBox.m_min.Y() = y; + else if ( y > m_bBox.m_max.Y()) m_bBox.m_max.Y() = y; + if ( z < m_bBox.m_min.Z()) m_bBox.m_min.Z() = z; + else if ( z > m_bBox.m_max.Z()) m_bBox.m_max.Z() = z; + } + } + void RaycastMesh::Initialize(size_t nVertices, size_t nTriangles, + Vec3 * vertices, Vec3 * triangles, + size_t maxDepth, size_t minLeafSize, Float minAxisSize) + { + m_triangles = triangles; + m_vertices = vertices; + m_nVertices = nVertices; + m_nTriangles = nTriangles; + delete [] m_nodes; + m_nNodes = 0; + m_nMaxNodes = 0; + for(size_t k = 0; k < maxDepth; k++) + { + m_nMaxNodes += (1 << maxDepth); + } + m_nodes = new RMNode[m_nMaxNodes]; + RMNode & root = m_nodes[AddNode()]; + root.m_triIDs.Resize(nTriangles); + for(size_t t = 0; t < m_nTriangles; ++t) root.m_triIDs.PushBack(t); + root.m_rm = this; + root.m_id = 0; + root.Create(0, maxDepth, minLeafSize, minAxisSize); + } + RaycastMesh::RaycastMesh(void) + { + m_triangles = 0; + m_vertices = 0; + m_nVertices = 0; + m_nTriangles = 0; + m_nodes = 0; + m_nNodes = 0; + m_nMaxNodes = 0; + } + RaycastMesh::~RaycastMesh(void) + { + delete [] m_nodes; + } + void RMNode::Create(size_t depth, size_t maxDepth, size_t minLeafSize, Float minAxisSize) + { + ComputeBB(); + Vec3 d = m_bBox.m_max - m_bBox.m_min; + Float maxDiff = std::max(d.X(), std::max(d.Y(), d.Z())); + RMSplitAxis split; + if (d.X() == maxDiff) split = RMSplitAxis_X; + else if (d.Y() == maxDiff) split = RMSplitAxis_Y; + else split = RMSplitAxis_Z; + + if (depth == maxDepth || minLeafSize >= m_triIDs.Size() || maxDiff < minAxisSize) + { + m_leaf = true; + return; + } + m_idLeft = m_rm->AddNode(); + m_idRight = m_rm->AddNode(); + RMNode & leftNode = m_rm->m_nodes[m_idLeft]; + RMNode & rightNode = m_rm->m_nodes[m_idRight]; + leftNode.m_id = m_idLeft; + rightNode.m_id = m_idRight; + leftNode.m_bBox = m_bBox; + rightNode.m_bBox = m_bBox; + leftNode.m_rm = m_rm; + rightNode.m_rm = m_rm; + + if ( split == RMSplitAxis_X) + { + leftNode.m_bBox.m_max.X() = leftNode.m_bBox.m_max.X() - d.X() * 0.5; + rightNode.m_bBox.m_min.X() = leftNode.m_bBox.m_max.X(); + } + else if ( split == RMSplitAxis_Y) + { + leftNode.m_bBox.m_max.Y() = leftNode.m_bBox.m_max.Y() - d.Y() * 0.5; + rightNode.m_bBox.m_min.Y() = leftNode.m_bBox.m_max.Y(); + } + else + { + leftNode.m_bBox.m_max.Z() = leftNode.m_bBox.m_max.Z() - d.Z() * 0.5; + rightNode.m_bBox.m_min.Z() = leftNode.m_bBox.m_max.Z(); + } + long f; + long pts[3]; + long v; + Vec3 * const triangles = m_rm->m_triangles; + Vec3 * const vertices = m_rm->m_vertices; + leftNode.m_triIDs.Resize(m_triIDs.Size()); + rightNode.m_triIDs.Resize(m_triIDs.Size()); + bool found; + for(size_t id = 0; id < m_triIDs.Size(); ++id) + { + f = m_triIDs[id]; + pts[0] = triangles[f].X(); + pts[1] = triangles[f].Y(); + pts[2] = triangles[f].Z(); + + found = false; + for (int k = 0; k < 3; ++k) + { + v = pts[k]; + if ( vertices[v].X() <= leftNode.m_bBox.m_max.X() && + vertices[v].X() >= leftNode.m_bBox.m_min.X() && + vertices[v].Y() <= leftNode.m_bBox.m_max.Y() && + vertices[v].Y() >= leftNode.m_bBox.m_min.Y() && + vertices[v].Z() <= leftNode.m_bBox.m_max.Z() && + vertices[v].Z() >= leftNode.m_bBox.m_min.Z() ) + { + leftNode.m_triIDs.PushBack(f); + found = true; + break; + } + } + if (!found) + { + for (int k = 0; k < 3; ++k) + { + v = pts[k]; + if ( vertices[v].X() <= rightNode.m_bBox.m_max.X() && + vertices[v].X() >= rightNode.m_bBox.m_min.X() && + vertices[v].Y() <= rightNode.m_bBox.m_max.Y() && + vertices[v].Y() >= rightNode.m_bBox.m_min.Y() && + vertices[v].Z() <= rightNode.m_bBox.m_max.Z() && + vertices[v].Z() >= rightNode.m_bBox.m_min.Z() ) + { + rightNode.m_triIDs.PushBack(f); + break; + } + } + } + } + rightNode.Create(depth+1, maxDepth, minLeafSize, minAxisSize); + leftNode.Create(depth+1, maxDepth, minLeafSize, minAxisSize); + m_triIDs.Clear(); + } + bool RaycastMesh::Raycast(const Vec3 & from, const Vec3 & dir, long & triID, Float & distance, Vec3 & hitPoint, Vec3 & hitNormal) const + { + distance = std::numeric_limits::max(); + if (m_nNodes == 0) return false; + return m_nodes[0].Raycast(from, dir, triID, distance, hitPoint, hitNormal); + } + bool RMNode::Raycast(const Vec3 & from, const Vec3 & dir, long & triID, Float & distance, Vec3 & hitPoint, Vec3 & hitNormal) const + { + Float distToSphere; + if (m_bBox.Raycast(from, dir, distToSphere) && (distToSphere < distance)) + { + if (m_leaf) + { + long f, i1, j1, k1; + Vec3 * const triangles = m_rm->m_triangles; + Vec3 * const vertices = m_rm->m_vertices; + Vec3 u1, v1, normal1; + double dist = 0.0; + long nhit = 0; + bool ret = false; + for(size_t id = 0; id < m_triIDs.Size(); ++id) + { + f = m_triIDs[id]; + i1 = triangles[f].X(); + j1 = triangles[f].Y(); + k1 = triangles[f].Z(); + u1 = vertices[j1] - vertices[i1]; + v1 = vertices[k1] - vertices[i1]; + normal1 = (u1 ^ v1); + if (dir * normal1 > 0.0) + { + nhit = IntersectRayTriangle(from, dir, vertices[i1], vertices[j1], vertices[k1], dist); + if (nhit==1 && distance>dist) + { + normal1.Normalize(); + hitNormal = normal1; + hitPoint = from + dist * dir; + distance = dist; + triID = f; + ret = true; + } + } + } + return ret; + } + bool ret1 = false; + bool ret2 = false; + if (m_idRight >= 0) ret1 = m_rm->m_nodes[m_idRight].Raycast(from, dir, triID, distance, hitPoint, hitNormal); + if (m_idLeft >= 0) ret2 = m_rm->m_nodes[m_idLeft].Raycast(from, dir, triID, distance, hitPoint, hitNormal); + return ret1 || ret2; + } + return false; + } +} diff --git a/indra/libhacd/hacdRaycastMesh.h b/indra/libhacd/hacdRaycastMesh.h new file mode 100644 index 000000000..080f228c8 --- /dev/null +++ b/indra/libhacd/hacdRaycastMesh.h @@ -0,0 +1,97 @@ + + +/* 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. + */ +#ifndef RAYCAST_MESH_H +#define RAYCAST_MESH_H +#include "hacdSArray.h" +#include +#include "hacdVersion.h" +#include "hacdVector.h" +#include "hacdSArray.h" + +namespace HACD +{ + typedef double Float; + class RaycastMesh; + class RMNode; + class BBox + { + public: + bool Raycast(const Vec3 & origin, const Vec3 & dir, Float & distance) const; + BBox(void){} + ~BBox(void){} + private: + Vec3 m_min; + Vec3 m_max; + friend class RMNode; + friend class RaycastMesh; + }; + + enum RMSplitAxis + { + RMSplitAxis_X, + RMSplitAxis_Y, + RMSplitAxis_Z + }; + class RMNode + { + public: + void ComputeBB(); + bool Raycast(const Vec3 & from, const Vec3 & dir, long & triID, Float & distance, Vec3 & hitPoint, Vec3 & hitNormal) const; + void Create(size_t depth, size_t maxDepth, size_t minLeafSize, Float minAxisSize); + ~RMNode(void){} + RMNode(void) + { + m_idRight = m_idLeft = m_id = -1; + m_rm = 0; + m_leaf = false; + } + long m_id; + long m_idLeft; + long m_idRight; + BBox m_bBox; + SArray m_triIDs; + RaycastMesh * m_rm; + bool m_leaf; + }; + + class RaycastMesh + { + public: + size_t GetNNodes() const { return m_nNodes;} + size_t AddNode() { m_nNodes++; return m_nNodes-1; } + void ComputeBB(); + bool Raycast(const Vec3 & from, const Vec3 & dir, long & triID, Float & distance, Vec3 & hitPoint, Vec3 & hitNormal) const; + void Initialize(size_t nVertices, size_t nTriangles, + Vec3 * vertices, Vec3 * triangles, + size_t maxDepth=15, size_t minLeafSize = 4, Float minAxisSize = 2.0); + RaycastMesh(void); + ~RaycastMesh(void); + private : + + private: + Vec3 * m_triangles; + Vec3 * m_vertices; + size_t m_nVertices; + size_t m_nTriangles; + RMNode * m_nodes; + BBox m_bBox; + size_t m_nNodes; + size_t m_nMaxNodes; + friend class RMNode; + }; +} +#endif diff --git a/indra/libhacd/hacdSArray.h b/indra/libhacd/hacdSArray.h new file mode 100644 index 000000000..50275063f --- /dev/null +++ b/indra/libhacd/hacdSArray.h @@ -0,0 +1,154 @@ +/* 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 HACD_SARRAY_H +#define HACD_SARRAY_H +#include "hacdVersion.h" +#include +#include +#include +#define SARRAY_DEFAULT_MIN_SIZE 16 + +namespace HACD +{ + //! SArray. + template < typename T, size_t N > 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 Resize(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 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; + }; +} +#endif diff --git a/indra/libhacd/hacdVector.h b/indra/libhacd/hacdVector.h new file mode 100644 index 000000000..0328965bf --- /dev/null +++ b/indra/libhacd/hacdVector.h @@ -0,0 +1,69 @@ +/* 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 HACD_VECTOR_H +#define HACD_VECTOR_H +#include +#include +#include "hacdVersion.h" + +namespace HACD +{ + typedef double Real; + //! 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; + Vec3(); + Vec3(T a); + Vec3(T x, T y, T z); + Vec3(const Vec3 & rhs); + /*virtual*/ ~Vec3(void); + + private: + T m_data[3]; + }; + template + const bool Colinear(const Vec3 & a, const Vec3 & b, const Vec3 & c); + template + const T Volume(const Vec3 & a, const Vec3 & b, const Vec3 & c, const Vec3 & d); + +} +#include "hacdVector.inl" // template implementation +#endif diff --git a/indra/libhacd/hacdVector.inl b/indra/libhacd/hacdVector.inl new file mode 100644 index 000000000..bf352f09d --- /dev/null +++ b/indra/libhacd/hacdVector.inl @@ -0,0 +1,178 @@ +#pragma once +#ifndef HACD_VECTOR_INL +#define HACD_VECTOR_INL +namespace HACD +{ + template + inline Vec3 operator*(T lhs, const Vec3 & rhs) + { + return Vec3(lhs * rhs.X(), lhs * rhs.Y(), lhs * rhs.Z()); + } + template + inline T & Vec3::X() + { + return m_data[0]; + } + template + inline T & Vec3::Y() + { + return m_data[1]; + } + template + inline T & Vec3::Z() + { + return m_data[2]; + } + template + inline const T & Vec3::X() const + { + return m_data[0]; + } + template + inline const T & Vec3::Y() const + { + return m_data[1]; + } + template + inline const T & Vec3::Z() const + { + return m_data[2]; + } + template + inline void Vec3::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 + inline T Vec3::GetNorm() const + { + return sqrt(m_data[0]*m_data[0]+m_data[1]*m_data[1]+m_data[2]*m_data[2]); + } + template + inline void Vec3::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 + inline void Vec3::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 + inline void Vec3::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 + inline void Vec3::operator-=(T a) + { + this->m_data[0] -= a; + this->m_data[1] -= a; + this->m_data[2] -= a; + } + template + inline void Vec3::operator+=(T a) + { + this->m_data[0] += a; + this->m_data[1] += a; + this->m_data[2] += a; + } + template + inline void Vec3::operator/=(T a) + { + this->m_data[0] /= a; + this->m_data[1] /= a; + this->m_data[2] /= a; + } + template + inline void Vec3::operator*=(T a) + { + this->m_data[0] *= a; + this->m_data[1] *= a; + this->m_data[2] *= a; + } + template + inline Vec3 Vec3::operator^ (const Vec3 & rhs) const + { + return Vec3(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 + inline T Vec3::operator*(const Vec3 & 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 + inline Vec3 Vec3::operator+(const Vec3 & rhs) const + { + return Vec3(m_data[0] + rhs.m_data[0],m_data[1] + rhs.m_data[1],m_data[2] + rhs.m_data[2]); + } + template + inline Vec3 Vec3::operator-(const Vec3 & rhs) const + { + return Vec3(m_data[0] - rhs.m_data[0],m_data[1] - rhs.m_data[1],m_data[2] - rhs.m_data[2]) ; + } + template + inline Vec3 Vec3::operator-() const + { + return Vec3(-m_data[0],-m_data[1],-m_data[2]) ; + } + + template + inline Vec3 Vec3::operator*(T rhs) const + { + return Vec3(rhs * this->m_data[0], rhs * this->m_data[1], rhs * this->m_data[2]); + } + template + inline Vec3 Vec3::operator/ (T rhs) const + { + return Vec3(m_data[0] / rhs, m_data[1] / rhs, m_data[2] / rhs); + } + template + inline Vec3::Vec3(T a) + { + m_data[0] = m_data[1] = m_data[2] = a; + } + template + inline Vec3::Vec3(T x, T y, T z) + { + m_data[0] = x; + m_data[1] = y; + m_data[2] = z; + } + template + inline Vec3::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 + inline Vec3::~Vec3(void){}; + + template + inline Vec3::Vec3() {} + + template + inline const bool Colinear(const Vec3 & a, const Vec3 & b, const Vec3 & 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 + inline const T Volume(const Vec3 & a, const Vec3 & b, const Vec3 & c, const Vec3 & d) + { + return (a-d) * ((b-d) ^ (c-d)); + } +} +#endif //HACD_VECTOR_INL diff --git a/indra/libhacd/hacdVersion.h b/indra/libhacd/hacdVersion.h new file mode 100644 index 000000000..1b7c016b8 --- /dev/null +++ b/indra/libhacd/hacdVersion.h @@ -0,0 +1,20 @@ +/* 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 HACD_VERSION_H +#define HACD_VERSION_H +#define HACD_VERSION_MAJOR 0 +#define HACD_VERSION_MINOR 0 +#endif diff --git a/indra/libndhacd/CMakeLists.txt b/indra/libndhacd/CMakeLists.txt new file mode 100644 index 000000000..93cf90a13 --- /dev/null +++ b/indra/libndhacd/CMakeLists.txt @@ -0,0 +1,9 @@ +project(libndhacd) + +include_directories(${LIBS_OPEN_DIR}/libhacd) + +set (SOURCE_FILES LLConvexDecomposition.cpp nd_hacdConvexDecomposition.cpp nd_hacdStructs.cpp nd_hacdUtils.cpp nd_EnterExitTracer.cpp nd_StructTracer.cpp ) +file(GLOB HEADER_FILES *.h) + +add_library( nd_hacdConvexDecomposition STATIC ${SOURCE_FILES} ${HEADER_FILES}) + diff --git a/indra/libndhacd/LLConvexDecomposition.cpp b/indra/libndhacd/LLConvexDecomposition.cpp new file mode 100644 index 000000000..80ec5f435 --- /dev/null +++ b/indra/libndhacd/LLConvexDecomposition.cpp @@ -0,0 +1,80 @@ +/** + * @file LLConvexDecomposition.cpp + * @author falcon@lindenlab.com + * @brief A stub implementation of LLConvexDecomposition interface + * + * $LicenseInfo:firstyear=2002&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2010, Linden Research, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * $/LicenseInfo$ + */ + +#if defined(_WINDOWS) +# include "windowsincludes.h" +#endif + +#ifndef NULL +#define NULL 0 +#endif + +#include "nd_hacdConvexDecomposition.h" + +#include "LLConvexDecomposition.h" + + +/*static */bool LLConvexDecomposition::s_isInitialized = false; + +/*static*/LLConvexDecomposition* LLConvexDecomposition::getInstance() +{ + if ( !s_isInitialized ) + { + return NULL; + } + else + { + return nd_hacdConvexDecomposition::getInstance(); + } +} + +/*static */LLCDResult LLConvexDecomposition::initSystem() +{ + LLCDResult result = nd_hacdConvexDecomposition::initSystem(); + if ( result == LLCD_OK ) + { + s_isInitialized = true; + } + return result; +} + +/*static */LLCDResult LLConvexDecomposition::initThread() +{ + return nd_hacdConvexDecomposition::initThread(); +} + +/*static */LLCDResult LLConvexDecomposition::quitThread() +{ + return nd_hacdConvexDecomposition::quitThread(); +} + +/*static */LLCDResult LLConvexDecomposition::quitSystem() +{ + return nd_hacdConvexDecomposition::quitSystem(); +} + + diff --git a/indra/libndhacd/LLConvexDecomposition.h b/indra/libndhacd/LLConvexDecomposition.h new file mode 100644 index 000000000..2d7f5aa3a --- /dev/null +++ b/indra/libndhacd/LLConvexDecomposition.h @@ -0,0 +1,231 @@ +/** + * @file LLConvexDecomposition.cpp + * @brief LLConvexDecomposition interface definition + * + * $LicenseInfo:firstyear=2011&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2011, Linden Research, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * $/LicenseInfo$ + */ + +#ifndef LL_CONVEX_DECOMPOSITION +#define LL_CONVEX_DECOMPOSITION + +typedef int bool32; + +#if defined(_WIN32) || defined(_WIN64) +#define LLCD_CALL __cdecl +#else +#define LLCD_CALL +#endif + +struct LLCDParam +{ + enum LLCDParamType + { + LLCD_INVALID = 0, + LLCD_INTEGER, + LLCD_FLOAT, + LLCD_BOOLEAN, + LLCD_ENUM + }; + + struct LLCDEnumItem + { + const char* mName; + int mValue; + }; + + union LLCDValue + { + float mFloat; + int mIntOrEnumValue; + bool32 mBool; + }; + + union LLCDParamDetails + { + struct { + LLCDValue mLow; + LLCDValue mHigh; + LLCDValue mDelta; + } mRange; + + struct { + int mNumEnums; + LLCDEnumItem* mEnumsArray; + } mEnumValues; + }; + + const char* mName; + const char* mDescription; + LLCDParamType mType; + LLCDParamDetails mDetails; + LLCDValue mDefault; + int mStage; + + // WARNING: Only the LLConvexDecomposition implementation + // should change this value + int mReserved; +}; + +struct LLCDStageData +{ + const char* mName; + const char* mDescription; + bool32 mSupportsCallback; +}; + +struct LLCDMeshData +{ + enum IndexType + { + INT_16, + INT_32 + }; + + const float* mVertexBase; + int mVertexStrideBytes; + int mNumVertices; + const void* mIndexBase; + IndexType mIndexType; + int mIndexStrideBytes; + int mNumTriangles; +}; + +struct LLCDHull +{ + const float* mVertexBase; + int mVertexStrideBytes; + int mNumVertices; +}; + +enum LLCDResult +{ + LLCD_OK = 0, + LLCD_UNKOWN_ERROR, + LLCD_NULL_PTR, + LLCD_INVALID_STAGE, + LLCD_UNKNOWN_PARAM, + LLCD_BAD_VALUE, + LLCD_REQUEST_OUT_OF_RANGE, + LLCD_INVALID_MESH_DATA, + LLCD_INVALID_HULL_DATA, + LLCD_STAGE_NOT_READY, + LLCD_INVALID_THREAD, + LLCD_NOT_IMPLEMENTED +}; + +// This callback will receive a string describing the current subtask being performed +// as well as a pair of numbers indicating progress. (The values should not be interpreted +// as a completion percentage as 'current' may be greater than 'final'.) +// If the callback returns zero, the decomposition will be terminated +typedef int (LLCD_CALL *llcdCallbackFunc)(const char* description, int current, int final); + +class LLConvexDecomposition +{ +public: + // Obtain a pointer to the actual implementation + static LLConvexDecomposition* getInstance(); + + static LLCDResult initSystem(); + static LLCDResult initThread(); + static LLCDResult quitThread(); + static LLCDResult quitSystem(); + + // Generate a decomposition object handle + virtual void genDecomposition(int& decomp) = 0; + // Delete decomposition object handle + virtual void deleteDecomposition(int decomp) = 0; + // Bind given decomposition handle + // Commands operate on currently bound decomposition + virtual void bindDecomposition(int decomp) = 0; + + // Sets *paramsOut to the address of the LLCDParam array and returns + // the number of parameters + virtual int getParameters(const LLCDParam** paramsOut) = 0; + + + // Sets *stagesOut to the address of the LLCDStageData array and returns + // the number of stages + virtual int getStages(const LLCDStageData** stagesOut) = 0; + + + // Set a parameter by name. Pass enum values as integers. + virtual LLCDResult setParam(const char* name, float val) = 0; + virtual LLCDResult setParam(const char* name, int val) = 0; + virtual LLCDResult setParam(const char* name, bool val) = 0; + + + // Set incoming mesh data. Data is copied to local buffers and will + // persist until the next setMeshData call + virtual LLCDResult setMeshData( const LLCDMeshData* data, bool vertex_based ) = 0; + + + // Register a callback to be called periodically during the specified stage + // See the typedef above for more information + virtual LLCDResult registerCallback( int stage, llcdCallbackFunc callback ) = 0; + + + // Execute the specified decomposition stage + virtual LLCDResult executeStage(int stage) = 0; + virtual LLCDResult buildSingleHull() = 0 ; + + + // Gets the number of hulls generated by the specified decompositions stage + virtual int getNumHullsFromStage(int stage) = 0; + + + // Populates hullOut to reference the internal copy of the requested hull + // The data will persist only until the next executeStage call for that stage. + virtual LLCDResult getHullFromStage( int stage, int hull, LLCDHull* hullOut ) = 0; + virtual LLCDResult getSingleHull( LLCDHull* hullOut ) = 0 ; + + // TODO: Implement lock of some kind to disallow this call if data not yet ready + // Populates the meshDataOut to reference the utility's copy of the mesh geometry + // for the hull and stage specified. + // You must copy this data if you want to continue using it after the next executeStage + // call + virtual LLCDResult getMeshFromStage( int stage, int hull, LLCDMeshData* meshDataOut) = 0; + + + // Creates a mesh from hullIn and temporarily stores it internally in the utility. + // The mesh data persists only until the next call to getMeshFromHull + virtual LLCDResult getMeshFromHull( LLCDHull* hullIn, LLCDMeshData* meshOut ) = 0; + + // Takes meshIn, generates a single convex hull from it, converts that to a mesh + // stored internally, and populates meshOut to reference the internally stored data. + // The data is persistent only until the next call to generateSingleHullMeshFromMesh + virtual LLCDResult generateSingleHullMeshFromMesh( LLCDMeshData* meshIn, LLCDMeshData* meshOut) = 0; + + // + /// Debug + virtual void loadMeshData(const char* fileIn, LLCDMeshData** meshDataOut) = 0; + + virtual bool isFunctional()=0; + +private: + static bool s_isInitialized; +}; + +// Pull in our trace interface +#include "ndConvexDecomposition.h" + +#endif //LL_CONVEX_DECOMPOSITION + diff --git a/indra/libndhacd/LLConvexDecompositionStubImpl.cpp b/indra/libndhacd/LLConvexDecompositionStubImpl.cpp new file mode 100644 index 000000000..018143de1 --- /dev/null +++ b/indra/libndhacd/LLConvexDecompositionStubImpl.cpp @@ -0,0 +1,148 @@ +/** + * @file LLConvexDecompositionStubImpl.cpp + * @author falcon@lindenlab.com + * @brief A stub implementation of LLConvexDecomposition + * + * $LicenseInfo:firstyear=2002&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2010, Linden Research, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * $/LicenseInfo$ + */ + + +#include +#include +#include "LLConvexDecompositionStubImpl.h" + +LLConvexDecomposition* LLConvexDecompositionImpl::getInstance() +{ + return NULL; +} + +LLCDResult LLConvexDecompositionImpl::initSystem() +{ + return LLCD_NOT_IMPLEMENTED; +} + +LLCDResult LLConvexDecompositionImpl::initThread() +{ + return LLCD_NOT_IMPLEMENTED; +} + +LLCDResult LLConvexDecompositionImpl::quitThread() +{ + return LLCD_NOT_IMPLEMENTED; +} + +LLCDResult LLConvexDecompositionImpl::quitSystem() +{ + return LLCD_NOT_IMPLEMENTED; +} + +void LLConvexDecompositionImpl::genDecomposition(int& decomp) +{ + +} + +void LLConvexDecompositionImpl::deleteDecomposition(int decomp) +{ + +} + +void LLConvexDecompositionImpl::bindDecomposition(int decomp) +{ + +} + +LLCDResult LLConvexDecompositionImpl::setParam(const char* name, float val) +{ + return LLCD_NOT_IMPLEMENTED; +} + +LLCDResult LLConvexDecompositionImpl::setParam(const char* name, bool val) +{ + return LLCD_NOT_IMPLEMENTED; +} + +LLCDResult LLConvexDecompositionImpl::setParam(const char* name, int val) +{ + return LLCD_NOT_IMPLEMENTED; +} + +LLCDResult LLConvexDecompositionImpl::setMeshData( const LLCDMeshData* data, bool vertex_based ) +{ + return LLCD_NOT_IMPLEMENTED; +} + +LLCDResult LLConvexDecompositionImpl::registerCallback(int stage, llcdCallbackFunc callback ) +{ + return LLCD_NOT_IMPLEMENTED; +} + +LLCDResult LLConvexDecompositionImpl::buildSingleHull() +{ + return LLCD_NOT_IMPLEMENTED; +} + +LLCDResult LLConvexDecompositionImpl::executeStage(int stage) +{ + return LLCD_NOT_IMPLEMENTED; +} + +int LLConvexDecompositionImpl::getNumHullsFromStage(int stage) +{ + return 0; +} + +LLCDResult LLConvexDecompositionImpl::getSingleHull( LLCDHull* hullOut ) +{ + memset( hullOut, 0, sizeof(LLCDHull) ); + return LLCD_NOT_IMPLEMENTED; +} + +LLCDResult LLConvexDecompositionImpl::getHullFromStage( int stage, int hull, LLCDHull* hullOut ) +{ + memset( hullOut, 0, sizeof(LLCDHull) ); + return LLCD_NOT_IMPLEMENTED; +} + +LLCDResult LLConvexDecompositionImpl::getMeshFromStage( int stage, int hull, LLCDMeshData* meshDataOut ) +{ + memset( meshDataOut, 0, sizeof(LLCDMeshData) ); + return LLCD_NOT_IMPLEMENTED; +} + +LLCDResult LLConvexDecompositionImpl::getMeshFromHull( LLCDHull* hullIn, LLCDMeshData* meshOut ) +{ + memset( meshOut, 0, sizeof(LLCDMeshData) ); + return LLCD_NOT_IMPLEMENTED; +} + +LLCDResult LLConvexDecompositionImpl::generateSingleHullMeshFromMesh(LLCDMeshData* meshIn, LLCDMeshData* meshOut) +{ + memset( meshOut, 0, sizeof(LLCDMeshData) ); + return LLCD_NOT_IMPLEMENTED; +} + +void LLConvexDecompositionImpl::loadMeshData(const char* fileIn, LLCDMeshData** meshDataOut) +{ + static LLCDMeshData meshData; + memset( &meshData, 0, sizeof(LLCDMeshData) ); + *meshDataOut = &meshData; +} diff --git a/indra/libndhacd/LLConvexDecompositionStubImpl.h b/indra/libndhacd/LLConvexDecompositionStubImpl.h new file mode 100644 index 000000000..9599175ef --- /dev/null +++ b/indra/libndhacd/LLConvexDecompositionStubImpl.h @@ -0,0 +1,97 @@ +/** + * @file LLConvexDecompositionStubImpl.h + * @author falcon@lindenlab.com + * @brief A stub implementation of LLConvexDecomposition + * + * $LicenseInfo:firstyear=2002&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2010, Linden Research, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * $/LicenseInfo$ + */ + + +#ifndef LL_CONVEX_DECOMP_UTIL_H +#define LL_CONVEX_DECOMP_UTIL_H + +#include "LLConvexDecomposition.h" + +class LLConvexDecompositionImpl : public LLConvexDecomposition +{ + public: + + virtual ~LLConvexDecompositionImpl() {} + + static LLConvexDecomposition* getInstance(); + static LLCDResult initSystem(); + static LLCDResult initThread(); + static LLCDResult quitThread(); + static LLCDResult quitSystem(); + + // Generate a decomposition object handle + void genDecomposition(int& decomp); + // Delete decomposition object handle + void deleteDecomposition(int decomp); + // Bind given decomposition handle + // Commands operate on currently bound decomposition + void bindDecomposition(int decomp); + + // Sets *paramsOut to the address of the LLCDParam array and returns + // the length of the array + int getParameters(const LLCDParam** paramsOut) + { + *paramsOut = NULL; + return 0; + } + + int getStages(const LLCDStageData** stagesOut) + { + *stagesOut = NULL; + return 0; + } + + // Set a parameter by name. Returns false if out of bounds or unsupported parameter + LLCDResult setParam(const char* name, float val); + LLCDResult setParam(const char* name, int val); + LLCDResult setParam(const char* name, bool val); + LLCDResult setMeshData( const LLCDMeshData* data, bool vertex_based ); + LLCDResult registerCallback(int stage, llcdCallbackFunc callback ); + + LLCDResult executeStage(int stage); + LLCDResult buildSingleHull() ; + + int getNumHullsFromStage(int stage); + + LLCDResult getHullFromStage( int stage, int hull, LLCDHull* hullOut ); + LLCDResult getSingleHull( LLCDHull* hullOut ) ; + + // TODO: Implement lock of some kind to disallow this call if data not yet ready + LLCDResult getMeshFromStage( int stage, int hull, LLCDMeshData* meshDataOut); + LLCDResult getMeshFromHull( LLCDHull* hullIn, LLCDMeshData* meshOut ); + + // For visualizing convex hull shapes in the viewer physics shape display + LLCDResult generateSingleHullMeshFromMesh( LLCDMeshData* meshIn, LLCDMeshData* meshOut); + + /// Debug + void loadMeshData(const char* fileIn, LLCDMeshData** meshDataOut); + + private: + LLConvexDecompositionImpl() {} +}; + +#endif //LL_CONVEX_DECOMP_UTIL_H diff --git a/indra/libndhacd/ndConvexDecomposition.h b/indra/libndhacd/ndConvexDecomposition.h new file mode 100644 index 000000000..ae8444b04 --- /dev/null +++ b/indra/libndhacd/ndConvexDecomposition.h @@ -0,0 +1,58 @@ +#ifndef ND_CONVEXDECOMPOSITION_H +#define ND_CONVEXDECOMPOSITION_H + +/** + * copyright 2011 sl.nicky.ml@googlemail.com + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef ND_HASCONVEXDECOMP_TRACER + #define ND_HASCONVEXDECOMP_TRACER +#endif + +class ndConvexDecompositionTracer +{ +public: + enum ETraceLevel + { + eNone = 0, + eTraceFunctions = 0x1, + eTraceData = 0x2, + }; + + virtual ~ndConvexDecompositionTracer() + { } + + virtual void trace( char const *a_strMsg ) = 0; + + virtual void startTraceData( char const *a_strWhat) = 0; + virtual void traceData( char const *a_strData ) = 0; + virtual void endTraceData() = 0; + + virtual int getLevel() = 0; + + virtual void addref() = 0; + virtual void release() = 0; +}; + +class ndConvexDecompositionTracable +{ +public: + virtual void setTracer( ndConvexDecompositionTracer *) = 0; +}; + + +#endif diff --git a/indra/libndhacd/nd_EnterExitTracer.cpp b/indra/libndhacd/nd_EnterExitTracer.cpp new file mode 100644 index 000000000..f971f30cb --- /dev/null +++ b/indra/libndhacd/nd_EnterExitTracer.cpp @@ -0,0 +1,51 @@ +/** + * copyright 2011 sl.nicky.ml@googlemail.com + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include "nd_EnterExitTracer.h" + +#include +#include + +void EnterExitTracer::doTrace( char const *aPrefix ) +{ + if( !mFunc || !mTracer ) + return; + + if( ndConvexDecompositionTracer::eTraceFunctions == (mTracer->getLevel() & ndConvexDecompositionTracer::eTraceFunctions ) ) + return; + + std::stringstream str; + if( aPrefix ) + str << aPrefix; + + str << mFunc; + mTracer->trace( str.str().c_str() ); +} + +EnterExitTracer::EnterExitTracer( char const *aFunc, ndConvexDecompositionTracer *aTracer ) + : mFunc( aFunc ) + , mTracer( aTracer ) +{ + doTrace( "enter: " ); +} + +EnterExitTracer::~EnterExitTracer( ) +{ + doTrace( "exit: " ); +} + diff --git a/indra/libndhacd/nd_EnterExitTracer.h b/indra/libndhacd/nd_EnterExitTracer.h new file mode 100644 index 000000000..b4ce31856 --- /dev/null +++ b/indra/libndhacd/nd_EnterExitTracer.h @@ -0,0 +1,43 @@ +#ifndef ND_ENTEREXITTRACER_H +#define ND_ENTEREXITTRACER_H + +/** + * copyright 2011 sl.nicky.ml@googlemail.com + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include "ndConvexDecomposition.h" + +class EnterExitTracer +{ + char const *mFunc; + ndConvexDecompositionTracer *mTracer; + + void doTrace( char const *aPrefix ); + +public: + EnterExitTracer( char const *aFunc, ndConvexDecompositionTracer *aTracer ); + ~EnterExitTracer( ); + +}; + +#ifdef __GNUC__ + #define TRACE_FUNC(ATRACER) EnterExitTracer oETTrace( __PRETTY_FUNCTION__, ATRACER ); +#else + #define TRACE_FUNC(ATRACER) EnterExitTracer oETTrace( __FUNCTION__, ATRACER ); +#endif + +#endif diff --git a/indra/libndhacd/nd_StructTracer.cpp b/indra/libndhacd/nd_StructTracer.cpp new file mode 100644 index 000000000..d1c3697ce --- /dev/null +++ b/indra/libndhacd/nd_StructTracer.cpp @@ -0,0 +1,188 @@ +/** + * copyright 2011 sl.nicky.ml@googlemail.com + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include "nd_StructTracer.h" +#include +#include + +using namespace std; + +namespace ndStructTracer +{ + bool doTrace( ndConvexDecompositionTracer *aTracer ) + { + if( !aTracer ) + return false; + + return ndConvexDecompositionTracer::eTraceData == (aTracer->getLevel() & ndConvexDecompositionTracer::eTraceData) ; + } + + + template void traceVecDbl( T const &aValue, ndConvexDecompositionTracer *aTracer ) + { + stringstream str; + str << fixed + << "\tX: " << aValue.X() + << " Y: " << aValue.Y() + << " Z: " << aValue.Z(); + + aTracer->traceData( str.str().c_str() ); + } + + template< typename T> void traceVecDoubles( vector< T > const &aVertices, ndConvexDecompositionTracer *aTracer ) + { + for( size_t i = 0; i < aVertices.size(); ++i ) + traceVecDbl( aVertices[i], aTracer ); + } + + void traceVertices( vector< tVecDbl > const &aVertices, ndConvexDecompositionTracer *aTracer ) + { + aTracer->traceData( "Triangles\n" ); + traceVecDoubles( aVertices, aTracer ); + } + + void traceTriangles( vector< tVecLong > const &aTriangles, ndConvexDecompositionTracer *aTracer ) + { + aTracer->traceData( "Vertices\n" ); + traceVecDoubles( aTriangles, aTracer ); + } + + void trace( DecompData const &aData, ndConvexDecompositionTracer *aTracer ) + { + if( !doTrace( aTracer ) ) + return; + + aTracer->startTraceData("Hulls"); + + stringstream str; + str << "Hulls: " << aData.mHulls.size(); + aTracer->traceData( str.str().c_str() ); + str.seekp(0); + + for( size_t i = 0; i < aData.mHulls.size(); ++i ) + { + str << "Hull #" << i; + aTracer->traceData( str.str().c_str() ); + str.seekp(0); + + DecompHull const &oHull( aData.mHulls[i] ); + traceVertices( oHull.mVertices, aTracer ); + traceTriangles( oHull.mTriangles, aTracer ); + } + + aTracer->endTraceData(); + } + + template< typename T> void traceVertex( T aX, T aY, T aZ, ndConvexDecompositionTracer *aTracer ) + { + stringstream str; + str << fixed + << "X: " << aX + << "Y: " << aY + << "Z: " << aZ; + + aTracer->traceData( str.str().c_str() ); + } + + template< typename T> void traceTriangle( void *& aPtr, int aStride, ndConvexDecompositionTracer *aTracer ) + { + T *pData = reinterpret_cast< T* >(aPtr); + long X( pData[0] ), Y( pData[1] ), Z( pData[2] ); + + traceVertex( X, Y, Z, aTracer ); + + pData += aStride; + aPtr = pData; + } + + void traceVertices( int aNumVertices, float const *aVertices, int aStride, ndConvexDecompositionTracer *aTracer ) + { + for( int i = 0; i < aNumVertices; ++i ) + { + float X( aVertices[0] ), Y( aVertices[1] ), Z( aVertices[2] ); + traceVertex( X, Y, Z, aTracer ); + aVertices += aStride; + } + } + + void traceTriangles( LLCDMeshData const *aData, ndConvexDecompositionTracer *aTracer ) + { + int nCount = aData->mNumTriangles; + int nStride = aData->mIndexStrideBytes; + void *pData = const_cast( aData->mIndexBase ); + int nIndexType = aData->mIndexType; + + if ( LLCDMeshData::INT_16 == nIndexType ) + nStride /= 2; + else + nStride /= 4; + + for ( int i = 0; i < nCount; ++i ) + { + if( LLCDMeshData::INT_16 == nIndexType ) + traceTriangle< hacdUINT16 >( pData, nStride, aTracer ); + else + traceTriangle< hacdUINT32 >( pData, nStride, aTracer ); + } + } + + void trace( LLCDMeshData const *aData, bool aVertexBased, ndConvexDecompositionTracer *aTracer ) + { + if( !doTrace( aTracer ) ) + return; + + aTracer->startTraceData("LLCDMeshData"); + + string strVBased("true"); + if( !aVertexBased ) + strVBased = "false"; + + stringstream str; + str << "LLCDMeshData vertex based: " << strVBased + << "# vertices " << aData->mNumVertices + << "# triangles " << aData->mNumTriangles; + + aTracer->traceData( str.str().c_str() ); + + traceVertices( aData->mNumVertices, aData->mVertexBase, aData->mVertexStrideBytes/sizeof(float), aTracer ); + + if( !aVertexBased ) + traceTriangles( aData, aTracer ); + + aTracer->endTraceData(); + } + + void trace( LLCDHull const *aData, ndConvexDecompositionTracer *aTracer ) + { + if( !doTrace( aTracer ) ) + return; + + aTracer->startTraceData("LLCDHull"); + + stringstream str; + str << "LLCDHull # vertices " << aData->mNumVertices; + + aTracer->traceData( str.str().c_str() ); + + traceVertices( aData->mNumVertices, aData->mVertexBase, aData->mVertexStrideBytes/sizeof(float), aTracer ); + + + aTracer->endTraceData(); + } +}; + diff --git a/indra/libndhacd/nd_StructTracer.h b/indra/libndhacd/nd_StructTracer.h new file mode 100644 index 000000000..6dbe3ce3e --- /dev/null +++ b/indra/libndhacd/nd_StructTracer.h @@ -0,0 +1,34 @@ +#ifndef ND_STRUCTTRACER_H +#define ND_STRUCTTRACER_H + +/** + * copyright 2011 sl.nicky.ml@googlemail.com + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + + +#include "ndConvexDecomposition.h" +#include "LLConvexDecomposition.h" +#include "nd_hacdStructs.h" + +namespace ndStructTracer +{ + void trace( DecompData const &aData, ndConvexDecompositionTracer *aTracer ); + void trace( LLCDMeshData const *aData, bool aVertexBased, ndConvexDecompositionTracer *aTracer ); + void trace( LLCDHull const *aData, ndConvexDecompositionTracer *aTracer ); +}; + +#endif diff --git a/indra/libndhacd/nd_hacdConvexDecomposition.cpp b/indra/libndhacd/nd_hacdConvexDecomposition.cpp new file mode 100644 index 000000000..e85775072 --- /dev/null +++ b/indra/libndhacd/nd_hacdConvexDecomposition.cpp @@ -0,0 +1,393 @@ +/** + * copyright 2011 sl.nicky.ml@googlemail.com + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include +#include +#include "nd_hacdConvexDecomposition.h" + +#include "nd_hacdDefines.h" +#include "nd_hacdStructs.h" +#include "nd_hacdUtils.h" +#include "ndConvexDecomposition.h" +#include "nd_EnterExitTracer.h" +#include "nd_StructTracer.h" + +LLCDStageData nd_hacdConvexDecomposition::mStages[1]; + +LLCDParam nd_hacdConvexDecomposition::mParams[4]; +LLCDParam::LLCDEnumItem nd_hacdConvexDecomposition::mMethods[1]; +LLCDParam::LLCDEnumItem nd_hacdConvexDecomposition::mQuality[1]; +LLCDParam::LLCDEnumItem nd_hacdConvexDecomposition::mSimplify[1]; + +LLConvexDecomposition* nd_hacdConvexDecomposition::getInstance() +{ + static nd_hacdConvexDecomposition sImpl; + return &sImpl; +} + +nd_hacdConvexDecomposition::nd_hacdConvexDecomposition() +{ + mNextId = 0; + mCurrentDecoder = 0; + mSingleHullMeshFromMesh = new HACDDecoder(); + mTracer = 0; +} + +nd_hacdConvexDecomposition::~nd_hacdConvexDecomposition() +{ + if( mTracer ) + mTracer->release(); +} + +LLCDResult nd_hacdConvexDecomposition::initSystem() +{ + memset( &mStages[0], 0, sizeof( mStages ) ); + memset( &mParams[0], 0, sizeof( mParams ) ); + memset( &mMethods[0], 0, sizeof( mMethods ) ); + memset( &mQuality[0], 0, sizeof( mQuality ) ); + memset( &mSimplify[0], 0, sizeof( mSimplify ) ); + + mStages[0].mName = "Decompose"; + mStages[0].mDescription = NULL; + + mMethods[0].mName = "Default"; + mMethods[0].mValue = 0; + + mQuality[0].mName = "Default"; + mQuality[0].mValue = 0; + + mSimplify[0].mName = "None"; + mSimplify[0].mValue = 0; + + mParams[0].mName = "nd_AlwaysNeedTriangles"; + mParams[0].mDescription = 0; + mParams[0].mType = LLCDParam::LLCD_BOOLEAN; + mParams[0].mDefault.mBool = true; + + mParams[1].mName = "Method"; + mParams[1].mType = LLCDParam::LLCD_ENUM; + mParams[1].mDetails.mEnumValues.mNumEnums = sizeof(mMethods)/sizeof(LLCDParam::LLCDEnumItem); + mParams[1].mDetails.mEnumValues.mEnumsArray = mMethods; + mParams[1].mDefault.mIntOrEnumValue = 0; + + mParams[2].mName = "Decompose Quality"; + mParams[2].mType = LLCDParam::LLCD_ENUM; + mParams[2].mDetails.mEnumValues.mNumEnums = sizeof(mQuality)/sizeof(LLCDParam::LLCDEnumItem); + mParams[2].mDetails.mEnumValues.mEnumsArray = mQuality; + mParams[2].mDefault.mIntOrEnumValue = 0; + + mParams[3].mName = "Simplify Method"; + mParams[3].mType = LLCDParam::LLCD_ENUM; + mParams[3].mDetails.mEnumValues.mNumEnums = sizeof(mSimplify)/sizeof(LLCDParam::LLCDEnumItem); + mParams[3].mDetails.mEnumValues.mEnumsArray = mSimplify; + mParams[3].mDefault.mIntOrEnumValue = 0; + + return LLCD_OK; +} + +LLCDResult nd_hacdConvexDecomposition::initThread() +{ + return LLCD_OK; +} + +LLCDResult nd_hacdConvexDecomposition::quitThread() +{ + return LLCD_OK; +} + +LLCDResult nd_hacdConvexDecomposition::quitSystem() +{ + return LLCD_OK; +} + +void nd_hacdConvexDecomposition::genDecomposition( int& decomp ) +{ + HACDDecoder *pGen = new HACDDecoder(); + decomp = mNextId; + ++mNextId; + + mDecoders[ decomp ] = pGen; +} + +void nd_hacdConvexDecomposition::deleteDecomposition( int decomp ) +{ + delete mDecoders[ decomp ]; + mDecoders.erase( decomp ); +} + +void nd_hacdConvexDecomposition::bindDecomposition( int decomp ) +{ + TRACE_FUNC( mTracer ); + mCurrentDecoder = decomp; +} + +LLCDResult nd_hacdConvexDecomposition::setParam( const char* name, float val ) +{ + TRACE_FUNC( mTracer ); + return LLCD_OK; +} + +LLCDResult nd_hacdConvexDecomposition::setParam( const char* name, bool val ) +{ + TRACE_FUNC( mTracer ); + return LLCD_OK; +} + +LLCDResult nd_hacdConvexDecomposition::setParam( const char* name, int val ) +{ + TRACE_FUNC( mTracer ); + return LLCD_OK; +} + +LLCDResult nd_hacdConvexDecomposition::setMeshData( const LLCDMeshData* data, bool vertex_based ) +{ + TRACE_FUNC( mTracer ); + ndStructTracer::trace( data, vertex_based, mTracer ); + + HACDDecoder *pC = mDecoders[ mCurrentDecoder ]; + return ::setMeshData( data, vertex_based, pC ); +} + +LLCDResult nd_hacdConvexDecomposition::registerCallback( int stage, llcdCallbackFunc callback ) +{ + TRACE_FUNC( mTracer ); + if( mDecoders.end() == mDecoders.find( mCurrentDecoder ) ) + return LLCD_STAGE_NOT_READY; + + HACDDecoder *pC = mDecoders[ mCurrentDecoder ]; + pC->mCallback = callback; + + return LLCD_OK; +} + +LLCDResult nd_hacdConvexDecomposition::buildSingleHull() +{ + TRACE_FUNC( mTracer ); + return LLCD_OK; +} + +LLCDResult nd_hacdConvexDecomposition::executeStage( int stage ) +{ + TRACE_FUNC( mTracer ); + if ( stage < 0 || stage >= NUM_STAGES ) + return LLCD_INVALID_STAGE; + + HACDDecoder *pC = mDecoders[ mCurrentDecoder ]; + tHACD *pHACD = init( 1, MIN_NUMBER_OF_CLUSTERS, MAX_VERTICES_PER_HULL, CONNECT_DISTS[0], pC ); + + DecompData oRes = decompose( pHACD ); + ndStructTracer::trace( oRes, mTracer ); + + delete pHACD; + + pC->mStages[ stage ] = oRes; + + return LLCD_OK; +} + +int nd_hacdConvexDecomposition::getNumHullsFromStage( int stage ) +{ + TRACE_FUNC( mTracer ); + HACDDecoder *pC = mDecoders[ mCurrentDecoder ]; + + if ( !pC ) + return 0; + + if ( stage < 0 || static_cast(stage) >= pC->mStages.size() ) + return 0; + + return pC->mStages[stage].mHulls.size(); +} + +DecompData toSingleHull( HACDDecoder *aDecoder, LLCDResult &aRes, ndConvexDecompositionTracer *aTracer ) +{ + TRACE_FUNC( aTracer ); + aRes = LLCD_REQUEST_OUT_OF_RANGE; + + for ( int i = 0; i < TO_SINGLE_HULL_TRIES; ++i ) + { + tHACD *pHACD = init( CONCAVITY_FOR_SINGLE_HULL[i], 1, MAX_VERTICES_PER_HULL, CONNECT_DISTS[i], aDecoder ); + + DecompData oRes = decompose( pHACD ); + delete pHACD; + + ndStructTracer::trace( oRes, aTracer ); + + if ( oRes.mHulls.size() == 1 ) + { + aRes = LLCD_OK; + return oRes; + } + } + + return DecompData(); +} + +LLCDResult nd_hacdConvexDecomposition::getSingleHull( LLCDHull* hullOut ) +{ + TRACE_FUNC( mTracer ); + HACDDecoder *pC = mDecoders[ mCurrentDecoder ]; + + memset( hullOut, 0, sizeof( LLCDHull ) ); + + LLCDResult res; + + // Will already trace oRes + DecompData oRes = ::toSingleHull( pC, res, mTracer ); + + if ( LLCD_OK != res || oRes.mHulls.size() != 1 ) + return res; + + pC->mSingleHull = oRes.mHulls[0]; + pC->mSingleHull.toLLHull( hullOut ); + + return LLCD_OK; +} + +LLCDResult nd_hacdConvexDecomposition::getHullFromStage( int stage, int hull, LLCDHull* hullOut ) +{ + TRACE_FUNC( mTracer ); + HACDDecoder *pC = mDecoders[ mCurrentDecoder ]; + + memset( hullOut, 0, sizeof( LLCDHull ) ); + + if ( stage < 0 || static_cast(stage) >= pC->mStages.size() ) + return LLCD_INVALID_STAGE; + + DecompData &oData = pC->mStages[ stage ]; + + if ( hull < 0 ||static_cast(hull) >= oData.mHulls.size() ) + return LLCD_REQUEST_OUT_OF_RANGE; + + oData.mHulls[ hull ].toLLHull( hullOut ); + ndStructTracer::trace( hullOut, mTracer ); + + return LLCD_OK; +} + +LLCDResult nd_hacdConvexDecomposition::getMeshFromStage( int stage, int hull, LLCDMeshData* meshDataOut ) +{ + TRACE_FUNC( mTracer ); + HACDDecoder *pC = mDecoders[ mCurrentDecoder ]; + + memset( meshDataOut, 0, sizeof( LLCDHull ) ); + + if ( stage < 0 || static_cast(stage) >= pC->mStages.size() ) + return LLCD_INVALID_STAGE; + + DecompData &oData = pC->mStages[ stage ]; + + if ( hull < 0 || static_cast(hull) >= oData.mHulls.size() ) + return LLCD_REQUEST_OUT_OF_RANGE; + + oData.mHulls[ hull ].toLLMesh( meshDataOut ); + ndStructTracer::trace( meshDataOut, true, mTracer ); + return LLCD_OK; +} + +LLCDResult nd_hacdConvexDecomposition::getMeshFromHull( LLCDHull* hullIn, LLCDMeshData* meshOut ) +{ + TRACE_FUNC( mTracer ); + memset( meshOut, 0, sizeof( LLCDMeshData ) ); + + mMeshToHullVertices.clear(); + mMeshToHullTriangles.clear(); + + if( !hullIn || !hullIn->mVertexBase || !meshOut ) + return LLCD_NULL_PTR; + if( hullIn->mVertexStrideBytes < 3*sizeof(float) || hullIn->mNumVertices < 3 ) + return LLCD_INVALID_HULL_DATA; + + LLCDResult oRet = convertHullToMesh( hullIn, mMeshToHullVertices, mMeshToHullTriangles ); + + if( LLCD_OK != oRet ) + return oRet; + + meshOut->mVertexStrideBytes = sizeof( float )*3; + meshOut->mNumVertices = mMeshToHullVertices.size()/3; + meshOut->mVertexBase = &mMeshToHullVertices[0]; + + meshOut->mIndexType = LLCDMeshData::INT_32; + meshOut->mIndexStrideBytes = sizeof( hacdUINT32 ) * 3; + meshOut->mNumTriangles = mMeshToHullTriangles.size()/3; + meshOut->mIndexBase = &mMeshToHullTriangles[0]; + + return LLCD_OK; +} + +LLCDResult nd_hacdConvexDecomposition::generateSingleHullMeshFromMesh( LLCDMeshData* meshIn, LLCDMeshData* meshOut ) +{ + TRACE_FUNC( mTracer ); + memset( meshOut, 0, sizeof( LLCDMeshData ) ); + mSingleHullMeshFromMesh->clear(); + LLCDResult res = ::setMeshData( meshIn, meshIn->mNumVertices > 3, mSingleHullMeshFromMesh ); + + if ( LLCD_OK != res ) + return res; + + // Will already trace oRes + DecompData oRes = ::toSingleHull( mSingleHullMeshFromMesh, res, mTracer ); + + if ( LLCD_OK != res || oRes.mHulls.size() != 1 ) + return res; + + mSingleHullMeshFromMesh->mSingleHull = oRes.mHulls[0]; + mSingleHullMeshFromMesh->mSingleHull.toLLMesh( meshOut ); + + return LLCD_OK; +} + +void nd_hacdConvexDecomposition::loadMeshData( const char* fileIn, LLCDMeshData** meshDataOut ) +{ + TRACE_FUNC( mTracer ); + static LLCDMeshData meshData; + memset( &meshData, 0, sizeof( LLCDMeshData ) ); + *meshDataOut = &meshData; +} + +int nd_hacdConvexDecomposition::getParameters( const LLCDParam** paramsOut ) +{ + TRACE_FUNC( mTracer ); + *paramsOut = mParams; + return sizeof(mParams)/sizeof(LLCDParam); +} + +int nd_hacdConvexDecomposition::getStages( const LLCDStageData** stagesOut ) +{ + TRACE_FUNC( mTracer ); + + *stagesOut = mStages; + return sizeof(mStages)/sizeof(LLCDStageData); +} + +void nd_hacdConvexDecomposition::setTracer( ndConvexDecompositionTracer * aTracer) +{ + if( mTracer ) + mTracer->release(); + + mTracer = aTracer; + + if( mTracer ) + mTracer->addref(); +} + + +bool nd_hacdConvexDecomposition::isFunctional() +{ + return true; +} diff --git a/indra/libndhacd/nd_hacdConvexDecomposition.h b/indra/libndhacd/nd_hacdConvexDecomposition.h new file mode 100644 index 000000000..e31779c65 --- /dev/null +++ b/indra/libndhacd/nd_hacdConvexDecomposition.h @@ -0,0 +1,103 @@ +/** + * copyright 2011 sl.nicky.ml@googlemail.com + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef ND_HACD_CONVEXDECOMP_H +#define ND_HACD_CONVEXDECOMP_H + +#include "LLConvexDecomposition.h" + +#include +#include + +struct HACDDecoder; + +class nd_hacdConvexDecomposition : public LLConvexDecomposition, public ndConvexDecompositionTracable +{ + int mNextId; + int mCurrentDecoder; + std::map< int, HACDDecoder * > mDecoders; + HACDDecoder *mSingleHullMeshFromMesh; + + std::vector< float > mMeshToHullVertices; + std::vector< int > mMeshToHullTriangles; + + ndConvexDecompositionTracer *mTracer; + + static LLCDStageData mStages[1]; + + static LLCDParam mParams[4]; + static LLCDParam::LLCDEnumItem mMethods[1]; + static LLCDParam::LLCDEnumItem mQuality[1]; + static LLCDParam::LLCDEnumItem mSimplify[1]; + +public: + virtual ~nd_hacdConvexDecomposition(); + + static LLConvexDecomposition* getInstance(); + static LLCDResult initSystem(); + static LLCDResult initThread(); + static LLCDResult quitThread(); + static LLCDResult quitSystem(); + + // Generate a decomposition object handle + void genDecomposition( int& decomp ); + // Delete decomposition object handle + void deleteDecomposition( int decomp ); + // Bind given decomposition handle + // Commands operate on currently bound decomposition + void bindDecomposition( int decomp ); + + // Sets *paramsOut to the address of the LLCDParam array and returns + // the length of the array + int getParameters( const LLCDParam** paramsOut ); + int getStages( const LLCDStageData** stagesOut ) ; + + // Set a parameter by name. Returns false if out of bounds or unsupported parameter + LLCDResult setParam( const char* name, float val ); + LLCDResult setParam( const char* name, int val ); + LLCDResult setParam( const char* name, bool val ); + LLCDResult setMeshData( const LLCDMeshData* data, bool vertex_based ); + LLCDResult registerCallback( int stage, llcdCallbackFunc callback ); + + LLCDResult executeStage( int stage ); + LLCDResult buildSingleHull() ; + + int getNumHullsFromStage( int stage ); + + LLCDResult getHullFromStage( int stage, int hull, LLCDHull* hullOut ); + LLCDResult getSingleHull( LLCDHull* hullOut ) ; + + // TODO: Implement lock of some kind to disallow this call if data not yet ready + LLCDResult getMeshFromStage( int stage, int hull, LLCDMeshData* meshDataOut ); + LLCDResult getMeshFromHull( LLCDHull* hullIn, LLCDMeshData* meshOut ); + + // For visualizing convex hull shapes in the viewer physics shape display + LLCDResult generateSingleHullMeshFromMesh( LLCDMeshData* meshIn, LLCDMeshData* meshOut ); + + /// Debug + void loadMeshData( const char* fileIn, LLCDMeshData** meshDataOut ); + + virtual void setTracer( ndConvexDecompositionTracer *); + + virtual bool isFunctional(); + +private: + nd_hacdConvexDecomposition(); +}; + +#endif diff --git a/indra/libndhacd/nd_hacdDefines.h b/indra/libndhacd/nd_hacdDefines.h new file mode 100644 index 000000000..e612c89ed --- /dev/null +++ b/indra/libndhacd/nd_hacdDefines.h @@ -0,0 +1,46 @@ +/** + * copyright 2011 sl.nicky.ml@googlemail.com + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef ND_HACD_DEFINES_H +#define ND_HACD_DEFINES_H + +#include "hacdHACD.h" + +#define NUM_STAGES 1 + +typedef unsigned short hacdUINT16; +typedef unsigned int hacdUINT32; + +typedef HACD::HACD tHACD; +typedef HACD::Vec3< double > tVecDbl; +typedef HACD::Vec3< long > tVecLong; + +typedef tVecLong ( *fFromIXX )( void const *&, int ); + +const int MAX_VERTICES_PER_HULL = 256; // see http://wiki.secondlife.com/wiki/Mesh/Mesh_physics +const int MIN_NUMBER_OF_CLUSTERS = 1; + +int const TO_SINGLE_HULL_TRIES = 10; + +// Use a high value so HACD will generate just one hull. For now we use the same concavity for each run. +int const CONCAVITY_FOR_SINGLE_HULL[ TO_SINGLE_HULL_TRIES ] = { 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000 }; + +// Max distance to connect CC. Increase this each run. +double const CONNECT_DISTS[ TO_SINGLE_HULL_TRIES ] = { 30, 60, 120, 240, 480, 960, 1920, 3840, 7680, 10000 }; + +#endif diff --git a/indra/libndhacd/nd_hacdStructs.cpp b/indra/libndhacd/nd_hacdStructs.cpp new file mode 100644 index 000000000..4a5c9e6fb --- /dev/null +++ b/indra/libndhacd/nd_hacdStructs.cpp @@ -0,0 +1,107 @@ +/** + * copyright 2011 sl.nicky.ml@googlemail.com + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include "nd_hacdStructs.h" +#include "LLConvexDecomposition.h" + +void DecompHull::clear() +{ + mVertices.clear(); + mTriangles.clear(); + mLLVertices.clear(); + mLLTriangles.clear(); +} + +void DecompHull::computeLLVertices() +{ + if ( mLLVertices.size()*3 != mVertices.size() ) + { + for ( size_t i = 0; i < mVertices.size(); ++i ) + { + mLLVertices.push_back( static_cast( mVertices[i].X() ) ); + mLLVertices.push_back( static_cast( mVertices[i].Y() ) ); + mLLVertices.push_back( static_cast( mVertices[i].Z() ) ); + } + } +} + +void DecompHull::computeLLTriangles() +{ + if ( mLLTriangles.size()*3 != mTriangles.size() ) + { + for ( size_t i = 0; i < mTriangles.size(); ++i ) + { + mLLTriangles.push_back( mTriangles[i].X() ); + mLLTriangles.push_back( mTriangles[i].Y() ); + mLLTriangles.push_back( mTriangles[i].Z() ); + } + } + +} + +void DecompHull::toLLHull( LLCDHull *aHull ) +{ + computeLLVertices(); + + aHull->mVertexBase = &mLLVertices[0]; + aHull->mVertexStrideBytes = sizeof( float ) * 3; + aHull->mNumVertices = mVertices.size(); +} + +void DecompHull::toLLMesh( LLCDMeshData *aMesh ) +{ + computeLLVertices(); + computeLLTriangles(); + + aMesh->mIndexType = LLCDMeshData::INT_32; + aMesh->mVertexBase = &mLLVertices[0]; + aMesh->mNumVertices = mVertices.size(); + aMesh->mVertexStrideBytes = sizeof( float ) * 3; + + aMesh->mIndexBase = &mLLTriangles[0]; + aMesh->mIndexStrideBytes = sizeof( hacdUINT32 ) * 3; + aMesh->mNumTriangles = mTriangles.size(); +} + +void DecompData::clear() +{ + mHulls.clear(); +} + +HACDDecoder::HACDDecoder() +{ + mStages.resize( NUM_STAGES ); + mCallback = 0; +} + +void HACDDecoder::clear() +{ + mVertices.clear(); + mTriangles.clear(); + + for ( size_t i = 0; i < mStages.size(); ++i ) + mStages[i].clear(); + + mSingleHull.clear(); + + mCallback = 0; +} + + + + diff --git a/indra/libndhacd/nd_hacdStructs.h b/indra/libndhacd/nd_hacdStructs.h new file mode 100644 index 000000000..5097b7428 --- /dev/null +++ b/indra/libndhacd/nd_hacdStructs.h @@ -0,0 +1,77 @@ +/** + * copyright 2011 sl.nicky.ml@googlemail.com + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef ND_HACD_DECOMPSTRUCTS_H +#define ND_HACD_DECOMPSTRUCTS_H + +#include "nd_hacdDefines.h" +#include "hacdHACD.h" +#include "LLConvexDecomposition.h" +#include + +struct LLCDHull; +struct LLCDMeshData; + +struct DecompHull +{ + std::vector< tVecDbl > mVertices; + std::vector< tVecLong > mTriangles; + + std::vector< float > mLLVertices; + std::vector< hacdUINT32 > mLLTriangles; + + void clear(); + + void computeLLVertices(); + + void computeLLTriangles(); + + void toLLHull( LLCDHull *aHull ); + void toLLMesh( LLCDMeshData *aMesh ); +}; + +struct DecompData +{ + std::vector< DecompHull > mHulls; + + void clear(); +}; + +struct HACDDecoder: public HACD::ICallback +{ + std::vector< tVecDbl > mVertices; + std::vector< tVecLong > mTriangles; + + std::vector< DecompData > mStages; + DecompHull mSingleHull; + + llcdCallbackFunc mCallback; + + HACDDecoder(); + void clear(); + + virtual void operator()( char const *aMsg, double aProgress, double aConcavity, size_t aVertices) + { + if( mCallback ) + (*mCallback)(aMsg, static_cast(aProgress), aVertices ); + } + +}; + + +#endif diff --git a/indra/libndhacd/nd_hacdUtils.cpp b/indra/libndhacd/nd_hacdUtils.cpp new file mode 100644 index 000000000..8735f3f9a --- /dev/null +++ b/indra/libndhacd/nd_hacdUtils.cpp @@ -0,0 +1,183 @@ +/** + * copyright 2011 sl.nicky.ml@googlemail.com + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include "nd_hacdUtils.h" + +tHACD* init( int nConcavity, int nClusters, int nMaxVerticesPerHull, double dMaxConnectDist, HACDDecoder *aData ) +{ + tHACD *pDec = HACD::CreateHACD(0); + pDec->SetPoints( &aData->mVertices[0] ); + pDec->SetNPoints( aData->mVertices.size() ); + + if ( aData->mTriangles.size() ) + { + pDec->SetTriangles( &aData->mTriangles[0] ); + pDec->SetNTriangles( aData->mTriangles.size() ); + } + + pDec->SetCompacityWeight( 0.1f ); + pDec->SetVolumeWeight( 0 ); + pDec->SetNClusters( nClusters ); + pDec->SetAddExtraDistPoints( true ); + pDec->SetAddFacesPoints( true ); + pDec->SetNVerticesPerCH( nMaxVerticesPerHull ); + pDec->SetConcavity( nConcavity ); + pDec->SetConnectDist( dMaxConnectDist ); + + pDec->SetCallBack( aData ); + + return pDec; +} + +DecompData decompose( tHACD *aHACD ) +{ + aHACD->Compute(); + + DecompData oRet; + + int nClusters = aHACD->GetNClusters(); + + for ( int i = 0; i < nClusters; ++i ) + { + int nVertices = aHACD->GetNPointsCH( i ); + int nTriangles = aHACD->GetNTrianglesCH( i ); + DecompHull oHull; + + oHull.mVertices.resize( nVertices ); + oHull.mTriangles.resize( nTriangles ); + aHACD->GetCH( i, &oHull.mVertices[0], &oHull.mTriangles[0] ); + + oRet.mHulls.push_back( oHull ); + } + + return oRet; +} + +tVecLong fromI16( void const *& pPtr, int aStride ) +{ + hacdUINT16 const *pVal = reinterpret_cast< hacdUINT16 const * >( pPtr ); + tVecLong oRet( pVal[0], pVal[1], pVal[2] ); + pVal += aStride / 2; + pPtr = pVal; + return oRet; +} + +tVecLong fromI32( void const *& pPtr, int aStride ) +{ + hacdUINT32 const *pVal = reinterpret_cast< hacdUINT32 const * >( pPtr ); + tVecLong oRet( pVal[0], pVal[1], pVal[2] ); + pVal += aStride / 4; + pPtr = pVal; + return oRet; +} + +LLCDResult setMeshData( const LLCDMeshData* data, bool vertex_based, HACDDecoder *aDec ) +{ + if ( !data || !data->mVertexBase || ( data->mNumVertices < 3 ) || ( data->mVertexStrideBytes != 12 && data->mVertexStrideBytes != 16 ) ) + return LLCD_INVALID_MESH_DATA; + + if ( !vertex_based && ( ( data->mNumTriangles < 1 ) || ! data->mIndexBase ) ) + return LLCD_INVALID_MESH_DATA; + + aDec->clear(); + int nCount = data->mNumVertices; + float const* pVertex = data->mVertexBase; + int nStride = data->mVertexStrideBytes / sizeof( float ); + + for ( int i = 0; i < nCount; ++i ) + { + tVecDbl oPt( pVertex[0], pVertex[1], pVertex[2] ); + aDec->mVertices.push_back( oPt ); + pVertex += nStride; + } + + if ( !vertex_based ) + { + fFromIXX pFunc = 0; + nCount = data->mNumTriangles; + nStride = data->mIndexStrideBytes; + void const *pData = data->mIndexBase; + + if ( data->mIndexType == LLCDMeshData::INT_16 ) + pFunc = fromI16; + else + pFunc = fromI32; + + for ( int i = 0; i < nCount; ++i ) + { + tVecLong oVal( ( *pFunc )( pData, nStride ) ); + aDec->mTriangles.push_back( oVal ); + } + } + + return LLCD_OK; +} + +LLCDResult convertHullToMesh( const LLCDHull* aHull, std::vector< float > &aVerticesOut, std::vector< int > &aTrianglesOut ) +{ + if( !aHull || !aHull->mVertexBase ) + return LLCD_NULL_PTR; + if( aHull->mVertexStrideBytes < 3*sizeof(float) || aHull->mNumVertices < 3 ) + return LLCD_INVALID_HULL_DATA; + + HACD::ICHull oHull; + + int nCount = aHull->mNumVertices; + float const *pVertex = aHull->mVertexBase; + int nStride = aHull->mVertexStrideBytes / sizeof( float ); + + std::vector< tVecDbl > vcPoints; + + for ( int i = 0; i < nCount; ++i ) + { + vcPoints.push_back( tVecDbl( pVertex[0], pVertex[1], pVertex[2] ) ); + pVertex += nStride; + } + + oHull.AddPoints( &vcPoints[0], vcPoints.size() ); + + HACD::ICHullError eErr = oHull.Process( MAX_VERTICES_PER_HULL ); + if( HACD::ICHullErrorOK != eErr ) + return LLCD_INVALID_HULL_DATA; + + HACD::TMMesh &oMesh = oHull.GetMesh(); + + std::vector< tVecDbl > vPoints; + std::vector< tVecLong > vTriangles; + + vPoints.resize( oMesh.GetNVertices() ); + vTriangles.resize( oMesh.GetNTriangles() ); + + oMesh.GetIFS( &vPoints[0], &vTriangles[0] ); + + for( size_t i = 0; i < vPoints.size(); ++i ) + { + aVerticesOut.push_back( static_cast( vPoints[i].X() ) ); + aVerticesOut.push_back( static_cast( vPoints[i].Y() ) ); + aVerticesOut.push_back( static_cast( vPoints[i].Z() ) ); + } + + for( size_t i = 0; i < vTriangles.size(); ++i ) + { + aTrianglesOut.push_back( vTriangles[i].X() ); + aTrianglesOut.push_back( vTriangles[i].Y() ); + aTrianglesOut.push_back( vTriangles[i].Z() ); + } + + return LLCD_OK; +} diff --git a/indra/libndhacd/nd_hacdUtils.h b/indra/libndhacd/nd_hacdUtils.h new file mode 100644 index 000000000..e2d80e78d --- /dev/null +++ b/indra/libndhacd/nd_hacdUtils.h @@ -0,0 +1,34 @@ +/** + * copyright 2011 sl.nicky.ml@googlemail.com + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef ND_HACD_UTILS_H +#define ND_HACD_UTILS_H + +#include "nd_hacdStructs.h" +#include "LLConvexDecomposition.h" + +tHACD* init( int nConcavity, int nClusters, int nMaxVerticesPerHull, double dMaxConnectDist, HACDDecoder *aData ); +DecompData decompose( tHACD *aHACD ); + +tVecLong fromI16( void *& pPtr, int aStride ); +tVecLong fromI32( void *& pPtr, int aStride ); + +LLCDResult setMeshData( const LLCDMeshData* data, bool vertex_based, HACDDecoder *aDec ); +LLCDResult convertHullToMesh( const LLCDHull* aHull, std::vector< float > &aVerticesOut, std::vector< int > &aTrianglesOut ); + +#endif diff --git a/indra/libndhacd/windowsincludes.h b/indra/libndhacd/windowsincludes.h new file mode 100644 index 000000000..901bc768a --- /dev/null +++ b/indra/libndhacd/windowsincludes.h @@ -0,0 +1,59 @@ +/** + * @file windowsincludes.h + * @brief includes that need to be involved in the windows version of llconvexdecompositionstub + * + * $LicenseInfo:firstyear=2002&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2010, Linden Research, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * $/LicenseInfo$ + */ + + +// stdafx.h : include file for standard system include files, +// or project specific include files that are used frequently, but +// are changed infrequently +// + +#pragma once + +// Modify the following defines if you have to target a platform prior to the ones specified below. +// Refer to MSDN for the latest info on corresponding values for different platforms. +#ifndef WINVER // Allow use of features specific to Windows XP or later. +#define WINVER 0x0501 // Change this to the appropriate value to target other versions of Windows. +#endif + +#ifndef _WIN32_WINNT // Allow use of features specific to Windows XP or later. +#define _WIN32_WINNT 0x0501 // Change this to the appropriate value to target other versions of Windows. +#endif + +#ifndef _WIN32_WINDOWS // Allow use of features specific to Windows 98 or later. +#define _WIN32_WINDOWS 0x0410 // Change this to the appropriate value to target Windows Me or later. +#endif + +#ifndef _WIN32_IE // Allow use of features specific to IE 6.0 or later. +#define _WIN32_IE 0x0600 // Change this to the appropriate value to target other versions of IE. +#endif + +#define WIN32_LEAN_AND_MEAN // Exclude rarely-used stuff from Windows headers +// Windows Header Files: +#include + + + +// TODO: reference additional headers your program requires here diff --git a/indra/libpathing/CMakeLists.txt b/indra/libpathing/CMakeLists.txt new file mode 100644 index 000000000..12d409f20 --- /dev/null +++ b/indra/libpathing/CMakeLists.txt @@ -0,0 +1,13 @@ +cmake_minimum_required(VERSION 2.6.4) + +project(ndPathingLib CXX C) + +if( MSVC ) + add_definitions(-D_HAS_ITERATOR_DEBUGGING=0 -D_SECURE_SCL=0 -D_CRT_SECURE_NO_WARNINGS=1) +endif( MSVC ) + +file (GLOB SOURCE_FILES *.cpp ) +file (GLOB INCLUDE_FILES *.h ) + +add_library(nd_Pathing STATIC ${SOURCE_FILES} ${INCLUDE_FILES} ) + diff --git a/indra/libpathing/llpathinglib.cpp b/indra/libpathing/llpathinglib.cpp new file mode 100644 index 000000000..041311de9 --- /dev/null +++ b/indra/libpathing/llpathinglib.cpp @@ -0,0 +1,89 @@ +#include "llpathinglib.h" + +void LLPathingLib::initSystem() +{ +} + +void LLPathingLib::quitSystem() +{ +} + +LLPathingLib* LLPathingLib::getInstance() +{ + static LLPathingLib sObj; + return &sObj; +} + +bool LLPathingLib::isFunctional() +{ + return false; +} + +void LLPathingLib::extractNavMeshSrcFromLLSD( std::vector< unsigned char > const &aMeshData, int aNavigation ) +{ +} + +LLPathingLib::LLPLResult LLPathingLib::generatePath( PathingPacket const& ) +{ + return LLPL_NO_PATH; +} + +void LLPathingLib::cleanupResidual() +{ +} + +void LLPathingLib::stitchNavMeshes( ) +{ +} + +void LLPathingLib::renderNavMesh( void ) +{ +} + +void LLPathingLib::renderNavMeshShapesVBO( unsigned int aShapeTypeBitmask ) +{ +} + +void LLPathingLib::renderPath() +{ +} + +void LLPathingLib::cleanupVBOManager() +{ +} + +void LLPathingLib::setNavMeshColors( NavMeshColors const &aColors ) +{ +} + +void LLPathingLib::setNavMeshMaterialType( LLPLCharacterType aType ) +{ +} + +void LLPathingLib::renderNavMeshEdges() +{ +} + +void LLPathingLib::renderPathBookend( LLRender&, LLPLRenderType ) +{ +} + +void LLPathingLib::renderSimpleShapes( LLRender&, float ) +{ +} + +void LLPathingLib::createPhysicsCapsuleRep( float, float, bool, LLUUID const& ) +{ +} + +void LLPathingLib::cleanupPhysicsCapsuleRepResiduals() +{ +} + +void LLPathingLib::processNavMeshData() +{ +} + +void LLPathingLib::renderSimpleShapeCapsuleID( LLRender&, LLUUID const&, LLVector3 const&, LLQuaternion const& ) +{ +} diff --git a/indra/libpathing/llpathinglib.h b/indra/libpathing/llpathinglib.h new file mode 100644 index 000000000..70fd1d767 --- /dev/null +++ b/indra/libpathing/llpathinglib.h @@ -0,0 +1,181 @@ +#ifndef LL_PATHINGLIB_H +#define LL_PATHINGLIB_H + +#include + +#ifndef LL_V3MATH_H +class LLVector3 +{ +public: + float mV[3]; +}; +#endif + +#ifndef LL_V4COLORU_H +class LLColor4U +{ +public: + unsigned char mV[4]; +}; +#endif + +#ifndef LL_LLUUID_H +class LLUUID +{ +public: + unsigned char mData[16]; +}; +#endif + +#ifndef LLQUATERNION_H +class LLQuaternion +{ +public: + double mQ[4]; +}; +#endif + +class LLRender; + +class LLPathingLib +{ +public: + enum LLPLResult + { + LLPL_NO_PATH, + LLPL_PATH_GENERATED_OK, + }; + + enum LLPLCharacterType + { + LLPL_CHARACTER_TYPE_NONE, + LLPL_CHARACTER_TYPE_A, + LLPL_CHARACTER_TYPE_B, + LLPL_CHARACTER_TYPE_C, + LLPL_CHARACTER_TYPE_D, + }; + + enum LLShapeType + { + LLST_WalkableObjects = 1, + LLST_ObstacleObjects = 2, + LLST_MaterialPhantoms = 3, + LLST_ExclusionPhantoms = 4, + }; + + enum LLPLRenderType + { + LLPL_START, + LLPL_END, + }; + + struct Vector + { + double mX; + double mY; + double mZ; + + Vector& operator=( LLVector3 const &aRHS ) + { + mX = aRHS.mV[0]; + mY = aRHS.mV[1]; + mZ = aRHS.mV[2]; + + return *this; + } + + operator LLVector3() const + { + LLVector3 ret; + ret.mV[0] = mX; + ret.mV[1] = mY; + ret.mV[2] = mZ; + return ret; + } + }; + + struct PathingPacket + { + Vector mStartPointA; + Vector mEndPointA; + + Vector mStartPointB; + Vector mEndPointB; + + bool mHasPointA; + bool mHasPointB; + + double mCharacterWidth; + LLPLCharacterType mCharacterType; + }; + + struct NavMeshColor + { + unsigned char mR; + unsigned char mG; + unsigned char mB; + unsigned char mAlpha; + + NavMeshColor& operator=( LLColor4U const &aRHS ) + { + mR = aRHS.mV[0]; + mG = aRHS.mV[1]; + mB = aRHS.mV[2]; + mAlpha = aRHS.mV[3]; + + return *this; + } + + }; + + struct NavMeshColors + { + NavMeshColor mWalkable; + NavMeshColor mObstacle; + NavMeshColor mMaterial; + NavMeshColor mExclusion; + NavMeshColor mConnectedEdge; + NavMeshColor mBoundaryEdge; + NavMeshColor mHeatColorBase; + NavMeshColor mHeatColorMax; + NavMeshColor mFaceColor; + NavMeshColor mStarValid; + NavMeshColor mStarInvalid; + NavMeshColor mTestPath; + NavMeshColor mWaterColor; + }; + + static void initSystem(); + static void quitSystem(); + static LLPathingLib* getInstance(); + + bool isFunctional(); + + void extractNavMeshSrcFromLLSD( std::vector< unsigned char> const &aMeshData, int aNavigation ); + + LLPLResult generatePath( PathingPacket const& ); + void cleanupResidual(); + + void stitchNavMeshes( ); + + void renderNavMesh( void ); + void renderNavMeshShapesVBO( unsigned int aShapeTypeBitmask ); + void renderPath(); + void cleanupVBOManager(); + + void renderNavMeshEdges(); + void renderPathBookend( LLRender&, LLPLRenderType ); + + void setNavMeshColors( NavMeshColors const& ); + void setNavMeshMaterialType( LLPLCharacterType ); + + void renderSimpleShapes( LLRender&, float ); + + void createPhysicsCapsuleRep( float, float, bool, LLUUID const& ); + void cleanupPhysicsCapsuleRepResiduals(); + + void processNavMeshData(); + void renderSimpleShapeCapsuleID( LLRender&, LLUUID const&, LLVector3 const&, LLQuaternion const& ); +}; + +#endif diff --git a/indra/libpathing/llphysicsextensions.cpp b/indra/libpathing/llphysicsextensions.cpp new file mode 100644 index 000000000..ad90d3ce1 --- /dev/null +++ b/indra/libpathing/llphysicsextensions.cpp @@ -0,0 +1,10 @@ +#include "llphysicsextensions.h" + +void LLPhysicsExtensions::quitSystem() +{ +} + +bool LLPhysicsExtensions::isFunctional() +{ + return false; +} diff --git a/indra/libpathing/llphysicsextensions.h b/indra/libpathing/llphysicsextensions.h new file mode 100644 index 000000000..4bd6d7929 --- /dev/null +++ b/indra/libpathing/llphysicsextensions.h @@ -0,0 +1,12 @@ +#ifndef LLPYHSICS_EXTENSIONS_H +#define LLPYHSICS_EXTENSIONS_H + +class LLPhysicsExtensions +{ +public: + static void quitSystem(); + + bool isFunctional(); +}; + +#endif diff --git a/indra/llprimitive/llmodel.cpp b/indra/llprimitive/llmodel.cpp index 6bf7819a4..6779b49a1 100644 --- a/indra/llprimitive/llmodel.cpp +++ b/indra/llprimitive/llmodel.cpp @@ -28,7 +28,7 @@ #include "llmodel.h" #include "llmemory.h" -#include "llconvexdecomposition.h" +#include "LLConvexDecomposition.h" #include "llsdserialize.h" #include "llvector4a.h" #if LL_MSVC diff --git a/indra/newview/llmeshrepository.h b/indra/newview/llmeshrepository.h index b60450564..5ee1523f5 100644 --- a/indra/newview/llmeshrepository.h +++ b/indra/newview/llmeshrepository.h @@ -35,7 +35,7 @@ #define LLCONVEXDECOMPINTER_STATIC 1 -#include "llconvexdecomposition.h" +#include "LLConvexDecomposition.h" #include "lluploadfloaterobservers.h" class LLVOVolume; diff --git a/indra/newview/viewer_manifest.py b/indra/newview/viewer_manifest.py index 8e5d4d049..5c70ae782 100755 --- a/indra/newview/viewer_manifest.py +++ b/indra/newview/viewer_manifest.py @@ -849,10 +849,13 @@ class Linux_x86_64Manifest(LinuxManifest): if (not self.standalone()) and self.prefix("../../libraries/x86_64-linux/lib/release", dst="lib64"): self.path("libapr-1.so*") self.path("libaprutil-1.so*") + self.path("libcollada14dom.so.2.2", "libcollada14dom.so") self.path("libdb-*.so*") self.path("libcrypto.so.*") self.path("libexpat.so*") + self.path("libglod.so") self.path("libhunspell-1.3.so*") + self.path("libminizip.so.1.2.3", "libminizip.so"); self.path("libssl.so*") self.path("libuuid.so*") self.path("libSDL-1.2.so*") diff --git a/install.xml b/install.xml index add4b7e95..25c169284 100644 --- a/install.xml +++ b/install.xml @@ -50,6 +50,13 @@ url https://bitbucket.org/SingularityViewer/libraries/downloads/glod-1.0pre4-linux-20110611.tar.bz2 + linux64 + + md5sum + 9552782ad333c819c91d31087750db64 + url + https://bitbucket.org/Siana/singularityviewer/downloads/glod-1.0pre4-linux-x86_64-20120609.tar.bz2 + windows md5sum @@ -225,6 +232,13 @@ url https://bitbucket.org/SingularityViewer/libraries/downloads/colladadom-2.2-linux-20110621.tar.bz2 + linux64 + + md5sum + bab6e7fea2411dd375d76bb4ce9118a5 + url + https://bitbucket.org/Siana/singularityviewer/downloads/colladadom-2.2-linux-x86_64-20120719.tar.bz2 + windows md5sum @@ -932,37 +946,6 @@ - ndPhysicsStub - - description - llphysicsextension_stub modified with hacd by NickyD - license - lgpl - packages - - darwin - - md5sum - 0b51a73814b755bc001e13ca2e6202eb - url - https://bitbucket.org/LightDrake/singularitylibraries/downloads/ndPhysicsStub-0.1.1-Darwin_ao10.5-singu.tar.bz2 - - linux - - md5sum - 017e37250d49686a11db0b88d6633404 - url - https://bitbucket.org/LightDrake/singularitylibraries/downloads/ndPhysicsStub-r65-linux32-singu.tar.bz2 - - windows - - md5sum - 77ac92467e9c4e28cd7e9f9bc4f5a1c1 - url - https://bitbucket.org/LightDrake/singularitylibraries/downloads/ndPhysicsStub-r65-win32-singu.tar.bz2 - - - ogg-vorbis copyright @@ -1096,6 +1079,13 @@ url https://bitbucket.org/SingularityViewer/libraries/downloads/pcre-7.6-linux-20110504.tar.bz2 + linux64 + + md5sum + be0a9c92b5ab7d6761574b61685d6300 + url + https://bitbucket.org/Siana/singularityviewer/downloads/pcre-7.6-linux-x86_64-20120715.tar.bz2 + pulseaudio