From 240c33708cd33eccb266bbb60658d21574d7fa6c Mon Sep 17 00:00:00 2001 From: PouleyKetchoupp Date: Fri, 9 Jul 2021 15:31:05 -0700 Subject: [PATCH] Options to clean/simplify convex hull generated from mesh Clean: remove duplicate and interior vertices (uses Bullet algorithm) Simplify: modify the geometry for further simplification (uses VHACD algorithm) In the editor, single convex hull now uses the clean option. Added a new editor entry to create a simplified convex hull, can be useful for creating convex hull from highly tessellated triangle meshes. Specific change for 3.x: Add support for Vector and PoolVector in the convex hull generator. --- core/math/convex_hull.cpp | 12 +++++- core/math/convex_hull.h | 2 + doc/classes/Mesh.xml | 6 +++ doc/classes/MeshInstance.xml | 6 +++ .../plugins/mesh_instance_editor_plugin.cpp | 19 ++++++++-- editor/plugins/mesh_instance_editor_plugin.h | 1 + modules/vhacd/register_types.cpp | 8 +++- scene/3d/mesh_instance.cpp | 10 ++--- scene/3d/mesh_instance.h | 4 +- scene/resources/mesh.cpp | 37 ++++++++++++++++--- scene/resources/mesh.h | 6 +-- 11 files changed, 88 insertions(+), 23 deletions(-) diff --git a/core/math/convex_hull.cpp b/core/math/convex_hull.cpp index 8cc9088620b2..85fafa47b48f 100644 --- a/core/math/convex_hull.cpp +++ b/core/math/convex_hull.cpp @@ -2249,14 +2249,22 @@ real_t ConvexHullComputer::compute(const Vector3 *p_coords, int32_t p_count, rea } Error ConvexHullComputer::convex_hull(const Vector &p_points, Geometry::MeshData &r_mesh) { + return convex_hull(p_points.ptr(), p_points.size(), r_mesh); +} + +Error ConvexHullComputer::convex_hull(const PoolVector &p_points, Geometry::MeshData &r_mesh) { + return convex_hull(p_points.read().ptr(), p_points.size(), r_mesh); +} + +Error ConvexHullComputer::convex_hull(const Vector3 *p_points, int32_t p_point_count, Geometry::MeshData &r_mesh) { r_mesh = Geometry::MeshData(); // clear - if (p_points.size() == 0) { + if (p_point_count == 0) { return FAILED; // matches QuickHull } ConvexHullComputer ch; - ch.compute(p_points.ptr(), p_points.size(), -1.0, -1.0); + ch.compute(p_points, p_point_count, -1.0, -1.0); r_mesh.vertices = ch.vertices; diff --git a/core/math/convex_hull.h b/core/math/convex_hull.h index 3660c99038ee..c58b5246beba 100644 --- a/core/math/convex_hull.h +++ b/core/math/convex_hull.h @@ -111,6 +111,8 @@ class ConvexHullComputer { real_t compute(const Vector3 *p_coords, int32_t p_count, real_t p_shrink, real_t p_shrink_clamp); static Error convex_hull(const Vector &p_points, Geometry::MeshData &r_mesh); + static Error convex_hull(const PoolVector &p_points, Geometry::MeshData &r_mesh); + static Error convex_hull(const Vector3 *p_points, int32_t p_point_count, Geometry::MeshData &r_mesh); }; #endif // CONVEX_HULL_H diff --git a/doc/classes/Mesh.xml b/doc/classes/Mesh.xml index 948b2928f1c9..78bd3106ece4 100644 --- a/doc/classes/Mesh.xml +++ b/doc/classes/Mesh.xml @@ -16,8 +16,14 @@ + + + + Calculate a [ConvexPolygonShape] from the mesh. + If [code]clean[/code] is [code]true[/code] (default), duplicate and interior vertices are removed automatically. You can set it to [code]false[/code] to make the process faster if not needed. + If [code]simplify[/code] is [code]true[/code], the geometry can be further simplified to reduce the amount of vertices. Disabled by default. diff --git a/doc/classes/MeshInstance.xml b/doc/classes/MeshInstance.xml index 3500c611e650..708b58b8a544 100644 --- a/doc/classes/MeshInstance.xml +++ b/doc/classes/MeshInstance.xml @@ -16,8 +16,14 @@ + + + + This helper creates a [StaticBody] child node with a [ConvexPolygonShape] collision shape calculated from the mesh geometry. It's mainly used for testing. + If [code]clean[/code] is [code]true[/code] (default), duplicate and interior vertices are removed automatically. You can set it to [code]false[/code] to make the process faster if not needed. + If [code]simplify[/code] is [code]true[/code], the geometry can be further simplified to reduce the amount of vertices. Disabled by default. diff --git a/editor/plugins/mesh_instance_editor_plugin.cpp b/editor/plugins/mesh_instance_editor_plugin.cpp index 912840c4b32b..f9090f19841d 100644 --- a/editor/plugins/mesh_instance_editor_plugin.cpp +++ b/editor/plugins/mesh_instance_editor_plugin.cpp @@ -153,14 +153,18 @@ void MeshInstanceEditor::_menu_option(int p_option) { ur->add_undo_method(node->get_parent(), "remove_child", cshape); ur->commit_action(); } break; - case MENU_OPTION_CREATE_SINGLE_CONVEX_COLLISION_SHAPE: { + + case MENU_OPTION_CREATE_SINGLE_CONVEX_COLLISION_SHAPE: + case MENU_OPTION_CREATE_SIMPLIFIED_CONVEX_COLLISION_SHAPE: { if (node == get_tree()->get_edited_scene_root()) { err_dialog->set_text(TTR("Can't create a single convex collision shape for the scene root.")); err_dialog->popup_centered_minsize(); return; } - Ref shape = mesh->create_convex_shape(); + bool simplify = (p_option == MENU_OPTION_CREATE_SIMPLIFIED_CONVEX_COLLISION_SHAPE); + + Ref shape = mesh->create_convex_shape(true, simplify); if (shape.is_null()) { err_dialog->set_text(TTR("Couldn't create a single convex collision shape.")); @@ -169,7 +173,11 @@ void MeshInstanceEditor::_menu_option(int p_option) { } UndoRedo *ur = EditorNode::get_singleton()->get_undo_redo(); - ur->create_action(TTR("Create Single Convex Shape")); + if (simplify) { + ur->create_action(TTR("Create Simplified Convex Shape")); + } else { + ur->create_action(TTR("Create Single Convex Shape")); + } CollisionShape *cshape = memnew(CollisionShape); cshape->set_shape(shape); @@ -186,6 +194,7 @@ void MeshInstanceEditor::_menu_option(int p_option) { ur->commit_action(); } break; + case MENU_OPTION_CREATE_MULTIPLE_CONVEX_COLLISION_SHAPES: { if (node == get_tree()->get_edited_scene_root()) { err_dialog->set_text(TTR("Can't create multiple convex collision shapes for the scene root.")); @@ -447,8 +456,10 @@ MeshInstanceEditor::MeshInstanceEditor() { options->get_popup()->set_item_tooltip(options->get_popup()->get_item_count() - 1, TTR("Creates a polygon-based collision shape.\nThis is the most accurate (but slowest) option for collision detection.")); options->get_popup()->add_item(TTR("Create Single Convex Collision Sibling"), MENU_OPTION_CREATE_SINGLE_CONVEX_COLLISION_SHAPE); options->get_popup()->set_item_tooltip(options->get_popup()->get_item_count() - 1, TTR("Creates a single convex collision shape.\nThis is the fastest (but least accurate) option for collision detection.")); + options->get_popup()->add_item(TTR("Create Simplified Convex Collision Sibling"), MENU_OPTION_CREATE_SIMPLIFIED_CONVEX_COLLISION_SHAPE); + options->get_popup()->set_item_tooltip(options->get_popup()->get_item_count() - 1, TTR("Creates a simplified convex collision shape.\nThis is similar to single collision shape, but can result in a simpler geometry in some cases, at the cost of accuracy.")); options->get_popup()->add_item(TTR("Create Multiple Convex Collision Siblings"), MENU_OPTION_CREATE_MULTIPLE_CONVEX_COLLISION_SHAPES); - options->get_popup()->set_item_tooltip(options->get_popup()->get_item_count() - 1, TTR("Creates a polygon-based collision shape.\nThis is a performance middle-ground between the two above options.")); + options->get_popup()->set_item_tooltip(options->get_popup()->get_item_count() - 1, TTR("Creates a polygon-based collision shape.\nThis is a performance middle-ground between a single convex collision and a polygon-based collision.")); options->get_popup()->add_separator(); options->get_popup()->add_item(TTR("Create Navigation Mesh"), MENU_OPTION_CREATE_NAVMESH); options->get_popup()->add_separator(); diff --git a/editor/plugins/mesh_instance_editor_plugin.h b/editor/plugins/mesh_instance_editor_plugin.h index 28099949cf8f..403f9c727cfb 100644 --- a/editor/plugins/mesh_instance_editor_plugin.h +++ b/editor/plugins/mesh_instance_editor_plugin.h @@ -44,6 +44,7 @@ class MeshInstanceEditor : public Control { MENU_OPTION_CREATE_STATIC_TRIMESH_BODY, MENU_OPTION_CREATE_TRIMESH_COLLISION_SHAPE, MENU_OPTION_CREATE_SINGLE_CONVEX_COLLISION_SHAPE, + MENU_OPTION_CREATE_SIMPLIFIED_CONVEX_COLLISION_SHAPE, MENU_OPTION_CREATE_MULTIPLE_CONVEX_COLLISION_SHAPES, MENU_OPTION_CREATE_NAVMESH, MENU_OPTION_CREATE_OUTLINE_MESH, diff --git a/modules/vhacd/register_types.cpp b/modules/vhacd/register_types.cpp index daad39bdfb29..3d7aaee921e7 100644 --- a/modules/vhacd/register_types.cpp +++ b/modules/vhacd/register_types.cpp @@ -32,7 +32,7 @@ #include "scene/resources/mesh.h" #include "thirdparty/vhacd/public/VHACD.h" -static Vector> convex_decompose(const Vector &p_faces) { +static Vector> convex_decompose(const Vector &p_faces, int p_max_convex_hulls = -1) { Vector vertices; vertices.resize(p_faces.size() * 9); Vector indices; @@ -47,8 +47,12 @@ static Vector> convex_decompose(const Vector &p_faces) { } } - VHACD::IVHACD *decomposer = VHACD::CreateVHACD(); VHACD::IVHACD::Parameters params; + if (p_max_convex_hulls > 0) { + params.m_maxConvexHulls = p_max_convex_hulls; + } + + VHACD::IVHACD *decomposer = VHACD::CreateVHACD(); decomposer->Compute(vertices.ptr(), vertices.size() / 3, indices.ptr(), indices.size() / 3, params); int hull_count = decomposer->GetNConvexHulls(); diff --git a/scene/3d/mesh_instance.cpp b/scene/3d/mesh_instance.cpp index a97555bfb912..68fb4bd548c2 100644 --- a/scene/3d/mesh_instance.cpp +++ b/scene/3d/mesh_instance.cpp @@ -611,12 +611,12 @@ void MeshInstance::create_multiple_convex_collisions() { } } -Node *MeshInstance::create_convex_collision_node() { +Node *MeshInstance::create_convex_collision_node(bool p_clean, bool p_simplify) { if (mesh.is_null()) { return nullptr; } - Ref shape = mesh->create_convex_shape(); + Ref shape = mesh->create_convex_shape(p_clean, p_simplify); if (shape.is_null()) { return nullptr; } @@ -628,8 +628,8 @@ Node *MeshInstance::create_convex_collision_node() { return static_body; } -void MeshInstance::create_convex_collision() { - StaticBody *static_body = Object::cast_to(create_convex_collision_node()); +void MeshInstance::create_convex_collision(bool p_clean, bool p_simplify) { + StaticBody *static_body = Object::cast_to(create_convex_collision_node(p_clean, p_simplify)); ERR_FAIL_COND(!static_body); static_body->set_name(String(get_name()) + "_col"); @@ -841,7 +841,7 @@ void MeshInstance::_bind_methods() { ClassDB::set_method_flags("MeshInstance", "create_trimesh_collision", METHOD_FLAGS_DEFAULT); ClassDB::bind_method(D_METHOD("create_multiple_convex_collisions"), &MeshInstance::create_multiple_convex_collisions); ClassDB::set_method_flags("MeshInstance", "create_multiple_convex_collisions", METHOD_FLAGS_DEFAULT); - ClassDB::bind_method(D_METHOD("create_convex_collision"), &MeshInstance::create_convex_collision); + ClassDB::bind_method(D_METHOD("create_convex_collision", "clean", "simplify"), &MeshInstance::create_convex_collision, DEFVAL(true), DEFVAL(false)); ClassDB::set_method_flags("MeshInstance", "create_convex_collision", METHOD_FLAGS_DEFAULT); ClassDB::bind_method(D_METHOD("_mesh_changed"), &MeshInstance::_mesh_changed); ClassDB::bind_method(D_METHOD("_update_skinning"), &MeshInstance::_update_skinning); diff --git a/scene/3d/mesh_instance.h b/scene/3d/mesh_instance.h index af4da9515e22..8b84b4545fa6 100644 --- a/scene/3d/mesh_instance.h +++ b/scene/3d/mesh_instance.h @@ -128,8 +128,8 @@ class MeshInstance : public GeometryInstance { Node *create_multiple_convex_collisions_node(); void create_multiple_convex_collisions(); - Node *create_convex_collision_node(); - void create_convex_collision(); + Node *create_convex_collision_node(bool p_clean = true, bool p_simplify = false); + void create_convex_collision(bool p_clean = true, bool p_simplify = false); void create_debug_tangents(); diff --git a/scene/resources/mesh.cpp b/scene/resources/mesh.cpp index 6bb8ab4ae3a1..628601ec7063 100644 --- a/scene/resources/mesh.cpp +++ b/scene/resources/mesh.cpp @@ -32,6 +32,7 @@ #include "core/crypto/crypto_core.h" #include "core/local_vector.h" +#include "core/math/convex_hull.h" #include "core/pair.h" #include "scene/resources/concave_polygon_shape.h" #include "scene/resources/convex_polygon_shape.h" @@ -229,9 +230,17 @@ PoolVector Mesh::get_faces() const { */ } -Ref Mesh::create_convex_shape() const { - PoolVector vertices; +Ref Mesh::create_convex_shape(bool p_clean, bool p_simplify) const { + if (p_simplify) { + Vector> decomposed = convex_decompose(1); + if (decomposed.size() == 1) { + return decomposed[0]; + } else { + ERR_PRINT("Convex shape simplification failed, falling back to simpler process."); + } + } + PoolVector vertices; for (int i = 0; i < get_surface_count(); i++) { Array a = surface_get_arrays(i); ERR_FAIL_COND_V(a.empty(), Ref()); @@ -240,6 +249,24 @@ Ref Mesh::create_convex_shape() const { } Ref shape = memnew(ConvexPolygonShape); + + if (p_clean) { + Geometry::MeshData md; + Error err = ConvexHullComputer::convex_hull(vertices, md); + if (err == OK) { + int vertex_count = md.vertices.size(); + vertices.resize(vertex_count); + { + PoolVector::Write w = vertices.write(); + for (int idx = 0; idx < vertex_count; ++idx) { + w[idx] = md.vertices[idx]; + } + } + } else { + ERR_PRINT("Convex shape cleaning failed, falling back to simpler process."); + } + } + shape->set_points(vertices); return shape; } @@ -539,7 +566,7 @@ void Mesh::clear_cache() const { debug_lines.clear(); } -Vector> Mesh::convex_decompose() const { +Vector> Mesh::convex_decompose(int p_max_convex_hulls) const { ERR_FAIL_COND_V(!convex_composition_function, Vector>()); PoolVector faces = get_faces(); @@ -550,7 +577,7 @@ Vector> Mesh::convex_decompose() const { f3.write[i] = f[i]; } - Vector> decomposed = convex_composition_function(f3); + Vector> decomposed = convex_composition_function(f3, p_max_convex_hulls); Vector> ret; @@ -1428,7 +1455,7 @@ void ArrayMesh::_bind_methods() { ClassDB::bind_method(D_METHOD("surface_set_name", "surf_idx", "name"), &ArrayMesh::surface_set_name); ClassDB::bind_method(D_METHOD("surface_get_name", "surf_idx"), &ArrayMesh::surface_get_name); ClassDB::bind_method(D_METHOD("create_trimesh_shape"), &ArrayMesh::create_trimesh_shape); - ClassDB::bind_method(D_METHOD("create_convex_shape"), &ArrayMesh::create_convex_shape); + ClassDB::bind_method(D_METHOD("create_convex_shape", "clean", "simplify"), &ArrayMesh::create_convex_shape, DEFVAL(true), DEFVAL(false)); ClassDB::bind_method(D_METHOD("create_outline", "margin"), &ArrayMesh::create_outline); ClassDB::bind_method(D_METHOD("regen_normalmaps"), &ArrayMesh::regen_normalmaps); ClassDB::set_method_flags(get_class_static(), _scs_create("regen_normalmaps"), METHOD_FLAGS_DEFAULT | METHOD_FLAG_EDITOR); diff --git a/scene/resources/mesh.h b/scene/resources/mesh.h index 312c058229aa..0d05fd85bb37 100644 --- a/scene/resources/mesh.h +++ b/scene/resources/mesh.h @@ -137,7 +137,7 @@ class Mesh : public Resource { void generate_debug_mesh_indices(Vector &r_points); Ref create_trimesh_shape() const; - Ref create_convex_shape() const; + Ref create_convex_shape(bool p_clean = true, bool p_simplify = false) const; Ref create_outline(float p_margin) const; @@ -147,11 +147,11 @@ class Mesh : public Resource { Size2 get_lightmap_size_hint() const; void clear_cache() const; - typedef Vector> (*ConvexDecompositionFunc)(const Vector &); + typedef Vector> (*ConvexDecompositionFunc)(const Vector &p_faces, int p_max_convex_hulls); static ConvexDecompositionFunc convex_composition_function; - Vector> convex_decompose() const; + Vector> convex_decompose(int p_max_convex_hulls = -1) const; Mesh(); };