Merge pull request #52544 from JFonS/lod_fixes
Auto LOD fixes and improvements
This commit is contained in:
commit
c370b4c4d0
|
@ -0,0 +1,40 @@
|
|||
/*************************************************************************/
|
||||
/* static_raycaster.cpp */
|
||||
/*************************************************************************/
|
||||
/* This file is part of: */
|
||||
/* GODOT ENGINE */
|
||||
/* https://godotengine.org */
|
||||
/*************************************************************************/
|
||||
/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
|
||||
/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
|
||||
/* */
|
||||
/* Permission is hereby granted, free of charge, to any person obtaining */
|
||||
/* a copy of this software and associated documentation files (the */
|
||||
/* "Software"), to deal in the Software without restriction, including */
|
||||
/* without limitation the rights to use, copy, modify, merge, publish, */
|
||||
/* distribute, sublicense, and/or sell copies of the Software, and to */
|
||||
/* permit persons to whom the Software is furnished to do so, subject to */
|
||||
/* the following conditions: */
|
||||
/* */
|
||||
/* The above copyright notice and this permission notice shall be */
|
||||
/* included in all copies or substantial portions of the Software. */
|
||||
/* */
|
||||
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
|
||||
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
|
||||
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
|
||||
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
|
||||
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
|
||||
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
|
||||
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
|
||||
/*************************************************************************/
|
||||
|
||||
#include "static_raycaster.h"
|
||||
|
||||
StaticRaycaster *(*StaticRaycaster::create_function)() = nullptr;
|
||||
|
||||
Ref<StaticRaycaster> StaticRaycaster::create() {
|
||||
if (create_function) {
|
||||
return Ref<StaticRaycaster>(create_function());
|
||||
}
|
||||
return Ref<StaticRaycaster>();
|
||||
}
|
|
@ -0,0 +1,111 @@
|
|||
/*************************************************************************/
|
||||
/* static_raycaster.h */
|
||||
/*************************************************************************/
|
||||
/* This file is part of: */
|
||||
/* GODOT ENGINE */
|
||||
/* https://godotengine.org */
|
||||
/*************************************************************************/
|
||||
/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
|
||||
/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
|
||||
/* */
|
||||
/* Permission is hereby granted, free of charge, to any person obtaining */
|
||||
/* a copy of this software and associated documentation files (the */
|
||||
/* "Software"), to deal in the Software without restriction, including */
|
||||
/* without limitation the rights to use, copy, modify, merge, publish, */
|
||||
/* distribute, sublicense, and/or sell copies of the Software, and to */
|
||||
/* permit persons to whom the Software is furnished to do so, subject to */
|
||||
/* the following conditions: */
|
||||
/* */
|
||||
/* The above copyright notice and this permission notice shall be */
|
||||
/* included in all copies or substantial portions of the Software. */
|
||||
/* */
|
||||
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
|
||||
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
|
||||
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
|
||||
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
|
||||
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
|
||||
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
|
||||
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
|
||||
/*************************************************************************/
|
||||
|
||||
#ifndef STATIC_RAYCASTER_H
|
||||
#define STATIC_RAYCASTER_H
|
||||
|
||||
#include "core/object/ref_counted.h"
|
||||
|
||||
#if !defined(__aligned)
|
||||
|
||||
#if defined(_WIN32) && defined(_MSC_VER)
|
||||
#define __aligned(...) __declspec(align(__VA_ARGS__))
|
||||
#else
|
||||
#define __aligned(...) __attribute__((aligned(__VA_ARGS__)))
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
||||
class StaticRaycaster : public RefCounted {
|
||||
GDCLASS(StaticRaycaster, RefCounted)
|
||||
protected:
|
||||
static StaticRaycaster *(*create_function)();
|
||||
|
||||
public:
|
||||
// compatible with embree3 rays
|
||||
struct __aligned(16) Ray {
|
||||
const static unsigned int INVALID_GEOMETRY_ID = ((unsigned int)-1); // from rtcore_common.h
|
||||
|
||||
/*! Default construction does nothing. */
|
||||
_FORCE_INLINE_ Ray() :
|
||||
geomID(INVALID_GEOMETRY_ID) {}
|
||||
|
||||
/*! Constructs a ray from origin, direction, and ray segment. Near
|
||||
* has to be smaller than far. */
|
||||
_FORCE_INLINE_ Ray(const Vector3 &org,
|
||||
const Vector3 &dir,
|
||||
float tnear = 0.0f,
|
||||
float tfar = INFINITY) :
|
||||
org(org),
|
||||
tnear(tnear),
|
||||
dir(dir),
|
||||
time(0.0f),
|
||||
tfar(tfar),
|
||||
mask(-1),
|
||||
u(0.0),
|
||||
v(0.0),
|
||||
primID(INVALID_GEOMETRY_ID),
|
||||
geomID(INVALID_GEOMETRY_ID),
|
||||
instID(INVALID_GEOMETRY_ID) {}
|
||||
|
||||
/*! Tests if we hit something. */
|
||||
_FORCE_INLINE_ explicit operator bool() const { return geomID != INVALID_GEOMETRY_ID; }
|
||||
|
||||
public:
|
||||
Vector3 org; //!< Ray origin + tnear
|
||||
float tnear; //!< Start of ray segment
|
||||
Vector3 dir; //!< Ray direction + tfar
|
||||
float time; //!< Time of this ray for motion blur.
|
||||
float tfar; //!< End of ray segment
|
||||
unsigned int mask; //!< used to mask out objects during traversal
|
||||
unsigned int id; //!< ray ID
|
||||
unsigned int flags; //!< ray flags
|
||||
|
||||
Vector3 normal; //!< Not normalized geometry normal
|
||||
float u; //!< Barycentric u coordinate of hit
|
||||
float v; //!< Barycentric v coordinate of hit
|
||||
unsigned int primID; //!< primitive ID
|
||||
unsigned int geomID; //!< geometry ID
|
||||
unsigned int instID; //!< instance ID
|
||||
};
|
||||
|
||||
virtual bool intersect(Ray &p_ray) = 0;
|
||||
virtual void intersect(Vector<Ray> &r_rays) = 0;
|
||||
|
||||
virtual void add_mesh(const PackedVector3Array &p_vertices, const PackedInt32Array &p_indices, unsigned int p_id) = 0;
|
||||
virtual void commit() = 0;
|
||||
|
||||
virtual void set_mesh_filter(const Set<int> &p_mesh_ids) = 0;
|
||||
virtual void clear_mesh_filter() = 0;
|
||||
|
||||
static Ref<StaticRaycaster> create();
|
||||
};
|
||||
|
||||
#endif // STATIC_RAYCASTER_H
|
|
@ -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();
|
||||
}
|
||||
|
|
|
@ -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 <cstdint>
|
||||
|
||||
void EditorSceneImporterMesh::Surface::split_normals(const LocalVector<int> &p_indices, const LocalVector<Vector3> &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<Mate
|
|||
mesh.unref();
|
||||
}
|
||||
|
||||
Basis EditorSceneImporterMesh::compute_rotation_matrix_from_ortho_6d(Vector3 p_x_raw, Vector3 p_y_raw) {
|
||||
Vector3 x = p_x_raw.normalized();
|
||||
Vector3 z = x.cross(p_y_raw);
|
||||
z = z.normalized();
|
||||
Vector3 y = z.cross(x);
|
||||
Basis basis;
|
||||
basis.set_axis(Vector3::AXIS_X, x);
|
||||
basis.set_axis(Vector3::AXIS_Y, y);
|
||||
basis.set_axis(Vector3::AXIS_Z, z);
|
||||
return basis;
|
||||
}
|
||||
|
||||
void EditorSceneImporterMesh::generate_lods() {
|
||||
if (!SurfaceTool::simplify_func) {
|
||||
return;
|
||||
}
|
||||
void EditorSceneImporterMesh::generate_lods(float p_normal_merge_angle, float p_normal_split_angle) {
|
||||
if (!SurfaceTool::simplify_scale_func) {
|
||||
return;
|
||||
}
|
||||
if (!SurfaceTool::simplify_sloppy_func) {
|
||||
if (!SurfaceTool::simplify_with_attrib_func) {
|
||||
return;
|
||||
}
|
||||
if (!SurfaceTool::simplify_with_attrib_func) {
|
||||
if (!SurfaceTool::optimize_vertex_cache_func) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -190,67 +263,343 @@ void EditorSceneImporterMesh::generate_lods() {
|
|||
|
||||
surfaces.write[i].lods.clear();
|
||||
Vector<Vector3> vertices = surfaces[i].arrays[RS::ARRAY_VERTEX];
|
||||
Vector<int> indices = surfaces[i].arrays[RS::ARRAY_INDEX];
|
||||
if (indices.size() == 0) {
|
||||
PackedInt32Array indices = surfaces[i].arrays[RS::ARRAY_INDEX];
|
||||
Vector<Vector3> normals = surfaces[i].arrays[RS::ARRAY_NORMAL];
|
||||
Vector<Vector2> 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<Vector3> normals = surfaces[i].arrays[RS::ARRAY_NORMAL];
|
||||
uint32_t vertex_count = vertices.size();
|
||||
|
||||
const Vector3 *vertices_ptr = vertices.ptr();
|
||||
Vector<float> attributes;
|
||||
Vector<float> 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;
|
||||
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;
|
||||
}
|
||||
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;
|
||||
}
|
||||
} 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<Vector3, LocalVector<Pair<int, int>>> unique_vertices;
|
||||
|
||||
LocalVector<int> vertex_remap;
|
||||
LocalVector<int> vertex_inverse_remap;
|
||||
LocalVector<Vector3> merged_vertices;
|
||||
LocalVector<Vector3> merged_normals;
|
||||
LocalVector<int> 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<Vector3, LocalVector<Pair<int, int>>>::Element *E = unique_vertices.find(v);
|
||||
|
||||
if (E) {
|
||||
const LocalVector<Pair<int, int>> &close_verts = E->get();
|
||||
|
||||
bool found = false;
|
||||
for (unsigned int k = 0; k < close_verts.size(); k++) {
|
||||
const Pair<int, int> &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<int, int>(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<Pair<int, int>>();
|
||||
unique_vertices[v].push_back(Pair<int, int>(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<int> 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<float> 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<int> 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)) {
|
||||
|
||||
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<Vector3> split_vertex_normals;
|
||||
LocalVector<int> 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<StaticRaycaster> 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;
|
||||
}
|
||||
|
||||
if (new_index_count <= 0 || (new_index_count >= (index_count * 0.75f))) {
|
||||
break;
|
||||
}
|
||||
|
||||
new_indices.resize(new_index_count);
|
||||
|
||||
LocalVector<LocalVector<int>> 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;
|
||||
}
|
||||
}
|
||||
|
||||
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<StaticRaycaster::Ray> rays;
|
||||
LocalVector<Vector2> 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<Vector3> ray_normals;
|
||||
LocalVector<float> 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<LocalVector<int>> normal_group_indices;
|
||||
LocalVector<Vector3> normal_group_averages;
|
||||
normal_group_indices.reserve(24);
|
||||
normal_group_averages.reserve(24);
|
||||
|
||||
for (unsigned int j = 0; j < vertex_count; j++) {
|
||||
const LocalVector<int> &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<int> &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<int> 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<int> &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 = mesh_error * scale;
|
||||
if (Math::is_zero_approx(mesh_error)) {
|
||||
break;
|
||||
}
|
||||
if (new_len <= 0) {
|
||||
break;
|
||||
}
|
||||
new_indices.resize(new_len);
|
||||
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<Surface::LODComparator>();
|
||||
|
||||
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<Vector3, int> 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<Vector3, int>::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> 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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<BlendShape> blend_shape_data;
|
||||
struct LOD {
|
||||
Vector<int> indices;
|
||||
float distance;
|
||||
float distance = 0.0f;
|
||||
};
|
||||
Vector<LOD> lods;
|
||||
Ref<Material> 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<int> &p_indices, const LocalVector<Vector3> &p_normals);
|
||||
};
|
||||
Vector<Surface> surfaces;
|
||||
Vector<String> blend_shapes;
|
||||
|
@ -71,7 +80,6 @@ class EditorSceneImporterMesh : public Resource {
|
|||
Ref<EditorSceneImporterMesh> 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<Material> &p_material);
|
||||
|
||||
void generate_lods();
|
||||
void generate_lods(float p_normal_merge_angle, float p_normal_split_angle);
|
||||
|
||||
void create_shadow_mesh();
|
||||
Ref<EditorSceneImporterMesh> get_shadow_mesh() const;
|
||||
|
|
|
@ -2601,6 +2601,9 @@ void Node3DEditorViewport::_project_settings_changed() {
|
|||
|
||||
const bool use_occlusion_culling = GLOBAL_GET("rendering/occlusion_culling/use_occlusion_culling");
|
||||
viewport->set_use_occlusion_culling(use_occlusion_culling);
|
||||
|
||||
const float lod_threshold = GLOBAL_GET("rendering/mesh_lod/lod_change/threshold_pixels");
|
||||
viewport->set_lod_threshold(lod_threshold);
|
||||
}
|
||||
|
||||
void Node3DEditorViewport::_notification(int p_what) {
|
||||
|
|
|
@ -168,7 +168,7 @@ void LightmapRaycasterEmbree::clear_mesh_filter() {
|
|||
filter_meshes.clear();
|
||||
}
|
||||
|
||||
void embree_error_handler(void *p_user_data, RTCError p_code, const char *p_str) {
|
||||
void embree_lm_error_handler(void *p_user_data, RTCError p_code, const char *p_str) {
|
||||
print_error("Embree error: " + String(p_str));
|
||||
}
|
||||
|
||||
|
@ -179,16 +179,11 @@ LightmapRaycasterEmbree::LightmapRaycasterEmbree() {
|
|||
#endif
|
||||
|
||||
embree_device = rtcNewDevice(nullptr);
|
||||
rtcSetDeviceErrorFunction(embree_device, &embree_error_handler, nullptr);
|
||||
rtcSetDeviceErrorFunction(embree_device, &embree_lm_error_handler, nullptr);
|
||||
embree_scene = rtcNewScene(embree_device);
|
||||
}
|
||||
|
||||
LightmapRaycasterEmbree::~LightmapRaycasterEmbree() {
|
||||
#ifdef __SSE2__
|
||||
_MM_SET_FLUSH_ZERO_MODE(_MM_FLUSH_ZERO_OFF);
|
||||
_MM_SET_DENORMALS_ZERO_MODE(_MM_DENORMALS_ZERO_OFF);
|
||||
#endif
|
||||
|
||||
if (embree_scene != nullptr) {
|
||||
rtcReleaseScene(embree_scene);
|
||||
}
|
||||
|
|
|
@ -30,9 +30,9 @@
|
|||
|
||||
#ifdef TOOLS_ENABLED
|
||||
|
||||
#include "core/io/image.h"
|
||||
#include "core/object/object.h"
|
||||
#include "scene/3d/lightmapper.h"
|
||||
#include "scene/resources/mesh.h"
|
||||
|
||||
#include <embree3/rtcore.h>
|
||||
|
||||
|
|
|
@ -32,12 +32,14 @@
|
|||
|
||||
#include "lightmap_raycaster.h"
|
||||
#include "raycast_occlusion_cull.h"
|
||||
#include "static_raycaster.h"
|
||||
|
||||
RaycastOcclusionCull *raycast_occlusion_cull = nullptr;
|
||||
|
||||
void register_raycast_types() {
|
||||
#ifdef TOOLS_ENABLED
|
||||
LightmapRaycasterEmbree::make_default_raycaster();
|
||||
StaticRaycasterEmbree::make_default_raycaster();
|
||||
#endif
|
||||
raycast_occlusion_cull = memnew(RaycastOcclusionCull);
|
||||
}
|
||||
|
@ -46,4 +48,7 @@ void unregister_raycast_types() {
|
|||
if (raycast_occlusion_cull) {
|
||||
memdelete(raycast_occlusion_cull);
|
||||
}
|
||||
#ifdef TOOLS_ENABLED
|
||||
StaticRaycasterEmbree::free();
|
||||
#endif
|
||||
}
|
||||
|
|
|
@ -0,0 +1,137 @@
|
|||
/*************************************************************************/
|
||||
/* static_raycaster.cpp */
|
||||
/*************************************************************************/
|
||||
/* This file is part of: */
|
||||
/* GODOT ENGINE */
|
||||
/* https://godotengine.org */
|
||||
/*************************************************************************/
|
||||
/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
|
||||
/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
|
||||
/* */
|
||||
/* Permission is hereby granted, free of charge, to any person obtaining */
|
||||
/* a copy of this software and associated documentation files (the */
|
||||
/* "Software"), to deal in the Software without restriction, including */
|
||||
/* without limitation the rights to use, copy, modify, merge, publish, */
|
||||
/* distribute, sublicense, and/or sell copies of the Software, and to */
|
||||
/* permit persons to whom the Software is furnished to do so, subject to */
|
||||
/* the following conditions: */
|
||||
/* */
|
||||
/* The above copyright notice and this permission notice shall be */
|
||||
/* included in all copies or substantial portions of the Software. */
|
||||
/* */
|
||||
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
|
||||
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
|
||||
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
|
||||
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
|
||||
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
|
||||
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
|
||||
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
|
||||
/*************************************************************************/
|
||||
|
||||
#ifdef TOOLS_ENABLED
|
||||
|
||||
#include "static_raycaster.h"
|
||||
|
||||
#ifdef __SSE2__
|
||||
#include <pmmintrin.h>
|
||||
#endif
|
||||
|
||||
RTCDevice StaticRaycasterEmbree::embree_device;
|
||||
|
||||
StaticRaycaster *StaticRaycasterEmbree::create_embree_raycaster() {
|
||||
return memnew(StaticRaycasterEmbree);
|
||||
}
|
||||
|
||||
void StaticRaycasterEmbree::make_default_raycaster() {
|
||||
create_function = create_embree_raycaster;
|
||||
}
|
||||
|
||||
void StaticRaycasterEmbree::free() {
|
||||
if (embree_device) {
|
||||
rtcReleaseDevice(embree_device);
|
||||
}
|
||||
}
|
||||
|
||||
bool StaticRaycasterEmbree::intersect(Ray &r_ray) {
|
||||
RTCIntersectContext context;
|
||||
rtcInitIntersectContext(&context);
|
||||
rtcIntersect1(embree_scene, &context, (RTCRayHit *)&r_ray);
|
||||
return r_ray.geomID != RTC_INVALID_GEOMETRY_ID;
|
||||
}
|
||||
|
||||
void StaticRaycasterEmbree::intersect(Vector<Ray> &r_rays) {
|
||||
Ray *rays = r_rays.ptrw();
|
||||
for (int i = 0; i < r_rays.size(); ++i) {
|
||||
intersect(rays[i]);
|
||||
}
|
||||
}
|
||||
|
||||
void StaticRaycasterEmbree::add_mesh(const PackedVector3Array &p_vertices, const PackedInt32Array &p_indices, unsigned int p_id) {
|
||||
RTCGeometry embree_mesh = rtcNewGeometry(embree_device, RTC_GEOMETRY_TYPE_TRIANGLE);
|
||||
|
||||
int vertex_count = p_vertices.size();
|
||||
|
||||
Vector3 *embree_vertices = (Vector3 *)rtcSetNewGeometryBuffer(embree_mesh, RTC_BUFFER_TYPE_VERTEX, 0, RTC_FORMAT_FLOAT3, sizeof(Vector3), vertex_count);
|
||||
memcpy(embree_vertices, p_vertices.ptr(), sizeof(Vector3) * vertex_count);
|
||||
|
||||
if (p_indices.is_empty()) {
|
||||
ERR_FAIL_COND(vertex_count % 3 != 0);
|
||||
uint32_t *embree_triangles = (uint32_t *)rtcSetNewGeometryBuffer(embree_mesh, RTC_BUFFER_TYPE_INDEX, 0, RTC_FORMAT_UINT3, sizeof(uint32_t) * 3, vertex_count / 3);
|
||||
for (int i = 0; i < vertex_count; i++) {
|
||||
embree_triangles[i] = i;
|
||||
}
|
||||
} else {
|
||||
uint32_t *embree_triangles = (uint32_t *)rtcSetNewGeometryBuffer(embree_mesh, RTC_BUFFER_TYPE_INDEX, 0, RTC_FORMAT_UINT3, sizeof(uint32_t) * 3, p_indices.size() / 3);
|
||||
memcpy(embree_triangles, p_indices.ptr(), sizeof(uint32_t) * p_indices.size());
|
||||
}
|
||||
|
||||
rtcCommitGeometry(embree_mesh);
|
||||
rtcAttachGeometryByID(embree_scene, embree_mesh, p_id);
|
||||
rtcReleaseGeometry(embree_mesh);
|
||||
}
|
||||
|
||||
void StaticRaycasterEmbree::commit() {
|
||||
rtcCommitScene(embree_scene);
|
||||
}
|
||||
|
||||
void StaticRaycasterEmbree::set_mesh_filter(const Set<int> &p_mesh_ids) {
|
||||
for (Set<int>::Element *E = p_mesh_ids.front(); E; E = E->next()) {
|
||||
rtcDisableGeometry(rtcGetGeometry(embree_scene, E->get()));
|
||||
}
|
||||
rtcCommitScene(embree_scene);
|
||||
filter_meshes = p_mesh_ids;
|
||||
}
|
||||
|
||||
void StaticRaycasterEmbree::clear_mesh_filter() {
|
||||
for (Set<int>::Element *E = filter_meshes.front(); E; E = E->next()) {
|
||||
rtcEnableGeometry(rtcGetGeometry(embree_scene, E->get()));
|
||||
}
|
||||
rtcCommitScene(embree_scene);
|
||||
filter_meshes.clear();
|
||||
}
|
||||
|
||||
void embree_error_handler(void *p_user_data, RTCError p_code, const char *p_str) {
|
||||
print_error("Embree error: " + String(p_str));
|
||||
}
|
||||
|
||||
StaticRaycasterEmbree::StaticRaycasterEmbree() {
|
||||
#ifdef __SSE2__
|
||||
_MM_SET_FLUSH_ZERO_MODE(_MM_FLUSH_ZERO_ON);
|
||||
_MM_SET_DENORMALS_ZERO_MODE(_MM_DENORMALS_ZERO_ON);
|
||||
#endif
|
||||
|
||||
if (!embree_device) {
|
||||
embree_device = rtcNewDevice(nullptr);
|
||||
rtcSetDeviceErrorFunction(embree_device, &embree_error_handler, nullptr);
|
||||
}
|
||||
|
||||
embree_scene = rtcNewScene(embree_device);
|
||||
}
|
||||
|
||||
StaticRaycasterEmbree::~StaticRaycasterEmbree() {
|
||||
if (embree_scene != nullptr) {
|
||||
rtcReleaseScene(embree_scene);
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
|
@ -0,0 +1,64 @@
|
|||
/*************************************************************************/
|
||||
/* static_raycaster.h */
|
||||
/*************************************************************************/
|
||||
/* This file is part of: */
|
||||
/* GODOT ENGINE */
|
||||
/* https://godotengine.org */
|
||||
/*************************************************************************/
|
||||
/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
|
||||
/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
|
||||
/* */
|
||||
/* Permission is hereby granted, free of charge, to any person obtaining */
|
||||
/* a copy of this software and associated documentation files (the */
|
||||
/* "Software"), to deal in the Software without restriction, including */
|
||||
/* without limitation the rights to use, copy, modify, merge, publish, */
|
||||
/* distribute, sublicense, and/or sell copies of the Software, and to */
|
||||
/* permit persons to whom the Software is furnished to do so, subject to */
|
||||
/* the following conditions: */
|
||||
/* */
|
||||
/* The above copyright notice and this permission notice shall be */
|
||||
/* included in all copies or substantial portions of the Software. */
|
||||
/* */
|
||||
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
|
||||
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
|
||||
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
|
||||
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
|
||||
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
|
||||
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
|
||||
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
|
||||
/*************************************************************************/
|
||||
|
||||
#ifdef TOOLS_ENABLED
|
||||
|
||||
#include "core/math/static_raycaster.h"
|
||||
|
||||
#include <embree3/rtcore.h>
|
||||
|
||||
class StaticRaycasterEmbree : public StaticRaycaster {
|
||||
GDCLASS(StaticRaycasterEmbree, StaticRaycaster);
|
||||
|
||||
private:
|
||||
static RTCDevice embree_device;
|
||||
RTCScene embree_scene;
|
||||
|
||||
Set<int> filter_meshes;
|
||||
|
||||
public:
|
||||
virtual bool intersect(Ray &p_ray) override;
|
||||
virtual void intersect(Vector<Ray> &r_rays) override;
|
||||
|
||||
virtual void add_mesh(const PackedVector3Array &p_vertices, const PackedInt32Array &p_indices, unsigned int p_id) override;
|
||||
virtual void commit() override;
|
||||
|
||||
virtual void set_mesh_filter(const Set<int> &p_mesh_ids) override;
|
||||
virtual void clear_mesh_filter() override;
|
||||
|
||||
static StaticRaycaster *create_embree_raycaster();
|
||||
static void make_default_raycaster();
|
||||
static void free();
|
||||
|
||||
StaticRaycasterEmbree();
|
||||
~StaticRaycasterEmbree();
|
||||
};
|
||||
|
||||
#endif
|
|
@ -295,7 +295,7 @@ public:
|
|||
|
||||
AABB aabb;
|
||||
struct LOD {
|
||||
float edge_length;
|
||||
float edge_length = 0.0f;
|
||||
Vector<uint8_t> index_data;
|
||||
};
|
||||
Vector<LOD> lods;
|
||||
|
|
|
@ -373,7 +373,9 @@ Files extracted from upstream repository:
|
|||
- `LICENSE.md`.
|
||||
|
||||
An [experimental upstream feature](https://github.com/zeux/meshoptimizer/tree/simplify-attr),
|
||||
has been backported, see patch in `patches` directory.
|
||||
has been backported. On top of that, it was modified to report only distance error metrics
|
||||
instead of a combination of distance and attribute errors. Patches for both changes can be
|
||||
found in the `patches` directory.
|
||||
|
||||
|
||||
## miniupnpc
|
||||
|
|
176
thirdparty/meshoptimizer/patches/attribute-aware-simplify-distance-only-metric.patch
vendored
Normal file
176
thirdparty/meshoptimizer/patches/attribute-aware-simplify-distance-only-metric.patch
vendored
Normal file
|
@ -0,0 +1,176 @@
|
|||
diff --git a/thirdparty/meshoptimizer/simplifier.cpp b/thirdparty/meshoptimizer/simplifier.cpp
|
||||
index 0f10ebef4b..cf5db4e119 100644
|
||||
--- a/thirdparty/meshoptimizer/simplifier.cpp
|
||||
+++ b/thirdparty/meshoptimizer/simplifier.cpp
|
||||
@@ -20,7 +20,7 @@
|
||||
#define TRACESTATS(i) (void)0
|
||||
#endif
|
||||
|
||||
-#define ATTRIBUTES 8
|
||||
+#define ATTRIBUTES 3
|
||||
|
||||
// This work is based on:
|
||||
// Michael Garland and Paul S. Heckbert. Surface simplification using quadric error metrics. 1997
|
||||
@@ -445,6 +445,7 @@ struct Collapse
|
||||
float error;
|
||||
unsigned int errorui;
|
||||
};
|
||||
+ float distance_error;
|
||||
};
|
||||
|
||||
static float normalize(Vector3& v)
|
||||
@@ -525,6 +526,34 @@ static float quadricError(const Quadric& Q, const Vector3& v)
|
||||
return fabsf(r) * s;
|
||||
}
|
||||
|
||||
+static float quadricErrorNoAttributes(const Quadric& Q, const Vector3& v)
|
||||
+{
|
||||
+ float rx = Q.b0;
|
||||
+ float ry = Q.b1;
|
||||
+ float rz = Q.b2;
|
||||
+
|
||||
+ rx += Q.a10 * v.y;
|
||||
+ ry += Q.a21 * v.z;
|
||||
+ rz += Q.a20 * v.x;
|
||||
+
|
||||
+ rx *= 2;
|
||||
+ ry *= 2;
|
||||
+ rz *= 2;
|
||||
+
|
||||
+ rx += Q.a00 * v.x;
|
||||
+ ry += Q.a11 * v.y;
|
||||
+ rz += Q.a22 * v.z;
|
||||
+
|
||||
+ float r = Q.c;
|
||||
+ r += rx * v.x;
|
||||
+ r += ry * v.y;
|
||||
+ r += rz * v.z;
|
||||
+
|
||||
+ float s = Q.w == 0.f ? 0.f : 1.f / Q.w;
|
||||
+
|
||||
+ return fabsf(r) * s;
|
||||
+}
|
||||
+
|
||||
static void quadricFromPlane(Quadric& Q, float a, float b, float c, float d, float w)
|
||||
{
|
||||
float aw = a * w;
|
||||
@@ -680,7 +709,7 @@ static void quadricUpdateAttributes(Quadric& Q, const Vector3& p0, const Vector3
|
||||
}
|
||||
#endif
|
||||
|
||||
-static void fillFaceQuadrics(Quadric* vertex_quadrics, const unsigned int* indices, size_t index_count, const Vector3* vertex_positions, const unsigned int* remap)
|
||||
+static void fillFaceQuadrics(Quadric* vertex_quadrics, Quadric* vertex_no_attrib_quadrics, const unsigned int* indices, size_t index_count, const Vector3* vertex_positions, const unsigned int* remap)
|
||||
{
|
||||
for (size_t i = 0; i < index_count; i += 3)
|
||||
{
|
||||
@@ -690,6 +719,9 @@ static void fillFaceQuadrics(Quadric* vertex_quadrics, const unsigned int* indic
|
||||
|
||||
Quadric Q;
|
||||
quadricFromTriangle(Q, vertex_positions[i0], vertex_positions[i1], vertex_positions[i2], 1.f);
|
||||
+ quadricAdd(vertex_no_attrib_quadrics[remap[i0]], Q);
|
||||
+ quadricAdd(vertex_no_attrib_quadrics[remap[i1]], Q);
|
||||
+ quadricAdd(vertex_no_attrib_quadrics[remap[i2]], Q);
|
||||
|
||||
#if ATTRIBUTES
|
||||
quadricUpdateAttributes(Q, vertex_positions[i0], vertex_positions[i1], vertex_positions[i2], Q.w);
|
||||
@@ -700,7 +732,7 @@ static void fillFaceQuadrics(Quadric* vertex_quadrics, const unsigned int* indic
|
||||
}
|
||||
}
|
||||
|
||||
-static void fillEdgeQuadrics(Quadric* vertex_quadrics, const unsigned int* indices, size_t index_count, const Vector3* vertex_positions, const unsigned int* remap, const unsigned char* vertex_kind, const unsigned int* loop, const unsigned int* loopback)
|
||||
+static void fillEdgeQuadrics(Quadric* vertex_quadrics, Quadric* vertex_no_attrib_quadrics, const unsigned int* indices, size_t index_count, const Vector3* vertex_positions, const unsigned int* remap, const unsigned char* vertex_kind, const unsigned int* loop, const unsigned int* loopback)
|
||||
{
|
||||
for (size_t i = 0; i < index_count; i += 3)
|
||||
{
|
||||
@@ -744,6 +776,9 @@ static void fillEdgeQuadrics(Quadric* vertex_quadrics, const unsigned int* indic
|
||||
|
||||
quadricAdd(vertex_quadrics[remap[i0]], Q);
|
||||
quadricAdd(vertex_quadrics[remap[i1]], Q);
|
||||
+
|
||||
+ quadricAdd(vertex_no_attrib_quadrics[remap[i0]], Q);
|
||||
+ quadricAdd(vertex_no_attrib_quadrics[remap[i1]], Q);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -848,7 +883,7 @@ static size_t pickEdgeCollapses(Collapse* collapses, const unsigned int* indices
|
||||
return collapse_count;
|
||||
}
|
||||
|
||||
-static void rankEdgeCollapses(Collapse* collapses, size_t collapse_count, const Vector3* vertex_positions, const Quadric* vertex_quadrics, const unsigned int* remap)
|
||||
+static void rankEdgeCollapses(Collapse* collapses, size_t collapse_count, const Vector3* vertex_positions, const Quadric* vertex_quadrics, const Quadric* vertex_no_attrib_quadrics, const unsigned int* remap)
|
||||
{
|
||||
for (size_t i = 0; i < collapse_count; ++i)
|
||||
{
|
||||
@@ -868,10 +903,14 @@ static void rankEdgeCollapses(Collapse* collapses, size_t collapse_count, const
|
||||
float ei = quadricError(qi, vertex_positions[i1]);
|
||||
float ej = quadricError(qj, vertex_positions[j1]);
|
||||
|
||||
+ const Quadric& naqi = vertex_no_attrib_quadrics[remap[i0]];
|
||||
+ const Quadric& naqj = vertex_no_attrib_quadrics[remap[j0]];
|
||||
+
|
||||
// pick edge direction with minimal error
|
||||
c.v0 = ei <= ej ? i0 : j0;
|
||||
c.v1 = ei <= ej ? i1 : j1;
|
||||
c.error = ei <= ej ? ei : ej;
|
||||
+ c.distance_error = ei <= ej ? quadricErrorNoAttributes(naqi, vertex_positions[i1]) : quadricErrorNoAttributes(naqj, vertex_positions[j1]);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -968,7 +1007,7 @@ static void sortEdgeCollapses(unsigned int* sort_order, const Collapse* collapse
|
||||
}
|
||||
}
|
||||
|
||||
-static size_t performEdgeCollapses(unsigned int* collapse_remap, unsigned char* collapse_locked, Quadric* vertex_quadrics, const Collapse* collapses, size_t collapse_count, const unsigned int* collapse_order, const unsigned int* remap, const unsigned int* wedge, const unsigned char* vertex_kind, const Vector3* vertex_positions, const EdgeAdjacency& adjacency, size_t triangle_collapse_goal, float error_limit, float& result_error)
|
||||
+static size_t performEdgeCollapses(unsigned int* collapse_remap, unsigned char* collapse_locked, Quadric* vertex_quadrics, Quadric* vertex_no_attrib_quadrics, const Collapse* collapses, size_t collapse_count, const unsigned int* collapse_order, const unsigned int* remap, const unsigned int* wedge, const unsigned char* vertex_kind, const Vector3* vertex_positions, const EdgeAdjacency& adjacency, size_t triangle_collapse_goal, float error_limit, float& result_error)
|
||||
{
|
||||
size_t edge_collapses = 0;
|
||||
size_t triangle_collapses = 0;
|
||||
@@ -1030,6 +1069,7 @@ static size_t performEdgeCollapses(unsigned int* collapse_remap, unsigned char*
|
||||
assert(collapse_remap[r1] == r1);
|
||||
|
||||
quadricAdd(vertex_quadrics[r1], vertex_quadrics[r0]);
|
||||
+ quadricAdd(vertex_no_attrib_quadrics[r1], vertex_no_attrib_quadrics[r0]);
|
||||
|
||||
if (vertex_kind[i0] == Kind_Complex)
|
||||
{
|
||||
@@ -1067,7 +1107,7 @@ static size_t performEdgeCollapses(unsigned int* collapse_remap, unsigned char*
|
||||
triangle_collapses += (vertex_kind[i0] == Kind_Border) ? 1 : 2;
|
||||
edge_collapses++;
|
||||
|
||||
- result_error = result_error < c.error ? c.error : result_error;
|
||||
+ result_error = result_error < c.distance_error ? c.distance_error : result_error;
|
||||
}
|
||||
|
||||
#if TRACE
|
||||
@@ -1455,9 +1495,11 @@ size_t meshopt_simplifyWithAttributes(unsigned int* destination, const unsigned
|
||||
|
||||
Quadric* vertex_quadrics = allocator.allocate<Quadric>(vertex_count);
|
||||
memset(vertex_quadrics, 0, vertex_count * sizeof(Quadric));
|
||||
+ Quadric* vertex_no_attrib_quadrics = allocator.allocate<Quadric>(vertex_count);
|
||||
+ memset(vertex_no_attrib_quadrics, 0, vertex_count * sizeof(Quadric));
|
||||
|
||||
- fillFaceQuadrics(vertex_quadrics, indices, index_count, vertex_positions, remap);
|
||||
- fillEdgeQuadrics(vertex_quadrics, indices, index_count, vertex_positions, remap, vertex_kind, loop, loopback);
|
||||
+ fillFaceQuadrics(vertex_quadrics, vertex_no_attrib_quadrics, indices, index_count, vertex_positions, remap);
|
||||
+ fillEdgeQuadrics(vertex_quadrics, vertex_no_attrib_quadrics, indices, index_count, vertex_positions, remap, vertex_kind, loop, loopback);
|
||||
|
||||
if (result != indices)
|
||||
memcpy(result, indices, index_count * sizeof(unsigned int));
|
||||
@@ -1488,7 +1530,7 @@ size_t meshopt_simplifyWithAttributes(unsigned int* destination, const unsigned
|
||||
if (edge_collapse_count == 0)
|
||||
break;
|
||||
|
||||
- rankEdgeCollapses(edge_collapses, edge_collapse_count, vertex_positions, vertex_quadrics, remap);
|
||||
+ rankEdgeCollapses(edge_collapses, edge_collapse_count, vertex_positions, vertex_quadrics, vertex_no_attrib_quadrics, remap);
|
||||
|
||||
#if TRACE > 1
|
||||
dumpEdgeCollapses(edge_collapses, edge_collapse_count, vertex_kind);
|
||||
@@ -1507,7 +1549,7 @@ size_t meshopt_simplifyWithAttributes(unsigned int* destination, const unsigned
|
||||
printf("pass %d: ", int(pass_count++));
|
||||
#endif
|
||||
|
||||
- size_t collapses = performEdgeCollapses(collapse_remap, collapse_locked, vertex_quadrics, edge_collapses, edge_collapse_count, collapse_order, remap, wedge, vertex_kind, vertex_positions, adjacency, triangle_collapse_goal, error_limit, result_error);
|
||||
+ size_t collapses = performEdgeCollapses(collapse_remap, collapse_locked, vertex_quadrics, vertex_no_attrib_quadrics, edge_collapses, edge_collapse_count, collapse_order, remap, wedge, vertex_kind, vertex_positions, adjacency, triangle_collapse_goal, error_limit, result_error);
|
||||
|
||||
// no edges can be collapsed any more due to hitting the error limit or triangle collapse limit
|
||||
if (collapses == 0)
|
|
@ -20,7 +20,7 @@
|
|||
#define TRACESTATS(i) (void)0
|
||||
#endif
|
||||
|
||||
#define ATTRIBUTES 8
|
||||
#define ATTRIBUTES 3
|
||||
|
||||
// This work is based on:
|
||||
// Michael Garland and Paul S. Heckbert. Surface simplification using quadric error metrics. 1997
|
||||
|
@ -445,6 +445,7 @@ struct Collapse
|
|||
float error;
|
||||
unsigned int errorui;
|
||||
};
|
||||
float distance_error;
|
||||
};
|
||||
|
||||
static float normalize(Vector3& v)
|
||||
|
@ -525,6 +526,34 @@ static float quadricError(const Quadric& Q, const Vector3& v)
|
|||
return fabsf(r) * s;
|
||||
}
|
||||
|
||||
static float quadricErrorNoAttributes(const Quadric& Q, const Vector3& v)
|
||||
{
|
||||
float rx = Q.b0;
|
||||
float ry = Q.b1;
|
||||
float rz = Q.b2;
|
||||
|
||||
rx += Q.a10 * v.y;
|
||||
ry += Q.a21 * v.z;
|
||||
rz += Q.a20 * v.x;
|
||||
|
||||
rx *= 2;
|
||||
ry *= 2;
|
||||
rz *= 2;
|
||||
|
||||
rx += Q.a00 * v.x;
|
||||
ry += Q.a11 * v.y;
|
||||
rz += Q.a22 * v.z;
|
||||
|
||||
float r = Q.c;
|
||||
r += rx * v.x;
|
||||
r += ry * v.y;
|
||||
r += rz * v.z;
|
||||
|
||||
float s = Q.w == 0.f ? 0.f : 1.f / Q.w;
|
||||
|
||||
return fabsf(r) * s;
|
||||
}
|
||||
|
||||
static void quadricFromPlane(Quadric& Q, float a, float b, float c, float d, float w)
|
||||
{
|
||||
float aw = a * w;
|
||||
|
@ -680,7 +709,7 @@ static void quadricUpdateAttributes(Quadric& Q, const Vector3& p0, const Vector3
|
|||
}
|
||||
#endif
|
||||
|
||||
static void fillFaceQuadrics(Quadric* vertex_quadrics, const unsigned int* indices, size_t index_count, const Vector3* vertex_positions, const unsigned int* remap)
|
||||
static void fillFaceQuadrics(Quadric* vertex_quadrics, Quadric* vertex_no_attrib_quadrics, const unsigned int* indices, size_t index_count, const Vector3* vertex_positions, const unsigned int* remap)
|
||||
{
|
||||
for (size_t i = 0; i < index_count; i += 3)
|
||||
{
|
||||
|
@ -690,6 +719,9 @@ static void fillFaceQuadrics(Quadric* vertex_quadrics, const unsigned int* indic
|
|||
|
||||
Quadric Q;
|
||||
quadricFromTriangle(Q, vertex_positions[i0], vertex_positions[i1], vertex_positions[i2], 1.f);
|
||||
quadricAdd(vertex_no_attrib_quadrics[remap[i0]], Q);
|
||||
quadricAdd(vertex_no_attrib_quadrics[remap[i1]], Q);
|
||||
quadricAdd(vertex_no_attrib_quadrics[remap[i2]], Q);
|
||||
|
||||
#if ATTRIBUTES
|
||||
quadricUpdateAttributes(Q, vertex_positions[i0], vertex_positions[i1], vertex_positions[i2], Q.w);
|
||||
|
@ -700,7 +732,7 @@ static void fillFaceQuadrics(Quadric* vertex_quadrics, const unsigned int* indic
|
|||
}
|
||||
}
|
||||
|
||||
static void fillEdgeQuadrics(Quadric* vertex_quadrics, const unsigned int* indices, size_t index_count, const Vector3* vertex_positions, const unsigned int* remap, const unsigned char* vertex_kind, const unsigned int* loop, const unsigned int* loopback)
|
||||
static void fillEdgeQuadrics(Quadric* vertex_quadrics, Quadric* vertex_no_attrib_quadrics, const unsigned int* indices, size_t index_count, const Vector3* vertex_positions, const unsigned int* remap, const unsigned char* vertex_kind, const unsigned int* loop, const unsigned int* loopback)
|
||||
{
|
||||
for (size_t i = 0; i < index_count; i += 3)
|
||||
{
|
||||
|
@ -744,6 +776,9 @@ static void fillEdgeQuadrics(Quadric* vertex_quadrics, const unsigned int* indic
|
|||
|
||||
quadricAdd(vertex_quadrics[remap[i0]], Q);
|
||||
quadricAdd(vertex_quadrics[remap[i1]], Q);
|
||||
|
||||
quadricAdd(vertex_no_attrib_quadrics[remap[i0]], Q);
|
||||
quadricAdd(vertex_no_attrib_quadrics[remap[i1]], Q);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -848,7 +883,7 @@ static size_t pickEdgeCollapses(Collapse* collapses, const unsigned int* indices
|
|||
return collapse_count;
|
||||
}
|
||||
|
||||
static void rankEdgeCollapses(Collapse* collapses, size_t collapse_count, const Vector3* vertex_positions, const Quadric* vertex_quadrics, const unsigned int* remap)
|
||||
static void rankEdgeCollapses(Collapse* collapses, size_t collapse_count, const Vector3* vertex_positions, const Quadric* vertex_quadrics, const Quadric* vertex_no_attrib_quadrics, const unsigned int* remap)
|
||||
{
|
||||
for (size_t i = 0; i < collapse_count; ++i)
|
||||
{
|
||||
|
@ -868,10 +903,14 @@ static void rankEdgeCollapses(Collapse* collapses, size_t collapse_count, const
|
|||
float ei = quadricError(qi, vertex_positions[i1]);
|
||||
float ej = quadricError(qj, vertex_positions[j1]);
|
||||
|
||||
const Quadric& naqi = vertex_no_attrib_quadrics[remap[i0]];
|
||||
const Quadric& naqj = vertex_no_attrib_quadrics[remap[j0]];
|
||||
|
||||
// pick edge direction with minimal error
|
||||
c.v0 = ei <= ej ? i0 : j0;
|
||||
c.v1 = ei <= ej ? i1 : j1;
|
||||
c.error = ei <= ej ? ei : ej;
|
||||
c.distance_error = ei <= ej ? quadricErrorNoAttributes(naqi, vertex_positions[i1]) : quadricErrorNoAttributes(naqj, vertex_positions[j1]);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -968,7 +1007,7 @@ static void sortEdgeCollapses(unsigned int* sort_order, const Collapse* collapse
|
|||
}
|
||||
}
|
||||
|
||||
static size_t performEdgeCollapses(unsigned int* collapse_remap, unsigned char* collapse_locked, Quadric* vertex_quadrics, const Collapse* collapses, size_t collapse_count, const unsigned int* collapse_order, const unsigned int* remap, const unsigned int* wedge, const unsigned char* vertex_kind, const Vector3* vertex_positions, const EdgeAdjacency& adjacency, size_t triangle_collapse_goal, float error_limit, float& result_error)
|
||||
static size_t performEdgeCollapses(unsigned int* collapse_remap, unsigned char* collapse_locked, Quadric* vertex_quadrics, Quadric* vertex_no_attrib_quadrics, const Collapse* collapses, size_t collapse_count, const unsigned int* collapse_order, const unsigned int* remap, const unsigned int* wedge, const unsigned char* vertex_kind, const Vector3* vertex_positions, const EdgeAdjacency& adjacency, size_t triangle_collapse_goal, float error_limit, float& result_error)
|
||||
{
|
||||
size_t edge_collapses = 0;
|
||||
size_t triangle_collapses = 0;
|
||||
|
@ -1030,6 +1069,7 @@ static size_t performEdgeCollapses(unsigned int* collapse_remap, unsigned char*
|
|||
assert(collapse_remap[r1] == r1);
|
||||
|
||||
quadricAdd(vertex_quadrics[r1], vertex_quadrics[r0]);
|
||||
quadricAdd(vertex_no_attrib_quadrics[r1], vertex_no_attrib_quadrics[r0]);
|
||||
|
||||
if (vertex_kind[i0] == Kind_Complex)
|
||||
{
|
||||
|
@ -1067,7 +1107,7 @@ static size_t performEdgeCollapses(unsigned int* collapse_remap, unsigned char*
|
|||
triangle_collapses += (vertex_kind[i0] == Kind_Border) ? 1 : 2;
|
||||
edge_collapses++;
|
||||
|
||||
result_error = result_error < c.error ? c.error : result_error;
|
||||
result_error = result_error < c.distance_error ? c.distance_error : result_error;
|
||||
}
|
||||
|
||||
#if TRACE
|
||||
|
@ -1455,9 +1495,11 @@ size_t meshopt_simplifyWithAttributes(unsigned int* destination, const unsigned
|
|||
|
||||
Quadric* vertex_quadrics = allocator.allocate<Quadric>(vertex_count);
|
||||
memset(vertex_quadrics, 0, vertex_count * sizeof(Quadric));
|
||||
Quadric* vertex_no_attrib_quadrics = allocator.allocate<Quadric>(vertex_count);
|
||||
memset(vertex_no_attrib_quadrics, 0, vertex_count * sizeof(Quadric));
|
||||
|
||||
fillFaceQuadrics(vertex_quadrics, indices, index_count, vertex_positions, remap);
|
||||
fillEdgeQuadrics(vertex_quadrics, indices, index_count, vertex_positions, remap, vertex_kind, loop, loopback);
|
||||
fillFaceQuadrics(vertex_quadrics, vertex_no_attrib_quadrics, indices, index_count, vertex_positions, remap);
|
||||
fillEdgeQuadrics(vertex_quadrics, vertex_no_attrib_quadrics, indices, index_count, vertex_positions, remap, vertex_kind, loop, loopback);
|
||||
|
||||
if (result != indices)
|
||||
memcpy(result, indices, index_count * sizeof(unsigned int));
|
||||
|
@ -1488,7 +1530,7 @@ size_t meshopt_simplifyWithAttributes(unsigned int* destination, const unsigned
|
|||
if (edge_collapse_count == 0)
|
||||
break;
|
||||
|
||||
rankEdgeCollapses(edge_collapses, edge_collapse_count, vertex_positions, vertex_quadrics, remap);
|
||||
rankEdgeCollapses(edge_collapses, edge_collapse_count, vertex_positions, vertex_quadrics, vertex_no_attrib_quadrics, remap);
|
||||
|
||||
#if TRACE > 1
|
||||
dumpEdgeCollapses(edge_collapses, edge_collapse_count, vertex_kind);
|
||||
|
@ -1507,7 +1549,7 @@ size_t meshopt_simplifyWithAttributes(unsigned int* destination, const unsigned
|
|||
printf("pass %d: ", int(pass_count++));
|
||||
#endif
|
||||
|
||||
size_t collapses = performEdgeCollapses(collapse_remap, collapse_locked, vertex_quadrics, edge_collapses, edge_collapse_count, collapse_order, remap, wedge, vertex_kind, vertex_positions, adjacency, triangle_collapse_goal, error_limit, result_error);
|
||||
size_t collapses = performEdgeCollapses(collapse_remap, collapse_locked, vertex_quadrics, vertex_no_attrib_quadrics, edge_collapses, edge_collapse_count, collapse_order, remap, wedge, vertex_kind, vertex_positions, adjacency, triangle_collapse_goal, error_limit, result_error);
|
||||
|
||||
// no edges can be collapsed any more due to hitting the error limit or triangle collapse limit
|
||||
if (collapses == 0)
|
||||
|
|
Loading…
Reference in New Issue