Merge pull request #911 from ousttrue/feature10/rename_importer_exporter

Feature10/rename importer exporter
This commit is contained in:
PoChang007
2021-04-26 14:09:06 +09:00
committed by GitHub
30 changed files with 516 additions and 563 deletions

View File

@@ -311,7 +311,7 @@ namespace UniVRM10.Samples
var parser = new UniGLTF.GltfParser();
parser.ParsePath(path);
using (var loader = new RuntimeUnityBuilder(parser))
using (var loader = new Vrm10Importer(parser))
{
loader.Load();
loader.ShowMeshes();

View File

@@ -38,7 +38,7 @@ namespace UniVRM10
m_parser = null;
return;
}
m_model = VrmLoader.CreateVrmModel(m_parser);
m_model = ModelReader.Read(m_parser);
}
enum Tabs

View File

@@ -96,7 +96,7 @@ namespace UniVRM10
//
var externalObjectMap = scriptedImporter.GetExternalObjectMap().ToDictionary(kv => new SubAssetKey(kv.Key.type, kv.Key.name), kv => kv.Value);
using (var loader = new RuntimeUnityBuilder(parser, externalObjectMap))
using (var loader = new Vrm10Importer(parser, externalObjectMap))
{
// settings TextureImporters
foreach (var (key, textureInfo) in Vrm10MaterialImporter.EnumerateAllTexturesDistinct(parser))

View File

@@ -281,7 +281,7 @@ namespace UniVRM10
try
{
var converter = new UniVRM10.RuntimeVrmConverter();
var converter = new UniVRM10.ModelExporter();
var model = converter.ToModelFrom10(root);
if (HumanoidValidator.HasRotationOrScale(root))

View File

@@ -1,29 +0,0 @@
using System;
using UnityEngine;
using System.Linq;
using System.Collections.Generic;
namespace UniVRM10
{
public static class ComponentBuilder
{
#region Util
static (Transform, Mesh) GetTransformAndMesh(Transform t)
{
var skinnedMeshRenderer = t.GetComponent<SkinnedMeshRenderer>();
if (skinnedMeshRenderer != null)
{
return (t, skinnedMeshRenderer.sharedMesh);
}
var filter = t.GetComponent<MeshFilter>();
if (filter != null)
{
return (t, filter.sharedMesh);
}
return default;
}
#endregion
}
}

View File

@@ -1,23 +0,0 @@
using System.Collections.Generic;
namespace UniVRM10
{
public static class DictionaryExtensions
{
public static U GetOrDefault<T, U>(this Dictionary<T, U> d, T key)
{
if(key == null)
{
return default;
}
if(d.TryGetValue(key, out U value))
{
return value;;
}
else{
return default;
}
}
}
}

View File

@@ -7,7 +7,7 @@ namespace UniVRM10
{
public static class ExpressionExtensions
{
public static UniVRM10.MorphTargetBinding Build10(this MorphTargetBind bind, GameObject root, RuntimeUnityBuilder.ModelMap loader, VrmLib.Model model)
public static UniVRM10.MorphTargetBinding Build10(this MorphTargetBind bind, GameObject root, Vrm10Importer.ModelMap loader, VrmLib.Model model)
{
var libNode = model.Nodes[bind.Node.Value];
var node = loader.Nodes[libNode].transform;

View File

@@ -1,401 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Numerics;
using System.Runtime.InteropServices;
using UniGLTF;
using VrmLib;
namespace UniVRM10
{
public static class MeshAdapter
{
/// <summary>
/// VertexBufferはひとつでIndexBufferの参照が異なる
///
/// VertexBuffer
/// +----------------------------------+
/// | |
/// +----------------------------------+
/// A A A
/// | | |
/// +---------+--------+--------+
/// | submesh0|submesh1|submesh2|
/// +---------+--------+--------+
/// IndexBuffer
/// </summary>
public static Mesh SharedBufferFromGltf(this glTFMesh x, Vrm10Storage storage)
{
// 先頭を使う
return FromGltf(storage, x, x.primitives[0], true);
}
/// <summary>
/// IndexBuffer毎に異なるVertexBufferを参照する
///
/// VertexBuffer
/// +--------+ +--------+ +--------+
/// |0 | |1 | |2 |
/// +--------+ +--------+ +--------+
/// A A A
/// | | |
/// +---------+--------+--------+
/// | submesh0|submesh1|submesh2|
/// +---------+--------+--------+
/// IndexBuffer
/// </summary>
public static Mesh FromGltf(this glTFPrimitives primitive, Vrm10Storage storage, glTFMesh x)
{
return FromGltf(storage, x, primitive, false);
}
static Mesh FromGltf(Vrm10Storage storage, glTFMesh x, glTFPrimitives primitive, bool isShared)
{
var mesh = new Mesh((TopologyType)primitive.mode)
{
VertexBuffer = primitive.attributes.FromGltf(storage)
};
if (isShared)
{
// create joined index buffer
mesh.IndexBuffer = storage.CreateAccessor(x.primitives.Select(y => y.indices).ToArray());
}
else
{
mesh.IndexBuffer = storage.CreateAccessor(primitive.indices);
}
{
gltf_mesh_extras_targetNames.TryGet(x, out List<string> targetNames);
for (int i = 0; i < primitive.targets.Count; ++i)
{
var gltfTarget = primitive.targets[i];
string targetName = null;
{
targetName = targetNames[i];
}
var target = new MorphTarget(targetName)
{
VertexBuffer = gltfTarget.FromGltf(storage)
};
// validate count
foreach (var kv in target.VertexBuffer)
{
if (kv.Value.Count != mesh.VertexBuffer.Count)
{
throw new Exception();
}
}
mesh.MorphTargets.Add(target);
}
}
return mesh;
}
public static VertexBuffer FromGltf(this glTFAttributes attributes,
Vrm10Storage storage)
{
var b = new VertexBuffer();
if (storage.TryCreateAccessor(attributes.POSITION, out BufferAccessor position))
{
b.Add(VertexBuffer.PositionKey, position);
}
else
{
// position required
throw new Exception();
}
if (storage.TryCreateAccessor(attributes.NORMAL, out BufferAccessor normal)) b.Add(VertexBuffer.NormalKey, normal);
if (storage.TryCreateAccessor(attributes.COLOR_0, out BufferAccessor color)) b.Add(VertexBuffer.ColorKey, color);
if (storage.TryCreateAccessor(attributes.TEXCOORD_0, out BufferAccessor tex0)) b.Add(VertexBuffer.TexCoordKey, tex0);
if (storage.TryCreateAccessor(attributes.TEXCOORD_1, out BufferAccessor tex1)) b.Add(VertexBuffer.TexCoordKey2, tex1);
// if(storage.TryCreateAccessor(attributes.TANGENT, out BufferAccessor tangent))b.Add(VertexBuffer.TangentKey, tangent);
if (storage.TryCreateAccessor(attributes.WEIGHTS_0, out BufferAccessor weights)) b.Add(VertexBuffer.WeightKey, weights);
if (storage.TryCreateAccessor(attributes.JOINTS_0, out BufferAccessor joints)) b.Add(VertexBuffer.JointKey, joints);
return b;
}
public static VertexBuffer FromGltf(this gltfMorphTarget target, Vrm10Storage storage)
{
var b = new VertexBuffer();
storage.CreateBufferAccessorAndAdd(target.POSITION, b, VertexBuffer.PositionKey);
storage.CreateBufferAccessorAndAdd(target.NORMAL, b, VertexBuffer.NormalKey);
storage.CreateBufferAccessorAndAdd(target.TANGENT, b, VertexBuffer.TangentKey);
return b;
}
public static bool HasSameVertexBuffer(this glTFPrimitives lhs, glTFPrimitives rhs)
{
if (lhs.attributes.POSITION != rhs.attributes.POSITION) return false;
if (lhs.attributes.NORMAL != rhs.attributes.NORMAL) return false;
if (lhs.attributes.TEXCOORD_0 != rhs.attributes.TEXCOORD_0) return false;
if (lhs.attributes.TEXCOORD_1 != rhs.attributes.TEXCOORD_1) return false;
if (lhs.attributes.COLOR_0 != rhs.attributes.COLOR_0) return false;
if (lhs.attributes.WEIGHTS_0 != rhs.attributes.WEIGHTS_0) return false;
if (lhs.attributes.JOINTS_0 != rhs.attributes.JOINTS_0) return false;
return true;
}
public static bool AllPrimitivesHasSameVertexBuffer(this glTFMesh m)
{
if (m.primitives.Count <= 1)
{
return true;
}
var first = m.primitives[0];
for (int i = 1; i < m.primitives.Count; ++i)
{
if (!first.HasSameVertexBuffer(m.primitives[i]))
{
return false;
}
}
return true;
}
public static MeshGroup FromGltf(this glTFMesh x, Vrm10Storage storage)
{
var group = new MeshGroup(x.name);
if (x.primitives.Count == 1)
{
var primitive = x.primitives[0];
var mesh = primitive.FromGltf(storage, x);
var materialIndex = primitive.material;
mesh.Submeshes.Add(
new Submesh(0, mesh.IndexBuffer.Count, materialIndex));
group.Meshes.Add(mesh);
}
else if (!x.AllPrimitivesHasSameVertexBuffer())
{
int offset = 0;
foreach (var primitive in x.primitives)
{
var mesh = primitive.FromGltf(storage, x);
var materialIndex = primitive.material;
mesh.Submeshes.Add(
new Submesh(offset, mesh.IndexBuffer.Count, materialIndex));
offset += mesh.IndexBuffer.Count;
group.Meshes.Add(mesh);
}
}
else
{
// for VRM
var mesh = x.SharedBufferFromGltf(storage);
int offset = 0;
foreach (var primitive in x.primitives)
{
var materialIndex = primitive.material;
var count = storage.Gltf.accessors[primitive.indices].count;
mesh.Submeshes.Add(
new Submesh(offset, count, materialIndex));
offset += count;
}
group.Meshes.Add(mesh);
}
return group;
}
static void Vec3MinMax(ArraySegment<byte> bytes, glTFAccessor accessor)
{
var positions = SpanLike.Wrap<Vector3>(bytes);
var min = new Vector3(float.PositiveInfinity, float.PositiveInfinity, float.PositiveInfinity);
var max = new Vector3(float.NegativeInfinity, float.NegativeInfinity, float.NegativeInfinity);
foreach (var p in positions)
{
min = Vector3.Min(min, p);
max = Vector3.Max(max, p);
}
accessor.min = min.ToFloat3();
accessor.max = max.ToFloat3();
}
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);
}
}
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));
}
}
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;
}
}
}

