diff --git a/dlls/d3d8/tests/visual.c b/dlls/d3d8/tests/visual.c index afba0ffe0f..ee4656f5ad 100644 --- a/dlls/d3d8/tests/visual.c +++ b/dlls/d3d8/tests/visual.c @@ -19,6 +19,8 @@ /* See comment in dlls/d3d9/tests/visual.c for general guidelines */ +#include + #define COBJMACROS #include #include "wine/test.h" @@ -402,6 +404,302 @@ done: 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; + static const DWORD fvf = D3DFVF_XYZ | D3DFVF_NORMAL; + static const D3DMATRIX mat = + {{{ + 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, + }}}; + static const D3DLIGHT8 directional = + { + D3DLIGHT_DIRECTIONAL, + {0.0f, 0.0f, 0.0f, 0.0f}, + {1.0f, 1.0f, 1.0f, 0.0f}, + {0.0f, 0.0f, 0.0f, 0.0f}, + {0.0f, 0.0f, 0.0f}, + {0.0f, 0.0f, 1.0f}, + }, + point = + { + D3DLIGHT_POINT, + {0.0f, 0.0f, 0.0f, 0.0f}, + {1.0f, 1.0f, 1.0f, 0.0f}, + {0.0f, 0.0f, 0.0f, 0.0f}, + {0.0f, 0.0f, 0.0f}, + {0.0f, 0.0f, 0.0f}, + 100.0f, + 0.0f, + 0.0f, 0.0f, 1.0f, + }, + spot = + { + D3DLIGHT_SPOT, + {0.0f, 0.0f, 0.0f, 0.0f}, + {1.0f, 1.0f, 1.0f, 0.0f}, + {0.0f, 0.0f, 0.0f, 0.0f}, + {0.0f, 0.0f, 0.0f}, + {0.0f, 0.0f, 1.0f}, + 100.0f, + 1.0f, + 0.0f, 0.0f, 1.0f, + M_PI / 12.0f, M_PI / 3.0f + }, + /* The chosen range value makes the test fail when using a manhattan + * distance metric vs the correct euclidean distance. */ + point_range = + { + D3DLIGHT_POINT, + {0.0f, 0.0f, 0.0f, 0.0f}, + {1.0f, 1.0f, 1.0f, 0.0f}, + {0.0f, 0.0f, 0.0f, 0.0f}, + {0.0f, 0.0f, 0.0f}, + {0.0f, 0.0f, 0.0f}, + 1.2f, + 0.0f, + 0.0f, 0.0f, 1.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_directional_local[] = + { + {160, 120, 0x003c3c3c}, + {320, 120, 0x00717171}, + {480, 120, 0x003c3c3c}, + {160, 240, 0x00717171}, + {320, 240, 0x00ffffff}, + {480, 240, 0x00717171}, + {160, 360, 0x003c3c3c}, + {320, 360, 0x00717171}, + {480, 360, 0x003c3c3c}, + }, + expected_point[] = + { + {160, 120, 0x00282828}, + {320, 120, 0x005a5a5a}, + {480, 120, 0x00282828}, + {160, 240, 0x005a5a5a}, + {320, 240, 0x00ffffff}, + {480, 240, 0x005a5a5a}, + {160, 360, 0x00282828}, + {320, 360, 0x005a5a5a}, + {480, 360, 0x00282828}, + }, + expected_point_local[] = + { + {160, 120, 0x00000000}, + {320, 120, 0x00070707}, + {480, 120, 0x00000000}, + {160, 240, 0x00070707}, + {320, 240, 0x00ffffff}, + {480, 240, 0x00070707}, + {160, 360, 0x00000000}, + {320, 360, 0x00070707}, + {480, 360, 0x00000000}, + }, + expected_spot[] = + { + {160, 120, 0x00000000}, + {320, 120, 0x00141414}, + {480, 120, 0x00000000}, + {160, 240, 0x00141414}, + {320, 240, 0x00ffffff}, + {480, 240, 0x00141414}, + {160, 360, 0x00000000}, + {320, 360, 0x00141414}, + {480, 360, 0x00000000}, + }, + expected_spot_local[] = + { + {160, 120, 0x00000000}, + {320, 120, 0x00020202}, + {480, 120, 0x00000000}, + {160, 240, 0x00020202}, + {320, 240, 0x00ffffff}, + {480, 240, 0x00020202}, + {160, 360, 0x00000000}, + {320, 360, 0x00020202}, + {480, 360, 0x00000000}, + }, + expected_point_range[] = + { + {160, 120, 0x00000000}, + {320, 120, 0x005a5a5a}, + {480, 120, 0x00000000}, + {160, 240, 0x005a5a5a}, + {320, 240, 0x00ffffff}, + {480, 240, 0x005a5a5a}, + {160, 360, 0x00000000}, + {320, 360, 0x005a5a5a}, + {480, 360, 0x00000000}, + }; + static const struct + { + const D3DLIGHT8 *light; + BOOL local_viewer; + const struct expected_color *expected; + unsigned int expected_count; + } + tests[] = + { + {&directional, FALSE, expected_directional, + sizeof(expected_directional) / sizeof(expected_directional[0])}, + {&directional, TRUE, expected_directional_local, + sizeof(expected_directional_local) / sizeof(expected_directional_local[0])}, + {&point, FALSE, expected_point, + sizeof(expected_point) / sizeof(expected_point[0])}, + {&point, TRUE, expected_point_local, + sizeof(expected_point_local) / sizeof(expected_point_local[0])}, + {&spot, FALSE, expected_spot, + sizeof(expected_spot) / sizeof(expected_spot[0])}, + {&spot, TRUE, expected_spot_local, + sizeof(expected_spot_local) / sizeof(expected_spot_local[0])}, + {&point_range, FALSE, expected_point_range, + sizeof(expected_point_range) / sizeof(expected_point_range[0])}, + }; + IDirect3DDevice8 *device; + D3DMATERIAL8 material; + IDirect3D8 *d3d; + D3DCOLOR color; + ULONG refcount; + HWND window; + HRESULT hr; + unsigned int i, j, x, y; + struct + { + struct vec3 position; + struct vec3 normal; + } *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) + { + quad[i].position.x = x * 2.0f / (vertices_side - 1) - 1.0f; + quad[i].position.y = y * 2.0f / (vertices_side - 1) - 1.0f; + quad[i].position.z = 1.0f; + quad[i].normal.x = 0.0f; + quad[i].normal.y = 0.0f; + quad[i++].normal.z = -1.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", "d3d8_test", WS_OVERLAPPEDWINDOW | WS_VISIBLE, + 0, 0, 640, 480, NULL, NULL, NULL, NULL); + d3d = Direct3DCreate8(D3D_SDK_VERSION); + ok(!!d3d, "Failed to create a D3D object.\n"); + if (!(device = create_device(d3d, window, window, TRUE))) + { + skip("Failed to create a D3D device, skipping tests.\n"); + goto done; + } + + hr = IDirect3DDevice8_SetTransform(device, D3DTS_WORLD, &mat); + ok(SUCCEEDED(hr), "Failed to set world transform, hr %#x.\n", hr); + hr = IDirect3DDevice8_SetTransform(device, D3DTS_VIEW, &mat); + ok(SUCCEEDED(hr), "Failed to set view transform, hr %#x.\n", hr); + hr = IDirect3DDevice8_SetTransform(device, D3DTS_PROJECTION, &mat); + ok(SUCCEEDED(hr), "Failed to set projection transform, hr %#x.\n", hr); + hr = IDirect3DDevice8_SetRenderState(device, D3DRS_CLIPPING, FALSE); + ok(SUCCEEDED(hr), "Failed to disable clipping, hr %#x.\n", hr); + hr = IDirect3DDevice8_SetRenderState(device, D3DRS_ZENABLE, FALSE); + ok(SUCCEEDED(hr), "Failed to disable z test, hr %#x.\n", hr); + hr = IDirect3DDevice8_SetRenderState(device, D3DRS_FOGENABLE, FALSE); + ok(SUCCEEDED(hr), "Failed to disable fog, hr %#x.\n", hr); + + hr = IDirect3DDevice8_SetVertexShader(device, fvf); + ok(SUCCEEDED(hr), "Failed to set FVF, hr %#x.\n", hr); + + memset(&material, 0, sizeof(material)); + material.Specular.r = 1.0f; + material.Specular.g = 1.0f; + material.Specular.b = 1.0f; + material.Specular.a = 1.0f; + material.Power = 30.0f; + hr = IDirect3DDevice8_SetMaterial(device, &material); + ok(SUCCEEDED(hr), "Failed to set material, hr %#x.\n", hr); + + hr = IDirect3DDevice8_LightEnable(device, 0, TRUE); + ok(SUCCEEDED(hr), "Failed to enable light 0, hr %#x.\n", hr); + hr = IDirect3DDevice8_SetRenderState(device, D3DRS_SPECULARENABLE, TRUE); + ok(SUCCEEDED(hr), "Failed to enable specular lighting, hr %#x.\n", hr); + + for (i = 0; i < sizeof(tests) / sizeof(tests[0]); ++i) + { + hr = IDirect3DDevice8_SetLight(device, 0, tests[i].light); + ok(SUCCEEDED(hr), "Failed to set light parameters, hr %#x.\n", hr); + + hr = IDirect3DDevice8_SetRenderState(device, D3DRS_LOCALVIEWER, tests[i].local_viewer); + ok(SUCCEEDED(hr), "Failed to set local viewer state, hr %#x.\n", hr); + + hr = IDirect3DDevice8_Clear(device, 0, NULL, D3DCLEAR_TARGET, 0xffffffff, 0.0, 0); + ok(SUCCEEDED(hr), "Failed to clear, hr %#x.\n", hr); + + hr = IDirect3DDevice8_BeginScene(device); + ok(SUCCEEDED(hr), "Failed to begin scene, hr %#x.\n", hr); + + hr = IDirect3DDevice8_DrawIndexedPrimitiveUP(device, D3DPT_TRIANGLELIST, + 0, vertices_side * vertices_side, indices_count / 3, indices, + D3DFMT_INDEX16, quad, sizeof(quad[0])); + ok(SUCCEEDED(hr), "Failed to draw, hr %#x.\n", hr); + + hr = IDirect3DDevice8_EndScene(device); + ok(SUCCEEDED(hr), "Failed to end scene, hr %#x.\n", hr); + + for (j = 0; j < tests[i].expected_count; ++j) + { + color = getPixelColor(device, tests[i].expected[j].x, tests[i].expected[j].y); + ok(color_match(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); + } + } + + refcount = IDirect3DDevice8_Release(device); + ok(!refcount, "Device has %u references left.\n", refcount); +done: + IDirect3D8_Release(d3d); + DestroyWindow(window); + HeapFree(GetProcessHeap(), 0, indices); + HeapFree(GetProcessHeap(), 0, quad); +} + static void clear_test(void) { /* Tests the correctness of clearing parameters */ @@ -6075,6 +6373,7 @@ START_TEST(visual) test_sanity(); depth_clamp_test(); lighting_test(); + test_specular_lighting(); clear_test(); fog_test(); z_range_test();