public void DividedVertexBufferTest() { var glTF = new glTF(); var bytesBuffer = new ArrayByteBuffer(new byte[50 * 1024 * 1024]); var bufferIndex = glTF.AddBuffer(bytesBuffer); var Materials = new List <Material> { new Material(Shader.Find("Standard")), // A new Material(Shader.Find("Standard")), // B }; var(go, mesh) = CreateMesh(Materials.ToArray()); var meshExportSettings = new GltfExportSettings { DivideVertexBuffer = true }; var axisInverter = Axes.X.Create(); var unityMesh = MeshExportList.Create(go); var(gltfMesh, blendShapeIndexMap) = meshExportSettings.DivideVertexBuffer ? MeshExporter_DividedVertexBuffer.Export(glTF, bufferIndex, unityMesh, Materials, axisInverter, meshExportSettings) : MeshExporter_SharedVertexBuffer.Export(glTF, bufferIndex, unityMesh, Materials, axisInverter, meshExportSettings) ; { var indices = glTF.GetIndices(gltfMesh.primitives[0].indices); Assert.AreEqual(0, indices[0]); Assert.AreEqual(1, indices[1]); Assert.AreEqual(3, indices[2]); Assert.AreEqual(3, indices[3]); Assert.AreEqual(1, indices[4]); Assert.AreEqual(2, indices[5]); } { var positions = glTF.GetArrayFromAccessor <Vector3>(gltfMesh.primitives[0].attributes.POSITION); Assert.AreEqual(4, positions.Length); } { var indices = glTF.GetIndices(gltfMesh.primitives[1].indices); Assert.AreEqual(0, indices[0]); Assert.AreEqual(1, indices[1]); Assert.AreEqual(3, indices[2]); Assert.AreEqual(3, indices[3]); Assert.AreEqual(1, indices[4]); Assert.AreEqual(2, indices[5]); } { var positions = glTF.GetArrayFromAccessor <Vector3>(gltfMesh.primitives[1].attributes.POSITION); Assert.AreEqual(4, positions.Length); } }
public static (Getter, int) GetAccessor(glTF gltf, int accessorIndex) { var gltfAccessor = gltf.accessors[accessorIndex]; switch (gltfAccessor.componentType) { case glComponentType.UNSIGNED_BYTE: { var array = gltf.GetArrayFromAccessor <Byte4>(accessorIndex); Getter getter = (i) => { var value = array[i]; var inv = 1.0f / byte.MaxValue; return(value.x * inv, value.y *inv, value.z *inv, value.w *inv); }; return(getter, array.Length); } case glComponentType.UNSIGNED_SHORT: { var array = gltf.GetArrayFromAccessor <UShort4>(accessorIndex); Getter getter = (i) => { var value = array[i]; var inv = 1.0f / ushort.MaxValue; return(value.x * inv, value.y *inv, value.z *inv, value.w *inv); }; return(getter, array.Length); } case glComponentType.FLOAT: { var array = gltf.GetArrayFromAccessor <Vector4>(accessorIndex); Getter getter = (i) => { var value = array[i]; return(value.x, value.y, value.z, value.w); }; return(getter, array.Length); } }
public static (Getter, int) GetAccessor(glTF gltf, int accessorIndex) { var gltfAccessor = gltf.accessors[accessorIndex]; switch (gltfAccessor.componentType) { case glComponentType.UNSIGNED_BYTE: { var array = gltf.GetArrayFromAccessor<Byte4>(accessorIndex); Getter getter = (i) => { var value = array[i]; return (value.x, value.y, value.z, value.w); }; return (getter, array.Length); }
public static AnimationClip ConvertAnimationClip(glTF gltf, glTFAnimation animation, AxisInverter inverter, glTFNode root = null) { var clip = new AnimationClip(); clip.ClearCurves(); clip.legacy = true; clip.name = animation.name; clip.wrapMode = WrapMode.Loop; foreach (var channel in animation.channels) { var relativePath = RelativePathFrom(gltf.nodes, root, gltf.nodes[channel.target.node]); switch (channel.target.path) { case glTFAnimationTarget.PATH_TRANSLATION: { var sampler = animation.samplers[channel.sampler]; var input = gltf.GetArrayFromAccessor <float>(sampler.input); var output = gltf.FlatternFloatArrayFromAccessor(sampler.output); AnimationImporterUtil.SetAnimationCurve( clip, relativePath, new string[] { "localPosition.x", "localPosition.y", "localPosition.z" }, input, output, sampler.interpolation, typeof(Transform), (values, last) => { Vector3 temp = new Vector3(values[0], values[1], values[2]); return(inverter.InvertVector3(temp).ToArray()); } ); } break; case glTFAnimationTarget.PATH_ROTATION: { var sampler = animation.samplers[channel.sampler]; var input = gltf.GetArrayFromAccessor <float>(sampler.input); var output = gltf.FlatternFloatArrayFromAccessor(sampler.output); AnimationImporterUtil.SetAnimationCurve( clip, relativePath, new string[] { "localRotation.x", "localRotation.y", "localRotation.z", "localRotation.w" }, input, output, sampler.interpolation, typeof(Transform), (values, last) => { Quaternion currentQuaternion = new Quaternion(values[0], values[1], values[2], values[3]); Quaternion lastQuaternion = new Quaternion(last[0], last[1], last[2], last[3]); return(AnimationImporterUtil.GetShortest(lastQuaternion, inverter.InvertQuaternion(currentQuaternion)).ToArray()); } ); clip.EnsureQuaternionContinuity(); } break; case glTFAnimationTarget.PATH_SCALE: { var sampler = animation.samplers[channel.sampler]; var input = gltf.GetArrayFromAccessor <float>(sampler.input); var output = gltf.FlatternFloatArrayFromAccessor(sampler.output); AnimationImporterUtil.SetAnimationCurve( clip, relativePath, new string[] { "localScale.x", "localScale.y", "localScale.z" }, input, output, sampler.interpolation, typeof(Transform), (values, last) => values); } break; case glTFAnimationTarget.PATH_WEIGHT: { var node = gltf.nodes[channel.target.node]; var mesh = gltf.meshes[node.mesh]; var primitive = mesh.primitives.FirstOrDefault(); var targets = primitive.targets; if (!gltf_mesh_extras_targetNames.TryGet(mesh, out List <string> targetNames)) { throw new Exception("glTF BlendShape Animation. targetNames invalid."); } var keyNames = targetNames .Where(x => !string.IsNullOrEmpty(x)) .Select(x => "blendShape." + x) .ToArray(); var sampler = animation.samplers[channel.sampler]; var input = gltf.GetArrayFromAccessor <float>(sampler.input); var output = gltf.GetArrayFromAccessor <float>(sampler.output); AnimationImporterUtil.SetAnimationCurve( clip, relativePath, keyNames, input, output, sampler.interpolation, typeof(SkinnedMeshRenderer), (values, last) => { for (int j = 0; j < values.Length; j++) { values[j] *= 100.0f; } return(values); }); } break; default: Debug.LogWarningFormat("unknown path: {0}", channel.target.path); break; } } return(clip); }
public static void SetupSkinning(glTF gltf, List <TransformWithSkin> nodes, int i, IAxisInverter inverter) { var x = nodes[i]; var skinnedMeshRenderer = x.Transform.GetComponent <SkinnedMeshRenderer>(); if (skinnedMeshRenderer != null) { var mesh = skinnedMeshRenderer.sharedMesh; if (x.SkinIndex.HasValue) { if (mesh == null) { throw new Exception(); } if (skinnedMeshRenderer == null) { throw new Exception(); } if (x.SkinIndex.Value < gltf.skins.Count) { // calculate internal values(boundingBox etc...) when sharedMesh assigned ? skinnedMeshRenderer.sharedMesh = null; var skin = gltf.skins[x.SkinIndex.Value]; var joints = skin.joints.Select(y => nodes[y].Transform).ToArray(); if (joints.Any()) { // have bones skinnedMeshRenderer.bones = joints; if (skin.inverseBindMatrices != -1) { var bindPoses = gltf.GetArrayFromAccessor <Matrix4x4>(skin.inverseBindMatrices) .Select(inverter.InvertMat4) .ToArray() ; mesh.bindposes = bindPoses; } else { // // calc default matrices // https://docs.unity3d.com/ScriptReference/Mesh-bindposes.html // var meshCoords = skinnedMeshRenderer.transform; // ? var calculatedBindPoses = joints.Select(y => y.worldToLocalMatrix * meshCoords.localToWorldMatrix).ToArray(); mesh.bindposes = calculatedBindPoses; } } else { // BlendShape only ? } skinnedMeshRenderer.sharedMesh = mesh; if (skin.skeleton >= 0 && skin.skeleton < nodes.Count) { skinnedMeshRenderer.rootBone = nodes[skin.skeleton].Transform; } } } } }
static MeshWithMaterials ImportMesh(glTF gltf, int meshIndex, glTFMesh gltfMesh, List <Material> materials, GetBlendShapeName getBlendShapeName ) { glTFAttributes lastAttributes = null; var sharedAttributes = true; foreach (var prim in gltfMesh.primitives) { if (lastAttributes != null && !prim.attributes.Equals(lastAttributes)) { sharedAttributes = false; break; } lastAttributes = prim.attributes; } var positions = new List <Vector3>(); var normals = new List <Vector3>(); var tangents = new List <Vector4>(); var colors = new List <Vector4>(); var uv = new List <Vector2>(); var boneWeights = new List <BoneWeight>(); var subMeshes = new List <int[]>(); var materialIndices = new List <int>(); BlendShape[] blendShapes = null; if (sharedAttributes) { // multiple submesh sharing same VertexBuffer { var prim = gltfMesh.primitives.First(); positions.AddRange(gltf.GetArrayFromAccessor <Vector3>(prim.attributes.POSITION).Select(x => x.ReverseZ())); // normal if (prim.attributes.NORMAL != -1) { normals.AddRange(gltf.GetArrayFromAccessor <Vector3>(prim.attributes.NORMAL).Select(x => x.ReverseZ())); } // tangent if (prim.attributes.TANGENT != -1) { tangents.AddRange(gltf.GetArrayFromAccessor <Vector4>(prim.attributes.TANGENT).Select(x => x.ReverseZ())); } // vertex color if (prim.attributes.COLOR_0 != -1) { colors.AddRange(gltf.GetArrayFromAccessor <Vector4>(prim.attributes.COLOR_0)); } // uv if (prim.attributes.TEXCOORD_0 != -1) { uv.AddRange(gltf.GetArrayFromAccessor <Vector2>(prim.attributes.TEXCOORD_0).Select(x => x.ReverseY())); } else { // for inconsistent attributes in primitives uv.AddRange(new Vector2[positions.Count]); } // skin if (prim.attributes.JOINTS_0 != -1 && prim.attributes.WEIGHTS_0 != -1) { var joints0 = gltf.GetArrayFromAccessor <UShort4>(prim.attributes.JOINTS_0); // uint4 var weights0 = gltf.GetArrayFromAccessor <Float4>(prim.attributes.WEIGHTS_0).Select(x => x.One()).ToArray(); for (int j = 0; j < joints0.Length; ++j) { var bw = new BoneWeight(); bw.boneIndex0 = joints0[j].x; bw.weight0 = weights0[j].x; bw.boneIndex1 = joints0[j].y; bw.weight1 = weights0[j].y; bw.boneIndex2 = joints0[j].z; bw.weight2 = weights0[j].z; bw.boneIndex3 = joints0[j].w; bw.weight3 = weights0[j].w; boneWeights.Add(bw); } } // blendshape if (prim.targets != null && prim.targets.Count > 0) { if (blendShapes == null) { blendShapes = prim.targets.Select((x, i) => new BlendShape(getBlendShapeName(meshIndex, i))).ToArray(); } for (int i = 0; i < prim.targets.Count; ++i) { //var name = string.Format("target{0}", i++); var primTarget = prim.targets[i]; var blendShape = blendShapes[i]; if (primTarget.POSITION != -1) { blendShape.Positions.AddRange( gltf.GetArrayFromAccessor <Vector3>(primTarget.POSITION).Select(x => x.ReverseZ()).ToArray()); } if (primTarget.NORMAL != -1) { blendShape.Normals.AddRange( gltf.GetArrayFromAccessor <Vector3>(primTarget.NORMAL).Select(x => x.ReverseZ()).ToArray()); } if (primTarget.TANGENT != -1) { blendShape.Tangents.AddRange( gltf.GetArrayFromAccessor <Vector3>(primTarget.TANGENT).Select(x => x.ReverseZ()).ToArray()); } } } } foreach (var prim in gltfMesh.primitives) { var indices = gltf.GetIndices(prim.indices).Select(x => x).ToArray(); subMeshes.Add(indices); // material materialIndices.Add(prim.material); } } else { // multiple submMesh is not sharing a VertexBuffer. // each subMesh use a independent VertexBuffer. var targets = gltfMesh.primitives[0].targets; for (int i = 1; i < gltfMesh.primitives.Count; ++i) { if (!gltfMesh.primitives[i].targets.SequenceEqual(targets)) { throw new NotImplementedException(string.Format("diffirent targets: {0} with {1}", gltfMesh.primitives[i], targets)); } } foreach (var prim in gltfMesh.primitives) { var indexOffset = positions.Count; var indexBuffer = prim.indices; var positionCount = positions.Count; positions.AddRange(gltf.GetArrayFromAccessor <Vector3>(prim.attributes.POSITION).Select(x => x.ReverseZ())); positionCount = positions.Count - positionCount; // normal if (prim.attributes.NORMAL != -1) { normals.AddRange(gltf.GetArrayFromAccessor <Vector3>(prim.attributes.NORMAL).Select(x => x.ReverseZ())); } if (prim.attributes.TANGENT != -1) { tangents.AddRange(gltf.GetArrayFromAccessor <Vector4>(prim.attributes.TANGENT).Select(x => x.ReverseZ())); } // vertex color if (prim.attributes.COLOR_0 != -1) { colors.AddRange(gltf.GetArrayFromAccessor <Vector4>(prim.attributes.COLOR_0)); } // uv if (prim.attributes.TEXCOORD_0 != -1) { uv.AddRange(gltf.GetArrayFromAccessor <Vector2>(prim.attributes.TEXCOORD_0).Select(x => x.ReverseY())); } else { // for inconsistent attributes in primitives uv.AddRange(new Vector2[positionCount]); } // skin if (prim.attributes.JOINTS_0 != -1 && prim.attributes.WEIGHTS_0 != -1) { var joints0 = gltf.GetArrayFromAccessor <UShort4>(prim.attributes.JOINTS_0); // uint4 var weights0 = gltf.GetArrayFromAccessor <Float4>(prim.attributes.WEIGHTS_0).Select(x => x.One()).ToArray(); for (int j = 0; j < joints0.Length; ++j) { var bw = new BoneWeight(); bw.boneIndex0 = joints0[j].x; bw.weight0 = weights0[j].x; bw.boneIndex1 = joints0[j].y; bw.weight1 = weights0[j].y; bw.boneIndex2 = joints0[j].z; bw.weight2 = weights0[j].z; bw.boneIndex3 = joints0[j].w; bw.weight3 = weights0[j].w; boneWeights.Add(bw); } } // blendshape if (prim.targets != null && prim.targets.Count > 0) { if (blendShapes == null) { blendShapes = prim.targets.Select((x, i) => new BlendShape(i.ToString())).ToArray(); } for (int i = 0; i < prim.targets.Count; ++i) { //var name = string.Format("target{0}", i++); var primTarget = prim.targets[i]; var blendShape = blendShapes[i]; if (primTarget.POSITION != -1) { blendShape.Positions.AddRange( gltf.GetArrayFromAccessor <Vector3>(primTarget.POSITION).Select(x => x.ReverseZ()).ToArray()); } if (primTarget.NORMAL != -1) { blendShape.Normals.AddRange( gltf.GetArrayFromAccessor <Vector3>(primTarget.NORMAL).Select(x => x.ReverseZ()).ToArray()); } if (primTarget.TANGENT != -1) { blendShape.Tangents.AddRange( gltf.GetArrayFromAccessor <Vector3>(primTarget.TANGENT).Select(x => x.ReverseZ()).ToArray()); } } } var indices = (indexBuffer >= 0) ? gltf.GetIndices(indexBuffer).Select(x => x + indexOffset).ToArray() : TriangleUtil.FlipTriangle(Enumerable.Range(0, positions.Count)).ToArray() // without index array ; subMeshes.Add(indices); // material materialIndices.Add(prim.material); } } if (!materialIndices.Any()) { materialIndices.Add(0); } //Debug.Log(prims.ToJson()); var mesh = new Mesh(); mesh.name = gltfMesh.name; if (positions.Count > UInt16.MaxValue) { #if UNITY_2017_3_OR_NEWER mesh.indexFormat = UnityEngine.Rendering.IndexFormat.UInt32; #else Debug.LogWarningFormat("vertices {0} exceed 65535. not implemented. Unity2017.3 supports large mesh", positions.Count); #endif } mesh.vertices = positions.ToArray(); if (normals.Any()) { mesh.normals = normals.ToArray(); } else { mesh.RecalculateNormals(); } if (tangents.Any()) { mesh.tangents = tangents.ToArray(); } else { mesh.RecalculateTangents(); } if (colors.Any()) { mesh.colors = colors.ConvertAll(x => new Color(x.x, x.y, x.z, 1f)).ToArray(); } if (uv.Any()) { mesh.uv = uv.ToArray(); } if (boneWeights.Any()) { mesh.boneWeights = boneWeights.ToArray(); } mesh.subMeshCount = subMeshes.Count; for (int i = 0; i < subMeshes.Count; ++i) { mesh.SetTriangles(subMeshes[i], i); } var result = new MeshWithMaterials { Mesh = mesh, Materials = materialIndices.Select(x => materials[x]).ToArray() }; List <Material> matList = result.Materials.ToList(); matList.RemoveAll(x => x == null); result.Materials = matList.ToArray(); if (blendShapes != null) { foreach (var blendShape in blendShapes) { if (blendShape.Positions.Count > 0) { if (blendShape.Positions.Count == mesh.vertexCount) { mesh.AddBlendShapeFrame(blendShape.Name, FRAME_WEIGHT, blendShape.Positions.ToArray(), normals.Count == mesh.vertexCount ? blendShape.Normals.ToArray() : null, null ); } else { Debug.LogWarningFormat("May be partial primitive has blendShape. Rquire separete mesh or extend blend shape, but not implemented: {0}", blendShape.Name); } } } } return(result); }
public static GameObject Import(IImporterContext ctx, string json, ArraySegment <Byte> glbBinChunk, OnLoadCallback callback = null, GetBlendShapeName getBlendShapeName = null, CreateMaterialFunc createMaterial = null ) { if (getBlendShapeName == null) { getBlendShapeName = DefaultGetBlendShapeName; } // exclude not gltf-2.0 var parsed = json.ParseAsJson(); try { if (parsed["asset"]["version"].GetString() != "2.0") { Debug.LogWarningFormat("is not gltf-2.0: {0}", ctx.Path); return(null); } } catch (Exception) { Debug.LogWarningFormat("{0}: fail to parse json", ctx.Path); return(null); } // parse json glTF gltf = null; try { gltf = JsonUtility.FromJson <glTF>(json); } catch (Exception) { Debug.LogWarningFormat("{0}: fail to parse json", ctx.Path); return(null); } if (gltf == null) { Debug.LogWarningFormat("{0}: fail to parse json", ctx.Path); return(null); } if (gltf.asset.version != "2.0") { Debug.LogWarningFormat("unknown gltf version {0}", gltf.asset.version); return(null); } gltf.baseDir = Path.GetDirectoryName(ctx.Path); //Debug.LogFormat("{0}: {1}", ctx.Path, gltf); foreach (var buffer in gltf.buffers) { buffer.OpenStorage(gltf.baseDir, glbBinChunk); } // textures var textures = ImportTextures(gltf) .Select(x => { var samplerIndex = gltf.textures[x.TextureIndex].sampler; var sampler = gltf.samplers[samplerIndex]; if (x.Texture == null) { Debug.LogWarningFormat("May be import order, not yet texture is not imported. Later, manualy reimport {0}", ctx.Path); } else { SetSampler(x.Texture, sampler); if (!x.IsAsset) { ctx.AddObjectToAsset(x.Texture.name, x.Texture); } } return(x); }) .ToArray(); if (createMaterial == null) { createMaterial = CreateMaterialFuncFromShader(Shader.Find("Standard")); } // materials List <Material> materials = new List <Material>(); if (gltf.materials == null || !gltf.materials.Any()) { materials.Add(createMaterial(ctx, 0, null, textures)); } else { for (int i = 0; i < gltf.materials.Count; ++i) { Material mat = null; // [Maquette Code] if (Maquette.Unity.MqAddonUtility.NeedsMaquetteMaterial(gltf.materials[i].name)) { string materialName = gltf.materials[i].name; materialName = materialName.Replace(" (Instance)", ""); mat = Maquette.Unity.MqAddonUtility.CreateMaquetteMaterial(materialName); if (gltf.materials[i].pbrMetallicRoughness.baseColorTexture != null) { int baseTextureIndex = gltf.materials[i].pbrMetallicRoughness.baseColorTexture.index; if (baseTextureIndex >= 0 && baseTextureIndex < textures.Length) { var texture = textures[baseTextureIndex]; mat.mainTexture = texture.Texture; } } } else { mat = createMaterial(ctx, i, gltf.materials[i], textures); } materials.Add(mat); } } foreach (var material in materials) { if (material != null) { ctx.AddObjectToAsset(material.name, material); } } // meshes var meshes = gltf.meshes.Select((x, i) => { var meshWithMaterials = ImportMesh(gltf, i, x, materials, getBlendShapeName); var mesh = meshWithMaterials.Mesh; if (string.IsNullOrEmpty(mesh.name)) { mesh.name = string.Format("UniGLTF import#{0}", i); } ctx.AddObjectToAsset(mesh.name, mesh); return(meshWithMaterials); }).ToArray(); // nodes var _nodes = gltf.nodes.Select(x => ImportNode(x)).ToArray(); var nodes = _nodes.Select((go, i) => { if (string.IsNullOrEmpty(go.name)) { go.name = string.Format("node{0:000}", i); } var nodeWithSkin = new TransformWithSkin { Transform = go.transform, }; var node = gltf.nodes[i]; // // build hierachy // if (node.children != null) { foreach (var child in node.children) { _nodes[child].transform.SetParent(_nodes[i].transform, false // node has local transform ); } } // // attach mesh // if (node.mesh != -1) { var mesh = meshes[node.mesh]; if (mesh.Mesh.blendShapeCount == 0 && node.skin == -1) { // without blendshape and bone skinning var filter = go.AddComponent <MeshFilter>(); filter.sharedMesh = mesh.Mesh; var renderer = go.AddComponent <MeshRenderer>(); renderer.sharedMaterials = mesh.Materials; } else { var renderer = go.AddComponent <SkinnedMeshRenderer>(); if (node.skin != -1) { nodeWithSkin.SkinIndex = node.skin; } renderer.sharedMesh = mesh.Mesh; renderer.sharedMaterials = mesh.Materials; } } return(nodeWithSkin); }).ToArray(); // // fix node's coordinate. z-back to z-forward // var globalTransformMap = nodes.ToDictionary(x => x.Transform, x => new PosRot { Position = x.Transform.position, Rotation = x.Transform.rotation, }); foreach (var x in gltf.rootnodes) { // fix nodes coordinate // reverse Z in global var t = nodes[x].Transform; //t.SetParent(root.transform, false); foreach (var transform in t.Traverse()) { var g = globalTransformMap[transform]; transform.position = g.Position.ReverseZ(); transform.rotation = g.Rotation.ReverseZ(); } } // skinning foreach (var x in nodes) { var skinnedMeshRenderer = x.Transform.GetComponent <SkinnedMeshRenderer>(); if (skinnedMeshRenderer != null) { var mesh = skinnedMeshRenderer.sharedMesh; if (x.SkinIndex.HasValue) { if (mesh == null) { throw new Exception(); } if (skinnedMeshRenderer == null) { throw new Exception(); } if (x.SkinIndex.Value < gltf.skins.Count) { var skin = gltf.skins[x.SkinIndex.Value]; skinnedMeshRenderer.sharedMesh = null; var joints = skin.joints.Select(y => nodes[y].Transform).ToArray(); skinnedMeshRenderer.bones = joints; //skinnedMeshRenderer.rootBone = nodes[skin.skeleton].Transform; if (skin.inverseBindMatrices != -1) { // BlendShape only ? #if false // https://docs.unity3d.com/ScriptReference/Mesh-bindposes.html var hipsParent = nodes[0].Transform; var calculatedBindPoses = joints.Select(y => y.worldToLocalMatrix * hipsParent.localToWorldMatrix).ToArray(); mesh.bindposes = calculatedBindPoses; #else var bindPoses = gltf.GetArrayFromAccessor <Matrix4x4>(skin.inverseBindMatrices) .Select(y => y.ReverseZ()) .ToArray() ; mesh.bindposes = bindPoses; #endif } skinnedMeshRenderer.sharedMesh = mesh; } } } } var root = new GameObject("_root_"); ctx.SetMainGameObject("root", root); foreach (var x in gltf.rootnodes) { // fix nodes coordinate // reverse Z in global var t = nodes[x].Transform; t.SetParent(root.transform, false); } // animation if (gltf.animations != null && gltf.animations.Any()) { var clip = new AnimationClip(); clip.name = ANIMATION_NAME; clip.ClearCurves(); ImportAnimation(root.transform, clip, gltf.animations, nodes.Select(x => x.Transform).ToArray(), gltf); clip.legacy = true; clip.name = "legacy"; clip.wrapMode = WrapMode.Loop; var animation = root.AddComponent <Animation>(); animation.clip = clip; ctx.AddObjectToAsset(ANIMATION_NAME, clip); } if (callback != null) { callback(ctx, json, nodes.Select(x => x.Transform).ToArray(), meshes.Select(x => x.Mesh).ToList() ); } Debug.LogFormat("Import {0}", ctx.Path); return(root); }
public static void ImportAnimation(Transform root, AnimationClip clip, List <glTFAnimation> animations, Transform[] nodes, glTF gltf) { for (int i = 0; i < animations.Count; ++i) { var animation = animations[i]; if (string.IsNullOrEmpty(animation.name)) { animation.name = string.Format("animation:{0}", i); } foreach (var y in animation.channels) { var targetTransform = nodes[y.target.node]; var relativePath = targetTransform.RelativePathFrom(root); switch (y.target.path) { case glTFAnimationTarget.PATH_TRANSLATION: { var curveX = new AnimationCurve(); var curveY = new AnimationCurve(); var curveZ = new AnimationCurve(); var sampler = animation.samplers[y.sampler]; var input = gltf.GetArrayFromAccessor <float>(sampler.input); var output = gltf.GetArrayFromAccessor <Vector3>(sampler.output); for (int j = 0; j < input.Length; ++j) { var time = input[j]; var pos = output[j].ReverseZ(); curveX.AddKey(time, pos.x); curveY.AddKey(time, pos.y); curveZ.AddKey(time, pos.z); } clip.SetCurve(relativePath, typeof(Transform), "localPosition.x", curveX); clip.SetCurve(relativePath, typeof(Transform), "localPosition.y", curveY); clip.SetCurve(relativePath, typeof(Transform), "localPosition.z", curveZ); } break; case glTFAnimationTarget.PATH_ROTATION: { var curveX = new AnimationCurve(); var curveY = new AnimationCurve(); var curveZ = new AnimationCurve(); var curveW = new AnimationCurve(); var sampler = animation.samplers[y.sampler]; var input = gltf.GetArrayFromAccessor <float>(sampler.input); var output = gltf.GetArrayFromAccessor <Quaternion>(sampler.output); for (int j = 0; j < input.Length; ++j) { var time = input[j]; var rot = output[j].ReverseZ(); curveX.AddKey(time, rot.x); curveY.AddKey(time, rot.y); curveZ.AddKey(time, rot.z); curveW.AddKey(time, rot.w); } clip.SetCurve(relativePath, typeof(Transform), "localRotation.x", curveX); clip.SetCurve(relativePath, typeof(Transform), "localRotation.y", curveY); clip.SetCurve(relativePath, typeof(Transform), "localRotation.z", curveZ); clip.SetCurve(relativePath, typeof(Transform), "localRotation.w", curveW); } break; case glTFAnimationTarget.PATH_SCALE: { var curveX = new AnimationCurve(); var curveY = new AnimationCurve(); var curveZ = new AnimationCurve(); var sampler = animation.samplers[y.sampler]; var input = gltf.GetArrayFromAccessor <float>(sampler.input); var output = gltf.GetArrayFromAccessor <Vector3>(sampler.output); for (int j = 0; j < input.Length; ++j) { var time = input[j]; var scale = output[j]; curveX.AddKey(time, scale.x); curveY.AddKey(time, scale.y); curveZ.AddKey(time, scale.z); } clip.SetCurve(relativePath, typeof(Transform), "localScale.x", curveX); clip.SetCurve(relativePath, typeof(Transform), "localScale.y", curveY); clip.SetCurve(relativePath, typeof(Transform), "localScale.z", curveZ); } break; case glTFAnimationTarget.PATH_WEIGHT: { var node = gltf.nodes[y.target.node]; var mesh = gltf.meshes[node.mesh]; for (int k = 0; k < mesh.weights.Length; ++k) { //var weight = mesh.weights[k]; var curve = new AnimationCurve(); var sampler = animation.samplers[y.sampler]; var input = gltf.GetArrayFromAccessor <float>(sampler.input); var output = gltf.GetArrayFromAccessor <float>(sampler.output); for (int j = 0; j < input.Length; ++j) { curve.AddKey(input[j], output[j]); } clip.SetCurve(relativePath, typeof(SkinnedMeshRenderer), "blendShape." + k, curve); } } break; } } } }