Subdivision.cs
14 removals
1016 lines
193 additions
1103 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.0f;
p += w * ps[i];
p += w * ps[i];
ws += w;
ws += w;
}
}
p /= ws;
p /= ws;
return p;
return p;
}
}
private static Dictionary<int, float> boneWeightBuffer = new Dictionary<int, float>();
private static List<KeyValuePair<int, float>> dominantBoneWeights = new List<KeyValuePair<int, float>>();
// BoneWeightを追加
// インデックスごとにウェイトを集計し、上位4本を合成ウェイトとして採用する
private static Vertex AverageVertices(Vertex[] vs, VertexContent c, float[] weights = null) {
private static Vertex AverageVertices(Vertex[] vs, VertexContent c, float[] weights = null) {
Vertex r = new Vertex();
Vertex r = new Vertex();
boneWeightBuffer.Clear();
float ws = 0;
float ws = 0;
for (int i = 0; i < vs.Length; ++i) {
for (int i = 0; i < vs.Length; ++i) {
Vertex v = vs[i];
Vertex v = vs[i];
float w = weights != null ? weights[i] : 1f;
float w = weights != null ? weights[i] : 1f;
if (c.HasPosition()) r.position += w * v.position;
if (c.HasPosition()) r.position += w * v.position;
if (c.HasNormal()) r.normal += w * v.normal;
if (c.HasNormal()) r.normal += w * v.normal;
if (c.HasTangent()) r.tangent += w * v.tangent;
if (c.HasTangent()) r.tangent += w * v.tangent;
if (c.HasUV0()) r.uv0 += w * v.uv0;
if (c.HasUV0()) r.uv0 += w * v.uv0;
if (c.HasUV1()) r.uv1 += w * v.uv1;
if (c.HasUV1()) r.uv1 += w * v.uv1;
if (c.HasUV2()) r.uv2 += w * v.uv2;
if (c.HasUV2()) r.uv2 += w * v.uv2;
if (c.HasUV3()) r.uv3 += w * v.uv3;
if (c.HasUV3()) r.uv3 += w * v.uv3;
if (c.HasBoneWeight()) {
BoneWeight bw = v.boneWeight;
boneWeightBuffer.TryAdd(bw.boneIndex0, 0f);
boneWeightBuffer.TryAdd(bw.boneIndex1, 0f);
boneWeightBuffer.TryAdd(bw.boneIndex2, 0f);
boneWeightBuffer.TryAdd(bw.boneIndex3, 0f);
boneWeightBuffer[bw.boneIndex0] += w * bw.weight0;
boneWeightBuffer[bw.boneIndex1] += w * bw.weight1;
boneWeightBuffer[bw.boneIndex2] += w * bw.weight2;
boneWeightBuffer[bw.boneIndex3] += w * bw.weight3;
}
ws += w;
ws += w;
}
}
if (c.HasPosition()) r.position /= ws;
if (c.HasPosition()) r.position /= ws;
if (c.HasNormal()) r.normal.Normalize();
if (c.HasNormal()) r.normal.Normalize();
if (c.HasTangent()) r.tangent.Normalize();
if (c.HasTangent()) r.tangent.Normalize();
if (c.HasUV0()) r.uv0 /= ws;
if (c.HasUV0()) r.uv0 /= ws;
if (c.HasUV1()) r.uv1 /= ws;
if (c.HasUV1()) r.uv1 /= ws;
if (c.HasUV2()) r.uv2 /= ws;
if (c.HasUV2()) r.uv2 /= ws;
if (c.HasUV3()) r.uv3 /= ws;
if (c.HasUV3()) r.uv3 /= ws;
if (c.HasBoneWeight()) {
dominantBoneWeights.Clear();
dominantBoneWeights.AddRange(boneWeightBuffer.OrderByDescending(p => p.Value).Take(4));
float boneWeightSum = dominantBoneWeights.Select(p => p.Value).Sum();
int boneCount = dominantBoneWeights.Count;
if (boneWeightSum <= 0f) boneWeightSum = 1f;
BoneWeight bw = new BoneWeight();
if (boneCount > 0) {
bw.boneIndex0 = dominantBoneWeights[0].Key;
bw.weight0 = dominantBoneWeights[0].Value / boneWeightSum;
if (boneCount > 1) {
bw.boneIndex1 = dominantBoneWeights[1].Key;
bw.weight1 = dominantBoneWeights[1].Value / boneWeightSum;
if (boneCount > 2) {
bw.boneIndex2 = dominantBoneWeights[2].Key;
bw.weight2 = dominantBoneWeights[2].Value / boneWeightSum;
if (boneCount > 3) {
bw.boneIndex3 = dominantBoneWeights[3].Key;
bw.weight3 = dominantBoneWeights[3].Value / boneWeightSum;
}
}
}
}
r.boneWeight = bw;
}
r.posIndex = -1;
r.posIndex = -1;
return r;
return r;
}
}
public Vertex Average(Vertex[] vs, VertexContent mask = VertexContent.full, float[] weights = null) {
public Vertex Average(Vertex[] vs, VertexContent mask = VertexContent.full, float[] weights = null) {
return MeshX.AverageVertices(vs, content & mask, weights);
return MeshX.AverageVertices(vs, content & mask, weights);
}
}
}
}
}
}
namespace Torec
namespace Torec
{
{
using VertexKey = System.UInt32;
using VertexKey = System.UInt32;
public static class CatmullClark {
public static class CatmullClark {
// based on:
// based on:
// http://www.rorydriscoll.com/2008/08/01/catmull-clark-subdivision-the-basics/
// http://www.rorydriscoll.com/2008/08/01/catmull-clark-subdivision-the-basics/
// https://rosettacode.org/wiki/Catmull%E2%80%93Clark_subdivision_surface
// https://rosettacode.org/wiki/Catmull%E2%80%93Clark_subdivision_surface
// https://graphics.pixar.com/opensubdiv/docs/subdivision_surfaces.html
// https://graphics.pixar.com/opensubdiv/docs/subdivision_surfaces.html
// calculation of unique keys for faces and edges
// calculation of unique keys for faces and edges
#region Vertex keys
#region Vertex keys
// Check limits of mesh data arrays for valid vertex keys
// Check limits of mesh data arrays for valid vertex keys
private static readonly int keyBitsSubmeshes = 6;
private static readonly int keyBitsSubmeshes = 6;
private static readonly int keyBitsSubmeshFaces = 32 - keyBitsSubmeshes - 2 - 1;
private static readonly int keyBitsSubmeshFaces = 32 - keyBitsSubmeshes - 2 - 1;
private static readonly int keyMaxSubmeshes = 1 << keyBitsSubmeshes;
private static readonly int keyMaxSubmeshes = 1 << keyBitsSubmeshes;
private static readonly int keyMaxSubmeshFaces = 1 << keyBitsSubmeshFaces;
private static readonly int keyMaxSubmeshFaces = 1 << keyBitsSubmeshFaces;
//
//
private static void CheckForVertexKeys(MeshX mesh) {
private static void CheckForVertexKeys(MeshX mesh) {
Debug.AssertFormat(mesh.submeshes.Length <= keyMaxSubmeshes,
Debug.AssertFormat(mesh.submeshes.Length <= keyMaxSubmeshes,
"Subdivision may not work correctly for submesh count ({0}) > {1}", mesh.submeshes.Length, keyMaxSubmeshes);
"Subdivision may not work correctly for submesh count ({0}) > {1}", mesh.submeshes.Length, keyMaxSubmeshes);
foreach (Submesh s in mesh.submeshes) {
foreach (Submesh s in mesh.submeshes) {
Debug.AssertFormat(s.faces.Length <= keyMaxSubmeshFaces,
Debug.AssertFormat(s.faces.Length <= keyMaxSubmeshFaces,
"Subdivision may not work correctly for submesh face count ({0}) > {1}", s.faces.Length, keyMaxSubmeshFaces);
"Subdivision may not work correctly for submesh face count ({0}) > {1}", s.faces.Length, keyMaxSubmeshFaces);
}
}
}
}
private static VertexKey GetFacePointKey(int si, int fi) {
private static VertexKey GetFacePointKey(int si, int fi) {
// Bit format: [face index *23][submesh index *6][1]
// Bit format: [face index *23][submesh index *6][1]
VertexKey key = 0;
VertexKey key = 0;
key ^= (VertexKey)fi;
key ^= (VertexKey)fi;
key <<= keyBitsSubmeshes; key ^= (VertexKey)si;
key <<= keyBitsSubmeshes; key ^= (VertexKey)si;
key <<= 1; key |= 1; // lowest bit 1 for face point key
key <<= 1; key |= 1; // lowest bit 1 for face point key
return key;
return key;
}
}
private static VertexKey GetEdgePointKey(int si, int fi, int ei) {
private static VertexKey GetEdgePointKey(int si, int fi, int ei) {
// Bit format: [face index *23][submesh index *6][face edge index *2][0]
// Bit format: [face index *23][submesh index *6][face edge index *2][0]
VertexKey key = 0;
VertexKey key = 0;
key ^= (VertexKey)fi;
key ^= (VertexKey)fi;
key <<= keyBitsSubmeshes; key ^= (VertexKey)si;
key <<= keyBitsSubmeshes; key ^= (VertexKey)si;
key <<= 2; key ^= (VertexKey)ei;
key <<= 2; key ^= (VertexKey)ei;
key <<= 1; key |= 0; // lowest bit 0 for edge point key
key <<= 1; key |= 0; // lowest bit 0 for edge point key
return key;
return key;
}
}
private static VertexKey GetFacePointKey(Face f) {
private static VertexKey GetFacePointKey(Face f) {
return GetFacePointKey(f.submesh, f.index);
return GetFacePointKey(f.submesh, f.index);
}
}
private static VertexKey GetEdgePointKey(Edge e) { // Be sure it's not EdgeType.back
private static VertexKey GetEdgePointKey(Edge e) { // Be sure it's not EdgeType.back
return GetEdgePointKey(e.face.submesh, e.face.index, e.index);
return GetEdgePointKey(e.face.submesh, e.face.index, e.index);
}
}
#endregion Vertex keys
#endregion Vertex keys
private class MeshVertices { // Allows to add and get mesh vertices by keys
private class MeshVertices { // Allows to add and get mesh vertices by keys
public MeshX mesh;
public MeshX mesh;
public Dictionary<VertexKey, int> vertexIndices = new Dictionary<VertexKey, int>();
public Dictionary<VertexKey, int> vertexIndices = new Dictionary<VertexKey, int>();
public struct VertexKeyPair {
public struct VertexKeyPair {
public Vertex vertex;
public Vertex vertex;
public VertexKey key;
public VertexKey key;
}
}
public void AddVertices(Vertex position, params VertexKeyPair[] vertexPairs) { // Add a single position and several corresponding vertices
public void AddVertices(Vertex position, params VertexKeyPair[] vertexPairs) { // Add a single position and several corresponding vertices
int pi = mesh.AddPosition(position);
int pi = mesh.AddPosition(position);
foreach (VertexKeyPair p in vertexPairs) {
foreach (VertexKeyPair p in vertexPairs) {
Vertex v = p.vertex;
Vertex v = p.vertex;
v.posIndex = pi;
v.posIndex = pi;
int vi = mesh.AddVertex(v, addPosition: false);
int vi = mesh.AddVertex(v, addPosition: false);
vertexIndices[p.key] = vi;
vertexIndices[p.key] = vi;
}
}
}
}
public Vertex GetVertex(VertexKey key, VertexContent mask = VertexContent.full) {
public Vertex GetVertex(VertexKey key, VertexContent mask = VertexContent.full) {
return mesh.GetVertex(vertexIndices[key], mask);
return mesh.GetVertex(vertexIndices[key], mask);
}
}
}
}
private static MeshVertices.VertexKeyPair Pair(Vertex v, VertexKey k) { // new VertexKeyPair shortcut
private static MeshVertices.VertexKeyPair Pair(Vertex v, VertexKey k) { // new VertexKeyPair shortcut
return new MeshVertices.VertexKeyPair { vertex = v, key = k };
return new MeshVertices.VertexKeyPair { vertex = v, key = k };
}
}
private static VertexContent maskPosition = VertexContent.pos | VertexContent.normal;
private static VertexContent maskPosition = VertexContent.pos | VertexContent.normal;
private static VertexContent maskVertex = VertexContent.tangent | VertexContent.uv0 | VertexContent.uv1 | VertexContent.uv2 | VertexContent.uv3;
private static VertexContent maskVertex = VertexContent.tangent | VertexContent.uv0 | VertexContent.uv1 | VertexContent.uv2 | VertexContent.uv3 | VertexContent.boneWeight; // BoneWeightを追加
public static MeshX Subdivide(MeshX mesh, Options options)
public static MeshX Subdivide(MeshX mesh, Options options)
{
{
if (!mesh.helpersInited) mesh.InitHelpers();
if (!mesh.helpersInited) mesh.InitHelpers();
if (!mesh.normalPerPosition) mesh.MakeNormalPerPosition();
if (!mesh.normalPerPosition) mesh.MakeNormalPerPosition();
MeshX newMesh = new MeshX {
MeshX newMesh = new MeshX {
name = mesh.name + "/s",
name = mesh.name + "/s",
content = mesh.content,
content = mesh.content,
normalPerPosition = true,
normalPerPosition = true,
bindposes = mesh.bindposes,
};
};
newMesh.StartBuilding();
newMesh.StartBuilding();
CheckForVertexKeys(mesh);
CheckForVertexKeys(mesh);
var newVertices = new MeshVertices { mesh = newMesh };
var newVertices = new MeshVertices { mesh = newMesh };
var edgeMidPositions = new Dictionary<VertexKey, Vertex>(); // position & normal
var edgeMidPositions = new Dictionary<VertexKey, Vertex>(); // position & normal
// reserve indices for new control-points: they keep indices and we need no special keys for them.
// reserve indices for new control-points: they keep indices and we need no special keys for them.
// later for control-points we set position & normal only: tangent & uv stay the same.
// later for control-points we set position & normal only: tangent & uv stay the same.
for (int pi = 0; pi < mesh.positions.Count; ++pi) {
for (int pi = 0; pi < mesh.positions.Count; ++pi) {
newMesh.AddPosition(default(Vertex));
newMesh.AddPosition(default(Vertex));
}
}
for (int vi = 0; vi < mesh.vertexCount; ++vi) {
for (int vi = 0; vi < mesh.vertexCount; ++vi) {
Vertex v = mesh.GetVertex(vi, maskVertex);
Vertex v = mesh.GetVertex(vi, maskVertex);
newMesh.AddVertex(v, addPosition: false);
newMesh.AddVertex(v, addPosition: false);
}
}
// add face-points
// add face-points
// "for each face, a face point is created which is the average of all the points of the face."
// "for each face, a face point is created which is the average of all the points of the face."
foreach (Face f in mesh.IterAllFaces()) {
foreach (Face f in mesh.IterAllFaces()) {
Vertex[] vs = mesh.GetVertices(mesh.GetFaceVertexIndices(f));
Vertex[] vs = mesh.GetVertices(mesh.GetFaceVertexIndices(f));
Vertex v = mesh.Average(vs);
Vertex v = mesh.Average(vs);
VertexKey keyF = GetFacePointKey(f);
VertexKey keyF = GetFacePointKey(f);
newVertices.AddVertices(v, Pair(v, keyF));
newVertices.AddVertices(v, Pair(v, keyF));
}
}
// add edge-points
// add edge-points
foreach (Edge e in mesh.IterAllEdges()) {
foreach (Edge e in mesh.IterAllEdges()) {
EdgeType type = mesh.GetEdgeType(e);
EdgeType type = mesh.GetEdgeType(e);
if (type == EdgeType.back) continue;
if (type == EdgeType.back) continue;
if (type == EdgeType.boundary) {
if (type == EdgeType.boundary) {
// "for the edges that are on the border of a hole, the edge point is just the middle of the edge."
// "for the edges that are on the border of a hole, the edge point is just the middle of the edge."
Vertex midE = mesh.Average(
Vertex midE = mesh.Average(
mesh.GetVertices(mesh.GetEdgeVertexIndices(e))
mesh.GetVertices(mesh.GetEdgeVertexIndices(e))
);
);
VertexKey keyE = GetEdgePointKey(e);
VertexKey keyE = GetEdgePointKey(e);
edgeMidPositions[keyE] = midE;
edgeMidPositions[keyE] = midE;
newVertices.AddVertices(midE, Pair(midE, keyE));
newVertices.AddVertices(midE, Pair(midE, keyE));
} else if (type == EdgeType.solid) {
} else if (type == EdgeType.solid) {
// "for each edge, an edge point is created which is the average between the center of the edge
// "for each edge, an edge point is created which is the average between the center of the edge
// and the center of the segment made with the face points of the two adjacent faces."
// and the center of the segment made with the face points of the two adjacent faces."
Edge n = mesh.GetNeighbor(e);
Edge n = mesh.GetNeighbor(e);
Vertex midE = mesh.Average(
Vertex midE = mesh.Average(
mesh.GetVertices(mesh.GetEdgeVertexIndices(e))
mesh.GetVertices(mesh.GetEdgeVertexIndices(e))
);
);
VertexKey keyE = GetEdgePointKey(e);
VertexKey keyE = GetEdgePointKey(e);
edgeMidPositions[keyE] = midE;
edgeMidPositions[keyE] = midE;
Vertex v = mesh.Average(
Vertex v = mesh.Average(
new[] {
new[] {
midE,
midE,
newVertices.GetVertex(GetFacePointKey(e.face)),
newVertices.GetVertex(GetFacePointKey(e.face)),
newVertices.GetVertex(GetFacePointKey(n.face)),
newVertices.GetVertex(GetFacePointKey(n.face)),
},
},
weights: new[] { 2f, 1f, 1f }
weights: new[] { 2f, 1f, 1f }
);
);
newVertices.AddVertices(v, Pair(v, keyE));
newVertices.AddVertices(v, Pair(v, keyE));
} else { // seam edge
} else { // seam edge
Edge n = mesh.GetNeighbor(e);
Edge n = mesh.GetNeighbor(e);
Vertex midE = mesh.Average(
Vertex midE = mesh.Average(
mesh.GetVertices(mesh.GetEdgeVertexIndices(e))
mesh.GetVertices(mesh.GetEdgeVertexIndices(e))
);
);
Vertex midN = mesh.Average(
Vertex midN = mesh.Average(
mesh.GetVertices(mesh.GetEdgeVertexIndices(n)),
mesh.GetVertices(mesh.GetEdgeVertexIndices(n)),
maskVertex // pos & normal already got in midE
maskVertex // pos & normal already got in midE
);
);
VertexKey keyE = GetEdgePointKey(e);
VertexKey keyE = GetEdgePointKey(e);
VertexKey keyN = GetEdgePointKey(n);
VertexKey keyN = GetEdgePointKey(n);
edgeMidPositions[keyE] = midE;
edgeMidPositions[keyE] = midE;
Vertex p = mesh.Average( // pos & normal only
Vertex p = mesh.Average( // pos & normal only
new[] {
new[] {
midE,
midE,
newVertices.GetVertex(GetFacePointKey(e.face), maskPosition),
newVertices.GetVertex(GetFacePointKey(e.face), maskPosition),
newVertices.GetVertex(GetFacePointKey(n.face), maskPosition),
newVertices.GetVertex(GetFacePointKey(n.face), maskPosition),
},
},
maskPosition,
maskPosition,
new[] { 2f, 1f, 1f }
new[] { 2f, 1f, 1f }
);
);
newVertices.AddVertices(p, Pair(midE, keyE), Pair(midN, keyN));
newVertices.AddVertices(p, Pair(midE, keyE), Pair(midN, keyN));
}
}
}
}
// move control-points
// move control-points
for (int pi = 0; pi < mesh.positions.Count; ++pi) {
for (int pi = 0; pi < mesh.positions.Count; ++pi) {
// count edges
// count edges
var edges = new List<Edge>(); // edges outcoming from the position
var edges = new List<Edge>(); // edges outcoming from the position
var boundaries = new List<Edge>();
var boundaries = new List<Edge>();
var front = new List<Edge>();
var front = new List<Edge>();
foreach (int vi in mesh.positionVertices[pi]) {
foreach (int vi in mesh.positionVertices[pi]) {
foreach (Edge e in mesh.vertexEdges[vi]) {
foreach (Edge e in mesh.vertexEdges[vi]) {
edges.Add(e);
edges.Add(e);
foreach (Edge edge in new[] { e, mesh.GetNextInFace(e, -1) }) {
foreach (Edge edge in new[] { e, mesh.GetNextInFace(e, -1) }) {
EdgeType type = mesh.GetEdgeType(edge);
EdgeType type = mesh.GetEdgeType(edge);
if (type == EdgeType.boundary) {
if (type == EdgeType.boundary) {
boundaries.Add(edge);
boundaries.Add(edge);
} else if (type != EdgeType.back) {
} else if (type != EdgeType.back) {
front.Add(edge);
front.Add(edge);
}
}
}
}
}
}
}
}
Debug.AssertFormat(boundaries.Count > 0 || (edges.Count == front.Count),
Debug.AssertFormat(boundaries.Count > 0 || (edges.Count == front.Count),
"Counting edges error: boundaries: {0}, edges {1}, front: {2}", boundaries.Count, edges.Count, front.Count);
"Counting edges error: boundaries: {0}, edges {1}, front: {2}", boundaries.Count, edges.Count, front.Count);
Vertex controlPoint;
Vertex controlPoint;
if (boundaries.Count > 0) {
if (boundaries.Count > 0) {
bool isCorner = edges.Count == 1;
bool isCorner = edges.Count == 1;
if (options.boundaryInterpolation == Options.BoundaryInterpolation.fixBoundaries ||
if (options.boundaryInterpolation == Options.BoundaryInterpolation.fixBoundaries ||
options.boundaryInterpolation == Options.BoundaryInterpolation.fixCorners && isCorner) {
options.boundaryInterpolation == Options.BoundaryInterpolation.fixCorners && isCorner) {
controlPoint = mesh.GetPosition(pi); // keep same position
controlPoint = mesh.GetPosition(pi); // keep same position
} else {
} else {
// "for the vertex points that are on the border of a hole, the new coordinates are calculated as follows:
// "for the vertex points that are on the border of a hole, the new coordinates are calculated as follows:
// 1. in all the edges the point belongs to, only take in account the middles of the edges that are on the border of the hole
// 1. in all the edges the point belongs to, only take in account the middles of the edges that are on the border of the hole
// 2. calculate the average between these points (on the hole boundary) and the old coordinates (also on the hole boundary)."
// 2. calculate the average between these points (on the hole boundary) and the old coordinates (also on the hole boundary)."
Vertex[] vs = new Vertex[boundaries.Count + 1];
Vertex[] vs = new Vertex[boundaries.Count + 1];
vs[0] = mesh.GetPosition(pi);
vs[0] = mesh.GetPosition(pi);
for (int e = 0; e < boundaries.Count; ++e) {
for (int e = 0; e < boundaries.Count; ++e) {
vs[e + 1] = edgeMidPositions[GetEdgePointKey(boundaries[e])];
vs[e + 1] = edgeMidPositions[GetEdgePointKey(boundaries[e])];
}
}
controlPoint = mesh.Average(vs, maskPosition);
controlPoint = mesh.Average(vs, maskPosition);
}
}
} else {
} else {
// "for each vertex point, its coordinates are updated from (new_coords):
// "for each vertex point, its coordinates are updated from (new_coords):
// the old coordinates (P),
// the old coordinates (P),
// the average of the face points of the faces the point belongs to (F),
// the average of the face points of the faces the point belongs to (F),
// the average of the centers of edges the point belongs to (R),
// the average of the centers of edges the point belongs to (R),
// how many faces a point belongs to (n), then use this formula:
// how many faces a point belongs to (n), then use this formula:
// (F + 2R + (n-3)P) / n"
// (F + 2R + (n-3)P) / n"
// edge-midpoints
// edge-midpoints
Vertex[] ms = new Vertex[front.Count];
Vertex[] ms = new Vertex[front.Count];
for (int e = 0; e < front.Count; ++e) {
for (int e = 0; e < front.Count; ++e) {
ms[e] = edgeMidPositions[GetEdgePointKey(front[e])];
ms[e] = edgeMidPositions[GetEdgePointKey(front[e])];
}
}
Vertex edgeMidAverage = mesh.Average(ms, maskPosition);
Vertex edgeMidAverage = mesh.Average(ms, maskPosition);
// face-points
// face-points
Vertex[] fs = new Vertex[edges.Count];
Vertex[] fs = new Vertex[edges.Count];
for (int e = 0; e < edges.Count; ++e) {
for (int e = 0; e < edges.Count; ++e) {
fs[e] = newVertices.GetVertex(GetFacePointKey(edges[e].face), maskPosition);
fs[e] = newVertices.GetVertex(GetFacePointKey(edges[e].face), maskPosition);
}
}
Vertex faceAverage = mesh.Average(fs, maskPosition);
Vertex faceAverage = mesh.Average(fs, maskPosition);
// new control-point
// new control-point
controlPoint = mesh.Average(
controlPoint = mesh.Average(
new[] {
new[] {
faceAverage,
faceAverage,
edgeMidAverage,
edgeMidAverage,
mesh.GetPosition(pi)
mesh.GetPosition(pi)
},
},
maskPosition,
maskPosition,
new[] { 1f, 2f, edges.Count - 3f }
new[] { 1f, 2f, edges.Count - 3f }
);
);
}
}
// set moved control-point position to reserved index
// set moved control-point position to reserved index
newMesh.SetPosition(pi, controlPoint);
newMesh.SetPosition(pi, controlPoint);
}
}
// add 4 new quads per face
// add 4 new quads per face
// eis[i]
// eis[i]
// fis[i].----.----.fis[i+1]
// fis[i].----.----.fis[i+1]
// | |
// | |
// eis[i-1]. ci. .
// eis[i-1]. ci. .
// | |
// | |
// .____.____.
// .____.____.
newMesh.submeshes = new Submesh[mesh.submeshes.Length];
newMesh.submeshes = new Submesh[mesh.submeshes.Length];
for (int si = 0; si < mesh.submeshes.Length; ++si) {
for (int si = 0; si < mesh.submeshes.Length; ++si) {
int[][] faces = mesh.submeshes[si].faces;
int[][] faces = mesh.submeshes[si].faces;
// get new face count
// get new face count
int faceCount = 0;
int faceCount = 0;
foreach (int[] face in faces) faceCount += face.Length;
foreach (int[] face in faces) faceCount += face.Length;
newMesh.submeshes[si].faces = new int[faceCount][];
newMesh.submeshes[si].faces = new int[faceCount][];
// fill faces
// fill faces
int faceIndex = 0;
int faceIndex = 0;
for (int fi = 0; fi < faces.Length; ++fi) {
for (int fi = 0; fi < faces.Length; ++fi) {
int[] fis = faces[fi];
int[] fis = faces[fi];
int edgeCount = fis.Length; // 3 or 4
int edgeCount = fis.Length; // 3 or 4
//
//
Face f = new Face { submesh = si, index = fi };
Face f = new Face { submesh = si, index = fi };
int ci = newVertices.vertexIndices[GetFacePointKey(f)]; // face-point index
int ci = newVertices.vertexIndices[GetFacePointKey(f)]; // face-point index
//
//
int[] eis = new int[edgeCount]; // edge-point indices
int[] eis = new int[edgeCount]; // edge-point indices
for (int i = 0; i < edgeCount; ++i) {
for (int i = 0; i < edgeCount; ++i) {
Edge e = new Edge { face = f, index = i };
Edge e = new Edge { face = f, index = i };
// get neighbor for solid back edges
// get neighbor for solid back edges
if (mesh.GetEdgeType(e) == EdgeType.back) { //!!! here should be some EdgeType.backOfSeam or EdgeType.backOfSolid
if (mesh.GetEdgeType(e) == EdgeType.back) { //!!! here should be some EdgeType.backOfSeam or EdgeType.backOfSolid
Edge n = mesh.GetNeighbor(e);
Edge n = mesh.GetNeighbor(e);
if (mesh.GetEdgeType(n) == EdgeType.solid) {
if (mesh.GetEdgeType(n) == EdgeType.solid) {
e = n;
e = n;
}
}
}
}
eis[i] = newVertices.vertexIndices[GetEdgePointKey(e)];
eis[i] = newVertices.vertexIndices[GetEdgePointKey(e)];
}
}
//
//
for (int i = 0; i < edgeCount; ++i) {
for (int i = 0; i < edgeCount; ++i) {
int[] q = new int[4]; // new faces are always quads
int[] q = new int[4]; // new faces are always quads
int s = edgeCount == 4 ? i : 0; // shift indices (+ i) to keep quad orientation
int s = edgeCount == 4 ? i : 0; // shift indices (+ i) to keep quad orientation
q[(0 + s) % 4] = fis[i];
q[(0 + s) % 4] = fis[i];
q[(1 + s) % 4] = eis[i];
q[(1 + s) % 4] = eis[i];
q[(2 + s) % 4] = ci;
q[(2 + s) % 4] = ci;
q[(3 + s) % 4] = eis[(i - 1 + edgeCount) % edgeCount];
q[(3 + s) % 4] = eis[(i - 1 + edgeCount) % edgeCount];
newMesh.submeshes[si].faces[faceIndex++] = q;
newMesh.submeshes[si].faces[faceIndex++] = q;
}
}
}
}
}
}
newMesh.FinishBuilding();
newMesh.FinishBuilding();
return newMesh;
return newMesh;
}
}
public static MeshX Subdivide(MeshX mesh, int iterations, Options options = default(Options)) {
public static MeshX Subdivide(MeshX mesh, int iterations, Options options = default(Options)) {
string name = mesh.name;
string name = mesh.name;
// subdivide
// subdivide
for (int i = 0; i < iterations; ++i) {
for (int i = 0; i < iterations; ++i) {
mesh = Subdivide(mesh, options);
mesh = Subdivide(mesh, options);
}
}
// rename
// rename
if (iterations > 0) {
if (iterations > 0) {
mesh.name = name + "/s" + iterations;
mesh.name = name + "/s" + iterations;
}
}
return mesh;
return mesh;
}
}
public struct Options {
public struct Options {
// like https://graphics.pixar.com/opensubdiv/docs/subdivision_surfaces.html#boundary-interpolation-rules
// like https://graphics.pixar.com/opensubdiv/docs/subdivision_surfaces.html#boundary-interpolation-rules
public enum BoundaryInterpolation {
public enum BoundaryInterpolation {
normal, // default
normal, // default
fixCorners,
fixCorners,
fixBoundaries,
fixBoundaries,
}
}
public BoundaryInterpolation boundaryInterpolation;
public BoundaryInterpolation boundaryInterpolation;
}
}
#if !SUBDIVIDEVECTOR4
#if !SUBDIVIDEVECTOR4
public static Mesh Subdivide(Mesh mesh, int iterations, Options options = default(Options)) {
public static Mesh Subdivide(Mesh mesh, int iterations, Options options = default(Options)) {
var m = new MeshX(mesh);
var m = new MeshX(mesh);
//m.Trace();
//m.Trace();
m = Subdivide(m, iterations, options);
m = Subdivide(m, iterations, options);
//Debug.Log("----->"); m.Trace();
//Debug.Log("----->"); m.Trace();
return m.ConvertToMesh();
return m.ConvertToMesh();
}
}
// SkinnedMeshRendererにも対応させる
public static void Subdivide(GameObject obj, int iterations, Options options = default(Options)) {
public static void Subdivide(GameObject obj, int iterations, Options options = default(Options)) {
if (iterations <= 0) return;
if (iterations <= 0) return;
MeshFilter mf = CheckMeshFilter(obj);
Component c = CheckMeshComponent(obj);
MeshFilter mf = c as MeshFilter;
SkinnedMeshRenderer smr = c as SkinnedMeshRenderer;
Mesh originalMesh = mf.sharedMesh;
Mesh originalMesh = (mf != null) ? mf.sharedMesh : (smr != null) ? smr.sharedMesh : null;
//Mesh mesh = Object.Instantiate<Mesh>(originalMesh); // no need to copy: Subdivide doesn't change the Mesh
//Mesh mesh = Object.Instantiate<Mesh>(originalMesh); // no need to copy: Subdivide doesn't change the Mesh
Mesh mesh = originalMesh;
Mesh mesh = originalMesh;
mesh = Subdivide(mesh, iterations, options);
mesh = Subdivide(mesh, iterations, options);
mf.sharedMesh = mesh;
if (mf != null) mf.sharedMesh = mesh;
if (smr != null) smr.sharedMesh = mesh;
}
}
public static MeshFilter CheckMeshFilter(GameObject obj)
// SkinnedMeshRendererにも対応させるのに併せ、名前を「CheckMeshFilter」から「CheckMeshComponent」に変更
public static Component CheckMeshComponent(GameObject obj)
{
{
if (obj == null) throw new System.NullReferenceException("No GameObject specified");
if (obj == null) throw new System.NullReferenceException("No GameObject specified");
var mf = obj.GetComponent<MeshFilter>();
var mf = obj.GetComponent<MeshFilter>();
if (mf == null) throw new System.Exception("No MeshFilter found for " + obj.name);
var smr = obj.GetComponent<SkinnedMeshRenderer>();
if (mf == null && smr == null) throw new System.Exception("No MeshFilter/SkinnedMeshRenderer found for " + obj.name);
if (mf.sharedMesh == null) throw new System.Exception("No mesh set to " + obj.name);
if ((mf != null && mf.sharedMesh == null) || (smr != null && smr.sharedMesh == null)) throw new System.Exception("No mesh set to " + obj.name);
return mf;
return (mf != null) ? mf : smr;
}
}
#endif
#endif
}
}
[System.Obsolete("MeshData.Subdivide has been deprecated. Use Torec.CatmullClark.Subdivide instead", true)]
[System.Obsolete("MeshData.Subdivide has been deprecated. Use Torec.CatmullClark.Subdivide instead", true)]
public class MeshData {
public class MeshData {
public static MeshData Subdivide(MeshData meshData, int iterations) { return meshData; } // dummy
public static MeshData Subdivide(MeshData meshData, int iterations) { return meshData; } // dummy
}
}
}
}