public static Mesh CreateWirePyramid(Vector3 baseCenter, float baseWidth, float baseDepth, float height, Color color)
        {
            baseWidth = Mathf.Max(Mathf.Abs(baseWidth), 1e-4f);
            baseDepth = Mathf.Max(Mathf.Abs(baseDepth), 1e-4f);
            height    = Mathf.Max(Mathf.Abs(height), 1e-4f);

            float halfBaseWidth = baseWidth * 0.5f;
            float halfBaseDepth = baseDepth * 0.5f;

            Vector3[] positions = new Vector3[]
            {
                baseCenter - Vector3.right * halfBaseWidth - Vector3.forward * halfBaseDepth,
                baseCenter + Vector3.right * halfBaseWidth - Vector3.forward * halfBaseDepth,
                baseCenter + Vector3.right * halfBaseWidth + Vector3.forward * halfBaseDepth,
                baseCenter - Vector3.right * halfBaseWidth + Vector3.forward * halfBaseDepth,
                baseCenter + Vector3.up * height
            };

            int[] indices = new int[] { 0, 1, 1, 2, 2, 3, 3, 0, 0, 4, 1, 4, 2, 4, 3, 4 };

            Mesh mesh = new Mesh();

            mesh.vertices = positions;
            mesh.colors   = ColorEx.GetFilledColorArray(positions.Length, color);
            mesh.SetIndices(indices, MeshTopology.Lines, 0);
            mesh.UploadMeshData(false);

            return(mesh);
        }
示例#2
0
        public static Mesh CreateWireCircleXY(float circleRadius, int numBorderPoints, Color color)
        {
            if (circleRadius < 1e-4f || numBorderPoints < 4)
            {
                return(null);
            }

            Vector3[] positions = new Vector3[numBorderPoints];
            int[]     indices   = new int[numBorderPoints];

            float angleStep = 360.0f / (numBorderPoints - 1);

            for (int ptIndex = 0; ptIndex < numBorderPoints; ++ptIndex)
            {
                float angle = angleStep * ptIndex * Mathf.Deg2Rad;
                positions[ptIndex] = new Vector3(Mathf.Sin(angle) * circleRadius, Mathf.Cos(angle) * circleRadius, 0.0f);
                indices[ptIndex]   = ptIndex;
            }

            Mesh mesh = new Mesh();

            mesh.vertices = positions;
            mesh.colors   = ColorEx.GetFilledColorArray(numBorderPoints, color);
            mesh.SetIndices(indices, MeshTopology.LineStrip, 0);
            mesh.UploadMeshData(false);

            return(mesh);
        }
示例#3
0
        public void SetAxisColor(int axisIndex, Color color)
        {
            GetSglSliderLookAndFeel(axisIndex, AxisSign.Positive).Color = color;
            GetSglSliderLookAndFeel(axisIndex, AxisSign.Positive).CapLookAndFeel.Color = color;
            GetSglSliderLookAndFeel(axisIndex, AxisSign.Negative).Color = color;
            GetSglSliderLookAndFeel(axisIndex, AxisSign.Negative).CapLookAndFeel.Color = color;

            GizmoPlaneSlider3DLookAndFeel dblLookAndFeel = null;

            if (axisIndex == 0)
            {
                dblLookAndFeel = GetDblSliderLookAndFeel(PlaneId.YZ);
                _scaleGuideLookAndFeel.XAxisColor = color;
            }
            else
            if (axisIndex == 1)
            {
                dblLookAndFeel = GetDblSliderLookAndFeel(PlaneId.ZX);
                _scaleGuideLookAndFeel.YAxisColor = color;
            }
            else
            if (axisIndex == 2)
            {
                dblLookAndFeel = GetDblSliderLookAndFeel(PlaneId.XY);
                _scaleGuideLookAndFeel.ZAxisColor = color;
            }

            dblLookAndFeel.Color       = ColorEx.KeepAllButAlpha(color, dblLookAndFeel.Color.a);
            dblLookAndFeel.BorderColor = color;
        }
        public override void Render(Camera camera)
        {
            var sgLookAndFeel = _sceneGizmo.LookAndFeel;
            RTSceneGizmoCamera sceneGizmoCamera = _sceneGizmo.SceneGizmoCamera;

            _cap.Render(camera);

            if (_axisDesc.IsPositive)
            {
                GizmoLabelMaterial labelMaterial = GizmoLabelMaterial.Get;
                labelMaterial.SetZWriteEnabled(false);
                labelMaterial.SetZTestLessEqual();
                labelMaterial.SetColor(ColorEx.KeepAllButAlpha(sgLookAndFeel.AxesLabelTint, _color.Value.a));
                labelMaterial.SetTexture(_labelTexture);
                labelMaterial.SetPass(0);

                Vector3 gizmoAxis  = _sceneGizmo.Gizmo.Transform.GetAxis3D(_axisDesc);
                Vector3 labelScale = Vector3Ex.FromValue(sgLookAndFeel.GetAxesLabelWorldSize(sceneGizmoCamera.Camera, _cap.Position));
                Vector3 labelPos   = _cap.Position + gizmoAxis * (labelScale.x * 0.5f);

                Vector2 labelScreenPos   = sceneGizmoCamera.Camera.WorldToScreenPoint(labelPos);
                Vector2 midAxisScreenPos = sceneGizmoCamera.Camera.WorldToScreenPoint(_sceneGizmo.SceneGizmoCamera.LookAtPoint);
                Vector2 labelScreenDir   = (labelScreenPos - midAxisScreenPos).normalized;

                float absDotCamLook = Mathf.Abs(Vector3Ex.AbsDot(sceneGizmoCamera.Look, gizmoAxis));
                labelScreenPos = labelScreenPos + Vector2.Scale(labelScreenDir, Vector2Ex.FromValue(SceneGizmoLookAndFeel.AxisLabelScreenSize)) * absDotCamLook;
                labelPos       = sceneGizmoCamera.Camera.ScreenToWorldPoint(new Vector3(labelScreenPos.x, labelScreenPos.y, (labelPos - sceneGizmoCamera.WorldPosition).magnitude));

                Quaternion labelRotation     = Quaternion.LookRotation(sceneGizmoCamera.Look, sceneGizmoCamera.Up);
                Matrix4x4  labelTransformMtx = Matrix4x4.TRS(labelPos, labelRotation, labelScale);

                Graphics.DrawMeshNow(MeshPool.Get.UnitQuadXY, labelTransformMtx);
            }
        }
