diff --git a/drivers/gles3/rasterizer_scene_gles3.cpp b/drivers/gles3/rasterizer_scene_gles3.cpp index 4f2d2e29ce2..205f9a552ef 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 b3a3506d401..a3ed75c7039 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 de9daddd380..3f7ed8ab8b6 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 646831ca248..082e78fdbed 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 ecfb035b82b..90280e03729 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 270a34d5412..e5467ea72d9 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 9676474a664..5134f4d5452 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 0739cd9f86f..3b83b2b5822 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 a6a32d9e861..99b416d575f 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 b2c0d4b1bda..f1cec0e07cf 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 5c76d892477..da189c6f678 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 78b5656a740..620262f30e9 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);