static void ReverseX(BufferAccessor ba) { if (ba.ComponentType != AccessorValueType.FLOAT) { throw new Exception(); } if (ba.AccessorType == AccessorVectorType.VEC3) { var span = SpanLike.Wrap <Vector3>(ba.Bytes); for (int i = 0; i < span.Length; ++i) { span[i] = span[i].ReverseX(); } } else if (ba.AccessorType == AccessorVectorType.MAT4) { var span = SpanLike.Wrap <Matrix4x4>(ba.Bytes); for (int i = 0; i < span.Length; ++i) { span[i] = span[i].ReverseX(); } } else { throw new NotImplementedException(); } }
/// <summary> /// indicesの最大値が65535未満(-1を避ける)ならばushort 型で、 /// そうでなければ int型で IndexBufferを代入する /// </summary> public void AssignIndexBuffer(SpanLike <int> indices) { bool isInt = false; foreach (var i in indices) { if (i >= short.MaxValue) { isInt = true; break; } } if (isInt) { if (IndexBuffer.Stride != 4) { IndexBuffer.ComponentType = AccessorValueType.UNSIGNED_INT; if (IndexBuffer.AccessorType != AccessorVectorType.SCALAR) { throw new Exception(); } } // 変換なし IndexBuffer.Assign(indices); } else { // int to ushort IndexBuffer.AssignAsShort(indices); } }
/// weightを付与して、matrixを適用する static void FixNoSkinJoints(SpanLike <SkinJoints> joints, SpanLike <Vector4> weights, int jointIndex, SpanLike <Vector3> positions, SpanLike <Vector3> normals, Matrix4x4 matrix) { for (int i = 0; i < joints.Length; ++i) { var j = new SkinJoints { Joint0 = (ushort)jointIndex }; joints[i] = j; var w = new Vector4 { X = 1.0f, }; weights[i] = w; { // position var src = positions[i]; var dst = Vector4.Transform(new Vector4(src, 1), matrix); positions[i] = new Vector3(dst.X, dst.Y, dst.Z); } { // normal var src = normals[i]; var dst = Vector4.Transform(new Vector4(src, 0), matrix); normals[i] = new Vector3(dst.X, dst.Y, dst.Z); } } }
// Joints用 public UShort4[] GetAsUShort4() { if (AccessorType != AccessorVectorType.VEC4) { throw new InvalidOperationException("not vec4"); } switch (ComponentType) { case AccessorValueType.UNSIGNED_SHORT: return(SpanLike.Wrap <UShort4>(Bytes).ToArray()); case AccessorValueType.UNSIGNED_BYTE: { var array = new UShort4[Count]; var span = SpanLike.Wrap <Byte4>(Bytes); for (int i = 0; i < span.Length; ++i) { array[i].X = span[i].X; array[i].Y = span[i].Y; array[i].Z = span[i].Z; array[i].W = span[i].W; } return(array); } default: throw new NotImplementedException(); } }
List <ValueTuple <int, Triangle> > > SplitTriangles(Mesh src) { var triangles = src.Triangles.ToArray(); var morphUseCount = new int[triangles.Length]; // 各モーフ foreach (var morph in src.MorphTargets) { if (morph.VertexBuffer.Count == 0) { // 無効 continue; } // POSITIONが使っているtriangleのカウンターをアップさせる var positions = SpanLike.Wrap <Vector3>(morph.VertexBuffer.Positions.Bytes); for (int i = 0; i < triangles.Length; ++i) { ref var triangle = ref triangles[i]; if (positions[triangle.Item2.Vertex0] != Vector3.Zero) { ++morphUseCount[i]; } if (positions[triangle.Item2.Vertex1] != Vector3.Zero) { ++morphUseCount[i]; } if (positions[triangle.Item2.Vertex2] != Vector3.Zero) { ++morphUseCount[i]; } } }
void Apply(VertexBuffer vertexBuffer) { var dstPosition = SpanLike.Wrap <Vector3>(vertexBuffer.Positions.Bytes); // Span<Vector3> emptyNormal = stackalloc Vector3[0]; Apply(vertexBuffer, dstPosition, vertexBuffer.Normals != null ? SpanLike.Wrap <Vector3>(vertexBuffer.Normals.Bytes) : default); }
public override string ToString() { if (InverseMatrices != null) { var sb = new StringBuilder(); var matrices = SpanLike.Wrap <Matrix4x4>(InverseMatrices.Bytes); var count = 0; // var rootMatrix = Matrix4x4.Identity; // if (Root != null) // { // rootMatrix = Root.InverseMatrix; // } for (int i = 0; i < matrices.Length; ++i) { var m = matrices[i] * Joints[i].Matrix; if (!IsIdentity(m)) { ++count; } } if (count > 0) { sb.Append($"{count}/{Joints.Count} is not normalized"); } else { sb.Append($"{Joints.Count} joints normalized"); } return(sb.ToString()); } else { return($"{Joints.Count} joints without InverseMatrices"); } }
// Index用 public int[] GetAsIntArray() { if (AccessorType != AccessorVectorType.SCALAR) { throw new InvalidOperationException("not scalar"); } switch (ComponentType) { case AccessorValueType.UNSIGNED_SHORT: { var span = SpanLike.Wrap <UInt16>(Bytes); var array = new int[span.Length]; for (int i = 0; i < span.Length; ++i) { array[i] = span[i]; } return(array); } case AccessorValueType.UNSIGNED_INT: return(SpanLike.Wrap <Int32>(Bytes).ToArray()); default: throw new NotImplementedException(); } }
public SpanLike <T> GetSpan <T>(bool checkStride = true) where T : struct { if (checkStride && Marshal.SizeOf(typeof(T)) != Stride) { throw new Exception("different sizeof(T) with stride"); } return(SpanLike.Wrap <T>(Bytes)); }
public void Assign <T>(SpanLike <T> values) where T : struct { if (Marshal.SizeOf(typeof(T)) != Stride) { throw new Exception("invalid element size"); } Bytes = values.Bytes; Count = values.Length; }
static void FlipTriangle(SpanLike <uint> indices) { for (int i = 0; i < indices.Length; i += 3) { // 0, 1, 2 to 2, 1, 0 var tmp = indices[i + 2]; indices[i + 2] = indices[i]; indices[i] = tmp; } }
/// <summary> /// BoneSkinningもしくはMorphTargetの適用 /// <summary> public void Skinning(VertexBuffer vertexBuffer = null) { m_indexOfRoot = (ushort)Joints.IndexOf(Root); var addRoot = Root != null && m_indexOfRoot == ushort.MaxValue; if (addRoot) { m_indexOfRoot = (ushort)Joints.Count; Joints.Add(Root); } if (m_matrices == null) { m_matrices = new Matrix4x4[Joints.Count]; } if (InverseMatrices == null) { CalcInverseMatrices(); } else { if (addRoot) { var inverseArray = SpanLike.Wrap <Matrix4x4>(InverseMatrices.Bytes).ToArray(); var concat = inverseArray.Concat(new[] { Root.InverseMatrix }).ToArray(); InverseMatrices.Assign(concat); } } var inverse = InverseMatrices.GetSpan <Matrix4x4>(); // if (Root != null) // { // var rootInverse = Root.InverseMatrix; // var root = Root.Matrix; // for (int i = 0; i < m_matrices.Length; ++i) // { // m_matrices[i] = inverse[i] * Joints[i].Matrix * rootInverse; // } // } // else { for (int i = 0; i < m_matrices.Length; ++i) { var inv = i < inverse.Length ? inverse[i] : Joints[i].InverseMatrix; m_matrices[i] = inv * Joints[i].Matrix; } } if (vertexBuffer != null) { Apply(vertexBuffer); } }
public static BufferAccessor Create <T>(T[] list) where T : struct { var t = typeof(T); var bytes = new byte[list.Length * Marshal.SizeOf(t)]; var span = SpanLike.Wrap <T>(new ArraySegment <byte>(bytes)); for (int i = 0; i < list.Length; ++i) { span[i] = list[i]; } AccessorValueType componentType = default(AccessorValueType); AccessorVectorType accessorType = default(AccessorVectorType); if (t == typeof(Vector2)) { componentType = AccessorValueType.FLOAT; accessorType = AccessorVectorType.VEC2; } else if (t == typeof(Vector3)) { componentType = AccessorValueType.FLOAT; accessorType = AccessorVectorType.VEC3; } else if (t == typeof(Vector4)) { componentType = AccessorValueType.FLOAT; accessorType = AccessorVectorType.VEC4; } else if (t == typeof(Quaternion)) { componentType = AccessorValueType.FLOAT; accessorType = AccessorVectorType.VEC4; } else if (t == typeof(SkinJoints)) { componentType = AccessorValueType.UNSIGNED_SHORT; accessorType = AccessorVectorType.VEC4; } else if (t == typeof(int)) { componentType = AccessorValueType.UNSIGNED_INT; accessorType = AccessorVectorType.SCALAR; } else { throw new NotImplementedException(); } return(new BufferAccessor( new ArraySegment <byte>(bytes), componentType, accessorType, list.Length)); }
public SpanLike <SkinJoints> GetOrCreateJoints() { var buffer = Joints; if (buffer == null) { buffer = new BufferAccessor( new ArraySegment <byte>(new byte[Marshal.SizeOf(typeof(SkinJoints)) * Count]), AccessorValueType.UNSIGNED_SHORT, AccessorVectorType.VEC4, Count); Add(JointKey, buffer); } return(SpanLike.Wrap <SkinJoints>(buffer.Bytes)); }
public SpanLike <Vector4> GetOrCreateWeights() { var buffer = Weights; if (buffer == null) { buffer = new BufferAccessor( new ArraySegment <byte>(new byte[Marshal.SizeOf(typeof(Vector4)) * Count]), AccessorValueType.FLOAT, AccessorVectorType.VEC4, Count); Add(WeightKey, buffer); } return(SpanLike.Wrap <Vector4>(buffer.Bytes)); }
// Weigt用 public Vector4[] GetAsVector4() { if (AccessorType != AccessorVectorType.VEC4) { throw new InvalidOperationException("not vec4"); } switch (ComponentType) { case AccessorValueType.FLOAT: return(SpanLike.Wrap <Vector4>(Bytes).ToArray()); default: throw new NotImplementedException(); } }
// for index buffer public void AssignAsShort(SpanLike <int> values) { if (AccessorType != AccessorVectorType.SCALAR) { throw new NotImplementedException(); } ComponentType = AccessorValueType.UNSIGNED_SHORT; Bytes = new ArraySegment <byte>(new byte[Stride * values.Length]); var span = GetSpan <ushort>(); Count = values.Length; for (int i = 0; i < values.Length; ++i) { span[i] = (ushort)values[i]; } }
/// <summary> /// UVのVを反転する。 => V = 1.0 - V /// </summary> static void UVVerticalFlip(this Model model) { foreach (var g in model.MeshGroups) { foreach (var m in g.Meshes) { var uv = m.VertexBuffer.TexCoords; if (uv != null) { var span = SpanLike.Wrap <Vector2>(uv.Bytes); for (int i = 0; i < span.Length; ++i) { span[i] = span[i].UVVerticalFlip(); } } } } }
// Skin.Normalize public static void ApplyRotationAndScaling(this Mesh mesh, Matrix4x4 m) { m.Translation = Vector3.Zero; var position = SpanLike.Wrap <Vector3>(mesh.VertexBuffer.Positions.Bytes); var normal = SpanLike.Wrap <Vector3>(mesh.VertexBuffer.Normals.Bytes); for (int i = 0; i < position.Length; ++i) { { var dst = Vector4.Transform(new Vector4(position[i], 1), m); position[i] = new Vector3(dst.X, dst.Y, dst.Z); } { var dst = Vector4.Transform(new Vector4(normal[i], 0), m); normal[i] = new Vector3(dst.X, dst.Y, dst.Z); } } }
public bool Equals(SpanLike <T> other) { if (Length != other.Length) { return(false); } var end = Length; for (int i = 0; i < end; ++i) { if (!this[i].Equals(other[i])) { return(false); } } return(true); }
// joint index の調整 static void FixSkinJoints(SpanLike <SkinJoints> joints, SpanLike <Vector4> weights, int[] jointIndexMap) { for (int i = 0; i < joints.Length; ++i) { var j = joints[i]; var w = weights[i]; var sum = w.X + w.Y + w.Z + w.W; if (sum == 0) { throw new Exception("zero weight"); } var factor = 1.0f / sum; { var index = jointIndexMap[j.Joint0]; j.Joint0 = (ushort)(index >= 0 ? index : 0); w.X *= factor; } { var index = jointIndexMap[j.Joint1]; j.Joint1 = (ushort)(index >= 0 ? index : 0); w.X *= factor; } { var index = jointIndexMap[j.Joint2]; j.Joint2 = (ushort)(index >= 0 ? index : 0); w.X *= factor; } { var index = jointIndexMap[j.Joint3]; j.Joint3 = (ushort)(index >= 0 ? index : 0); w.X *= factor; } weights[i] = w; joints[i] = j; } }
public List <int> GetAsIntList() { if (AccessorType != AccessorVectorType.SCALAR) { throw new InvalidOperationException("not scalar"); } switch (ComponentType) { case AccessorValueType.UNSIGNED_SHORT: { var span = SpanLike.Wrap <UInt16>(Bytes); var array = new List <int>(Count); if (span.Length != Count) { for (int i = 0; i < Count; ++i) { array.Add(span[i]); } } else { // Spanが動かない?WorkAround var bytes = Bytes.ToArray(); var offset = 0; for (int i = 0; i < Count; ++i) { array.Add(BitConverter.ToUInt16(bytes, offset)); offset += 2; } } return(array); } case AccessorValueType.UNSIGNED_INT: return(SpanLike.Wrap <Int32>(Bytes).ToArray().ToList()); default: throw new NotImplementedException(); } }
public void CalcInverseMatrices() { // var root = Root; // if (root == null) // { // root = Joints[0].Ancestors().Last(); // } // root.CalcWorldMatrix(Matrix4x4.Identity, true); // calc inverse bind matrices var matricesBytes = new Byte[Marshal.SizeOf(typeof(Matrix4x4)) * Joints.Count]; var matrices = SpanLike.Wrap <Matrix4x4>(new ArraySegment <byte>(matricesBytes)); for (int i = 0; i < Joints.Count; ++i) { // var w = Joints[i].Matrix; // Matrix4x4.Invert(w, out Matrix4x4 inv); if (Joints[i] != null) { matrices[i] = Joints[i].InverseMatrix; } } InverseMatrices = new BufferAccessor(new ArraySegment <byte>(matricesBytes), AccessorValueType.FLOAT, AccessorVectorType.MAT4, Joints.Count); }
/// <summary> /// * Position, Normal の Z座標に -1 を乗算する /// * Rotation => Axis Angle に分解 => Axis の Z座標に -1 を乗算。Angle に -1 を乗算 /// * Triangle の index を 0, 1, 2 から 2, 1, 0 に反転する /// </summary> static void ReverseZAndFlipTriangle(this Model model, bool ignoreVrm) { foreach (var g in model.MeshGroups) { foreach (var m in g.Meshes) { foreach (var(k, v) in m.VertexBuffer) { if (k == VertexBuffer.PositionKey || k == VertexBuffer.NormalKey) { ReverseZ(v); } if (k == VertexBuffer.TangentKey) { // I don't know } } switch (m.IndexBuffer.ComponentType) { case AccessorValueType.UNSIGNED_BYTE: FlipTriangle(SpanLike.Wrap <Byte>(m.IndexBuffer.Bytes)); break; case AccessorValueType.UNSIGNED_SHORT: FlipTriangle(SpanLike.Wrap <UInt16>(m.IndexBuffer.Bytes)); break; case AccessorValueType.UNSIGNED_INT: FlipTriangle(SpanLike.Wrap <UInt32>(m.IndexBuffer.Bytes)); break; default: throw new NotImplementedException(); } foreach (var mt in m.MorphTargets) { foreach (var(k, v) in mt.VertexBuffer) { if (k == VertexBuffer.PositionKey || k == VertexBuffer.NormalKey) { ReverseZ(v); } if (k == VertexBuffer.TangentKey) { // I don't know } } } } } // 親から順に処理する // Rootは原点決め打ちのノード(GLTFに含まれない) foreach (var n in model.Root.Traverse().Skip(1)) { n.SetMatrix(n.Matrix.ReverseZ(), false); } // 親から順に処理したので不要 // model.Root.CalcWorldMatrix(); foreach (var s in model.Skins) { if (s.InverseMatrices != null) { ReverseZ(s.InverseMatrices); } } foreach (var a in model.Animations) { // TODO: } if (model.Vrm != null) { if (!ignoreVrm) { // LookAt if (model.Vrm.LookAt != null) { model.Vrm.LookAt.OffsetFromHeadBone = model.Vrm.LookAt.OffsetFromHeadBone.ReverseZ(); } // SpringBone if (model.Vrm.SpringBone != null) { foreach (var b in model.Vrm.SpringBone.Springs) { foreach (var c in b.Colliders) { for (int i = 0; i < c.Colliders.Count; ++i) { var s = c.Colliders[i]; switch (s.ColliderType) { case VrmSpringBoneColliderTypes.Sphere: c.Colliders[i] = VrmSpringBoneCollider.CreateSphere(s.Offset.ReverseZ(), s.Radius); break; case VrmSpringBoneColliderTypes.Capsule: c.Colliders[i] = VrmSpringBoneCollider.CreateCapsule(s.Offset.ReverseZ(), s.Radius, s.CapsuleTail.ReverseZ()); break; default: throw new NotImplementedException(); } } } b.GravityDir = b.GravityDir.ReverseZ(); } } } } }
// // ArraySegment<byte> を新規に確保して置き換える // public void Append(BufferAccessor a, int offset = -1) { if (AccessorType != a.AccessorType) { System.Console.WriteLine(AccessorType.ToString() + "!=" + a.AccessorType.ToString()); throw new Exception("different AccessorType"); } // UNSIGNED_SHORT <-> UNSIGNED_INT の変換を許容して処理を続行 // 統合メッシュのprimitiveのIndexBufferが65,535(ushort.MaxValue)を超える場合や、変換前にindexBuffer.ComponetTypeがushortとuint混在する場合など if (ComponentType != a.ComponentType) { switch (a.ComponentType) { //ushort to uint case AccessorValueType.UNSIGNED_SHORT: { var src = SpanLike.Wrap <UInt16>(a.Bytes).Slice(0, a.Count); var bytes = new byte[src.Length * 4]; var dst = SpanLike.Wrap <UInt32>(new ArraySegment <byte>(bytes)); for (int i = 0; i < src.Length; ++i) { dst[i] = (uint)src[i]; } var accessor = new BufferAccessor(new ArraySegment <byte>(bytes), AccessorValueType.UNSIGNED_INT, AccessorVectorType.SCALAR, a.Count); a = accessor; break; } //uint to ushort (おそらく通ることはない) case AccessorValueType.UNSIGNED_INT: { var src = SpanLike.Wrap <UInt32>(a.Bytes).Slice(0, a.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), ComponentType, AccessorVectorType.SCALAR, a.Count); a = accessor; break; } default: throw new Exception("Cannot Convert ComponentType"); } } // 連結した新しいバッファを確保 var oldLength = Bytes.Count; ToByteLength(oldLength + a.Bytes.Count); // 後ろにコピー Buffer.BlockCopy(a.Bytes.Array, a.Bytes.Offset, Bytes.Array, Bytes.Offset + oldLength, a.Bytes.Count); Count += a.Count; if (offset > 0) { // 後半にoffsetを足す switch (ComponentType) { case AccessorValueType.UNSIGNED_SHORT: { var span = SpanLike.Wrap <UInt16>(Bytes.Slice(oldLength)); var ushortOffset = (ushort)offset; for (int i = 0; i < span.Length; ++i) { span[i] += ushortOffset; } } break; case AccessorValueType.UNSIGNED_INT: { var span = SpanLike.Wrap <UInt32>(Bytes.Slice(oldLength)); var uintOffset = (uint)offset; for (int i = 0; i < span.Length; ++i) { span[i] += uintOffset; } } break; default: throw new NotImplementedException(); } } }
/// <summary> /// * Position, Normal の Z座標に -1 を乗算する /// * Rotation => Axis Angle に分解 => Axis の Z座標に -1 を乗算。Angle に -1 を乗算 /// * Triangle の index を 0, 1, 2 から 2, 1, 0 に反転する /// </summary> static void ReverseAxisAndFlipTriangle(this Model model, Reverser reverser, bool ignoreVrm) { foreach (var g in model.MeshGroups) { foreach (var m in g.Meshes) { foreach (var(k, v) in m.VertexBuffer) { if (k == VertexBuffer.PositionKey || k == VertexBuffer.NormalKey) { reverser.ReverseBuffer(v); } else if (k == VertexBuffer.TangentKey) { // I don't know } } switch (m.IndexBuffer.ComponentType) { case AccessorValueType.UNSIGNED_BYTE: FlipTriangle(SpanLike.Wrap <Byte>(m.IndexBuffer.Bytes)); break; case AccessorValueType.UNSIGNED_SHORT: FlipTriangle(SpanLike.Wrap <UInt16>(m.IndexBuffer.Bytes)); break; case AccessorValueType.UNSIGNED_INT: FlipTriangle(SpanLike.Wrap <UInt32>(m.IndexBuffer.Bytes)); break; default: throw new NotImplementedException(); } foreach (var mt in m.MorphTargets) { foreach (var(k, v) in mt.VertexBuffer) { if (k == VertexBuffer.PositionKey || k == VertexBuffer.NormalKey) { reverser.ReverseBuffer(v); } if (k == VertexBuffer.TangentKey) { // I don't know } } } } } // 親から順に処理する // Rootは原点決め打ちのノード(GLTFに含まれない) foreach (var n in model.Root.Traverse().Skip(1)) { n.SetMatrix(reverser.ReverseMatrix(n.Matrix), false); } // 親から順に処理したので不要 // model.Root.CalcWorldMatrix(); foreach (var s in model.Skins) { if (s.InverseMatrices != null) { reverser.ReverseBuffer(s.InverseMatrices); } } foreach (var a in model.Animations) { // TODO: } }
public static IEnumerable <Node> GetRemoveNodes(this Model model) { var nodeUsage = model.Nodes.Select(x => new NodeUsage { HasMesh = x.MeshGroup != null, HumanBone = x.HumanoidBone, TreeHasHumanBone = x.Traverse().Any(y => y.HumanoidBone.HasValue), }) .ToArray(); var bones = model.Nodes.Where(x => x.HumanoidBone.HasValue).ToArray(); // joint use foreach (var meshGroup in model.MeshGroups) { var skin = meshGroup.Skin; if (skin != null) { foreach (var mesh in meshGroup.Meshes) { var joints = mesh.VertexBuffer.Joints; var weights = mesh.VertexBuffer.Weights; if (joints != null && weights != null) { var jointsSpan = SpanLike.Wrap <SkinJoints>(joints.Bytes); var weightsSpan = SpanLike.Wrap <Vector4>(weights.Bytes); for (int i = 0; i < jointsSpan.Length; ++i) { var w = weightsSpan[i]; var j = jointsSpan[i]; if (w.X > 0) { nodeUsage[model.Nodes.IndexOf(skin.Joints[j.Joint0])].WeightUsed++; } if (w.Y > 0) { nodeUsage[model.Nodes.IndexOf(skin.Joints[j.Joint1])].WeightUsed++; } if (w.Z > 0) { nodeUsage[model.Nodes.IndexOf(skin.Joints[j.Joint2])].WeightUsed++; } if (w.W > 0) { nodeUsage[model.Nodes.IndexOf(skin.Joints[j.Joint3])].WeightUsed++; } } } } } } // 削除されるNodeのうち、子階層に1つでも残るNodeがあればそのNodeも残す for (int i = 0; i < nodeUsage.Length; i++) { if (nodeUsage[i].Used) { continue; } var children = model.Nodes[i].Traverse(); nodeUsage[i].TreeHasUsedBone = children.Where(x => nodeUsage[model.Nodes.IndexOf(x)].Used).Any(); } var spring = model.Vrm?.SpringBone; if (spring != null) { foreach (var x in spring.Springs) { foreach (var y in x.Joints) { nodeUsage[model.Nodes.IndexOf(y.Node)].SpringUse = true; } foreach (var y in x.Colliders) { nodeUsage[model.Nodes.IndexOf(y.Node)].SpringUse = true; } } } var nodes = nodeUsage.Select((x, i) => (i, x)) .Where(x => !x.Item2.Used) .Select(x => model.Nodes[x.Item1]) .ToArray(); return(nodes); }
// Meshを連結する。SingleMesh で使う public static void Append(this Mesh mesh, VertexBuffer vertices, BufferAccessor indices, List <Submesh> submeshes, List <MorphTarget> targets, int[] jointIndexMap, int rootIndex = -1, Matrix4x4 matrix = default(Matrix4x4)) { var lastCount = mesh.VertexBuffer != null ? mesh.VertexBuffer.Count : 0; // index buffer if (mesh.IndexBuffer == null) { var accessor = new BufferAccessor(new ArraySegment <byte>(new byte[0]), AccessorValueType.UNSIGNED_INT, indices.AccessorType, 0); mesh.IndexBuffer = accessor; mesh.IndexBuffer.Append(indices, 0); } else { mesh.IndexBuffer.Append(indices, lastCount); } { var submeshOffset = mesh.SubmeshTotalDrawCount; for (int i = 0; i < submeshes.Count; ++i) { var submesh = submeshes[i]; mesh.Submeshes.Add(new Submesh(submeshOffset, submesh.DrawCount, submesh.Material)); submeshOffset += submesh.DrawCount; } } // vertex buffer var vertexOffset = 0; if (mesh.VertexBuffer == null) { mesh.VertexBuffer = vertices; } else { vertexOffset = mesh.VertexBuffer.Count; mesh.VertexBuffer.Append(vertices); } if (jointIndexMap != null && mesh.VertexBuffer.Joints != null && mesh.VertexBuffer.Weights != null) { // JOINT index 参照の修正 var joints = SpanLike.Wrap <SkinJoints>(mesh.VertexBuffer.Joints.Bytes).Slice(vertexOffset); var weights = SpanLike.Wrap <Vector4>(mesh.VertexBuffer.Weights.Bytes).Slice(vertexOffset); FixSkinJoints(joints, weights, jointIndexMap); } else { var position = SpanLike.Wrap <Vector3>(mesh.VertexBuffer.Positions.Bytes).Slice(vertexOffset); var normal = SpanLike.Wrap <Vector3>(mesh.VertexBuffer.Normals.Bytes).Slice(vertexOffset); var joints = mesh.VertexBuffer.GetOrCreateJoints().Slice(vertexOffset); var weights = mesh.VertexBuffer.GetOrCreateWeights().Slice(vertexOffset); // Nodeの姿勢を反映して // JOINT と WEIGHT を追加する FixNoSkinJoints(joints, weights, rootIndex, position, normal, matrix); } // morph target foreach (var target in targets) { if (string.IsNullOrEmpty(target.Name)) { continue; } foreach (var kv in target.VertexBuffer) { if (kv.Value.Count != target.VertexBuffer.Count) { throw new Exception("different length"); } } var found = mesh.MorphTargets.FirstOrDefault(x => x.Name == target.Name); if (found == null) { // targetの前に0を足す found = new MorphTarget(target.Name); found.VertexBuffer = target.VertexBuffer.CloneWithOffset(lastCount); mesh.MorphTargets.Add(found); foreach (var kv in found.VertexBuffer) { if (kv.Value.Count != mesh.VertexBuffer.Count) { throw new Exception(); } } } else { found.VertexBuffer.Resize(lastCount); // foundの後ろにtargetを足す found.VertexBuffer.Append(target.VertexBuffer); foreach (var kv in found.VertexBuffer) { if (kv.Value.Count != mesh.VertexBuffer.Count) { throw new Exception(); } } } } }
/// <summary> /// 統合できる同一とみなせる頂点を探す /// </summary> private static void FindMergeTargetVertices(SpanLike <Vertex> vertices) { var sameVertices = new HashSet <Vertex>(); for (int i = 0; i < vertices.Length; i++) { sameVertices.Clear(); var v0 = vertices[i]; int minIndex = v0.Index; if (v0.targetIndex > 0) { continue; } for (int j = i + 1; j < vertices.Length; j++) { var v1 = vertices[j]; if (v1.targetIndex > 0) { continue; } if (!v0.Equals(v1)) { continue; } if (!sameVertices.Contains(v0)) { sameVertices.Add(v0); } if (!sameVertices.Contains(v1)) { sameVertices.Add(v1); if (v1.Index < minIndex) { minIndex = v1.Index; } } } if (sameVertices.Count() >= 2) { foreach (var v in sameVertices) { v.targetIndex = minIndex; if (v.IsMergeTarget) { Vector3?n = Vector3.Zero; Vector4?c = Vector4.Zero; foreach (var vert in sameVertices) { if (vert.Normal.HasValue) { n += vert.Normal; } if (vert.Color.HasValue) { c += vert.Color; } } if (n.HasValue) { Vector3.Normalize(n.Value); } if (c.HasValue) { c = c.Value / sameVertices.Count(); } v.Normal = n; v.Color = c; } } } } return; }
/// <summary> /// 同一位置にある頂点を統合する /// </summary> public static string MergeSameVertices(this Mesh mesh) { var indices = mesh.IndexBuffer.GetAsIntArray(); var positions = SpanLike.CreateVector3(mesh.VertexBuffer.Positions.Bytes); var normals = mesh.VertexBuffer.Normals != null?SpanLike.CreateVector3(mesh.VertexBuffer.Normals.Bytes) : default; var UVs = mesh.VertexBuffer.TexCoords != null?SpanLike.CreateVector2(mesh.VertexBuffer.TexCoords.Bytes) : default; var joints = mesh.VertexBuffer.Joints != null?SpanLike.CreateSkinJoints(mesh.VertexBuffer.Joints.Bytes) : default; var weights = mesh.VertexBuffer.Weights != null?SpanLike.CreateVector4(mesh.VertexBuffer.Weights.Bytes) : default; var colors = mesh.VertexBuffer.Colors != null?SpanLike.CreateVector4(mesh.VertexBuffer.Colors.Bytes) : default; int before = positions.Length; var verts = new Vertex[positions.Length]; for (int i = 0; i < positions.Length; i++) { var pos = positions[i]; Vector3?normal = null; if (normals.Length > 0) { normal = normals[i]; } Vector2?uv = null; if (UVs.Length > 0) { uv = UVs[i]; } SkinJoints?joint = null; if (joints.Length > 0) { joint = joints[i]; } Vector4?weight = null; if (weights.Length > 0) { weight = weights[i]; } Vector4?color = null; if (colors.Length > 0) { color = colors[i]; } verts[i] = new Vertex(i, pos, normal, uv, joint, weight, color); } Array.Sort(verts); // var vertices = new Span<Vertex>(verts); var vertices = new ArraySegment <Vertex>(verts); var stride = 200; // 総当り比較するブロック数。小さいほど計算は早いが余裕を持たせてある程度の値を確保している for (int i = 0; i < vertices.Count; i += (stride)) { stride = Math.Min(stride, vertices.Count - i); var dividedIndices = vertices.Slice(i, stride); FindMergeTargetVertices(ref dividedIndices); } // 統合する頂点の組み合わせを作成 var mergePairVatexDictionary = new Dictionary <int, int>(); // key: remove vertex index , value : merge target vertex index foreach (var v in vertices) { if (v.IsMergeTarget) { continue; } if (v.targetIndex > 0) { if (mergePairVatexDictionary.ContainsKey(v.Index)) { continue; } mergePairVatexDictionary.Add(v.Index, v.targetIndex); } } // 統合されて消える頂点ID群を作成 var removeIndexList = new List <int>(); // index補正用 var removeIndexHashSet = new HashSet <int>(); foreach (var v in vertices) { if (v.IsMergeTarget) { continue; } if (v.targetIndex > 0) { removeIndexList.Add(v.Index); removeIndexHashSet.Add(v.Index); } } removeIndexList.Sort(); int resizedLength = positions.Length - removeIndexHashSet.Count(); var indexOffsetArray = new int[indices.Length]; int offset = 0; int currentIndex = 0; // 統合後の頂点IDの補正値を計算 foreach (var index in removeIndexList) { for (int i = currentIndex; i <= index; i++) { indexOffsetArray[i] = offset; } currentIndex = index + 1; offset++; } for (int i = currentIndex; i < indices.Length; i++) { indexOffsetArray[i] = offset; } // merge vertex index for (int i = 0; i < indices.Length; i++) { if (mergePairVatexDictionary.ContainsKey(indices[i])) { var newIndex = mergePairVatexDictionary[indices[i]]; var correctedIndex = newIndex - indexOffsetArray[newIndex]; if (correctedIndex < 0 || correctedIndex >= resizedLength) { throw new IndexOutOfRangeException("recalculated vertex index are invalid after vertex merged"); } indices[i] = correctedIndex; } else { var correctedIndex = indices[i] - indexOffsetArray[indices[i]]; if (correctedIndex < 0 || correctedIndex > resizedLength) { throw new IndexOutOfRangeException("recalculated vertex index are invalid after vertex merged"); } indices[i] = correctedIndex; } } if (positions.Length > ushort.MaxValue) { mesh.IndexBuffer.Assign(indices.AsSpan()); } else { mesh.IndexBuffer.AssignAsShort(indices.AsSpan()); } // 統合後、平均値を割り当てるパラメータは更新する foreach (var v in vertices) { if (v.IsMergeTarget) { if (normals != null) { normals[v.Index] = v.Normal.Value; } if (colors != null) { colors[v.Index] = v.Color.Value; } } } // merge vertex var newPositions = new Vector3[resizedLength]; var newNormals = normals != null ? new Vector3[resizedLength] : null; var newJoints = joints != null ? new SkinJoints[resizedLength] : null; var newWeights = weights != null ? new Vector4[resizedLength] : null; var newUVs = UVs != null ? new Vector2[resizedLength] : null; var newColors = colors != null ? new Vector4[resizedLength] : null; for (int i = 0, targetIndex = 0; i < positions.Length; i++) { if (removeIndexHashSet.Contains(i)) { continue; } newPositions[targetIndex] = positions[i]; if (normals != null) { newNormals[targetIndex] = normals[i]; } if (joints != null) { newJoints[targetIndex] = joints[i]; } if (weights != null) { newWeights[targetIndex] = weights[i]; } if (UVs != null) { newUVs[targetIndex] = UVs[i]; } if (colors != null) { newColors[targetIndex] = colors[i]; } targetIndex++; } mesh.VertexBuffer.Positions.Assign(newPositions.AsSpan()); if (normals != null) { mesh.VertexBuffer.Normals.Assign(newNormals.AsSpan()); } if (joints != null) { mesh.VertexBuffer.Joints.Assign(newJoints.AsSpan()); } if (weights != null) { mesh.VertexBuffer.Weights.Assign(newWeights.AsSpan()); } if (UVs != null) { mesh.VertexBuffer.TexCoords.Assign(newUVs.AsSpan()); } if (colors != null) { mesh.VertexBuffer.Colors.Assign(newColors.AsSpan()); } mesh.VertexBuffer.RemoveTangent(); mesh.VertexBuffer.ValidateLength(); // // merge morph vertex buffer // Span <Vector3> morphPositions = null; Span <Vector3> morphNormals = null; Span <Vector2> morphUVs = null; foreach (var morph in mesh.MorphTargets) { morphPositions = morph.VertexBuffer.Positions != null?morph.VertexBuffer.Positions.GetSpan <Vector3>() : null; morphNormals = morph.VertexBuffer.Normals != null?morph.VertexBuffer.Normals.GetSpan <Vector3>() : null; morphUVs = morph.VertexBuffer.TexCoords != null?morph.VertexBuffer.TexCoords.GetSpan <Vector2>() : null; for (int i = 0, targetIndex = 0; i < positions.Length; i++) { if (removeIndexHashSet.Contains(i)) { continue; } if (morphPositions != null) { newPositions[targetIndex] = morphPositions[i]; } if (morphNormals != null) { newNormals[targetIndex] = morphNormals[i]; } if (morphUVs != null) { newUVs[targetIndex] = morphUVs[i]; } targetIndex++; } if (morphPositions != null) { morph.VertexBuffer.Positions.Assign(newPositions.AsSpan()); } if (morphNormals != null) { morph.VertexBuffer.Normals.Assign(newNormals.AsSpan()); } if (morphUVs != null) { morph.VertexBuffer.Normals.Assign(newUVs.AsSpan()); } morph.VertexBuffer.RemoveTangent(); morph.VertexBuffer.ValidateLength(morph.Name); } return($"({before} => {resizedLength})"); }