/*
** Command & Conquer Generals(tm)
** Copyright 2025 Electronic Arts Inc.
**
** This program is free software: you can redistribute it and/or modify
** it under the terms of the GNU General Public License as published by
** the Free Software Foundation, either version 3 of the License, or
** (at your option) any later version.
**
** This program is distributed in the hope that it will be useful,
** but WITHOUT ANY WARRANTY; without even the implied warranty of
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
** GNU General Public License for more details.
**
** You should have received a copy of the GNU General Public License
** along with this program. If not, see .
*/
/***********************************************************************************************
*** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S ***
***********************************************************************************************
* *
* Project Name : WW3D *
* *
* $Archive:: /VSS_Sync/ww3d2/dx8wrapper.cpp $*
* *
* Original Author:: Jani Penttinen *
* *
* $Author:: Vss_sync $*
* *
* $Modtime:: 8/29/01 7:29p $*
* *
* $Revision:: 134 $*
* *
*---------------------------------------------------------------------------------------------*
* Functions: *
* DX8Wrapper::_Update_Texture -- Copies a texture from system memory to video memory *
* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
//#define CREATE_DX8_MULTI_THREADED
#include "dx8wrapper.h"
#include "dx8fvf.h"
#include "dx8vertexbuffer.h"
#include "dx8indexbuffer.h"
#include "dx8renderer.h"
#include "ww3d.h"
#include "camera.h"
#include "wwstring.h"
#include "matrix4.h"
#include "vertmaterial.h"
#include "rddesc.h"
#include "lightenvironment.h"
#include "statistics.h"
#include "registry.h"
#include "boxrobj.h"
#include "pointgr.h"
#include "render2d.h"
#include "sortingrenderer.h"
#include "shattersystem.h"
#include "light.h"
#include "assetmgr.h"
#include "textureloader.h"
#include "missingtexture.h"
#include "thread.h"
#include
#include
#include "pot.h"
#include "wwprofile.h"
#include "ffactory.h"
#include "dx8caps.h"
#include "formconv.h"
#include "dx8texman.h"
#include "bound.h"
#include "dx8webbrowser.h"
#define WW3D_DEVTYPE D3DDEVTYPE_HAL
const int DEFAULT_RESOLUTION_WIDTH = 640;
const int DEFAULT_RESOLUTION_HEIGHT = 480;
const int DEFAULT_BIT_DEPTH = 32;
const int DEFAULT_TEXTURE_BIT_DEPTH = 16;
bool DX8Wrapper_IsWindowed = true;
// FPU_PRESERVE
int DX8Wrapper_PreserveFPU = 0;
/***********************************************************************************
**
** DX8Wrapper Static Variables
**
***********************************************************************************/
static HWND _Hwnd = NULL;
bool DX8Wrapper::IsInitted = false;
bool DX8Wrapper::_EnableTriangleDraw = true;
int DX8Wrapper::CurRenderDevice = -1;
int DX8Wrapper::ResolutionWidth = DEFAULT_RESOLUTION_WIDTH;
int DX8Wrapper::ResolutionHeight = DEFAULT_RESOLUTION_HEIGHT;
int DX8Wrapper::BitDepth = DEFAULT_BIT_DEPTH;
int DX8Wrapper::TextureBitDepth = DEFAULT_TEXTURE_BIT_DEPTH;
bool DX8Wrapper::IsWindowed = false;
D3DFORMAT DX8Wrapper::DisplayFormat = D3DFMT_UNKNOWN;
D3DMATRIX DX8Wrapper::old_world;
D3DMATRIX DX8Wrapper::old_view;
D3DMATRIX DX8Wrapper::old_prj;
bool DX8Wrapper::world_identity;
unsigned DX8Wrapper::RenderStates[256];
unsigned DX8Wrapper::TextureStageStates[MAX_TEXTURE_STAGES][32];
IDirect3DBaseTexture8 * DX8Wrapper::Textures[MAX_TEXTURE_STAGES];
RenderStateStruct DX8Wrapper::render_state;
unsigned DX8Wrapper::render_state_changed;
bool DX8Wrapper::FogEnable = false;
D3DCOLOR DX8Wrapper::FogColor = 0;
IDirect3D8 * DX8Wrapper::D3DInterface = NULL;
IDirect3DDevice8 * DX8Wrapper::D3DDevice = NULL;
IDirect3DSurface8 * DX8Wrapper::CurrentRenderTarget = NULL;
IDirect3DSurface8 * DX8Wrapper::DefaultRenderTarget = NULL;
unsigned DX8Wrapper::matrix_changes = 0;
unsigned DX8Wrapper::material_changes = 0;
unsigned DX8Wrapper::vertex_buffer_changes = 0;
unsigned DX8Wrapper::index_buffer_changes = 0;
unsigned DX8Wrapper::light_changes = 0;
unsigned DX8Wrapper::texture_changes = 0;
unsigned DX8Wrapper::render_state_changes = 0;
unsigned DX8Wrapper::texture_stage_state_changes = 0;
unsigned DX8Wrapper::_MainThreadID = 0;
bool DX8Wrapper::CurrentDX8LightEnables[4];
D3DADAPTER_IDENTIFIER8 DX8Wrapper::CurrentAdapterIdentifier;
unsigned long DX8Wrapper::FrameCount = 0;
bool _DX8SingleThreaded = false;
unsigned number_of_DX8_calls = 0;
static unsigned last_frame_matrix_changes = 0;
static unsigned last_frame_material_changes = 0;
static unsigned last_frame_vertex_buffer_changes = 0;
static unsigned last_frame_index_buffer_changes = 0;
static unsigned last_frame_light_changes = 0;
static unsigned last_frame_texture_changes = 0;
static unsigned last_frame_render_state_changes = 0;
static unsigned last_frame_texture_stage_state_changes = 0;
static unsigned last_frame_number_of_DX8_calls = 0;
static D3DPRESENT_PARAMETERS _PresentParameters;
static DynamicVectorClass _RenderDeviceNameTable;
static DynamicVectorClass _RenderDeviceShortNameTable;
static DynamicVectorClass _RenderDeviceDescriptionTable;
/*
** Registry value names
*/
#define VALUE_NAME_RENDER_DEVICE_NAME "RenderDeviceName"
#define VALUE_NAME_RENDER_DEVICE_WIDTH "RenderDeviceWidth"
#define VALUE_NAME_RENDER_DEVICE_HEIGHT "RenderDeviceHeight"
#define VALUE_NAME_RENDER_DEVICE_DEPTH "RenderDeviceDepth"
#define VALUE_NAME_RENDER_DEVICE_WINDOWED "RenderDeviceWindowed"
#define VALUE_NAME_RENDER_DEVICE_TEXTURE_DEPTH "RenderDeviceTextureDepth"
DX8_CleanupHook *DX8Wrapper::m_pCleanupHook=NULL;
#ifdef EXTENDED_STATS
DX8_Stats DX8Wrapper::stats;
#endif
/***********************************************************************************
**
** DX8Wrapper Implementation
**
***********************************************************************************/
void Log_DX8_ErrorCode(unsigned res)
{
char tmp[256]="";
HRESULT new_res=D3DXGetErrorStringA(
res,
tmp,
sizeof(tmp));
if (new_res==D3D_OK) {
WWDEBUG_SAY((tmp));
}
WWASSERT(0);
}
void Non_Fatal_Log_DX8_ErrorCode(unsigned res,const char * file,int line)
{
char tmp[256]="";
HRESULT new_res=D3DXGetErrorStringA(
res,
tmp,
sizeof(tmp));
if (new_res==D3D_OK) {
WWDEBUG_SAY(("DX8 Error: %s, File: %s, Line: %d\n",tmp,file,line));
}
}
bool DX8Wrapper::Init(void * hwnd)
{
WWASSERT(!IsInitted);
/*
** Initialize all variables!
*/
_Hwnd = (HWND)hwnd;
_MainThreadID=ThreadClass::_Get_Current_Thread_ID();
CurRenderDevice = -1;
ResolutionWidth = DEFAULT_RESOLUTION_WIDTH;
ResolutionHeight = DEFAULT_RESOLUTION_HEIGHT;
// Initialize Render2DClass Screen Resolution
Render2DClass::Set_Screen_Resolution( RectClass( 0, 0, ResolutionWidth, ResolutionHeight ) );
BitDepth = DEFAULT_BIT_DEPTH;
IsWindowed = false;
DX8Wrapper_IsWindowed = false;
for (int light=0;light<4;++light) CurrentDX8LightEnables[light]=false;
::ZeroMemory(&old_world, sizeof(D3DMATRIX));
::ZeroMemory(&old_view, sizeof(D3DMATRIX));
::ZeroMemory(&old_prj, sizeof(D3DMATRIX));
//old_vertex_shader; TODO
//old_sr_shader;
//current_shader;
//world_identity;
//CurrentFogColor;
D3DInterface = NULL;
D3DDevice = NULL;
Reset_Statistics();
Invalidate_Cached_Render_States();
/*
** Create the D3D interface object
*/
D3DInterface = Direct3DCreate8(D3D_SDK_VERSION); // TODO: handle failure cases...
if (!D3DInterface)
return false;
IsInitted = true;
/*
** Enumerate the available devices
*/
Enumerate_Devices();
return true;
}
void DX8Wrapper::Shutdown(void)
{
if (D3DDevice) {
Set_Render_Target ((IDirect3DSurface8 *)NULL);
Release_Device();
}
for (int i = 0; i < MAX_TEXTURE_STAGES; i++) {
if (Textures[i]) {
Textures[i]->Release();
Textures[i] = NULL;
}
}
if (D3DInterface) {
UINT newRefCount=D3DInterface->Release();
D3DInterface=NULL;
}
_RenderDeviceNameTable.Clear(); // note - Delete_All() resizes the vector, causing a reallocation. Clear is better. jba.
_RenderDeviceShortNameTable.Clear();
_RenderDeviceDescriptionTable.Clear();
IsInitted = false; // 010803 srj
}
void DX8Wrapper::Do_Onetime_Device_Dependent_Inits(void)
{
/*
** Set Global render states (some of which depend on caps)
*/
Compute_Caps(DisplayFormat,_PresentParameters.AutoDepthStencilFormat);
/*
** Initalize any other subsystems inside of WW3D
*/
MissingTexture::_Init();
TextureClass::_Init_Filters();
TheDX8MeshRenderer.Init();
BoxRenderObjClass::Init();
VertexMaterialClass::Init();
PointGroupClass::_Init(); // This needs the VertexMaterialClass to be initted
ShatterSystem::Init();
TextureLoader::Init();
#ifdef WW3D_DX8
// WW3DAssetManager::Get_Instance()->Open_Texture_File_Cache("cache_");
/*
** Initialize the dazzle system
*/
FileClass * dazzle_ini_file = _TheFileFactory->Get_File(DAZZLE_INI_FILENAME);
if (dazzle_ini_file) {
INIClass dazzle_ini(*dazzle_ini_file);
DazzleRenderObjClass::Init_From_INI(&dazzle_ini);
_TheFileFactory->Return_File(dazzle_ini_file);
}
#endif //WW3D_DX8
Set_Default_Global_Render_States();
}
inline DWORD F2DW(float f) { return *((unsigned*)&f); }
void DX8Wrapper::Set_Default_Global_Render_States(void)
{
DX8_THREAD_ASSERT();
const D3DCAPS8 &caps = DX8Caps::Get_Default_Caps();
Set_DX8_Render_State(D3DRS_RANGEFOGENABLE, (caps.RasterCaps & D3DPRASTERCAPS_FOGRANGE) ? TRUE : FALSE);
Set_DX8_Render_State(D3DRS_FOGTABLEMODE, D3DFOG_NONE);
Set_DX8_Render_State(D3DRS_FOGVERTEXMODE, D3DFOG_LINEAR);
Set_DX8_Render_State(D3DRS_SPECULARMATERIALSOURCE, D3DMCS_MATERIAL);
Set_DX8_Render_State(D3DRS_COLORVERTEX, TRUE);
Set_DX8_Render_State(D3DRS_ZBIAS,0);
Set_DX8_Texture_Stage_State(1, D3DTSS_BUMPENVLSCALE, F2DW(1.0f));
Set_DX8_Texture_Stage_State(1, D3DTSS_BUMPENVLOFFSET, F2DW(0.0f));
Set_DX8_Texture_Stage_State(0, D3DTSS_BUMPENVMAT00,F2DW(1.0f));
Set_DX8_Texture_Stage_State(0, D3DTSS_BUMPENVMAT01,F2DW(0.0f));
Set_DX8_Texture_Stage_State(0, D3DTSS_BUMPENVMAT10,F2DW(0.0f));
Set_DX8_Texture_Stage_State(0, D3DTSS_BUMPENVMAT11,F2DW(1.0f));
// Set_DX8_Render_State(D3DRS_CULLMODE, D3DCULL_CW);
// Set dither mode here?
}
//MW: I added this for 'Generals'.
bool DX8Wrapper::Validate_Device(void)
{ DWORD numPasses=0;
HRESULT hRes;
hRes=_Get_D3D_Device8()->ValidateDevice(&numPasses);
return (hRes == D3D_OK);
}
void DX8Wrapper::Invalidate_Cached_Render_States(void)
{
render_state_changed = 0;
int a;
for (a=0;aSetTexture(a,NULL);
if (Textures[a] != NULL)
Textures[a]->Release();
Textures[a]=NULL;
}
ShaderClass::Invalidate();
//Need to explicitly set render_state texture pointers to NULL. MW
Release_Render_State();
}
void DX8Wrapper::Do_Onetime_Device_Dependent_Shutdowns(void)
{
/*
** Shutdown ww3d systems
*/
if (render_state.vertex_buffer) render_state.vertex_buffer->Release_Engine_Ref();
REF_PTR_RELEASE(render_state.vertex_buffer);
if (render_state.index_buffer) render_state.index_buffer->Release_Engine_Ref();
REF_PTR_RELEASE(render_state.index_buffer);
REF_PTR_RELEASE(render_state.material);
for (unsigned i=0;iGetDeviceCaps(
CurRenderDevice,
WW3D_DEVTYPE,
&caps))) {
return false;
}
::ZeroMemory(&CurrentAdapterIdentifier, sizeof(D3DADAPTER_IDENTIFIER8));
if (FAILED( D3DInterface->GetAdapterIdentifier(CurRenderDevice,D3DENUM_NO_WHQL_LEVEL,&CurrentAdapterIdentifier))) {
return false;
}
unsigned vertex_processing_type=D3DCREATE_SOFTWARE_VERTEXPROCESSING;
if (caps.DevCaps&D3DDEVCAPS_HWTRANSFORMANDLIGHT) {
vertex_processing_type=D3DCREATE_MIXED_VERTEXPROCESSING;
}
#ifdef CREATE_DX8_MULTI_THREADED
vertex_processing_type|=D3DCREATE_MULTITHREADED;
_DX8SingleThreaded=false;
#else
_DX8SingleThreaded=true;
#endif
if (DX8Wrapper_PreserveFPU)
vertex_processing_type |= D3DCREATE_FPU_PRESERVE;
if (FAILED( D3DInterface->CreateDevice(
CurRenderDevice,
WW3D_DEVTYPE,
_Hwnd,
vertex_processing_type,
&_PresentParameters,
&D3DDevice ) ) )
{
return false;
}
/*
** Initialize all subsystems
*/
Do_Onetime_Device_Dependent_Inits();
return true;
}
bool DX8Wrapper::Reset_Device(bool reload_assets)
{
DX8_THREAD_ASSERT();
if ((IsInitted) && (D3DDevice != NULL)) {
// Release all non-MANAGED stuff
Set_Vertex_Buffer (NULL);
Set_Index_Buffer (NULL, 0);
if (m_pCleanupHook) {
m_pCleanupHook->ReleaseResources();
}
DynamicVBAccessClass::_Deinit();
DynamicIBAccessClass::_Deinit();
DX8TextureManagerClass::Release_Textures();
HRESULT hr=_Get_D3D_Device8()->TestCooperativeLevel();
if (hr != D3DERR_DEVICELOST )
{ DX8CALL_HRES(Reset(&_PresentParameters),hr)
if (hr != D3D_OK)
return false; //reset failed.
}
else
return false; //device is lost and can't be reset.
if (reload_assets)
{
DX8TextureManagerClass::Recreate_Textures();
if (m_pCleanupHook) {
m_pCleanupHook->ReAcquireResources();
}
}
Invalidate_Cached_Render_States();
Set_Default_Global_Render_States();
return true;
}
return false;
}
void DX8Wrapper::Release_Device(void)
{
if (D3DDevice) {
for (int a=0;aRelease_Engine_Ref();
REF_PTR_RELEASE(render_state.vertex_buffer);
if (render_state.index_buffer) render_state.index_buffer->Release_Engine_Ref();
REF_PTR_RELEASE(render_state.index_buffer);
/*
** Shutdown all subsystems
*/
Do_Onetime_Device_Dependent_Shutdowns();
/*
** Release the device
*/
D3DDevice->Release();
D3DDevice=NULL;
}
}
void DX8Wrapper::Enumerate_Devices()
{
DX8_Assert();
int adapter_count = D3DInterface->GetAdapterCount();
for (int adapter_index=0; adapter_indexGetAdapterIdentifier(adapter_index,D3DENUM_NO_WHQL_LEVEL,&id);
if (res == D3D_OK) {
/*
** Set up the device name
*/
StringClass device_name = id.Description;
_RenderDeviceNameTable.Add(device_name);
_RenderDeviceShortNameTable.Add(device_name); // for now, just add the same name to the "pretty name table"
/*
** Set up the render device description
** TODO: Fill in more fields of the render device description? (need some lookup tables)
*/
RenderDeviceDescClass desc;
desc.set_device_name(id.Description);
desc.set_driver_name(id.Driver);
char buf[64];
sprintf(buf,"%d.%d.%d.%d", //"%04x.%04x.%04x.%04x",
HIWORD(id.DriverVersion.HighPart),
LOWORD(id.DriverVersion.HighPart),
HIWORD(id.DriverVersion.LowPart),
LOWORD(id.DriverVersion.LowPart));
desc.set_driver_version(buf);
/*
** Enumerate the resolutions
*/
desc.reset_resolution_list();
int mode_count = D3DInterface->GetAdapterModeCount(adapter_index);
for (int mode_index=0; mode_indexEnumAdapterModes(adapter_index,mode_index,&d3dmode);
if (res == D3D_OK) {
int bits = 0;
switch (d3dmode.Format)
{
case D3DFMT_R8G8B8:
case D3DFMT_A8R8G8B8:
case D3DFMT_X8R8G8B8: bits = 32; break;
case D3DFMT_R5G6B5:
case D3DFMT_X1R5G5B5: bits = 16; break;
}
/*
** If we recognize the format, add it to the list
** TODO: should we handle more formats? will any cards report more than 24 or 16 bit?
*/
if (bits != 0) {
desc.add_resolution(d3dmode.Width,d3dmode.Height,bits);
}
}
}
/*
** Add the render device to our table
*/
_RenderDeviceDescriptionTable.Add(desc);
}
}
}
bool DX8Wrapper::Set_Any_Render_Device(void)
{
// Try windowed first
for (int dev_number = 0; dev_number < _RenderDeviceNameTable.Count(); dev_number++) {
if (Set_Render_Device(dev_number,-1,-1,-1,1,false)) {
return true;
}
}
// Then fullscreen
for (dev_number = 0; dev_number < _RenderDeviceNameTable.Count(); dev_number++) {
if (Set_Render_Device(dev_number,-1,-1,-1,0,false)) {
return true;
}
}
return false;
}
bool DX8Wrapper::Set_Render_Device
(
const char * dev_name,
int width,
int height,
int bits,
int windowed,
bool resize_window
)
{
for ( int dev_number = 0; dev_number < _RenderDeviceNameTable.Count(); dev_number++) {
if ( strcmp( dev_name, _RenderDeviceNameTable[dev_number]) == 0) {
return Set_Render_Device( dev_number, width, height, bits, windowed, resize_window );
}
if ( strcmp( dev_name, _RenderDeviceShortNameTable[dev_number]) == 0) {
return Set_Render_Device( dev_number, width, height, bits, windowed, resize_window );
}
}
return false;
}
void DX8Wrapper::Get_Format_Name(unsigned int format, StringClass *tex_format)
{
*tex_format="Unknown";
switch (format) {
case D3DFMT_A8R8G8B8: *tex_format="D3DFMT_A8R8G8B8"; break;
case D3DFMT_R8G8B8: *tex_format="D3DFMT_R8G8B8"; break;
case D3DFMT_A4R4G4B4: *tex_format="D3DFMT_A4R4G4B4"; break;
case D3DFMT_A1R5G5B5: *tex_format="D3DFMT_A1R5G5B5"; break;
case D3DFMT_R5G6B5: *tex_format="D3DFMT_R5G6B5"; break;
case D3DFMT_L8: *tex_format="D3DFMT_L8"; break;
case D3DFMT_A8: *tex_format="D3DFMT_A8"; break;
case D3DFMT_P8: *tex_format="D3DFMT_P8"; break;
case D3DFMT_X8R8G8B8: *tex_format="D3DFMT_X8R8G8B8"; break;
case D3DFMT_X1R5G5B5: *tex_format="D3DFMT_X1R5G5B5"; break;
case D3DFMT_R3G3B2: *tex_format="D3DFMT_R3G3B2"; break;
case D3DFMT_A8R3G3B2: *tex_format="D3DFMT_A8R3G3B2"; break;
case D3DFMT_X4R4G4B4: *tex_format="D3DFMT_X4R4G4B4"; break;
case D3DFMT_A8P8: *tex_format="D3DFMT_A8P8"; break;
case D3DFMT_A8L8: *tex_format="D3DFMT_A8L8"; break;
case D3DFMT_A4L4: *tex_format="D3DFMT_A4L4"; break;
case D3DFMT_V8U8: *tex_format="D3DFMT_V8U8"; break;
case D3DFMT_L6V5U5: *tex_format="D3DFMT_L6V5U5"; break;
case D3DFMT_X8L8V8U8: *tex_format="D3DFMT_X8L8V8U8"; break;
case D3DFMT_Q8W8V8U8: *tex_format="D3DFMT_Q8W8V8U8"; break;
case D3DFMT_V16U16: *tex_format="D3DFMT_V16U16"; break;
case D3DFMT_W11V11U10: *tex_format="D3DFMT_W11V11U10"; break;
case D3DFMT_UYVY: *tex_format="D3DFMT_UYVY"; break;
case D3DFMT_YUY2: *tex_format="D3DFMT_YUY2"; break;
case D3DFMT_DXT1: *tex_format="D3DFMT_DXT1"; break;
case D3DFMT_DXT2: *tex_format="D3DFMT_DXT2"; break;
case D3DFMT_DXT3: *tex_format="D3DFMT_DXT3"; break;
case D3DFMT_DXT4: *tex_format="D3DFMT_DXT4"; break;
case D3DFMT_DXT5: *tex_format="D3DFMT_DXT5"; break;
case D3DFMT_D16_LOCKABLE: *tex_format="D3DFMT_D16_LOCKABLE"; break;
case D3DFMT_D32: *tex_format="D3DFMT_D32"; break;
case D3DFMT_D15S1: *tex_format="D3DFMT_D15S1"; break;
case D3DFMT_D24S8: *tex_format="D3DFMT_D24S8"; break;
case D3DFMT_D16: *tex_format="D3DFMT_D16"; break;
case D3DFMT_D24X8: *tex_format="D3DFMT_D24X8"; break;
case D3DFMT_D24X4S4: *tex_format="D3DFMT_D24X4S4"; break;
default: break;
}
}
bool DX8Wrapper::Set_Render_Device(int dev, int width, int height, int bits, int windowed,
bool resize_window,bool reset_device, bool restore_assets)
{
WWASSERT(IsInitted);
WWASSERT(dev >= -1);
WWASSERT(dev < _RenderDeviceNameTable.Count());
/*
** If user has never selected a render device, start out with device 0
*/
if ((CurRenderDevice == -1) && (dev == -1)) {
CurRenderDevice = 0;
} else if (dev != -1) {
CurRenderDevice = dev;
}
/*
** If user doesn't want to change res, set the res variables to match the
** current resolution
*/
if (width != -1) ResolutionWidth = width;
if (height != -1) ResolutionHeight = height;
// Initialize Render2DClass Screen Resolution
Render2DClass::Set_Screen_Resolution( RectClass( 0, 0, ResolutionWidth, ResolutionHeight ) );
if (bits != -1) BitDepth = bits;
if (windowed != -1) IsWindowed = (windowed != 0);
DX8Wrapper_IsWindowed = IsWindowed;
WWDEBUG_SAY(("Attempting Set_Render_Device: name: %s (%s:%s), width: %d, height: %d, windowed: %d\n",
_RenderDeviceNameTable[CurRenderDevice],_RenderDeviceDescriptionTable[CurRenderDevice].Get_Driver_Name(),
_RenderDeviceDescriptionTable[CurRenderDevice].Get_Driver_Version(),ResolutionWidth,ResolutionHeight,(IsWindowed ? 1 : 0)));
#ifdef _WINDOWS
// PWG 4/13/2000 - changed so that if you say to resize the window it resizes
// regardless of whether its windowed or not as OpenGL resizes its self around
// the caption and edges of the window type you provide, so its important to
// push the client area to be the size you really want.
// if ( resize_window && windowed ) {
if (resize_window) {
// Get the current dimensions of the 'render area' of the window
RECT rect = { 0 };
::GetClientRect (_Hwnd, &rect);
// Is the window the correct size for this resolution?
if ((rect.right-rect.left) != ResolutionWidth ||
(rect.bottom-rect.top) != ResolutionHeight) {
// Calculate what the main window's bounding rectangle should be to
// accomodate this resolution
rect.left = 0;
rect.top = 0;
rect.right = ResolutionWidth;
rect.bottom = ResolutionHeight;
DWORD dwstyle = ::GetWindowLong (_Hwnd, GWL_STYLE);
AdjustWindowRect (&rect, dwstyle, FALSE);
// Resize the window to fit this resolution
if (!windowed)
::SetWindowPos(_Hwnd, HWND_TOPMOST, 0, 0, rect.right-rect.left, rect.bottom-rect.top,SWP_NOSIZE |SWP_NOMOVE);
else
::SetWindowPos (_Hwnd,
NULL,
0,
0,
rect.right-rect.left,
rect.bottom-rect.top,
SWP_NOZORDER | SWP_NOMOVE);
}
}
#endif
//must be either resetting existing device or creating a new one.
WWASSERT(reset_device || D3DDevice == NULL);
/*
** Initialize values for D3DPRESENT_PARAMETERS members.
*/
::ZeroMemory(&_PresentParameters, sizeof(D3DPRESENT_PARAMETERS));
_PresentParameters.BackBufferWidth = ResolutionWidth;
_PresentParameters.BackBufferHeight = ResolutionHeight;
_PresentParameters.BackBufferCount = IsWindowed ? 1 : 2;
_PresentParameters.MultiSampleType = D3DMULTISAMPLE_NONE;
_PresentParameters.SwapEffect = IsWindowed ? D3DSWAPEFFECT_DISCARD : D3DSWAPEFFECT_FLIP; // Shouldn't this be D3DSWAPEFFECT_FLIP?
_PresentParameters.hDeviceWindow = _Hwnd;
_PresentParameters.Windowed = IsWindowed;
_PresentParameters.EnableAutoDepthStencil = TRUE; // Driver will attempt to match Z-buffer depth
_PresentParameters.Flags=0; // We're not going to lock the backbuffer
_PresentParameters.FullScreen_PresentationInterval = D3DPRESENT_INTERVAL_DEFAULT;
_PresentParameters.FullScreen_RefreshRateInHz = D3DPRESENT_RATE_DEFAULT;
/*
** Set up the buffer formats. Several issues here:
** - if in windowed mode, the backbuffer must use the current display format.
** - the depth buffer must use
*/
if (IsWindowed) {
D3DDISPLAYMODE desktop_mode;
::ZeroMemory(&desktop_mode, sizeof(D3DDISPLAYMODE));
D3DInterface->GetAdapterDisplayMode( CurRenderDevice, &desktop_mode );
DisplayFormat=_PresentParameters.BackBufferFormat = desktop_mode.Format;
// In windowed mode, define the bitdepth from desktop mode (as it can't be changed)
switch (_PresentParameters.BackBufferFormat) {
case D3DFMT_X8R8G8B8:
case D3DFMT_A8R8G8B8:
case D3DFMT_R8G8B8: BitDepth=32; break;
case D3DFMT_A4R4G4B4:
case D3DFMT_A1R5G5B5:
case D3DFMT_R5G6B5: BitDepth=16; break;
case D3DFMT_L8:
case D3DFMT_A8:
case D3DFMT_P8: BitDepth=8; break;
default:
// Unknown backbuffer format probably means the device can't do windowed
return false;
}
if (BitDepth==32 && D3DInterface->CheckDeviceType(0,D3DDEVTYPE_HAL,desktop_mode.Format,D3DFMT_A8R8G8B8, TRUE) == D3D_OK)
{ //promote 32-bit modes to include destination alpha
_PresentParameters.BackBufferFormat = D3DFMT_A8R8G8B8;
}
/*
** Find a appropriate Z buffer
*/
if (!Find_Z_Mode(DisplayFormat,_PresentParameters.BackBufferFormat,&_PresentParameters.AutoDepthStencilFormat))
{
// If opening 32 bit mode failed, try 16 bit, even if the desktop happens to be 32 bit
if (BitDepth==32) {
BitDepth=16;
_PresentParameters.BackBufferFormat=D3DFMT_R5G6B5;
if (!Find_Z_Mode(_PresentParameters.BackBufferFormat,_PresentParameters.BackBufferFormat,&_PresentParameters.AutoDepthStencilFormat)) {
_PresentParameters.AutoDepthStencilFormat=D3DFMT_UNKNOWN;
}
}
else {
_PresentParameters.AutoDepthStencilFormat=D3DFMT_UNKNOWN;
}
}
} else {
/*
** Try to find a mode that matches the user's desired bit-depth.
*/
Find_Color_And_Z_Mode(ResolutionWidth,ResolutionHeight,BitDepth,&DisplayFormat,
&_PresentParameters.BackBufferFormat,&_PresentParameters.AutoDepthStencilFormat);
}
/*
** Time to actually create the device.
*/
if (_PresentParameters.AutoDepthStencilFormat==D3DFMT_UNKNOWN) {
if (BitDepth==32) {
_PresentParameters.AutoDepthStencilFormat=D3DFMT_D32;
}
else {
_PresentParameters.AutoDepthStencilFormat=D3DFMT_D16;
}
}
StringClass displayFormat;
StringClass backbufferFormat;
Get_Format_Name(DisplayFormat,&displayFormat);
Get_Format_Name(_PresentParameters.BackBufferFormat,&backbufferFormat);
WWDEBUG_SAY(("Using Display/BackBuffer Formats: %s/%s\n",displayFormat,backbufferFormat));
bool ret;
if (reset_device)
ret = Reset_Device(restore_assets); //reset device without restoring data - we're likely switching out of the app.
else
ret = Create_Device();
WWDEBUG_SAY(("Reset/Create_Device done, reset_device=%d, restore_assets=%d\n", reset_device, restore_assets));
return ret;
}
bool DX8Wrapper::Set_Next_Render_Device(void)
{
int new_dev = (CurRenderDevice + 1) % _RenderDeviceNameTable.Count();
return Set_Render_Device(new_dev);
}
bool DX8Wrapper::Toggle_Windowed(void)
{
#ifdef WW3D_DX8
// State OK?
assert (IsInitted);
if (IsInitted) {
// Get information about the current render device's resolutions
const RenderDeviceDescClass &render_device = Get_Render_Device_Desc ();
const DynamicVectorClass &resolutions = render_device.Enumerate_Resolutions ();
// Loop through all the resolutions supported by the current device.
// If we aren't currently running under one of these resolutions,
// then we should probably to the closest resolution before
// toggling the windowed state.
int curr_res = -1;
for (int res = 0;
(res < resolutions.Count ()) && (curr_res == -1);
res ++) {
// Is this the resolution we are looking for?
if ((resolutions[res].Width == ResolutionWidth) &&
(resolutions[res].Height == ResolutionHeight) &&
(resolutions[res].BitDepth == BitDepth)) {
curr_res = res;
}
}
if (curr_res == -1) {
// We don't match any of the standard resolutions,
// so set the first resolution and toggle the windowed state.
return Set_Device_Resolution (resolutions[0].Width,
resolutions[0].Height,
resolutions[0].BitDepth,
!IsWindowed, true);
} else {
// Toggle the windowed state
return Set_Device_Resolution (-1, -1, -1, !IsWindowed, true);
}
}
#endif //WW3D_DX8
return false;
}
void DX8Wrapper::Set_Swap_Interval(int swap)
{
switch (swap) {
case 0: _PresentParameters.FullScreen_PresentationInterval = D3DPRESENT_INTERVAL_IMMEDIATE; break;
case 1: _PresentParameters.FullScreen_PresentationInterval = D3DPRESENT_INTERVAL_ONE ; break;
case 2: _PresentParameters.FullScreen_PresentationInterval = D3DPRESENT_INTERVAL_TWO; break;
case 3: _PresentParameters.FullScreen_PresentationInterval = D3DPRESENT_INTERVAL_THREE; break;
default: _PresentParameters.FullScreen_PresentationInterval = D3DPRESENT_INTERVAL_ONE ; break;
}
Reset_Device();
}
int DX8Wrapper::Get_Swap_Interval(void)
{
return _PresentParameters.FullScreen_PresentationInterval;
}
bool DX8Wrapper::Has_Stencil(void)
{
bool has_stencil = (_PresentParameters.AutoDepthStencilFormat == D3DFMT_D24S8 ||
_PresentParameters.AutoDepthStencilFormat == D3DFMT_D24X4S4);
return has_stencil;
}
int DX8Wrapper::Get_Render_Device_Count(void)
{
return _RenderDeviceNameTable.Count();
}
int DX8Wrapper::Get_Render_Device(void)
{
assert(IsInitted);
return CurRenderDevice;
}
const RenderDeviceDescClass & DX8Wrapper::Get_Render_Device_Desc(int deviceidx)
{
WWASSERT(IsInitted);
if ((deviceidx == -1) && (CurRenderDevice == -1)) {
CurRenderDevice = 0;
}
// if the device index is -1 then we want the current device
if (deviceidx == -1) {
WWASSERT(CurRenderDevice >= 0);
WWASSERT(CurRenderDevice < _RenderDeviceNameTable.Count());
return _RenderDeviceDescriptionTable[CurRenderDevice];
}
// We can only ask for multiple device information if the devices
// have been detected.
WWASSERT(deviceidx >= 0);
WWASSERT(deviceidx < _RenderDeviceNameTable.Count());
return _RenderDeviceDescriptionTable[deviceidx];
}
const char * DX8Wrapper::Get_Render_Device_Name(int device_index)
{
device_index = device_index % _RenderDeviceShortNameTable.Count();
return _RenderDeviceShortNameTable[device_index];
}
bool DX8Wrapper::Set_Device_Resolution(int width,int height,int bits,int windowed, bool resize_window)
{
if (D3DDevice != NULL) {
if (width != -1) {
_PresentParameters.BackBufferWidth = ResolutionWidth = width;
}
if (height != -1) {
_PresentParameters.BackBufferHeight = ResolutionHeight = height;
}
if (resize_window)
{
// Get the current dimensions of the 'render area' of the window
RECT rect = { 0 };
::GetClientRect (_Hwnd, &rect);
// Is the window the correct size for this resolution?
if ((rect.right-rect.left) != ResolutionWidth ||
(rect.bottom-rect.top) != ResolutionHeight)
{
// Calculate what the main window's bounding rectangle should be to
// accomodate this resolution
rect.left = 0;
rect.top = 0;
rect.right = ResolutionWidth;
rect.bottom = ResolutionHeight;
DWORD dwstyle = ::GetWindowLong (_Hwnd, GWL_STYLE);
AdjustWindowRect (&rect, dwstyle, FALSE);
// Resize the window to fit this resolution
if (!windowed)
::SetWindowPos(_Hwnd, HWND_TOPMOST, 0, 0, rect.right-rect.left, rect.bottom-rect.top,SWP_NOSIZE |SWP_NOMOVE);
else
::SetWindowPos (_Hwnd,
NULL,
0,
0,
rect.right-rect.left,
rect.bottom-rect.top,
SWP_NOZORDER | SWP_NOMOVE);
}
}
#pragma message("TODO: support changing windowed status and changing the bit depth")
return Reset_Device();
} else {
return false;
}
}
void DX8Wrapper::Get_Device_Resolution(int & set_w,int & set_h,int & set_bits,bool & set_windowed)
{
WWASSERT(IsInitted);
set_w = ResolutionWidth;
set_h = ResolutionHeight;
set_bits = BitDepth;
set_windowed = IsWindowed;
return ;
}
void DX8Wrapper::Get_Render_Target_Resolution(int & set_w,int & set_h,int & set_bits,bool & set_windowed)
{
WWASSERT(IsInitted);
if (CurrentRenderTarget != NULL) {
D3DSURFACE_DESC info;
CurrentRenderTarget->GetDesc (&info);
set_w = info.Width;
set_h = info.Height;
set_bits = BitDepth; // should we get the actual bit depth of the target?
set_windowed = IsWindowed; // this doesn't really make sense for render targets (shouldn't matter)...
} else {
Get_Device_Resolution (set_w, set_h, set_bits, set_windowed);
}
return ;
}
bool DX8Wrapper::Registry_Save_Render_Device( const char * sub_key )
{
int width, height, depth;
bool windowed;
Get_Device_Resolution(width, height, depth, windowed);
return Registry_Save_Render_Device(sub_key, CurRenderDevice, ResolutionWidth, ResolutionHeight, BitDepth, IsWindowed, TextureBitDepth);
}
bool DX8Wrapper::Registry_Save_Render_Device( const char *sub_key, int device, int width, int height, int depth, bool windowed, int texture_depth)
{
RegistryClass * registry = W3DNEW RegistryClass( sub_key );
WWASSERT( registry );
if ( !registry->Is_Valid() ) {
delete registry;
WWDEBUG_SAY(( "Error getting Registry\n" ));
return false;
}
registry->Set_String( VALUE_NAME_RENDER_DEVICE_NAME,
_RenderDeviceShortNameTable[device] );
registry->Set_Int( VALUE_NAME_RENDER_DEVICE_WIDTH, width );
registry->Set_Int( VALUE_NAME_RENDER_DEVICE_HEIGHT, height );
registry->Set_Int( VALUE_NAME_RENDER_DEVICE_DEPTH, depth );
registry->Set_Int( VALUE_NAME_RENDER_DEVICE_WINDOWED, windowed );
registry->Set_Int( VALUE_NAME_RENDER_DEVICE_TEXTURE_DEPTH, texture_depth );
delete registry;
return true;
}
bool DX8Wrapper::Registry_Load_Render_Device( const char * sub_key, bool resize_window )
{
char name[ 200 ];
int width,height,depth,windowed;
if ( Registry_Load_Render_Device( sub_key,
name,
sizeof(name),
width,
height,
depth,
windowed,
TextureBitDepth) &&
(*name != 0))
{
WWDEBUG_SAY(( "Device %s (%d X %d) %d bit windowed:%d\n", name,width,height,depth,windowed));
if (TextureBitDepth==16 || TextureBitDepth==32) {
// WWDEBUG_SAY(( "Texture depth %d\n", TextureBitDepth));
} else {
WWDEBUG_SAY(( "Invalid texture depth %d, switching to 16 bits\n", TextureBitDepth));
TextureBitDepth=16;
}
if ( Set_Render_Device( name, width,height,depth,windowed, resize_window ) != true) {
return Set_Any_Render_Device();
}
return true;
}
WWDEBUG_SAY(( "Error getting Registry\n" ));
return Set_Any_Render_Device();
}
bool DX8Wrapper::Registry_Load_Render_Device( const char * sub_key, char *device, int device_len, int &width, int &height, int &depth, int &windowed, int &texture_depth)
{
RegistryClass registry( sub_key );
if ( registry.Is_Valid() ) {
registry.Get_String( VALUE_NAME_RENDER_DEVICE_NAME,
device, device_len);
width = registry.Get_Int( VALUE_NAME_RENDER_DEVICE_WIDTH, -1 );
height = registry.Get_Int( VALUE_NAME_RENDER_DEVICE_HEIGHT, -1 );
depth = registry.Get_Int( VALUE_NAME_RENDER_DEVICE_DEPTH, -1 );
windowed = registry.Get_Int( VALUE_NAME_RENDER_DEVICE_WINDOWED, -1 );
texture_depth = registry.Get_Int( VALUE_NAME_RENDER_DEVICE_TEXTURE_DEPTH, -1 );
return true;
}
return false;
}
bool DX8Wrapper::Find_Color_And_Z_Mode(int resx,int resy,int bitdepth,D3DFORMAT * set_colorbuffer,D3DFORMAT * set_backbuffer,D3DFORMAT * set_zmode)
{
static D3DFORMAT _formats16[] =
{
D3DFMT_R5G6B5,
D3DFMT_X1R5G5B5,
D3DFMT_A1R5G5B5
};
static D3DFORMAT _formats32[] =
{
D3DFMT_A8R8G8B8,
D3DFMT_X8R8G8B8,
D3DFMT_R8G8B8,
};
/*
** Select the table that we're going to use to search for a valid backbuffer format
*/
D3DFORMAT * format_table = NULL;
int format_count = 0;
if (BitDepth == 16) {
format_table = _formats16;
format_count = sizeof(_formats16) / sizeof(D3DFORMAT);
} else {
format_table = _formats32;
format_count = sizeof(_formats32) / sizeof(D3DFORMAT);
}
/*
** now search for a valid format
*/
bool found = false;
unsigned int mode = 0;
for (int format_index=0; format_index < format_count; format_index++) {
found |= Find_Color_Mode(format_table[format_index],resx,resy,&mode);
if (found) break;
}
if (!found) {
return false;
} else {
*set_backbuffer=*set_colorbuffer = format_table[format_index];
}
if (bitdepth==32 && *set_colorbuffer == D3DFMT_X8R8G8B8 && D3DInterface->CheckDeviceType(0,D3DDEVTYPE_HAL,*set_colorbuffer,D3DFMT_A8R8G8B8, TRUE) == D3D_OK)
{ //promote 32-bit modes to include destination alpha when supported
*set_backbuffer = D3DFMT_A8R8G8B8;
}
/*
** We found a backbuffer format, now find a zbuffer format
*/
return Find_Z_Mode(*set_colorbuffer,*set_backbuffer, set_zmode);
};
// find the resolution mode with at least resx,resy with the highest supported
// refresh rate
bool DX8Wrapper::Find_Color_Mode(D3DFORMAT colorbuffer, int resx, int resy, UINT *mode)
{
UINT i,j,modemax;
UINT rx,ry;
D3DDISPLAYMODE dmode;
::ZeroMemory(&dmode, sizeof(D3DDISPLAYMODE));
rx=(unsigned int) resx;
ry=(unsigned int) resy;
bool found=false;
modemax=D3DInterface->GetAdapterModeCount(D3DADAPTER_DEFAULT);
i=0;
while (iEnumAdapterModes(D3DADAPTER_DEFAULT, i, &dmode);
if (dmode.Width==rx && dmode.Height==ry && dmode.Format==colorbuffer)
found=true;
i++;
}
i--; // this is the first valid mode
// no match
if (!found) return false;
// go to the highest refresh rate in this mode
bool stillok=true;
j=i;
while (jEnumAdapterModes(D3DADAPTER_DEFAULT, j, &dmode);
if (dmode.Width==rx && dmode.Height==ry && dmode.Format==colorbuffer)
stillok=true; else stillok=false;
j++;
}
if (stillok==false) *mode=j-2;
else *mode=i;
return true;
}
// Helper function to find a Z buffer mode for the colorbuffer
// Will look for greatest Z precision
bool DX8Wrapper::Find_Z_Mode(D3DFORMAT colorbuffer,D3DFORMAT backbuffer, D3DFORMAT *zmode)
{
//MW: Swapped the next 2 tests so that Stencil modes get tested first.
if (Test_Z_Mode(colorbuffer,backbuffer,D3DFMT_D24S8))
{
*zmode=D3DFMT_D24S8;
WWDEBUG_SAY(("Found zbuffer mode D3DFMT_D24S8\n"));
return true;
}
if (Test_Z_Mode(colorbuffer,backbuffer,D3DFMT_D32))
{
*zmode=D3DFMT_D32;
WWDEBUG_SAY(("Found zbuffer mode D3DFMT_D32\n"));
return true;
}
if (Test_Z_Mode(colorbuffer,backbuffer,D3DFMT_D24X8))
{
*zmode=D3DFMT_D24X8;
WWDEBUG_SAY(("Found zbuffer mode D3DFMT_D24X8\n"));
return true;
}
if (Test_Z_Mode(colorbuffer,backbuffer,D3DFMT_D24X4S4))
{
*zmode=D3DFMT_D24X4S4;
WWDEBUG_SAY(("Found zbuffer mode D3DFMT_D24X4S4\n"));
return true;
}
if (Test_Z_Mode(colorbuffer,backbuffer,D3DFMT_D16))
{
*zmode=D3DFMT_D16;
WWDEBUG_SAY(("Found zbuffer mode D3DFMT_D16\n"));
return true;
}
if (Test_Z_Mode(colorbuffer,backbuffer,D3DFMT_D15S1))
{
*zmode=D3DFMT_D15S1;
WWDEBUG_SAY(("Found zbuffer mode D3DFMT_D15S1\n"));
return true;
}
// can't find a match
return false;
}
bool DX8Wrapper::Test_Z_Mode(D3DFORMAT colorbuffer,D3DFORMAT backbuffer, D3DFORMAT zmode)
{
// See if we have this mode first
if (FAILED(D3DInterface->CheckDeviceFormat(D3DADAPTER_DEFAULT,WW3D_DEVTYPE,
colorbuffer,D3DUSAGE_DEPTHSTENCIL,D3DRTYPE_SURFACE,zmode)))
{
WWDEBUG_SAY(("CheckDeviceFormat failed. Colorbuffer format = %d Zbufferformat = %d\n",colorbuffer,zmode));
return false;
}
// Then see if it matches the color buffer
if(FAILED(D3DInterface->CheckDepthStencilMatch(D3DADAPTER_DEFAULT, WW3D_DEVTYPE,
colorbuffer,backbuffer,zmode)))
{
WWDEBUG_SAY(("CheckDepthStencilMatch failed. Colorbuffer format = %d Backbuffer format = %d Zbufferformat = %d\n",colorbuffer,backbuffer,zmode));
return false;
}
return true;
}
void DX8Wrapper::Reset_Statistics()
{
matrix_changes = 0;
material_changes = 0;
vertex_buffer_changes = 0;
index_buffer_changes = 0;
light_changes = 0;
texture_changes = 0;
render_state_changes =0;
texture_stage_state_changes =0;
number_of_DX8_calls = 0;
last_frame_matrix_changes = 0;
last_frame_material_changes = 0;
last_frame_vertex_buffer_changes = 0;
last_frame_index_buffer_changes = 0;
last_frame_light_changes = 0;
last_frame_texture_changes = 0;
last_frame_render_state_changes = 0;
last_frame_texture_stage_state_changes = 0;
last_frame_number_of_DX8_calls = 0;
}
void DX8Wrapper::Begin_Statistics()
{
matrix_changes=0;
material_changes=0;
vertex_buffer_changes=0;
index_buffer_changes=0;
light_changes=0;
texture_changes = 0;
render_state_changes =0;
texture_stage_state_changes =0;
number_of_DX8_calls=0;
}
void DX8Wrapper::End_Statistics()
{
last_frame_matrix_changes=matrix_changes;
last_frame_material_changes=material_changes;
last_frame_vertex_buffer_changes=vertex_buffer_changes;
last_frame_index_buffer_changes=index_buffer_changes;
last_frame_light_changes=light_changes;
last_frame_texture_changes = texture_changes;
last_frame_render_state_changes = render_state_changes;
last_frame_texture_stage_state_changes = texture_stage_state_changes;
last_frame_number_of_DX8_calls=number_of_DX8_calls;
}
unsigned DX8Wrapper::Get_Last_Frame_Matrix_Changes() { return last_frame_matrix_changes; }
unsigned DX8Wrapper::Get_Last_Frame_Material_Changes() { return last_frame_material_changes; }
unsigned DX8Wrapper::Get_Last_Frame_Vertex_Buffer_Changes() { return last_frame_vertex_buffer_changes; }
unsigned DX8Wrapper::Get_Last_Frame_Index_Buffer_Changes() { return last_frame_index_buffer_changes; }
unsigned DX8Wrapper::Get_Last_Frame_Light_Changes() { return last_frame_light_changes; }
unsigned DX8Wrapper::Get_Last_Frame_Texture_Changes() { return last_frame_texture_changes; }
unsigned DX8Wrapper::Get_Last_Frame_Render_State_Changes() { return last_frame_render_state_changes; }
unsigned DX8Wrapper::Get_Last_Frame_Texture_Stage_State_Changes() { return last_frame_texture_stage_state_changes; }
unsigned DX8Wrapper::Get_Last_Frame_DX8_Calls() { return last_frame_number_of_DX8_calls; }
unsigned long DX8Wrapper::Get_FrameCount(void) {return FrameCount;}
void DX8_Assert()
{
WWASSERT(DX8Wrapper::_Get_D3D8());
DX8_THREAD_ASSERT();
}
void DX8Wrapper::Begin_Scene(void)
{
DX8_THREAD_ASSERT();
DX8CALL(BeginScene());
DX8WebBrowser::Update();
}
void DX8Wrapper::End_Scene(bool flip_frames)
{
DX8_THREAD_ASSERT();
DX8CALL(EndScene());
DX8WebBrowser::Render(0);
if (flip_frames) {
DX8_Assert();
HRESULT hr=_Get_D3D_Device8()->Present(NULL, NULL, NULL, NULL);
number_of_DX8_calls++;
if (SUCCEEDED(hr)) {
#ifdef EXTENDED_STATS
if (stats.m_sleepTime) {
::Sleep(stats.m_sleepTime);
}
#endif
FrameCount++;
}
// If the device was lost we need to check for cooperative level and possibly reset the device
if (hr==D3DERR_DEVICELOST) {
hr=_Get_D3D_Device8()->TestCooperativeLevel();
if (hr==D3DERR_DEVICENOTRESET) {
Reset_Device();
}
}
else {
DX8_ErrorCode(hr);
}
}
// Each frame, release all of the buffers and textures.
Set_Vertex_Buffer(NULL);
Set_Index_Buffer(NULL,0);
for (unsigned i=0;i 0) && (resetAttempts < 3)) {
HRESULT hr = _Get_D3D_Device8()->TestCooperativeLevel();
if (FAILED(hr)) {
WWDEBUG_SAY(("TestCooperativeLevel Failed!\n"));
if (D3DERR_DEVICELOST == hr) {
WWDEBUG_SAY(("DEVICELOST: Cannot flip to primary.\n"));
return;
}
if (D3DERR_DEVICENOTRESET == hr) {
WWDEBUG_SAY(("DEVICENOTRESET: Resetting device.\n"));
Reset_Device();
resetAttempts++;
}
} else {
WWDEBUG_SAY(("Flipping: %ld\n", FrameCount));
hr = _Get_D3D_Device8()->Present(NULL, NULL, NULL, NULL);
if (SUCCEEDED(hr)) {
FrameCount++;
WWDEBUG_SAY(("Flip to primary succeeded %ld\n", FrameCount));
}
}
--flipCount;
}
}
}
void DX8Wrapper::Clear(bool clear_color, bool clear_z_stencil, const Vector3 &color, float dest_alpha, float z, unsigned int stencil)
{
DX8_THREAD_ASSERT();
// If we try to clear a stencil buffer which is not there, the entire call will fail
bool has_stencil = ( _PresentParameters.AutoDepthStencilFormat == D3DFMT_D15S1 ||
_PresentParameters.AutoDepthStencilFormat == D3DFMT_D24S8 ||
_PresentParameters.AutoDepthStencilFormat == D3DFMT_D24X4S4);
DWORD flags = 0;
if (clear_color) flags |= D3DCLEAR_TARGET;
if (clear_z_stencil) flags |= D3DCLEAR_ZBUFFER;
if (clear_z_stencil && has_stencil) flags |= D3DCLEAR_STENCIL;
if (flags)
{
DX8CALL(Clear(0, NULL, flags, Convert_Color(color,dest_alpha), z, stencil));
}
}
void DX8Wrapper::Set_Viewport(CONST D3DVIEWPORT8* pViewport)
{
DX8_THREAD_ASSERT();
DX8CALL(SetViewport(pViewport));
}
// ----------------------------------------------------------------------------
//
// Set vertex buffer. A reference to previous vertex buffer is released and
// this one is assigned the current vertex buffer. The DX8 vertex buffer will
// actually be set in Apply() which is called by Draw_Indexed_Triangles().
//
// ----------------------------------------------------------------------------
void DX8Wrapper::Set_Vertex_Buffer(const VertexBufferClass* vb)
{
render_state.vba_offset=0;
render_state.vba_count=0;
if (render_state.vertex_buffer) {
render_state.vertex_buffer->Release_Engine_Ref();
}
REF_PTR_SET(render_state.vertex_buffer,const_cast(vb));
if (vb) {
vb->Add_Engine_Ref();
render_state.vertex_buffer_type=vb->Type();
}
else {
render_state.index_buffer_type=BUFFER_TYPE_INVALID;
}
render_state_changed|=VERTEX_BUFFER_CHANGED;
}
// ----------------------------------------------------------------------------
//
// Set index buffer. A reference to previous index buffer is released and
// this one is assigned the current index buffer. The DX8 index buffer will
// actually be set in Apply() which is called by Draw_Indexed_Triangles().
//
// ----------------------------------------------------------------------------
void DX8Wrapper::Set_Index_Buffer(const IndexBufferClass* ib,unsigned short index_base_offset)
{
render_state.iba_offset=0;
if (render_state.index_buffer) {
render_state.index_buffer->Release_Engine_Ref();
}
REF_PTR_SET(render_state.index_buffer,const_cast(ib));
render_state.index_base_offset=index_base_offset;
if (ib) {
ib->Add_Engine_Ref();
render_state.index_buffer_type=ib->Type();
}
else {
render_state.index_buffer_type=BUFFER_TYPE_INVALID;
}
render_state_changed|=INDEX_BUFFER_CHANGED;
}
// ----------------------------------------------------------------------------
//
// Set vertex buffer using dynamic access object.
//
// ----------------------------------------------------------------------------
void DX8Wrapper::Set_Vertex_Buffer(const DynamicVBAccessClass& vba_)
{
if (render_state.vertex_buffer) render_state.vertex_buffer->Release_Engine_Ref();
DynamicVBAccessClass& vba=const_cast(vba_);
render_state.vertex_buffer_type=vba.Get_Type();
render_state.vba_offset=vba.VertexBufferOffset;
render_state.vba_count=vba.Get_Vertex_Count();
REF_PTR_SET(render_state.vertex_buffer,vba.VertexBuffer);
render_state.vertex_buffer->Add_Engine_Ref();
render_state_changed|=VERTEX_BUFFER_CHANGED;
render_state_changed|=INDEX_BUFFER_CHANGED; // vba_offset changes so index buffer needs to be reset as well.
}
// ----------------------------------------------------------------------------
//
// Set index buffer using dynamic access object.
//
// ----------------------------------------------------------------------------
void DX8Wrapper::Set_Index_Buffer(const DynamicIBAccessClass& iba_,unsigned short index_base_offset)
{
if (render_state.index_buffer) render_state.index_buffer->Release_Engine_Ref();
DynamicIBAccessClass& iba=const_cast(iba_);
render_state.index_base_offset=index_base_offset;
render_state.index_buffer_type=iba.Get_Type();
render_state.iba_offset=iba.IndexBufferOffset;
REF_PTR_SET(render_state.index_buffer,iba.IndexBuffer);
render_state.index_buffer->Add_Engine_Ref();
render_state_changed|=INDEX_BUFFER_CHANGED;
}
// ----------------------------------------------------------------------------
//
// Private function for the special case of rendering polygons from sorting
// index and vertex buffers.
//
// ----------------------------------------------------------------------------
void DX8Wrapper::Draw_Sorting_IB_VB(
unsigned primitive_type,
unsigned short start_index,
unsigned short polygon_count,
unsigned short min_vertex_index,
unsigned short vertex_count)
{
WWASSERT(render_state.vertex_buffer_type==BUFFER_TYPE_SORTING || render_state.vertex_buffer_type==BUFFER_TYPE_DYNAMIC_SORTING);
WWASSERT(render_state.index_buffer_type==BUFFER_TYPE_SORTING || render_state.index_buffer_type==BUFFER_TYPE_DYNAMIC_SORTING);
// Fill dynamic vertex buffer with sorting vertex buffer vertices
DynamicVBAccessClass dyn_vb_access(BUFFER_TYPE_DYNAMIC_DX8,dynamic_fvf_type,vertex_count);
{
DynamicVBAccessClass::WriteLockClass lock(&dyn_vb_access);
VertexFormatXYZNDUV2* src = static_cast(render_state.vertex_buffer)->VertexBuffer;
VertexFormatXYZNDUV2* dest= lock.Get_Formatted_Vertex_Array();
src += render_state.vba_offset + render_state.index_base_offset + min_vertex_index;
unsigned size = dyn_vb_access.FVF_Info().Get_FVF_Size()*vertex_count/sizeof(unsigned);
unsigned *dest_u =(unsigned*) dest;
unsigned *src_u = (unsigned*) src;
for (unsigned i=0;i(dyn_vb_access.VertexBuffer)->Get_DX8_Vertex_Buffer(),
dyn_vb_access.FVF_Info().Get_FVF_Size()));
DX8CALL(SetVertexShader(dyn_vb_access.FVF_Info().Get_FVF()));
DX8_RECORD_VERTEX_BUFFER_CHANGE();
unsigned index_count=0;
switch (primitive_type) {
case D3DPT_TRIANGLELIST: index_count=polygon_count*3; break;
case D3DPT_TRIANGLESTRIP: index_count=polygon_count+2; break;
case D3DPT_TRIANGLEFAN: index_count=polygon_count+2; break;
default: WWASSERT(0); break; // Unsupported primitive type
}
// Fill dynamic index buffer with sorting index buffer vertices
DynamicIBAccessClass dyn_ib_access(BUFFER_TYPE_DYNAMIC_DX8,index_count);
{
DynamicIBAccessClass::WriteLockClass lock(&dyn_ib_access);
unsigned short* dest=lock.Get_Index_Array();
unsigned short* src=NULL;
src=static_cast(render_state.index_buffer)->index_buffer;
src+=render_state.iba_offset+start_index;
for (unsigned short i=0;i(dyn_ib_access.IndexBuffer)->Get_DX8_Index_Buffer(),
dyn_vb_access.VertexBufferOffset));
DX8_RECORD_INDEX_BUFFER_CHANGE();
DX8CALL(DrawIndexedPrimitive(
D3DPT_TRIANGLELIST,
0, // start vertex
vertex_count,
dyn_ib_access.IndexBufferOffset,
polygon_count));
DX8_RECORD_RENDER(polygon_count,vertex_count,render_state.shader);
}
// ----------------------------------------------------------------------------
//
//
//
// ----------------------------------------------------------------------------
void DX8Wrapper::Draw(
unsigned primitive_type,
unsigned short start_index,
unsigned short polygon_count,
unsigned short min_vertex_index,
unsigned short vertex_count)
{
DX8_THREAD_ASSERT();
SNAPSHOT_SAY(("DX8 - draw\n"));
Apply_Render_State_Changes();
// Debug feature to disable triangle drawing...
if (!_Is_Triangle_Draw_Enabled()) return;
SNAPSHOT_SAY(("DX8 - draw %s polygons (%d vertices)\n",polygon_count,vertex_count));
if (vertex_count<3) {
min_vertex_index=0;
switch (render_state.vertex_buffer_type) {
case BUFFER_TYPE_DX8:
case BUFFER_TYPE_SORTING:
vertex_count=render_state.vertex_buffer->Get_Vertex_Count()-render_state.index_base_offset-render_state.vba_offset-min_vertex_index;
break;
case BUFFER_TYPE_DYNAMIC_DX8:
case BUFFER_TYPE_DYNAMIC_SORTING:
vertex_count=render_state.vba_count;
break;
}
}
switch (render_state.vertex_buffer_type) {
case BUFFER_TYPE_DX8:
case BUFFER_TYPE_DYNAMIC_DX8:
switch (render_state.index_buffer_type) {
case BUFFER_TYPE_DX8:
case BUFFER_TYPE_DYNAMIC_DX8:
{
/* if ((start_index+render_state.iba_offset+polygon_count*3) > render_state.index_buffer->Get_Index_Count())
{ WWASSERT_PRINT(0,"OVERFLOWING INDEX BUFFER");
///@todo: MUST FIND OUT WHY THIS HAPPENS WITH LOTS OF PARTICLES ON BIG FIGHT! -MW
break;
}*/
DX8_RECORD_RENDER(polygon_count,vertex_count,render_state.shader);
DX8CALL(DrawIndexedPrimitive(
(D3DPRIMITIVETYPE)primitive_type,
min_vertex_index,
vertex_count,
start_index+render_state.iba_offset,
polygon_count));
}
break;
case BUFFER_TYPE_SORTING:
case BUFFER_TYPE_DYNAMIC_SORTING:
WWASSERT_PRINT(0,"VB and IB must of same type (sorting or dx8)");
break;
case BUFFER_TYPE_INVALID:
WWASSERT(0);
break;
}
break;
case BUFFER_TYPE_SORTING:
case BUFFER_TYPE_DYNAMIC_SORTING:
switch (render_state.index_buffer_type) {
case BUFFER_TYPE_DX8:
case BUFFER_TYPE_DYNAMIC_DX8:
WWASSERT_PRINT(0,"VB and IB must of same type (sorting or dx8)");
break;
case BUFFER_TYPE_SORTING:
case BUFFER_TYPE_DYNAMIC_SORTING:
Draw_Sorting_IB_VB(primitive_type,start_index,polygon_count,min_vertex_index,vertex_count);
break;
case BUFFER_TYPE_INVALID:
WWASSERT(0);
break;
}
break;
case BUFFER_TYPE_INVALID:
WWASSERT(0);
break;
}
}
// ----------------------------------------------------------------------------
//
//
//
// ----------------------------------------------------------------------------
void DX8Wrapper::Draw_Triangles(
unsigned buffer_type,
unsigned short start_index,
unsigned short polygon_count,
unsigned short min_vertex_index,
unsigned short vertex_count)
{
if (buffer_type==BUFFER_TYPE_SORTING || buffer_type==BUFFER_TYPE_DYNAMIC_SORTING) {
SortingRendererClass::Insert_Triangles(start_index,polygon_count,min_vertex_index,vertex_count);
}
else {
Draw(D3DPT_TRIANGLELIST,start_index,polygon_count,min_vertex_index,vertex_count);
}
}
// ----------------------------------------------------------------------------
//
//
//
// ----------------------------------------------------------------------------
void DX8Wrapper::Draw_Triangles(
unsigned short start_index,
unsigned short polygon_count,
unsigned short min_vertex_index,
unsigned short vertex_count)
{
Draw(D3DPT_TRIANGLELIST,start_index,polygon_count,min_vertex_index,vertex_count);
}
// ----------------------------------------------------------------------------
//
//
//
// ----------------------------------------------------------------------------
void DX8Wrapper::Draw_Strip(
unsigned short start_index,
unsigned short polygon_count,
unsigned short min_vertex_index,
unsigned short vertex_count)
{
Draw(D3DPT_TRIANGLESTRIP,start_index,polygon_count,min_vertex_index,vertex_count);
}
// ----------------------------------------------------------------------------
//
//
//
// ----------------------------------------------------------------------------
void DX8Wrapper::Apply_Render_State_Changes()
{
if (!render_state_changed) return;
if (render_state_changed&SHADER_CHANGED) {
SNAPSHOT_SAY(("DX8 - apply shader\n"));
render_state.shader.Apply();
}
unsigned mask=TEXTURE0_CHANGED;
for (unsigned i=0;iApply(i);
else TextureClass::Apply_Null(i);
}
}
if (render_state_changed&MATERIAL_CHANGED) {
SNAPSHOT_SAY(("DX8 - apply material\n"));
VertexMaterialClass* material=const_cast(render_state.material);
if (material) {
material->Apply();
}
else VertexMaterialClass::Apply_Null();
}
if (render_state_changed&LIGHTS_CHANGED)
{
unsigned mask=LIGHT0_CHANGED;
for (unsigned index=0;index<4;++index,mask<<=1) {
if (render_state_changed&mask) {
SNAPSHOT_SAY(("DX8 - apply light %d\n",index));
if (render_state.LightEnable[index]) {
Set_DX8_Light(index,&render_state.Lights[index]);
}
else {
Set_DX8_Light(index,NULL);
}
}
}
}
if (render_state_changed&WORLD_CHANGED) {
SNAPSHOT_SAY(("DX8 - apply world matrix\n"));
_Set_DX8_Transform(D3DTS_WORLD,render_state.world);
}
if (render_state_changed&VIEW_CHANGED) {
SNAPSHOT_SAY(("DX8 - apply view matrix\n"));
_Set_DX8_Transform(D3DTS_VIEW,render_state.view);
}
if (render_state_changed&VERTEX_BUFFER_CHANGED) {
SNAPSHOT_SAY(("DX8 - apply vb change\n"));
if (render_state.vertex_buffer) {
switch (render_state.vertex_buffer_type) {//->Type()) {
case BUFFER_TYPE_DX8:
case BUFFER_TYPE_DYNAMIC_DX8:
DX8CALL(SetStreamSource(
0,
static_cast(render_state.vertex_buffer)->Get_DX8_Vertex_Buffer(),
render_state.vertex_buffer->FVF_Info().Get_FVF_Size()));
DX8_RECORD_VERTEX_BUFFER_CHANGE();
DX8CALL(SetVertexShader(render_state.vertex_buffer->FVF_Info().Get_FVF()));
break;
case BUFFER_TYPE_SORTING:
case BUFFER_TYPE_DYNAMIC_SORTING:
break;
default:
WWASSERT(0);
}
} else {
DX8CALL(SetStreamSource(0,NULL,0));
DX8_RECORD_VERTEX_BUFFER_CHANGE();
}
}
if (render_state_changed&INDEX_BUFFER_CHANGED) {
SNAPSHOT_SAY(("DX8 - apply ib change\n"));
if (render_state.index_buffer) {
switch (render_state.index_buffer_type) {//->Type()) {
case BUFFER_TYPE_DX8:
case BUFFER_TYPE_DYNAMIC_DX8:
DX8CALL(SetIndices(
static_cast(render_state.index_buffer)->Get_DX8_Index_Buffer(),
render_state.index_base_offset+render_state.vba_offset));
DX8_RECORD_INDEX_BUFFER_CHANGE();
break;
case BUFFER_TYPE_SORTING:
case BUFFER_TYPE_DYNAMIC_SORTING:
break;
default:
WWASSERT(0);
}
}
else {
DX8CALL(SetIndices(
NULL,
0));
DX8_RECORD_INDEX_BUFFER_CHANGE();
}
}
render_state_changed&=((unsigned)WORLD_IDENTITY|(unsigned)VIEW_IDENTITY);
}
IDirect3DTexture8 * DX8Wrapper::_Create_DX8_Texture(
unsigned int width,
unsigned int height,
WW3DFormat format,
TextureClass::MipCountType mip_level_count,
D3DPOOL pool,
bool rendertarget)
{
DX8_THREAD_ASSERT();
DX8_Assert();
IDirect3DTexture8 *texture = NULL;
// Paletted textures not supported!
WWASSERT(format!=D3DFMT_P8);
// NOTE: If 'format' is not supported as a texture format, this function will find the closest
// format that is supported and use that instead.
// Render target may return NOTAVAILABLE, in
// which case we return NULL.
if (rendertarget) {
unsigned ret=D3DXCreateTexture(
DX8Wrapper::_Get_D3D_Device8(),
width,
height,
mip_level_count,
D3DUSAGE_RENDERTARGET,
WW3DFormat_To_D3DFormat(format),
pool,
&texture);
if (ret==D3DERR_NOTAVAILABLE) {
Non_Fatal_Log_DX8_ErrorCode(ret,__FILE__,__LINE__);
return NULL;
}
if (ret==D3DERR_OUTOFVIDEOMEMORY) {
Non_Fatal_Log_DX8_ErrorCode(ret,__FILE__,__LINE__);
return NULL;
}
DX8_ErrorCode(ret);
// Just return the texture, no reduction
// allowed for render targets.
return texture;
}
// Don't allow any errors in non-render target
// texture creation.
DX8_ErrorCode(D3DXCreateTexture(
DX8Wrapper::_Get_D3D_Device8(),
width,
height,
mip_level_count,
0,
WW3DFormat_To_D3DFormat(format),
pool,
&texture));
// unsigned reduction=WW3D::Get_Texture_Reduction();
// unsigned level_count=texture->GetLevelCount();
// if (reduction>=level_count) reduction=level_count-1;
// texture->SetLOD(reduction);
return texture;
}
IDirect3DTexture8 * DX8Wrapper::_Create_DX8_Texture(
const char *filename,
TextureClass::MipCountType mip_level_count)
{
DX8_THREAD_ASSERT();
DX8_Assert();
IDirect3DTexture8 *texture = NULL;
// NOTE: If the original image format is not supported as a texture format, it will
// automatically be converted to an appropriate format.
// NOTE: It is possible to get the size and format of the original image file from this
// function as well, so if we later want to second-guess D3DX's format conversion decisions
// we can do so after this function is called..
unsigned result = D3DXCreateTextureFromFileExA(
_Get_D3D_Device8(),
filename,
D3DX_DEFAULT,
D3DX_DEFAULT,
mip_level_count,//create_mipmaps ? 0 : 1,
0,
D3DFMT_UNKNOWN,
D3DPOOL_MANAGED,
D3DX_FILTER_BOX,
D3DX_FILTER_BOX,
0,
NULL,
NULL,
&texture);
if (result != D3D_OK) {
return MissingTexture::_Get_Missing_Texture();
}
// Make sure texture wasn't paletted!
D3DSURFACE_DESC desc;
texture->GetLevelDesc(0,&desc);
if (desc.Format==D3DFMT_P8) {
texture->Release();
return MissingTexture::_Get_Missing_Texture();
}
else {
// unsigned reduction=WW3D::Get_Texture_Reduction();
// unsigned level_count=texture->GetLevelCount();
// if (reduction>=level_count) reduction=level_count-1;
// texture->SetLOD(reduction);
}
return texture;
}
IDirect3DTexture8 * DX8Wrapper::_Create_DX8_Texture(
IDirect3DSurface8 *surface,
TextureClass::MipCountType mip_level_count)
{
DX8_THREAD_ASSERT();
DX8_Assert();
IDirect3DTexture8 *texture = NULL;
D3DSURFACE_DESC surface_desc;
::ZeroMemory(&surface_desc, sizeof(D3DSURFACE_DESC));
surface->GetDesc(&surface_desc);
// This function will create a texture with a different (but similar) format if the surface is
// not in a supported texture format.
WW3DFormat format=D3DFormat_To_WW3DFormat(surface_desc.Format);
texture = _Create_DX8_Texture(surface_desc.Width, surface_desc.Height, format, mip_level_count);
// Copy the surface to the texture
IDirect3DSurface8 *tex_surface = NULL;
texture->GetSurfaceLevel(0, &tex_surface);
DX8_ErrorCode(D3DXLoadSurfaceFromSurface(tex_surface, NULL, NULL, surface, NULL, NULL, D3DX_FILTER_BOX, 0));
tex_surface->Release();
// Create mipmaps if needed
if (mip_level_count!=TextureClass::MIP_LEVELS_1) {
DX8_ErrorCode(D3DXFilterTexture(texture, NULL, 0, D3DX_FILTER_BOX));
}
return texture;
}
IDirect3DSurface8 * DX8Wrapper::_Create_DX8_Surface(unsigned int width, unsigned int height, WW3DFormat format)
{
DX8_THREAD_ASSERT();
DX8_Assert();
IDirect3DSurface8 *surface = NULL;
// Paletted surfaces not supported!
WWASSERT(format!=D3DFMT_P8);
DX8CALL(CreateImageSurface(width, height, WW3DFormat_To_D3DFormat(format), &surface));
return surface;
}
IDirect3DSurface8 * DX8Wrapper::_Create_DX8_Surface(const char *filename_)
{
DX8_THREAD_ASSERT();
DX8_Assert();
// Note: Since there is no "D3DXCreateSurfaceFromFile" and no "GetSurfaceInfoFromFile" (the
// latter is supposed to be added to D3DX in a future version), we create a texture from the
// file (w/o mipmaps), check that its surface is equal to the original file data (which it
// will not be if the file is not in a texture-supported format or size). If so, copy its
// surface (we might be able to just get its surface and add a ref to it but I'm not sure so
// I'm not going to risk it) and release the texture. If not, create a surface according to
// the file data and use D3DXLoadSurfaceFromFile. This is a horrible hack, but it saves us
// having to write file loaders. Will fix this when D3DX provides us with the right functions.
// Create a surface the size of the file image data
IDirect3DSurface8 *surface = NULL;
{
file_auto_ptr myfile(_TheFileFactory,filename_);
// If file not found, create a surface with missing texture in it
if (!myfile->Is_Available()) {
return MissingTexture::_Create_Missing_Surface();
}
}
surface=TextureLoader::Load_Surface_Immediate(
filename_,
WW3D_FORMAT_UNKNOWN,
true);
return surface;
}
/***********************************************************************************************
* DX8Wrapper::_Update_Texture -- Copies a texture from system memory to video memory *
* *
* *
* *
* *
* INPUT: *
* *
* OUTPUT: *
* *
* WARNINGS: *
* *
* HISTORY: *
* 4/26/2001 hy : Created. *
*=============================================================================================*/
void DX8Wrapper::_Update_Texture(TextureClass *system, TextureClass *video)
{
WWASSERT(system);
WWASSERT(video);
WWASSERT(system->Pool==TextureClass::POOL_SYSTEMMEM);
WWASSERT(video->Pool==TextureClass::POOL_DEFAULT);
DX8CALL(UpdateTexture(system->D3DTexture,video->D3DTexture));
}
void DX8Wrapper::Compute_Caps(D3DFORMAT display_format,D3DFORMAT depth_stencil_format)
{
DX8_THREAD_ASSERT();
DX8_Assert();
DX8Caps::Compute_Caps(display_format,depth_stencil_format,D3DDevice);
}
void DX8Wrapper::Set_Light(unsigned index,const LightClass &light)
{
D3DLIGHT8 dlight;
Vector3 temp;
memset(&dlight,0,sizeof(D3DLIGHT8));
switch (light.Get_Type())
{
case LightClass::POINT:
{
dlight.Type=D3DLIGHT_POINT;
}
break;
case LightClass::DIRECTIONAL:
{
dlight.Type=D3DLIGHT_DIRECTIONAL;
}
break;
case LightClass::SPOT:
{
dlight.Type=D3DLIGHT_SPOT;
}
break;
}
light.Get_Diffuse(&temp);
temp*=light.Get_Intensity();
dlight.Diffuse.r=temp.X;
dlight.Diffuse.g=temp.Y;
dlight.Diffuse.b=temp.Z;
dlight.Diffuse.a=1.0f;
light.Get_Specular(&temp);
temp*=light.Get_Intensity();
dlight.Specular.r=temp.X;
dlight.Specular.g=temp.Y;
dlight.Specular.b=temp.Z;
dlight.Specular.a=1.0f;
light.Get_Ambient(&temp);
temp*=light.Get_Intensity();
dlight.Ambient.r=temp.X;
dlight.Ambient.g=temp.Y;
dlight.Ambient.b=temp.Z;
dlight.Ambient.a=1.0f;
temp=light.Get_Position();
dlight.Position=*(D3DVECTOR*) &temp;
light.Get_Spot_Direction(temp);
dlight.Direction=*(D3DVECTOR*) &temp;
dlight.Range=light.Get_Attenuation_Range();
dlight.Falloff=light.Get_Spot_Exponent();
dlight.Theta=light.Get_Spot_Angle();
dlight.Phi=light.Get_Spot_Angle();
// Inverse linear light 1/(1+D)
double a,b;
light.Get_Far_Attenuation_Range(a,b);
dlight.Attenuation0=1.0f;
if (fabs(a-b)<1e-5)
// if the attenuation range is too small assume uniform with cutoff
dlight.Attenuation1=0.0f;
else
// this will cause the light to drop to half intensity at the first far attenuation
dlight.Attenuation1=(float) 1.0/a;
dlight.Attenuation2=0.0f;
Set_Light(index,&dlight);
}
// ----------------------------------------------------------------------------
//
// Set the light environment. This is a lighting model which used up to four
// directional lights to produce the lighting.
//
// ----------------------------------------------------------------------------
void DX8Wrapper::Set_Light_Environment(LightEnvironmentClass* light_env)
{
if (light_env) {
int light_count = light_env->Get_Light_Count();
unsigned int color=Convert_Color(light_env->Get_Equivalent_Ambient(),0.0f);
if (RenderStates[D3DRS_AMBIENT]!=color)
{
Set_DX8_Render_State(D3DRS_AMBIENT,color);
//buggy Radeon 9700 driver doesn't apply new ambient unless the material also changes.
#if 1
render_state_changed|=MATERIAL_CHANGED;
#endif
}
D3DLIGHT8 light;
for (int l=0;lGet_Light_Diffuse(l);
Vector3 dir=-light_env->Get_Light_Direction(l);
light.Direction=(const D3DVECTOR&)(dir);
if (light_env->isPointLight(l)) {
light.Type = D3DLIGHT_POINT;
(Vector3&)light.Diffuse=light_env->getPointDiffuse(l);
(Vector3&)light.Ambient=light_env->getPointAmbient(l);
light.Position = (const D3DVECTOR&)light_env->getPointCenter(l);
light.Range = light_env->getPointOrad(l);
// Inverse linear light 1/(1+D)
double a,b;
b = light_env->getPointOrad(l);
a = light_env->getPointIrad(l);
light.Attenuation0=0.01f;
if (fabs(a-b)<1e-5)
// if the attenuation range is too small assume uniform with cutoff
light.Attenuation1=0.0f;
else
// this will cause the light to drop to half intensity at the first far attenuation
light.Attenuation1=(float) 0.1/a;
light.Attenuation2=8.0f/(b*b);
}
Set_Light(l,&light);
}
for (;l<4;++l) {
Set_Light(l,NULL);
}
}
/* else {
for (int l=0;l<4;++l) {
Set_Light(l,NULL);
}
}
*/
}
IDirect3DSurface8 * DX8Wrapper::_Get_DX8_Front_Buffer()
{
DX8_THREAD_ASSERT();
D3DDISPLAYMODE mode;
DX8CALL(GetDisplayMode(&mode));
IDirect3DSurface8 * fb=NULL;
DX8CALL(CreateImageSurface(mode.Width,mode.Height,D3DFMT_A8R8G8B8,&fb));
DX8CALL(GetFrontBuffer(fb));
return fb;
}
SurfaceClass * DX8Wrapper::_Get_DX8_Back_Buffer(unsigned int num)
{
DX8_THREAD_ASSERT();
IDirect3DSurface8 * bb;
SurfaceClass *surf=NULL;
DX8CALL(GetBackBuffer(num,D3DBACKBUFFER_TYPE_MONO,&bb));
if (bb)
{
surf=NEW_REF(SurfaceClass,(bb));
bb->Release();
}
return surf;
}
TextureClass *
DX8Wrapper::Create_Render_Target (int width, int height, bool alpha)
{
DX8_THREAD_ASSERT();
DX8_Assert();
const D3DCAPS8& dx8caps=DX8Caps::Get_Default_Caps();
//
// Note: We're going to force the width and height to be powers of two and equal
//
float poweroftwosize = width;
if (height > 0 && height < width) {
poweroftwosize = height;
}
poweroftwosize = ::Find_POT (poweroftwosize);
if (poweroftwosize>dx8caps.MaxTextureWidth) {
poweroftwosize=dx8caps.MaxTextureWidth;
}
if (poweroftwosize>dx8caps.MaxTextureHeight) {
poweroftwosize=dx8caps.MaxTextureHeight;
}
width = height = poweroftwosize;
//
// Get the current format of the display
//
D3DDISPLAYMODE mode;
DX8CALL(GetDisplayMode(&mode));
// If the user requested a render-target texture and this device does not support that
// feature, return NULL
HRESULT hr;
if (alpha)
{
//user wants a texture with destination alpha channel - only 1 such format
//ever exists on current hardware - D3DFMT_A8R8G8B8
hr = D3DInterface->CheckDeviceFormat( D3DADAPTER_DEFAULT,
WW3D_DEVTYPE,
mode.Format,
D3DUSAGE_RENDERTARGET,
D3DRTYPE_TEXTURE,
D3DFMT_A8R8G8B8 );
mode.Format=D3DFMT_A8R8G8B8;
}
else
{
hr = D3DInterface->CheckDeviceFormat( D3DADAPTER_DEFAULT,
WW3D_DEVTYPE,
mode.Format,
D3DUSAGE_RENDERTARGET,
D3DRTYPE_TEXTURE,
mode.Format );
}
number_of_DX8_calls++;
if (hr != D3D_OK) {
WWDEBUG_SAY(("DX8Wrapper - Driver cannot create render target!\n"));
return NULL;
}
//
// Attempt to create the render target
//
DX8_Assert();
WW3DFormat format=D3DFormat_To_WW3DFormat(mode.Format);
TextureClass * tex = NEW_REF(TextureClass,(width,height,format,TextureClass::MIP_LEVELS_1,TextureClass::POOL_DEFAULT,true));
// 3dfx drivers are lying in the CheckDeviceFormat call and claiming
// that they support render targets!
if (tex->Peek_DX8_Texture() == NULL) {
WWDEBUG_SAY(("DX8Wrapper - Render target creation failed!\n"));
REF_PTR_RELEASE(tex);
}
return tex;
}
void
DX8Wrapper::Set_Render_Target (TextureClass * texture)
{
WWASSERT(texture != NULL);
SurfaceClass * surf = texture->Get_Surface_Level();
WWASSERT(surf != NULL);
Set_Render_Target(surf->Peek_D3D_Surface());
REF_PTR_RELEASE(surf);
}
void
DX8Wrapper::Set_Render_Target(IDirect3DSwapChain8 *swap_chain)
{
DX8_THREAD_ASSERT();
WWASSERT (swap_chain != NULL);
//
// Get the back buffer for the swap chain
//
LPDIRECT3DSURFACE8 render_target = NULL;
swap_chain->GetBackBuffer (0, D3DBACKBUFFER_TYPE_MONO, &render_target);
//
// Set this back buffer as the render targer
//
Set_Render_Target (render_target);
//
// Release our hold on the back buffer
//
if (render_target != NULL) {
render_target->Release ();
render_target = NULL;
}
return ;
}
void
DX8Wrapper::Set_Render_Target(IDirect3DSurface8 *render_target)
{
DX8_THREAD_ASSERT();
DX8_Assert();
//
// We'll need the depth buffer later...
//
IDirect3DSurface8 *depth_buffer = NULL;
DX8CALL(GetDepthStencilSurface (&depth_buffer));
//
// Should we restore the default render target set a new one?
//
if (render_target == NULL || render_target == DefaultRenderTarget) {
//
// Restore the default render target
//
if (DefaultRenderTarget != NULL) {
DX8CALL(SetRenderTarget (DefaultRenderTarget, depth_buffer));
DefaultRenderTarget->Release ();
DefaultRenderTarget = NULL;
}
//
// Release our hold on the "current" render target
//
if (CurrentRenderTarget != NULL) {
CurrentRenderTarget->Release ();
CurrentRenderTarget = NULL;
}
} else if (render_target != CurrentRenderTarget) {
//
// Get a pointer to the default render target (if necessary)
//
if (DefaultRenderTarget == NULL) {
DX8CALL(GetRenderTarget (&DefaultRenderTarget));
}
//
// Release our hold on the old "current" render target
//
if (CurrentRenderTarget != NULL) {
CurrentRenderTarget->Release ();
CurrentRenderTarget = NULL;
}
//
// Keep a copy of the current render target (for housekeeping)
//
CurrentRenderTarget = render_target;
WWASSERT (CurrentRenderTarget != NULL);
if (CurrentRenderTarget != NULL) {
CurrentRenderTarget->AddRef ();
//
// Switch render targets
//
DX8CALL(SetRenderTarget (CurrentRenderTarget, depth_buffer));
}
}
//
// Free our hold on the depth buffer
//
if (depth_buffer != NULL) {
depth_buffer->Release ();
depth_buffer = NULL;
}
return ;
}
IDirect3DSwapChain8 *
DX8Wrapper::Create_Additional_Swap_Chain (HWND render_window)
{
DX8_Assert();
//
// Configure the presentation parameters for a windowed render target
//
D3DPRESENT_PARAMETERS params = { 0 };
params.BackBufferFormat = _PresentParameters.BackBufferFormat;
params.BackBufferCount = 1;
params.MultiSampleType = D3DMULTISAMPLE_NONE;
params.SwapEffect = D3DSWAPEFFECT_COPY_VSYNC;
params.hDeviceWindow = render_window;
params.Windowed = TRUE;
params.EnableAutoDepthStencil = TRUE;
params.AutoDepthStencilFormat = _PresentParameters.AutoDepthStencilFormat;
params.Flags = 0;
params.FullScreen_RefreshRateInHz = D3DPRESENT_RATE_DEFAULT;
params.FullScreen_PresentationInterval = D3DPRESENT_INTERVAL_DEFAULT;
//
// Create the swap chain
//
IDirect3DSwapChain8 *swap_chain = NULL;
DX8CALL(CreateAdditionalSwapChain(¶ms, &swap_chain));
return swap_chain;
}
void DX8Wrapper::Flush_DX8_Resource_Manager(unsigned int bytes)
{
DX8_Assert();
DX8CALL(ResourceManagerDiscardBytes(bytes));
}
unsigned int DX8Wrapper::Get_Free_Texture_RAM()
{
DX8_Assert();
number_of_DX8_calls++;
return DX8Wrapper::_Get_D3D_Device8()->GetAvailableTextureMem();
}
// Converts a linear gamma ramp to one that is controlled by:
// Gamma - controls the curvature of the middle of the curve
// Bright - controls the minimum value of the curve
// Contrast - controls the difference between the maximum and the minimum of the curve
void DX8Wrapper::Set_Gamma(float gamma,float bright,float contrast,bool calibrate,bool uselimit)
{
gamma=Bound(gamma,0.6f,6.0f);
bright=Bound(bright,-0.5f,0.5f);
contrast=Bound(contrast,0.5f,2.0f);
float oo_gamma=1.0f/gamma;
DX8_Assert();
number_of_DX8_calls++;
DWORD flag=(calibrate?D3DSGR_CALIBRATE:D3DSGR_NO_CALIBRATION);
D3DGAMMARAMP ramp;
float limit;
// IML: I'm not really sure what the intent of the 'limit' variable is. It does not produce useful results for my purposes.
if (uselimit) {
limit=(contrast-1)/2*contrast;
} else {
limit = 0.0f;
}
// HY - arrived at this equation after much trial and error.
for (int i=0; i<256; i++) {
float in,out;
in=i/256.0f;
float x=in-limit;
x=Bound(x,0.0f,1.0f);
x=powf(x,oo_gamma);
out=contrast*x+bright;
out=Bound(out,0.0f,1.0f);
ramp.red[i]=(WORD) (out*65535);
ramp.green[i]=(WORD) (out*65535);
ramp.blue[i]=(WORD) (out*65535);
}
if (DX8Caps::Support_Gamma()) {
DX8Wrapper::_Get_D3D_Device8()->SetGammaRamp(flag,&ramp);
} else {
HWND hwnd = GetDesktopWindow();
HDC hdc = GetDC(hwnd);
if (hdc)
{
SetDeviceGammaRamp (hdc, &ramp);
ReleaseDC (hwnd, hdc);
}
}
}
//============================================================================
// DX8Wrapper::getBackBufferFormat
//============================================================================
WW3DFormat DX8Wrapper::getBackBufferFormat( void )
{
return D3DFormat_To_WW3DFormat( _PresentParameters.BackBufferFormat );
}