#include "Globals.h" #include "D3DShader.h" #include "PixelShader.h" #include "BPStructs.h" #include "XFStructs.h" #include "W32Util/Misc.h" #include "Utils.h" /* old tev->pixelshader notes color for this stage (alpha, color) is given by bpmem.tevorders[0].colorchan0 konstant for this stage (alpha, color) is given by bpmem.tevksel inputs are given by bpmem.combiners[0].colorC.a/b/c/d << could be current chan color according to GXTevColorArg table above output is given by .outreg tevtemp is set according to swapmodetables and */ const float epsilon = 1.0f/255.0f; const char *tevKSelTableC[] = { "1,1,1", //KCSEL_1 = 0x00 "0.875,0.875,0.875",//KCSEL_7_8 = 0x01 "0.75,0.75,0.75", //KCSEL_3_4 = 0x02 "0.625,0.625,0.625",//KCSEL_5_8 = 0x03 "0.5,0.5,0.5", //KCSEL_1_2 = 0x04 "0.375,0.375,0.375",//KCSEL_3_8 = 0x05 "0.25,0.25,0.25", //KCSEL_1_4 = 0x06 "0.125,0.125,0.125",//KCSEL_1_8 = 0x07 "ERROR", //0x08 "ERROR", //0x09 "ERROR", //0x0a "ERROR", //0x0b "k0.rgb",//KCSEL_K0 = 0x0C "k1.rgb",//KCSEL_K1 = 0x0D "k2.rgb",//KCSEL_K2 = 0x0E "k3.rgb",//KCSEL_K3 = 0x0F "k0.rrr",//KCSEL_K0_R = 0x10 "k1.rrr",//KCSEL_K1_R = 0x11 "k2.rrr",//KCSEL_K2_R = 0x12 "k3.rrr",//KCSEL_K3_R = 0x13 "k0.ggg",//KCSEL_K0_G = 0x14 "k1.ggg",//KCSEL_K1_G = 0x15 "k2.ggg",//KCSEL_K2_G = 0x16 "k3.ggg",//KCSEL_K3_G = 0x17 "k0.bbb",//KCSEL_K0_B = 0x18 "k1.bbb",//KCSEL_K1_B = 0x19 "k2.bbb",//KCSEL_K2_B = 0x1A "k3.bbb",//KCSEL_K3_B = 0x1B "k0.aaa",//KCSEL_K0_A = 0x1C "k1.aaa",//KCSEL_K1_A = 0x1D "k2.aaa",//KCSEL_K2_A = 0x1E "k3.aaa",//KCSEL_K3_A = 0x1F }; const char *tevKSelTableA[] = { "1", //KASEL_1 = 0x00 "0.875",//KASEL_7_8 = 0x01 "0.75", //KASEL_3_4 = 0x02 "0.625",//KASEL_5_8 = 0x03 "0.5", //KASEL_1_2 = 0x04 "0.375",//KASEL_3_8 = 0x05 "0.25", //KASEL_1_4 = 0x06 "0.125",//KASEL_1_8 = 0x07 "ERROR",//0x08 "ERROR",//0x09 "ERROR",//0x0a "ERROR",//0x0b "ERROR",//0x0c "ERROR",//0x0d "ERROR",//0x0e "ERROR",//0x0f "k0.r", //KASEL_K0_R = 0x10 "k1.r", //KASEL_K1_R = 0x11 "k2.r", //KASEL_K2_R = 0x12 "k3.r", //KASEL_K3_R = 0x13 "k0.g", //KASEL_K0_G = 0x14 "k1.g", //KASEL_K1_G = 0x15 "k2.g", //KASEL_K2_G = 0x16 "k3.g", //KASEL_K3_G = 0x17 "k0.b", //KASEL_K0_B = 0x18 "k1.b", //KASEL_K1_B = 0x19 "k2.b", //KASEL_K2_B = 0x1A "k3.b", //KASEL_K3_B = 0x1B "k0.a", //KASEL_K0_A = 0x1C "k1.a", //KASEL_K1_A = 0x1D "k2.a", //KASEL_K2_A = 0x1E "k3.a", //KASEL_K3_A = 0x1F }; const char *tevScaleTable[] = { "1", //SCALE_1 "2", //SCALE_2 "4", //SCALE_4 "0.5", //DIVIDE_2 }; const char *tevBiasTable[] = { "", //ZERO, "+0.5", //ADD_HALF, "-0.5", //SUB_HALF, "", //WTF? seen in shadow2 }; const char *tevOpTable[] = { "+", //ADD = 0, "-", //SUB = 1, }; const char *tevCompOpTable[] = { ">", "==", }; #define TEV_COMP_R8 0 #define TEV_COMP_GR16 1 #define TEV_COMP_BGR24 2 #define TEV_COMP_RGB8 3 const char *tevCInputTable[] = { "prev.rgb", //CPREV, "prev.aaa", //APREV, "c0.rgb", //C0, "c0.aaa", //A0, "c1.rgb", //C1, "c1.aaa", //A1, "c2.rgb", //C2, "c2.aaa", //A2, "textemp.rgb", //TEXC, "textemp.aaa", //TEXA, "rastemp.rgb", //RASC, "rastemp.aaa", //RASA, "float3(1,1,1)", //ONE, "float3(.5,.5,.5)", //HALF, "konsttemp.rgb", //KONST, "float3(0,0,0)", //ZERO "PADERROR", "PADERROR", "PADERROR", "PADERROR", "PADERROR", "PADERROR", "PADERROR", "PADERROR", "PADERROR", "PADERROR", "PADERROR", "PADERROR", "PADERROR", "PADERROR", "PADERROR", "PADERROR", "PADERROR", "PADERROR", "PADERROR", "PADERROR", }; const char *tevCInputTable2[] = { "prev", //CPREV, "(prev.aaa)", //APREV, "c0", //C0, "(c0.aaa)", //A0, "c1", //C1, "(c1.aaa)", //A1, "c2", //C2, "(c2.aaa)", //A2, "textemp", //TEXC, "(textemp.aaa)", //TEXA, "rastemp", //RASC, "(rastemp.aaa)", //RASA, "float3(1,1,1)", //ONE, "float3(.5,.5,.5)", //HALF, "konsttemp", //KONST, "float3(0,0,0)", //ZERO "PADERROR", "PADERROR", "PADERROR", "PADERROR", "PADERROR", "PADERROR", "PADERROR", "PADERROR", "PADERROR", "PADERROR", "PADERROR", "PADERROR", "PADERROR", "PADERROR", "PADERROR", "PADERROR", "PADERROR", "PADERROR", "PADERROR", "PADERROR", }; const char *tevAInputTable[] = { "prev.a", //APREV, "c0.a", //A0, "c1.a", //A1, "c2.a", //A2, "textemp.a", //TEXA, "rastemp.a", //RASA, "konsttemp.a", //KONST, (hw1 had quarter) "0.0", //ZERO "PADERROR", "PADERROR", "PADERROR", "PADERROR", "PADERROR", "PADERROR", "PADERROR", "PADERROR", "PADERROR", "PADERROR", "PADERROR", "PADERROR", "PADERROR", "PADERROR", "PADERROR", }; const char *tevRasTable[] = { "colors[0]",//RAS1_CC_0 0x00000000 /* color channel 0 */ "colors[1]",//RAS1_CC_1 0x00000001 /* color channel 1 */ "ERROR", //2 "ERROR", //3 "ERROR", //4 "float4(0,1,0,1)", //RAS1_CC_B 0x00000005 /* indirect texture bump alpha */ //green cuz unsupported "float4(0,1,0,1)", //RAS1_CC_BN 0x00000006 /* ind tex bump alpha, normalized 0-255 *///green cuz unsupported "float4(0,0,0,0)", //RAS1_CC_Z 0x00000007 /* set color value to zero */ }; const char *tevCOutputTable[] = { "prev.rgb", "c0.rgb", "c1.rgb", "c2.rgb", }; const char *tevAOutputTable[] = { "prev.a", "c0.a", "c1.a", "c2.a", }; const char *texFuncs[] = { "tex2D", "tex2Dproj" }; //I hope we don't get too many hash collisions :p //all these magic numbers are primes, it should help a bit tevhash GetCurrentTEV() { u32 hash = bpmem.genMode.numindstages + bpmem.genMode.numtevstages*11 + bpmem.genMode.numtexgens*8*17; for (int i=0; i<(int)bpmem.genMode.numtevstages+1; i++) { hash = _rotl(hash,3) ^ (bpmem.combiners[i].colorC.hex*13); hash = _rotl(hash,7) ^ ((bpmem.combiners[i].alphaC.hex&0xFFFFFFFC)*3); hash = _rotl(hash,9) ^ texcoords[i].texmtxinfo.projection*451; } for (int i=0; i<(int)bpmem.genMode.numtevstages/2+1; i++) { hash = _rotl(hash,13) ^ (bpmem.tevorders[i].hex*7); } for (int i=0; i<8; i++) { hash = _rotl(hash,3) ^ bpmem.tevksel[i].swap1; hash = _rotl(hash,3) ^ bpmem.tevksel[i].swap2; } hash ^= bpmem.dstalpha.enable ^ 0xc0debabe; hash = _rotl(hash,4) ^ bpmem.alphaFunc.comp0*7; hash = _rotl(hash,4) ^ bpmem.alphaFunc.comp1*13; hash = _rotl(hash,4) ^ bpmem.alphaFunc.logic*11; return hash; } char text[65536]; #define WRITE p+=sprintf void WriteStage(char *&p, int n); void WriteAlphaTest(char *&p); char *swapColors = "rgba"; char swapModeTable[4][5]; void BuildSwapModeTable() { //bpmem.tevregs[0]. for (int i=0; i<4; i++) { swapModeTable[i][0]=swapColors[bpmem.tevksel[i*2].swap1]; swapModeTable[i][1]=swapColors[bpmem.tevksel[i*2].swap2]; swapModeTable[i][2]=swapColors[bpmem.tevksel[i*2+1].swap1]; swapModeTable[i][3]=swapColors[bpmem.tevksel[i*2+1].swap2]; swapModeTable[i][4]=0; } } LPDIRECT3DPIXELSHADER9 GeneratePixelShader() { DVSTARTPROFILE(); BuildSwapModeTable(); int numStages = bpmem.genMode.numtevstages + 1; int numTexgen = bpmem.genMode.numtexgens; int numSamplers = 8; char *p = text; WRITE(p,"//Pixel Shader for TEV stages\n"); WRITE(p,"//%i TEV stages, %i texgens, %i IND stages, %i COL channels\n", bpmem.genMode.numtevstages,bpmem.genMode.numtexgens,bpmem.genMode.numindstages,bpmem.genMode.numcolchans); //write kcolor declarations for (int i=0; i<4; i++) WRITE(p,"float4 k%i : register(c%i);\n",i,PS_CONST_KCOLORS+i); for (int i=0; i<3; i++) WRITE(p,"float4 color%i : register(c%i);\n",i,PS_CONST_COLORS+i+1); WRITE(p,"float constalpha : register(c%i);\n",PS_CONST_CONSTALPHA); WRITE(p,"float2 alphaRef : register(c%i);\n",PS_CONST_ALPHAREF); WRITE(p,"\n"); WRITE(p,"sampler samp[%i] : register(s0);\n",numSamplers,numSamplers); WRITE(p,"\n"); WRITE(p,"float4 main(in float4 colors[2] : COLOR0"); if (numTexgen) WRITE(p,", float4 uv[%i] : TEXCOORD0",numTexgen); else WRITE(p,", float4 uv[1] : TEXCOORD0"); //HACK WRITE(p,") : COLOR\n"); WRITE(p,"{\n"); WRITE(p,"float4 c0=color0,c1=color1,c2=color2,prev=float4(0.0f,0.0f,0.0f,0.0f),textemp,rastemp,konsttemp;\n"); WRITE(p,"\n"); //WRITE(p, "return 1;}\n"); //return D3D::CompilePShader(text,(int)(p-text)); for (int i=0; i %s.r) ? %s : float3(0,0,0)),\n", tevCInputTable[cc.d],tevCInputTable2[cc.a], tevCInputTable2[cc.b],tevCInputTable[cc.c]); else WRITE(p," %s + (abs(%s.r - %s.r)<%f ? %s : float3(0,0,0)),\n", tevCInputTable[cc.d],tevCInputTable2[cc.a], tevCInputTable2[cc.b],epsilon,tevCInputTable[cc.c]); break; default: WRITE(p,"float3(0,0,0),\n"); break; } } //end of color ////////////////////////////////////////////////////////////////////////// //start of alpha ////////////////////////////////////////////////////////////////////////// if (ac.bias != TB_COMPARE) { //normal alpha combiner goes here WRITE(p," %s*(%s%s",tevScaleTable[ac.scale],tevAInputTable[ac.d],tevOpTable[ac.op]); WRITE(p,"(lerp(%s,%s,%s)%s))\n", tevAInputTable[ac.a],tevAInputTable[ac.b], tevAInputTable[ac.c],tevBiasTable[ac.bias]); } else { //compare alpha combiner goes here if (ac.op==0) WRITE(p," %s + ((%s > %s) ? %s : 0)\n", tevAInputTable[ac.d],tevAInputTable[ac.a], tevAInputTable[ac.b],tevAInputTable[ac.c]); else WRITE(p," %s + (abs(%s - %s)<%f ? %s : 0)\n", tevAInputTable[ac.d],tevAInputTable[ac.a], tevAInputTable[ac.b],epsilon,tevAInputTable[ac.c]); } WRITE(p,");"); //end of alpha WRITE(p,"\n"); } char *alphaRef[2] = { "alphaRef.x", "alphaRef.y" }; void WriteAlphaCompare(char *&p, int num, int comp) { WRITE(p," res%i = ",num); switch(comp) { case COMPARE_ALWAYS: WRITE(p,"0;\n"); break; case COMPARE_NEVER: WRITE(p,"1;\n"); break; case COMPARE_LEQUAL: WRITE(p,"prev.a - %s.x;\n",alphaRef[num]); break; case COMPARE_LESS: WRITE(p,"prev.a - %s.x + %f;\n",alphaRef[num],epsilon*2);break; case COMPARE_GEQUAL: WRITE(p,"%s - prev.a;\n",alphaRef[num]); break; case COMPARE_GREATER: WRITE(p,"%s - prev.a + %f;\n",alphaRef[num],epsilon*2);break; case COMPARE_EQUAL: WRITE(p,"abs(%s-prev.a)-%f;\n",alphaRef[num],epsilon*2); break; case COMPARE_NEQUAL: WRITE(p,"%f-abs(%s-prev.a);\n",epsilon*2,alphaRef[num]); break; } } void WriteAlphaTest(char *&p) { AlphaOp op = (AlphaOp)bpmem.alphaFunc.logic; Compare comp[2] = {(Compare)bpmem.alphaFunc.comp0,(Compare)bpmem.alphaFunc.comp1}; //first kill all the simple cases if (op == ALPHAOP_AND && (comp[0] == COMPARE_ALWAYS && comp[1] == COMPARE_ALWAYS)) return; if (op == ALPHAOP_OR && (comp[0] == COMPARE_ALWAYS || comp[1] == COMPARE_ALWAYS)) return; for (int i=0; i<2; i++) { int one = i; int other = 1-i; switch(op) { case ALPHAOP_XOR: if (comp[one] == COMPARE_ALWAYS && comp[other] == COMPARE_NEVER) return; break; case ALPHAOP_XNOR: if (comp[one] == COMPARE_ALWAYS && comp[other] == COMPARE_ALWAYS) return; if (comp[one] == COMPARE_ALWAYS && comp[other] == COMPARE_NEVER) return; break; } } //Ok, didn't get to do the easy way out :P // do the general way WRITE(p,"float res0,res1;\n"); WriteAlphaCompare(p,0,bpmem.alphaFunc.comp0); WriteAlphaCompare(p,1,bpmem.alphaFunc.comp1); WRITE(p,"res0=max(res0,0);\n"); WRITE(p,"res1=max(res1,0);\n"); //probably should use lookup textures for some of these :P switch(bpmem.alphaFunc.logic) { case ALPHAOP_AND: // if both are 0 WRITE(p,"clip(-(res0+res1)+%f);\n",epsilon); break; case ALPHAOP_OR: //if either is 0 WRITE(p,"clip(-res0*res1+%f);\n",epsilon*epsilon); break; case ALPHAOP_XOR: //hmm, this might work: WRITE(p,"res0=(res0>0?1:0)-.5;\n"); WRITE(p,"res1=(res1>0?1:0)-.5;\n"); WRITE(p,"clip(-res0*res1);\n",epsilon); break; case ALPHAOP_XNOR: WRITE(p,"res0=(res0>0?1:0)-.5;\n"); WRITE(p,"res1=(res1>0?1:0)-.5;\n"); WRITE(p,"clip(res0*res1);\n",epsilon); break; } }