public void FillOval( float centerX, float centerY, float centerZ, float radiusX, float radiusY, TVertexParameters parameters, int edges) { if (edges < 3) { throw new ArgumentOutOfRangeException(nameof(edges), "Must draw at least three edges."); } meshBuilder.Add(edges, (edges - 2) * 3, out var vertices, out var indices, out var indexOffset); var rotation = Matrix2.CreateRotation(MathHelper.TwoPi / edges); var xy = new Vector2(0, -1); vertices[0] = createVertex(new Vector3( centerX + xy.X * radiusX, centerY + xy.Y * radiusY, centerZ), parameters); for (var i = 1; i < edges; i++) { xy = rotation * xy; vertices[i] = createVertex(new Vector3( centerX + xy.X * radiusX, centerY + xy.Y * radiusY, centerZ), parameters); } for (var i = 0; i < edges - 2; i++) { var o = i * 3; indices[o++] = indexOffset; indices[o++] = (ushort)(indexOffset + i + 1); indices[o] = (ushort)(indexOffset + i + 2); } }
public void SetTransform(float SX, float SY, float Rotate, float TX, float TY) { EnsureInitialized(); Matrix2 Transform; Transform = Matrix2.CreateScale(SX, SY); Transform *= Matrix2.CreateRotation(Rotate); Vector2 Offs = new Vector2(TX, TY); int CurrentProgram = GL.GetInteger(GetPName.CurrentProgram); GL.UseProgram(Shader.Handle); int TransformUniformLocation = GL.GetUniformLocation(Shader.Handle, "transform"); GL.UniformMatrix2(TransformUniformLocation, false, ref Transform); int OffsetUniformLocation = GL.GetUniformLocation(Shader.Handle, "offset"); GL.Uniform2(OffsetUniformLocation, ref Offs); GL.UseProgram(CurrentProgram); }
public Polygon(string Scene, ushort Corners) : base(Scene) { if (Corners < 3) { Logger.Log(new LogMessage(LogSeverity.Warning, $"I was told to create a polygon with {Corners} corners. What do I do with this?")); return; } List <Vector2> _verts = new(); // FIXME: Half circle for (float i = 0; i < 360; i += 360f / Corners) { Matrix2.CreateRotation(i, out var _rot); _verts.Add(new Vector2(-1, 1) * _rot); } transform.Vertices = _verts.ToArray(); transform.UV = _verts.ToArray(); List <Vector3i> _indices = new(); for (int i = 1; i <= Corners; i++) { _indices.Add(new Vector3i(0, i, i + 1)); } transform.Indices = new uint[Corners * 3]; _indices.ForEach(x => { for (int i = 0; i < 3; i++) { transform.Indices[_indices.IndexOf(x) * 3 + i] = (uint)x[i]; } } ); }
/// <summary> /// Draws a filled oval. /// </summary> /// <param name="centerX">The center X.</param> /// <param name="centerY">The center Y.</param> /// <param name="centerZ">The center Z.</param> /// <param name="halfWidth">Half of the width.</param> /// <param name="halfHeight">Half of the height.</param> /// <param name="edges">The number of edges to draw the oval width.</param> private void drawOvalFilled(float centerX, float centerY, float centerZ, float halfWidth, float halfHeight, int edges) { var vertices = new PrimitiveVertexData[edges]; Matrix2 rotation = Matrix2.CreateRotation(MathHelper.TwoPi / edges); Vector2 xy = new Vector2(0, -1); Color argb = this.Color; vertices[0] = new PrimitiveVertexData( centerX + xy.X * halfWidth, centerY + xy.Y * halfHeight, centerZ, argb); for (int i = 1; i < edges; i++) { xy = rotation.Times(xy); vertices[i] = new PrimitiveVertexData( centerX + xy.X * halfWidth, centerY + xy.Y * halfHeight, centerZ, argb); } ushort offset = this.surface.AddVertices(vertices); var ids = new ushort[(edges - 2) * 3]; for (int i = 0; i < edges - 2; i++) { int o = i * 3; ids[o++] = offset; ids[o++] = (ushort)(offset + i + 1); ids[o] = (ushort)(offset + i + 2); } this.surface.AddIndices(ids); }
/// <summary> /// Draws an unfilled oval. /// </summary> /// <param name="centerX">The center X.</param> /// <param name="centerY">The center Y.</param> /// <param name="centerZ">The center Z.</param> /// <param name="halfWidth">Half of the width.</param> /// <param name="halfHeight">Half of the height.</param> /// <param name="edges">The number of edges to draw the oval width.</param> private void drawOvalUnfilled(float centerX, float centerY, float centerZ, float halfWidth, float halfHeight, int edges) { var vertices = new PrimitiveVertexData[edges * 2]; Matrix2 rotation = Matrix2.CreateRotation(MathHelper.TwoPi / edges); Vector2 xy = new Vector2(0, -1); Color argb = this.Color; float innerW = halfWidth - this.LineWidth; float innerH = halfHeight - this.LineWidth; vertices[0] = new PrimitiveVertexData( centerX + xy.X * halfWidth, centerY + xy.Y * halfHeight, centerZ, argb); vertices[1] = new PrimitiveVertexData( centerX + xy.X * innerW, centerY + xy.Y * innerH, centerZ, argb); for (int i = 1; i < edges; i++) { xy = rotation.Times(xy); vertices[2 * i] = new PrimitiveVertexData( centerX + xy.X * halfWidth, centerY + xy.Y * halfHeight, centerZ, argb); vertices[2 * i + 1] = new PrimitiveVertexData( centerX + xy.X * innerW, centerY + xy.Y * innerH, centerZ, argb); } ushort offset = this.surface.AddVertices(vertices); var ids = new ushort[edges * 6]; for (int i = 0; i < edges - 1; i++) { int j = i * 6; int o = i * 2; ids[j] = (ushort)(offset + o); ids[j + 1] = (ushort)(offset + o + 2); ids[j + 2] = (ushort)(offset + o + 1); ids[j + 3] = (ushort)(offset + o + 1); ids[j + 4] = (ushort)(offset + o + 2); ids[j + 5] = (ushort)(offset + o + 3); } int lastJ = edges * 6 - 6; ids[lastJ] = (ushort)(offset + edges * 2 - 2); ids[lastJ + 1] = (ushort)(offset); ids[lastJ + 2] = (ushort)(offset + edges * 2 - 1); ids[lastJ + 3] = (ushort)(offset + edges * 2 - 1); ids[lastJ + 4] = (ushort)(offset); ids[lastJ + 5] = (ushort)(offset + 1); this.surface.AddIndices(ids); }
public void DrawOval( float centerX, float centerY, float centerZ, float radiusX, float radiusY, float lineWidth, TVertexParameters parameters, int edges) { if (edges < 3) { throw new ArgumentOutOfRangeException(nameof(edges), "Must draw at least three edges."); } meshBuilder.Add(edges * 2, edges * 6, out var vertices, out var indices, out var indexOffset); var rotation = Matrix2.CreateRotation(MathHelper.TwoPi / edges); var xy = new Vector2(0, -1); var innerW = radiusX - lineWidth; var innerH = radiusY - lineWidth; vertices[0] = createVertex(new Vector3( centerX + xy.X * radiusX, centerY + xy.Y * radiusY, centerZ), parameters); vertices[1] = createVertex(new Vector3( centerX + xy.X * innerW, centerY + xy.Y * innerH, centerZ), parameters); for (var i = 1; i < edges; i++) { xy = rotation * xy; vertices[2 * i] = createVertex(new Vector3( centerX + xy.X * radiusX, centerY + xy.Y * radiusY, centerZ), parameters); vertices[2 * i + 1] = createVertex(new Vector3( centerX + xy.X * innerW, centerY + xy.Y * innerH, centerZ), parameters); } for (var i = 0; i < edges - 1; i++) { var j = i * 6; var o = i * 2; indices[j] = (ushort)(indexOffset + o); indices[j + 1] = (ushort)(indexOffset + o + 2); indices[j + 2] = (ushort)(indexOffset + o + 1); indices[j + 3] = (ushort)(indexOffset + o + 1); indices[j + 4] = (ushort)(indexOffset + o + 2); indices[j + 5] = (ushort)(indexOffset + o + 3); } var lastJ = edges * 6 - 6; indices[lastJ] = (ushort)(indexOffset + edges * 2 - 2); indices[lastJ + 1] = indexOffset; indices[lastJ + 2] = (ushort)(indexOffset + edges * 2 - 1); indices[lastJ + 3] = (ushort)(indexOffset + edges * 2 - 1); indices[lastJ + 4] = indexOffset; indices[lastJ + 5] = (ushort)(indexOffset + 1); }
public void GetRotatedCorners(Vector2 rotationCenter, float angle, out Vector2 topLeft, out Vector2 topRight, out Vector2 bottomLeft, out Vector2 bottomRight) { Matrix2 rotationMatrix = Matrix2.CreateRotation(angle); topLeft = (new Vector2(Left, Top) - rotationCenter) * rotationMatrix + rotationCenter; topRight = (new Vector2(Right, Top) - rotationCenter) * rotationMatrix + rotationCenter; bottomLeft = (new Vector2(Left, Bottom) - rotationCenter) * rotationMatrix + rotationCenter; bottomRight = (new Vector2(Right, Bottom) - rotationCenter) * rotationMatrix + rotationCenter; }
/// <summary> /// Rotates the four corners of the <see cref="UVRectangle"/> by a given angle around a given point. /// </summary> /// <param name="angle">The angle by which to rotate</param> /// <param name="center">The point around which to rotate</param> public void Rotate(float angle, Vector2 center) { Matrix2 m = Matrix2.CreateRotation(angle); this.TopLeft = center + m.Times(this.TopLeft - center); this.TopRight = center + m.Times(this.TopRight - center); this.BottomLeft = center + m.Times(this.BottomLeft - center); this.BottomRight = center + m.Times(this.BottomRight - center); }
public void SetFrameBufferTransform(float SX, float SY, float Rotate, float TX, float TY) { Matrix2 Transform; Transform = Matrix2.CreateScale(SX, SY); Transform *= Matrix2.CreateRotation(Rotate); Vector2 Offs = new Vector2(TX, TY); ActionsQueue.Enqueue(() => FrameBuffer.SetTransform(Transform, Offs)); }
public bool ContainsRotated(Vector2 pt, Vector2 rotationCenter, float angle) { if (angle == 0) { return(Contains(pt)); } Matrix2 rotationMatrix = Matrix2.CreateRotation(-angle); Vector2 rotatedPt = (pt - rotationCenter) * rotationMatrix + rotationCenter; return(Contains(rotatedPt.X, rotatedPt.Y)); }
public void MatrixCreateRotation() { Matrix2 rot0deg = new Matrix2(1, 0, 0, 1); Matrix2 rot90deg = new Matrix2(0, -1, 1, 0); Matrix2 rot180deg = new Matrix2(-1, 0, 0, -1); Matrix2 rot270deg = new Matrix2(0, 1, -1, 0); CompareMatrices(Matrix2.CreateRotation((float)Math.PI * 0.0f), rot0deg, 10e-7f); CompareMatrices(Matrix2.CreateRotation((float)Math.PI * 0.5f), rot90deg, 10e-7f); CompareMatrices(Matrix2.CreateRotation((float)Math.PI * 1.0f), rot180deg, 10e-7f); CompareMatrices(Matrix2.CreateRotation((float)Math.PI * 1.5f), rot270deg, 10e-7f); CompareMatrices(Matrix2.CreateRotation((float)Math.PI * 2.0f), rot0deg, 10e-7f); }
private Line RotateLine(Line stick, float rotationAngle) { var mtxRotation = Matrix2.CreateRotation(rotationAngle); Vector2 a; a.X = Vector2.Dot(mtxRotation.Column0, stick.Item1); a.Y = Vector2.Dot(mtxRotation.Column1, stick.Item1); Vector2 b; b.X = Vector2.Dot(mtxRotation.Column0, stick.Item2); b.Y = Vector2.Dot(mtxRotation.Column1, stick.Item2); return(new Line(a, b)); }
public static Vector2 GetRotatedVector(Vector2 vector, float angle, bool clockwise) { if (clockwise) { angle *= -1; } Matrix2 rotationMatrix = Matrix2.CreateRotation(angle); float x = rotationMatrix.M11 * vector.X + rotationMatrix.M12 * vector.Y; float y = rotationMatrix.M21 * vector.X + rotationMatrix.M22 * vector.Y; return(new Vector2(x, y)); }
protected override void OnBeforeUpdaters(object sender, EventArgs e) { _eyeInput.Update(); _moveInput.Update(); _eye.Rotation += _eyeInput.Value.Xy; var tmp = Matrix2.CreateRotation(MathHelper.DegreesToRadians(_eye.Rotation.X)) * new Vector2(_moveInput.Value.X, -_moveInput.Value.Z); _playerObject.Velocity = new Vector3d(tmp.X, Math.Min(5D, _playerObject.Velocity.Y + _moveInput.BaseInput.Value.Y * .25), -tmp.Y); PlayerMovement(); Title = _playerObject.Velocity.ToString(); base.OnBeforeUpdaters(sender, e); }
public unsafe void SetFrameBuffer( byte *Fb, int Width, int Height, float ScaleX, float ScaleY, float Rotate) { Matrix2 Transform; Transform = Matrix2.CreateScale(ScaleX, ScaleY); Transform *= Matrix2.CreateRotation(Rotate); FbRenderer.Set(Fb, Width, Height, Transform); }
private void UpdateChildRotation() { //delta from previous frame to current frame float deltaAngle = Parent.Rotation - previousParentRotation; //Change Rotation this.Rotation += deltaAngle; //Change Position Matrix2 newRotation = Matrix2.CreateRotation(-deltaAngle); LocalPosition = newRotation.PerVector2(LocalPosition); this.position = Parent.position + LocalPosition; //save prev parent rot previousParentRotation = Parent.Rotation; }
public RelevantPoint GetRelevantObjects(RelevantPoint point1, RelevantPoint point2) { // Any point can be the origin if (MySettings.OriginInputPredicate.Check(point1, this) && MySettings.OtherInputPredicate.Check(point2, this)) { return(new RelevantPoint(Matrix2.Mult(Matrix2.CreateRotation(MathHelper.DegreesToRadians(MySettings.Angle)), point2.Child - point1.Child) * MySettings.Scalar + point1.Child)); } if (MySettings.OriginInputPredicate.Check(point2, this) && MySettings.OtherInputPredicate.Check(point1, this)) { return(new RelevantPoint(Matrix2.Mult(Matrix2.CreateRotation(MathHelper.DegreesToRadians(MySettings.Angle)), point1.Child - point2.Child) * MySettings.Scalar + point2.Child)); } return(null); }
public override void DrawSprite(Vector3 position, float angle, float scale) { Vector2 expand1 = new Vector2(expandX, expandY) * scale; Vector2 expand2 = new Vector2(-expand1.X, expand1.Y); if (angle != 0) { Matrix2 rotation = Matrix2.CreateRotation(angle); expand1 = rotation.Times(expand1); expand2 = rotation.Times(expand2); } this.Surface.AddQuad( new SimpleSpriteVertexData(position, this.UV.TopLeft, this.Color, expand2), new SimpleSpriteVertexData(position, this.UV.TopRight, this.Color, expand1), new SimpleSpriteVertexData(position, this.UV.BottomRight, this.Color, -expand2), new SimpleSpriteVertexData(position, this.UV.BottomLeft, this.Color, -expand1) ); }
public Obj3DText(string txt, TextPlane plane, float size, float posX, float posY, float posZ, float dirAngle, string color, Texture fontTex) { Matrix2 rotMat = Matrix2.CreateRotation(MathHelper.DegreesToRadians(dirAngle)); float [] verts = new float[txt.Length * 4 * 5]; uint [] inds = new uint[txt.Length * 6]; int vp = 0; int ip = 0; for (int i = 0; i < txt.Length; i++) { char ch = txt[i]; uint svp = (uint)vp; // save first index // top left Vector3 v3p = GetTranslatedPoint(posX, posY, posZ, size, i, 0, rotMat, plane); // 3d vector Vector2 vtp = GetTextureCorner(ch, 0, 0); // texture vector SetVertex(verts, vp++, v3p, vtp); // top right v3p = GetTranslatedPoint(posX, posY, posZ, size, i + 1, 0, rotMat, plane); // 3d vector vtp = GetTextureCorner(ch, 1, 0); // texture vector SetVertex(verts, vp++, v3p, vtp); // bootom left v3p = GetTranslatedPoint(posX, posY, posZ, size, i, -1, rotMat, plane); // 3d vector vtp = GetTextureCorner(ch, 0, 1); // texture vector SetVertex(verts, vp++, v3p, vtp); // bootom right v3p = GetTranslatedPoint(posX, posY, posZ, size, i + 1, -1, rotMat, plane); // 3d vector vtp = GetTextureCorner(ch, 1, 1); // texture vector SetVertex(verts, vp++, v3p, vtp); // indexes, 2 triangles inds[ip++] = svp; inds[ip++] = svp + 2; inds[ip++] = svp + 1; inds[ip++] = svp + 2; inds[ip++] = svp + 3; inds[ip++] = svp + 1; } Init(Shader.ShadingType.Textured3D, verts, inds, fontTex); SetColor(color); }
private static List <Vector2> MakePenis(List <Vector2> points, double sliderLength) { // Penis shape List <Vector2> newPoints = new List <Vector2> { new(0, 0), new(40, -40), new(0, -70), new(-40, -40), new(0, 0), new(0, 0), new(96, 24), new(168, 0), new(168, 0), new(96, -24), new(0, 0), new(0, 0), new(-40, 40), new(0, 70), new(40, 40), new(0, 0) }; double sizeMultiplier = sliderLength / 591 * 2; // 591 is the size of the dick double normalAngle = -(points.Last() - points.First()).Theta; var mat = Matrix2.CreateRotation(normalAngle); mat *= sizeMultiplier; for (var i = 0; i < newPoints.Count; i++) { Vector2 point = newPoints[i]; // transform to slider Vector2 transformPoint = Matrix2.Mult(mat, point); newPoints[i] = points.First() + transformPoint; } return(newPoints); }
/// <summary> /// Draws a sprite. /// </summary> /// <param name="position">The coordinates to draw the sprite at. The sprite is drawn centered around this point.</param> /// <param name="angle">The angle to rotate the sprite by, around the z axis, in radians.</param> /// <param name="scale">An additional scalar to scale the sprite by, relative to <see cref="Size"/>.</param> public override void DrawSprite(Vector3 position, float angle, float scale) { float x = this.expandX * scale; float y = this.expandY * scale; Vector2 topLeft = new Vector2(-x, -y); Vector2 topRight = new Vector2(x, -y); Vector2 bottomLeft = new Vector2(-x, y); Vector2 bottomRight = new Vector2(x, y); if (angle != 0) { Matrix2 rotation = Matrix2.CreateRotation(angle); topLeft = rotation.Times(topLeft); topRight = rotation.Times(topRight); bottomLeft = rotation.Times(bottomLeft); bottomRight = rotation.Times(bottomRight); } this.Surface.AddQuad( new UVColorVertexData(position.X + topLeft.X, position.Y + topLeft.Y, position.Z, this.UV.TopLeft, this.Color), new UVColorVertexData(position.X + topRight.X, position.Y + topRight.Y, position.Z, this.UV.TopRight, this.Color), new UVColorVertexData(position.X + bottomRight.X, position.Y + bottomRight.Y, position.Z, this.UV.BottomRight, this.Color), new UVColorVertexData(position.X + bottomLeft.X, position.Y + bottomLeft.Y, position.Z, this.UV.BottomLeft, this.Color) ); }
public void Recalculate() { if (this.localAngleChanged) { this.rotationLocal = Matrix2.CreateRotation(this.parameters.Angle) * this.parameters.Scale; this.localAngleChanged = false; } if (this.parent == null) { this.angleGlobal = this.parameters.Angle; this.offsetGlobal = this.parameters.Offset; this.rotationGlobal = this.rotationLocal; this.scaleGlobal = this.parameters.Scale; } else { var t = parent.Transformation; this.angleGlobal = t.angleGlobal + this.parameters.Angle; this.offsetGlobal = t.offsetGlobal + t.rotationGlobal.Times(this.parameters.Offset); this.rotationGlobal = t.rotationGlobal * this.rotationLocal; this.scaleGlobal = t.ScaleGlobal * this.parameters.Scale; } }
/// <summary> /// Does a procedure similar to <see cref="MapCleaner"/> which adjusts the pattern so it fits in the beatmap. /// It does so according to the options selected in this. /// </summary> /// <param name="patternBeatmap"></param> /// <param name="beatmap"></param> /// <param name="parts"></param> /// <param name="timingPointsChanges"></param> private void PreparePattern(Beatmap patternBeatmap, Beatmap beatmap, out List <Part> parts, out List <TimingPointsChange> timingPointsChanges) { double patternStartTime = patternBeatmap.GetHitObjectStartTime(); Timing originalTiming = beatmap.BeatmapTiming; Timing patternTiming = patternBeatmap.BeatmapTiming; GameMode targetMode = (GameMode)beatmap.General["Mode"].IntValue; double originalCircleSize = beatmap.Difficulty["CircleSize"].DoubleValue; double patternCircleSize = patternBeatmap.Difficulty["CircleSize"].DoubleValue; double originalTickRate = beatmap.Difficulty["SliderTickRate"].DoubleValue; double patternTickRate = patternBeatmap.Difficulty["SliderTickRate"].DoubleValue; // Don't include SV changes if it is based on nothing bool includePatternSliderVelocity = patternTiming.Count > 0; // Avoid including hitsounds if there are no timingpoints to get hitsounds from bool includeTimingPointHitsounds = IncludeHitsounds && patternTiming.Count > 0; // Don't scale to new timing if the pattern has no timing to speak of bool scaleToNewTiming = ScaleToNewTiming && patternTiming.Redlines.Count > 0; // Avoid overwriting timing if the pattern has no redlines TimingOverwriteMode timingOverwriteMode = patternTiming.Redlines.Count > 0 ? TimingOverwriteMode : TimingOverwriteMode.OriginalTimingOnly; // Get the scale for custom scale x CS scale double csScale = Beatmap.GetHitObjectRadius(originalCircleSize) / Beatmap.GetHitObjectRadius(patternCircleSize); double spatialScale = ScaleToNewCircleSize && !double.IsNaN(csScale) ? CustomScale * csScale : CustomScale; // Get a BPM multiplier to fix the tick rate // This multiplier is not meant to change SV so this is subtracted from the greenline SV later double bpmMultiplier = FixTickRate ? patternTickRate / originalTickRate : 1; // Dont give new combo to all hit objects which were actually new combo in the pattern, // because it leads to unexpected NC's at the start of patterns. // Collect Kiai toggles List <TimingPoint> kiaiToggles = new List <TimingPoint>(); bool lastKiai = false; // If not including the kiai of the pattern, add the kiai of the original map. // This has to be done because this part of the original map might get deleted. foreach (TimingPoint tp in IncludeKiai ? patternTiming.TimingPoints : originalTiming.TimingPoints) { if (tp.Kiai != lastKiai || kiaiToggles.Count == 0) { kiaiToggles.Add(tp.Copy()); lastKiai = tp.Kiai; } } // Collect SliderVelocity changes for mania/taiko List <TimingPoint> svChanges = new List <TimingPoint>(); double lastSV = -100; // If not including the SV of the pattern, add the SV of the original map. // This has to be done because this part of the original map might get deleted. foreach (TimingPoint tp in includePatternSliderVelocity ? patternTiming.TimingPoints : originalTiming.TimingPoints) { if (tp.Uninherited) { lastSV = -100; } else { if (Math.Abs(tp.MpB - lastSV) > Precision.DOUBLE_EPSILON) { svChanges.Add(tp.Copy()); lastSV = tp.MpB; } } } // If not including the SV of the pattern, set the SV of sliders to that of the original beatmap, // so the pattern will take over the SV of the original beatmap. if (!includePatternSliderVelocity) { foreach (var ho in patternBeatmap.HitObjects.Where(ho => ho.IsSlider)) { ho.SliderVelocity = originalTiming.GetSvAtTime(ho.Time); } } // Get the timeline before moving all objects so it has the correct hitsounds // Make sure that moving the objects in the pattern moves the timeline objects aswell // This method is NOT safe to use in beat time Timeline patternTimeline = patternBeatmap.GetTimeline(); Timing transformOriginalTiming = originalTiming; Timing transformPatternTiming = patternTiming; if (scaleToNewTiming) { // Transform everything to beat time relative to pattern start time foreach (var ho in patternBeatmap.HitObjects) { double oldEndTime = ho.GetEndTime(false); ho.Time = patternTiming.GetBeatLength(patternStartTime, ho.Time); ho.EndTime = patternTiming.GetBeatLength(patternStartTime, oldEndTime); // The body hitsounds are not copies of timingpoints in patternTiming so they should be copied before changing offset for (int i = 0; i < ho.BodyHitsounds.Count; i++) { TimingPoint tp = ho.BodyHitsounds[i].Copy(); tp.Offset = patternTiming.GetBeatLength(patternStartTime, tp.Offset); ho.BodyHitsounds[i] = tp; } } foreach (var tp in kiaiToggles.Concat(svChanges)) { tp.Offset = patternTiming.GetBeatLength(patternStartTime, tp.Offset); } // Transform the pattern redlines to beat time // This will not change the order of redlines (unless negative BPM exists) transformPatternTiming = patternTiming.Copy(); foreach (var tp in transformPatternTiming.Redlines) { tp.Offset = patternTiming.GetBeatLength(patternStartTime, tp.Offset); } // Transform the original timingpoints to beat time // This will not change the order of timingpoints (unless negative BPM exists) transformOriginalTiming = originalTiming.Copy(); foreach (var tp in transformOriginalTiming.TimingPoints) { tp.Offset = originalTiming.GetBeatLength(patternStartTime, tp.Offset); } } // Fix SV for the new global SV var globalSvFactor = transformOriginalTiming.SliderMultiplier / transformPatternTiming.SliderMultiplier; if (FixGlobalSv) { foreach (HitObject ho in patternBeatmap.HitObjects.Where(o => o.IsSlider)) { ho.SliderVelocity *= globalSvFactor; } foreach (TimingPoint tp in svChanges) { tp.MpB *= globalSvFactor; } } else { foreach (HitObject ho in patternBeatmap.HitObjects.Where(o => o.IsSlider)) { ho.TemporalLength /= globalSvFactor; } } // Partition the pattern based on the timing in the pattern if (PatternOverwriteMode == PatternOverwriteMode.PartitionedOverwrite) { parts = PartitionBeatmap(patternBeatmap, scaleToNewTiming); } else { parts = new List <Part> { new Part(patternBeatmap.HitObjects[0].Time, patternBeatmap.HitObjects[patternBeatmap.HitObjects.Count - 1].Time, patternBeatmap.HitObjects) }; } // Construct a new timing which is a mix of the beatmap and the pattern. // If scaleToNewTiming then use beat relative values to determine the duration of timing sections in the pattern. // scaleToNewTiming must scale all the partitions, timingpoints, hitobjects, and events (if applicable). Timing newTiming = new Timing(transformOriginalTiming.SliderMultiplier); var lastEndTime = double.NegativeInfinity; foreach (var part in parts) { var startTime = part.StartTime; var endTime = part.EndTime; // Subtract one to omit BPM changes right on the end of the part. // Add the redlines in between patterns newTiming.AddRange(transformOriginalTiming.GetRedlinesInRange(lastEndTime, startTime, false)); var startOriginalRedline = transformOriginalTiming.GetRedlineAtTime(startTime); // Minus 1 the offset so its possible to have a custom BPM redline right on the start time if you have // the default BPM redline before it. var patternDefaultMpb = transformPatternTiming.GetMpBAtTime(startTime - 2 * Precision.DOUBLE_EPSILON); TimingPoint[] inPartRedlines; TimingPoint startPartRedline; switch (timingOverwriteMode) { case TimingOverwriteMode.PatternTimingOnly: // Subtract one from the end time to omit BPM changes right on the end of the part. inPartRedlines = transformPatternTiming.GetRedlinesInRange(startTime, Math.Max(startTime, endTime - 2 * Precision.DOUBLE_EPSILON)).ToArray(); startPartRedline = transformPatternTiming.GetRedlineAtTime(startTime); break; case TimingOverwriteMode.InPatternAbsoluteTiming: var tempInPartRedlines = transformPatternTiming.GetRedlinesInRange(startTime, endTime - 2 * Precision.DOUBLE_EPSILON); // Replace all parts in the pattern which have the default BPM to timing from the target beatmap. inPartRedlines = tempInPartRedlines.Select(tp => { if (Precision.AlmostEquals(tp.MpB, patternDefaultMpb)) { var tp2 = transformOriginalTiming.GetRedlineAtTime(tp.Offset).Copy(); tp2.Offset = tp2.Offset; return(tp2); } return(tp); }).ToArray(); startPartRedline = startOriginalRedline; break; case TimingOverwriteMode.InPatternRelativeTiming: // Multiply mix the pattern timing and the original timing together. // The pattern timing divided by the default BPM will be used as a scalar for the original timing. var tempInPartRedlines2 = transformPatternTiming.GetRedlinesInRange(startTime, endTime - 2 * Precision.DOUBLE_EPSILON); var tempInOriginalRedlines = transformOriginalTiming.GetRedlinesInRange(startTime, endTime - 2 * Precision.DOUBLE_EPSILON); // Replace all parts in the pattern which have the default BPM to timing from the target beatmap. inPartRedlines = tempInPartRedlines2.Select(tp => { var tp2 = tp.Copy(); tp2.MpB *= transformOriginalTiming.GetMpBAtTime(tp.Offset) / patternDefaultMpb; return(tp2); }).Concat(tempInOriginalRedlines.Select(tp => { var tp2 = tp.Copy(); tp2.MpB *= transformPatternTiming.GetMpBAtTime(tp.Offset) / patternDefaultMpb; return(tp2); })).ToArray(); startPartRedline = transformPatternTiming.GetRedlineAtTime(startTime).Copy(); startPartRedline.MpB *= transformOriginalTiming.GetMpBAtTime(startTime) / patternDefaultMpb; break; default: // Original timing only // Subtract one from the end time to omit BPM changes right on the end of the part. inPartRedlines = transformOriginalTiming.GetRedlinesInRange(startTime, Math.Max(startTime, endTime - 2 * Precision.DOUBLE_EPSILON)).ToArray(); startPartRedline = transformOriginalTiming.GetRedlineAtTime(startTime); break; } // Add the redlines for inside the part newTiming.AddRange(inPartRedlines); // If the pattern starts with different BPM than the map add an extra redline at the start of the pattern // to make sure it the pattern starts out at the right BPM as we only copy the timingpoints during the pattern itself // and the redline may be way before that. // This will probably only do something on the PatternTimingOnly mode as the other modes make sure // the BPM at the start of the pattern will be the same as the original beatmap anyways. if (Math.Abs(startPartRedline.MpB * bpmMultiplier - startOriginalRedline.MpB) > Precision.DOUBLE_EPSILON) { // We dont have to add the redline again if its already during the pattern. if (Math.Abs(startPartRedline.Offset - startTime) > Precision.DOUBLE_EPSILON) { var copy = startPartRedline.Copy(); copy.Offset = startTime; newTiming.Add(copy); } } // Fix SV for the new BPM, so the SV effect of the new BPM is cancelled if (FixBpmSv) { if (scaleToNewTiming) { foreach (HitObject ho in patternBeatmap.HitObjects.Where(o => o.IsSlider)) { var bpmSvFactor = SnapToNewTiming ? transformPatternTiming.GetMpBAtTime(ho.Time) / newTiming.GetMpBAtTime(newTiming.ResnapBeatTime(ho.Time, BeatDivisors)) : transformPatternTiming.GetMpBAtTime(ho.Time) / newTiming.GetMpBAtTime(ho.Time); ho.SliderVelocity *= bpmSvFactor; } } else { foreach (HitObject ho in patternBeatmap.HitObjects.Where(o => o.IsSlider)) { var bpmSvFactor = SnapToNewTiming ? transformPatternTiming.GetMpBAtTime(ho.Time) / newTiming.GetMpBAtTime(newTiming.Resnap(ho.Time, BeatDivisors)) : transformPatternTiming.GetMpBAtTime(ho.Time) / newTiming.GetMpBAtTime(ho.Time); ho.SliderVelocity *= bpmSvFactor; } } } // Recalculate temporal length and re-assign redline for the sliderend resnapping later foreach (var ho in part.HitObjects) { ho.UnInheritedTimingPoint = newTiming.GetRedlineAtTime(ho.Time); if (ho.IsSlider) { // If scaleToNewTiming then the end time is already at the correct beat time // The SV has to be adjusted so the sliderend is really on the end time if (scaleToNewTiming) { var wantedMsDuration = (newTiming.GetMilliseconds(ho.GetEndTime(false), patternStartTime) - newTiming.GetMilliseconds(ho.Time, patternStartTime)) / ho.Repeat; var trueMsDuration = newTiming.CalculateSliderTemporalLength(SnapToNewTiming ? newTiming.ResnapBeatTime(ho.Time, BeatDivisors) : ho.Time, ho.PixelLength, ho.SliderVelocity); ho.SliderVelocity /= trueMsDuration / wantedMsDuration; } else { ho.TemporalLength = newTiming.CalculateSliderTemporalLength(SnapToNewTiming ? newTiming.Resnap(ho.Time, BeatDivisors) : ho.Time, ho.PixelLength, ho.SliderVelocity); } } } // Update the end time because the lengths of sliders changed endTime = part.HitObjects.Max(o => o.GetEndTime(!scaleToNewTiming)); part.EndTime = endTime; // Add a redline at the end of the pattern to make sure the BPM goes back to normal after the pattern. var endOriginalRedline = transformOriginalTiming.GetRedlineAtTime(endTime); var endPartRedline = inPartRedlines.LastOrDefault() ?? startPartRedline; if (Math.Abs(endPartRedline.MpB * bpmMultiplier - endOriginalRedline.MpB) > Precision.DOUBLE_EPSILON) { // We dont have to add the redline again if its already during the parts in between parts. if (Math.Abs(endOriginalRedline.Offset - endTime) > Precision.DOUBLE_EPSILON) { var copy = endOriginalRedline.Copy(); copy.Offset = endTime; newTiming.Add(copy); } } lastEndTime = endTime; } // Transform the beat time back to millisecond time Timing transformNewTiming = newTiming; if (scaleToNewTiming) { // Transform back the timing transformNewTiming = newTiming.Copy(); foreach (var tp in transformNewTiming.TimingPoints) { tp.Offset = Math.Floor(newTiming.GetMilliseconds(tp.Offset, patternStartTime) + Precision.DOUBLE_EPSILON); } // Transform back the parts foreach (Part part in parts) { part.StartTime = Math.Floor(newTiming.GetMilliseconds(part.StartTime, patternStartTime)); part.EndTime = Math.Floor(newTiming.GetMilliseconds(part.EndTime, patternStartTime)); } // Transform everything to millisecond time relative to pattern start time foreach (var ho in patternBeatmap.HitObjects) { // Calculate the millisecond end time before changing the start time because the end time getter uses the beat time start time var msEndTime = newTiming.GetMilliseconds(ho.GetEndTime(false), patternStartTime); ho.Time = newTiming.GetMilliseconds(ho.Time, patternStartTime); // End time has to be set after the time because the end time setter uses the millisecond start time ho.EndTime = msEndTime; foreach (var tp in ho.BodyHitsounds) { tp.Offset = newTiming.GetMilliseconds(tp.Offset, patternStartTime); } // It is necessary to resnap early so it can recalculate the duration using the right offset if (SnapToNewTiming) { ho.ResnapSelf(transformNewTiming, BeatDivisors); } if (ho.IsSlider) { ho.CalculateSliderTemporalLength(transformNewTiming, true); } ho.UnInheritedTimingPoint = transformNewTiming.GetRedlineAtTime(ho.Time); ho.UpdateTimelineObjectTimes(); } foreach (var tp in kiaiToggles.Concat(svChanges)) { tp.Offset = Math.Floor(newTiming.GetMilliseconds(tp.Offset, patternStartTime)); } } // Apply custom scale and rotate if (Math.Abs(spatialScale - 1) > Precision.DOUBLE_EPSILON || Math.Abs(CustomRotate) > Precision.DOUBLE_EPSILON) { // Create a transformation matrix for the custom scale and rotate // The rotation is inverted because the default osu! rotation goes clockwise Matrix2 transform = Matrix2.Mult(Matrix2.CreateScale(spatialScale), Matrix2.CreateRotation(-CustomRotate)); Vector2 centre = new Vector2(256, 192); foreach (var ho in patternBeatmap.HitObjects) { ho.Move(-centre); ho.Transform(transform); ho.Move(centre); // Scale pixel length and SV for sliders aswell if (ho.IsSlider) { ho.PixelLength *= spatialScale; ho.SliderVelocity /= spatialScale; } } // osu! clips coordinates to the bounds (0,512), so there is some space downwards to still place the pattern // Calculate the new bounds of the pattern and try to place it in the playfield var minX = patternBeatmap.HitObjects.Min(o => o.Pos.X); var minY = patternBeatmap.HitObjects.Min(o => o.Pos.Y); Vector2 offset = new Vector2(Math.Max(-minX, 0), Math.Max(-minY, 0)); if (offset.LengthSquared > 0) { foreach (var ho in patternBeatmap.HitObjects) { ho.Move(offset); } } } // Manualify stacks if (FixStackLeniency) { // If scale to new timing was used update the circle size of the pattern, // so it calculates stacks at the new size of the pattern. if (ScaleToNewCircleSize) { patternBeatmap.Difficulty["CircleSize"].DoubleValue = originalCircleSize; } patternBeatmap.CalculateEndPositions(); patternBeatmap.UpdateStacking(rounded: true); // Manualify by setting the base position to the stacked position foreach (var ho in patternBeatmap.HitObjects) { var offset = ho.StackedPos - ho.Pos; ho.Move(offset); } } // Resnap everything to the new timing. if (SnapToNewTiming) { // Resnap all objects foreach (HitObject ho in patternBeatmap.HitObjects) { ho.ResnapSelf(transformNewTiming, BeatDivisors); ho.ResnapEnd(transformNewTiming, BeatDivisors); ho.ResnapPosition(targetMode, patternCircleSize); // Resnap to column X positions for mania only } // Resnap Kiai toggles foreach (TimingPoint tp in kiaiToggles) { tp.ResnapSelf(transformNewTiming, BeatDivisors); } // Resnap SliderVelocity changes foreach (TimingPoint tp in svChanges) { tp.ResnapSelf(transformNewTiming, BeatDivisors); } } // Multiply BPM and divide SV foreach (var part in parts) { foreach (var tp in transformNewTiming.GetRedlinesInRange(part.StartTime - 2 * Precision.DOUBLE_EPSILON, part.EndTime, false)) { tp.MpB /= bpmMultiplier; // MpB is the inverse of the BPM } foreach (var ho in part.HitObjects) { ho.SliderVelocity *= bpmMultiplier; // SliderVelocity is the inverse of the multiplier } } // Make new timingpoints changes for the hitsounds and other stuff // Add redlines timingPointsChanges = transformNewTiming.Redlines.Select(tp => new TimingPointsChange(tp, mpb: true, meter: true, unInherited: true, omitFirstBarLine: true, fuzzyness: Precision.DOUBLE_EPSILON)).ToList(); // Add SliderVelocity changes for taiko and mania if (includePatternSliderVelocity && (targetMode == GameMode.Taiko || targetMode == GameMode.Mania)) { timingPointsChanges.AddRange(svChanges.Select(tp => new TimingPointsChange(tp, mpb: true, fuzzyness: 0.4))); } // Add Kiai toggles timingPointsChanges.AddRange(kiaiToggles.Select(tp => new TimingPointsChange(tp, kiai: true))); // Add Hitobject stuff foreach (HitObject ho in patternBeatmap.HitObjects) { if (ho.IsSlider) // SliderVelocity changes { TimingPoint tp = ho.TimingPoint.Copy(); tp.Offset = ho.Time; tp.MpB = ho.SliderVelocity; timingPointsChanges.Add(new TimingPointsChange(tp, mpb: true, fuzzyness: 0.4)); } if (!IncludeHitsounds) { // Remove hitsounds and skip adding body hitsounds ho.ResetHitsounds(); continue; } if (includeTimingPointHitsounds) { // Body hitsounds bool vol = ho.IsSlider || ho.IsSpinner; bool sam = ho.IsSlider && ho.SampleSet == 0; bool ind = ho.IsSlider; timingPointsChanges.AddRange(ho.BodyHitsounds.Select(tp => new TimingPointsChange(tp, volume: vol, index: ind, sampleset: sam))); } } // Add timeline hitsounds if (includeTimingPointHitsounds) { foreach (TimelineObject tlo in patternTimeline.TimelineObjects) { if (tlo.HasHitsound) { // Add greenlines for hitsounds TimingPoint tp = tlo.HitsoundTimingPoint.Copy(); tp.Offset = tlo.Time; timingPointsChanges.Add(new TimingPointsChange(tp, sampleset: true, volume: true, index: true)); } } } // Replace the old timingpoints patternTiming.Clear(); TimingPointsChange.ApplyChanges(patternTiming, timingPointsChanges); patternBeatmap.GiveObjectsGreenlines(); patternBeatmap.CalculateSliderEndTimes(); }
/// <summary> /// Compiles all data necessary for rendering into a Vertex object /// </summary> public Vertex[] CompileData(Color4 ObjectColor, Scene Scene) { Vector2[] _coordinates = Vertices; Vector2 _aspectRatioFactor = new((float)Camera.Resolution.Y / Camera.Resolution.X, 1); // -> Local <- Matrix2.CreateRotation(Rotation, out var _localRotation); Vector2 _localSkew = Skew / GlobalScale; Vector2 _localSize = Size / GlobalScale * new Vector2(Camera.Resolution.X / 720f); Vector2 _localPosition = (Position / GlobalScale) + Vector2.Divide(Anchor.Xy, _aspectRatioFactor); // -> Parent <- Matrix2.CreateRotation(ParentObject?.Rotation ?? 0, out var _parentRotation); Vector2 _parentSize = ParentObject?.Size / GlobalScale ?? Vector2.One; Vector2 _parentPosition = ParentObject?.Position / GlobalScale ?? Vector2.Zero; // -> Scene <- Matrix2.CreateRotation(Scene.Rotation, out var _sceneRotation); Vector2 _sceneSize = Scene.Scale; Vector2 _scenePosition = Scene.Position / GlobalScale; // -> Global <- Matrix2.CreateRotation(-Camera.Rotation, out var _globalRotation); Vector2 _globalSize = Camera.Zoom; Vector2 _globalPosition = Camera.Position; // -> Local <- _coordinates = Array.ConvertAll(_coordinates, vec => vec + vec + _localSkew * vec.Yx); _coordinates = Array.ConvertAll(_coordinates, vec => vec * _localRotation); _coordinates = Array.ConvertAll(_coordinates, vec => vec * _localSize); _coordinates = Array.ConvertAll(_coordinates, vec => vec + _localPosition); // -> Parent <- _coordinates = Array.ConvertAll(_coordinates, vec => vec * _parentRotation); // Make this optional? //_coordinates = Array.ConvertAll(_coordinates, vec => vec * _parentSize); _coordinates = Array.ConvertAll(_coordinates, vec => vec + _parentPosition); // -> Scene <- _coordinates = Array.ConvertAll(_coordinates, vec => vec * _sceneRotation); _coordinates = Array.ConvertAll(_coordinates, vec => vec * _sceneSize); _coordinates = Array.ConvertAll(_coordinates, vec => vec + _scenePosition); // -> Global <- _coordinates = Array.ConvertAll(_coordinates, vec => vec * _globalRotation); _coordinates = Array.ConvertAll(_coordinates, vec => vec * _globalSize); _coordinates = Array.ConvertAll(_coordinates, vec => vec + _globalPosition); _coordinates = Array.ConvertAll(_coordinates, vec => vec * _aspectRatioFactor); List <Vertex> _output = new(); // New C# 9 syntax, nice! for (int i = 0; i < Vertices.Length; i++) { _output.Add(new Vertex { Position = _coordinates[i], UV = UV[i], InnerBounds = UV[i], Color = ObjectColor }); } return(_output.ToArray()); }
public RelevantCircle GetRelevantObjects(RelevantPoint origin, RelevantCircle circle) { return(!MySettings.OriginInputPredicate.Check(origin, this) || !MySettings.OtherInputPredicate.Check(circle, this) ? null : new RelevantCircle(new Circle(Matrix2.Mult(Matrix2.CreateRotation(MathHelper.DegreesToRadians(MySettings.Angle)), circle.Child.Centre - origin.Child) * MySettings.Scalar + origin.Child, circle.Child.Radius * MySettings.Scalar))); }
public RelevantLine GetRelevantObjects(RelevantPoint origin, RelevantLine line) { return(!MySettings.OriginInputPredicate.Check(origin, this) || !MySettings.OtherInputPredicate.Check(line, this) ? null : new RelevantLine(Line2.FromPoints(Matrix2.Mult(Matrix2.CreateRotation(MathHelper.DegreesToRadians(MySettings.Angle)), line.Child.PositionVector - origin.Child) * MySettings.Scalar + origin.Child, Matrix2.Mult(Matrix2.CreateRotation(MathHelper.DegreesToRadians(MySettings.Angle)), line.Child.PositionVector + line.Child.DirectionVector - origin.Child) * MySettings.Scalar + origin.Child))); }