Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add Merge function #394

Merged
merged 25 commits into from
Apr 10, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 3 additions & 4 deletions .github/workflows/manifold.yml
Original file line number Diff line number Diff line change
Expand Up @@ -193,7 +193,7 @@ jobs:
steps:
- name: Install common dependencies
run: |
brew install pkg-config
brew install pkg-config assimp
pip install trimesh
- name: Install OpenMP
if: matrix.parallel_backend == 'OMP'
Expand All @@ -209,7 +209,7 @@ jobs:
run: |
mkdir build
cd build
cmake -DCMAKE_BUILD_TYPE=Release -DBUILD_SHARED_LIBS=ON -DMANIFOLD_DEBUG=ON -DMANIFOLD_PAR=${{matrix.parallel_backend}} .. && make
cmake -DCMAKE_BUILD_TYPE=Release -DBUILD_SHARED_LIBS=ON -DMANIFOLD_DEBUG=ON -DMANIFOLD_EXPORT=ON -DMANIFOLD_PAR=${{matrix.parallel_backend}} .. && make
- name: Test
run: |
cd build/test
Expand All @@ -227,5 +227,4 @@ jobs:
with:
submodules: recursive
- uses: cachix/install-nix-action@v15
- run: |
nix build -L '.?submodules=1#manifold-${{matrix.variant}}'
- run: nix build -L '.?submodules=1#manifold-${{matrix.variant}}'
2 changes: 1 addition & 1 deletion .vscode/settings.json
Original file line number Diff line number Diff line change
Expand Up @@ -104,7 +104,7 @@
"editor.formatOnSave": true,
"cmake.configureArgs": [
"-DBUILD_SHARED_LIBS=ON",
"-DMANIFOLD_EXPORT=OFF",
"-DMANIFOLD_EXPORT=ON",
"-DMANIFOLD_DEBUG=ON",
"-DMANIFOLD_USE_CUDA=OFF",
"-DMANIFOLD_PAR=NONE",
Expand Down
6 changes: 4 additions & 2 deletions meshIO/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -14,15 +14,17 @@

project(meshIO)

find_package(assimp REQUIRED)

add_library(${PROJECT_NAME} src/meshIO.cpp)

target_include_directories(${PROJECT_NAME}
PUBLIC ${PROJECT_SOURCE_DIR}/include
)

target_link_libraries(${PROJECT_NAME}
PUBLIC utilities
PRIVATE assimp
PUBLIC manifold
PRIVATE assimp::assimp
)

target_compile_options(${PROJECT_NAME} PRIVATE ${MANIFOLD_FLAGS})
Expand Down
2 changes: 1 addition & 1 deletion meshIO/include/meshIO.h
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
#pragma once
#include <string>

#include "public.h"
#include "manifold.h"

namespace manifold {

Expand Down
5 changes: 1 addition & 4 deletions src/collider/include/collider.h
Original file line number Diff line number Diff line change
Expand Up @@ -24,12 +24,9 @@ class Collider {
public:
Collider() {}
Collider(const VecDH<Box>& leafBB, const VecDH<uint32_t>& leafMorton);
// Aborts and returns false if transform is not axis aligned.
bool Transform(glm::mat4x3);
void UpdateBoxes(const VecDH<Box>& leafBB);
// Collisions returns a sparse result, where i is the query index and j is
// the leaf index where their bounding boxes overlap.
template <typename T>
template <const bool selfCollision = false, typename T>
SparseIndices Collisions(const VecDH<T>& queriesIn) const;

private:
Expand Down
44 changes: 26 additions & 18 deletions src/collider/src/collider.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -157,7 +157,7 @@ struct CreateRadixTree {
}
};

template <typename T, const bool allocateOnly>
template <typename T, const bool allocateOnly, const bool selfCollision>
struct FindCollisions {
thrust::pair<int*, int*> queryTri_;
int* counts;
Expand All @@ -172,12 +172,15 @@ struct FindCollisions {

bool overlaps = nodeBBox_[node].DoesOverlap(queryObj);
if (overlaps && IsLeaf(node)) {
if (allocateOnly) {
count++;
} else {
int pos = count++;
queryTri_.first[pos] = queryIdx;
queryTri_.second[pos] = Node2Leaf(node);
const int leafIdx = Node2Leaf(node);
if (!selfCollision || leafIdx != queryIdx) {
if (allocateOnly) {
count++;
} else {
int pos = count++;
queryTri_.first[pos] = queryIdx;
queryTri_.second[pos] = leafIdx;
}
}
}
return overlaps && IsInternal(node); // Should traverse into node
Expand Down Expand Up @@ -267,9 +270,11 @@ Collider::Collider(const VecDH<Box>& 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.
* 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 <typename T>
template <const bool selfCollision, typename T>
SparseIndices Collider::Collisions(const VecDH<T>& queriesIn) const {
// note that the length is 1 larger than the number of queries so the last
// element can store the sum when using exclusive scan
Expand All @@ -278,17 +283,17 @@ SparseIndices Collider::Collisions(const VecDH<T>& queriesIn) const {
// compute the number of collisions to determine the size for allocation and
// offset, this avoids the need for atomic
for_each_n(policy, zip(queriesIn.cbegin(), countAt(0)), queriesIn.size(),
FindCollisions<T, true>(
FindCollisions<T, true, selfCollision>(
{thrust::pair<int*, int*>(nullptr, nullptr), counts.ptrD(),
nodeBBox_.ptrD(), internalChildren_.ptrD()}));
// compute start index for each query and total count
exclusive_scan(policy, counts.begin(), counts.end(), counts.begin());
SparseIndices queryTri(counts.back());
// actually recording collisions
for_each_n(
policy, zip(queriesIn.cbegin(), countAt(0)), queriesIn.size(),
FindCollisions<T, false>({queryTri.ptrDpq(), counts.ptrD(),
nodeBBox_.ptrD(), internalChildren_.ptrD()}));
for_each_n(policy, zip(queriesIn.cbegin(), countAt(0)), queriesIn.size(),
FindCollisions<T, false, selfCollision>(
{queryTri.ptrDpq(), counts.ptrD(), nodeBBox_.ptrD(),
internalChildren_.ptrD()}));
return queryTri;
}

Expand All @@ -308,8 +313,8 @@ void Collider::UpdateBoxes(const VecDH<Box>& leafBB) {
// kernel over leaves to save internal Boxes
for_each_n(
policy, countAt(0), NumLeaves(),
BuildInternalBoxes({nodeBBox_.ptrD(), counter.ptrD(), nodeParent_.ptrD(),
internalChildren_.ptrD()}));
BuildInternalBoxes({nodeBBox_.ptrD(), counter.ptrD(), nodeParent_.cptrD(),
internalChildren_.cptrD()}));
}

/**
Expand All @@ -332,9 +337,12 @@ bool Collider::Transform(glm::mat4x3 transform) {
return axisAligned;
}

template SparseIndices Collider::Collisions<Box>(const VecDH<Box>&) const;
template SparseIndices Collider::Collisions<true, Box>(const VecDH<Box>&) const;

template SparseIndices Collider::Collisions<false, Box>(
const VecDH<Box>&) const;

template SparseIndices Collider::Collisions<glm::vec3>(
template SparseIndices Collider::Collisions<false, glm::vec3>(
const VecDH<glm::vec3>&) const;

} // namespace manifold
74 changes: 74 additions & 0 deletions src/manifold/include/manifold.h
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,80 @@ ExecutionParams& ManifoldParams();
class CsgNode;
class CsgLeafNode;

/** @ingroup Connections
* @{
*/

/**
* An alternative to Mesh for output suitable for pushing into graphics
* libraries directly. This may not be manifold since the verts are duplicated
* along property boundaries that do not match. The additional merge vectors
* store this missing information, allowing the manifold to be reconstructed.
*/
struct MeshGL {
/// Number of property vertices
uint32_t NumVert() const { return vertProperties.size() / numProp; };
/// Number of triangles
uint32_t NumTri() const { return triVerts.size() / 3; };

/// Number of properties per vertex, always >= 3.
uint32_t numProp = 3;
/// Flat, GL-style interleaved list of all vertex properties: propVal =
/// vertProperties[vert * numProp + propIdx]. The first three properties are
/// always the position x, y, z.
std::vector<float> vertProperties;
/// The vertex indices of the three triangle corners in CCW (from the outside)
/// order, for each triangle.
std::vector<uint32_t> triVerts;
/// Optional: A list of only the vertex indicies that need to be merged to
/// reconstruct the manifold.
std::vector<uint32_t> mergeFromVert;
/// Optional: The same length as mergeFromVert, and the corresponding value
/// contains the vertex to merge with. It will have an identical position, but
/// the other properties may differ.
std::vector<uint32_t> mergeToVert;
/// Optional: Indicates runs of triangles that correspond to a particular
/// input mesh instance. The runs encompass all of triVerts and are sorted
/// by runOriginalID. Run i begins at triVerts[runIndex[i]] and ends at
/// triVerts[runIndex[i+1]]. All runIndex values are divisible by 3.
std::vector<uint32_t> runIndex;
/// Optional: The OriginalID of the mesh this triangle run came from. This ID
/// is ideal for reapplying materials to the output mesh. Multiple runs may
/// have the same ID, e.g. representing different copies of the same input
/// mesh. If you create an input MeshGL that you want to be able to reference
/// as one or more originals, be sure to set unique values from ReserveIDs().
std::vector<uint32_t> runOriginalID;
/// Optional: For each run, a 3x4 transform is stored representing how the
/// corresponding original mesh was transformed to create this triangle run.
/// This matrix is stored in column-major order and the length of the overall
/// vector is 12 * runOriginalID.size().
std::vector<float> runTransform;
/// Optional: Length NumTri, contains an ID of the source face this triangle
/// comes from. When auto-generated, this ID will be a triangle index into the
/// original mesh. All neighboring coplanar triangles from that input mesh
/// will refer to a single triangle of that group as the faceID. When
/// supplying faceIDs, ensure that triangles with the same ID are in fact
/// coplanar and have consistent properties (within some tolerance) or the
/// output will be surprising.
std::vector<uint32_t> faceID;
/// Optional: The X-Y-Z-W weighted tangent vectors for smooth Refine(). If
/// non-empty, must be exactly four times as long as Mesh.triVerts. Indexed
/// as 4 * (3 * tri + i) + j, i < 3, j < 4, representing the tangent value
/// Mesh.triVerts[tri][i] along the CCW edge. If empty, mesh is faceted.
std::vector<float> halfedgeTangent;
/// The absolute precision of the vertex positions, based on accrued rounding
/// errors. When creating a Manifold, the precision used will be the maximum
/// of this and a baseline precision from the size of the bounding box. Any
/// edge shorter than precision may be collapsed.
float precision = 0;

MeshGL() = default;
MeshGL(const Mesh& mesh);

bool Merge();
};
/** @} */

/** @defgroup Core
* @brief The central classes of the library
* @{
Expand Down
14 changes: 7 additions & 7 deletions src/manifold/src/impl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -242,20 +242,17 @@ struct CoplanarEdge {
const int edgeIdx = thrust::get<2>(inOut);

const Halfedge edge = halfedge[edgeIdx];
if (!edge.IsForward()) return;
Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This fixed a bug I found - I was not deduplicating property verts correctly before.

const Halfedge pair = halfedge[edge.pairedHalfedge];

if (triRef[edge.face].meshID != triRef[pair.face].meshID) return;

const glm::vec3 base = vertPos[edge.startVert];

const int baseNum = edgeIdx - 3 * edge.face;
const int jointNum = edge.pairedHalfedge - 3 * pair.face;
const int edgeNum = baseNum == 0 ? 2 : baseNum - 1;
const int pairNum = jointNum == 0 ? 2 : jointNum - 1;

if (numProp > 0) {
const int prop0 = triProp[edge.face][baseNum];
const int prop1 = triProp[edge.face][edgeNum];
const int prop1 = triProp[pair.face][jointNum == 2 ? 0 : jointNum + 1];
bool propEqual = true;
for (int p = 0; p < numProp; ++p) {
if (glm::abs(prop[numProp * prop0 + p] - prop[numProp * prop1 + p]) >
Expand All @@ -270,6 +267,10 @@ struct CoplanarEdge {
}
}

if (!edge.IsForward()) return;

const int edgeNum = baseNum == 0 ? 2 : baseNum - 1;
const int pairNum = jointNum == 0 ? 2 : jointNum - 1;
const glm::vec3 jointVec = vertPos[pair.startVert] - base;
const glm::vec3 edgeVec =
vertPos[halfedge[3 * edge.face + edgeNum].startVert] - base;
Expand Down Expand Up @@ -778,8 +779,7 @@ Manifold::Impl Manifold::Impl::Transform(const glm::mat4x3& transform_) const {
* by the optional input.
*/
void Manifold::Impl::SetPrecision(float minPrecision) {
precision_ = glm::max(minPrecision, kTolerance * bBox_.Scale());
if (!glm::isfinite(precision_)) precision_ = -1;
precision_ = MaxPrecision(minPrecision, bBox_);
}

/**
Expand Down
6 changes: 6 additions & 0 deletions src/manifold/src/shared.h
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,12 @@ __host__ __device__ inline glm::vec3 SafeNormalize(glm::vec3 v) {
return glm::isfinite(v.x) ? v : glm::vec3(0);
}

__host__ __device__ inline float MaxPrecision(float minPrecision,
const Box& bBox) {
float precision = glm::max(minPrecision, kTolerance * bBox.Scale());
return glm::isfinite(precision) ? precision : -1;
}

__host__ __device__ inline int NextHalfedge(int current) {
++current;
if (current % 3 == 0) current -= 3;
Expand Down
Loading