From 57855a1701474c65b8dd95d0c312d02fae8fe1a6 Mon Sep 17 00:00:00 2001
From: Jannik Vogel <email@jannikvogel.de>
Date: Wed, 11 May 2016 13:39:28 +0200
Subject: [PATCH] Pica: Add fog state

---
 src/video_core/command_processor.cpp | 14 ++++++++
 src/video_core/pica.h                | 53 +++++++++++++++++++++-------
 src/video_core/pica_state.h          | 16 +++++++--
 3 files changed, 69 insertions(+), 14 deletions(-)

diff --git a/src/video_core/command_processor.cpp b/src/video_core/command_processor.cpp
index 19e03adf46..689859049d 100644
--- a/src/video_core/command_processor.cpp
+++ b/src/video_core/command_processor.cpp
@@ -423,6 +423,20 @@ static void WritePicaReg(u32 id, u32 value, u32 mask) {
             break;
         }
 
+        case PICA_REG_INDEX_WORKAROUND(fog_lut_data[0], 0xe8):
+        case PICA_REG_INDEX_WORKAROUND(fog_lut_data[1], 0xe9):
+        case PICA_REG_INDEX_WORKAROUND(fog_lut_data[2], 0xea):
+        case PICA_REG_INDEX_WORKAROUND(fog_lut_data[3], 0xeb):
+        case PICA_REG_INDEX_WORKAROUND(fog_lut_data[4], 0xec):
+        case PICA_REG_INDEX_WORKAROUND(fog_lut_data[5], 0xed):
+        case PICA_REG_INDEX_WORKAROUND(fog_lut_data[6], 0xee):
+        case PICA_REG_INDEX_WORKAROUND(fog_lut_data[7], 0xef):
+        {
+            g_state.fog.lut[regs.fog_lut_offset % 128].raw = value;
+            regs.fog_lut_offset.Assign(regs.fog_lut_offset + 1);
+            break;
+        }
+
         default:
             break;
     }
diff --git a/src/video_core/pica.h b/src/video_core/pica.h
index 544ea037f2..09702d46ad 100644
--- a/src/video_core/pica.h
+++ b/src/video_core/pica.h
@@ -401,22 +401,47 @@ struct Regs {
     TevStageConfig tev_stage3;
     INSERT_PADDING_WORDS(0x3);
 
+    enum class FogMode : u32 {
+        None = 0,
+        Fog  = 5,
+        Gas  = 7,
+    };
+
     union {
-        // Tev stages 0-3 write their output to the combiner buffer if the corresponding bit in
-        // these masks are set
-        BitField< 8, 4, u32> update_mask_rgb;
-        BitField<12, 4, u32> update_mask_a;
+        BitField<0, 3, FogMode> fog_mode;
+        BitField<16, 1, u32> fog_flip;
 
-        bool TevStageUpdatesCombinerBufferColor(unsigned stage_index) const {
-            return (stage_index < 4) && (update_mask_rgb & (1 << stage_index));
-        }
+        union {
+            // Tev stages 0-3 write their output to the combiner buffer if the corresponding bit in
+            // these masks are set
+            BitField< 8, 4, u32> update_mask_rgb;
+            BitField<12, 4, u32> update_mask_a;
 
-        bool TevStageUpdatesCombinerBufferAlpha(unsigned stage_index) const {
-            return (stage_index < 4) && (update_mask_a & (1 << stage_index));
-        }
-    } tev_combiner_buffer_input;
+            bool TevStageUpdatesCombinerBufferColor(unsigned stage_index) const {
+                return (stage_index < 4) && (update_mask_rgb & (1 << stage_index));
+            }
+
+            bool TevStageUpdatesCombinerBufferAlpha(unsigned stage_index) const {
+                return (stage_index < 4) && (update_mask_a & (1 << stage_index));
+            }
+        } tev_combiner_buffer_input;
+    };
+
+    union {
+        u32 raw;
+        BitField< 0, 8, u32> r;
+        BitField< 8, 8, u32> g;
+        BitField<16, 8, u32> b;
+    } fog_color;
+
+    INSERT_PADDING_WORDS(0x4);
+
+    BitField<0, 16, u32> fog_lut_offset;
+
+    INSERT_PADDING_WORDS(0x1);
+
+    u32 fog_lut_data[8];
 
-    INSERT_PADDING_WORDS(0xf);
     TevStageConfig tev_stage4;
     INSERT_PADDING_WORDS(0x3);
     TevStageConfig tev_stage5;
@@ -1318,6 +1343,10 @@ ASSERT_REG_POSITION(tev_stage1, 0xc8);
 ASSERT_REG_POSITION(tev_stage2, 0xd0);
 ASSERT_REG_POSITION(tev_stage3, 0xd8);
 ASSERT_REG_POSITION(tev_combiner_buffer_input, 0xe0);
+ASSERT_REG_POSITION(fog_mode, 0xe0);
+ASSERT_REG_POSITION(fog_color, 0xe1);
+ASSERT_REG_POSITION(fog_lut_offset, 0xe6);
+ASSERT_REG_POSITION(fog_lut_data, 0xe8);
 ASSERT_REG_POSITION(tev_stage4, 0xf0);
 ASSERT_REG_POSITION(tev_stage5, 0xf8);
 ASSERT_REG_POSITION(tev_combiner_buffer_color, 0xfd);
diff --git a/src/video_core/pica_state.h b/src/video_core/pica_state.h
index 495174c250..01f4285a88 100644
--- a/src/video_core/pica_state.h
+++ b/src/video_core/pica_state.h
@@ -33,10 +33,10 @@ struct State {
             u32 raw;
 
             // LUT value, encoded as 12-bit fixed point, with 12 fraction bits
-            BitField< 0, 12, u32> value;
+            BitField< 0, 12, u32> value; // 0.0.12 fixed point
 
             // Used by HW for efficient interpolation, Citra does not use these
-            BitField<12, 12, u32> difference;
+            BitField<12, 12, s32> difference; // 1.0.11 fixed point
 
             float ToFloat() {
                 return static_cast<float>(value) / 4095.f;
@@ -46,6 +46,18 @@ struct State {
         std::array<std::array<LutEntry, 256>, 24> luts;
     } lighting;
 
+    struct {
+        union LutEntry {
+            // Used for raw access
+            u32 raw;
+
+            BitField< 0, 13, s32> difference; // 1.1.11 fixed point
+            BitField<13, 11, u32> value; // 0.0.11 fixed point
+        };
+
+        std::array<LutEntry, 128> lut;
+    } fog;
+
     /// Current Pica command list
     struct {
         const u32* head_ptr;