From faa6c2d46154e47dbacea3976ab4af6be6d2356c Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Mon, 26 Sep 2022 18:12:20 -0700 Subject: [PATCH] softgpu: Implement triangle texture projection. --- GPU/Software/Rasterizer.cpp | 47 +++++++++++++++++++++++++++++----- GPU/Software/Rasterizer.h | 1 + GPU/Software/SoftGpu.cpp | 2 +- GPU/Software/TransformUnit.cpp | 10 +++----- 4 files changed, 46 insertions(+), 14 deletions(-) diff --git a/GPU/Software/Rasterizer.cpp b/GPU/Software/Rasterizer.cpp index 8101e63f0..950642f6d 100644 --- a/GPU/Software/Rasterizer.cpp +++ b/GPU/Software/Rasterizer.cpp @@ -129,6 +129,7 @@ void ComputeRasterizerState(RasterizerState *state) { state->mipFilt = gstate.isMipmapFilteringEnabled(); state->minFilt = gstate.isMinifyFilteringEnabled(); state->magFilt = gstate.isMagnifyFilteringEnabled(); + state->textureProj = gstate.getUVGenMode() == GE_TEXMAP_TEXTURE_MATRIX; } state->shadeGouraud = gstate.getShadeMode() == GE_SHADE_GOURAUD; @@ -224,8 +225,6 @@ static inline u8 ClampFogDepth(float fogdepth) { } static inline void GetTextureCoordinates(const VertexData& v0, const VertexData& v1, const float p, float &s, float &t) { - // All UV gen modes, by the time they get here, behave the same. - // Note that for environment mapping, texture coordinates have been calculated during lighting float q0 = 1.f / v0.clipw; float q1 = 1.f / v1.clipw; @@ -233,14 +232,26 @@ static inline void GetTextureCoordinates(const VertexData& v0, const VertexData& float wq1 = (1.0f - p) * q1; float q_recip = 1.0f / (wq0 + wq1); - // TODO: Handle projection. + s = (v0.texturecoords.s() * wq0 + v1.texturecoords.s() * wq1) * q_recip; + t = (v0.texturecoords.t() * wq0 + v1.texturecoords.t() * wq1) * q_recip; +} + +static inline void GetTextureCoordinatesProj(const VertexData& v0, const VertexData& v1, const float p, float &s, float &t) { + // This is for texture matrix projection. + float q0 = 1.f / v0.clipw; + float q1 = 1.f / v1.clipw; + float wq0 = p * q0; + float wq1 = (1.0f - p) * q1; + + float q_recip = 1.0f / (wq0 + wq1); + float q = (v0.texturecoords.q() * wq0 + v1.texturecoords.q() * wq1) * q_recip; + q_recip *= 1.0f / q; + s = (v0.texturecoords.s() * wq0 + v1.texturecoords.s() * wq1) * q_recip; t = (v0.texturecoords.t() * wq0 + v1.texturecoords.t() * wq1) * q_recip; } static inline void GetTextureCoordinates(const VertexData &v0, const VertexData &v1, const VertexData &v2, const Vec4 &w0, const Vec4 &w1, const Vec4 &w2, const Vec4 &wsum_recip, Vec4 &s, Vec4 &t) { - // All UV gen modes, by the time they get here, behave the same. - // Note that for environment mapping, texture coordinates have been calculated during lighting. float q0 = 1.f / v0.clipw; float q1 = 1.f / v1.clipw; @@ -250,7 +261,23 @@ static inline void GetTextureCoordinates(const VertexData &v0, const VertexData Vec4 wq2 = w2.Cast() * q2; Vec4 q_recip = (wq0 + wq1 + wq2).Reciprocal(); - // TODO: Handle projection. + s = Interpolate(v0.texturecoords.s(), v1.texturecoords.s(), v2.texturecoords.s(), wq0, wq1, wq2, q_recip); + t = Interpolate(v0.texturecoords.t(), v1.texturecoords.t(), v2.texturecoords.t(), wq0, wq1, wq2, q_recip); +} + +static inline void GetTextureCoordinatesProj(const VertexData &v0, const VertexData &v1, const VertexData &v2, const Vec4 &w0, const Vec4 &w1, const Vec4 &w2, const Vec4 &wsum_recip, Vec4 &s, Vec4 &t) { + // This is for texture matrix projection. + float q0 = 1.f / v0.clipw; + float q1 = 1.f / v1.clipw; + float q2 = 1.f / v2.clipw; + Vec4 wq0 = w0.Cast() * q0; + Vec4 wq1 = w1.Cast() * q1; + Vec4 wq2 = w2.Cast() * q2; + + Vec4 q_recip = (wq0 + wq1 + wq2).Reciprocal(); + Vec4 q = Interpolate(v0.texturecoords.q(), v1.texturecoords.q(), v2.texturecoords.q(), wq0, wq1, wq2, q_recip); + q_recip = q_recip * q.Reciprocal(); + s = Interpolate(v0.texturecoords.s(), v1.texturecoords.s(), v2.texturecoords.s(), wq0, wq1, wq2, q_recip); t = Interpolate(v0.texturecoords.t(), v1.texturecoords.t(), v2.texturecoords.t(), wq0, wq1, wq2, q_recip); } @@ -676,6 +703,9 @@ void DrawTriangleSlice( // For levels > 0, mipmapping is always based on level 0. Simpler to scale first. s *= 1.0f / (float)(1 << state.samplerID.width0Shift); t *= 1.0f / (float)(1 << state.samplerID.height0Shift); + } else if (state.textureProj) { + // Texture coordinate interpolation must definitely be perspective-correct. + GetTextureCoordinatesProj(v0, v1, v2, w0, w1, w2, wsum_recip, s, t); } else { // Texture coordinate interpolation must definitely be perspective-correct. GetTextureCoordinates(v0, v1, v2, w0, w1, w2, wsum_recip, s, t); @@ -961,6 +991,8 @@ void DrawPoint(const VertexData &v0, const BinCoords &range, const RasterizerSta if (state.throughMode) { s *= 1.0f / (float)(1 << state.samplerID.width0Shift); t *= 1.0f / (float)(1 << state.samplerID.height0Shift); + } else if (state.textureProj) { + GetTextureCoordinatesProj(v0, v0, 0.0f, s, t); } else { // Texture coordinate interpolation must definitely be perspective-correct. GetTextureCoordinates(v0, v0, 0.0f, s, t); @@ -1278,6 +1310,9 @@ void DrawLine(const VertexData &v0, const VertexData &v1, const BinCoords &range s1 = tc1.s() * (1.0f / (float)(1 << state.samplerID.width0Shift)); t = tc.t() * (1.0f / (float)(1 << state.samplerID.height0Shift)); t1 = tc1.t() * (1.0f / (float)(1 << state.samplerID.height0Shift)); + } else if (state.textureProj) { + GetTextureCoordinatesProj(v0, v1, (float)(steps - i) / steps1, s, t); + GetTextureCoordinatesProj(v0, v1, (float)(steps - i - 1) / steps1, s1, t1); } else { // Texture coordinate interpolation must definitely be perspective-correct. GetTextureCoordinates(v0, v1, (float)(steps - i) / steps1, s, t); diff --git a/GPU/Software/Rasterizer.h b/GPU/Software/Rasterizer.h index 6c350e222..bd0b82028 100644 --- a/GPU/Software/Rasterizer.h +++ b/GPU/Software/Rasterizer.h @@ -54,6 +54,7 @@ struct RasterizerState { bool minFilt : 1; bool magFilt : 1; bool antialiasLines : 1; + bool textureProj : 1; }; #if defined(SOFTGPU_MEMORY_TAGGING_DETAILED) || defined(SOFTGPU_MEMORY_TAGGING_BASIC) diff --git a/GPU/Software/SoftGpu.cpp b/GPU/Software/SoftGpu.cpp index ecef1ab64..21f77d426 100644 --- a/GPU/Software/SoftGpu.cpp +++ b/GPU/Software/SoftGpu.cpp @@ -160,7 +160,7 @@ const SoftwareCommandTableEntry softgpuCommandTable[] = { { GE_CMD_LOGICOP, 0, SoftDirty::PIXEL_BASIC | SoftDirty::PIXEL_CACHED }, { GE_CMD_LOGICOPENABLE, 0, SoftDirty::PIXEL_BASIC | SoftDirty::PIXEL_CACHED }, - { GE_CMD_TEXMAPMODE, 0, SoftDirty::TRANSFORM_BASIC }, + { GE_CMD_TEXMAPMODE, 0, SoftDirty::TRANSFORM_BASIC | SoftDirty::RAST_TEX }, // These are read on every SubmitPrim, no need for dirtying or flushing. { GE_CMD_TEXSCALEU }, diff --git a/GPU/Software/TransformUnit.cpp b/GPU/Software/TransformUnit.cpp index 9fd3540cf..633af978e 100644 --- a/GPU/Software/TransformUnit.cpp +++ b/GPU/Software/TransformUnit.cpp @@ -261,6 +261,8 @@ void ComputeTransformState(TransformState *state, const VertexReader &vreader) { state->negateNormals = gstate.areNormalsReversed(); state->uvGenMode = gstate.getUVGenMode(); + if (state->uvGenMode == GE_TEXMAP_UNKNOWN) + state->uvGenMode = GE_TEXMAP_TEXTURE_COORDS; if (state->enableTransform) { bool canSkipWorldPos = true; @@ -441,17 +443,11 @@ ClipVertexData TransformUnit::ReadVertex(VertexReader &vreader, const TransformS case GE_PROJMAP_NORMAL: source = normal; break; - - default: - source = Vec3f::AssignToAll(0.0f); - ERROR_LOG_REPORT(G3D, "Software: Unsupported UV projection mode %x", gstate.getUVProjMode()); - break; } // Note that UV scale/offset are not used in this mode. Vec3 stq = Vec3ByMatrix43(source, gstate.tgenMatrix); - float z_recip = 1.0f / stq.z; - vertex.v.texturecoords = Vec3Packedf(stq.x * z_recip, stq.y * z_recip, 1.0f); + vertex.v.texturecoords = Vec3Packedf(stq.x, stq.y, stq.z); } else if (state.uvGenMode == GE_TEXMAP_ENVIRONMENT_MAP) { Lighting::GenerateLightST(vertex.v, worldnormal); }