using System.Collections.Generic; using UnityEditor; namespace UnityEngine.ProBuilder.Shapes { [Shape("Cone")] public class Cone : Shape { [Range(3,64)] [SerializeField] internal int m_NumberOfSides = 6; float m_Radius = 0; [SerializeField] bool m_Smooth = true; public override void CopyShape(Shape shape) { if(shape is Cone) { Cone cone = (Cone) shape; m_NumberOfSides = cone.m_NumberOfSides; m_Radius = cone.m_Radius; m_Smooth = cone.m_Smooth; } } public override Bounds UpdateBounds(ProBuilderMesh mesh, Vector3 size, Quaternion rotation, Bounds bounds) { var upLocalAxis = rotation * Vector3.up; upLocalAxis = Math.Abs(upLocalAxis); Vector3 boxSize = mesh.mesh.bounds.size; boxSize.x = Mathf.Lerp(m_Radius * 2f, boxSize.x, upLocalAxis.x); boxSize.y = Mathf.Lerp(m_Radius * 2f, boxSize.y, upLocalAxis.y); boxSize.z = Mathf.Lerp(m_Radius * 2f, boxSize.z, upLocalAxis.z); bounds.size = boxSize; return bounds; } public override Bounds RebuildMesh(ProBuilderMesh mesh, Vector3 size, Quaternion rotation) { var meshSize = Math.Abs(size); m_Radius = System.Math.Min(meshSize.x, meshSize.z); var height = meshSize.y; var subdivAxis = m_NumberOfSides; // template is outer ring - radius refers to outer ring always Vector3[] template = new Vector3[subdivAxis]; for (int i = 0; i < subdivAxis; i++) { Vector2 ct = Math.PointInCircumference(m_Radius, i * (360f / subdivAxis), Vector2.zero); template[i] = new Vector3(ct.x, -height / 2f, ct.y); } List v = new List(); List f = new List(); // build sides for (int i = 0; i < subdivAxis; i++) { // side face v.Add(template[i]); v.Add((i < subdivAxis - 1) ? template[i + 1] : template[0]); v.Add(Vector3.up * height / 2f); // bottom face v.Add(template[i]); v.Add((i < subdivAxis - 1) ? template[i + 1] : template[0]); v.Add(Vector3.down * height / 2f); } List sideFaces = new List(); for (int i = 0; i < subdivAxis * 6; i += 6) { Face face = new Face(new int[3] { i + 2, i + 1, i + 0 }); face.smoothingGroup = m_Smooth ? 1 : 0; f.Add(face); sideFaces.Add(face); f.Add(new Face(new int[3] { i + 3, i + 4, i + 5 })); } var sizeSigns = Math.Sign(size); for(int i = 0; i < v.Count; i++) v[i] = Vector3.Scale(rotation * v[i], sizeSigns); var sizeSign = Mathf.Sign(size.x) * Mathf.Sign(size.y) * Mathf.Sign(size.z); if(sizeSign < 0) { foreach(var face in f) face.Reverse(); } mesh.RebuildWithPositionsAndFaces(v, f); mesh.unwrapParameters = new UnwrapParameters() { packMargin = 30f }; // Set the UVs manually for the side faces, so that they are uniform. // Calculate the UVs for the first face, then set the others to the same. var firstFace = sideFaces[0]; var uv = firstFace.uv; uv.anchor = AutoUnwrapSettings.Anchor.LowerLeft; firstFace.uv = uv; firstFace.manualUV = true; // Always use up vector for projection of side faces. // Otherwise the lines in the PB texture end up crooked. UvUnwrapping.Unwrap(mesh, firstFace, projection: Vector3.up); for (int i = 1; i < sideFaces.Count; i++) { var sideFace = sideFaces[i]; sideFace.manualUV = true; UvUnwrapping.CopyUVs(mesh, firstFace, sideFace); } mesh.RefreshUV(sideFaces); return UpdateBounds(mesh, size, rotation, new Bounds()); } } #if UNITY_EDITOR [CustomPropertyDrawer(typeof(Cone))] public class ConeDrawer : 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 cone.")); static readonly GUIContent k_SmoothContent = new GUIContent("Smooth", L10n.Tr("Whether to smooth the edges of the arch.")); public override void OnGUI(Rect position, SerializedProperty property, GUIContent label) { EditorGUI.BeginProperty(position, label, property); s_foldoutEnabled = EditorGUI.Foldout(position, s_foldoutEnabled, "Cone Settings", k_ToggleOnLabelClick); EditorGUI.indentLevel++; if(s_foldoutEnabled) { EditorGUILayout.PropertyField(property.FindPropertyRelative("m_NumberOfSides"), k_SidesContent); EditorGUILayout.PropertyField(property.FindPropertyRelative("m_Smooth"), k_SmoothContent); } EditorGUI.indentLevel--; EditorGUI.EndProperty(); } } #endif }