Merge pull request #2014 from NZJenkins/experiment/fixedfunc3
HLSL fixed function implementation
This commit is contained in:
commit
f390c79bdf
|
@ -117,10 +117,14 @@ file (GLOB CXBXR_HEADER_EMU
|
|||
"${CXBXR_ROOT_DIR}/src/common/util/gloffscreen/gloffscreen.h"
|
||||
"${CXBXR_ROOT_DIR}/src/common/XADPCM.h"
|
||||
"${CXBXR_ROOT_DIR}/src/common/xbox/Logging.hpp"
|
||||
"${CXBXR_ROOT_DIR}/src/core/hle/D3D8/Direct3D9/CxbxVertexShaderTemplate.hlsl"
|
||||
"${CXBXR_ROOT_DIR}/src/core/hle/D3D8/Direct3D9/Direct3D9.h"
|
||||
"${CXBXR_ROOT_DIR}/src/core/hle/D3D8/Direct3D9/FixedFunctionVertexShader.hlsl"
|
||||
"${CXBXR_ROOT_DIR}/src/core/hle/D3D8/Direct3D9/FixedFunctionVertexShaderState.hlsli"
|
||||
"${CXBXR_ROOT_DIR}/src/core/hle/D3D8/Direct3D9/VertexShader.h"
|
||||
"${CXBXR_ROOT_DIR}/src/core/hle/D3D8/Direct3D9/VertexShaderSource.h"
|
||||
"${CXBXR_ROOT_DIR}/src/core/hle/D3D8/Direct3D9/WalkIndexBuffer.h"
|
||||
"${CXBXR_ROOT_DIR}/src/core/hle/D3D8/FixedFunctionState.h"
|
||||
"${CXBXR_ROOT_DIR}/src/core/hle/D3D8/ResourceTracker.h"
|
||||
"${CXBXR_ROOT_DIR}/src/core/hle/D3D8/XbConvert.h"
|
||||
"${CXBXR_ROOT_DIR}/src/core/hle/D3D8/XbD3D8Logging.h"
|
||||
|
@ -191,6 +195,11 @@ file (GLOB CXBXR_HEADER_EMU
|
|||
"${CXBXR_ROOT_DIR}/src/devices/Xbox.h"
|
||||
)
|
||||
|
||||
# filter hlsl files into its own list
|
||||
# excluding hlsli file(s)
|
||||
set(CXBXR_HEADER_HLSL ${CXBXR_HEADER_EMU})
|
||||
list(FILTER CXBXR_HEADER_HLSL INCLUDE REGEX ".*\\.hlsl$")
|
||||
|
||||
# Common (GUI and Emulator)
|
||||
file (GLOB CXBXR_SOURCE_COMMON
|
||||
"${CXBXR_ROOT_DIR}/src/common/crypto/EmuDes.cpp"
|
||||
|
@ -266,6 +275,7 @@ file (GLOB CXBXR_SOURCE_EMU
|
|||
"${CXBXR_ROOT_DIR}/src/core/hle/D3D8/Direct3D9/VertexShader.cpp"
|
||||
"${CXBXR_ROOT_DIR}/src/core/hle/D3D8/Direct3D9/VertexShaderSource.cpp"
|
||||
"${CXBXR_ROOT_DIR}/src/core/hle/D3D8/Direct3D9/WalkIndexBuffer.cpp"
|
||||
"${CXBXR_ROOT_DIR}/src/core/hle/D3D8/FixedFunctionState.cpp"
|
||||
"${CXBXR_ROOT_DIR}/src/core/hle/D3D8/ResourceTracker.cpp"
|
||||
"${CXBXR_ROOT_DIR}/src/core/hle/D3D8/XbConvert.cpp"
|
||||
"${CXBXR_ROOT_DIR}/src/core/hle/D3D8/XbD3D8Logging.cpp"
|
||||
|
@ -407,6 +417,12 @@ install(FILES ${cxbxr_INSTALL_files}
|
|||
DESTINATION bin
|
||||
)
|
||||
|
||||
install(FILES
|
||||
"${CMAKE_SOURCE_DIR}/src/core/hle/D3D8/Direct3D9/FixedFunctionVertexShaderState.hlsli"
|
||||
"${CMAKE_SOURCE_DIR}/src/core/hle/D3D8/Direct3D9/FixedFunctionVertexShader.hlsl"
|
||||
DESTINATION bin/hlsl
|
||||
)
|
||||
|
||||
set(cxbxr_GLEW_DLL "${CMAKE_SOURCE_DIR}/import/glew-2.0.0/bin/Release/Win32/glew32.dll")
|
||||
|
||||
install(PROGRAMS ${cxbxr_GLEW_DLL}
|
||||
|
|
|
@ -194,3 +194,12 @@ if(${CMAKE_GENERATOR} MATCHES "Visual Studio ([^9]|[9][0-9])")
|
|||
endif()
|
||||
|
||||
add_dependencies(cxbx cxbxr-ldr cxbxr-emu misc-batch)
|
||||
|
||||
# Try to stop cmake from building hlsl files
|
||||
# Which are all currently loaded at runtime only
|
||||
set_source_files_properties(
|
||||
${CXBXR_HEADER_HLSL}
|
||||
PROPERTIES
|
||||
HEADER_FILE_ONLY TRUE
|
||||
VS_TOOL_OVERRIDE "None"
|
||||
)
|
||||
|
|
|
@ -166,3 +166,12 @@ install(TARGETS ${PROJECT_NAME}
|
|||
)
|
||||
|
||||
add_dependencies(cxbxr-emu cxbxr-ldr misc-batch)
|
||||
|
||||
# Try to stop cmake from building hlsl files
|
||||
# Which are all currently loaded at runtime only
|
||||
set_source_files_properties(
|
||||
${CXBXR_HEADER_HLSL}
|
||||
PROPERTIES
|
||||
HEADER_FILE_ONLY TRUE
|
||||
VS_TOOL_OVERRIDE "None"
|
||||
)
|
||||
|
|
|
@ -27,3 +27,12 @@ message("Runtime Build Directory: ${TargetRunTimeDir}")
|
|||
# Copy glew32.dll to build type's folder.
|
||||
set(CXBXR_GLEW_DLL "${CMAKE_SOURCE_DIR}/import/glew-2.0.0/bin/Release/Win32/glew32.dll")
|
||||
file(COPY ${CXBXR_GLEW_DLL} DESTINATION ${TargetRunTimeDir})
|
||||
|
||||
# Copy certain HLSL files to the output directory, which we will load at runtime
|
||||
set(CXBXR_HLSL_FILES
|
||||
"${CMAKE_SOURCE_DIR}/src/core/hle/D3D8/Direct3D9/FixedFunctionVertexShaderState.hlsli"
|
||||
"${CMAKE_SOURCE_DIR}/src/core/hle/D3D8/Direct3D9/FixedFunctionVertexShader.hlsl"
|
||||
)
|
||||
set(HlslOutputDir ${TargetRunTimeDir}/hlsl)
|
||||
file(MAKE_DIRECTORY ${HlslOutputDir})
|
||||
file(COPY ${CXBXR_HLSL_FILES} DESTINATION ${HlslOutputDir})
|
||||
|
|
|
@ -37,6 +37,7 @@
|
|||
#include "core\kernel\init\CxbxKrnl.h"
|
||||
#include "core\kernel\support\Emu.h"
|
||||
#include "EmuShared.h"
|
||||
#include "..\FixedFunctionState.h"
|
||||
#include "core\hle\D3D8\ResourceTracker.h"
|
||||
#include "core\hle\D3D8\Direct3D9\Direct3D9.h" // For LPDIRECTDRAWSURFACE7
|
||||
#include "core\hle\D3D8\XbVertexBuffer.h"
|
||||
|
@ -70,6 +71,10 @@
|
|||
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)
|
||||
using namespace std::literals::chrono_literals;
|
||||
|
||||
|
@ -302,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**)); \
|
||||
|
@ -1892,6 +1897,10 @@ static LRESULT WINAPI EmuMsgProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lPar
|
|||
extern void DSound_PrintStats(); //TODO: move into plugin class usage.
|
||||
DSound_PrintStats();
|
||||
}
|
||||
else if (wParam == VK_F2)
|
||||
{
|
||||
g_UseFixedFunctionVertexShader = !g_UseFixedFunctionVertexShader;
|
||||
}
|
||||
else if (wParam == VK_F3)
|
||||
{
|
||||
g_bClipCursor = !g_bClipCursor;
|
||||
|
@ -2869,8 +2878,6 @@ void GetMultiSampleScaleRaw(float& xScale, float& yScale) {
|
|||
// Titles can render pre-transformed vertices to screen space (using XYZRHW vertex position data or otherwise)
|
||||
// so we need to know the space they are in to interpret it correctly
|
||||
void GetScreenScaleFactors(float& scaleX, float& scaleY) {
|
||||
extern bool g_Xbox_VertexShader_IsPassthrough;
|
||||
|
||||
// Example:
|
||||
// NFS HP2 renders in-game at 640*480
|
||||
// The title uses MSAA, which increases the rendertarget size, but leaves the screen scale unaffected
|
||||
|
@ -2900,7 +2907,7 @@ void GetScreenScaleFactors(float& scaleX, float& scaleY) {
|
|||
// - Antialias sample (background gradient)
|
||||
// Vertex program passthrough equivalent (title does apply backbuffer scale):
|
||||
// - NFS:HP2 (car speed and other in-game UI elements)
|
||||
if (!g_Xbox_VertexShader_IsPassthrough) {
|
||||
if (g_Xbox_VertexShaderMode != VertexShaderMode::Passthrough) {
|
||||
scaleX *= g_Xbox_BackbufferScaleX;
|
||||
scaleY *= g_Xbox_BackbufferScaleY;
|
||||
}
|
||||
|
@ -4153,8 +4160,6 @@ void GetXboxViewportOffsetAndScale(float (&vOffset)[4], float(&vScale)[4])
|
|||
|
||||
void CxbxUpdateHostViewPortOffsetAndScaleConstants()
|
||||
{
|
||||
extern bool g_Xbox_VertexShader_IsPassthrough;
|
||||
|
||||
float vScaleOffset[2][4]; // 0 - scale 1 - offset
|
||||
GetXboxViewportOffsetAndScale(vScaleOffset[1], vScaleOffset[0]);
|
||||
|
||||
|
@ -4180,7 +4185,7 @@ void CxbxUpdateHostViewPortOffsetAndScaleConstants()
|
|||
|
||||
// Passthrough should range 0 to 1, instead of 0 to zbuffer depth
|
||||
// Test case: DoA3 character select
|
||||
float zOutputScale = g_Xbox_VertexShader_IsPassthrough ? 1 : g_ZScale;
|
||||
float zOutputScale = g_Xbox_VertexShaderMode == VertexShaderMode::Passthrough ? 1 : g_ZScale;
|
||||
|
||||
float screenspaceScale[4] = { xboxScreenspaceWidth / 2, -xboxScreenspaceHeight / 2, zOutputScale, 1 };
|
||||
float screenspaceOffset[4] = { xboxScreenspaceWidth / 2 + aaOffsetX, xboxScreenspaceHeight / 2 + aaOffsetY, 0, 0 };
|
||||
|
@ -6252,6 +6257,220 @@ void CreateHostResource(xbox::X_D3DResource *pResource, DWORD D3DUsage, int iTex
|
|||
} // switch XboxResourceType
|
||||
}
|
||||
|
||||
D3DXVECTOR4 toVector(D3DCOLOR color) {
|
||||
D3DXVECTOR4 v;
|
||||
// ARGB to XYZW
|
||||
v.w = (color >> 24 & 0xFF) / 255.f;
|
||||
v.x = (color >> 16 & 0xFF) / 255.f;
|
||||
v.y = (color >> 8 & 0xFF) / 255.f;
|
||||
v.z = (color >> 0 & 0xFF) / 255.f;
|
||||
return v;
|
||||
}
|
||||
|
||||
D3DXVECTOR4 toVector(D3DCOLORVALUE val) {
|
||||
return D3DXVECTOR4(val.r, val.g, val.b, val.a);
|
||||
}
|
||||
|
||||
void UpdateFixedFunctionShaderLight(int d3dLightIndex, Light* pShaderLight, D3DXVECTOR4* pLightAmbient) {
|
||||
if (d3dLightIndex == -1) {
|
||||
pShaderLight->Type = 0; // Disable the light
|
||||
return;
|
||||
}
|
||||
|
||||
auto d3dLight = &d3d8LightState.Lights[d3dLightIndex];
|
||||
auto viewTransform = (D3DXMATRIX)d3d8TransformState.Transforms[xbox::X_D3DTS_VIEW];
|
||||
|
||||
// TODO remove D3DX usage
|
||||
// Pre-transform light position to viewspace
|
||||
D3DXVECTOR4 positionV;
|
||||
D3DXVec3Transform(&positionV, (D3DXVECTOR3*)&d3dLight->Position, &viewTransform);
|
||||
pShaderLight->PositionV = (D3DXVECTOR3)positionV;
|
||||
|
||||
// Pre-transform light direction to viewspace and normalize
|
||||
D3DXVECTOR4 directionV;
|
||||
D3DXMATRIX viewTransform3x3;
|
||||
D3DXMatrixIdentity(&viewTransform3x3);
|
||||
for (int y = 0; y < 3; y++) {
|
||||
for (int x = 0; x < 3; x++) {
|
||||
viewTransform3x3.m[x][y] = viewTransform.m[x][y];
|
||||
}
|
||||
}
|
||||
|
||||
D3DXVec3Transform(&directionV, (D3DXVECTOR3*)&d3dLight->Direction, &viewTransform3x3);
|
||||
D3DXVec3Normalize((D3DXVECTOR3*)&pShaderLight->DirectionVN, (D3DXVECTOR3*)&directionV);
|
||||
|
||||
bool SpecularEnable = XboxRenderStates.GetXboxRenderState(xbox::X_D3DRS_SPECULARENABLE) != FALSE;
|
||||
|
||||
// Map D3D light to state struct
|
||||
pShaderLight->Type = (float)((int)d3dLight->Type);
|
||||
pShaderLight->Diffuse = toVector(d3dLight->Diffuse);
|
||||
pShaderLight->Specular = SpecularEnable ? toVector(d3dLight->Specular) : toVector(0);
|
||||
pShaderLight->Range = d3dLight->Range;
|
||||
pShaderLight->Falloff = d3dLight->Falloff;
|
||||
pShaderLight->Attenuation.x = d3dLight->Attenuation0;
|
||||
pShaderLight->Attenuation.y = d3dLight->Attenuation1;
|
||||
pShaderLight->Attenuation.z = d3dLight->Attenuation2;
|
||||
|
||||
pLightAmbient->x += d3dLight->Ambient.r;
|
||||
pLightAmbient->y += d3dLight->Ambient.g;
|
||||
pLightAmbient->z += d3dLight->Ambient.b;
|
||||
|
||||
auto cosHalfPhi = cos(d3dLight->Phi / 2);
|
||||
pShaderLight->CosHalfPhi = cosHalfPhi;
|
||||
pShaderLight->SpotIntensityDivisor = cos(d3dLight->Theta / 2) - cos(d3dLight->Phi / 2);
|
||||
}
|
||||
|
||||
float AsFloat(uint32_t value) {
|
||||
auto v = value;
|
||||
return *(float*)&v;
|
||||
}
|
||||
|
||||
void UpdateFixedFunctionVertexShaderState()
|
||||
{
|
||||
using namespace xbox;
|
||||
|
||||
// Vertex blending
|
||||
// Prepare vertex blending mode variables used in transforms, below
|
||||
auto VertexBlend = XboxRenderStates.GetXboxRenderState(X_D3DRS_VERTEXBLEND);
|
||||
// Xbox and host D3DVERTEXBLENDFLAGS :
|
||||
// D3DVBF_DISABLE = 0 : 1 matrix, 0 weights => final weight 1
|
||||
// D3DVBF_1WEIGHTS = 1 : 2 matrices, 1 weights => final weight calculated
|
||||
// D3DVBF_2WEIGHTS = 3 : 3 matrices, 2 weights => final weight calculated
|
||||
// D3DVBF_3WEIGHTS = 5 : 4 matrices, 3 weights => final weight calculated
|
||||
// Xbox X_D3DVERTEXBLENDFLAGS :
|
||||
// X_D3DVBF_2WEIGHTS2MATRICES = 2 : 2 matrices, 2 weights
|
||||
// X_D3DVBF_3WEIGHTS3MATRICES = 4 : 3 matrices, 3 weights
|
||||
// X_D3DVBF_4WEIGHTS4MATRICES = 6 : 4 matrices, 4 weights
|
||||
//
|
||||
if (VertexBlend > xbox::X_D3DVBF_4WEIGHTS4MATRICES) LOG_TEST_CASE("X_D3DRS_VERTEXBLEND out of range");
|
||||
// Calculate the number of matrices, by adding the LSB to turn (0,1,3,5) and (0,2,4,6) into (0,2,4,6); Then divide by 2 to get (0,1,2,3), and add 1 to get 1, 2, 3 or 4 matrices :
|
||||
auto NrBlendMatrices = ((VertexBlend + (VertexBlend & 1)) / 2) + 1;
|
||||
// Looking at the above values, 0 or the LSB of VertexBlend signals that the final weight needs to be calculated from all previous weigths (deducting them all from an initial 1) :
|
||||
auto CalcLastBlendWeight = (VertexBlend == xbox::X_D3DVBF_DISABLE) || (VertexBlend & 1);
|
||||
// Copy the resulting values over to shader state :
|
||||
ffShaderState.Modes.VertexBlend_NrOfMatrices = (float)NrBlendMatrices;
|
||||
ffShaderState.Modes.VertexBlend_CalcLastWeight = (float)CalcLastBlendWeight;
|
||||
|
||||
// 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 (unsigned i = 0; i < 4; i++) { // TODO : Would it help to limit this to just the active texture channels?
|
||||
D3DXMatrixTranspose((D3DXMATRIX*)&ffShaderState.Transforms.Texture[i], (D3DXMATRIX*)&d3d8TransformState.Transforms[X_D3DTS_TEXTURE0 + i]);
|
||||
}
|
||||
|
||||
for (unsigned i = 0; i < ffShaderState.Modes.VertexBlend_NrOfMatrices; i++) {
|
||||
D3DXMatrixTranspose((D3DXMATRIX*)&ffShaderState.Transforms.WorldView[i], (D3DXMATRIX*)d3d8TransformState.GetWorldView(i));
|
||||
D3DXMatrixTranspose((D3DXMATRIX*)&ffShaderState.Transforms.WorldViewInverseTranspose[i], (D3DXMATRIX*)d3d8TransformState.GetWorldViewInverseTranspose(i));
|
||||
}
|
||||
|
||||
// Lighting
|
||||
ffShaderState.Modes.Lighting = (float)XboxRenderStates.GetXboxRenderState(X_D3DRS_LIGHTING);
|
||||
ffShaderState.Modes.TwoSidedLighting = (float)XboxRenderStates.GetXboxRenderState(X_D3DRS_TWOSIDEDLIGHTING);
|
||||
ffShaderState.Modes.LocalViewer = (float)XboxRenderStates.GetXboxRenderState(X_D3DRS_LOCALVIEWER);
|
||||
|
||||
// Material sources
|
||||
bool ColorVertex = XboxRenderStates.GetXboxRenderState(X_D3DRS_COLORVERTEX) != FALSE;
|
||||
ffShaderState.Modes.AmbientMaterialSource = (float)(ColorVertex ? XboxRenderStates.GetXboxRenderState(X_D3DRS_AMBIENTMATERIALSOURCE) : D3DMCS_MATERIAL);
|
||||
ffShaderState.Modes.DiffuseMaterialSource = (float)(ColorVertex ? XboxRenderStates.GetXboxRenderState(X_D3DRS_DIFFUSEMATERIALSOURCE) : D3DMCS_MATERIAL);
|
||||
ffShaderState.Modes.SpecularMaterialSource = (float)(ColorVertex ? XboxRenderStates.GetXboxRenderState(X_D3DRS_SPECULARMATERIALSOURCE) : D3DMCS_MATERIAL);
|
||||
ffShaderState.Modes.EmissiveMaterialSource = (float)(ColorVertex ? XboxRenderStates.GetXboxRenderState(X_D3DRS_EMISSIVEMATERIALSOURCE) : D3DMCS_MATERIAL);
|
||||
ffShaderState.Modes.BackAmbientMaterialSource = (float)(ColorVertex ? XboxRenderStates.GetXboxRenderState(X_D3DRS_BACKAMBIENTMATERIALSOURCE) : D3DMCS_MATERIAL);
|
||||
ffShaderState.Modes.BackDiffuseMaterialSource = (float)(ColorVertex ? XboxRenderStates.GetXboxRenderState(X_D3DRS_BACKDIFFUSEMATERIALSOURCE) : D3DMCS_MATERIAL);
|
||||
ffShaderState.Modes.BackSpecularMaterialSource = (float)(ColorVertex ? XboxRenderStates.GetXboxRenderState(X_D3DRS_BACKSPECULARMATERIALSOURCE) : D3DMCS_MATERIAL);
|
||||
ffShaderState.Modes.BackEmissiveMaterialSource = (float)(ColorVertex ? XboxRenderStates.GetXboxRenderState(X_D3DRS_BACKEMISSIVEMATERIALSOURCE) : D3DMCS_MATERIAL);
|
||||
|
||||
// Point sprites
|
||||
auto pointSize = XboxRenderStates.GetXboxRenderState(X_D3DRS_POINTSIZE);
|
||||
auto pointSizeMin = XboxRenderStates.GetXboxRenderState(X_D3DRS_POINTSIZE_MIN);
|
||||
auto pointSizeMax = XboxRenderStates.GetXboxRenderState(X_D3DRS_POINTSIZE_MAX);
|
||||
ffShaderState.PointSprite.PointSize = *reinterpret_cast<float*>(&pointSize);
|
||||
ffShaderState.PointSprite.PointSizeMin = *reinterpret_cast<float*>(&pointSizeMin);
|
||||
ffShaderState.PointSprite.PointSizeMax = *reinterpret_cast<float*>(&pointSizeMax);
|
||||
|
||||
bool PointScaleEnable = XboxRenderStates.GetXboxRenderState(X_D3DRS_POINTSCALEENABLE);
|
||||
auto scaleA = XboxRenderStates.GetXboxRenderState(X_D3DRS_POINTSCALE_A);
|
||||
auto scaleB = XboxRenderStates.GetXboxRenderState(X_D3DRS_POINTSCALE_B);
|
||||
auto scaleC = XboxRenderStates.GetXboxRenderState(X_D3DRS_POINTSCALE_C);
|
||||
ffShaderState.PointSprite.ScaleABC.x = PointScaleEnable ? *reinterpret_cast<float*>(&scaleA) : 1.0f;
|
||||
ffShaderState.PointSprite.ScaleABC.y = PointScaleEnable ? *reinterpret_cast<float*>(&scaleB) : 0.0f;
|
||||
ffShaderState.PointSprite.ScaleABC.z = PointScaleEnable ? *reinterpret_cast<float*>(&scaleC) : 0.0f;
|
||||
ffShaderState.PointSprite.XboxRenderTargetHeight = PointScaleEnable ? (float)GetPixelContainerHeight(g_pXbox_RenderTarget) : 1.0f;
|
||||
ffShaderState.PointSprite.RenderUpscaleFactor = g_RenderUpscaleFactor;
|
||||
|
||||
// Fog
|
||||
// Determine how fog depth is calculated
|
||||
if (XboxRenderStates.GetXboxRenderState(X_D3DRS_FOGENABLE) &&
|
||||
XboxRenderStates.GetXboxRenderState(X_D3DRS_FOGTABLEMODE) != D3DFOG_NONE) {
|
||||
auto proj = &ffShaderState.Transforms.Projection;
|
||||
|
||||
if (XboxRenderStates.GetXboxRenderState(X_D3DRS_RANGEFOGENABLE)) {
|
||||
LOG_TEST_CASE("Using RANGE fog");
|
||||
ffShaderState.Fog.DepthMode = FixedFunctionVertexShader::FOG_DEPTH_RANGE;
|
||||
}
|
||||
else if (proj->_14 == 0 &&
|
||||
proj->_24 == 0 &&
|
||||
proj->_34 == 0 &&
|
||||
proj->_44 == 1) {
|
||||
LOG_TEST_CASE("Using Z fog");
|
||||
ffShaderState.Fog.DepthMode = FixedFunctionVertexShader::FOG_DEPTH_Z;
|
||||
}
|
||||
else {
|
||||
// Test case:
|
||||
// Fog sample
|
||||
// JSRF (non-compliant projection matrix)
|
||||
ffShaderState.Fog.DepthMode = FixedFunctionVertexShader::FOG_DEPTH_W;
|
||||
}
|
||||
}
|
||||
else {
|
||||
ffShaderState.Fog.DepthMode = FixedFunctionVertexShader::FOG_DEPTH_NONE;
|
||||
}
|
||||
|
||||
// Texture state
|
||||
for (int i = 0; i < 4; i++) {
|
||||
auto transformFlags = XboxTextureStates.Get(i, X_D3DTSS_TEXTURETRANSFORMFLAGS);
|
||||
ffShaderState.TextureStates[i].TextureTransformFlagsCount = (float)(transformFlags & ~D3DTTFF_PROJECTED);
|
||||
ffShaderState.TextureStates[i].TextureTransformFlagsProjected = (float)(transformFlags & D3DTTFF_PROJECTED);
|
||||
|
||||
auto texCoordIndex = XboxTextureStates.Get(i, X_D3DTSS_TEXCOORDINDEX);
|
||||
ffShaderState.TextureStates[i].TexCoordIndex = (float)(texCoordIndex & 0x7); // 8 coords
|
||||
ffShaderState.TextureStates[i].TexCoordIndexGen = (float)(texCoordIndex >> 16); // D3DTSS_TCI flags
|
||||
}
|
||||
|
||||
// TexCoord component counts
|
||||
extern xbox::X_VERTEXATTRIBUTEFORMAT* GetXboxVertexAttributeFormat(); // TMP glue
|
||||
xbox::X_VERTEXATTRIBUTEFORMAT* pXboxVertexAttributeFormat = GetXboxVertexAttributeFormat();
|
||||
for (int i = 0; i < xbox::X_D3DTS_STAGECOUNT; i++) {
|
||||
auto vertexDataFormat = pXboxVertexAttributeFormat->Slots[xbox::X_D3DVSDE_TEXCOORD0 + i].Format;
|
||||
ffShaderState.TexCoordComponentCount[i] = (float)GetXboxVertexDataComponentCount(vertexDataFormat);
|
||||
}
|
||||
|
||||
// Update lights
|
||||
auto LightAmbient = D3DXVECTOR4(0.f, 0.f, 0.f, 0.f);
|
||||
for (size_t i = 0; i < ffShaderState.Lights.size(); i++) {
|
||||
UpdateFixedFunctionShaderLight(d3d8LightState.EnabledLights[i], &ffShaderState.Lights[i], &LightAmbient);
|
||||
}
|
||||
|
||||
D3DXVECTOR4 Ambient = toVector(XboxRenderStates.GetXboxRenderState(X_D3DRS_AMBIENT));
|
||||
D3DXVECTOR4 BackAmbient = toVector(XboxRenderStates.GetXboxRenderState(X_D3DRS_BACKAMBIENT));
|
||||
|
||||
ffShaderState.TotalLightsAmbient.Front = (D3DXVECTOR3)(LightAmbient + Ambient);
|
||||
ffShaderState.TotalLightsAmbient.Back = (D3DXVECTOR3)(LightAmbient + BackAmbient);
|
||||
|
||||
// Misc flags
|
||||
ffShaderState.Modes.NormalizeNormals = (float)XboxRenderStates.GetXboxRenderState(X_D3DRS_NORMALIZENORMALS);
|
||||
|
||||
// Write fixed function state to shader constants
|
||||
const int slotSize = 16;
|
||||
const int fixedFunctionStateSize = (sizeof(FixedFunctionVertexShaderState) + slotSize - 1) / slotSize;
|
||||
auto hRet = g_pD3DDevice->SetVertexShaderConstantF(0, (float*)&ffShaderState, fixedFunctionStateSize);
|
||||
|
||||
if (FAILED(hRet)) {
|
||||
CxbxKrnlCleanup("Failed to write fixed-function HLSL state");
|
||||
}
|
||||
}
|
||||
|
||||
// ******************************************************************
|
||||
// * patch: D3DDevice_EnableOverlay
|
||||
// ******************************************************************
|
||||
|
@ -6388,19 +6607,21 @@ xbox::void_xt __fastcall xbox::EMUPATCH(D3DDevice_SetRenderState_Simple)
|
|||
|
||||
void CxbxImpl_SetTransform
|
||||
(
|
||||
D3DTRANSFORMSTATETYPE State,
|
||||
xbox::X_D3DTRANSFORMSTATETYPE State,
|
||||
CONST D3DMATRIX *pMatrix
|
||||
)
|
||||
{
|
||||
LOG_INIT
|
||||
|
||||
State = EmuXB2PC_D3DTS(State);
|
||||
d3d8TransformState.SetTransform(State, pMatrix);
|
||||
|
||||
HRESULT hRet = g_pD3DDevice->SetTransform(State, pMatrix);
|
||||
auto d3d9State = EmuXB2PC_D3DTS(State);
|
||||
|
||||
HRESULT hRet = g_pD3DDevice->SetTransform(d3d9State, pMatrix);
|
||||
DEBUG_D3DRESULT(hRet, "g_pD3DDevice->SetTransform");
|
||||
}
|
||||
|
||||
// MultiplyTransform can call SetTransform, nested call detection is required
|
||||
// MultiplyTransform should call SetTransform, we'd like to know if it didn't
|
||||
// Test case: 25 to Life
|
||||
static thread_local uint32_t setTransformCount = 0;
|
||||
|
||||
|
@ -6411,7 +6632,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
|
||||
)
|
||||
{
|
||||
|
@ -6420,7 +6641,7 @@ static void D3DDevice_SetTransform_0
|
|||
LOG_FUNC_ARG(pMatrix)
|
||||
LOG_FUNC_END;
|
||||
|
||||
NestedPatchCounter call(setTransformCount);
|
||||
setTransformCount++;
|
||||
|
||||
__asm {
|
||||
// Trampoline to guest code to remove the need for a GetTransform patch
|
||||
|
@ -6429,17 +6650,14 @@ static void D3DDevice_SetTransform_0
|
|||
call XB_TRMP(D3DDevice_SetTransform_0)
|
||||
}
|
||||
|
||||
if (call.GetLevel() == 0) {
|
||||
// Skip if this patch is called from MultiplyTransform
|
||||
CxbxImpl_SetTransform(State, pMatrix);
|
||||
}
|
||||
CxbxImpl_SetTransform(State, pMatrix);
|
||||
}
|
||||
|
||||
__declspec(naked) xbox::void_xt WINAPI xbox::EMUPATCH(D3DDevice_SetTransform_0)
|
||||
(
|
||||
)
|
||||
{
|
||||
D3DTRANSFORMSTATETYPE State;
|
||||
xbox::X_D3DTRANSFORMSTATETYPE State;
|
||||
CONST D3DMATRIX *pMatrix;
|
||||
__asm {
|
||||
LTCG_PROLOGUE
|
||||
|
@ -6461,7 +6679,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
|
||||
)
|
||||
{
|
||||
|
@ -6470,14 +6688,11 @@ xbox::void_xt WINAPI xbox::EMUPATCH(D3DDevice_SetTransform)
|
|||
LOG_FUNC_ARG(pMatrix)
|
||||
LOG_FUNC_END;
|
||||
|
||||
NestedPatchCounter call(setTransformCount);
|
||||
setTransformCount++;
|
||||
|
||||
// Trampoline to guest code to remove the need for a GetTransform patch
|
||||
XB_TRMP(D3DDevice_SetTransform)(State, pMatrix);
|
||||
if (call.GetLevel() == 0) {
|
||||
// Skip if this patch is called from MultiplyTransform
|
||||
CxbxImpl_SetTransform(State, pMatrix);
|
||||
}
|
||||
CxbxImpl_SetTransform(State, pMatrix);
|
||||
}
|
||||
|
||||
// ******************************************************************
|
||||
|
@ -6485,7 +6700,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
|
||||
)
|
||||
{
|
||||
|
@ -6494,15 +6709,17 @@ xbox::void_xt WINAPI xbox::EMUPATCH(D3DDevice_MultiplyTransform)
|
|||
LOG_FUNC_ARG(pMatrix)
|
||||
LOG_FUNC_END;
|
||||
|
||||
NestedPatchCounter call(setTransformCount);
|
||||
setTransformCount = 0;
|
||||
|
||||
// Trampoline to guest code to remove the need for a GetTransform patch
|
||||
// Trampoline to guest code, which we expect to call SetTransform
|
||||
// If we find a case where the trampoline doesn't call SetTransform
|
||||
// (or we can't detect the call) we will need to implement this
|
||||
XB_TRMP(D3DDevice_MultiplyTransform)(State, pMatrix);
|
||||
|
||||
State = EmuXB2PC_D3DTS(State);
|
||||
if (setTransformCount == 0) {
|
||||
LOG_TEST_CASE("MultiplyTransform did not appear to call SetTransform");
|
||||
}
|
||||
|
||||
HRESULT hRet = g_pD3DDevice->MultiplyTransform(State, pMatrix);
|
||||
DEBUG_D3DRESULT(hRet, "g_pD3DDevice->MultiplyTransform");
|
||||
}
|
||||
|
||||
// ******************************************************************
|
||||
|
@ -7118,8 +7335,6 @@ void CxbxUpdateHostTextures()
|
|||
|
||||
void CxbxUpdateHostTextureScaling()
|
||||
{
|
||||
extern xbox::X_VERTEXATTRIBUTEFORMAT* GetXboxVertexAttributeFormat(); // TMP glue
|
||||
|
||||
// Xbox works with "Linear" and "Swizzled" texture formats
|
||||
// Linear formats are not addressed with normalized coordinates (similar to https://www.khronos.org/opengl/wiki/Rectangle_Texture?)
|
||||
// We want to use normalized coordinates in our shaders, so need to be able to scale the coordinates back
|
||||
|
@ -7141,7 +7356,7 @@ void CxbxUpdateHostTextureScaling()
|
|||
|
||||
// Texcoord index. Just the texture stage unless fixed function mode
|
||||
int texCoordIndex = stage;
|
||||
if (g_Xbox_VertexShader_IsFixedFunction) {
|
||||
if (g_Xbox_VertexShaderMode == VertexShaderMode::FixedFunction) {
|
||||
// Get TEXCOORDINDEX for the current texture stage's state
|
||||
// Stores both the texture stage index and information for generating coordinates
|
||||
// See D3DTSS_TEXCOORDINDEX
|
||||
|
@ -7191,18 +7406,10 @@ extern float* HLE_get_NV2A_vertex_constant_float4_ptr(unsigned const_index); //
|
|||
// remove our patches on D3DDevice_SetVertexShaderConstant (and CxbxImpl_SetVertexShaderConstant)
|
||||
void CxbxUpdateHostVertexShaderConstants()
|
||||
{
|
||||
// Transfer all constants that have been flagged dirty to host
|
||||
auto nv2a = g_NV2A->GetDeviceState();
|
||||
for (int i = 0; i < X_D3DVS_CONSTREG_COUNT; i++) {
|
||||
if (nv2a->pgraph.vsh_constants_dirty[i]) {
|
||||
nv2a->pgraph.vsh_constants_dirty[i] = false;
|
||||
|
||||
float *constant_floats = HLE_get_NV2A_vertex_constant_float4_ptr(i);
|
||||
// Note : If host SetVertexShaderConstantF has high overhead (unlikely),
|
||||
// we could combine multiple adjacent constants into one call.
|
||||
g_pD3DDevice->SetVertexShaderConstantF(i, constant_floats, 1);
|
||||
}
|
||||
}
|
||||
// Copy all constants (as they may have been overwritten with fixed-function mode)
|
||||
// Though we should only have to copy overwritten or dirty constants
|
||||
float* constant_floats = HLE_get_NV2A_vertex_constant_float4_ptr(0);
|
||||
g_pD3DDevice->SetVertexShaderConstantF(0, constant_floats, X_D3DVS_CONSTREG_COUNT);
|
||||
|
||||
// FIXME our viewport constants don't match Xbox values
|
||||
// If we write them to pgraph constants, like we do with constants set by the title,
|
||||
|
@ -7232,7 +7439,7 @@ void CxbxUpdateHostViewport() {
|
|||
aaScaleX *= g_RenderTargetUpscaleFactor;
|
||||
aaScaleY *= g_RenderTargetUpscaleFactor;
|
||||
|
||||
if (g_Xbox_VertexShader_IsFixedFunction) {
|
||||
if (g_Xbox_VertexShaderMode == VertexShaderMode::FixedFunction) {
|
||||
// Set viewport
|
||||
D3DVIEWPORT hostViewport = g_Xbox_Viewport;
|
||||
hostViewport.X *= aaScaleX;
|
||||
|
@ -7293,6 +7500,11 @@ void CxbxUpdateNativeD3DResources()
|
|||
CxbxUpdateHostVertexShaderConstants();
|
||||
|
||||
CxbxUpdateHostViewport();
|
||||
|
||||
// Update fixed function vertex shader state
|
||||
if (g_Xbox_VertexShaderMode == VertexShaderMode::FixedFunction && g_UseFixedFunctionVertexShader) {
|
||||
UpdateFixedFunctionVertexShaderState();
|
||||
}
|
||||
|
||||
// NOTE: Order is important here
|
||||
// Some Texture States depend on RenderState values (Point Sprites)
|
||||
|
@ -7886,6 +8098,8 @@ xbox::hresult_xt WINAPI xbox::EMUPATCH(D3DDevice_SetLight)
|
|||
|
||||
XB_TRMP(D3DDevice_SetLight)(Index, pLight);
|
||||
|
||||
d3d8LightState.Lights[Index] = *pLight;
|
||||
|
||||
HRESULT hRet = g_pD3DDevice->SetLight(Index, pLight);
|
||||
DEBUG_D3DRESULT(hRet, "g_pD3DDevice->SetLight");
|
||||
|
||||
|
@ -7902,6 +8116,12 @@ xbox::void_xt WINAPI xbox::EMUPATCH(D3DDevice_SetMaterial)
|
|||
{
|
||||
LOG_FUNC_ONE_ARG(pMaterial);
|
||||
|
||||
ffShaderState.Materials[0].Ambient = toVector(pMaterial->Ambient);
|
||||
ffShaderState.Materials[0].Diffuse = toVector(pMaterial->Diffuse);
|
||||
ffShaderState.Materials[0].Specular = toVector(pMaterial->Specular);
|
||||
ffShaderState.Materials[0].Emissive = toVector(pMaterial->Emissive);
|
||||
ffShaderState.Materials[0].Power = pMaterial->Power;
|
||||
|
||||
HRESULT hRet = g_pD3DDevice->SetMaterial(pMaterial);
|
||||
DEBUG_D3DRESULT(hRet, "g_pD3DDevice->SetMaterial");
|
||||
}
|
||||
|
@ -7922,6 +8142,8 @@ xbox::hresult_xt WINAPI xbox::EMUPATCH(D3DDevice_LightEnable)
|
|||
|
||||
XB_TRMP(D3DDevice_LightEnable)(Index, bEnable);
|
||||
|
||||
d3d8LightState.EnableLight(Index, bEnable);
|
||||
|
||||
HRESULT hRet = g_pD3DDevice->LightEnable(Index, bEnable);
|
||||
DEBUG_D3DRESULT(hRet, "g_pD3DDevice->LightEnable");
|
||||
|
||||
|
|
|
@ -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
|
||||
);
|
||||
|
||||
|
|
|
@ -0,0 +1,518 @@
|
|||
#include "FixedFunctionVertexShaderState.hlsli"
|
||||
|
||||
// Default values for vertex registers, and whether to use them
|
||||
uniform float4 vRegisterDefaultValues[16] : register(c192);
|
||||
uniform float4 vRegisterDefaultFlagsPacked[4] : register(c208);
|
||||
static bool vRegisterDefaultFlags[16];
|
||||
|
||||
uniform FixedFunctionVertexShaderState state : register(c0);
|
||||
|
||||
uniform float4 xboxTextureScale[4] : register(c214);
|
||||
|
||||
#undef CXBX_ALL_TEXCOORD_INPUTS // Enable this to disable semantics in VS_INPUT (instead, we'll use an array of generic TEXCOORD's)
|
||||
|
||||
// Input registers
|
||||
struct VS_INPUT
|
||||
{
|
||||
#ifdef CXBX_ALL_TEXCOORD_INPUTS
|
||||
float4 v[16] : TEXCOORD;
|
||||
#else
|
||||
float4 pos : POSITION;
|
||||
float4 bw : BLENDWEIGHT;
|
||||
float4 color[2] : COLOR;
|
||||
float4 backColor[2] : TEXCOORD4;
|
||||
float4 normal : NORMAL;
|
||||
float4 texcoord[4] : TEXCOORD;
|
||||
#endif
|
||||
};
|
||||
|
||||
// Input register indices (also known as attributes, as given in VS_INPUT.v array)
|
||||
// TODO : Convert FVF codes on CPU to a vertex declaration with these standardized register indices:
|
||||
// NOTE : Converting FVF vertex indices must also consider NV2A vertex attribute 'slot mapping',
|
||||
// as set in NV2A_VTXFMT/NV097_SET_VERTEX_DATA_ARRAY_FORMAT!
|
||||
// TODO : Rename these into SLOT_POSITION, SLOT_WEIGHT, SLOT_TEXTURE0, SLOT_TEXTURE3, etc :
|
||||
static const uint position = 0; // See X_D3DFVF_XYZ / X_D3DVSDE_POSITION was float4 pos : POSITION;
|
||||
static const uint weight = 1; // See X_D3DFVF_XYZB1-4 / X_D3DVSDE_BLENDWEIGHT was float4 bw : BLENDWEIGHT;
|
||||
static const uint normal = 2; // See X_D3DFVF_NORMAL / X_D3DVSDE_NORMAL was float4 normal : NORMAL; // Note : Only normal.xyz is used.
|
||||
static const uint diffuse = 3; // See X_D3DFVF_DIFFUSE / X_D3DVSDE_DIFFUSE was float4 color[2] : COLOR;
|
||||
static const uint specular = 4; // See X_D3DFVF_SPECULAR / X_D3DVSDE_SPECULAR
|
||||
static const uint fogCoord = 5; // Has no X_D3DFVF_* ! See X_D3DVSDE_FOG Note : Only fog.x is used.
|
||||
static const uint pointSize = 6; // Has no X_D3DFVF_* ! See X_D3DVSDE_POINTSIZE
|
||||
static const uint backDiffuse = 7; // Has no X_D3DFVF_* ! See X_D3DVSDE_BACKDIFFUSE was float4 backColor[2] : TEXCOORD4;
|
||||
static const uint backSpecular = 8; // Has no X_D3DFVF_* ! See X_D3DVSDE_BACKSPECULAR
|
||||
static const uint texcoord0 = 9; // See X_D3DFVF_TEX1 / X_D3DVSDE_TEXCOORD0 was float4 texcoord[4] : TEXCOORD;
|
||||
static const uint texcoord1 = 10; // See X_D3DFVF_TEX2 / X_D3DVSDE_TEXCOORD1
|
||||
static const uint texcoord2 = 11; // See X_D3DFVF_TEX3 / X_D3DVSDE_TEXCOORD2
|
||||
static const uint texcoord3 = 12; // See X_D3DFVF_TEX4 / X_D3DVSDE_TEXCOORD3
|
||||
static const uint reserved0 = 13; // Has no X_D3DFVF_* / X_D3DVSDE_*
|
||||
static const uint reserved1 = 14; // Has no X_D3DFVF_* / X_D3DVSDE_*
|
||||
static const uint reserved2 = 15; // Has no X_D3DFVF_* / X_D3DVSDE_*
|
||||
|
||||
float4 Get(const VS_INPUT xIn, const uint index)
|
||||
{
|
||||
#ifdef CXBX_ALL_TEXCOORD_INPUTS
|
||||
return xIn.v[index];
|
||||
#else
|
||||
// switch statements inexplicably don't work here
|
||||
if(index == position) return xIn.pos;
|
||||
if(index == weight) return xIn.bw;
|
||||
if(index == normal) return xIn.normal;
|
||||
if(index == diffuse) return xIn.color[0];
|
||||
if(index == specular) return xIn.color[1];
|
||||
if(index == backDiffuse) return xIn.backColor[0];
|
||||
if(index == backSpecular) return xIn.backColor[1];
|
||||
if(index == texcoord0) return xIn.texcoord[0];
|
||||
if(index == texcoord1) return xIn.texcoord[1];
|
||||
if(index == texcoord2) return xIn.texcoord[2];
|
||||
if(index == texcoord3) return xIn.texcoord[3];
|
||||
return 1;
|
||||
#endif
|
||||
}
|
||||
|
||||
// Output registers
|
||||
struct VS_OUTPUT
|
||||
{
|
||||
float4 oPos : POSITION; // Homogeneous clip space position
|
||||
float4 oD0 : COLOR0; // Primary color (front-facing)
|
||||
float4 oD1 : COLOR1; // Secondary color (front-facing)
|
||||
float oFog : FOG; // Fog coordinate
|
||||
float oPts : PSIZE; // Point size
|
||||
float4 oB0 : TEXCOORD4; // Back-facing primary color
|
||||
float4 oB1 : TEXCOORD5; // Back-facing secondary color
|
||||
float4 oT0 : TEXCOORD0; // Texture coordinate set 0
|
||||
float4 oT1 : TEXCOORD1; // Texture coordinate set 1
|
||||
float4 oT2 : TEXCOORD2; // Texture coordinate set 2
|
||||
float4 oT3 : TEXCOORD3; // Texture coordinate set 3
|
||||
};
|
||||
|
||||
struct TransformInfo
|
||||
{
|
||||
float4 Position;
|
||||
float3 Normal;
|
||||
};
|
||||
|
||||
static TransformInfo View; // Vertex transformed to viewspace
|
||||
static TransformInfo Projection; // Vertex transformed to projection space
|
||||
|
||||
static const int LIGHT_TYPE_NONE = 0;
|
||||
static const int LIGHT_TYPE_POINT = 1;
|
||||
static const int LIGHT_TYPE_SPOT = 2;
|
||||
static const int LIGHT_TYPE_DIRECTIONAL = 3;
|
||||
|
||||
// Final lighting output
|
||||
struct LightingOutput
|
||||
{
|
||||
TwoSidedColor Diffuse;
|
||||
TwoSidedColor Specular;
|
||||
};
|
||||
|
||||
// useful reference https://drivers.amd.com/misc/samples/dx9/FixedFuncShader.pdf
|
||||
LightingOutput DoLight(const Light l, const float2 powers)
|
||||
{
|
||||
LightingOutput o;
|
||||
o.Diffuse.Front = o.Diffuse.Back = float3(0, 0, 0);
|
||||
o.Specular.Front = o.Specular.Back = float3(0, 0, 0);
|
||||
|
||||
float3 toLight;
|
||||
float3 toLightN;
|
||||
float attenuation = 1;
|
||||
float spotIntensity = 1;
|
||||
|
||||
if (l.Type == LIGHT_TYPE_DIRECTIONAL) {
|
||||
toLight = toLightN = -l.DirectionVN;
|
||||
}
|
||||
else {
|
||||
toLight = l.PositionV - View.Position.xyz;
|
||||
toLightN = normalize(toLight);
|
||||
}
|
||||
|
||||
if (l.Type == LIGHT_TYPE_SPOT) {
|
||||
// Spotlight factors
|
||||
// https://docs.microsoft.com/en-us/windows/win32/direct3d9/light-types
|
||||
float3 toVertexN = -toLightN;
|
||||
float cosAlpha = dot(l.DirectionVN, toVertexN);
|
||||
// I = ( cos(a) - cos(phi/2) ) / ( cos(theta/2) - cos(phi/2) )
|
||||
float spotBase = saturate((cosAlpha - l.CosHalfPhi) / l.SpotIntensityDivisor);
|
||||
float spotIntensity = pow(spotBase, l.Falloff);
|
||||
}
|
||||
|
||||
if (l.Type == LIGHT_TYPE_POINT || l.Type == LIGHT_TYPE_SPOT) {
|
||||
float lightDist = length(toLight);
|
||||
|
||||
// A(Constant) + A(Linear) * dist + A(Exp) * dist^2
|
||||
attenuation =
|
||||
1 / (l.Attenuation[0]
|
||||
+ l.Attenuation[1] * lightDist
|
||||
+ l.Attenuation[2] * lightDist * lightDist);
|
||||
|
||||
// Range cutoff
|
||||
if (lightDist > l.Range)
|
||||
attenuation = 0;
|
||||
}
|
||||
|
||||
// Diffuse lighting calculation
|
||||
const float NdotLFront = dot(View.Normal, toLightN);
|
||||
const float NdotLBack = dot(-View.Normal, toLightN);
|
||||
o.Diffuse.Front = max(NdotLFront, 0) * l.Diffuse.rgb * attenuation * spotIntensity;
|
||||
o.Diffuse.Back = max(NdotLBack, 0) * l.Diffuse.rgb * attenuation * spotIntensity;
|
||||
|
||||
// Specular lighting calculation
|
||||
float3 toViewerN = state.Modes.LocalViewer
|
||||
? normalize(-View.Position.xyz) // Strip sample
|
||||
: float3(0, 0, -1); // DoA 3 character select
|
||||
|
||||
// Note : if X_D3DRS_SPECULARENABLE is false then all light specular colours should have been zeroed out
|
||||
// Blinn-Phong
|
||||
// https://learnopengl.com/Advanced-Lighting/Advanced-Lighting
|
||||
const float3 halfway = normalize(toViewerN + toLightN);
|
||||
const float NdotHFront = dot(View.Normal, halfway);
|
||||
const float NdotHBack = dot(-View.Normal, halfway);
|
||||
|
||||
o.Specular.Front = pow(max(NdotHFront, 0), powers[0]) * l.Specular.rgb * attenuation * spotIntensity;
|
||||
o.Specular.Back = pow(max(NdotHBack, 0), powers[1]) * l.Specular.rgb * attenuation * spotIntensity;
|
||||
|
||||
return o;
|
||||
}
|
||||
|
||||
LightingOutput CalcLighting(const float2 powers)
|
||||
{
|
||||
LightingOutput totalLightOutput;
|
||||
totalLightOutput.Diffuse.Front = float3(0, 0, 0);
|
||||
totalLightOutput.Diffuse.Back = float3(0, 0, 0);
|
||||
totalLightOutput.Specular.Front = float3(0, 0, 0);
|
||||
totalLightOutput.Specular.Back = float3(0, 0, 0);
|
||||
|
||||
for (uint i = 0; i < 8; i++)
|
||||
{
|
||||
const Light currentLight = state.Lights[i];
|
||||
LightingOutput currentLightOutput;
|
||||
|
||||
if (currentLight.Type != LIGHT_TYPE_NONE) {
|
||||
currentLightOutput = DoLight(currentLight, powers);
|
||||
|
||||
totalLightOutput.Diffuse.Front += currentLightOutput.Diffuse.Front;
|
||||
totalLightOutput.Diffuse.Back += currentLightOutput.Diffuse.Back;
|
||||
totalLightOutput.Specular.Front += currentLightOutput.Specular.Front;
|
||||
totalLightOutput.Specular.Back += currentLightOutput.Specular.Back;
|
||||
}
|
||||
}
|
||||
|
||||
return totalLightOutput;
|
||||
}
|
||||
|
||||
TransformInfo DoTransform(const float4 position, const float3 normal, const float4 blendWeights)
|
||||
{
|
||||
TransformInfo output;
|
||||
output.Position = float4(0, 0, 0, 0);
|
||||
output.Normal = float3(0, 0, 0);
|
||||
|
||||
// The number of matrices to blend (always in the range [1..4])
|
||||
const int matrices = state.Modes.VertexBlend_NrOfMatrices;
|
||||
|
||||
// Initialize the final matrix its blend weight at 1, from which all preceding blend weights will be deducted :
|
||||
float lastBlend = 1;
|
||||
for (int i = 0; i < matrices; i++)
|
||||
{
|
||||
// Do we have to calculate the last blend value (never happens when there's already 4 matrices) ?
|
||||
const bool bCalcFinalWeight = (state.Modes.VertexBlend_CalcLastWeight > 0) && (i == (matrices - 1));
|
||||
// Note : In case of X_D3DVBF_DISABLE, no prior weights have been deducted from lastBlend, so it will still be 1.
|
||||
// The number of matrices will also be 1, which effectively turns this into non-weighted single-matrix multiplications :
|
||||
const float blendWeight = bCalcFinalWeight ? lastBlend : blendWeights[i];
|
||||
// Reduce the blend weight for the final matrix :
|
||||
lastBlend -= blendWeights[i];
|
||||
// Add this matrix (multiplied by its blend weight) to the output :
|
||||
output.Position += mul(position, state.Transforms.WorldView[i]) * blendWeight;
|
||||
output.Normal += mul(normal, (float3x3) state.Transforms.WorldViewInverseTranspose[i]) * blendWeight;
|
||||
}
|
||||
|
||||
return output;
|
||||
}
|
||||
|
||||
Material DoMaterial(const uint index, const uint diffuseReg, const uint specularReg, const VS_INPUT xIn)
|
||||
{
|
||||
// Get the material from material state
|
||||
Material material = state.Materials[index];
|
||||
|
||||
// Note : if (state.Modes.ColorVertex) no longer required because when disabled, CPU sets all MaterialSource's to D3DMCS_MATERIAL
|
||||
{
|
||||
// https://docs.microsoft.com/en-us/windows/win32/direct3d9/d3dmaterialcolorsource
|
||||
static const int D3DMCS_MATERIAL = 0;
|
||||
static const int D3DMCS_COLOR1 = 1;
|
||||
static const int D3DMCS_COLOR2 = 2;
|
||||
|
||||
// If COLORVERTEX mode, AND the desired diffuse or specular colour is defined in the vertex declaration
|
||||
// Then use the vertex colour instead of the material
|
||||
|
||||
if (!vRegisterDefaultFlags[diffuseReg]) {
|
||||
const float4 diffuseVertexColour = Get(xIn, diffuseReg);
|
||||
if (state.Modes.AmbientMaterialSource == D3DMCS_COLOR1) material.Ambient = diffuseVertexColour;
|
||||
if (state.Modes.DiffuseMaterialSource == D3DMCS_COLOR1) material.Diffuse = diffuseVertexColour;
|
||||
if (state.Modes.SpecularMaterialSource == D3DMCS_COLOR1) material.Specular = diffuseVertexColour;
|
||||
if (state.Modes.EmissiveMaterialSource == D3DMCS_COLOR1) material.Emissive = diffuseVertexColour;
|
||||
}
|
||||
|
||||
if (!vRegisterDefaultFlags[specularReg]) {
|
||||
const float4 specularVertexColour = Get(xIn, specularReg);
|
||||
if (state.Modes.AmbientMaterialSource == D3DMCS_COLOR2) material.Ambient = specularVertexColour;
|
||||
if (state.Modes.DiffuseMaterialSource == D3DMCS_COLOR2) material.Diffuse = specularVertexColour;
|
||||
if (state.Modes.SpecularMaterialSource == D3DMCS_COLOR2) material.Specular = specularVertexColour;
|
||||
if (state.Modes.EmissiveMaterialSource == D3DMCS_COLOR2) material.Emissive = specularVertexColour;
|
||||
}
|
||||
}
|
||||
|
||||
return material;
|
||||
}
|
||||
|
||||
float DoFog(const VS_INPUT xIn)
|
||||
{
|
||||
// TODO implement properly
|
||||
// Until we have pixel shader HLSL we are still leaning on D3D renderstates for fogging
|
||||
// So we are not doing any fog density calculations here
|
||||
// http://developer.download.nvidia.com/assets/gamedev/docs/Fog2.pdf
|
||||
|
||||
float fogDepth;
|
||||
|
||||
if (state.Fog.DepthMode == FixedFunctionVertexShader::FOG_DEPTH_NONE)
|
||||
fogDepth = Get(xIn, specular).a; // In fixed-function mode, fog is passed in the specular alpha
|
||||
if (state.Fog.DepthMode == FixedFunctionVertexShader::FOG_DEPTH_RANGE)
|
||||
fogDepth = length(View.Position.xyz);
|
||||
if (state.Fog.DepthMode == FixedFunctionVertexShader::FOG_DEPTH_Z)
|
||||
fogDepth = abs(Projection.Position.z);
|
||||
if (state.Fog.DepthMode == FixedFunctionVertexShader::FOG_DEPTH_W)
|
||||
fogDepth = Projection.Position.w;
|
||||
|
||||
return fogDepth;
|
||||
}
|
||||
|
||||
float4 DoTexCoord(const uint stage, const VS_INPUT xIn)
|
||||
{
|
||||
// Texture transform flags
|
||||
// https://docs.microsoft.com/en-gb/windows/win32/direct3d9/d3dtexturetransformflags
|
||||
static const int D3DTTFF_DISABLE = 0;
|
||||
static const int D3DTTFF_COUNT1 = 1;
|
||||
static const int D3DTTFF_COUNT2 = 2;
|
||||
static const int D3DTTFF_COUNT3 = 3;
|
||||
static const int D3DTTFF_COUNT4 = 4;
|
||||
static const int D3DTTFF_PROJECTED = 256; // This is the only real flag
|
||||
|
||||
// https://docs.microsoft.com/en-us/windows/win32/direct3d9/d3dtss-tci
|
||||
// Pre-shifted
|
||||
static const int TCI_PASSTHRU = 0;
|
||||
static const int TCI_CAMERASPACENORMAL = 1;
|
||||
static const int TCI_CAMERASPACEPOSITION = 2;
|
||||
static const int TCI_CAMERASPACEREFLECTIONVECTOR = 3;
|
||||
static const int TCI_OBJECT = 4; // Xbox
|
||||
static const int TCI_SPHERE = 5; // Xbox
|
||||
|
||||
const TextureState tState = state.TextureStates[stage];
|
||||
|
||||
// Extract transform flags
|
||||
const int countFlag = tState.TextureTransformFlagsCount;
|
||||
const bool projected = tState.TextureTransformFlagsProjected;
|
||||
|
||||
// Get texture coordinates
|
||||
// Coordinates are either from the vertex texcoord data
|
||||
// Or generated
|
||||
float4 texCoord = float4(0, 0, 0, 0);
|
||||
if (tState.TexCoordIndexGen == TCI_PASSTHRU)
|
||||
{
|
||||
// Get from vertex data
|
||||
const uint texCoordIndex = abs(tState.TexCoordIndex); // Note : abs() avoids error X3548 : in vs_3_0 uints can only be used with known - positive values, use int if possible
|
||||
texCoord = Get(xIn, texcoord0+texCoordIndex);
|
||||
|
||||
// Make coordinates homogenous
|
||||
// For example, if a title supplies (u, v)
|
||||
// We need to make transform (u, v, 1) to allow translation with a 3x3 matrix
|
||||
// We'll need to get this from the current FVF or VertexDeclaration
|
||||
// Test case: JSRF scrolling texture effect.
|
||||
// Test case: Madagascar shadows
|
||||
// Test case: Modify pixel shader sample
|
||||
|
||||
// TODO move alongside the texture transformation when it stops angering the HLSL compiler
|
||||
const float componentCount = state.TexCoordComponentCount[texCoordIndex];
|
||||
if (componentCount == 1)
|
||||
texCoord.yzw = float3(1, 0, 0);
|
||||
if (componentCount == 2)
|
||||
texCoord.zw = float2(1, 0);
|
||||
if (componentCount == 3)
|
||||
texCoord.w = 1;
|
||||
} // Generate texture coordinates
|
||||
else if (tState.TexCoordIndexGen == TCI_CAMERASPACENORMAL)
|
||||
texCoord = float4(View.Normal, 1);
|
||||
else if (tState.TexCoordIndexGen == TCI_CAMERASPACEPOSITION)
|
||||
texCoord = View.Position;
|
||||
else
|
||||
{
|
||||
const float3 reflected = reflect(normalize(View.Position.xyz), View.Normal);
|
||||
|
||||
if (tState.TexCoordIndexGen == TCI_CAMERASPACEREFLECTIONVECTOR)
|
||||
texCoord.xyz = reflected;
|
||||
// else if TCI_OBJECT TODO is this just model position?
|
||||
else if (tState.TexCoordIndexGen == TCI_SPHERE)
|
||||
{
|
||||
// TODO verify
|
||||
// http://www.bluevoid.com/opengl/sig99/advanced99/notes/node177.html
|
||||
const float3 R = reflected;
|
||||
const float p = sqrt(pow(R.x, 2) + pow(R.y, 2) + pow(R.z + 1, 2));
|
||||
texCoord.x = R.x / 2 * p + 0.5f;
|
||||
texCoord.y = R.y / 2 * p + 0.5f;
|
||||
}
|
||||
}
|
||||
|
||||
// Transform the texture coordinates if requested
|
||||
if (countFlag != D3DTTFF_DISABLE)
|
||||
texCoord = mul(texCoord, state.Transforms.Texture[stage]);
|
||||
|
||||
// We always send four coordinates
|
||||
// If we are supposed to send less than four
|
||||
// then copy the last coordinate to the remaining coordinates
|
||||
// For D3DTTFF_PROJECTED, the value of the *last* coordinate is important
|
||||
// Test case: ProjectedTexture sample, which uses 3 coordinates
|
||||
// We'll need to implement the divide when D3D stops handling it for us?
|
||||
// https://docs.microsoft.com/en-us/windows/win32/direct3d9/d3dtexturetransformflags
|
||||
if (projected)
|
||||
{
|
||||
if (countFlag == 1)
|
||||
texCoord.yzw = texCoord.x;
|
||||
if (countFlag == 2)
|
||||
texCoord.zw = texCoord.y;
|
||||
if (countFlag == 3)
|
||||
texCoord.w = texCoord.z;
|
||||
}
|
||||
|
||||
return texCoord;
|
||||
}
|
||||
|
||||
// Point size for Point Sprites
|
||||
// https://docs.microsoft.com/en-us/windows/win32/direct3d9/point-sprites
|
||||
// Test case: Point sprite sample
|
||||
float DoPointSpriteSize()
|
||||
{
|
||||
const PointSprite ps = state.PointSprite;
|
||||
float pointSize = ps.PointSize;
|
||||
float A = ps.ScaleABC.x;
|
||||
float B = ps.ScaleABC.y;
|
||||
float C = ps.ScaleABC.z;
|
||||
|
||||
// Note : if (ps.PointScaleEnable) not required because when disabled, CPU sets RenderTargetHeight and ScaleA to 1, and ScaleB and ScaleC to 0
|
||||
{
|
||||
const float eyeDistance = length(View.Position);
|
||||
const float factor = A + (B * eyeDistance) + (C * (eyeDistance * eyeDistance));
|
||||
|
||||
pointSize *= ps.XboxRenderTargetHeight * sqrt(1 / factor);
|
||||
}
|
||||
|
||||
return clamp(pointSize, ps.PointSizeMin, ps.PointSizeMax) * ps.RenderUpscaleFactor;
|
||||
}
|
||||
|
||||
VS_INPUT InitializeInputRegisters(const VS_INPUT xInput)
|
||||
{
|
||||
VS_INPUT xIn;
|
||||
|
||||
// Initialize input registers from the vertex buffer data
|
||||
// Or use the register's default value (which can be changed by the title)
|
||||
for (uint i = 0; i < 16; i++)
|
||||
{
|
||||
const float4 value = lerp(Get(xInput, i), vRegisterDefaultValues[i], vRegisterDefaultFlags[i]);
|
||||
#ifdef CXBX_ALL_TEXCOORD_INPUTS
|
||||
xIn.v[i] = value;
|
||||
#else
|
||||
// switch statements inexplicably don't work here
|
||||
if(i == position) xIn.pos = value;
|
||||
if(i == weight) xIn.bw = value;
|
||||
if(i == normal) xIn.normal = value;
|
||||
if(i == diffuse) xIn.color[0] = value;
|
||||
if(i == specular) xIn.color[1] = value;
|
||||
if(i == backDiffuse) xIn.backColor[0] = value;
|
||||
if(i == backSpecular) xIn.backColor[1] = value;
|
||||
if(i == texcoord0) xIn.texcoord[0] = value;
|
||||
if(i == texcoord1) xIn.texcoord[1] = value;
|
||||
if(i == texcoord2) xIn.texcoord[2] = value;
|
||||
if(i == texcoord3) xIn.texcoord[3] = value;
|
||||
#endif
|
||||
}
|
||||
|
||||
return xIn;
|
||||
}
|
||||
|
||||
VS_OUTPUT main(const VS_INPUT xInput)
|
||||
{
|
||||
VS_OUTPUT xOut;
|
||||
|
||||
// Unpack 16 bool flags from 4 float4 constant registers
|
||||
vRegisterDefaultFlags = (bool[16]) vRegisterDefaultFlagsPacked;
|
||||
|
||||
// TODO make sure this goes fast
|
||||
|
||||
// Map default values
|
||||
VS_INPUT xIn = InitializeInputRegisters(xInput);
|
||||
|
||||
// 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.Normal = normalize(View.Normal);
|
||||
|
||||
// Projection transform
|
||||
Projection.Position = mul(View.Position, state.Transforms.Projection);
|
||||
// Normal unused...
|
||||
|
||||
// Projection transform - final position
|
||||
xOut.oPos = Projection.Position;
|
||||
|
||||
// Diffuse and specular for when lighting is disabled
|
||||
xOut.oD0 = Get(xIn, diffuse);
|
||||
xOut.oD1 = Get(xIn, specular);
|
||||
xOut.oB0 = Get(xIn, backDiffuse);
|
||||
xOut.oB1 = Get(xIn, backSpecular);
|
||||
|
||||
// Vertex lighting
|
||||
if (state.Modes.Lighting) // TODO : Remove this check by incorporating this boolean into the variables used below (set DiffuseMaterialSource to D3DMCS_COLOR1, SpecularMaterialSource to D3DMCS_COLOR2, all other to D3DMCS_MATERIAL and their colors and TotalLightsAmbient to zero, etc)
|
||||
{
|
||||
// Materials
|
||||
Material material = DoMaterial(0, diffuse, specular, xIn);
|
||||
Material backMaterial = DoMaterial(1, backDiffuse, backSpecular, xIn);
|
||||
|
||||
// Compute each lighting component
|
||||
const float2 powers = float2(material.Power, backMaterial.Power);
|
||||
const LightingOutput lighting = CalcLighting(powers);
|
||||
|
||||
// Frontface
|
||||
material.Specular.rgb *= lighting.Specular.Front;
|
||||
material.Diffuse.rgb *= lighting.Diffuse.Front;
|
||||
material.Ambient.rgb *= state.TotalLightsAmbient.Front;
|
||||
xOut.oD0 = float4(material.Diffuse.rgb + material.Ambient.rgb + material.Emissive.rgb, material.Diffuse.a);
|
||||
xOut.oD1 = float4(material.Specular.rgb, 0);
|
||||
|
||||
if(state.Modes.TwoSidedLighting) // TODO : Same as above, for backface lighting variables
|
||||
{
|
||||
// Backface
|
||||
backMaterial.Specular.rgb *= lighting.Specular.Back;
|
||||
backMaterial.Diffuse.rgb *= lighting.Diffuse.Back;
|
||||
backMaterial.Ambient.rgb *= state.TotalLightsAmbient.Back;
|
||||
xOut.oB0 = float4(backMaterial.Diffuse.rgb + backMaterial.Ambient.rgb + backMaterial.Emissive.rgb, backMaterial.Diffuse.a);
|
||||
xOut.oB1 = float4(backMaterial.Specular.rgb, 0);
|
||||
}
|
||||
}
|
||||
|
||||
// Colour
|
||||
xOut.oD0 = saturate(xOut.oD0);
|
||||
xOut.oD1 = saturate(xOut.oD1);
|
||||
xOut.oB0 = saturate(xOut.oB0);
|
||||
xOut.oB1 = saturate(xOut.oB1);
|
||||
|
||||
// Fog
|
||||
xOut.oFog = DoFog(xIn);
|
||||
|
||||
// Point Sprite
|
||||
xOut.oPts = DoPointSpriteSize();
|
||||
|
||||
// Texture coordinates
|
||||
xOut.oT0 = DoTexCoord(0, xIn) / xboxTextureScale[0];
|
||||
xOut.oT1 = DoTexCoord(1, xIn) / xboxTextureScale[1];
|
||||
xOut.oT2 = DoTexCoord(2, xIn) / xboxTextureScale[2];
|
||||
xOut.oT3 = DoTexCoord(3, xIn) / xboxTextureScale[3];
|
||||
|
||||
return xOut;
|
||||
}
|
|
@ -0,0 +1,157 @@
|
|||
// C++ / HLSL shared state block for fixed function support
|
||||
#ifdef __cplusplus
|
||||
#pragma once
|
||||
|
||||
#include <d3d9.h>
|
||||
#include <d3d9types.h> // for D3DFORMAT, D3DLIGHT9, etc
|
||||
#include <d3dx9math.h> // for D3DXVECTOR4, etc
|
||||
#include <array>
|
||||
|
||||
#define float4x4 D3DMATRIX
|
||||
#define float4 D3DXVECTOR4
|
||||
#define float3 D3DVECTOR
|
||||
#define float2 D3DXVECTOR2
|
||||
#define arr(name, type, length) std::array<type, length> name
|
||||
|
||||
#else
|
||||
// HLSL
|
||||
#define arr(name, type, length) type name[length]
|
||||
#define alignas(x)
|
||||
#define const static
|
||||
#endif // __cplusplus
|
||||
|
||||
namespace FixedFunctionVertexShader {
|
||||
// Fog depth is taken from the vertex, rather than generated
|
||||
const float FOG_DEPTH_NONE = 0;
|
||||
// Fog depth is the output Z coordinate
|
||||
const float FOG_DEPTH_Z = 1;
|
||||
// Fog depth is based on the output W coordinate (1 / W)
|
||||
const float FOG_DEPTH_W = 2;
|
||||
// Fog depth is based distance of the vertex from the eye position
|
||||
const float FOG_DEPTH_RANGE = 3;
|
||||
}
|
||||
|
||||
// Shared HLSL structures
|
||||
// Taking care with packing rules
|
||||
// In VS_3_0, packing works in mysterious ways
|
||||
// * Structs inside arrays are not packed
|
||||
// * Floats can't be packed at all (?)
|
||||
// We don't get documented packing until vs_4_0
|
||||
|
||||
struct Transforms {
|
||||
float4x4 View; // 0
|
||||
float4x4 Projection; // 1
|
||||
arr(Texture, float4x4, 4); // 2, 3, 4, 5
|
||||
// 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
|
||||
struct Light {
|
||||
// TODO in vs_4_0+ when floats are packable
|
||||
// Change colour values to float3
|
||||
// And put something useful in the alpha slot
|
||||
float4 Diffuse;
|
||||
float4 Specular;
|
||||
|
||||
// Viewspace light position
|
||||
alignas(16) float3 PositionV;
|
||||
alignas(16) float Range;
|
||||
|
||||
// Viewspace light direction (normalized)
|
||||
alignas(16) float3 DirectionVN;
|
||||
alignas(16) float Type; // 1=Point, 2=Spot, 3=Directional
|
||||
|
||||
alignas(16) float3 Attenuation;
|
||||
alignas(16) float Falloff;
|
||||
|
||||
alignas(16) float CosHalfPhi;
|
||||
// cos(theta/2) - cos(phi/2)
|
||||
alignas(16) float SpotIntensityDivisor;
|
||||
};
|
||||
|
||||
struct Material {
|
||||
float4 Diffuse;
|
||||
float4 Ambient;
|
||||
float4 Specular;
|
||||
float4 Emissive;
|
||||
|
||||
alignas(16) float Power;
|
||||
};
|
||||
|
||||
struct Modes {
|
||||
alignas(16) float AmbientMaterialSource;
|
||||
alignas(16) float DiffuseMaterialSource;
|
||||
alignas(16) float SpecularMaterialSource;
|
||||
alignas(16) float EmissiveMaterialSource;
|
||||
|
||||
alignas(16) float BackAmbientMaterialSource;
|
||||
alignas(16) float BackDiffuseMaterialSource;
|
||||
alignas(16) float BackSpecularMaterialSource;
|
||||
alignas(16) float BackEmissiveMaterialSource;
|
||||
|
||||
alignas(16) float Lighting;
|
||||
alignas(16) float TwoSidedLighting;
|
||||
// alignas(16) float SpecularEnable;
|
||||
alignas(16) float LocalViewer;
|
||||
|
||||
/// alignas(16) float ColorVertex;
|
||||
alignas(16) float VertexBlend_NrOfMatrices;
|
||||
alignas(16) float VertexBlend_CalcLastWeight; // Could be a bool in higer shader models
|
||||
alignas(16) float NormalizeNormals;
|
||||
};
|
||||
|
||||
struct PointSprite {
|
||||
alignas(16) float PointSize;
|
||||
alignas(16) float PointSizeMin;
|
||||
alignas(16) float PointSizeMax;
|
||||
// alignas(16) float PointScaleEnable;
|
||||
alignas(16) float XboxRenderTargetHeight;
|
||||
alignas(16) float3 ScaleABC;
|
||||
alignas(16) float RenderUpscaleFactor;
|
||||
};
|
||||
|
||||
struct TextureState {
|
||||
alignas(16) float TextureTransformFlagsCount;
|
||||
alignas(16) float TextureTransformFlagsProjected;
|
||||
alignas(16) float TexCoordIndex;
|
||||
alignas(16) float TexCoordIndexGen;
|
||||
};
|
||||
|
||||
struct Fog {
|
||||
alignas(16) float DepthMode;
|
||||
};
|
||||
|
||||
// Vertex lighting
|
||||
// Both frontface and backface lighting can be calculated
|
||||
struct TwoSidedColor
|
||||
{
|
||||
alignas(16) float3 Front;
|
||||
alignas(16) float3 Back;
|
||||
};
|
||||
|
||||
struct FixedFunctionVertexShaderState {
|
||||
alignas(16) Transforms Transforms;
|
||||
alignas(16) arr(Lights, Light, 8);
|
||||
alignas(16) TwoSidedColor TotalLightsAmbient;
|
||||
alignas(16) arr(Materials, Material, 2);
|
||||
alignas(16) Modes Modes;
|
||||
alignas(16) Fog Fog;
|
||||
alignas(16) arr(TextureStates, TextureState, 4);
|
||||
alignas(16) PointSprite PointSprite;
|
||||
alignas(16) float4 TexCoordComponentCount;
|
||||
};
|
||||
|
||||
#ifdef __cplusplus
|
||||
#undef float4x4
|
||||
#undef float4
|
||||
#undef float3
|
||||
#undef float2
|
||||
#undef arr
|
||||
#else // HLSL
|
||||
#undef arr
|
||||
#undef alignas
|
||||
#undef const
|
||||
#endif // __cplusplus
|
|
@ -396,14 +396,13 @@ void XboxRenderStateConverter::ApplyComplexRenderState(uint32_t State, uint32_t
|
|||
|
||||
switch (State) {
|
||||
case xbox::X_D3DRS_VERTEXBLEND:
|
||||
// convert from Xbox direct3d to PC direct3d enumeration
|
||||
if (Value <= 1) {
|
||||
Value = Value;
|
||||
} else if (Value == 3) {
|
||||
Value = 2;
|
||||
} else if (Value == 5) {
|
||||
Value = 3;
|
||||
} else {
|
||||
// convert from Xbox X_D3DVERTEXBLENDFLAGS to PC D3DVERTEXBLENDFLAGS enumeration
|
||||
switch (Value) {
|
||||
case xbox::X_D3DVBF_DISABLE: Value = D3DVBF_DISABLE; break;
|
||||
case xbox::X_D3DVBF_1WEIGHTS: Value = D3DVBF_1WEIGHTS; break;
|
||||
case xbox::X_D3DVBF_2WEIGHTS: Value = D3DVBF_2WEIGHTS; break;
|
||||
case xbox::X_D3DVBF_3WEIGHTS: Value = D3DVBF_3WEIGHTS; break;
|
||||
default:
|
||||
LOG_TEST_CASE("Unsupported D3DVERTEXBLENDFLAGS (%d)");
|
||||
return;
|
||||
}
|
||||
|
|
|
@ -4,6 +4,7 @@
|
|||
#include "core\kernel\init\CxbxKrnl.h"
|
||||
#include "core\kernel\support\Emu.h"
|
||||
|
||||
#include <fstream>
|
||||
#include <sstream>
|
||||
|
||||
extern const char* g_vs_model = vs_model_2_a;
|
||||
|
@ -266,24 +267,25 @@ HRESULT CompileHlsl(const std::string& hlsl, ID3DBlob** ppHostShader, const char
|
|||
|
||||
if (FAILED(hRet)) {
|
||||
LOG_TEST_CASE("Couldn't assemble vertex shader");
|
||||
//EmuLog(LOG_LEVEL::WARNING, "Couldn't assemble recompiled vertex shader");
|
||||
}
|
||||
}
|
||||
|
||||
// Determine the log level
|
||||
if (pErrors) {
|
||||
// Log HLSL compiler errors
|
||||
// Log errors from the initial compilation
|
||||
EmuLog(hlslErrorLogLevel, "%s", (char*)(pErrors->GetBufferPointer()));
|
||||
pErrors->Release();
|
||||
pErrors = nullptr;
|
||||
if (pErrorsCompatibility != nullptr) {
|
||||
pErrorsCompatibility->Release();
|
||||
pErrorsCompatibility = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
LOG_CHECK_ENABLED(LOG_LEVEL::DEBUG)
|
||||
if (g_bPrintfOn)
|
||||
// Failure to recompile in compatibility mode ignored for now
|
||||
if (pErrorsCompatibility != nullptr) {
|
||||
pErrorsCompatibility->Release();
|
||||
pErrorsCompatibility = nullptr;
|
||||
}
|
||||
|
||||
LOG_CHECK_ENABLED(LOG_LEVEL::DEBUG) {
|
||||
if (g_bPrintfOn) {
|
||||
if (!FAILED(hRet)) {
|
||||
// Log disassembly
|
||||
hRet = D3DDisassemble(
|
||||
|
@ -298,6 +300,8 @@ HRESULT CompileHlsl(const std::string& hlsl, ID3DBlob** ppHostShader, const char
|
|||
pErrors->Release();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return hRet;
|
||||
}
|
||||
|
@ -329,6 +333,31 @@ extern HRESULT EmuCompileShader
|
|||
return CompileHlsl(hlsl_str, ppHostShader, "CxbxVertexShaderTemplate.hlsl");
|
||||
}
|
||||
|
||||
extern void EmuCompileFixedFunction(ID3DBlob** ppHostShader)
|
||||
{
|
||||
static ID3DBlob* pShader = nullptr;
|
||||
|
||||
// TODO does this need to be thread safe?
|
||||
if (pShader == nullptr) {
|
||||
// Determine the filename and directory for the fixed function shader
|
||||
auto hlslDir = std::filesystem::path(szFilePath_CxbxReloaded_Exe)
|
||||
.parent_path()
|
||||
.append("hlsl");
|
||||
|
||||
auto sourceFile = hlslDir.append("FixedFunctionVertexShader.hlsl").string();
|
||||
|
||||
// Load the shader into a string
|
||||
std::ifstream hlslStream(sourceFile);
|
||||
std::stringstream hlsl;
|
||||
hlsl << hlslStream.rdbuf();
|
||||
|
||||
// Compile the shader
|
||||
CompileHlsl(hlsl.str(), &pShader, sourceFile.c_str());
|
||||
}
|
||||
|
||||
*ppHostShader = pShader;
|
||||
};
|
||||
|
||||
static ID3DBlob* pPassthroughShader = nullptr;
|
||||
|
||||
extern HRESULT EmuCompileXboxPassthrough(ID3DBlob** ppHostShader)
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
#define DIRECT3D9VERTEXSHADER_H
|
||||
|
||||
#include "core\hle\D3D8\XbVertexShader.h"
|
||||
#include "FixedFunctionVertexShaderState.hlsli"
|
||||
|
||||
enum class ShaderType {
|
||||
Empty = 0,
|
||||
|
@ -20,6 +21,8 @@ extern HRESULT EmuCompileShader
|
|||
ID3DBlob** ppHostShader
|
||||
);
|
||||
|
||||
extern void EmuCompileFixedFunction(ID3DBlob** ppHostShader);
|
||||
|
||||
extern HRESULT EmuCompileXboxPassthrough(ID3DBlob** ppHostShader);
|
||||
|
||||
#endif
|
||||
|
|
|
@ -0,0 +1,157 @@
|
|||
#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];
|
||||
}
|
|
@ -0,0 +1,46 @@
|
|||
#ifndef FIXEDFUNCTIONSTATE_H
|
||||
#define FIXEDFUNCTIONSTATE_H
|
||||
|
||||
#include "XbD3D8Types.h"
|
||||
#include <array>
|
||||
|
||||
class D3D8LightState {
|
||||
public:
|
||||
std::array<xbox::X_D3DLIGHT8, 4096> Lights;
|
||||
|
||||
// The indices of last 8 enabled lights
|
||||
// From least recently to most recently enabled
|
||||
// -1 represents empty light slots
|
||||
// which always appear after enabled lights
|
||||
std::array<int, 8> EnabledLights;
|
||||
|
||||
// The number of enabled lights
|
||||
uint32_t EnabledLightCount = 0;
|
||||
|
||||
D3D8LightState();
|
||||
|
||||
// Enable a light
|
||||
void EnableLight(uint32_t index, bool enable);
|
||||
};
|
||||
|
||||
class D3D8TransformState {
|
||||
public:
|
||||
D3D8TransformState();
|
||||
void SetTransform(xbox::X_D3DTRANSFORMSTATETYPE state, const D3DMATRIX* pMatrix);
|
||||
D3DMATRIX* GetWorldView(unsigned i);
|
||||
D3DMATRIX* GetWorldViewInverseTranspose(unsigned i);
|
||||
|
||||
// The transforms set by the Xbox title
|
||||
std::array<D3DMATRIX, xbox::X_D3DTS_MAX> Transforms;
|
||||
|
||||
private:
|
||||
void RecalculateDependentMatrices(unsigned i);
|
||||
|
||||
std::array<bool, 4> bWorldViewDirty;
|
||||
// Combines world/view matrices
|
||||
std::array<D3DMATRIX, 4> WorldView;
|
||||
// World/view inverse transpose for lighting calculations
|
||||
std::array<D3DMATRIX, 4> WorldViewInverseTranspose;
|
||||
};
|
||||
|
||||
#endif
|
|
@ -94,25 +94,25 @@ 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
|
||||
if (State >= 256 && State <= 511) {
|
||||
return D3DTS_WORLDMATRIX(State - 256);
|
||||
return D3DTS_WORLDMATRIX(State - 256);
|
||||
}
|
||||
|
||||
CxbxKrnlCleanupEx(LOG_PREFIX_D3DCVT, "Unknown Transform State Type (%d)", State);
|
||||
|
@ -1796,7 +1796,7 @@ typedef enum _TXBType {
|
|||
xtD3DTEXTUREOP, // Used for TextureStageState X_D3DTSS_COLOROP and X_D3DTSS_ALPHAOP
|
||||
xtD3DTEXTURESTAGESTATETYPE,
|
||||
xtD3DTRANSFORMSTATETYPE,
|
||||
xtD3DVERTEXBLENDFLAGS,
|
||||
xtD3DVERTEXBLENDFLAGS, // Used for X_D3DRS_VERTEXBLEND
|
||||
xtD3DVSDE,
|
||||
xtD3DWRAP,
|
||||
xtDWORD,
|
||||
|
|
|
@ -1007,6 +1007,18 @@ constexpr DWORD X_D3DCOLORWRITEENABLE_ALL = 0x01010101; // Xbox ext.
|
|||
// deferred texture stage state "unknown" flag
|
||||
#define X_D3DTSS_UNK 0x7fffffff
|
||||
|
||||
typedef enum _D3DVERTEXBLENDFLAGS
|
||||
{
|
||||
X_D3DVBF_DISABLE = 0, // 1 matrix, 0 weights => final weight effectively 1 (Disable vertex blending)
|
||||
X_D3DVBF_1WEIGHTS = 1, // 2 matrices, 1 weights => final weight calculated
|
||||
X_D3DVBF_2WEIGHTS = 3, // 3 matrices, 2 weights => final weight calculated
|
||||
X_D3DVBF_3WEIGHTS = 5, // 4 matrices, 3 weights => final weight calculated
|
||||
X_D3DVBF_2WEIGHTS2MATRICES = 2, // 2 matrices, 2 weights (Xbox ext.)
|
||||
X_D3DVBF_3WEIGHTS3MATRICES = 4, // 3 matrices, 3 weights (Xbox ext.)
|
||||
X_D3DVBF_4WEIGHTS4MATRICES = 6, // 4 matrices, 4 weights (Xbox ext.)
|
||||
X_D3DVBF_FORCE_DWORD = 0x7fffffff
|
||||
} X_D3DVERTEXBLENDFLAGS;
|
||||
|
||||
typedef DWORD X_VERTEXSHADERCONSTANTMODE;
|
||||
|
||||
// Xbox vertex shader constant modes
|
||||
|
@ -1305,6 +1317,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;
|
||||
|
||||
//
|
||||
|
|
|
@ -844,7 +844,7 @@ void CxbxImpl_End()
|
|||
// Arrange for g_InlineVertexBuffer_AttributeFormat to be returned in CxbxGetVertexDeclaration,
|
||||
// so that our above composed declaration will be used for the next draw :
|
||||
g_InlineVertexBuffer_DeclarationOverride = true;
|
||||
// Note, that g_Xbox_VertexShader_IsFixedFunction should be left untouched,
|
||||
// Note, that g_Xbox_VertexShaderMode should be left untouched,
|
||||
// because except for the declaration override, the Xbox shader (either FVF
|
||||
// or a program, or even passthrough shaders) should still be in effect!
|
||||
|
||||
|
|
|
@ -58,9 +58,9 @@ extern XboxRenderStateConverter XboxRenderStates; // Declared in Direct3D9.cpp
|
|||
xbox::X_STREAMINPUT g_Xbox_SetVertexShaderInput_Data[X_VSH_MAX_STREAMS] = { 0 }; // Active when g_Xbox_SetVertexShaderInput_Count > 0
|
||||
xbox::X_VERTEXATTRIBUTEFORMAT g_Xbox_SetVertexShaderInput_Attributes = { 0 }; // Read by GetXboxVertexAttributes when g_Xbox_SetVertexShaderInput_Count > 0
|
||||
|
||||
// Variables set by [D3DDevice|CxbxImpl]_SetVertexShader() and [D3DDevice|CxbxImpl]_SelectVertexShader() :
|
||||
bool g_Xbox_VertexShader_IsFixedFunction = true;
|
||||
bool g_Xbox_VertexShader_IsPassthrough = false;
|
||||
VertexShaderMode g_Xbox_VertexShaderMode = VertexShaderMode::FixedFunction;
|
||||
bool g_UseFixedFunctionVertexShader = true;
|
||||
|
||||
xbox::dword_xt g_Xbox_VertexShader_Handle = 0;
|
||||
#ifdef CXBX_USE_GLOBAL_VERTEXSHADER_POINTER // TODO : Would this be more accurate / simpler?
|
||||
xbox::X_D3DVertexShader *g_Xbox_VertexShader_Ptr = nullptr;
|
||||
|
@ -323,6 +323,23 @@ static DWORD* CxbxGetVertexShaderTokens(xbox::X_D3DVertexShader* pXboxVertexShad
|
|||
return &pXboxVertexShader->ProgramAndConstants[0];
|
||||
}
|
||||
|
||||
int GetXboxVertexDataComponentCount(int d3dvsdt) {
|
||||
using namespace xbox;
|
||||
switch (d3dvsdt) {
|
||||
case X_D3DVSDT_NORMPACKED3:
|
||||
return 3;
|
||||
case X_D3DVSDT_FLOAT2H:
|
||||
LOG_TEST_CASE("Attempting to use component count for X_D3DVSDT_FLOAT2H, which uses an odd (value, value, 0, value) layout");
|
||||
// This is a bit of an odd case. Will call it 4 since it writes a value to the 4th component...
|
||||
return 4;
|
||||
default:
|
||||
// Most data types have a representation consistent with the number of components
|
||||
const int countMask = 0x7;
|
||||
const int countShift = 4;
|
||||
return (d3dvsdt >> countShift) & countMask;
|
||||
}
|
||||
}
|
||||
|
||||
extern bool g_InlineVertexBuffer_DeclarationOverride; // TMP glue
|
||||
extern xbox::X_VERTEXATTRIBUTEFORMAT g_InlineVertexBuffer_AttributeFormat; // TMP glue
|
||||
|
||||
|
@ -1075,7 +1092,9 @@ VertexDeclarationKey GetXboxVertexAttributesKey(xbox::X_VERTEXATTRIBUTEFORMAT* p
|
|||
auto attributeHash = ComputeHash((void*)pXboxVertexAttributeFormat, sizeof(xbox::X_VERTEXATTRIBUTEFORMAT));
|
||||
// For now, we use different declarations depending on if the fixed function pipeline
|
||||
// is in use, even if the attributes are the same
|
||||
return attributeHash ^ (VertexDeclarationKey)g_Xbox_VertexShader_IsFixedFunction;
|
||||
return g_Xbox_VertexShaderMode == VertexShaderMode::FixedFunction
|
||||
? attributeHash
|
||||
: attributeHash ^ 1;
|
||||
}
|
||||
|
||||
std::unordered_map<VertexDeclarationKey, CxbxVertexDeclaration*> g_CxbxVertexDeclarations;
|
||||
|
@ -1122,18 +1141,27 @@ void CxbxUpdateHostVertexShader()
|
|||
|
||||
LOG_INIT; // Allows use of DEBUG_D3DRESULT
|
||||
|
||||
if (g_Xbox_VertexShader_IsFixedFunction) {
|
||||
HRESULT hRet = g_pD3DDevice->SetVertexShader(nullptr);
|
||||
DEBUG_D3DRESULT(hRet, "g_pD3DDevice->SetVertexShader");
|
||||
// TODO : Once available, start using host Fixed Function HLSL shader
|
||||
// instead of using deprecated host fixed function (by setting a null
|
||||
// vertex shader).
|
||||
// As for the required host vertex declaration :
|
||||
// CxbxUpdateHostVertexDeclaration already been
|
||||
// called, which sets host vertex declaration based on the
|
||||
// declaration that XboxVertexShaderFromFVF generated.
|
||||
if (g_Xbox_VertexShaderMode == VertexShaderMode::FixedFunction) {
|
||||
IDirect3DVertexShader* fixedFunctionShader = nullptr;
|
||||
HRESULT hRet;
|
||||
|
||||
if (g_UseFixedFunctionVertexShader) {
|
||||
static IDirect3DVertexShader* ffHlsl = nullptr;
|
||||
if (ffHlsl == nullptr) {
|
||||
ID3DBlob* pBlob = nullptr;
|
||||
EmuCompileFixedFunction(&pBlob);
|
||||
if (pBlob) {
|
||||
hRet = g_pD3DDevice->CreateVertexShader((DWORD*)pBlob->GetBufferPointer(), &ffHlsl);
|
||||
if (FAILED(hRet)) CxbxKrnlCleanup("Failed to create fixed-function shader");
|
||||
}
|
||||
}
|
||||
fixedFunctionShader = ffHlsl;
|
||||
}
|
||||
|
||||
hRet = g_pD3DDevice->SetVertexShader(fixedFunctionShader);
|
||||
if (FAILED(hRet)) CxbxKrnlCleanup("Failed to set fixed-function shader");
|
||||
}
|
||||
else if (g_Xbox_VertexShader_IsPassthrough && g_bUsePassthroughHLSL) {
|
||||
else if (g_Xbox_VertexShaderMode == VertexShaderMode::Passthrough && g_bUsePassthroughHLSL) {
|
||||
if (passthroughshader == nullptr) {
|
||||
ID3DBlob* pBlob = nullptr;
|
||||
EmuCompileXboxPassthrough(&pBlob);
|
||||
|
@ -1247,7 +1275,7 @@ CxbxVertexDeclaration* CxbxGetVertexDeclaration()
|
|||
// Convert Xbox vertex attributes towards host Direct3D 9 vertex element
|
||||
D3DVERTEXELEMENT* pRecompiledVertexElements = EmuRecompileVshDeclaration(
|
||||
pXboxVertexAttributeFormat,
|
||||
g_Xbox_VertexShader_IsFixedFunction,
|
||||
g_Xbox_VertexShaderMode == VertexShaderMode::FixedFunction,
|
||||
pCxbxVertexDeclaration);
|
||||
|
||||
// Create the vertex declaration
|
||||
|
@ -1337,6 +1365,8 @@ void CxbxImpl_SelectVertexShader(DWORD Handle, DWORD Address)
|
|||
// Either way, the given address slot is selected as the start of the current vertex shader program
|
||||
g_Xbox_VertexShader_FunctionSlots_StartAddress = Address;
|
||||
|
||||
g_Xbox_VertexShaderMode = VertexShaderMode::ShaderProgram;
|
||||
|
||||
if (Handle) {
|
||||
if (!VshHandleIsVertexShader(Handle))
|
||||
LOG_TEST_CASE("Non-zero handle must be a VertexShader!");
|
||||
|
@ -1345,8 +1375,6 @@ void CxbxImpl_SelectVertexShader(DWORD Handle, DWORD Address)
|
|||
g_Xbox_VertexShader_Ptr = VshHandleToXboxVertexShader(Handle);
|
||||
#endif
|
||||
g_Xbox_VertexShader_Handle = Handle;
|
||||
g_Xbox_VertexShader_IsFixedFunction = false;
|
||||
g_Xbox_VertexShader_IsPassthrough = false;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1417,6 +1445,37 @@ void CxbxImpl_LoadVertexShader(DWORD Handle, DWORD Address)
|
|||
}
|
||||
}
|
||||
|
||||
// Set default values for attributes missing from vertex declaration
|
||||
void SetFixedFunctionDefaultVertexAttributes(DWORD vshFlags) {
|
||||
// Test case: Mechassault (skybox)
|
||||
// Test case: KOTOR (overlay)
|
||||
auto decl = CxbxGetVertexDeclaration();
|
||||
for (int i = 0; i < xbox::X_D3DVSDE_TEXCOORD3; i++) {
|
||||
if (decl->vRegisterInDeclaration[i]) {
|
||||
continue; // only reset missing attributes
|
||||
}
|
||||
|
||||
const float white[4] = { 1, 1, 1, 1 };
|
||||
const float black[4] = { 0, 0, 0, 0 };
|
||||
const float unset[4] = { 0, 0, 0, 1 };
|
||||
const float* value = unset;
|
||||
|
||||
// Account for flags that override this reset behaviour
|
||||
if (i == xbox::X_D3DVSDE_DIFFUSE && !(vshFlags & X_VERTEXSHADER_FLAG_HASDIFFUSE) ||
|
||||
i == xbox::X_D3DVSDE_BACKDIFFUSE && !(vshFlags & X_VERTEXSHADER_FLAG_HASBACKDIFFUSE)) {
|
||||
value = white;
|
||||
}
|
||||
else if (i == xbox::X_D3DVSDE_SPECULAR && !(vshFlags & X_VERTEXSHADER_FLAG_HASSPECULAR) ||
|
||||
i == xbox::X_D3DVSDE_BACKSPECULAR && !(vshFlags & X_VERTEXSHADER_FLAG_HASBACKSPECULAR)) {
|
||||
value = black;
|
||||
}
|
||||
|
||||
// Note : We avoid calling CxbxImpl_SetVertexData4f here, as that would
|
||||
// start populating g_InlineVertexBuffer_Table, which is not our intent here.
|
||||
CxbxSetVertexAttribute(i, value[0], value[1], value[2], value[3]);
|
||||
}
|
||||
}
|
||||
|
||||
void CxbxImpl_SetVertexShader(DWORD Handle)
|
||||
{
|
||||
LOG_INIT; // Allows use of DEBUG_D3DRESULT
|
||||
|
@ -1433,7 +1492,6 @@ void CxbxImpl_SetVertexShader(DWORD Handle)
|
|||
HRESULT hRet = D3D_OK;
|
||||
|
||||
xbox::X_D3DVertexShader* pXboxVertexShader = CxbxGetXboxVertexShaderForHandle(Handle);
|
||||
g_Xbox_VertexShader_IsPassthrough = false;
|
||||
|
||||
if ((pXboxVertexShader->Flags & g_X_VERTEXSHADER_FLAG_VALID_MASK) != pXboxVertexShader->Flags) {
|
||||
LOG_TEST_CASE("Unknown vertex shader flag");
|
||||
|
@ -1453,8 +1511,8 @@ void CxbxImpl_SetVertexShader(DWORD Handle)
|
|||
LOG_TEST_CASE("g_Xbox_VertexShader_FunctionSlots_StartAddress != 0");
|
||||
bHackCallSelectAgain = true;
|
||||
}
|
||||
if (g_Xbox_VertexShader_IsFixedFunction != false) {
|
||||
LOG_TEST_CASE("g_Xbox_VertexShader_IsFixedFunction != false");
|
||||
if (g_Xbox_VertexShaderMode != VertexShaderMode::ShaderProgram) {
|
||||
LOG_TEST_CASE("Not in shader program mode after SetVertexShader trampoline");
|
||||
bHackCallSelectAgain = true;
|
||||
}
|
||||
|
||||
|
@ -1463,7 +1521,6 @@ void CxbxImpl_SetVertexShader(DWORD Handle)
|
|||
// _SelectVertexShader isn't applied;
|
||||
// 'solve' that by calling it here instead.
|
||||
CxbxImpl_SelectVertexShader(Handle, 0);
|
||||
g_Xbox_VertexShader_IsFixedFunction = false;
|
||||
}
|
||||
#endif
|
||||
} else {
|
||||
|
@ -1475,31 +1532,16 @@ void CxbxImpl_SetVertexShader(DWORD Handle)
|
|||
g_Xbox_VertexShader_Handle = Handle;
|
||||
g_Xbox_VertexShader_FunctionSlots_StartAddress = 0;
|
||||
|
||||
// Only when there's no program, set default values for attributes missing from vertex shader
|
||||
// Note : We avoid calling CxbxImpl_SetVertexData4f here, as that would
|
||||
// start populating g_InlineVertexBuffer_Table, which is not our intend here.
|
||||
if (!(pXboxVertexShader->Flags & X_VERTEXSHADER_FLAG_HASDIFFUSE)) {
|
||||
CxbxSetVertexAttribute(xbox::X_D3DVSDE_DIFFUSE, 1, 1, 1, 1);
|
||||
}
|
||||
if (!(pXboxVertexShader->Flags & X_VERTEXSHADER_FLAG_HASSPECULAR)) {
|
||||
CxbxSetVertexAttribute(xbox::X_D3DVSDE_SPECULAR, 0, 0, 0, 0);
|
||||
}
|
||||
if (!(pXboxVertexShader->Flags & X_VERTEXSHADER_FLAG_HASBACKDIFFUSE)) {
|
||||
CxbxSetVertexAttribute(xbox::X_D3DVSDE_BACKDIFFUSE, 1, 1, 1, 1);
|
||||
}
|
||||
if (!(pXboxVertexShader->Flags & X_VERTEXSHADER_FLAG_HASBACKSPECULAR)) {
|
||||
CxbxSetVertexAttribute(xbox::X_D3DVSDE_BACKSPECULAR, 0, 0, 0, 0);
|
||||
}
|
||||
SetFixedFunctionDefaultVertexAttributes(pXboxVertexShader->Flags);
|
||||
|
||||
// Switch to passthrough program, if so required
|
||||
if (pXboxVertexShader->Flags & X_VERTEXSHADER_FLAG_PASSTHROUGH) {
|
||||
CxbxSetVertexShaderPassthroughProgram();
|
||||
g_Xbox_VertexShader_IsFixedFunction = false;
|
||||
g_Xbox_VertexShader_IsPassthrough = true;
|
||||
g_Xbox_VertexShaderMode = VertexShaderMode::Passthrough;
|
||||
} else {
|
||||
// Test-case : Many XDK samples, Crazy taxi 3
|
||||
//LOG_TEST_CASE("Other or no vertex shader flags");
|
||||
g_Xbox_VertexShader_IsFixedFunction = true;
|
||||
g_Xbox_VertexShaderMode = VertexShaderMode::FixedFunction;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -80,6 +80,17 @@ typedef struct _CxbxVertexDeclaration
|
|||
}
|
||||
CxbxVertexDeclaration;
|
||||
|
||||
enum class VertexShaderMode {
|
||||
FixedFunction,
|
||||
// When titles use Xbox fixed function with pre-transformed vertices
|
||||
// it actually uses a special "passthrough" shader program
|
||||
Passthrough,
|
||||
ShaderProgram
|
||||
};
|
||||
|
||||
extern VertexShaderMode g_Xbox_VertexShaderMode;
|
||||
extern bool g_UseFixedFunctionVertexShader;
|
||||
|
||||
// Intermediate vertex shader structures
|
||||
|
||||
enum VSH_OREG_NAME {
|
||||
|
@ -200,6 +211,9 @@ extern size_t GetVshFunctionSize(const xbox::dword_xt* pXboxFunction);
|
|||
inline boolean VshHandleIsVertexShader(DWORD Handle) { return (Handle & X_D3DFVF_RESERVED0) ? TRUE : FALSE; }
|
||||
inline xbox::X_D3DVertexShader *VshHandleToXboxVertexShader(DWORD Handle) { return (xbox::X_D3DVertexShader *)(Handle & ~X_D3DFVF_RESERVED0);}
|
||||
|
||||
// Get the number of components represented by the given xbox vertex data type
|
||||
extern int GetXboxVertexDataComponentCount(int d3dvsdt);
|
||||
|
||||
extern bool g_Xbox_VertexShader_IsFixedFunction;
|
||||
|
||||
extern CxbxVertexDeclaration* CxbxGetVertexDeclaration();
|
||||
|
@ -214,4 +228,5 @@ extern void CxbxImpl_SetVertexShaderInput(DWORD Handle, UINT StreamCount, xbox::
|
|||
extern void CxbxImpl_SetVertexShaderConstant(INT Register, PVOID pConstantData, DWORD ConstantCount);
|
||||
extern void CxbxImpl_DeleteVertexShader(DWORD Handle);
|
||||
extern void CxbxVertexShaderSetFlags();
|
||||
extern HRESULT SetVertexShader(IDirect3DVertexShader* pShader);
|
||||
#endif
|
||||
|
|
Loading…
Reference in New Issue