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