248 lines
8.1 KiB
C#
248 lines
8.1 KiB
C#
using UnityEditor;
|
|
|
|
namespace UnityEngine.ProBuilder.Shapes
|
|
{
|
|
/// <summary>
|
|
/// Represents a basic [sphere](../manual/Sphere.html) shape.
|
|
/// </summary>
|
|
[Shape("Sphere")]
|
|
public class Sphere : Shape
|
|
{
|
|
static readonly Vector3[] k_IcosphereVertices = new Vector3[12]
|
|
{
|
|
new Vector3(-1f, Math.phi, 0f),
|
|
new Vector3(1f, Math.phi, 0f),
|
|
new Vector3(-1f, -Math.phi, 0f),
|
|
new Vector3(1f, -Math.phi, 0f),
|
|
|
|
new Vector3(0f, -1f, Math.phi),
|
|
new Vector3(0f, 1f, Math.phi),
|
|
new Vector3(0f, -1f, -Math.phi),
|
|
new Vector3(0f, 1f, -Math.phi),
|
|
|
|
new Vector3(Math.phi, 0f, -1f),
|
|
new Vector3(Math.phi, 0f, 1f),
|
|
new Vector3(-Math.phi, 0f, -1f),
|
|
new Vector3(-Math.phi, 0f, 1f)
|
|
};
|
|
|
|
static readonly int[] k_IcosphereTriangles = new int[60]
|
|
{
|
|
0, 11, 5,
|
|
0, 5, 1,
|
|
0, 1, 7,
|
|
0, 7, 10,
|
|
0, 10, 11,
|
|
|
|
1, 5, 9,
|
|
5, 11, 4,
|
|
11, 10, 2,
|
|
10, 7, 6,
|
|
7, 1, 8,
|
|
|
|
3, 9, 4,
|
|
3, 4, 2,
|
|
3, 2, 6,
|
|
3, 6, 8,
|
|
3, 8, 9,
|
|
|
|
4, 9, 5,
|
|
2, 4, 11,
|
|
6, 2, 10,
|
|
8, 6, 7,
|
|
9, 8, 1
|
|
};
|
|
|
|
/// <summary>
|
|
/// Sets the number of times to subdivide each triangle. The more subdivisions you create, the smoother the sphere appears.
|
|
/// However, remember that each subdivision increases the number of triangles exponentially, which means that it uses a lot
|
|
/// more resources to render.
|
|
///
|
|
/// The default value is 3. Valid values range from 1 to 5.
|
|
/// </summary>
|
|
[Range(1, 5)]
|
|
[SerializeField]
|
|
int m_Subdivisions = 3;
|
|
|
|
int m_BottomMostVertexIndex = 0;
|
|
|
|
/// <summary>
|
|
/// Determines whether to smooth the edges of the polygons.
|
|
/// This property is enabled by default.
|
|
/// </summary>
|
|
[SerializeField]
|
|
bool m_Smooth = true;
|
|
|
|
/// <inheritdoc/>
|
|
public override void CopyShape(Shape shape)
|
|
{
|
|
if(shape is Sphere)
|
|
{
|
|
Sphere sphere = ( (Sphere) shape );
|
|
m_Subdivisions = sphere.m_Subdivisions;
|
|
m_BottomMostVertexIndex = sphere.m_BottomMostVertexIndex;
|
|
m_Smooth = sphere.m_Smooth;
|
|
}
|
|
}
|
|
|
|
/// <inheritdoc/>
|
|
public override Bounds UpdateBounds(ProBuilderMesh mesh, Vector3 size, Quaternion rotation, Bounds bounds)
|
|
{
|
|
bounds = mesh.mesh.bounds;
|
|
return bounds;
|
|
}
|
|
|
|
/// <inheritdoc/>
|
|
public override Bounds RebuildMesh(ProBuilderMesh mesh, Vector3 size, Quaternion rotation)
|
|
{
|
|
var radius = .5f;
|
|
// http://blog.andreaskahler.com/2009/06/creating-icosphere-mesh-in-code.html
|
|
|
|
Vector3[] v = new Vector3[k_IcosphereTriangles.Length];
|
|
|
|
// Regular Icosahedron - 12 vertices, 20 faces.
|
|
for (int i = 0; i < k_IcosphereTriangles.Length; i += 3)
|
|
{
|
|
v[i + 0] = k_IcosphereVertices[k_IcosphereTriangles[i + 0]].normalized * radius;
|
|
v[i + 1] = k_IcosphereVertices[k_IcosphereTriangles[i + 1]].normalized * radius;
|
|
v[i + 2] = k_IcosphereVertices[k_IcosphereTriangles[i + 2]].normalized * radius;
|
|
}
|
|
|
|
for (int i = 0; i < m_Subdivisions; i++)
|
|
{
|
|
v = SubdivideIcosahedron(v, radius);
|
|
}
|
|
|
|
Face[] f = new Face[v.Length / 3];
|
|
|
|
Vector3 bottomMostVertexPosition = Vector3.positiveInfinity;
|
|
|
|
for (int i = 0; i < v.Length; i += 3)
|
|
{
|
|
f[i / 3] = new Face(new int[3] { i, i + 1, i + 2 });
|
|
f[i / 3].smoothingGroup = m_Smooth ? 1 : 0;
|
|
f[i / 3].manualUV = false;
|
|
|
|
// Get the bottom most vertex of the whole shape. We'll use it as a pivot point.
|
|
for (int j = 0; j < f[i / 3].indexes.Count; ++j)
|
|
{
|
|
int index = f[i / 3].indexes[j];
|
|
|
|
if (v[index].y < bottomMostVertexPosition.y)
|
|
{
|
|
bottomMostVertexPosition = v[index];
|
|
m_BottomMostVertexIndex = index;
|
|
}
|
|
}
|
|
}
|
|
|
|
for (int i = 0; i < f.Length; i++)
|
|
{
|
|
var nrm = Math.Normal(v[f[i].indexesInternal[0]], v[f[i].indexesInternal[1]], v[f[i].indexesInternal[2]]);
|
|
var axis = Projection.VectorToProjectionAxis(nrm);
|
|
|
|
if (axis == ProjectionAxis.X)
|
|
f[i].textureGroup = 2;
|
|
else if (axis == ProjectionAxis.Y)
|
|
f[i].textureGroup = 3;
|
|
else if (axis == ProjectionAxis.Z)
|
|
f[i].textureGroup = 4;
|
|
else if (axis == ProjectionAxis.XNegative)
|
|
f[i].textureGroup = 5;
|
|
else if (axis == ProjectionAxis.YNegative)
|
|
f[i].textureGroup = 6;
|
|
else if (axis == ProjectionAxis.ZNegative)
|
|
f[i].textureGroup = 7;
|
|
}
|
|
|
|
mesh.unwrapParameters = new UnwrapParameters()
|
|
{
|
|
packMargin = 30f
|
|
};
|
|
|
|
mesh.RebuildWithPositionsAndFaces(v, f);
|
|
|
|
return UpdateBounds(mesh, size, rotation, new Bounds());
|
|
}
|
|
|
|
// Subdivides a set of vertices (wound as individual triangles) on an icosphere.
|
|
//
|
|
// /\ /\
|
|
// / \ -> /--\
|
|
// /____\ /_\/_\
|
|
//
|
|
static Vector3[] SubdivideIcosahedron(Vector3[] vertices, float radius)
|
|
{
|
|
Vector3[] v = new Vector3[vertices.Length * 4];
|
|
|
|
int index = 0;
|
|
|
|
Vector3 p0 = Vector3.zero, // 5
|
|
p1 = Vector3.zero, // 3 4
|
|
p2 = Vector3.zero, // 0, 1, 2
|
|
p3 = Vector3.zero,
|
|
p4 = Vector3.zero,
|
|
p5 = Vector3.zero;
|
|
|
|
for (int i = 0; i < vertices.Length; i += 3)
|
|
{
|
|
p0 = vertices[i + 0];
|
|
p2 = vertices[i + 1];
|
|
p5 = vertices[i + 2];
|
|
p1 = ((p0 + p2) * .5f).normalized * radius;
|
|
p3 = ((p0 + p5) * .5f).normalized * radius;
|
|
p4 = ((p2 + p5) * .5f).normalized * radius;
|
|
|
|
v[index++] = p0;
|
|
v[index++] = p1;
|
|
v[index++] = p3;
|
|
|
|
v[index++] = p1;
|
|
v[index++] = p2;
|
|
v[index++] = p4;
|
|
|
|
v[index++] = p1;
|
|
v[index++] = p4;
|
|
v[index++] = p3;
|
|
|
|
v[index++] = p3;
|
|
v[index++] = p4;
|
|
v[index++] = p5;
|
|
}
|
|
|
|
return v;
|
|
}
|
|
}
|
|
|
|
#if UNITY_EDITOR
|
|
[CustomPropertyDrawer(typeof(Sphere))]
|
|
public class SphereDrawer : PropertyDrawer
|
|
{
|
|
static bool s_foldoutEnabled = true;
|
|
|
|
const bool k_ToggleOnLabelClick = true;
|
|
|
|
static readonly GUIContent k_SubdivisionsContent = new GUIContent("Subdivisions", L10n.Tr("Number of time each triangle of the basic sphere is divided."));
|
|
static readonly GUIContent k_SmoothContent = new GUIContent("Smooth", L10n.Tr("Whether to smooth the edges of the sphere."));
|
|
|
|
public override void OnGUI(Rect position, SerializedProperty property, GUIContent label)
|
|
{
|
|
EditorGUI.BeginProperty(position, label, property);
|
|
|
|
s_foldoutEnabled = EditorGUI.Foldout(position, s_foldoutEnabled, "Sphere Settings", k_ToggleOnLabelClick);
|
|
|
|
EditorGUI.indentLevel++;
|
|
|
|
if(s_foldoutEnabled)
|
|
{
|
|
EditorGUILayout.PropertyField(property.FindPropertyRelative("m_Subdivisions"), k_SubdivisionsContent);
|
|
EditorGUILayout.PropertyField(property.FindPropertyRelative("m_Smooth"), k_SmoothContent);
|
|
}
|
|
|
|
EditorGUI.indentLevel--;
|
|
EditorGUI.EndProperty();
|
|
}
|
|
}
|
|
#endif
|
|
}
|