public override void Draw(int beatmapTicks, float currentY)
        {
            var metrics  = _metrics;
            var skyNotes = SkyVisualNotes;
            var n        = _baseNote;

            var startX = (n.StartX - -0.5f) * metrics.TrackInnerWidth / (1.5f - -0.5f) - metrics.HalfTrackInnerWidth;
            var startY = PreviewStartY - currentY;
            var startZ = metrics.SkyInputZ * n.StartY + metrics.PlayableArcTallness * (1 - n.StartY) / 4;

            var endX = (n.EndX - -0.5f) * metrics.TrackInnerWidth / (1.5f - -0.5f) - metrics.HalfTrackInnerWidth;
            var endY = PreviewEndY - currentY;
            var endZ = metrics.SkyInputZ * n.EndY + metrics.PlayableArcTallness * (1 - n.EndY) / 4;

            var start = new Vector3(startX, startY, startZ);
            var end   = new Vector3(endX, endY, endZ);

            if (skyNotes?.Length > 0)
            {
                var skyNoteSize = new Vector3(metrics.SkyNoteWidth, metrics.SkyNoteHeight, metrics.SkyNoteTallness);

                foreach (var skyNote in skyNotes)
                {
                    if (!skyNote.IsVisible(beatmapTicks, currentY))
                    {
                        continue;
                    }

                    var ratio = (float)(((SkyNote)skyNote.BaseNote).Tick - n.StartTick) / (n.EndTick - n.StartTick);

                    //var bottomNearLeft = new Vector3(left, bottom, corner);

                    var bottomNearLeft = ArcEasingHelper.Ease(start, end, ratio, n.Easing);

                    bottomNearLeft.X -= metrics.SkyNoteWidth / 2;
                    bottomNearLeft.Z -= metrics.SkyNoteTallness / 2;

                    skyNote.SetVertices(bottomNearLeft, skyNoteSize);

                    skyNote.SetTexture(_skyVisualNoteTexture);

                    skyNote.Draw(beatmapTicks, currentY);
                }
            }


            Color   arcColor;
            Vector2 arcSectionSize;
            float   alpha;
            bool    castShadow;

            if (n.IsPlayable)
            {
                arcColor       = n.Color == ArcColor.Magenta ? RedArc : BlueArc;
                arcSectionSize = new Vector2(metrics.PlayableArcWidth, metrics.PlayableArcTallness);
                alpha          = 0.75f;
                castShadow     = true;
            }
            else
            {
                arcColor       = GhostArc;
                arcSectionSize = new Vector2(metrics.GuidingArcWidth, metrics.GuidingArcTallness);
                alpha          = 0.5f;
                castShadow     = false;
            }

            var segmentCount = n.EndTick - n.StartTick > 1000 ? 14 : 7;
            var effect       = (BasicEffect)NoteEffects.Effects[(int)n.Type];

            DrawArc(effect, segmentCount, start, end, n.Easing, arcColor, alpha, arcSectionSize, castShadow);

            if (n.IsPlayable)
            {
                if (ShouldDrawHeader && beatmapTicks <= n.StartTick)
                {
                    _header.SetVerticesXZ(start, arcColor, arcSectionSize.X);

                    effect.TextureEnabled     = false;
                    effect.VertexColorEnabled = true;

                    effect.Alpha = alpha;

                    _header.Draw(effect.CurrentTechnique);
                }

                if (ShouldDrawSupport && startZ > 0 && beatmapTicks <= n.StartTick)
                {
                    var supportBottom = new Vector2(startX - metrics.PlayableArcWidth / 2, 0);
                    var supportSize   = new Vector2(metrics.PlayableArcWidth, startZ);

                    _support.SetVerticesXZTextureRotated(supportBottom, supportSize, arcColor, startY);

                    effect.TextureEnabled     = true;
                    effect.VertexColorEnabled = true;

                    effect.Alpha   = 0.8f;
                    effect.Texture = _supportTexture;

                    _support.Draw(effect.CurrentTechnique);
                }
            }
        }
