Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support for non triangle topology #692

Merged
merged 9 commits into from
Nov 5, 2023
1 change: 1 addition & 0 deletions CHANGELOG-PRERELEASE.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ The format is based on [Keep a Changelog].
### Added
- Remove Zero Sized Polygons `#659`
- Add support for UniVRM components `#653`
- Support for Mesh Topologies other than Triangles `#692`

### Changed

Expand Down
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ The format is based on [Keep a Changelog].
- Ability to prevent changing enablement of component `#668`
- Remove Zero Sized Polygons `#659`
- Add support for UniVRM components `#653`
- Support for Mesh Topologies other than Triangles `#692`

### Changed
- All logs passed to ErrorReport is now shown on the console log `#643`
Expand Down
20 changes: 13 additions & 7 deletions Editor/Processors/SkinnedMeshes/MergeSkinnedMeshProcessor.cs
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ public override void Process(BuildContext context, MeshInfo2 target)

foreach (var meshInfo2 in meshInfos) meshInfo2.FlattenMultiPassRendering("Merge Skinned Mesh");

var sourceMaterials = meshInfos.Select(x => x.SubMeshes.Select(y => y.SharedMaterial).ToArray()).ToArray();
var sourceMaterials = meshInfos.Select(x => x.SubMeshes.Select(y => (y.Topology, y.SharedMaterial)).ToArray()).ToArray();
Profiler.EndSample();

Profiler.BeginSample("Material Normal Configuration Check");
Expand Down Expand Up @@ -97,7 +97,7 @@ public override void Process(BuildContext context, MeshInfo2 target)
target.Clear();
target.SubMeshes.Capacity = Math.Max(target.SubMeshes.Capacity, materials.Count);
foreach (var material in materials)
target.SubMeshes.Add(new SubMesh(material));
target.SubMeshes.Add(new SubMesh(material.material, material.topology));

TexCoordStatus TexCoordStatusMax(TexCoordStatus x, TexCoordStatus y) =>
(TexCoordStatus)Math.Max((int)x, (int)y);
Expand All @@ -124,7 +124,10 @@ TexCoordStatus TexCoordStatusMax(TexCoordStatus x, TexCoordStatus y) =>
for (var j = 0; j < meshInfo.SubMeshes.Count; j++)
{
var targetSubMeshIndex = subMeshIndexMap[i][j];
target.SubMeshes[targetSubMeshIndex].Triangles.AddRange(meshInfo.SubMeshes[j].Triangles);
var targetSubMesh = target.SubMeshes[targetSubMeshIndex];
var sourceSubMesh = meshInfo.SubMeshes[j];
System.Diagnostics.Debug.Assert(targetSubMesh.Topology == sourceSubMesh.Topology);
targetSubMesh.Vertices.AddRange(sourceSubMesh.Vertices);
mappings.Add(($"m_Materials.Array.data[{j}]",
$"m_Materials.Array.data[{targetSubMeshIndex}]"));
}
Expand Down Expand Up @@ -259,11 +262,12 @@ TexCoordStatus TexCoordStatusMax(TexCoordStatus x, TexCoordStatus y) =>
#endif
}

