From 3f5c16dd9ec100dfd3b43a01e8e5a37bee523ae8 Mon Sep 17 00:00:00 2001 From: clayjohn Date: Sun, 29 Oct 2023 18:18:18 +0100 Subject: [PATCH] Fix multiple issues with UV compression --- doc/classes/ImporterMesh.xml | 2 +- doc/classes/Mesh.xml | 3 +++ doc/classes/RenderingServer.xml | 2 +- scene/resources/mesh.cpp | 2 ++ servers/rendering_server.cpp | 41 +++++++++++++++++++++++++-------- servers/rendering_server.h | 2 +- 6 files changed, 39 insertions(+), 13 deletions(-) diff --git a/doc/classes/ImporterMesh.xml b/doc/classes/ImporterMesh.xml index 657d5659f833..9bc1bf035e7a 100644 --- a/doc/classes/ImporterMesh.xml +++ b/doc/classes/ImporterMesh.xml @@ -92,7 +92,7 @@ - Returns the arrays for the vertices, normals, uvs, etc. that make up the requested surface. See [method add_surface]. + Returns the arrays for the vertices, normals, UVs, etc. that make up the requested surface. See [method add_surface]. diff --git a/doc/classes/Mesh.xml b/doc/classes/Mesh.xml index 6f0662812287..2b2dc63a3368 100644 --- a/doc/classes/Mesh.xml +++ b/doc/classes/Mesh.xml @@ -366,6 +366,9 @@ Flag used to mark that the mesh intentionally contains no vertex array. + + Flag used to mark that a mesh is using compressed attributes (vertices, normals, tangents, UVs). When this form of compression is enabled, vertex positions will be packed into an RGBA16UNORM attribute and scaled in the vertex shader. The normal and tangent will be packed into an RG16UNORM representing an axis, and a 16-bit float stored in the A-channel of the vertex. UVs will use 16-bit normalized floats instead of full 32-bit signed floats. When using this compression mode you must use either vertices, normals, and tangents or only vertices. You cannot use normals without tangents. Importers will automatically enable this compression if they can. + Blend shapes are normalized. diff --git a/doc/classes/RenderingServer.xml b/doc/classes/RenderingServer.xml index dd279206ab75..e5409a6e8aca 100644 --- a/doc/classes/RenderingServer.xml +++ b/doc/classes/RenderingServer.xml @@ -4226,7 +4226,7 @@ Flag used to mark that the mesh does not have a vertex array and instead will infer vertex positions in the shader using indices and other information. - Flag used to mark that a mesh is using compressed attributes (vertices, normals, tangents, uvs). When this form of compression is enabled, vertex positions will be packed into into an RGBA16UNORM attribute and scaled in the vertex shader. The normal and tangent will be packed into a RG16UNORM representing an axis, and an 16 bit float stored in the A-channel of the vertex. UVs will use 16-bit normalized floats instead of full 32 bit signed floats. When using this compression mode you must either use vertices, normals, and tangents or only vertices. You cannot use normals without tangents. Importers will automatically enable this compression if they can. + Flag used to mark that a mesh is using compressed attributes (vertices, normals, tangents, UVs). When this form of compression is enabled, vertex positions will be packed into an RGBA16UNORM attribute and scaled in the vertex shader. The normal and tangent will be packed into an RG16UNORM representing an axis, and a 16-bit float stored in the A-channel of the vertex. UVs will use 16-bit normalized floats instead of full 32-bit signed floats. When using this compression mode you must use either vertices, normals, and tangents or only vertices. You cannot use normals without tangents. Importers will automatically enable this compression if they can. Flag used to mark the start of the bits used to store the mesh version. diff --git a/scene/resources/mesh.cpp b/scene/resources/mesh.cpp index b122189558ee..6f12539a6db3 100644 --- a/scene/resources/mesh.cpp +++ b/scene/resources/mesh.cpp @@ -870,6 +870,8 @@ void Mesh::_bind_methods() { BIND_BITFIELD_FLAG(ARRAY_FLAG_USE_8_BONE_WEIGHTS); BIND_BITFIELD_FLAG(ARRAY_FLAG_USES_EMPTY_VERTEX_ARRAY); + BIND_BITFIELD_FLAG(ARRAY_FLAG_COMPRESS_ATTRIBUTES); + BIND_ENUM_CONSTANT(BLEND_SHAPE_MODE_NORMALIZED); BIND_ENUM_CONSTANT(BLEND_SHAPE_MODE_RELATIVE); diff --git a/servers/rendering_server.cpp b/servers/rendering_server.cpp index d0a62ddb4d74..eb940cdd56d6 100644 --- a/servers/rendering_server.cpp +++ b/servers/rendering_server.cpp @@ -400,7 +400,14 @@ Error RenderingServer::_surface_set_data(Array p_arrays, uint64_t p_format, uint max_val = max_val.abs().max(min_val.abs()); max_val2 = max_val2.abs().max(min_val2.abs()); - r_uv_scale = Vector4(max_val.x, max_val.y, max_val2.x, max_val2.y) * Vector4(2.0, 2.0, 2.0, 2.0); + if (min_val.x >= 0.0 && min_val2.x >= 0.0 && max_val.x <= 1.0 && max_val2.x <= 1.0 && + min_val.y >= 0.0 && min_val2.y >= 0.0 && max_val.y <= 1.0 && max_val2.y <= 1.0) { + // When all channels are in the 0-1 range, we will compress to 16-bit without scaling to + // preserve the bits as best as possible. + r_uv_scale = Vector4(0.0, 0.0, 0.0, 0.0); + } else { + r_uv_scale = Vector4(max_val.x, max_val.y, max_val2.x, max_val2.y) * Vector4(2.0, 2.0, 2.0, 2.0); + } } for (int ai = 0; ai < RS::ARRAY_MAX; ai++) { @@ -670,8 +677,11 @@ Error RenderingServer::_surface_set_data(Array p_arrays, uint64_t p_format, uint if (p_format & RS::ARRAY_FLAG_COMPRESS_ATTRIBUTES) { for (int i = 0; i < p_vertex_array_len; i++) { Vector2 vec = src[i]; - // Normalize into 0-1 from possible range -uv_scale - uv_scale. - vec = vec / (Vector2(r_uv_scale.x, r_uv_scale.y)) + Vector2(0.5, 0.5); + if (!r_uv_scale.is_zero_approx()) { + // Normalize into 0-1 from possible range -uv_scale - uv_scale. + vec = vec / (Vector2(r_uv_scale.x, r_uv_scale.y)) + Vector2(0.5, 0.5); + } + uint16_t uv[2] = { (uint16_t)CLAMP(vec.x * 65535, 0, 65535), (uint16_t)CLAMP(vec.y * 65535, 0, 65535) }; memcpy(&aw[p_offsets[ai] + i * p_attrib_stride], uv, 4); } @@ -695,8 +705,10 @@ Error RenderingServer::_surface_set_data(Array p_arrays, uint64_t p_format, uint if (p_format & RS::ARRAY_FLAG_COMPRESS_ATTRIBUTES) { for (int i = 0; i < p_vertex_array_len; i++) { Vector2 vec = src[i]; - // Normalize into 0-1 from possible range -uv_scale - uv_scale. - vec = vec / (Vector2(r_uv_scale.z, r_uv_scale.w)) + Vector2(0.5, 0.5); + if (!r_uv_scale.is_zero_approx()) { + // Normalize into 0-1 from possible range -uv_scale - uv_scale. + vec = vec / (Vector2(r_uv_scale.z, r_uv_scale.w)) + Vector2(0.5, 0.5); + } uint16_t uv[2] = { (uint16_t)CLAMP(vec.x * 65535, 0, 65535), (uint16_t)CLAMP(vec.y * 65535, 0, 65535) }; memcpy(&aw[p_offsets[ai] + i * p_attrib_stride], uv, 4); } @@ -1311,7 +1323,7 @@ void RenderingServer::mesh_add_surface_from_arrays(RID p_mesh, PrimitiveType p_p mesh_add_surface(p_mesh, sd); } -Array RenderingServer::_get_array_from_surface(uint64_t p_format, Vector p_vertex_data, Vector p_attrib_data, Vector p_skin_data, int p_vertex_len, Vector p_index_data, int p_index_len, const AABB &p_aabb) const { +Array RenderingServer::_get_array_from_surface(uint64_t p_format, Vector p_vertex_data, Vector p_attrib_data, Vector p_skin_data, int p_vertex_len, Vector p_index_data, int p_index_len, const AABB &p_aabb, const Vector4 &p_uv_scale) const { uint32_t offsets[RS::ARRAY_MAX]; uint32_t vertex_elem_size; @@ -1469,7 +1481,12 @@ Array RenderingServer::_get_array_from_surface(uint64_t p_format, Vector(&ar[j * attrib_elem_size + offsets[i]]); - w[j] = Vector2(float(v[0]) / 65535.0, float(v[1]) / 65535.0); + Vector2 vec = Vector2(float(v[0]) / 65535.0, float(v[1]) / 65535.0); + if (!p_uv_scale.is_zero_approx()) { + vec = (vec - Vector2(0.5, 0.5)) * Vector2(p_uv_scale.x, p_uv_scale.y); + } + + w[j] = vec; } } else { for (int j = 0; j < p_vertex_len; j++) { @@ -1489,7 +1506,11 @@ Array RenderingServer::_get_array_from_surface(uint64_t p_format, Vector(&ar[j * attrib_elem_size + offsets[i]]); - w[j] = Vector2(float(v[0]) / 65535.0, float(v[1]) / 65535.0); + Vector2 vec = Vector2(float(v[0]) / 65535.0, float(v[1]) / 65535.0); + if (!p_uv_scale.is_zero_approx()) { + vec = (vec - Vector2(0.5, 0.5)) * Vector2(p_uv_scale.z, p_uv_scale.w); + } + w[j] = vec; } } else { for (int j = 0; j < p_vertex_len; j++) { @@ -1686,7 +1707,7 @@ TypedArray RenderingServer::mesh_surface_get_blend_shape_arrays(RID p_mes for (uint32_t i = 0; i < blend_shape_count; i++) { Vector bs_data = blend_shape_data.slice(i * divisor, (i + 1) * divisor); Vector unused; - blend_shape_array.set(i, _get_array_from_surface(bs_format, bs_data, unused, unused, sd.vertex_count, unused, 0, sd.aabb)); + blend_shape_array.set(i, _get_array_from_surface(bs_format, bs_data, unused, unused, sd.vertex_count, unused, 0, sd.aabb, sd.uv_scale)); } return blend_shape_array; @@ -1708,7 +1729,7 @@ Array RenderingServer::mesh_create_arrays_from_surface_data(const SurfaceData &p uint64_t format = p_data.format; - return _get_array_from_surface(format, vertex_data, attrib_data, skin_data, vertex_len, index_data, index_len, p_data.aabb); + return _get_array_from_surface(format, vertex_data, attrib_data, skin_data, vertex_len, index_data, index_len, p_data.aabb, p_data.uv_scale); } #if 0 Array RenderingServer::_mesh_surface_get_skeleton_aabb_bind(RID p_mesh, int p_surface) const { diff --git a/servers/rendering_server.h b/servers/rendering_server.h index d23e0fb48d8a..fbc67fc84dfc 100644 --- a/servers/rendering_server.h +++ b/servers/rendering_server.h @@ -52,7 +52,7 @@ class RenderingServer : public Object { int mm_policy = 0; bool render_loop_enabled = true; - Array _get_array_from_surface(uint64_t p_format, Vector p_vertex_data, Vector p_attrib_data, Vector p_skin_data, int p_vertex_len, Vector p_index_data, int p_index_len, const AABB &p_aabb) const; + Array _get_array_from_surface(uint64_t p_format, Vector p_vertex_data, Vector p_attrib_data, Vector p_skin_data, int p_vertex_len, Vector p_index_data, int p_index_len, const AABB &p_aabb, const Vector4 &p_uv_scale) const; const Vector2 SMALL_VEC2 = Vector2(CMP_EPSILON, CMP_EPSILON); const Vector3 SMALL_VEC3 = Vector3(CMP_EPSILON, CMP_EPSILON, CMP_EPSILON);