Skip to content

Commit

Permalink
When importing a mesh, allow the user to simplify the created physics…
Browse files Browse the repository at this point in the history
… TriMesh with mesh optimizer.

Adds a new simplification option to the model importer when the trimesh
physics mesh mode is selected similar to how the options for convex
decomposition are presented.

Also adds GDScript exports to generate a convex shapes and trimesh shapes
(both simplified and simplified) to ImportMesh match Mesh.

Closes godotengine/godot-proposals#3603, tested with the example project posted there.

Update doc/classes/MeshSimplificationSettings.xml
Update doc/classes/MeshSimplificationSettings.xml

Co-authored-by: Hugo Locurcio <hugo.locurcio@hugo.pro>
  • Loading branch information
basicer and Calinou committed Oct 4, 2023
1 parent a2f90d5 commit e3a65a1
Show file tree
Hide file tree
Showing 9 changed files with 225 additions and 1 deletion.
23 changes: 23 additions & 0 deletions doc/classes/ImporterMesh.xml
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,29 @@
Removes all surfaces and blend shapes from this [ImporterMesh].
</description>
</method>
<method name="create_convex_shape" qualifiers="const">
<return type="ConvexPolygonShape3D" />
<param index="0" name="clean" type="bool" default="true" />
<param index="1" name="simplify" type="bool" default="false" />
<description>
Calculate a [ConvexPolygonShape3D] from this [ImporterMesh].
If [param clean] 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 [param simplify] is [code]true[/code], the geometry can be further simplified to reduce the number of vertices. Disabled by default.
</description>
</method>
<method name="create_simplified_trimesh_shape" qualifiers="const">
<return type="ConcavePolygonShape3D" />
<param index="0" name="settings" type="MeshSimplificationSettings" />
<description>
Calculate a simplified [ConcavePolygonShape3D] from this [ImporterMesh].
</description>
</method>
<method name="create_trimesh_shape" qualifiers="const">
<return type="ConcavePolygonShape3D" />
<description>
Calculate a [ConcavePolygonShape3D] from this [ImporterMesh].
</description>
</method>
<method name="generate_lods">
<return type="void" />
<param index="0" name="normal_merge_angle" type="float" />
Expand Down
22 changes: 22 additions & 0 deletions doc/classes/MeshSimplificationSettings.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
<?xml version="1.0" encoding="UTF-8" ?>
<class name="MeshSimplificationSettings" inherits="RefCounted" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../class.xsd">
<brief_description>
Parameters to be used with a [Mesh] simplification operation.
</brief_description>
<description>
Parameters to be used with a [Mesh] simplification operation.
</description>
<tutorials>
</tutorials>
<members>
<member name="sloppy" type="bool" setter="set_sloppy" getter="get_sloppy" default="true">
If [code]true[/code], use the sloppy simplification algorithm. The sloppy algorithm does not try to follow the topography of the original mesh.
</member>
<member name="target_error" type="float" setter="set_target_error" getter="get_target_error" default="0.01">
How much the simplified mesh should be allowed to deviate from the source mesh normalized to 0..1 range. For example, a value of [code]0.01[/code] will try to maintain the error to be below 1% of the mesh extents.
</member>
<member name="target_vertex_reduction" type="float" setter="set_target_vertex_reduction" getter="get_target_vertex_reduction" default="0.2">
Fraction of vertices that should be in the simplified mesh.
</member>
</members>
</class>
17 changes: 17 additions & 0 deletions editor/import/resource_importer_scene.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1686,6 +1686,13 @@ void ResourceImporterScene::get_internal_import_options(InternalImportCategory p
r_options->push_back(ImportOption(PropertyInfo(Variant::INT, "decomposition/max_convex_hulls", PROPERTY_HINT_RANGE, "1,100,1", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_UPDATE_ALL_IF_MODIFIED), decomposition_default->get_max_convex_hulls()));
r_options->push_back(ImportOption(PropertyInfo(Variant::BOOL, "decomposition/project_hull_vertices", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_UPDATE_ALL_IF_MODIFIED), decomposition_default->get_project_hull_vertices()));

Ref<MeshSimplificationSettings> simplify_default = Ref<MeshSimplificationSettings>();
simplify_default.instantiate();
r_options->push_back(ImportOption(PropertyInfo(Variant::BOOL, "simplification/enabled", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_UPDATE_ALL_IF_MODIFIED), false));
r_options->push_back(ImportOption(PropertyInfo(Variant::BOOL, "simplification/sloppy", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_UPDATE_ALL_IF_MODIFIED), simplify_default->get_sloppy()));
r_options->push_back(ImportOption(PropertyInfo(Variant::FLOAT, "simplification/target_error", PROPERTY_HINT_RANGE, "0.0,1.0,0.001", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_UPDATE_ALL_IF_MODIFIED), simplify_default->get_target_error()));
r_options->push_back(ImportOption(PropertyInfo(Variant::FLOAT, "simplification/target_vertex_reduction", PROPERTY_HINT_RANGE, "0.0,1.0,0.001", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_UPDATE_ALL_IF_MODIFIED), simplify_default->get_target_vertex_reduction()));

// Primitives: Box, Sphere, Cylinder, Capsule.
r_options->push_back(ImportOption(PropertyInfo(Variant::VECTOR3, "primitive/size", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_UPDATE_ALL_IF_MODIFIED), Vector3(2.0, 2.0, 2.0)));
r_options->push_back(ImportOption(PropertyInfo(Variant::FLOAT, "primitive/height", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_UPDATE_ALL_IF_MODIFIED), 1.0));
Expand Down Expand Up @@ -1792,6 +1799,16 @@ bool ResourceImporterScene::get_internal_option_visibility(InternalImportCategor
return false;
}

if (p_option.find("simplification/") >= 0) {
if (!generate_physics || p_options["physics/shape_type"] != Variant(SHAPE_TYPE_TRIMESH)) {
return false;
}
if (p_option == "simplification/enabled") {
return true;
}
return p_options.has("simplification/enabled") && p_options["simplification/enabled"].operator bool();
}

if (p_option == "primitive/position" || p_option == "primitive/rotation") {
const ShapeType physics_shape = (ShapeType)p_options["physics/shape_type"].operator int();
return generate_physics &&
Expand Down
27 changes: 26 additions & 1 deletion editor/import/resource_importer_scene.h
Original file line number Diff line number Diff line change
Expand Up @@ -407,7 +407,32 @@ Vector<Ref<Shape3D>> ResourceImporterScene::get_collision_shapes(const Ref<Impor
return shapes;
} else if (generate_shape_type == SHAPE_TYPE_TRIMESH) {
Vector<Ref<Shape3D>> shapes;
shapes.push_back(p_mesh->create_trimesh_shape());
bool simplify = false;
if (p_options.has(SNAME("simplification/enabled"))) {
simplify = p_options[SNAME("simplification/enabled")];
}

if (simplify) {
Ref<MeshSimplificationSettings> simplification_settings = Ref<MeshSimplificationSettings>();
simplification_settings.instantiate();

if (p_options.has(SNAME("simplification/sloppy"))) {
simplification_settings->set_sloppy(p_options[SNAME("simplification/sloppy")]);
}

if (p_options.has(SNAME("simplification/target_error"))) {
simplification_settings->set_target_error(p_options[SNAME("simplification/target_error")]);
}

if (p_options.has(SNAME("simplification/target_vertex_reduction"))) {
simplification_settings->set_target_vertex_reduction(p_options[SNAME("simplification/target_vertex_reduction")]);
}

shapes.push_back(p_mesh->create_simplified_trimesh_shape(simplification_settings));
} else {
shapes.push_back(p_mesh->create_trimesh_shape());
}

return shapes;
} else if (generate_shape_type == SHAPE_TYPE_BOX) {
Ref<BoxShape3D> box;
Expand Down
1 change: 1 addition & 0 deletions scene/register_scene_types.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -805,6 +805,7 @@ void register_scene_types() {

GDREGISTER_VIRTUAL_CLASS(Mesh);
GDREGISTER_CLASS(MeshConvexDecompositionSettings);
GDREGISTER_CLASS(MeshSimplificationSettings);
GDREGISTER_CLASS(ArrayMesh);
GDREGISTER_CLASS(PlaceholderMesh);
GDREGISTER_CLASS(ImmediateMesh);
Expand Down
75 changes: 75 additions & 0 deletions scene/resources/importer_mesh.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1054,6 +1054,76 @@ Ref<ConcavePolygonShape3D> ImporterMesh::create_trimesh_shape() const {
return shape;
}

Ref<ConcavePolygonShape3D> ImporterMesh::create_simplified_trimesh_shape(const Ref<MeshSimplificationSettings> &p_simplification_settings) const {
Vector<Vector3> face_points;
ERR_FAIL_COND_V(!SurfaceTool::simplify_sloppy_func, Ref<ConcavePolygonShape3D>());

for (int i = 0; i < surfaces.size(); i++) {
if (surfaces[i].primitive == Mesh::PRIMITIVE_TRIANGLES) {
Vector<Vector3> vertices = surfaces[i].arrays[Mesh::ARRAY_VERTEX];
Vector<int> indices = surfaces[i].arrays[Mesh::ARRAY_INDEX];

if (!indices.size()) {
// Meshoptimzer needs the vertices to be indexed.
indices.resize(vertices.size());
for (int j = 0; j < vertices.size(); ++j)
indices.set(j, j);
}

// Remap duplicate vertices
Vector<unsigned int> remap;
remap.resize(indices.size());
size_t new_vertex_count = SurfaceTool::generate_remap_func(
remap.ptrw(),
(unsigned int *)indices.ptr(), indices.size(),
vertices.ptr(), vertices.size(),
sizeof(Vector3));

SurfaceTool::remap_index_func(
(unsigned int *)indices.ptrw(),
(unsigned int *)indices.ptr(), indices.size(),
remap.ptr());

SurfaceTool::remap_vertex_func(
vertices.ptrw(),
vertices.ptr(), vertices.size(),
sizeof(Vector3),
remap.ptr());

vertices.resize(new_vertex_count);

float r_error;
Vector<int> result;
result.resize(indices.size());
int keep;

if (p_simplification_settings->get_sloppy()) {
keep = SurfaceTool::simplify_sloppy_func(
(unsigned int *)result.ptrw(),
(unsigned int *)indices.ptr(), indices.size(),
(float *)vertices.ptr(), vertices.size(),
sizeof(Vector3), size_t(p_simplification_settings->get_target_vertex_reduction() * vertices.size()), p_simplification_settings->get_target_error(),
&r_error);
} else {
keep = SurfaceTool::simplify_func(
(unsigned int *)result.ptrw(),
(unsigned int *)indices.ptr(), indices.size(),
(float *)vertices.ptr(), vertices.size(),
sizeof(Vector3), size_t(p_simplification_settings->get_target_vertex_reduction() * vertices.size()), p_simplification_settings->get_target_error(),
1, &r_error);
}

for (int j = 0; j < keep; ++j) {
face_points.push_back(vertices[result[j]]);
}
}
}

Ref<ConcavePolygonShape3D> shape = memnew(ConcavePolygonShape3D);
shape->set_faces(face_points);
return shape;
}

Ref<NavigationMesh> ImporterMesh::create_navigation_mesh() {
Vector<Face3> faces = get_faces();
if (faces.size() == 0) {
Expand Down Expand Up @@ -1344,6 +1414,11 @@ Size2i ImporterMesh::get_lightmap_size_hint() const {

void ImporterMesh::_bind_methods() {
ClassDB::bind_method(D_METHOD("add_blend_shape", "name"), &ImporterMesh::add_blend_shape);

ClassDB::bind_method(D_METHOD("create_convex_shape", "clean", "simplify"), &ImporterMesh::create_convex_shape, DEFVAL(true), DEFVAL(false));
ClassDB::bind_method(D_METHOD("create_trimesh_shape"), &ImporterMesh::create_trimesh_shape);
ClassDB::bind_method(D_METHOD("create_simplified_trimesh_shape", "settings"), &ImporterMesh::create_simplified_trimesh_shape);

ClassDB::bind_method(D_METHOD("get_blend_shape_count"), &ImporterMesh::get_blend_shape_count);
ClassDB::bind_method(D_METHOD("get_blend_shape_name", "blend_shape_idx"), &ImporterMesh::get_blend_shape_name);

Expand Down
1 change: 1 addition & 0 deletions scene/resources/importer_mesh.h
Original file line number Diff line number Diff line change
Expand Up @@ -121,6 +121,7 @@ class ImporterMesh : public Resource {
Vector<Ref<Shape3D>> convex_decompose(const Ref<MeshConvexDecompositionSettings> &p_settings) const;
Ref<ConvexPolygonShape3D> create_convex_shape(bool p_clean = true, bool p_simplify = false) const;
Ref<ConcavePolygonShape3D> create_trimesh_shape() const;
Ref<ConcavePolygonShape3D> create_simplified_trimesh_shape(const Ref<MeshSimplificationSettings> &p_simplification_settings) const;
Ref<NavigationMesh> create_navigation_mesh();
Error lightmap_unwrap_cached(const Transform3D &p_base_transform, float p_texel_size, const Vector<uint8_t> &p_src_cache, Vector<uint8_t> &r_dst_cache);

Expand Down
37 changes: 37 additions & 0 deletions scene/resources/mesh.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -201,6 +201,43 @@ void MeshConvexDecompositionSettings::_bind_methods() {

Mesh::ConvexDecompositionFunc Mesh::convex_decomposition_function = nullptr;

void MeshSimplificationSettings::set_sloppy(bool p_sloppy) {
sloppy = p_sloppy;
}

bool MeshSimplificationSettings::get_sloppy() const {
return sloppy;
};

void MeshSimplificationSettings::set_target_error(real_t p_target_error) {
target_error = CLAMP(p_target_error, 0.001, 1.0);
}

real_t MeshSimplificationSettings::get_target_error() const {
return target_error;
};

void MeshSimplificationSettings::set_target_vertex_reduction(real_t p_target_vertex_reduction) {
target_vertex_reduction = CLAMP(target_vertex_reduction, 0.0, 1.0);
};

real_t MeshSimplificationSettings::get_target_vertex_reduction() const {
return target_vertex_reduction;
};

void MeshSimplificationSettings::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_sloppy", "sloppy"), &MeshSimplificationSettings::set_sloppy);
ClassDB::bind_method(D_METHOD("get_sloppy"), &MeshSimplificationSettings::get_sloppy);
ClassDB::bind_method(D_METHOD("set_target_error", "target_error"), &MeshSimplificationSettings::set_target_error);
ClassDB::bind_method(D_METHOD("get_target_error"), &MeshSimplificationSettings::get_target_error);
ClassDB::bind_method(D_METHOD("set_target_vertex_reduction", "target_vertex_reduction"), &MeshSimplificationSettings::set_target_vertex_reduction);
ClassDB::bind_method(D_METHOD("get_target_vertex_reduction"), &MeshSimplificationSettings::get_target_vertex_reduction);

ADD_PROPERTY(PropertyInfo(Variant::BOOL, "sloppy"), "set_sloppy", "get_sloppy");
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "target_error", PROPERTY_HINT_RANGE, "0.001,1.0,0.001,exp"), "set_target_error", "get_target_error");
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "target_vertex_reduction", PROPERTY_HINT_RANGE, "0.0,1.0,0.01"), "set_target_vertex_reduction", "get_target_vertex_reduction");
}

int Mesh::get_surface_count() const {
int ret = 0;
GDVIRTUAL_REQUIRED_CALL(_get_surface_count, ret);
Expand Down
23 changes: 23 additions & 0 deletions scene/resources/mesh.h
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@
class ConcavePolygonShape3D;
class ConvexPolygonShape3D;
class MeshConvexDecompositionSettings;
class MeshSimplificationSettings;
class Shape3D;

class Mesh : public Resource {
Expand Down Expand Up @@ -197,6 +198,28 @@ class Mesh : public Resource {
Mesh();
};

class MeshSimplificationSettings : public RefCounted {
GDCLASS(MeshSimplificationSettings, RefCounted);

private:
bool sloppy = true;
real_t target_error = 0.01;
real_t target_vertex_reduction = 0.2;

protected:
static void _bind_methods();

public:
void set_sloppy(bool p_sloppy);
bool get_sloppy() const;

void set_target_error(real_t p_target_error);
real_t get_target_error() const;

void set_target_vertex_reduction(real_t p_target_vector_reduction);
real_t get_target_vertex_reduction() const;
};

class MeshConvexDecompositionSettings : public RefCounted {
GDCLASS(MeshConvexDecompositionSettings, RefCounted);

Expand Down

0 comments on commit e3a65a1

Please sign in to comment.