From 26ebe396c31cbc29335b5d26a4f43821d805c1a6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Stefan=20D=C3=B6singer?= Date: Wed, 4 Jul 2007 17:57:45 +0200 Subject: [PATCH] wined3d: Implement high order patches. --- dlls/wined3d/device.c | 118 ++++++++-- dlls/wined3d/directx.c | 7 +- dlls/wined3d/drawprim.c | 384 +++++++++++++++++++++++++++++++++ dlls/wined3d/utils.c | 19 ++ dlls/wined3d/wined3d_private.h | 30 ++- 5 files changed, 542 insertions(+), 16 deletions(-) diff --git a/dlls/wined3d/device.c b/dlls/wined3d/device.c index 9d90df718f6..bcca5d79137 100644 --- a/dlls/wined3d/device.c +++ b/dlls/wined3d/device.c @@ -1930,6 +1930,16 @@ static HRESULT WINAPI IWineD3DDeviceImpl_Uninit3D(IWineD3DDevice *iface, D3DCB_D ActivateContext(This, This->lastActiveRenderTarget, CTXUSAGE_RESOURCELOAD); LEAVE_GL(); + TRACE("Deleting high order patches\n"); + for(i = 0; i < PATCHMAP_SIZE; i++) { + struct list *e1, *e2; + struct WineD3DRectPatch *patch; + LIST_FOR_EACH_SAFE(e1, e2, &This->patches[i]) { + patch = LIST_ENTRY(e1, struct WineD3DRectPatch, entry); + IWineD3DDevice_DeletePatch(iface, patch->Handle); + } + } + /* Delete the pbuffer context if there is any */ if(This->pbufferContext) DestroyContext(This, This->pbufferContext); @@ -5194,20 +5204,87 @@ static HRESULT WINAPI IWineD3DDeviceImpl_UpdateSurface(IWineD3DDevice *iface, return WINED3D_OK; } -/* Implementation details at http://developer.nvidia.com/attach/6494 -and -http://oss.sgi.com/projects/ogl-sample/registry/NV/evaluators.txt -hmm.. no longer supported use -OpenGL evaluators or tessellate surfaces within your application. -*/ - -/* http://msdn.microsoft.com/library/default.asp?url=/library/en-us/directx9_c/directx/graphics/reference/d3d/interfaces/idirect3ddevice9/DrawRectPatch.asp */ static HRESULT WINAPI IWineD3DDeviceImpl_DrawRectPatch(IWineD3DDevice *iface, UINT Handle, CONST float* pNumSegs, CONST WINED3DRECTPATCH_INFO* pRectPatchInfo) { IWineD3DDeviceImpl *This = (IWineD3DDeviceImpl *)iface; + struct WineD3DRectPatch *patch; + unsigned int i; + struct list *e; + BOOL found; TRACE("(%p) Handle(%d) noSegs(%p) rectpatch(%p)\n", This, Handle, pNumSegs, pRectPatchInfo); - FIXME("(%p) : Stub\n", This); - return WINED3D_OK; + if(!(Handle || pRectPatchInfo)) { + /* TODO: Write a test for the return value, thus the FIXME */ + FIXME("Both Handle and pRectPatchInfo are NULL\n"); + return WINED3DERR_INVALIDCALL; + } + + if(Handle) { + i = PATCHMAP_HASHFUNC(Handle); + found = FALSE; + LIST_FOR_EACH(e, &This->patches[i]) { + patch = LIST_ENTRY(e, struct WineD3DRectPatch, entry); + if(patch->Handle == Handle) { + found = TRUE; + break; + } + } + + if(!found) { + TRACE("Patch does not exist. Creating a new one\n"); + patch = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(*patch)); + patch->Handle = Handle; + list_add_head(&This->patches[i], &patch->entry); + } else { + TRACE("Found existing patch %p\n", patch); + } + } else { + /* Since opengl does not load tesselated vertex attributes into numbered vertex + * attributes we have to tesselate, read back, and draw. This needs a patch + * management structure instance. Create one. + * + * A possible improvement is to check if a vertex shader is used, and if not directly + * draw the patch. + */ + FIXME("Drawing an uncached patch. This is slow\n"); + patch = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(*patch)); + } + + if(pNumSegs[0] != patch->numSegs[0] || pNumSegs[1] != patch->numSegs[1] || + pNumSegs[2] != patch->numSegs[2] || pNumSegs[3] != patch->numSegs[3] || + (pRectPatchInfo && memcmp(pRectPatchInfo, &patch->RectPatchInfo, sizeof(*pRectPatchInfo)) != 0) ) { + HRESULT hr; + TRACE("Tesselation density or patch info changed, retesselating\n"); + + if(pRectPatchInfo) { + memcpy(&patch->RectPatchInfo, pRectPatchInfo, sizeof(*pRectPatchInfo)); + } + patch->numSegs[0] = pNumSegs[0]; + patch->numSegs[1] = pNumSegs[1]; + patch->numSegs[2] = pNumSegs[2]; + patch->numSegs[3] = pNumSegs[3]; + + hr = tesselate_rectpatch(This, patch); + if(FAILED(hr)) { + WARN("Patch tesselation failed\n"); + + /* Do not release the handle to store the params of the patch */ + if(!Handle) { + HeapFree(GetProcessHeap(), 0, patch); + } + return hr; + } + } + + This->currentPatch = patch; + IWineD3DDevice_DrawPrimitiveStrided(iface, WINED3DPT_TRIANGLELIST, patch->numSegs[0] * patch->numSegs[1] * 2, &patch->strided); + This->currentPatch = NULL; + + /* Destroy uncached patches */ + if(!Handle) { + HeapFree(GetProcessHeap(), 0, patch->mem); + HeapFree(GetProcessHeap(), 0, patch); + } + return WINED3D_OK; } /* http://msdn.microsoft.com/library/default.asp?url=/library/en-us/directx9_c/directx/graphics/reference/d3d/interfaces/idirect3ddevice9/DrawTriPatch.asp */ @@ -5220,9 +5297,26 @@ static HRESULT WINAPI IWineD3DDeviceImpl_DrawTriPatch(IWineD3DDevice *iface, UIN static HRESULT WINAPI IWineD3DDeviceImpl_DeletePatch(IWineD3DDevice *iface, UINT Handle) { IWineD3DDeviceImpl *This = (IWineD3DDeviceImpl *)iface; + int i; + struct WineD3DRectPatch *patch; + struct list *e; TRACE("(%p) Handle(%d)\n", This, Handle); - FIXME("(%p) : Stub\n", This); - return WINED3D_OK; + + i = PATCHMAP_HASHFUNC(Handle); + LIST_FOR_EACH(e, &This->patches[i]) { + patch = LIST_ENTRY(e, struct WineD3DRectPatch, entry); + if(patch->Handle == Handle) { + TRACE("Deleting patch %p\n", patch); + list_remove(&patch->entry); + HeapFree(GetProcessHeap(), 0, patch->mem); + HeapFree(GetProcessHeap(), 0, patch); + return WINED3D_OK; + } + } + + /* TODO: Write a test for the return value */ + FIXME("Attempt to destroy nonexistant patch\n"); + return WINED3DERR_INVALIDCALL; } static IWineD3DSwapChain *get_swapchain(IWineD3DSurface *target) { diff --git a/dlls/wined3d/directx.c b/dlls/wined3d/directx.c index 171d96950d8..ce2ca7027c1 100644 --- a/dlls/wined3d/directx.c +++ b/dlls/wined3d/directx.c @@ -1863,7 +1863,8 @@ static HRESULT WINAPI IWineD3DImpl_GetDeviceCaps(IWineD3D *iface, UINT Adapter, WINED3DDEVCAPS_TEXTURESYSTEMMEMORY | WINED3DDEVCAPS_CANRENDERAFTERFLIP | WINED3DDEVCAPS_DRAWPRIMITIVES2 | - WINED3DDEVCAPS_DRAWPRIMITIVES2EX; + WINED3DDEVCAPS_DRAWPRIMITIVES2EX | + WINED3DDEVCAPS_RTPATCHES; *pCaps->PrimitiveMiscCaps = WINED3DPMISCCAPS_CULLNONE | WINED3DPMISCCAPS_CULLCCW | @@ -2382,6 +2383,7 @@ static HRESULT WINAPI IWineD3DImpl_CreateDevice(IWineD3D *iface, UINT Adapter, IWineD3DImpl *This = (IWineD3DImpl *)iface; HDC hDC; HRESULT temp_result; + int i; /* Validate the adapter number */ if (Adapter >= IWineD3D_GetAdapterCount(iface)) { @@ -2472,6 +2474,9 @@ static HRESULT WINAPI IWineD3DImpl_CreateDevice(IWineD3D *iface, UINT Adapter, object->ddraw_format = pixelformat_for_depth(GetDeviceCaps(hDC, BITSPIXEL) * GetDeviceCaps(hDC, PLANES)); ReleaseDC(0, hDC); + for(i = 0; i < PATCHMAP_SIZE; i++) { + list_init(&object->patches[i]); + } return WINED3D_OK; create_device_error: diff --git a/dlls/wined3d/drawprim.c b/dlls/wined3d/drawprim.c index 10f952ea37f..1b03528db6a 100644 --- a/dlls/wined3d/drawprim.c +++ b/dlls/wined3d/drawprim.c @@ -1148,3 +1148,387 @@ if(stridedlcl.u.s.type.VBO) { \ /* Control goes back to the device, stateblock values may change again */ This->isInDraw = FALSE; } + +static void normalize_normal(float *n) { + float length = n[0] * n[0] + n[1] * n[1] + n[2] * n[2]; + if(length == 0.0) return; + length = sqrt(length); + n[0] = n[0] / length; + n[1] = n[1] / length; + n[2] = n[2] / length; +} + +/* Tesselates a high order rectangular patch into single triangles using gl evaluators + * + * The problem is that OpenGL does not offer a direct way to return the tesselated primitives, + * and they can't be sent off for rendering directly either. Tesselating is slow, so we want + * to chache the patches in a vertex buffer. But more importantly, gl can't bind generated + * attributes to numbered shader attributes, so we have to store them and rebind them as needed + * in drawprim. + * + * To read back, the opengl feedback mode is used. This creates a proplem because we want + * untransformed, unlit vertices, but feedback runs everything through transform and lighting. + * Thus disable lighting and set identity matrices to get unmodified colors and positions. + * To overcome clipping find the biggest x, y and z values of the vertices in the patch and scale + * them to [-1.0;+1.0] and set the viewport up to scale them back. + * + * Normals are more tricky: Draw white vertices with 3 directional lights, and calculate the + * resulting colors back to the normals. + * + * NOTE: This function activates a context for blitting, modifies matrices & viewport, but + * does not restore it because normally a draw follows immediately afterwards. The caller is + * responsible of taking care that either the gl states are restored, or the context activated + * for drawing to reset the lastWasBlit flag. + */ +HRESULT tesselate_rectpatch(IWineD3DDeviceImpl *This, + struct WineD3DRectPatch *patch) { + unsigned int i, j, num_quads, out_vertex_size, buffer_size, d3d_out_vertex_size; + float max_x = 0.0, max_y = 0.0, max_z = 0.0, neg_z = 0.0; + WineDirect3DVertexStridedData strided; + BYTE *data; + WINED3DRECTPATCH_INFO *info = &patch->RectPatchInfo; + DWORD vtxStride; + GLenum feedback_type; + GLfloat *feedbuffer; + + /* First, locate the position data. This is provided in a vertex buffer in the stateblock. + * Beware of vbos + */ + memset(&strided, 0, sizeof(strided)); + primitiveDeclarationConvertToStridedData((IWineD3DDevice *) This, FALSE, &strided, NULL); + if(strided.u.s.position.VBO) { + IWineD3DVertexBufferImpl *vb; + vb = (IWineD3DVertexBufferImpl *) This->stateBlock->streamSource[strided.u.s.position.streamNo]; + strided.u.s.position.lpData = (BYTE *) ((unsigned long) strided.u.s.position.lpData + + (unsigned long) vb->resource.allocatedMemory); + } + vtxStride = strided.u.s.position.dwStride; + data = strided.u.s.position.lpData + + vtxStride * info->Stride * info->StartVertexOffsetHeight + + vtxStride * info->StartVertexOffsetWidth; + + /* Not entirely sure about what happens with transformed vertices */ + if(strided.u.s.position_transformed) { + FIXME("Transformed position in rectpatch generation\n"); + } + if(vtxStride % sizeof(GLfloat)) { + /* glMap2f reads vertex sizes in GLfloats, the d3d stride is in bytes. + * I don't see how the stride could not be a multiple of 4, but make sure + * to check it + */ + ERR("Vertex stride is not a multiple of sizeof(GLfloat)\n"); + } + if(info->Basis != WINED3DBASIS_BEZIER) { + FIXME("Basis is %s, how to handle this?\n", debug_d3dbasis(info->Basis)); + } + if(info->Degree != WINED3DDEGREE_CUBIC) { + FIXME("Degree is %s, how to handle this?\n", debug_d3ddegree(info->Degree)); + } + + /* First, get the boundary cube of the input data */ + for(j = 0; j < info->Height; j++) { + for(i = 0; i < info->Width; i++) { + float *v = (float *) (data + vtxStride * i + vtxStride * info->Stride * j); + if(fabs(v[0]) > max_x) max_x = fabs(v[0]); + if(fabs(v[1]) > max_y) max_y = fabs(v[1]); + if(fabs(v[2]) > max_z) max_z = fabs(v[2]); + if(v[2] < neg_z) neg_z = v[2]; + } + } + + /* This needs some improvements in the vertex decl code */ + FIXME("Cannot find data to generate. Only generating position and normals\n"); + patch->has_normals = TRUE; + patch->has_texcoords = FALSE; + + ENTER_GL(); + /* Simply activate the context for blitting. This disables all the things we don't want and + * takes care for dirtifying. Dirtifying is prefered over pushing / popping, since drawing the + * patch(as opposed to normal draws) will most likely need different changes anyway + */ + ActivateContext(This, This->lastActiveRenderTarget, CTXUSAGE_BLIT); + + glMatrixMode(GL_PROJECTION); + checkGLcall("glMatrixMode(GL_PROJECTION)"); + glLoadIdentity(); + checkGLcall("glLoadIndentity()"); + glScalef(1 / (max_x) , 1 / (max_y), max_z == 0 ? 1 : 1 / ( 2 * max_z)); + glTranslatef(0, 0, 0.5); + checkGLcall("glScalef"); + glViewport(-max_x, -max_y, 2 * (max_x), 2 * (max_y)); + checkGLcall("glViewport"); + + /* Some states to take care of. If we're in wireframe opengl will produce lines, and confuse + * our feedback buffer parser + */ + glPolygonMode(GL_FRONT_AND_BACK, GL_FILL); + checkGLcall("glPolygonMode(GL_FRONT_AND_BACK, GL_FILL)"); + IWineD3DDeviceImpl_MarkStateDirty(This, STATE_RENDER(WINED3DRS_FILLMODE)); + if(patch->has_normals) { + float black[4] = {0, 0, 0, 0}; + float red[4] = {1, 0, 0, 0}; + float green[4] = {0, 1, 0, 0}; + float blue[4] = {0, 0, 1, 0}; + float white[4] = {1, 1, 1, 1}; + glEnable(GL_LIGHTING); + checkGLcall("glEnable(GL_LIGHTING)"); + glLightModelfv(GL_LIGHT_MODEL_AMBIENT, black); + checkGLcall("glLightModel for MODEL_AMBIENT"); + IWineD3DDeviceImpl_MarkStateDirty(This, STATE_RENDER(WINED3DRS_AMBIENT)); + + for(i = 3; i < GL_LIMITS(lights); i++) { + glDisable(GL_LIGHT0 + i); + checkGLcall("glDisable(GL_LIGHT0 + i)"); + IWineD3DDeviceImpl_MarkStateDirty(This, STATE_ACTIVELIGHT(i)); + } + + IWineD3DDeviceImpl_MarkStateDirty(This, STATE_ACTIVELIGHT(0)); + glLightfv(GL_LIGHT0, GL_DIFFUSE, red); + glLightfv(GL_LIGHT0, GL_SPECULAR, black); + glLightfv(GL_LIGHT0, GL_AMBIENT, black); + glLightfv(GL_LIGHT0, GL_POSITION, red); + glEnable(GL_LIGHT0); + checkGLcall("Setting up light 1\n"); + IWineD3DDeviceImpl_MarkStateDirty(This, STATE_ACTIVELIGHT(1)); + glLightfv(GL_LIGHT1, GL_DIFFUSE, green); + glLightfv(GL_LIGHT1, GL_SPECULAR, black); + glLightfv(GL_LIGHT1, GL_AMBIENT, black); + glLightfv(GL_LIGHT1, GL_POSITION, green); + glEnable(GL_LIGHT1); + checkGLcall("Setting up light 2\n"); + IWineD3DDeviceImpl_MarkStateDirty(This, STATE_ACTIVELIGHT(2)); + glLightfv(GL_LIGHT2, GL_DIFFUSE, blue); + glLightfv(GL_LIGHT2, GL_SPECULAR, black); + glLightfv(GL_LIGHT2, GL_AMBIENT, black); + glLightfv(GL_LIGHT2, GL_POSITION, blue); + glEnable(GL_LIGHT2); + checkGLcall("Setting up light 3\n"); + + IWineD3DDeviceImpl_MarkStateDirty(This, STATE_MATERIAL); + IWineD3DDeviceImpl_MarkStateDirty(This, STATE_RENDER(WINED3DRS_COLORVERTEX)); + glDisable(GL_COLOR_MATERIAL); + glMaterialfv(GL_FRONT_AND_BACK, GL_EMISSION, black); + glMaterialfv(GL_FRONT_AND_BACK, GL_SPECULAR, black); + glMaterialfv(GL_FRONT_AND_BACK, GL_DIFFUSE, white); + checkGLcall("Setting up materials\n"); + } + + /* Enable the needed maps. + * GL_MAP2_VERTEX_3 is needed for positional data. + * GL_AUTO_NORMAL to generate normals from the position. Do not use GL_MAP2_NORMAL. + * GL_MAP2_TEXTURE_COORD_4 for texture coords + */ + num_quads = ceilf(patch->numSegs[0]) * ceilf(patch->numSegs[1]); + out_vertex_size = 3 /* position */; + d3d_out_vertex_size = 3; + glEnable(GL_MAP2_VERTEX_3); + if(patch->has_normals && patch->has_texcoords) { + FIXME("Texcoords not handled yet\n"); + feedback_type = GL_3D_COLOR_TEXTURE; + out_vertex_size += 8; + d3d_out_vertex_size += 7; + glEnable(GL_AUTO_NORMAL); + glEnable(GL_MAP2_TEXTURE_COORD_4); + } else if(patch->has_texcoords) { + FIXME("Texcoords not handled yet\n"); + feedback_type = GL_3D_COLOR_TEXTURE; + out_vertex_size += 7; + d3d_out_vertex_size += 4; + glEnable(GL_MAP2_TEXTURE_COORD_4); + } else if(patch->has_normals) { + feedback_type = GL_3D_COLOR; + out_vertex_size += 4; + d3d_out_vertex_size += 3; + glEnable(GL_AUTO_NORMAL); + } else { + feedback_type = GL_3D; + } + checkGLcall("glEnable vertex attrib generation"); + + buffer_size = num_quads * out_vertex_size * 2 /* triangle list */ * 3 /* verts per tri */ + + 4 * num_quads /* 2 triangle markers per quad + num verts in tri */; + feedbuffer = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, buffer_size * sizeof(float) * 8); + + glMap2f(GL_MAP2_VERTEX_3, + 0, 1, vtxStride / sizeof(float), info->Width, + 0, 1, info->Stride * vtxStride / sizeof(float), info->Height, + (float *) data); + checkGLcall("glMap2f"); + if(patch->has_texcoords) { + glMap2f(GL_MAP2_TEXTURE_COORD_4, + 0, 1, vtxStride / sizeof(float), info->Width, + 0, 1, info->Stride * vtxStride / sizeof(float), info->Height, + (float *) data); + checkGLcall("glMap2f"); + } + glMapGrid2f(ceilf(patch->numSegs[0]), 0.0, 1.0, ceilf(patch->numSegs[1]), 0.0, 1.0); + checkGLcall("glMapGrid2f"); + + glFeedbackBuffer(buffer_size * 2, feedback_type, feedbuffer); + checkGLcall("glFeedbackBuffer"); + glRenderMode(GL_FEEDBACK); + + glEvalMesh2(GL_FILL, 0, ceilf(patch->numSegs[0]), 0, ceilf(patch->numSegs[1])); + checkGLcall("glEvalMesh2\n"); + + i = glRenderMode(GL_RENDER); + if(i == -1) { + ERR("Feedback failed. Expected %d elements back\n", buffer_size); + Sleep(10000); + HeapFree(GetProcessHeap(), 0, feedbuffer); + return WINED3DERR_DRIVERINTERNALERROR; + } else if(i != buffer_size) { + ERR("Unexpected amount of elements returned. Expected %d, got %d\n", buffer_size, i); + Sleep(10000); + HeapFree(GetProcessHeap(), 0, feedbuffer); + return WINED3DERR_DRIVERINTERNALERROR; + } else { + TRACE("Got %d elements as expected\n", i); + } + + HeapFree(GetProcessHeap(), 0, patch->mem); + patch->mem = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, num_quads * 6 * d3d_out_vertex_size * sizeof(float) * 8); + i = 0; + for(j = 0; j < buffer_size; j += (3 /* num verts */ * out_vertex_size + 2 /* tri marker */)) { + if(feedbuffer[j] != GL_POLYGON_TOKEN) { + ERR("Unexpected token: %f\n", feedbuffer[j]); + continue; + } + if(feedbuffer[j + 1] != 3) { + ERR("Unexpected polygon: %f corners\n", feedbuffer[j + 1]); + continue; + } + /* Somehow there are different ideas about back / front facing, so fix up the + * vertex order + */ + patch->mem[i + 0] = feedbuffer[j + out_vertex_size * 2 + 2]; /* x, triangle 2 */ + patch->mem[i + 1] = feedbuffer[j + out_vertex_size * 2 + 3]; /* y, triangle 2 */ + patch->mem[i + 2] = (feedbuffer[j + out_vertex_size * 2 + 4] - 0.5) * 4 * max_z; /* z, triangle 3 */ + if(patch->has_normals) { + patch->mem[i + 3] = feedbuffer[j + out_vertex_size * 2 + 5]; + patch->mem[i + 4] = feedbuffer[j + out_vertex_size * 2 + 6]; + patch->mem[i + 5] = feedbuffer[j + out_vertex_size * 2 + 7]; + } + i += d3d_out_vertex_size; + + patch->mem[i + 0] = feedbuffer[j + out_vertex_size * 1 + 2]; /* x, triangle 2 */ + patch->mem[i + 1] = feedbuffer[j + out_vertex_size * 1 + 3]; /* y, triangle 2 */ + patch->mem[i + 2] = (feedbuffer[j + out_vertex_size * 1 + 4] - 0.5) * 4 * max_z; /* z, triangle 2 */ + if(patch->has_normals) { + patch->mem[i + 3] = feedbuffer[j + out_vertex_size * 1 + 5]; + patch->mem[i + 4] = feedbuffer[j + out_vertex_size * 1 + 6]; + patch->mem[i + 5] = feedbuffer[j + out_vertex_size * 1 + 7]; + } + i += d3d_out_vertex_size; + + patch->mem[i + 0] = feedbuffer[j + out_vertex_size * 0 + 2]; /* x, triangle 1 */ + patch->mem[i + 1] = feedbuffer[j + out_vertex_size * 0 + 3]; /* y, triangle 1 */ + patch->mem[i + 2] = (feedbuffer[j + out_vertex_size * 0 + 4] - 0.5) * 4 * max_z; /* z, triangle 1 */ + if(patch->has_normals) { + patch->mem[i + 3] = feedbuffer[j + out_vertex_size * 0 + 5]; + patch->mem[i + 4] = feedbuffer[j + out_vertex_size * 0 + 6]; + patch->mem[i + 5] = feedbuffer[j + out_vertex_size * 0 + 7]; + } + i += d3d_out_vertex_size; + } + + if(patch->has_normals) { + /* Now do the same with reverse light directions */ + float x[4] = {-1, 0, 0, 0}; + float y[4] = { 0, -1, 0, 0}; + float z[4] = { 0, 0, -1, 0}; + glLightfv(GL_LIGHT0, GL_POSITION, x); + glLightfv(GL_LIGHT1, GL_POSITION, y); + glLightfv(GL_LIGHT2, GL_POSITION, z); + checkGLcall("Setting up reverse light directions\n"); + + glRenderMode(GL_FEEDBACK); + checkGLcall("glRenderMode(GL_FEEDBACK)"); + glEvalMesh2(GL_FILL, 0, ceilf(patch->numSegs[0]), 0, ceilf(patch->numSegs[1])); + checkGLcall("glEvalMesh2\n"); + i = glRenderMode(GL_RENDER); + checkGLcall("glRenderMode(GL_RENDER)"); + + i = 0; + for(j = 0; j < buffer_size; j += (3 /* num verts */ * out_vertex_size + 2 /* tri marker */)) { + if(feedbuffer[j] != GL_POLYGON_TOKEN) { + ERR("Unexpected token: %f\n", feedbuffer[j]); + continue; + } + if(feedbuffer[j + 1] != 3) { + ERR("Unexpected polygon: %f corners\n", feedbuffer[j + 1]); + continue; + } + if(patch->mem[i + 3] == 0.0) + patch->mem[i + 3] = -feedbuffer[j + out_vertex_size * 2 + 5]; + if(patch->mem[i + 4] == 0.0) + patch->mem[i + 4] = -feedbuffer[j + out_vertex_size * 2 + 6]; + if(patch->mem[i + 5] == 0.0) + patch->mem[i + 5] = -feedbuffer[j + out_vertex_size * 2 + 7]; + normalize_normal(patch->mem + i + 3); + i += d3d_out_vertex_size; + + if(patch->mem[i + 3] == 0.0) + patch->mem[i + 3] = -feedbuffer[j + out_vertex_size * 1 + 5]; + if(patch->mem[i + 4] == 0.0) + patch->mem[i + 4] = -feedbuffer[j + out_vertex_size * 1 + 6]; + if(patch->mem[i + 5] == 0.0) + patch->mem[i + 5] = -feedbuffer[j + out_vertex_size * 1 + 7]; + normalize_normal(patch->mem + i + 3); + i += d3d_out_vertex_size; + + if(patch->mem[i + 3] == 0.0) + patch->mem[i + 3] = -feedbuffer[j + out_vertex_size * 0 + 5]; + if(patch->mem[i + 4] == 0.0) + patch->mem[i + 4] = -feedbuffer[j + out_vertex_size * 0 + 6]; + if(patch->mem[i + 5] == 0.0) + patch->mem[i + 5] = -feedbuffer[j + out_vertex_size * 0 + 7]; + normalize_normal(patch->mem + i + 3); + i += d3d_out_vertex_size; + } + } + + glDisable(GL_MAP2_VERTEX_3); + glDisable(GL_AUTO_NORMAL); + glDisable(GL_MAP2_NORMAL); + glDisable(GL_MAP2_TEXTURE_COORD_4); + checkGLcall("glDisable vertex attrib generation"); + LEAVE_GL(); + + HeapFree(GetProcessHeap(), 0, feedbuffer); + + vtxStride = 3 * sizeof(float); + if(patch->has_normals) { + vtxStride += 3 * sizeof(float); + } + if(patch->has_texcoords) { + vtxStride += 4 * sizeof(float); + } + memset(&patch->strided, 0, sizeof(&patch->strided)); + patch->strided.u.s.position.lpData = (BYTE *) patch->mem; + patch->strided.u.s.position.dwStride = vtxStride; + patch->strided.u.s.position.dwType = WINED3DDECLTYPE_FLOAT3; + patch->strided.u.s.position.streamNo = 255; + + if(patch->has_normals) { + patch->strided.u.s.normal.lpData = (BYTE *) patch->mem + 3 * sizeof(float) /* pos */; + patch->strided.u.s.normal.dwStride = vtxStride; + patch->strided.u.s.normal.dwType = WINED3DDECLTYPE_FLOAT3; + patch->strided.u.s.normal.streamNo = 255; + } + if(patch->has_texcoords) { + patch->strided.u.s.texCoords[0].lpData = (BYTE *) patch->mem + 3 * sizeof(float) /* pos */; + if(patch->has_normals) { + patch->strided.u.s.texCoords[0].lpData += 3 * sizeof(float); + } + patch->strided.u.s.texCoords[0].dwStride = vtxStride; + patch->strided.u.s.texCoords[0].dwType = WINED3DDECLTYPE_FLOAT4; + /* MAX_STREAMS index points to an unused element in stateblock->streamOffsets which + * always remains set to 0. Windows uses stream 255 here, but this is not visible to the + * application. + */ + patch->strided.u.s.texCoords[0].streamNo = MAX_STREAMS; + } + + return WINED3D_OK; +} diff --git a/dlls/wined3d/utils.c b/dlls/wined3d/utils.c index 65bff5cc7b7..99835dca2e9 100644 --- a/dlls/wined3d/utils.c +++ b/dlls/wined3d/utils.c @@ -732,6 +732,25 @@ const char *debug_glerror(GLenum error) { } } +const char *debug_d3dbasis(WINED3DBASISTYPE basis) { + switch(basis) { + case WINED3DBASIS_BEZIER: return "WINED3DBASIS_BEZIER"; + case WINED3DBASIS_BSPLINE: return "WINED3DBASIS_BSPLINE"; + case WINED3DBASIS_INTERPOLATE: return "WINED3DBASIS_INTERPOLATE"; + default: return "unrecognized"; + } +} + +const char *debug_d3ddegree(WINED3DDEGREETYPE degree) { + switch(degree) { + case WINED3DDEGREE_LINEAR: return "WINED3DDEGREE_LINEAR"; + case WINED3DDEGREE_QUADRATIC: return "WINED3DDEGREE_QUADRATIC"; + case WINED3DDEGREE_CUBIC: return "WINED3DDEGREE_CUBIC"; + case WINED3DDEGREE_QUINTIC: return "WINED3DDEGREE_QUINTIC"; + default: return "unrecognized"; + } +} + /***************************************************************************** * Useful functions mapping GL <-> D3D values */ diff --git a/dlls/wined3d/wined3d_private.h b/dlls/wined3d/wined3d_private.h index 558773ccf98..86cad0b1d00 100644 --- a/dlls/wined3d/wined3d_private.h +++ b/dlls/wined3d/wined3d_private.h @@ -582,6 +582,22 @@ struct WineD3DAdapter extern BOOL InitAdapters(void); +/***************************************************************************** + * High order patch management + */ +struct WineD3DRectPatch +{ + UINT Handle; + float *mem; + WineDirect3DVertexStridedData strided; + WINED3DRECTPATCH_INFO RectPatchInfo; + float numSegs[4]; + char has_normals, has_texcoords; + struct list entry; +}; + +HRESULT tesselate_rectpatch(IWineD3DDeviceImpl *This, struct WineD3DRectPatch *patch); + /***************************************************************************** * IWineD3D implementation structure */ @@ -741,6 +757,12 @@ struct IWineD3DDeviceImpl UINT numContexts; WineD3DContext *pbufferContext; /* The context that has a pbuffer as drawable */ DWORD pbufferWidth, pbufferHeight; /* Size of the buffer drawable */ + + /* High level patch management */ +#define PATCHMAP_SIZE 43 +#define PATCHMAP_HASHFUNC(x) ((x) % PATCHMAP_SIZE) /* Primitive and simple function */ + struct list patches[PATCHMAP_SIZE]; + struct WineD3DRectPatch *currentPatch; }; extern const IWineD3DDeviceVtbl IWineD3DDevice_Vtbl; @@ -1271,10 +1293,10 @@ struct IWineD3DStateBlockImpl /* Stream Source */ BOOL streamIsUP; UINT streamStride[MAX_STREAMS]; - UINT streamOffset[MAX_STREAMS]; + UINT streamOffset[MAX_STREAMS + 1 /* tesselated pseudo-stream */ ]; IWineD3DVertexBuffer *streamSource[MAX_STREAMS]; - UINT streamFreq[MAX_STREAMS]; - UINT streamFlags[MAX_STREAMS]; /*0 | WINED3DSTREAMSOURCE_INSTANCEDATA | WINED3DSTREAMSOURCE_INDEXEDDATA */ + UINT streamFreq[MAX_STREAMS + 1]; + UINT streamFlags[MAX_STREAMS + 1]; /*0 | WINED3DSTREAMSOURCE_INSTANCEDATA | WINED3DSTREAMSOURCE_INDEXEDDATA */ /* Indices */ IWineD3DIndexBuffer* pIndexData; @@ -1436,6 +1458,8 @@ const char* debug_d3dtstype(WINED3DTRANSFORMSTATETYPE tstype); const char* debug_d3dpool(WINED3DPOOL pool); const char *debug_fbostatus(GLenum status); const char *debug_glerror(GLenum error); +const char *debug_d3dbasis(WINED3DBASISTYPE basis); +const char *debug_d3ddegree(WINED3DDEGREETYPE order); /* Routines for GL <-> D3D values */ GLenum StencilOp(DWORD op);