您最多选择25个主题
主题必须以中文或者字母或数字开头,可以包含连字符 (-),并且长度不得超过35个字符
808 行
40 KiB
808 行
40 KiB
using System;
|
|
using System.Collections.Generic;
|
|
using System.Linq;
|
|
using UniGLTF;
|
|
using UnityEngine;
|
|
using VrmLib;
|
|
using VRMShaders;
|
|
|
|
|
|
namespace UniVRM10
|
|
{
|
|
public class Vrm10Exporter : IDisposable
|
|
{
|
|
public const string VRM_SPEC_VERSION = "1.0";
|
|
public const string SPRINGBONE_SPEC_VERSION = "1.0";
|
|
public const string NODE_CONSTRAINT_SPEC_VERSION = "1.0";
|
|
public const string MTOON_SPEC_VERSION = "1.0";
|
|
|
|
public const string LICENSE_URL_JA = "https://vrm.dev/licenses/1.0/";
|
|
public const string LICENSE_URL_EN = "https://vrm.dev/licenses/1.0/en/";
|
|
|
|
public readonly ExportingGltfData Storage = new ExportingGltfData();
|
|
|
|
public readonly string VrmExtensionName = "VRMC_vrm";
|
|
|
|
ITextureSerializer m_textureSerializer;
|
|
TextureExporter m_textureExporter;
|
|
|
|
GltfExportSettings m_settings;
|
|
public AssetBundleManifest BundleManifest{get;set;}
|
|
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 void Dispose()
|
|
{
|
|
m_textureExporter.Dispose();
|
|
}
|
|
|
|
public static glTFAssets ExportAsset(Model model)
|
|
{
|
|
var asset = new glTFAssets();
|
|
if (!string.IsNullOrEmpty(model.AssetVersion)) asset.version = model.AssetVersion;
|
|
if (!string.IsNullOrEmpty(model.AssetMinVersion)) asset.minVersion = model.AssetMinVersion;
|
|
if (!string.IsNullOrEmpty(model.AssetGenerator)) asset.generator = model.AssetGenerator;
|
|
if (!string.IsNullOrEmpty(model.AssetCopyright)) asset.copyright = model.AssetCopyright;
|
|
return asset;
|
|
}
|
|
|
|
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);
|
|
}
|
|
}
|
|
|
|
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);
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// revere X
|
|
/// </summary>
|
|
/// <param name="v"></param>
|
|
/// <returns></returns>
|
|
static float[] ReverseX(Vector3 v)
|
|
{
|
|
return new float[] { -v.x, v.y, v.z };
|
|
}
|
|
|
|
///
|
|
/// 必要な容量を計算
|
|
/// (sparseは考慮してないので大きめ)
|
|
static int CalcReserveBytes(Model model)
|
|
{
|
|
int 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;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return reserveBytes;
|
|
}
|
|
|
|
static IEnumerable<glTFMaterial> ExportMaterials(Model model, ITextureExporter textureExporter, GltfExportSettings settings,AssetBundleManifest bundleManifest)
|
|
{
|
|
if(settings.useCustomShader)
|
|
{
|
|
var materialExporter = new CustomMaterialExporter();
|
|
foreach (Material material in model.Materials)
|
|
{
|
|
yield return materialExporter.ExportMaterial(material, textureExporter, settings,bundleManifest);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
var materialExporter = new BuiltInVrm10MaterialExporter();
|
|
foreach (Material material in model.Materials)
|
|
{
|
|
yield return materialExporter.ExportMaterial(material, textureExporter, settings);
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
public void Export(GameObject root, Model model, ModelExporter converter, ExportArgs option, VRM10ObjectMeta vrmMeta = null)
|
|
{
|
|
Storage.Gltf.asset = ExportAsset(model);
|
|
|
|
Storage.Reserve(CalcReserveBytes(model));
|
|
|
|
foreach (var material in ExportMaterials(model, m_textureExporter, m_settings,BundleManifest))
|
|
{
|
|
Storage.Gltf.materials.Add(material);
|
|
}
|
|
|
|
foreach (var mesh in ExportMeshes(model.MeshGroups, model.Materials, Storage, option))
|
|
{
|
|
Storage.Gltf.meshes.Add(mesh);
|
|
}
|
|
|
|
using (var arrayManager = new NativeArrayManager())
|
|
{
|
|
foreach (var (node, skin) in ExportNodes(arrayManager, model.Nodes, model.MeshGroups, Storage, option))
|
|
{
|
|
Storage.Gltf.nodes.Add(node);
|
|
if (skin != null)
|
|
{
|
|
var skinIndex = Storage.Gltf.skins.Count;
|
|
Storage.Gltf.skins.Add(skin);
|
|
node.skin = skinIndex;
|
|
}
|
|
}
|
|
}
|
|
Storage.Gltf.scenes.Add(new gltfScene()
|
|
{
|
|
nodes = model.Root.Children.Select(child => model.Nodes.IndexOfThrow(child)).ToArray()
|
|
});
|
|
|
|
var (vrm, vrmSpringBone, thumbnailTextureIndex) = ExportVrm(root, model, converter, vrmMeta, Storage.Gltf.nodes, m_textureExporter);
|
|
|
|
// Extension で Texture が増える場合があるので最後に呼ぶ
|
|
var exportedTextures = m_textureExporter.Export();
|
|
for (var exportedTextureIdx = 0; exportedTextureIdx < exportedTextures.Count; ++exportedTextureIdx)
|
|
{
|
|
var (unityTexture, texColorSpace) = exportedTextures[exportedTextureIdx];
|
|
GltfTextureExporter.PushGltfTexture(Storage, unityTexture, texColorSpace, m_textureSerializer);
|
|
}
|
|
|
|
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);
|
|
}
|
|
|
|
// Fix Duplicated name
|
|
gltfExporter.FixName(Storage.Gltf);
|
|
}
|
|
|
|
|
|
/// <summary>
|
|
/// VRMコンポーネントのエクスポート
|
|
/// </summary>
|
|
/// <param name="vrm"></param>
|
|
/// <param name="springBone"></param>
|
|
/// <param name="constraint"></param>
|
|
/// <param name="root"></param>
|
|
/// <param name="model"></param>
|
|
/// <param name="converter"></param>
|
|
/// <param name="vrmObject"></param>
|
|
/// <returns></returns>
|
|
(UniGLTF.Extensions.VRMC_vrm.VRMC_vrm vrm,
|
|
UniGLTF.Extensions.VRMC_springBone.VRMC_springBone springBone,
|
|
int? thumbnailIndex) ExportVrm(GameObject root, Model model, ModelExporter converter,
|
|
VRM10ObjectMeta vrmMeta, List<glTFNode> nodes, ITextureExporter textureExporter)
|
|
{
|
|
var vrmController = root?.GetComponent<Vrm10Instance>();
|
|
|
|
if (vrmMeta == null)
|
|
{
|
|
if (vrmController?.Vrm?.Meta == null)
|
|
{
|
|
throw new NullReferenceException("metaObject is null");
|
|
}
|
|
vrmMeta = vrmController.Vrm.Meta;
|
|
}
|
|
|
|
var vrm = new UniGLTF.Extensions.VRMC_vrm.VRMC_vrm
|
|
{
|
|
SpecVersion = VRM_SPEC_VERSION,
|
|
Humanoid = new UniGLTF.Extensions.VRMC_vrm.Humanoid
|
|
{
|
|
HumanBones = new UniGLTF.Extensions.VRMC_vrm.HumanBones
|
|
{
|
|
},
|
|
},
|
|
Meta = new UniGLTF.Extensions.VRMC_vrm.Meta
|
|
{
|
|
LicenseUrl = LICENSE_URL_JA,
|
|
AllowExcessivelySexualUsage = false,
|
|
AllowExcessivelyViolentUsage = false,
|
|
AllowPoliticalOrReligiousUsage = false,
|
|
AllowRedistribution = false,
|
|
},
|
|
};
|
|
|
|
//
|
|
// required
|
|
//
|
|
ExportHumanoid(vrm, model);
|
|
var thumbnailTextureIndex = ExportMeta(vrm, vrmMeta, textureExporter);
|
|
|
|
//
|
|
// optional
|
|
//
|
|
UniGLTF.Extensions.VRMC_springBone.VRMC_springBone vrmSpringBone = default;
|
|
if (vrmController != null)
|
|
{
|
|
ExportExpression(vrm, vrmController, model, converter);
|
|
ExportLookAt(vrm, vrmController);
|
|
ExportFirstPerson(vrm, vrmController, model, converter);
|
|
|
|
vrmSpringBone = ExportSpringBone(vrmController, model, converter);
|
|
ExportConstraints(vrmController, model, converter, nodes);
|
|
}
|
|
|
|
return (vrm, vrmSpringBone, thumbnailTextureIndex);
|
|
}
|
|
|
|
static UniGLTF.Extensions.VRMC_springBone.ColliderShape ExportShape(VRM10SpringBoneCollider z)
|
|
{
|
|
var shape = new UniGLTF.Extensions.VRMC_springBone.ColliderShape();
|
|
switch (z.ColliderType)
|
|
{
|
|
case VRM10SpringBoneColliderTypes.Sphere:
|
|
{
|
|
shape.Sphere = new UniGLTF.Extensions.VRMC_springBone.ColliderShapeSphere
|
|
{
|
|
Radius = z.Radius,
|
|
Offset = ReverseX(z.Offset),
|
|
};
|
|
break;
|
|
}
|
|
|
|
case VRM10SpringBoneColliderTypes.Capsule:
|
|
{
|
|
shape.Capsule = new UniGLTF.Extensions.VRMC_springBone.ColliderShapeCapsule
|
|
{
|
|
Radius = z.Radius,
|
|
Offset = ReverseX(z.Offset),
|
|
Tail = ReverseX(z.Tail),
|
|
};
|
|
break;
|
|
}
|
|
}
|
|
return shape;
|
|
}
|
|
|
|
static UniGLTF.Extensions.VRMC_springBone.SpringBoneJoint ExportJoint(VRM10SpringBoneJoint y, Func<Transform, int> getIndexFromTransform)
|
|
{
|
|
var joint = new UniGLTF.Extensions.VRMC_springBone.SpringBoneJoint
|
|
{
|
|
Node = getIndexFromTransform(y.transform),
|
|
HitRadius = y.m_jointRadius,
|
|
DragForce = y.m_dragForce,
|
|
Stiffness = y.m_stiffnessForce,
|
|
GravityDir = ReverseX(y.m_gravityDir),
|
|
GravityPower = y.m_gravityPower,
|
|
};
|
|
return joint;
|
|
}
|
|
|
|
static UniGLTF.Extensions.VRMC_springBone.VRMC_springBone ExportSpringBone(Vrm10Instance controller, Model model, ModelExporter converter)
|
|
{
|
|
var colliders = controller.GetComponentsInChildren<VRM10SpringBoneCollider>();
|
|
|
|
// if colliders, collider groups and springs don't exist, don't export the extension
|
|
if (
|
|
colliders.Length == 0 &&
|
|
controller.SpringBone.ColliderGroups.Count == 0 &&
|
|
controller.SpringBone.Springs.Count == 0
|
|
)
|
|
{
|
|
return null;
|
|
}
|
|
|
|
var springBone = new UniGLTF.Extensions.VRMC_springBone.VRMC_springBone
|
|
{
|
|
SpecVersion = SPRINGBONE_SPEC_VERSION,
|
|
Colliders = new List<UniGLTF.Extensions.VRMC_springBone.Collider>(),
|
|
ColliderGroups = new List<UniGLTF.Extensions.VRMC_springBone.ColliderGroup>(),
|
|
Springs = new List<UniGLTF.Extensions.VRMC_springBone.Spring>(),
|
|
};
|
|
|
|
// colliders
|
|
Func<Transform, int> getNodeIndexFromTransform = t =>
|
|
{
|
|
var node = converter.Nodes[t.gameObject];
|
|
return model.Nodes.IndexOf(node);
|
|
};
|
|
|
|
foreach (var c in colliders)
|
|
{
|
|
springBone.Colliders.Add(new UniGLTF.Extensions.VRMC_springBone.Collider
|
|
{
|
|
Node = getNodeIndexFromTransform(c.transform),
|
|
Shape = ExportShape(c),
|
|
});
|
|
}
|
|
|
|
// colliderGroups
|
|
foreach (var x in controller.SpringBone.ColliderGroups)
|
|
{
|
|
springBone.ColliderGroups.Add(new UniGLTF.Extensions.VRMC_springBone.ColliderGroup
|
|
{
|
|
Colliders = x.Colliders.Select(y => Array.IndexOf(colliders, y)).ToArray(),
|
|
});
|
|
}
|
|
|
|
// springs
|
|
foreach (var x in controller.SpringBone.Springs)
|
|
{
|
|
var spring = new UniGLTF.Extensions.VRMC_springBone.Spring
|
|
{
|
|
Name = x.Name,
|
|
Joints = x.Joints.Select(y => ExportJoint(y, getNodeIndexFromTransform)).ToList(),
|
|
ColliderGroups = x.ColliderGroups.Select(y => controller.SpringBone.ColliderGroups.IndexOf(y)).ToArray(),
|
|
};
|
|
|
|
if (x.Center != null)
|
|
{
|
|
var center = model.Nodes.IndexOf(converter.Nodes[x.Center.gameObject]);
|
|
if (center != -1)
|
|
{
|
|
spring.Center = center;
|
|
}
|
|
}
|
|
|
|
springBone.Springs.Add(spring);
|
|
}
|
|
|
|
return springBone;
|
|
}
|
|
|
|
static void ExportConstraints(Vrm10Instance vrmController, Model model, ModelExporter converter, List<glTFNode> nodes)
|
|
{
|
|
var constraints = vrmController.GetComponentsInChildren<IVrm10Constraint>();
|
|
foreach (var constraint in constraints)
|
|
{
|
|
var vrmConstraint = new UniGLTF.Extensions.VRMC_node_constraint.VRMC_node_constraint
|
|
{
|
|
SpecVersion = NODE_CONSTRAINT_SPEC_VERSION,
|
|
Constraint = new UniGLTF.Extensions.VRMC_node_constraint.Constraint
|
|
{
|
|
},
|
|
};
|
|
|
|
switch (constraint)
|
|
{
|
|
case Vrm10AimConstraint aimConstraint:
|
|
vrmConstraint.Constraint.Aim = new UniGLTF.Extensions.VRMC_node_constraint.AimConstraint
|
|
{
|
|
Source = model.Nodes.IndexOf(converter.Nodes[aimConstraint.Source.gameObject]),
|
|
Weight = aimConstraint.Weight,
|
|
AimAxis = Vrm10ConstraintUtil.ReverseX(aimConstraint.AimAxis),
|
|
};
|
|
break;
|
|
|
|
case Vrm10RollConstraint rollConstraint:
|
|
vrmConstraint.Constraint.Roll = new UniGLTF.Extensions.VRMC_node_constraint.RollConstraint
|
|
{
|
|
Source = model.Nodes.IndexOf(converter.Nodes[rollConstraint.Source.gameObject]),
|
|
Weight = rollConstraint.Weight,
|
|
RollAxis = rollConstraint.RollAxis,
|
|
};
|
|
break;
|
|
|
|
case Vrm10RotationConstraint rotationConstraint:
|
|
vrmConstraint.Constraint.Rotation = new UniGLTF.Extensions.VRMC_node_constraint.RotationConstraint
|
|
{
|
|
Source = model.Nodes.IndexOf(converter.Nodes[rotationConstraint.Source.gameObject]),
|
|
Weight = rotationConstraint.Weight,
|
|
};
|
|
break;
|
|
|
|
default:
|
|
throw new NotImplementedException();
|
|
}
|
|
|
|
// serialize to gltfNode
|
|
var node = converter.Nodes[constraint.ConstraintTarget];
|
|
var nodeIndex = model.Nodes.IndexOf(node);
|
|
var gltfNode = nodes[nodeIndex];
|
|
UniGLTF.Extensions.VRMC_node_constraint.GltfSerializer.SerializeTo(ref gltfNode.extensions, vrmConstraint);
|
|
}
|
|
}
|
|
|
|
static bool[] ToArray(AxisMask mask)
|
|
{
|
|
return new bool[]
|
|
{
|
|
mask.HasFlag(AxisMask.X),
|
|
mask.HasFlag(AxisMask.Y),
|
|
mask.HasFlag(AxisMask.Z),
|
|
};
|
|
}
|
|
|
|
|
|
static UniGLTF.Extensions.VRMC_vrm.MeshAnnotation ExportMeshAnnotation(RendererFirstPersonFlags flags, Transform root, Func<Renderer, int> getIndex)
|
|
{
|
|
return new UniGLTF.Extensions.VRMC_vrm.MeshAnnotation
|
|
{
|
|
Node = getIndex(flags.GetRenderer(root)),
|
|
Type = flags.FirstPersonFlag,
|
|
};
|
|
}
|
|
|
|
void ExportFirstPerson(UniGLTF.Extensions.VRMC_vrm.VRMC_vrm vrm, Vrm10Instance vrmController, Model model, ModelExporter converter)
|
|
{
|
|
if (!(vrmController?.Vrm?.FirstPerson is VRM10ObjectFirstPerson firstPerson))
|
|
{
|
|
return;
|
|
}
|
|
|
|
vrm.FirstPerson = new UniGLTF.Extensions.VRMC_vrm.FirstPerson
|
|
{
|
|
MeshAnnotations = new List<UniGLTF.Extensions.VRMC_vrm.MeshAnnotation>(),
|
|
};
|
|
Func<Renderer, int> getIndex = r =>
|
|
{
|
|
var node = converter.Nodes[r.gameObject];
|
|
return model.Nodes.IndexOf(node);
|
|
};
|
|
foreach (var f in firstPerson.Renderers)
|
|
{
|
|
vrm.FirstPerson.MeshAnnotations.Add(ExportMeshAnnotation(f, vrmController.transform, getIndex));
|
|
}
|
|
}
|
|
|
|
static UniGLTF.Extensions.VRMC_vrm.LookAtRangeMap ExportLookAtRangeMap(CurveMapper mapper)
|
|
{
|
|
return new UniGLTF.Extensions.VRMC_vrm.LookAtRangeMap
|
|
{
|
|
InputMaxValue = mapper.CurveXRangeDegree,
|
|
OutputScale = mapper.CurveYRangeDegree,
|
|
};
|
|
}
|
|
|
|
static void ExportLookAt(UniGLTF.Extensions.VRMC_vrm.VRMC_vrm vrm, Vrm10Instance vrmController)
|
|
{
|
|
if (!(vrmController?.Vrm?.LookAt is VRM10ObjectLookAt lookAt))
|
|
{
|
|
return;
|
|
}
|
|
|
|
vrm.LookAt = new UniGLTF.Extensions.VRMC_vrm.LookAt
|
|
{
|
|
Type = lookAt.LookAtType,
|
|
OffsetFromHeadBone = lookAt.OffsetFromHead.ReverseX().ToFloat3(),
|
|
RangeMapHorizontalInner = ExportLookAtRangeMap(lookAt.HorizontalInner),
|
|
RangeMapHorizontalOuter = ExportLookAtRangeMap(lookAt.HorizontalOuter),
|
|
RangeMapVerticalDown = ExportLookAtRangeMap(lookAt.VerticalDown),
|
|
RangeMapVerticalUp = ExportLookAtRangeMap(lookAt.VerticalUp),
|
|
};
|
|
}
|
|
|
|
static UniGLTF.Extensions.VRMC_vrm.MorphTargetBind ExportMorphTargetBinding(MorphTargetBinding binding, Func<string, int> getIndex)
|
|
{
|
|
return new UniGLTF.Extensions.VRMC_vrm.MorphTargetBind
|
|
{
|
|
Node = getIndex(binding.RelativePath),
|
|
Index = binding.Index,
|
|
Weight = binding.Weight,
|
|
};
|
|
}
|
|
|
|
static UniGLTF.Extensions.VRMC_vrm.MaterialColorBind ExportMaterialColorBinding(MaterialColorBinding binding, Func<string, int> getIndex)
|
|
{
|
|
return new UniGLTF.Extensions.VRMC_vrm.MaterialColorBind
|
|
{
|
|
Material = getIndex(binding.MaterialName),
|
|
Type = binding.BindType,
|
|
TargetValue = new float[] { binding.TargetValue.x, binding.TargetValue.y, binding.TargetValue.z, binding.TargetValue.w },
|
|
};
|
|
}
|
|
|
|
static UniGLTF.Extensions.VRMC_vrm.TextureTransformBind ExportTextureTransformBinding(MaterialUVBinding binding, Func<string, int> getIndex)
|
|
{
|
|
var (scale, offset) = TextureTransform.VerticalFlipScaleOffset(binding.Scaling, binding.Offset);
|
|
return new UniGLTF.Extensions.VRMC_vrm.TextureTransformBind
|
|
{
|
|
Material = getIndex(binding.MaterialName),
|
|
Offset = new float[] { offset.x, offset.y },
|
|
Scale = new float[] { scale.x, scale.y },
|
|
};
|
|
}
|
|
|
|
static UniGLTF.Extensions.VRMC_vrm.Expression ExportExpression(VRM10Expression e, Vrm10Instance vrmController, Model model, ModelExporter converter)
|
|
{
|
|
if (e == null)
|
|
{
|
|
return null;
|
|
}
|
|
|
|
Func<string, int> getIndexFromRelativePath = relativePath =>
|
|
{
|
|
var rendererNode = vrmController.transform.GetFromPath(relativePath);
|
|
var node = converter.Nodes[rendererNode.gameObject];
|
|
return model.Nodes.IndexOf(node);
|
|
};
|
|
|
|
var vrmExpression = new UniGLTF.Extensions.VRMC_vrm.Expression
|
|
{
|
|
// Preset = e.Preset,
|
|
// Name = e.ExpressionName,
|
|
IsBinary = e.IsBinary,
|
|
OverrideBlink = e.OverrideBlink,
|
|
OverrideLookAt = e.OverrideLookAt,
|
|
OverrideMouth = e.OverrideMouth,
|
|
MorphTargetBinds = new List<UniGLTF.Extensions.VRMC_vrm.MorphTargetBind>(),
|
|
MaterialColorBinds = new List<UniGLTF.Extensions.VRMC_vrm.MaterialColorBind>(),
|
|
TextureTransformBinds = new List<UniGLTF.Extensions.VRMC_vrm.TextureTransformBind>(),
|
|
};
|
|
Func<string, int> getIndexFromMaterialName = materialName =>
|
|
{
|
|
for (int i = 0; i < model.Materials.Count; ++i)
|
|
{
|
|
var m = model.Materials[i] as Material;
|
|
if (m.name == materialName)
|
|
{
|
|
return i;
|
|
}
|
|
}
|
|
throw new KeyNotFoundException();
|
|
};
|
|
|
|
foreach (var b in e.MorphTargetBindings)
|
|
{
|
|
try
|
|
{
|
|
vrmExpression.MorphTargetBinds.Add(ExportMorphTargetBinding(b, getIndexFromRelativePath));
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
Debug.LogWarning(ex);
|
|
}
|
|
}
|
|
foreach (var b in e.MaterialColorBindings)
|
|
{
|
|
try
|
|
{
|
|
vrmExpression.MaterialColorBinds.Add(ExportMaterialColorBinding(b, getIndexFromMaterialName));
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
Debug.LogWarning(ex);
|
|
}
|
|
}
|
|
foreach (var b in e.MaterialUVBindings)
|
|
{
|
|
try
|
|
{
|
|
vrmExpression.TextureTransformBinds.Add(ExportTextureTransformBinding(b, getIndexFromMaterialName));
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
Debug.LogWarning(ex);
|
|
}
|
|
}
|
|
return vrmExpression;
|
|
}
|
|
|
|
static void ExportExpression(UniGLTF.Extensions.VRMC_vrm.VRMC_vrm vrm, Vrm10Instance vrmController, Model model, ModelExporter converter)
|
|
{
|
|
if (vrmController?.Vrm?.Expression?.Clips == null)
|
|
{
|
|
return;
|
|
}
|
|
|
|
vrm.Expressions = new UniGLTF.Extensions.VRMC_vrm.Expressions
|
|
{
|
|
Preset = new UniGLTF.Extensions.VRMC_vrm.Preset
|
|
{
|
|
Happy = ExportExpression(vrmController.Vrm.Expression.Happy, vrmController, model, converter),
|
|
Angry = ExportExpression(vrmController.Vrm.Expression.Angry, vrmController, model, converter),
|
|
Sad = ExportExpression(vrmController.Vrm.Expression.Sad, vrmController, model, converter),
|
|
Relaxed = ExportExpression(vrmController.Vrm.Expression.Relaxed, vrmController, model, converter),
|
|
Surprised = ExportExpression(vrmController.Vrm.Expression.Surprised, vrmController, model, converter),
|
|
Aa = ExportExpression(vrmController.Vrm.Expression.Aa, vrmController, model, converter),
|
|
Ih = ExportExpression(vrmController.Vrm.Expression.Ih, vrmController, model, converter),
|
|
Ou = ExportExpression(vrmController.Vrm.Expression.Ou, vrmController, model, converter),
|
|
Ee = ExportExpression(vrmController.Vrm.Expression.Ee, vrmController, model, converter),
|
|
Oh = ExportExpression(vrmController.Vrm.Expression.Oh, vrmController, model, converter),
|
|
Blink = ExportExpression(vrmController.Vrm.Expression.Blink, vrmController, model, converter),
|
|
BlinkLeft = ExportExpression(vrmController.Vrm.Expression.BlinkLeft, vrmController, model, converter),
|
|
BlinkRight = ExportExpression(vrmController.Vrm.Expression.BlinkRight, vrmController, model, converter),
|
|
LookUp = ExportExpression(vrmController.Vrm.Expression.LookUp, vrmController, model, converter),
|
|
LookDown = ExportExpression(vrmController.Vrm.Expression.LookDown, vrmController, model, converter),
|
|
LookLeft = ExportExpression(vrmController.Vrm.Expression.LookLeft, vrmController, model, converter),
|
|
LookRight = ExportExpression(vrmController.Vrm.Expression.LookRight, vrmController, model, converter),
|
|
Neutral = ExportExpression(vrmController.Vrm.Expression.Neutral, vrmController, model, converter),
|
|
},
|
|
Custom = vrmController.Vrm.Expression.CustomClips.ToDictionary(c => c.name, c => ExportExpression(c, vrmController, model, converter)),
|
|
};
|
|
}
|
|
|
|
public static int? ExportMeta(UniGLTF.Extensions.VRMC_vrm.VRMC_vrm vrm, VRM10ObjectMeta meta, ITextureExporter textureExporter)
|
|
{
|
|
vrm.Meta.Name = meta.Name;
|
|
vrm.Meta.Version = meta.Version;
|
|
vrm.Meta.Authors = meta.Authors.ToList();
|
|
vrm.Meta.CopyrightInformation = meta.CopyrightInformation;
|
|
vrm.Meta.ContactInformation = meta.ContactInformation;
|
|
vrm.Meta.References = meta.References.ToList();
|
|
vrm.Meta.ThirdPartyLicenses = meta.ThirdPartyLicenses;
|
|
vrm.Meta.AvatarPermission = meta.AvatarPermission;
|
|
vrm.Meta.AllowExcessivelyViolentUsage = meta.ViolentUsage;
|
|
vrm.Meta.AllowExcessivelySexualUsage = meta.SexualUsage;
|
|
vrm.Meta.CommercialUsage = meta.CommercialUsage;
|
|
vrm.Meta.AllowPoliticalOrReligiousUsage = meta.PoliticalOrReligiousUsage;
|
|
vrm.Meta.AllowAntisocialOrHateUsage = meta.AntisocialOrHateUsage;
|
|
vrm.Meta.CreditNotation = meta.CreditNotation;
|
|
vrm.Meta.AllowRedistribution = meta.Redistribution;
|
|
vrm.Meta.Modification = meta.Modification;
|
|
vrm.Meta.OtherLicenseUrl = meta.OtherLicenseUrl;
|
|
int? thumbnailTextureIndex = default;
|
|
if (meta.Thumbnail != null)
|
|
{
|
|
thumbnailTextureIndex = textureExporter.RegisterExportingAsSRgb(meta.Thumbnail, needsAlpha: true);
|
|
}
|
|
return thumbnailTextureIndex;
|
|
}
|
|
|
|
static void ExportHumanoid(UniGLTF.Extensions.VRMC_vrm.VRMC_vrm vrm, Model model)
|
|
{
|
|
// humanoid
|
|
for (int i = 0; i < model.Nodes.Count; ++i)
|
|
{
|
|
var bone = model.Nodes[i];
|
|
switch (bone.HumanoidBone)
|
|
{
|
|
case HumanoidBones.hips: vrm.Humanoid.HumanBones.Hips = new UniGLTF.Extensions.VRMC_vrm.HumanBone { Node = i }; break;
|
|
case HumanoidBones.spine: vrm.Humanoid.HumanBones.Spine = new UniGLTF.Extensions.VRMC_vrm.HumanBone { Node = i }; break;
|
|
case HumanoidBones.chest: vrm.Humanoid.HumanBones.Chest = new UniGLTF.Extensions.VRMC_vrm.HumanBone { Node = i }; break;
|
|
case HumanoidBones.upperChest: vrm.Humanoid.HumanBones.UpperChest = new UniGLTF.Extensions.VRMC_vrm.HumanBone { Node = i }; break;
|
|
case HumanoidBones.neck: vrm.Humanoid.HumanBones.Neck = new UniGLTF.Extensions.VRMC_vrm.HumanBone { Node = i }; break;
|
|
case HumanoidBones.head: vrm.Humanoid.HumanBones.Head = new UniGLTF.Extensions.VRMC_vrm.HumanBone { Node = i }; break;
|
|
case HumanoidBones.leftEye: vrm.Humanoid.HumanBones.LeftEye = new UniGLTF.Extensions.VRMC_vrm.HumanBone { Node = i }; break;
|
|
case HumanoidBones.rightEye: vrm.Humanoid.HumanBones.RightEye = new UniGLTF.Extensions.VRMC_vrm.HumanBone { Node = i }; break;
|
|
case HumanoidBones.jaw: vrm.Humanoid.HumanBones.Jaw = new UniGLTF.Extensions.VRMC_vrm.HumanBone { Node = i }; break;
|
|
case HumanoidBones.leftUpperLeg: vrm.Humanoid.HumanBones.LeftUpperLeg = new UniGLTF.Extensions.VRMC_vrm.HumanBone { Node = i }; break;
|
|
case HumanoidBones.leftLowerLeg: vrm.Humanoid.HumanBones.LeftLowerLeg = new UniGLTF.Extensions.VRMC_vrm.HumanBone { Node = i }; break;
|
|
case HumanoidBones.leftFoot: vrm.Humanoid.HumanBones.LeftFoot = new UniGLTF.Extensions.VRMC_vrm.HumanBone { Node = i }; break;
|
|
case HumanoidBones.leftToes: vrm.Humanoid.HumanBones.LeftToes = new UniGLTF.Extensions.VRMC_vrm.HumanBone { Node = i }; break;
|
|
case HumanoidBones.rightUpperLeg: vrm.Humanoid.HumanBones.RightUpperLeg = new UniGLTF.Extensions.VRMC_vrm.HumanBone { Node = i }; break;
|
|
case HumanoidBones.rightLowerLeg: vrm.Humanoid.HumanBones.RightLowerLeg = new UniGLTF.Extensions.VRMC_vrm.HumanBone { Node = i }; break;
|
|
case HumanoidBones.rightFoot: vrm.Humanoid.HumanBones.RightFoot = new UniGLTF.Extensions.VRMC_vrm.HumanBone { Node = i }; break;
|
|
case HumanoidBones.rightToes: vrm.Humanoid.HumanBones.RightToes = new UniGLTF.Extensions.VRMC_vrm.HumanBone { Node = i }; break;
|
|
case HumanoidBones.leftShoulder: vrm.Humanoid.HumanBones.LeftShoulder = new UniGLTF.Extensions.VRMC_vrm.HumanBone { Node = i }; break;
|
|
case HumanoidBones.leftUpperArm: vrm.Humanoid.HumanBones.LeftUpperArm = new UniGLTF.Extensions.VRMC_vrm.HumanBone { Node = i }; break;
|
|
case HumanoidBones.leftLowerArm: vrm.Humanoid.HumanBones.LeftLowerArm = new UniGLTF.Extensions.VRMC_vrm.HumanBone { Node = i }; break;
|
|
case HumanoidBones.leftHand: vrm.Humanoid.HumanBones.LeftHand = new UniGLTF.Extensions.VRMC_vrm.HumanBone { Node = i }; break;
|
|
case HumanoidBones.rightShoulder: vrm.Humanoid.HumanBones.RightShoulder = new UniGLTF.Extensions.VRMC_vrm.HumanBone { Node = i }; break;
|
|
case HumanoidBones.rightUpperArm: vrm.Humanoid.HumanBones.RightUpperArm = new UniGLTF.Extensions.VRMC_vrm.HumanBone { Node = i }; break;
|
|
case HumanoidBones.rightLowerArm: vrm.Humanoid.HumanBones.RightLowerArm = new UniGLTF.Extensions.VRMC_vrm.HumanBone { Node = i }; break;
|
|
case HumanoidBones.rightHand: vrm.Humanoid.HumanBones.RightHand = new UniGLTF.Extensions.VRMC_vrm.HumanBone { Node = i }; break;
|
|
case HumanoidBones.leftThumbMetacarpal: vrm.Humanoid.HumanBones.LeftThumbMetacarpal = new UniGLTF.Extensions.VRMC_vrm.HumanBone { Node = i }; break;
|
|
case HumanoidBones.leftThumbProximal: vrm.Humanoid.HumanBones.LeftThumbProximal = new UniGLTF.Extensions.VRMC_vrm.HumanBone { Node = i }; break;
|
|
case HumanoidBones.leftThumbDistal: vrm.Humanoid.HumanBones.LeftThumbDistal = new UniGLTF.Extensions.VRMC_vrm.HumanBone { Node = i }; break;
|
|
case HumanoidBones.leftIndexProximal: vrm.Humanoid.HumanBones.LeftIndexProximal = new UniGLTF.Extensions.VRMC_vrm.HumanBone { Node = i }; break;
|
|
case HumanoidBones.leftIndexIntermediate: vrm.Humanoid.HumanBones.LeftIndexIntermediate = new UniGLTF.Extensions.VRMC_vrm.HumanBone { Node = i }; break;
|
|
case HumanoidBones.leftIndexDistal: vrm.Humanoid.HumanBones.LeftIndexDistal = new UniGLTF.Extensions.VRMC_vrm.HumanBone { Node = i }; break;
|
|
case HumanoidBones.leftMiddleProximal: vrm.Humanoid.HumanBones.LeftMiddleProximal = new UniGLTF.Extensions.VRMC_vrm.HumanBone { Node = i }; break;
|
|
case HumanoidBones.leftMiddleIntermediate: vrm.Humanoid.HumanBones.LeftMiddleIntermediate = new UniGLTF.Extensions.VRMC_vrm.HumanBone { Node = i }; break;
|
|
case HumanoidBones.leftMiddleDistal: vrm.Humanoid.HumanBones.LeftMiddleDistal = new UniGLTF.Extensions.VRMC_vrm.HumanBone { Node = i }; break;
|
|
case HumanoidBones.leftRingProximal: vrm.Humanoid.HumanBones.LeftRingProximal = new UniGLTF.Extensions.VRMC_vrm.HumanBone { Node = i }; break;
|
|
case HumanoidBones.leftRingIntermediate: vrm.Humanoid.HumanBones.LeftRingIntermediate = new UniGLTF.Extensions.VRMC_vrm.HumanBone { Node = i }; break;
|
|
case HumanoidBones.leftRingDistal: vrm.Humanoid.HumanBones.LeftRingDistal = new UniGLTF.Extensions.VRMC_vrm.HumanBone { Node = i }; break;
|
|
case HumanoidBones.leftLittleProximal: vrm.Humanoid.HumanBones.LeftLittleProximal = new UniGLTF.Extensions.VRMC_vrm.HumanBone { Node = i }; break;
|
|
case HumanoidBones.leftLittleIntermediate: vrm.Humanoid.HumanBones.LeftLittleIntermediate = new UniGLTF.Extensions.VRMC_vrm.HumanBone { Node = i }; break;
|
|
case HumanoidBones.leftLittleDistal: vrm.Humanoid.HumanBones.LeftLittleDistal = new UniGLTF.Extensions.VRMC_vrm.HumanBone { Node = i }; break;
|
|
case HumanoidBones.rightThumbMetacarpal: vrm.Humanoid.HumanBones.RightThumbMetacarpal = new UniGLTF.Extensions.VRMC_vrm.HumanBone { Node = i }; break;
|
|
case HumanoidBones.rightThumbProximal: vrm.Humanoid.HumanBones.RightThumbProximal = new UniGLTF.Extensions.VRMC_vrm.HumanBone { Node = i }; break;
|
|
case HumanoidBones.rightThumbDistal: vrm.Humanoid.HumanBones.RightThumbDistal = new UniGLTF.Extensions.VRMC_vrm.HumanBone { Node = i }; break;
|
|
case HumanoidBones.rightIndexProximal: vrm.Humanoid.HumanBones.RightIndexProximal = new UniGLTF.Extensions.VRMC_vrm.HumanBone { Node = i }; break;
|
|
case HumanoidBones.rightIndexIntermediate: vrm.Humanoid.HumanBones.RightIndexIntermediate = new UniGLTF.Extensions.VRMC_vrm.HumanBone { Node = i }; break;
|
|
case HumanoidBones.rightIndexDistal: vrm.Humanoid.HumanBones.RightIndexDistal = new UniGLTF.Extensions.VRMC_vrm.HumanBone { Node = i }; break;
|
|
case HumanoidBones.rightMiddleProximal: vrm.Humanoid.HumanBones.RightMiddleProximal = new UniGLTF.Extensions.VRMC_vrm.HumanBone { Node = i }; break;
|
|
case HumanoidBones.rightMiddleIntermediate: vrm.Humanoid.HumanBones.RightMiddleIntermediate = new UniGLTF.Extensions.VRMC_vrm.HumanBone { Node = i }; break;
|
|
case HumanoidBones.rightMiddleDistal: vrm.Humanoid.HumanBones.RightMiddleDistal = new UniGLTF.Extensions.VRMC_vrm.HumanBone { Node = i }; break;
|
|
case HumanoidBones.rightRingProximal: vrm.Humanoid.HumanBones.RightRingProximal = new UniGLTF.Extensions.VRMC_vrm.HumanBone { Node = i }; break;
|
|
case HumanoidBones.rightRingIntermediate: vrm.Humanoid.HumanBones.RightRingIntermediate = new UniGLTF.Extensions.VRMC_vrm.HumanBone { Node = i }; break;
|
|
case HumanoidBones.rightRingDistal: vrm.Humanoid.HumanBones.RightRingDistal = new UniGLTF.Extensions.VRMC_vrm.HumanBone { Node = i }; break;
|
|
case HumanoidBones.rightLittleProximal: vrm.Humanoid.HumanBones.RightLittleProximal = new UniGLTF.Extensions.VRMC_vrm.HumanBone { Node = i }; break;
|
|
case HumanoidBones.rightLittleIntermediate: vrm.Humanoid.HumanBones.RightLittleIntermediate = new UniGLTF.Extensions.VRMC_vrm.HumanBone { Node = i }; break;
|
|
case HumanoidBones.rightLittleDistal: vrm.Humanoid.HumanBones.RightLittleDistal = new UniGLTF.Extensions.VRMC_vrm.HumanBone { Node = i }; break;
|
|
}
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// 便利関数
|
|
/// </summary>
|
|
/// <param name="go"></param>
|
|
/// <param name="getTextureBytes"></param>
|
|
/// <returns></returns>
|
|
public static byte[] Export(GameObject go, ITextureSerializer textureSerializer = null, VRM10ObjectMeta vrmMeta = null)
|
|
{
|
|
using (var arrayManager = new NativeArrayManager())
|
|
{
|
|
// ヒエラルキーからジオメトリーを収集
|
|
var converter = new UniVRM10.ModelExporter();
|
|
var model = converter.Export(arrayManager, go);
|
|
|
|
// 右手系に変換
|
|
model.ConvertCoordinate(VrmLib.Coordinates.Vrm1);
|
|
|
|
// Model と go から VRM-1.0 にExport
|
|
var exporter10 = new Vrm10Exporter(textureSerializer ?? new RuntimeTextureSerializer(), new GltfExportSettings());
|
|
var option = new VrmLib.ExportArgs
|
|
{
|
|
};
|
|
exporter10.Export(go, model, converter, option, vrmMeta);
|
|
return exporter10.Storage.ToGlbBytes();
|
|
}
|
|
}
|
|
}
|
|
}
|