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