revert lazy collider (#1569)

This commit is contained in:
pca006132
2026-03-07 15:26:44 +08:00
committed by GitHub
parent d7c19d7b30
commit 5e434a07d6
10 changed files with 58 additions and 262 deletions
-2
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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);
-133
View File
@@ -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
-76
View File
@@ -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
View File
@@ -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
View File
@@ -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,