diff --git a/Source/Core/VideoBackends/Software/TransformUnit.cpp b/Source/Core/VideoBackends/Software/TransformUnit.cpp index 6fcf42df9e..a967e85ca3 100644 --- a/Source/Core/VideoBackends/Software/TransformUnit.cpp +++ b/Source/Core/VideoBackends/Software/TransformUnit.cpp @@ -218,22 +218,24 @@ static float CalculateLightAttn(const LightPointer* light, Vec3* _ldir, const Ve switch (chan.attnfunc) { case AttenuationFunc::None: + { + PanicAlertFmt("None lighting in use!"); + ldir = ldir.Normalized(); + break; + } case AttenuationFunc::Dir: { ldir = ldir.Normalized(); - if (ldir == Vec3(0.0f, 0.0f, 0.0f)) - ldir = normal; break; } case AttenuationFunc::Spec: { + PanicAlertFmt("Specular lighting in use!"); ldir = ldir.Normalized(); attn = (ldir * normal) >= 0.0 ? std::max(0.0f, light->dir * normal) : 0; Vec3 attLen = Vec3(1.0, attn, attn * attn); Vec3 cosAttn = light->cosatt; Vec3 distAttn = light->distatt; - if (chan.diffusefunc != DiffuseFunc::None) - distAttn = distAttn.Normalized(); attn = SafeDivide(std::max(0.0f, attLen * cosAttn), attLen * distAttn); break; diff --git a/Source/Core/VideoCommon/LightingShaderGen.cpp b/Source/Core/VideoCommon/LightingShaderGen.cpp index 4fb2c98ebd..cc774feac6 100644 --- a/Source/Core/VideoCommon/LightingShaderGen.cpp +++ b/Source/Core/VideoCommon/LightingShaderGen.cpp @@ -11,69 +11,97 @@ #include "VideoCommon/XFMemory.h" static void GenerateLightShader(ShaderCode& object, const LightingUidData& uid_data, int index, - int litchan_index, bool alpha) + AttenuationFunc attn_func, DiffuseFunc diffuse_func, bool alpha) { + object.Write(" {{ // {} light {}\n", alpha ? "Alpha" : "Color", index); const char* swizzle = alpha ? "a" : "rgb"; const char* swizzle_components = (alpha) ? "" : "3"; - const auto attnfunc = - static_cast((uid_data.attnfunc >> (2 * litchan_index)) & 0x3); - const auto diffusefunc = - static_cast((uid_data.diffusefunc >> (2 * litchan_index)) & 0x3); + // Create a normalized vector pointing from the light to the current position. We manualy + // normalize instead of using normalize() because the raw distance is needed for spot lights. + object.Write(" float3 ldir = " LIGHT_POS ".xyz - pos.xyz;\n", LIGHT_POS_PARAMS(index)); + object.Write(" float dist2 = dot(ldir, ldir);\n" + " float dist = sqrt(dist2);\n" + " ldir = ldir / dist;\n"); - switch (attnfunc) + switch (attn_func) { case AttenuationFunc::None: + // This logic correctly reproduces the behavior (if diffuse > 0, then lacc is 255, if it's < 0 + // lacc is 0, and if it's equal to 0 lacc is unchanged, but with DiffuseFunc::None lacc instead + // has the light's color added to it), but may be an overly jank implementation (and might give + // incorrect results for a light value of 1/256, for instance; testing is needed) + if (diffuse_func == DiffuseFunc::None) + object.Write(" float attn = 1.0;\n"); + else + object.Write(" float attn = 1024.0;\n"); + break; case AttenuationFunc::Dir: - object.Write("ldir = normalize(" LIGHT_POS ".xyz - pos.xyz);\n", LIGHT_POS_PARAMS(index)); - object.Write("attn = 1.0;\n"); - object.Write("if (length(ldir) == 0.0)\n\t ldir = _normal;\n"); + object.Write(" float attn = 1.0;\n"); break; case AttenuationFunc::Spec: - object.Write("ldir = normalize(" LIGHT_POS ".xyz - pos.xyz);\n", LIGHT_POS_PARAMS(index)); - object.Write("attn = (dot(_normal, ldir) >= 0.0) ? max(0.0, dot(_normal, " LIGHT_DIR - ".xyz)) : 0.0;\n", + object.Write(" float cosine = 0.0;\n" + " // Ensure that the object is facing the light\n" + " if (dot(_normal, ldir) >= 0.0) {{\n" + " // Compute the cosine of the angle between the object normal\n" + " // and the half-angle direction for the viewer\n" + " // (assuming the half-angle direction is a unit vector)\n" + " cosine = max(0.0, dot(_normal, " LIGHT_DIR ".xyz));\n", LIGHT_DIR_PARAMS(index)); - object.Write("cosAttn = " LIGHT_COSATT ".xyz;\n", LIGHT_COSATT_PARAMS(index)); - object.Write("distAttn = {}(" LIGHT_DISTATT ".xyz);\n", - (diffusefunc == DiffuseFunc::None) ? "" : "normalize", - LIGHT_DISTATT_PARAMS(index)); - object.Write("attn = max(0.0f, dot(cosAttn, float3(1.0, attn, attn*attn))) / dot(distAttn, " - "float3(1.0, attn, attn*attn));\n"); + object.Write(" }}\n" + " // Specular lights use the angle for the denominator as well\n" + " dist = cosine;\n" + " dist2 = dist * dist;\n"); break; case AttenuationFunc::Spot: - object.Write("ldir = " LIGHT_POS ".xyz - pos.xyz;\n", LIGHT_POS_PARAMS(index)); - object.Write("dist2 = dot(ldir, ldir);\n" - "dist = sqrt(dist2);\n" - "ldir = ldir / dist;\n" - "attn = max(0.0, dot(ldir, " LIGHT_DIR ".xyz));\n", + object.Write(" // Compute the cosine of the angle between the vector to the object\n" + " // and the light's direction (assuming the direction is a unit vector)\n" + " float cosine = max(0.0, dot(ldir, " LIGHT_DIR ".xyz));\n", LIGHT_DIR_PARAMS(index)); - // attn*attn may overflow - object.Write("attn = max(0.0, " LIGHT_COSATT ".x + " LIGHT_COSATT ".y*attn + " LIGHT_COSATT - ".z*attn*attn) / dot(" LIGHT_DISTATT ".xyz, float3(1.0,dist,dist2));\n", - LIGHT_COSATT_PARAMS(index), LIGHT_COSATT_PARAMS(index), LIGHT_COSATT_PARAMS(index), - LIGHT_DISTATT_PARAMS(index)); break; } - switch (diffusefunc) + if (attn_func == AttenuationFunc::Spot || attn_func == AttenuationFunc::Spec) + { + object.Write(" float3 cosAttn = " LIGHT_COSATT ".xyz;\n", LIGHT_COSATT_PARAMS(index)); + object.Write(" float3 distAttn = " LIGHT_DISTATT ".xyz;\n", LIGHT_DISTATT_PARAMS(index)); + object.Write(" float cosine2 = cosine * cosine;\n" + "" + " // This is equivalent to dot(cosAttn, float3(1.0, attn, attn*attn)),\n" + " // except with spot lights games often don't set the direction value,\n" + " // as they configure cosAttn to (1, 0, 0). GX light objects are often\n" + " // stack-allocated, so those values are uninitialized and may be\n" + " // arbitrary garbage, including NaN or Inf, or become Inf when squared.\n" + " float numerator = cosAttn.x; // constant term\n" + " if (cosAttn.y != 0.0) numerator += cosAttn.y * cosine; // linear term\n" + " if (cosAttn.z != 0.0) numerator += cosAttn.z * cosine2; // quadratic term\n" + " // Same with the denominator, though this generally is not garbage\n" + " // Note that VertexShaderManager ensures that distAttn is not zero, which\n" + " // should prevent division by zero (TODO: what does real hardware do?)\n" + " float denominator = distAttn.x; // constant term\n" + " if (distAttn.y != 0.0) denominator += distAttn.y * dist; // linear term\n" + " if (distAttn.z != 0.0) denominator += distAttn.z * dist2; // quadratic term\n" + "" + " float attn = max(0.0f, numerator / denominator);\n"); + } + + switch (diffuse_func) { case DiffuseFunc::None: - object.Write("lacc.{} += int{}(round(attn * float{}(" LIGHT_COL ")));\n", swizzle, - swizzle_components, swizzle_components, LIGHT_COL_PARAMS(index, swizzle)); + object.Write(" float diffuse = 1.0;\n"); break; case DiffuseFunc::Sign: - case DiffuseFunc::Clamp: - object.Write("lacc.{} += int{}(round(attn * {}dot(ldir, _normal)) * float{}(" LIGHT_COL - ")));\n", - swizzle, swizzle_components, diffusefunc != DiffuseFunc::Sign ? "max(0.0," : "(", - swizzle_components, LIGHT_COL_PARAMS(index, swizzle)); + default: // Confirmed by hardware testing that invalid values use this + object.Write(" float diffuse = dot(ldir, _normal);\n"); + break; + case DiffuseFunc::Clamp: + object.Write(" float diffuse = max(0.0, dot(ldir, _normal));\n"); break; - default: - ASSERT(false); } - object.Write("\n"); + object.Write(" lacc.{} += int{}(round(attn * diffuse * float{}(" LIGHT_COL ")));\n", swizzle, + swizzle_components, swizzle_components, LIGHT_COL_PARAMS(index, swizzle)); + object.Write(" }}\n"); } // vertex shader @@ -84,94 +112,133 @@ static void GenerateLightShader(ShaderCode& object, const LightingUidData& uid_d void GenerateLightingShaderCode(ShaderCode& object, const LightingUidData& uid_data, std::string_view in_color_name, std::string_view dest) { - for (u32 j = 0; j < NUM_XF_COLOR_CHANNELS; j++) + for (u32 chan = 0; chan < NUM_XF_COLOR_CHANNELS; chan++) { - object.Write("{{\n"); + // Data for alpha is stored after all colors + const u32 chan_a = chan + NUM_XF_COLOR_CHANNELS; - const bool colormatsource = !!(uid_data.matsource & (1 << j)); - if (colormatsource) // from vertex - object.Write("int4 mat = int4(round({}{} * 255.0));\n", in_color_name, j); - else // from color - object.Write("int4 mat = {}[{}];\n", I_MATERIALS, j + 2); + const auto color_matsource = static_cast((uid_data.matsource >> chan) & 1); + const auto color_ambsource = static_cast((uid_data.ambsource >> chan) & 1); + const bool color_enable = ((uid_data.enablelighting >> chan) & 1) != 0; + const auto alpha_matsource = static_cast((uid_data.matsource >> chan_a) & 1); + const auto alpha_ambsource = static_cast((uid_data.ambsource >> chan_a) & 1); + const bool alpha_enable = ((uid_data.enablelighting >> chan_a) & 1) != 0; - if ((uid_data.enablelighting & (1 << j)) != 0) + object.Write("{{\n" + " // Lighting for channel {}:\n" + " // Color material source: {}\n" + " // Color ambient source: {}\n" + " // Color lighting enabled: {}\n" + " // Alpha material source: {}\n" + " // Alpha ambient source: {}\n" + " // Alpha lighting enabled: {}\n", + chan, color_matsource, color_ambsource, color_enable, alpha_matsource, + alpha_ambsource, alpha_enable); + + if (color_matsource == MatSource::Vertex) + object.Write(" int4 mat = int4(round({}{} * 255.0));\n", in_color_name, chan); + else // from material color register + object.Write(" int4 mat = {}[{}];\n", I_MATERIALS, chan + 2); + + if (color_enable) { - if ((uid_data.ambsource & (1 << j)) != 0) // from vertex - object.Write("lacc = int4(round({}{} * 255.0));\n", in_color_name, j); - else // from color - object.Write("lacc = {}[{}];\n", I_MATERIALS, j); + if (color_ambsource == AmbSource::Vertex) + object.Write(" int4 lacc = int4(round({}{} * 255.0));\n", in_color_name, chan); + else // from ambient color register + object.Write(" int4 lacc = {}[{}];\n", I_MATERIALS, chan); } else { - object.Write("lacc = int4(255, 255, 255, 255);\n"); + object.Write(" int4 lacc = int4(255, 255, 255, 255);\n"); } // check if alpha is different - const bool alphamatsource = !!(uid_data.matsource & (1 << (j + 2))); - if (alphamatsource != colormatsource) + if (color_matsource != alpha_matsource) { - if (alphamatsource) // from vertex - object.Write("mat.w = int(round({}{}.w * 255.0));\n", in_color_name, j); - else // from color - object.Write("mat.w = {}[{}].w;\n", I_MATERIALS, j + 2); + if (alpha_matsource == MatSource::Vertex) + object.Write(" mat.w = int(round({}{}.w * 255.0));\n", in_color_name, chan); + else // from material color register + object.Write(" mat.w = {}[{}].w;\n", I_MATERIALS, chan + 2); } - if ((uid_data.enablelighting & (1 << (j + 2))) != 0) + if (alpha_enable) { - if ((uid_data.ambsource & (1 << (j + 2))) != 0) // from vertex - object.Write("lacc.w = int(round({}{}.w * 255.0));\n", in_color_name, j); - else // from color - object.Write("lacc.w = {}[{}].w;\n", I_MATERIALS, j); + if (alpha_ambsource == AmbSource::Vertex) // from vertex + object.Write(" lacc.w = int(round({}{}.w * 255.0));\n", in_color_name, chan); + else // from ambient color register + object.Write(" lacc.w = {}[{}].w;\n", I_MATERIALS, chan); } else { - object.Write("lacc.w = 255;\n"); + object.Write(" lacc.w = 255;\n"); } - if ((uid_data.enablelighting & (1 << j)) != 0) // Color lights + if (color_enable) { - for (int i = 0; i < 8; ++i) + const auto attnfunc = static_cast((uid_data.attnfunc >> (2 * chan)) & 3); + const auto diffusefunc = static_cast((uid_data.diffusefunc >> (2 * chan)) & 3); + const u32 light_mask = + (uid_data.light_mask >> (NUM_XF_LIGHTS * chan)) & ((1 << NUM_XF_LIGHTS) - 1); + object.Write(" // Color attenuation function: {}\n", attnfunc); + object.Write(" // Color diffuse function: {}\n", diffusefunc); + object.Write(" // Color light mask: {:08b}\n", light_mask); + for (u32 light = 0; light < NUM_XF_LIGHTS; light++) { - if ((uid_data.light_mask & (1 << (i + 8 * j))) != 0) - GenerateLightShader(object, uid_data, i, j, false); + if ((light_mask & (1 << light)) != 0) + { + GenerateLightShader(object, uid_data, light, attnfunc, diffusefunc, false); + } } } - if ((uid_data.enablelighting & (1 << (j + 2))) != 0) // Alpha lights + if (alpha_enable) { - for (int i = 0; i < 8; ++i) + const auto attnfunc = static_cast((uid_data.attnfunc >> (2 * chan_a)) & 3); + const auto diffusefunc = static_cast((uid_data.diffusefunc >> (2 * chan_a)) & 3); + const u32 light_mask = + (uid_data.light_mask >> (NUM_XF_LIGHTS * chan_a)) & ((1 << NUM_XF_LIGHTS) - 1); + object.Write(" // Alpha attenuation function: {}\n", attnfunc); + object.Write(" // Alpha diffuse function: {}\n", diffusefunc); + object.Write(" // Alpha light mask function: {:08b}\n", light_mask); + for (u32 light = 0; light < NUM_XF_LIGHTS; light++) { - if ((uid_data.light_mask & (1 << (i + 8 * (j + 2)))) != 0) - GenerateLightShader(object, uid_data, i, j + 2, true); + if ((light_mask & (1 << light)) != 0) + { + GenerateLightShader(object, uid_data, light, attnfunc, diffusefunc, true); + } } } - object.Write("lacc = clamp(lacc, 0, 255);\n"); - object.Write("{}{} = float4((mat * (lacc + (lacc >> 7))) >> 8) / 255.0;\n", dest, j); + object.Write(" lacc = clamp(lacc, 0, 255);\n"); + object.Write(" {}{} = float4((mat * (lacc + (lacc >> 7))) >> 8) / 255.0;\n", dest, chan); object.Write("}}\n"); } } void GetLightingShaderUid(LightingUidData& uid_data) { - for (u32 j = 0; j < NUM_XF_COLOR_CHANNELS; j++) + for (u32 chan = 0; chan < NUM_XF_COLOR_CHANNELS; chan++) { - uid_data.matsource |= static_cast(xfmem.color[j].matsource.Value()) << j; - uid_data.matsource |= static_cast(xfmem.alpha[j].matsource.Value()) << (j + 2); - uid_data.enablelighting |= xfmem.color[j].enablelighting << j; - uid_data.enablelighting |= xfmem.alpha[j].enablelighting << (j + 2); + // Data for alpha is stored after all colors + const u32 chan_a = chan + NUM_XF_COLOR_CHANNELS; - if ((uid_data.enablelighting & (1 << j)) != 0) // Color lights + uid_data.matsource |= static_cast(xfmem.color[chan].matsource.Value()) << chan; + uid_data.matsource |= static_cast(xfmem.alpha[chan].matsource.Value()) << chan_a; + uid_data.enablelighting |= xfmem.color[chan].enablelighting << chan; + uid_data.enablelighting |= xfmem.alpha[chan].enablelighting << chan_a; + + if ((uid_data.enablelighting & (1 << chan)) != 0) // Color lights { - uid_data.ambsource |= static_cast(xfmem.color[j].ambsource.Value()) << j; - uid_data.attnfunc |= static_cast(xfmem.color[j].attnfunc.Value()) << (2 * j); - uid_data.diffusefunc |= static_cast(xfmem.color[j].diffusefunc.Value()) << (2 * j); - uid_data.light_mask |= xfmem.color[j].GetFullLightMask() << (8 * j); + uid_data.ambsource |= static_cast(xfmem.color[chan].ambsource.Value()) << chan; + uid_data.attnfunc |= static_cast(xfmem.color[chan].attnfunc.Value()) << (2 * chan); + uid_data.diffusefunc |= static_cast(xfmem.color[chan].diffusefunc.Value()) << (2 * chan); + uid_data.light_mask |= xfmem.color[chan].GetFullLightMask() << (NUM_XF_LIGHTS * chan); } - if ((uid_data.enablelighting & (1 << (j + 2))) != 0) // Alpha lights + if ((uid_data.enablelighting & (1 << chan_a)) != 0) // Alpha lights { - uid_data.ambsource |= static_cast(xfmem.alpha[j].ambsource.Value()) << (j + 2); - uid_data.attnfunc |= static_cast(xfmem.alpha[j].attnfunc.Value()) << (2 * (j + 2)); - uid_data.diffusefunc |= static_cast(xfmem.alpha[j].diffusefunc.Value()) << (2 * (j + 2)); - uid_data.light_mask |= xfmem.alpha[j].GetFullLightMask() << (8 * (j + 2)); + uid_data.ambsource |= static_cast(xfmem.alpha[chan].ambsource.Value()) << chan_a; + uid_data.attnfunc |= static_cast(xfmem.alpha[chan].attnfunc.Value()) << (2 * chan_a); + uid_data.diffusefunc |= static_cast(xfmem.alpha[chan].diffusefunc.Value()) + << (2 * chan_a); + uid_data.light_mask |= xfmem.alpha[chan].GetFullLightMask() << (NUM_XF_LIGHTS * chan_a); } } } diff --git a/Source/Core/VideoCommon/PixelShaderGen.cpp b/Source/Core/VideoCommon/PixelShaderGen.cpp index 851d127963..09f7b0bb84 100644 --- a/Source/Core/VideoCommon/PixelShaderGen.cpp +++ b/Source/Core/VideoCommon/PixelShaderGen.cpp @@ -1124,10 +1124,6 @@ ShaderCode GeneratePixelShaderCode(APIType api_type, const ShaderHostConfig& hos out.Write("\tfloat3 _normal = normalize(Normal.xyz);\n\n" "\tfloat3 pos = WorldPos;\n"); - out.Write("\tint4 lacc;\n" - "\tfloat3 ldir, h, cosAttn, distAttn;\n" - "\tfloat dist, dist2, attn;\n"); - // TODO: Our current constant usage code isn't able to handle more than one buffer. // So we can't mark the VS constant as used here. But keep them here as reference. // out.SetConstantsUsed(C_PLIGHT_COLORS, C_PLIGHT_COLORS+7); // TODO: Can be optimized further diff --git a/Source/Core/VideoCommon/UberShaderCommon.cpp b/Source/Core/VideoCommon/UberShaderCommon.cpp index d35f203737..a5f7a2cc12 100644 --- a/Source/Core/VideoCommon/UberShaderCommon.cpp +++ b/Source/Core/VideoCommon/UberShaderCommon.cpp @@ -26,18 +26,13 @@ void WriteLightingFunction(ShaderCode& out) out.Write(" case {:s}:\n", AttenuationFunc::Dir); out.Write(" ldir = normalize(" I_LIGHTS "[index].pos.xyz - pos.xyz);\n" " attn = 1.0;\n" - " if (length(ldir) == 0.0)\n" - " ldir = normal;\n" " break;\n\n"); out.Write(" case {:s}:\n", AttenuationFunc::Spec); out.Write(" ldir = normalize(" I_LIGHTS "[index].pos.xyz - pos.xyz);\n" " attn = (dot(normal, ldir) >= 0.0) ? max(0.0, dot(normal, " I_LIGHTS "[index].dir.xyz)) : 0.0;\n" - " cosAttn = " I_LIGHTS "[index].cosatt.xyz;\n"); - out.Write(" if (diffusefunc == {:s})\n", DiffuseFunc::None); - out.Write(" distAttn = " I_LIGHTS "[index].distatt.xyz;\n" - " else\n" - " distAttn = normalize(" I_LIGHTS "[index].distatt.xyz);\n" + " cosAttn = " I_LIGHTS "[index].cosatt.xyz;\n" + " distAttn = " I_LIGHTS "[index].distatt.xyz;\n" " attn = max(0.0, dot(cosAttn, float3(1.0, attn, attn*attn))) / dot(distAttn, " "float3(1.0, attn, attn*attn));\n" " break;\n\n"); diff --git a/Source/Core/VideoCommon/VertexShaderGen.cpp b/Source/Core/VideoCommon/VertexShaderGen.cpp index 41512cd489..8e4ffff9bf 100644 --- a/Source/Core/VideoCommon/VertexShaderGen.cpp +++ b/Source/Core/VideoCommon/VertexShaderGen.cpp @@ -430,10 +430,6 @@ ShaderCode GenerateVertexShaderCode(APIType api_type, const ShaderHostConfig& ho out.Write("o.pos = float4(dot(" I_PROJECTION "[0], pos), dot(" I_PROJECTION "[1], pos), dot(" I_PROJECTION "[2], pos), dot(" I_PROJECTION "[3], pos));\n"); - out.Write("int4 lacc;\n" - "float3 ldir, h, cosAttn, distAttn;\n" - "float dist, dist2, attn;\n"); - GenerateLightingShaderCode(out, uid_data->lighting, "vertex_color_", "o.colors_"); // transform texcoords @@ -497,7 +493,7 @@ ShaderCode GenerateVertexShaderCode(APIType api_type, const ShaderHostConfig& ho case TexGenType::EmbossMap: // calculate tex coords into bump map // transform the light dir into tangent space - out.Write("ldir = normalize(" LIGHT_POS ".xyz - pos.xyz);\n", + out.Write("float3 ldir = normalize(" LIGHT_POS ".xyz - pos.xyz);\n", LIGHT_POS_PARAMS(texinfo.embosslightshift)); out.Write( "o.tex{}.xyz = o.tex{}.xyz + float3(dot(ldir, _tangent), dot(ldir, _binormal), 0.0);\n", diff --git a/Source/Core/VideoCommon/VertexShaderManager.cpp b/Source/Core/VideoCommon/VertexShaderManager.cpp index bb3a7f2910..aeeefd82c1 100644 --- a/Source/Core/VideoCommon/VertexShaderManager.cpp +++ b/Source/Core/VideoCommon/VertexShaderManager.cpp @@ -256,22 +256,9 @@ void VertexShaderManager::SetConstants(const std::vector& textures, dstlight.pos[1] = light.dpos[1]; dstlight.pos[2] = light.dpos[2]; - // TODO: Hardware testing is needed to confirm that this normalization is correct - auto sanitize = [](float f) { - if (std::isnan(f)) - return 0.0f; - else if (std::isinf(f)) - return f > 0.0f ? 1.0f : -1.0f; - else - return f; - }; - double norm = double(light.ddir[0]) * double(light.ddir[0]) + - double(light.ddir[1]) * double(light.ddir[1]) + - double(light.ddir[2]) * double(light.ddir[2]); - norm = 1.0 / sqrt(norm); - dstlight.dir[0] = sanitize(static_cast(light.ddir[0] * norm)); - dstlight.dir[1] = sanitize(static_cast(light.ddir[1] * norm)); - dstlight.dir[2] = sanitize(static_cast(light.ddir[2] * norm)); + dstlight.dir[0] = light.ddir[0]; + dstlight.dir[1] = light.ddir[1]; + dstlight.dir[2] = light.ddir[2]; } dirty = true; diff --git a/Source/Core/VideoCommon/XFMemory.h b/Source/Core/VideoCommon/XFMemory.h index e0a2696317..5dce25951c 100644 --- a/Source/Core/VideoCommon/XFMemory.h +++ b/Source/Core/VideoCommon/XFMemory.h @@ -14,6 +14,7 @@ #include "VideoCommon/CPMemory.h" constexpr size_t NUM_XF_COLOR_CHANNELS = 2; +constexpr size_t NUM_XF_LIGHTS = 8; // Lighting @@ -430,7 +431,7 @@ struct alignas(16) XFMemory float normalMatrices[96]; // 0x0400 - 0x045f u32 unk1[160]; // 0x0460 - 0x04ff float postMatrices[256]; // 0x0500 - 0x05ff - Light lights[8]; // 0x0600 - 0x067f + Light lights[NUM_XF_LIGHTS]; // 0x0600 - 0x067f u32 unk2[2432]; // 0x0680 - 0x0fff u32 error; // 0x1000 u32 diag; // 0x1001