Skip to content

Commit

Permalink
Print better manifold errors and avoid crash on non manifold csg input.
Browse files Browse the repository at this point in the history
* Manifold does not have a snap property.
* Tolerance means simplification amount.
* CSG snap has been removed
* Add better error messages.
  • Loading branch information
fire committed Dec 3, 2024
1 parent 0f20e67 commit b8d042e
Show file tree
Hide file tree
Showing 2 changed files with 49 additions and 11 deletions.
56 changes: 47 additions & 9 deletions modules/csg/csg_shape.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -232,21 +232,53 @@ static void _unpack_manifold(
r_mesh_merge->_regen_face_aabbs();
}

// Errors matching `thirdparty/manifold/include/manifold/manifold.h`.
static std::string manifold_error_to_string(const manifold::Manifold::Error &p_error) {
switch (p_error) {
case manifold::Manifold::Error::NoError:
return "No Error";
case manifold::Manifold::Error::NonFiniteVertex:
return "Non Finite Vertex";
case manifold::Manifold::Error::NotManifold:
return "Not Manifold";
case manifold::Manifold::Error::VertexOutOfBounds:
return "Vertex Out Of Bounds";
case manifold::Manifold::Error::PropertiesWrongLength:
return "Properties Wrong Length";
case manifold::Manifold::Error::MissingPositionProperties:
return "Missing Position Properties";
case manifold::Manifold::Error::MergeVectorsDifferentLengths:
return "Merge Vectors Different Lengths";
case manifold::Manifold::Error::MergeIndexOutOfBounds:
return "Merge Index Out Of Bounds";
case manifold::Manifold::Error::TransformWrongLength:
return "Transform Wrong Length";
case manifold::Manifold::Error::RunIndexWrongLength:
return "Run Index Wrong Length";
case manifold::Manifold::Error::FaceIDWrongLength:
return "Face ID Wrong Length";
case manifold::Manifold::Error::InvalidConstruction:
return "Invalid Construction";
default:
return "Unknown Error";
};
}

static void _pack_manifold(
const CSGBrush *const p_mesh_merge,
manifold::Manifold &r_manifold,
HashMap<int32_t, Ref<Material>> &p_mesh_materials,
float p_snap) {
float p_snap,
CSGShape3D *p_csg_shape) {
ERR_FAIL_NULL_MSG(p_mesh_merge, "p_mesh_merge is null");

ERR_FAIL_NULL_MSG(p_csg_shape, "p_shape is null");
HashMap<uint32_t, Vector<CSGBrush::Face>> faces_by_material;
for (int face_i = 0; face_i < p_mesh_merge->faces.size(); face_i++) {
const CSGBrush::Face &face = p_mesh_merge->faces[face_i];
faces_by_material[face.material].push_back(face);
}

manifold::MeshGL64 mesh;
mesh.tolerance = p_snap;
mesh.numProp = MANIFOLD_PROPERTY_MAX;
mesh.runOriginalID.reserve(faces_by_material.size());
mesh.runIndex.reserve(faces_by_material.size() + 1);
Expand Down Expand Up @@ -294,9 +326,15 @@ static void _pack_manifold(
ERR_FAIL_COND_MSG(mesh.vertProperties.size() % mesh.numProp != 0, "Invalid vertex properties size.");
mesh.Merge();
r_manifold = manifold::Manifold(mesh);
manifold::Manifold::Error err = r_manifold.Status();
if (err != manifold::Manifold::Error::NoError) {
print_error(String("Manifold creation from mesh failed:" + itos((int)err)));
manifold::Manifold::Error error = r_manifold.Status();
if (error == manifold::Manifold::Error::NoError) {
return;
}
if (p_csg_shape->get_owner()) {
NodePath child_path = p_csg_shape->get_owner()->get_path_to(p_csg_shape, true);
print_error(vformat("Child CSGShape3D manifold creation from mesh failed at %s: %s", child_path, manifold_error_to_string(error).c_str()));
} else {
print_error(vformat("Child CSGShape3D manifold creation from mesh failed: %s", manifold_error_to_string(error).c_str()));
}
}

Expand Down Expand Up @@ -330,7 +368,7 @@ CSGBrush *CSGShape3D::_get_brush() {
CSGBrush *n = _build_brush();
HashMap<int32_t, Ref<Material>> mesh_materials;
manifold::Manifold root_manifold;
_pack_manifold(n, root_manifold, mesh_materials, get_snap());
_pack_manifold(n, root_manifold, mesh_materials, get_snap(), this);
manifold::OpType current_op = ManifoldOperation::convert_csg_op(get_operation());
std::vector<manifold::Manifold> manifolds;
manifolds.push_back(root_manifold);
Expand All @@ -346,7 +384,7 @@ CSGBrush *CSGShape3D::_get_brush() {
CSGBrush transformed_brush;
transformed_brush.copy_from(*child_brush, child->get_transform());
manifold::Manifold child_manifold;
_pack_manifold(&transformed_brush, child_manifold, mesh_materials, get_snap());
_pack_manifold(&transformed_brush, child_manifold, mesh_materials, get_snap(), child);
manifold::OpType child_operation = ManifoldOperation::convert_csg_op(child->get_operation());
if (child_operation != current_op) {
manifold::Manifold result = manifold::Manifold::BatchBoolean(manifolds, current_op);
Expand Down Expand Up @@ -864,7 +902,7 @@ void CSGShape3D::_bind_methods() {
ClassDB::bind_method(D_METHOD("bake_collision_shape"), &CSGShape3D::bake_collision_shape);

ADD_PROPERTY(PropertyInfo(Variant::INT, "operation", PROPERTY_HINT_ENUM, "Union,Intersection,Subtraction"), "set_operation", "get_operation");
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "snap", PROPERTY_HINT_RANGE, "0.000001,1,0.000001,suffix:m"), "set_snap", "get_snap");
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "snap", PROPERTY_HINT_RANGE, "0.000001,1,0.000001,suffix:m", PROPERTY_USAGE_NO_EDITOR), "set_snap", "get_snap");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "calculate_tangents"), "set_calculate_tangents", "is_calculating_tangents");

ADD_GROUP("Collision", "collision_");
Expand Down
4 changes: 2 additions & 2 deletions modules/csg/doc_classes/CSGShape3D.xml
Original file line number Diff line number Diff line change
Expand Up @@ -89,8 +89,8 @@
<member name="operation" type="int" setter="set_operation" getter="get_operation" enum="CSGShape3D.Operation" default="0">
The operation that is performed on this shape. This is ignored for the first CSG child node as the operation is between this node and the previous child of this nodes parent.
</member>
<member name="snap" type="float" setter="set_snap" getter="get_snap" default="0.001">
Snap makes the mesh vertices snap to a given distance so that the faces of two meshes can be perfectly aligned. A lower value results in greater precision but may be harder to adjust. The top-level CSG shape's snap value is used for the entire CSG tree.
<member name="snap" type="float" setter="set_snap" getter="get_snap" default="0.001" deprecated="Snap is not used by CSG anymore.">
CSG no longer uses snap and has been removed because snapping is not implemented in the backing CSG manifold library.
</member>
<member name="use_collision" type="bool" setter="set_use_collision" getter="is_using_collision" default="false">
Adds a collision shape to the physics engine for our CSG shape. This will always act like a static body. Note that the collision shape is still active even if the CSG shape itself is hidden. See also [member collision_mask] and [member collision_priority].
Expand Down

0 comments on commit b8d042e

Please sign in to comment.