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 = MemoryMarshal.Cast <byte, ushort>(bytes); for (int i = 0; i < src.Length; ++i) { dst[i] = (ushort)src[i]; } var accessor = new BufferAccessor(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)); } }
public static VertexBuffer FromGltf(this VrmProtobuf.target 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 VertexBuffer FromGltf(this global::Google.Protobuf.Collections.MapField <string, int> attributes, Vrm10Storage storage) { var b = new VertexBuffer(); foreach (var kv in attributes) { var accessor = storage.CreateAccessor(kv.Value); b.Add(kv.Key, accessor); } return(b); }
public static VrmProtobuf.Image ToGltf(this Image src, Vrm10Storage storage) { var viewIndex = storage.AppendToBuffer(0, src.Bytes, 1); var gltf = storage.Gltf; return(new VrmProtobuf.Image { Name = src.Name, MimeType = src.MimeType, BufferView = viewIndex, }); }
static VrmProtobuf.Image GetNormalImage(Vrm10Storage storage, VrmProtobuf.Material m) { if (m.NormalTexture == null) { return(null); } if (!m.NormalTexture.Index.TryGetValidIndex(storage.TextureCount, out int index)) { return(null); } return(GetTexture(storage, index)); }
public static MeshGroup FromGltf(this VrmProtobuf.Mesh x, Vrm10Storage storage, List <Material> materials) { 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.HasValue ? primitive.Material.Value : 0; mesh.Submeshes.Add( new Submesh(0, mesh.IndexBuffer.Count, materials[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.HasValue ? primitive.Material.Value : 0; mesh.Submeshes.Add( new Submesh(offset, mesh.IndexBuffer.Count, materials[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.HasValue ? primitive.Material.Value : 0; var count = storage.Gltf.Accessors[primitive.Indices.Value].Count.Value; mesh.Submeshes.Add( new Submesh(offset, count, materials[materialIndex])); offset += count; } group.Meshes.Add(mesh); } return(group); }
public static int AddViewTo(this BufferAccessor self, Vrm10Storage storage, int bufferIndex, int offset = 0, int count = 0) { var stride = self.Stride; if (count == 0) { count = self.Count; } var slice = self.Bytes.Slice(offset * stride, count * stride); return(storage.AppendToBuffer(bufferIndex, slice, stride)); }
static VrmProtobuf.Image GetTexture(Vrm10Storage storage, int index) { if (index < 0 || index >= storage.Gltf.Textures.Count) { return(null); } var texture = storage.Gltf.Textures[index]; if (texture.Source.HasValue && texture.Source < 0 || texture.Source >= storage.Gltf.Images.Count) { return(null); } return(storage.Gltf.Images[texture.Source.Value]); }
static Mesh FromGltf(Vrm10Storage storage, VrmProtobuf.Mesh x, VrmProtobuf.MeshPrimitive primitive, bool isShared) { var mesh = new Mesh((TopologyType)primitive.Mode.GetValueOrDefault()) { VertexBuffer = primitive.Attributes.FromGltf(storage) }; if (isShared) { // create joined index buffer mesh.IndexBuffer = storage.CreateAccessor(x.Primitives.Select(y => y.Indices.Value).ToArray()); } else { mesh.IndexBuffer = storage.CreateAccessor(primitive.Indices.Value); } { for (int i = 0; i < primitive.Targets.Count; ++i) { var gltfTarget = primitive.Targets[i]; string targetName = null; if (primitive.Extras != null && primitive.Extras.TargetNames != null && i < primitive.Extras.TargetNames.Count ) { targetName = primitive.Extras.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 VrmProtobuf.Image GetColorImage(Vrm10Storage storage, VrmProtobuf.Material m) { if (m.PbrMetallicRoughness == null) { return(null); } if (m.PbrMetallicRoughness.BaseColorTexture == null) { return(null); } if (!m.PbrMetallicRoughness.BaseColorTexture.Index.TryGetValidIndex(storage.TextureCount, out int index)) { return(null); } return(GetTexture(storage, index)); }
private ModelAsset ToUnity(byte[] bytes) { if (!Glb.TryParse(bytes, out Glb glb, out Exception ex)) { throw ex; } // Vrm => Model var storage = new Vrm10.Vrm10Storage(glb.Json.Bytes, glb.Binary.Bytes); var model = ModelLoader.Load(storage, "test"); model.ConvertCoordinate(Coordinates.Unity); model.RemoveSecondary(); return(ToUnity(model)); }
public static int AddAccessorTo(this BufferAccessor self, Vrm10Storage storage, int viewIndex, Action <Memory <byte>, VrmProtobuf.Accessor> minMax = null, int offset = 0, int count = 0) { var gltf = storage.Gltf; var accessorIndex = gltf.Accessors.Count; var accessor = self.CreateGltfAccessor(viewIndex, count, offset * self.Stride); if (minMax != null) { minMax(self.Bytes, accessor); } gltf.Accessors.Add(accessor); return(accessorIndex); }
public static Image FromGltf(this VrmProtobuf.Image x, Vrm10Storage storage) { if (!x.BufferView.HasValue) { // 外部参照? throw new Exception(); } var view = storage.Gltf.BufferViews[x.BufferView.Value]; var buffer = storage.Gltf.Buffers[view.Buffer.Value]; // テクスチャの用途を調べる var usage = default(ImageUsage); foreach (var material in storage.Gltf.Materials) { var colorImage = GetColorImage(storage, material); if (colorImage == x) { usage |= ImageUsage.Color; } var normalImage = GetNormalImage(storage, material); if (normalImage == x) { usage |= ImageUsage.Normal; } } var memory = storage.GetBufferBytes(buffer); return(new Image(x.Name, x.MimeType, usage, memory.Slice(view.ByteOffset.GetValueOrDefault(), view.ByteLength.Value))); }
static void ExportMesh(this Mesh mesh, List <Material> materials, Vrm10Storage storage, VrmProtobuf.Mesh 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 <Memory <byte>, VrmProtobuf.Accessor>)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 <Memory <byte>, VrmProtobuf.Accessor>)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 VrmProtobuf.MeshPrimitive { Mode = (int)mesh.Topology, Material = materials.IndexOfNullable(y.Material), Indices = indicesAccessorIndex, }; gltfMesh.Primitives.Add(prim); // attribute foreach (var kv in mesh.VertexBuffer) { var attributeAccessorIndex = attributeAccessorIndexMap[kv.Key]; prim.Attributes.Add(kv.Key, attributeAccessorIndex); } // morph target if (mesh.MorphTargets.Any()) { foreach (var(t, accessorIndexMap) in Enumerable.Zip(mesh.MorphTargets, morphTargetAccessorIndexMapList, (t, v) => (t, v))) { var target = new VrmProtobuf.target(); 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(); } } } if (mesh.MorphTargets.Any()) { prim.Extras = new VrmProtobuf.MeshPrimitive.Types.Extras(); foreach (var name in mesh.MorphTargets.Select(z => z.Name)) { prim.Extras.TargetNames.Add(name); } } } } }
public static int AddAccessorTo(this BufferAccessor self, Vrm10Storage storage, int bufferIndex, // GltfBufferTargetType targetType, bool useSparse, Action <Memory <byte>, VrmProtobuf.Accessor> minMax = null, int offset = 0, int count = 0) { if (self.ComponentType == AccessorValueType.FLOAT && self.AccessorType == AccessorVectorType.VEC3 ) { var values = self.GetSpan <Vector3>(); // 巨大ポリゴンのモデル対策にValueTupleの型をushort -> uint へ var sparseValuesWithIndex = new List <ValueTuple <int, Vector3> >(); for (int i = 0; i < values.Length; ++i) { var v = values[i]; if (v != Vector3.Zero) { sparseValuesWithIndex.Add((i, v)); } } //var status = $"{sparseIndices.Count * 14}/{values.Length * 12}"; if (useSparse && sparseValuesWithIndex.Count > 0 && // avoid empty sparse sparseValuesWithIndex.Count * 16 < values.Length * 12) { // use sparse var sparseIndexBin = new byte[sparseValuesWithIndex.Count * 4].AsMemory(); var sparseIndexSpan = MemoryMarshal.Cast <byte, int>(sparseIndexBin.Span); var sparseValueBin = new byte[sparseValuesWithIndex.Count * 12].AsMemory(); var sparseValueSpan = MemoryMarshal.Cast <byte, Vector3>(sparseValueBin.Span); for (int i = 0; i < sparseValuesWithIndex.Count; ++i) { var(index, value) = sparseValuesWithIndex[i]; sparseIndexSpan[i] = index; sparseValueSpan[i] = value; } var sparseIndexView = storage.AppendToBuffer(bufferIndex, sparseIndexBin, 4); var sparseValueView = storage.AppendToBuffer(bufferIndex, sparseValueBin, 12); var accessorIndex = storage.Gltf.Accessors.Count; var accessor = new VrmProtobuf.Accessor { ComponentType = (int)self.ComponentType, Type = EnumUtil.Cast <VrmProtobuf.Accessor.Types.accessorType>(self.AccessorType), Count = self.Count, Sparse = new VrmProtobuf.AccessorSparse { Count = sparseValuesWithIndex.Count, Indices = new VrmProtobuf.AccessorSparseIndices { ComponentType = (int)AccessorValueType.UNSIGNED_INT, BufferView = sparseIndexView, }, Values = new VrmProtobuf.AccessorSparseValues { BufferView = sparseValueView, }, } }; if (minMax != null) { minMax(sparseValueBin, accessor); } storage.Gltf.Accessors.Add(accessor); return(accessorIndex); } } var viewIndex = self.AddViewTo(storage, bufferIndex, offset, count); return(self.AddAccessorTo(storage, viewIndex, minMax, 0, count)); }
/// <summary> /// VertexBufferはひとつでIndexBufferの参照が異なる /// /// VertexBuffer /// +----------------------------------+ /// | | /// +----------------------------------+ /// A A A /// | | | /// +---------+--------+--------+ /// | submesh0|submesh1|submesh2| /// +---------+--------+--------+ /// IndexBuffer /// </summary> public static Mesh SharedBufferFromGltf(this VrmProtobuf.Mesh 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 VrmProtobuf.MeshPrimitive primitive, Vrm10Storage storage, VrmProtobuf.Mesh x) { return(FromGltf(storage, x, primitive, false)); }
public static VrmProtobuf.Mesh ExportMeshGroup(this MeshGroup src, List <Material> materials, Vrm10Storage storage, ExportArgs option) { var mesh = new VrmProtobuf.Mesh { Name = src.Name }; foreach (var x in src.Meshes) { // MeshとSubmeshがGltfのPrimitiveに相当する? x.ExportMesh(materials, storage, mesh, option); } return(mesh); }