public static void Main(string[] args) { switch (args[0]) { case "install": InstallArgs installArgs = new InstallArgs(args); InstallProcess installProcess = new InstallProcess(); installProcess.Execute(installArgs); break; case "delete": DeleteArgs deleteArgs = new DeleteArgs(args); DeleteProcess deleteProcess = new DeleteProcess(); deleteProcess.Execute(deleteArgs); break; case "export": var exportArgs = new ExportArgs(args); var exportProcess = new ExportProcess(); exportProcess.Execute(exportArgs); break; case "import": var importArgs = new ImportArgs(args); var importProcess = new ImportProcess(); importProcess.Execute(importArgs); break; } }
private void exportBtn_Click(object sender, EventArgs e) { var btn = (Button)sender; if (exportSaveFileDialog.ShowDialog() == DialogResult.OK) { btn.Text = "Cancel"; btn.Click -= exportBtn_Click; btn.Click += cancelBtn_Click; var args = new ExportArgs { Input = Stream, Filename = exportSaveFileDialog.FileName }; if (args.Input != null) { exportBackgroundWorker.RunWorkerAsync(args); } } }
public static byte[] Export(this Vrm10Exporter exporter, Model m, ExportArgs option) { exporter.ExportAsset(m); /// /// 必要な容量を先に確保 /// (sparseは考慮してないので大きめ) /// { var reserveBytes = 0; // mesh foreach (var g in m.MeshGroups) { foreach (var mesh in g.Meshes) { // 頂点バッファ reserveBytes += mesh.IndexBuffer.ByteLength; foreach (var kv in mesh.VertexBuffer) { reserveBytes += kv.Value.ByteLength; } // morph foreach (var morph in mesh.MorphTargets) { foreach (var kv in morph.VertexBuffer) { reserveBytes += kv.Value.ByteLength; } } } } exporter.Reserve(reserveBytes); } // mesh exporter.ExportMeshes(m.MeshGroups, m.Materials, option); // node exporter.ExportNodes(m.Root, m.Nodes, m.MeshGroups, option); return(exporter.ToBytes()); }
public static void Main(string[] args) { switch (args[0]) { case "install": InstallArgs installArgs = new InstallArgs(args); InstallProcess installProcess = new InstallProcess(); installProcess.Execute(installArgs); break; case "delete": DeleteArgs deleteArgs = new DeleteArgs(args); DeleteProcess deleteProcess = new DeleteProcess(); deleteProcess.Execute(deleteArgs); break; case "export": var exportArgs = new ExportArgs(args); var exportProcess = new ExportProcess(); exportProcess.Execute(exportArgs); break; } }
private static Options[] ParseOptions(ExportArgs args) { var result = new List <Options>(); if (!string.IsNullOrEmpty(args.Options)) { foreach (var file in args.Options.Split(';') .Select(x => x.Trim())) { var options = Options.Load(file); result.Add(options); } } if (!string.IsNullOrEmpty(args.Input) && !string.IsNullOrEmpty(args.Output) && !string.IsNullOrEmpty(args.Platform)) { var options = new Options(args.Platform, args.Output, new[] { args.Input }); result.Add(options); } return(result.ToArray()); }
static int ExportIndices(Vrm10Storage storage, BufferAccessor x, int offset, int count, ExportArgs option) { if (x.Count <= ushort.MaxValue) { if (x.ComponentType == AccessorValueType.UNSIGNED_INT) { // ensure ushort var src = x.GetSpan <UInt32>().Slice(offset, count); var bytes = new byte[src.Length * 2]; var dst = SpanLike.Wrap <UInt16>(new ArraySegment <byte>(bytes)); for (int i = 0; i < src.Length; ++i) { dst[i] = (ushort)src[i]; } var accessor = new BufferAccessor(new ArraySegment <byte>(bytes), AccessorValueType.UNSIGNED_SHORT, AccessorVectorType.SCALAR, count); return(accessor.AddAccessorTo(storage, 0, option.sparse, null, 0, count)); } else { return(x.AddAccessorTo(storage, 0, option.sparse, null, offset, count)); } } else { return(x.AddAccessorTo(storage, 0, option.sparse, null, offset, count)); } }
/// <summary> /// ModelExporter.Export で作られた Model.MeshGroups[*] を GLTF 化する /// </summary> /// <param name="src"></param> /// <param name="materials"></param> /// <param name="storage"></param> /// <param name="option"></param> /// <returns></returns> public static glTFMesh ExportMeshGroup(this MeshGroup src, List <object> materials, Vrm10Storage storage, 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, storage, 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 void Export(GameObject root, Model model, ModelExporter converter, ExportArgs option, gltfExporter.GetBytesWithMimeFromTexture2D getTextureBytes, VRM10MetaObject metaObject = null) { ExportAsset(model); /// /// 必要な容量を先に確保 /// (sparseは考慮してないので大きめ) /// { var reserveBytes = 0; // mesh foreach (var g in model.MeshGroups) { foreach (var mesh in g.Meshes) { // 頂点バッファ reserveBytes += mesh.IndexBuffer.ByteLength; foreach (var kv in mesh.VertexBuffer) { reserveBytes += kv.Value.ByteLength; } // morph foreach (var morph in mesh.MorphTargets) { foreach (var kv in morph.VertexBuffer) { reserveBytes += kv.Value.ByteLength; } } } } Reserve(reserveBytes); } // material var materialExporter = new Vrm10MaterialExporter(); foreach (Material material in model.Materials) { var glTFMaterial = materialExporter.ExportMaterial(material, m_textureExporter); Storage.Gltf.materials.Add(glTFMaterial); } // mesh ExportMeshes(model.MeshGroups, model.Materials, option); // node ExportNodes(model.Root, model.Nodes, model.MeshGroups, option); var(vrm, vrmSpringBone, thumbnailTextureIndex) = ExportVrm(root, model, converter, metaObject); // Extension で Texture が増える場合があるので最後に呼ぶ for (int i = 0; i < m_textureExporter.Exported.Count; ++i) { var(unityTexture, texColorSpace) = m_textureExporter.Exported[i]; Storage.Gltf.PushGltfTexture(0, unityTexture, texColorSpace, getTextureBytes); } if (thumbnailTextureIndex.HasValue) { vrm.Meta.ThumbnailImage = Storage.Gltf.textures[thumbnailTextureIndex.Value].source; } UniGLTF.Extensions.VRMC_vrm.GltfSerializer.SerializeTo(ref Storage.Gltf.extensions, vrm); if (vrmSpringBone != null) { UniGLTF.Extensions.VRMC_springBone.GltfSerializer.SerializeTo(ref Storage.Gltf.extensions, vrmSpringBone); } }
public void ExportNodes(Node root, List <Node> nodes, List <MeshGroup> groups, ExportArgs option) { foreach (var x in nodes) { var node = new glTFNode { name = x.Name, }; node.translation = x.LocalTranslation.ToFloat3(); node.rotation = x.LocalRotation.ToFloat4(); node.scale = x.LocalScaling.ToFloat3(); if (x.MeshGroup != null) { node.mesh = groups.IndexOfThrow(x.MeshGroup); var skin = x.MeshGroup.Skin; if (skin != null) { var skinIndex = Storage.Gltf.skins.Count; var gltfSkin = new glTFSkin() { joints = skin.Joints.Select(joint => nodes.IndexOfThrow(joint)).ToArray() }; if (skin.InverseMatrices == null) { skin.CalcInverseMatrices(); } if (skin.InverseMatrices != null) { gltfSkin.inverseBindMatrices = skin.InverseMatrices.AddAccessorTo(Storage, 0, option.sparse); } if (skin.Root != null) { gltfSkin.skeleton = nodes.IndexOf(skin.Root); } Storage.Gltf.skins.Add(gltfSkin); node.skin = skinIndex; } } node.children = x.Children.Select(child => nodes.IndexOfThrow(child)).ToArray(); Storage.Gltf.nodes.Add(node); } Storage.Gltf.scenes.Add(new gltfScene() { nodes = root.Children.Select(child => nodes.IndexOfThrow(child)).ToArray() }); }
public void ExportAnimations(List <Animation> animations, List <Node> nodes, ExportArgs option) { // throw new System.NotImplementedException(); }
public void ExportNodes(Node root, List <Node> nodes, List <MeshGroup> groups, ExportArgs option) { foreach (var x in nodes) { var node = new VrmProtobuf.Node { Name = x.Name, }; node.Translation.Add(x.LocalTranslation.X); node.Translation.Add(x.LocalTranslation.Y); node.Translation.Add(x.LocalTranslation.Z); node.Rotation.Add(x.LocalRotation.X); node.Rotation.Add(x.LocalRotation.Y); node.Rotation.Add(x.LocalRotation.Z); node.Rotation.Add(x.LocalRotation.W); node.Scale.Add(x.LocalScaling.X); node.Scale.Add(x.LocalScaling.Y); node.Scale.Add(x.LocalScaling.Z); if (x.MeshGroup != null) { node.Mesh = groups.IndexOfThrow(x.MeshGroup); var skin = x.MeshGroup.Skin; if (skin != null) { var skinIndex = Gltf.Skins.Count; var gltfSkin = new VrmProtobuf.Skin(); foreach (var joint in skin.Joints) { gltfSkin.Joints.Add(nodes.IndexOfThrow(joint)); } if (skin.InverseMatrices == null) { skin.CalcInverseMatrices(); } if (skin.InverseMatrices != null) { gltfSkin.InverseBindMatrices = skin.InverseMatrices.AddAccessorTo(Storage, 0, option.sparse); } if (skin.Root != null) { gltfSkin.Skeleton = nodes.IndexOfNullable(skin.Root); } Gltf.Skins.Add(gltfSkin); node.Skin = skinIndex; } } foreach (var child in x.Children) { node.Children.Add(nodes.IndexOfThrow(child)); } Gltf.Nodes.Add(node); } Gltf.Scenes.Add(new VrmProtobuf.Scene()); foreach (var child in root.Children) { Gltf.Scenes[0].Nodes.Add(nodes.IndexOfThrow(child)); } }
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)); } }
static void ExportMesh(this Mesh mesh, List <Material> materials, Vrm10Storage storage, VrmProtobuf.Mesh gltfMesh, ExportArgs option) { // // primitive share vertex buffer // var attributeAccessorIndexMap = mesh.VertexBuffer .ToDictionary( kv => kv.Key, kv => kv.Value.AddAccessorTo( storage, 0, option.sparse, kv.Key == VertexBuffer.PositionKey ? (Action <Memory <byte>, VrmProtobuf.Accessor>)Vec3MinMax : null ) ); List <Dictionary <string, int> > morphTargetAccessorIndexMapList = null; if (mesh.MorphTargets.Any()) { morphTargetAccessorIndexMapList = new List <Dictionary <string, int> >(); foreach (var morphTarget in mesh.MorphTargets) { var dict = new Dictionary <string, int>(); foreach (var kv in morphTarget.VertexBuffer) { if (option.removeTangent && kv.Key == VertexBuffer.TangentKey) { // remove tangent continue; } if (option.removeMorphNormal && kv.Key == VertexBuffer.NormalKey) { // normal normal continue; } if (kv.Value.Count != mesh.VertexBuffer.Count) { throw new Exception("inavlid data"); } var accessorIndex = kv.Value.AddAccessorTo(storage, 0, option.sparse, kv.Key == VertexBuffer.PositionKey ? (Action <Memory <byte>, VrmProtobuf.Accessor>)Vec3MinMax : null); dict.Add(kv.Key, accessorIndex); } morphTargetAccessorIndexMapList.Add(dict); } } var drawCountOffset = 0; foreach (var y in mesh.Submeshes) { // index // slide index buffer accessor var indicesAccessorIndex = ExportIndices(storage, mesh.IndexBuffer, drawCountOffset, y.DrawCount, option); drawCountOffset += y.DrawCount; var prim = new VrmProtobuf.MeshPrimitive { Mode = (int)mesh.Topology, Material = materials.IndexOfNullable(y.Material), Indices = indicesAccessorIndex, }; gltfMesh.Primitives.Add(prim); // attribute foreach (var kv in mesh.VertexBuffer) { var attributeAccessorIndex = attributeAccessorIndexMap[kv.Key]; prim.Attributes.Add(kv.Key, attributeAccessorIndex); } // morph target if (mesh.MorphTargets.Any()) { foreach (var(t, accessorIndexMap) in Enumerable.Zip(mesh.MorphTargets, morphTargetAccessorIndexMapList, (t, v) => (t, v))) { var target = new VrmProtobuf.target(); prim.Targets.Add(target); foreach (var kv in t.VertexBuffer) { if (!accessorIndexMap.TryGetValue(kv.Key, out int targetAccessorIndex)) { continue; } switch (kv.Key) { case VertexBuffer.PositionKey: target.POSITION = targetAccessorIndex; break; case VertexBuffer.NormalKey: target.NORMAL = targetAccessorIndex; break; case VertexBuffer.TangentKey: target.TANGENT = targetAccessorIndex; break; default: throw new NotImplementedException(); } } } if (mesh.MorphTargets.Any()) { prim.Extras = new VrmProtobuf.MeshPrimitive.Types.Extras(); foreach (var name in mesh.MorphTargets.Select(z => z.Name)) { prim.Extras.TargetNames.Add(name); } } } } }
/// <summary> /// https://github.com/vrm-c/UniVRM/issues/800 /// /// SubMesh 単位に分割する。 /// SubMesh を Gltf の Primitive に対応させる。 /// </summary> /// <param name="mesh"></param> /// <param name="materials"></param> /// <param name="storage"></param> /// <param name="gltfMesh"></param> /// <param name="option"></param> static IEnumerable <glTFPrimitives> ExportMeshDivided(this VrmLib.Mesh mesh, List <object> materials, Vrm10Storage storage, ExportArgs option) { var bufferIndex = 0; var usedIndices = new List <int>(); var meshIndices = SpanLike.CopyFrom(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.Slice(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.Push(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.Push(boneWeight); } } } var materialIndex = submesh.Material; var gltfPrimitive = buffer.ToGltfPrimitive(storage.Gltf, bufferIndex, materialIndex, indices); // blendShape for (int j = 0; j < mesh.MorphTargets.Count; ++j) { var blendShape = new MeshExportUtil.BlendShapeBuffer(indices.Length); // index の順に attributes を蓄える var morph = mesh.MorphTargets[j]; var blendShapePositions = morph.VertexBuffer.Positions.GetSpan <UnityEngine.Vector3>(); SpanLike <UnityEngine.Vector3>?blendShapeNormals = default; if (morph.VertexBuffer.Normals != null) { blendShapeNormals = morph.VertexBuffer.Normals.GetSpan <UnityEngine.Vector3>(); } foreach (var k in usedIndices) { blendShape.Push( blendShapePositions[k], blendShapeNormals.HasValue ? blendShapeNormals.Value[k] : UnityEngine.Vector3.zero ); } gltfPrimitive.targets.Add(blendShape.ToGltf(storage.Gltf, bufferIndex, !option.removeMorphNormal)); } yield return(gltfPrimitive); } }
public void Export(GameObject root, Model model, ModelExporter converter, ExportArgs option, Func <Texture2D, (byte[], string)> getTextureBytes, VRM10MetaObject metaObject = null)
static void ExportMesh(this Mesh mesh, List <object> materials, Vrm10Storage storage, glTFMesh gltfMesh, ExportArgs option) { // // primitive share vertex buffer // var attributeAccessorIndexMap = mesh.VertexBuffer .ToDictionary( kv => kv.Key, kv => kv.Value.AddAccessorTo( storage, 0, option.sparse, kv.Key == VertexBuffer.PositionKey ? (Action <ArraySegment <byte>, glTFAccessor>)Vec3MinMax : null ) ); List <Dictionary <string, int> > morphTargetAccessorIndexMapList = null; if (mesh.MorphTargets.Any()) { morphTargetAccessorIndexMapList = new List <Dictionary <string, int> >(); foreach (var morphTarget in mesh.MorphTargets) { var dict = new Dictionary <string, int>(); foreach (var kv in morphTarget.VertexBuffer) { if (option.removeTangent && kv.Key == VertexBuffer.TangentKey) { // remove tangent continue; } if (option.removeMorphNormal && kv.Key == VertexBuffer.NormalKey) { // normal normal continue; } if (kv.Value.Count != mesh.VertexBuffer.Count) { throw new Exception("inavlid data"); } var accessorIndex = kv.Value.AddAccessorTo(storage, 0, option.sparse, kv.Key == VertexBuffer.PositionKey ? (Action <ArraySegment <byte>, glTFAccessor>)Vec3MinMax : null); dict.Add(kv.Key, accessorIndex); } morphTargetAccessorIndexMapList.Add(dict); } } var drawCountOffset = 0; foreach (var y in mesh.Submeshes) { // index // slide index buffer accessor var indicesAccessorIndex = ExportIndices(storage, mesh.IndexBuffer, drawCountOffset, y.DrawCount, option); drawCountOffset += y.DrawCount; var prim = new glTFPrimitives { mode = (int)mesh.Topology, material = y.Material, indices = indicesAccessorIndex, attributes = new glTFAttributes(), }; gltfMesh.primitives.Add(prim); // attribute foreach (var kv in mesh.VertexBuffer) { var attributeAccessorIndex = attributeAccessorIndexMap[kv.Key]; switch (kv.Key) { case VertexBuffer.PositionKey: prim.attributes.POSITION = attributeAccessorIndex; break; case VertexBuffer.NormalKey: prim.attributes.NORMAL = attributeAccessorIndex; break; case VertexBuffer.ColorKey: prim.attributes.COLOR_0 = attributeAccessorIndex; break; case VertexBuffer.TexCoordKey: prim.attributes.TEXCOORD_0 = attributeAccessorIndex; break; case VertexBuffer.TexCoordKey2: prim.attributes.TEXCOORD_1 = attributeAccessorIndex; break; case VertexBuffer.JointKey: prim.attributes.JOINTS_0 = attributeAccessorIndex; break; case VertexBuffer.WeightKey: prim.attributes.WEIGHTS_0 = attributeAccessorIndex; break; } } // morph target if (mesh.MorphTargets.Any()) { foreach (var(t, accessorIndexMap) in Enumerable.Zip(mesh.MorphTargets, morphTargetAccessorIndexMapList, (t, v) => (t, v))) { var target = new gltfMorphTarget(); prim.targets.Add(target); foreach (var kv in t.VertexBuffer) { if (!accessorIndexMap.TryGetValue(kv.Key, out int targetAccessorIndex)) { continue; } switch (kv.Key) { case VertexBuffer.PositionKey: target.POSITION = targetAccessorIndex; break; case VertexBuffer.NormalKey: target.NORMAL = targetAccessorIndex; break; case VertexBuffer.TangentKey: target.TANGENT = targetAccessorIndex; break; default: throw new NotImplementedException(); } } } } } // target name if (mesh.MorphTargets.Any()) { gltf_mesh_extras_targetNames.Serialize(gltfMesh, mesh.MorphTargets.Select(z => z.Name)); } }
/// <summary> /// Converts a dds stream to another texture file type and writes it to file /// </summary> /// <param name="ms">The input dds stream</param> /// <param name="outfilename">The output filename. Extension will be overwritten with the correct filetype</param> /// <param name="args"></param> /// <returns></returns> public static unsafe bool ConvertFromDdsAndSave(Stream ms, string outfilename, ExportArgs args) { // check if stream is dds if (!DDSUtils.IsDdsFile(ms)) { throw new ArgumentException("Input stream not a dds file", nameof(ms)); } // get arguments var uext = EUncookExtension.dds; var vflip = false; if (args is not XbmExportArgs and not MlmaskExportArgs) { return(false); } if (args is XbmExportArgs xbm) { uext = xbm.UncookExtension; vflip = xbm.Flip; } if (args is MlmaskExportArgs ml) { uext = ml.UncookExtension; } if (uext == EUncookExtension.dds) { return(false); } return(ConvertFromDdsAndSave(ms, outfilename, (DirectXTexSharp.ESaveFileTypes)uext, vflip)); }
public static glTFMesh ExportMeshGroup(this MeshGroup src, List <object> materials, Vrm10Storage storage, ExportArgs option) { var mesh = new glTFMesh { name = src.Name }; foreach (var x in src.Meshes) { // MeshとSubmeshがGltfのPrimitiveに相当する? x.ExportMesh(materials, storage, mesh, option); } return(mesh); }