You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
242 lines
9.6 KiB
242 lines
9.6 KiB
using System.Collections.Generic;
|
|
using System.Linq;
|
|
using UnityEditor;
|
|
using UnityEditor.AssetImporters;
|
|
using UnityEngine;
|
|
using UnityEngine.Rendering;
|
|
using static Metamesh.GoldbergBuilder;
|
|
|
|
namespace Metamesh
|
|
{
|
|
[ScriptedImporter(1, "gph")]
|
|
public class GoldbergPolyhedronImporter : ScriptedImporter
|
|
{
|
|
[SerializeField] float radius = 1;
|
|
[SerializeField, Min(2)] uint subdivision = 2;
|
|
[SerializeField, Min(1)] int m = 1;
|
|
[SerializeField, Min(0)] int n = 0;
|
|
|
|
[SerializeField] bool generateLightmapUVs = false;
|
|
[SerializeField] bool readWriteMeshes = false;
|
|
|
|
[MenuItem("Assets/Create/GoldbergPolyhedron")]
|
|
public static void CreateNewAsset() => ProjectWindowUtil.CreateAssetWithContent("New GoldbergPolyhedron.gph", "");
|
|
|
|
public override void OnImportAsset(AssetImportContext context)
|
|
{
|
|
var gameObject = new GameObject();
|
|
var mesh = ImportAsMesh();
|
|
|
|
var meshFilter = gameObject.AddComponent<MeshFilter>();
|
|
meshFilter.sharedMesh = mesh;
|
|
|
|
var pipelineAsset = GraphicsSettings.currentRenderPipeline;
|
|
var baseMaterial = pipelineAsset ? pipelineAsset.defaultMaterial : AssetDatabase.GetBuiltinExtraResource<Material>("Default-Diffuse.mat");
|
|
|
|
var meshRenderer = gameObject.AddComponent<MeshRenderer>();
|
|
meshRenderer.sharedMaterial = baseMaterial;
|
|
|
|
context.AddObjectToAsset("prefab", gameObject);
|
|
if (mesh != null) context.AddObjectToAsset("mesh", mesh);
|
|
|
|
context.SetMainObject(gameObject);
|
|
}
|
|
|
|
private Mesh ImportAsMesh()
|
|
{
|
|
var mesh = new Mesh();
|
|
mesh.name = "Mesh";
|
|
/*
|
|
var builder = new IcosphereBuilder();
|
|
for (var i = 1; i < subdivision; ++i)
|
|
{
|
|
builder = new IcosphereBuilder(builder);
|
|
}
|
|
|
|
var vertices = builder.Vertices.Select(v => (Vector3)(v * radius)).ToList();
|
|
var normals = builder.Vertices.Select(v => (Vector3)v).ToList();
|
|
var indices = builder.Indices.ToList();
|
|
|
|
ApplyGoldbergTransformation(vertices, normals, indices);
|
|
*/
|
|
var builder = new GoldbergBuilder();
|
|
var options = new GoldbergCreationOption()
|
|
{
|
|
m = m,
|
|
n = n,
|
|
};
|
|
var data = builder.CreateGoldberg(options);
|
|
var vertices = data.vertices;
|
|
var normals = data.normals;
|
|
var indices = data.indices;
|
|
|
|
if (vertices.Count > 65535) mesh.indexFormat = IndexFormat.UInt32;
|
|
mesh.SetVertices(vertices);
|
|
mesh.SetNormals(normals);
|
|
mesh.SetIndices(indices, MeshTopology.Triangles, 0);
|
|
|
|
mesh.RecalculateBounds();
|
|
mesh.RecalculateNormals();
|
|
if (generateLightmapUVs) Unwrapping.GenerateSecondaryUVSet(mesh);
|
|
mesh.UploadMeshData(!readWriteMeshes);
|
|
|
|
return mesh;
|
|
}
|
|
|
|
private void ApplyGoldbergTransformation(List<Vector3> vertices, List<Vector3> normals, List<int> indices)
|
|
{
|
|
var newVertices = new List<Vector3>();
|
|
var newNormals = new List<Vector3>();
|
|
var newIndices = new List<int>();
|
|
|
|
var centers = new Dictionary<int, List<int>>();
|
|
var edges = new HashSet<int>();
|
|
|
|
{
|
|
var neighbors = FindNeighbors(0, indices);
|
|
centers.Add(0, neighbors);
|
|
foreach (var i in neighbors)
|
|
{
|
|
edges.Add(i);
|
|
}
|
|
}
|
|
|
|
int inc = 0;
|
|
while (edges.Count + centers.Count < vertices.Count)
|
|
{
|
|
inc++;
|
|
if (inc > 3) break;
|
|
|
|
var ccs = FindCenters(edges, indices).Where(c => !centers.ContainsKey(c)).Distinct().ToArray();
|
|
Debug.Log($"while cc in nb: {ccs.Where(c => edges.Contains(c)).Count()}");
|
|
var nbs = ccs.SelectMany(c =>
|
|
{
|
|
var neighbors = FindNeighbors(c, indices);
|
|
centers.Add(c, neighbors);
|
|
return neighbors;
|
|
}).Distinct().Where(i => !edges.Contains(i) && !centers.ContainsKey(i)).ToArray();
|
|
Debug.Log($"while nb in cc: {nbs.Where(c => centers.ContainsKey(c)).Count()}");
|
|
foreach (var i in nbs)
|
|
{
|
|
edges.Add(i);
|
|
}
|
|
Debug.Log($"centers: {ccs.Count()}/{centers.Count}, edges: {nbs.Count()}/{edges.Count}, vertices: {vertices.Count}");
|
|
}
|
|
|
|
Debug.Log($"cc in nb: {centers.Keys.Where(c => edges.Contains(c)).Count()}");
|
|
Debug.Log($"nb in cc: {edges.Where(c => centers.Keys.Contains(c)).Count()}");
|
|
Debug.Log($"missing: {Enumerable.Range(0, vertices.Count).Where(i => !centers.ContainsKey(i)).Where(i => !edges.Contains(i)).Count()}");
|
|
|
|
var pentagon = 0;
|
|
var hexagon = 0;
|
|
var start = 0;
|
|
foreach (var i in centers.Keys)
|
|
{
|
|
var neighbors = centers[i].Distinct().ToArray();
|
|
var neighborCount = neighbors.Length;
|
|
if (neighborCount != 5 && neighborCount != 6)
|
|
{
|
|
Debug.Log($"Unexpected number of neighbors: {neighborCount}");
|
|
continue;
|
|
}
|
|
if (neighborCount == 5) pentagon++;
|
|
if (neighborCount == 6) hexagon++;
|
|
/*
|
|
var p1 = vertices[neighbors[0]];
|
|
var p2 = vertices[neighbors[2]];
|
|
var p3 = vertices[neighbors[4]];
|
|
|
|
var edge1 = p2 - p1;
|
|
var edge2 = p3 - p1;
|
|
var normal = Vector3.Cross(edge1, edge2).normalized;
|
|
|
|
var projection = ProjectPointOntoPlane(p1, normal);
|
|
*/
|
|
var x = neighbors.Average(i => vertices[i].x);
|
|
var y = neighbors.Average(i => vertices[i].y);
|
|
var z = neighbors.Average(i => vertices[i].z);
|
|
vertices[i] = new Vector3(x, y, z);
|
|
var normal = vertices[i];
|
|
|
|
var q = Quaternion.FromToRotation(vertices[i], Vector3.up);
|
|
//var mtx = Matrix4x4.TRS(-vertices[i], q, Vector3.one);
|
|
var mtx = Matrix4x4.TRS(Vector3.zero, q, Vector3.one) * Matrix4x4.TRS(-vertices[i], Quaternion.identity, Vector3.one);
|
|
var sortedNeighbors = neighbors.OrderBy(n =>
|
|
{
|
|
var direction = mtx.MultiplyPoint(vertices[n]).normalized;
|
|
var crossProduct = Vector3.Cross(direction, Vector3.up);
|
|
return Mathf.Atan2(crossProduct.z, crossProduct.x);
|
|
}).ToArray();
|
|
|
|
newVertices.Add(vertices[i]);
|
|
newVertices.AddRange(sortedNeighbors.Select(n => vertices[n]));
|
|
newNormals.AddRange(Enumerable.Repeat(normal, neighborCount + 1));
|
|
|
|
for (int k = 0; k < neighborCount; ++k)
|
|
{
|
|
newIndices.Add(start);
|
|
newIndices.Add(start + (k + 1) % neighborCount);
|
|
newIndices.Add(start + (k + 0) % neighborCount);
|
|
}
|
|
start += neighborCount + 1;
|
|
}
|
|
|
|
vertices.Clear();
|
|
vertices.AddRange(newVertices);
|
|
normals.Clear();
|
|
normals.AddRange(newNormals);
|
|
indices.Clear();
|
|
indices.AddRange(newIndices);
|
|
|
|
Debug.Log($"Pentagon: {pentagon}, Hexagon: {hexagon}");
|
|
}
|
|
|
|
private Vector3 ProjectPointOntoPlane(Vector3 planePoint, Vector3 planeNormal)
|
|
{
|
|
float distance = Vector3.Dot(planeNormal, Vector3.zero - planePoint);
|
|
Vector3 projection = Vector3.zero - planeNormal * distance;
|
|
return projection;
|
|
}
|
|
|
|
private List<int> FindNeighbors(int index, List<int> indices)
|
|
{
|
|
var results = new List<int>();
|
|
for (int i = 0; i < indices.Count; i += 3)
|
|
{
|
|
var i0 = indices[i + 0];
|
|
var i1 = indices[i + 1];
|
|
var i2 = indices[i + 2];
|
|
if (i0 == index) results.AddRange(new[] { i1, i2 });
|
|
if (i1 == index) results.AddRange(new[] { i0, i2 });
|
|
if (i2 == index) results.AddRange(new[] { i0, i1 });
|
|
}
|
|
results = results.Distinct().ToList();
|
|
if (results.Count != 5 && results.Count != 6)
|
|
Debug.Log($"Center#{index}'s neighbors count is {results.Count}");
|
|
return results;
|
|
}
|
|
|
|
private List<int> FindCenters(HashSet<int> edges, List<int> indices)
|
|
{
|
|
var results = new List<int>();
|
|
for (int i = 0; i < indices.Count; i += 3)
|
|
{
|
|
var i0 = indices[i + 0];
|
|
var i1 = indices[i + 1];
|
|
var i2 = indices[i + 2];
|
|
var notInEdges = new List<int> { i0, i1, i2 }
|
|
.Where(v => !edges.Contains(v))
|
|
.ToList();
|
|
if (notInEdges.Count == 1)
|
|
{
|
|
var candidate = notInEdges[0];
|
|
var neighbors = FindNeighbors(candidate, indices);
|
|
if (neighbors.Count != 5 && neighbors.Count != 6)
|
|
Debug.Log($"candidate#{candidate}'s neighbors count is {neighbors.Count}");
|
|
results.Add(candidate);
|
|
}
|
|
}
|
|
return results.Distinct().ToList();
|
|
}
|
|
}
|
|
} |