diff --git a/include/renderer_gl/renderer_gl.hpp b/include/renderer_gl/renderer_gl.hpp index 8ab8b953..7ed9c1b2 100644 --- a/include/renderer_gl/renderer_gl.hpp +++ b/include/renderer_gl/renderer_gl.hpp @@ -2,6 +2,7 @@ #include #include +#include "PICA/float_types.hpp" #include "helpers.hpp" #include "logger.hpp" #include "opengl.hpp" @@ -14,7 +15,11 @@ class GPU; struct Vertex { OpenGL::vec4 position; OpenGL::vec4 colour; - OpenGL::vec2 UVs; + OpenGL::vec2 texcoord0; + OpenGL::vec2 texcoord1; + Floats::f24 texcoord0_w; + u32 padding; // pad so that texcoord2 is 64-bit aligned + OpenGL::vec2 texcoord2; }; class Renderer { diff --git a/src/core/PICA/gpu.cpp b/src/core/PICA/gpu.cpp index 554471a9..e771fd86 100644 --- a/src/core/PICA/gpu.cpp +++ b/src/core/PICA/gpu.cpp @@ -247,7 +247,10 @@ void GPU::drawArrays() { std::memcpy(&vertices[i].position, &out.s.positions, sizeof(vec4f)); std::memcpy(&vertices[i].colour, &out.s.colour, sizeof(vec4f)); - std::memcpy(&vertices[i].UVs, &out.s.texcoord0, 2 * sizeof(f24)); + std::memcpy(&vertices[i].texcoord0, &out.s.texcoord0, 2 * sizeof(f24)); + std::memcpy(&vertices[i].texcoord1, &out.s.texcoord1, 2 * sizeof(f24)); + std::memcpy(&vertices[i].texcoord0_w, &out.s.texcoord0_w, sizeof(f24)); + std::memcpy(&vertices[i].texcoord2, &out.s.texcoord2, 2 * sizeof(f24)); //printf("(x, y, z, w) = (%f, %f, %f, %f)\n", (double)vertices[i].position.x(), (double)vertices[i].position.y(), (double)vertices[i].position.z(), (double)vertices[i].position.w()); //printf("(r, g, b, a) = (%f, %f, %f, %f)\n", (double)vertices[i].colour.r(), (double)vertices[i].colour.g(), (double)vertices[i].colour.b(), (double)vertices[i].colour.a()); @@ -275,11 +278,11 @@ Vertex GPU::getImmediateModeVertex() { shaderUnit.vs.run(); std::memcpy(&v.position, &shaderUnit.vs.outputs[0], sizeof(vec4f)); std::memcpy(&v.colour, &shaderUnit.vs.outputs[1], sizeof(vec4f)); - std::memcpy(&v.UVs, &shaderUnit.vs.outputs[2], 2 * sizeof(f24)); + std::memcpy(&v.texcoord0, &shaderUnit.vs.outputs[2], 2 * sizeof(f24)); printf("(x, y, z, w) = (%f, %f, %f, %f)\n", (double)v.position.x(), (double)v.position.y(), (double)v.position.z(), (double)v.position.w()); printf("(r, g, b, a) = (%f, %f, %f, %f)\n", (double)v.colour.r(), (double)v.colour.g(), (double)v.colour.b(), (double)v.colour.a()); - printf("(u, v ) = (%f, %f)\n", v.UVs.u(), v.UVs.v()); + printf("(u, v ) = (%f, %f)\n", v.texcoord0.u(), v.texcoord0.v()); return v; } diff --git a/src/core/renderer_gl/renderer_gl.cpp b/src/core/renderer_gl/renderer_gl.cpp index e3c8e12f..584cc610 100644 --- a/src/core/renderer_gl/renderer_gl.cpp +++ b/src/core/renderer_gl/renderer_gl.cpp @@ -13,17 +13,24 @@ const char* vertexShader = R"( layout (location = 0) in vec4 coords; layout (location = 1) in vec4 vertexColour; - layout (location = 2) in vec2 inUVs_texture0; + layout (location = 2) in vec2 in_texcoord0; + layout (location = 3) in vec2 in_texcoord1; + layout (location = 4) in float in_texcoord0_w; + layout (location = 5) in vec2 in_texcoord2; out vec4 colour; - out vec2 tex0_UVs; + out vec3 texcoord0; + out vec2 texcoord1; + out vec2 texcoord2; void main() { gl_Position = coords; colour = vertexColour; // Flip y axis of UVs because OpenGL uses an inverted y for texture sampling compared to the PICA - tex0_UVs = vec2(inUVs_texture0.x, 1.0 - inUVs_texture0.y); + texcoord0 = vec3(in_texcoord0.x, 1.0 - in_texcoord0.y, in_texcoord0_w); + texcoord1 = vec2(in_texcoord1.x, 1.0 - in_texcoord1.y); + texcoord2 = vec2(in_texcoord2.x, 1.0 - in_texcoord2.y); } )"; @@ -31,7 +38,9 @@ const char* fragmentShader = R"( #version 410 core in vec4 colour; - in vec2 tex0_UVs; + in vec3 texcoord0; + in vec2 texcoord1; + in vec2 texcoord2; out vec4 fragColour; @@ -53,6 +62,8 @@ const char* fragmentShader = R"( uniform bool u_depthmapEnable; uniform sampler2D u_tex0; + uniform sampler2D u_tex1; + uniform sampler2D u_tex2; // TODO: figure out the color that is returned to the TEVs when reading from a disabled texture slot. vec4 tev_texture_sources[4] = vec4[](vec4(0.0), vec4(0.0), vec4(0.0), vec4(0.0)); @@ -76,6 +87,8 @@ const char* fragmentShader = R"( switch (rgb_source) { case 0u: source.rgb = colour.rgb; break; // Primary color, TODO: confirm that this is correct case 3u: source.rgb = tev_texture_sources[0].rgb; break; // Texture 0 + case 4u: source.rgb = tev_texture_sources[1].rgb; break; // Texture 1 + case 5u: source.rgb = tev_texture_sources[2].rgb; break; // Texture 2 case 13u: source.rgb = tev_previous_buffer[0].rgb; break; // Previous buffer case 14u: source.rgb = u_textureEnvColor[tev_id].rgb; break; // Constant (GPUREG_TEXENVi_COLOR) case 15u: source.rgb = tev_previous.rgb; break; // Previous (output from TEV #n-1) @@ -85,6 +98,8 @@ const char* fragmentShader = R"( switch (alpha_source) { case 0u: source.a = colour.a; break; // Primary color, TODO: confirm that this is correct case 3u: source.a = tev_texture_sources[0].a; break; // Texture 0 + case 4u: source.a = tev_texture_sources[1].a; break; // Texture 1 + case 5u: source.a = tev_texture_sources[2].a; break; // Texture 2 case 13u: source.a = tev_previous_buffer[0].a; break; // Previous buffer case 14u: source.a = u_textureEnvColor[tev_id].a; break; // Constant (GPUREG_TEXENVi_COLOR) case 15u: source.a = tev_previous.a; break; // Previous (output from TEV #n-1) @@ -172,7 +187,11 @@ const char* fragmentShader = R"( } void main() { - if ((u_textureConfig & 1u) != 0u) tev_texture_sources[0] = texture(u_tex0, tex0_UVs); + vec2 tex2_uv = (u_textureConfig & (1 << 13)) != 0u ? texcoord1 : texcoord2; + + if ((u_textureConfig & 1u) != 0u) tev_texture_sources[0] = texture(u_tex0, texcoord0.xy); + if ((u_textureConfig & 2u) != 0u) tev_texture_sources[1] = texture(u_tex1, texcoord1); + if ((u_textureConfig & 4u) != 0u) tev_texture_sources[2] = texture(u_tex2, tex2_uv); tev_previous_buffer[0] = vec4(0.0); tev_previous_buffer[1] = u_textureEnvBufferColor; @@ -196,7 +215,7 @@ const char* fragmentShader = R"( fragColour = tev_previous; if (tev_unimplemented_source) { - // fragColour = vec4(1.0, 0.0, 1.0, 1.0); + // fragColour = vec4(1.0, 0.0, 1.0, 1.0); } // Get original depth value by converting from [near, far] = [0, 1] to [-1, 1] @@ -337,7 +356,11 @@ void Renderer::initGraphicsContext() { depthScaleLoc = OpenGL::uniformLocation(triangleProgram, "u_depthScale"); depthOffsetLoc = OpenGL::uniformLocation(triangleProgram, "u_depthOffset"); depthmapEnableLoc = OpenGL::uniformLocation(triangleProgram, "u_depthmapEnable"); - glUniform1i(OpenGL::uniformLocation(triangleProgram, "u_tex0"), 0); // Init sampler object + + // Init sampler objects + glUniform1i(OpenGL::uniformLocation(triangleProgram, "u_tex0"), 0); + glUniform1i(OpenGL::uniformLocation(triangleProgram, "u_tex1"), 1); + glUniform1i(OpenGL::uniformLocation(triangleProgram, "u_tex2"), 2); OpenGL::Shader vertDisplay(displayVertexShader, OpenGL::Vertex); OpenGL::Shader fragDisplay(displayFragmentShader, OpenGL::Fragment); @@ -357,9 +380,18 @@ void Renderer::initGraphicsContext() { // Colour attribute vao.setAttributeFloat(1, 4, sizeof(Vertex), offsetof(Vertex, colour)); vao.enableAttribute(1); - // UV attribute - vao.setAttributeFloat(2, 2, sizeof(Vertex), offsetof(Vertex, UVs)); + // UV 0 attribute + vao.setAttributeFloat(2, 2, sizeof(Vertex), offsetof(Vertex, texcoord0)); vao.enableAttribute(2); + // UV 1 attribute + vao.setAttributeFloat(3, 2, sizeof(Vertex), offsetof(Vertex, texcoord1)); + vao.enableAttribute(3); + // UV 0 W-component attribute + vao.setAttributeFloat(4, 1, sizeof(Vertex), offsetof(Vertex, texcoord0_w)); + vao.enableAttribute(4); + // UV 2 attribute + vao.setAttributeFloat(5, 2, sizeof(Vertex), offsetof(Vertex, texcoord2)); + vao.enableAttribute(5); dummyVBO.create(); dummyVAO.create(); @@ -535,20 +567,33 @@ void Renderer::drawVertices(OpenGL::Primitives primType, std::span setupTextureEnvState(); - // Hack for rendering texture 1 - if (regs[0x80] & 1) { - const u32 dim = regs[0x82]; - const u32 config = regs[0x83]; + // Bind textures 0 to 2 + for (int i = 0; i < 3; i++) { + if ((regs[0x80] & (1 << i)) == 0) { + continue; + } + + static constexpr std::array ioBases = { + 0x80, 0x90, 0x98 + }; + + const size_t ioBase = ioBases[i]; + + const u32 dim = regs[ioBase + 2]; + const u32 config = regs[ioBase + 3]; const u32 height = dim & 0x7ff; const u32 width = getBits<16, 11>(dim); - const u32 addr = (regs[0x85] & 0x0FFFFFFF) << 3; - const u32 format = regs[0x8E] & 0xF; + const u32 addr = (regs[ioBase + 5] & 0x0FFFFFFF) << 3; + u32 format = regs[ioBase + (i == 0 ? 14 : 6)] & 0xF; + glActiveTexture(GL_TEXTURE0 + i); Texture targetTex(addr, static_cast(format), width, height, config); OpenGL::Texture tex = getTexture(targetTex); tex.bind(); } + glActiveTexture(GL_TEXTURE0); + // Update the texture unit configuration uniform if it changed const u32 texUnitConfig = regs[PICAInternalRegs::TexUnitCfg]; if (oldTexUnitConfig != texUnitConfig) {