diff options
| -rw-r--r-- | src/common/quaternion.h | 5 | ||||
| -rw-r--r-- | src/common/vector_math.h | 7 | ||||
| -rw-r--r-- | src/core/hle/service/dlp/dlp_clnt.cpp | 21 | ||||
| -rw-r--r-- | src/core/hle/service/dlp/dlp_fkcl.cpp | 18 | ||||
| -rw-r--r-- | src/core/hle/service/dlp/dlp_srvr.cpp | 9 | ||||
| -rw-r--r-- | src/video_core/CMakeLists.txt | 2 | ||||
| -rw-r--r-- | src/video_core/command_processor.cpp | 62 | ||||
| -rw-r--r-- | src/video_core/pica_state.h | 2 | ||||
| -rw-r--r-- | src/video_core/regs_pipeline.h | 9 | ||||
| -rw-r--r-- | src/video_core/swrasterizer/clipper.cpp | 11 | ||||
| -rw-r--r-- | src/video_core/swrasterizer/lighting.cpp | 250 | ||||
| -rw-r--r-- | src/video_core/swrasterizer/lighting.h | 18 | ||||
| -rw-r--r-- | src/video_core/swrasterizer/rasterizer.cpp | 29 | 
13 files changed, 404 insertions, 39 deletions
diff --git a/src/common/quaternion.h b/src/common/quaternion.h index 84ac82ed3..77f626bcb 100644 --- a/src/common/quaternion.h +++ b/src/common/quaternion.h @@ -30,6 +30,11 @@ public:          return {xyz * other.w + other.xyz * w + Cross(xyz, other.xyz),                  w * other.w - Dot(xyz, other.xyz)};      } + +    Quaternion<T> Normalized() const { +        T length = std::sqrt(xyz.Length2() + w * w); +        return {xyz / length, w / length}; +    }  };  template <typename T> diff --git a/src/common/vector_math.h b/src/common/vector_math.h index c7a461a1e..6e2a5ad60 100644 --- a/src/common/vector_math.h +++ b/src/common/vector_math.h @@ -31,7 +31,6 @@  #pragma once  #include <cmath> -#include <type_traits>  namespace Math { @@ -90,7 +89,7 @@ public:          x -= other.x;          y -= other.y;      } -    template <typename Q = T, class = typename std::enable_if<std::is_signed<Q>::value>::type> +      Vec2<decltype(-T{})> operator-() const {          return MakeVec(-x, -y);      } @@ -247,7 +246,7 @@ public:          y -= other.y;          z -= other.z;      } -    template <typename Q = T, class = typename std::enable_if<std::is_signed<Q>::value>::type> +      Vec3<decltype(-T{})> operator-() const {          return MakeVec(-x, -y, -z);      } @@ -462,7 +461,7 @@ public:          z -= other.z;          w -= other.w;      } -    template <typename Q = T, class = typename std::enable_if<std::is_signed<Q>::value>::type> +      Vec4<decltype(-T{})> operator-() const {          return MakeVec(-x, -y, -z, -w);      } diff --git a/src/core/hle/service/dlp/dlp_clnt.cpp b/src/core/hle/service/dlp/dlp_clnt.cpp index 56f934b3f..6f2bf2061 100644 --- a/src/core/hle/service/dlp/dlp_clnt.cpp +++ b/src/core/hle/service/dlp/dlp_clnt.cpp @@ -8,7 +8,26 @@ namespace Service {  namespace DLP {  const Interface::FunctionInfo FunctionTable[] = { -    {0x000100C3, nullptr, "Initialize"}, {0x00110000, nullptr, "GetWirelessRebootPassphrase"}, +    {0x000100C3, nullptr, "Initialize"}, +    {0x00020000, nullptr, "Finalize"}, +    {0x00030000, nullptr, "GetEventDesc"}, +    {0x00040000, nullptr, "GetChannel"}, +    {0x00050180, nullptr, "StartScan"}, +    {0x00060000, nullptr, "StopScan"}, +    {0x00070080, nullptr, "GetServerInfo"}, +    {0x00080100, nullptr, "GetTitleInfo"}, +    {0x00090040, nullptr, "GetTitleInfoInOrder"}, +    {0x000A0080, nullptr, "DeleteScanInfo"}, +    {0x000B0100, nullptr, "PrepareForSystemDownload"}, +    {0x000C0000, nullptr, "StartSystemDownload"}, +    {0x000D0100, nullptr, "StartTitleDownload"}, +    {0x000E0000, nullptr, "GetMyStatus"}, +    {0x000F0040, nullptr, "GetConnectingNodes"}, +    {0x00100040, nullptr, "GetNodeInfo"}, +    {0x00110000, nullptr, "GetWirelessRebootPassphrase"}, +    {0x00120000, nullptr, "StopSession"}, +    {0x00130100, nullptr, "GetCupVersion"}, +    {0x00140100, nullptr, "GetDupAvailability"},  };  DLP_CLNT_Interface::DLP_CLNT_Interface() { diff --git a/src/core/hle/service/dlp/dlp_fkcl.cpp b/src/core/hle/service/dlp/dlp_fkcl.cpp index 29b9d52e0..fe6be7d32 100644 --- a/src/core/hle/service/dlp/dlp_fkcl.cpp +++ b/src/core/hle/service/dlp/dlp_fkcl.cpp @@ -8,7 +8,23 @@ namespace Service {  namespace DLP {  const Interface::FunctionInfo FunctionTable[] = { -    {0x00010083, nullptr, "Initialize"}, {0x000F0000, nullptr, "GetWirelessRebootPassphrase"}, +    {0x00010083, nullptr, "Initialize"}, +    {0x00020000, nullptr, "Finalize"}, +    {0x00030000, nullptr, "GetEventDesc"}, +    {0x00040000, nullptr, "GetChannels"}, +    {0x00050180, nullptr, "StartScan"}, +    {0x00060000, nullptr, "StopScan"}, +    {0x00070080, nullptr, "GetServerInfo"}, +    {0x00080100, nullptr, "GetTitleInfo"}, +    {0x00090040, nullptr, "GetTitleInfoInOrder"}, +    {0x000A0080, nullptr, "DeleteScanInfo"}, +    {0x000B0100, nullptr, "StartFakeSession"}, +    {0x000C0000, nullptr, "GetMyStatus"}, +    {0x000D0040, nullptr, "GetConnectingNodes"}, +    {0x000E0040, nullptr, "GetNodeInfo"}, +    {0x000F0000, nullptr, "GetWirelessRebootPassphrase"}, +    {0x00100000, nullptr, "StopSession"}, +    {0x00110203, nullptr, "Initialize2"},  };  DLP_FKCL_Interface::DLP_FKCL_Interface() { diff --git a/src/core/hle/service/dlp/dlp_srvr.cpp b/src/core/hle/service/dlp/dlp_srvr.cpp index 32cfa2c44..1bcea43d3 100644 --- a/src/core/hle/service/dlp/dlp_srvr.cpp +++ b/src/core/hle/service/dlp/dlp_srvr.cpp @@ -11,7 +11,7 @@  namespace Service {  namespace DLP { -static void unk_0x000E0040(Interface* self) { +static void IsChild(Interface* self) {      u32* cmd_buff = Kernel::GetCommandBuffer();      cmd_buff[1] = RESULT_SUCCESS.raw; @@ -24,14 +24,19 @@ const Interface::FunctionInfo FunctionTable[] = {      {0x00010183, nullptr, "Initialize"},      {0x00020000, nullptr, "Finalize"},      {0x00030000, nullptr, "GetServerState"}, +    {0x00040000, nullptr, "GetEventDescription"},      {0x00050080, nullptr, "StartAccepting"}, +    {0x00060000, nullptr, "EndAccepting"},      {0x00070000, nullptr, "StartDistribution"},      {0x000800C0, nullptr, "SendWirelessRebootPassphrase"},      {0x00090040, nullptr, "AcceptClient"}, +    {0x000A0040, nullptr, "DisconnectClient"},      {0x000B0042, nullptr, "GetConnectingClients"},      {0x000C0040, nullptr, "GetClientInfo"},      {0x000D0040, nullptr, "GetClientState"}, -    {0x000E0040, unk_0x000E0040, "unk_0x000E0040"}, +    {0x000E0040, IsChild, "IsChild"}, +    {0x000F0303, nullptr, "InitializeWithName"}, +    {0x00100000, nullptr, "GetDupNoticeNeed"},  };  DLP_SRVR_Interface::DLP_SRVR_Interface() { diff --git a/src/video_core/CMakeLists.txt b/src/video_core/CMakeLists.txt index 0961a3251..cffa4c952 100644 --- a/src/video_core/CMakeLists.txt +++ b/src/video_core/CMakeLists.txt @@ -15,6 +15,7 @@ set(SRCS              shader/shader_interpreter.cpp              swrasterizer/clipper.cpp              swrasterizer/framebuffer.cpp +            swrasterizer/lighting.cpp              swrasterizer/proctex.cpp              swrasterizer/rasterizer.cpp              swrasterizer/swrasterizer.cpp @@ -55,6 +56,7 @@ set(HEADERS              shader/shader_interpreter.h              swrasterizer/clipper.h              swrasterizer/framebuffer.h +            swrasterizer/lighting.h              swrasterizer/proctex.h              swrasterizer/rasterizer.h              swrasterizer/swrasterizer.h diff --git a/src/video_core/command_processor.cpp b/src/video_core/command_processor.cpp index 4633a1df1..f98ca3302 100644 --- a/src/video_core/command_processor.cpp +++ b/src/video_core/command_processor.cpp @@ -119,27 +119,6 @@ static void WriteUniformFloatReg(ShaderRegs& config, Shader::ShaderSetup& setup,      }  } -static void WriteProgramCode(ShaderRegs& config, Shader::ShaderSetup& setup, -                             unsigned max_program_code_length, u32 value) { -    if (config.program.offset >= max_program_code_length) { -        LOG_ERROR(HW_GPU, "Invalid %s program offset %d", GetShaderSetupTypeName(setup), -                  (int)config.program.offset); -    } else { -        setup.program_code[config.program.offset] = value; -        config.program.offset++; -    } -} - -static void WriteSwizzlePatterns(ShaderRegs& config, Shader::ShaderSetup& setup, u32 value) { -    if (config.swizzle_patterns.offset >= setup.swizzle_data.size()) { -        LOG_ERROR(HW_GPU, "Invalid %s swizzle pattern offset %d", GetShaderSetupTypeName(setup), -                  (int)config.swizzle_patterns.offset); -    } else { -        setup.swizzle_data[config.swizzle_patterns.offset] = value; -        config.swizzle_patterns.offset++; -    } -} -  static void WritePicaReg(u32 id, u32 value, u32 mask) {      auto& regs = g_state.regs; @@ -458,7 +437,13 @@ static void WritePicaReg(u32 id, u32 value, u32 mask) {      case PICA_REG_INDEX_WORKAROUND(gs.program.set_word[5], 0x2a1):      case PICA_REG_INDEX_WORKAROUND(gs.program.set_word[6], 0x2a2):      case PICA_REG_INDEX_WORKAROUND(gs.program.set_word[7], 0x2a3): { -        WriteProgramCode(g_state.regs.gs, g_state.gs, 4096, value); +        u32& offset = g_state.regs.gs.program.offset; +        if (offset >= 4096) { +            LOG_ERROR(HW_GPU, "Invalid GS program offset %u", offset); +        } else { +            g_state.gs.program_code[offset] = value; +            offset++; +        }          break;      } @@ -470,11 +455,18 @@ static void WritePicaReg(u32 id, u32 value, u32 mask) {      case PICA_REG_INDEX_WORKAROUND(gs.swizzle_patterns.set_word[5], 0x2ab):      case PICA_REG_INDEX_WORKAROUND(gs.swizzle_patterns.set_word[6], 0x2ac):      case PICA_REG_INDEX_WORKAROUND(gs.swizzle_patterns.set_word[7], 0x2ad): { -        WriteSwizzlePatterns(g_state.regs.gs, g_state.gs, value); +        u32& offset = g_state.regs.gs.swizzle_patterns.offset; +        if (offset >= g_state.gs.swizzle_data.size()) { +            LOG_ERROR(HW_GPU, "Invalid GS swizzle pattern offset %u", offset); +        } else { +            g_state.gs.swizzle_data[offset] = value; +            offset++; +        }          break;      }      case PICA_REG_INDEX(vs.bool_uniforms): +        // TODO (wwylele): does regs.pipeline.gs_unit_exclusive_configuration affect this?          WriteUniformBoolReg(g_state.vs, g_state.regs.vs.bool_uniforms.Value());          break; @@ -482,6 +474,7 @@ static void WritePicaReg(u32 id, u32 value, u32 mask) {      case PICA_REG_INDEX_WORKAROUND(vs.int_uniforms[1], 0x2b2):      case PICA_REG_INDEX_WORKAROUND(vs.int_uniforms[2], 0x2b3):      case PICA_REG_INDEX_WORKAROUND(vs.int_uniforms[3], 0x2b4): { +        // TODO (wwylele): does regs.pipeline.gs_unit_exclusive_configuration affect this?          unsigned index = (id - PICA_REG_INDEX_WORKAROUND(vs.int_uniforms[0], 0x2b1));          auto values = regs.vs.int_uniforms[index];          WriteUniformIntReg(g_state.vs, index, @@ -497,6 +490,7 @@ static void WritePicaReg(u32 id, u32 value, u32 mask) {      case PICA_REG_INDEX_WORKAROUND(vs.uniform_setup.set_value[5], 0x2c6):      case PICA_REG_INDEX_WORKAROUND(vs.uniform_setup.set_value[6], 0x2c7):      case PICA_REG_INDEX_WORKAROUND(vs.uniform_setup.set_value[7], 0x2c8): { +        // TODO (wwylele): does regs.pipeline.gs_unit_exclusive_configuration affect this?          WriteUniformFloatReg(g_state.regs.vs, g_state.vs, vs_float_regs_counter,                               vs_uniform_write_buffer, value);          break; @@ -510,7 +504,16 @@ static void WritePicaReg(u32 id, u32 value, u32 mask) {      case PICA_REG_INDEX_WORKAROUND(vs.program.set_word[5], 0x2d1):      case PICA_REG_INDEX_WORKAROUND(vs.program.set_word[6], 0x2d2):      case PICA_REG_INDEX_WORKAROUND(vs.program.set_word[7], 0x2d3): { -        WriteProgramCode(g_state.regs.vs, g_state.vs, 512, value); +        u32& offset = g_state.regs.vs.program.offset; +        if (offset >= 512) { +            LOG_ERROR(HW_GPU, "Invalid VS program offset %u", offset); +        } else { +            g_state.vs.program_code[offset] = value; +            if (!g_state.regs.pipeline.gs_unit_exclusive_configuration) { +                g_state.gs.program_code[offset] = value; +            } +            offset++; +        }          break;      } @@ -522,7 +525,16 @@ static void WritePicaReg(u32 id, u32 value, u32 mask) {      case PICA_REG_INDEX_WORKAROUND(vs.swizzle_patterns.set_word[5], 0x2db):      case PICA_REG_INDEX_WORKAROUND(vs.swizzle_patterns.set_word[6], 0x2dc):      case PICA_REG_INDEX_WORKAROUND(vs.swizzle_patterns.set_word[7], 0x2dd): { -        WriteSwizzlePatterns(g_state.regs.vs, g_state.vs, value); +        u32& offset = g_state.regs.vs.swizzle_patterns.offset; +        if (offset >= g_state.vs.swizzle_data.size()) { +            LOG_ERROR(HW_GPU, "Invalid VS swizzle pattern offset %u", offset); +        } else { +            g_state.vs.swizzle_data[offset] = value; +            if (!g_state.regs.pipeline.gs_unit_exclusive_configuration) { +                g_state.gs.swizzle_data[offset] = value; +            } +            offset++; +        }          break;      } diff --git a/src/video_core/pica_state.h b/src/video_core/pica_state.h index 2d23d34e6..864a2c9e6 100644 --- a/src/video_core/pica_state.h +++ b/src/video_core/pica_state.h @@ -79,7 +79,7 @@ struct State {          std::array<ColorDifferenceEntry, 256> color_diff_table;      } proctex; -    struct { +    struct Lighting {          union LutEntry {              // Used for raw access              u32 raw; diff --git a/src/video_core/regs_pipeline.h b/src/video_core/regs_pipeline.h index 31c747d77..8b6369297 100644 --- a/src/video_core/regs_pipeline.h +++ b/src/video_core/regs_pipeline.h @@ -202,7 +202,14 @@ struct PipelineRegs {      /// Number of input attributes to the vertex shader minus 1      BitField<0, 4, u32> max_input_attrib_index; -    INSERT_PADDING_WORDS(2); +    INSERT_PADDING_WORDS(1); + +    // The shader unit 3, which can be used for both vertex and geometry shader, gets its +    // configuration depending on this register. If this is not set, unit 3 will share some +    // configuration with other units. It is known that program code and swizzle pattern uploaded +    // via regs.vs will be also uploaded to unit 3 if this is not set. Although very likely, it is +    // still unclear whether uniforms and other configuration can be also shared. +    BitField<0, 1, u32> gs_unit_exclusive_configuration;      enum class GPUMode : u32 {          Drawing = 0, diff --git a/src/video_core/swrasterizer/clipper.cpp b/src/video_core/swrasterizer/clipper.cpp index 6fb923756..7537689b7 100644 --- a/src/video_core/swrasterizer/clipper.cpp +++ b/src/video_core/swrasterizer/clipper.cpp @@ -95,6 +95,17 @@ void ProcessTriangle(const OutputVertex& v0, const OutputVertex& v1, const Outpu      static const size_t MAX_VERTICES = 9;      static_vector<Vertex, MAX_VERTICES> buffer_a = {v0, v1, v2};      static_vector<Vertex, MAX_VERTICES> buffer_b; + +    auto FlipQuaternionIfOpposite = [](auto& a, const auto& b) { +        if (Math::Dot(a, b) < float24::Zero()) +            a = -a; +    }; + +    // Flip the quaternions if they are opposite to prevent interpolating them over the wrong +    // direction. +    FlipQuaternionIfOpposite(buffer_a[1].quat, buffer_a[0].quat); +    FlipQuaternionIfOpposite(buffer_a[2].quat, buffer_a[0].quat); +      auto* output_list = &buffer_a;      auto* input_list = &buffer_b; diff --git a/src/video_core/swrasterizer/lighting.cpp b/src/video_core/swrasterizer/lighting.cpp new file mode 100644 index 000000000..63088eee8 --- /dev/null +++ b/src/video_core/swrasterizer/lighting.cpp @@ -0,0 +1,250 @@ +// Copyright 2017 Citra Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#include "common/math_util.h" +#include "video_core/swrasterizer/lighting.h" + +namespace Pica { + +static float LookupLightingLut(const Pica::State::Lighting& lighting, size_t lut_index, u8 index, +                               float delta) { +    ASSERT_MSG(lut_index < lighting.luts.size(), "Out of range lut"); +    ASSERT_MSG(index < lighting.luts[lut_index].size(), "Out of range index"); + +    const auto& lut = lighting.luts[lut_index][index]; + +    float lut_value = lut.ToFloat(); +    float lut_diff = lut.DiffToFloat(); + +    return lut_value + lut_diff * delta; +} + +std::tuple<Math::Vec4<u8>, Math::Vec4<u8>> ComputeFragmentsColors( +    const Pica::LightingRegs& lighting, const Pica::State::Lighting& lighting_state, +    const Math::Quaternion<float>& normquat, const Math::Vec3<float>& view) { + +    // TODO(Subv): Bump mapping +    Math::Vec3<float> surface_normal = {0.0f, 0.0f, 1.0f}; + +    if (lighting.config0.bump_mode != LightingRegs::LightingBumpMode::None) { +        LOG_CRITICAL(HW_GPU, "unimplemented bump mapping"); +        UNIMPLEMENTED(); +    } + +    // Use the normalized the quaternion when performing the rotation +    auto normal = Math::QuaternionRotate(normquat, surface_normal); + +    Math::Vec4<float> diffuse_sum = {0.0f, 0.0f, 0.0f, 1.0f}; +    Math::Vec4<float> specular_sum = {0.0f, 0.0f, 0.0f, 1.0f}; + +    for (unsigned light_index = 0; light_index <= lighting.max_light_index; ++light_index) { +        unsigned num = lighting.light_enable.GetNum(light_index); +        const auto& light_config = lighting.light[num]; + +        Math::Vec3<float> refl_value = {}; +        Math::Vec3<float> position = {float16::FromRaw(light_config.x).ToFloat32(), +                                      float16::FromRaw(light_config.y).ToFloat32(), +                                      float16::FromRaw(light_config.z).ToFloat32()}; +        Math::Vec3<float> light_vector; + +        if (light_config.config.directional) +            light_vector = position; +        else +            light_vector = position + view; + +        light_vector.Normalize(); + +        float dist_atten = 1.0f; +        if (!lighting.IsDistAttenDisabled(num)) { +            auto distance = (-view - position).Length(); +            float scale = Pica::float20::FromRaw(light_config.dist_atten_scale).ToFloat32(); +            float bias = Pica::float20::FromRaw(light_config.dist_atten_bias).ToFloat32(); +            size_t lut = +                static_cast<size_t>(LightingRegs::LightingSampler::DistanceAttenuation) + num; + +            float sample_loc = MathUtil::Clamp(scale * distance + bias, 0.0f, 1.0f); + +            u8 lutindex = +                static_cast<u8>(MathUtil::Clamp(std::floor(sample_loc * 256.0f), 0.0f, 255.0f)); +            float delta = sample_loc * 256 - lutindex; +            dist_atten = LookupLightingLut(lighting_state, lut, lutindex, delta); +        } + +        auto GetLutValue = [&](LightingRegs::LightingLutInput input, bool abs, +                               LightingRegs::LightingScale scale_enum, +                               LightingRegs::LightingSampler sampler) { +            Math::Vec3<float> norm_view = view.Normalized(); +            Math::Vec3<float> half_angle = (norm_view + light_vector).Normalized(); +            float result = 0.0f; + +            switch (input) { +            case LightingRegs::LightingLutInput::NH: +                result = Math::Dot(normal, half_angle); +                break; + +            case LightingRegs::LightingLutInput::VH: +                result = Math::Dot(norm_view, half_angle); +                break; + +            case LightingRegs::LightingLutInput::NV: +                result = Math::Dot(normal, norm_view); +                break; + +            case LightingRegs::LightingLutInput::LN: +                result = Math::Dot(light_vector, normal); +                break; + +            default: +                LOG_CRITICAL(HW_GPU, "Unknown lighting LUT input %u\n", static_cast<u32>(input)); +                UNIMPLEMENTED(); +                result = 0.0f; +            } + +            u8 index; +            float delta; + +            if (abs) { +                if (light_config.config.two_sided_diffuse) +                    result = std::abs(result); +                else +                    result = std::max(result, 0.0f); + +                float flr = std::floor(result * 256.0f); +                index = static_cast<u8>(MathUtil::Clamp(flr, 0.0f, 255.0f)); +                delta = result * 256 - index; +            } else { +                float flr = std::floor(result * 128.0f); +                s8 signed_index = static_cast<s8>(MathUtil::Clamp(flr, -128.0f, 127.0f)); +                delta = result * 128.0f - signed_index; +                index = static_cast<u8>(signed_index); +            } + +            float scale = lighting.lut_scale.GetScale(scale_enum); +            return scale * +                   LookupLightingLut(lighting_state, static_cast<size_t>(sampler), index, delta); +        }; + +        // Specular 0 component +        float d0_lut_value = 1.0f; +        if (lighting.config1.disable_lut_d0 == 0 && +            LightingRegs::IsLightingSamplerSupported( +                lighting.config0.config, LightingRegs::LightingSampler::Distribution0)) { +            d0_lut_value = +                GetLutValue(lighting.lut_input.d0, lighting.abs_lut_input.disable_d0 == 0, +                            lighting.lut_scale.d0, LightingRegs::LightingSampler::Distribution0); +        } + +        Math::Vec3<float> specular_0 = d0_lut_value * light_config.specular_0.ToVec3f(); + +        // If enabled, lookup ReflectRed value, otherwise, 1.0 is used +        if (lighting.config1.disable_lut_rr == 0 && +            LightingRegs::IsLightingSamplerSupported(lighting.config0.config, +                                                     LightingRegs::LightingSampler::ReflectRed)) { +            refl_value.x = +                GetLutValue(lighting.lut_input.rr, lighting.abs_lut_input.disable_rr == 0, +                            lighting.lut_scale.rr, LightingRegs::LightingSampler::ReflectRed); +        } else { +            refl_value.x = 1.0f; +        } + +        // If enabled, lookup ReflectGreen value, otherwise, ReflectRed value is used +        if (lighting.config1.disable_lut_rg == 0 && +            LightingRegs::IsLightingSamplerSupported(lighting.config0.config, +                                                     LightingRegs::LightingSampler::ReflectGreen)) { +            refl_value.y = +                GetLutValue(lighting.lut_input.rg, lighting.abs_lut_input.disable_rg == 0, +                            lighting.lut_scale.rg, LightingRegs::LightingSampler::ReflectGreen); +        } else { +            refl_value.y = refl_value.x; +        } + +        // If enabled, lookup ReflectBlue value, otherwise, ReflectRed value is used +        if (lighting.config1.disable_lut_rb == 0 && +            LightingRegs::IsLightingSamplerSupported(lighting.config0.config, +                                                     LightingRegs::LightingSampler::ReflectBlue)) { +            refl_value.z = +                GetLutValue(lighting.lut_input.rb, lighting.abs_lut_input.disable_rb == 0, +                            lighting.lut_scale.rb, LightingRegs::LightingSampler::ReflectBlue); +        } else { +            refl_value.z = refl_value.x; +        } + +        // Specular 1 component +        float d1_lut_value = 1.0f; +        if (lighting.config1.disable_lut_d1 == 0 && +            LightingRegs::IsLightingSamplerSupported( +                lighting.config0.config, LightingRegs::LightingSampler::Distribution1)) { +            d1_lut_value = +                GetLutValue(lighting.lut_input.d1, lighting.abs_lut_input.disable_d1 == 0, +                            lighting.lut_scale.d1, LightingRegs::LightingSampler::Distribution1); +        } + +        Math::Vec3<float> specular_1 = +            d1_lut_value * refl_value * light_config.specular_1.ToVec3f(); + +        // Fresnel +        if (lighting.config1.disable_lut_fr == 0 && +            LightingRegs::IsLightingSamplerSupported(lighting.config0.config, +                                                     LightingRegs::LightingSampler::Fresnel)) { + +            float lut_value = +                GetLutValue(lighting.lut_input.fr, lighting.abs_lut_input.disable_fr == 0, +                            lighting.lut_scale.fr, LightingRegs::LightingSampler::Fresnel); + +            // Enabled for diffuse lighting alpha component +            if (lighting.config0.fresnel_selector == +                    LightingRegs::LightingFresnelSelector::PrimaryAlpha || +                lighting.config0.fresnel_selector == LightingRegs::LightingFresnelSelector::Both) { +                diffuse_sum.a() *= lut_value; +            } + +            // Enabled for the specular lighting alpha component +            if (lighting.config0.fresnel_selector == +                    LightingRegs::LightingFresnelSelector::SecondaryAlpha || +                lighting.config0.fresnel_selector == LightingRegs::LightingFresnelSelector::Both) { +                specular_sum.a() *= lut_value; +            } +        } + +        auto dot_product = Math::Dot(light_vector, normal); + +        // Calculate clamp highlights before applying the two-sided diffuse configuration to the dot +        // product. +        float clamp_highlights = 1.0f; +        if (lighting.config0.clamp_highlights) { +            if (dot_product <= 0.0f) +                clamp_highlights = 0.0f; +            else +                clamp_highlights = 1.0f; +        } + +        if (light_config.config.two_sided_diffuse) +            dot_product = std::abs(dot_product); +        else +            dot_product = std::max(dot_product, 0.0f); + +        auto diffuse = +            light_config.diffuse.ToVec3f() * dot_product + light_config.ambient.ToVec3f(); +        diffuse_sum += Math::MakeVec(diffuse * dist_atten, 0.0f); + +        specular_sum += +            Math::MakeVec((specular_0 + specular_1) * clamp_highlights * dist_atten, 0.0f); +    } + +    diffuse_sum += Math::MakeVec(lighting.global_ambient.ToVec3f(), 0.0f); + +    auto diffuse = Math::MakeVec<float>(MathUtil::Clamp(diffuse_sum.x, 0.0f, 1.0f) * 255, +                                        MathUtil::Clamp(diffuse_sum.y, 0.0f, 1.0f) * 255, +                                        MathUtil::Clamp(diffuse_sum.z, 0.0f, 1.0f) * 255, +                                        MathUtil::Clamp(diffuse_sum.w, 0.0f, 1.0f) * 255) +                       .Cast<u8>(); +    auto specular = Math::MakeVec<float>(MathUtil::Clamp(specular_sum.x, 0.0f, 1.0f) * 255, +                                         MathUtil::Clamp(specular_sum.y, 0.0f, 1.0f) * 255, +                                         MathUtil::Clamp(specular_sum.z, 0.0f, 1.0f) * 255, +                                         MathUtil::Clamp(specular_sum.w, 0.0f, 1.0f) * 255) +                        .Cast<u8>(); +    return {diffuse, specular}; +} + +} // namespace Pica diff --git a/src/video_core/swrasterizer/lighting.h b/src/video_core/swrasterizer/lighting.h new file mode 100644 index 000000000..438dca926 --- /dev/null +++ b/src/video_core/swrasterizer/lighting.h @@ -0,0 +1,18 @@ +// Copyright 2017 Citra Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once + +#include <tuple> +#include "common/quaternion.h" +#include "common/vector_math.h" +#include "video_core/pica_state.h" + +namespace Pica { + +std::tuple<Math::Vec4<u8>, Math::Vec4<u8>> ComputeFragmentsColors( +    const Pica::LightingRegs& lighting, const Pica::State::Lighting& lighting_state, +    const Math::Quaternion<float>& normquat, const Math::Vec3<float>& view); + +} // namespace Pica diff --git a/src/video_core/swrasterizer/rasterizer.cpp b/src/video_core/swrasterizer/rasterizer.cpp index 512e81c08..fdc1df199 100644 --- a/src/video_core/swrasterizer/rasterizer.cpp +++ b/src/video_core/swrasterizer/rasterizer.cpp @@ -13,6 +13,7 @@  #include "common/logging/log.h"  #include "common/math_util.h"  #include "common/microprofile.h" +#include "common/quaternion.h"  #include "common/vector_math.h"  #include "core/hw/gpu.h"  #include "core/memory.h" @@ -24,6 +25,7 @@  #include "video_core/regs_texturing.h"  #include "video_core/shader/shader.h"  #include "video_core/swrasterizer/framebuffer.h" +#include "video_core/swrasterizer/lighting.h"  #include "video_core/swrasterizer/proctex.h"  #include "video_core/swrasterizer/rasterizer.h"  #include "video_core/swrasterizer/texturing.h" @@ -419,6 +421,26 @@ static void ProcessTriangleInternal(const Vertex& v0, const Vertex& v1, const Ve                  regs.texturing.tev_combiner_buffer_color.a,              }; +            Math::Vec4<u8> primary_fragment_color = {0, 0, 0, 0}; +            Math::Vec4<u8> secondary_fragment_color = {0, 0, 0, 0}; + +            if (!g_state.regs.lighting.disable) { +                Math::Quaternion<float> normquat = Math::Quaternion<float>{ +                    {GetInterpolatedAttribute(v0.quat.x, v1.quat.x, v2.quat.x).ToFloat32(), +                     GetInterpolatedAttribute(v0.quat.y, v1.quat.y, v2.quat.y).ToFloat32(), +                     GetInterpolatedAttribute(v0.quat.z, v1.quat.z, v2.quat.z).ToFloat32()}, +                    GetInterpolatedAttribute(v0.quat.w, v1.quat.w, v2.quat.w).ToFloat32(), +                }.Normalized(); + +                Math::Vec3<float> view{ +                    GetInterpolatedAttribute(v0.view.x, v1.view.x, v2.view.x).ToFloat32(), +                    GetInterpolatedAttribute(v0.view.y, v1.view.y, v2.view.y).ToFloat32(), +                    GetInterpolatedAttribute(v0.view.z, v1.view.z, v2.view.z).ToFloat32(), +                }; +                std::tie(primary_fragment_color, secondary_fragment_color) = +                    ComputeFragmentsColors(g_state.regs.lighting, g_state.lighting, normquat, view); +            } +              for (unsigned tev_stage_index = 0; tev_stage_index < tev_stages.size();                   ++tev_stage_index) {                  const auto& tev_stage = tev_stages[tev_stage_index]; @@ -427,14 +449,13 @@ static void ProcessTriangleInternal(const Vertex& v0, const Vertex& v1, const Ve                  auto GetSource = [&](Source source) -> Math::Vec4<u8> {                      switch (source) {                      case Source::PrimaryColor: +                        return primary_color; -                    // HACK: Until we implement fragment lighting, use primary_color                      case Source::PrimaryFragmentColor: -                        return primary_color; +                        return primary_fragment_color; -                    // HACK: Until we implement fragment lighting, use zero                      case Source::SecondaryFragmentColor: -                        return {0, 0, 0, 0}; +                        return secondary_fragment_color;                      case Source::Texture0:                          return texture_color[0];  | 
