d3dx9_36: Implementation of D3DXSHMultiply3.
[wine] / dlls / ddraw / viewport.c
1 /* Direct3D Viewport
2  * Copyright (c) 1998 Lionel ULMER
3  * Copyright (c) 2006-2007 Stefan DÖSINGER
4  *
5  * This file contains the implementation of Direct3DViewport2.
6  *
7  * This library is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU Lesser General Public
9  * License as published by the Free Software Foundation; either
10  * version 2.1 of the License, or (at your option) any later version.
11  *
12  * This library is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15  * Lesser General Public License for more details.
16  *
17  * You should have received a copy of the GNU Lesser General Public
18  * License along with this library; if not, write to the Free Software
19  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
20  */
21
22 #include "config.h"
23 #include "wine/port.h"
24
25 #include "ddraw_private.h"
26
27 WINE_DEFAULT_DEBUG_CHANNEL(ddraw);
28
29 /*****************************************************************************
30  * Helper functions
31  *****************************************************************************/
32
33 static void update_clip_space(struct d3d_device *device,
34         struct wined3d_vec3 *scale, struct wined3d_vec3 *offset)
35 {
36     D3DMATRIX clip_space =
37     {
38         scale->x,  0.0f,      0.0f,      0.0f,
39         0.0f,      scale->y,  0.0f,      0.0f,
40         0.0f,      0.0f,      scale->z,  0.0f,
41         offset->x, offset->y, offset->z, 1.0f,
42     };
43     D3DMATRIX projection;
44     HRESULT hr;
45
46     multiply_matrix(&projection, &clip_space, &device->legacy_projection);
47     hr = wined3d_device_set_transform(device->wined3d_device,
48             WINED3D_TS_PROJECTION, (struct wined3d_matrix *)&projection);
49     if (SUCCEEDED(hr))
50         device->legacy_clipspace = clip_space;
51 }
52
53 /*****************************************************************************
54  * viewport_activate
55  *
56  * activates the viewport using IDirect3DDevice7::SetViewport
57  *
58  *****************************************************************************/
59 void viewport_activate(struct d3d_viewport *This, BOOL ignore_lights)
60 {
61     struct wined3d_vec3 scale, offset;
62     D3DVIEWPORT7 vp;
63
64     if (!ignore_lights)
65     {
66         struct d3d_light *light;
67
68         /* Activate all the lights associated with this context */
69         LIST_FOR_EACH_ENTRY(light, &This->light_list, struct d3d_light, entry)
70         {
71             light_activate(light);
72         }
73     }
74
75     /* And copy the values in the structure used by the device */
76     if (This->use_vp2)
77     {
78         vp.dwX = This->viewports.vp2.dwX;
79         vp.dwY = This->viewports.vp2.dwY;
80         vp.dwHeight = This->viewports.vp2.dwHeight;
81         vp.dwWidth = This->viewports.vp2.dwWidth;
82         vp.dvMinZ = 0.0f;
83         vp.dvMaxZ = 1.0f;
84
85         scale.x = 2.0f / This->viewports.vp2.dvClipWidth;
86         scale.y = 2.0f / This->viewports.vp2.dvClipHeight;
87         scale.z = 1.0f / (This->viewports.vp2.dvMaxZ - This->viewports.vp2.dvMinZ);
88         offset.x = -2.0f * This->viewports.vp2.dvClipX / This->viewports.vp2.dvClipWidth - 1.0f;
89         offset.y = -2.0f * This->viewports.vp2.dvClipY / This->viewports.vp2.dvClipHeight + 1.0f;
90         offset.z = -This->viewports.vp2.dvMinZ / (This->viewports.vp2.dvMaxZ - This->viewports.vp2.dvMinZ);
91     }
92     else
93     {
94         vp.dwX = This->viewports.vp1.dwX;
95         vp.dwY = This->viewports.vp1.dwY;
96         vp.dwHeight = This->viewports.vp1.dwHeight;
97         vp.dwWidth = This->viewports.vp1.dwWidth;
98         vp.dvMinZ = 0.0f;
99         vp.dvMaxZ = 1.0f;
100
101         scale.x = 2.0f * This->viewports.vp1.dvScaleX / This->viewports.vp1.dwWidth;
102         scale.y = 2.0f * This->viewports.vp1.dvScaleY / This->viewports.vp1.dwHeight;
103         scale.z = 1.0f;
104         offset.x = 0.0f;
105         offset.y = 0.0f;
106         offset.z = 0.0f;
107     }
108
109     update_clip_space(This->active_device, &scale, &offset);
110     IDirect3DDevice7_SetViewport(&This->active_device->IDirect3DDevice7_iface, &vp);
111 }
112
113 /*****************************************************************************
114  * _dump_D3DVIEWPORT, _dump_D3DVIEWPORT2
115  *
116  * Writes viewport information to TRACE
117  *
118  *****************************************************************************/
119 static void _dump_D3DVIEWPORT(const D3DVIEWPORT *lpvp)
120 {
121     TRACE("    - dwSize = %d   dwX = %d   dwY = %d\n",
122             lpvp->dwSize, lpvp->dwX, lpvp->dwY);
123     TRACE("    - dwWidth = %d   dwHeight = %d\n",
124             lpvp->dwWidth, lpvp->dwHeight);
125     TRACE("    - dvScaleX = %f   dvScaleY = %f\n",
126             lpvp->dvScaleX, lpvp->dvScaleY);
127     TRACE("    - dvMaxX = %f   dvMaxY = %f\n",
128             lpvp->dvMaxX, lpvp->dvMaxY);
129     TRACE("    - dvMinZ = %f   dvMaxZ = %f\n",
130             lpvp->dvMinZ, lpvp->dvMaxZ);
131 }
132
133 static void _dump_D3DVIEWPORT2(const D3DVIEWPORT2 *lpvp)
134 {
135     TRACE("    - dwSize = %d   dwX = %d   dwY = %d\n",
136             lpvp->dwSize, lpvp->dwX, lpvp->dwY);
137     TRACE("    - dwWidth = %d   dwHeight = %d\n",
138             lpvp->dwWidth, lpvp->dwHeight);
139     TRACE("    - dvClipX = %f   dvClipY = %f\n",
140             lpvp->dvClipX, lpvp->dvClipY);
141     TRACE("    - dvClipWidth = %f   dvClipHeight = %f\n",
142             lpvp->dvClipWidth, lpvp->dvClipHeight);
143     TRACE("    - dvMinZ = %f   dvMaxZ = %f\n",
144             lpvp->dvMinZ, lpvp->dvMaxZ);
145 }
146
147 static inline struct d3d_viewport *impl_from_IDirect3DViewport3(IDirect3DViewport3 *iface)
148 {
149     return CONTAINING_RECORD(iface, struct d3d_viewport, IDirect3DViewport3_iface);
150 }
151
152 /*****************************************************************************
153  * IUnknown Methods.
154  *****************************************************************************/
155
156 /*****************************************************************************
157  * IDirect3DViewport3::QueryInterface
158  *
159  * A normal QueryInterface. Can query all interface versions and the
160  * IUnknown interface. The VTables of the different versions
161  * are equal
162  *
163  * Params:
164  *  refiid: Interface id queried for
165  *  obj: Address to write the interface pointer to
166  *
167  * Returns:
168  *  S_OK on success.
169  *  E_NOINTERFACE if the requested interface wasn't found
170  *
171  *****************************************************************************/
172 static HRESULT WINAPI d3d_viewport_QueryInterface(IDirect3DViewport3 *iface, REFIID riid, void **object)
173 {
174     TRACE("iface %p, riid %s, object %p.\n", iface, debugstr_guid(riid), object);
175
176     if (IsEqualGUID(&IID_IDirect3DViewport3, riid)
177             || IsEqualGUID(&IID_IDirect3DViewport2, riid)
178             || IsEqualGUID(&IID_IDirect3DViewport, riid)
179             || IsEqualGUID(&IID_IUnknown, riid))
180     {
181         IDirect3DViewport3_AddRef(iface);
182         *object = iface;
183         return S_OK;
184     }
185
186     WARN("%s not implemented, returning E_NOINTERFACE.\n", debugstr_guid(riid));
187
188     *object = NULL;
189     return E_NOINTERFACE;
190 }
191
192 /*****************************************************************************
193  * IDirect3DViewport3::AddRef
194  *
195  * Increases the refcount.
196  *
197  * Returns:
198  *  The new refcount
199  *
200  *****************************************************************************/
201 static ULONG WINAPI d3d_viewport_AddRef(IDirect3DViewport3 *iface)
202 {
203     struct d3d_viewport *viewport = impl_from_IDirect3DViewport3(iface);
204     ULONG ref = InterlockedIncrement(&viewport->ref);
205
206     TRACE("%p increasing refcount to %u.\n", viewport, ref);
207
208     return ref;
209 }
210
211 /*****************************************************************************
212  * IDirect3DViewport3::Release
213  *
214  * Reduces the refcount. If it falls to 0, the interface is released
215  *
216  * Returns:
217  *  The new refcount
218  *
219  *****************************************************************************/
220 static ULONG WINAPI d3d_viewport_Release(IDirect3DViewport3 *iface)
221 {
222     struct d3d_viewport *viewport = impl_from_IDirect3DViewport3(iface);
223     ULONG ref = InterlockedDecrement(&viewport->ref);
224
225     TRACE("%p decreasing refcount to %u.\n", viewport, ref);
226
227     if (!ref)
228         HeapFree(GetProcessHeap(), 0, viewport);
229
230     return ref;
231 }
232
233 /*****************************************************************************
234  * IDirect3DViewport Methods.
235  *****************************************************************************/
236
237 /*****************************************************************************
238  * IDirect3DViewport3::Initialize
239  *
240  * No-op initialization.
241  *
242  * Params:
243  *  Direct3D: The direct3D device this viewport is assigned to
244  *
245  * Returns:
246  *  DDERR_ALREADYINITIALIZED
247  *
248  *****************************************************************************/
249 static HRESULT WINAPI d3d_viewport_Initialize(IDirect3DViewport3 *iface, IDirect3D *d3d)
250 {
251     TRACE("iface %p, d3d %p.\n", iface, d3d);
252
253     return DDERR_ALREADYINITIALIZED;
254 }
255
256 /*****************************************************************************
257  * IDirect3DViewport3::GetViewport
258  *
259  * Returns the viewport data assigned to this viewport interface
260  *
261  * Params:
262  *  Data: Address to store the data
263  *
264  * Returns:
265  *  D3D_OK on success
266  *  DDERR_INVALIDPARAMS if Data is NULL
267  *
268  *****************************************************************************/
269 static HRESULT WINAPI d3d_viewport_GetViewport(IDirect3DViewport3 *iface, D3DVIEWPORT *lpData)
270 {
271     struct d3d_viewport *This = impl_from_IDirect3DViewport3(iface);
272     DWORD dwSize;
273
274     TRACE("iface %p, data %p.\n", iface, lpData);
275
276     wined3d_mutex_lock();
277
278     dwSize = lpData->dwSize;
279     memset(lpData, 0, dwSize);
280     if (!This->use_vp2)
281         memcpy(lpData, &(This->viewports.vp1), dwSize);
282     else {
283         D3DVIEWPORT vp1;
284         vp1.dwSize = sizeof(vp1);
285         vp1.dwX = This->viewports.vp2.dwX;
286         vp1.dwY = This->viewports.vp2.dwY;
287         vp1.dwWidth = This->viewports.vp2.dwWidth;
288         vp1.dwHeight = This->viewports.vp2.dwHeight;
289         vp1.dvMaxX = 0.0;
290         vp1.dvMaxY = 0.0;
291         vp1.dvScaleX = 0.0;
292         vp1.dvScaleY = 0.0;
293         vp1.dvMinZ = This->viewports.vp2.dvMinZ;
294         vp1.dvMaxZ = This->viewports.vp2.dvMaxZ;
295         memcpy(lpData, &vp1, dwSize);
296     }
297
298     if (TRACE_ON(ddraw))
299     {
300         TRACE("  returning D3DVIEWPORT :\n");
301         _dump_D3DVIEWPORT(lpData);
302     }
303
304     wined3d_mutex_unlock();
305
306     return DD_OK;
307 }
308
309 /*****************************************************************************
310  * IDirect3DViewport3::SetViewport
311  *
312  * Sets the viewport information for this interface
313  *
314  * Params:
315  *  lpData: Viewport to set
316  *
317  * Returns:
318  *  D3D_OK on success
319  *  DDERR_INVALIDPARAMS if Data is NULL
320  *
321  *****************************************************************************/
322 static HRESULT WINAPI d3d_viewport_SetViewport(IDirect3DViewport3 *iface, D3DVIEWPORT *lpData)
323 {
324     struct d3d_viewport *This = impl_from_IDirect3DViewport3(iface);
325     IDirect3DViewport3 *current_viewport;
326
327     TRACE("iface %p, data %p.\n", iface, lpData);
328
329     if (TRACE_ON(ddraw))
330     {
331         TRACE("  getting D3DVIEWPORT :\n");
332         _dump_D3DVIEWPORT(lpData);
333     }
334
335     wined3d_mutex_lock();
336
337     This->use_vp2 = 0;
338     memset(&(This->viewports.vp1), 0, sizeof(This->viewports.vp1));
339     memcpy(&(This->viewports.vp1), lpData, lpData->dwSize);
340
341     /* Tests on two games show that these values are never used properly so override
342        them with proper ones :-)
343     */
344     This->viewports.vp1.dvMinZ = 0.0;
345     This->viewports.vp1.dvMaxZ = 1.0;
346
347     if (This->active_device) {
348         IDirect3DDevice3 *d3d_device3 = &This->active_device->IDirect3DDevice3_iface;
349         IDirect3DDevice3_GetCurrentViewport(d3d_device3, &current_viewport);
350         if (current_viewport)
351         {
352             if (current_viewport == iface) viewport_activate(This, FALSE);
353             IDirect3DViewport3_Release(current_viewport);
354         }
355     }
356
357     wined3d_mutex_unlock();
358
359     return DD_OK;
360 }
361
362 /*****************************************************************************
363  * IDirect3DViewport3::TransformVertices
364  *
365  * Transforms vertices by the transformation matrix.
366  *
367  * This function is pretty similar to IDirect3DVertexBuffer7::ProcessVertices,
368  * so it's tempting to forward it to there. However, there are some
369  * tiny differences. First, the lpOffscreen flag that is reported back,
370  * then there is the homogeneous vertex that is generated. Also there's a lack
371  * of FVFs, but still a custom stride. Last, the d3d1 - d3d3 viewport has some
372  * settings (scale) that d3d7 and wined3d do not have. All in all wrapping to
373  * ProcessVertices doesn't pay of in terms of wrapper code needed and code
374  * reused.
375  *
376  * Params:
377  *  dwVertexCount: The number of vertices to be transformed
378  *  lpData: Pointer to the vertex data
379  *  dwFlags: D3DTRANSFORM_CLIPPED or D3DTRANSFORM_UNCLIPPED
380  *  lpOffScreen: Set to the clipping plane clipping the vertex, if only one
381  *               vertex is transformed and clipping is on. 0 otherwise
382  *
383  * Returns:
384  *  D3D_OK on success
385  *  D3DERR_VIEWPORTHASNODEVICE if the viewport is not assigned to a device
386  *  DDERR_INVALIDPARAMS if no clipping flag is specified
387  *
388  *****************************************************************************/
389 static HRESULT WINAPI d3d_viewport_TransformVertices(IDirect3DViewport3 *iface,
390         DWORD dwVertexCount, D3DTRANSFORMDATA *lpData, DWORD dwFlags, DWORD *lpOffScreen)
391 {
392     struct d3d_viewport *viewport = impl_from_IDirect3DViewport3(iface);
393     D3DVIEWPORT vp = viewport->viewports.vp1;
394     D3DMATRIX view_mat, world_mat, mat;
395     float *in;
396     float *out;
397     float x, y, z, w;
398     unsigned int i;
399     D3DHVERTEX *outH;
400
401     TRACE("iface %p, vertex_count %u, vertex_data %p, flags %#x, clip_plane %p.\n",
402             iface, dwVertexCount, lpData, dwFlags, lpOffScreen);
403
404     /* Tests on windows show that Windows crashes when this occurs,
405      * so don't return the (intuitive) return value
406     if (!viewport->active_device)
407     {
408         WARN("No device active, returning D3DERR_VIEWPORTHASNODEVICE\n");
409         return D3DERR_VIEWPORTHASNODEVICE;
410     }
411      */
412
413     if(!(dwFlags & (D3DTRANSFORM_UNCLIPPED | D3DTRANSFORM_CLIPPED)))
414     {
415         WARN("No clipping flag passed, returning DDERR_INVALIDPARAMS\n");
416         return DDERR_INVALIDPARAMS;
417     }
418
419
420     wined3d_mutex_lock();
421     wined3d_device_get_transform(viewport->active_device->wined3d_device,
422             D3DTRANSFORMSTATE_VIEW, (struct wined3d_matrix *)&view_mat);
423     wined3d_device_get_transform(viewport->active_device->wined3d_device,
424             WINED3D_TS_WORLD_MATRIX(0), (struct wined3d_matrix *)&world_mat);
425     multiply_matrix(&mat, &view_mat, &world_mat);
426     multiply_matrix(&mat, &viewport->active_device->legacy_projection, &mat);
427
428     in = lpData->lpIn;
429     out = lpData->lpOut;
430     outH = lpData->lpHOut;
431     for(i = 0; i < dwVertexCount; i++)
432     {
433         x = (in[0] * mat._11) + (in[1] * mat._21) + (in[2] * mat._31) + (1.0 * mat._41);
434         y = (in[0] * mat._12) + (in[1] * mat._22) + (in[2] * mat._32) + (1.0 * mat._42);
435         z = (in[0] * mat._13) + (in[1] * mat._23) + (in[2] * mat._33) + (1.0 * mat._43);
436         w = (in[0] * mat._14) + (in[1] * mat._24) + (in[2] * mat._34) + (1.0 * mat._44);
437
438         if(dwFlags & D3DTRANSFORM_CLIPPED)
439         {
440             /* If clipping is enabled, Windows assumes that outH is
441              * a valid pointer
442              */
443             outH[i].u1.hx = x; outH[i].u2.hy = y; outH[i].u3.hz = z;
444
445             outH[i].dwFlags = 0;
446             if(x * vp.dvScaleX > ((float) vp.dwWidth * 0.5))
447                 outH[i].dwFlags |= D3DCLIP_RIGHT;
448             if(x * vp.dvScaleX <= -((float) vp.dwWidth) * 0.5)
449                 outH[i].dwFlags |= D3DCLIP_LEFT;
450             if(y * vp.dvScaleY > ((float) vp.dwHeight * 0.5))
451                 outH[i].dwFlags |= D3DCLIP_TOP;
452             if(y * vp.dvScaleY <= -((float) vp.dwHeight) * 0.5)
453                 outH[i].dwFlags |= D3DCLIP_BOTTOM;
454             if(z < 0.0)
455                 outH[i].dwFlags |= D3DCLIP_FRONT;
456             if(z > 1.0)
457                 outH[i].dwFlags |= D3DCLIP_BACK;
458
459             if(outH[i].dwFlags)
460             {
461                 /* Looks like native just drops the vertex, leaves whatever data
462                  * it has in the output buffer and goes on with the next vertex.
463                  * The exact scheme hasn't been figured out yet, but windows
464                  * definitely writes something there.
465                  */
466                 out[0] = x;
467                 out[1] = y;
468                 out[2] = z;
469                 out[3] = w;
470                 in = (float *) ((char *) in + lpData->dwInSize);
471                 out = (float *) ((char *) out + lpData->dwOutSize);
472                 continue;
473             }
474         }
475
476         w = 1 / w;
477         x *= w; y *= w; z *= w;
478
479         out[0] = vp.dwWidth / 2 + vp.dwX + x * vp.dvScaleX;
480         out[1] = vp.dwHeight / 2 + vp.dwY - y * vp.dvScaleY;
481         out[2] = z;
482         out[3] = w;
483         in = (float *) ((char *) in + lpData->dwInSize);
484         out = (float *) ((char *) out + lpData->dwOutSize);
485     }
486
487     /* According to the d3d test, the offscreen flag is set only
488      * if exactly one vertex is transformed. Its not documented,
489      * but the test shows that the lpOffscreen flag is set to the
490      * flag combination of clipping planes that clips the vertex.
491      *
492      * If clipping is requested, Windows assumes that the offscreen
493      * param is a valid pointer.
494      */
495     if(dwVertexCount == 1 && dwFlags & D3DTRANSFORM_CLIPPED)
496     {
497         *lpOffScreen = outH[0].dwFlags;
498     }
499     else if(*lpOffScreen)
500     {
501         *lpOffScreen = 0;
502     }
503     wined3d_mutex_unlock();
504
505     TRACE("All done\n");
506     return DD_OK;
507 }
508
509 /*****************************************************************************
510  * IDirect3DViewport3::LightElements
511  *
512  * The DirectX 5.0 sdk says that it's not implemented
513  *
514  * Params:
515  *  ?
516  *
517  * Returns:
518  *  DDERR_UNSUPPORTED
519  *
520  *****************************************************************************/
521 static HRESULT WINAPI d3d_viewport_LightElements(IDirect3DViewport3 *iface,
522         DWORD element_count, D3DLIGHTDATA *data)
523 {
524     TRACE("iface %p, element_count %u, data %p.\n", iface, element_count, data);
525
526     return DDERR_UNSUPPORTED;
527 }
528
529 /*****************************************************************************
530  * IDirect3DViewport3::SetBackground
531  *
532  * Sets the background material
533  *
534  * Params:
535  *  hMat: Handle from a IDirect3DMaterial interface
536  *
537  * Returns:
538  *  D3D_OK on success
539  *
540  *****************************************************************************/
541 static HRESULT WINAPI d3d_viewport_SetBackground(IDirect3DViewport3 *iface, D3DMATERIALHANDLE hMat)
542 {
543     struct d3d_viewport *viewport = impl_from_IDirect3DViewport3(iface);
544     struct d3d_material *m;
545
546     TRACE("iface %p, material %#x.\n", iface, hMat);
547
548     wined3d_mutex_lock();
549
550     if (!hMat)
551     {
552         viewport->background = NULL;
553         TRACE("Setting background to NULL\n");
554         wined3d_mutex_unlock();
555         return D3D_OK;
556     }
557
558     m = ddraw_get_object(&viewport->ddraw->d3ddevice->handle_table, hMat - 1, DDRAW_HANDLE_MATERIAL);
559     if (!m)
560     {
561         WARN("Invalid material handle.\n");
562         wined3d_mutex_unlock();
563         return DDERR_INVALIDPARAMS;
564     }
565
566     TRACE("Setting background color : %.8e %.8e %.8e %.8e.\n",
567             m->mat.u.diffuse.u1.r, m->mat.u.diffuse.u2.g,
568             m->mat.u.diffuse.u3.b, m->mat.u.diffuse.u4.a);
569     viewport->background = m;
570
571     wined3d_mutex_unlock();
572
573     return D3D_OK;
574 }
575
576 /*****************************************************************************
577  * IDirect3DViewport3::GetBackground
578  *
579  * Returns the material handle assigned to the background of the viewport
580  *
581  * Params:
582  *  lphMat: Address to store the handle
583  *  lpValid: is set to FALSE if no background is set, TRUE if one is set
584  *
585  * Returns:
586  *  D3D_OK
587  *
588  *****************************************************************************/
589 static HRESULT WINAPI d3d_viewport_GetBackground(IDirect3DViewport3 *iface,
590         D3DMATERIALHANDLE *material, BOOL *valid)
591 {
592     struct d3d_viewport *viewport = impl_from_IDirect3DViewport3(iface);
593
594     TRACE("iface %p, material %p, valid %p.\n", iface, material, valid);
595
596     wined3d_mutex_lock();
597     if (valid)
598         *valid = !!viewport->background;
599     if (material)
600         *material = viewport->background ? viewport->background->Handle : 0;
601     wined3d_mutex_unlock();
602
603     return D3D_OK;
604 }
605
606 /*****************************************************************************
607  * IDirect3DViewport3::SetBackgroundDepth
608  *
609  * Sets a surface that represents the background depth. It's contents are
610  * used to set the depth buffer in IDirect3DViewport3::Clear
611  *
612  * Params:
613  *  lpDDSurface: Surface to set
614  *
615  * Returns: D3D_OK, because it's a stub
616  *
617  *****************************************************************************/
618 static HRESULT WINAPI d3d_viewport_SetBackgroundDepth(IDirect3DViewport3 *iface, IDirectDrawSurface *surface)
619 {
620     FIXME("iface %p, surface %p stub!\n", iface, surface);
621
622     return D3D_OK;
623 }
624
625 /*****************************************************************************
626  * IDirect3DViewport3::GetBackgroundDepth
627  *
628  * Returns the surface that represents the depth field
629  *
630  * Params:
631  *  lplpDDSurface: Address to store the interface pointer
632  *  lpValid: Set to TRUE if a depth is assigned, FALSE otherwise
633  *
634  * Returns:
635  *  D3D_OK, because it's a stub
636  *  (DDERR_INVALIDPARAMS if DDSurface of Valid is NULL)
637  *
638  *****************************************************************************/
639 static HRESULT WINAPI d3d_viewport_GetBackgroundDepth(IDirect3DViewport3 *iface,
640         IDirectDrawSurface **surface, BOOL *valid)
641 {
642     FIXME("iface %p, surface %p, valid %p stub!\n", iface, surface, valid);
643
644     return DD_OK;
645 }
646
647 /*****************************************************************************
648  * IDirect3DViewport3::Clear
649  *
650  * Clears the render target and / or the z buffer
651  *
652  * Params:
653  *  dwCount: The amount of rectangles to clear. If 0, the whole buffer is
654  *           cleared
655  *  lpRects: Pointer to the array of rectangles. If NULL, Count must be 0
656  *  dwFlags: D3DCLEAR_ZBUFFER and / or D3DCLEAR_TARGET
657  *
658  * Returns:
659  *  D3D_OK on success
660  *  D3DERR_VIEWPORTHASNODEVICE if there's no active device
661  *  The return value of IDirect3DDevice7::Clear
662  *
663  *****************************************************************************/
664 static HRESULT WINAPI d3d_viewport_Clear(IDirect3DViewport3 *iface,
665         DWORD rect_count, D3DRECT *rects, DWORD flags)
666 {
667     struct d3d_viewport *This = impl_from_IDirect3DViewport3(iface);
668     DWORD color = 0x00000000;
669     HRESULT hr;
670     IDirect3DViewport3 *current_viewport;
671     IDirect3DDevice3 *d3d_device3;
672
673     TRACE("iface %p, rect_count %u, rects %p, flags %#x.\n", iface, rect_count, rects, flags);
674
675     if (This->active_device == NULL) {
676         ERR(" Trying to clear a viewport not attached to a device !\n");
677         return D3DERR_VIEWPORTHASNODEVICE;
678     }
679     d3d_device3 = &This->active_device->IDirect3DDevice3_iface;
680
681     wined3d_mutex_lock();
682
683     if (flags & D3DCLEAR_TARGET)
684     {
685         if (This->background == NULL) {
686             ERR(" Trying to clear the color buffer without background material !\n");
687         }
688         else
689         {
690             color = ((int)((This->background->mat.u.diffuse.u1.r) * 255) << 16)
691                     | ((int) ((This->background->mat.u.diffuse.u2.g) * 255) <<  8)
692                     | ((int) ((This->background->mat.u.diffuse.u3.b) * 255) <<  0)
693                     | ((int) ((This->background->mat.u.diffuse.u4.a) * 255) << 24);
694         }
695     }
696
697     /* Need to temporarily activate viewport to clear it. Previously active one will be restored
698         afterwards. */
699     viewport_activate(This, TRUE);
700
701     hr = IDirect3DDevice7_Clear(&This->active_device->IDirect3DDevice7_iface, rect_count, rects,
702             flags & (D3DCLEAR_ZBUFFER | D3DCLEAR_TARGET), color, 1.0, 0x00000000);
703
704     IDirect3DDevice3_GetCurrentViewport(d3d_device3, &current_viewport);
705     if (current_viewport)
706     {
707         struct d3d_viewport *vp = impl_from_IDirect3DViewport3(current_viewport);
708         viewport_activate(vp, TRUE);
709         IDirect3DViewport3_Release(current_viewport);
710     }
711
712     wined3d_mutex_unlock();
713
714     return hr;
715 }
716
717 /*****************************************************************************
718  * IDirect3DViewport3::AddLight
719  *
720  * Adds an light to the viewport
721  *
722  * Params:
723  *  lpDirect3DLight: Interface of the light to add
724  *
725  * Returns:
726  *  D3D_OK on success
727  *  DDERR_INVALIDPARAMS if Direct3DLight is NULL
728  *  DDERR_INVALIDPARAMS if there are 8 lights or more
729  *
730  *****************************************************************************/
731 static HRESULT WINAPI d3d_viewport_AddLight(IDirect3DViewport3 *iface, IDirect3DLight *lpDirect3DLight)
732 {
733     struct d3d_viewport *This = impl_from_IDirect3DViewport3(iface);
734     struct d3d_light *light_impl = unsafe_impl_from_IDirect3DLight(lpDirect3DLight);
735     DWORD i = 0;
736     DWORD map = This->map_lights;
737
738     TRACE("iface %p, light %p.\n", iface, lpDirect3DLight);
739
740     wined3d_mutex_lock();
741
742     if (This->num_lights >= 8)
743     {
744         wined3d_mutex_unlock();
745         return DDERR_INVALIDPARAMS;
746     }
747
748     /* Find a light number and update both light and viewports objects accordingly */
749     while (map & 1)
750     {
751         map >>= 1;
752         ++i;
753     }
754     light_impl->dwLightIndex = i;
755     This->num_lights++;
756     This->map_lights |= 1<<i;
757
758     /* Add the light in the 'linked' chain */
759     list_add_head(&This->light_list, &light_impl->entry);
760     IDirect3DLight_AddRef(lpDirect3DLight);
761
762     /* Attach the light to the viewport */
763     light_impl->active_viewport = This;
764
765     /* If active, activate the light */
766     if (This->active_device)
767         light_activate(light_impl);
768
769     wined3d_mutex_unlock();
770
771     return D3D_OK;
772 }
773
774 /*****************************************************************************
775  * IDirect3DViewport3::DeleteLight
776  *
777  * Deletes a light from the viewports' light list
778  *
779  * Params:
780  *  lpDirect3DLight: Light to delete
781  *
782  * Returns:
783  *  D3D_OK on success
784  *  DDERR_INVALIDPARAMS if the light wasn't found
785  *
786  *****************************************************************************/
787 static HRESULT WINAPI d3d_viewport_DeleteLight(IDirect3DViewport3 *iface, IDirect3DLight *lpDirect3DLight)
788 {
789     struct d3d_viewport *viewport = impl_from_IDirect3DViewport3(iface);
790     struct d3d_light *l = unsafe_impl_from_IDirect3DLight(lpDirect3DLight);
791
792     TRACE("iface %p, light %p.\n", iface, lpDirect3DLight);
793
794     wined3d_mutex_lock();
795
796     if (l->active_viewport != viewport)
797     {
798         WARN("Light %p active viewport is %p.\n", l, l->active_viewport);
799         wined3d_mutex_unlock();
800         return DDERR_INVALIDPARAMS;
801     }
802
803     light_deactivate(l);
804     list_remove(&l->entry);
805     l->active_viewport = NULL;
806     IDirect3DLight_Release(lpDirect3DLight);
807     --viewport->num_lights;
808     viewport->map_lights &= ~(1 << l->dwLightIndex);
809
810     wined3d_mutex_unlock();
811
812     return D3D_OK;
813 }
814
815 /*****************************************************************************
816  * IDirect3DViewport::NextLight
817  *
818  * Enumerates the lights associated with the viewport
819  *
820  * Params:
821  *  lpDirect3DLight: Light to start with
822  *  lplpDirect3DLight: Address to store the successor to
823  *
824  * Returns:
825  *  D3D_OK, because it's a stub
826  *
827  *****************************************************************************/
828 static HRESULT WINAPI d3d_viewport_NextLight(IDirect3DViewport3 *iface,
829         IDirect3DLight *lpDirect3DLight, IDirect3DLight **lplpDirect3DLight, DWORD flags)
830 {
831     struct d3d_viewport *viewport = impl_from_IDirect3DViewport3(iface);
832     struct d3d_light *l = unsafe_impl_from_IDirect3DLight(lpDirect3DLight);
833     struct list *entry;
834     HRESULT hr;
835
836     TRACE("iface %p, light %p, next_light %p, flags %#x.\n",
837             iface, lpDirect3DLight, lplpDirect3DLight, flags);
838
839     if (!lplpDirect3DLight)
840         return DDERR_INVALIDPARAMS;
841
842     wined3d_mutex_lock();
843
844     switch (flags)
845     {
846         case D3DNEXT_NEXT:
847             if (!l || l->active_viewport != viewport)
848             {
849                 if (l)
850                     WARN("Light %p active viewport is %p.\n", l, l->active_viewport);
851                 entry = NULL;
852             }
853             else
854                 entry = list_next(&viewport->light_list, &l->entry);
855             break;
856
857         case D3DNEXT_HEAD:
858             entry = list_head(&viewport->light_list);
859             break;
860
861         case D3DNEXT_TAIL:
862             entry = list_tail(&viewport->light_list);
863             break;
864
865         default:
866             entry = NULL;
867             WARN("Invalid flags %#x.\n", flags);
868             break;
869     }
870
871     if (entry)
872     {
873         *lplpDirect3DLight = (IDirect3DLight *)LIST_ENTRY(entry, struct d3d_light, entry);
874         IDirect3DLight_AddRef(*lplpDirect3DLight);
875         hr = D3D_OK;
876     }
877     else
878     {
879         *lplpDirect3DLight = NULL;
880         hr = DDERR_INVALIDPARAMS;
881     }
882
883     wined3d_mutex_unlock();
884
885     return hr;
886 }
887
888 /*****************************************************************************
889  * IDirect3DViewport2 Methods.
890  *****************************************************************************/
891
892 /*****************************************************************************
893  * IDirect3DViewport3::GetViewport2
894  *
895  * Returns the currently set viewport in a D3DVIEWPORT2 structure.
896  * Similar to IDirect3DViewport3::GetViewport
897  *
898  * Params:
899  *  lpData: Pointer to the structure to fill
900  *
901  * Returns:
902  *  D3D_OK on success
903  *  DDERR_INVALIDPARAMS if the viewport was set with
904  *                      IDirect3DViewport3::SetViewport
905  *  DDERR_INVALIDPARAMS if Data is NULL
906  *
907  *****************************************************************************/
908 static HRESULT WINAPI d3d_viewport_GetViewport2(IDirect3DViewport3 *iface, D3DVIEWPORT2 *lpData)
909 {
910     struct d3d_viewport *This = impl_from_IDirect3DViewport3(iface);
911     DWORD dwSize;
912
913     TRACE("iface %p, data %p.\n", iface, lpData);
914
915     wined3d_mutex_lock();
916     dwSize = lpData->dwSize;
917     memset(lpData, 0, dwSize);
918     if (This->use_vp2)
919         memcpy(lpData, &(This->viewports.vp2), dwSize);
920     else {
921         D3DVIEWPORT2 vp2;
922         vp2.dwSize = sizeof(vp2);
923         vp2.dwX = This->viewports.vp1.dwX;
924         vp2.dwY = This->viewports.vp1.dwY;
925         vp2.dwWidth = This->viewports.vp1.dwWidth;
926         vp2.dwHeight = This->viewports.vp1.dwHeight;
927         vp2.dvClipX = 0.0;
928         vp2.dvClipY = 0.0;
929         vp2.dvClipWidth = 0.0;
930         vp2.dvClipHeight = 0.0;
931         vp2.dvMinZ = This->viewports.vp1.dvMinZ;
932         vp2.dvMaxZ = This->viewports.vp1.dvMaxZ;
933         memcpy(lpData, &vp2, dwSize);
934     }
935
936     if (TRACE_ON(ddraw))
937     {
938         TRACE("  returning D3DVIEWPORT2 :\n");
939         _dump_D3DVIEWPORT2(lpData);
940     }
941
942     wined3d_mutex_unlock();
943
944     return D3D_OK;
945 }
946
947 /*****************************************************************************
948  * IDirect3DViewport3::SetViewport2
949  *
950  * Sets the viewport from a D3DVIEWPORT2 structure
951  *
952  * Params:
953  *  lpData: Viewport to set
954  *
955  * Returns:
956  *  D3D_OK on success
957  *
958  *****************************************************************************/
959 static HRESULT WINAPI d3d_viewport_SetViewport2(IDirect3DViewport3 *iface, D3DVIEWPORT2 *lpData)
960 {
961     struct d3d_viewport *This = impl_from_IDirect3DViewport3(iface);
962     IDirect3DViewport3 *current_viewport;
963
964     TRACE("iface %p, data %p.\n", iface, lpData);
965
966     if (TRACE_ON(ddraw))
967     {
968         TRACE("  getting D3DVIEWPORT2 :\n");
969         _dump_D3DVIEWPORT2(lpData);
970     }
971
972     wined3d_mutex_lock();
973
974     This->use_vp2 = 1;
975     memset(&(This->viewports.vp2), 0, sizeof(This->viewports.vp2));
976     memcpy(&(This->viewports.vp2), lpData, lpData->dwSize);
977
978     if (This->active_device) {
979         IDirect3DDevice3 *d3d_device3 = &This->active_device->IDirect3DDevice3_iface;
980         IDirect3DDevice3_GetCurrentViewport(d3d_device3, &current_viewport);
981         if (current_viewport)
982         {
983             if (current_viewport == iface) viewport_activate(This, FALSE);
984             IDirect3DViewport3_Release(current_viewport);
985         }
986     }
987
988     wined3d_mutex_unlock();
989
990     return D3D_OK;
991 }
992
993 /*****************************************************************************
994  * IDirect3DViewport3 Methods.
995  *****************************************************************************/
996
997 /*****************************************************************************
998  * IDirect3DViewport3::SetBackgroundDepth2
999  *
1000  * Sets a IDirectDrawSurface4 surface as the background depth surface
1001  *
1002  * Params:
1003  *  lpDDS: Surface to set
1004  *
1005  * Returns:
1006  *  D3D_OK, because it's stub
1007  *
1008  *****************************************************************************/
1009 static HRESULT WINAPI d3d_viewport_SetBackgroundDepth2(IDirect3DViewport3 *iface,
1010         IDirectDrawSurface4 *surface)
1011 {
1012     FIXME("iface %p, surface %p stub!\n", iface, surface);
1013
1014     return D3D_OK;
1015 }
1016
1017 /*****************************************************************************
1018  * IDirect3DViewport3::GetBackgroundDepth2
1019  *
1020  * Returns the IDirect3DSurface4 interface to the background depth surface
1021  *
1022  * Params:
1023  *  lplpDDS: Address to store the interface pointer at
1024  *  lpValid: Set to true if a surface is assigned
1025  *
1026  * Returns:
1027  *  D3D_OK because it's a stub
1028  *
1029  *****************************************************************************/
1030 static HRESULT WINAPI d3d_viewport_GetBackgroundDepth2(IDirect3DViewport3 *iface,
1031         IDirectDrawSurface4 **surface, BOOL *valid)
1032 {
1033     FIXME("iface %p, surface %p, valid %p stub!\n", iface, surface, valid);
1034
1035     return D3D_OK;
1036 }
1037
1038 /*****************************************************************************
1039  * IDirect3DViewport3::Clear2
1040  *
1041  * Another clearing method
1042  *
1043  * Params:
1044  *  Count: Number of rectangles to clear
1045  *  Rects: Rectangle array to clear
1046  *  Flags: Some flags :)
1047  *  Color: Color to fill the render target with
1048  *  Z: Value to fill the depth buffer with
1049  *  Stencil: Value to fill the stencil bits with
1050  *
1051  * Returns:
1052  *
1053  *****************************************************************************/
1054 static HRESULT WINAPI d3d_viewport_Clear2(IDirect3DViewport3 *iface, DWORD rect_count,
1055         D3DRECT *rects, DWORD flags, DWORD color, D3DVALUE depth, DWORD stencil)
1056 {
1057     struct d3d_viewport *viewport = impl_from_IDirect3DViewport3(iface);
1058     HRESULT hr;
1059     IDirect3DViewport3 *current_viewport;
1060     IDirect3DDevice3 *d3d_device3;
1061
1062     TRACE("iface %p, rect_count %u, rects %p, flags %#x, color 0x%08x, depth %.8e, stencil %u.\n",
1063             iface, rect_count, rects, flags, color, depth, stencil);
1064
1065     wined3d_mutex_lock();
1066
1067     if (!viewport->active_device)
1068     {
1069         WARN("Trying to clear a viewport not attached to a device.\n");
1070         wined3d_mutex_unlock();
1071         return D3DERR_VIEWPORTHASNODEVICE;
1072     }
1073     d3d_device3 = &viewport->active_device->IDirect3DDevice3_iface;
1074     /* Need to temporarily activate viewport to clear it. Previously active
1075      * one will be restored afterwards. */
1076     viewport_activate(viewport, TRUE);
1077
1078     hr = IDirect3DDevice7_Clear(&viewport->active_device->IDirect3DDevice7_iface,
1079             rect_count, rects, flags, color, depth, stencil);
1080     IDirect3DDevice3_GetCurrentViewport(d3d_device3, &current_viewport);
1081     if (current_viewport)
1082     {
1083         struct d3d_viewport *vp = impl_from_IDirect3DViewport3(current_viewport);
1084         viewport_activate(vp, TRUE);
1085         IDirect3DViewport3_Release(current_viewport);
1086     }
1087
1088     wined3d_mutex_unlock();
1089
1090     return hr;
1091 }
1092
1093 /*****************************************************************************
1094  * The VTable
1095  *****************************************************************************/
1096
1097 static const struct IDirect3DViewport3Vtbl d3d_viewport_vtbl =
1098 {
1099     /*** IUnknown Methods ***/
1100     d3d_viewport_QueryInterface,
1101     d3d_viewport_AddRef,
1102     d3d_viewport_Release,
1103     /*** IDirect3DViewport Methods */
1104     d3d_viewport_Initialize,
1105     d3d_viewport_GetViewport,
1106     d3d_viewport_SetViewport,
1107     d3d_viewport_TransformVertices,
1108     d3d_viewport_LightElements,
1109     d3d_viewport_SetBackground,
1110     d3d_viewport_GetBackground,
1111     d3d_viewport_SetBackgroundDepth,
1112     d3d_viewport_GetBackgroundDepth,
1113     d3d_viewport_Clear,
1114     d3d_viewport_AddLight,
1115     d3d_viewport_DeleteLight,
1116     d3d_viewport_NextLight,
1117     /*** IDirect3DViewport2 Methods ***/
1118     d3d_viewport_GetViewport2,
1119     d3d_viewport_SetViewport2,
1120     /*** IDirect3DViewport3 Methods ***/
1121     d3d_viewport_SetBackgroundDepth2,
1122     d3d_viewport_GetBackgroundDepth2,
1123     d3d_viewport_Clear2,
1124 };
1125
1126 struct d3d_viewport *unsafe_impl_from_IDirect3DViewport3(IDirect3DViewport3 *iface)
1127 {
1128     if (!iface) return NULL;
1129     assert(iface->lpVtbl == &d3d_viewport_vtbl);
1130     return CONTAINING_RECORD(iface, struct d3d_viewport, IDirect3DViewport3_iface);
1131 }
1132
1133 struct d3d_viewport *unsafe_impl_from_IDirect3DViewport2(IDirect3DViewport2 *iface)
1134 {
1135     /* IDirect3DViewport and IDirect3DViewport3 use the same iface. */
1136     if (!iface) return NULL;
1137     assert(iface->lpVtbl == (IDirect3DViewport2Vtbl *)&d3d_viewport_vtbl);
1138     return CONTAINING_RECORD(iface, struct d3d_viewport, IDirect3DViewport3_iface);
1139 }
1140
1141 struct d3d_viewport *unsafe_impl_from_IDirect3DViewport(IDirect3DViewport *iface)
1142 {
1143     /* IDirect3DViewport and IDirect3DViewport3 use the same iface. */
1144     if (!iface) return NULL;
1145     assert(iface->lpVtbl == (IDirect3DViewportVtbl *)&d3d_viewport_vtbl);
1146     return CONTAINING_RECORD(iface, struct d3d_viewport, IDirect3DViewport3_iface);
1147 }
1148
1149 void d3d_viewport_init(struct d3d_viewport *viewport, struct ddraw *ddraw)
1150 {
1151     viewport->IDirect3DViewport3_iface.lpVtbl = &d3d_viewport_vtbl;
1152     viewport->ref = 1;
1153     viewport->ddraw = ddraw;
1154     viewport->use_vp2 = 0xff;
1155     list_init(&viewport->light_list);
1156 }