562 lines
17 KiB
C#
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();
|
|
}
|
|
}
|
|
}
|