示例#5
0
        public static Mesh CreateCoordSystemAxesLines(float axisLength, Color color)
        {
            if (axisLength < 1e-4f)
            {
                return(null);
            }

            Vector3[] positions = new Vector3[]
            {
                Vector3.zero,
                Vector3.right *axisLength,
                Vector3.up *axisLength,
                Vector3.forward *axisLength
            };

            int[] indices = new int[]
            {
                0, 1,
                0, 2,
                0, 3
            };

            Mesh mesh = new Mesh();

            mesh.vertices = positions;
            mesh.colors   = ColorEx.GetFilledColorArray(4, color);
            mesh.SetIndices(indices, MeshTopology.Lines, 0);
            mesh.UploadMeshData(false);

            return(mesh);
        }
示例#6
0
        public static Mesh CreateWireTriangularPrism(Vector3 baseCenter, float baseWidth, float baseDepth, float topWidth, float topDepth, float height, Color color)
        {
            baseWidth = Mathf.Max(Mathf.Abs(baseWidth), 1e-4f);
            baseDepth = Mathf.Max(Mathf.Abs(baseDepth), 1e-4f);
            topWidth  = Mathf.Max(Mathf.Abs(topWidth), 1e-4f);
            topDepth  = Mathf.Max(Mathf.Abs(topDepth), 1e-4f);
            height    = Mathf.Max(Mathf.Abs(height), 1e-4f);

            List <Vector3> cornerPoints  = PrismMath.CalcTriangPrismCornerPoints(baseCenter, baseWidth, baseDepth, topWidth, topDepth, height, Quaternion.identity);
            Vector3        baseLeftPt    = cornerPoints[(int)TriangularPrismCorner.BaseLeft];
            Vector3        baseRightPt   = cornerPoints[(int)TriangularPrismCorner.BaseRight];
            Vector3        baseForwardPt = cornerPoints[(int)TriangularPrismCorner.BaseForward];

            Vector3 topLeftPt    = cornerPoints[(int)TriangularPrismCorner.TopLeft];
            Vector3 topRightPt   = cornerPoints[(int)TriangularPrismCorner.TopRight];
            Vector3 topForwardPt = cornerPoints[(int)TriangularPrismCorner.TopForward];

            Vector3[] positions = new Vector3[]
            {
                baseLeftPt, baseForwardPt, baseRightPt,
                topLeftPt, topForwardPt, topRightPt
            };

            int[] indices = new int[] { 0, 1, 1, 2, 2, 0, 3, 4, 4, 5, 5, 3, 0, 3, 1, 4, 2, 5 };

            Mesh mesh = new Mesh();

            mesh.vertices = positions;
            mesh.colors   = ColorEx.GetFilledColorArray(positions.Length, color);
            mesh.SetIndices(indices, MeshTopology.Lines, 0);
            mesh.UploadMeshData(false);

            return(mesh);
        }
        public static Mesh CreateWireQuadXY(Vector3 center, Vector2 size, Color color)
        {
            Vector2 halfSize = size * 0.5f;

            Vector3[] positions = new Vector3[]
            {
                center - Vector3.right * halfSize.x - Vector3.up * halfSize.y,
                center - Vector3.right * halfSize.x + Vector3.up * halfSize.y,
                center + Vector3.right * halfSize.x + Vector3.up * halfSize.y,
                center + Vector3.right * halfSize.x - Vector3.up * halfSize.y
            };

            int[] indices = new int[]
            {
                0, 1, 1, 2, 2, 3, 3, 0
            };

            Mesh mesh = new Mesh();

            mesh.vertices = positions;
            mesh.colors   = ColorEx.GetFilledColorArray(4, color);
            mesh.SetIndices(indices, MeshTopology.Lines, 0);
            mesh.UploadMeshData(false);

            return(mesh);
        }
示例#8
0
        public UniversalGizmoLookAndFeel2D()
        {
            for (int axisIndex = 0; axisIndex < _mvSglSliderLookAndFeel.Length; ++axisIndex)
            {
                _mvSglSliderLookAndFeel[axisIndex] = new GizmoLineSlider2DLookAndFeel();
            }

            SetMvAxisColor(0, RTSystemValues.XAxisColor);
            SetMvAxisColor(1, RTSystemValues.YAxisColor);
            SetMvAxisBorderColor(0, RTSystemValues.XAxisColor);
            SetMvAxisBorderColor(1, RTSystemValues.YAxisColor);
            SetMvSliderHoveredFillColor(RTSystemValues.HoveredAxisColor);
            SetMvSliderHoveredBorderColor(RTSystemValues.HoveredAxisColor);
            SetMvSliderCapType(GizmoCap2DType.Arrow);
            SetMvSliderCapFillMode(GizmoFillMode2D.Filled);

            SetMvSliderFillMode(GizmoFillMode2D.Filled);
            SetMvSliderVisible(0, AxisSign.Positive, true);
            SetMvSliderVisible(1, AxisSign.Positive, true);
            SetMvSliderCapVisible(0, AxisSign.Positive, true);
            SetMvSliderCapVisible(1, AxisSign.Positive, true);

            SetMvDblSliderFillMode(GizmoFillMode2D.Border);
            SetMvDblSliderColor(ColorEx.KeepAllButAlpha(Color.white, RTSystemValues.AxisAlpha));
            SetMvDblSliderBorderColor(Color.white);
            SetMvDblSliderHoveredColor(ColorEx.KeepAllButAlpha(RTSystemValues.HoveredAxisColor, RTSystemValues.AxisAlpha));
            SetMvDblSliderHoveredBorderColor(RTSystemValues.HoveredAxisColor);
        }
示例#9
0
 public void SetDblSliderFillAlpha(float alpha)
 {
     alpha = Mathf.Clamp(alpha, 0.0f, 1.0f);
     foreach (var lookAndFeel in _dblSlidersLookAndFeel)
     {
         lookAndFeel.Color        = ColorEx.KeepAllButAlpha(lookAndFeel.Color, alpha);
         lookAndFeel.HoveredColor = ColorEx.KeepAllButAlpha(lookAndFeel.HoveredColor, alpha);
     }
 }
示例#10
0
        public static Mesh CreateLine(Vector3 startPoint, Vector3 endPoint, Color color)
        {
            Mesh mesh = new Mesh();

            mesh.vertices = new Vector3[] { startPoint, endPoint };
            mesh.colors   = ColorEx.GetFilledColorArray(2, color);
            mesh.SetIndices(new int[] { 0, 1 }, MeshTopology.Lines, 0);
            mesh.UploadMeshData(false);

            return(mesh);
        }
示例#11
0
        /// <summary>
        /// Can be used to draw the tree in the scene for debugging purposes.
        /// </summary>
        public void DebugDraw()
        {
            // Setup the rendering material
            Material material = MaterialPool.Get.GizmoSolidHandle;

            material.SetInt("_IsLit", 0);
            material.SetColor(ColorEx.KeepAllButAlpha(Color.green, 0.3f));
            material.SetPass(0);

            // Draw the entire tree
            _root.DebugDraw();
        }
示例#12
0
        public void OnGUI()
        {
            var    sgLookAndFeel = _sceneGizmo.LookAndFeel;
            Camera gizmoCamera   = _sceneGizmo.SceneGizmoCamera.Camera;

            if (sgLookAndFeel.IsCamPrjSwitchLabelVisible)
            {
                if (_sceneGizmo.SceneGizmoCamera.SceneCamera != RTFocusCamera.Get.TargetCamera ||
                    !RTFocusCamera.Get.IsDoingProjectionSwitch)
                {
                    Texture2D labelTexture = gizmoCamera.orthographic ? sgLookAndFeel.CamOrthoModeLabelTexture : sgLookAndFeel.CamPerspModeLabelTexture;
                    GUIEx.PushColor(sgLookAndFeel.CamPrjSwitchLabelTint);

                    Rect drawRect = RectEx.FromTexture2D(labelTexture).PlaceBelowCenterHrz(gizmoCamera.pixelRect).InvertScreenY();
                    drawRect.center = new Vector2(drawRect.center.x, Screen.height - 1 - _labelQuad.Center.y);

                    GUI.DrawTexture(drawRect, labelTexture);
                    GUIEx.PopColor();
                }
                else
                {
                    Texture2D destLabelTexture   = sgLookAndFeel.CamOrthoModeLabelTexture;
                    Texture2D sourceLabelTexture = sgLookAndFeel.CamPerspModeLabelTexture;
                    if (RTFocusCamera.Get.PrjSwitchTransitionType == CameraPrjSwitchTransition.Type.ToPerspective)
                    {
                        destLabelTexture   = sgLookAndFeel.CamPerspModeLabelTexture;
                        sourceLabelTexture = sgLookAndFeel.CamOrthoModeLabelTexture;
                    }

                    AnimationCurve srcAnimCurve  = AnimationCurve.EaseInOut(0.0f, sgLookAndFeel.CamPrjSwitchLabelTint.a, 1.0f, 0.0f);
                    AnimationCurve destAnimCurve = AnimationCurve.EaseInOut(0.0f, 0.0f, 1.0f, sgLookAndFeel.CamPrjSwitchLabelTint.a);

                    float destAlpha = destAnimCurve.Evaluate(RTFocusCamera.Get.PrjSwitchProgress);
                    float srcAlpha  = srcAnimCurve.Evaluate(RTFocusCamera.Get.PrjSwitchProgress);

                    GUIEx.PushColor(ColorEx.KeepAllButAlpha(sgLookAndFeel.CamPrjSwitchLabelTint, srcAlpha));
                    Rect drawRect = RectEx.FromTexture2D(sourceLabelTexture).PlaceBelowCenterHrz(gizmoCamera.pixelRect).InvertScreenY();
                    drawRect.center = new Vector2(drawRect.center.x, Screen.height - 1 - _labelQuad.Center.y);
                    GUI.DrawTexture(drawRect, sourceLabelTexture);
                    GUIEx.PopColor();

                    GUIEx.PushColor(ColorEx.KeepAllButAlpha(sgLookAndFeel.CamPrjSwitchLabelTint, destAlpha));
                    drawRect        = RectEx.FromTexture2D(destLabelTexture).PlaceBelowCenterHrz(gizmoCamera.pixelRect).InvertScreenY();
                    drawRect.center = new Vector2(drawRect.center.x, Screen.height - 1 - _labelQuad.Center.y);
                    GUI.DrawTexture(drawRect, destLabelTexture);
                    GUIEx.PopColor();
                }
            }
        }
示例#13
0
        public void SetHoveredColor(Color hoveredColor)
        {
            foreach (var lookAndFeel in _sglSlidersLookAndFeel)
            {
                lookAndFeel.HoveredColor = hoveredColor;
                lookAndFeel.CapLookAndFeel.HoveredColor = hoveredColor;
            }

            foreach (var lookAndFeel in _dblSlidersLookAndFeel)
            {
                lookAndFeel.HoveredBorderColor = hoveredColor;
                lookAndFeel.HoveredColor       = ColorEx.KeepAllButAlpha(hoveredColor, lookAndFeel.Color.a);
            }

            _midCapLookAndFeel.HoveredColor = hoveredColor;
        }
示例#14
0
        public static Mesh CreateCircleXY(float circleRadius, int numBorderPoints, Color color)
        {
            if (circleRadius < 1e-4f || numBorderPoints < 4)
            {
                return(null);
            }

            int numVerts     = numBorderPoints + 1;
            int numTriangles = numBorderPoints - 1;

            Vector3[] positions = new Vector3[numBorderPoints + 1];
            Vector3[] normals   = new Vector3[positions.Length];
            int[]     indices   = new int[numTriangles * 3];

            int indexPtr = 0;

            positions[0] = Vector3.zero;
            float angleStep = 360.0f / (numBorderPoints - 1);

            for (int ptIndex = 0; ptIndex < numBorderPoints; ++ptIndex)
            {
                float angle = angleStep * ptIndex * Mathf.Deg2Rad;

                int vertIndex = ptIndex + 1;
                positions[vertIndex] = new Vector3(Mathf.Sin(angle) * circleRadius, Mathf.Cos(angle) * circleRadius, 0.0f);
                normals[vertIndex]   = Vector3.forward;
            }

            for (int vertIndex = 1; vertIndex < numVerts - 1; ++vertIndex)
            {
                indices[indexPtr++] = 0;
                indices[indexPtr++] = vertIndex;
                indices[indexPtr++] = vertIndex + 1;
            }

            Mesh mesh = new Mesh();

            mesh.vertices = positions;
            mesh.colors   = ColorEx.GetFilledColorArray(positions.Length, color);
            mesh.normals  = normals;
            mesh.SetIndices(indices, MeshTopology.Triangles, 0);
            mesh.UploadMeshData(false);

            return(mesh);
        }
        private void UpdateColor()
        {
            var   sgLookAndFeel    = _sceneGizmo.LookAndFeel;
            Color lookAndFeelColor = sgLookAndFeel.GetAxisCapColor(_axisDesc.Index, _axisDesc.Sign);

            if (_cap.IsHovered)
            {
                lookAndFeelColor = sgLookAndFeel.HoveredColor;
            }
            ColorTransition.State ctState = _colorTransition.TransitionState;

            Vector3 axis      = _sceneGizmo.Gizmo.Transform.GetAxis3D(_axisDesc);
            float   alignment = Vector3Ex.AbsDot(axis, _sceneGizmo.SceneGizmoCamera.Look);

            if (alignment > SceneGizmoLookAndFeel.AxisCamAlignFadeOutThreshold)
            {
                if (ctState != ColorTransition.State.CompleteFadeOut &&
                    ctState != ColorTransition.State.FadingOut)
                {
                    _colorTransition.DurationInSeconds = SceneGizmoLookAndFeel.AxisCamAlignFadeOutDuration;
                    _colorTransition.FadeOutColor      = ColorEx.KeepAllButAlpha(lookAndFeelColor, SceneGizmoLookAndFeel.AxisCamAlignFadeOutAlpha);
                    _colorTransition.BeginFadeOut(true);
                }
            }
            else
            {
                if (ctState != ColorTransition.State.FadingIn &&
                    ctState != ColorTransition.State.CompleteFadeIn &&
                    ctState != ColorTransition.State.Ready)
                {
                    _colorTransition.DurationInSeconds = SceneGizmoLookAndFeel.AxisCamAlignFadeOutDuration;
                    _colorTransition.FadeInColor       = lookAndFeelColor;
                    _colorTransition.BeginFadeIn(true);
                }
                else
                {
                    _color.Value = lookAndFeelColor;
                }
            }

            _colorTransition.Update(Time.deltaTime);
            _cap.OverrideColor.IsActive = true;
            _cap.OverrideColor.Color    = _color.Value;
        }
        public static Mesh CreateQuadXZ(float width, float depth, Color color)
        {
            if (width < 1e-4f)
            {
                return(null);
            }
            if (depth < 1e-4f)
            {
                return(null);
            }

            float halfWidth = width * 0.5f;
            float halfDepth = depth * 0.5f;

            Vector3[] positions = new Vector3[]
            {
                -Vector3.right * halfWidth - Vector3.forward * halfDepth,
                -Vector3.right * halfWidth + Vector3.forward * halfDepth,
                Vector3.right *halfWidth + Vector3.forward * halfDepth,
                Vector3.right *halfWidth - Vector3.forward * halfDepth
            };

            Vector3[] normals = new Vector3[]
            {
                Vector3.up, Vector3.up, Vector3.up, Vector3.up
            };

            Vector2[] uvs = new Vector2[]
            {
                Vector2.zero, new Vector2(0.0f, 1.0f), new Vector2(1.0f, 1.0f), new Vector2(1.0f, 0.0f)
            };

            Mesh mesh = new Mesh();

            mesh.vertices = positions;
            mesh.normals  = normals;
            mesh.uv       = uvs;
            mesh.colors   = ColorEx.GetFilledColorArray(4, color);
            mesh.SetIndices(new int[] { 0, 1, 2, 2, 3, 0 }, MeshTopology.Triangles, 0);
            mesh.UploadMeshData(false);

            return(mesh);
        }
示例#17
0
        public static Mesh CreateTriangularPrism(Vector3 baseCenter, float baseWidth, float baseDepth, float topWidth, float topDepth, float height, Color color)
        {
            baseWidth = Mathf.Max(Mathf.Abs(baseWidth), 1e-4f);
            baseDepth = Mathf.Max(Mathf.Abs(baseDepth), 1e-4f);
            topWidth  = Mathf.Max(Mathf.Abs(topWidth), 1e-4f);
            topDepth  = Mathf.Max(Mathf.Abs(topDepth), 1e-4f);
            height    = Mathf.Max(Mathf.Abs(height), 1e-4f);

            List <Vector3> cornerPoints  = PrismMath.CalcTriangPrismCornerPoints(baseCenter, baseWidth, baseDepth, topWidth, topDepth, height, Quaternion.identity);
            Vector3        baseLeftPt    = cornerPoints[(int)TriangularPrismCorner.BaseLeft];
            Vector3        baseRightPt   = cornerPoints[(int)TriangularPrismCorner.BaseRight];
            Vector3        baseForwardPt = cornerPoints[(int)TriangularPrismCorner.BaseForward];

            Vector3 topLeftPt    = cornerPoints[(int)TriangularPrismCorner.TopLeft];
            Vector3 topRightPt   = cornerPoints[(int)TriangularPrismCorner.TopRight];
            Vector3 topForwardPt = cornerPoints[(int)TriangularPrismCorner.TopForward];

            Vector3[] positions = new Vector3[]
            {
                // Top face
                topLeftPt, topForwardPt, topRightPt,

                // Bottom face
                baseLeftPt, baseRightPt, baseForwardPt,

                // Back face
                baseLeftPt, topLeftPt, topRightPt, baseRightPt,

                // Left face
                baseLeftPt, baseForwardPt, topForwardPt, topLeftPt,

                // Right face
                baseRightPt, topRightPt, topForwardPt, baseForwardPt
            };

            int[] indices = new int[]
            {
                // Top face
                0, 1, 2,

                // Bottom face
                3, 4, 5,

                // Back face
                6, 7, 8,
                8, 9, 6,

                // Left face
                10, 11, 12,
                12, 13, 10,

                // Right face
                14, 15, 16,
                16, 17, 14
            };

            Vector3 leftFaceNormal  = Vector3.Cross((positions[11] - positions[10]), (positions[13] - positions[10])).normalized;
            Vector3 rightFaceNormal = Vector3.Cross((positions[15] - positions[14]), (positions[17] - positions[14])).normalized;

            Vector3[] normals = new Vector3[]
            {
                // Top face
                Vector3.up, Vector3.up, Vector3.up,

                // Bottom face
                -Vector3.up, -Vector3.up, -Vector3.up,

                // Back face
                -Vector3.forward, -Vector3.forward, -Vector3.forward, -Vector3.forward,

                // Left face
                leftFaceNormal, leftFaceNormal, leftFaceNormal, leftFaceNormal,

                // Right face
                rightFaceNormal, rightFaceNormal, rightFaceNormal, rightFaceNormal
            };

            Mesh mesh = new Mesh();

            mesh.vertices = positions;
            mesh.colors   = ColorEx.GetFilledColorArray(positions.Length, color);
            mesh.normals  = normals;
            mesh.SetIndices(indices, MeshTopology.Triangles, 0);
            mesh.UploadMeshData(false);

            return(mesh);
        }
        public static Mesh CreatePyramid(Vector3 baseCenter, float baseWidth, float baseDepth, float height, Color color)
        {
            baseWidth = Mathf.Max(Mathf.Abs(baseWidth), 1e-4f);
            baseDepth = Mathf.Max(Mathf.Abs(baseDepth), 1e-4f);
            height    = Mathf.Max(Mathf.Abs(height), 1e-4f);

            float halfBaseWidth = baseWidth * 0.5f;
            float halfBaseDepth = baseDepth * 0.5f;

            Vector3 tipPosition = baseCenter + Vector3.up * height;

            Vector3[] positions = new Vector3[]
            {
                // Front face
                tipPosition,
                baseCenter + Vector3.right * halfBaseWidth - Vector3.forward * halfBaseDepth,
                baseCenter - Vector3.right * halfBaseWidth - Vector3.forward * halfBaseDepth,

                // Right face
                tipPosition,
                baseCenter + Vector3.right * halfBaseWidth + Vector3.forward * halfBaseDepth,
                baseCenter + Vector3.right * halfBaseWidth - Vector3.forward * halfBaseDepth,

                // Back face
                tipPosition,
                baseCenter - Vector3.right * halfBaseWidth + Vector3.forward * halfBaseDepth,
                baseCenter + Vector3.right * halfBaseWidth + Vector3.forward * halfBaseDepth,

                // Left face
                tipPosition,
                baseCenter - Vector3.right * halfBaseWidth - Vector3.forward * halfBaseDepth,
                baseCenter - Vector3.right * halfBaseWidth + Vector3.forward * halfBaseDepth,

                // Bottom face
                baseCenter - Vector3.right * halfBaseWidth - Vector3.forward * halfBaseDepth,
                baseCenter + Vector3.right * halfBaseWidth - Vector3.forward * halfBaseDepth,
                baseCenter + Vector3.right * halfBaseWidth + Vector3.forward * halfBaseDepth,
                baseCenter - Vector3.right * halfBaseWidth + Vector3.forward * halfBaseDepth
            };

            int[] indices = new int[]
            {
                0, 1, 2,
                3, 4, 5,
                6, 7, 8,
                9, 10, 11,

                12, 13, 14,
                12, 14, 15
            };

            Vector3[] normals = new Vector3[positions.Length];
            for (int triangleIndex = 0; triangleIndex < 4; ++triangleIndex)
            {
                int index0 = indices[triangleIndex * 3];
                int index1 = indices[triangleIndex * 3 + 1];
                int index2 = indices[triangleIndex * 3 + 2];

                Vector3 edge0  = positions[index1] - positions[index0];
                Vector3 edge1  = positions[index2] - positions[index0];
                Vector3 normal = Vector3.Cross(edge0, edge1).normalized;

                normals[index0] = normal;
                normals[index1] = normal;
                normals[index2] = normal;
            }

            normals[normals.Length - 4] = -Vector3.up;
            normals[normals.Length - 3] = -Vector3.up;
            normals[normals.Length - 2] = -Vector3.up;
            normals[normals.Length - 1] = -Vector3.up;

            Mesh mesh = new Mesh();

            mesh.vertices = positions;
            mesh.colors   = ColorEx.GetFilledColorArray(positions.Length, color);
            mesh.normals  = normals;
            mesh.SetIndices(indices, MeshTopology.Triangles, 0);
            mesh.UploadMeshData(false);

            return(mesh);
        }
        public static Mesh CreateCylindricalTorus(Vector3 center, float coreRadius, float tubeHrzRadius, float tubeVertRadius, int numTubeSlices, Color color)
        {
            if (coreRadius < 1e-4f || tubeHrzRadius < 1e-4f || numTubeSlices < 3)
            {
                return(null);
            }

            int numVertsPerTubeSlice = 8;
            int numVerts             = numVertsPerTubeSlice * (numTubeSlices + 1);

            Vector3[] positions  = new Vector3[numVerts];
            Vector3[] normals    = new Vector3[numVerts];
            Vector2[] radiusDirs = new Vector2[numVerts];

            int   vertexPtr     = 0;
            float tubeAngleStep = 360.0f / (numTubeSlices - 1);

            for (int tubeSliceIndex = 0; tubeSliceIndex <= numTubeSlices; ++tubeSliceIndex)
            {
                float tubeAngle = tubeAngleStep * tubeSliceIndex * Mathf.Deg2Rad;
                float cosTube   = Mathf.Cos(tubeAngle);
                float sinTube   = Mathf.Sin(tubeAngle);

                Vector3 tubeSliceCenterDir = new Vector3(sinTube, 0.0f, cosTube).normalized;
                Vector3 tubeSliceCenter    = center + tubeSliceCenterDir * coreRadius;
                Vector2 radiusDir          = new Vector2(tubeSliceCenterDir.x, tubeSliceCenterDir.z);

                // Top
                radiusDirs[vertexPtr] = radiusDir;
                positions[vertexPtr]  = tubeSliceCenter + Vector3.up * tubeVertRadius - tubeSliceCenterDir * tubeHrzRadius;
                normals[vertexPtr++]  = Vector3.up;

                radiusDirs[vertexPtr] = radiusDir;
                positions[vertexPtr]  = tubeSliceCenter + Vector3.up * tubeVertRadius + tubeSliceCenterDir * tubeHrzRadius;
                normals[vertexPtr++]  = Vector3.up;

                // Back
                radiusDirs[vertexPtr] = radiusDir;
                positions[vertexPtr]  = tubeSliceCenter + Vector3.up * tubeVertRadius + tubeSliceCenterDir * tubeHrzRadius;
                normals[vertexPtr++]  = tubeSliceCenterDir;

                radiusDirs[vertexPtr] = radiusDir;
                positions[vertexPtr]  = tubeSliceCenter - Vector3.up * tubeVertRadius + tubeSliceCenterDir * tubeHrzRadius;
                normals[vertexPtr++]  = tubeSliceCenterDir;

                // Bottom
                radiusDirs[vertexPtr] = radiusDir;
                positions[vertexPtr]  = tubeSliceCenter - Vector3.up * tubeVertRadius + tubeSliceCenterDir * tubeHrzRadius;
                normals[vertexPtr++]  = -Vector3.up;

                radiusDirs[vertexPtr] = radiusDir;
                positions[vertexPtr]  = tubeSliceCenter - Vector3.up * tubeVertRadius - tubeSliceCenterDir * tubeHrzRadius;
                normals[vertexPtr++]  = -Vector3.up;

                // Front
                radiusDirs[vertexPtr] = radiusDir;
                positions[vertexPtr]  = tubeSliceCenter - Vector3.up * tubeVertRadius - tubeSliceCenterDir * tubeHrzRadius;
                normals[vertexPtr++]  = -tubeSliceCenterDir;

                radiusDirs[vertexPtr] = radiusDir;
                positions[vertexPtr]  = tubeSliceCenter + Vector3.up * tubeVertRadius - tubeSliceCenterDir * tubeHrzRadius;
                normals[vertexPtr++]  = -tubeSliceCenterDir;
            }

            int indexPtr   = 0;
            int numIndices = numTubeSlices * 24;

            int[] indices = new int[numIndices];
            for (int tubeSliceIndex = 0; tubeSliceIndex < numTubeSlices - 1; ++tubeSliceIndex)
            {
                int baseIndex = tubeSliceIndex * numVertsPerTubeSlice;

                // Top quad
                indices[indexPtr++] = baseIndex;
                indices[indexPtr++] = baseIndex + 1;
                indices[indexPtr++] = baseIndex + 1 + numVertsPerTubeSlice;

                indices[indexPtr++] = baseIndex;
                indices[indexPtr++] = baseIndex + 1 + numVertsPerTubeSlice;
                indices[indexPtr++] = baseIndex + numVertsPerTubeSlice;

                // Back quad
                baseIndex          += 2;
                indices[indexPtr++] = baseIndex;
                indices[indexPtr++] = baseIndex + 1;
                indices[indexPtr++] = baseIndex + 1 + numVertsPerTubeSlice;

                indices[indexPtr++] = baseIndex;
                indices[indexPtr++] = baseIndex + 1 + numVertsPerTubeSlice;
                indices[indexPtr++] = baseIndex + numVertsPerTubeSlice;

                // Bottom quad
                baseIndex          += 2;
                indices[indexPtr++] = baseIndex;
                indices[indexPtr++] = baseIndex + 1;
                indices[indexPtr++] = baseIndex + 1 + numVertsPerTubeSlice;

                indices[indexPtr++] = baseIndex;
                indices[indexPtr++] = baseIndex + 1 + numVertsPerTubeSlice;
                indices[indexPtr++] = baseIndex + numVertsPerTubeSlice;

                // Front quad
                baseIndex          += 2;
                indices[indexPtr++] = baseIndex;
                indices[indexPtr++] = baseIndex + 1;
                indices[indexPtr++] = baseIndex + 1 + numVertsPerTubeSlice;

                indices[indexPtr++] = baseIndex;
                indices[indexPtr++] = baseIndex + 1 + numVertsPerTubeSlice;
                indices[indexPtr++] = baseIndex + numVertsPerTubeSlice;
            }

            Mesh mesh = new Mesh();

            mesh.vertices = positions;
            mesh.normals  = normals;
            mesh.uv2      = radiusDirs;
            mesh.colors   = ColorEx.GetFilledColorArray(numVerts, color);
            mesh.SetIndices(indices, MeshTopology.Triangles, 0);
            mesh.UploadMeshData(false);

            return(mesh);
        }
