diff --git a/include/dolphin/gx/GXAurora.h b/include/dolphin/gx/GXAurora.h index ff5f52c..3ee2cd4 100644 --- a/include/dolphin/gx/GXAurora.h +++ b/include/dolphin/gx/GXAurora.h @@ -23,6 +23,12 @@ extern "C" { */ #define GX_AURORA_LOAD_SCISSOR_RENDER 0x0002 +/** + * Loads a full 4x4 projection matrix, bypassing GXSetProjection's 6-parameter + * hardware encoding. Must be followed by sixteen f32 values in row-major order. + */ +#define GX_AURORA_LOAD_PROJECTION_FULL 0x0003 + /** * Aurora equivalent of CP_REG_ARRAYBASE_ID: sets the base address and size of a vertex array. * This command must be followed by a 64-bit memory address, 32-bit size, and 1-byte little-endian flag. @@ -144,6 +150,11 @@ void GXSetScissorRender(u32 left, u32 top, u32 wd, u32 ht); void GX2SetPolygonOffset(f32 mFrontOffset, f32 mFrontScale, f32 mBackOffset, f32 mBackScale, f32 mClamp); +/** + * Load an arbitrary 4x4 projection matrix, avoiding the 6-parameter hardware encoding. + */ +void GXSetProjectionFull(const void* mtx); + /** * Create an offscreen framebuffer and switch rendering to it. * All subsequent GX rendering will target this framebuffer until GXRestoreFrameBuffer() is called. diff --git a/lib/dolphin/gx/GXAurora.cpp b/lib/dolphin/gx/GXAurora.cpp index 2549a6b..af3c4cb 100644 --- a/lib/dolphin/gx/GXAurora.cpp +++ b/lib/dolphin/gx/GXAurora.cpp @@ -66,6 +66,14 @@ void GXSetScissorRender(u32 left, u32 top, u32 wd, u32 ht) { GX_WRITE_U32(ht); } +void GXSetProjectionFull(const void* mtx) { + const f32* values = reinterpret_cast(mtx); + GX_WRITE_AURORA(GX_AURORA_LOAD_PROJECTION_FULL); + for (int i = 0; i < 16; ++i) { + GX_WRITE_F32(values[i]); + } +} + void GX2SetPolygonOffset(f32 mFrontOffset, f32 mFrontScale, f32 mBackOffset, f32 mBackScale, f32 mClamp) { GX_WRITE_AURORA(GX2_SET_POLYGON_OFFSET); GX_WRITE_F32(mFrontOffset); diff --git a/lib/gx/command_processor.cpp b/lib/gx/command_processor.cpp index cd12369..ef54b31 100644 --- a/lib/gx/command_processor.cpp +++ b/lib/gx/command_processor.cpp @@ -1741,6 +1741,16 @@ void handle_aurora(const u8* data, u32& pos, u32 size, bool bigEndian) { const int32_t height = static_cast(read_u32(data + pos, bigEndian)); pos += 4; set_render_scissor({left, top, width, height}); + } else if (subCmd == GX_AURORA_LOAD_PROJECTION_FULL) { + CHECK(pos + 64 <= size, "GX_AURORA_LOAD_PROJECTION_FULL read overrun"); + auto& proj = g_gxState.proj; + for (int r = 0; r < 4; ++r) { + for (int c = 0; c < 4; ++c) { + proj[r][c] = read_f32(data + pos, bigEndian); + pos += 4; + } + } + g_gxState.stateDirty = true; } else if (subCmd >= GX_AURORA_LOAD_ARRAYBASE && subCmd <= (GX_AURORA_LOAD_ARRAYBASE | 0x0f)) { CHECK(pos + 13 <= size, "GX_AURORA_LOAD_ARRAYBASE read overrun"); u32 attrIdx = subCmd - GX_AURORA_LOAD_ARRAYBASE + GX_VA_POS;