/*	Direct3D OpenGL driver
 *
 * Copyright 2001-2003 TransGaming Technologies Inc.
 */

/* This is not a self-contained driver - in the same way that OpenGL requires
 * an OS-specific interface like GLX or WGL to operate, this GL driver is
 * supposed to be layered onto a platform-specific Direct3D driver
 */

#include "config.h"

#include <assert.h>
#include <stdlib.h>

#include "winbase.h"
#include "ddrawi.h"
#include "d3d9.h"
#include "d3dhal.h"
#include "d3dgl_private.h"
#include "wine/debug.h"

WINE_DEFAULT_DEBUG_CHANNEL(ddraw);

static BOOL DDHAL_GetDriverInfo(LPDDHAL_GETDRIVERINFO dd_get_driver_info,
				const GUID* guidInfo,
				LPVOID lpData,
				DWORD dwExpectedSize)
{
    DDHAL_GETDRIVERINFODATA data;

    if (!dd_get_driver_info) return FALSE;

    data.dwSize = sizeof(DDHAL_GETDRIVERINFODATA);
    data.dwFlags = 0;
    data.guidInfo = *guidInfo;
    data.dwExpectedSize = dwExpectedSize;
    data.lpvData = lpData;
    data.dwActualSize = 0;
    data.ddRVal = DD_OK;
    data.dwContext = 0; /* XXX */

    dd_get_driver_info(&data);
    if (FAILED(data.ddRVal)) return FALSE;

    *(LPDWORD)lpData = data.dwActualSize;
    return TRUE;
}

GL_D3D_priv* WINAPI D3DGL_Construct(LPD3D_P d3dp, DWORD d3d_version)
{
    LPDDRAWI_DIRECTDRAW_LCL dd_lcl = d3dp->lcl;
    LPDDRAWI_DIRECTDRAW_GBL dd_gbl = dd_lcl->lpGbl;
    GL_D3D_priv* priv;

    TRACE("(%p)\n", d3dp);

    priv = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(GL_D3D_priv));
    priv->d3dp = d3dp;
    priv->dd_lcl = dd_lcl;
    priv->dd_gbl = dd_gbl;
    priv->gl_data = (DD_GL_data*)dd_gbl->dwReserved3;
    priv->lpCaps = (LPD3DHAL_D3DEXTENDEDCAPS)dd_gbl->lpD3DExtendedCaps;
    priv->glFlags = GLF_NEW_TRANSFORM | GLF_NEW_LIGHTING;
    priv->material_flags = GLMF_ALL_MATERIAL_DIRTY;
    priv->texture_flags = GLTF_DEFAULT_TEXCOORD_TRANSFORM;
    priv->d3d_version = d3d_version;

    priv->color_array_size = 8192;
    priv->color_array = VirtualAlloc(NULL, priv->color_array_size,
				     MEM_COMMIT | MEM_RESERVE,
				     PAGE_READWRITE);
    priv->vertex_array_size = 8192;
    priv->vertex_array = VirtualAlloc(NULL, priv->vertex_array_size,
				      MEM_COMMIT | MEM_RESERVE,
				      PAGE_READWRITE);

    DDHAL_GetDriverInfo(d3dp->gdi, &GUID_WineGLD3DCallbacks,
			&priv->callbacks, sizeof(priv->callbacks));

    /* also need to initialize OpenGL */
    priv->block = &priv->state;
    priv->glFlags |= GLF_INITIALIZE;

    return priv;
}

void WINAPI D3DGL_Destruct(GL_D3D_priv* priv)
{
    if (priv->light)
	HeapFree(GetProcessHeap(), 0, priv->light);
    VirtualFree(priv->color_array, 0, MEM_RELEASE);
    VirtualFree(priv->vertex_array, 0, MEM_RELEASE);
}

static const float zero4f[4] = { 0, 0, 0, 0 };
static const float one4f[4] = { 1, 1, 1, 1 };
static const DWORD dummy_texture_pixels;

/* This routine should only initialize anything non-render-state-specific
 * (the default render states are set separately) */
HRESULT D3D_GL_initialize(GL_D3D_priv* priv)
{
    static const GLfloat sgenparams[] = {1.0, 0.0, 0.0, 0.0};
    static const GLfloat tgenparams[] = {0.0, 1.0, 0.0, 0.0};
    static const GLfloat rgenparams[] = {0.0, 0.0, 1.0, 0.0};
    unsigned u;

    TRACE("initializing GL context\n");

#ifndef HAVE_OSMESA
    glFrontFace(GL_CW);
#else
    /* OSMESA y-swap */
    glFrontFace(GL_CCW);
#endif

    glMaterialfv(GL_FRONT_AND_BACK, GL_DIFFUSE, one4f);
    glMaterialfv(GL_FRONT_AND_BACK, GL_SPECULAR, zero4f);
    glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT, zero4f);
    glMaterialfv(GL_FRONT_AND_BACK, GL_EMISSION, zero4f);
    glLightModelfv(GL_LIGHT_MODEL_AMBIENT, zero4f);
    glLightModeli(GL_LIGHT_MODEL_COLOR_CONTROL, GL_SEPARATE_SPECULAR_COLOR);
    priv->current_color_material = GL_AMBIENT_AND_DIFFUSE;

    glPixelStorei(GL_UNPACK_ALIGNMENT, 4);
    glPixelStorei(GL_PACK_ALIGNMENT, 4);

    glDisable(GL_DITHER);
    glEnable(GL_SCISSOR_TEST);

    glMatrixMode(GL_MODELVIEW);
    glPushMatrix();
    glLoadIdentity();

    for (u=0; u < priv->lpCaps->wMaxTextureBlendStages; u++) {
	GLD(priv)->glActiveTextureARB(GL_TEXTURE0_ARB + u);
	glTexGenfv(GL_S, GL_EYE_PLANE, sgenparams);
	glTexGenfv(GL_T, GL_EYE_PLANE, tgenparams);
	glTexGenfv(GL_R, GL_EYE_PLANE, rgenparams);
    }

    glPopMatrix();

    /* if texture_env_combine is available, enable it */
    if (HAVE_GL_EXT(priv, ARB_texture_env_combine) || HAVE_GL_EXT(priv, EXT_texture_env_combine)) {
	for (u=0; u<priv->lpCaps->wMaxTextureBlendStages; u++) {
	    GLD(priv)->glActiveTextureARB(GL_TEXTURE0_ARB + u);
	    glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE_EXT);
	}
    }

    /* create a dummy texture for stages without a real texture */
    glGenTextures(1, &priv->dummy_texture);
    glBindTexture(GL_TEXTURE_2D, priv->dummy_texture);
    glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA,
		 1, 1,
		 0, GL_RGB, GL_UNSIGNED_BYTE,
		 &dummy_texture_pixels);
    priv->texture_target[0] = GL_TEXTURE_2D;
    for (u=1; u<priv->lpCaps->wMaxTextureBlendStages; u++) {
	GLD(priv)->glActiveTextureARB(GL_TEXTURE0_ARB + u);
	glBindTexture(GL_TEXTURE_2D, priv->dummy_texture);
	priv->texture_target[u] = GL_TEXTURE_2D;
    }

    if (HAVE_GL_EXT(priv, ARB_vertex_blend))
	glEnable(GL_WEIGHT_SUM_UNITY_ARB);

    /* glEnable(GL_RESCALE_NORMAL); */
    glDisable(GL_LIGHTING);
    priv->isLightingEnabled = FALSE;
    priv->isVertexProgramEnabled = FALSE;
    priv->isClipPlaneEnabled = 0;
    priv->isFogEnabled = FALSE;
    priv->isFogLinear = FALSE;

    priv->pVertexArray = NULL;
    priv->pNormalArray = NULL;
    priv->pColorArray = NULL;
    priv->pSecondaryColorArray = NULL;
    priv->pFogCoordArray = NULL;
    for (u=0; u < 8; u++) 
	priv->pTexCoordArray[u] = NULL;

    priv->glFlags &= ~GLF_INITIALIZE;

    return D3D_OK;
}

HRESULT WINAPI D3DGL_AllocStateBlock(GL_D3D_priv* priv, LPDWORD lpdwBlock)
{
    struct GL_D3D_state_block *block = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY,
						 sizeof(struct GL_D3D_state_block));
    TRACE("creating state block %p\n", block);
    /* won't bother to allocate a handle yet, just cast the pointer... */
    *lpdwBlock = (DWORD)block;
    return D3D_OK;
}

HRESULT WINAPI D3DGL_FreeStateBlock(GL_D3D_priv* priv, DWORD dwBlock)
{
    /* don't do anything here, let D3DHAL_STATESETDELETE take care of it */
    return D3D_OK;
}

void WINAPI D3DGL_DemolishTexture(GL_D3D_priv* priv, DWORD dwTexture)
{
    TRACE("demolishing 0x%08lx\n", dwTexture);
}

static HRESULT D3D_GL_state_set(GL_D3D_priv* priv, LPD3DHAL_DP2COMMAND cmd, LPVOID data1, LPVOID data2)
{
    LPD3DHAL_DP2STATESET dat = (LPD3DHAL_DP2STATESET)data1;
    struct GL_D3D_state_block *block = (struct GL_D3D_state_block *)dat->dwParam;
    unsigned u, m, c;
    DWORD b;

    switch (dat->dwOperation) {
    case D3DHAL_STATESETBEGIN:
	TRACE("recording to state block %p\n", block);
	priv->block = block;
	break;
    case D3DHAL_STATESETEND:
	TRACE("state block %p complete\n", block);
	priv->block = &priv->state;
	break;
    case D3DHAL_STATESETDELETE:
	TRACE("deleting state block %p\n", block);
	for (u=0; u<block->num_lights; u++) {
	    if (block->light[u].data)
		HeapFree(GetProcessHeap(), 0, block->light[u].data);
	}
	if (block->light)
	    HeapFree(GetProcessHeap(), 0, block->light);
	HeapFree(GetProcessHeap(), 0, block);
	break;
    case D3DHAL_STATESETEXECUTE:
	TRACE("executing state block %p\n", block);
	/* execute render states */
	for (m=0; m<(256/32); m++) if (block->dirty_rs[m]) {
	    c = m*32;
	    b = 1;
	    while (b) {
		if (block->dirty_rs[m] & b) {
		    priv->state.renderstate[c] = block->renderstate[c];
		    priv->state.dirty_rs[m] |= b;

		    /* needed by high-level d3d implementation */
		    priv->d3dp->renderstate[c] = block->renderstate[c];
		}
		c++;
		b <<= 1;
	    }
	    priv->glFlags |= GLF_NEW_RENDER_STATES;
	}
	/* execute texture stage states */
	for (u=0; u<8; u++) if (block->dirty_ts & (1 << u)) {
	    for (m=0; m<(64/32); m++) if (block->dirty_tss[u][m]) {
		c = m*32;
		b = 1;
		while (b) {
		    if (block->dirty_tss[u][m] & b) {
			priv->state.texstagestate[u][c] = block->texstagestate[u][c];
			priv->state.dirty_tss[u][m] |= b;

			priv->d3dp->texstagestate[u][c] = block->texstagestate[u][c];
		    }
		    c++;
		    b <<= 1;
		}
	    }
	    priv->state.dirty_ts |= 1 << u;
	    priv->glFlags |= GLF_NEW_TEXTURE_STATES;
	}
	/* execute transforms */
	for (u=0; u<8; u++) if (block->dirty_xfrm & (1 << u)) {
	    priv->state.xfrm[u] = block->xfrm[u];
	    priv->state.dirty_xfrm |= (1 << u);
	    priv->glFlags |= GLF_NEW_TRANSFORM;
	    if (u == D3DTRANSFORMSTATE_VIEW) priv->glFlags |= GLF_NEW_VIEW;

	    priv->d3dp->xfrm[u] = block->xfrm[u];
	}
	for (u=0; u<8; u++) if (block->dirty_txfrm & (1 << u)) {
	    priv->state.txfrm[u] = block->txfrm[u];
	    priv->state.dirty_txfrm |= (1 << u);
	    priv->glFlags |= GLF_NEW_TEXCOORD_TRANSFORM;
	    priv->texture_flags &= ~GLTF_DEFAULT_TEXCOORD_TRANSFORM;

	    priv->d3dp->txfrm[u] = block->txfrm[u];
	}
	/* execute viewport */
	if (block->dirty_viewport) {
	    memcpy(&priv->state.viewport, &block->viewport, sizeof(priv->state.viewport));
	    priv->state.dirty_viewport = TRUE;
	    priv->glFlags |= GLF_NEW_TRANSFORM;

	    priv->d3dp->viewport = block->viewport;
	}
	/* execute material */
	if (block->dirty_material) {
	    D3D_GL_set_current_material(priv, &block->material);

	    priv->d3dp->material = block->material;
	}
	/* execute clip planes */
	for (u=0; u<32; u++) if (block->dirty_clip_planes & (1 << u)) {
	    memcpy(priv->state.clipplane[u], block->clipplane[u], sizeof(priv->state.clipplane[u]));
	    priv->state.dirty_clip_planes |= (1 << u);
	    priv->glFlags |= GLF_NEW_CLIP_PLANES;

	    memcpy(priv->d3dp->clipplane[u], block->clipplane[u], sizeof(priv->d3dp->clipplane[u]));
	}
	/* execute lights */
	for (u=0; u<block->num_lights; u++) {
	    if (block->light[u].dwEnable & GL_D3D_LIGHT_ENABLE) {
		D3D_GL_light_enable(priv, u);
	    }
	    if (block->light[u].dwEnable & GL_D3D_LIGHT_DISABLE) {
		D3D_GL_light_disable(priv, u);
	    }
	    if (block->light[u].data) {
		D3D_GL_set_light_data(priv, u, block->light[u].data);
	    }
	}
	/* execute vertex shader */
	if (block->dirty_vs && priv->d3d_version >= 9) {
	    TRACE("dx9 vs: %08lx\n", block->vertex_shader);
	    D3D_GL_set_current_vertex_shader_func(priv, block->vertex_shader);
	}
	else if (block->dirty_vs) {
	    TRACE("dx8 vs: %08lx\n", block->vertex_shader);
	    D3D_GL_set_current_vertex_shader(priv, block->vertex_shader);

	    if (IS_VS(block->vertex_shader)) {
		priv->d3dp->vertex_shader = VS_FROM_HANDLE(block->vertex_shader);
		priv->d3dp->vertex_fvf = priv->d3dp->vertex_shader->FVF;
	    } else {
		priv->d3dp->vertex_shader = NULL;
		priv->d3dp->vertex_fvf = block->vertex_shader;
	    }
	}
	if (block->dirty_vsd && priv->d3d_version >= 9) {
	    TRACE("dx9 vsd: %08lx\n", block->vertex_decl);
	    D3D_GL_set_current_vertex_shader_decl(priv, block->vertex_decl);
	}
	/* execute vertex shader constants */
	for (m=0; m<(96/32); m++) if (block->dirty_vsc[m]) {
	    c = m*32;
	    b = 1;
	    while (b) {
		if (block->dirty_vsc[m] & b) {
		    memcpy(priv->state.vertex_const + 4*c, block->vertex_const + 4*c, 4*sizeof(float));
		    priv->state.dirty_vsc[m] |= b;

		    if (priv->d3dp->vertex_const)
			memcpy(priv->d3dp->vertex_const + 4*c, block->vertex_const + 4*c, 4*sizeof(float));
		}
		c++;
		b <<= 1;
	    }
	    priv->glFlags |= GLF_NEW_VSHADER_CONSTS;
	}
	break;
    case D3DHAL_STATESETCAPTURE:
	TRACE("capturing state block %p\n", block);
	/* capture render states */
	memcpy(block->renderstate, priv->state.renderstate, sizeof(block->renderstate));
	/* capture texture stage states */
	memcpy(block->texstagestate, priv->state.texstagestate, sizeof(block->texstagestate));
	/* capture transforms */
	memcpy(block->xfrm, priv->state.xfrm, sizeof(block->xfrm));
	memcpy(block->txfrm, priv->state.txfrm, sizeof(block->txfrm));
	/* capture viewport */
	memcpy(&block->viewport, &priv->state.viewport, sizeof(block->viewport));
	/* capture material */
	memcpy(&block->material, &priv->state.material, sizeof(block->material));
	/* capture clip planes */
	memcpy(block->clipplane, priv->state.clipplane, sizeof(block->clipplane));
	/* capture lights */
	for (u=0; u<block->num_lights; u++) {
	    if (block->light[u].dwEnable) {
		block->light[u].dwEnable = (priv->light[u].dwFlags & D3DLIGHT_ACTIVE) ?
					   GL_D3D_LIGHT_ENABLE : GL_D3D_LIGHT_DISABLE;
	    }
	    if (block->light[u].data) {
		*block->light[u].data = priv->light[u].l;
	    }
	}
	/* capture vertex shader */
	block->vertex_shader = priv->state.vertex_shader;
	/* capture vertex shader constants */
	memcpy(block->vertex_const, priv->state.vertex_const, sizeof(block->vertex_const));
	break;
    default:
	break;
    }
    return D3D_OK;
}

typedef HRESULT (*GL_dp2_handler)(GL_D3D_priv* priv, LPD3DHAL_DP2COMMAND cmd, LPVOID data1, LPVOID data2);

#define PRIM 1
#define STAT 2
#define OTHR 3

static const struct {
    GL_dp2_handler func;
    int type;
    const char*name;
} GL_dp2_func[] = {
    {NULL,				OTHR, "INIT"},
    {D3D_GL_draw,			PRIM, "POINTS"},
    {NULL,				PRIM, "INDEXEDLINELIST"},
    {NULL,				PRIM, "INDEXEDTRIANGLELIST"},
    {NULL}, {NULL}, {NULL}, {NULL},
    {D3D_GL_set_render_state,		STAT, "RENDERSTATE"},
    {NULL}, {NULL}, {NULL}, {NULL}, {NULL}, {NULL},
    {D3D_GL_draw,			PRIM, "LINELIST"},
    {D3D_GL_draw,			PRIM, "LINESTRIP"},
    {D3D_GL_draw,			PRIM, "INDEXEDLINESTRIP"},
    {D3D_GL_draw,			PRIM, "TRIANGLELIST"},
    {D3D_GL_draw,			PRIM, "TRIANGLESTRIP"},
    {D3D_GL_draw,			PRIM, "INDEXEDTRIANGLESTRIP"},
    {D3D_GL_draw,			PRIM, "TRIANGLEFAN"},
    {D3D_GL_draw,			PRIM, "INDEXEDTRIANGLEFAN"},
    {NULL,				PRIM, "TRIANGLEFAN_IMM"},
    {NULL,				PRIM, "LINELIST_IMM"},
    {D3D_GL_set_texture_stage_state,	STAT, "TEXTURESTAGESTATE"},
    {D3D_GL_draw,			PRIM, "INDEXEDTRIANGLELIST2"},
    {D3D_GL_draw,			PRIM, "INDEXEDLINELIST2"},
    {D3D_GL_viewport_info,		STAT, "VIEWPORTINFO"},
    {NULL,				STAT, "WINFO"},
    {D3D_GL_set_palette,		OTHR, "SETPALETTE"},
    {D3D_GL_update_palette,		OTHR, "UPDATEPALETTE"},
    {D3D_GL_z_range, 			STAT, "ZRANGE"},
    {D3D_GL_set_material,		STAT, "SETMATERIAL"},
    {D3D_GL_set_light,			STAT, "SETLIGHT"},
    {D3D_GL_create_light,		OTHR, "CREATELIGHT"},
    {D3D_GL_set_transform,		STAT, "SETTRANSFORM"},
    {NULL,				OTHR, "EXT"},
    {D3D_GL_async_texblt,		OTHR, "TEXBLT"},
    {D3D_GL_state_set,			OTHR, "STATESET"},
    {NULL,				OTHR, "SETPRIORITY"},
    {NULL,				OTHR, "SETRENDERTARGET"},
    {D3D_GL_clear,			PRIM, "CLEAR"},
    {NULL,				OTHR, "SETTEXLOD"},
    {D3D_GL_set_clip_plane,		STAT, "SETCLIPPLANE"},
    {D3D_GL_create_vertex_shader,	OTHR, "CREATEVERTEXSHADER"}, 
    {D3D_GL_delete_vertex_shader,	OTHR, "DELETEVERTEXSHADER"},
    {D3D_GL_set_vertex_shader,		STAT, "SETVERTEXSHADER"},
    {D3D_GL_set_vertex_shader_const,	STAT, "SETVERTEXSHADERCONST"},
    {NULL,				OTHR, "SETSTREAMSOURCE"},
    {NULL,				OTHR, "SETSTREAMSOURCEUM"},
    {NULL,				OTHR, "SETINDICES"},
    {NULL,				PRIM, "DRAWPRIMITIVE"},
    {NULL,				PRIM, "DRAWINDEXEDPRIMITIVE"},
    {NULL,				OTHR, "CREATEPIXELSHADER"},
    {NULL,				OTHR, "DELETEPIXELSHADER"},
    {NULL,				STAT, "SETPIXELSHADER"},
    {NULL,				STAT, "SETPIXELSHADERCONST"},
    {NULL,				PRIM, "CLIPPEDTRIANGLEFAN"},
    {NULL,				PRIM, "DRAWPRIMITIVE2"},
    {NULL,				PRIM, "DRAWINDEXEDPRIMITIVE2"},
    {NULL,				PRIM, "DRAWRECTPATCH"},
    {NULL,				PRIM, "DRAWTRIPATCH"},
    {NULL,				OTHR, "VOLUMEBLT"},
    {NULL,				OTHR, "BUFFERBLT"},
    {NULL,				OTHR, "MULTIPLYTRANSFORM"},
    {NULL,				OTHR, "ADDDIRTYRECT"},
    {NULL,				OTHR, "ADDDIRTYBOX"},
    {NULL}, {NULL}, {NULL},
    {D3D_GL_create_vertex_shader_decl,	OTHR, "CREATEVERTEXSHADERDECL"},
    {D3D_GL_delete_vertex_shader_decl,	OTHR, "DELETEVERTEXSHADERDECL"},
    {D3D_GL_set_vertex_shader_decl,	STAT, "SETVERTEXSHADERDECL"},
    {D3D_GL_create_vertex_shader_func,	OTHR, "CREATEVERTEXSHADERFUNC"},
    {D3D_GL_delete_vertex_shader_func,	OTHR, "DELETEVERTEXSHADERFUNC"},
    {D3D_GL_set_vertex_shader_func,	STAT, "SETVERTEXSHADERFUNC"},
    {NULL,				STAT, "SETVERTEXSHADERCONSTI"},
    {NULL},
    {NULL,				STAT, "SETSCISSORRECT"},
    {NULL,				OTHR, "SETSTREAMSOURCE2"},
    {NULL,				OTHR, "BLT"},
    {NULL,				OTHR, "COLORFILL"},
    {NULL,				STAT, "SETVERTEXSHADERCONSTB"},
    {NULL,				OTHR, "CREATEQUERY"},
    {NULL,				OTHR, "SETRENDERTARGET2"},
    {NULL,				OTHR, "SETDEPTHSTENCIL"},
    {NULL,				OTHR, "RESPONSECONTINUE"},
    {NULL,				OTHR, "RESPONSEQUERY"},
    {NULL,				OTHR, "GENERATEMIPSUBLEVELS"},
    {NULL,				OTHR, "DELETEQUERY"},
    {NULL,				OTHR, "ISSUEQUERY"},
    {NULL},
    {NULL,				STAT, "SETPIXELSHADERCONSTI"},
    {NULL,				STAT, "SETPIXELSHADERCONSTB"},
    {NULL,				OTHR, "SETSTREAMSOURCEFREQ"},
    {NULL,				OTHR, "SURFACEBLT"}
};

HRESULT WINAPI D3DGL_StartDP2SD(GL_D3D_priv* priv, DWORD dwFlags,
				DWORD dwVertexType, LPVOID lpVertices,
				DWORD dwVertexOffset, DWORD dwVertexLength,
				DWORD dwReqVertexBufSize, DWORD dwReqCommandBufSize,
				LPD3DDRAWPRIMITIVESTRIDEDDATA lpStridedData)
{
    priv->glFlags &= ~(GLF_STRIDED_DATA|GLF_VERTEX_BUFFER);
    priv->dp2_VertexBuffer = NULL;
    priv->dp2_lpVertices = NULL;

    if (lpStridedData)
    {
	priv->sd = *lpStridedData;
	priv->glFlags |= GLF_STRIDED_DATA;
    }
    else if (!(dwFlags & D3DHALDP2_USERMEMVERTICES))
    {
	priv->dp2_VertexBuffer = lpVertices;
	priv->glFlags |= GLF_VERTEX_BUFFER;
    }
    else
    {
	priv->dp2_lpVertices = lpVertices;
    }

    if (GL_CHECK_NEW_TNL(dwVertexType, priv->dp2_dwVertexType))
	priv->glFlags |= GLF_NEW_TNL_MODE;
    if (GL_CHECK_NEW_TEXCOORD(dwVertexType, priv->dp2_dwVertexType))
	priv->glFlags |= GLF_NEW_TEXCOORD_TRANSFORM;

    priv->dp2_dwVertexType = dwVertexType;
    priv->dp2_dwVertexOffset = dwVertexOffset;
    priv->dp2_dwVertexLength = dwVertexLength;
    priv->dp2_dwFlags = dwFlags;

    return D3D_OK;
}

HRESULT WINAPI D3DGL_StartDP2(GL_D3D_priv* priv, DWORD dwFlags,
			      DWORD dwVertexType, LPVOID lpVertices,
			      DWORD dwVertexOffset, DWORD dwVertexLength,
			      DWORD dwReqVertexBufSize, DWORD dwReqCommandBufSize)
{
    return D3DGL_StartDP2SD(priv, dwFlags, dwVertexType, lpVertices,
			    dwVertexOffset, dwVertexLength,
			    dwReqVertexBufSize, dwReqCommandBufSize, NULL);
}

HRESULT WINAPI D3DGL_EmitDP2(GL_D3D_priv* priv, LPD3DHAL_DP2COMMAND cmd,
			     LPVOID data1, DWORD dwSize1, LPVOID data2, DWORD dwSize2)
{
    GL_dp2_handler func;

    func = GL_dp2_func[cmd->bCommand].func;
    if (!func) {
	FIXME("handler for %s not implemented\n", GL_dp2_func[cmd->bCommand].name);
	/* return D3DERR_EXECUTE_FAILED; */
	return D3D_OK; /* pretend success */
    }
    if (GL_IN_BLOCK(priv) && GL_dp2_func[cmd->bCommand].type == STAT) {
	/* record to state block */
	TRACE("saving state %s\n", GL_dp2_func[cmd->bCommand].name);
	return func(priv, cmd, data1, data2);
    }
    if (GL_dp2_func[cmd->bCommand].type == PRIM && priv->d3dp->in_scene) {
	/* draw primitive */
	return func(priv, cmd, data1, data2);
    }
    if (func == D3D_GL_draw) return D3DERR_INVALIDCALL;
    return func(priv, cmd, data1, data2);
}

HRESULT WINAPI D3DGL_FlushDP2(GL_D3D_priv* priv)
{
#if 0
    if (!priv->d3dp->in_scene) return D3D_OK;
    CHECK_GL_ERROR("FlushDP2 (enter)");
    glFlush();
    CHECK_GL_ERROR("FlushDP2 (exit)");
#endif
    return D3D_OK;
}
