ddraw/tests: Add some clipper tests.
[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(IDirect3DDeviceImpl *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             WINED3DTS_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(IDirect3DViewportImpl *This, BOOL ignore_lights)
60 {
61     struct wined3d_vec3 scale, offset;
62     D3DVIEWPORT7 vp;
63
64     if (!ignore_lights)
65     {
66         IDirect3DLightImpl *light;
67
68         /* Activate all the lights associated with this context */
69         LIST_FOR_EACH_ENTRY(light, &This->light_list, IDirect3DLightImpl, 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 IDirect3DViewportImpl *impl_from_IDirect3DViewport3(IDirect3DViewport3 *iface)
148 {
149     return CONTAINING_RECORD(iface, IDirect3DViewportImpl, 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 IDirect3DViewportImpl_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
202 IDirect3DViewportImpl_AddRef(IDirect3DViewport3 *iface)
203 {
204     IDirect3DViewportImpl *This = impl_from_IDirect3DViewport3(iface);
205     ULONG ref = InterlockedIncrement(&This->ref);
206
207     TRACE("%p increasing refcount to %u.\n", This, ref);
208
209     return ref;
210 }
211
212 /*****************************************************************************
213  * IDirect3DViewport3::Release
214  *
215  * Reduces the refcount. If it falls to 0, the interface is released
216  *
217  * Returns:
218  *  The new refcount
219  *
220  *****************************************************************************/
221 static ULONG WINAPI
222 IDirect3DViewportImpl_Release(IDirect3DViewport3 *iface)
223 {
224     IDirect3DViewportImpl *This = impl_from_IDirect3DViewport3(iface);
225     ULONG ref = InterlockedDecrement(&This->ref);
226
227     TRACE("%p decreasing refcount to %u.\n", This, ref);
228
229     if (!ref) {
230         HeapFree(GetProcessHeap(), 0, This);
231         return 0;
232     }
233     return ref;
234 }
235
236 /*****************************************************************************
237  * IDirect3DViewport Methods.
238  *****************************************************************************/
239
240 /*****************************************************************************
241  * IDirect3DViewport3::Initialize
242  *
243  * No-op initialization.
244  *
245  * Params:
246  *  Direct3D: The direct3D device this viewport is assigned to
247  *
248  * Returns:
249  *  DDERR_ALREADYINITIALIZED
250  *
251  *****************************************************************************/
252 static HRESULT WINAPI
253 IDirect3DViewportImpl_Initialize(IDirect3DViewport3 *iface,
254                                  IDirect3D *Direct3D)
255 {
256     TRACE("iface %p, d3d %p.\n", iface, Direct3D);
257
258     return DDERR_ALREADYINITIALIZED;
259 }
260
261 /*****************************************************************************
262  * IDirect3DViewport3::GetViewport
263  *
264  * Returns the viewport data assigned to this viewport interface
265  *
266  * Params:
267  *  Data: Address to store the data
268  *
269  * Returns:
270  *  D3D_OK on success
271  *  DDERR_INVALIDPARAMS if Data is NULL
272  *
273  *****************************************************************************/
274 static HRESULT WINAPI
275 IDirect3DViewportImpl_GetViewport(IDirect3DViewport3 *iface,
276                                   D3DVIEWPORT *lpData)
277 {
278     IDirect3DViewportImpl *This = impl_from_IDirect3DViewport3(iface);
279     DWORD dwSize;
280
281     TRACE("iface %p, data %p.\n", iface, lpData);
282
283     wined3d_mutex_lock();
284
285     dwSize = lpData->dwSize;
286     memset(lpData, 0, dwSize);
287     if (!This->use_vp2)
288         memcpy(lpData, &(This->viewports.vp1), dwSize);
289     else {
290         D3DVIEWPORT vp1;
291         vp1.dwSize = sizeof(vp1);
292         vp1.dwX = This->viewports.vp2.dwX;
293         vp1.dwY = This->viewports.vp2.dwY;
294         vp1.dwWidth = This->viewports.vp2.dwWidth;
295         vp1.dwHeight = This->viewports.vp2.dwHeight;
296         vp1.dvMaxX = 0.0;
297         vp1.dvMaxY = 0.0;
298         vp1.dvScaleX = 0.0;
299         vp1.dvScaleY = 0.0;
300         vp1.dvMinZ = This->viewports.vp2.dvMinZ;
301         vp1.dvMaxZ = This->viewports.vp2.dvMaxZ;
302         memcpy(lpData, &vp1, dwSize);
303     }
304
305     if (TRACE_ON(ddraw))
306     {
307         TRACE("  returning D3DVIEWPORT :\n");
308         _dump_D3DVIEWPORT(lpData);
309     }
310
311     wined3d_mutex_unlock();
312
313     return DD_OK;
314 }
315
316 /*****************************************************************************
317  * IDirect3DViewport3::SetViewport
318  *
319  * Sets the viewport information for this interface
320  *
321  * Params:
322  *  lpData: Viewport to set
323  *
324  * Returns:
325  *  D3D_OK on success
326  *  DDERR_INVALIDPARAMS if Data is NULL
327  *
328  *****************************************************************************/
329 static HRESULT WINAPI
330 IDirect3DViewportImpl_SetViewport(IDirect3DViewport3 *iface,
331                                   D3DVIEWPORT *lpData)
332 {
333     IDirect3DViewportImpl *This = impl_from_IDirect3DViewport3(iface);
334     IDirect3DViewport3 *current_viewport;
335
336     TRACE("iface %p, data %p.\n", iface, lpData);
337
338     if (TRACE_ON(ddraw))
339     {
340         TRACE("  getting D3DVIEWPORT :\n");
341         _dump_D3DVIEWPORT(lpData);
342     }
343
344     wined3d_mutex_lock();
345
346     This->use_vp2 = 0;
347     memset(&(This->viewports.vp1), 0, sizeof(This->viewports.vp1));
348     memcpy(&(This->viewports.vp1), lpData, lpData->dwSize);
349
350     /* Tests on two games show that these values are never used properly so override
351        them with proper ones :-)
352     */
353     This->viewports.vp1.dvMinZ = 0.0;
354     This->viewports.vp1.dvMaxZ = 1.0;
355
356     if (This->active_device) {
357         IDirect3DDevice3 *d3d_device3 = &This->active_device->IDirect3DDevice3_iface;
358         IDirect3DDevice3_GetCurrentViewport(d3d_device3, &current_viewport);
359         if (current_viewport)
360         {
361             if (current_viewport == iface) viewport_activate(This, FALSE);
362             IDirect3DViewport3_Release(current_viewport);
363         }
364     }
365
366     wined3d_mutex_unlock();
367
368     return DD_OK;
369 }
370
371 /*****************************************************************************
372  * IDirect3DViewport3::TransformVertices
373  *
374  * Transforms vertices by the transformation matrix.
375  *
376  * This function is pretty similar to IDirect3DVertexBuffer7::ProcessVertices,
377  * so it's tempting to forward it to there. However, there are some
378  * tiny differences. First, the lpOffscreen flag that is reported back,
379  * then there is the homogeneous vertex that is generated. Also there's a lack
380  * of FVFs, but still a custom stride. Last, the d3d1 - d3d3 viewport has some
381  * settings (scale) that d3d7 and wined3d do not have. All in all wrapping to
382  * ProcessVertices doesn't pay of in terms of wrapper code needed and code
383  * reused.
384  *
385  * Params:
386  *  dwVertexCount: The number of vertices to be transformed
387  *  lpData: Pointer to the vertex data
388  *  dwFlags: D3DTRANSFORM_CLIPPED or D3DTRANSFORM_UNCLIPPED
389  *  lpOffScreen: Set to the clipping plane clipping the vertex, if only one
390  *               vertex is transformed and clipping is on. 0 otherwise
391  *
392  * Returns:
393  *  D3D_OK on success
394  *  D3DERR_VIEWPORTHASNODEVICE if the viewport is not assigned to a device
395  *  DDERR_INVALIDPARAMS if no clipping flag is specified
396  *
397  *****************************************************************************/
398 static HRESULT WINAPI
399 IDirect3DViewportImpl_TransformVertices(IDirect3DViewport3 *iface,
400                                         DWORD dwVertexCount,
401                                         D3DTRANSFORMDATA *lpData,
402                                         DWORD dwFlags,
403                                         DWORD *lpOffScreen)
404 {
405     IDirect3DViewportImpl *This = impl_from_IDirect3DViewport3(iface);
406     D3DMATRIX view_mat, world_mat, mat;
407     float *in;
408     float *out;
409     float x, y, z, w;
410     unsigned int i;
411     D3DVIEWPORT vp = This->viewports.vp1;
412     D3DHVERTEX *outH;
413
414     TRACE("iface %p, vertex_count %u, vertex_data %p, flags %#x, clip_plane %p.\n",
415             iface, dwVertexCount, lpData, dwFlags, lpOffScreen);
416
417     /* Tests on windows show that Windows crashes when this occurs,
418      * so don't return the (intuitive) return value
419     if(!This->active_device)
420     {
421         WARN("No device active, returning D3DERR_VIEWPORTHASNODEVICE\n");
422         return D3DERR_VIEWPORTHASNODEVICE;
423     }
424      */
425
426     if(!(dwFlags & (D3DTRANSFORM_UNCLIPPED | D3DTRANSFORM_CLIPPED)))
427     {
428         WARN("No clipping flag passed, returning DDERR_INVALIDPARAMS\n");
429         return DDERR_INVALIDPARAMS;
430     }
431
432
433     wined3d_mutex_lock();
434     wined3d_device_get_transform(This->active_device->wined3d_device,
435             D3DTRANSFORMSTATE_VIEW, (struct wined3d_matrix *)&view_mat);
436     wined3d_device_get_transform(This->active_device->wined3d_device,
437             WINED3DTS_WORLDMATRIX(0), (struct wined3d_matrix *)&world_mat);
438     multiply_matrix(&mat, &view_mat, &world_mat);
439     multiply_matrix(&mat, &This->active_device->legacy_projection, &mat);
440
441     in = lpData->lpIn;
442     out = lpData->lpOut;
443     outH = lpData->lpHOut;
444     for(i = 0; i < dwVertexCount; i++)
445     {
446         x = (in[0] * mat._11) + (in[1] * mat._21) + (in[2] * mat._31) + (1.0 * mat._41);
447         y = (in[0] * mat._12) + (in[1] * mat._22) + (in[2] * mat._32) + (1.0 * mat._42);
448         z = (in[0] * mat._13) + (in[1] * mat._23) + (in[2] * mat._33) + (1.0 * mat._43);
449         w = (in[0] * mat._14) + (in[1] * mat._24) + (in[2] * mat._34) + (1.0 * mat._44);
450
451         if(dwFlags & D3DTRANSFORM_CLIPPED)
452         {
453             /* If clipping is enabled, Windows assumes that outH is
454              * a valid pointer
455              */
456             outH[i].u1.hx = x; outH[i].u2.hy = y; outH[i].u3.hz = z;
457
458             outH[i].dwFlags = 0;
459             if(x * vp.dvScaleX > ((float) vp.dwWidth * 0.5))
460                 outH[i].dwFlags |= D3DCLIP_RIGHT;
461             if(x * vp.dvScaleX <= -((float) vp.dwWidth) * 0.5)
462                 outH[i].dwFlags |= D3DCLIP_LEFT;
463             if(y * vp.dvScaleY > ((float) vp.dwHeight * 0.5))
464                 outH[i].dwFlags |= D3DCLIP_TOP;
465             if(y * vp.dvScaleY <= -((float) vp.dwHeight) * 0.5)
466                 outH[i].dwFlags |= D3DCLIP_BOTTOM;
467             if(z < 0.0)
468                 outH[i].dwFlags |= D3DCLIP_FRONT;
469             if(z > 1.0)
470                 outH[i].dwFlags |= D3DCLIP_BACK;
471
472             if(outH[i].dwFlags)
473             {
474                 /* Looks like native just drops the vertex, leaves whatever data
475                  * it has in the output buffer and goes on with the next vertex.
476                  * The exact scheme hasn't been figured out yet, but windows
477                  * definitely writes something there.
478                  */
479                 out[0] = x;
480                 out[1] = y;
481                 out[2] = z;
482                 out[3] = w;
483                 in = (float *) ((char *) in + lpData->dwInSize);
484                 out = (float *) ((char *) out + lpData->dwOutSize);
485                 continue;
486             }
487         }
488
489         w = 1 / w;
490         x *= w; y *= w; z *= w;
491
492         out[0] = vp.dwWidth / 2 + vp.dwX + x * vp.dvScaleX;
493         out[1] = vp.dwHeight / 2 + vp.dwY - y * vp.dvScaleY;
494         out[2] = z;
495         out[3] = w;
496         in = (float *) ((char *) in + lpData->dwInSize);
497         out = (float *) ((char *) out + lpData->dwOutSize);
498     }
499
500     /* According to the d3d test, the offscreen flag is set only
501      * if exactly one vertex is transformed. Its not documented,
502      * but the test shows that the lpOffscreen flag is set to the
503      * flag combination of clipping planes that clips the vertex.
504      *
505      * If clipping is requested, Windows assumes that the offscreen
506      * param is a valid pointer.
507      */
508     if(dwVertexCount == 1 && dwFlags & D3DTRANSFORM_CLIPPED)
509     {
510         *lpOffScreen = outH[0].dwFlags;
511     }
512     else if(*lpOffScreen)
513     {
514         *lpOffScreen = 0;
515     }
516     wined3d_mutex_unlock();
517
518     TRACE("All done\n");
519     return DD_OK;
520 }
521
522 /*****************************************************************************
523  * IDirect3DViewport3::LightElements
524  *
525  * The DirectX 5.0 sdk says that it's not implemented
526  *
527  * Params:
528  *  ?
529  *
530  * Returns:
531  *  DDERR_UNSUPPORTED
532  *
533  *****************************************************************************/
534 static HRESULT WINAPI
535 IDirect3DViewportImpl_LightElements(IDirect3DViewport3 *iface,
536                                     DWORD dwElementCount,
537                                     LPD3DLIGHTDATA lpData)
538 {
539     TRACE("iface %p, element_count %u, data %p.\n", iface, dwElementCount, lpData);
540
541     return DDERR_UNSUPPORTED;
542 }
543
544 /*****************************************************************************
545  * IDirect3DViewport3::SetBackground
546  *
547  * Sets tje background material
548  *
549  * Params:
550  *  hMat: Handle from a IDirect3DMaterial interface
551  *
552  * Returns:
553  *  D3D_OK on success
554  *
555  *****************************************************************************/
556 static HRESULT WINAPI
557 IDirect3DViewportImpl_SetBackground(IDirect3DViewport3 *iface,
558                                     D3DMATERIALHANDLE hMat)
559 {
560     IDirect3DViewportImpl *This = impl_from_IDirect3DViewport3(iface);
561     IDirect3DMaterialImpl *m;
562
563     TRACE("iface %p, material %#x.\n", iface, hMat);
564
565     wined3d_mutex_lock();
566
567     if (!hMat)
568     {
569         This->background = NULL;
570         TRACE("Setting background to NULL\n");
571         wined3d_mutex_unlock();
572         return D3D_OK;
573     }
574
575     m = ddraw_get_object(&This->ddraw->d3ddevice->handle_table, hMat - 1, DDRAW_HANDLE_MATERIAL);
576     if (!m)
577     {
578         WARN("Invalid material handle.\n");
579         wined3d_mutex_unlock();
580         return DDERR_INVALIDPARAMS;
581     }
582
583     TRACE("Setting background color : %.8e %.8e %.8e %.8e.\n",
584             m->mat.u.diffuse.u1.r, m->mat.u.diffuse.u2.g,
585             m->mat.u.diffuse.u3.b, m->mat.u.diffuse.u4.a);
586     This->background = m;
587
588     wined3d_mutex_unlock();
589
590     return D3D_OK;
591 }
592
593 /*****************************************************************************
594  * IDirect3DViewport3::GetBackground
595  *
596  * Returns the material handle assigned to the background of the viewport
597  *
598  * Params:
599  *  lphMat: Address to store the handle
600  *  lpValid: is set to FALSE if no background is set, TRUE if one is set
601  *
602  * Returns:
603  *  D3D_OK
604  *
605  *****************************************************************************/
606 static HRESULT WINAPI
607 IDirect3DViewportImpl_GetBackground(IDirect3DViewport3 *iface,
608                                     D3DMATERIALHANDLE *lphMat,
609                                     BOOL *lpValid)
610 {
611     IDirect3DViewportImpl *This = impl_from_IDirect3DViewport3(iface);
612
613     TRACE("iface %p, material %p, valid %p.\n", iface, lphMat, lpValid);
614
615     wined3d_mutex_lock();
616     if(lpValid)
617     {
618         *lpValid = This->background != NULL;
619     }
620     if(lphMat)
621     {
622         if(This->background)
623         {
624             *lphMat = This->background->Handle;
625         }
626         else
627         {
628             *lphMat = 0;
629         }
630     }
631     wined3d_mutex_unlock();
632
633     return D3D_OK;
634 }
635
636 /*****************************************************************************
637  * IDirect3DViewport3::SetBackgroundDepth
638  *
639  * Sets a surface that represents the background depth. It's contents are
640  * used to set the depth buffer in IDirect3DViewport3::Clear
641  *
642  * Params:
643  *  lpDDSurface: Surface to set
644  *
645  * Returns: D3D_OK, because it's a stub
646  *
647  *****************************************************************************/
648 static HRESULT WINAPI
649 IDirect3DViewportImpl_SetBackgroundDepth(IDirect3DViewport3 *iface,
650                                          IDirectDrawSurface *lpDDSurface)
651 {
652     FIXME("iface %p, surface %p stub!\n", iface, lpDDSurface);
653
654     return D3D_OK;
655 }
656
657 /*****************************************************************************
658  * IDirect3DViewport3::GetBackgroundDepth
659  *
660  * Returns the surface that represents the depth field
661  *
662  * Params:
663  *  lplpDDSurface: Address to store the interface pointer
664  *  lpValid: Set to TRUE if a depth is assigned, FALSE otherwise
665  *
666  * Returns:
667  *  D3D_OK, because it's a stub
668  *  (DDERR_INVALIDPARAMS if DDSurface of Valid is NULL)
669  *
670  *****************************************************************************/
671 static HRESULT WINAPI
672 IDirect3DViewportImpl_GetBackgroundDepth(IDirect3DViewport3 *iface,
673                                          IDirectDrawSurface **lplpDDSurface,
674                                          LPBOOL lpValid)
675 {
676     FIXME("iface %p, surface %p, valid %p stub!\n", iface, lplpDDSurface, lpValid);
677
678     return DD_OK;
679 }
680
681 /*****************************************************************************
682  * IDirect3DViewport3::Clear
683  *
684  * Clears the render target and / or the z buffer
685  *
686  * Params:
687  *  dwCount: The amount of rectangles to clear. If 0, the whole buffer is
688  *           cleared
689  *  lpRects: Pointer to the array of rectangles. If NULL, Count must be 0
690  *  dwFlags: D3DCLEAR_ZBUFFER and / or D3DCLEAR_TARGET
691  *
692  * Returns:
693  *  D3D_OK on success
694  *  D3DERR_VIEWPORTHASNODEVICE if there's no active device
695  *  The return value of IDirect3DDevice7::Clear
696  *
697  *****************************************************************************/
698 static HRESULT WINAPI IDirect3DViewportImpl_Clear(IDirect3DViewport3 *iface,
699         DWORD dwCount, D3DRECT *lpRects, DWORD dwFlags)
700 {
701     IDirect3DViewportImpl *This = impl_from_IDirect3DViewport3(iface);
702     DWORD color = 0x00000000;
703     HRESULT hr;
704     IDirect3DViewport3 *current_viewport;
705     IDirect3DDevice3 *d3d_device3;
706
707     TRACE("iface %p, rect_count %u, rects %p, flags %#x.\n", iface, dwCount, lpRects, dwFlags);
708
709     if (This->active_device == NULL) {
710         ERR(" Trying to clear a viewport not attached to a device !\n");
711         return D3DERR_VIEWPORTHASNODEVICE;
712     }
713     d3d_device3 = &This->active_device->IDirect3DDevice3_iface;
714
715     wined3d_mutex_lock();
716
717     if (dwFlags & D3DCLEAR_TARGET) {
718         if (This->background == NULL) {
719             ERR(" Trying to clear the color buffer without background material !\n");
720         }
721         else
722         {
723             color = ((int)((This->background->mat.u.diffuse.u1.r) * 255) << 16)
724                     | ((int) ((This->background->mat.u.diffuse.u2.g) * 255) <<  8)
725                     | ((int) ((This->background->mat.u.diffuse.u3.b) * 255) <<  0)
726                     | ((int) ((This->background->mat.u.diffuse.u4.a) * 255) << 24);
727         }
728     }
729
730     /* Need to temporarily activate viewport to clear it. Previously active one will be restored
731         afterwards. */
732     viewport_activate(This, TRUE);
733
734     hr = IDirect3DDevice7_Clear(&This->active_device->IDirect3DDevice7_iface, dwCount, lpRects,
735             dwFlags & (D3DCLEAR_ZBUFFER | D3DCLEAR_TARGET), color, 1.0, 0x00000000);
736
737     IDirect3DDevice3_GetCurrentViewport(d3d_device3, &current_viewport);
738     if(current_viewport) {
739         IDirect3DViewportImpl *vp = impl_from_IDirect3DViewport3(current_viewport);
740         viewport_activate(vp, TRUE);
741         IDirect3DViewport3_Release(current_viewport);
742     }
743
744     wined3d_mutex_unlock();
745
746     return hr;
747 }
748
749 /*****************************************************************************
750  * IDirect3DViewport3::AddLight
751  *
752  * Adds an light to the viewport
753  *
754  * Params:
755  *  lpDirect3DLight: Interface of the light to add
756  *
757  * Returns:
758  *  D3D_OK on success
759  *  DDERR_INVALIDPARAMS if Direct3DLight is NULL
760  *  DDERR_INVALIDPARAMS if there are 8 lights or more
761  *
762  *****************************************************************************/
763 static HRESULT WINAPI IDirect3DViewportImpl_AddLight(IDirect3DViewport3 *iface,
764         IDirect3DLight *lpDirect3DLight)
765 {
766     IDirect3DViewportImpl *This = impl_from_IDirect3DViewport3(iface);
767     IDirect3DLightImpl *lpDirect3DLightImpl = unsafe_impl_from_IDirect3DLight(lpDirect3DLight);
768     DWORD i = 0;
769     DWORD map = This->map_lights;
770
771     TRACE("iface %p, light %p.\n", iface, lpDirect3DLight);
772
773     wined3d_mutex_lock();
774
775     if (This->num_lights >= 8)
776     {
777         wined3d_mutex_unlock();
778         return DDERR_INVALIDPARAMS;
779     }
780
781     /* Find a light number and update both light and viewports objects accordingly */
782     while (map & 1)
783     {
784         map >>= 1;
785         ++i;
786     }
787     lpDirect3DLightImpl->dwLightIndex = i;
788     This->num_lights++;
789     This->map_lights |= 1<<i;
790
791     /* Add the light in the 'linked' chain */
792     list_add_head(&This->light_list, &lpDirect3DLightImpl->entry);
793     IDirect3DLight_AddRef(lpDirect3DLight);
794
795     /* Attach the light to the viewport */
796     lpDirect3DLightImpl->active_viewport = This;
797
798     /* If active, activate the light */
799     if (This->active_device)
800         light_activate(lpDirect3DLightImpl);
801
802     wined3d_mutex_unlock();
803
804     return D3D_OK;
805 }
806
807 /*****************************************************************************
808  * IDirect3DViewport3::DeleteLight
809  *
810  * Deletes a light from the viewports' light list
811  *
812  * Params:
813  *  lpDirect3DLight: Light to delete
814  *
815  * Returns:
816  *  D3D_OK on success
817  *  DDERR_INVALIDPARAMS if the light wasn't found
818  *
819  *****************************************************************************/
820 static HRESULT WINAPI IDirect3DViewportImpl_DeleteLight(IDirect3DViewport3 *iface,
821         IDirect3DLight *lpDirect3DLight)
822 {
823     IDirect3DViewportImpl *This = impl_from_IDirect3DViewport3(iface);
824     IDirect3DLightImpl *l = unsafe_impl_from_IDirect3DLight(lpDirect3DLight);
825
826     TRACE("iface %p, light %p.\n", iface, lpDirect3DLight);
827
828     wined3d_mutex_lock();
829
830     if (l->active_viewport != This)
831     {
832         WARN("Light %p active viewport is %p.\n", l, l->active_viewport);
833         wined3d_mutex_unlock();
834         return DDERR_INVALIDPARAMS;
835     }
836
837     light_deactivate(l);
838     list_remove(&l->entry);
839     l->active_viewport = NULL;
840     IDirect3DLight_Release(lpDirect3DLight);
841     --This->num_lights;
842     This->map_lights &= ~(1 << l->dwLightIndex);
843
844     wined3d_mutex_unlock();
845
846     return D3D_OK;
847 }
848
849 /*****************************************************************************
850  * IDirect3DViewport::NextLight
851  *
852  * Enumerates the lights associated with the viewport
853  *
854  * Params:
855  *  lpDirect3DLight: Light to start with
856  *  lplpDirect3DLight: Address to store the successor to
857  *
858  * Returns:
859  *  D3D_OK, because it's a stub
860  *
861  *****************************************************************************/
862 static HRESULT WINAPI IDirect3DViewportImpl_NextLight(IDirect3DViewport3 *iface,
863         IDirect3DLight *lpDirect3DLight, IDirect3DLight **lplpDirect3DLight, DWORD dwFlags)
864 {
865     IDirect3DViewportImpl *This = impl_from_IDirect3DViewport3(iface);
866     IDirect3DLightImpl *l = unsafe_impl_from_IDirect3DLight(lpDirect3DLight);
867     struct list *entry;
868     HRESULT hr;
869
870     TRACE("iface %p, light %p, next_light %p, flags %#x.\n",
871             iface, lpDirect3DLight, lplpDirect3DLight, dwFlags);
872
873     if (!lplpDirect3DLight)
874         return DDERR_INVALIDPARAMS;
875
876     wined3d_mutex_lock();
877
878     switch (dwFlags)
879     {
880         case D3DNEXT_NEXT:
881             if (!l || l->active_viewport != This)
882             {
883                 if (l)
884                     WARN("Light %p active viewport is %p.\n", l, l->active_viewport);
885                 entry = NULL;
886             }
887             else
888                 entry = list_next(&This->light_list, &l->entry);
889             break;
890
891         case D3DNEXT_HEAD:
892             entry = list_head(&This->light_list);
893             break;
894
895         case D3DNEXT_TAIL:
896             entry = list_tail(&This->light_list);
897             break;
898
899         default:
900             entry = NULL;
901             WARN("Invalid flags %#x.\n", dwFlags);
902             break;
903     }
904
905     if (entry)
906     {
907         *lplpDirect3DLight = (IDirect3DLight *)LIST_ENTRY(entry, IDirect3DLightImpl, entry);
908         IDirect3DLight_AddRef(*lplpDirect3DLight);
909         hr = D3D_OK;
910     }
911     else
912     {
913         *lplpDirect3DLight = NULL;
914         hr = DDERR_INVALIDPARAMS;
915     }
916
917     wined3d_mutex_unlock();
918
919     return hr;
920 }
921
922 /*****************************************************************************
923  * IDirect3DViewport2 Methods.
924  *****************************************************************************/
925
926 /*****************************************************************************
927  * IDirect3DViewport3::GetViewport2
928  *
929  * Returns the currently set viewport in a D3DVIEWPORT2 structure.
930  * Similar to IDirect3DViewport3::GetViewport
931  *
932  * Params:
933  *  lpData: Pointer to the structure to fill
934  *
935  * Returns:
936  *  D3D_OK on success
937  *  DDERR_INVALIDPARAMS if the viewport was set with
938  *                      IDirect3DViewport3::SetViewport
939  *  DDERR_INVALIDPARAMS if Data is NULL
940  *
941  *****************************************************************************/
942 static HRESULT WINAPI
943 IDirect3DViewportImpl_GetViewport2(IDirect3DViewport3 *iface,
944                                    D3DVIEWPORT2 *lpData)
945 {
946     IDirect3DViewportImpl *This = impl_from_IDirect3DViewport3(iface);
947     DWORD dwSize;
948
949     TRACE("iface %p, data %p.\n", iface, lpData);
950
951     wined3d_mutex_lock();
952     dwSize = lpData->dwSize;
953     memset(lpData, 0, dwSize);
954     if (This->use_vp2)
955         memcpy(lpData, &(This->viewports.vp2), dwSize);
956     else {
957         D3DVIEWPORT2 vp2;
958         vp2.dwSize = sizeof(vp2);
959         vp2.dwX = This->viewports.vp1.dwX;
960         vp2.dwY = This->viewports.vp1.dwY;
961         vp2.dwWidth = This->viewports.vp1.dwWidth;
962         vp2.dwHeight = This->viewports.vp1.dwHeight;
963         vp2.dvClipX = 0.0;
964         vp2.dvClipY = 0.0;
965         vp2.dvClipWidth = 0.0;
966         vp2.dvClipHeight = 0.0;
967         vp2.dvMinZ = This->viewports.vp1.dvMinZ;
968         vp2.dvMaxZ = This->viewports.vp1.dvMaxZ;
969         memcpy(lpData, &vp2, dwSize);
970     }
971
972     if (TRACE_ON(ddraw))
973     {
974         TRACE("  returning D3DVIEWPORT2 :\n");
975         _dump_D3DVIEWPORT2(lpData);
976     }
977
978     wined3d_mutex_unlock();
979
980     return D3D_OK;
981 }
982
983 /*****************************************************************************
984  * IDirect3DViewport3::SetViewport2
985  *
986  * Sets the viewport from a D3DVIEWPORT2 structure
987  *
988  * Params:
989  *  lpData: Viewport to set
990  *
991  * Returns:
992  *  D3D_OK on success
993  *
994  *****************************************************************************/
995 static HRESULT WINAPI
996 IDirect3DViewportImpl_SetViewport2(IDirect3DViewport3 *iface,
997                                    D3DVIEWPORT2 *lpData)
998 {
999     IDirect3DViewportImpl *This = impl_from_IDirect3DViewport3(iface);
1000     IDirect3DViewport3 *current_viewport;
1001
1002     TRACE("iface %p, data %p.\n", iface, lpData);
1003
1004     if (TRACE_ON(ddraw))
1005     {
1006         TRACE("  getting D3DVIEWPORT2 :\n");
1007         _dump_D3DVIEWPORT2(lpData);
1008     }
1009
1010     wined3d_mutex_lock();
1011
1012     This->use_vp2 = 1;
1013     memset(&(This->viewports.vp2), 0, sizeof(This->viewports.vp2));
1014     memcpy(&(This->viewports.vp2), lpData, lpData->dwSize);
1015
1016     if (This->active_device) {
1017         IDirect3DDevice3 *d3d_device3 = &This->active_device->IDirect3DDevice3_iface;
1018         IDirect3DDevice3_GetCurrentViewport(d3d_device3, &current_viewport);
1019         if (current_viewport)
1020         {
1021             if (current_viewport == iface) viewport_activate(This, FALSE);
1022             IDirect3DViewport3_Release(current_viewport);
1023         }
1024     }
1025
1026     wined3d_mutex_unlock();
1027
1028     return D3D_OK;
1029 }
1030
1031 /*****************************************************************************
1032  * IDirect3DViewport3 Methods.
1033  *****************************************************************************/
1034
1035 /*****************************************************************************
1036  * IDirect3DViewport3::SetBackgroundDepth2
1037  *
1038  * Sets a IDirectDrawSurface4 surface as the background depth surface
1039  *
1040  * Params:
1041  *  lpDDS: Surface to set
1042  *
1043  * Returns:
1044  *  D3D_OK, because it's stub
1045  *
1046  *****************************************************************************/
1047 static HRESULT WINAPI
1048 IDirect3DViewportImpl_SetBackgroundDepth2(IDirect3DViewport3 *iface,
1049                                           IDirectDrawSurface4 *lpDDS)
1050 {
1051     FIXME("iface %p, surface %p stub!\n", iface, lpDDS);
1052
1053     return D3D_OK;
1054 }
1055
1056 /*****************************************************************************
1057  * IDirect3DViewport3::GetBackgroundDepth2
1058  *
1059  * Returns the IDirect3DSurface4 interface to the background depth surface
1060  *
1061  * Params:
1062  *  lplpDDS: Address to store the interface pointer at
1063  *  lpValid: Set to true if a surface is assigned
1064  *
1065  * Returns:
1066  *  D3D_OK because it's a stub
1067  *
1068  *****************************************************************************/
1069 static HRESULT WINAPI
1070 IDirect3DViewportImpl_GetBackgroundDepth2(IDirect3DViewport3 *iface,
1071                                           IDirectDrawSurface4 **lplpDDS,
1072                                           BOOL *lpValid)
1073 {
1074     FIXME("iface %p, surface %p, valid %p stub!\n", iface, lplpDDS, lpValid);
1075
1076     return D3D_OK;
1077 }
1078
1079 /*****************************************************************************
1080  * IDirect3DViewport3::Clear2
1081  *
1082  * Another clearing method
1083  *
1084  * Params:
1085  *  Count: Number of rectangles to clear
1086  *  Rects: Rectangle array to clear
1087  *  Flags: Some flags :)
1088  *  Color: Color to fill the render target with
1089  *  Z: Value to fill the depth buffer with
1090  *  Stencil: Value to fill the stencil bits with
1091  *
1092  * Returns:
1093  *
1094  *****************************************************************************/
1095 static HRESULT WINAPI
1096 IDirect3DViewportImpl_Clear2(IDirect3DViewport3 *iface,
1097                              DWORD dwCount,
1098                              LPD3DRECT lpRects,
1099                              DWORD dwFlags,
1100                              DWORD dwColor,
1101                              D3DVALUE dvZ,
1102                              DWORD dwStencil)
1103 {
1104     IDirect3DViewportImpl *This = impl_from_IDirect3DViewport3(iface);
1105     HRESULT hr;
1106     IDirect3DViewport3 *current_viewport;
1107     IDirect3DDevice3 *d3d_device3;
1108
1109     TRACE("iface %p, rect_count %u, rects %p, flags %#x, color 0x%08x, depth %.8e, stencil %u.\n",
1110             iface, dwCount, lpRects, dwFlags, dwColor, dvZ, dwStencil);
1111
1112     wined3d_mutex_lock();
1113
1114     if (This->active_device == NULL) {
1115         ERR(" Trying to clear a viewport not attached to a device !\n");
1116         wined3d_mutex_unlock();
1117         return D3DERR_VIEWPORTHASNODEVICE;
1118     }
1119     d3d_device3 = &This->active_device->IDirect3DDevice3_iface;
1120     /* Need to temporarily activate viewport to clear it. Previously active
1121      * one will be restored afterwards. */
1122     viewport_activate(This, TRUE);
1123
1124     hr = IDirect3DDevice7_Clear(&This->active_device->IDirect3DDevice7_iface,
1125             dwCount, lpRects, dwFlags, dwColor, dvZ, dwStencil);
1126     IDirect3DDevice3_GetCurrentViewport(d3d_device3, &current_viewport);
1127     if(current_viewport) {
1128         IDirect3DViewportImpl *vp = impl_from_IDirect3DViewport3(current_viewport);
1129         viewport_activate(vp, TRUE);
1130         IDirect3DViewport3_Release(current_viewport);
1131     }
1132
1133     wined3d_mutex_unlock();
1134
1135     return hr;
1136 }
1137
1138 /*****************************************************************************
1139  * The VTable
1140  *****************************************************************************/
1141
1142 static const struct IDirect3DViewport3Vtbl d3d_viewport_vtbl =
1143 {
1144     /*** IUnknown Methods ***/
1145     IDirect3DViewportImpl_QueryInterface,
1146     IDirect3DViewportImpl_AddRef,
1147     IDirect3DViewportImpl_Release,
1148     /*** IDirect3DViewport Methods */
1149     IDirect3DViewportImpl_Initialize,
1150     IDirect3DViewportImpl_GetViewport,
1151     IDirect3DViewportImpl_SetViewport,
1152     IDirect3DViewportImpl_TransformVertices,
1153     IDirect3DViewportImpl_LightElements,
1154     IDirect3DViewportImpl_SetBackground,
1155     IDirect3DViewportImpl_GetBackground,
1156     IDirect3DViewportImpl_SetBackgroundDepth,
1157     IDirect3DViewportImpl_GetBackgroundDepth,
1158     IDirect3DViewportImpl_Clear,
1159     IDirect3DViewportImpl_AddLight,
1160     IDirect3DViewportImpl_DeleteLight,
1161     IDirect3DViewportImpl_NextLight,
1162     /*** IDirect3DViewport2 Methods ***/
1163     IDirect3DViewportImpl_GetViewport2,
1164     IDirect3DViewportImpl_SetViewport2,
1165     /*** IDirect3DViewport3 Methods ***/
1166     IDirect3DViewportImpl_SetBackgroundDepth2,
1167     IDirect3DViewportImpl_GetBackgroundDepth2,
1168     IDirect3DViewportImpl_Clear2,
1169 };
1170
1171 IDirect3DViewportImpl *unsafe_impl_from_IDirect3DViewport3(IDirect3DViewport3 *iface)
1172 {
1173     if (!iface) return NULL;
1174     assert(iface->lpVtbl == &d3d_viewport_vtbl);
1175     return CONTAINING_RECORD(iface, IDirect3DViewportImpl, IDirect3DViewport3_iface);
1176 }
1177
1178 IDirect3DViewportImpl *unsafe_impl_from_IDirect3DViewport2(IDirect3DViewport2 *iface)
1179 {
1180     /* IDirect3DViewport and IDirect3DViewport3 use the same iface. */
1181     if (!iface) return NULL;
1182     assert(iface->lpVtbl == (IDirect3DViewport2Vtbl *)&d3d_viewport_vtbl);
1183     return CONTAINING_RECORD(iface, IDirect3DViewportImpl, IDirect3DViewport3_iface);
1184 }
1185
1186 IDirect3DViewportImpl *unsafe_impl_from_IDirect3DViewport(IDirect3DViewport *iface)
1187 {
1188     /* IDirect3DViewport and IDirect3DViewport3 use the same iface. */
1189     if (!iface) return NULL;
1190     assert(iface->lpVtbl == (IDirect3DViewportVtbl *)&d3d_viewport_vtbl);
1191     return CONTAINING_RECORD(iface, IDirect3DViewportImpl, IDirect3DViewport3_iface);
1192 }
1193
1194 void d3d_viewport_init(IDirect3DViewportImpl *viewport, IDirectDrawImpl *ddraw)
1195 {
1196     viewport->IDirect3DViewport3_iface.lpVtbl = &d3d_viewport_vtbl;
1197     viewport->ref = 1;
1198     viewport->ddraw = ddraw;
1199     viewport->use_vp2 = 0xff;
1200     list_init(&viewport->light_list);
1201 }