From 87f4c83ff2b06165ce7fb3f36bae6adfe60b2179 Mon Sep 17 00:00:00 2001 From: Anders Jenbo Date: Sat, 28 Jun 2025 06:26:53 +0200 Subject: [PATCH] Implement lighting --- .../src/d3drm/backends/citro3d/renderer.cpp | 111 +++++++++++++----- .../src/d3drm/backends/citro3d/vshader.v.pica | 108 ++++++++++++++--- miniwin/src/d3drm/d3drmdevice.cpp | 4 + miniwin/src/internal/d3drmrenderer_citro3d.h | 1 + 4 files changed, 181 insertions(+), 43 deletions(-) diff --git a/miniwin/src/d3drm/backends/citro3d/renderer.cpp b/miniwin/src/d3drm/backends/citro3d/renderer.cpp index 2837d089..5ebf6b75 100644 --- a/miniwin/src/d3drm/backends/citro3d/renderer.cpp +++ b/miniwin/src/d3drm/backends/citro3d/renderer.cpp @@ -9,30 +9,35 @@ bool g_rendering = false; -#define DISPLAY_TRANSFER_FLAGS \ - (GX_TRANSFER_FLIP_VERT(0) | GX_TRANSFER_OUT_TILED(0) | GX_TRANSFER_RAW_COPY(0) | \ - GX_TRANSFER_IN_FORMAT(GX_TRANSFER_FMT_RGBA8) | GX_TRANSFER_OUT_FORMAT(GX_TRANSFER_FMT_RGB8) | \ - GX_TRANSFER_SCALING(GX_TRANSFER_SCALE_NO)) - static DVLB_s* vshader_dvlb; static shaderProgram_s program; static int uLoc_projection; static int uLoc_modelView; static int uLoc_meshColor; +static int uLoc_lightVec; +static int uLoc_lightClr; +static int uLoc_shininess; Citro3DRenderer::Citro3DRenderer(DWORD width, DWORD height) { - m_width = 400; + m_width = 320; m_height = 240; m_virtualWidth = width; m_virtualHeight = height; gfxInitDefault(); - consoleInit(GFX_BOTTOM, nullptr); + consoleInit(GFX_TOP, nullptr); C3D_Init(C3D_DEFAULT_CMDBUF_SIZE); m_renderTarget = C3D_RenderTargetCreate(m_height, m_width, GPU_RB_RGBA8, GPU_RB_DEPTH24_STENCIL8); - C3D_RenderTargetSetOutput(m_renderTarget, GFX_TOP, GFX_LEFT, DISPLAY_TRANSFER_FLAGS); + C3D_RenderTargetSetOutput( + m_renderTarget, + GFX_BOTTOM, + GFX_LEFT, + GX_TRANSFER_FLIP_VERT(0) | GX_TRANSFER_OUT_TILED(0) | GX_TRANSFER_RAW_COPY(0) | + GX_TRANSFER_IN_FORMAT(GX_TRANSFER_FMT_RGBA8) | GX_TRANSFER_OUT_FORMAT(GX_TRANSFER_FMT_RGB8) | + GX_TRANSFER_SCALING(GX_TRANSFER_SCALE_NO) + ); vshader_dvlb = DVLB_ParseFile((u32*) vshader_shbin, vshader_shbin_size); shaderProgramInit(&program); @@ -44,6 +49,9 @@ Citro3DRenderer::Citro3DRenderer(DWORD width, DWORD height) uLoc_projection = shaderInstanceGetUniformLocation(program.vertexShader, "projection"); uLoc_modelView = shaderInstanceGetUniformLocation(program.vertexShader, "modelView"); uLoc_meshColor = shaderInstanceGetUniformLocation(program.vertexShader, "meshColor"); + uLoc_lightVec = shaderInstanceGetUniformLocation(program.vertexShader, "lightVec"); + uLoc_lightClr = shaderInstanceGetUniformLocation(program.vertexShader, "lightClr"); + uLoc_shininess = shaderInstanceGetUniformLocation(program.vertexShader, "shininess"); C3D_AttrInfo* attrInfo = C3D_GetAttrInfo(); AttrInfo_Init(attrInfo); @@ -60,9 +68,9 @@ Citro3DRenderer::~Citro3DRenderer() gfxExit(); } -void Citro3DRenderer::PushLights(const SceneLight* lightsArray, size_t count) +void Citro3DRenderer::PushLights(const SceneLight* lights, size_t count) { - MINIWIN_NOT_IMPLEMENTED(); + m_lights.assign(lights, lights + count); } void Citro3DRenderer::SetProjection(const D3DRMMATRIX4D& projection, D3DVALUE front, D3DVALUE back) @@ -362,10 +370,59 @@ void Citro3DRenderer::StartFrame() C3D_FrameDrawOn(m_renderTarget); } +void ConvertPerspective(const D3DRMMATRIX4D in, C3D_Mtx* out) +{ + float f_h = in[0][0]; + float f_v = in[1][1]; + + float aspect = f_v / f_h; + float fovY = 2.0f * atanf(1.0f / f_v); + + float nearZ = -in[3][2] / in[2][2]; + float farZ = nearZ * in[2][2] / (in[2][2] - 1.0f); + + Mtx_PerspTilt(out, fovY, aspect, nearZ, farZ, true); +} + HRESULT Citro3DRenderer::BeginFrame() { StartFrame(); C3D_DepthTest(true, GPU_GREATER, GPU_WRITE_ALL); + + C3D_Mtx projection; + ConvertPerspective(m_projection, &projection); + C3D_FVUnifMtx4x4(GPU_VERTEX_SHADER, uLoc_projection, &projection); + + for (const auto& light : m_lights) { + FColor lightColor = light.color; + if (light.positional == 0.0f && light.directional == 0.0f) { + // Ambient light + C3D_FVUnifSet(GPU_VERTEX_SHADER, uLoc_lightClr + 2, lightColor.r, lightColor.g, lightColor.b, 1.0f); + } + else if (light.directional == 1.0f) { + C3D_FVUnifSet( + GPU_VERTEX_SHADER, + uLoc_lightVec + 1, + -light.direction.x, + -light.direction.y, + -light.direction.z, + 0.0f + ); + C3D_FVUnifSet(GPU_VERTEX_SHADER, uLoc_lightClr + 1, lightColor.r, lightColor.g, lightColor.b, 0.0f); + } + else if (light.positional == 1.0f) { + C3D_FVUnifSet( + GPU_VERTEX_SHADER, + uLoc_lightVec + 0, + light.position.x, + light.position.y, + light.position.z, + 0.0f + ); + C3D_FVUnifSet(GPU_VERTEX_SHADER, uLoc_lightClr + 0, lightColor.r, lightColor.g, lightColor.b, 0.0f); + } + } + return S_OK; } @@ -384,20 +441,6 @@ void ConvertMatrix(const D3DRMMATRIX4D in, C3D_Mtx* out) } } -void ConvertPerspective(const D3DRMMATRIX4D in, C3D_Mtx* out) -{ - float f_h = in[0][0]; - float f_v = in[1][1]; - - float aspect = f_v / f_h; - float fovY = 2.0f * atanf(1.0f / f_v); - - float nearZ = -in[3][2] / in[2][2]; - float farZ = nearZ * in[2][2] / (in[2][2] - 1.0f); - - Mtx_PerspTilt(out, fovY, aspect, nearZ, farZ, true); -} - void Citro3DRenderer::SubmitDraw( DWORD meshId, const D3DRMMATRIX4D& modelViewMatrix, @@ -407,12 +450,8 @@ void Citro3DRenderer::SubmitDraw( const Appearance& appearance ) { - C3D_Mtx projection, modelView; - - ConvertPerspective(m_projection, &projection); + C3D_Mtx modelView; ConvertMatrix(modelViewMatrix, &modelView); - - C3D_FVUnifMtx4x4(GPU_VERTEX_SHADER, uLoc_projection, &projection); C3D_FVUnifMtx4x4(GPU_VERTEX_SHADER, uLoc_modelView, &modelView); auto& mesh = m_meshs[meshId]; @@ -430,6 +469,8 @@ void Citro3DRenderer::SubmitDraw( appearance.color.a / 255.0f ); + C3D_FVUnifSet(GPU_VERTEX_SHADER, uLoc_shininess, appearance.shininess / 255.0f, 0.0f, 0.0f, 0.0f); + if (appearance.textureId != NO_TEXTURE_ID) { C3D_TexBind(0, &m_textures[appearance.textureId].c3dTex); C3D_TexEnv* env = C3D_GetTexEnv(0); @@ -470,6 +511,8 @@ void Citro3DRenderer::Clear(float r, float g, float b) void Citro3DRenderer::Flip() { C3D_FrameEnd(0); + gfxFlushBuffers(); + gspWaitForVBlank(); } void Citro3DRenderer::Draw2DImage(Uint32 textureId, const SDL_Rect& srcRect, const SDL_Rect& dstRect) @@ -489,6 +532,16 @@ void Citro3DRenderer::Draw2DImage(Uint32 textureId, const SDL_Rect& srcRect, con Mtx_Identity(&modelView); C3D_FVUnifMtx4x4(GPU_VERTEX_SHADER, uLoc_modelView, &modelView); + // Set light directions + C3D_FVUnifSet(GPU_VERTEX_SHADER, uLoc_lightVec + 0, 0.0f, 0.0f, 0.0f, 0.0f); + C3D_FVUnifSet(GPU_VERTEX_SHADER, uLoc_lightVec + 1, 0.0f, 0.0f, 0.0f, 0.0f); + + // Set light colors + C3D_FVUnifSet(GPU_VERTEX_SHADER, uLoc_lightClr + 0, 0.0f, 0.0f, 0.0f, 0.0f); + C3D_FVUnifSet(GPU_VERTEX_SHADER, uLoc_lightClr + 1, 0.0f, 0.0f, 0.0f, 0.0f); + C3D_FVUnifSet(GPU_VERTEX_SHADER, uLoc_lightClr + 2, 1.0f, 1.0f, 1.0f, 1.0f); // Ambient + + C3D_FVUnifSet(GPU_VERTEX_SHADER, uLoc_shininess, 0.0f, 0.0f, 0.0f, 0.0f); C3D_FVUnifSet(GPU_VERTEX_SHADER, uLoc_meshColor, 1.0f, 1.0f, 1.0f, 1.0f); C3DTextureCacheEntry& texture = m_textures[textureId]; diff --git a/miniwin/src/d3drm/backends/citro3d/vshader.v.pica b/miniwin/src/d3drm/backends/citro3d/vshader.v.pica index fa1c9870..cd7f91c4 100644 --- a/miniwin/src/d3drm/backends/citro3d/vshader.v.pica +++ b/miniwin/src/d3drm/backends/citro3d/vshader.v.pica @@ -1,31 +1,29 @@ -; From https://github.com/devkitPro/citro3d/blob/master/test/3ds/source/vshader.v.pica -; zlib license -; ---------- - -; Example PICA200 vertex shader - ; Uniforms -.fvec projection[4], modelView[4], normView[2], meshColor +.fvec projection[4], modelView[4], meshColor +.fvec lightVec[2], lightClr[3], shininess ; Constants .constf myconst(0.0, 1.0, -1.0, -0.5) -.alias zeros myconst.xxxx ; Vector full of zeros -.alias ones myconst.yyyy ; Vector full of ones ; Outputs .out outpos position .out outtc0 texcoord0 .out outclr color -; Inputs (defined as aliases for convenience) +; Inputs .alias inpos v0 .alias innrm v1 .alias intex v2 .proc main + ; Prepare constants in usable temp regs + mov r15.x, myconst.x ; 0.0 + mov r15.y, myconst.y ; 1.0 + mov r15.z, myconst.z ; -1.0 + ; Force the w component of inpos to be 1.0 mov r0.xyz, inpos - mov r0.w, ones + mov r0.w, r15.y ; r1 = modelView * inpos dp4 r1.x, modelView[0], r0 @@ -42,9 +40,91 @@ ; outtex = intex mov outtc0, intex mov outtc0.zw, myconst.xy - ; outclr = ones - mov outclr, meshColor - ; We're finished + ; Transform normal + mov r2.xyz, innrm + mov r2.w, r15.x + dp4 r3.x, modelView[0], r2 + dp4 r3.y, modelView[1], r2 + dp4 r3.z, modelView[2], r2 + mov r3.w, r15.x + dp3 r4.x, r3, r3 + rsq r4.x, r4.x + mul r3, r4.xxxx, r3 ; r3 = normalized normal + + ; Normalize lightVec[0] + mov r5, lightVec[0] + dp3 r6.x, r5, r5 + rsq r6.x, r6.x + mul r5, r6.xxxx, r5 + + ; dot(normal, lightVec[0]) + dp3 r6.x, r3, r5 + max r6.x, r6.x, r15.xxxx + + ; Normalize lightVec[1] + mov r7, lightVec[1] + dp3 r8.x, r7, r7 + rsq r8.x, r8.x + mul r7, r8.xxxx, r7 + + ; dot(normal, lightVec[1]) + dp3 r6.y, r3, r7 + max r6.y, r6.y, r15.xxxx + + ; Load lightClr + mov r8, lightClr[2] ; ambient + mov r9, lightClr[0] ; point + mov r10, lightClr[1] ; directional + + ; diffuse = ambient + (lightClr[0] * dot0) + (lightClr[1] * dot1) + mul r11, r9, r6.xxxx + add r8, r8, r11 + mul r11, r10, r6.yyyy + add r8, r8, r11 ; r8 = diffuse + + ; Check if shininess > 0 + mov r12, shininess + slt r13.x, r15.x, r12.x + + ; viewVec = normalize(-position.xyz) + mov r14.xyz, r1.xyz + mul r14.xyz, r14.xyz, r15.zzz + dp3 r4.x, r14, r14 + rsq r4.x, r4.x + mul r14, r4.xxxx, r14 + + ; H = normalize(view + lightVec[1]) + add r11, r14, r7 + dp3 r4.x, r11, r11 + rsq r4.x, r4.x + mul r11, r4.xxxx, r11 + + ; dot(normal, H) + dp3 r4.x, r3, r11 + max r4.x, r4.x, r15.x + + ; Approximate pow(dotNH, 10) by repeated multiplication + mul r5.x, r4.x, r4.x ; dotNH^2 + mul r5.x, r5.x, r5.x ; dotNH^4 + mul r5.x, r5.x, r5.x ; dotNH^8 + mul r4.x, r5.x, r4.x ; dotNH^9 + mul r4.x, r4.x, r4.x ; dotNH^10 + + ; Multiply by shininess > 0 flag + mul r4.x, r4.x, r13.x + + ; specular = lightClr[1] * spec + mul r5, r10, r4.xxxx + + ; final = diffuse * meshColor + specular * lightClr[1] + mov r9, meshColor + mul r6, r8, r9 ; diffuse * meshColor + add r7.xyz, r6.xyz, r5.xyz ; add specular (already multiplied by lightClr) + min r7.xyz, r7.xyz, r15.yyyy + + mov outclr.xyz, r7.xyz + mov outclr.w, meshColor.w + end .end diff --git a/miniwin/src/d3drm/d3drmdevice.cpp b/miniwin/src/d3drm/d3drmdevice.cpp index f6b2f9f7..b1ae249b 100644 --- a/miniwin/src/d3drm/d3drmdevice.cpp +++ b/miniwin/src/d3drm/d3drmdevice.cpp @@ -155,6 +155,10 @@ void Direct3DRMDevice2Impl::Resize() { int width, height; SDL_GetWindowSizeInPixels(DDWindow, &width, &height); +#ifdef USE_CITRO3D + width = 320; // We are on the lower screen + height = 240; +#endif m_viewportTransform = CalculateViewportTransform(m_virtualWidth, m_virtualHeight, width, height); m_renderer->Resize(width, height, m_viewportTransform); for (int i = 0; i < m_viewports->GetSize(); i++) { diff --git a/miniwin/src/internal/d3drmrenderer_citro3d.h b/miniwin/src/internal/d3drmrenderer_citro3d.h index 09c0001d..e08035c4 100644 --- a/miniwin/src/internal/d3drmrenderer_citro3d.h +++ b/miniwin/src/internal/d3drmrenderer_citro3d.h @@ -64,6 +64,7 @@ class Citro3DRenderer : public Direct3DRMRenderer { std::vector m_textures; std::vector m_meshs; ViewportTransform m_viewportTransform; + std::vector m_lights; }; inline static void Citro3DRenderer_EnumDevice(LPD3DENUMDEVICESCALLBACK cb, void* ctx)