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); } } }
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; } }