desmume/src/gfx3d.cpp

2415 lines
64 KiB
C++

/* Copyright (C) 2006 yopyop
yopyop156@ifrance.com
yopyop156.ifrance.com
Copyright (C) 2008-2009 DeSmuME team
This file is part of DeSmuME
DeSmuME is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
DeSmuME is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with DeSmuME; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
//This file implements the geometry engine hardware component.
//This handles almost all of the work of 3d rendering, leaving the renderer
// plugin responsible only for drawing primitives.
#include <algorithm>
#include <assert.h>
#include <math.h>
#include <string.h>
#include "armcpu.h"
#include "debug.h"
#include "gfx3d.h"
#include "matrix.h"
#include "bits.h"
#include "MMU.h"
#include "render3D.h"
#include "mem.h"
#include "types.h"
#include "saves.h"
#include "readwrite.h"
#include "FIFO.h"
using std::max;
using std::min;
GFX3D gfx3d;
//tables that are provided to anyone
CACHE_ALIGN u32 color_15bit_to_24bit_reverse[32768];
CACHE_ALIGN u32 color_15bit_to_24bit[32768];
CACHE_ALIGN u16 color_15bit_to_16bit_reverse[32768];
CACHE_ALIGN u8 mixTable555[32][32][32];
//is this a crazy idea? this table spreads 5 bits evenly over 31 from exactly 0 to INT_MAX
CACHE_ALIGN const int material_5bit_to_31bit[] = {
0x00000000, 0x04210842, 0x08421084, 0x0C6318C6,
0x10842108, 0x14A5294A, 0x18C6318C, 0x1CE739CE,
0x21084210, 0x25294A52, 0x294A5294, 0x2D6B5AD6,
0x318C6318, 0x35AD6B5A, 0x39CE739C, 0x3DEF7BDE,
0x42108421, 0x46318C63, 0x4A5294A5, 0x4E739CE7,
0x5294A529, 0x56B5AD6B, 0x5AD6B5AD, 0x5EF7BDEF,
0x6318C631, 0x6739CE73, 0x6B5AD6B5, 0x6F7BDEF7,
0x739CE739, 0x77BDEF7B, 0x7BDEF7BD, 0x7FFFFFFF
};
CACHE_ALIGN const u8 material_5bit_to_6bit[] = {
0x00, 0x02, 0x04, 0x06, 0x08, 0x0A, 0x0C, 0x0E,
0x10, 0x12, 0x14, 0x16, 0x19, 0x1A, 0x1C, 0x1E,
0x21, 0x23, 0x25, 0x27, 0x29, 0x2B, 0x2D, 0x2F,
0x31, 0x33, 0x35, 0x37, 0x39, 0x3B, 0x3D, 0x3F
};
CACHE_ALIGN const u8 material_5bit_to_8bit[] = {
0x00, 0x08, 0x10, 0x18, 0x21, 0x29, 0x31, 0x39,
0x42, 0x4A, 0x52, 0x5A, 0x63, 0x6B, 0x73, 0x7B,
0x84, 0x8C, 0x94, 0x9C, 0xA5, 0xAD, 0xB5, 0xBD,
0xC6, 0xCE, 0xD6, 0xDE, 0xE7, 0xEF, 0xF7, 0xFF
};
CACHE_ALIGN const u8 material_3bit_to_8bit[] = {
0x00, 0x24, 0x49, 0x6D, 0x92, 0xB6, 0xDB, 0xFF
};
//maybe not very precise
CACHE_ALIGN const u8 material_3bit_to_5bit[] = {
0, 4, 8, 13, 17, 22, 26, 31
};
CACHE_ALIGN const u8 alpha_5bit_to_4bit[] = {
0x00, 0x00,
0x01, 0x01,
0x02, 0x02,
0x03, 0x03,
0x04, 0x04,
0x05, 0x05,
0x06, 0x06,
0x07, 0x07,
0x08, 0x08,
0x09, 0x09,
0x0A, 0x0A,
0x0B, 0x0B,
0x0C, 0x0C,
0x0D, 0x0D,
0x0E, 0x0E,
0x10, 0x10
};
CACHE_ALIGN const u16 alpha_lookup[] = {
0x0000,0x8000,0x8000,0x8000,0x8000,0x8000,0x8000,0x8000,
0x8000,0x8000,0x8000,0x8000,0x8000,0x8000,0x8000,0x8000,
0x8000,0x8000,0x8000,0x8000,0x8000,0x8000,0x8000,0x8000,
0x8000,0x8000,0x8000,0x8000,0x8000,0x8000,0x8000,0x8000};
//private acceleration tables
static float float16table[65536];
static float float10Table[1024];
static float float10RelTable[1024];
static float normalTable[1024];
#define fix2float(v) (((float)((s32)(v))) / (float)(1<<12))
#define fix10_2float(v) (((float)((s32)(v))) / (float)(1<<9))
CACHE_ALIGN u16 gfx3d_convertedScreen[256*192];
//this extra *2 is a HACK to salvage some savestates. remove me when the savestate format changes.
CACHE_ALIGN u8 gfx3d_convertedAlpha[256*192*2];
// Matrix stack handling
static CACHE_ALIGN MatrixStack mtxStack[4] = {
MatrixStack(1), // Projection stack
MatrixStack(31), // Coordinate stack
MatrixStack(31), // Directional stack
MatrixStack(1), // Texture stack
};
static CACHE_ALIGN float mtxCurrent [4][16];
static CACHE_ALIGN float mtxTemporal[16];
static u32 mode = 0;
// Indexes for matrix loading/multiplication
static u8 ML4x4ind = 0;
static u8 ML4x3ind = 0;
static u8 MM4x4ind = 0;
static u8 MM4x3ind = 0;
static u8 MM3x3ind = 0;
// Data for vertex submission
static CACHE_ALIGN float coord[4] = {0.0, 0.0, 0.0, 0.0};
static char coordind = 0;
static u32 vtxFormat;
static BOOL inBegin = FALSE;
// Data for basic transforms
static CACHE_ALIGN float trans[4] = {0.0, 0.0, 0.0, 0.0};
static int transind = 0;
static CACHE_ALIGN float scale[4] = {0.0, 0.0, 0.0, 0.0};
static int scaleind = 0;
static u32 viewport;
//various other registers
static float _t=0, _s=0;
static float last_t, last_s;
static u32 clCmd = 0;
static u32 clInd = 0;
#ifdef USE_GEOMETRY_FIFO_EMULATION
static u32 clInd2 = 0;
#endif
static u32 BTind = 0;
static u32 PTind = 0;
//static CACHE_ALIGN float BTcoords[6] = {0.0, 0.0, 0.0, 0.0, 0.0, 0.0};
static CACHE_ALIGN float PTcoords[4] = {0.0, 0.0, 0.0, 1.0};
//raw ds format poly attributes
static u32 polyAttr=0,textureFormat=0, texturePalette=0, polyAttrPending=0;
//the current vertex color, 5bit values
static int colorRGB[4] = { 31,31,31,31 };
u32 control = 0;
//light state:
static u32 lightColor[4] = {0,0,0,0};
static u32 lightDirection[4] = {0,0,0,0};
//material state:
static u16 dsDiffuse, dsAmbient, dsSpecular, dsEmission;
// Shininess
static float shininessTable[128] = {0};
static int shininessInd = 0;
//-----------cached things:
//these dont need to go into the savestate. they can be regenerated from HW registers
//from polygonattr:
static unsigned int cullingMask=0;
static u8 colorAlpha=0;
static u32 envMode=0;
static u32 lightMask=0;
//other things:
static int texCoordinateTransform = 0;
static CACHE_ALIGN float cacheLightDirection[4][4];
static CACHE_ALIGN float cacheHalfVector[4][4];
//------------------
#define RENDER_FRONT_SURFACE 0x80
#define RENDER_BACK_SURFACE 0X40
//-------------poly and vertex lists and such things
POLYLIST* polylists = NULL;
POLYLIST* polylist = NULL;
VERTLIST* vertlists = NULL;
VERTLIST* vertlist = NULL;
int listTwiddle = 1;
int triStripToggle;
//list-building state
struct tmpVertInfo {
//the number of verts registered in this list
int count;
//indices to the main vert list
int map[4];
//indicates that the first poly in a list has been completed
BOOL first;
} tempVertInfo;
static void twiddleLists() {
listTwiddle++;
listTwiddle &= 1;
polylist = &polylists[listTwiddle];
vertlist = &vertlists[listTwiddle];
polylist->count = 0;
vertlist->count = 0;
}
static BOOL flushPending = FALSE;
static BOOL drawPending = FALSE;
//------------------------------------------------------------
static void makeTables() {
//produce the color bits of a 24bpp color from a DS RGB15 using bit logic (internal use only)
#define RGB15TO24_BITLOGIC(col) ( (material_5bit_to_8bit[((col)>>10)&0x1F]<<16) | (material_5bit_to_8bit[((col)>>5)&0x1F]<<8) | material_5bit_to_8bit[(col)&0x1F] )
for(int i=0;i<32768;i++)
color_15bit_to_24bit[i] = RGB15TO24_BITLOGIC((u16)i);
//produce the color bits of a 24bpp color from a DS RGB15 using bit logic (internal use only). RGB are reverse of usual
#define RGB15TO24_BITLOGIC_REVERSE(col) ( (material_5bit_to_8bit[(col)&0x1F]<<16) | (material_5bit_to_8bit[((col)>>5)&0x1F]<<8) | material_5bit_to_8bit[((col)>>10)&0x1F] )
for(int i=0;i<32768;i++)
{
color_15bit_to_24bit_reverse[i] = RGB15TO24_BITLOGIC_REVERSE((u16)i);
color_15bit_to_16bit_reverse[i] = (((i & 0x001F) << 11) | (material_5bit_to_6bit[(i & 0x03E0) >> 5] << 5) | ((i & 0x7C00) >> 10));
}
for (int i = 0; i < 65536; i++)
float16table[i] = fix2float((signed short)i);
for (int i = 0; i < 1024; i++)
float10Table[i] = ((signed short)(i<<6)) / (float)(1<<12);
for (int i = 0; i < 1024; i++)
float10RelTable[i] = ((signed short)(i<<6)) / (float)(1<<18);
for (int i = 0; i < 1024; i++)
normalTable[i] = ((signed short)(i<<6)) / (float)(1<<15);
for(int r=0;r<=31;r++)
for(int oldr=0;oldr<=31;oldr++)
for(int a=0;a<=31;a++) {
int temp = (r*a + oldr*(31-a)) / 31;
mixTable555[a][r][oldr] = temp;
}
}
void gfx3d_init()
{
if(polylists == NULL) { polylists = new POLYLIST[2]; polylist = &polylists[0]; }
if(vertlists == NULL) { vertlists = new VERTLIST[2]; vertlist = &vertlists[0]; }
makeTables();
gfx3d_reset();
}
void gfx3d_reset()
{
gfx3d = GFX3D();
flushPending = FALSE;
listTwiddle = 1;
twiddleLists();
MatrixInit (mtxCurrent[0]);
MatrixInit (mtxCurrent[1]);
MatrixInit (mtxCurrent[2]);
MatrixInit (mtxCurrent[3]);
MatrixInit (mtxTemporal);
clCmd = 0;
clInd = 0;
#ifdef USE_GEOMETRY_FIFO_EMULATION
clInd2 = 0;
#endif
ML4x4ind = 0;
ML4x3ind = 0;
MM4x4ind = 0;
MM4x3ind = 0;
MM3x3ind = 0;
BTind = 0;
PTind = 0;
_t=0;
_s=0;
last_t = 0;
last_s = 0;
viewport = 0xBFFF0000;
memset(gfx3d_convertedScreen,0,sizeof(gfx3d_convertedScreen));
memset(gfx3d_convertedAlpha,0,sizeof(gfx3d_convertedAlpha));
GFX_FIFOclear();
}
void gfx3d_glViewPort(u32 v)
{
viewport = v;
}
void VIEWPORT::decode(u32 v)
{
x = (v&0xFF);
y = std::min(191,(int)(((v>>8)&0xFF)));
width = (((v>>16)&0xFF)+1)-(v&0xFF);
height = ((v>>24)+1)-((v>>8)&0xFF);
}
void gfx3d_glClearColor(u32 v)
{
gfx3d.clearColor = v;
}
void gfx3d_glFogColor(u32 v)
{
gfx3d.fogColor[0] = ((float)((v )&0x1F))/31.0f;
gfx3d.fogColor[1] = ((float)((v>> 5)&0x1F))/31.0f;
gfx3d.fogColor[2] = ((float)((v>>10)&0x1F))/31.0f;
gfx3d.fogColor[3] = ((float)((v>>16)&0x1F))/31.0f;
}
void gfx3d_glFogOffset (u32 v)
{
gfx3d.fogOffset = (float)(v&0xffff);
}
void gfx3d_glClearDepth(u32 v)
{
//formula from http://nocash.emubase.de/gbatek.htm#ds3drearplane
v &= 0x7FFFF;
gfx3d.clearDepth = (v*0x200)+((v+1)/0x8000)*0x01FF;
}
void gfx3d_glMatrixMode(u32 v)
{
mode = (v&3);
}
void gfx3d_glLoadIdentity()
{
MatrixIdentity (mtxCurrent[mode]);
if (mode == 2)
MatrixIdentity (mtxCurrent[1]);
}
BOOL gfx3d_glLoadMatrix4x4(s32 v)
{
mtxCurrent[mode][ML4x4ind] = fix2float(v);
++ML4x4ind;
if(ML4x4ind<16) return FALSE;
ML4x4ind = 0;
if (mode == 2)
MatrixCopy (mtxCurrent[1], mtxCurrent[2]);
return TRUE;
}
BOOL gfx3d_glLoadMatrix4x3(s32 v)
{
mtxCurrent[mode][ML4x3ind] = fix2float(v);
ML4x3ind++;
if((ML4x3ind & 0x03) == 3) ML4x3ind++;
if(ML4x3ind<16) return FALSE;
ML4x3ind = 0;
//fill in the unusued matrix values
mtxCurrent[mode][3] = mtxCurrent[mode][7] = mtxCurrent[mode][11] = 0;
mtxCurrent[mode][15] = 1;
if (mode == 2)
MatrixCopy (mtxCurrent[1], mtxCurrent[2]);
return TRUE;
}
void gfx3d_glStoreMatrix(u32 v)
{
//this command always works on both pos and vector when either pos or pos-vector are the current mtx mode
short mymode = (mode==1?2:mode);
//for the projection matrix, the provided value is supposed to be reset to zero
if(mymode==0)
v = 0;
if(v==31) v=30; //? what should happen in this case?
MatrixStackLoadMatrix (&mtxStack[mymode], v&31, mtxCurrent[mymode]);
if(mymode==2)
MatrixStackLoadMatrix (&mtxStack[1], v&31, mtxCurrent[1]);
}
void gfx3d_glRestoreMatrix(u32 v)
{
//this command always works on both pos and vector when either pos or pos-vector are the current mtx mode
short mymode = (mode==1?2:mode);
//for the projection matrix, the provided value is supposed to be reset to zero
if(mymode==0)
v = 0;
if(v==31) v=30; //? what should happen in this case?
MatrixCopy (mtxCurrent[mymode], MatrixStackGetPos(&mtxStack[mymode], v&31));
if (mymode == 2)
MatrixCopy (mtxCurrent[1], MatrixStackGetPos(&mtxStack[1], v&31));
}
void gfx3d_glPushMatrix()
{
u32 gxstat = T1ReadLong(MMU.MMU_MEM[ARMCPU_ARM9][0x40], 0x600);
//this command always works on both pos and vector when either pos or pos-vector are the current mtx mode
short mymode = (mode==1?2:mode);
if (mtxStack[mymode].position > mtxStack[mymode].size)
{
gxstat |= (1<<15);
T1WriteLong(MMU.MMU_MEM[ARMCPU_ARM9][0x40], 0x600, gxstat);
return;
}
gxstat &= 0xFFFF00FF;
MatrixStackPushMatrix(&mtxStack[mymode], mtxCurrent[mymode]);
if(mymode==2)
MatrixStackPushMatrix (&mtxStack[1], mtxCurrent[1]);
gxstat |= ((mtxStack[0].position << 13) | (mtxStack[1].position << 8));
T1WriteLong(MMU.MMU_MEM[ARMCPU_ARM9][0x40], 0x600, gxstat);
}
void gfx3d_glPopMatrix(s32 i)
{
u32 gxstat = T1ReadLong(MMU.MMU_MEM[ARMCPU_ARM9][0x40], 0x600);
//this command always works on both pos and vector when either pos or pos-vector are the current mtx mode
short mymode = (mode==1?2:mode);
/*
if (i > mtxStack[mymode].position)
{
gxstat |= (1<<15);
T1WriteLong(MMU.MMU_MEM[ARMCPU_ARM9][0x40], 0x600, gxstat);
return;
}
*/
gxstat &= 0xFFFF00FF;
MatrixCopy(mtxCurrent[mymode], MatrixStackPopMatrix (&mtxStack[mymode], i));
if (mymode == 2)
MatrixCopy(mtxCurrent[1], MatrixStackPopMatrix (&mtxStack[1], i));
gxstat |= ((mtxStack[0].position << 13) | (mtxStack[1].position << 8));
T1WriteLong(MMU.MMU_MEM[ARMCPU_ARM9][0x40], 0x600, gxstat);
}
BOOL gfx3d_glTranslate(s32 v)
{
trans[transind] = fix2float(v);
++transind;
if(transind<3) return FALSE;
transind = 0;
MatrixTranslate (mtxCurrent[mode], trans);
if (mode == 2)
MatrixTranslate (mtxCurrent[1], trans);
return TRUE;
}
BOOL gfx3d_glScale(s32 v)
{
short mymode = (mode==2?1:mode);
scale[scaleind] = fix2float(v);
++scaleind;
if(scaleind<3) return FALSE;
scaleind = 0;
MatrixScale (mtxCurrent[mymode], scale);
//note: pos-vector mode should not cause both matrices to scale.
//the whole purpose is to keep the vector matrix orthogonal
//so, I am leaving this commented out as an example of what not to do.
//if (mode == 2)
// MatrixScale (mtxCurrent[1], scale);
return TRUE;
}
BOOL gfx3d_glMultMatrix3x3(s32 v)
{
mtxTemporal[MM3x3ind] = fix2float(v);
MM3x3ind++;
if((MM3x3ind & 0x03) == 3) MM3x3ind++;
if(MM3x3ind<12) return FALSE;
MM3x3ind = 0;
//fill in the unusued matrix values
mtxTemporal[3] = mtxTemporal[7] = mtxTemporal[11] = 0;
mtxTemporal[15] = 1;
mtxTemporal[12] = mtxTemporal[13] = mtxTemporal[14] = 0;
MatrixMultiply (mtxCurrent[mode], mtxTemporal);
if (mode == 2)
MatrixMultiply (mtxCurrent[1], mtxTemporal);
//does this really need to be done?
MatrixIdentity (mtxTemporal);
return TRUE;
}
BOOL gfx3d_glMultMatrix4x3(s32 v)
{
mtxTemporal[MM4x3ind] = fix2float(v);
MM4x3ind++;
if((MM4x3ind & 0x03) == 3) MM4x3ind++;
if(MM4x3ind<16) return FALSE;
MM4x3ind = 0;
//fill in the unusued matrix values
mtxTemporal[3] = mtxTemporal[7] = mtxTemporal[11] = 0;
mtxTemporal[15] = 1;
MatrixMultiply (mtxCurrent[mode], mtxTemporal);
if (mode == 2)
MatrixMultiply (mtxCurrent[1], mtxTemporal);
//does this really need to be done?
MatrixIdentity (mtxTemporal);
return TRUE;
}
BOOL gfx3d_glMultMatrix4x4(s32 v)
{
mtxTemporal[MM4x4ind] = fix2float(v);
MM4x4ind++;
if(MM4x4ind<16) return FALSE;
MM4x4ind = 0;
MatrixMultiply (mtxCurrent[mode], mtxTemporal);
if (mode == 2)
MatrixMultiply (mtxCurrent[1], mtxTemporal);
MatrixIdentity (mtxTemporal);
return TRUE;
}
static void gfx3d_glPolygonAttrib_cache()
{
// Light enable/disable
lightMask = (polyAttr&0xF);
// texture environment
envMode = (polyAttr&0x30)>>4;
// back face culling
cullingMask = (polyAttr>>6)&3;
// Alpha value, actually not well handled, 0 should be wireframe
colorRGB[3] = colorAlpha = ((polyAttr>>16)&0x1F);
}
void gfx3d_glBegin(u32 v)
{
inBegin = TRUE;
vtxFormat = v&0x03;
triStripToggle = 0;
tempVertInfo.count = 0;
tempVertInfo.first = true;
polyAttr = polyAttrPending;
gfx3d_glPolygonAttrib_cache();
}
void gfx3d_glEnd(void)
{
inBegin = FALSE;
tempVertInfo.count = 0;
}
void gfx3d_glColor3b(u32 v)
{
colorRGB[0] = (v&0x1F);
colorRGB[1] = ((v>>5)&0x1F);
colorRGB[2] = ((v>>10)&0x1F);
}
static void SUBMITVERTEX(int i, int n)
{
polylist->list[polylist->count].vertIndexes[i] = tempVertInfo.map[n];
}
//Submit a vertex to the GE
static void SetVertex()
{
ALIGN(16) float coordTransformed[4] = { coord[0], coord[1], coord[2], 1 };
if (texCoordinateTransform == 3)
{
last_s =((coord[0]*mtxCurrent[3][0] +
coord[1]*mtxCurrent[3][4] +
coord[2]*mtxCurrent[3][8]) + _s);
last_t =((coord[0]*mtxCurrent[3][1] +
coord[1]*mtxCurrent[3][5] +
coord[2]*mtxCurrent[3][9]) + _t);
}
//refuse to do anything if we have too many verts or polys
if(vertlist->count >= VERTLIST_SIZE)
return;
if(polylist->count >= POLYLIST_SIZE)
return;
//apply modelview matrix
MatrixMultVec4x4 (mtxCurrent[1], coordTransformed);
//apply projection matrix
MatrixMultVec4x4 (mtxCurrent[0], coordTransformed);
//TODO - culling should be done here.
//TODO - viewport transform?
int continuation = 0;
if(vtxFormat==2 && !tempVertInfo.first)
continuation = 2;
else if(vtxFormat==3 && !tempVertInfo.first)
continuation = 2;
//record the vertex
//VERT &vert = tempVertList.list[tempVertList.count];
VERT &vert = vertlist->list[vertlist->count + tempVertInfo.count - continuation];
vert.texcoord[0] = last_s;
vert.texcoord[1] = last_t;
vert.coord[0] = coordTransformed[0];
vert.coord[1] = coordTransformed[1];
vert.coord[2] = coordTransformed[2];
vert.coord[3] = coordTransformed[3];
vert.color[0] = colorRGB[0];
vert.color[1] = colorRGB[1];
vert.color[2] = colorRGB[2];
tempVertInfo.map[tempVertInfo.count] = vertlist->count + tempVertInfo.count - continuation;
tempVertInfo.count++;
//possibly complete a polygon
{
int completed=0;
switch(vtxFormat) {
case 0: //GL_TRIANGLES
if(tempVertInfo.count!=3)
break;
completed = 1;
//vertlist->list[polylist->list[polylist->count].vertIndexes[i] = vertlist->count++] = tempVertList.list[n];
SUBMITVERTEX(0,0);
SUBMITVERTEX(1,1);
SUBMITVERTEX(2,2);
vertlist->count+=3;
polylist->list[polylist->count].type = 3;
tempVertInfo.count = 0;
break;
case 1: //GL_QUADS
if(tempVertInfo.count!=4)
break;
completed = 1;
SUBMITVERTEX(0,0);
SUBMITVERTEX(1,1);
SUBMITVERTEX(2,2);
SUBMITVERTEX(3,3);
vertlist->count+=4;
polylist->list[polylist->count].type = 4;
tempVertInfo.count = 0;
break;
case 2: //GL_TRIANGLE_STRIP
if(tempVertInfo.count!=3)
break;
completed = 1;
SUBMITVERTEX(0,0);
SUBMITVERTEX(1,1);
SUBMITVERTEX(2,2);
polylist->list[polylist->count].type = 3;
if(triStripToggle)
tempVertInfo.map[1] = vertlist->count+2-continuation;
else
tempVertInfo.map[0] = vertlist->count+2-continuation;
if(tempVertInfo.first)
vertlist->count+=3;
else
vertlist->count+=1;
triStripToggle ^= 1;
tempVertInfo.first = false;
tempVertInfo.count = 2;
break;
case 3: //GL_QUAD_STRIP
if(tempVertInfo.count!=4)
break;
completed = 1;
SUBMITVERTEX(0,0);
SUBMITVERTEX(1,1);
SUBMITVERTEX(2,3);
SUBMITVERTEX(3,2);
polylist->list[polylist->count].type = 4;
tempVertInfo.map[0] = vertlist->count+2-continuation;
tempVertInfo.map[1] = vertlist->count+3-continuation;
if(tempVertInfo.first)
vertlist->count+=4;
else vertlist->count+=2;
tempVertInfo.first = false;
tempVertInfo.count = 2;
break;
}
if(completed)
{
POLY &poly = polylist->list[polylist->count];
poly.polyAttr = polyAttr;
poly.texParam = textureFormat;
poly.texPalette = texturePalette;
poly.viewport = viewport;
polylist->count++;
}
}
}
BOOL gfx3d_glVertex16b(unsigned int v)
{
if(coordind==0)
{
coord[0] = float16table[v&0xFFFF];
coord[1] = float16table[v>>16];
++coordind;
return FALSE;
}
coord[2] = float16table[v&0xFFFF];
coordind = 0;
SetVertex ();
return TRUE;
}
void gfx3d_glVertex10b(u32 v)
{
coord[0] = float10Table[v&1023];
coord[1] = float10Table[(v>>10)&1023];
coord[2] = float10Table[(v>>20)&1023];
SetVertex ();
}
void gfx3d_glVertex3_cord(unsigned int one, unsigned int two, unsigned int v)
{
coord[one] = float16table[v&0xffff];
coord[two] = float16table[v>>16];
SetVertex ();
}
void gfx3d_glVertex_rel(u32 v)
{
coord[0] += float10RelTable[v&1023];
coord[1] += float10RelTable[(v>>10)&1023];
coord[2] += float10RelTable[(v>>20)&1023];
SetVertex ();
}
// Ignored for now
void gfx3d_glSwapScreen(unsigned int screen)
{
}
int gfx3d_GetNumPolys()
{
//so is this in the currently-displayed or currently-built list?
return 0;
}
int gfx3d_GetNumVertex()
{
//so is this in the currently-displayed or currently-built list?
return 0;
}
void gfx3d_glPolygonAttrib (u32 val)
{
if(inBegin) {
PROGINFO("Set polyattr in the middle of a begin/end pair.\n (This won't be activated until the next begin)\n");
//TODO - we need some some similar checking for teximageparam etc.
}
polyAttrPending = val;
}
/*
0-4 Diffuse Reflection Red
5-9 Diffuse Reflection Green
10-14 Diffuse Reflection Blue
15 Set Vertex Color (0=No, 1=Set Diffuse Reflection Color as Vertex Color)
16-20 Ambient Reflection Red
21-25 Ambient Reflection Green
26-30 Ambient Reflection Blue
*/
void gfx3d_glMaterial0(u32 val)
{
dsDiffuse = val&0xFFFF;
dsAmbient = val>>16;
if (BIT15(val))
{
colorRGB[0] = (val)&0x1F;
colorRGB[1] = (val>>5)&0x1F;
colorRGB[2] = (val>>10)&0x1F;
}
}
void gfx3d_glMaterial1(u32 val)
{
dsSpecular = val&0xFFFF;
dsEmission = val>>16;
}
BOOL gfx3d_glShininess (u32 val)
{
shininessTable[shininessInd++] = ((val & 0xFF) / 256.0f);
shininessTable[shininessInd++] = (((val >> 8) & 0xFF) / 256.0f);
shininessTable[shininessInd++] = (((val >> 16) & 0xFF) / 256.0f);
shininessTable[shininessInd++] = (((val >> 24) & 0xFF) / 256.0f);
if (shininessInd < 128) return FALSE;
shininessInd = 0;
return TRUE;
}
void gfx3d_UpdateToonTable(u8 offset, u16 val)
{
gfx3d.rgbToonTable[offset] = RGB15TO32(val, 255);
}
void gfx3d_UpdateToonTable(u8 offset, u32 val)
{
gfx3d.rgbToonTable[offset] = RGB15TO32(val & 0xFFFF, 255);
gfx3d.rgbToonTable[offset+1] = RGB15TO32(val >> 8, 255);
}
static void gfx3d_glTexImage_cache()
{
texCoordinateTransform = (textureFormat>>30);
}
void gfx3d_glTexImage(u32 val)
{
textureFormat = val;
gfx3d_glTexImage_cache();
}
void gfx3d_glTexPalette(u32 val)
{
texturePalette = val;
}
void gfx3d_glTexCoord(u32 val)
{
_t = (s16)(val>>16);
_s = (s16)(val&0xFFFF);
_s /= 16.0f;
_t /= 16.0f;
if (texCoordinateTransform == 1)
{
last_s =_s*mtxCurrent[3][0] + _t*mtxCurrent[3][4] +
0.0625f*mtxCurrent[3][8] + 0.0625f*mtxCurrent[3][12];
last_t =_s*mtxCurrent[3][1] + _t*mtxCurrent[3][5] +
0.0625f*mtxCurrent[3][9] + 0.0625f*mtxCurrent[3][13];
}
else
{
last_s=_s;
last_t=_t;
}
}
#define vec3dot(a, b) (((a[0]) * (b[0])) + ((a[1]) * (b[1])) + ((a[2]) * (b[2])))
void gfx3d_glNormal(u32 v)
{
int i,c;
ALIGN(16) float normal[3] = { normalTable[v&1023],
normalTable[(v>>10)&1023],
normalTable[(v>>20)&1023]};
if (texCoordinateTransform == 2)
{
last_s =( (normal[0] *mtxCurrent[3][0] + normal[1] *mtxCurrent[3][4] +
normal[2] *mtxCurrent[3][8]) + (_s*16.0f)) / 16.0f;
last_t =( (normal[0] *mtxCurrent[3][1] + normal[1] *mtxCurrent[3][5] +
normal[2] *mtxCurrent[3][9]) + (_t*16.0f)) / 16.0f;
}
//use the current normal transform matrix
MatrixMultVec3x3 (mtxCurrent[2], normal);
//apply lighting model
{
u8 diffuse[3] = {
(dsDiffuse)&0x1F,
(dsDiffuse>>5)&0x1F,
(dsDiffuse>>10)&0x1F };
u8 ambient[3] = {
(dsAmbient)&0x1F,
(dsAmbient>>5)&0x1F,
(dsAmbient>>10)&0x1F };
u8 emission[3] = {
(dsEmission)&0x1F,
(dsEmission>>5)&0x1F,
(dsEmission>>10)&0x1F };
u8 specular[3] = {
(dsSpecular)&0x1F,
(dsSpecular>>5)&0x1F,
(dsSpecular>>10)&0x1F };
int vertexColor[3] = { emission[0], emission[1], emission[2] };
for(i=0;i<4;i++) {
if(!((lightMask>>i)&1))
continue;
{
u8 _lightColor[3] = {
(lightColor[i])&0x1F,
(lightColor[i]>>5)&0x1F,
(lightColor[i]>>10)&0x1F };
/* This formula is the one used by the DS */
/* Reference : http://nocash.emubase.de/gbatek.htm#ds3dpolygonlightparameters */
float diffuseLevel = std::max(0.0f, -vec3dot(cacheLightDirection[i], normal));
float shininessLevel = pow(std::max(0.0f, vec3dot(-cacheHalfVector[i], normal)), 2);
if(dsSpecular & 0x8000)
{
int shininessIndex = (int)(shininessLevel * 128);
if(shininessIndex >= (int)ARRAY_SIZE(shininessTable)) {
//we can't print this right now, because when a game triggers this it triggers it _A_LOT_
//so wait until we have per-frame diagnostics.
//this was tested using Princess Debut (US) after proceeding through the intro and getting the tiara.
//After much research, I determined that this was caused by the game feeding in a totally jacked matrix
//to mult4x4 from 0x02129B80 (after feeding two other valid matrices)
//the game seems to internally index these as: ?, 0x37, 0x2B <-- error
//but, man... this is seriously messed up. there must be something going wrong.
//maybe it has something to do with what looks like a mirror room effect that is going on during this time?
//PROGINFO("ERROR: shininess table out of bounds.\n maybe an emulator error; maybe a non-unit normal; setting to 0\n");
shininessIndex = 0;
}
shininessLevel = shininessTable[shininessIndex];
}
for(c = 0; c < 3; c++)
{
vertexColor[c] += (int)(((specular[c] * _lightColor[c] * shininessLevel)
+ (diffuse[c] * _lightColor[c] * diffuseLevel)
+ (ambient[c] * _lightColor[c])) / 31.0f);
}
}
}
for(c=0;c<3;c++)
colorRGB[c] = std::min(31,vertexColor[c]);
}
}
s32 gfx3d_GetClipMatrix (unsigned int index)
{
float val = MatrixGetMultipliedIndex (index, mtxCurrent[0], mtxCurrent[1]);
val *= (1<<12);
return (s32)val;
}
s32 gfx3d_GetDirectionalMatrix (unsigned int index)
{
int _index = (((index / 3) * 4) + (index % 3));
return (s32)(mtxCurrent[2][_index]*(1<<12));
}
static void gfx3d_glLightDirection_cache(int index)
{
u32 v = lightDirection[index];
// Convert format into floating point value
cacheLightDirection[index][0] = normalTable[v&1023];
cacheLightDirection[index][1] = normalTable[(v>>10)&1023];
cacheLightDirection[index][2] = normalTable[(v>>20)&1023];
cacheLightDirection[index][3] = 0;
/* Multiply the vector by the directional matrix */
MatrixMultVec3x3(mtxCurrent[2], cacheLightDirection[index]);
/* Calculate the half vector */
float lineOfSight[4] = {0.0f, 0.0f, -1.0f, 0.0f};
for(int i = 0; i < 4; i++)
{
cacheHalfVector[index][i] = ((cacheLightDirection[index][i] + lineOfSight[i]) / 2.0f);
}
}
/*
0-9 Directional Vector's X component (1bit sign + 9bit fractional part)
10-19 Directional Vector's Y component (1bit sign + 9bit fractional part)
20-29 Directional Vector's Z component (1bit sign + 9bit fractional part)
30-31 Light Number (0..3)
*/
void gfx3d_glLightDirection (u32 v)
{
int index = v>>30;
lightDirection[index] = v;
gfx3d_glLightDirection_cache(index);
}
void gfx3d_glLightColor (u32 v)
{
int index = v>>30;
lightColor[index] = v;
}
void gfx3d_glAlphaFunc(u32 v)
{
gfx3d.alphaTestRef = v&31;
}
BOOL gfx3d_glBoxTest(u32 v)
{
u32 gxstat = T1ReadLong(MMU.MMU_MEM[ARMCPU_ARM9][0x40], 0x600);
gxstat |= 0x00000001; // busy
T1WriteLong(MMU.MMU_MEM[ARMCPU_ARM9][0x40], 0x600, gxstat);
BTind++;
if (BTind < 3)
{
/* if(BTind == 1)
{
BTcoords[0] = float16table[v & 0xFFFF];
BTcoords[1] = float16table[v >> 16];
}
else if(BTind == 2)
{
BTcoords[2] = float16table[v & 0xFFFF];
BTcoords[3] = float16table[v >> 16];
}*/
return FALSE;
}
BTind = 0;
/* BTcoords[4] = float16table[v & 0xFFFF];
BTcoords[5] = float16table[v >> 16];
ALIGN(16) float boxCoords[6][4][4] = {
// near
{{BTcoords[0], BTcoords[1], BTcoords[2], 1.0f},
{BTcoords[0]+BTcoords[3], BTcoords[1], BTcoords[2], 1.0f},
{BTcoords[0]+BTcoords[3], BTcoords[1]+BTcoords[4], BTcoords[2], 1.0f},
{BTcoords[0], BTcoords[1]+BTcoords[4], BTcoords[2], 1.0f}},
// far
{{BTcoords[0], BTcoords[1], BTcoords[2]+BTcoords[5], 1.0f},
{BTcoords[0]+BTcoords[3], BTcoords[1], BTcoords[2]+BTcoords[5], 1.0f},
{BTcoords[0]+BTcoords[3], BTcoords[1]+BTcoords[4], BTcoords[2]+BTcoords[5], 1.0f},
{BTcoords[0], BTcoords[1]+BTcoords[4], BTcoords[2]+BTcoords[5], 1.0f}},
// left
{{BTcoords[0], BTcoords[1], BTcoords[2]+BTcoords[5], 1.0f},
{BTcoords[0], BTcoords[1], BTcoords[2], 1.0f},
{BTcoords[0], BTcoords[1]+BTcoords[4], BTcoords[2], 1.0f},
{BTcoords[0], BTcoords[1]+BTcoords[4], BTcoords[2]+BTcoords[5], 1.0f}},
// right
{{BTcoords[0]+BTcoords[3], BTcoords[1], BTcoords[2], 1.0f},
{BTcoords[0]+BTcoords[3], BTcoords[1], BTcoords[2]+BTcoords[5], 1.0f},
{BTcoords[0]+BTcoords[3], BTcoords[1]+BTcoords[4], BTcoords[2]+BTcoords[5], 1.0f},
{BTcoords[0]+BTcoords[3], BTcoords[1]+BTcoords[4], BTcoords[2], 1.0f}},
// top
{{BTcoords[0], BTcoords[1], BTcoords[2]+BTcoords[5], 1.0f},
{BTcoords[0]+BTcoords[3], BTcoords[1], BTcoords[2]+BTcoords[5], 1.0f},
{BTcoords[0]+BTcoords[3], BTcoords[1], BTcoords[2], 1.0f},
{BTcoords[0], BTcoords[1], BTcoords[2], 1.0f}},
// bottom
{{BTcoords[0], BTcoords[1]+BTcoords[4], BTcoords[2]+BTcoords[5], 1.0f},
{BTcoords[0]+BTcoords[3], BTcoords[1]+BTcoords[4], BTcoords[2]+BTcoords[5], 1.0f},
{BTcoords[0]+BTcoords[3], BTcoords[1]+BTcoords[4], BTcoords[2], 1.0f},
{BTcoords[0], BTcoords[1]+BTcoords[4], BTcoords[2], 1.0f}}
};
for(int face = 0; face < 6; face++)
{
for(int vtx = 0; vtx < 4; vtx++)
{
MatrixMultVec4x4(mtxCurrent[1], boxCoords[face][vtx]);
MatrixMultVec4x4(mtxCurrent[0], boxCoords[face][vtx]);
boxCoords[face][vtx][0] = ((boxCoords[face][vtx][0] + boxCoords[face][vtx][3]) / (2 * boxCoords[face][vtx][3]));
boxCoords[face][vtx][1] = ((boxCoords[face][vtx][1] + boxCoords[face][vtx][3]) / (2 * boxCoords[face][vtx][3]));
boxCoords[face][vtx][2] = ((boxCoords[face][vtx][2] + boxCoords[face][vtx][3]) / (2 * boxCoords[face][vtx][3]));
// if(face==0)INFO("box test: testing face %i, vtx %i: %f %f %f %f\n", face, vtx,
// boxCoords[face][vtx][0], boxCoords[face][vtx][1], boxCoords[face][vtx][2], boxCoords[face][vtx][3]);
if ((boxCoords[face][vtx][0] >= 0.0f) && (boxCoords[face][vtx][0] <= 1.0f) &&
(boxCoords[face][vtx][1] >= 0.0f) && (boxCoords[face][vtx][1] <= 1.0f) &&
(boxCoords[face][vtx][2] >= 0.0f) && (boxCoords[face][vtx][2] <= 1.0f))
{
// goto faceInside;
}
else
goto noFaceInside;
}
}
goto faceInside;
faceInside:
gxstat |= 0x2;
noFaceInside:*/
gxstat &= 0xFFFFFFFE;
T1WriteLong(MMU.MMU_MEM[ARMCPU_ARM9][0x40], 0x600, gxstat);
return TRUE;
}
BOOL gfx3d_glPosTest(u32 v)
{
u32 gxstat = T1ReadLong(MMU.MMU_MEM[ARMCPU_ARM9][0x40], 0x600);
gxstat |= 0x00000001; // busy
T1WriteLong(MMU.MMU_MEM[ARMCPU_ARM9][0x40], 0x600, gxstat);
PTind++;
if (PTind < 2)
{
PTcoords[0] = float16table[v & 0xFFFF];
PTcoords[1] = float16table[v >> 16];
return FALSE;
}
PTind = 0;
PTcoords[2] = float16table[v & 0xFFFF];
PTcoords[3] = 1.0f;
MatrixMultVec4x4(mtxCurrent[1], PTcoords);
MatrixMultVec4x4(mtxCurrent[0], PTcoords);
gxstat &= 0xFFFFFFFE;
T1WriteLong(MMU.MMU_MEM[ARMCPU_ARM9][0x40], 0x600, gxstat);
return TRUE;
}
void gfx3d_glVecTest(u32 v)
{
u32 gxstat = T1ReadLong(MMU.MMU_MEM[ARMCPU_ARM9][0x40], 0x600);
gxstat &= 0xFFFFFFFE;
T1WriteLong(MMU.MMU_MEM[ARMCPU_ARM9][0x40], 0x600, gxstat);
//INFO("NDS_glVecTest\n");
}
unsigned int gfx3d_glGetPosRes(unsigned int index)
{
return (unsigned int)(PTcoords[index] * 4096.0f);
}
unsigned short gfx3d_glGetVecRes(unsigned int index)
{
//INFO("NDS_glGetVecRes\n");
return 0;
}
#ifdef USE_GEOMETRY_FIFO_EMULATION
void gfx3d_execute(u8 cmd, u32 param)
{
switch (cmd)
{
case 0x10: // MTX_MODE - Set Matrix Mode (W)
gfx3d_glMatrixMode(param);
break;
case 0x11: // MTX_PUSH - Push Current Matrix on Stack (W)
gfx3d_glPushMatrix();
break;
case 0x12: // MTX_POP - Pop Current Matrix from Stack (W)
gfx3d_glPopMatrix(param);
break;
case 0x13: // MTX_STORE - Store Current Matrix on Stack (W)
gfx3d_glStoreMatrix(param);
break;
case 0x14: // MTX_RESTORE - Restore Current Matrix from Stack (W)
gfx3d_glRestoreMatrix(param);
break;
case 0x15: // MTX_IDENTITY - Load Unit Matrix to Current Matrix (W)
gfx3d_glLoadIdentity();
break;
case 0x16: // MTX_LOAD_4x4 - Load 4x4 Matrix to Current Matrix (W)
gfx3d_glLoadMatrix4x4(param);
break;
case 0x17: // MTX_LOAD_4x3 - Load 4x3 Matrix to Current Matrix (W)
gfx3d_glLoadMatrix4x3(param);
break;
case 0x18: // MTX_MULT_4x4 - Multiply Current Matrix by 4x4 Matrix (W)
gfx3d_glMultMatrix4x4(param);
break;
case 0x19: // MTX_MULT_4x3 - Multiply Current Matrix by 4x3 Matrix (W)
gfx3d_glMultMatrix4x3(param);
break;
case 0x1A: // MTX_MULT_3x3 - Multiply Current Matrix by 3x3 Matrix (W)
gfx3d_glMultMatrix3x3(param);
break;
case 0x1B: // MTX_SCALE - Multiply Current Matrix by Scale Matrix (W)
gfx3d_glScale(param);
break;
case 0x1C: // MTX_TRANS - Mult. Curr. Matrix by Translation Matrix (W)
gfx3d_glTranslate(param);
break;
case 0x20: // COLOR - Directly Set Vertex Color (W)
gfx3d_glColor3b(param);
break;
case 0x21: // NORMAL - Set Normal Vector (W)
gfx3d_glNormal(param);
break;
case 0x22: // TEXCOORD - Set Texture Coordinates (W)
gfx3d_glTexCoord(param);
break;
case 0x23: // VTX_16 - Set Vertex XYZ Coordinates (W)
gfx3d_glVertex16b(param);
break;
case 0x24: // VTX_10 - Set Vertex XYZ Coordinates (W)
gfx3d_glVertex10b(param);
break;
case 0x25: // VTX_XY - Set Vertex XY Coordinates (W)
gfx3d_glVertex3_cord(0, 1, param);
break;
case 0x26: // VTX_XZ - Set Vertex XZ Coordinates (W)
gfx3d_glVertex3_cord(0, 2, param);
break;
case 0x27: // VTX_YZ - Set Vertex YZ Coordinates (W)
gfx3d_glVertex3_cord(1, 2, param);
break;
case 0x28: // VTX_DIFF - Set Relative Vertex Coordinates (W)
gfx3d_glVertex_rel(param);
break;
case 0x29: // POLYGON_ATTR - Set Polygon Attributes (W)
gfx3d_glPolygonAttrib(param);
break;
case 0x2A: // TEXIMAGE_PARAM - Set Texture Parameters (W)
gfx3d_glTexImage(param);
break;
case 0x2B: // PLTT_BASE - Set Texture Palette Base Address (W)
gfx3d_glTexPalette(param);
break;
case 0x30: // DIF_AMB - MaterialColor0 - Diffuse/Ambient Reflect. (W)
gfx3d_glMaterial0(param);
break;
case 0x31: // SPE_EMI - MaterialColor1 - Specular Ref. & Emission (W)
gfx3d_glMaterial1(param);
break;
case 0x32: // LIGHT_VECTOR - Set Light's Directional Vector (W)
gfx3d_glLightDirection(param);
break;
case 0x33: // LIGHT_COLOR - Set Light Color (W)
gfx3d_glLightColor(param);
break;
case 0x34: // SHININESS - Specular Reflection Shininess Table (W)
gfx3d_glShininess(param);
break;
case 0x40: // BEGIN_VTXS - Start of Vertex List (W)
gfx3d_glBegin(param);
break;
case 0x41: // END_VTXS - End of Vertex List (W)
gfx3d_glEnd();
break;
case 0x50: // SWAP_BUFFERS - Swap Rendering Engine Buffer (W)
gfx3d_glFlush(param);
break;
case 0x60: // VIEWPORT - Set Viewport (W)
gfx3d_glViewPort(param);
break;
case 0x70: // BOX_TEST - Test if Cuboid Sits inside View Volume (W)
gfx3d_glBoxTest(param);
break;
case 0x71: // POS_TEST - Set Position Coordinates for Test (W)
gfx3d_glPosTest(param);
break;
case 0x72: // VEC_TEST - Set Directional Vector for Test (W)
gfx3d_glVecTest(param);
break;
default:
INFO("Unknown execute FIFO 3D command 0x%02X with param 0x%08X\n", cmd, param);
break;
}
}
#endif
static void gfx3d_FlushFIFO()
{
if (!gxFIFO.tail) return;
#ifdef USE_GEOMETRY_FIFO_EMULATION
for (int i=0; i < gxFIFO.tail; i++)
{
//INFO("3D execute command 0x%02X with param 0x%08X (pos %03i)\n", gxFIFO.cmd[i], gxFIFO.param[i], i);
gfx3d_execute(gxFIFO.cmd[i], gxFIFO.param[i]);
}
#endif
GFX_FIFOclear();
}
void gfx3d_glFlush(u32 v)
{
flushPending = TRUE;
gfx3d.sortmode = BIT0(v);
gfx3d.wbuffer = BIT1(v);
}
static int _CDECL_ gfx3d_ysort_compare_old_qsort(const void * elem1, const void * elem2)
{
int num1 = *(int*)elem1;
int num2 = *(int*)elem2;
POLY &poly1 = polylist->list[num1];
POLY &poly2 = polylist->list[num2];
if(poly1.maxy > poly2.maxy)
return 1;
else if(poly1.maxy < poly2.maxy)
return -1;
else if(poly1.miny < poly2.miny)
return 1;
else if(poly1.miny > poly2.miny)
return -1;
else
return 0;
}
static bool gfx3d_ysort_compare(int num1, int num2)
{
const POLY &poly1 = polylist->list[num1];
const POLY &poly2 = polylist->list[num2];
if(poly1.maxy > poly2.maxy)
return true;
else if(poly1.maxy < poly2.maxy)
return false;
else if(poly1.miny < poly2.miny)
return true;
else if(poly1.miny > poly2.miny)
return false;
else
return false; //equal should always return false "strict weak ordering"
}
void gfx3d_VBlankSignal()
{
//the 3d buffers are swapped when a vblank begins.
//so, if we have a redraw pending, now is a safe time to do it
if(!flushPending)
{
gfx3d_FlushFIFO();
return;
}
gfx3d.frameCtr++;
gfx3d_FlushFIFO();
// reset
clInd = 0;
clCmd = 0;
//the renderer wil lget the lists we just built
gfx3d.polylist = polylist;
gfx3d.vertlist = vertlist;
int polycount = polylist->count;
//find the min and max y values for each poly.
//TODO - this could be a small waste of time if we are manual sorting the translucent polys
for(int i=0;i<polycount;i++) {
POLY &poly = polylist->list[i];
for(int j=0;j<poly.type;j++) {
VERT &vert = vertlist->list[poly.vertIndexes[j]];
poly.miny = j==0?vert.y:min(poly.miny,vert.y);
poly.maxy = j==0?vert.y:max(poly.maxy,vert.y);
}
}
//we need to sort the poly list with alpha polys last
//first, look for opaque polys
int ctr=0;
for(int i=0;i<polycount;i++) {
POLY &poly = polylist->list[i];
if(!poly.isTranslucent())
gfx3d.indexlist[ctr++] = i;
}
int opaqueCount = ctr;
//then look for translucent polys
for(int i=0;i<polycount;i++) {
POLY &poly = polylist->list[i];
if(poly.isTranslucent())
gfx3d.indexlist[ctr++] = i;
}
//========NOT SURE YET WHETHER I NEED A STABLE SORT========
//now we have to sort the opaque polys by y-value.
//should this be done after clipping??
//does this need to be a stable sort???
//test case: harvest moon island of happiness character cretor UI
//std::stable_sort(gfx3d.indexlist, gfx3d.indexlist + opaqueCount, gfx3d_ysort_compare);
qsort(gfx3d.indexlist, opaqueCount, 4, gfx3d_ysort_compare_old_qsort);
if(!gfx3d.sortmode)
{
//if we are autosorting translucent polys, we need to do this also
//TODO - this is unverified behavior. need a test case
//std::stable_sort(gfx3d.indexlist + opaqueCount, gfx3d.indexlist + polycount - opaqueCount, gfx3d_ysort_compare);
qsort(gfx3d.indexlist + opaqueCount, polycount - opaqueCount, 4, gfx3d_ysort_compare_old_qsort);
}
//switch to the new lists
twiddleLists();
flushPending = FALSE;
drawPending = TRUE;
}
void gfx3d_VBlankEndSignal(bool skipFrame)
{
//if we are skipping 3d frames then the 3d rendering will get held up here.
//but, as soon as we quit skipping frames, the held-up 3d frame will render
if(skipFrame) return;
if(!drawPending) return;
drawPending = FALSE;
gpu3D->NDS_3D_Render();
//if the null 3d core is chosen, then we need to clear out the 3d buffers to keep old data from being rendered
if(gpu3D == &gpu3DNull)
{
memset(gfx3d_convertedScreen,0,sizeof(gfx3d_convertedScreen));
memset(gfx3d_convertedScreen,0,sizeof(gfx3d_convertedAlpha));
}
}
#ifdef USE_GEOMETRY_FIFO_EMULATION
//#define _3D_LOG
static void NOPARAMS()
{
for (;;)
{
if (!clInd) return;
switch (clCmd & 0xFF)
{
case 0x00:
{
clCmd >>= 8;
clInd--;
#ifdef _3D_LOG
INFO("GX FIFO !!!ZERO!!! (%03i) (without params)\n", gxFIFO.tail);
#endif
//gfx3d_FlushFIFO();
continue;
}
case 0x11: // MTX_PUSH - Push Current Matrix on Stack (W)
case 0x15: // MTX_IDENTITY - Load Unit Matrix to Current Matrix (W)
case 0x41: // END_VTXS - End of Vertex List (W)
{
#ifdef _3D_LOG
INFO("GX FIFO cmd 0x%02X (without params)\n", clCmd, gxFIFO.tail);
#endif
GFX_FIFOsend(clCmd & 0xFF, 0);
clCmd >>= 8;
clInd--;
continue;
}
}
break;
}
}
void gfx3d_sendCommandToFIFO(u32 val)
{
if (!clInd)
{
if (val == 0)
{
//gfx3d_FlushFIFO();
return;
}
#ifdef _3D_LOG
INFO("GX FIFO cmd 0x%02X, gxstat 0x%08X (%03i)\n", val, gxstat, gxFIFO.tail);
#endif
clCmd = val;
if (!(clCmd & 0xFFFFFF00)) // unpacked command
clInd = 1;
else
if (!(clCmd & 0xFFFF0000)) // packed command
clInd = 2;
else
if (!(clCmd & 0xFF000000)) // packed command
clInd = 3;
else
clInd = 4;
NOPARAMS();
return;
}
switch (clCmd & 0xFF)
{
case 0x34: // SHININESS - Specular Reflection Shininess Table (W)
GFX_FIFOsend(clCmd & 0xFF, val);
clInd2++;
if (clInd2 < 32) return;
clInd2 = 0;
clCmd >>= 8;
clInd--;
break;
case 0x16: // MTX_LOAD_4x4 - Load 4x4 Matrix to Current Matrix (W)
case 0x18: // MTX_MULT_4x4 - Multiply Current Matrix by 4x4 Matrix (W)
GFX_FIFOsend(clCmd & 0xFF, val);
clInd2++;
if (clInd2 < 16) return;
clInd2 = 0;
clCmd >>= 8;
clInd--;
break;
case 0x17: // MTX_LOAD_4x3 - Load 4x3 Matrix to Current Matrix (W)
case 0x19: // MTX_MULT_4x3 - Multiply Current Matrix by 4x3 Matrix (W)
GFX_FIFOsend(clCmd & 0xFF, val);
clInd2++;
if (clInd2 < 12) return;
clInd2 = 0;
clCmd >>= 8;
clInd--;
break;
case 0x1A: // MTX_MULT_3x3 - Multiply Current Matrix by 3x3 Matrix (W)
GFX_FIFOsend(clCmd & 0xFF, val);
clInd2++;
if (clInd2 < 9) return;
clInd2 = 0;
clCmd >>= 8;
clInd--;
break;
case 0x1B: // MTX_SCALE - Multiply Current Matrix by Scale Matrix (W)
case 0x1C: // MTX_TRANS - Mult. Curr. Matrix by Translation Matrix (W)
case 0x70: // BOX_TEST - Test if Cuboid Sits inside View Volume (W)
GFX_FIFOsend(clCmd & 0xFF, val);
clInd2++;
if (clInd2 < 3) return;
clInd2 = 0;
clCmd >>= 8;
clInd--;
break;
case 0x23: // VTX_16 - Set Vertex XYZ Coordinates (W)
case 0x71: // POS_TEST - Set Position Coordinates for Test (W)
GFX_FIFOsend(clCmd & 0xFF, val);
clInd2++;
if (clInd2 < 2) return;
clInd2 = 0;
clCmd >>= 8;
clInd--;
break;
case 0x10: // MTX_MODE - Set Matrix Mode (W)
case 0x12: // MTX_POP - Pop Current Matrix from Stack (W)
case 0x13: // MTX_STORE - Store Current Matrix on Stack (W)
case 0x14: // MTX_RESTORE - Restore Current Matrix from Stack (W)
case 0x20: // COLOR - Directly Set Vertex Color (W)
case 0x21: // NORMAL - Set Normal Vector (W)
case 0x22: // TEXCOORD - Set Texture Coordinates (W)
case 0x24: // VTX_10 - Set Vertex XYZ Coordinates (W)
case 0x25: // VTX_XY - Set Vertex XY Coordinates (W)
case 0x26: // VTX_XZ - Set Vertex XZ Coordinates (W)
case 0x27: // VTX_YZ - Set Vertex YZ Coordinates (W)
case 0x28: // VTX_DIFF - Set Relative Vertex Coordinates (W)
case 0x29: // POLYGON_ATTR - Set Polygon Attributes (W)
case 0x2A: // TEXIMAGE_PARAM - Set Texture Parameters (W)
case 0x2B: // PLTT_BASE - Set Texture Palette Base Address (W)
case 0x30: // DIF_AMB - MaterialColor0 - Diffuse/Ambient Reflect. (W)
case 0x31: // SPE_EMI - MaterialColor1 - Specular Ref. & Emission (W)
case 0x32: // LIGHT_VECTOR - Set Light's Directional Vector (W)
case 0x33: // LIGHT_COLOR - Set Light Color (W)
case 0x40: // BEGIN_VTXS - Start of Vertex List (W)
case 0x60: // VIEWPORT - Set Viewport (W)
case 0x72: // VEC_TEST - Set Directional Vector for Test (W)
GFX_FIFOsend(clCmd & 0xFF, val);
clCmd >>= 8;
clInd--;
break;
case 0x50: // SWAP_BUFFERS - Swap Rendering Engine Buffer (W)
gfx3d_glFlush(val);
break;
default:
INFO("Unknown FIFO 3D command 0x%02X (0x%08X)\n", clCmd&0xFF, clCmd);
clCmd >>= 8;
clInd--;
return;
}
NOPARAMS();
}
void gfx3d_sendCommand(u32 cmd, u32 param)
{
cmd = (cmd & 0x01FF) >> 2;
#ifdef _3D_LOG
INFO("GX (dir) cmd 0x%02X = 0x%08X, gxstat 0x%08X (%02i)\n", cmd, param, gxstat, gxFIFO.tail);
#endif
switch (cmd)
{
case 0x10: // MTX_MODE - Set Matrix Mode (W)
case 0x11: // MTX_PUSH - Push Current Matrix on Stack (W)
case 0x12: // MTX_POP - Pop Current Matrix from Stack (W)
case 0x13: // MTX_STORE - Store Current Matrix on Stack (W)
case 0x14: // MTX_RESTORE - Restore Current Matrix from Stack (W)
case 0x15: // MTX_IDENTITY - Load Unit Matrix to Current Matrix (W)
case 0x16: // MTX_LOAD_4x4 - Load 4x4 Matrix to Current Matrix (W)
case 0x17: // MTX_LOAD_4x3 - Load 4x3 Matrix to Current Matrix (W)
case 0x18: // MTX_MULT_4x4 - Multiply Current Matrix by 4x4 Matrix (W)
case 0x19: // MTX_MULT_4x3 - Multiply Current Matrix by 4x3 Matrix (W)
case 0x1A: // MTX_MULT_3x3 - Multiply Current Matrix by 3x3 Matrix (W)
case 0x1B: // MTX_SCALE - Multiply Current Matrix by Scale Matrix (W)
case 0x1C: // MTX_TRANS - Mult. Curr. Matrix by Translation Matrix (W)
case 0x20: // COLOR - Directly Set Vertex Color (W)
case 0x21: // NORMAL - Set Normal Vector (W)
case 0x22: // TEXCOORD - Set Texture Coordinates (W)
case 0x23: // VTX_16 - Set Vertex XYZ Coordinates (W)
case 0x24: // VTX_10 - Set Vertex XYZ Coordinates (W)
case 0x25: // VTX_XY - Set Vertex XY Coordinates (W)
case 0x26: // VTX_XZ - Set Vertex XZ Coordinates (W)
case 0x27: // VTX_YZ - Set Vertex YZ Coordinates (W)
case 0x28: // VTX_DIFF - Set Relative Vertex Coordinates (W)
case 0x29: // POLYGON_ATTR - Set Polygon Attributes (W)
case 0x2A: // TEXIMAGE_PARAM - Set Texture Parameters (W)
case 0x2B: // PLTT_BASE - Set Texture Palette Base Address (W)
case 0x30: // DIF_AMB - MaterialColor0 - Diffuse/Ambient Reflect. (W)
case 0x31: // SPE_EMI - MaterialColor1 - Specular Ref. & Emission (W)
case 0x32: // LIGHT_VECTOR - Set Light's Directional Vector (W)
case 0x33: // LIGHT_COLOR - Set Light Color (W)
case 0x34: // SHININESS - Specular Reflection Shininess Table (W)
case 0x40: // BEGIN_VTXS - Start of Vertex List (W)
case 0x41: // END_VTXS - End of Vertex List (W)
case 0x60: // VIEWPORT - Set Viewport (W)
case 0x70: // BOX_TEST - Test if Cuboid Sits inside View Volume (W)
case 0x71: // POS_TEST - Set Position Coordinates for Test (W)
case 0x72: // VEC_TEST - Set Directional Vector for Test (W)
GFX_FIFOsend(cmd, param);
break;
case 0x50: // SWAP_BUFFERS - Swap Rendering Engine Buffer (W)
gfx3d_glFlush(param);
break;
default:
INFO("Unknown 3D command %03X with param 0x%08X (directport)\n", cmd, param);
break;
}
}
#else
static void NOPARAMS()
{
for (;;)
{
if (!clInd) return;
switch (clCmd & 0xFF)
{
case 0x00:
{
clCmd >>= 8;
clInd--;
continue;
}
case 0x11:
{
*(u32 *)(ARM9Mem.ARM9_REG + 0x444) = 0;
gfx3d_glPushMatrix();
GFX_FIFOsend(clCmd & 0xFF, 0);
clCmd >>= 8;
clInd--;
continue;
}
case 0x15:
{
*(u32 *)(ARM9Mem.ARM9_REG + 0x454) = 0;
gfx3d_glLoadIdentity();
GFX_FIFOsend(clCmd & 0xFF, 0);
clCmd >>= 8;
clInd--;
continue;
}
case 0x41:
{
*(u32 *)(ARM9Mem.ARM9_REG + 0x504) = 0;
gfx3d_glEnd();
GFX_FIFOsend(clCmd & 0xFF, 0);
clCmd >>= 8;
clInd--;
continue;
}
}
break;
}
}
void gfx3d_sendCommandToFIFO(u32 val)
{
if (!clInd)
{
if (val == 0) return;
#ifdef _3D_LOG
INFO("GFX FIFO: Send GFX 3D cmd 0x%02X to FIFO (0x%08X)\n", clCmd, val);
#endif
clCmd = val;
if (!(clCmd & 0xFFFFFF00)) // unpacked command
clInd = 1;
else
if (!(clCmd & 0xFFFF0000)) // packed command
clInd = 2;
else
if (!(clCmd & 0xFF000000)) // packed command
clInd = 3;
else
clInd = 4;
NOPARAMS();
return;
}
switch (clCmd & 0xFF)
{
case 0x10: // MTX_MODE - Set Matrix Mode (W)
*(u32 *)(ARM9Mem.ARM9_REG + 0x440) = val;
GFX_FIFOsend(clCmd & 0xFF, val);
gfx3d_glMatrixMode(val);
clCmd >>= 8;
clInd--;
break;
case 0x12: // MTX_POP - Pop Current Matrix from Stack (W)
*(u32 *)(ARM9Mem.ARM9_REG + 0x448) = val;
GFX_FIFOsend(clCmd & 0xFF, val);
gfx3d_glPopMatrix(val);
clCmd >>= 8;
clInd--;
break;
case 0x13: // MTX_STORE - Store Current Matrix on Stack (W)
*(u32 *)(ARM9Mem.ARM9_REG + 0x44C) = val;
GFX_FIFOsend(clCmd & 0xFF, val);
gfx3d_glStoreMatrix(val);
clCmd >>= 8;
clInd--;
break;
case 0x14: // MTX_RESTORE - Restore Current Matrix from Stack (W)
*(u32 *)(ARM9Mem.ARM9_REG + 0x450) = val;
GFX_FIFOsend(clCmd & 0xFF, val);
gfx3d_glRestoreMatrix(val);
clCmd >>= 8;
clInd--;
break;
case 0x16: // MTX_LOAD_4x4 - Load 4x4 Matrix to Current Matrix (W)
*(u32 *)(ARM9Mem.ARM9_REG + 0x458) = val;
GFX_FIFOsend(clCmd & 0xFF, val);
if (!gfx3d_glLoadMatrix4x4(val)) break;
clCmd >>= 8;
clInd--;
break;
case 0x17: // MTX_LOAD_4x3 - Load 4x3 Matrix to Current Matrix (W)
*(u32 *)(ARM9Mem.ARM9_REG + 0x45C) = val;
GFX_FIFOsend(clCmd & 0xFF, val);
if (!gfx3d_glLoadMatrix4x3(val)) break;
clCmd >>= 8;
clInd--;
break;
case 0x18: // MTX_MULT_4x4 - Multiply Current Matrix by 4x4 Matrix (W)
*(u32 *)(ARM9Mem.ARM9_REG + 0x460) = val;
GFX_FIFOsend(clCmd & 0xFF, val);
if (!gfx3d_glMultMatrix4x4(val)) break;
clCmd >>= 8;
clInd--;
break;
case 0x19: // MTX_MULT_4x3 - Multiply Current Matrix by 4x3 Matrix (W)
*(u32 *)(ARM9Mem.ARM9_REG + 0x464) = val;
GFX_FIFOsend(clCmd & 0xFF, val);
if (!gfx3d_glMultMatrix4x3(val)) break;
clCmd >>= 8;
clInd--;
break;
case 0x1A: // MTX_MULT_3x3 - Multiply Current Matrix by 3x3 Matrix (W)
*(u32 *)(ARM9Mem.ARM9_REG + 0x468) = val;
GFX_FIFOsend(clCmd & 0xFF, val);
if (!gfx3d_glMultMatrix3x3(val)) break;
clCmd >>= 8;
clInd--;
break;
case 0x1B: // MTX_SCALE - Multiply Current Matrix by Scale Matrix (W)
*(u32 *)(ARM9Mem.ARM9_REG + 0x46C) = val;
GFX_FIFOsend(clCmd & 0xFF, val);
if (!gfx3d_glScale(val)) break;
clCmd >>= 8;
clInd--;
break;
case 0x1C: // MTX_TRANS - Mult. Curr. Matrix by Translation Matrix (W)
*(u32 *)(ARM9Mem.ARM9_REG + 0x470) = val;
GFX_FIFOsend(clCmd & 0xFF, val);
if (!gfx3d_glTranslate(val)) break;
clCmd >>= 8;
clInd--;
break;
case 0x20: // COLOR - Directly Set Vertex Color (W)
*(u32 *)(ARM9Mem.ARM9_REG + 0x480) = val;
GFX_FIFOsend(clCmd & 0xFF, val);
gfx3d_glColor3b(val);
clCmd >>= 8;
clInd--;
break;
case 0x21: // NORMAL - Set Normal Vector (W)
*(u32 *)(ARM9Mem.ARM9_REG + 0x484) = val;
GFX_FIFOsend(clCmd & 0xFF, val);
gfx3d_glNormal(val);
clCmd >>= 8;
clInd--;
break;
case 0x22: // TEXCOORD - Set Texture Coordinates (W)
*(u32 *)(ARM9Mem.ARM9_REG + 0x488) = val;
GFX_FIFOsend(clCmd & 0xFF, val);
gfx3d_glTexCoord(val);
clCmd >>= 8;
clInd--;
break;
case 0x23: // VTX_16 - Set Vertex XYZ Coordinates (W)
*(u32 *)(ARM9Mem.ARM9_REG + 0x48C) = val;
GFX_FIFOsend(clCmd & 0xFF, val);
if (!gfx3d_glVertex16b(val)) break;
clCmd >>= 8;
clInd--;
break;
case 0x24: // VTX_10 - Set Vertex XYZ Coordinates (W)
*(u32 *)(ARM9Mem.ARM9_REG + 0x490) = val;
GFX_FIFOsend(clCmd & 0xFF, val);
gfx3d_glVertex10b(val);
clCmd >>= 8;
clInd--;
break;
case 0x25: // VTX_XY - Set Vertex XY Coordinates (W)
*(u32 *)(ARM9Mem.ARM9_REG + 0x494) = val;
GFX_FIFOsend(clCmd & 0xFF, val);
gfx3d_glVertex3_cord(0, 1, val);
clCmd >>= 8;
clInd--;
break;
case 0x26: // VTX_XZ - Set Vertex XZ Coordinates (W)
*(u32 *)(ARM9Mem.ARM9_REG + 0x498) = val;
GFX_FIFOsend(clCmd & 0xFF, val);
gfx3d_glVertex3_cord(0, 2, val);
clCmd >>= 8;
clInd--;
break;
case 0x27: // VTX_YZ - Set Vertex YZ Coordinates (W)
*(u32 *)(ARM9Mem.ARM9_REG + 0x49C) = val;
GFX_FIFOsend(clCmd & 0xFF, val);
gfx3d_glVertex3_cord(1, 2, val);
clCmd >>= 8;
clInd--;
break;
case 0x28: // VTX_DIFF - Set Relative Vertex Coordinates (W)
*(u32 *)(ARM9Mem.ARM9_REG + 0x4A0) = val;
GFX_FIFOsend(clCmd & 0xFF, val);
gfx3d_glVertex_rel(val);
clCmd >>= 8;
clInd--;
break;
case 0x29: // POLYGON_ATTR - Set Polygon Attributes (W)
*(u32 *)(ARM9Mem.ARM9_REG + 0x4A4) = val;
GFX_FIFOsend(clCmd & 0xFF, val);
gfx3d_glPolygonAttrib(val);
clCmd >>= 8;
clInd--;
break;
case 0x2A: // TEXIMAGE_PARAM - Set Texture Parameters (W)
*(u32 *)(ARM9Mem.ARM9_REG + 0x4A8) = val;
GFX_FIFOsend(clCmd & 0xFF, val);
gfx3d_glTexImage(val);
clCmd >>= 8;
clInd--;
break;
case 0x2B: // PLTT_BASE - Set Texture Palette Base Address (W)
*(u32 *)(ARM9Mem.ARM9_REG + 0x4AC) = val;
GFX_FIFOsend(clCmd & 0xFF, val);
gfx3d_glTexPalette(val);
clCmd >>= 8;
clInd--;
break;
case 0x30: // DIF_AMB - MaterialColor0 - Diffuse/Ambient Reflect. (W)
*(u32 *)(ARM9Mem.ARM9_REG + 0x4C0) = val;
GFX_FIFOsend(clCmd & 0xFF, val);
gfx3d_glMaterial0(val);
clCmd >>= 8;
clInd--;
break;
case 0x31: // SPE_EMI - MaterialColor1 - Specular Ref. & Emission (W)
*(u32 *)(ARM9Mem.ARM9_REG + 0x4C4) = val;
GFX_FIFOsend(clCmd & 0xFF, val);
gfx3d_glMaterial1(val);
clCmd >>= 8;
clInd--;
break;
case 0x32: // LIGHT_VECTOR - Set Light's Directional Vector (W)
*(u32 *)(ARM9Mem.ARM9_REG + 0x4C8) = val;
GFX_FIFOsend(clCmd & 0xFF, val);
gfx3d_glLightDirection(val);
clCmd >>= 8;
clInd--;
break;
case 0x33: // LIGHT_COLOR - Set Light Color (W)
*(u32 *)(ARM9Mem.ARM9_REG + 0x4CC) = val;
GFX_FIFOsend(clCmd & 0xFF, val);
gfx3d_glLightColor(val);
clCmd >>= 8;
clInd--;
break;
case 0x34: // SHININESS - Specular Reflection Shininess Table (W)
*(u32 *)(ARM9Mem.ARM9_REG + 0x4D0) = val;
GFX_FIFOsend(clCmd & 0xFF, val);
if (!gfx3d_glShininess(val)) break;
clCmd >>= 8;
clInd--;
break;
case 0x40: // BEGIN_VTXS - Start of Vertex List (W)
*(u32 *)(ARM9Mem.ARM9_REG + 0x500) = val;
GFX_FIFOsend(clCmd & 0xFF, val);
gfx3d_glBegin(val);
clCmd >>= 8;
clInd--;
break;
case 0x50: // SWAP_BUFFERS - Swap Rendering Engine Buffer (W)
*(u32 *)(ARM9Mem.ARM9_REG + 0x540) = val;
gfx3d_glFlush(val);
break;
case 0x60: // VIEWPORT - Set Viewport (W)
*(u32 *)(ARM9Mem.ARM9_REG + 0x580) = val;
GFX_FIFOsend(clCmd & 0xFF, val);
gfx3d_glViewPort(val);
clCmd >>= 8;
clInd--;
break;
case 0x70: // BOX_TEST - Test if Cuboid Sits inside View Volume (W)
*(u32 *)(ARM9Mem.ARM9_REG + 0x5C0) = val;
GFX_FIFOsend(clCmd & 0xFF, val);
if (!gfx3d_glBoxTest(val)) break;
clCmd >>= 8;
clInd--;
break;
case 0x71: // POS_TEST - Set Position Coordinates for Test (W)
*(u32 *)(ARM9Mem.ARM9_REG + 0x5C4) = val;
GFX_FIFOsend(clCmd & 0xFF, val);
if (!gfx3d_glPosTest(val)) break;
clCmd >>= 8;
clInd--;
break;
case 0x72: // VEC_TEST - Set Directional Vector for Test (W)
*(u32 *)(ARM9Mem.ARM9_REG + 0x5C8) = val;
GFX_FIFOsend(clCmd & 0xFF, val);
gfx3d_glVecTest(val);
clCmd >>= 8;
clInd--;
break;
default:
LOG("Unknown FIFO 3D command 0x%02X in cmd=0x%02X\n", clCmd&0xFF, val);
clCmd >>= 8;
clInd--;
break;
}
NOPARAMS();
}
void gfx3d_sendCommand(u32 cmd, u32 param)
{
cmd &= 0x0FFF;
#ifdef _3D_LOG
INFO("GFX FIFO: Send GFX 3D cmd 0x%02X to FIFO (0x%08X) - DIRECT\n", (cmd & 0x1FF)>>2, param);
#endif
switch (cmd)
{
case 0x340: // Alpha test reference value - Parameters:1
gfx3d_glAlphaFunc(param);
break;
case 0x350: // Clear background color setup - Parameters:2
gfx3d_glClearColor(param);
break;
case 0x354: // Clear background depth setup - Parameters:2
gfx3d_glClearDepth(param);
break;
case 0x356: // Rear-plane Bitmap Scroll Offsets (W)
break;
case 0x358: // Fog Color - Parameters:4b
gfx3d_glFogColor(param);
break;
case 0x35C:
gfx3d_glFogOffset(param);
break;
case 0x440: // MTX_MODE - Set Matrix Mode (W)
gfx3d_glMatrixMode(param);
break;
case 0x444: // MTX_PUSH - Push Current Matrix on Stack (W)
gfx3d_glPushMatrix();
break;
case 0x448: // MTX_POP - Pop Current Matrix from Stack (W)
gfx3d_glPopMatrix(param);
break;
case 0x44C: // MTX_STORE - Store Current Matrix on Stack (W)
gfx3d_glStoreMatrix(param);
break;
case 0x450: // MTX_RESTORE - Restore Current Matrix from Stack (W)
gfx3d_glRestoreMatrix(param);
break;
case 0x454: // MTX_IDENTITY - Load Unit Matrix to Current Matrix (W)
gfx3d_glLoadIdentity();
break;
case 0x458: // MTX_LOAD_4x4 - Load 4x4 Matrix to Current Matrix (W)
gfx3d_glLoadMatrix4x4(param);
break;
case 0x45C: // MTX_LOAD_4x3 - Load 4x3 Matrix to Current Matrix (W)
gfx3d_glLoadMatrix4x3(param);
break;
case 0x460: // MTX_MULT_4x4 - Multiply Current Matrix by 4x4 Matrix (W)
gfx3d_glMultMatrix4x4(param);
break;
case 0x464: // MTX_MULT_4x3 - Multiply Current Matrix by 4x3 Matrix (W)
gfx3d_glMultMatrix4x3(param);
break;
case 0x468: // MTX_MULT_3x3 - Multiply Current Matrix by 3x3 Matrix (W)
gfx3d_glMultMatrix3x3(param);
break;
case 0x46C: // MTX_SCALE - Multiply Current Matrix by Scale Matrix (W)
gfx3d_glScale(param);
break;
case 0x470: // MTX_TRANS - Mult. Curr. Matrix by Translation Matrix (W)
gfx3d_glTranslate(param);
break;
case 0x480: // COLOR - Directly Set Vertex Color (W)
gfx3d_glColor3b(param);
break;
case 0x484: // NORMAL - Set Normal Vector (W)
gfx3d_glNormal(param);
break;
case 0x488: // TEXCOORD - Set Texture Coordinates (W)
gfx3d_glTexCoord(param);
break;
case 0x48C: // VTX_16 - Set Vertex XYZ Coordinates (W)
gfx3d_glVertex16b(param);
break;
case 0x490: // VTX_10 - Set Vertex XYZ Coordinates (W)
gfx3d_glVertex10b(param);
break;
case 0x494: // VTX_XY - Set Vertex XY Coordinates (W)
gfx3d_glVertex3_cord(0, 1, param);
break;
case 0x498: // VTX_XZ - Set Vertex XZ Coordinates (W)
gfx3d_glVertex3_cord(0, 2, param);
break;
case 0x49C: // VTX_YZ - Set Vertex YZ Coordinates (W)
gfx3d_glVertex3_cord(1, 2, param);
break;
case 0x4A0: // VTX_DIFF - Set Relative Vertex Coordinates (W)
gfx3d_glVertex_rel(param);
break;
case 0x4A4: // POLYGON_ATTR - Set Polygon Attributes (W)
gfx3d_glPolygonAttrib(param);
break;
case 0x4A8: // TEXIMAGE_PARAM - Set Texture Parameters (W)
gfx3d_glTexImage(param);
break;
case 0x4AC: // PLTT_BASE - Set Texture Palette Base Address (W)
gfx3d_glTexPalette(param);
break;
case 0x4C0: // DIF_AMB - MaterialColor0 - Diffuse/Ambient Reflect. (W)
gfx3d_glMaterial0(param);
break;
case 0x4C4: // SPE_EMI - MaterialColor1 - Specular Ref. & Emission (W)
gfx3d_glMaterial1(param);
break;
case 0x4C8: // LIGHT_VECTOR - Set Light's Directional Vector (W)
gfx3d_glLightDirection(param);
break;
case 0x4CC: // LIGHT_COLOR - Set Light Color (W)
gfx3d_glLightColor(param);
break;
case 0x4D0: // SHININESS - Specular Reflection Shininess Table (W)
gfx3d_glShininess(param);
break;
case 0x500: // BEGIN_VTXS - Start of Vertex List (W)
gfx3d_glBegin(param);
break;
case 0x504: // END_VTXS - End of Vertex List (W)
gfx3d_glEnd();
break;
case 0x540: // SWAP_BUFFERS - Swap Rendering Engine Buffer (W)
gfx3d_glFlush(param);
break;
case 0x580: // VIEWPORT - Set Viewport (W)
gfx3d_glViewPort(param);
break;
case 0x5C0: // BOX_TEST - Test if Cuboid Sits inside View Volume (W)
gfx3d_glBoxTest(param);
break;
case 0x5C4: // POS_TEST - Set Position Coordinates for Test (W)
gfx3d_glPosTest(param);
break;
case 0x5C8: // VEC_TEST - Set Directional Vector for Test (W)
gfx3d_glVecTest(param);
break;
default:
LOG("Execute direct Port 3D command %03X in param=0x%08X\n", cmd, param);
break;
}
}
#endif
static void gfx3d_Control_cache()
{
u32 v = control;
if(v&1) gfx3d.enableTexturing = TRUE;
else gfx3d.enableTexturing = FALSE;
if((v>>1)&1) gfx3d.shading = GFX3D::HIGHLIGHT;
else gfx3d.shading = GFX3D::TOON;
if((v>>2)&1) gfx3d.enableAlphaTest = TRUE;
else gfx3d.enableAlphaTest = FALSE;
if((v>>3)&1) gfx3d.enableAlphaBlending = TRUE;
else gfx3d.enableAlphaBlending = FALSE;
if((v>>4)&1) gfx3d.enableAntialiasing = TRUE;
else gfx3d.enableAntialiasing = FALSE;
if((v>>5)&1) gfx3d.enableEdgeMarking = TRUE;
else gfx3d.enableEdgeMarking = FALSE;
//other junk
if (v&(1<<14))
{
LOG("Enabled BITMAP background mode\n");
}
}
void gfx3d_Control(u32 v)
{
control = v;
gfx3d_Control_cache();
}
//--------------
//other misc stuff
void gfx3d_glGetMatrix(unsigned int m_mode, int index, float* dest)
{
if(index == -1)
{
MatrixCopy(dest, mtxCurrent[m_mode]);
return;
}
MatrixCopy(dest, MatrixStackGetPos(&mtxStack[m_mode], index));
}
void gfx3d_glGetLightDirection(unsigned int index, unsigned int* dest)
{
*dest = lightDirection[index];
}
void gfx3d_glGetLightColor(unsigned int index, unsigned int* dest)
{
*dest = lightColor[index];
}
void gfx3d_GetLineData(int line, u16** dst, u8** dstAlpha)
{
*dst = gfx3d_convertedScreen+((line)<<8);
if(dstAlpha != NULL)
{
*dstAlpha = gfx3d_convertedAlpha+((line)<<8);
}
}
//http://www.opengl.org/documentation/specs/version1.1/glspec1.1/node17.html
//talks about the state required to process verts in quadlists etc. helpful ideas.
//consider building a little state structure that looks exactly like this describes
SFORMAT SF_GFX3D[]={
{ "GCTL", 4, 1, &control},
{ "GPAT", 4, 1, &polyAttr},
{ "GPAP", 4, 1, &polyAttrPending},
{ "GINB", 4, 1, &inBegin},
{ "GTFM", 4, 1, &textureFormat},
{ "GTPA", 4, 1, &texturePalette},
{ "GMOD", 4, 1, &mode},
{ "GMTM", 4,16, mtxTemporal},
{ "GMCU", 4,64, mtxCurrent},
{ "ML4I", 1, 1, &ML4x4ind},
{ "ML3I", 1, 1, &ML4x3ind},
{ "MM4I", 1, 1, &MM4x4ind},
{ "MM3I", 1, 1, &MM4x3ind},
{ "MMxI", 1, 1, &MM3x3ind},
{ "GCOR", 4, 1, coord},
{ "GCOI", 1, 1, &coordind},
{ "GVFM", 4, 1, &vtxFormat},
{ "GTRN", 4, 4, trans},
{ "GTRI", 1, 1, &transind},
{ "GSCA", 4, 4, scale},
{ "GSCI", 1, 1, &scaleind},
{ "G_T_", 4, 1, &_t},
{ "G_S_", 4, 1, &_s},
{ "GL_T", 4, 1, &last_t},
{ "GL_S", 4, 1, &last_s},
{ "GLCM", 4, 1, &clCmd},
{ "GLIN", 4, 1, &clInd},
#ifdef USE_GEOMETRY_FIFO_EMULATION
{ "GLI2", 4, 1, &clInd2},
#endif
{ "GLBT", 4, 1, &BTind},
{ "GLPT", 4, 1, &PTind},
{ "GLPC", 4, 4, PTcoords},
{ "GLF9", 4, 1, &gxFIFO.tail},
{ "GLF9", 1, 261, &gxFIFO.cmd[0]},
{ "GLF9", 4, 261, &gxFIFO.param[0]},
{ "GCOL", 1, 4, colorRGB},
{ "GLCO", 4, 4, lightColor},
{ "GLDI", 4, 4, lightDirection},
{ "GMDI", 2, 1, &dsDiffuse},
{ "GMAM", 2, 1, &dsAmbient},
{ "GMSP", 2, 1, &dsSpecular},
{ "GMEM", 2, 1, &dsEmission},
{ "GFLP", 4, 1, &flushPending},
{ "GDRP", 4, 1, &drawPending},
{ "GSET", 4, 1, &gfx3d.enableTexturing},
{ "GSEA", 4, 1, &gfx3d.enableAlphaTest},
{ "GSEB", 4, 1, &gfx3d.enableAlphaBlending},
{ "GSEX", 4, 1, &gfx3d.enableAntialiasing},
{ "GSEE", 4, 1, &gfx3d.enableEdgeMarking},
{ "GSSH", 4, 1, &gfx3d.shading},
{ "GSWB", 4, 1, &gfx3d.wbuffer},
{ "GSSM", 4, 1, &gfx3d.sortmode},
{ "GSAR", 1, 1, &gfx3d.alphaTestRef},
{ "GSVP", 4, 1, &viewport},
{ "GSCC", 4, 1, &gfx3d.clearColor},
{ "GSCD", 4, 1, &gfx3d.clearDepth},
{ "GSFC", 4, 4, gfx3d.fogColor},
{ "GSFO", 4, 1, &gfx3d.fogOffset},
{ "GSTT", 4, 32, gfx3d.rgbToonTable},
{ "GSST", 4, 128, shininessTable},
{ "GSSI", 4, 1, &shininessInd},
//------------------------
{ "GTST", 4, 1, &triStripToggle},
{ "GTVC", 4, 1, &tempVertInfo.count},
{ "GTVM", 4, 4, tempVertInfo.map},
{ "GTVF", 4, 1, &tempVertInfo.first},
{ "G3CS", 2, 256*192, gfx3d_convertedScreen},
{ "G3CA", 2, 256*192, gfx3d_convertedAlpha},
{ 0 }
};
//-------------savestate
void gfx3d_savestate(std::ostream* os)
{
//version
write32le(2,os);
//dump the render lists
OSWRITE(vertlist->count);
for(int i=0;i<vertlist->count;i++)
vertlist->list[i].save(os);
OSWRITE(polylist->count);
for(int i=0;i<polylist->count;i++)
polylist->list[i].save(os);
for(int i=0;i<4;i++)
{
OSWRITE(mtxStack[i].position);
for(int j=0;j<mtxStack[i].size*16+16;j++)
OSWRITE(mtxStack[i].matrix[j]);
}
}
bool gfx3d_loadstate(std::istream* is, int size)
{
int version;
if(read32le(&version,is) != 1) return false;
if(size==8) version = 0;
gfx3d_glPolygonAttrib_cache();
gfx3d_glTexImage_cache();
gfx3d_Control_cache();
gfx3d_glLightDirection_cache(0);
gfx3d_glLightDirection_cache(1);
gfx3d_glLightDirection_cache(2);
gfx3d_glLightDirection_cache(3);
//jiggle the lists. and also wipe them. this is clearly not the best thing to be doing.
listTwiddle = 0;
polylist = &polylists[listTwiddle];
vertlist = &vertlists[listTwiddle];
if(version>=1)
{
OSREAD(vertlist->count);
for(int i=0;i<vertlist->count;i++)
vertlist->list[i].load(is);
OSREAD(polylist->count);
for(int i=0;i<polylist->count;i++)
polylist->list[i].load(is);
}
if(version>=2)
{
for(int i=0;i<4;i++)
{
OSREAD(mtxStack[i].position);
for(int j=0;j<mtxStack[i].size*16+16;j++)
OSREAD(mtxStack[i].matrix[j]);
}
}
gfx3d.polylist = &polylists[listTwiddle^1];
gfx3d.vertlist = &vertlists[listTwiddle^1];
gfx3d.polylist->count=0;
gfx3d.vertlist->count=0;
return true;
}