diff --git a/GPU/Software/Lighting.cpp b/GPU/Software/Lighting.cpp index 8214f20f63..6988ef1828 100644 --- a/GPU/Software/Lighting.cpp +++ b/GPU/Software/Lighting.cpp @@ -195,12 +195,29 @@ static inline float GENormalDot(const Vec3f &v, const Vec3f &n, float nRsqrt) { return ProductToFloat24((double)GEDot(v, n) * nRsqrt); } -static float GEShadeMapCoord(int l, const WorldCoords &worldpos, const WorldCoords &worldnormal, float normalRsqrt, const Vec3f &viewDir) { - Vec3f L(getFloat24(gstate.lpos[l * 3]), getFloat24(gstate.lpos[l * 3 + 1]), getFloat24(gstate.lpos[l * 3 + 2])); - if (gstate.getLightType(l) != GE_LIGHTTYPE_DIRECTIONAL) { - for (int i = 0; i < 3; ++i) - L[i] = GEAddFloat24(L[i], -worldpos[i]); +// The vector from a vertex to a point light as the GE computes it (gpu/probe exp153-158): the light position +// minus the world translation (with the GE's adder), minus the model position times the world matrix, summed +// like a matrix row. No world space position is formed; lighting with one in float24 loses the products' low +// bits to a large translation (Syphon Filter #13568). +static Vec3f GELightVector(const Vec3f &lpos, const Vec3f &modelpos) { + const float *m = gstate.worldMatrix; + Vec3f L; + for (int i = 0; i < 3; ++i) { + const GERowTerm terms[4] = { + GEProduct(1.0f, GEAddFloat24(lpos[i], -TruncateToFloat24(m[9 + i]))), + GEProduct(TruncateToFloat24(modelpos.x), -m[i]), + GEProduct(TruncateToFloat24(modelpos.y), -m[3 + i]), + GEProduct(TruncateToFloat24(modelpos.z), -m[6 + i]), + }; + L[i] = GERowSum(terms, 4); } + return L; +} + +static float GEShadeMapCoord(int l, const Vec3f &modelpos, const WorldCoords &worldnormal, float normalRsqrt, const Vec3f &viewDir) { + Vec3f L(getFloat24(gstate.lpos[l * 3]), getFloat24(gstate.lpos[l * 3 + 1]), getFloat24(gstate.lpos[l * 3 + 2])); + if (gstate.getLightType(l) != GE_LIGHTTYPE_DIRECTIONAL) + L = GELightVector(L, modelpos); GENormalize(L); if (gstate.isUsingSpecularLight(l)) { for (int i = 0; i < 3; ++i) @@ -210,11 +227,11 @@ static float GEShadeMapCoord(int l, const WorldCoords &worldpos, const WorldCoor return GEAddFloat24(GENormalDot(L, worldnormal, normalRsqrt), 1.0f) * 0.5f; } -void GenerateLightST(VertexData &vertex, const WorldCoords &worldpos, const WorldCoords &worldnormal, float normalRsqrt, const Vec3f &viewDir) { +void GenerateLightST(VertexData &vertex, const Vec3f &modelpos, const WorldCoords &worldnormal, float normalRsqrt, const Vec3f &viewDir) { // Always calculate texture coords from lighting results if environment mapping is active // This should be done even if lighting is disabled altogether. - vertex.texturecoords.s() = GEShadeMapCoord(gstate.getUVLS0(), worldpos, worldnormal, normalRsqrt, viewDir); - vertex.texturecoords.t() = GEShadeMapCoord(gstate.getUVLS1(), worldpos, worldnormal, normalRsqrt, viewDir); + vertex.texturecoords.s() = GEShadeMapCoord(gstate.getUVLS0(), modelpos, worldnormal, normalRsqrt, viewDir); + vertex.texturecoords.t() = GEShadeMapCoord(gstate.getUVLS1(), modelpos, worldnormal, normalRsqrt, viewDir); } @@ -246,7 +263,7 @@ static inline float Dot33(const Vec3f &a, const Vec3f &b) { } template -static void ProcessSIMD(VertexData &vertex, const WorldCoords &worldpos, const WorldCoords &worldnormal, float normalRsqrt, const State &state) { +static void ProcessSIMD(VertexData &vertex, const Vec3f &modelpos, const WorldCoords &worldnormal, float normalRsqrt, const State &state) { // Lighting blending rounds using the half offset method (like alpha blend.) Vec4 colorFactor; if (state.colorForAmbient || state.colorForDiffuse || state.colorForSpecular) { @@ -274,8 +291,7 @@ static void ProcessSIMD(VertexData &vertex, const WorldCoords &worldpos, const W float att = 1.0f; float spot = 1.0f; if (!lstate.directional) { - for (int i = 0; i < 3; ++i) - L[i] = GEAddFloat24(L[i], -worldpos[i]); + L = GELightVector(L, modelpos); // TODO: Should this normalize (0, 0, 0) to (0, 0, 1)? // The quadratic term takes the squared length from the normalization, not d * d (gpu/probe exp63). const float d2 = GEDot(L, L); @@ -366,14 +382,14 @@ static void ProcessSIMD(VertexData &vertex, const WorldCoords &worldpos, const W } } -void Process(VertexData &vertex, const WorldCoords &worldpos, const WorldCoords &worldnormal, float normalRsqrt, const State &state) { +void Process(VertexData &vertex, const Vec3f &modelpos, const WorldCoords &worldnormal, float normalRsqrt, const State &state) { #ifdef _M_SSE if (cpu_info.bSSE4_1) { - ProcessSIMD(vertex, worldpos, worldnormal, normalRsqrt, state); + ProcessSIMD(vertex, modelpos, worldnormal, normalRsqrt, state); return; } #endif - ProcessSIMD(vertex, worldpos, worldnormal, normalRsqrt, state); + ProcessSIMD(vertex, modelpos, worldnormal, normalRsqrt, state); } } // namespace diff --git a/GPU/Software/Lighting.h b/GPU/Software/Lighting.h index 1bf07534ab..669c9d0b9c 100644 --- a/GPU/Software/Lighting.h +++ b/GPU/Software/Lighting.h @@ -70,7 +70,7 @@ struct State { void ComputeState(State *state, bool hasColor0); // worldnormal isn't normalized; normalRsqrt is its reciprocal length. -void GenerateLightST(VertexData &vertex, const WorldCoords &worldpos, const WorldCoords &worldnormal, float normalRsqrt, const Vec3f &viewDir); -void Process(VertexData &vertex, const WorldCoords &worldpos, const WorldCoords &worldnormal, float normalRsqrt, const State &state); +void GenerateLightST(VertexData &vertex, const Vec3f &modelpos, const WorldCoords &worldnormal, float normalRsqrt, const Vec3f &viewDir); +void Process(VertexData &vertex, const Vec3f &modelpos, const WorldCoords &worldnormal, float normalRsqrt, const State &state); } diff --git a/GPU/Software/TransformUnit.cpp b/GPU/Software/TransformUnit.cpp index d57166a1e2..c9846330b9 100644 --- a/GPU/Software/TransformUnit.cpp +++ b/GPU/Software/TransformUnit.cpp @@ -148,12 +148,6 @@ VertexDecoder *SoftwareDrawEngine::FindVertexDecoder(u32 vtype) { return DrawEngineCommon::GetVertexDecoder(vertTypeID); } -static float GEWorldComponent(const Vec3f &v, int c); - -WorldCoords TransformUnit::ModelToWorld(const ModelCoords &coords) { - return WorldCoords(GEWorldComponent(coords, 0), GEWorldComponent(coords, 1), GEWorldComponent(coords, 2)); -} - WorldCoords TransformUnit::ModelToWorldNormal(const ModelCoords &coords) { // Each component summed like a matrix row (gpu/probe exp61). const float *m = gstate.worldMatrix; @@ -172,17 +166,6 @@ static inline float GEClipComponent(const Vec3f &v, const float m[16], int c) { return GERowSum(terms, 4); } -// A world space component, summed like a clip space row. -static float GEWorldComponent(const Vec3f &v, int c) { - const float *m = gstate.worldMatrix; - GERowTerm terms[4] = { - GEProduct(TruncateToFloat24(v.x), m[c]), - GEProduct(TruncateToFloat24(v.y), m[3 + c]), - GEProduct(TruncateToFloat24(v.z), m[6 + c]), - GEProduct(1.0f, m[9 + c]), - }; - return GERowSum(terms, 4); -} // A texture coordinate from the 4x3 texture matrix, summed like a clip space row (gpu/probe exp64). static inline float GETexGenComponent(const Vec3f &v, const float m[12], int c) { @@ -508,11 +491,6 @@ ClipVertexData TransformUnit::ReadVertex(const VertexReader &vreader, const Tran vertex.v.color1 = 0; if (state.enableTransform) { - WorldCoords worldpos; - - if (MatrixMode(state.matrixMode) == MatrixMode::WORLD_TO_CLIP) { - worldpos = TransformUnit::ModelToWorld(pos); - } // Clip coordinates with the GE's precision; the Test Drive map depends on it together with the // depth math below (#12786). { @@ -590,12 +568,12 @@ ClipVertexData TransformUnit::ReadVertex(const VertexReader &vreader, const Tran // Note that UV scale/offset are not used in this mode. vertex.v.texturecoords = Vec3Packedf(GETexGenComponent(source, gstate.tgenMatrix, 0), GETexGenComponent(source, gstate.tgenMatrix, 1), GETexGenComponent(source, gstate.tgenMatrix, 2)); } else if (state.uvGenMode == GE_TEXMAP_ENVIRONMENT_MAP) { - Lighting::GenerateLightST(vertex.v, worldpos, worldnormal, normalRsqrt, state.lightingState.viewDir); + Lighting::GenerateLightST(vertex.v, pos, worldnormal, normalRsqrt, state.lightingState.viewDir); } PROFILE_THIS_SCOPE("light"); if (state.enableLighting) - Lighting::Process(vertex.v, worldpos, worldnormal, normalRsqrt, state.lightingState); + Lighting::Process(vertex.v, pos, worldnormal, normalRsqrt, state.lightingState); } else { vertex.v.screenpos.x = (int)(pos[0] * SCREEN_SCALE_FACTOR); vertex.v.screenpos.y = (int)(pos[1] * SCREEN_SCALE_FACTOR); diff --git a/GPU/Software/TransformUnit.h b/GPU/Software/TransformUnit.h index b302c50fa8..0a3f27368b 100644 --- a/GPU/Software/TransformUnit.h +++ b/GPU/Software/TransformUnit.h @@ -119,7 +119,6 @@ public: ~TransformUnit(); static WorldCoords ModelToWorldNormal(const ModelCoords& coords); - static WorldCoords ModelToWorld(const ModelCoords& coords); static ScreenCoords ClipToScreen(const ClipCoords &coords, bool *outsideRangeFlag); // Where an edge from an inside vertex crosses the near plane, as the GE computes it. static float NearPlaneT(const ClipCoords &in, const ClipCoords &out);