Exemple #2
0
        public override void Draw(int beatmapTicks, float currentY)
        {
            var metrics = _metrics;
            var left    = ((int)_baseNote.Track - 0.5f) * metrics.TrackInnerWidth / 4 - metrics.FloorNoteWidth / 2 - metrics.HalfTrackInnerWidth;
            var bottom  = PreviewY - currentY;

            var bottomLeft = new Vector2(left, bottom);
            var size       = new Vector2(metrics.FloorNoteWidth, metrics.FloorNoteHeight);

            _noteRectangle.SetVerticesXY(bottomLeft, size, Color.White, Z);

            var effect = (BasicEffect)NoteEffects.Effects[(int)_baseNote.Type];

            effect.Alpha = 0.75f;

            effect.TextureEnabled     = true;
            effect.VertexColorEnabled = true;

            effect.Texture = _texture;

            _noteRectangle.Draw(effect.CurrentTechnique);

            var synced = SynchronizedSkyVisualNote;

            if (synced != null)
            {
                // Draw the sync line.

                var n = (ArcNote)synced.Parent.BaseNote;

                // TODO: This calculation can be executed when loading the beatmap, and store in some fields like "PreviewX".

                // var skyPoint = new Vector3(skyMiddle, skyBottom, skyEdge);
                // Optimization for sky point calculation needed

                var ratio = (float)(((SkyNote)synced.BaseNote).Tick - n.StartTick) / (n.EndTick - n.StartTick);

                var startX = (n.StartX - -0.5f) * metrics.TrackInnerWidth / (1.5f - -0.5f) - metrics.HalfTrackInnerWidth;
                var startZ = metrics.SkyInputZ * n.StartY + metrics.ArcHeightLowerBorder * (1 - n.StartY);

                var endX = (n.EndX - -0.5f) * metrics.TrackInnerWidth / (1.5f - -0.5f) - metrics.HalfTrackInnerWidth;
                var endZ = metrics.SkyInputZ * n.EndY + metrics.ArcHeightLowerBorder * (1 - n.EndY);

                var start = new Vector3(startX, 1, startZ);
                var end   = new Vector3(endX, 1, endZ);

                var skyPoint = ArcEasingHelper.Ease(start, end, ratio, n.Easing);
                skyPoint.Y = synced.PreviewY - currentY;

                var thisPoint = new Vector3(left + metrics.FloorNoteWidth / 2, bottom, Z);

                _linkHexahedron.SetVerticesXZ(thisPoint, skyPoint, Color.PaleVioletRed, LinkSectionSize);

                effect.Alpha = 0.2f;

                effect.TextureEnabled     = false;
                effect.VertexColorEnabled = true;

                _linkHexahedron.Draw(effect.CurrentTechnique);
            }
        }
        private void DrawArc([NotNull] BasicEffect effect, int segmentCount, Vector3 start, Vector3 end, ArcEasing easing, Color color, float alpha, Vector2 arcSectionSize, bool castShadow)
        {
            var lastPoint = start;
            var zeroY     = _metrics.FinishLineY;
            var trackEndY = _metrics.TrackLength;

            for (var i = 1; i <= segmentCount; ++i)
            {
                if (lastPoint.Y > trackEndY)
                {
                    // This segment and later segments have not entered the track yet.
                    break;
                }

                Vector3 currentPoint;

                if (i == segmentCount)
                {
                    currentPoint = end;
                }
                else
                {
                    var ratio = (float)i / segmentCount;
                    currentPoint = ArcEasingHelper.Ease(start, end, ratio, easing);
                }

                if (lastPoint.Y < zeroY && currentPoint.Y < zeroY)
                {
                    // This segment has passed.
                    continue;
                }

                Vector3 fixedLastPoint, fixedCurrentPoint;

                // Recalculate the segment's start and end if needed.
                // However, we must ensure that the movement of the intersection of the arc and XoZ plane is always continuous,
                // therefore we must call the easing function again to retrieve its precise new location, instead of learping
                // inside the segment's start and end (shows recognizable "shaking" effect).
                // Credit: @18111398
                if (lastPoint.Y < zeroY)
                {
                    var ratio = (zeroY - start.Y) / (end.Y - start.Y);
                    fixedLastPoint = ArcEasingHelper.Ease(start, end, ratio, easing);
                }
                else
                {
                    fixedLastPoint = lastPoint;
                }

                if (currentPoint.Y > trackEndY)
                {
                    var ratio = (trackEndY - start.Y) / (end.Y - start.Y);
                    fixedCurrentPoint = ArcEasingHelper.Ease(start, end, ratio, easing);
                }
                else
                {
                    fixedCurrentPoint = currentPoint;
                }

                _arcMesh.SetVerticesXY(fixedLastPoint, fixedCurrentPoint, color, arcSectionSize.X);

                effect.TextureEnabled     = false;
                effect.VertexColorEnabled = true;

                effect.Alpha = alpha;

                _arcMesh.Draw(effect.CurrentTechnique);

                // Draw shadow if needed.
                if (castShadow)
                {
                    _shadow.SetVerticesXYParallel(fixedLastPoint.XY(), fixedCurrentPoint.XY(), arcSectionSize.X, Color.Gray, ShadowZ);

                    effect.TextureEnabled     = false;
                    effect.VertexColorEnabled = true;

                    effect.Alpha = 0.5f;

                    _shadow.Draw(effect.CurrentTechnique);
                }

                // Update the point.
                lastPoint = currentPoint;
            }
        }