View File

@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 18a0f7c989f911547aa4b75abb583b4c
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -1,12 +1,17 @@
using System;
using UnityEngine;
using UnityEngine.Rendering;
namespace UniVRM10
{
public static class MeshLoader
public static class MeshImporter
{
public static void LoadMesh(this Mesh mesh, VrmLib.Mesh src, VrmLib.Skin skin = null)
/// <summary>
/// VrmLib.Mesh => UnityEngine.Mesh
/// </summary>
/// <param name="mesh"></param>
/// <param name="src"></param>
/// <param name="skin"></param>
public static void LoadSharedMesh(UnityEngine.Mesh mesh, VrmLib.Mesh src, VrmLib.Skin skin = null)
{
mesh.vertices = src.VertexBuffer.Positions.GetSpan<Vector3>().ToArray();
mesh.normals = src.VertexBuffer.Normals?.GetSpan<Vector3>().ToArray();

View File

@@ -1,5 +1,5 @@
fileFormatVersion: 2
guid: 573f68f30d3f14a4cb8181b07981c6e0
guid: 98874f743a5ae754eb8382f3991a5646
MonoImporter:
externalObjects: {}
serializedVersion: 2

View File

@@ -0,0 +1,222 @@
using System;
using System.Collections.Generic;
using System.Linq;
using UniGLTF;
using VrmLib;
namespace UniVRM10
{
/// <summary>
/// GLTF -> VrmLib.MeshGroup
/// </summary>
public static class MeshReader
{
/// <summary>
/// VertexBufferはひとつでIndexBufferの参照が異なる
///
/// VertexBuffer
/// +----------------------------------+
/// | |
/// +----------------------------------+
/// A A A
/// | | |
/// +---------+--------+--------+
/// | submesh0|submesh1|submesh2|
/// +---------+--------+--------+
/// IndexBuffer
/// </summary>
static Mesh SharedBufferFromGltf(this glTFMesh x, Vrm10Storage storage)
{
// 先頭を使う
return FromGltf(storage, x, x.primitives[0], true);
}
/// <summary>
/// IndexBuffer毎に異なるVertexBufferを参照する
///
/// VertexBuffer
/// +--------+ +--------+ +--------+
/// |0 | |1 | |2 |
/// +--------+ +--------+ +--------+
/// A A A
/// | | |
/// +---------+--------+--------+
/// | submesh0|submesh1|submesh2|
/// +---------+--------+--------+
/// IndexBuffer
/// </summary>
static Mesh FromGltf(this glTFPrimitives primitive, Vrm10Storage storage, glTFMesh x)
{
return FromGltf(storage, x, primitive, false);
}
static Mesh FromGltf(Vrm10Storage storage, glTFMesh x, glTFPrimitives primitive, bool isShared)
{
var mesh = new Mesh((TopologyType)primitive.mode)
{
VertexBuffer = primitive.attributes.FromGltf(storage)
};
if (isShared)
{
// create joined index buffer
mesh.IndexBuffer = storage.CreateAccessor(x.primitives.Select(y => y.indices).ToArray());
}
else
{
mesh.IndexBuffer = storage.CreateAccessor(primitive.indices);
}
{
gltf_mesh_extras_targetNames.TryGet(x, out List<string> targetNames);
for (int i = 0; i < primitive.targets.Count; ++i)
{
var gltfTarget = primitive.targets[i];
string targetName = null;
{
targetName = targetNames[i];
}
var target = new MorphTarget(targetName)
{
VertexBuffer = gltfTarget.FromGltf(storage)
};
// validate count
foreach (var kv in target.VertexBuffer)
{
if (kv.Value.Count != mesh.VertexBuffer.Count)
{
throw new Exception();
}
}
mesh.MorphTargets.Add(target);
}
}
return mesh;
}
static VertexBuffer FromGltf(this glTFAttributes attributes,
Vrm10Storage storage)
{
var b = new VertexBuffer();
if (storage.TryCreateAccessor(attributes.POSITION, out BufferAccessor position))
{
b.Add(VertexBuffer.PositionKey, position);
}
else
{
// position required
throw new Exception();
}
if (storage.TryCreateAccessor(attributes.NORMAL, out BufferAccessor normal)) b.Add(VertexBuffer.NormalKey, normal);
if (storage.TryCreateAccessor(attributes.COLOR_0, out BufferAccessor color)) b.Add(VertexBuffer.ColorKey, color);
if (storage.TryCreateAccessor(attributes.TEXCOORD_0, out BufferAccessor tex0)) b.Add(VertexBuffer.TexCoordKey, tex0);
if (storage.TryCreateAccessor(attributes.TEXCOORD_1, out BufferAccessor tex1)) b.Add(VertexBuffer.TexCoordKey2, tex1);
// if(storage.TryCreateAccessor(attributes.TANGENT, out BufferAccessor tangent))b.Add(VertexBuffer.TangentKey, tangent);
if (storage.TryCreateAccessor(attributes.WEIGHTS_0, out BufferAccessor weights)) b.Add(VertexBuffer.WeightKey, weights);
if (storage.TryCreateAccessor(attributes.JOINTS_0, out BufferAccessor joints)) b.Add(VertexBuffer.JointKey, joints);
return b;
}
static VertexBuffer FromGltf(this gltfMorphTarget target, Vrm10Storage storage)
{
var b = new VertexBuffer();
storage.CreateBufferAccessorAndAdd(target.POSITION, b, VertexBuffer.PositionKey);
storage.CreateBufferAccessorAndAdd(target.NORMAL, b, VertexBuffer.NormalKey);
storage.CreateBufferAccessorAndAdd(target.TANGENT, b, VertexBuffer.TangentKey);
return b;
}
static bool HasSameVertexBuffer(this glTFPrimitives lhs, glTFPrimitives rhs)
{
if (lhs.attributes.POSITION != rhs.attributes.POSITION) return false;
if (lhs.attributes.NORMAL != rhs.attributes.NORMAL) return false;
if (lhs.attributes.TEXCOORD_0 != rhs.attributes.TEXCOORD_0) return false;
if (lhs.attributes.TEXCOORD_1 != rhs.attributes.TEXCOORD_1) return false;
if (lhs.attributes.COLOR_0 != rhs.attributes.COLOR_0) return false;
if (lhs.attributes.WEIGHTS_0 != rhs.attributes.WEIGHTS_0) return false;
if (lhs.attributes.JOINTS_0 != rhs.attributes.JOINTS_0) return false;
return true;
}
static bool AllPrimitivesHasSameVertexBuffer(this glTFMesh m)
{
if (m.primitives.Count <= 1)
{
return true;
}
var first = m.primitives[0];
for (int i = 1; i < m.primitives.Count; ++i)
{
if (!first.HasSameVertexBuffer(m.primitives[i]))
{
return false;
}
}
return true;
}
public static MeshGroup FromGltf(this glTFMesh x, Vrm10Storage storage)
{
var group = new MeshGroup(x.name);
if (x.primitives.Count == 1)
{
var primitive = x.primitives[0];
var mesh = primitive.FromGltf(storage, x);
var materialIndex = primitive.material;
mesh.Submeshes.Add(
new Submesh(0, mesh.IndexBuffer.Count, materialIndex));
group.Meshes.Add(mesh);
}
else if (!x.AllPrimitivesHasSameVertexBuffer())
{
int offset = 0;
foreach (var primitive in x.primitives)
{
var mesh = primitive.FromGltf(storage, x);
var materialIndex = primitive.material;
mesh.Submeshes.Add(
new Submesh(offset, mesh.IndexBuffer.Count, materialIndex));
offset += mesh.IndexBuffer.Count;
group.Meshes.Add(mesh);
}
}
else
{
//
// obsolete
//
// for VRM
var mesh = x.SharedBufferFromGltf(storage);
int offset = 0;
foreach (var primitive in x.primitives)
{
var materialIndex = primitive.material;
var count = storage.Gltf.accessors[primitive.indices].count;
mesh.Submeshes.Add(
new Submesh(offset, count, materialIndex));
offset += count;
}
group.Meshes.Add(mesh);
}
return group;
}
}
}

View File

@@ -1,5 +1,5 @@
fileFormatVersion: 2
guid: a6cc4e6978a4fdf40999cc13aaa472fe
guid: 2f4647a633dfc844eb1b8e7563addba2
MonoImporter:
externalObjects: {}
serializedVersion: 2

View File

@@ -0,0 +1,198 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Numerics;
using UniGLTF;
using VrmLib;
namespace UniVRM10
{
/// <summary>
/// VrmLib.MeshGroup => GLTF
/// </summary>
public static class MeshWriter
{
static void Vec3MinMax(ArraySegment<byte> bytes, glTFAccessor accessor)
{
var positions = SpanLike.Wrap<Vector3>(bytes);
var min = new Vector3(float.PositiveInfinity, float.PositiveInfinity, float.PositiveInfinity);
var max = new Vector3(float.NegativeInfinity, float.NegativeInfinity, float.NegativeInfinity);
foreach (var p in positions)
{
min = Vector3.Min(min, p);
max = Vector3.Max(max, p);
}
accessor.min = min.ToFloat3();
accessor.max = max.ToFloat3();
}
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);
}
}
static void ExportMesh(this VrmLib.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));
}
}
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;
}
}
}

