mirror of
https://github.com/xivdev/Penumbra.git
synced 2025-12-12 18:27:24 +01:00
Init support for DT model i/o
This commit is contained in:
parent
389c42e68f
commit
8084f48144
3 changed files with 281 additions and 78 deletions
|
|
@ -311,15 +311,28 @@ public class MeshExporter
|
||||||
MdlFile.VertexType.Single3 => new Vector3(reader.ReadSingle(), reader.ReadSingle(), reader.ReadSingle()),
|
MdlFile.VertexType.Single3 => new Vector3(reader.ReadSingle(), reader.ReadSingle(), reader.ReadSingle()),
|
||||||
MdlFile.VertexType.Single4 => new Vector4(reader.ReadSingle(), reader.ReadSingle(), reader.ReadSingle(), reader.ReadSingle()),
|
MdlFile.VertexType.Single4 => new Vector4(reader.ReadSingle(), reader.ReadSingle(), reader.ReadSingle(), reader.ReadSingle()),
|
||||||
MdlFile.VertexType.UByte4 => reader.ReadBytes(4),
|
MdlFile.VertexType.UByte4 => reader.ReadBytes(4),
|
||||||
MdlFile.VertexType.NByte4 => new Vector4(reader.ReadByte() / 255f, reader.ReadByte() / 255f, reader.ReadByte() / 255f,
|
MdlFile.VertexType.NByte4 => new Vector4(reader.ReadByte() / 255f, reader.ReadByte() / 255f, reader.ReadByte() / 255f, reader.ReadByte() / 255f),
|
||||||
reader.ReadByte() / 255f),
|
|
||||||
MdlFile.VertexType.Half2 => new Vector2((float)reader.ReadHalf(), (float)reader.ReadHalf()),
|
MdlFile.VertexType.Half2 => new Vector2((float)reader.ReadHalf(), (float)reader.ReadHalf()),
|
||||||
MdlFile.VertexType.Half4 => new Vector4((float)reader.ReadHalf(), (float)reader.ReadHalf(), (float)reader.ReadHalf(),
|
MdlFile.VertexType.Half4 => new Vector4((float)reader.ReadHalf(), (float)reader.ReadHalf(), (float)reader.ReadHalf(), (float)reader.ReadHalf()),
|
||||||
(float)reader.ReadHalf()),
|
MdlFile.VertexType.UShort4 => ReadUShort4(reader),
|
||||||
|
|
||||||
var other => throw _notifier.Exception<ArgumentOutOfRangeException>($"Unhandled vertex type {other}"),
|
var other => throw _notifier.Exception<ArgumentOutOfRangeException>($"Unhandled vertex type {other}"),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private byte[] ReadUShort4(BinaryReader reader)
|
||||||
|
{
|
||||||
|
var buffer = reader.ReadBytes(8);
|
||||||
|
var byteValues = new byte[8];
|
||||||
|
byteValues[0] = buffer[0];
|
||||||
|
byteValues[4] = buffer[1];
|
||||||
|
byteValues[1] = buffer[2];
|
||||||
|
byteValues[5] = buffer[3];
|
||||||
|
byteValues[2] = buffer[4];
|
||||||
|
byteValues[6] = buffer[5];
|
||||||
|
byteValues[3] = buffer[6];
|
||||||
|
byteValues[7] = buffer[7];
|
||||||
|
return byteValues;
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary> Get the vertex geometry type for this mesh's vertex usages. </summary>
|
/// <summary> Get the vertex geometry type for this mesh's vertex usages. </summary>
|
||||||
private Type GetGeometryType(IReadOnlyDictionary<MdlFile.VertexUsage, MdlFile.VertexType> usages)
|
private Type GetGeometryType(IReadOnlyDictionary<MdlFile.VertexUsage, MdlFile.VertexType> usages)
|
||||||
|
|
@ -444,7 +457,16 @@ public class MeshExporter
|
||||||
private static Type GetSkinningType(IReadOnlyDictionary<MdlFile.VertexUsage, MdlFile.VertexType> usages)
|
private static Type GetSkinningType(IReadOnlyDictionary<MdlFile.VertexUsage, MdlFile.VertexType> usages)
|
||||||
{
|
{
|
||||||
if (usages.ContainsKey(MdlFile.VertexUsage.BlendWeights) && usages.ContainsKey(MdlFile.VertexUsage.BlendIndices))
|
if (usages.ContainsKey(MdlFile.VertexUsage.BlendWeights) && usages.ContainsKey(MdlFile.VertexUsage.BlendIndices))
|
||||||
return typeof(VertexJoints4);
|
{
|
||||||
|
if (usages[MdlFile.VertexUsage.BlendWeights] == MdlFile.VertexType.UShort4)
|
||||||
|
{
|
||||||
|
return typeof(VertexJoints8);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return typeof(VertexJoints4);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return typeof(VertexEmpty);
|
return typeof(VertexEmpty);
|
||||||
}
|
}
|
||||||
|
|
@ -455,15 +477,17 @@ public class MeshExporter
|
||||||
if (_skinningType == typeof(VertexEmpty))
|
if (_skinningType == typeof(VertexEmpty))
|
||||||
return new VertexEmpty();
|
return new VertexEmpty();
|
||||||
|
|
||||||
if (_skinningType == typeof(VertexJoints4))
|
if (_skinningType == typeof(VertexJoints4) || _skinningType == typeof(VertexJoints8))
|
||||||
{
|
{
|
||||||
if (_boneIndexMap == null)
|
if (_boneIndexMap == null)
|
||||||
throw _notifier.Exception("Tried to build skinned vertex but no bone mappings are available.");
|
throw _notifier.Exception("Tried to build skinned vertex but no bone mappings are available.");
|
||||||
|
|
||||||
var indices = ToByteArray(attributes[MdlFile.VertexUsage.BlendIndices]);
|
var indiciesData = attributes[MdlFile.VertexUsage.BlendIndices];
|
||||||
var weights = ToVector4(attributes[MdlFile.VertexUsage.BlendWeights]);
|
var weightsData = attributes[MdlFile.VertexUsage.BlendWeights];
|
||||||
|
var indices = ToByteArray(indiciesData);
|
||||||
var bindings = Enumerable.Range(0, 4)
|
var weights = ToFloatArray(weightsData);
|
||||||
|
|
||||||
|
var bindings = Enumerable.Range(0, indices.Length)
|
||||||
.Select(bindingIndex =>
|
.Select(bindingIndex =>
|
||||||
{
|
{
|
||||||
// NOTE: I've not seen any files that throw this error that aren't completely broken.
|
// NOTE: I've not seen any files that throw this error that aren't completely broken.
|
||||||
|
|
@ -474,7 +498,13 @@ public class MeshExporter
|
||||||
return (jointIndex, weights[bindingIndex]);
|
return (jointIndex, weights[bindingIndex]);
|
||||||
})
|
})
|
||||||
.ToArray();
|
.ToArray();
|
||||||
return new VertexJoints4(bindings);
|
|
||||||
|
return bindings.Length switch
|
||||||
|
{
|
||||||
|
4 => new VertexJoints4(bindings),
|
||||||
|
8 => new VertexJoints8(bindings),
|
||||||
|
_ => throw _notifier.Exception($"Invalid number of bone bindings {bindings.Length}.")
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
throw _notifier.Exception($"Unknown skinning type {_skinningType}");
|
throw _notifier.Exception($"Unknown skinning type {_skinningType}");
|
||||||
|
|
@ -517,4 +547,12 @@ public class MeshExporter
|
||||||
byte[] value => value,
|
byte[] value => value,
|
||||||
_ => throw new ArgumentOutOfRangeException($"Invalid byte[] input {data}"),
|
_ => throw new ArgumentOutOfRangeException($"Invalid byte[] input {data}"),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
private static float[] ToFloatArray(object data)
|
||||||
|
=> data switch
|
||||||
|
{
|
||||||
|
byte[] value => value.Select(x => x / 255f).ToArray(),
|
||||||
|
_ => throw new ArgumentOutOfRangeException($"Invalid float[] input {data}"),
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -194,17 +194,37 @@ public class MeshImporter(IEnumerable<Node> nodes, IoNotifier notifier)
|
||||||
foreach (var (primitive, primitiveIndex) in node.Mesh.Primitives.WithIndex())
|
foreach (var (primitive, primitiveIndex) in node.Mesh.Primitives.WithIndex())
|
||||||
{
|
{
|
||||||
// Per glTF specification, an asset with a skin MUST contain skinning attributes on its meshes.
|
// Per glTF specification, an asset with a skin MUST contain skinning attributes on its meshes.
|
||||||
var jointsAccessor = primitive.GetVertexAccessor("JOINTS_0")?.AsVector4Array();
|
var joints0Accessor = primitive.GetVertexAccessor("JOINTS_0")?.AsVector4Array();
|
||||||
var weightsAccessor = primitive.GetVertexAccessor("WEIGHTS_0")?.AsVector4Array();
|
var weights0Accessor = primitive.GetVertexAccessor("WEIGHTS_0")?.AsVector4Array();
|
||||||
|
|
||||||
if (jointsAccessor == null || weightsAccessor == null)
|
if (joints0Accessor == null || weights0Accessor == null)
|
||||||
throw notifier.Exception($"Primitive {primitiveIndex} is skinned but does not contain skinning vertex attributes.");
|
throw notifier.Exception($"Primitive {primitiveIndex} is skinned but does not contain skinning vertex attributes.");
|
||||||
|
|
||||||
// Build a set of joints that are referenced by this mesh.
|
// Build a set of joints that are referenced by this mesh.
|
||||||
for (var i = 0; i < jointsAccessor.Count; i++)
|
for (var i = 0; i < joints0Accessor.Count; i++)
|
||||||
{
|
{
|
||||||
var joints = jointsAccessor[i];
|
var joints = joints0Accessor[i];
|
||||||
var weights = weightsAccessor[i];
|
var weights = weights0Accessor[i];
|
||||||
|
for (var index = 0; index < 4; index++)
|
||||||
|
{
|
||||||
|
// If a joint has absolutely no weight, we omit the bone entirely.
|
||||||
|
if (weights[index] == 0)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
usedJoints.Add((ushort)joints[index]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var joints1Accessor = primitive.GetVertexAccessor("JOINTS_1")?.AsVector4Array();
|
||||||
|
var weights1Accessor = primitive.GetVertexAccessor("WEIGHTS_1")?.AsVector4Array();
|
||||||
|
|
||||||
|
if (joints1Accessor == null || weights1Accessor == null)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
for (var i = 0; i < joints1Accessor.Count; i++)
|
||||||
|
{
|
||||||
|
var joints = joints1Accessor[i];
|
||||||
|
var weights = weights1Accessor[i];
|
||||||
for (var index = 0; index < 4; index++)
|
for (var index = 0; index < 4; index++)
|
||||||
{
|
{
|
||||||
// If a joint has absolutely no weight, we omit the bone entirely.
|
// If a joint has absolutely no weight, we omit the bone entirely.
|
||||||
|
|
|
||||||
|
|
@ -40,6 +40,7 @@ public class VertexAttribute
|
||||||
MdlFile.VertexType.NByte4 => 4,
|
MdlFile.VertexType.NByte4 => 4,
|
||||||
MdlFile.VertexType.Half2 => 4,
|
MdlFile.VertexType.Half2 => 4,
|
||||||
MdlFile.VertexType.Half4 => 8,
|
MdlFile.VertexType.Half4 => 8,
|
||||||
|
MdlFile.VertexType.UShort4 => 8,
|
||||||
|
|
||||||
_ => throw new Exception($"Unhandled vertex type {(MdlFile.VertexType)Element.Type}"),
|
_ => throw new Exception($"Unhandled vertex type {(MdlFile.VertexType)Element.Type}"),
|
||||||
};
|
};
|
||||||
|
|
@ -121,89 +122,219 @@ public class VertexAttribute
|
||||||
|
|
||||||
public static VertexAttribute? BlendWeight(Accessors accessors, IoNotifier notifier)
|
public static VertexAttribute? BlendWeight(Accessors accessors, IoNotifier notifier)
|
||||||
{
|
{
|
||||||
if (!accessors.TryGetValue("WEIGHTS_0", out var accessor))
|
if (!accessors.TryGetValue("WEIGHTS_0", out var weights0Accessor))
|
||||||
return null;
|
return null;
|
||||||
|
|
||||||
if (!accessors.ContainsKey("JOINTS_0"))
|
if (!accessors.ContainsKey("JOINTS_0"))
|
||||||
throw notifier.Exception("Mesh contained WEIGHTS_0 attribute but no corresponding JOINTS_0 attribute.");
|
throw notifier.Exception("Mesh contained WEIGHTS_0 attribute but no corresponding JOINTS_0 attribute.");
|
||||||
|
|
||||||
var element = new MdlStructs.VertexElement()
|
if (accessors.TryGetValue("WEIGHTS_1", out var weights1Accessor))
|
||||||
{
|
{
|
||||||
Stream = 0,
|
if (!accessors.ContainsKey("JOINTS_1"))
|
||||||
Type = (byte)MdlFile.VertexType.NByte4,
|
throw notifier.Exception("Mesh contained WEIGHTS_1 attribute but no corresponding JOINTS_1 attribute.");
|
||||||
Usage = (byte)MdlFile.VertexUsage.BlendWeights,
|
|
||||||
};
|
var element = new MdlStructs.VertexElement()
|
||||||
|
{
|
||||||
|
Stream = 0,
|
||||||
|
Type = (byte)MdlFile.VertexType.UShort4,
|
||||||
|
Usage = (byte)MdlFile.VertexUsage.BlendWeights,
|
||||||
|
};
|
||||||
|
|
||||||
var values = accessor.AsVector4Array();
|
var weights0 = weights0Accessor.AsVector4Array();
|
||||||
|
var weights1 = weights1Accessor.AsVector4Array();
|
||||||
|
|
||||||
return new VertexAttribute(
|
return new VertexAttribute(
|
||||||
element,
|
element,
|
||||||
index => {
|
index => {
|
||||||
// Blend weights are _very_ sensitive to float imprecision - a vertex sum being off
|
var weight0 = weights0[index];
|
||||||
// by one, such as 256, is enough to cause a visible defect. To avoid this, we tweak
|
var weight1 = weights1[index];
|
||||||
// the converted values to have the expected sum, preferencing values with minimal differences.
|
var originalData = BuildUshort4(weight0, weight1);
|
||||||
var originalValues = values[index];
|
var byteValues = originalData.Select(x => (byte)Math.Round(x * 255f)).ToArray();
|
||||||
var byteValues = BuildNByte4(originalValues);
|
return AdjustByteArray(byteValues, originalData);
|
||||||
|
|
||||||
var adjustment = 255 - byteValues.Select(value => (int)value).Sum();
|
|
||||||
while (adjustment != 0)
|
|
||||||
{
|
|
||||||
var convertedValues = byteValues.Select(value => value * (1f / 255f)).ToArray();
|
|
||||||
var closestIndex = Enumerable.Range(0, 4)
|
|
||||||
.Where(index => {
|
|
||||||
var byteValue = byteValues[index];
|
|
||||||
if (adjustment < 0) return byteValue > 0;
|
|
||||||
if (adjustment > 0) return byteValue < 255;
|
|
||||||
return true;
|
|
||||||
})
|
|
||||||
.Select(index => (index, delta: Math.Abs(originalValues[index] - convertedValues[index])))
|
|
||||||
.MinBy(x => x.delta)
|
|
||||||
.index;
|
|
||||||
byteValues[closestIndex] = (byte)(byteValues[closestIndex] + Math.CopySign(1, adjustment));
|
|
||||||
adjustment = 255 - byteValues.Select(value => (int)value).Sum();
|
|
||||||
}
|
}
|
||||||
|
);
|
||||||
return byteValues;
|
}
|
||||||
}
|
else
|
||||||
);
|
{
|
||||||
|
var element = new MdlStructs.VertexElement()
|
||||||
|
{
|
||||||
|
Stream = 0,
|
||||||
|
Type = (byte)MdlFile.VertexType.UShort4,
|
||||||
|
Usage = (byte)MdlFile.VertexUsage.BlendWeights,
|
||||||
|
};
|
||||||
|
|
||||||
|
var weights0 = weights0Accessor.AsVector4Array();
|
||||||
|
|
||||||
|
return new VertexAttribute(
|
||||||
|
element,
|
||||||
|
index => {
|
||||||
|
var weight0 = weights0[index];
|
||||||
|
var weight1 = Vector4.Zero;
|
||||||
|
var originalData = BuildUshort4(weight0, weight1);
|
||||||
|
var byteValues = originalData.Select(x => (byte)Math.Round(x * 255f)).ToArray();
|
||||||
|
return AdjustByteArray(byteValues, originalData);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
/*var element = new MdlStructs.VertexElement()
|
||||||
|
{
|
||||||
|
Stream = 0,
|
||||||
|
Type = (byte)MdlFile.VertexType.NByte4,
|
||||||
|
Usage = (byte)MdlFile.VertexUsage.BlendWeights,
|
||||||
|
};
|
||||||
|
|
||||||
|
var weights0 = weights0Accessor.AsVector4Array();
|
||||||
|
|
||||||
|
return new VertexAttribute(
|
||||||
|
element,
|
||||||
|
index =>
|
||||||
|
{
|
||||||
|
var weight0 = weights0[index];
|
||||||
|
var originalData = new[] { weight0.X, weight0.Y, weight0.Z, weight0.W };
|
||||||
|
var byteValues = originalData.Select(x => (byte)Math.Round(x * 255f)).ToArray();
|
||||||
|
var newByteValues = AdjustByteArray(byteValues, originalData);
|
||||||
|
if (!newByteValues.SequenceEqual(byteValues))
|
||||||
|
notifier.Warning("Adjusted blend weights to maintain precision.");
|
||||||
|
return newByteValues;
|
||||||
|
});*/
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static byte[] AdjustByteArray(byte[] byteValues, float[] originalValues)
|
||||||
|
{
|
||||||
|
// Blend weights are _very_ sensitive to float imprecision - a vertex sum being off
|
||||||
|
// by one, such as 256, is enough to cause a visible defect. To avoid this, we tweak
|
||||||
|
// the converted values to have the expected sum, preferencing values with minimal differences.
|
||||||
|
var adjustment = 255 - byteValues.Select(value => (int)value).Sum();
|
||||||
|
while (adjustment != 0)
|
||||||
|
{
|
||||||
|
var convertedValues = byteValues.Select(value => value * (1f / 255f)).ToArray();
|
||||||
|
var closestIndex = Enumerable.Range(0, byteValues.Length)
|
||||||
|
.Where(index =>
|
||||||
|
{
|
||||||
|
var byteValue = byteValues[index];
|
||||||
|
if (adjustment < 0)
|
||||||
|
return byteValue > 0;
|
||||||
|
if (adjustment > 0)
|
||||||
|
return byteValue < 255;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
})
|
||||||
|
.Select(index => (index, delta: Math.Abs(originalValues[index] - convertedValues[index])))
|
||||||
|
.MinBy(x => x.delta)
|
||||||
|
.index;
|
||||||
|
byteValues[closestIndex] = (byte)(byteValues[closestIndex] + Math.CopySign(1, adjustment));
|
||||||
|
adjustment = 255 - byteValues.Select(value => (int)value).Sum();
|
||||||
|
}
|
||||||
|
|
||||||
|
return byteValues;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static VertexAttribute? BlendIndex(Accessors accessors, IDictionary<ushort, ushort>? boneMap, IoNotifier notifier)
|
public static VertexAttribute? BlendIndex(Accessors accessors, IDictionary<ushort, ushort>? boneMap, IoNotifier notifier)
|
||||||
{
|
{
|
||||||
if (!accessors.TryGetValue("JOINTS_0", out var jointsAccessor))
|
if (!accessors.TryGetValue("JOINTS_0", out var joints0Accessor))
|
||||||
return null;
|
return null;
|
||||||
|
|
||||||
if (!accessors.TryGetValue("WEIGHTS_0", out var weightsAccessor))
|
if (!accessors.TryGetValue("WEIGHTS_0", out var weights0Accessor))
|
||||||
throw notifier.Exception("Mesh contained JOINTS_0 attribute but no corresponding WEIGHTS_0 attribute.");
|
throw notifier.Exception("Mesh contained JOINTS_0 attribute but no corresponding WEIGHTS_0 attribute.");
|
||||||
|
|
||||||
if (boneMap == null)
|
if (boneMap == null)
|
||||||
throw notifier.Exception("Mesh contained JOINTS_0 attribute but no bone mapping was created.");
|
throw notifier.Exception("Mesh contained JOINTS_0 attribute but no bone mapping was created.");
|
||||||
|
|
||||||
var element = new MdlStructs.VertexElement()
|
var joints0 = joints0Accessor.AsVector4Array();
|
||||||
|
var weights0 = weights0Accessor.AsVector4Array();
|
||||||
|
|
||||||
|
if (accessors.TryGetValue("JOINTS_1", out var joints1Accessor))
|
||||||
{
|
{
|
||||||
Stream = 0,
|
if (!accessors.TryGetValue("WEIGHTS_1", out var weights1Accessor))
|
||||||
Type = (byte)MdlFile.VertexType.UByte4,
|
throw notifier.Exception("Mesh contained JOINTS_1 attribute but no corresponding WEIGHTS_1 attribute.");
|
||||||
Usage = (byte)MdlFile.VertexUsage.BlendIndices,
|
|
||||||
};
|
|
||||||
|
|
||||||
var joints = jointsAccessor.AsVector4Array();
|
var element = new MdlStructs.VertexElement
|
||||||
var weights = weightsAccessor.AsVector4Array();
|
|
||||||
|
|
||||||
return new VertexAttribute(
|
|
||||||
element,
|
|
||||||
index =>
|
|
||||||
{
|
{
|
||||||
var gltfIndices = joints[index];
|
Stream = 0,
|
||||||
var gltfWeights = weights[index];
|
Type = (byte)MdlFile.VertexType.UShort4,
|
||||||
|
Usage = (byte)MdlFile.VertexUsage.BlendIndices,
|
||||||
|
};
|
||||||
|
|
||||||
return BuildUByte4(new Vector4(
|
var joints1 = joints1Accessor.AsVector4Array();
|
||||||
gltfWeights.X == 0 ? 0 : boneMap[(ushort)gltfIndices.X],
|
var weights1 = weights1Accessor.AsVector4Array();
|
||||||
gltfWeights.Y == 0 ? 0 : boneMap[(ushort)gltfIndices.Y],
|
|
||||||
gltfWeights.Z == 0 ? 0 : boneMap[(ushort)gltfIndices.Z],
|
return new VertexAttribute(
|
||||||
gltfWeights.W == 0 ? 0 : boneMap[(ushort)gltfIndices.W]
|
element,
|
||||||
));
|
index =>
|
||||||
}
|
{
|
||||||
);
|
var gltfIndices0 = joints0[index];
|
||||||
|
var gltfWeights0 = weights0[index];
|
||||||
|
var gltfIndices1 = joints1[index];
|
||||||
|
var gltfWeights1 = weights1[index];
|
||||||
|
var v0 = new Vector4(
|
||||||
|
gltfWeights0.X == 0 ? 0 : boneMap[(ushort)gltfIndices0.X],
|
||||||
|
gltfWeights0.Y == 0 ? 0 : boneMap[(ushort)gltfIndices0.Y],
|
||||||
|
gltfWeights0.Z == 0 ? 0 : boneMap[(ushort)gltfIndices0.Z],
|
||||||
|
gltfWeights0.W == 0 ? 0 : boneMap[(ushort)gltfIndices0.W]
|
||||||
|
);
|
||||||
|
var v1 = new Vector4(
|
||||||
|
gltfWeights1.X == 0 ? 0 : boneMap[(ushort)gltfIndices1.X],
|
||||||
|
gltfWeights1.Y == 0 ? 0 : boneMap[(ushort)gltfIndices1.Y],
|
||||||
|
gltfWeights1.Z == 0 ? 0 : boneMap[(ushort)gltfIndices1.Z],
|
||||||
|
gltfWeights1.W == 0 ? 0 : boneMap[(ushort)gltfIndices1.W]
|
||||||
|
);
|
||||||
|
|
||||||
|
var byteValues = BuildUshort4(v0, v1);
|
||||||
|
|
||||||
|
return byteValues.Select(x => (byte)x).ToArray();
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
var element = new MdlStructs.VertexElement
|
||||||
|
{
|
||||||
|
Stream = 0,
|
||||||
|
Type = (byte)MdlFile.VertexType.UShort4,
|
||||||
|
Usage = (byte)MdlFile.VertexUsage.BlendIndices,
|
||||||
|
};
|
||||||
|
|
||||||
|
return new VertexAttribute(
|
||||||
|
element,
|
||||||
|
index =>
|
||||||
|
{
|
||||||
|
var gltfIndices0 = joints0[index];
|
||||||
|
var gltfWeights0 = weights0[index];
|
||||||
|
var v0 = new Vector4(
|
||||||
|
gltfWeights0.X == 0 ? 0 : boneMap[(ushort)gltfIndices0.X],
|
||||||
|
gltfWeights0.Y == 0 ? 0 : boneMap[(ushort)gltfIndices0.Y],
|
||||||
|
gltfWeights0.Z == 0 ? 0 : boneMap[(ushort)gltfIndices0.Z],
|
||||||
|
gltfWeights0.W == 0 ? 0 : boneMap[(ushort)gltfIndices0.W]
|
||||||
|
);
|
||||||
|
var v1 = Vector4.Zero;
|
||||||
|
var byteValues = BuildUshort4(v0, v1);
|
||||||
|
|
||||||
|
return byteValues.Select(x => (byte)x).ToArray();
|
||||||
|
}
|
||||||
|
);
|
||||||
|
/*var element = new MdlStructs.VertexElement()
|
||||||
|
{
|
||||||
|
Stream = 0,
|
||||||
|
Type = (byte)MdlFile.VertexType.UByte4,
|
||||||
|
Usage = (byte)MdlFile.VertexUsage.BlendIndices,
|
||||||
|
};
|
||||||
|
|
||||||
|
return new VertexAttribute(
|
||||||
|
element,
|
||||||
|
index =>
|
||||||
|
{
|
||||||
|
var gltfIndices = joints0[index];
|
||||||
|
var gltfWeights = weights0[index];
|
||||||
|
return BuildUByte4(new Vector4(
|
||||||
|
gltfWeights.X == 0 ? 0 : boneMap[(ushort)gltfIndices.X],
|
||||||
|
gltfWeights.Y == 0 ? 0 : boneMap[(ushort)gltfIndices.Y],
|
||||||
|
gltfWeights.Z == 0 ? 0 : boneMap[(ushort)gltfIndices.Z],
|
||||||
|
gltfWeights.W == 0 ? 0 : boneMap[(ushort)gltfIndices.W]
|
||||||
|
));
|
||||||
|
}
|
||||||
|
);*/
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static VertexAttribute? Normal(Accessors accessors, IEnumerable<Accessors> morphAccessors)
|
public static VertexAttribute? Normal(Accessors accessors, IEnumerable<Accessors> morphAccessors)
|
||||||
|
|
@ -232,7 +363,7 @@ public class VertexAttribute
|
||||||
var value = values[vertexIndex];
|
var value = values[vertexIndex];
|
||||||
|
|
||||||
var delta = morphValues[morphIndex]?[vertexIndex];
|
var delta = morphValues[morphIndex]?[vertexIndex];
|
||||||
if (delta != null)
|
if (delta != null)
|
||||||
value += delta.Value;
|
value += delta.Value;
|
||||||
|
|
||||||
return BuildSingle3(value);
|
return BuildSingle3(value);
|
||||||
|
|
@ -489,4 +620,18 @@ public class VertexAttribute
|
||||||
(byte)Math.Round(input.Z * 255f),
|
(byte)Math.Round(input.Z * 255f),
|
||||||
(byte)Math.Round(input.W * 255f),
|
(byte)Math.Round(input.W * 255f),
|
||||||
];
|
];
|
||||||
|
|
||||||
|
private static float[] BuildUshort4(Vector4 v0, Vector4 v1)
|
||||||
|
{
|
||||||
|
var buf = new float[8];
|
||||||
|
buf[0] = v0.X;
|
||||||
|
buf[4] = v1.X;
|
||||||
|
buf[1] = v0.Y;
|
||||||
|
buf[5] = v1.Y;
|
||||||
|
buf[2] = v0.Z;
|
||||||
|
buf[6] = v1.Z;
|
||||||
|
buf[3] = v0.W;
|
||||||
|
buf[7] = v1.W;
|
||||||
|
return buf;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue