Subdivision.cs

Created Diff never expires
56 removals
Lines
Total
Removed
Words
Total
Removed
To continue using this feature, upgrade to
Diffchecker logo
Diffchecker Pro
636 lines
125 additions
Lines
Total
Added
Words
Total
Added
To continue using this feature, upgrade to
Diffchecker logo
Diffchecker Pro
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.