diff options
author | WlodekM <[email protected]> | 2024-06-16 10:35:45 +0300 |
---|---|---|
committer | WlodekM <[email protected]> | 2024-06-16 10:35:45 +0300 |
commit | abef6da56913f1c55528103e60a50451a39628b1 (patch) | |
tree | b3c8092471ecbb73e568cd0d336efa0e7871ee8d /src/Graphics_GCWii.c |
initial commit
Diffstat (limited to 'src/Graphics_GCWii.c')
-rw-r--r-- | src/Graphics_GCWii.c | 598 |
1 files changed, 598 insertions, 0 deletions
diff --git a/src/Graphics_GCWii.c b/src/Graphics_GCWii.c new file mode 100644 index 0000000..d894271 --- /dev/null +++ b/src/Graphics_GCWii.c @@ -0,0 +1,598 @@ +#include "Core.h" +#if defined CC_BUILD_GCWII +#include "_GraphicsBase.h" +#include "Errors.h" +#include "Logger.h" +#include "Window.h" +#include <malloc.h> +#include <string.h> +#include <gccore.h> + +static void* fifo_buffer; +#define FIFO_SIZE (256 * 1024) +extern void* Window_XFB; +static void* xfbs[2]; +static int curFB; +static GfxResourceID white_square; +// https://wiibrew.org/wiki/Developer_tips +// https://devkitpro.org/wiki/libogc/GX + + +/*########################################################################################################################* +*---------------------------------------------------------General---------------------------------------------------------* +*#########################################################################################################################*/ +static void InitGX(void) { + GXRModeObj* mode = VIDEO_GetPreferredMode(NULL); + fifo_buffer = MEM_K0_TO_K1(memalign(32, FIFO_SIZE)); + memset(fifo_buffer, 0, FIFO_SIZE); + + GX_Init(fifo_buffer, FIFO_SIZE); + Gfx_SetViewport(0, 0, mode->fbWidth, mode->efbHeight); + GX_SetDispCopyYScale((f32)mode->xfbHeight / (f32)mode->efbHeight); + GX_SetDispCopySrc(0, 0, mode->fbWidth, mode->efbHeight); + GX_SetDispCopyDst(mode->fbWidth, mode->xfbHeight); + GX_SetCopyFilter(mode->aa, mode->sample_pattern, + GX_TRUE, mode->vfilter); + GX_SetFieldMode(mode->field_rendering, + ((mode->viHeight==2*mode->xfbHeight)?GX_ENABLE:GX_DISABLE)); + + GX_SetCullMode(GX_CULL_NONE); + GX_SetDispCopyGamma(GX_GM_1_0); + GX_InvVtxCache(); + + GX_SetNumChans(1); + + xfbs[0] = Window_XFB; + xfbs[1] = MEM_K0_TO_K1(SYS_AllocateFramebuffer(mode)); +} + +void Gfx_Create(void) { + if (!Gfx.Created) InitGX(); + + Gfx.MaxTexWidth = 1024; + Gfx.MaxTexHeight = 1024; + Gfx.MaxTexSize = 512 * 512; + + Gfx.MinTexWidth = 4; + Gfx.MinTexHeight = 4; + Gfx.Created = true; + gfx_vsync = true; + + Gfx_RestoreState(); +} + +void Gfx_Free(void) { + Gfx_FreeState(); + GX_AbortFrame(); + //GX_Flush(); // TODO needed? + VIDEO_Flush(); +} +cc_bool Gfx_TryRestoreContext(void) { return true; } + +void Gfx_RestoreState(void) { + InitDefaultResources(); + + // 4x4 dummy white texture (textures must be at least 1 4x4 tile) + struct Bitmap bmp; + BitmapCol pixels[4 * 4]; + Mem_Set(pixels, 0xFF, sizeof(pixels)); + Bitmap_Init(bmp, 4, 4, pixels); + white_square = Gfx_CreateTexture(&bmp, 0, false); +} + +void Gfx_FreeState(void) { + FreeDefaultResources(); + Gfx_DeleteTexture(&white_square); +} + +/*########################################################################################################################* +*---------------------------------------------------------Textures--------------------------------------------------------* +*#########################################################################################################################*/ +typedef struct CCTexture_ { + GXTexObj obj; + cc_uint32 pixels[]; +} CCTexture; + +// ClassiCube RGBA8 bitmaps +// - store pixels in simple linear order +// i.e. pixels are ordered as (x=0,y=0), (x=1,y=0), (x=2,y=0) ... (x=0,y=1) ... +// - store colour components in interleaved order +// i.e. pixels are stored in memory as ARGB ARGB ARGB ARGB ... +// GX RGBA8 textures +// - store pixels in 4x4 tiles +// - store all of the AR values of the tile's pixels, then store all of the GB values +static void ReorderPixels(CCTexture* tex, struct Bitmap* bmp, + int originX, int originY, int rowWidth) { + int stride = GX_GetTexObjWidth(&tex->obj) * 4; + // TODO not really right + // TODO originX ignored + originX &= ~0x03; + originY &= ~0x03; + + // http://hitmen.c02.at/files/yagcd/yagcd/chap15.html + // section 15.35 TPL (Texture Palette) + // "RGBA8 (4x4 tiles in two cache lines - first is AR and second is GB" + uint8_t *src = (uint8_t*)bmp->scan0; + uint8_t *dst = (uint8_t*)tex->pixels + stride * originY; + int srcWidth = bmp->width, srcHeight = bmp->height; + + for (int tileY = 0; tileY < srcHeight; tileY += 4) + for (int tileX = 0; tileX < srcWidth; tileX += 4) + { + for (int y = 0; y < 4; y++) { + for (int x = 0; x < 4; x++) { + uint32_t idx = (((tileY + y) * rowWidth) + tileX + x) << 2; + + *dst++ = src[idx + 0]; // A + *dst++ = src[idx + 1]; // R + } + } + + for (int y = 0; y < 4; y++) { + for (int x = 0; x < 4; x++) { + uint32_t idx = (((tileY + y) * rowWidth) + tileX + x) << 2; + + *dst++ = src[idx + 2]; // G + *dst++ = src[idx + 3]; // B + } + } + } +} + +static GfxResourceID Gfx_AllocTexture(struct Bitmap* bmp, int rowWidth, cc_uint8 flags, cc_bool mipmaps) { + int size = bmp->width * bmp->height * 4; + CCTexture* tex = (CCTexture*)memalign(32, 32 + size); + if (!tex) return NULL; + + GX_InitTexObj(&tex->obj, tex->pixels, bmp->width, bmp->height, + GX_TF_RGBA8, GX_REPEAT, GX_REPEAT, GX_FALSE); + GX_InitTexObjFilterMode(&tex->obj, GX_NEAR, GX_NEAR); + + ReorderPixels(tex, bmp, 0, 0, rowWidth); + DCFlushRange(tex->pixels, size); + return tex; +} + +void Gfx_UpdateTexture(GfxResourceID texId, int x, int y, struct Bitmap* part, int rowWidth, cc_bool mipmaps) { + CCTexture* tex = (CCTexture*)texId; + // TODO: wrong behaviour if x/y/part isn't multiple of 4 pixels + ReorderPixels(tex, part, x, y, rowWidth); + GX_InvalidateTexAll(); +} + +void Gfx_DeleteTexture(GfxResourceID* texId) { + GfxResourceID data = *texId; + if (data) Mem_Free(data); + *texId = NULL; +} + +void Gfx_EnableMipmaps(void) { } +void Gfx_DisableMipmaps(void) { } + +void Gfx_BindTexture(GfxResourceID texId) { + CCTexture* tex = (CCTexture*)texId; + if (!tex) tex = white_square; + + GX_LoadTexObj(&tex->obj, GX_TEXMAP0); +} + + +/*########################################################################################################################* +*-----------------------------------------------------State management----------------------------------------------------* +*#########################################################################################################################*/ +static GXColor gfx_clearColor = { 0, 0, 0, 255 }; + +void Gfx_SetFaceCulling(cc_bool enabled) { + // NOTE: seems like ClassiCube's triangle ordering is opposite of what GX considers front facing + GX_SetCullMode(enabled ? GX_CULL_FRONT : GX_CULL_NONE); +} + +static void SetAlphaBlend(cc_bool enabled) { + if (enabled) { + GX_SetBlendMode(GX_BM_BLEND, GX_BL_SRCALPHA, GX_BL_INVSRCALPHA, GX_LO_CLEAR); + } else { + GX_SetBlendMode(GX_BM_NONE, GX_BL_ONE, GX_BL_ZERO, GX_LO_CLEAR); + } +} + +void Gfx_SetAlphaArgBlend(cc_bool enabled) { +} + +void Gfx_ClearColor(PackedCol color) { + gfx_clearColor.r = PackedCol_R(color); + gfx_clearColor.g = PackedCol_G(color); + gfx_clearColor.b = PackedCol_B(color); + + GX_SetCopyClear(gfx_clearColor, 0x00ffffff); // TODO: use GX_MAX_Z24 +} + +static void SetColorWrite(cc_bool r, cc_bool g, cc_bool b, cc_bool a) { + // TODO +} + +static cc_bool depth_write = true, depth_test = true; +static void UpdateDepthState(void) { + GX_SetZMode(depth_test, GX_LEQUAL, depth_write); +} + +void Gfx_SetDepthWrite(cc_bool enabled) { + depth_write = enabled; + UpdateDepthState(); +} + +void Gfx_SetDepthTest(cc_bool enabled) { + depth_test = enabled; + UpdateDepthState(); +} + + +/*########################################################################################################################* +*-----------------------------------------------------------Misc----------------------------------------------------------* +*#########################################################################################################################*/ +static BitmapCol* GCWii_GetRow(struct Bitmap* bmp, int y, void* ctx) { + u8* buffer = (u8*)ctx; + u8 a, r, g, b; + int blockYStride = 4 * (bmp->width * 4); // tile row stride = 4 * row stride + int blockXStride = (4 * 4) * 4; // 16 pixels per tile + + // Do the inverse of converting from 4x4 tiled to linear + for (u32 x = 0; x < bmp->width; x++){ + int tileY = y >> 2, tileX = x >> 2; + int locY = y & 0x3, locX = x & 0x3; + int idx = (tileY * blockYStride) + (tileX * blockXStride) + ((locY << 2) + locX) * 2; + + // All 16 pixels are stored with AR first, then GB + //a = buffer[idx ]; + r = buffer[idx + 1]; + g = buffer[idx + 32]; + b = buffer[idx + 33]; + + bmp->scan0[x] = BitmapColor_RGB(r, g, b); + } + return bmp->scan0; +} + +cc_result Gfx_TakeScreenshot(struct Stream* output) { + BitmapCol tmp[1024]; + GXRModeObj* vmode = VIDEO_GetPreferredMode(NULL); + int width = vmode->fbWidth; + int height = vmode->efbHeight; + + u8* buffer = memalign(32, width * height * 4); + if (!buffer) return ERR_OUT_OF_MEMORY; + + GX_SetTexCopySrc(0, 0, width, height); + GX_SetTexCopyDst(width, height, GX_TF_RGBA8, GX_FALSE); + GX_CopyTex(buffer, GX_FALSE); + GX_PixModeSync(); + GX_Flush(); + DCFlushRange(buffer, width * height * 4); + + struct Bitmap bmp; + bmp.scan0 = tmp; + bmp.width = width; + bmp.height = height; + + cc_result res = Png_Encode(&bmp, output, GCWii_GetRow, false, buffer); + free(buffer); + return res; +} + +void Gfx_GetApiInfo(cc_string* info) { + String_AppendConst(info, "-- Using GC/Wii --\n"); + PrintMaxTextureInfo(info); +} + +void Gfx_SetFpsLimit(cc_bool vsync, float minFrameMs) { + gfx_minFrameMs = minFrameMs; + gfx_vsync = vsync; +} + +void Gfx_BeginFrame(void) { +} + +void Gfx_ClearBuffers(GfxBuffers buffers) { + // TODO clear only some buffers +} + +void Gfx_EndFrame(void) { + curFB ^= 1; // swap "front" and "back" buffers + GX_CopyDisp(xfbs[curFB], GX_TRUE); + GX_DrawDone(); + + VIDEO_SetNextFramebuffer(xfbs[curFB]); + VIDEO_Flush(); + + if (gfx_vsync) VIDEO_WaitVSync(); + if (gfx_minFrameMs) LimitFPS(); +} + +void Gfx_OnWindowResize(void) { } + +void Gfx_SetViewport(int x, int y, int w, int h) { + GX_SetViewport(x, y, w, h, 0, 1); + GX_SetScissor(x, y, w, h); +} + +cc_bool Gfx_WarnIfNecessary(void) { return false; } + + +/*########################################################################################################################* +*-------------------------------------------------------Index Buffers-----------------------------------------------------* +*#########################################################################################################################*/ +//static cc_uint16 __attribute__((aligned(16))) gfx_indices[GFX_MAX_INDICES]; + +GfxResourceID Gfx_CreateIb2(int count, Gfx_FillIBFunc fillFunc, void* obj) { + //fillFunc(gfx_indices, count, obj); + // not used since render using GX_QUADS anyways + return (void*)1; +} + +void Gfx_BindIb(GfxResourceID ib) { } +void Gfx_DeleteIb(GfxResourceID* ib) { } + + +/*########################################################################################################################* +*------------------------------------------------------Vertex Buffers-----------------------------------------------------* +*#########################################################################################################################*/ +static cc_uint8* gfx_vertices; +static int vb_size; + +static GfxResourceID Gfx_AllocStaticVb(VertexFormat fmt, int count) { + return memalign(16, count * strideSizes[fmt]); +} + +void Gfx_BindVb(GfxResourceID vb) { gfx_vertices = vb; } + +void Gfx_DeleteVb(GfxResourceID* vb) { + GfxResourceID data = *vb; + if (data) Mem_Free(data); + *vb = 0; +} + +void* Gfx_LockVb(GfxResourceID vb, VertexFormat fmt, int count) { + vb_size = count * strideSizes[fmt]; + return vb; +} + +void Gfx_UnlockVb(GfxResourceID vb) { + gfx_vertices = vb; + DCFlushRange(vb, vb_size); +} + + +static GfxResourceID Gfx_AllocDynamicVb(VertexFormat fmt, int maxVertices) { + return memalign(16, maxVertices * strideSizes[fmt]); +} + +void Gfx_BindDynamicVb(GfxResourceID vb) { Gfx_BindVb(vb); } + +void* Gfx_LockDynamicVb(GfxResourceID vb, VertexFormat fmt, int count) { + vb_size = count * strideSizes[fmt]; + return vb; +} + +void Gfx_UnlockDynamicVb(GfxResourceID vb) { + gfx_vertices = vb; + DCFlushRange(vb, vb_size); +} + +void Gfx_DeleteDynamicVb(GfxResourceID* vb) { Gfx_DeleteVb(vb); } + + +/*########################################################################################################################* +*-----------------------------------------------------State management----------------------------------------------------* +*#########################################################################################################################*/ +static PackedCol gfx_fogColor; +static float gfx_fogEnd = -1.0f, gfx_fogDensity = -1.0f; +static int gfx_fogMode = -1; + +void Gfx_SetFog(cc_bool enabled) { + gfx_fogEnabled = enabled; +} + +void Gfx_SetFogCol(PackedCol color) { + if (color == gfx_fogColor) return; + gfx_fogColor = color; +} + +void Gfx_SetFogDensity(float value) { +} + +void Gfx_SetFogEnd(float value) { + if (value == gfx_fogEnd) return; + gfx_fogEnd = value; +} + +void Gfx_SetFogMode(FogFunc func) { +} + +static void SetAlphaTest(cc_bool enabled) { + if (enabled) { + GX_SetAlphaCompare(GX_GREATER, 127, GX_AOP_AND, GX_ALWAYS, 0); + } else { + GX_SetAlphaCompare(GX_ALWAYS, 0, GX_AOP_AND, GX_ALWAYS, 0); + } + + // See explanation from libGX headers + // Normally, Z buffering should happen before texturing, as this enables better performance by not texturing pixels that are not visible; + // however, when alpha compare is used, Z buffering must be done after texturing + // Parameter[0] = Enables Z-buffering before texturing when set to GX_TRUE; otherwise, Z-buffering takes place after texturing. + GX_SetZCompLoc(!enabled); +} + +void Gfx_DepthOnlyRendering(cc_bool depthOnly) { + GX_SetColorUpdate(!depthOnly); + GX_SetAlphaUpdate(!depthOnly); +} + + +/*########################################################################################################################* +*---------------------------------------------------------Matrices--------------------------------------------------------* +*#########################################################################################################################*/ +void Gfx_CalcOrthoMatrix(struct Matrix* matrix, float width, float height, float zNear, float zFar) { + // Transposed, source guOrtho https://github.com/devkitPro/libogc/blob/master/libogc/gu.c + // The simplified calculation below uses: L = 0, R = width, T = 0, B = height + *matrix = Matrix_Identity; + + matrix->row1.x = 2.0f / width; + matrix->row2.y = -2.0f / height; + matrix->row3.z = -1.0f / (zFar - zNear); + + matrix->row4.x = -1.0f; + matrix->row4.y = 1.0f; + matrix->row4.z = -zFar / (zFar - zNear); +} + +static float Cotangent(float x) { return Math_CosF(x) / Math_SinF(x); } +void Gfx_CalcPerspectiveMatrix(struct Matrix* matrix, float fov, float aspect, float zFar) { + float zNear = 0.1f; + float c = Cotangent(0.5f * fov); + + // Transposed, source guPersepctive https://github.com/devkitPro/libogc/blob/master/libogc/gu.c + *matrix = Matrix_Identity; + matrix->row1.x = c / aspect; + matrix->row2.y = c; + matrix->row3.z = -(zNear) / (zFar - zNear); + matrix->row3.w = -1.0f; + matrix->row4.z = -(zFar * zNear) / (zFar - zNear); + matrix->row4.w = 0.0f; +} + +void Gfx_LoadMatrix(MatrixType type, const struct Matrix* matrix) { + const float* m = (const float*)matrix; + float tmp[16]; + + // Transpose matrix + for (int i = 0; i < 4; i++) + { + tmp[i * 4 + 0] = m[0 + i]; + tmp[i * 4 + 1] = m[4 + i]; + tmp[i * 4 + 2] = m[8 + i]; + tmp[i * 4 + 3] = m[12 + i]; + } + + if (type == MATRIX_PROJECTION) { + GX_LoadProjectionMtx(tmp, + tmp[3*4+3] == 0.0f ? GX_PERSPECTIVE : GX_ORTHOGRAPHIC); + } else { + GX_LoadPosMtxImm(tmp, GX_PNMTX0); + } +} + +void Gfx_LoadIdentityMatrix(MatrixType type) { + Gfx_LoadMatrix(type, &Matrix_Identity); +} +static float texOffsetX, texOffsetY; +static void UpdateTexCoordGen(void) { + if (texOffsetX || texOffsetY) { + Mtx mat = { 0 }; + // https://registry.khronos.org/OpenGL-Refpages/gl2.1/xhtml/glTranslate.xml + mat[0][0] = 1; mat[0][3] = texOffsetX; + mat[1][1] = 1; mat[1][3] = texOffsetY; + + GX_LoadTexMtxImm(mat, GX_TEXMTX0, GX_MTX2x4); + GX_SetTexCoordGen(GX_TEXCOORD0, GX_TG_MTX2x4, GX_TG_TEX0, GX_TEXMTX0); + } else { + GX_SetTexCoordGen(GX_TEXCOORD0, GX_TG_MTX2x4, GX_TG_TEX0, GX_IDENTITY); + } +} + +void Gfx_EnableTextureOffset(float x, float y) { + texOffsetX = x; texOffsetY = y; + UpdateTexCoordGen(); +} + +void Gfx_DisableTextureOffset(void) { + texOffsetX = 0; texOffsetY = 0; + UpdateTexCoordGen(); +} + + + +/*########################################################################################################################* +*---------------------------------------------------------Drawing---------------------------------------------------------* +*#########################################################################################################################*/ +void Gfx_SetVertexFormat(VertexFormat fmt) { + if (fmt == gfx_format) return; + gfx_format = fmt; + gfx_stride = strideSizes[fmt]; + + GX_ClearVtxDesc(); + if (fmt == VERTEX_FORMAT_TEXTURED) { + GX_SetVtxDesc(GX_VA_POS, GX_DIRECT); + GX_SetVtxDesc(GX_VA_CLR0, GX_DIRECT); + GX_SetVtxDesc(GX_VA_TEX0, GX_DIRECT); + + GX_SetVtxAttrFmt(GX_VTXFMT0, GX_VA_POS, GX_POS_XYZ, GX_F32, 0); + GX_SetVtxAttrFmt(GX_VTXFMT0, GX_VA_CLR0, GX_CLR_RGBA, GX_RGBA8, 0); + GX_SetVtxAttrFmt(GX_VTXFMT0, GX_VA_TEX0, GX_TEX_ST, GX_F32, 0); + + GX_SetNumTexGens(1); + // TODO avoid calling here. only call in Gfx_Init and Enable/Disable Texture Offset ??? + UpdateTexCoordGen(); + GX_SetTevOrder(GX_TEVSTAGE0, GX_TEXCOORD0, GX_TEXMAP0, GX_COLOR0A0); + GX_SetTevOp(GX_TEVSTAGE0, GX_MODULATE); + } else { + GX_SetVtxDesc(GX_VA_POS, GX_DIRECT); + GX_SetVtxDesc(GX_VA_CLR0, GX_DIRECT); + + GX_SetVtxAttrFmt(GX_VTXFMT0, GX_VA_POS, GX_POS_XYZ, GX_F32, 0); + GX_SetVtxAttrFmt(GX_VTXFMT0, GX_VA_CLR0, GX_CLR_RGBA, GX_RGBA8, 0); + + GX_SetNumTexGens(0); + GX_SetTevOrder(GX_TEVSTAGE0, GX_TEXCOORDNULL, GX_TEXMAP_NULL, GX_COLOR0A0); + GX_SetTevOp(GX_TEVSTAGE0, GX_PASSCLR); + } +} + +void Gfx_DrawVb_Lines(int verticesCount) { +} + + +static void Draw_ColouredTriangles(int verticesCount, int startVertex) { + GX_Begin(GX_QUADS, GX_VTXFMT0, verticesCount); + // TODO: Ditch indexed rendering and use GX_QUADS instead ?? + for (int i = 0; i < verticesCount; i++) + { + struct VertexColoured* v = (struct VertexColoured*)gfx_vertices + startVertex + i; + + GX_Position3f32(v->x, v->y, v->z); + GX_Color1u32(v->Col); + } + GX_End(); +} + +static void Draw_TexturedTriangles(int verticesCount, int startVertex) { + GX_Begin(GX_QUADS, GX_VTXFMT0, verticesCount); + for (int i = 0; i < verticesCount; i++) + { + struct VertexTextured* v = (struct VertexTextured*)gfx_vertices + startVertex + i; + + GX_Position3f32(v->x, v->y, v->z); + GX_Color1u32(v->Col); + GX_TexCoord2f32(v->U, v->V); + } + GX_End(); +} + +void Gfx_DrawVb_IndexedTris_Range(int verticesCount, int startVertex) { + if (gfx_format == VERTEX_FORMAT_TEXTURED) { + Draw_TexturedTriangles(verticesCount, startVertex); + } else { + Draw_ColouredTriangles(verticesCount, startVertex); + } +} + +void Gfx_DrawVb_IndexedTris(int verticesCount) { + if (gfx_format == VERTEX_FORMAT_TEXTURED) { + Draw_TexturedTriangles(verticesCount, 0); + } else { + Draw_ColouredTriangles(verticesCount, 0); + } +} + +void Gfx_DrawIndexedTris_T2fC4b(int verticesCount, int startVertex) { + Draw_TexturedTriangles(verticesCount, startVertex); +} +#endif |