2025-04-22 17:16:40 +08:00

562 lines
17 KiB
C#

using System;
using System.Linq;
using System.Text;
using Unity.Collections;
using UnityEngine;
using UnityEngine.Rendering;
namespace Unity.HLODSystem.Utils
{
public static class MeshExtensions
{
public static WorkingMesh ToWorkingMesh(this Mesh mesh, Allocator allocator)
{
var bindposes = mesh.bindposes;
var wm = new WorkingMesh(allocator, mesh.vertexCount, mesh.triangles.Length, mesh.subMeshCount, bindposes.Length);
mesh.ApplyToWorkingMesh(ref wm, bindposes);
return wm;
}
// Taking bindposes optional parameter is ugly, but saves an additional array allocation if it was already
// accessed to get the length
public static void ApplyToWorkingMesh(this Mesh mesh, ref WorkingMesh wm, Matrix4x4[] bindposes = null)
{
wm.indexFormat = mesh.indexFormat;
wm.vertices = mesh.vertices;
wm.normals = mesh.normals;
wm.tangents = mesh.tangents;
wm.uv = mesh.uv;
wm.uv2 = mesh.uv2;
wm.uv3 = mesh.uv3;
wm.uv4 = mesh.uv4;
wm.colors = mesh.colors;
wm.boneWeights = mesh.boneWeights;
wm.bindposes = bindposes ?? mesh.bindposes;
wm.subMeshCount = mesh.subMeshCount;
for (int i = 0; i < mesh.subMeshCount; i++)
{
wm.SetTriangles(mesh.GetTriangles(i), i);
}
wm.name = mesh.name;
wm.bounds = mesh.bounds;
}
}
public class WorkingMesh : IDisposable
{
private NativeArray<int> m_detector = new NativeArray<int>(1, Allocator.Persistent);
enum Channel
{
Vertices,
Normals,
Tangents,
UV,
UV2,
UV3,
UV4,
Colors,
BoneWeights,
Bindposes,
Triangles,
SubmeshOffset
}
const int k_MaxNameSize = 128;
public Vector3[] vertices
{
get
{
return m_Vertices.Slice(0, vertexCount).ToArray();
}
set
{
vertexCount = value.Length;
m_Vertices.Slice(0, vertexCount).CopyFrom(value);
}
}
NativeArray<Vector3> m_Vertices;
public int vertexCount
{
get { return m_Counts[(int)Channel.Vertices]; }
private set { m_Counts[(int)Channel.Vertices] = value; }
}
public int[] triangles
{
get { return m_Triangles.Slice(0, trianglesCount).ToArray(); }
set
{
subMeshCount = 1;
trianglesCount = value.Length;
SetTriangles(value, 0);
}
}
NativeArray<int> m_Triangles;
int trianglesCount
{
get { return m_Counts[(int)Channel.Triangles]; }
set { m_Counts[(int)Channel.Triangles] = value; }
}
public Vector3[] normals
{
get { return m_Normals.Slice(0, normalsCount).ToArray(); }
set
{
if (value == null || value.Length == 0)
{
normalsCount = 0;
}
else
{
normalsCount = value.Length;
m_Normals.Slice(0, normalsCount).CopyFrom(value);
}
}
}
NativeArray<Vector3> m_Normals;
int normalsCount
{
get { return m_Counts[(int)Channel.Normals]; }
set { m_Counts[(int)Channel.Normals] = value; }
}
public Vector4[] tangents
{
get { return m_Tangents.Slice(0, tangentsCount).ToArray(); }
set
{
if (value == null || value.Length == 0)
{
tangentsCount = 0;
}
else
{
tangentsCount = value.Length;
m_Tangents.Slice(0, tangentsCount).CopyFrom(value);
}
}
}
NativeArray<Vector4> m_Tangents;
int tangentsCount
{
get { return m_Counts[(int)Channel.Tangents]; }
set { m_Counts[(int)Channel.Tangents] = value; }
}
public Vector2[] uv
{
get { return m_UV.Slice(0, uvCount).ToArray(); }
set
{
if (value == null || value.Length == 0)
{
uvCount = 0;
}
else
{
uvCount = value.Length;
m_UV.Slice(0, uvCount).CopyFrom(value);
}
}
}
NativeArray<Vector2> m_UV;
int uvCount
{
get { return m_Counts[(int)Channel.UV]; }
set { m_Counts[(int)Channel.UV] = value; }
}
public Vector2[] uv2
{
get { return m_UV2.Slice(0, uv2Count).ToArray(); }
set
{
if (value == null || value.Length == 0)
{
uv2Count = 0;
}
else
{
uv2Count = value.Length;
m_UV2.Slice(0, uv2Count).CopyFrom(value);
}
}
}
NativeArray<Vector2> m_UV2;
int uv2Count
{
get { return m_Counts[(int)Channel.UV2]; }
set { m_Counts[(int)Channel.UV2] = value; }
}
public Vector2[] uv3
{
get { return m_UV3.Slice(0, uv3Count).ToArray(); }
set
{
if (value == null || value.Length == 0)
{
uv3Count = 0;
}
else
{
uv3Count = value.Length;
m_UV3.Slice(0, uv3Count).CopyFrom(value);
}
}
}
NativeArray<Vector2> m_UV3;
int uv3Count
{
get { return m_Counts[(int)Channel.UV3]; }
set { m_Counts[(int)Channel.UV3] = value; }
}
public Vector2[] uv4
{
get { return m_UV4.Slice(0, uv4Count).ToArray(); }
set
{
if (value == null || value.Length == 0)
{
uv4Count = 0;
}
else
{
uv4Count = value.Length;
m_UV4.Slice(0, uv4Count).CopyFrom(value);
}
}
}
NativeArray<Vector2> m_UV4;
int uv4Count
{
get { return m_Counts[(int)Channel.UV4]; }
set { m_Counts[(int)Channel.UV4] = value; }
}
public Color[] colors
{
get { return m_Colors.Slice(0, colorsCount).ToArray(); }
set
{
if (value == null || value.Length == 0)
{
colorsCount = 0;
}
else
{
colorsCount = value.Length;
m_Colors.Slice(0, colorsCount).CopyFrom(value);
}
}
}
NativeArray<Color> m_Colors;
int colorsCount
{
get { return m_Counts[(int)Channel.Colors]; }
set { m_Counts[(int)Channel.Colors] = value; }
}
public Color32[] colors32
{
get { return colors != null ? colors.Select(c => (Color32)c).ToArray() : null; }
set { colors = value != null ? value.Select(c => (Color)c).ToArray() : null; }
}
public BoneWeight[] boneWeights
{
get { return m_BoneWeights.Slice(0, boneWeightsCount).ToArray(); }
set
{
if (value == null || value.Length == 0)
{
boneWeightsCount = 0;
}
else
{
boneWeightsCount = value.Length;
m_BoneWeights.Slice(0, boneWeightsCount).CopyFrom(value);
}
}
}
NativeArray<BoneWeight> m_BoneWeights;
int boneWeightsCount
{
get { return m_Counts[(int)Channel.BoneWeights]; }
set { m_Counts[(int)Channel.BoneWeights] = value; }
}
public Matrix4x4[] bindposes
{
get { return m_Bindposes.Slice(0, bindposesCount).ToArray(); }
set
{
if (value == null || value.Length == 0)
{
bindposesCount = 0;
}
else
{
bindposesCount = value.Length;
m_Bindposes.Slice(0, bindposesCount).CopyFrom(value);
}
}
}
NativeArray<Matrix4x4> m_Bindposes;
int bindposesCount
{
get { return m_Counts[(int)Channel.Bindposes]; }
set { m_Counts[(int)Channel.Bindposes] = value; }
}
public int subMeshCount
{
get { return submeshOffsetCount; }
set
{
if (submeshOffsetCount == value)
return;
var previousCount = submeshOffsetCount;
submeshOffsetCount = value;
for (var i = previousCount; i < submeshOffsetCount; i++)
{
// Initialize these offsets to be invalid, so we don't use stale values
m_SubmeshOffset[i] = -1;
}
}
}
NativeArray<int> m_SubmeshOffset;
int submeshOffsetCount
{
get { return m_Counts[(int)Channel.SubmeshOffset]; }
set { m_Counts[(int)Channel.SubmeshOffset] = value; }
}
public string name
{
get { return Encoding.UTF8.GetString(m_Name.ToArray()); }
set
{
if (value == null)
value = string.Empty;
var bytes = Encoding.UTF8.GetBytes(value);
var length = Mathf.Min(bytes.Length, k_MaxNameSize);
m_Name.Slice(0, length).CopyFrom(bytes);
}
}
NativeArray<byte> m_Name;
// This data does not cross the job threshold, so if it needs to be read back, then it will need to be
// in a NativeArray or some other type of NativeContainer
public IndexFormat indexFormat { get; set; }
public Bounds bounds { get; set; }
NativeArray<int> m_Counts;
// These are stubbed out for API completeness, but obviously don't do anything
public void RecalculateBounds() { }
public void RecalculateNormals() { }
public void RecalculateTangents() { }
public void SetTriangles(int[] triangles, int submesh)
{
if (submesh >= subMeshCount)
subMeshCount = submesh + 1;
var preSliceLength = m_SubmeshOffset[submesh];
if (preSliceLength < 0)
{
if (submesh > 0)
{
m_SubmeshOffset[submesh] = trianglesCount;
preSliceLength = trianglesCount;
}
else
{
m_SubmeshOffset[submesh] = 0;
preSliceLength = 0;
}
}
var totalCount = preSliceLength; // count prior to submesh
totalCount += triangles.Length; // new submesh triangle count
var postSliceOffset = 0;
var postSliceLength = 0;
if (submesh < subMeshCount - 2) // count of all triangles after submesh
{
postSliceOffset = m_SubmeshOffset[submesh + 1];
if (postSliceOffset >= 0)
{
postSliceLength = trianglesCount - postSliceOffset;
totalCount += postSliceLength;
}
}
trianglesCount = totalCount;
// Shift other following triangles up/down
if (postSliceOffset > 0)
{
var offset = preSliceLength + triangles.Length;
m_SubmeshOffset[submesh + 1] = offset;
var sourceSlice = new NativeSlice<int>(m_Triangles, postSliceOffset, postSliceLength);
var destSlice = new NativeSlice<int>(m_Triangles, offset, postSliceLength);
destSlice.CopyFrom(sourceSlice);
}
m_Triangles.Slice(preSliceLength, triangles.Length).CopyFrom(triangles);
}
public int[] GetTriangles(int submesh)
{
if (submesh < m_SubmeshOffset.Length)
{
var start = 0;
var stop = 0;
GetTriangleRange(submesh, out start, out stop);
var length = stop - start;
var slice = new NativeSlice<int>(m_Triangles, start, length);
return slice.ToArray();
}
return new int[0];
}
void GetTriangleRange(int submesh, out int start, out int stop)
{
if (submesh < m_SubmeshOffset.Length)
{
start = m_SubmeshOffset[submesh];
stop = trianglesCount;
if (submesh < m_SubmeshOffset.Length - 1)
stop = m_SubmeshOffset[submesh + 1];
return;
}
start = -1;
stop = -1;
return;
}
public WorkingMesh(Allocator allocator, int maxVertices, int maxTriangles, int maxSubmeshes, int maxBindposes)
{
m_Counts = new NativeArray<int>(Enum.GetValues(typeof(Channel)).Length, allocator);
m_Vertices = new NativeArray<Vector3>(maxVertices, allocator);
m_Normals = new NativeArray<Vector3>(maxVertices, allocator);
m_Tangents = new NativeArray<Vector4>(maxVertices, allocator);
m_UV = new NativeArray<Vector2>(maxVertices, allocator);
m_UV2 = new NativeArray<Vector2>(maxVertices, allocator);
m_UV3 = new NativeArray<Vector2>(maxVertices, allocator);
m_UV4 = new NativeArray<Vector2>(maxVertices, allocator);
m_Colors = new NativeArray<Color>(maxVertices, allocator);
m_BoneWeights = new NativeArray<BoneWeight>(maxVertices, allocator);
m_Bindposes = new NativeArray<Matrix4x4>(maxBindposes, allocator);
m_Name = new NativeArray<byte>(k_MaxNameSize, allocator);
m_SubmeshOffset = new NativeArray<int>(maxSubmeshes, allocator);
m_Triangles = new NativeArray<int>(maxTriangles, allocator);
}
public void Dispose()
{
if (m_Counts.IsCreated)
m_Counts.Dispose();
if (m_Vertices.IsCreated)
m_Vertices.Dispose();
if (m_Normals.IsCreated)
m_Normals.Dispose();
if (m_Tangents.IsCreated)
m_Tangents.Dispose();
if (m_UV.IsCreated)
m_UV.Dispose();
if (m_UV2.IsCreated)
m_UV2.Dispose();
if (m_UV3.IsCreated)
m_UV3.Dispose();
if (m_UV4.IsCreated)
m_UV4.Dispose();
if (m_Colors.IsCreated)
m_Colors.Dispose();
if (m_BoneWeights.IsCreated)
m_BoneWeights.Dispose();
if (m_Bindposes.IsCreated)
m_Bindposes.Dispose();
if (m_Name.IsCreated)
m_Name.Dispose();
if (m_SubmeshOffset.IsCreated)
m_SubmeshOffset.Dispose();
if (m_Triangles.IsCreated)
m_Triangles.Dispose();
m_detector.Dispose();
}
public Mesh ToMesh()
{
Mesh mesh = new Mesh();
ApplyToMesh(mesh);
return mesh;
}
public void ApplyToMesh(Mesh mesh)
{
mesh.indexFormat = indexFormat;
if (vertices.Length > 65535)
mesh.indexFormat = IndexFormat.UInt32;
mesh.vertices = vertices;
mesh.normals = normals;
mesh.tangents = tangents;
mesh.uv = uv;
mesh.uv2 = uv2;
mesh.uv3 = uv3;
mesh.uv4 = uv4;
mesh.colors = colors;
mesh.boneWeights = boneWeights;
mesh.bindposes = bindposes;
mesh.subMeshCount = subMeshCount;
for (int i = 0; i < subMeshCount; i++)
{
mesh.SetTriangles(GetTriangles(i), i);
}
mesh.name = name;
mesh.bounds = bounds;
mesh.RecalculateBounds();
}
}
}