Cores/Nds/Compute/ComputeRenderer3D.Setup.cs
using System.Runtime.InteropServices;

namespace Emulotl.Nds;

[StructLayout( LayoutKind.Sequential )]
public struct RenderPolygonGpu
{
	public uint FirstXSpan;
	public int YTop, YBot;
	public int XMin, XMax;
	public int XMinY, XMaxY;
	public uint Variant;
	public uint Attr;
	public float TextureLayer;
	public uint TexParam;
}

[StructLayout( LayoutKind.Sequential )]
public struct SpanSetupY
{
	public int Z0, Z1, W0, W1;
	public int ColorR0, ColorG0, ColorB0;
	public int ColorR1, ColorG1, ColorB1;
	public int TexcoordU0, TexcoordV0;
	public int TexcoordU1, TexcoordV1;

	public int I0, I1;
	public int Linear;
	public int IRecip;
	public int W0n, W0d, W1d;

	public int Increment;

	public int X0, X1, Y0, Y1;
	public int XMin, XMax;
	public int DxInitial;

	public int XCovIncr;
	public uint IsDummy;
}

[StructLayout( LayoutKind.Sequential )]
public struct SpanSetupX
{
	public int X0, X1;

	public int EdgeLenL, EdgeLenR, EdgeCovL, EdgeCovR;

	public int XRecip;

	public uint Flags;

	public int Z0, Z1, W0, W1;
	public int ColorR0, ColorG0, ColorB0;
	public int ColorR1, ColorG1, ColorB1;
	public int TexcoordU0, TexcoordV0;
	public int TexcoordU1, TexcoordV1;

	public int CovLInitial, CovRInitial;
}

[StructLayout( LayoutKind.Sequential )]
public struct SetupIndices
{
	public ushort PolyIdx, SpanIdxL, SpanIdxR, Y;
}

public sealed partial class ComputeRenderer3D
{
	private void SetupAttrs( ref SpanSetupY span, Polygon poly, int from, int to )
	{
		span.Z0 = poly.FinalZ[from];
		span.W0 = poly.FinalW[from];
		span.Z1 = poly.FinalZ[to];
		span.W1 = poly.FinalW[to];

		ref Vertex vf = ref _gpu.VertexRAM[poly.Vertices[from]];
		ref Vertex vt = ref _gpu.VertexRAM[poly.Vertices[to]];

		span.ColorR0 = vf.Fcr;
		span.ColorG0 = vf.Fcg;
		span.ColorB0 = vf.Fcb;
		span.ColorR1 = vt.Fcr;
		span.ColorG1 = vt.Fcg;
		span.ColorB1 = vt.Fcb;
		span.TexcoordU0 = vf.Tu;
		span.TexcoordV0 = vf.Tv;
		span.TexcoordU1 = vt.Tu;
		span.TexcoordV1 = vt.Tv;
	}

	private void SetupYSpanDummy( ref RenderPolygonGpu rp, ref SpanSetupY span, Polygon poly, int vertex, int side, int[,] positions )
	{
		int x0 = positions[vertex, 0];
		if ( side != 0 )
		{
			span.DxInitial = -0x40000;
			x0--;
		}
		else
		{
			span.DxInitial = 0;
		}

		span.X0 = span.X1 = x0;
		span.XMin = x0;
		span.XMax = x0;
		span.Y0 = span.Y1 = positions[vertex, 1];

		if ( span.XMin < rp.XMin )
		{
			rp.XMin = span.XMin;
			rp.XMinY = span.Y0;
		}
		if ( span.XMax > rp.XMax )
		{
			rp.XMax = span.XMax;
			rp.XMaxY = span.Y0;
		}

		span.Increment = 0;

		span.I0 = span.I1 = span.IRecip = 0;
		span.Linear = 1;

		span.XCovIncr = 0;

		span.IsDummy = 1;

		SetupAttrs( ref span, poly, vertex, vertex );
	}

	private void SetupYSpan( ref RenderPolygonGpu rp, ref SpanSetupY span, Polygon poly, int from, int to, int side, int[,] positions )
	{
		span.X0 = positions[from, 0];
		span.X1 = positions[to, 0];
		span.Y0 = positions[from, 1];
		span.Y1 = positions[to, 1];

		SetupAttrs( ref span, poly, from, to );

		int minXY, maxXY;
		bool negative = false;
		if ( span.X1 > span.X0 )
		{
			span.XMin = span.X0;
			span.XMax = span.X1 - 1;

			minXY = span.Y0;
			maxXY = span.Y1;
		}
		else if ( span.X1 < span.X0 )
		{
			span.XMin = span.X1;
			span.XMax = span.X0 - 1;
			negative = true;

			minXY = span.Y1;
			maxXY = span.Y0;
		}
		else
		{
			span.XMin = span.X0;
			if ( side != 0 ) span.XMin--;
			span.XMax = span.XMin;

			minXY = span.Y0;
			maxXY = span.Y0;
		}

		if ( span.XMin < rp.XMin )
		{
			rp.XMin = span.XMin;
			rp.XMinY = minXY;
		}
		if ( span.XMax > rp.XMax )
		{
			rp.XMax = span.XMax;
			rp.XMaxY = maxXY;
		}

		span.IsDummy = 0;

		int xlen = span.XMax + 1 - span.XMin;
		int ylen = span.Y1 - span.Y0;

		if ( ylen == 0 )
		{
			span.Increment = 0;
		}
		else if ( ylen == xlen )
		{
			span.Increment = 0x40000;
		}
		else
		{
			int yrecip = (1 << 18) / ylen;
			span.Increment = (span.X1 - span.X0) * yrecip;
			if ( span.Increment < 0 ) span.Increment = -span.Increment;
		}

		bool xMajor = span.Increment > 0x40000;

		if ( side != 0 )
		{
			if ( xMajor )
				span.DxInitial = negative ? (0x20000 + 0x40000) : (span.Increment - 0x20000);
			else if ( span.Increment != 0 )
				span.DxInitial = negative ? 0x40000 : 0;
			else
				span.DxInitial = -0x40000;
		}
		else
		{
			if ( xMajor )
				span.DxInitial = negative ? (span.Increment - 0x20000 + 0x40000) : 0x20000;
			else if ( span.Increment != 0 )
				span.DxInitial = negative ? 0x40000 : 0;
			else
				span.DxInitial = 0;
		}

		if ( xMajor )
		{
			if ( side != 0 )
			{
				span.I0 = span.X0 - 1;
				span.I1 = span.X1 - 1;
			}
			else
			{
				span.I0 = span.X0;
				span.I1 = span.X1;
			}

			span.XCovIncr = (ylen << 10) / xlen;
		}
		else
		{
			span.I0 = span.Y0;
			span.I1 = span.Y1;
		}

		if ( span.I0 != span.I1 )
			span.IRecip = (1 << 30) / (span.I1 - span.I0);
		else
			span.IRecip = 0;

		span.Linear = (span.W0 == span.W1) && ((span.W0 & 0x7E) == 0) && ((span.W1 & 0x7E) == 0) ? 1 : 0;

		if ( (span.W0 & 0x1) != 0 && (span.W1 & 0x1) == 0 )
		{
			span.W0n = (span.W0 - 1) >> 1;
			span.W0d = (span.W0 + 1) >> 1;
			span.W1d = span.W1 >> 1;
		}
		else
		{
			span.W0n = span.W0 >> 1;
			span.W0d = span.W0 >> 1;
			span.W1d = span.W1 >> 1;
		}
	}
}