viewport.c 36.9 KB
Newer Older
1
/* Direct3D Viewport
2
 * Copyright (c) 1998 Lionel ULMER
3
 * Copyright (c) 2006-2007 Stefan DÖSINGER
4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
 *
 * This file contains the implementation of Direct3DViewport2.
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * 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 31
/*****************************************************************************
 * Helper functions
 *****************************************************************************/
32

33 34 35 36 37 38
/*****************************************************************************
 * viewport_activate
 *
 * activates the viewport using IDirect3DDevice7::SetViewport
 *
 *****************************************************************************/
39
void viewport_activate(IDirect3DViewportImpl* This, BOOL ignore_lights) {
40
    IDirect3DLightImpl* light;
41
    D3DVIEWPORT7 vp;
42

43 44 45 46
    if (!ignore_lights) {
        /* Activate all the lights associated with this context */
        light = This->lights;

47 48 49
        while (light)
        {
            light_activate(light);
50 51
            light = light->next;
        }
52
    }
53 54

    /* And copy the values in the structure used by the device */
55 56
    if (This->use_vp2)
    {
57
        vp.dwX = This->viewports.vp2.dwX;
58 59 60 61 62 63 64 65
        vp.dwY = This->viewports.vp2.dwY;
        vp.dwHeight = This->viewports.vp2.dwHeight;
        vp.dwWidth = This->viewports.vp2.dwWidth;
        vp.dvMinZ = This->viewports.vp2.dvMinZ;
        vp.dvMaxZ = This->viewports.vp2.dvMaxZ;
    }
    else
    {
66
        vp.dwX = This->viewports.vp1.dwX;
67 68 69 70 71
        vp.dwY = This->viewports.vp1.dwY;
        vp.dwHeight = This->viewports.vp1.dwHeight;
        vp.dwWidth = This->viewports.vp1.dwWidth;
        vp.dvMinZ = This->viewports.vp1.dvMinZ;
        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 80 81 82 83
/*****************************************************************************
 * _dump_D3DVIEWPORT, _dump_D3DVIEWPORT2
 *
 * Writes viewport information to TRACE
 *
 *****************************************************************************/
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 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131
/*****************************************************************************
 * IUnknown Methods.
 *****************************************************************************/

/*****************************************************************************
 * IDirect3DViewport3::QueryInterface
 *
 * A normal QueryInterface. Can query all interface versions and the
 * IUnknown interface. The VTables of the different versions
 * are equal
 *
 * Params:
 *  refiid: Interface id queried for
 *  obj: Address to write the interface pointer to
 *
 * Returns:
 *  S_OK on success.
 *  E_NOINTERFACE if the requested interface wasn't found
 *
 *****************************************************************************/
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 137 138 139 140
    if (IsEqualGUID(&IID_IDirect3DViewport3, riid)
            || IsEqualGUID(&IID_IDirect3DViewport2, riid)
            || IsEqualGUID(&IID_IDirect3DViewport, riid)
            || IsEqualGUID(&IID_IUnknown, riid))
    {
141
        IDirect3DViewport3_AddRef(iface);
142 143
        *object = iface;
        return S_OK;
144
    }
145 146 147 148

    WARN("%s not implemented, returning E_NOINTERFACE.\n", debugstr_guid(riid));

    *object = NULL;
149
    return E_NOINTERFACE;
150 151
}

152 153 154 155 156 157 158 159 160 161 162
/*****************************************************************************
 * IDirect3DViewport3::AddRef
 *
 * Increases the refcount.
 *
 * Returns:
 *  The new refcount
 *
 *****************************************************************************/
static ULONG WINAPI
IDirect3DViewportImpl_AddRef(IDirect3DViewport3 *iface)
163
{
164
    IDirect3DViewportImpl *This = (IDirect3DViewportImpl *)iface;
165 166
    ULONG ref = InterlockedIncrement(&This->ref);

167
    TRACE("%p increasing refcount to %u.\n", This, ref);
168 169

    return ref;
170 171
}

172 173 174 175 176 177 178 179 180 181 182
/*****************************************************************************
 * IDirect3DViewport3::Release
 *
 * Reduces the refcount. If it falls to 0, the interface is released
 *
 * Returns:
 *  The new refcount
 *
 *****************************************************************************/
static ULONG WINAPI
IDirect3DViewportImpl_Release(IDirect3DViewport3 *iface)
183
{
184
    IDirect3DViewportImpl *This = (IDirect3DViewportImpl *)iface;
185 186
    ULONG ref = InterlockedDecrement(&This->ref);

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 198
/*****************************************************************************
 * IDirect3DViewport Methods.
 *****************************************************************************/
199

200 201 202 203 204 205 206 207 208 209 210 211 212 213 214
/*****************************************************************************
 * IDirect3DViewport3::Initialize
 *
 * No-op initialization.
 *
 * Params:
 *  Direct3D: The direct3D device this viewport is assigned to
 *
 * Returns:
 *  DDERR_ALREADYINITIALIZED
 *
 *****************************************************************************/
static HRESULT WINAPI
IDirect3DViewportImpl_Initialize(IDirect3DViewport3 *iface,
                                 IDirect3D *Direct3D)
215
{
216 217
    TRACE("iface %p, d3d %p.\n", iface, Direct3D);

218
    return DDERR_ALREADYINITIALIZED;
219 220
}

221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236
/*****************************************************************************
 * IDirect3DViewport3::GetViewport
 *
 * Returns the viewport data assigned to this viewport interface
 *
 * Params:
 *  Data: Address to store the data
 *
 * Returns:
 *  D3D_OK on success
 *  DDERR_INVALIDPARAMS if Data is NULL
 *
 *****************************************************************************/
static HRESULT WINAPI
IDirect3DViewportImpl_GetViewport(IDirect3DViewport3 *iface,
                                  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 245
    dwSize = lpData->dwSize;
    memset(lpData, 0, dwSize);
246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262
    if (!This->use_vp2)
        memcpy(lpData, &(This->viewports.vp1), dwSize);
    else {
        D3DVIEWPORT vp1;
        vp1.dwSize = sizeof(vp1);
        vp1.dwX = This->viewports.vp2.dwX;
        vp1.dwY = This->viewports.vp2.dwY;
        vp1.dwWidth = This->viewports.vp2.dwWidth;
        vp1.dwHeight = This->viewports.vp2.dwHeight;
        vp1.dvMaxX = 0.0;
        vp1.dvMaxY = 0.0;
        vp1.dvScaleX = 0.0;
        vp1.dvScaleY = 0.0;
        vp1.dvMinZ = This->viewports.vp2.dvMinZ;
        vp1.dvMaxZ = This->viewports.vp2.dvMaxZ;
        memcpy(lpData, &vp1, dwSize);
    }
263

264 265
    if (TRACE_ON(ddraw))
    {
266
        TRACE("  returning D3DVIEWPORT :\n");
267
        _dump_D3DVIEWPORT(lpData);
268
    }
269 270
    LeaveCriticalSection(&ddraw_cs);

271
    return DD_OK;
272 273
}

274 275 276 277 278 279 280 281 282
/*****************************************************************************
 * IDirect3DViewport3::SetViewport
 *
 * Sets the viewport information for this interface
 *
 * Params:
 *  lpData: Viewport to set
 *
 * Returns:
283
 *  D3D_OK on success
284 285 286
 *  DDERR_INVALIDPARAMS if Data is NULL
 *
 *****************************************************************************/
287
static HRESULT WINAPI
288 289
IDirect3DViewportImpl_SetViewport(IDirect3DViewport3 *iface,
                                  D3DVIEWPORT *lpData)
290
{
291
    IDirect3DViewportImpl *This = (IDirect3DViewportImpl *)iface;
292
    LPDIRECT3DVIEWPORT3 current_viewport;
293

294 295 296 297
    TRACE("iface %p, data %p.\n", iface, lpData);

    if (TRACE_ON(ddraw))
    {
298
        TRACE("  getting D3DVIEWPORT :\n");
299
        _dump_D3DVIEWPORT(lpData);
300
    }
301

302
    EnterCriticalSection(&ddraw_cs);
303 304 305
    This->use_vp2 = 0;
    memset(&(This->viewports.vp1), 0, sizeof(This->viewports.vp1));
    memcpy(&(This->viewports.vp1), lpData, lpData->dwSize);
306

307
    /* Tests on two games show that these values are never used properly so override
308 309 310 311
       them with proper ones :-)
    */
    This->viewports.vp1.dvMinZ = 0.0;
    This->viewports.vp1.dvMaxZ = 1.0;
312 313

    if (This->active_device) {
314 315
        IDirect3DDevice3 *d3d_device3 = (IDirect3DDevice3 *)&This->active_device->IDirect3DDevice3_vtbl;
        IDirect3DDevice3_GetCurrentViewport(d3d_device3, &current_viewport);
316 317 318
        if (current_viewport)
        {
            if ((IDirect3DViewportImpl *)current_viewport == This) viewport_activate(This, FALSE);
319 320
            IDirect3DViewport3_Release(current_viewport);
        }
321
    }
322
    LeaveCriticalSection(&ddraw_cs);
323

324 325
    return DD_OK;
}
326

327 328 329 330 331
/*****************************************************************************
 * IDirect3DViewport3::TransformVertices
 *
 * Transforms vertices by the transformation matrix.
 *
332 333
 * This function is pretty similar to IDirect3DVertexBuffer7::ProcessVertices,
 * 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 339 340
 * ProcessVertices doesn't pay of in terms of wrapper code needed and code
 * reused.
 *
341 342 343 344
 * Params:
 *  dwVertexCount: The number of vertices to be transformed
 *  lpData: Pointer to the vertex data
 *  dwFlags: D3DTRANSFORM_CLIPPED or D3DTRANSFORM_UNCLIPPED
345 346
 *  lpOffScreen: Set to the clipping plane clipping the vertex, if only one
 *               vertex is transformed and clipping is on. 0 otherwise
347 348
 *
 * Returns:
349 350 351
 *  D3D_OK on success
 *  D3DERR_VIEWPORTHASNODEVICE if the viewport is not assigned to a device
 *  DDERR_INVALIDPARAMS if no clipping flag is specified
352 353 354 355 356 357 358 359
 *
 *****************************************************************************/
static HRESULT WINAPI
IDirect3DViewportImpl_TransformVertices(IDirect3DViewport3 *iface,
                                        DWORD dwVertexCount,
                                        D3DTRANSFORMDATA *lpData,
                                        DWORD dwFlags,
                                        DWORD *lpOffScreen)
360
{
361
    IDirect3DViewportImpl *This = (IDirect3DViewportImpl *)iface;
362 363 364 365 366 367 368
    D3DMATRIX view_mat, world_mat, proj_mat, mat;
    float *in;
    float *out;
    float x, y, z, w;
    unsigned int i;
    D3DVIEWPORT vp = This->viewports.vp1;
    D3DHVERTEX *outH;
369 370 371

    TRACE("iface %p, vertex_count %u, vertex_data %p, flags %#x, clip_plane %p.\n",
            iface, dwVertexCount, lpData, dwFlags, lpOffScreen);
372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403

    /* Tests on windows show that Windows crashes when this occurs,
     * so don't return the (intuitive) return value
    if(!This->active_device)
    {
        WARN("No device active, returning D3DERR_VIEWPORTHASNODEVICE\n");
        return D3DERR_VIEWPORTHASNODEVICE;
    }
     */

    if(!(dwFlags & (D3DTRANSFORM_UNCLIPPED | D3DTRANSFORM_CLIPPED)))
    {
        WARN("No clipping flag passed, returning DDERR_INVALIDPARAMS\n");
        return DDERR_INVALIDPARAMS;
    }


    EnterCriticalSection(&ddraw_cs);
    IWineD3DDevice_GetTransform(This->active_device->wineD3DDevice,
                                D3DTRANSFORMSTATE_VIEW,
                                (WINED3DMATRIX*) &view_mat);

    IWineD3DDevice_GetTransform(This->active_device->wineD3DDevice,
                                D3DTRANSFORMSTATE_PROJECTION,
                                (WINED3DMATRIX*) &proj_mat);

    IWineD3DDevice_GetTransform(This->active_device->wineD3DDevice,
                                WINED3DTS_WORLDMATRIX(0),
                                (WINED3DMATRIX*) &world_mat);
    multiply_matrix(&mat,&view_mat,&world_mat);
    multiply_matrix(&mat,&proj_mat,&mat);

404 405
    in = lpData->lpIn;
    out = lpData->lpOut;
406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439
    outH = lpData->lpHOut;
    for(i = 0; i < dwVertexCount; i++)
    {
        x = (in[0] * mat._11) + (in[1] * mat._21) + (in[2] * mat._31) + (1.0 * mat._41);
        y = (in[0] * mat._12) + (in[1] * mat._22) + (in[2] * mat._32) + (1.0 * mat._42);
        z = (in[0] * mat._13) + (in[1] * mat._23) + (in[2] * mat._33) + (1.0 * mat._43);
        w = (in[0] * mat._14) + (in[1] * mat._24) + (in[2] * mat._34) + (1.0 * mat._44);

        if(dwFlags & D3DTRANSFORM_CLIPPED)
        {
            /* If clipping is enabled, Windows assumes that outH is
             * a valid pointer
             */
            outH[i].u1.hx = x; outH[i].u2.hy = y; outH[i].u3.hz = z;

            outH[i].dwFlags = 0;
            if(x * vp.dvScaleX > ((float) vp.dwWidth * 0.5))
                outH[i].dwFlags |= D3DCLIP_RIGHT;
            if(x * vp.dvScaleX <= -((float) vp.dwWidth) * 0.5)
                outH[i].dwFlags |= D3DCLIP_LEFT;
            if(y * vp.dvScaleY > ((float) vp.dwHeight * 0.5))
                outH[i].dwFlags |= D3DCLIP_TOP;
            if(y * vp.dvScaleY <= -((float) vp.dwHeight) * 0.5)
                outH[i].dwFlags |= D3DCLIP_BOTTOM;
            if(z < 0.0)
                outH[i].dwFlags |= D3DCLIP_FRONT;
            if(z > 1.0)
                outH[i].dwFlags |= D3DCLIP_BACK;

            if(outH[i].dwFlags)
            {
                /* Looks like native just drops the vertex, leaves whatever data
                 * it has in the output buffer and goes on with the next vertex.
                 * The exact scheme hasn't been figured out yet, but windows
440
                 * definitely writes something there.
441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481
                 */
                out[0] = x;
                out[1] = y;
                out[2] = z;
                out[3] = w;
                in = (float *) ((char *) in + lpData->dwInSize);
                out = (float *) ((char *) out + lpData->dwOutSize);
                continue;
            }
        }

        w = 1 / w;
        x *= w; y *= w; z *= w;

        out[0] = vp.dwWidth / 2 + vp.dwX + x * vp.dvScaleX;
        out[1] = vp.dwHeight / 2 + vp.dwY - y * vp.dvScaleY;
        out[2] = z;
        out[3] = w;
        in = (float *) ((char *) in + lpData->dwInSize);
        out = (float *) ((char *) out + lpData->dwOutSize);
    }

    /* According to the d3d test, the offscreen flag is set only
     * if exactly one vertex is transformed. Its not documented,
     * but the test shows that the lpOffscreen flag is set to the
     * flag combination of clipping planes that clips the vertex.
     *
     * If clipping is requested, Windows assumes that the offscreen
     * param is a valid pointer.
     */
    if(dwVertexCount == 1 && dwFlags & D3DTRANSFORM_CLIPPED)
    {
        *lpOffScreen = outH[0].dwFlags;
    }
    else if(*lpOffScreen)
    {
        *lpOffScreen = 0;
    }
    LeaveCriticalSection(&ddraw_cs);

    TRACE("All done\n");
482
    return DD_OK;
483 484
}

485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500
/*****************************************************************************
 * IDirect3DViewport3::LightElements
 *
 * The DirectX 5.0 sdk says that it's not implemented
 *
 * Params:
 *  ?
 *
 * Returns:
 *  DDERR_UNSUPPORTED
 *
 *****************************************************************************/
static HRESULT WINAPI
IDirect3DViewportImpl_LightElements(IDirect3DViewport3 *iface,
                                    DWORD dwElementCount,
                                    LPD3DLIGHTDATA lpData)
501
{
502 503
    TRACE("iface %p, element_count %u, data %p.\n", iface, dwElementCount, lpData);

504
    return DDERR_UNSUPPORTED;
505
}
506

507 508 509 510 511 512 513 514 515 516 517 518 519 520 521
/*****************************************************************************
 * IDirect3DViewport3::SetBackground
 *
 * Sets tje background material
 *
 * Params:
 *  hMat: Handle from a IDirect3DMaterial interface
 *
 * Returns:
 *  D3D_OK on success
 *
 *****************************************************************************/
static HRESULT WINAPI
IDirect3DViewportImpl_SetBackground(IDirect3DViewport3 *iface,
                                    D3DMATERIALHANDLE hMat)
522
{
523
    IDirect3DViewportImpl *This = (IDirect3DViewportImpl *)iface;
524 525
    IDirect3DMaterialImpl *m;

526
    TRACE("iface %p, material %#x.\n", iface, hMat);
527

528
    EnterCriticalSection(&ddraw_cs);
529 530

    if (!hMat)
531
    {
532 533
        This->background = NULL;
        TRACE("Setting background to NULL\n");
534
        LeaveCriticalSection(&ddraw_cs);
535
        return D3D_OK;
536
    }
537 538 539

    m = ddraw_get_object(&This->ddraw->d3ddevice->handle_table, hMat - 1, DDRAW_HANDLE_MATERIAL);
    if (!m)
540
    {
541
        WARN("Invalid material handle.\n");
542
        LeaveCriticalSection(&ddraw_cs);
543 544 545
        return DDERR_INVALIDPARAMS;
    }

546 547 548 549
    TRACE("Setting background color : %.8e %.8e %.8e %.8e.\n",
            m->mat.u.diffuse.u1.r, m->mat.u.diffuse.u2.g,
            m->mat.u.diffuse.u3.b, m->mat.u.diffuse.u4.a);
    This->background = m;
550

551
    LeaveCriticalSection(&ddraw_cs);
552
    return D3D_OK;
553
}
554

555 556 557 558 559 560 561 562 563 564
/*****************************************************************************
 * IDirect3DViewport3::GetBackground
 *
 * Returns the material handle assigned to the background of the viewport
 *
 * Params:
 *  lphMat: Address to store the handle
 *  lpValid: is set to FALSE if no background is set, TRUE if one is set
 *
 * Returns:
565
 *  D3D_OK
566 567 568 569 570 571
 *
 *****************************************************************************/
static HRESULT WINAPI
IDirect3DViewportImpl_GetBackground(IDirect3DViewport3 *iface,
                                    D3DMATERIALHANDLE *lphMat,
                                    BOOL *lpValid)
572
{
573
    IDirect3DViewportImpl *This = (IDirect3DViewportImpl *)iface;
574 575

    TRACE("iface %p, material %p, valid %p.\n", iface, lphMat, lpValid);
576

577
    EnterCriticalSection(&ddraw_cs);
578 579 580 581 582 583 584 585 586 587 588 589 590 591 592
    if(lpValid)
    {
        *lpValid = This->background != NULL;
    }
    if(lphMat)
    {
        if(This->background)
        {
            *lphMat = This->background->Handle;
        }
        else
        {
            *lphMat = 0;
        }
    }
593
    LeaveCriticalSection(&ddraw_cs);
594

595
    return D3D_OK;
596
}
597

598 599 600 601 602 603 604 605 606 607 608 609 610 611 612
/*****************************************************************************
 * IDirect3DViewport3::SetBackgroundDepth
 *
 * Sets a surface that represents the background depth. It's contents are
 * used to set the depth buffer in IDirect3DViewport3::Clear
 *
 * Params:
 *  lpDDSurface: Surface to set
 *
 * Returns: D3D_OK, because it's a stub
 *
 *****************************************************************************/
static HRESULT WINAPI
IDirect3DViewportImpl_SetBackgroundDepth(IDirect3DViewport3 *iface,
                                         IDirectDrawSurface *lpDDSurface)
613
{
614 615
    FIXME("iface %p, surface %p stub!\n", iface, lpDDSurface);

616
    return D3D_OK;
617
}
618

619 620 621 622 623 624 625
/*****************************************************************************
 * IDirect3DViewport3::GetBackgroundDepth
 *
 * Returns the surface that represents the depth field
 *
 * Params:
 *  lplpDDSurface: Address to store the interface pointer
Austin English's avatar
Austin English committed
626
 *  lpValid: Set to TRUE if a depth is assigned, FALSE otherwise
627 628 629 630 631 632 633 634 635 636
 *
 * Returns:
 *  D3D_OK, because it's a stub
 *  (DDERR_INVALIDPARAMS if DDSurface of Valid is NULL)
 *
 *****************************************************************************/
static HRESULT WINAPI
IDirect3DViewportImpl_GetBackgroundDepth(IDirect3DViewport3 *iface,
                                         IDirectDrawSurface **lplpDDSurface,
                                         LPBOOL lpValid)
637
{
638 639
    FIXME("iface %p, surface %p, valid %p stub!\n", iface, lplpDDSurface, lpValid);

640
    return DD_OK;
641 642
}

643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659
/*****************************************************************************
 * IDirect3DViewport3::Clear
 *
 * Clears the render target and / or the z buffer
 *
 * Params:
 *  dwCount: The amount of rectangles to clear. If 0, the whole buffer is
 *           cleared
 *  lpRects: Pointer to the array of rectangles. If NULL, Count must be 0
 *  dwFlags: D3DCLEAR_ZBUFFER and / or D3DCLEAR_TARGET
 *
 * Returns:
 *  D3D_OK on success
 *  D3DERR_VIEWPORTHASNODEVICE if there's no active device
 *  The return value of IDirect3DDevice7::Clear
 *
 *****************************************************************************/
660 661
static HRESULT WINAPI IDirect3DViewportImpl_Clear(IDirect3DViewport3 *iface,
        DWORD dwCount, D3DRECT *lpRects, DWORD dwFlags)
662
{
663
    IDirect3DViewportImpl *This = (IDirect3DViewportImpl *)iface;
664
    DWORD color = 0x00000000;
665
    HRESULT hr;
666
    LPDIRECT3DVIEWPORT3 current_viewport;
667
    IDirect3DDevice3 *d3d_device3;
668

669 670
    TRACE("iface %p, rect_count %u, rects %p, flags %#x.\n", iface, dwCount, lpRects, dwFlags);

671 672
    if (This->active_device == NULL) {
        ERR(" Trying to clear a viewport not attached to a device !\n");
673
        return D3DERR_VIEWPORTHASNODEVICE;
674
    }
675
    d3d_device3 = (IDirect3DDevice3 *)&This->active_device->IDirect3DDevice3_vtbl;
676 677

    EnterCriticalSection(&ddraw_cs);
678 679
    if (dwFlags & D3DCLEAR_TARGET) {
        if (This->background == NULL) {
680 681 682 683 684 685 686 687 688
            ERR(" Trying to clear the color buffer without background material !\n");
        }
        else
        {
            color = ((int)((This->background->mat.u.diffuse.u1.r) * 255) << 16)
                    | ((int) ((This->background->mat.u.diffuse.u2.g) * 255) <<  8)
                    | ((int) ((This->background->mat.u.diffuse.u3.b) * 255) <<  0)
                    | ((int) ((This->background->mat.u.diffuse.u4.a) * 255) << 24);
        }
689
    }
690

691 692
    /* Need to temporarily activate viewport to clear it. Previously active one will be restored
        afterwards. */
693
    viewport_activate(This, TRUE);
694

695 696
    hr = IDirect3DDevice7_Clear((IDirect3DDevice7 *)This->active_device, dwCount, lpRects,
            dwFlags & (D3DCLEAR_ZBUFFER | D3DCLEAR_TARGET), color, 1.0, 0x00000000);
697

698
    IDirect3DDevice3_GetCurrentViewport(d3d_device3, &current_viewport);
699
    if(current_viewport) {
700
        IDirect3DViewportImpl *vp = (IDirect3DViewportImpl *)current_viewport;
701
        viewport_activate(vp, TRUE);
702 703 704
        IDirect3DViewport3_Release(current_viewport);
    }

705 706
    LeaveCriticalSection(&ddraw_cs);
    return hr;
707
}
708

709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725
/*****************************************************************************
 * IDirect3DViewport3::AddLight
 *
 * Adds an light to the viewport
 *
 * Params:
 *  lpDirect3DLight: Interface of the light to add
 *
 * Returns:
 *  D3D_OK on success
 *  DDERR_INVALIDPARAMS if Direct3DLight is NULL
 *  DDERR_INVALIDPARAMS if there are 8 lights or more
 *
 *****************************************************************************/
static HRESULT WINAPI
IDirect3DViewportImpl_AddLight(IDirect3DViewport3 *iface,
                               IDirect3DLight *lpDirect3DLight)
726
{
727
    IDirect3DViewportImpl *This = (IDirect3DViewportImpl *)iface;
728
    IDirect3DLightImpl *lpDirect3DLightImpl = (IDirect3DLightImpl *)lpDirect3DLight;
729 730
    DWORD i = 0;
    DWORD map = This->map_lights;
731

732
    TRACE("iface %p, light %p.\n", iface, lpDirect3DLight);
733 734

    EnterCriticalSection(&ddraw_cs);
735
    if (This->num_lights >= 8)
736 737
    {
        LeaveCriticalSection(&ddraw_cs);
738
        return DDERR_INVALIDPARAMS;
739
    }
740 741

    /* Find a light number and update both light and viewports objects accordingly */
742 743 744 745
    while (map & 1)
    {
        map >>= 1;
        ++i;
746 747 748 749 750
    }
    lpDirect3DLightImpl->dwLightIndex = i;
    This->num_lights++;
    This->map_lights |= 1<<i;

751 752 753
    /* Add the light in the 'linked' chain */
    lpDirect3DLightImpl->next = This->lights;
    This->lights = lpDirect3DLightImpl;
754
    IDirect3DLight_AddRef(lpDirect3DLight);
755

756 757
    /* Attach the light to the viewport */
    lpDirect3DLightImpl->active_viewport = This;
758

759
    /* If active, activate the light */
760 761
    if (This->active_device)
        light_activate(lpDirect3DLightImpl);
762 763

    LeaveCriticalSection(&ddraw_cs);
764
    return D3D_OK;
765 766
}

767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782
/*****************************************************************************
 * IDirect3DViewport3::DeleteLight
 *
 * Deletes a light from the viewports' light list
 *
 * Params:
 *  lpDirect3DLight: Light to delete
 *
 * Returns:
 *  D3D_OK on success
 *  DDERR_INVALIDPARAMS if the light wasn't found
 *
 *****************************************************************************/
static HRESULT WINAPI
IDirect3DViewportImpl_DeleteLight(IDirect3DViewport3 *iface,
                                  IDirect3DLight *lpDirect3DLight)
783
{
784
    IDirect3DViewportImpl *This = (IDirect3DViewportImpl *)iface;
785
    IDirect3DLightImpl *lpDirect3DLightImpl = (IDirect3DLightImpl *)lpDirect3DLight;
786
    IDirect3DLightImpl *cur_light, *prev_light = NULL;
787

788
    TRACE("iface %p, light %p.\n", iface, lpDirect3DLight);
789 790

    EnterCriticalSection(&ddraw_cs);
791 792
    cur_light = This->lights;
    while (cur_light != NULL) {
793 794 795
        if (cur_light == lpDirect3DLightImpl)
        {
            light_deactivate(lpDirect3DLightImpl);
796 797 798 799 800 801 802
            if (!prev_light) This->lights = cur_light->next;
            else prev_light->next = cur_light->next;
            /* Detach the light from the viewport. */
            cur_light->active_viewport = NULL;
            IDirect3DLight_Release((IDirect3DLight *)cur_light);
            --This->num_lights;
            This->map_lights &= ~(1 << lpDirect3DLightImpl->dwLightIndex);
803 804
            LeaveCriticalSection(&ddraw_cs);
            return D3D_OK;
805 806 807
        }
        prev_light = cur_light;
        cur_light = cur_light->next;
808
    }
809 810
    LeaveCriticalSection(&ddraw_cs);

811 812
    return DDERR_INVALIDPARAMS;
}
813

814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831
/*****************************************************************************
 * IDirect3DViewport::NextLight
 *
 * Enumerates the lights associated with the viewport
 *
 * Params:
 *  lpDirect3DLight: Light to start with
 *  lplpDirect3DLight: Address to store the successor to
 *
 * Returns:
 *  D3D_OK, because it's a stub
 *
 *****************************************************************************/
static HRESULT WINAPI
IDirect3DViewportImpl_NextLight(IDirect3DViewport3 *iface,
                                IDirect3DLight *lpDirect3DLight,
                                IDirect3DLight **lplpDirect3DLight,
                                DWORD dwFlags)
832
{
833
    IDirect3DViewportImpl *This = (IDirect3DViewportImpl *)iface;
834 835
    IDirect3DLightImpl *cur_light, *prev_light = NULL;

836 837
    TRACE("iface %p, light %p, next_light %p, flags %#x.\n",
            iface, lpDirect3DLight, lplpDirect3DLight, dwFlags);
838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861 862 863 864 865 866 867 868 869 870 871 872 873 874 875 876

    if (!lplpDirect3DLight)
        return DDERR_INVALIDPARAMS;

    *lplpDirect3DLight = NULL;

    EnterCriticalSection(&ddraw_cs);

    cur_light = This->lights;

    switch (dwFlags) {
        case D3DNEXT_NEXT:
            if (!lpDirect3DLight) {
                LeaveCriticalSection(&ddraw_cs);
                return DDERR_INVALIDPARAMS;
            }
            while (cur_light != NULL) {
                if (cur_light == (IDirect3DLightImpl *)lpDirect3DLight) {
                    *lplpDirect3DLight = (IDirect3DLight*)cur_light->next;
                    break;
                }
                cur_light = cur_light->next;
            }
            break;
        case D3DNEXT_HEAD:
            *lplpDirect3DLight = (IDirect3DLight*)This->lights;
            break;
        case D3DNEXT_TAIL:
            while (cur_light != NULL) {
                prev_light = cur_light;
                cur_light = cur_light->next;
            }
            *lplpDirect3DLight = (IDirect3DLight*)prev_light;
            break;
        default:
            ERR("Unknown flag %d\n", dwFlags);
            break;
    }

877 878 879
    if (*lplpDirect3DLight)
        IDirect3DLight_AddRef(*lplpDirect3DLight);

880 881 882
    LeaveCriticalSection(&ddraw_cs);

    return *lplpDirect3DLight ? D3D_OK : DDERR_INVALIDPARAMS;
883 884
}

885 886 887 888 889 890 891 892
/*****************************************************************************
 * IDirect3DViewport2 Methods.
 *****************************************************************************/

/*****************************************************************************
 * IDirect3DViewport3::GetViewport2
 *
 * Returns the currently set viewport in a D3DVIEWPORT2 structure.
893
 * Similar to IDirect3DViewport3::GetViewport
894 895 896 897 898 899 900 901 902 903 904 905 906 907
 *
 * Params:
 *  lpData: Pointer to the structure to fill
 *
 * Returns:
 *  D3D_OK on success
 *  DDERR_INVALIDPARAMS if the viewport was set with
 *                      IDirect3DViewport3::SetViewport
 *  DDERR_INVALIDPARAMS if Data is NULL
 *
 *****************************************************************************/
static HRESULT WINAPI
IDirect3DViewportImpl_GetViewport2(IDirect3DViewport3 *iface,
                                   D3DVIEWPORT2 *lpData)
908
{
909
    IDirect3DViewportImpl *This = (IDirect3DViewportImpl *)iface;
910
    DWORD dwSize;
911 912

    TRACE("iface %p, data %p.\n", iface, lpData);
913 914

    EnterCriticalSection(&ddraw_cs);
915 916
    dwSize = lpData->dwSize;
    memset(lpData, 0, dwSize);
917 918 919 920 921 922 923 924 925 926 927 928 929 930 931 932 933
    if (This->use_vp2)
        memcpy(lpData, &(This->viewports.vp2), dwSize);
    else {
        D3DVIEWPORT2 vp2;
        vp2.dwSize = sizeof(vp2);
        vp2.dwX = This->viewports.vp1.dwX;
        vp2.dwY = This->viewports.vp1.dwY;
        vp2.dwWidth = This->viewports.vp1.dwWidth;
        vp2.dwHeight = This->viewports.vp1.dwHeight;
        vp2.dvClipX = 0.0;
        vp2.dvClipY = 0.0;
        vp2.dvClipWidth = 0.0;
        vp2.dvClipHeight = 0.0;
        vp2.dvMinZ = This->viewports.vp1.dvMinZ;
        vp2.dvMaxZ = This->viewports.vp1.dvMaxZ;
        memcpy(lpData, &vp2, dwSize);
    }
934

935 936
    if (TRACE_ON(ddraw))
    {
937
        TRACE("  returning D3DVIEWPORT2 :\n");
938
        _dump_D3DVIEWPORT2(lpData);
939
    }
940 941

    LeaveCriticalSection(&ddraw_cs);
942
    return D3D_OK;
943 944
}

945 946 947 948 949 950 951 952 953 954 955 956 957 958 959
/*****************************************************************************
 * IDirect3DViewport3::SetViewport2
 *
 * Sets the viewport from a D3DVIEWPORT2 structure
 *
 * Params:
 *  lpData: Viewport to set
 *
 * Returns:
 *  D3D_OK on success
 *
 *****************************************************************************/
static HRESULT WINAPI
IDirect3DViewportImpl_SetViewport2(IDirect3DViewport3 *iface,
                                   D3DVIEWPORT2 *lpData)
960
{
961
    IDirect3DViewportImpl *This = (IDirect3DViewportImpl *)iface;
962
    LPDIRECT3DVIEWPORT3 current_viewport;
963

964 965 966 967
    TRACE("iface %p, data %p.\n", iface, lpData);

    if (TRACE_ON(ddraw))
    {
968
        TRACE("  getting D3DVIEWPORT2 :\n");
969
        _dump_D3DVIEWPORT2(lpData);
970 971
    }

972
    EnterCriticalSection(&ddraw_cs);
973 974 975
    This->use_vp2 = 1;
    memset(&(This->viewports.vp2), 0, sizeof(This->viewports.vp2));
    memcpy(&(This->viewports.vp2), lpData, lpData->dwSize);
976 977

    if (This->active_device) {
978 979
        IDirect3DDevice3 *d3d_device3 = (IDirect3DDevice3 *)&This->active_device->IDirect3DDevice3_vtbl;
        IDirect3DDevice3_GetCurrentViewport(d3d_device3, &current_viewport);
980 981 982
        if (current_viewport)
        {
            if ((IDirect3DViewportImpl *)current_viewport == This) viewport_activate(This, FALSE);
983 984
            IDirect3DViewport3_Release(current_viewport);
        }
985
    }
986
    LeaveCriticalSection(&ddraw_cs);
987

988
    return D3D_OK;
989 990
}

991 992 993 994 995 996 997 998 999 1000 1001 1002 1003 1004 1005 1006 1007 1008 1009
/*****************************************************************************
 * IDirect3DViewport3 Methods.
 *****************************************************************************/

/*****************************************************************************
 * IDirect3DViewport3::SetBackgroundDepth2
 *
 * Sets a IDirectDrawSurface4 surface as the background depth surface
 *
 * Params:
 *  lpDDS: Surface to set
 *
 * Returns:
 *  D3D_OK, because it's stub
 *
 *****************************************************************************/
static HRESULT WINAPI
IDirect3DViewportImpl_SetBackgroundDepth2(IDirect3DViewport3 *iface,
                                          IDirectDrawSurface4 *lpDDS)
1010
{
1011 1012
    FIXME("iface %p, surface %p stub!\n", iface, lpDDS);

1013
    return D3D_OK;
1014 1015
}

1016 1017 1018 1019 1020 1021 1022 1023 1024 1025 1026 1027 1028 1029 1030 1031 1032
/*****************************************************************************
 * IDirect3DViewport3::GetBackgroundDepth2
 *
 * Returns the IDirect3DSurface4 interface to the background depth surface
 *
 * Params:
 *  lplpDDS: Address to store the interface pointer at
 *  lpValid: Set to true if a surface is assigned
 *
 * Returns:
 *  D3D_OK because it's a stub
 *
 *****************************************************************************/
static HRESULT WINAPI
IDirect3DViewportImpl_GetBackgroundDepth2(IDirect3DViewport3 *iface,
                                          IDirectDrawSurface4 **lplpDDS,
                                          BOOL *lpValid)
1033
{
1034 1035
    FIXME("iface %p, surface %p, valid %p stub!\n", iface, lplpDDS, lpValid);

1036
    return D3D_OK;
1037
}
1038

1039 1040 1041 1042 1043 1044 1045 1046 1047 1048 1049 1050 1051 1052 1053 1054 1055 1056 1057 1058 1059 1060 1061 1062
/*****************************************************************************
 * IDirect3DViewport3::Clear2
 *
 * Another clearing method
 *
 * Params:
 *  Count: Number of rectangles to clear
 *  Rects: Rectangle array to clear
 *  Flags: Some flags :)
 *  Color: Color to fill the render target with
 *  Z: Value to fill the depth buffer with
 *  Stencil: Value to fill the stencil bits with
 *
 * Returns:
 *
 *****************************************************************************/
static HRESULT WINAPI
IDirect3DViewportImpl_Clear2(IDirect3DViewport3 *iface,
                             DWORD dwCount,
                             LPD3DRECT lpRects,
                             DWORD dwFlags,
                             DWORD dwColor,
                             D3DVALUE dvZ,
                             DWORD dwStencil)
1063
{
1064
    IDirect3DViewportImpl *This = (IDirect3DViewportImpl *)iface;
1065
    HRESULT hr;
1066
    LPDIRECT3DVIEWPORT3 current_viewport;
1067
    IDirect3DDevice3 *d3d_device3;
1068 1069 1070

    TRACE("iface %p, rect_count %u, rects %p, flags %#x, color 0x%08x, depth %.8e, stencil %u.\n",
            iface, dwCount, lpRects, dwFlags, dwColor, dvZ, dwStencil);
1071 1072

    EnterCriticalSection(&ddraw_cs);
1073 1074
    if (This->active_device == NULL) {
        ERR(" Trying to clear a viewport not attached to a device !\n");
1075 1076
        LeaveCriticalSection(&ddraw_cs);
        return D3DERR_VIEWPORTHASNODEVICE;
1077
    }
1078
    d3d_device3 = (IDirect3DDevice3 *)&This->active_device->IDirect3DDevice3_vtbl;
1079 1080 1081
    /* Need to temporarily activate viewport to clear it. Previously active
     * one will be restored afterwards. */
    viewport_activate(This, TRUE);
1082

1083 1084 1085
    hr = IDirect3DDevice7_Clear((IDirect3DDevice7 *)This->active_device,
            dwCount, lpRects, dwFlags, dwColor, dvZ, dwStencil);
    IDirect3DDevice3_GetCurrentViewport(d3d_device3, &current_viewport);
1086
    if(current_viewport) {
1087
        IDirect3DViewportImpl *vp = (IDirect3DViewportImpl *)current_viewport;
1088
        viewport_activate(vp, TRUE);
1089 1090
        IDirect3DViewport3_Release(current_viewport);
    }
1091 1092
    LeaveCriticalSection(&ddraw_cs);
    return hr;
1093 1094
}

1095 1096 1097
/*****************************************************************************
 * The VTable
 *****************************************************************************/
1098

1099
static const struct IDirect3DViewport3Vtbl d3d_viewport_vtbl =
1100
{
1101 1102 1103 1104 1105 1106 1107 1108 1109 1110 1111 1112 1113 1114 1115 1116 1117 1118 1119 1120 1121 1122 1123 1124 1125
    /*** IUnknown Methods ***/
    IDirect3DViewportImpl_QueryInterface,
    IDirect3DViewportImpl_AddRef,
    IDirect3DViewportImpl_Release,
    /*** IDirect3DViewport Methods */
    IDirect3DViewportImpl_Initialize,
    IDirect3DViewportImpl_GetViewport,
    IDirect3DViewportImpl_SetViewport,
    IDirect3DViewportImpl_TransformVertices,
    IDirect3DViewportImpl_LightElements,
    IDirect3DViewportImpl_SetBackground,
    IDirect3DViewportImpl_GetBackground,
    IDirect3DViewportImpl_SetBackgroundDepth,
    IDirect3DViewportImpl_GetBackgroundDepth,
    IDirect3DViewportImpl_Clear,
    IDirect3DViewportImpl_AddLight,
    IDirect3DViewportImpl_DeleteLight,
    IDirect3DViewportImpl_NextLight,
    /*** IDirect3DViewport2 Methods ***/
    IDirect3DViewportImpl_GetViewport2,
    IDirect3DViewportImpl_SetViewport2,
    /*** IDirect3DViewport3 Methods ***/
    IDirect3DViewportImpl_SetBackgroundDepth2,
    IDirect3DViewportImpl_GetBackgroundDepth2,
    IDirect3DViewportImpl_Clear2,
1126
};
1127 1128 1129 1130 1131 1132 1133 1134

void d3d_viewport_init(IDirect3DViewportImpl *viewport, IDirectDrawImpl *ddraw)
{
    viewport->lpVtbl = &d3d_viewport_vtbl;
    viewport->ref = 1;
    viewport->ddraw = ddraw;
    viewport->use_vp2 = 0xff;
}