revert lazy collider (#1569)
This commit is contained in:
@@ -21,7 +21,6 @@ set(
|
||||
edge_op.cpp
|
||||
face_op.cpp
|
||||
impl.cpp
|
||||
lazy_collider.cpp
|
||||
manifold.cpp
|
||||
minkowski.cpp
|
||||
polygon.cpp
|
||||
@@ -44,7 +43,6 @@ set(
|
||||
hashtable.h
|
||||
impl.h
|
||||
iters.h
|
||||
lazy_collider.h
|
||||
mesh_fixes.h
|
||||
parallel.h
|
||||
quickhull.h
|
||||
|
||||
+2
-2
@@ -371,7 +371,7 @@ Intersections Intersect12_(const Manifold::Impl& inP,
|
||||
a.vertPos_[a.halfedge_[i].endVert])
|
||||
: Box();
|
||||
};
|
||||
b.collider_->Collisions<false>(recorder, f, a.halfedge_.size());
|
||||
b.collider_.Collisions<false>(recorder, f, a.halfedge_.size());
|
||||
|
||||
Intersections result = recorder.get();
|
||||
auto& p1q2 = result.p1q2;
|
||||
@@ -456,7 +456,7 @@ Vec<int> Winding03_(const Manifold::Impl& inP, const Manifold::Impl& inQ,
|
||||
};
|
||||
auto recorder = MakeSimpleRecorder(recorderf);
|
||||
auto f = [&](int i) { return a.vertPos_[verts[i]]; };
|
||||
b.collider_->Collisions<false>(recorder, f, verts.size());
|
||||
b.collider_.Collisions<false>(recorder, f, verts.size());
|
||||
// flood fill
|
||||
for_each(autoPolicy(w03.size()), countAt(0), countAt(w03.size()),
|
||||
[&](size_t i) {
|
||||
|
||||
+30
-25
@@ -157,21 +157,18 @@ struct CreateRadixTree {
|
||||
}
|
||||
};
|
||||
|
||||
template <typename F, const bool selfCollision, const bool hasTransform,
|
||||
typename Recorder>
|
||||
template <typename F, const bool selfCollision, typename Recorder>
|
||||
struct FindCollision {
|
||||
F& f;
|
||||
VecView<const Box> nodeBBox_;
|
||||
VecView<const std::pair<int, int>> internalChildren_;
|
||||
Recorder& recorder;
|
||||
mat3x4 transform;
|
||||
|
||||
using Local = typename Recorder::Local;
|
||||
|
||||
inline int RecordCollision(std::invoke_result_t<F, const int> query, int node,
|
||||
const int queryIdx, Local& local) {
|
||||
auto box = nodeBBox_[node];
|
||||
if (hasTransform) box = box.Transform(transform);
|
||||
bool overlaps = box.DoesOverlap(query);
|
||||
if (overlaps && IsLeaf(node)) {
|
||||
int leafIdx = Node2Leaf(node);
|
||||
@@ -299,27 +296,26 @@ class Collider {
|
||||
{nodeBBox_, counter, nodeParent_, internalChildren_}));
|
||||
}
|
||||
|
||||
void Transform(const mat3x4& transform) {
|
||||
ZoneScoped;
|
||||
DEBUG_ASSERT(IsAxisAligned(transform), userErr,
|
||||
"transform must be axis-aligned");
|
||||
for_each(autoPolicy(nodeBBox_.size()), countAt(0),
|
||||
countAt(nodeBBox_.size()), [&transform, this](size_t i) {
|
||||
nodeBBox_[i] = nodeBBox_[i].Transform(transform);
|
||||
});
|
||||
}
|
||||
|
||||
template <const bool selfCollision = false, typename F, typename Recorder>
|
||||
void Collisions(Recorder& recorder, std::optional<mat3x4> transform, F f,
|
||||
int n, bool parallel = true) const {
|
||||
void Collisions(Recorder& recorder, F f, int n, bool parallel = true) const {
|
||||
ZoneScoped;
|
||||
using collider_internal::FindCollision;
|
||||
if (internalChildren_.empty()) return;
|
||||
if (transform) {
|
||||
for_each_n(
|
||||
parallel ? autoPolicy(n, collider_internal::kSequentialThreshold)
|
||||
: ExecutionPolicy::Seq,
|
||||
countAt(0), n,
|
||||
FindCollision<decltype(f), selfCollision, true, Recorder>{
|
||||
f, nodeBBox_, internalChildren_, recorder, transform.value()});
|
||||
} else {
|
||||
for_each_n(parallel
|
||||
? autoPolicy(n, collider_internal::kSequentialThreshold)
|
||||
: ExecutionPolicy::Seq,
|
||||
countAt(0), n,
|
||||
FindCollision<decltype(f), selfCollision, false, Recorder>{
|
||||
f, nodeBBox_, internalChildren_, recorder, la::identity});
|
||||
}
|
||||
for_each_n(parallel ? autoPolicy(n, collider_internal::kSequentialThreshold)
|
||||
: ExecutionPolicy::Seq,
|
||||
countAt(0), n,
|
||||
FindCollision<decltype(f), selfCollision, Recorder>{
|
||||
f, nodeBBox_, internalChildren_, recorder});
|
||||
}
|
||||
|
||||
// This function iterates over queriesIn and calls recorder.record(queryIdx,
|
||||
@@ -332,12 +328,10 @@ class Collider {
|
||||
//
|
||||
// If thread local storage is not needed, use SimpleRecorder.
|
||||
template <const bool selfCollision = false, typename T, typename Recorder>
|
||||
void Collisions(Recorder& recorder, std::optional<mat3x4> transform,
|
||||
const VecView<const T>& queriesIn,
|
||||
void Collisions(Recorder& recorder, const VecView<const T>& queriesIn,
|
||||
bool parallel = true) const {
|
||||
auto f = [queriesIn](const int i) { return queriesIn[i]; };
|
||||
Collisions<selfCollision>(recorder, transform, f, queriesIn.size(),
|
||||
parallel);
|
||||
Collisions<selfCollision>(recorder, f, queriesIn.size(), parallel);
|
||||
}
|
||||
|
||||
static uint32_t MortonCode(vec3 position, Box bBox) {
|
||||
@@ -350,6 +344,17 @@ class Collider {
|
||||
return x * 4 + y * 2 + z;
|
||||
}
|
||||
|
||||
static bool IsAxisAligned(const mat3x4& transform) {
|
||||
for (int row : {0, 1, 2}) {
|
||||
int count = 0;
|
||||
for (int col : {0, 1, 2}) {
|
||||
if (transform[col][row] == 0.0) ++count;
|
||||
}
|
||||
if (count != 2) return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private:
|
||||
Vec<Box> nodeBBox_;
|
||||
Vec<int> nodeParent_;
|
||||
|
||||
+1
-1
@@ -274,7 +274,7 @@ Polygons Manifold::Impl::Slice(double height) const {
|
||||
};
|
||||
|
||||
auto recorder = MakeSimpleRecorder(recordCollision);
|
||||
collider_->Collisions<false>(recorder, query.cview(), false);
|
||||
collider_.Collisions<false>(recorder, query.cview(), false);
|
||||
|
||||
Polygons polys;
|
||||
while (!tris.empty()) {
|
||||
|
||||
+12
-11
@@ -601,7 +601,7 @@ void Manifold::Impl::MakeEmpty(Error status) {
|
||||
faceNormal_.clear();
|
||||
halfedgeTangent_.clear();
|
||||
meshRelation_ = MeshRelationD();
|
||||
collider_ = std::make_shared<LazyCollider>(LazyCollider::Empty());
|
||||
collider_ = {};
|
||||
status_ = status;
|
||||
}
|
||||
|
||||
@@ -683,16 +683,17 @@ Manifold::Impl Manifold::Impl::Transform(const mat3x4& transform_) const {
|
||||
// Maximum of inherited epsilon loss and translational epsilon loss.
|
||||
result.SetEpsilon(result.epsilon_);
|
||||
|
||||
if (LazyCollider::IsAxisAligned(transform_)) {
|
||||
result.collider_ = std::make_shared<LazyCollider>(collider_, transform_);
|
||||
} else if (!result.IsEmpty()) {
|
||||
Vec<Box> faceBox;
|
||||
Vec<uint32_t> faceMorton;
|
||||
result.GetFaceBoxMorton(faceBox, faceMorton);
|
||||
result.collider_ =
|
||||
std::make_shared<LazyCollider>(collider_, std::move(faceBox));
|
||||
} else {
|
||||
result.collider_ = std::make_shared<LazyCollider>(LazyCollider::Empty());
|
||||
if (!result.IsEmpty()) {
|
||||
if (Collider::IsAxisAligned(transform_)) {
|
||||
result.collider_ = collider_;
|
||||
result.collider_.Transform(transform_);
|
||||
} else if (!result.IsEmpty()) {
|
||||
result.collider_ = collider_;
|
||||
Vec<Box> faceBox;
|
||||
Vec<uint32_t> faceMorton;
|
||||
result.GetFaceBoxMorton(faceBox, faceMorton);
|
||||
result.collider_.UpdateBoxes(faceBox);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
+3
-3
@@ -15,7 +15,7 @@
|
||||
#pragma once
|
||||
#include <map>
|
||||
|
||||
#include "lazy_collider.h"
|
||||
#include "collider.h"
|
||||
#include "manifold/common.h"
|
||||
#include "manifold/manifold.h"
|
||||
#include "shared.h"
|
||||
@@ -52,12 +52,12 @@ struct Manifold::Impl {
|
||||
Vec<vec3> faceNormal_;
|
||||
Vec<vec4> halfedgeTangent_;
|
||||
MeshRelationD meshRelation_;
|
||||
std::shared_ptr<LazyCollider> collider_ = std::make_shared<LazyCollider>();
|
||||
Collider collider_;
|
||||
|
||||
static std::atomic<uint32_t> meshIDCounter_;
|
||||
static uint32_t ReserveIDs(uint32_t);
|
||||
|
||||
Impl() : collider_{std::make_shared<LazyCollider>(LazyCollider::Empty())} {};
|
||||
Impl() {};
|
||||
enum class Shape { Tetrahedron, Cube, Octahedron };
|
||||
Impl(Shape, const mat3x4 = la::identity);
|
||||
|
||||
|
||||
@@ -1,133 +0,0 @@
|
||||
// Copyright 2025 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 "lazy_collider.h"
|
||||
|
||||
namespace manifold {
|
||||
LazyCollider::LazyCollider() = default;
|
||||
|
||||
LazyCollider::LazyCollider(LeafData&& leafData)
|
||||
: leafData_(std::move(leafData)) {}
|
||||
|
||||
LazyCollider::LazyCollider(std::shared_ptr<const LazyCollider> base,
|
||||
const mat3x4& transform) {
|
||||
DEBUG_ASSERT(base != nullptr, logicErr, "invalid base ptr");
|
||||
// keeps as an optimization
|
||||
mat3x4 composed = transform;
|
||||
// attempt to unwrap one level, if the base is also a transformed collider
|
||||
// we do not unwrap if the base has updatedLeafBox
|
||||
std::lock_guard<std::mutex> lock(base->mutex_);
|
||||
if (base->base_ && !base->base_->updatedLeafBox.has_value()) {
|
||||
composed = composed * Mat4(base->base_->transform);
|
||||
base = base->base_->base;
|
||||
}
|
||||
base_ = Base{base, std::nullopt, composed};
|
||||
}
|
||||
|
||||
LazyCollider::LazyCollider(std::shared_ptr<const LazyCollider> base,
|
||||
std::optional<Vec<Box>> updatedLeafBox) {
|
||||
DEBUG_ASSERT(base != nullptr, logicErr, "invalid base ptr");
|
||||
// attempt to unwrap base
|
||||
// there are two cases:
|
||||
// 1. base contains updatedLeafBox, which means that its points to the root
|
||||
// 2. base is a transformed node, which either points to the root or points to
|
||||
// a node with updatedLeafBox, which is case 1
|
||||
// For case 2, the max depth is 2, and in any case we ignore transforms
|
||||
std::lock_guard<std::mutex> lock(base->mutex_);
|
||||
if (base->base_) base = base->base_->base;
|
||||
if (base->base_) base = base->base_->base;
|
||||
base_ = Base{base, std::move(updatedLeafBox), la::identity};
|
||||
}
|
||||
|
||||
LazyCollider::LazyCollider(const LazyCollider& other) {
|
||||
std::lock_guard<std::mutex> lock(other.mutex_);
|
||||
built_ = other.built_;
|
||||
leafData_ = other.leafData_;
|
||||
base_ = other.base_;
|
||||
}
|
||||
|
||||
LazyCollider::LazyCollider(LazyCollider&& other) noexcept {
|
||||
std::lock_guard<std::mutex> lock(other.mutex_);
|
||||
built_ = std::move(other.built_);
|
||||
leafData_ = std::move(other.leafData_);
|
||||
base_ = std::move(other.base_);
|
||||
}
|
||||
|
||||
LazyCollider& LazyCollider::operator=(const LazyCollider& other) {
|
||||
if (this == &other) return *this;
|
||||
std::scoped_lock lock(mutex_, other.mutex_);
|
||||
built_ = other.built_;
|
||||
leafData_ = other.leafData_;
|
||||
base_ = other.base_;
|
||||
return *this;
|
||||
}
|
||||
|
||||
LazyCollider& LazyCollider::operator=(LazyCollider&& other) noexcept {
|
||||
if (this == &other) return *this;
|
||||
std::scoped_lock lock(mutex_, other.mutex_);
|
||||
built_ = std::move(other.built_);
|
||||
leafData_ = std::move(other.leafData_);
|
||||
base_ = std::move(other.base_);
|
||||
return *this;
|
||||
}
|
||||
|
||||
bool LazyCollider::IsBuilt() const {
|
||||
std::lock_guard<std::mutex> lock(mutex_);
|
||||
return built_.has_value();
|
||||
}
|
||||
|
||||
bool LazyCollider::IsAxisAligned(const mat3x4& transform) {
|
||||
for (int row : {0, 1, 2}) {
|
||||
int count = 0;
|
||||
for (int col : {0, 1, 2}) {
|
||||
if (transform[col][row] == 0.0) ++count;
|
||||
}
|
||||
if (count != 2) return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
const LazyCollider::Built& LazyCollider::EnsureBuilt() const {
|
||||
std::lock_guard<std::mutex> lock(mutex_);
|
||||
if (built_) return built_.value();
|
||||
|
||||
if (base_) {
|
||||
const Built& baseBuilt = base_->base->EnsureBuilt();
|
||||
// propagate transform when collider was built after construct
|
||||
mat3x4 composed = base_->transform;
|
||||
if (baseBuilt.transform) composed = composed * Mat4(*baseBuilt.transform);
|
||||
built_ = {baseBuilt.collider, composed};
|
||||
if (base_->updatedLeafBox) {
|
||||
auto leafBox = std::move(base_->updatedLeafBox.value());
|
||||
base_ = std::nullopt;
|
||||
// need a unique collider, it may be possible that the base reference to
|
||||
// the collider was dropped when we reset the base_ pointer
|
||||
if (built_->collider.use_count() != 1)
|
||||
built_->collider = std::make_shared<Collider>(*built_->collider);
|
||||
built_->collider->UpdateBoxes(leafBox);
|
||||
// note that after updating the boxes, we don't need transform
|
||||
built_->transform = std::nullopt;
|
||||
}
|
||||
base_ = std::nullopt;
|
||||
} else {
|
||||
DEBUG_ASSERT(leafData_.has_value(), logicErr, "uninitialized collider");
|
||||
built_ = {
|
||||
std::make_shared<Collider>(leafData_->leafBox, leafData_->leafMorton),
|
||||
std::nullopt};
|
||||
leafData_ = std::nullopt;
|
||||
}
|
||||
return built_.value();
|
||||
}
|
||||
|
||||
} // namespace manifold
|
||||
@@ -1,76 +0,0 @@
|
||||
// Copyright 2025 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.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <memory>
|
||||
#include <mutex>
|
||||
|
||||
#include "collider.h"
|
||||
|
||||
namespace manifold {
|
||||
|
||||
class LazyCollider {
|
||||
public:
|
||||
struct LeafData {
|
||||
Vec<Box> leafBox;
|
||||
Vec<uint32_t> leafMorton;
|
||||
};
|
||||
|
||||
struct Built {
|
||||
std::shared_ptr<Collider> collider;
|
||||
std::optional<mat3x4> transform;
|
||||
};
|
||||
|
||||
struct Base {
|
||||
std::shared_ptr<const LazyCollider> base;
|
||||
std::optional<Vec<Box>> updatedLeafBox;
|
||||
mat3x4 transform;
|
||||
};
|
||||
|
||||
static LazyCollider Empty() { return LazyCollider(LeafData()); }
|
||||
LazyCollider();
|
||||
LazyCollider(LeafData&& leafData);
|
||||
LazyCollider(std::shared_ptr<const LazyCollider> base,
|
||||
const mat3x4& transform);
|
||||
LazyCollider(std::shared_ptr<const LazyCollider> base,
|
||||
std::optional<Vec<Box>> updatedLeafBox);
|
||||
LazyCollider(const LazyCollider& other);
|
||||
LazyCollider(LazyCollider&& other) noexcept;
|
||||
LazyCollider& operator=(const LazyCollider& other);
|
||||
LazyCollider& operator=(LazyCollider&& other) noexcept;
|
||||
~LazyCollider() = default;
|
||||
|
||||
bool IsBuilt() const;
|
||||
|
||||
template <const bool selfCollision = false, typename Recorder,
|
||||
typename... Args>
|
||||
void Collisions(Recorder& recorder, Args... args) const {
|
||||
const Built& built = EnsureBuilt();
|
||||
built.collider->Collisions<false>(recorder, built.transform, args...);
|
||||
}
|
||||
|
||||
static bool IsAxisAligned(const mat3x4& transform);
|
||||
|
||||
private:
|
||||
const Built& EnsureBuilt() const;
|
||||
|
||||
mutable std::mutex mutex_;
|
||||
|
||||
mutable std::optional<Built> built_;
|
||||
mutable std::optional<LeafData> leafData_;
|
||||
mutable std::optional<Base> base_;
|
||||
};
|
||||
|
||||
} // namespace manifold
|
||||
+2
-2
@@ -185,7 +185,7 @@ bool Manifold::Impl::IsSelfIntersecting() const {
|
||||
};
|
||||
|
||||
auto recorder = MakeSimpleRecorder(f);
|
||||
collider_->Collisions<true>(recorder, faceBox.cview());
|
||||
collider_.Collisions<true>(recorder, faceBox.cview());
|
||||
|
||||
return intersecting.load();
|
||||
}
|
||||
@@ -455,7 +455,7 @@ double Manifold::Impl::MinGap(const Manifold::Impl& other,
|
||||
});
|
||||
|
||||
MinDistanceRecorder recorder{*this, other};
|
||||
collider_->Collisions<false>(recorder, faceBoxOther.cview(), false);
|
||||
collider_.Collisions<false>(recorder, faceBoxOther.cview(), false);
|
||||
double minDistanceSquared =
|
||||
std::min(recorder.get(), searchLength * searchLength);
|
||||
return sqrt(minDistanceSquared);
|
||||
|
||||
+8
-7
@@ -159,7 +159,7 @@ bool MergeMeshGLP(MeshGLP<Precision, I>& mesh) {
|
||||
return uf.unite(openVerts[a], openVerts[b]);
|
||||
};
|
||||
auto recorder = MakeSimpleRecorder(f);
|
||||
collider.Collisions<true>(recorder, std::nullopt, vertBox.cview(), false);
|
||||
collider.Collisions<true>(recorder, vertBox.cview(), false);
|
||||
|
||||
for (size_t i = 0; i < mesh.mergeFromVert.size(); ++i) {
|
||||
uf.unite(static_cast<int>(mesh.mergeFromVert[i]),
|
||||
@@ -189,20 +189,21 @@ namespace manifold {
|
||||
*/
|
||||
void Manifold::Impl::SortGeometry() {
|
||||
if (halfedge_.size() == 0) {
|
||||
collider_ = std::make_shared<LazyCollider>(LazyCollider::Empty());
|
||||
collider_ = {};
|
||||
return;
|
||||
}
|
||||
|
||||
halfedge_.MakeUnique();
|
||||
SortVerts();
|
||||
LazyCollider::LeafData leafData;
|
||||
GetFaceBoxMorton(leafData.leafBox, leafData.leafMorton);
|
||||
SortFaces(leafData.leafBox, leafData.leafMorton);
|
||||
Vec<Box> faceBox;
|
||||
Vec<uint32_t> faceMorton;
|
||||
GetFaceBoxMorton(faceBox, faceMorton);
|
||||
SortFaces(faceBox, faceMorton);
|
||||
if (halfedge_.size() == 0) {
|
||||
collider_ = std::make_shared<LazyCollider>(LazyCollider::Empty());
|
||||
collider_ = {};
|
||||
return;
|
||||
}
|
||||
collider_ = std::make_shared<LazyCollider>(std::move(leafData));
|
||||
collider_ = Collider(faceBox, faceMorton);
|
||||
CompactProps();
|
||||
|
||||
DEBUG_ASSERT(halfedge_.size() % 6 == 0, topologyErr,
|
||||
|
||||
Reference in New Issue
Block a user