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