BITFALL/Assets/Plugins/FImpossible Creations/Plugins - Level Design/Generators Shared/Mesh Generating/VertexPaintHelper.cs

371 lines
14 KiB
C#

using UnityEngine;
using UnityEditor;
using System.Collections.Generic;
namespace FIMSpace.Generating
{
public class MeshPaintHelper
{
public Transform parent;
public Mesh mesh;
public List<Color> colors;
public List<Vector3> verts;
public Vector3[] worldPosVerts;
public Dictionary<Vector3Int, VertexCluster> clusterMap;
public bool ClustersWasGenerated { get; private set; }
float padding = 1f;
#region Caching meshes
static Dictionary<Mesh, MeshPaintHelper> cache = null;
static double cacheTime = -1;
public static void PrepareCache()
{
double currentTime = Time.unscaledTime;
#if UNITY_EDITOR
if (Application.isPlaying == false) currentTime = EditorApplication.timeSinceStartup;
#endif
if (cacheTime == currentTime) return;
cacheTime = currentTime;
cache = new Dictionary<Mesh, MeshPaintHelper>();
}
/// <summary> Using caching feature </summary>
public static MeshPaintHelper GetMeshHelper(Transform parent, Mesh m)
{
if (cache != null) if (cache.ContainsKey(m)) return cache[m];
MeshPaintHelper mesh = new MeshPaintHelper(parent, m);
if (cache != null) cache.Add(m, mesh);
return mesh;
}
#endregion
public MeshPaintHelper(Transform parent, Mesh mesh)
{
ClustersWasGenerated = false;
this.parent = parent;
this.mesh = mesh;
colors = new List<Color>();
mesh.GetColors(colors);
verts = new List<Vector3>();
mesh.GetVertices(verts);
worldPosVerts = new Vector3[(int)verts.Count];
if (parent != null)
{
for (int v = 0; v < verts.Count; v++) worldPosVerts[v] = parent.TransformPoint(verts[v]);
}
else
{
for (int v = 0; v < verts.Count; v++) worldPosVerts[v] = (verts[v]);
}
}
public bool IsValid
{
get
{
if (mesh == null) return false;
if (colors == null) return false;
if (verts == null) return false;
if (verts.Count == 0) return false;
if (verts.Count != colors.Count) return false;
return true;
}
}
//public struct VertexHelper
//{
// public Vector3 worldPos;
// public Vector3 vertPos;
// public Vector3 freeDir;
// public float collisionDistance;
// public int nearest0;
// public int nearest1;
// public int nearest2;
// public int nearest3;
// public int nearest4;
// public VertexHelper(Vector3 vPos, Vector3 wPos)
// {
// vertPos = vPos;
// worldPos = wPos;
// freeDir = Vector3.zero;
// collisionDistance = 0f;
// nearest0 = -1;
// nearest1 = -1;
// nearest2 = -1;
// nearest3 = -1;
// nearest4 = -1;
// }
//}
public class VertexCluster
{
public Vector3Int clusterID;
bool boundsSet;
public Bounds clusterBounds;
public Bounds neightbourCoverBounds;
public List<int> neightCoverVertsIn;
public List<int> vertsIn;
public VertexCluster(Vector3Int id)
{
clusterID = id; boundsSet = false;
clusterBounds = new Bounds(Vector3.zero, Vector3.zero);
vertsIn = new List<int>();
neightCoverVertsIn = new List<int>();
boundsSet = false;
}
public void AddVertex(int i, Vector3 wPos)
{
vertsIn.Add(i);
if (!boundsSet) { clusterBounds = new Bounds(wPos, Vector3.zero); boundsSet = true; }
clusterBounds.Encapsulate(wPos);
}
}
VertexCluster GetCluster(Vector3Int key)
{
VertexCluster get;
if (clusterMap.TryGetValue(key, out get)) return get;
get = new VertexCluster(key);
clusterMap.Add(key, get);
return get;
}
// Precomputing vertex structure to help out edges painting for each vertex
public void GeneratePaintingGrid(float clusterSize = 0f)
{
Vector3 lossyScale = Vector3.one;
if (parent != null) lossyScale = parent.lossyScale;
if (clusterSize < 0.01f)
{
Vector3 wBounds = Vector3.Scale(lossyScale, mesh.bounds.size);
padding = ((wBounds.x + wBounds.y + wBounds.z) / 3f) * 0.75f;
}
else
{
padding = clusterSize;
}
clusterMap = new Dictionary<Vector3Int, VertexCluster>();
for (int v = 0; v < verts.Count; v++)
{
Vector3 wPos = worldPosVerts[v];
Vector3Int key = V3toV3Int(RoundValueTo(wPos, padding));
VertexCluster cluster = GetCluster(key);
cluster.AddVertex(v, wPos);
}
float sqrtPadding = padding * padding * 0.5f;
foreach (KeyValuePair<Vector3Int, VertexCluster> item in clusterMap)
{
item.Value.neightbourCoverBounds = item.Value.clusterBounds;
VertexCluster c;
if (clusterMap.TryGetValue(item.Key + new Vector3Int(1, 0, 0), out c)) EncapsulateNeightbours(item.Value, c, sqrtPadding);
if (clusterMap.TryGetValue(item.Key + new Vector3Int(-1, 0, 0), out c)) EncapsulateNeightbours(item.Value, c, sqrtPadding);
if (clusterMap.TryGetValue(item.Key + new Vector3Int(0, 0, 1), out c)) EncapsulateNeightbours(item.Value, c, sqrtPadding);
if (clusterMap.TryGetValue(item.Key + new Vector3Int(0, 0, -1), out c)) EncapsulateNeightbours(item.Value, c, sqrtPadding);
if (clusterMap.TryGetValue(item.Key + new Vector3Int(0, 1, 0), out c)) EncapsulateNeightbours(item.Value, c, sqrtPadding);
if (clusterMap.TryGetValue(item.Key + new Vector3Int(0, -1, 0), out c)) EncapsulateNeightbours(item.Value, c, sqrtPadding);
if (clusterMap.TryGetValue(item.Key + new Vector3Int(1, 0, 1), out c)) EncapsulateNeightbours(item.Value, c, sqrtPadding);
if (clusterMap.TryGetValue(item.Key + new Vector3Int(-1, 0, 1), out c)) EncapsulateNeightbours(item.Value, c, sqrtPadding);
if (clusterMap.TryGetValue(item.Key + new Vector3Int(1, 0, -1), out c)) EncapsulateNeightbours(item.Value, c, sqrtPadding);
if (clusterMap.TryGetValue(item.Key + new Vector3Int(-1, 0, -1), out c)) EncapsulateNeightbours(item.Value, c, sqrtPadding);
//if (clusterMap.TryGetValue(item.Key + new Vector3Int(2, 0, 0), out c)) EncapsulateNeightbours(item.Value, c, sqrtPadding);
//if (clusterMap.TryGetValue(item.Key + new Vector3Int(-2, 0, 0), out c)) EncapsulateNeightbours(item.Value, c, sqrtPadding);
//if (clusterMap.TryGetValue(item.Key + new Vector3Int(0, 0, 2), out c)) EncapsulateNeightbours(item.Value, c, sqrtPadding);
//if (clusterMap.TryGetValue(item.Key + new Vector3Int(0, 0, -2), out c)) EncapsulateNeightbours(item.Value, c, sqrtPadding);
Bounds fixBounds = item.Value.neightbourCoverBounds;
if (fixBounds.size.x == 0f) fixBounds.size = new Vector3(padding * 0.05f, fixBounds.size.y, fixBounds.size.z);
if (fixBounds.size.y == 0f) fixBounds.size = new Vector3(fixBounds.size.x, padding * 0.05f, fixBounds.size.z);
if (fixBounds.size.z == 0f) fixBounds.size = new Vector3(fixBounds.size.x, fixBounds.size.y, padding * 0.05f);
item.Value.neightbourCoverBounds = fixBounds;
}
// Debug Draw
//float h = 0f;
//foreach (var item in clusterMap)
//{
// h += 0.135f;
// if (h > 1f) h -= 1f;
// Color debCol = Color.HSVToRGB(h, 0.6f, 0.6f);
// FDebug.DrawBounds3D(item.Value.neightbourCoverBounds, Color.red * 1.1f, 1f);
// //for (int i = 0; i < item.Value.vertsIn.Count - 1; i++) UnityEngine.Debug.DrawLine(worldPosVerts[item.Value.vertsIn[i]], worldPosVerts[item.Value.vertsIn[i + 1]], debCol, 1.01f);
//}
ClustersWasGenerated = true;
}
void EncapsulateNeightbours(VertexCluster cluster, VertexCluster other, float sqrtPadding)
{
for (int i = 0; i < other.vertsIn.Count; i++)
{
Vector3 pos = worldPosVerts[other.vertsIn[i]];
float dist = cluster.clusterBounds.SqrDistance(pos);
if (dist <= sqrtPadding)
{
cluster.neightbourCoverBounds.Encapsulate(pos);
cluster.neightCoverVertsIn.Add(other.vertsIn[i]);
}
}
}
public float GetEdgeFactor(int i, Vector3 vertWPos, bool upper, float falloff)
{
Vector3Int key = V3toV3Int(RoundValueTo(vertWPos, padding));
VertexCluster helperCluster;
clusterMap.TryGetValue(key + (upper ? new Vector3Int(0, 1, 0) : new Vector3Int(0, -1, 0)), out helperCluster);
var cluster = GetCluster(key);
//if ( helperCluster == null) if (vertWPos.y > cluster.clusterBounds.max.y - cluster.clusterBounds.extents.y * 0.01f) return 1f;
int highest = i;
for (int c = 0; c < cluster.vertsIn.Count; c++)
{
int othId = cluster.vertsIn[c];
Vector3 othWpos = worldPosVerts[othId];
float dist = Vector2.Distance(new Vector2(othWpos.x, othWpos.z), new Vector2(vertWPos.x, vertWPos.z));
if (dist > padding * 0.2f) continue;
if (upper)
{
if (othWpos.y > worldPosVerts[highest].y) highest = othId;
}
else
if (othWpos.y < worldPosVerts[highest].y) highest = othId;
}
if (helperCluster != null)
{
for (int c = 0; c < helperCluster.vertsIn.Count; c++)
{
int othId = helperCluster.vertsIn[c];
Vector3 othWpos = worldPosVerts[othId];
float dist = Vector2.Distance(new Vector2(othWpos.x, othWpos.z), new Vector2(vertWPos.x, vertWPos.z));
if (dist > padding * 0.2f) continue;
if (upper)
{
if (othWpos.y > worldPosVerts[highest].y) highest = othId;
}
else
if (othWpos.y < worldPosVerts[highest].y) highest = othId;
}
}
// Debug backup
//if (highest != i) UnityEngine.Debug.DrawLine(vertWPos, worldPosVerts[highest], Color.green, 1.01f);
float edgeDistance = 0f;
if (highest != i) edgeDistance = Mathf.Abs(vertWPos.y - worldPosVerts[highest].y);
//if (highest != i) edgeDistance = Vector3.Distance(vertWPos, worldPosVerts[highest]);
if (falloff < 0.0001f)
{
if (edgeDistance < padding * 0.001f) return 1f;
}
return Mathf.InverseLerp(falloff, 0f, edgeDistance); // Far from edge = 0 blend, near edge = 1
}
Vector3Int WPosToKey(Vector3 wpos)
{
return V3toV3Int(RoundValueTo(wpos, padding));
}
public bool IsSideVertex(Vector3 vertWPos, float checkOffset)
{
if (!WorldPosContainedNotPrecise(vertWPos + new Vector3(checkOffset, 0, 0))) return true;
if (!WorldPosContainedNotPrecise(vertWPos + new Vector3(-checkOffset, 0, 0))) return true;
if (!WorldPosContainedNotPrecise(vertWPos + new Vector3(0, 0, checkOffset))) return true;
if (!WorldPosContainedNotPrecise(vertWPos + new Vector3(0, 0, -checkOffset))) return true;
if (!WorldPosContainedNotPrecise(vertWPos + new Vector3(checkOffset, 0, checkOffset))) return true;
if (!WorldPosContainedNotPrecise(vertWPos + new Vector3(-checkOffset, 0, checkOffset))) return true;
if (!WorldPosContainedNotPrecise(vertWPos + new Vector3(-checkOffset, 0, -checkOffset))) return true;
if (!WorldPosContainedNotPrecise(vertWPos + new Vector3(checkOffset, 0, -checkOffset))) return true;
return false;
}
bool WorldPosContainedNotPrecise(Vector3 wPos)
{
VertexCluster outClusterCheck;
if (!clusterMap.TryGetValue(WPosToKey(wPos), out outClusterCheck)) return false;
else
if (outClusterCheck.neightbourCoverBounds.Contains(wPos))
{
//float range = padding * padding * 1f;
//for (int v = 0; v < outClusterCheck.neightCoverVertsIn.Count; v++)
//{
// float dist = Vector3.SqrMagnitude(wPos - worldPosVerts[outClusterCheck.neightCoverVertsIn[v]]);
// if ( dist < range) return true;
//}
return true;
}
return false;
}
private Vector3 RoundValueTo(Vector3 toRound, float to)
{
return new Vector3(RoundValueTo(toRound.x, to), RoundValueTo(toRound.y, to), RoundValueTo(toRound.z, to));
}
private float RoundValueTo(float toRound, float to)
{
return Mathf.Round(toRound / to);
}
private Vector3Int V3toV3Int(Vector3 v)
{
return new Vector3Int(Mathf.RoundToInt(v.x), Mathf.RoundToInt(v.y), Mathf.RoundToInt(v.z));
}
}
}