Skip to content

Commit 4b91e98

Browse files
committed
Merge pull request #99959 from fire/vsk-csg-error-and-ctd
Print better manifold errors and avoid crash on non manifold csg input.
2 parents 7bff6c8 + 6cf1d3c commit 4b91e98

File tree

11 files changed

+443
-321
lines changed

11 files changed

+443
-321
lines changed

modules/csg/csg_shape.cpp

Lines changed: 125 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,9 @@
3030

3131
#include "csg_shape.h"
3232

33+
#ifdef DEV_ENABLED
34+
#include "core/io/json.h"
35+
#endif // DEV_ENABLED
3336
#include "core/math/geometry_2d.h"
3437

3538
#include <manifold/manifold.h>
@@ -142,6 +145,7 @@ bool CSGShape3D::is_root_shape() const {
142145
return !parent_shape;
143146
}
144147

148+
#ifndef DISABLE_DEPRECATED
145149
void CSGShape3D::set_snap(float p_snap) {
146150
if (snap == p_snap) {
147151
return;
@@ -154,6 +158,7 @@ void CSGShape3D::set_snap(float p_snap) {
154158
float CSGShape3D::get_snap() const {
155159
return snap;
156160
}
161+
#endif // DISABLE_DEPRECATED
157162

158163
void CSGShape3D::_make_dirty(bool p_parent_removing) {
159164
if ((p_parent_removing || is_root_shape()) && !dirty) {
@@ -232,21 +237,118 @@ static void _unpack_manifold(
232237
r_mesh_merge->_regen_face_aabbs();
233238
}
234239

240+
// Errors matching `thirdparty/manifold/include/manifold/manifold.h`.
241+
static String manifold_error_to_string(const manifold::Manifold::Error &p_error) {
242+
switch (p_error) {
243+
case manifold::Manifold::Error::NoError:
244+
return "No Error";
245+
case manifold::Manifold::Error::NonFiniteVertex:
246+
return "Non Finite Vertex";
247+
case manifold::Manifold::Error::NotManifold:
248+
return "Not Manifold";
249+
case manifold::Manifold::Error::VertexOutOfBounds:
250+
return "Vertex Out Of Bounds";
251+
case manifold::Manifold::Error::PropertiesWrongLength:
252+
return "Properties Wrong Length";
253+
case manifold::Manifold::Error::MissingPositionProperties:
254+
return "Missing Position Properties";
255+
case manifold::Manifold::Error::MergeVectorsDifferentLengths:
256+
return "Merge Vectors Different Lengths";
257+
case manifold::Manifold::Error::MergeIndexOutOfBounds:
258+
return "Merge Index Out Of Bounds";
259+
case manifold::Manifold::Error::TransformWrongLength:
260+
return "Transform Wrong Length";
261+
case manifold::Manifold::Error::RunIndexWrongLength:
262+
return "Run Index Wrong Length";
263+
case manifold::Manifold::Error::FaceIDWrongLength:
264+
return "Face ID Wrong Length";
265+
case manifold::Manifold::Error::InvalidConstruction:
266+
return "Invalid Construction";
267+
default:
268+
return "Unknown Error";
269+
}
270+
}
271+
272+
#ifdef DEV_ENABLED
273+
static String _export_meshgl_as_json(const manifold::MeshGL64 &p_mesh) {
274+
Dictionary mesh_dict;
275+
mesh_dict["numProp"] = p_mesh.numProp;
276+
277+
Array vert_properties;
278+
for (const double &val : p_mesh.vertProperties) {
279+
vert_properties.append(val);
280+
}
281+
mesh_dict["vertProperties"] = vert_properties;
282+
283+
Array tri_verts;
284+
for (const uint64_t &val : p_mesh.triVerts) {
285+
tri_verts.append(val);
286+
}
287+
mesh_dict["triVerts"] = tri_verts;
288+
289+
Array merge_from_vert;
290+
for (const uint64_t &val : p_mesh.mergeFromVert) {
291+
merge_from_vert.append(val);
292+
}
293+
mesh_dict["mergeFromVert"] = merge_from_vert;
294+
295+
Array merge_to_vert;
296+
for (const uint64_t &val : p_mesh.mergeToVert) {
297+
merge_to_vert.append(val);
298+
}
299+
mesh_dict["mergeToVert"] = merge_to_vert;
300+
301+
Array run_index;
302+
for (const uint64_t &val : p_mesh.runIndex) {
303+
run_index.append(val);
304+
}
305+
mesh_dict["runIndex"] = run_index;
306+
307+
Array run_original_id;
308+
for (const uint32_t &val : p_mesh.runOriginalID) {
309+
run_original_id.append(val);
310+
}
311+
mesh_dict["runOriginalID"] = run_original_id;
312+
313+
Array run_transform;
314+
for (const double &val : p_mesh.runTransform) {
315+
run_transform.append(val);
316+
}
317+
mesh_dict["runTransform"] = run_transform;
318+
319+
Array face_id;
320+
for (const uint64_t &val : p_mesh.faceID) {
321+
face_id.append(val);
322+
}
323+
mesh_dict["faceID"] = face_id;
324+
325+
Array halfedge_tangent;
326+
for (const double &val : p_mesh.halfedgeTangent) {
327+
halfedge_tangent.append(val);
328+
}
329+
mesh_dict["halfedgeTangent"] = halfedge_tangent;
330+
331+
mesh_dict["tolerance"] = p_mesh.tolerance;
332+
333+
String json_string = JSON::stringify(mesh_dict);
334+
return json_string;
335+
}
336+
#endif // DEV_ENABLED
337+
235338
static void _pack_manifold(
236339
const CSGBrush *const p_mesh_merge,
237340
manifold::Manifold &r_manifold,
238341
HashMap<int32_t, Ref<Material>> &p_mesh_materials,
239-
float p_snap) {
342+
CSGShape3D *p_csg_shape) {
240343
ERR_FAIL_NULL_MSG(p_mesh_merge, "p_mesh_merge is null");
241-
344+
ERR_FAIL_NULL_MSG(p_csg_shape, "p_shape is null");
242345
HashMap<uint32_t, Vector<CSGBrush::Face>> faces_by_material;
243346
for (int face_i = 0; face_i < p_mesh_merge->faces.size(); face_i++) {
244347
const CSGBrush::Face &face = p_mesh_merge->faces[face_i];
245348
faces_by_material[face.material].push_back(face);
246349
}
247350

248351
manifold::MeshGL64 mesh;
249-
mesh.tolerance = p_snap;
250352
mesh.numProp = MANIFOLD_PROPERTY_MAX;
251353
mesh.runOriginalID.reserve(faces_by_material.size());
252354
mesh.runIndex.reserve(faces_by_material.size() + 1);
@@ -291,12 +393,22 @@ static void _pack_manifold(
291393
}
292394
// runIndex needs an explicit end value.
293395
mesh.runIndex.push_back(mesh.triVerts.size());
396+
mesh.tolerance = 2 * FLT_EPSILON;
294397
ERR_FAIL_COND_MSG(mesh.vertProperties.size() % mesh.numProp != 0, "Invalid vertex properties size.");
295398
mesh.Merge();
399+
#ifdef DEV_ENABLED
400+
print_verbose(_export_meshgl_as_json(mesh));
401+
#endif // DEV_ENABLED
296402
r_manifold = manifold::Manifold(mesh);
297-
manifold::Manifold::Error err = r_manifold.Status();
298-
if (err != manifold::Manifold::Error::NoError) {
299-
print_error(String("Manifold creation from mesh failed:" + itos((int)err)));
403+
manifold::Manifold::Error error = r_manifold.Status();
404+
if (error == manifold::Manifold::Error::NoError) {
405+
return;
406+
}
407+
if (p_csg_shape->get_owner()) {
408+
NodePath path = p_csg_shape->get_owner()->get_path_to(p_csg_shape, true);
409+
print_error(vformat("CSGShape3D manifold creation from mesh failed at %s: %s.", path, manifold_error_to_string(error)));
410+
} else {
411+
print_error(vformat("CSGShape3D manifold creation from mesh failed at .: %s.", manifold_error_to_string(error)));
300412
}
301413
}
302414

@@ -330,7 +442,7 @@ CSGBrush *CSGShape3D::_get_brush() {
330442
CSGBrush *n = _build_brush();
331443
HashMap<int32_t, Ref<Material>> mesh_materials;
332444
manifold::Manifold root_manifold;
333-
_pack_manifold(n, root_manifold, mesh_materials, get_snap());
445+
_pack_manifold(n, root_manifold, mesh_materials, this);
334446
manifold::OpType current_op = ManifoldOperation::convert_csg_op(get_operation());
335447
std::vector<manifold::Manifold> manifolds;
336448
manifolds.push_back(root_manifold);
@@ -346,7 +458,7 @@ CSGBrush *CSGShape3D::_get_brush() {
346458
CSGBrush transformed_brush;
347459
transformed_brush.copy_from(*child_brush, child->get_transform());
348460
manifold::Manifold child_manifold;
349-
_pack_manifold(&transformed_brush, child_manifold, mesh_materials, get_snap());
461+
_pack_manifold(&transformed_brush, child_manifold, mesh_materials, child);
350462
manifold::OpType child_operation = ManifoldOperation::convert_csg_op(child->get_operation());
351463
if (child_operation != current_op) {
352464
manifold::Manifold result = manifold::Manifold::BatchBoolean(manifolds, current_op);
@@ -834,8 +946,10 @@ void CSGShape3D::_bind_methods() {
834946
ClassDB::bind_method(D_METHOD("set_operation", "operation"), &CSGShape3D::set_operation);
835947
ClassDB::bind_method(D_METHOD("get_operation"), &CSGShape3D::get_operation);
836948

949+
#ifndef DISABLE_DEPRECATED
837950
ClassDB::bind_method(D_METHOD("set_snap", "snap"), &CSGShape3D::set_snap);
838951
ClassDB::bind_method(D_METHOD("get_snap"), &CSGShape3D::get_snap);
952+
#endif // DISABLE_DEPRECATED
839953

840954
ClassDB::bind_method(D_METHOD("set_use_collision", "operation"), &CSGShape3D::set_use_collision);
841955
ClassDB::bind_method(D_METHOD("is_using_collision"), &CSGShape3D::is_using_collision);
@@ -864,7 +978,9 @@ void CSGShape3D::_bind_methods() {
864978
ClassDB::bind_method(D_METHOD("bake_collision_shape"), &CSGShape3D::bake_collision_shape);
865979

866980
ADD_PROPERTY(PropertyInfo(Variant::INT, "operation", PROPERTY_HINT_ENUM, "Union,Intersection,Subtraction"), "set_operation", "get_operation");
867-
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "snap", PROPERTY_HINT_RANGE, "0.000001,1,0.000001,suffix:m"), "set_snap", "get_snap");
981+
#ifndef DISABLE_DEPRECATED
982+
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "snap", PROPERTY_HINT_RANGE, "0.000001,1,0.000001,suffix:m", PROPERTY_USAGE_NONE), "set_snap", "get_snap");
983+
#endif // DISABLE_DEPRECATED
868984
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "calculate_tangents"), "set_calculate_tangents", "is_calculating_tangents");
869985

870986
ADD_GROUP("Collision", "collision_");

modules/csg/csg_shape.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -155,8 +155,10 @@ class CSGShape3D : public GeometryInstance3D {
155155
void set_collision_priority(real_t p_priority);
156156
real_t get_collision_priority() const;
157157

158+
#ifndef DISABLE_DEPRECATED
158159
void set_snap(float p_snap);
159160
float get_snap() const;
161+
#endif // DISABLE_DEPRECATED
160162

161163
void set_calculate_tangents(bool p_calculate_tangents);
162164
bool is_calculating_tangents() const;

modules/csg/doc_classes/CSGShape3D.xml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -89,8 +89,8 @@
8989
<member name="operation" type="int" setter="set_operation" getter="get_operation" enum="CSGShape3D.Operation" default="0">
9090
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.
9191
</member>
92-
<member name="snap" type="float" setter="set_snap" getter="get_snap" default="0.001">
93-
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.
92+
<member name="snap" type="float" setter="set_snap" getter="get_snap" deprecated="The CSG library no longer uses snapping.">
93+
This property does nothing.
9494
</member>
9595
<member name="use_collision" type="bool" setter="set_use_collision" getter="is_using_collision" default="false">
9696
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].

thirdparty/README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -549,7 +549,7 @@ in the MSVC debugger.
549549
## manifold
550550

551551
- Upstream: https://github.com/elalish/manifold
552-
- Version: 3.0.0 (5d127e57fbfb89225a8e905d0d914ccc86c139c8, 2024)
552+
- Version: master (36035428bc32302a9d7c9ee1ecc833fb8394a2a3, 2024)
553553
- License: Apache 2.0
554554

555555
File extracted from upstream source:

thirdparty/manifold/src/constructors.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -436,7 +436,7 @@ Manifold Manifold::Compose(const std::vector<Manifold>& manifolds) {
436436
for (const auto& manifold : manifolds) {
437437
children.push_back(manifold.pNode_->ToLeafNode());
438438
}
439-
return Manifold(std::make_shared<Impl>(CsgLeafNode::Compose(children)));
439+
return Manifold(CsgLeafNode::Compose(children));
440440
}
441441

442442
/**

0 commit comments

Comments
 (0)