ddraw: Implement IDirect3DViewport3::TransformVertices.

This commit is contained in:
Stefan Dösinger 2007-07-01 20:23:02 +02:00 committed by Alexandre Julliard
parent 125b9b7dcf
commit e4bf7ab578
2 changed files with 413 additions and 9 deletions

View File

@ -818,7 +818,14 @@ static void CapsTest(void)
IDirectDraw_Release(dd1);
}
static void ExecutebufferTest(void)
struct v_in {
float x, y, z;
};
struct v_out {
float x, y, z, rhw;
};
static void Direct3D1Test(void)
{
IDirect3DDevice *dev1 = NULL;
IDirectDraw *dd;
@ -833,6 +840,21 @@ static void ExecutebufferTest(void)
D3DINSTRUCTION *instr;
D3DBRANCH *branch;
unsigned int idx = 0;
static struct v_in testverts[] = {
{0.0, 0.0, 0.0}, { 1.0, 1.0, 1.0}, {-1.0, -1.0, -1.0},
{0.5, 0.5, 0.5}, {-0.5, -0.5, -0.5}, {-0.5, -0.5, 0.0},
};
static struct v_in cliptest[] = {
{25.59, 25.59, 1.0}, {-25.59, -25.59, 0.0},
{25.61, 25.61, 1.01}, {-25.60, -25.60, -0.01},
};
static struct v_in offscreentest[] = {
{128.1, 0.0, 0.0},
};
struct v_out out[sizeof(testverts) / sizeof(testverts[0])];
D3DHVERTEX outH[sizeof(testverts) / sizeof(testverts[0])];
D3DTRANSFORMDATA transformdata;
DWORD i = FALSE;
/* An IDirect3DDevice cannot be queryInterfaced from an IDirect3DDevice7 on windows */
hr = DirectDrawCreate(NULL, &dd, NULL);
@ -897,6 +919,7 @@ static void ExecutebufferTest(void)
ok(hr == D3D_OK, "IDirect3D_CreateViewport failed: %08x\n", hr);
hr = IDirect3DViewport_Initialize(vp, d3d);
ok(hr == DDERR_ALREADYINITIALIZED, "IDirect3DViewport_Initialize returned %08x\n", hr);
hr = IDirect3DDevice_AddViewport(dev1, vp);
ok(hr == D3D_OK, "IDirect3DDevice_AddViewport returned %08x\n", hr);
vp_data.dwSize = sizeof(vp_data);
@ -916,8 +939,262 @@ static void ExecutebufferTest(void)
hr = IDirect3DDevice_Execute(dev1, exebuf, vp, D3DEXECUTE_CLIPPED);
ok(hr == D3D_OK, "IDirect3DDevice_Execute returned %08x\n", hr);
memset(&transformdata, 0, sizeof(transformdata));
transformdata.dwSize = sizeof(transformdata);
transformdata.lpIn = (void *) testverts;
transformdata.dwInSize = sizeof(testverts[0]);
transformdata.lpOut = out;
transformdata.dwOutSize = sizeof(out[0]);
transformdata.lpHOut = NULL;
hr = IDirect3DViewport_TransformVertices(vp, sizeof(testverts) / sizeof(testverts[0]),
&transformdata, D3DTRANSFORM_UNCLIPPED,
&i);
ok(hr == D3D_OK, "IDirect3DViewport_TransformVertices returned %08x\n", hr);
transformdata.lpHOut = outH;
memset(outH, 0xaa, sizeof(outH));
hr = IDirect3DViewport_TransformVertices(vp, sizeof(testverts) / sizeof(testverts[0]),
&transformdata, D3DTRANSFORM_UNCLIPPED,
&i);
ok(hr == D3D_OK, "IDirect3DViewport_TransformVertices returned %08x\n", hr);
ok(i == 0, "Offscreen is %d\n", i);
for(i = 0; i < sizeof(testverts) / sizeof(testverts[0]); i++) {
static const struct v_out cmp[] = {
{128.0, 128.0, 0.0, 1}, {129.0, 127.0, 1.0, 1}, {127.0, 129.0, -1, 1},
{128.5, 127.5, 0.5, 1}, {127.5, 128.5, -0.5, 1}, {127.5, 128.5, 0, 1}
};
ok(cmp[i].x == out[i].x && cmp[i].y == out[i].y &&
cmp[i].z == out[i].z && cmp[i].rhw == out[i].rhw,
"Vertex %d differs. Got %f %f %f %f, expexted %f %f %f %f\n", i + 1,
out[i].x, out[i].y, out[i].z, out[i].rhw,
cmp[i].x, cmp[i].y, cmp[i].z, cmp[i].rhw);
}
for(i = 0; i < sizeof(outH); i++) {
if(((unsigned char *) outH)[i] != 0xaa) {
ok(FALSE, "Homogenous output was generated despite UNCLIPPED flag\n");
break;
}
}
vp_data.dvScaleX = 5;
vp_data.dvScaleY = 5;
vp_data.dvMinZ = -25;
vp_data.dvMaxZ = 60;
hr = IDirect3DViewport_SetViewport(vp, &vp_data);
ok(hr == D3D_OK, "IDirect3DViewport_SetViewport returned %08x\n", hr);
hr = IDirect3DViewport_TransformVertices(vp, sizeof(testverts) / sizeof(testverts[0]),
&transformdata, D3DTRANSFORM_UNCLIPPED,
&i);
ok(hr == D3D_OK, "IDirect3DViewport_TransformVertices returned %08x\n", hr);
ok(i == 0, "Offscreen is %d\n", i);
for(i = 0; i < sizeof(testverts) / sizeof(testverts[0]); i++) {
static const struct v_out cmp[] = {
{128.0, 128.0, 0.0, 1}, {133.0, 123.0, 1.0, 1}, {123.0, 133.0, -1, 1},
{130.5, 125.5, 0.5, 1}, {125.5, 130.5, -0.5, 1}, {125.5, 130.5, 0, 1}
};
ok(cmp[i].x == out[i].x && cmp[i].y == out[i].y &&
cmp[i].z == out[i].z && cmp[i].rhw == out[i].rhw,
"Vertex %d differs. Got %f %f %f %f, expexted %f %f %f %f\n", i + 1,
out[i].x, out[i].y, out[i].z, out[i].rhw,
cmp[i].x, cmp[i].y, cmp[i].z, cmp[i].rhw);
}
vp_data.dwX = 10;
vp_data.dwY = 20;
hr = IDirect3DViewport_SetViewport(vp, &vp_data);
ok(hr == D3D_OK, "IDirect3DViewport_SetViewport returned %08x\n", hr);
hr = IDirect3DViewport_TransformVertices(vp, sizeof(testverts) / sizeof(testverts[0]),
&transformdata, D3DTRANSFORM_UNCLIPPED,
&i);
ok(hr == D3D_OK, "IDirect3DViewport_TransformVertices returned %08x\n", hr);
ok(i == 0, "Offscreen is %d\n", i);
for(i = 0; i < sizeof(testverts) / sizeof(testverts[0]); i++) {
static const struct v_out cmp[] = {
{138.0, 148.0, 0.0, 1}, {143.0, 143.0, 1.0, 1}, {133.0, 153.0, -1, 1},
{140.5, 145.5, 0.5, 1}, {135.5, 150.5, -0.5, 1}, {135.5, 150.5, 0, 1}
};
ok(cmp[i].x == out[i].x && cmp[i].y == out[i].y &&
cmp[i].z == out[i].z && cmp[i].rhw == out[i].rhw,
"Vertex %d differs. Got %f %f %f %f, expexted %f %f %f %f\n", i + 1,
out[i].x, out[i].y, out[i].z, out[i].rhw,
cmp[i].x, cmp[i].y, cmp[i].z, cmp[i].rhw);
}
memset(out, 0xbb, sizeof(out));
hr = IDirect3DViewport_TransformVertices(vp, sizeof(testverts) / sizeof(testverts[0]),
&transformdata, D3DTRANSFORM_CLIPPED,
&i);
ok(hr == D3D_OK, "IDirect3DViewport_TransformVertices returned %08x\n", hr);
ok(i == 0, "Offscreen is %d\n", i);
for(i = 0; i < sizeof(testverts) / sizeof(testverts[0]); i++) {
static const D3DHVERTEX cmpH[] = {
{0, { 0.0}, { 0.0}, { 0.0}}, {0, { 1.0}, { 1.0}, {1.0}},
{D3DCLIP_FRONT, {-1.0}, {-1.0}, {-1.0}}, {0, { 0.5}, { 0.5}, {0.5}},
{D3DCLIP_FRONT, {-0.5}, {-0.5}, {-0.5}}, {0, {-0.5}, {-0.5}, {0.0}}
};
ok(cmpH[i].hx == outH[i].hx && cmpH[i].hy == outH[i].hy &&
cmpH[i].hz == outH[i].hz && cmpH[i].dwFlags == outH[i].dwFlags,
"HVertex %d differs. Got %08x %f %f %f, expexted %08x %f %f %f\n", i + 1,
outH[i].dwFlags, outH[i].hx, outH[i].hy, outH[i].hz,
cmpH[i].dwFlags, cmpH[i].hx, cmpH[i].hy, cmpH[i].hz);
/* No scheme has been found behind those return values. It seems to be
* whatever data windows has when throwing the vertex away. Modify the
* input test vertices to test this more. Depending on the input data
* it can happen that the z coord gets written into y, or simmilar things
*/
if(0)
{
static const struct v_out cmp[] = {
{138.0, 148.0, 0.0, 1}, {143.0, 143.0, 1.0, 1}, { -1.0, -1.0, 0.5, 1},
{140.5, 145.5, 0.5, 1}, { -0.5, -0.5, -0.5, 1}, {135.5, 150.5, 0.0, 1}
};
ok(cmp[i].x == out[i].x && cmp[i].y == out[i].y &&
cmp[i].z == out[i].z && cmp[i].rhw == out[i].rhw,
"Vertex %d differs. Got %f %f %f %f, expexted %f %f %f %f\n", i + 1,
out[i].x, out[i].y, out[i].z, out[i].rhw,
cmp[i].x, cmp[i].y, cmp[i].z, cmp[i].rhw);
}
}
for(i = 0; i < sizeof(out) / sizeof(DWORD); i++) {
ok(((DWORD *) out)[i] != 0xbbbbbbbb,
"Regular output DWORD %d remained untouched\n", i);
}
transformdata.lpIn = (void *) cliptest;
transformdata.dwInSize = sizeof(cliptest[0]);
hr = IDirect3DViewport_TransformVertices(vp, sizeof(cliptest) / sizeof(cliptest[0]),
&transformdata, D3DTRANSFORM_CLIPPED,
&i);
ok(hr == D3D_OK, "IDirect3DViewport_TransformVertices returned %08x\n", hr);
ok(i == 0, "Offscreen is %d\n", i);
for(i = 0; i < sizeof(cliptest) / sizeof(cliptest[0]); i++) {
DWORD Flags[sizeof(cliptest) / sizeof(cliptest[0])] =
{
0,
0,
D3DCLIP_RIGHT | D3DCLIP_BACK | D3DCLIP_TOP,
D3DCLIP_LEFT | D3DCLIP_BOTTOM | D3DCLIP_FRONT,
};
ok(Flags[i] == outH[i].dwFlags,
"Cliptest %d differs. Got %08x expexted %08x\n", i + 1,
outH[i].dwFlags, Flags[i]);
}
vp_data.dwWidth = 10;
vp_data.dwHeight = 1000;
hr = IDirect3DViewport_SetViewport(vp, &vp_data);
i = 10;
ok(hr == D3D_OK, "IDirect3DViewport_SetViewport returned %08x\n", hr);
hr = IDirect3DViewport_TransformVertices(vp, sizeof(cliptest) / sizeof(cliptest[0]),
&transformdata, D3DTRANSFORM_CLIPPED,
&i);
ok(hr == D3D_OK, "IDirect3DViewport_TransformVertices returned %08x\n", hr);
ok(i == 0, "Offscreen is %d\n", i);
for(i = 0; i < sizeof(cliptest) / sizeof(cliptest[0]); i++) {
DWORD Flags[sizeof(cliptest) / sizeof(cliptest[0])] =
{
D3DCLIP_RIGHT,
D3DCLIP_LEFT,
D3DCLIP_RIGHT | D3DCLIP_BACK,
D3DCLIP_LEFT | D3DCLIP_FRONT,
};
ok(Flags[i] == outH[i].dwFlags,
"Cliptest %d differs. Got %08x expexted %08x\n", i + 1,
outH[i].dwFlags, Flags[i]);
}
vp_data.dwWidth = 256;
vp_data.dwHeight = 256;
vp_data.dvScaleX = 1;
vp_data.dvScaleY = 1;
hr = IDirect3DViewport_SetViewport(vp, &vp_data);
ok(hr == D3D_OK, "IDirect3DViewport_SetViewport returned %08x\n", hr);
hr = IDirect3DViewport_TransformVertices(vp, sizeof(cliptest) / sizeof(cliptest[0]),
&transformdata, D3DTRANSFORM_CLIPPED,
&i);
ok(hr == D3D_OK, "IDirect3DViewport_TransformVertices returned %08x\n", hr);
ok(i == 0, "Offscreen is %s\n", i ? "TRUE" : "FALSE");
for(i = 0; i < sizeof(cliptest) / sizeof(cliptest[0]); i++) {
DWORD Flags[sizeof(cliptest) / sizeof(cliptest[0])] =
{
0,
0,
D3DCLIP_BACK,
D3DCLIP_FRONT,
};
ok(Flags[i] == outH[i].dwFlags,
"Cliptest %d differs. Got %08x expexted %08x\n", i + 1,
outH[i].dwFlags, Flags[i]);
}
/* Finally try to figure out how the DWORD dwOffscreen works.
* Apparently no vertex is offscreen with clipping off,
* and with clipping on the offscreen flag is set if only one vertex
* is transformed, and this vertex is offscreen.
*/
vp_data.dwWidth = 5;
vp_data.dwHeight = 5;
vp_data.dvScaleX = 10000;
vp_data.dvScaleY = 10000;
hr = IDirect3DViewport_SetViewport(vp, &vp_data);
ok(hr == D3D_OK, "IDirect3DViewport_SetViewport returned %08x\n", hr);
transformdata.lpIn = cliptest;
hr = IDirect3DViewport_TransformVertices(vp, 1,
&transformdata, D3DTRANSFORM_UNCLIPPED,
&i);
ok(hr == D3D_OK, "IDirect3DViewport_TransformVertices returned %08x\n", hr);
ok(i == 0, "Offscreen is %d\n", i);
hr = IDirect3DViewport_TransformVertices(vp, 1,
&transformdata, D3DTRANSFORM_CLIPPED,
&i);
ok(hr == D3D_OK, "IDirect3DViewport_TransformVertices returned %08x\n", hr);
ok(i == (D3DCLIP_RIGHT | D3DCLIP_TOP), "Offscreen is %d\n", i);
hr = IDirect3DViewport_TransformVertices(vp, 2,
&transformdata, D3DTRANSFORM_CLIPPED,
&i);
ok(hr == D3D_OK, "IDirect3DViewport_TransformVertices returned %08x\n", hr);
ok(i == 0, "Offscreen is %d\n", i);
transformdata.lpIn = cliptest + 1;
hr = IDirect3DViewport_TransformVertices(vp, 1,
&transformdata, D3DTRANSFORM_CLIPPED,
&i);
ok(hr == D3D_OK, "IDirect3DViewport_TransformVertices returned %08x\n", hr);
ok(i == (D3DCLIP_BOTTOM | D3DCLIP_LEFT), "Offscreen is %d\n", i);
transformdata.lpIn = (void *) offscreentest;
transformdata.dwInSize = sizeof(offscreentest[0]);
vp_data.dwWidth = 257;
vp_data.dwHeight = 257;
vp_data.dvScaleX = 1;
vp_data.dvScaleY = 1;
hr = IDirect3DViewport_SetViewport(vp, &vp_data);
i = 12345;
hr = IDirect3DViewport_TransformVertices(vp, sizeof(offscreentest) / sizeof(offscreentest[0]),
&transformdata, D3DTRANSFORM_CLIPPED,
&i);
ok(hr == D3D_OK, "IDirect3DViewport_TransformVertices returned %08x\n", hr);
ok(i == 0, "Offscreen is %d\n", i);
vp_data.dwWidth = 256;
vp_data.dwHeight = 256;
hr = IDirect3DViewport_SetViewport(vp, &vp_data);
i = 12345;
hr = IDirect3DViewport_TransformVertices(vp, sizeof(offscreentest) / sizeof(offscreentest[0]),
&transformdata, D3DTRANSFORM_CLIPPED,
&i);
ok(hr == D3D_OK, "IDirect3DViewport_TransformVertices returned %08x\n", hr);
ok(i == D3DCLIP_RIGHT, "Offscreen is %d\n", i);
hr = IDirect3DViewport_TransformVertices(vp, sizeof(testverts) / sizeof(testverts[0]),
&transformdata, 0,
&i);
ok(hr == DDERR_INVALIDPARAMS, "IDirect3DViewport_TransformVertices returned %08x\n", hr);
hr = IDirect3DDevice_DeleteViewport(dev1, vp);
ok(hr == D3D_OK, "IDirect3DDevice_AddViewport returned %08x\n", hr);
ok(hr == D3D_OK, "IDirect3DDevice_DeleteViewport returned %08x\n", hr);
IDirect3DViewport_Release(vp);
IDirect3DExecuteBuffer_Release(exebuf);
IDirect3DDevice_Release(dev1);
@ -946,5 +1223,5 @@ START_TEST(d3d)
LimitTest();
CapsTest();
ReleaseDirect3D();
ExecutebufferTest();
Direct3D1Test();
}

