Skip to content

Commit

Permalink
Merge remote-tracking branch 'origin/fix/equal-skinnedmesh-export' in…
Browse files Browse the repository at this point in the history
…to dev
  • Loading branch information
hybridherbst committed Apr 13, 2024
2 parents 2e746d0 + 826ade5 commit d0cfc9d
Show file tree
Hide file tree
Showing 3 changed files with 72 additions and 23 deletions.
31 changes: 26 additions & 5 deletions Runtime/Scripts/GLTFSceneExporter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -449,7 +449,6 @@ public bool Equals(UniquePrimitive other)
{
if (!Equals(Mesh, other.Mesh)) return false;
if (Materials == null && other.Materials == null) return true;
if (!Equals(SkinnedMeshRenderer, other.SkinnedMeshRenderer)) return false;
if (!(Materials != null && other.Materials != null)) return false;
if (!Equals(Materials.Length, other.Materials.Length)) return false;
for (var i = 0; i < Materials.Length; i++)
Expand All @@ -470,10 +469,6 @@ public override int GetHashCode()
unchecked
{
var code = (Mesh != null ? Mesh.GetHashCode() : 0) * 397;
if (SkinnedMeshRenderer != null)
{
code = code ^ SkinnedMeshRenderer.GetHashCode() * 397;
}
if (Materials != null)
{
code = code ^ Materials.Length.GetHashCode() * 397;
Expand Down Expand Up @@ -1094,6 +1089,32 @@ private NodeId ExportNode(Transform nodeTransform)
{
node.Mesh = ExportMesh(nodeTransform.name, uniquePrimitives);
RegisterPrimitivesWithNode(node, uniquePrimitives);

// Node - BlendShape Weights
if (uniquePrimitives[0].SkinnedMeshRenderer)
{
var meshObj = uniquePrimitives[0].Mesh;
var smr = uniquePrimitives[0].SkinnedMeshRenderer;
// Only export the blendShapeWeights into the Node, when it's not the first SkinnedMeshRenderer with the same Mesh
// Because the weights already exported into the GltfMesh
if (smr && meshObj && _meshToBlendShapeAccessors.TryGetValue(meshObj, out var data) && smr != data.firstSkinnedMeshRenderer)
{
var blendShapeWeights = GetBlendShapeWeights(smr, meshObj);
if (blendShapeWeights != null)
{
if (data.weights != null)
{
// Check if the blendShapeWeights has any differences to the weights already exported into the gltfMesh
// When not, we don't need to set the same values to the Node Weights
bool isSame = true;
for (int i = 0; i < blendShapeWeights.Count; i++)
isSame &= System.Math.Abs(blendShapeWeights[i] - data.weights[i]) < double.Epsilon;
if (!isSame)
node.Weights = blendShapeWeights;
}
}
}
}
}
}

Expand Down
54 changes: 36 additions & 18 deletions Runtime/Scripts/SceneExporter/ExporterMeshes.cs
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ public partial class GLTFSceneExporter
{
private struct MeshAccessors
{
public AccessorId aPosition, aNormal, aTangent, aTexcoord0, aTexcoord1, aColor0;
public AccessorId aPosition, aNormal, aTangent, aTexcoord0, aTexcoord1, aColor0, aJoints0, aWeights0;
public Dictionary<int, MeshPrimitive> subMeshPrimitives;
}

Expand All @@ -26,10 +26,12 @@ private struct BlendShapeAccessors
public List<Dictionary<string, AccessorId>> targets;
public List<Double> weights;
public List<string> targetNames;
internal SkinnedMeshRenderer firstSkinnedMeshRenderer;
}

private readonly Dictionary<Mesh, MeshAccessors> _meshToPrims = new Dictionary<Mesh, MeshAccessors>();
private readonly Dictionary<Mesh, BlendShapeAccessors> _meshToBlendShapeAccessors = new Dictionary<Mesh, BlendShapeAccessors>();
private readonly Dictionary<SkinnedMeshRenderer, List<double>> _NodeBlendShapeWeights = new Dictionary<SkinnedMeshRenderer, List<double>>();

public void RegisterPrimitivesWithNode(Node node, List<UniquePrimitive> uniquePrimitives)
{
Expand Down Expand Up @@ -362,6 +364,32 @@ private MeshPrimitive[] ExportPrimitive(UniquePrimitive primKey, GLTFMesh mesh)
return prims;
}

private List<double> GetBlendShapeWeights(SkinnedMeshRenderer smr, Mesh meshObj)
{
if (_NodeBlendShapeWeights.TryGetValue(smr, out var w))
return w;

List<Double> weights = new List<double>(meshObj.blendShapeCount);

for (int blendShapeIndex = 0; blendShapeIndex < meshObj.blendShapeCount; blendShapeIndex++)
{
// We need to get the weight from the SkinnedMeshRenderer because this represents the currently
// defined weight by the user to apply to this blend shape. If we instead got the value from
// the unityMesh, it would be a _per frame_ weight, and for a single-frame blend shape, that would
// always be 100. A blend shape might have more than one frame if a user wanted to more tightly
// control how a blend shape will be animated during weight changes (e.g. maybe they want changes
// between 0-50% to be really minor, but between 50-100 to be extreme, hence they'd have two frames
// where the first frame would have a weight of 50 (meaning any weight between 0-50 should be relative
// to the values in this frame) and then any weight between 50-100 would be relevant to the weights in
// the second frame. See Post 20 for more info:
// https://forum.unity3d.com/threads/is-there-some-method-to-add-blendshape-in-editor.298002/#post-2015679
var frameWeight = meshObj.GetBlendShapeFrameWeight(blendShapeIndex, 0);
weights.Add(smr.GetBlendShapeWeight(blendShapeIndex) / frameWeight);
}

return weights;
}

// Blend Shapes / Morph Targets
// Adopted from Gary Hsu (bghgary)
// https://github.com/bghgary/glTF-Tools-for-Unity/blob/master/UnityProject/Assets/Gltf/Editor/Exporter.cs
Expand All @@ -381,7 +409,7 @@ private void ExportBlendShapes(SkinnedMeshRenderer smr, Mesh meshObj, int submes
if (smr != null && meshObj.blendShapeCount > 0)
{
List<Dictionary<string, AccessorId>> targets = new List<Dictionary<string, AccessorId>>(meshObj.blendShapeCount);
List<Double> weights = new List<double>(meshObj.blendShapeCount);
List<Double> weights;
List<string> targetNames = new List<string>(meshObj.blendShapeCount);

#if UNITY_2019_3_OR_NEWER
Expand Down Expand Up @@ -455,30 +483,19 @@ private void ExportBlendShapes(SkinnedMeshRenderer smr, Mesh meshObj, int submes
exportTargets.Add(SemanticProperties.TANGENT, ExportSparseAccessor(null, null, SchemaExtensions.ConvertVector3CoordinateSpaceAndCopy(deltaTangents, SchemaExtensions.CoordinateSpaceConversionScale)));
}
}
targets.Add(exportTargets);

// We need to get the weight from the SkinnedMeshRenderer because this represents the currently
// defined weight by the user to apply to this blend shape. If we instead got the value from
// the unityMesh, it would be a _per frame_ weight, and for a single-frame blend shape, that would
// always be 100. A blend shape might have more than one frame if a user wanted to more tightly
// control how a blend shape will be animated during weight changes (e.g. maybe they want changes
// between 0-50% to be really minor, but between 50-100 to be extreme, hence they'd have two frames
// where the first frame would have a weight of 50 (meaning any weight between 0-50 should be relative
// to the values in this frame) and then any weight between 50-100 would be relevant to the weights in
// the second frame. See Post 20 for more info:
// https://forum.unity3d.com/threads/is-there-some-method-to-add-blendshape-in-editor.298002/#post-2015679
var frameWeight = meshObj.GetBlendShapeFrameWeight(blendShapeIndex, 0);
if(exportTargets.Any())
weights.Add(smr.GetBlendShapeWeight(blendShapeIndex) / frameWeight);

targets.Add(exportTargets);

exportBlendShapeMarker.End();
}

weights = GetBlendShapeWeights(smr, meshObj);
if(weights.Any() && targets.Any())
{
mesh.Weights = weights;
mesh.TargetNames = targetNames;
primitive.Targets = targets;
_NodeBlendShapeWeights.Add(smr, weights);
}
else
{
Expand All @@ -492,7 +509,8 @@ private void ExportBlendShapes(SkinnedMeshRenderer smr, Mesh meshObj, int submes
{
targets = targets,
weights = weights,
targetNames = targetNames
targetNames = targetNames,
firstSkinnedMeshRenderer = smr
});
}
}
Expand Down
10 changes: 10 additions & 0 deletions Runtime/Scripts/SceneExporter/ExporterSkinning.cs
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,12 @@ private void ExportSkinFromNode(Transform transform)
GLTF.Schema.GLTFMesh gltfMesh = _root.Meshes[val.Id];
if(gltfMesh != null)
{
var accessors = _meshToPrims[mesh];
if (accessors.aJoints0 != null)
sharedBones = accessors.aJoints0;
if (accessors.aWeights0 != null)
sharedWeights = accessors.aWeights0;

foreach (MeshPrimitive prim in gltfMesh.Primitives)
{
if (!prim.Attributes.ContainsKey("JOINTS_0"))
Expand All @@ -101,6 +107,8 @@ private void ExportSkinFromNode(Transform transform)
jointsAccessor.Value.BufferView.Value.Target = BufferViewTarget.ArrayBuffer;
prim.Attributes.Add("JOINTS_0", jointsAccessor);
sharedBones = jointsAccessor;
accessors.aJoints0 = jointsAccessor;
_meshToPrims[mesh] = accessors;
}
}

Expand All @@ -114,6 +122,8 @@ private void ExportSkinFromNode(Transform transform)
weightsAccessor.Value.BufferView.Value.Target = BufferViewTarget.ArrayBuffer;
prim.Attributes.Add("WEIGHTS_0", weightsAccessor);
sharedWeights = weightsAccessor;
accessors.aWeights0 = weightsAccessor;
_meshToPrims[mesh] = accessors;
}
}
}
Expand Down

0 comments on commit d0cfc9d

Please sign in to comment.