using System.Collections.Generic; using UnityEditor; namespace UnityEngine.ProBuilder.Shapes { [Shape("Pipe")] public class Pipe : Shape { [Min(0.01f)] [SerializeField] float m_Thickness = .25f; [Range(3, 64)] [SerializeField] int m_NumberOfSides = 6; [Range(0, 31)] [SerializeField] int m_HeightCuts = 0; [SerializeField] bool m_Smooth = true; public override void CopyShape(Shape shape) { if(shape is Pipe) { Pipe pipe = (Pipe) shape; m_Thickness = pipe.m_Thickness; m_NumberOfSides = pipe.m_NumberOfSides; m_HeightCuts = pipe.m_HeightCuts; m_Smooth = pipe.m_Smooth; } } public override Bounds UpdateBounds(ProBuilderMesh mesh, Vector3 size, Quaternion rotation, Bounds bounds) { bounds.size = size; return bounds; } public override Bounds RebuildMesh(ProBuilderMesh mesh, Vector3 size, Quaternion rotation) { var upDir = Vector3.Scale(rotation * Vector3.up, size) ; var rightDir = Vector3.Scale(rotation * Vector3.right, size) ; var forwardDir = Vector3.Scale(rotation * Vector3.forward, size) ; var height = upDir.magnitude; var xRadius = rightDir.magnitude / 2f; var zRadius = forwardDir.magnitude / 2f; // template is outer ring - radius refers to outer ring always Vector2[] templateOut = new Vector2[m_NumberOfSides]; Vector2[] templateIn = new Vector2[m_NumberOfSides]; Vector2 tangent; for (int i = 0; i < m_NumberOfSides; i++) { float angle = i * ( 360f / m_NumberOfSides ); templateOut[i] = Math.PointInEllipseCircumference(xRadius, zRadius, angle, Vector2.zero, out tangent); Vector2 tangentOrtho = new Vector2(-tangent.y, tangent.x); templateIn[i] = templateOut[i] + (m_Thickness * tangentOrtho); } List v = new List(); var baseY = height / 2f; // build out sides Vector2 tmp, tmp2, tmp3, tmp4; var heightSegments = m_HeightCuts + 1; for (int i = 0; i < heightSegments; i++) { // height subdivisions float y = i * (height / heightSegments) - baseY; float y2 = (i + 1) * (height / heightSegments) - baseY; for (int n = 0; n < m_NumberOfSides; n++) { tmp = templateOut[n]; tmp2 = n < (m_NumberOfSides - 1) ? templateOut[n + 1] : templateOut[0]; // outside quads Vector3[] qvo = new Vector3[4] { new Vector3(tmp2.x, y, tmp2.y), new Vector3(tmp.x, y, tmp.y), new Vector3(tmp2.x, y2, tmp2.y), new Vector3(tmp.x, y2, tmp.y) }; // inside quad tmp = templateIn[n]; tmp2 = n < (m_NumberOfSides - 1) ? templateIn[n + 1] : templateIn[0]; Vector3[] qvi = new Vector3[4] { new Vector3(tmp.x, y, tmp.y), new Vector3(tmp2.x, y, tmp2.y), new Vector3(tmp.x, y2, tmp.y), new Vector3(tmp2.x, y2, tmp2.y) }; v.AddRange(qvo); v.AddRange(qvi); } } // build top and bottom for (int i = 0; i < m_NumberOfSides; i++) { tmp = templateOut[i]; tmp2 = (i < m_NumberOfSides - 1) ? templateOut[i + 1] : templateOut[0]; tmp3 = templateIn[i]; tmp4 = (i < m_NumberOfSides - 1) ? templateIn[i + 1] : templateIn[0]; // top Vector3[] tpt = new Vector3[4] { new Vector3(tmp2.x, height-baseY, tmp2.y), new Vector3(tmp.x, height-baseY, tmp.y), new Vector3(tmp4.x, height-baseY, tmp4.y), new Vector3(tmp3.x, height-baseY, tmp3.y) }; // bottom Vector3[] tpb = new Vector3[4] { new Vector3(tmp.x, -baseY, tmp.y), new Vector3(tmp2.x, -baseY, tmp2.y), new Vector3(tmp3.x, -baseY, tmp3.y), new Vector3(tmp4.x, -baseY, tmp4.y), }; v.AddRange(tpb); v.AddRange(tpt); } for(int i = 0; i < v.Count; i++) v[i] = rotation * v[i]; mesh.GeometryWithPoints(v.ToArray()); //Smooth internal and external faces if(m_Smooth) { int smoothCount = 2 * heightSegments * m_NumberOfSides; for(int i = 0; i < smoothCount; i++) mesh.facesInternal[i].smoothingGroup = 1; } return UpdateBounds(mesh, size, rotation, new Bounds()); } } #if UNITY_EDITOR [CustomPropertyDrawer(typeof(Pipe))] public class PipeDrawer : PropertyDrawer { static bool s_foldoutEnabled = true; const bool k_ToggleOnLabelClick = true; static readonly GUIContent k_ThicknessContent = new GUIContent("Thickness", L10n.Tr("Thickness of the pipe borders. Larger value creates a smaller hole.")); static readonly GUIContent k_SidesContent = new GUIContent("Sides Count", L10n.Tr("Number of sides of the pipe.")); static readonly GUIContent k_HeightCutsContent = new GUIContent("Height Cuts", L10n.Tr("Number of divisions in the pipe height.")); static readonly GUIContent k_SmoothContent = new GUIContent("Smooth", L10n.Tr("Whether to smooth the edges of the pipe.")); public override void OnGUI(Rect position, SerializedProperty property, GUIContent label) { EditorGUI.BeginProperty(position, label, property); s_foldoutEnabled = EditorGUI.Foldout(position, s_foldoutEnabled, "Pipe Settings", k_ToggleOnLabelClick); EditorGUI.indentLevel++; if(s_foldoutEnabled) { EditorGUILayout.PropertyField(property.FindPropertyRelative("m_Thickness"), k_ThicknessContent); EditorGUILayout.PropertyField(property.FindPropertyRelative("m_NumberOfSides"), k_SidesContent); EditorGUILayout.PropertyField(property.FindPropertyRelative("m_HeightCuts"), k_HeightCutsContent); EditorGUILayout.PropertyField(property.FindPropertyRelative("m_Smooth"), k_SmoothContent); } EditorGUI.indentLevel--; EditorGUI.EndProperty(); } } #endif }