View File

@ -1,6 +1,6 @@
/* Direct3D Viewport
* Copyright (c) 1998 Lionel ULMER
* Copyright (c) 2006 Stefan DÖSINGER
* Copyright (c) 2006-2007 Stefan DÖSINGER
*
* This file contains the implementation of Direct3DViewport2.
*
@ -325,14 +325,26 @@ IDirect3DViewportImpl_SetViewport(IDirect3DViewport3 *iface,
*
* Transforms vertices by the transformation matrix.
*
* This function is pretty simmilar to IDirect3DVertexBuffer7::ProcessVertices,
* so its tempting to forward it to ProcessVertices. However, there are some
* tiny differences. First, the lpOffscreen flag that is reported back,
* then there is the homogenous vertex that is generated. Also there's a lack
* of FVFs, but still a custom stride. Last, the d3d1 - d3d3 viewport has some
* settings(scale) that d3d7 and wined3d do not have. All in all wrapping to
* ProcessVertices doesn't pay of in terms of wrapper code needed and code
* reused.
*
* Params:
* dwVertexCount: The number of vertices to be transformed
* lpData: Pointer to the vertex data
* dwFlags: D3DTRANSFORM_CLIPPED or D3DTRANSFORM_UNCLIPPED
* lpOffScreen: Is set to nonzero if all vertices are off-screen
* lpOffScreen: Set to the clipping plane clipping the vertex, if only one
* vertex is transformed and clipping is on. 0 otherwise
*
* Returns:
* D3D_OK because it's a stub
* D3D_OK on success
* D3DERR_VIEWPORTHASNODEVICE if the viewport is not assigned to a device
* DDERR_INVALIDPARAMS if no clipping flag is specified
*
*****************************************************************************/
static HRESULT WINAPI
@ -343,9 +355,124 @@ IDirect3DViewportImpl_TransformVertices(IDirect3DViewport3 *iface,
DWORD *lpOffScreen)
{
ICOM_THIS_FROM(IDirect3DViewportImpl, IDirect3DViewport3, iface);
FIXME("(%p)->(%08x,%p,%08x,%p): stub!\n", This, dwVertexCount, lpData, dwFlags, lpOffScreen);
if (lpOffScreen)
*lpOffScreen = 0;
D3DMATRIX view_mat, world_mat, proj_mat, mat;
float *in;
float *out;
float x, y, z, w;
unsigned int i;
D3DVIEWPORT vp = This->viewports.vp1;
D3DHVERTEX *outH;
TRACE("(%p)->(%08x,%p,%08x,%p)\n", This, dwVertexCount, lpData, dwFlags, lpOffScreen);
/* Tests on windows show that Windows crashes when this occurs,
* so don't return the (intuitive) return value
if(!This->active_device)
{
WARN("No device active, returning D3DERR_VIEWPORTHASNODEVICE\n");
return D3DERR_VIEWPORTHASNODEVICE;
}
*/
if(!(dwFlags & (D3DTRANSFORM_UNCLIPPED | D3DTRANSFORM_CLIPPED)))
{
WARN("No clipping flag passed, returning DDERR_INVALIDPARAMS\n");
return DDERR_INVALIDPARAMS;
}
EnterCriticalSection(&ddraw_cs);
IWineD3DDevice_GetTransform(This->active_device->wineD3DDevice,
D3DTRANSFORMSTATE_VIEW,
(WINED3DMATRIX*) &view_mat);
IWineD3DDevice_GetTransform(This->active_device->wineD3DDevice,
D3DTRANSFORMSTATE_PROJECTION,
(WINED3DMATRIX*) &proj_mat);
IWineD3DDevice_GetTransform(This->active_device->wineD3DDevice,
WINED3DTS_WORLDMATRIX(0),
(WINED3DMATRIX*) &world_mat);
multiply_matrix(&mat,&view_mat,&world_mat);
multiply_matrix(&mat,&proj_mat,&mat);
in = (float *) lpData->lpIn;
out = (float *) lpData->lpOut;
outH = lpData->lpHOut;
for(i = 0; i < dwVertexCount; i++)
{
x = (in[0] * mat._11) + (in[1] * mat._21) + (in[2] * mat._31) + (1.0 * mat._41);
y = (in[0] * mat._12) + (in[1] * mat._22) + (in[2] * mat._32) + (1.0 * mat._42);
z = (in[0] * mat._13) + (in[1] * mat._23) + (in[2] * mat._33) + (1.0 * mat._43);
w = (in[0] * mat._14) + (in[1] * mat._24) + (in[2] * mat._34) + (1.0 * mat._44);
if(dwFlags & D3DTRANSFORM_CLIPPED)
{
/* If clipping is enabled, Windows assumes that outH is
* a valid pointer
*/
outH[i].u1.hx = x; outH[i].u2.hy = y; outH[i].u3.hz = z;
outH[i].dwFlags = 0;
if(x * vp.dvScaleX > ((float) vp.dwWidth * 0.5))
outH[i].dwFlags |= D3DCLIP_RIGHT;
if(x * vp.dvScaleX <= -((float) vp.dwWidth) * 0.5)
outH[i].dwFlags |= D3DCLIP_LEFT;
if(y * vp.dvScaleY > ((float) vp.dwHeight * 0.5))
outH[i].dwFlags |= D3DCLIP_TOP;
if(y * vp.dvScaleY <= -((float) vp.dwHeight) * 0.5)
outH[i].dwFlags |= D3DCLIP_BOTTOM;
if(z < 0.0)
outH[i].dwFlags |= D3DCLIP_FRONT;
if(z > 1.0)
outH[i].dwFlags |= D3DCLIP_BACK;
if(outH[i].dwFlags)
{
/* Looks like native just drops the vertex, leaves whatever data
* it has in the output buffer and goes on with the next vertex.
* The exact scheme hasn't been figured out yet, but windows
* definitly writes something there.
*/
out[0] = x;
out[1] = y;
out[2] = z;
out[3] = w;
in = (float *) ((char *) in + lpData->dwInSize);
out = (float *) ((char *) out + lpData->dwOutSize);
continue;
}
}
w = 1 / w;
x *= w; y *= w; z *= w;
out[0] = vp.dwWidth / 2 + vp.dwX + x * vp.dvScaleX;
out[1] = vp.dwHeight / 2 + vp.dwY - y * vp.dvScaleY;
out[2] = z;
out[3] = w;
in = (float *) ((char *) in + lpData->dwInSize);
out = (float *) ((char *) out + lpData->dwOutSize);
}
/* According to the d3d test, the offscreen flag is set only
* if exactly one vertex is transformed. Its not documented,
* but the test shows that the lpOffscreen flag is set to the
* flag combination of clipping planes that clips the vertex.
*
* If clipping is requested, Windows assumes that the offscreen
* param is a valid pointer.
*/
if(dwVertexCount == 1 && dwFlags & D3DTRANSFORM_CLIPPED)
{
*lpOffScreen = outH[0].dwFlags;
}
else if(*lpOffScreen)
{
*lpOffScreen = 0;
}
LeaveCriticalSection(&ddraw_cs);
TRACE("All done\n");
return DD_OK;
}