diff options
Diffstat (limited to 'Magic2/VideoDX9.cpp')
-rw-r--r-- | Magic2/VideoDX9.cpp | 3641 |
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."; + + } +} + |