DeSmuMEの3Dレンダラの行列をLuaからいじれるようにするhack
(2015年にやったものを発掘して記事にしているのでもう細部を忘れています)
ゲーム内の処理とか一切触らずにこんな風に斜めから映したり、街の全体像を映したりできます。
(建物の一部が映ってなかったり、キャラクターの位置がずれているのはすでに修正済みのバグだった気がします)
EmuLuaにemu.extramatrixmul(), emu.extramatrixproj()という関数を用意しています。
それを使ってたとえばこんな風に書きます。
function translate(x, y, z) emu.extramatrixmul({1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, x, y, z, 1}) end function rotateY(theta) local c = math.cos(theta) local s = math.sin(theta) emu.extramatrixmul({c, 0, s, 0, 0, 1, 0, 0,-s, 0, c, 0, 0, 0, 0, 1}) end function rotateX(theta) local c = math.cos(theta) local s = math.sin(theta) emu.extramatrixmul({1, 0, 0, 0, 0, c,-s, 0, 0, s, c, 0, 0, 0, 0, 1}) end function scale(k) emu.extramatrixmul({k, 0, 0, 0, 0, k, 0, 0, 0, 0, k, 0, 0, 0, 0, 1}) end function proj(scalar, x, y, z) emu.extramatrixproj({scalar, 0, 0, 0, 0, scalar, 0, 0, 0, 0, 1, 0, x, y, z, 1}) end emu.registerbefore(function () emu.extramatrixinit() translate(-200, 0, 0) rotateY(0.5) scale(0.6) proj(1/2,0,0, 0) end)
DeSmuMEのソースコードのdiffです。
元のなったDeSmuMEのリビジョンは忘れました(
diff -ru trunk-copy/desmume/src/gfx3d.cpp 3d-hack/desmume/src/gfx3d.cpp --- trunk-copy/desmume/src/gfx3d.cpp 2014-09-02 11:36:48.539893300 +0900 +++ 3d-hack/desmume/src/gfx3d.cpp 2015-12-17 15:36:03.263874500 +0900 @@ -54,6 +54,7 @@ #include "GPU_OSD.h" #endif +//#define DEBUG_MATRIX /* thoughts on flush timing: @@ -320,6 +321,10 @@ static CACHE_ALIGN s32 mtxCurrent [4][16]; static CACHE_ALIGN s32 mtxTemporal[16]; static u32 mode = 0; +s32 extraMatrix[16]; +s32 extraMatrixProj[16]; + +static const char * const mode_name[] = {"projection", "coordinate", "directional", "texture"}; // Indexes for matrix loading/multiplication static u8 ML4x4ind = 0; @@ -534,6 +539,8 @@ MatrixInit (mtxCurrent[2]); MatrixInit (mtxCurrent[3]); MatrixInit (mtxTemporal); + MatrixInit (extraMatrix); + MatrixInit (extraMatrixProj); MatrixStackInit(&mtxStack[0]); MatrixStackInit(&mtxStack[1]); @@ -560,7 +567,7 @@ memset(gfx3d_convertedScreen,0,sizeof(gfx3d_convertedScreen)); - gfx3d.state.clearDepth = DS_DEPTH15TO24(0x7FFF); + gfx3d.state.clearDepth = 0xffffffffu; clInd2 = 0; isSwapBuffers = FALSE; @@ -587,6 +594,8 @@ return fx32_shiftdown(fx32_mul(a[0],b[0]) + fx32_mul(a[1],b[1]) + fx32_mul(a[2],b[2])); } +static void printMatrix4x4(s32 *m); + #define SUBMITVERTEX(ii, nn) polylist->list[polylist->count].vertIndexes[ii] = tempVertInfo.map[nn]; //Submit a vertex to the GE static void SetVertex() @@ -623,8 +632,11 @@ //so that we only have to multiply one matrix here //(we could lazy cache the concatenated clip matrix and only generate it //when we need to) - MatrixMultVec4x4_M2(mtxCurrent[0], coordTransformed); - + MatrixMultVec4x4(mtxCurrent[1],coordTransformed); + MatrixMultVec4x4(extraMatrix,coordTransformed); + MatrixMultVec4x4(mtxCurrent[0],coordTransformed); + MatrixMultVec4x4(extraMatrixProj,coordTransformed); + //printf("%f %f %f\n",s16coord[0]/4096.0f,s16coord[1]/4096.0f,s16coord[2]/4096.0f); //printf("x %f %f %f %f\n",mtxCurrent[0][0]/4096.0f,mtxCurrent[0][1]/4096.0f,mtxCurrent[0][2]/4096.0f,mtxCurrent[0][3]/4096.0f); //printf(" = %f %f %f %f\n",coordTransformed[0]/4096.0f,coordTransformed[1]/4096.0f,coordTransformed[2]/4096.0f,coordTransformed[3]/4096.0f); @@ -645,6 +657,18 @@ printf("wtf\n"); } VERT &vert = vertlist->list[vertIndex]; + + //coordTransformed[0] *= 0.5; + //coordTransformed[1] *= 0.5; +#ifdef DEBUG_MATRIX + printf("projection matrix = "); + printMatrix4x4(mtxCurrent[0]); + puts(""); + printf("coordinate matrix = "); + printMatrix4x4(mtxCurrent[1]); + puts(""); + printf("setVertex: %d (%.1f,%.1f,%.1f) -> (%.1f,%.1f,%.1f,%.1f)\n", vertIndex, coord[0] / 4096.0f, coord[1] / 4096.0f, coord[2] / 4096.0f, coordTransformed[0] / 4096.0f, coordTransformed[1] / 4096.0f, coordTransformed[2] / 4096.0f, coordTransformed[3] / 4096.0f); +#endif //printf("%f %f %f\n",coordTransformed[0],coordTransformed[1],coordTransformed[2]); //if(coordTransformed[1] > 20) @@ -839,6 +863,17 @@ //=============================================================================== +static void printMatrix4x4(s32 *m) +{ + printf("[[%.1f,%.1f,%.1f,%.1f],[%.1f,%.1f,%.1f,%.1f],[%.1f,%.1f,%.1f,%.1f],[%.1f,%.1f,%.1f,%.1f]]", m[0] / 4096.0, m[1] / 4096.0, m[2] / 4096.0, m[3] / 4096.0, m[4] / 4096.0, m[5] / 4096.0, m[6] / 4096.0, m[7] / 4096.0, m[8] / 4096.0, m[9] / 4096.0, m[10] / 4096.0, m[11] / 4096.0, m[12] / 4096.0, m[13] / 4096.0, m[14] / 4096.0, m[15] / 4096.0); +} + +static void printMatrix4x3(s32 *m) +{ + printf("[[%.1f,%.1f,%.1f],[%.1f,%.1f,%.1f],[%.1f,%.1f,%.1f],[%.1f,%.1f,%.1f]]", m[0] / 4096.0, m[1] / 4096.0, m[2] / 4096.0, m[4] / 4096.0, m[5] / 4096.0, m[6] / 4096.0, m[8] / 4096.0, m[9] / 4096.0, m[10] / 4096.0, m[12] / 4096.0, m[13] / 4096.0, m[14] / 4096.0); +} + + static void gfx3d_glMatrixMode(u32 v) { mode = (v&3); @@ -850,13 +885,22 @@ { //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); - + +#ifdef DEBUG_MATRIX + printf("pushMatrix: %s (cur = ", mode_name[mymode]); + printMatrix4x4(mtxCurrent[mymode]); + printf(")\n"); MatrixStackPushMatrix(&mtxStack[mymode], mtxCurrent[mymode]); +#endif GFX_DELAY(17); - if(mymode==2) + if(mymode==2) { +#ifdef DEBUG_MATRIX + printf("pushMatrix: %s\n", mode_name[1]); +#endif MatrixStackPushMatrix (&mtxStack[1], mtxCurrent[1]); + } } static void gfx3d_glPopMatrix(s32 i) @@ -877,11 +921,18 @@ //please note that our ability to skip treating this as signed is dependent on the modular addressing later. if that ever changes, we need to change this back. MatrixStackPopMatrix(mtxCurrent[mymode], &mtxStack[mymode], i); +#ifdef DEBUG_MATRIX + printf("popMatrix: %s %d\n", mode_name[mymode], i); +#endif GFX_DELAY(36); - if (mymode == 2) + if (mymode == 2) { +#ifdef DEBUG_MATRIX + printf("popMatrix: %s %d\n", mode_name[1], i); +#endif MatrixStackPopMatrix(mtxCurrent[1], &mtxStack[1], i); + } } static void gfx3d_glStoreMatrix(u32 v) @@ -903,12 +954,23 @@ if(v==31) MMU_new.gxstat.se = 1; +#ifdef DEBUG_MATRIX + printf("storeMatrix: %s %d (mtx = ", mode_name[mymode], v); + printMatrix4x4(mtxCurrent[mymode]); + printf(")\n"); +#endif MatrixStackLoadMatrix (&mtxStack[mymode], v, mtxCurrent[mymode]); GFX_DELAY(17); - if(mymode==2) + if(mymode==2) { +#ifdef DEBUG_MATRIX + printf("storeMatrix: %s %d (mtx = ", mode_name[1], v); + printMatrix4x4(mtxCurrent[1]); + printf(")\n"); +#endif MatrixStackLoadMatrix (&mtxStack[1], v, mtxCurrent[1]); + } } static void gfx3d_glRestoreMatrix(u32 v) @@ -930,23 +992,36 @@ if(v==31) MMU_new.gxstat.se = 1; - +#ifdef DEBUG_MATRIX + printf("restoreMatrix: %s %d\n", mode_name[mymode], v); +#endif MatrixCopy (mtxCurrent[mymode], MatrixStackGetPos(&mtxStack[mymode], v)); GFX_DELAY(36); - if (mymode == 2) + if (mymode == 2) { +#ifdef DEBUG_MATRIX + printf("restoreMatrix: %s %d\n", mode_name[1], v); +#endif MatrixCopy (mtxCurrent[1], MatrixStackGetPos(&mtxStack[1], v)); + } } static void gfx3d_glLoadIdentity() { +#ifdef DEBUG_MATRIX + printf("loadIdentity: %s\n", mode_name[mode]); +#endif MatrixIdentity (mtxCurrent[mode]); GFX_DELAY(19); - if (mode == 2) + if (mode == 2) { +#ifdef DEBUG_MATRIX + printf("loadIdentity: %s\n", mode_name[1]); +#endif MatrixIdentity (mtxCurrent[1]); + } //printf("identity: %d to: \n",mode); MatrixPrint(mtxCurrent[1]); } @@ -959,13 +1034,22 @@ if(ML4x4ind<16) return FALSE; ML4x4ind = 0; +#ifdef DEBUG_MATRIX + printf("loadMatrix4x4: %s ", mode_name[mode]); + printMatrix4x4(mtxCurrent[mode]); + puts(""); +#endif + GFX_DELAY(19); //vector_fix2float<4>(mtxCurrent[mode], 4096.f); - if (mode == 2) + if (mode == 2) { +#ifdef DEBUG_MATRIX + printf("copy from %s to %s\n", mode_name[2], mode_name[1]); +#endif MatrixCopy (mtxCurrent[1], mtxCurrent[2]); - + } //printf("load4x4: matrix %d to: \n",mode); MatrixPrint(mtxCurrent[1]); return TRUE; } @@ -985,10 +1069,19 @@ mtxCurrent[mode][3] = mtxCurrent[mode][7] = mtxCurrent[mode][11] = 0; mtxCurrent[mode][15] = (1<<12); +#ifdef DEBUG_MATRIX + printf("loadMatrix4x3: %s ", mode_name[mode]); + printMatrix4x3(mtxCurrent[mode]); + puts(""); +#endif GFX_DELAY(30); - if (mode == 2) + if (mode == 2) { +#ifdef DEBUG_MATRIX + printf("copy from %s to %s\n", mode_name[2], mode_name[1]); +#endif MatrixCopy (mtxCurrent[1], mtxCurrent[2]); + } //printf("load4x3: matrix %d to: \n",mode); MatrixPrint(mtxCurrent[1]); return TRUE; } @@ -1005,10 +1098,18 @@ //vector_fix2float<4>(mtxTemporal, 4096.f); +#ifdef DEBUG_MATRIX + printf("multMatrix4x4: %s ", mode_name[mode]); + printMatrix4x4(mtxTemporal); + puts(""); +#endif MatrixMultiply (mtxCurrent[mode], mtxTemporal); if (mode == 2) { +#ifdef DEBUG_MATRIX + printf("copy from %s to %s\n", mode_name[2], mode_name[1]); +#endif MatrixMultiply (mtxCurrent[1], mtxTemporal); GFX_DELAY_M2(30); } @@ -1036,10 +1137,18 @@ mtxTemporal[3] = mtxTemporal[7] = mtxTemporal[11] = 0; mtxTemporal[15] = 1<<12; +#ifdef DEBUG_MATRIX + printf("multMatrix4x3: %s ", mode_name[mode]); + printMatrix4x3(mtxTemporal); + puts(""); +#endif MatrixMultiply (mtxCurrent[mode], mtxTemporal); if (mode == 2) { +#ifdef DEBUG_MATRIX + printf("copy from %s to %s\n", mode_name[2], mode_name[1]); +#endif MatrixMultiply (mtxCurrent[1], mtxTemporal); GFX_DELAY_M2(30); } @@ -1070,10 +1179,16 @@ mtxTemporal[15] = 1<<12; mtxTemporal[12] = mtxTemporal[13] = mtxTemporal[14] = 0; +#ifdef DEBUG_MATRIX + printf("multMatrix3x3: %s [[%.1f,%.1f,%.1f],[%.1f,%.1f,%.1f],[%.1f,%.1f,%.1f]]\n", mode_name[mode], mtxCurrent[mode][0] / 4096.0, mtxCurrent[mode][1] / 4096.0, mtxCurrent[mode][2] / 4096.0, mtxCurrent[mode][4] / 4096.0, mtxCurrent[mode][5] / 4096.0, mtxCurrent[mode][6] / 4096.0, mtxCurrent[mode][8] / 4096.0, mtxCurrent[mode][9] / 4096.0, mtxCurrent[mode][10] / 4096.0); +#endif MatrixMultiply (mtxCurrent[mode], mtxTemporal); if (mode == 2) { +#ifdef DEBUG_MATRIX + printf("copy from %s to %s\n", mode_name[2], mode_name[1]); +#endif MatrixMultiply (mtxCurrent[1], mtxTemporal); GFX_DELAY_M2(30); } @@ -1095,6 +1210,9 @@ if(scaleind<3) return FALSE; scaleind = 0; +#ifdef DEBUG_MATRIX + printf("scaleMatrix: %s [%.1f,%.1f,%.1f]\n", mode_name[(mode==2?1:mode)], scale[0] / 4096.0, scale[1] / 4096.0, scale[2] / 4096.0); +#endif MatrixScale (mtxCurrent[(mode==2?1:mode)], scale); //printf("scale: matrix %d to: \n",mode); MatrixPrint(mtxCurrent[1]); @@ -1117,12 +1235,18 @@ if(transind<3) return FALSE; transind = 0; +#ifdef DEBUG_MATRIX + printf("translateMatrix: %s [%.1f,%.1f,%.1f]\n", mode_name[mode], trans[0] / 4096.0, trans[1] / 4096.0, trans[2] / 4096.0); +#endif MatrixTranslate (mtxCurrent[mode], trans); GFX_DELAY(22); if (mode == 2) { +#ifdef DEBUG_MATRIX + printf("translateMatrix: %s [%.1f,%.1f,%.1f]\n", mode_name[1], trans[0] / 4096.0, trans[1] / 4096.0, trans[2] / 4096.0); +#endif MatrixTranslate (mtxCurrent[1], trans); GFX_DELAY_M2(30); } @@ -1538,11 +1662,15 @@ //but change it all to floating point and do it that way instead CACHE_ALIGN float temp1[16] = {mtxCurrent[1][0]/4096.0f,mtxCurrent[1][1]/4096.0f,mtxCurrent[1][2]/4096.0f,mtxCurrent[1][3]/4096.0f,mtxCurrent[1][4]/4096.0f,mtxCurrent[1][5]/4096.0f,mtxCurrent[1][6]/4096.0f,mtxCurrent[1][7]/4096.0f,mtxCurrent[1][8]/4096.0f,mtxCurrent[1][9]/4096.0f,mtxCurrent[1][10]/4096.0f,mtxCurrent[1][11]/4096.0f,mtxCurrent[1][12]/4096.0f,mtxCurrent[1][13]/4096.0f,mtxCurrent[1][14]/4096.0f,mtxCurrent[1][15]/4096.0f}; CACHE_ALIGN float temp0[16] = {mtxCurrent[0][0]/4096.0f,mtxCurrent[0][1]/4096.0f,mtxCurrent[0][2]/4096.0f,mtxCurrent[0][3]/4096.0f,mtxCurrent[0][4]/4096.0f,mtxCurrent[0][5]/4096.0f,mtxCurrent[0][6]/4096.0f,mtxCurrent[0][7]/4096.0f,mtxCurrent[0][8]/4096.0f,mtxCurrent[0][9]/4096.0f,mtxCurrent[0][10]/4096.0f,mtxCurrent[0][11]/4096.0f,mtxCurrent[0][12]/4096.0f,mtxCurrent[0][13]/4096.0f,mtxCurrent[0][14]/4096.0f,mtxCurrent[0][15]/4096.0f}; + CACHE_ALIGN float extra[16] = {extraMatrix[0]/4096.0f,extraMatrix[1]/4096.0f,extraMatrix[2]/4096.0f,extraMatrix[3]/4096.0f,extraMatrix[4]/4096.0f,extraMatrix[5]/4096.0f,extraMatrix[6]/4096.0f,extraMatrix[7]/4096.0f,extraMatrix[8]/4096.0f,extraMatrix[9]/4096.0f,extraMatrix[10]/4096.0f,extraMatrix[11]/4096.0f,extraMatrix[12]/4096.0f,extraMatrix[13]/4096.0f,extraMatrix[14]/4096.0f,extraMatrix[15]/4096.0f}; + CACHE_ALIGN float extra2[16] = {extraMatrixProj[0]/4096.0f,extraMatrixProj[1]/4096.0f,extraMatrixProj[2]/4096.0f,extraMatrixProj[3]/4096.0f,extraMatrixProj[4]/4096.0f,extraMatrixProj[5]/4096.0f,extraMatrixProj[6]/4096.0f,extraMatrixProj[7]/4096.0f,extraMatrixProj[8]/4096.0f,extraMatrixProj[9]/4096.0f,extraMatrixProj[10]/4096.0f,extraMatrixProj[11]/4096.0f,extraMatrixProj[12]/4096.0f,extraMatrixProj[13]/4096.0f,extraMatrixProj[14]/4096.0f,extraMatrixProj[15]/4096.0f}; DS_ALIGN(16) VERT_POS4f vert = { verts[i].x, verts[i].y, verts[i].z, verts[i].w }; - + _NOSSE_MatrixMultVec4x4(temp1,verts[i].coord); + _NOSSE_MatrixMultVec4x4(extra,verts[i].coord); _NOSSE_MatrixMultVec4x4(temp0,verts[i].coord); + _NOSSE_MatrixMultVec4x4(extra2,verts[i].coord); } //clip each poly @@ -1654,7 +1782,11 @@ void gfx3d_glClearDepth(u32 v) { - gfx3d.state.clearDepth = DS_DEPTH15TO24(v); + if (v == 0x7fff) { + gfx3d.state.clearDepth = 0xffffffffu; + } else { + gfx3d.state.clearDepth = DS_DEPTH15TO24(v); + } } // Ignored for now diff -ru trunk-copy/desmume/src/lua-engine.cpp 3d-hack/desmume/src/lua-engine.cpp --- trunk-copy/desmume/src/lua-engine.cpp 2014-09-02 11:36:48.390793300 +0900 +++ 3d-hack/desmume/src/lua-engine.cpp 2015-12-17 15:35:16.475717600 +0900 @@ -3788,6 +3788,42 @@ return 1; } +extern s32 extraMatrix[16]; +extern s32 extraMatrixProj[16]; + +DEFINE_LUA_FUNCTION(emu_extramatrixinit, "") +{ + MatrixIdentity(extraMatrix); + MatrixIdentity(extraMatrixProj); + return 0; +} + +DEFINE_LUA_FUNCTION(emu_extramatrixmul, "matrix") +{ + s32 tmp[16]; + luaL_checktype(L, 1, LUA_TTABLE); + for (int i = 0; i < 16; i++) { + lua_rawgeti(L, 1, i + 1); + tmp[i] = (s32)(lua_tonumber(L, -1) * 4096); + lua_pop(L, 1); + } + MatrixMultiply(extraMatrix, tmp); + return 0; +} + +DEFINE_LUA_FUNCTION(emu_extramatrixproj, "matrix") +{ + s32 tmp[16]; + luaL_checktype(L, 1, LUA_TTABLE); + for (int i = 0; i < 16; i++) { + lua_rawgeti(L, 1, i + 1); + tmp[i] = (s32)(lua_tonumber(L, -1) * 4096); + lua_pop(L, 1); + } + MatrixMultiply(extraMatrixProj, tmp); + return 0; +} + // TODO /* DEFINE_LUA_FUNCTION(emu_loadrom, "filename") @@ -4541,6 +4577,9 @@ {"registermenustart", emu_registermenustart}, // alternative names // {"openrom", emu_loadrom}, + {"extramatrixinit", emu_extramatrixinit}, + {"extramatrixmul", emu_extramatrixmul}, + {"extramatrixproj", emu_extramatrixproj}, {NULL, NULL} }; static const struct luaL_reg guilib [] = diff -ru trunk-copy/desmume/src/rasterize.cpp 3d-hack/desmume/src/rasterize.cpp --- trunk-copy/desmume/src/rasterize.cpp 2014-09-02 11:36:37.489425300 +0900 +++ 3d-hack/desmume/src/rasterize.cpp 2015-12-17 15:01:54.567041000 +0900 @@ -869,7 +869,8 @@ left->Step(); right->Step(); - if(!RENDERER && _debug_thisPoly) + //if(!RENDERER && _debug_thisPoly) + if (0) { //debug drawing bool top = (horizontal&&first); @@ -1034,6 +1035,7 @@ template<bool SLI> FORCEINLINE void mainLoop(SoftRasterizerEngine* const engine) { + INFO("mainLoop() polyCount=%d\n", engine->clippedPolyCounter); this->engine = engine; lastTexKey = NULL; @@ -1236,7 +1238,7 @@ //I am not sure whether it is right, though. previously this was cleared to 0, as a guess, //but in spiderman2 some fires with polyid 0 try to render on top of the background clearFragment.polyid.translucent = kUnsetTranslucentPolyID; - clearFragment.depth = gfx3d.renderState.clearDepth; + clearFragment.depth = 0xffffffffu; //gfx3d.renderState.clearDepth; clearFragment.stencil = 0; clearFragment.isTranslucentPoly = 0; clearFragment.fogged = BIT15(gfx3d.renderState.clearColor); @@ -1686,17 +1688,18 @@ softRastHasNewData = true; - if (rasterizerCores > 1) + /*if (rasterizerCores > 1) { for(unsigned int i = 0; i < rasterizerCores; i++) { rasterizerUnitTask[i].execute(&execRasterizerUnit, (void *)i); } } - else + else*/ { rasterizerUnit[0].mainLoop<false>(&mainSoftRasterizer); } + //driver->EMU_PauseEmulation(true); } static void SoftRastRenderFinish()