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