| /* Direct3D ExecuteBuffer |
| (c) 1998 Lionel ULMER |
| |
| This files contains the implementation of Direct3DExecuteBuffer. */ |
| |
| |
| #include "config.h" |
| #include "windows.h" |
| #include "wintypes.h" |
| #include "winerror.h" |
| #include "interfaces.h" |
| #include "heap.h" |
| #include "ddraw.h" |
| #include "d3d.h" |
| #include "debug.h" |
| |
| #include "d3d_private.h" |
| |
| #ifdef HAVE_MESAGL |
| |
| static IDirect3DExecuteBuffer_VTable executebuffer_vtable; |
| |
| /******************************************************************************* |
| * ExecuteBuffer static functions |
| */ |
| void _dump_d3dstatus(LPD3DSTATUS lpStatus) { |
| |
| } |
| |
| void _dump_executedata(LPD3DEXECUTEDATA lpData) { |
| DUMP("dwSize : %ld\n", lpData->dwSize); |
| DUMP("Vertex Offset : %ld Count : %ld\n", lpData->dwVertexOffset, lpData->dwVertexCount); |
| DUMP("Instruction Offset : %ld Length : %ld\n", lpData->dwInstructionOffset, lpData->dwInstructionLength); |
| DUMP("HVertex Offset : %ld\n", lpData->dwHVertexOffset); |
| _dump_d3dstatus(&(lpData->dsStatus)); |
| } |
| |
| #define DO_VERTEX(index) \ |
| glNormal3f(tvert[index].nx.nx, \ |
| tvert[index].ny.ny, \ |
| tvert[index].nz.nz); \ |
| glVertex3f(tvert[index].x.x, \ |
| tvert[index].y.y, \ |
| tvert[index].z.z); |
| |
| |
| static void execute(LPDIRECT3DEXECUTEBUFFER lpBuff, |
| LPDIRECT3DDEVICE dev, |
| LPDIRECT3DVIEWPORT vp) { |
| DWORD bs = lpBuff->desc.dwBufferSize; |
| DWORD vs = lpBuff->data.dwVertexOffset; |
| DWORD vc = lpBuff->data.dwVertexCount; |
| DWORD is = lpBuff->data.dwInstructionOffset; |
| DWORD il = lpBuff->data.dwInstructionLength; |
| |
| unsigned char *instr = lpBuff->desc.lpData + is; |
| LPD3DVERTEX vert = (LPD3DVERTEX) (lpBuff->desc.lpData + vs); |
| LPD3DVERTEX tvert = lpBuff->vertex_data; |
| OpenGL_IDirect3DDevice *odev = (OpenGL_IDirect3DDevice *) dev; |
| |
| TRACE(ddraw, "ExecuteData : \n"); |
| _dump_executedata(&(lpBuff->data)); |
| |
| while (1) { |
| LPD3DINSTRUCTION current = (LPD3DINSTRUCTION) instr; |
| BYTE size; |
| WORD count; |
| |
| count = current->wCount; |
| size = current->bSize; |
| instr += sizeof(D3DINSTRUCTION); |
| |
| switch (current->bOpcode) { |
| case D3DOP_POINT: { |
| TRACE(ddraw, "POINT-s (%d)\n", count); |
| |
| instr += count * size; |
| } break; |
| |
| case D3DOP_LINE: { |
| TRACE(ddraw, "LINE-s (%d)\n", count); |
| |
| instr += count * size; |
| } break; |
| |
| case D3DOP_TRIANGLE: { |
| int i; |
| TRACE(ddraw, "TRIANGLE (%d)\n", count); |
| |
| /* Use given matrixes */ |
| glMatrixMode(GL_MODELVIEW); |
| glLoadIdentity(); /* The model transformation was done during the |
| transformation phase */ |
| glMatrixMode(GL_PROJECTION); |
| glLoadMatrixf((float *) odev->proj_mat); |
| glMultMatrixf((float *) odev->view_mat); |
| |
| for (i = 0; i < count; i++) { |
| LPD3DTRIANGLE ci = (LPD3DTRIANGLE) instr; |
| |
| TRACE(ddraw, " v1: %d v2: %d v3: %d\n", |
| ci->v1.v1, ci->v2.v2, ci->v3.v3); |
| TRACE(ddraw, " Flags : "); |
| if (TRACE_ON(ddraw)) { |
| /* Wireframe */ |
| if (ci->wFlags & D3DTRIFLAG_EDGEENABLE1) |
| DUMP("EDGEENABLE1 "); |
| if (ci->wFlags & D3DTRIFLAG_EDGEENABLE2) |
| DUMP("EDGEENABLE2 "); |
| if (ci->wFlags & D3DTRIFLAG_EDGEENABLE1) |
| DUMP("EDGEENABLE3 "); |
| |
| /* Strips / Fans */ |
| if (ci->wFlags == D3DTRIFLAG_EVEN) |
| DUMP("EVEN "); |
| if (ci->wFlags == D3DTRIFLAG_ODD) |
| DUMP("ODD "); |
| if (ci->wFlags == D3DTRIFLAG_START) |
| DUMP("START "); |
| if ((ci->wFlags > 0) && (ci->wFlags < 30)) |
| DUMP("STARTFLAT(%d) ", ci->wFlags); |
| DUMP("\n"); |
| } |
| |
| glBegin(GL_TRIANGLES); { |
| DO_VERTEX(ci->v1.v1); |
| DO_VERTEX(ci->v2.v2); |
| DO_VERTEX(ci->v3.v3); |
| } glEnd(); |
| |
| instr += size; |
| } |
| } break; |
| |
| case D3DOP_MATRIXLOAD: { |
| TRACE(ddraw, "MATRIXLOAD-s (%d)\n", count); |
| |
| instr += count * size; |
| } break; |
| |
| case D3DOP_MATRIXMULTIPLY: { |
| int i; |
| TRACE(ddraw, "MATRIXMULTIPLY (%d)\n", count); |
| |
| for (i = 0; i < count; i++) { |
| LPD3DMATRIXMULTIPLY ci = (LPD3DMATRIXMULTIPLY) instr; |
| LPD3DMATRIX a = (LPD3DMATRIX) ci->hDestMatrix; |
| LPD3DMATRIX b = (LPD3DMATRIX) ci->hSrcMatrix1; |
| LPD3DMATRIX c = (LPD3DMATRIX) ci->hSrcMatrix2; |
| |
| TRACE(ddraw, " Dest : %08lx Src1 : %08lx Src2 : %08lx\n", |
| ci->hDestMatrix, ci->hSrcMatrix1, ci->hSrcMatrix2); |
| |
| /* Do the multiplication.. |
| As I am VERY lazy, I let OpenGL do the multiplication for me */ |
| glMatrixMode(GL_PROJECTION); |
| /* Save the current matrix */ |
| glPushMatrix(); |
| /* Load Matrix one and do the multiplication */ |
| glLoadMatrixf((float *) ci->hSrcMatrix1); |
| glMultMatrixf((float *) ci->hSrcMatrix2); |
| glGetFloatv(GL_PROJECTION_MATRIX, (float *) ci->hDestMatrix); |
| /* Restore the current matrix */ |
| glPopMatrix(); |
| |
| instr += size; |
| } |
| } break; |
| |
| case D3DOP_STATETRANSFORM: { |
| int i; |
| TRACE(ddraw, "STATETRANSFORM (%d)\n", count); |
| |
| for (i = 0; i < count; i++) { |
| LPD3DSTATE ci = (LPD3DSTATE) instr; |
| |
| /* Handle the state transform */ |
| switch (ci->t.dtstTransformStateType) { |
| case D3DTRANSFORMSTATE_WORLD: { |
| TRACE(ddraw, " WORLD (%p)\n", (D3DMATRIX*) ci->v.dwArg[0]); |
| odev->world_mat = (D3DMATRIX*) ci->v.dwArg[0]; |
| } break; |
| |
| case D3DTRANSFORMSTATE_VIEW: { |
| TRACE(ddraw, " VIEW (%p)\n", (D3DMATRIX*) ci->v.dwArg[0]); |
| odev->view_mat = (D3DMATRIX*) ci->v.dwArg[0]; |
| } break; |
| |
| case D3DTRANSFORMSTATE_PROJECTION: { |
| TRACE(ddraw, " PROJECTION (%p)\n", (D3DMATRIX*) ci->v.dwArg[0]); |
| odev->proj_mat = (D3DMATRIX*) ci->v.dwArg[0]; |
| } break; |
| |
| default: |
| ERR(ddraw, " Unhandled state transformation !! (%d)\n", (int) ci->t.dtstTransformStateType); |
| break; |
| |
| } |
| |
| instr += size; |
| } |
| } break; |
| |
| case D3DOP_STATELIGHT: { |
| int i; |
| TRACE(ddraw, "STATELIGHT (%d)\n", count); |
| |
| for (i = 0; i < count; i++) { |
| LPD3DSTATE ci = (LPD3DSTATE) instr; |
| |
| /* Handle the state transform */ |
| switch (ci->t.dlstLightStateType) { |
| case D3DLIGHTSTATE_MATERIAL: { |
| LPDIRECT3DMATERIAL mat = (LPDIRECT3DMATERIAL) ci->v.dwArg[0]; |
| TRACE(ddraw, " MATERIAL\n"); |
| |
| if (mat != NULL) { |
| mat->activate(mat); |
| } else { |
| TRACE(ddraw, " bad Material Handle\n"); |
| } |
| } break ; |
| |
| case D3DLIGHTSTATE_AMBIENT: { |
| float light[4]; |
| DWORD dwLightState = ci->v.dwArg[0]; |
| TRACE(ddraw, " AMBIENT\n"); |
| |
| light[0] = ((dwLightState >> 16) & 0xFF) / 255.0; |
| light[1] = ((dwLightState >> 8) & 0xFF) / 255.0; |
| light[2] = ((dwLightState >> 0) & 0xFF) / 255.0; |
| light[3] = 1.0; |
| glLightModelfv(GL_LIGHT_MODEL_AMBIENT, (float *) light); |
| |
| TRACE(ddraw, " R:%02lx G:%02lx B:%02lx A:%02lx\n", |
| ((dwLightState >> 16) & 0xFF), |
| ((dwLightState >> 8) & 0xFF), |
| ((dwLightState >> 0) & 0xFF), |
| ((dwLightState >> 24) & 0xFF)); |
| } break ; |
| |
| case D3DLIGHTSTATE_COLORMODEL: { |
| TRACE(ddraw, " COLORMODEL\n"); |
| } break ; |
| |
| case D3DLIGHTSTATE_FOGMODE: { |
| TRACE(ddraw, " FOGMODE\n"); |
| } break ; |
| |
| case D3DLIGHTSTATE_FOGSTART: { |
| TRACE(ddraw, " FOGSTART\n"); |
| } break ; |
| |
| case D3DLIGHTSTATE_FOGEND: { |
| TRACE(ddraw, " FOGEND\n"); |
| } break ; |
| |
| case D3DLIGHTSTATE_FOGDENSITY: { |
| TRACE(ddraw, " FOGDENSITY\n"); |
| } break ; |
| |
| default: |
| ERR(ddraw, " Unhandled light state !! (%d)\n", (int) ci->t.dlstLightStateType); |
| break; |
| } |
| instr += size; |
| } |
| } break; |
| |
| case D3DOP_STATERENDER: { |
| int i; |
| TRACE(ddraw, "STATERENDER (%d)\n", count); |
| |
| for (i = 0; i < count; i++) { |
| LPD3DSTATE ci = (LPD3DSTATE) instr; |
| |
| /* Handle the state transform */ |
| set_render_state(ci->t.drstRenderStateType, ci->v.dwArg[0]); |
| |
| instr += size; |
| } |
| } break; |
| |
| case D3DOP_PROCESSVERTICES: { |
| int i; |
| TRACE(ddraw, "PROCESSVERTICES (%d)\n", count); |
| |
| for (i = 0; i < count; i++) { |
| LPD3DPROCESSVERTICES ci = (LPD3DPROCESSVERTICES) instr; |
| |
| TRACE(ddraw, " Start : %d Dest : %d Count : %ld\n", |
| ci->wStart, ci->wDest, ci->dwCount); |
| TRACE(ddraw, " Flags : "); |
| if (TRACE_ON(ddraw)) { |
| if (ci->dwFlags & D3DPROCESSVERTICES_COPY) |
| DUMP("COPY "); |
| if (ci->dwFlags & D3DPROCESSVERTICES_NOCOLOR) |
| DUMP("NOCOLOR "); |
| if (ci->dwFlags == D3DPROCESSVERTICES_OPMASK) |
| DUMP("OPMASK "); |
| if (ci->dwFlags & D3DPROCESSVERTICES_TRANSFORM) |
| DUMP("TRANSFORM "); |
| if (ci->dwFlags == D3DPROCESSVERTICES_TRANSFORMLIGHT) |
| DUMP("TRANSFORMLIGHT "); |
| if (ci->dwFlags & D3DPROCESSVERTICES_UPDATEEXTENTS) |
| DUMP("UPDATEEXTENTS "); |
| DUMP("\n"); |
| } |
| |
| /* This is where doing Direct3D on top on OpenGL is quite difficult. |
| This method transforms a set of vertices using the CURRENT state |
| (lighting, projection, ...) but does not rasterize them. |
| They will oinly be put on screen later (with the POINT / LINE and |
| TRIANGLE op-codes). The problem is that you can have a triangle |
| with each point having been transformed using another state... |
| |
| In this implementation, I will emulate only ONE thing : each |
| vertex can have its own "WORLD" transformation (this is used in the |
| TWIST.EXE demo of the 5.2 SDK). I suppose that all vertices of the |
| execute buffer use the same state. |
| |
| If I find applications that change other states, I will try to do a |
| more 'fine-tuned' state emulation (but I may become quite tricky if |
| it changes a light position in the middle of a triangle). |
| |
| In this case, a 'direct' approach (i.e. without using OpenGL, but |
| writing our own 3D rasterizer) would be easier. */ |
| |
| /* The current method (with the hypothesis that only the WORLD matrix |
| will change between two points) is like this : |
| - I transform 'manually' all the vertices with the current WORLD |
| matrix and store them in the vertex buffer |
| - during the rasterization phase, the WORLD matrix will be set to |
| the Identity matrix */ |
| |
| /* Enough for the moment */ |
| if (ci->dwFlags == D3DPROCESSVERTICES_TRANSFORMLIGHT) { |
| /* Enable lighting, so that when the rasterization will take place, |
| the correct LIGHTING state is active. */ |
| glEnable(GL_LIGHTING); |
| |
| /* */ |
| } |
| |
| instr += size; |
| } |
| } break; |
| |
| case D3DOP_TEXTURELOAD: { |
| TRACE(ddraw, "TEXTURELOAD-s (%d)\n", count); |
| |
| instr += count * size; |
| } break; |
| |
| case D3DOP_EXIT: { |
| TRACE(ddraw, "EXIT (%d)\n", count); |
| /* We did this instruction */ |
| instr += size; |
| /* Exit this loop */ |
| goto end_of_buffer; |
| } break; |
| |
| case D3DOP_BRANCHFORWARD: { |
| TRACE(ddraw, "BRANCHFORWARD-s (%d)\n", count); |
| |
| instr += count * size; |
| } break; |
| |
| case D3DOP_SPAN: { |
| TRACE(ddraw, "SPAN-s (%d)\n", count); |
| |
| instr += count * size; |
| } break; |
| |
| case D3DOP_SETSTATUS: { |
| TRACE(ddraw, "SETSTATUS-s (%d)\n", count); |
| |
| instr += count * size; |
| } break; |
| |
| default: |
| ERR(ddraw, "Unhandled OpCode !!!\n"); |
| /* Try to save ... */ |
| instr += count * size; |
| break; |
| } |
| } |
| |
| end_of_buffer: |
| } |
| |
| /******************************************************************************* |
| * ExecuteBuffer Creation functions |
| */ |
| LPDIRECT3DEXECUTEBUFFER d3dexecutebuffer_create(LPDIRECT3DDEVICE d3ddev, LPD3DEXECUTEBUFFERDESC lpDesc) |
| { |
| LPDIRECT3DEXECUTEBUFFER eb; |
| |
| eb = HeapAlloc(GetProcessHeap(),HEAP_ZERO_MEMORY,sizeof(IDirect3DExecuteBuffer)); |
| eb->ref = 1; |
| eb->lpvtbl = &executebuffer_vtable; |
| eb->d3ddev = d3ddev; |
| |
| /* Initializes memory */ |
| eb->desc = *lpDesc; |
| |
| /* No buffer given */ |
| if (!(eb->desc.dwFlags & D3DDEB_LPDATA)) |
| eb->desc.lpData = NULL; |
| |
| /* No buffer size given */ |
| if (!(lpDesc->dwFlags & D3DDEB_BUFSIZE)) |
| eb->desc.dwBufferSize = 0; |
| |
| /* Create buffer if asked */ |
| if ((eb->desc.lpData == NULL) && (eb->desc.dwBufferSize > 0)) { |
| eb->need_free = TRUE; |
| eb->desc.lpData = HeapAlloc(GetProcessHeap(),HEAP_ZERO_MEMORY,eb->desc.dwBufferSize); |
| } else { |
| eb->need_free = FALSE; |
| } |
| |
| /* No vertices for the moment */ |
| eb->vertex_data = NULL; |
| |
| eb->desc.dwFlags |= D3DDEB_LPDATA; |
| |
| eb->execute = execute; |
| |
| return eb; |
| } |
| |
| /******************************************************************************* |
| * IDirect3DLight methods |
| */ |
| |
| static HRESULT WINAPI IDirect3DExecuteBuffer_QueryInterface(LPDIRECT3DEXECUTEBUFFER this, |
| REFIID riid, |
| LPVOID* ppvObj) |
| { |
| char xrefiid[50]; |
| |
| WINE_StringFromCLSID((LPCLSID)riid,xrefiid); |
| FIXME(ddraw, "(%p)->(%s,%p): stub\n", this, xrefiid,ppvObj); |
| |
| return S_OK; |
| } |
| |
| |
| |
| static ULONG WINAPI IDirect3DExecuteBuffer_AddRef(LPDIRECT3DEXECUTEBUFFER this) |
| { |
| TRACE(ddraw, "(%p)->()incrementing from %lu.\n", this, this->ref ); |
| |
| return ++(this->ref); |
| } |
| |
| |
| |
| static ULONG WINAPI IDirect3DExecuteBuffer_Release(LPDIRECT3DEXECUTEBUFFER this) |
| { |
| FIXME( ddraw, "(%p)->() decrementing from %lu.\n", this, this->ref ); |
| |
| if (!--(this->ref)) { |
| if ((this->desc.lpData != NULL) && this->need_free) |
| HeapFree(GetProcessHeap(),0,this->desc.lpData); |
| |
| if (this->vertex_data != NULL) |
| HeapFree(GetProcessHeap(),0,this->vertex_data); |
| |
| HeapFree(GetProcessHeap(),0,this); |
| return 0; |
| } |
| |
| return this->ref; |
| } |
| |
| static HRESULT WINAPI IDirect3DExecuteBuffer_Initialize(LPDIRECT3DEXECUTEBUFFER this, |
| LPDIRECT3DDEVICE lpDirect3DDevice, |
| LPD3DEXECUTEBUFFERDESC lpDesc) |
| { |
| FIXME(ddraw, "(%p)->(%p,%p): stub\n", this, lpDirect3DDevice, lpDesc); |
| |
| return DD_OK; |
| } |
| |
| static HRESULT WINAPI IDirect3DExecuteBuffer_Lock(LPDIRECT3DEXECUTEBUFFER this, |
| LPD3DEXECUTEBUFFERDESC lpDesc) |
| { |
| TRACE(ddraw, "(%p)->(%p)\n", this, lpDesc); |
| |
| /* Copies the buffer description */ |
| *lpDesc = this->desc; |
| |
| return DD_OK; |
| } |
| |
| static HRESULT WINAPI IDirect3DExecuteBuffer_Unlock(LPDIRECT3DEXECUTEBUFFER this) |
| { |
| TRACE(ddraw, "(%p)->()\n", this); |
| |
| return DD_OK; |
| } |
| |
| static HRESULT WINAPI IDirect3DExecuteBuffer_SetExecuteData(LPDIRECT3DEXECUTEBUFFER this, |
| LPD3DEXECUTEDATA lpData) |
| { |
| DWORD nbvert; |
| |
| TRACE(ddraw, "(%p)->(%p)\n", this, lpData); |
| |
| this->data = *lpData; |
| |
| /* Get the number of vertices in the execute buffer */ |
| nbvert = this->data.dwVertexCount; |
| |
| /* Prepares the transformed vertex buffer */ |
| if (this->vertex_data != NULL) |
| HeapFree(GetProcessHeap(), 0, this->vertex_data); |
| this->vertex_data = HeapAlloc(GetProcessHeap(),HEAP_ZERO_MEMORY,nbvert * sizeof(D3DVERTEX)); |
| |
| |
| if (TRACE_ON(ddraw)) { |
| _dump_executedata(lpData); |
| } |
| |
| return DD_OK; |
| } |
| |
| static HRESULT WINAPI IDirect3DExecuteBuffer_GetExecuteData(LPDIRECT3DEXECUTEBUFFER this, |
| LPD3DEXECUTEDATA lpData) |
| { |
| TRACE(ddraw, "(%p)->(%p): stub\n", this, lpData); |
| |
| *lpData = this->data; |
| |
| return DD_OK; |
| } |
| |
| static HRESULT WINAPI IDirect3DExecuteBuffer_Validate(LPDIRECT3DEXECUTEBUFFER this, |
| LPDWORD lpdwOffset, |
| LPD3DVALIDATECALLBACK lpFunc, |
| LPVOID lpUserArg, |
| DWORD dwReserved) |
| { |
| TRACE(ddraw, "(%p)->(%p,%p,%p,%lu)\n", this, lpdwOffset, lpFunc, lpUserArg, dwReserved); |
| |
| return DD_OK; |
| } |
| |
| static HRESULT WINAPI IDirect3DExecuteBuffer_Optimize(LPDIRECT3DEXECUTEBUFFER this, |
| DWORD dwReserved) |
| { |
| TRACE(ddraw, "(%p)->(%lu)\n", this, dwReserved); |
| |
| return DD_OK; |
| } |
| |
| |
| /******************************************************************************* |
| * IDirect3DLight VTable |
| */ |
| static IDirect3DExecuteBuffer_VTable executebuffer_vtable = { |
| /*** IUnknown methods ***/ |
| IDirect3DExecuteBuffer_QueryInterface, |
| IDirect3DExecuteBuffer_AddRef, |
| IDirect3DExecuteBuffer_Release, |
| /*** IDirect3DExecuteBuffer methods ***/ |
| IDirect3DExecuteBuffer_Initialize, |
| IDirect3DExecuteBuffer_Lock, |
| IDirect3DExecuteBuffer_Unlock, |
| IDirect3DExecuteBuffer_SetExecuteData, |
| IDirect3DExecuteBuffer_GetExecuteData, |
| IDirect3DExecuteBuffer_Validate, |
| IDirect3DExecuteBuffer_Optimize |
| }; |
| |
| #endif /* HAVE_MESAGL */ |