mirror of
https://github.com/vrm-c/UniVRM.git
synced 2026-05-13 05:54:59 -05:00
798 lines
36 KiB
C#
798 lines
36 KiB
C#
using System;
|
|
using System.Collections.Generic;
|
|
using System.Linq;
|
|
using UniGLTF;
|
|
using UnityEngine;
|
|
using VrmLib;
|
|
using VRMShaders;
|
|
|
|
|
|
namespace UniVRM10
|
|
{
|
|
public class Vrm10Exporter : IDisposable
|
|
{
|
|
public readonly Vrm10Storage Storage = new Vrm10Storage();
|
|
|
|
public readonly string VrmExtensionName = "VRMC_vrm";
|
|
|
|
TextureExporter m_textureExporter;
|
|
|
|
public Vrm10Exporter(Func<Texture, bool> useAsset)
|
|
{
|
|
Storage.Gltf.extensionsUsed.Add(glTF_KHR_materials_unlit.ExtensionName);
|
|
Storage.Gltf.extensionsUsed.Add(glTF_KHR_texture_transform.ExtensionName);
|
|
Storage.Gltf.extensionsUsed.Add(UniGLTF.Extensions.VRMC_vrm.VRMC_vrm.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_collider.VRMC_node_collider.ExtensionName);
|
|
Storage.Gltf.extensionsUsed.Add(UniGLTF.Extensions.VRMC_node_constraint.VRMC_node_constraint.ExtensionName);
|
|
Storage.Gltf.buffers.Add(new glTFBuffer
|
|
{
|
|
|
|
});
|
|
|
|
m_textureExporter = new TextureExporter(useAsset);
|
|
}
|
|
|
|
public void Dispose()
|
|
{
|
|
m_textureExporter.Dispose();
|
|
}
|
|
|
|
public void ExportAsset(Model model)
|
|
{
|
|
Storage.Gltf.asset = new glTFAssets
|
|
{
|
|
};
|
|
if (!string.IsNullOrEmpty(model.AssetVersion)) Storage.Gltf.asset.version = model.AssetVersion;
|
|
if (!string.IsNullOrEmpty(model.AssetMinVersion)) Storage.Gltf.asset.minVersion = model.AssetMinVersion;
|
|
|
|
if (!string.IsNullOrEmpty(model.AssetGenerator)) Storage.Gltf.asset.generator = model.AssetGenerator;
|
|
|
|
if (!string.IsNullOrEmpty(model.AssetCopyright)) Storage.Gltf.asset.copyright = model.AssetCopyright;
|
|
}
|
|
|
|
public void Reserve(int bytesLength)
|
|
{
|
|
Storage.Reserve(bytesLength);
|
|
}
|
|
|
|
public void ExportMeshes(List<MeshGroup> groups, List<object> materials, ExportArgs option)
|
|
{
|
|
foreach (var group in groups)
|
|
{
|
|
var mesh = group.ExportMeshGroup(materials, Storage, option);
|
|
Storage.Gltf.meshes.Add(mesh);
|
|
}
|
|
}
|
|
|
|
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()
|
|
});
|
|
}
|
|
|
|
/// <summary>
|
|
/// revere X
|
|
/// </summary>
|
|
/// <param name="v"></param>
|
|
/// <returns></returns>
|
|
static float[] ReverseX(Vector3 v)
|
|
{
|
|
return new float[] { -v.x, v.y, v.z };
|
|
}
|
|
|
|
public void Export(GameObject root, Model model, RuntimeVrmConverter converter, ExportArgs option, Func<Texture2D, (byte[], string)> 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 = m_textureExporter.Exported[i];
|
|
Storage.Gltf.PushGltfTexture(0, unityTexture, 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);
|
|
}
|
|
}
|
|
|
|
/// <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="meta"></param>
|
|
/// <returns></returns>
|
|
(UniGLTF.Extensions.VRMC_vrm.VRMC_vrm vrm,
|
|
UniGLTF.Extensions.VRMC_springBone.VRMC_springBone springBone,
|
|
int? thumbnailIndex) ExportVrm(GameObject root, Model model, RuntimeVrmConverter converter, VRM10MetaObject meta)
|
|
{
|
|
var vrmController = root?.GetComponent<VRM10Controller>();
|
|
|
|
if (meta == null)
|
|
{
|
|
if (vrmController == null || vrmController.Meta == null)
|
|
{
|
|
throw new NullReferenceException("metaObject is null");
|
|
}
|
|
meta = vrmController.Meta.Meta;
|
|
}
|
|
|
|
var vrm = new UniGLTF.Extensions.VRMC_vrm.VRMC_vrm
|
|
{
|
|
Humanoid = new UniGLTF.Extensions.VRMC_vrm.Humanoid
|
|
{
|
|
HumanBones = new UniGLTF.Extensions.VRMC_vrm.HumanBones
|
|
{
|
|
},
|
|
},
|
|
Meta = new UniGLTF.Extensions.VRMC_vrm.Meta
|
|
{
|
|
AllowExcessivelySexualUsage = false,
|
|
AllowExcessivelyViolentUsage = false,
|
|
AllowPoliticalOrReligiousUsage = false,
|
|
AllowRedistribution = false,
|
|
},
|
|
};
|
|
|
|
//
|
|
// required
|
|
//
|
|
ExportHumanoid(vrm, model);
|
|
var thumbnailTextureIndex = ExportMeta(vrm, meta);
|
|
|
|
//
|
|
// 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);
|
|
}
|
|
|
|
return (vrm, vrmSpringBone, thumbnailTextureIndex);
|
|
}
|
|
|
|
UniGLTF.Extensions.VRMC_node_collider.ColliderShape ExportShape(VRM10SpringBoneCollider z)
|
|
{
|
|
var shape = new UniGLTF.Extensions.VRMC_node_collider.ColliderShape();
|
|
switch (z.ColliderType)
|
|
{
|
|
case VRM10SpringBoneColliderTypes.Sphere:
|
|
{
|
|
shape.Sphere = new UniGLTF.Extensions.VRMC_node_collider.ColliderShapeSphere
|
|
{
|
|
Radius = z.Radius,
|
|
Offset = ReverseX(z.Offset),
|
|
};
|
|
break;
|
|
}
|
|
|
|
case VRM10SpringBoneColliderTypes.Capsule:
|
|
{
|
|
shape.Capsule = new UniGLTF.Extensions.VRMC_node_collider.ColliderShapeCapsule
|
|
{
|
|
Radius = z.Radius,
|
|
Offset = new float[] { z.Offset.x, z.Offset.y, z.Offset.z },
|
|
Tail = new float[] { z.Tail.x, z.Tail.y, z.Tail.z },
|
|
};
|
|
break;
|
|
}
|
|
}
|
|
return shape;
|
|
}
|
|
|
|
UniGLTF.Extensions.VRMC_springBone.SpringBoneJoint ExportJoint(VRM10SpringJoint 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;
|
|
}
|
|
|
|
UniGLTF.Extensions.VRMC_springBone.VRMC_springBone ExportSpringBone(VRM10Controller vrmController, Model model, RuntimeVrmConverter converter)
|
|
{
|
|
var springBone = new UniGLTF.Extensions.VRMC_springBone.VRMC_springBone
|
|
{
|
|
Springs = new List<UniGLTF.Extensions.VRMC_springBone.Spring>(),
|
|
};
|
|
|
|
Func<Transform, int> getIndexFromTransform = t =>
|
|
{
|
|
var node = converter.Nodes[t.gameObject];
|
|
return model.Nodes.IndexOf(node);
|
|
};
|
|
|
|
foreach (var x in vrmController.GetComponentsInChildren<VRM10SpringBone>())
|
|
{
|
|
var spring = new UniGLTF.Extensions.VRMC_springBone.Spring
|
|
{
|
|
Name = x.Comment,
|
|
Joints = x.Joints.Select(y => ExportJoint(y, getIndexFromTransform)).ToList(),
|
|
};
|
|
springBone.Springs.Add(spring);
|
|
|
|
List<int> colliders = new List<int>();
|
|
foreach (var y in x.ColliderGroups)
|
|
{
|
|
// node
|
|
var node = converter.Nodes[y.gameObject];
|
|
var nodeIndex = model.Nodes.IndexOf(node);
|
|
colliders.Add(nodeIndex);
|
|
var gltfNode = Storage.Gltf.nodes[nodeIndex];
|
|
|
|
// VRMC_node_collider
|
|
var collider = new UniGLTF.Extensions.VRMC_node_collider.VRMC_node_collider
|
|
{
|
|
Shapes = y.Colliders.Select(ExportShape).ToList(),
|
|
};
|
|
|
|
// serialize
|
|
UniGLTF.Extensions.VRMC_node_collider.GltfSerializer.SerializeTo(ref gltfNode.extensions, collider);
|
|
}
|
|
spring.Colliders = colliders.ToArray();
|
|
}
|
|
|
|
return springBone;
|
|
}
|
|
|
|
void ExportConstraints(VRM10Controller vrmController, Model model, RuntimeVrmConverter converter)
|
|
{
|
|
var constraints = vrmController.GetComponentsInChildren<VRM10Constraint>();
|
|
foreach (var constraint in constraints)
|
|
{
|
|
UniGLTF.Extensions.VRMC_node_constraint.VRMC_node_constraint vrmConstraint = default;
|
|
switch (constraint)
|
|
{
|
|
case VRM10PositionConstraint positionConstraint:
|
|
vrmConstraint = ExportPostionConstraint(positionConstraint, model, converter);
|
|
break;
|
|
|
|
case VRM10RotationConstraint rotationConstraint:
|
|
vrmConstraint = ExportRotationConstraint(rotationConstraint, model, converter);
|
|
break;
|
|
|
|
case VRM10AimConstraint aimConstraint:
|
|
vrmConstraint = ExportAimConstraint(aimConstraint, model, converter);
|
|
break;
|
|
|
|
default:
|
|
throw new NotImplementedException();
|
|
}
|
|
|
|
// serialize to gltfNode
|
|
var node = converter.Nodes[constraint.gameObject];
|
|
var nodeIndex = model.Nodes.IndexOf(node);
|
|
var gltfNode = Storage.Gltf.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_node_constraint.VRMC_node_constraint ExportPostionConstraint(VRM10PositionConstraint c, Model model, RuntimeVrmConverter converter)
|
|
{
|
|
return new UniGLTF.Extensions.VRMC_node_constraint.VRMC_node_constraint
|
|
{
|
|
Constraint = new UniGLTF.Extensions.VRMC_node_constraint.Constraint
|
|
{
|
|
Position = new UniGLTF.Extensions.VRMC_node_constraint.PositionConstraint
|
|
{
|
|
Source = model.Nodes.IndexOf(converter.Nodes[c.Source.gameObject]),
|
|
SourceSpace = c.SourceCoordinate,
|
|
DestinationSpace = c.DestinationCoordinate,
|
|
FreezeAxes = ToArray(c.FreezeAxes),
|
|
Weight = c.Weight,
|
|
}
|
|
},
|
|
};
|
|
}
|
|
|
|
static UniGLTF.Extensions.VRMC_node_constraint.VRMC_node_constraint ExportRotationConstraint(VRM10RotationConstraint c, Model model, RuntimeVrmConverter converter)
|
|
{
|
|
return new UniGLTF.Extensions.VRMC_node_constraint.VRMC_node_constraint
|
|
{
|
|
Constraint = new UniGLTF.Extensions.VRMC_node_constraint.Constraint
|
|
{
|
|
Rotation = new UniGLTF.Extensions.VRMC_node_constraint.RotationConstraint
|
|
{
|
|
Source = model.Nodes.IndexOf(converter.Nodes[c.Source.gameObject]),
|
|
SourceSpace = c.SourceCoordinate,
|
|
DestinationSpace = c.DestinationCoordinate,
|
|
FreezeAxes = ToArray(c.FreezeAxes),
|
|
Weight = c.Weight,
|
|
},
|
|
},
|
|
};
|
|
}
|
|
|
|
static UniGLTF.Extensions.VRMC_node_constraint.VRMC_node_constraint ExportAimConstraint(VRM10AimConstraint c, Model model, RuntimeVrmConverter converter)
|
|
{
|
|
return new UniGLTF.Extensions.VRMC_node_constraint.VRMC_node_constraint
|
|
{
|
|
Constraint = new UniGLTF.Extensions.VRMC_node_constraint.Constraint
|
|
{
|
|
Aim = new UniGLTF.Extensions.VRMC_node_constraint.AimConstraint
|
|
{
|
|
Source = model.Nodes.IndexOf(converter.Nodes[c.Source.gameObject]),
|
|
AimVector = ReverseX(c.AimVector),
|
|
UpVector = ReverseX(c.UpVector),
|
|
Weight = c.Weight,
|
|
},
|
|
},
|
|
};
|
|
}
|
|
|
|
static UniGLTF.Extensions.VRMC_vrm.MeshAnnotation ExportMeshAnnotation(RendererFirstPersonFlags flags, Func<Renderer, int> getIndex)
|
|
{
|
|
return new UniGLTF.Extensions.VRMC_vrm.MeshAnnotation
|
|
{
|
|
Node = getIndex(flags.Renderer),
|
|
FirstPersonType = flags.FirstPersonFlag,
|
|
};
|
|
}
|
|
|
|
void ExportFirstPerson(UniGLTF.Extensions.VRMC_vrm.VRMC_vrm vrm, VRM10Controller vrmController, Model model, RuntimeVrmConverter converter)
|
|
{
|
|
if (vrmController?.FirstPerson == null)
|
|
{
|
|
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 vrmController.FirstPerson.Renderers)
|
|
{
|
|
vrm.FirstPerson.MeshAnnotations.Add(ExportMeshAnnotation(f, getIndex));
|
|
}
|
|
}
|
|
|
|
UniGLTF.Extensions.VRMC_vrm.LookAtRangeMap ExportLookAtRangeMap(CurveMapper mapper)
|
|
{
|
|
return new UniGLTF.Extensions.VRMC_vrm.LookAtRangeMap
|
|
{
|
|
InputMaxValue = mapper.CurveXRangeDegree,
|
|
OutputScale = mapper.CurveYRangeDegree,
|
|
};
|
|
}
|
|
|
|
void ExportLookAt(UniGLTF.Extensions.VRMC_vrm.VRMC_vrm vrm, VRM10Controller vrmController)
|
|
{
|
|
if (vrmController?.LookAt == null)
|
|
{
|
|
return;
|
|
}
|
|
|
|
vrm.LookAt = new UniGLTF.Extensions.VRMC_vrm.LookAt
|
|
{
|
|
LookAtType = vrmController.LookAt.LookAtType,
|
|
OffsetFromHeadBone = new float[]{
|
|
vrmController.LookAt.OffsetFromHead.x ,
|
|
vrmController.LookAt.OffsetFromHead.y ,
|
|
vrmController.LookAt.OffsetFromHead.z ,
|
|
},
|
|
LookAtHorizontalInner = ExportLookAtRangeMap(vrmController.LookAt.HorizontalInner),
|
|
LookAtHorizontalOuter = ExportLookAtRangeMap(vrmController.LookAt.HorizontalOuter),
|
|
LookAtVerticalDown = ExportLookAtRangeMap(vrmController.LookAt.VerticalDown),
|
|
LookAtVerticalUp = ExportLookAtRangeMap(vrmController.LookAt.VerticalUp),
|
|
};
|
|
}
|
|
|
|
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,
|
|
};
|
|
}
|
|
|
|
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 },
|
|
};
|
|
}
|
|
|
|
UniGLTF.Extensions.VRMC_vrm.TextureTransformBind ExportTextureTransformBinding(MaterialUVBinding binding, Func<string, int> getIndex)
|
|
{
|
|
return new UniGLTF.Extensions.VRMC_vrm.TextureTransformBind
|
|
{
|
|
Material = getIndex(binding.MaterialName),
|
|
Offset = new float[] { binding.Offset.x, binding.Offset.y },
|
|
Scaling = new float[] { binding.Scaling.x, binding.Scaling.y },
|
|
};
|
|
}
|
|
|
|
void ExportExpression(UniGLTF.Extensions.VRMC_vrm.VRMC_vrm vrm, VRM10Controller vrmController, Model model, RuntimeVrmConverter converter)
|
|
{
|
|
if (vrmController?.Expression?.ExpressionAvatar?.Clips == null)
|
|
{
|
|
return;
|
|
}
|
|
|
|
Func<string, int> getIndexFromRelativePath = relativePath =>
|
|
{
|
|
var rendererNode = vrmController.transform.GetFromPath(relativePath);
|
|
var node = converter.Nodes[rendererNode.gameObject];
|
|
return model.Nodes.IndexOf(node);
|
|
};
|
|
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();
|
|
};
|
|
|
|
vrm.Expressions = new List<UniGLTF.Extensions.VRMC_vrm.Expression>();
|
|
foreach (var e in vrmController.Expression.ExpressionAvatar.Clips)
|
|
{
|
|
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>(),
|
|
};
|
|
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);
|
|
}
|
|
}
|
|
vrm.Expressions.Add(vrmExpression);
|
|
}
|
|
}
|
|
|
|
int? ExportMeta(UniGLTF.Extensions.VRMC_vrm.VRMC_vrm vrm, VRM10MetaObject meta)
|
|
{
|
|
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 =
|
|
vrm.Meta.AvatarPermission = meta.AllowedUser;
|
|
vrm.Meta.AllowExcessivelyViolentUsage = meta.ViolentUsage;
|
|
vrm.Meta.AllowExcessivelySexualUsage = meta.SexualUsage;
|
|
vrm.Meta.CommercialUsage = meta.CommercialUsage;
|
|
vrm.Meta.AllowPoliticalOrReligiousUsage = meta.PoliticalOrReligiousUsage;
|
|
vrm.Meta.CreditNotation = meta.CreditNotation;
|
|
vrm.Meta.AllowRedistribution = meta.Redistribution;
|
|
vrm.Meta.Modification = meta.ModificationLicense;
|
|
vrm.Meta.OtherLicenseUrl = meta.OtherLicenseUrl;
|
|
int? thumbnailTextureIndex = default;
|
|
if (meta.Thumbnail != null)
|
|
{
|
|
thumbnailTextureIndex = m_textureExporter.ExportSRGB(meta.Thumbnail);
|
|
}
|
|
return thumbnailTextureIndex;
|
|
}
|
|
|
|
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.leftThumbProximal: vrm.Humanoid.HumanBones.LeftThumbProximal = new UniGLTF.Extensions.VRMC_vrm.HumanBone { Node = i }; break;
|
|
|
|
case HumanoidBones.leftThumbIntermediate: vrm.Humanoid.HumanBones.LeftThumbIntermediate = 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.rightThumbProximal: vrm.Humanoid.HumanBones.RightThumbProximal = new UniGLTF.Extensions.VRMC_vrm.HumanBone { Node = i }; break;
|
|
|
|
case HumanoidBones.rightThumbIntermediate: vrm.Humanoid.HumanBones.RightThumbIntermediate = 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, Func<Texture2D, (byte[], string)> getTextureBytes = null)
|
|
{
|
|
if (getTextureBytes == null)
|
|
{
|
|
// default for runtime export
|
|
getTextureBytes = TextureExporter.GetTextureBytesWithMime;
|
|
}
|
|
|
|
// ヒエラルキーからジオメトリーを収集
|
|
var converter = new UniVRM10.RuntimeVrmConverter();
|
|
var model = converter.ToModelFrom10(go);
|
|
|
|
// 右手系に変換
|
|
VrmLib.ModelExtensionsForCoordinates.ConvertCoordinate(model, VrmLib.Coordinates.Vrm1);
|
|
|
|
// Model と go から VRM-1.0 にExport
|
|
var exporter10 = new Vrm10Exporter(_ => false);
|
|
var option = new VrmLib.ExportArgs
|
|
{
|
|
};
|
|
exporter10.Export(go, model, converter, option, getTextureBytes);
|
|
return exporter10.Storage.ToBytes();
|
|
}
|
|
}
|
|
}
|