Create tangent array if mesh created without tangents

This extends our previous change to ensure that compressed meshes have tangents

Now we ensure tangents are always used. This greatly simplifies our compression code at the cost of a small amount of bandwidth
This commit is contained in:
clayjohn 2023-11-07 14:24:23 +01:00
parent 4c96e9676b
commit 031f221b9d
2 changed files with 42 additions and 10 deletions

View File

@ -166,7 +166,7 @@ void ImmediateMesh::surface_end() {
normal_tangent_stride += sizeof(uint32_t); normal_tangent_stride += sizeof(uint32_t);
} }
uint32_t tangent_offset = 0; uint32_t tangent_offset = 0;
if (uses_tangents) { if (uses_tangents || uses_normals) {
format |= ARRAY_FORMAT_TANGENT; format |= ARRAY_FORMAT_TANGENT;
tangent_offset = vertex_stride * vertices.size() + normal_tangent_stride; tangent_offset = vertex_stride * vertices.size() + normal_tangent_stride;
normal_tangent_stride += sizeof(uint32_t); normal_tangent_stride += sizeof(uint32_t);
@ -202,9 +202,16 @@ void ImmediateMesh::surface_end() {
*normal = value; *normal = value;
} }
if (uses_tangents) { if (uses_tangents || uses_normals) {
uint32_t *tangent = (uint32_t *)&surface_vertex_ptr[i * normal_tangent_stride + tangent_offset]; uint32_t *tangent = (uint32_t *)&surface_vertex_ptr[i * normal_tangent_stride + tangent_offset];
Vector2 t = tangents[i].normal.octahedron_tangent_encode(tangents[i].d); Vector2 t;
if (uses_tangents) {
t = tangents[i].normal.octahedron_tangent_encode(tangents[i].d);
} else {
Vector3 tan = Vector3(0.0, 1.0, 0.0).cross(normals[i].normalized());
t = tan.octahedron_tangent_encode(1.0);
}
uint32_t value = 0; uint32_t value = 0;
value |= (uint16_t)CLAMP(t.x * 65535, 0, 65535); value |= (uint16_t)CLAMP(t.x * 65535, 0, 65535);
value |= (uint16_t)CLAMP(t.y * 65535, 0, 65535) << 16; value |= (uint16_t)CLAMP(t.y * 65535, 0, 65535) << 16;

View File

@ -635,7 +635,8 @@ Error RenderingServer::_surface_set_data(Array p_arrays, uint64_t p_format, uint
// If using compression we store tangent while storing vertices. // If using compression we store tangent while storing vertices.
if (!(p_format & RS::ARRAY_FLAG_COMPRESS_ATTRIBUTES)) { if (!(p_format & RS::ARRAY_FLAG_COMPRESS_ATTRIBUTES)) {
Variant::Type type = p_arrays[ai].get_type(); Variant::Type type = p_arrays[ai].get_type();
ERR_FAIL_COND_V(type != Variant::PACKED_FLOAT32_ARRAY && type != Variant::PACKED_FLOAT64_ARRAY, ERR_INVALID_PARAMETER); ERR_FAIL_COND_V(type != Variant::PACKED_FLOAT32_ARRAY && type != Variant::PACKED_FLOAT64_ARRAY && type != Variant::NIL, ERR_INVALID_PARAMETER);
if (type == Variant::PACKED_FLOAT32_ARRAY) { if (type == Variant::PACKED_FLOAT32_ARRAY) {
Vector<float> array = p_arrays[ai]; Vector<float> array = p_arrays[ai];
ERR_FAIL_COND_V(array.size() != p_vertex_array_len * 4, ERR_INVALID_PARAMETER); ERR_FAIL_COND_V(array.size() != p_vertex_array_len * 4, ERR_INVALID_PARAMETER);
@ -657,7 +658,7 @@ Error RenderingServer::_surface_set_data(Array p_arrays, uint64_t p_format, uint
memcpy(&vw[p_offsets[ai] + i * p_normal_stride], vector, 4); memcpy(&vw[p_offsets[ai] + i * p_normal_stride], vector, 4);
} }
} else { // PACKED_FLOAT64_ARRAY } else if (type == Variant::PACKED_FLOAT64_ARRAY) {
Vector<double> array = p_arrays[ai]; Vector<double> array = p_arrays[ai];
ERR_FAIL_COND_V(array.size() != p_vertex_array_len * 4, ERR_INVALID_PARAMETER); ERR_FAIL_COND_V(array.size() != p_vertex_array_len * 4, ERR_INVALID_PARAMETER);
const double *src_ptr = array.ptr(); const double *src_ptr = array.ptr();
@ -676,6 +677,30 @@ Error RenderingServer::_surface_set_data(Array p_arrays, uint64_t p_format, uint
vector[0] = 65535; vector[0] = 65535;
} }
memcpy(&vw[p_offsets[ai] + i * p_normal_stride], vector, 4);
}
} else { // No tangent array.
ERR_FAIL_COND_V(p_arrays[RS::ARRAY_NORMAL].get_type() != Variant::PACKED_VECTOR3_ARRAY, ERR_INVALID_PARAMETER);
Vector<Vector3> normal_array = p_arrays[RS::ARRAY_NORMAL];
ERR_FAIL_COND_V(normal_array.size() != p_vertex_array_len, ERR_INVALID_PARAMETER);
const Vector3 *normal_src = normal_array.ptr();
// Set data for tangent.
for (int i = 0; i < p_vertex_array_len; i++) {
// Generate an arbitrary vector that is tangential to normal.
Vector3 tan = Vector3(0.0, 1.0, 0.0).cross(normal_src[i].normalized());
Vector2 res = tan.octahedron_tangent_encode(1.0);
uint16_t vector[2] = {
(uint16_t)CLAMP(res.x * 65535, 0, 65535),
(uint16_t)CLAMP(res.y * 65535, 0, 65535),
};
if (vector[0] == 0 && vector[1] == 65535) {
// (1, 1) and (0, 1) decode to the same value, but (0, 1) messes with our compression detection.
// So we sanitize here.
vector[0] = 65535;
}
memcpy(&vw[p_offsets[ai] + i * p_normal_stride], vector, 4); memcpy(&vw[p_offsets[ai] + i * p_normal_stride], vector, 4);
} }
} }
@ -1172,6 +1197,11 @@ Error RenderingServer::mesh_create_surface_data_from_arrays(SurfaceData *r_surfa
} break; } break;
} }
ERR_FAIL_COND_V(array_len == 0, ERR_INVALID_DATA); ERR_FAIL_COND_V(array_len == 0, ERR_INVALID_DATA);
} else if (i == RS::ARRAY_NORMAL) {
if (p_arrays[RS::ARRAY_TANGENT].get_type() == Variant::NIL) {
// We must use tangents if using normals.
format |= (1ULL << RS::ARRAY_TANGENT);
}
} else if (i == RS::ARRAY_BONES) { } else if (i == RS::ARRAY_BONES) {
switch (p_arrays[i].get_type()) { switch (p_arrays[i].get_type()) {
case Variant::PACKED_INT32_ARRAY: { case Variant::PACKED_INT32_ARRAY: {
@ -1242,11 +1272,6 @@ Error RenderingServer::mesh_create_surface_data_from_arrays(SurfaceData *r_surfa
ERR_FAIL_COND_V_MSG(!(format & RS::ARRAY_FORMAT_NORMAL), ERR_INVALID_PARAMETER, "Can't use compression flag 'ARRAY_FLAG_COMPRESS_ATTRIBUTES' while using tangents without normal array."); ERR_FAIL_COND_V_MSG(!(format & RS::ARRAY_FORMAT_NORMAL), ERR_INVALID_PARAMETER, "Can't use compression flag 'ARRAY_FLAG_COMPRESS_ATTRIBUTES' while using tangents without normal array.");
} }
if ((format & RS::ARRAY_FLAG_COMPRESS_ATTRIBUTES) && !(format & RS::ARRAY_FORMAT_TANGENT)) {
// If no tangent array provided, we will generate one.
format |= RS::ARRAY_FORMAT_TANGENT;
}
int vertex_array_size = (vertex_element_size + normal_element_size) * array_len; int vertex_array_size = (vertex_element_size + normal_element_size) * array_len;
int attrib_array_size = attrib_element_size * array_len; int attrib_array_size = attrib_element_size * array_len;
int skin_array_size = skin_element_size * array_len; int skin_array_size = skin_element_size * array_len;