crypt32: Add additional path for Solaris 11 Express.
[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 IDirect3DViewportImpl_AddLight(IDirect3DViewport3 *iface,
719         IDirect3DLight *lpDirect3DLight)
720 {
721     IDirect3DViewportImpl *This = (IDirect3DViewportImpl *)iface;
722     IDirect3DLightImpl *lpDirect3DLightImpl = unsafe_impl_from_IDirect3DLight(lpDirect3DLight);
723     DWORD i = 0;
724     DWORD map = This->map_lights;
725
726     TRACE("iface %p, light %p.\n", iface, lpDirect3DLight);
727
728     EnterCriticalSection(&ddraw_cs);
729     if (This->num_lights >= 8)
730     {
731         LeaveCriticalSection(&ddraw_cs);
732         return DDERR_INVALIDPARAMS;
733     }
734
735     /* Find a light number and update both light and viewports objects accordingly */
736     while (map & 1)
737     {
738         map >>= 1;
739         ++i;
740     }
741     lpDirect3DLightImpl->dwLightIndex = i;
742     This->num_lights++;
743     This->map_lights |= 1<<i;
744
745     /* Add the light in the 'linked' chain */
746     list_add_head(&This->light_list, &lpDirect3DLightImpl->entry);
747     IDirect3DLight_AddRef(lpDirect3DLight);
748
749     /* Attach the light to the viewport */
750     lpDirect3DLightImpl->active_viewport = This;
751
752     /* If active, activate the light */
753     if (This->active_device)
754         light_activate(lpDirect3DLightImpl);
755
756     LeaveCriticalSection(&ddraw_cs);
757     return D3D_OK;
758 }
759
760 /*****************************************************************************
761  * IDirect3DViewport3::DeleteLight
762  *
763  * Deletes a light from the viewports' light list
764  *
765  * Params:
766  *  lpDirect3DLight: Light to delete
767  *
768  * Returns:
769  *  D3D_OK on success
770  *  DDERR_INVALIDPARAMS if the light wasn't found
771  *
772  *****************************************************************************/
773 static HRESULT WINAPI IDirect3DViewportImpl_DeleteLight(IDirect3DViewport3 *iface,
774         IDirect3DLight *lpDirect3DLight)
775 {
776     IDirect3DViewportImpl *This = (IDirect3DViewportImpl *)iface;
777     IDirect3DLightImpl *l = unsafe_impl_from_IDirect3DLight(lpDirect3DLight);
778
779     TRACE("iface %p, light %p.\n", iface, lpDirect3DLight);
780
781     EnterCriticalSection(&ddraw_cs);
782
783     if (l->active_viewport != This)
784     {
785         WARN("Light %p active viewport is %p.\n", l, l->active_viewport);
786         LeaveCriticalSection(&ddraw_cs);
787         return DDERR_INVALIDPARAMS;
788     }
789
790     light_deactivate(l);
791     list_remove(&l->entry);
792     l->active_viewport = NULL;
793     IDirect3DLight_Release(lpDirect3DLight);
794     --This->num_lights;
795     This->map_lights &= ~(1 << l->dwLightIndex);
796
797     LeaveCriticalSection(&ddraw_cs);
798
799     return D3D_OK;
800 }
801
802 /*****************************************************************************
803  * IDirect3DViewport::NextLight
804  *
805  * Enumerates the lights associated with the viewport
806  *
807  * Params:
808  *  lpDirect3DLight: Light to start with
809  *  lplpDirect3DLight: Address to store the successor to
810  *
811  * Returns:
812  *  D3D_OK, because it's a stub
813  *
814  *****************************************************************************/
815 static HRESULT WINAPI IDirect3DViewportImpl_NextLight(IDirect3DViewport3 *iface,
816         IDirect3DLight *lpDirect3DLight, IDirect3DLight **lplpDirect3DLight, DWORD dwFlags)
817 {
818     IDirect3DViewportImpl *This = (IDirect3DViewportImpl *)iface;
819     IDirect3DLightImpl *l = unsafe_impl_from_IDirect3DLight(lpDirect3DLight);
820     struct list *entry;
821     HRESULT hr;
822
823     TRACE("iface %p, light %p, next_light %p, flags %#x.\n",
824             iface, lpDirect3DLight, lplpDirect3DLight, dwFlags);
825
826     if (!lplpDirect3DLight)
827         return DDERR_INVALIDPARAMS;
828
829     EnterCriticalSection(&ddraw_cs);
830
831     switch (dwFlags)
832     {
833         case D3DNEXT_NEXT:
834             if (!l || l->active_viewport != This)
835             {
836                 if (l)
837                     WARN("Light %p active viewport is %p.\n", l, l->active_viewport);
838                 entry = NULL;
839             }
840             else
841                 entry = list_next(&This->light_list, &l->entry);
842             break;
843
844         case D3DNEXT_HEAD:
845             entry = list_head(&This->light_list);
846             break;
847
848         case D3DNEXT_TAIL:
849             entry = list_tail(&This->light_list);
850             break;
851
852         default:
853             entry = NULL;
854             WARN("Invalid flags %#x.\n", dwFlags);
855             break;
856     }
857
858     if (entry)
859     {
860         *lplpDirect3DLight = (IDirect3DLight *)LIST_ENTRY(entry, IDirect3DLightImpl, entry);
861         IDirect3DLight_AddRef(*lplpDirect3DLight);
862         hr = D3D_OK;
863     }
864     else
865     {
866         *lplpDirect3DLight = NULL;
867         hr = DDERR_INVALIDPARAMS;
868     }
869
870     LeaveCriticalSection(&ddraw_cs);
871
872     return hr;
873 }
874
875 /*****************************************************************************
876  * IDirect3DViewport2 Methods.
877  *****************************************************************************/
878
879 /*****************************************************************************
880  * IDirect3DViewport3::GetViewport2
881  *
882  * Returns the currently set viewport in a D3DVIEWPORT2 structure.
883  * Similar to IDirect3DViewport3::GetViewport
884  *
885  * Params:
886  *  lpData: Pointer to the structure to fill
887  *
888  * Returns:
889  *  D3D_OK on success
890  *  DDERR_INVALIDPARAMS if the viewport was set with
891  *                      IDirect3DViewport3::SetViewport
892  *  DDERR_INVALIDPARAMS if Data is NULL
893  *
894  *****************************************************************************/
895 static HRESULT WINAPI
896 IDirect3DViewportImpl_GetViewport2(IDirect3DViewport3 *iface,
897                                    D3DVIEWPORT2 *lpData)
898 {
899     IDirect3DViewportImpl *This = (IDirect3DViewportImpl *)iface;
900     DWORD dwSize;
901
902     TRACE("iface %p, data %p.\n", iface, lpData);
903
904     EnterCriticalSection(&ddraw_cs);
905     dwSize = lpData->dwSize;
906     memset(lpData, 0, dwSize);
907     if (This->use_vp2)
908         memcpy(lpData, &(This->viewports.vp2), dwSize);
909     else {
910         D3DVIEWPORT2 vp2;
911         vp2.dwSize = sizeof(vp2);
912         vp2.dwX = This->viewports.vp1.dwX;
913         vp2.dwY = This->viewports.vp1.dwY;
914         vp2.dwWidth = This->viewports.vp1.dwWidth;
915         vp2.dwHeight = This->viewports.vp1.dwHeight;
916         vp2.dvClipX = 0.0;
917         vp2.dvClipY = 0.0;
918         vp2.dvClipWidth = 0.0;
919         vp2.dvClipHeight = 0.0;
920         vp2.dvMinZ = This->viewports.vp1.dvMinZ;
921         vp2.dvMaxZ = This->viewports.vp1.dvMaxZ;
922         memcpy(lpData, &vp2, dwSize);
923     }
924
925     if (TRACE_ON(ddraw))
926     {
927         TRACE("  returning D3DVIEWPORT2 :\n");
928         _dump_D3DVIEWPORT2(lpData);
929     }
930
931     LeaveCriticalSection(&ddraw_cs);
932     return D3D_OK;
933 }
934
935 /*****************************************************************************
936  * IDirect3DViewport3::SetViewport2
937  *
938  * Sets the viewport from a D3DVIEWPORT2 structure
939  *
940  * Params:
941  *  lpData: Viewport to set
942  *
943  * Returns:
944  *  D3D_OK on success
945  *
946  *****************************************************************************/
947 static HRESULT WINAPI
948 IDirect3DViewportImpl_SetViewport2(IDirect3DViewport3 *iface,
949                                    D3DVIEWPORT2 *lpData)
950 {
951     IDirect3DViewportImpl *This = (IDirect3DViewportImpl *)iface;
952     LPDIRECT3DVIEWPORT3 current_viewport;
953
954     TRACE("iface %p, data %p.\n", iface, lpData);
955
956     if (TRACE_ON(ddraw))
957     {
958         TRACE("  getting D3DVIEWPORT2 :\n");
959         _dump_D3DVIEWPORT2(lpData);
960     }
961
962     EnterCriticalSection(&ddraw_cs);
963     This->use_vp2 = 1;
964     memset(&(This->viewports.vp2), 0, sizeof(This->viewports.vp2));
965     memcpy(&(This->viewports.vp2), lpData, lpData->dwSize);
966
967     if (This->active_device) {
968         IDirect3DDevice3 *d3d_device3 = (IDirect3DDevice3 *)&This->active_device->IDirect3DDevice3_vtbl;
969         IDirect3DDevice3_GetCurrentViewport(d3d_device3, &current_viewport);
970         if (current_viewport)
971         {
972             if ((IDirect3DViewportImpl *)current_viewport == This) viewport_activate(This, FALSE);
973             IDirect3DViewport3_Release(current_viewport);
974         }
975     }
976     LeaveCriticalSection(&ddraw_cs);
977
978     return D3D_OK;
979 }
980
981 /*****************************************************************************
982  * IDirect3DViewport3 Methods.
983  *****************************************************************************/
984
985 /*****************************************************************************
986  * IDirect3DViewport3::SetBackgroundDepth2
987  *
988  * Sets a IDirectDrawSurface4 surface as the background depth surface
989  *
990  * Params:
991  *  lpDDS: Surface to set
992  *
993  * Returns:
994  *  D3D_OK, because it's stub
995  *
996  *****************************************************************************/
997 static HRESULT WINAPI
998 IDirect3DViewportImpl_SetBackgroundDepth2(IDirect3DViewport3 *iface,
999                                           IDirectDrawSurface4 *lpDDS)
1000 {
1001     FIXME("iface %p, surface %p stub!\n", iface, lpDDS);
1002
1003     return D3D_OK;
1004 }
1005
1006 /*****************************************************************************
1007  * IDirect3DViewport3::GetBackgroundDepth2
1008  *
1009  * Returns the IDirect3DSurface4 interface to the background depth surface
1010  *
1011  * Params:
1012  *  lplpDDS: Address to store the interface pointer at
1013  *  lpValid: Set to true if a surface is assigned
1014  *
1015  * Returns:
1016  *  D3D_OK because it's a stub
1017  *
1018  *****************************************************************************/
1019 static HRESULT WINAPI
1020 IDirect3DViewportImpl_GetBackgroundDepth2(IDirect3DViewport3 *iface,
1021                                           IDirectDrawSurface4 **lplpDDS,
1022                                           BOOL *lpValid)
1023 {
1024     FIXME("iface %p, surface %p, valid %p stub!\n", iface, lplpDDS, lpValid);
1025
1026     return D3D_OK;
1027 }
1028
1029 /*****************************************************************************
1030  * IDirect3DViewport3::Clear2
1031  *
1032  * Another clearing method
1033  *
1034  * Params:
1035  *  Count: Number of rectangles to clear
1036  *  Rects: Rectangle array to clear
1037  *  Flags: Some flags :)
1038  *  Color: Color to fill the render target with
1039  *  Z: Value to fill the depth buffer with
1040  *  Stencil: Value to fill the stencil bits with
1041  *
1042  * Returns:
1043  *
1044  *****************************************************************************/
1045 static HRESULT WINAPI
1046 IDirect3DViewportImpl_Clear2(IDirect3DViewport3 *iface,
1047                              DWORD dwCount,
1048                              LPD3DRECT lpRects,
1049                              DWORD dwFlags,
1050                              DWORD dwColor,
1051                              D3DVALUE dvZ,
1052                              DWORD dwStencil)
1053 {
1054     IDirect3DViewportImpl *This = (IDirect3DViewportImpl *)iface;
1055     HRESULT hr;
1056     LPDIRECT3DVIEWPORT3 current_viewport;
1057     IDirect3DDevice3 *d3d_device3;
1058
1059     TRACE("iface %p, rect_count %u, rects %p, flags %#x, color 0x%08x, depth %.8e, stencil %u.\n",
1060             iface, dwCount, lpRects, dwFlags, dwColor, dvZ, dwStencil);
1061
1062     EnterCriticalSection(&ddraw_cs);
1063     if (This->active_device == NULL) {
1064         ERR(" Trying to clear a viewport not attached to a device !\n");
1065         LeaveCriticalSection(&ddraw_cs);
1066         return D3DERR_VIEWPORTHASNODEVICE;
1067     }
1068     d3d_device3 = (IDirect3DDevice3 *)&This->active_device->IDirect3DDevice3_vtbl;
1069     /* Need to temporarily activate viewport to clear it. Previously active
1070      * one will be restored afterwards. */
1071     viewport_activate(This, TRUE);
1072
1073     hr = IDirect3DDevice7_Clear((IDirect3DDevice7 *)This->active_device,
1074             dwCount, lpRects, dwFlags, dwColor, dvZ, dwStencil);
1075     IDirect3DDevice3_GetCurrentViewport(d3d_device3, &current_viewport);
1076     if(current_viewport) {
1077         IDirect3DViewportImpl *vp = (IDirect3DViewportImpl *)current_viewport;
1078         viewport_activate(vp, TRUE);
1079         IDirect3DViewport3_Release(current_viewport);
1080     }
1081     LeaveCriticalSection(&ddraw_cs);
1082     return hr;
1083 }
1084
1085 /*****************************************************************************
1086  * The VTable
1087  *****************************************************************************/
1088
1089 static const struct IDirect3DViewport3Vtbl d3d_viewport_vtbl =
1090 {
1091     /*** IUnknown Methods ***/
1092     IDirect3DViewportImpl_QueryInterface,
1093     IDirect3DViewportImpl_AddRef,
1094     IDirect3DViewportImpl_Release,
1095     /*** IDirect3DViewport Methods */
1096     IDirect3DViewportImpl_Initialize,
1097     IDirect3DViewportImpl_GetViewport,
1098     IDirect3DViewportImpl_SetViewport,
1099     IDirect3DViewportImpl_TransformVertices,
1100     IDirect3DViewportImpl_LightElements,
1101     IDirect3DViewportImpl_SetBackground,
1102     IDirect3DViewportImpl_GetBackground,
1103     IDirect3DViewportImpl_SetBackgroundDepth,
1104     IDirect3DViewportImpl_GetBackgroundDepth,
1105     IDirect3DViewportImpl_Clear,
1106     IDirect3DViewportImpl_AddLight,
1107     IDirect3DViewportImpl_DeleteLight,
1108     IDirect3DViewportImpl_NextLight,
1109     /*** IDirect3DViewport2 Methods ***/
1110     IDirect3DViewportImpl_GetViewport2,
1111     IDirect3DViewportImpl_SetViewport2,
1112     /*** IDirect3DViewport3 Methods ***/
1113     IDirect3DViewportImpl_SetBackgroundDepth2,
1114     IDirect3DViewportImpl_GetBackgroundDepth2,
1115     IDirect3DViewportImpl_Clear2,
1116 };
1117
1118 void d3d_viewport_init(IDirect3DViewportImpl *viewport, IDirectDrawImpl *ddraw)
1119 {
1120     viewport->lpVtbl = &d3d_viewport_vtbl;
1121     viewport->ref = 1;
1122     viewport->ddraw = ddraw;
1123     viewport->use_vp2 = 0xff;
1124     list_init(&viewport->light_list);
1125 }