From d1043a5f930513aa4b48b014ec7961cad48b0560 Mon Sep 17 00:00:00 2001 From: clayjohn Date: Tue, 31 Oct 2023 15:51:07 +0100 Subject: [PATCH] Enhance checks and user experience around tangents. Ensure `ensure_tangents` option actually creates tangent array. Even if it is just a dummy array. Allow mesh to generate its own tangents when using compression. This allows users to compress meshes without tangents. Warn users if they are trying to read from tangents without providing tangents. --- drivers/gles3/rasterizer_scene_gles3.cpp | 5 ++ drivers/gles3/storage/material_storage.cpp | 2 + editor/import/editor_import_collada.cpp | 13 ++++- editor/import/resource_importer_obj.cpp | 14 ++++- modules/gltf/gltf_document.cpp | 23 +++++++- .../render_forward_clustered.cpp | 5 ++ .../scene_shader_forward_clustered.cpp | 11 +++- .../scene_shader_forward_clustered.h | 1 + .../forward_mobile/render_forward_mobile.cpp | 5 ++ .../scene_shader_forward_mobile.cpp | 11 +++- .../scene_shader_forward_mobile.h | 1 + servers/rendering_server.cpp | 55 +++++++++++++++---- 12 files changed, 127 insertions(+), 19 deletions(-) diff --git a/drivers/gles3/rasterizer_scene_gles3.cpp b/drivers/gles3/rasterizer_scene_gles3.cpp index 21e14c1ec959..6e8fdd91dc13 100644 --- a/drivers/gles3/rasterizer_scene_gles3.cpp +++ b/drivers/gles3/rasterizer_scene_gles3.cpp @@ -259,6 +259,11 @@ void RasterizerSceneGLES3::_geometry_instance_add_surface_with_material(Geometry sdcache->sort.shader_id = p_shader_id; sdcache->sort.geometry_id = p_mesh.get_local_index(); sdcache->sort.priority = p_material->priority; + + GLES3::Mesh::Surface *s = reinterpret_cast(sdcache->surface); + if (p_material->shader_data->uses_tangent && !(s->format & RS::ARRAY_FORMAT_TANGENT)) { + WARN_PRINT_ED("Attempting to use a shader that requires tangents with a mesh that doesn't contain tangents. Ensure that meshes are imported with the 'ensure_tangents' option. If creating your own meshes, add an `ARRAY_TANGENT` array (when using ArrayMesh) or call `generate_tangents()` (when using SurfaceTool)."); + } } void RasterizerSceneGLES3::_geometry_instance_add_surface_with_material_chain(GeometryInstanceGLES3 *ginstance, uint32_t p_surface, GLES3::SceneMaterialData *p_material_data, RID p_mat_src, RID p_mesh) { diff --git a/drivers/gles3/storage/material_storage.cpp b/drivers/gles3/storage/material_storage.cpp index b3a3506d401d..a3ed75c7039d 100644 --- a/drivers/gles3/storage/material_storage.cpp +++ b/drivers/gles3/storage/material_storage.cpp @@ -2924,6 +2924,8 @@ void SceneShaderData::set_code(const String &p_code) { actions.usage_flag_pointers["TANGENT"] = &uses_tangent; actions.usage_flag_pointers["BINORMAL"] = &uses_tangent; + actions.usage_flag_pointers["ANISOTROPY"] = &uses_tangent; + actions.usage_flag_pointers["ANISOTROPY_FLOW"] = &uses_tangent; actions.usage_flag_pointers["COLOR"] = &uses_color; actions.usage_flag_pointers["UV"] = &uses_uv; actions.usage_flag_pointers["UV2"] = &uses_uv2; diff --git a/editor/import/editor_import_collada.cpp b/editor/import/editor_import_collada.cpp index de9daddd3804..3f7ed8ab8b61 100644 --- a/editor/import/editor_import_collada.cpp +++ b/editor/import/editor_import_collada.cpp @@ -925,6 +925,9 @@ Error ColladaImport::_create_mesh_surfaces(bool p_optimize, Ref &p mesh_flags = RS::ARRAY_FLAG_COMPRESS_ATTRIBUTES; } + // We can't generate tangents without UVs, so create dummy tangents. + bool generate_dummy_tangents = (!binormal_src || !tangent_src) && !uv_src && force_make_tangents; + Ref surftool; surftool.instantiate(); surftool->begin(Mesh::PRIMITIVE_TRIANGLES); @@ -934,6 +937,14 @@ Error ColladaImport::_create_mesh_surfaces(bool p_optimize, Ref &p surftool->set_normal(vertex_array[k].normal); if (binormal_src && tangent_src) { surftool->set_tangent(vertex_array[k].tangent); + } else if (generate_dummy_tangents) { + Vector3 tan = Vector3(0.0, 1.0, 0.0).cross(vertex_array[k].normal); + surftool->set_tangent(Plane(tan.x, tan.y, tan.z, 1.0)); + } + } else { + // No normals, use a dummy normal since normals will be generated. + if (generate_dummy_tangents) { + surftool->set_tangent(Plane(1.0, 0.0, 0.0, 1.0)); } } if (uv_src) { @@ -985,7 +996,7 @@ Error ColladaImport::_create_mesh_surfaces(bool p_optimize, Ref &p surftool->generate_tangents(); } - if (!binormal_src || !(tangent_src || generate_tangents) || p_mesh->get_blend_shape_count() != 0 || p_skin_controller) { + if (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; } diff --git a/editor/import/resource_importer_obj.cpp b/editor/import/resource_importer_obj.cpp index 646831ca2483..082e78fdbed0 100644 --- a/editor/import/resource_importer_obj.cpp +++ b/editor/import/resource_importer_obj.cpp @@ -327,6 +327,17 @@ static Error _parse_obj(const String &p_path, List> &r_meshes, bool p_ } ERR_FAIL_INDEX_V(norm, normals.size(), ERR_FILE_CORRUPT); surf_tool->set_normal(normals[norm]); + if (generate_tangents && uvs.is_empty()) { + // We can't generate tangents without UVs, so create dummy tangents. + Vector3 tan = Vector3(0.0, 1.0, 0.0).cross(normals[norm]); + surf_tool->set_tangent(Plane(tan.x, tan.y, tan.z, 1.0)); + } + } else { + // No normals, use a dummy normal since normals will be generated. + if (generate_tangents && uvs.is_empty()) { + // We can't generate tangents without UVs, so create dummy tangents. + surf_tool->set_tangent(Plane(1.0, 0.0, 0.0, 1.0)); + } } if (face[idx].size() >= 2 && !face[idx][1].is_empty()) { @@ -383,9 +394,6 @@ static Error _parse_obj(const String &p_path, List> &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 = 0; } surf_tool->index(); diff --git a/modules/gltf/gltf_document.cpp b/modules/gltf/gltf_document.cpp index ecfb035b82b9..90280e037290 100644 --- a/modules/gltf/gltf_document.cpp +++ b/modules/gltf/gltf_document.cpp @@ -2797,9 +2797,26 @@ Error GLTFDocument::_parse_meshes(Ref p_state) { array[Mesh::ARRAY_INDEX] = indices; } - bool generate_tangents = p_state->force_generate_tangents && (primitive == Mesh::PRIMITIVE_TRIANGLES && !a.has("TANGENT") && a.has("TEXCOORD_0") && a.has("NORMAL")); + bool generate_tangents = p_state->force_generate_tangents && (primitive == Mesh::PRIMITIVE_TRIANGLES && !a.has("TANGENT") && a.has("NORMAL")); - if (p_state->force_disable_compression || !a.has("POSITION") || !a.has("NORMAL") || !(a.has("TANGENT") || generate_tangents) || p.has("targets") || (a.has("JOINTS_0") || a.has("JOINTS_1"))) { + if (generate_tangents && !a.has("TEXCOORD_0")) { + // If we don't have UVs we provide a dummy tangent array. + Vector tangents; + tangents.resize(vertex_num * 4); + float *tangentsw = tangents.ptrw(); + + Vector normals = array[Mesh::ARRAY_NORMAL]; + for (int k = 0; k < vertex_num; k++) { + Vector3 tan = Vector3(0.0, 1.0, 0.0).cross(normals[k]); + tangentsw[k * 4 + 0] = tan.x; + tangentsw[k * 4 + 1] = tan.y; + tangentsw[k * 4 + 2] = tan.z; + tangentsw[k * 4 + 3] = 1.0; + } + array[Mesh::ARRAY_TANGENT] = tangents; + } + + if (p_state->force_disable_compression || !a.has("POSITION") || !a.has("NORMAL") || p.has("targets") || (a.has("JOINTS_0") || a.has("JOINTS_1"))) { flags &= ~RS::ARRAY_FLAG_COMPRESS_ATTRIBUTES; } @@ -2810,7 +2827,7 @@ Error GLTFDocument::_parse_meshes(Ref p_state) { mesh_surface_tool->set_skin_weight_count(SurfaceTool::SKIN_8_WEIGHTS); } mesh_surface_tool->index(); - if (generate_tangents) { + if (generate_tangents && a.has("TEXCOORD_0")) { //must generate mikktspace tangents.. ergh.. mesh_surface_tool->generate_tangents(); } diff --git a/servers/rendering/renderer_rd/forward_clustered/render_forward_clustered.cpp b/servers/rendering/renderer_rd/forward_clustered/render_forward_clustered.cpp index 270a34d5412b..e5467ea72d9a 100644 --- a/servers/rendering/renderer_rd/forward_clustered/render_forward_clustered.cpp +++ b/servers/rendering/renderer_rd/forward_clustered/render_forward_clustered.cpp @@ -3707,6 +3707,11 @@ void RenderForwardClustered::_geometry_instance_add_surface_with_material(Geomet sdcache->sort.priority = p_material->priority; sdcache->sort.uses_projector = ginstance->using_projectors; sdcache->sort.uses_softshadow = ginstance->using_softshadows; + + uint64_t format = RendererRD::MeshStorage::get_singleton()->mesh_surface_get_format(sdcache->surface); + if (p_material->shader_data->uses_tangent && !(format & RS::ARRAY_FORMAT_TANGENT)) { + WARN_PRINT_ED("Attempting to use a shader that requires tangents with a mesh that doesn't contain tangents. Ensure that meshes are imported with the 'ensure_tangents' option. If creating your own meshes, add an `ARRAY_TANGENT` array (when using ArrayMesh) or call `generate_tangents()` (when using SurfaceTool)."); + } } void RenderForwardClustered::_geometry_instance_add_surface_with_material_chain(GeometryInstanceForwardClustered *ginstance, uint32_t p_surface, SceneShaderForwardClustered::MaterialData *p_material, RID p_mat_src, RID p_mesh) { diff --git a/servers/rendering/renderer_rd/forward_clustered/scene_shader_forward_clustered.cpp b/servers/rendering/renderer_rd/forward_clustered/scene_shader_forward_clustered.cpp index 9676474a664c..5134f4d5452e 100644 --- a/servers/rendering/renderer_rd/forward_clustered/scene_shader_forward_clustered.cpp +++ b/servers/rendering/renderer_rd/forward_clustered/scene_shader_forward_clustered.cpp @@ -65,6 +65,8 @@ void SceneShaderForwardClustered::ShaderData::set_code(const String &p_code) { uses_discard = false; uses_roughness = false; uses_normal = false; + uses_tangent = false; + bool uses_normal_map = false; bool wireframe = false; unshaded = false; @@ -121,11 +123,16 @@ void SceneShaderForwardClustered::ShaderData::set_code(const String &p_code) { actions.usage_flag_pointers["TIME"] = &uses_time; actions.usage_flag_pointers["ROUGHNESS"] = &uses_roughness; actions.usage_flag_pointers["NORMAL"] = &uses_normal; - actions.usage_flag_pointers["NORMAL_MAP"] = &uses_normal; + actions.usage_flag_pointers["NORMAL_MAP"] = &uses_normal_map; actions.usage_flag_pointers["POINT_SIZE"] = &uses_point_size; actions.usage_flag_pointers["POINT_COORD"] = &uses_point_size; + actions.usage_flag_pointers["TANGENT"] = &uses_tangent; + actions.usage_flag_pointers["BINORMAL"] = &uses_tangent; + actions.usage_flag_pointers["ANISOTROPY"] = &uses_tangent; + actions.usage_flag_pointers["ANISOTROPY_FLOW"] = &uses_tangent; + actions.write_flag_pointers["MODELVIEW_MATRIX"] = &writes_modelview_or_projection; actions.write_flag_pointers["PROJECTION_MATRIX"] = &writes_modelview_or_projection; actions.write_flag_pointers["VERTEX"] = &uses_vertex; @@ -150,6 +157,8 @@ void SceneShaderForwardClustered::ShaderData::set_code(const String &p_code) { uses_normal_texture = gen_code.uses_normal_roughness_texture; uses_vertex_time = gen_code.uses_vertex_time; uses_fragment_time = gen_code.uses_fragment_time; + uses_normal |= uses_normal_map; + uses_tangent |= uses_normal_map; #if 0 print_line("**compiling shader:"); diff --git a/servers/rendering/renderer_rd/forward_clustered/scene_shader_forward_clustered.h b/servers/rendering/renderer_rd/forward_clustered/scene_shader_forward_clustered.h index 0739cd9f86f3..3b83b2b58221 100644 --- a/servers/rendering/renderer_rd/forward_clustered/scene_shader_forward_clustered.h +++ b/servers/rendering/renderer_rd/forward_clustered/scene_shader_forward_clustered.h @@ -165,6 +165,7 @@ public: bool uses_discard = false; bool uses_roughness = false; bool uses_normal = false; + bool uses_tangent = false; bool uses_particle_trails = false; bool unshaded = false; diff --git a/servers/rendering/renderer_rd/forward_mobile/render_forward_mobile.cpp b/servers/rendering/renderer_rd/forward_mobile/render_forward_mobile.cpp index a6a32d9e861b..99b416d575f7 100644 --- a/servers/rendering/renderer_rd/forward_mobile/render_forward_mobile.cpp +++ b/servers/rendering/renderer_rd/forward_mobile/render_forward_mobile.cpp @@ -2508,6 +2508,11 @@ void RenderForwardMobile::_geometry_instance_add_surface_with_material(GeometryI sdcache->sort.geometry_id = p_mesh.get_local_index(); // sdcache->sort.uses_forward_gi = ginstance->can_sdfgi; sdcache->sort.priority = p_material->priority; + + uint64_t format = RendererRD::MeshStorage::get_singleton()->mesh_surface_get_format(sdcache->surface); + if (p_material->shader_data->uses_tangent && !(format & RS::ARRAY_FORMAT_TANGENT)) { + WARN_PRINT_ED("Attempting to use a shader that requires tangents with a mesh that doesn't contain tangents. Ensure that meshes are imported with the 'ensure_tangents' option. If creating your own meshes, add an `ARRAY_TANGENT` array (when using ArrayMesh) or call `generate_tangents()` (when using SurfaceTool)."); + } } void RenderForwardMobile::_geometry_instance_add_surface_with_material_chain(GeometryInstanceForwardMobile *ginstance, uint32_t p_surface, SceneShaderForwardMobile::MaterialData *p_material, RID p_mat_src, RID p_mesh) { diff --git a/servers/rendering/renderer_rd/forward_mobile/scene_shader_forward_mobile.cpp b/servers/rendering/renderer_rd/forward_mobile/scene_shader_forward_mobile.cpp index b2c0d4b1bdaa..f1cec0e07cfe 100644 --- a/servers/rendering/renderer_rd/forward_mobile/scene_shader_forward_mobile.cpp +++ b/servers/rendering/renderer_rd/forward_mobile/scene_shader_forward_mobile.cpp @@ -67,6 +67,8 @@ void SceneShaderForwardMobile::ShaderData::set_code(const String &p_code) { uses_discard = false; uses_roughness = false; uses_normal = false; + uses_tangent = false; + bool uses_normal_map = false; bool wireframe = false; unshaded = false; @@ -122,7 +124,12 @@ void SceneShaderForwardMobile::ShaderData::set_code(const String &p_code) { actions.usage_flag_pointers["TIME"] = &uses_time; actions.usage_flag_pointers["ROUGHNESS"] = &uses_roughness; actions.usage_flag_pointers["NORMAL"] = &uses_normal; - actions.usage_flag_pointers["NORMAL_MAP"] = &uses_normal; + actions.usage_flag_pointers["NORMAL_MAP"] = &uses_normal_map; + + actions.usage_flag_pointers["TANGENT"] = &uses_tangent; + actions.usage_flag_pointers["BINORMAL"] = &uses_tangent; + actions.usage_flag_pointers["ANISOTROPY"] = &uses_tangent; + actions.usage_flag_pointers["ANISOTROPY_FLOW"] = &uses_tangent; actions.usage_flag_pointers["POINT_SIZE"] = &uses_point_size; actions.usage_flag_pointers["POINT_COORD"] = &uses_point_size; @@ -150,6 +157,8 @@ void SceneShaderForwardMobile::ShaderData::set_code(const String &p_code) { uses_screen_texture = gen_code.uses_screen_texture; uses_depth_texture = gen_code.uses_depth_texture; uses_normal_texture = gen_code.uses_normal_roughness_texture; + uses_normal |= uses_normal_map; + uses_tangent |= uses_normal_map; #ifdef DEBUG_ENABLED if (uses_sss) { diff --git a/servers/rendering/renderer_rd/forward_mobile/scene_shader_forward_mobile.h b/servers/rendering/renderer_rd/forward_mobile/scene_shader_forward_mobile.h index 5c76d8924775..da189c6f678a 100644 --- a/servers/rendering/renderer_rd/forward_mobile/scene_shader_forward_mobile.h +++ b/servers/rendering/renderer_rd/forward_mobile/scene_shader_forward_mobile.h @@ -119,6 +119,7 @@ public: bool uses_discard = false; bool uses_roughness = false; bool uses_normal = false; + bool uses_tangent = false; bool uses_particle_trails = false; bool unshaded = false; diff --git a/servers/rendering_server.cpp b/servers/rendering_server.cpp index eb940cdd56d6..e65944bcbac8 100644 --- a/servers/rendering_server.cpp +++ b/servers/rendering_server.cpp @@ -460,9 +460,7 @@ Error RenderingServer::_surface_set_data(Array p_arrays, uint64_t p_format, uint } } - bool using_normals_tangents = (p_format & RS::ARRAY_FORMAT_NORMAL) && (p_format & RS::ARRAY_FORMAT_TANGENT); - - if (!using_normals_tangents) { + if (!(p_format & RS::ARRAY_FORMAT_NORMAL)) { // Early out if we are only setting vertex positions. for (int i = 0; i < p_vertex_array_len; i++) { Vector3 pos = (src[i] - r_aabb.position) / r_aabb.size; @@ -480,13 +478,14 @@ Error RenderingServer::_surface_set_data(Array p_arrays, uint64_t p_format, uint // Validate normal and tangent arrays. ERR_FAIL_COND_V(p_arrays[RS::ARRAY_NORMAL].get_type() != Variant::PACKED_VECTOR3_ARRAY, ERR_INVALID_PARAMETER); - Variant::Type tangent_type = p_arrays[RS::ARRAY_TANGENT].get_type(); - ERR_FAIL_COND_V(tangent_type != Variant::PACKED_FLOAT32_ARRAY && tangent_type != Variant::PACKED_FLOAT64_ARRAY, ERR_INVALID_PARAMETER); Vector 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(); + Variant::Type tangent_type = p_arrays[RS::ARRAY_TANGENT].get_type(); + ERR_FAIL_COND_V(tangent_type != Variant::PACKED_FLOAT32_ARRAY && tangent_type != Variant::PACKED_FLOAT64_ARRAY && tangent_type != Variant::NIL, ERR_INVALID_PARAMETER); + // We need a different version if using double precision tangents. if (tangent_type == Variant::PACKED_FLOAT32_ARRAY) { Vector tangent_array = p_arrays[RS::ARRAY_TANGENT]; @@ -524,7 +523,7 @@ Error RenderingServer::_surface_set_data(Array p_arrays, uint64_t p_format, uint memcpy(&vw[p_offsets[ai] + i * p_vertex_stride], vector, sizeof(uint16_t) * 4); } } - } else { // PACKED_FLOAT64_ARRAY + } else if (tangent_type == Variant::PACKED_FLOAT64_ARRAY) { Vector tangent_array = p_arrays[RS::ARRAY_TANGENT]; ERR_FAIL_COND_V(tangent_array.size() != p_vertex_array_len * 4, ERR_INVALID_PARAMETER); const double *tangent_src = tangent_array.ptr(); @@ -547,6 +546,40 @@ Error RenderingServer::_surface_set_data(Array p_arrays, uint64_t p_format, uint memcpy(&vw[p_offsets[RS::ARRAY_NORMAL] + i * p_normal_stride], vector, 4); } + // Store vertex position + angle. + { + Vector3 pos = (src[i] - r_aabb.position) / r_aabb.size; + uint16_t vector[4] = { + (uint16_t)CLAMP(pos.x * 65535, 0, 65535), + (uint16_t)CLAMP(pos.y * 65535, 0, 65535), + (uint16_t)CLAMP(pos.z * 65535, 0, 65535), + (uint16_t)CLAMP(angle * 65535, 0, 65535) + }; + + memcpy(&vw[p_offsets[ai] + i * p_vertex_stride], vector, sizeof(uint16_t) * 4); + } + } + } else { // No tangent array. + // Set data for vertex, normal, and tangent. + for (int i = 0; i < p_vertex_array_len; i++) { + float angle; + Vector3 axis; + // Generate an arbitrary vector that is tangential to normal. + Vector3 tan = Vector3(0.0, 1.0, 0.0).cross(normal_src[i].normalized()); + Vector4 tangent = Vector4(tan.x, tan.y, tan.z, 1.0); + _get_axis_angle(normal_src[i], tangent, angle, axis); + + // Store axis. + { + Vector2 res = axis.octahedron_encode(); + uint16_t vector[2] = { + (uint16_t)CLAMP(res.x * 65535, 0, 65535), + (uint16_t)CLAMP(res.y * 65535, 0, 65535), + }; + + memcpy(&vw[p_offsets[RS::ARRAY_NORMAL] + i * p_normal_stride], vector, 4); + } + // Store vertex position + angle. { Vector3 pos = (src[i] - r_aabb.position) / r_aabb.size; @@ -1207,7 +1240,11 @@ Error RenderingServer::mesh_create_surface_data_from_arrays(SurfaceData *r_surfa // If using normals or tangents, then we need all three. ERR_FAIL_COND_V_MSG(!(format & RS::ARRAY_FORMAT_VERTEX), ERR_INVALID_PARAMETER, "Can't use compression flag 'ARRAY_FLAG_COMPRESS_ATTRIBUTES' while using normals or tangents without vertex 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."); - ERR_FAIL_COND_V_MSG(!(format & RS::ARRAY_FORMAT_TANGENT), ERR_INVALID_PARAMETER, "Can't use compression flag 'ARRAY_FLAG_COMPRESS_ATTRIBUTES' while using normals without tangent 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; @@ -1368,10 +1405,8 @@ Array RenderingServer::_get_array_from_surface(uint64_t p_format, Vector(&r[j * vertex_elem_size + offsets[i]]); Vector3 vec = Vector3(float(v[0]) / 65535.0, float(v[1]) / 65535.0, float(v[2]) / 65535.0);