mirror of
https://github.com/xivdev/Penumbra.git
synced 2025-12-15 21:24:18 +01:00
Cleanup, fix tangent/normal mixup.
This commit is contained in:
parent
47e6e70272
commit
da1b9e9e90
3 changed files with 29 additions and 31 deletions
|
|
@ -23,7 +23,7 @@ public class MeshExporter
|
||||||
? scene.AddSkinnedMesh(data.Mesh, Matrix4x4.Identity, joints)
|
? scene.AddSkinnedMesh(data.Mesh, Matrix4x4.Identity, joints)
|
||||||
: scene.AddRigidMesh(data.Mesh, Matrix4x4.Identity);
|
: scene.AddRigidMesh(data.Mesh, Matrix4x4.Identity);
|
||||||
|
|
||||||
var extras = new Dictionary<string, object>();
|
var extras = new Dictionary<string, object>(data.Attributes.Length);
|
||||||
foreach (var attribute in data.Attributes)
|
foreach (var attribute in data.Attributes)
|
||||||
extras.Add(attribute, true);
|
extras.Add(attribute, true);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -140,7 +140,7 @@ public class SubMeshImporter
|
||||||
|
|
||||||
private void BuildIndices()
|
private void BuildIndices()
|
||||||
{
|
{
|
||||||
// TODO: glTF supports a bunch of primitive types, ref. Schema2.PrimitiveType. All this code is currently assuming that it's using plain triangles (4). It should probably be generalised to other formats - I _suspect_ we should be able to get away with evaulating the indices to triangles with GetTriangleIndices, but will need investigation.
|
// TODO: glTF supports a bunch of primitive types, ref. Schema2.PrimitiveType. All this code is currently assuming that it's using plain triangles (4). It should probably be generalised to other formats - I _suspect_ we should be able to get away with evaluating the indices to triangles with GetTriangleIndices, but will need investigation.
|
||||||
_indices = _primitive.GetIndices().Select(idx => (ushort)idx).ToArray();
|
_indices = _primitive.GetIndices().Select(idx => (ushort)idx).ToArray();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -244,7 +244,7 @@ public class VertexAttribute
|
||||||
|
|
||||||
public static VertexAttribute? Tangent1(Accessors accessors, IEnumerable<Accessors> morphAccessors, ushort[] indices)
|
public static VertexAttribute? Tangent1(Accessors accessors, IEnumerable<Accessors> morphAccessors, ushort[] indices)
|
||||||
{
|
{
|
||||||
if (!accessors.TryGetValue("NORMAL", out var normalAccessor))
|
if (!accessors.TryGetValue("NORMAL", out var normalAccessor))
|
||||||
{
|
{
|
||||||
Penumbra.Log.Warning("Normals are required to facilitate import or calculation of tangents.");
|
Penumbra.Log.Warning("Normals are required to facilitate import or calculation of tangents.");
|
||||||
return null;
|
return null;
|
||||||
|
|
@ -261,7 +261,7 @@ public class VertexAttribute
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
var element = new MdlStructs.VertexElement()
|
var element = new MdlStructs.VertexElement
|
||||||
{
|
{
|
||||||
Stream = 1,
|
Stream = 1,
|
||||||
Type = (byte)MdlFile.VertexType.ByteFloat4,
|
Type = (byte)MdlFile.VertexType.ByteFloat4,
|
||||||
|
|
@ -269,26 +269,23 @@ public class VertexAttribute
|
||||||
};
|
};
|
||||||
|
|
||||||
// Per glTF specification, TANGENT morph values are stored as vec3, with the W component always considered to be 0.
|
// Per glTF specification, TANGENT morph values are stored as vec3, with the W component always considered to be 0.
|
||||||
var tangentMorphValues = morphAccessors
|
var morphValues = morphAccessors
|
||||||
.Select(a => a.GetValueOrDefault("TANGENT")?.AsVector3Array())
|
.Select(a => (Tangent: a.GetValueOrDefault("TANGENT")?.AsVector3Array(),
|
||||||
.ToArray();
|
Normal: a.GetValueOrDefault("NORMAL")?.AsVector3Array()))
|
||||||
|
.ToList();
|
||||||
var normalMorphValues = morphAccessors
|
|
||||||
.Select(a => a.GetValueOrDefault("TANGENT")?.AsVector3Array())
|
|
||||||
.ToArray();
|
|
||||||
|
|
||||||
return new VertexAttribute(
|
return new VertexAttribute(
|
||||||
element,
|
element,
|
||||||
index => BuildBitangent(tangents[index], normals[index]),
|
index => BuildBitangent(tangents[index], normals[index]),
|
||||||
buildMorph: (morphIndex, vertexIndex) =>
|
buildMorph: (morphIndex, vertexIndex) =>
|
||||||
{
|
{
|
||||||
var tangent = tangents[vertexIndex];
|
var tangent = tangents[vertexIndex];
|
||||||
var tangentDelta = tangentMorphValues[morphIndex]?[vertexIndex];
|
var tangentDelta = morphValues[morphIndex].Tangent?[vertexIndex];
|
||||||
if (tangentDelta != null)
|
if (tangentDelta != null)
|
||||||
tangent += new Vector4(tangentDelta.Value, 0);
|
tangent += new Vector4(tangentDelta.Value, 0);
|
||||||
|
|
||||||
var normal = normals[vertexIndex];
|
var normal = normals[vertexIndex];
|
||||||
var normalDelta = normalMorphValues[morphIndex]?[vertexIndex];
|
var normalDelta = morphValues[morphIndex].Normal?[vertexIndex];
|
||||||
if (normalDelta != null)
|
if (normalDelta != null)
|
||||||
normal += normalDelta.Value;
|
normal += normalDelta.Value;
|
||||||
|
|
||||||
|
|
@ -297,13 +294,13 @@ public class VertexAttribute
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary> Build a byte array representing bitagent data computed from the provided tangent and normal. </summary>
|
/// <summary> Build a byte array representing bitangent data computed from the provided tangent and normal. </summary>
|
||||||
/// <remarks> XIV primarily stores bitangents, rather than tangents as with most other software, so we calculate on import. </remarks>
|
/// <remarks> XIV primarily stores bitangents, rather than tangents as with most other software, so we calculate on import. </remarks>
|
||||||
private static byte[] BuildBitangent(Vector4 tangent, Vector3 normal)
|
private static byte[] BuildBitangent(Vector4 tangent, Vector3 normal)
|
||||||
{
|
{
|
||||||
var handedness = tangent.W;
|
var handedness = tangent.W;
|
||||||
var tangent3 = new Vector3(tangent.X, tangent.Y, tangent.Z);
|
var tangent3 = new Vector3(tangent.X, tangent.Y, tangent.Z);
|
||||||
var bitangent = Vector3.Normalize(Vector3.Cross(normal, tangent3));
|
var bitangent = Vector3.Normalize(Vector3.Cross(normal, tangent3));
|
||||||
bitangent *= handedness;
|
bitangent *= handedness;
|
||||||
|
|
||||||
// Byte floats encode 0..1, and bitangents are stored as -1..1. Convert.
|
// Byte floats encode 0..1, and bitangents are stored as -1..1. Convert.
|
||||||
|
|
@ -319,17 +316,18 @@ public class VertexAttribute
|
||||||
return null;
|
return null;
|
||||||
|
|
||||||
var positions = accessors["POSITION"].AsVector3Array();
|
var positions = accessors["POSITION"].AsVector3Array();
|
||||||
var uvs = uvAccessor.AsVector2Array();
|
var uvs = uvAccessor.AsVector2Array();
|
||||||
|
|
||||||
// TODO: Surface this in the UI.
|
// TODO: Surface this in the UI.
|
||||||
Penumbra.Log.Warning("Calculating tangents, this may result in degraded light interaction. For best results, ensure tangents are caculated or retained during export from 3D modelling tools.");
|
Penumbra.Log.Warning(
|
||||||
|
"Calculating tangents, this may result in degraded light interaction. For best results, ensure tangents are caculated or retained during export from 3D modelling tools.");
|
||||||
|
|
||||||
var vertexCount = positions.Count;
|
var vertexCount = positions.Count;
|
||||||
|
|
||||||
// https://github.com/TexTools/xivModdingFramework/blob/master/xivModdingFramework/Models/Helpers/ModelModifiers.cs#L1569
|
// https://github.com/TexTools/xivModdingFramework/blob/master/xivModdingFramework/Models/Helpers/ModelModifiers.cs#L1569
|
||||||
// https://gamedev.stackexchange.com/a/68617
|
// https://gamedev.stackexchange.com/a/68617
|
||||||
// https://marti.works/posts/post-calculating-tangents-for-your-mesh/post/
|
// https://marti.works/posts/post-calculating-tangents-for-your-mesh/post/
|
||||||
var tangents = new Vector3[vertexCount];
|
var tangents = new Vector3[vertexCount];
|
||||||
var bitangents = new Vector3[vertexCount];
|
var bitangents = new Vector3[vertexCount];
|
||||||
|
|
||||||
// Iterate over triangles, calculating tangents relative to the UVs.
|
// Iterate over triangles, calculating tangents relative to the UVs.
|
||||||
|
|
@ -344,16 +342,16 @@ public class VertexAttribute
|
||||||
var position2 = positions[vertexIndex2];
|
var position2 = positions[vertexIndex2];
|
||||||
var position3 = positions[vertexIndex3];
|
var position3 = positions[vertexIndex3];
|
||||||
|
|
||||||
var texcoord1 = uvs[vertexIndex1];
|
var texCoord1 = uvs[vertexIndex1];
|
||||||
var texcoord2 = uvs[vertexIndex2];
|
var texCoord2 = uvs[vertexIndex2];
|
||||||
var texcoord3 = uvs[vertexIndex3];
|
var texCoord3 = uvs[vertexIndex3];
|
||||||
|
|
||||||
// Calculate deltas for the position XYZ, and texcoord UV.
|
// Calculate deltas for the position XYZ, and texcoord UV.
|
||||||
var edge1 = position2 - position1;
|
var edge1 = position2 - position1;
|
||||||
var edge2 = position3 - position1;
|
var edge2 = position3 - position1;
|
||||||
|
|
||||||
var uv1 = texcoord2 - texcoord1;
|
var uv1 = texCoord2 - texCoord1;
|
||||||
var uv2 = texcoord3 - texcoord1;
|
var uv2 = texCoord3 - texCoord1;
|
||||||
|
|
||||||
// Solve.
|
// Solve.
|
||||||
var r = 1.0f / (uv1.X * uv2.Y - uv1.Y * uv2.X);
|
var r = 1.0f / (uv1.X * uv2.Y - uv1.Y * uv2.X);
|
||||||
|
|
@ -378,7 +376,7 @@ public class VertexAttribute
|
||||||
bitangents[vertexIndex3] += bitangent;
|
bitangents[vertexIndex3] += bitangent;
|
||||||
}
|
}
|
||||||
|
|
||||||
// All the triangles have been calcualted, normalise the results for each vertex.
|
// All the triangles have been calculated, normalise the results for each vertex.
|
||||||
var result = new Vector4[vertexCount];
|
var result = new Vector4[vertexCount];
|
||||||
for (var vertexIndex = 0; vertexIndex < vertexCount; vertexIndex++)
|
for (var vertexIndex = 0; vertexIndex < vertexCount; vertexIndex++)
|
||||||
{
|
{
|
||||||
|
|
@ -387,7 +385,7 @@ public class VertexAttribute
|
||||||
var b = bitangents[vertexIndex];
|
var b = bitangents[vertexIndex];
|
||||||
|
|
||||||
// Gram-Schmidt orthogonalize and calculate handedness.
|
// Gram-Schmidt orthogonalize and calculate handedness.
|
||||||
var tangent = Vector3.Normalize(t - n * Vector3.Dot(n, t));
|
var tangent = Vector3.Normalize(t - n * Vector3.Dot(n, t));
|
||||||
var handedness = Vector3.Dot(Vector3.Cross(t, b), n) > 0 ? 1 : -1;
|
var handedness = Vector3.Dot(Vector3.Cross(t, b), n) > 0 ? 1 : -1;
|
||||||
|
|
||||||
result[vertexIndex] = new Vector4(tangent, handedness);
|
result[vertexIndex] = new Vector4(tangent, handedness);
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue