wine/dlls/wined3d/drawprim.c
Oliver Stieber c99a3fafef Change the format of surface_SaveSnapshot to TGA from PPM and add
support for compressed textures and an alpha channel.
2005-09-27 10:49:59 +00:00

2104 lines
92 KiB
C

/*
* WINED3D draw functions
*
* Copyright 2002-2004 Jason Edmeades
* Copyright 2002-2004 Raphael Junqueira
* Copyright 2004 Christian Costa
* Copyright 2005 Oliver Stieber
*
* 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
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include "config.h"
#include "wined3d_private.h"
WINE_DEFAULT_DEBUG_CHANNEL(d3d_draw);
WINE_DECLARE_DEBUG_CHANNEL(d3d_shader);
#define GLINFO_LOCATION ((IWineD3DImpl *)(This->wineD3D))->gl_info
#ifdef SHOW_FRAME_MAKEUP
#include <stdio.h>
#endif
#if 0 /* TODO */
extern IWineD3DVertexShaderImpl* VertexShaders[64];
extern IWineD3DVertexDeclarationImpl* VertexShaderDeclarations[64];
extern IWineD3DPixelShaderImpl* PixelShaders[64];
#undef GL_VERSION_1_4 /* To be fixed, caused by mesa headers */
#endif
/* Returns bits for what is expected from the fixed function pipeline, and whether
a vertex shader will be in use. Note the fvf bits returned may be split over
multiple streams only if the vertex shader was created, otherwise it all relates
to stream 0 */
static BOOL initializeFVF(IWineD3DDevice *iface, DWORD *FVFbits)
{
IWineD3DDeviceImpl *This = (IWineD3DDeviceImpl *)iface;
#if 0 /* TODO: d3d8 call setvertexshader needs to set the FVF in the state block when implemented */
/* The first thing to work out is if we are using the fixed function pipeline
which is either SetVertexShader with < VS_HIGHESTFIXEDFXF - in which case this
is the FVF, or with a shader which was created with no function - in which
case there is an FVF per declared stream. If this occurs, we also maintain
an 'OR' of all the FVF's together so we know what to expect across all the
streams */
#endif
*FVFbits = This->stateBlock->fvf;
#if 0
*FVFbits = This->stateBlock->vertexShaderDecl->allFVF;
#endif
return FALSE;
}
/* Issues the glBegin call for gl given the primitive type and count */
static DWORD primitiveToGl(D3DPRIMITIVETYPE PrimitiveType,
DWORD NumPrimitives,
GLenum *primType)
{
DWORD NumVertexes = NumPrimitives;
switch (PrimitiveType) {
case D3DPT_POINTLIST:
TRACE("POINTS\n");
*primType = GL_POINTS;
NumVertexes = NumPrimitives;
break;
case D3DPT_LINELIST:
TRACE("LINES\n");
*primType = GL_LINES;
NumVertexes = NumPrimitives * 2;
break;
case D3DPT_LINESTRIP:
TRACE("LINE_STRIP\n");
*primType = GL_LINE_STRIP;
NumVertexes = NumPrimitives + 1;
break;
case D3DPT_TRIANGLELIST:
TRACE("TRIANGLES\n");
*primType = GL_TRIANGLES;
NumVertexes = NumPrimitives * 3;
break;
case D3DPT_TRIANGLESTRIP:
TRACE("TRIANGLE_STRIP\n");
*primType = GL_TRIANGLE_STRIP;
NumVertexes = NumPrimitives + 2;
break;
case D3DPT_TRIANGLEFAN:
TRACE("TRIANGLE_FAN\n");
*primType = GL_TRIANGLE_FAN;
NumVertexes = NumPrimitives + 2;
break;
default:
FIXME("Unhandled primitive\n");
*primType = GL_POINTS;
break;
}
return NumVertexes;
}
/* Ensure the appropriate material states are set up - only change
state if really required */
static void init_materials(IWineD3DDevice *iface, BOOL isDiffuseSupplied) {
BOOL requires_material_reset = FALSE;
IWineD3DDeviceImpl *This = (IWineD3DDeviceImpl *)iface;
if (This->tracking_color == NEEDS_TRACKING && isDiffuseSupplied) {
/* If we have not set up the material color tracking, do it now as required */
glDisable(GL_COLOR_MATERIAL); /* Note: Man pages state must enable AFTER calling glColorMaterial! Required?*/
checkGLcall("glDisable GL_COLOR_MATERIAL");
TRACE("glColorMaterial Parm=%x\n", This->tracking_parm);
glColorMaterial(GL_FRONT_AND_BACK, This->tracking_parm);
checkGLcall("glColorMaterial(GL_FRONT_AND_BACK, Parm)");
glEnable(GL_COLOR_MATERIAL);
checkGLcall("glEnable GL_COLOR_MATERIAL");
This->tracking_color = IS_TRACKING;
requires_material_reset = TRUE; /* Restore material settings as will be used */
} else if ((This->tracking_color == IS_TRACKING && isDiffuseSupplied == FALSE) ||
(This->tracking_color == NEEDS_TRACKING && isDiffuseSupplied == FALSE)) {
/* If we are tracking the current color but one isn't supplied, don't! */
glDisable(GL_COLOR_MATERIAL);
checkGLcall("glDisable GL_COLOR_MATERIAL");
This->tracking_color = NEEDS_TRACKING;
requires_material_reset = TRUE; /* Restore material settings as will be used */
} else if (This->tracking_color == IS_TRACKING && isDiffuseSupplied) {
/* No need to reset material colors since no change to gl_color_material */
requires_material_reset = FALSE;
} else if (This->tracking_color == NEEDS_DISABLE) {
glDisable(GL_COLOR_MATERIAL);
checkGLcall("glDisable GL_COLOR_MATERIAL");
This->tracking_color = DISABLED_TRACKING;
requires_material_reset = TRUE; /* Restore material settings as will be used */
}
/* Reset the material colors which may have been tracking the color*/
if (requires_material_reset) {
glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT, (float*) &This->stateBlock->material.Ambient);
checkGLcall("glMaterialfv");
glMaterialfv(GL_FRONT_AND_BACK, GL_DIFFUSE, (float*) &This->stateBlock->material.Diffuse);
checkGLcall("glMaterialfv");
if (This->stateBlock->renderState[WINED3DRS_SPECULARENABLE]) {
glMaterialfv(GL_FRONT_AND_BACK, GL_SPECULAR, (float*) &This->stateBlock->material.Specular);
checkGLcall("glMaterialfv");
} else {
float black[4] = {0.0f, 0.0f, 0.0f, 0.0f};
glMaterialfv(GL_FRONT_AND_BACK, GL_SPECULAR, &black[0]);
checkGLcall("glMaterialfv");
}
glMaterialfv(GL_FRONT_AND_BACK, GL_EMISSION, (float*) &This->stateBlock->material.Emissive);
checkGLcall("glMaterialfv");
}
}
static GLfloat invymat[16] = {
1.0f, 0.0f, 0.0f, 0.0f,
0.0f, -1.0f, 0.0f, 0.0f,
0.0f, 0.0f, 1.0f, 0.0f,
0.0f, 0.0f, 0.0f, 1.0f};
/* Setup views - Transformed & lit if RHW, else untransformed.
Only unlit if Normals are supplied
Returns: Whether to restore lighting afterwards */
static BOOL primitiveInitState(IWineD3DDevice *iface, BOOL vtx_transformed, BOOL vtx_lit, BOOL useVS) {
BOOL isLightingOn = FALSE;
IWineD3DDeviceImpl *This = (IWineD3DDeviceImpl *)iface;
/* If no normals, DISABLE lighting otherwise, don't touch lighing as it is
set by the appropriate render state. Note Vertex Shader output is already lit */
if (vtx_lit || useVS) {
isLightingOn = glIsEnabled(GL_LIGHTING);
glDisable(GL_LIGHTING);
checkGLcall("glDisable(GL_LIGHTING);");
TRACE("Disabled lighting as no normals supplied, old state = %d\n", isLightingOn);
}
if (!useVS && vtx_transformed) {
/* If the last draw was transformed as well, no need to reapply all the matrixes */
if (!This->last_was_rhw) {
double X, Y, height, width, minZ, maxZ;
This->last_was_rhw = TRUE;
/* Transformed already into viewport coordinates, so we do not need transform
matrices. Reset all matrices to identity and leave the default matrix in world
mode. */
glMatrixMode(GL_MODELVIEW);
checkGLcall("glMatrixMode(GL_MODELVIEW)");
glLoadIdentity();
checkGLcall("glLoadIdentity");
glMatrixMode(GL_PROJECTION);
checkGLcall("glMatrixMode(GL_PROJECTION)");
glLoadIdentity();
checkGLcall("glLoadIdentity");
/* Set up the viewport to be full viewport */
X = This->stateBlock->viewport.X;
Y = This->stateBlock->viewport.Y;
height = This->stateBlock->viewport.Height;
width = This->stateBlock->viewport.Width;
minZ = This->stateBlock->viewport.MinZ;
maxZ = This->stateBlock->viewport.MaxZ;
TRACE("Calling glOrtho with %f, %f, %f, %f\n", width, height, -minZ, -maxZ);
glOrtho(X, X + width, Y + height, Y, -minZ, -maxZ);
checkGLcall("glOrtho");
/* Window Coord 0 is the middle of the first pixel, so translate by half
a pixel (See comment above glTranslate below) */
glTranslatef(0.5, 0.5, 0);
checkGLcall("glTranslatef(0.5, 0.5, 0)");
if (This->renderUpsideDown) {
glMultMatrixf(invymat);
checkGLcall("glMultMatrixf(invymat)");
}
}
} else {
/* Untransformed, so relies on the view and projection matrices */
if (!useVS && (This->last_was_rhw || !This->modelview_valid)) {
/* Only reapply when have to */
This->modelview_valid = TRUE;
glMatrixMode(GL_MODELVIEW);
checkGLcall("glMatrixMode");
/* In the general case, the view matrix is the identity matrix */
if (This->view_ident) {
glLoadMatrixf((float *) &This->stateBlock->transforms[D3DTS_WORLDMATRIX(0)].u.m[0][0]);
checkGLcall("glLoadMatrixf");
} else {
glLoadMatrixf((float *) &This->stateBlock->transforms[D3DTS_VIEW].u.m[0][0]);
checkGLcall("glLoadMatrixf");
glMultMatrixf((float *) &This->stateBlock->transforms[D3DTS_WORLDMATRIX(0)].u.m[0][0]);
checkGLcall("glMultMatrixf");
}
}
if (!useVS && (This->last_was_rhw || !This->proj_valid)) {
/* Only reapply when have to */
This->proj_valid = TRUE;
glMatrixMode(GL_PROJECTION);
checkGLcall("glMatrixMode");
/* The rule is that the window coordinate 0 does not correspond to the
beginning of the first pixel, but the center of the first pixel.
As a consequence if you want to correctly draw one line exactly from
the left to the right end of the viewport (with all matrices set to
be identity), the x coords of both ends of the line would be not
-1 and 1 respectively but (-1-1/viewport_widh) and (1-1/viewport_width)
instead. */
glLoadIdentity();
glTranslatef(0.9 / This->stateBlock->viewport.Width, -0.9 / This->stateBlock->viewport.Height, 0);
checkGLcall("glTranslatef (0.9 / width, -0.9 / height, 0)");
if (This->renderUpsideDown) {
glMultMatrixf(invymat);
checkGLcall("glMultMatrixf(invymat)");
}
glMultMatrixf((float *) &This->stateBlock->transforms[D3DTS_PROJECTION].u.m[0][0]);
checkGLcall("glLoadMatrixf");
}
/* Vertex Shader output is already transformed, so set up identity matrices */
/* FIXME: Actually, only true for software emulated ones, so when h/w ones
come along this needs to take into account whether s/w ones were
requested or not */
if (useVS) {
glMatrixMode(GL_MODELVIEW);
checkGLcall("glMatrixMode");
glLoadIdentity();
glMatrixMode(GL_PROJECTION);
checkGLcall("glMatrixMode");
glLoadIdentity();
/* Window Coord 0 is the middle of the first pixel, so translate by half
a pixel (See comment above glTranslate above) */
glTranslatef(0.9 / This->stateBlock->viewport.Width, -0.9 / This->stateBlock->viewport.Height, 0);
checkGLcall("glTranslatef (0.9 / width, -0.9 / height, 0)");
if (This->renderUpsideDown) {
glMultMatrixf(invymat);
checkGLcall("glMultMatrixf(invymat)");
}
This->modelview_valid = FALSE;
This->proj_valid = FALSE;
}
This->last_was_rhw = FALSE;
}
return isLightingOn;
}
void primitiveDeclarationConvertToStridedData(IWineD3DDevice *iface, Direct3DVertexStridedData *strided, LONG BaseVertexIndex, DWORD *fvf) {
/* We need to deal with frequency data!*/
int textureNo =0;
BYTE *data = NULL;
IWineD3DDeviceImpl *This = (IWineD3DDeviceImpl *)iface;
IWineD3DVertexDeclarationImpl* vertexDeclaration = (IWineD3DVertexDeclarationImpl*)This->stateBlock->vertexDecl;
int i;
D3DVERTEXELEMENT9 *element;
DWORD stride;
for (i = 0 ; i < vertexDeclaration->declaration9NumElements - 1; ++i) {
element = vertexDeclaration->pDeclaration9 + i;
TRACE("%p Elements %p %d or %d\n", vertexDeclaration->pDeclaration9, element, i, vertexDeclaration->declaration9NumElements);
if (This->stateBlock->streamIsUP) {
TRACE("Stream is up %d, %p\n", element->Stream, This->stateBlock->streamSource[element->Stream]);
data = (BYTE *)This->stateBlock->streamSource[element->Stream];
} else {
TRACE("Stream isn't up %d, %p\n", element->Stream, This->stateBlock->streamSource[element->Stream]);
data = IWineD3DVertexBufferImpl_GetMemory(This->stateBlock->streamSource[element->Stream], 0);
}
stride = This->stateBlock->streamStride[element->Stream];
data += (BaseVertexIndex * stride);
data += element->Offset;
/* Why can't I just use a lookup table instead of a switch statment? */
switch (element->Usage) {
case D3DDECLUSAGE_POSITION:
switch (element->UsageIndex) {
case 0: /* N-patch */
strided->u.s.position.lpData = data;
strided->u.s.position.dwType = element->Type;
strided->u.s.position.dwStride = stride;
break;
case 1: /* tweened see http://www.gamedev.net/reference/articles/article2017.asp */
TRACE("Tweened positions\n");
strided->u.s.position2.lpData = data;
strided->u.s.position2.dwType = element->Type;
strided->u.s.position2.dwStride = stride;
break;
}
break;
case D3DDECLUSAGE_NORMAL:
switch (element->UsageIndex) {
case 0: /* N-patch */
strided->u.s.normal.lpData = data;
strided->u.s.normal.dwType = element->Type;
strided->u.s.normal.dwStride = stride;
break;
case 1: /* skinning */
TRACE("Skinning / tween normals\n");
strided->u.s.normal2.lpData = data;
strided->u.s.normal2.dwType = element->Type;
strided->u.s.normal2.dwStride = stride;
break;
}
*fvf |= D3DFVF_NORMAL;
break;
case D3DDECLUSAGE_BLENDINDICES:
/* demo @http://www.ati.com/developer/vertexblend.html
and http://www.flipcode.com/articles/article_dx8shaders.shtml
*/
strided->u.s.blendMatrixIndices.lpData = data;
strided->u.s.blendMatrixIndices.dwType = element->Type;
strided->u.s.blendMatrixIndices.dwStride= stride;
break;
case D3DDECLUSAGE_BLENDWEIGHT:
strided->u.s.blendWeights.lpData = data;
strided->u.s.blendWeights.dwType = element->Type;
strided->u.s.blendWeights.dwStride = stride;
break;
case D3DDECLUSAGE_PSIZE:
strided->u.s.pSize.lpData = data;
strided->u.s.pSize.dwType = element->Type;
strided->u.s.pSize.dwStride = stride;
break;
case D3DDECLUSAGE_COLOR:
switch (element->UsageIndex) {
case 0:/* diffuse */
strided->u.s.diffuse.lpData = data;
strided->u.s.diffuse.dwType = element->Type;
strided->u.s.diffuse.dwStride = stride;
break;
case 1: /* specular */
strided->u.s.specular.lpData = data;
strided->u.s.specular.dwType = element->Type;
strided->u.s.specular.dwStride = stride;
}
break;
case D3DDECLUSAGE_TEXCOORD:
/* For some odd reason Microsoft decided to sum usage accross all the streams,
which means we need to do a count and not just use the usage number */
strided->u.s.texCoords[textureNo].lpData = data;
strided->u.s.texCoords[textureNo].dwType = element->Type;
strided->u.s.texCoords[textureNo].dwStride = stride;
++textureNo;
break;
case D3DDECLUSAGE_TANGENT:
/* Implement tangents and binormals using http://oss.sgi.com/projects/ogl-sample/registry/EXT/coordinate_frame.txt
this is easy so long as the OpenGL implementation supports it, otherwise drop back to calculating the
normal using tangents where no normal data has been provided */
TRACE("Tangents\n");
strided->u.s.tangent.lpData = data;
strided->u.s.tangent.dwType = element->Type;
strided->u.s.tangent.dwStride = stride;
break;
case D3DDECLUSAGE_BINORMAL:
/* Binormals are really bitangents perpendicular to the normal but s-aligned to the tangent, basically they are the vectors of any two lines on the plain at right angles to the normal and at right angles to each other, like the x,y,z axis.
tangent data makes it easier to perform some calculations (a bit like using 2d graph paper instead of the normal of the piece of paper)
The only thing they are useful for in fixed function would be working out normals when none are given.
*/
TRACE("BI-Normal\n");
strided->u.s.binormal.lpData = data;
strided->u.s.binormal.dwType = element->Type;
strided->u.s.binormal.dwStride = stride;
break;
case D3DDECLUSAGE_TESSFACTOR:
/* a google for D3DDECLUSAGE_TESSFACTOR turns up a whopping 36 entries, 7 of which are from MSDN.
*/
TRACE("Tess Factor\n");
strided->u.s.tessFactor.lpData = data;
strided->u.s.tessFactor.dwType = element->Type;
strided->u.s.tessFactor.dwStride = stride;
break;
case D3DDECLUSAGE_POSITIONT:
switch (element->UsageIndex) {
case 0: /* N-patch */
strided->u.s.position.lpData = data;
strided->u.s.position.dwType = element->Type;
strided->u.s.position.dwStride = stride;
break;
case 1: /* skinning */
/* see http://rsn.gamedev.net/tutorials/ms3danim.asp
http://xface.blogspot.com/2004_08_01_xface_archive.html
*/
TRACE("Skinning positionsT\n");
strided->u.s.position2.lpData = data;
strided->u.s.position2.dwType = element->Type;
strided->u.s.position2.dwStride = stride;
break;
}
/* TODO: change fvf usage to a plain boolean flag */
*fvf |= D3DFVF_XYZRHW;
/* FIXME: were faking this flag so that we don't transform the data again */
break;
case D3DDECLUSAGE_FOG:
/* maybe GL_EXT_fog_coord ?
* http://oss.sgi.com/projects/ogl-sample/registry/EXT/fog_coord.txt
* This extension allows specifying an explicit per-vertex fog
* coordinate to be used in fog computations, rather than using a
* fragment depth-based fog equation.
*
* */
TRACE("Fog\n");
strided->u.s.fog.lpData = data;
strided->u.s.fog.dwType = element->Type;
strided->u.s.fog.dwStride = stride;
break;
case D3DDECLUSAGE_DEPTH:
TRACE("depth\n");
strided->u.s.depth.lpData = data;
strided->u.s.depth.dwType = element->Type;
strided->u.s.depth.dwStride = stride;
break;
case D3DDECLUSAGE_SAMPLE: /* VertexShader textures */
TRACE("depth\n");
strided->u.s.sample.lpData = data;
strided->u.s.sample.dwType = element->Type;
strided->u.s.sample.dwStride = stride;
break;
};
};
}
static void primitiveConvertToStridedData(IWineD3DDevice *iface, Direct3DVertexStridedData *strided, LONG BaseVertexIndex) {
short LoopThroughTo = 0;
short nStream;
int numBlends;
int numTextures;
int textureNo;
int coordIdxInfo = 0x00; /* Information on number of coords supplied */
int numCoords[8]; /* Holding place for D3DFVF_TEXTUREFORMATx */
IWineD3DDeviceImpl *This = (IWineD3DDeviceImpl *)iface;
/* OK, Now to setup the data locations
For the non-created vertex shaders, the VertexShader var holds the real
FVF and only stream 0 matters
For the created vertex shaders, there is an FVF per stream */
if (!This->stateBlock->streamIsUP && !(This->stateBlock->vertexShader == NULL)) {
LoopThroughTo = MAX_STREAMS;
} else {
LoopThroughTo = 1;
}
/* Work through stream by stream */
for (nStream=0; nStream<LoopThroughTo; ++nStream) {
DWORD stride = This->stateBlock->streamStride[nStream];
BYTE *data = NULL;
DWORD thisFVF = 0;
/* Skip empty streams */
if (This->stateBlock->streamSource[nStream] == NULL) continue;
/* Retrieve appropriate FVF */
if (LoopThroughTo == 1) { /* Use FVF, not vertex shader */
thisFVF = This->stateBlock->fvf;
/* Handle memory passed directly as well as vertex buffers */
if (This->stateBlock->streamIsUP) {
data = (BYTE *)This->stateBlock->streamSource[nStream];
} else {
data = IWineD3DVertexBufferImpl_GetMemory(This->stateBlock->streamSource[nStream], 0);
}
} else {
#if 0 /* TODO: Vertex shader support */
thisFVF = This->stateBlock->vertexShaderDecl->fvf[nStream];
data = IWineD3DVertexBufferImpl_GetMemory(This->stateBlock->streamSource[nStream], 0);
#endif
}
VTRACE(("FVF for stream %d is %lx\n", nStream, thisFVF));
if (thisFVF == 0) continue;
/* Now convert the stream into pointers */
/* Shuffle to the beginning of the vertexes to render and index from there */
data = data + (BaseVertexIndex * stride);
/* Either 3 or 4 floats depending on the FVF */
/* FIXME: Can blending data be in a different stream to the position data?
and if so using the fixed pipeline how do we handle it */
if (thisFVF & D3DFVF_POSITION_MASK) {
strided->u.s.position.lpData = data;
strided->u.s.position.dwType = D3DDECLTYPE_FLOAT3;
strided->u.s.position.dwStride = stride;
data += 3 * sizeof(float);
if (thisFVF & D3DFVF_XYZRHW) {
strided->u.s.position.dwType = D3DDECLTYPE_FLOAT4;
data += sizeof(float);
}
}
/* Blending is numBlends * FLOATs followed by a DWORD for UBYTE4 */
/** do we have to Check This->stateBlock->renderState[D3DRS_INDEXEDVERTEXBLENDENABLE] ? */
numBlends = 1 + (((thisFVF & D3DFVF_XYZB5) - D3DFVF_XYZB1) >> 1);
if(thisFVF & D3DFVF_LASTBETA_UBYTE4) numBlends--;
if ((thisFVF & D3DFVF_XYZB5 ) > D3DFVF_XYZRHW) {
TRACE("Setting blend Weights to %p \n", data);
strided->u.s.blendWeights.lpData = data;
strided->u.s.blendWeights.dwType = D3DDECLTYPE_FLOAT1 + numBlends - 1;
strided->u.s.blendWeights.dwStride = stride;
data += numBlends * sizeof(FLOAT);
if (thisFVF & D3DFVF_LASTBETA_UBYTE4) {
strided->u.s.blendMatrixIndices.lpData = data;
strided->u.s.blendMatrixIndices.dwType = D3DDECLTYPE_UBYTE4;
strided->u.s.blendMatrixIndices.dwStride= stride;
data += sizeof(DWORD);
}
}
/* Normal is always 3 floats */
if (thisFVF & D3DFVF_NORMAL) {
strided->u.s.normal.lpData = data;
strided->u.s.normal.dwType = D3DDECLTYPE_FLOAT3;
strided->u.s.normal.dwStride = stride;
data += 3 * sizeof(FLOAT);
}
/* Pointsize is a single float */
if (thisFVF & D3DFVF_PSIZE) {
strided->u.s.pSize.lpData = data;
strided->u.s.pSize.dwType = D3DDECLTYPE_FLOAT1;
strided->u.s.pSize.dwStride = stride;
data += sizeof(FLOAT);
}
/* Diffuse is 4 unsigned bytes */
if (thisFVF & D3DFVF_DIFFUSE) {
strided->u.s.diffuse.lpData = data;
strided->u.s.diffuse.dwType = D3DDECLTYPE_SHORT4;
strided->u.s.diffuse.dwStride = stride;
data += sizeof(DWORD);
}
/* Specular is 4 unsigned bytes */
if (thisFVF & D3DFVF_SPECULAR) {
strided->u.s.specular.lpData = data;
strided->u.s.specular.dwType = D3DDECLTYPE_SHORT4;
strided->u.s.specular.dwStride = stride;
data += sizeof(DWORD);
}
/* Texture coords */
numTextures = (thisFVF & D3DFVF_TEXCOUNT_MASK) >> D3DFVF_TEXCOUNT_SHIFT;
coordIdxInfo = (thisFVF & 0x00FF0000) >> 16; /* 16 is from definition of D3DFVF_TEXCOORDSIZE1, and is 8 (0-7 stages) * 2bits long */
/* numTextures indicates the number of texture coordinates supplied */
/* However, the first set may not be for stage 0 texture - it all */
/* depends on D3DTSS_TEXCOORDINDEX. */
/* The number of bytes for each coordinate set is based off */
/* D3DFVF_TEXCOORDSIZEn, which are the bottom 2 bits */
/* So, for each supplied texture extract the coords */
for (textureNo = 0; textureNo < numTextures; ++textureNo) {
strided->u.s.texCoords[textureNo].lpData = data;
strided->u.s.texCoords[textureNo].dwType = D3DDECLTYPE_FLOAT1;
strided->u.s.texCoords[textureNo].dwStride = stride;
numCoords[textureNo] = coordIdxInfo & 0x03;
/* Always one set */
data += sizeof(float);
if (numCoords[textureNo] != D3DFVF_TEXTUREFORMAT1) {
strided->u.s.texCoords[textureNo].dwType = D3DDECLTYPE_FLOAT2;
data += sizeof(float);
if (numCoords[textureNo] != D3DFVF_TEXTUREFORMAT2) {
strided->u.s.texCoords[textureNo].dwType = D3DDECLTYPE_FLOAT3;
data += sizeof(float);
if (numCoords[textureNo] != D3DFVF_TEXTUREFORMAT3) {
strided->u.s.texCoords[textureNo].dwType = D3DDECLTYPE_FLOAT4;
data += sizeof(float);
}
}
}
coordIdxInfo = coordIdxInfo >> 2; /* Drop bottom two bits */
}
}
}
#if 0 /* TODO: Software Shaders */
/* Draw a single vertex using this information */
static void draw_vertex(IWineD3DDevice *iface, /* interface */
BOOL isXYZ, float x, float y, float z, float rhw, /* xyzn position*/
BOOL isNormal, float nx, float ny, float nz, /* normal */
BOOL isDiffuse, float *dRGBA, /* 1st colors */
BOOL isSpecular, float *sRGB, /* 2ndry colors */
BOOL isPtSize, float ptSize, /* pointSize */
WINED3DVECTOR_4 *texcoords, int *numcoords) /* texture info */
{
unsigned int textureNo;
float s, t, r, q;
IWineD3DDeviceImpl *This = (IWineD3DDeviceImpl *)iface;
/* Diffuse -------------------------------- */
if (isDiffuse) {
glColor4fv(dRGBA);
VTRACE(("glColor4f: r,g,b,a=%f,%f,%f,%f\n", dRGBA[0], dRGBA[1], dRGBA[2], dRGBA[3]));
}
/* Specular Colour ------------------------------------------*/
if (isSpecular) {
if (GL_SUPPORT(EXT_SECONDARY_COLOR)) {
GL_EXTCALL(glSecondaryColor3fvEXT(sRGB));
VTRACE(("glSecondaryColor4f: r,g,b=%f,%f,%f\n", sRGB[0], sRGB[1], sRGB[2]));
} else {
VTRACE(("Specular color extensions not supplied\n"));
}
}
/* Normal -------------------------------- */
if (isNormal) {
VTRACE(("glNormal:nx,ny,nz=%f,%f,%f\n", nx,ny,nz));
glNormal3f(nx, ny, nz);
}
/* Point Size ----------------------------------------------*/
if (isPtSize) {
/* no such functionality in the fixed function GL pipeline */
FIXME("Cannot change ptSize here in openGl\n");
}
/* Texture coords --------------------------- */
for (textureNo = 0; textureNo < GL_LIMITS(textures); ++textureNo) {
if (!GL_SUPPORT(ARB_MULTITEXTURE) && textureNo > 0) {
FIXME("Program using multiple concurrent textures which this opengl implementation doesn't support\n");
continue ;
}
/* Query tex coords */
if (This->stateBlock->textures[textureNo] != NULL) {
int coordIdx = This->stateBlock->textureState[textureNo][D3DTSS_TEXCOORDINDEX];
if (coordIdx >= MAX_TEXTURES) {
VTRACE(("tex: %d - Skip tex coords, as being system generated\n", textureNo));
continue;
} else if (numcoords[coordIdx] == 0) {
TRACE("tex: %d - Skipping tex coords, as no data supplied or no coords supplied\n", textureNo);
continue;
} else {
/* Initialize vars */
s = 0.0f;
t = 0.0f;
r = 0.0f;
q = 0.0f;
switch (numcoords[coordIdx]) {
case 4: q = texcoords[coordIdx].w; /* drop through */
case 3: r = texcoords[coordIdx].z; /* drop through */
case 2: t = texcoords[coordIdx].y; /* drop through */
case 1: s = texcoords[coordIdx].x;
}
switch (numcoords[coordIdx]) { /* Supply the provided texture coords */
case D3DTTFF_COUNT1:
VTRACE(("tex:%d, s=%f\n", textureNo, s));
if (GL_SUPPORT(ARB_MULTITEXTURE)) {
GLMULTITEXCOORD1F(textureNo, s);
} else {
glTexCoord1f(s);
}
break;
case D3DTTFF_COUNT2:
VTRACE(("tex:%d, s=%f, t=%f\n", textureNo, s, t));
if (GL_SUPPORT(ARB_MULTITEXTURE)) {
GLMULTITEXCOORD2F(textureNo, s, t);
} else {
glTexCoord2f(s, t);
}
break;
case D3DTTFF_COUNT3:
VTRACE(("tex:%d, s=%f, t=%f, r=%f\n", textureNo, s, t, r));
if (GL_SUPPORT(ARB_MULTITEXTURE)) {
GLMULTITEXCOORD3F(textureNo, s, t, r);
} else {
glTexCoord3f(s, t, r);
}
break;
case D3DTTFF_COUNT4:
VTRACE(("tex:%d, s=%f, t=%f, r=%f, q=%f\n", textureNo, s, t, r, q));
if (GL_SUPPORT(ARB_MULTITEXTURE)) {
GLMULTITEXCOORD4F(textureNo, s, t, r, q);
} else {
glTexCoord4f(s, t, r, q);
}
break;
default:
FIXME("Should not get here as numCoords should be 0->4 (%x)!\n", numcoords[coordIdx]);
}
}
}
} /* End of textures */
/* Position -------------------------------- */
if (isXYZ) {
if (1.0f == rhw || rhw < 0.00001f) {
VTRACE(("Vertex: glVertex:x,y,z=%f,%f,%f\n", x,y,z));
glVertex3f(x, y, z);
} else {
/* Cannot optimize by dividing through by rhw as rhw is required
later for perspective in the GL pipeline for vertex shaders */
VTRACE(("Vertex: glVertex:x,y,z=%f,%f,%f / rhw=%f\n", x,y,z,rhw));
glVertex4f(x,y,z,rhw);
}
}
}
#endif /* TODO: Software shaders */
void loadNumberedArrays(IWineD3DDevice *iface, Direct3DVertexStridedData *sd, INT arrayUsageMap[WINED3DSHADERDECLUSAGE_MAX_USAGE]) {
IWineD3DDeviceImpl *This = (IWineD3DDeviceImpl *)iface;
#define LOAD_NUMBERED_ARRAY(_arrayName, _lookupName) \
if (sd->u.s._arrayName.lpData != NULL && ((arrayUsageMap[WINED3DSHADERDECLUSAGE_##_lookupName] & 0x7FFF) == arrayUsageMap[WINED3DSHADERDECLUSAGE_##_lookupName])) { \
TRACE_(d3d_shader)("Loading array %u with data from %s\n", arrayUsageMap[WINED3DSHADERDECLUSAGE_##_lookupName], #_arrayName); \
GL_EXTCALL(glVertexAttribPointerARB(arrayUsageMap[WINED3DSHADERDECLUSAGE_##_lookupName], \
WINED3D_ATR_SIZE(_arrayName), \
WINED3D_ATR_GLTYPE(_arrayName), \
WINED3D_ATR_NORMALIZED(_arrayName), \
sd->u.s._arrayName.dwStride, \
sd->u.s._arrayName.lpData)); \
GL_EXTCALL(glEnableVertexAttribArrayARB(arrayUsageMap[WINED3DSHADERDECLUSAGE_##_lookupName])); \
}
#define LOAD_NUMBERED_POSITION_ARRAY(_lookupNumber) \
if (sd->u.s.position2.lpData != NULL && ((arrayUsageMap[WINED3DSHADERDECLUSAGE_POSITION2 + _lookupNumber] & 0x7FFF) == arrayUsageMap[WINED3DSHADERDECLUSAGE_POSITION2 + _lookupNumber])) { \
FIXME_(d3d_shader)("Loading array %u with data from %s\n", arrayUsageMap[WINED3DSHADERDECLUSAGE_POSITION2 + _lookupNumber], "position2"); \
GL_EXTCALL(glVertexAttribPointerARB(arrayUsageMap[WINED3DSHADERDECLUSAGE_POSITION2 + _lookupNumber], \
WINED3D_ATR_SIZE(position2), \
WINED3D_ATR_GLTYPE(position2), \
WINED3D_ATR_NORMALIZED(position2), \
sd->u.s.position2.dwStride, \
((char *)sd->u.s.position2.lpData) + \
WINED3D_ATR_SIZE(position2) * WINED3D_ATR_TYPESIZE(position2) * _lookupNumber)); \
GL_EXTCALL(glEnableVertexAttribArrayARB(arrayUsageMap[WINED3DSHADERDECLUSAGE_POSITION2 + _lookupNumber])); \
}
/* Generate some lookup tables */
/* drop the RHW coord, there must be a nicer way of doing this. */
sd->u.s.position.dwType = min(D3DDECLTYPE_FLOAT3, sd->u.s.position.dwType);
sd->u.s.position2.dwType = min(D3DDECLTYPE_FLOAT3, sd->u.s.position.dwType);
LOAD_NUMBERED_ARRAY(blendWeights,BLENDWEIGHT);
LOAD_NUMBERED_ARRAY(blendMatrixIndices,BLENDINDICES);
LOAD_NUMBERED_ARRAY(position,POSITION);
LOAD_NUMBERED_ARRAY(normal,NORMAL);
LOAD_NUMBERED_ARRAY(pSize,PSIZE);
LOAD_NUMBERED_ARRAY(diffuse,DIFFUSE);
LOAD_NUMBERED_ARRAY(specular,SPECULAR);
LOAD_NUMBERED_ARRAY(texCoords[0],TEXCOORD0);
LOAD_NUMBERED_ARRAY(texCoords[1],TEXCOORD1);
LOAD_NUMBERED_ARRAY(texCoords[2],TEXCOORD2);
LOAD_NUMBERED_ARRAY(texCoords[3],TEXCOORD3);
LOAD_NUMBERED_ARRAY(texCoords[4],TEXCOORD4);
LOAD_NUMBERED_ARRAY(texCoords[5],TEXCOORD5);
LOAD_NUMBERED_ARRAY(texCoords[6],TEXCOORD6);
LOAD_NUMBERED_ARRAY(texCoords[7],TEXCOORD7);
#if 0 /* TODO: Samplers may allow for more texture coords */
LOAD_NUMBERED_ARRAY(texCoords[8],TEXCOORD8);
LOAD_NUMBERED_ARRAY(texCoords[9],TEXCOORD9);
LOAD_NUMBERED_ARRAY(texCoords[10],TEXCOORD10);
LOAD_NUMBERED_ARRAY(texCoords[11],TEXCOORD11);
LOAD_NUMBERED_ARRAY(texCoords[12],TEXCOORD12);
LOAD_NUMBERED_ARRAY(texCoords[13],TEXCOORD13);
LOAD_NUMBERED_ARRAY(texCoords[14],TEXCOORD14);
LOAD_NUMBERED_ARRAY(texCoords[15],TEXCOORD15);
#endif
LOAD_NUMBERED_ARRAY(position,POSITIONT);
/* d3d9 types */
LOAD_NUMBERED_ARRAY(tangent,TANGENT);
LOAD_NUMBERED_ARRAY(binormal,BINORMAL);
LOAD_NUMBERED_ARRAY(tessFactor,TESSFACTOR);
LOAD_NUMBERED_ARRAY(position2,POSITION2);
/* there can be lots of position arrays */
LOAD_NUMBERED_POSITION_ARRAY(0);
LOAD_NUMBERED_POSITION_ARRAY(1);
LOAD_NUMBERED_POSITION_ARRAY(2);
LOAD_NUMBERED_POSITION_ARRAY(3);
LOAD_NUMBERED_POSITION_ARRAY(4);
LOAD_NUMBERED_POSITION_ARRAY(5);
LOAD_NUMBERED_POSITION_ARRAY(6);
LOAD_NUMBERED_POSITION_ARRAY(7);
LOAD_NUMBERED_ARRAY(position2,POSITIONT2);
LOAD_NUMBERED_ARRAY(normal2,NORMAL2);
LOAD_NUMBERED_ARRAY(fog,FOG);
LOAD_NUMBERED_ARRAY(depth,DEPTH);
LOAD_NUMBERED_ARRAY(sample,SAMPLE);
#undef LOAD_NUMBERED_ARRAY
}
static void loadVertexData(IWineD3DDevice *iface, Direct3DVertexStridedData *sd) {
unsigned int textureNo = 0;
IWineD3DDeviceImpl *This = (IWineD3DDeviceImpl *)iface;
TRACE("Using fast vertex array code\n");
/* Blend Data ---------------------------------------------- */
if ((sd->u.s.blendWeights.lpData != NULL) ||
(sd->u.s.blendMatrixIndices.lpData != NULL)) {
if (GL_SUPPORT(ARB_VERTEX_BLEND)) {
#if 1
glEnableClientState(GL_WEIGHT_ARRAY_ARB);
checkGLcall("glEnableClientState(GL_WEIGHT_ARRAY_ARB)");
#endif
TRACE("Blend %d %p %ld\n", WINED3D_ATR_SIZE(blendWeights), sd->u.s.blendWeights.lpData, sd->u.s.blendWeights.dwStride);
/* FIXME("TODO\n");*/
/* Note dwType == float3 or float4 == 2 or 3 */
#if 0
/* with this on, the normals appear to be being modified,
but the vertices aren't being translated as they should be
Maybe the world matrix aren't being setup properly? */
glVertexBlendARB(WINED3D_ATR_SIZE(blendWeights) + 1);
#endif
VTRACE(("glWeightPointerARB(%ld, GL_FLOAT, %ld, %p)\n",
WINED3D_ATR_SIZE(blendWeights) ,
sd->u.s.blendWeights.dwStride,
sd->u.s.blendWeights.lpData));
GL_EXTCALL(glWeightPointerARB)(WINED3D_ATR_SIZE(blendWeights), WINED3D_ATR_GLTYPE(blendWeights),
sd->u.s.blendWeights.dwStride,
sd->u.s.blendWeights.lpData);
checkGLcall("glWeightPointerARB");
if(sd->u.s.blendMatrixIndices.lpData != NULL){
static BOOL showfixme = TRUE;
if(showfixme){
FIXME("blendMatrixIndices support\n");
showfixme = FALSE;
}
}
} else if (GL_SUPPORT(EXT_VERTEX_WEIGHTING)) {
/* FIXME("TODO\n");*/
#if 0
GL_EXTCALL(glVertexWeightPointerEXT)(WINED3D_ATR_SIZE(blendWeights), WINED3D_ATR_GLTYPE(blendWeights),
sd->u.s.blendWeights.dwStride,
sd->u.s.blendWeights.lpData);
checkGLcall("glVertexWeightPointerEXT(numBlends, ...)");
glEnableClientState(GL_VERTEX_WEIGHT_ARRAY_EXT);
checkGLcall("glEnableClientState(GL_VERTEX_WEIGHT_ARRAY_EXT)");
#endif
} else {
/* TODO: support blends in fixupVertices */
FIXME("unsupported blending in openGl\n");
}
} else {
if (GL_SUPPORT(ARB_VERTEX_BLEND)) {
#if 0 /* TODO: Vertex blending */
glDisable(GL_VERTEX_BLEND_ARB);
#endif
TRACE("ARB_VERTEX_BLEND \n");
} else if (GL_SUPPORT(EXT_VERTEX_WEIGHTING)) {
TRACE(" EXT_VERTEX_WEIGHTING\n");
glDisableClientState(GL_VERTEX_WEIGHT_ARRAY_EXT);
checkGLcall("glDisableClientState(GL_VERTEX_WEIGHT_ARRAY_EXT)");
}
}
#if 0 /* FOG ----------------------------------------------*/
if (sd->u.s.fog.lpData != NULL) {
/* TODO: fog*/
if (GL_SUPPORT(EXT_FOG_COORD) {
glEnableClientState(GL_FOG_COORD_EXT);
(GL_EXTCALL)(FogCoordPointerEXT)(WINED3D_ATR_GLTYPE(fog),
sd->u.s.fog.dwStride,
sd->u.s.fog.lpData);
} else {
/* don't bother falling back to 'slow' as we don't support software FOG yet. */
/* FIXME: fixme once */
TRACE("Hardware support for FOG is not avaiable, FOG disabled. \n");
}
} else {
if (GL_SUPPRT(EXT_FOR_COORD) {
/* make sure fog is disabled */
glDisableClientState(GL_FOG_COORD_EXT);
}
}
#endif
#if 0 /* tangents ----------------------------------------------*/
if (sd->u.s.tangent.lpData != NULL || sd->u.s.binormal.lpData != NULL) {
/* TODO: tangents*/
if (GL_SUPPORT(EXT_COORDINATE_FRAME) {
if (sd->u.s.tangent.lpData != NULL) {
glEnable(GL_TANGENT_ARRAY_EXT);
(GL_EXTCALL)(TangentPointerEXT)(WINED3D_ATR_GLTYPE(tangent),
sd->u.s.tangent.dwStride,
sd->u.s.tangent.lpData);
} else {
glDisable(GL_TANGENT_ARRAY_EXT);
}
if (sd->u.s.binormal.lpData != NULL) {
glEnable(GL_BINORMAL_ARRAY_EXT);
(GL_EXTCALL)(BinormalPointerEXT)(WINED3D_ATR_GLTYPE(binormal),
sd->u.s.binormal.dwStride,
sd->u.s.binormal.lpData);
} else{
glDisable(GL_BINORMAL_ARRAY_EXT);
}
} else {
/* don't bother falling back to 'slow' as we don't support software tangents and binormals yet . */
/* FIXME: fixme once */
TRACE("Hardware support for tangents and binormals is not avaiable, tangents and binormals disabled. \n");
}
} else {
if (GL_SUPPORT(EXT_COORDINATE_FRAME) {
/* make sure fog is disabled */
glDisable(GL_TANGENT_ARRAY_EXT);
glDisable(GL_BINORMAL_ARRAY_EXT);
}
}
#endif
/* Point Size ----------------------------------------------*/
if (sd->u.s.pSize.lpData != NULL) {
/* no such functionality in the fixed function GL pipeline */
TRACE("Cannot change ptSize here in openGl\n");
/* TODO: Implement this function in using shaders if they are available */
}
/* Vertex Pointers -----------------------------------------*/
if (sd->u.s.position.lpData != NULL) {
/* Note dwType == float3 or float4 == 2 or 3 */
VTRACE(("glVertexPointer(%ld, GL_FLOAT, %ld, %p)\n",
sd->u.s.position.dwStride,
sd->u.s.position.dwType + 1,
sd->u.s.position.lpData));
/* min(WINED3D_ATR_SIZE(position),3) to Disable RHW mode as 'w' coord
handling for rhw mode should not impact screen position whereas in GL it does.
This may result in very slightly distored textures in rhw mode, but
a very minimal different. There's always the other option of
fixing the view matrix to prevent w from having any effect */
glVertexPointer(3 /* min(WINED3D_ATR_SIZE(position),3) */, WINED3D_ATR_GLTYPE(position),
sd->u.s.position.dwStride, sd->u.s.position.lpData);
checkGLcall("glVertexPointer(...)");
glEnableClientState(GL_VERTEX_ARRAY);
checkGLcall("glEnableClientState(GL_VERTEX_ARRAY)");
} else {
glDisableClientState(GL_VERTEX_ARRAY);
checkGLcall("glDisableClientState(GL_VERTEX_ARRAY)");
}
/* Normals -------------------------------------------------*/
if (sd->u.s.normal.lpData != NULL) {
/* Note dwType == float3 or float4 == 2 or 3 */
VTRACE(("glNormalPointer(GL_FLOAT, %ld, %p)\n",
sd->u.s.normal.dwStride,
sd->u.s.normal.lpData));
glNormalPointer(WINED3D_ATR_GLTYPE(normal),
sd->u.s.normal.dwStride,
sd->u.s.normal.lpData);
checkGLcall("glNormalPointer(...)");
glEnableClientState(GL_NORMAL_ARRAY);
checkGLcall("glEnableClientState(GL_NORMAL_ARRAY)");
} else {
glDisableClientState(GL_NORMAL_ARRAY);
checkGLcall("glDisableClientState(GL_NORMAL_ARRAY)");
glNormal3f(0, 0, 1);
checkGLcall("glNormal3f(0, 0, 1)");
}
/* Diffuse Colour --------------------------------------------*/
/* WARNING: Data here MUST be in RGBA format, so cannot */
/* go directly into fast mode from app pgm, because */
/* directx requires data in BGRA format. */
/* currently fixupVertices swizels the format, but this isn't */
/* very practical when using VBOS */
/* NOTE: Unless we write a vertex shader to swizel the colour */
/* , or the user doesn't care and wants the speed advantage */
if (sd->u.s.diffuse.lpData != NULL) {
/* Note dwType == float3 or float4 == 2 or 3 */
VTRACE(("glColorPointer(4, GL_UNSIGNED_BYTE, %ld, %p)\n",
sd->u.s.diffuse.dwStride,
sd->u.s.diffuse.lpData));
glColorPointer(4, GL_UNSIGNED_BYTE,
sd->u.s.diffuse.dwStride,
sd->u.s.diffuse.lpData);
checkGLcall("glColorPointer(4, GL_UNSIGNED_BYTE, ...)");
glEnableClientState(GL_COLOR_ARRAY);
checkGLcall("glEnableClientState(GL_COLOR_ARRAY)");
} else {
glDisableClientState(GL_COLOR_ARRAY);
checkGLcall("glDisableClientState(GL_COLOR_ARRAY)");
glColor4f(1.0f, 1.0f, 1.0f, 1.0f);
checkGLcall("glColor4f(1, 1, 1, 1)");
}
/* Specular Colour ------------------------------------------*/
if (sd->u.s.specular.lpData != NULL) {
TRACE("setting specular colour\n");
/* Note dwType == float3 or float4 == 2 or 3 */
VTRACE(("glSecondaryColorPointer(4, GL_UNSIGNED_BYTE, %ld, %p)\n",
sd->u.s.specular.dwStride,
sd->u.s.specular.lpData));
if (GL_SUPPORT(EXT_SECONDARY_COLOR)) {
GL_EXTCALL(glSecondaryColorPointerEXT)(4, GL_UNSIGNED_BYTE,
sd->u.s.specular.dwStride,
sd->u.s.specular.lpData);
vcheckGLcall("glSecondaryColorPointerEXT(4, GL_UNSIGNED_BYTE, ...)");
glEnableClientState(GL_SECONDARY_COLOR_ARRAY_EXT);
vcheckGLcall("glEnableClientState(GL_SECONDARY_COLOR_ARRAY_EXT)");
} else {
/* Missing specular color is not critical, no warnings */
VTRACE(("Specular colour is not supported in this GL implementation\n"));
}
} else {
if (GL_SUPPORT(EXT_SECONDARY_COLOR)) {
glDisableClientState(GL_SECONDARY_COLOR_ARRAY_EXT);
checkGLcall("glDisableClientState(GL_SECONDARY_COLOR_ARRAY_EXT)");
GL_EXTCALL(glSecondaryColor3fEXT)(0, 0, 0);
checkGLcall("glSecondaryColor3fEXT(0, 0, 0)");
} else {
/* Missing specular color is not critical, no warnings */
VTRACE(("Specular colour is not supported in this GL implementation\n"));
}
}
/* Texture coords -------------------------------------------*/
for (textureNo = 0; textureNo < GL_LIMITS(textures); ++textureNo) {
/* Select the correct texture stage */
GLCLIENTACTIVETEXTURE(textureNo);
if (This->stateBlock->textures[textureNo] != NULL) {
int coordIdx = This->stateBlock->textureState[textureNo][D3DTSS_TEXCOORDINDEX];
TRACE("Setting up texture %u, cordindx %u, data %p\n", textureNo, coordIdx, sd->u.s.texCoords[coordIdx].lpData);
if (!GL_SUPPORT(ARB_MULTITEXTURE) && textureNo > 0) {
FIXME("Program using multiple concurrent textures which this opengl implementation doesn't support\n");
glDisableClientState(GL_TEXTURE_COORD_ARRAY);
GLMULTITEXCOORD4F(textureNo, 0, 0, 0, 1);
continue;
}
if (coordIdx >= MAX_TEXTURES) {
VTRACE(("tex: %d - Skip tex coords, as being system generated\n", textureNo));
glDisableClientState(GL_TEXTURE_COORD_ARRAY);
GLMULTITEXCOORD4F(textureNo, 0, 0, 0, 1);
} else if (sd->u.s.texCoords[coordIdx].lpData == NULL) {
VTRACE(("Bound texture but no texture coordinates supplied, so skipping\n"));
glDisableClientState(GL_TEXTURE_COORD_ARRAY);
GLMULTITEXCOORD4F(textureNo, 0, 0, 0, 1);
} else {
/* The coords to supply depend completely on the fvf / vertex shader */
glTexCoordPointer(WINED3D_ATR_SIZE(texCoords[coordIdx]), WINED3D_ATR_GLTYPE(texCoords[coordIdx]), sd->u.s.texCoords[coordIdx].dwStride, sd->u.s.texCoords[coordIdx].lpData);
glEnableClientState(GL_TEXTURE_COORD_ARRAY);
}
} else {
glDisableClientState(GL_TEXTURE_COORD_ARRAY);
GLMULTITEXCOORD4F(textureNo, 0, 0, 0, 1);
}
}
}
static void drawStridedFast(IWineD3DDevice *iface,UINT numberOfVertices, GLenum glPrimitiveType,
const void *idxData, short idxSize, ULONG minIndex, ULONG startIdx) {
IWineD3DDeviceImpl *This = (IWineD3DDeviceImpl *)iface;
if (idxData != NULL /* This crashes sometimes!*/) {
TRACE("(%p) : glElements(%x, %d, %ld, ...)\n", This, glPrimitiveType, numberOfVertices, minIndex);
idxData = idxData == (void *)-1 ? NULL : idxData;
#if 1
#if 0
glIndexPointer(idxSize == 2 ? GL_UNSIGNED_SHORT : GL_UNSIGNED_INT, idxSize, startIdx);
glEnableClientState(GL_INDEX_ARRAY);
#endif
glDrawElements(glPrimitiveType, numberOfVertices, idxSize == 2 ? GL_UNSIGNED_SHORT : GL_UNSIGNED_INT,
(const char *)idxData+(idxSize * startIdx));
#else /* using drawRangeElements may be faster */
glDrawRangeElements(glPrimitiveType, minIndex, minIndex + numberOfVertices - 1, numberOfVertices,
idxSize == 2 ? GL_UNSIGNED_SHORT : GL_UNSIGNED_INT,
(const char *)idxData+(idxSize * startIdx));
#endif
checkGLcall("glDrawRangeElements");
} else {
/* Note first is now zero as we shuffled along earlier */
TRACE("(%p) : glDrawArrays(%x, 0, %d)\n", This, glPrimitiveType, numberOfVertices);
glDrawArrays(glPrimitiveType, 0, numberOfVertices);
checkGLcall("glDrawArrays");
}
return;
}
/*
* Actually draw using the supplied information.
* Slower GL version which extracts info about each vertex in turn
*/
static void drawStridedSlow(IWineD3DDevice *iface, Direct3DVertexStridedData *sd,
UINT NumVertexes, GLenum glPrimType,
const void *idxData, short idxSize, ULONG minIndex, ULONG startIdx) {
unsigned int textureNo = 0;
const short *pIdxBufS = NULL;
const long *pIdxBufL = NULL;
LONG SkipnStrides = 0;
LONG vx_index;
float x = 0.0f, y = 0.0f, z = 0.0f; /* x,y,z coordinates */
float nx = 0.0f, ny = 0.0, nz = 0.0f; /* normal x,y,z coordinates */
float rhw = 0.0f; /* rhw */
float ptSize = 0.0f; /* Point size */
DWORD diffuseColor = 0xFFFFFFFF; /* Diffuse Color */
DWORD specularColor = 0; /* Specular Color */
IWineD3DDeviceImpl *This = (IWineD3DDeviceImpl *)iface;
TRACE("Using slow vertex array code\n");
/* Variable Initialization */
if (idxData != NULL) {
if (idxSize == 2) pIdxBufS = (const short *) idxData;
else pIdxBufL = (const long *) idxData;
}
/* Start drawing in GL */
VTRACE(("glBegin(%x)\n", glPrimType));
glBegin(glPrimType);
/* For each primitive */
for (vx_index = 0; vx_index < NumVertexes; ++vx_index) {
/* Initialize diffuse color */
diffuseColor = 0xFFFFFFFF;
/* For indexed data, we need to go a few more strides in */
if (idxData != NULL) {
/* Indexed so work out the number of strides to skip */
if (idxSize == 2) {
VTRACE(("Idx for vertex %ld = %d\n", vx_index, pIdxBufS[startIdx+vx_index]));
SkipnStrides = pIdxBufS[startIdx + vx_index];
} else {
VTRACE(("Idx for vertex %ld = %ld\n", vx_index, pIdxBufL[startIdx+vx_index]));
SkipnStrides = pIdxBufL[startIdx + vx_index];
}
}
/* Position Information ------------------ */
if (sd->u.s.position.lpData != NULL) {
float *ptrToCoords = (float *)(sd->u.s.position.lpData + (SkipnStrides * sd->u.s.position.dwStride));
x = ptrToCoords[0];
y = ptrToCoords[1];
z = ptrToCoords[2];
rhw = 1.0;
VTRACE(("x,y,z=%f,%f,%f\n", x,y,z));
/* RHW follows, only if transformed, ie 4 floats were provided */
if (sd->u.s.position.dwType == D3DDECLTYPE_FLOAT4) {
rhw = ptrToCoords[3];
VTRACE(("rhw=%f\n", rhw));
}
}
/* Blending data -------------------------- */
if (sd->u.s.blendWeights.lpData != NULL) {
/* float *ptrToCoords = (float *)(sd->u.s.blendWeights.lpData + (SkipnStrides * sd->u.s.blendWeights.dwStride)); */
FIXME("Blending not supported yet\n");
if (sd->u.s.blendMatrixIndices.lpData != NULL) {
/*DWORD *ptrToCoords = (DWORD *)(sd->u.s.blendMatrixIndices.lpData + (SkipnStrides * sd->u.s.blendMatrixIndices.dwStride));*/
}
}
/* Vertex Normal Data (untransformed only)- */
if (sd->u.s.normal.lpData != NULL) {
float *ptrToCoords = (float *)(sd->u.s.normal.lpData + (SkipnStrides * sd->u.s.normal.dwStride));
nx = ptrToCoords[0];
ny = ptrToCoords[1];
nz = ptrToCoords[2];
VTRACE(("nx,ny,nz=%f,%f,%f\n", nx, ny, nz));
}
/* Point Size ----------------------------- */
if (sd->u.s.pSize.lpData != NULL) {
float *ptrToCoords = (float *)(sd->u.s.pSize.lpData + (SkipnStrides * sd->u.s.pSize.dwStride));
ptSize = ptrToCoords[0];
VTRACE(("ptSize=%f\n", ptSize));
FIXME("No support for ptSize yet\n");
}
/* Diffuse -------------------------------- */
if (sd->u.s.diffuse.lpData != NULL) {
DWORD *ptrToCoords = (DWORD *)(sd->u.s.diffuse.lpData + (SkipnStrides * sd->u.s.diffuse.dwStride));
diffuseColor = ptrToCoords[0];
VTRACE(("diffuseColor=%lx\n", diffuseColor));
}
/* Specular -------------------------------- */
if (sd->u.s.specular.lpData != NULL) {
DWORD *ptrToCoords = (DWORD *)(sd->u.s.specular.lpData + (SkipnStrides * sd->u.s.specular.dwStride));
specularColor = ptrToCoords[0];
VTRACE(("specularColor=%lx\n", specularColor));
}
/* Texture coords --------------------------- */
for (textureNo = 0; textureNo < GL_LIMITS(textures); ++textureNo) {
if (!GL_SUPPORT(ARB_MULTITEXTURE) && textureNo > 0) {
FIXME("Program using multiple concurrent textures which this opengl implementation doesn't support\n");
continue ;
}
/* Query tex coords */
if (This->stateBlock->textures[textureNo] != NULL) {
int coordIdx = This->stateBlock->textureState[textureNo][D3DTSS_TEXCOORDINDEX];
float *ptrToCoords = (float *)(sd->u.s.texCoords[coordIdx].lpData + (SkipnStrides * sd->u.s.texCoords[coordIdx].dwStride));
float s = 0.0, t = 0.0, r = 0.0, q = 0.0;
if (coordIdx > 7) {
VTRACE(("tex: %d - Skip tex coords, as being system generated\n", textureNo));
continue;
} else if (sd->u.s.texCoords[coordIdx].lpData == NULL) {
TRACE("tex: %d - Skipping tex coords, as no data supplied\n", textureNo);
continue;
} else {
int coordsToUse = sd->u.s.texCoords[coordIdx].dwType + 1; /* 0 == D3DDECLTYPE_FLOAT1 etc */
/* The coords to supply depend completely on the fvf / vertex shader */
switch (coordsToUse) {
case 4: q = ptrToCoords[3]; /* drop through */
case 3: r = ptrToCoords[2]; /* drop through */
case 2: t = ptrToCoords[1]; /* drop through */
case 1: s = ptrToCoords[0];
}
/* Projected is more 'fun' - Move the last coord to the 'q'
parameter (see comments under D3DTSS_TEXTURETRANSFORMFLAGS */
if ((This->stateBlock->textureState[textureNo][D3DTSS_TEXTURETRANSFORMFLAGS] != D3DTTFF_DISABLE) &&
(This->stateBlock->textureState[textureNo][D3DTSS_TEXTURETRANSFORMFLAGS] & D3DTTFF_PROJECTED)) {
if (This->stateBlock->textureState[textureNo][D3DTSS_TEXTURETRANSFORMFLAGS] & D3DTTFF_PROJECTED) {
switch (coordsToUse) {
case 0: /* Drop Through */
case 1:
FIXME("D3DTTFF_PROJECTED but only zero or one coordinate?\n");
break;
case 2:
q = t;
t = 0.0;
coordsToUse = 4;
break;
case 3:
q = r;
r = 0.0;
coordsToUse = 4;
break;
case 4: /* Nop here */
break;
default:
FIXME("Unexpected D3DTSS_TEXTURETRANSFORMFLAGS value of %ld\n",
This->stateBlock->textureState[textureNo][D3DTSS_TEXTURETRANSFORMFLAGS] & D3DTTFF_PROJECTED);
}
}
}
switch (coordsToUse) { /* Supply the provided texture coords */
case D3DTTFF_COUNT1:
VTRACE(("tex:%d, s=%f\n", textureNo, s));
if (GL_SUPPORT(ARB_MULTITEXTURE)) {
GLMULTITEXCOORD1F(textureNo, s);
} else {
glTexCoord1f(s);
}
break;
case D3DTTFF_COUNT2:
VTRACE(("tex:%d, s=%f, t=%f\n", textureNo, s, t));
if (GL_SUPPORT(ARB_MULTITEXTURE)) {
GLMULTITEXCOORD2F(textureNo, s, t);
} else {
glTexCoord2f(s, t);
}
break;
case D3DTTFF_COUNT3:
VTRACE(("tex:%d, s=%f, t=%f, r=%f\n", textureNo, s, t, r));
if (GL_SUPPORT(ARB_MULTITEXTURE)) {
GLMULTITEXCOORD3F(textureNo, s, t, r);
} else {
glTexCoord3f(s, t, r);
}
break;
case D3DTTFF_COUNT4:
VTRACE(("tex:%d, s=%f, t=%f, r=%f, q=%f\n", textureNo, s, t, r, q));
if (GL_SUPPORT(ARB_MULTITEXTURE)) {
GLMULTITEXCOORD4F(textureNo, s, t, r, q);
} else {
glTexCoord4f(s, t, r, q);
}
break;
default:
FIXME("Should not get here as coordsToUse is two bits only (%x)!\n", coordsToUse);
}
}
}
} /* End of textures */
/* Diffuse -------------------------------- */
if (sd->u.s.diffuse.lpData != NULL) {
glColor4ub((diffuseColor >> 16) & 0xFF,
(diffuseColor >> 8) & 0xFF,
(diffuseColor >> 0) & 0xFF,
(diffuseColor >> 24) & 0xFF);
VTRACE(("glColor4f: r,g,b,a=%f,%f,%f,%f\n",
((diffuseColor >> 16) & 0xFF) / 255.0f,
((diffuseColor >> 8) & 0xFF) / 255.0f,
((diffuseColor >> 0) & 0xFF) / 255.0f,
((diffuseColor >> 24) & 0xFF) / 255.0f));
} else {
if (vx_index == 0) glColor4f(1.0f, 1.0f, 1.0f, 1.0f);
}
/* Specular ------------------------------- */
if (sd->u.s.diffuse.lpData != NULL) {
VTRACE(("glSecondaryColor4ub: r,g,b=%f,%f,%f\n",
((specularColor >> 16) & 0xFF) / 255.0f,
((specularColor >> 8) & 0xFF) / 255.0f,
((specularColor >> 0) & 0xFF) / 255.0f));
if (GL_SUPPORT(EXT_SECONDARY_COLOR)) {
GL_EXTCALL(glSecondaryColor3ubEXT)(
(specularColor >> 16) & 0xFF,
(specularColor >> 8) & 0xFF,
(specularColor >> 0) & 0xFF);
} else {
/* Do not worry if specular colour missing and disable request */
VTRACE(("Specular color extensions not supplied\n"));
}
} else {
if (vx_index == 0) {
if (GL_SUPPORT(EXT_SECONDARY_COLOR)) {
GL_EXTCALL(glSecondaryColor3fEXT)(0, 0, 0);
} else {
/* Do not worry if specular colour missing and disable request */
VTRACE(("Specular color extensions not supplied\n"));
}
}
}
/* Normal -------------------------------- */
if (sd->u.s.normal.lpData != NULL) {
VTRACE(("glNormal:nx,ny,nz=%f,%f,%f\n", nx,ny,nz));
glNormal3f(nx, ny, nz);
} else {
if (vx_index == 0) glNormal3f(0, 0, 1);
}
/* Position -------------------------------- */
if (sd->u.s.position.lpData != NULL) {
if (1.0f == rhw || ((rhw < 0.0001f) && (rhw > -0.0001f))) {
VTRACE(("Vertex: glVertex:x,y,z=%f,%f,%f\n", x,y,z));
glVertex3f(x, y, z);
} else {
GLfloat w = 1.0 / rhw;
VTRACE(("Vertex: glVertex:x,y,z=%f,%f,%f / rhw=%f\n", x,y,z,rhw));
glVertex4f(x*w, y*w, z*w, w);
}
}
/* For non indexed mode, step onto next parts */
if (idxData == NULL) {
++SkipnStrides;
}
}
glEnd();
checkGLcall("glEnd and previous calls");
}
#if 0 /* TODO: Software/Hardware vertex blending support */
/*
* Draw with emulated vertex shaders
* Note: strided data is uninitialized, as we need to pass the vertex
* shader directly as ordering irs yet
*/
void drawStridedSoftwareVS(IWineD3DDevice *iface, Direct3DVertexStridedData *sd,
int PrimitiveType, ULONG NumPrimitives,
const void *idxData, short idxSize, ULONG minIndex, ULONG startIdx) {
unsigned int textureNo = 0;
GLenum glPrimType = GL_POINTS;
int NumVertexes = NumPrimitives;
const short *pIdxBufS = NULL;
const long *pIdxBufL = NULL;
LONG SkipnStrides = 0;
LONG vx_index;
float x = 0.0f, y = 0.0f, z = 0.0f; /* x,y,z coordinates */
float rhw = 0.0f; /* rhw */
float ptSize = 0.0f; /* Point size */
D3DVECTOR_4 texcoords[8]; /* Texture Coords */
int numcoords[8]; /* Number of coords */
IWineD3DDeviceImpl *This = (IWineD3DDeviceImpl *)iface;
IDirect3DVertexShaderImpl* vertexShader = NULL;
TRACE("Using slow software vertex shader code\n");
/* Variable Initialization */
if (idxData != NULL) {
if (idxSize == 2) pIdxBufS = (const short *) idxData;
else pIdxBufL = (const long *) idxData;
}
/* Ok, Work out which primitive is requested and how many vertexes that will be */
NumVertexes = primitiveToGl(PrimitiveType, NumPrimitives, &glPrimType);
/* Retrieve the VS information */
vertexShader = (IWineD3DVertexShaderImp *)This->stateBlock->VertexShader;
/* Start drawing in GL */
VTRACE(("glBegin(%x)\n", glPrimType));
glBegin(glPrimType);
/* For each primitive */
for (vx_index = 0; vx_index < NumVertexes; ++vx_index) {
/* For indexed data, we need to go a few more strides in */
if (idxData != NULL) {
/* Indexed so work out the number of strides to skip */
if (idxSize == 2) {
VTRACE(("Idx for vertex %ld = %d\n", vx_index, pIdxBufS[startIdx+vx_index]));
SkipnStrides = pIdxBufS[startIdx+vx_index];
} else {
VTRACE(("Idx for vertex %ld = %ld\n", vx_index, pIdxBufL[startIdx+vx_index]));
SkipnStrides = pIdxBufL[startIdx+vx_index];
}
}
/* Fill the vertex shader input */
IDirect3DDeviceImpl_FillVertexShaderInputSW(This, vertexShader, SkipnStrides);
/* Initialize the output fields to the same defaults as it would normally have */
memset(&vertexShader->output, 0, sizeof(VSHADEROUTPUTDATA8));
vertexShader->output.oD[0].x = 1.0;
vertexShader->output.oD[0].y = 1.0;
vertexShader->output.oD[0].z = 1.0;
vertexShader->output.oD[0].w = 1.0;
/* Now execute the vertex shader */
IDirect3DVertexShaderImpl_ExecuteSW(vertexShader, &vertexShader->input, &vertexShader->output);
/*
TRACE_VECTOR(vertexShader->output.oPos);
TRACE_VECTOR(vertexShader->output.oD[0]);
TRACE_VECTOR(vertexShader->output.oD[1]);
TRACE_VECTOR(vertexShader->output.oT[0]);
TRACE_VECTOR(vertexShader->output.oT[1]);
TRACE_VECTOR(vertexShader->input.V[0]);
TRACE_VECTOR(vertexShader->data->C[0]);
TRACE_VECTOR(vertexShader->data->C[1]);
TRACE_VECTOR(vertexShader->data->C[2]);
TRACE_VECTOR(vertexShader->data->C[3]);
TRACE_VECTOR(vertexShader->data->C[4]);
TRACE_VECTOR(vertexShader->data->C[5]);
TRACE_VECTOR(vertexShader->data->C[6]);
TRACE_VECTOR(vertexShader->data->C[7]);
*/
/* Extract out the output */
/* FIXME: Fog coords? */
x = vertexShader->output.oPos.x;
y = vertexShader->output.oPos.y;
z = vertexShader->output.oPos.z;
rhw = vertexShader->output.oPos.w;
ptSize = vertexShader->output.oPts.x; /* Fixme - Is this right? */
/** Update textures coords using vertexShader->output.oT[0->7] */
memset(texcoords, 0x00, sizeof(texcoords));
memset(numcoords, 0x00, sizeof(numcoords));
for (textureNo = 0; textureNo < GL_LIMITS(textures); ++textureNo) {
if (This->stateBlock->textures[textureNo] != NULL) {
texcoords[textureNo].x = vertexShader->output.oT[textureNo].x;
texcoords[textureNo].y = vertexShader->output.oT[textureNo].y;
texcoords[textureNo].z = vertexShader->output.oT[textureNo].z;
texcoords[textureNo].w = vertexShader->output.oT[textureNo].w;
if (This->stateBlock->texture_state[textureNo][D3DTSS_TEXTURETRANSFORMFLAGS] != D3DTTFF_DISABLE) {
numcoords[textureNo] = This->stateBlock->texture_state[textureNo][D3DTSS_TEXTURETRANSFORMFLAGS] & ~D3DTTFF_PROJECTED;
} else {
switch (IDirect3DBaseTexture8Impl_GetType((LPDIRECT3DBASETEXTURE8) This->stateBlock->textures[textureNo])) {
case D3DRTYPE_TEXTURE: numcoords[textureNo] = 2; break;
case D3DRTYPE_VOLUMETEXTURE: numcoords[textureNo] = 3; break;
default: numcoords[textureNo] = 4;
}
}
} else {
numcoords[textureNo] = 0;
}
}
/* Draw using this information */
draw_vertex(iface,
TRUE, x, y, z, rhw,
TRUE, 0.0f, 0.0f, 1.0f,
TRUE, (float*) &vertexShader->output.oD[0],
TRUE, (float*) &vertexShader->output.oD[1],
FALSE, ptSize, /* FIXME: Change back when supported */
texcoords, numcoords);
/* For non indexed mode, step onto next parts */
if (idxData == NULL) {
++SkipnStrides;
}
} /* for each vertex */
glEnd();
checkGLcall("glEnd and previous calls");
}
#endif
void inline drawPrimitiveDrawStrided(IWineD3DDevice *iface, BOOL useVertexShaderFunction, int useHW, Direct3DVertexStridedData *dataLocations,
UINT numberOfvertices, UINT numberOfIndicies, GLenum glPrimType, const void *idxData, short idxSize, int minIndex, long StartIdx) {
IWineD3DDeviceImpl *This = (IWineD3DDeviceImpl *)iface;
/* Now draw the graphics to the screen */
if (FALSE /* disable software vs for now */ && useVertexShaderFunction && !useHW) {
FIXME("drawing using software vertex shaders (line %d)\n", __LINE__);
/* Ideally, we should have software FV and hardware VS, possibly
depending on the device type? */
#if 0 /* TODO: vertex and pixel shaders */
drawStridedSoftwareVS(iface, dataLocations, PrimitiveType, NumPrimitives,
idxData, idxSize, minIndex, StartIdx);
#endif
} else {
/* TODO: Work out if fixup are required at all (this can be a flag against the vertex declaration) */
int startStride = idxData == NULL ? 0 : idxData == (void *) -1 ? 0 :(idxSize == 2 ? *(((const short *) idxData) + StartIdx) : *((const int *) idxData) + StartIdx);
int endStride = startStride;
TRACE("begin Start stride %d, end stride %d, number of indices%d, number of vertices%d\n", startStride, endStride, numberOfIndicies, numberOfvertices);
#if 0 /* TODO: Vertex fixups (diffuse and specular) */
if (idxData != NULL) { /* index data isn't linear, so lookup the real start and end strides */
int t;
if (idxSize == 2) {
unsigned short *index = (unsigned short *)idxData;
index += StartIdx;
for (t = 0 ; t < numberOfIndicies; t++) {
if (startStride > *index)
startStride = *index;
if (endStride < *index)
endStride = *index;
index++;
}
} else { /* idxSize == 4 */
unsigned int *index = (unsigned int *)idxData;
index += StartIdx;
for (t = 0 ; t < numberOfIndicies; t++) {
if (startStride > *index)
startStride = *index;
if (endStride < *index)
endStride = *index;
index++;
}
}
} else {
endStride += numberOfvertices -1;
}
#endif
TRACE("end Start stride %d, end stride %d, number of indices%d, number of vertices%d\n", startStride, endStride, numberOfIndicies, numberOfvertices);
/* pre-transform verticex */
/* TODO: Caching, VBO's etc.. */
/* Generate some fixme's if unsupported functionality is being used */
#define BUFFER_OR_DATA(_attribute) dataLocations->u.s._attribute.lpData
/* TODO: Either support missing functionality in fixupVertices or by creating a shader to replace the pipeline. */
if (!useVertexShaderFunction && (BUFFER_OR_DATA(blendMatrixIndices) || BUFFER_OR_DATA(blendWeights))) {
FIXME("Blending data is only valid with vertex shaders %p %p\n",dataLocations->u.s.blendWeights.lpData,dataLocations->u.s.blendWeights.lpData);
}
if (!useVertexShaderFunction && (BUFFER_OR_DATA(position2) || BUFFER_OR_DATA(normal2))) {
FIXME("Tweening is only valid with vertex shaders\n");
}
if (!useVertexShaderFunction && (BUFFER_OR_DATA(tangent) || BUFFER_OR_DATA(binormal))) {
FIXME("Tangent and binormal bump mapping is only valid with vertex shaders\n");
}
if (!useVertexShaderFunction && (BUFFER_OR_DATA(tessFactor) || BUFFER_OR_DATA(fog) || BUFFER_OR_DATA(depth) || BUFFER_OR_DATA(sample))) {
FIXME("Extended attributes are only valid with vertex shaders\n");
}
#undef BUFFER_OR_DATA
#if 0/* TODO: Vertex fixups (diffuse and specular) */
fixupVertices(This, dataLocations, &transformedDataLocations, 1 + endStride - startStride, startStride);
#endif
/* vertex shaders */
/* If the only vertex data used by the shader is supported by OpenGL then*/
if ((!useVertexShaderFunction && dataLocations->u.s.pSize.lpData == NULL
&& dataLocations->u.s.diffuse.lpData == NULL && dataLocations->u.s.specular.lpData == NULL)
|| (useVertexShaderFunction && ((IWineD3DVertexShaderImpl *)This->stateBlock->vertexShader)->namedArrays && !((IWineD3DVertexShaderImpl *)This->stateBlock->vertexShader)->declaredArrays)) {
/* Load the vertex data using named arrays */
TRACE("(%p) Loading vertex data\n", This);
loadVertexData(iface, dataLocations);
} else /* Otherwise */
if(useVertexShaderFunction && ((IWineD3DVertexShaderImpl *)This->stateBlock->vertexShader)->declaredArrays) {
/* load the array data using ordinal mapping */
loadNumberedArrays(iface, dataLocations, ((IWineD3DVertexShaderImpl *)This->stateBlock->vertexShader)->arrayUsageMap);
} else { /* If this happens we must drawStridedSlow later on */
TRACE("Not loading vertex data\n");
}
TRACE("Loaded arrays\n");
if (useVertexShaderFunction) {
int i;
GLint errPos;
FIXME("Using vertex shader\n");
/* Bind the vertex program */
GL_EXTCALL(glBindProgramARB(GL_VERTEX_PROGRAM_ARB, ((IWineD3DVertexShaderImpl *)This->stateBlock->vertexShader)->prgId));
checkGLcall("glBindProgramARB(GL_VERTEX_PROGRAM_ARB, vertexShader->prgId);");
/* and enable gl vertex shaders */
glEnable(GL_VERTEX_PROGRAM_ARB);
checkGLcall("glEnable(GL_VERTEX_PROGRAM_ARB);");
TRACE_(d3d_shader)("(%p) bound program %u and enabled vertex program ARB\n", This, ((IWineD3DVertexShaderImpl *)This->stateBlock->vertexShader)->prgId);
/* Update the constants */
for (i = 0; i < WINED3D_VSHADER_MAX_CONSTANTS; i++) {
/* TODO: add support for Integer and Boolean constants */
GL_EXTCALL(glProgramEnvParameter4fvARB(GL_VERTEX_PROGRAM_ARB, i, &This->stateBlock->vertexShaderConstantF[i * 4]));
TRACE_(d3d_shader)("Loading constants %u = %f %f %f %f\n",i, This->stateBlock->vertexShaderConstantF[i *4 ], This->stateBlock->vertexShaderConstantF[i * 4 + 1], This->stateBlock->vertexShaderConstantF[i *4 + 2], This->stateBlock->vertexShaderConstantF[i * 4 + 3]);
checkGLcall("glProgramEnvParameter4fvARB(GL_VERTEX_PROGRAM_ARB");
}
/* TODO: Vertex Shader 8 constants*/
/* always draw strided fast if a vertex shader is being used */
drawStridedFast(iface, numberOfIndicies, glPrimType,
idxData, idxSize, minIndex, StartIdx);
/* check for any errors */
glGetIntegerv( GL_PROGRAM_ERROR_POSITION_ARB, &errPos );
if (errPos != -1) {
FIXME("HW VertexShader Error at position: %d\n%s\n", errPos, glGetString( GL_PROGRAM_ERROR_STRING_ARB) );
}
/* disable any attribs */
if(((IWineD3DVertexShaderImpl *)This->stateBlock->vertexShader)->declaredArrays) {
GLint maxAttribs;
int i;
/* Leave all the attribs disabled */
glGetIntegerv(GL_MAX_VERTEX_ATTRIBS_ARB, &maxAttribs);
/* MESA does not support it right not */
if (glGetError() != GL_NO_ERROR)
maxAttribs = 16;
for (i = 0; i < maxAttribs; i++) {
GL_EXTCALL(glDisableVertexAttribArrayARB(i));
checkGLcall("glDisableVertexAttribArrayARB(reg);");
}
}
/* Done */
glDisable(GL_VERTEX_PROGRAM_ARB);
} else {
/* DirectX colours are in a different format to opengl colours
so if diffuse or specular are used then we need to use drawStridedSlow
to correct the colours */
if ((dataLocations->u.s.pSize.lpData != NULL)
|| (dataLocations->u.s.diffuse.lpData != NULL)
|| (dataLocations->u.s.specular.lpData != NULL)) {
/* TODO: replace drawStridedSlow with veretx fixups */
#if 1
drawStridedSlow(iface, dataLocations, numberOfIndicies, glPrimType,
idxData, idxSize, minIndex, StartIdx) ;
/*
* drawStridedSlow(iface, dataLocations, numberOfIndicies, glPrimType,
* idxData, idxSize, minIndex, StartIdx);
*/
#endif
} else {
/* OpenGL can manage everything in hardware so we can use drawStridedFast */
drawStridedFast(iface, numberOfIndicies, glPrimType,
idxData, idxSize, minIndex, StartIdx);
}
}
}
}
void inline drawPrimitiveTraceDataLocations(Direct3DVertexStridedData *dataLocations,DWORD fvf) {
/* Dump out what parts we have supplied */
TRACE("Strided Data (from FVF/VS): %lx\n", fvf);
TRACE_STRIDED((dataLocations), position);
TRACE_STRIDED((dataLocations), blendWeights);
TRACE_STRIDED((dataLocations), blendMatrixIndices);
TRACE_STRIDED((dataLocations), normal);
TRACE_STRIDED((dataLocations), pSize);
TRACE_STRIDED((dataLocations), diffuse);
TRACE_STRIDED((dataLocations), specular);
TRACE_STRIDED((dataLocations), texCoords[0]);
TRACE_STRIDED((dataLocations), texCoords[1]);
TRACE_STRIDED((dataLocations), texCoords[2]);
TRACE_STRIDED((dataLocations), texCoords[3]);
TRACE_STRIDED((dataLocations), texCoords[4]);
TRACE_STRIDED((dataLocations), texCoords[5]);
TRACE_STRIDED((dataLocations), texCoords[6]);
TRACE_STRIDED((dataLocations), texCoords[7]);
TRACE_STRIDED((dataLocations), position2);
TRACE_STRIDED((dataLocations), normal2);
TRACE_STRIDED((dataLocations), tangent);
TRACE_STRIDED((dataLocations), binormal);
TRACE_STRIDED((dataLocations), tessFactor);
TRACE_STRIDED((dataLocations), fog);
TRACE_STRIDED((dataLocations), depth);
TRACE_STRIDED((dataLocations), sample);
return;
}
/* uploads textures and setup texture states ready for rendering */
void inline drawPrimitiveUploadTextures(IWineD3DDeviceImpl* This) {
unsigned int i;
/**
* OK, here we clear down any old junk iect in the context
* enable the new texture and apply any state changes:
*
* Loop through all textures
* select texture unit
* if there is a texture bound to that unit then..
* disable all textures types on that unit
* enable and bind the texture that is bound to that unit.
* otherwise disable all texture types on that unit.
**/
/* upload the textures */
for (i = 0; i< GL_LIMITS(textures); ++i) {
/* Bind the texture to the stage here */
if (GL_SUPPORT(ARB_MULTITEXTURE)) {
GLACTIVETEXTURE(i);
} else if (0 < i) {
/* This isn't so much a warn as a message to the user about lack of hardware support */
WARN("Program using multiple concurrent textures which this opengl implementation doesn't support\n");
}
/* don't bother with textures that have a colorop of disable */
if (This->stateBlock->textureState[i][WINED3DTSS_COLOROP] != D3DTOP_DISABLE) {
if (This->stateBlock->textures[i] != NULL) {
glDisable(GL_TEXTURE_1D);
This->stateBlock->textureDimensions[i] = IWineD3DBaseTexture_GetTextureDimensions(This->stateBlock->textures[i]);
/* disable all texture states that aren't the selected textures' dimension */
switch(This->stateBlock->textureDimensions[i]) {
case GL_TEXTURE_2D:
glDisable(GL_TEXTURE_3D);
glDisable(GL_TEXTURE_CUBE_MAP_ARB);
break;
case GL_TEXTURE_3D:
glDisable(GL_TEXTURE_CUBE_MAP_ARB);
glDisable(GL_TEXTURE_2D);
break;
case GLTEXTURECUBEMAP:
glDisable(GL_TEXTURE_2D);
glDisable(GL_TEXTURE_3D);
break;
}
/* imply GL_SUPPORT(NV_TEXTURE_SHADER) when setting texture_shader_active */
if (This->texture_shader_active && This->stateBlock->textureDimensions[i] == GL_TEXTURE_2D) {
glTexEnvi(GL_TEXTURE_SHADER_NV, GL_SHADER_OPERATION_NV, GL_TEXTURE_2D);
} else {
glEnable(This->stateBlock->textureDimensions[i]);
}
/* Load up the texture now */
IWineD3DBaseTexture_PreLoad((IWineD3DBaseTexture *) This->stateBlock->textures[i]);
IWineD3DDevice_SetupTextureStates((IWineD3DDevice *)This, i, REAPPLY_ALPHAOP);
/* this is a stub function representing the state blocks
* being separated here we are only updating the texture
* state changes, other objects and units get updated when
* they change (or need to be updated), e.g. states that
* relate to a context member line the texture unit are
* only updated when the context needs updating
*/
/* Tell the abse texture to sync it's states */
IWineD3DBaseTexture_ApplyStateChanges(This->stateBlock->textures[i], This->stateBlock->textureState[i], This->stateBlock->samplerState[i]);
}
/* Bind a default texture if no texture has been set, but colour-op is enabled */
else {
glDisable(GL_TEXTURE_2D);
glDisable(GL_TEXTURE_3D);
glDisable(GL_TEXTURE_CUBE_MAP_ARB);
glEnable(GL_TEXTURE_1D);
This->stateBlock->textureDimensions[i] = GL_TEXTURE_1D;
glBindTexture(GL_TEXTURE_1D, This->dummyTextureName[i]);
}
/** these ops apply to the texture unit, so they are preserved between texture changes, but for now brute force and reapply all
dx9_1pass_emboss_bump_mapping and dx9_2pass_emboss_bump_mapping are good texts to make sure the states are being applied when needed **/
set_tex_op((IWineD3DDevice *)This, FALSE, i, This->stateBlock->textureState[i][WINED3DTSS_COLOROP],
This->stateBlock->textureState[i][WINED3DTSS_COLORARG1],
This->stateBlock->textureState[i][WINED3DTSS_COLORARG2],
This->stateBlock->textureState[i][WINED3DTSS_COLORARG0]);
/* alphaop */
set_tex_op((IWineD3DDevice *)This, TRUE, i, This->stateBlock->textureState[i][WINED3DTSS_ALPHAOP],
This->stateBlock->textureState[i][WINED3DTSS_ALPHAARG1],
This->stateBlock->textureState[i][WINED3DTSS_ALPHAARG2],
This->stateBlock->textureState[i][WINED3DTSS_ALPHAARG0]);
} else {
/* no colorop so disable all the texture states */
glDisable(GL_TEXTURE_1D);
glDisable(GL_TEXTURE_2D);
glDisable(GL_TEXTURE_3D);
glDisable(GL_TEXTURE_CUBE_MAP_ARB);
}
}
}
/* Routine common to the draw primitive and draw indexed primitive routines */
void drawPrimitive(IWineD3DDevice *iface,
int PrimitiveType,
long NumPrimitives,
/* for Indexed: */
long StartVertexIndex,
UINT numberOfVertices,
long StartIdx,
short idxSize,
const void *idxData,
int minIndex) {
BOOL rc = FALSE;
DWORD fvf = 0;
IWineD3DDeviceImpl *This = (IWineD3DDeviceImpl *)iface;
BOOL useVertexShaderFunction = FALSE;
BOOL isLightingOn = FALSE;
Direct3DVertexStridedData dataLocations;
int useHW = FALSE;
useVertexShaderFunction = This->stateBlock->vertexShader != NULL ? wined3d_settings.vs_mode != VS_NONE ? ((IWineD3DVertexShaderImpl *)This->stateBlock->vertexShader)->function != NULL ? TRUE: FALSE : FALSE : FALSE;
if (This->stateBlock->vertexDecl == NULL) {
/* Work out what the FVF should look like */
rc = initializeFVF(iface, &fvf);
if (rc) return;
} else {
TRACE("(%p) : using vertex declaration %p \n", iface, This->stateBlock->vertexDecl);
}
/* Ok, we will be updating the screen from here onwards so grab the lock */
ENTER_GL();
#if 0 /* TODO: vertex and pixel shaders */
/* If we will be using a pixel, do some initialization for it */
if ((pixel_shader = PIXEL_SHADER(This->stateBlock->PixelShader))) {
TRACE("drawing with pixel shader handle %p\n", pixel_shader);
memset(&pixel_shader->input, 0, sizeof(PSHADERINPUTDATA8));
GL_EXTCALL(glBindProgramARB(GL_FRAGMENT_PROGRAM_ARB, pixel_shader->prgId));
checkGLcall("glBindProgramARB(GL_FRAGMENT_PROGRAM_ARB, pixel_shader->prgId);");
glEnable(GL_FRAGMENT_PROGRAM_ARB);
checkGLcall("glEnable(GL_FRAGMENT_PROGRAM_ARB);");
/* init Constants */
if (This->stateBlock->Changed.pixelShaderConstant) {
TRACE_(d3d_shader)("pixel shader initializing constants %p\n",pixel_shader);
IDirect3DPixelShaderImpl_SetConstantF(pixel_shader, 0, (CONST FLOAT*) &This->stateBlock->pixelShaderConstant[0], 8);
}
/* Update the constants */
for (i = 0; i < D3D8_PSHADER_MAX_CONSTANTS; ++i) {
GL_EXTCALL(glProgramEnvParameter4fvARB(GL_FRAGMENT_PROGRAM_ARB, i, (GLfloat *)&This->stateBlock->pixelShaderConstant[i]));
checkGLcall("glProgramEnvParameter4fvARB(GL_FRAGMENT_PROGRAM_ARB");
}
}
#endif /* TODO: vertex and pixel shaders */
/* Initialize all values to null */
memset(&dataLocations, 0x00, sizeof(dataLocations));
/* convert the FVF or vertexDeclaration into a strided stream (this should be done when the fvf or declaration is created) */
if (This->stateBlock->vertexDecl != NULL) {
TRACE("================ Vertex Declaration ===================\n");
primitiveDeclarationConvertToStridedData(iface, &dataLocations, StartVertexIndex, &fvf);
} else {
TRACE("================ FVF ===================\n");
primitiveConvertToStridedData(iface, &dataLocations, StartVertexIndex);
}
/* write out some debug information*/
drawPrimitiveTraceDataLocations(&dataLocations, fvf);
/* Setup transform matrices and sort out */
if (useHW) {
/* Lighting is not completely bypassed with ATI drivers although it should be. Mesa is ok from this respect...
So make sure lighting is disabled. */
isLightingOn = glIsEnabled(GL_LIGHTING);
glDisable(GL_LIGHTING);
checkGLcall("glDisable(GL_LIGHTING);");
TRACE("Disabled lighting as no normals supplied, old state = %d\n", isLightingOn);
} else {
isLightingOn = primitiveInitState(iface,
fvf & D3DFVF_XYZRHW,
!(fvf & D3DFVF_NORMAL),
useVertexShaderFunction);
}
/* Now initialize the materials state */
init_materials(iface, (dataLocations.u.s.diffuse.lpData != NULL));
drawPrimitiveUploadTextures(This);
{
GLenum glPrimType;
/* Ok, Work out which primitive is requested and how many vertexes that
will be */
UINT calculatedNumberOfindices = primitiveToGl(PrimitiveType, NumPrimitives, &glPrimType);
#if 0 /* debugging code... just information not an error */
if(numberOfVertices != 0 && numberOfVertices != calculatedNumberOfindices){
FIXME("Number of vertices %u and Caculated number of indicies %u differ\n", numberOfVertices, calculatedNumberOfindices);
}
#endif
if (numberOfVertices == 0 )
numberOfVertices = calculatedNumberOfindices;
drawPrimitiveDrawStrided(iface, useVertexShaderFunction, useHW, &dataLocations, numberOfVertices, calculatedNumberOfindices, glPrimType, idxData, idxSize, minIndex, StartIdx);
}
/* If vertex shaders or no normals, restore previous lighting state */
if (useVertexShaderFunction || !(fvf & D3DFVF_NORMAL)) {
if (isLightingOn) glEnable(GL_LIGHTING);
else glDisable(GL_LIGHTING);
TRACE("Restored lighting to original state\n");
}
#if 0 /* TODO: vertex and pixel shaders */
if (pixel_shader)
{
#if 0
GLint errPos;
glGetIntegerv( GL_PROGRAM_ERROR_POSITION_ARB, &errPos );
if (errPos != -1)
FIXME("HW PixelShader Error at position: %d\n%s\n", errPos, glGetString( GL_PROGRAM_ERROR_STRING_ARB) );
#endif
glDisable(GL_FRAGMENT_PROGRAM_ARB);
}
#endif
/* Finshed updating the screen, restore lock */
LEAVE_GL();
TRACE("Done all gl drawing\n");
/* Diagnostics */
#ifdef SHOW_FRAME_MAKEUP
{
static long int primCounter = 0;
/* NOTE: set primCounter to the value reported by drawprim
before you want to to write frame makeup to /tmp */
if (primCounter >= 0) {
D3DLOCKED_RECT r;
char buffer[80];
IWineD3DSurface_LockRect(This->renderTarget, &r, NULL, D3DLOCK_READONLY);
sprintf(buffer, "/tmp/backbuffer_%ld.tga", primCounter);
TRACE("Saving screenshot %s\n", buffer);
IWineD3DSurface_SaveSnapshot(This->renderTarget, buffer);
IWineD3DSurface_UnlockRect(This->renderTarget);
#ifdef SHOW_TEXTURE_MAKEUP
{
IWineD3DSurface *pSur;
int textureNo;
for (textureNo = 0; textureNo < GL_LIMITS(textures); ++textureNo) {
if (This->stateBlock->textures[textureNo] != NULL) {
sprintf(buffer, "/tmp/texture_%p_%ld_%d.tga", This->stateBlock->textures[textureNo], primCounter, textureNo);
TRACE("Saving texture %s\n", buffer);
if (IWineD3DBaseTexture_GetType(This->stateBlock->textures[textureNo]) == D3DRTYPE_TEXTURE) {
IWineD3DTexture_GetSurfaceLevel((IWineD3DTexture *)This->stateBlock->textures[textureNo], 0, &pSur);
IWineD3DSurface_SaveSnapshot(pSur, buffer);
IWineD3DSurface_Release(pSur);
} else {
FIXME("base Texture isn't of type texture %d\n", IWineD3DBaseTexture_GetType(This->stateBlock->textures[textureNo]));
}
}
}
}
#endif
}
TRACE("drawprim #%ld\n", primCounter);
++primCounter;
}
#endif
}