View File

@@ -1,5 +1,5 @@
fileFormatVersion: 2
guid: d80d88b180871234ea03afcc24e118da
guid: f71759cba771a95449d96df3fa37b115
MonoImporter:
externalObjects: {}
serializedVersion: 2

View File

@@ -8,7 +8,10 @@ using VrmLib;
namespace UniVRM10
{
public class RuntimeVrmConverter
/// <summary>
/// UnityEngine.GameObject hierarchy => GLTF.Nodes, GLTF.Meshes, GLTF.Skins
/// </summary>
public class ModelExporter
{
public VrmLib.Model Model;
@@ -16,15 +19,16 @@ namespace UniVRM10
public List<UnityEngine.Material> Materials = new List<UnityEngine.Material>();
public Dictionary<UnityEngine.Mesh, VrmLib.MeshGroup> Meshes = new Dictionary<UnityEngine.Mesh, VrmLib.MeshGroup>();
#region Export 1.0
/// <summary>
/// metaObject が null のときは、root から取得する
/// GameObject to VrmLib.Model
/// </summary>
/// <param name="root"></param>
/// <returns></returns>
public VrmLib.Model ToModelFrom10(GameObject root)
{
Model = new VrmLib.Model(VrmLib.Coordinates.Unity);
ToGlbModel(root);
Export(root);
// humanoid
{
@@ -48,7 +52,7 @@ namespace UniVRM10
return Model;
}
public VrmLib.Model ToGlbModel(GameObject root)
VrmLib.Model Export(GameObject root)
{
if (Model == null)
{
@@ -123,9 +127,6 @@ namespace UniVRM10
return Model;
}
#endregion
private static void CreateNodes(
Transform parentTransform,

View File

@@ -1,5 +1,5 @@
fileFormatVersion: 2
guid: 936824d3022642f40a65a003e0ee3b83
guid: 096d30ddf9b2aee4f96746f05610d75f
MonoImporter:
externalObjects: {}
serializedVersion: 2

View File

@@ -1,12 +1,15 @@
using System;
using System.IO;
using System.Linq;
using VrmLib;
namespace UniVRM10
{
public static class ModelLoader
/// <summary>
/// GLTF => VrmLib.Model
/// </summary>
public static class ModelReader
{
public static Model Load(Vrm10Storage storage, string rootName)
static Model Load(Vrm10Storage storage, string rootName)
{
if (storage == null)
{
@@ -70,5 +73,13 @@ namespace UniVRM10
return model;
}
public static Model Read(UniGLTF.GltfParser parser)
{
var storage = new Vrm10Storage(parser);
var model = Load(storage, Path.GetFileName(parser.TargetPath));
model.ConvertCoordinate(Coordinates.Unity);
return model;
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: fb219f8797f990649a7531dbb96d3305
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -1,11 +0,0 @@
fileFormatVersion: 2
guid: 42576b7471f4a4e4fb25c1815a92f44b
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -1,11 +0,0 @@
fileFormatVersion: 2
guid: 962f584a4519d62419f01b8151f99169
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -1,11 +0,0 @@
fileFormatVersion: 2
guid: 6b619b24f8ac1494290458193ebca6aa
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -128,7 +128,7 @@ namespace UniVRM10
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)
public void Export(GameObject root, Model model, ModelExporter converter, ExportArgs option, Func<Texture2D, (byte[], string)> getTextureBytes, VRM10MetaObject metaObject = null)
{
ExportAsset(model);
@@ -211,7 +211,7 @@ namespace UniVRM10
/// <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)
int? thumbnailIndex) ExportVrm(GameObject root, Model model, ModelExporter converter, VRM10MetaObject meta)
{
var vrmController = root?.GetComponent<VRM10Controller>();
@@ -307,7 +307,7 @@ namespace UniVRM10
return joint;
}
UniGLTF.Extensions.VRMC_springBone.VRMC_springBone ExportSpringBone(VRM10Controller vrmController, Model model, RuntimeVrmConverter converter)
UniGLTF.Extensions.VRMC_springBone.VRMC_springBone ExportSpringBone(VRM10Controller vrmController, Model model, ModelExporter converter)
{
var springBone = new UniGLTF.Extensions.VRMC_springBone.VRMC_springBone
{
@@ -353,7 +353,7 @@ namespace UniVRM10
return springBone;
}
void ExportConstraints(VRM10Controller vrmController, Model model, RuntimeVrmConverter converter)
void ExportConstraints(VRM10Controller vrmController, Model model, ModelExporter converter)
{
var constraints = vrmController.GetComponentsInChildren<VRM10Constraint>();
foreach (var constraint in constraints)
@@ -395,7 +395,7 @@ namespace UniVRM10
};
}
static UniGLTF.Extensions.VRMC_node_constraint.VRMC_node_constraint ExportPostionConstraint(VRM10PositionConstraint c, Model model, RuntimeVrmConverter converter)
static UniGLTF.Extensions.VRMC_node_constraint.VRMC_node_constraint ExportPostionConstraint(VRM10PositionConstraint c, Model model, ModelExporter converter)
{
return new UniGLTF.Extensions.VRMC_node_constraint.VRMC_node_constraint
{
@@ -413,7 +413,7 @@ namespace UniVRM10
};
}
static UniGLTF.Extensions.VRMC_node_constraint.VRMC_node_constraint ExportRotationConstraint(VRM10RotationConstraint c, Model model, RuntimeVrmConverter converter)
static UniGLTF.Extensions.VRMC_node_constraint.VRMC_node_constraint ExportRotationConstraint(VRM10RotationConstraint c, Model model, ModelExporter converter)
{
return new UniGLTF.Extensions.VRMC_node_constraint.VRMC_node_constraint
{
@@ -431,7 +431,7 @@ namespace UniVRM10
};
}
static UniGLTF.Extensions.VRMC_node_constraint.VRMC_node_constraint ExportAimConstraint(VRM10AimConstraint c, Model model, RuntimeVrmConverter converter)
static UniGLTF.Extensions.VRMC_node_constraint.VRMC_node_constraint ExportAimConstraint(VRM10AimConstraint c, Model model, ModelExporter converter)
{
return new UniGLTF.Extensions.VRMC_node_constraint.VRMC_node_constraint
{
@@ -457,7 +457,7 @@ namespace UniVRM10
};
}
void ExportFirstPerson(UniGLTF.Extensions.VRMC_vrm.VRMC_vrm vrm, VRM10Controller vrmController, Model model, RuntimeVrmConverter converter)
void ExportFirstPerson(UniGLTF.Extensions.VRMC_vrm.VRMC_vrm vrm, VRM10Controller vrmController, Model model, ModelExporter converter)
{
if (vrmController?.FirstPerson == null)
{
@@ -540,7 +540,7 @@ namespace UniVRM10
};
}
void ExportExpression(UniGLTF.Extensions.VRMC_vrm.VRMC_vrm vrm, VRM10Controller vrmController, Model model, RuntimeVrmConverter converter)
void ExportExpression(UniGLTF.Extensions.VRMC_vrm.VRMC_vrm vrm, VRM10Controller vrmController, Model model, ModelExporter converter)
{
if (vrmController?.Expression?.ExpressionAvatar?.Clips == null)
{
@@ -779,7 +779,7 @@ namespace UniVRM10
}
// ヒエラルキーからジオメトリーを収集
var converter = new UniVRM10.RuntimeVrmConverter();
var converter = new UniVRM10.ModelExporter();
var model = converter.ToModelFrom10(go);
// 右手系に変換

View File

@@ -1,5 +1,6 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Threading.Tasks;
using UniGLTF;
@@ -12,7 +13,7 @@ namespace UniVRM10
/// <summary>
/// VrmLib.Model から UnityPrefab を構築する
/// </summary>
public class RuntimeUnityBuilder : UniGLTF.ImporterContext
public class Vrm10Importer : UniGLTF.ImporterContext
{
readonly Model m_model;
@@ -20,11 +21,15 @@ namespace UniVRM10
IDictionary<SubAssetKey, UnityEngine.Object> m_externalMap;
public RuntimeUnityBuilder(UniGLTF.GltfParser parser, IDictionary<SubAssetKey, UnityEngine.Object> externalObjectMap = null)
: base(parser, externalObjectMap.Select(kv => (kv.Key.Name, kv.Value)))
public Vrm10Importer(UniGLTF.GltfParser parser, IDictionary<SubAssetKey, UnityEngine.Object> externalObjectMap = null)
: base(parser, externalObjectMap?.Select(kv => (kv.Key.Name, kv.Value)))
{
m_externalMap = externalObjectMap;
m_model = VrmLoader.CreateVrmModel(parser);
if (m_externalMap == null)
{
m_externalMap = new Dictionary<SubAssetKey, UnityEngine.Object>();
}
m_model = ModelReader.Read(parser);
// for `VRMC_materials_mtoon`
this.GltfMaterialImporter.GltfMaterialParamProcessors.Insert(0, Vrm10MaterialImporter.TryCreateParam);
@@ -140,7 +145,7 @@ namespace UniVRM10
// submesh 方式
var mesh = new UnityEngine.Mesh();
mesh.name = src.Name;
mesh.LoadMesh(src.Meshes[0], src.Skin);
MeshImporter.LoadSharedMesh(mesh, src.Meshes[0], src.Skin);
m_map.Meshes.Add(src, mesh);
Meshes.Add(new MeshWithMaterials
{

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: ae61d167cf541b44c8645b3c864390f0
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -1,20 +0,0 @@
using System.IO;
using VrmLib;
using UniGLTF;
namespace UniVRM10
{
/// <summary>
/// utility for load VrmLib Model from byte[]
/// </summary>
public static class VrmLoader
{
public static Model CreateVrmModel(GltfParser parser)
{
var storage = new Vrm10Storage(parser);
var model = ModelLoader.Load(storage, Path.GetFileName(parser.TargetPath));
model.ConvertCoordinate(Coordinates.Unity);
return model;
}
}
}

View File

@@ -1,11 +0,0 @@
fileFormatVersion: 2
guid: ef069ccddc286cc4e930ba33447b0094
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -16,7 +16,7 @@ public class Sample : MonoBehaviour
var parser = new GltfParser();
parser.Parse(path.FullName, bytes);
using (var loader = new RuntimeUnityBuilder(parser))
using (var loader = new Vrm10Importer(parser))
{
loader.Load();
loader.ShowMeshes();

View File

@@ -39,7 +39,7 @@ namespace UniVRM10.Test
private (GameObject, IReadOnlyList<VRMShaders.MaterialFactory.MaterialLoadInfo>) ToUnity(GltfParser parser)
{
// Model => Unity
using (var loader = new RuntimeUnityBuilder(parser))
using (var loader = new Vrm10Importer(parser))
{
loader.Load();
loader.DisposeOnGameObjectDestroyed();
@@ -49,7 +49,7 @@ namespace UniVRM10.Test
private Model ToVrmModel(GameObject root)
{
var exporter = new UniVRM10.RuntimeVrmConverter();
var exporter = new UniVRM10.ModelExporter();
var model = exporter.ToModelFrom10(root);
model.ConvertCoordinate(VrmLib.Coordinates.Vrm1, ignoreVrm: false);

View File

@@ -1,9 +1,7 @@
using System;
using System.IO;
using System.IO;
using NUnit.Framework;
using UniGLTF;
using UnityEngine;
using UnityEngine.TestTools;
using VRMShaders;
namespace UniVRM10.Test
@@ -17,13 +15,13 @@ namespace UniVRM10.Test
var parser = new GltfParser();
parser.Parse("migrated", bytes);
var model = UniVRM10.VrmLoader.CreateVrmModel(parser);
var model = ModelReader.Read(parser);
return model;
}
GameObject BuildGameObject(GltfParser parser, bool showMesh)
{
using (var loader = new RuntimeUnityBuilder(parser))
using (var loader = new Vrm10Importer(parser))
{
loader.Load();
if (showMesh)