158 lines
4.6 KiB
C++
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];
|
|
}
|