Cores/Nds/Compute/ComputeRenderer3D.RenderFrame.cs
namespace Emulotl.Nds;

public sealed partial class ComputeRenderer3D
{
	private int _numYSpans;
	private int _numSetupIndices;
	private readonly int[,] _scaledPositions = new int[10, 2];

	public int NumYSpans => _numYSpans;
	public int NumSetupIndices => _numSetupIndices;

	private void BuildSpanSetups()
	{
		_numYSpans = 0;
		_numSetupIndices = 0;
		_ySpanIndices.Clear();
		ResetVariants();
		BeginTextureFrame();

		if ( _cap128 != null )
			_gpu2d.GetCaptureInfo_Texture( _captureInfoTex );

		bool enableTex = (_gpu.RenderDispCnt & 1) != 0;

		int screenWidth = 256 * _scaleFactor;
		int screenHeight = 192 * _scaleFactor;

		for ( int i = 0; i < _gpu.RenderNumPolygons; i++ )
		{
			Polygon polygon = _gpu.RenderPolygonRAM[i];

			int nverts = (int)polygon.NumVertices;
			uint vtop = polygon.VTop, vbot = polygon.VBottom;

			uint curVL = vtop, curVR = vtop;
			uint nextVL, nextVR;

			_renderPolygons[i].FirstXSpan = (uint)_numSetupIndices;
			_renderPolygons[i].Attr = polygon.Attr;
			_renderPolygons[i].TexParam = polygon.TexParam;
			AssignVariant( polygon, i, enableTex );

			if ( polygon.FacingView )
			{
				nextVL = curVL + 1;
				if ( nextVL >= nverts ) nextVL = 0;
				nextVR = curVR - 1;
				if ( (int)nextVR < 0 ) nextVR = (uint)(nverts - 1);
			}
			else
			{
				nextVL = curVL - 1;
				if ( (int)nextVL < 0 ) nextVL = (uint)(nverts - 1);
				nextVR = curVR + 1;
				if ( nextVR >= nverts ) nextVR = 0;
			}

			int ytop = screenHeight, ybot = 0;
			for ( int v = 0; v < nverts; v++ )
			{
				ref Vertex vtx = ref _gpu.VertexRAM[polygon.Vertices[v]];
				if ( _hiresCoordinates )
				{
					_scaledPositions[v, 0] = (vtx.Hx * _scaleFactor) >> 4;
					_scaledPositions[v, 1] = (vtx.Hy * _scaleFactor) >> 4;
				}
				else
				{
					_scaledPositions[v, 0] = vtx.Fx * _scaleFactor;
					_scaledPositions[v, 1] = vtx.Fy * _scaleFactor;
				}
				ytop = Math.Min( _scaledPositions[v, 1], ytop );
				ybot = Math.Max( _scaledPositions[v, 1], ybot );
			}
			_renderPolygons[i].YTop = ytop;
			_renderPolygons[i].YBot = ybot;
			_renderPolygons[i].XMin = screenWidth;
			_renderPolygons[i].XMax = 0;

			if ( ybot == ytop )
			{
				int dtop = 0, dbot = 0;

				_renderPolygons[i].YBot++;

				int j = 1;
				if ( _scaledPositions[j, 0] < _scaledPositions[dtop, 0] ) dtop = j;
				if ( _scaledPositions[j, 0] > _scaledPositions[dbot, 0] ) dbot = j;

				j = nverts - 1;
				if ( _scaledPositions[j, 0] < _scaledPositions[dtop, 0] ) dtop = j;
				if ( _scaledPositions[j, 0] > _scaledPositions[dbot, 0] ) dbot = j;

				int curSpanL = _numYSpans;
				SetupYSpanDummy( ref _renderPolygons[i], ref _ySpanSetups[_numYSpans++], polygon, dtop, 0, _scaledPositions );
				int curSpanR = _numYSpans;
				SetupYSpanDummy( ref _renderPolygons[i], ref _ySpanSetups[_numYSpans++], polygon, dbot, 1, _scaledPositions );

				_ySpanIndices.Add( new SetupIndices
				{
					PolyIdx = (ushort)i,
					SpanIdxL = (ushort)curSpanL,
					SpanIdxR = (ushort)curSpanR,
					Y = (ushort)ytop
				} );
				_numSetupIndices++;
			}
			else
			{
				int curSpanL = _numYSpans;
				SetupYSpan( ref _renderPolygons[i], ref _ySpanSetups[_numYSpans++], polygon, (int)curVL, (int)nextVL, 0, _scaledPositions );
				int curSpanR = _numYSpans;
				SetupYSpan( ref _renderPolygons[i], ref _ySpanSetups[_numYSpans++], polygon, (int)curVR, (int)nextVR, 1, _scaledPositions );

				for ( int y = ytop; y < ybot; y++ )
				{
					if ( y >= _scaledPositions[nextVL, 1] && curVL != polygon.VBottom )
					{
						while ( y >= _scaledPositions[nextVL, 1] && curVL != polygon.VBottom )
						{
							curVL = nextVL;
							if ( polygon.FacingView )
							{
								nextVL = curVL + 1;
								if ( nextVL >= nverts ) nextVL = 0;
							}
							else
							{
								nextVL = curVL - 1;
								if ( (int)nextVL < 0 ) nextVL = (uint)(nverts - 1);
							}
						}

						curSpanL = _numYSpans;
						SetupYSpan( ref _renderPolygons[i], ref _ySpanSetups[_numYSpans++], polygon, (int)curVL, (int)nextVL, 0, _scaledPositions );
					}
					if ( y >= _scaledPositions[nextVR, 1] && curVR != polygon.VBottom )
					{
						while ( y >= _scaledPositions[nextVR, 1] && curVR != polygon.VBottom )
						{
							curVR = nextVR;
							if ( polygon.FacingView )
							{
								nextVR = curVR - 1;
								if ( (int)nextVR < 0 ) nextVR = (uint)(nverts - 1);
							}
							else
							{
								nextVR = curVR + 1;
								if ( nextVR >= nverts ) nextVR = 0;
							}
						}

						curSpanR = _numYSpans;
						SetupYSpan( ref _renderPolygons[i], ref _ySpanSetups[_numYSpans++], polygon, (int)curVR, (int)nextVR, 1, _scaledPositions );
					}

					_ySpanIndices.Add( new SetupIndices
					{
						PolyIdx = (ushort)i,
						SpanIdxL = (ushort)curSpanL,
						SpanIdxR = (ushort)curSpanR,
						Y = (ushort)y
					} );
					_numSetupIndices++;
				}
			}
		}
	}
}