Xbox fixed function vertex shader (not yet enabled)

The shader should behave similarly to D3D9's fixed function pipeline
But supports Xbox extensions and helps to move off D3D9
Co-authored with PatrickVL
This commit is contained in:
Anthony Miles 2020-10-27 22:07:12 +13:00 committed by Anthony
parent 99a96e675d
commit 3bfad0327f
11 changed files with 1058 additions and 2 deletions

View File

@ -117,7 +117,10 @@ 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"

View File

@ -194,3 +194,14 @@ 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(HlslHeaders ${CXBXR_HEADER_EMU})
list(FILTER HlslHeaders INCLUDE REGEX ".*\\.hlsl$")
set_source_files_properties(
${HlslHeaders}
PROPERTIES
HEADER_FILE_ONLY TRUE
VS_TOOL_OVERRIDE "None"
)

View File

@ -166,3 +166,14 @@ 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(HlslHeaders ${CXBXR_HEADER_EMU})
list(FILTER HlslHeaders INCLUDE REGEX ".*\\.hlsl$")
set_source_files_properties(
${HlslHeaders}
PROPERTIES
HEADER_FILE_ONLY TRUE
VS_TOOL_OVERRIDE "None"
)

View File

@ -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})

View File

@ -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,9 @@
XboxRenderStateConverter XboxRenderStates;
XboxTextureStateConverter XboxTextureStates;
D3D8LightState d3d8LightState = D3D8LightState();
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;
@ -6263,6 +6267,179 @@ 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, D3DXMATRIX viewTransform) {
if (d3dLightIndex == -1) {
pShaderLight->Type = 0; // Disable the light
return;
}
auto d3dLight = &d3d8LightState.Lights[d3dLightIndex];
// 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);
// Map D3D light to state struct
pShaderLight->Type = (float)((int)d3dLight->Type);
pShaderLight->Diffuse = toVector(d3dLight->Diffuse);
pShaderLight->Specular = toVector(d3dLight->Specular);
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;
// Lighting
ffShaderState.Modes.Lighting = (float)XboxRenderStates.GetXboxRenderState(X_D3DRS_LIGHTING);
ffShaderState.Modes.TwoSidedLighting = (float)XboxRenderStates.GetXboxRenderState(X_D3DRS_TWOSIDEDLIGHTING);
ffShaderState.Modes.SpecularEnable = (float)XboxRenderStates.GetXboxRenderState(X_D3DRS_SPECULARENABLE);
ffShaderState.Modes.LocalViewer = (float)XboxRenderStates.GetXboxRenderState(X_D3DRS_LOCALVIEWER);
ffShaderState.Modes.ColorVertex = (float)XboxRenderStates.GetXboxRenderState(X_D3DRS_COLORVERTEX);
D3DXVECTOR4 Ambient = toVector(XboxRenderStates.GetXboxRenderState(X_D3DRS_AMBIENT));
D3DXVECTOR4 BackAmbient = toVector(XboxRenderStates.GetXboxRenderState(X_D3DRS_BACKAMBIENT));
// Material sources
ffShaderState.Modes.AmbientMaterialSource = (float)XboxRenderStates.GetXboxRenderState(X_D3DRS_AMBIENTMATERIALSOURCE);
ffShaderState.Modes.DiffuseMaterialSource = (float)XboxRenderStates.GetXboxRenderState(X_D3DRS_DIFFUSEMATERIALSOURCE);
ffShaderState.Modes.SpecularMaterialSource = (float)XboxRenderStates.GetXboxRenderState(X_D3DRS_SPECULARMATERIALSOURCE);
ffShaderState.Modes.EmissiveMaterialSource = (float)XboxRenderStates.GetXboxRenderState(X_D3DRS_EMISSIVEMATERIALSOURCE);
ffShaderState.Modes.BackAmbientMaterialSource = (float)XboxRenderStates.GetXboxRenderState(X_D3DRS_BACKAMBIENTMATERIALSOURCE);
ffShaderState.Modes.BackDiffuseMaterialSource = (float)XboxRenderStates.GetXboxRenderState(X_D3DRS_BACKDIFFUSEMATERIALSOURCE);
ffShaderState.Modes.BackSpecularMaterialSource = (float)XboxRenderStates.GetXboxRenderState(X_D3DRS_BACKSPECULARMATERIALSOURCE);
ffShaderState.Modes.BackEmissiveMaterialSource = (float)XboxRenderStates.GetXboxRenderState(X_D3DRS_BACKEMISSIVEMATERIALSOURCE);
// Point sprites
auto pointSize = XboxRenderStates.GetXboxRenderState(X_D3DRS_POINTSIZE);
ffShaderState.PointSprite.PointSize = *reinterpret_cast<float*>(&pointSize);
ffShaderState.PointSprite.PointScaleEnable = (float)XboxRenderStates.GetXboxRenderState(X_D3DRS_POINTSCALEENABLE);
ffShaderState.PointSprite.RenderTargetHeight = GetPixelContainerHeight(g_pXbox_RenderTarget);
auto scaleA = XboxRenderStates.GetXboxRenderState(X_D3DRS_POINTSCALE_A);
ffShaderState.PointSprite.ScaleA = *reinterpret_cast<float*>(&scaleA);
auto scaleB = XboxRenderStates.GetXboxRenderState(X_D3DRS_POINTSCALE_B);
ffShaderState.PointSprite.ScaleB = *reinterpret_cast<float*>(&scaleB);
auto scaleC = XboxRenderStates.GetXboxRenderState(X_D3DRS_POINTSCALE_C);
ffShaderState.PointSprite.ScaleC = *reinterpret_cast<float*>(&scaleC);
// 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);
}
// Misc flags
ffShaderState.Modes.VertexBlend = (float)XboxRenderStates.GetXboxRenderState(X_D3DRS_VERTEXBLEND);
ffShaderState.Modes.NormalizeNormals = (float)XboxRenderStates.GetXboxRenderState(X_D3DRS_NORMALIZENORMALS);
// Update lights
auto LightAmbient = D3DXVECTOR4(0.f, 0.f, 0.f, 0.f);
D3DXMATRIX rowMajorViewTransform;
D3DXMatrixTranspose(&rowMajorViewTransform, (D3DXMATRIX*)&ffShaderState.Transforms.View);
for (size_t i = 0; i < ffShaderState.Lights.size(); i++) {
UpdateFixedFunctionShaderLight(d3d8LightState.EnabledLights[i], &ffShaderState.Lights[i], &LightAmbient, rowMajorViewTransform);
}
ffShaderState.AmbientPlusLightAmbient = Ambient + LightAmbient;
ffShaderState.BackAmbientPlusLightAmbient = BackAmbient + LightAmbient;
// 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
// ******************************************************************
@ -6405,9 +6582,15 @@ void CxbxImpl_SetTransform
{
LOG_INIT
State = EmuXB2PC_D3DTS(State);
// Transpose row major to column major for HLSL
D3DXMATRIX hlslMatrix;
D3DXMatrixTranspose(&hlslMatrix, (D3DXMATRIX*)pMatrix);
// Save to vertex shader state
((D3DXMATRIX*)&ffShaderState.Transforms)[State] = hlslMatrix;
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");
}
@ -7897,6 +8080,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");
@ -7913,6 +8098,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");
}
@ -7933,6 +8124,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");

