271 lines
12 KiB
C#
271 lines
12 KiB
C#
#nullable enable
|
|
|
|
using System;
|
|
using System.Collections.Generic;
|
|
using System.Collections.Immutable;
|
|
using Microsoft.Xna.Framework;
|
|
using Microsoft.Xna.Framework.Graphics;
|
|
|
|
namespace Barotrauma
|
|
{
|
|
internal class CircuitBoxWireRenderer
|
|
{
|
|
private const int VertsPerQuad = 4, // how many points per quad
|
|
QuadsPerLine = 10, // how many quads per line
|
|
VertsPerLine = QuadsPerLine * VertsPerQuad, // how many points we need to draw all the quads for a single line
|
|
TotalVertsPerWire = VertsPerLine * 2; // we are drawing 2 lines
|
|
|
|
private readonly Texture2D texture;
|
|
|
|
private VertexPositionColorTexture[] verts = new VertexPositionColorTexture[TotalVertsPerWire];
|
|
private readonly Vector2[][] colliders = new Vector2[2][];
|
|
private SquareLine skeleton;
|
|
|
|
private Vector2 lastStart, lastEnd;
|
|
private Color lastColor;
|
|
private readonly Option<CircuitBoxWire> wire;
|
|
|
|
public CircuitBoxWireRenderer(Option<CircuitBoxWire> wire, Vector2 start, Vector2 end, Color color, Sprite? wireSprite)
|
|
{
|
|
this.wire = wire;
|
|
texture = wireSprite?.Texture ?? GUI.WhiteTexture;
|
|
Recompute(start, end, color);
|
|
}
|
|
|
|
private void UpdateColor(Color color)
|
|
{
|
|
for (int i = 0; i < TotalVertsPerWire; i++)
|
|
{
|
|
verts[i].Color = color;
|
|
}
|
|
|
|
lastColor = color;
|
|
}
|
|
|
|
public void Recompute(Vector2 start, Vector2 end, Color color)
|
|
{
|
|
if (MathUtils.NearlyEqual(lastStart, start) && MathUtils.NearlyEqual(lastEnd, end))
|
|
{
|
|
if (lastColor == color) { return; }
|
|
|
|
UpdateColor(color);
|
|
return;
|
|
}
|
|
|
|
lastStart = start;
|
|
lastEnd = end;
|
|
lastColor = color;
|
|
|
|
skeleton = ToolBox.GetSquareLineBetweenPoints(start, end, CircuitBoxSizes.WireKnobLength);
|
|
var points = skeleton.Points;
|
|
|
|
Vector2 centerOfLine = (points[2] + points[3]) / 2f;
|
|
|
|
ImmutableArray<Vector2> points1 = GetLinePoints(points[1], points[2], centerOfLine),
|
|
points2 = GetLinePoints(centerOfLine, points[3], points[4]);
|
|
|
|
colliders[0] = ConstructQuads(ref verts, 0, points1, color);
|
|
colliders[1] = ConstructQuads(ref verts, VertsPerLine, points2, color);
|
|
|
|
static ImmutableArray<Vector2> GetLinePoints(Vector2 start, Vector2 control, Vector2 end)
|
|
{
|
|
var points = ImmutableArray.CreateBuilder<Vector2>(QuadsPerLine);
|
|
for (int i = 0; i < QuadsPerLine; i++)
|
|
{
|
|
float t = (float)i / (QuadsPerLine - 1);
|
|
Vector2 pos = MathUtils.Bezier(start, control, end, t);
|
|
points.Add(pos);
|
|
}
|
|
|
|
return points.ToImmutable();
|
|
}
|
|
|
|
static Vector2[] ConstructQuads(ref VertexPositionColorTexture[] verts, int startOffset, IReadOnlyList<Vector2> points, Color color)
|
|
{
|
|
// ok I don't know why this needs to be one quad less, maybe we are drawing with only 9 quads lol
|
|
var collider = new Vector2[VertsPerLine - VertsPerQuad];
|
|
|
|
int leftIndex = collider.Length - 1,
|
|
rightIndex = 0;
|
|
|
|
// we need to calculate half of the width since the way we expand the quads from origin, otherwise the line will be twice as wide
|
|
const float halfWidth = CircuitBoxSizes.WireWidth / 2f;
|
|
|
|
// draw the line using quads
|
|
for (int i = 0; i < points.Count - 1; i++)
|
|
{
|
|
bool isFirst = i == 0 && startOffset == 0,
|
|
isLast = i == points.Count - 2 && startOffset > 0;
|
|
|
|
Vector2 start = points[i],
|
|
end = points[i + 1];
|
|
|
|
Vector2 dir = Vector2.Normalize(end - start);
|
|
Vector2 length = new Vector2(dir.Y, -dir.X) * halfWidth;
|
|
|
|
int vertIndex = startOffset + i * 4;
|
|
|
|
Vector2 topRight = end + length;
|
|
Vector2 topLeft = end - length;
|
|
|
|
Vector2 bottomRight;
|
|
Vector2 bottomLeft;
|
|
|
|
// get previous points if any
|
|
int prevIndex = vertIndex - 4;
|
|
|
|
if ((prevIndex - startOffset) >= 0)
|
|
{
|
|
// connect the previous "upper" corners into the current "lower" corners to stitch the line together
|
|
Vector3 prevTopRight = verts[TopRight(prevIndex)].Position,
|
|
prevTopLeft = verts[TopLeft(prevIndex)].Position;
|
|
|
|
bottomRight = ToVector2(prevTopRight);
|
|
bottomLeft = ToVector2(prevTopLeft);
|
|
}
|
|
else
|
|
{
|
|
bottomRight = start + length;
|
|
bottomLeft = start - length;
|
|
}
|
|
|
|
if (isFirst)
|
|
{
|
|
if (MathF.Abs(dir.Y) > MathF.Abs(dir.X))
|
|
{
|
|
float offset = dir.Y < 0 ? halfWidth : -halfWidth;
|
|
// if the line is more vertical than horizontal, we want to move the bottom corners to the left
|
|
bottomRight.Y = start.Y - offset;
|
|
bottomLeft.Y = start.Y - offset;
|
|
}
|
|
else
|
|
{
|
|
// otherwise we want to move the bottom corners to the top
|
|
bottomRight.X = start.X;
|
|
bottomLeft.X = start.X;
|
|
}
|
|
}
|
|
else if (isLast)
|
|
{
|
|
if (MathF.Abs(dir.Y) > MathF.Abs(dir.X))
|
|
{
|
|
float offset = dir.Y < 0 ? halfWidth : -halfWidth;
|
|
// if the line is more vertical than horizontal, we want to move the bottom corners to the left
|
|
topRight.Y = end.Y + offset;
|
|
topLeft.Y = end.Y + offset;
|
|
}
|
|
else
|
|
{
|
|
// otherwise we want to move the bottom corners to the top
|
|
topRight.X = end.X;
|
|
topLeft.X = end.X;
|
|
}
|
|
}
|
|
|
|
collider[rightIndex++] = bottomLeft;
|
|
collider[rightIndex++] = topLeft;
|
|
|
|
collider[leftIndex--] = bottomRight;
|
|
collider[leftIndex--] = topRight;
|
|
|
|
// adjust this if we want sprites to support sourceRects
|
|
Vector2 uvTopRight = new Vector2(0, 1),
|
|
uvTopLeft = new Vector2(0, 0),
|
|
uvBottomRight = new Vector2(1, 1),
|
|
uvBottomLeft = new Vector2(1, 0);
|
|
|
|
SetPos(ref verts, TopRight(vertIndex), topRight, color, uvTopRight);
|
|
SetPos(ref verts, TopLeft(vertIndex), topLeft, color, uvTopLeft);
|
|
SetPos(ref verts, BottomRight(vertIndex), bottomRight, color, uvBottomRight);
|
|
SetPos(ref verts, BottomLeft(vertIndex), bottomLeft, color, uvBottomLeft);
|
|
|
|
static void SetPos(ref VertexPositionColorTexture[] verts, int index, Vector2 pos, Color color, Vector2 uv)
|
|
{
|
|
verts[index].Position = ToVector3(pos);
|
|
verts[index].Color = color;
|
|
verts[index].TextureCoordinate = uv;
|
|
static Vector3 ToVector3(Vector2 v) => new Vector3(v.X, v.Y, 0f);
|
|
}
|
|
|
|
static int TopRight(int vertIndex) => vertIndex;
|
|
static int TopLeft(int vertIndex) => vertIndex + 1;
|
|
static int BottomRight(int vertIndex) => vertIndex + 2;
|
|
static int BottomLeft(int vertIndex) => vertIndex + 3;
|
|
|
|
static Vector2 ToVector2(Vector3 v) => new Vector2(v.X, v.Y);
|
|
}
|
|
|
|
return collider;
|
|
}
|
|
}
|
|
|
|
public bool Contains(Vector2 pos)
|
|
{
|
|
pos.Y = -pos.Y;
|
|
foreach (Vector2[] collider in colliders)
|
|
{
|
|
if (ToolBox.PointIntersectsWithPolygon(pos, collider, checkBoundingBox: false)) { return true; }
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
public void Draw(SpriteBatch spriteBatch, Color selectionColor)
|
|
{
|
|
if (GameMain.DebugDraw)
|
|
{
|
|
for (int i = 0; i < skeleton.Points.Length; i++)
|
|
{
|
|
Vector2 point = skeleton.Points[i];
|
|
spriteBatch.DrawPoint(point, Color.White, 25f);
|
|
GUI.DrawString(spriteBatch, point - new Vector2(5f, 17f), i.ToString(), Color.Black, font: GUIStyle.LargeFont);
|
|
}
|
|
|
|
spriteBatch.DrawLine(skeleton.Points[0], skeleton.Points[1], GUIStyle.Green, thickness: 2f);
|
|
spriteBatch.DrawLine(skeleton.Points[1], skeleton.Points[2], GUIStyle.Green, thickness: 2f);
|
|
spriteBatch.DrawLine(skeleton.Points[2], skeleton.Points[3], GUIStyle.Green, thickness: 2f);
|
|
spriteBatch.DrawLine(skeleton.Points[3], skeleton.Points[4], GUIStyle.Green, thickness: 2f);
|
|
spriteBatch.DrawLine(skeleton.Points[4], skeleton.Points[5], GUIStyle.Green, thickness: 2f);
|
|
}
|
|
|
|
bool isSelected = wire.TryUnwrap(out var w) && w.IsSelected;
|
|
|
|
if (isSelected)
|
|
{
|
|
foreach (var colliderPolys in colliders)
|
|
{
|
|
spriteBatch.DrawPolygon(Vector2.Zero, colliderPolys, selectionColor, 5f);
|
|
}
|
|
}
|
|
|
|
spriteBatch.Draw(texture, verts, 0f);
|
|
|
|
if (skeleton.Type is SquareLine.LineType.SixPointBackwardsLine)
|
|
{
|
|
// we need to expand the start and end points to make the line look like it's connected to the "smooth" part of the line
|
|
Vector2 expandedEnd = skeleton.Points[1],
|
|
expandedStart = skeleton.Points[4];
|
|
|
|
expandedEnd.X += CircuitBoxSizes.WireWidth / 2f;
|
|
expandedStart.X -= CircuitBoxSizes.WireWidth / 2f;
|
|
|
|
spriteBatch.DrawLineWithTexture(texture, skeleton.Points[0], expandedEnd, lastColor, thickness: CircuitBoxSizes.WireWidth);
|
|
spriteBatch.DrawLineWithTexture(texture, expandedStart, skeleton.Points[5], lastColor, thickness: CircuitBoxSizes.WireWidth);
|
|
|
|
const float rectSize = CircuitBoxSizes.WireWidth * 1.5f;
|
|
RectangleF startKnob = new RectangleF(skeleton.Points[1] - new Vector2(rectSize / 2f), new Vector2(rectSize)),
|
|
endKnob = new RectangleF(skeleton.Points[4] - new Vector2(rectSize / 2f), new Vector2(rectSize));
|
|
|
|
GUI.DrawFilledRectangle(spriteBatch, startKnob, lastColor);
|
|
GUI.DrawFilledRectangle(spriteBatch, endKnob, lastColor);
|
|
}
|
|
|
|
if (!GameMain.DebugDraw) { return; }
|
|
|
|
foreach (var colliderPolys in colliders)
|
|
{
|
|
spriteBatch.DrawPolygonInner(Vector2.Zero, colliderPolys, Color.Lime, 1f);
|
|
}
|
|
}
|
|
}
|
|
} |