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

[3.x] Options to clean/simplify convex hull generated from mesh #50328

Merged
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
12 changes: 10 additions & 2 deletions core/math/convex_hull.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2249,14 +2249,22 @@ real_t ConvexHullComputer::compute(const Vector3 *p_coords, int32_t p_count, rea
}

Error ConvexHullComputer::convex_hull(const Vector<Vector3> &p_points, Geometry::MeshData &r_mesh) {
return convex_hull(p_points.ptr(), p_points.size(), r_mesh);
}

Error ConvexHullComputer::convex_hull(const PoolVector<Vector3> &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;

Expand Down
2 changes: 2 additions & 0 deletions core/math/convex_hull.h
Original file line number Diff line number Diff line change
Expand Up @@ -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<Vector3> &p_points, Geometry::MeshData &r_mesh);
static Error convex_hull(const PoolVector<Vector3> &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
6 changes: 6 additions & 0 deletions doc/classes/Mesh.xml
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,14 @@
<method name="create_convex_shape" qualifiers="const">
<return type="Shape">
</return>
<argument index="0" name="clean" type="bool" default="true">
</argument>
<argument index="1" name="simplify" type="bool" default="false">
</argument>
<description>
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.
</description>
</method>
<method name="create_outline" qualifiers="const">
Expand Down
6 changes: 6 additions & 0 deletions doc/classes/MeshInstance.xml
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,14 @@
<method name="create_convex_collision">
<return type="void">
</return>
<argument index="0" name="clean" type="bool" default="true">
</argument>
<argument index="1" name="simplify" type="bool" default="false">
</argument>
<description>
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.
</description>
</method>
<method name="create_debug_tangents">
Expand Down
19 changes: 15 additions & 4 deletions editor/plugins/mesh_instance_editor_plugin.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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> shape = mesh->create_convex_shape();
bool simplify = (p_option == MENU_OPTION_CREATE_SIMPLIFIED_CONVEX_COLLISION_SHAPE);

Ref<Shape> shape = mesh->create_convex_shape(true, simplify);

if (shape.is_null()) {
err_dialog->set_text(TTR("Couldn't create a single convex collision shape."));
Expand All @@ -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);
Expand All @@ -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."));
Expand Down Expand Up @@ -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();
Expand Down
1 change: 1 addition & 0 deletions editor/plugins/mesh_instance_editor_plugin.h
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
8 changes: 6 additions & 2 deletions modules/vhacd/register_types.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@
#include "scene/resources/mesh.h"
#include "thirdparty/vhacd/public/VHACD.h"

static Vector<Vector<Face3>> convex_decompose(const Vector<Face3> &p_faces) {
static Vector<Vector<Face3>> convex_decompose(const Vector<Face3> &p_faces, int p_max_convex_hulls = -1) {
Vector<float> vertices;
vertices.resize(p_faces.size() * 9);
Vector<uint32_t> indices;
Expand All @@ -47,8 +47,12 @@ static Vector<Vector<Face3>> convex_decompose(const Vector<Face3> &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();
Expand Down
10 changes: 5 additions & 5 deletions scene/3d/mesh_instance.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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> shape = mesh->create_convex_shape();
Ref<Shape> shape = mesh->create_convex_shape(p_clean, p_simplify);
if (shape.is_null()) {
return nullptr;
}
Expand All @@ -628,8 +628,8 @@ Node *MeshInstance::create_convex_collision_node() {
return static_body;
}

void MeshInstance::create_convex_collision() {
StaticBody *static_body = Object::cast_to<StaticBody>(create_convex_collision_node());
void MeshInstance::create_convex_collision(bool p_clean, bool p_simplify) {
StaticBody *static_body = Object::cast_to<StaticBody>(create_convex_collision_node(p_clean, p_simplify));
ERR_FAIL_COND(!static_body);
static_body->set_name(String(get_name()) + "_col");

Expand Down Expand Up @@ -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);
Expand Down
4 changes: 2 additions & 2 deletions scene/3d/mesh_instance.h
Original file line number Diff line number Diff line change
Expand Up @@ -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();

Expand Down
37 changes: 32 additions & 5 deletions scene/resources/mesh.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down Expand Up @@ -229,9 +230,17 @@ PoolVector<Face3> Mesh::get_faces() const {
*/
}

Ref<Shape> Mesh::create_convex_shape() const {
PoolVector<Vector3> vertices;
Ref<Shape> Mesh::create_convex_shape(bool p_clean, bool p_simplify) const {
if (p_simplify) {
Vector<Ref<Shape>> decomposed = convex_decompose(1);
if (decomposed.size() == 1) {
return decomposed[0];
} else {
ERR_PRINT("Convex shape simplification failed, falling back to simpler process.");
}
}

PoolVector<Vector3> vertices;
for (int i = 0; i < get_surface_count(); i++) {
Array a = surface_get_arrays(i);
ERR_FAIL_COND_V(a.empty(), Ref<ConvexPolygonShape>());
Expand All @@ -240,6 +249,24 @@ Ref<Shape> Mesh::create_convex_shape() const {
}

Ref<ConvexPolygonShape> 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<Vector3>::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;
}
Expand Down Expand Up @@ -539,7 +566,7 @@ void Mesh::clear_cache() const {
debug_lines.clear();
}

Vector<Ref<Shape>> Mesh::convex_decompose() const {
Vector<Ref<Shape>> Mesh::convex_decompose(int p_max_convex_hulls) const {
ERR_FAIL_COND_V(!convex_composition_function, Vector<Ref<Shape>>());

PoolVector<Face3> faces = get_faces();
Expand All @@ -550,7 +577,7 @@ Vector<Ref<Shape>> Mesh::convex_decompose() const {
f3.write[i] = f[i];
}

Vector<Vector<Face3>> decomposed = convex_composition_function(f3);
Vector<Vector<Face3>> decomposed = convex_composition_function(f3, p_max_convex_hulls);

Vector<Ref<Shape>> ret;

Expand Down Expand Up @@ -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);
Expand Down
6 changes: 3 additions & 3 deletions scene/resources/mesh.h
Original file line number Diff line number Diff line change
Expand Up @@ -137,7 +137,7 @@ class Mesh : public Resource {
void generate_debug_mesh_indices(Vector<Vector3> &r_points);

Ref<Shape> create_trimesh_shape() const;
Ref<Shape> create_convex_shape() const;
Ref<Shape> create_convex_shape(bool p_clean = true, bool p_simplify = false) const;

Ref<Mesh> create_outline(float p_margin) const;

Expand All @@ -147,11 +147,11 @@ class Mesh : public Resource {
Size2 get_lightmap_size_hint() const;
void clear_cache() const;

typedef Vector<Vector<Face3>> (*ConvexDecompositionFunc)(const Vector<Face3> &);
typedef Vector<Vector<Face3>> (*ConvexDecompositionFunc)(const Vector<Face3> &p_faces, int p_max_convex_hulls);

static ConvexDecompositionFunc convex_composition_function;

Vector<Ref<Shape>> convex_decompose() const;
Vector<Ref<Shape>> convex_decompose(int p_max_convex_hulls = -1) const;

Mesh();
};
Expand Down