Path: blob/master/thirdparty/manifold/src/collider.h
10278 views
// Copyright 2021 The Manifold Authors.1//2// Licensed under the Apache License, Version 2.0 (the "License");3// you may not use this file except in compliance with the License.4// You may obtain a copy of the License at5//6// http://www.apache.org/licenses/LICENSE-2.07//8// Unless required by applicable law or agreed to in writing, software9// distributed under the License is distributed on an "AS IS" BASIS,10// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.11// See the License for the specific language governing permissions and12// limitations under the License.1314#pragma once15#include "manifold/common.h"16#include "parallel.h"17#include "utils.h"18#include "vec.h"1920#ifdef _MSC_VER21#include <intrin.h>22#endif2324#if (MANIFOLD_PAR == 1)25#include <tbb/combinable.h>26#endif2728namespace manifold {2930namespace collider_internal {31// Adjustable parameters32constexpr int kInitialLength = 128;33constexpr int kLengthMultiple = 4;34constexpr int kSequentialThreshold = 512;35// Fundamental constants36constexpr int kRoot = 1;3738#ifdef _MSC_VER3940#ifndef _WINDEF_41using DWORD = unsigned long;42#endif4344uint32_t inline ctz(uint32_t value) {45DWORD trailing_zero = 0;4647if (_BitScanForward(&trailing_zero, value)) {48return trailing_zero;49} else {50// This is undefined, I better choose 32 than 051return 32;52}53}5455uint32_t inline clz(uint32_t value) {56DWORD leading_zero = 0;5758if (_BitScanReverse(&leading_zero, value)) {59return 31 - leading_zero;60} else {61// Same remarks as above62return 32;63}64}65#endif6667constexpr inline bool IsLeaf(int node) { return node % 2 == 0; }68constexpr inline bool IsInternal(int node) { return node % 2 == 1; }69constexpr inline int Node2Internal(int node) { return (node - 1) / 2; }70constexpr inline int Internal2Node(int internal) { return internal * 2 + 1; }71constexpr inline int Node2Leaf(int node) { return node / 2; }72constexpr inline int Leaf2Node(int leaf) { return leaf * 2; }7374struct CreateRadixTree {75VecView<int> nodeParent_;76VecView<std::pair<int, int>> internalChildren_;77const VecView<const uint32_t> leafMorton_;7879int PrefixLength(uint32_t a, uint32_t b) const {80// count-leading-zeros is used to find the number of identical highest-order81// bits82#ifdef _MSC_VER83// return __lzcnt(a ^ b);84return clz(a ^ b);85#else86return __builtin_clz(a ^ b);87#endif88}8990int PrefixLength(int i, int j) const {91if (j < 0 || j >= static_cast<int>(leafMorton_.size())) {92return -1;93} else {94int out;95if (leafMorton_[i] == leafMorton_[j])96// use index to disambiguate97out = 32 +98PrefixLength(static_cast<uint32_t>(i), static_cast<uint32_t>(j));99else100out = PrefixLength(leafMorton_[i], leafMorton_[j]);101return out;102}103}104105int RangeEnd(int i) const {106// Determine direction of range (+1 or -1)107int dir = PrefixLength(i, i + 1) - PrefixLength(i, i - 1);108dir = (dir > 0) - (dir < 0);109// Compute conservative range length with exponential increase110int commonPrefix = PrefixLength(i, i - dir);111int max_length = kInitialLength;112while (PrefixLength(i, i + dir * max_length) > commonPrefix)113max_length *= kLengthMultiple;114// Compute precise range length with binary search115int length = 0;116for (int step = max_length / 2; step > 0; step /= 2) {117if (PrefixLength(i, i + dir * (length + step)) > commonPrefix)118length += step;119}120return i + dir * length;121}122123int FindSplit(int first, int last) const {124int commonPrefix = PrefixLength(first, last);125// Find the furthest object that shares more than commonPrefix bits with the126// first one, using binary search.127int split = first;128int step = last - first;129do {130step = (step + 1) >> 1; // divide by 2, rounding up131int newSplit = split + step;132if (newSplit < last) {133int splitPrefix = PrefixLength(first, newSplit);134if (splitPrefix > commonPrefix) split = newSplit;135}136} while (step > 1);137return split;138}139140void operator()(int internal) {141int first = internal;142// Find the range of objects with a common prefix143int last = RangeEnd(first);144if (first > last) std::swap(first, last);145// Determine where the next-highest difference occurs146int split = FindSplit(first, last);147int child1 = split == first ? Leaf2Node(split) : Internal2Node(split);148++split;149int child2 = split == last ? Leaf2Node(split) : Internal2Node(split);150// Record parent_child relationships.151internalChildren_[internal].first = child1;152internalChildren_[internal].second = child2;153int node = Internal2Node(internal);154nodeParent_[child1] = node;155nodeParent_[child2] = node;156}157};158159template <typename F, const bool selfCollision, typename Recorder>160struct FindCollision {161F& f;162VecView<const Box> nodeBBox_;163VecView<const std::pair<int, int>> internalChildren_;164Recorder& recorder;165166using Local = typename Recorder::Local;167168inline int RecordCollision(int node, const int queryIdx, Local& local) {169bool overlaps = nodeBBox_[node].DoesOverlap(f(queryIdx));170if (overlaps && IsLeaf(node)) {171int leafIdx = Node2Leaf(node);172if (!selfCollision || leafIdx != queryIdx) {173recorder.record(queryIdx, leafIdx, local);174}175}176return overlaps && IsInternal(node); // Should traverse into node177}178179void operator()(const int queryIdx) {180// stack cannot overflow because radix tree has max depth 30 (Morton code) +181// 32 (index).182int stack[64];183int top = -1;184// Depth-first search185int node = kRoot;186Local& local = recorder.local();187while (1) {188int internal = Node2Internal(node);189int child1 = internalChildren_[internal].first;190int child2 = internalChildren_[internal].second;191192int traverse1 = RecordCollision(child1, queryIdx, local);193int traverse2 = RecordCollision(child2, queryIdx, local);194195if (!traverse1 && !traverse2) {196if (top < 0) break; // done197node = stack[top--]; // get a saved node198} else {199node = traverse1 ? child1 : child2; // go here next200if (traverse1 && traverse2) {201stack[++top] = child2; // save the other for later202}203}204}205}206};207208struct BuildInternalBoxes {209VecView<Box> nodeBBox_;210VecView<int> counter_;211const VecView<int> nodeParent_;212const VecView<std::pair<int, int>> internalChildren_;213214void operator()(int leaf) {215int node = Leaf2Node(leaf);216do {217node = nodeParent_[node];218int internal = Node2Internal(node);219if (AtomicAdd(counter_[internal], 1) == 0) return;220nodeBBox_[node] = nodeBBox_[internalChildren_[internal].first].Union(221nodeBBox_[internalChildren_[internal].second]);222} while (node != kRoot);223}224};225226struct TransformBox {227const mat3x4 transform;228void operator()(Box& box) { box = box.Transform(transform); }229};230231constexpr inline uint32_t SpreadBits3(uint32_t v) {232v = 0xFF0000FFu & (v * 0x00010001u);233v = 0x0F00F00Fu & (v * 0x00000101u);234v = 0xC30C30C3u & (v * 0x00000011u);235v = 0x49249249u & (v * 0x00000005u);236return v;237}238} // namespace collider_internal239240template <typename F>241struct SimpleRecorder {242using Local = F;243F& f;244245inline void record(int queryIdx, int leafIdx, F& f) const {246f(queryIdx, leafIdx);247}248Local& local() { return f; }249};250251template <typename F>252inline SimpleRecorder<F> MakeSimpleRecorder(F& f) {253return SimpleRecorder<F>{f};254}255256/** @ingroup Private */257class Collider {258public:259Collider() {};260261Collider(const VecView<const Box>& leafBB,262const VecView<const uint32_t>& leafMorton) {263ZoneScoped;264DEBUG_ASSERT(leafBB.size() == leafMorton.size(), userErr,265"vectors must be the same length");266int num_nodes = 2 * leafBB.size() - 1;267// assign and allocate members268nodeBBox_.resize_nofill(num_nodes);269nodeParent_.resize(num_nodes, -1);270internalChildren_.resize(leafBB.size() - 1, std::make_pair(-1, -1));271// organize tree272for_each_n(autoPolicy(NumInternal(), 1e4), countAt(0), NumInternal(),273collider_internal::CreateRadixTree(274{nodeParent_, internalChildren_, leafMorton}));275UpdateBoxes(leafBB);276}277278bool Transform(mat3x4 transform) {279ZoneScoped;280bool axisAligned = true;281for (int row : {0, 1, 2}) {282int count = 0;283for (int col : {0, 1, 2}) {284if (transform[col][row] == 0.0) ++count;285}286if (count != 2) axisAligned = false;287}288if (axisAligned) {289for_each(autoPolicy(nodeBBox_.size(), 1e5), nodeBBox_.begin(),290nodeBBox_.end(),291[transform](Box& box) { box = box.Transform(transform); });292}293return axisAligned;294}295296void UpdateBoxes(const VecView<const Box>& leafBB) {297ZoneScoped;298DEBUG_ASSERT(leafBB.size() == NumLeaves(), userErr,299"must have the same number of updated boxes as original");300// copy in leaf node Boxes301auto leaves = StridedRange(nodeBBox_.begin(), nodeBBox_.end(), 2);302copy(leafBB.cbegin(), leafBB.cend(), leaves.begin());303// create global counters304Vec<int> counter(NumInternal(), 0);305// kernel over leaves to save internal Boxes306for_each_n(autoPolicy(NumInternal(), 1e3), countAt(0), NumLeaves(),307collider_internal::BuildInternalBoxes(308{nodeBBox_, counter, nodeParent_, internalChildren_}));309}310311// This function iterates over queriesIn and calls recorder.record(queryIdx,312// leafIdx, local) for each collision it found.313// If selfCollisionl is true, it will skip the case where queryIdx == leafIdx.314// The recorder should provide a local() method that returns a Recorder::Local315// type, representing thread local storage. By default, recorder.record can316// run in parallel and the thread local storage can be combined at the end.317// If parallel is false, the function will run in sequential mode.318//319// If thread local storage is not needed, use SimpleRecorder.320template <const bool selfCollision = false, typename T, typename Recorder>321void Collisions(const VecView<const T>& queriesIn, Recorder& recorder,322bool parallel = true) const {323ZoneScoped;324using collider_internal::FindCollision;325if (internalChildren_.empty()) return;326auto f = [queriesIn](const int i) { return queriesIn[i]; };327for_each_n(parallel ? autoPolicy(queriesIn.size(),328collider_internal::kSequentialThreshold)329: ExecutionPolicy::Seq,330countAt(0), queriesIn.size(),331FindCollision<decltype(f), selfCollision, Recorder>{332f, nodeBBox_, internalChildren_, recorder});333}334335template <const bool selfCollision = false, typename F, typename Recorder>336void Collisions(F f, int n, Recorder& recorder, bool parallel = true) const {337ZoneScoped;338using collider_internal::FindCollision;339if (internalChildren_.empty()) return;340for_each_n(parallel ? autoPolicy(n, collider_internal::kSequentialThreshold)341: ExecutionPolicy::Seq,342countAt(0), n,343FindCollision<decltype(f), selfCollision, Recorder>{344f, nodeBBox_, internalChildren_, recorder});345}346347static uint32_t MortonCode(vec3 position, Box bBox) {348using collider_internal::SpreadBits3;349vec3 xyz = (position - bBox.min) / (bBox.max - bBox.min);350xyz = la::min(vec3(1023.0), la::max(vec3(0.0), 1024.0 * xyz));351uint32_t x = SpreadBits3(static_cast<uint32_t>(xyz.x));352uint32_t y = SpreadBits3(static_cast<uint32_t>(xyz.y));353uint32_t z = SpreadBits3(static_cast<uint32_t>(xyz.z));354return x * 4 + y * 2 + z;355}356357private:358Vec<Box> nodeBBox_;359Vec<int> nodeParent_;360// even nodes are leaves, odd nodes are internal, root is 1361Vec<std::pair<int, int>> internalChildren_;362363size_t NumInternal() const { return internalChildren_.size(); };364size_t NumLeaves() const {365return internalChildren_.empty() ? 0 : (NumInternal() + 1);366};367};368369} // namespace manifold370371372