citra/src/video_core/regs_lighting.h
FearlessTobi 20139141f7 video_core: Remove unnecessary enum class casting in logging messages
fmt now automatically prints the numeric value of an enum class member by default, so we don't need to use casts any more.

Reduces the line noise in our code a bit.

Co-Authored-By: LC <712067+lioncash@users.noreply.github.com>
2020-12-28 16:50:23 +01:00

325 lines
12 KiB
C++

// Copyright 2017 Citra Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#pragma once
#include <array>
#include "common/assert.h"
#include "common/bit_field.h"
#include "common/common_funcs.h"
#include "common/common_types.h"
#include "common/vector_math.h"
namespace Pica {
struct LightingRegs {
enum class LightingSampler {
Distribution0 = 0,
Distribution1 = 1,
Fresnel = 3,
ReflectBlue = 4,
ReflectGreen = 5,
ReflectRed = 6,
SpotlightAttenuation = 8,
DistanceAttenuation = 16,
};
static constexpr unsigned NumLightingSampler = 24;
static LightingSampler SpotlightAttenuationSampler(unsigned index) {
return static_cast<LightingSampler>(
static_cast<unsigned>(LightingSampler::SpotlightAttenuation) + index);
}
static LightingSampler DistanceAttenuationSampler(unsigned index) {
return static_cast<LightingSampler>(
static_cast<unsigned>(LightingSampler::DistanceAttenuation) + index);
}
/**
* Pica fragment lighting supports using different LUTs for each lighting component: Reflectance
* R, G, and B channels, distribution function for specular components 0 and 1, fresnel factor,
* and spotlight attenuation. Furthermore, which LUTs are used for each channel (or whether a
* channel is enabled at all) is specified by various pre-defined lighting configurations. With
* configurations that require more LUTs, more cycles are required on HW to perform lighting
* computations.
*/
enum class LightingConfig : u32 {
Config0 = 0, ///< Reflect Red, Distribution 0, Spotlight
Config1 = 1, ///< Reflect Red, Fresnel, Spotlight
Config2 = 2, ///< Reflect Red, Distribution 0/1
Config3 = 3, ///< Distribution 0/1, Fresnel
Config4 = 4, ///< Reflect Red/Green/Blue, Distribution 0/1, Spotlight
Config5 = 5, ///< Reflect Red/Green/Blue, Distribution 0, Fresnel, Spotlight
Config6 = 6, ///< Reflect Red, Distribution 0/1, Fresnel, Spotlight
Config7 = 8, ///< Reflect Red/Green/Blue, Distribution 0/1, Fresnel, Spotlight
///< NOTE: '8' is intentional, '7' does not appear to be a valid configuration
};
/// Factor used to scale the output of a lighting LUT
enum class LightingScale : u32 {
Scale1 = 0, ///< Scale is 1x
Scale2 = 1, ///< Scale is 2x
Scale4 = 2, ///< Scale is 4x
Scale8 = 3, ///< Scale is 8x
Scale1_4 = 6, ///< Scale is 0.25x
Scale1_2 = 7, ///< Scale is 0.5x
};
enum class LightingLutInput : u32 {
NH = 0, // Cosine of the angle between the normal and half-angle vectors
VH = 1, // Cosine of the angle between the view and half-angle vectors
NV = 2, // Cosine of the angle between the normal and the view vector
LN = 3, // Cosine of the angle between the light and the normal vectors
SP = 4, // Cosine of the angle between the light and the inverse spotlight vectors
CP = 5, // Cosine of the angle between the tangent and projection of half-angle vectors
};
enum class LightingBumpMode : u32 {
None = 0,
NormalMap = 1,
TangentMap = 2,
};
union LightColor {
BitField<0, 10, u32> b;
BitField<10, 10, u32> g;
BitField<20, 10, u32> r;
Common::Vec3f ToVec3f() const {
// These fields are 10 bits wide, however 255 corresponds to 1.0f for each color
// component
return Common::MakeVec((f32)r / 255.f, (f32)g / 255.f, (f32)b / 255.f);
}
};
/// Returns true if the specified lighting sampler is supported by the current Pica lighting
/// configuration
static bool IsLightingSamplerSupported(LightingConfig config, LightingSampler sampler) {
switch (sampler) {
case LightingSampler::Distribution0:
return (config != LightingConfig::Config1);
case LightingSampler::Distribution1:
return (config != LightingConfig::Config0) && (config != LightingConfig::Config1) &&
(config != LightingConfig::Config5);
case LightingSampler::SpotlightAttenuation:
return (config != LightingConfig::Config2) && (config != LightingConfig::Config3);
case LightingSampler::Fresnel:
return (config != LightingConfig::Config0) && (config != LightingConfig::Config2) &&
(config != LightingConfig::Config4);
case LightingSampler::ReflectRed:
return (config != LightingConfig::Config3);
case LightingSampler::ReflectGreen:
case LightingSampler::ReflectBlue:
return (config == LightingConfig::Config4) || (config == LightingConfig::Config5) ||
(config == LightingConfig::Config7);
default:
UNREACHABLE_MSG("Regs::IsLightingSamplerSupported: Reached unreachable section, "
"sampler should be one of Distribution0, Distribution1, "
"SpotlightAttenuation, Fresnel, ReflectRed, ReflectGreen or "
"ReflectBlue, instead got %i",
config);
}
}
struct LightSrc {
LightColor specular_0; // material.specular_0 * light.specular_0
LightColor specular_1; // material.specular_1 * light.specular_1
LightColor diffuse; // material.diffuse * light.diffuse
LightColor ambient; // material.ambient * light.ambient
// Encoded as 16-bit floating point
union {
BitField<0, 16, u32> x;
BitField<16, 16, u32> y;
};
union {
BitField<0, 16, u32> z;
};
// inverse spotlight direction vector, encoded as fixed1.1.11
union {
BitField<0, 13, s32> spot_x;
BitField<16, 13, s32> spot_y;
};
union {
BitField<0, 13, s32> spot_z;
};
INSERT_PADDING_WORDS(0x1);
union {
BitField<0, 1, u32> directional;
BitField<1, 1, u32> two_sided_diffuse; // When disabled, clamp dot-product to 0
BitField<2, 1, u32> geometric_factor_0;
BitField<3, 1, u32> geometric_factor_1;
} config;
BitField<0, 20, u32> dist_atten_bias;
BitField<0, 20, u32> dist_atten_scale;
INSERT_PADDING_WORDS(0x4);
};
static_assert(sizeof(LightSrc) == 0x10 * sizeof(u32), "LightSrc structure must be 0x10 words");
LightSrc light[8];
LightColor global_ambient; // Emission + (material.ambient * lighting.ambient)
INSERT_PADDING_WORDS(0x1);
BitField<0, 3, u32> max_light_index; // Number of enabled lights - 1
union {
BitField<0, 1, u32> enable_shadow;
BitField<2, 1, u32> enable_primary_alpha;
BitField<3, 1, u32> enable_secondary_alpha;
BitField<4, 4, LightingConfig> config;
BitField<16, 1, u32> shadow_primary;
BitField<17, 1, u32> shadow_secondary;
BitField<18, 1, u32> shadow_invert;
BitField<19, 1, u32> shadow_alpha;
BitField<22, 2, u32> bump_selector; // 0: Texture 0, 1: Texture 1, 2: Texture 2
BitField<24, 2, u32> shadow_selector;
BitField<27, 1, u32> clamp_highlights;
BitField<28, 2, LightingBumpMode> bump_mode;
BitField<30, 1, u32> disable_bump_renorm;
} config0;
union {
u32 raw;
// Each bit specifies whether shadow should be applied for the corresponding light.
BitField<0, 8, u32> disable_shadow;
// Each bit specifies whether spot light attenuation should be applied for the corresponding
// light.
BitField<8, 8, u32> disable_spot_atten;
BitField<16, 1, u32> disable_lut_d0;
BitField<17, 1, u32> disable_lut_d1;
// Note: by intuition, BitField<18, 1, u32> should be disable_lut_sp, but it is actually a
// dummy bit which is always set as 1.
BitField<19, 1, u32> disable_lut_fr;
BitField<20, 1, u32> disable_lut_rr;
BitField<21, 1, u32> disable_lut_rg;
BitField<22, 1, u32> disable_lut_rb;
// Each bit specifies whether distance attenuation should be applied for the corresponding
// light.
BitField<24, 8, u32> disable_dist_atten;
} config1;
bool IsDistAttenDisabled(unsigned index) const {
return (config1.disable_dist_atten & (1 << index)) != 0;
}
bool IsSpotAttenDisabled(unsigned index) const {
return (config1.disable_spot_atten & (1 << index)) != 0;
}
bool IsShadowDisabled(unsigned index) const {
return (config1.disable_shadow & (1 << index)) != 0;
}
union {
BitField<0, 8, u32> index; ///< Index at which to set data in the LUT
BitField<8, 5, u32> type; ///< Type of LUT for which to set data
} lut_config;
BitField<0, 1, u32> disable;
INSERT_PADDING_WORDS(0x1);
// When data is written to any of these registers, it gets written to the lookup table of the
// selected type at the selected index, specified above in the `lut_config` register. With each
// write, `lut_config.index` is incremented. It does not matter which of these registers is
// written to, the behavior will be the same.
u32 lut_data[8];
// These are used to specify if absolute (abs) value should be used for each LUT index. When
// abs mode is disabled, LUT indexes are in the range of (-1.0, 1.0). Otherwise, they are in
// the range of (0.0, 1.0).
union {
BitField<1, 1, u32> disable_d0;
BitField<5, 1, u32> disable_d1;
BitField<9, 1, u32> disable_sp;
BitField<13, 1, u32> disable_fr;
BitField<17, 1, u32> disable_rb;
BitField<21, 1, u32> disable_rg;
BitField<25, 1, u32> disable_rr;
} abs_lut_input;
union {
BitField<0, 3, LightingLutInput> d0;
BitField<4, 3, LightingLutInput> d1;
BitField<8, 3, LightingLutInput> sp;
BitField<12, 3, LightingLutInput> fr;
BitField<16, 3, LightingLutInput> rb;
BitField<20, 3, LightingLutInput> rg;
BitField<24, 3, LightingLutInput> rr;
} lut_input;
union {
BitField<0, 3, LightingScale> d0;
BitField<4, 3, LightingScale> d1;
BitField<8, 3, LightingScale> sp;
BitField<12, 3, LightingScale> fr;
BitField<16, 3, LightingScale> rb;
BitField<20, 3, LightingScale> rg;
BitField<24, 3, LightingScale> rr;
static float GetScale(LightingScale scale) {
switch (scale) {
case LightingScale::Scale1:
return 1.0f;
case LightingScale::Scale2:
return 2.0f;
case LightingScale::Scale4:
return 4.0f;
case LightingScale::Scale8:
return 8.0f;
case LightingScale::Scale1_4:
return 0.25f;
case LightingScale::Scale1_2:
return 0.5f;
}
return 0.0f;
}
} lut_scale;
INSERT_PADDING_WORDS(0x6);
union {
// There are 8 light enable "slots", corresponding to the total number of lights supported
// by Pica. For N enabled lights (specified by register 0x1c2, or 'src_num' above), the
// first N slots below will be set to integers within the range of 0-7, corresponding to the
// actual light that is enabled for each slot.
BitField<0, 3, u32> slot_0;
BitField<4, 3, u32> slot_1;
BitField<8, 3, u32> slot_2;
BitField<12, 3, u32> slot_3;
BitField<16, 3, u32> slot_4;
BitField<20, 3, u32> slot_5;
BitField<24, 3, u32> slot_6;
BitField<28, 3, u32> slot_7;
unsigned GetNum(unsigned index) const {
const unsigned enable_slots[] = {slot_0, slot_1, slot_2, slot_3,
slot_4, slot_5, slot_6, slot_7};
return enable_slots[index];
}
} light_enable;
INSERT_PADDING_WORDS(0x26);
};
static_assert(sizeof(LightingRegs) == 0xC0 * sizeof(u32), "LightingRegs struct has incorrect size");
} // namespace Pica