You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

206 lines
7.6 KiB
C#

3 years ago
using UnityEditor;
namespace UnityEngine.ProBuilder.Shapes
{
[Shape("Cylinder")]
public class Cylinder : Shape
{
[Range(4,64)]
[SerializeField]
int m_AxisDivisions = 6;
[Min(0)]
[SerializeField]
int m_HeightCuts = 0;
[SerializeField]
bool m_Smooth = true;
public override void CopyShape(Shape shape)
{
if(shape is Cylinder)
{
m_AxisDivisions = ((Cylinder)shape).m_AxisDivisions;
m_HeightCuts = ((Cylinder)shape).m_HeightCuts;
m_Smooth = ((Cylinder)shape).m_Smooth;
}
}
public override Bounds UpdateBounds(ProBuilderMesh mesh, Vector3 size, Quaternion rotation, Bounds bounds)
{
var upLocalAxis = rotation * Vector3.up;
upLocalAxis = Math.Abs(upLocalAxis);
bounds = mesh.mesh.bounds;
Vector3 boxSize = bounds.size;
var maxAxis = Mathf.Max(Mathf.Max(
(1f - upLocalAxis.x)*boxSize.x,
(1f - upLocalAxis.y)*boxSize.y),
(1f - upLocalAxis.z)*boxSize.z);
boxSize.x = Mathf.Lerp(maxAxis, boxSize.x, upLocalAxis.x);
boxSize.y = Mathf.Lerp(maxAxis, boxSize.y, upLocalAxis.y);
boxSize.z = Mathf.Lerp(maxAxis, boxSize.z, upLocalAxis.z);
bounds.size = boxSize;
return bounds;
}
public override Bounds RebuildMesh(ProBuilderMesh mesh, Vector3 size, Quaternion rotation)
{
var meshSize = Math.Abs(size);
var radius = Mathf.Min(meshSize.x, meshSize.z) * .5f;
var height = meshSize.y;
if (m_AxisDivisions % 2 != 0)
m_AxisDivisions++;
float stepAngle = 360f / m_AxisDivisions;
float heightStep = height / (m_HeightCuts + 1);
Vector3[] circle = new Vector3[m_AxisDivisions];
// get a circle
for (int i = 0; i < m_AxisDivisions; i++)
{
float angle0 = stepAngle * i * Mathf.Deg2Rad;
float x = Mathf.Cos(angle0) * radius;
float z = Mathf.Sin(angle0) * radius;
circle[i] = new Vector3(x, 0f, z);
}
// add two because end caps
Vector3[] vertices = new Vector3[(m_AxisDivisions * (m_HeightCuts + 1) * 4) + (m_AxisDivisions * 6)];
Face[] faces = new Face[m_AxisDivisions * (m_HeightCuts + 1) + (m_AxisDivisions * 2)];
// build vertex array
int it = 0;
// +1 to account for 0 height cuts
for (int i = 0; i < m_HeightCuts + 1; i++)
{
float Y = i * heightStep - height * .5f;
float Y2 = (i + 1) * heightStep - height * .5f;
for (int n = 0; n < m_AxisDivisions; n++)
{
vertices[it + 0] = new Vector3(circle[n + 0].x, Y, circle[n + 0].z);
vertices[it + 1] = new Vector3(circle[n + 0].x, Y2, circle[n + 0].z);
if (n != m_AxisDivisions - 1)
{
vertices[it + 2] = new Vector3(circle[n + 1].x, Y, circle[n + 1].z);
vertices[it + 3] = new Vector3(circle[n + 1].x, Y2, circle[n + 1].z);
}
else
{
vertices[it + 2] = new Vector3(circle[0].x, Y, circle[0].z);
vertices[it + 3] = new Vector3(circle[0].x, Y2, circle[0].z);
}
it += 4;
}
}
// wind side faces
int f = 0;
for (int i = 0; i < m_HeightCuts + 1; i++)
{
for (int n = 0; n < m_AxisDivisions * 4; n += 4)
{
int index = (i * (m_AxisDivisions * 4)) + n;
int zero = index;
int one = index + 1;
int two = index + 2;
int three = index + 3;
faces[f++] = new Face(
new int[6] { zero, one, two, one, three, two },
0,
AutoUnwrapSettings.tile,
m_Smooth ? 1 : -1,
-1,
-1,
false);
}
}
// construct caps separately, cause they aren't wound the same way
int ind = (m_AxisDivisions * (m_HeightCuts + 1) * 4);
int f_ind = m_AxisDivisions * (m_HeightCuts + 1);
for (int n = 0; n < m_AxisDivisions; n++)
{
// bottom faces
var bottomCapHeight = -height * .5f;
vertices[ind + 0] = new Vector3(circle[n].x, bottomCapHeight, circle[n].z);
vertices[ind + 1] = new Vector3(0f, bottomCapHeight, 0f);
if (n != m_AxisDivisions - 1)
vertices[ind + 2] = new Vector3(circle[n + 1].x, bottomCapHeight, circle[n + 1].z);
else
vertices[ind + 2] = new Vector3(circle[000].x, bottomCapHeight, circle[000].z);
faces[f_ind + n] = new Face(new int[3] { ind + 2, ind + 1, ind + 0 });
ind += 3;
// top faces
var topCapHeight = height * .5f;
vertices[ind + 0] = new Vector3(circle[n].x, topCapHeight, circle[n].z);
vertices[ind + 1] = new Vector3(0f, topCapHeight, 0f);
if (n != m_AxisDivisions - 1)
vertices[ind + 2] = new Vector3(circle[n + 1].x, topCapHeight, circle[n + 1].z);
else
vertices[ind + 2] = new Vector3(circle[000].x, topCapHeight, circle[000].z);
faces[f_ind + (n + m_AxisDivisions)] = new Face(new int[3] { ind + 0, ind + 1, ind + 2 });
ind += 3;
}
for(int i = 0; i < vertices.Length; i++)
vertices[i] = rotation * vertices[i];
mesh.RebuildWithPositionsAndFaces(vertices, faces);
return UpdateBounds(mesh, size, rotation, new Bounds());
}
}
#if UNITY_EDITOR
[CustomPropertyDrawer(typeof(Cylinder))]
public class CylinderDrawer : PropertyDrawer
{
static bool s_foldoutEnabled = true;
const bool k_ToggleOnLabelClick = true;
static readonly GUIContent k_SidesContent = new GUIContent("Sides Count", L10n.Tr("Number of sides of the cylinder."));
static readonly GUIContent k_HeightCutsContent = new GUIContent("Height Cuts", L10n.Tr("Number of divisions in the cylinder height."));
static readonly GUIContent k_SmoothContent = new GUIContent("Smooth", L10n.Tr("Whether to smooth the edges of the cylinder."));
public override void OnGUI(Rect position, SerializedProperty property, GUIContent label)
{
EditorGUI.BeginProperty(position, label, property);
s_foldoutEnabled = EditorGUI.Foldout(position, s_foldoutEnabled, "Cylinder Settings", k_ToggleOnLabelClick);
EditorGUI.indentLevel++;
if(s_foldoutEnabled)
{
EditorGUILayout.PropertyField(property.FindPropertyRelative("m_AxisDivisions"), k_SidesContent);
EditorGUILayout.PropertyField(property.FindPropertyRelative("m_HeightCuts"), k_HeightCutsContent);
EditorGUILayout.PropertyField(property.FindPropertyRelative("m_Smooth"), k_SmoothContent);
}
EditorGUI.indentLevel--;
EditorGUI.EndProperty();
}
}
#endif
}