From afb836d5b167667194157183409d0252b41d36b8 Mon Sep 17 00:00:00 2001 From: weibo Date: Sun, 16 Feb 2020 13:32:49 +0800 Subject: [PATCH 01/10] Add exporting Unity animation clips and skeleton to GLTF. We can export GLTF file with animation clips and skeleton throght "GLTF -> Export ***" menu now. --- .../GLTFSerialization/Schema/GLTFId.cs | 25 + .../Scripts/Extensions/SchemaExtensions.cs | 66 +- .../Runtime/Scripts/GLTFSceneExporter.cs | 702 +++++++++++++++++- 3 files changed, 757 insertions(+), 36 deletions(-) diff --git a/GLTFSerialization/GLTFSerialization/Schema/GLTFId.cs b/GLTFSerialization/GLTFSerialization/Schema/GLTFId.cs index 6b9422b5d..9a5b5ee0a 100644 --- a/GLTFSerialization/GLTFSerialization/Schema/GLTFId.cs +++ b/GLTFSerialization/GLTFSerialization/Schema/GLTFId.cs @@ -209,6 +209,31 @@ public static MeshId Deserialize(GLTFRoot root, JsonReader reader) } } + public class AnimationId : GLTFId + { + public AnimationId() + { + } + + public AnimationId(AnimationId id, GLTFRoot newRoot) : base(id, newRoot) + { + } + + public override GLTFAnimation Value + { + get { return Root.Animations[Id]; } + } + + public static AnimationId Deserialize(GLTFRoot root, JsonReader reader) + { + return new AnimationId + { + Id = reader.ReadAsInt32().Value, + Root = root + }; + } + } + public class NodeId : GLTFId { public NodeId() diff --git a/UnityGLTF/Assets/UnityGLTF/Runtime/Scripts/Extensions/SchemaExtensions.cs b/UnityGLTF/Assets/UnityGLTF/Runtime/Scripts/Extensions/SchemaExtensions.cs index c0d10aa1e..55d148517 100644 --- a/UnityGLTF/Assets/UnityGLTF/Runtime/Scripts/Extensions/SchemaExtensions.cs +++ b/UnityGLTF/Assets/UnityGLTF/Runtime/Scripts/Extensions/SchemaExtensions.cs @@ -417,7 +417,7 @@ public static void ConvertVector3CoordinateSpace(ref AttributeAccessor attribute } /// - /// Converts and copies based on the specified coordinate scale + /// Converts and copies based on the specified coordinate space /// /// The array to convert and copy /// The specified coordinate space @@ -453,7 +453,7 @@ public static void ConvertVector4CoordinateSpace(ref AttributeAccessor attribute } /// - /// Converts and copies based on the specified coordinate scale + /// Converts and copies based on the specified coordinate space /// /// The array to convert and copy /// The specified coordinate space @@ -472,6 +472,68 @@ public static Vector4[] ConvertVector4CoordinateSpaceAndCopy(Vector4[] array, GL return returnArray; } + + /// + /// Converts and copies based on the specified coordinate space + /// + /// The array to convert and copy + /// The copied and converted Quaternion array + public static Quaternion[] ConvertQuaternionCoordinateSpaceAndCopy(Quaternion[] array) + { + var returnArray = new Quaternion[array.Length]; + + for (var i = 0; i < array.Length; i++) + { + var unityQuat = array[i]; + Vector3 fromAxisOfRotation = new Vector3(unityQuat.x, unityQuat.y, unityQuat.z); + float axisFlipScale = CoordinateSpaceConversionRequiresHandednessFlip ? -1.0f : 1.0f; + Vector3 toAxisOfRotation = axisFlipScale * Vector3.Scale(fromAxisOfRotation, CoordinateSpaceConversionScale.ToUnityVector3Raw()); + + returnArray[i] = new Quaternion(toAxisOfRotation.x, toAxisOfRotation.y, toAxisOfRotation.z, unityQuat.w); + } + + return returnArray; + } + + /// + /// Extract the joint values + /// + /// The array to extract from and copy + /// Copied Vector4 with joints + public static Vector4[] ExtractJointAndCopy(BoneWeight[] array) + { + var returnArray = new Vector4[array.Length]; + + for (var i = 0; i < array.Length; i++) + { + returnArray[i].x = array[i].boneIndex0; + returnArray[i].y = array[i].boneIndex1; + returnArray[i].z = array[i].boneIndex2; + returnArray[i].w = array[i].boneIndex3; + } + + return returnArray; + } + + /// + /// Extract the weight values + /// + /// The array to extract from and copy + /// Copied Vector4 with weights + public static Vector4[] ExtractWeightAndCopy(BoneWeight[] array) + { + var returnArray = new Vector4[array.Length]; + + for (var i = 0; i < array.Length; i++) + { + returnArray[i].x = array[i].weight0; + returnArray[i].y = array[i].weight1; + returnArray[i].z = array[i].weight2; + returnArray[i].w = array[i].weight3; + } + + return returnArray; + } /// /// Rewinds the indicies into Unity coordinate space from glTF space diff --git a/UnityGLTF/Assets/UnityGLTF/Runtime/Scripts/GLTFSceneExporter.cs b/UnityGLTF/Assets/UnityGLTF/Runtime/Scripts/GLTFSceneExporter.cs index a63448de5..4575ac5fe 100644 --- a/UnityGLTF/Assets/UnityGLTF/Runtime/Scripts/GLTFSceneExporter.cs +++ b/UnityGLTF/Assets/UnityGLTF/Runtime/Scripts/GLTFSceneExporter.cs @@ -3,6 +3,7 @@ using System.IO; using System.Text; using GLTF.Schema; +using UnityEditor; using UnityEngine; using UnityEngine.Rendering; using UnityGLTF.Extensions; @@ -48,6 +49,38 @@ private struct ImageInfo public Texture2D texture; public TextureMapType textureMapType; } + + private class PropertyCurveBindings + { + public string name; + public List curveBindings; + + public PropertyCurveBindings(string name, EditorCurveBinding curveBinding) + { + this.name = name; + this.curveBindings = new List + { + curveBinding + }; + } + } + + private class CurveBindingGroup + { + public string path; + public Type type; + public List properties; + + public CurveBindingGroup(string path, Type type, PropertyCurveBindings property) + { + this.path = path; + this.type = type; + this.properties = new List + { + property + }; + } + } private Transform[] _rootTransforms; private GLTFRoot _root; @@ -79,6 +112,10 @@ protected struct PrimKey private readonly Dictionary _primOwner = new Dictionary(); private readonly Dictionary _meshToPrims = new Dictionary(); + private readonly Dictionary _existedNodes = new Dictionary(); + private readonly Dictionary _existedAnimation = new Dictionary(); + + // Settings public static bool ExportNames = true; public static bool ExportFullPath = true; @@ -125,7 +162,9 @@ public GLTFSceneExporter(Transform[] rootTransforms, ExportOptions options) Nodes = new List(), Samplers = new List(), Scenes = new List(), - Textures = new List() + Textures = new List(), + Skins = new List(), + Animations = new List() }; _imageInfos = new List(); @@ -492,13 +531,37 @@ private NodeId ExportNode(Transform nodeTransform) }; _root.Nodes.Add(node); + _existedNodes.Add(nodeTransform.GetInstanceID(), id); + // children that are primitives get put in a mesh - GameObject[] primitives, nonPrimitives; - FilterPrimitives(nodeTransform, out primitives, out nonPrimitives); - if (primitives.Length > 0) + GameObject[] meshPrimitives, skinnedMeshPrimitives, nonPrimitives; + FilterPrimitives(nodeTransform, out meshPrimitives, out skinnedMeshPrimitives, out nonPrimitives); + + // children that are not primitives get added as child nodes + if (nonPrimitives.Length > 0) { - node.Mesh = ExportMesh(nodeTransform.name, primitives); + node.Children = new List(nonPrimitives.Length); + foreach (var child in nonPrimitives) + { + node.Children.Add(ExportNode(child.transform)); + } + } + + if (meshPrimitives.Length + skinnedMeshPrimitives.Length > 0) + { + if (skinnedMeshPrimitives.Length > 0) + { + ExportSkin(nodeTransform.name, skinnedMeshPrimitives, node); + } + + if (meshPrimitives.Length > 0) + { + node.Mesh = ExportMesh(nodeTransform.name, meshPrimitives); + } + var primitives = new List(); + primitives.AddRange(meshPrimitives); + primitives.AddRange(skinnedMeshPrimitives); // associate unity meshes with gltf mesh id foreach (var prim in primitives) { @@ -516,13 +579,16 @@ private NodeId ExportNode(Transform nodeTransform) } } - // children that are not primitives get added as child nodes - if (nonPrimitives.Length > 0) + var animator = nodeTransform.gameObject.GetComponent(); + if (animator != null) { - node.Children = new List(nonPrimitives.Length); - foreach (var child in nonPrimitives) - { - node.Children.Add(ExportNode(child.transform)); + if (animator.runtimeAnimatorController != null) + { + var animationClips = animator.runtimeAnimatorController.animationClips; + if (animationClips != null && animationClips.Length != 0) + { + ExportAnimations(animationClips, nodeTransform); + } } } @@ -597,30 +663,51 @@ private static bool ContainsValidRenderer (GameObject gameObject) || (gameObject.GetComponent() != null); } - private void FilterPrimitives(Transform transform, out GameObject[] primitives, out GameObject[] nonPrimitives) + private void FilterPrimitives(Transform transform, out GameObject[] meshPrimitives, out GameObject[] skinnedMeshPrimitives, out GameObject[] nonPrimitives) { var childCount = transform.childCount; - var prims = new List(childCount + 1); + var meshPrims = new List(childCount + 1); + var skinnedMeshPrims = new List(childCount + 1); var nonPrims = new List(childCount); // add another primitive if the root object also has a mesh - if (transform.gameObject.activeSelf) + var gameObject = transform.gameObject; + if (gameObject.activeSelf) { - if (ContainsValidRenderer(transform.gameObject)) + if (ContainsValidRenderer(gameObject)) { - prims.Add(transform.gameObject); + if(gameObject.GetComponent() != null) + { + skinnedMeshPrims.Add(gameObject); + } + else + { + meshPrims.Add(gameObject); + } } } for (var i = 0; i < childCount; i++) { var go = transform.GetChild(i).gameObject; if (IsPrimitive(go)) - prims.Add(go); + { + if(go.GetComponent() != null) + { + skinnedMeshPrims.Add(go); + } + else + { + meshPrims.Add(go); + } + } else + { nonPrims.Add(go); + } } - primitives = prims.ToArray(); + meshPrimitives = meshPrims.ToArray(); + skinnedMeshPrimitives = skinnedMeshPrims.ToArray(); nonPrimitives = nonPrims.ToArray(); } @@ -637,10 +724,97 @@ private static bool IsPrimitive(GameObject gameObject) && gameObject.transform.localRotation == Quaternion.identity && gameObject.transform.localScale == Vector3.one && ContainsValidRenderer(gameObject); + } + + private void ExportSkin(string name, GameObject[] primitives, Node node) + { + foreach (var prim in primitives) + { + var smr = prim.GetComponent(); + + var skin = new Skin + { + Joints = new List() + }; + + var baseId = _root.Nodes.Count; + + foreach(var bone in smr.bones) + { + var translation = bone.localPosition; + var rotation = bone.localRotation; + var scale = bone.localScale; + + NodeId nodeId = null; + if(!_existedNodes.TryGetValue(bone.GetInstanceID(), out nodeId)) + { + var boneNode = new Node + { + Name = bone.gameObject.name, + Translation = new GLTF.Math.Vector3(translation.x, translation.y, translation.z), + Rotation = new GLTF.Math.Quaternion(rotation.x, rotation.y, rotation.z, rotation.w), + Scale = new GLTF.Math.Vector3(scale.x, scale.y, scale.z), + }; + + if (bone.childCount > 0) + { + boneNode.Children = new List(); + for (var i = 0; i < bone.childCount; ++i) + { + var childIndex = Array.IndexOf(smr.bones, bone.GetChild(i)); + if (-1 == childIndex) + { + continue; + } + boneNode.Children.Add( + new NodeId + { + Id = childIndex + baseId, + Root = _root + } + ); + } + } + + nodeId = new NodeId + { + Id = _root.Nodes.Count, + Root = _root + }; + + _root.Nodes.Add(boneNode); + } + + skin.Joints.Add(nodeId); + } + NodeId rootBoneId = null; + if (!_existedNodes.TryGetValue(smr.rootBone.GetInstanceID(), out rootBoneId)) + { + rootBoneId = new NodeId + { + Id = baseId, + Root = _root + }; + } + + skin.Skeleton = rootBoneId; + + var skinId = new SkinId + { + Id = _root.Skins.Count, + Root = _root + }; + + _root.Skins.Add(skin); + + node.Skin = skinId; + + node.Mesh = ExportMesh(prim.name, new GameObject[] { prim }, skin); + } } - private MeshId ExportMesh(string name, GameObject[] primitives) + private MeshId ExportMesh(string name, GameObject[] primitives, Skin skin = null) { // check if this set of primitives is already a mesh MeshId existingMeshId = null; @@ -690,7 +864,7 @@ private MeshId ExportMesh(string name, GameObject[] primitives) mesh.Primitives = new List(primitives.Length); foreach (var prim in primitives) { - MeshPrimitive[] meshPrimitives = ExportPrimitive(prim, mesh); + MeshPrimitive[] meshPrimitives = ExportPrimitive(prim, mesh, skin); if (meshPrimitives != null) { mesh.Primitives.AddRange(meshPrimitives); @@ -707,8 +881,255 @@ private MeshId ExportMesh(string name, GameObject[] primitives) return id; } + private void ExportAnimations(AnimationClip[] animationClips, Transform rootNodeTransform) + { + if(null == animationClips) + { + return; + } + + foreach (var animationClip in animationClips) + { + var animInstId = animationClip.GetInstanceID(); + AnimationId existedAnimId; + if (!_existedAnimation.TryGetValue(animInstId, out existedAnimId)) + { + var animId = ExportAnimationClip(animationClip, rootNodeTransform); + _existedAnimation.Add(animInstId, animId); + } + } + } + + private AnimationId ExportAnimationClip(AnimationClip animationClip, Transform rootNodeTransform) + { + var curveBindings = AnimationUtility.GetCurveBindings(animationClip); + + var curveBindingGroups = CurveMarshalling(curveBindings); + + var animation = new GLTFAnimation() + { + Name = animationClip.name, + Channels = new List(), + Samplers = new List() + }; + + var frameCount = Mathf.CeilToInt(animationClip.length * animationClip.frameRate); + var timestamps = new float[frameCount]; + for (var i = 0; i < frameCount; ++i) + { + var timestamp = i / animationClip.frameRate; + timestamps[i] = timestamp; + } + + var timesstampsId = ExportAccessor(timestamps); + + foreach (var curveBindingGroup in curveBindingGroups) + { + var bone = rootNodeTransform.Find(curveBindingGroup.path); + if (bone == null) + { + continue; + } + + NodeId nodeId = null; + if (!_existedNodes.TryGetValue(bone.GetInstanceID(), out nodeId)) + { + continue; + } + + foreach(var property in curveBindingGroup.properties) + { + GLTFAnimationChannelPath path; + var sampler = new AnimationSampler() + { + Input = timesstampsId, + Interpolation = InterpolationType.LINEAR, + Output = ExportCurve(animationClip, property.name, property.curveBindings, frameCount, out path) + }; + + var samplerId = new SamplerId() + { + Id = animation.Samplers.Count, + Root = _root + }; + + animation.Samplers.Add(sampler); + + var channel = new AnimationChannel() + { + Sampler = samplerId, + Target = new AnimationChannelTarget() + { + Node = nodeId, + Path = path + } + }; + + animation.Channels.Add(channel); + } + } + + var id = new AnimationId + { + Id = _root.Animations.Count, + Root = _root + }; + + _root.Animations.Add(animation); + + return id; + } + + private AccessorId ExportCurve(AnimationClip animationClip, string propertyName, List curveBindings, int frameCount, out GLTFAnimationChannelPath path) + { + switch (propertyName) + { + case "m_LocalPosition": + { + var data = new Vector3[frameCount]; + for (var i = 0; i < frameCount; ++i) + { + var time = i / animationClip.frameRate; + + var curveIndex = 0; + var value = new float[3]; + foreach (var curveBinding in curveBindings) + { + var curve = AnimationUtility.GetEditorCurve(animationClip, curveBinding); + if (curve != null) value[curveIndex++] = curve.Evaluate(time); + } + + data[i] = new Vector3(value[0], value[1], value[2]); + } + + path = GLTFAnimationChannelPath.translation; + return ExportAccessor(SchemaExtensions.ConvertVector3CoordinateSpaceAndCopy(data, SchemaExtensions.CoordinateSpaceConversionScale)); + } + case "m_LocalRotation": + { + var data = new Quaternion[frameCount]; + for (var i = 0; i < frameCount; ++i) + { + var time = i / animationClip.frameRate; + + var curveIndex = 0; + var value = new float[4]; + foreach (var curveBinding in curveBindings) + { + var curve = AnimationUtility.GetEditorCurve(animationClip, curveBinding); + if (curve != null) value[curveIndex++] = curve.Evaluate(time); + } + + data[i] = new Quaternion(value[0], value[1], value[2], value[3]); + } + + path = GLTFAnimationChannelPath.rotation; + return ExportAccessor(SchemaExtensions.ConvertQuaternionCoordinateSpaceAndCopy(data)); + } + case "m_LocalScale": + { + var data = new Vector3[frameCount]; + for (var i = 0; i < frameCount; ++i) + { + var time = i / animationClip.frameRate; + + var curveIndex = 0; + var value = new float[3]; + foreach (var curveBinding in curveBindings) + { + var curve = AnimationUtility.GetEditorCurve(animationClip, curveBinding); + if (curve != null) value[curveIndex++] = curve.Evaluate(time); + } + + data[i] = new Vector3(value[0], value[1], value[2]); + } + + path = GLTFAnimationChannelPath.scale; + return ExportAccessor(data); + } + case "localEulerAnglesRaw": + { + throw new Exception("Parsing of localEulerAnglesRaw is not supported."); + } + } + + throw new Exception("Unrecognized property name."); + } + + private List CurveMarshalling(EditorCurveBinding[] curveBindings) + { + List curveGroups = new List(); + foreach (var curveBinding in curveBindings) + { + bool bFoundGroup = false; + foreach (var curveGroup in curveGroups) + { + if ((curveGroup.path == curveBinding.path) && + (curveGroup.type == curveBinding.type)) + { + bFoundGroup = true; + + bool bFoundCurveBindingSlot = false; + foreach (var property in curveGroup.properties) + { + if(property.name == Path.GetFileNameWithoutExtension(curveBinding.propertyName)) + { + property.curveBindings.Add(curveBinding); + bFoundCurveBindingSlot = true; + } + } + + if(!bFoundCurveBindingSlot) + { + curveGroup.properties.Add(new PropertyCurveBindings(Path.GetFileNameWithoutExtension(curveBinding.propertyName), curveBinding)); + } + } + } + + if (!bFoundGroup) + { + var property = new PropertyCurveBindings(Path.GetFileNameWithoutExtension(curveBinding.propertyName), curveBinding); + curveGroups.Add(new CurveBindingGroup(curveBinding.path, curveBinding.type, property)); + } + } + + foreach (var curveGroup in curveGroups) + { + curveGroup.properties.Sort((l, r) => + { + Func getPriority = (string propertyName) => + { + int priority = -1; + if (propertyName == "m_LocalPosition") + { + priority = 0; + } + else if (propertyName == "m_LocalRotation") + { + priority = 1; + } + else if (propertyName == "localEulerAnglesRaw") + { + priority = 2; + } + else if (propertyName == "m_LocalScale") + { + priority = 3; + } + return priority; + }; + + var lp = getPriority(l.name); + var rp = getPriority(r.name); + return lp < rp ? -1 : 1; + }); + } + + return curveGroups; + } + // a mesh *might* decode to multiple prims if there are submeshes - private MeshPrimitive[] ExportPrimitive(GameObject gameObject, GLTFMesh mesh) + private MeshPrimitive[] ExportPrimitive(GameObject gameObject, GLTFMesh mesh, Skin skin = null) { Mesh meshObj = null; SkinnedMeshRenderer smr = null; @@ -742,7 +1163,7 @@ private MeshPrimitive[] ExportPrimitive(GameObject gameObject, GLTFMesh mesh) { prims[i] = new MeshPrimitive(primVariations[i], _root) { - Material = ExportMaterial(materialsObj[i]) + Material = ExportMaterial(materialsObj[i], renderer) }; } @@ -750,8 +1171,9 @@ private MeshPrimitive[] ExportPrimitive(GameObject gameObject, GLTFMesh mesh) } AccessorId aPosition = null, aNormal = null, aTangent = null, - aTexcoord0 = null, aTexcoord1 = null, aColor0 = null; - + aTexcoord0 = null, aTexcoord1 = null, aColor0 = null, + aJoint0 = null, aWeight0 = null; + aPosition = ExportAccessor(SchemaExtensions.ConvertVector3CoordinateSpaceAndCopy(meshObj.vertices, SchemaExtensions.CoordinateSpaceConversionScale)); if (meshObj.normals.Length != 0) @@ -769,6 +1191,18 @@ private MeshPrimitive[] ExportPrimitive(GameObject gameObject, GLTFMesh mesh) if (meshObj.colors.Length != 0) aColor0 = ExportAccessor(meshObj.colors); + if(meshObj.boneWeights.Length != 0) + { + aJoint0 = ExportAccessor(SchemaExtensions.ExtractJointAndCopy(meshObj.boneWeights)); + aWeight0 = ExportAccessor(SchemaExtensions.ExtractWeightAndCopy(meshObj.boneWeights)); + } + + if(skin != null) + { + if (meshObj.bindposes.Length != 0) + skin.InverseBindMatrices = ExportAccessor(meshObj.bindposes); + } + MaterialId lastMaterialId = null; for (var submesh = 0; submesh < meshObj.subMeshCount; submesh++) @@ -795,10 +1229,14 @@ private MeshPrimitive[] ExportPrimitive(GameObject gameObject, GLTFMesh mesh) primitive.Attributes.Add(SemanticProperties.TEXCOORD_1, aTexcoord1); if (aColor0 != null) primitive.Attributes.Add(SemanticProperties.COLOR_0, aColor0); + if (aJoint0 != null) + primitive.Attributes.Add(SemanticProperties.JOINTS_0, aJoint0); + if (aWeight0 != null) + primitive.Attributes.Add(SemanticProperties.WEIGHTS_0, aWeight0); if (submesh < materialsObj.Length) { - primitive.Material = ExportMaterial(materialsObj[submesh]); + primitive.Material = ExportMaterial(materialsObj[submesh], renderer); lastMaterialId = primitive.Material; } else @@ -816,7 +1254,7 @@ private MeshPrimitive[] ExportPrimitive(GameObject gameObject, GLTFMesh mesh) return prims; } - private MaterialId ExportMaterial(Material materialObj) + private MaterialId ExportMaterial(Material materialObj, MeshRenderer renderer) { MaterialId id = GetMaterialId(_root, materialObj); if (id != null) @@ -1232,13 +1670,13 @@ private TextureId ExportTexture(Texture textureObj, TextureMapType textureMapTyp } if (_shouldUseInternalBufferForImages) - { + { texture.Source = ExportImageInternalBuffer(textureObj, textureMapType); - } - else - { + } + else + { texture.Source = ExportImage(textureObj, textureMapType); - } + } texture.Sampler = ExportSampler(textureObj); _textures.Add(textureObj); @@ -1301,13 +1739,12 @@ private ImageId ExportImage(Texture texture, TextureMapType texturMapType) private ImageId ExportImageInternalBuffer(UnityEngine.Texture texture, TextureMapType texturMapType) { - if (texture == null) { - throw new Exception("texture can not be NULL."); + throw new Exception("texture can not be NULL."); } - var image = new GLTFImage(); + var image = new GLTFImage(); image.MimeType = "image/png"; var byteOffset = _bufferWriter.BaseStream.Position; @@ -1425,6 +1862,63 @@ private SamplerId ExportSampler(Texture texture) return samplerId; } + private AccessorId ExportAccessor(float[] arr) + { + uint count = (uint)arr.Length; + + if (count == 0) + { + throw new Exception("Accessors can not have a count of 0."); + } + + var accessor = new Accessor(); + accessor.Count = count; + accessor.Type = GLTFAccessorAttributeType.SCALAR; + + var min = arr[0]; + var max = arr[0]; + + for (var i = 1; i < count; i++) + { + var cur = arr[i]; + + if (cur < min) + { + min = cur; + } + if (cur > max) + { + max = cur; + } + } + + AlignToBoundary(_bufferWriter.BaseStream, 0x00); + uint byteOffset = CalculateAlignment((uint)_bufferWriter.BaseStream.Position, 4); + + accessor.ComponentType = GLTFComponentType.Float; + + foreach (var v in arr) + { + _bufferWriter.Write(v); + } + + accessor.Min = new List { min }; + accessor.Max = new List { max }; + + uint byteLength = CalculateAlignment((uint)_bufferWriter.BaseStream.Position - byteOffset, 4); + + accessor.BufferView = ExportBufferView(byteOffset, byteLength); + + var id = new AccessorId + { + Id = _root.Accessors.Count, + Root = _root + }; + _root.Accessors.Add(accessor); + + return id; + } + private AccessorId ExportAccessor(int[] arr, bool isIndices = false) { uint count = (uint)arr.Length; @@ -1778,6 +2272,95 @@ private AccessorId ExportAccessor(Vector4[] arr) return id; } + + private AccessorId ExportAccessor(Quaternion[] arr) + { + uint count = (uint)arr.Length; + + if (count == 0) + { + throw new Exception("Accessors can not have a count of 0."); + } + + var accessor = new Accessor(); + accessor.ComponentType = GLTFComponentType.Float; + accessor.Count = count; + accessor.Type = GLTFAccessorAttributeType.VEC4; + + float minX = arr[0].x; + float minY = arr[0].y; + float minZ = arr[0].z; + float minW = arr[0].w; + float maxX = arr[0].x; + float maxY = arr[0].y; + float maxZ = arr[0].z; + float maxW = arr[0].w; + + for (var i = 1; i < count; i++) + { + var cur = arr[i]; + + if (cur.x < minX) + { + minX = cur.x; + } + if (cur.y < minY) + { + minY = cur.y; + } + if (cur.z < minZ) + { + minZ = cur.z; + } + if (cur.w < minW) + { + minW = cur.w; + } + if (cur.x > maxX) + { + maxX = cur.x; + } + if (cur.y > maxY) + { + maxY = cur.y; + } + if (cur.z > maxZ) + { + maxZ = cur.z; + } + if (cur.w > maxW) + { + maxW = cur.w; + } + } + + accessor.Min = new List { minX, minY, minZ, minW }; + accessor.Max = new List { maxX, maxY, maxZ, maxW }; + + AlignToBoundary(_bufferWriter.BaseStream, 0x00); + uint byteOffset = CalculateAlignment((uint)_bufferWriter.BaseStream.Position, 4); + + foreach (var vec in arr) + { + _bufferWriter.Write(vec.x); + _bufferWriter.Write(vec.y); + _bufferWriter.Write(vec.z); + _bufferWriter.Write(vec.w); + } + + uint byteLength = CalculateAlignment((uint)_bufferWriter.BaseStream.Position - byteOffset, 4); + + accessor.BufferView = ExportBufferView(byteOffset, byteLength); + + var id = new AccessorId + { + Id = _root.Accessors.Count, + Root = _root + }; + _root.Accessors.Add(accessor); + + return id; + } private AccessorId ExportAccessor(Color[] arr) { @@ -1868,6 +2451,57 @@ private AccessorId ExportAccessor(Color[] arr) return id; } + private AccessorId ExportAccessor(Matrix4x4[] arr) + { + var count = (uint)arr.Length; + + if (count == 0) + { + throw new Exception("Accessors can not have a count of 0."); + } + + var accessor = new Accessor(); + accessor.ComponentType = GLTFComponentType.Float; + accessor.Count = count; + accessor.Type = GLTFAccessorAttributeType.MAT4; + + AlignToBoundary(_bufferWriter.BaseStream, 0x00); + uint byteOffset = CalculateAlignment((uint)_bufferWriter.BaseStream.Position, 4); + + foreach (var mat in arr) + { + _bufferWriter.Write(mat.m00); + _bufferWriter.Write(mat.m10); + _bufferWriter.Write(mat.m20); + _bufferWriter.Write(mat.m30); + _bufferWriter.Write(mat.m01); + _bufferWriter.Write(mat.m11); + _bufferWriter.Write(mat.m21); + _bufferWriter.Write(mat.m31); + _bufferWriter.Write(mat.m02); + _bufferWriter.Write(mat.m12); + _bufferWriter.Write(mat.m22); + _bufferWriter.Write(mat.m32); + _bufferWriter.Write(mat.m03); + _bufferWriter.Write(mat.m13); + _bufferWriter.Write(mat.m23); + _bufferWriter.Write(mat.m33); + } + + uint byteLength = CalculateAlignment((uint)_bufferWriter.BaseStream.Position - byteOffset, 4); + + accessor.BufferView = ExportBufferView(byteOffset, byteLength); + + var id = new AccessorId + { + Id = _root.Accessors.Count, + Root = _root + }; + _root.Accessors.Add(accessor); + + return id; + } + private BufferViewId ExportBufferView(uint byteOffset, uint byteLength) { var bufferView = new BufferView From 9c4d8b5a241097fa772d308ce5107296bed81e96 Mon Sep 17 00:00:00 2001 From: weibo Date: Sun, 16 Feb 2020 14:31:31 +0800 Subject: [PATCH 02/10] Fix bug. --- .../UnityGLTF/Runtime/Scripts/GLTFSceneExporter.cs | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/UnityGLTF/Assets/UnityGLTF/Runtime/Scripts/GLTFSceneExporter.cs b/UnityGLTF/Assets/UnityGLTF/Runtime/Scripts/GLTFSceneExporter.cs index 4575ac5fe..7e851ea47 100644 --- a/UnityGLTF/Assets/UnityGLTF/Runtime/Scripts/GLTFSceneExporter.cs +++ b/UnityGLTF/Assets/UnityGLTF/Runtime/Scripts/GLTFSceneExporter.cs @@ -940,11 +940,16 @@ private AnimationId ExportAnimationClip(AnimationClip animationClip, Transform r foreach(var property in curveBindingGroup.properties) { GLTFAnimationChannelPath path; + AccessorId accessorId = ExportCurve(animationClip, property.name, property.curveBindings, frameCount, out path); + if(accessorId == null) + { + continue; + } var sampler = new AnimationSampler() { Input = timesstampsId, Interpolation = InterpolationType.LINEAR, - Output = ExportCurve(animationClip, property.name, property.curveBindings, frameCount, out path) + Output = accessorId }; var samplerId = new SamplerId() @@ -1052,8 +1057,10 @@ private AccessorId ExportCurve(AnimationClip animationClip, string propertyName, throw new Exception("Parsing of localEulerAnglesRaw is not supported."); } } - - throw new Exception("Unrecognized property name."); + + Debug.LogError("Unrecognized property name: " + propertyName); + path = GLTFAnimationChannelPath.translation; + return null; } private List CurveMarshalling(EditorCurveBinding[] curveBindings) From fe1e39eb5de61205ac29b32f494b45ed2dd1fbf8 Mon Sep 17 00:00:00 2001 From: weibo Date: Sun, 16 Feb 2020 18:32:59 +0800 Subject: [PATCH 03/10] Convert Unity matrix to GLTF coordinate space. --- .../Scripts/Extensions/SchemaExtensions.cs | 21 ++++++++++++++++++- .../Runtime/Scripts/GLTFSceneExporter.cs | 2 +- 2 files changed, 21 insertions(+), 2 deletions(-) diff --git a/UnityGLTF/Assets/UnityGLTF/Runtime/Scripts/Extensions/SchemaExtensions.cs b/UnityGLTF/Assets/UnityGLTF/Runtime/Scripts/Extensions/SchemaExtensions.cs index 55d148517..7a2ab28e9 100644 --- a/UnityGLTF/Assets/UnityGLTF/Runtime/Scripts/Extensions/SchemaExtensions.cs +++ b/UnityGLTF/Assets/UnityGLTF/Runtime/Scripts/Extensions/SchemaExtensions.cs @@ -494,7 +494,26 @@ public static Quaternion[] ConvertQuaternionCoordinateSpaceAndCopy(Quaternion[] return returnArray; } - + + /// + /// Converts and copies based on the specified coordinate space + /// + /// The array to convert and copy + /// The copied and converted Matrix4x4 array + public static Matrix4x4[] ConvertMatrix4x4CoordinateSpaceAndCopy(Matrix4x4[] array) + { + var returnArr = new Matrix4x4[array.Length]; + + for (int i = 0; i < array.Length; ++i) + { + Vector3 coordinateSpaceConversionScale = CoordinateSpaceConversionScale.ToUnityVector3Raw(); + Matrix4x4 convert = Matrix4x4.Scale(coordinateSpaceConversionScale); + returnArr[i] = convert * array[i] * convert; + } + + return returnArr; + } + /// /// Extract the joint values /// diff --git a/UnityGLTF/Assets/UnityGLTF/Runtime/Scripts/GLTFSceneExporter.cs b/UnityGLTF/Assets/UnityGLTF/Runtime/Scripts/GLTFSceneExporter.cs index 7e851ea47..ef91c1aa1 100644 --- a/UnityGLTF/Assets/UnityGLTF/Runtime/Scripts/GLTFSceneExporter.cs +++ b/UnityGLTF/Assets/UnityGLTF/Runtime/Scripts/GLTFSceneExporter.cs @@ -1207,7 +1207,7 @@ private MeshPrimitive[] ExportPrimitive(GameObject gameObject, GLTFMesh mesh, Sk if(skin != null) { if (meshObj.bindposes.Length != 0) - skin.InverseBindMatrices = ExportAccessor(meshObj.bindposes); + skin.InverseBindMatrices = ExportAccessor(SchemaExtensions.ConvertMatrix4x4CoordinateSpaceAndCopy(meshObj.bindposes)); } MaterialId lastMaterialId = null; From 9b5a7901c259c1ebf632feb24e1dbb02d2736eab Mon Sep 17 00:00:00 2001 From: William Date: Sun, 16 Feb 2020 19:32:20 +0800 Subject: [PATCH 04/10] Export Unity uv3 and uv4 to GLTF. --- .../UnityGLTF/Runtime/Scripts/GLTFSceneExporter.cs | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/UnityGLTF/Assets/UnityGLTF/Runtime/Scripts/GLTFSceneExporter.cs b/UnityGLTF/Assets/UnityGLTF/Runtime/Scripts/GLTFSceneExporter.cs index ef91c1aa1..49004f5f7 100644 --- a/UnityGLTF/Assets/UnityGLTF/Runtime/Scripts/GLTFSceneExporter.cs +++ b/UnityGLTF/Assets/UnityGLTF/Runtime/Scripts/GLTFSceneExporter.cs @@ -1178,8 +1178,8 @@ private MeshPrimitive[] ExportPrimitive(GameObject gameObject, GLTFMesh mesh, Sk } AccessorId aPosition = null, aNormal = null, aTangent = null, - aTexcoord0 = null, aTexcoord1 = null, aColor0 = null, - aJoint0 = null, aWeight0 = null; + aTexcoord0 = null, aTexcoord1 = null, aTexcoord2 = null, aTexcoord3 = null, + aColor0 = null, aJoint0 = null, aWeight0 = null; aPosition = ExportAccessor(SchemaExtensions.ConvertVector3CoordinateSpaceAndCopy(meshObj.vertices, SchemaExtensions.CoordinateSpaceConversionScale)); @@ -1195,6 +1195,12 @@ private MeshPrimitive[] ExportPrimitive(GameObject gameObject, GLTFMesh mesh, Sk if (meshObj.uv2.Length != 0) aTexcoord1 = ExportAccessor(SchemaExtensions.FlipTexCoordArrayVAndCopy(meshObj.uv2)); + if (meshObj.uv3.Length != 0) + aTexcoord2 = ExportAccessor(SchemaExtensions.FlipTexCoordArrayVAndCopy(meshObj.uv3)); + + if (meshObj.uv4.Length != 0) + aTexcoord3 = ExportAccessor(SchemaExtensions.FlipTexCoordArrayVAndCopy(meshObj.uv4)); + if (meshObj.colors.Length != 0) aColor0 = ExportAccessor(meshObj.colors); @@ -1234,6 +1240,10 @@ private MeshPrimitive[] ExportPrimitive(GameObject gameObject, GLTFMesh mesh, Sk primitive.Attributes.Add(SemanticProperties.TEXCOORD_0, aTexcoord0); if (aTexcoord1 != null) primitive.Attributes.Add(SemanticProperties.TEXCOORD_1, aTexcoord1); + if (aTexcoord2 != null) + primitive.Attributes.Add(SemanticProperties.TEXCOORD_2, aTexcoord2); + if (aTexcoord3 != null) + primitive.Attributes.Add(SemanticProperties.TEXCOORD_3, aTexcoord3); if (aColor0 != null) primitive.Attributes.Add(SemanticProperties.COLOR_0, aColor0); if (aJoint0 != null) From dc8335b68489ae29963f2f30edcdfc9649613f90 Mon Sep 17 00:00:00 2001 From: William Date: Thu, 27 Feb 2020 13:06:54 +0800 Subject: [PATCH 05/10] Change joint type from float to int. --- .../Runtime/Scripts/Extensions/SchemaExtensions.cs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/UnityGLTF/Assets/UnityGLTF/Runtime/Scripts/Extensions/SchemaExtensions.cs b/UnityGLTF/Assets/UnityGLTF/Runtime/Scripts/Extensions/SchemaExtensions.cs index 7a2ab28e9..3c7e8b463 100644 --- a/UnityGLTF/Assets/UnityGLTF/Runtime/Scripts/Extensions/SchemaExtensions.cs +++ b/UnityGLTF/Assets/UnityGLTF/Runtime/Scripts/Extensions/SchemaExtensions.cs @@ -519,16 +519,16 @@ public static Matrix4x4[] ConvertMatrix4x4CoordinateSpaceAndCopy(Matrix4x4[] arr /// /// The array to extract from and copy /// Copied Vector4 with joints - public static Vector4[] ExtractJointAndCopy(BoneWeight[] array) + public static int[] ExtractJointAndCopy(BoneWeight[] array) { - var returnArray = new Vector4[array.Length]; + var returnArray = new int[array.Length * 4]; for (var i = 0; i < array.Length; i++) { - returnArray[i].x = array[i].boneIndex0; - returnArray[i].y = array[i].boneIndex1; - returnArray[i].z = array[i].boneIndex2; - returnArray[i].w = array[i].boneIndex3; + returnArray[i * 4] = array[i].boneIndex0; + returnArray[i * 4 + 1] = array[i].boneIndex1; + returnArray[i * 4 + 2] = array[i].boneIndex2; + returnArray[i * 4 + 3] = array[i].boneIndex3; } return returnArray; From 306d3c11ddb5c7fff4ffba7c598d241767321381 Mon Sep 17 00:00:00 2001 From: William Date: Fri, 28 Feb 2020 23:30:18 +0800 Subject: [PATCH 06/10] Fixed a bug that joint index was out of range. --- .../UnityGLTF/Runtime/Scripts/GLTFSceneExporter.cs | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/UnityGLTF/Assets/UnityGLTF/Runtime/Scripts/GLTFSceneExporter.cs b/UnityGLTF/Assets/UnityGLTF/Runtime/Scripts/GLTFSceneExporter.cs index 49004f5f7..62389e182 100644 --- a/UnityGLTF/Assets/UnityGLTF/Runtime/Scripts/GLTFSceneExporter.cs +++ b/UnityGLTF/Assets/UnityGLTF/Runtime/Scripts/GLTFSceneExporter.cs @@ -1206,7 +1206,7 @@ private MeshPrimitive[] ExportPrimitive(GameObject gameObject, GLTFMesh mesh, Sk if(meshObj.boneWeights.Length != 0) { - aJoint0 = ExportAccessor(SchemaExtensions.ExtractJointAndCopy(meshObj.boneWeights)); + aJoint0 = ExportAccessor(SchemaExtensions.ExtractJointAndCopy(meshObj.boneWeights), SemanticProperties.JOINTS_0); aWeight0 = ExportAccessor(SchemaExtensions.ExtractWeightAndCopy(meshObj.boneWeights)); } @@ -1227,7 +1227,7 @@ private MeshPrimitive[] ExportPrimitive(GameObject gameObject, GLTFMesh mesh, Sk if (topology == MeshTopology.Triangles) SchemaExtensions.FlipTriangleFaces(indices); primitive.Mode = GetDrawMode(topology); - primitive.Indices = ExportAccessor(indices, true); + primitive.Indices = ExportAccessor(indices, SemanticProperties.INDICES); primitive.Attributes = new Dictionary(); primitive.Attributes.Add(SemanticProperties.POSITION, aPosition); @@ -1936,7 +1936,7 @@ private AccessorId ExportAccessor(float[] arr) return id; } - private AccessorId ExportAccessor(int[] arr, bool isIndices = false) + private AccessorId ExportAccessor(int[] arr, string property) { uint count = (uint)arr.Length; @@ -1945,9 +1945,12 @@ private AccessorId ExportAccessor(int[] arr, bool isIndices = false) throw new Exception("Accessors can not have a count of 0."); } + bool isJoints = property == SemanticProperties.JOINTS_0; + bool isIndices = property == SemanticProperties.INDICES; + var accessor = new Accessor(); - accessor.Count = count; - accessor.Type = GLTFAccessorAttributeType.SCALAR; + accessor.Count = isJoints ? count / 4 : count; + accessor.Type = isJoints ? GLTFAccessorAttributeType.VEC4 : GLTFAccessorAttributeType.SCALAR; int min = arr[0]; int max = arr[0]; From 3fa66198752bf877db2a475ec561588cd75c2dc4 Mon Sep 17 00:00:00 2001 From: William Date: Sat, 29 Feb 2020 13:19:04 +0800 Subject: [PATCH 07/10] Optimize skeleton structure to reduce redundant data. --- .../Runtime/Scripts/GLTFSceneExporter.cs | 49 ++++++++++++------- 1 file changed, 30 insertions(+), 19 deletions(-) diff --git a/UnityGLTF/Assets/UnityGLTF/Runtime/Scripts/GLTFSceneExporter.cs b/UnityGLTF/Assets/UnityGLTF/Runtime/Scripts/GLTFSceneExporter.cs index 62389e182..81bf33f9d 100644 --- a/UnityGLTF/Assets/UnityGLTF/Runtime/Scripts/GLTFSceneExporter.cs +++ b/UnityGLTF/Assets/UnityGLTF/Runtime/Scripts/GLTFSceneExporter.cs @@ -114,7 +114,7 @@ protected struct PrimKey private readonly Dictionary _existedNodes = new Dictionary(); private readonly Dictionary _existedAnimation = new Dictionary(); - + private readonly Dictionary _existedSkin = new Dictionary(); // Settings public static bool ExportNames = true; @@ -731,22 +731,34 @@ private void ExportSkin(string name, GameObject[] primitives, Node node) foreach (var prim in primitives) { var smr = prim.GetComponent(); - + if (_existedSkin.TryGetValue(smr.rootBone.GetInstanceID(), out SkinId skinId)) + { + node.Skin = skinId; + node.Mesh = ExportMesh(prim.name, new GameObject[] { prim }); + continue; + } + var skin = new Skin { Joints = new List() }; - + + var mesh = smr.sharedMesh; + if (mesh.bindposes.Length != 0) + { + skin.InverseBindMatrices = ExportAccessor(SchemaExtensions.ConvertMatrix4x4CoordinateSpaceAndCopy(mesh.bindposes)); + } + var baseId = _root.Nodes.Count; - - foreach(var bone in smr.bones) + + foreach (var bone in smr.bones) { var translation = bone.localPosition; var rotation = bone.localRotation; var scale = bone.localScale; NodeId nodeId = null; - if(!_existedNodes.TryGetValue(bone.GetInstanceID(), out nodeId)) + if (!_existedNodes.TryGetValue(bone.GetInstanceID(), out nodeId)) { var boneNode = new Node { @@ -800,7 +812,7 @@ private void ExportSkin(string name, GameObject[] primitives, Node node) skin.Skeleton = rootBoneId; - var skinId = new SkinId + skinId = new SkinId { Id = _root.Skins.Count, Root = _root @@ -810,11 +822,13 @@ private void ExportSkin(string name, GameObject[] primitives, Node node) node.Skin = skinId; - node.Mesh = ExportMesh(prim.name, new GameObject[] { prim }, skin); + node.Mesh = ExportMesh(prim.name, new GameObject[] { prim }); + + _existedSkin.Add(smr.rootBone.GetInstanceID(), skinId); } } - private MeshId ExportMesh(string name, GameObject[] primitives, Skin skin = null) + private MeshId ExportMesh(string name, GameObject[] primitives) { // check if this set of primitives is already a mesh MeshId existingMeshId = null; @@ -864,7 +878,7 @@ private MeshId ExportMesh(string name, GameObject[] primitives, Skin skin = null mesh.Primitives = new List(primitives.Length); foreach (var prim in primitives) { - MeshPrimitive[] meshPrimitives = ExportPrimitive(prim, mesh, skin); + MeshPrimitive[] meshPrimitives = ExportPrimitive(prim, mesh); if (meshPrimitives != null) { mesh.Primitives.AddRange(meshPrimitives); @@ -1136,7 +1150,7 @@ private List CurveMarshalling(EditorCurveBinding[] curveBindi } // a mesh *might* decode to multiple prims if there are submeshes - private MeshPrimitive[] ExportPrimitive(GameObject gameObject, GLTFMesh mesh, Skin skin = null) + private MeshPrimitive[] ExportPrimitive(GameObject gameObject, GLTFMesh mesh) { Mesh meshObj = null; SkinnedMeshRenderer smr = null; @@ -1210,12 +1224,6 @@ private MeshPrimitive[] ExportPrimitive(GameObject gameObject, GLTFMesh mesh, Sk aWeight0 = ExportAccessor(SchemaExtensions.ExtractWeightAndCopy(meshObj.boneWeights)); } - if(skin != null) - { - if (meshObj.bindposes.Length != 0) - skin.InverseBindMatrices = ExportAccessor(SchemaExtensions.ConvertMatrix4x4CoordinateSpaceAndCopy(meshObj.bindposes)); - } - MaterialId lastMaterialId = null; for (var submesh = 0; submesh < meshObj.subMeshCount; submesh++) @@ -2027,8 +2035,11 @@ private AccessorId ExportAccessor(int[] arr, string property) } } - accessor.Min = new List { min }; - accessor.Max = new List { max }; + if (accessor.Type == GLTFAccessorAttributeType.SCALAR) + { + accessor.Min = new List { min }; + accessor.Max = new List { max }; + } uint byteLength = CalculateAlignment((uint)_bufferWriter.BaseStream.Position - byteOffset, 4); From 042834980989bcf8a94cdf2a07a8a5ec6659a740 Mon Sep 17 00:00:00 2001 From: William Date: Sat, 29 Feb 2020 13:29:31 +0800 Subject: [PATCH 08/10] Format code. --- .../Runtime/Scripts/GLTFSceneExporter.cs | 28 +++++++++---------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/UnityGLTF/Assets/UnityGLTF/Runtime/Scripts/GLTFSceneExporter.cs b/UnityGLTF/Assets/UnityGLTF/Runtime/Scripts/GLTFSceneExporter.cs index 81bf33f9d..1f6acfb34 100644 --- a/UnityGLTF/Assets/UnityGLTF/Runtime/Scripts/GLTFSceneExporter.cs +++ b/UnityGLTF/Assets/UnityGLTF/Runtime/Scripts/GLTFSceneExporter.cs @@ -676,7 +676,7 @@ private void FilterPrimitives(Transform transform, out GameObject[] meshPrimitiv { if (ContainsValidRenderer(gameObject)) { - if(gameObject.GetComponent() != null) + if (gameObject.GetComponent() != null) { skinnedMeshPrims.Add(gameObject); } @@ -691,7 +691,7 @@ private void FilterPrimitives(Transform transform, out GameObject[] meshPrimitiv var go = transform.GetChild(i).gameObject; if (IsPrimitive(go)) { - if(go.GetComponent() != null) + if (go.GetComponent() != null) { skinnedMeshPrims.Add(go); } @@ -897,7 +897,7 @@ private MeshId ExportMesh(string name, GameObject[] primitives) private void ExportAnimations(AnimationClip[] animationClips, Transform rootNodeTransform) { - if(null == animationClips) + if (null == animationClips) { return; } @@ -955,7 +955,7 @@ private AnimationId ExportAnimationClip(AnimationClip animationClip, Transform r { GLTFAnimationChannelPath path; AccessorId accessorId = ExportCurve(animationClip, property.name, property.curveBindings, frameCount, out path); - if(accessorId == null) + if (accessorId == null) { continue; } @@ -1093,14 +1093,14 @@ private List CurveMarshalling(EditorCurveBinding[] curveBindi bool bFoundCurveBindingSlot = false; foreach (var property in curveGroup.properties) { - if(property.name == Path.GetFileNameWithoutExtension(curveBinding.propertyName)) + if (property.name == Path.GetFileNameWithoutExtension(curveBinding.propertyName)) { property.curveBindings.Add(curveBinding); bFoundCurveBindingSlot = true; } } - if(!bFoundCurveBindingSlot) + if (!bFoundCurveBindingSlot) { curveGroup.properties.Add(new PropertyCurveBindings(Path.GetFileNameWithoutExtension(curveBinding.propertyName), curveBinding)); } @@ -1218,7 +1218,7 @@ private MeshPrimitive[] ExportPrimitive(GameObject gameObject, GLTFMesh mesh) if (meshObj.colors.Length != 0) aColor0 = ExportAccessor(meshObj.colors); - if(meshObj.boneWeights.Length != 0) + if (meshObj.boneWeights.Length != 0) { aJoint0 = ExportAccessor(SchemaExtensions.ExtractJointAndCopy(meshObj.boneWeights), SemanticProperties.JOINTS_0); aWeight0 = ExportAccessor(SchemaExtensions.ExtractWeightAndCopy(meshObj.boneWeights)); @@ -1315,7 +1315,7 @@ private MaterialId ExportMaterial(Material materialObj, MeshRenderer renderer) material.DoubleSided = materialObj.HasProperty("_Cull") && materialObj.GetInt("_Cull") == (float)CullMode.Off; - if(materialObj.IsKeywordEnabled("_EMISSION")) + if (materialObj.IsKeywordEnabled("_EMISSION")) { if (materialObj.HasProperty("_EmissionColor")) { @@ -1328,7 +1328,7 @@ private MaterialId ExportMaterial(Material materialObj, MeshRenderer renderer) if (emissionTex != null) { - if(emissionTex is Texture2D) + if (emissionTex is Texture2D) { material.EmissiveTexture = ExportTextureInfo(emissionTex, TextureMapType.Emission); @@ -1348,7 +1348,7 @@ private MaterialId ExportMaterial(Material materialObj, MeshRenderer renderer) if (normalTex != null) { - if(normalTex is Texture2D) + if (normalTex is Texture2D) { material.NormalTexture = ExportNormalTextureInfo(normalTex, TextureMapType.Bump, materialObj); ExportTextureTransform(material.NormalTexture, materialObj, "_BumpMap"); @@ -1365,7 +1365,7 @@ private MaterialId ExportMaterial(Material materialObj, MeshRenderer renderer) var occTex = materialObj.GetTexture("_OcclusionMap"); if (occTex != null) { - if(occTex is Texture2D) + if (occTex is Texture2D) { material.OcclusionTexture = ExportOcclusionTextureInfo(occTex, TextureMapType.Occlusion, materialObj); ExportTextureTransform(material.OcclusionTexture, materialObj, "_OcclusionMap"); @@ -1552,7 +1552,7 @@ private PbrMetallicRoughness ExportPBRMetallicRoughness(Material material) if (mainTex != null) { - if(mainTex is Texture2D) + if (mainTex is Texture2D) { pbr.BaseColorTexture = ExportTextureInfo(mainTex, TextureMapType.Main); ExportTextureTransform(pbr.BaseColorTexture, material, "_MainTex"); @@ -1582,7 +1582,7 @@ private PbrMetallicRoughness ExportPBRMetallicRoughness(Material material) if (mrTex != null) { - if(mrTex is Texture2D) + if (mrTex is Texture2D) { pbr.MetallicRoughnessTexture = ExportTextureInfo(mrTex, TextureMapType.MetallicGloss); ExportTextureTransform(pbr.MetallicRoughnessTexture, material, "_MetallicGlossMap"); @@ -1599,7 +1599,7 @@ private PbrMetallicRoughness ExportPBRMetallicRoughness(Material material) if (mgTex != null) { - if(mgTex is Texture2D) + if (mgTex is Texture2D) { pbr.MetallicRoughnessTexture = ExportTextureInfo(mgTex, TextureMapType.SpecGloss); ExportTextureTransform(pbr.MetallicRoughnessTexture, material, "_SpecGlossMap"); From a6557cddab572b869f4e7c2b1d704b36558648f2 Mon Sep 17 00:00:00 2001 From: William Date: Sat, 29 Feb 2020 13:38:28 +0800 Subject: [PATCH 09/10] Change variable names. --- .../UnityGLTF/Runtime/Scripts/GLTFSceneExporter.cs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/UnityGLTF/Assets/UnityGLTF/Runtime/Scripts/GLTFSceneExporter.cs b/UnityGLTF/Assets/UnityGLTF/Runtime/Scripts/GLTFSceneExporter.cs index 1f6acfb34..e18b70ed0 100644 --- a/UnityGLTF/Assets/UnityGLTF/Runtime/Scripts/GLTFSceneExporter.cs +++ b/UnityGLTF/Assets/UnityGLTF/Runtime/Scripts/GLTFSceneExporter.cs @@ -113,8 +113,8 @@ protected struct PrimKey private readonly Dictionary _meshToPrims = new Dictionary(); private readonly Dictionary _existedNodes = new Dictionary(); - private readonly Dictionary _existedAnimation = new Dictionary(); - private readonly Dictionary _existedSkin = new Dictionary(); + private readonly Dictionary _existedAnimations = new Dictionary(); + private readonly Dictionary _existedSkins = new Dictionary(); // Settings public static bool ExportNames = true; @@ -731,7 +731,7 @@ private void ExportSkin(string name, GameObject[] primitives, Node node) foreach (var prim in primitives) { var smr = prim.GetComponent(); - if (_existedSkin.TryGetValue(smr.rootBone.GetInstanceID(), out SkinId skinId)) + if (_existedSkins.TryGetValue(smr.rootBone.GetInstanceID(), out SkinId skinId)) { node.Skin = skinId; node.Mesh = ExportMesh(prim.name, new GameObject[] { prim }); @@ -824,7 +824,7 @@ private void ExportSkin(string name, GameObject[] primitives, Node node) node.Mesh = ExportMesh(prim.name, new GameObject[] { prim }); - _existedSkin.Add(smr.rootBone.GetInstanceID(), skinId); + _existedSkins.Add(smr.rootBone.GetInstanceID(), skinId); } } @@ -906,10 +906,10 @@ private void ExportAnimations(AnimationClip[] animationClips, Transform rootNode { var animInstId = animationClip.GetInstanceID(); AnimationId existedAnimId; - if (!_existedAnimation.TryGetValue(animInstId, out existedAnimId)) + if (!_existedAnimations.TryGetValue(animInstId, out existedAnimId)) { var animId = ExportAnimationClip(animationClip, rootNodeTransform); - _existedAnimation.Add(animInstId, animId); + _existedAnimations.Add(animInstId, animId); } } } From e475c8f29d16984e524198b67f6f9558f444590b Mon Sep 17 00:00:00 2001 From: William Date: Sun, 12 Apr 2020 17:25:58 +0800 Subject: [PATCH 10/10] Fix bugs. --- .../Runtime/Scripts/GLTFSceneExporter.cs | 23 +++++++++++++++++-- 1 file changed, 21 insertions(+), 2 deletions(-) diff --git a/UnityGLTF/Assets/UnityGLTF/Runtime/Scripts/GLTFSceneExporter.cs b/UnityGLTF/Assets/UnityGLTF/Runtime/Scripts/GLTFSceneExporter.cs index e18b70ed0..0f8ade26e 100644 --- a/UnityGLTF/Assets/UnityGLTF/Runtime/Scripts/GLTFSceneExporter.cs +++ b/UnityGLTF/Assets/UnityGLTF/Runtime/Scripts/GLTFSceneExporter.cs @@ -531,7 +531,10 @@ private NodeId ExportNode(Transform nodeTransform) }; _root.Nodes.Add(node); - _existedNodes.Add(nodeTransform.GetInstanceID(), id); + if (!_existedNodes.ContainsKey(nodeTransform.GetInstanceID())) + { + _existedNodes.Add(nodeTransform.GetInstanceID(), id); + } // children that are primitives get put in a mesh GameObject[] meshPrimitives, skinnedMeshPrimitives, nonPrimitives; @@ -541,7 +544,21 @@ private NodeId ExportNode(Transform nodeTransform) if (nonPrimitives.Length > 0) { node.Children = new List(nonPrimitives.Length); + List postProcess = new List(); foreach (var child in nonPrimitives) + { + GameObject[] childMeshPrimitives, childSkinnedMeshPrimitives, childNonPrimitives; + FilterPrimitives(child.transform, out childMeshPrimitives, out childSkinnedMeshPrimitives, out childNonPrimitives); + if (childMeshPrimitives.Length + childSkinnedMeshPrimitives.Length > 0) + { + postProcess.Add(child); + continue; + } + + node.Children.Add(ExportNode(child.transform)); + } + + foreach (var child in postProcess) { node.Children.Add(ExportNode(child.transform)); } @@ -762,7 +779,7 @@ private void ExportSkin(string name, GameObject[] primitives, Node node) { var boneNode = new Node { - Name = bone.gameObject.name, + Name = ExportNames ? bone.gameObject.name : null, Translation = new GLTF.Math.Vector3(translation.x, translation.y, translation.z), Rotation = new GLTF.Math.Quaternion(rotation.x, rotation.y, rotation.z, rotation.w), Scale = new GLTF.Math.Vector3(scale.x, scale.y, scale.z), @@ -795,6 +812,7 @@ private void ExportSkin(string name, GameObject[] primitives, Node node) }; _root.Nodes.Add(boneNode); + _existedNodes.Add(bone.GetInstanceID(), nodeId); } skin.Joints.Add(nodeId); @@ -808,6 +826,7 @@ private void ExportSkin(string name, GameObject[] primitives, Node node) Id = baseId, Root = _root }; + _existedNodes.Add(smr.rootBone.GetInstanceID(), rootBoneId); } skin.Skeleton = rootBoneId;