Starshatter_Open
Open source Starshatter engine
 All Classes Files Functions Variables Typedefs Enumerations Enumerator Friends Macros
VideoDX9.cpp
Go to the documentation of this file.
1 /* Project nGenEx
2  Destroyer Studios LLC
3  Copyright © 1997-2004. All Rights Reserved.
4 
5  SUBSYSTEM: nGenEx.lib
6  FILE: VideoDX9.cpp
7  AUTHOR: John DiCamillo
8 
9 
10  OVERVIEW
11  ========
12  Direct3D Video class for DirectX 9
13 */
14 
15 #include "MemDebug.h"
16 #include "VideoDX9.h"
17 #include "VideoDX9Enum.h"
18 #include "VideoDX9VertexBuffer.h"
19 #include "TexDX9.h"
20 #include "TexCubeDX9.h"
21 #include "Camera.h"
22 #include "Color.h"
23 #include "DataLoader.h"
24 #include "Polygon.h"
25 #include "Light.h"
26 #include "Solid.h"
27 
28 // +--------------------------------------------------------------------+
29 
30 void Print(const char* msg, ...);
31 char* D3DErrStr(HRESULT dderr);
32 void VideoDX9Error(const char* msg, HRESULT dderr);
33 static TexCacheDX9* texcache = 0;
34 static TexCubeDX9* environment_cube = 0;
35 static bool surface_has_tangent_data = false;
36 static Light* main_light;
37 static Light* back_light;
38 static D3DXMATRIX matrixWorld;
39 static D3DXMATRIX matrixView;
40 static D3DXMATRIX matrixProj;
41 static D3DXMATRIX matrixWorldInverse;
42 
43 extern int VD3D_describe_things;
44 
45 #ifndef RELEASE
46 #define RELEASE(x) if (x) { x->Release(); x=NULL; }
47 #endif
48 
49 #ifndef F2DW
50 #define F2DW(x) (*(DWORD*)(&x))
51 #endif
52 
53 #ifndef DW2I
54 #define DW2I(x) (*(int*)(&x))
55 #endif
56 
57 // +--------------------------------------------------------------------+
58 
59 typedef HRESULT (WINAPI * LPDDCE)(GUID FAR *, LPVOID *, REFIID , IUnknown FAR *);
60 
61 static D3DMATRIX identity_matrix = {
62  FLOAT(1.0), FLOAT(0.0), FLOAT(0.0), FLOAT(0.0),
63  FLOAT(0.0), FLOAT(1.0), FLOAT(0.0), FLOAT(0.0),
64  FLOAT(0.0), FLOAT(0.0), FLOAT(1.0), FLOAT(0.0),
65  FLOAT(0.0), FLOAT(0.0), FLOAT(0.0), FLOAT(1.0)
66 };
67 
68 // +--------------------------------------------------------------------+
69 
71 
73 {
74 public:
76  if (!model_clients.contains(model))
77  model_clients.append(model);
78  }
79 
80  virtual ~VideoDX9SurfaceData() {
81  model_clients.remove(model);
82 
83  delete vertex_buffer;
84  delete index_buffer;
85  }
86 
87  enum { TYPE = 9001 };
88  virtual int GetType() const { return TYPE; }
89 
93 };
94 
96 {
97 public:
99  virtual ~VideoDX9SegmentData() { }
100 
101  enum { TYPE = 9002 };
102  virtual int GetType() const { return TYPE; }
103 
107  int num_tris;
108 };
109 
110 // +--------------------------------------------------------------------+
111 
112 static int d3dstate_table[] = {
113  D3DRS_FILLMODE, // FILL_MODE
114  D3DRS_SHADEMODE, // SHADE_MODE
115  D3DRS_LIGHTING, // LIGHTING_ENABLE
116  D3DRS_ZENABLE, // Z_ENABLE
117  D3DRS_ZWRITEENABLE, // Z_WRITE_ENABLE
118  D3DRS_DEPTHBIAS, // Z_BIAS
119  0, // TEXTURE_FILTER
120  D3DRS_DITHERENABLE, // DITHER_ENABLE
121  D3DRS_SPECULARENABLE, // SPECULAR_ENABLE
122  D3DRS_FOGENABLE, // FOG_ENABLE
123  D3DRS_FOGCOLOR, // FOG_COLOR
124  D3DRS_FOGDENSITY, // FOG_DENSITY
125  D3DRS_STENCILENABLE, // STENCIL_ENABLE
126  0x11111111, // TEXTURE_WRAP (special case)
127  0 // LIGHTING_PASS
128 };
129 
130 static const int NUM_SCREEN_VERTS = 1024;
131 static const int NUM_SCREEN_INDICES = NUM_SCREEN_VERTS * 2;
132 
133 // +--------------------------------------------------------------------+
134 
136 {
137  FLOAT sx, sy, sz, rhw;
138  DWORD diffuse;
139  FLOAT tu, tv;
140 
141  static DWORD FVF;
142 };
143 
144 DWORD VideoDX9ScreenVertex::FVF = D3DFVF_XYZRHW |
145 D3DFVF_DIFFUSE |
146 D3DFVF_TEX1;
147 
149 {
150  FLOAT x, y, z;
151  FLOAT nx, ny, nz;
152  FLOAT t0u, t0v;
153  FLOAT t1u, t1v;
154  FLOAT tx, ty, tz;
155  FLOAT bx, by, bz;
156 
157  static DWORD FVF;
158 };
159 
160 DWORD VideoDX9NormalVertex::FVF = 0;
161 
162 // Global Vertex Declaration shared by shaders
163 D3DVERTEXELEMENT9 videoDX9NormalVertexElements[] =
164 {
165  { 0, 0, D3DDECLTYPE_FLOAT3, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_POSITION, 0 },
166  { 0, 12, D3DDECLTYPE_FLOAT3, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_NORMAL, 0 },
167  { 0, 24, D3DDECLTYPE_FLOAT2, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_TEXCOORD, 0 },
168  { 0, 32, D3DDECLTYPE_FLOAT2, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_TEXCOORD, 1 },
169  { 0, 40, D3DDECLTYPE_FLOAT3, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_TANGENT, 2 },
170  { 0, 52, D3DDECLTYPE_FLOAT3, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_BINORMAL, 3 },
171  D3DDECL_END()
172 };
173 
175 {
176  FLOAT x, y, z;
177  FLOAT nx, ny, nz;
178  FLOAT tu, tv;
179 
180  static DWORD FVF;
181 };
182 
183 DWORD VideoDX9SolidVertex::FVF = D3DFVF_XYZ |
184 D3DFVF_NORMAL |
185 D3DFVF_TEX1 |
186 D3DFVF_TEXCOORDSIZE2(0);
187 
189 {
190  FLOAT x, y, z;
191  DWORD diffuse;
192  FLOAT tu, tv;
193 
194  static DWORD FVF;
195 };
196 
197 DWORD VideoDX9LuminousVertex::FVF = D3DFVF_XYZ |
198 D3DFVF_DIFFUSE |
199 D3DFVF_TEX1 |
200 D3DFVF_TEXCOORDSIZE2(0);
201 
203 {
204  FLOAT x, y, z;
205  DWORD diffuse;
206  DWORD specular;
207  FLOAT tu, tv;
208  FLOAT tu1, tv1;
209 
210  static DWORD FVF;
211 };
212 
213 DWORD VideoDX9DetailVertex::FVF = D3DFVF_XYZ |
214 D3DFVF_DIFFUSE |
215 D3DFVF_SPECULAR |
216 D3DFVF_TEX2;
217 
219 {
220  FLOAT x, y, z;
221  DWORD diffuse;
222 
223  static DWORD FVF;
224 };
225 
226 DWORD VideoDX9LineVertex::FVF = D3DFVF_XYZ |
227 D3DFVF_DIFFUSE;
228 
229 enum {
238 };
239 
240 // +--------------------------------------------------------------------+
241 
242 static VideoDX9* video_dx9_instance = 0;
243 
244 VideoDX9::VideoDX9(const HWND& window, VideoSettings* vs)
245 : width(0), height(0), bpp(0), hwnd(window), surface(0),
246 d3d(0), d3ddevice(0), device_lost(false), fade(0),
247 zdepth(0), gamma(128), num_verts(0), first_vert(0),
248 current_texture(0), screen_vbuf(0), screen_ibuf(0),
249 font_verts(0), font_indices(0), font_nverts(0),
250 nlights(0), use_material(0), d3dx_font(0),
251 segment_material(0), strategy(0), passes(0),
252 screen_line_verts(0), line_verts(0),
253 vertex_declaration(0),
254 magic_fx(0), magic_fx_code(0), magic_fx_code_len(0)
255 {
256  video_dx9_instance = this;
257 
258  Print("\n********************************\n");
259  Print("* Direct 3D version 9 *\n");
260  Print("********************************\n\n");
261 
262  status = VIDEO_ERR;
263  HRESULT err = E_OUTOFMEMORY;
264 
265  d3d = Direct3DCreate9(D3D_SDK_VERSION);
266  dx9enum = new(__FILE__,__LINE__) VideoDX9Enum(d3d);
267 
268  if (d3d && dx9enum) {
269  if (vs) {
270  dx9enum->req_fullscreen = vs->is_windowed ? false : true;
271  dx9enum->req_windowed = vs->is_windowed ? true : false;
272  dx9enum->min_stencil_bits = vs->shadows ? 8 : 0;
273  dx9enum->uses_depth_buffer = true;
274  }
275  else {
276  dx9enum->req_fullscreen = video_settings.is_windowed ? false : true;
277  dx9enum->req_windowed = video_settings.is_windowed ? true : false;
278  dx9enum->min_stencil_bits = video_settings.shadows ? 8 : 0;
279  dx9enum->uses_depth_buffer = true;
280  }
281 
282  err = dx9enum->Enumerate();
283 
284  if (FAILED(err)) {
285  VideoDX9Error("(ctor) could not enumerate dx9 properties", err);
286  delete dx9enum;
287  return;
288  }
289  }
290  else {
291  VideoDX9Error("(ctor) could not create enumerator", err);
292  return;
293  }
294 
295  SetVideoSettings(vs);
296 
297  if (video_settings.is_windowed)
298  dx9enum->SuggestWindowSettings(&video_settings);
299  else
300  dx9enum->SuggestFullscreenSettings(&video_settings);
301 
302  SetupParams();
303 
304  if (VD3D_describe_things > 2) {
305  Print("\nD3DPRESENT_PARAMETERS:\n");
306  Print(" BackBufferWidth: %d\n", d3dparams.BackBufferWidth);
307  Print(" BackBufferHeight: %d\n", d3dparams.BackBufferHeight);
308  Print(" BackBufferCount: %d\n", d3dparams.BackBufferCount);
309  Print(" BackBufferFormat: %s\n", VideoDX9DisplayMode::D3DFormatToString(d3dparams.BackBufferFormat));
310  Print(" Multisample Type: %d\n", d3dparams.MultiSampleType);
311  Print(" Multisample Qual: %d\n", d3dparams.MultiSampleQuality);
312  Print(" Swap Effect: %d\n", d3dparams.SwapEffect);
313  Print(" Device Window: %08X\n", d3dparams.hDeviceWindow);
314  Print(" Windowed: %s\n", d3dparams.Windowed ? "true" : "false");
315  Print(" Enable Depth/Stencil: %s\n", d3dparams.EnableAutoDepthStencil ? "true" : "false");
316  Print(" Depth/Stencil Format: %s\n", VideoDX9DisplayMode::D3DFormatToString(d3dparams.AutoDepthStencilFormat));
317  Print(" Flags: %08X\n", d3dparams.Flags);
318  Print(" Fullscreen Refresh: %d Hz\n", d3dparams.FullScreen_RefreshRateInHz);
319 
320  switch (d3dparams.PresentationInterval) {
321  case D3DPRESENT_INTERVAL_IMMEDIATE:
322  Print(" Present Interval: IMMEDIATE\n");
323  break;
324 
325  case D3DPRESENT_INTERVAL_DEFAULT:
326  Print(" Present Interval: DEFAULT\n");
327  break;
328 
329  case D3DPRESENT_INTERVAL_ONE:
330  Print(" Present Interval: ONE\n");
331  break;
332 
333  case D3DPRESENT_INTERVAL_TWO:
334  Print(" Present Interval: TWO\n");
335  break;
336 
337  case D3DPRESENT_INTERVAL_THREE:
338  Print(" Present Interval: THREE\n");
339  break;
340 
341  case D3DPRESENT_INTERVAL_FOUR:
342  Print(" Present Interval: FOUR\n");
343  break;
344 
345  default:
346  Print(" Present Interval: Unknown (%d)\n", d3dparams.PresentationInterval);
347  break;
348  }
349 
350  Print("\n");
351  }
352 
353  Print(" Creating Video Device for HWND = %08x\n", window);
354 
355  err = d3d->CreateDevice(D3DADAPTER_DEFAULT,
356  D3DDEVTYPE_HAL,
357  window,
358  D3DCREATE_HARDWARE_VERTEXPROCESSING,
359  &d3dparams,
360  &d3ddevice);
361 
362  if (FAILED(err)) {
363  VideoDX9Error("(ctor) could not create device", err);
364  return;
365  }
366 
367  width = video_settings.GetWidth();
368  height = video_settings.GetHeight();
369  bpp = video_settings.GetDepth();
370 
371  shadow_enabled = vs->shadows;
372  bump_enabled = vs->bumpmaps;
373  spec_enabled = vs->specmaps;
374 
375  render_state[FILL_MODE] = FILL_SOLID;
376  render_state[SHADE_MODE] = SHADE_GOURAUD;
377  render_state[Z_ENABLE] = false;
378  render_state[Z_WRITE_ENABLE] = false;
379  render_state[Z_BIAS] = 0;
380  render_state[TEXTURE_FILTER] = FILTER_LINEAR;
381  render_state[DITHER_ENABLE] = false;
382  render_state[SPECULAR_ENABLE] = true;
383  render_state[FOG_ENABLE] = false;
384  render_state[FOG_COLOR] = 0;
385  render_state[FOG_DENSITY] = 0;
386  render_state[STENCIL_ENABLE] = false;
387  render_state[TEXTURE_WRAP] = true;
388  render_state[LIGHTING_PASS] = 0;
389 
390  ZeroMemory(&rect, sizeof(rect));
391 
392  if (!texcache)
393  texcache = new(__FILE__,__LINE__) TexCacheDX9(this);
394 
395  if (texcache)
396  texcache->count++;
397 
398  if (VD3D_describe_things > 0) {
399  DWORD vmf = VidMemFree() / (1024 * 1024);
400  Print(" Available Texture Memory: %d MB\n\n", vmf);
401  }
402 
403  if (CreateBuffers()) {
404  d3ddevice->SetRenderState(D3DRS_ALPHATESTENABLE, false);
405  d3ddevice->SetRenderState(D3DRS_ZFUNC, D3DCMP_LESSEQUAL);
406 
407  d3ddevice->SetTextureStageState(0, D3DTSS_ALPHAOP, D3DTOP_MODULATE);
408  d3ddevice->SetTextureStageState(0, D3DTSS_ALPHAARG1, D3DTA_TEXTURE);
409  d3ddevice->SetTextureStageState(0, D3DTSS_ALPHAARG2, D3DTA_DIFFUSE);
410  d3ddevice->SetSamplerState(0, D3DSAMP_MAGFILTER, D3DTEXF_LINEAR);
411  d3ddevice->SetSamplerState(0, D3DSAMP_MINFILTER, D3DTEXF_LINEAR);
412  d3ddevice->SetSamplerState(0, D3DSAMP_MIPFILTER, D3DTEXF_NONE);
413 
414  status = VIDEO_OK;
415  }
416 
417  ZeroMemory(font_name, 64);
418  font_size = 0;
419  font_bold = false;
420  font_ital = false;
421 }
422 
423 // +--------------------------------------------------------------------+
424 
426 {
427  DestroyBuffers();
428 
429  texcache->count--;
430  if (!texcache->count) {
431  delete texcache;
432  texcache = 0;
433  }
434 
435  delete environment_cube;
436  delete dx9enum;
437 
438  RELEASE(d3dx_font);
439  RELEASE(d3ddevice);
440  RELEASE(d3d);
441 
442  if (magic_fx_code)
443  delete [] magic_fx_code;
444 
445  Print(" VideoDX9: shutdown\n");
446  video_dx9_instance = 0;
447 }
448 
449 IDirect3DDevice9*
451 {
452  if (video_dx9_instance)
453  return video_dx9_instance->d3ddevice;
454 
455  return 0;
456 }
457 
458 // +--------------------------------------------------------------------+
459 
460 bool
462 {
463  if (!dx9enum || dx9enum->NumAdapters() < 1) {
464  status = VIDEO_ERR;
465  return false;
466  }
467 
468  int adapter_index = video_settings.GetAdapterIndex();
469 
470  if (adapter_index < 0 || adapter_index >= dx9enum->NumAdapters()) {
471  ::Print("WARNING: VideoDX9 could not select adapter %d (max=%d)\n",
472  adapter_index, dx9enum->NumAdapters());
473 
474  adapter_index = 0;
475  }
476 
477  dx9enum->SelectAdapter(adapter_index);
478 
479  d3dparams.Windowed = video_settings.IsWindowed();
480  d3dparams.BackBufferCount = 2;
481  d3dparams.MultiSampleType = D3DMULTISAMPLE_NONE;
482  d3dparams.MultiSampleQuality = 0;
483  d3dparams.SwapEffect = D3DSWAPEFFECT_DISCARD;
484  d3dparams.EnableAutoDepthStencil = dx9enum->uses_depth_buffer;
485  d3dparams.hDeviceWindow = hwnd;
486 
487  if (dx9enum->uses_depth_buffer) {
488  d3dparams.Flags = D3DPRESENTFLAG_DISCARD_DEPTHSTENCIL;
489  d3dparams.AutoDepthStencilFormat = (D3DFORMAT) video_settings.GetDepthStencilFormat();
490  }
491  else {
492  d3dparams.Flags = 0;
493  }
494 
495  d3dparams.Flags |= D3DPRESENTFLAG_DEVICECLIP;
496 
497  if (video_settings.IsWindowed()) {
498  d3dparams.BackBufferWidth = video_settings.window_width;
499  d3dparams.BackBufferHeight = video_settings.window_height;
500  d3dparams.BackBufferFormat = (D3DFORMAT) video_settings.GetBackBufferFormat();
501  d3dparams.FullScreen_RefreshRateInHz = 0;
502  d3dparams.PresentationInterval = D3DPRESENT_INTERVAL_IMMEDIATE;
503  }
504  else {
505  d3dparams.BackBufferWidth = video_settings.GetWidth();
506  d3dparams.BackBufferHeight = video_settings.GetHeight();
507  d3dparams.BackBufferFormat = (D3DFORMAT) video_settings.GetBackBufferFormat();
508  d3dparams.FullScreen_RefreshRateInHz = video_settings.GetRefreshRate();
509  d3dparams.PresentationInterval = D3DPRESENT_INTERVAL_DEFAULT;
510  }
511 
512  return true;
513 }
514 
515 
516 bool
517 VideoDX9::IsModeSupported(int w, int h, int b) const
518 {
519  if (dx9enum)
520  return dx9enum->IsModeSupported(w, h, b);
521 
522  return false;
523 }
524 
525 bool
527 {
528  // custom video settings:
529  if (vs) {
530  if (vs != &video_settings)
531  CopyMemory(&video_settings, vs, sizeof(VideoSettings));
532  }
533 
534  // default video settings:
535  else {
536  ZeroMemory(&video_settings, sizeof(VideoSettings));
537 
538  video_settings.fullscreen_mode.width = 800;
539  video_settings.fullscreen_mode.height = 600;
541  }
542 
543  return true;
544 }
545 
546 bool
548 {
549  if (!d3ddevice || !SetVideoSettings(vs)) {
550  status = VIDEO_ERR;
551  return false;
552  }
553 
554  bool using_x_font = (d3dx_font != 0);
555 
556  RELEASE(d3dx_font);
557  InvalidateCache();
558  DestroyBuffers();
559  SetupParams();
560 
561  HRESULT hr = d3ddevice->Reset(&d3dparams);
562 
563  if (FAILED(hr)) {
564  VideoDX9Error("could not reset d3d device", hr);
565  status = VIDEO_ERR;
566  return false;
567  }
568 
569  // Store render target surface desc
570  IDirect3DSurface9* back_buffer;
571  d3ddevice->GetBackBuffer(0, 0, D3DBACKBUFFER_TYPE_MONO, &back_buffer);
572  back_buffer->GetDesc(&back_buffer_desc);
573  RELEASE(back_buffer);
574 
575  width = video_settings.GetWidth();
576  height = video_settings.GetHeight();
577  bpp = video_settings.GetDepth();
578 
579  shadow_enabled = vs->shadows;
580  bump_enabled = vs->bumpmaps;
581  spec_enabled = vs->specmaps;
582 
583 
584  if (CreateBuffers()) {
585  d3ddevice->SetRenderState(D3DRS_ALPHATESTENABLE, false);
586  d3ddevice->SetRenderState(D3DRS_ZFUNC, D3DCMP_LESSEQUAL);
587 
588  d3ddevice->SetTextureStageState(0, D3DTSS_ALPHAOP, D3DTOP_MODULATE);
589  d3ddevice->SetTextureStageState(0, D3DTSS_ALPHAARG1, D3DTA_TEXTURE);
590  d3ddevice->SetTextureStageState(0, D3DTSS_ALPHAARG2, D3DTA_DIFFUSE);
591  d3ddevice->SetSamplerState(0, D3DSAMP_MAGFILTER, D3DTEXF_LINEAR);
592  d3ddevice->SetSamplerState(0, D3DSAMP_MINFILTER, D3DTEXF_LINEAR);
593  d3ddevice->SetSamplerState(0, D3DSAMP_MIPFILTER, D3DTEXF_NONE);
594 
595  D3DVIEWPORT9 view;
596 
597  hr = d3ddevice->GetViewport(&view);
598  if (SUCCEEDED(hr)) {
599  rect.x = view.X;
600  rect.y = view.Y;
601  rect.w = view.Width;
602  rect.h = view.Height;
603  }
604 
605  if (using_x_font)
606  UseXFont(font_name, font_size, font_bold, font_ital);
607 
608  status = VIDEO_OK;
609  }
610 
611  return status == VIDEO_OK;
612 }
613 
614 // +--------------------------------------------------------------------+
615 
616 bool
617 VideoDX9::CreateBuffers()
618 {
619  if (d3ddevice) {
620  UINT vertex_size = sizeof(VideoDX9ScreenVertex);
621  UINT index_size = sizeof(WORD);
622 
623  if (!screen_vbuf) {
624  screen_vbuf = new(__FILE__,__LINE__) VideoDX9VertexBuffer(
625  this,
626  NUM_SCREEN_VERTS,
627  vertex_size,
629  D3DUSAGE_DYNAMIC | D3DUSAGE_WRITEONLY);
630  }
631 
632  if (!screen_ibuf) {
633  screen_ibuf = new(__FILE__,__LINE__) VideoDX9IndexBuffer(
634  this,
635  NUM_SCREEN_INDICES,
636  D3DUSAGE_DYNAMIC | D3DUSAGE_WRITEONLY);
637  }
638 
639  screen_line_verts = new(__FILE__,__LINE__) VideoDX9ScreenVertex[256];
640  line_verts = new(__FILE__,__LINE__) VideoDX9LineVertex[512];
641 
642  // create effects:
643  LPD3DXBUFFER code_buffer = 0;
644  DataLoader* loader = DataLoader::GetLoader();
645  HRESULT hr = E_FAIL;
646 
647  hr = d3ddevice->CreateVertexDeclaration(videoDX9NormalVertexElements,
648  &vertex_declaration);
649 
650  // The E - We want to load our shader from the standard filesystem by default, to allow for better modding.
651  if (video_settings.use_effects && !magic_fx_code) {
652  FILE* f;
653  ::fopen_s(&f, "magic.fx", "rb");
654 
655  if (f) {
656  ::fseek(f, 0, SEEK_END);
657  magic_fx_code_len = ftell(f);
658  ::fseek(f, 0, SEEK_SET);
659 
660  magic_fx_code = new(__FILE__,__LINE__) BYTE[magic_fx_code_len+1];
661  if (magic_fx_code) {
662  ::fread(magic_fx_code, magic_fx_code_len, 1, f);
663  magic_fx_code[magic_fx_code_len] = 0;
664  }
665  } else if (loader) {
666  magic_fx_code_len = loader->LoadBuffer("magic.fx", magic_fx_code, true, true);
667  }
668 
669  ::fclose(f);
670  }
671 
672  if (video_settings.use_effects && magic_fx_code && magic_fx_code_len) {
673  hr = D3DXCreateEffect(d3ddevice,
674  magic_fx_code,
675  magic_fx_code_len,
676  0, 0, 0, 0,
677  &magic_fx,
678  &code_buffer);
679 
680  if (code_buffer) {
681  ::Print("ERROR - Failed to compile 'magic.fx'\n");
682  ::Print((const char*) code_buffer->GetBufferPointer());
683  ::Print("\n\n");
684  RELEASE(code_buffer);
685  }
686  }
687  }
688 
689  return screen_vbuf && screen_ibuf;
690 }
691 
692 bool
693 VideoDX9::DestroyBuffers()
694 {
695  if (line_verts) {
696  delete line_verts;
697  line_verts = 0;
698  }
699 
700  if (screen_line_verts) {
701  delete screen_line_verts;
702  screen_line_verts = 0;
703  }
704 
705  if (screen_vbuf) {
706  delete screen_vbuf;
707  screen_vbuf = 0;
708  }
709 
710  if (screen_ibuf) {
711  delete screen_ibuf;
712  screen_ibuf = 0;
713  }
714 
715  if (font_verts) {
716  delete [] font_verts;
717  font_verts = 0;
718  }
719 
720  if (font_indices) {
721  delete [] font_indices;
722  font_indices = 0;
723  }
724 
725  font_nverts = 0;
726 
727  RELEASE(vertex_declaration);
728  RELEASE(magic_fx);
729 
730  return true;
731 }
732 
733 // +--------------------------------------------------------------------+
734 
735 DWORD
737 {
738  UINT result = 0;
739 
740  if (d3ddevice)
741  result = d3ddevice->GetAvailableTextureMem();
742 
743  return result;
744 }
745 
746 int
748 {
749  if (d3d && dx9enum && dx9enum->GetAdapterInfo()) {
750  VideoDX9DeviceInfo* dev_info = dx9enum->GetDeviceInfo(video_settings.GetDeviceType());
751 
752  if (dev_info) {
753  return (int) dev_info->caps.MaxTextureWidth;
754  }
755  }
756 
757  return 0;
758 }
759 
760 int
762 {
763  if (d3d && dx9enum && dx9enum->GetAdapterInfo()) {
764  VideoDX9DeviceInfo* dev_info = dx9enum->GetDeviceInfo(video_settings.GetDeviceType());
765 
766  if (dev_info) {
767  return (int) dev_info->caps.MaxTextureAspectRatio;
768  }
769  }
770 
771  return 0;
772 }
773 
774 // +--------------------------------------------------------------------+
775 
776 void
778 {
779  Print("VideoDX9::RecoverSurfaces()\n");
780 
781  HRESULT hr = D3D_OK;
782 
783  surface = 0;
784 
785  hr = d3ddevice->TestCooperativeLevel();
786 
787  if (hr == D3DERR_DEVICELOST) {
788  // This means that some app took exclusive mode access
789  // we need to sit in a loop till we get back to the right mode.
790  Print("D3DERR_DEVICELOST\n");
791 
792  do {
793  Sleep(500);
794  hr = d3ddevice->TestCooperativeLevel();
795  } while (hr == D3DERR_DEVICELOST);
796  }
797 
798  if (hr == D3DERR_DEVICENOTRESET) {
799  if (Reset(&video_settings))
800  hr = S_OK;
801  }
802 
803  if (SUCCEEDED(hr)) {
804  Print("* Invalidating Texture Cache\n");
805  // Re-fill the contents of textures which just got restored:
806  InvalidateCache();
807 
808  device_lost = false;
809  }
810 
811  Print("* Vid Mem Free: %8d\n", VidMemFree());
812  Print("* Recover Surfaces Complete.\n\n");
813 }
814 
815 // +--------------------------------------------------------------------+
816 
817 bool
819 {
820  background = c;
821  return true;
822 }
823 
824 //-----------------------------------------------------------------------------
825 // RampValue
826 //
827 // The gamma function with inputs in [0,255], scaled to a range with the
828 // default range appropriate for D3DGAMMARAMP.
829 //
830 inline WORD
831 RampValue(UINT i, double recip_gamma, double fade)
832 {
833  return (WORD) (65535.0 * fade * pow((double)i/255.f, recip_gamma));
834 }
835 
836 //-----------------------------------------------------------------------------
837 // ReciprocalGamma
838 //
839 // Given a gamma corrected i in [0,255], return 1/gamma
840 //
841 inline float
843 {
844  return logf(i/255.f)/logf(0.5f);
845 }
846 
847 //-----------------------------------------------------------------------------
848 // GammaValue
849 //
850 // Given a gamma corrected color channel value in [0,255], return the gamma.
851 //
852 inline float
853 GammaValue(UINT i)
854 {
855  return logf(0.5f)/logf(i/255.f);
856 }
857 
858 bool
860 {
861  HRESULT hr = E_FAIL;
862  double f = Color::GetFade();
863 
864  if (gamma != g || fade != f) {
865  if (d3ddevice) {
866  //::Print("VideoDX9 - SetGammaLevel(%d) fade = %f\n", g, f);
867 
868  // compute 1/gamma
869  float recip_gray = ReciprocalGamma(g);
870 
871  // compute i**(1/gamma) for all i and scale to range
872  for (UINT i = 0; i < 256; i++) {
873  int val = RampValue(i, recip_gray, f);
874 
875  gamma_ramp.red[i] = val;
876  gamma_ramp.green[i] = val;
877  gamma_ramp.blue[i] = val;
878  }
879 
880  d3ddevice->SetGammaRamp(0, D3DSGR_NO_CALIBRATION, &gamma_ramp);
881  hr = D3D_OK;
882  }
883 
884  gamma = g;
885  fade = f;
886  }
887 
888  return SUCCEEDED(hr);
889 }
890 
891 bool
892 VideoDX9::SetObjTransform(const Matrix& orient, const Point& loc)
893 {
894  HRESULT hr = E_FAIL;
895 
896  if (d3ddevice) {
897  D3DMATRIX world_matrix;
898  CreateD3DMatrix(world_matrix, orient, loc);
899  hr = d3ddevice->SetTransform(D3DTS_WORLD, &world_matrix);
900 
901  matrixWorld = world_matrix;
902  D3DXMatrixInverse(&matrixWorldInverse, 0, &matrixWorld);
903  }
904 
905  return SUCCEEDED(hr);
906 }
907 
908 // +--------------------------------------------------------------------+
909 
910 bool
912 {
913  HRESULT err;
914 
915  err = d3ddevice->Clear(0,
916  NULL,
917  D3DCLEAR_TARGET | D3DCLEAR_STENCIL | D3DCLEAR_ZBUFFER,
918  background.Value(),
919  1.0f,
920  0);
921 
922  if (FAILED(err)) {
923  static int report = 10;
924  if (report > 0) {
925  VideoDX9Error("Failed to clear device", err);
926  report--;
927  }
928  }
929 
930  return true;
931 }
932 
933 bool
935 {
936  HRESULT err;
937 
938  err = d3ddevice->Clear(0,
939  NULL,
940  D3DCLEAR_STENCIL | D3DCLEAR_ZBUFFER,
941  0,
942  1.0f,
943  0);
944 
945  if (FAILED(err)) {
946  static int report = 10;
947  if (report > 0) {
948  VideoDX9Error("Failed to clear depth buffer", err);
949  report--;
950  }
951  }
952 
953  return true;
954 }
955 
956 // +--------------------------------------------------------------------+
957 
958 bool
960 {
961  // Show the frame on the primary surface.
962  HRESULT err = d3ddevice->Present( NULL, NULL, NULL, NULL );
963 
964  if (FAILED(err)) {
965  if (err == D3DERR_DEVICELOST) {
966  device_lost = true;
967  }
968 
969  else {
970  static int report = 10;
971  if (report > 0) {
972  VideoDX9Error("Could not present frame", err);
973  report--;
974  }
975  }
976  }
977 
978  return true;
979 }
980 
981 // +--------------------------------------------------------------------+
982 
983 bool
985 {
986  return true;
987 }
988 
989 // +--------------------------------------------------------------------+
990 
991 bool
993 {
994  return true;
995 }
996 
997 // +--------------------------------------------------------------------+
998 
999 void
1001 {
1002  if (s)
1003  PrepareSurface(s);
1004 }
1005 
1006 void
1008 {
1009  if (texcache && tex)
1010  texcache->FindTexture(tex);
1011 }
1012 
1013 void
1015 {
1017  while (++iter) {
1018  // remove each model from the list...
1019  Model* model = iter.removeItem();
1020 
1021  // ...so that the buffer destructor doesn't
1022  // do it and mess up the iterator.
1023  model->DeletePrivateData();
1024  }
1025 
1026  if (texcache)
1027  texcache->InvalidateCache();
1028 }
1029 
1030 // +--------------------------------------------------------------------+
1031 
1032 void
1033 VideoDX9::CreateD3DMatrix(D3DMATRIX& result, const Matrix& m, const Point& p)
1034 {
1035  result._11 = (float) m.elem[0][0];
1036  result._12 = (float) m.elem[1][0];
1037  result._13 = (float) m.elem[2][0];
1038  result._14 = 0.0f;
1039 
1040  result._21 = (float) m.elem[0][1];
1041  result._22 = (float) m.elem[1][1];
1042  result._23 = (float) m.elem[2][1];
1043  result._24 = 0.0f;
1044 
1045  result._31 = (float) m.elem[0][2];
1046  result._32 = (float) m.elem[1][2];
1047  result._33 = (float) m.elem[2][2];
1048  result._34 = 0.0f;
1049 
1050  result._41 = (float) p.x;
1051  result._42 = (float) p.y;
1052  result._43 = (float) p.z;
1053  result._44 = 1.0f;
1054 }
1055 
1056 void
1057 VideoDX9::CreateD3DMatrix(D3DMATRIX& result, const Matrix& m, const Vec3& v)
1058 {
1059  result._11 = (float) m.elem[0][0];
1060  result._12 = (float) m.elem[1][0];
1061  result._13 = (float) m.elem[2][0];
1062  result._14 = 0.0f;
1063 
1064  result._21 = (float) m.elem[0][1];
1065  result._22 = (float) m.elem[1][1];
1066  result._23 = (float) m.elem[2][1];
1067  result._24 = 0.0f;
1068 
1069  result._31 = (float) m.elem[0][2];
1070  result._32 = (float) m.elem[1][2];
1071  result._33 = (float) m.elem[2][2];
1072  result._34 = 0.0f;
1073 
1074  result._41 = v.x;
1075  result._42 = v.y;
1076  result._43 = v.z;
1077  result._44 = 1.0f;
1078 }
1079 
1080 void
1081 VideoDX9::CreateD3DMaterial(D3DMATERIAL9& result, const Material& mtl)
1082 {
1083  CopyMemory(&result.Diffuse, &mtl.Kd, sizeof(D3DCOLORVALUE));
1084  CopyMemory(&result.Ambient, &mtl.Ka, sizeof(D3DCOLORVALUE));
1085  CopyMemory(&result.Specular, &mtl.Ks, sizeof(D3DCOLORVALUE));
1086  CopyMemory(&result.Emissive, &mtl.Ke, sizeof(D3DCOLORVALUE));
1087 
1088  result.Power = mtl.power;
1089 }
1090 
1091 
1092 // +--------------------------------------------------------------------+
1093 
1094 bool
1096 {
1097  if (d3ddevice) {
1098  HRESULT hr = E_FAIL;
1099  LPDIRECT3DSURFACE9 pSurf=NULL, pTempSurf=NULL;
1100  D3DSURFACE_DESC desc;
1101  D3DDISPLAYMODE dm;
1102 
1103  // get display dimensions
1104  // this will be the dimensions of the front buffer
1105  hr = d3ddevice->GetDisplayMode(0, &dm);
1106 
1107  if (FAILED(hr))
1108  VideoDX9Error("VideoDX9::Capture - Can't get display mode!", hr);
1109 
1110  desc.Width = dm.Width;
1111  desc.Height = dm.Height;
1112  desc.Format = D3DFMT_A8R8G8B8;
1113 
1114  hr = d3ddevice->CreateOffscreenPlainSurface(
1115  desc.Width,
1116  desc.Height,
1117  desc.Format,
1118  D3DPOOL_SYSTEMMEM,
1119  &pTempSurf,
1120  NULL);
1121 
1122  if (FAILED(hr)) {
1123  VideoDX9Error("VideoDX9::Capture - Cannot create offscreen buffer 1", hr);
1124  return false;
1125  }
1126 
1127  hr = d3ddevice->GetFrontBufferData(0, pTempSurf);
1128 
1129  if (FAILED(hr)) {
1130  RELEASE(pTempSurf);
1131  VideoDX9Error("VideoDX9::Capture - Can't get front buffer", hr);
1132  return false;
1133  }
1134 
1135 
1136  if (video_settings.IsWindowed()) {
1137  POINT pt={0, 0};
1138  RECT srcRect;
1139 
1140  // capture only the client area of the screen:
1141  ::GetClientRect(hwnd, &srcRect);
1142  ::ClientToScreen(hwnd, (LPPOINT) &srcRect);
1143  srcRect.right += srcRect.left;
1144  srcRect.bottom += srcRect.top;
1145 
1146  desc.Width = srcRect.right - srcRect.left;
1147  desc.Height = srcRect.bottom - srcRect.top;
1148  desc.Format = D3DFMT_A8R8G8B8; // this is what we get from the screen, so stick with it
1149 
1150  // NB we can't lock the back buffer direct because it's no created that way
1151  // and to do so hits performance, so copy to another surface
1152  // Must be the same format as the source surface
1153  hr = d3ddevice->CreateOffscreenPlainSurface(
1154  desc.Width,
1155  desc.Height,
1156  desc.Format,
1157  D3DPOOL_DEFAULT,
1158  &pSurf,
1159  NULL);
1160 
1161  if (FAILED(hr)) {
1162  RELEASE(pSurf);
1163  VideoDX9Error("VideoDX9::Capture - Cannot create offscreen buffer 2", hr);
1164  return false;
1165  }
1166 
1167  // Copy
1168  hr = d3ddevice->UpdateSurface(pTempSurf, &srcRect, pSurf, &pt);
1169 
1170  if (FAILED(hr)) {
1171  RELEASE(pTempSurf);
1172  RELEASE(pSurf);
1173  VideoDX9Error("VideoDX9::Capture - Cannot update surface", hr);
1174  return false;
1175  }
1176 
1177  RELEASE(pTempSurf);
1178  pTempSurf = pSurf;
1179  pSurf = NULL;
1180  }
1181 
1182  D3DLOCKED_RECT lockedRect;
1183  hr = pTempSurf->LockRect(&lockedRect, NULL,
1184  D3DLOCK_READONLY | D3DLOCK_NOSYSLOCK);
1185 
1186  if (FAILED(hr)) {
1187  VideoDX9Error("VideoDX9::Capture - can't lock rect", hr);
1188  RELEASE(pTempSurf);
1189  return false;
1190  }
1191 
1192  // Allocate color buffer
1193  DWORD* buffer = new DWORD[desc.Width * desc.Height];
1194  BYTE* src = (BYTE*) lockedRect.pBits;
1195  BYTE* dst = (BYTE*) buffer;
1196  Color clr;
1197 
1198  for (DWORD y = 0; y < desc.Height; y++) {
1199  BYTE *pRow = src;
1200 
1201  for (DWORD x = 0; x < desc.Width; x++) {
1202  switch(desc.Format) {
1203  case D3DFMT_R5G6B5:
1204  clr = Color::Unformat(*((WORD*) (pRow)));
1205 
1206  *dst++ = (BYTE) clr.Red();
1207  *dst++ = (BYTE) clr.Green();
1208  *dst++ = (BYTE) clr.Blue();
1209  *dst++ = 255;
1210  break;
1211 
1212  case D3DFMT_A8R8G8B8:
1213  case D3DFMT_X8R8G8B8:
1214  *dst++ = pRow[0]; // R
1215  *dst++ = pRow[1]; // G
1216  *dst++ = pRow[2]; // B
1217  *dst++ = 255;
1218 
1219  pRow += 4;
1220  break;
1221 
1222  case D3DFMT_R8G8B8:
1223  *dst++ = pRow[0]; // R
1224  *dst++ = pRow[1]; // G
1225  *dst++ = pRow[2]; // B
1226  *dst++ = 255;
1227 
1228  pRow += 3;
1229  break;
1230  }
1231 
1232  }
1233 
1234  src += lockedRect.Pitch;
1235  }
1236 
1237  bmp.CopyHighColorImage(desc.Width, desc.Height, buffer);
1238 
1239  delete [] buffer;
1240 
1241  RELEASE(pTempSurf);
1242  RELEASE(pSurf);
1243 
1244  return SUCCEEDED(hr);
1245  }
1246 
1247  return false;
1248 }
1249 
1250 // +--------------------------------------------------------------------+
1251 
1252 bool
1254 {
1255  if (d3ddevice && (rect.w < 1 || rect.h < 1)) {
1256  D3DVIEWPORT9 view;
1257  HRESULT hr = d3ddevice->GetViewport(&view);
1258  if (SUCCEEDED(hr)) {
1259  rect.x = view.X;
1260  rect.y = view.Y;
1261  rect.w = view.Width;
1262  rect.h = view.Height;
1263  }
1264  }
1265 
1266  r = rect;
1267  return true;
1268 }
1269 
1270 // +--------------------------------------------------------------------+
1271 
1272 bool
1274 {
1275  return SetViewport(r.x, r.y, r.w, r.h);
1276 }
1277 
1278 // +--------------------------------------------------------------------+
1279 
1280 bool
1281 VideoDX9::SetViewport(int x, int y, int w, int h)
1282 {
1283  if (!d3d || !d3ddevice)
1284  return false;
1285 
1286  HRESULT hr;
1287 
1288  // set up the viewport according to args:
1289  D3DVIEWPORT9 view;
1290 
1291  view.X = x;
1292  view.Y = y;
1293  view.Width = w;
1294  view.Height = h;
1295  view.MinZ = 0.0f;
1296  view.MaxZ = 1.0f;
1297 
1298  hr = d3ddevice->SetViewport(&view);
1299  if (FAILED(hr)) {
1300  VideoDX9Error("could not initialize viewport", hr);
1301  return false;
1302  }
1303 
1304  // set up the render state:
1305  for (int i = FILL_MODE; i < TEXTURE_WRAP; i++) {
1306  if (d3dstate_table[i]) {
1307  d3ddevice->SetRenderState((D3DRENDERSTATETYPE) d3dstate_table[i], render_state[i]);
1308  }
1309  }
1310 
1311  d3ddevice->SetRenderState(D3DRS_ZFUNC, D3DCMP_LESSEQUAL);
1312 
1313  rect.x = x;
1314  rect.y = y;
1315  rect.w = w;
1316  rect.h = h;
1317 
1318  return true;
1319 }
1320 
1321 // +--------------------------------------------------------------------+
1322 
1323 bool
1325 {
1326  ambient = c;
1327  return true;
1328 }
1329 
1330 bool
1332 {
1333  if (d3ddevice) {
1334  main_light = 0;
1335  back_light = 0;
1336 
1337  ListIter<Light> iter = (List<Light>&) lights;
1338  int index = -1;
1339 
1340  while (++iter) {
1341  Light* light = iter.value();
1342 
1343  if (light->IsActive()) {
1344  D3DLIGHT9 d3d_light;
1345  ZeroMemory(&d3d_light, sizeof(d3d_light));
1346  d3d_light.Type = (D3DLIGHTTYPE) light->Type();
1347 
1348  if (light->Type() == Light::LIGHT_DIRECTIONAL) {
1349  d3d_light.Direction.x = (float) (-light->Location().x);
1350  d3d_light.Direction.y = (float) (-light->Location().y);
1351  d3d_light.Direction.z = (float) (-light->Location().z);
1352 
1353  if (d3d_light.Direction.x == 0 &&
1354  d3d_light.Direction.y == 0 &&
1355  d3d_light.Direction.z == 0) {
1356 
1357  d3d_light.Direction.y = -1;
1358  }
1359 
1360  if (light->CastsShadow()) {
1361  if (!main_light || light->Intensity() > main_light->Intensity())
1362  main_light = light;
1363  }
1364  else if (!back_light) {
1365  back_light = light;
1366  }
1367  }
1368  else {
1369  d3d_light.Position.x = (float) ( light->Location().x);
1370  d3d_light.Position.y = (float) ( light->Location().y);
1371  d3d_light.Position.z = (float) ( light->Location().z);
1372  }
1373 
1374  float r = (light->GetColor().Red() / 255.0f) * light->Intensity();
1375  float g = (light->GetColor().Green() / 255.0f) * light->Intensity();
1376  float b = (light->GetColor().Blue() / 255.0f) * light->Intensity();
1377 
1378  d3d_light.Diffuse.r = r;
1379  d3d_light.Diffuse.g = g;
1380  d3d_light.Diffuse.b = b;
1381 
1382  d3d_light.Specular.r = r;
1383  d3d_light.Specular.g = g;
1384  d3d_light.Specular.b = b;
1385 
1386  d3d_light.Range = light->Intensity() * 10.0f;
1387  d3d_light.Attenuation0 = 0.1f;
1388  d3d_light.Attenuation1 = 0.7f;
1389  d3d_light.Attenuation2 = 0.0f;
1390 
1391  index++;
1392  d3ddevice->SetLight(index, &d3d_light);
1393  d3ddevice->LightEnable(index, TRUE);
1394  }
1395  }
1396 
1397  // turn off any unused lights from before:
1398  while (nlights > index+1) {
1399  d3ddevice->LightEnable(--nlights, FALSE);
1400  }
1401 
1402  nlights = index + 1;
1403 
1404  return true;
1405  }
1406 
1407  return false;
1408 }
1409 
1410 bool
1412 {
1413  if (d3ddevice) {
1414  camera = cam;
1415 
1416  D3DMATRIX m;
1417  CreateD3DMatrix(m, cam->Orientation(), cam->Pos());
1418  d3ddevice->SetTransform(D3DTS_VIEW, &m);
1419  matrixView = m;
1420 
1421  return true;
1422  }
1423 
1424  return false;
1425 }
1426 
1427 bool
1428 VideoDX9::SetProjection(float fov, float znear, float zfar, DWORD type)
1429 {
1430  if (d3ddevice && zfar > znear) {
1431  D3DMATRIX m;
1432  float h, w, Q;
1433 
1434  double width = (float) (rect.w);
1435  double height = (float) (rect.h);
1436  ZeroMemory(&m, sizeof(m));
1437 
1438  /***
1439  *** PERSPECTIVE PROJECTION:
1440  ***/
1441 
1442  if (type == PROJECTION_PERSPECTIVE) {
1443  double xscale = width / fov;
1444  double yscale = height / fov;
1445 
1446  double maxscale = xscale;
1447  if (yscale > xscale) maxscale = yscale;
1448 
1449  double xangle = atan(fov/2 * maxscale/xscale);
1450 
1451  w = (float) (2/tan(xangle)); // 1/tan(x) == cot(x)
1452  h = (float) (w * width/height);
1453  Q = zfar/(zfar - znear);
1454 
1455  m._11 = w;
1456  m._22 = h;
1457  m._33 = Q;
1458  m._43 = -Q*znear;
1459  m._34 = 1;
1460  }
1461 
1462  /***
1463  *** ORTHOGONAL PROJECTION:
1464  ***/
1465 
1466  else if (type == PROJECTION_ORTHOGONAL) {
1467  m._11 = (float) (fov/width);
1468  m._22 = (float) (fov/height);
1469  m._33 = (float) (1/(zfar-znear));
1470  m._43 = (float) (znear/(znear-zfar));
1471  m._44 = (float) (1);
1472  }
1473 
1474  else {
1475  return false;
1476  }
1477 
1478  d3ddevice->SetTransform(D3DTS_PROJECTION, &m);
1479  matrixProj = m;
1480 
1481  return true;
1482  }
1483 
1484  return false;
1485 }
1486 
1487 // +--------------------------------------------------------------------+
1488 
1489 bool
1491 {
1492  if (environment_cube && !faces) {
1493  delete environment_cube;
1494  environment_cube = 0;
1495  return true;
1496  }
1497 
1498  if (!environment_cube) {
1499  environment_cube = new(__FILE__,__LINE__) TexCubeDX9(this);
1500  }
1501 
1502  if (environment_cube) {
1503  bool ok = true;
1504  for (int i = 0; i < 6; i++)
1505  ok = ok && environment_cube->LoadTexture(faces[i], i);
1506  return ok;
1507  }
1508 
1509  return false;
1510 }
1511 
1512 // +--------------------------------------------------------------------+
1513 
1514 bool
1516 {
1517  if (!d3ddevice)
1518  return false;
1519 
1520  if (render_state[state] == value || d3dstate_table[state] == 0) {
1521  render_state[state] = value;
1522  return true;
1523  }
1524 
1525  HRESULT hr = E_FAIL;
1526 
1527  // special case for texture wrapping:
1528  if (state == TEXTURE_WRAP) {
1529  DWORD wrap = D3DTADDRESS_CLAMP;
1530 
1531  if (value)
1532  wrap = D3DTADDRESS_WRAP;
1533 
1534  hr = d3ddevice->SetSamplerState(0, D3DSAMP_ADDRESSU, wrap);
1535  hr = d3ddevice->SetSamplerState(0, D3DSAMP_ADDRESSV, wrap);
1536  }
1537 
1538  // special case for fog enable:
1539  else if (state == FOG_ENABLE) {
1540  if (value) {
1541  hr = d3ddevice->SetRenderState(D3DRS_FOGVERTEXMODE, D3DFOG_EXP);
1542  hr = d3ddevice->SetRenderState(D3DRS_FOGTABLEMODE, D3DFOG_NONE);
1543  }
1544 
1545  hr = d3ddevice->SetRenderState(D3DRS_FOGENABLE, value);
1546  }
1547 
1548  // special case for z bias
1549  else if (state == Z_BIAS) {
1550  if (value) {
1551  FLOAT bias_scale = 1.0f;
1552  FLOAT depth_bias = (FLOAT) (DW2I(value) / -10000.0);
1553 
1554  hr = d3ddevice->SetRenderState(D3DRS_SLOPESCALEDEPTHBIAS, F2DW(bias_scale));
1555  hr = d3ddevice->SetRenderState(D3DRS_DEPTHBIAS, F2DW(depth_bias));
1556  }
1557  else {
1558  hr = d3ddevice->SetRenderState(D3DRS_SLOPESCALEDEPTHBIAS, 0);
1559  hr = d3ddevice->SetRenderState(D3DRS_DEPTHBIAS, 0);
1560  }
1561  }
1562 
1563  // set default z func along with z enable
1564  else if (state == Z_ENABLE) {
1565  hr = d3ddevice->SetRenderState(D3DRS_ZFUNC, D3DCMP_LESSEQUAL);
1566  hr = d3ddevice->SetRenderState(D3DRS_ZENABLE, value);
1567  }
1568 
1569  // all other render states:
1570  else {
1571  hr = d3ddevice->SetRenderState((D3DRENDERSTATETYPE) d3dstate_table[state], value);
1572  }
1573 
1574  if (FAILED(hr)) {
1575  VideoDX9Error("could not SetRenderState", hr);
1576  return false;
1577  }
1578  else {
1579  render_state[state] = value;
1580  }
1581 
1582  return true;
1583 }
1584 
1585 bool
1586 VideoDX9::SetBlendType(int blend_type)
1587 {
1588  if (blend_type == current_blend_state)
1589  return true;
1590 
1591  switch (blend_type) {
1592  default:
1593  // map misc blend types to SOLID
1594  // and fall through to that case
1595 
1596  blend_type = BLEND_SOLID;
1597 
1598  case BLEND_SOLID:
1599  d3ddevice->SetRenderState(D3DRS_ALPHABLENDENABLE, false);
1600  break;
1601 
1602  case BLEND_ALPHA:
1603  d3ddevice->SetRenderState(D3DRS_ALPHABLENDENABLE, true);
1604  d3ddevice->SetRenderState(D3DRS_SRCBLEND, D3DBLEND_SRCALPHA);
1605  d3ddevice->SetRenderState(D3DRS_DESTBLEND, D3DBLEND_INVSRCALPHA);
1606  break;
1607 
1608  case BLEND_ADDITIVE:
1609  d3ddevice->SetRenderState(D3DRS_ALPHABLENDENABLE, true);
1610  d3ddevice->SetRenderState(D3DRS_SRCBLEND, D3DBLEND_ONE);
1611  d3ddevice->SetRenderState(D3DRS_DESTBLEND, D3DBLEND_ONE);
1612  break;
1613  }
1614 
1615  current_blend_state = blend_type;
1616  return true;
1617 }
1618 
1619 // +--------------------------------------------------------------------+
1620 
1621 bool
1623 {
1624  if (device_lost) {
1625  RecoverSurfaces();
1626 
1627  if (status != VIDEO_OK)
1628  return false;
1629  }
1630 
1631  stats.Clear();
1632 
1633  HRESULT err = 0;
1634  static int frame_number = 1;
1635  static int report_errs = 100;
1636 
1637  stats.nframe = frame_number;
1638  texcache->FrameNumber(frame_number++);
1639 
1640  // update gamma ramp for global fade value:
1641  SetGammaLevel(gamma);
1642 
1643  ClearAll();
1644 
1645  err = d3ddevice->BeginScene();
1646 
1647  if (FAILED(err)) {
1648  if (report_errs > 0) {
1649  report_errs--;
1650  VideoDX9Error("could not begin scene", err);
1651  }
1652 
1653  return false;
1654  }
1655 
1656  scene_active = 1;
1657  current_blend_state = -1;
1658 
1661 
1662  return true;
1663 }
1664 
1665 // +--------------------------------------------------------------------+
1666 
1667 bool
1669 {
1670  HRESULT err = 0;
1671 
1672  if (scene_active) {
1673  err = d3ddevice->EndScene();
1674 
1675  if (FAILED(err)) {
1676  VideoDX9Error("could not end scene", err);
1677  return false;
1678  }
1679  }
1680 
1681  scene_active = 0;
1682  return true;
1683 }
1684 
1685 // +--------------------------------------------------------------------+
1686 
1687 static DWORD ColorModulate(DWORD a, DWORD b)
1688 {
1689  float a0 = (float) ((a ) & 0xff)/255.0f;
1690  float a1 = (float) ((a>> 8) & 0xff)/255.0f;
1691  float a2 = (float) ((a>>16) & 0xff)/255.0f;
1692  float a3 = (float) ((a>>24) & 0xff)/255.0f;
1693 
1694  float b0 = (float) ((b ) & 0xff)/255.0f;
1695  float b1 = (float) ((b>> 8) & 0xff)/255.0f;
1696  float b2 = (float) ((b>>16) & 0xff)/255.0f;
1697  float b3 = (float) ((b>>24) & 0xff)/255.0f;
1698 
1699  return (DWORD) ((BYTE)(a3*b3*255.0f) << 24) |
1700  ((BYTE)(a2*b2*255.0f) << 16) |
1701  ((BYTE)(a1*b1*255.0f) << 8) |
1702  ((BYTE)(a0*b0*255.0f));
1703 }
1704 
1705 // +--------------------------------------------------------------------+
1706 
1707 bool
1708 VideoDX9::PopulateScreenVerts(VertexSet* vset)
1709 {
1710  if (!vset || !screen_vbuf)
1711  return false;
1712 
1713  num_verts = vset->nverts;
1714 
1715  VideoDX9ScreenVertex* v = (VideoDX9ScreenVertex*) screen_vbuf->Lock(num_verts);
1716 
1717  if (v) {
1718  first_vert = screen_vbuf->GetNextVert();
1719 
1720  for (int i = 0; i < num_verts; i++) {
1721  v->sx = vset->s_loc[i].x;
1722  v->sy = vset->s_loc[i].y;
1723  v->sz = vset->s_loc[i].z;
1724  v->rhw = vset->rw[i];
1725 
1726  v->diffuse = vset->diffuse[i];
1727 
1728  v->tu = vset->tu[i];
1729  v->tv = vset->tv[i];
1730 
1731  v++;
1732  }
1733 
1734  screen_vbuf->Unlock();
1735  return true;
1736  }
1737 
1738  Print(" VideoDX9: could not lock screen vbuf for %d verts.\n", num_verts);
1739  return false;
1740 }
1741 
1742 // +--------------------------------------------------------------------+
1743 
1744 bool
1745 VideoDX9::DrawPolys(int npolys, Poly* polys)
1746 {
1747  bool result = false;
1748 
1749  if (d3ddevice && polys && npolys > 0) {
1750  // screen space polys:
1751  if (polys->vertex_set->space == VertexSet::SCREEN_SPACE)
1752  return DrawScreenPolys(npolys, polys);
1753 
1754  // world space polys:
1755  stats.ncalls++;
1756 
1757  VertexSet* vset = polys->vertex_set;
1758  int nverts = vset->nverts;
1759  bool luminous = false;
1760  bool detail = false;
1761  DWORD fvf = 0;
1762  void* verts = 0;
1763  int vsize = 0;
1764  WORD* indices = new(__FILE__,__LINE__) WORD[npolys*6];
1765 
1766  if (polys->material) {
1767  luminous = polys->material->luminous;
1768  }
1769 
1770  if (vset->tu1 != 0) {
1771  VideoDX9DetailVertex* v = new(__FILE__,__LINE__) VideoDX9DetailVertex[nverts];
1772  verts = v;
1773  vsize = sizeof(VideoDX9DetailVertex);
1775 
1776  for (int i = 0; i < nverts; i++) {
1777  v->x = vset->loc[i].x;
1778  v->y = vset->loc[i].y;
1779  v->z = vset->loc[i].z;
1780 
1781  v->diffuse = vset->diffuse[i];
1782  v->specular = vset->specular[i];
1783 
1784  v->tu = vset->tu[i];
1785  v->tv = vset->tv[i];
1786  v->tu1 = vset->tu1[i];
1787  v->tv1 = vset->tv1[i];
1788 
1789  v++;
1790  }
1791  }
1792 
1793  else if (luminous) {
1794  VideoDX9LuminousVertex* v = new(__FILE__,__LINE__) VideoDX9LuminousVertex[nverts];
1795  verts = v;
1796  vsize = sizeof(VideoDX9LuminousVertex);
1798 
1799  for (int i = 0; i < nverts; i++) {
1800  v->x = vset->loc[i].x;
1801  v->y = vset->loc[i].y;
1802  v->z = vset->loc[i].z;
1803 
1804  v->diffuse = vset->diffuse[i];
1805 
1806  v->tu = vset->tu[i];
1807  v->tv = vset->tv[i];
1808 
1809  v++;
1810  }
1811  }
1812 
1813  else {
1814  VideoDX9SolidVertex* v = new(__FILE__,__LINE__) VideoDX9SolidVertex[nverts];
1815  verts = v;
1816  vsize = sizeof(VideoDX9SolidVertex);
1818 
1819  for (int i = 0; i < nverts; i++) {
1820  v->x = vset->loc[i].x;
1821  v->y = vset->loc[i].y;
1822  v->z = vset->loc[i].z;
1823 
1824  v->nx = vset->nrm[i].x;
1825  v->ny = vset->nrm[i].y;
1826  v->nz = vset->nrm[i].z;
1827 
1828  v->tu = vset->tu[i];
1829  v->tv = vset->tv[i];
1830 
1831  v++;
1832  }
1833  }
1834 
1835  if (verts && indices) {
1836  HRESULT hr = E_FAIL;
1837 
1838  // fill index array
1839  int num_indices = 0;
1840  int num_tris = 0;
1841 
1842  WORD* s = indices;
1843  Poly* p = polys;
1844 
1845  for (int i = 0; i < npolys; i++) {
1846  if (p->nverts == 3) {
1847  num_indices += 3;
1848  num_tris += 1;
1849 
1850  *s++ = p->verts[0];
1851  *s++ = p->verts[1];
1852  *s++ = p->verts[2];
1853  }
1854 
1855  else if (p->nverts == 4) {
1856  num_indices += 6;
1857  num_tris += 2;
1858 
1859  *s++ = p->verts[0];
1860  *s++ = p->verts[1];
1861  *s++ = p->verts[2];
1862 
1863  *s++ = p->verts[0];
1864  *s++ = p->verts[2];
1865  *s++ = p->verts[3];
1866  }
1867 
1868  p++;
1869  }
1870 
1871  hr = d3ddevice->SetTransform(D3DTS_WORLD, &identity_matrix);
1872  hr = d3ddevice->SetVertexShader(NULL);
1873  hr = d3ddevice->SetFVF(fvf);
1874 
1875  // send primitives to the device
1876  Material* mtl = polys->material;
1877  IDirect3DTexture9* texture = 0;
1878 
1879  if (mtl && texcache && mtl->tex_diffuse) {
1880  texture = texcache->FindTexture(mtl->tex_diffuse);
1881  }
1882 
1883  if (current_texture != texture) {
1884  hr = d3ddevice->SetTexture(0, texture);
1885  current_texture = texture;
1886  }
1887 
1888  if (mtl && texcache && mtl->tex_detail) {
1889  texture = texcache->FindTexture(mtl->tex_detail);
1890  hr = d3ddevice->SetTexture(1, texture);
1891  }
1892 
1893  if (mtl && !luminous) {
1894  D3DMATERIAL9 d3dmtl;
1895  CreateD3DMaterial(d3dmtl, *mtl);
1896 
1897  hr = d3ddevice->SetMaterial(&d3dmtl);
1898  hr = d3ddevice->SetRenderState(D3DRS_AMBIENT, ambient.Value());
1899  }
1900 
1901  // set render states and select buffers
1902  SetRenderState(FILL_MODE, D3DFILL_SOLID);
1903  SetRenderState(LIGHTING_ENABLE, luminous ? FALSE : TRUE);
1904  SetBlendType(mtl->blend);
1905 
1906  d3ddevice->SetTextureStageState(0, D3DTSS_COLOROP, D3DTOP_MODULATE);
1907  d3ddevice->SetTextureStageState(0, D3DTSS_COLORARG1, D3DTA_TEXTURE);
1908  d3ddevice->SetTextureStageState(0, D3DTSS_COLORARG2, D3DTA_DIFFUSE);
1909  d3ddevice->SetTextureStageState(0, D3DTSS_ALPHAOP, D3DTOP_MODULATE);
1910  d3ddevice->SetTextureStageState(0, D3DTSS_ALPHAARG1, D3DTA_TEXTURE);
1911  d3ddevice->SetTextureStageState(0, D3DTSS_ALPHAARG2, D3DTA_DIFFUSE);
1912 
1913  d3ddevice->SetTextureStageState(1, D3DTSS_COLOROP, D3DTOP_DISABLE);
1914 
1915 
1916  hr = d3ddevice->DrawIndexedPrimitiveUP(
1917  D3DPT_TRIANGLELIST,
1918  0,
1919  nverts,
1920  num_tris,
1921  indices,
1922  D3DFMT_INDEX16,
1923  verts,
1924  vsize);
1925 
1926  if (FAILED(hr)) {
1927  static int report = 10;
1928  if (report-- > 0)
1929  VideoDX9Error("Could not draw 3D polys.", hr);
1930  }
1931 
1932  delete [] verts;
1933  delete [] indices;
1934 
1935  if (SUCCEEDED(hr)) {
1936  stats.nverts += nverts;
1937  stats.npolys += num_tris;
1938  result = true;
1939  }
1940  }
1941  }
1942 
1943  return result;
1944 }
1945 
1946 // +--------------------------------------------------------------------+
1947 
1948 bool
1949 VideoDX9::DrawScreenPolys(int npolys, Poly* polys, int blend)
1950 {
1951  bool result = false;
1952  HRESULT hr = E_FAIL;
1953 
1954  if (d3ddevice && polys && npolys > 0 && screen_vbuf && screen_ibuf) {
1955  stats.ncalls++;
1956 
1957  // fill screen vertex buffer
1958  if (!PopulateScreenVerts(polys->vertex_set))
1959  return false;
1960 
1961  // fill screen index buffer
1962  int num_indices = 0;
1963  int num_tris = 0;
1964 
1965  // count the number of indices needed for these polys
1966  for (int i = 0; i < npolys; i++) {
1967  Poly* p = polys + i;
1968 
1969  if (p->nverts == 3) {
1970  num_indices += 3;
1971  num_tris += 1;
1972  }
1973 
1974  else if (p->nverts == 4) {
1975  num_indices += 6;
1976  num_tris += 2;
1977  }
1978  }
1979 
1980  WORD* screen_indices = screen_ibuf->Lock(num_indices);
1981  int first_index = screen_ibuf->GetNextIndex();
1982 
1983  if (!screen_indices) {
1984  Print(" VideoDX9: could not lock screen ibuf for %d indices.\n", num_indices);
1985  return false;
1986  }
1987 
1988  // copy the indices into the locked index buffer
1989  WORD* s = screen_indices;
1990  Poly* p = polys;
1991 
1992  for (int i = 0; i < npolys; i++) {
1993  if (p->nverts == 3) {
1994  *s++ = p->verts[0] + first_vert;
1995  *s++ = p->verts[1] + first_vert;
1996  *s++ = p->verts[2] + first_vert;
1997  }
1998  else if (p->nverts == 4) {
1999  *s++ = p->verts[0] + first_vert;
2000  *s++ = p->verts[1] + first_vert;
2001  *s++ = p->verts[2] + first_vert;
2002 
2003  *s++ = p->verts[0] + first_vert;
2004  *s++ = p->verts[2] + first_vert;
2005  *s++ = p->verts[3] + first_vert;
2006  }
2007 
2008  p++;
2009  }
2010 
2011  screen_ibuf->Unlock();
2012 
2013  // set render states and select buffers
2014  SetRenderState(FILL_MODE, D3DFILL_SOLID);
2015  SetRenderState(Z_ENABLE, D3DZB_FALSE);
2016  SetRenderState(Z_WRITE_ENABLE, D3DZB_FALSE);
2018  SetBlendType(blend);
2019 
2020  d3ddevice->SetTextureStageState(0, D3DTSS_COLOROP, D3DTOP_MODULATE);
2021  d3ddevice->SetTextureStageState(0, D3DTSS_COLORARG1, D3DTA_TEXTURE);
2022  d3ddevice->SetTextureStageState(0, D3DTSS_COLORARG2, D3DTA_DIFFUSE);
2023  d3ddevice->SetTextureStageState(0, D3DTSS_ALPHAOP, D3DTOP_MODULATE);
2024  d3ddevice->SetTextureStageState(0, D3DTSS_ALPHAARG1, D3DTA_TEXTURE);
2025  d3ddevice->SetTextureStageState(0, D3DTSS_ALPHAARG2, D3DTA_DIFFUSE);
2026 
2027  d3ddevice->SetTextureStageState(1, D3DTSS_COLOROP, D3DTOP_DISABLE);
2028 
2029  // send primitives to the device
2030  Material* mtl = polys->material;
2031  IDirect3DTexture9* texture = 0;
2032 
2033  if (mtl && texcache && mtl->tex_diffuse) {
2034  texture = texcache->FindTexture(mtl->tex_diffuse);
2035  }
2036 
2037  if (current_texture != texture) {
2038  hr = d3ddevice->SetTexture(0, texture);
2039  current_texture = texture;
2040  }
2041 
2042  screen_vbuf->Select(0);
2043  screen_ibuf->Select();
2044  hr = d3ddevice->SetVertexShader(NULL);
2045  hr = d3ddevice->SetFVF(VideoDX9ScreenVertex::FVF);
2046 
2047  hr = d3ddevice->DrawIndexedPrimitive(
2048  D3DPT_TRIANGLELIST,
2049  0,
2050  first_vert,
2051  num_verts,
2052  first_index,
2053  num_tris);
2054 
2055  if (FAILED(hr)) {
2056  static int report = 10;
2057  if (report-- > 0)
2058  VideoDX9Error("Could not draw screen polys.", hr);
2059  }
2060  else {
2061  stats.nverts += num_verts;
2062  stats.npolys += num_tris;
2063  result = true;
2064  }
2065  }
2066 
2067  return result;
2068 }
2069 
2070 // +--------------------------------------------------------------------+
2071 
2072 bool
2073 VideoDX9::DrawSolid(Solid* s, DWORD blend_modes)
2074 {
2075  bool result = false;
2076  HRESULT hr = E_FAIL;
2077 
2078  if (d3ddevice && s && s->GetModel()) {
2079  Model* model = s->GetModel();
2080  Matrix orient = s->Orientation();
2081  orient.Transpose();
2082 
2083  D3DMATRIX world_matrix;
2084  CreateD3DMatrix(world_matrix, orient, s->Location());
2085  d3ddevice->SetTransform(D3DTS_WORLD, &world_matrix);
2086  matrixWorld = world_matrix;
2087  D3DXMatrixInverse(&matrixWorldInverse, 0, &matrixWorld);
2088 
2089  ListIter<Surface> surf_iter = model->GetSurfaces();
2090  while (++surf_iter) {
2091  Surface* surf = surf_iter.value();
2092 
2093  if (surf->IsHidden() || surf->IsSimplified())
2094  continue;
2095 
2096  if (PrepareSurface(surf)) {
2097  result = true;
2098 
2100  surf_data->vertex_buffer->Select(0);
2101  surf_data->index_buffer->Select();
2102 
2103  ListIter<Segment> seg_iter = surf->GetSegments();
2104  while (++seg_iter) {
2105  Segment* segment = seg_iter.value();
2106  Material* mtl = segment->material;
2107 
2108  if (mtl && (blend_modes & mtl->blend)) {
2109  result = result && DrawSegment(segment);
2110  }
2111  }
2112  }
2113  }
2114  }
2115 
2116  surface_has_tangent_data = false;
2117 
2118  return result;
2119 }
2120 
2121 bool
2122 VideoDX9::DrawSegment(Segment* segment)
2123 {
2124  bool result = false;
2125  bool detail = false;
2126  bool luminous = false;
2127  HRESULT hr = E_FAIL;
2128 
2129  if (segment && segment->video_data) {
2130  stats.ncalls++;
2131 
2132  VideoDX9SegmentData* seg_data = (VideoDX9SegmentData*) segment->video_data;
2133  int first_vert = seg_data->first_vert;
2134  int num_verts = seg_data->num_verts;
2135  int first_index = seg_data->first_index;
2136  int num_tris = seg_data->num_tris;
2137 
2138  if (segment->model)
2139  luminous = segment->model->IsLuminous();
2140 
2141  // set render states and select buffers
2142  d3ddevice->SetRenderState(D3DRS_AMBIENT, ambient.Value());
2143 
2144  Material* mtl = segment->material;
2145 
2146  if (use_material)
2147  mtl = use_material;
2148 
2149  if (segment->polys && segment->polys->vertex_set && segment->polys->vertex_set->tu1)
2150  detail = true;
2151 
2152  // send primitives to the device
2153  if (detail) {
2154  hr = d3ddevice->SetVertexShader(NULL);
2155  hr = d3ddevice->SetFVF(VideoDX9DetailVertex::FVF);
2156  }
2157 
2158  else if (luminous) {
2159  hr = d3ddevice->SetVertexShader(NULL);
2160  hr = d3ddevice->SetFVF(VideoDX9LuminousVertex::FVF);
2161  }
2162 
2163  else if (surface_has_tangent_data && vertex_declaration) {
2164  hr = d3ddevice->SetVertexDeclaration(vertex_declaration);
2165  hr = d3ddevice->SetVertexShader(NULL);
2166  }
2167 
2168  else {
2169  hr = d3ddevice->SetVertexShader(NULL);
2170  hr = d3ddevice->SetFVF(VideoDX9SolidVertex::FVF);
2171  }
2172 
2173  if (render_state[FILL_MODE] == FILL_WIREFRAME) {
2174  PrepareMaterial(mtl);
2175  SetupPass(0);
2176 
2177  for (int i = 0; i < segment->npolys; i++) {
2178  DrawPolyOutline(segment->polys + i);
2179  }
2180  }
2181 
2182  else if (luminous) {
2183  PrepareMaterial(mtl);
2184 
2185  SetRenderState(FILL_MODE, D3DFILL_SOLID);
2187  SetBlendType(mtl->blend);
2188 
2189  for (int pass = 0; pass < passes; pass++) {
2190  SetupPass(pass);
2191 
2192  hr = d3ddevice->DrawIndexedPrimitive(
2193  D3DPT_TRIANGLELIST,
2194  0,
2195  first_vert,
2196  num_verts,
2197  first_index,
2198  num_tris);
2199  }
2200 
2201  d3ddevice->SetTextureStageState(1, D3DTSS_COLOROP, D3DTOP_DISABLE);
2202  }
2203 
2204  else {
2205  PrepareMaterial(mtl);
2206 
2207  if (strategy == DX9_STRATEGY_GLOW && render_state[LIGHTING_PASS] > 0) {
2208  hr = 0;
2209  }
2210 
2211  else if (magic_fx && strategy < DX9_STRATEGY_BLEND && !detail) {
2212  DWORD vs_version = 0;
2213  DWORD ps_version = 0;
2214  bool shaders_ok = false;
2215 
2216  VideoDX9DeviceInfo* dev_info = dx9enum->GetDeviceInfo(video_settings.GetDeviceType());
2217 
2218  if (dev_info) {
2219  vs_version = video_settings.enable_vs ? dev_info->caps.VertexShaderVersion : 0;
2220  ps_version = video_settings.enable_ps ? dev_info->caps.PixelShaderVersion : 0;
2221 
2222  if (vs_version >= D3DVS_VERSION(1,1))
2223  shaders_ok = true;
2224  }
2225 
2226  if (surface_has_tangent_data && vertex_declaration) {
2227  hr = d3ddevice->SetVertexShader(NULL);
2228  hr = d3ddevice->SetVertexDeclaration(vertex_declaration);
2229  }
2230 
2231  else {
2232  hr = d3ddevice->SetVertexShader(NULL);
2233  hr = d3ddevice->SetFVF(VideoDX9SolidVertex::FVF);
2234  }
2235 
2236  bool would_bump = !IsBumpMapEnabled() && mtl->bump > 0.5;
2237 
2238  bool will_bump = IsBumpMapEnabled() && surface_has_tangent_data && shaders_ok && camera;
2239 
2240  bool do_pix = will_bump && render_state[LIGHTING_PASS] == 0;
2241  bool do_bump = will_bump && render_state[LIGHTING_PASS] > 0;
2242 
2243  D3DXMATRIX matrixWVP = matrixWorld * matrixView * matrixProj;
2244 
2245  D3DXVECTOR4 eyePos( (float) camera->Pos().x, (float) camera->Pos().y, (float) camera->Pos().z, 1.0f);
2246 
2247  D3DXVECTOR4 eyeObj;
2248  D3DXVec4Transform(&eyeObj, &eyePos, &matrixWorldInverse);
2249 
2250  D3DXVECTOR4 lightPos(100.0f, 100.0f, 100.0f, 1.0f);
2251  D3DXVECTOR4 lightColor(1,1,1,1);
2252  D3DXVECTOR4 ambientColor(0,0,0,1);
2253 
2254  ambientColor.x = ambient.fRed();
2255  ambientColor.y = ambient.fGreen();
2256  ambientColor.z = ambient.fBlue();
2257 
2258  if (main_light && (render_state[LIGHTING_PASS] > 0 || mtl->blend > Material::MTL_SOLID)) {
2259  lightPos.x = (float) main_light->Location().x;
2260  lightPos.y = (float) main_light->Location().y;
2261  lightPos.z = (float) main_light->Location().z;
2262 
2263  if (mtl->tex_bumpmap && do_bump)
2264  D3DXVec4Transform(&lightPos, &lightPos, &matrixWorldInverse);
2265 
2266  lightColor.x = main_light->GetColor().fRed() * main_light->Intensity();
2267  lightColor.y = main_light->GetColor().fGreen() * main_light->Intensity();
2268  lightColor.z = main_light->GetColor().fBlue() * main_light->Intensity();
2269  lightColor.w = 1.0f;
2270  }
2271 
2272  else if (back_light && render_state[LIGHTING_PASS] == 0) {
2273  lightPos.x = (float) back_light->Location().x;
2274  lightPos.y = (float) back_light->Location().y;
2275  lightPos.z = (float) back_light->Location().z;
2276 
2277  lightColor.x = back_light->GetColor().fRed() * back_light->Intensity();
2278  lightColor.y = back_light->GetColor().fGreen() * back_light->Intensity();
2279  lightColor.z = back_light->GetColor().fBlue() * back_light->Intensity();
2280  lightColor.w = 1.0f;
2281  }
2282 
2283  D3DXVECTOR4 lightDir = D3DXVECTOR4(0.0f, 0.0f, 0.0f, 0.0f) - lightPos;
2284  D3DXVec4Normalize(&lightDir, &lightDir);
2285 
2286  magic_fx->SetMatrix("wvp", &matrixWVP);
2287  magic_fx->SetMatrix("world", &matrixWorld);
2288  magic_fx->SetMatrix("view", &matrixView);
2289  magic_fx->SetMatrix("proj", &matrixProj);
2290  magic_fx->SetMatrix("worldInv", &matrixWorldInverse);
2291 
2292  magic_fx->SetVector("light1Pos", &lightPos);
2293  magic_fx->SetVector("light1Dir", &lightDir);
2294  magic_fx->SetVector("light1Color", &lightColor);
2295  magic_fx->SetVector("ambientColor", &ambientColor);
2296  magic_fx->SetVector("eyeObj", &eyeObj);
2297 
2298  FLOAT base_bias = (FLOAT) (DW2I(render_state[Z_BIAS]) / -10000.0);
2299  magic_fx->SetFloat("bias", base_bias + video_settings.depth_bias);
2300 
2301  ColorValue orig_ks = mtl->Ks;
2302 
2303  if (would_bump && mtl->specular_value >= 0.5)
2304  mtl->Ks = mtl->Ks * 0.3;
2305 
2306  magic_fx->SetValue("Ka", &mtl->Ka, sizeof(ColorValue));
2307  magic_fx->SetValue("Kd", &mtl->Kd, sizeof(ColorValue));
2308  magic_fx->SetValue("Ke", &mtl->Ke, sizeof(ColorValue));
2309  magic_fx->SetValue("Ks", &mtl->Ks, sizeof(ColorValue));
2310  magic_fx->SetFloat("Ns", mtl->power);
2311 
2312  if (would_bump && mtl->specular_value >= 0.5)
2313  mtl->Ks = orig_ks;
2314 
2315  if (mtl->tex_diffuse) {
2316  IDirect3DTexture9* texture = texcache->FindTexture(mtl->tex_diffuse);
2317  magic_fx->SetTexture("tex_d", texture);
2318  } else {
2319  magic_fx->SetTexture("tex_d", 0);
2320  }
2321 
2322  if (mtl->tex_emissive) {
2323  IDirect3DTexture9* texture = texcache->FindTexture(mtl->tex_emissive);
2324  magic_fx->SetTexture("tex_e", texture);
2325  } else {
2326  magic_fx->SetTexture("tex_e", 0);
2327  }
2328 
2329  if (mtl->tex_specular && IsSpecMapEnabled()) {
2330  IDirect3DTexture9* texture = texcache->FindTexture(mtl->tex_specular);
2331  magic_fx->SetTexture("tex_s", texture);
2332  } else {
2333  magic_fx->SetTexture("tex_s", 0);
2334  }
2335 
2336  if (mtl->tex_bumpmap && do_bump) {
2337  IDirect3DTexture9* texture = texcache->FindNormalMap(mtl->tex_bumpmap, mtl->bump);
2338  magic_fx->SetTexture("tex_n", texture);
2339  magic_fx->SetFloat("offsetAmp", mtl->bump / mtl->tex_bumpmap->Height());
2340  } else if (mtl->tex_bumpmap && !do_bump) {
2341  IDirect3DTexture9* texture = texcache->FindTexture(mtl->tex_bumpmap);
2342  magic_fx->SetTexture("tex_x", texture);
2343  } else {
2344  magic_fx->SetTexture("tex_x", 0);
2345  }
2346 
2347  const char* mtl_shader = mtl->GetShader(render_state[LIGHTING_PASS]);
2348  D3DXHANDLE hnd_shader = 0;
2349 
2350  if (mtl_shader) {
2351  if (!strcmp(mtl_shader, "null"))
2352  return true;
2353 
2354  hnd_shader = magic_fx->GetTechniqueByName(mtl_shader);
2355  }
2356 
2357  if (hnd_shader) {
2358  hr = magic_fx->SetTechnique(hnd_shader);
2359  } else {
2360  if (will_bump) {
2361  if (mtl->tex_specular && IsSpecMapEnabled()) {
2362  if (mtl->tex_bumpmap && do_bump) {
2363  if (ps_version >= D3DPS_VERSION(2,0))
2364  hr = magic_fx->SetTechnique("BumpSpecMapPix");
2365  else
2366  hr = magic_fx->SetTechnique("BumpSpecMap");
2367  } else if (mtl->tex_emissive && render_state[LIGHTING_PASS] == 0) {
2368  if (ps_version >= D3DPS_VERSION(2,0))
2369  hr = magic_fx->SetTechnique("EmissiveSpecMapPix");
2370  else
2371  hr = magic_fx->SetTechnique("EmissiveSpecularTexture");
2372  } else {
2373  if (ps_version >= D3DPS_VERSION(2,0))
2374  hr = magic_fx->SetTechnique("SpecMapPix");
2375  else
2376  hr = magic_fx->SetTechnique("SpecularTexture");
2377  }
2378  } else {
2379  if (mtl->tex_bumpmap && do_bump) {
2380  if (ps_version >= D3DPS_VERSION(2,0))
2381  hr = magic_fx->SetTechnique("BumpMapPix");
2382  else
2383  hr = magic_fx->SetTechnique("BumpMap");
2384  } else if (mtl->tex_emissive && render_state[LIGHTING_PASS] == 0) {
2385  if (ps_version >= D3DPS_VERSION(2,0))
2386  hr = magic_fx->SetTechnique("EmissivePix");
2387  else
2388  hr = magic_fx->SetTechnique("EmissiveTexture");
2389  } else {
2390  if (ps_version >= D3DPS_VERSION(2,0))
2391  hr = magic_fx->SetTechnique("SimplePix");
2392  else
2393  hr = magic_fx->SetTechnique("SimpleTexture");
2394  }
2395  }
2396  }
2397 
2398  else if (texcache && mtl->tex_diffuse) {
2399  if (mtl->tex_specular && IsSpecMapEnabled()) {
2400  if (mtl->tex_emissive && render_state[LIGHTING_PASS] == 0) {
2401  hr = magic_fx->SetTechnique("EmissiveSpecularTexture");
2402  } else {
2403  hr = magic_fx->SetTechnique("SpecularTexture");
2404  }
2405  }
2406 
2407  else {
2408  if (mtl->tex_emissive && render_state[LIGHTING_PASS] == 0) {
2409  hr = magic_fx->SetTechnique("EmissiveTexture");
2410  } else {
2411  hr = magic_fx->SetTechnique("SimpleTexture");
2412  }
2413  }
2414  } else {
2415  hr = magic_fx->SetTechnique("SimpleMaterial");
2416  }
2417  }
2418 
2419  if (environment_cube != 0 && magic_fx->IsParameterUsed("env_cube", hnd_shader)) {
2420  D3DXMATRIX env_matrix;
2421  D3DXMatrixIdentity(&env_matrix);
2422 
2423  magic_fx->SetMatrix("env_matrix", &env_matrix);
2424  magic_fx->SetTexture("env_cube", environment_cube->GetTexture());
2425  }
2426 
2427  if (render_state[STENCIL_ENABLE]) {
2428  d3ddevice->SetRenderState(D3DRS_STENCILENABLE, TRUE);
2429  d3ddevice->SetRenderState(D3DRS_STENCILREF, 0x01);
2430  d3ddevice->SetRenderState(D3DRS_STENCILFUNC, D3DCMP_GREATER);
2431  } else {
2432  d3ddevice->SetRenderState(D3DRS_STENCILENABLE, FALSE);
2433  }
2434 
2435  if (render_state[LIGHTING_PASS] > 0) {
2436  current_blend_state = 100;
2439  } else {
2440  current_blend_state = 100;
2441  SetBlendType(mtl->blend);
2442  }
2443 
2444  UINT nPasses = 0;
2445 
2446  hr = magic_fx->Begin(&nPasses, 0);
2447 
2448  for (UINT i = 0; i < nPasses; i++) {
2449  hr = magic_fx->BeginPass(i);
2450 
2451  hr = d3ddevice->DrawIndexedPrimitive(
2452  D3DPT_TRIANGLELIST,
2453  0,
2454  first_vert,
2455  num_verts,
2456  first_index,
2457  num_tris);
2458 
2459  hr = magic_fx->EndPass();
2460  }
2461 
2462  hr = magic_fx->End();
2463  } else {
2464  for (int pass = 0; pass < passes; pass++) {
2465  SetupPass(pass);
2466 
2467  hr = d3ddevice->DrawIndexedPrimitive(
2468  D3DPT_TRIANGLELIST,
2469  0,
2470  first_vert,
2471  num_verts,
2472  first_index,
2473  num_tris);
2474 
2475  if (detail) {
2476  hr = d3ddevice->SetVertexShader(NULL);
2477  hr = d3ddevice->SetFVF(VideoDX9DetailVertex::FVF);
2478  } else if (luminous) {
2479  hr = d3ddevice->SetVertexShader(NULL);
2480  hr = d3ddevice->SetFVF(VideoDX9LuminousVertex::FVF);
2481  } else if (surface_has_tangent_data && vertex_declaration) {
2482  hr = d3ddevice->SetVertexShader(NULL);
2483  hr = d3ddevice->SetVertexDeclaration(vertex_declaration);
2484  } else {
2485  hr = d3ddevice->SetVertexShader(NULL);
2486  hr = d3ddevice->SetFVF(VideoDX9SolidVertex::FVF);
2487  }
2488  }
2489  }
2490  }
2491 
2492  if (FAILED(hr)) {
2493  static int report = 10;
2494  if (report-- > 0)
2495  VideoDX9Error("Could not draw solid polys.", hr);
2496  } else {
2497  stats.nverts += num_verts;
2498  stats.npolys += num_tris;
2499  result = true;
2500  }
2501  }
2502 
2503  return result;
2504 }
2505 
2506 bool
2508 {
2509  if (d3ddevice && p && p->nverts >= 3) {
2510  static VideoDX9LineVertex verts[8];
2511 
2512  int nlines = p->nverts;
2513  VertexSet* vset = p->vertex_set;
2514  WORD index = 0;
2515  Color color = Color::Black;
2516  HRESULT hr = E_FAIL;
2517 
2518  ZeroMemory(verts, sizeof(verts));
2519 
2520  if (p->material)
2521  color = p->material->Kd.ToColor();
2522 
2523  for (int i = 0; i < p->nverts; i++) {
2524  index = p->verts[i];
2525 
2526  verts[i].x = vset->loc[index].x;
2527  verts[i].y = vset->loc[index].y;
2528  verts[i].z = vset->loc[index].z;
2529  verts[i].diffuse = color.Value();
2530  }
2531 
2532  // last vertex, to close the loop
2533  index = p->verts[0];
2534  int i = p->nverts;
2535 
2536  verts[i].x = vset->loc[index].x;
2537  verts[i].y = vset->loc[index].y;
2538  verts[i].z = vset->loc[index].z;
2539  verts[i].diffuse = color.Value();
2540 
2541  current_texture = 0;
2542 
2543  hr = d3ddevice->SetVertexShader(NULL);
2544  hr = d3ddevice->SetFVF(VideoDX9LineVertex::FVF);
2545  hr = d3ddevice->SetTexture(0, 0);
2546  hr = d3ddevice->DrawPrimitiveUP(
2547  D3DPT_LINESTRIP,
2548  nlines,
2549  verts,
2550  sizeof(VideoDX9LineVertex));
2551  return true;
2552  }
2553 
2554  return false;
2555 }
2556 
2557 // +--------------------------------------------------------------------+
2558 
2559 bool
2560 VideoDX9::DrawShadow(Solid* s, int nverts, Vec3* shadow_verts, bool visible)
2561 {
2562  bool result = false;
2563  HRESULT hr = E_FAIL;
2564 
2565  if (d3ddevice && s && nverts && shadow_verts && IsShadowEnabled()) {
2566  Matrix orient = s->Orientation();
2567  orient.Transpose();
2568 
2569  D3DMATRIX world_matrix;
2570  CreateD3DMatrix(world_matrix, orient, s->Location());
2571  d3ddevice->SetTransform(D3DTS_WORLD, &world_matrix);
2572  matrixWorld = world_matrix;
2573 
2574  // show shadow outlines:
2575  if (visible) {
2576  static VideoDX9LineVertex verts[4];
2577 
2578  d3ddevice->SetRenderState(D3DRS_ZENABLE, TRUE);
2579  d3ddevice->SetRenderState(D3DRS_ZWRITEENABLE, FALSE);
2580  d3ddevice->SetRenderState(D3DRS_ZFUNC, D3DCMP_LESSEQUAL);
2581  d3ddevice->SetRenderState(D3DRS_SHADEMODE, D3DSHADE_FLAT);
2582  d3ddevice->SetRenderState(D3DRS_LIGHTING, FALSE);
2583  d3ddevice->SetVertexShader(NULL);
2584  d3ddevice->SetFVF(VideoDX9LineVertex::FVF);
2585  d3ddevice->SetTexture(0, 0);
2586 
2588 
2589  for (int i = 0; i < nverts; i+=3) {
2590  DWORD c = 0xa0ffff80;
2591 
2592  verts[0].x = shadow_verts[i+0].x;
2593  verts[0].y = shadow_verts[i+0].y;
2594  verts[0].z = shadow_verts[i+0].z;
2595  verts[0].diffuse = c;
2596 
2597  verts[1].x = shadow_verts[i+1].x;
2598  verts[1].y = shadow_verts[i+1].y;
2599  verts[1].z = shadow_verts[i+1].z;
2600  verts[1].diffuse = c;
2601 
2602  verts[2].x = shadow_verts[i+2].x;
2603  verts[2].y = shadow_verts[i+2].y;
2604  verts[2].z = shadow_verts[i+2].z;
2605  verts[2].diffuse = c;
2606 
2607  verts[3].x = shadow_verts[i+0].x;
2608  verts[3].y = shadow_verts[i+0].y;
2609  verts[3].z = shadow_verts[i+0].z;
2610  verts[3].diffuse = c;
2611 
2612  hr = d3ddevice->DrawPrimitiveUP(
2613  D3DPT_LINESTRIP,
2614  3,
2615  verts,
2616  sizeof(VideoDX9LineVertex));
2617  }
2618 
2619  // restore lighting state
2620  d3ddevice->SetRenderState(D3DRS_LIGHTING, render_state[LIGHTING_ENABLE]);
2621  }
2622 
2623  // render shadows into stencil buffer:
2624 
2625  // Disable z-buffer writes (note: z-testing still occurs), and enable the
2626  // stencil-buffer
2627  d3ddevice->SetRenderState(D3DRS_ZENABLE, TRUE);
2628  d3ddevice->SetRenderState(D3DRS_ZWRITEENABLE, FALSE);
2629  d3ddevice->SetRenderState(D3DRS_STENCILENABLE, TRUE);
2630 
2631  // Dont bother with interpolating color
2632  d3ddevice->SetRenderState(D3DRS_SHADEMODE, D3DSHADE_FLAT);
2633 
2634  // Set up stencil compare fuction, reference value, and masks.
2635  // Stencil test passes if ((ref & mask) cmpfn (stencil & mask)) is true.
2636  // Note: since we set up the stencil-test to always pass, the STENCILFAIL
2637  // renderstate is really not needed.
2638  d3ddevice->SetRenderState(D3DRS_STENCILFUNC, D3DCMP_ALWAYS );
2639  d3ddevice->SetRenderState(D3DRS_STENCILZFAIL, D3DSTENCILOP_KEEP );
2640  d3ddevice->SetRenderState(D3DRS_STENCILFAIL, D3DSTENCILOP_KEEP );
2641 
2642  // If z-test passes, inc/decrement stencil buffer value
2643  d3ddevice->SetRenderState(D3DRS_STENCILREF, 0x1 );
2644  d3ddevice->SetRenderState(D3DRS_STENCILMASK, 0xff );
2645  d3ddevice->SetRenderState(D3DRS_STENCILWRITEMASK, 0xff );
2646  d3ddevice->SetRenderState(D3DRS_STENCILPASS, D3DSTENCILOP_INCR );
2647 
2648  // Make sure that no pixels get drawn to the frame buffer
2649  d3ddevice->SetRenderState(D3DRS_ALPHABLENDENABLE, TRUE );
2650  d3ddevice->SetRenderState(D3DRS_SRCBLEND, D3DBLEND_ZERO );
2651  d3ddevice->SetRenderState(D3DRS_DESTBLEND, D3DBLEND_ONE );
2652 
2653  d3ddevice->SetVertexShader(NULL);
2654  d3ddevice->SetFVF(D3DFVF_XYZ);
2655 
2656  // Draw front-side of shadow volume in stencil/z only
2657  hr = d3ddevice->DrawPrimitiveUP(D3DPT_TRIANGLELIST, nverts/3, shadow_verts, sizeof(Vec3));
2658 
2659  // Now reverse cull order so back sides of shadow volume are written.
2660  d3ddevice->SetRenderState(D3DRS_CULLMODE, D3DCULL_CW );
2661 
2662  // Decrement stencil buffer value
2663  d3ddevice->SetRenderState(D3DRS_STENCILPASS, D3DSTENCILOP_DECR );
2664 
2665  // Draw back-side of shadow volume in stencil/z only
2666  hr = d3ddevice->DrawPrimitiveUP(D3DPT_TRIANGLELIST, nverts/3, shadow_verts, sizeof(Vec3));
2667 
2668  // restore render states
2669  d3ddevice->SetRenderState(D3DRS_SHADEMODE, D3DSHADE_GOURAUD);
2670  d3ddevice->SetRenderState(D3DRS_CULLMODE, D3DCULL_CCW);
2671  d3ddevice->SetRenderState(D3DRS_ZWRITEENABLE, TRUE);
2672  d3ddevice->SetRenderState(D3DRS_STENCILENABLE, FALSE);
2673 
2674  // force restore of current blend type
2675  int type = current_blend_state;
2676  current_blend_state = 100;
2677  SetBlendType(type);
2678 
2679  result = SUCCEEDED(hr);
2680  }
2681 
2682  return result;
2683 }
2684 
2685 // +--------------------------------------------------------------------+
2686 
2687 bool
2688 VideoDX9::DrawLines(int nlines, Vec3* points, Color c, int blend)
2689 {
2690  bool result = false;
2691 
2692  if (d3ddevice && points && nlines > 0 && nlines <= 256) {
2693  stats.ncalls++;
2694 
2695  VideoDX9LineVertex* verts = line_verts;
2696 
2697  if (verts) {
2698  HRESULT hr = E_FAIL;
2699 
2700  for (int i = 0; i < 2*nlines; i++) {
2701  VideoDX9LineVertex* v = verts + i;
2702  Vec3* p = points + i;
2703 
2704  v->x = p->x;
2705  v->y = p->y;
2706  v->z = p->z;
2707  v->diffuse = c.Value();
2708  }
2709 
2710  hr = d3ddevice->SetTransform(D3DTS_WORLD, &identity_matrix);
2711  hr = d3ddevice->SetVertexShader(NULL);
2712  hr = d3ddevice->SetFVF(VideoDX9LineVertex::FVF);
2713 
2714  DWORD old_lighting = render_state[LIGHTING_ENABLE];
2715 
2716  // untextured lines:
2717  if (current_texture) {
2718  d3ddevice->SetTexture(0, 0);
2719  current_texture = 0;
2720  }
2721 
2723  SetBlendType(blend);
2724 
2725  hr = d3ddevice->DrawPrimitiveUP(
2726  D3DPT_LINELIST,
2727  nlines,
2728  verts,
2729  sizeof(VideoDX9LineVertex));
2730 
2731  if (FAILED(hr)) {
2732  static int report = 10;
2733  if (report-- > 0)
2734  VideoDX9Error("Could not draw 3D lines.", hr);
2735  }
2736 
2737  SetRenderState(LIGHTING_ENABLE, old_lighting);
2738 
2739  if (SUCCEEDED(hr)) {
2740  stats.nverts += 2*nlines;
2741  stats.nlines += nlines;
2742  result = true;
2743  }
2744  }
2745  }
2746 
2747  return result;
2748 }
2749 
2750 // +--------------------------------------------------------------------+
2751 
2752 bool
2753 VideoDX9::DrawScreenLines(int nlines, float* points, Color c, int blend)
2754 {
2755  bool result = false;
2756 
2757  if (d3ddevice && points && nlines > 0 && nlines <= 64) {
2758  stats.ncalls++;
2759 
2760  VideoDX9ScreenVertex* verts = screen_line_verts;
2761 
2762  if (verts) {
2763  HRESULT hr = E_FAIL;
2764 
2765  for (int i = 0; i < 2*nlines; i++) {
2766  VideoDX9ScreenVertex* v = verts + i;
2767 
2768  v->sx = points[2*i + 0];
2769  v->sy = points[2*i + 1];
2770  v->sz = 0.0f;
2771  v->rhw = 1.0f;
2772  v->diffuse = c.Value();
2773  v->tu = 0.0f;
2774  v->tv = 0.0f;
2775  }
2776 
2777  hr = d3ddevice->SetVertexShader(NULL);
2778  hr = d3ddevice->SetFVF(VideoDX9ScreenVertex::FVF);
2779 
2780  if (FAILED(hr)) {
2781  static int report = 10;
2782  if (report-- > 0)
2783  VideoDX9Error("Could not set FVF for screen lines.", hr);
2784  }
2785 
2786  else {
2787  if (current_texture != 0) {
2788  hr = d3ddevice->SetTexture(0, 0);
2789  current_texture = 0;
2790  }
2791 
2792  SetRenderState(FILL_MODE, D3DFILL_SOLID);
2793  SetRenderState(Z_ENABLE, D3DZB_FALSE);
2795  SetBlendType(blend);
2796 
2797  hr = d3ddevice->DrawPrimitiveUP(
2798  D3DPT_LINELIST,
2799  nlines,
2800  verts,
2801  sizeof(VideoDX9ScreenVertex));
2802 
2803  if (FAILED(hr)) {
2804  static int report = 10;
2805  if (report-- > 0)
2806  VideoDX9Error("Could not draw screen lines.", hr);
2807  }
2808  }
2809 
2810  if (SUCCEEDED(hr)) {
2811  stats.nverts += 2*nlines;
2812  stats.nlines += nlines;
2813  result = true;
2814  }
2815  }
2816  }
2817 
2818  return result;
2819 }
2820 
2821 // +--------------------------------------------------------------------+
2822 
2823 bool
2825 {
2826  if (vset && vset->nverts) {
2827  HRESULT hr = E_FAIL;
2828 
2829  int nverts = vset->nverts;
2830  VideoDX9LineVertex* verts = new(__FILE__,__LINE__) VideoDX9LineVertex[nverts];
2831 
2832  if (verts) {
2833  for (int i = 0; i < nverts; i++) {
2834  VideoDX9LineVertex* v = verts + i;
2835  Vec3* p = vset->loc + i;
2836 
2837  v->x = p->x;
2838  v->y = p->y;
2839  v->z = p->z;
2840  v->diffuse = vset->diffuse[i];
2841  }
2842 
2844 
2845  hr = d3ddevice->SetTransform(D3DTS_WORLD, &identity_matrix);
2846  hr = d3ddevice->SetVertexShader(NULL);
2847  hr = d3ddevice->SetFVF(VideoDX9LineVertex::FVF);
2848  hr = d3ddevice->SetTexture(0, 0);
2849  hr = d3ddevice->DrawPrimitiveUP(
2850  D3DPT_POINTLIST,
2851  nverts,
2852  verts,
2853  sizeof(VideoDX9LineVertex));
2854 
2855  delete [] verts;
2856  return true;
2857  }
2858  }
2859 
2860  return false;
2861 }
2862 
2863 // +--------------------------------------------------------------------+
2864 
2865 bool
2867 {
2868  use_material = m;
2869  return true;
2870 }
2871 
2872 // +--------------------------------------------------------------------+
2873 
2874 bool
2875 VideoDX9::UseXFont(const char* name, int size, bool bold, bool ital)
2876 {
2877  if (d3ddevice && name && *name && size > 4) {
2878  RELEASE(d3dx_font);
2879 
2880  strcpy_s(font_name, name);
2881  font_size = size;
2882  font_bold = bold;
2883  font_ital = ital;
2884 
2885  HRESULT hr = E_FAIL;
2886  HDC hdc = GetDC(NULL);
2887  int nLogPixelsY = GetDeviceCaps(hdc, LOGPIXELSY);
2888 
2889  ReleaseDC(NULL, hdc);
2890 
2891  int nHeight = -size * nLogPixelsY / 72;
2892 
2893  hr = D3DXCreateFont(d3ddevice, // D3D device
2894  nHeight, // Height
2895  0, // Width
2896  bold ? FW_BOLD : FW_NORMAL, // Weight
2897  1, // MipLevels, 0 = autogen mipmaps
2898  ital, // Italic
2899  DEFAULT_CHARSET, // CharSet
2900  OUT_DEFAULT_PRECIS, // OutputPrecision
2901  DEFAULT_QUALITY, // Quality
2902  DEFAULT_PITCH | FF_DONTCARE, // PitchAndFamily
2903  name, // pFaceName
2904  &d3dx_font); // ppFont
2905 
2906  if (SUCCEEDED(hr)) {
2907  return true;
2908  }
2909  }
2910 
2911  RELEASE(d3dx_font);
2912  return false;
2913 }
2914 
2915 bool
2916 VideoDX9::DrawText(const char* text, int count, const Rect& rect, DWORD format, Color c)
2917 {
2918  if (d3ddevice && text && *text && d3dx_font) {
2919  RECT r;
2920  r.left = rect.x;
2921  r.top = rect.y;
2922  r.right = rect.x + rect.w;
2923  r.bottom = rect.y + rect.h;
2924 
2925  d3dx_font->DrawText(0, text, count, &r, format, c.Value());
2926  }
2927 
2928  return false;
2929 }
2930 
2931 // +--------------------------------------------------------------------+
2932 
2933 bool
2934 VideoDX9::PrepareSurface(Surface* surf)
2935 {
2936  if (surf) {
2937  int nverts = surf->NumVerts();
2938  int nindices = surf->NumIndices();
2939  bool detail = surf->GetVertexSet()->tu1 != 0;
2940  bool luminous = false;
2941  DWORD dynamic = 0;
2942 
2943  if (surf->GetModel()) {
2944  luminous = surf->GetModel()->IsLuminous();
2945  dynamic = surf->GetModel()->IsDynamic() ? D3DUSAGE_DYNAMIC : 0;
2946  }
2947 
2948  surface_has_tangent_data = !luminous && (surf->GetVertexSet()->tangent && surf->GetVertexSet()->binormal);
2949 
2951 
2952  if (!surf_data) {
2953  surf_data = new(__FILE__,__LINE__) VideoDX9SurfaceData(surf->GetModel());
2954 
2955  surface_has_tangent_data = false;
2956 
2957  if (surf->GetVertexSet()->tangent && surf->GetVertexSet()->binormal) {
2958  surface_has_tangent_data = true;
2959  surf_data->vertex_buffer = new(__FILE__,__LINE__) VideoDX9VertexBuffer(
2960  this,
2961  nverts,
2962  sizeof(VideoDX9NormalVertex),
2963  0, // not an FVF vertex buffer
2964  dynamic | D3DUSAGE_WRITEONLY);
2965  }
2966 
2967  else if (detail) {
2968  surf_data->vertex_buffer = new(__FILE__,__LINE__) VideoDX9VertexBuffer(
2969  this,
2970  nverts,
2971  sizeof(VideoDX9DetailVertex),
2973  dynamic | D3DUSAGE_WRITEONLY);
2974  }
2975 
2976  else if (luminous) {
2977  surf_data->vertex_buffer = new(__FILE__,__LINE__) VideoDX9VertexBuffer(
2978  this,
2979  nverts,
2980  sizeof(VideoDX9LuminousVertex),
2982  dynamic | D3DUSAGE_WRITEONLY);
2983  }
2984 
2985  else {
2986  surf_data->vertex_buffer = new(__FILE__,__LINE__) VideoDX9VertexBuffer(
2987  this,
2988  nverts,
2989  sizeof(VideoDX9SolidVertex),
2991  dynamic | D3DUSAGE_WRITEONLY);
2992  }
2993 
2994  surf_data->index_buffer = new(__FILE__,__LINE__) VideoDX9IndexBuffer(
2995  this,
2996  nindices,
2997  dynamic | D3DUSAGE_WRITEONLY);
2998 
2999  if (!surf_data->vertex_buffer || !surf_data->index_buffer) {
3000  Print("VideoDX9: Unable to prepare surface '%s'\n", surf->Name());
3001  delete surf_data;
3002  return false;
3003  }
3004 
3005  surf->SetVideoPrivateData(surf_data);
3006  }
3007 
3008  if (surf_data && !surf_data->IsValid()) {
3009  if (detail) {
3010  VideoDX9DetailVertex* v = (VideoDX9DetailVertex*) surf_data->vertex_buffer->Lock(nverts);
3011 
3012  if (v) {
3013  const VertexSet* vset = surf->GetVertexSet();
3014  for (int i = 0; i < nverts; i++) {
3015  v->x = vset->loc[i].x;
3016  v->y = vset->loc[i].y;
3017  v->z = vset->loc[i].z;
3018 
3019  v->diffuse = vset->diffuse[i];
3020  v->specular = vset->specular[i];
3021 
3022  v->tu = vset->tu[i];
3023  v->tv = vset->tv[i];
3024  v->tu1 = vset->tu1[i];
3025  v->tv1 = vset->tv1[i];
3026 
3027  v++;
3028  }
3029 
3030  surf_data->vertex_buffer->Unlock();
3031  }
3032  }
3033 
3034  else if (luminous) {
3035  VideoDX9LuminousVertex* v = (VideoDX9LuminousVertex*) surf_data->vertex_buffer->Lock(nverts);
3036 
3037  if (v) {
3038  const VertexSet* vset = surf->GetVertexSet();
3039  for (int i = 0; i < nverts; i++) {
3040  v->x = vset->loc[i].x;
3041  v->y = vset->loc[i].y;
3042  v->z = vset->loc[i].z;
3043 
3044  v->diffuse = vset->diffuse[i];
3045 
3046  v->tu = vset->tu[i];
3047  v->tv = vset->tv[i];
3048 
3049  v++;
3050  }
3051 
3052  surf_data->vertex_buffer->Unlock();
3053  }
3054  }
3055 
3056  else if (surface_has_tangent_data) {
3057  VideoDX9NormalVertex* v = (VideoDX9NormalVertex*) surf_data->vertex_buffer->Lock(nverts);
3058 
3059  if (v) {
3060  const VertexSet* vset = surf->GetVertexSet();
3061  for (int i = 0; i < nverts; i++) {
3062  v->x = vset->loc[i].x;
3063  v->y = vset->loc[i].y;
3064  v->z = vset->loc[i].z;
3065 
3066  v->nx = vset->nrm[i].x;
3067  v->ny = vset->nrm[i].y;
3068  v->nz = vset->nrm[i].z;
3069 
3070  v->t0u = vset->tu[i];
3071  v->t0v = vset->tv[i];
3072  v->t1u = vset->tu[i];
3073  v->t1v = vset->tv[i];
3074 
3075  v->tx = vset->tangent[i].x;
3076  v->ty = vset->tangent[i].y;
3077  v->tz = vset->tangent[i].z;
3078 
3079  v->bx = vset->binormal[i].x;
3080  v->by = vset->binormal[i].y;
3081  v->bz = vset->binormal[i].z;
3082 
3083  v++;
3084  }
3085 
3086  surf_data->vertex_buffer->Unlock();
3087  }
3088  }
3089 
3090  else {
3091  VideoDX9SolidVertex* v = (VideoDX9SolidVertex*) surf_data->vertex_buffer->Lock(nverts);
3092 
3093  if (v) {
3094  const VertexSet* vset = surf->GetVertexSet();
3095  for (int i = 0; i < nverts; i++) {
3096  v->x = vset->loc[i].x;
3097  v->y = vset->loc[i].y;
3098  v->z = vset->loc[i].z;
3099 
3100  v->nx = vset->nrm[i].x;
3101  v->ny = vset->nrm[i].y;
3102  v->nz = vset->nrm[i].z;
3103 
3104  v->tu = vset->tu[i];
3105  v->tv = vset->tv[i];
3106 
3107  v++;
3108  }
3109 
3110  surf_data->vertex_buffer->Unlock();
3111  }
3112  }
3113 
3114  WORD* indices = surf_data->index_buffer->Lock(nindices);
3115 
3116  if (indices) {
3117  // copy the indices into the locked index buffer
3118  WORD* s = indices;
3119  Poly* p = surf->GetPolys();
3120 
3121  for (int i = 0; i < surf->NumPolys(); i++) {
3122  if (p->nverts == 3) {
3123  *s++ = p->verts[0];
3124  *s++ = p->verts[2];
3125  *s++ = p->verts[1];
3126  }
3127  else if (p->nverts == 4) {
3128  *s++ = p->verts[0];
3129  *s++ = p->verts[2];
3130  *s++ = p->verts[1];
3131 
3132  *s++ = p->verts[0];
3133  *s++ = p->verts[3];
3134  *s++ = p->verts[2];
3135  }
3136 
3137  p++;
3138  }
3139 
3140  surf_data->index_buffer->Unlock();
3141  }
3142 
3143  surf_data->Validate();
3144  }
3145 
3146  int first_index = 0;
3147 
3148  ListIter<Segment> seg_iter = surf->GetSegments();
3149  while (++seg_iter) {
3150  Segment* segment = seg_iter.value();
3151 
3152  if (!segment->video_data) {
3153  VideoDX9SegmentData* seg_data = new(__FILE__,__LINE__) VideoDX9SegmentData;
3154 
3155  int num_tris = 0;
3156  for (int i = 0; i < segment->npolys; i++)
3157  num_tris += segment->polys[i].nverts-2;
3158 
3159  seg_data->first_vert = 0;
3160  seg_data->num_verts = surf->NumVerts();
3161  seg_data->first_index = first_index;
3162  seg_data->num_tris = num_tris;
3163 
3164  segment->video_data = seg_data;
3165 
3166  first_index += num_tris * 3;
3167  }
3168  }
3169  }
3170 
3171  return true;
3172 }
3173 
3174 // +--------------------------------------------------------------------+
3175 
3176 int
3177 VideoDX9::PrepareMaterial(Material* m)
3178 {
3179  segment_material = m;
3180  strategy = 0;
3181  passes = 0;
3182 
3183  if (m) {
3184  int max_stages = 1;
3185  int max_textures = 1;
3186  bool multiply_add = false;
3187  bool dotproduct3 = false;
3188  bool vertexshader = false;
3189  bool pixelshader = false;
3190  DWORD vs_version = 0;
3191  DWORD ps_version = 0;
3192 
3193  VideoDX9DeviceInfo* dev_info = dx9enum->GetDeviceInfo(video_settings.GetDeviceType());
3194 
3195  if (dev_info) {
3196  max_stages = (int) dev_info->caps.MaxTextureBlendStages;
3197  max_textures = (int) dev_info->caps.MaxSimultaneousTextures;
3198  multiply_add = (dev_info->caps.TextureOpCaps & D3DTEXOPCAPS_MULTIPLYADD) ? true : false;
3199  dotproduct3 = (dev_info->caps.TextureOpCaps & D3DTEXOPCAPS_DOTPRODUCT3) ? true : false;
3200 
3201  vs_version = video_settings.enable_vs ? dev_info->caps.VertexShaderVersion : 0;
3202  ps_version = video_settings.enable_ps ? dev_info->caps.PixelShaderVersion : 0;
3203 
3204  vertexshader = vs_version >= D3DVS_VERSION(1,1);
3205  pixelshader = ps_version >= D3DPS_VERSION(2,0);
3206  }
3207 
3208  strategy = DX9_STRATEGY_SIMPLE;
3209  passes = 1;
3210 
3211  if (m->tex_alternate) {
3212  if (m->tex_detail && max_textures > 2 && max_stages > 4)
3213  strategy = DX9_STRATEGY_BLEND_DETAIL;
3214 
3215  else if (max_textures > 1 && max_stages > 3)
3216  strategy = DX9_STRATEGY_BLEND;
3217  }
3218 
3219  else if (m->tex_emissive && (!m->tex_diffuse || m->tex_diffuse == m->tex_emissive)) {
3220  strategy = DX9_STRATEGY_GLOW;
3221  }
3222 
3223  else if (IsSpecMapEnabled() && m->tex_specular && !m->tex_emissive) {
3224  strategy = DX9_STRATEGY_SPECMAP;
3225 
3226  if (max_textures < 2 || max_stages < 2 || !multiply_add)
3227  passes = 2;
3228  }
3229 
3230  else if ((!IsSpecMapEnabled() || !m->tex_specular) && m->tex_emissive) {
3231  strategy = DX9_STRATEGY_EMISSIVE;
3232 
3233  if (max_textures < 2 || max_stages < 2)
3234  passes = 2;
3235  }
3236 
3237  else if (IsSpecMapEnabled() && m->tex_specular && m->tex_emissive) {
3238  strategy = DX9_STRATEGY_SPEC_EMISSIVE;
3239 
3240  if (max_textures < 2 || max_stages < 2)
3241  passes = 3;
3242 
3243  else if (max_textures < 3 || max_stages < 3 || !multiply_add)
3244  passes = 2;
3245  }
3246  }
3247 
3248  return passes;
3249 }
3250 
3251 bool
3252 VideoDX9::SetupPass(int pass)
3253 {
3254  if (pass < 0 || pass >= passes)
3255  return false;
3256 
3257  if (pass == 0) {
3258  D3DMATERIAL9 d3dmtl;
3259  IDirect3DTexture9* texture_0 = 0;
3260  IDirect3DTexture9* texture_1 = 0;
3261  IDirect3DTexture9* texture_2 = 0;
3262  Bitmap* tex_bmp_0 = 0;
3263  Bitmap* tex_bmp_1 = 0;
3264  Bitmap* tex_bmp_2 = 0;
3265  ColorValue orig_spec = segment_material->Ks;
3266  HRESULT hr = E_FAIL;
3267 
3268  if (segment_material->tex_specular && passes > 1)
3269  segment_material->Ks = Color::Black;
3270 
3271  CreateD3DMaterial(d3dmtl, *segment_material);
3272  segment_material->Ks = orig_spec;
3273 
3274  hr = d3ddevice->SetMaterial(&d3dmtl);
3275 
3276  if (strategy == DX9_STRATEGY_SIMPLE) {
3277  tex_bmp_0 = segment_material->tex_diffuse;
3278  }
3279 
3280  else if (strategy == DX9_STRATEGY_BLEND) {
3281  tex_bmp_0 = segment_material->tex_diffuse;
3282  tex_bmp_1 = segment_material->tex_alternate;
3283  }
3284 
3285  else if (strategy == DX9_STRATEGY_BLEND_DETAIL) {
3286  tex_bmp_0 = segment_material->tex_diffuse;
3287  tex_bmp_1 = segment_material->tex_alternate;
3288  tex_bmp_2 = segment_material->tex_detail;
3289  }
3290 
3291  else if (strategy == DX9_STRATEGY_SPECMAP) {
3292  if (passes == 1) {
3293  tex_bmp_0 = segment_material->tex_diffuse;
3294  tex_bmp_1 = segment_material->tex_specular;
3295  }
3296  else {
3297  tex_bmp_0 = segment_material->tex_diffuse;
3298  }
3299  }
3300 
3301  else if (strategy == DX9_STRATEGY_EMISSIVE && passes == 1 ||
3302  strategy == DX9_STRATEGY_SPEC_EMISSIVE && passes == 2) {
3303  if (segment_material->tex_diffuse) {
3304  tex_bmp_0 = segment_material->tex_diffuse;
3305  tex_bmp_1 = segment_material->tex_emissive;
3306  }
3307  else {
3308  tex_bmp_0 = segment_material->tex_emissive;
3309  }
3310  }
3311 
3312  else {
3313  tex_bmp_0 = segment_material->tex_emissive;
3314  }
3315 
3316  if (texcache && tex_bmp_0) {
3317  texture_0 = texcache->FindTexture(tex_bmp_0);
3318 
3319  hr = d3ddevice->SetTexture(0, texture_0);
3320  current_texture = texture_0;
3321 
3322  if (tex_bmp_1) {
3323  texture_1 = texcache->FindTexture(tex_bmp_1);
3324  hr = d3ddevice->SetTexture(1, texture_1);
3325 
3326  if (tex_bmp_2) {
3327  texture_2 = texcache->FindTexture(tex_bmp_2);
3328  hr = d3ddevice->SetTexture(2, texture_2);
3329  }
3330  }
3331  }
3332  else {
3333  hr = d3ddevice->SetTexture(0, 0);
3334  current_texture = 0;
3335  }
3336 
3337  SetBlendType(segment_material->blend);
3338 
3339  if (texture_0 && texture_1 && strategy == DX9_STRATEGY_BLEND) {
3340  d3ddevice->SetSamplerState(1, D3DSAMP_MAGFILTER, D3DTEXF_LINEAR);
3341  d3ddevice->SetSamplerState(1, D3DSAMP_MINFILTER, D3DTEXF_LINEAR);
3342  d3ddevice->SetSamplerState(1, D3DSAMP_MIPFILTER, D3DTEXF_LINEAR);
3343 
3344  d3ddevice->SetTextureStageState(0, D3DTSS_COLOROP, D3DTOP_SELECTARG1);
3345  d3ddevice->SetTextureStageState(0, D3DTSS_COLORARG1, D3DTA_TEXTURE);
3346  d3ddevice->SetTextureStageState(0, D3DTSS_ALPHAOP, D3DTOP_SELECTARG1);
3347  d3ddevice->SetTextureStageState(0, D3DTSS_ALPHAARG1, D3DTA_DIFFUSE);
3348 
3349  d3ddevice->SetTextureStageState(1, D3DTSS_COLOROP, D3DTOP_BLENDCURRENTALPHA);
3350  d3ddevice->SetTextureStageState(1, D3DTSS_COLORARG1, D3DTA_CURRENT);
3351  d3ddevice->SetTextureStageState(1, D3DTSS_COLORARG2, D3DTA_TEXTURE);
3352  d3ddevice->SetTextureStageState(1, D3DTSS_ALPHAOP, D3DTOP_SELECTARG1);
3353  d3ddevice->SetTextureStageState(1, D3DTSS_ALPHAARG1, D3DTA_CURRENT);
3354  d3ddevice->SetTextureStageState(1, D3DTSS_TEXCOORDINDEX, 0);
3355 
3356  d3ddevice->SetTextureStageState(2, D3DTSS_COLOROP, D3DTOP_MODULATE);
3357  d3ddevice->SetTextureStageState(2, D3DTSS_COLORARG1, D3DTA_CURRENT);
3358  d3ddevice->SetTextureStageState(2, D3DTSS_COLORARG2, D3DTA_DIFFUSE);
3359  d3ddevice->SetTextureStageState(2, D3DTSS_ALPHAOP, D3DTOP_SELECTARG1);
3360  d3ddevice->SetTextureStageState(2, D3DTSS_ALPHAARG1, D3DTA_CURRENT);
3361 
3362  d3ddevice->SetTextureStageState(3, D3DTSS_COLOROP, D3DTOP_DISABLE);
3363  }
3364 
3365  else if (texture_0 && texture_1 && texture_2 && strategy == DX9_STRATEGY_BLEND_DETAIL) {
3366  d3ddevice->SetSamplerState(1, D3DSAMP_MAGFILTER, D3DTEXF_LINEAR);
3367  d3ddevice->SetSamplerState(1, D3DSAMP_MINFILTER, D3DTEXF_LINEAR);
3368  d3ddevice->SetSamplerState(1, D3DSAMP_MIPFILTER, D3DTEXF_LINEAR);
3369  d3ddevice->SetSamplerState(2, D3DSAMP_MAGFILTER, D3DTEXF_LINEAR);
3370  d3ddevice->SetSamplerState(2, D3DSAMP_MINFILTER, D3DTEXF_LINEAR);
3371  d3ddevice->SetSamplerState(2, D3DSAMP_MIPFILTER, D3DTEXF_LINEAR);
3372 
3373  d3ddevice->SetTextureStageState(0, D3DTSS_COLOROP, D3DTOP_SELECTARG1);
3374  d3ddevice->SetTextureStageState(0, D3DTSS_COLORARG1, D3DTA_TEXTURE);
3375  d3ddevice->SetTextureStageState(0, D3DTSS_ALPHAOP, D3DTOP_SELECTARG1);
3376  d3ddevice->SetTextureStageState(0, D3DTSS_ALPHAARG1, D3DTA_DIFFUSE);
3377  d3ddevice->SetTextureStageState(0, D3DTSS_TEXCOORDINDEX, 0);
3378 
3379  d3ddevice->SetTextureStageState(1, D3DTSS_COLOROP, D3DTOP_BLENDCURRENTALPHA);
3380  d3ddevice->SetTextureStageState(1, D3DTSS_COLORARG1, D3DTA_CURRENT);
3381  d3ddevice->SetTextureStageState(1, D3DTSS_COLORARG2, D3DTA_TEXTURE);
3382  d3ddevice->SetTextureStageState(1, D3DTSS_ALPHAOP, D3DTOP_SELECTARG1);
3383  d3ddevice->SetTextureStageState(1, D3DTSS_ALPHAARG1, D3DTA_CURRENT);
3384  d3ddevice->SetTextureStageState(1, D3DTSS_TEXCOORDINDEX, 0);
3385 
3386  d3ddevice->SetTextureStageState(2, D3DTSS_COLOROP, D3DTOP_BLENDTEXTUREALPHA);
3387  d3ddevice->SetTextureStageState(2, D3DTSS_COLORARG1, D3DTA_TEXTURE);
3388  d3ddevice->SetTextureStageState(2, D3DTSS_COLORARG2, D3DTA_CURRENT);
3389  d3ddevice->SetTextureStageState(2, D3DTSS_ALPHAOP, D3DTOP_SELECTARG1);
3390  d3ddevice->SetTextureStageState(2, D3DTSS_ALPHAARG1, D3DTA_CURRENT);
3391  d3ddevice->SetTextureStageState(2, D3DTSS_TEXCOORDINDEX, 1);
3392 
3393  d3ddevice->SetTextureStageState(3, D3DTSS_COLOROP, D3DTOP_MODULATE);
3394  d3ddevice->SetTextureStageState(3, D3DTSS_COLORARG1, D3DTA_CURRENT);
3395  d3ddevice->SetTextureStageState(3, D3DTSS_COLORARG2, D3DTA_DIFFUSE);
3396  d3ddevice->SetTextureStageState(3, D3DTSS_ALPHAOP, D3DTOP_SELECTARG1);
3397  d3ddevice->SetTextureStageState(3, D3DTSS_ALPHAARG1, D3DTA_CURRENT);
3398  }
3399 
3400  else if (texture_0 && strategy == DX9_STRATEGY_GLOW) {
3401  d3ddevice->SetTextureStageState(0, D3DTSS_COLOROP, D3DTOP_SELECTARG1);
3402  d3ddevice->SetTextureStageState(0, D3DTSS_COLORARG1, D3DTA_TEXTURE);
3403  d3ddevice->SetTextureStageState(0, D3DTSS_COLORARG2, D3DTA_CURRENT);
3404  d3ddevice->SetTextureStageState(0, D3DTSS_ALPHAOP, D3DTOP_SELECTARG1);
3405  d3ddevice->SetTextureStageState(0, D3DTSS_ALPHAARG1, D3DTA_TEXTURE);
3406  }
3407 
3408  else if (texture_0) {
3409  d3ddevice->SetTextureStageState(0, D3DTSS_COLOROP, D3DTOP_MODULATE);
3410  d3ddevice->SetTextureStageState(0, D3DTSS_COLORARG1, D3DTA_TEXTURE);
3411  d3ddevice->SetTextureStageState(0, D3DTSS_COLORARG2, D3DTA_CURRENT);
3412  d3ddevice->SetTextureStageState(0, D3DTSS_ALPHAOP, D3DTOP_SELECTARG1);
3413  d3ddevice->SetTextureStageState(0, D3DTSS_ALPHAARG1, D3DTA_TEXTURE);
3414  }
3415 
3416  if (texture_1 && strategy == DX9_STRATEGY_SPECMAP) {
3417  d3ddevice->SetSamplerState(1, D3DSAMP_MAGFILTER, D3DTEXF_LINEAR);
3418  d3ddevice->SetSamplerState(1, D3DSAMP_MINFILTER, D3DTEXF_LINEAR);
3419  d3ddevice->SetSamplerState(1, D3DSAMP_MIPFILTER, D3DTEXF_LINEAR);
3420 
3421  d3ddevice->SetTextureStageState(1, D3DTSS_TEXCOORDINDEX, 0);
3422  d3ddevice->SetTextureStageState(1, D3DTSS_COLOROP, D3DTOP_MULTIPLYADD);
3423  d3ddevice->SetTextureStageState(1, D3DTSS_COLORARG1, D3DTA_TEXTURE);
3424  d3ddevice->SetTextureStageState(1, D3DTSS_COLORARG2, D3DTA_SPECULAR);
3425  d3ddevice->SetTextureStageState(1, D3DTSS_ALPHAOP, D3DTOP_SELECTARG1);
3426  d3ddevice->SetTextureStageState(1, D3DTSS_ALPHAARG1, D3DTA_TEXTURE);
3427  }
3428 
3429  else if (texture_1 && strategy == DX9_STRATEGY_EMISSIVE) {
3430  d3ddevice->SetSamplerState(1, D3DSAMP_MAGFILTER, D3DTEXF_LINEAR);
3431  d3ddevice->SetSamplerState(1, D3DSAMP_MINFILTER, D3DTEXF_LINEAR);
3432  d3ddevice->SetSamplerState(1, D3DSAMP_MIPFILTER, D3DTEXF_LINEAR);
3433 
3434  d3ddevice->SetTextureStageState(1, D3DTSS_TEXCOORDINDEX, 0);
3435  d3ddevice->SetTextureStageState(1, D3DTSS_COLOROP, D3DTOP_ADD);
3436  d3ddevice->SetTextureStageState(1, D3DTSS_COLORARG1, D3DTA_TEXTURE);
3437  d3ddevice->SetTextureStageState(1, D3DTSS_COLORARG2, D3DTA_CURRENT);
3438  d3ddevice->SetTextureStageState(1, D3DTSS_ALPHAOP, D3DTOP_SELECTARG1);
3439  d3ddevice->SetTextureStageState(1, D3DTSS_ALPHAARG1, D3DTA_TEXTURE);
3440  }
3441 
3442  else if (texture_1 && strategy == DX9_STRATEGY_SPEC_EMISSIVE) {
3443  d3ddevice->SetSamplerState(1, D3DSAMP_MAGFILTER, D3DTEXF_LINEAR);
3444  d3ddevice->SetSamplerState(1, D3DSAMP_MINFILTER, D3DTEXF_LINEAR);
3445  d3ddevice->SetSamplerState(1, D3DSAMP_MIPFILTER, D3DTEXF_LINEAR);
3446 
3447  d3ddevice->SetTextureStageState(1, D3DTSS_TEXCOORDINDEX, 0);
3448  d3ddevice->SetTextureStageState(1, D3DTSS_COLOROP, D3DTOP_ADD);
3449  d3ddevice->SetTextureStageState(1, D3DTSS_COLORARG1, D3DTA_TEXTURE);
3450  d3ddevice->SetTextureStageState(1, D3DTSS_COLORARG2, D3DTA_CURRENT);
3451  d3ddevice->SetTextureStageState(1, D3DTSS_ALPHAOP, D3DTOP_SELECTARG1);
3452  d3ddevice->SetTextureStageState(1, D3DTSS_ALPHAARG1, D3DTA_TEXTURE);
3453  }
3454 
3455  else if (strategy < DX9_STRATEGY_BLEND) {
3456  d3ddevice->SetTextureStageState(1, D3DTSS_COLOROP, D3DTOP_DISABLE);
3457  }
3458  }
3459 
3460  else if (pass == 1) {
3461  D3DMATERIAL9 d3dmtl;
3462  IDirect3DTexture9* texture = 0;
3463  Bitmap* tex_bmp = 0;
3464  ColorValue orig_Ka = segment_material->Ka;
3465  ColorValue orig_Kd = segment_material->Kd;
3466  HRESULT hr = E_FAIL;
3467 
3468  if (segment_material->tex_specular) {
3469  segment_material->Ka = Color::Black;
3470  segment_material->Kd = Color::Black;
3471  }
3472 
3473  CreateD3DMaterial(d3dmtl, *segment_material);
3474 
3475  segment_material->Ka = orig_Ka;
3476  segment_material->Kd = orig_Kd;
3477 
3478  hr = d3ddevice->SetMaterial(&d3dmtl);
3479 
3480  if (strategy == DX9_STRATEGY_SPECMAP ||
3481  strategy == DX9_STRATEGY_SPEC_EMISSIVE) {
3482  tex_bmp = segment_material->tex_specular;
3483  d3ddevice->SetTextureStageState(0, D3DTSS_COLOROP, D3DTOP_MODULATE);
3484  d3ddevice->SetTextureStageState(0, D3DTSS_COLORARG1, D3DTA_TEXTURE);
3485  d3ddevice->SetTextureStageState(0, D3DTSS_COLORARG2, D3DTA_SPECULAR);
3486  }
3487 
3488  else if (strategy == DX9_STRATEGY_EMISSIVE) {
3489  tex_bmp = segment_material->tex_emissive;
3490  d3ddevice->SetTextureStageState(0, D3DTSS_COLOROP, D3DTOP_SELECTARG1);
3491  d3ddevice->SetTextureStageState(0, D3DTSS_COLORARG1, D3DTA_TEXTURE);
3492  }
3493 
3494  d3ddevice->SetTextureStageState(1, D3DTSS_COLOROP, D3DTOP_DISABLE);
3495 
3496  if (texcache && tex_bmp) {
3497  texture = texcache->FindTexture(tex_bmp);
3498  hr = d3ddevice->SetTexture(0, texture);
3499  current_texture = texture;
3500 
3502  }
3503  }
3504 
3505  if (render_state[STENCIL_ENABLE]) {
3506  d3ddevice->SetRenderState(D3DRS_STENCILENABLE, TRUE);
3507  d3ddevice->SetRenderState(D3DRS_STENCILREF, 0x01);
3508  d3ddevice->SetRenderState(D3DRS_STENCILFUNC, D3DCMP_GREATER);
3509  }
3510  else {
3511  d3ddevice->SetRenderState(D3DRS_STENCILENABLE, FALSE);
3512  }
3513 
3514  if (render_state[LIGHTING_PASS] > 0) {
3517  }
3518 
3519  return true;
3520 }
3521 
3522 
3523 // +--------------------------------------------------------------------+
3524 
3525 void
3526 VideoDX9Error(const char* msg, HRESULT err)
3527 {
3528  Print(" VideoDX9: %s. [%s]\n", msg, D3DErrStr(err));
3529 }
3530 
3531 char* D3DErrStr(HRESULT hr)
3532 {
3533  static char errstrbuf[128];
3534 
3535  switch (hr) {
3536  default:
3537  sprintf_s(errstrbuf, "Unrecognized error value = %08x.", hr);
3538  return errstrbuf;
3539 
3540  case D3D_OK:
3541  return "No error.";
3542 
3543  case D3DERR_WRONGTEXTUREFORMAT:
3544  return "Wrong texture format.";
3545 
3546  case D3DERR_UNSUPPORTEDCOLOROPERATION:
3547  return "Unsupported color operation.";
3548 
3549  case D3DERR_UNSUPPORTEDCOLORARG:
3550  return "Unsupported color argument.";
3551 
3552  case D3DERR_UNSUPPORTEDALPHAOPERATION:
3553  return "Unsupported alpha operation.";
3554 
3555  case D3DERR_UNSUPPORTEDALPHAARG:
3556  return "Unsupported alpha argument.";
3557 
3558  case D3DERR_TOOMANYOPERATIONS:
3559  return "Too many operations.";
3560 
3561  case D3DERR_CONFLICTINGTEXTUREFILTER:
3562  return "Conflicting texture filter.";
3563 
3564  case D3DERR_UNSUPPORTEDFACTORVALUE:
3565  return "Unsupported factor value.";
3566 
3567  case D3DERR_CONFLICTINGRENDERSTATE:
3568  return "Conflicting render state.";
3569 
3570  case D3DERR_UNSUPPORTEDTEXTUREFILTER:
3571  return "Unsupported texture filter.";
3572 
3573  case D3DERR_CONFLICTINGTEXTUREPALETTE:
3574  return "Conflicting texture palette.";
3575 
3576  case D3DERR_DRIVERINTERNALERROR:
3577  return "Driver internal error.";
3578 
3579 
3580  case D3DERR_NOTFOUND:
3581  return "Resource was not found.";
3582 
3583  case D3DERR_MOREDATA:
3584  return "More data?";
3585 
3586  case D3DERR_DEVICELOST:
3587  return "Device lost.";
3588 
3589  case D3DERR_DEVICENOTRESET:
3590  return "Device is not reset.";
3591 
3592  case D3DERR_NOTAVAILABLE:
3593  return "Not available.";
3594 
3595  case D3DERR_OUTOFVIDEOMEMORY:
3596  return "Out of video memory.";
3597 
3598  case E_OUTOFMEMORY:
3599  return "Out of system memory.";
3600 
3601  case D3DERR_INVALIDDEVICE:
3602  return "Invalid device selection.";
3603 
3604  case D3DERR_INVALIDCALL:
3605  return "Invalid call or parameter.";
3606 
3607  case D3DERR_DRIVERINVALIDCALL:
3608  return "Driver invalid call.";
3609 
3610  case D3DERR_WASSTILLDRAWING:
3611  return "The device was still drawing.";
3612 
3613  case D3DOK_NOAUTOGEN:
3614  return "Autogeneration is not supported by this device.";
3615 
3616  }
3617 }
3618