From 9e1810695c926286ddfe3e1614a6a54044583ac0 Mon Sep 17 00:00:00 2001 From: jfons Date: Tue, 7 Sep 2021 17:44:50 +0200 Subject: Auto LOD fixes and improvements * Fixed LODs for shadow meshes. * Added a merging step before simplification. This helps with tesselated meshes that were previously left untouched. The angle difference at wich edges ar considered "hard" can be tweaked as an import setting. * LODs will now start with the highest decimation possible and keep doubling (approximately) the number of triangles from there. This makes sure that very low triangle counts are included when possible. * Given more weight to normal preservation. * Modified MeshOptimizer to report distance-based error instead of including attributes in the reported metrics. * Added attribute transference between the original mesh and the various LODs. Right now only normals are taken into account, but it could be expanded to other attributes in the future. --- editor/import/resource_importer_scene.cpp | 15 +- editor/import/scene_importer_mesh.cpp | 499 +++++++++++++++++++++++++----- editor/import/scene_importer_mesh.h | 14 +- 3 files changed, 449 insertions(+), 79 deletions(-) (limited to 'editor/import') diff --git a/editor/import/resource_importer_scene.cpp b/editor/import/resource_importer_scene.cpp index c48d9bb117..2c9bc7dadf 100644 --- a/editor/import/resource_importer_scene.cpp +++ b/editor/import/resource_importer_scene.cpp @@ -980,6 +980,8 @@ void ResourceImporterScene::get_internal_import_options(InternalImportCategory p r_options->push_back(ImportOption(PropertyInfo(Variant::INT, "generate/shadow_meshes", PROPERTY_HINT_ENUM, "Default,Enable,Disable"), 0)); r_options->push_back(ImportOption(PropertyInfo(Variant::INT, "generate/lightmap_uv", PROPERTY_HINT_ENUM, "Default,Enable,Disable"), 0)); r_options->push_back(ImportOption(PropertyInfo(Variant::INT, "generate/lods", PROPERTY_HINT_ENUM, "Default,Enable,Disable"), 0)); + r_options->push_back(ImportOption(PropertyInfo(Variant::FLOAT, "lods/normal_split_angle", PROPERTY_HINT_RANGE, "0,180,0.1,degrees"), 25.0f)); + r_options->push_back(ImportOption(PropertyInfo(Variant::FLOAT, "lods/normal_merge_angle", PROPERTY_HINT_RANGE, "0,180,0.1,degrees"), 60.0f)); } break; case INTERNAL_IMPORT_CATEGORY_MATERIAL: { r_options->push_back(ImportOption(PropertyInfo(Variant::BOOL, "use_external/enabled", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_UPDATE_ALL_IF_MODIFIED), false)); @@ -1259,6 +1261,8 @@ void ResourceImporterScene::_generate_meshes(Node *p_node, const Dictionary &p_m //do mesh processing bool generate_lods = p_generate_lods; + float split_angle = 25.0f; + float merge_angle = 60.0f; bool create_shadow_meshes = p_create_shadow_meshes; bool bake_lightmaps = p_light_bake_mode == LIGHT_BAKE_STATIC_LIGHTMAPS; String save_to_file; @@ -1301,6 +1305,14 @@ void ResourceImporterScene::_generate_meshes(Node *p_node, const Dictionary &p_m } } + if (mesh_settings.has("lods/normal_split_angle")) { + split_angle = mesh_settings["lods/normal_split_angle"]; + } + + if (mesh_settings.has("lods/normal_merge_angle")) { + merge_angle = mesh_settings["lods/normal_merge_angle"]; + } + if (mesh_settings.has("save_to_file/enabled") && bool(mesh_settings["save_to_file/enabled"]) && mesh_settings.has("save_to_file/path")) { save_to_file = mesh_settings["save_to_file/path"]; if (!save_to_file.is_resource_file()) { @@ -1310,8 +1322,9 @@ void ResourceImporterScene::_generate_meshes(Node *p_node, const Dictionary &p_m } if (generate_lods) { - src_mesh_node->get_mesh()->generate_lods(); + src_mesh_node->get_mesh()->generate_lods(merge_angle, split_angle); } + if (create_shadow_meshes) { src_mesh_node->get_mesh()->create_shadow_mesh(); } diff --git a/editor/import/scene_importer_mesh.cpp b/editor/import/scene_importer_mesh.cpp index 5e6dd08e79..f83a7d046a 100644 --- a/editor/import/scene_importer_mesh.cpp +++ b/editor/import/scene_importer_mesh.cpp @@ -30,11 +30,99 @@ #include "scene_importer_mesh.h" -#include "core/math/math_defs.h" +#include "core/math/random_pcg.h" +#include "core/math/static_raycaster.h" #include "scene/resources/surface_tool.h" #include +void EditorSceneImporterMesh::Surface::split_normals(const LocalVector &p_indices, const LocalVector &p_normals) { + ERR_FAIL_COND(arrays.size() != RS::ARRAY_MAX); + + const PackedVector3Array &vertices = arrays[RS::ARRAY_VERTEX]; + int current_vertex_count = vertices.size(); + int new_vertex_count = p_indices.size(); + int final_vertex_count = current_vertex_count + new_vertex_count; + const int *indices_ptr = p_indices.ptr(); + + for (int i = 0; i < arrays.size(); i++) { + if (i == RS::ARRAY_INDEX) { + continue; + } + + if (arrays[i].get_type() == Variant::NIL) { + continue; + } + + switch (arrays[i].get_type()) { + case Variant::PACKED_VECTOR3_ARRAY: { + PackedVector3Array data = arrays[i]; + data.resize(final_vertex_count); + Vector3 *data_ptr = data.ptrw(); + if (i == RS::ARRAY_NORMAL) { + const Vector3 *normals_ptr = p_normals.ptr(); + memcpy(&data_ptr[current_vertex_count], normals_ptr, sizeof(Vector3) * new_vertex_count); + } else { + for (int j = 0; j < new_vertex_count; j++) { + data_ptr[current_vertex_count + j] = data_ptr[indices_ptr[j]]; + } + } + arrays[i] = data; + } break; + case Variant::PACKED_VECTOR2_ARRAY: { + PackedVector2Array data = arrays[i]; + data.resize(final_vertex_count); + Vector2 *data_ptr = data.ptrw(); + for (int j = 0; j < new_vertex_count; j++) { + data_ptr[current_vertex_count + j] = data_ptr[indices_ptr[j]]; + } + arrays[i] = data; + } break; + case Variant::PACKED_FLOAT32_ARRAY: { + PackedFloat32Array data = arrays[i]; + int elements = data.size() / current_vertex_count; + data.resize(final_vertex_count * elements); + float *data_ptr = data.ptrw(); + for (int j = 0; j < new_vertex_count; j++) { + memcpy(&data_ptr[(current_vertex_count + j) * elements], &data_ptr[indices_ptr[j] * elements], sizeof(float) * elements); + } + arrays[i] = data; + } break; + case Variant::PACKED_INT32_ARRAY: { + PackedInt32Array data = arrays[i]; + int elements = data.size() / current_vertex_count; + data.resize(final_vertex_count * elements); + int32_t *data_ptr = data.ptrw(); + for (int j = 0; j < new_vertex_count; j++) { + memcpy(&data_ptr[(current_vertex_count + j) * elements], &data_ptr[indices_ptr[j] * elements], sizeof(int32_t) * elements); + } + arrays[i] = data; + } break; + case Variant::PACKED_BYTE_ARRAY: { + PackedByteArray data = arrays[i]; + int elements = data.size() / current_vertex_count; + data.resize(final_vertex_count * elements); + uint8_t *data_ptr = data.ptrw(); + for (int j = 0; j < new_vertex_count; j++) { + memcpy(&data_ptr[(current_vertex_count + j) * elements], &data_ptr[indices_ptr[j] * elements], sizeof(uint8_t) * elements); + } + arrays[i] = data; + } break; + case Variant::PACKED_COLOR_ARRAY: { + PackedColorArray data = arrays[i]; + data.resize(final_vertex_count); + Color *data_ptr = data.ptrw(); + for (int j = 0; j < new_vertex_count; j++) { + data_ptr[current_vertex_count + j] = data_ptr[indices_ptr[j]]; + } + } break; + default: { + ERR_FAIL_MSG("Uhandled array type."); + } break; + } + } +} + void EditorSceneImporterMesh::add_blend_shape(const String &p_name) { ERR_FAIL_COND(surfaces.size() > 0); blend_shapes.push_back(p_name); @@ -157,29 +245,14 @@ void EditorSceneImporterMesh::set_surface_material(int p_surface, const Ref vertices = surfaces[i].arrays[RS::ARRAY_VERTEX]; - Vector indices = surfaces[i].arrays[RS::ARRAY_INDEX]; - if (indices.size() == 0) { + PackedInt32Array indices = surfaces[i].arrays[RS::ARRAY_INDEX]; + Vector normals = surfaces[i].arrays[RS::ARRAY_NORMAL]; + Vector uvs = surfaces[i].arrays[RS::ARRAY_TEX_UV]; + + unsigned int index_count = indices.size(); + unsigned int vertex_count = vertices.size(); + + if (index_count == 0) { continue; //no lods if no indices } - Vector normals = surfaces[i].arrays[RS::ARRAY_NORMAL]; - uint32_t vertex_count = vertices.size(); + const Vector3 *vertices_ptr = vertices.ptr(); - Vector attributes; - Vector normal_weights; - int32_t attribute_count = 6; - if (normals.size()) { - attributes.resize(normals.size() * attribute_count); - for (int32_t normal_i = 0; normal_i < normals.size(); normal_i++) { - Basis basis; - basis.set_euler(normals[normal_i]); - Vector3 basis_x = basis.get_axis(0); - Vector3 basis_y = basis.get_axis(1); - basis = compute_rotation_matrix_from_ortho_6d(basis_x, basis_y); - basis_x = basis.get_axis(0); - basis_y = basis.get_axis(1); - attributes.write[normal_i * attribute_count + 0] = basis_x.x; - attributes.write[normal_i * attribute_count + 1] = basis_x.y; - attributes.write[normal_i * attribute_count + 2] = basis_x.z; - attributes.write[normal_i * attribute_count + 3] = basis_y.x; - attributes.write[normal_i * attribute_count + 4] = basis_y.y; - attributes.write[normal_i * attribute_count + 5] = basis_y.z; - } - normal_weights.resize(vertex_count); - for (int32_t weight_i = 0; weight_i < normal_weights.size(); weight_i++) { - normal_weights.write[weight_i] = 1.0; + const int *indices_ptr = indices.ptr(); + + if (normals.is_empty()) { + normals.resize(vertices.size()); + Vector3 *n_ptr = normals.ptrw(); + for (unsigned int j = 0; j < index_count; j += 3) { + const Vector3 &v0 = vertices_ptr[indices_ptr[j + 0]]; + const Vector3 &v1 = vertices_ptr[indices_ptr[j + 1]]; + const Vector3 &v2 = vertices_ptr[indices_ptr[j + 2]]; + Vector3 n = vec3_cross(v0 - v2, v0 - v1).normalized(); + n_ptr[j + 0] = n; + n_ptr[j + 1] = n; + n_ptr[j + 2] = n; } - } else { - attribute_count = 0; } - const int min_indices = 10; - const float error_tolerance = 1.44224'95703; // Cube root of 3 - const float threshold = 1.0 / error_tolerance; - int index_target = indices.size() * threshold; - float max_mesh_error_percentage = 1e0f; + + float normal_merge_threshold = Math::cos(Math::deg2rad(p_normal_merge_angle)); + float normal_pre_split_threshold = Math::cos(Math::deg2rad(MIN(180.0f, p_normal_split_angle * 2.0f))); + float normal_split_threshold = Math::cos(Math::deg2rad(p_normal_split_angle)); + const Vector3 *normals_ptr = normals.ptr(); + + Map>> unique_vertices; + + LocalVector vertex_remap; + LocalVector vertex_inverse_remap; + LocalVector merged_vertices; + LocalVector merged_normals; + LocalVector merged_normals_counts; + const Vector2 *uvs_ptr = uvs.ptr(); + + for (unsigned int j = 0; j < vertex_count; j++) { + const Vector3 &v = vertices_ptr[j]; + const Vector3 &n = normals_ptr[j]; + + Map>>::Element *E = unique_vertices.find(v); + + if (E) { + const LocalVector> &close_verts = E->get(); + + bool found = false; + for (unsigned int k = 0; k < close_verts.size(); k++) { + const Pair &idx = close_verts[k]; + + // TODO check more attributes? + if ((!uvs_ptr || uvs_ptr[j].distance_squared_to(uvs_ptr[idx.second]) < CMP_EPSILON2) && normals[idx.second].dot(n) > normal_merge_threshold) { + vertex_remap.push_back(idx.first); + merged_normals[idx.first] += normals[idx.second]; + merged_normals_counts[idx.first]++; + found = true; + break; + } + } + + if (!found) { + int vcount = merged_vertices.size(); + unique_vertices[v].push_back(Pair(vcount, j)); + vertex_inverse_remap.push_back(j); + merged_vertices.push_back(v); + vertex_remap.push_back(vcount); + merged_normals.push_back(normals_ptr[j]); + merged_normals_counts.push_back(1); + } + } else { + int vcount = merged_vertices.size(); + unique_vertices[v] = LocalVector>(); + unique_vertices[v].push_back(Pair(vcount, j)); + vertex_inverse_remap.push_back(j); + merged_vertices.push_back(v); + vertex_remap.push_back(vcount); + merged_normals.push_back(normals_ptr[j]); + merged_normals_counts.push_back(1); + } + } + + LocalVector merged_indices; + merged_indices.resize(index_count); + for (unsigned int j = 0; j < index_count; j++) { + merged_indices[j] = vertex_remap[indices[j]]; + } + + unsigned int merged_vertex_count = merged_vertices.size(); + const Vector3 *merged_vertices_ptr = merged_vertices.ptr(); + const int32_t *merged_indices_ptr = merged_indices.ptr(); + + { + const int *counts_ptr = merged_normals_counts.ptr(); + Vector3 *merged_normals_ptrw = merged_normals.ptr(); + for (unsigned int j = 0; j < merged_vertex_count; j++) { + merged_normals_ptrw[j] /= counts_ptr[j]; + } + } + + LocalVector normal_weights; + normal_weights.resize(merged_vertex_count); + for (unsigned int j = 0; j < merged_vertex_count; j++) { + normal_weights[j] = 2.0; // Give some weight to normal preservation, may be worth exposing as an import setting + } + + const float max_mesh_error = FLT_MAX; // We don't want to limit by error, just by index target + float scale = SurfaceTool::simplify_scale_func((const float *)merged_vertices_ptr, merged_vertex_count, sizeof(Vector3)); float mesh_error = 0.0f; - float scale = SurfaceTool::simplify_scale_func((const float *)vertices_ptr, vertex_count, sizeof(Vector3)); - while (index_target > min_indices) { - Vector new_indices; - new_indices.resize(indices.size()); - size_t new_len = SurfaceTool::simplify_with_attrib_func((unsigned int *)new_indices.ptrw(), (const unsigned int *)indices.ptr(), indices.size(), (const float *)vertices_ptr, vertex_count, sizeof(Vector3), index_target, max_mesh_error_percentage, &mesh_error, (float *)attributes.ptrw(), normal_weights.ptrw(), attribute_count); - if ((int)new_len > (index_target * error_tolerance)) { - break; + + unsigned int index_target = 12; // Start with the smallest target, 4 triangles + unsigned int last_index_count = 0; + + int split_vertex_count = vertex_count; + LocalVector split_vertex_normals; + LocalVector split_vertex_indices; + split_vertex_normals.reserve(index_count / 3); + split_vertex_indices.reserve(index_count / 3); + + RandomPCG pcg; + pcg.seed(123456789); // Keep seed constant across imports + + Ref raycaster = StaticRaycaster::create(); + if (raycaster.is_valid()) { + raycaster->add_mesh(vertices, indices, 0); + raycaster->commit(); + } + + while (index_target < index_count) { + PackedInt32Array new_indices; + new_indices.resize(index_count); + + size_t new_index_count = SurfaceTool::simplify_with_attrib_func((unsigned int *)new_indices.ptrw(), (const uint32_t *)merged_indices_ptr, index_count, (const float *)merged_vertices_ptr, merged_vertex_count, sizeof(Vector3), index_target, max_mesh_error, &mesh_error, (float *)merged_normals.ptr(), normal_weights.ptr(), 3); + + if (new_index_count < last_index_count * 1.5f) { + index_target = index_target * 1.5f; + continue; } - Surface::LOD lod; - lod.distance = mesh_error * scale; - if (Math::is_zero_approx(mesh_error)) { + + if (new_index_count <= 0 || (new_index_count >= (index_count * 0.75f))) { break; } - if (new_len <= 0) { - break; + + new_indices.resize(new_index_count); + + LocalVector> vertex_corners; + vertex_corners.resize(vertex_count); + { + int *ptrw = new_indices.ptrw(); + for (unsigned int j = 0; j < new_index_count; j++) { + const int &remapped = vertex_inverse_remap[ptrw[j]]; + vertex_corners[remapped].push_back(j); + ptrw[j] = remapped; + } } - new_indices.resize(new_len); + + if (raycaster.is_valid()) { + float error_factor = 1.0f / (scale * MAX(mesh_error, 0.15)); + const float ray_bias = 0.05; + float ray_length = ray_bias + mesh_error * scale * 3.0f; + + Vector rays; + LocalVector ray_uvs; + + int32_t *new_indices_ptr = new_indices.ptrw(); + + int current_ray_count = 0; + for (unsigned int j = 0; j < new_index_count; j += 3) { + const Vector3 &v0 = vertices_ptr[new_indices_ptr[j + 0]]; + const Vector3 &v1 = vertices_ptr[new_indices_ptr[j + 1]]; + const Vector3 &v2 = vertices_ptr[new_indices_ptr[j + 2]]; + Vector3 face_normal = vec3_cross(v0 - v2, v0 - v1); + float face_area = face_normal.length(); // Actually twice the face area, since it's the same error_factor on all faces, we don't care + + Vector3 dir = face_normal / face_area; + int ray_count = CLAMP(5.0 * face_area * error_factor, 16, 64); + + rays.resize(current_ray_count + ray_count); + StaticRaycaster::Ray *rays_ptr = rays.ptrw(); + + ray_uvs.resize(current_ray_count + ray_count); + Vector2 *ray_uvs_ptr = ray_uvs.ptr(); + + for (int k = 0; k < ray_count; k++) { + float u = pcg.randf(); + float v = pcg.randf(); + + if (u + v >= 1.0f) { + u = 1.0f - u; + v = 1.0f - v; + } + + u = 0.9f * u + 0.05f / 3.0f; // Give barycentric coordinates some padding, we don't want to sample right on the edge + v = 0.9f * v + 0.05f / 3.0f; // v = (v - one_third) * 0.95f + one_third; + float w = 1.0f - u - v; + + Vector3 org = v0 * w + v1 * u + v2 * v; + org -= dir * ray_bias; + rays_ptr[current_ray_count + k] = StaticRaycaster::Ray(org, dir, 0.0f, ray_length); + rays_ptr[current_ray_count + k].id = j / 3; + ray_uvs_ptr[current_ray_count + k] = Vector2(u, v); + } + + current_ray_count += ray_count; + } + + raycaster->intersect(rays); + + LocalVector ray_normals; + LocalVector ray_normal_weights; + + ray_normals.resize(new_index_count); + ray_normal_weights.resize(new_index_count); + + for (unsigned int j = 0; j < new_index_count; j++) { + ray_normal_weights[j] = 0.0f; + } + + const StaticRaycaster::Ray *rp = rays.ptr(); + for (int j = 0; j < rays.size(); j++) { + if (rp[j].geomID != 0) { // Ray missed + continue; + } + + if (rp[j].normal.normalized().dot(rp[j].dir) > 0.0f) { // Hit a back face. + continue; + } + + const float &u = rp[j].u; + const float &v = rp[j].v; + const float w = 1.0f - u - v; + + const unsigned int &hit_tri_id = rp[j].primID; + const unsigned int &orig_tri_id = rp[j].id; + + const Vector3 &n0 = normals_ptr[indices_ptr[hit_tri_id * 3 + 0]]; + const Vector3 &n1 = normals_ptr[indices_ptr[hit_tri_id * 3 + 1]]; + const Vector3 &n2 = normals_ptr[indices_ptr[hit_tri_id * 3 + 2]]; + Vector3 normal = n0 * w + n1 * u + n2 * v; + + Vector2 orig_uv = ray_uvs[j]; + float orig_bary[3] = { 1.0f - orig_uv.x - orig_uv.y, orig_uv.x, orig_uv.y }; + for (int k = 0; k < 3; k++) { + int idx = orig_tri_id * 3 + k; + float weight = orig_bary[k]; + ray_normals[idx] += normal * weight; + ray_normal_weights[idx] += weight; + } + } + + for (unsigned int j = 0; j < new_index_count; j++) { + if (ray_normal_weights[j] < 1.0f) { // Not enough data, the new normal would be just a bad guess + ray_normals[j] = Vector3(); + } else { + ray_normals[j] /= ray_normal_weights[j]; + } + } + + LocalVector> normal_group_indices; + LocalVector normal_group_averages; + normal_group_indices.reserve(24); + normal_group_averages.reserve(24); + + for (unsigned int j = 0; j < vertex_count; j++) { + const LocalVector &corners = vertex_corners[j]; + const Vector3 &vertex_normal = normals_ptr[j]; + + for (unsigned int k = 0; k < corners.size(); k++) { + const int &corner_idx = corners[k]; + const Vector3 &ray_normal = ray_normals[corner_idx]; + + if (ray_normal.length_squared() < CMP_EPSILON2) { + continue; + } + + bool found = false; + for (unsigned int l = 0; l < normal_group_indices.size(); l++) { + LocalVector &group_indices = normal_group_indices[l]; + Vector3 n = normal_group_averages[l] / group_indices.size(); + if (n.dot(ray_normal) > normal_pre_split_threshold) { + found = true; + group_indices.push_back(corner_idx); + normal_group_averages[l] += ray_normal; + break; + } + } + + if (!found) { + LocalVector new_group; + new_group.push_back(corner_idx); + normal_group_indices.push_back(new_group); + normal_group_averages.push_back(ray_normal); + } + } + + for (unsigned int k = 0; k < normal_group_indices.size(); k++) { + LocalVector &group_indices = normal_group_indices[k]; + Vector3 n = normal_group_averages[k] / group_indices.size(); + + if (vertex_normal.dot(n) < normal_split_threshold) { + split_vertex_indices.push_back(j); + split_vertex_normals.push_back(n); + int new_idx = split_vertex_count++; + for (unsigned int l = 0; l < group_indices.size(); l++) { + new_indices_ptr[group_indices[l]] = new_idx; + } + } + } + + normal_group_indices.clear(); + normal_group_averages.clear(); + } + } + + Surface::LOD lod; + lod.distance = MAX(mesh_error * scale, CMP_EPSILON2); lod.indices = new_indices; - print_line("Lod " + itos(surfaces.write[i].lods.size()) + " begin with " + itos(indices.size() / 3) + " triangles and shoot for " + itos(index_target / 3) + " triangles. Got " + itos(new_len / 3) + " triangles. Lod screen ratio " + rtos(lod.distance)); surfaces.write[i].lods.push_back(lod); - index_target *= threshold; + index_target = MAX(new_index_count, index_target) * 2; + last_index_count = new_index_count; + + if (mesh_error == 0.0f) { + break; + } + } + + surfaces.write[i].split_normals(split_vertex_indices, split_vertex_normals); + surfaces.write[i].lods.sort_custom(); + + for (int j = 0; j < surfaces.write[i].lods.size(); j++) { + Surface::LOD &lod = surfaces.write[i].lods.write[j]; + unsigned int *lod_indices_ptr = (unsigned int *)lod.indices.ptrw(); + SurfaceTool::optimize_vertex_cache_func(lod_indices_ptr, lod_indices_ptr, lod.indices.size(), split_vertex_count); } } } @@ -347,7 +696,7 @@ void EditorSceneImporterMesh::create_shadow_mesh() { Map unique_vertices; const Vector3 *vptr = vertices.ptr(); for (int j = 0; j < vertex_count; j++) { - Vector3 v = vptr[j]; + const Vector3 &v = vptr[j]; Map::Element *E = unique_vertices.find(v); @@ -397,9 +746,9 @@ void EditorSceneImporterMesh::create_shadow_mesh() { index_wptr = new_indices.ptrw(); for (int k = 0; k < index_count; k++) { - int index = index_rptr[j]; + int index = index_rptr[k]; ERR_FAIL_INDEX(index, vertex_count); - index_wptr[j] = vertex_remap[index]; + index_wptr[k] = vertex_remap[index]; } lods[surfaces[i].lods[j].distance] = new_indices; @@ -436,9 +785,9 @@ void EditorSceneImporterMesh::_set_data(const Dictionary &p_data) { if (s.has("lods")) { lods = s["lods"]; } - Array blend_shapes; - if (s.has("blend_shapes")) { - blend_shapes = s["blend_shapes"]; + Array b_shapes; + if (s.has("b_shapes")) { + b_shapes = s["b_shapes"]; } Ref material; if (s.has("material")) { @@ -448,7 +797,7 @@ void EditorSceneImporterMesh::_set_data(const Dictionary &p_data) { if (s.has("flags")) { flags = s["flags"]; } - add_surface(prim, arr, blend_shapes, lods, material, name, flags); + add_surface(prim, arr, b_shapes, lods, material, name, flags); } } } diff --git a/editor/import/scene_importer_mesh.h b/editor/import/scene_importer_mesh.h index d32b1fdf74..111b191cae 100644 --- a/editor/import/scene_importer_mesh.h +++ b/editor/import/scene_importer_mesh.h @@ -32,6 +32,7 @@ #define EDITOR_SCENE_IMPORTER_MESH_H #include "core/io/resource.h" +#include "core/templates/local_vector.h" #include "scene/resources/concave_polygon_shape_3d.h" #include "scene/resources/convex_polygon_shape_3d.h" #include "scene/resources/mesh.h" @@ -55,12 +56,20 @@ class EditorSceneImporterMesh : public Resource { Vector blend_shape_data; struct LOD { Vector indices; - float distance; + float distance = 0.0f; }; Vector lods; Ref material; String name; uint32_t flags = 0; + + struct LODComparator { + _FORCE_INLINE_ bool operator()(const LOD &l, const LOD &r) const { + return l.distance < r.distance; + } + }; + + void split_normals(const LocalVector &p_indices, const LocalVector &p_normals); }; Vector surfaces; Vector blend_shapes; @@ -71,7 +80,6 @@ class EditorSceneImporterMesh : public Resource { Ref shadow_mesh; Size2i lightmap_size_hint; - Basis compute_rotation_matrix_from_ortho_6d(Vector3 p_x_raw, Vector3 y_raw); protected: void _set_data(const Dictionary &p_data); @@ -103,7 +111,7 @@ public: void set_surface_material(int p_surface, const Ref &p_material); - void generate_lods(); + void generate_lods(float p_normal_merge_angle, float p_normal_split_angle); void create_shadow_mesh(); Ref get_shadow_mesh() const; -- cgit v1.2.3