Cxbx-Reloaded/src/core/hle/D3D8/FixedFunctionState.cpp

158 lines
4.6 KiB
C++

#define LOG_PREFIX CXBXR_MODULE::D3D8
#include "FixedFunctionState.h"
#include "Logging.h"
#include "core/kernel/init/CxbxKrnl.h"
D3DCOLORVALUE colorValue(float r, float g, float b, float a) {
auto value = D3DCOLORVALUE();
value.r = r;
value.g = g;
value.b = b;
value.a = a;
return value;
}
D3DVECTOR toVector(float x, float y, float z) {
auto value = D3DVECTOR();
value.x = x;
value.y = y;
value.z = z;
return value;
}
D3D8LightState::D3D8LightState() {
// Define the default light
// When unset lights are enabled, they're set to the default light
auto defaultLight = xbox::X_D3DLIGHT8();
defaultLight.Type = D3DLIGHT_DIRECTIONAL;
defaultLight.Diffuse = colorValue(1, 1, 1, 0);
defaultLight.Specular = colorValue(0, 0, 0, 0);
defaultLight.Ambient = colorValue(0, 0, 0, 0);
defaultLight.Position = toVector(0, 0, 0);
defaultLight.Direction = toVector(0, 0, 1);
defaultLight.Range = 0;
defaultLight.Falloff = 0;
defaultLight.Attenuation0 = 0;
defaultLight.Attenuation1 = 0;
defaultLight.Attenuation2 = 0;
defaultLight.Theta = 0;
defaultLight.Phi = 0;
// We'll just preset every light to the default light
Lights.fill(defaultLight);
EnabledLights.fill(-1);
}
void D3D8LightState::EnableLight(uint32_t index, bool enable) {
// Since Xbox only supports 8 lights, we keep track of the 8 most recently enabled lights
// Lights are ordered oldest to newest, with disabled lights at the end
// Check to see if the light is already enabled
for (size_t i = 0; i < EnabledLightCount; i++) {
// If the light is already in the enabled lights
if (EnabledLights[i] == index) {
// Either way we move this light to the end
std::rotate(std::begin(EnabledLights) + i, std::begin(EnabledLights) + i + 1, std::begin(EnabledLights) + EnabledLightCount);
if (enable) {
// Don't need to do anything
EmuLog(LOG_LEVEL::INFO, "Enabled light %d but it was already enabled", index);
}
else {
// Disable the light
EnabledLights[EnabledLightCount - 1] = -1;
EnabledLightCount--;
}
return;
}
}
if (enable) {
// The light was not in the enabled lights. Let's add it
if (EnabledLightCount < EnabledLights.size()) {
EnabledLights[EnabledLightCount] = index; // add it to the end
EnabledLightCount++;
}
else {
// Replace the oldest element and move to end
EmuLog(LOG_LEVEL::INFO, "Can't enable any more lights. Replacing the oldest light %i", EnabledLights[0]);
EnabledLights[0] = index;
std::rotate(std::begin(EnabledLights), std::begin(EnabledLights) + 1, std::end(EnabledLights));
}
}
else {
EmuLog(LOG_LEVEL::INFO, "Could not disable light %d because it wasn't enabled", index);
}
}
D3D8TransformState::D3D8TransformState() {
D3DMATRIX identity;
D3DXMatrixIdentity((D3DXMATRIX*)&identity);
this->Transforms.fill(identity);
this->WorldView.fill(identity);
this->WorldViewInverseTranspose.fill(identity);
bWorldViewDirty.fill(true);
}
void D3D8TransformState::SetTransform(xbox::X_D3DTRANSFORMSTATETYPE state, const D3DMATRIX* pMatrix)
{
using namespace xbox;
LOG_INIT
if (state >= this->Transforms.size()) {
LOG_TEST_CASE("Transform state was not in expected range");
return;
}
// Update transform state
this->Transforms[state] = *pMatrix;
if (state == X_D3DTS_VIEW) {
bWorldViewDirty.fill(true);
}
if ((X_D3DTS_WORLD <= state) && (state <= X_D3DTS_WORLD3)) {
bWorldViewDirty[state - X_D3DTS_WORLD] = true;
}
}
void D3D8TransformState::RecalculateDependentMatrices(unsigned i)
{
auto worldState = xbox::X_D3DTS_WORLD + i;
D3DXMATRIX worldView;
D3DXMatrixMultiply(&worldView, (D3DXMATRIX*)&Transforms[worldState], (D3DXMATRIX*)&Transforms[xbox::X_D3DTS_VIEW]);
this->WorldView[i] = worldView;
D3DXMATRIX worldViewInverseTranspose;
D3DXMatrixInverse(&worldViewInverseTranspose, nullptr, &worldView);
D3DXMatrixTranspose(&worldViewInverseTranspose, &worldViewInverseTranspose);
this->WorldViewInverseTranspose[i] = worldViewInverseTranspose;
}
D3DMATRIX* D3D8TransformState::GetWorldView(unsigned i)
{
assert(i < 4);
if (bWorldViewDirty[i]) {
RecalculateDependentMatrices(i);
bWorldViewDirty[i] = false;
}
return &WorldView[i];
}
D3DMATRIX* D3D8TransformState::GetWorldViewInverseTranspose(unsigned i)
{
assert(i < 4);
if (bWorldViewDirty[i]) {
RecalculateDependentMatrices(i);
bWorldViewDirty[i] = false;
}
return &WorldViewInverseTranspose[i];
}