Merge pull request #1736 from ousttrue/fix/pass_gltf_validator

[exporter] fix for gltf_validator
This commit is contained in:
ousttrue 2022-07-12 16:44:58 +09:00 committed by GitHub
commit 085c33e774
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
17 changed files with 269 additions and 90 deletions

View File

@ -75,6 +75,10 @@ namespace UniGLTF
return new StringKeyDictionarySerialization(t,
GetSerialization(t.GetGenericArguments()[1], path, attr, prefix));
}
else if (t.IsGenericType && t.GetGenericTypeDefinition() == typeof(Nullable<>))
{
return new NullableSerialization(t.GetGenericArguments()[0], path, attr, prefix);
}
// GetCollectionType(fi.FieldType, out suffix, out t);
if (t == typeof(sbyte))

View File

@ -0,0 +1,44 @@
using System;
using System.IO;
namespace UniGLTF
{
public class NullableSerialization : IValueSerialization
{
public NullableSerialization(Type t, string path, JsonSchemaAttribute attr, string prefix)
{
if (t != typeof(int))
{
throw new NotImplementedException();
}
}
public Type ValueType => typeof(Int32);
public bool IsInline => true;
public string CreateSerializationCondition(string argName, JsonSchemaAttribute t)
{
return $"{argName}.HasValue";
}
public string GenerateSerializerCall(string callName, string argName)
{
return $"f.Value({argName}.Value)";
}
public void GenerateSerializer(StreamWriter writer, string callName)
{
throw new NotImplementedException();
}
public string GenerateDeserializerCall(string callName, string argName)
{
return argName + ".GetInt32()";
}
public void GenerateDeserializer(StreamWriter writer, string callName)
{
throw new NotImplementedException();
}
}
}

View File

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

View File