示例#20
0
        public static Mesh CreateCylinder(float bottomRadius, float topRadius, float height, int numSlices,
                                          int numStacks, int numBottomCapRings, int numTopCapRings, Color color)
        {
            const float minSize = 1e-4f;

            if (bottomRadius < minSize)
            {
                bottomRadius = minSize;
            }
            if (topRadius < minSize)
            {
                topRadius = minSize;
            }
            if (height < minSize)
            {
                height = minSize;
            }

            const int minNumSlices = 3;

            if (numSlices < minNumSlices)
            {
                numSlices = minNumSlices;
            }

            const int minNumStacks = 1;

            if (numStacks < minNumStacks)
            {
                numStacks = minNumStacks;
            }

            int  minNumCapRings    = 1;
            bool generateBottomCap = numBottomCapRings >= minNumCapRings;
            bool generateTopCap    = numTopCapRings >= minNumCapRings;

            int numAxialRows     = numStacks + 1;
            int numVertsPerRow   = numSlices + 1;
            int numAxialVertices = numAxialRows * numVertsPerRow;
            int totalNumVerts    = numAxialVertices;

            Vector3[]      axialPositions = new Vector3[numAxialVertices];
            Vector3[]      axialNormals   = new Vector3[numAxialVertices];
            List <Vector3> allPositions   = new List <Vector3>(numAxialVertices);
            List <Vector3> allNormals     = new List <Vector3>(numAxialVertices);

            // Generate the axial vertices
            int     vertexPtr    = 0;
            Vector3 basePosition = Vector3.zero;
            Vector3 topPosition  = Vector3.up * height;
            Vector3 cylinderUp   = Vector3.up;
            float   yPosStep     = height / numStacks;
            float   angleStep    = 360.0f / numSlices;

            for (int axialRowIndex = 0; axialRowIndex < numAxialRows; ++axialRowIndex)
            {
                float rowYPos = basePosition.y + axialRowIndex * yPosStep;
                float radius  = Mathf.Lerp(bottomRadius, topRadius, rowYPos / topPosition.y);
                for (int vIndex = 0; vIndex < numVertsPerRow; ++vIndex)
                {
                    float   angle  = vIndex * angleStep;
                    Vector3 normal = (new Vector3(Mathf.Cos(angle * Mathf.Deg2Rad), 0.0f, Mathf.Sin(angle * Mathf.Deg2Rad))).normalized;

                    axialNormals[vertexPtr]   = normal;
                    axialPositions[vertexPtr] = basePosition + rowYPos * cylinderUp + normal * radius;
                    ++vertexPtr;
                }
            }
            allPositions.AddRange(axialPositions);
            allNormals.AddRange(axialNormals);

            // Generate the axial vertex indices
            int        indexPtr        = 0;
            List <int> allIndices      = new List <int>(100);
            int        numAxialIndices = numSlices * numStacks * 6;

            int[] axialIndices = new int[numAxialIndices];
            for (int axialRowIndex = 0; axialRowIndex < numAxialRows - 1; ++axialRowIndex)
            {
                for (int vIndex = 0; vIndex < numVertsPerRow - 1; ++vIndex)
                {
                    int indexOffset = axialRowIndex * numVertsPerRow + vIndex;

                    // First triangle
                    axialIndices[indexPtr++] = indexOffset;
                    axialIndices[indexPtr++] = indexOffset + numVertsPerRow;
                    axialIndices[indexPtr++] = indexOffset + 1;

                    // Second triangle
                    axialIndices[indexPtr++] = indexOffset + numVertsPerRow;
                    axialIndices[indexPtr++] = indexOffset + numVertsPerRow + 1;
                    axialIndices[indexPtr++] = indexOffset + 1;
                }
            }
            allIndices.AddRange(axialIndices);

            // Generate bottom cap if necessary
            if (generateBottomCap)
            {
                int numVertRings    = numBottomCapRings + 1;
                int numVertsPerRing = numSlices + 1;
                int numCapVerts     = numVertRings * numVertsPerRing;
                totalNumVerts += numCapVerts;

                vertexPtr = 0;
                Vector3[] capPositions = new Vector3[numCapVerts];
                Vector3[] capNormals   = new Vector3[numCapVerts];

                for (int ringIndex = 0; ringIndex < numVertRings; ++ringIndex)
                {
                    float radius = Mathf.Lerp(bottomRadius, 0.0f, ringIndex / (float)(numVertRings - 1));
                    for (int vIndex = 0; vIndex < numVertsPerRing; ++vIndex)
                    {
                        float   angle       = vIndex * angleStep;
                        Vector3 positionDir = (new Vector3(Mathf.Cos(angle * Mathf.Deg2Rad), 0.0f, Mathf.Sin(angle * Mathf.Deg2Rad))).normalized;
                        capPositions[vertexPtr] = basePosition + positionDir * radius;
                        capNormals[vertexPtr]   = -cylinderUp;
                        ++vertexPtr;
                    }
                }

                int baseVertexIndex = allPositions.Count;
                allPositions.AddRange(capPositions);
                allNormals.AddRange(capNormals);

                indexPtr = 0;
                int   numCapIndices = numSlices * numBottomCapRings * 6;
                int[] capIndices    = new int[numCapIndices];
                for (int vertexRingIndex = 0; vertexRingIndex < numVertRings - 1; ++vertexRingIndex)
                {
                    for (int vIndex = 0; vIndex < numVertsPerRing - 1; ++vIndex)
                    {
                        int indexOffset = baseVertexIndex + vertexRingIndex * numVertsPerRing + vIndex;

                        // First triangle
                        capIndices[indexPtr++] = indexOffset;
                        capIndices[indexPtr++] = indexOffset + 1;
                        capIndices[indexPtr++] = indexOffset + numVertsPerRing;

                        // Second triangle
                        capIndices[indexPtr++] = indexOffset + numVertsPerRing;
                        capIndices[indexPtr++] = indexOffset + 1;
                        capIndices[indexPtr++] = indexOffset + numVertsPerRing + 1;
                    }
                }
                allIndices.AddRange(capIndices);
            }

            // Generate top cap if necessary
            if (generateTopCap)
            {
                int numVertRings    = numTopCapRings + 1;
                int numVertsPerRing = numSlices + 1;
                int numCapVerts     = numVertRings * numVertsPerRing;
                totalNumVerts += numCapVerts;

                vertexPtr = 0;
                Vector3[] capPositions = new Vector3[numCapVerts];
                Vector3[] capNormals   = new Vector3[numCapVerts];

                for (int ringIndex = 0; ringIndex < numVertRings; ++ringIndex)
                {
                    float radius = Mathf.Lerp(topRadius, 0.0f, ringIndex / (float)(numVertRings - 1));
                    for (int vIndex = 0; vIndex < numVertsPerRing; ++vIndex)
                    {
                        float   angle       = vIndex * angleStep;
                        Vector3 positionDir = (new Vector3(Mathf.Cos(angle * Mathf.Deg2Rad), 0.0f, Mathf.Sin(angle * Mathf.Deg2Rad))).normalized;
                        capPositions[vertexPtr] = topPosition + positionDir * radius;
                        capNormals[vertexPtr]   = cylinderUp;
                        ++vertexPtr;
                    }
                }

                int baseVertexIndex = allPositions.Count;
                allPositions.AddRange(capPositions);
                allNormals.AddRange(capNormals);

                indexPtr = 0;
                int   numCapIndices = numSlices * numTopCapRings * 6;
                int[] capIndices    = new int[numCapIndices];
                for (int vertexRingIndex = 0; vertexRingIndex < numVertRings - 1; ++vertexRingIndex)
                {
                    for (int vIndex = 0; vIndex < numVertsPerRing - 1; ++vIndex)
                    {
                        int indexOffset = baseVertexIndex + vertexRingIndex * numVertsPerRing + vIndex;

                        // First triangle
                        capIndices[indexPtr++] = indexOffset;
                        capIndices[indexPtr++] = indexOffset + numVertsPerRing;
                        capIndices[indexPtr++] = indexOffset + 1;

                        // Second triangle
                        capIndices[indexPtr++] = indexOffset + numVertsPerRing;
                        capIndices[indexPtr++] = indexOffset + numVertsPerRing + 1;
                        capIndices[indexPtr++] = indexOffset + 1;
                    }
                }
                allIndices.AddRange(capIndices);
            }

            Mesh mesh = new Mesh();

            mesh.vertices = allPositions.ToArray();
            mesh.normals  = allNormals.ToArray();
            mesh.colors   = ColorEx.GetFilledColorArray(totalNumVerts, color);
            mesh.SetIndices(allIndices.ToArray(), MeshTopology.Triangles, 0);
            mesh.UploadMeshData(false);

            return(mesh);
        }
        public static Mesh CreateBox(float width, float height, float depth, Color color)
        {
            if (width < 1e-4f)
            {
                return(null);
            }
            if (height < 1e-4f)
            {
                return(null);
            }
            if (depth < 1e-4f)
            {
                return(null);
            }

            float halfWidth  = width * 0.5f;
            float halfHeight = height * 0.5f;
            float halfDepth  = depth * 0.5f;

            Vector3[] positions = new Vector3[]
            {
                // Front face
                new Vector3(-halfWidth, -halfHeight, -halfDepth),
                new Vector3(-halfWidth, halfHeight, -halfDepth),
                new Vector3(halfWidth, halfHeight, -halfDepth),
                new Vector3(halfWidth, -halfHeight, -halfDepth),

                // Back face
                new Vector3(halfWidth, -halfHeight, halfDepth),
                new Vector3(halfWidth, halfHeight, halfDepth),
                new Vector3(-halfWidth, halfHeight, halfDepth),
                new Vector3(-halfWidth, -halfHeight, halfDepth),

                // Top face
                new Vector3(-halfWidth, halfHeight, -halfDepth),
                new Vector3(-halfWidth, halfHeight, halfDepth),
                new Vector3(halfWidth, halfHeight, halfDepth),
                new Vector3(halfWidth, halfHeight, -halfDepth),

                // Bottom face
                new Vector3(halfWidth, -halfHeight, -halfDepth),
                new Vector3(halfWidth, -halfHeight, halfDepth),
                new Vector3(-halfWidth, -halfHeight, halfDepth),
                new Vector3(-halfWidth, -halfHeight, -halfDepth),

                // Left face
                new Vector3(-halfWidth, -halfHeight, halfDepth),
                new Vector3(-halfWidth, halfHeight, halfDepth),
                new Vector3(-halfWidth, halfHeight, -halfDepth),
                new Vector3(-halfWidth, -halfHeight, -halfDepth),

                // Right face
                new Vector3(halfWidth, -halfHeight, -halfDepth),
                new Vector3(halfWidth, halfHeight, -halfDepth),
                new Vector3(halfWidth, halfHeight, halfDepth),
                new Vector3(halfWidth, -halfHeight, halfDepth)
            };

            Vector3[] normals = new Vector3[]
            {
                // Front face
                -Vector3.forward, -Vector3.forward, -Vector3.forward, -Vector3.forward,

                // Back face
                Vector3.forward, Vector3.forward, Vector3.forward, Vector3.forward,

                // Top face
                Vector3.up, Vector3.up, Vector3.up, Vector3.up,

                // Bottom face
                -Vector3.up, -Vector3.up, -Vector3.up, -Vector3.up,

                // Left face
                -Vector3.right, -Vector3.right, -Vector3.right, -Vector3.right,

                // Right face
                Vector3.right, Vector3.right, Vector3.right, Vector3.right
            };

            int[] indices = new int[]
            {
                // Front face
                0, 1, 2, 2, 3, 0,

                // Back face
                4, 5, 6, 6, 7, 4,

                // Top face
                8, 9, 10, 10, 11, 8,

                // Bottom face
                12, 13, 14, 14, 15, 12,

                // Left face
                16, 17, 18, 18, 19, 16,

                // Right face
                20, 21, 22, 22, 23, 20
            };

            Mesh mesh = new Mesh();

            mesh.vertices = positions;
            mesh.normals  = normals;
            mesh.colors   = ColorEx.GetFilledColorArray(24, color);
            mesh.SetIndices(indices, MeshTopology.Triangles, 0);
            mesh.UploadMeshData(false);

            return(mesh);
        }
        public static Mesh CreateSphere(float radius, int numSlices, int numStacks, Color color)
        {
            if (radius < 1e-4f || numSlices < 3 || numStacks < 2)
            {
                return(null);
            }

            int numVertRows    = numStacks + 1;
            int numVertsPerRow = numSlices + 1;
            int numVerts       = numVertRows * numVertsPerRow;

            Vector3[] positions = new Vector3[numVerts];
            Vector3[] normals   = new Vector3[numVerts];

            int vertexPtr = 0;

            float angleStep = 360.0f / (numVertsPerRow - 1);

            for (int vertRowIndex = 0; vertRowIndex < numVertRows; ++vertRowIndex)
            {
                float theta    = Mathf.PI * (float)vertRowIndex / (numVertRows - 1);
                float cosTheta = Mathf.Cos(theta);
                float sinTheta = Mathf.Sin(theta);

                for (int vertIndex = 0; vertIndex < numVertsPerRow; ++vertIndex)
                {
                    float   centralAxisRotAngle = angleStep * vertIndex * Mathf.Deg2Rad;
                    Vector3 rotatedAxis         = Vector3.right * Mathf.Sin(centralAxisRotAngle) +
                                                  Vector3.forward * Mathf.Cos(centralAxisRotAngle);
                    positions[vertexPtr] = rotatedAxis * sinTheta * radius + Vector3.up * cosTheta * radius;
                    normals[vertexPtr]   = Vector3.Normalize(positions[vertexPtr]);
                    ++vertexPtr;
                }
            }

            int indexPtr   = 0;
            int numIndices = numSlices * numStacks * 6;

            int[] indices = new int[numIndices];
            for (int vertRowIndex = 0; vertRowIndex < numVertRows - 1; ++vertRowIndex)
            {
                for (int vertIndex = 0; vertIndex < numVertsPerRow - 1; ++vertIndex)
                {
                    // Calculate the index of the first vertex inside the first triangle
                    int baseIndex = vertRowIndex * numVertsPerRow + vertIndex;

                    // First triangle
                    indices[indexPtr++] = baseIndex;
                    indices[indexPtr++] = baseIndex + numVertsPerRow;
                    indices[indexPtr++] = baseIndex + numVertsPerRow + 1;

                    // Second triangle
                    indices[indexPtr++] = baseIndex + numVertsPerRow + 1;
                    indices[indexPtr++] = baseIndex + 1;
                    indices[indexPtr++] = baseIndex;
                }
            }

            Mesh mesh = new Mesh();

            mesh.vertices = positions;
            mesh.normals  = normals;
            mesh.colors   = ColorEx.GetFilledColorArray(numVerts, color);
            mesh.SetIndices(indices, MeshTopology.Triangles, 0);
            mesh.UploadMeshData(false);

            return(mesh);
        }
        public static Mesh CreateTorus(Vector3 center, float coreRadius, float tubeRadius, int numTubeSlices, int numSlices, Color color)
        {
            if (coreRadius < 1e-4f || tubeRadius < 1e-4f || numTubeSlices < 3 || numSlices < 3)
            {
                return(null);
            }

            int numVertsPerTubeSlice = (numSlices + 1);
            int numVerts             = numVertsPerTubeSlice * (numTubeSlices + 1);

            Vector3[] positions = new Vector3[numVerts];
            Vector3[] normals   = new Vector3[numVerts];

            int vertexPtr = 0;

            float outerAngleStep = 360.0f / (numSlices - 1);
            float tubeAngleStep  = 360.0f / (numTubeSlices - 1);

            for (int tubeSliceIndex = 0; tubeSliceIndex <= numTubeSlices; ++tubeSliceIndex)
            {
                float tubeAngle = tubeAngleStep * tubeSliceIndex * Mathf.Deg2Rad;
                float cosTube   = Mathf.Cos(tubeAngle);
                float sinTube   = Mathf.Sin(tubeAngle);

                Vector3 tubeSliceCenter = new Vector3(sinTube * coreRadius, 0.0f, cosTube * coreRadius);

                for (int sliceIndex = 0; sliceIndex <= numSlices; ++sliceIndex)
                {
                    float outerAngle = outerAngleStep * sliceIndex * Mathf.Deg2Rad;
                    float cosOuter   = Mathf.Cos(outerAngle);
                    float sinOuter   = Mathf.Sin(outerAngle);

                    Vector3 vPos = tubeSliceCenter;
                    vPos.x += sinTube * sinOuter * tubeRadius;
                    vPos.y += cosOuter * tubeRadius;
                    vPos.z += cosTube * sinOuter * tubeRadius;

                    vPos += center;
                    positions[vertexPtr] = vPos;
                    normals[vertexPtr]   = (vPos - center).normalized;

                    ++vertexPtr;
                }
            }

            int indexPtr   = 0;
            int numIndices = numTubeSlices * numSlices * 6;

            int[] indices = new int[numIndices];
            for (int tubeSliceIndex = 0; tubeSliceIndex < numTubeSlices; ++tubeSliceIndex)
            {
                for (int sliceIndex = 0; sliceIndex < numSlices; ++sliceIndex)
                {
                    int baseIndex = tubeSliceIndex * numVertsPerTubeSlice + sliceIndex;

                    indices[indexPtr++] = baseIndex;
                    indices[indexPtr++] = baseIndex + 1;
                    indices[indexPtr++] = baseIndex + numVertsPerTubeSlice;

                    indices[indexPtr++] = baseIndex + 1;
                    indices[indexPtr++] = baseIndex + 1 + numVertsPerTubeSlice;
                    indices[indexPtr++] = baseIndex + numVertsPerTubeSlice;
                }
            }

            Mesh mesh = new Mesh();

            mesh.vertices = positions;
            mesh.normals  = normals;
            mesh.colors   = ColorEx.GetFilledColorArray(numVerts, color);
            mesh.SetIndices(indices, MeshTopology.Triangles, 0);
            mesh.UploadMeshData(false);

            return(mesh);
        }