Fetching DirectX 9 Device: Difference between revisions
No edit summary |
No edit summary |
||
| Line 7: | Line 7: | ||
== Tutorial == | == Tutorial == | ||
Open the client initialization code in <code>game/client/cdll_client_int.cpp</code>. | Open the client initialization code in <code>game/client/cdll_client_int.cpp</code>, and it's corresponding header file <code>cdll_client_int.h</code>. | ||
In the <code>cpp</code> file, following the big wall of <code>#include</code>s, add this: | |||
<syntaxhighlight lang="cpp"> | <syntaxhighlight lang="cpp"> | ||
#include "shaderapi/IShaderDevice.h" | #include "shaderapi/IShaderDevice.h" | ||
| Line 20: | Line 20: | ||
<syntaxhighlight lang="cpp"> | <syntaxhighlight lang="cpp"> | ||
IShaderDevice* g_pShaderDevice = NULL; | IShaderDevice* g_pShaderDevice = NULL; | ||
IDirect3DDevice9* g_pDirect3DDevice9 = NULL; | |||
</syntaxhighlight> | </syntaxhighlight> | ||
Now go to the header file, where the device will be forward declared and externed. | |||
First, add this forward declaration below the list of class forwards: | |||
<syntaxhighlight lang="cpp"> | <syntaxhighlight lang="cpp"> | ||
extern | struct IDirect3DDevice9; | ||
</syntaxhighlight> | |||
Then scroll down and add this in, below the list of interfaces, to expose it for the rest of the client code. | |||
<syntaxhighlight lang="cpp"> | |||
extern IDirect3DDevice9* g_pDirect3DDevice9; | |||
</syntaxhighlight> | </syntaxhighlight> | ||
| Line 45: | Line 52: | ||
{ | { | ||
// Dereference the virtual table and access the IsUsingGraphics method | // Dereference the virtual table and access the IsUsingGraphics method | ||
byte* pIShaderDevice_IsUsingGraphics = (byte*)(*(void***)g_pShaderDevice)[5]; | byte* pIShaderDevice_IsUsingGraphics = ( byte* )( *( void*** )g_pShaderDevice)[5]; | ||
// Resolve the RIP instruction to get the absolute address | // Resolve the RIP instruction to get the absolute address | ||
byte* ppDirect3DDevice9 = pIShaderDevice_IsUsingGraphics + 8 + *( int32* )( pIShaderDevice_IsUsingGraphics + 3 ); | byte* ppDirect3DDevice9 = pIShaderDevice_IsUsingGraphics + 8 + *( int32* )( pIShaderDevice_IsUsingGraphics + 3 ); | ||
| Line 77: | Line 84: | ||
// ... | // ... | ||
void | void ExampleToFetchSwapChain() | ||
{ | { | ||
IDirect3DSwapChain9* pSwapChain = NULL; | IDirect3DSwapChain9* pSwapChain = NULL; | ||
Revision as of 02:44, 3 November 2025
This tutorial shows how to fetch the DirectX 9 Device in 64-bit games on the
Team Fortress 2 branch. This method works on both Windows and Linux platforms.
Access to the low-level DirectX 9 device allows the creation of effects that Source has no support for, such as rendering cubemaps in real-time.
Tutorial
Open the client initialization code in game/client/cdll_client_int.cpp, and it's corresponding header file cdll_client_int.h.
In the cpp file, following the big wall of #includes, add this:
#include "shaderapi/IShaderDevice.h"
Scrolling further down, you will see a list of interface pointers, below the comment that says this:
// IF YOU ADD AN INTERFACE, EXTERN IT IN THE HEADER FILE.
Add these interfaces:
IShaderDevice* g_pShaderDevice = NULL;
IDirect3DDevice9* g_pDirect3DDevice9 = NULL;
Now go to the header file, where the device will be forward declared and externed.
First, add this forward declaration below the list of class forwards:
struct IDirect3DDevice9;
Then scroll down and add this in, below the list of interfaces, to expose it for the rest of the client code.
extern IDirect3DDevice9* g_pDirect3DDevice9;
Back to the .cpp file, go to the CHLClient::Init function and add the following after the if (!g_pMatSystemSurface) check:
g_pShaderDevice = (IShaderDevice*)appSystemFactory( SHADER_DEVICE_INTERFACE_VERSION, NULL );
if ( !g_pShaderDevice )
return false;
Next comes the fun part. The IShaderDevice class has a public function that checks whether the DirectX 9 device exists, named IsUsingGraphics. We can abuse this to fetch the actual pointer to the device.
Copy the following function into the file somewhere above. You don't need to worry about the specific details of how this works.
IDirect3DDevice9* GetDX9Device()
{
// Dereference the virtual table and access the IsUsingGraphics method
byte* pIShaderDevice_IsUsingGraphics = ( byte* )( *( void*** )g_pShaderDevice)[5];
// Resolve the RIP instruction to get the absolute address
byte* ppDirect3DDevice9 = pIShaderDevice_IsUsingGraphics + 8 + *( int32* )( pIShaderDevice_IsUsingGraphics + 3 );
return *( IDirect3DDevice9** )ppDirect3DDevice9;
}
Now go back to where you added the code to fetch the g_pShaderDevice interface, and add the following below:
g_pDirect3DDevice9 = GetDX9Device();
if ( !g_pDirect3DDevice9 )
{
Error( "Failed to get DirectX9 device pointer" );
return false;
}
And that's all! You can now call the raw DirectX9 API using the g_pDirect3DDevice9 interface.
Usage
To call functions on the DirectX 9 device, you will need to download the DirectX 9 SDK.
After you download it, extract it to a place in your mod's folder. The convention is to make a dx9sdk directory in the top-level folder of your source code.
You can then include the DirectX 9 API like-so in client code:
#include "../../dx9sdk/include/d3d9.h"
// ...
void ExampleToFetchSwapChain()
{
IDirect3DSwapChain9* pSwapChain = NULL;
g_pDirect3DDevice9->GetSwapChain( 0, &pSwapChain );
}