Merge pull request #84252 from clayjohn/ensure_tangents
Enhance checks and user experience around tangent arrays in meshes.
This commit is contained in:
commit
44a54f4500
|
@ -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<GLES3::Mesh::Surface *>(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) {
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -925,6 +925,9 @@ Error ColladaImport::_create_mesh_surfaces(bool p_optimize, Ref<ImporterMesh> &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<SurfaceTool> surftool;
|
||||
surftool.instantiate();
|
||||
surftool->begin(Mesh::PRIMITIVE_TRIANGLES);
|
||||
|
@ -934,6 +937,14 @@ Error ColladaImport::_create_mesh_surfaces(bool p_optimize, Ref<ImporterMesh> &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<ImporterMesh> &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;
|
||||
}
|
||||
|
|
|
@ -327,6 +327,17 @@ static Error _parse_obj(const String &p_path, List<Ref<Mesh>> &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<Ref<Mesh>> &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();
|
||||
|
|
|
@ -2797,9 +2797,26 @@ Error GLTFDocument::_parse_meshes(Ref<GLTFState> 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<float> tangents;
|
||||
tangents.resize(vertex_num * 4);
|
||||
float *tangentsw = tangents.ptrw();
|
||||
|
||||
Vector<Vector3> 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<GLTFState> 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();
|
||||
}
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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:");
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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<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();
|
||||
|
||||
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<float> 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<double> 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<uint8_t
|
|||
Vector3 *w = arr_3d.ptrw();
|
||||
|
||||
if (p_format & ARRAY_FLAG_COMPRESS_ATTRIBUTES) {
|
||||
bool using_normals_tangents = (p_format & RS::ARRAY_FORMAT_NORMAL) && (p_format & RS::ARRAY_FORMAT_TANGENT);
|
||||
|
||||
// We only have vertices to read, so just read them and skip everything else.
|
||||
if (!using_normals_tangents) {
|
||||
if (!(p_format & RS::ARRAY_FORMAT_NORMAL)) {
|
||||
for (int j = 0; j < p_vertex_len; j++) {
|
||||
const uint16_t *v = reinterpret_cast<const uint16_t *>(&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);
|
||||
|
|
Loading…
Reference in New Issue