Subdivision.cs
56 removals
636 lines
125 additions
620 lines
//#define SUBDIVIDEVECTOR4 // used to subdivide meshes in 4D
//#define SUBDIVIDEVECTOR4 // used to subdivide meshes in 4D
using System.Collections.Generic;
using System.Collections.Generic;
using System.Linq;
using UnityEngine;
using UnityEngine;
using UnityEngine.Rendering;
//using System.Linq; -- don't use Linq for performance
//using System.Linq; -- don't use Linq for performance
namespace Torec {
namespace Torec {
// Vector used for position, normal and tangent of a Vertex
// Vector used for position, normal and tangent of a Vertex
#if !SUBDIVIDEVECTOR4
#if !SUBDIVIDEVECTOR4
using Vector = Vector3;
using Vector = Vector3;
#else
#else
using Vector = Vector4; // to subdivide 4D meshes
using Vector = Vector4; // to subdivide 4D meshes
#endif
#endif
/// <summary>
/// <summary>
/// Temporal Vertex object extracted from (inserted to) Mesh data arrays (positions, normals, uvs,..)
/// Temporal Vertex object extracted from (inserted to) Mesh data arrays (positions, normals, uvs,..)
/// </summary>
/// </summary>
[System.Diagnostics.DebuggerDisplay("Vertex: pos {posIndex}. {position}; normal {normal}")]
[System.Diagnostics.DebuggerDisplay("Vertex: pos {posIndex}. {position}; normal {normal}")]
public struct Vertex {
public struct Vertex {
public int posIndex;
public int posIndex;
public Vector position;
public Vector position;
public Vector normal;
public Vector normal;
public Vector tangent;
public Vector tangent;
public Vector2 uv0;
public Vector2 uv0;
public Vector2 uv1;
public Vector2 uv1;
public Vector2 uv2;
public Vector2 uv2;
public Vector2 uv3;
public Vector2 uv3;
public BoneWeight boneWeight; // 頂点属性にBoneWeightを追加
}
}
/// <summary>
/// <summary>
/// Content of Mesh data arrays
/// Content of Mesh data arrays
/// </summary>
/// </summary>
public enum VertexContent {
public enum VertexContent {
none = 0x00,
none = 0x00,
pos = 0x01,
pos = 0x01,
normal = 0x02,
normal = 0x02,
tangent = 0x04,
tangent = 0x04,
uv0 = 0x08,
uv0 = 0x08,
uv1 = 0x10,
uv1 = 0x10,
uv2 = 0x20,
uv2 = 0x20,
uv3 = 0x40,
uv3 = 0x40,
full = 0x7F,
boneWeight = 0x80, // BoneWeight用フラグを追加
full = 0xFF, // フラグを増やしたのでfullも増やす
}
}
public static class VertexContentMethods {
public static class VertexContentMethods {
public static bool HasPosition(this VertexContent c) { return (c & VertexContent.pos) != 0; }
public static bool HasPosition (this VertexContent c) { return (c & VertexContent.pos) != 0; }
public static bool HasNormal (this VertexContent c) { return (c & VertexContent.normal) != 0; }
public static bool HasNormal (this VertexContent c) { return (c & VertexContent.normal) != 0; }
public static bool HasTangent (this VertexContent c) { return (c & VertexContent.tangent) != 0; }
public static bool HasTangent (this VertexContent c) { return (c & VertexContent.tangent) != 0; }
public static bool HasUV0 (this VertexContent c) { return (c & VertexContent.uv0) != 0; }
public static bool HasUV0 (this VertexContent c) { return (c & VertexContent.uv0) != 0; }
public static bool HasUV1 (this VertexContent c) { return (c & VertexContent.uv1) != 0; }
public static bool HasUV1 (this VertexContent c) { return (c & VertexContent.uv1) != 0; }
public static bool HasUV2 (this VertexContent c) { return (c & VertexContent.uv2) != 0; }
public static bool HasUV2 (this VertexContent c) { return (c & VertexContent.uv2) != 0; }
public static bool HasUV3 (this VertexContent c) { return (c & VertexContent.uv3) != 0; }
public static bool HasUV3 (this VertexContent c) { return (c & VertexContent.uv3) != 0; }
public static bool HasBoneWeight(this VertexContent c) { return (c & VertexContent.boneWeight) != 0; } // BoneWeight用メソッドを追加
}
}
public struct Submesh {
public struct Submesh {
public int[][] faces; // [face index] => [face vertex indices]
public int[][] faces; // [face index] => [face vertex indices]
}
}
public struct Face {
public struct Face {
public int submesh;
public int submesh;
public int index; // face index
public int index; // face index
}
}
public struct Edge {
public struct Edge {
public Face face;
public Face face;
public int index; // edge index in a face (0..3)
public int index; // edge index in a face (0..3)
//
//
public static Edge Invalid = new Edge { index = -1 };
public static Edge Invalid = new Edge { index = -1 };
public bool IsValid() { return index != -1; }
public bool IsValid() { return index != -1; }
}
}
public enum EdgeType { // see GetEdgeType(Edge e)
public enum EdgeType { // see GetEdgeType(Edge e)
boundary = -1, // the edge has no opposite neighbor
boundary = -1, // the edge has no opposite neighbor
back = -2, // back edge - skip it to not count the edge twice
back = -2, // back edge - skip it to not count the edge twice
solid = 0, // front solid edge: both edge vertices belong to both faces
solid = 0, // front solid edge: both edge vertices belong to both faces
seam0 = 1, // front seam edge: faces have different uv on this edge
seam0 = 1, // front seam edge: faces have different uv on this edge
seam1 = 2,
seam1 = 2,
}
}
/// <summary>
/// <summary>
/// Extended mesh. Allows to weld vertex positions keeping different uvs. So 'positions.Count' may be less than 'vertexCount'
/// Extended mesh. Allows to weld vertex positions keeping different uvs. So 'positions.Count' may be less than 'vertexCount'
/// </summary>
/// </summary>
public class MeshX
public class MeshX
{
{
// by position index
// by position index
public IList<Vector> positions = null;
public IList<Vector> positions = null;
// by vertex index
// by vertex index
public IList<int> posIndices = null;
public IList<int> posIndices = null;
public IList<Vector> normals = null; // may be per position: if 'normalPerPosition'
public IList<Vector> normals = null; // may be per position: if 'normalPerPosition'
public IList<Vector> tangents = null;
public IList<Vector> tangents = null;
public IList<Vector2> uvs0 = null;
public IList<Vector2> uvs0 = null;
public IList<Vector2> uvs1 = null;
public IList<Vector2> uvs1 = null;
public IList<Vector2> uvs2 = null;
public IList<Vector2> uvs2 = null;
public IList<Vector2> uvs3 = null;
public IList<Vector2> uvs3 = null;
public IList<BoneWeight> boneWeights = null; // BoneWeightリストを追加
//
//
// bindposesを追加
public Matrix4x4[] bindposes = null;
public VertexContent content = VertexContent.none;
public VertexContent content = VertexContent.none;
public bool normalPerPosition = false; // normal per position or per vertex
public bool normalPerPosition = false; // normal per position or per vertex
public string name = null;
public string name = null;
public bool buildMode = false;
public bool buildMode = false;
// faces
// faces
public Submesh[] submeshes = null;
public Submesh[] submeshes = null;
public int vertexCount { get { return posIndices == null ? -1 : posIndices.Count; } }
public int vertexCount { get { return posIndices == null ? -1 : posIndices.Count; } }
// helpers
// helpers
public bool helpersInited { get { return positionVertices != null; } }
public bool helpersInited { get { return positionVertices != null; } }
public int[][] positionVertices = null; // [position index] => [vertex indices]
public int[][] positionVertices = null; // [position index] => [vertex indices]
public Edge[][] vertexEdges = null; // [vertex index] => [face edges started from the vertex]
public Edge[][] vertexEdges = null; // [vertex index] => [face edges started from the vertex]
private Edge[][][] neighbors = null; // [submesh index][face index][face edge index] => neighbor edge
private Edge[][][] neighbors = null; // [submesh index][face index][face edge index] => neighbor edge
//
//
public MeshX() {}
public MeshX() {}
public void Trace() {
public void Trace() {
Debug.Log("Positions:");
Debug.Log("Positions:");
for (int i = 0; i < positions.Count; ++i) {
for (int i = 0; i < positions.Count; ++i) {
Debug.LogFormat(" {0,3}: {1}", i, positions[i].ToString("0.0000"));
Debug.LogFormat(" {0,3}: {1}", i, positions[i].ToString("0.0000"));
}
}
Debug.Log("Vertices:");
Debug.Log("Vertices:");
for (int v = 0; v < vertexCount; ++v) {
for (int v = 0; v < vertexCount; ++v) {
Debug.LogFormat(" {0,3}: pos {1}", v, posIndices[v]);
Debug.LogFormat(" {0,3}: pos {1}", v, posIndices[v]);
}
}
Debug.Log("Faces:");
Debug.Log("Faces:");
for (int s = 0; s < submeshes.Length; ++s) {
for (int s = 0; s < submeshes.Length; ++s) {
Debug.Log(" Submesh: " + s);
Debug.Log(" Submesh: " + s);
for (int i = 0; i < submeshes[s].faces.Length; ++i) {
for (int i = 0; i < submeshes[s].faces.Length; ++i) {
string l = string.Format(" {0,3}:", i);
string l = string.Format(" {0,3}:", i);
foreach (int v in submeshes[s].faces[i]) l += " " + v;
foreach (int v in submeshes[s].faces[i]) l += " " + v;
Debug.Log(l);
Debug.Log(l);
}
}
}
}
}
}
#region Build mode
#region Build mode
/// <summary>
/// <summary>
/// In Build mode we store Mesh data in lists (not arrays) allowing to add vertices
/// In Build mode we store Mesh data in lists (not arrays) allowing to add vertices
/// </summary>
/// </summary>
public void StartBuilding() {
public void StartBuilding() {
if (content.HasPosition()) positions = new List<Vector>();
if (content.HasPosition ()) positions = new List<Vector>();
if (content.HasNormal ()) normals = new List<Vector>();
if (content.HasNormal ()) normals = new List<Vector>();
if (content.HasTangent ()) tangents = new List<Vector>();
if (content.HasTangent ()) tangents = new List<Vector>();
if (content.HasUV0 ()) uvs0 = new List<Vector2>();
if (content.HasUV0 ()) uvs0 = new List<Vector2>();
if (content.HasUV1 ()) uvs1 = new List<Vector2>();
if (content.HasUV1 ()) uvs1 = new List<Vector2>();
if (content.HasUV2 ()) uvs2 = new List<Vector2>();
if (content.HasUV2 ()) uvs2 = new List<Vector2>();
if (content.HasUV3 ()) uvs3 = new List<Vector2>();
if (content.HasUV3 ()) uvs3 = new List<Vector2>();
if (content.HasBoneWeight()) boneWeights = new List<BoneWeight>(); // BoneWeightを追加
posIndices = new List<int>();
posIndices = new List<int>();
buildMode = true;
buildMode = true;
}
}
public void FinishBuilding() {
public void FinishBuilding() {
if (content.HasPosition()) positions = ToArray(positions);
if (content.HasPosition ()) positions = ToArray(positions);
if (content.HasNormal ()) normals = ToArray(normals);
if (content.HasNormal ()) normals = ToArray(normals);
if (content.HasTangent ()) tangents = ToArray(tangents);
if (content.HasTangent ()) tangents = ToArray(tangents);
if (content.HasUV0 ()) uvs0 = ToArray(uvs0);
if (content.HasUV0 ()) uvs0 = ToArray(uvs0);
if (content.HasUV1 ()) uvs1 = ToArray(uvs1);
if (content.HasUV1 ()) uvs1 = ToArray(uvs1);
if (content.HasUV2 ()) uvs2 = ToArray(uvs2);
if (content.HasUV2 ()) uvs2 = ToArray(uvs2);
if (content.HasUV3 ()) uvs3 = ToArray(uvs3);
if (content.HasUV3 ()) uvs3 = ToArray(uvs3);
if (content.HasBoneWeight()) boneWeights = ToArray(boneWeights); // BoneWeightを追加
posIndices = ToArray(posIndices);
posIndices = ToArray(posIndices);
buildMode = false;
buildMode = false;
}
}
private static T[] ToArray<T>(IList<T> source) {
private static T[] ToArray<T>(IList<T> source) {
var array = new T[source.Count];
var array = new T[source.Count];
source.CopyTo(array, 0);
source.CopyTo(array, 0);
return array;
return array;
}
}
public int AddPosition(Vertex v) {
public int AddPosition(Vertex v) {
Debug.Assert(buildMode, "AddPosition in buildMode only");
Debug.Assert(buildMode, "AddPosition in buildMode only");
positions.Add(v.position);
positions.Add(v.position);
if (content.HasNormal() && normalPerPosition) normals.Add(v.normal);
if (content.HasNormal() && normalPerPosition) normals.Add(v.normal);
return positions.Count - 1;
return positions.Count - 1;
}
}
public int AddVertex(Vertex v, bool addPosition = false) {
public int AddVertex(Vertex v, bool addPosition = false) {
Debug.Assert(buildMode, "AddVertex in buildMode only");
Debug.Assert(buildMode, "AddVertex in buildMode only");
int pi = addPosition ? AddPosition(v) : v.posIndex;
int pi = addPosition ? AddPosition(v) : v.posIndex;
posIndices.Add(pi);
posIndices.Add(pi);
if (content.HasNormal() && !normalPerPosition) normals.Add(v.normal);
if (content.HasNormal() && !normalPerPosition) normals.Add(v.normal);
if (content.HasTangent()) tangents.Add(v.tangent);
if (content.HasTangent()) tangents.Add(v.tangent);
if (content.HasUV0()) uvs0.Add(v.uv0);
if (content.HasUV0()) uvs0.Add(v.uv0);
if (content.HasUV1()) uvs1.Add(v.uv1);
if (content.HasUV1()) uvs1.Add(v.uv1);
if (content.HasUV2()) uvs2.Add(v.uv2);
if (content.HasUV2()) uvs2.Add(v.uv2);
if (content.HasUV3()) uvs3.Add(v.uv3);
if (content.HasUV3()) uvs3.Add(v.uv3);
if (content.HasBoneWeight()) boneWeights.Add(v.boneWeight); // BoneWeightを追加
return posIndices.Count - 1;
return posIndices.Count - 1;
}
}
#endregion Build mode
#endregion Build mode
// positions & vertices
// positions & vertices
public void SetPosition(int pi, Vertex v) {
public void SetPosition(int pi, Vertex v) {
positions[pi] = v.position;
positions[pi] = v.position;
if (content.HasNormal() && normalPerPosition) normals[pi] = v.normal;
if (content.HasNormal() && normalPerPosition) normals[pi] = v.normal;
}
}
public void SetVertex(int vi, Vertex v, bool setPosition = false) {
public void SetVertex(int vi, Vertex v, bool setPosition = false) {
if (setPosition) SetPosition(v.posIndex, v);
if (setPosition) SetPosition(v.posIndex, v);
posIndices[vi] = v.posIndex;
posIndices[vi] = v.posIndex;
if (content.HasNormal() && !normalPerPosition) normals[vi] = v.normal;
if (content.HasNormal() && !normalPerPosition) normals[vi] = v.normal;
if (content.HasTangent()) tangents[vi] = v.tangent;
if (content.HasTangent()) tangents[vi] = v.tangent;
if (content.HasUV0()) uvs0[vi] = v.uv0;
if (content.HasUV0()) uvs0[vi] = v.uv0;
if (content.HasUV1()) uvs1[vi] = v.uv1;
if (content.HasUV1()) uvs1[vi] = v.uv1;
if (content.HasUV2()) uvs2[vi] = v.uv2;
if (content.HasUV2()) uvs2[vi] = v.uv2;
if (content.HasUV3()) uvs3[vi] = v.uv3;
if (content.HasUV3()) uvs3[vi] = v.uv3;
if (content.HasBoneWeight()) boneWeights[vi] = v.boneWeight; // BoneWeightを追加
}
}
public Vertex GetPosition(int pi) { // position & normal (if per position) only
public Vertex GetPosition(int pi) { // position & normal (if per position) only
Vertex v = new Vertex();
Vertex v = new Vertex();
v.position = positions[pi];
v.position = positions[pi];
if (content.HasNormal() && normalPerPosition) v.normal = normals[pi];
if (content.HasNormal() && normalPerPosition) v.normal = normals[pi];
return v;
return v;
}
}
public Vertex GetVertex(int vi, VertexContent mask = VertexContent.full) {
public Vertex GetVertex(int vi, VertexContent mask = VertexContent.full) {
VertexContent c = content & mask;
VertexContent c = content & mask;
int pi = posIndices[vi];
int pi = posIndices[vi];
Vertex v = new Vertex();
Vertex v = new Vertex();
v.posIndex = pi; // always set
v.posIndex = pi; // always set
if (c.HasPosition()) v.position = positions[pi];
if (c.HasPosition()) v.position = positions[pi];
if (c.HasNormal()) v.normal = normals[normalPerPosition ? pi : vi];
if (c.HasNormal()) v.normal = normals[normalPerPosition ? pi : vi];
if (c.HasTangent()) v.tangent = tangents[vi];
if (c.HasTangent()) v.tangent = tangents[vi];
if (c.HasUV0()) v.uv0 = uvs0[vi];
if (c.HasUV0()) v.uv0 = uvs0[vi];
if (c.HasUV1()) v.uv1 = uvs1[vi];
if (c.HasUV1()) v.uv1 = uvs1[vi];
if (c.HasUV2()) v.uv2 = uvs2[vi];
if (c.HasUV2()) v.uv2 = uvs2[vi];
if (c.HasUV3()) v.uv3 = uvs3[vi];
if (c.HasUV3()) v.uv3 = uvs3[vi];
if (c.HasBoneWeight()) v.boneWeight = boneWeights[vi]; // BoneWeightを追加
return v;
return v;
}
}
public Vertex[] GetVertices(int[] indices) {
public Vertex[] GetVertices(int[] indices) {
Vertex[] vs = new Vertex[indices.Length];
Vertex[] vs = new Vertex[indices.Length];
for (int i = 0; i < indices.Length; ++i) {
for (int i = 0; i < indices.Length; ++i) {
vs[i] = GetVertex(indices[i]);
vs[i] = GetVertex(indices[i]);
}
}
return vs;
return vs;
}
}
public void MakeNormalPerPosition() {
public void MakeNormalPerPosition() {
if (normalPerPosition) return;
if (normalPerPosition) return;
if (!content.HasNormal()) return;
if (!content.HasNormal()) return;
Vector[] newNormals = new Vector[positions.Count];
Vector[] newNormals = new Vector[positions.Count];
for (int i = 0; i < positions.Count; ++i) {
for (int i = 0; i < positions.Count; ++i) {
int[] vs = positionVertices[i];
int[] vs = positionVertices[i];
Vector[] ns = new Vector[vs.Length];
Vector[] ns = new Vector[vs.Length];
for (int j = 0; j < vs.Length; ++j) {
for (int j = 0; j < vs.Length; ++j) {
ns[j] = normals[vs[j]];
ns[j] = normals[vs[j]];
}
}
newNormals[i] = AverageVectors(ns).normalized;
newNormals[i] = AverageVectors(ns).normalized;
}
}
normals = newNormals;
normals = newNormals;
normalPerPosition = true;
normalPerPosition = true;
}
}
// neighbnors
// neighbnors
private void SetNeighbor(Edge e, Edge n) {
private void SetNeighbor(Edge e, Edge n) {
neighbors[e.face.submesh][e.face.index][e.index] = n;
neighbors[e.face.submesh][e.face.index][e.index] = n;
}
}
public Edge GetNeighbor(Edge e) {
public Edge GetNeighbor(Edge e) {
return neighbors[e.face.submesh][e.face.index][e.index];
return neighbors[e.face.submesh][e.face.index][e.index];
}
}
public void InitHelpers() {
public void InitHelpers() {
// vertex edges
// vertex edges
vertexEdges = new Edge[vertexCount][];
vertexEdges = new Edge[vertexCount][];
for (int v = 0; v < vertexCount; ++v) {
for (int v = 0; v < vertexCount; ++v) {
var es = new List<Edge>();
var es = new List<Edge>();
foreach (Face face in IterAllFaces()) {
foreach (Face face in IterAllFaces()) {
int e = GetIndexInFace(GetFaceVertexIndices(face), v);
int e = GetIndexInFace(GetFaceVertexIndices(face), v);
if (e != -1) {
if (e != -1) {
es.Add(new Edge { face = face, index = e });
es.Add(new Edge { face = face, index = e });
}
}
}
}
vertexEdges[v] = es.ToArray();
vertexEdges[v] = es.ToArray();
}
}
// position vertices
// position vertices
positionVertices = new int[positions.Count][];
positionVertices = new int[positions.Count][];
for (int p = 0; p < positions.Count; ++p) {
for (int p = 0; p < positions.Count; ++p) {
var vs = new List<int>();
var vs = new List<int>();
for (int v = 0; v < vertexCount; ++v) {
for (int v = 0; v < vertexCount; ++v) {
if (posIndices[v] == p) {
if (posIndices[v] == p) {
vs.Add(v);
vs.Add(v);
}
}
}
}
positionVertices[p] = vs.ToArray();
positionVertices[p] = vs.ToArray();
}
}
// neighbors
// neighbors
neighbors = new Edge[submeshes.Length][][];
neighbors = new Edge[submeshes.Length][][];
for (int s = 0; s < submeshes.Length; ++s) {
for (int s = 0; s < submeshes.Length; ++s) {
int[][] faces = submeshes[s].faces;
int[][] faces = submeshes[s].faces;
neighbors[s] = new Edge[faces.Length][];
neighbors[s] = new Edge[faces.Length][];
for (int f = 0; f < faces.Length; ++f) {
for (int f = 0; f < faces.Length; ++f) {
int[] es = faces[f];
int[] es = faces[f];
neighbors[s][f] = new Edge[es.Length];
neighbors[s][f] = new Edge[es.Length];
for (int e = 0; e < es.Length; ++e) {
for (int e = 0; e < es.Length; ++e) {
neighbors[s][f][e] = Edge.Invalid;
neighbors[s][f][e] = Edge.Invalid;
}
}
}
}
}
}
foreach (Edge e in IterAllEdges()) {
foreach (Edge e in IterAllEdges()) {
if (GetNeighbor(e).IsValid()) continue; // already found
if (GetNeighbor(e).IsValid()) continue; // already found
Edge n = FindNeighbor(e);
Edge n = FindNeighbor(e);
if (!n.IsValid()) continue; // e is boundary edge and has no neighbor
if (!n.IsValid()) continue; // e is boundary edge and has no neighbor
SetNeighbor(e, n);
SetNeighbor(e, n);
SetNeighbor(n, e);
SetNeighbor(n, e);
}
}
}
}
#region triangles <-> (quad or triangle) faces
#region triangles <-> (quad or triangle) faces
// Convert: triangles -> faces
// Convert: triangles -> faces
private struct QuadVariant { // how two triangles (6 indices) compose a quad (4 indices)
private struct QuadVariant { // how two triangles (6 indices) compose a quad (4 indices)
public int[] v0; // [2] 1st matching vertex indices (value 0..5)
public int[] v0; // [2] 1st matching vertex indices (value 0..5)
public int[] v1; // [2] 2nd matching vertex indices (value 0..5)
public int[] v1; // [2] 2nd matching vertex indices (value 0..5)
public int[] vs; // [4] quad vertex indices (value 0..5)
public int[] vs; // [4] quad vertex indices (value 0..5)
}
}
private static QuadVariant[] GetQuadVariants() {
private static QuadVariant[] GetQuadVariants() {
var qs = new QuadVariant[9];
var qs = new QuadVariant[9];
int[] tv = new[] { 0, 1, 2 };
int[] tv = new[] { 0, 1, 2 };
foreach (int i in tv) {
foreach (int i in tv) {
foreach (int j in tv) {
foreach (int j in tv) {
qs[3*i+j] = new QuadVariant {
qs[3*i+j] = new QuadVariant {
v0 = new[] { (i+1)%3, 3+(j+2)%3 },
v0 = new[] { (i+1)%3, 3+(j+2)%3 },
v1 = new[] { (i+2)%3, 3+(j+1)%3 },
v1 = new[] { (i+2)%3, 3+(j+1)%3 },
vs = new[] { (i+2)%3, (i+0)%3, (i+1)%3, 3+(j+0)%3 },
vs = new[] { (i+2)%3, (i+0)%3, (i+1)%3, 3+(j+0)%3 },
};
};
}
}
}
}
return qs;
return qs;
}
}
private static QuadVariant[] quadVariants = null;
private static QuadVariant[] quadVariants = null;
private static int[][] MakeQuadFaces(int[] ts) {
private static int[][] MakeQuadFaces(int[] ts) {
// prepare quad variants once
// prepare quad variants once
if (quadVariants == null) {
if (quadVariants == null) {
quadVariants = GetQuadVariants();
quadVariants = GetQuadVariants();
}
}
// try to get quad faces
// try to get quad faces
var qs = new int[ts.Length / 6][];
var qs = new int[ts.Length / 6][];
for (int i = 0; i < ts.Length; i += 6) {
for (int i = 0; i < ts.Length; i += 6) {
bool quadFound = false;
bool quadFound = false;
foreach (QuadVariant q in quadVariants) {
foreach (QuadVariant q in quadVariants) {
if (ts[i + q.v0[0]] == ts[i + q.v0[1]] &&
if (ts[i + q.v0[0]] == ts[i + q.v0[1]] &&
ts[i + q.v1[0]] == ts[i + q.v1[1]])
ts[i + q.v1[0]] == ts[i + q.v1[1]])
{
{
qs[i / 6] = new[] {
qs[i / 6] = new[] {
ts[i + q.vs[0]],
ts[i + q.vs[0]],
ts[i + q.vs[1]],
ts[i + q.vs[1]],
ts[i + q.vs[2]],
ts[i + q.vs[2]],
ts[i + q.vs[3]],
ts[i + q.vs[3]],
};
};
quadFound = true;
quadFound = true;
break;
break;
}
}
}
}
if (!quadFound) {
if (!quadFound) {
//if (i != 0) {
//if (i != 0) {
// Debug.LogFormat("Non-quad found at {0}: {1} {2} {3} {4} {5} {6}",
// Debug.LogFormat("Non-quad found at {0}: {1} {2} {3} {4} {5} {6}",
// i / 6, ts[i], ts[i+1], ts[i+2], ts[i+3], ts[i+4], ts[i+5]);
// i / 6, ts[i], ts[i+1], ts[i+2], ts[i+3], ts[i+4], ts[i+5]);
//}
//}
return null;
return null;
}
}
}
}
return qs;
return qs;
}
}
private static int[][] MakeTriangleFaces(int[] ts) {
private static int[][] MakeTriangleFaces(int[] ts) {
var fs = new int[ts.Length / 3][];
var fs = new int[ts.Length / 3][];
for (int i = 0; i < ts.Length; i += 3) {
for (int i = 0; i < ts.Length; i += 3) {
fs[i / 3] = new[] {
fs[i / 3] = new[] {
ts[i + 0],
ts[i + 0],
ts[i + 1],
ts[i + 1],
ts[i + 2],
ts[i + 2],
};
};
}
}
return fs;
return fs;
}
}
private static int[][] TrianglesToFaces(int[] triangles) {
private static int[][] TrianglesToFaces(int[] triangles) {
//!!! here might be some mode to mix quad (when possible) and triangle faces
//!!! here might be some mode to mix quad (when possible) and triangle faces
return
return
MakeQuadFaces(triangles) ??
MakeQuadFaces(triangles) ??
MakeTriangleFaces(triangles);
MakeTriangleFaces(triangles);
}
}
// Convert: faces -> triangles
// Convert: faces -> triangles
public static int[] FacesToTriangles(int[][] faces) {
public static int[] FacesToTriangles(int[][] faces) {
var ts = new List<int>();
var ts = new List<int>();
foreach (int[] f in faces) {
foreach (int[] f in faces) {
for (int i = 2; i < f.Length; ++i) {
for (int i = 2; i < f.Length; ++i) {
ts.AddRange(new[] { f[0], f[i-1], f[i] }); // e.g. {0,1,2}, {0,2,3} for quads
ts.AddRange(new[] { f[0], f[i-1], f[i] }); // e.g. {0,1,2}, {0,2,3} for quads
}
}
}
}
return ts.ToArray();
return ts.ToArray();
}
}
#endregion triangles <-> faces
#endregion triangles <-> faces
#if !SUBDIVIDEVECTOR4
#if !SUBDIVIDEVECTOR4
#region Mesh <-> MeshX
#region Mesh <-> MeshX
private static VertexContent GetMeshVertexContent(Mesh mesh) {
private static VertexContent GetMeshVertexContent(Mesh mesh) {
var c = VertexContent.pos;
var c = VertexContent.pos;
if (mesh.normals != null && mesh.normals .Length == mesh.vertexCount) c |= VertexContent.normal;
if (mesh.normals != null && mesh.normals .Length == mesh.vertexCount) c |= VertexContent.normal;
if (mesh.tangents != null && mesh.tangents.Length == mesh.vertexCount) c |= VertexContent.tangent;
if (mesh.tangents != null && mesh.tangents .Length == mesh.vertexCount) c |= VertexContent.tangent;
if (mesh.uv != null && mesh.uv .Length == mesh.vertexCount) c |= VertexContent.uv0;
if (mesh.uv != null && mesh.uv .Length == mesh.vertexCount) c |= VertexContent.uv0;
if (mesh.uv2 != null && mesh.uv2 .Length == mesh.vertexCount) c |= VertexContent.uv1;
if (mesh.uv2 != null && mesh.uv2 .Length == mesh.vertexCount) c |= VertexContent.uv1;
if (mesh.uv3 != null && mesh.uv3 .Length == mesh.vertexCount) c |= VertexContent.uv2;
if (mesh.uv3 != null && mesh.uv3 .Length == mesh.vertexCount) c |= VertexContent.uv2;
if (mesh.uv4 != null && mesh.uv4 .Length == mesh.vertexCount) c |= VertexContent.uv3;
if (mesh.uv4 != null && mesh.uv4 .Length == mesh.vertexCount) c |= VertexContent.uv3;
if (mesh.boneWeights != null && mesh.boneWeights.Length == mesh.vertexCount) c |= VertexContent.boneWeight; // BoneWeightを追加
return c;
return c;
}
}
public MeshX(Mesh mesh) {
public MeshX(Mesh mesh) {
// set vertex content
// set vertex content
content = GetMeshVertexContent(mesh);
content = GetMeshVertexContent(mesh);
// init positions
// init positions
var poses = new List<Vector>();
var poses = new List<Vector>();
posIndices = new int[mesh.vertexCount];
posIndices = new int[mesh.vertexCount];
for (int i = 0; i < mesh.vertexCount; ++i) {
for (int i = 0; i < mesh.vertexCount; ++i) {
Vector pos = mesh.vertices[i];
Vector pos = mesh.vertices[i];
int posIndex = poses.IndexOf(pos); //!!! use threshold?
int posIndex = poses.IndexOf(pos); //!!! use threshold?
if (posIndex == -1) {
if (posIndex == -1) {
poses.Add(pos);
poses.Add(pos);
posIndex = poses.Count - 1;
posIndex = poses.Count - 1;
}
}
posIndices[i] = posIndex;
posIndices[i] = posIndex;
}
}
positions = poses.ToArray();
positions = poses.ToArray();
// init vertex content
// init vertex content
if (content.HasNormal()) {
if (content.HasNormal()) {
normals = mesh.normals;
normals = mesh.normals;
normalPerPosition = false;
normalPerPosition = false;
}
}
if (content.HasTangent()) {
if (content.HasTangent()) {
tangents = new Vector[mesh.vertexCount];
tangents = new Vector[mesh.vertexCount];
for (int i = 0; i < mesh.vertexCount; ++i) tangents[i] = (Vector)mesh.tangents[i];
for (int i = 0; i < mesh.vertexCount; ++i) tangents[i] = (Vector)mesh.tangents[i];
}
}
if (content.HasUV0()) uvs0 = mesh.uv;
if (content.HasUV0()) uvs0 = mesh.uv;
if (content.HasUV1()) uvs1 = mesh.uv2;
if (content.HasUV1()) uvs1 = mesh.uv2;
if (content.HasUV2()) uvs2 = mesh.uv3;
if (content.HasUV2()) uvs2 = mesh.uv3;
if (content.HasUV3()) uvs3 = mesh.uv4;
if (content.HasUV3()) uvs3 = mesh.uv4;
// BoneWeight、bindposesを追加
if (content.HasBoneWeight()) {
boneWeights = mesh.boneWeights;
bindposes = mesh.bindposes;
}
// set faces
// set faces
int submeshCount = mesh.subMeshCount;
int submeshCount = mesh.subMeshCount;
submeshes = new Submesh[submeshCount];
submeshes = new Submesh[submeshCount];
for (int s = 0; s < submeshCount; ++s) {
for (int s = 0; s < submeshCount; ++s) {
int[][] faces = MeshX.TrianglesToFaces(mesh.GetTriangles(s));
int[][] faces = MeshX.TrianglesToFaces(mesh.GetTriangles(s));
submeshes[s] = new Submesh { faces = faces };
submeshes[s] = new Submesh { faces = faces };
}
}
// copy name
// copy name
this.name = mesh.name;
this.name = mesh.name;
}
}
public Mesh ConvertToMesh() {
public Mesh ConvertToMesh() {
Mesh m = new Mesh { name = this.name };
Mesh m = new Mesh { name = this.name };
// positions
// positions
var vs = new List<Vector3>();
var vs = new List<Vector3>();
foreach (int i in posIndices) {
foreach (int i in posIndices) {
vs.Add(positions[i]);
vs.Add(positions[i]);
}
}
// 頂点数が多い場合は、インデックスフォーマットを32ビットに拡張する
if (vs.Count > 65535) m.indexFormat = IndexFormat.UInt32;
m.SetVertices(vs);
m.SetVertices(vs);
// normals
// normals
if (content.HasNormal()) {
if (content.HasNormal()) {
List<Vector3> ns;
List<Vector3> ns;
if (normalPerPosition) {
if (normalPerPosition) {
ns = new List<Vector3>();
ns = new List<Vector3>();
foreach (int i in posIndices) {
foreach (int i in posIndices) {
ns.Add(normals[i]);
ns.Add(normals[i]);
}
}
} else {
} else {
ns = new List<Vector3>(normals);
ns = new List<Vector3>(normals);
}
}
m.SetNormals(ns);
m.SetNormals(ns);
}
}
// tangents
// tangents
if (content.HasTangent()) {
if (content.HasTangent()) {
var ts = new List<Vector4>();
var ts = new List<Vector4>();
foreach (Vector3 t in tangents) {
foreach (Vector3 t in tangents) {
ts.Add(new Vector4(t.x, t.y, t.z, 1f));
ts.Add(new Vector4(t.x, t.y, t.z, 1f));
}
}
m.SetTangents(ts);
m.SetTangents(ts);
}
}
// uvs
// uvs
if (content.HasUV0()) m.SetUVs(0, new List<Vector2>(uvs0));
if (content.HasUV0()) m.SetUVs(0, new List<Vector2>(uvs0));
if (content.HasUV1()) m.SetUVs(1, new List<Vector2>(uvs1));
if (content.HasUV1()) m.SetUVs(1, new List<Vector2>(uvs1));
if (content.HasUV2()) m.SetUVs(2, new List<Vector2>(uvs2));
if (content.HasUV2()) m.SetUVs(2, new List<Vector2>(uvs2));
if (content.HasUV3()) m.SetUVs(3, new List<Vector2>(uvs3));
if (content.HasUV3()) m.SetUVs(3, new List<Vector2>(uvs3));
// BoneWeight、bindposesを追加
if (content.HasBoneWeight()) {
m.boneWeights = ToArray(boneWeights);
m.bindposes = bindposes;
}
// faces
// faces
m.subMeshCount = submeshes.Length;
m.subMeshCount = submeshes.Length;
for (int s = 0; s < submeshes.Length; ++s) {
for (int s = 0; s < submeshes.Length; ++s) {
m.SetTriangles(FacesToTriangles(submeshes[s].faces), s);
m.SetTriangles(FacesToTriangles(submeshes[s].faces), s);
}
}
return m;
return m;
}
}
#endregion Mesh <-> MeshX
#endregion Mesh <-> MeshX
#endif
#endif
public static int GetIndexInFace(int[] face, int v) {
public static int GetIndexInFace(int[] face, int v) {
return System.Array.IndexOf<int>(face, v);
return System.Array.IndexOf<int>(face, v);
}
}
public static int GetNextInFace(int[] face, int v, int shift = 1) {
public static int GetNextInFace(int[] face, int v, int shift = 1) {
// v. -> .
// v. -> .
// f v
// f v
// . < .
// . < .
int i = GetIndexInFace(face, v);
int i = GetIndexInFace(face, v);
int l = face.Length;
int l = face.Length;
return face[(i + shift + l) % l];
return face[(i + shift + l) % l];
}
}
public Edge GetNextInFace(Edge edge, int shift = 1) {
public Edge GetNextInFace(Edge edge, int shift = 1) {
int[] face = GetFaceVertexIndices(edge.face);
int[] face = GetFaceVertexIndices(edge.face);
int l = face.Length;
int l = face.Length;
return new Edge {
return new Edge {
face = edge.face,
face = edge.face,
index = (edge.index + shift + l) % l
index = (edge.index + shift + l) % l
};
};
}
}
public IEnumerable<Face> IterAllFaces() {
public IEnumerable<Face> IterAllFaces() {
for (int s = 0; s < submeshes.Length; ++s) {
for (int s = 0; s < submeshes.Length; ++s) {
int[][] faces = submeshes[s].faces;
int[][] faces = submeshes[s].faces;
for (int f = 0; f < faces.Length; ++f) {
for (int f = 0; f < faces.Length; ++f) {
yield return new Face { submesh = s, index = f };
yield return new Face { submesh = s, index = f };
}
}
}
}
}
}
public IEnumerable<Edge> IterAllEdges() {
public IEnumerable<Edge> IterAllEdges() {
foreach (Face face in IterAllFaces()) {
foreach (Face face in IterAllFaces()) {
int[] vs = GetFaceVertexIndices(face);
int[] vs = GetFaceVertexIndices(face);
for (int i = 0; i < vs.Length; ++i) {
for (int i = 0; i < vs.Length; ++i) {
yield return new Edge {
yield return new Edge {
face = face,
face = face,
index = i
index = i
};
};
}
}
}
}
}
}
public int[] GetFaceVertexIndices(Face f) {
public int[] GetFaceVertexIndices(Face f) {
return submeshes[f.submesh].faces[f.index];
return submeshes[f.submesh].faces[f.index];
}
}
public int[] GetEdgeVertexIndices(Edge e) {
public int[] GetEdgeVertexIndices(Edge e) {
int[] vs = GetFaceVertexIndices(e.face);
int[] vs = GetFaceVertexIndices(e.face);
int v0 = vs[e.index];
int v0 = vs[e.index];
int v1 = GetNextInFace(vs, v0);
int v1 = GetNextInFace(vs, v0);
return new[] { v0, v1 };
return new[] { v0, v1 };
}
}
private Edge FindNeighbor(Edge e) {
private Edge FindNeighbor(Edge e) {
int[] vs = GetEdgeVertexIndices(e);
int[] vs = GetEdgeVertexIndices(e);
// edge "p0 -> p1"
// edge "p0 -> p1"
int p0 = posIndices[vs[0]];
int p0 = posIndices[vs[0]];
int p1 = posIndices[vs[1]];
int p1 = posIndices[vs[1]];
// find edge "p1 -> p0"
// find edge "p1 -> p0"
foreach (int V1 in positionVertices[p1]) {
foreach (int V1 in positionVertices[p1]) {
foreach (Edge E in vertexEdges[V1]) {
foreach (Edge E in vertexEdges[V1]) {
int V0 = GetEdgeVertexIndices(E)[1];
int V0 = GetEdgeVertexIndices(E)[1];
int P0 = posIndices[V0];
int P0 = posIndices[V0];
if (P0 == p0) {
if (P0 == p0) {
return E;
return E;
}
}
}
}
}
}
return Edge.Invalid;
return Edge.Invalid;
}
}
public EdgeType GetEdgeType(Edge e) {
public EdgeType GetEdgeType(Edge e) {
Debug.Assert(e.IsValid(), "GetEdgeType for invalid edge");
Debug.Assert(e.IsValid(), "GetEdgeType for invalid edge");
Edge n = GetNeighbor(e);
Edge n = GetNeighbor(e);
if (!n.IsValid()) return EdgeType.boundary;
if (!n.IsValid()) return EdgeType.boundary;
int[] E = GetEdgeVertexIndices(e);
int[] E = GetEdgeVertexIndices(e);
int[] N = GetEdgeVertexIndices(n);
int[] N = GetEdgeVertexIndices(n);
if (E[0] > N[0]) return EdgeType.back;
if (E[0] > N[0]) return EdgeType.back;
EdgeType type = EdgeType.solid;
EdgeType type = EdgeType.solid;
if (E[0] != N[1]) type ^= EdgeType.seam0;
if (E[0] != N[1]) type ^= EdgeType.seam0;
if (E[1] != N[0]) type ^= EdgeType.seam1;
if (E[1] != N[0]) type ^= EdgeType.seam1;
return type;
return type;
}
}
public static Vector AverageVectors(Vector[] ps, float[] weights = null) {
public static Vector AverageVectors(Vector[] ps, float[] weights = null) {
Vector p = new Vector();
Vector p = new Vector();
float ws = 0;
float ws = 0;
for (int i = 0; i < ps.Length; ++i) {
for (int i = 0; i < ps.Length; ++i) {
float w = weights != null ? weights[i] : 1.0f;
float w = weights != null ? weights[i] : 1
p += w * ps[i];
ws += w;
}
p /= ws;
return p;
}
private static Vertex AverageVertices(Vertex[] vs, VertexContent c, float[] weights = null) {
Vertex r = new Vertex();
float ws = 0;
for (int i = 0; i < vs.Length; ++i) {
Vertex v = vs[i];
float w = weights != null ? weights[i] : 1f;
if (c.HasPosition()) r.position += w * v.position;
if (c.HasNormal()) r.normal += w * v.normal;
if (c.HasTangent()) r.tangent += w * v.tangent;
if (c.HasUV0()) r.uv0 += w * v.uv0;
if (c.HasUV1()) r.uv1 += w * v.uv1;
if (c.HasUV2()) r.uv2 += w * v.uv2;
if (c.HasUV3()) r.uv3 += w * v.uv3;
ws += w;
}
if (c.HasPosition()) r.position /= ws;
if (c.HasNormal()) r.normal.Normalize();
if (c.HasTangent()) r.tangent.Normalize();
if (c.HasUV0()) r.uv0 /= ws;
if (c.HasUV1()) r.uv1 /= ws;
if (c.HasUV2()) r.uv2 /= ws;
if (c.HasUV3()) r.uv3 /= ws;
r.posIndex = -1;
return r;
}
public Vertex Average(Vertex[] vs, VertexContent mask = VertexContent.full, float[] weights = null) {
return MeshX.AverageVertices(vs, content & mask, weights);
}
}
}
namespace Torec
{
using VertexKey = System.UInt32;
public static class CatmullClark {
// based on:
// http://www.rorydriscoll.com/2008/08/01/catmull-clark-subdivision-the-basics/
// https://rosettacode.