public TexturePacker(ITextureSerializer serializer, float uvInset, bool square, int sizeLimit, int unitSize, int padding) { if (serializer is null) { throw new ArgumentNullException(nameof(serializer)); } if (uvInset < 0 || uvInset > .5f) { throw new ArgumentException("UV inset should be in 0 to 0.5 range."); } if (sizeLimit < 1) { throw new ArgumentException("Size limit should be greater than zero."); } if (unitSize < 1 || unitSize > sizeLimit) { throw new ArgumentException("Unit size should be in 1 to size limit range."); } if (padding < 0 || padding > unitSize) { throw new ArgumentException("Padding should be in 0 to unit size range."); } this.serializer = serializer; this.uvInset = uvInset; this.square = square; this.sizeLimit = sizeLimit; this.unitSize = unitSize; this.padding = padding; paddedUnitSize = unitSize + padding * 2; unitsPerAtlasLimit = Mathf.FloorToInt(Mathf.Pow(Mathf.FloorToInt(sizeLimit / (float)paddedUnitSize), 2)); }
public static glTF Export(GltfExportSettings configuration, GameObject go, ITextureSerializer textureSerializer) { var gltf = new glTF(); using (var exporter = new VRMExporter(gltf)) { exporter.Prepare(go); exporter.Export(configuration, textureSerializer); } return(gltf); }
public Vrm10Exporter(ITextureSerializer textureSerializer, GltfExportSettings settings) { m_settings = settings; if (textureSerializer == null) { throw new ArgumentException(nameof(textureSerializer)); } Storage.Gltf.extensionsUsed.Add(glTF_KHR_texture_transform.ExtensionName); Storage.Gltf.extensionsUsed.Add(UniGLTF.Extensions.VRMC_vrm.VRMC_vrm.ExtensionName); Storage.Gltf.extensionsUsed.Add(glTF_KHR_materials_unlit.ExtensionName); Storage.Gltf.extensionsUsed.Add(UniGLTF.Extensions.VRMC_materials_mtoon.VRMC_materials_mtoon.ExtensionName); Storage.Gltf.extensionsUsed.Add(UniGLTF.Extensions.VRMC_springBone.VRMC_springBone.ExtensionName); Storage.Gltf.extensionsUsed.Add(UniGLTF.Extensions.VRMC_node_constraint.VRMC_node_constraint.ExtensionName); m_textureSerializer = textureSerializer; m_textureExporter = new TextureExporter(m_textureSerializer); }
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); }
public virtual void ExportExtensions(ITextureSerializer textureSerializer) { // do nothing }
/// <summary> /// gltf に texture を足す /// /// * textures /// * samplers /// * images /// * bufferViews /// /// を更新し、textures の index を返す /// /// もっとも根本の Exporter クラスのみが呼び出すべきである。 /// 他の拡張機能などが呼び出すべきではない。 /// /// </summary> /// <returns>gltf texture index</returns> public static int PushGltfTexture(ExportingGltfData data, Texture2D texture, ColorSpace textureColorSpace, ITextureSerializer textureSerializer) { var bytesWithMime = textureSerializer.ExportBytesWithMime(texture, textureColorSpace); // add view var viewIndex = data.ExtendBufferAndGetViewIndex(bytesWithMime.bytes); // add image var imageIndex = data.GLTF.images.Count; data.GLTF.images.Add(new glTFImage { name = TextureImportName.RemoveSuffix(texture.name), bufferView = viewIndex, mimeType = bytesWithMime.mime, }); // add sampler var samplerIndex = data.GLTF.samplers.Count; var sampler = TextureSamplerUtil.Export(texture); data.GLTF.samplers.Add(sampler); // add texture var textureIndex = data.GLTF.textures.Count; data.GLTF.textures.Add(new glTFTexture { sampler = samplerIndex, source = imageIndex, }); return(textureIndex); }
public TextureExporter(ITextureSerializer textureSerializer) { m_textureSerializer = textureSerializer; }
public virtual void Export(MeshExportSettings meshExportSettings, ITextureSerializer textureSerializer) { var bytesBuffer = new ArrayByteBuffer(new byte[50 * 1024 * 1024]); var bufferIndex = glTF.AddBuffer(bytesBuffer); Nodes = Copy.transform.Traverse() .Skip(1) // exclude root object for the symmetry with the importer .ToList(); var unityMeshes = MeshWithRenderer.FromNodes(Nodes).Where(x => x.Mesh.vertices.Any()).ToList(); var uniqueUnityMeshes = new List <MeshWithRenderer>(); foreach (var um in unityMeshes) { if (!uniqueUnityMeshes.Any(x => x.IsSameMeshAndMaterials(um))) { uniqueUnityMeshes.Add(um); } } #region Materials and Textures Materials = uniqueUnityMeshes.SelectMany(x => x.Renderer.sharedMaterials).Where(x => x != null).Distinct().ToList(); TextureExporter = new TextureExporter(textureSerializer); var materialExporter = CreateMaterialExporter(); glTF.materials = Materials.Select(x => materialExporter.ExportMaterial(x, TextureExporter)).ToList(); #endregion #region Meshes MeshBlendShapeIndexMap = new Dictionary <Mesh, Dictionary <int, int> >(); foreach (var unityMesh in uniqueUnityMeshes) { var(gltfMesh, blendShapeIndexMap) = MeshExporter.ExportMesh(glTF, bufferIndex, unityMesh, Materials, meshExportSettings, m_axisInverter); glTF.meshes.Add(gltfMesh); Meshes.Add(unityMesh.Mesh); if (!MeshBlendShapeIndexMap.ContainsKey(unityMesh.Mesh)) { // 同じmeshが複数回現れた MeshBlendShapeIndexMap.Add(unityMesh.Mesh, blendShapeIndexMap); } } #endregion #region Nodes and Skins var unitySkins = uniqueUnityMeshes .Where(x => x.UniqueBones != null) .ToList(); glTF.nodes = Nodes.Select(x => ExportNode(x, Nodes, uniqueUnityMeshes, unitySkins.Select(y => y.Renderer as SkinnedMeshRenderer).ToList())).ToList(); glTF.scenes = new List <gltfScene> { new gltfScene { nodes = Copy.transform.GetChildren().Select(x => Nodes.IndexOf(x)).ToArray(), } }; foreach (var x in unitySkins) { var matrices = x.GetBindPoses().Select(m_axisInverter.InvertMat4).ToArray(); var accessor = glTF.ExtendBufferAndGetAccessorIndex(bufferIndex, matrices, glBufferTarget.NONE); var renderer = x.Renderer as SkinnedMeshRenderer; var skin = new glTFSkin { inverseBindMatrices = accessor, joints = x.UniqueBones.Select(y => Nodes.IndexOf(y)).ToArray(), skeleton = Nodes.IndexOf(renderer.rootBone), }; var skinIndex = glTF.skins.Count; glTF.skins.Add(skin); foreach (var z in Nodes.Where(y => y.Has(x.Renderer))) { var nodeIndex = Nodes.IndexOf(z); var node = glTF.nodes[nodeIndex]; node.skin = skinIndex; } } #endregion #if UNITY_EDITOR #region Animations 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 = glTF.ExtendBufferAndGetAccessorIndex(bufferIndex, kv.Value.Input); sampler.input = inputAccessorIndex; var outputAccessorIndex = glTF.ExtendBufferAndGetAccessorIndex(bufferIndex, 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 が増える場合があるので最後に呼ぶ for (int i = 0; i < TextureExporter.Exported.Count; ++i) { var(unityTexture, colorSpace) = TextureExporter.Exported[i]; glTF.PushGltfTexture(bufferIndex, unityTexture, colorSpace, textureSerializer); } }
public override void ExportExtensions(ITextureSerializer textureSerializer) { // avatar var animator = Copy.GetComponent <Animator>(); if (animator != null) { var humanoid = Copy.GetComponent <VRMHumanoidDescription>(); UniHumanoid.AvatarDescription description = null; var nodes = Copy.transform.Traverse().Skip(1).ToList(); { var isCreated = false; if (humanoid != null) { description = humanoid.GetDescription(out isCreated); } if (description != null) { // use description VRM.humanoid.Apply(description, nodes); } if (isCreated) { GameObject.DestroyImmediate(description); } } { // set humanoid bone mapping var avatar = animator.avatar; foreach (HumanBodyBones key in Enum.GetValues(typeof(HumanBodyBones))) { if (key == HumanBodyBones.LastBone) { break; } var transform = animator.GetBoneTransform(key); if (transform != null) { VRM.humanoid.SetNodeIndex(key, nodes.IndexOf(transform)); } } } } // morph var master = Copy.GetComponent <VRMBlendShapeProxy>(); if (master != null) { var avatar = master.BlendShapeAvatar; if (avatar != null) { foreach (var x in avatar.Clips) { VRM.blendShapeMaster.Add(x, this); } } } // secondary VRMSpringUtility.ExportSecondary(Copy.transform, Nodes, x => VRM.secondaryAnimation.colliderGroups.Add(x), x => VRM.secondaryAnimation.boneGroups.Add(x) ); #pragma warning disable 0618 // meta(obsolete) { var meta = Copy.GetComponent <VRMMetaInformation>(); if (meta != null) { VRM.meta.author = meta.Author; VRM.meta.contactInformation = meta.ContactInformation; VRM.meta.title = meta.Title; if (meta.Thumbnail != null) { VRM.meta.texture = glTF.PushGltfTexture(glTF.buffers.Count - 1, meta.Thumbnail, ColorSpace.sRGB, textureSerializer); } VRM.meta.licenseType = meta.LicenseType; VRM.meta.otherLicenseUrl = meta.OtherLicenseUrl; VRM.meta.reference = meta.Reference; } } #pragma warning restore 0618 // meta { var _meta = Copy.GetComponent <VRMMeta>(); if (_meta != null && _meta.Meta != null) { var meta = _meta.Meta; // info VRM.meta.version = meta.Version; VRM.meta.author = meta.Author; VRM.meta.contactInformation = meta.ContactInformation; VRM.meta.reference = meta.Reference; VRM.meta.title = meta.Title; if (meta.Thumbnail != null) { VRM.meta.texture = TextureExporter.RegisterExportingAsSRgb(meta.Thumbnail, needsAlpha: true); } // ussage permission VRM.meta.allowedUser = meta.AllowedUser; VRM.meta.violentUssage = meta.ViolentUssage; VRM.meta.sexualUssage = meta.SexualUssage; VRM.meta.commercialUssage = meta.CommercialUssage; VRM.meta.otherPermissionUrl = meta.OtherPermissionUrl; // distribution license VRM.meta.licenseType = meta.LicenseType; if (meta.LicenseType == LicenseType.Other) { VRM.meta.otherLicenseUrl = meta.OtherLicenseUrl; } } } // firstPerson var firstPerson = Copy.GetComponent <VRMFirstPerson>(); if (firstPerson != null) { if (firstPerson.FirstPersonBone != null) { VRM.firstPerson.firstPersonBone = Nodes.IndexOf(firstPerson.FirstPersonBone); VRM.firstPerson.firstPersonBoneOffset = firstPerson.FirstPersonOffset; VRM.firstPerson.meshAnnotations = firstPerson.Renderers.Select(x => new glTF_VRM_MeshAnnotation { mesh = Meshes.IndexOf(x.SharedMesh), firstPersonFlag = x.FirstPersonFlag.ToString(), }).ToList(); } // lookAt { var lookAtHead = Copy.GetComponent <VRMLookAtHead>(); if (lookAtHead != null) { var boneApplyer = Copy.GetComponent <VRMLookAtBoneApplyer>(); var blendShapeApplyer = Copy.GetComponent <VRMLookAtBlendShapeApplyer>(); if (boneApplyer != null) { VRM.firstPerson.lookAtType = LookAtType.Bone; VRM.firstPerson.lookAtHorizontalInner.Apply(boneApplyer.HorizontalInner); VRM.firstPerson.lookAtHorizontalOuter.Apply(boneApplyer.HorizontalOuter); VRM.firstPerson.lookAtVerticalDown.Apply(boneApplyer.VerticalDown); VRM.firstPerson.lookAtVerticalUp.Apply(boneApplyer.VerticalUp); } else if (blendShapeApplyer != null) { VRM.firstPerson.lookAtType = LookAtType.BlendShape; VRM.firstPerson.lookAtHorizontalOuter.Apply(blendShapeApplyer.Horizontal); VRM.firstPerson.lookAtVerticalDown.Apply(blendShapeApplyer.VerticalDown); VRM.firstPerson.lookAtVerticalUp.Apply(blendShapeApplyer.VerticalUp); } } } } // materials foreach (var m in Materials) { VRM.materialProperties.Add(VRMMaterialExporter.CreateFromMaterial(m, TextureExporter)); } // Serialize VRM var f = new JsonFormatter(); VRMSerializer.Serialize(f, VRM); var bytes = f.GetStoreBytes(); glTFExtensionExport.GetOrCreate(ref glTF.extensions).Add("VRM", bytes); }
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; }
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); }
/// <summary> /// gltf に texture を足す /// /// * textures /// * samplers /// * images /// * bufferViews /// /// を更新し、textures の index を返す /// /// </summary> /// <param name="gltf"></param> /// <param name="bufferIndex"></param> /// <param name="texture"></param> /// <returns>gltf texture index</returns> public static int PushGltfTexture(this glTF gltf, int bufferIndex, Texture2D texture, ColorSpace textureColorSpace, ITextureSerializer textureSerializer) { var bytesWithMime = textureSerializer.ExportBytesWithMime(texture, textureColorSpace); // add view var view = gltf.buffers[bufferIndex].Append(bytesWithMime.bytes, glBufferTarget.NONE); var viewIndex = gltf.AddBufferView(view); // add image var imageIndex = gltf.images.Count; gltf.images.Add(new glTFImage { name = TextureImportName.RemoveSuffix(texture.name), bufferView = viewIndex, mimeType = bytesWithMime.mime, }); // add sampler var samplerIndex = gltf.samplers.Count; var sampler = TextureSamplerUtil.Export(texture); gltf.samplers.Add(sampler); // add texture var textureIndex = gltf.textures.Count; gltf.textures.Add(new glTFTexture { sampler = samplerIndex, source = imageIndex, }); return(textureIndex); }