diff --git a/src/collider/CMakeLists.txt b/src/collider/CMakeLists.txt index b2d90a5f7..ecbccfdfe 100644 --- a/src/collider/CMakeLists.txt +++ b/src/collider/CMakeLists.txt @@ -14,16 +14,10 @@ project (collider) -file(GLOB_RECURSE SOURCE_FILES CONFIGURE_DEPENDS *.cpp) -add_library(${PROJECT_NAME} OBJECT ${SOURCE_FILES}) -target_include_directories(${PROJECT_NAME} PUBLIC +add_library(${PROJECT_NAME} INTERFACE) +target_include_directories(${PROJECT_NAME} INTERFACE $ $) -target_link_libraries(${PROJECT_NAME} PUBLIC utilities) -target_compile_options(${PROJECT_NAME} PRIVATE ${MANIFOLD_FLAGS}) - -target_compile_features(${PROJECT_NAME} - PUBLIC cxx_std_17 -) -install(TARGETS ${PROJECT_NAME} EXPORT manifoldTargets) -install(FILES include/collider.h DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/${CMAKE_PROJECT_NAME}) +target_link_libraries(${PROJECT_NAME} INTERFACE utilities) +target_compile_options(${PROJECT_NAME} INTERFACE ${MANIFOLD_FLAGS}) +target_compile_features(${PROJECT_NAME} INTERFACE cxx_std_17) diff --git a/src/collider/include/collider.h b/src/collider/include/collider.h index 8ffed384c..c7dd9a962 100644 --- a/src/collider/include/collider.h +++ b/src/collider/include/collider.h @@ -13,24 +13,374 @@ // limitations under the License. #pragma once +#include "par.h" #include "public.h" #include "sparse.h" +#include "utils.h" #include "vec.h" +#ifdef _MSC_VER +#include +#endif + namespace manifold { +namespace collider_internal { +// Adjustable parameters +constexpr int kInitialLength = 128; +constexpr int kLengthMultiple = 4; +constexpr int kSequentialThreshold = 512; +// Fundamental constants +constexpr int kRoot = 1; + +#ifdef _MSC_VER + +#ifndef _WINDEF_ +typedef unsigned long DWORD; +#endif + +uint32_t inline ctz(uint32_t value) { + DWORD trailing_zero = 0; + + if (_BitScanForward(&trailing_zero, value)) { + return trailing_zero; + } else { + // This is undefined, I better choose 32 than 0 + return 32; + } +} + +uint32_t inline clz(uint32_t value) { + DWORD leading_zero = 0; + + if (_BitScanReverse(&leading_zero, value)) { + return 31 - leading_zero; + } else { + // Same remarks as above + return 32; + } +} +#endif + +constexpr inline bool IsLeaf(int node) { return node % 2 == 0; } +constexpr inline bool IsInternal(int node) { return node % 2 == 1; } +constexpr inline int Node2Internal(int node) { return (node - 1) / 2; } +constexpr inline int Internal2Node(int internal) { return internal * 2 + 1; } +constexpr inline int Node2Leaf(int node) { return node / 2; } +constexpr inline int Leaf2Node(int leaf) { return leaf * 2; } + +struct CreateRadixTree { + VecView nodeParent_; + VecView> internalChildren_; + const VecView leafMorton_; + + int PrefixLength(uint32_t a, uint32_t b) const { +// count-leading-zeros is used to find the number of identical highest-order +// bits +#ifdef _MSC_VER + // return __lzcnt(a ^ b); + return clz(a ^ b); +#else + return __builtin_clz(a ^ b); +#endif + } + + int PrefixLength(int i, int j) const { + if (j < 0 || j >= static_cast(leafMorton_.size())) { + return -1; + } else { + int out; + if (leafMorton_[i] == leafMorton_[j]) + // use index to disambiguate + out = 32 + + PrefixLength(static_cast(i), static_cast(j)); + else + out = PrefixLength(leafMorton_[i], leafMorton_[j]); + return out; + } + } + + int RangeEnd(int i) const { + // Determine direction of range (+1 or -1) + int dir = PrefixLength(i, i + 1) - PrefixLength(i, i - 1); + dir = (dir > 0) - (dir < 0); + // Compute conservative range length with exponential increase + int commonPrefix = PrefixLength(i, i - dir); + int max_length = kInitialLength; + while (PrefixLength(i, i + dir * max_length) > commonPrefix) + max_length *= kLengthMultiple; + // Compute precise range length with binary search + int length = 0; + for (int step = max_length / 2; step > 0; step /= 2) { + if (PrefixLength(i, i + dir * (length + step)) > commonPrefix) + length += step; + } + return i + dir * length; + } + + int FindSplit(int first, int last) const { + int commonPrefix = PrefixLength(first, last); + // Find the furthest object that shares more than commonPrefix bits with the + // first one, using binary search. + int split = first; + int step = last - first; + do { + step = (step + 1) >> 1; // divide by 2, rounding up + int newSplit = split + step; + if (newSplit < last) { + int splitPrefix = PrefixLength(first, newSplit); + if (splitPrefix > commonPrefix) split = newSplit; + } + } while (step > 1); + return split; + } + + void operator()(int internal) { + int first = internal; + // Find the range of objects with a common prefix + int last = RangeEnd(first); + if (first > last) std::swap(first, last); + // Determine where the next-highest difference occurs + int split = FindSplit(first, last); + int child1 = split == first ? Leaf2Node(split) : Internal2Node(split); + ++split; + int child2 = split == last ? Leaf2Node(split) : Internal2Node(split); + // Record parent_child relationships. + internalChildren_[internal].first = child1; + internalChildren_[internal].second = child2; + int node = Internal2Node(internal); + nodeParent_[child1] = node; + nodeParent_[child2] = node; + } +}; + +template +struct FindCollisions { + VecView queries; + VecView nodeBBox_; + VecView> internalChildren_; + Recorder recorder; + + int RecordCollision(int node, const int queryIdx) { + bool overlaps = nodeBBox_[node].DoesOverlap(queries[queryIdx]); + if (overlaps && IsLeaf(node)) { + int leafIdx = Node2Leaf(node); + if (!selfCollision || leafIdx != queryIdx) { + recorder.record(queryIdx, leafIdx); + } + } + return overlaps && IsInternal(node); // Should traverse into node + } + + void operator()(const int queryIdx) { + // stack cannot overflow because radix tree has max depth 30 (Morton code) + + // 32 (index). + int stack[64]; + int top = -1; + // Depth-first search + int node = kRoot; + // same implies that this query do not have any collision + if (recorder.earlyexit(queryIdx)) return; + while (1) { + int internal = Node2Internal(node); + int child1 = internalChildren_[internal].first; + int child2 = internalChildren_[internal].second; + + int traverse1 = RecordCollision(child1, queryIdx); + int traverse2 = RecordCollision(child2, queryIdx); + + if (!traverse1 && !traverse2) { + if (top < 0) break; // done + node = stack[top--]; // get a saved node + } else { + node = traverse1 ? child1 : child2; // go here next + if (traverse1 && traverse2) { + stack[++top] = child2; // save the other for later + } + } + } + recorder.end(queryIdx); + } +}; + +struct CountCollisions { + VecView counts; + VecView empty; + void record(int queryIdx, int _leafIdx) { counts[queryIdx]++; } + bool earlyexit(int _queryIdx) { return false; } + void end(int queryIdx) { + if (counts[queryIdx] == 0) empty[queryIdx] = 1; + } +}; + +template +struct SeqCollisionRecorder { + SparseIndices& queryTri_; + void record(int queryIdx, int leafIdx) const { + if (inverted) + queryTri_.Add(leafIdx, queryIdx); + else + queryTri_.Add(queryIdx, leafIdx); + } + bool earlyexit(int queryIdx) const { return false; } + void end(int queryIdx) const {} +}; + +template +struct ParCollisionRecorder { + SparseIndices& queryTri; + VecView counts; + VecView empty; + void record(int queryIdx, int leafIdx) { + int pos = counts[queryIdx]++; + if (inverted) + queryTri.Set(pos, leafIdx, queryIdx); + else + queryTri.Set(pos, queryIdx, leafIdx); + } + bool earlyexit(int queryIdx) const { return empty[queryIdx] == 1; } + void end(int queryIdx) const {} +}; + +struct BuildInternalBoxes { + VecView nodeBBox_; + VecView counter_; + const VecView nodeParent_; + const VecView> internalChildren_; + + void operator()(int leaf) { + int node = Leaf2Node(leaf); + do { + node = nodeParent_[node]; + int internal = Node2Internal(node); + if (AtomicAdd(counter_[internal], 1) == 0) return; + nodeBBox_[node] = nodeBBox_[internalChildren_[internal].first].Union( + nodeBBox_[internalChildren_[internal].second]); + } while (node != kRoot); + } +}; + +struct TransformBox { + const glm::mat4x3 transform; + void operator()(Box& box) { box = box.Transform(transform); } +}; + +constexpr inline uint32_t SpreadBits3(uint32_t v) { + v = 0xFF0000FFu & (v * 0x00010001u); + v = 0x0F00F00Fu & (v * 0x00000101u); + v = 0xC30C30C3u & (v * 0x00000011u); + v = 0x49249249u & (v * 0x00000005u); + return v; +} +} // namespace collider_internal + /** @ingroup Private */ class Collider { public: - Collider() {} + Collider(){}; + Collider(const VecView& leafBB, - const VecView& leafMorton); - bool Transform(glm::mat4x3); - void UpdateBoxes(const VecView& leafBB); + const VecView& leafMorton) { + ZoneScoped; + DEBUG_ASSERT(leafBB.size() == leafMorton.size(), userErr, + "vectors must be the same length"); + int num_nodes = 2 * leafBB.size() - 1; + // assign and allocate members + nodeBBox_.resize(num_nodes); + nodeParent_.resize(num_nodes, -1); + internalChildren_.resize(leafBB.size() - 1, std::make_pair(-1, -1)); + // organize tree + for_each_n(autoPolicy(NumInternal(), 1e4), countAt(0), NumInternal(), + collider_internal::CreateRadixTree( + {nodeParent_, internalChildren_, leafMorton})); + UpdateBoxes(leafBB); + } + + bool Transform(glm::mat4x3 transform) { + ZoneScoped; + bool axisAligned = true; + for (int row : {0, 1, 2}) { + int count = 0; + for (int col : {0, 1, 2}) { + if (transform[col][row] == 0.0f) ++count; + } + if (count != 2) axisAligned = false; + } + if (axisAligned) { + for_each(autoPolicy(nodeBBox_.size(), 1e5), nodeBBox_.begin(), + nodeBBox_.end(), + [transform](Box& box) { box = box.Transform(transform); }); + } + return axisAligned; + } + + void UpdateBoxes(const VecView& leafBB) { + ZoneScoped; + DEBUG_ASSERT(leafBB.size() == NumLeaves(), userErr, + "must have the same number of updated boxes as original"); + // copy in leaf node Boxes + auto leaves = StridedRange(nodeBBox_.begin(), nodeBBox_.end(), 2); + copy(leafBB.cbegin(), leafBB.cend(), leaves.begin()); + // create global counters + Vec counter(NumInternal(), 0); + // kernel over leaves to save internal Boxes + for_each_n(autoPolicy(NumInternal(), 1e3), countAt(0), NumLeaves(), + collider_internal::BuildInternalBoxes( + {nodeBBox_, counter, nodeParent_, internalChildren_})); + } + template - SparseIndices Collisions(const VecView& queriesIn) const; - static uint32_t MortonCode(glm::vec3 position, Box bBox); + SparseIndices Collisions(const VecView& queriesIn) const { + ZoneScoped; + using collider_internal::FindCollisions; + // note that the length is 1 larger than the number of queries so the last + // element can store the sum when using exclusive scan + if (queriesIn.size() < collider_internal::kSequentialThreshold) { + SparseIndices queryTri; + for_each_n( + ExecutionPolicy::Seq, countAt(0), queriesIn.size(), + FindCollisions>{ + queriesIn, nodeBBox_, internalChildren_, {queryTri}}); + return queryTri; + } else { + // compute the number of collisions to determine the size for allocation + // and offset, this avoids the need for atomic + Vec counts(queriesIn.size() + 1, 0); + Vec empty(queriesIn.size(), 0); + for_each_n( + ExecutionPolicy::Par, countAt(0), queriesIn.size(), + FindCollisions{ + queriesIn, nodeBBox_, internalChildren_, {counts, empty}}); + // compute start index for each query and total count + manifold::exclusive_scan(counts.begin(), counts.end(), counts.begin(), 0, + std::plus()); + if (counts.back() == 0) return SparseIndices(0); + SparseIndices queryTri(counts.back()); + // actually recording collisions + for_each_n( + ExecutionPolicy::Par, countAt(0), queriesIn.size(), + FindCollisions>{ + queriesIn, + nodeBBox_, + internalChildren_, + {queryTri, counts, empty}}); + return queryTri; + } + } + + static uint32_t MortonCode(glm::vec3 position, Box bBox) { + using collider_internal::SpreadBits3; + glm::vec3 xyz = (position - bBox.min) / (bBox.max - bBox.min); + xyz = + glm::min(glm::vec3(1023.0f), glm::max(glm::vec3(0.0f), 1024.0f * xyz)); + uint32_t x = SpreadBits3(static_cast(xyz.x)); + uint32_t y = SpreadBits3(static_cast(xyz.y)); + uint32_t z = SpreadBits3(static_cast(xyz.z)); + return x * 4 + y * 2 + z; + } private: Vec nodeBBox_; diff --git a/src/collider/src/collider.cpp b/src/collider/src/collider.cpp deleted file mode 100644 index ed7778dbe..000000000 --- a/src/collider/src/collider.cpp +++ /dev/null @@ -1,410 +0,0 @@ -// Copyright 2021 The Manifold Authors. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#include "collider.h" - -#include "par.h" -#include "utils.h" - -#ifdef _MSC_VER -#include -#endif - -// Adjustable parameters -constexpr int kInitialLength = 128; -constexpr int kLengthMultiple = 4; -constexpr int kSequentialThreshold = 512; -// Fundamental constants -constexpr int kRoot = 1; - -#ifdef _MSC_VER - -#ifndef _WINDEF_ -typedef unsigned long DWORD; -#endif - -uint32_t __inline ctz(uint32_t value) { - DWORD trailing_zero = 0; - - if (_BitScanForward(&trailing_zero, value)) { - return trailing_zero; - } else { - // This is undefined, I better choose 32 than 0 - return 32; - } -} - -uint32_t __inline clz(uint32_t value) { - DWORD leading_zero = 0; - - if (_BitScanReverse(&leading_zero, value)) { - return 31 - leading_zero; - } else { - // Same remarks as above - return 32; - } -} -#endif - -namespace { -using namespace manifold; - -bool IsLeaf(int node) { return node % 2 == 0; } -bool IsInternal(int node) { return node % 2 == 1; } -int Node2Internal(int node) { return (node - 1) / 2; } -int Internal2Node(int internal) { return internal * 2 + 1; } -int Node2Leaf(int node) { return node / 2; } -int Leaf2Node(int leaf) { return leaf * 2; } - -struct CreateRadixTree { - VecView nodeParent_; - VecView> internalChildren_; - const VecView leafMorton_; - - int PrefixLength(uint32_t a, uint32_t b) const { -// count-leading-zeros is used to find the number of identical highest-order -// bits -#ifdef _MSC_VER - // return __lzcnt(a ^ b); - return clz(a ^ b); -#else - return __builtin_clz(a ^ b); -#endif - } - - int PrefixLength(int i, int j) const { - if (j < 0 || j >= static_cast(leafMorton_.size())) { - return -1; - } else { - int out; - if (leafMorton_[i] == leafMorton_[j]) - // use index to disambiguate - out = 32 + - PrefixLength(static_cast(i), static_cast(j)); - else - out = PrefixLength(leafMorton_[i], leafMorton_[j]); - return out; - } - } - - int RangeEnd(int i) const { - // Determine direction of range (+1 or -1) - int dir = PrefixLength(i, i + 1) - PrefixLength(i, i - 1); - dir = (dir > 0) - (dir < 0); - // Compute conservative range length with exponential increase - int commonPrefix = PrefixLength(i, i - dir); - int max_length = kInitialLength; - while (PrefixLength(i, i + dir * max_length) > commonPrefix) - max_length *= kLengthMultiple; - // Compute precise range length with binary search - int length = 0; - for (int step = max_length / 2; step > 0; step /= 2) { - if (PrefixLength(i, i + dir * (length + step)) > commonPrefix) - length += step; - } - return i + dir * length; - } - - int FindSplit(int first, int last) const { - int commonPrefix = PrefixLength(first, last); - // Find the furthest object that shares more than commonPrefix bits with the - // first one, using binary search. - int split = first; - int step = last - first; - do { - step = (step + 1) >> 1; // divide by 2, rounding up - int newSplit = split + step; - if (newSplit < last) { - int splitPrefix = PrefixLength(first, newSplit); - if (splitPrefix > commonPrefix) split = newSplit; - } - } while (step > 1); - return split; - } - - void operator()(int internal) { - int first = internal; - // Find the range of objects with a common prefix - int last = RangeEnd(first); - if (first > last) std::swap(first, last); - // Determine where the next-highest difference occurs - int split = FindSplit(first, last); - int child1 = split == first ? Leaf2Node(split) : Internal2Node(split); - ++split; - int child2 = split == last ? Leaf2Node(split) : Internal2Node(split); - // Record parent_child relationships. - internalChildren_[internal].first = child1; - internalChildren_[internal].second = child2; - int node = Internal2Node(internal); - nodeParent_[child1] = node; - nodeParent_[child2] = node; - } -}; - -template -struct FindCollisions { - VecView queries; - VecView nodeBBox_; - VecView> internalChildren_; - Recorder recorder; - - int RecordCollision(int node, const int queryIdx) { - bool overlaps = nodeBBox_[node].DoesOverlap(queries[queryIdx]); - if (overlaps && IsLeaf(node)) { - int leafIdx = Node2Leaf(node); - if (!selfCollision || leafIdx != queryIdx) { - recorder.record(queryIdx, leafIdx); - } - } - return overlaps && IsInternal(node); // Should traverse into node - } - - void operator()(const int queryIdx) { - // stack cannot overflow because radix tree has max depth 30 (Morton code) + - // 32 (index). - int stack[64]; - int top = -1; - // Depth-first search - int node = kRoot; - // same implies that this query do not have any collision - if (recorder.earlyexit(queryIdx)) return; - while (1) { - int internal = Node2Internal(node); - int child1 = internalChildren_[internal].first; - int child2 = internalChildren_[internal].second; - - int traverse1 = RecordCollision(child1, queryIdx); - int traverse2 = RecordCollision(child2, queryIdx); - - if (!traverse1 && !traverse2) { - if (top < 0) break; // done - node = stack[top--]; // get a saved node - } else { - node = traverse1 ? child1 : child2; // go here next - if (traverse1 && traverse2) { - stack[++top] = child2; // save the other for later - } - } - } - recorder.end(queryIdx); - } -}; - -struct CountCollisions { - VecView counts; - VecView empty; - void record(int queryIdx, int _leafIdx) { counts[queryIdx]++; } - bool earlyexit(int _queryIdx) { return false; } - void end(int queryIdx) { - if (counts[queryIdx] == 0) empty[queryIdx] = 1; - } -}; - -template -struct SeqCollisionRecorder { - SparseIndices& queryTri_; - void record(int queryIdx, int leafIdx) const { - if (inverted) - queryTri_.Add(leafIdx, queryIdx); - else - queryTri_.Add(queryIdx, leafIdx); - } - bool earlyexit(int queryIdx) const { return false; } - void end(int queryIdx) const {} -}; - -template -struct ParCollisionRecorder { - SparseIndices& queryTri; - VecView counts; - VecView empty; - void record(int queryIdx, int leafIdx) { - int pos = counts[queryIdx]++; - if (inverted) - queryTri.Set(pos, leafIdx, queryIdx); - else - queryTri.Set(pos, queryIdx, leafIdx); - } - bool earlyexit(int queryIdx) const { return empty[queryIdx] == 1; } - void end(int queryIdx) const {} -}; - -struct BuildInternalBoxes { - VecView nodeBBox_; - VecView counter_; - const VecView nodeParent_; - const VecView> internalChildren_; - - void operator()(int leaf) { - int node = Leaf2Node(leaf); - do { - node = nodeParent_[node]; - int internal = Node2Internal(node); - if (AtomicAdd(counter_[internal], 1) == 0) return; - nodeBBox_[node] = nodeBBox_[internalChildren_[internal].first].Union( - nodeBBox_[internalChildren_[internal].second]); - } while (node != kRoot); - } -}; - -struct TransformBox { - const glm::mat4x3 transform; - void operator()(Box& box) { box = box.Transform(transform); } -}; - -uint32_t SpreadBits3(uint32_t v) { - v = 0xFF0000FFu & (v * 0x00010001u); - v = 0x0F00F00Fu & (v * 0x00000101u); - v = 0xC30C30C3u & (v * 0x00000011u); - v = 0x49249249u & (v * 0x00000005u); - return v; -} -} // namespace - -namespace manifold { - -/** - * Creates a Bounding Volume Hierarchy (BVH) from an input set of axis-aligned - * bounding boxes and corresponding Morton codes. It is assumed these vectors - * are already sorted by increasing Morton code. - */ -Collider::Collider(const VecView& leafBB, - const VecView& leafMorton) { - ZoneScoped; - DEBUG_ASSERT(leafBB.size() == leafMorton.size(), userErr, - "vectors must be the same length"); - int num_nodes = 2 * leafBB.size() - 1; - // assign and allocate members - nodeBBox_.resize(num_nodes); - nodeParent_.resize(num_nodes, -1); - internalChildren_.resize(leafBB.size() - 1, std::make_pair(-1, -1)); - // organize tree - for_each_n(autoPolicy(NumInternal(), 1e4), countAt(0), NumInternal(), - CreateRadixTree({nodeParent_, internalChildren_, leafMorton})); - UpdateBoxes(leafBB); -} - -/** - * For a vector of query objects, this returns a sparse array of overlaps - * between the queries and the bounding boxes of the collider. Queries are - * normally axis-aligned bounding boxes. Points can also be used, and this case - * overlaps are defined as lying in the XY projection of the bounding box. If - * the query vector is the leaf vector, set selfCollision to true, which will - * then not report any collisions between an index and itself. - */ -template -SparseIndices Collider::Collisions(const VecView& queriesIn) const { - ZoneScoped; - // note that the length is 1 larger than the number of queries so the last - // element can store the sum when using exclusive scan - if (queriesIn.size() < kSequentialThreshold) { - SparseIndices queryTri; - for_each_n(ExecutionPolicy::Seq, countAt(0), queriesIn.size(), - FindCollisions>{ - queriesIn, nodeBBox_, internalChildren_, {queryTri}}); - return queryTri; - } else { - // compute the number of collisions to determine the size for allocation and - // offset, this avoids the need for atomic - Vec counts(queriesIn.size() + 1, 0); - Vec empty(queriesIn.size(), 0); - for_each_n(ExecutionPolicy::Par, countAt(0), queriesIn.size(), - FindCollisions{ - queriesIn, nodeBBox_, internalChildren_, {counts, empty}}); - // compute start index for each query and total count - manifold::exclusive_scan(counts.begin(), counts.end(), counts.begin(), 0, - std::plus()); - if (counts.back() == 0) return SparseIndices(0); - SparseIndices queryTri(counts.back()); - // actually recording collisions - for_each_n(ExecutionPolicy::Par, countAt(0), queriesIn.size(), - FindCollisions>{ - queriesIn, - nodeBBox_, - internalChildren_, - {queryTri, counts, empty}}); - return queryTri; - } -} - -/** - * Recalculate the collider's internal bounding boxes without changing the - * hierarchy. - */ -void Collider::UpdateBoxes(const VecView& leafBB) { - ZoneScoped; - DEBUG_ASSERT(leafBB.size() == NumLeaves(), userErr, - "must have the same number of updated boxes as original"); - // copy in leaf node Boxes - auto leaves = StridedRange(nodeBBox_.begin(), nodeBBox_.end(), 2); - copy(leafBB.cbegin(), leafBB.cend(), leaves.begin()); - // create global counters - Vec counter(NumInternal(), 0); - // kernel over leaves to save internal Boxes - for_each_n( - autoPolicy(NumInternal(), 1e3), countAt(0), NumLeaves(), - BuildInternalBoxes({nodeBBox_, counter, nodeParent_, internalChildren_})); -} - -/** - * Apply axis-aligned transform to all bounding boxes. If transform is not - * axis-aligned, abort and return false to indicate recalculation is necessary. - */ -bool Collider::Transform(glm::mat4x3 transform) { - ZoneScoped; - bool axisAligned = true; - for (int row : {0, 1, 2}) { - int count = 0; - for (int col : {0, 1, 2}) { - if (transform[col][row] == 0.0f) ++count; - } - if (count != 2) axisAligned = false; - } - if (axisAligned) { - for_each(autoPolicy(nodeBBox_.size(), 1e5), nodeBBox_.begin(), - nodeBBox_.end(), TransformBox({transform})); - } - return axisAligned; -} - -/** - * Calculate the 32-bit Morton code of a position within a bounding box. - */ -uint32_t Collider::MortonCode(glm::vec3 position, Box bBox) { - glm::vec3 xyz = (position - bBox.min) / (bBox.max - bBox.min); - xyz = glm::min(glm::vec3(1023.0f), glm::max(glm::vec3(0.0f), 1024.0f * xyz)); - uint32_t x = SpreadBits3(static_cast(xyz.x)); - uint32_t y = SpreadBits3(static_cast(xyz.y)); - uint32_t z = SpreadBits3(static_cast(xyz.z)); - return x * 4 + y * 2 + z; -} - -template SparseIndices Collider::Collisions( - const VecView&) const; - -template SparseIndices Collider::Collisions( - const VecView&) const; - -template SparseIndices Collider::Collisions( - const VecView&) const; - -template SparseIndices Collider::Collisions( - const VecView&) const; - -template SparseIndices Collider::Collisions( - const VecView&) const; - -template SparseIndices Collider::Collisions( - const VecView&) const; -} // namespace manifold diff --git a/src/manifold/CMakeLists.txt b/src/manifold/CMakeLists.txt index d0405bf91..ecea10840 100644 --- a/src/manifold/CMakeLists.txt +++ b/src/manifold/CMakeLists.txt @@ -22,7 +22,7 @@ target_include_directories(${PROJECT_NAME} PUBLIC $) target_link_libraries(${PROJECT_NAME} PUBLIC utilities sdf - PRIVATE collider polygon ${MANIFOLD_INCLUDE} quickhull + PRIVATE $ polygon ${MANIFOLD_INCLUDE} quickhull ) target_compile_options(${PROJECT_NAME} PRIVATE ${MANIFOLD_FLAGS}) diff --git a/src/manifold/src/smoothing.cpp b/src/manifold/src/smoothing.cpp index 433432ffc..30f5979f3 100644 --- a/src/manifold/src/smoothing.cpp +++ b/src/manifold/src/smoothing.cpp @@ -76,7 +76,7 @@ struct InterpTri { static glm::vec4 Homogeneous(glm::vec3 v) { return glm::vec4(v, 1.0f); } static glm::vec3 HNormalize(glm::vec4 v) { - return v.w == 0 ? v : (glm::vec3(v) / v.w); + return v.w == 0 ? glm::vec3(v) : (glm::vec3(v) / v.w); } static glm::vec4 Scale(glm::vec4 v, float scale) { diff --git a/src/polygon/CMakeLists.txt b/src/polygon/CMakeLists.txt index 459e6f619..46274125a 100644 --- a/src/polygon/CMakeLists.txt +++ b/src/polygon/CMakeLists.txt @@ -21,7 +21,7 @@ target_include_directories(${PROJECT_NAME} PUBLIC $) target_link_libraries(${PROJECT_NAME} PUBLIC utilities - PRIVATE collider + PRIVATE $ ) target_compile_options(${PROJECT_NAME} PRIVATE ${MANIFOLD_FLAGS}) diff --git a/src/utilities/CMakeLists.txt b/src/utilities/CMakeLists.txt index 1aeb58606..ceb004c25 100644 --- a/src/utilities/CMakeLists.txt +++ b/src/utilities/CMakeLists.txt @@ -57,6 +57,12 @@ target_include_directories(${PROJECT_NAME} INTERFACE $) target_link_libraries(${PROJECT_NAME} INTERFACE glm::glm) +if(NOT MSVC) +target_precompile_headers(${PROJECT_NAME} INTERFACE + $ + $) +endif() + if(MANIFOLD_EXCEPTIONS) target_compile_options(${PROJECT_NAME} INTERFACE -DMANIFOLD_EXCEPTIONS=1 diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index ad8c90915..1353fd0b7 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -73,6 +73,7 @@ target_compile_options(${PROJECT_NAME} PRIVATE ${MANIFOLD_FLAGS}) target_compile_features(${PROJECT_NAME} PUBLIC cxx_std_17) add_test(test_all ${PROJECT_NAME}) +target_precompile_headers(${PROJECT_NAME} INTERFACE test.h) if(EMSCRIPTEN) set_target_properties(${PROJECT_NAME} PROPERTIES LINK_FLAGS