Cores/Nds/GPU3D.Geometry.cs
namespace Emulotl.Nds;
public sealed partial class GPU3D
{
public int[] ProjMatrix = new int[16];
public int[] PosMatrix = new int[16];
public int[] VecMatrix = new int[16];
public int[] TexMatrix = new int[16];
public int[] ClipMatrix = new int[16];
public bool ClipMatrixDirty;
public uint[] Viewport = new uint[6];
public int[] ProjMatrixStack = new int[16];
public int[,] PosMatrixStack = new int[32, 16];
public int[,] VecMatrixStack = new int[32, 16];
public int[] TexMatrixStack = new int[16];
public byte AlphaRefVal, AlphaRef;
public ushort[] ToonTable = new ushort[32];
public ushort[] EdgeTable = new ushort[8];
public uint FogColor, FogOffset;
public byte[] FogDensityTable = new byte[32];
public uint ClearAttr1, ClearAttr2;
public uint RenderDispCnt;
public byte RenderAlphaRef;
public ushort[] RenderToonTable = new ushort[32];
public ushort[] RenderEdgeTable = new ushort[8];
public uint RenderFogColor, RenderFogOffset, RenderFogShift;
public byte[] RenderFogDensityTable = new byte[34];
public uint RenderClearAttr1, RenderClearAttr2;
public bool RenderFrameIdentical;
public ushort RenderXPos;
public bool AbortFrame;
public uint PolygonMode;
public short[] CurVertex = new short[3];
public byte[] VertexColor = new byte[3];
public short[] TexCoords = new short[2];
public short[] RawTexCoords = new short[2];
public short[] Normal = new short[3];
public short[,] LightDirection = new short[4, 3];
public int[] SpecRecip = new int[4];
public byte[,] LightColor = new byte[4, 3];
public byte[] MatDiffuse = new byte[3];
public byte[] MatAmbient = new byte[3];
public byte[] MatSpecular = new byte[3];
public byte[] MatEmission = new byte[3];
public bool UseShininessTable;
public byte[] ShininessTable = new byte[128];
public uint PolygonAttr, CurPolygonAttr;
public uint TexParam, TexPalette;
public Vertex[] TempVertexBuffer = new Vertex[4];
public uint VertexNum, VertexNumInPoly, NumConsecutivePolygons;
public int LastStripPolygon = -1;
public uint NumOpaquePolygons;
public Vertex[] VertexRAM = new Vertex[6144 * 2];
public Polygon[] PolygonRAM = new Polygon[2048 * 2];
public int CurVertexBase, CurPolygonBase;
public uint CurRAMBank;
public Polygon[] RenderPolygonRAM = new Polygon[2048];
public uint RenderNumPolygons;
private readonly Vertex[] _clipTemp = new Vertex[10];
private readonly Vertex[] _clippedVertices = new Vertex[10];
private void NextVertexSlot()
{
int num = 9 - VertexSlotCounter + 1;
for (; ; )
{
CycleCount += num;
if ( VertexPipeline > 0 )
{
if ( VertexPipeline > num ) VertexPipeline -= num;
else VertexPipeline = 0;
}
if ( PolygonPipeline > 0 )
{
if ( PolygonPipeline > num )
{
PolygonPipeline -= num;
VertexSlotCounter = 1;
VertexSlotsFree >>= 1;
if ( (VertexSlotsFree & 0x1) != 0 )
{
VertexSlotsFree &= ~0x1u;
break;
}
else
{
num = 9;
continue;
}
}
else
{
PolygonPipeline = 0;
VertexSlotCounter = 0;
VertexSlotsFree = 1;
break;
}
}
else
{
break;
}
}
}
private void StallPolygonPipeline( int delay, int nonstalldelay )
{
if ( PolygonPipeline > 0 )
{
CycleCount += PolygonPipeline + delay;
VertexPipeline = 0;
NormalPipeline = 0;
PolygonPipeline = 0;
VertexSlotCounter = 0;
VertexSlotsFree = 1;
}
else
{
if ( VertexPipeline > nonstalldelay )
AddCycles( VertexPipeline - nonstalldelay + 1 );
else
AddCycles( NormalPipeline + 1 );
}
}
private void VertexPipelineSubmitCmd()
{
if ( (VertexSlotsFree & 0x1) == 0 ) NextVertexSlot();
else AddCycles( 1 );
NormalPipeline = 0;
}
private void VertexPipelineCmdDelayed6()
{
if ( VertexPipeline > 2 ) AddCycles( VertexPipeline - 2 + 1 );
else AddCycles( NormalPipeline + 1 );
NormalPipeline = 0;
}
private void VertexPipelineCmdDelayed8()
{
if ( VertexPipeline > 0 ) AddCycles( VertexPipeline + 1 );
else AddCycles( NormalPipeline + 1 );
NormalPipeline = 0;
}
private void VertexPipelineCmdDelayed4()
{
AddCycles( NormalPipeline + 1 );
NormalPipeline = 0;
}
private static void ClipSegment( ref Vertex outv, in Vertex vin, in Vertex vout, int comp, int plane, bool attribs )
{
long factor_num = vin.Pw - (long)plane * vin.GetPos( comp );
int factor_den = (int)(factor_num - (vout.Pw - (long)plane * vout.GetPos( comp )));
if ( comp != 0 ) outv.Px = (int)(vin.Px + (vout.Px - (long)vin.Px) * factor_num / factor_den);
if ( comp != 1 ) outv.Py = (int)(vin.Py + (vout.Py - (long)vin.Py) * factor_num / factor_den);
if ( comp != 2 ) outv.Pz = (int)(vin.Pz + (vout.Pz - (long)vin.Pz) * factor_num / factor_den);
outv.Pw = (int)(vin.Pw + (vout.Pw - (long)vin.Pw) * factor_num / factor_den);
outv.SetPos( comp, plane * outv.Pw );
if ( attribs )
{
outv.Cr = (int)(vin.Cr + (vout.Cr - (long)vin.Cr) * factor_num / factor_den);
outv.Cg = (int)(vin.Cg + (vout.Cg - (long)vin.Cg) * factor_num / factor_den);
outv.Cb = (int)(vin.Cb + (vout.Cb - (long)vin.Cb) * factor_num / factor_den);
outv.Tu = (short)(vin.Tu + (vout.Tu - (long)vin.Tu) * factor_num / factor_den);
outv.Tv = (short)(vin.Tv + (vout.Tv - (long)vin.Tv) * factor_num / factor_den);
}
outv.Clipped = true;
}
private int ClipAgainstPlane( Vertex[] vertices, int nverts, int clipstart, int comp, bool attribs )
{
Vertex[] temp = _clipTemp;
int prev, next;
int c = clipstart;
if ( clipstart == 2 )
{
temp[0] = vertices[0];
temp[1] = vertices[1];
}
for ( int i = clipstart; i < nverts; i++ )
{
prev = i - 1; if ( prev < 0 ) prev = nverts - 1;
next = i + 1; if ( next >= nverts ) next = 0;
Vertex vtx = vertices[i];
if ( vtx.GetPos( comp ) > vtx.Pw )
{
if ( (comp == 2) && ((CurPolygonAttr & (1 << 12)) == 0) ) return 0;
Vertex vprev = vertices[prev];
if ( vprev.GetPos( comp ) <= vprev.Pw )
{
ClipSegment( ref temp[c], vtx, vprev, comp, 1, attribs );
c++;
}
Vertex vnext = vertices[next];
if ( vnext.GetPos( comp ) <= vnext.Pw )
{
ClipSegment( ref temp[c], vtx, vnext, comp, 1, attribs );
c++;
}
}
else
temp[c++] = vtx;
}
nverts = c; c = clipstart;
for ( int i = clipstart; i < nverts; i++ )
{
prev = i - 1; if ( prev < 0 ) prev = nverts - 1;
next = i + 1; if ( next >= nverts ) next = 0;
Vertex vtx = temp[i];
if ( vtx.GetPos( comp ) < -vtx.Pw )
{
Vertex vprev = temp[prev];
if ( vprev.GetPos( comp ) >= -vprev.Pw )
{
ClipSegment( ref vertices[c], vtx, vprev, comp, -1, attribs );
c++;
}
Vertex vnext = temp[next];
if ( vnext.GetPos( comp ) >= -vnext.Pw )
{
ClipSegment( ref vertices[c], vtx, vnext, comp, -1, attribs );
c++;
}
}
else
vertices[c++] = vtx;
}
for ( int i = 0; i < c; i++ )
{
vertices[i].Cr = (vertices[i].Cr & ~0xFFF) + 0xFFF;
vertices[i].Cg = (vertices[i].Cg & ~0xFFF) + 0xFFF;
vertices[i].Cb = (vertices[i].Cb & ~0xFFF) + 0xFFF;
}
return c;
}
private int ClipPolygon( Vertex[] vertices, int nverts, int clipstart, bool attribs )
{
nverts = ClipAgainstPlane( vertices, nverts, clipstart, 2, attribs );
nverts = ClipAgainstPlane( vertices, nverts, clipstart, 1, attribs );
nverts = ClipAgainstPlane( vertices, nverts, clipstart, 0, attribs );
return nverts;
}
private static bool ClipCoordsEqual( in Vertex a, in Vertex b )
{
return a.Px == b.Px && a.Py == b.Py && a.Pz == b.Pz && a.Pw == b.Pw;
}
private void SubmitPolygon()
{
Vertex[] clippedvertices = _clippedVertices;
int reused0 = -1, reused1 = -1;
int clipstart = 0;
int lastpolyverts = 0;
int nverts = (PolygonMode & 0x1) != 0 ? 4 : 3;
PolygonPipeline = 8;
VertexSlotCounter = 1;
VertexSlotsFree = 0b11110;
Vertex v0 = TempVertexBuffer[0];
Vertex v1 = TempVertexBuffer[1];
Vertex v2 = TempVertexBuffer[2];
long normalX = ((long)(v0.Py - v1.Py) * (v2.Pw - v1.Pw)) - ((long)(v0.Pw - v1.Pw) * (v2.Py - v1.Py));
long normalY = ((long)(v0.Pw - v1.Pw) * (v2.Px - v1.Px)) - ((long)(v0.Px - v1.Px) * (v2.Pw - v1.Pw));
long normalZ = ((long)(v0.Px - v1.Px) * (v2.Py - v1.Py)) - ((long)(v0.Py - v1.Py) * (v2.Px - v1.Px));
while ( (((normalX >> 31) ^ (normalX >> 63)) != 0) ||
(((normalY >> 31) ^ (normalY >> 63)) != 0) ||
(((normalZ >> 31) ^ (normalZ >> 63)) != 0) )
{
normalX >>= 4;
normalY >>= 4;
normalZ >>= 4;
}
long dot = (v1.Px * normalX) + (v1.Py * normalY) + (v1.Pw * normalZ);
bool facingview = dot <= 0;
if ( dot < 0 )
{
if ( (CurPolygonAttr & (1 << 7)) == 0 )
{
LastStripPolygon = -1;
return;
}
}
else if ( dot > 0 )
{
if ( (CurPolygonAttr & (1 << 6)) == 0 )
{
LastStripPolygon = -1;
return;
}
}
if ( PolygonMode >= 2 && LastStripPolygon >= 0 )
{
int id0, id1;
if ( PolygonMode == 2 )
{
if ( (NumConsecutivePolygons & 1) != 0 ) { id0 = 2; id1 = 1; }
else { id0 = 0; id1 = 2; }
lastpolyverts = 3;
}
else
{
id0 = 3; id1 = 2;
lastpolyverts = 4;
}
Polygon lsp = PolygonRAM[LastStripPolygon];
if ( lsp.NumVertices == lastpolyverts &&
!VertexRAM[lsp.Vertices[id0]].Clipped &&
!VertexRAM[lsp.Vertices[id1]].Clipped )
{
reused0 = lsp.Vertices[id0];
reused1 = lsp.Vertices[id1];
clippedvertices[0] = VertexRAM[reused0];
clippedvertices[1] = VertexRAM[reused1];
clipstart = 2;
}
}
for ( int i = clipstart; i < nverts; i++ )
clippedvertices[i] = TempVertexBuffer[i];
int polytype = 0;
if ( nverts == 3 )
{
if ( ClipCoordsEqual( clippedvertices[0], clippedvertices[1] ) ||
ClipCoordsEqual( clippedvertices[0], clippedvertices[2] ) ||
ClipCoordsEqual( clippedvertices[1], clippedvertices[2] ) )
{
polytype = 1;
}
}
nverts = ClipPolygon( clippedvertices, nverts, clipstart, true );
if ( nverts == 0 )
{
LastStripPolygon = -1;
return;
}
if ( NumPolygons >= 2048 || NumVertices + nverts > 6144 )
{
LastStripPolygon = -1;
DispCnt |= 1 << 13;
return;
}
for ( int i = clipstart; i < nverts; i++ )
{
ref Vertex vtx = ref clippedvertices[i];
vtx.Pw &= 0x00FFFFFF;
uint w = (uint)vtx.Pw;
uint posX, posY;
if ( w == 0 )
{
posX = 0;
posY = 0;
}
else
{
posX = (uint)vtx.Px + w;
posY = (uint)-vtx.Py + w;
uint den = w;
if ( w > 0xFFFF )
{
posX >>= 1;
posY >>= 1;
den >>= 1;
}
den <<= 1;
posX = (posX * Viewport[4] / den) + Viewport[0];
posY = (posY * Viewport[5] / den) + Viewport[3];
}
vtx.Fx = (int)(posX & 0x1FF);
vtx.Fy = (int)(posY & 0xFF);
if ( w != 0 )
{
long hx = ((((long)((uint)vtx.Px + w) * Viewport[4]) << 4) / (((long)w) << 1)) + (Viewport[0] << 4);
long hy = ((((long)((uint)-vtx.Py + w) * Viewport[5]) << 4) / (((long)w) << 1)) + (Viewport[3] << 4);
vtx.Hx = (int)((uint)hx & 0x1FFF);
vtx.Hy = (int)((uint)hy & 0xFFF);
}
}
if ( (CurPolygonAttr & (1 << 13)) == 0 )
{
bool zerodot = true;
bool allbehind = true;
for ( int i = 0; i < nverts; i++ )
{
ref Vertex vtx = ref clippedvertices[i];
if ( vtx.Fx != clippedvertices[0].Fx || vtx.Fy != clippedvertices[0].Fy )
{
zerodot = false;
break;
}
if ( (uint)vtx.Pw <= ZeroDotWLimit )
{
allbehind = false;
break;
}
}
if ( zerodot && allbehind )
{
LastStripPolygon = -1;
return;
}
}
if ( nverts == 4 )
{
PolygonPipeline = 35;
VertexSlotCounter = 1;
if ( (PolygonMode & 0x2) != 0 ) VertexSlotsFree = 0b11100;
else VertexSlotsFree = 0b11110;
}
else
{
PolygonPipeline = 26;
VertexSlotCounter = 1;
if ( (PolygonMode & 0x2) != 0 ) VertexSlotsFree = 0b1000;
else VertexSlotsFree = 0b1110;
}
int polyidx = CurPolygonBase + (int)NumPolygons;
Polygon poly = PolygonRAM[polyidx];
NumPolygons++;
poly.NumVertices = 0;
poly.Attr = CurPolygonAttr;
poly.TexParam = TexParam;
poly.TexPalette = TexPalette;
poly.Degenerate = false;
poly.Type = 0;
poly.FacingView = facingview;
uint texfmt = (TexParam >> 26) & 0x7;
uint polyalpha = (CurPolygonAttr >> 16) & 0x1F;
poly.Translucent = texfmt == 1 || texfmt == 6 || (polyalpha > 0 && polyalpha < 31);
poly.IsShadowMask = (CurPolygonAttr & 0x3F000030) == 0x00000030;
poly.IsShadow = ((CurPolygonAttr & 0x30) == 0x30) && !poly.IsShadowMask;
if ( !poly.Translucent ) NumOpaquePolygons++;
poly.Type = polytype;
if ( LastStripPolygon >= 0 && clipstart > 0 )
{
if ( nverts == lastpolyverts )
{
poly.Vertices[0] = reused0;
poly.Vertices[1] = reused1;
}
else
{
VertexRAM[CurVertexBase + (int)NumVertices] = VertexRAM[reused0];
poly.Vertices[0] = CurVertexBase + (int)NumVertices;
VertexRAM[CurVertexBase + (int)NumVertices + 1] = VertexRAM[reused1];
poly.Vertices[1] = CurVertexBase + (int)NumVertices + 1;
NumVertices += 2;
}
poly.NumVertices += 2;
}
for ( int i = clipstart; i < nverts; i++ )
{
int slot = CurVertexBase + (int)NumVertices;
VertexRAM[slot] = clippedvertices[i];
poly.Vertices[i] = slot;
NumVertices++;
poly.NumVertices++;
ref Vertex vtx = ref VertexRAM[slot];
vtx.Fcr = vtx.Cr >> 12;
if ( vtx.Fcr != 0 ) vtx.Fcr = (vtx.Fcr << 4) + 0xF;
vtx.Fcg = vtx.Cg >> 12;
if ( vtx.Fcg != 0 ) vtx.Fcg = (vtx.Fcg << 4) + 0xF;
vtx.Fcb = vtx.Cb >> 12;
if ( vtx.Fcb != 0 ) vtx.Fcb = (vtx.Fcb << 4) + 0xF;
}
uint vtop = 0, vbot = 0;
int ytop = 192, ybot = 0;
int xtop = 256, xbot = 0;
int wsize = 0;
for ( int i = 0; i < nverts; i++ )
{
ref Vertex vtx = ref VertexRAM[poly.Vertices[i]];
if ( vtx.Fy < ytop )
{
xtop = vtx.Fx;
ytop = vtx.Fy;
vtop = (uint)i;
}
if ( vtx.Fy > ybot || (vtx.Fy == ybot && vtx.Fx > xbot) )
{
xbot = vtx.Fx;
ybot = vtx.Fy;
vbot = (uint)i;
}
uint w = (uint)vtx.Pw;
if ( w == 0 ) poly.Degenerate = true;
while ( (w >> wsize) != 0 && (wsize < 32) )
wsize += 4;
}
poly.VTop = vtop; poly.VBottom = vbot;
poly.YTop = ytop; poly.YBottom = ybot;
poly.XTop = xtop; poly.XBottom = xbot;
if ( ybot > 192 ) poly.Degenerate = true;
poly.SortKey = (uint)((ybot << 8) | ytop);
if ( poly.Translucent ) poly.SortKey |= 0x10000;
poly.WBuffer = (FlushAttributes & 0x2) != 0;
for ( int i = 0; i < nverts; i++ )
{
ref Vertex vtx = ref VertexRAM[poly.Vertices[i]];
int w, wshifted;
if ( wsize < 16 )
{
w = vtx.Pw << (16 - wsize);
wshifted = w >> (16 - wsize);
}
else
{
w = vtx.Pw >> (wsize - 16);
wshifted = w << (wsize - 16);
}
int z;
if ( (FlushAttributes & 0x2) != 0 )
z = wshifted;
else if ( vtx.Pw != 0 )
z = (int)((((long)vtx.Pz * 0x4000 / vtx.Pw) + 0x3FFF) * 0x200);
else
z = 0x7FFE00;
if ( z < 0 ) z = 0;
else if ( z > 0xFFFFFF ) z = 0xFFFFFF;
poly.FinalZ[i] = z;
poly.FinalW[i] = w;
}
if ( PolygonMode >= 2 )
LastStripPolygon = polyidx;
else
LastStripPolygon = -1;
}
private void SubmitVertex()
{
long vx = CurVertex[0];
long vy = CurVertex[1];
long vz = CurVertex[2];
const long vw = 0x1000;
ref Vertex vertextrans = ref TempVertexBuffer[VertexNumInPoly];
UpdateClipMatrix();
vertextrans.Px = (int)((vx * ClipMatrix[0] + vy * ClipMatrix[4] + vz * ClipMatrix[8] + vw * ClipMatrix[12]) >> 12);
vertextrans.Py = (int)((vx * ClipMatrix[1] + vy * ClipMatrix[5] + vz * ClipMatrix[9] + vw * ClipMatrix[13]) >> 12);
vertextrans.Pz = (int)((vx * ClipMatrix[2] + vy * ClipMatrix[6] + vz * ClipMatrix[10] + vw * ClipMatrix[14]) >> 12);
vertextrans.Pw = (int)((vx * ClipMatrix[3] + vy * ClipMatrix[7] + vz * ClipMatrix[11] + vw * ClipMatrix[15]) >> 12);
vertextrans.Cr = (VertexColor[0] << 12) + 0xFFF;
vertextrans.Cg = (VertexColor[1] << 12) + 0xFFF;
vertextrans.Cb = (VertexColor[2] << 12) + 0xFFF;
if ( (TexParam >> 30) == 3 )
{
vertextrans.Tu = (short)(((vx * TexMatrix[0] + vy * TexMatrix[4] + vz * TexMatrix[8]) >> 24) + RawTexCoords[0]);
vertextrans.Tv = (short)(((vx * TexMatrix[1] + vy * TexMatrix[5] + vz * TexMatrix[9]) >> 24) + RawTexCoords[1]);
}
else
{
vertextrans.Tu = TexCoords[0];
vertextrans.Tv = TexCoords[1];
}
vertextrans.Clipped = false;
VertexNum++;
VertexNumInPoly++;
switch ( PolygonMode )
{
case 0:
if ( VertexNumInPoly == 3 )
{
VertexNumInPoly = 0;
SubmitPolygon();
NumConsecutivePolygons++;
}
break;
case 1:
if ( VertexNumInPoly == 4 )
{
VertexNumInPoly = 0;
SubmitPolygon();
NumConsecutivePolygons++;
}
break;
case 2:
if ( (NumConsecutivePolygons & 1) != 0 )
{
Vertex tmp = TempVertexBuffer[1];
TempVertexBuffer[1] = TempVertexBuffer[0];
TempVertexBuffer[0] = tmp;
VertexNumInPoly = 2;
SubmitPolygon();
NumConsecutivePolygons++;
TempVertexBuffer[1] = TempVertexBuffer[2];
}
else if ( VertexNumInPoly == 3 )
{
VertexNumInPoly = 2;
SubmitPolygon();
NumConsecutivePolygons++;
TempVertexBuffer[0] = TempVertexBuffer[1];
TempVertexBuffer[1] = TempVertexBuffer[2];
}
break;
case 3:
if ( VertexNumInPoly == 4 )
{
Vertex tmp = TempVertexBuffer[3];
TempVertexBuffer[3] = TempVertexBuffer[2];
TempVertexBuffer[2] = tmp;
VertexNumInPoly = 2;
SubmitPolygon();
NumConsecutivePolygons++;
TempVertexBuffer[0] = TempVertexBuffer[3];
TempVertexBuffer[1] = TempVertexBuffer[2];
}
break;
}
VertexPipeline = 7;
AddCycles( 3 );
}
private void CalculateLighting()
{
if ( (TexParam >> 30) == 2 )
{
TexCoords[0] = (short)(RawTexCoords[0] + (((long)Normal[0] * TexMatrix[0] + (long)Normal[1] * TexMatrix[4] + (long)Normal[2] * TexMatrix[8]) >> 21));
TexCoords[1] = (short)(RawTexCoords[1] + (((long)Normal[0] * TexMatrix[1] + (long)Normal[1] * TexMatrix[5] + (long)Normal[2] * TexMatrix[9]) >> 21));
}
int[] normaltrans =
[
((Normal[0] * VecMatrix[0] + Normal[1] * VecMatrix[4] + Normal[2] * VecMatrix[8]) << 9) >> 21,
((Normal[0] * VecMatrix[1] + Normal[1] * VecMatrix[5] + Normal[2] * VecMatrix[9]) << 9) >> 21,
((Normal[0] * VecMatrix[2] + Normal[1] * VecMatrix[6] + Normal[2] * VecMatrix[10]) << 9) >> 21,
];
int c = 0;
uint[] vtxbuff =
[
(uint)MatEmission[0] << 14,
(uint)MatEmission[1] << 14,
(uint)MatEmission[2] << 14
];
for ( int i = 0; i < 4; i++ )
{
if ( (CurPolygonAttr & (1 << i)) == 0 )
continue;
int dot = ((LightDirection[i, 0] * normaltrans[0]) >> 9) +
((LightDirection[i, 1] * normaltrans[1]) >> 9) +
((LightDirection[i, 2] * normaltrans[2]) >> 9);
int shinelevel;
if ( dot > 0 )
{
int diffdot = (dot << 21) >> 21;
vtxbuff[0] += (uint)((MatDiffuse[0] * LightColor[i, 0] * diffdot) & 0xFFFFF);
vtxbuff[1] += (uint)((MatDiffuse[1] * LightColor[i, 1] * diffdot) & 0xFFFFF);
vtxbuff[2] += (uint)((MatDiffuse[2] * LightColor[i, 2] * diffdot) & 0xFFFFF);
dot += normaltrans[2];
dot = (dot << 21) >> 21;
dot = ((dot * dot) >> 10) & 0x3FF;
shinelevel = ((dot * SpecRecip[i]) >> 8) - (1 << 9);
if ( shinelevel < 0 ) shinelevel = 0;
else
{
shinelevel = (shinelevel << 18) >> 18;
if ( shinelevel < 0 ) shinelevel = 0;
else if ( shinelevel > 0x1FF ) shinelevel = 0x1FF;
}
}
else shinelevel = 0;
if ( UseShininessTable )
{
shinelevel >>= 2;
shinelevel = ShininessTable[shinelevel];
shinelevel <<= 1;
}
vtxbuff[0] += (uint)(((MatSpecular[0] * shinelevel) + (MatAmbient[0] << 9)) * LightColor[i, 0]);
vtxbuff[1] += (uint)(((MatSpecular[1] * shinelevel) + (MatAmbient[1] << 9)) * LightColor[i, 1]);
vtxbuff[2] += (uint)(((MatSpecular[2] * shinelevel) + (MatAmbient[2] << 9)) * LightColor[i, 2]);
c++;
}
VertexColor[0] = (byte)((vtxbuff[0] >> 14) > 31 ? 31 : (vtxbuff[0] >> 14));
VertexColor[1] = (byte)((vtxbuff[1] >> 14) > 31 ? 31 : (vtxbuff[1] >> 14));
VertexColor[2] = (byte)((vtxbuff[2] >> 14) > 31 ? 31 : (vtxbuff[2] >> 14));
if ( c < 1 ) c = 1;
NormalPipeline = 7;
AddCycles( c );
}
private void BoxTest( int[] paramArr )
{
Vertex[] cube = new Vertex[8];
Vertex[] face = new Vertex[10];
AddCycles( 254 );
GXStat &= ~(1u << 1);
short x0 = (short)(paramArr[0] & 0xFFFF);
short y0 = (short)(paramArr[0] >> 16);
short z0 = (short)(paramArr[1] & 0xFFFF);
short x1 = (short)(paramArr[1] >> 16);
short y1 = (short)(paramArr[2] & 0xFFFF);
short z1 = (short)(paramArr[2] >> 16);
x1 += x0;
y1 += y0;
z1 += z0;
cube[0].Px = x0; cube[0].Py = y0; cube[0].Pz = z0;
cube[1].Px = x1; cube[1].Py = y0; cube[1].Pz = z0;
cube[2].Px = x1; cube[2].Py = y1; cube[2].Pz = z0;
cube[3].Px = x0; cube[3].Py = y1; cube[3].Pz = z0;
cube[4].Px = x0; cube[4].Py = y1; cube[4].Pz = z1;
cube[5].Px = x0; cube[5].Py = y0; cube[5].Pz = z1;
cube[6].Px = x1; cube[6].Py = y0; cube[6].Pz = z1;
cube[7].Px = x1; cube[7].Py = y1; cube[7].Pz = z1;
UpdateClipMatrix();
for ( int i = 0; i < 8; i++ )
{
long x = cube[i].Px;
long y = cube[i].Py;
long z = cube[i].Pz;
cube[i].Px = (int)((x * ClipMatrix[0] + y * ClipMatrix[4] + z * ClipMatrix[8] + (long)0x1000 * ClipMatrix[12]) >> 12);
cube[i].Py = (int)((x * ClipMatrix[1] + y * ClipMatrix[5] + z * ClipMatrix[9] + (long)0x1000 * ClipMatrix[13]) >> 12);
cube[i].Pz = (int)((x * ClipMatrix[2] + y * ClipMatrix[6] + z * ClipMatrix[10] + (long)0x1000 * ClipMatrix[14]) >> 12);
cube[i].Pw = (int)((x * ClipMatrix[3] + y * ClipMatrix[7] + z * ClipMatrix[11] + (long)0x1000 * ClipMatrix[15]) >> 12);
}
if ( BoxTestFace( cube, face, 0, 1, 2, 3 ) ) { GXStat |= 1u << 1; return; }
if ( BoxTestFace( cube, face, 4, 5, 6, 7 ) ) { GXStat |= 1u << 1; return; }
if ( BoxTestFace( cube, face, 0, 3, 4, 5 ) ) { GXStat |= 1u << 1; return; }
if ( BoxTestFace( cube, face, 1, 2, 7, 6 ) ) { GXStat |= 1u << 1; return; }
if ( BoxTestFace( cube, face, 0, 1, 6, 5 ) ) { GXStat |= 1u << 1; return; }
if ( BoxTestFace( cube, face, 2, 3, 4, 7 ) ) { GXStat |= 1u << 1; return; }
}
private bool BoxTestFace( Vertex[] cube, Vertex[] face, int a, int b, int c, int d )
{
face[0] = cube[a]; face[1] = cube[b]; face[2] = cube[c]; face[3] = cube[d];
return ClipPolygon( face, 4, 0, false ) > 0;
}
private void PosTest()
{
long vx = CurVertex[0];
long vy = CurVertex[1];
long vz = CurVertex[2];
const long vw = 0x1000;
UpdateClipMatrix();
PosTestResult[0] = (int)((vx * ClipMatrix[0] + vy * ClipMatrix[4] + vz * ClipMatrix[8] + vw * ClipMatrix[12]) >> 12);
PosTestResult[1] = (int)((vx * ClipMatrix[1] + vy * ClipMatrix[5] + vz * ClipMatrix[9] + vw * ClipMatrix[13]) >> 12);
PosTestResult[2] = (int)((vx * ClipMatrix[2] + vy * ClipMatrix[6] + vz * ClipMatrix[10] + vw * ClipMatrix[14]) >> 12);
PosTestResult[3] = (int)((vx * ClipMatrix[3] + vy * ClipMatrix[7] + vz * ClipMatrix[11] + vw * ClipMatrix[15]) >> 12);
AddCycles( 5 );
}
private void VecTest( uint param )
{
short[] normal =
[
(short)((short)((param & 0x000003FF) << 6) >> 6),
(short)((short)((param & 0x000FFC00) >> 4) >> 6),
(short)((short)((param & 0x3FF00000) >> 14) >> 6),
];
VecTestResult[0] = (short)((normal[0] * VecMatrix[0] + normal[1] * VecMatrix[4] + normal[2] * VecMatrix[8]) >> 9);
VecTestResult[1] = (short)((normal[0] * VecMatrix[1] + normal[1] * VecMatrix[5] + normal[2] * VecMatrix[9]) >> 9);
VecTestResult[2] = (short)((normal[0] * VecMatrix[2] + normal[1] * VecMatrix[6] + normal[2] * VecMatrix[10]) >> 9);
if ( (VecTestResult[0] & 0x1000) != 0 ) VecTestResult[0] |= unchecked((short)0xF000);
if ( (VecTestResult[1] & 0x1000) != 0 ) VecTestResult[1] |= unchecked((short)0xF000);
if ( (VecTestResult[2] & 0x1000) != 0 ) VecTestResult[2] |= unchecked((short)0xF000);
AddCycles( 4 );
}
}