private (int[][] mapping, List<Material> materials) CreateMergedMaterialsAndSubMeshIndexMapping(
Material[][] sourceMaterials)
private (int[][] mapping, List<(MeshTopology topology, Material material)> materials)
CreateMergedMaterialsAndSubMeshIndexMapping(
(MeshTopology topology, Material material)[][] sourceMaterials)
{
var doNotMerges = Component.doNotMergeMaterials.GetAsSet();
var resultMaterials = new List<Material>();
var resultMaterials = new List<(MeshTopology, Material)>();
var resultIndices = new int[sourceMaterials.Length][];

for (var i = 0; i < sourceMaterials.Length; i++)
Expand All @@ -275,7 +279,7 @@ TexCoordStatus TexCoordStatusMax(TexCoordStatus x, TexCoordStatus y) =>
{
var material = materials[j];
var foundIndex = resultMaterials.IndexOf(material);
if (doNotMerges.Contains(material) || foundIndex == -1)
if (doNotMerges.Contains(material.material) || foundIndex == -1)
{
indices[j] = resultMaterials.Count;
resultMaterials.Add(material);
Expand Down Expand Up @@ -336,10 +340,12 @@ public Material[] Materials(bool fast = true)
{
var sourceMaterials = _processor.SkinnedMeshRenderers.Select(EditSkinnedMeshComponentUtil.GetMaterials)
.Concat(_processor.StaticMeshRenderers.Select(x => x.sharedMaterials))
.Select(a => a.Select(b => (MeshTopology.Triangles, b)).ToArray())
.ToArray();

return _processor.CreateMergedMaterialsAndSubMeshIndexMapping(sourceMaterials)
.materials
.Select(x => x.material)
.ToArray();
}

Expand Down
22 changes: 11 additions & 11 deletions Editor/Processors/SkinnedMeshes/MergeToonLitMaterialProcessor.cs
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ public override void Process(BuildContext context, MeshInfo2 target)
foreach (var v in target.Vertices) users[v] = 0;

foreach (var targetSubMesh in target.SubMeshes)
foreach (var v in targetSubMesh.Triangles.Distinct())
foreach (var v in targetSubMesh.Vertices.Distinct())
users[v]++;

// compute per-material data
Expand All @@ -55,30 +55,30 @@ public override void Process(BuildContext context, MeshInfo2 target)
var subMesh = target.SubMeshes[subMeshI];
var targetRect = targetRectForMaterial[subMeshI];
var vertexCache = new Dictionary<Vertex, Vertex>();
for (var i = 0; i < subMesh.Triangles.Count; i++)
for (var i = 0; i < subMesh.Vertices.Count; i++)
{
if (vertexCache.TryGetValue(subMesh.Triangles[i], out var cached))
if (vertexCache.TryGetValue(subMesh.Vertices[i], out var cached))
{
subMesh.Triangles[i] = cached;
subMesh.Vertices[i] = cached;
continue;
}
if (users[subMesh.Triangles[i]] != 1)
if (users[subMesh.Vertices[i]] != 1)
{
// if there are multiple users for the vertex: duplicate it
var cloned = subMesh.Triangles[i].Clone();
var cloned = subMesh.Vertices[i].Clone();
target.Vertices.Add(cloned);

users[subMesh.Triangles[i]]--;
users[subMesh.Vertices[i]]--;

vertexCache[subMesh.Triangles[i]] = cloned;
subMesh.Triangles[i] = cloned;
vertexCache[subMesh.Vertices[i]] = cloned;
subMesh.Vertices[i] = cloned;
}
else
{
vertexCache[subMesh.Triangles[i]] = subMesh.Triangles[i];
vertexCache[subMesh.Vertices[i]] = subMesh.Vertices[i];
}

subMesh.Triangles[i].TexCoord0 = MapUV(subMesh.Triangles[i].TexCoord0, targetRect);
subMesh.Vertices[i].TexCoord0 = MapUV(subMesh.Vertices[i].TexCoord0, targetRect);
}
}
}
Expand Down
135 changes: 98 additions & 37 deletions Editor/Processors/SkinnedMeshes/MeshInfo2.cs
Original file line number Diff line number Diff line change
Expand Up @@ -124,7 +124,7 @@ private void SetMaterials(Renderer renderer)
public void AssertInvariantContract(string context)
{
var vertices = new HashSet<Vertex>(Vertices);
Debug.Assert(SubMeshes.SelectMany(x => x.Triangles).All(vertices.Contains),
Debug.Assert(SubMeshes.SelectMany(x => x.Vertices).All(vertices.Contains),
$"{context}: some SubMesh has invalid triangles");
var bones = new HashSet<Bone>(Bones);
Debug.Assert(Vertices.SelectMany(x => x.BoneWeights).Select(x => x.bone).All(bones.Contains),
Expand Down Expand Up @@ -336,11 +336,15 @@ public void ReadStaticMesh([NotNull] Mesh mesh)
// ReSharper restore AccessToModifiedClosure
}

var triangles = mesh.triangles;
SubMeshes.Clear();
SubMeshes.Capacity = Math.Max(SubMeshes.Capacity, mesh.subMeshCount);

var triangles = new List<int>();
for (var i = 0; i < mesh.subMeshCount; i++)
{
mesh.GetIndices(triangles, i);
SubMeshes.Add(new SubMesh(Vertices, triangles, mesh.GetSubMesh(i)));
}
Profiler.EndSample();
}

Expand Down Expand Up @@ -412,7 +416,7 @@ public void FlattenMultiPassRendering(string reasonComponent)
SubMeshes.Clear();
foreach (var subMesh in subMeshes)
foreach (var material in subMesh.SharedMaterials)
SubMeshes.Add(new SubMesh(subMesh.Triangles, material));
SubMeshes.Add(new SubMesh(subMesh, material));
}

public void WriteToMesh(Mesh destMesh)
Expand Down Expand Up @@ -512,59 +516,50 @@ public void WriteToMesh(Mesh destMesh)
for (var i = 0; i < Vertices.Count; i++)
vertexIndices.Add(Vertices[i], i);

var totalTriangles = 0;
var maxIndices = 0;
var totalSubMeshes = 0;
for (var i = 0; i < SubMeshes.Count - 1; i++)
{
maxIndices = Mathf.Max(maxIndices, SubMeshes[i].Vertices.Count);
// for non-last submesh, we have to duplicate submesh for multi pass rendering
for (var j = 0; j < SubMeshes[i].SharedMaterials.Length; j++)
{
totalTriangles += SubMeshes[i].Triangles.Count;
totalSubMeshes++;
}
}
{
maxIndices = Mathf.Max(maxIndices, SubMeshes[SubMeshes.Count - 1].Vertices.Count);
// for last submesh, we can use single submesh for multi pass reendering
totalTriangles += SubMeshes[SubMeshes.Count - 1].Triangles.Count;
totalSubMeshes++;
}

var triangles = new int[totalTriangles];
var subMeshDescriptors = new SubMeshDescriptor[totalSubMeshes];
var trianglesIndex = 0;
var indices = new int[maxIndices];
var submeshIndex = 0;

destMesh.indexFormat = Vertices.Count <= ushort.MaxValue ? IndexFormat.UInt16 : IndexFormat.UInt32;
destMesh.subMeshCount = totalSubMeshes;

for (var i = 0; i < SubMeshes.Count - 1; i++)
{
var subMesh = SubMeshes[i];
var descriptor = new SubMeshDescriptor(trianglesIndex, subMesh.Triangles.Count);
foreach (var triangle in subMesh.Triangles)
triangles[trianglesIndex++] = vertexIndices[triangle];

for (var index = 0; index < subMesh.Vertices.Count; index++)
indices[index] = vertexIndices[subMesh.Vertices[index]];

// general case: for non-last submesh, we have to duplicate submesh for multi pass rendering
for (var j = 0; j < subMesh.SharedMaterials.Length; j++)
subMeshDescriptors[submeshIndex++] = descriptor;
destMesh.SetIndices(indices, 0, subMesh.Vertices.Count, subMesh.Topology, submeshIndex++);
}

{
var subMesh = SubMeshes[SubMeshes.Count - 1];

var descriptor = new SubMeshDescriptor(trianglesIndex, subMesh.Triangles.Count);
foreach (var triangle in subMesh.Triangles)
triangles[trianglesIndex++] = vertexIndices[triangle];
for (var index = 0; index < subMesh.Vertices.Count; index++)
indices[index] = vertexIndices[subMesh.Vertices[index]];

// for last submesh, we can use single submesh for multi pass reendering
subMeshDescriptors[submeshIndex++] = descriptor;
destMesh.SetIndices(indices, 0, subMesh.Vertices.Count, subMesh.Topology, submeshIndex++);
}

Debug.Assert(subMeshDescriptors.Length == submeshIndex);
Debug.Assert(triangles.Length == trianglesIndex);

destMesh.indexFormat = Vertices.Count <= ushort.MaxValue ? IndexFormat.UInt16 : IndexFormat.UInt32;
destMesh.triangles = triangles;
destMesh.subMeshCount = submeshIndex;
for (var i = 0; i < subMeshDescriptors.Length; i++)
destMesh.SetSubMesh(i, subMeshDescriptors[i]);
Debug.Assert(totalSubMeshes == submeshIndex);
}
Profiler.EndSample();