@ -272,14 +272,14 @@ public static void Serialize_gltf_accessors_ITEM(JsonFormatter f, glTFAccessor v
f.BeginMap();
if(value.bufferView>=0){
if(value.bufferView.HasValue){
f.Key("bufferView");
f.Value(value.bufferView);
f.Value(value.bufferView.Value);
}
if(value.byteOffset>=0){
if(value.byteOffset.HasValue){
f.Key("byteOffset");
f.Value(value.byteOffset);
f.Value(value.byteOffset.Value);
}
if(!string.IsNullOrEmpty(value.type)){

View File

@ -93,10 +93,10 @@ namespace UniGLTF
public class glTFAccessor
{
[JsonSchema(Minimum = 0)]
public int bufferView = -1;
public int? bufferView;
[JsonSchema(Minimum = 0, Dependencies = new string[] { "bufferView" })]
public int byteOffset;
public int? byteOffset;
[JsonSchema(Required = true, EnumValues = new object[] { "SCALAR", "VEC2", "VEC3", "VEC4", "MAT2", "MAT3", "MAT4" }, EnumSerializationType = EnumSerializationType.AsString)]
public string type;

View File

@ -115,7 +115,7 @@ namespace UniGLTF
var accessorIndex = Gltf.accessors.Count;
Gltf.accessors.Add(new glTFAccessor
{
byteOffset = 0,
byteOffset = default,
componentType = glTFExtensions.GetComponentType<T>(),
type = glTFExtensions.GetAccessorType<T>(),
count = accessorCount,

View File

@ -166,7 +166,7 @@ namespace UniGLTF
NativeArray<T> GetTypedFromAccessor<T>(glTFAccessor accessor, glTFBufferView view) where T : struct
{
var bytes = GetBytesFromBufferView(view);
return bytes.GetSubArray(accessor.byteOffset, bytes.Length - accessor.byteOffset).Reinterpret<T>(1).GetSubArray(0, accessor.count);
return bytes.GetSubArray(accessor.byteOffset.GetValueOrDefault(), bytes.Length - accessor.byteOffset.GetValueOrDefault()).Reinterpret<T>(1).GetSubArray(0, accessor.count);
}
/// <summary>
@ -208,8 +208,8 @@ namespace UniGLTF
public BufferAccessor GetIndicesFromAccessorIndex(int accessorIndex)
{
var accessor = GLTF.accessors[accessorIndex];
var view = GLTF.bufferViews[accessor.bufferView];
return GetIntIndicesFromView(view, accessor.count, accessor.byteOffset, accessor.componentType);
var view = GLTF.bufferViews[accessor.bufferView.Value];
return GetIntIndicesFromView(view, accessor.count, accessor.byteOffset.GetValueOrDefault(), accessor.componentType);
}
public NativeArray<T> GetArrayFromAccessor<T>(int accessorIndex) where T : struct
@ -218,8 +218,8 @@ namespace UniGLTF
if (vertexAccessor.count <= 0) return NativeArrayManager.CreateNativeArray<T>(0);
var result = (vertexAccessor.bufferView != -1)
? GetTypedFromAccessor<T>(vertexAccessor, GLTF.bufferViews[vertexAccessor.bufferView])
var result = (vertexAccessor.bufferView.HasValue)
? GetTypedFromAccessor<T>(vertexAccessor, GLTF.bufferViews[vertexAccessor.bufferView.Value])
: NativeArrayManager.CreateNativeArray<T>(vertexAccessor.count)
;
@ -277,11 +277,11 @@ namespace UniGLTF
var bufferCount = vertexAccessor.count * vertexAccessor.TypeCount;
NativeArray<float> result = default;
if (vertexAccessor.bufferView != -1)
if (vertexAccessor.bufferView.HasValue)
{
var view = GLTF.bufferViews[vertexAccessor.bufferView];
var view = GLTF.bufferViews[vertexAccessor.bufferView.Value];
var segment = GetBytesFromBuffer(view.buffer);
result = segment.GetSubArray(view.byteOffset + vertexAccessor.byteOffset, vertexAccessor.count * 4 * vertexAccessor.TypeCount).Reinterpret<float>(1);
result = segment.GetSubArray(view.byteOffset + vertexAccessor.byteOffset.GetValueOrDefault(), vertexAccessor.count * 4 * vertexAccessor.TypeCount).Reinterpret<float>(1);
}
else
{

View File

@ -9,7 +9,7 @@ namespace UniGLTF
{
public static class GltfJsonUtil
{
const string EXTENSION_USED_KEY = "extensionUsed";
public const string EXTENSION_USED_KEY = "extensionsUsed";
/// <summary>
/// JsonPath を 再帰的に列挙する
@ -96,14 +96,14 @@ namespace UniGLTF
return false;
}
static void CopyJson(IReadOnlyList<string> extensionUsed, JsonFormatter dst, JsonNode src, int level)
static void CopyJson(IReadOnlyList<string> extensionsUsed, JsonFormatter dst, JsonNode src, int level)
{
if (src.IsArray())
{
dst.BeginList();
foreach (var v in src.ArrayItems())
{
CopyJson(extensionUsed, dst, v, level + 1);
CopyJson(extensionsUsed, dst, v, level + 1);
}
dst.EndList();
}
@ -119,7 +119,7 @@ namespace UniGLTF
var key = kv.Key.GetString();
if (key == EXTENSION_USED_KEY)
{
if (extensionUsed.Count == 0)
if (extensionsUsed.Count == 0)
{
// skip
}
@ -128,7 +128,7 @@ namespace UniGLTF
dst.Key(key);
// replace
dst.BeginList();
foreach (var ex in extensionUsed)
foreach (var ex in extensionsUsed)
{
dst.Value(ex);
}
@ -140,15 +140,15 @@ namespace UniGLTF
else
{
dst.Key(key);
CopyJson(extensionUsed, dst, kv.Value, level + 1);
CopyJson(extensionsUsed, dst, kv.Value, level + 1);
}
}
if (!done && level == 0 && extensionUsed.Count > 0)
if (!done && level == 0 && extensionsUsed.Count > 0)
{
// add
dst.Key(EXTENSION_USED_KEY);
dst.BeginList();
foreach (var ex in extensionUsed)
foreach (var ex in extensionsUsed)
{
dst.Value(ex);
}
@ -162,7 +162,7 @@ namespace UniGLTF
foreach (var kv in src.ObjectItems())
{
dst.Key(kv.Key.GetUtf8String());
CopyJson(extensionUsed, dst, kv.Value, level + 1);
CopyJson(extensionsUsed, dst, kv.Value, level + 1);
}
dst.EndMap();
}
@ -177,7 +177,7 @@ namespace UniGLTF
/// <summary>
/// https://github.com/KhronosGroup/glTF/blob/main/specification/2.0/schema/glTF.schema.json
///
/// extensionUsed の更新を各拡張自身にやらせるのは無駄だし、手動でコントロールするのも間違いの元である。
/// extensionsUsed の更新を各拡張自身にやらせるのは無駄だし、手動でコントロールするのも間違いの元である。
/// 完成品の JSON から後付けで作ることにした。
///
/// * Exporter しか使わない処理なので、GC, 処理速度は気にしてない

View File

@ -48,6 +48,8 @@ namespace UniGLTF
positionAccessorIndex = data.ExtendSparseBufferAndGetAccessorIndex(accessorCount,
sparseIndices.Select(x => positions[x]).ToArray(), sparseIndices, sparseIndicesViewIndex,
glBufferTarget.NONE);
data.Gltf.accessors[positionAccessorIndex].min = positions.Aggregate(positions[0], (a, b) => new Vector3(Mathf.Min(a.x, b.x), Math.Min(a.y, b.y), Mathf.Min(a.z, b.z))).ToArray();
data.Gltf.accessors[positionAccessorIndex].max = positions.Aggregate(positions[0], (a, b) => new Vector3(Mathf.Max(a.x, b.x), Math.Max(a.y, b.y), Mathf.Max(a.z, b.z))).ToArray();
}
// normals

View File

@ -51,18 +51,6 @@ namespace UniGLTF
return new MaterialExporter();
}
/// <summary>
/// このエクスポーターがサポートするExtension
/// </summary>
protected virtual IEnumerable<string> ExtensionUsed
{
get
{
yield return glTF_KHR_materials_unlit.ExtensionName;
yield return glTF_KHR_texture_transform.ExtensionName;
}
}
protected ITextureExporter TextureExporter => _textureExporter;
private TextureExporter _textureExporter;
@ -86,8 +74,6 @@ namespace UniGLTF
{
_data = data;
_gltf.extensionsUsed.AddRange(ExtensionUsed);
_gltf.asset = new glTFAssets
{
generator = "UniGLTF-" + UniGLTFVersion.VERSION,
@ -353,7 +339,7 @@ namespace UniGLTF
}
/// <summary>
/// GlbLowPevelParser.FixNameUnique で付与した Suffix を remove
/// GlbLowLevelParser.FixNameUnique で付与した Suffix を remove
/// </summary>
public static void FixName(glTF gltf)
{

View File

@ -7,7 +7,7 @@ namespace UniGLTF
public class GltfJsonUtilTests
{
[Test]
public void Update_extensionUsed()
public void Update_extensionsUsed()
{
var dst = GltfJsonUtil.FindUsedExtensionsAndUpdateJson(@"{
""asset"": {
@ -44,11 +44,11 @@ namespace UniGLTF
var parsed = dst.ParseAsJson();
Assert.AreEqual(new string[] { "KHR_materials_unlit" },
parsed["extensionUsed"].ArrayItems().Select(x => x.GetString()).ToArray());
parsed[GltfJsonUtil.EXTENSION_USED_KEY].ArrayItems().Select(x => x.GetString()).ToArray());
}
[Test]
public void Replace_extensionUsed()
public void Replace_extensionsUsed()
{
var dst = GltfJsonUtil.FindUsedExtensionsAndUpdateJson(@"{
""asset"": {
@ -63,7 +63,7 @@ namespace UniGLTF
]
}
],
""extensionUsed"": [""dummy""],
""extensionsUsed"": [""dummy""],
""materials"": [
{
""pbrMetallicRoughness"": {
@ -86,11 +86,11 @@ namespace UniGLTF
var parsed = dst.ParseAsJson();
Assert.AreEqual(new string[] { "KHR_materials_unlit" },
parsed["extensionUsed"].ArrayItems().Select(x => x.GetString()).ToArray());
parsed[GltfJsonUtil.EXTENSION_USED_KEY].ArrayItems().Select(x => x.GetString()).ToArray());
}
[Test]
public void Empty_extensionUsed()
public void Empty_extensionsUsed()
{
var dst = GltfJsonUtil.FindUsedExtensionsAndUpdateJson(@"{
""asset"": {
@ -105,7 +105,7 @@ namespace UniGLTF
]
}
],
""extensionUsed"": [""dummy""] ,
""extensionsUsed"": [""dummy""] ,
""materials"": [
{
""pbrMetallicRoughness"": {
@ -123,11 +123,11 @@ namespace UniGLTF
}");
var parsed = dst.ParseAsJson();
Assert.False(parsed.ContainsKey("extensionUsed"));
Assert.False(parsed.ContainsKey(GltfJsonUtil.EXTENSION_USED_KEY));
}
[Test]
public void Empty2_extensionUsed()
public void Empty2_extensionsUsed()
{
var dst = GltfJsonUtil.FindUsedExtensionsAndUpdateJson(@"{
""asset"": {
@ -156,12 +156,11 @@ namespace UniGLTF
""name"": ""Red""
}
],
""extensionUsed"": [""dummy""]
""extensionsUsed"": [""dummy""]
}");
var parsed = dst.ParseAsJson();
Assert.False(parsed.ContainsKey("extensionUsed"));
Assert.False(parsed.ContainsKey(GltfJsonUtil.EXTENSION_USED_KEY));
}
}
}

View File

@ -18,5 +18,27 @@ namespace UniVRM10
index = value;
return true;
}
public static bool TryGetValidIndex(this int? value, int count, out int index)
{
if (!value.HasValue)
{
index = -1;
return false;
}
if (value < 0)
{
index = -1;
return false;
}
if (value >= count)
{
index = -1;
return false;
}
index = value.Value;
return true;
}
}
}

View File

@ -136,7 +136,7 @@ namespace UniVRM10
{
var buffer = m_data.Bin;
var byteSize = accessor.CalcByteSize();
bytes = m_data.Bin.GetSubArray(view.byteOffset, view.byteLength).GetSubArray(accessor.byteOffset, byteSize);
bytes = m_data.Bin.GetSubArray(view.byteOffset, view.byteLength).GetSubArray(accessor.byteOffset.GetValueOrDefault(), byteSize);
}
}
@ -226,7 +226,7 @@ namespace UniVRM10
bool AccessorsIsContinuous(int[] accessorIndices)
{
var firstAccessor = Gltf.accessors[accessorIndices[0]];
var firstView = Gltf.bufferViews[firstAccessor.bufferView];
var firstView = Gltf.bufferViews[firstAccessor.bufferView.Value];
var start = firstView.byteOffset + firstAccessor.byteOffset;
var pos = start;
foreach (var i in accessorIndices)
@ -241,7 +241,7 @@ namespace UniVRM10
return false;
}
var view = Gltf.bufferViews[current.bufferView];
var view = Gltf.bufferViews[current.bufferView.Value];
if (pos != view.byteOffset + current.byteOffset)
{
return false;
@ -267,8 +267,8 @@ namespace UniVRM10
{
// IndexBufferが連続して格納されている => Slice でいける
var firstAccessor = Gltf.accessors[accessorIndices[0]];
var firstView = Gltf.bufferViews[firstAccessor.bufferView];
var start = firstView.byteOffset + firstAccessor.byteOffset;
var firstView = Gltf.bufferViews[firstAccessor.bufferView.Value];
var start = firstView.byteOffset + firstAccessor.byteOffset.GetValueOrDefault();
if (!firstView.buffer.TryGetValidIndex(Gltf.buffers.Count, out int firstViewBufferIndex))
{
throw new Exception();
@ -295,14 +295,14 @@ namespace UniVRM10
{
throw new ArgumentException($"accessor.type: {accessor.type}");
}
var view = Gltf.bufferViews[accessor.bufferView];
var view = Gltf.bufferViews[accessor.bufferView.Value];
if (!view.buffer.TryGetValidIndex(Gltf.buffers.Count, out int viewBufferIndex))
{
throw new Exception();
}
var buffer = Gltf.buffers[viewBufferIndex];
var bin = GetBufferBytes(buffer);
var start = view.byteOffset + accessor.byteOffset;
var start = view.byteOffset + accessor.byteOffset.GetValueOrDefault();
var bytes = bin.GetSubArray(start, accessor.count * accessor.GetStride());
var dst = indices.Reinterpret<Int32>(1).GetSubArray(offset, accessor.count);
offset += accessor.count;

View File

@ -41,17 +41,45 @@ namespace UniVRM10
return new MeshUpdater(data).Update(model);
}
int AddBuffer(NativeArray<byte> bytes)
int AddBuffer(NativeArray<byte> bytes, glBufferTarget target)
{
var bufferView = _buffer.Extend(bytes);
var bufferView = _buffer.Extend(bytes, target);
var index = _bufferViews.Count;
_bufferViews.Add(bufferView);
// padding for 4byte alignment
var mod = bytes.Length % 4;
if (mod != 0)
{
_buffer.Extend(new ArraySegment<byte>(new byte[4 - mod]));
}
return index;
}
int AddAccessor<T>(NativeArray<T> span) where T : struct
static (float[], float[]) GetMinMax(NativeArray<Vector3> v)
{
var bufferViewIndex = AddBuffer(span.Reinterpret<byte>(Marshal.SizeOf<T>()));
var minX = float.PositiveInfinity;
var minY = float.PositiveInfinity;
var minZ = float.PositiveInfinity;
var maxX = float.NegativeInfinity;
var maxY = float.NegativeInfinity;
var maxZ = float.NegativeInfinity;
for (int i = 0; i < v.Length; ++i)
{
if (v[i].x < minX) minX = v[i].x;
if (v[i].y < minY) minY = v[i].y;
if (v[i].z < minZ) minZ = v[i].z;
if (v[i].x > maxX) maxX = v[i].x;
if (v[i].y > maxY) maxY = v[i].y;
if (v[i].z > maxZ) maxZ = v[i].z;
}
return (new float[] { minX, minY, minZ }, new float[] { maxX, maxY, maxZ });
}
int AddAccessor<T>(NativeArray<T> span, glBufferTarget target, bool minMaxBounds) where T : struct
{
var bufferViewIndex = AddBuffer(span.Reinterpret<byte>(Marshal.SizeOf<T>()), target);
var accessor = new glTFAccessor
{
bufferView = bufferViewIndex,
@ -60,18 +88,27 @@ namespace UniVRM10
componentType = glTFExtensions.GetComponentType<T>(),
type = glTFExtensions.GetAccessorType<T>(),
};
if (minMaxBounds)
{
if (span is NativeArray<Vector3> positions)
{
var (min, max) = GetMinMax(positions);
accessor.min = min;
accessor.max = max;
}
}
var index = _accessors.Count;
_accessors.Add(accessor);
return index;
}
int? AddAccessor<T>(BufferAccessor buffer) where T : struct
int? AddAccessor<T>(BufferAccessor buffer, glBufferTarget target, bool minMaxBounds) where T : struct
{
if (buffer == null)
{
return default;
}
return AddAccessor(buffer.GetSpan<T>());
return AddAccessor(buffer.GetSpan<T>(), target, minMaxBounds);
}
struct MorphAccessor
@ -93,7 +130,7 @@ namespace UniVRM10
foreach (var image in gltf.images)
{
var bytes = _data.GetBytesFromBufferView(image.bufferView);
image.bufferView = AddBuffer(bytes);
image.bufferView = AddBuffer(bytes, default);
}
// copy mesh
@ -130,7 +167,7 @@ namespace UniVRM10
if (skinIndex == -1)
{
skinIndex = gltf.skins.Count;
gltfSkin.inverseBindMatrices = AddAccessor(node.MeshGroup.Skin.InverseMatrices.GetSpan<Matrix4x4>());
gltfSkin.inverseBindMatrices = AddAccessor(node.MeshGroup.Skin.InverseMatrices.GetSpan<Matrix4x4>(), default, false);
gltf.skins.Add(gltfSkin);
}
else
@ -169,6 +206,7 @@ namespace UniVRM10
}
// replace
gltf.buffers[0].byteLength = _buffer.Bytes.Count;
gltf.bufferViews = _bufferViews;
gltf.accessors = _accessors;
@ -216,20 +254,20 @@ namespace UniVRM10
default:
throw new NotImplementedException();
}
var position = AddAccessor<Vector3>(mesh.VertexBuffer.Positions);
var normal = AddAccessor<Vector3>(mesh.VertexBuffer.Normals);
var uv = AddAccessor<Vector2>(mesh.VertexBuffer.TexCoords);
var weights = AddAccessor<Vector4>(mesh.VertexBuffer.Weights);
var joints = AddAccessor<UShort4>(mesh.VertexBuffer.Joints);
var color = AddAccessor<Vector4>(mesh.VertexBuffer.Colors);
var position = AddAccessor<Vector3>(mesh.VertexBuffer.Positions, glBufferTarget.ARRAY_BUFFER, true);
var normal = AddAccessor<Vector3>(mesh.VertexBuffer.Normals, glBufferTarget.ARRAY_BUFFER, false);
var uv = AddAccessor<Vector2>(mesh.VertexBuffer.TexCoords, glBufferTarget.ARRAY_BUFFER, false);
var weights = AddAccessor<Vector4>(mesh.VertexBuffer.Weights, glBufferTarget.ARRAY_BUFFER, false);
var joints = AddAccessor<UShort4>(mesh.VertexBuffer.Joints, glBufferTarget.ARRAY_BUFFER, false);
var color = AddAccessor<Vector4>(mesh.VertexBuffer.Colors, glBufferTarget.ARRAY_BUFFER, false);
var morphTargets = new MorphAccessor[] { };
if (mesh.MorphTargets != null)
{
morphTargets = mesh.MorphTargets.Select(x => new MorphAccessor
{
Position = AddAccessor<Vector3>(x.VertexBuffer.Positions),
Normal = AddAccessor<Vector3>(x.VertexBuffer.Normals),
Position = AddAccessor<Vector3>(x.VertexBuffer.Positions, glBufferTarget.ARRAY_BUFFER, true),
Normal = AddAccessor<Vector3>(x.VertexBuffer.Normals, glBufferTarget.ARRAY_BUFFER, false),
}).ToArray();
}
@ -237,7 +275,7 @@ namespace UniVRM10
foreach (var (gltfPrim, submesh) in Enumerable.Zip(gltfMesh.primitives, mesh.Submeshes, (l, r) => (l, r)))
{
var subIndices = indices.GetSubArray(submesh.Offset, submesh.DrawCount);
gltfPrim.indices = AddAccessor(subIndices);
gltfPrim.indices = AddAccessor(subIndices, glBufferTarget.ELEMENT_ARRAY_BUFFER, false);
gltfPrim.attributes.POSITION = position.Value;
gltfPrim.attributes.NORMAL = normal.GetValueOrDefault(-1); // たぶん、ありえる
gltfPrim.attributes.TANGENT = -1;
@ -300,24 +338,24 @@ namespace UniVRM10
throw new NotImplementedException();
}
var position = AddAccessor<Vector3>(mesh.VertexBuffer.Positions);
var normal = AddAccessor<Vector3>(mesh.VertexBuffer.Normals);
var uv = AddAccessor<Vector2>(mesh.VertexBuffer.TexCoords);
var weights = AddAccessor<Vector4>(mesh.VertexBuffer.Weights);
var joints = AddAccessor<UShort4>(mesh.VertexBuffer.Joints);
var color = AddAccessor<Vector4>(mesh.VertexBuffer.Colors);
var position = AddAccessor<Vector3>(mesh.VertexBuffer.Positions, glBufferTarget.ARRAY_BUFFER, true);
var normal = AddAccessor<Vector3>(mesh.VertexBuffer.Normals, glBufferTarget.ARRAY_BUFFER, false);
var uv = AddAccessor<Vector2>(mesh.VertexBuffer.TexCoords, glBufferTarget.ARRAY_BUFFER, false);
var weights = AddAccessor<Vector4>(mesh.VertexBuffer.Weights, glBufferTarget.ARRAY_BUFFER, false);
var joints = AddAccessor<UShort4>(mesh.VertexBuffer.Joints, glBufferTarget.ARRAY_BUFFER, false);
var color = AddAccessor<Vector4>(mesh.VertexBuffer.Colors, glBufferTarget.ARRAY_BUFFER, false);
var morphTargets = new MorphAccessor[] { };
if (mesh.MorphTargets != null)
{
morphTargets = mesh.MorphTargets.Select(x => new MorphAccessor
{
Position = AddAccessor<Vector3>(x.VertexBuffer.Positions),
Normal = AddAccessor<Vector3>(x.VertexBuffer.Normals),
Position = AddAccessor<Vector3>(x.VertexBuffer.Positions, glBufferTarget.ARRAY_BUFFER, true),
Normal = AddAccessor<Vector3>(x.VertexBuffer.Normals, glBufferTarget.ARRAY_BUFFER, false),
}).ToArray();
}
gltfPrim.indices = AddAccessor(indices);
gltfPrim.indices = AddAccessor(indices, glBufferTarget.ELEMENT_ARRAY_BUFFER, false);
gltfPrim.attributes.POSITION = position.Value;
gltfPrim.attributes.NORMAL = normal.GetValueOrDefault(-1); // たぶん、ありえる
gltfPrim.attributes.TANGENT = -1;

View File

@ -61,9 +61,9 @@ namespace UniVRM10
var accessor = data.GLTF.accessors[accessorIndex];
var bufferViewIndex = -1;
if (accessor.bufferView != -1)
if (accessor.bufferView.HasValue)
{
bufferViewIndex = accessor.bufferView;
bufferViewIndex = accessor.bufferView.Value;
}
else if (accessor.sparse?.values != null && accessor.sparse.values.bufferView != -1)
{
@ -113,7 +113,7 @@ namespace UniVRM10
if (used.Add(skin.inverseBindMatrices))
{
var accessor = data.GLTF.accessors[skin.inverseBindMatrices];
var buffer = data.GetBytesFromBufferView(accessor.bufferView);
var buffer = data.GetBytesFromBufferView(accessor.bufferView.Value);
var span = buffer.Reinterpret<UnityEngine.Matrix4x4>(1);
for (int i = 0; i < span.Length; ++i)
{

View File

@ -257,11 +257,70 @@ namespace UniVRM10
[Test]
public void MigrateMeta()
{
using (var data = new GlbFileParser(AliciaPath).Parse())
{
using (var migrated = Vrm10Data.Migrate(data, out Vrm10Data vrm, out MigrationData migration))
{
Assert.NotNull(vrm);
Assert.NotNull(migration);
}
}
}
class TempFile : IDisposable
{
public string Path { get; }
TempFile(string path)
{
Path = path;
}
public void Dispose()
{
// File.Delete(Path);
}
public static TempFile Create(string path, byte[] bytes)
{
File.WriteAllBytes(path, bytes);
return new TempFile(path);
}
}
[Test]
public void GltfValidator()
{
if (!VRMShaders.PathObject.TryGetFromEnvironmentVariable("GLTF_VALIDATOR", out var exe))
{
return;
}
if (!exe.Exists)
{
return;
}
using (var data = new GlbFileParser(AliciaPath).Parse())
using (var migrated = Vrm10Data.Migrate(data, out Vrm10Data vrm, out MigrationData migration))
{
Assert.NotNull(vrm);
Assert.NotNull(migration);
var json = GltfJsonUtil.FindUsedExtensionsAndUpdateJson(migrated.Json);
var glb = Glb.Create(json, new ArraySegment<byte>(migrated.Bin.ToArray())).ToBytes();
using (var tmp = TempFile.Create("GltfValidator_tmp.glb", glb))
{
var processStartInfo = new System.Diagnostics.ProcessStartInfo(exe.FullPath, $"{tmp.Path} -o")
{
CreateNoWindow = true,
UseShellExecute = false,
RedirectStandardOutput = true,
RedirectStandardError = true,
};
var process = System.Diagnostics.Process.Start(processStartInfo);
string standardOutput = process.StandardOutput.ReadToEnd();
string standardError = process.StandardError.ReadToEnd();
int exitCode = process.ExitCode;
Debug.Log($"{exitCode}\n{standardOutput}\n{standardError}\n");
Assert.AreEqual(0, exitCode);
}
}
}
}

View File

@ -27,6 +27,8 @@ namespace VRMShaders
public bool IsUnderAsset => IsDescendantOf(UnityAssets);
public bool Exists => System.IO.File.Exists(FullPath);
/// <summary>
/// AssetDatabase の引き数になるパスを想定。
/// Assets のひとつ上を 基準とする相対パス。
@ -136,6 +138,18 @@ namespace VRMShaders
return true;
}
public static bool TryGetFromEnvironmentVariable(string key, out PathObject dst)
{
var value = System.Environment.GetEnvironmentVariable(key);
if (string.IsNullOrEmpty(value))
{
dst = default;
return false;
}
dst = PathObject.FromFullPath(value);
return true;
}
public PathObject Child(string child)
{
return FromFullPath(Path.Combine(FullPath, child));