Vertex and attribute compression to reduce the size of the vertex format.

This allows Godot to automatically compress meshes to save a lot of bandwidth.

In general, this requires no interaction from the user and should result in
no noticable quality loss.

This scheme is not backwards compatible, so we have provided an upgrade
mechanism, and a mesh versioning mechanism.

Existing meshes can still be used as a result, but users can get a
performance boost by reimporting assets.
This commit is contained in:
clayjohn
2023-08-29 21:04:32 +02:00
parent d31794c4a2
commit 51ed3aef63
59 changed files with 1752 additions and 670 deletions
+18 -5
View File
@@ -919,6 +919,12 @@ Error ColladaImport::_create_mesh_surfaces(bool p_optimize, Ref<ImporterMesh> &p
}
}
uint64_t mesh_flags = 0;
if (p_use_compression) {
mesh_flags = RS::ARRAY_FLAG_COMPRESS_ATTRIBUTES;
}
Ref<SurfaceTool> surftool;
surftool.instantiate();
surftool->begin(Mesh::PRIMITIVE_TRIANGLES);
@@ -969,14 +975,21 @@ Error ColladaImport::_create_mesh_surfaces(bool p_optimize, Ref<ImporterMesh> &p
}
if (!normal_src) {
//should always be normals
// Should always have normals.
surftool->generate_normals();
}
if ((!binormal_src || !tangent_src) && normal_src && uv_src && force_make_tangents) {
bool generate_tangents = (!binormal_src || !tangent_src) && uv_src && force_make_tangents;
if (generate_tangents) {
surftool->generate_tangents();
}
if (!binormal_src || !(tangent_src || generate_tangents) || p_mesh->get_blend_shape_count() != 0 || p_skin_controller) {
// Can't compress if attributes missing or if using vertex weights.
mesh_flags &= ~RS::ARRAY_FLAG_COMPRESS_ATTRIBUTES;
}
////////////////////////////
// FINALLY CREATE SUFRACE //
////////////////////////////
@@ -996,7 +1009,7 @@ Error ColladaImport::_create_mesh_surfaces(bool p_optimize, Ref<ImporterMesh> &p
// Enforce blend shape mask array format
for (int mj = 0; mj < Mesh::ARRAY_MAX; mj++) {
if (!(Mesh::ARRAY_FORMAT_BLEND_SHAPE_MASK & (1 << mj))) {
if (!(Mesh::ARRAY_FORMAT_BLEND_SHAPE_MASK & (1ULL << mj))) {
a[mj] = Variant();
}
}
@@ -1011,7 +1024,7 @@ Error ColladaImport::_create_mesh_surfaces(bool p_optimize, Ref<ImporterMesh> &p
}
surface_name = material->get_name();
}
p_mesh->add_surface(Mesh::PRIMITIVE_TRIANGLES, d, mr, Dictionary(), mat, surface_name);
p_mesh->add_surface(Mesh::PRIMITIVE_TRIANGLES, d, mr, Dictionary(), mat, surface_name, mesh_flags);
}
/*****************/
@@ -1773,7 +1786,7 @@ Node *EditorSceneFormatImporterCollada::import_scene(const String &p_path, uint3
state.use_mesh_builtin_materials = true;
state.bake_fps = (float)p_options["animation/fps"];
Error err = state.load(p_path, flags, p_flags & EditorSceneFormatImporter::IMPORT_GENERATE_TANGENT_ARRAYS, false);
Error err = state.load(p_path, flags, p_flags & EditorSceneFormatImporter::IMPORT_GENERATE_TANGENT_ARRAYS, !bool(p_flags & EditorSceneFormatImporter::IMPORT_FORCE_DISABLE_MESH_COMPRESSION));
if (r_err) {
*r_err = err;
+12 -5
View File
@@ -202,7 +202,7 @@ static Error _parse_material_library(const String &p_path, HashMap<String, Ref<S
return OK;
}
static Error _parse_obj(const String &p_path, List<Ref<Mesh>> &r_meshes, bool p_single_mesh, bool p_generate_tangents, bool p_optimize, Vector3 p_scale_mesh, Vector3 p_offset_mesh, List<String> *r_missing_deps) {
static Error _parse_obj(const String &p_path, List<Ref<Mesh>> &r_meshes, bool p_single_mesh, bool p_generate_tangents, bool p_optimize, Vector3 p_scale_mesh, Vector3 p_offset_mesh, bool p_disable_compression, List<String> *r_missing_deps) {
Ref<FileAccess> f = FileAccess::open(p_path, FileAccess::READ);
ERR_FAIL_COND_V_MSG(f.is_null(), ERR_CANT_OPEN, vformat("Couldn't open OBJ file '%s', it may not exist or not be readable.", p_path));
@@ -226,7 +226,11 @@ static Error _parse_obj(const String &p_path, List<Ref<Mesh>> &r_meshes, bool p_
bool generate_tangents = p_generate_tangents;
Vector3 scale_mesh = p_scale_mesh;
Vector3 offset_mesh = p_offset_mesh;
int mesh_flags = 0;
uint64_t mesh_flags = RS::ARRAY_FLAG_COMPRESS_ATTRIBUTES;
if (p_disable_compression) {
mesh_flags = 0;
}
Vector<Vector3> vertices;
Vector<Vector3> normals;
@@ -287,7 +291,6 @@ static Error _parse_obj(const String &p_path, List<Ref<Mesh>> &r_meshes, bool p_
uv.x = v[1].to_float();
uv.y = 1.0 - v[2].to_float();
uvs.push_back(uv);
} else if (l.begins_with("vn ")) {
//normal
Vector<String> v = l.split(" ", false);
@@ -380,6 +383,9 @@ static Error _parse_obj(const String &p_path, List<Ref<Mesh>> &r_meshes, bool p_
if (generate_tangents && uvs.size()) {
surf_tool->generate_tangents();
} else {
// We need tangents in order to compress vertex data. So disable if tangents aren't generated.
mesh_flags &= ~RS::ARRAY_FLAG_COMPRESS_ATTRIBUTES;
}
surf_tool->index();
@@ -464,7 +470,7 @@ static Error _parse_obj(const String &p_path, List<Ref<Mesh>> &r_meshes, bool p_
Node *EditorOBJImporter::import_scene(const String &p_path, uint32_t p_flags, const HashMap<StringName, Variant> &p_options, List<String> *r_missing_deps, Error *r_err) {
List<Ref<Mesh>> meshes;
Error err = _parse_obj(p_path, meshes, false, p_flags & IMPORT_GENERATE_TANGENT_ARRAYS, false, Vector3(1, 1, 1), Vector3(0, 0, 0), r_missing_deps);
Error err = _parse_obj(p_path, meshes, false, p_flags & IMPORT_GENERATE_TANGENT_ARRAYS, false, Vector3(1, 1, 1), Vector3(0, 0, 0), p_flags & IMPORT_FORCE_DISABLE_MESH_COMPRESSION, r_missing_deps);
if (err != OK) {
if (r_err) {
@@ -543,6 +549,7 @@ void ResourceImporterOBJ::get_import_options(const String &p_path, List<ImportOp
r_options->push_back(ImportOption(PropertyInfo(Variant::VECTOR3, "scale_mesh"), Vector3(1, 1, 1)));
r_options->push_back(ImportOption(PropertyInfo(Variant::VECTOR3, "offset_mesh"), Vector3(0, 0, 0)));
r_options->push_back(ImportOption(PropertyInfo(Variant::BOOL, "optimize_mesh"), true));
r_options->push_back(ImportOption(PropertyInfo(Variant::BOOL, "force_disable_mesh_compression"), false));
}
bool ResourceImporterOBJ::get_option_visibility(const String &p_path, const String &p_option, const HashMap<StringName, Variant> &p_options) const {
@@ -552,7 +559,7 @@ bool ResourceImporterOBJ::get_option_visibility(const String &p_path, const Stri
Error ResourceImporterOBJ::import(const String &p_source_file, const String &p_save_path, const HashMap<StringName, Variant> &p_options, List<String> *r_platform_variants, List<String> *r_gen_files, Variant *r_metadata) {
List<Ref<Mesh>> meshes;
Error err = _parse_obj(p_source_file, meshes, true, p_options["generate_tangents"], p_options["optimize_mesh"], p_options["scale_mesh"], p_options["offset_mesh"], nullptr);
Error err = _parse_obj(p_source_file, meshes, true, p_options["generate_tangents"], p_options["optimize_mesh"], p_options["scale_mesh"], p_options["offset_mesh"], p_options["force_disable_mesh_compression"], nullptr);
ERR_FAIL_COND_V(err != OK, err);
ERR_FAIL_COND_V(meshes.size() != 1, ERR_BUG);
@@ -112,6 +112,7 @@ void EditorSceneFormatImporter::_bind_methods() {
BIND_CONSTANT(IMPORT_GENERATE_TANGENT_ARRAYS);
BIND_CONSTANT(IMPORT_USE_NAMED_SKIN_BINDS);
BIND_CONSTANT(IMPORT_DISCARD_MESHES_AND_MATERIALS);
BIND_CONSTANT(IMPORT_FORCE_DISABLE_MESH_COMPRESSION);
}
/////////////////////////////////
@@ -1934,6 +1935,7 @@ void ResourceImporterScene::get_import_options(const String &p_path, List<Import
r_options->push_back(ImportOption(PropertyInfo(Variant::BOOL, "meshes/create_shadow_meshes"), true));
r_options->push_back(ImportOption(PropertyInfo(Variant::INT, "meshes/light_baking", PROPERTY_HINT_ENUM, "Disabled,Static (VoxelGI/SDFGI),Static Lightmaps (VoxelGI/SDFGI/LightmapGI),Dynamic (VoxelGI only)", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_UPDATE_ALL_IF_MODIFIED), 1));
r_options->push_back(ImportOption(PropertyInfo(Variant::FLOAT, "meshes/lightmap_texel_size", PROPERTY_HINT_RANGE, "0.001,100,0.001"), 0.2));
r_options->push_back(ImportOption(PropertyInfo(Variant::BOOL, "meshes/force_disable_compression"), false));
r_options->push_back(ImportOption(PropertyInfo(Variant::BOOL, "skins/use_named_skins"), true));
r_options->push_back(ImportOption(PropertyInfo(Variant::BOOL, "animation/import"), true));
r_options->push_back(ImportOption(PropertyInfo(Variant::FLOAT, "animation/fps", PROPERTY_HINT_RANGE, "1,120,1"), 30));
@@ -2428,6 +2430,11 @@ Error ResourceImporterScene::import(const String &p_source_file, const String &p
import_flags |= EditorSceneFormatImporter::IMPORT_GENERATE_TANGENT_ARRAYS;
}
bool force_disable_compression = p_options["meshes/force_disable_compression"];
if (force_disable_compression) {
import_flags |= EditorSceneFormatImporter::IMPORT_FORCE_DISABLE_MESH_COMPRESSION;
}
Error err = OK;
List<String> missing_deps; // for now, not much will be done with this
Node *scene = importer->import_scene(src_path, import_flags, p_options, &missing_deps, &err);
+1
View File
@@ -71,6 +71,7 @@ public:
IMPORT_GENERATE_TANGENT_ARRAYS = 8,
IMPORT_USE_NAMED_SKIN_BINDS = 16,
IMPORT_DISCARD_MESHES_AND_MATERIALS = 32, //used for optimizing animation import
IMPORT_FORCE_DISABLE_MESH_COMPRESSION = 64,
};
virtual uint32_t get_import_flags() const;