View File

@ -0,0 +1,635 @@
#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 World; // Vertex worldspace transform
static TransformInfo View; // Vertex viewspace/cameraspace transform
static TransformInfo Projection; // Vertex projection transform
// Vertex lighting
// Both frontface and backface lighting can be calculated
struct LightingInfo
{
float3 Front;
float3 Back;
};
// Final lighting output
struct LightingOutput
{
LightingInfo Diffuse;
LightingInfo Specular;
};
LightingInfo DoSpecular(const float3 toLightVN, const float3 toViewerVN, const float2 powers, const float4 lightSpecular)
{
LightingInfo o;
o.Front = o.Back = float3(0, 0, 0);
// Specular
if (state.Modes.SpecularEnable)
{
// Blinn-Phong
// https://learnopengl.com/Advanced-Lighting/Advanced-Lighting
float3 halfway = normalize(toViewerVN + toLightVN);
float NdotH = dot(View.Normal, halfway);
float3 frontSpecular = pow(abs(NdotH), powers[0]) * lightSpecular.rgb;
float3 backSpecular = pow(abs(NdotH), powers[1]) * lightSpecular.rgb;
if (NdotH >= 0)
o.Front = frontSpecular;
else
o.Back = backSpecular;
}
return o;
}
// useful reference https://drivers.amd.com/misc/samples/dx9/FixedFuncShader.pdf
LightingOutput DoPointLight(const Light l, const float3 toViewerVN, 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);
// Diffuse
float3 toLightV = l.PositionV - View.Position.xyz;
float lightDist = length(toLightV);
float3 toLightVN = normalize(toLightV);
// A(Constant) + A(Linear) * dist + A(Exp) * dist^2
float attenuation =
1 / (l.Attenuation[0]
+ l.Attenuation[1] * lightDist
+ l.Attenuation[2] * lightDist * lightDist);
// Range cutoff
if (lightDist > l.Range)
attenuation = 0;
float NdotL = dot(View.Normal, toLightVN);
float3 lightDiffuse = abs(NdotL) * attenuation * l.Diffuse.rgb;
if (NdotL >= 0)
o.Diffuse.Front = lightDiffuse;
else
o.Diffuse.Back = lightDiffuse;
// Specular
o.Specular = DoSpecular(toLightVN, toViewerVN, powers, l.Specular);
o.Specular.Front *= attenuation;
o.Specular.Back *= attenuation;
return o;
}
LightingOutput DoSpotLight(const Light l, const float3 toViewerVN, 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);
// Diffuse
float3 toLightV = l.PositionV - View.Position.xyz;
float lightDist = length(toLightV);
float3 toLightVN = normalize(toLightV);
float3 toVertexVN = -toLightVN;
// https://docs.microsoft.com/en-us/windows/win32/direct3d9/light-types
float cosAlpha = dot(l.DirectionVN, toVertexVN);
// 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);
// A(Constant) + A(Linear) * dist + A(Exp) * dist^2
float attenuation =
1 / (l.Attenuation[0]
+ l.Attenuation[1] * lightDist
+ l.Attenuation[2] * lightDist * lightDist);
// Range cutoff
if (lightDist > l.Range)
attenuation = 0;
float NdotL = dot(View.Normal, toLightVN);
float3 lightDiffuse = abs(NdotL) * attenuation * l.Diffuse.rgb * spotIntensity;
if (NdotL >= 0)
o.Diffuse.Front = lightDiffuse;
else
o.Diffuse.Back = lightDiffuse;
// Specular
o.Specular = DoSpecular(toLightVN, toViewerVN, powers, l.Specular);
o.Specular.Front *= attenuation;
o.Specular.Back *= attenuation;
return o;
}
LightingOutput DoDirectionalLight(const Light l, const float3 toViewerVN, 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);
// Diffuse
// Intensity from N . L
float3 toLightVN = -l.DirectionVN;
float NdotL = dot(View.Normal, toLightVN);
float3 lightDiffuse = abs(NdotL * l.Diffuse.rgb);
// Apply light contribution to front or back face
// as the case may be
if (NdotL >= 0)
o.Diffuse.Front = lightDiffuse;
else
o.Diffuse.Back = lightDiffuse;
// Specular
o.Specular = DoSpecular(toLightVN, toViewerVN, powers, l.Specular);
return o;
}
LightingOutput CalcLighting(const float2 powers)
{
const int LIGHT_TYPE_NONE = 0;
const int LIGHT_TYPE_POINT = 1;
const int LIGHT_TYPE_SPOT = 2;
const int LIGHT_TYPE_DIRECTIONAL = 3;
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);
float3 toViewerVN = state.Modes.LocalViewer
? float3(0, 0, 1)
: normalize(-View.Position.xyz);
for (uint i = 0; i < 8; i++)
{
const Light currentLight = state.Lights[i];
LightingOutput currentLightOutput;
if(currentLight.Type == LIGHT_TYPE_POINT)
currentLightOutput = DoPointLight(currentLight, toViewerVN, powers);
else if(currentLight.Type == LIGHT_TYPE_SPOT)
currentLightOutput = DoSpotLight(currentLight, toViewerVN, powers);
else if (currentLight.Type == LIGHT_TYPE_DIRECTIONAL)
currentLightOutput = DoDirectionalLight(currentLight, toViewerVN, powers);
else
continue;
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 DoWorldTransform(const float4 position, const float3 normal, const float4 blendWeights)
{
TransformInfo output;
output.Position = float4(0, 0, 0, 0);
output.Normal = float3(0, 0, 0);
// D3D
const int _BLEND_OFF = 0;
const int _1WEIGHT_2MAT = 1;
const int _2WEIGHT_3MAT = 3;
const int _3WEIGHT_4MAT = 5;
// Xbox
const int _2WEIGHT_2MAT = 2;
const int _3WEIGHT_3MAT = 4;
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]);
return output;
}
// The number of matrices to blend
int mats = floor((state.Modes.VertexBlend - 1) / 2 + 2);
// If we have to calculate the last blend value
bool calcLastBlend = fmod(state.Modes.VertexBlend, 2) == 1;
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];
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;
}
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];
}
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];
if (state.Modes.ColorVertex)
{
// https://docs.microsoft.com/en-us/windows/win32/direct3d9/d3dmaterialcolorsource
const int D3DMCS_MATERIAL = 0;
const int D3DMCS_COLOR1 = 1;
const int D3DMCS_COLOR2 = 2;
// TODO preprocess on the CPU
// 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]) {
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]) {
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(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 = xIn.color[1].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
const int D3DTTFF_DISABLE = 0;
const int D3DTTFF_COUNT1 = 1;
const int D3DTTFF_COUNT2 = 2;
const int D3DTTFF_COUNT3 = 3;
const int D3DTTFF_COUNT4 = 4;
const int D3DTTFF_PROJECTED = 256; // This is the only real flag
// https://docs.microsoft.com/en-us/windows/win32/direct3d9/d3dtss-tci
// Pre-shifted
const int TCI_PASSTHRU = 0;
const int TCI_CAMERASPACENORMAL = 1;
const int TCI_CAMERASPACEPOSITION = 2;
const int TCI_CAMERASPACEREFLECTIONVECTOR = 3;
const int TCI_OBJECT = 4; // Xbox
const int TCI_SPHERE = 5; // Xbox
const TextureState tState = state.TextureStates[stage];
// Extract transform flags
int countFlag = tState.TextureTransformFlagsCount;
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
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
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;
}
else
{
// Generate texture coordinates
float3 reflected = reflect(normalize(View.Position.xyz), View.Normal);
if (tState.TexCoordIndexGen == TCI_CAMERASPACENORMAL)
texCoord = float4(View.Normal, 1);
else if (tState.TexCoordIndexGen == TCI_CAMERASPACEPOSITION)
texCoord = View.Position;
else 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
float3 R = reflected;
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()
{
PointSprite ps = state.PointSprite;
float pointSize = ps.PointSize;
if (ps.PointScaleEnable)
{
float eyeDistance = length(View.Position);
float factor = ps.ScaleA + ps.ScaleB * eyeDistance + ps.ScaleC * (eyeDistance * eyeDistance);
pointSize *= ps.RenderTargetHeight * sqrt(1 / factor);
}
return pointSize;
}
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++) {
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 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);
// 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;
// Vertex lighting
if (state.Modes.Lighting || state.Modes.TwoSidedLighting)
{
// Materials
Material material = DoMaterial(0, diffuse, specular, xIn);
Material backMaterial = DoMaterial(1, backDiffuse, backSpecular, xIn);
float2 powers = float2(material.Power, backMaterial.Power);
LightingOutput lighting = CalcLighting(powers);
// Compute each lighting component
float3 ambient = material.Ambient.rgb * state.AmbientPlusLightAmbient.rgb;
float3 backAmbient = backMaterial.Ambient.rgb * state.BackAmbientPlusLightAmbient.rgb;
float3 diffuse = material.Diffuse.rgb * lighting.Diffuse.Front;
float3 backDiffuse = backMaterial.Diffuse.rgb * lighting.Diffuse.Back;
float3 specular = material.Specular.rgb * lighting.Specular.Front;
float3 backSpecular = backMaterial.Specular.rgb * lighting.Specular.Back;
float3 emissive = material.Emissive.rgb;
float3 backEmissive = backMaterial.Emissive.rgb;
// Frontface
xOut.oD0 = float4(ambient + diffuse + emissive, material.Diffuse.a);
xOut.oD1 = float4(specular, 0);
// Backface
xOut.oB0 = float4(backAmbient + backDiffuse + backEmissive, backMaterial.Diffuse.a);
xOut.oB1 = float4(backSpecular, 0);
}
// TODO does TwoSidedLighting imply Lighting? Verify if TwoSidedLighting can be enabled independently of Lighting
// Diffuse and specular for when lighting is disabled
if (!state.Modes.Lighting)
{
xOut.oD0 = Get(xIn, diffuse);
xOut.oD1 = Get(xIn, specular);
}
if(!state.Modes.TwoSidedLighting)
{
xOut.oB0 = Get(xIn, backDiffuse);
xOut.oB1 = Get(xIn, backSpecular);
}
// 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;
}

View File

@ -0,0 +1,145 @@
// 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
arr(World, float4x4, 4); // 6, 7, 8, 9
};
// 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;
alignas(16) float NormalizeNormals;
};
struct PointSprite {
alignas(16) float PointSize;
alignas(16) float PointScaleEnable;
alignas(16) float RenderTargetHeight;
alignas(16) float ScaleA;
alignas(16) float ScaleB;
alignas(16) float ScaleC;
};
struct TextureState {
alignas(16) float TextureTransformFlagsCount;
alignas(16) float TextureTransformFlagsProjected;
alignas(16) float TexCoordIndex;
alignas(16) float TexCoordIndexGen;
};
struct Fog {
alignas(16) float DepthMode;
};
struct FixedFunctionVertexShaderState {
alignas(16) Transforms Transforms;
alignas(16) arr(Lights, Light, 8);
alignas(16) float4 AmbientPlusLightAmbient;
alignas(16) float4 BackAmbientPlusLightAmbient;
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

View File

@ -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_3_0;
@ -304,6 +305,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)

View File

@ -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

View File

@ -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

View File

@ -200,6 +200,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();