882 lines
25 KiB
C++
882 lines
25 KiB
C++
#include "pch.cpp"
|
|
#pragma hdrstop
|
|
|
|
#include "glrend.h"
|
|
#include "util.h"
|
|
#include "globals.h"
|
|
#include "gltk.h"
|
|
|
|
// #define NO_LIGHTING
|
|
|
|
// Translation tables for D3D -> OpenGL
|
|
GLenum eDepthFuncTranslate[] =
|
|
{
|
|
0,
|
|
GL_NEVER,
|
|
GL_LESS,
|
|
GL_EQUAL,
|
|
GL_LEQUAL,
|
|
GL_GREATER,
|
|
GL_NOTEQUAL,
|
|
GL_GEQUAL,
|
|
GL_ALWAYS
|
|
};
|
|
GLenum eTextureFilterTranslate[] =
|
|
{
|
|
0,
|
|
GL_NEAREST,
|
|
GL_LINEAR,
|
|
GL_NEAREST_MIPMAP_NEAREST,
|
|
GL_NEAREST_MIPMAP_LINEAR,
|
|
GL_LINEAR_MIPMAP_NEAREST,
|
|
GL_LINEAR_MIPMAP_LINEAR
|
|
};
|
|
GLenum eBlendFuncTranslate[] =
|
|
{
|
|
0,
|
|
GL_ZERO,
|
|
GL_ONE,
|
|
GL_SRC_COLOR,
|
|
GL_ONE_MINUS_SRC_COLOR,
|
|
GL_SRC_ALPHA,
|
|
GL_ONE_MINUS_SRC_ALPHA,
|
|
GL_DST_ALPHA,
|
|
GL_ONE_MINUS_DST_ALPHA,
|
|
GL_DST_COLOR,
|
|
GL_ONE_MINUS_DST_COLOR,
|
|
GL_SRC_ALPHA_SATURATE
|
|
};
|
|
|
|
GlWindow::GlWindow(void)
|
|
{
|
|
_iLight = 0;
|
|
memset(&_drcBound, 0, sizeof(_drcBound));
|
|
|
|
_dmView = dmIdentity;
|
|
_dmProjection = dmIdentity;
|
|
|
|
_pfWorld = NULL;
|
|
_pfView = NULL;
|
|
_pfProjection = NULL;
|
|
memset(&_gltkw, 0, sizeof(_gltkw));
|
|
_hrc = NULL;
|
|
}
|
|
|
|
BOOL GlWindow::Initialize(int x, int y,
|
|
UINT uiWidth, UINT uiHeight, UINT uiBuffers)
|
|
{
|
|
PIXELFORMATDESCRIPTOR pfd;
|
|
int iFmt;
|
|
|
|
if (gltkCreateWindow(app.hDlg, "OpenGL Performance Test",
|
|
x, y, uiWidth, uiHeight, &_gltkw) == NULL)
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
memset(&pfd, 0, sizeof(pfd));
|
|
pfd.nSize = sizeof(pfd);
|
|
pfd.nVersion = 1;
|
|
pfd.dwFlags = PFD_DRAW_TO_WINDOW | PFD_SUPPORT_OPENGL;
|
|
if (uiBuffers & REND_BUFFER_BACK)
|
|
{
|
|
pfd.dwFlags |= PFD_DOUBLEBUFFER;
|
|
}
|
|
pfd.iPixelType = PFD_TYPE_RGBA;
|
|
pfd.cColorBits = GetDeviceCaps(_gltkw.hdc, BITSPIXEL)*
|
|
GetDeviceCaps(_gltkw.hdc, PLANES);
|
|
if (uiBuffers & REND_BUFFER_Z)
|
|
{
|
|
pfd.cDepthBits = 16;
|
|
}
|
|
pfd.cAuxBuffers = 0;
|
|
pfd.iLayerType = PFD_MAIN_PLANE;
|
|
iFmt = ChoosePixelFormat(_gltkw.hdc, &pfd);
|
|
if (iFmt == 0)
|
|
{
|
|
return FALSE;
|
|
}
|
|
if (DescribePixelFormat(_gltkw.hdc, iFmt, sizeof(pfd), &pfd) == 0)
|
|
{
|
|
return FALSE;
|
|
}
|
|
if (!SetPixelFormat(_gltkw.hdc, iFmt, &pfd))
|
|
{
|
|
return FALSE;
|
|
}
|
|
if ((pfd.dwFlags & PFD_NEED_PALETTE) &&
|
|
gltkCreateRGBPalette(&_gltkw) == NULL)
|
|
{
|
|
return FALSE;
|
|
}
|
|
_hrc = wglCreateContext(_gltkw.hdc);
|
|
if (_hrc == NULL)
|
|
{
|
|
return FALSE;
|
|
}
|
|
if (!wglMakeCurrent(_gltkw.hdc, _hrc))
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
if (!SetViewport(0, 0, uiWidth, uiHeight))
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
glEnableClientState(GL_VERTEX_ARRAY);
|
|
glEnableClientState(GL_TEXTURE_COORD_ARRAY);
|
|
|
|
glEnable(GL_CULL_FACE);
|
|
glEnable(GL_DEPTH_TEST);
|
|
glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST);
|
|
|
|
#if 0
|
|
// Unclear what D3D's model is
|
|
glLightModeli(GL_LIGHT_MODEL_LOCAL_VIEWER, GL_TRUE);
|
|
#endif
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
GlWindow::~GlWindow(void)
|
|
{
|
|
if (_hrc != NULL)
|
|
{
|
|
wglDeleteContext(_hrc);
|
|
}
|
|
if (_gltkw.hwnd != NULL)
|
|
{
|
|
DestroyWindow(_gltkw.hwnd);
|
|
}
|
|
}
|
|
|
|
void GlWindow::Release(void)
|
|
{
|
|
delete this;
|
|
}
|
|
|
|
BOOL GlWindow::SetViewport(int x, int y, UINT uiWidth, UINT uiHeight)
|
|
{
|
|
_drcBound.x1 = x;
|
|
_drcBound.y1 = y;
|
|
_drcBound.x2 = x+uiWidth;
|
|
_drcBound.y2 = y+uiHeight;
|
|
|
|
glViewport(x, y, uiWidth, uiHeight);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
RendLight* GlWindow::NewLight(int iType)
|
|
{
|
|
GlLight* prlight;
|
|
GLenum eLight;
|
|
|
|
eLight = GL_LIGHT0+_iLight;
|
|
prlight = new GlLight(iType, eLight);
|
|
if (prlight != NULL)
|
|
{
|
|
_iLight++;
|
|
}
|
|
return prlight;
|
|
}
|
|
|
|
RendTexture* GlWindow::NewTexture(char* pszFile, UINT uiFlags)
|
|
{
|
|
GlTexture* pgtex;
|
|
GLuint uiTexObj;
|
|
Image im;
|
|
ImageFormat ifmt;
|
|
|
|
pgtex = new GlTexture;
|
|
if (pgtex == NULL)
|
|
{
|
|
return NULL;
|
|
}
|
|
|
|
if (!pgtex->Initialize(pszFile))
|
|
{
|
|
delete pgtex;
|
|
return NULL;
|
|
}
|
|
|
|
return pgtex;
|
|
}
|
|
|
|
RendExecuteBuffer* GlWindow::NewExecuteBuffer(UINT cbSize, UINT uiFlags)
|
|
{
|
|
GlExecuteBuffer* pgeb;
|
|
|
|
pgeb = new GlExecuteBuffer;
|
|
if (pgeb == NULL)
|
|
{
|
|
return NULL;
|
|
}
|
|
|
|
if (!pgeb->Initialize(cbSize, uiFlags))
|
|
{
|
|
delete pgeb;
|
|
return NULL;
|
|
}
|
|
|
|
return pgeb;
|
|
}
|
|
|
|
RendMatrix* GlWindow::NewMatrix(void)
|
|
{
|
|
return new GlMatrix(this);
|
|
}
|
|
|
|
RendMaterial* GlWindow::NewMaterial(UINT nColorEntries)
|
|
{
|
|
return new GlMaterial;
|
|
}
|
|
|
|
BOOL GlWindow::BeginScene(void)
|
|
{
|
|
_eTexMin = DEF_TEX_MIN;
|
|
_eTexMag = DEF_TEX_MAG;
|
|
_eSrcBlend = GL_ONE;
|
|
_eDstBlend = GL_ZERO;
|
|
glDisable(GL_TEXTURE_2D);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
BOOL GlWindow::Execute(RendExecuteBuffer* preb)
|
|
{
|
|
BOOL bSucc;
|
|
|
|
#ifdef GL_EXT_clip_volume_hint
|
|
int i;
|
|
|
|
glGetIntegerv(GL_CLIP_VOLUME_CLIPPING_HINT_EXT, &i);
|
|
glHint(GL_CLIP_VOLUME_CLIPPING_HINT_EXT, GL_FASTEST);
|
|
#endif
|
|
|
|
bSucc = DoExecute(preb);
|
|
|
|
#ifdef GL_EXT_clip_volume_hint
|
|
glHint(GL_CLIP_VOLUME_CLIPPING_HINT_EXT, i);
|
|
#endif
|
|
|
|
return bSucc;
|
|
}
|
|
|
|
BOOL GlWindow::ExecuteClipped(RendExecuteBuffer* preb)
|
|
{
|
|
return DoExecute(preb);
|
|
}
|
|
|
|
BOOL GlWindow::EndScene(D3DRECT* pdrcBound)
|
|
{
|
|
if (pdrcBound != NULL)
|
|
{
|
|
if (app.uiCurrentTest == THROUGHPUT_TEST)
|
|
{
|
|
// The throughput test only draws on a small portion of the
|
|
// viewport so it's critical that we provide a small
|
|
// bound
|
|
pdrcBound->x1 = _drcBound.x1+(WIN_WIDTH*34/100);
|
|
pdrcBound->y1 = _drcBound.y1+(WIN_HEIGHT*34/100);
|
|
pdrcBound->x2 = _drcBound.x2-(WIN_WIDTH*34/100);
|
|
pdrcBound->y2 = _drcBound.y2-(WIN_HEIGHT*34/100);
|
|
}
|
|
else
|
|
{
|
|
// All other tests fill the whole viewport
|
|
*pdrcBound = _drcBound;
|
|
}
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
BOOL GlWindow::Clear(UINT uiBuffers, D3DRECT* pdrc)
|
|
{
|
|
GLbitfield grfMask;
|
|
|
|
if (pdrc != NULL)
|
|
{
|
|
glEnable(GL_SCISSOR_TEST);
|
|
glScissor(pdrc->x1, pdrc->y1, pdrc->x2-pdrc->x1, pdrc->y2-pdrc->y1);
|
|
}
|
|
|
|
// Assumes window is always double-buffered
|
|
if (uiBuffers & REND_BUFFER_FRONT)
|
|
{
|
|
glDrawBuffer(GL_FRONT);
|
|
glClear(GL_COLOR_BUFFER_BIT);
|
|
glDrawBuffer(GL_BACK);
|
|
}
|
|
|
|
grfMask = 0;
|
|
if (uiBuffers & REND_BUFFER_BACK)
|
|
{
|
|
grfMask |= GL_COLOR_BUFFER_BIT;
|
|
}
|
|
if (uiBuffers & REND_BUFFER_Z)
|
|
{
|
|
grfMask |= GL_DEPTH_BUFFER_BIT;
|
|
}
|
|
glClear(grfMask);
|
|
|
|
if (pdrc != NULL)
|
|
{
|
|
glDisable(GL_SCISSOR_TEST);
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
BOOL GlWindow::Flip(void)
|
|
{
|
|
SwapBuffers(_gltkw.hdc);
|
|
return TRUE;
|
|
}
|
|
|
|
// #define SHOW_BOUND
|
|
|
|
BOOL GlWindow::CopyForward(D3DRECT* pdrc)
|
|
{
|
|
#ifdef GL_WIN_swap_hint
|
|
if (pdrc != NULL && glAddSwapHintRectWIN != NULL)
|
|
{
|
|
glAddSwapHintRectWIN(pdrc->x1, pdrc->y1,
|
|
pdrc->x2-pdrc->x1, pdrc->y2-pdrc->y1);
|
|
}
|
|
#endif
|
|
|
|
SwapBuffers(_gltkw.hdc);
|
|
|
|
#ifdef SHOW_BOUND
|
|
SelectObject(_gltkw.hdc, GetStockObject(WHITE_PEN));
|
|
MoveToEx(_gltkw.hdc, pdrc->x1, pdrc->y1, NULL);
|
|
LineTo(_gltkw.hdc, pdrc->x1, pdrc->y2);
|
|
LineTo(_gltkw.hdc, pdrc->x2, pdrc->y2);
|
|
LineTo(_gltkw.hdc, pdrc->x2, pdrc->y1);
|
|
LineTo(_gltkw.hdc, pdrc->x1, pdrc->y1);
|
|
#endif
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
HWND GlWindow::Handle(void)
|
|
{
|
|
return _gltkw.hwnd;
|
|
}
|
|
|
|
static D3DMATRIX dmFlipZ =
|
|
{
|
|
D3DVAL(1.0), D3DVAL(0.0), D3DVAL(0.0), D3DVAL(0.0),
|
|
D3DVAL(0.0), D3DVAL(1.0), D3DVAL(0.0), D3DVAL(0.0),
|
|
D3DVAL(0.0), D3DVAL(0.0), D3DVAL(-1.0), D3DVAL(0.0),
|
|
D3DVAL(0.0), D3DVAL(0.0), D3DVAL(0.0), D3DVAL(1.0)
|
|
};
|
|
|
|
void GlWindow::ChangeWorld(float *pfMatrix)
|
|
{
|
|
glLoadMatrixf(pfMatrix);
|
|
_pfWorld = pfMatrix;
|
|
}
|
|
|
|
void GlWindow::ChangeView(float *pfMatrix)
|
|
{
|
|
memcpy(&_dmView, pfMatrix, sizeof(_dmView));
|
|
glMatrixMode(GL_PROJECTION);
|
|
glLoadMatrixf((float *)&_dmProjection);
|
|
glMultMatrixf((float *)&_dmView);
|
|
glMatrixMode(GL_MODELVIEW);
|
|
_pfView = pfMatrix;
|
|
}
|
|
|
|
void GlWindow::ChangeProjection(float *pfMatrix)
|
|
{
|
|
// D3D is left-handed while OpenGL is right-handed
|
|
// Flip the Z direction to account for this
|
|
// Could just change row three's signs but this
|
|
// matrix doesn't change often
|
|
MultiplyD3DMATRIX(&_dmProjection, (D3DMATRIX *)pfMatrix, &dmFlipZ);
|
|
glMatrixMode(GL_PROJECTION);
|
|
glLoadMatrixf((float *)&_dmProjection);
|
|
glMultMatrixf((float *)&_dmView);
|
|
glMatrixMode(GL_MODELVIEW);
|
|
_pfProjection = pfMatrix;
|
|
}
|
|
|
|
void GlWindow::MatrixChanged(float *pfMatrix)
|
|
{
|
|
if (pfMatrix == _pfWorld)
|
|
{
|
|
ChangeWorld(pfMatrix);
|
|
}
|
|
else if (pfMatrix == _pfView)
|
|
{
|
|
ChangeView(pfMatrix);
|
|
}
|
|
else if (pfMatrix == _pfProjection)
|
|
{
|
|
ChangeProjection(pfMatrix);
|
|
}
|
|
}
|
|
|
|
BOOL GlWindow::DoExecute(RendExecuteBuffer* preb)
|
|
{
|
|
D3DINSTRUCTION* pdinst;
|
|
GlExecuteBuffer* pgeb;
|
|
BOOL bExit;
|
|
ULONG i;
|
|
D3DSTATE* pdst;
|
|
float fv4[4];
|
|
BOOL bProcessedVertices;
|
|
BOOL bDisplayList;
|
|
#if DBG
|
|
D3DINSTRUCTION *pdinstPrev;
|
|
#endif
|
|
|
|
pgeb = (GlExecuteBuffer *)preb;
|
|
bDisplayList = FALSE;
|
|
if (pgeb->_dlist != 0)
|
|
{
|
|
glCallList(pgeb->_dlist);
|
|
return TRUE;
|
|
}
|
|
else if (pgeb->_nVertices > 0)
|
|
{
|
|
// Create a display list for any execute buffer with vertices
|
|
// This is a performance win for glDrawElements
|
|
|
|
// This is wasted time in the full fill test since
|
|
// the vertex data changes all the time
|
|
if (app.uiCurrentTest != FILL_RATE_TEST)
|
|
{
|
|
pgeb->_dlist = glGenLists(1);
|
|
glNewList(pgeb->_dlist, GL_COMPILE_AND_EXECUTE);
|
|
bDisplayList = TRUE;
|
|
}
|
|
}
|
|
|
|
pdinst = (D3DINSTRUCTION *)(pgeb->_pbData+pgeb->_cbStart);
|
|
bProcessedVertices = FALSE;
|
|
|
|
// Account for RH vs. LH
|
|
glDepthRange(1, 0);
|
|
glEnableClientState(GL_NORMAL_ARRAY);
|
|
glDisableClientState(GL_COLOR_ARRAY);
|
|
#ifndef NO_LIGHTING
|
|
if (_iLight > 0)
|
|
{
|
|
glEnable(GL_LIGHTING);
|
|
}
|
|
#endif
|
|
|
|
bExit = FALSE;
|
|
#if DBG
|
|
pdinstPrev = NULL;
|
|
#endif
|
|
while (!bExit &&
|
|
(BYTE *)pdinst < (pgeb->_pbData+pgeb->_cbStart+pgeb->_cbSize))
|
|
{
|
|
#if 0
|
|
printf("Op %d, size %d, count %d\n",
|
|
pdinst->bOpcode, pdinst->bSize, pdinst->wCount);
|
|
#endif
|
|
|
|
switch(pdinst->bOpcode)
|
|
{
|
|
case D3DOP_POINT:
|
|
// Consider implementing
|
|
break;
|
|
|
|
case D3DOP_LINE:
|
|
// Consider implementing
|
|
break;
|
|
|
|
case D3DOP_TRIANGLE:
|
|
if (pdinst->wCount > 0)
|
|
{
|
|
Msg("Unprocessed execute buffer");
|
|
return FALSE;
|
|
}
|
|
break;
|
|
|
|
case D3DOP_MATRIXLOAD:
|
|
D3DMATRIXLOAD* pdml;
|
|
|
|
pdml = (D3DMATRIXLOAD *)(pdinst+1);
|
|
for (i = 0; i < pdinst->wCount; i++)
|
|
{
|
|
memcpy((BYTE *)pdml->hDestMatrix,
|
|
(BYTE *)pdml->hSrcMatrix,
|
|
sizeof(D3DMATRIX));
|
|
#if 0
|
|
// Should we check here also?
|
|
MatrixChanged((float *)pdml->hDestMatrix);
|
|
#endif
|
|
pdml++;
|
|
}
|
|
break;
|
|
|
|
case D3DOP_MATRIXMULTIPLY:
|
|
D3DMATRIXMULTIPLY* pdmm;
|
|
|
|
pdmm = (D3DMATRIXMULTIPLY *)(pdinst+1);
|
|
for (i = 0; i < pdinst->wCount; i++)
|
|
{
|
|
MultiplyD3DMATRIX((D3DMATRIX *)pdmm->hDestMatrix,
|
|
(D3DMATRIX *)pdmm->hSrcMatrix1,
|
|
(D3DMATRIX *)pdmm->hSrcMatrix2);
|
|
#if 0
|
|
// Should we check here also?
|
|
MatrixChanged((float *)pdmm->hDestMatrix);
|
|
#endif
|
|
pdmm++;
|
|
}
|
|
break;
|
|
|
|
case D3DOP_STATETRANSFORM:
|
|
pdst = (D3DSTATE *)(pdinst+1);
|
|
for (i = 0; i < pdinst->wCount; i++)
|
|
{
|
|
switch(pdst->dtstTransformStateType)
|
|
{
|
|
case D3DTRANSFORMSTATE_WORLD:
|
|
ChangeWorld((float *)pdst->dwArg[0]);
|
|
break;
|
|
case D3DTRANSFORMSTATE_VIEW:
|
|
ChangeView((float *)pdst->dwArg[0]);
|
|
break;
|
|
case D3DTRANSFORMSTATE_PROJECTION:
|
|
ChangeProjection((float *)pdst->dwArg[0]);
|
|
break;
|
|
}
|
|
|
|
pdst++;
|
|
}
|
|
break;
|
|
|
|
case D3DOP_STATELIGHT:
|
|
pdst = (D3DSTATE *)(pdinst+1);
|
|
for (i = 0; i < pdinst->wCount; i++)
|
|
{
|
|
switch(pdst->dlstLightStateType)
|
|
{
|
|
case D3DLIGHTSTATE_MATERIAL:
|
|
// Should probably optimize this so
|
|
// that work is done only when material properties
|
|
// change
|
|
((GlMaterial *)pdst->dwArg[0])->Apply();
|
|
break;
|
|
case D3DLIGHTSTATE_AMBIENT:
|
|
fv4[0] = (float)((pdst->dwArg[0] >> 16) & 0xff) / 255.0f;
|
|
fv4[1] = (float)((pdst->dwArg[0] >> 8) & 0xff) / 255.0f;
|
|
fv4[2] = (float)((pdst->dwArg[0] >> 0) & 0xff) / 255.0f;
|
|
fv4[3] = (float)((pdst->dwArg[0] >> 24) & 0xff) / 255.0f;
|
|
glLightModelfv(GL_LIGHT_MODEL_AMBIENT, fv4);
|
|
break;
|
|
// Others
|
|
}
|
|
|
|
pdst++;
|
|
}
|
|
break;
|
|
|
|
case D3DOP_STATERENDER:
|
|
pdst = (D3DSTATE *)(pdinst+1);
|
|
for (i = 0; i < pdinst->wCount; i++)
|
|
{
|
|
switch(pdst->drstRenderStateType)
|
|
{
|
|
// Many to implement but all currently
|
|
// used values are handled
|
|
case D3DRENDERSTATE_TEXTUREHANDLE:
|
|
if (pdst->dwArg[0] == 0)
|
|
{
|
|
glDisable(GL_TEXTURE_2D);
|
|
}
|
|
else
|
|
{
|
|
glEnable(GL_TEXTURE_2D);
|
|
glBindTexture(GL_TEXTURE_2D, (GLuint)pdst->dwArg[0]);
|
|
// Establish current texture params
|
|
glTexParameteri(GL_TEXTURE_2D,
|
|
GL_TEXTURE_MIN_FILTER, _eTexMin);
|
|
glTexParameteri(GL_TEXTURE_2D,
|
|
GL_TEXTURE_MAG_FILTER, _eTexMag);
|
|
}
|
|
break;
|
|
case D3DRENDERSTATE_WRAPU:
|
|
case D3DRENDERSTATE_WRAPV:
|
|
// These should probably be supported
|
|
// but it's non-trivial plus the default state
|
|
// is currently wrap and that's what the tests
|
|
// care about
|
|
break;
|
|
case D3DRENDERSTATE_SHADEMODE:
|
|
switch(pdst->dwArg[0])
|
|
{
|
|
case D3DSHADE_FLAT:
|
|
glShadeModel(GL_FLAT);
|
|
break;
|
|
case D3DSHADE_GOURAUD:
|
|
glShadeModel(GL_SMOOTH);
|
|
break;
|
|
default:
|
|
Msg("Unsupported SHADEMODE %d", pdst->dwArg[0]);
|
|
return FALSE;
|
|
}
|
|
break;
|
|
case D3DRENDERSTATE_TEXTUREMAPBLEND:
|
|
GLenum eTexMode;
|
|
|
|
switch(pdst->dwArg[0])
|
|
{
|
|
case D3DTBLEND_DECAL:
|
|
// Technically D3DTBLEND_DECAL doesn't match
|
|
// GL_DECAL, but it's close enough
|
|
case D3DTBLEND_DECALALPHA:
|
|
eTexMode = GL_DECAL;
|
|
break;
|
|
case D3DTBLEND_MODULATE:
|
|
// Technically D3DTBLEND_MODULATE doesn't match
|
|
// GL_MODULATE, but it's close enough
|
|
case D3DTBLEND_MODULATEALPHA:
|
|
eTexMode = GL_MODULATE;
|
|
break;
|
|
case D3DTBLEND_COPY:
|
|
eTexMode = GL_REPLACE;
|
|
break;
|
|
default:
|
|
Msg("Unsupported TEXTUREMAPBLEND %d", pdst->dwArg[0]);
|
|
return FALSE;
|
|
}
|
|
if (eTexMode != DEF_TEX_MODE)
|
|
{
|
|
glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE,
|
|
eTexMode);
|
|
}
|
|
break;
|
|
case D3DRENDERSTATE_TEXTUREPERSPECTIVE:
|
|
glHint(GL_PERSPECTIVE_CORRECTION_HINT,
|
|
pdst->dwArg[0] ? GL_NICEST : GL_FASTEST);
|
|
break;
|
|
case D3DRENDERSTATE_ZENABLE:
|
|
if (pdst->dwArg[0])
|
|
{
|
|
glEnable(GL_DEPTH_TEST);
|
|
}
|
|
else
|
|
{
|
|
glDisable(GL_DEPTH_TEST);
|
|
}
|
|
break;
|
|
case D3DRENDERSTATE_ZWRITEENABLE:
|
|
glDepthMask((GLboolean)pdst->dwArg[0]);
|
|
break;
|
|
case D3DRENDERSTATE_ZFUNC:
|
|
glDepthFunc(eDepthFuncTranslate[pdst->dwArg[0]]);
|
|
break;
|
|
case D3DRENDERSTATE_TEXTUREMAG:
|
|
_eTexMag = eTextureFilterTranslate[pdst->dwArg[0]];
|
|
break;
|
|
case D3DRENDERSTATE_TEXTUREMIN:
|
|
_eTexMin = eTextureFilterTranslate[pdst->dwArg[0]];
|
|
break;
|
|
case D3DRENDERSTATE_DITHERENABLE:
|
|
if (pdst->dwArg[0])
|
|
{
|
|
glEnable(GL_DITHER);
|
|
break;
|
|
}
|
|
else
|
|
{
|
|
glDisable(GL_DITHER);
|
|
break;
|
|
}
|
|
break;
|
|
case D3DRENDERSTATE_BLENDENABLE:
|
|
if (pdst->dwArg[0])
|
|
{
|
|
glEnable(GL_BLEND);
|
|
break;
|
|
}
|
|
else
|
|
{
|
|
glDisable(GL_BLEND);
|
|
break;
|
|
}
|
|
break;
|
|
case D3DRENDERSTATE_SRCBLEND:
|
|
if (pdst->dwArg[0] == D3DBLEND_BOTHSRCALPHA)
|
|
{
|
|
_eSrcBlend = GL_SRC_ALPHA;
|
|
_eDstBlend = GL_ONE_MINUS_SRC_ALPHA;
|
|
}
|
|
else if (pdst->dwArg[0] == D3DBLEND_BOTHINVSRCALPHA)
|
|
{
|
|
_eSrcBlend = GL_ONE_MINUS_SRC_ALPHA;
|
|
_eDstBlend = GL_SRC_ALPHA;
|
|
}
|
|
else
|
|
{
|
|
_eSrcBlend = eBlendFuncTranslate[pdst->dwArg[0]];
|
|
}
|
|
glBlendFunc(_eSrcBlend, _eDstBlend);
|
|
break;
|
|
case D3DRENDERSTATE_DESTBLEND:
|
|
_eDstBlend = eBlendFuncTranslate[pdst->dwArg[0]];
|
|
glBlendFunc(_eSrcBlend, _eDstBlend);
|
|
break;
|
|
case D3DRENDERSTATE_SPECULARENABLE:
|
|
// Can't turn this off
|
|
break;
|
|
case D3DRENDERSTATE_MONOENABLE:
|
|
if (pdst->dwArg[0] != 0)
|
|
{
|
|
Msg("Can't render in MONO mode");
|
|
}
|
|
break;
|
|
|
|
default:
|
|
Msg("Unhandled RENDERSTATE %d", pdst->drstRenderStateType);
|
|
break;
|
|
}
|
|
|
|
pdst++;
|
|
}
|
|
break;
|
|
|
|
case D3DOP_PROCESSVERTICES:
|
|
D3DPROCESSVERTICES* pdpv;
|
|
|
|
pdpv = (D3DPROCESSVERTICES *)(pdinst+1);
|
|
for (i = 0; i < pdinst->wCount; i++)
|
|
{
|
|
if (bProcessedVertices)
|
|
{
|
|
Msg("Multiple PROCESSVERTICES in execute buffer");
|
|
return FALSE;
|
|
}
|
|
|
|
switch(pdpv->dwFlags & D3DPROCESSVERTICES_OPMASK)
|
|
{
|
|
case D3DPROCESSVERTICES_TRANSFORMLIGHT:
|
|
D3DVERTEX* pdvtx;
|
|
|
|
pdvtx = (D3DVERTEX *)(pgeb->_pbData+pdpv->wStart);
|
|
glVertexPointer(3, GL_FLOAT, sizeof(D3DVERTEX),
|
|
&pdvtx[0].x);
|
|
glNormalPointer(GL_FLOAT, sizeof(D3DVERTEX), &pdvtx[0].nx);
|
|
glTexCoordPointer(2, GL_FLOAT, sizeof(D3DVERTEX),
|
|
&pdvtx[0].tu);
|
|
break;
|
|
|
|
case D3DPROCESSVERTICES_COPY:
|
|
D3DTLVERTEX* pdtlvtx;
|
|
|
|
pdtlvtx = (D3DTLVERTEX *)(pgeb->_pbData+pdpv->wStart);
|
|
glVertexPointer(3, GL_FLOAT, sizeof(D3DTLVERTEX),
|
|
&pdtlvtx[0].sx);
|
|
glTexCoordPointer(2, GL_FLOAT, sizeof(D3DTLVERTEX),
|
|
&pdtlvtx[0].tu);
|
|
glColorPointer(4, GL_UNSIGNED_BYTE, sizeof(D3DTLVERTEX),
|
|
&pdtlvtx[0].color);
|
|
glDisableClientState(GL_NORMAL_ARRAY);
|
|
glEnableClientState(GL_COLOR_ARRAY);
|
|
glDisable(GL_LIGHTING);
|
|
glLoadIdentity();
|
|
glMatrixMode(GL_PROJECTION);
|
|
glLoadIdentity();
|
|
// Flip Y, Z to match D3D
|
|
glOrtho(0, WIN_WIDTH, WIN_HEIGHT, 0, 0, 1);
|
|
// Flip Z to match D3D
|
|
glMultMatrixf((float *)&dmFlipZ);
|
|
glDepthRange(0, 1);
|
|
glMatrixMode(GL_MODELVIEW);
|
|
break;
|
|
|
|
default:
|
|
Msg("Unsupported vertex processing type %X",
|
|
pdpv->dwFlags & D3DPROCESSVERTICES_OPMASK);
|
|
return FALSE;
|
|
}
|
|
|
|
bProcessedVertices = TRUE;
|
|
pdpv++;
|
|
}
|
|
break;
|
|
|
|
case D3DOP_TEXTURELOAD:
|
|
D3DTEXTURELOAD* pdtl;
|
|
|
|
pdtl = (D3DTEXTURELOAD *)(pdinst+1);
|
|
for (i = 0; i < pdinst->wCount; i++)
|
|
{
|
|
// Consider implementing
|
|
pdtl++;
|
|
}
|
|
break;
|
|
|
|
case D3DOP_EXIT:
|
|
bExit = TRUE;
|
|
break;
|
|
|
|
case D3DOP_BRANCHFORWARD:
|
|
// Consider implementing
|
|
break;
|
|
|
|
case D3DOP_SPAN:
|
|
// Consider implementing
|
|
break;
|
|
|
|
case D3DOP_SETSTATUS:
|
|
// Consider implementing
|
|
break;
|
|
|
|
case PROCESSED_TRIANGLE:
|
|
if (!bProcessedVertices)
|
|
{
|
|
Msg("Triangles without vertex data");
|
|
return FALSE;
|
|
}
|
|
|
|
glDrawElements(GL_TRIANGLES, pdinst->wCount*3L, GL_UNSIGNED_INT,
|
|
*(GLuint **)(pdinst+1));
|
|
break;
|
|
|
|
default:
|
|
#if DBG
|
|
if (pdinstPrev != NULL)
|
|
{
|
|
Msg("Invalid execute buffer opcode %d:%d (%08lX), "
|
|
"prev %d:%d (%08lX)",
|
|
pdinst->bOpcode, pdinst->wCount, pdinst,
|
|
pdinstPrev->bOpcode, pdinstPrev->wCount, pdinstPrev);
|
|
}
|
|
else
|
|
{
|
|
Msg("Invalid execute buffer opcode %d", pdinst->bOpcode);
|
|
}
|
|
#else
|
|
Msg("Invalid execute buffer opcode %d", pdinst->bOpcode);
|
|
#endif
|
|
return FALSE;
|
|
}
|
|
|
|
#if DBG
|
|
pdinstPrev = pdinst;
|
|
#endif
|
|
|
|
// Could eliminate this, it has a multiply
|
|
pdinst = NEXT_PDINST(pdinst);
|
|
}
|
|
|
|
if (bDisplayList)
|
|
{
|
|
glEndList();
|
|
}
|
|
|
|
return TRUE;
|
|
}
|