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)); } }
private static VrmLib.BufferAccessor ToBufferAccessor <T>(T[] value, VrmLib.AccessorValueType valueType, VrmLib.AccessorVectorType vectorType) where T : struct { var span = SpanLike.CopyFrom(value); return(new VrmLib.BufferAccessor( span.Bytes, valueType, vectorType, value.Length )); }
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(); }
int AddAccessor <T>(SpanLike <T> span) where T : struct { var bufferViewIndex = AddBuffer(span.Bytes); var accessor = new glTFAccessor { bufferView = bufferViewIndex, count = span.Length, byteOffset = 0, componentType = glTFExtensions.GetComponentType <T>(), type = glTFExtensions.GetAccessorType <T>(), }; var index = _accessors.Count; _accessors.Add(accessor); return(index); }
public void Vector4Test() { var bytes = new byte[4 * 4]; { var span = SpanLike.Wrap <Single>(new ArraySegment <byte>(bytes)); span[0] = 1.0f; } { var span = SpanLike.Wrap <Vector4>(new ArraySegment <byte>(bytes)); Assert.AreEqual(1.0f, span[0].X); } { var span = SpanLike.Wrap <Quaternion>(new ArraySegment <byte>(bytes)); Assert.AreEqual(1.0f, span[0].X); } }
static (int, int) GetVertexRange(SpanLike <int> indices) { var min = int.MaxValue;; var max = 0; foreach (var i in indices) { if (i < min) { min = i; } if (i > max) { max = i; } } return(min, max); }
static ArraySegment <byte> RestoreSparseAccessorUInt16 <T>(ArraySegment <byte> bytes, int accessorCount, ArraySegment <byte> indicesBytes, ArraySegment <byte> valuesBytes) where T : struct { var stride = Marshal.SizeOf(typeof(T)); if (bytes.Count == 0) { bytes = new ArraySegment <byte>(new byte[accessorCount * stride]); } var dst = SpanLike.Wrap <T>(bytes); var indices = SpanLike.Wrap <UInt16>(indicesBytes); var values = SpanLike.Wrap <T>(valuesBytes); for (int i = 0; i < indices.Length; ++i) { var index = indices[i]; var value = values[i]; dst[index] = value; } return(bytes); }
/// <summary> /// https://github.com/vrm-c/UniVRM/issues/800 /// /// SubMesh 単位に分割する。 /// SubMesh を Gltf の Primitive に対応させる。 /// </summary> /// <param name="mesh"></param> /// <param name="materials"></param> /// <param name="storage"></param> /// <param name="gltfMesh"></param> /// <param name="option"></param> static IEnumerable <glTFPrimitives> ExportMeshDivided(this VrmLib.Mesh mesh, List <object> materials, Vrm10Storage storage, ExportArgs option) { var bufferIndex = 0; var usedIndices = new List <int>(); var meshIndices = SpanLike.CopyFrom(mesh.IndexBuffer.GetAsIntArray()); var positions = mesh.VertexBuffer.Positions.GetSpan <UnityEngine.Vector3>().ToArray(); var normals = mesh.VertexBuffer.Normals.GetSpan <UnityEngine.Vector3>().ToArray(); var uv = mesh.VertexBuffer.TexCoords.GetSpan <UnityEngine.Vector2>().ToArray(); var hasSkin = mesh.VertexBuffer.Weights != null; var weights = mesh.VertexBuffer.Weights?.GetSpan <UnityEngine.Vector4>().ToArray(); var joints = mesh.VertexBuffer.Joints?.GetSpan <SkinJoints>().ToArray(); Func <int, int> getJointIndex = default; if (hasSkin) { getJointIndex = i => { return(i); }; } foreach (var submesh in mesh.Submeshes) { var indices = meshIndices.Slice(submesh.Offset, submesh.DrawCount).ToArray(); var hash = new HashSet <int>(indices); // mesh // index の順に attributes を蓄える var buffer = new MeshExportUtil.VertexBuffer(indices.Length, getJointIndex); usedIndices.Clear(); for (int k = 0; k < positions.Length; ++k) { if (hash.Contains(k)) { // indices から参照される頂点だけを蓄える usedIndices.Add(k); buffer.Push(k, positions[k], normals[k], uv[k]); if (getJointIndex != null) { var j = joints[k]; var w = weights[k]; var boneWeight = new UnityEngine.BoneWeight { boneIndex0 = j.Joint0, boneIndex1 = j.Joint1, boneIndex2 = j.Joint2, boneIndex3 = j.Joint3, weight0 = w.x, weight1 = w.y, weight2 = w.z, weight3 = w.w, }; buffer.Push(boneWeight); } } } var materialIndex = submesh.Material; var gltfPrimitive = buffer.ToGltfPrimitive(storage.Gltf, bufferIndex, materialIndex, indices); // blendShape for (int j = 0; j < mesh.MorphTargets.Count; ++j) { var blendShape = new MeshExportUtil.BlendShapeBuffer(indices.Length); // index の順に attributes を蓄える var morph = mesh.MorphTargets[j]; var blendShapePositions = morph.VertexBuffer.Positions.GetSpan <UnityEngine.Vector3>(); SpanLike <UnityEngine.Vector3>?blendShapeNormals = default; if (morph.VertexBuffer.Normals != null) { blendShapeNormals = morph.VertexBuffer.Normals.GetSpan <UnityEngine.Vector3>(); } foreach (var k in usedIndices) { blendShape.Push( blendShapePositions[k], blendShapeNormals.HasValue ? blendShapeNormals.Value[k] : UnityEngine.Vector3.zero ); } gltfPrimitive.targets.Add(blendShape.ToGltf(storage.Gltf, bufferIndex, !option.removeMorphNormal)); } yield return(gltfPrimitive); } }
/// <summary> /// Gltfの Primitive[] の indices をひとまとめにした /// IndexBuffer を返す。 /// </summary> public BufferAccessor CreateAccessor(int[] accessorIndices) { var totalCount = accessorIndices.Sum(x => Gltf.accessors[x].count); if (AccessorsIsContinuous(accessorIndices)) { // IndexBufferが連続して格納されている => Slice でいける var firstAccessor = Gltf.accessors[accessorIndices[0]]; var firstView = Gltf.bufferViews[firstAccessor.bufferView]; var start = firstView.byteOffset + firstAccessor.byteOffset; if (!firstView.buffer.TryGetValidIndex(Gltf.buffers.Count, out int firstViewBufferIndex)) { throw new Exception(); } var buffer = Gltf.buffers[firstViewBufferIndex]; var bin = GetBufferBytes(buffer); var bytes = bin.Slice(start, totalCount * firstAccessor.GetStride()); return(new BufferAccessor(bytes, (AccessorValueType)firstAccessor.componentType, EnumUtil.Parse <AccessorVectorType>(firstAccessor.type), totalCount)); } else { // IndexBufferが連続して格納されていない => Int[] を作り直す var indices = new byte[totalCount * Marshal.SizeOf(typeof(int))]; var span = SpanLike.Wrap <Int32>(new ArraySegment <byte>(indices)); var offset = 0; foreach (var accessorIndex in accessorIndices) { var accessor = Gltf.accessors[accessorIndex]; if (accessor.type != "SCALAR") { throw new ArgumentException($"accessor.type: {accessor.type}"); } var view = Gltf.bufferViews[accessor.bufferView]; 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 bytes = bin.Slice(start, accessor.count * accessor.GetStride()); var dst = SpanLike.Wrap <Int32>(new ArraySegment <byte>(indices)).Slice(offset, accessor.count); offset += accessor.count; switch ((AccessorValueType)accessor.componentType) { case AccessorValueType.UNSIGNED_BYTE: { var src = SpanLike.Wrap <Byte>(bytes); for (int i = 0; i < src.Length; ++i) { // byte to int dst[i] = src[i]; } } break; case AccessorValueType.UNSIGNED_SHORT: { var src = SpanLike.Wrap <UInt16>(bytes); for (int i = 0; i < src.Length; ++i) { // ushort to int dst[i] = src[i]; } } break; case AccessorValueType.UNSIGNED_INT: { Buffer.BlockCopy(bytes.Array, bytes.Offset, dst.Bytes.Array, dst.Bytes.Offset, bytes.Count); } break; default: throw new NotImplementedException($"accessor.componentType: {accessor.componentType}"); } } return(new BufferAccessor(new ArraySegment <byte>(indices), AccessorValueType.UNSIGNED_INT, AccessorVectorType.SCALAR, totalCount)); } }
public static int AddAccessorTo(this VrmLib.BufferAccessor self, Vrm10Storage storage, int bufferIndex, // GltfBufferTargetType targetType, bool useSparse, Action <ArraySegment <byte>, glTFAccessor> minMax = null, int offset = 0, int count = 0) { if (self.ComponentType == VrmLib.AccessorValueType.FLOAT && self.AccessorType == VrmLib.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 ArraySegment <byte>(new byte[sparseValuesWithIndex.Count * 4]); var sparseIndexSpan = SpanLike.Wrap <Int32>(sparseIndexBin); var sparseValueBin = new ArraySegment <byte>(new byte[sparseValuesWithIndex.Count * 12]); var sparseValueSpan = SpanLike.Wrap <Vector3>(sparseValueBin); for (int i = 0; i < sparseValuesWithIndex.Count; ++i) { var(index, value) = sparseValuesWithIndex[i]; sparseIndexSpan[i] = index; sparseValueSpan[i] = value; } var sparseIndexView = storage.AppendToBuffer(sparseIndexBin); var sparseValueView = storage.AppendToBuffer(sparseValueBin); var accessorIndex = storage.Gltf.accessors.Count; var accessor = new glTFAccessor { componentType = (glComponentType)self.ComponentType, type = self.AccessorType.ToString(), count = self.Count, byteOffset = -1, sparse = new glTFSparse { count = sparseValuesWithIndex.Count, indices = new glTFSparseIndices { componentType = (glComponentType)VrmLib.AccessorValueType.UNSIGNED_INT, bufferView = sparseIndexView, }, values = new glTFSparseValues { 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)); }