public VRMExporter(ExportingGltfData data, GltfExportSettings exportSettings) : base(data, exportSettings) { if (exportSettings == null || exportSettings.InverseAxis != Vrm0xSpecificationInverseAxis) { throw new Exception($"VRM specification requires InverseAxis settings as {Vrm0xSpecificationInverseAxis}"); } _gltf.extensionsUsed.Add(glTF_VRM_extensions.ExtensionName); }
public static ExportingGltfData Export(GltfExportSettings configuration, GameObject go, ITextureSerializer textureSerializer) { var data = new ExportingGltfData(); using (var exporter = new VRMExporter(data, configuration)) { exporter.Prepare(go); exporter.Export(textureSerializer); } return data; }
/// <summary> /// https://github.com/vrm-c/UniVRM/issues/800 /// /// SubMesh 単位に分割する。 /// SubMesh を Gltf の Primitive に対応させる。 /// </summary> /// <param name="mesh"></param> /// <param name="materials"></param> /// <param name="data"></param> /// <param name="gltfMesh"></param> /// <param name="option"></param> static IEnumerable <glTFPrimitives> ExportMeshDivided(this VrmLib.Mesh mesh, List <object> materials, ExportingGltfData writer, ExportArgs option) { var usedIndices = new List <int>(); var meshIndices = mesh.IndexBuffer.GetAsIntArray(); var positions = mesh.VertexBuffer.Positions.GetSpan <UnityEngine.Vector3>().ToArray(); var normals = mesh.VertexBuffer.Normals.GetSpan <UnityEngine.Vector3>().ToArray(); var uv = mesh.VertexBuffer.TexCoords.GetSpan <UnityEngine.Vector2>().ToArray(); var hasSkin = mesh.VertexBuffer.Weights != null; var weights = mesh.VertexBuffer.Weights?.GetSpan <UnityEngine.Vector4>().ToArray(); var joints = mesh.VertexBuffer.Joints?.GetSpan <SkinJoints>().ToArray(); Func <int, int> getJointIndex = default; if (hasSkin) { getJointIndex = i => { return(i); }; } foreach (var submesh in mesh.Submeshes) { var indices = meshIndices.GetSubArray(submesh.Offset, submesh.DrawCount).ToArray(); var hash = new HashSet <int>(indices); // mesh // index の順に attributes を蓄える var buffer = new MeshExportUtil.VertexBuffer(indices.Length, getJointIndex); usedIndices.Clear(); for (int k = 0; k < positions.Length; ++k) { if (hash.Contains(k)) { // indices から参照される頂点だけを蓄える usedIndices.Add(k); buffer.PushVertex(k, positions[k], normals[k], uv[k]); if (getJointIndex != null) { var j = joints[k]; var w = weights[k]; var boneWeight = new UnityEngine.BoneWeight { boneIndex0 = j.Joint0, boneIndex1 = j.Joint1, boneIndex2 = j.Joint2, boneIndex3 = j.Joint3, weight0 = w.x, weight1 = w.y, weight2 = w.z, weight3 = w.w, }; buffer.PushBoneWeight(boneWeight); } } } var materialIndex = submesh.Material; var gltfPrimitive = buffer.ToGltfPrimitive(writer, materialIndex, indices); // blendShape for (int j = 0; j < mesh.MorphTargets.Count; ++j) { var blendShape = new MeshExportUtil.BlendShapeBuffer(usedIndices.Count); // index の順に attributes を蓄える var morph = mesh.MorphTargets[j]; var blendShapePositions = morph.VertexBuffer.Positions.GetSpan <UnityEngine.Vector3>(); NativeArray <UnityEngine.Vector3>?blendShapeNormals = default; if (morph.VertexBuffer.Normals != null) { blendShapeNormals = morph.VertexBuffer.Normals.GetSpan <UnityEngine.Vector3>(); } int l = 0; foreach (var k in usedIndices) { blendShape.Set(l++, blendShapePositions[k], blendShapeNormals.HasValue ? blendShapeNormals.Value[k] : UnityEngine.Vector3.zero ); } gltfPrimitive.targets.Add(blendShape.ToGltf(writer, !option.removeMorphNormal, option.sparse)); } yield return(gltfPrimitive); } }
/// <summary> /// ModelExporter.Export で作られた Model.MeshGroups[*] を GLTF 化する /// </summary> /// <param name="src"></param> /// <param name="materials"></param> /// <param name="data"></param> /// <param name="option"></param> /// <returns></returns> public static glTFMesh ExportMeshGroup(this MeshGroup src, List <object> materials, ExportingGltfData writer, ExportArgs option) { var gltfMesh = new glTFMesh { name = src.Name }; if (src.Meshes.Count != 1) { throw new NotImplementedException(); } foreach (var prim in src.Meshes[0].ExportMeshDivided(materials, writer, option)) { gltfMesh.primitives.Add(prim); } var targetNames = src.Meshes[0].MorphTargets.Select(x => x.Name).ToArray(); gltf_mesh_extras_targetNames.Serialize(gltfMesh, targetNames); return(gltfMesh); }
public static IEnumerable <(glTFNode, glTFSkin)> ExportNodes(INativeArrayManager arrayManager, List <Node> nodes, List <MeshGroup> groups, ExportingGltfData data, ExportArgs option) { foreach (var node in nodes) { var gltfNode = new glTFNode { name = node.Name, }; glTFSkin gltfSkin = default; gltfNode.translation = node.LocalTranslation.ToFloat3(); gltfNode.rotation = node.LocalRotation.ToFloat4(); gltfNode.scale = node.LocalScaling.ToFloat3(); if (node.MeshGroup != null) { gltfNode.mesh = groups.IndexOfThrow(node.MeshGroup); var skin = node.MeshGroup.Skin; if (skin != null) { gltfSkin = new glTFSkin() { joints = skin.Joints.Select(joint => nodes.IndexOfThrow(joint)).ToArray() }; if (skin.InverseMatrices == null) { skin.CalcInverseMatrices(arrayManager); } if (skin.InverseMatrices != null) { gltfSkin.inverseBindMatrices = skin.InverseMatrices.AddAccessorTo(data, 0, option.sparse); } if (skin.Root != null) { gltfSkin.skeleton = nodes.IndexOf(skin.Root); } } } gltfNode.children = node.Children.Select(child => nodes.IndexOfThrow(child)).ToArray(); yield return(gltfNode, gltfSkin); } }
public static IEnumerable <glTFMesh> ExportMeshes(List <MeshGroup> groups, List <object> materials, ExportingGltfData data, ExportArgs option) { foreach (var group in groups) { yield return(group.ExportMeshGroup(materials, data, option)); } }