2022-09-18 19:08:34 +00:00
|
|
|
|
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
|
|
|
|
// See the LICENCE file in the repository root for full licence text.
|
|
|
|
|
|
2022-09-18 19:10:01 +00:00
|
|
|
|
using System;
|
2022-09-18 19:08:34 +00:00
|
|
|
|
using System.Collections.Generic;
|
|
|
|
|
using System.Diagnostics;
|
|
|
|
|
using osu.Framework.Allocation;
|
2022-09-18 19:10:01 +00:00
|
|
|
|
using osu.Framework.Extensions.Color4Extensions;
|
2022-09-18 19:08:34 +00:00
|
|
|
|
using osu.Framework.Graphics;
|
2022-09-18 19:10:01 +00:00
|
|
|
|
using osu.Framework.Graphics.Colour;
|
|
|
|
|
using osu.Framework.Graphics.Primitives;
|
|
|
|
|
using osu.Framework.Graphics.Rendering;
|
|
|
|
|
using osu.Framework.Graphics.Rendering.Vertices;
|
|
|
|
|
using osu.Framework.Graphics.Shaders;
|
|
|
|
|
using osu.Framework.Graphics.Textures;
|
|
|
|
|
using osu.Framework.Timing;
|
|
|
|
|
using osu.Framework.Utils;
|
2022-09-18 19:08:34 +00:00
|
|
|
|
using osu.Game.Rulesets.Osu.UI;
|
|
|
|
|
using osuTK;
|
2022-09-18 19:10:01 +00:00
|
|
|
|
using osuTK.Graphics;
|
2022-09-18 19:08:34 +00:00
|
|
|
|
|
|
|
|
|
namespace osu.Game.Rulesets.Osu.Skinning
|
|
|
|
|
{
|
2022-09-18 19:10:01 +00:00
|
|
|
|
public abstract class Smoke : Drawable, ITexturedShaderDrawable
|
2022-09-18 19:08:34 +00:00
|
|
|
|
{
|
2022-09-18 19:10:01 +00:00
|
|
|
|
public IShader? TextureShader { get; private set; }
|
|
|
|
|
public IShader? RoundedTextureShader { get; private set; }
|
|
|
|
|
|
|
|
|
|
private float radius = 1;
|
2022-09-19 01:32:33 +00:00
|
|
|
|
|
2022-09-18 19:10:01 +00:00
|
|
|
|
protected float Radius
|
|
|
|
|
{
|
|
|
|
|
get => radius;
|
|
|
|
|
set
|
|
|
|
|
{
|
|
|
|
|
if (radius == value)
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
radius = value;
|
|
|
|
|
Invalidate(Invalidation.DrawNode);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private Texture? texture;
|
2022-09-19 01:32:33 +00:00
|
|
|
|
|
2022-09-18 19:10:01 +00:00
|
|
|
|
protected Texture? Texture
|
|
|
|
|
{
|
|
|
|
|
get => texture;
|
|
|
|
|
set
|
|
|
|
|
{
|
|
|
|
|
texture = value;
|
|
|
|
|
Invalidate(Invalidation.DrawNode);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private double smokeTimeStart = double.MinValue;
|
2022-09-19 01:32:33 +00:00
|
|
|
|
|
2022-09-18 19:10:01 +00:00
|
|
|
|
protected double SmokeStartTime
|
|
|
|
|
{
|
|
|
|
|
get => smokeTimeStart;
|
|
|
|
|
private set
|
|
|
|
|
{
|
|
|
|
|
if (smokeTimeStart == value)
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
smokeTimeStart = value;
|
|
|
|
|
Invalidate(Invalidation.DrawNode);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private double smokeTimeEnd = double.MaxValue;
|
2022-09-19 01:32:33 +00:00
|
|
|
|
|
2022-09-18 19:10:01 +00:00
|
|
|
|
protected double SmokeEndTime
|
|
|
|
|
{
|
|
|
|
|
get => smokeTimeEnd;
|
|
|
|
|
private set
|
|
|
|
|
{
|
|
|
|
|
if (smokeTimeEnd == value)
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
smokeTimeEnd = value;
|
|
|
|
|
Invalidate(Invalidation.DrawNode);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public override IFrameBasedClock Clock
|
|
|
|
|
{
|
|
|
|
|
get => base.Clock;
|
|
|
|
|
set
|
|
|
|
|
{
|
|
|
|
|
base.Clock = value;
|
|
|
|
|
Invalidate(Invalidation.DrawNode);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private Vector2 topLeft;
|
2022-09-19 01:32:33 +00:00
|
|
|
|
|
2022-09-18 19:10:01 +00:00
|
|
|
|
protected Vector2 TopLeft
|
|
|
|
|
{
|
|
|
|
|
get => topLeft;
|
|
|
|
|
set
|
|
|
|
|
{
|
|
|
|
|
if (topLeft == value)
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
topLeft = value;
|
2022-09-19 01:32:33 +00:00
|
|
|
|
Invalidate();
|
2022-09-18 19:10:01 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private Vector2 bottomRight;
|
2022-09-19 01:32:33 +00:00
|
|
|
|
|
2022-09-18 19:10:01 +00:00
|
|
|
|
protected Vector2 BottomRight
|
|
|
|
|
{
|
|
|
|
|
get => bottomRight;
|
|
|
|
|
set
|
|
|
|
|
{
|
|
|
|
|
if (bottomRight == value)
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
bottomRight = value;
|
|
|
|
|
Invalidate(Invalidation.Layout);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
protected abstract double LifetimeAfterSmokeEnd { get; }
|
|
|
|
|
protected virtual float PointInterval => Radius * 7f / 8;
|
2022-09-18 19:08:34 +00:00
|
|
|
|
protected bool IsActive { get; private set; }
|
|
|
|
|
|
2022-09-18 19:10:01 +00:00
|
|
|
|
protected readonly List<SmokePoint> SmokePoints = new List<SmokePoint>();
|
|
|
|
|
|
|
|
|
|
private float totalDistance;
|
2022-09-19 01:32:33 +00:00
|
|
|
|
private Vector2? lastPosition;
|
2022-09-18 19:10:01 +00:00
|
|
|
|
|
|
|
|
|
private const double max_duration = 60_000;
|
|
|
|
|
|
|
|
|
|
public override float Height
|
|
|
|
|
{
|
|
|
|
|
get => base.Height = BottomRight.Y - TopLeft.Y;
|
|
|
|
|
set => throw new InvalidOperationException($"Cannot manually set {nameof(Height)} of {nameof(Smoke)}.");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public override float Width
|
|
|
|
|
{
|
|
|
|
|
get => base.Width = BottomRight.X - TopLeft.X;
|
|
|
|
|
set => throw new InvalidOperationException($"Cannot manually set {nameof(Width)} of {nameof(Smoke)}.");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public override Vector2 Size
|
|
|
|
|
{
|
|
|
|
|
get => base.Size = BottomRight - TopLeft;
|
|
|
|
|
set => throw new InvalidOperationException($"Cannot manually set {nameof(Size)} of {nameof(Smoke)}.");
|
|
|
|
|
}
|
2022-09-18 19:08:34 +00:00
|
|
|
|
|
|
|
|
|
[Resolved(CanBeNull = true)]
|
|
|
|
|
private SmokeContainer? smokeContainer { get; set; }
|
|
|
|
|
|
2022-09-18 19:10:01 +00:00
|
|
|
|
[BackgroundDependencyLoader]
|
|
|
|
|
private void load(ShaderManager shaders)
|
2022-09-18 19:08:34 +00:00
|
|
|
|
{
|
2022-09-18 19:10:01 +00:00
|
|
|
|
RoundedTextureShader = shaders.Load(VertexShaderDescriptor.TEXTURE_2, FragmentShaderDescriptor.TEXTURE_ROUNDED);
|
|
|
|
|
TextureShader = shaders.Load(VertexShaderDescriptor.TEXTURE_2, FragmentShaderDescriptor.TEXTURE);
|
2022-09-18 19:08:34 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
protected override void LoadComplete()
|
|
|
|
|
{
|
|
|
|
|
base.LoadComplete();
|
|
|
|
|
|
|
|
|
|
if (smokeContainer != null)
|
|
|
|
|
{
|
2022-09-18 19:10:01 +00:00
|
|
|
|
smokeContainer.SmokeMoved += onSmokeMoved;
|
|
|
|
|
smokeContainer.SmokeEnded += onSmokeEnded;
|
2022-09-18 19:08:34 +00:00
|
|
|
|
IsActive = true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Anchor = Anchor.TopLeft;
|
|
|
|
|
Origin = Anchor.TopLeft;
|
|
|
|
|
|
2022-09-18 19:10:01 +00:00
|
|
|
|
SmokeStartTime = Time.Current;
|
2022-09-18 19:08:34 +00:00
|
|
|
|
}
|
|
|
|
|
|
2022-09-19 05:06:07 +00:00
|
|
|
|
private Vector2 nextPointDirection()
|
|
|
|
|
{
|
|
|
|
|
float angle = RNG.NextSingle(0, 2 * MathF.PI);
|
|
|
|
|
return new Vector2(MathF.Sin(angle), -MathF.Cos(angle));
|
|
|
|
|
}
|
|
|
|
|
|
2022-09-18 19:10:01 +00:00
|
|
|
|
private void onSmokeMoved(Vector2 position, double time)
|
2022-09-18 19:08:34 +00:00
|
|
|
|
{
|
2022-09-18 19:10:01 +00:00
|
|
|
|
if (!IsActive)
|
|
|
|
|
return;
|
2022-09-18 19:08:34 +00:00
|
|
|
|
|
2022-09-18 19:10:01 +00:00
|
|
|
|
lastPosition ??= position;
|
2022-09-18 19:08:34 +00:00
|
|
|
|
|
2022-09-18 19:10:01 +00:00
|
|
|
|
float delta = (position - (Vector2)lastPosition).LengthFast;
|
|
|
|
|
totalDistance += delta;
|
|
|
|
|
int count = (int)(totalDistance / PointInterval);
|
|
|
|
|
|
|
|
|
|
if (count > 0)
|
|
|
|
|
{
|
|
|
|
|
Vector2 increment = position - (Vector2)lastPosition;
|
|
|
|
|
increment.NormalizeFast();
|
|
|
|
|
|
|
|
|
|
Vector2 pointPos = (PointInterval - (totalDistance - delta)) * increment + (Vector2)lastPosition;
|
|
|
|
|
increment *= PointInterval;
|
|
|
|
|
|
|
|
|
|
if (SmokePoints.Count > 0 && SmokePoints[^1].Time > time)
|
|
|
|
|
{
|
|
|
|
|
int index = ~SmokePoints.BinarySearch(new SmokePoint { Time = time }, new SmokePoint.UpperBoundComparer());
|
|
|
|
|
SmokePoints.RemoveRange(index, SmokePoints.Count - index);
|
|
|
|
|
recalculateBounds();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
totalDistance %= PointInterval;
|
2022-09-19 01:32:33 +00:00
|
|
|
|
|
2022-09-18 19:10:01 +00:00
|
|
|
|
for (int i = 0; i < count; i++)
|
|
|
|
|
{
|
|
|
|
|
SmokePoints.Add(new SmokePoint
|
|
|
|
|
{
|
|
|
|
|
Position = pointPos,
|
|
|
|
|
Time = time,
|
2022-09-19 05:06:07 +00:00
|
|
|
|
Direction = nextPointDirection(),
|
2022-09-18 19:10:01 +00:00
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
pointPos += increment;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Invalidate(Invalidation.DrawNode);
|
|
|
|
|
adaptBounds(position);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
lastPosition = position;
|
|
|
|
|
|
|
|
|
|
if (time - SmokeStartTime > max_duration)
|
|
|
|
|
onSmokeEnded(time);
|
2022-09-18 19:08:34 +00:00
|
|
|
|
}
|
|
|
|
|
|
2022-09-18 19:10:01 +00:00
|
|
|
|
private void recalculateBounds()
|
2022-09-18 19:08:34 +00:00
|
|
|
|
{
|
2022-09-18 19:10:01 +00:00
|
|
|
|
TopLeft = BottomRight = Vector2.Zero;
|
2022-09-18 19:08:34 +00:00
|
|
|
|
|
2022-09-18 19:10:01 +00:00
|
|
|
|
foreach (var point in SmokePoints)
|
|
|
|
|
adaptBounds(point.Position);
|
|
|
|
|
}
|
2022-09-18 19:08:34 +00:00
|
|
|
|
|
2022-09-18 19:10:01 +00:00
|
|
|
|
private void adaptBounds(Vector2 position)
|
|
|
|
|
{
|
|
|
|
|
if (position.X < TopLeft.X)
|
|
|
|
|
TopLeft = new Vector2(position.X, TopLeft.Y);
|
|
|
|
|
else if (position.X > BottomRight.X)
|
|
|
|
|
BottomRight = new Vector2(position.X, BottomRight.Y);
|
2022-09-18 19:08:34 +00:00
|
|
|
|
|
2022-09-18 19:10:01 +00:00
|
|
|
|
if (position.Y < TopLeft.Y)
|
|
|
|
|
TopLeft = new Vector2(TopLeft.X, position.Y);
|
|
|
|
|
else if (position.Y > BottomRight.Y)
|
|
|
|
|
BottomRight = new Vector2(BottomRight.X, position.Y);
|
2022-09-18 19:08:34 +00:00
|
|
|
|
}
|
|
|
|
|
|
2022-09-18 19:10:01 +00:00
|
|
|
|
private void onSmokeEnded(double time)
|
2022-09-18 19:08:34 +00:00
|
|
|
|
{
|
2022-09-18 19:10:01 +00:00
|
|
|
|
if (!IsActive)
|
|
|
|
|
return;
|
|
|
|
|
|
2022-09-18 19:08:34 +00:00
|
|
|
|
IsActive = false;
|
2022-09-18 19:10:01 +00:00
|
|
|
|
SmokeEndTime = time;
|
|
|
|
|
LifetimeEnd = time + LifetimeAfterSmokeEnd + 100;
|
2022-09-18 19:08:34 +00:00
|
|
|
|
}
|
|
|
|
|
|
2022-09-18 19:10:01 +00:00
|
|
|
|
protected abstract override DrawNode CreateDrawNode();
|
|
|
|
|
|
2022-09-18 19:08:34 +00:00
|
|
|
|
protected override void Update()
|
|
|
|
|
{
|
|
|
|
|
base.Update();
|
|
|
|
|
|
2022-09-18 19:10:01 +00:00
|
|
|
|
Position = TopLeft;
|
2022-09-18 19:08:34 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
protected override void Dispose(bool isDisposing)
|
|
|
|
|
{
|
|
|
|
|
base.Dispose(isDisposing);
|
|
|
|
|
|
|
|
|
|
if (smokeContainer != null)
|
|
|
|
|
{
|
2022-09-18 19:10:01 +00:00
|
|
|
|
smokeContainer.SmokeMoved -= onSmokeMoved;
|
|
|
|
|
smokeContainer.SmokeEnded -= onSmokeEnded;
|
2022-09-18 19:08:34 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2022-09-18 19:10:01 +00:00
|
|
|
|
protected struct SmokePoint
|
2022-09-18 19:08:34 +00:00
|
|
|
|
{
|
2022-09-18 19:10:01 +00:00
|
|
|
|
public Vector2 Position;
|
|
|
|
|
public double Time;
|
2022-09-19 05:06:07 +00:00
|
|
|
|
public Vector2 Direction;
|
2022-09-18 19:10:01 +00:00
|
|
|
|
|
|
|
|
|
public struct UpperBoundComparer : IComparer<SmokePoint>
|
|
|
|
|
{
|
|
|
|
|
public int Compare(SmokePoint x, SmokePoint target)
|
|
|
|
|
{
|
|
|
|
|
// By returning -1 when the target value is equal to x, guarantees that the
|
|
|
|
|
// element at BinarySearch's returned index will always be the first element
|
|
|
|
|
// larger. Since 0 is never returned, the target is never "found", so the return
|
|
|
|
|
// value will be the index's complement.
|
|
|
|
|
|
|
|
|
|
return x.Time > target.Time ? 1 : -1;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
protected abstract class SmokeDrawNode : TexturedShaderDrawNode
|
|
|
|
|
{
|
|
|
|
|
protected new Smoke Source => (Smoke)base.Source;
|
|
|
|
|
|
|
|
|
|
protected IVertexBatch<TexturedVertex2D>? QuadBatch;
|
|
|
|
|
|
|
|
|
|
protected readonly List<SmokePoint> Points = new List<SmokePoint>();
|
|
|
|
|
|
|
|
|
|
protected float Radius;
|
|
|
|
|
protected Vector2 DrawSize;
|
|
|
|
|
protected Vector2 PositionOffset;
|
|
|
|
|
protected Texture? Texture;
|
|
|
|
|
|
|
|
|
|
protected double SmokeStartTime;
|
|
|
|
|
protected double SmokeEndTime;
|
|
|
|
|
protected double CurrentTime;
|
|
|
|
|
|
|
|
|
|
protected RectangleF TextureRect;
|
|
|
|
|
|
|
|
|
|
private IFrameBasedClock? clock;
|
|
|
|
|
|
2022-09-19 01:32:33 +00:00
|
|
|
|
protected SmokeDrawNode(ITexturedShaderDrawable source)
|
2022-09-18 19:10:01 +00:00
|
|
|
|
: base(source)
|
|
|
|
|
{
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public override void ApplyState()
|
|
|
|
|
{
|
|
|
|
|
base.ApplyState();
|
|
|
|
|
|
|
|
|
|
Points.Clear();
|
|
|
|
|
Points.AddRange(Source.SmokePoints);
|
|
|
|
|
|
|
|
|
|
Radius = Source.Radius;
|
|
|
|
|
DrawSize = Source.DrawSize;
|
|
|
|
|
PositionOffset = Source.TopLeft;
|
|
|
|
|
Texture = Source.Texture;
|
|
|
|
|
clock = Source.Clock;
|
|
|
|
|
|
|
|
|
|
SmokeStartTime = Source.SmokeStartTime;
|
|
|
|
|
SmokeEndTime = Source.SmokeEndTime;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public sealed override void Draw(IRenderer renderer)
|
2022-09-18 19:08:34 +00:00
|
|
|
|
{
|
2022-09-18 19:10:01 +00:00
|
|
|
|
base.Draw(renderer);
|
|
|
|
|
|
|
|
|
|
if (Points.Count == 0)
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
QuadBatch ??= renderer.CreateQuadBatch<TexturedVertex2D>(7200, 10);
|
|
|
|
|
Texture ??= renderer.WhitePixel;
|
|
|
|
|
|
|
|
|
|
var shader = GetAppropriateShader(renderer);
|
2022-09-19 05:06:07 +00:00
|
|
|
|
|
|
|
|
|
renderer.SetBlend(BlendingParameters.Additive);
|
|
|
|
|
|
2022-09-18 19:10:01 +00:00
|
|
|
|
shader.Bind();
|
|
|
|
|
Texture.Bind();
|
|
|
|
|
|
|
|
|
|
UpdateDrawVariables(renderer);
|
|
|
|
|
UpdateVertexBuffer();
|
|
|
|
|
|
|
|
|
|
shader.Unbind();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
protected Color4 ColorAtPosition(Vector2 localPos) => DrawColourInfo.Colour.HasSingleColour
|
|
|
|
|
? ((SRGBColour)DrawColourInfo.Colour).Linear
|
|
|
|
|
: DrawColourInfo.Colour.Interpolate(Vector2.Divide(localPos, DrawSize)).Linear;
|
|
|
|
|
|
2022-09-19 05:06:07 +00:00
|
|
|
|
protected abstract Color4 PointColor(SmokePoint point);
|
|
|
|
|
|
|
|
|
|
protected abstract float PointScale(SmokePoint point);
|
|
|
|
|
|
|
|
|
|
protected abstract Vector2 PointDirection(SmokePoint point);
|
2022-09-18 19:10:01 +00:00
|
|
|
|
|
|
|
|
|
protected virtual void UpdateDrawVariables(IRenderer renderer)
|
|
|
|
|
{
|
|
|
|
|
Debug.Assert(clock != null);
|
|
|
|
|
Debug.Assert(Texture != null);
|
|
|
|
|
|
|
|
|
|
CurrentTime = clock.CurrentTime;
|
|
|
|
|
TextureRect = Texture.GetTextureRect();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
protected virtual void UpdateVertexBuffer()
|
|
|
|
|
{
|
|
|
|
|
foreach (var point in Points)
|
|
|
|
|
drawPointQuad(point);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private void drawPointQuad(SmokePoint point)
|
|
|
|
|
{
|
|
|
|
|
Debug.Assert(QuadBatch != null);
|
|
|
|
|
|
2022-09-19 05:06:07 +00:00
|
|
|
|
var color = PointColor(point);
|
|
|
|
|
float scale = PointScale(point);
|
|
|
|
|
var dir = PointDirection(point);
|
2022-09-18 19:10:01 +00:00
|
|
|
|
var ortho = dir.PerpendicularLeft;
|
|
|
|
|
|
2022-09-19 05:06:07 +00:00
|
|
|
|
var localTopLeft = point.Position + (Radius * scale * (-ortho - dir)) - PositionOffset;
|
|
|
|
|
var localTopRight = point.Position + (Radius * scale * (-ortho + dir)) - PositionOffset;
|
|
|
|
|
var localBotLeft = point.Position + (Radius * scale * (ortho - dir)) - PositionOffset;
|
|
|
|
|
var localBotRight = point.Position + (Radius * scale * (ortho + dir)) - PositionOffset;
|
2022-09-18 19:08:34 +00:00
|
|
|
|
|
2022-09-18 19:10:01 +00:00
|
|
|
|
QuadBatch.Add(new TexturedVertex2D
|
|
|
|
|
{
|
|
|
|
|
Position = Vector2Extensions.Transform(localTopLeft, DrawInfo.Matrix),
|
|
|
|
|
TexturePosition = TextureRect.TopLeft,
|
|
|
|
|
Colour = Color4Extensions.Multiply(ColorAtPosition(localTopLeft), color),
|
|
|
|
|
});
|
|
|
|
|
QuadBatch.Add(new TexturedVertex2D
|
|
|
|
|
{
|
|
|
|
|
Position = Vector2Extensions.Transform(localTopRight, DrawInfo.Matrix),
|
|
|
|
|
TexturePosition = TextureRect.TopRight,
|
|
|
|
|
Colour = Color4Extensions.Multiply(ColorAtPosition(localTopRight), color),
|
|
|
|
|
});
|
|
|
|
|
QuadBatch.Add(new TexturedVertex2D
|
|
|
|
|
{
|
|
|
|
|
Position = Vector2Extensions.Transform(localBotRight, DrawInfo.Matrix),
|
|
|
|
|
TexturePosition = TextureRect.BottomRight,
|
|
|
|
|
Colour = Color4Extensions.Multiply(ColorAtPosition(localBotRight), color),
|
|
|
|
|
});
|
|
|
|
|
QuadBatch.Add(new TexturedVertex2D
|
|
|
|
|
{
|
|
|
|
|
Position = Vector2Extensions.Transform(localBotLeft, DrawInfo.Matrix),
|
|
|
|
|
TexturePosition = TextureRect.BottomLeft,
|
|
|
|
|
Colour = Color4Extensions.Multiply(ColorAtPosition(localBotLeft), color),
|
|
|
|
|
});
|
2022-09-18 19:08:34 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|