public void Export(ExportingGltfData _data, GameObject Copy, List <Transform> Nodes) { var clips = GetAnimationClips(Copy); foreach (AnimationClip clip in clips) { var animationWithCurve = AnimationExporter.Export(clip, Copy.transform, Nodes); foreach (var kv in animationWithCurve.SamplerMap) { var sampler = animationWithCurve.Animation.samplers[kv.Key]; var inputAccessorIndex = _data.ExtendBufferAndGetAccessorIndex(kv.Value.Input); sampler.input = inputAccessorIndex; var outputAccessorIndex = _data.ExtendBufferAndGetAccessorIndex(kv.Value.Output); sampler.output = outputAccessorIndex; // modify accessors var outputAccessor = _data.Gltf.accessors[outputAccessorIndex]; var channel = animationWithCurve.Animation.channels.First(x => x.sampler == kv.Key); switch (glTFAnimationTarget.GetElementCount(channel.target.path)) { case 1: outputAccessor.type = "SCALAR"; //outputAccessor.count = ; break; case 3: outputAccessor.type = "VEC3"; outputAccessor.count /= 3; break; case 4: outputAccessor.type = "VEC4"; outputAccessor.count /= 4; break; default: throw new NotImplementedException(); } } animationWithCurve.Animation.name = clip.name; _data.Gltf.animations.Add(animationWithCurve.Animation); } }
public glTFPrimitives ToGltfPrimitive(ExportingGltfData data, int materialIndex, IEnumerable <int> indices) { var indicesAccessorIndex = data.ExtendBufferAndGetAccessorIndex(indices.Select(x => (uint)m_vertexIndexMap[x]).ToArray(), glBufferTarget.ELEMENT_ARRAY_BUFFER); var positions = m_positions.ToArray(); var positionAccessorIndex = data.ExtendBufferAndGetAccessorIndex(positions, glBufferTarget.ARRAY_BUFFER); AccessorsBounds.UpdatePositionAccessorsBounds(data.Gltf.accessors[positionAccessorIndex], positions); var normals = m_normals.ToArray(); var normalAccessorIndex = data.ExtendBufferAndGetAccessorIndex(normals, glBufferTarget.ARRAY_BUFFER); var uvAccessorIndex0 = data.ExtendBufferAndGetAccessorIndex(m_uv.ToArray(), glBufferTarget.ARRAY_BUFFER); int?jointsAccessorIndex = default; if (m_joints != null) { jointsAccessorIndex = data.ExtendBufferAndGetAccessorIndex(m_joints.ToArray(), glBufferTarget.ARRAY_BUFFER); } int?weightAccessorIndex = default; if (m_weights != null) { weightAccessorIndex = data.ExtendBufferAndGetAccessorIndex(m_weights.ToArray(), glBufferTarget.ARRAY_BUFFER); } int?vertexColorIndex = default; if (m_color.Count == m_positions.Count) { vertexColorIndex = data.ExtendBufferAndGetAccessorIndex(m_color.ToArray(), glBufferTarget.ARRAY_BUFFER); } var primitive = new glTFPrimitives { indices = indicesAccessorIndex, attributes = new glTFAttributes { POSITION = positionAccessorIndex, NORMAL = normalAccessorIndex, TEXCOORD_0 = uvAccessorIndex0, JOINTS_0 = jointsAccessorIndex.GetValueOrDefault(-1), WEIGHTS_0 = weightAccessorIndex.GetValueOrDefault(-1), COLOR_0 = vertexColorIndex.GetValueOrDefault(-1), }, material = materialIndex, mode = 4, }; return(primitive); }
public virtual void Export(ITextureSerializer textureSerializer) { Nodes = Copy.transform.Traverse() .Skip(1) // exclude root object for the symmetry with the importer .ToList(); var uniqueUnityMeshes = new MeshExportList(); uniqueUnityMeshes.GetInfo(Nodes, m_settings); #region Materials and Textures ReportProgress("Materials and Textures", 0.2f); Materials = uniqueUnityMeshes.GetUniqueMaterials().ToList(); _textureExporter = new TextureExporter(textureSerializer); var materialExporter = CreateMaterialExporter(); _gltf.materials = Materials.Select(x => materialExporter.ExportMaterial(x, TextureExporter, m_settings)).ToList(); #endregion #region Meshes ReportProgress("Meshes", 0.4f); MeshBlendShapeIndexMap = new Dictionary <Mesh, Dictionary <int, int> >(); foreach (var unityMesh in uniqueUnityMeshes) { if (!unityMesh.CanExport) { continue; } var(gltfMesh, blendShapeIndexMap) = m_settings.DivideVertexBuffer ? MeshExporter_DividedVertexBuffer.Export(_data, unityMesh, Materials, m_settings.InverseAxis.Create(), m_settings) : MeshExporter_SharedVertexBuffer.Export(_data, unityMesh, Materials, m_settings.InverseAxis.Create(), m_settings) ; _gltf.meshes.Add(gltfMesh); Meshes.Add(unityMesh.Mesh); if (!MeshBlendShapeIndexMap.ContainsKey(unityMesh.Mesh)) { // 重複防止 MeshBlendShapeIndexMap.Add(unityMesh.Mesh, blendShapeIndexMap); } } #endregion #region Nodes and Skins ReportProgress("Nodes and Skins", 0.8f); var skins = uniqueUnityMeshes .SelectMany(x => x.Renderers) .Where(x => x.Item1 is SkinnedMeshRenderer && x.UniqueBones != null) .Select(x => x.Item1 as SkinnedMeshRenderer) .ToList() ; foreach (var node in Nodes) { var gltfNode = ExportNode(node, Nodes, uniqueUnityMeshes, skins); _gltf.nodes.Add(gltfNode); } _gltf.scenes = new List <gltfScene> { new gltfScene { nodes = Copy.transform.GetChildren().Select(x => Nodes.IndexOf(x)).ToArray(), } }; foreach (var x in uniqueUnityMeshes) { foreach (var(renderer, uniqueBones) in x.Renderers) { if (uniqueBones != null && renderer is SkinnedMeshRenderer smr) { var matrices = x.GetBindPoses().Select(m_settings.InverseAxis.Create().InvertMat4).ToArray(); var accessor = _data.ExtendBufferAndGetAccessorIndex(matrices, glBufferTarget.NONE); var skin = new glTFSkin { inverseBindMatrices = accessor, joints = uniqueBones.Select(y => Nodes.IndexOf(y)).ToArray(), skeleton = Nodes.IndexOf(smr.rootBone), }; var skinIndex = _gltf.skins.Count; _gltf.skins.Add(skin); foreach (var z in Nodes.Where(y => y.Has(renderer))) { var nodeIndex = Nodes.IndexOf(z); var node = _gltf.nodes[nodeIndex]; node.skin = skinIndex; } } } } #endregion #if UNITY_EDITOR #region Animations ReportProgress("Animations", 0.9f); var clips = new List <AnimationClip>(); var animator = Copy.GetComponent <Animator>(); var animation = Copy.GetComponent <Animation>(); if (animator != null) { clips = AnimationExporter.GetAnimationClips(animator); } else if (animation != null) { clips = AnimationExporter.GetAnimationClips(animation); } if (clips.Any()) { foreach (AnimationClip clip in clips) { var animationWithCurve = AnimationExporter.Export(clip, Copy.transform, Nodes); foreach (var kv in animationWithCurve.SamplerMap) { var sampler = animationWithCurve.Animation.samplers[kv.Key]; var inputAccessorIndex = _data.ExtendBufferAndGetAccessorIndex(kv.Value.Input); sampler.input = inputAccessorIndex; var outputAccessorIndex = _data.ExtendBufferAndGetAccessorIndex(kv.Value.Output); sampler.output = outputAccessorIndex; // modify accessors var outputAccessor = _gltf.accessors[outputAccessorIndex]; var channel = animationWithCurve.Animation.channels.First(x => x.sampler == kv.Key); switch (glTFAnimationTarget.GetElementCount(channel.target.path)) { case 1: outputAccessor.type = "SCALAR"; //outputAccessor.count = ; break; case 3: outputAccessor.type = "VEC3"; outputAccessor.count /= 3; break; case 4: outputAccessor.type = "VEC4"; outputAccessor.count /= 4; break; default: throw new NotImplementedException(); } } animationWithCurve.Animation.name = clip.name; _gltf.animations.Add(animationWithCurve.Animation); } } #endregion #endif ExportExtensions(textureSerializer); // Extension で Texture が増える場合があるので最後に呼ぶ var exported = _textureExporter.Export(); for (var exportedTextureIdx = 0; exportedTextureIdx < exported.Count; ++exportedTextureIdx) { var(unityTexture, colorSpace) = exported[exportedTextureIdx]; GltfTextureExporter.PushGltfTexture(_data, unityTexture, colorSpace, textureSerializer); } FixName(_gltf); }
/// <summary> /// primitive 間で vertex を共有する形で Export する。 /// UniVRM-0.71.0 以降は、MeshExporterDivided.Export もある。 /// /// * GLB/GLTF は shared(default) と divided を選択可能 /// * VRM0 は shared 仕様 /// * VRM1 は divided 仕様 /// /// /// </summary> /// <param name="gltf"></param> /// <param name="bufferIndex"></param> /// <param name="unityMesh"></param> /// <param name="unityMaterials"></param> /// <param name="axisInverter"></param> /// <param name="settings"></param> /// <returns></returns> public static (glTFMesh, Dictionary <int, int> blendShapeIndexMap) Export(ExportingGltfData data, MeshExportInfo unityMesh, List <Material> unityMaterials, IAxisInverter axisInverter, GltfExportSettings settings) { var mesh = unityMesh.Mesh; var materials = unityMesh.Materials; var positions = mesh.vertices.Select(axisInverter.InvertVector3).ToArray(); var positionAccessorIndex = data.ExtendBufferAndGetAccessorIndex(positions, glBufferTarget.ARRAY_BUFFER); data.GLTF.accessors[positionAccessorIndex].min = positions.Aggregate(positions[0], (a, b) => new Vector3(Mathf.Min(a.x, b.x), Math.Min(a.y, b.y), Mathf.Min(a.z, b.z))).ToArray(); data.GLTF.accessors[positionAccessorIndex].max = positions.Aggregate(positions[0], (a, b) => new Vector3(Mathf.Max(a.x, b.x), Math.Max(a.y, b.y), Mathf.Max(a.z, b.z))).ToArray(); var normalAccessorIndex = data.ExtendBufferAndGetAccessorIndex(mesh.normals.Select(y => axisInverter.InvertVector3(y.normalized)).ToArray(), glBufferTarget.ARRAY_BUFFER); int?tangentAccessorIndex = default; if (settings.ExportTangents) { tangentAccessorIndex = data.ExtendBufferAndGetAccessorIndex(mesh.tangents.Select(axisInverter.InvertVector4).ToArray(), glBufferTarget.ARRAY_BUFFER); } var uvAccessorIndex0 = data.ExtendBufferAndGetAccessorIndex(mesh.uv.Select(y => y.ReverseUV()).ToArray(), glBufferTarget.ARRAY_BUFFER); var uvAccessorIndex1 = data.ExtendBufferAndGetAccessorIndex(mesh.uv2.Select(y => y.ReverseUV()).ToArray(), glBufferTarget.ARRAY_BUFFER); var colorAccessorIndex = -1; var vColorState = VertexColorUtility.DetectVertexColor(mesh, materials); if (vColorState == VertexColorState.ExistsAndIsUsed || // VColor使っている vColorState == VertexColorState.ExistsAndMixed // VColorを使っているところと使っていないところが混在(とりあえずExportする) ) { // UniUnlit で Multiply 設定になっている colorAccessorIndex = data.ExtendBufferAndGetAccessorIndex(mesh.colors, glBufferTarget.ARRAY_BUFFER); } var boneweights = mesh.boneWeights; var weightAccessorIndex = data.ExtendBufferAndGetAccessorIndex(boneweights.Select(y => new Vector4(y.weight0, y.weight1, y.weight2, y.weight3)).ToArray(), glBufferTarget.ARRAY_BUFFER); var jointsAccessorIndex = data.ExtendBufferAndGetAccessorIndex(boneweights.Select(y => new UShort4( (ushort)unityMesh.GetJointIndex(y.boneIndex0), (ushort)unityMesh.GetJointIndex(y.boneIndex1), (ushort)unityMesh.GetJointIndex(y.boneIndex2), (ushort)unityMesh.GetJointIndex(y.boneIndex3)) ).ToArray(), glBufferTarget.ARRAY_BUFFER); var attributes = new glTFAttributes { POSITION = positionAccessorIndex, }; if (normalAccessorIndex != -1) { attributes.NORMAL = normalAccessorIndex; } if (tangentAccessorIndex.HasValue) { attributes.TANGENT = tangentAccessorIndex.Value; } if (uvAccessorIndex0 != -1) { attributes.TEXCOORD_0 = uvAccessorIndex0; } if (uvAccessorIndex1 != -1) { attributes.TEXCOORD_1 = uvAccessorIndex1; } if (colorAccessorIndex != -1) { attributes.COLOR_0 = colorAccessorIndex; } if (weightAccessorIndex != -1) { attributes.WEIGHTS_0 = weightAccessorIndex; } if (jointsAccessorIndex != -1) { attributes.JOINTS_0 = jointsAccessorIndex; } var gltfMesh = new glTFMesh(mesh.name); var indices = new List <uint>(); for (int j = 0; j < mesh.subMeshCount; ++j) { indices.Clear(); var triangles = mesh.GetIndices(j); if (triangles.Length == 0) { // https://github.com/vrm-c/UniVRM/issues/664 continue; } for (int i = 0; i < triangles.Length; i += 3) { var i0 = triangles[i]; var i1 = triangles[i + 1]; var i2 = triangles[i + 2]; // flip triangle indices.Add((uint)i2); indices.Add((uint)i1); indices.Add((uint)i0); } var indicesAccessorIndex = data.ExtendBufferAndGetAccessorIndex(indices.ToArray(), glBufferTarget.ELEMENT_ARRAY_BUFFER); if (indicesAccessorIndex < 0) { // https://github.com/vrm-c/UniVRM/issues/664 throw new Exception(); } if (j >= materials.Length) { Debug.LogWarningFormat("{0}.materials is not enough", unityMesh.Mesh.name); break; } gltfMesh.primitives.Add(new glTFPrimitives { attributes = attributes, indices = indicesAccessorIndex, mode = 4, // triangles ? material = unityMaterials.IndexOf(materials[j]) }); } var blendShapeIndexMap = new Dictionary <int, int>(); { var targetNames = new List <string>(); int exportBlendShapes = 0; for (int j = 0; j < unityMesh.Mesh.blendShapeCount; ++j) { var morphTarget = ExportMorphTarget(data, unityMesh.Mesh, j, settings.UseSparseAccessorForMorphTarget, settings.ExportOnlyBlendShapePosition, axisInverter); if (morphTarget.POSITION < 0) { // Skip empty blendShape. // Shift blendShape's index. continue; } var blendShapeName = unityMesh.Mesh.GetBlendShapeName(j); blendShapeIndexMap.Add(j, exportBlendShapes++); targetNames.Add(blendShapeName); // // all primitive has same blendShape // for (int k = 0; k < gltfMesh.primitives.Count; ++k) { gltfMesh.primitives[k].targets.Add(morphTarget); } } gltf_mesh_extras_targetNames.Serialize(gltfMesh, targetNames); } return(gltfMesh, blendShapeIndexMap); }
public virtual void Export(ITextureSerializer textureSerializer) { Nodes = Copy.transform.Traverse() .Skip(1) // exclude root object for the symmetry with the importer .ToList(); var uniqueUnityMeshes = new MeshExportList(); uniqueUnityMeshes.GetInfo(Nodes, m_settings); #region Materials and Textures ReportProgress("Materials and Textures", 0.2f); Materials = uniqueUnityMeshes.GetUniqueMaterials().ToList(); _textureExporter = new TextureExporter(textureSerializer); var materialExporter = CreateMaterialExporter(); _gltf.materials = Materials.Select(x => materialExporter.ExportMaterial(x, TextureExporter, m_settings)).ToList(); #endregion #region Meshes ReportProgress("Meshes", 0.4f); MeshBlendShapeIndexMap = new Dictionary <Mesh, Dictionary <int, int> >(); foreach (var unityMesh in uniqueUnityMeshes) { if (!unityMesh.CanExport) { continue; } var(gltfMesh, blendShapeIndexMap) = m_settings.DivideVertexBuffer ? MeshExporter_DividedVertexBuffer.Export(_data, unityMesh, Materials, m_settings.InverseAxis.Create(), m_settings) : MeshExporter_SharedVertexBuffer.Export(_data, unityMesh, Materials, m_settings.InverseAxis.Create(), m_settings) ; _gltf.meshes.Add(gltfMesh); Meshes.Add(unityMesh.Mesh); if (!MeshBlendShapeIndexMap.ContainsKey(unityMesh.Mesh)) { // 重複防止 MeshBlendShapeIndexMap.Add(unityMesh.Mesh, blendShapeIndexMap); } } #endregion #region Nodes and Skins ReportProgress("Nodes and Skins", 0.8f); var skins = uniqueUnityMeshes .SelectMany(x => x.Renderers) .Where(x => x.Item1 is SkinnedMeshRenderer && x.UniqueBones != null) .Select(x => x.Item1 as SkinnedMeshRenderer) .ToList() ; foreach (var node in Nodes) { var gltfNode = ExportNode(node, Nodes, uniqueUnityMeshes, skins); _gltf.nodes.Add(gltfNode); } _gltf.scenes = new List <gltfScene> { new gltfScene { nodes = Copy.transform.GetChildren().Select(x => Nodes.IndexOf(x)).ToArray(), } }; foreach (var x in uniqueUnityMeshes) { foreach (var(renderer, uniqueBones) in x.Renderers) { if (uniqueBones != null && renderer is SkinnedMeshRenderer smr) { var matrices = x.GetBindPoses().Select(m_settings.InverseAxis.Create().InvertMat4).ToArray(); var accessor = _data.ExtendBufferAndGetAccessorIndex(matrices, glBufferTarget.NONE); var skin = new glTFSkin { inverseBindMatrices = accessor, joints = uniqueBones.Select(y => Nodes.IndexOf(y)).ToArray(), skeleton = Nodes.IndexOf(smr.rootBone), }; var skinIndex = _gltf.skins.Count; _gltf.skins.Add(skin); foreach (var z in Nodes.Where(y => y.Has(renderer))) { var nodeIndex = Nodes.IndexOf(z); var node = _gltf.nodes[nodeIndex]; node.skin = skinIndex; } } } } #endregion if (m_animationExporter != null) { ReportProgress("Animations", 0.9f); m_animationExporter.Export(_data, Copy, Nodes); } ExportExtensions(textureSerializer); // Extension で Texture が増える場合があるので最後に呼ぶ var exported = _textureExporter.Export(); for (var exportedTextureIdx = 0; exportedTextureIdx < exported.Count; ++exportedTextureIdx) { var(unityTexture, colorSpace) = exported[exportedTextureIdx]; GltfTextureExporter.PushGltfTexture(_data, unityTexture, colorSpace, textureSerializer); } FixName(_gltf); }
public static gltfMorphTarget Export(ExportingGltfData data, Vector3[] positions, Vector3[] normals, bool useSparse) { var accessorCount = positions.Length; if (normals != null && positions.Length != normals.Length) { throw new Exception(); } int[] sparseIndices = default; if (useSparse) { sparseIndices = Enumerable.Range(0, positions.Length).Where(x => positions[x] != Vector3.zero).ToArray(); if (sparseIndices.Length == 0) { // sparse 対象がすべて [0, 0, 0] の場合 // new glTFSparse // { // count = 0, // } // のようになる。 // たぶん、仕様的にはあり。 // 解釈できない場合あり。 useSparse = false; } } if (useSparse) { if (sparseIndices == null) { throw new Exception(); } // positions var positionAccessorIndex = -1; if (sparseIndices.Length > 0) { var sparseIndicesViewIndex = data.ExtendBufferAndGetViewIndex(sparseIndices); positionAccessorIndex = data.ExtendSparseBufferAndGetAccessorIndex(accessorCount, sparseIndices.Select(x => positions[x]).ToArray(), sparseIndices, sparseIndicesViewIndex, glBufferTarget.NONE); } // normals var normalAccessorIndex = -1; if (normals != null) { var sparseNormalIndices = Enumerable.Range(0, positions.Length).Where(x => normals[x] != Vector3.zero).ToArray(); if (sparseNormalIndices.Length > 0) { var sparseNormalIndicesViewIndex = data.ExtendBufferAndGetViewIndex(sparseNormalIndices); normalAccessorIndex = data.ExtendSparseBufferAndGetAccessorIndex(accessorCount, sparseNormalIndices.Select(x => normals[x]).ToArray(), sparseNormalIndices, sparseNormalIndicesViewIndex, glBufferTarget.NONE); } } return(new gltfMorphTarget { POSITION = positionAccessorIndex, NORMAL = normalAccessorIndex, }); } else { // position var positionAccessorIndex = data.ExtendBufferAndGetAccessorIndex(positions, glBufferTarget.ARRAY_BUFFER); data.GLTF.accessors[positionAccessorIndex].min = positions.Aggregate(positions[0], (a, b) => new Vector3(Mathf.Min(a.x, b.x), Math.Min(a.y, b.y), Mathf.Min(a.z, b.z))).ToArray(); data.GLTF.accessors[positionAccessorIndex].max = positions.Aggregate(positions[0], (a, b) => new Vector3(Mathf.Max(a.x, b.x), Math.Max(a.y, b.y), Mathf.Max(a.z, b.z))).ToArray(); // normal var normalAccessorIndex = -1; if (normals != null) { normalAccessorIndex = data.ExtendBufferAndGetAccessorIndex(normals, glBufferTarget.ARRAY_BUFFER); } return(new gltfMorphTarget { POSITION = positionAccessorIndex, NORMAL = normalAccessorIndex, }); } }