Expand Down Expand Up @@ -674,8 +669,19 @@ public void WriteToMeshRenderer(MeshRenderer targetRenderer)

internal class SubMesh
{
public readonly MeshTopology Topology = MeshTopology.Triangles;

// size of this must be 3 * n
public readonly List<Vertex> Triangles = new List<Vertex>();
public List<Vertex> Triangles
{
get
{
Debug.Assert(Topology == MeshTopology.Triangles);
return Vertices;
}
}

public List<Vertex> Vertices { get; } = new List<Vertex>();

public Material SharedMaterial
{
Expand All @@ -689,18 +695,73 @@ public SubMesh()
{
}

public SubMesh(List<Vertex> vertices) => Triangles = vertices;
public SubMesh(List<Vertex> vertices) => Vertices = vertices;
public SubMesh(List<Vertex> vertices, Material sharedMaterial) =>
(Triangles, SharedMaterial) = (vertices, sharedMaterial);
public SubMesh(Material sharedMaterial) =>
SharedMaterial = sharedMaterial;
(Vertices, SharedMaterial) = (vertices, sharedMaterial);
public SubMesh(Material sharedMaterial) => SharedMaterial = sharedMaterial;
public SubMesh(Material sharedMaterial, MeshTopology topology) =>
(SharedMaterial, Topology) = (sharedMaterial, topology);

public SubMesh(SubMesh subMesh, Material triangles)
{
Topology = subMesh.Topology;
Vertices = new List<Vertex>(subMesh.Vertices);
SharedMaterial = triangles;
}

public SubMesh(List<Vertex> vertices, ReadOnlySpan<int> triangles, SubMeshDescriptor descriptor)
public SubMesh(List<Vertex> vertices, List<int> triangles, SubMeshDescriptor descriptor)
{
Assert.AreEqual(MeshTopology.Triangles, descriptor.topology);
Triangles.Capacity = descriptor.indexCount;
foreach (var i in triangles.Slice(descriptor.indexStart, descriptor.indexCount))
Triangles.Add(vertices[i]);
Topology = descriptor.topology;
Vertices.Capacity = descriptor.indexCount;
foreach (var i in triangles)
Vertices.Add(vertices[i]);
}

public bool TryGetPrimitiveSize(string component, out int primitiveSize)
{
switch (Topology)
{
case MeshTopology.Triangles:
primitiveSize = 3;
return true;
case MeshTopology.Quads:
primitiveSize = 4;
return true;
case MeshTopology.Lines:
primitiveSize = 2;
return true;
case MeshTopology.Points:
primitiveSize = 1;
return true;
case MeshTopology.LineStrip:
BuildReport.LogWarning("MeshInfo2:warning:lineStrip", component);
primitiveSize = default;
return false;
default:
throw new ArgumentOutOfRangeException();
}
}

public void RemovePrimitives(string component, Func<Vertex[], bool> condition)
{
if (!TryGetPrimitiveSize(component, out var primitiveSize))
return;
var primitiveBuffer = new Vertex[primitiveSize];
int srcI = 0, dstI = 0;
for (; srcI < Vertices.Count; srcI += primitiveSize)
{
for (var i = 0; i < primitiveSize; i++)
primitiveBuffer[i] = Vertices[srcI + i];

if (condition(primitiveBuffer))
continue;

// no vertex is in box:
for (var i = 0; i < primitiveSize; i++)
Vertices[dstI + i] = primitiveBuffer[i];
dstI += primitiveSize;
}
Vertices.RemoveRange(dstI, Vertices.Count - dstI);
}
}

Expand Down
23 changes: 3 additions & 20 deletions Editor/Processors/SkinnedMeshes/RemoveMeshByBlendShapeProcessor.cs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
using System;
using System.Collections.Generic;
using System.Linq;
using nadena.dev.ndmf;
Expand Down Expand Up @@ -26,27 +27,9 @@ public override void Process(BuildContext context, MeshInfo2 target)
byBlendShapeVertices.Add(vertex);
}

Func<Vertex[], bool> condition = primitive => primitive.Any(byBlendShapeVertices.Contains);
foreach (var subMesh in target.SubMeshes)
{
int srcI = 0, dstI = 0;
for (; srcI < subMesh.Triangles.Count; srcI += 3)
{
// process 3 vertex in sub mesh at once to process one polygon
var v0 = subMesh.Triangles[srcI + 0];
var v1 = subMesh.Triangles[srcI + 1];
var v2 = subMesh.Triangles[srcI + 2];

if (byBlendShapeVertices.Contains(v0) || byBlendShapeVertices.Contains(v1) || byBlendShapeVertices.Contains(v2))
continue;

// no vertex is affected by the blend shape:
subMesh.Triangles[dstI + 0] = v0;
subMesh.Triangles[dstI + 1] = v1;
subMesh.Triangles[dstI + 2] = v2;
dstI += 3;
}
subMesh.Triangles.RemoveRange(dstI, subMesh.Triangles.Count - dstI);
}
subMesh.RemovePrimitives("RemoveMeshByBlendShape", condition);

// remove unused vertices
target.Vertices.RemoveAll(x => byBlendShapeVertices.Contains(x));
Expand Down
Loading