[PATCH 08/11] ddraw/tests: Backport test_specular_lighting() for ddraw1.
Matteo Bruni
matteo.mystral at gmail.com
Thu May 16 14:28:39 CDT 2019
Thanks for the test. I've had a similar one in my queue for a long
time but never got around to finishing it, mostly because I couldn't
understand the results I got.
On Wed, May 15, 2019 at 5:10 PM Paul Gofman <gofmanp at gmail.com> wrote:
> + /* Use of D3DLIGHT2 instead of D3DLIGHT is intentional. Using D3DLIGHT
> + without dwFlags looks broken on Windows 7: directional light behaves
> + as if _LOCALVIEWER state is on, specular lights do not work at all and
> + always output zero colors. */
Oh, interesting. BTW, did you actually mean "specular lights" here or
something else?
FWIW it looks like I used D3DLIGHT and somehow got a fully white quad
out of the directional light instead. Not sure where I tested it but
it was most likely Windows 7 Nvidia. Still, it might explain at least
some of the weird results I got.
Actually, let me attach my patch. In particular, see the large comment
block in there. I don't know how much of that is actually true and how
that's affected by D3DLIGHT vs D3DLIGHT2.
-------------- next part --------------
commit 3d75939fef5f032cc68527960b561e61fd13a14f
Author: Matteo Bruni <mbruni at codeweavers.com>
Date: Mon Apr 20 15:33:48 2015 +0200
ddraw/tests: Add a ddraw1 specular lighting test.
diff --git a/dlls/ddraw/tests/ddraw1.c b/dlls/ddraw/tests/ddraw1.c
index 7dda6eba201..b98ea7f9ae0 100644
--- a/dlls/ddraw/tests/ddraw1.c
+++ b/dlls/ddraw/tests/ddraw1.c
@@ -17,6 +17,9 @@
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
*/
+
+#include <math.h>
+
#define COBJMACROS
#include "wine/test.h"
@@ -415,6 +418,28 @@ static void emit_tquad_tlist(void **ptr, WORD base_idx)
*ptr = tri;
}
+static void emit_tlist(void **ptr, WORD *indices, unsigned int count)
+{
+ D3DINSTRUCTION *inst = *ptr;
+ D3DTRIANGLE *tri = (D3DTRIANGLE *)(inst + 1);
+ unsigned int i;
+
+ inst->bOpcode = D3DOP_TRIANGLE;
+ inst->bSize = sizeof(*tri);
+ inst->wCount = count / 3;
+
+ for (i = 0; i < count / 3; ++i)
+ {
+ U1(*tri).v1 = indices[i * 3];
+ U2(*tri).v2 = indices[i * 3 + 1];
+ U3(*tri).v3 = indices[i * 3 + 2];
+ tri->wFlags = D3DTRIFLAG_START;
+ ++tri;
+ }
+
+ *ptr = tri;
+}
+
static void emit_texture_load(void **ptr, D3DTEXTUREHANDLE dst_texture,
D3DTEXTUREHANDLE src_texture)
{
@@ -665,6 +690,22 @@ static IDirect3DMaterial *create_diffuse_and_ambient_material(IDirect3DDevice *d
return create_material(device, &mat);
}
+static IDirect3DMaterial *create_specular_material(IDirect3DDevice *device,
+ float r, float g, float b, float a, float power)
+{
+ D3DMATERIAL mat;
+
+ memset(&mat, 0, sizeof(mat));
+ mat.dwSize = sizeof(mat);
+ U1(U2(mat).specular).r = r;
+ U2(U2(mat).specular).g = g;
+ U3(U2(mat).specular).b = b;
+ U4(U2(mat).specular).a = a;
+ U4(mat).power = power;
+
+ return create_material(device, &mat);
+}
+
static IDirect3DMaterial *create_emissive_material(IDirect3DDevice *device, float r, float g, float b, float a)
{
D3DMATERIAL mat;
@@ -6294,6 +6335,306 @@ static void test_lighting(void)
DestroyWindow(window);
}
+static void test_specular_lighting(void)
+{
+ static const unsigned int vertices_side = 5;
+ const unsigned int indices_count = (vertices_side - 1) * (vertices_side - 1) * 2 * 3;
+ const unsigned int vertices_data_size = vertices_side * vertices_side * sizeof(D3DVERTEX);
+ static D3DRECT clear_rect = {{0}, {0}, {640}, {480}};
+ static D3DLIGHT directional =
+ {
+ sizeof(D3DLIGHT),
+ D3DLIGHT_DIRECTIONAL,
+ {{1.0f}, {1.0f}, {1.0f}, {0.0f}},
+ {{0.0f}, {0.0f}, {0.0f}},
+ {{0.0f}, {0.0f}, {1.0f}},
+ },
+ /* For some reason point and spot lights don't do anything when they have
+ * position.z >= 0.0f (that's limited to specular, diffuse works just fine).
+ * My current hypothesis is that, for specular only, the light direction is
+ * computed as (0 - light position), which is then normalized and used with
+ * the vertex position to compute the (local viewer) halfvector. This
+ * doesn't exactly match the test results though.
+ *
+ * Actually in the D3D5 and D3D6 docs there is this sentence "Specular
+ * highlights are calculated as though every vertex in the object being lit
+ * were at the object's origin. This gives the expected results as long as
+ * the object is modeled around the origin and the distance from the light
+ * to the object is relatively large." We know this is false for D3D6 but
+ * check if this actually matches D3D3 and below...
+ *
+ * In other news, attenuation in D3D3 and below works like D3D7+ (higher
+ * attenuation coefficients = dimmer lights) and unlike D3D5 and 6. There
+ * might be more weirdness though. */
+ point =
+ {
+ sizeof(D3DLIGHT),
+ D3DLIGHT_POINT,
+ {{1.0f}, {1.0f}, {1.0f}, {1.0f}},
+ {{0.1f}, {0.0f}, {-0.1f}},
+ {{0.0f}, {0.0f}, {0.0f}},
+ 1e10f,
+ 0.0f,
+ 0.0f, 0.0f, 0.0f,
+ },
+ spot =
+ {
+ sizeof(D3DLIGHT),
+ D3DLIGHT_SPOT,
+ {{1.0f}, {1.0f}, {1.0f}, {0.0f}},
+ {{0.0f}, {0.0f}, {-0.1f}},
+ {{0.0f}, {0.0f}, {1.0f}},
+ 1000.0f,
+ 1.0f,
+ 0.0f, 0.0f, 1.0f,
+ M_PI / 12.0f, M_PI / 3.0f
+ },
+ parallelpoint =
+ {
+ sizeof(D3DLIGHT),
+ D3DLIGHT_PARALLELPOINT,
+ {{1.0f}, {1.0f}, {1.0f}, {0.0f}},
+ {{0.5f}, {0.0f}, {-1.0f}},
+ {{0.0f}, {0.0f}, {0.0f}},
+ };
+ static const struct expected_color
+ {
+ unsigned int x, y;
+ D3DCOLOR color;
+ }
+ expected_directional[] =
+ {
+ {160, 120, 0x00ffffff},
+ {320, 120, 0x00ffffff},
+ {480, 120, 0x00ffffff},
+ {160, 240, 0x00ffffff},
+ {320, 240, 0x00ffffff},
+ {480, 240, 0x00ffffff},
+ {160, 360, 0x00ffffff},
+ {320, 360, 0x00ffffff},
+ {480, 360, 0x00ffffff},
+ },
+ expected_point[] =
+ {
+ {160, 120, 0x00868686},
+ {320, 120, 0x00959595},
+ {480, 120, 0x008e8e8e},
+ {160, 240, 0x00909090},
+ {320, 240, 0x00a3a3a3},
+ {480, 240, 0x009a9a9a},
+ {160, 360, 0x00868686},
+ {320, 360, 0x00959595},
+ {480, 360, 0x008e8e8e},
+ },
+ expected_spot[] =
+ {
+ {160, 120, 0x00000000},
+ {320, 120, 0x00333333},
+ {480, 120, 0x00000000},
+ {160, 240, 0x00333333},
+ {320, 240, 0x00c0c0c0},
+ {480, 240, 0x00333333},
+ {160, 360, 0x00000000},
+ {320, 360, 0x00333333},
+ {480, 360, 0x00000000},
+ },
+ expected_parallelpoint[] =
+ {
+ {160, 120, 0x00e4e4e4},
+ {320, 120, 0x00e4e4e4},
+ {480, 120, 0x00e4e4e4},
+ {160, 240, 0x00e4e4e4},
+ {320, 240, 0x00e4e4e4},
+ {480, 240, 0x00e4e4e4},
+ {160, 360, 0x00e4e4e4},
+ {320, 360, 0x00e4e4e4},
+ {480, 360, 0x00e4e4e4},
+ };
+ static const struct
+ {
+ D3DLIGHT *light;
+ const struct expected_color *expected;
+ unsigned int expected_count;
+ BOOL todo;
+ }
+ tests[] =
+ {
+ {&directional, expected_directional,
+ sizeof(expected_directional) / sizeof(expected_directional[0]), FALSE},
+ {&point, expected_point,
+ sizeof(expected_point) / sizeof(expected_point[0]), TRUE},
+ {&spot, expected_spot,
+ sizeof(expected_spot) / sizeof(expected_spot[0]), TRUE},
+ {¶llelpoint, expected_parallelpoint,
+ sizeof(expected_parallelpoint) / sizeof(expected_parallelpoint[0]), TRUE},
+ };
+ IDirect3D *d3d;
+ IDirect3DDevice *device;
+ IDirectDraw *ddraw;
+ IDirectDrawSurface *rt;
+ IDirect3DViewport *viewport;
+ IDirect3DMaterial *material, *background_material;
+ IDirect3DLight *light;
+ IDirect3DExecuteBuffer *execute_buffer;
+ D3DEXECUTEBUFFERDESC exec_desc;
+ D3DMATERIALHANDLE mat_handle;
+ D3DCOLOR color;
+ D3DDEVICEDESC device_desc, hel_desc;
+ void *ptr;
+ UINT inst_length;
+ ULONG refcount;
+ HWND window;
+ HRESULT hr;
+ unsigned int i, j, x, y;
+ D3DVERTEX *quad;
+ WORD *indices;
+
+ quad = HeapAlloc(GetProcessHeap(), 0, vertices_side * vertices_side * sizeof(*quad));
+ indices = HeapAlloc(GetProcessHeap(), 0, indices_count * sizeof(*indices));
+ for (i = 0, y = 0; y < vertices_side; ++y)
+ {
+ for (x = 0; x < vertices_side; ++x)
+ {
+ U1(quad[i]).x = x * 2.0f / (vertices_side - 1) - 1.0f;
+ U2(quad[i]).y = y * 2.0f / (vertices_side - 1) - 1.0f;
+ U3(quad[i]).z = 1.0f;
+ U4(quad[i]).nx = 0.0f;
+ U5(quad[i]).ny = 0.0f;
+ U6(quad[i]).nz = -1.0f;
+ U7(quad[i]).tu = 0.0f;
+ U8(quad[i++]).tv = 0.0f;
+ }
+ }
+ for (i = 0, y = 0; y < (vertices_side - 1); ++y)
+ {
+ for (x = 0; x < (vertices_side - 1); ++x)
+ {
+ indices[i++] = y * vertices_side + x + 1;
+ indices[i++] = y * vertices_side + x;
+ indices[i++] = (y + 1) * vertices_side + x;
+ indices[i++] = y * vertices_side + x + 1;
+ indices[i++] = (y + 1) * vertices_side + x;
+ indices[i++] = (y + 1) * vertices_side + x + 1;
+ }
+ }
+
+ window = CreateWindowA("static", "ddraw_test", WS_OVERLAPPEDWINDOW /* | WS_VISIBLE */,
+ 0, 0, 640, 480, 0, 0, 0, 0);
+ ddraw = create_ddraw();
+ ok(!!ddraw, "Failed to create a ddraw object.\n");
+ if (!(device = create_device(ddraw, window, DDSCL_NORMAL)))
+ {
+ skip("Failed to create a 3D device, skipping test.\n");
+ DestroyWindow(window);
+ return;
+ }
+
+ device_desc.dwSize = sizeof(device_desc);
+ hel_desc.dwSize = sizeof(hel_desc);
+ hr = IDirect3DDevice_GetCaps(device, &device_desc, &hel_desc);
+ ok(SUCCEEDED(hr), "Failed to get device caps.\n");
+
+ ok(!(device_desc.dlcLightingCaps.dwCaps & D3DLIGHTCAPS_GLSPOT), "GLSPOT lights supported.\n");
+
+ hr = IDirect3DDevice_GetDirect3D(device, &d3d);
+ ok(SUCCEEDED(hr), "Failed to get D3D interface, hr %#x.\n", hr);
+
+ hr = IDirect3DDevice_QueryInterface(device, &IID_IDirectDrawSurface, (void **)&rt);
+ ok(SUCCEEDED(hr), "Failed to get render target, hr %#x.\n", hr);
+
+ viewport = create_viewport(device, 0, 0, 640, 480);
+
+ background_material = create_diffuse_material(device, 1.0f, 1.0f, 1.0f, 1.0f);
+ viewport_set_background(device, viewport, background_material);
+
+ material = create_specular_material(device, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f);
+ hr = IDirect3DMaterial_GetHandle(material, device, &mat_handle);
+ ok(SUCCEEDED(hr), "Failed to get material handle, hr %#x.\n", hr);
+
+ hr = IDirect3D_CreateLight(d3d, &light, NULL);
+ ok(SUCCEEDED(hr), "Failed to create a light object, hr %#x.\n", hr);
+ hr = IDirect3DViewport_AddLight(viewport, light);
+ ok(SUCCEEDED(hr), "Failed to add a light to the viewport, hr %#x.\n", hr);
+
+ memset(&exec_desc, 0, sizeof(exec_desc));
+ exec_desc.dwSize = sizeof(exec_desc);
+ exec_desc.dwFlags = D3DDEB_BUFSIZE | D3DDEB_CAPS;
+ exec_desc.dwBufferSize = 2048;
+ exec_desc.dwCaps = D3DDEBCAPS_SYSTEMMEMORY;
+
+ hr = IDirect3DDevice_CreateExecuteBuffer(device, &exec_desc, &execute_buffer, NULL);
+ ok(SUCCEEDED(hr), "Failed to create execute buffer, hr %#x.\n", hr);
+
+ for (i = 0; i < sizeof(tests) / sizeof(tests[0]); ++i)
+ {
+ hr = IDirect3DLight_SetLight(light, tests[i].light);
+ ok(SUCCEEDED(hr), "Failed to set light, hr %#x.\n", hr);
+
+ hr = IDirect3DViewport_Clear(viewport, 1, &clear_rect, D3DCLEAR_TARGET);
+ ok(SUCCEEDED(hr), "Failed to clear viewport, hr %#x.\n", hr);
+
+ hr = IDirect3DExecuteBuffer_Lock(execute_buffer, &exec_desc);
+ ok(SUCCEEDED(hr), "Failed to lock execute buffer, hr %#x.\n", hr);
+
+ memcpy(exec_desc.lpData, quad, vertices_data_size);
+ ptr = ((BYTE *)exec_desc.lpData) + vertices_data_size;
+ emit_set_rs(&ptr, D3DRENDERSTATE_ZENABLE, FALSE);
+ emit_set_rs(&ptr, D3DRENDERSTATE_FOGENABLE, FALSE);
+ emit_set_rs(&ptr, D3DRENDERSTATE_SPECULARENABLE, TRUE);
+ emit_set_ls(&ptr, D3DLIGHTSTATE_MATERIAL, mat_handle);
+ emit_process_vertices(&ptr, D3DPROCESSVERTICES_TRANSFORMLIGHT, 0, vertices_side * vertices_side);
+ emit_tlist(&ptr, indices, indices_count);
+ emit_end(&ptr);
+ inst_length = (BYTE *)ptr - (BYTE *)exec_desc.lpData;
+ inst_length -= vertices_data_size;
+
+ hr = IDirect3DExecuteBuffer_Unlock(execute_buffer);
+ ok(SUCCEEDED(hr), "Failed to unlock execute buffer, hr %#x.\n", hr);
+
+ hr = IDirect3DDevice_BeginScene(device);
+ ok(SUCCEEDED(hr), "Failed to begin scene, hr %#x.\n", hr);
+
+ set_execute_data(execute_buffer, vertices_side * vertices_side, vertices_data_size, inst_length);
+ hr = IDirect3DDevice_Execute(device, execute_buffer, viewport, D3DEXECUTE_CLIPPED);
+ ok(SUCCEEDED(hr), "Failed to execute exec buffer, hr %#x.\n", hr);
+
+ hr = IDirect3DDevice_EndScene(device);
+ ok(SUCCEEDED(hr), "Failed to end scene, hr %#x.\n", hr);
+
+ for (j = 0; j < tests[i].expected_count; ++j)
+ {
+ color = get_surface_color(rt, tests[i].expected[j].x, tests[i].expected[j].y);
+ if (tests[i].todo)
+ todo_wine ok(compare_color(color, tests[i].expected[j].color, 1),
+ "Expected color 0x%08x at location (%u, %u), got 0x%08x, case %u.\n",
+ tests[i].expected[j].color, tests[i].expected[j].x,
+ tests[i].expected[j].y, color, i);
+ else
+ ok(compare_color(color, tests[i].expected[j].color, 1),
+ "Expected color 0x%08x at location (%u, %u), got 0x%08x, case %u.\n",
+ tests[i].expected[j].color, tests[i].expected[j].x,
+ tests[i].expected[j].y, color, i);
+ }
+ }
+
+ IDirect3DExecuteBuffer_Release(execute_buffer);
+ hr = IDirect3DViewport_DeleteLight(viewport, light);
+ ok(SUCCEEDED(hr), "Failed to remove a light from the viewport, hr %#x.\n", hr);
+ IDirect3DLight_Release(light);
+ destroy_material(material);
+ destroy_material(background_material);
+ destroy_viewport(device, viewport);
+ IDirectDrawSurface_Release(rt);
+ refcount = IDirect3DDevice_Release(device);
+ ok(!refcount, "Device has %u references left.\n", refcount);
+ IDirect3D_Release(d3d);
+ refcount = IDirectDraw_Release(ddraw);
+ ok(!refcount, "Ddraw object has %u references left.\n", refcount);
+ DestroyWindow(window);
+ HeapFree(GetProcessHeap(), 0, indices);
+ HeapFree(GetProcessHeap(), 0, quad);
+}
+
static void test_palette_gdi(void)
{
IDirectDrawSurface *surface, *primary;
@@ -12552,6 +12893,7 @@ START_TEST(ddraw1)
test_p8_blit();
test_material();
test_lighting();
+ test_specular_lighting();
test_palette_gdi();
test_palette_alpha();
test_lost_device();
More information about the wine-devel
mailing list