/*==========================================================================;
 *
 *  Copyright (C) 1998 Microsoft Corporation.  All Rights Reserved.
 *
 *  File:   haltrans.cpp
 *  Content:    Direct3D HAL transform handler
 *
 ***************************************************************************/

#include "pch.cpp"
#pragma hdrstop

//---------------------------------------------------------------------
// Update pre-computed constants related to viewport
//
// This functions should be called every time the viewport parameters are
// changed
//
// Notes:
//      1. scaleY and offsetY are computed to flip Y axes from up to down.
//      2. Mclip matrix is computed multiplied by Mshift matrix
//
const D3DVALUE SMALL_NUMBER = 0.000001f;

void UpdateViewportCache(LPDIRECT3DDEVICEI device, D3DVIEWPORT2 *data)
{
    // Bail if we are going to cause any divide by zero exceptions.
    // The likely reason is that we have a bogus viewport set by
    // TLVertex execute buffer app.
    if(data->dwWidth == 0 ||
        data->dwHeight == 0 ||
        FLOAT_EQZ(data->dvClipWidth) ||
        FLOAT_EQZ(data->dvClipHeight) ||
        data->dvMaxZ - data->dvMinZ == D3DVAL(0.f))
        return;
    D3DFE_VIEWPORTCACHE *cache = &device->vcache;
    cache->dvX = D3DVAL(data->dwX);
    cache->dvY = D3DVAL(data->dwY);
    cache->dvWidth = D3DVAL(data->dwWidth);
    cache->dvHeight = D3DVAL(data->dwHeight);
    cache->mclip11 = D3DVAL(1.0) / data->dvClipWidth;
    cache->mclip41 = - cache->mclip11 * data->dvClipX;
    cache->mclip22 = D3DVAL(1) / data->dvClipHeight;
    cache->mclip42 = D3DVAL(1) - cache->mclip22 * data->dvClipY;
    cache->mclip33 = D3DVAL(1) / (data->dvMaxZ - data->dvMinZ);
    cache->mclip43 = - data->dvMinZ * cache->mclip33;
    cache->scaleX  = cache->dvWidth;
    cache->scaleY  = - cache->dvHeight;
    cache->offsetX = cache->dvX;
    cache->offsetY = cache->dvY + cache->dvHeight;
    // Small offset is added to prevent generation of negative screen
    // coordinates (this could happen because of precision errors).
    // Not needed (or wanted) for devices which do guardband.
    if (IS_HW_DEVICE(device))
    {
        cache->offsetX += SMALL_NUMBER;
        cache->offsetY += SMALL_NUMBER;
    }
    device->dwFEFlags |= D3DFE_VIEWPORT_DIRTY | D3DFE_INVERSEMCLIP_DIRTY;
    cache->scaleXi = D3DVAL(1) / cache->scaleX;
    cache->scaleYi = D3DVAL(1) / cache->scaleY;
    cache->minX = cache->dvX;
    cache->maxX = cache->dvX + cache->dvWidth;
    cache->minY = cache->dvY;
    cache->maxY = cache->dvY + cache->dvHeight;
    cache->minXi = FTOI(cache->minX);
    cache->maxXi = FTOI(cache->maxX);
    cache->minYi = FTOI(cache->minY);
    cache->maxYi = FTOI(cache->maxY);
    if (device->dwDeviceFlags & D3DDEV_GUARDBAND)
    {
        LPD3DHAL_D3DEXTENDEDCAPS lpCaps = device->lpD3DExtendedCaps;

        // Because we clip by guard band window we have to use its extents
        cache->minXgb = lpCaps->dvGuardBandLeft;
        cache->maxXgb = lpCaps->dvGuardBandRight;
        cache->minYgb = lpCaps->dvGuardBandTop;
        cache->maxYgb = lpCaps->dvGuardBandBottom;

        D3DVALUE w = 2.0f / cache->dvWidth;
        D3DVALUE h = 2.0f / cache->dvHeight;
        D3DVALUE ax1 = -lpCaps->dvGuardBandLeft   * w + 1.0f;
        D3DVALUE ax2 =  lpCaps->dvGuardBandRight  * w - 1.0f;
        D3DVALUE ay1 =  lpCaps->dvGuardBandBottom * h - 1.0f;
        D3DVALUE ay2 = -lpCaps->dvGuardBandTop * h + 1.0f;
        cache->gb11 = 2.0f / (ax1 + ax2);
        cache->gb41 = cache->gb11 * (ax1 - 1.0f) * 0.5f;
        cache->gb22 = 2.0f / (ay1 + ay2);
        cache->gb42 = cache->gb22 * (ay1 - 1.0f) * 0.5f;

        cache->Kgbx1 = 0.5f * (1.0f - ax1);
        cache->Kgbx2 = 0.5f * (1.0f + ax2);
        cache->Kgby1 = 0.5f * (1.0f - ay1);
        cache->Kgby2 = 0.5f * (1.0f + ay2);
    }
    else
    {
        cache->minXgb = cache->minX;
        cache->maxXgb = cache->maxX;
        cache->minYgb = cache->minY;
        cache->maxYgb = cache->maxY;
    }
}
//---------------------------------------------------------------------
HRESULT
D3DHAL_MatrixCreate(LPDIRECT3DDEVICEI lpDevI, LPD3DMATRIXHANDLE lphMat)
{
    LPD3DMATRIXI lpMat;
    HRESULT ret;

    if ((ret = D3DMalloc((void**)&lpMat, sizeof(D3DMATRIXI))) != DD_OK)
    {
        return ret;
    }

    setIdentity(lpMat);

    LIST_INSERT_ROOT(&lpDevI->transform.matrices, lpMat, link);
    *lphMat = (DWORD)((ULONG_PTR)lpMat);

    return (D3D_OK);
}
//---------------------------------------------------------------------
HRESULT
D3DHAL_MatrixDestroy(LPDIRECT3DDEVICEI lpDevI, D3DMATRIXHANDLE hMat)
{
    LPD3DMATRIXI lpMat = (LPD3DMATRIXI)ULongToPtr(hMat);
    LIST_DELETE(lpMat, link);
    D3DFree(lpMat);

    return D3D_OK;
}
//---------------------------------------------------------------------
HRESULT
D3DHAL_MatrixSetData(LPDIRECT3DDEVICEI lpDevI, D3DMATRIXHANDLE hMat,
                     LPD3DMATRIX lpMat)
{
    D3DFE_TRANSFORM& TRANSFORM = lpDevI->transform;

    LPD3DMATRIXI lpDstMat;

    lpDstMat = HANDLE_TO_MAT(lpDevI, hMat);

    if (!lpDstMat)
    {
        return D3DERR_MATRIX_SETDATA_FAILED;
    }

    *(D3DMATRIX*)lpDstMat = *lpMat;

    if (hMat == TRANSFORM.hWorld)
        D3DFE_SetMatrixWorld(lpDevI, lpMat);
    else
        if (hMat == TRANSFORM.hView)
            D3DFE_SetMatrixView(lpDevI, lpMat);
        else
            if (hMat == TRANSFORM.hProj)
                D3DFE_SetMatrixProj(lpDevI, lpMat);

    return (D3D_OK);
}
//---------------------------------------------------------------------
HRESULT
D3DHAL_MatrixGetData(LPDIRECT3DDEVICEI lpDevI, D3DMATRIXHANDLE hMat,
                     LPD3DMATRIX lpMat)
{
    LPD3DMATRIXI lpSrcMat = HANDLE_TO_MAT(lpDevI, hMat);

    if (!lpSrcMat)
        return D3DERR_MATRIX_GETDATA_FAILED;

    *lpMat = *(D3DMATRIX*)lpSrcMat;
    return (D3D_OK);
}