Fix normal transformations

- Keep track of transforms set by the title
- Transform normals by the inverse transpose of the position transform
- X_D3DTRANSFORMSTATETYPE enum
This commit is contained in:
Anthony 2020-12-07 21:40:37 +13:00
parent 3bfad0327f
commit 5720444c6b
8 changed files with 124 additions and 47 deletions

View File

@ -72,6 +72,7 @@ XboxRenderStateConverter XboxRenderStates;
XboxTextureStateConverter XboxTextureStates;
D3D8LightState d3d8LightState = D3D8LightState();
D3D8TransformState d3d8TransformState = D3D8TransformState();
FixedFunctionVertexShaderState ffShaderState = {0}; // TODO find a home for this and associated code
// Allow use of time duration literals (making 16ms, etc possible)
@ -306,9 +307,9 @@ g_EmuCDPD;
XB_MACRO(xbox::void_xt, WINAPI, D3DDevice_SetVertexShader_0, () ); \
XB_MACRO(xbox::void_xt, WINAPI, D3DDevice_SetVertexShaderInput, (xbox::dword_xt, xbox::uint_xt, xbox::X_STREAMINPUT*) ); \
XB_MACRO(xbox::void_xt, WINAPI, D3DDevice_SetViewport, (CONST xbox::X_D3DVIEWPORT8*) ); \
XB_MACRO(xbox::void_xt, WINAPI, D3DDevice_SetTransform, (D3DTRANSFORMSTATETYPE, CONST D3DMATRIX*) ); \
XB_MACRO(xbox::void_xt, WINAPI, D3DDevice_SetTransform, (xbox::X_D3DTRANSFORMSTATETYPE, CONST D3DMATRIX*) ); \
XB_MACRO(xbox::void_xt, WINAPI, D3DDevice_SetTransform_0, () ); \
XB_MACRO(xbox::void_xt, WINAPI, D3DDevice_MultiplyTransform, (D3DTRANSFORMSTATETYPE, CONST D3DMATRIX*) ); \
XB_MACRO(xbox::void_xt, WINAPI, D3DDevice_MultiplyTransform, (xbox::X_D3DTRANSFORMSTATETYPE, CONST D3DMATRIX*) ); \
XB_MACRO(xbox::void_xt, WINAPI, D3D_DestroyResource, (xbox::X_D3DResource*) ); \
XB_MACRO(xbox::void_xt, WINAPI, D3D_DestroyResource__LTCG, (xbox::void_xt) ); \
XB_MACRO(xbox::hresult_xt, WINAPI, Direct3D_CreateDevice, (xbox::uint_xt, D3DDEVTYPE, HWND, xbox::dword_xt, xbox::X_D3DPRESENT_PARAMETERS*, xbox::X_D3DDevice**)); \
@ -6336,6 +6337,17 @@ void UpdateFixedFunctionVertexShaderState()
{
using namespace xbox;
// Transforms
// Transpose row major to column major for HLSL
D3DXMatrixTranspose((D3DXMATRIX*)&ffShaderState.Transforms.Projection, (D3DXMATRIX*)&d3d8TransformState.Transforms[X_D3DTS_PROJECTION]);
D3DXMatrixTranspose((D3DXMATRIX*)&ffShaderState.Transforms.View, (D3DXMATRIX*)&d3d8TransformState.Transforms[X_D3DTS_VIEW]);
for (int i = 0; i < 4; i++) {
D3DXMatrixTranspose((D3DXMATRIX*)&ffShaderState.Transforms.Texture[i], (D3DXMATRIX*)&d3d8TransformState.Transforms[X_D3DTS_TEXTURE0 + i]);
D3DXMatrixTranspose((D3DXMATRIX*)&ffShaderState.Transforms.WorldView[i], (D3DXMATRIX*)&d3d8TransformState.WorldView[i]);
D3DXMatrixTranspose((D3DXMATRIX*)&ffShaderState.Transforms.WorldViewInverseTranspose[i], (D3DXMATRIX*)&d3d8TransformState.WorldViewInverseTranspose[i]);
}
// Lighting
ffShaderState.Modes.Lighting = (float)XboxRenderStates.GetXboxRenderState(X_D3DRS_LIGHTING);
ffShaderState.Modes.TwoSidedLighting = (float)XboxRenderStates.GetXboxRenderState(X_D3DRS_TWOSIDEDLIGHTING);
@ -6576,17 +6588,13 @@ xbox::void_xt __fastcall xbox::EMUPATCH(D3DDevice_SetRenderState_Simple)
void CxbxImpl_SetTransform
(
D3DTRANSFORMSTATETYPE State,
xbox::X_D3DTRANSFORMSTATETYPE State,
CONST D3DMATRIX *pMatrix
)
{
LOG_INIT
// Transpose row major to column major for HLSL
D3DXMATRIX hlslMatrix;
D3DXMatrixTranspose(&hlslMatrix, (D3DXMATRIX*)pMatrix);
// Save to vertex shader state
((D3DXMATRIX*)&ffShaderState.Transforms)[State] = hlslMatrix;
d3d8TransformState.SetTransform(State, pMatrix);
auto d3d9State = EmuXB2PC_D3DTS(State);
@ -6605,7 +6613,7 @@ static thread_local uint32_t setTransformCount = 0;
// so we cheat a bit by stashing the function body in a separate function
static void D3DDevice_SetTransform_0
(
D3DTRANSFORMSTATETYPE State,
xbox::X_D3DTRANSFORMSTATETYPE State,
CONST D3DMATRIX *pMatrix
)
{
@ -6633,7 +6641,7 @@ __declspec(naked) xbox::void_xt WINAPI xbox::EMUPATCH(D3DDevice_SetTransform_0)
(
)
{
D3DTRANSFORMSTATETYPE State;
xbox::X_D3DTRANSFORMSTATETYPE State;
CONST D3DMATRIX *pMatrix;
__asm {
LTCG_PROLOGUE
@ -6655,7 +6663,7 @@ __declspec(naked) xbox::void_xt WINAPI xbox::EMUPATCH(D3DDevice_SetTransform_0)
// ******************************************************************
xbox::void_xt WINAPI xbox::EMUPATCH(D3DDevice_SetTransform)
(
D3DTRANSFORMSTATETYPE State,
xbox::X_D3DTRANSFORMSTATETYPE State,
CONST D3DMATRIX *pMatrix
)
{
@ -6679,7 +6687,7 @@ xbox::void_xt WINAPI xbox::EMUPATCH(D3DDevice_SetTransform)
// ******************************************************************
xbox::void_xt WINAPI xbox::EMUPATCH(D3DDevice_MultiplyTransform)
(
D3DTRANSFORMSTATETYPE State,
xbox::X_D3DTRANSFORMSTATETYPE State,
CONST D3DMATRIX *pMatrix
)
{
@ -6693,9 +6701,9 @@ xbox::void_xt WINAPI xbox::EMUPATCH(D3DDevice_MultiplyTransform)
// Trampoline to guest code to remove the need for a GetTransform patch
XB_TRMP(D3DDevice_MultiplyTransform)(State, pMatrix);
State = EmuXB2PC_D3DTS(State);
auto pcState = EmuXB2PC_D3DTS(State);
HRESULT hRet = g_pD3DDevice->MultiplyTransform(State, pMatrix);
HRESULT hRet = g_pD3DDevice->MultiplyTransform(pcState, pMatrix);
DEBUG_D3DRESULT(hRet, "g_pD3DDevice->MultiplyTransform");
}

View File

@ -1306,7 +1306,7 @@ xbox::void_xt WINAPI EMUPATCH(D3DDevice_SetRenderState_ShadowFunc)
// ******************************************************************
xbox::void_xt WINAPI EMUPATCH(D3DDevice_SetTransform)
(
D3DTRANSFORMSTATETYPE State,
xbox::X_D3DTRANSFORMSTATETYPE State,
CONST D3DMATRIX *pMatrix
);
@ -1317,7 +1317,7 @@ xbox::void_xt WINAPI EMUPATCH(D3DDevice_SetTransform_0)();
// ******************************************************************
xbox::void_xt WINAPI EMUPATCH(D3DDevice_MultiplyTransform)
(
D3DTRANSFORMSTATETYPE State,
xbox::X_D3DTRANSFORMSTATETYPE State,
CONST D3DMATRIX *pMatrix
);

View File

@ -91,9 +91,8 @@ struct TransformInfo
float3 Normal;
};
static TransformInfo World; // Vertex worldspace transform
static TransformInfo View; // Vertex viewspace/cameraspace transform
static TransformInfo Projection; // Vertex projection transform
static TransformInfo View; // Vertex transformed to viewspace
static TransformInfo Projection; // Vertex transformed to projection space
// Vertex lighting
// Both frontface and backface lighting can be calculated
@ -285,7 +284,7 @@ LightingOutput CalcLighting(const float2 powers)
return totalLightOutput;
}
TransformInfo DoWorldTransform(const float4 position, const float3 normal, const float4 blendWeights)
TransformInfo DoTransform(const float4 position, const float3 normal, const float4 blendWeights)
{
TransformInfo output;
output.Position = float4(0, 0, 0, 0);
@ -302,8 +301,8 @@ TransformInfo DoWorldTransform(const float4 position, const float3 normal, const
const int _4WEIGHT_4MAT = 6;
if (state.Modes.VertexBlend == _BLEND_OFF) {
output.Position = mul(position, state.Transforms.World[0]);
output.Normal = mul(normal, (float3x3)state.Transforms.World[0]);
output.Position = mul(position, state.Transforms.WorldView[0]);
output.Normal = mul(normal, (float3x3)state.Transforms.WorldViewInverseTranspose[0]);
return output;
}
@ -315,20 +314,20 @@ TransformInfo DoWorldTransform(const float4 position, const float3 normal, const
float lastBlend = 1;
for (int i = 0; i < mats - 1; i++)
{
output.Position += mul(position, state.Transforms.World[i]) * blendWeights[i];
output.Normal += mul(normal, (float3x3) state.Transforms.World[i]) * blendWeights[i];
output.Position += mul(position, state.Transforms.WorldView[i]) * blendWeights[i];
output.Normal += mul(normal, (float3x3) state.Transforms.WorldViewInverseTranspose[i]) * blendWeights[i];
lastBlend -= blendWeights[i];
}
if (calcLastBlend)
{
output.Position += mul(position, state.Transforms.World[mats-1]) * lastBlend;
output.Normal += mul(normal, (float3x3) state.Transforms.World[mats-1]) * lastBlend;
output.Position += mul(position, state.Transforms.WorldView[mats-1]) * lastBlend;
output.Normal += mul(normal, (float3x3) state.Transforms.WorldViewInverseTranspose[mats-1]) * lastBlend;
}
else
{
output.Position += mul(position, state.Transforms.World[mats-1]) * blendWeights[mats-1];
output.Normal += mul(normal, (float3x3) state.Transforms.World[mats-1]) * blendWeights[mats-1];
output.Position += mul(position, state.Transforms.WorldView[mats-1]) * blendWeights[mats-1];
output.Normal += mul(normal, (float3x3) state.Transforms.WorldViewInverseTranspose[mats-1]) * blendWeights[mats-1];
}
return output;
@ -549,12 +548,8 @@ VS_OUTPUT main(const VS_INPUT xInput)
// Map default values
VS_INPUT xIn = InitializeInputRegisters(xInput);
// World transform with vertex blending
World = DoWorldTransform(Get(xIn, position), Get(xIn, normal).xyz, Get(xIn, weight));
// View transform
View.Position = mul(World.Position, state.Transforms.View);
View.Normal = mul(World.Normal, (float3x3) state.Transforms.View);
// World + View transform with vertex blending
View = DoTransform(Get(xIn, position), Get(xIn, normal).xyz, Get(xIn, weight));
// Optionally normalize camera-space normals
if (state.Modes.NormalizeNormals)

View File

@ -42,7 +42,10 @@ struct Transforms {
float4x4 View; // 0
float4x4 Projection; // 1
arr(Texture, float4x4, 4); // 2, 3, 4, 5
arr(World, float4x4, 4); // 6, 7, 8, 9
// World matrices are 6, 7, 8, 9
// But we use combined WorldView matrices in the shader
arr(WorldView, float4x4, 4);
arr(WorldViewInverseTranspose, float4x4, 4);
};
// See D3DLIGHT

View File

@ -2,6 +2,7 @@
#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();
@ -85,3 +86,43 @@ void D3D8LightState::EnableLight(uint32_t index, bool enable) {
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);
}
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;
// Recalculate dependent matrices
for (int i = 0; i < 4; i++) {
auto worldState = X_D3DTS_WORLD + i;
if (state == X_D3DTS_VIEW || state == worldState) {
D3DXMATRIX worldView;
D3DXMatrixMultiply(&worldView, (D3DXMATRIX*)&Transforms[worldState], (D3DXMATRIX*)&Transforms[X_D3DTS_VIEW]);
this->WorldView[i] = worldView;
D3DXMATRIX worldViewInverseTranspose;
D3DXMatrixInverse(&worldViewInverseTranspose, nullptr, &worldView);
D3DXMatrixTranspose(&worldViewInverseTranspose, &worldViewInverseTranspose);
this->WorldViewInverseTranspose[i] = worldViewInverseTranspose;
}
}
}

View File

@ -23,4 +23,18 @@ public:
void EnableLight(uint32_t index, bool enable);
};
class D3D8TransformState {
public:
D3D8TransformState();
void SetTransform(xbox::X_D3DTRANSFORMSTATETYPE state, const D3DMATRIX* pMatrix);
// The transforms set by the Xbox title
std::array<D3DMATRIX, xbox::X_D3DTS_MAX> Transforms;
// Combines world/view matrices
std::array<D3DMATRIX, 4> WorldView;
// World/view inverse transpose for lighting calculations
std::array<D3DMATRIX, 4> WorldViewInverseTranspose;
};
#endif

View File

@ -94,20 +94,20 @@ else
//*/
// convert from xbox to pc texture transform state types
inline D3DTRANSFORMSTATETYPE EmuXB2PC_D3DTS(D3DTRANSFORMSTATETYPE State)
inline D3DTRANSFORMSTATETYPE EmuXB2PC_D3DTS(xbox::X_D3DTRANSFORMSTATETYPE State)
{
// Handle Xbox -> D3D State mapping
switch (State) {
case 0: return (D3DTRANSFORMSTATETYPE)D3DTS_VIEW;
case 1: return (D3DTRANSFORMSTATETYPE)D3DTS_PROJECTION;
case 2: return (D3DTRANSFORMSTATETYPE)D3DTS_TEXTURE0;
case 3: return (D3DTRANSFORMSTATETYPE)D3DTS_TEXTURE1;
case 4: return (D3DTRANSFORMSTATETYPE)D3DTS_TEXTURE2;
case 5: return (D3DTRANSFORMSTATETYPE)D3DTS_TEXTURE3;
case 6: return (D3DTRANSFORMSTATETYPE)D3DTS_WORLD;
case 7: return (D3DTRANSFORMSTATETYPE)D3DTS_WORLD1;
case 8: return (D3DTRANSFORMSTATETYPE)D3DTS_WORLD2;
case 9: return (D3DTRANSFORMSTATETYPE)D3DTS_WORLD3;
case xbox::X_D3DTS_VIEW: return D3DTS_VIEW;
case xbox::X_D3DTS_PROJECTION: return D3DTS_PROJECTION;
case xbox::X_D3DTS_TEXTURE0: return D3DTS_TEXTURE0;
case xbox::X_D3DTS_TEXTURE1: return D3DTS_TEXTURE1;
case xbox::X_D3DTS_TEXTURE2: return D3DTS_TEXTURE2;
case xbox::X_D3DTS_TEXTURE3: return D3DTS_TEXTURE3;
case xbox::X_D3DTS_WORLD: return D3DTS_WORLD;
case xbox::X_D3DTS_WORLD1: return D3DTS_WORLD1;
case xbox::X_D3DTS_WORLD2: return D3DTS_WORLD2;
case xbox::X_D3DTS_WORLD3: return D3DTS_WORLD3;
}
// Handle World Matrix offsets

View File

@ -1305,6 +1305,22 @@ typedef enum _X_D3DVSD_TOKENTYPE
#define X_D3DTSS_TCI_OBJECT 0x00040000 // Warning! Collides with host Direct3D 9 D3DTSS_TCI_SPHEREMAP
#define X_D3DTSS_TCI_SPHEREMAP 0x00050000
enum X_D3DTRANSFORMSTATETYPE {
X_D3DTS_VIEW = 0,
X_D3DTS_PROJECTION = 1,
X_D3DTS_TEXTURE0 = 2,
X_D3DTS_TEXTURE1 = 3,
X_D3DTS_TEXTURE2 = 4,
X_D3DTS_TEXTURE3 = 5,
X_D3DTS_WORLD = 6,
X_D3DTS_WORLD1 = 7,
X_D3DTS_WORLD2 = 8,
X_D3DTS_WORLD3 = 9,
X_D3DTS_MAX = 10,
X_D3DTS_FORCE_DWORD = 0x7FFFFFFF,
};
typedef DWORD NV2AMETHOD;
//