diff --git a/doc/classes/ImporterMesh.xml b/doc/classes/ImporterMesh.xml
index 657d5659f83..9bc1bf035e7 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 6f066281228..2b2dc63a336 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 dd279206ab7..e5409a6e8ac 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 b122189558e..6f12539a6db 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 d0a62ddb4d7..eb940cdd56d 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 d23e0fb48d8..fbc67fc84df 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);