summaryrefslogtreecommitdiffhomepage
path: root/Magic2/VideoDX9.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'Magic2/VideoDX9.cpp')
-rw-r--r--Magic2/VideoDX9.cpp3641
1 files changed, 3641 insertions, 0 deletions
diff --git a/Magic2/VideoDX9.cpp b/Magic2/VideoDX9.cpp
new file mode 100644
index 0000000..0c9e24d
--- /dev/null
+++ b/Magic2/VideoDX9.cpp
@@ -0,0 +1,3641 @@
+/* Starshatter OpenSource Distribution
+ Copyright (c) 1997-2004, Destroyer Studios LLC.
+ All Rights Reserved.
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions are met:
+
+ * Redistributions of source code must retain the above copyright notice,
+ this list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above copyright notice,
+ this list of conditions and the following disclaimer in the documentation
+ and/or other materials provided with the distribution.
+ * Neither the name "Destroyer Studios" nor the names of its contributors
+ may be used to endorse or promote products derived from this software
+ without specific prior written permission.
+
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ POSSIBILITY OF SUCH DAMAGE.
+
+ SUBSYSTEM: nGenEx.lib
+ FILE: VideoDX9.cpp
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ Direct3D Video class for DirectX 9
+*/
+
+#include "MemDebug.h"
+#include "VideoDX9.h"
+#include "VideoDX9Enum.h"
+#include "VideoDX9VertexBuffer.h"
+#include "TexDX9.h"
+#include "TexCubeDX9.h"
+#include "Camera.h"
+#include "Color.h"
+#include "DataLoader.h"
+#include "Polygon.h"
+#include "Light.h"
+#include "Solid.h"
+
+// +--------------------------------------------------------------------+
+
+void Print(const char* msg, ...);
+char* D3DErrStr(HRESULT dderr);
+void VideoDX9Error(const char* msg, HRESULT dderr);
+static TexCacheDX9* texcache = 0;
+static TexCubeDX9* environment_cube = 0;
+static bool surface_has_tangent_data = false;
+static Light* main_light;
+static Light* back_light;
+static D3DXMATRIX matrixWorld;
+static D3DXMATRIX matrixView;
+static D3DXMATRIX matrixProj;
+static D3DXMATRIX matrixWorldInverse;
+
+extern int VD3D_describe_things;
+
+#ifndef RELEASE
+#define RELEASE(x) if (x) { x->Release(); x=NULL; }
+#endif
+
+#ifndef F2DW
+#define F2DW(x) (*(DWORD*)(&x))
+#endif
+
+#ifndef DW2I
+#define DW2I(x) (*(int*)(&x))
+#endif
+
+// +--------------------------------------------------------------------+
+
+typedef HRESULT (WINAPI * LPDDCE)(GUID FAR *, LPVOID *, REFIID , IUnknown FAR *);
+
+static D3DMATRIX identity_matrix = {
+ FLOAT(1.0), FLOAT(0.0), FLOAT(0.0), FLOAT(0.0),
+ FLOAT(0.0), FLOAT(1.0), FLOAT(0.0), FLOAT(0.0),
+ FLOAT(0.0), FLOAT(0.0), FLOAT(1.0), FLOAT(0.0),
+ FLOAT(0.0), FLOAT(0.0), FLOAT(0.0), FLOAT(1.0)
+};
+
+// +--------------------------------------------------------------------+
+
+List<Model> model_clients;
+
+class VideoDX9SurfaceData : public VideoPrivateData
+{
+public:
+ VideoDX9SurfaceData(Model* m) : model(m), vertex_buffer(0), index_buffer(0) {
+ if (!model_clients.contains(model))
+ model_clients.append(model);
+ }
+
+ virtual ~VideoDX9SurfaceData() {
+ model_clients.remove(model);
+
+ delete vertex_buffer;
+ delete index_buffer;
+ }
+
+ enum { TYPE = 9001 };
+ virtual int GetType() const { return TYPE; }
+
+ Model* model;
+ VideoDX9VertexBuffer* vertex_buffer;
+ VideoDX9IndexBuffer* index_buffer;
+};
+
+class VideoDX9SegmentData : public VideoPrivateData
+{
+public:
+ VideoDX9SegmentData() : first_vert(0), num_verts(0), first_index(0), num_tris(0) { }
+ virtual ~VideoDX9SegmentData() { }
+
+ enum { TYPE = 9002 };
+ virtual int GetType() const { return TYPE; }
+
+ int first_vert;
+ int num_verts;
+ int first_index;
+ int num_tris;
+};
+
+// +--------------------------------------------------------------------+
+
+static int d3dstate_table[] = {
+ D3DRS_FILLMODE, // FILL_MODE
+ D3DRS_SHADEMODE, // SHADE_MODE
+ D3DRS_LIGHTING, // LIGHTING_ENABLE
+ D3DRS_ZENABLE, // Z_ENABLE
+ D3DRS_ZWRITEENABLE, // Z_WRITE_ENABLE
+ D3DRS_DEPTHBIAS, // Z_BIAS
+ 0, // TEXTURE_FILTER
+ D3DRS_DITHERENABLE, // DITHER_ENABLE
+ D3DRS_SPECULARENABLE, // SPECULAR_ENABLE
+ D3DRS_FOGENABLE, // FOG_ENABLE
+ D3DRS_FOGCOLOR, // FOG_COLOR
+ D3DRS_FOGDENSITY, // FOG_DENSITY
+ D3DRS_STENCILENABLE, // STENCIL_ENABLE
+ 0x11111111, // TEXTURE_WRAP (special case)
+ 0 // LIGHTING_PASS
+};
+
+static const int NUM_SCREEN_VERTS = 1024;
+static const int NUM_SCREEN_INDICES = NUM_SCREEN_VERTS * 2;
+
+// +--------------------------------------------------------------------+
+
+struct VideoDX9ScreenVertex
+{
+ FLOAT sx, sy, sz, rhw;
+ DWORD diffuse;
+ FLOAT tu, tv;
+
+ static DWORD FVF;
+};
+
+DWORD VideoDX9ScreenVertex::FVF = D3DFVF_XYZRHW |
+D3DFVF_DIFFUSE |
+D3DFVF_TEX1;
+
+struct VideoDX9NormalVertex
+{
+ FLOAT x, y, z;
+ FLOAT nx, ny, nz;
+ FLOAT t0u, t0v;
+ FLOAT t1u, t1v;
+ FLOAT tx, ty, tz;
+ FLOAT bx, by, bz;
+
+ static DWORD FVF;
+};
+
+DWORD VideoDX9NormalVertex::FVF = 0;
+
+// Global Vertex Declaration shared by shaders
+D3DVERTEXELEMENT9 videoDX9NormalVertexElements[] =
+{
+ { 0, 0, D3DDECLTYPE_FLOAT3, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_POSITION, 0 },
+ { 0, 12, D3DDECLTYPE_FLOAT3, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_NORMAL, 0 },
+ { 0, 24, D3DDECLTYPE_FLOAT2, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_TEXCOORD, 0 },
+ { 0, 32, D3DDECLTYPE_FLOAT2, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_TEXCOORD, 1 },
+ { 0, 40, D3DDECLTYPE_FLOAT3, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_TANGENT, 2 },
+ { 0, 52, D3DDECLTYPE_FLOAT3, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_BINORMAL, 3 },
+ D3DDECL_END()
+};
+
+struct VideoDX9SolidVertex
+{
+ FLOAT x, y, z;
+ FLOAT nx, ny, nz;
+ FLOAT tu, tv;
+
+ static DWORD FVF;
+};
+
+DWORD VideoDX9SolidVertex::FVF = D3DFVF_XYZ |
+D3DFVF_NORMAL |
+D3DFVF_TEX1 |
+D3DFVF_TEXCOORDSIZE2(0);
+
+struct VideoDX9LuminousVertex
+{
+ FLOAT x, y, z;
+ DWORD diffuse;
+ FLOAT tu, tv;
+
+ static DWORD FVF;
+};
+
+DWORD VideoDX9LuminousVertex::FVF = D3DFVF_XYZ |
+D3DFVF_DIFFUSE |
+D3DFVF_TEX1 |
+D3DFVF_TEXCOORDSIZE2(0);
+
+struct VideoDX9DetailVertex
+{
+ FLOAT x, y, z;
+ DWORD diffuse;
+ DWORD specular;
+ FLOAT tu, tv;
+ FLOAT tu1, tv1;
+
+ static DWORD FVF;
+};
+
+DWORD VideoDX9DetailVertex::FVF = D3DFVF_XYZ |
+D3DFVF_DIFFUSE |
+D3DFVF_SPECULAR |
+D3DFVF_TEX2;
+
+struct VideoDX9LineVertex
+{
+ FLOAT x, y, z;
+ DWORD diffuse;
+
+ static DWORD FVF;
+};
+
+DWORD VideoDX9LineVertex::FVF = D3DFVF_XYZ |
+D3DFVF_DIFFUSE;
+
+enum {
+ DX9_STRATEGY_NONE,
+ DX9_STRATEGY_SIMPLE,
+ DX9_STRATEGY_GLOW,
+ DX9_STRATEGY_SPECMAP,
+ DX9_STRATEGY_EMISSIVE,
+ DX9_STRATEGY_SPEC_EMISSIVE,
+ DX9_STRATEGY_BLEND,
+ DX9_STRATEGY_BLEND_DETAIL
+};
+
+// +--------------------------------------------------------------------+
+
+static VideoDX9* video_dx9_instance = 0;
+
+VideoDX9::VideoDX9(const HWND& window, VideoSettings* vs)
+: width(0), height(0), bpp(0), hwnd(window), surface(0),
+d3d(0), d3ddevice(0), device_lost(false), fade(0),
+zdepth(0), gamma(128), num_verts(0), first_vert(0),
+current_texture(0), screen_vbuf(0), screen_ibuf(0),
+font_verts(0), font_indices(0), font_nverts(0),
+nlights(0), use_material(0), d3dx_font(0),
+segment_material(0), strategy(0), passes(0),
+screen_line_verts(0), line_verts(0),
+vertex_declaration(0),
+magic_fx(0), magic_fx_code(0), magic_fx_code_len(0)
+{
+ video_dx9_instance = this;
+
+ Print("\n********************************\n");
+ Print("* Direct 3D version 9 *\n");
+ Print("********************************\n\n");
+
+ status = VIDEO_ERR;
+ HRESULT err = E_OUTOFMEMORY;
+
+ d3d = Direct3DCreate9(D3D_SDK_VERSION);
+ dx9enum = new(__FILE__,__LINE__) VideoDX9Enum(d3d);
+
+ if (d3d && dx9enum) {
+ if (vs) {
+ dx9enum->req_fullscreen = vs->is_windowed ? false : true;
+ dx9enum->req_windowed = vs->is_windowed ? true : false;
+ dx9enum->min_stencil_bits = vs->shadows ? 8 : 0;
+ dx9enum->uses_depth_buffer = true;
+ }
+ else {
+ dx9enum->req_fullscreen = video_settings.is_windowed ? false : true;
+ dx9enum->req_windowed = video_settings.is_windowed ? true : false;
+ dx9enum->min_stencil_bits = video_settings.shadows ? 8 : 0;
+ dx9enum->uses_depth_buffer = true;
+ }
+
+ err = dx9enum->Enumerate();
+
+ if (FAILED(err)) {
+ VideoDX9Error("(ctor) could not enumerate dx9 properties", err);
+ delete dx9enum;
+ return;
+ }
+ }
+ else {
+ VideoDX9Error("(ctor) could not create enumerator", err);
+ return;
+ }
+
+ SetVideoSettings(vs);
+
+ if (video_settings.is_windowed)
+ dx9enum->SuggestWindowSettings(&video_settings);
+ else
+ dx9enum->SuggestFullscreenSettings(&video_settings);
+
+ SetupParams();
+
+ if (VD3D_describe_things > 2) {
+ Print("\nD3DPRESENT_PARAMETERS:\n");
+ Print(" BackBufferWidth: %d\n", d3dparams.BackBufferWidth);
+ Print(" BackBufferHeight: %d\n", d3dparams.BackBufferHeight);
+ Print(" BackBufferCount: %d\n", d3dparams.BackBufferCount);
+ Print(" BackBufferFormat: %s\n", VideoDX9DisplayMode::D3DFormatToString(d3dparams.BackBufferFormat));
+ Print(" Multisample Type: %d\n", d3dparams.MultiSampleType);
+ Print(" Multisample Qual: %d\n", d3dparams.MultiSampleQuality);
+ Print(" Swap Effect: %d\n", d3dparams.SwapEffect);
+ Print(" Device Window: %08X\n", d3dparams.hDeviceWindow);
+ Print(" Windowed: %s\n", d3dparams.Windowed ? "true" : "false");
+ Print(" Enable Depth/Stencil: %s\n", d3dparams.EnableAutoDepthStencil ? "true" : "false");
+ Print(" Depth/Stencil Format: %s\n", VideoDX9DisplayMode::D3DFormatToString(d3dparams.AutoDepthStencilFormat));
+ Print(" Flags: %08X\n", d3dparams.Flags);
+ Print(" Fullscreen Refresh: %d Hz\n", d3dparams.FullScreen_RefreshRateInHz);
+
+ switch (d3dparams.PresentationInterval) {
+ case D3DPRESENT_INTERVAL_IMMEDIATE:
+ Print(" Present Interval: IMMEDIATE\n");
+ break;
+
+ case D3DPRESENT_INTERVAL_DEFAULT:
+ Print(" Present Interval: DEFAULT\n");
+ break;
+
+ case D3DPRESENT_INTERVAL_ONE:
+ Print(" Present Interval: ONE\n");
+ break;
+
+ case D3DPRESENT_INTERVAL_TWO:
+ Print(" Present Interval: TWO\n");
+ break;
+
+ case D3DPRESENT_INTERVAL_THREE:
+ Print(" Present Interval: THREE\n");
+ break;
+
+ case D3DPRESENT_INTERVAL_FOUR:
+ Print(" Present Interval: FOUR\n");
+ break;
+
+ default:
+ Print(" Present Interval: Unknown (%d)\n", d3dparams.PresentationInterval);
+ break;
+ }
+
+ Print("\n");
+ }
+
+ Print(" Creating Video Device for HWND = %08x\n", window);
+
+ err = d3d->CreateDevice(D3DADAPTER_DEFAULT,
+ D3DDEVTYPE_HAL,
+ window,
+ D3DCREATE_HARDWARE_VERTEXPROCESSING,
+ &d3dparams,
+ &d3ddevice);
+
+ if (FAILED(err)) {
+ VideoDX9Error("(ctor) could not create device", err);
+ return;
+ }
+
+ width = video_settings.GetWidth();
+ height = video_settings.GetHeight();
+ bpp = video_settings.GetDepth();
+
+ shadow_enabled = vs->shadows;
+ bump_enabled = vs->bumpmaps;
+ spec_enabled = vs->specmaps;
+
+ render_state[FILL_MODE] = FILL_SOLID;
+ render_state[SHADE_MODE] = SHADE_GOURAUD;
+ render_state[Z_ENABLE] = false;
+ render_state[Z_WRITE_ENABLE] = false;
+ render_state[Z_BIAS] = 0;
+ render_state[TEXTURE_FILTER] = FILTER_LINEAR;
+ render_state[DITHER_ENABLE] = false;
+ render_state[SPECULAR_ENABLE] = true;
+ render_state[FOG_ENABLE] = false;
+ render_state[FOG_COLOR] = 0;
+ render_state[FOG_DENSITY] = 0;
+ render_state[STENCIL_ENABLE] = false;
+ render_state[TEXTURE_WRAP] = true;
+ render_state[LIGHTING_PASS] = 0;
+
+ ZeroMemory(&rect, sizeof(rect));
+
+ if (!texcache)
+ texcache = new(__FILE__,__LINE__) TexCacheDX9(this);
+
+ if (texcache)
+ texcache->count++;
+
+ if (VD3D_describe_things > 0) {
+ DWORD vmf = VidMemFree() / (1024 * 1024);
+ Print(" Available Texture Memory: %d MB\n\n", vmf);
+ }
+
+ if (CreateBuffers()) {
+ d3ddevice->SetRenderState(D3DRS_ALPHATESTENABLE, false);
+ d3ddevice->SetRenderState(D3DRS_ZFUNC, D3DCMP_LESSEQUAL);
+
+ d3ddevice->SetTextureStageState(0, D3DTSS_ALPHAOP, D3DTOP_MODULATE);
+ d3ddevice->SetTextureStageState(0, D3DTSS_ALPHAARG1, D3DTA_TEXTURE);
+ d3ddevice->SetTextureStageState(0, D3DTSS_ALPHAARG2, D3DTA_DIFFUSE);
+ d3ddevice->SetSamplerState(0, D3DSAMP_MAGFILTER, D3DTEXF_LINEAR);
+ d3ddevice->SetSamplerState(0, D3DSAMP_MINFILTER, D3DTEXF_LINEAR);
+ d3ddevice->SetSamplerState(0, D3DSAMP_MIPFILTER, D3DTEXF_NONE);
+
+ status = VIDEO_OK;
+ }
+
+ ZeroMemory(font_name, 64);
+ font_size = 0;
+ font_bold = false;
+ font_ital = false;
+}
+
+// +--------------------------------------------------------------------+
+
+VideoDX9::~VideoDX9()
+{
+ DestroyBuffers();
+
+ texcache->count--;
+ if (!texcache->count) {
+ delete texcache;
+ texcache = 0;
+ }
+
+ delete environment_cube;
+ delete dx9enum;
+
+ RELEASE(d3dx_font);
+ RELEASE(d3ddevice);
+ RELEASE(d3d);
+
+ if (magic_fx_code)
+ delete [] magic_fx_code;
+
+ Print(" VideoDX9: shutdown\n");
+ video_dx9_instance = 0;
+}
+
+IDirect3DDevice9*
+VideoDX9::GetD3DDevice9()
+{
+ if (video_dx9_instance)
+ return video_dx9_instance->d3ddevice;
+
+ return 0;
+}
+
+// +--------------------------------------------------------------------+
+
+bool
+VideoDX9::SetupParams()
+{
+ if (!dx9enum || dx9enum->NumAdapters() < 1) {
+ status = VIDEO_ERR;
+ return false;
+ }
+
+ int adapter_index = video_settings.GetAdapterIndex();
+
+ if (adapter_index < 0 || adapter_index >= dx9enum->NumAdapters()) {
+ ::Print("WARNING: VideoDX9 could not select adapter %d (max=%d)\n",
+ adapter_index, dx9enum->NumAdapters());
+
+ adapter_index = 0;
+ }
+
+ dx9enum->SelectAdapter(adapter_index);
+
+ d3dparams.Windowed = video_settings.IsWindowed();
+ d3dparams.BackBufferCount = 2;
+ d3dparams.MultiSampleType = D3DMULTISAMPLE_NONE;
+ d3dparams.MultiSampleQuality = 0;
+ d3dparams.SwapEffect = D3DSWAPEFFECT_DISCARD;
+ d3dparams.EnableAutoDepthStencil = dx9enum->uses_depth_buffer;
+ d3dparams.hDeviceWindow = hwnd;
+
+ if (dx9enum->uses_depth_buffer) {
+ d3dparams.Flags = D3DPRESENTFLAG_DISCARD_DEPTHSTENCIL;
+ d3dparams.AutoDepthStencilFormat = (D3DFORMAT) video_settings.GetDepthStencilFormat();
+ }
+ else {
+ d3dparams.Flags = 0;
+ }
+
+ d3dparams.Flags |= D3DPRESENTFLAG_DEVICECLIP;
+
+ if (video_settings.IsWindowed()) {
+ d3dparams.BackBufferWidth = video_settings.window_width;
+ d3dparams.BackBufferHeight = video_settings.window_height;
+ d3dparams.BackBufferFormat = (D3DFORMAT) video_settings.GetBackBufferFormat();
+ d3dparams.FullScreen_RefreshRateInHz = 0;
+ d3dparams.PresentationInterval = D3DPRESENT_INTERVAL_IMMEDIATE;
+ }
+ else {
+ d3dparams.BackBufferWidth = video_settings.GetWidth();
+ d3dparams.BackBufferHeight = video_settings.GetHeight();
+ d3dparams.BackBufferFormat = (D3DFORMAT) video_settings.GetBackBufferFormat();
+ d3dparams.FullScreen_RefreshRateInHz = video_settings.GetRefreshRate();
+ d3dparams.PresentationInterval = D3DPRESENT_INTERVAL_DEFAULT;
+ }
+
+ return true;
+}
+
+
+bool
+VideoDX9::IsModeSupported(int w, int h, int b) const
+{
+ if (dx9enum)
+ return dx9enum->IsModeSupported(w, h, b);
+
+ return false;
+}
+
+bool
+VideoDX9::SetVideoSettings(const VideoSettings* vs)
+{
+ // custom video settings:
+ if (vs) {
+ if (vs != &video_settings)
+ CopyMemory(&video_settings, vs, sizeof(VideoSettings));
+ }
+
+ // default video settings:
+ else {
+ ZeroMemory(&video_settings, sizeof(VideoSettings));
+
+ video_settings.fullscreen_mode.width = 800;
+ video_settings.fullscreen_mode.height = 600;
+ video_settings.fullscreen_mode.format = VideoMode::FMT_X8R8G8B8;
+ }
+
+ return true;
+}
+
+bool
+VideoDX9::Reset(const VideoSettings* vs)
+{
+ if (!d3ddevice || !SetVideoSettings(vs)) {
+ status = VIDEO_ERR;
+ return false;
+ }
+
+ bool using_x_font = (d3dx_font != 0);
+
+ RELEASE(d3dx_font);
+ InvalidateCache();
+ DestroyBuffers();
+ SetupParams();
+
+ HRESULT hr = d3ddevice->Reset(&d3dparams);
+
+ if (FAILED(hr)) {
+ VideoDX9Error("could not reset d3d device", hr);
+ status = VIDEO_ERR;
+ return false;
+ }
+
+ // Store render target surface desc
+ IDirect3DSurface9* back_buffer;
+ d3ddevice->GetBackBuffer(0, 0, D3DBACKBUFFER_TYPE_MONO, &back_buffer);
+ back_buffer->GetDesc(&back_buffer_desc);
+ RELEASE(back_buffer);
+
+ width = video_settings.GetWidth();
+ height = video_settings.GetHeight();
+ bpp = video_settings.GetDepth();
+
+ shadow_enabled = vs->shadows;
+ bump_enabled = vs->bumpmaps;
+ spec_enabled = vs->specmaps;
+
+
+ if (CreateBuffers()) {
+ d3ddevice->SetRenderState(D3DRS_ALPHATESTENABLE, false);
+ d3ddevice->SetRenderState(D3DRS_ZFUNC, D3DCMP_LESSEQUAL);
+
+ d3ddevice->SetTextureStageState(0, D3DTSS_ALPHAOP, D3DTOP_MODULATE);
+ d3ddevice->SetTextureStageState(0, D3DTSS_ALPHAARG1, D3DTA_TEXTURE);
+ d3ddevice->SetTextureStageState(0, D3DTSS_ALPHAARG2, D3DTA_DIFFUSE);
+ d3ddevice->SetSamplerState(0, D3DSAMP_MAGFILTER, D3DTEXF_LINEAR);
+ d3ddevice->SetSamplerState(0, D3DSAMP_MINFILTER, D3DTEXF_LINEAR);
+ d3ddevice->SetSamplerState(0, D3DSAMP_MIPFILTER, D3DTEXF_NONE);
+
+ D3DVIEWPORT9 view;
+
+ hr = d3ddevice->GetViewport(&view);
+ if (SUCCEEDED(hr)) {
+ rect.x = view.X;
+ rect.y = view.Y;
+ rect.w = view.Width;
+ rect.h = view.Height;
+ }
+
+ if (using_x_font)
+ UseXFont(font_name, font_size, font_bold, font_ital);
+
+ status = VIDEO_OK;
+ }
+
+ return status == VIDEO_OK;
+}
+
+// +--------------------------------------------------------------------+
+
+bool
+VideoDX9::CreateBuffers()
+{
+ if (d3ddevice) {
+ UINT vertex_size = sizeof(VideoDX9ScreenVertex);
+ UINT index_size = sizeof(WORD);
+
+ if (!screen_vbuf) {
+ screen_vbuf = new(__FILE__,__LINE__) VideoDX9VertexBuffer(
+ this,
+ NUM_SCREEN_VERTS,
+ vertex_size,
+ VideoDX9ScreenVertex::FVF,
+ D3DUSAGE_DYNAMIC | D3DUSAGE_WRITEONLY);
+ }
+
+ if (!screen_ibuf) {
+ screen_ibuf = new(__FILE__,__LINE__) VideoDX9IndexBuffer(
+ this,
+ NUM_SCREEN_INDICES,
+ D3DUSAGE_DYNAMIC | D3DUSAGE_WRITEONLY);
+ }
+
+ screen_line_verts = new(__FILE__,__LINE__) VideoDX9ScreenVertex[256];
+ line_verts = new(__FILE__,__LINE__) VideoDX9LineVertex[512];
+
+ // create effects:
+ LPD3DXBUFFER code_buffer = 0;
+ DataLoader* loader = DataLoader::GetLoader();
+ HRESULT hr = E_FAIL;
+
+ hr = d3ddevice->CreateVertexDeclaration(videoDX9NormalVertexElements,
+ &vertex_declaration);
+
+ // The E - We want to load our shader from the standard filesystem by default, to allow for better modding.
+ if (video_settings.use_effects && !magic_fx_code) {
+ FILE* f;
+ ::fopen_s(&f, "magic.fx", "rb");
+
+ if (f) {
+ ::fseek(f, 0, SEEK_END);
+ magic_fx_code_len = ftell(f);
+ ::fseek(f, 0, SEEK_SET);
+
+ magic_fx_code = new(__FILE__,__LINE__) BYTE[magic_fx_code_len+1];
+ if (magic_fx_code) {
+ ::fread(magic_fx_code, magic_fx_code_len, 1, f);
+ magic_fx_code[magic_fx_code_len] = 0;
+ }
+ ::fclose(f);
+ } else if (loader) {
+ magic_fx_code_len = loader->LoadBuffer("magic.fx", magic_fx_code, true, true);
+ }
+ }
+
+ if (video_settings.use_effects && magic_fx_code && magic_fx_code_len) {
+ hr = D3DXCreateEffect(d3ddevice,
+ magic_fx_code,
+ magic_fx_code_len,
+ 0, 0, 0, 0,
+ &magic_fx,
+ &code_buffer);
+
+ if (code_buffer) {
+ ::Print("ERROR - Failed to compile 'magic.fx'\n");
+ ::Print((const char*) code_buffer->GetBufferPointer());
+ ::Print("\n\n");
+ RELEASE(code_buffer);
+ }
+ }
+ }
+
+ return screen_vbuf && screen_ibuf;
+}
+
+bool
+VideoDX9::DestroyBuffers()
+{
+ if (line_verts) {
+ delete line_verts;
+ line_verts = 0;
+ }
+
+ if (screen_line_verts) {
+ delete screen_line_verts;
+ screen_line_verts = 0;
+ }
+
+ if (screen_vbuf) {
+ delete screen_vbuf;
+ screen_vbuf = 0;
+ }
+
+ if (screen_ibuf) {
+ delete screen_ibuf;
+ screen_ibuf = 0;
+ }
+
+ if (font_verts) {
+ delete [] font_verts;
+ font_verts = 0;
+ }
+
+ if (font_indices) {
+ delete [] font_indices;
+ font_indices = 0;
+ }
+
+ font_nverts = 0;
+
+ RELEASE(vertex_declaration);
+ RELEASE(magic_fx);
+
+ return true;
+}
+
+// +--------------------------------------------------------------------+
+
+DWORD
+VideoDX9::VidMemFree() const
+{
+ UINT result = 0;
+
+ if (d3ddevice)
+ result = d3ddevice->GetAvailableTextureMem();
+
+ return result;
+}
+
+int
+VideoDX9::MaxTexSize() const
+{
+ if (d3d && dx9enum && dx9enum->GetAdapterInfo()) {
+ VideoDX9DeviceInfo* dev_info = dx9enum->GetDeviceInfo(video_settings.GetDeviceType());
+
+ if (dev_info) {
+ return (int) dev_info->caps.MaxTextureWidth;
+ }
+ }
+
+ return 0;
+}
+
+int
+VideoDX9::MaxTexAspect() const
+{
+ if (d3d && dx9enum && dx9enum->GetAdapterInfo()) {
+ VideoDX9DeviceInfo* dev_info = dx9enum->GetDeviceInfo(video_settings.GetDeviceType());
+
+ if (dev_info) {
+ return (int) dev_info->caps.MaxTextureAspectRatio;
+ }
+ }
+
+ return 0;
+}
+
+// +--------------------------------------------------------------------+
+
+void
+VideoDX9::RecoverSurfaces()
+{
+ Print("VideoDX9::RecoverSurfaces()\n");
+
+ HRESULT hr = D3D_OK;
+
+ surface = 0;
+
+ hr = d3ddevice->TestCooperativeLevel();
+
+ if (hr == D3DERR_DEVICELOST) {
+ // This means that some app took exclusive mode access
+ // we need to sit in a loop till we get back to the right mode.
+ Print("D3DERR_DEVICELOST\n");
+
+ do {
+ Sleep(500);
+ hr = d3ddevice->TestCooperativeLevel();
+ } while (hr == D3DERR_DEVICELOST);
+ }
+
+ if (hr == D3DERR_DEVICENOTRESET) {
+ if (Reset(&video_settings))
+ hr = S_OK;
+ }
+
+ if (SUCCEEDED(hr)) {
+ Print("* Invalidating Texture Cache\n");
+ // Re-fill the contents of textures which just got restored:
+ InvalidateCache();
+
+ device_lost = false;
+ }
+
+ Print("* Vid Mem Free: %8d\n", VidMemFree());
+ Print("* Recover Surfaces Complete.\n\n");
+}
+
+// +--------------------------------------------------------------------+
+
+bool
+VideoDX9::SetBackgroundColor(Color c)
+{
+ background = c;
+ return true;
+}
+
+//-----------------------------------------------------------------------------
+// RampValue
+//
+// The gamma function with inputs in [0,255], scaled to a range with the
+// default range appropriate for D3DGAMMARAMP.
+//
+inline WORD
+RampValue(UINT i, double recip_gamma, double fade)
+{
+ return (WORD) (65535.0 * fade * pow((double)i/255.f, recip_gamma));
+}
+
+//-----------------------------------------------------------------------------
+// ReciprocalGamma
+//
+// Given a gamma corrected i in [0,255], return 1/gamma
+//
+inline float
+ReciprocalGamma(UINT i)
+{
+ return logf(i/255.f)/logf(0.5f);
+}
+
+//-----------------------------------------------------------------------------
+// GammaValue
+//
+// Given a gamma corrected color channel value in [0,255], return the gamma.
+//
+inline float
+GammaValue(UINT i)
+{
+ return logf(0.5f)/logf(i/255.f);
+}
+
+bool
+VideoDX9::SetGammaLevel(int g)
+{
+ HRESULT hr = E_FAIL;
+ double f = Color::GetFade();
+
+ if (gamma != g || fade != f) {
+ if (d3ddevice) {
+ //::Print("VideoDX9 - SetGammaLevel(%d) fade = %f\n", g, f);
+
+ // compute 1/gamma
+ float recip_gray = ReciprocalGamma(g);
+
+ // compute i**(1/gamma) for all i and scale to range
+ for (UINT i = 0; i < 256; i++) {
+ int val = RampValue(i, recip_gray, f);
+
+ gamma_ramp.red[i] = val;
+ gamma_ramp.green[i] = val;
+ gamma_ramp.blue[i] = val;
+ }
+
+ d3ddevice->SetGammaRamp(0, D3DSGR_NO_CALIBRATION, &gamma_ramp);
+ hr = D3D_OK;
+ }
+
+ gamma = g;
+ fade = f;
+ }
+
+ return SUCCEEDED(hr);
+}
+
+bool
+VideoDX9::SetObjTransform(const Matrix& orient, const Point& loc)
+{
+ HRESULT hr = E_FAIL;
+
+ if (d3ddevice) {
+ D3DMATRIX world_matrix;
+ CreateD3DMatrix(world_matrix, orient, loc);
+ hr = d3ddevice->SetTransform(D3DTS_WORLD, &world_matrix);
+
+ matrixWorld = world_matrix;
+ D3DXMatrixInverse(&matrixWorldInverse, 0, &matrixWorld);
+ }
+
+ return SUCCEEDED(hr);
+}
+
+// +--------------------------------------------------------------------+
+
+bool
+VideoDX9::ClearAll()
+{
+ HRESULT err;
+
+ err = d3ddevice->Clear(0,
+ NULL,
+ D3DCLEAR_TARGET | D3DCLEAR_STENCIL | D3DCLEAR_ZBUFFER,
+ background.Value(),
+ 1.0f,
+ 0);
+
+ if (FAILED(err)) {
+ static int report = 10;
+ if (report > 0) {
+ VideoDX9Error("Failed to clear device", err);
+ report--;
+ }
+ }
+
+ return true;
+}
+
+bool
+VideoDX9::ClearDepthBuffer()
+{
+ HRESULT err;
+
+ err = d3ddevice->Clear(0,
+ NULL,
+ D3DCLEAR_STENCIL | D3DCLEAR_ZBUFFER,
+ 0,
+ 1.0f,
+ 0);
+
+ if (FAILED(err)) {
+ static int report = 10;
+ if (report > 0) {
+ VideoDX9Error("Failed to clear depth buffer", err);
+ report--;
+ }
+ }
+
+ return true;
+}
+
+// +--------------------------------------------------------------------+
+
+bool
+VideoDX9::Present()
+{
+ // Show the frame on the primary surface.
+ HRESULT err = d3ddevice->Present( NULL, NULL, NULL, NULL );
+
+ if (FAILED(err)) {
+ if (err == D3DERR_DEVICELOST) {
+ device_lost = true;
+ }
+
+ else {
+ static int report = 10;
+ if (report > 0) {
+ VideoDX9Error("Could not present frame", err);
+ report--;
+ }
+ }
+ }
+
+ return true;
+}
+
+// +--------------------------------------------------------------------+
+
+bool
+VideoDX9::Pause()
+{
+ return true;
+}
+
+// +--------------------------------------------------------------------+
+
+bool
+VideoDX9::Resume()
+{
+ return true;
+}
+
+// +--------------------------------------------------------------------+
+
+void
+VideoDX9::PreloadSurface(Surface* s)
+{
+ if (s)
+ PrepareSurface(s);
+}
+
+void
+VideoDX9::PreloadTexture(Bitmap* tex)
+{
+ if (texcache && tex)
+ texcache->FindTexture(tex);
+}
+
+void
+VideoDX9::InvalidateCache()
+{
+ ListIter<Model> iter = model_clients;
+ while (++iter) {
+ // remove each model from the list...
+ Model* model = iter.removeItem();
+
+ // ...so that the buffer destructor doesn't
+ // do it and mess up the iterator.
+ model->DeletePrivateData();
+ }
+
+ if (texcache)
+ texcache->InvalidateCache();
+}
+
+// +--------------------------------------------------------------------+
+
+void
+VideoDX9::CreateD3DMatrix(D3DMATRIX& result, const Matrix& m, const Point& p)
+{
+ result._11 = (float) m.elem[0][0];
+ result._12 = (float) m.elem[1][0];
+ result._13 = (float) m.elem[2][0];
+ result._14 = 0.0f;
+
+ result._21 = (float) m.elem[0][1];
+ result._22 = (float) m.elem[1][1];
+ result._23 = (float) m.elem[2][1];
+ result._24 = 0.0f;
+
+ result._31 = (float) m.elem[0][2];
+ result._32 = (float) m.elem[1][2];
+ result._33 = (float) m.elem[2][2];
+ result._34 = 0.0f;
+
+ result._41 = (float) p.x;
+ result._42 = (float) p.y;
+ result._43 = (float) p.z;
+ result._44 = 1.0f;
+}
+
+void
+VideoDX9::CreateD3DMatrix(D3DMATRIX& result, const Matrix& m, const Vec3& v)
+{
+ result._11 = (float) m.elem[0][0];
+ result._12 = (float) m.elem[1][0];
+ result._13 = (float) m.elem[2][0];
+ result._14 = 0.0f;
+
+ result._21 = (float) m.elem[0][1];
+ result._22 = (float) m.elem[1][1];
+ result._23 = (float) m.elem[2][1];
+ result._24 = 0.0f;
+
+ result._31 = (float) m.elem[0][2];
+ result._32 = (float) m.elem[1][2];
+ result._33 = (float) m.elem[2][2];
+ result._34 = 0.0f;
+
+ result._41 = v.x;
+ result._42 = v.y;
+ result._43 = v.z;
+ result._44 = 1.0f;
+}
+
+void
+VideoDX9::CreateD3DMaterial(D3DMATERIAL9& result, const Material& mtl)
+{
+ CopyMemory(&result.Diffuse, &mtl.Kd, sizeof(D3DCOLORVALUE));
+ CopyMemory(&result.Ambient, &mtl.Ka, sizeof(D3DCOLORVALUE));
+ CopyMemory(&result.Specular, &mtl.Ks, sizeof(D3DCOLORVALUE));
+ CopyMemory(&result.Emissive, &mtl.Ke, sizeof(D3DCOLORVALUE));
+
+ result.Power = mtl.power;
+}
+
+
+// +--------------------------------------------------------------------+
+
+bool
+VideoDX9::Capture(Bitmap& bmp)
+{
+ if (d3ddevice) {
+ HRESULT hr = E_FAIL;
+ LPDIRECT3DSURFACE9 pSurf=NULL, pTempSurf=NULL;
+ D3DSURFACE_DESC desc;
+ D3DDISPLAYMODE dm;
+
+ // get display dimensions
+ // this will be the dimensions of the front buffer
+ hr = d3ddevice->GetDisplayMode(0, &dm);
+
+ if (FAILED(hr))
+ VideoDX9Error("VideoDX9::Capture - Can't get display mode!", hr);
+
+ desc.Width = dm.Width;
+ desc.Height = dm.Height;
+ desc.Format = D3DFMT_A8R8G8B8;
+
+ hr = d3ddevice->CreateOffscreenPlainSurface(
+ desc.Width,
+ desc.Height,
+ desc.Format,
+ D3DPOOL_SYSTEMMEM,
+ &pTempSurf,
+ NULL);
+
+ if (FAILED(hr)) {
+ VideoDX9Error("VideoDX9::Capture - Cannot create offscreen buffer 1", hr);
+ return false;
+ }
+
+ hr = d3ddevice->GetFrontBufferData(0, pTempSurf);
+
+ if (FAILED(hr)) {
+ RELEASE(pTempSurf);
+ VideoDX9Error("VideoDX9::Capture - Can't get front buffer", hr);
+ return false;
+ }
+
+
+ if (video_settings.IsWindowed()) {
+ POINT pt={0, 0};
+ RECT srcRect;
+
+ // capture only the client area of the screen:
+ ::GetClientRect(hwnd, &srcRect);
+ ::ClientToScreen(hwnd, (LPPOINT) &srcRect);
+ srcRect.right += srcRect.left;
+ srcRect.bottom += srcRect.top;
+
+ desc.Width = srcRect.right - srcRect.left;
+ desc.Height = srcRect.bottom - srcRect.top;
+ desc.Format = D3DFMT_A8R8G8B8; // this is what we get from the screen, so stick with it
+
+ // NB we can't lock the back buffer direct because it's no created that way
+ // and to do so hits performance, so copy to another surface
+ // Must be the same format as the source surface
+ hr = d3ddevice->CreateOffscreenPlainSurface(
+ desc.Width,
+ desc.Height,
+ desc.Format,
+ D3DPOOL_DEFAULT,
+ &pSurf,
+ NULL);
+
+ if (FAILED(hr)) {
+ RELEASE(pSurf);
+ VideoDX9Error("VideoDX9::Capture - Cannot create offscreen buffer 2", hr);
+ return false;
+ }
+
+ // Copy
+ hr = d3ddevice->UpdateSurface(pTempSurf, &srcRect, pSurf, &pt);
+
+ if (FAILED(hr)) {
+ RELEASE(pTempSurf);
+ RELEASE(pSurf);
+ VideoDX9Error("VideoDX9::Capture - Cannot update surface", hr);
+ return false;
+ }
+
+ RELEASE(pTempSurf);
+ pTempSurf = pSurf;
+ pSurf = NULL;
+ }
+
+ D3DLOCKED_RECT lockedRect;
+ hr = pTempSurf->LockRect(&lockedRect, NULL,
+ D3DLOCK_READONLY | D3DLOCK_NOSYSLOCK);
+
+ if (FAILED(hr)) {
+ VideoDX9Error("VideoDX9::Capture - can't lock rect", hr);
+ RELEASE(pTempSurf);
+ return false;
+ }
+
+ // Allocate color buffer
+ DWORD* buffer = new DWORD[desc.Width * desc.Height];
+ BYTE* src = (BYTE*) lockedRect.pBits;
+ BYTE* dst = (BYTE*) buffer;
+ Color clr;
+
+ for (DWORD y = 0; y < desc.Height; y++) {
+ BYTE *pRow = src;
+
+ for (DWORD x = 0; x < desc.Width; x++) {
+ switch(desc.Format) {
+ case D3DFMT_R5G6B5:
+ clr = Color::Unformat(*((WORD*) (pRow)));
+
+ *dst++ = (BYTE) clr.Red();
+ *dst++ = (BYTE) clr.Green();
+ *dst++ = (BYTE) clr.Blue();
+ *dst++ = 255;
+ break;
+
+ case D3DFMT_A8R8G8B8:
+ case D3DFMT_X8R8G8B8:
+ *dst++ = pRow[0]; // R
+ *dst++ = pRow[1]; // G
+ *dst++ = pRow[2]; // B
+ *dst++ = 255;
+
+ pRow += 4;
+ break;
+
+ case D3DFMT_R8G8B8:
+ *dst++ = pRow[0]; // R
+ *dst++ = pRow[1]; // G
+ *dst++ = pRow[2]; // B
+ *dst++ = 255;
+
+ pRow += 3;
+ break;
+ }
+
+ }
+
+ src += lockedRect.Pitch;
+ }
+
+ bmp.CopyHighColorImage(desc.Width, desc.Height, buffer);
+
+ delete [] buffer;
+
+ RELEASE(pTempSurf);
+ RELEASE(pSurf);
+
+ return SUCCEEDED(hr);
+ }
+
+ return false;
+}
+
+// +--------------------------------------------------------------------+
+
+bool
+VideoDX9::GetWindowRect(Rect& r)
+{
+ if (d3ddevice && (rect.w < 1 || rect.h < 1)) {
+ D3DVIEWPORT9 view;
+ HRESULT hr = d3ddevice->GetViewport(&view);
+ if (SUCCEEDED(hr)) {
+ rect.x = view.X;
+ rect.y = view.Y;
+ rect.w = view.Width;
+ rect.h = view.Height;
+ }
+ }
+
+ r = rect;
+ return true;
+}
+
+// +--------------------------------------------------------------------+
+
+bool
+VideoDX9::SetWindowRect(const Rect& r)
+{
+ return SetViewport(r.x, r.y, r.w, r.h);
+}
+
+// +--------------------------------------------------------------------+
+
+bool
+VideoDX9::SetViewport(int x, int y, int w, int h)
+{
+ if (!d3d || !d3ddevice)
+ return false;
+
+ HRESULT hr;
+
+ // set up the viewport according to args:
+ D3DVIEWPORT9 view;
+
+ view.X = x;
+ view.Y = y;
+ view.Width = w;
+ view.Height = h;
+ view.MinZ = 0.0f;
+ view.MaxZ = 1.0f;
+
+ hr = d3ddevice->SetViewport(&view);
+ if (FAILED(hr)) {
+ VideoDX9Error("could not initialize viewport", hr);
+ return false;
+ }
+
+ // set up the render state:
+ for (int i = FILL_MODE; i < TEXTURE_WRAP; i++) {
+ if (d3dstate_table[i]) {
+ d3ddevice->SetRenderState((D3DRENDERSTATETYPE) d3dstate_table[i], render_state[i]);
+ }
+ }
+
+ d3ddevice->SetRenderState(D3DRS_ZFUNC, D3DCMP_LESSEQUAL);
+
+ rect.x = x;
+ rect.y = y;
+ rect.w = w;
+ rect.h = h;
+
+ return true;
+}
+
+// +--------------------------------------------------------------------+
+
+bool
+VideoDX9::SetAmbient(Color c)
+{
+ ambient = c;
+ return true;
+}
+
+bool
+VideoDX9::SetLights(const List<Light>& lights)
+{
+ if (d3ddevice) {
+ main_light = 0;
+ back_light = 0;
+
+ ListIter<Light> iter = (List<Light>&) lights;
+ int index = -1;
+
+ while (++iter) {
+ Light* light = iter.value();
+
+ if (light->IsActive()) {
+ D3DLIGHT9 d3d_light;
+ ZeroMemory(&d3d_light, sizeof(d3d_light));
+ d3d_light.Type = (D3DLIGHTTYPE) light->Type();
+
+ if (light->Type() == Light::LIGHT_DIRECTIONAL) {
+ d3d_light.Direction.x = (float) (-light->Location().x);
+ d3d_light.Direction.y = (float) (-light->Location().y);
+ d3d_light.Direction.z = (float) (-light->Location().z);
+
+ if (d3d_light.Direction.x == 0 &&
+ d3d_light.Direction.y == 0 &&
+ d3d_light.Direction.z == 0) {
+
+ d3d_light.Direction.y = -1;
+ }
+
+ if (light->CastsShadow()) {
+ if (!main_light || light->Intensity() > main_light->Intensity())
+ main_light = light;
+ }
+ else if (!back_light) {
+ back_light = light;
+ }
+ }
+ else {
+ d3d_light.Position.x = (float) ( light->Location().x);
+ d3d_light.Position.y = (float) ( light->Location().y);
+ d3d_light.Position.z = (float) ( light->Location().z);
+ }
+
+ float r = (light->GetColor().Red() / 255.0f) * light->Intensity();
+ float g = (light->GetColor().Green() / 255.0f) * light->Intensity();
+ float b = (light->GetColor().Blue() / 255.0f) * light->Intensity();
+
+ d3d_light.Diffuse.r = r;
+ d3d_light.Diffuse.g = g;
+ d3d_light.Diffuse.b = b;
+
+ d3d_light.Specular.r = r;
+ d3d_light.Specular.g = g;
+ d3d_light.Specular.b = b;
+
+ d3d_light.Range = light->Intensity() * 10.0f;
+ d3d_light.Attenuation0 = 0.1f;
+ d3d_light.Attenuation1 = 0.7f;
+ d3d_light.Attenuation2 = 0.0f;
+
+ index++;
+ d3ddevice->SetLight(index, &d3d_light);
+ d3ddevice->LightEnable(index, TRUE);
+ }
+ }
+
+ // turn off any unused lights from before:
+ while (nlights > index+1) {
+ d3ddevice->LightEnable(--nlights, FALSE);
+ }
+
+ nlights = index + 1;
+
+ return true;
+ }
+
+ return false;
+}
+
+bool
+VideoDX9::SetCamera(const Camera* cam)
+{
+ if (d3ddevice) {
+ camera = cam;
+
+ D3DMATRIX m;
+ CreateD3DMatrix(m, cam->Orientation(), cam->Pos());
+ d3ddevice->SetTransform(D3DTS_VIEW, &m);
+ matrixView = m;
+
+ return true;
+ }
+
+ return false;
+}
+
+bool
+VideoDX9::SetProjection(float fov, float znear, float zfar, DWORD type)
+{
+ if (d3ddevice && zfar > znear) {
+ D3DMATRIX m;
+ float h, w, Q;
+
+ double width = (float) (rect.w);
+ double height = (float) (rect.h);
+ ZeroMemory(&m, sizeof(m));
+
+ /***
+ *** PERSPECTIVE PROJECTION:
+ ***/
+
+ if (type == PROJECTION_PERSPECTIVE) {
+ double xscale = width / fov;
+ double yscale = height / fov;
+
+ double maxscale = xscale;
+ if (yscale > xscale) maxscale = yscale;
+
+ double xangle = atan(fov/2 * maxscale/xscale);
+
+ w = (float) (2/tan(xangle)); // 1/tan(x) == cot(x)
+ h = (float) (w * width/height);
+ Q = zfar/(zfar - znear);
+
+ m._11 = w;
+ m._22 = h;
+ m._33 = Q;
+ m._43 = -Q*znear;
+ m._34 = 1;
+ }
+
+ /***
+ *** ORTHOGONAL PROJECTION:
+ ***/
+
+ else if (type == PROJECTION_ORTHOGONAL) {
+ m._11 = (float) (fov/width);
+ m._22 = (float) (fov/height);
+ m._33 = (float) (1/(zfar-znear));
+ m._43 = (float) (znear/(znear-zfar));
+ m._44 = (float) (1);
+ }
+
+ else {
+ return false;
+ }
+
+ d3ddevice->SetTransform(D3DTS_PROJECTION, &m);
+ matrixProj = m;
+
+ return true;
+ }
+
+ return false;
+}
+
+// +--------------------------------------------------------------------+
+
+bool
+VideoDX9::SetEnvironment(Bitmap** faces)
+{
+ if (environment_cube && !faces) {
+ delete environment_cube;
+ environment_cube = 0;
+ return true;
+ }
+
+ if (!environment_cube) {
+ environment_cube = new(__FILE__,__LINE__) TexCubeDX9(this);
+ }
+
+ if (environment_cube) {
+ bool ok = true;
+ for (int i = 0; i < 6; i++)
+ ok = ok && environment_cube->LoadTexture(faces[i], i);
+ return ok;
+ }
+
+ return false;
+}
+
+// +--------------------------------------------------------------------+
+
+bool
+VideoDX9::SetRenderState(RENDER_STATE state, DWORD value)
+{
+ if (!d3ddevice)
+ return false;
+
+ if (render_state[state] == value || d3dstate_table[state] == 0) {
+ render_state[state] = value;
+ return true;
+ }
+
+ HRESULT hr = E_FAIL;
+
+ // special case for texture wrapping:
+ if (state == TEXTURE_WRAP) {
+ DWORD wrap = D3DTADDRESS_CLAMP;
+
+ if (value)
+ wrap = D3DTADDRESS_WRAP;
+
+ hr = d3ddevice->SetSamplerState(0, D3DSAMP_ADDRESSU, wrap);
+ hr = d3ddevice->SetSamplerState(0, D3DSAMP_ADDRESSV, wrap);
+ }
+
+ // special case for fog enable:
+ else if (state == FOG_ENABLE) {
+ if (value) {
+ hr = d3ddevice->SetRenderState(D3DRS_FOGVERTEXMODE, D3DFOG_EXP);
+ hr = d3ddevice->SetRenderState(D3DRS_FOGTABLEMODE, D3DFOG_NONE);
+ }
+
+ hr = d3ddevice->SetRenderState(D3DRS_FOGENABLE, value);
+ }
+
+ // special case for z bias
+ else if (state == Z_BIAS) {
+ if (value) {
+ FLOAT bias_scale = 1.0f;
+ FLOAT depth_bias = (FLOAT) (DW2I(value) / -10000.0);
+
+ hr = d3ddevice->SetRenderState(D3DRS_SLOPESCALEDEPTHBIAS, F2DW(bias_scale));
+ hr = d3ddevice->SetRenderState(D3DRS_DEPTHBIAS, F2DW(depth_bias));
+ }
+ else {
+ hr = d3ddevice->SetRenderState(D3DRS_SLOPESCALEDEPTHBIAS, 0);
+ hr = d3ddevice->SetRenderState(D3DRS_DEPTHBIAS, 0);
+ }
+ }
+
+ // set default z func along with z enable
+ else if (state == Z_ENABLE) {
+ hr = d3ddevice->SetRenderState(D3DRS_ZFUNC, D3DCMP_LESSEQUAL);
+ hr = d3ddevice->SetRenderState(D3DRS_ZENABLE, value);
+ }
+
+ // all other render states:
+ else {
+ hr = d3ddevice->SetRenderState((D3DRENDERSTATETYPE) d3dstate_table[state], value);
+ }
+
+ if (FAILED(hr)) {
+ VideoDX9Error("could not SetRenderState", hr);
+ return false;
+ }
+ else {
+ render_state[state] = value;
+ }
+
+ return true;
+}
+
+bool
+VideoDX9::SetBlendType(int blend_type)
+{
+ if (blend_type == current_blend_state)
+ return true;
+
+ switch (blend_type) {
+ default:
+ // map misc blend types to SOLID
+ // and fall through to that case
+
+ blend_type = BLEND_SOLID;
+
+ case BLEND_SOLID:
+ d3ddevice->SetRenderState(D3DRS_ALPHABLENDENABLE, false);
+ break;
+
+ case BLEND_ALPHA:
+ d3ddevice->SetRenderState(D3DRS_ALPHABLENDENABLE, true);
+ d3ddevice->SetRenderState(D3DRS_SRCBLEND, D3DBLEND_SRCALPHA);
+ d3ddevice->SetRenderState(D3DRS_DESTBLEND, D3DBLEND_INVSRCALPHA);
+ break;
+
+ case BLEND_ADDITIVE:
+ d3ddevice->SetRenderState(D3DRS_ALPHABLENDENABLE, true);
+ d3ddevice->SetRenderState(D3DRS_SRCBLEND, D3DBLEND_ONE);
+ d3ddevice->SetRenderState(D3DRS_DESTBLEND, D3DBLEND_ONE);
+ break;
+ }
+
+ current_blend_state = blend_type;
+ return true;
+}
+
+// +--------------------------------------------------------------------+
+
+bool
+VideoDX9::StartFrame()
+{
+ if (device_lost) {
+ RecoverSurfaces();
+
+ if (status != VIDEO_OK)
+ return false;
+ }
+
+ stats.Clear();
+
+ HRESULT err = 0;
+ static int frame_number = 1;
+ static int report_errs = 100;
+
+ stats.nframe = frame_number;
+ texcache->FrameNumber(frame_number++);
+
+ // update gamma ramp for global fade value:
+ SetGammaLevel(gamma);
+
+ ClearAll();
+
+ err = d3ddevice->BeginScene();
+
+ if (FAILED(err)) {
+ if (report_errs > 0) {
+ report_errs--;
+ VideoDX9Error("could not begin scene", err);
+ }
+
+ return false;
+ }
+
+ scene_active = 1;
+ current_blend_state = -1;
+
+ SetRenderState(LIGHTING_PASS, 0);
+ SetRenderState(STENCIL_ENABLE, false);
+
+ return true;
+}
+
+// +--------------------------------------------------------------------+
+
+bool
+VideoDX9::EndFrame()
+{
+ HRESULT err = 0;
+
+ if (scene_active) {
+ err = d3ddevice->EndScene();
+
+ if (FAILED(err)) {
+ VideoDX9Error("could not end scene", err);
+ return false;
+ }
+ }
+
+ scene_active = 0;
+ return true;
+}
+
+// +--------------------------------------------------------------------+
+
+static DWORD ColorModulate(DWORD a, DWORD b)
+{
+ float a0 = (float) ((a ) & 0xff)/255.0f;
+ float a1 = (float) ((a>> 8) & 0xff)/255.0f;
+ float a2 = (float) ((a>>16) & 0xff)/255.0f;
+ float a3 = (float) ((a>>24) & 0xff)/255.0f;
+
+ float b0 = (float) ((b ) & 0xff)/255.0f;
+ float b1 = (float) ((b>> 8) & 0xff)/255.0f;
+ float b2 = (float) ((b>>16) & 0xff)/255.0f;
+ float b3 = (float) ((b>>24) & 0xff)/255.0f;
+
+ return (DWORD) ((BYTE)(a3*b3*255.0f) << 24) |
+ ((BYTE)(a2*b2*255.0f) << 16) |
+ ((BYTE)(a1*b1*255.0f) << 8) |
+ ((BYTE)(a0*b0*255.0f));
+}
+
+// +--------------------------------------------------------------------+
+
+bool
+VideoDX9::PopulateScreenVerts(VertexSet* vset)
+{
+ if (!vset || !screen_vbuf)
+ return false;
+
+ num_verts = vset->nverts;
+
+ VideoDX9ScreenVertex* v = (VideoDX9ScreenVertex*) screen_vbuf->Lock(num_verts);
+
+ if (v) {
+ first_vert = screen_vbuf->GetNextVert();
+
+ for (int i = 0; i < num_verts; i++) {
+ v->sx = vset->s_loc[i].x;
+ v->sy = vset->s_loc[i].y;
+ v->sz = vset->s_loc[i].z;
+ v->rhw = vset->rw[i];
+
+ v->diffuse = vset->diffuse[i];
+
+ v->tu = vset->tu[i];
+ v->tv = vset->tv[i];
+
+ v++;
+ }
+
+ screen_vbuf->Unlock();
+ return true;
+ }
+
+ Print(" VideoDX9: could not lock screen vbuf for %d verts.\n", num_verts);
+ return false;
+}
+
+// +--------------------------------------------------------------------+
+
+bool
+VideoDX9::DrawPolys(int npolys, Poly* polys)
+{
+ bool result = false;
+
+ if (d3ddevice && polys && npolys > 0) {
+ // screen space polys:
+ if (polys->vertex_set->space == VertexSet::SCREEN_SPACE)
+ return DrawScreenPolys(npolys, polys);
+
+ // world space polys:
+ stats.ncalls++;
+
+ VertexSet* vset = polys->vertex_set;
+ int nverts = vset->nverts;
+ bool luminous = false;
+ bool detail = false;
+ DWORD fvf = 0;
+ void* verts = 0;
+ int vsize = 0;
+ WORD* indices = new(__FILE__,__LINE__) WORD[npolys*6];
+
+ if (polys->material) {
+ luminous = polys->material->luminous;
+ }
+
+ if (vset->tu1 != 0) {
+ VideoDX9DetailVertex* v = new(__FILE__,__LINE__) VideoDX9DetailVertex[nverts];
+ verts = v;
+ vsize = sizeof(VideoDX9DetailVertex);
+ fvf = VideoDX9DetailVertex::FVF;
+
+ for (int i = 0; i < nverts; i++) {
+ v->x = vset->loc[i].x;
+ v->y = vset->loc[i].y;
+ v->z = vset->loc[i].z;
+
+ v->diffuse = vset->diffuse[i];
+ v->specular = vset->specular[i];
+
+ v->tu = vset->tu[i];
+ v->tv = vset->tv[i];
+ v->tu1 = vset->tu1[i];
+ v->tv1 = vset->tv1[i];
+
+ v++;
+ }
+ }
+
+ else if (luminous) {
+ VideoDX9LuminousVertex* v = new(__FILE__,__LINE__) VideoDX9LuminousVertex[nverts];
+ verts = v;
+ vsize = sizeof(VideoDX9LuminousVertex);
+ fvf = VideoDX9LuminousVertex::FVF;
+
+ for (int i = 0; i < nverts; i++) {
+ v->x = vset->loc[i].x;
+ v->y = vset->loc[i].y;
+ v->z = vset->loc[i].z;
+
+ v->diffuse = vset->diffuse[i];
+
+ v->tu = vset->tu[i];
+ v->tv = vset->tv[i];
+
+ v++;
+ }
+ }
+
+ else {
+ VideoDX9SolidVertex* v = new(__FILE__,__LINE__) VideoDX9SolidVertex[nverts];
+ verts = v;
+ vsize = sizeof(VideoDX9SolidVertex);
+ fvf = VideoDX9SolidVertex::FVF;
+
+ for (int i = 0; i < nverts; i++) {
+ v->x = vset->loc[i].x;
+ v->y = vset->loc[i].y;
+ v->z = vset->loc[i].z;
+
+ v->nx = vset->nrm[i].x;
+ v->ny = vset->nrm[i].y;
+ v->nz = vset->nrm[i].z;
+
+ v->tu = vset->tu[i];
+ v->tv = vset->tv[i];
+
+ v++;
+ }
+ }
+
+ if (verts && indices) {
+ HRESULT hr = E_FAIL;
+
+ // fill index array
+ int num_indices = 0;
+ int num_tris = 0;
+
+ WORD* s = indices;
+ Poly* p = polys;
+
+ for (int i = 0; i < npolys; i++) {
+ if (p->nverts == 3) {
+ num_indices += 3;
+ num_tris += 1;
+
+ *s++ = p->verts[0];
+ *s++ = p->verts[1];
+ *s++ = p->verts[2];
+ }
+
+ else if (p->nverts == 4) {
+ num_indices += 6;
+ num_tris += 2;
+
+ *s++ = p->verts[0];
+ *s++ = p->verts[1];
+ *s++ = p->verts[2];
+
+ *s++ = p->verts[0];
+ *s++ = p->verts[2];
+ *s++ = p->verts[3];
+ }
+
+ p++;
+ }
+
+ hr = d3ddevice->SetTransform(D3DTS_WORLD, &identity_matrix);
+ hr = d3ddevice->SetVertexShader(NULL);
+ hr = d3ddevice->SetFVF(fvf);
+
+ // send primitives to the device
+ Material* mtl = polys->material;
+ IDirect3DTexture9* texture = 0;
+
+ if (mtl && texcache && mtl->tex_diffuse) {
+ texture = texcache->FindTexture(mtl->tex_diffuse);
+ }
+
+ if (current_texture != texture) {
+ hr = d3ddevice->SetTexture(0, texture);
+ current_texture = texture;
+ }
+
+ if (mtl && texcache && mtl->tex_detail) {
+ texture = texcache->FindTexture(mtl->tex_detail);
+ hr = d3ddevice->SetTexture(1, texture);
+ }
+
+ if (mtl && !luminous) {
+ D3DMATERIAL9 d3dmtl;
+ CreateD3DMaterial(d3dmtl, *mtl);
+
+ hr = d3ddevice->SetMaterial(&d3dmtl);
+ hr = d3ddevice->SetRenderState(D3DRS_AMBIENT, ambient.Value());
+ }
+
+ // set render states and select buffers
+ SetRenderState(FILL_MODE, D3DFILL_SOLID);
+ SetRenderState(LIGHTING_ENABLE, luminous ? FALSE : TRUE);
+ SetBlendType(mtl->blend);
+
+ d3ddevice->SetTextureStageState(0, D3DTSS_COLOROP, D3DTOP_MODULATE);
+ d3ddevice->SetTextureStageState(0, D3DTSS_COLORARG1, D3DTA_TEXTURE);
+ d3ddevice->SetTextureStageState(0, D3DTSS_COLORARG2, D3DTA_DIFFUSE);
+ d3ddevice->SetTextureStageState(0, D3DTSS_ALPHAOP, D3DTOP_MODULATE);
+ d3ddevice->SetTextureStageState(0, D3DTSS_ALPHAARG1, D3DTA_TEXTURE);
+ d3ddevice->SetTextureStageState(0, D3DTSS_ALPHAARG2, D3DTA_DIFFUSE);
+
+ d3ddevice->SetTextureStageState(1, D3DTSS_COLOROP, D3DTOP_DISABLE);
+
+
+ hr = d3ddevice->DrawIndexedPrimitiveUP(
+ D3DPT_TRIANGLELIST,
+ 0,
+ nverts,
+ num_tris,
+ indices,
+ D3DFMT_INDEX16,
+ verts,
+ vsize);
+
+ if (FAILED(hr)) {
+ static int report = 10;
+ if (report-- > 0)
+ VideoDX9Error("Could not draw 3D polys.", hr);
+ }
+
+ delete [] verts;
+ delete [] indices;
+
+ if (SUCCEEDED(hr)) {
+ stats.nverts += nverts;
+ stats.npolys += num_tris;
+ result = true;
+ }
+ }
+ }
+
+ return result;
+}
+
+// +--------------------------------------------------------------------+
+
+bool
+VideoDX9::DrawScreenPolys(int npolys, Poly* polys, int blend)
+{
+ bool result = false;
+ HRESULT hr = E_FAIL;
+
+ if (d3ddevice && polys && npolys > 0 && screen_vbuf && screen_ibuf) {
+ stats.ncalls++;
+
+ // fill screen vertex buffer
+ if (!PopulateScreenVerts(polys->vertex_set))
+ return false;
+
+ // fill screen index buffer
+ int num_indices = 0;
+ int num_tris = 0;
+
+ // count the number of indices needed for these polys
+ for (int i = 0; i < npolys; i++) {
+ Poly* p = polys + i;
+
+ if (p->nverts == 3) {
+ num_indices += 3;
+ num_tris += 1;
+ }
+
+ else if (p->nverts == 4) {
+ num_indices += 6;
+ num_tris += 2;
+ }
+ }
+
+ WORD* screen_indices = screen_ibuf->Lock(num_indices);
+ int first_index = screen_ibuf->GetNextIndex();
+
+ if (!screen_indices) {
+ Print(" VideoDX9: could not lock screen ibuf for %d indices.\n", num_indices);
+ return false;
+ }
+
+ // copy the indices into the locked index buffer
+ WORD* s = screen_indices;
+ Poly* p = polys;
+
+ for (int i = 0; i < npolys; i++) {
+ if (p->nverts == 3) {
+ *s++ = p->verts[0] + first_vert;
+ *s++ = p->verts[1] + first_vert;
+ *s++ = p->verts[2] + first_vert;
+ }
+ else if (p->nverts == 4) {
+ *s++ = p->verts[0] + first_vert;
+ *s++ = p->verts[1] + first_vert;
+ *s++ = p->verts[2] + first_vert;
+
+ *s++ = p->verts[0] + first_vert;
+ *s++ = p->verts[2] + first_vert;
+ *s++ = p->verts[3] + first_vert;
+ }
+
+ p++;
+ }
+
+ screen_ibuf->Unlock();
+
+ // set render states and select buffers
+ SetRenderState(FILL_MODE, D3DFILL_SOLID);
+ SetRenderState(Z_ENABLE, D3DZB_FALSE);
+ SetRenderState(Z_WRITE_ENABLE, D3DZB_FALSE);
+ SetRenderState(LIGHTING_ENABLE, FALSE);
+ SetBlendType(blend);
+
+ d3ddevice->SetTextureStageState(0, D3DTSS_COLOROP, D3DTOP_MODULATE);
+ d3ddevice->SetTextureStageState(0, D3DTSS_COLORARG1, D3DTA_TEXTURE);
+ d3ddevice->SetTextureStageState(0, D3DTSS_COLORARG2, D3DTA_DIFFUSE);
+ d3ddevice->SetTextureStageState(0, D3DTSS_ALPHAOP, D3DTOP_MODULATE);
+ d3ddevice->SetTextureStageState(0, D3DTSS_ALPHAARG1, D3DTA_TEXTURE);
+ d3ddevice->SetTextureStageState(0, D3DTSS_ALPHAARG2, D3DTA_DIFFUSE);
+
+ d3ddevice->SetTextureStageState(1, D3DTSS_COLOROP, D3DTOP_DISABLE);
+
+ // send primitives to the device
+ Material* mtl = polys->material;
+ IDirect3DTexture9* texture = 0;
+
+ if (mtl && texcache && mtl->tex_diffuse) {
+ texture = texcache->FindTexture(mtl->tex_diffuse);
+ }
+
+ if (current_texture != texture) {
+ hr = d3ddevice->SetTexture(0, texture);
+ current_texture = texture;
+ }
+
+ screen_vbuf->Select(0);
+ screen_ibuf->Select();
+ hr = d3ddevice->SetVertexShader(NULL);
+ hr = d3ddevice->SetFVF(VideoDX9ScreenVertex::FVF);
+
+ hr = d3ddevice->DrawIndexedPrimitive(
+ D3DPT_TRIANGLELIST,
+ 0,
+ first_vert,
+ num_verts,
+ first_index,
+ num_tris);
+
+ if (FAILED(hr)) {
+ static int report = 10;
+ if (report-- > 0)
+ VideoDX9Error("Could not draw screen polys.", hr);
+ }
+ else {
+ stats.nverts += num_verts;
+ stats.npolys += num_tris;
+ result = true;
+ }
+ }
+
+ return result;
+}
+
+// +--------------------------------------------------------------------+
+
+bool
+VideoDX9::DrawSolid(Solid* s, DWORD blend_modes)
+{
+ bool result = false;
+ HRESULT hr = E_FAIL;
+
+ if (d3ddevice && s && s->GetModel()) {
+ Model* model = s->GetModel();
+ Matrix orient = s->Orientation();
+ orient.Transpose();
+
+ D3DMATRIX world_matrix;
+ CreateD3DMatrix(world_matrix, orient, s->Location());
+ d3ddevice->SetTransform(D3DTS_WORLD, &world_matrix);
+ matrixWorld = world_matrix;
+ D3DXMatrixInverse(&matrixWorldInverse, 0, &matrixWorld);
+
+ ListIter<Surface> surf_iter = model->GetSurfaces();
+ while (++surf_iter) {
+ Surface* surf = surf_iter.value();
+
+ if (surf->IsHidden() || surf->IsSimplified())
+ continue;
+
+ if (PrepareSurface(surf)) {
+ result = true;
+
+ VideoDX9SurfaceData* surf_data = (VideoDX9SurfaceData*) surf->GetVideoPrivateData();
+ surf_data->vertex_buffer->Select(0);
+ surf_data->index_buffer->Select();
+
+ ListIter<Segment> seg_iter = surf->GetSegments();
+ while (++seg_iter) {
+ Segment* segment = seg_iter.value();
+ Material* mtl = segment->material;
+
+ if (mtl && (blend_modes & mtl->blend)) {
+ result = result && DrawSegment(segment);
+ }
+ }
+ }
+ }
+ }
+
+ surface_has_tangent_data = false;
+
+ return result;
+}
+
+bool
+VideoDX9::DrawSegment(Segment* segment)
+{
+ bool result = false;
+ bool detail = false;
+ bool luminous = false;
+ HRESULT hr = E_FAIL;
+
+ if (segment && segment->video_data) {
+ stats.ncalls++;
+
+ VideoDX9SegmentData* seg_data = (VideoDX9SegmentData*) segment->video_data;
+ int first_vert = seg_data->first_vert;
+ int num_verts = seg_data->num_verts;
+ int first_index = seg_data->first_index;
+ int num_tris = seg_data->num_tris;
+
+ if (segment->model)
+ luminous = segment->model->IsLuminous();
+
+ // set render states and select buffers
+ d3ddevice->SetRenderState(D3DRS_AMBIENT, ambient.Value());
+
+ Material* mtl = segment->material;
+
+ if (use_material)
+ mtl = use_material;
+
+ if (segment->polys && segment->polys->vertex_set && segment->polys->vertex_set->tu1)
+ detail = true;
+
+ // send primitives to the device
+ if (detail) {
+ hr = d3ddevice->SetVertexShader(NULL);
+ hr = d3ddevice->SetFVF(VideoDX9DetailVertex::FVF);
+ }
+
+ else if (luminous) {
+ hr = d3ddevice->SetVertexShader(NULL);
+ hr = d3ddevice->SetFVF(VideoDX9LuminousVertex::FVF);
+ }
+
+ else if (surface_has_tangent_data && vertex_declaration) {
+ hr = d3ddevice->SetVertexDeclaration(vertex_declaration);
+ hr = d3ddevice->SetVertexShader(NULL);
+ }
+
+ else {
+ hr = d3ddevice->SetVertexShader(NULL);
+ hr = d3ddevice->SetFVF(VideoDX9SolidVertex::FVF);
+ }
+
+ if (render_state[FILL_MODE] == FILL_WIREFRAME) {
+ PrepareMaterial(mtl);
+ SetupPass(0);
+
+ for (int i = 0; i < segment->npolys; i++) {
+ DrawPolyOutline(segment->polys + i);
+ }
+ }
+
+ else if (luminous) {
+ PrepareMaterial(mtl);
+
+ SetRenderState(FILL_MODE, D3DFILL_SOLID);
+ SetRenderState(LIGHTING_ENABLE, FALSE);
+ SetBlendType(mtl->blend);
+
+ for (int pass = 0; pass < passes; pass++) {
+ SetupPass(pass);
+
+ hr = d3ddevice->DrawIndexedPrimitive(
+ D3DPT_TRIANGLELIST,
+ 0,
+ first_vert,
+ num_verts,
+ first_index,
+ num_tris);
+ }
+
+ d3ddevice->SetTextureStageState(1, D3DTSS_COLOROP, D3DTOP_DISABLE);
+ }
+
+ else {
+ PrepareMaterial(mtl);
+
+ if (strategy == DX9_STRATEGY_GLOW && render_state[LIGHTING_PASS] > 0) {
+ hr = 0;
+ }
+
+ else if (magic_fx && strategy < DX9_STRATEGY_BLEND && !detail) {
+ DWORD vs_version = 0;
+ DWORD ps_version = 0;
+ bool shaders_ok = false;
+
+ VideoDX9DeviceInfo* dev_info = dx9enum->GetDeviceInfo(video_settings.GetDeviceType());
+
+ if (dev_info) {
+ vs_version = video_settings.enable_vs ? dev_info->caps.VertexShaderVersion : 0;
+ ps_version = video_settings.enable_ps ? dev_info->caps.PixelShaderVersion : 0;
+
+ if (vs_version >= D3DVS_VERSION(1,1))
+ shaders_ok = true;
+ }
+
+ if (surface_has_tangent_data && vertex_declaration) {
+ hr = d3ddevice->SetVertexShader(NULL);
+ hr = d3ddevice->SetVertexDeclaration(vertex_declaration);
+ }
+
+ else {
+ hr = d3ddevice->SetVertexShader(NULL);
+ hr = d3ddevice->SetFVF(VideoDX9SolidVertex::FVF);
+ }
+
+ bool would_bump = !IsBumpMapEnabled() && mtl->bump > 0.5;
+
+ bool will_bump = IsBumpMapEnabled() && surface_has_tangent_data && shaders_ok && camera;
+
+ bool do_pix = will_bump && render_state[LIGHTING_PASS] == 0;
+ bool do_bump = will_bump && render_state[LIGHTING_PASS] > 0;
+
+ D3DXMATRIX matrixWVP = matrixWorld * matrixView * matrixProj;
+
+ D3DXVECTOR4 eyePos( (float) camera->Pos().x, (float) camera->Pos().y, (float) camera->Pos().z, 1.0f);
+
+ D3DXVECTOR4 eyeObj;
+ D3DXVec4Transform(&eyeObj, &eyePos, &matrixWorldInverse);
+
+ D3DXVECTOR4 lightPos(100.0f, 100.0f, 100.0f, 1.0f);
+ D3DXVECTOR4 lightColor(1,1,1,1);
+ D3DXVECTOR4 ambientColor(0,0,0,1);
+
+ ambientColor.x = ambient.fRed();
+ ambientColor.y = ambient.fGreen();
+ ambientColor.z = ambient.fBlue();
+
+ if (main_light && (render_state[LIGHTING_PASS] > 0 || mtl->blend > Material::MTL_SOLID)) {
+ lightPos.x = (float) main_light->Location().x;
+ lightPos.y = (float) main_light->Location().y;
+ lightPos.z = (float) main_light->Location().z;
+
+ if (mtl->tex_bumpmap && do_bump)
+ D3DXVec4Transform(&lightPos, &lightPos, &matrixWorldInverse);
+
+ lightColor.x = main_light->GetColor().fRed() * main_light->Intensity();
+ lightColor.y = main_light->GetColor().fGreen() * main_light->Intensity();
+ lightColor.z = main_light->GetColor().fBlue() * main_light->Intensity();
+ lightColor.w = 1.0f;
+ }
+
+ else if (back_light && render_state[LIGHTING_PASS] == 0) {
+ lightPos.x = (float) back_light->Location().x;
+ lightPos.y = (float) back_light->Location().y;
+ lightPos.z = (float) back_light->Location().z;
+
+ lightColor.x = back_light->GetColor().fRed() * back_light->Intensity();
+ lightColor.y = back_light->GetColor().fGreen() * back_light->Intensity();
+ lightColor.z = back_light->GetColor().fBlue() * back_light->Intensity();
+ lightColor.w = 1.0f;
+ }
+
+ D3DXVECTOR4 lightDir = D3DXVECTOR4(0.0f, 0.0f, 0.0f, 0.0f) - lightPos;
+ D3DXVec4Normalize(&lightDir, &lightDir);
+
+ magic_fx->SetMatrix("wvp", &matrixWVP);
+ magic_fx->SetMatrix("world", &matrixWorld);
+ magic_fx->SetMatrix("view", &matrixView);
+ magic_fx->SetMatrix("proj", &matrixProj);
+ magic_fx->SetMatrix("worldInv", &matrixWorldInverse);
+
+ magic_fx->SetVector("light1Pos", &lightPos);
+ magic_fx->SetVector("light1Dir", &lightDir);
+ magic_fx->SetVector("light1Color", &lightColor);
+ magic_fx->SetVector("ambientColor", &ambientColor);
+ magic_fx->SetVector("eyeObj", &eyeObj);
+
+ FLOAT base_bias = (FLOAT) (DW2I(render_state[Z_BIAS]) / -10000.0);
+ magic_fx->SetFloat("bias", base_bias + video_settings.depth_bias);
+
+ ColorValue orig_ks = mtl->Ks;
+
+ if (would_bump && mtl->specular_value >= 0.5)
+ mtl->Ks = mtl->Ks * 0.3;
+
+ magic_fx->SetValue("Ka", &mtl->Ka, sizeof(ColorValue));
+ magic_fx->SetValue("Kd", &mtl->Kd, sizeof(ColorValue));
+ magic_fx->SetValue("Ke", &mtl->Ke, sizeof(ColorValue));
+ magic_fx->SetValue("Ks", &mtl->Ks, sizeof(ColorValue));
+ magic_fx->SetFloat("Ns", mtl->power);
+
+ if (would_bump && mtl->specular_value >= 0.5)
+ mtl->Ks = orig_ks;
+
+ if (mtl->tex_diffuse) {
+ IDirect3DTexture9* texture = texcache->FindTexture(mtl->tex_diffuse);
+ magic_fx->SetTexture("tex_d", texture);
+ } else {
+ magic_fx->SetTexture("tex_d", 0);
+ }
+
+ if (mtl->tex_emissive) {
+ IDirect3DTexture9* texture = texcache->FindTexture(mtl->tex_emissive);
+ magic_fx->SetTexture("tex_e", texture);
+ } else {
+ magic_fx->SetTexture("tex_e", 0);
+ }
+
+ if (mtl->tex_specular && IsSpecMapEnabled()) {
+ IDirect3DTexture9* texture = texcache->FindTexture(mtl->tex_specular);
+ magic_fx->SetTexture("tex_s", texture);
+ } else {
+ magic_fx->SetTexture("tex_s", 0);
+ }
+
+ if (mtl->tex_bumpmap && do_bump) {
+ IDirect3DTexture9* texture = texcache->FindNormalMap(mtl->tex_bumpmap, mtl->bump);
+ magic_fx->SetTexture("tex_n", texture);
+ magic_fx->SetFloat("offsetAmp", mtl->bump / mtl->tex_bumpmap->Height());
+ } else if (mtl->tex_bumpmap && !do_bump) {
+ IDirect3DTexture9* texture = texcache->FindTexture(mtl->tex_bumpmap);
+ magic_fx->SetTexture("tex_x", texture);
+ } else {
+ magic_fx->SetTexture("tex_x", 0);
+ }
+
+ const char* mtl_shader = mtl->GetShader(render_state[LIGHTING_PASS]);
+ D3DXHANDLE hnd_shader = 0;
+
+ if (mtl_shader) {
+ if (!strcmp(mtl_shader, "null"))
+ return true;
+
+ hnd_shader = magic_fx->GetTechniqueByName(mtl_shader);
+ }
+
+ if (hnd_shader) {
+ hr = magic_fx->SetTechnique(hnd_shader);
+ } else {
+ if (will_bump) {
+ if (mtl->tex_specular && IsSpecMapEnabled()) {
+ if (mtl->tex_bumpmap && do_bump) {
+ if (ps_version >= D3DPS_VERSION(2,0))
+ hr = magic_fx->SetTechnique("BumpSpecMapPix");
+ else
+ hr = magic_fx->SetTechnique("BumpSpecMap");
+ } else if (mtl->tex_emissive && render_state[LIGHTING_PASS] == 0) {
+ if (ps_version >= D3DPS_VERSION(2,0))
+ hr = magic_fx->SetTechnique("EmissiveSpecMapPix");
+ else
+ hr = magic_fx->SetTechnique("EmissiveSpecularTexture");
+ } else {
+ if (ps_version >= D3DPS_VERSION(2,0))
+ hr = magic_fx->SetTechnique("SpecMapPix");
+ else
+ hr = magic_fx->SetTechnique("SpecularTexture");
+ }
+ } else {
+ if (mtl->tex_bumpmap && do_bump) {
+ if (ps_version >= D3DPS_VERSION(2,0))
+ hr = magic_fx->SetTechnique("BumpMapPix");
+ else
+ hr = magic_fx->SetTechnique("BumpMap");
+ } else if (mtl->tex_emissive && render_state[LIGHTING_PASS] == 0) {
+ if (ps_version >= D3DPS_VERSION(2,0))
+ hr = magic_fx->SetTechnique("EmissivePix");
+ else
+ hr = magic_fx->SetTechnique("EmissiveTexture");
+ } else {
+ if (ps_version >= D3DPS_VERSION(2,0))
+ hr = magic_fx->SetTechnique("SimplePix");
+ else
+ hr = magic_fx->SetTechnique("SimpleTexture");
+ }
+ }
+ }
+
+ else if (texcache && mtl->tex_diffuse) {
+ if (mtl->tex_specular && IsSpecMapEnabled()) {
+ if (mtl->tex_emissive && render_state[LIGHTING_PASS] == 0) {
+ hr = magic_fx->SetTechnique("EmissiveSpecularTexture");
+ } else {
+ hr = magic_fx->SetTechnique("SpecularTexture");
+ }
+ }
+
+ else {
+ if (mtl->tex_emissive && render_state[LIGHTING_PASS] == 0) {
+ hr = magic_fx->SetTechnique("EmissiveTexture");
+ } else {
+ hr = magic_fx->SetTechnique("SimpleTexture");
+ }
+ }
+ } else {
+ hr = magic_fx->SetTechnique("SimpleMaterial");
+ }
+ }
+
+ if (environment_cube != 0 && magic_fx->IsParameterUsed("env_cube", hnd_shader)) {
+ D3DXMATRIX env_matrix;
+ D3DXMatrixIdentity(&env_matrix);
+
+ magic_fx->SetMatrix("env_matrix", &env_matrix);
+ magic_fx->SetTexture("env_cube", environment_cube->GetTexture());
+ }
+
+ if (render_state[STENCIL_ENABLE]) {
+ d3ddevice->SetRenderState(D3DRS_STENCILENABLE, TRUE);
+ d3ddevice->SetRenderState(D3DRS_STENCILREF, 0x01);
+ d3ddevice->SetRenderState(D3DRS_STENCILFUNC, D3DCMP_GREATER);
+ } else {
+ d3ddevice->SetRenderState(D3DRS_STENCILENABLE, FALSE);
+ }
+
+ if (render_state[LIGHTING_PASS] > 0) {
+ current_blend_state = 100;
+ SetBlendType(BLEND_ADDITIVE);
+ SetRenderState(Z_WRITE_ENABLE, FALSE);
+ } else {
+ current_blend_state = 100;
+ SetBlendType(mtl->blend);
+ }
+
+ UINT nPasses = 0;
+
+ hr = magic_fx->Begin(&nPasses, 0);
+
+ for (UINT i = 0; i < nPasses; i++) {
+ hr = magic_fx->BeginPass(i);
+
+ hr = d3ddevice->DrawIndexedPrimitive(
+ D3DPT_TRIANGLELIST,
+ 0,
+ first_vert,
+ num_verts,
+ first_index,
+ num_tris);
+
+ hr = magic_fx->EndPass();
+ }
+
+ hr = magic_fx->End();
+ } else {
+ for (int pass = 0; pass < passes; pass++) {
+ SetupPass(pass);
+
+ hr = d3ddevice->DrawIndexedPrimitive(
+ D3DPT_TRIANGLELIST,
+ 0,
+ first_vert,
+ num_verts,
+ first_index,
+ num_tris);
+
+ if (detail) {
+ hr = d3ddevice->SetVertexShader(NULL);
+ hr = d3ddevice->SetFVF(VideoDX9DetailVertex::FVF);
+ } else if (luminous) {
+ hr = d3ddevice->SetVertexShader(NULL);
+ hr = d3ddevice->SetFVF(VideoDX9LuminousVertex::FVF);
+ } else if (surface_has_tangent_data && vertex_declaration) {
+ hr = d3ddevice->SetVertexShader(NULL);
+ hr = d3ddevice->SetVertexDeclaration(vertex_declaration);
+ } else {
+ hr = d3ddevice->SetVertexShader(NULL);
+ hr = d3ddevice->SetFVF(VideoDX9SolidVertex::FVF);
+ }
+ }
+ }
+ }
+
+ if (FAILED(hr)) {
+ static int report = 10;
+ if (report-- > 0)
+ VideoDX9Error("Could not draw solid polys.", hr);
+ } else {
+ stats.nverts += num_verts;
+ stats.npolys += num_tris;
+ result = true;
+ }
+ }
+
+ return result;
+}
+
+bool
+VideoDX9::DrawPolyOutline(Poly* p)
+{
+ if (d3ddevice && p && p->nverts >= 3) {
+ static VideoDX9LineVertex verts[8];
+
+ int nlines = p->nverts;
+ VertexSet* vset = p->vertex_set;
+ WORD index = 0;
+ Color color = Color::Black;
+ HRESULT hr = E_FAIL;
+
+ ZeroMemory(verts, sizeof(verts));
+
+ if (p->material)
+ color = p->material->Kd.ToColor();
+
+ for (int i = 0; i < p->nverts; i++) {
+ index = p->verts[i];
+
+ verts[i].x = vset->loc[index].x;
+ verts[i].y = vset->loc[index].y;
+ verts[i].z = vset->loc[index].z;
+ verts[i].diffuse = color.Value();
+ }
+
+ // last vertex, to close the loop
+ index = p->verts[0];
+ int i = p->nverts;
+
+ verts[i].x = vset->loc[index].x;
+ verts[i].y = vset->loc[index].y;
+ verts[i].z = vset->loc[index].z;
+ verts[i].diffuse = color.Value();
+
+ current_texture = 0;
+
+ hr = d3ddevice->SetVertexShader(NULL);
+ hr = d3ddevice->SetFVF(VideoDX9LineVertex::FVF);
+ hr = d3ddevice->SetTexture(0, 0);
+ hr = d3ddevice->DrawPrimitiveUP(
+ D3DPT_LINESTRIP,
+ nlines,
+ verts,
+ sizeof(VideoDX9LineVertex));
+ return true;
+ }
+
+ return false;
+}
+
+// +--------------------------------------------------------------------+
+
+bool
+VideoDX9::DrawShadow(Solid* s, int nverts, Vec3* shadow_verts, bool visible)
+{
+ bool result = false;
+ HRESULT hr = E_FAIL;
+
+ if (d3ddevice && s && nverts && shadow_verts && IsShadowEnabled()) {
+ Matrix orient = s->Orientation();
+ orient.Transpose();
+
+ D3DMATRIX world_matrix;
+ CreateD3DMatrix(world_matrix, orient, s->Location());
+ d3ddevice->SetTransform(D3DTS_WORLD, &world_matrix);
+ matrixWorld = world_matrix;
+
+ // show shadow outlines:
+ if (visible) {
+ static VideoDX9LineVertex verts[4];
+
+ d3ddevice->SetRenderState(D3DRS_ZENABLE, TRUE);
+ d3ddevice->SetRenderState(D3DRS_ZWRITEENABLE, FALSE);
+ d3ddevice->SetRenderState(D3DRS_ZFUNC, D3DCMP_LESSEQUAL);
+ d3ddevice->SetRenderState(D3DRS_SHADEMODE, D3DSHADE_FLAT);
+ d3ddevice->SetRenderState(D3DRS_LIGHTING, FALSE);
+ d3ddevice->SetVertexShader(NULL);
+ d3ddevice->SetFVF(VideoDX9LineVertex::FVF);
+ d3ddevice->SetTexture(0, 0);
+
+ SetBlendType(BLEND_ALPHA);
+
+ for (int i = 0; i < nverts; i+=3) {
+ DWORD c = 0xa0ffff80;
+
+ verts[0].x = shadow_verts[i+0].x;
+ verts[0].y = shadow_verts[i+0].y;
+ verts[0].z = shadow_verts[i+0].z;
+ verts[0].diffuse = c;
+
+ verts[1].x = shadow_verts[i+1].x;
+ verts[1].y = shadow_verts[i+1].y;
+ verts[1].z = shadow_verts[i+1].z;
+ verts[1].diffuse = c;
+
+ verts[2].x = shadow_verts[i+2].x;
+ verts[2].y = shadow_verts[i+2].y;
+ verts[2].z = shadow_verts[i+2].z;
+ verts[2].diffuse = c;
+
+ verts[3].x = shadow_verts[i+0].x;
+ verts[3].y = shadow_verts[i+0].y;
+ verts[3].z = shadow_verts[i+0].z;
+ verts[3].diffuse = c;
+
+ hr = d3ddevice->DrawPrimitiveUP(
+ D3DPT_LINESTRIP,
+ 3,
+ verts,
+ sizeof(VideoDX9LineVertex));
+ }
+
+ // restore lighting state
+ d3ddevice->SetRenderState(D3DRS_LIGHTING, render_state[LIGHTING_ENABLE]);
+ }
+
+ // render shadows into stencil buffer:
+
+ // Disable z-buffer writes (note: z-testing still occurs), and enable the
+ // stencil-buffer
+ d3ddevice->SetRenderState(D3DRS_ZENABLE, TRUE);
+ d3ddevice->SetRenderState(D3DRS_ZWRITEENABLE, FALSE);
+ d3ddevice->SetRenderState(D3DRS_STENCILENABLE, TRUE);
+
+ // Dont bother with interpolating color
+ d3ddevice->SetRenderState(D3DRS_SHADEMODE, D3DSHADE_FLAT);
+
+ // Set up stencil compare fuction, reference value, and masks.
+ // Stencil test passes if ((ref & mask) cmpfn (stencil & mask)) is true.
+ // Note: since we set up the stencil-test to always pass, the STENCILFAIL
+ // renderstate is really not needed.
+ d3ddevice->SetRenderState(D3DRS_STENCILFUNC, D3DCMP_ALWAYS );
+ d3ddevice->SetRenderState(D3DRS_STENCILZFAIL, D3DSTENCILOP_KEEP );
+ d3ddevice->SetRenderState(D3DRS_STENCILFAIL, D3DSTENCILOP_KEEP );
+
+ // If z-test passes, inc/decrement stencil buffer value
+ d3ddevice->SetRenderState(D3DRS_STENCILREF, 0x1 );
+ d3ddevice->SetRenderState(D3DRS_STENCILMASK, 0xff );
+ d3ddevice->SetRenderState(D3DRS_STENCILWRITEMASK, 0xff );
+ d3ddevice->SetRenderState(D3DRS_STENCILPASS, D3DSTENCILOP_INCR );
+
+ // Make sure that no pixels get drawn to the frame buffer
+ d3ddevice->SetRenderState(D3DRS_ALPHABLENDENABLE, TRUE );
+ d3ddevice->SetRenderState(D3DRS_SRCBLEND, D3DBLEND_ZERO );
+ d3ddevice->SetRenderState(D3DRS_DESTBLEND, D3DBLEND_ONE );
+
+ d3ddevice->SetVertexShader(NULL);
+ d3ddevice->SetFVF(D3DFVF_XYZ);
+
+ // Draw front-side of shadow volume in stencil/z only
+ hr = d3ddevice->DrawPrimitiveUP(D3DPT_TRIANGLELIST, nverts/3, shadow_verts, sizeof(Vec3));
+
+ // Now reverse cull order so back sides of shadow volume are written.
+ d3ddevice->SetRenderState(D3DRS_CULLMODE, D3DCULL_CW );
+
+ // Decrement stencil buffer value
+ d3ddevice->SetRenderState(D3DRS_STENCILPASS, D3DSTENCILOP_DECR );
+
+ // Draw back-side of shadow volume in stencil/z only
+ hr = d3ddevice->DrawPrimitiveUP(D3DPT_TRIANGLELIST, nverts/3, shadow_verts, sizeof(Vec3));
+
+ // restore render states
+ d3ddevice->SetRenderState(D3DRS_SHADEMODE, D3DSHADE_GOURAUD);
+ d3ddevice->SetRenderState(D3DRS_CULLMODE, D3DCULL_CCW);
+ d3ddevice->SetRenderState(D3DRS_ZWRITEENABLE, TRUE);
+ d3ddevice->SetRenderState(D3DRS_STENCILENABLE, FALSE);
+
+ // force restore of current blend type
+ int type = current_blend_state;
+ current_blend_state = 100;
+ SetBlendType(type);
+
+ result = SUCCEEDED(hr);
+ }
+
+ return result;
+}
+
+// +--------------------------------------------------------------------+
+
+bool
+VideoDX9::DrawLines(int nlines, Vec3* points, Color c, int blend)
+{
+ bool result = false;
+
+ if (d3ddevice && points && nlines > 0 && nlines <= 256) {
+ stats.ncalls++;
+
+ VideoDX9LineVertex* verts = line_verts;
+
+ if (verts) {
+ HRESULT hr = E_FAIL;
+
+ for (int i = 0; i < 2*nlines; i++) {
+ VideoDX9LineVertex* v = verts + i;
+ Vec3* p = points + i;
+
+ v->x = p->x;
+ v->y = p->y;
+ v->z = p->z;
+ v->diffuse = c.Value();
+ }
+
+ hr = d3ddevice->SetTransform(D3DTS_WORLD, &identity_matrix);
+ hr = d3ddevice->SetVertexShader(NULL);
+ hr = d3ddevice->SetFVF(VideoDX9LineVertex::FVF);
+
+ DWORD old_lighting = render_state[LIGHTING_ENABLE];
+
+ // untextured lines:
+ if (current_texture) {
+ d3ddevice->SetTexture(0, 0);
+ current_texture = 0;
+ }
+
+ SetRenderState(LIGHTING_ENABLE, FALSE);
+ SetBlendType(blend);
+
+ hr = d3ddevice->DrawPrimitiveUP(
+ D3DPT_LINELIST,
+ nlines,
+ verts,
+ sizeof(VideoDX9LineVertex));
+
+ if (FAILED(hr)) {
+ static int report = 10;
+ if (report-- > 0)
+ VideoDX9Error("Could not draw 3D lines.", hr);
+ }
+
+ SetRenderState(LIGHTING_ENABLE, old_lighting);
+
+ if (SUCCEEDED(hr)) {
+ stats.nverts += 2*nlines;
+ stats.nlines += nlines;
+ result = true;
+ }
+ }
+ }
+
+ return result;
+}
+
+// +--------------------------------------------------------------------+
+
+bool
+VideoDX9::DrawScreenLines(int nlines, float* points, Color c, int blend)
+{
+ bool result = false;
+
+ if (d3ddevice && points && nlines > 0 && nlines <= 64) {
+ stats.ncalls++;
+
+ VideoDX9ScreenVertex* verts = screen_line_verts;
+
+ if (verts) {
+ HRESULT hr = E_FAIL;
+
+ for (int i = 0; i < 2*nlines; i++) {
+ VideoDX9ScreenVertex* v = verts + i;
+
+ v->sx = points[2*i + 0];
+ v->sy = points[2*i + 1];
+ v->sz = 0.0f;
+ v->rhw = 1.0f;
+ v->diffuse = c.Value();
+ v->tu = 0.0f;
+ v->tv = 0.0f;
+ }
+
+ hr = d3ddevice->SetVertexShader(NULL);
+ hr = d3ddevice->SetFVF(VideoDX9ScreenVertex::FVF);
+
+ if (FAILED(hr)) {
+ static int report = 10;
+ if (report-- > 0)
+ VideoDX9Error("Could not set FVF for screen lines.", hr);
+ }
+
+ else {
+ if (current_texture != 0) {
+ hr = d3ddevice->SetTexture(0, 0);
+ current_texture = 0;
+ }
+
+ SetRenderState(FILL_MODE, D3DFILL_SOLID);
+ SetRenderState(Z_ENABLE, D3DZB_FALSE);
+ SetRenderState(LIGHTING_ENABLE, FALSE);
+ SetBlendType(blend);
+
+ hr = d3ddevice->DrawPrimitiveUP(
+ D3DPT_LINELIST,
+ nlines,
+ verts,
+ sizeof(VideoDX9ScreenVertex));
+
+ if (FAILED(hr)) {
+ static int report = 10;
+ if (report-- > 0)
+ VideoDX9Error("Could not draw screen lines.", hr);
+ }
+ }
+
+ if (SUCCEEDED(hr)) {
+ stats.nverts += 2*nlines;
+ stats.nlines += nlines;
+ result = true;
+ }
+ }
+ }
+
+ return result;
+}
+
+// +--------------------------------------------------------------------+
+
+bool
+VideoDX9::DrawPoints(VertexSet* vset)
+{
+ if (vset && vset->nverts) {
+ HRESULT hr = E_FAIL;
+
+ int nverts = vset->nverts;
+ VideoDX9LineVertex* verts = new(__FILE__,__LINE__) VideoDX9LineVertex[nverts];
+
+ if (verts) {
+ for (int i = 0; i < nverts; i++) {
+ VideoDX9LineVertex* v = verts + i;
+ Vec3* p = vset->loc + i;
+
+ v->x = p->x;
+ v->y = p->y;
+ v->z = p->z;
+ v->diffuse = vset->diffuse[i];
+ }
+
+ SetRenderState(LIGHTING_ENABLE, FALSE);
+
+ hr = d3ddevice->SetTransform(D3DTS_WORLD, &identity_matrix);
+ hr = d3ddevice->SetVertexShader(NULL);
+ hr = d3ddevice->SetFVF(VideoDX9LineVertex::FVF);
+ hr = d3ddevice->SetTexture(0, 0);
+ hr = d3ddevice->DrawPrimitiveUP(
+ D3DPT_POINTLIST,
+ nverts,
+ verts,
+ sizeof(VideoDX9LineVertex));
+
+ delete [] verts;
+ return true;
+ }
+ }
+
+ return false;
+}
+
+// +--------------------------------------------------------------------+
+
+bool
+VideoDX9::UseMaterial(Material* m)
+{
+ use_material = m;
+ return true;
+}
+
+// +--------------------------------------------------------------------+
+
+bool
+VideoDX9::UseXFont(const char* name, int size, bool bold, bool ital)
+{
+ if (d3ddevice && name && *name && size > 4) {
+ RELEASE(d3dx_font);
+
+ strcpy_s(font_name, name);
+ font_size = size;
+ font_bold = bold;
+ font_ital = ital;
+
+ HRESULT hr = E_FAIL;
+ HDC hdc = GetDC(NULL);
+ int nLogPixelsY = GetDeviceCaps(hdc, LOGPIXELSY);
+
+ ReleaseDC(NULL, hdc);
+
+ int nHeight = -size * nLogPixelsY / 72;
+
+ hr = D3DXCreateFont(d3ddevice, // D3D device
+ nHeight, // Height
+ 0, // Width
+ bold ? FW_BOLD : FW_NORMAL, // Weight
+ 1, // MipLevels, 0 = autogen mipmaps
+ ital, // Italic
+ DEFAULT_CHARSET, // CharSet
+ OUT_DEFAULT_PRECIS, // OutputPrecision
+ DEFAULT_QUALITY, // Quality
+ DEFAULT_PITCH | FF_DONTCARE, // PitchAndFamily
+ name, // pFaceName
+ &d3dx_font); // ppFont
+
+ if (SUCCEEDED(hr)) {
+ return true;
+ }
+ }
+
+ RELEASE(d3dx_font);
+ return false;
+}
+
+bool
+VideoDX9::DrawText(const char* text, int count, const Rect& rect, DWORD format, Color c)
+{
+ if (d3ddevice && text && *text && d3dx_font) {
+ RECT r;
+ r.left = rect.x;
+ r.top = rect.y;
+ r.right = rect.x + rect.w;
+ r.bottom = rect.y + rect.h;
+
+ d3dx_font->DrawText(0, text, count, &r, format, c.Value());
+ }
+
+ return false;
+}
+
+// +--------------------------------------------------------------------+
+
+bool
+VideoDX9::PrepareSurface(Surface* surf)
+{
+ if (surf) {
+ int nverts = surf->NumVerts();
+ int nindices = surf->NumIndices();
+ bool detail = surf->GetVertexSet()->tu1 != 0;
+ bool luminous = false;
+ DWORD dynamic = 0;
+
+ if (surf->GetModel()) {
+ luminous = surf->GetModel()->IsLuminous();
+ dynamic = surf->GetModel()->IsDynamic() ? D3DUSAGE_DYNAMIC : 0;
+ }
+
+ surface_has_tangent_data = !luminous && (surf->GetVertexSet()->tangent && surf->GetVertexSet()->binormal);
+
+ VideoDX9SurfaceData* surf_data = (VideoDX9SurfaceData*) surf->GetVideoPrivateData();
+
+ if (!surf_data) {
+ surf_data = new(__FILE__,__LINE__) VideoDX9SurfaceData(surf->GetModel());
+
+ surface_has_tangent_data = false;
+
+ if (surf->GetVertexSet()->tangent && surf->GetVertexSet()->binormal) {
+ surface_has_tangent_data = true;
+ surf_data->vertex_buffer = new(__FILE__,__LINE__) VideoDX9VertexBuffer(
+ this,
+ nverts,
+ sizeof(VideoDX9NormalVertex),
+ 0, // not an FVF vertex buffer
+ dynamic | D3DUSAGE_WRITEONLY);
+ }
+
+ else if (detail) {
+ surf_data->vertex_buffer = new(__FILE__,__LINE__) VideoDX9VertexBuffer(
+ this,
+ nverts,
+ sizeof(VideoDX9DetailVertex),
+ VideoDX9DetailVertex::FVF,
+ dynamic | D3DUSAGE_WRITEONLY);
+ }
+
+ else if (luminous) {
+ surf_data->vertex_buffer = new(__FILE__,__LINE__) VideoDX9VertexBuffer(
+ this,
+ nverts,
+ sizeof(VideoDX9LuminousVertex),
+ VideoDX9LuminousVertex::FVF,
+ dynamic | D3DUSAGE_WRITEONLY);
+ }
+
+ else {
+ surf_data->vertex_buffer = new(__FILE__,__LINE__) VideoDX9VertexBuffer(
+ this,
+ nverts,
+ sizeof(VideoDX9SolidVertex),
+ VideoDX9SolidVertex::FVF,
+ dynamic | D3DUSAGE_WRITEONLY);
+ }
+
+ surf_data->index_buffer = new(__FILE__,__LINE__) VideoDX9IndexBuffer(
+ this,
+ nindices,
+ dynamic | D3DUSAGE_WRITEONLY);
+
+ if (!surf_data->vertex_buffer || !surf_data->index_buffer) {
+ Print("VideoDX9: Unable to prepare surface '%s'\n", surf->Name());
+ delete surf_data;
+ return false;
+ }
+
+ surf->SetVideoPrivateData(surf_data);
+ }
+
+ if (surf_data && !surf_data->IsValid()) {
+ if (detail) {
+ VideoDX9DetailVertex* v = (VideoDX9DetailVertex*) surf_data->vertex_buffer->Lock(nverts);
+
+ if (v) {
+ const VertexSet* vset = surf->GetVertexSet();
+ for (int i = 0; i < nverts; i++) {
+ v->x = vset->loc[i].x;
+ v->y = vset->loc[i].y;
+ v->z = vset->loc[i].z;
+
+ v->diffuse = vset->diffuse[i];
+ v->specular = vset->specular[i];
+
+ v->tu = vset->tu[i];
+ v->tv = vset->tv[i];
+ v->tu1 = vset->tu1[i];
+ v->tv1 = vset->tv1[i];
+
+ v++;
+ }
+
+ surf_data->vertex_buffer->Unlock();
+ }
+ }
+
+ else if (luminous) {
+ VideoDX9LuminousVertex* v = (VideoDX9LuminousVertex*) surf_data->vertex_buffer->Lock(nverts);
+
+ if (v) {
+ const VertexSet* vset = surf->GetVertexSet();
+ for (int i = 0; i < nverts; i++) {
+ v->x = vset->loc[i].x;
+ v->y = vset->loc[i].y;
+ v->z = vset->loc[i].z;
+
+ v->diffuse = vset->diffuse[i];
+
+ v->tu = vset->tu[i];
+ v->tv = vset->tv[i];
+
+ v++;
+ }
+
+ surf_data->vertex_buffer->Unlock();
+ }
+ }
+
+ else if (surface_has_tangent_data) {
+ VideoDX9NormalVertex* v = (VideoDX9NormalVertex*) surf_data->vertex_buffer->Lock(nverts);
+
+ if (v) {
+ const VertexSet* vset = surf->GetVertexSet();
+ for (int i = 0; i < nverts; i++) {
+ v->x = vset->loc[i].x;
+ v->y = vset->loc[i].y;
+ v->z = vset->loc[i].z;
+
+ v->nx = vset->nrm[i].x;
+ v->ny = vset->nrm[i].y;
+ v->nz = vset->nrm[i].z;
+
+ v->t0u = vset->tu[i];
+ v->t0v = vset->tv[i];
+ v->t1u = vset->tu[i];
+ v->t1v = vset->tv[i];
+
+ v->tx = vset->tangent[i].x;
+ v->ty = vset->tangent[i].y;
+ v->tz = vset->tangent[i].z;
+
+ v->bx = vset->binormal[i].x;
+ v->by = vset->binormal[i].y;
+ v->bz = vset->binormal[i].z;
+
+ v++;
+ }
+
+ surf_data->vertex_buffer->Unlock();
+ }
+ }
+
+ else {
+ VideoDX9SolidVertex* v = (VideoDX9SolidVertex*) surf_data->vertex_buffer->Lock(nverts);
+
+ if (v) {
+ const VertexSet* vset = surf->GetVertexSet();
+ for (int i = 0; i < nverts; i++) {
+ v->x = vset->loc[i].x;
+ v->y = vset->loc[i].y;
+ v->z = vset->loc[i].z;
+
+ v->nx = vset->nrm[i].x;
+ v->ny = vset->nrm[i].y;
+ v->nz = vset->nrm[i].z;
+
+ v->tu = vset->tu[i];
+ v->tv = vset->tv[i];
+
+ v++;
+ }
+
+ surf_data->vertex_buffer->Unlock();
+ }
+ }
+
+ WORD* indices = surf_data->index_buffer->Lock(nindices);
+
+ if (indices) {
+ // copy the indices into the locked index buffer
+ WORD* s = indices;
+ Poly* p = surf->GetPolys();
+
+ for (int i = 0; i < surf->NumPolys(); i++) {
+ if (p->nverts == 3) {
+ *s++ = p->verts[0];
+ *s++ = p->verts[2];
+ *s++ = p->verts[1];
+ }
+ else if (p->nverts == 4) {
+ *s++ = p->verts[0];
+ *s++ = p->verts[2];
+ *s++ = p->verts[1];
+
+ *s++ = p->verts[0];
+ *s++ = p->verts[3];
+ *s++ = p->verts[2];
+ }
+
+ p++;
+ }
+
+ surf_data->index_buffer->Unlock();
+ }
+
+ surf_data->Validate();
+ }
+
+ int first_index = 0;
+
+ ListIter<Segment> seg_iter = surf->GetSegments();
+ while (++seg_iter) {
+ Segment* segment = seg_iter.value();
+
+ if (!segment->video_data) {
+ VideoDX9SegmentData* seg_data = new(__FILE__,__LINE__) VideoDX9SegmentData;
+
+ int num_tris = 0;
+ for (int i = 0; i < segment->npolys; i++)
+ num_tris += segment->polys[i].nverts-2;
+
+ seg_data->first_vert = 0;
+ seg_data->num_verts = surf->NumVerts();
+ seg_data->first_index = first_index;
+ seg_data->num_tris = num_tris;
+
+ segment->video_data = seg_data;
+
+ first_index += num_tris * 3;
+ }
+ }
+ }
+
+ return true;
+}
+
+// +--------------------------------------------------------------------+
+
+int
+VideoDX9::PrepareMaterial(Material* m)
+{
+ segment_material = m;
+ strategy = 0;
+ passes = 0;
+
+ if (m) {
+ int max_stages = 1;
+ int max_textures = 1;
+ bool multiply_add = false;
+ bool dotproduct3 = false;
+ bool vertexshader = false;
+ bool pixelshader = false;
+ DWORD vs_version = 0;
+ DWORD ps_version = 0;
+
+ VideoDX9DeviceInfo* dev_info = dx9enum->GetDeviceInfo(video_settings.GetDeviceType());
+
+ if (dev_info) {
+ max_stages = (int) dev_info->caps.MaxTextureBlendStages;
+ max_textures = (int) dev_info->caps.MaxSimultaneousTextures;
+ multiply_add = (dev_info->caps.TextureOpCaps & D3DTEXOPCAPS_MULTIPLYADD) ? true : false;
+ dotproduct3 = (dev_info->caps.TextureOpCaps & D3DTEXOPCAPS_DOTPRODUCT3) ? true : false;
+
+ vs_version = video_settings.enable_vs ? dev_info->caps.VertexShaderVersion : 0;
+ ps_version = video_settings.enable_ps ? dev_info->caps.PixelShaderVersion : 0;
+
+ vertexshader = vs_version >= D3DVS_VERSION(1,1);
+ pixelshader = ps_version >= D3DPS_VERSION(2,0);
+ }
+
+ strategy = DX9_STRATEGY_SIMPLE;
+ passes = 1;
+
+ if (m->tex_alternate) {
+ if (m->tex_detail && max_textures > 2 && max_stages > 4)
+ strategy = DX9_STRATEGY_BLEND_DETAIL;
+
+ else if (max_textures > 1 && max_stages > 3)
+ strategy = DX9_STRATEGY_BLEND;
+ }
+
+ else if (m->tex_emissive && (!m->tex_diffuse || m->tex_diffuse == m->tex_emissive)) {
+ strategy = DX9_STRATEGY_GLOW;
+ }
+
+ else if (IsSpecMapEnabled() && m->tex_specular && !m->tex_emissive) {
+ strategy = DX9_STRATEGY_SPECMAP;
+
+ if (max_textures < 2 || max_stages < 2 || !multiply_add)
+ passes = 2;
+ }
+
+ else if ((!IsSpecMapEnabled() || !m->tex_specular) && m->tex_emissive) {
+ strategy = DX9_STRATEGY_EMISSIVE;
+
+ if (max_textures < 2 || max_stages < 2)
+ passes = 2;
+ }
+
+ else if (IsSpecMapEnabled() && m->tex_specular && m->tex_emissive) {
+ strategy = DX9_STRATEGY_SPEC_EMISSIVE;
+
+ if (max_textures < 2 || max_stages < 2)
+ passes = 3;
+
+ else if (max_textures < 3 || max_stages < 3 || !multiply_add)
+ passes = 2;
+ }
+ }
+
+ return passes;
+}
+
+bool
+VideoDX9::SetupPass(int pass)
+{
+ if (pass < 0 || pass >= passes)
+ return false;
+
+ if (pass == 0) {
+ D3DMATERIAL9 d3dmtl;
+ IDirect3DTexture9* texture_0 = 0;
+ IDirect3DTexture9* texture_1 = 0;
+ IDirect3DTexture9* texture_2 = 0;
+ Bitmap* tex_bmp_0 = 0;
+ Bitmap* tex_bmp_1 = 0;
+ Bitmap* tex_bmp_2 = 0;
+ ColorValue orig_spec = segment_material->Ks;
+ HRESULT hr = E_FAIL;
+
+ if (segment_material->tex_specular && passes > 1)
+ segment_material->Ks = Color::Black;
+
+ CreateD3DMaterial(d3dmtl, *segment_material);
+ segment_material->Ks = orig_spec;
+
+ hr = d3ddevice->SetMaterial(&d3dmtl);
+
+ if (strategy == DX9_STRATEGY_SIMPLE) {
+ tex_bmp_0 = segment_material->tex_diffuse;
+ }
+
+ else if (strategy == DX9_STRATEGY_BLEND) {
+ tex_bmp_0 = segment_material->tex_diffuse;
+ tex_bmp_1 = segment_material->tex_alternate;
+ }
+
+ else if (strategy == DX9_STRATEGY_BLEND_DETAIL) {
+ tex_bmp_0 = segment_material->tex_diffuse;
+ tex_bmp_1 = segment_material->tex_alternate;
+ tex_bmp_2 = segment_material->tex_detail;
+ }
+
+ else if (strategy == DX9_STRATEGY_SPECMAP) {
+ if (passes == 1) {
+ tex_bmp_0 = segment_material->tex_diffuse;
+ tex_bmp_1 = segment_material->tex_specular;
+ }
+ else {
+ tex_bmp_0 = segment_material->tex_diffuse;
+ }
+ }
+
+ else if (strategy == DX9_STRATEGY_EMISSIVE && passes == 1 ||
+ strategy == DX9_STRATEGY_SPEC_EMISSIVE && passes == 2) {
+ if (segment_material->tex_diffuse) {
+ tex_bmp_0 = segment_material->tex_diffuse;
+ tex_bmp_1 = segment_material->tex_emissive;
+ }
+ else {
+ tex_bmp_0 = segment_material->tex_emissive;
+ }
+ }
+
+ else {
+ tex_bmp_0 = segment_material->tex_emissive;
+ }
+
+ if (texcache && tex_bmp_0) {
+ texture_0 = texcache->FindTexture(tex_bmp_0);
+
+ hr = d3ddevice->SetTexture(0, texture_0);
+ current_texture = texture_0;
+
+ if (tex_bmp_1) {
+ texture_1 = texcache->FindTexture(tex_bmp_1);
+ hr = d3ddevice->SetTexture(1, texture_1);
+
+ if (tex_bmp_2) {
+ texture_2 = texcache->FindTexture(tex_bmp_2);
+ hr = d3ddevice->SetTexture(2, texture_2);
+ }
+ }
+ }
+ else {
+ hr = d3ddevice->SetTexture(0, 0);
+ current_texture = 0;
+ }
+
+ SetBlendType(segment_material->blend);
+
+ if (texture_0 && texture_1 && strategy == DX9_STRATEGY_BLEND) {
+ d3ddevice->SetSamplerState(1, D3DSAMP_MAGFILTER, D3DTEXF_LINEAR);
+ d3ddevice->SetSamplerState(1, D3DSAMP_MINFILTER, D3DTEXF_LINEAR);
+ d3ddevice->SetSamplerState(1, D3DSAMP_MIPFILTER, D3DTEXF_LINEAR);
+
+ d3ddevice->SetTextureStageState(0, D3DTSS_COLOROP, D3DTOP_SELECTARG1);
+ d3ddevice->SetTextureStageState(0, D3DTSS_COLORARG1, D3DTA_TEXTURE);
+ d3ddevice->SetTextureStageState(0, D3DTSS_ALPHAOP, D3DTOP_SELECTARG1);
+ d3ddevice->SetTextureStageState(0, D3DTSS_ALPHAARG1, D3DTA_DIFFUSE);
+
+ d3ddevice->SetTextureStageState(1, D3DTSS_COLOROP, D3DTOP_BLENDCURRENTALPHA);
+ d3ddevice->SetTextureStageState(1, D3DTSS_COLORARG1, D3DTA_CURRENT);
+ d3ddevice->SetTextureStageState(1, D3DTSS_COLORARG2, D3DTA_TEXTURE);
+ d3ddevice->SetTextureStageState(1, D3DTSS_ALPHAOP, D3DTOP_SELECTARG1);
+ d3ddevice->SetTextureStageState(1, D3DTSS_ALPHAARG1, D3DTA_CURRENT);
+ d3ddevice->SetTextureStageState(1, D3DTSS_TEXCOORDINDEX, 0);
+
+ d3ddevice->SetTextureStageState(2, D3DTSS_COLOROP, D3DTOP_MODULATE);
+ d3ddevice->SetTextureStageState(2, D3DTSS_COLORARG1, D3DTA_CURRENT);
+ d3ddevice->SetTextureStageState(2, D3DTSS_COLORARG2, D3DTA_DIFFUSE);
+ d3ddevice->SetTextureStageState(2, D3DTSS_ALPHAOP, D3DTOP_SELECTARG1);
+ d3ddevice->SetTextureStageState(2, D3DTSS_ALPHAARG1, D3DTA_CURRENT);
+
+ d3ddevice->SetTextureStageState(3, D3DTSS_COLOROP, D3DTOP_DISABLE);
+ }
+
+ else if (texture_0 && texture_1 && texture_2 && strategy == DX9_STRATEGY_BLEND_DETAIL) {
+ d3ddevice->SetSamplerState(1, D3DSAMP_MAGFILTER, D3DTEXF_LINEAR);
+ d3ddevice->SetSamplerState(1, D3DSAMP_MINFILTER, D3DTEXF_LINEAR);
+ d3ddevice->SetSamplerState(1, D3DSAMP_MIPFILTER, D3DTEXF_LINEAR);
+ d3ddevice->SetSamplerState(2, D3DSAMP_MAGFILTER, D3DTEXF_LINEAR);
+ d3ddevice->SetSamplerState(2, D3DSAMP_MINFILTER, D3DTEXF_LINEAR);
+ d3ddevice->SetSamplerState(2, D3DSAMP_MIPFILTER, D3DTEXF_LINEAR);
+
+ d3ddevice->SetTextureStageState(0, D3DTSS_COLOROP, D3DTOP_SELECTARG1);
+ d3ddevice->SetTextureStageState(0, D3DTSS_COLORARG1, D3DTA_TEXTURE);
+ d3ddevice->SetTextureStageState(0, D3DTSS_ALPHAOP, D3DTOP_SELECTARG1);
+ d3ddevice->SetTextureStageState(0, D3DTSS_ALPHAARG1, D3DTA_DIFFUSE);
+ d3ddevice->SetTextureStageState(0, D3DTSS_TEXCOORDINDEX, 0);
+
+ d3ddevice->SetTextureStageState(1, D3DTSS_COLOROP, D3DTOP_BLENDCURRENTALPHA);
+ d3ddevice->SetTextureStageState(1, D3DTSS_COLORARG1, D3DTA_CURRENT);
+ d3ddevice->SetTextureStageState(1, D3DTSS_COLORARG2, D3DTA_TEXTURE);
+ d3ddevice->SetTextureStageState(1, D3DTSS_ALPHAOP, D3DTOP_SELECTARG1);
+ d3ddevice->SetTextureStageState(1, D3DTSS_ALPHAARG1, D3DTA_CURRENT);
+ d3ddevice->SetTextureStageState(1, D3DTSS_TEXCOORDINDEX, 0);
+
+ d3ddevice->SetTextureStageState(2, D3DTSS_COLOROP, D3DTOP_BLENDTEXTUREALPHA);
+ d3ddevice->SetTextureStageState(2, D3DTSS_COLORARG1, D3DTA_TEXTURE);
+ d3ddevice->SetTextureStageState(2, D3DTSS_COLORARG2, D3DTA_CURRENT);
+ d3ddevice->SetTextureStageState(2, D3DTSS_ALPHAOP, D3DTOP_SELECTARG1);
+ d3ddevice->SetTextureStageState(2, D3DTSS_ALPHAARG1, D3DTA_CURRENT);
+ d3ddevice->SetTextureStageState(2, D3DTSS_TEXCOORDINDEX, 1);
+
+ d3ddevice->SetTextureStageState(3, D3DTSS_COLOROP, D3DTOP_MODULATE);
+ d3ddevice->SetTextureStageState(3, D3DTSS_COLORARG1, D3DTA_CURRENT);
+ d3ddevice->SetTextureStageState(3, D3DTSS_COLORARG2, D3DTA_DIFFUSE);
+ d3ddevice->SetTextureStageState(3, D3DTSS_ALPHAOP, D3DTOP_SELECTARG1);
+ d3ddevice->SetTextureStageState(3, D3DTSS_ALPHAARG1, D3DTA_CURRENT);
+ }
+
+ else if (texture_0 && strategy == DX9_STRATEGY_GLOW) {
+ d3ddevice->SetTextureStageState(0, D3DTSS_COLOROP, D3DTOP_SELECTARG1);
+ d3ddevice->SetTextureStageState(0, D3DTSS_COLORARG1, D3DTA_TEXTURE);
+ d3ddevice->SetTextureStageState(0, D3DTSS_COLORARG2, D3DTA_CURRENT);
+ d3ddevice->SetTextureStageState(0, D3DTSS_ALPHAOP, D3DTOP_SELECTARG1);
+ d3ddevice->SetTextureStageState(0, D3DTSS_ALPHAARG1, D3DTA_TEXTURE);
+ }
+
+ else if (texture_0) {
+ d3ddevice->SetTextureStageState(0, D3DTSS_COLOROP, D3DTOP_MODULATE);
+ d3ddevice->SetTextureStageState(0, D3DTSS_COLORARG1, D3DTA_TEXTURE);
+ d3ddevice->SetTextureStageState(0, D3DTSS_COLORARG2, D3DTA_CURRENT);
+ d3ddevice->SetTextureStageState(0, D3DTSS_ALPHAOP, D3DTOP_SELECTARG1);
+ d3ddevice->SetTextureStageState(0, D3DTSS_ALPHAARG1, D3DTA_TEXTURE);
+ }
+
+ if (texture_1 && strategy == DX9_STRATEGY_SPECMAP) {
+ d3ddevice->SetSamplerState(1, D3DSAMP_MAGFILTER, D3DTEXF_LINEAR);
+ d3ddevice->SetSamplerState(1, D3DSAMP_MINFILTER, D3DTEXF_LINEAR);
+ d3ddevice->SetSamplerState(1, D3DSAMP_MIPFILTER, D3DTEXF_LINEAR);
+
+ d3ddevice->SetTextureStageState(1, D3DTSS_TEXCOORDINDEX, 0);
+ d3ddevice->SetTextureStageState(1, D3DTSS_COLOROP, D3DTOP_MULTIPLYADD);
+ d3ddevice->SetTextureStageState(1, D3DTSS_COLORARG1, D3DTA_TEXTURE);
+ d3ddevice->SetTextureStageState(1, D3DTSS_COLORARG2, D3DTA_SPECULAR);
+ d3ddevice->SetTextureStageState(1, D3DTSS_ALPHAOP, D3DTOP_SELECTARG1);
+ d3ddevice->SetTextureStageState(1, D3DTSS_ALPHAARG1, D3DTA_TEXTURE);
+ }
+
+ else if (texture_1 && strategy == DX9_STRATEGY_EMISSIVE) {
+ d3ddevice->SetSamplerState(1, D3DSAMP_MAGFILTER, D3DTEXF_LINEAR);
+ d3ddevice->SetSamplerState(1, D3DSAMP_MINFILTER, D3DTEXF_LINEAR);
+ d3ddevice->SetSamplerState(1, D3DSAMP_MIPFILTER, D3DTEXF_LINEAR);
+
+ d3ddevice->SetTextureStageState(1, D3DTSS_TEXCOORDINDEX, 0);
+ d3ddevice->SetTextureStageState(1, D3DTSS_COLOROP, D3DTOP_ADD);
+ d3ddevice->SetTextureStageState(1, D3DTSS_COLORARG1, D3DTA_TEXTURE);
+ d3ddevice->SetTextureStageState(1, D3DTSS_COLORARG2, D3DTA_CURRENT);
+ d3ddevice->SetTextureStageState(1, D3DTSS_ALPHAOP, D3DTOP_SELECTARG1);
+ d3ddevice->SetTextureStageState(1, D3DTSS_ALPHAARG1, D3DTA_TEXTURE);
+ }
+
+ else if (texture_1 && strategy == DX9_STRATEGY_SPEC_EMISSIVE) {
+ d3ddevice->SetSamplerState(1, D3DSAMP_MAGFILTER, D3DTEXF_LINEAR);
+ d3ddevice->SetSamplerState(1, D3DSAMP_MINFILTER, D3DTEXF_LINEAR);
+ d3ddevice->SetSamplerState(1, D3DSAMP_MIPFILTER, D3DTEXF_LINEAR);
+
+ d3ddevice->SetTextureStageState(1, D3DTSS_TEXCOORDINDEX, 0);
+ d3ddevice->SetTextureStageState(1, D3DTSS_COLOROP, D3DTOP_ADD);
+ d3ddevice->SetTextureStageState(1, D3DTSS_COLORARG1, D3DTA_TEXTURE);
+ d3ddevice->SetTextureStageState(1, D3DTSS_COLORARG2, D3DTA_CURRENT);
+ d3ddevice->SetTextureStageState(1, D3DTSS_ALPHAOP, D3DTOP_SELECTARG1);
+ d3ddevice->SetTextureStageState(1, D3DTSS_ALPHAARG1, D3DTA_TEXTURE);
+ }
+
+ else if (strategy < DX9_STRATEGY_BLEND) {
+ d3ddevice->SetTextureStageState(1, D3DTSS_COLOROP, D3DTOP_DISABLE);
+ }
+ }
+
+ else if (pass == 1) {
+ D3DMATERIAL9 d3dmtl;
+ IDirect3DTexture9* texture = 0;
+ Bitmap* tex_bmp = 0;
+ ColorValue orig_Ka = segment_material->Ka;
+ ColorValue orig_Kd = segment_material->Kd;
+ HRESULT hr = E_FAIL;
+
+ if (segment_material->tex_specular) {
+ segment_material->Ka = Color::Black;
+ segment_material->Kd = Color::Black;
+ }
+
+ CreateD3DMaterial(d3dmtl, *segment_material);
+
+ segment_material->Ka = orig_Ka;
+ segment_material->Kd = orig_Kd;
+
+ hr = d3ddevice->SetMaterial(&d3dmtl);
+
+ if (strategy == DX9_STRATEGY_SPECMAP ||
+ strategy == DX9_STRATEGY_SPEC_EMISSIVE) {
+ tex_bmp = segment_material->tex_specular;
+ d3ddevice->SetTextureStageState(0, D3DTSS_COLOROP, D3DTOP_MODULATE);
+ d3ddevice->SetTextureStageState(0, D3DTSS_COLORARG1, D3DTA_TEXTURE);
+ d3ddevice->SetTextureStageState(0, D3DTSS_COLORARG2, D3DTA_SPECULAR);
+ }
+
+ else if (strategy == DX9_STRATEGY_EMISSIVE) {
+ tex_bmp = segment_material->tex_emissive;
+ d3ddevice->SetTextureStageState(0, D3DTSS_COLOROP, D3DTOP_SELECTARG1);
+ d3ddevice->SetTextureStageState(0, D3DTSS_COLORARG1, D3DTA_TEXTURE);
+ }
+
+ d3ddevice->SetTextureStageState(1, D3DTSS_COLOROP, D3DTOP_DISABLE);
+
+ if (texcache && tex_bmp) {
+ texture = texcache->FindTexture(tex_bmp);
+ hr = d3ddevice->SetTexture(0, texture);
+ current_texture = texture;
+
+ SetBlendType(BLEND_ADDITIVE);
+ }
+ }
+
+ if (render_state[STENCIL_ENABLE]) {
+ d3ddevice->SetRenderState(D3DRS_STENCILENABLE, TRUE);
+ d3ddevice->SetRenderState(D3DRS_STENCILREF, 0x01);
+ d3ddevice->SetRenderState(D3DRS_STENCILFUNC, D3DCMP_GREATER);
+ }
+ else {
+ d3ddevice->SetRenderState(D3DRS_STENCILENABLE, FALSE);
+ }
+
+ if (render_state[LIGHTING_PASS] > 0) {
+ SetBlendType(BLEND_ADDITIVE);
+ SetRenderState(Z_WRITE_ENABLE, FALSE);
+ }
+
+ return true;
+}
+
+
+// +--------------------------------------------------------------------+
+
+void
+VideoDX9Error(const char* msg, HRESULT err)
+{
+ Print(" VideoDX9: %s. [%s]\n", msg, D3DErrStr(err));
+}
+
+char* D3DErrStr(HRESULT hr)
+{
+ static char errstrbuf[128];
+
+ switch (hr) {
+ default:
+ sprintf_s(errstrbuf, "Unrecognized error value = %08x.", hr);
+ return errstrbuf;
+
+ case D3D_OK:
+ return "No error.";
+
+ case D3DERR_WRONGTEXTUREFORMAT:
+ return "Wrong texture format.";
+
+ case D3DERR_UNSUPPORTEDCOLOROPERATION:
+ return "Unsupported color operation.";
+
+ case D3DERR_UNSUPPORTEDCOLORARG:
+ return "Unsupported color argument.";
+
+ case D3DERR_UNSUPPORTEDALPHAOPERATION:
+ return "Unsupported alpha operation.";
+
+ case D3DERR_UNSUPPORTEDALPHAARG:
+ return "Unsupported alpha argument.";
+
+ case D3DERR_TOOMANYOPERATIONS:
+ return "Too many operations.";
+
+ case D3DERR_CONFLICTINGTEXTUREFILTER:
+ return "Conflicting texture filter.";
+
+ case D3DERR_UNSUPPORTEDFACTORVALUE:
+ return "Unsupported factor value.";
+
+ case D3DERR_CONFLICTINGRENDERSTATE:
+ return "Conflicting render state.";
+
+ case D3DERR_UNSUPPORTEDTEXTUREFILTER:
+ return "Unsupported texture filter.";
+
+ case D3DERR_CONFLICTINGTEXTUREPALETTE:
+ return "Conflicting texture palette.";
+
+ case D3DERR_DRIVERINTERNALERROR:
+ return "Driver internal error.";
+
+
+ case D3DERR_NOTFOUND:
+ return "Resource was not found.";
+
+ case D3DERR_MOREDATA:
+ return "More data?";
+
+ case D3DERR_DEVICELOST:
+ return "Device lost.";
+
+ case D3DERR_DEVICENOTRESET:
+ return "Device is not reset.";
+
+ case D3DERR_NOTAVAILABLE:
+ return "Not available.";
+
+ case D3DERR_OUTOFVIDEOMEMORY:
+ return "Out of video memory.";
+
+ case E_OUTOFMEMORY:
+ return "Out of system memory.";
+
+ case D3DERR_INVALIDDEVICE:
+ return "Invalid device selection.";
+
+ case D3DERR_INVALIDCALL:
+ return "Invalid call or parameter.";
+
+ case D3DERR_DRIVERINVALIDCALL:
+ return "Driver invalid call.";
+
+ case D3DERR_WASSTILLDRAWING:
+ return "The device was still drawing.";
+
+ case D3DOK_NOAUTOGEN:
+ return "Autogeneration is not supported by this device.";
+
+ }
+}
+