Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fixed shared SkinnedMesh on export #729

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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