Created
May 9, 2022 21:26
-
-
Save kraj0t/4b0739f8ada920980693537a13a103bf to your computer and use it in GitHub Desktop.
[Unity] MeshDataCache - collect mesh data and modify it before applying the changes to the Mesh. Compatible with VertexHelper
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using System.Collections.Generic; | |
using UnityEngine; | |
using UnityEngine.Rendering; | |
using UnityEngine.UI; | |
namespace kraj0t.Rendering | |
{ | |
/// <summary> | |
/// This class is a convenient way of collecting mesh data and avoiding having to constantly query and dirty a Unity mesh, which is BAD for performance. | |
/// | |
/// It allows you to edit to and from Unity.Mesh and VertexHelper instances. | |
/// | |
/// The current implementation is limited to the same structure as VertexHelper to ensure compatibility. However, the said class presents a number of | |
/// limitations that do not allow for easy access to the data. See <see cref="VertexHelperExtensions"/> for more info. | |
/// | |
/// Current known limitations and future improvements: | |
/// - All limitations of VertexHelper are currently inherited. See <see cref="VertexHelperExtensions"/> for more info. | |
/// - No support for submeshes | |
/// - Up to 4 uv channels. | |
/// - No bone information. | |
/// - For greater performance, this class could use the methods in the Mesh class that expect NativeArray or buffers. | |
/// </summary> | |
public class MeshDataCache | |
{ | |
private const MeshUpdateFlags DISABLE_MESH_UPDATE_FLAGS = MeshUpdateFlags.DontNotifyMeshUsers | MeshUpdateFlags.DontRecalculateBounds | MeshUpdateFlags.DontResetBoneBounds | MeshUpdateFlags.DontValidateIndices; | |
private static MeshDataCache _reusableInstance; | |
// TODO support multiple meshes. Some of these lists should be Lists of Lists. | |
public List<Vector3> vertices; | |
public List<Color32> colors; | |
public List<Vector4> uv0; | |
public List<Vector4> uv1; | |
public List<Vector4> uv2; | |
public List<Vector4> uv3; | |
public List<Vector3> normals; | |
public List<Vector4> tangents; | |
public List<int> indices; | |
public bool HasColors => colors.Count != 0; | |
public bool HasUV0 => uv0.Count != 0; | |
public bool HasUV1 => uv1.Count != 0; | |
public bool HasUV2 => uv2.Count != 0; | |
public bool HasUV3 => uv3.Count != 0; | |
public bool HasNormals => normals.Count != 0; | |
public bool HasTangents => tangents.Count != 0; | |
/// <summary>Returns an instance of MeshDataCache that can be shared across different parts of the code. | |
/// Consider using this instance instead of creating a new one.</summary> | |
public static MeshDataCache ReusableInstance => _reusableInstance ??= new MeshDataCache(); | |
public void Clear() | |
{ | |
vertices.Clear(); | |
colors.Clear(); | |
uv0.Clear(); | |
uv1.Clear(); | |
uv2.Clear(); | |
uv3.Clear(); | |
normals.Clear(); | |
tangents.Clear(); | |
indices.Clear(); | |
} | |
/// <summary>Populate the cache from the given mesh.</summary> | |
public void Read(Mesh mesh) | |
{ | |
InitializeListsIfNeeded(); | |
mesh.GetVertices(vertices); | |
mesh.GetColors(colors); | |
mesh.GetUVs(0, uv0); | |
mesh.GetUVs(1, uv1); | |
mesh.GetUVs(2, uv2); | |
mesh.GetUVs(3, uv3); | |
mesh.GetNormals(normals); | |
mesh.GetTangents(tangents); | |
mesh.GetIndices(indices, 0); | |
} | |
/// <summary>Populate the cache from the given VertexHelper. | |
/// The lists will be copied by reference from the VertexHelper. This means that, after calling this, modifying the cache's lists will affect the | |
/// VertexHelper directly, without needing to set the helper's lists all the time. This also means that when the VertexHelper is disposed this | |
/// cache will become unusable too.</summary> | |
public void Read(VertexHelper helper) | |
{ | |
vertices = helper.GetPositions(); | |
colors = helper.GetColors(); | |
uv0 = helper.GetUV0(); | |
uv1 = helper.GetUV1(); | |
uv2 = helper.GetUV2(); | |
uv3 = helper.GetUV3(); | |
normals = helper.GetNormals(); | |
tangents = helper.GetTangents(); | |
indices = helper.GetIndices(); | |
} | |
/// <summary>Feed the cache's data to the given mesh.</summary> | |
public void Write(Mesh mesh, bool recalculateBounds = true, MeshTopology topology = MeshTopology.Triangles) | |
{ | |
mesh.SetVertices(vertices); | |
mesh.SetIndices(indices, topology, 0, false); | |
if (HasColors) mesh.SetColors(colors); | |
if (HasUV0) mesh.SetUVs(0, uv0); | |
if (HasUV1) mesh.SetUVs(1, uv1); | |
if (HasUV2) mesh.SetUVs(2, uv2); | |
if (HasUV3) mesh.SetUVs(3, uv3); | |
if (HasNormals) mesh.SetNormals(normals); | |
if (HasTangents) mesh.SetTangents(tangents); | |
if (recalculateBounds) | |
{ | |
mesh.RecalculateBounds(DISABLE_MESH_UPDATE_FLAGS); | |
} | |
mesh.MarkModified(); | |
} | |
/// <summary><para>Convenience method to quickly add a new vertex to the cache.</para> | |
/// | |
/// <para>Remember that you can always target the cache's lists directly if you know what you're doing</para> | |
/// | |
/// <para>IMPORTANT: you need to manually re-triangulate after calling this method!</para></summary> | |
/// <returns>The index of the new vertex, which is the last index in the Vertices list.</returns> | |
public int AddVertex(Vector3 position, Color32 color, Vector2 uv0, Vector2 uv1, Vector2 uv2, Vector2 uv3, Vector3 normal, Vector4 tangent) | |
{ | |
InitializeListsIfNeeded(); | |
var index = vertices.Count; | |
vertices.Add(position); | |
if (HasColors) colors.Add(color); | |
if (HasUV0) this.uv0.Add(uv0); | |
if (HasUV1) this.uv1.Add(uv1); | |
if (HasUV2) this.uv2.Add(uv2); | |
if (HasUV3) this.uv3.Add(uv3); | |
if (HasNormals) normals.Add(normal); | |
if (HasTangents) tangents.Add(tangent); | |
return index; | |
} | |
/// <summary><para>Convenience method to quickly add new a vertex to the cache by interpolating it from two other vertices in the cache.</para> | |
/// | |
/// <para>Remember that you can always target the cache's lists directly if you know what you're doing</para> | |
/// | |
/// <para>IMPORTANT: you need to manually re-triangulate after calling this method!</para></summary> | |
/// <returns>The index of the new vertex, which is the last index in the Vertices list.</returns> | |
public int AddVertex(Vector3 position, int neighbor0, int neighbor1, float t) | |
{ | |
var index = vertices.Count; | |
vertices.Add(position); | |
if (HasColors) colors.Add(Color32.LerpUnclamped(colors[neighbor0], colors[neighbor1], t)); | |
if (HasUV0) uv0.Add(Vector2.LerpUnclamped(uv0[neighbor0], uv0[neighbor1], t)); | |
if (HasUV1) uv1.Add(Vector2.LerpUnclamped(uv1[neighbor0], uv1[neighbor1], t)); | |
if (HasUV2) uv2.Add(Vector2.LerpUnclamped(uv2[neighbor0], uv2[neighbor1], t)); | |
if (HasUV3) uv3.Add(Vector2.LerpUnclamped(uv3[neighbor0], uv3[neighbor1], t)); | |
if (HasNormals) normals.Add(Vector3.SlerpUnclamped(normals[neighbor0], normals[neighbor1], t)); | |
if (HasTangents) tangents.Add(Vector4.LerpUnclamped(tangents[neighbor0], tangents[neighbor1], t)); | |
return index; | |
} | |
private void InitializeListsIfNeeded() | |
{ | |
vertices ??= new List<Vector3>(); | |
colors ??= new List<Color32>(); | |
uv0 ??= new List<Vector4>(); | |
uv1 ??= new List<Vector4>(); | |
uv2 ??= new List<Vector4>(); | |
uv3 ??= new List<Vector4>(); | |
normals ??= new List<Vector3>(); | |
tangents ??= new List<Vector4>(); | |
indices ??= new List<int>(); | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment