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.
This commit is contained in:
parent
6afd320984
commit
d1043a5f93
12 changed files with 127 additions and 19 deletions
|
@ -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 a new issue