d3d10core: Fixup HRESULT in a bunch of error cases.
[wine] / dlls / d3dx9_36 / tests / mesh.c
1 /*
2  * Copyright 2008 David Adam
3  * Copyright 2008 Luis Busquets
4  * Copyright 2009 Henri Verbeet for CodeWeavers
5  * Copyright 2011 Michael Mc Donnell
6  *
7  * This library is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU Lesser General Public
9  * License as published by the Free Software Foundation; either
10  * version 2.1 of the License, or (at your option) any later version.
11  *
12  * This library is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15  * Lesser General Public License for more details.
16  *
17  * You should have received a copy of the GNU Lesser General Public
18  * License along with this library; if not, write to the Free Software
19  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
20  */
21
22 #define COBJMACROS
23 #include <stdio.h>
24 #include <float.h>
25 #include "wine/test.h"
26 #include "d3dx9.h"
27
28 /* Set the WINETEST_DEBUG environment variable to be greater than 1 for verbose
29  * function call traces of ID3DXAllocateHierarchy callbacks. */
30 #define TRACECALLBACK if(winetest_debug > 1) trace
31
32 #define admitted_error 0.0001f
33
34 #define ARRAY_SIZE(array) (sizeof(array)/sizeof(*array))
35
36 #define compare_vertex_sizes(type, exp) \
37     got=D3DXGetFVFVertexSize(type); \
38     ok(got==exp, "Expected: %d, Got: %d\n", exp, got);
39
40 #define compare_float(got, exp) \
41     do { \
42         float _got = (got); \
43         float _exp = (exp); \
44         ok(_got == _exp, "Expected: %g, Got: %g\n", _exp, _got); \
45     } while (0)
46
47 static BOOL compare(FLOAT u, FLOAT v)
48 {
49     return (fabs(u-v) < admitted_error);
50 }
51
52 static BOOL compare_vec3(D3DXVECTOR3 u, D3DXVECTOR3 v)
53 {
54     return ( compare(u.x, v.x) && compare(u.y, v.y) && compare(u.z, v.z) );
55 }
56
57 #define check_floats(got, exp, dim) check_floats_(__LINE__, "", got, exp, dim)
58 static void check_floats_(int line, const char *prefix, const float *got, const float *exp, int dim)
59 {
60     int i;
61     char exp_buffer[256] = "";
62     char got_buffer[256] = "";
63     char *exp_buffer_ptr = exp_buffer;
64     char *got_buffer_ptr = got_buffer;
65     BOOL equal = TRUE;
66
67     for (i = 0; i < dim; i++) {
68         if (i) {
69             exp_buffer_ptr += sprintf(exp_buffer_ptr, ", ");
70             got_buffer_ptr += sprintf(got_buffer_ptr, ", ");
71         }
72         equal = equal && compare(*exp, *got);
73         exp_buffer_ptr += sprintf(exp_buffer_ptr, "%g", *exp);
74         got_buffer_ptr += sprintf(got_buffer_ptr, "%g", *got);
75         exp++, got++;
76     }
77     ok_(__FILE__,line)(equal, "%sExpected (%s), got (%s)", prefix, exp_buffer, got_buffer);
78 }
79
80 struct vertex
81 {
82     D3DXVECTOR3 position;
83     D3DXVECTOR3 normal;
84 };
85
86 typedef WORD face[3];
87
88 static BOOL compare_face(face a, face b)
89 {
90     return (a[0]==b[0] && a[1] == b[1] && a[2] == b[2]);
91 }
92
93 struct test_context
94 {
95     HWND hwnd;
96     IDirect3D9 *d3d;
97     IDirect3DDevice9 *device;
98 };
99
100 /* Initializes a test context struct. Use it to initialize DirectX.
101  *
102  * Returns NULL if an error occurred.
103  */
104 static struct test_context *new_test_context(void)
105 {
106     HRESULT hr;
107     HWND hwnd = NULL;
108     IDirect3D9 *d3d = NULL;
109     IDirect3DDevice9 *device = NULL;
110     D3DPRESENT_PARAMETERS d3dpp = {0};
111     struct test_context *test_context;
112
113     hwnd = CreateWindow("static", "d3dx9_test", 0, 0, 0, 0, 0, NULL, NULL, NULL, NULL);
114     if (!hwnd)
115     {
116         skip("Couldn't create application window\n");
117         goto error;
118     }
119
120     d3d = Direct3DCreate9(D3D_SDK_VERSION);
121     if (!d3d)
122     {
123         skip("Couldn't create IDirect3D9 object\n");
124         goto error;
125     }
126
127     memset(&d3dpp, 0, sizeof(d3dpp));
128     d3dpp.Windowed = TRUE;
129     d3dpp.SwapEffect = D3DSWAPEFFECT_DISCARD;
130     hr = IDirect3D9_CreateDevice(d3d, D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, hwnd,
131                                  D3DCREATE_MIXED_VERTEXPROCESSING, &d3dpp, &device);
132     if (FAILED(hr))
133     {
134         skip("Couldn't create IDirect3DDevice9 object %#x\n", hr);
135         goto error;
136     }
137
138     test_context = HeapAlloc(GetProcessHeap(), 0, sizeof(*test_context));
139     if (!test_context)
140     {
141         skip("Couldn't allocate memory for test_context\n");
142         goto error;
143     }
144     test_context->hwnd = hwnd;
145     test_context->d3d = d3d;
146     test_context->device = device;
147
148     return test_context;
149
150 error:
151     if (device)
152         IDirect3DDevice9_Release(device);
153
154     if (d3d)
155         IDirect3D9_Release(d3d);
156
157     if (hwnd)
158         DestroyWindow(hwnd);
159
160     return NULL;
161 }
162
163 static void free_test_context(struct test_context *test_context)
164 {
165     if (!test_context)
166         return;
167
168     if (test_context->device)
169         IDirect3DDevice9_Release(test_context->device);
170
171     if (test_context->d3d)
172         IDirect3D9_Release(test_context->d3d);
173
174     if (test_context->hwnd)
175         DestroyWindow(test_context->hwnd);
176
177     HeapFree(GetProcessHeap(), 0, test_context);
178 }
179
180 struct mesh
181 {
182     DWORD number_of_vertices;
183     struct vertex *vertices;
184
185     DWORD number_of_faces;
186     face *faces;
187
188     DWORD fvf;
189     UINT vertex_size;
190 };
191
192 static void free_mesh(struct mesh *mesh)
193 {
194     HeapFree(GetProcessHeap(), 0, mesh->faces);
195     HeapFree(GetProcessHeap(), 0, mesh->vertices);
196 }
197
198 static BOOL new_mesh(struct mesh *mesh, DWORD number_of_vertices, DWORD number_of_faces)
199 {
200     mesh->vertices = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, number_of_vertices * sizeof(*mesh->vertices));
201     if (!mesh->vertices)
202     {
203         return FALSE;
204     }
205     mesh->number_of_vertices = number_of_vertices;
206
207     mesh->faces = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, number_of_faces * sizeof(*mesh->faces));
208     if (!mesh->faces)
209     {
210         HeapFree(GetProcessHeap(), 0, mesh->vertices);
211         return FALSE;
212     }
213     mesh->number_of_faces = number_of_faces;
214
215     return TRUE;
216 }
217
218 static void compare_mesh(const char *name, ID3DXMesh *d3dxmesh, struct mesh *mesh)
219 {
220     HRESULT hr;
221     DWORD number_of_vertices, number_of_faces;
222     IDirect3DVertexBuffer9 *vertex_buffer;
223     IDirect3DIndexBuffer9 *index_buffer;
224     D3DVERTEXBUFFER_DESC vertex_buffer_description;
225     D3DINDEXBUFFER_DESC index_buffer_description;
226     struct vertex *vertices;
227     face *faces;
228     int expected, i;
229
230     number_of_vertices = d3dxmesh->lpVtbl->GetNumVertices(d3dxmesh);
231     ok(number_of_vertices == mesh->number_of_vertices, "Test %s, result %u, expected %d\n",
232        name, number_of_vertices, mesh->number_of_vertices);
233
234     number_of_faces = d3dxmesh->lpVtbl->GetNumFaces(d3dxmesh);
235     ok(number_of_faces == mesh->number_of_faces, "Test %s, result %u, expected %d\n",
236        name, number_of_faces, mesh->number_of_faces);
237
238     /* vertex buffer */
239     hr = d3dxmesh->lpVtbl->GetVertexBuffer(d3dxmesh, &vertex_buffer);
240     ok(hr == D3D_OK, "Test %s, result %x, expected 0 (D3D_OK)\n", name, hr);
241
242     if (hr != D3D_OK)
243     {
244         skip("Couldn't get vertex buffer\n");
245     }
246     else
247     {
248         hr = IDirect3DVertexBuffer9_GetDesc(vertex_buffer, &vertex_buffer_description);
249         ok(hr == D3D_OK, "Test %s, result %x, expected 0 (D3D_OK)\n", name, hr);
250
251         if (hr != D3D_OK)
252         {
253             skip("Couldn't get vertex buffer description\n");
254         }
255         else
256         {
257             ok(vertex_buffer_description.Format == D3DFMT_VERTEXDATA, "Test %s, result %x, expected %x (D3DFMT_VERTEXDATA)\n",
258                name, vertex_buffer_description.Format, D3DFMT_VERTEXDATA);
259             ok(vertex_buffer_description.Type == D3DRTYPE_VERTEXBUFFER, "Test %s, result %x, expected %x (D3DRTYPE_VERTEXBUFFER)\n",
260                name, vertex_buffer_description.Type, D3DRTYPE_VERTEXBUFFER);
261             ok(vertex_buffer_description.Usage == 0, "Test %s, result %x, expected %x\n", name, vertex_buffer_description.Usage, 0);
262             ok(vertex_buffer_description.Pool == D3DPOOL_MANAGED, "Test %s, result %x, expected %x (D3DPOOL_MANAGED)\n",
263                name, vertex_buffer_description.Pool, D3DPOOL_MANAGED);
264             ok(vertex_buffer_description.FVF == mesh->fvf, "Test %s, result %x, expected %x\n",
265                name, vertex_buffer_description.FVF, mesh->fvf);
266             if (mesh->fvf == 0)
267             {
268                 expected = number_of_vertices * mesh->vertex_size;
269             }
270             else
271             {
272                 expected = number_of_vertices * D3DXGetFVFVertexSize(mesh->fvf);
273             }
274             ok(vertex_buffer_description.Size == expected, "Test %s, result %x, expected %x\n",
275                name, vertex_buffer_description.Size, expected);
276         }
277
278         /* specify offset and size to avoid potential overruns */
279         hr = IDirect3DVertexBuffer9_Lock(vertex_buffer, 0, number_of_vertices * sizeof(D3DXVECTOR3) * 2,
280                                          (LPVOID *)&vertices, D3DLOCK_DISCARD);
281         ok(hr == D3D_OK, "Test %s, result %x, expected 0 (D3D_OK)\n", name, hr);
282
283         if (hr != D3D_OK)
284         {
285             skip("Couldn't lock vertex buffer\n");
286         }
287         else
288         {
289             for (i = 0; i < number_of_vertices; i++)
290             {
291                 ok(compare_vec3(vertices[i].position, mesh->vertices[i].position),
292                    "Test %s, vertex position %d, result (%g, %g, %g), expected (%g, %g, %g)\n", name, i,
293                    vertices[i].position.x, vertices[i].position.y, vertices[i].position.z,
294                    mesh->vertices[i].position.x, mesh->vertices[i].position.y, mesh->vertices[i].position.z);
295                 ok(compare_vec3(vertices[i].normal, mesh->vertices[i].normal),
296                    "Test %s, vertex normal %d, result (%g, %g, %g), expected (%g, %g, %g)\n", name, i,
297                    vertices[i].normal.x, vertices[i].normal.y, vertices[i].normal.z,
298                    mesh->vertices[i].normal.x, mesh->vertices[i].normal.y, mesh->vertices[i].normal.z);
299             }
300
301             IDirect3DVertexBuffer9_Unlock(vertex_buffer);
302         }
303
304         IDirect3DVertexBuffer9_Release(vertex_buffer);
305     }
306
307     /* index buffer */
308     hr = d3dxmesh->lpVtbl->GetIndexBuffer(d3dxmesh, &index_buffer);
309     ok(hr == D3D_OK, "Test %s, result %x, expected 0 (D3D_OK)\n", name, hr);
310
311     if (!index_buffer)
312     {
313         skip("Couldn't get index buffer\n");
314     }
315     else
316     {
317         hr = IDirect3DIndexBuffer9_GetDesc(index_buffer, &index_buffer_description);
318         ok(hr == D3D_OK, "Test %s, result %x, expected 0 (D3D_OK)\n", name, hr);
319
320         if (hr != D3D_OK)
321         {
322             skip("Couldn't get index buffer description\n");
323         }
324         else
325         {
326             ok(index_buffer_description.Format == D3DFMT_INDEX16, "Test %s, result %x, expected %x (D3DFMT_INDEX16)\n",
327                name, index_buffer_description.Format, D3DFMT_INDEX16);
328             ok(index_buffer_description.Type == D3DRTYPE_INDEXBUFFER, "Test %s, result %x, expected %x (D3DRTYPE_INDEXBUFFER)\n",
329                name, index_buffer_description.Type, D3DRTYPE_INDEXBUFFER);
330             todo_wine ok(index_buffer_description.Usage == 0, "Test %s, result %x, expected %x\n", name, index_buffer_description.Usage, 0);
331             ok(index_buffer_description.Pool == D3DPOOL_MANAGED, "Test %s, result %x, expected %x (D3DPOOL_MANAGED)\n",
332                name, index_buffer_description.Pool, D3DPOOL_MANAGED);
333             expected = number_of_faces * sizeof(WORD) * 3;
334             ok(index_buffer_description.Size == expected, "Test %s, result %x, expected %x\n",
335                name, index_buffer_description.Size, expected);
336         }
337
338         /* specify offset and size to avoid potential overruns */
339         hr = IDirect3DIndexBuffer9_Lock(index_buffer, 0, number_of_faces * sizeof(WORD) * 3,
340                                         (LPVOID *)&faces, D3DLOCK_DISCARD);
341         ok(hr == D3D_OK, "Test %s, result %x, expected 0 (D3D_OK)\n", name, hr);
342
343         if (hr != D3D_OK)
344         {
345             skip("Couldn't lock index buffer\n");
346         }
347         else
348         {
349             for (i = 0; i < number_of_faces; i++)
350             {
351                 ok(compare_face(faces[i], mesh->faces[i]),
352                    "Test %s, face %d, result (%u, %u, %u), expected (%u, %u, %u)\n", name, i,
353                    faces[i][0], faces[i][1], faces[i][2],
354                    mesh->faces[i][0], mesh->faces[i][1], mesh->faces[i][2]);
355             }
356
357             IDirect3DIndexBuffer9_Unlock(index_buffer);
358         }
359
360         IDirect3DIndexBuffer9_Release(index_buffer);
361     }
362 }
363
364 static void D3DXBoundProbeTest(void)
365 {
366     BOOL result;
367     D3DXVECTOR3 bottom_point, center, top_point, raydirection, rayposition;
368     FLOAT radius;
369
370 /*____________Test the Box case___________________________*/
371     bottom_point.x = -3.0f; bottom_point.y = -2.0f; bottom_point.z = -1.0f;
372     top_point.x = 7.0f; top_point.y = 8.0f; top_point.z = 9.0f;
373
374     raydirection.x = -4.0f; raydirection.y = -5.0f; raydirection.z = -6.0f;
375     rayposition.x = 5.0f; rayposition.y = 5.0f; rayposition.z = 11.0f;
376     result = D3DXBoxBoundProbe(&bottom_point, &top_point, &rayposition, &raydirection);
377     ok(result == TRUE, "expected TRUE, received FALSE\n");
378
379     raydirection.x = 4.0f; raydirection.y = 5.0f; raydirection.z = 6.0f;
380     rayposition.x = 5.0f; rayposition.y = 5.0f; rayposition.z = 11.0f;
381     result = D3DXBoxBoundProbe(&bottom_point, &top_point, &rayposition, &raydirection);
382     ok(result == FALSE, "expected FALSE, received TRUE\n");
383
384     rayposition.x = -4.0f; rayposition.y = 1.0f; rayposition.z = -2.0f;
385     result = D3DXBoxBoundProbe(&bottom_point, &top_point, &rayposition, &raydirection);
386     ok(result == TRUE, "expected TRUE, received FALSE\n");
387
388     bottom_point.x = 1.0f; bottom_point.y = 0.0f; bottom_point.z = 0.0f;
389     top_point.x = 1.0f; top_point.y = 0.0f; top_point.z = 0.0f;
390     rayposition.x = 0.0f; rayposition.y = 1.0f; rayposition.z = 0.0f;
391     raydirection.x = 0.0f; raydirection.y = 3.0f; raydirection.z = 0.0f;
392     result = D3DXBoxBoundProbe(&bottom_point, &top_point, &rayposition, &raydirection);
393     ok(result == FALSE, "expected FALSE, received TRUE\n");
394
395     bottom_point.x = 1.0f; bottom_point.y = 2.0f; bottom_point.z = 3.0f;
396     top_point.x = 10.0f; top_point.y = 15.0f; top_point.z = 20.0f;
397
398     raydirection.x = 7.0f; raydirection.y = 8.0f; raydirection.z = 9.0f;
399     rayposition.x = 3.0f; rayposition.y = 7.0f; rayposition.z = -6.0f;
400     result = D3DXBoxBoundProbe(&bottom_point, &top_point, &rayposition, &raydirection);
401     ok(result == TRUE, "expected TRUE, received FALSE\n");
402
403     bottom_point.x = 0.0f; bottom_point.y = 0.0f; bottom_point.z = 0.0f;
404     top_point.x = 1.0f; top_point.y = 1.0f; top_point.z = 1.0f;
405
406     raydirection.x = 0.0f; raydirection.y = 1.0f; raydirection.z = .0f;
407     rayposition.x = -3.0f; rayposition.y = 0.0f; rayposition.z = 0.0f;
408     result = D3DXBoxBoundProbe(&bottom_point, &top_point, &rayposition, &raydirection);
409     ok(result == FALSE, "expected FALSE, received TRUE\n");
410
411     raydirection.x = 1.0f; raydirection.y = 0.0f; raydirection.z = .0f;
412     rayposition.x = -3.0f; rayposition.y = 0.0f; rayposition.z = 0.0f;
413     result = D3DXBoxBoundProbe(&bottom_point, &top_point, &rayposition, &raydirection);
414     ok(result == TRUE, "expected TRUE, received FALSE\n");
415
416 /*____________Test the Sphere case________________________*/
417     radius = sqrt(77.0f);
418     center.x = 1.0f; center.y = 2.0f; center.z = 3.0f;
419     raydirection.x = 2.0f; raydirection.y = -4.0f; raydirection.z = 2.0f;
420
421     rayposition.x = 5.0f; rayposition.y = 5.0f; rayposition.z = 9.0f;
422     result = D3DXSphereBoundProbe(&center, radius, &rayposition, &raydirection);
423     ok(result == TRUE, "expected TRUE, received FALSE\n");
424
425     rayposition.x = 45.0f; rayposition.y = -75.0f; rayposition.z = 49.0f;
426     result = D3DXSphereBoundProbe(&center, radius, &rayposition, &raydirection);
427     ok(result == FALSE, "expected FALSE, received TRUE\n");
428
429     rayposition.x = 5.0f; rayposition.y = 11.0f; rayposition.z = 9.0f;
430     result = D3DXSphereBoundProbe(&center, radius, &rayposition, &raydirection);
431     ok(result == FALSE, "expected FALSE, received TRUE\n");
432 }
433
434 static void D3DXComputeBoundingBoxTest(void)
435 {
436     D3DXVECTOR3 exp_max, exp_min, got_max, got_min, vertex[5];
437     HRESULT hr;
438
439     vertex[0].x = 1.0f; vertex[0].y = 1.0f; vertex[0].z = 1.0f;
440     vertex[1].x = 1.0f; vertex[1].y = 1.0f; vertex[1].z = 1.0f;
441     vertex[2].x = 1.0f; vertex[2].y = 1.0f; vertex[2].z = 1.0f;
442     vertex[3].x = 1.0f; vertex[3].y = 1.0f; vertex[3].z = 1.0f;
443     vertex[4].x = 9.0f; vertex[4].y = 9.0f; vertex[4].z = 9.0f;
444
445     exp_min.x = 1.0f; exp_min.y = 1.0f; exp_min.z = 1.0f;
446     exp_max.x = 9.0f; exp_max.y = 9.0f; exp_max.z = 9.0f;
447
448     hr = D3DXComputeBoundingBox(&vertex[3],2,D3DXGetFVFVertexSize(D3DFVF_XYZ),&got_min,&got_max);
449
450     ok( hr == D3D_OK, "Expected D3D_OK, got %#x\n", hr);
451     ok( compare_vec3(exp_min,got_min), "Expected min: (%f, %f, %f), got: (%f, %f, %f)\n", exp_min.x,exp_min.y,exp_min.z,got_min.x,got_min.y,got_min.z);
452     ok( compare_vec3(exp_max,got_max), "Expected max: (%f, %f, %f), got: (%f, %f, %f)\n", exp_max.x,exp_max.y,exp_max.z,got_max.x,got_max.y,got_max.z);
453
454 /*________________________*/
455
456     vertex[0].x = 2.0f; vertex[0].y = 5.9f; vertex[0].z = -1.2f;
457     vertex[1].x = -1.87f; vertex[1].y = 7.9f; vertex[1].z = 7.4f;
458     vertex[2].x = 7.43f; vertex[2].y = -0.9f; vertex[2].z = 11.9f;
459     vertex[3].x = -6.92f; vertex[3].y = 6.3f; vertex[3].z = -3.8f;
460     vertex[4].x = 11.4f; vertex[4].y = -8.1f; vertex[4].z = 4.5f;
461
462     exp_min.x = -6.92f; exp_min.y = -8.1f; exp_min.z = -3.80f;
463     exp_max.x = 11.4f; exp_max.y = 7.90f; exp_max.z = 11.9f;
464
465     hr = D3DXComputeBoundingBox(&vertex[0],5,D3DXGetFVFVertexSize(D3DFVF_XYZ),&got_min,&got_max);
466
467     ok( hr == D3D_OK, "Expected D3D_OK, got %#x\n", hr);
468     ok( compare_vec3(exp_min,got_min), "Expected min: (%f, %f, %f), got: (%f, %f, %f)\n", exp_min.x,exp_min.y,exp_min.z,got_min.x,got_min.y,got_min.z);
469     ok( compare_vec3(exp_max,got_max), "Expected max: (%f, %f, %f), got: (%f, %f, %f)\n", exp_max.x,exp_max.y,exp_max.z,got_max.x,got_max.y,got_max.z);
470
471 /*________________________*/
472
473     vertex[0].x = 2.0f; vertex[0].y = 5.9f; vertex[0].z = -1.2f;
474     vertex[1].x = -1.87f; vertex[1].y = 7.9f; vertex[1].z = 7.4f;
475     vertex[2].x = 7.43f; vertex[2].y = -0.9f; vertex[2].z = 11.9f;
476     vertex[3].x = -6.92f; vertex[3].y = 6.3f; vertex[3].z = -3.8f;
477     vertex[4].x = 11.4f; vertex[4].y = -8.1f; vertex[4].z = 4.5f;
478
479     exp_min.x = -6.92f; exp_min.y = -0.9f; exp_min.z = -3.8f;
480     exp_max.x = 7.43f; exp_max.y = 7.90f; exp_max.z = 11.9f;
481
482     hr = D3DXComputeBoundingBox(&vertex[0],4,D3DXGetFVFVertexSize(D3DFVF_XYZ),&got_min,&got_max);
483
484     ok( hr == D3D_OK, "Expected D3D_OK, got %#x\n", hr);
485     ok( compare_vec3(exp_min,got_min), "Expected min: (%f, %f, %f), got: (%f, %f, %f)\n", exp_min.x,exp_min.y,exp_min.z,got_min.x,got_min.y,got_min.z);
486     ok( compare_vec3(exp_max,got_max), "Expected max: (%f, %f, %f), got: (%f, %f, %f)\n", exp_max.x,exp_max.y,exp_max.z,got_max.x,got_max.y,got_max.z);
487
488 /*________________________*/
489     hr = D3DXComputeBoundingBox(NULL,5,D3DXGetFVFVertexSize(D3DFVF_XYZ),&got_min,&got_max);
490     ok( hr == D3DERR_INVALIDCALL, "Expected D3DERR_INVALIDCALL, got %#x\n", hr);
491
492 /*________________________*/
493     hr = D3DXComputeBoundingBox(&vertex[3],5,D3DXGetFVFVertexSize(D3DFVF_XYZ),NULL,&got_max);
494     ok( hr == D3DERR_INVALIDCALL, "Expected D3DERR_INVALIDCALL, got %#x\n", hr);
495
496 /*________________________*/
497     hr = D3DXComputeBoundingBox(&vertex[3],5,D3DXGetFVFVertexSize(D3DFVF_XYZ),&got_min,NULL);
498     ok( hr == D3DERR_INVALIDCALL, "Expected D3DERR_INVALIDCALL, got %#x\n", hr);
499 }
500
501 static void D3DXComputeBoundingSphereTest(void)
502 {
503     D3DXVECTOR3 exp_cen, got_cen, vertex[5];
504     FLOAT exp_rad, got_rad;
505     HRESULT hr;
506
507     vertex[0].x = 1.0f; vertex[0].y = 1.0f; vertex[0].z = 1.0f;
508     vertex[1].x = 1.0f; vertex[1].y = 1.0f; vertex[1].z = 1.0f;
509     vertex[2].x = 1.0f; vertex[2].y = 1.0f; vertex[2].z = 1.0f;
510     vertex[3].x = 1.0f; vertex[3].y = 1.0f; vertex[3].z = 1.0f;
511     vertex[4].x = 9.0f; vertex[4].y = 9.0f; vertex[4].z = 9.0f;
512
513     exp_rad = 6.928203f;
514     exp_cen.x = 5.0; exp_cen.y = 5.0; exp_cen.z = 5.0;
515
516     hr = D3DXComputeBoundingSphere(&vertex[3],2,D3DXGetFVFVertexSize(D3DFVF_XYZ),&got_cen,&got_rad);
517
518     ok( hr == D3D_OK, "Expected D3D_OK, got %#x\n", hr);
519     ok( compare(exp_rad, got_rad), "Expected radius: %f, got radius: %f\n", exp_rad, got_rad);
520     ok( compare_vec3(exp_cen,got_cen), "Expected center: (%f, %f, %f), got center: (%f, %f, %f)\n", exp_cen.x,exp_cen.y,exp_cen.z,got_cen.x,got_cen.y,got_cen.z);
521
522 /*________________________*/
523
524     vertex[0].x = 2.0f; vertex[0].y = 5.9f; vertex[0].z = -1.2f;
525     vertex[1].x = -1.87f; vertex[1].y = 7.9f; vertex[1].z = 7.4f;
526     vertex[2].x = 7.43f; vertex[2].y = -0.9f; vertex[2].z = 11.9f;
527     vertex[3].x = -6.92f; vertex[3].y = 6.3f; vertex[3].z = -3.8f;
528     vertex[4].x = 11.4f; vertex[4].y = -8.1f; vertex[4].z = 4.5f;
529
530     exp_rad = 13.707883f;
531     exp_cen.x = 2.408f; exp_cen.y = 2.22f; exp_cen.z = 3.76f;
532
533     hr = D3DXComputeBoundingSphere(&vertex[0],5,D3DXGetFVFVertexSize(D3DFVF_XYZ),&got_cen,&got_rad);
534
535     ok( hr == D3D_OK, "Expected D3D_OK, got %#x\n", hr);
536     ok( compare(exp_rad, got_rad), "Expected radius: %f, got radius: %f\n", exp_rad, got_rad);
537     ok( compare_vec3(exp_cen,got_cen), "Expected center: (%f, %f, %f), got center: (%f, %f, %f)\n", exp_cen.x,exp_cen.y,exp_cen.z,got_cen.x,got_cen.y,got_cen.z);
538
539 /*________________________*/
540     hr = D3DXComputeBoundingSphere(NULL,5,D3DXGetFVFVertexSize(D3DFVF_XYZ),&got_cen,&got_rad);
541     ok( hr == D3DERR_INVALIDCALL, "Expected D3DERR_INVALIDCALL, got %#x\n", hr);
542
543 /*________________________*/
544     hr = D3DXComputeBoundingSphere(&vertex[3],5,D3DXGetFVFVertexSize(D3DFVF_XYZ),NULL,&got_rad);
545     ok( hr == D3DERR_INVALIDCALL, "Expected D3DERR_INVALIDCALL, got %#x\n", hr);
546
547 /*________________________*/
548     hr = D3DXComputeBoundingSphere(&vertex[3],5,D3DXGetFVFVertexSize(D3DFVF_XYZ),&got_cen,NULL);
549     ok( hr == D3DERR_INVALIDCALL, "Expected D3DERR_INVALIDCALL, got %#x\n", hr);
550 }
551
552 static void print_elements(const D3DVERTEXELEMENT9 *elements)
553 {
554     D3DVERTEXELEMENT9 last = D3DDECL_END();
555     const D3DVERTEXELEMENT9 *ptr = elements;
556     int count = 0;
557
558     while (memcmp(ptr, &last, sizeof(D3DVERTEXELEMENT9)))
559     {
560         trace(
561             "[Element %d] Stream = %d, Offset = %d, Type = %d, Method = %d, Usage = %d, UsageIndex = %d\n",
562              count, ptr->Stream, ptr->Offset, ptr->Type, ptr->Method, ptr->Usage, ptr->UsageIndex);
563         ptr++;
564         count++;
565     }
566 }
567
568 static void compare_elements(const D3DVERTEXELEMENT9 *elements, const D3DVERTEXELEMENT9 *expected_elements,
569         unsigned int line, unsigned int test_id)
570 {
571     D3DVERTEXELEMENT9 last = D3DDECL_END();
572     unsigned int i;
573
574     for (i = 0; i < MAX_FVF_DECL_SIZE; i++)
575     {
576         int end1 = memcmp(&elements[i], &last, sizeof(last));
577         int end2 = memcmp(&expected_elements[i], &last, sizeof(last));
578         int status;
579
580         if (!end1 && !end2) break;
581
582         status = !end1 ^ !end2;
583         ok(!status, "Line %u, test %u: Mismatch in size, test declaration is %s than expected.\n",
584                 line, test_id, end1 ? "shorter" : "longer");
585         if (status)
586         {
587             print_elements(elements);
588             break;
589         }
590
591         status = memcmp(&elements[i], &expected_elements[i], sizeof(D3DVERTEXELEMENT9));
592         ok(!status, "Line %u, test %u: Mismatch in element %u.\n", line, test_id, i);
593         if (status)
594         {
595             print_elements(elements);
596             break;
597         }
598     }
599 }
600
601 static void test_fvf_to_decl(DWORD test_fvf, const D3DVERTEXELEMENT9 expected_elements[],
602         HRESULT expected_hr, unsigned int line, unsigned int test_id)
603 {
604     HRESULT hr;
605     D3DVERTEXELEMENT9 decl[MAX_FVF_DECL_SIZE];
606
607     hr = D3DXDeclaratorFromFVF(test_fvf, decl);
608     ok(hr == expected_hr,
609             "Line %u, test %u: D3DXDeclaratorFromFVF returned %#x, expected %#x.\n",
610             line, test_id, hr, expected_hr);
611     if (SUCCEEDED(hr)) compare_elements(decl, expected_elements, line, test_id);
612 }
613
614 static void test_decl_to_fvf(const D3DVERTEXELEMENT9 *decl, DWORD expected_fvf,
615         HRESULT expected_hr, unsigned int line, unsigned int test_id)
616 {
617     HRESULT hr;
618     DWORD result_fvf = 0xdeadbeef;
619
620     hr = D3DXFVFFromDeclarator(decl, &result_fvf);
621     ok(hr == expected_hr,
622        "Line %u, test %u: D3DXFVFFromDeclarator returned %#x, expected %#x.\n",
623        line, test_id, hr, expected_hr);
624     if (SUCCEEDED(hr))
625     {
626         ok(expected_fvf == result_fvf, "Line %u, test %u: Got FVF %#x, expected %#x.\n",
627                 line, test_id, result_fvf, expected_fvf);
628     }
629 }
630
631 static void test_fvf_decl_conversion(void)
632 {
633     static const struct
634     {
635         D3DVERTEXELEMENT9 decl[MAXD3DDECLLENGTH + 1];
636         DWORD fvf;
637     }
638     test_data[] =
639     {
640         {{
641             D3DDECL_END(),
642         }, 0},
643         {{
644             {0, 0, D3DDECLTYPE_FLOAT3, 0, D3DDECLUSAGE_POSITION, 0},
645             D3DDECL_END(),
646         }, D3DFVF_XYZ},
647         {{
648             {0, 0, D3DDECLTYPE_FLOAT4, 0, D3DDECLUSAGE_POSITIONT, 0},
649             D3DDECL_END(),
650         }, D3DFVF_XYZRHW},
651         {{
652             {0, 0, D3DDECLTYPE_FLOAT4, 0, D3DDECLUSAGE_POSITIONT, 0},
653             D3DDECL_END(),
654         }, D3DFVF_XYZRHW},
655         {{
656             {0, 0, D3DDECLTYPE_FLOAT3, 0, D3DDECLUSAGE_POSITION, 0},
657             {0, 12, D3DDECLTYPE_FLOAT1, 0, D3DDECLUSAGE_BLENDWEIGHT, 0},
658             D3DDECL_END(),
659         }, D3DFVF_XYZB1},
660         {{
661             {0, 0, D3DDECLTYPE_FLOAT3, 0, D3DDECLUSAGE_POSITION, 0},
662             {0, 12, D3DDECLTYPE_UBYTE4, 0, D3DDECLUSAGE_BLENDINDICES, 0},
663             D3DDECL_END(),
664         }, D3DFVF_XYZB1 | D3DFVF_LASTBETA_UBYTE4},
665         {{
666             {0, 0, D3DDECLTYPE_FLOAT3, 0, D3DDECLUSAGE_POSITION, 0},
667             {0, 12, D3DDECLTYPE_D3DCOLOR, 0, D3DDECLUSAGE_BLENDINDICES, 0},
668             D3DDECL_END(),
669         }, D3DFVF_XYZB1 | D3DFVF_LASTBETA_D3DCOLOR},
670         {{
671             {0, 0, D3DDECLTYPE_FLOAT3, 0, D3DDECLUSAGE_POSITION, 0},
672             {0, 12, D3DDECLTYPE_FLOAT2, 0, D3DDECLUSAGE_BLENDWEIGHT, 0},
673             D3DDECL_END(),
674         }, D3DFVF_XYZB2},
675         {{
676             {0, 0, D3DDECLTYPE_FLOAT3, 0, D3DDECLUSAGE_POSITION, 0},
677             {0, 12, D3DDECLTYPE_FLOAT1, 0, D3DDECLUSAGE_BLENDWEIGHT, 0},
678             {0, 16, D3DDECLTYPE_UBYTE4, 0, D3DDECLUSAGE_BLENDINDICES, 0},
679             D3DDECL_END(),
680         }, D3DFVF_XYZB2 | D3DFVF_LASTBETA_UBYTE4},
681         {{
682             {0, 0, D3DDECLTYPE_FLOAT3, 0, D3DDECLUSAGE_POSITION, 0},
683             {0, 12, D3DDECLTYPE_FLOAT1, 0, D3DDECLUSAGE_BLENDWEIGHT, 0},
684             {0, 16, D3DDECLTYPE_D3DCOLOR, 0, D3DDECLUSAGE_BLENDINDICES, 0},
685             D3DDECL_END(),
686         }, D3DFVF_XYZB2 | D3DFVF_LASTBETA_D3DCOLOR},
687         {{
688             {0, 0, D3DDECLTYPE_FLOAT3, 0, D3DDECLUSAGE_POSITION, 0},
689             {0, 12, D3DDECLTYPE_FLOAT3, 0, D3DDECLUSAGE_BLENDWEIGHT, 0},
690             D3DDECL_END(),
691         }, D3DFVF_XYZB3},
692         {{
693             {0, 0, D3DDECLTYPE_FLOAT3, 0, D3DDECLUSAGE_POSITION, 0},
694             {0, 12, D3DDECLTYPE_FLOAT2, 0, D3DDECLUSAGE_BLENDWEIGHT, 0},
695             {0, 20, D3DDECLTYPE_UBYTE4, 0, D3DDECLUSAGE_BLENDINDICES, 0},
696             D3DDECL_END(),
697         }, D3DFVF_XYZB3 | D3DFVF_LASTBETA_UBYTE4},
698         {{
699             {0, 0, D3DDECLTYPE_FLOAT3, 0, D3DDECLUSAGE_POSITION, 0},
700             {0, 12, D3DDECLTYPE_FLOAT2, 0, D3DDECLUSAGE_BLENDWEIGHT, 0},
701             {0, 20, D3DDECLTYPE_D3DCOLOR, 0, D3DDECLUSAGE_BLENDINDICES, 0},
702             D3DDECL_END(),
703         }, D3DFVF_XYZB3 | D3DFVF_LASTBETA_D3DCOLOR},
704         {{
705             {0, 0, D3DDECLTYPE_FLOAT3, 0, D3DDECLUSAGE_POSITION, 0},
706             {0, 12, D3DDECLTYPE_FLOAT4, 0, D3DDECLUSAGE_BLENDWEIGHT, 0},
707             D3DDECL_END(),
708         }, D3DFVF_XYZB4},
709         {{
710             {0, 0, D3DDECLTYPE_FLOAT3, 0, D3DDECLUSAGE_POSITION, 0},
711             {0, 12, D3DDECLTYPE_FLOAT3, 0, D3DDECLUSAGE_BLENDWEIGHT, 0},
712             {0, 24, D3DDECLTYPE_UBYTE4, 0, D3DDECLUSAGE_BLENDINDICES, 0},
713             D3DDECL_END(),
714         }, D3DFVF_XYZB4 | D3DFVF_LASTBETA_UBYTE4},
715         {{
716             {0, 0, D3DDECLTYPE_FLOAT3, 0, D3DDECLUSAGE_POSITION, 0},
717             {0, 12, D3DDECLTYPE_FLOAT3, 0, D3DDECLUSAGE_BLENDWEIGHT, 0},
718             {0, 24, D3DDECLTYPE_D3DCOLOR, 0, D3DDECLUSAGE_BLENDINDICES, 0},
719             D3DDECL_END(),
720         }, D3DFVF_XYZB4 | D3DFVF_LASTBETA_D3DCOLOR},
721         {{
722             {0, 0, D3DDECLTYPE_FLOAT3, 0, D3DDECLUSAGE_POSITION, 0},
723             {0, 12, D3DDECLTYPE_FLOAT4, 0, D3DDECLUSAGE_BLENDWEIGHT, 0},
724             {0, 28, D3DDECLTYPE_UBYTE4, 0, D3DDECLUSAGE_BLENDINDICES, 0},
725             D3DDECL_END(),
726         }, D3DFVF_XYZB5 | D3DFVF_LASTBETA_UBYTE4},
727         {{
728             {0, 0, D3DDECLTYPE_FLOAT3, 0, D3DDECLUSAGE_POSITION, 0},
729             {0, 12, D3DDECLTYPE_FLOAT4, 0, D3DDECLUSAGE_BLENDWEIGHT, 0},
730             {0, 28, D3DDECLTYPE_D3DCOLOR, 0, D3DDECLUSAGE_BLENDINDICES, 0},
731             D3DDECL_END(),
732         }, D3DFVF_XYZB5 | D3DFVF_LASTBETA_D3DCOLOR},
733         {{
734             {0, 0, D3DDECLTYPE_FLOAT3, 0, D3DDECLUSAGE_NORMAL, 0},
735             D3DDECL_END(),
736         }, D3DFVF_NORMAL},
737         {{
738             {0, 0, D3DDECLTYPE_FLOAT3, 0, D3DDECLUSAGE_NORMAL, 0},
739             {0, 12, D3DDECLTYPE_D3DCOLOR, 0, D3DDECLUSAGE_COLOR, 0},
740             D3DDECL_END(),
741         }, D3DFVF_NORMAL | D3DFVF_DIFFUSE},
742         {{
743             {0, 0, D3DDECLTYPE_FLOAT1, 0, D3DDECLUSAGE_PSIZE, 0},
744             D3DDECL_END(),
745         }, D3DFVF_PSIZE},
746         {{
747             {0, 0, D3DDECLTYPE_D3DCOLOR, 0, D3DDECLUSAGE_COLOR, 0},
748             D3DDECL_END(),
749         }, D3DFVF_DIFFUSE},
750         {{
751             {0, 0, D3DDECLTYPE_D3DCOLOR, 0, D3DDECLUSAGE_COLOR, 1},
752             D3DDECL_END(),
753         }, D3DFVF_SPECULAR},
754         /* Make sure textures of different sizes work. */
755         {{
756             {0, 0, D3DDECLTYPE_FLOAT1, 0, D3DDECLUSAGE_TEXCOORD, 0},
757             D3DDECL_END(),
758         }, D3DFVF_TEXCOORDSIZE1(0) | D3DFVF_TEX1},
759         {{
760             {0, 0, D3DDECLTYPE_FLOAT2, 0, D3DDECLUSAGE_TEXCOORD, 0},
761             D3DDECL_END(),
762         }, D3DFVF_TEXCOORDSIZE2(0) | D3DFVF_TEX1},
763         {{
764             {0, 0, D3DDECLTYPE_FLOAT3, 0, D3DDECLUSAGE_TEXCOORD, 0},
765             D3DDECL_END(),
766         }, D3DFVF_TEXCOORDSIZE3(0) | D3DFVF_TEX1},
767         {{
768             {0, 0, D3DDECLTYPE_FLOAT4, 0, D3DDECLUSAGE_TEXCOORD, 0},
769             D3DDECL_END(),
770         }, D3DFVF_TEXCOORDSIZE4(0) | D3DFVF_TEX1},
771         /* Make sure the TEXCOORD index works correctly - try several textures. */
772         {{
773             {0, 0, D3DDECLTYPE_FLOAT1, 0, D3DDECLUSAGE_TEXCOORD, 0},
774             {0, 4, D3DDECLTYPE_FLOAT3, 0, D3DDECLUSAGE_TEXCOORD, 1},
775             {0, 16, D3DDECLTYPE_FLOAT2, 0, D3DDECLUSAGE_TEXCOORD, 2},
776             {0, 24, D3DDECLTYPE_FLOAT4, 0, D3DDECLUSAGE_TEXCOORD, 3},
777             D3DDECL_END(),
778         }, D3DFVF_TEX4 | D3DFVF_TEXCOORDSIZE1(0) | D3DFVF_TEXCOORDSIZE3(1)
779                 | D3DFVF_TEXCOORDSIZE2(2) | D3DFVF_TEXCOORDSIZE4(3)},
780         /* Now try some combination tests. */
781         {{
782             {0, 0, D3DDECLTYPE_FLOAT3, 0, D3DDECLUSAGE_POSITION, 0},
783             {0, 12, D3DDECLTYPE_FLOAT4, 0, D3DDECLUSAGE_BLENDWEIGHT, 0},
784             {0, 28, D3DDECLTYPE_D3DCOLOR, 0, D3DDECLUSAGE_COLOR, 0},
785             {0, 32, D3DDECLTYPE_D3DCOLOR, 0, D3DDECLUSAGE_COLOR, 1},
786             {0, 36, D3DDECLTYPE_FLOAT2, 0, D3DDECLUSAGE_TEXCOORD, 0},
787             {0, 44, D3DDECLTYPE_FLOAT3, 0, D3DDECLUSAGE_TEXCOORD, 1},
788             D3DDECL_END(),
789         }, D3DFVF_XYZB4 | D3DFVF_DIFFUSE | D3DFVF_SPECULAR | D3DFVF_TEX2
790                 | D3DFVF_TEXCOORDSIZE2(0) | D3DFVF_TEXCOORDSIZE3(1)},
791         {{
792             {0, 0, D3DDECLTYPE_FLOAT3, 0, D3DDECLUSAGE_POSITION, 0},
793             {0, 12, D3DDECLTYPE_FLOAT3, 0, D3DDECLUSAGE_NORMAL, 0},
794             {0, 24, D3DDECLTYPE_FLOAT1, 0, D3DDECLUSAGE_PSIZE, 0},
795             {0, 28, D3DDECLTYPE_D3DCOLOR, 0, D3DDECLUSAGE_COLOR, 1},
796             {0, 32, D3DDECLTYPE_FLOAT1, 0, D3DDECLUSAGE_TEXCOORD, 0},
797             {0, 36, D3DDECLTYPE_FLOAT4, 0, D3DDECLUSAGE_TEXCOORD, 1},
798             D3DDECL_END(),
799         }, D3DFVF_XYZ | D3DFVF_NORMAL | D3DFVF_PSIZE | D3DFVF_SPECULAR | D3DFVF_TEX2
800                 | D3DFVF_TEXCOORDSIZE1(0) | D3DFVF_TEXCOORDSIZE4(1)},
801     };
802     unsigned int i;
803
804     for (i = 0; i < sizeof(test_data) / sizeof(*test_data); ++i)
805     {
806         test_decl_to_fvf(test_data[i].decl, test_data[i].fvf, D3D_OK, __LINE__, i);
807         test_fvf_to_decl(test_data[i].fvf, test_data[i].decl, D3D_OK, __LINE__, i);
808     }
809
810     /* Usage indices for position and normal are apparently ignored. */
811     {
812         const D3DVERTEXELEMENT9 decl[] =
813         {
814             {0, 0, D3DDECLTYPE_FLOAT3, 0, D3DDECLUSAGE_POSITION, 1},
815             D3DDECL_END(),
816         };
817         test_decl_to_fvf(decl, D3DFVF_XYZ, D3D_OK, __LINE__, 0);
818     }
819     {
820         const D3DVERTEXELEMENT9 decl[] =
821         {
822             {0, 0, D3DDECLTYPE_FLOAT3, 0, D3DDECLUSAGE_NORMAL, 1},
823             D3DDECL_END(),
824         };
825         test_decl_to_fvf(decl, D3DFVF_NORMAL, D3D_OK, __LINE__, 0);
826     }
827     /* D3DFVF_LASTBETA_UBYTE4 and D3DFVF_LASTBETA_D3DCOLOR are ignored if
828      * there are no blend matrices. */
829     {
830         const D3DVERTEXELEMENT9 decl[] =
831         {
832             {0, 0, D3DDECLTYPE_FLOAT3, 0, D3DDECLUSAGE_POSITION, 0},
833             D3DDECL_END(),
834         };
835         test_fvf_to_decl(D3DFVF_XYZ | D3DFVF_LASTBETA_UBYTE4, decl, D3D_OK, __LINE__, 0);
836     }
837     {
838         const D3DVERTEXELEMENT9 decl[] =
839         {
840             {0, 0, D3DDECLTYPE_FLOAT3, 0, D3DDECLUSAGE_POSITION, 0},
841             D3DDECL_END(),
842         };
843         test_fvf_to_decl(D3DFVF_XYZ | D3DFVF_LASTBETA_D3DCOLOR, decl, D3D_OK, __LINE__, 0);
844     }
845     /* D3DFVF_LASTBETA_UBYTE4 takes precedence over D3DFVF_LASTBETA_D3DCOLOR. */
846     {
847         const D3DVERTEXELEMENT9 decl[] =
848         {
849             {0, 0, D3DDECLTYPE_FLOAT3, 0, D3DDECLUSAGE_POSITION, 0},
850             {0, 12, D3DDECLTYPE_FLOAT4, 0, D3DDECLUSAGE_BLENDWEIGHT, 0},
851             {0, 28, D3DDECLTYPE_UBYTE4, 0, D3DDECLUSAGE_BLENDINDICES, 0},
852             D3DDECL_END(),
853         };
854         test_fvf_to_decl(D3DFVF_XYZB5 | D3DFVF_LASTBETA_D3DCOLOR | D3DFVF_LASTBETA_UBYTE4,
855                 decl, D3D_OK, __LINE__, 0);
856     }
857     /* These are supposed to fail, both ways. */
858     {
859         const D3DVERTEXELEMENT9 decl[] =
860         {
861             {0, 0, D3DDECLTYPE_FLOAT4, 0, D3DDECLUSAGE_POSITION, 0},
862             D3DDECL_END(),
863         };
864         test_decl_to_fvf(decl, D3DFVF_XYZW, D3DERR_INVALIDCALL, __LINE__, 0);
865         test_fvf_to_decl(D3DFVF_XYZW, decl, D3DERR_INVALIDCALL, __LINE__, 0);
866     }
867     {
868         const D3DVERTEXELEMENT9 decl[] =
869         {
870             {0, 0, D3DDECLTYPE_FLOAT4, 0, D3DDECLUSAGE_POSITION, 0},
871             {0, 16, D3DDECLTYPE_FLOAT3, 0, D3DDECLUSAGE_NORMAL, 0},
872             D3DDECL_END(),
873         };
874         test_decl_to_fvf(decl, D3DFVF_XYZW | D3DFVF_NORMAL, D3DERR_INVALIDCALL, __LINE__, 0);
875         test_fvf_to_decl(D3DFVF_XYZW | D3DFVF_NORMAL, decl, D3DERR_INVALIDCALL, __LINE__, 0);
876     }
877     {
878         const D3DVERTEXELEMENT9 decl[] =
879         {
880             {0, 0, D3DDECLTYPE_FLOAT3, 0, D3DDECLUSAGE_POSITION, 0},
881             {0, 12, D3DDECLTYPE_FLOAT4, 0, D3DDECLUSAGE_BLENDWEIGHT, 0},
882             {0, 28, D3DDECLTYPE_FLOAT1, 0, D3DDECLUSAGE_BLENDINDICES, 0},
883             D3DDECL_END(),
884         };
885         test_decl_to_fvf(decl, D3DFVF_XYZB5, D3DERR_INVALIDCALL, __LINE__, 0);
886         test_fvf_to_decl(D3DFVF_XYZB5, decl, D3DERR_INVALIDCALL, __LINE__, 0);
887     }
888     /* Test a declaration that can't be converted to an FVF. */
889     {
890         const D3DVERTEXELEMENT9 decl[] =
891         {
892             {0, 0, D3DDECLTYPE_FLOAT3, 0, D3DDECLUSAGE_POSITION, 0},
893             {0, 12, D3DDECLTYPE_FLOAT3, 0, D3DDECLUSAGE_NORMAL, 0},
894             {0, 24, D3DDECLTYPE_FLOAT1, 0, D3DDECLUSAGE_PSIZE, 0},
895             {0, 28, D3DDECLTYPE_D3DCOLOR, 0, D3DDECLUSAGE_COLOR, 1},
896             {0, 32, D3DDECLTYPE_FLOAT1, 0, D3DDECLUSAGE_TEXCOORD, 0},
897             /* 8 bytes padding */
898             {0, 44, D3DDECLTYPE_FLOAT4, 0, D3DDECLUSAGE_TEXCOORD, 1},
899             D3DDECL_END(),
900         };
901         test_decl_to_fvf(decl, 0, D3DERR_INVALIDCALL, __LINE__, 0);
902     }
903     /* Elements must be ordered by offset. */
904     {
905         const D3DVERTEXELEMENT9 decl[] =
906         {
907             {0, 12, D3DDECLTYPE_D3DCOLOR, 0, D3DDECLUSAGE_COLOR, 0},
908             {0, 0, D3DDECLTYPE_FLOAT3, 0, D3DDECLUSAGE_POSITION, 0},
909             D3DDECL_END(),
910         };
911         test_decl_to_fvf(decl, 0, D3DERR_INVALIDCALL, __LINE__, 0);
912     }
913     /* Basic tests for element order. */
914     {
915         const D3DVERTEXELEMENT9 decl[] =
916         {
917             {0, 0, D3DDECLTYPE_FLOAT3, 0, D3DDECLUSAGE_POSITION, 0},
918             {0, 12, D3DDECLTYPE_D3DCOLOR, 0, D3DDECLUSAGE_COLOR, 0},
919             {0, 16, D3DDECLTYPE_FLOAT3, 0, D3DDECLUSAGE_NORMAL, 0},
920             D3DDECL_END(),
921         };
922         test_decl_to_fvf(decl, 0, D3DERR_INVALIDCALL, __LINE__, 0);
923     }
924     {
925         const D3DVERTEXELEMENT9 decl[] =
926         {
927             {0, 0, D3DDECLTYPE_D3DCOLOR, 0, D3DDECLUSAGE_COLOR, 0},
928             {0, 4, D3DDECLTYPE_FLOAT3, 0, D3DDECLUSAGE_POSITION, 0},
929             D3DDECL_END(),
930         };
931         test_decl_to_fvf(decl, 0, D3DERR_INVALIDCALL, __LINE__, 0);
932     }
933     {
934         const D3DVERTEXELEMENT9 decl[] =
935         {
936             {0, 0, D3DDECLTYPE_FLOAT3, 0, D3DDECLUSAGE_NORMAL, 0},
937             {0, 12, D3DDECLTYPE_FLOAT3, 0, D3DDECLUSAGE_POSITION, 0},
938             D3DDECL_END(),
939         };
940         test_decl_to_fvf(decl, 0, D3DERR_INVALIDCALL, __LINE__, 0);
941     }
942     /* Textures must be ordered by texcoords. */
943     {
944         const D3DVERTEXELEMENT9 decl[] =
945         {
946             {0, 0, D3DDECLTYPE_FLOAT1, 0, D3DDECLUSAGE_TEXCOORD, 0},
947             {0, 4, D3DDECLTYPE_FLOAT3, 0, D3DDECLUSAGE_TEXCOORD, 2},
948             {0, 16, D3DDECLTYPE_FLOAT2, 0, D3DDECLUSAGE_TEXCOORD, 1},
949             {0, 24, D3DDECLTYPE_FLOAT4, 0, D3DDECLUSAGE_TEXCOORD, 3},
950             D3DDECL_END(),
951         };
952         test_decl_to_fvf(decl, 0, D3DERR_INVALIDCALL, __LINE__, 0);
953     }
954     /* Duplicate elements are not allowed. */
955     {
956         const D3DVERTEXELEMENT9 decl[] =
957         {
958             {0, 0, D3DDECLTYPE_FLOAT3, 0, D3DDECLUSAGE_POSITION, 0},
959             {0, 12, D3DDECLTYPE_D3DCOLOR, 0, D3DDECLUSAGE_COLOR, 0},
960             {0, 16, D3DDECLTYPE_D3DCOLOR, 0, D3DDECLUSAGE_COLOR, 0},
961             D3DDECL_END(),
962         };
963         test_decl_to_fvf(decl, 0, D3DERR_INVALIDCALL, __LINE__, 0);
964     }
965     /* Invalid FVFs cannot be converted to a declarator. */
966     test_fvf_to_decl(0xdeadbeef, NULL, D3DERR_INVALIDCALL, __LINE__, 0);
967 }
968
969 static void D3DXGetFVFVertexSizeTest(void)
970 {
971     UINT got;
972
973     compare_vertex_sizes (D3DFVF_XYZ, 12);
974
975     compare_vertex_sizes (D3DFVF_XYZB3, 24);
976
977     compare_vertex_sizes (D3DFVF_XYZB5, 32);
978
979     compare_vertex_sizes (D3DFVF_XYZ | D3DFVF_NORMAL, 24);
980
981     compare_vertex_sizes (D3DFVF_XYZ | D3DFVF_DIFFUSE, 16);
982
983     compare_vertex_sizes (
984         D3DFVF_XYZ |
985         D3DFVF_TEX1 |
986         D3DFVF_TEXCOORDSIZE1(0), 16);
987     compare_vertex_sizes (
988         D3DFVF_XYZ |
989         D3DFVF_TEX2 |
990         D3DFVF_TEXCOORDSIZE1(0) |
991         D3DFVF_TEXCOORDSIZE1(1), 20);
992
993     compare_vertex_sizes (
994         D3DFVF_XYZ |
995         D3DFVF_TEX1 |
996         D3DFVF_TEXCOORDSIZE2(0), 20);
997
998     compare_vertex_sizes (
999         D3DFVF_XYZ |
1000         D3DFVF_TEX2 |
1001         D3DFVF_TEXCOORDSIZE2(0) |
1002         D3DFVF_TEXCOORDSIZE2(1), 28);
1003
1004     compare_vertex_sizes (
1005         D3DFVF_XYZ |
1006         D3DFVF_TEX6 |
1007         D3DFVF_TEXCOORDSIZE2(0) |
1008         D3DFVF_TEXCOORDSIZE2(1) |
1009         D3DFVF_TEXCOORDSIZE2(2) |
1010         D3DFVF_TEXCOORDSIZE2(3) |
1011         D3DFVF_TEXCOORDSIZE2(4) |
1012         D3DFVF_TEXCOORDSIZE2(5), 60);
1013
1014     compare_vertex_sizes (
1015         D3DFVF_XYZ |
1016         D3DFVF_TEX8 |
1017         D3DFVF_TEXCOORDSIZE2(0) |
1018         D3DFVF_TEXCOORDSIZE2(1) |
1019         D3DFVF_TEXCOORDSIZE2(2) |
1020         D3DFVF_TEXCOORDSIZE2(3) |
1021         D3DFVF_TEXCOORDSIZE2(4) |
1022         D3DFVF_TEXCOORDSIZE2(5) |
1023         D3DFVF_TEXCOORDSIZE2(6) |
1024         D3DFVF_TEXCOORDSIZE2(7), 76);
1025
1026     compare_vertex_sizes (
1027         D3DFVF_XYZ |
1028         D3DFVF_TEX1 |
1029         D3DFVF_TEXCOORDSIZE3(0), 24);
1030
1031     compare_vertex_sizes (
1032         D3DFVF_XYZ |
1033         D3DFVF_TEX4 |
1034         D3DFVF_TEXCOORDSIZE3(0) |
1035         D3DFVF_TEXCOORDSIZE3(1) |
1036         D3DFVF_TEXCOORDSIZE3(2) |
1037         D3DFVF_TEXCOORDSIZE3(3), 60);
1038
1039     compare_vertex_sizes (
1040         D3DFVF_XYZ |
1041         D3DFVF_TEX1 |
1042         D3DFVF_TEXCOORDSIZE4(0), 28);
1043
1044     compare_vertex_sizes (
1045         D3DFVF_XYZ |
1046         D3DFVF_TEX2 |
1047         D3DFVF_TEXCOORDSIZE4(0) |
1048         D3DFVF_TEXCOORDSIZE4(1), 44);
1049
1050     compare_vertex_sizes (
1051         D3DFVF_XYZ |
1052         D3DFVF_TEX3 |
1053         D3DFVF_TEXCOORDSIZE4(0) |
1054         D3DFVF_TEXCOORDSIZE4(1) |
1055         D3DFVF_TEXCOORDSIZE4(2), 60);
1056
1057     compare_vertex_sizes (
1058         D3DFVF_XYZB5 |
1059         D3DFVF_NORMAL |
1060         D3DFVF_DIFFUSE |
1061         D3DFVF_SPECULAR |
1062         D3DFVF_TEX8 |
1063         D3DFVF_TEXCOORDSIZE4(0) |
1064         D3DFVF_TEXCOORDSIZE4(1) |
1065         D3DFVF_TEXCOORDSIZE4(2) |
1066         D3DFVF_TEXCOORDSIZE4(3) |
1067         D3DFVF_TEXCOORDSIZE4(4) |
1068         D3DFVF_TEXCOORDSIZE4(5) |
1069         D3DFVF_TEXCOORDSIZE4(6) |
1070         D3DFVF_TEXCOORDSIZE4(7), 180);
1071 }
1072
1073 static void D3DXIntersectTriTest(void)
1074 {
1075     BOOL exp_res, got_res;
1076     D3DXVECTOR3 position, ray, vertex[3];
1077     FLOAT exp_dist, got_dist, exp_u, got_u, exp_v, got_v;
1078
1079     vertex[0].x = 1.0f; vertex[0].y = 0.0f; vertex[0].z = 0.0f;
1080     vertex[1].x = 2.0f; vertex[1].y = 0.0f; vertex[1].z = 0.0f;
1081     vertex[2].x = 1.0f; vertex[2].y = 1.0f; vertex[2].z = 0.0f;
1082
1083     position.x = -14.5f; position.y = -23.75f; position.z = -32.0f;
1084
1085     ray.x = 2.0f; ray.y = 3.0f; ray.z = 4.0f;
1086
1087     exp_res = TRUE; exp_u = 0.5f; exp_v = 0.25f; exp_dist = 8.0f;
1088
1089     got_res = D3DXIntersectTri(&vertex[0],&vertex[1],&vertex[2],&position,&ray,&got_u,&got_v,&got_dist);
1090     ok( got_res == exp_res, "Expected result = %d, got %d\n",exp_res,got_res);
1091     ok( compare(exp_u,got_u), "Expected u = %f, got %f\n",exp_u,got_u);
1092     ok( compare(exp_v,got_v), "Expected v = %f, got %f\n",exp_v,got_v);
1093     ok( compare(exp_dist,got_dist), "Expected distance = %f, got %f\n",exp_dist,got_dist);
1094
1095 /*Only positive ray is taken in account*/
1096
1097     vertex[0].x = 1.0f; vertex[0].y = 0.0f; vertex[0].z = 0.0f;
1098     vertex[1].x = 2.0f; vertex[1].y = 0.0f; vertex[1].z = 0.0f;
1099     vertex[2].x = 1.0f; vertex[2].y = 1.0f; vertex[2].z = 0.0f;
1100
1101     position.x = 17.5f; position.y = 24.25f; position.z = 32.0f;
1102
1103     ray.x = 2.0f; ray.y = 3.0f; ray.z = 4.0f;
1104
1105     exp_res = FALSE;
1106
1107     got_res = D3DXIntersectTri(&vertex[0],&vertex[1],&vertex[2],&position,&ray,&got_u,&got_v,&got_dist);
1108     ok( got_res == exp_res, "Expected result = %d, got %d\n",exp_res,got_res);
1109
1110 /*Intersection between ray and triangle in a same plane is considered as empty*/
1111
1112     vertex[0].x = 4.0f; vertex[0].y = 0.0f; vertex[0].z = 0.0f;
1113     vertex[1].x = 6.0f; vertex[1].y = 0.0f; vertex[1].z = 0.0f;
1114     vertex[2].x = 4.0f; vertex[2].y = 2.0f; vertex[2].z = 0.0f;
1115
1116     position.x = 1.0f; position.y = 1.0f; position.z = 0.0f;
1117
1118     ray.x = 1.0f; ray.y = 0.0f; ray.z = 0.0f;
1119
1120     exp_res = FALSE;
1121
1122     got_res = D3DXIntersectTri(&vertex[0],&vertex[1],&vertex[2],&position,&ray,&got_u,&got_v,&got_dist);
1123     ok( got_res == exp_res, "Expected result = %d, got %d\n",exp_res,got_res);
1124 }
1125
1126 static void D3DXCreateMeshTest(void)
1127 {
1128     HRESULT hr;
1129     HWND wnd;
1130     IDirect3D9 *d3d;
1131     IDirect3DDevice9 *device, *test_device;
1132     D3DPRESENT_PARAMETERS d3dpp;
1133     ID3DXMesh *d3dxmesh;
1134     int i, size;
1135     D3DVERTEXELEMENT9 test_decl[MAX_FVF_DECL_SIZE];
1136     DWORD options;
1137     struct mesh mesh;
1138
1139     static const D3DVERTEXELEMENT9 decl1[3] = {
1140         {0, 0, D3DDECLTYPE_FLOAT3, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_POSITION, 0},
1141         {0, 12, D3DDECLTYPE_FLOAT3, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_NORMAL, 0},
1142         D3DDECL_END(), };
1143
1144     static const D3DVERTEXELEMENT9 decl2[] = {
1145         {0, 0, D3DDECLTYPE_FLOAT3, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_POSITION, 0},
1146         {0, 12, D3DDECLTYPE_FLOAT3, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_NORMAL, 0},
1147         {0, 24, D3DDECLTYPE_FLOAT1, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_PSIZE, 0},
1148         {0, 28, D3DDECLTYPE_D3DCOLOR, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_COLOR, 1},
1149         {0, 32, D3DDECLTYPE_FLOAT1, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_TEXCOORD, 0},
1150         /* 8 bytes padding */
1151         {0, 44, D3DDECLTYPE_FLOAT4, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_TEXCOORD, 1},
1152         D3DDECL_END(),
1153     };
1154
1155     static const D3DVERTEXELEMENT9 decl3[] = {
1156         {0, 0, D3DDECLTYPE_FLOAT3, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_POSITION, 0},
1157         {1, 0, D3DDECLTYPE_FLOAT3, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_NORMAL, 0},
1158         D3DDECL_END(),
1159     };
1160
1161     hr = D3DXCreateMesh(0, 0, 0, NULL, NULL, NULL);
1162     ok(hr == D3DERR_INVALIDCALL, "Got result %x, expected %x (D3DERR_INVALIDCALL)\n", hr, D3DERR_INVALIDCALL);
1163
1164     hr = D3DXCreateMesh(1, 3, D3DXMESH_MANAGED, decl1, NULL, &d3dxmesh);
1165     ok(hr == D3DERR_INVALIDCALL, "Got result %x, expected %x (D3DERR_INVALIDCALL)\n", hr, D3DERR_INVALIDCALL);
1166
1167     wnd = CreateWindow("static", "d3dx9_test", 0, 0, 0, 0, 0, NULL, NULL, NULL, NULL);
1168     if (!wnd)
1169     {
1170         skip("Couldn't create application window\n");
1171         return;
1172     }
1173     d3d = Direct3DCreate9(D3D_SDK_VERSION);
1174     if (!d3d)
1175     {
1176         skip("Couldn't create IDirect3D9 object\n");
1177         DestroyWindow(wnd);
1178         return;
1179     }
1180
1181     ZeroMemory(&d3dpp, sizeof(d3dpp));
1182     d3dpp.Windowed = TRUE;
1183     d3dpp.SwapEffect = D3DSWAPEFFECT_DISCARD;
1184     hr = IDirect3D9_CreateDevice(d3d, D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, wnd, D3DCREATE_MIXED_VERTEXPROCESSING, &d3dpp, &device);
1185     if (FAILED(hr))
1186     {
1187         skip("Failed to create IDirect3DDevice9 object %#x\n", hr);
1188         IDirect3D9_Release(d3d);
1189         DestroyWindow(wnd);
1190         return;
1191     }
1192
1193     hr = D3DXCreateMesh(0, 3, D3DXMESH_MANAGED, decl1, device, &d3dxmesh);
1194     ok(hr == D3DERR_INVALIDCALL, "Got result %x, expected %x (D3DERR_INVALIDCALL)\n", hr, D3DERR_INVALIDCALL);
1195
1196     hr = D3DXCreateMesh(1, 0, D3DXMESH_MANAGED, decl1, device, &d3dxmesh);
1197     ok(hr == D3DERR_INVALIDCALL, "Got result %x, expected %x (D3DERR_INVALIDCALL)\n", hr, D3DERR_INVALIDCALL);
1198
1199     hr = D3DXCreateMesh(1, 3, 0, decl1, device, &d3dxmesh);
1200     ok(hr == D3D_OK, "Got result %x, expected %x (D3D_OK)\n", hr, D3D_OK);
1201
1202     if (hr == D3D_OK)
1203     {
1204         d3dxmesh->lpVtbl->Release(d3dxmesh);
1205     }
1206
1207     hr = D3DXCreateMesh(1, 3, D3DXMESH_MANAGED, 0, device, &d3dxmesh);
1208     ok(hr == D3DERR_INVALIDCALL, "Got result %x, expected %x (D3DERR_INVALIDCALL)\n", hr, D3DERR_INVALIDCALL);
1209
1210     hr = D3DXCreateMesh(1, 3, D3DXMESH_MANAGED, decl1, device, NULL);
1211     ok(hr == D3DERR_INVALIDCALL, "Got result %x, expected %x (D3DERR_INVALIDCALL)\n", hr, D3DERR_INVALIDCALL);
1212
1213     hr = D3DXCreateMesh(1, 3, D3DXMESH_MANAGED, decl1, device, &d3dxmesh);
1214     ok(hr == D3D_OK, "Got result %x, expected 0 (D3D_OK)\n", hr);
1215
1216     if (hr == D3D_OK)
1217     {
1218         /* device */
1219         hr = d3dxmesh->lpVtbl->GetDevice(d3dxmesh, NULL);
1220         ok(hr == D3DERR_INVALIDCALL, "Got result %x, expected %x (D3DERR_INVALIDCALL)\n", hr, D3DERR_INVALIDCALL);
1221
1222         hr = d3dxmesh->lpVtbl->GetDevice(d3dxmesh, &test_device);
1223         ok(hr == D3D_OK, "Got result %x, expected %x (D3D_OK)\n", hr, D3D_OK);
1224         ok(test_device == device, "Got result %p, expected %p\n", test_device, device);
1225
1226         if (hr == D3D_OK)
1227         {
1228             IDirect3DDevice9_Release(device);
1229         }
1230
1231         /* declaration */
1232         hr = d3dxmesh->lpVtbl->GetDeclaration(d3dxmesh, NULL);
1233         ok(hr == D3DERR_INVALIDCALL, "Got result %x, expected %x (D3DERR_INVALIDCALL)\n", hr, D3DERR_INVALIDCALL);
1234
1235         hr = d3dxmesh->lpVtbl->GetDeclaration(d3dxmesh, test_decl);
1236         ok(hr == D3D_OK, "Got result %x, expected 0 (D3D_OK)\n", hr);
1237
1238         if (hr == D3D_OK)
1239         {
1240             size = sizeof(decl1) / sizeof(decl1[0]);
1241             for (i = 0; i < size - 1; i++)
1242             {
1243                 ok(test_decl[i].Stream == decl1[i].Stream, "Returned stream %d, expected %d\n", test_decl[i].Stream, decl1[i].Stream);
1244                 ok(test_decl[i].Type == decl1[i].Type, "Returned type %d, expected %d\n", test_decl[i].Type, decl1[i].Type);
1245                 ok(test_decl[i].Method == decl1[i].Method, "Returned method %d, expected %d\n", test_decl[i].Method, decl1[i].Method);
1246                 ok(test_decl[i].Usage == decl1[i].Usage, "Returned usage %d, expected %d\n", test_decl[i].Usage, decl1[i].Usage);
1247                 ok(test_decl[i].UsageIndex == decl1[i].UsageIndex, "Returned usage index %d, expected %d\n", test_decl[i].UsageIndex, decl1[i].UsageIndex);
1248                 ok(test_decl[i].Offset == decl1[i].Offset, "Returned offset %d, expected %d\n", test_decl[i].Offset, decl1[i].Offset);
1249             }
1250             ok(decl1[size-1].Stream == 0xFF, "Returned too long vertex declaration\n"); /* end element */
1251         }
1252
1253         /* options */
1254         options = d3dxmesh->lpVtbl->GetOptions(d3dxmesh);
1255         ok(options == D3DXMESH_MANAGED, "Got result %x, expected %x (D3DXMESH_MANAGED)\n", options, D3DXMESH_MANAGED);
1256
1257         /* rest */
1258         if (!new_mesh(&mesh, 3, 1))
1259         {
1260             skip("Couldn't create mesh\n");
1261         }
1262         else
1263         {
1264             memset(mesh.vertices, 0, mesh.number_of_vertices * sizeof(*mesh.vertices));
1265             memset(mesh.faces, 0, mesh.number_of_faces * sizeof(*mesh.faces));
1266             mesh.fvf = D3DFVF_XYZ | D3DFVF_NORMAL;
1267
1268             compare_mesh("createmesh1", d3dxmesh, &mesh);
1269
1270             free_mesh(&mesh);
1271         }
1272
1273         d3dxmesh->lpVtbl->Release(d3dxmesh);
1274     }
1275
1276     /* Test a declaration that can't be converted to an FVF. */
1277     hr = D3DXCreateMesh(1, 3, D3DXMESH_MANAGED, decl2, device, &d3dxmesh);
1278     ok(hr == D3D_OK, "Got result %x, expected 0 (D3D_OK)\n", hr);
1279
1280     if (hr == D3D_OK)
1281     {
1282         /* device */
1283         hr = d3dxmesh->lpVtbl->GetDevice(d3dxmesh, NULL);
1284         ok(hr == D3DERR_INVALIDCALL, "Got result %x, expected %x (D3DERR_INVALIDCALL)\n", hr, D3DERR_INVALIDCALL);
1285
1286         hr = d3dxmesh->lpVtbl->GetDevice(d3dxmesh, &test_device);
1287         ok(hr == D3D_OK, "Got result %x, expected %x (D3D_OK)\n", hr, D3D_OK);
1288         ok(test_device == device, "Got result %p, expected %p\n", test_device, device);
1289
1290         if (hr == D3D_OK)
1291         {
1292             IDirect3DDevice9_Release(device);
1293         }
1294
1295         /* declaration */
1296         hr = d3dxmesh->lpVtbl->GetDeclaration(d3dxmesh, test_decl);
1297         ok(hr == D3D_OK, "Got result %x, expected 0 (D3D_OK)\n", hr);
1298
1299         if (hr == D3D_OK)
1300         {
1301             size = sizeof(decl2) / sizeof(decl2[0]);
1302             for (i = 0; i < size - 1; i++)
1303             {
1304                 ok(test_decl[i].Stream == decl2[i].Stream, "Returned stream %d, expected %d\n", test_decl[i].Stream, decl2[i].Stream);
1305                 ok(test_decl[i].Type == decl2[i].Type, "Returned type %d, expected %d\n", test_decl[i].Type, decl2[i].Type);
1306                 ok(test_decl[i].Method == decl2[i].Method, "Returned method %d, expected %d\n", test_decl[i].Method, decl2[i].Method);
1307                 ok(test_decl[i].Usage == decl2[i].Usage, "Returned usage %d, expected %d\n", test_decl[i].Usage, decl2[i].Usage);
1308                 ok(test_decl[i].UsageIndex == decl2[i].UsageIndex, "Returned usage index %d, expected %d\n", test_decl[i].UsageIndex, decl2[i].UsageIndex);
1309                 ok(test_decl[i].Offset == decl2[i].Offset, "Returned offset %d, expected %d\n", test_decl[i].Offset, decl2[i].Offset);
1310             }
1311             ok(decl2[size-1].Stream == 0xFF, "Returned too long vertex declaration\n"); /* end element */
1312         }
1313
1314         /* options */
1315         options = d3dxmesh->lpVtbl->GetOptions(d3dxmesh);
1316         ok(options == D3DXMESH_MANAGED, "Got result %x, expected %x (D3DXMESH_MANAGED)\n", options, D3DXMESH_MANAGED);
1317
1318         /* rest */
1319         if (!new_mesh(&mesh, 3, 1))
1320         {
1321             skip("Couldn't create mesh\n");
1322         }
1323         else
1324         {
1325             memset(mesh.vertices, 0, mesh.number_of_vertices * sizeof(*mesh.vertices));
1326             memset(mesh.faces, 0, mesh.number_of_faces * sizeof(*mesh.faces));
1327             mesh.fvf = 0;
1328             mesh.vertex_size = 60;
1329
1330             compare_mesh("createmesh2", d3dxmesh, &mesh);
1331
1332             free_mesh(&mesh);
1333         }
1334
1335         mesh.vertex_size = d3dxmesh->lpVtbl->GetNumBytesPerVertex(d3dxmesh);
1336         ok(mesh.vertex_size == 60, "Got vertex size %u, expected %u\n", mesh.vertex_size, 60);
1337
1338         d3dxmesh->lpVtbl->Release(d3dxmesh);
1339     }
1340
1341     /* Test a declaration with multiple streams. */
1342     hr = D3DXCreateMesh(1, 3, D3DXMESH_MANAGED, decl3, device, &d3dxmesh);
1343     ok(hr == D3DERR_INVALIDCALL, "Got result %x, expected %x (D3DERR_INVALIDCALL)\n", hr, D3DERR_INVALIDCALL);
1344
1345     IDirect3DDevice9_Release(device);
1346     IDirect3D9_Release(d3d);
1347     DestroyWindow(wnd);
1348 }
1349
1350 static void D3DXCreateMeshFVFTest(void)
1351 {
1352     HRESULT hr;
1353     HWND wnd;
1354     IDirect3D9 *d3d;
1355     IDirect3DDevice9 *device, *test_device;
1356     D3DPRESENT_PARAMETERS d3dpp;
1357     ID3DXMesh *d3dxmesh;
1358     int i, size;
1359     D3DVERTEXELEMENT9 test_decl[MAX_FVF_DECL_SIZE];
1360     DWORD options;
1361     struct mesh mesh;
1362
1363     static const D3DVERTEXELEMENT9 decl[3] = {
1364         {0, 0, D3DDECLTYPE_FLOAT3, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_POSITION, 0},
1365         {0, 12, D3DDECLTYPE_FLOAT3, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_NORMAL, 0},
1366         D3DDECL_END(), };
1367
1368     hr = D3DXCreateMeshFVF(0, 0, 0, 0, NULL, NULL);
1369     ok(hr == D3DERR_INVALIDCALL, "Got result %x, expected %x (D3DERR_INVALIDCALL)\n", hr, D3DERR_INVALIDCALL);
1370
1371     hr = D3DXCreateMeshFVF(1, 3, D3DXMESH_MANAGED, D3DFVF_XYZ | D3DFVF_NORMAL, NULL, &d3dxmesh);
1372     ok(hr == D3DERR_INVALIDCALL, "Got result %x, expected %x (D3DERR_INVALIDCALL)\n", hr, D3DERR_INVALIDCALL);
1373
1374     wnd = CreateWindow("static", "d3dx9_test", 0, 0, 0, 0, 0, NULL, NULL, NULL, NULL);
1375     if (!wnd)
1376     {
1377         skip("Couldn't create application window\n");
1378         return;
1379     }
1380     d3d = Direct3DCreate9(D3D_SDK_VERSION);
1381     if (!d3d)
1382     {
1383         skip("Couldn't create IDirect3D9 object\n");
1384         DestroyWindow(wnd);
1385         return;
1386     }
1387
1388     ZeroMemory(&d3dpp, sizeof(d3dpp));
1389     d3dpp.Windowed = TRUE;
1390     d3dpp.SwapEffect = D3DSWAPEFFECT_DISCARD;
1391     hr = IDirect3D9_CreateDevice(d3d, D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, wnd, D3DCREATE_MIXED_VERTEXPROCESSING, &d3dpp, &device);
1392     if (FAILED(hr))
1393     {
1394         skip("Failed to create IDirect3DDevice9 object %#x\n", hr);
1395         IDirect3D9_Release(d3d);
1396         DestroyWindow(wnd);
1397         return;
1398     }
1399
1400     hr = D3DXCreateMeshFVF(0, 3, D3DXMESH_MANAGED, D3DFVF_XYZ | D3DFVF_NORMAL, device, &d3dxmesh);
1401     ok(hr == D3DERR_INVALIDCALL, "Got result %x, expected %x (D3DERR_INVALIDCALL)\n", hr, D3DERR_INVALIDCALL);
1402
1403     hr = D3DXCreateMeshFVF(1, 0, D3DXMESH_MANAGED, D3DFVF_XYZ | D3DFVF_NORMAL, device, &d3dxmesh);
1404     ok(hr == D3DERR_INVALIDCALL, "Got result %x, expected %x (D3DERR_INVALIDCALL)\n", hr, D3DERR_INVALIDCALL);
1405
1406     hr = D3DXCreateMeshFVF(1, 3, 0, D3DFVF_XYZ | D3DFVF_NORMAL, device, &d3dxmesh);
1407     ok(hr == D3D_OK, "Got result %x, expected %x (D3D_OK)\n", hr, D3D_OK);
1408
1409     if (hr == D3D_OK)
1410     {
1411         d3dxmesh->lpVtbl->Release(d3dxmesh);
1412     }
1413
1414     hr = D3DXCreateMeshFVF(1, 3, D3DXMESH_MANAGED, 0xdeadbeef, device, &d3dxmesh);
1415     ok(hr == D3DERR_INVALIDCALL, "Got result %x, expected %x (D3DERR_INVALIDCALL)\n", hr, D3DERR_INVALIDCALL);
1416
1417     hr = D3DXCreateMeshFVF(1, 3, D3DXMESH_MANAGED, D3DFVF_XYZ | D3DFVF_NORMAL, device, NULL);
1418     ok(hr == D3DERR_INVALIDCALL, "Got result %x, expected %x (D3DERR_INVALIDCALL)\n", hr, D3DERR_INVALIDCALL);
1419
1420     hr = D3DXCreateMeshFVF(1, 3, D3DXMESH_MANAGED, D3DFVF_XYZ | D3DFVF_NORMAL, device, &d3dxmesh);
1421     ok(hr == D3D_OK, "Got result %x, expected 0 (D3D_OK)\n", hr);
1422
1423     if (hr == D3D_OK)
1424     {
1425         /* device */
1426         hr = d3dxmesh->lpVtbl->GetDevice(d3dxmesh, NULL);
1427         ok(hr == D3DERR_INVALIDCALL, "Got result %x, expected %x (D3DERR_INVALIDCALL)\n", hr, D3DERR_INVALIDCALL);
1428
1429         hr = d3dxmesh->lpVtbl->GetDevice(d3dxmesh, &test_device);
1430         ok(hr == D3D_OK, "Got result %x, expected %x (D3D_OK)\n", hr, D3D_OK);
1431         ok(test_device == device, "Got result %p, expected %p\n", test_device, device);
1432
1433         if (hr == D3D_OK)
1434         {
1435             IDirect3DDevice9_Release(device);
1436         }
1437
1438         /* declaration */
1439         hr = d3dxmesh->lpVtbl->GetDeclaration(d3dxmesh, NULL);
1440         ok(hr == D3DERR_INVALIDCALL, "Got result %x, expected %x (D3DERR_INVALIDCALL)\n", hr, D3DERR_INVALIDCALL);
1441
1442         hr = d3dxmesh->lpVtbl->GetDeclaration(d3dxmesh, test_decl);
1443         ok(hr == D3D_OK, "Got result %x, expected 0 (D3D_OK)\n", hr);
1444
1445         if (hr == D3D_OK)
1446         {
1447             size = sizeof(decl) / sizeof(decl[0]);
1448             for (i = 0; i < size - 1; i++)
1449             {
1450                 ok(test_decl[i].Stream == decl[i].Stream, "Returned stream %d, expected %d\n", test_decl[i].Stream, decl[i].Stream);
1451                 ok(test_decl[i].Type == decl[i].Type, "Returned type %d, expected %d\n", test_decl[i].Type, decl[i].Type);
1452                 ok(test_decl[i].Method == decl[i].Method, "Returned method %d, expected %d\n", test_decl[i].Method, decl[i].Method);
1453                 ok(test_decl[i].Usage == decl[i].Usage, "Returned usage %d, expected %d\n", test_decl[i].Usage, decl[i].Usage);
1454                 ok(test_decl[i].UsageIndex == decl[i].UsageIndex, "Returned usage index %d, expected %d\n",
1455                    test_decl[i].UsageIndex, decl[i].UsageIndex);
1456                 ok(test_decl[i].Offset == decl[i].Offset, "Returned offset %d, expected %d\n", test_decl[i].Offset, decl[i].Offset);
1457             }
1458             ok(decl[size-1].Stream == 0xFF, "Returned too long vertex declaration\n"); /* end element */
1459         }
1460
1461         /* options */
1462         options = d3dxmesh->lpVtbl->GetOptions(d3dxmesh);
1463         ok(options == D3DXMESH_MANAGED, "Got result %x, expected %x (D3DXMESH_MANAGED)\n", options, D3DXMESH_MANAGED);
1464
1465         /* rest */
1466         if (!new_mesh(&mesh, 3, 1))
1467         {
1468             skip("Couldn't create mesh\n");
1469         }
1470         else
1471         {
1472             memset(mesh.vertices, 0, mesh.number_of_vertices * sizeof(*mesh.vertices));
1473             memset(mesh.faces, 0, mesh.number_of_faces * sizeof(*mesh.faces));
1474             mesh.fvf = D3DFVF_XYZ | D3DFVF_NORMAL;
1475
1476             compare_mesh("createmeshfvf", d3dxmesh, &mesh);
1477
1478             free_mesh(&mesh);
1479         }
1480
1481         d3dxmesh->lpVtbl->Release(d3dxmesh);
1482     }
1483
1484     IDirect3DDevice9_Release(device);
1485     IDirect3D9_Release(d3d);
1486     DestroyWindow(wnd);
1487 }
1488
1489 #define check_vertex_buffer(mesh, vertices, num_vertices, fvf) \
1490     check_vertex_buffer_(__LINE__, mesh, vertices, num_vertices, fvf)
1491 static void check_vertex_buffer_(int line, ID3DXMesh *mesh, const void *vertices, DWORD num_vertices, DWORD fvf)
1492 {
1493     DWORD mesh_num_vertices = mesh->lpVtbl->GetNumVertices(mesh);
1494     DWORD mesh_fvf = mesh->lpVtbl->GetFVF(mesh);
1495     const void *mesh_vertices;
1496     HRESULT hr;
1497
1498     ok_(__FILE__,line)(fvf == mesh_fvf, "expected FVF %x, got %x\n", fvf, mesh_fvf);
1499     ok_(__FILE__,line)(num_vertices == mesh_num_vertices,
1500        "Expected %u vertices, got %u\n", num_vertices, mesh_num_vertices);
1501
1502     hr = mesh->lpVtbl->LockVertexBuffer(mesh, D3DLOCK_READONLY, (void**)&mesh_vertices);
1503     ok_(__FILE__,line)(hr == D3D_OK, "LockVertexBuffer returned %x, expected %x (D3D_OK)\n", hr, D3D_OK);
1504     if (FAILED(hr))
1505         return;
1506
1507     if (mesh_fvf == fvf) {
1508         DWORD vertex_size = D3DXGetFVFVertexSize(fvf);
1509         int i;
1510         for (i = 0; i < min(num_vertices, mesh_num_vertices); i++)
1511         {
1512             const FLOAT *exp_float = vertices;
1513             const FLOAT *got_float = mesh_vertices;
1514             DWORD texcount;
1515             DWORD pos_dim = 0;
1516             int j;
1517             BOOL last_beta_dword = FALSE;
1518             char prefix[128];
1519
1520             switch (fvf & D3DFVF_POSITION_MASK) {
1521                 case D3DFVF_XYZ: pos_dim = 3; break;
1522                 case D3DFVF_XYZRHW: pos_dim = 4; break;
1523                 case D3DFVF_XYZB1:
1524                 case D3DFVF_XYZB2:
1525                 case D3DFVF_XYZB3:
1526                 case D3DFVF_XYZB4:
1527                 case D3DFVF_XYZB5:
1528                     pos_dim = (fvf & D3DFVF_POSITION_MASK) - D3DFVF_XYZB1 + 1;
1529                     if (fvf & (D3DFVF_LASTBETA_UBYTE4 | D3DFVF_LASTBETA_D3DCOLOR))
1530                     {
1531                         pos_dim--;
1532                         last_beta_dword = TRUE;
1533                     }
1534                     break;
1535                 case D3DFVF_XYZW: pos_dim = 4; break;
1536             }
1537             sprintf(prefix, "vertex[%u] position, ", i);
1538             check_floats_(line, prefix, got_float, exp_float, pos_dim);
1539             exp_float += pos_dim;
1540             got_float += pos_dim;
1541
1542             if (last_beta_dword) {
1543                 ok_(__FILE__,line)(*(DWORD*)exp_float == *(DWORD*)got_float,
1544                     "Vertex[%u]: Expected last beta %08x, got %08x\n", i, *(DWORD*)exp_float, *(DWORD*)got_float);
1545                 exp_float++;
1546                 got_float++;
1547             }
1548
1549             if (fvf & D3DFVF_NORMAL) {
1550                 sprintf(prefix, "vertex[%u] normal, ", i);
1551                 check_floats_(line, prefix, got_float, exp_float, 3);
1552                 exp_float += 3;
1553                 got_float += 3;
1554             }
1555             if (fvf & D3DFVF_PSIZE) {
1556                 ok_(__FILE__,line)(compare(*exp_float, *got_float),
1557                         "Vertex[%u]: Expected psize %g, got %g\n", i, *exp_float, *got_float);
1558                 exp_float++;
1559                 got_float++;
1560             }
1561             if (fvf & D3DFVF_DIFFUSE) {
1562                 ok_(__FILE__,line)(*(DWORD*)exp_float == *(DWORD*)got_float,
1563                     "Vertex[%u]: Expected diffuse %08x, got %08x\n", i, *(DWORD*)exp_float, *(DWORD*)got_float);
1564                 exp_float++;
1565                 got_float++;
1566             }
1567             if (fvf & D3DFVF_SPECULAR) {
1568                 ok_(__FILE__,line)(*(DWORD*)exp_float == *(DWORD*)got_float,
1569                     "Vertex[%u]: Expected specular %08x, got %08x\n", i, *(DWORD*)exp_float, *(DWORD*)got_float);
1570                 exp_float++;
1571                 got_float++;
1572             }
1573
1574             texcount = (fvf & D3DFVF_TEXCOUNT_MASK) >> D3DFVF_TEXCOUNT_SHIFT;
1575             for (j = 0; j < texcount; j++) {
1576                 DWORD dim = (((fvf >> (16 + 2 * j)) + 1) & 0x03) + 1;
1577                 sprintf(prefix, "vertex[%u] texture, ", i);
1578                 check_floats_(line, prefix, got_float, exp_float, dim);
1579                 exp_float += dim;
1580                 got_float += dim;
1581             }
1582
1583             vertices = (BYTE*)vertices + vertex_size;
1584             mesh_vertices = (BYTE*)mesh_vertices + vertex_size;
1585         }
1586     }
1587
1588     mesh->lpVtbl->UnlockVertexBuffer(mesh);
1589 }
1590
1591 #define check_index_buffer(mesh, indices, num_indices, index_size) \
1592     check_index_buffer_(__LINE__, mesh, indices, num_indices, index_size)
1593 static void check_index_buffer_(int line, ID3DXMesh *mesh, const void *indices, DWORD num_indices, DWORD index_size)
1594 {
1595     DWORD mesh_index_size = (mesh->lpVtbl->GetOptions(mesh) & D3DXMESH_32BIT) ? 4 : 2;
1596     DWORD mesh_num_indices = mesh->lpVtbl->GetNumFaces(mesh) * 3;
1597     const void *mesh_indices;
1598     HRESULT hr;
1599     DWORD i;
1600
1601     ok_(__FILE__,line)(index_size == mesh_index_size,
1602         "Expected index size %u, got %u\n", index_size, mesh_index_size);
1603     ok_(__FILE__,line)(num_indices == mesh_num_indices,
1604         "Expected %u indices, got %u\n", num_indices, mesh_num_indices);
1605
1606     hr = mesh->lpVtbl->LockIndexBuffer(mesh, D3DLOCK_READONLY, (void**)&mesh_indices);
1607     ok_(__FILE__,line)(hr == D3D_OK, "LockIndexBuffer returned %x, expected %x (D3D_OK)\n", hr, D3D_OK);
1608     if (FAILED(hr))
1609         return;
1610
1611     if (mesh_index_size == index_size) {
1612         for (i = 0; i < min(num_indices, mesh_num_indices); i++)
1613         {
1614             if (index_size == 4)
1615                 ok_(__FILE__,line)(*(DWORD*)indices == *(DWORD*)mesh_indices,
1616                     "Index[%u]: expected %u, got %u\n", i, *(DWORD*)indices, *(DWORD*)mesh_indices);
1617             else
1618                 ok_(__FILE__,line)(*(WORD*)indices == *(WORD*)mesh_indices,
1619                     "Index[%u]: expected %u, got %u\n", i, *(WORD*)indices, *(WORD*)mesh_indices);
1620             indices = (BYTE*)indices + index_size;
1621             mesh_indices = (BYTE*)mesh_indices + index_size;
1622         }
1623     }
1624     mesh->lpVtbl->UnlockIndexBuffer(mesh);
1625 }
1626
1627 #define check_matrix(got, expected) check_matrix_(__LINE__, got, expected)
1628 static void check_matrix_(int line, const D3DXMATRIX *got, const D3DXMATRIX *expected)
1629 {
1630     int i, j;
1631     for (i = 0; i < 4; i++) {
1632         for (j = 0; j < 4; j++) {
1633             ok_(__FILE__,line)(compare(U(*expected).m[i][j], U(*got).m[i][j]),
1634                     "matrix[%u][%u]: expected %g, got %g\n",
1635                     i, j, U(*expected).m[i][j], U(*got).m[i][j]);
1636         }
1637     }
1638 }
1639
1640 static void check_colorvalue_(int line, const char *prefix, const D3DCOLORVALUE got, const D3DCOLORVALUE expected)
1641 {
1642     ok_(__FILE__,line)(expected.r == got.r && expected.g == got.g && expected.b == got.b && expected.a == got.a,
1643             "%sExpected (%g, %g, %g, %g), got (%g, %g, %g, %g)\n", prefix,
1644             expected.r, expected.g, expected.b, expected.a, got.r, got.g, got.b, got.a);
1645 }
1646
1647 #define check_materials(got, got_count, expected, expected_count) \
1648     check_materials_(__LINE__, got, got_count, expected, expected_count)
1649 static void check_materials_(int line, const D3DXMATERIAL *got, DWORD got_count, const D3DXMATERIAL *expected, DWORD expected_count)
1650 {
1651     int i;
1652     ok_(__FILE__,line)(expected_count == got_count, "Expected %u materials, got %u\n", expected_count, got_count);
1653     if (!expected) {
1654         ok_(__FILE__,line)(got == NULL, "Expected NULL material ptr, got %p\n", got);
1655         return;
1656     }
1657     for (i = 0; i < min(expected_count, got_count); i++)
1658     {
1659         if (!expected[i].pTextureFilename)
1660             ok_(__FILE__,line)(got[i].pTextureFilename == NULL,
1661                     "Expected NULL pTextureFilename, got %p\n", got[i].pTextureFilename);
1662         else
1663             ok_(__FILE__,line)(!strcmp(expected[i].pTextureFilename, got[i].pTextureFilename),
1664                     "Expected '%s' for pTextureFilename, got '%s'\n", expected[i].pTextureFilename, got[i].pTextureFilename);
1665         check_colorvalue_(line, "Diffuse: ", got[i].MatD3D.Diffuse, expected[i].MatD3D.Diffuse);
1666         check_colorvalue_(line, "Ambient: ", got[i].MatD3D.Ambient, expected[i].MatD3D.Ambient);
1667         check_colorvalue_(line, "Specular: ", got[i].MatD3D.Specular, expected[i].MatD3D.Specular);
1668         check_colorvalue_(line, "Emissive: ", got[i].MatD3D.Emissive, expected[i].MatD3D.Emissive);
1669         ok_(__FILE__,line)(expected[i].MatD3D.Power == got[i].MatD3D.Power,
1670                 "Power: Expected %g, got %g\n", expected[i].MatD3D.Power, got[i].MatD3D.Power);
1671     }
1672 }
1673
1674 #define check_generated_adjacency(mesh, got, epsilon) check_generated_adjacency_(__LINE__, mesh, got, epsilon)
1675 static void check_generated_adjacency_(int line, ID3DXMesh *mesh, const DWORD *got, FLOAT epsilon)
1676 {
1677     DWORD *expected;
1678     DWORD num_faces = mesh->lpVtbl->GetNumFaces(mesh);
1679     HRESULT hr;
1680
1681     expected = HeapAlloc(GetProcessHeap(), 0, num_faces * sizeof(DWORD) * 3);
1682     if (!expected) {
1683         skip_(__FILE__, line)("Out of memory\n");
1684         return;
1685     }
1686     hr = mesh->lpVtbl->GenerateAdjacency(mesh, epsilon, expected);
1687     ok_(__FILE__, line)(hr == D3D_OK, "Expected D3D_OK, got %#x\n", hr);
1688     if (SUCCEEDED(hr))
1689     {
1690         int i;
1691         for (i = 0; i < num_faces; i++)
1692         {
1693             ok_(__FILE__, line)(expected[i * 3] == got[i * 3] &&
1694                     expected[i * 3 + 1] == got[i * 3 + 1] &&
1695                     expected[i * 3 + 2] == got[i * 3 + 2],
1696                     "Face %u adjacencies: Expected (%u, %u, %u), got (%u, %u, %u)\n", i,
1697                     expected[i * 3], expected[i * 3 + 1], expected[i * 3 + 2],
1698                     got[i * 3], got[i * 3 + 1], got[i * 3 + 2]);
1699         }
1700     }
1701     HeapFree(GetProcessHeap(), 0, expected);
1702 }
1703
1704 #define check_generated_effects(materials, num_materials, effects) \
1705     check_generated_effects_(__LINE__, materials, num_materials, effects)
1706 static void check_generated_effects_(int line, const D3DXMATERIAL *materials, DWORD num_materials, const D3DXEFFECTINSTANCE *effects)
1707 {
1708     int i;
1709     static const struct {
1710         const char *name;
1711         DWORD name_size;
1712         DWORD num_bytes;
1713         DWORD value_offset;
1714     } params[] = {
1715 #define EFFECT_TABLE_ENTRY(str, field) \
1716     {str, sizeof(str), sizeof(materials->MatD3D.field), offsetof(D3DXMATERIAL, MatD3D.field)}
1717         EFFECT_TABLE_ENTRY("Diffuse", Diffuse),
1718         EFFECT_TABLE_ENTRY("Power", Power),
1719         EFFECT_TABLE_ENTRY("Specular", Specular),
1720         EFFECT_TABLE_ENTRY("Emissive", Emissive),
1721         EFFECT_TABLE_ENTRY("Ambient", Ambient),
1722 #undef EFFECT_TABLE_ENTRY
1723     };
1724
1725     if (!num_materials) {
1726         ok_(__FILE__, line)(effects == NULL, "Expected NULL effects, got %p\n", effects);
1727         return;
1728     }
1729     for (i = 0; i < num_materials; i++)
1730     {
1731         int j;
1732         DWORD expected_num_defaults = ARRAY_SIZE(params) + (materials[i].pTextureFilename ? 1 : 0);
1733
1734         ok_(__FILE__,line)(expected_num_defaults == effects[i].NumDefaults,
1735                 "effect[%u] NumDefaults: Expected %u, got %u\n", i,
1736                 expected_num_defaults, effects[i].NumDefaults);
1737         for (j = 0; j < min(ARRAY_SIZE(params), effects[i].NumDefaults); j++)
1738         {
1739             int k;
1740             D3DXEFFECTDEFAULT *got_param = &effects[i].pDefaults[j];
1741             ok_(__FILE__,line)(!strcmp(params[j].name, got_param->pParamName),
1742                "effect[%u].pDefaults[%u].pParamName: Expected '%s', got '%s'\n", i, j,
1743                params[j].name, got_param->pParamName);
1744             ok_(__FILE__,line)(D3DXEDT_FLOATS == got_param->Type,
1745                "effect[%u].pDefaults[%u].Type: Expected %u, got %u\n", i, j,
1746                D3DXEDT_FLOATS, got_param->Type);
1747             ok_(__FILE__,line)(params[j].num_bytes == got_param->NumBytes,
1748                "effect[%u].pDefaults[%u].NumBytes: Expected %u, got %u\n", i, j,
1749                params[j].num_bytes, got_param->NumBytes);
1750             for (k = 0; k < min(params[j].num_bytes, got_param->NumBytes) / 4; k++)
1751             {
1752                 FLOAT expected = ((FLOAT*)((BYTE*)&materials[i] + params[j].value_offset))[k];
1753                 FLOAT got = ((FLOAT*)got_param->pValue)[k];
1754                 ok_(__FILE__,line)(compare(expected, got),
1755                    "effect[%u].pDefaults[%u] float value %u: Expected %g, got %g\n", i, j, k, expected, got);
1756             }
1757         }
1758         if (effects[i].NumDefaults > ARRAY_SIZE(params)) {
1759             D3DXEFFECTDEFAULT *got_param = &effects[i].pDefaults[j];
1760             static const char *expected_name = "Texture0@Name";
1761
1762             ok_(__FILE__,line)(!strcmp(expected_name, got_param->pParamName),
1763                "effect[%u].pDefaults[%u].pParamName: Expected '%s', got '%s'\n", i, j,
1764                expected_name, got_param->pParamName);
1765             ok_(__FILE__,line)(D3DXEDT_STRING == got_param->Type,
1766                "effect[%u].pDefaults[%u].Type: Expected %u, got %u\n", i, j,
1767                D3DXEDT_STRING, got_param->Type);
1768             if (materials[i].pTextureFilename) {
1769                 ok_(__FILE__,line)(strlen(materials[i].pTextureFilename) + 1 == got_param->NumBytes,
1770                    "effect[%u] texture filename length: Expected %u, got %u\n", i,
1771                    (DWORD)strlen(materials[i].pTextureFilename) + 1, got_param->NumBytes);
1772                 ok_(__FILE__,line)(!strcmp(materials[i].pTextureFilename, got_param->pValue),
1773                    "effect[%u] texture filename: Expected '%s', got '%s'\n", i,
1774                    materials[i].pTextureFilename, (char*)got_param->pValue);
1775             }
1776         }
1777     }
1778 }
1779
1780 static LPSTR strdupA(LPCSTR p)
1781 {
1782     LPSTR ret;
1783     if (!p) return NULL;
1784     ret = HeapAlloc(GetProcessHeap(), 0, strlen(p) + 1);
1785     if (ret) strcpy(ret, p);
1786     return ret;
1787 }
1788
1789 static CALLBACK HRESULT ID3DXAllocateHierarchyImpl_DestroyFrame(ID3DXAllocateHierarchy *iface, LPD3DXFRAME frame)
1790 {
1791     TRACECALLBACK("ID3DXAllocateHierarchyImpl_DestroyFrame(%p, %p)\n", iface, frame);
1792     if (frame) {
1793         HeapFree(GetProcessHeap(), 0, frame->Name);
1794         HeapFree(GetProcessHeap(), 0, frame);
1795     }
1796     return D3D_OK;
1797 }
1798
1799 static CALLBACK HRESULT ID3DXAllocateHierarchyImpl_CreateFrame(ID3DXAllocateHierarchy *iface, LPCSTR name, LPD3DXFRAME *new_frame)
1800 {
1801     LPD3DXFRAME frame;
1802
1803     TRACECALLBACK("ID3DXAllocateHierarchyImpl_CreateFrame(%p, '%s', %p)\n", iface, name, new_frame);
1804     frame = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(*frame));
1805     if (!frame)
1806         return E_OUTOFMEMORY;
1807     if (name) {
1808         frame->Name = strdupA(name);
1809         if (!frame->Name) {
1810             HeapFree(GetProcessHeap(), 0, frame);
1811             return E_OUTOFMEMORY;
1812         }
1813     }
1814     *new_frame = frame;
1815     return D3D_OK;
1816 }
1817
1818 static HRESULT destroy_mesh_container(LPD3DXMESHCONTAINER mesh_container)
1819 {
1820     int i;
1821
1822     if (!mesh_container)
1823         return D3D_OK;
1824     HeapFree(GetProcessHeap(), 0, mesh_container->Name);
1825     if (U(mesh_container->MeshData).pMesh)
1826         IUnknown_Release(U(mesh_container->MeshData).pMesh);
1827     if (mesh_container->pMaterials) {
1828         for (i = 0; i < mesh_container->NumMaterials; i++)
1829             HeapFree(GetProcessHeap(), 0, mesh_container->pMaterials[i].pTextureFilename);
1830         HeapFree(GetProcessHeap(), 0, mesh_container->pMaterials);
1831     }
1832     if (mesh_container->pEffects) {
1833         for (i = 0; i < mesh_container->NumMaterials; i++) {
1834             HeapFree(GetProcessHeap(), 0, mesh_container->pEffects[i].pEffectFilename);
1835             if (mesh_container->pEffects[i].pDefaults) {
1836                 int j;
1837                 for (j = 0; j < mesh_container->pEffects[i].NumDefaults; j++) {
1838                     HeapFree(GetProcessHeap(), 0, mesh_container->pEffects[i].pDefaults[j].pParamName);
1839                     HeapFree(GetProcessHeap(), 0, mesh_container->pEffects[i].pDefaults[j].pValue);
1840                 }
1841                 HeapFree(GetProcessHeap(), 0, mesh_container->pEffects[i].pDefaults);
1842             }
1843         }
1844         HeapFree(GetProcessHeap(), 0, mesh_container->pEffects);
1845     }
1846     HeapFree(GetProcessHeap(), 0, mesh_container->pAdjacency);
1847     if (mesh_container->pSkinInfo)
1848         IUnknown_Release(mesh_container->pSkinInfo);
1849     HeapFree(GetProcessHeap(), 0, mesh_container);
1850     return D3D_OK;
1851 }
1852
1853 static CALLBACK HRESULT ID3DXAllocateHierarchyImpl_DestroyMeshContainer(ID3DXAllocateHierarchy *iface, LPD3DXMESHCONTAINER mesh_container)
1854 {
1855     TRACECALLBACK("ID3DXAllocateHierarchyImpl_DestroyMeshContainer(%p, %p)\n", iface, mesh_container);
1856     return destroy_mesh_container(mesh_container);
1857 }
1858
1859 static CALLBACK HRESULT ID3DXAllocateHierarchyImpl_CreateMeshContainer(ID3DXAllocateHierarchy *iface,
1860         LPCSTR name, CONST D3DXMESHDATA *mesh_data, CONST D3DXMATERIAL *materials,
1861         CONST D3DXEFFECTINSTANCE *effects, DWORD num_materials, CONST DWORD *adjacency,
1862         LPD3DXSKININFO skin_info, LPD3DXMESHCONTAINER *new_mesh_container)
1863 {
1864     LPD3DXMESHCONTAINER mesh_container = NULL;
1865     int i;
1866
1867     TRACECALLBACK("ID3DXAllocateHierarchyImpl_CreateMeshContainer(%p, '%s', %u, %p, %p, %p, %d, %p, %p, %p)\n",
1868             iface, name, mesh_data->Type, U(*mesh_data).pMesh, materials, effects,
1869             num_materials, adjacency, skin_info, *new_mesh_container);
1870
1871     mesh_container = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(*mesh_container));
1872     if (!mesh_container)
1873         return E_OUTOFMEMORY;
1874
1875     if (name) {
1876         mesh_container->Name = strdupA(name);
1877         if (!mesh_container->Name)
1878             goto error;
1879     }
1880
1881     mesh_container->NumMaterials = num_materials;
1882     if (num_materials) {
1883         mesh_container->pMaterials = HeapAlloc(GetProcessHeap(), 0, num_materials * sizeof(*materials));
1884         if (!mesh_container->pMaterials)
1885             goto error;
1886
1887         memcpy(mesh_container->pMaterials, materials, num_materials * sizeof(*materials));
1888         for (i = 0; i < num_materials; i++)
1889             mesh_container->pMaterials[i].pTextureFilename = NULL;
1890         for (i = 0; i < num_materials; i++) {
1891             if (materials[i].pTextureFilename) {
1892                 mesh_container->pMaterials[i].pTextureFilename = strdupA(materials[i].pTextureFilename);
1893                 if (!mesh_container->pMaterials[i].pTextureFilename)
1894                     goto error;
1895             }
1896         }
1897
1898         mesh_container->pEffects = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, num_materials * sizeof(*effects));
1899         if (!mesh_container->pEffects)
1900             goto error;
1901         for (i = 0; i < num_materials; i++) {
1902             int j;
1903             const D3DXEFFECTINSTANCE *effect_src = &effects[i];
1904             D3DXEFFECTINSTANCE *effect_dest = &mesh_container->pEffects[i];
1905
1906             if (effect_src->pEffectFilename) {
1907                 effect_dest->pEffectFilename = strdupA(effect_src->pEffectFilename);
1908                 if (!effect_dest->pEffectFilename)
1909                     goto error;
1910             }
1911             effect_dest->pDefaults = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY,
1912                     effect_src->NumDefaults * sizeof(*effect_src->pDefaults));
1913             if (!effect_dest->pDefaults)
1914                 goto error;
1915             effect_dest->NumDefaults = effect_src->NumDefaults;
1916             for (j = 0; j < effect_src->NumDefaults; j++) {
1917                 const D3DXEFFECTDEFAULT *default_src = &effect_src->pDefaults[j];
1918                 D3DXEFFECTDEFAULT *default_dest = &effect_dest->pDefaults[j];
1919
1920                 if (default_src->pParamName) {
1921                     default_dest->pParamName = strdupA(default_src->pParamName);
1922                     if (!default_dest->pParamName)
1923                         goto error;
1924                 }
1925                 default_dest->NumBytes = default_src->NumBytes;
1926                 default_dest->Type = default_src->Type;
1927                 default_dest->pValue = HeapAlloc(GetProcessHeap(), 0, default_src->NumBytes);
1928                 memcpy(default_dest->pValue, default_src->pValue, default_src->NumBytes);
1929             }
1930         }
1931     }
1932
1933     ok(adjacency != NULL, "Expected non-NULL adjacency, got NULL\n");
1934     if (adjacency) {
1935         if (mesh_data->Type == D3DXMESHTYPE_MESH || mesh_data->Type == D3DXMESHTYPE_PMESH) {
1936             ID3DXBaseMesh *basemesh = (ID3DXBaseMesh*)U(*mesh_data).pMesh;
1937             DWORD num_faces = basemesh->lpVtbl->GetNumFaces(basemesh);
1938             size_t size = num_faces * sizeof(DWORD) * 3;
1939             mesh_container->pAdjacency = HeapAlloc(GetProcessHeap(), 0, size);
1940             if (!mesh_container->pAdjacency)
1941                 goto error;
1942             memcpy(mesh_container->pAdjacency, adjacency, size);
1943         } else {
1944             ok(mesh_data->Type == D3DXMESHTYPE_PATCHMESH, "Unknown mesh type %u\n", mesh_data->Type);
1945             if (mesh_data->Type == D3DXMESHTYPE_PATCHMESH)
1946                 trace("FIXME: copying adjacency data for patch mesh not implemented\n");
1947         }
1948     }
1949
1950     memcpy(&mesh_container->MeshData, mesh_data, sizeof(*mesh_data));
1951     if (U(*mesh_data).pMesh)
1952         IUnknown_AddRef(U(*mesh_data).pMesh);
1953     if (skin_info) {
1954         mesh_container->pSkinInfo = skin_info;
1955         skin_info->lpVtbl->AddRef(skin_info);
1956     }
1957     *new_mesh_container = mesh_container;
1958
1959     return S_OK;
1960 error:
1961     destroy_mesh_container(mesh_container);
1962     return E_OUTOFMEMORY;
1963 }
1964
1965 static ID3DXAllocateHierarchyVtbl ID3DXAllocateHierarchyImpl_Vtbl = {
1966     ID3DXAllocateHierarchyImpl_CreateFrame,
1967     ID3DXAllocateHierarchyImpl_CreateMeshContainer,
1968     ID3DXAllocateHierarchyImpl_DestroyFrame,
1969     ID3DXAllocateHierarchyImpl_DestroyMeshContainer,
1970 };
1971 static ID3DXAllocateHierarchy alloc_hier = { &ID3DXAllocateHierarchyImpl_Vtbl };
1972
1973 #define test_LoadMeshFromX(device, xfile_str, vertex_array, fvf, index_array, materials_array, check_adjacency) \
1974     test_LoadMeshFromX_(__LINE__, device, xfile_str, sizeof(xfile_str) - 1, vertex_array, ARRAY_SIZE(vertex_array), fvf, \
1975             index_array, ARRAY_SIZE(index_array), sizeof(*index_array), materials_array, ARRAY_SIZE(materials_array), \
1976             check_adjacency);
1977 static void test_LoadMeshFromX_(int line, IDirect3DDevice9 *device, const char *xfile_str, size_t xfile_strlen,
1978         const void *vertices, DWORD num_vertices, DWORD fvf, const void *indices, DWORD num_indices, size_t index_size,
1979         const D3DXMATERIAL *expected_materials, DWORD expected_num_materials, BOOL check_adjacency)
1980 {
1981     HRESULT hr;
1982     ID3DXBuffer *materials = NULL;
1983     ID3DXBuffer *effects = NULL;
1984     ID3DXBuffer *adjacency = NULL;
1985     ID3DXMesh *mesh = NULL;
1986     DWORD num_materials = 0;
1987
1988     /* Adjacency is not checked when the X file contains multiple meshes,
1989      * since calling GenerateAdjacency on the merged mesh is not equivalent
1990      * to calling GenerateAdjacency on the individual meshes and then merging
1991      * the adjacency data. */
1992     hr = D3DXLoadMeshFromXInMemory(xfile_str, xfile_strlen, D3DXMESH_MANAGED, device,
1993             check_adjacency ? &adjacency : NULL, &materials, &effects, &num_materials, &mesh);
1994     ok_(__FILE__,line)(hr == D3D_OK, "Expected D3D_OK, got %#x\n", hr);
1995     if (SUCCEEDED(hr)) {
1996         D3DXMATERIAL *materials_ptr = materials ? ID3DXBuffer_GetBufferPointer(materials) : NULL;
1997         D3DXEFFECTINSTANCE *effects_ptr = effects ? ID3DXBuffer_GetBufferPointer(effects) : NULL;
1998         DWORD *adjacency_ptr = check_adjacency ? ID3DXBuffer_GetBufferPointer(adjacency) : NULL;
1999
2000         check_vertex_buffer_(line, mesh, vertices, num_vertices, fvf);
2001         check_index_buffer_(line, mesh, indices, num_indices, index_size);
2002         check_materials_(line, materials_ptr, num_materials, expected_materials, expected_num_materials);
2003         check_generated_effects_(line, materials_ptr, num_materials, effects_ptr);
2004         if (check_adjacency)
2005             check_generated_adjacency_(line, mesh, adjacency_ptr, 0.0f);
2006
2007         if (materials) ID3DXBuffer_Release(materials);
2008         if (effects) ID3DXBuffer_Release(effects);
2009         if (adjacency) ID3DXBuffer_Release(adjacency);
2010         IUnknown_Release(mesh);
2011     }
2012 }
2013
2014 static void D3DXLoadMeshTest(void)
2015 {
2016     static const char empty_xfile[] = "xof 0303txt 0032";
2017     /*________________________*/
2018     static const char simple_xfile[] =
2019         "xof 0303txt 0032"
2020         "Mesh {"
2021             "3;"
2022             "0.0; 0.0; 0.0;,"
2023             "0.0; 1.0; 0.0;,"
2024             "1.0; 1.0; 0.0;;"
2025             "1;"
2026             "3; 0, 1, 2;;"
2027         "}";
2028     static const WORD simple_index_buffer[] = {0, 1, 2};
2029     static const D3DXVECTOR3 simple_vertex_buffer[] = {
2030         {0.0, 0.0, 0.0}, {0.0, 1.0, 0.0}, {1.0, 1.0, 0.0}
2031     };
2032     const DWORD simple_fvf = D3DFVF_XYZ;
2033     static const char framed_xfile[] =
2034         "xof 0303txt 0032"
2035         "Frame {"
2036             "Mesh { 3; 0.0; 0.0; 0.0;, 0.0; 1.0; 0.0;, 1.0; 1.0; 0.0;; 1; 3; 0, 1, 2;; }"
2037             "FrameTransformMatrix {" /* translation (0.0, 0.0, 2.0) */
2038               "1.0, 0.0, 0.0, 0.0,"
2039               "0.0, 1.0, 0.0, 0.0,"
2040               "0.0, 0.0, 1.0, 0.0,"
2041               "0.0, 0.0, 2.0, 1.0;;"
2042             "}"
2043             "Mesh { 3; 0.0; 0.0; 0.0;, 0.0; 1.0; 0.0;, 2.0; 1.0; 0.0;; 1; 3; 0, 1, 2;; }"
2044             "FrameTransformMatrix {" /* translation (0.0, 0.0, 3.0) */
2045               "1.0, 0.0, 0.0, 0.0,"
2046               "0.0, 1.0, 0.0, 0.0,"
2047               "0.0, 0.0, 1.0, 0.0,"
2048               "0.0, 0.0, 3.0, 1.0;;"
2049             "}"
2050             "Mesh { 3; 0.0; 0.0; 0.0;, 0.0; 1.0; 0.0;, 3.0; 1.0; 0.0;; 1; 3; 0, 1, 2;; }"
2051         "}";
2052     static const WORD framed_index_buffer[] = { 0, 1, 2 };
2053     static const D3DXVECTOR3 framed_vertex_buffers[3][3] = {
2054         {{0.0, 0.0, 0.0}, {0.0, 1.0, 0.0}, {1.0, 1.0, 0.0}},
2055         {{0.0, 0.0, 0.0}, {0.0, 1.0, 0.0}, {2.0, 1.0, 0.0}},
2056         {{0.0, 0.0, 0.0}, {0.0, 1.0, 0.0}, {3.0, 1.0, 0.0}},
2057     };
2058     static const WORD merged_index_buffer[] = { 0, 1, 2, 3, 4, 5, 6, 7, 8 };
2059     /* frame transforms accumulates for D3DXLoadMeshFromX */
2060     static const D3DXVECTOR3 merged_vertex_buffer[] = {
2061         {0.0, 0.0, 0.0}, {0.0, 1.0, 0.0}, {1.0, 1.0, 0.0},
2062         {0.0, 0.0, 2.0}, {0.0, 1.0, 2.0}, {2.0, 1.0, 2.0},
2063         {0.0, 0.0, 5.0}, {0.0, 1.0, 5.0}, {3.0, 1.0, 5.0},
2064     };
2065     const DWORD framed_fvf = D3DFVF_XYZ;
2066     /*________________________*/
2067     static const char box_xfile[] =
2068         "xof 0303txt 0032"
2069         "Mesh {"
2070             "8;" /* DWORD nVertices; */
2071             /* array Vector vertices[nVertices]; */
2072             "0.0; 0.0; 0.0;,"
2073             "0.0; 0.0; 1.0;,"
2074             "0.0; 1.0; 0.0;,"
2075             "0.0; 1.0; 1.0;,"
2076             "1.0; 0.0; 0.0;,"
2077             "1.0; 0.0; 1.0;,"
2078             "1.0; 1.0; 0.0;,"
2079             "1.0; 1.0; 1.0;;"
2080             "6;" /* DWORD nFaces; */
2081             /* array MeshFace faces[nFaces]; */
2082             "4; 0, 1, 3, 2;," /* (left side) */
2083             "4; 2, 3, 7, 6;," /* (top side) */
2084             "4; 6, 7, 5, 4;," /* (right side) */
2085             "4; 1, 0, 4, 5;," /* (bottom side) */
2086             "4; 1, 5, 7, 3;," /* (back side) */
2087             "4; 0, 2, 6, 4;;" /* (front side) */
2088             "MeshNormals {"
2089               "6;" /* DWORD nNormals; */
2090               /* array Vector normals[nNormals]; */
2091               "-1.0; 0.0; 0.0;,"
2092               "0.0; 1.0; 0.0;,"
2093               "1.0; 0.0; 0.0;,"
2094               "0.0; -1.0; 0.0;,"
2095               "0.0; 0.0; 1.0;,"
2096               "0.0; 0.0; -1.0;;"
2097               "6;" /* DWORD nFaceNormals; */
2098               /* array MeshFace faceNormals[nFaceNormals]; */
2099               "4; 0, 0, 0, 0;,"
2100               "4; 1, 1, 1, 1;,"
2101               "4; 2, 2, 2, 2;,"
2102               "4; 3, 3, 3, 3;,"
2103               "4; 4, 4, 4, 4;,"
2104               "4; 5, 5, 5, 5;;"
2105             "}"
2106             "MeshMaterialList materials {"
2107               "2;" /* DWORD nMaterials; */
2108               "6;" /* DWORD nFaceIndexes; */
2109               /* array DWORD faceIndexes[nFaceIndexes]; */
2110               "0, 0, 0, 1, 1, 1;;"
2111               "Material {"
2112                 /* ColorRGBA faceColor; */
2113                 "0.0; 0.0; 1.0; 1.0;;"
2114                 /* FLOAT power; */
2115                 "0.5;"
2116                 /* ColorRGB specularColor; */
2117                 "1.0; 1.0; 1.0;;"
2118                 /* ColorRGB emissiveColor; */
2119                 "0.0; 0.0; 0.0;;"
2120               "}"
2121               "Material {"
2122                 /* ColorRGBA faceColor; */
2123                 "1.0; 1.0; 1.0; 1.0;;"
2124                 /* FLOAT power; */
2125                 "1.0;"
2126                 /* ColorRGB specularColor; */
2127                 "1.0; 1.0; 1.0;;"
2128                 /* ColorRGB emissiveColor; */
2129                 "0.0; 0.0; 0.0;;"
2130                 "TextureFilename { \"texture.jpg\"; }"
2131               "}"
2132             "}"
2133             "MeshVertexColors {"
2134               "8;" /* DWORD nVertexColors; */
2135               /* array IndexedColor vertexColors[nVertexColors]; */
2136               "0; 0.0; 0.0; 0.0; 0.0;;"
2137               "1; 0.0; 0.0; 1.0; 0.1;;"
2138               "2; 0.0; 1.0; 0.0; 0.2;;"
2139               "3; 0.0; 1.0; 1.0; 0.3;;"
2140               "4; 1.0; 0.0; 0.0; 0.4;;"
2141               "5; 1.0; 0.0; 1.0; 0.5;;"
2142               "6; 1.0; 1.0; 0.0; 0.6;;"
2143               "7; 1.0; 1.0; 1.0; 0.7;;"
2144             "}"
2145             "MeshTextureCoords {"
2146               "8;" /* DWORD nTextureCoords; */
2147               /* array Coords2d textureCoords[nTextureCoords]; */
2148               "0.0; 1.0;,"
2149               "1.0; 1.0;,"
2150               "0.0; 0.0;,"
2151               "1.0; 0.0;,"
2152               "1.0; 1.0;,"
2153               "0.0; 1.0;,"
2154               "1.0; 0.0;,"
2155               "0.0; 0.0;;"
2156             "}"
2157           "}";
2158     static const WORD box_index_buffer[] = {
2159         0, 1, 3,
2160         0, 3, 2,
2161         8, 9, 7,
2162         8, 7, 6,
2163         10, 11, 5,
2164         10, 5, 4,
2165         12, 13, 14,
2166         12, 14, 15,
2167         16, 17, 18,
2168         16, 18, 19,
2169         20, 21, 22,
2170         20, 22, 23,
2171     };
2172     static const struct {
2173         D3DXVECTOR3 position;
2174         D3DXVECTOR3 normal;
2175         D3DCOLOR diffuse;
2176         D3DXVECTOR2 tex_coords;
2177     } box_vertex_buffer[] = {
2178         {{0.0, 0.0, 0.0}, {-1.0, 0.0, 0.0}, 0x00000000, {0.0, 1.0}},
2179         {{0.0, 0.0, 1.0}, {-1.0, 0.0, 0.0}, 0x1a0000ff, {1.0, 1.0}},
2180         {{0.0, 1.0, 0.0}, {-1.0, 0.0, 0.0}, 0x3300ff00, {0.0, 0.0}},
2181         {{0.0, 1.0, 1.0}, {-1.0, 0.0, 0.0}, 0x4d00ffff, {1.0, 0.0}},
2182         {{1.0, 0.0, 0.0}, {1.0, 0.0, 0.0}, 0x66ff0000, {1.0, 1.0}},
2183         {{1.0, 0.0, 1.0}, {1.0, 0.0, 0.0}, 0x80ff00ff, {0.0, 1.0}},
2184         {{1.0, 1.0, 0.0}, {0.0, 1.0, 0.0}, 0x99ffff00, {1.0, 0.0}},
2185         {{1.0, 1.0, 1.0}, {0.0, 1.0, 0.0}, 0xb3ffffff, {0.0, 0.0}},
2186         {{0.0, 1.0, 0.0}, {0.0, 1.0, 0.0}, 0x3300ff00, {0.0, 0.0}},
2187         {{0.0, 1.0, 1.0}, {0.0, 1.0, 0.0}, 0x4d00ffff, {1.0, 0.0}},
2188         {{1.0, 1.0, 0.0}, {1.0, 0.0, 0.0}, 0x99ffff00, {1.0, 0.0}},
2189         {{1.0, 1.0, 1.0}, {1.0, 0.0, 0.0}, 0xb3ffffff, {0.0, 0.0}},
2190         {{0.0, 0.0, 1.0}, {0.0, -1.0, 0.0}, 0x1a0000ff, {1.0, 1.0}},
2191         {{0.0, 0.0, 0.0}, {0.0, -1.0, 0.0}, 0x00000000, {0.0, 1.0}},
2192         {{1.0, 0.0, 0.0}, {0.0, -1.0, 0.0}, 0x66ff0000, {1.0, 1.0}},
2193         {{1.0, 0.0, 1.0}, {0.0, -1.0, 0.0}, 0x80ff00ff, {0.0, 1.0}},
2194         {{0.0, 0.0, 1.0}, {0.0, 0.0, 1.0}, 0x1a0000ff, {1.0, 1.0}},
2195         {{1.0, 0.0, 1.0}, {0.0, 0.0, 1.0}, 0x80ff00ff, {0.0, 1.0}},
2196         {{1.0, 1.0, 1.0}, {0.0, 0.0, 1.0}, 0xb3ffffff, {0.0, 0.0}},
2197         {{0.0, 1.0, 1.0}, {0.0, 0.0, 1.0}, 0x4d00ffff, {1.0, 0.0}},
2198         {{0.0, 0.0, 0.0}, {0.0, 0.0, -1.0}, 0x00000000, {0.0, 1.0}},
2199         {{0.0, 1.0, 0.0}, {0.0, 0.0, -1.0}, 0x3300ff00, {0.0, 0.0}},
2200         {{1.0, 1.0, 0.0}, {0.0, 0.0, -1.0}, 0x99ffff00, {1.0, 0.0}},
2201         {{1.0, 0.0, 0.0}, {0.0, 0.0, -1.0}, 0x66ff0000, {1.0, 1.0}},
2202     };
2203     static const D3DXMATERIAL box_materials[] = {
2204         {
2205             {
2206                 {0.0, 0.0, 1.0, 1.0}, /* Diffuse */
2207                 {0.0, 0.0, 0.0, 1.0}, /* Ambient */
2208                 {1.0, 1.0, 1.0, 1.0}, /* Specular */
2209                 {0.0, 0.0, 0.0, 1.0}, /* Emissive */
2210                 0.5, /* Power */
2211             },
2212             NULL, /* pTextureFilename */
2213         },
2214         {
2215             {
2216                 {1.0, 1.0, 1.0, 1.0}, /* Diffuse */
2217                 {0.0, 0.0, 0.0, 1.0}, /* Ambient */
2218                 {1.0, 1.0, 1.0, 1.0}, /* Specular */
2219                 {0.0, 0.0, 0.0, 1.0}, /* Emissive */
2220                 1.0, /* Power */
2221             },
2222             (char *)"texture.jpg", /* pTextureFilename */
2223         },
2224     };
2225     const DWORD box_fvf = D3DFVF_XYZ | D3DFVF_NORMAL | D3DFVF_DIFFUSE | D3DFVF_TEX1;
2226     /*________________________*/
2227     static const D3DXMATERIAL default_materials[] = {
2228         {
2229             {
2230                 {0.5, 0.5, 0.5, 0.0}, /* Diffuse */
2231                 {0.0, 0.0, 0.0, 0.0}, /* Ambient */
2232                 {0.5, 0.5, 0.5, 0.0}, /* Specular */
2233                 {0.0, 0.0, 0.0, 0.0}, /* Emissive */
2234                 0.0, /* Power */
2235             },
2236             NULL, /* pTextureFilename */
2237         }
2238     };
2239     HRESULT hr;
2240     HWND wnd = NULL;
2241     IDirect3D9 *d3d = NULL;
2242     IDirect3DDevice9 *device = NULL;
2243     D3DPRESENT_PARAMETERS d3dpp;
2244     ID3DXMesh *mesh = NULL;
2245     D3DXFRAME *frame_hier = NULL;
2246     D3DXMATRIX transform;
2247
2248     wnd = CreateWindow("static", "d3dx9_test", WS_POPUP, 0, 0, 1000, 1000, NULL, NULL, NULL, NULL);
2249     if (!wnd)
2250     {
2251         skip("Couldn't create application window\n");
2252         return;
2253     }
2254     d3d = Direct3DCreate9(D3D_SDK_VERSION);
2255     if (!d3d)
2256     {
2257         skip("Couldn't create IDirect3D9 object\n");
2258         goto cleanup;
2259     }
2260
2261     ZeroMemory(&d3dpp, sizeof(d3dpp));
2262     d3dpp.Windowed = TRUE;
2263     d3dpp.SwapEffect = D3DSWAPEFFECT_DISCARD;
2264     hr = IDirect3D9_CreateDevice(d3d, D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, wnd, D3DCREATE_MIXED_VERTEXPROCESSING, &d3dpp, &device);
2265     if (FAILED(hr))
2266     {
2267         skip("Failed to create IDirect3DDevice9 object %#x\n", hr);
2268         goto cleanup;
2269     }
2270
2271     hr = D3DXLoadMeshHierarchyFromXInMemory(NULL, sizeof(simple_xfile) - 1,
2272             D3DXMESH_MANAGED, device, &alloc_hier, NULL, &frame_hier, NULL);
2273     ok(hr == D3DERR_INVALIDCALL, "Expected D3DERR_INVALIDCALL, got %#x\n", hr);
2274
2275     hr = D3DXLoadMeshHierarchyFromXInMemory(simple_xfile, 0,
2276             D3DXMESH_MANAGED, device, &alloc_hier, NULL, &frame_hier, NULL);
2277     ok(hr == D3DERR_INVALIDCALL, "Expected D3DERR_INVALIDCALL, got %#x\n", hr);
2278
2279     hr = D3DXLoadMeshHierarchyFromXInMemory(simple_xfile, sizeof(simple_xfile) - 1,
2280             D3DXMESH_MANAGED, NULL, &alloc_hier, NULL, &frame_hier, NULL);
2281     ok(hr == D3DERR_INVALIDCALL, "Expected D3DERR_INVALIDCALL, got %#x\n", hr);
2282
2283     hr = D3DXLoadMeshHierarchyFromXInMemory(simple_xfile, sizeof(simple_xfile) - 1,
2284             D3DXMESH_MANAGED, device, NULL, NULL, &frame_hier, NULL);
2285     ok(hr == D3DERR_INVALIDCALL, "Expected D3DERR_INVALIDCALL, got %#x\n", hr);
2286
2287     hr = D3DXLoadMeshHierarchyFromXInMemory(empty_xfile, sizeof(empty_xfile) - 1,
2288             D3DXMESH_MANAGED, device, &alloc_hier, NULL, &frame_hier, NULL);
2289     ok(hr == E_FAIL, "Expected E_FAIL, got %#x\n", hr);
2290
2291     hr = D3DXLoadMeshHierarchyFromXInMemory(simple_xfile, sizeof(simple_xfile) - 1,
2292             D3DXMESH_MANAGED, device, &alloc_hier, NULL, NULL, NULL);
2293     ok(hr == D3DERR_INVALIDCALL, "Expected D3DERR_INVALIDCALL, got %#x\n", hr);
2294
2295     hr = D3DXLoadMeshHierarchyFromXInMemory(simple_xfile, sizeof(simple_xfile) - 1,
2296             D3DXMESH_MANAGED, device, &alloc_hier, NULL, &frame_hier, NULL);
2297     ok(hr == D3D_OK, "Expected D3D_OK, got %#x\n", hr);
2298     if (SUCCEEDED(hr)) {
2299         D3DXMESHCONTAINER *container = frame_hier->pMeshContainer;
2300
2301         ok(frame_hier->Name == NULL, "Expected NULL, got '%s'\n", frame_hier->Name);
2302         D3DXMatrixIdentity(&transform);
2303         check_matrix(&frame_hier->TransformationMatrix, &transform);
2304
2305         ok(!strcmp(container->Name, ""), "Expected '', got '%s'\n", container->Name);
2306         ok(container->MeshData.Type == D3DXMESHTYPE_MESH, "Expected %d, got %d\n",
2307            D3DXMESHTYPE_MESH, container->MeshData.Type);
2308         mesh = U(container->MeshData).pMesh;
2309         check_vertex_buffer(mesh, simple_vertex_buffer, ARRAY_SIZE(simple_vertex_buffer), simple_fvf);
2310         check_index_buffer(mesh, simple_index_buffer, ARRAY_SIZE(simple_index_buffer), sizeof(*simple_index_buffer));
2311         check_materials(container->pMaterials, container->NumMaterials, NULL, 0);
2312         check_generated_effects(container->pMaterials, container->NumMaterials, container->pEffects);
2313         check_generated_adjacency(mesh, container->pAdjacency, 0.0f);
2314         hr = D3DXFrameDestroy(frame_hier, &alloc_hier);
2315         ok(hr == D3D_OK, "Expected D3D_OK, got %#x\n", hr);
2316         frame_hier = NULL;
2317     }
2318
2319     hr = D3DXLoadMeshHierarchyFromXInMemory(box_xfile, sizeof(box_xfile) - 1,
2320             D3DXMESH_MANAGED, device, &alloc_hier, NULL, &frame_hier, NULL);
2321     ok(hr == D3D_OK, "Expected D3D_OK, got %#x\n", hr);
2322     if (SUCCEEDED(hr)) {
2323         D3DXMESHCONTAINER *container = frame_hier->pMeshContainer;
2324
2325         ok(frame_hier->Name == NULL, "Expected NULL, got '%s'\n", frame_hier->Name);
2326         D3DXMatrixIdentity(&transform);
2327         check_matrix(&frame_hier->TransformationMatrix, &transform);
2328
2329         ok(!strcmp(container->Name, ""), "Expected '', got '%s'\n", container->Name);
2330         ok(container->MeshData.Type == D3DXMESHTYPE_MESH, "Expected %d, got %d\n",
2331            D3DXMESHTYPE_MESH, container->MeshData.Type);
2332         mesh = U(container->MeshData).pMesh;
2333         check_vertex_buffer(mesh, box_vertex_buffer, ARRAY_SIZE(box_vertex_buffer), box_fvf);
2334         check_index_buffer(mesh, box_index_buffer, ARRAY_SIZE(box_index_buffer), sizeof(*box_index_buffer));
2335         check_materials(container->pMaterials, container->NumMaterials, box_materials, ARRAY_SIZE(box_materials));
2336         check_generated_effects(container->pMaterials, container->NumMaterials, container->pEffects);
2337         check_generated_adjacency(mesh, container->pAdjacency, 0.0f);
2338         hr = D3DXFrameDestroy(frame_hier, &alloc_hier);
2339         ok(hr == D3D_OK, "Expected D3D_OK, got %#x\n", hr);
2340         frame_hier = NULL;
2341     }
2342
2343     hr = D3DXLoadMeshHierarchyFromXInMemory(framed_xfile, sizeof(framed_xfile) - 1,
2344             D3DXMESH_MANAGED, device, &alloc_hier, NULL, &frame_hier, NULL);
2345     ok(hr == D3D_OK, "Expected D3D_OK, got %#x\n", hr);
2346     if (SUCCEEDED(hr)) {
2347         D3DXMESHCONTAINER *container = frame_hier->pMeshContainer;
2348         int i;
2349
2350         ok(!strcmp(frame_hier->Name, ""), "Expected '', got '%s'\n", frame_hier->Name);
2351         /* last frame transform replaces the first */
2352         D3DXMatrixIdentity(&transform);
2353         U(transform).m[3][2] = 3.0;
2354         check_matrix(&frame_hier->TransformationMatrix, &transform);
2355
2356         for (i = 0; i < 3; i++) {
2357             ok(!strcmp(container->Name, ""), "Expected '', got '%s'\n", container->Name);
2358             ok(container->MeshData.Type == D3DXMESHTYPE_MESH, "Expected %d, got %d\n",
2359                D3DXMESHTYPE_MESH, container->MeshData.Type);
2360             mesh = U(container->MeshData).pMesh;
2361             check_vertex_buffer(mesh, framed_vertex_buffers[i], ARRAY_SIZE(framed_vertex_buffers[0]), framed_fvf);
2362             check_index_buffer(mesh, framed_index_buffer, ARRAY_SIZE(framed_index_buffer), sizeof(*framed_index_buffer));
2363             check_materials(container->pMaterials, container->NumMaterials, NULL, 0);
2364             check_generated_effects(container->pMaterials, container->NumMaterials, container->pEffects);
2365             check_generated_adjacency(mesh, container->pAdjacency, 0.0f);
2366             container = container->pNextMeshContainer;
2367         }
2368         ok(container == NULL, "Expected NULL, got %p\n", container);
2369         hr = D3DXFrameDestroy(frame_hier, &alloc_hier);
2370         ok(hr == D3D_OK, "Expected D3D_OK, got %#x\n", hr);
2371         frame_hier = NULL;
2372     }
2373
2374
2375     hr = D3DXLoadMeshFromXInMemory(NULL, 0, D3DXMESH_MANAGED,
2376                                    device, NULL, NULL, NULL, NULL, &mesh);
2377     ok(hr == D3DERR_INVALIDCALL, "Expected D3DERR_INVALIDCALL, got %#x\n", hr);
2378
2379     hr = D3DXLoadMeshFromXInMemory(NULL, sizeof(simple_xfile) - 1, D3DXMESH_MANAGED,
2380                                    device, NULL, NULL, NULL, NULL, &mesh);
2381     ok(hr == D3DERR_INVALIDCALL, "Expected D3DERR_INVALIDCALL, got %#x\n", hr);
2382
2383     hr = D3DXLoadMeshFromXInMemory(simple_xfile, 0, D3DXMESH_MANAGED,
2384                                    device, NULL, NULL, NULL, NULL, &mesh);
2385     ok(hr == D3DERR_INVALIDCALL, "Expected D3DERR_INVALIDCALL, got %#x\n", hr);
2386
2387     hr = D3DXLoadMeshFromXInMemory(simple_xfile, sizeof(simple_xfile) - 1, D3DXMESH_MANAGED,
2388                                    device, NULL, NULL, NULL, NULL, NULL);
2389     ok(hr == D3DERR_INVALIDCALL, "Expected D3DERR_INVALIDCALL, got %#x\n", hr);
2390
2391     hr = D3DXLoadMeshFromXInMemory(simple_xfile, sizeof(simple_xfile) - 1, D3DXMESH_MANAGED,
2392                                    NULL, NULL, NULL, NULL, NULL, &mesh);
2393     ok(hr == D3DERR_INVALIDCALL, "Expected D3DERR_INVALIDCALL, got %#x\n", hr);
2394
2395     hr = D3DXLoadMeshFromXInMemory(empty_xfile, sizeof(empty_xfile) - 1, D3DXMESH_MANAGED,
2396                                    device, NULL, NULL, NULL, NULL, &mesh);
2397     ok(hr == E_FAIL, "Expected E_FAIL, got %#x\n", hr);
2398
2399     hr = D3DXLoadMeshFromXInMemory(simple_xfile, sizeof(simple_xfile) - 1, D3DXMESH_MANAGED,
2400                                    device, NULL, NULL, NULL, NULL, &mesh);
2401     ok(hr == D3D_OK, "Expected D3D_OK, got %#x\n", hr);
2402     if (SUCCEEDED(hr))
2403         IUnknown_Release(mesh);
2404
2405     test_LoadMeshFromX(device, simple_xfile, simple_vertex_buffer, simple_fvf, simple_index_buffer, default_materials, TRUE);
2406     test_LoadMeshFromX(device, box_xfile, box_vertex_buffer, box_fvf, box_index_buffer, box_materials, TRUE);
2407     test_LoadMeshFromX(device, framed_xfile, merged_vertex_buffer, framed_fvf, merged_index_buffer, default_materials, FALSE);
2408
2409 cleanup:
2410     if (device) IDirect3DDevice9_Release(device);
2411     if (d3d) IDirect3D9_Release(d3d);
2412     if (wnd) DestroyWindow(wnd);
2413 }
2414
2415 static void D3DXCreateBoxTest(void)
2416 {
2417     HRESULT hr;
2418     HWND wnd;
2419     WNDCLASS wc={0};
2420     IDirect3D9* d3d;
2421     IDirect3DDevice9* device;
2422     D3DPRESENT_PARAMETERS d3dpp;
2423     ID3DXMesh* box;
2424     ID3DXBuffer* ppBuffer;
2425     DWORD *buffer;
2426     static const DWORD adjacency[36]=
2427         {6, 9, 1, 2, 10, 0,
2428          1, 9, 3, 4, 10, 2,
2429          3, 8, 5, 7, 11, 4,
2430          0, 11, 7, 5, 8, 6,
2431          7, 4, 9, 2, 0, 8,
2432          1, 3, 11, 5, 6, 10};
2433     unsigned int i;
2434
2435     wc.lpfnWndProc = DefWindowProcA;
2436     wc.lpszClassName = "d3dx9_test_wc";
2437     if (!RegisterClass(&wc))
2438     {
2439         skip("RegisterClass failed\n");
2440         return;
2441     }
2442
2443     wnd = CreateWindow("d3dx9_test_wc", "d3dx9_test",
2444                         WS_SYSMENU | WS_POPUP , 0, 0, 640, 480, 0, 0, 0, 0);
2445     ok(wnd != NULL, "Expected to have a window, received NULL\n");
2446     if (!wnd)
2447     {
2448         skip("Couldn't create application window\n");
2449         return;
2450     }
2451
2452     d3d = Direct3DCreate9(D3D_SDK_VERSION);
2453     if (!d3d)
2454     {
2455         skip("Couldn't create IDirect3D9 object\n");
2456         DestroyWindow(wnd);
2457         return;
2458     }
2459
2460     memset(&d3dpp, 0, sizeof(d3dpp));
2461     d3dpp.Windowed = TRUE;
2462     d3dpp.SwapEffect = D3DSWAPEFFECT_DISCARD;
2463     hr = IDirect3D9_CreateDevice(d3d, D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, wnd, D3DCREATE_MIXED_VERTEXPROCESSING, &d3dpp, &device);
2464     if (FAILED(hr))
2465     {
2466         skip("Failed to create IDirect3DDevice9 object %#x\n", hr);
2467         IDirect3D9_Release(d3d);
2468         DestroyWindow(wnd);
2469         return;
2470     }
2471
2472     hr = D3DXCreateBuffer(36 * sizeof(DWORD), &ppBuffer);
2473     ok(hr==D3D_OK, "Expected D3D_OK, received %#x\n", hr);
2474     if (FAILED(hr)) goto end;
2475
2476     hr = D3DXCreateBox(device,2.0f,20.0f,4.9f,NULL, &ppBuffer);
2477     todo_wine ok(hr==D3DERR_INVALIDCALL, "Expected D3DERR_INVALIDCALL, received %#x\n", hr);
2478
2479     hr = D3DXCreateBox(NULL,22.0f,20.0f,4.9f,&box, &ppBuffer);
2480     todo_wine ok(hr==D3DERR_INVALIDCALL, "Expected D3DERR_INVALIDCALL, received %#x\n", hr);
2481
2482     hr = D3DXCreateBox(device,-2.0f,20.0f,4.9f,&box, &ppBuffer);
2483     todo_wine ok(hr==D3DERR_INVALIDCALL, "Expected D3DERR_INVALIDCALL, received %#x\n", hr);
2484
2485     hr = D3DXCreateBox(device,22.0f,-20.0f,4.9f,&box, &ppBuffer);
2486     todo_wine ok(hr==D3DERR_INVALIDCALL, "Expected D3DERR_INVALIDCALL, received %#x\n", hr);
2487
2488     hr = D3DXCreateBox(device,22.0f,20.0f,-4.9f,&box, &ppBuffer);
2489     todo_wine ok(hr==D3DERR_INVALIDCALL, "Expected D3DERR_INVALIDCALL, received %#x\n", hr);
2490
2491     hr = D3DXCreateBox(device,10.9f,20.0f,4.9f,&box, &ppBuffer);
2492     todo_wine ok(hr==D3D_OK, "Expected D3D_OK, received %#x\n", hr);
2493
2494     if (FAILED(hr))
2495     {
2496         skip("D3DXCreateBox failed\n");
2497         goto end;
2498     }
2499
2500     buffer = ID3DXBuffer_GetBufferPointer(ppBuffer);
2501     for(i=0; i<36; i++)
2502         todo_wine ok(adjacency[i]==buffer[i], "expected adjacency %d: %#x, received %#x\n",i,adjacency[i], buffer[i]);
2503
2504     box->lpVtbl->Release(box);
2505
2506 end:
2507     IDirect3DDevice9_Release(device);
2508     IDirect3D9_Release(d3d);
2509     ID3DXBuffer_Release(ppBuffer);
2510     DestroyWindow(wnd);
2511 }
2512
2513 struct sincos_table
2514 {
2515     float *sin;
2516     float *cos;
2517 };
2518
2519 static void free_sincos_table(struct sincos_table *sincos_table)
2520 {
2521     HeapFree(GetProcessHeap(), 0, sincos_table->cos);
2522     HeapFree(GetProcessHeap(), 0, sincos_table->sin);
2523 }
2524
2525 /* pre compute sine and cosine tables; caller must free */
2526 static BOOL compute_sincos_table(struct sincos_table *sincos_table, float angle_start, float angle_step, int n)
2527 {
2528     float angle;
2529     int i;
2530
2531     sincos_table->sin = HeapAlloc(GetProcessHeap(), 0, n * sizeof(*sincos_table->sin));
2532     if (!sincos_table->sin)
2533     {
2534         return FALSE;
2535     }
2536     sincos_table->cos = HeapAlloc(GetProcessHeap(), 0, n * sizeof(*sincos_table->cos));
2537     if (!sincos_table->cos)
2538     {
2539         HeapFree(GetProcessHeap(), 0, sincos_table->sin);
2540         return FALSE;
2541     }
2542
2543     angle = angle_start;
2544     for (i = 0; i < n; i++)
2545     {
2546         sincos_table->sin[i] = sin(angle);
2547         sincos_table->cos[i] = cos(angle);
2548         angle += angle_step;
2549     }
2550
2551     return TRUE;
2552 }
2553
2554 static WORD vertex_index(UINT slices, int slice, int stack)
2555 {
2556     return stack*slices+slice+1;
2557 }
2558
2559 /* slices = subdivisions along xy plane, stacks = subdivisions along z axis */
2560 static BOOL compute_sphere(struct mesh *mesh, FLOAT radius, UINT slices, UINT stacks)
2561 {
2562     float theta_step, theta_start;
2563     struct sincos_table theta;
2564     float phi_step, phi_start;
2565     struct sincos_table phi;
2566     DWORD number_of_vertices, number_of_faces;
2567     DWORD vertex, face;
2568     int slice, stack;
2569
2570     /* theta = angle on xy plane wrt x axis */
2571     theta_step = M_PI / stacks;
2572     theta_start = theta_step;
2573
2574     /* phi = angle on xz plane wrt z axis */
2575     phi_step = -2 * M_PI / slices;
2576     phi_start = M_PI / 2;
2577
2578     if (!compute_sincos_table(&theta, theta_start, theta_step, stacks))
2579     {
2580         return FALSE;
2581     }
2582     if (!compute_sincos_table(&phi, phi_start, phi_step, slices))
2583     {
2584         free_sincos_table(&theta);
2585         return FALSE;
2586     }
2587
2588     number_of_vertices = 2 + slices * (stacks-1);
2589     number_of_faces = 2 * slices + (stacks - 2) * (2 * slices);
2590
2591     if (!new_mesh(mesh, number_of_vertices, number_of_faces))
2592     {
2593         free_sincos_table(&phi);
2594         free_sincos_table(&theta);
2595         return FALSE;
2596     }
2597
2598     vertex = 0;
2599     face = 0;
2600
2601     mesh->vertices[vertex].normal.x = 0.0f;
2602     mesh->vertices[vertex].normal.y = 0.0f;
2603     mesh->vertices[vertex].normal.z = 1.0f;
2604     mesh->vertices[vertex].position.x = 0.0f;
2605     mesh->vertices[vertex].position.y = 0.0f;
2606     mesh->vertices[vertex].position.z = radius;
2607     vertex++;
2608
2609     for (stack = 0; stack < stacks - 1; stack++)
2610     {
2611         for (slice = 0; slice < slices; slice++)
2612         {
2613             mesh->vertices[vertex].normal.x = theta.sin[stack] * phi.cos[slice];
2614             mesh->vertices[vertex].normal.y = theta.sin[stack] * phi.sin[slice];
2615             mesh->vertices[vertex].normal.z = theta.cos[stack];
2616             mesh->vertices[vertex].position.x = radius * theta.sin[stack] * phi.cos[slice];
2617             mesh->vertices[vertex].position.y = radius * theta.sin[stack] * phi.sin[slice];
2618             mesh->vertices[vertex].position.z = radius * theta.cos[stack];
2619             vertex++;
2620
2621             if (slice > 0)
2622             {
2623                 if (stack == 0)
2624                 {
2625                     /* top stack is triangle fan */
2626                     mesh->faces[face][0] = 0;
2627                     mesh->faces[face][1] = slice + 1;
2628                     mesh->faces[face][2] = slice;
2629                     face++;
2630                 }
2631                 else
2632                 {
2633                     /* stacks in between top and bottom are quad strips */
2634                     mesh->faces[face][0] = vertex_index(slices, slice-1, stack-1);
2635                     mesh->faces[face][1] = vertex_index(slices, slice, stack-1);
2636                     mesh->faces[face][2] = vertex_index(slices, slice-1, stack);
2637                     face++;
2638
2639                     mesh->faces[face][0] = vertex_index(slices, slice, stack-1);
2640                     mesh->faces[face][1] = vertex_index(slices, slice, stack);
2641                     mesh->faces[face][2] = vertex_index(slices, slice-1, stack);
2642                     face++;
2643                 }
2644             }
2645         }
2646
2647         if (stack == 0)
2648         {
2649             mesh->faces[face][0] = 0;
2650             mesh->faces[face][1] = 1;
2651             mesh->faces[face][2] = slice;
2652             face++;
2653         }
2654         else
2655         {
2656             mesh->faces[face][0] = vertex_index(slices, slice-1, stack-1);
2657             mesh->faces[face][1] = vertex_index(slices, 0, stack-1);
2658             mesh->faces[face][2] = vertex_index(slices, slice-1, stack);
2659             face++;
2660
2661             mesh->faces[face][0] = vertex_index(slices, 0, stack-1);
2662             mesh->faces[face][1] = vertex_index(slices, 0, stack);
2663             mesh->faces[face][2] = vertex_index(slices, slice-1, stack);
2664             face++;
2665         }
2666     }
2667
2668     mesh->vertices[vertex].position.x = 0.0f;
2669     mesh->vertices[vertex].position.y = 0.0f;
2670     mesh->vertices[vertex].position.z = -radius;
2671     mesh->vertices[vertex].normal.x = 0.0f;
2672     mesh->vertices[vertex].normal.y = 0.0f;
2673     mesh->vertices[vertex].normal.z = -1.0f;
2674
2675     /* bottom stack is triangle fan */
2676     for (slice = 1; slice < slices; slice++)
2677     {
2678         mesh->faces[face][0] = vertex_index(slices, slice-1, stack-1);
2679         mesh->faces[face][1] = vertex_index(slices, slice, stack-1);
2680         mesh->faces[face][2] = vertex;
2681         face++;
2682     }
2683
2684     mesh->faces[face][0] = vertex_index(slices, slice-1, stack-1);
2685     mesh->faces[face][1] = vertex_index(slices, 0, stack-1);
2686     mesh->faces[face][2] = vertex;
2687
2688     free_sincos_table(&phi);
2689     free_sincos_table(&theta);
2690
2691     return TRUE;
2692 }
2693
2694 static void test_sphere(IDirect3DDevice9 *device, FLOAT radius, UINT slices, UINT stacks)
2695 {
2696     HRESULT hr;
2697     ID3DXMesh *sphere;
2698     struct mesh mesh;
2699     char name[256];
2700
2701     hr = D3DXCreateSphere(device, radius, slices, stacks, &sphere, NULL);
2702     ok(hr == D3D_OK, "Got result %x, expected 0 (D3D_OK)\n", hr);
2703     if (hr != D3D_OK)
2704     {
2705         skip("Couldn't create sphere\n");
2706         return;
2707     }
2708
2709     if (!compute_sphere(&mesh, radius, slices, stacks))
2710     {
2711         skip("Couldn't create mesh\n");
2712         sphere->lpVtbl->Release(sphere);
2713         return;
2714     }
2715
2716     mesh.fvf = D3DFVF_XYZ | D3DFVF_NORMAL;
2717
2718     sprintf(name, "sphere (%g, %u, %u)", radius, slices, stacks);
2719     compare_mesh(name, sphere, &mesh);
2720
2721     free_mesh(&mesh);
2722
2723     sphere->lpVtbl->Release(sphere);
2724 }
2725
2726 static void D3DXCreateSphereTest(void)
2727 {
2728     HRESULT hr;
2729     HWND wnd;
2730     IDirect3D9* d3d;
2731     IDirect3DDevice9* device;
2732     D3DPRESENT_PARAMETERS d3dpp;
2733     ID3DXMesh* sphere = NULL;
2734
2735     hr = D3DXCreateSphere(NULL, 0.0f, 0, 0, NULL, NULL);
2736     ok(hr == D3DERR_INVALIDCALL, "Got result %x, expected %x (D3DERR_INVALIDCALL)\n",hr,D3DERR_INVALIDCALL);
2737
2738     hr = D3DXCreateSphere(NULL, 0.1f, 0, 0, NULL, NULL);
2739     ok(hr == D3DERR_INVALIDCALL, "Got result %x, expected %x (D3DERR_INVALIDCALL)\n",hr,D3DERR_INVALIDCALL);
2740
2741     hr = D3DXCreateSphere(NULL, 0.0f, 1, 0, NULL, NULL);
2742     ok(hr == D3DERR_INVALIDCALL, "Got result %x, expected %x (D3DERR_INVALIDCALL)\n",hr,D3DERR_INVALIDCALL);
2743
2744     hr = D3DXCreateSphere(NULL, 0.0f, 0, 1, NULL, NULL);
2745     ok(hr == D3DERR_INVALIDCALL, "Got result %x, expected %x (D3DERR_INVALIDCALL)\n",hr,D3DERR_INVALIDCALL);
2746
2747     wnd = CreateWindow("static", "d3dx9_test", 0, 0, 0, 0, 0, NULL, NULL, NULL, NULL);
2748     d3d = Direct3DCreate9(D3D_SDK_VERSION);
2749     if (!wnd)
2750     {
2751         skip("Couldn't create application window\n");
2752         return;
2753     }
2754     if (!d3d)
2755     {
2756         skip("Couldn't create IDirect3D9 object\n");
2757         DestroyWindow(wnd);
2758         return;
2759     }
2760
2761     ZeroMemory(&d3dpp, sizeof(d3dpp));
2762     d3dpp.Windowed = TRUE;
2763     d3dpp.SwapEffect = D3DSWAPEFFECT_DISCARD;
2764     hr = IDirect3D9_CreateDevice(d3d, D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, wnd, D3DCREATE_MIXED_VERTEXPROCESSING, &d3dpp, &device);
2765     if (FAILED(hr))
2766     {
2767         skip("Failed to create IDirect3DDevice9 object %#x\n", hr);
2768         IDirect3D9_Release(d3d);
2769         DestroyWindow(wnd);
2770         return;
2771     }
2772
2773     hr = D3DXCreateSphere(device, 1.0f, 1, 1, &sphere, NULL);
2774     ok(hr == D3DERR_INVALIDCALL, "Got result %x, expected %x (D3DERR_INVALIDCALL)\n",hr,D3DERR_INVALIDCALL);
2775
2776     hr = D3DXCreateSphere(device, 1.0f, 2, 1, &sphere, NULL);
2777     ok(hr == D3DERR_INVALIDCALL, "Got result %x, expected %x (D3DERR_INVALIDCALL)\n", hr, D3DERR_INVALIDCALL);
2778
2779     hr = D3DXCreateSphere(device, 1.0f, 1, 2, &sphere, NULL);
2780     ok(hr == D3DERR_INVALIDCALL, "Got result %x, expected %x (D3DERR_INVALIDCALL)\n", hr, D3DERR_INVALIDCALL);
2781
2782     hr = D3DXCreateSphere(device, -0.1f, 1, 2, &sphere, NULL);
2783     ok(hr == D3DERR_INVALIDCALL, "Got result %x, expected %x (D3DERR_INVALIDCALL)\n", hr, D3DERR_INVALIDCALL);
2784
2785     test_sphere(device, 0.0f, 2, 2);
2786     test_sphere(device, 1.0f, 2, 2);
2787     test_sphere(device, 1.0f, 3, 2);
2788     test_sphere(device, 1.0f, 4, 4);
2789     test_sphere(device, 1.0f, 3, 4);
2790     test_sphere(device, 5.0f, 6, 7);
2791     test_sphere(device, 10.0f, 11, 12);
2792
2793     IDirect3DDevice9_Release(device);
2794     IDirect3D9_Release(d3d);
2795     DestroyWindow(wnd);
2796 }
2797
2798 static BOOL compute_cylinder(struct mesh *mesh, FLOAT radius1, FLOAT radius2, FLOAT length, UINT slices, UINT stacks)
2799 {
2800     float theta_step, theta_start;
2801     struct sincos_table theta;
2802     FLOAT delta_radius, radius, radius_step;
2803     FLOAT z, z_step, z_normal;
2804     DWORD number_of_vertices, number_of_faces;
2805     DWORD vertex, face;
2806     int slice, stack;
2807
2808     /* theta = angle on xy plane wrt x axis */
2809     theta_step = -2 * M_PI / slices;
2810     theta_start = M_PI / 2;
2811
2812     if (!compute_sincos_table(&theta, theta_start, theta_step, slices))
2813     {
2814         return FALSE;
2815     }
2816
2817     number_of_vertices = 2 + (slices * (3 + stacks));
2818     number_of_faces = 2 * slices + stacks * (2 * slices);
2819
2820     if (!new_mesh(mesh, number_of_vertices, number_of_faces))
2821     {
2822         free_sincos_table(&theta);
2823         return FALSE;
2824     }
2825
2826     vertex = 0;
2827     face = 0;
2828
2829     delta_radius = radius1 - radius2;
2830     radius = radius1;
2831     radius_step = delta_radius / stacks;
2832
2833     z = -length / 2;
2834     z_step = length / stacks;
2835     z_normal = delta_radius / length;
2836     if (isnan(z_normal))
2837     {
2838         z_normal = 0.0f;
2839     }
2840
2841     mesh->vertices[vertex].normal.x = 0.0f;
2842     mesh->vertices[vertex].normal.y = 0.0f;
2843     mesh->vertices[vertex].normal.z = -1.0f;
2844     mesh->vertices[vertex].position.x = 0.0f;
2845     mesh->vertices[vertex].position.y = 0.0f;
2846     mesh->vertices[vertex++].position.z = z;
2847
2848     for (slice = 0; slice < slices; slice++, vertex++)
2849     {
2850         mesh->vertices[vertex].normal.x = 0.0f;
2851         mesh->vertices[vertex].normal.y = 0.0f;
2852         mesh->vertices[vertex].normal.z = -1.0f;
2853         mesh->vertices[vertex].position.x = radius * theta.cos[slice];
2854         mesh->vertices[vertex].position.y = radius * theta.sin[slice];
2855         mesh->vertices[vertex].position.z = z;
2856
2857         if (slice > 0)
2858         {
2859             mesh->faces[face][0] = 0;
2860             mesh->faces[face][1] = slice;
2861             mesh->faces[face++][2] = slice + 1;
2862         }
2863     }
2864
2865     mesh->faces[face][0] = 0;
2866     mesh->faces[face][1] = slice;
2867     mesh->faces[face++][2] = 1;
2868
2869     for (stack = 1; stack <= stacks+1; stack++)
2870     {
2871         for (slice = 0; slice < slices; slice++, vertex++)
2872         {
2873             mesh->vertices[vertex].normal.x = theta.cos[slice];
2874             mesh->vertices[vertex].normal.y = theta.sin[slice];
2875             mesh->vertices[vertex].normal.z = z_normal;
2876             D3DXVec3Normalize(&mesh->vertices[vertex].normal, &mesh->vertices[vertex].normal);
2877             mesh->vertices[vertex].position.x = radius * theta.cos[slice];
2878             mesh->vertices[vertex].position.y = radius * theta.sin[slice];
2879             mesh->vertices[vertex].position.z = z;
2880
2881             if (stack > 1 && slice > 0)
2882             {
2883                 mesh->faces[face][0] = vertex_index(slices, slice-1, stack-1);
2884                 mesh->faces[face][1] = vertex_index(slices, slice-1, stack);
2885                 mesh->faces[face++][2] = vertex_index(slices, slice, stack-1);
2886
2887                 mesh->faces[face][0] = vertex_index(slices, slice, stack-1);
2888                 mesh->faces[face][1] = vertex_index(slices, slice-1, stack);
2889                 mesh->faces[face++][2] = vertex_index(slices, slice, stack);
2890             }
2891         }
2892
2893         if (stack > 1)
2894         {
2895             mesh->faces[face][0] = vertex_index(slices, slice-1, stack-1);
2896             mesh->faces[face][1] = vertex_index(slices, slice-1, stack);
2897             mesh->faces[face++][2] = vertex_index(slices, 0, stack-1);
2898
2899             mesh->faces[face][0] = vertex_index(slices, 0, stack-1);
2900             mesh->faces[face][1] = vertex_index(slices, slice-1, stack);
2901             mesh->faces[face++][2] = vertex_index(slices, 0, stack);
2902         }
2903
2904         if (stack < stacks + 1)
2905         {
2906             z += z_step;
2907             radius -= radius_step;
2908         }
2909     }
2910
2911     for (slice = 0; slice < slices; slice++, vertex++)
2912     {
2913         mesh->vertices[vertex].normal.x = 0.0f;
2914         mesh->vertices[vertex].normal.y = 0.0f;
2915         mesh->vertices[vertex].normal.z = 1.0f;
2916         mesh->vertices[vertex].position.x = radius * theta.cos[slice];
2917         mesh->vertices[vertex].position.y = radius * theta.sin[slice];
2918         mesh->vertices[vertex].position.z = z;
2919
2920         if (slice > 0)
2921         {
2922             mesh->faces[face][0] = vertex_index(slices, slice-1, stack);
2923             mesh->faces[face][1] = number_of_vertices - 1;
2924             mesh->faces[face++][2] = vertex_index(slices, slice, stack);
2925         }
2926     }
2927
2928     mesh->vertices[vertex].position.x = 0.0f;
2929     mesh->vertices[vertex].position.y = 0.0f;
2930     mesh->vertices[vertex].position.z = z;
2931     mesh->vertices[vertex].normal.x = 0.0f;
2932     mesh->vertices[vertex].normal.y = 0.0f;
2933     mesh->vertices[vertex].normal.z = 1.0f;
2934
2935     mesh->faces[face][0] = vertex_index(slices, slice-1, stack);
2936     mesh->faces[face][1] = number_of_vertices - 1;
2937     mesh->faces[face][2] = vertex_index(slices, 0, stack);
2938
2939     free_sincos_table(&theta);
2940
2941     return TRUE;
2942 }
2943
2944 static void test_cylinder(IDirect3DDevice9 *device, FLOAT radius1, FLOAT radius2, FLOAT length, UINT slices, UINT stacks)
2945 {
2946     HRESULT hr;
2947     ID3DXMesh *cylinder;
2948     struct mesh mesh;
2949     char name[256];
2950
2951     hr = D3DXCreateCylinder(device, radius1, radius2, length, slices, stacks, &cylinder, NULL);
2952     ok(hr == D3D_OK, "Got result %x, expected 0 (D3D_OK)\n", hr);
2953     if (hr != D3D_OK)
2954     {
2955         skip("Couldn't create cylinder\n");
2956         return;
2957     }
2958
2959     if (!compute_cylinder(&mesh, radius1, radius2, length, slices, stacks))
2960     {
2961         skip("Couldn't create mesh\n");
2962         cylinder->lpVtbl->Release(cylinder);
2963         return;
2964     }
2965
2966     mesh.fvf = D3DFVF_XYZ | D3DFVF_NORMAL;
2967
2968     sprintf(name, "cylinder (%g, %g, %g, %u, %u)", radius1, radius2, length, slices, stacks);
2969     compare_mesh(name, cylinder, &mesh);
2970
2971     free_mesh(&mesh);
2972
2973     cylinder->lpVtbl->Release(cylinder);
2974 }
2975
2976 static void D3DXCreateCylinderTest(void)
2977 {
2978     HRESULT hr;
2979     HWND wnd;
2980     IDirect3D9* d3d;
2981     IDirect3DDevice9* device;
2982     D3DPRESENT_PARAMETERS d3dpp;
2983     ID3DXMesh* cylinder = NULL;
2984
2985     hr = D3DXCreateCylinder(NULL, 0.0f, 0.0f, 0.0f, 0, 0, NULL, NULL);
2986     ok(hr == D3DERR_INVALIDCALL, "Got result %x, expected %x (D3DERR_INVALIDCALL)\n",hr,D3DERR_INVALIDCALL);
2987
2988     hr = D3DXCreateCylinder(NULL, 1.0f, 1.0f, 1.0f, 2, 1, &cylinder, NULL);
2989     ok(hr == D3DERR_INVALIDCALL, "Got result %x, expected %x (D3DERR_INVALIDCALL)\n",hr,D3DERR_INVALIDCALL);
2990
2991     wnd = CreateWindow("static", "d3dx9_test", 0, 0, 0, 0, 0, NULL, NULL, NULL, NULL);
2992     d3d = Direct3DCreate9(D3D_SDK_VERSION);
2993     if (!wnd)
2994     {
2995         skip("Couldn't create application window\n");
2996         return;
2997     }
2998     if (!d3d)
2999     {
3000         skip("Couldn't create IDirect3D9 object\n");
3001         DestroyWindow(wnd);
3002         return;
3003     }
3004
3005     ZeroMemory(&d3dpp, sizeof(d3dpp));
3006     d3dpp.Windowed = TRUE;
3007     d3dpp.SwapEffect = D3DSWAPEFFECT_DISCARD;
3008     hr = IDirect3D9_CreateDevice(d3d, D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, wnd, D3DCREATE_MIXED_VERTEXPROCESSING, &d3dpp, &device);
3009     if (FAILED(hr))
3010     {
3011         skip("Failed to create IDirect3DDevice9 object %#x\n", hr);
3012         IDirect3D9_Release(d3d);
3013         DestroyWindow(wnd);
3014         return;
3015     }
3016
3017     hr = D3DXCreateCylinder(device, -0.1f, 1.0f, 1.0f, 2, 1, &cylinder, NULL);
3018     ok(hr == D3DERR_INVALIDCALL, "Got result %x, expected %x (D3DERR_INVALIDCALL)\n",hr,D3DERR_INVALIDCALL);
3019
3020     hr = D3DXCreateCylinder(device, 0.0f, 1.0f, 1.0f, 2, 1, &cylinder, NULL);
3021     ok(hr == D3D_OK, "Got result %x, expected 0 (D3D_OK)\n",hr);
3022
3023     if (SUCCEEDED(hr) && cylinder)
3024     {
3025         cylinder->lpVtbl->Release(cylinder);
3026     }
3027
3028     hr = D3DXCreateCylinder(device, 1.0f, -0.1f, 1.0f, 2, 1, &cylinder, NULL);
3029     ok(hr == D3DERR_INVALIDCALL, "Got result %x, expected %x (D3DERR_INVALIDCALL)\n",hr,D3DERR_INVALIDCALL);
3030
3031     hr = D3DXCreateCylinder(device, 1.0f, 0.0f, 1.0f, 2, 1, &cylinder, NULL);
3032     ok(hr == D3D_OK, "Got result %x, expected 0 (D3D_OK)\n",hr);
3033
3034     if (SUCCEEDED(hr) && cylinder)
3035     {
3036         cylinder->lpVtbl->Release(cylinder);
3037     }
3038
3039     hr = D3DXCreateCylinder(device, 1.0f, 1.0f, -0.1f, 2, 1, &cylinder, NULL);
3040     ok(hr == D3DERR_INVALIDCALL, "Got result %x, expected %x (D3DERR_INVALIDCALL)\n",hr,D3DERR_INVALIDCALL);
3041
3042     /* Test with length == 0.0f succeeds */
3043     hr = D3DXCreateCylinder(device, 1.0f, 1.0f, 0.0f, 2, 1, &cylinder, NULL);
3044     ok(hr == D3D_OK, "Got result %x, expected 0 (D3D_OK)\n",hr);
3045
3046     if (SUCCEEDED(hr) && cylinder)
3047     {
3048         cylinder->lpVtbl->Release(cylinder);
3049     }
3050
3051     hr = D3DXCreateCylinder(device, 1.0f, 1.0f, 1.0f, 1, 1, &cylinder, NULL);
3052     ok(hr == D3DERR_INVALIDCALL, "Got result %x, expected %x (D3DERR_INVALIDCALL)\n",hr,D3DERR_INVALIDCALL);
3053
3054     hr = D3DXCreateCylinder(device, 1.0f, 1.0f, 1.0f, 2, 0, &cylinder, NULL);
3055     ok(hr == D3DERR_INVALIDCALL, "Got result %x, expected %x (D3DERR_INVALIDCALL)\n",hr,D3DERR_INVALIDCALL);
3056
3057     hr = D3DXCreateCylinder(device, 1.0f, 1.0f, 1.0f, 2, 1, NULL, NULL);
3058     ok(hr == D3DERR_INVALIDCALL, "Got result %x, expected %x (D3DERR_INVALIDCALL)\n",hr,D3DERR_INVALIDCALL);
3059
3060     test_cylinder(device, 0.0f, 0.0f, 0.0f, 2, 1);
3061     test_cylinder(device, 1.0f, 1.0f, 1.0f, 2, 1);
3062     test_cylinder(device, 1.0f, 1.0f, 2.0f, 3, 4);
3063     test_cylinder(device, 3.0f, 2.0f, 4.0f, 3, 4);
3064     test_cylinder(device, 2.0f, 3.0f, 4.0f, 3, 4);
3065     test_cylinder(device, 3.0f, 4.0f, 5.0f, 11, 20);
3066
3067     IDirect3DDevice9_Release(device);
3068     IDirect3D9_Release(d3d);
3069     DestroyWindow(wnd);
3070 }
3071
3072 struct dynamic_array
3073 {
3074     int count, capacity;
3075     void *items;
3076 };
3077
3078 enum pointtype {
3079     POINTTYPE_CURVE = 0,
3080     POINTTYPE_CORNER,
3081     POINTTYPE_CURVE_START,
3082     POINTTYPE_CURVE_END,
3083     POINTTYPE_CURVE_MIDDLE,
3084 };
3085
3086 struct point2d
3087 {
3088     D3DXVECTOR2 pos;
3089     enum pointtype corner;
3090 };
3091
3092 /* is a dynamic_array */
3093 struct outline
3094 {
3095     int count, capacity;
3096     struct point2d *items;
3097 };
3098
3099 /* is a dynamic_array */
3100 struct outline_array
3101 {
3102     int count, capacity;
3103     struct outline *items;
3104 };
3105
3106 struct glyphinfo
3107 {
3108     struct outline_array outlines;
3109     float offset_x;
3110 };
3111
3112 static BOOL reserve(struct dynamic_array *array, int count, int itemsize)
3113 {
3114     if (count > array->capacity) {
3115         void *new_buffer;
3116         int new_capacity;
3117         if (array->items && array->capacity) {
3118             new_capacity = max(array->capacity * 2, count);
3119             new_buffer = HeapReAlloc(GetProcessHeap(), 0, array->items, new_capacity * itemsize);
3120         } else {
3121             new_capacity = max(16, count);
3122             new_buffer = HeapAlloc(GetProcessHeap(), 0, new_capacity * itemsize);
3123         }
3124         if (!new_buffer)
3125             return FALSE;
3126         array->items = new_buffer;
3127         array->capacity = new_capacity;
3128     }
3129     return TRUE;
3130 }
3131
3132 static struct point2d *add_point(struct outline *array)
3133 {
3134     struct point2d *item;
3135
3136     if (!reserve((struct dynamic_array *)array, array->count + 1, sizeof(array->items[0])))
3137         return NULL;
3138
3139     item = &array->items[array->count++];
3140     ZeroMemory(item, sizeof(*item));
3141     return item;
3142 }
3143
3144 static struct outline *add_outline(struct outline_array *array)
3145 {
3146     struct outline *item;
3147
3148     if (!reserve((struct dynamic_array *)array, array->count + 1, sizeof(array->items[0])))
3149         return NULL;
3150
3151     item = &array->items[array->count++];
3152     ZeroMemory(item, sizeof(*item));
3153     return item;
3154 }
3155
3156 static inline D3DXVECTOR2 *convert_fixed_to_float(POINTFX *pt, int count, float emsquare)
3157 {
3158     D3DXVECTOR2 *ret = (D3DXVECTOR2*)pt;
3159     while (count--) {
3160         D3DXVECTOR2 *pt_flt = (D3DXVECTOR2*)pt;
3161         pt_flt->x = (pt->x.value + pt->x.fract / (float)0x10000) / emsquare;
3162         pt_flt->y = (pt->y.value + pt->y.fract / (float)0x10000) / emsquare;
3163         pt++;
3164     }
3165     return ret;
3166 }
3167
3168 static HRESULT add_bezier_points(struct outline *outline, const D3DXVECTOR2 *p1,
3169                                  const D3DXVECTOR2 *p2, const D3DXVECTOR2 *p3,
3170                                  float max_deviation)
3171 {
3172     D3DXVECTOR2 split1 = {0, 0}, split2 = {0, 0}, middle, vec;
3173     float deviation;
3174
3175     D3DXVec2Scale(&split1, D3DXVec2Add(&split1, p1, p2), 0.5f);
3176     D3DXVec2Scale(&split2, D3DXVec2Add(&split2, p2, p3), 0.5f);
3177     D3DXVec2Scale(&middle, D3DXVec2Add(&middle, &split1, &split2), 0.5f);
3178
3179     deviation = D3DXVec2Length(D3DXVec2Subtract(&vec, &middle, p2));
3180     if (deviation < max_deviation) {
3181         struct point2d *pt = add_point(outline);
3182         if (!pt) return E_OUTOFMEMORY;
3183         pt->pos = *p2;
3184         pt->corner = POINTTYPE_CURVE;
3185         /* the end point is omitted because the end line merges into the next segment of
3186          * the split bezier curve, and the end of the split bezier curve is added outside
3187          * this recursive function. */
3188     } else {
3189         HRESULT hr = add_bezier_points(outline, p1, &split1, &middle, max_deviation);
3190         if (hr != S_OK) return hr;
3191         hr = add_bezier_points(outline, &middle, &split2, p3, max_deviation);
3192         if (hr != S_OK) return hr;
3193     }
3194
3195     return S_OK;
3196 }
3197
3198 static inline BOOL is_direction_similar(D3DXVECTOR2 *dir1, D3DXVECTOR2 *dir2, float cos_theta)
3199 {
3200     /* dot product = cos(theta) */
3201     return D3DXVec2Dot(dir1, dir2) > cos_theta;
3202 }
3203
3204 static inline D3DXVECTOR2 *unit_vec2(D3DXVECTOR2 *dir, const D3DXVECTOR2 *pt1, const D3DXVECTOR2 *pt2)
3205 {
3206     return D3DXVec2Normalize(D3DXVec2Subtract(dir, pt2, pt1), dir);
3207 }
3208
3209 static BOOL attempt_line_merge(struct outline *outline,
3210                                int pt_index,
3211                                const D3DXVECTOR2 *nextpt,
3212                                BOOL to_curve)
3213 {
3214     D3DXVECTOR2 curdir, lastdir;
3215     struct point2d *prevpt, *pt;
3216     BOOL ret = FALSE;
3217     const float cos_half = cos(D3DXToRadian(0.5f));
3218
3219     pt = &outline->items[pt_index];
3220     pt_index = (pt_index - 1 + outline->count) % outline->count;
3221     prevpt = &outline->items[pt_index];
3222
3223     if (to_curve)
3224         pt->corner = pt->corner != POINTTYPE_CORNER ? POINTTYPE_CURVE_MIDDLE : POINTTYPE_CURVE_START;
3225
3226     if (outline->count < 2)
3227         return FALSE;
3228
3229     /* remove last point if the next line continues the last line */
3230     unit_vec2(&lastdir, &prevpt->pos, &pt->pos);
3231     unit_vec2(&curdir, &pt->pos, nextpt);
3232     if (is_direction_similar(&lastdir, &curdir, cos_half))
3233     {
3234         outline->count--;
3235         if (pt->corner == POINTTYPE_CURVE_END)
3236             prevpt->corner = pt->corner;
3237         if (prevpt->corner == POINTTYPE_CURVE_END && to_curve)
3238             prevpt->corner = POINTTYPE_CURVE_MIDDLE;
3239         pt = prevpt;
3240
3241         ret = TRUE;
3242         if (outline->count < 2)
3243             return ret;
3244
3245         pt_index = (pt_index - 1 + outline->count) % outline->count;
3246         prevpt = &outline->items[pt_index];
3247         unit_vec2(&lastdir, &prevpt->pos, &pt->pos);
3248         unit_vec2(&curdir, &pt->pos, nextpt);
3249     }
3250     return ret;
3251 }
3252
3253 static HRESULT create_outline(struct glyphinfo *glyph, void *raw_outline, int datasize,
3254                               float max_deviation, float emsquare)
3255 {
3256     const float cos_45 = cos(D3DXToRadian(45.0f));
3257     const float cos_90 = cos(D3DXToRadian(90.0f));
3258     TTPOLYGONHEADER *header = (TTPOLYGONHEADER *)raw_outline;
3259
3260     while ((char *)header < (char *)raw_outline + datasize)
3261     {
3262         TTPOLYCURVE *curve = (TTPOLYCURVE *)(header + 1);
3263         struct point2d *lastpt, *pt;
3264         D3DXVECTOR2 lastdir;
3265         D3DXVECTOR2 *pt_flt;
3266         int j;
3267         struct outline *outline = add_outline(&glyph->outlines);
3268
3269         if (!outline)
3270             return E_OUTOFMEMORY;
3271
3272         pt = add_point(outline);
3273         if (!pt)
3274             return E_OUTOFMEMORY;
3275         pt_flt = convert_fixed_to_float(&header->pfxStart, 1, emsquare);
3276         pt->pos = *pt_flt;
3277         pt->corner = POINTTYPE_CORNER;
3278
3279         if (header->dwType != TT_POLYGON_TYPE)
3280             trace("Unknown header type %d\n", header->dwType);
3281
3282         while ((char *)curve < (char *)header + header->cb)
3283         {
3284             D3DXVECTOR2 bezier_start = outline->items[outline->count - 1].pos;
3285             BOOL to_curve = curve->wType != TT_PRIM_LINE && curve->cpfx > 1;
3286
3287             if (!curve->cpfx) {
3288                 curve = (TTPOLYCURVE *)&curve->apfx[curve->cpfx];
3289                 continue;
3290             }
3291
3292             pt_flt = convert_fixed_to_float(curve->apfx, curve->cpfx, emsquare);
3293
3294             attempt_line_merge(outline, outline->count - 1, &pt_flt[0], to_curve);
3295
3296             if (to_curve)
3297             {
3298                 HRESULT hr;
3299                 int count = curve->cpfx;
3300                 j = 0;
3301
3302                 while (count > 2)
3303                 {
3304                     D3DXVECTOR2 bezier_end;
3305
3306                     D3DXVec2Scale(&bezier_end, D3DXVec2Add(&bezier_end, &pt_flt[j], &pt_flt[j+1]), 0.5f);
3307                     hr = add_bezier_points(outline, &bezier_start, &pt_flt[j], &bezier_end, max_deviation);
3308                     if (hr != S_OK)
3309                         return hr;
3310                     bezier_start = bezier_end;
3311                     count--;
3312                     j++;
3313                 }
3314                 hr = add_bezier_points(outline, &bezier_start, &pt_flt[j], &pt_flt[j+1], max_deviation);
3315                 if (hr != S_OK)
3316                     return hr;
3317
3318                 pt = add_point(outline);
3319                 if (!pt)
3320                     return E_OUTOFMEMORY;
3321                 j++;
3322                 pt->pos = pt_flt[j];
3323                 pt->corner = POINTTYPE_CURVE_END;
3324             } else {
3325                 for (j = 0; j < curve->cpfx; j++)
3326                 {
3327                     pt = add_point(outline);
3328                     if (!pt)
3329                         return E_OUTOFMEMORY;
3330                     pt->pos = pt_flt[j];
3331                     pt->corner = POINTTYPE_CORNER;
3332                 }
3333             }
3334
3335             curve = (TTPOLYCURVE *)&curve->apfx[curve->cpfx];
3336         }
3337
3338         /* remove last point if the next line continues the last line */
3339         if (outline->count >= 3) {
3340             BOOL to_curve;
3341
3342             lastpt = &outline->items[outline->count - 1];
3343             pt = &outline->items[0];
3344             if (pt->pos.x == lastpt->pos.x && pt->pos.y == lastpt->pos.y) {
3345                 if (lastpt->corner == POINTTYPE_CURVE_END)
3346                 {
3347                     if (pt->corner == POINTTYPE_CURVE_START)
3348                         pt->corner = POINTTYPE_CURVE_MIDDLE;
3349                     else
3350                         pt->corner = POINTTYPE_CURVE_END;
3351                 }
3352                 outline->count--;
3353                 lastpt = &outline->items[outline->count - 1];
3354             } else {
3355                 /* outline closed with a line from end to start point */
3356                 attempt_line_merge(outline, outline->count - 1, &pt->pos, FALSE);
3357             }
3358             lastpt = &outline->items[0];
3359             to_curve = lastpt->corner != POINTTYPE_CORNER && lastpt->corner != POINTTYPE_CURVE_END;
3360             if (lastpt->corner == POINTTYPE_CURVE_START)
3361                 lastpt->corner = POINTTYPE_CORNER;
3362             pt = &outline->items[1];
3363             if (attempt_line_merge(outline, 0, &pt->pos, to_curve))
3364                 *lastpt = outline->items[outline->count];
3365         }
3366
3367         lastpt = &outline->items[outline->count - 1];
3368         pt = &outline->items[0];
3369         unit_vec2(&lastdir, &lastpt->pos, &pt->pos);
3370         for (j = 0; j < outline->count; j++)
3371         {
3372             D3DXVECTOR2 curdir;
3373
3374             lastpt = pt;
3375             pt = &outline->items[(j + 1) % outline->count];
3376             unit_vec2(&curdir, &lastpt->pos, &pt->pos);
3377
3378             switch (lastpt->corner)
3379             {
3380                 case POINTTYPE_CURVE_START:
3381                 case POINTTYPE_CURVE_END:
3382                     if (!is_direction_similar(&lastdir, &curdir, cos_45))
3383                         lastpt->corner = POINTTYPE_CORNER;
3384                     break;
3385                 case POINTTYPE_CURVE_MIDDLE:
3386                     if (!is_direction_similar(&lastdir, &curdir, cos_90))
3387                         lastpt->corner = POINTTYPE_CORNER;
3388                     else
3389                         lastpt->corner = POINTTYPE_CURVE;
3390                     break;
3391                 default:
3392                     break;
3393             }
3394             lastdir = curdir;
3395         }
3396
3397         header = (TTPOLYGONHEADER *)((char *)header + header->cb);
3398     }
3399     return S_OK;
3400 }
3401
3402 static BOOL compute_text_mesh(struct mesh *mesh, HDC hdc, LPCSTR text, FLOAT deviation, FLOAT extrusion, FLOAT otmEMSquare)
3403 {
3404     HRESULT hr = E_FAIL;
3405     DWORD nb_vertices, nb_faces;
3406     DWORD nb_corners, nb_outline_points;
3407     int textlen = 0;
3408     float offset_x;
3409     char *raw_outline = NULL;
3410     struct glyphinfo *glyphs = NULL;
3411     GLYPHMETRICS gm;
3412     int i;
3413     struct vertex *vertex_ptr;
3414     face *face_ptr;
3415
3416     if (deviation == 0.0f)
3417         deviation = 1.0f / otmEMSquare;
3418
3419     textlen = strlen(text);
3420     glyphs = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, textlen * sizeof(*glyphs));
3421     if (!glyphs) {
3422         hr = E_OUTOFMEMORY;
3423         goto error;
3424     }
3425
3426     offset_x = 0.0f;
3427     for (i = 0; i < textlen; i++)
3428     {
3429         /* get outline points from data returned from GetGlyphOutline */
3430         const MAT2 identity = {{0, 1}, {0, 0}, {0, 0}, {0, 1}};
3431         int datasize;
3432
3433         glyphs[i].offset_x = offset_x;
3434
3435         datasize = GetGlyphOutline(hdc, text[i], GGO_NATIVE, &gm, 0, NULL, &identity);
3436         if (datasize < 0) {
3437             hr = E_FAIL;
3438             goto error;
3439         }
3440         HeapFree(GetProcessHeap(), 0, raw_outline);
3441         raw_outline = HeapAlloc(GetProcessHeap(), 0, datasize);
3442         if (!glyphs) {
3443             hr = E_OUTOFMEMORY;
3444             goto error;
3445         }
3446         datasize = GetGlyphOutline(hdc, text[i], GGO_NATIVE, &gm, datasize, raw_outline, &identity);
3447
3448         create_outline(&glyphs[i], raw_outline, datasize, deviation, otmEMSquare);
3449
3450         offset_x += gm.gmCellIncX / (float)otmEMSquare;
3451     }
3452
3453     /* corner points need an extra vertex for the different side faces normals */
3454     nb_corners = 0;
3455     nb_outline_points = 0;
3456     for (i = 0; i < textlen; i++)
3457     {
3458         int j;
3459         for (j = 0; j < glyphs[i].outlines.count; j++)
3460         {
3461             int k;
3462             struct outline *outline = &glyphs[i].outlines.items[j];
3463             nb_outline_points += outline->count;
3464             nb_corners++; /* first outline point always repeated as a corner */
3465             for (k = 1; k < outline->count; k++)
3466                 if (outline->items[k].corner)
3467                     nb_corners++;
3468         }
3469     }
3470
3471     nb_vertices = (nb_outline_points + nb_corners) * 2 + textlen;
3472     nb_faces = nb_outline_points * 2;
3473
3474     if (!new_mesh(mesh, nb_vertices, nb_faces))
3475         goto error;
3476
3477     /* convert 2D vertices and faces into 3D mesh */
3478     vertex_ptr = mesh->vertices;
3479     face_ptr = mesh->faces;
3480     for (i = 0; i < textlen; i++)
3481     {
3482         int j;
3483
3484         /* side vertices and faces */
3485         for (j = 0; j < glyphs[i].outlines.count; j++)
3486         {
3487             struct vertex *outline_vertices = vertex_ptr;
3488             struct outline *outline = &glyphs[i].outlines.items[j];
3489             int k;
3490             struct point2d *prevpt = &outline->items[outline->count - 1];
3491             struct point2d *pt = &outline->items[0];
3492
3493             for (k = 1; k <= outline->count; k++)
3494             {
3495                 struct vertex vtx;
3496                 struct point2d *nextpt = &outline->items[k % outline->count];
3497                 WORD vtx_idx = vertex_ptr - mesh->vertices;
3498                 D3DXVECTOR2 vec;
3499
3500                 if (pt->corner == POINTTYPE_CURVE_START)
3501                     D3DXVec2Subtract(&vec, &pt->pos, &prevpt->pos);
3502                 else if (pt->corner)
3503                     D3DXVec2Subtract(&vec, &nextpt->pos, &pt->pos);
3504                 else
3505                     D3DXVec2Subtract(&vec, &nextpt->pos, &prevpt->pos);
3506                 D3DXVec2Normalize(&vec, &vec);
3507                 vtx.normal.x = -vec.y;
3508                 vtx.normal.y = vec.x;
3509                 vtx.normal.z = 0;
3510
3511                 vtx.position.x = pt->pos.x + glyphs[i].offset_x;
3512                 vtx.position.y = pt->pos.y;
3513                 vtx.position.z = 0;
3514                 *vertex_ptr++ = vtx;
3515
3516                 vtx.position.z = -extrusion;
3517                 *vertex_ptr++ = vtx;
3518
3519                 vtx.position.x = nextpt->pos.x + glyphs[i].offset_x;
3520                 vtx.position.y = nextpt->pos.y;
3521                 if (pt->corner && nextpt->corner && nextpt->corner != POINTTYPE_CURVE_END) {
3522                     vtx.position.z = -extrusion;
3523                     *vertex_ptr++ = vtx;
3524                     vtx.position.z = 0;
3525                     *vertex_ptr++ = vtx;
3526
3527                     (*face_ptr)[0] = vtx_idx;
3528                     (*face_ptr)[1] = vtx_idx + 2;
3529                     (*face_ptr)[2] = vtx_idx + 1;
3530                     face_ptr++;
3531
3532                     (*face_ptr)[0] = vtx_idx;
3533                     (*face_ptr)[1] = vtx_idx + 3;
3534                     (*face_ptr)[2] = vtx_idx + 2;
3535                     face_ptr++;
3536                 } else {
3537                     if (nextpt->corner) {
3538                         if (nextpt->corner == POINTTYPE_CURVE_END) {
3539                             struct point2d *nextpt2 = &outline->items[(k + 1) % outline->count];
3540                             D3DXVec2Subtract(&vec, &nextpt2->pos, &nextpt->pos);
3541                         } else {
3542                             D3DXVec2Subtract(&vec, &nextpt->pos, &pt->pos);
3543                         }
3544                         D3DXVec2Normalize(&vec, &vec);
3545                         vtx.normal.x = -vec.y;
3546                         vtx.normal.y = vec.x;
3547
3548                         vtx.position.z = 0;
3549                         *vertex_ptr++ = vtx;
3550                         vtx.position.z = -extrusion;
3551                         *vertex_ptr++ = vtx;
3552                     }
3553
3554                     (*face_ptr)[0] = vtx_idx;
3555                     (*face_ptr)[1] = vtx_idx + 3;
3556                     (*face_ptr)[2] = vtx_idx + 1;
3557                     face_ptr++;
3558
3559                     (*face_ptr)[0] = vtx_idx;
3560                     (*face_ptr)[1] = vtx_idx + 2;
3561                     (*face_ptr)[2] = vtx_idx + 3;
3562                     face_ptr++;
3563                 }
3564
3565                 prevpt = pt;
3566                 pt = nextpt;
3567             }
3568             if (!pt->corner) {
3569                 *vertex_ptr++ = *outline_vertices++;
3570                 *vertex_ptr++ = *outline_vertices++;
3571             }
3572         }
3573
3574         /* FIXME: compute expected faces */
3575         /* Add placeholder to separate glyph outlines */
3576         vertex_ptr->position.x = 0;
3577         vertex_ptr->position.y = 0;
3578         vertex_ptr->position.z = 0;
3579         vertex_ptr->normal.x = 0;
3580         vertex_ptr->normal.y = 0;
3581         vertex_ptr->normal.z = 1;
3582         vertex_ptr++;
3583     }
3584
3585     hr = D3D_OK;
3586 error:
3587     if (glyphs) {
3588         for (i = 0; i < textlen; i++)
3589         {
3590             int j;
3591             for (j = 0; j < glyphs[i].outlines.count; j++)
3592                 HeapFree(GetProcessHeap(), 0, glyphs[i].outlines.items[j].items);
3593             HeapFree(GetProcessHeap(), 0, glyphs[i].outlines.items);
3594         }
3595         HeapFree(GetProcessHeap(), 0, glyphs);
3596     }
3597     HeapFree(GetProcessHeap(), 0, raw_outline);
3598
3599     return hr == D3D_OK;
3600 }
3601
3602 static void compare_text_outline_mesh(const char *name, ID3DXMesh *d3dxmesh, struct mesh *mesh, int textlen, float extrusion)
3603 {
3604     HRESULT hr;
3605     DWORD number_of_vertices, number_of_faces;
3606     IDirect3DVertexBuffer9 *vertex_buffer = NULL;
3607     IDirect3DIndexBuffer9 *index_buffer = NULL;
3608     D3DVERTEXBUFFER_DESC vertex_buffer_description;
3609     D3DINDEXBUFFER_DESC index_buffer_description;
3610     struct vertex *vertices = NULL;
3611     face *faces = NULL;
3612     int expected, i;
3613     int vtx_idx1, face_idx1, vtx_idx2, face_idx2;
3614
3615     number_of_vertices = d3dxmesh->lpVtbl->GetNumVertices(d3dxmesh);
3616     number_of_faces = d3dxmesh->lpVtbl->GetNumFaces(d3dxmesh);
3617
3618     /* vertex buffer */
3619     hr = d3dxmesh->lpVtbl->GetVertexBuffer(d3dxmesh, &vertex_buffer);
3620     ok(hr == D3D_OK, "Test %s, result %x, expected 0 (D3D_OK)\n", name, hr);
3621     if (hr != D3D_OK)
3622     {
3623         skip("Couldn't get vertex buffers\n");
3624         goto error;
3625     }
3626
3627     hr = IDirect3DVertexBuffer9_GetDesc(vertex_buffer, &vertex_buffer_description);
3628     ok(hr == D3D_OK, "Test %s, result %x, expected 0 (D3D_OK)\n", name, hr);
3629
3630     if (hr != D3D_OK)
3631     {
3632         skip("Couldn't get vertex buffer description\n");
3633     }
3634     else
3635     {
3636         ok(vertex_buffer_description.Format == D3DFMT_VERTEXDATA, "Test %s, result %x, expected %x (D3DFMT_VERTEXDATA)\n",
3637            name, vertex_buffer_description.Format, D3DFMT_VERTEXDATA);
3638         ok(vertex_buffer_description.Type == D3DRTYPE_VERTEXBUFFER, "Test %s, result %x, expected %x (D3DRTYPE_VERTEXBUFFER)\n",
3639            name, vertex_buffer_description.Type, D3DRTYPE_VERTEXBUFFER);
3640         ok(vertex_buffer_description.Usage == 0, "Test %s, result %x, expected %x\n", name, vertex_buffer_description.Usage, 0);
3641         ok(vertex_buffer_description.Pool == D3DPOOL_MANAGED, "Test %s, result %x, expected %x (D3DPOOL_MANAGED)\n",
3642            name, vertex_buffer_description.Pool, D3DPOOL_MANAGED);
3643         ok(vertex_buffer_description.FVF == mesh->fvf, "Test %s, result %x, expected %x\n",
3644            name, vertex_buffer_description.FVF, mesh->fvf);
3645         if (mesh->fvf == 0)
3646         {
3647             expected = number_of_vertices * mesh->vertex_size;
3648         }
3649         else
3650         {
3651             expected = number_of_vertices * D3DXGetFVFVertexSize(mesh->fvf);
3652         }
3653         ok(vertex_buffer_description.Size == expected, "Test %s, result %x, expected %x\n",
3654            name, vertex_buffer_description.Size, expected);
3655     }
3656
3657     hr = d3dxmesh->lpVtbl->GetIndexBuffer(d3dxmesh, &index_buffer);
3658     ok(hr == D3D_OK, "Test %s, result %x, expected 0 (D3D_OK)\n", name, hr);
3659     if (hr != D3D_OK)
3660     {
3661         skip("Couldn't get index buffer\n");
3662         goto error;
3663     }
3664
3665     hr = IDirect3DIndexBuffer9_GetDesc(index_buffer, &index_buffer_description);
3666     ok(hr == D3D_OK, "Test %s, result %x, expected 0 (D3D_OK)\n", name, hr);
3667
3668     if (hr != D3D_OK)
3669     {
3670         skip("Couldn't get index buffer description\n");
3671     }
3672     else
3673     {
3674         ok(index_buffer_description.Format == D3DFMT_INDEX16, "Test %s, result %x, expected %x (D3DFMT_INDEX16)\n",
3675            name, index_buffer_description.Format, D3DFMT_INDEX16);
3676         ok(index_buffer_description.Type == D3DRTYPE_INDEXBUFFER, "Test %s, result %x, expected %x (D3DRTYPE_INDEXBUFFER)\n",
3677            name, index_buffer_description.Type, D3DRTYPE_INDEXBUFFER);
3678         todo_wine ok(index_buffer_description.Usage == 0, "Test %s, result %x, expected %x\n", name, index_buffer_description.Usage, 0);
3679         ok(index_buffer_description.Pool == D3DPOOL_MANAGED, "Test %s, result %x, expected %x (D3DPOOL_MANAGED)\n",
3680            name, index_buffer_description.Pool, D3DPOOL_MANAGED);
3681         expected = number_of_faces * sizeof(WORD) * 3;
3682         ok(index_buffer_description.Size == expected, "Test %s, result %x, expected %x\n",
3683            name, index_buffer_description.Size, expected);
3684     }
3685
3686     /* specify offset and size to avoid potential overruns */
3687     hr = IDirect3DVertexBuffer9_Lock(vertex_buffer, 0, number_of_vertices * sizeof(D3DXVECTOR3) * 2,
3688                                      (LPVOID *)&vertices, D3DLOCK_DISCARD);
3689     ok(hr == D3D_OK, "Test %s, result %x, expected 0 (D3D_OK)\n", name, hr);
3690     if (hr != D3D_OK)
3691     {
3692         skip("Couldn't lock vertex buffer\n");
3693         goto error;
3694     }
3695     hr = IDirect3DIndexBuffer9_Lock(index_buffer, 0, number_of_faces * sizeof(WORD) * 3,
3696                                     (LPVOID *)&faces, D3DLOCK_DISCARD);
3697     ok(hr == D3D_OK, "Test %s, result %x, expected 0 (D3D_OK)\n", name, hr);
3698     if (hr != D3D_OK)
3699     {
3700         skip("Couldn't lock index buffer\n");
3701         goto error;
3702     }
3703
3704     face_idx1 = 0;
3705     vtx_idx2 = 0;
3706     face_idx2 = 0;
3707     vtx_idx1 = 0;
3708     for (i = 0; i < textlen; i++)
3709     {
3710         int nb_outline_vertices1, nb_outline_faces1;
3711         int nb_outline_vertices2, nb_outline_faces2;
3712         int nb_back_vertices, nb_back_faces;
3713         int first_vtx1, first_vtx2;
3714         int first_face1, first_face2;
3715         int j;
3716
3717         first_vtx1 = vtx_idx1;
3718         first_vtx2 = vtx_idx2;
3719         for (; vtx_idx1 < number_of_vertices; vtx_idx1++) {
3720             if (vertices[vtx_idx1].normal.z != 0)
3721                 break;
3722         }
3723         for (; vtx_idx2 < mesh->number_of_vertices; vtx_idx2++) {
3724             if (mesh->vertices[vtx_idx2].normal.z != 0)
3725                 break;
3726         }
3727         nb_outline_vertices1 = vtx_idx1 - first_vtx1;
3728         nb_outline_vertices2 = vtx_idx2 - first_vtx2;
3729         ok(nb_outline_vertices1 == nb_outline_vertices2,
3730            "Test %s, glyph %d, outline vertex count result %d, expected %d\n", name, i,
3731            nb_outline_vertices1, nb_outline_vertices2);
3732
3733         for (j = 0; j < min(nb_outline_vertices1, nb_outline_vertices2); j++)
3734         {
3735             vtx_idx1 = first_vtx1 + j;
3736             vtx_idx2 = first_vtx2 + j;
3737             ok(compare_vec3(vertices[vtx_idx1].position, mesh->vertices[vtx_idx2].position),
3738                "Test %s, glyph %d, vertex position %d, result (%g, %g, %g), expected (%g, %g, %g)\n", name, i, vtx_idx1,
3739                vertices[vtx_idx1].position.x, vertices[vtx_idx1].position.y, vertices[vtx_idx1].position.z,
3740                mesh->vertices[vtx_idx2].position.x, mesh->vertices[vtx_idx2].position.y, mesh->vertices[vtx_idx2].position.z);
3741             ok(compare_vec3(vertices[vtx_idx1].normal, mesh->vertices[first_vtx2 + j].normal),
3742                "Test %s, glyph %d, vertex normal %d, result (%g, %g, %g), expected (%g, %g, %g)\n", name, i, vtx_idx1,
3743                vertices[vtx_idx1].normal.x, vertices[vtx_idx1].normal.y, vertices[vtx_idx1].normal.z,
3744                mesh->vertices[vtx_idx2].normal.x, mesh->vertices[vtx_idx2].normal.y, mesh->vertices[vtx_idx2].normal.z);
3745         }
3746         vtx_idx1 = first_vtx1 + nb_outline_vertices1;
3747         vtx_idx2 = first_vtx2 + nb_outline_vertices2;
3748
3749         first_face1 = face_idx1;
3750         first_face2 = face_idx2;
3751         for (; face_idx1 < number_of_faces; face_idx1++)
3752         {
3753             if (faces[face_idx1][0] >= vtx_idx1 ||
3754                 faces[face_idx1][1] >= vtx_idx1 ||
3755                 faces[face_idx1][2] >= vtx_idx1)
3756                 break;
3757         }
3758         for (; face_idx2 < mesh->number_of_faces; face_idx2++)
3759         {
3760             if (mesh->faces[face_idx2][0] >= vtx_idx2 ||
3761                 mesh->faces[face_idx2][1] >= vtx_idx2 ||
3762                 mesh->faces[face_idx2][2] >= vtx_idx2)
3763                 break;
3764         }
3765         nb_outline_faces1 = face_idx1 - first_face1;
3766         nb_outline_faces2 = face_idx2 - first_face2;
3767         ok(nb_outline_faces1 == nb_outline_faces2,
3768            "Test %s, glyph %d, outline face count result %d, expected %d\n", name, i,
3769            nb_outline_faces1, nb_outline_faces2);
3770
3771         for (j = 0; j < min(nb_outline_faces1, nb_outline_faces2); j++)
3772         {
3773             face_idx1 = first_face1 + j;
3774             face_idx2 = first_face2 + j;
3775             ok(faces[face_idx1][0] - first_vtx1 == mesh->faces[face_idx2][0] - first_vtx2 &&
3776                faces[face_idx1][1] - first_vtx1 == mesh->faces[face_idx2][1] - first_vtx2 &&
3777                faces[face_idx1][2] - first_vtx1 == mesh->faces[face_idx2][2] - first_vtx2,
3778                "Test %s, glyph %d, face %d, result (%d, %d, %d), expected (%d, %d, %d)\n", name, i, face_idx1,
3779                faces[face_idx1][0], faces[face_idx1][1], faces[face_idx1][2],
3780                mesh->faces[face_idx2][0] - first_vtx2 + first_vtx1,
3781                mesh->faces[face_idx2][1] - first_vtx2 + first_vtx1,
3782                mesh->faces[face_idx2][2] - first_vtx2 + first_vtx1);
3783         }
3784         face_idx1 = first_face1 + nb_outline_faces1;
3785         face_idx2 = first_face2 + nb_outline_faces2;
3786
3787         /* partial test on back vertices and faces  */
3788         first_vtx1 = vtx_idx1;
3789         for (; vtx_idx1 < number_of_vertices; vtx_idx1++) {
3790             struct vertex vtx;
3791
3792             if (vertices[vtx_idx1].normal.z != 1.0f)
3793                 break;
3794
3795             vtx.position.z = 0.0f;
3796             vtx.normal.x = 0.0f;
3797             vtx.normal.y = 0.0f;
3798             vtx.normal.z = 1.0f;
3799             ok(compare(vertices[vtx_idx1].position.z, vtx.position.z),
3800                "Test %s, glyph %d, vertex position.z %d, result %g, expected %g\n", name, i, vtx_idx1,
3801                vertices[vtx_idx1].position.z, vtx.position.z);
3802             ok(compare_vec3(vertices[vtx_idx1].normal, vtx.normal),
3803                "Test %s, glyph %d, vertex normal %d, result (%g, %g, %g), expected (%g, %g, %g)\n", name, i, vtx_idx1,
3804                vertices[vtx_idx1].normal.x, vertices[vtx_idx1].normal.y, vertices[vtx_idx1].normal.z,
3805                vtx.normal.x, vtx.normal.y, vtx.normal.z);
3806         }
3807         nb_back_vertices = vtx_idx1 - first_vtx1;
3808         first_face1 = face_idx1;
3809         for (; face_idx1 < number_of_faces; face_idx1++)
3810         {
3811             const D3DXVECTOR3 *vtx1, *vtx2, *vtx3;
3812             D3DXVECTOR3 normal;
3813             D3DXVECTOR3 v1 = {0, 0, 0};
3814             D3DXVECTOR3 v2 = {0, 0, 0};
3815             D3DXVECTOR3 forward = {0.0f, 0.0f, 1.0f};
3816
3817             if (faces[face_idx1][0] >= vtx_idx1 ||
3818                 faces[face_idx1][1] >= vtx_idx1 ||
3819                 faces[face_idx1][2] >= vtx_idx1)
3820                 break;
3821
3822             vtx1 = &vertices[faces[face_idx1][0]].position;
3823             vtx2 = &vertices[faces[face_idx1][1]].position;
3824             vtx3 = &vertices[faces[face_idx1][2]].position;
3825
3826             D3DXVec3Subtract(&v1, vtx2, vtx1);
3827             D3DXVec3Subtract(&v2, vtx3, vtx2);
3828             D3DXVec3Cross(&normal, &v1, &v2);
3829             D3DXVec3Normalize(&normal, &normal);
3830             ok(!D3DXVec3Length(&normal) || compare_vec3(normal, forward),
3831                "Test %s, glyph %d, face %d normal, result (%g, %g, %g), expected (%g, %g, %g)\n", name, i, face_idx1,
3832                normal.x, normal.y, normal.z, forward.x, forward.y, forward.z);
3833         }
3834         nb_back_faces = face_idx1 - first_face1;
3835
3836         /* compare front and back faces & vertices */
3837         if (extrusion == 0.0f) {
3838             /* Oddly there are only back faces in this case */
3839             nb_back_vertices /= 2;
3840             nb_back_faces /= 2;
3841             face_idx1 -= nb_back_faces;
3842             vtx_idx1 -= nb_back_vertices;
3843         }
3844         for (j = 0; j < nb_back_vertices; j++)
3845         {
3846             struct vertex vtx = vertices[first_vtx1];
3847             vtx.position.z = -extrusion;
3848             vtx.normal.x = 0.0f;
3849             vtx.normal.y = 0.0f;
3850             vtx.normal.z = extrusion == 0.0f ? 1.0f : -1.0f;
3851             ok(compare_vec3(vertices[vtx_idx1].position, vtx.position),
3852                "Test %s, glyph %d, vertex position %d, result (%g, %g, %g), expected (%g, %g, %g)\n", name, i, vtx_idx1,
3853                vertices[vtx_idx1].position.x, vertices[vtx_idx1].position.y, vertices[vtx_idx1].position.z,
3854                vtx.position.x, vtx.position.y, vtx.position.z);
3855             ok(compare_vec3(vertices[vtx_idx1].normal, vtx.normal),
3856                "Test %s, glyph %d, vertex normal %d, result (%g, %g, %g), expected (%g, %g, %g)\n", name, i, vtx_idx1,
3857                vertices[vtx_idx1].normal.x, vertices[vtx_idx1].normal.y, vertices[vtx_idx1].normal.z,
3858                vtx.normal.x, vtx.normal.y, vtx.normal.z);
3859             vtx_idx1++;
3860             first_vtx1++;
3861         }
3862         for (j = 0; j < nb_back_faces; j++)
3863         {
3864             int f1, f2;
3865             if (extrusion == 0.0f) {
3866                 f1 = 1;
3867                 f2 = 2;
3868             } else {
3869                 f1 = 2;
3870                 f2 = 1;
3871             }
3872             ok(faces[face_idx1][0] == faces[first_face1][0] + nb_back_vertices &&
3873                faces[face_idx1][1] == faces[first_face1][f1] + nb_back_vertices &&
3874                faces[face_idx1][2] == faces[first_face1][f2] + nb_back_vertices,
3875                "Test %s, glyph %d, face %d, result (%d, %d, %d), expected (%d, %d, %d)\n", name, i, face_idx1,
3876                faces[face_idx1][0], faces[face_idx1][1], faces[face_idx1][2],
3877                faces[first_face1][0] - nb_back_faces,
3878                faces[first_face1][f1] - nb_back_faces,
3879                faces[first_face1][f2] - nb_back_faces);
3880             first_face1++;
3881             face_idx1++;
3882         }
3883
3884         /* skip to the outline for the next glyph */
3885         for (; vtx_idx2 < mesh->number_of_vertices; vtx_idx2++) {
3886             if (mesh->vertices[vtx_idx2].normal.z == 0)
3887                 break;
3888         }
3889         for (; face_idx2 < mesh->number_of_faces; face_idx2++)
3890         {
3891             if (mesh->faces[face_idx2][0] >= vtx_idx2 ||
3892                 mesh->faces[face_idx2][1] >= vtx_idx2 ||
3893                 mesh->faces[face_idx2][2] >= vtx_idx2) break;
3894         }
3895     }
3896
3897 error:
3898     if (vertices) IDirect3DVertexBuffer9_Unlock(vertex_buffer);
3899     if (faces) IDirect3DIndexBuffer9_Unlock(index_buffer);
3900     if (index_buffer) IDirect3DIndexBuffer9_Release(index_buffer);
3901     if (vertex_buffer) IDirect3DVertexBuffer9_Release(vertex_buffer);
3902 }
3903
3904 static void test_createtext(IDirect3DDevice9 *device, HDC hdc, LPCSTR text, FLOAT deviation, FLOAT extrusion)
3905 {
3906     HRESULT hr;
3907     ID3DXMesh *d3dxmesh;
3908     struct mesh mesh;
3909     char name[256];
3910     OUTLINETEXTMETRIC otm;
3911     GLYPHMETRICS gm;
3912     GLYPHMETRICSFLOAT *glyphmetrics_float = HeapAlloc(GetProcessHeap(), 0, sizeof(GLYPHMETRICSFLOAT) * strlen(text));
3913     int i;
3914     LOGFONT lf;
3915     HFONT font = NULL, oldfont = NULL;
3916
3917     sprintf(name, "text ('%s', %f, %f)", text, deviation, extrusion);
3918
3919     hr = D3DXCreateText(device, hdc, text, deviation, extrusion, &d3dxmesh, NULL, glyphmetrics_float);
3920     ok(hr == D3D_OK, "Got result %x, expected 0 (D3D_OK)\n", hr);
3921     if (hr != D3D_OK)
3922     {
3923         skip("Couldn't create text with D3DXCreateText\n");
3924         return;
3925     }
3926
3927     /* must select a modified font having lfHeight = otm.otmEMSquare before
3928      * calling GetGlyphOutline to get the expected values */
3929     if (!GetObject(GetCurrentObject(hdc, OBJ_FONT), sizeof(lf), &lf) ||
3930         !GetOutlineTextMetrics(hdc, sizeof(otm), &otm))
3931     {
3932         d3dxmesh->lpVtbl->Release(d3dxmesh);
3933         skip("Couldn't get text outline\n");
3934         return;
3935     }
3936     lf.lfHeight = otm.otmEMSquare;
3937     lf.lfWidth = 0;
3938     font = CreateFontIndirect(&lf);
3939     if (!font) {
3940         d3dxmesh->lpVtbl->Release(d3dxmesh);
3941         skip("Couldn't create the modified font\n");
3942         return;
3943     }
3944     oldfont = SelectObject(hdc, font);
3945
3946     for (i = 0; i < strlen(text); i++)
3947     {
3948         const MAT2 identity = {{0, 1}, {0, 0}, {0, 0}, {0, 1}};
3949         GetGlyphOutlineA(hdc, text[i], GGO_NATIVE, &gm, 0, NULL, &identity);
3950         compare_float(glyphmetrics_float[i].gmfBlackBoxX, gm.gmBlackBoxX / (float)otm.otmEMSquare);
3951         compare_float(glyphmetrics_float[i].gmfBlackBoxY, gm.gmBlackBoxY / (float)otm.otmEMSquare);
3952         compare_float(glyphmetrics_float[i].gmfptGlyphOrigin.x, gm.gmptGlyphOrigin.x / (float)otm.otmEMSquare);
3953         compare_float(glyphmetrics_float[i].gmfptGlyphOrigin.y, gm.gmptGlyphOrigin.y / (float)otm.otmEMSquare);
3954         compare_float(glyphmetrics_float[i].gmfCellIncX, gm.gmCellIncX / (float)otm.otmEMSquare);
3955         compare_float(glyphmetrics_float[i].gmfCellIncY, gm.gmCellIncY / (float)otm.otmEMSquare);
3956     }
3957
3958     ZeroMemory(&mesh, sizeof(mesh));
3959     if (!compute_text_mesh(&mesh, hdc, text, deviation, extrusion, otm.otmEMSquare))
3960     {
3961         skip("Couldn't create mesh\n");
3962         d3dxmesh->lpVtbl->Release(d3dxmesh);
3963         return;
3964     }
3965     mesh.fvf = D3DFVF_XYZ | D3DFVF_NORMAL;
3966
3967     compare_text_outline_mesh(name, d3dxmesh, &mesh, strlen(text), extrusion);
3968
3969     free_mesh(&mesh);
3970
3971     d3dxmesh->lpVtbl->Release(d3dxmesh);
3972     SelectObject(hdc, oldfont);
3973     HeapFree(GetProcessHeap(), 0, glyphmetrics_float);
3974 }
3975
3976 static void D3DXCreateTextTest(void)
3977 {
3978     HRESULT hr;
3979     HWND wnd;
3980     HDC hdc;
3981     IDirect3D9* d3d;
3982     IDirect3DDevice9* device;
3983     D3DPRESENT_PARAMETERS d3dpp;
3984     ID3DXMesh* d3dxmesh = NULL;
3985     HFONT hFont;
3986     OUTLINETEXTMETRIC otm;
3987     int number_of_vertices;
3988     int number_of_faces;
3989
3990     wnd = CreateWindow("static", "d3dx9_test", WS_POPUP, 0, 0, 1000, 1000, NULL, NULL, NULL, NULL);
3991     d3d = Direct3DCreate9(D3D_SDK_VERSION);
3992     if (!wnd)
3993     {
3994         skip("Couldn't create application window\n");
3995         return;
3996     }
3997     if (!d3d)
3998     {
3999         skip("Couldn't create IDirect3D9 object\n");
4000         DestroyWindow(wnd);
4001         return;
4002     }
4003
4004     ZeroMemory(&d3dpp, sizeof(d3dpp));
4005     d3dpp.Windowed = TRUE;
4006     d3dpp.SwapEffect = D3DSWAPEFFECT_DISCARD;
4007     hr = IDirect3D9_CreateDevice(d3d, D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, wnd, D3DCREATE_MIXED_VERTEXPROCESSING, &d3dpp, &device);
4008     if (FAILED(hr))
4009     {
4010         skip("Failed to create IDirect3DDevice9 object %#x\n", hr);
4011         IDirect3D9_Release(d3d);
4012         DestroyWindow(wnd);
4013         return;
4014     }
4015
4016     hdc = CreateCompatibleDC(NULL);
4017
4018     hFont = CreateFont(12, 0, 0, 0, FW_NORMAL, FALSE, FALSE, FALSE, DEFAULT_CHARSET,
4019                        OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, DEFAULT_PITCH | FF_DONTCARE,
4020                        "Arial");
4021     SelectObject(hdc, hFont);
4022     GetOutlineTextMetrics(hdc, sizeof(otm), &otm);
4023
4024     hr = D3DXCreateText(device, hdc, "wine", 0.001f, 0.4f, NULL, NULL, NULL);
4025     ok(hr == D3DERR_INVALIDCALL, "Got result %x, expected %x (D3DERR_INVALIDCALL)\n", hr, D3DERR_INVALIDCALL);
4026
4027     /* D3DXCreateTextA page faults from passing NULL text */
4028
4029     hr = D3DXCreateTextW(device, hdc, NULL, 0.001f, 0.4f, &d3dxmesh, NULL, NULL);
4030     ok(hr == D3DERR_INVALIDCALL, "Got result %x, expected %x (D3DERR_INVALIDCALL)\n", hr, D3DERR_INVALIDCALL);
4031
4032     hr = D3DXCreateText(device, hdc, "", 0.001f, 0.4f, &d3dxmesh, NULL, NULL);
4033     ok(hr == D3DERR_INVALIDCALL, "Got result %x, expected %x (D3DERR_INVALIDCALL)\n", hr, D3DERR_INVALIDCALL);
4034
4035     hr = D3DXCreateText(device, hdc, " ", 0.001f, 0.4f, &d3dxmesh, NULL, NULL);
4036     ok(hr == D3DERR_INVALIDCALL, "Got result %x, expected %x (D3DERR_INVALIDCALL)\n", hr, D3DERR_INVALIDCALL);
4037
4038     hr = D3DXCreateText(NULL, hdc, "wine", 0.001f, 0.4f, &d3dxmesh, NULL, NULL);
4039     ok(hr == D3DERR_INVALIDCALL, "Got result %x, expected %x (D3DERR_INVALIDCALL)\n", hr, D3DERR_INVALIDCALL);
4040
4041     hr = D3DXCreateText(device, NULL, "wine", 0.001f, 0.4f, &d3dxmesh, NULL, NULL);
4042     ok(hr == D3DERR_INVALIDCALL, "Got result %x, expected %x (D3DERR_INVALIDCALL)\n", hr, D3DERR_INVALIDCALL);
4043
4044     hr = D3DXCreateText(device, hdc, "wine", -FLT_MIN, 0.4f, &d3dxmesh, NULL, NULL);
4045     ok(hr == D3DERR_INVALIDCALL, "Got result %x, expected %x (D3DERR_INVALIDCALL)\n", hr, D3DERR_INVALIDCALL);
4046
4047     hr = D3DXCreateText(device, hdc, "wine", 0.001f, -FLT_MIN, &d3dxmesh, NULL, NULL);
4048     ok(hr == D3DERR_INVALIDCALL, "Got result %x, expected %x (D3DERR_INVALIDCALL)\n", hr, D3DERR_INVALIDCALL);
4049
4050     /* deviation = 0.0f treated as if deviation = 1.0f / otm.otmEMSquare */
4051     hr = D3DXCreateText(device, hdc, "wine", 1.0f / otm.otmEMSquare, 0.4f, &d3dxmesh, NULL, NULL);
4052     ok(hr == D3D_OK, "Got result %x, expected %x (D3D_OK)\n", hr, D3D_OK);
4053     number_of_vertices = d3dxmesh->lpVtbl->GetNumVertices(d3dxmesh);
4054     number_of_faces = d3dxmesh->lpVtbl->GetNumFaces(d3dxmesh);
4055     if (SUCCEEDED(hr) && d3dxmesh) d3dxmesh->lpVtbl->Release(d3dxmesh);
4056
4057     hr = D3DXCreateText(device, hdc, "wine", 0.0f, 0.4f, &d3dxmesh, NULL, NULL);
4058     ok(hr == D3D_OK, "Got result %x, expected %x (D3D_OK)\n", hr, D3D_OK);
4059     ok(number_of_vertices == d3dxmesh->lpVtbl->GetNumVertices(d3dxmesh),
4060        "Got %d vertices, expected %d\n",
4061        d3dxmesh->lpVtbl->GetNumVertices(d3dxmesh), number_of_vertices);
4062     ok(number_of_faces == d3dxmesh->lpVtbl->GetNumFaces(d3dxmesh),
4063        "Got %d faces, expected %d\n",
4064        d3dxmesh->lpVtbl->GetNumVertices(d3dxmesh), number_of_faces);
4065     if (SUCCEEDED(hr) && d3dxmesh) d3dxmesh->lpVtbl->Release(d3dxmesh);
4066
4067 #if 0
4068     /* too much detail requested, so will appear to hang */
4069     trace("Waiting for D3DXCreateText to finish with deviation = FLT_MIN ...\n");
4070     hr = D3DXCreateText(device, hdc, "wine", FLT_MIN, 0.4f, &d3dxmesh, NULL, NULL);
4071     ok(hr == D3D_OK, "Got result %x, expected %x (D3D_OK)\n", hr, D3D_OK);
4072     if (SUCCEEDED(hr) && d3dxmesh) d3dxmesh->lpVtbl->Release(d3dxmesh);
4073     trace("D3DXCreateText finish with deviation = FLT_MIN\n");
4074 #endif
4075
4076     hr = D3DXCreateText(device, hdc, "wine", 0.001f, 0.4f, &d3dxmesh, NULL, NULL);
4077     ok(hr == D3D_OK, "Got result %x, expected %x (D3D_OK)\n", hr, D3D_OK);
4078     if (SUCCEEDED(hr) && d3dxmesh) d3dxmesh->lpVtbl->Release(d3dxmesh);
4079
4080     test_createtext(device, hdc, "wine", FLT_MAX, 0.4f);
4081     test_createtext(device, hdc, "wine", 0.001f, FLT_MIN);
4082     test_createtext(device, hdc, "wine", 0.001f, 0.0f);
4083     test_createtext(device, hdc, "wine", 0.001f, FLT_MAX);
4084     test_createtext(device, hdc, "wine", 0.0f, 1.0f);
4085
4086     DeleteDC(hdc);
4087
4088     IDirect3DDevice9_Release(device);
4089     IDirect3D9_Release(d3d);
4090     DestroyWindow(wnd);
4091 }
4092
4093 static void test_get_decl_length(void)
4094 {
4095     static const D3DVERTEXELEMENT9 declaration1[] =
4096     {
4097         {0, 0, D3DDECLTYPE_FLOAT1, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_POSITION, 0},
4098         {1, 0, D3DDECLTYPE_FLOAT2, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_POSITION, 0},
4099         {2, 0, D3DDECLTYPE_FLOAT3, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_POSITION, 0},
4100         {3, 0, D3DDECLTYPE_FLOAT4, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_POSITION, 0},
4101         {4, 0, D3DDECLTYPE_D3DCOLOR, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_POSITION, 0},
4102         {5, 0, D3DDECLTYPE_UBYTE4, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_POSITION, 0},
4103         {6, 0, D3DDECLTYPE_SHORT2, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_POSITION, 0},
4104         {7, 0, D3DDECLTYPE_SHORT4, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_POSITION, 0},
4105         {8, 0, D3DDECLTYPE_UBYTE4N, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_POSITION, 0},
4106         {9, 0, D3DDECLTYPE_SHORT2N, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_POSITION, 0},
4107         {10, 0, D3DDECLTYPE_SHORT4N, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_POSITION, 0},
4108         {11, 0, D3DDECLTYPE_UDEC3, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_POSITION, 0},
4109         {12, 0, D3DDECLTYPE_DEC3N, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_POSITION, 0},
4110         {13, 0, D3DDECLTYPE_FLOAT16_2, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_POSITION, 0},
4111         {14, 0, D3DDECLTYPE_FLOAT16_4, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_POSITION, 0},
4112         D3DDECL_END(),
4113     };
4114     static const D3DVERTEXELEMENT9 declaration2[] =
4115     {
4116         {0, 8, D3DDECLTYPE_FLOAT1, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_POSITION, 0},
4117         {1, 8, D3DDECLTYPE_FLOAT2, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_POSITION, 0},
4118         {2, 8, D3DDECLTYPE_FLOAT3, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_POSITION, 0},
4119         {3, 8, D3DDECLTYPE_FLOAT4, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_POSITION, 0},
4120         {4, 8, D3DDECLTYPE_D3DCOLOR, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_POSITION, 0},
4121         {5, 8, D3DDECLTYPE_UBYTE4, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_POSITION, 0},
4122         {6, 8, D3DDECLTYPE_SHORT2, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_POSITION, 0},
4123         {7, 8, D3DDECLTYPE_SHORT4, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_POSITION, 0},
4124         {0, 8, D3DDECLTYPE_UBYTE4N, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_POSITION, 0},
4125         {1, 8, D3DDECLTYPE_SHORT2N, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_POSITION, 0},
4126         {2, 8, D3DDECLTYPE_SHORT4N, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_POSITION, 0},
4127         {3, 8, D3DDECLTYPE_UDEC3, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_POSITION, 0},
4128         {4, 8, D3DDECLTYPE_DEC3N, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_POSITION, 0},
4129         {5, 8, D3DDECLTYPE_FLOAT16_2, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_POSITION, 0},
4130         {6, 8, D3DDECLTYPE_FLOAT16_4, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_POSITION, 0},
4131         {7, 8, D3DDECLTYPE_FLOAT1, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_POSITION, 0},
4132         D3DDECL_END(),
4133     };
4134     UINT size;
4135
4136     size = D3DXGetDeclLength(declaration1);
4137     ok(size == 15, "Got size %u, expected 15.\n", size);
4138
4139     size = D3DXGetDeclLength(declaration2);
4140     ok(size == 16, "Got size %u, expected 16.\n", size);
4141 }
4142
4143 static void test_get_decl_vertex_size(void)
4144 {
4145     static const D3DVERTEXELEMENT9 declaration1[] =
4146     {
4147         {0, 0, D3DDECLTYPE_FLOAT1, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_POSITION, 0},
4148         {1, 0, D3DDECLTYPE_FLOAT2, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_POSITION, 0},
4149         {2, 0, D3DDECLTYPE_FLOAT3, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_POSITION, 0},
4150         {3, 0, D3DDECLTYPE_FLOAT4, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_POSITION, 0},
4151         {4, 0, D3DDECLTYPE_D3DCOLOR, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_POSITION, 0},
4152         {5, 0, D3DDECLTYPE_UBYTE4, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_POSITION, 0},
4153         {6, 0, D3DDECLTYPE_SHORT2, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_POSITION, 0},
4154         {7, 0, D3DDECLTYPE_SHORT4, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_POSITION, 0},
4155         {8, 0, D3DDECLTYPE_UBYTE4N, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_POSITION, 0},
4156         {9, 0, D3DDECLTYPE_SHORT2N, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_POSITION, 0},
4157         {10, 0, D3DDECLTYPE_SHORT4N, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_POSITION, 0},
4158         {11, 0, D3DDECLTYPE_UDEC3, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_POSITION, 0},
4159         {12, 0, D3DDECLTYPE_DEC3N, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_POSITION, 0},
4160         {13, 0, D3DDECLTYPE_FLOAT16_2, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_POSITION, 0},
4161         {14, 0, D3DDECLTYPE_FLOAT16_4, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_POSITION, 0},
4162         D3DDECL_END(),
4163     };
4164     static const D3DVERTEXELEMENT9 declaration2[] =
4165     {
4166         {0, 8, D3DDECLTYPE_FLOAT1, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_POSITION, 0},
4167         {1, 8, D3DDECLTYPE_FLOAT2, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_POSITION, 0},
4168         {2, 8, D3DDECLTYPE_FLOAT3, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_POSITION, 0},
4169         {3, 8, D3DDECLTYPE_FLOAT4, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_POSITION, 0},
4170         {4, 8, D3DDECLTYPE_D3DCOLOR, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_POSITION, 0},
4171         {5, 8, D3DDECLTYPE_UBYTE4, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_POSITION, 0},
4172         {6, 8, D3DDECLTYPE_SHORT2, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_POSITION, 0},
4173         {7, 8, D3DDECLTYPE_SHORT4, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_POSITION, 0},
4174         {0, 8, D3DDECLTYPE_UBYTE4N, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_POSITION, 0},
4175         {1, 8, D3DDECLTYPE_SHORT2N, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_POSITION, 0},
4176         {2, 8, D3DDECLTYPE_SHORT4N, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_POSITION, 0},
4177         {3, 8, D3DDECLTYPE_UDEC3, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_POSITION, 0},
4178         {4, 8, D3DDECLTYPE_DEC3N, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_POSITION, 0},
4179         {5, 8, D3DDECLTYPE_FLOAT16_2, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_POSITION, 0},
4180         {6, 8, D3DDECLTYPE_FLOAT16_4, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_POSITION, 0},
4181         {7, 8, D3DDECLTYPE_FLOAT1, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_POSITION, 0},
4182         D3DDECL_END(),
4183     };
4184     static const UINT sizes1[] =
4185     {
4186         4,  8,  12, 16,
4187         4,  4,  4,  8,
4188         4,  4,  8,  4,
4189         4,  4,  8,  0,
4190     };
4191     static const UINT sizes2[] =
4192     {
4193         12, 16, 20, 24,
4194         12, 12, 16, 16,
4195     };
4196     unsigned int i;
4197     UINT size;
4198
4199     size = D3DXGetDeclVertexSize(NULL, 0);
4200     ok(size == 0, "Got size %#x, expected 0.\n", size);
4201
4202     for (i = 0; i < 16; ++i)
4203     {
4204         size = D3DXGetDeclVertexSize(declaration1, i);
4205         ok(size == sizes1[i], "Got size %u for stream %u, expected %u.\n", size, i, sizes1[i]);
4206     }
4207
4208     for (i = 0; i < 8; ++i)
4209     {
4210         size = D3DXGetDeclVertexSize(declaration2, i);
4211         ok(size == sizes2[i], "Got size %u for stream %u, expected %u.\n", size, i, sizes2[i]);
4212     }
4213 }
4214
4215 static void D3DXGenerateAdjacencyTest(void)
4216 {
4217     HRESULT hr;
4218     HWND wnd;
4219     IDirect3D9 *d3d;
4220     IDirect3DDevice9 *device;
4221     D3DPRESENT_PARAMETERS d3dpp;
4222     ID3DXMesh *d3dxmesh = NULL;
4223     D3DXVECTOR3 *vertices = NULL;
4224     WORD *indices = NULL;
4225     int i;
4226     struct {
4227         DWORD num_vertices;
4228         D3DXVECTOR3 vertices[6];
4229         DWORD num_faces;
4230         WORD indices[3 * 3];
4231         FLOAT epsilon;
4232         DWORD adjacency[3 * 3];
4233     } test_data[] = {
4234         { /* for epsilon < 0, indices must match for faces to be adjacent */
4235             4, {{0.0, 0.0, 0.0}, {1.0, 0.0, 0.0}, {1.0, 1.0, 0.0}, {0.0, 1.0, 0.0}},
4236             2, {0, 1, 2,  0, 2, 3},
4237             -1.0,
4238             {-1, -1, 1,  0, -1, -1},
4239         },
4240         {
4241             6, {{0.0, 0.0, 0.0}, {1.0, 0.0, 0.0}, {1.0, 1.0, 0.0}, {0.0, 0.0, 0.0}, {1.0, 1.0, 0.0}, {0.0, 1.0, 0.0}},
4242             2, {0, 1, 2,  3, 4, 5},
4243             -1.0,
4244             {-1, -1, -1,  -1, -1, -1},
4245         },
4246         { /* for epsilon == 0, indices or vertices must match for faces to be adjacent */
4247             6, {{0.0, 0.0, 0.0}, {1.0, 0.0, 0.0}, {1.0, 1.0, 0.0}, {0.0, 0.0, 0.0}, {1.0, 1.0, 0.0}, {0.0, 1.0, 0.0}},
4248             2, {0, 1, 2,  3, 4, 5},
4249             0.0,
4250             {-1, -1, 1,  0, -1, -1},
4251         },
4252         { /* for epsilon > 0, vertices must be less than (but NOT equal to) epsilon distance away */
4253             6, {{0.0, 0.0, 0.0}, {1.0, 0.0, 0.0}, {1.0, 1.0, 0.0}, {0.0, 0.0, 0.25}, {1.0, 1.0, 0.25}, {0.0, 1.0, 0.25}},
4254             2, {0, 1, 2,  3, 4, 5},
4255             0.25,
4256             {-1, -1, -1,  -1, -1, -1},
4257         },
4258         { /* for epsilon > 0, vertices must be less than (but NOT equal to) epsilon distance away */
4259             6, {{0.0, 0.0, 0.0}, {1.0, 0.0, 0.0}, {1.0, 1.0, 0.0}, {0.0, 0.0, 0.25}, {1.0, 1.0, 0.25}, {0.0, 1.0, 0.25}},
4260             2, {0, 1, 2,  3, 4, 5},
4261             0.250001,
4262             {-1, -1, 1,  0, -1, -1},
4263         },
4264         { /* length between vertices are compared to epsilon, not the individual dimension deltas */
4265             6, {{0.0, 0.0, 0.0}, {1.0, 0.0, 0.0}, {1.0, 1.0, 0.0}, {0.0, 0.25, 0.25}, {1.0, 1.25, 0.25}, {0.0, 1.25, 0.25}},
4266             2, {0, 1, 2,  3, 4, 5},
4267             0.353, /* < sqrt(0.25*0.25 + 0.25*0.25) */
4268             {-1, -1, -1,  -1, -1, -1},
4269         },
4270         {
4271             6, {{0.0, 0.0, 0.0}, {1.0, 0.0, 0.0}, {1.0, 1.0, 0.0}, {0.0, 0.25, 0.25}, {1.0, 1.25, 0.25}, {0.0, 1.25, 0.25}},
4272             2, {0, 1, 2,  3, 4, 5},
4273             0.354, /* > sqrt(0.25*0.25 + 0.25*0.25) */
4274             {-1, -1, 1,  0, -1, -1},
4275         },
4276         { /* adjacent faces must have opposite winding orders at the shared edge */
4277             4, {{0.0, 0.0, 0.0}, {1.0, 0.0, 0.0}, {1.0, 1.0, 0.0}, {0.0, 1.0, 0.0}},
4278             2, {0, 1, 2,  0, 3, 2},
4279             0.0,
4280             {-1, -1, -1,  -1, -1, -1},
4281         },
4282     };
4283
4284     wnd = CreateWindow("static", "d3dx9_test", 0, 0, 0, 0, 0, NULL, NULL, NULL, NULL);
4285     if (!wnd)
4286     {
4287         skip("Couldn't create application window\n");
4288         return;
4289     }
4290     d3d = Direct3DCreate9(D3D_SDK_VERSION);
4291     if (!d3d)
4292     {
4293         skip("Couldn't create IDirect3D9 object\n");
4294         DestroyWindow(wnd);
4295         return;
4296     }
4297
4298     ZeroMemory(&d3dpp, sizeof(d3dpp));
4299     d3dpp.Windowed = TRUE;
4300     d3dpp.SwapEffect = D3DSWAPEFFECT_DISCARD;
4301     hr = IDirect3D9_CreateDevice(d3d, D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, wnd, D3DCREATE_MIXED_VERTEXPROCESSING, &d3dpp, &device);
4302     if (FAILED(hr))
4303     {
4304         skip("Failed to create IDirect3DDevice9 object %#x\n", hr);
4305         IDirect3D9_Release(d3d);
4306         DestroyWindow(wnd);
4307         return;
4308     }
4309
4310     for (i = 0; i < ARRAY_SIZE(test_data); i++)
4311     {
4312         DWORD adjacency[ARRAY_SIZE(test_data[0].adjacency)];
4313         int j;
4314
4315         if (d3dxmesh) d3dxmesh->lpVtbl->Release(d3dxmesh);
4316         d3dxmesh = NULL;
4317
4318         hr = D3DXCreateMeshFVF(test_data[i].num_faces, test_data[i].num_vertices, 0, D3DFVF_XYZ, device, &d3dxmesh);
4319         ok(hr == D3D_OK, "Got result %x, expected %x (D3D_OK)\n", hr, D3D_OK);
4320
4321         hr = d3dxmesh->lpVtbl->LockVertexBuffer(d3dxmesh, D3DLOCK_DISCARD, (void**)&vertices);
4322         ok(hr == D3D_OK, "test %d: Got result %x, expected %x (D3D_OK)\n", i, hr, D3D_OK);
4323         if (FAILED(hr)) continue;
4324         CopyMemory(vertices, test_data[i].vertices, test_data[i].num_vertices * sizeof(test_data[0].vertices[0]));
4325         d3dxmesh->lpVtbl->UnlockVertexBuffer(d3dxmesh);
4326
4327         hr = d3dxmesh->lpVtbl->LockIndexBuffer(d3dxmesh, D3DLOCK_DISCARD, (void**)&indices);
4328         ok(hr == D3D_OK, "test %d: Got result %x, expected %x (D3D_OK)\n", i, hr, D3D_OK);
4329         if (FAILED(hr)) continue;
4330         CopyMemory(indices, test_data[i].indices, test_data[i].num_faces * 3 * sizeof(test_data[0].indices[0]));
4331         d3dxmesh->lpVtbl->UnlockIndexBuffer(d3dxmesh);
4332
4333         if (i == 0) {
4334             hr = d3dxmesh->lpVtbl->GenerateAdjacency(d3dxmesh, 0.0f, NULL);
4335             ok(hr == D3DERR_INVALIDCALL, "Got result %x, expected %x (D3DERR_INVALIDCALL)\n", hr, D3DERR_INVALIDCALL);
4336         }
4337
4338         hr = d3dxmesh->lpVtbl->GenerateAdjacency(d3dxmesh, test_data[i].epsilon, adjacency);
4339         ok(hr == D3D_OK, "Got result %x, expected %x (D3D_OK)\n", hr, D3D_OK);
4340         if (FAILED(hr)) continue;
4341
4342         for (j = 0; j < test_data[i].num_faces * 3; j++)
4343             ok(adjacency[j] == test_data[i].adjacency[j],
4344                "Test %d adjacency %d: Got result %u, expected %u\n", i, j,
4345                adjacency[j], test_data[i].adjacency[j]);
4346     }
4347     if (d3dxmesh) d3dxmesh->lpVtbl->Release(d3dxmesh);
4348 }
4349
4350 static void test_update_semantics(void)
4351 {
4352     HRESULT hr;
4353     struct test_context *test_context = NULL;
4354     ID3DXMesh *mesh = NULL;
4355     D3DVERTEXELEMENT9 declaration0[] =
4356     {
4357          {0, 0, D3DDECLTYPE_FLOAT3, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_POSITION, 0},
4358          {0, 24, D3DDECLTYPE_FLOAT3, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_NORMAL, 0},
4359          {0, 36, D3DDECLTYPE_D3DCOLOR, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_COLOR, 0},
4360          D3DDECL_END()
4361     };
4362     D3DVERTEXELEMENT9 declaration_pos_type_color[] =
4363     {
4364          {0, 0, D3DDECLTYPE_D3DCOLOR, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_POSITION, 0},
4365          {0, 24, D3DDECLTYPE_FLOAT3, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_NORMAL, 0},
4366          {0, 36, D3DDECLTYPE_D3DCOLOR, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_COLOR, 0},
4367          D3DDECL_END()
4368     };
4369     D3DVERTEXELEMENT9 declaration_smaller[] =
4370     {
4371          {0, 0, D3DDECLTYPE_FLOAT3, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_POSITION, 0},
4372          {0, 24, D3DDECLTYPE_FLOAT3, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_NORMAL, 0},
4373          D3DDECL_END()
4374     };
4375     D3DVERTEXELEMENT9 declaration_larger[] =
4376     {
4377          {0, 0, D3DDECLTYPE_FLOAT3, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_POSITION, 0},
4378          {0, 24, D3DDECLTYPE_FLOAT3, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_NORMAL, 0},
4379          {0, 36, D3DDECLTYPE_D3DCOLOR, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_COLOR, 0},
4380          {0, 40, D3DDECLTYPE_FLOAT3, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_TANGENT, 0},
4381          D3DDECL_END()
4382     };
4383     D3DVERTEXELEMENT9 declaration_multiple_streams[] =
4384     {
4385          {0, 0, D3DDECLTYPE_FLOAT3, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_POSITION, 0},
4386          {1, 12, D3DDECLTYPE_FLOAT3, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_TANGENT, 0},
4387          {0, 24, D3DDECLTYPE_FLOAT3, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_NORMAL, 0},
4388          {0, 36, D3DDECLTYPE_D3DCOLOR, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_COLOR, 0},
4389
4390          D3DDECL_END()
4391     };
4392     D3DVERTEXELEMENT9 declaration_double_usage[] =
4393     {
4394          {0, 0, D3DDECLTYPE_FLOAT3, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_POSITION, 0},
4395          {0, 12, D3DDECLTYPE_FLOAT3, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_POSITION, 0},
4396          {0, 24, D3DDECLTYPE_FLOAT3, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_NORMAL, 0},
4397          {0, 36, D3DDECLTYPE_D3DCOLOR, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_COLOR, 0},
4398          D3DDECL_END()
4399     };
4400     D3DVERTEXELEMENT9 declaration_undefined_type[] =
4401     {
4402          {0, 0, D3DDECLTYPE_UNUSED+1, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_POSITION, 0},
4403          {0, 24, D3DDECLTYPE_FLOAT3, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_NORMAL, 0},
4404          {0, 36, D3DDECLTYPE_D3DCOLOR, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_COLOR, 0},
4405          D3DDECL_END()
4406     };
4407     D3DVERTEXELEMENT9 declaration_not_4_byte_aligned_offset[] =
4408     {
4409          {0, 3, D3DDECLTYPE_FLOAT3, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_POSITION, 0},
4410          {0, 24, D3DDECLTYPE_FLOAT3, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_NORMAL, 0},
4411          {0, 36, D3DDECLTYPE_D3DCOLOR, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_COLOR, 0},
4412          D3DDECL_END()
4413     };
4414     static const struct
4415     {
4416         D3DXVECTOR3 position0;
4417         D3DXVECTOR3 position1;
4418         D3DXVECTOR3 normal;
4419         DWORD color;
4420     }
4421     vertices[] =
4422     {
4423         { { 0.0f,  1.0f,  0.f}, { 1.0f,  0.0f,  0.f}, {0.0f, 0.0f, 1.0f}, 0xffff0000 },
4424         { { 1.0f, -1.0f,  0.f}, {-1.0f, -1.0f,  0.f}, {0.0f, 0.0f, 1.0f}, 0xff00ff00 },
4425         { {-1.0f, -1.0f,  0.f}, {-1.0f,  1.0f,  0.f}, {0.0f, 0.0f, 1.0f}, 0xff0000ff },
4426     };
4427     unsigned int faces[] = {0, 1, 2};
4428     unsigned int attributes[] = {0};
4429     unsigned int num_faces = ARRAY_SIZE(faces) / 3;
4430     unsigned int num_vertices = ARRAY_SIZE(vertices);
4431     int offset = sizeof(D3DXVECTOR3);
4432     DWORD options = D3DXMESH_32BIT | D3DXMESH_SYSTEMMEM;
4433     void *vertex_buffer;
4434     void *index_buffer;
4435     DWORD *attributes_buffer;
4436     D3DVERTEXELEMENT9 declaration[MAX_FVF_DECL_SIZE];
4437     D3DVERTEXELEMENT9 *decl_ptr;
4438     DWORD exp_vertex_size = sizeof(*vertices);
4439     DWORD vertex_size = 0;
4440     int equal;
4441     int i = 0;
4442     int *decl_mem;
4443     int filler_a = 0xaaaaaaaa;
4444     int filler_b = 0xbbbbbbbb;
4445
4446     test_context = new_test_context();
4447     if (!test_context)
4448     {
4449         skip("Couldn't create a test_context\n");
4450         goto cleanup;
4451     }
4452
4453     hr = D3DXCreateMesh(num_faces, num_vertices, options, declaration0,
4454                         test_context->device, &mesh);
4455     if (FAILED(hr))
4456     {
4457         skip("Couldn't create test mesh %#x\n", hr);
4458         goto cleanup;
4459     }
4460
4461     mesh->lpVtbl->LockVertexBuffer(mesh, 0, &vertex_buffer);
4462     memcpy(vertex_buffer, vertices, sizeof(vertices));
4463     mesh->lpVtbl->UnlockVertexBuffer(mesh);
4464
4465     mesh->lpVtbl->LockIndexBuffer(mesh, 0, &index_buffer);
4466     memcpy(index_buffer, faces, sizeof(faces));
4467     mesh->lpVtbl->UnlockIndexBuffer(mesh);
4468
4469     mesh->lpVtbl->LockAttributeBuffer(mesh, 0, &attributes_buffer);
4470     memcpy(attributes_buffer, attributes, sizeof(attributes));
4471     mesh->lpVtbl->UnlockAttributeBuffer(mesh);
4472
4473     /* Get the declaration and try to change it */
4474     hr = mesh->lpVtbl->GetDeclaration(mesh, declaration);
4475     if (FAILED(hr))
4476     {
4477         skip("Couldn't get vertex declaration %#x\n", hr);
4478         goto cleanup;
4479     }
4480     equal = memcmp(declaration, declaration0, sizeof(declaration0));
4481     ok(equal == 0, "Vertex declarations were not equal\n");
4482
4483     for (decl_ptr = declaration; decl_ptr->Stream != 0xFF; decl_ptr++)
4484     {
4485         if (decl_ptr->Usage == D3DDECLUSAGE_POSITION)
4486         {
4487             /* Use second vertex position instead of first */
4488             decl_ptr->Offset = offset;
4489         }
4490     }
4491
4492     hr = mesh->lpVtbl->UpdateSemantics(mesh, declaration);
4493     ok(hr == D3D_OK, "Test UpdateSematics, got %#x expected %#x\n", hr, D3D_OK);
4494
4495     /* Check that declaration was written by getting it again */
4496     memset(declaration, 0, sizeof(declaration));
4497     hr = mesh->lpVtbl->GetDeclaration(mesh, declaration);
4498     if (FAILED(hr))
4499     {
4500         skip("Couldn't get vertex declaration %#x\n", hr);
4501         goto cleanup;
4502     }
4503
4504     for (decl_ptr = declaration; decl_ptr->Stream != 0xFF; decl_ptr++)
4505     {
4506         if (decl_ptr->Usage == D3DDECLUSAGE_POSITION)
4507         {
4508             ok(decl_ptr->Offset == offset, "Test UpdateSematics, got offset %d expected %d\n",
4509                decl_ptr->Offset, offset);
4510         }
4511     }
4512
4513     /* Check that GetDeclaration only writes up to the D3DDECL_END() marker and
4514      * not the full MAX_FVF_DECL_SIZE elements.
4515      */
4516     memset(declaration, filler_a, sizeof(declaration));
4517     memcpy(declaration, declaration0, sizeof(declaration0));
4518     hr = mesh->lpVtbl->UpdateSemantics(mesh, declaration);
4519     ok(hr == D3D_OK, "Test UpdateSematics, "
4520        "got %#x expected D3D_OK\n", hr);
4521     memset(declaration, filler_b, sizeof(declaration));
4522     hr = mesh->lpVtbl->GetDeclaration(mesh, declaration);
4523     ok(hr == D3D_OK, "Couldn't get vertex declaration. Got %#x, expected D3D_OK\n", hr);
4524     decl_mem = (int*)declaration;
4525     for (i = sizeof(declaration0)/sizeof(*decl_mem); i < sizeof(declaration)/sizeof(*decl_mem); i++)
4526     {
4527         equal = memcmp(&decl_mem[i], &filler_b, sizeof(filler_b));
4528         ok(equal == 0,
4529            "GetDeclaration wrote past the D3DDECL_END() marker. "
4530            "Got %#x, expected  %#x\n", decl_mem[i], filler_b);
4531         if (equal != 0) break;
4532     }
4533
4534     /* UpdateSemantics does not check for overlapping fields */
4535     memset(declaration, 0, sizeof(declaration));
4536     hr = mesh->lpVtbl->GetDeclaration(mesh, declaration);
4537     if (FAILED(hr))
4538     {
4539         skip("Couldn't get vertex declaration %#x\n", hr);
4540         goto cleanup;
4541     }
4542
4543     for (decl_ptr = declaration; decl_ptr->Stream != 0xFF; decl_ptr++)
4544     {
4545         if (decl_ptr->Type == D3DDECLTYPE_FLOAT3)
4546         {
4547             decl_ptr->Type = D3DDECLTYPE_FLOAT4;
4548         }
4549     }
4550
4551     hr = mesh->lpVtbl->UpdateSemantics(mesh, declaration);
4552     ok(hr == D3D_OK, "Test UpdateSematics for overlapping fields, "
4553        "got %#x expected D3D_OK\n", hr);
4554
4555     /* Set the position type to color instead of float3 */
4556     hr = mesh->lpVtbl->UpdateSemantics(mesh, declaration_pos_type_color);
4557     ok(hr == D3D_OK, "Test UpdateSematics position type color, "
4558        "got %#x expected D3D_OK\n", hr);
4559
4560     /* The following test cases show that NULL, smaller or larger declarations,
4561      * and declarations with non-zero Stream values are not accepted.
4562      * UpdateSemantics returns D3DERR_INVALIDCALL and the previously set
4563      * declaration will be used by DrawSubset, GetNumBytesPerVertex, and
4564      * GetDeclaration.
4565      */
4566
4567     /* Null declaration (invalid declaration) */
4568     mesh->lpVtbl->UpdateSemantics(mesh, declaration0); /* Set a valid declaration */
4569     hr = mesh->lpVtbl->UpdateSemantics(mesh, NULL);
4570     ok(hr == D3DERR_INVALIDCALL, "Test UpdateSematics null pointer declaration, "
4571        "got %#x expected D3DERR_INVALIDCALL\n", hr);
4572     vertex_size = mesh->lpVtbl->GetNumBytesPerVertex(mesh);
4573     ok(vertex_size == exp_vertex_size, "Got vertex declaration size %u, expected %u\n",
4574        vertex_size, exp_vertex_size);
4575     memset(declaration, 0, sizeof(declaration));
4576     hr = mesh->lpVtbl->GetDeclaration(mesh, declaration);
4577     ok(hr == D3D_OK, "Couldn't get vertex declaration. Got %#x, expected D3D_OK\n", hr);
4578     equal = memcmp(declaration, declaration0, sizeof(declaration0));
4579     ok(equal == 0, "Vertex declarations were not equal\n");
4580
4581     /* Smaller vertex declaration (invalid declaration) */
4582     mesh->lpVtbl->UpdateSemantics(mesh, declaration0); /* Set a valid declaration */
4583     hr = mesh->lpVtbl->UpdateSemantics(mesh, declaration_smaller);
4584     ok(hr == D3DERR_INVALIDCALL, "Test UpdateSematics for smaller vertex declaration, "
4585        "got %#x expected D3DERR_INVALIDCALL\n", hr);
4586     vertex_size = mesh->lpVtbl->GetNumBytesPerVertex(mesh);
4587     ok(vertex_size == exp_vertex_size, "Got vertex declaration size %u, expected %u\n",
4588        vertex_size, exp_vertex_size);
4589     memset(declaration, 0, sizeof(declaration));
4590     hr = mesh->lpVtbl->GetDeclaration(mesh, declaration);
4591     ok(hr == D3D_OK, "Couldn't get vertex declaration. Got %#x, expected D3D_OK\n", hr);
4592     equal = memcmp(declaration, declaration0, sizeof(declaration0));
4593     ok(equal == 0, "Vertex declarations were not equal\n");
4594
4595     /* Larger vertex declaration (invalid declaration) */
4596     mesh->lpVtbl->UpdateSemantics(mesh, declaration0); /* Set a valid declaration */
4597     hr = mesh->lpVtbl->UpdateSemantics(mesh, declaration_larger);
4598     ok(hr == D3DERR_INVALIDCALL, "Test UpdateSematics for larger vertex declaration, "
4599        "got %#x expected D3DERR_INVALIDCALL\n", hr);
4600     vertex_size = mesh->lpVtbl->GetNumBytesPerVertex(mesh);
4601     ok(vertex_size == exp_vertex_size, "Got vertex declaration size %u, expected %u\n",
4602        vertex_size, exp_vertex_size);
4603     memset(declaration, 0, sizeof(declaration));
4604     hr = mesh->lpVtbl->GetDeclaration(mesh, declaration);
4605     ok(hr == D3D_OK, "Couldn't get vertex declaration. Got %#x, expected D3D_OK\n", hr);
4606     equal = memcmp(declaration, declaration0, sizeof(declaration0));
4607     ok(equal == 0, "Vertex declarations were not equal\n");
4608
4609     /* Use multiple streams and keep the same vertex size (invalid declaration) */
4610     mesh->lpVtbl->UpdateSemantics(mesh, declaration0); /* Set a valid declaration */
4611     hr = mesh->lpVtbl->UpdateSemantics(mesh, declaration_multiple_streams);
4612     ok(hr == D3DERR_INVALIDCALL, "Test UpdateSematics using multiple streams, "
4613                  "got %#x expected D3DERR_INVALIDCALL\n", hr);
4614     vertex_size = mesh->lpVtbl->GetNumBytesPerVertex(mesh);
4615     ok(vertex_size == exp_vertex_size, "Got vertex declaration size %u, expected %u\n",
4616        vertex_size, exp_vertex_size);
4617     memset(declaration, 0, sizeof(declaration));
4618     hr = mesh->lpVtbl->GetDeclaration(mesh, declaration);
4619     ok(hr == D3D_OK, "Couldn't get vertex declaration. Got %#x, expected D3D_OK\n", hr);
4620     equal = memcmp(declaration, declaration0, sizeof(declaration0));
4621     ok(equal == 0, "Vertex declarations were not equal\n");
4622
4623     /* The next following test cases show that some invalid declarations are
4624      * accepted with a D3D_OK. An access violation is thrown on Windows if
4625      * DrawSubset is called. The methods GetNumBytesPerVertex and GetDeclaration
4626      * are not affected, which indicates that the declaration is cached.
4627      */
4628
4629     /* Double usage (invalid declaration) */
4630     mesh->lpVtbl->UpdateSemantics(mesh, declaration0); /* Set a valid declaration */
4631     hr = mesh->lpVtbl->UpdateSemantics(mesh, declaration_double_usage);
4632     ok(hr == D3D_OK, "Test UpdateSematics double usage, "
4633        "got %#x expected D3D_OK\n", hr);
4634     vertex_size = mesh->lpVtbl->GetNumBytesPerVertex(mesh);
4635     ok(vertex_size == exp_vertex_size, "Got vertex declaration size %u, expected %u\n",
4636        vertex_size, exp_vertex_size);
4637     memset(declaration, 0, sizeof(declaration));
4638     hr = mesh->lpVtbl->GetDeclaration(mesh, declaration);
4639     ok(hr == D3D_OK, "Couldn't get vertex declaration. Got %#x, expected D3D_OK\n", hr);
4640     equal = memcmp(declaration, declaration_double_usage, sizeof(declaration_double_usage));
4641     ok(equal == 0, "Vertex declarations were not equal\n");
4642
4643     /* Set the position to an undefined type (invalid declaration) */
4644     mesh->lpVtbl->UpdateSemantics(mesh, declaration0); /* Set a valid declaration */
4645     hr = mesh->lpVtbl->UpdateSemantics(mesh, declaration_undefined_type);
4646     ok(hr == D3D_OK, "Test UpdateSematics undefined type, "
4647        "got %#x expected D3D_OK\n", hr);
4648     vertex_size = mesh->lpVtbl->GetNumBytesPerVertex(mesh);
4649     ok(vertex_size == exp_vertex_size, "Got vertex declaration size %u, expected %u\n",
4650        vertex_size, exp_vertex_size);
4651     memset(declaration, 0, sizeof(declaration));
4652     hr = mesh->lpVtbl->GetDeclaration(mesh, declaration);
4653     ok(hr == D3D_OK, "Couldn't get vertex declaration. Got %#x, expected D3D_OK\n", hr);
4654     equal = memcmp(declaration, declaration_undefined_type, sizeof(declaration_undefined_type));
4655     ok(equal == 0, "Vertex declarations were not equal\n");
4656
4657     /* Use a not 4 byte aligned offset (invalid declaration) */
4658     mesh->lpVtbl->UpdateSemantics(mesh, declaration0); /* Set a valid declaration */
4659     hr = mesh->lpVtbl->UpdateSemantics(mesh, declaration_not_4_byte_aligned_offset);
4660     ok(hr == D3D_OK, "Test UpdateSematics not 4 byte aligned offset, "
4661        "got %#x expected D3D_OK\n", hr);
4662     vertex_size = mesh->lpVtbl->GetNumBytesPerVertex(mesh);
4663     ok(vertex_size == exp_vertex_size, "Got vertex declaration size %u, expected %u\n",
4664        vertex_size, exp_vertex_size);
4665     memset(declaration, 0, sizeof(declaration));
4666     hr = mesh->lpVtbl->GetDeclaration(mesh, declaration);
4667     ok(hr == D3D_OK, "Couldn't get vertex declaration. Got %#x, expected D3D_OK\n", hr);
4668     equal = memcmp(declaration, declaration_not_4_byte_aligned_offset,
4669                    sizeof(declaration_not_4_byte_aligned_offset));
4670     ok(equal == 0, "Vertex declarations were not equal\n");
4671
4672 cleanup:
4673     if (mesh)
4674         mesh->lpVtbl->Release(mesh);
4675
4676     free_test_context(test_context);
4677 }
4678
4679 static void test_create_skin_info(void)
4680 {
4681     HRESULT hr;
4682     ID3DXSkinInfo *skininfo = NULL;
4683     D3DVERTEXELEMENT9 empty_declaration[] = { D3DDECL_END() };
4684     D3DVERTEXELEMENT9 declaration_out[MAX_FVF_DECL_SIZE];
4685     const D3DVERTEXELEMENT9 declaration_with_nonzero_stream[] = {
4686         {1, 0, D3DDECLTYPE_FLOAT3, 0, D3DDECLUSAGE_POSITION, 0},
4687         D3DDECL_END()
4688     };
4689
4690     hr = D3DXCreateSkinInfo(0, empty_declaration, 0, &skininfo);
4691     ok(hr == D3D_OK, "Expected D3D_OK, got %#x\n", hr);
4692     if (skininfo) IUnknown_Release(skininfo);
4693     skininfo = NULL;
4694
4695     hr = D3DXCreateSkinInfo(1, NULL, 1, &skininfo);
4696     ok(hr == D3DERR_INVALIDCALL, "Expected D3DERR_INVALIDCALL, got %#x\n", hr);
4697
4698     hr = D3DXCreateSkinInfo(1, declaration_with_nonzero_stream, 1, &skininfo);
4699     ok(hr == D3DERR_INVALIDCALL, "Expected D3DERR_INVALIDCALL, got %#x\n", hr);
4700
4701     hr = D3DXCreateSkinInfoFVF(1, 0, 1, &skininfo);
4702     ok(hr == D3D_OK, "Expected D3D_OK, got %#x\n", hr);
4703     if (skininfo) {
4704         DWORD dword_result;
4705         FLOAT flt_result;
4706         LPCSTR string_result;
4707         D3DXMATRIX *transform;
4708         D3DXMATRIX identity_matrix;
4709
4710         /* test initial values */
4711         hr = skininfo->lpVtbl->GetDeclaration(skininfo, declaration_out);
4712         ok(hr == D3D_OK, "Expected D3D_OK, got %#x\n", hr);
4713         if (SUCCEEDED(hr))
4714             compare_elements(declaration_out, empty_declaration, __LINE__, 0);
4715
4716         dword_result = skininfo->lpVtbl->GetNumBones(skininfo);
4717         ok(dword_result == 1, "Expected 1, got %u\n", dword_result);
4718
4719         flt_result = skininfo->lpVtbl->GetMinBoneInfluence(skininfo);
4720         ok(flt_result == 0.0f, "Expected 0.0, got %g\n", flt_result);
4721
4722         string_result = skininfo->lpVtbl->GetBoneName(skininfo, 0);
4723         ok(string_result == NULL, "Expected NULL, got %p\n", string_result);
4724
4725         dword_result = skininfo->lpVtbl->GetFVF(skininfo);
4726         ok(dword_result == 0, "Expected 0, got %u\n", dword_result);
4727
4728         dword_result = skininfo->lpVtbl->GetNumBoneInfluences(skininfo, 0);
4729         ok(dword_result == 0, "Expected 0, got %u\n", dword_result);
4730
4731         dword_result = skininfo->lpVtbl->GetNumBoneInfluences(skininfo, 1);
4732         ok(dword_result == 0, "Expected 0, got %u\n", dword_result);
4733
4734         transform = skininfo->lpVtbl->GetBoneOffsetMatrix(skininfo, -1);
4735         ok(transform == NULL, "Expected NULL, got %p\n", transform);
4736
4737         {
4738             /* test [GS]etBoneOffsetMatrix */
4739             hr = skininfo->lpVtbl->SetBoneOffsetMatrix(skininfo, 1, &identity_matrix);
4740             ok(hr == D3DERR_INVALIDCALL, "Expected D3DERR_INVALIDCALL, got %#x\n", hr);
4741
4742             hr = skininfo->lpVtbl->SetBoneOffsetMatrix(skininfo, 0, NULL);
4743             ok(hr == D3DERR_INVALIDCALL, "Expected D3DERR_INVALIDCALL, got %#x\n", hr);
4744
4745             D3DXMatrixIdentity(&identity_matrix);
4746             hr = skininfo->lpVtbl->SetBoneOffsetMatrix(skininfo, 0, &identity_matrix);
4747             ok(hr == D3D_OK, "Expected D3D_OK, got %#x\n", hr);
4748
4749             transform = skininfo->lpVtbl->GetBoneOffsetMatrix(skininfo, 0);
4750             check_matrix(transform, &identity_matrix);
4751         }
4752
4753         {
4754             /* test [GS]etBoneName */
4755             const char *name_in = "testBoneName";
4756             const char *string_result2;
4757
4758             hr = skininfo->lpVtbl->SetBoneName(skininfo, 1, name_in);
4759             ok(hr == D3DERR_INVALIDCALL, "Expected D3DERR_INVALIDCALL, got %#x\n", hr);
4760
4761             hr = skininfo->lpVtbl->SetBoneName(skininfo, 0, NULL);
4762             ok(hr == D3DERR_INVALIDCALL, "Expected D3DERR_INVALIDCALL, got %#x\n", hr);
4763
4764             hr = skininfo->lpVtbl->SetBoneName(skininfo, 0, name_in);
4765             ok(hr == D3D_OK, "Expected D3D_OK, got %#x\n", hr);
4766
4767             string_result = skininfo->lpVtbl->GetBoneName(skininfo, 0);
4768             ok(string_result != NULL, "Expected non-NULL string, got %p\n", string_result);
4769             ok(!strcmp(string_result, name_in), "Expected '%s', got '%s'\n", name_in, string_result);
4770
4771             string_result2 = skininfo->lpVtbl->GetBoneName(skininfo, 0);
4772             ok(string_result == string_result2, "Expected %p, got %p\n", string_result, string_result2);
4773
4774             string_result = skininfo->lpVtbl->GetBoneName(skininfo, 1);
4775             ok(string_result == NULL, "Expected NULL, got %p\n", string_result);
4776         }
4777
4778         {
4779             /* test [GS]etBoneInfluence */
4780             DWORD vertices[2];
4781             FLOAT weights[2];
4782             int i;
4783             DWORD num_influences;
4784             DWORD exp_vertices[2];
4785             FLOAT exp_weights[2];
4786
4787             /* vertex and weight arrays untouched when num_influences is 0 */
4788             vertices[0] = 0xdeadbeef;
4789             weights[0] = FLT_MAX;
4790             hr = skininfo->lpVtbl->GetBoneInfluence(skininfo, 0, vertices, weights);
4791             ok(hr == D3D_OK, "Expected D3D_OK, got %#x\n", hr);
4792             ok(vertices[0] == 0xdeadbeef, "expected 0xdeadbeef, got %#x\n", vertices[0]);
4793             ok(weights[0] == FLT_MAX, "expected %g, got %g\n", FLT_MAX, weights[0]);
4794
4795             hr = skininfo->lpVtbl->GetBoneInfluence(skininfo, 1, vertices, weights);
4796             ok(hr == D3DERR_INVALIDCALL, "Expected D3DERR_INVALIDCALL, got %#x\n", hr);
4797
4798             hr = skininfo->lpVtbl->GetBoneInfluence(skininfo, 0, NULL, NULL);
4799             ok(hr == D3DERR_INVALIDCALL, "Expected D3DERR_INVALIDCALL, got %#x\n", hr);
4800
4801             hr = skininfo->lpVtbl->GetBoneInfluence(skininfo, 0, vertices, NULL);
4802             ok(hr == D3D_OK, "Expected D3D_OK, got %#x\n", hr);
4803
4804             hr = skininfo->lpVtbl->GetBoneInfluence(skininfo, 0, NULL, weights);
4805             ok(hr == D3DERR_INVALIDCALL, "Expected D3DERR_INVALIDCALL, got %#x\n", hr);
4806
4807
4808             /* no vertex or weight value checking */
4809             exp_vertices[0] = 0;
4810             exp_vertices[1] = 0x87654321;
4811             exp_weights[0] = 0.5;
4812             exp_weights[1] = 0.0f / 0.0f; /* NAN */
4813             num_influences = 2;
4814
4815             hr = skininfo->lpVtbl->SetBoneInfluence(skininfo, 1, num_influences, vertices, weights);
4816             ok(hr == D3DERR_INVALIDCALL, "Expected D3DERR_INVALIDCALL, got %#x\n", hr);
4817
4818             hr = skininfo->lpVtbl->SetBoneInfluence(skininfo, 0, num_influences, NULL, weights);
4819             ok(hr == D3DERR_INVALIDCALL, "Expected D3DERR_INVALIDCALL, got %#x\n", hr);
4820
4821             hr = skininfo->lpVtbl->SetBoneInfluence(skininfo, 0, num_influences, vertices, NULL);
4822             ok(hr == D3DERR_INVALIDCALL, "Expected D3DERR_INVALIDCALL, got %#x\n", hr);
4823
4824             hr = skininfo->lpVtbl->SetBoneInfluence(skininfo, 0, num_influences, NULL, NULL);
4825             ok(hr == D3DERR_INVALIDCALL, "Expected D3DERR_INVALIDCALL, got %#x\n", hr);
4826
4827             hr = skininfo->lpVtbl->SetBoneInfluence(skininfo, 0, num_influences, exp_vertices, exp_weights);
4828             ok(hr == D3D_OK, "Expected D3D_OK, got %#x\n", hr);
4829
4830             memset(vertices, 0, sizeof(vertices));
4831             memset(weights, 0, sizeof(weights));
4832             hr = skininfo->lpVtbl->GetBoneInfluence(skininfo, 0, vertices, weights);
4833             ok(hr == D3D_OK, "Expected D3D_OK, got %#x\n", hr);
4834             for (i = 0; i < num_influences; i++) {
4835                 ok(exp_vertices[i] == vertices[i],
4836                    "influence[%d]: expected vertex %u, got %u\n", i, exp_vertices[i], vertices[i]);
4837                 ok((isnan(exp_weights[i]) && isnan(weights[i])) || exp_weights[i] == weights[i],
4838                    "influence[%d]: expected weights %g, got %g\n", i, exp_weights[i], weights[i]);
4839             }
4840
4841             /* vertices and weights aren't returned after setting num_influences to 0 */
4842             memset(vertices, 0, sizeof(vertices));
4843             memset(weights, 0, sizeof(weights));
4844             hr = skininfo->lpVtbl->SetBoneInfluence(skininfo, 0, 0, vertices, weights);
4845             ok(hr == D3D_OK, "Expected D3D_OK, got %#x\n", hr);
4846
4847             vertices[0] = 0xdeadbeef;
4848             weights[0] = FLT_MAX;
4849             hr = skininfo->lpVtbl->GetBoneInfluence(skininfo, 0, vertices, weights);
4850             ok(hr == D3D_OK, "Expected D3D_OK, got %#x\n", hr);
4851             ok(vertices[0] == 0xdeadbeef, "expected vertex 0xdeadbeef, got %u\n", vertices[0]);
4852             ok(weights[0] == FLT_MAX, "expected weight %g, got %g\n", FLT_MAX, weights[0]);
4853         }
4854
4855         {
4856             /* test [GS]etFVF and [GS]etDeclaration */
4857             D3DVERTEXELEMENT9 declaration_in[MAX_FVF_DECL_SIZE];
4858             DWORD fvf = D3DFVF_XYZ;
4859             DWORD got_fvf;
4860
4861             hr = skininfo->lpVtbl->SetDeclaration(skininfo, NULL);
4862             ok(hr == D3DERR_INVALIDCALL, "Expected D3DERR_INVALIDCALL, got %#x\n", hr);
4863
4864             hr = skininfo->lpVtbl->SetDeclaration(skininfo, declaration_with_nonzero_stream);
4865             ok(hr == D3DERR_INVALIDCALL, "Expected D3DERR_INVALIDCALL, got %#x\n", hr);
4866
4867             hr = skininfo->lpVtbl->SetFVF(skininfo, 0);
4868             ok(hr == D3D_OK, "Expected D3D_OK, got %#x\n", hr);
4869
4870             hr = D3DXDeclaratorFromFVF(fvf, declaration_in);
4871             ok(hr == D3D_OK, "Expected D3D_OK, got %#x\n", hr);
4872             hr = skininfo->lpVtbl->SetDeclaration(skininfo, declaration_in);
4873             ok(hr == D3D_OK, "Expected D3D_OK, got %#x\n", hr);
4874             got_fvf = skininfo->lpVtbl->GetFVF(skininfo);
4875             ok(fvf == got_fvf, "Expected %#x, got %#x\n", fvf, got_fvf);
4876             hr = skininfo->lpVtbl->GetDeclaration(skininfo, declaration_out);
4877             ok(hr == D3D_OK, "Expected D3D_OK, got %#x\n", hr);
4878             compare_elements(declaration_out, declaration_in, __LINE__, 0);
4879
4880             hr = skininfo->lpVtbl->SetDeclaration(skininfo, empty_declaration);
4881             ok(hr == D3D_OK, "Expected D3D_OK, got %#x\n", hr);
4882             got_fvf = skininfo->lpVtbl->GetFVF(skininfo);
4883             ok(got_fvf == 0, "Expected 0, got %#x\n", got_fvf);
4884             hr = skininfo->lpVtbl->GetDeclaration(skininfo, declaration_out);
4885             ok(hr == D3D_OK, "Expected D3D_OK, got %#x\n", hr);
4886             compare_elements(declaration_out, empty_declaration, __LINE__, 0);
4887
4888             hr = skininfo->lpVtbl->SetFVF(skininfo, fvf);
4889             ok(hr == D3D_OK, "Expected D3D_OK, got %#x\n", hr);
4890             got_fvf = skininfo->lpVtbl->GetFVF(skininfo);
4891             ok(fvf == got_fvf, "Expected %#x, got %#x\n", fvf, got_fvf);
4892             hr = skininfo->lpVtbl->GetDeclaration(skininfo, declaration_out);
4893             ok(hr == D3D_OK, "Expected D3D_OK, got %#x\n", hr);
4894             compare_elements(declaration_out, declaration_in, __LINE__, 0);
4895         }
4896     }
4897     if (skininfo) IUnknown_Release(skininfo);
4898     skininfo = NULL;
4899
4900     hr = D3DXCreateSkinInfoFVF(1, D3DFVF_XYZ, 1, NULL);
4901     ok(hr == D3DERR_INVALIDCALL, "Expected D3DERR_INVALIDCALL, got %#x\n", hr);
4902
4903     hr = D3DXCreateSkinInfo(1, NULL, 1, &skininfo);
4904     ok(hr == D3DERR_INVALIDCALL, "Expected D3DERR_INVALIDCALL, got %#x\n", hr);
4905 }
4906
4907 static void test_convert_adjacency_to_point_reps(void)
4908 {
4909     HRESULT hr;
4910     struct test_context *test_context = NULL;
4911     const DWORD options = D3DXMESH_32BIT | D3DXMESH_SYSTEMMEM;
4912     const DWORD options_16bit = D3DXMESH_SYSTEMMEM;
4913     const D3DVERTEXELEMENT9 declaration[] =
4914     {
4915         {0, 0, D3DDECLTYPE_FLOAT3, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_POSITION, 0},
4916         {0, 12, D3DDECLTYPE_FLOAT3, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_NORMAL, 0},
4917         {0, 24, D3DDECLTYPE_D3DCOLOR, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_COLOR, 0},
4918         D3DDECL_END()
4919     };
4920     const unsigned int VERTS_PER_FACE = 3;
4921     void *vertex_buffer;
4922     void *index_buffer;
4923     DWORD *attributes_buffer;
4924     int i, j;
4925     enum color { RED = 0xffff0000, GREEN = 0xff00ff00, BLUE = 0xff0000ff};
4926     struct vertex_pnc
4927     {
4928         D3DXVECTOR3 position;
4929         D3DXVECTOR3 normal;
4930         enum color color; /* In case of manual visual inspection */
4931     };
4932     D3DXVECTOR3 up = {0.0f, 0.0f, 1.0f};
4933     /* mesh0 (one face)
4934      *
4935      * 0--1
4936      * | /
4937      * |/
4938      * 2
4939      */
4940     const struct vertex_pnc vertices0[] =
4941     {
4942         {{ 0.0f,  3.0f,  0.f}, up, RED},
4943         {{ 2.0f,  3.0f,  0.f}, up, GREEN},
4944         {{ 0.0f,  0.0f,  0.f}, up, BLUE},
4945     };
4946     const DWORD indices0[] = {0, 1, 2};
4947     const unsigned int num_vertices0 = ARRAY_SIZE(vertices0);
4948     const unsigned int num_faces0 = ARRAY_SIZE(indices0) / VERTS_PER_FACE;
4949     const DWORD adjacency0[] = {-1, -1, -1};
4950     const DWORD exp_point_rep0[] = {0, 1, 2};
4951     /* mesh1 (right)
4952      *
4953      * 0--1 3
4954      * | / /|
4955      * |/ / |
4956      * 2 5--4
4957      */
4958     const struct vertex_pnc vertices1[] =
4959     {
4960         {{ 0.0f,  3.0f,  0.f}, up, RED},
4961         {{ 2.0f,  3.0f,  0.f}, up, GREEN},
4962         {{ 0.0f,  0.0f,  0.f}, up, BLUE},
4963
4964         {{ 3.0f,  3.0f,  0.f}, up, GREEN},
4965         {{ 3.0f,  0.0f,  0.f}, up, RED},
4966         {{ 1.0f,  0.0f,  0.f}, up, BLUE},
4967     };
4968     const DWORD indices1[] = {0, 1, 2, 3, 4, 5};
4969     const unsigned int num_vertices1 = ARRAY_SIZE(vertices1);
4970     const unsigned int num_faces1 = ARRAY_SIZE(indices1) / VERTS_PER_FACE;
4971     const DWORD adjacency1[] = {-1, 1, -1, -1, -1, 0};
4972     const DWORD exp_point_rep1[] = {0, 1, 2, 1, 4, 2};
4973     /* mesh2 (left)
4974      *
4975      *    3 0--1
4976      *   /| | /
4977      *  / | |/
4978      * 5--4 2
4979      */
4980     const struct vertex_pnc vertices2[] =
4981     {
4982         {{ 0.0f,  3.0f,  0.f}, up, RED},
4983         {{ 2.0f,  3.0f,  0.f}, up, GREEN},
4984         {{ 0.0f,  0.0f,  0.f}, up, BLUE},
4985
4986         {{-1.0f,  3.0f,  0.f}, up, RED},
4987         {{-1.0f,  0.0f,  0.f}, up, GREEN},
4988         {{-3.0f,  0.0f,  0.f}, up, BLUE},
4989     };
4990     const DWORD indices2[] = {0, 1, 2, 3, 4, 5};
4991     const unsigned int num_vertices2 = ARRAY_SIZE(vertices2);
4992     const unsigned int num_faces2 = ARRAY_SIZE(indices2) / VERTS_PER_FACE;
4993     const DWORD adjacency2[] = {-1, -1, 1, 0, -1, -1};
4994     const DWORD exp_point_rep2[] = {0, 1, 2, 0, 2, 5};
4995     /* mesh3 (above)
4996      *
4997      *    3
4998      *   /|
4999      *  / |
5000      * 5--4
5001      * 0--1
5002      * | /
5003      * |/
5004      * 2
5005      */
5006     struct vertex_pnc vertices3[] =
5007     {
5008         {{ 0.0f,  3.0f,  0.f}, up, RED},
5009         {{ 2.0f,  3.0f,  0.f}, up, GREEN},
5010         {{ 0.0f,  0.0f,  0.f}, up, BLUE},
5011
5012         {{ 2.0f,  7.0f,  0.f}, up, BLUE},
5013         {{ 2.0f,  4.0f,  0.f}, up, GREEN},
5014         {{ 0.0f,  4.0f,  0.f}, up, RED},
5015     };
5016     const DWORD indices3[] = {0, 1, 2, 3, 4, 5};
5017     const unsigned int num_vertices3 = ARRAY_SIZE(vertices3);
5018     const unsigned int num_faces3 = ARRAY_SIZE(indices3) / VERTS_PER_FACE;
5019     const DWORD adjacency3[] = {1, -1, -1, -1, 0, -1};
5020     const DWORD exp_point_rep3[] = {0, 1, 2, 3, 1, 0};
5021     /* mesh4 (below, tip against tip)
5022      *
5023      * 0--1
5024      * | /
5025      * |/
5026      * 2
5027      * 3
5028      * |\
5029      * | \
5030      * 5--4
5031      */
5032     struct vertex_pnc vertices4[] =
5033     {
5034         {{ 0.0f,  3.0f,  0.f}, up, RED},
5035         {{ 2.0f,  3.0f,  0.f}, up, GREEN},
5036         {{ 0.0f,  0.0f,  0.f}, up, BLUE},
5037
5038         {{ 0.0f, -4.0f,  0.f}, up, BLUE},
5039         {{ 2.0f, -7.0f,  0.f}, up, GREEN},
5040         {{ 0.0f, -7.0f,  0.f}, up, RED},
5041     };
5042     const DWORD indices4[] = {0, 1, 2, 3, 4, 5};
5043     const unsigned int num_vertices4 = ARRAY_SIZE(vertices4);
5044     const unsigned int num_faces4 = ARRAY_SIZE(indices4) / VERTS_PER_FACE;
5045     const DWORD adjacency4[] = {-1, -1, -1, -1, -1, -1};
5046     const DWORD exp_point_rep4[] = {0, 1, 2, 3, 4, 5};
5047     /* mesh5 (gap in mesh)
5048      *
5049      *    0      3-----4  15
5050      *   / \      \   /  /  \
5051      *  /   \      \ /  /    \
5052      * 2-----1      5 17-----16
5053      * 6-----7      9 12-----13
5054      *  \   /      / \  \    /
5055      *   \ /      /   \  \  /
5056      *    8     10-----11 14
5057      *
5058      */
5059     const struct vertex_pnc vertices5[] =
5060     {
5061         {{ 0.0f,  1.0f,  0.f}, up, RED},
5062         {{ 1.0f, -1.0f,  0.f}, up, GREEN},
5063         {{-1.0f, -1.0f,  0.f}, up, BLUE},
5064
5065         {{ 0.1f,  1.0f,  0.f}, up, RED},
5066         {{ 2.1f,  1.0f,  0.f}, up, BLUE},
5067         {{ 1.1f, -1.0f,  0.f}, up, GREEN},
5068
5069         {{-1.0f, -1.1f,  0.f}, up, BLUE},
5070         {{ 1.0f, -1.1f,  0.f}, up, GREEN},
5071         {{ 0.0f, -3.1f,  0.f}, up, RED},
5072
5073         {{ 1.1f, -1.1f,  0.f}, up, GREEN},
5074         {{ 2.1f, -3.1f,  0.f}, up, BLUE},
5075         {{ 0.1f, -3.1f,  0.f}, up, RED},
5076
5077         {{ 1.2f, -1.1f,  0.f}, up, GREEN},
5078         {{ 3.2f, -1.1f,  0.f}, up, RED},
5079         {{ 2.2f, -3.1f,  0.f}, up, BLUE},
5080
5081         {{ 2.2f,  1.0f,  0.f}, up, BLUE},
5082         {{ 3.2f, -1.0f,  0.f}, up, RED},
5083         {{ 1.2f, -1.0f,  0.f}, up, GREEN},
5084     };
5085     const DWORD indices5[] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17};
5086     const unsigned int num_vertices5 = ARRAY_SIZE(vertices5);
5087     const unsigned int num_faces5 = ARRAY_SIZE(indices5) / VERTS_PER_FACE;
5088     const DWORD adjacency5[] = {-1, 2, -1, -1, 5, -1, 0, -1, -1, 4, -1, -1, 5, -1, 3, -1, 4, 1};
5089     const DWORD exp_point_rep5[] = {0, 1, 2, 3, 4, 5, 2, 1, 8, 5, 10, 11, 5, 13, 10, 4, 13, 5};
5090     const WORD indices5_16bit[] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17};
5091     /* mesh6 (indices re-ordering)
5092      *
5093      * 0--1 6 3
5094      * | / /| |\
5095      * |/ / | | \
5096      * 2 8--7 5--4
5097      */
5098     const struct vertex_pnc vertices6[] =
5099     {
5100         {{ 0.0f,  3.0f,  0.f}, up, RED},
5101         {{ 2.0f,  3.0f,  0.f}, up, GREEN},
5102         {{ 0.0f,  0.0f,  0.f}, up, BLUE},
5103
5104         {{ 3.0f,  3.0f,  0.f}, up, GREEN},
5105         {{ 3.0f,  0.0f,  0.f}, up, RED},
5106         {{ 1.0f,  0.0f,  0.f}, up, BLUE},
5107
5108         {{ 4.0f,  3.0f,  0.f}, up, GREEN},
5109         {{ 6.0f,  0.0f,  0.f}, up, BLUE},
5110         {{ 4.0f,  0.0f,  0.f}, up, RED},
5111     };
5112     const DWORD indices6[] = {0, 1, 2, 6, 7, 8, 3, 4, 5};
5113     const unsigned int num_vertices6 = ARRAY_SIZE(vertices6);
5114     const unsigned int num_faces6 = ARRAY_SIZE(indices6) / VERTS_PER_FACE;
5115     const DWORD adjacency6[] = {-1, 1, -1, 2, -1, 0, -1, -1, 1};
5116     const DWORD exp_point_rep6[] = {0, 1, 2, 1, 4, 5, 1, 5, 2};
5117     /* mesh7 (expands collapsed triangle)
5118      *
5119      * 0--1 3
5120      * | / /|
5121      * |/ / |
5122      * 2 5--4
5123      */
5124     const struct vertex_pnc vertices7[] =
5125     {
5126         {{ 0.0f,  3.0f,  0.f}, up, RED},
5127         {{ 2.0f,  3.0f,  0.f}, up, GREEN},
5128         {{ 0.0f,  0.0f,  0.f}, up, BLUE},
5129
5130         {{ 3.0f,  3.0f,  0.f}, up, GREEN},
5131         {{ 3.0f,  0.0f,  0.f}, up, RED},
5132         {{ 1.0f,  0.0f,  0.f}, up, BLUE},
5133     };
5134     const DWORD indices7[] = {0, 1, 2, 3, 3, 3}; /* Face 1 is collapsed*/
5135     const unsigned int num_vertices7 = ARRAY_SIZE(vertices7);
5136     const unsigned int num_faces7 = ARRAY_SIZE(indices7) / VERTS_PER_FACE;
5137     const DWORD adjacency7[] = {-1, -1, -1, -1, -1, -1};
5138     const DWORD exp_point_rep7[] = {0, 1, 2, 3, 4, 5};
5139     /* mesh8 (indices re-ordering and double replacement)
5140      *
5141      * 0--1 9  6
5142      * | / /|  |\
5143      * |/ / |  | \
5144      * 2 11-10 8--7
5145      *         3--4
5146      *         | /
5147      *         |/
5148      *         5
5149      */
5150     const struct vertex_pnc vertices8[] =
5151     {
5152         {{ 0.0f,  3.0f,  0.f}, up, RED},
5153         {{ 2.0f,  3.0f,  0.f}, up, GREEN},
5154         {{ 0.0f,  0.0f,  0.f}, up, BLUE},
5155
5156         {{ 4.0,  -4.0,  0.f}, up, RED},
5157         {{ 6.0,  -4.0,  0.f}, up, BLUE},
5158         {{ 4.0,  -7.0,  0.f}, up, GREEN},
5159
5160         {{ 4.0f,  3.0f,  0.f}, up, GREEN},
5161         {{ 6.0f,  0.0f,  0.f}, up, BLUE},
5162         {{ 4.0f,  0.0f,  0.f}, up, RED},
5163
5164         {{ 3.0f,  3.0f,  0.f}, up, GREEN},
5165         {{ 3.0f,  0.0f,  0.f}, up, RED},
5166         {{ 1.0f,  0.0f,  0.f}, up, BLUE},
5167     };
5168     const DWORD indices8[] = {0, 1, 2, 9, 10, 11, 6, 7, 8, 3, 4, 5};
5169     const unsigned int num_vertices8 = ARRAY_SIZE(vertices8);
5170     const unsigned int num_faces8 = ARRAY_SIZE(indices8) / VERTS_PER_FACE;
5171     const DWORD adjacency8[] = {-1, 1, -1, 2, -1, 0, -1, 3, 1, 2, -1, -1};
5172     const DWORD exp_point_rep8[] = {0, 1, 2, 3, 4, 5, 1, 4, 3, 1, 3, 2};
5173     /* mesh9 (right, shared vertices)
5174      *
5175      * 0--1
5176      * | /|
5177      * |/ |
5178      * 2--3
5179      */
5180     const struct vertex_pnc vertices9[] =
5181     {
5182         {{ 0.0f,  3.0f,  0.f}, up, RED},
5183         {{ 2.0f,  3.0f,  0.f}, up, GREEN},
5184         {{ 0.0f,  0.0f,  0.f}, up, BLUE},
5185
5186         {{ 2.0f,  0.0f,  0.f}, up, RED},
5187     };
5188     const DWORD indices9[] = {0, 1, 2, 1, 3, 2};
5189     const unsigned int num_vertices9 = ARRAY_SIZE(vertices9);
5190     const unsigned int num_faces9 = ARRAY_SIZE(indices9) / VERTS_PER_FACE;
5191     const DWORD adjacency9[] = {-1, 1, -1, -1, -1, 0};
5192     const DWORD exp_point_rep9[] = {0, 1, 2, 3};
5193     /* All mesh data */
5194     ID3DXMesh *mesh = NULL;
5195     ID3DXMesh *mesh_null_check = NULL;
5196     unsigned int attributes[] = {0};
5197     struct
5198     {
5199         const struct vertex_pnc *vertices;
5200         const DWORD *indices;
5201         const DWORD num_vertices;
5202         const DWORD num_faces;
5203         const DWORD *adjacency;
5204         const DWORD *exp_point_reps;
5205         const DWORD options;
5206     }
5207     tc[] =
5208     {
5209         {
5210             vertices0,
5211             indices0,
5212             num_vertices0,
5213             num_faces0,
5214             adjacency0,
5215             exp_point_rep0,
5216             options
5217         },
5218         {
5219             vertices1,
5220             indices1,
5221             num_vertices1,
5222             num_faces1,
5223             adjacency1,
5224             exp_point_rep1,
5225             options
5226         },
5227         {
5228             vertices2,
5229             indices2,
5230             num_vertices2,
5231             num_faces2,
5232             adjacency2,
5233             exp_point_rep2,
5234             options
5235         },
5236         {
5237             vertices3,
5238             indices3,
5239             num_vertices3,
5240             num_faces3,
5241             adjacency3,
5242             exp_point_rep3,
5243             options
5244         },
5245         {
5246             vertices4,
5247             indices4,
5248             num_vertices4,
5249             num_faces4,
5250             adjacency4,
5251             exp_point_rep4,
5252             options
5253         },
5254         {
5255             vertices5,
5256             indices5,
5257             num_vertices5,
5258             num_faces5,
5259             adjacency5,
5260             exp_point_rep5,
5261             options
5262         },
5263         {
5264             vertices6,
5265             indices6,
5266             num_vertices6,
5267             num_faces6,
5268             adjacency6,
5269             exp_point_rep6,
5270             options
5271         },
5272         {
5273             vertices7,
5274             indices7,
5275             num_vertices7,
5276             num_faces7,
5277             adjacency7,
5278             exp_point_rep7,
5279             options
5280         },
5281         {
5282             vertices8,
5283             indices8,
5284             num_vertices8,
5285             num_faces8,
5286             adjacency8,
5287             exp_point_rep8,
5288             options
5289         },
5290         {
5291             vertices9,
5292             indices9,
5293             num_vertices9,
5294             num_faces9,
5295             adjacency9,
5296             exp_point_rep9,
5297             options
5298         },
5299         {
5300             vertices5,
5301             (DWORD*)indices5_16bit,
5302             num_vertices5,
5303             num_faces5,
5304             adjacency5,
5305             exp_point_rep5,
5306             options_16bit
5307         },
5308     };
5309     DWORD *point_reps = NULL;
5310
5311     test_context = new_test_context();
5312     if (!test_context)
5313     {
5314         skip("Couldn't create test context\n");
5315         goto cleanup;
5316     }
5317
5318     for (i = 0; i < ARRAY_SIZE(tc); i++)
5319     {
5320         hr = D3DXCreateMesh(tc[i].num_faces, tc[i].num_vertices, tc[i].options, declaration,
5321                             test_context->device, &mesh);
5322         if (FAILED(hr))
5323         {
5324             skip("Couldn't create mesh %d. Got %x expected D3D_OK\n", i, hr);
5325             goto cleanup;
5326         }
5327
5328         if (i == 0) /* Save first mesh for later NULL checks */
5329             mesh_null_check = mesh;
5330
5331         point_reps = HeapAlloc(GetProcessHeap(), 0, tc[i].num_vertices * sizeof(*point_reps));
5332         if (!point_reps)
5333         {
5334             skip("Couldn't allocate point reps array.\n");
5335             goto cleanup;
5336         }
5337
5338         hr = mesh->lpVtbl->LockVertexBuffer(mesh, 0, &vertex_buffer);
5339         if (FAILED(hr))
5340         {
5341             skip("Couldn't lock vertex buffer.\n");
5342             goto cleanup;
5343         }
5344         memcpy(vertex_buffer, tc[i].vertices, tc[i].num_vertices * sizeof(*tc[i].vertices));
5345         hr = mesh->lpVtbl->UnlockVertexBuffer(mesh);
5346         if (FAILED(hr))
5347         {
5348             skip("Couldn't unlock vertex buffer.\n");
5349             goto cleanup;
5350         }
5351
5352         hr = mesh->lpVtbl->LockIndexBuffer(mesh, 0, &index_buffer);
5353         if (FAILED(hr))
5354         {
5355             skip("Couldn't lock index buffer.\n");
5356             goto cleanup;
5357         }
5358         if (tc[i].options & D3DXMESH_32BIT)
5359         {
5360             memcpy(index_buffer, tc[i].indices, VERTS_PER_FACE * tc[i].num_faces * sizeof(DWORD));
5361         }
5362         else
5363         {
5364             memcpy(index_buffer, tc[i].indices, VERTS_PER_FACE * tc[i].num_faces * sizeof(WORD));
5365         }
5366         hr = mesh->lpVtbl->UnlockIndexBuffer(mesh);
5367         if (FAILED(hr)) {
5368             skip("Couldn't unlock index buffer.\n");
5369             goto cleanup;
5370         }
5371
5372         hr = mesh->lpVtbl->LockAttributeBuffer(mesh, 0, &attributes_buffer);
5373         if (FAILED(hr))
5374         {
5375             skip("Couldn't lock attributes buffer.\n");
5376             goto cleanup;
5377         }
5378         memcpy(attributes_buffer, attributes, sizeof(attributes));
5379         hr = mesh->lpVtbl->UnlockAttributeBuffer(mesh);
5380         if (FAILED(hr))
5381         {
5382             skip("Couldn't unlock attributes buffer.\n");
5383             goto cleanup;
5384         }
5385
5386         /* Convert adjacency to point representation */
5387         for (j = 0; j < tc[i].num_vertices; j++) point_reps[j] = -1;
5388         hr = mesh->lpVtbl->ConvertAdjacencyToPointReps(mesh, tc[i].adjacency, point_reps);
5389         ok(hr == D3D_OK, "ConvertAdjacencyToPointReps failed case %d. "
5390            "Got %x expected D3D_OK\n", i, hr);
5391
5392         /* Check point representation */
5393         for (j = 0; j < tc[i].num_vertices; j++)
5394         {
5395             ok(point_reps[j] == tc[i].exp_point_reps[j],
5396                "Unexpected point representation at (%d, %d)."
5397                " Got %d expected %d\n",
5398                i, j, point_reps[j], tc[i].exp_point_reps[j]);
5399         }
5400
5401         HeapFree(GetProcessHeap(), 0, point_reps);
5402         point_reps = NULL;
5403
5404         if (i != 0) /* First mesh will be freed during cleanup */
5405             mesh->lpVtbl->Release(mesh);
5406     }
5407
5408     /* NULL checks */
5409     hr = mesh_null_check->lpVtbl->ConvertAdjacencyToPointReps(mesh_null_check, tc[0].adjacency, NULL);
5410     ok(hr == D3DERR_INVALIDCALL, "ConvertAdjacencyToPointReps point_reps NULL. "
5411        "Got %x expected D3DERR_INVALIDCALL\n", hr);
5412     hr = mesh_null_check->lpVtbl->ConvertAdjacencyToPointReps(mesh_null_check, NULL, NULL);
5413     ok(hr == D3DERR_INVALIDCALL, "ConvertAdjacencyToPointReps adjacency and point_reps NULL. "
5414        "Got %x expected D3DERR_INVALIDCALL\n", hr);
5415
5416 cleanup:
5417     if (mesh_null_check)
5418         mesh_null_check->lpVtbl->Release(mesh_null_check);
5419     HeapFree(GetProcessHeap(), 0, point_reps);
5420     free_test_context(test_context);
5421 }
5422
5423 static void test_convert_point_reps_to_adjacency(void)
5424 {
5425     HRESULT hr;
5426     struct test_context *test_context = NULL;
5427     const DWORD options = D3DXMESH_32BIT | D3DXMESH_SYSTEMMEM;
5428     const DWORD options_16bit = D3DXMESH_SYSTEMMEM;
5429     const D3DVERTEXELEMENT9 declaration[] =
5430     {
5431         {0, 0, D3DDECLTYPE_FLOAT3, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_POSITION, 0},
5432         {0, 12, D3DDECLTYPE_FLOAT3, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_NORMAL, 0},
5433         {0, 24, D3DDECLTYPE_D3DCOLOR, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_COLOR, 0},
5434         D3DDECL_END()
5435     };
5436     const unsigned int VERTS_PER_FACE = 3;
5437     void *vertex_buffer;
5438     void *index_buffer;
5439     DWORD *attributes_buffer;
5440     int i, j;
5441     enum color { RED = 0xffff0000, GREEN = 0xff00ff00, BLUE = 0xff0000ff};
5442     struct vertex_pnc
5443     {
5444         D3DXVECTOR3 position;
5445         D3DXVECTOR3 normal;
5446         enum color color; /* In case of manual visual inspection */
5447     };
5448     D3DXVECTOR3 up = {0.0f, 0.0f, 1.0f};
5449     /* mesh0 (one face)
5450      *
5451      * 0--1
5452      * | /
5453      * |/
5454      * 2
5455      */
5456     const struct vertex_pnc vertices0[] =
5457     {
5458         {{ 0.0f,  3.0f,  0.f}, up, RED},
5459         {{ 2.0f,  3.0f,  0.f}, up, GREEN},
5460         {{ 0.0f,  0.0f,  0.f}, up, BLUE},
5461     };
5462     const DWORD indices0[] = {0, 1, 2};
5463     const unsigned int num_vertices0 = ARRAY_SIZE(vertices0);
5464     const unsigned int num_faces0 = num_vertices0 / VERTS_PER_FACE;
5465     const DWORD exp_adjacency0[] = {-1, -1, -1};
5466     const DWORD exp_id_adjacency0[] = {-1, -1, -1};
5467     const DWORD point_rep0[] = {0, 1, 2};
5468     /* mesh1 (right)
5469      *
5470      * 0--1 3
5471      * | / /|
5472      * |/ / |
5473      * 2 5--4
5474      */
5475     const struct vertex_pnc vertices1[] =
5476     {
5477         {{ 0.0f,  3.0f,  0.f}, up, RED},
5478         {{ 2.0f,  3.0f,  0.f}, up, GREEN},
5479         {{ 0.0f,  0.0f,  0.f}, up, BLUE},
5480
5481         {{ 3.0f,  3.0f,  0.f}, up, GREEN},
5482         {{ 3.0f,  0.0f,  0.f}, up, RED},
5483         {{ 1.0f,  0.0f,  0.f}, up, BLUE},
5484     };
5485     const DWORD indices1[] = {0, 1, 2, 3, 4, 5};
5486     const unsigned int num_vertices1 = ARRAY_SIZE(vertices1);
5487     const unsigned int num_faces1 = num_vertices1 / VERTS_PER_FACE;
5488     const DWORD exp_adjacency1[] = {-1, 1, -1, -1, -1, 0};
5489     const DWORD exp_id_adjacency1[] = {-1, -1, -1, -1, -1, -1};
5490     const DWORD point_rep1[] = {0, 1, 2, 1, 4, 2};
5491     /* mesh2 (left)
5492      *
5493      *    3 0--1
5494      *   /| | /
5495      *  / | |/
5496      * 5--4 2
5497      */
5498     const struct vertex_pnc vertices2[] =
5499     {
5500         {{ 0.0f,  3.0f,  0.f}, up, RED},
5501         {{ 2.0f,  3.0f,  0.f}, up, GREEN},
5502         {{ 0.0f,  0.0f,  0.f}, up, BLUE},
5503
5504         {{-1.0f,  3.0f,  0.f}, up, RED},
5505         {{-1.0f,  0.0f,  0.f}, up, GREEN},
5506         {{-3.0f,  0.0f,  0.f}, up, BLUE},
5507     };
5508     const DWORD indices2[] = {0, 1, 2, 3, 4, 5};
5509     const unsigned int num_vertices2 = ARRAY_SIZE(vertices2);
5510     const unsigned int num_faces2 = num_vertices2 / VERTS_PER_FACE;
5511     const DWORD exp_adjacency2[] = {-1, -1, 1, 0, -1, -1};
5512     const DWORD exp_id_adjacency2[] = {-1, -1, -1, -1, -1, -1};
5513     const DWORD point_rep2[] = {0, 1, 2, 0, 2, 5};
5514     /* mesh3 (above)
5515      *
5516      *    3
5517      *   /|
5518      *  / |
5519      * 5--4
5520      * 0--1
5521      * | /
5522      * |/
5523      * 2
5524      */
5525     struct vertex_pnc vertices3[] =
5526     {
5527         {{ 0.0f,  3.0f,  0.f}, up, RED},
5528         {{ 2.0f,  3.0f,  0.f}, up, GREEN},
5529         {{ 0.0f,  0.0f,  0.f}, up, BLUE},
5530
5531         {{ 2.0f,  7.0f,  0.f}, up, BLUE},
5532         {{ 2.0f,  4.0f,  0.f}, up, GREEN},
5533         {{ 0.0f,  4.0f,  0.f}, up, RED},
5534     };
5535     const DWORD indices3[] = {0, 1, 2, 3, 4, 5};
5536     const unsigned int num_vertices3 = ARRAY_SIZE(vertices3);
5537     const unsigned int num_faces3 = num_vertices3 / VERTS_PER_FACE;
5538     const DWORD exp_adjacency3[] = {1, -1, -1, -1, 0, -1};
5539     const DWORD exp_id_adjacency3[] = {-1, -1, -1, -1, -1, -1};
5540     const DWORD point_rep3[] = {0, 1, 2, 3, 1, 0};
5541     /* mesh4 (below, tip against tip)
5542      *
5543      * 0--1
5544      * | /
5545      * |/
5546      * 2
5547      * 3
5548      * |\
5549      * | \
5550      * 5--4
5551      */
5552     struct vertex_pnc vertices4[] =
5553     {
5554         {{ 0.0f,  3.0f,  0.f}, up, RED},
5555         {{ 2.0f,  3.0f,  0.f}, up, GREEN},
5556         {{ 0.0f,  0.0f,  0.f}, up, BLUE},
5557
5558         {{ 0.0f, -4.0f,  0.f}, up, BLUE},
5559         {{ 2.0f, -7.0f,  0.f}, up, GREEN},
5560         {{ 0.0f, -7.0f,  0.f}, up, RED},
5561     };
5562     const DWORD indices4[] = {0, 1, 2, 3, 4, 5};
5563     const unsigned int num_vertices4 = ARRAY_SIZE(vertices4);
5564     const unsigned int num_faces4 = num_vertices4 / VERTS_PER_FACE;
5565     const DWORD exp_adjacency4[] = {-1, -1, -1, -1, -1, -1};
5566     const DWORD exp_id_adjacency4[] = {-1, -1, -1, -1, -1, -1};
5567     const DWORD point_rep4[] = {0, 1, 2, 3, 4, 5};
5568     /* mesh5 (gap in mesh)
5569      *
5570      *    0      3-----4  15
5571      *   / \      \   /  /  \
5572      *  /   \      \ /  /    \
5573      * 2-----1      5 17-----16
5574      * 6-----7      9 12-----13
5575      *  \   /      / \  \    /
5576      *   \ /      /   \  \  /
5577      *    8     10-----11 14
5578      *
5579      */
5580     const struct vertex_pnc vertices5[] =
5581     {
5582         {{ 0.0f,  1.0f,  0.f}, up, RED},
5583         {{ 1.0f, -1.0f,  0.f}, up, GREEN},
5584         {{-1.0f, -1.0f,  0.f}, up, BLUE},
5585
5586         {{ 0.1f,  1.0f,  0.f}, up, RED},
5587         {{ 2.1f,  1.0f,  0.f}, up, BLUE},
5588         {{ 1.1f, -1.0f,  0.f}, up, GREEN},
5589
5590         {{-1.0f, -1.1f,  0.f}, up, BLUE},
5591         {{ 1.0f, -1.1f,  0.f}, up, GREEN},
5592         {{ 0.0f, -3.1f,  0.f}, up, RED},
5593
5594         {{ 1.1f, -1.1f,  0.f}, up, GREEN},
5595         {{ 2.1f, -3.1f,  0.f}, up, BLUE},
5596         {{ 0.1f, -3.1f,  0.f}, up, RED},
5597
5598         {{ 1.2f, -1.1f,  0.f}, up, GREEN},
5599         {{ 3.2f, -1.1f,  0.f}, up, RED},
5600         {{ 2.2f, -3.1f,  0.f}, up, BLUE},
5601
5602         {{ 2.2f,  1.0f,  0.f}, up, BLUE},
5603         {{ 3.2f, -1.0f,  0.f}, up, RED},
5604         {{ 1.2f, -1.0f,  0.f}, up, GREEN},
5605     };
5606     const DWORD indices5[] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17};
5607     const unsigned int num_vertices5 = ARRAY_SIZE(vertices5);
5608     const unsigned int num_faces5 = num_vertices5 / VERTS_PER_FACE;
5609     const DWORD exp_adjacency5[] = {-1, 2, -1, -1, 5, -1, 0, -1, -1, 4, -1, -1, 5, -1, 3, -1, 4, 1};
5610     const DWORD exp_id_adjacency5[] = {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1};
5611     const DWORD point_rep5[] = {0, 1, 2, 3, 4, 5, 2, 1, 8, 5, 10, 11, 5, 13, 10, 4, 13, 5};
5612     /* mesh6 (indices re-ordering)
5613      *
5614      * 0--1 6 3
5615      * | / /| |\
5616      * |/ / | | \
5617      * 2 8--7 5--4
5618      */
5619     const struct vertex_pnc vertices6[] =
5620     {
5621         {{ 0.0f,  3.0f,  0.f}, up, RED},
5622         {{ 2.0f,  3.0f,  0.f}, up, GREEN},
5623         {{ 0.0f,  0.0f,  0.f}, up, BLUE},
5624
5625         {{ 3.0f,  3.0f,  0.f}, up, GREEN},
5626         {{ 3.0f,  0.0f,  0.f}, up, RED},
5627         {{ 1.0f,  0.0f,  0.f}, up, BLUE},
5628
5629         {{ 4.0f,  3.0f,  0.f}, up, GREEN},
5630         {{ 6.0f,  0.0f,  0.f}, up, BLUE},
5631         {{ 4.0f,  0.0f,  0.f}, up, RED},
5632     };
5633     const DWORD indices6[] = {0, 1, 2, 6, 7, 8, 3, 4, 5};
5634     const unsigned int num_vertices6 = ARRAY_SIZE(vertices6);
5635     const unsigned int num_faces6 = num_vertices6 / VERTS_PER_FACE;
5636     const DWORD exp_adjacency6[] = {-1, 1, -1, 2, -1, 0, -1, -1, 1};
5637     const DWORD exp_id_adjacency6[] = {-1, -1, -1, -1, -1, -1, -1, -1, -1};
5638     const DWORD point_rep6[] = {0, 1, 2, 1, 4, 5, 1, 5, 2};
5639     /* mesh7 (expands collapsed triangle)
5640      *
5641      * 0--1 3
5642      * | / /|
5643      * |/ / |
5644      * 2 5--4
5645      */
5646     const struct vertex_pnc vertices7[] =
5647     {
5648         {{ 0.0f,  3.0f,  0.f}, up, RED},
5649         {{ 2.0f,  3.0f,  0.f}, up, GREEN},
5650         {{ 0.0f,  0.0f,  0.f}, up, BLUE},
5651
5652         {{ 3.0f,  3.0f,  0.f}, up, GREEN},
5653         {{ 3.0f,  0.0f,  0.f}, up, RED},
5654         {{ 1.0f,  0.0f,  0.f}, up, BLUE},
5655     };
5656     const DWORD indices7[] = {0, 1, 2, 3, 3, 3}; /* Face 1 is collapsed*/
5657     const unsigned int num_vertices7 = ARRAY_SIZE(vertices7);
5658     const unsigned int num_faces7 = num_vertices7 / VERTS_PER_FACE;
5659     const DWORD exp_adjacency7[] = {-1, -1, -1, -1, -1, -1};
5660     const DWORD exp_id_adjacency7[] = {-1, -1, -1, -1, -1, -1};
5661     const DWORD point_rep7[] = {0, 1, 2, 3, 4, 5};
5662     /* mesh8 (indices re-ordering and double replacement)
5663      *
5664      * 0--1 9  6
5665      * | / /|  |\
5666      * |/ / |  | \
5667      * 2 11-10 8--7
5668      *         3--4
5669      *         | /
5670      *         |/
5671      *         5
5672      */
5673     const struct vertex_pnc vertices8[] =
5674     {
5675         {{ 0.0f,  3.0f,  0.f}, up, RED},
5676         {{ 2.0f,  3.0f,  0.f}, up, GREEN},
5677         {{ 0.0f,  0.0f,  0.f}, up, BLUE},
5678
5679         {{ 4.0,  -4.0,  0.f}, up, RED},
5680         {{ 6.0,  -4.0,  0.f}, up, BLUE},
5681         {{ 4.0,  -7.0,  0.f}, up, GREEN},
5682
5683         {{ 4.0f,  3.0f,  0.f}, up, GREEN},
5684         {{ 6.0f,  0.0f,  0.f}, up, BLUE},
5685         {{ 4.0f,  0.0f,  0.f}, up, RED},
5686
5687         {{ 3.0f,  3.0f,  0.f}, up, GREEN},
5688         {{ 3.0f,  0.0f,  0.f}, up, RED},
5689         {{ 1.0f,  0.0f,  0.f}, up, BLUE},
5690     };
5691     const DWORD indices8[] = {0, 1, 2, 9, 10, 11, 6, 7, 8, 3, 4, 5};
5692     const WORD indices8_16bit[] = {0, 1, 2, 9, 10, 11, 6, 7, 8, 3, 4, 5};
5693     const unsigned int num_vertices8 = ARRAY_SIZE(vertices8);
5694     const unsigned int num_faces8 = num_vertices8 / VERTS_PER_FACE;
5695     const DWORD exp_adjacency8[] = {-1, 1, -1, 2, -1, 0, -1, 3, 1, 2, -1, -1};
5696     const DWORD exp_id_adjacency8[] = {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1};
5697     const DWORD point_rep8[] = {0, 1, 2, 3, 4, 5, 1, 4, 3, 1, 3, 2};
5698      /* mesh9 (right, shared vertices)
5699      *
5700      * 0--1
5701      * | /|
5702      * |/ |
5703      * 2--3
5704      */
5705     const struct vertex_pnc vertices9[] =
5706     {
5707         {{ 0.0f,  3.0f,  0.f}, up, RED},
5708         {{ 2.0f,  3.0f,  0.f}, up, GREEN},
5709         {{ 0.0f,  0.0f,  0.f}, up, BLUE},
5710
5711         {{ 2.0f,  0.0f,  0.f}, up, RED},
5712     };
5713     const DWORD indices9[] = {0, 1, 2, 1, 3, 2};
5714     const unsigned int num_vertices9 = ARRAY_SIZE(vertices9);
5715     const unsigned int num_faces9 = 2;
5716     const DWORD exp_adjacency9[] = {-1, 1, -1, -1, -1, 0};
5717     const DWORD exp_id_adjacency9[] = {-1, 1, -1, -1, -1, 0};
5718     const DWORD point_rep9[] = {0, 1, 2, 3};
5719     /* All mesh data */
5720     ID3DXMesh *mesh = NULL;
5721     ID3DXMesh *mesh_null_check = NULL;
5722     unsigned int attributes[] = {0};
5723     struct
5724     {
5725         const struct vertex_pnc *vertices;
5726         const DWORD *indices;
5727         const DWORD num_vertices;
5728         const DWORD num_faces;
5729         const DWORD *point_reps;
5730         const DWORD *exp_adjacency;
5731         const DWORD *exp_id_adjacency;
5732         const DWORD options;
5733     }
5734     tc[] =
5735     {
5736         {
5737             vertices0,
5738             indices0,
5739             num_vertices0,
5740             num_faces0,
5741             point_rep0,
5742             exp_adjacency0,
5743             exp_id_adjacency0,
5744             options
5745         },
5746         {
5747             vertices1,
5748             indices1,
5749             num_vertices1,
5750             num_faces1,
5751             point_rep1,
5752             exp_adjacency1,
5753             exp_id_adjacency1,
5754             options
5755         },
5756         {
5757             vertices2,
5758             indices2,
5759             num_vertices2,
5760             num_faces2,
5761             point_rep2,
5762             exp_adjacency2,
5763             exp_id_adjacency2,
5764             options
5765         },
5766         {
5767             vertices3,
5768             indices3,
5769             num_vertices3,
5770             num_faces3,
5771             point_rep3,
5772             exp_adjacency3,
5773             exp_id_adjacency3,
5774             options
5775         },
5776         {
5777             vertices4,
5778             indices4,
5779             num_vertices4,
5780             num_faces4,
5781             point_rep4,
5782             exp_adjacency4,
5783             exp_id_adjacency4,
5784             options
5785         },
5786         {
5787             vertices5,
5788             indices5,
5789             num_vertices5,
5790             num_faces5,
5791             point_rep5,
5792             exp_adjacency5,
5793             exp_id_adjacency5,
5794             options
5795         },
5796         {
5797             vertices6,
5798             indices6,
5799             num_vertices6,
5800             num_faces6,
5801             point_rep6,
5802             exp_adjacency6,
5803             exp_id_adjacency6,
5804             options
5805         },
5806         {
5807             vertices7,
5808             indices7,
5809             num_vertices7,
5810             num_faces7,
5811             point_rep7,
5812             exp_adjacency7,
5813             exp_id_adjacency7,
5814             options
5815         },
5816         {
5817             vertices8,
5818             indices8,
5819             num_vertices8,
5820             num_faces8,
5821             point_rep8,
5822             exp_adjacency8,
5823             exp_id_adjacency8,
5824             options
5825         },
5826         {
5827             vertices9,
5828             indices9,
5829             num_vertices9,
5830             num_faces9,
5831             point_rep9,
5832             exp_adjacency9,
5833             exp_id_adjacency9,
5834             options
5835         },
5836         {
5837             vertices8,
5838             (DWORD*)indices8_16bit,
5839             num_vertices8,
5840             num_faces8,
5841             point_rep8,
5842             exp_adjacency8,
5843             exp_id_adjacency8,
5844             options_16bit
5845         },
5846     };
5847     DWORD *adjacency = NULL;
5848
5849     test_context = new_test_context();
5850     if (!test_context)
5851     {
5852         skip("Couldn't create test context\n");
5853         goto cleanup;
5854     }
5855
5856     for (i = 0; i < ARRAY_SIZE(tc); i++)
5857     {
5858         hr = D3DXCreateMesh(tc[i].num_faces, tc[i].num_vertices, tc[i].options,
5859                             declaration, test_context->device, &mesh);
5860         if (FAILED(hr))
5861         {
5862             skip("Couldn't create mesh %d. Got %x expected D3D_OK\n", i, hr);
5863             goto cleanup;
5864         }
5865
5866         if (i == 0) /* Save first mesh for later NULL checks */
5867             mesh_null_check = mesh;
5868
5869         adjacency = HeapAlloc(GetProcessHeap(), 0, VERTS_PER_FACE * tc[i].num_faces * sizeof(*adjacency));
5870         if (!adjacency)
5871         {
5872             skip("Couldn't allocate adjacency array.\n");
5873             goto cleanup;
5874         }
5875
5876         hr = mesh->lpVtbl->LockVertexBuffer(mesh, 0, &vertex_buffer);
5877         if (FAILED(hr))
5878         {
5879             skip("Couldn't lock vertex buffer.\n");
5880             goto cleanup;
5881         }
5882         memcpy(vertex_buffer, tc[i].vertices, tc[i].num_vertices * sizeof(*tc[i].vertices));
5883         hr = mesh->lpVtbl->UnlockVertexBuffer(mesh);
5884         if (FAILED(hr))
5885         {
5886             skip("Couldn't unlock vertex buffer.\n");
5887             goto cleanup;
5888         }
5889         hr = mesh->lpVtbl->LockIndexBuffer(mesh, 0, &index_buffer);
5890         if (FAILED(hr))
5891         {
5892             skip("Couldn't lock index buffer.\n");
5893             goto cleanup;
5894         }
5895         if (tc[i].options & D3DXMESH_32BIT)
5896         {
5897             memcpy(index_buffer, tc[i].indices, VERTS_PER_FACE * tc[i].num_faces * sizeof(DWORD));
5898         }
5899         else
5900         {
5901             memcpy(index_buffer, tc[i].indices, VERTS_PER_FACE * tc[i].num_faces * sizeof(WORD));
5902         }
5903         hr = mesh->lpVtbl->UnlockIndexBuffer(mesh);
5904         if (FAILED(hr)) {
5905             skip("Couldn't unlock index buffer.\n");
5906             goto cleanup;
5907         }
5908
5909         hr = mesh->lpVtbl->LockAttributeBuffer(mesh, 0, &attributes_buffer);
5910         if (FAILED(hr))
5911         {
5912             skip("Couldn't lock attributes buffer.\n");
5913             goto cleanup;
5914         }
5915         memcpy(attributes_buffer, attributes, sizeof(attributes));
5916         hr = mesh->lpVtbl->UnlockAttributeBuffer(mesh);
5917         if (FAILED(hr))
5918         {
5919             skip("Couldn't unlock attributes buffer.\n");
5920             goto cleanup;
5921         }
5922
5923         /* Convert point representation to adjacency*/
5924         for (j = 0; j < VERTS_PER_FACE * tc[i].num_faces; j++) adjacency[j] = -2;
5925
5926         hr = mesh->lpVtbl->ConvertPointRepsToAdjacency(mesh, tc[i].point_reps, adjacency);
5927         ok(hr == D3D_OK, "ConvertPointRepsToAdjacency failed case %d. "
5928            "Got %x expected D3D_OK\n", i, hr);
5929         /* Check adjacency */
5930         for (j = 0; j < VERTS_PER_FACE * tc[i].num_faces; j++)
5931         {
5932             ok(adjacency[j] == tc[i].exp_adjacency[j],
5933                "Unexpected adjacency information at (%d, %d)."
5934                " Got %d expected %d\n",
5935                i, j, adjacency[j], tc[i].exp_adjacency[j]);
5936         }
5937
5938         /* NULL point representation is considered identity. */
5939         for (j = 0; j < VERTS_PER_FACE * tc[i].num_faces; j++) adjacency[j] = -2;
5940         hr = mesh_null_check->lpVtbl->ConvertPointRepsToAdjacency(mesh, NULL, adjacency);
5941         ok(hr == D3D_OK, "ConvertPointRepsToAdjacency NULL point_reps. "
5942                      "Got %x expected D3D_OK\n", hr);
5943         for (j = 0; j < VERTS_PER_FACE * tc[i].num_faces; j++)
5944         {
5945             ok(adjacency[j] == tc[i].exp_id_adjacency[j],
5946                "Unexpected adjacency information (id) at (%d, %d)."
5947                " Got %d expected %d\n",
5948                i, j, adjacency[j], tc[i].exp_id_adjacency[j]);
5949         }
5950
5951         HeapFree(GetProcessHeap(), 0, adjacency);
5952         if (i != 0) /* First mesh will be freed during cleanup */
5953             mesh->lpVtbl->Release(mesh);
5954     }
5955
5956     /* NULL checks */
5957     hr = mesh_null_check->lpVtbl->ConvertPointRepsToAdjacency(mesh_null_check, tc[0].point_reps, NULL);
5958     ok(hr == D3DERR_INVALIDCALL, "ConvertPointRepsToAdjacency NULL adjacency. "
5959        "Got %x expected D3DERR_INVALIDCALL\n", hr);
5960     hr = mesh_null_check->lpVtbl->ConvertPointRepsToAdjacency(mesh_null_check, NULL, NULL);
5961     ok(hr == D3DERR_INVALIDCALL, "ConvertPointRepsToAdjacency NULL point_reps and adjacency. "
5962        "Got %x expected D3DERR_INVALIDCALL\n", hr);
5963
5964 cleanup:
5965     if (mesh_null_check)
5966         mesh_null_check->lpVtbl->Release(mesh_null_check);
5967     HeapFree(GetProcessHeap(), 0, adjacency);
5968     free_test_context(test_context);
5969 }
5970
5971 static HRESULT init_test_mesh(const DWORD num_faces, const DWORD num_vertices,
5972                               const DWORD options,
5973                               const D3DVERTEXELEMENT9 *declaration,
5974                               IDirect3DDevice9 *device, ID3DXMesh **mesh_ptr,
5975                               const void *vertices, const DWORD vertex_size,
5976                               const DWORD *indices, const DWORD *attributes)
5977 {
5978     HRESULT hr;
5979     void *vertex_buffer;
5980     void *index_buffer;
5981     DWORD *attributes_buffer;
5982     ID3DXMesh *mesh = NULL;
5983
5984     hr = D3DXCreateMesh(num_faces, num_vertices, options, declaration, device, mesh_ptr);
5985     if (FAILED(hr))
5986     {
5987         skip("Couldn't create mesh. Got %x expected D3D_OK\n", hr);
5988         goto cleanup;
5989     }
5990     mesh = *mesh_ptr;
5991
5992     hr = mesh->lpVtbl->LockVertexBuffer(mesh, 0, &vertex_buffer);
5993     if (FAILED(hr))
5994     {
5995         skip("Couldn't lock vertex buffer.\n");
5996         goto cleanup;
5997     }
5998     memcpy(vertex_buffer, vertices, num_vertices * vertex_size);
5999     hr = mesh->lpVtbl->UnlockVertexBuffer(mesh);
6000     if (FAILED(hr))
6001     {
6002         skip("Couldn't unlock vertex buffer.\n");
6003         goto cleanup;
6004     }
6005
6006     hr = mesh->lpVtbl->LockIndexBuffer(mesh, 0, &index_buffer);
6007     if (FAILED(hr))
6008     {
6009         skip("Couldn't lock index buffer.\n");
6010         goto cleanup;
6011     }
6012     if (options & D3DXMESH_32BIT)
6013     {
6014         if (indices)
6015             memcpy(index_buffer, indices, 3 * num_faces * sizeof(DWORD));
6016         else
6017         {
6018             /* Fill index buffer with 0, 1, 2, ...*/
6019             DWORD *indices_32bit = (DWORD*)index_buffer;
6020             UINT i;
6021             for (i = 0; i < 3 * num_faces; i++)
6022                 indices_32bit[i] = i;
6023         }
6024     }
6025     else
6026     {
6027         if (indices)
6028             memcpy(index_buffer, indices, 3 * num_faces * sizeof(WORD));
6029         else
6030         {
6031             /* Fill index buffer with 0, 1, 2, ...*/
6032             WORD *indices_16bit = (WORD*)index_buffer;
6033             UINT i;
6034             for (i = 0; i < 3 * num_faces; i++)
6035                 indices_16bit[i] = i;
6036         }
6037     }
6038     hr = mesh->lpVtbl->UnlockIndexBuffer(mesh);
6039     if (FAILED(hr)) {
6040         skip("Couldn't unlock index buffer.\n");
6041         goto cleanup;
6042     }
6043
6044     hr = mesh->lpVtbl->LockAttributeBuffer(mesh, 0, &attributes_buffer);
6045     if (FAILED(hr))
6046     {
6047         skip("Couldn't lock attributes buffer.\n");
6048         goto cleanup;
6049     }
6050
6051     if (attributes)
6052         memcpy(attributes_buffer, attributes, num_faces * sizeof(*attributes));
6053     else
6054         memset(attributes_buffer, 0, num_faces * sizeof(*attributes));
6055
6056     hr = mesh->lpVtbl->UnlockAttributeBuffer(mesh);
6057     if (FAILED(hr))
6058     {
6059         skip("Couldn't unlock attributes buffer.\n");
6060         goto cleanup;
6061     }
6062
6063     hr = D3D_OK;
6064 cleanup:
6065     return hr;
6066 }
6067
6068 /* Using structs instead of bit-fields in order to avoid compiler issues. */
6069 struct udec3
6070 {
6071     UINT x;
6072     UINT y;
6073     UINT z;
6074     UINT w;
6075 };
6076
6077 struct dec3n
6078 {
6079     INT x;
6080     INT y;
6081     INT z;
6082     INT w;
6083 };
6084
6085 static DWORD init_udec3_dword(UINT x, UINT y, UINT z, UINT w)
6086 {
6087     DWORD d = 0;
6088
6089     d |= x & 0x3ff;
6090     d |= (y << 10) & 0xffc00;
6091     d |= (z << 20) & 0x3ff00000;
6092     d |= (w << 30) & 0xc0000000;
6093
6094     return d;
6095 }
6096
6097 static DWORD init_dec3n_dword(INT x, INT y, INT z, INT w)
6098 {
6099     DWORD d = 0;
6100
6101     d |= x & 0x3ff;
6102     d |= (y << 10) & 0xffc00;
6103     d |= (z << 20) & 0x3ff00000;
6104     d |= (w << 30) & 0xc0000000;
6105
6106     return d;
6107 }
6108
6109 static struct udec3 dword_to_udec3(DWORD d)
6110 {
6111     struct udec3 v;
6112
6113     v.x = d & 0x3ff;
6114     v.y = (d & 0xffc00) >> 10;
6115     v.z = (d & 0x3ff00000) >> 20;
6116     v.w = (d & 0xc0000000) >> 30;
6117
6118     return v;
6119 }
6120
6121 static struct dec3n dword_to_dec3n(DWORD d)
6122 {
6123     struct dec3n v;
6124
6125     v.x = d & 0x3ff;
6126     v.y = (d & 0xffc00) >> 10;
6127     v.z = (d & 0x3ff00000) >> 20;
6128     v.w = (d & 0xc0000000) >> 30;
6129
6130     return v;
6131 }
6132
6133 static void check_vertex_components(int line, int mesh_number, int vertex_number, BYTE *got_ptr, const BYTE *exp_ptr, D3DVERTEXELEMENT9 *declaration)
6134 {
6135     const char *usage_strings[] =
6136     {
6137         "position",
6138         "blend weight",
6139         "blend indices",
6140         "normal",
6141         "point size",
6142         "texture coordinates",
6143         "tangent",
6144         "binormal",
6145         "tessellation factor",
6146         "position transformed",
6147         "color",
6148         "fog",
6149         "depth",
6150         "sample"
6151     };
6152     D3DVERTEXELEMENT9 *decl_ptr;
6153
6154     for (decl_ptr = declaration; decl_ptr->Stream != 0xFF; decl_ptr++)
6155     {
6156         switch (decl_ptr->Type)
6157         {
6158             case D3DDECLTYPE_FLOAT1:
6159             {
6160                 FLOAT *got = (FLOAT*)(got_ptr + decl_ptr->Offset);
6161                 FLOAT *exp = (FLOAT*)(exp_ptr + decl_ptr->Offset);
6162                 FLOAT diff = fabsf(*got - *exp);
6163                 ok_(__FILE__,line)(diff <= FLT_EPSILON, "Mesh %d: Got %f for vertex %d %s, expected %f.\n",
6164                     mesh_number, *got, vertex_number, usage_strings[decl_ptr->Usage], *exp);
6165                 break;
6166             }
6167             case D3DDECLTYPE_FLOAT2:
6168             {
6169                 D3DXVECTOR2 *got = (D3DXVECTOR2*)(got_ptr + decl_ptr->Offset);
6170                 D3DXVECTOR2 *exp = (D3DXVECTOR2*)(exp_ptr + decl_ptr->Offset);
6171                 FLOAT diff = fmaxf(fabsf(got->x - exp->x), fabsf(got->y - exp->y));
6172                 ok_(__FILE__,line)(diff <= FLT_EPSILON, "Mesh %d: Got (%f, %f) for vertex %d %s, expected (%f, %f).\n",
6173                     mesh_number, got->x, got->y, vertex_number, usage_strings[decl_ptr->Usage], exp->x, exp->y);
6174                 break;
6175             }
6176             case D3DDECLTYPE_FLOAT3:
6177             {
6178                 D3DXVECTOR3 *got = (D3DXVECTOR3*)(got_ptr + decl_ptr->Offset);
6179                 D3DXVECTOR3 *exp = (D3DXVECTOR3*)(exp_ptr + decl_ptr->Offset);
6180                 FLOAT diff = fmaxf(fabsf(got->x - exp->x), fabsf(got->y - exp->y));
6181                 diff = fmaxf(diff, fabsf(got->z - exp->z));
6182                 ok_(__FILE__,line)(diff <= FLT_EPSILON, "Mesh %d: Got (%f, %f, %f) for vertex %d %s, expected (%f, %f, %f).\n",
6183                     mesh_number, got->x, got->y, got->z, vertex_number, usage_strings[decl_ptr->Usage], exp->x, exp->y, exp->z);
6184                 break;
6185             }
6186             case D3DDECLTYPE_FLOAT4:
6187             {
6188                 D3DXVECTOR4 *got = (D3DXVECTOR4*)(got_ptr + decl_ptr->Offset);
6189                 D3DXVECTOR4 *exp = (D3DXVECTOR4*)(exp_ptr + decl_ptr->Offset);
6190                 FLOAT diff = fmaxf(fabsf(got->x - exp->x), fabsf(got->y - exp->y));
6191                 diff = fmaxf(diff, fabsf(got->z - exp->z));
6192                 diff = fmaxf(diff, fabsf(got->w - exp->w));
6193                 ok_(__FILE__,line)(diff <= FLT_EPSILON, "Mesh %d: Got (%f, %f, %f, %f) for vertex %d %s, expected (%f, %f, %f, %f).\n",
6194                     mesh_number, got->x, got->y, got->z, got->w, vertex_number, usage_strings[decl_ptr->Usage], exp->x, exp->y, exp->z, got->w);
6195                 break;
6196             }
6197             case D3DDECLTYPE_D3DCOLOR:
6198             {
6199                 BYTE *got = got_ptr + decl_ptr->Offset;
6200                 const BYTE *exp = exp_ptr + decl_ptr->Offset;
6201                 BOOL same_color = got[0] == exp[0] && got[1] == exp[1]
6202                                   && got[2] == exp[2] && got[3] == exp[3];
6203                 const char *color_types[] = {"diffuse", "specular", "undefined color"};
6204                 BYTE usage_index = decl_ptr->UsageIndex;
6205                 if (usage_index > 1) usage_index = 2;
6206                 ok_(__FILE__,line)(same_color, "Mesh %d: Got (%u, %u, %u, %u) for vertex %d %s, expected (%u, %u, %u, %u).\n",
6207                     mesh_number, got[0], got[1], got[2], got[3], vertex_number, color_types[usage_index], exp[0], exp[1], exp[2], exp[3]);
6208                 break;
6209             }
6210             case D3DDECLTYPE_UBYTE4:
6211             case D3DDECLTYPE_UBYTE4N:
6212             {
6213                 BYTE *got = got_ptr + decl_ptr->Offset;
6214                 const BYTE *exp = exp_ptr + decl_ptr->Offset;
6215                 BOOL same = got[0] == exp[0] && got[1] == exp[1]
6216                             && got[2] == exp[2] && got[3] == exp[3];
6217                 ok_(__FILE__,line)(same, "Mesh %d: Got (%u, %u, %u, %u) for vertex %d %s, expected (%u, %u, %u, %u).\n",
6218                     mesh_number, got[0], got[1], got[2], got[3], vertex_number, usage_strings[decl_ptr->Usage], exp[0], exp[1], exp[2], exp[3]);
6219                 break;
6220             }
6221             case D3DDECLTYPE_SHORT2:
6222             case D3DDECLTYPE_SHORT2N:
6223             {
6224                 SHORT *got = (SHORT*)(got_ptr + decl_ptr->Offset);
6225                 SHORT *exp = (SHORT*)(exp_ptr + decl_ptr->Offset);
6226                 BOOL same = got[0] == exp[0] && got[1] == exp[1];
6227                 ok_(__FILE__,line)(same, "Mesh %d: Got (%hd, %hd) for vertex %d %s, expected (%hd, %hd).\n",
6228                     mesh_number, got[0], got[1], vertex_number, usage_strings[decl_ptr->Usage], exp[0], exp[1]);
6229                 break;
6230             }
6231             case D3DDECLTYPE_SHORT4:
6232             case D3DDECLTYPE_SHORT4N:
6233             {
6234                 SHORT *got = (SHORT*)(got_ptr + decl_ptr->Offset);
6235                 SHORT *exp = (SHORT*)(exp_ptr + decl_ptr->Offset);
6236                 BOOL same = got[0] == exp[0] && got[1] == exp[1]
6237                             && got[2] == exp[2] && got[3] == exp[3];
6238                 ok_(__FILE__,line)(same, "Mesh %d: Got (%hd, %hd, %hd, %hd) for vertex %d %s, expected (%hd, %hd, %hd, %hd).\n",
6239                     mesh_number, got[0], got[1], got[2], got[3], vertex_number, usage_strings[decl_ptr->Usage], exp[0], exp[1], exp[2], exp[3]);
6240                 break;
6241             }
6242             case D3DDECLTYPE_USHORT2N:
6243             {
6244                 USHORT *got = (USHORT*)(got_ptr + decl_ptr->Offset);
6245                 USHORT *exp = (USHORT*)(exp_ptr + decl_ptr->Offset);
6246                 BOOL same = got[0] == exp[0] && got[1] == exp[1];
6247                 ok_(__FILE__,line)(same, "Mesh %d: Got (%hu, %hu) for vertex %d %s, expected (%hu, %hu).\n",
6248                     mesh_number, got[0], got[1], vertex_number, usage_strings[decl_ptr->Usage], exp[0], exp[1]);
6249                 break;
6250             }
6251             case D3DDECLTYPE_USHORT4N:
6252             {
6253                 USHORT *got = (USHORT*)(got_ptr + decl_ptr->Offset);
6254                 USHORT *exp = (USHORT*)(exp_ptr + decl_ptr->Offset);
6255                 BOOL same = got[0] == exp[0] && got[1] == exp[1]
6256                             && got[2] == exp[2] && got[3] == exp[3];
6257                 ok_(__FILE__,line)(same, "Mesh %d: Got (%hu, %hu, %hu, %hu) for vertex %d %s, expected (%hu, %hu, %hu, %hu).\n",
6258                     mesh_number, got[0], got[1], got[2], got[3], vertex_number, usage_strings[decl_ptr->Usage], exp[0], exp[1], exp[2], exp[3]);
6259                 break;
6260             }
6261             case D3DDECLTYPE_UDEC3:
6262             {
6263                 DWORD *got = (DWORD*)(got_ptr + decl_ptr->Offset);
6264                 DWORD *exp = (DWORD*)(exp_ptr + decl_ptr->Offset);
6265                 BOOL same = memcmp(got, exp, sizeof(*got)) == 0;
6266                 struct udec3 got_udec3 = dword_to_udec3(*got);
6267                 struct udec3 exp_udec3 = dword_to_udec3(*exp);
6268                 ok_(__FILE__,line)(same, "Mesh %d: Got (%u, %u, %u, %u) for vertex %d %s, expected (%u, %u, %u, %u).\n",
6269                     mesh_number, got_udec3.x, got_udec3.y, got_udec3.z, got_udec3.w, vertex_number, usage_strings[decl_ptr->Usage], exp_udec3.x, exp_udec3.y, exp_udec3.z, exp_udec3.w);
6270
6271                 break;
6272             }
6273             case D3DDECLTYPE_DEC3N:
6274             {
6275                 DWORD *got = (DWORD*)(got_ptr + decl_ptr->Offset);
6276                 DWORD *exp = (DWORD*)(exp_ptr + decl_ptr->Offset);
6277                 BOOL same = memcmp(got, exp, sizeof(*got)) == 0;
6278                 struct dec3n got_dec3n = dword_to_dec3n(*got);
6279                 struct dec3n exp_dec3n = dword_to_dec3n(*exp);
6280                 ok_(__FILE__,line)(same, "Mesh %d: Got (%d, %d, %d, %d) for vertex %d %s, expected (%d, %d, %d, %d).\n",
6281                     mesh_number, got_dec3n.x, got_dec3n.y, got_dec3n.z, got_dec3n.w, vertex_number, usage_strings[decl_ptr->Usage], exp_dec3n.x, exp_dec3n.y, exp_dec3n.z, exp_dec3n.w);
6282                 break;
6283             }
6284             case D3DDECLTYPE_FLOAT16_2:
6285             {
6286                 WORD *got = (WORD*)(got_ptr + decl_ptr->Offset);
6287                 WORD *exp = (WORD*)(exp_ptr + decl_ptr->Offset);
6288                 BOOL same = got[0] == exp[0] && got[1] == exp[1];
6289                 ok_(__FILE__,line)(same, "Mesh %d: Got (%hx, %hx) for vertex %d %s, expected (%hx, %hx).\n",
6290                     mesh_number, got[0], got[1], vertex_number, usage_strings[decl_ptr->Usage], exp[0], exp[1]);
6291                 break;
6292             }
6293             case D3DDECLTYPE_FLOAT16_4:
6294             {
6295                 WORD *got = (WORD*)(got_ptr + decl_ptr->Offset);
6296                 WORD *exp = (WORD*)(exp_ptr + decl_ptr->Offset);
6297                 BOOL same = got[0] == exp[0] && got[1] == exp[1]
6298                             && got[2] == exp[2] && got[3] == exp[3];
6299                 ok_(__FILE__,line)(same, "Mesh %d: Got (%hx, %hx, %hx, %hx) for vertex %d %s, expected (%hx, %hx, %hx, %hx).\n",
6300                     mesh_number, got[0], got[1], got[2], got[3], vertex_number, usage_strings[decl_ptr->Usage], exp[0], exp[1], exp[3], exp[4]);
6301                 break;
6302             }
6303             default:
6304                 break;
6305         }
6306     }
6307 }
6308
6309 static void test_weld_vertices(void)
6310 {
6311     HRESULT hr;
6312     struct test_context *test_context = NULL;
6313     DWORD i;
6314     const DWORD options = D3DXMESH_32BIT | D3DXMESH_SYSTEMMEM;
6315     const DWORD options_16bit = D3DXMESH_SYSTEMMEM;
6316     BYTE *vertices = NULL;
6317     DWORD *indices = NULL;
6318     WORD *indices_16bit = NULL;
6319     const UINT VERTS_PER_FACE = 3;
6320     const D3DXVECTOR3 up = {0.0f, 0.0f, 1.0f};
6321     struct vertex_normal
6322     {
6323         D3DXVECTOR3 position;
6324         D3DXVECTOR3 normal;
6325     };
6326     struct vertex_blendweight
6327     {
6328         D3DXVECTOR3 position;
6329         FLOAT blendweight;
6330     };
6331     struct vertex_texcoord
6332     {
6333         D3DXVECTOR3 position;
6334         D3DXVECTOR2 texcoord;
6335     };
6336     struct vertex_color
6337     {
6338         D3DXVECTOR3 position;
6339         DWORD color;
6340     };
6341     struct vertex_color_ubyte4
6342     {
6343         D3DXVECTOR3 position;
6344         BYTE color[4];
6345     };
6346     struct vertex_texcoord_short2
6347     {
6348         D3DXVECTOR3 position;
6349         SHORT texcoord[2];
6350     };
6351     struct vertex_texcoord_ushort2n
6352     {
6353         D3DXVECTOR3 position;
6354         USHORT texcoord[2];
6355     };
6356     struct vertex_normal_short4
6357     {
6358         D3DXVECTOR3 position;
6359         SHORT normal[4];
6360     };
6361     struct vertex_color_float4
6362     {
6363         D3DXVECTOR3 position;
6364         D3DXVECTOR4 color;
6365     };
6366     struct vertex_texcoord_float16_2
6367     {
6368         D3DXVECTOR3 position;
6369         WORD texcoord[2];
6370     };
6371     struct vertex_texcoord_float16_4
6372     {
6373         D3DXVECTOR3 position;
6374         WORD texcoord[4];
6375     };
6376     struct vertex_normal_udec3
6377     {
6378         D3DXVECTOR3 position;
6379         DWORD normal;
6380     };
6381     struct vertex_normal_dec3n
6382     {
6383         D3DXVECTOR3 position;
6384         DWORD normal;
6385     };
6386     UINT vertex_size_normal = sizeof(struct vertex_normal);
6387     UINT vertex_size_blendweight = sizeof(struct vertex_blendweight);
6388     UINT vertex_size_texcoord = sizeof(struct vertex_texcoord);
6389     UINT vertex_size_color = sizeof(struct vertex_color);
6390     UINT vertex_size_color_ubyte4 = sizeof(struct vertex_color_ubyte4);
6391     UINT vertex_size_texcoord_short2 = sizeof(struct vertex_texcoord_short2);
6392     UINT vertex_size_normal_short4 = sizeof(struct vertex_normal_short4);
6393     UINT vertex_size_color_float4 = sizeof(struct vertex_color_float4);
6394     UINT vertex_size_texcoord_float16_2 = sizeof(struct vertex_texcoord_float16_2);
6395     UINT vertex_size_texcoord_float16_4 = sizeof(struct vertex_texcoord_float16_4);
6396     UINT vertex_size_normal_udec3 = sizeof(struct vertex_normal_udec3);
6397     UINT vertex_size_normal_dec3n = sizeof(struct vertex_normal_dec3n);
6398     D3DVERTEXELEMENT9 declaration_normal[] =
6399     {
6400         {0, 0, D3DDECLTYPE_FLOAT3, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_POSITION, 0},
6401         {0, 12, D3DDECLTYPE_FLOAT3, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_NORMAL, 0},
6402         D3DDECL_END()
6403     };
6404     D3DVERTEXELEMENT9 declaration_normal3[] =
6405     {
6406         {0, 0, D3DDECLTYPE_FLOAT3, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_POSITION, 3},
6407         {0, 12, D3DDECLTYPE_FLOAT3, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_NORMAL, 0},
6408         D3DDECL_END()
6409     };
6410     D3DVERTEXELEMENT9 declaration_blendweight[] =
6411     {
6412         {0, 0, D3DDECLTYPE_FLOAT3, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_POSITION, 0},
6413         {0, 12, D3DDECLTYPE_FLOAT1, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_BLENDWEIGHT, 0},
6414         D3DDECL_END()
6415     };
6416     D3DVERTEXELEMENT9 declaration_texcoord[] =
6417     {
6418         {0, 0, D3DDECLTYPE_FLOAT3, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_POSITION, 0},
6419         {0, 12, D3DDECLTYPE_FLOAT2, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_TEXCOORD, 0},
6420         D3DDECL_END()
6421     };
6422     D3DVERTEXELEMENT9 declaration_color[] =
6423     {
6424         {0, 0, D3DDECLTYPE_FLOAT3, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_POSITION, 0},
6425         {0, 12, D3DDECLTYPE_D3DCOLOR, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_COLOR, 0},
6426         D3DDECL_END()
6427     };
6428     D3DVERTEXELEMENT9 declaration_color_ubyte4n[] =
6429     {
6430         {0, 0, D3DDECLTYPE_FLOAT3, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_POSITION, 0},
6431         {0, 12, D3DDECLTYPE_UBYTE4N, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_COLOR, 0},
6432         D3DDECL_END()
6433     };
6434     D3DVERTEXELEMENT9 declaration_color_ubyte4[] =
6435     {
6436         {0, 0, D3DDECLTYPE_FLOAT3, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_POSITION, 0},
6437         {0, 12, D3DDECLTYPE_UBYTE4, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_COLOR, 0},
6438         D3DDECL_END()
6439     };
6440     D3DVERTEXELEMENT9 declaration_texcoord_short2[] =
6441     {
6442         {0, 0, D3DDECLTYPE_FLOAT3, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_POSITION, 0},
6443         {0, 12, D3DDECLTYPE_SHORT2, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_TEXCOORD, 0},
6444         D3DDECL_END()
6445     };
6446     D3DVERTEXELEMENT9 declaration_texcoord_short2n[] =
6447     {
6448         {0, 0, D3DDECLTYPE_FLOAT3, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_POSITION, 0},
6449         {0, 12, D3DDECLTYPE_SHORT2N, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_TEXCOORD, 0},
6450         D3DDECL_END()
6451     };
6452     D3DVERTEXELEMENT9 declaration_texcoord_ushort2n[] =
6453     {
6454         {0, 0, D3DDECLTYPE_FLOAT3, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_POSITION, 0},
6455         {0, 12, D3DDECLTYPE_USHORT2N, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_TEXCOORD, 0},
6456         D3DDECL_END()
6457     };
6458     D3DVERTEXELEMENT9 declaration_normal_short4[] =
6459     {
6460         {0, 0, D3DDECLTYPE_FLOAT3, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_POSITION, 0},
6461         {0, 12, D3DDECLTYPE_SHORT4, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_NORMAL, 0},
6462         D3DDECL_END()
6463     };
6464     D3DVERTEXELEMENT9 declaration_normal_short4n[] =
6465     {
6466         {0, 0, D3DDECLTYPE_FLOAT3, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_POSITION, 0},
6467         {0, 12, D3DDECLTYPE_SHORT4N, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_NORMAL, 0},
6468         D3DDECL_END()
6469     };
6470     D3DVERTEXELEMENT9 declaration_normal_ushort4n[] =
6471     {
6472         {0, 0, D3DDECLTYPE_FLOAT3, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_POSITION, 0},
6473         {0, 12, D3DDECLTYPE_USHORT4N, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_NORMAL, 0},
6474         D3DDECL_END()
6475     };
6476     D3DVERTEXELEMENT9 declaration_texcoord10[] =
6477     {
6478         {0, 0, D3DDECLTYPE_FLOAT3, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_POSITION, 0},
6479         {0, 12, D3DDECLTYPE_FLOAT2, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_TEXCOORD, 10},
6480         D3DDECL_END()
6481     };
6482     D3DVERTEXELEMENT9 declaration_color2[] =
6483     {
6484         {0, 0, D3DDECLTYPE_FLOAT3, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_POSITION, 0},
6485         {0, 12, D3DDECLTYPE_D3DCOLOR, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_COLOR, 2},
6486         D3DDECL_END()
6487     };
6488     D3DVERTEXELEMENT9 declaration_color2_float4[] =
6489     {
6490         {0, 0, D3DDECLTYPE_FLOAT3, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_POSITION, 0},
6491         {0, 12, D3DDECLTYPE_FLOAT4, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_COLOR, 2},
6492         D3DDECL_END()
6493     };
6494     D3DVERTEXELEMENT9 declaration_texcoord_float16_2[] =
6495     {
6496         {0, 0, D3DDECLTYPE_FLOAT3, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_POSITION, 0},
6497         {0, 12, D3DDECLTYPE_FLOAT16_2, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_TEXCOORD, 0},
6498         D3DDECL_END()
6499     };
6500     D3DVERTEXELEMENT9 declaration_texcoord_float16_4[] =
6501     {
6502         {0, 0, D3DDECLTYPE_FLOAT3, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_POSITION, 0},
6503         {0, 12, D3DDECLTYPE_FLOAT16_4, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_TEXCOORD, 0},
6504         D3DDECL_END()
6505     };
6506     D3DVERTEXELEMENT9 declaration_normal_udec3[] =
6507    {
6508         {0, 0, D3DDECLTYPE_FLOAT3, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_POSITION, 0},
6509         {0, 12, D3DDECLTYPE_UDEC3, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_NORMAL, 0},
6510         D3DDECL_END()
6511     };
6512     D3DVERTEXELEMENT9 declaration_normal_dec3n[] =
6513     {
6514         {0, 0, D3DDECLTYPE_FLOAT3, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_POSITION, 0},
6515         {0, 12, D3DDECLTYPE_DEC3N, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_NORMAL, 0},
6516         D3DDECL_END()
6517     };
6518     /* Test 0. One face and no welding.
6519      *
6520      * 0--1
6521      * | /
6522      * |/
6523      * 2
6524      */
6525     const struct vertex vertices0[] =
6526     {
6527         {{ 0.0f,  3.0f,  0.f}, up},
6528         {{ 2.0f,  3.0f,  0.f}, up},
6529         {{ 0.0f,  0.0f,  0.f}, up},
6530     };
6531     const DWORD indices0[] = {0, 1, 2};
6532     const DWORD attributes0[] = {0};
6533     const DWORD exp_indices0[] = {0, 1, 2};
6534     const UINT num_vertices0 = ARRAY_SIZE(vertices0);
6535     const UINT num_faces0 = ARRAY_SIZE(indices0) / VERTS_PER_FACE;
6536     const DWORD flags0 = D3DXWELDEPSILONS_WELDALL;
6537     /* epsilons0 is NULL */
6538     const DWORD adjacency0[] = {-1, -1, -1};
6539     const struct vertex exp_vertices0[] =
6540     {
6541         {{ 0.0f,  3.0f,  0.f}, up},
6542         {{ 2.0f,  3.0f,  0.f}, up},
6543         {{ 0.0f,  0.0f,  0.f}, up},
6544     };
6545     const DWORD exp_face_remap0[] = {0};
6546     const DWORD exp_vertex_remap0[] = {0, 1, 2};
6547     const DWORD exp_new_num_vertices0 = ARRAY_SIZE(exp_vertices0);
6548     /* Test 1. Two vertices should be removed without regard to epsilon.
6549      *
6550      * 0--1 3
6551      * | / /|
6552      * |/ / |
6553      * 2 5--4
6554      */
6555     const struct vertex_normal vertices1[] =
6556     {
6557         {{ 0.0f,  3.0f,  0.f}, up},
6558         {{ 2.0f,  3.0f,  0.f}, up},
6559         {{ 0.0f,  0.0f,  0.f}, up},
6560
6561         {{ 3.0f,  3.0f,  0.f}, up},
6562         {{ 3.0f,  0.0f,  0.f}, up},
6563         {{ 1.0f,  0.0f,  0.f}, up},
6564     };
6565     const DWORD indices1[] = {0, 1, 2, 3, 4, 5};
6566     const DWORD attributes1[] = {0, 0};
6567     const UINT num_vertices1 = ARRAY_SIZE(vertices1);
6568     const UINT num_faces1 = ARRAY_SIZE(indices1) / VERTS_PER_FACE;
6569     const DWORD flags1 = D3DXWELDEPSILONS_WELDALL;
6570     /* epsilons1 is NULL */
6571     const DWORD adjacency1[] = {-1, 1, -1, -1, -1, 0};
6572     const struct vertex_normal exp_vertices1[] =
6573     {
6574         {{ 0.0f,  3.0f,  0.f}, up},
6575         {{ 2.0f,  3.0f,  0.f}, up},
6576         {{ 0.0f,  0.0f,  0.f}, up},
6577
6578         {{ 3.0f,  0.0f,  0.f}, up}
6579     };
6580     const DWORD exp_indices1[] = {0, 1, 2, 1, 3, 2};
6581     const DWORD exp_face_remap1[] = {0, 1};
6582     const DWORD exp_vertex_remap1[] = {0, 1, 2, 4, -1, -1};
6583     const DWORD exp_new_num_vertices1 = ARRAY_SIZE(exp_vertices1);
6584     /* Test 2. Two faces. No vertices should be removed because of normal
6585      * epsilon, but the positions should be replaced. */
6586     const struct vertex_normal vertices2[] =
6587     {
6588         {{ 0.0f,  3.0f,  0.f}, up},
6589         {{ 2.0f,  3.0f,  0.f}, up},
6590         {{ 0.0f,  0.0f,  0.f}, up},
6591
6592         {{ 3.0f,  3.0f,  0.f}, {0.0f, 0.5f, 0.5f}},
6593         {{ 3.0f,  0.0f,  0.f}, up},
6594         {{ 1.0f,  0.0f,  0.f}, {0.2f, 0.4f, 0.4f}},
6595     };
6596     const DWORD indices2[] = {0, 1, 2, 3, 4, 5};
6597     const DWORD attributes2[] = {0, 0};
6598     const UINT num_vertices2 = ARRAY_SIZE(vertices2);
6599     const UINT num_faces2 = ARRAY_SIZE(indices2) / VERTS_PER_FACE;
6600     DWORD flags2 = D3DXWELDEPSILONS_WELDPARTIALMATCHES;
6601     const D3DXWELDEPSILONS epsilons2 = {1.0f, 0.0f, 0.499999f, 0.0f, 0.0f, 0.0f, {0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, 0.0f, 0.0f, 0.0f};
6602     const DWORD adjacency2[] = {-1, 1, -1, -1, -1, 0};
6603     const struct vertex_normal exp_vertices2[] =
6604     {
6605         {{ 0.0f,  3.0f,  0.f}, up},
6606         {{ 2.0f,  3.0f,  0.f}, up},
6607         {{ 0.0f,  0.0f,  0.f}, up},
6608
6609         {{ 2.0f,  3.0f,  0.f}, {0.0f, 0.5f, 0.5f}},
6610         {{ 3.0f,  0.0f,  0.f}, up},
6611         {{ 0.0f,  0.0f,  0.f}, {0.2f, 0.4f, 0.4f}},
6612     };
6613     const DWORD exp_indices2[] = {0, 1, 2, 3, 4, 5};
6614     const DWORD exp_face_remap2[] = {0, 1};
6615     const DWORD exp_vertex_remap2[] = {0, 1, 2, 3, 4, 5};
6616     const DWORD exp_new_num_vertices2 = ARRAY_SIZE(exp_vertices2);
6617     /* Test 3. Two faces. One vertex should be removed because of normal epsilon. */
6618     const struct vertex_normal vertices3[] =
6619     {
6620         {{ 0.0f,  3.0f,  0.f}, up},
6621         {{ 2.0f,  3.0f,  0.f}, up},
6622         {{ 0.0f,  0.0f,  0.f}, up},
6623
6624         {{ 3.0f,  3.0f,  0.f}, {0.0f, 0.5f, 0.5f}},
6625         {{ 3.0f,  0.0f,  0.f}, up},
6626         {{ 1.0f,  0.0f,  0.f}, {0.2f, 0.4f, 0.4f}},
6627     };
6628     const DWORD indices3[] = {0, 1, 2, 3, 4, 5};
6629     const DWORD attributes3[] = {0, 0};
6630     const UINT num_vertices3 = ARRAY_SIZE(vertices3);
6631     const UINT num_faces3 = ARRAY_SIZE(indices3) / VERTS_PER_FACE;
6632     DWORD flags3 = D3DXWELDEPSILONS_WELDPARTIALMATCHES;
6633     const D3DXWELDEPSILONS epsilons3 = {1.0f, 0.0f, 0.5f, 0.0f, 0.0f, 0.0f, {0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, 0.0f, 0.0f, 0.0f};
6634     const DWORD adjacency3[] = {-1, 1, -1, -1, -1, 0};
6635     const struct vertex_normal exp_vertices3[] =
6636     {
6637         {{ 0.0f,  3.0f,  0.f}, up},
6638         {{ 2.0f,  3.0f,  0.f}, up},
6639         {{ 0.0f,  0.0f,  0.f}, up},
6640
6641         {{ 3.0f,  0.0f,  0.f}, up},
6642         {{ 0.0f,  0.0f,  0.f}, {0.2f, 0.4f, 0.4f}},
6643     };
6644     const DWORD exp_indices3[] = {0, 1, 2, 1, 3, 4};
6645     const DWORD exp_face_remap3[] = {0, 1};
6646     const DWORD exp_vertex_remap3[] = {0, 1, 2, 4, 5, -1};
6647     const DWORD exp_new_num_vertices3 = ARRAY_SIZE(exp_vertices3);
6648     /* Test 4  Two faces. Two vertices should be removed. */
6649     const struct vertex_normal vertices4[] =
6650     {
6651         {{ 0.0f,  3.0f,  0.f}, up},
6652         {{ 2.0f,  3.0f,  0.f}, up},
6653         {{ 0.0f,  0.0f,  0.f}, up},
6654
6655         {{ 3.0f,  3.0f,  0.f}, {0.0f, 0.5f, 0.5f}},
6656         {{ 3.0f,  0.0f,  0.f}, up},
6657         {{ 1.0f,  0.0f,  0.f}, {0.2f, 0.4f, 0.4f}},
6658     };
6659     const DWORD indices4[] = {0, 1, 2, 3, 4, 5};
6660     const DWORD attributes4[] = {0, 0};
6661     const UINT num_vertices4 = ARRAY_SIZE(vertices4);
6662     const UINT num_faces4 = ARRAY_SIZE(indices4) / VERTS_PER_FACE;
6663     DWORD flags4 = D3DXWELDEPSILONS_WELDPARTIALMATCHES;
6664     const D3DXWELDEPSILONS epsilons4 = {1.0f, 0.0f, 0.6f, 0.0f, 0.0f, 0.0f, {0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, 0.0f, 0.0f, 0.0f};
6665     const DWORD adjacency4[] = {-1, 1, -1, -1, -1, 0};
6666     const struct vertex_normal exp_vertices4[] =
6667     {
6668         {{ 0.0f,  3.0f,  0.f}, up},
6669         {{ 2.0f,  3.0f,  0.f}, up},
6670         {{ 0.0f,  0.0f,  0.f}, up},
6671
6672         {{ 3.0f,  0.0f,  0.f}, up},
6673     };
6674     const DWORD exp_indices4[] = {0, 1, 2, 1, 3, 2};
6675     const DWORD exp_face_remap4[] = {0, 1};
6676     const DWORD exp_vertex_remap4[] = {0, 1, 2, 4, -1, -1};
6677     const DWORD exp_new_num_vertices4 = ARRAY_SIZE(exp_vertices4);
6678     /* Test 5. Odd face ordering.
6679      *
6680      * 0--1 6 3
6681      * | / /| |\
6682      * |/ / | | \
6683      * 2 8--7 5--4
6684      */
6685     const struct vertex_normal vertices5[] =
6686     {
6687         {{ 0.0f,  3.0f,  0.f}, up},
6688         {{ 2.0f,  3.0f,  0.f}, up},
6689         {{ 0.0f,  0.0f,  0.f}, up},
6690
6691         {{ 3.0f,  3.0f,  0.f}, up},
6692         {{ 3.0f,  0.0f,  0.f}, up},
6693         {{ 1.0f,  0.0f,  0.f}, up},
6694
6695         {{ 4.0f,  3.0f,  0.f}, up},
6696         {{ 6.0f,  0.0f,  0.f}, up},
6697         {{ 4.0f,  0.0f,  0.f}, up},
6698     };
6699     const DWORD indices5[] = {0, 1, 2, 6, 7, 8, 3, 4, 5};
6700     const DWORD exp_indices5[] = {0, 1, 2, 1, 4, 2, 1, 3, 4};
6701     const DWORD attributes5[] = {0, 0, 0};
6702     const UINT num_vertices5 = ARRAY_SIZE(vertices5);
6703     const UINT num_faces5 = ARRAY_SIZE(indices5) / VERTS_PER_FACE;
6704     DWORD flags5 = D3DXWELDEPSILONS_WELDALL;
6705     const DWORD adjacency5[] = {-1, 1, -1, 2, -1, 0, -1, -1, 1};
6706     const struct vertex_normal exp_vertices5[] =
6707     {
6708         {{ 0.0f,  3.0f,  0.f}, up},
6709         {{ 2.0f,  3.0f,  0.f}, up},
6710         {{ 0.0f,  0.0f,  0.f}, up},
6711
6712         {{ 3.0f,  0.0f,  0.f}, up},
6713         {{ 1.0f,  0.0f,  0.f}, up},
6714     };
6715     const DWORD exp_face_remap5[] = {0, 1, 2};
6716     const DWORD exp_vertex_remap5[] = {0, 1, 2, 4, 5, -1, -1, -1, -1};
6717     const DWORD exp_new_num_vertices5 = ARRAY_SIZE(exp_vertices5);
6718     /* Test 6. Two faces. Do not remove flag is used, so no vertices should be
6719      * removed. */
6720     const struct vertex_normal vertices6[] =
6721     {
6722         {{ 0.0f,  3.0f,  0.f}, up},
6723         {{ 2.0f,  3.0f,  0.f}, up},
6724         {{ 0.0f,  0.0f,  0.f}, up},
6725
6726         {{ 3.0f,  3.0f,  0.f}, {0.0f, 0.5f, 0.5f}},
6727         {{ 3.0f,  0.0f,  0.f}, up},
6728         {{ 1.0f,  0.0f,  0.f}, {0.2f, 0.4f, 0.4f}},
6729     };
6730     const DWORD indices6[] = {0, 1, 2, 3, 4, 5};
6731     const DWORD attributes6[] = {0, 0};
6732     const UINT num_vertices6 = ARRAY_SIZE(vertices6);
6733     const UINT num_faces6 = ARRAY_SIZE(indices6) / VERTS_PER_FACE;
6734     DWORD flags6 = D3DXWELDEPSILONS_WELDPARTIALMATCHES | D3DXWELDEPSILONS_DONOTREMOVEVERTICES;
6735     const D3DXWELDEPSILONS epsilons6 = {1.0f, 0.0f, 0.6f, 0.0f, 0.0f, 0.0f, {0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, 0.0f, 0.0f, 0.0f};
6736     const DWORD adjacency6[] = {-1, 1, -1, -1, -1, 0};
6737     const struct vertex_normal exp_vertices6[] =
6738     {
6739         {{ 0.0f,  3.0f,  0.f}, up},
6740         {{ 2.0f,  3.0f,  0.f}, up},
6741         {{ 0.0f,  0.0f,  0.f}, up},
6742
6743         {{ 2.0f,  3.0f,  0.f}, up},
6744         {{ 3.0f,  0.0f,  0.f}, up},
6745         {{ 0.0f,  0.0f,  0.f}, up},
6746
6747     };
6748     const DWORD exp_indices6[] = {0, 1, 2, 3, 4, 5};
6749     const DWORD exp_face_remap6[] = {0, 1};
6750     const DWORD exp_vertex_remap6[] = {0, 1, 2, 3, 4, 5};
6751     const DWORD exp_new_num_vertices6 = ARRAY_SIZE(exp_vertices6);
6752     /* Test 7. Same as test 6 but with 16 bit indices. */
6753     const WORD indices6_16bit[] = {0, 1, 2, 3, 4, 5};
6754     /* Test 8. No flags. Same result as D3DXWELDEPSILONS_WELDPARTIALMATCHES. */
6755     const struct vertex_normal vertices8[] =
6756     {
6757         {{ 0.0f,  3.0f,  0.f}, up},
6758         {{ 2.0f,  3.0f,  0.f}, up},
6759         {{ 0.0f,  0.0f,  0.f}, up},
6760
6761         {{ 3.0f,  3.0f,  0.f}, {0.0f, 0.5f, 0.5f}},
6762         {{ 3.0f,  0.0f,  0.f}, up},
6763         {{ 1.0f,  0.0f,  0.f}, {0.2f, 0.4f, 0.4f}},
6764     };
6765     const DWORD indices8[] = {0, 1, 2, 1, 3, 4};
6766     const DWORD attributes8[] = {0, 0};
6767     const UINT num_vertices8 = ARRAY_SIZE(vertices8);
6768     const UINT num_faces8 = ARRAY_SIZE(indices8) / VERTS_PER_FACE;
6769     DWORD flags8 = 0;
6770     const D3DXWELDEPSILONS epsilons8 = {1.0f, 0.0f, 0.5f, 0.0f, 0.0f, 0.0f, {0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, 0.0f, 0.0f, 0.0f};
6771     const DWORD adjacency8[] = {-1, 1, -1, -1, -1, 0};
6772     const struct vertex_normal exp_vertices8[] =
6773     {
6774         {{ 0.0f,  3.0f,  0.f}, up},
6775         {{ 2.0f,  3.0f,  0.f}, up},
6776         {{ 0.0f,  0.0f,  0.f}, up},
6777
6778         {{ 3.0f,  3.0f,  0.f}, {0.0f, 0.5f, 0.5f}},
6779         {{ 3.0f,  0.0f,  0.f}, up},
6780     };
6781     const DWORD exp_indices8[] = {0, 1, 2, 1, 3, 4};
6782     const DWORD exp_face_remap8[] = {0, 1};
6783     const DWORD exp_vertex_remap8[] = {0, 1, 2, 3, 4, -1};
6784     const DWORD exp_new_num_vertices8 = ARRAY_SIZE(exp_vertices8);
6785     /* Test 9. Vertices are removed even though they belong to separate
6786      * attribute groups if D3DXWELDEPSILONS_DONOTSPLIT is set. */
6787     const struct vertex_normal vertices9[] =
6788     {
6789         {{ 0.0f,  3.0f,  0.f}, up},
6790         {{ 2.0f,  3.0f,  0.f}, up},
6791         {{ 0.0f,  0.0f,  0.f}, up},
6792
6793         {{ 3.0f,  3.0f,  0.f}, {0.0f, 0.5f, 0.5f}},
6794         {{ 3.0f,  0.0f,  0.f}, up},
6795         {{ 1.0f,  0.0f,  0.f}, {0.2f, 0.4f, 0.4f}},
6796     };
6797     const DWORD indices9[] = {0, 1, 2, 3, 4, 5};
6798     const DWORD attributes9[] = {0, 1};
6799     const UINT num_vertices9 = ARRAY_SIZE(vertices9);
6800     const UINT num_faces9 = ARRAY_SIZE(indices9) / VERTS_PER_FACE;
6801     DWORD flags9 = D3DXWELDEPSILONS_WELDPARTIALMATCHES | D3DXWELDEPSILONS_DONOTSPLIT;
6802     const D3DXWELDEPSILONS epsilons9 = {1.0f, 0.0f, 0.6f, 0.0f, 0.0f, 0.0f, {0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, 0.0f, 0.0f, 0.0f};
6803     const DWORD adjacency9[] = {-1, 1, -1, -1, -1, 0};
6804     const struct vertex_normal exp_vertices9[] =
6805     {
6806         {{ 0.0f,  3.0f,  0.f}, up},
6807         {{ 2.0f,  3.0f,  0.f}, up},
6808         {{ 0.0f,  0.0f,  0.f}, up},
6809
6810         {{ 3.0f,  0.0f,  0.f}, up},
6811     };
6812     const DWORD exp_indices9[] = {0, 1, 2, 1, 3, 2};
6813     const DWORD exp_face_remap9[] = {0, 1};
6814     const DWORD exp_vertex_remap9[] = {0, 1, 2, 4, -1, -1};
6815     const DWORD exp_new_num_vertices9 = ARRAY_SIZE(exp_vertices9);
6816     /* Test 10. Weld blendweight (FLOAT1). */
6817     const struct vertex_blendweight vertices10[] =
6818     {
6819         {{ 0.0f,  3.0f,  0.f}, 1.0f},
6820         {{ 2.0f,  3.0f,  0.f}, 1.0f},
6821         {{ 0.0f,  0.0f,  0.f}, 1.0f},
6822
6823         {{ 3.0f,  3.0f,  0.f}, 0.9},
6824         {{ 3.0f,  0.0f,  0.f}, 1.0},
6825         {{ 1.0f,  0.0f,  0.f}, 0.4},
6826     };
6827     const DWORD indices10[] = {0, 1, 2, 3, 4, 5};
6828     const DWORD attributes10[] = {0, 0};
6829     const UINT num_vertices10 = ARRAY_SIZE(vertices10);
6830     const UINT num_faces10 = ARRAY_SIZE(indices10) / VERTS_PER_FACE;
6831     DWORD flags10 = D3DXWELDEPSILONS_WELDPARTIALMATCHES;
6832     const D3DXWELDEPSILONS epsilons10 = {1.0f, 0.1f + FLT_EPSILON, 0.0f, 0.0f, 0.0f, 0.0f, {0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, 0.0f, 0.0f, 0.0f};
6833     const DWORD adjacency10[] = {-1, 1, -1, -1, -1, 0};
6834     const struct vertex_blendweight exp_vertices10[] =
6835     {
6836         {{ 0.0f,  3.0f,  0.f}, 1.0f},
6837         {{ 2.0f,  3.0f,  0.f}, 1.0f},
6838         {{ 0.0f,  0.0f,  0.f}, 1.0f},
6839
6840         {{ 3.0f,  0.0f,  0.f}, 1.0},
6841         {{ 0.0f,  0.0f,  0.f}, 0.4},
6842     };
6843     const DWORD exp_indices10[] = {0, 1, 2, 1, 3, 4};
6844     const DWORD exp_face_remap10[] = {0, 1};
6845     const DWORD exp_vertex_remap10[] = {0, 1, 2, 4, 5, -1};
6846     const DWORD exp_new_num_vertices10 = ARRAY_SIZE(exp_vertices10);
6847     /* Test 11. Weld texture coordinates. */
6848     const struct vertex_texcoord vertices11[] =
6849     {
6850         {{ 0.0f,  3.0f,  0.f}, {1.0f, 1.0f}},
6851         {{ 2.0f,  3.0f,  0.f}, {0.5f, 0.7f}},
6852         {{ 0.0f,  0.0f,  0.f}, {-0.2f, -0.3f}},
6853
6854         {{ 3.0f,  3.0f,  0.f}, {0.2f, 0.3f}},
6855         {{ 3.0f,  0.0f,  0.f}, {1.0f, 1.0f}},
6856         {{ 1.0f,  0.0f,  0.f}, {0.1f, 0.2f}}
6857     };
6858     const DWORD indices11[] = {0, 1, 2, 3, 4, 5};
6859     const DWORD attributes11[] = {0, 0};
6860     const UINT num_vertices11 = ARRAY_SIZE(vertices11);
6861     const UINT num_faces11 = ARRAY_SIZE(indices11) / VERTS_PER_FACE;
6862     DWORD flags11 = D3DXWELDEPSILONS_WELDPARTIALMATCHES;
6863     const D3DXWELDEPSILONS epsilons11 = {1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, {0.4f + FLT_EPSILON, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, 0.0f, 0.0f, 0.0f};
6864     const DWORD adjacency11[] = {-1, 1, -1, -1, -1, 0};
6865     const struct vertex_texcoord exp_vertices11[] =
6866     {
6867         {{ 0.0f,  3.0f,  0.f}, {1.0f, 1.0f}},
6868         {{ 2.0f,  3.0f,  0.f}, {0.5f, 0.7f}},
6869         {{ 0.0f,  0.0f,  0.f}, {-0.2f, -0.3f}},
6870
6871         {{ 3.0f,  0.0f,  0.f}, {1.0f, 1.0f}},
6872         {{ 0.0f,  0.0f,  0.f}, {0.1f, 0.2f}},
6873     };
6874     const DWORD exp_indices11[] = {0, 1, 2, 1, 3, 4};
6875     const DWORD exp_face_remap11[] = {0, 1};
6876     const DWORD exp_vertex_remap11[] = {0, 1, 2, 4, 5, -1};
6877     const DWORD exp_new_num_vertices11 = ARRAY_SIZE(exp_vertices11);
6878     /* Test 12. Weld with color. */
6879     const struct vertex_color vertices12[] =
6880     {
6881         {{ 0.0f,  3.0f,  0.f}, 0xFFFFFFFF},
6882         {{ 2.0f,  3.0f,  0.f}, 0xFFFFFFFF},
6883         {{ 0.0f,  0.0f,  0.f}, 0xFFFFFFFF},
6884
6885         {{ 3.0f,  3.0f,  0.f}, 0x00000000},
6886         {{ 3.0f,  0.0f,  0.f}, 0xFFFFFFFF},
6887         {{ 1.0f,  0.0f,  0.f}, 0x88888888},
6888     };
6889     const DWORD indices12[] = {0, 1, 2, 3, 4, 5};
6890     const DWORD attributes12[] = {0, 0};
6891     const UINT num_vertices12 = ARRAY_SIZE(vertices12);
6892     const UINT num_faces12 = ARRAY_SIZE(indices12) / VERTS_PER_FACE;
6893     DWORD flags12 = D3DXWELDEPSILONS_WELDPARTIALMATCHES;
6894     const D3DXWELDEPSILONS epsilons12 = {1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.5f, {0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, 0.0f, 0.0f, 0.0f};
6895     const DWORD adjacency12[] = {-1, 1, -1, -1, -1, 0};
6896     const struct vertex_color exp_vertices12[] =
6897     {
6898         {{ 0.0f,  3.0f,  0.f}, 0xFFFFFFFF},
6899         {{ 2.0f,  3.0f,  0.f}, 0xFFFFFFFF},
6900         {{ 0.0f,  0.0f,  0.f}, 0xFFFFFFFF},
6901
6902         {{ 2.0f,  3.0f,  0.f}, 0x00000000},
6903         {{ 3.0f,  0.0f,  0.f}, 0xFFFFFFFF},
6904     };
6905     const DWORD exp_indices12[] = {0, 1, 2, 3, 4, 2};
6906     const DWORD exp_face_remap12[] = {0, 1};
6907     const DWORD exp_vertex_remap12[] = {0, 1, 2, 3, 4, -1};
6908     const DWORD exp_new_num_vertices12 = ARRAY_SIZE(exp_vertices12);
6909     /* Test 13. Two faces. One vertex should be removed because of normal epsilon.
6910      * This is similar to test 3, but the declaration has been changed to NORMAL3.
6911      */
6912     const struct vertex_normal vertices13[] =
6913     {
6914         {{ 0.0f,  3.0f,  0.f}, up},
6915         {{ 2.0f,  3.0f,  0.f}, up},
6916         {{ 0.0f,  0.0f,  0.f}, up},
6917
6918         {{ 3.0f,  3.0f,  0.f}, {0.0f, 0.5f, 0.5f}},
6919         {{ 3.0f,  0.0f,  0.f}, up},
6920         {{ 1.0f,  0.0f,  0.f}, {0.2f, 0.4f, 0.4f}},
6921     };
6922     const DWORD indices13[] = {0, 1, 2, 3, 4, 5};
6923     const DWORD attributes13[] = {0, 0};
6924     const UINT num_vertices13 = ARRAY_SIZE(vertices3);
6925     const UINT num_faces13 = ARRAY_SIZE(indices3) / VERTS_PER_FACE;
6926     DWORD flags13 = D3DXWELDEPSILONS_WELDPARTIALMATCHES;
6927     const D3DXWELDEPSILONS epsilons13 = {1.0f, 0.0f, 0.5f, 0.0f, 0.0f, 0.0f, {0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, 0.0f, 0.0f, 0.0f};
6928     const DWORD adjacency13[] = {-1, 1, -1, -1, -1, 0};
6929     const struct vertex_normal exp_vertices13[] =
6930     {
6931         {{ 0.0f,  3.0f,  0.f}, up},
6932         {{ 2.0f,  3.0f,  0.f}, up},
6933         {{ 0.0f,  0.0f,  0.f}, up},
6934
6935         {{ 3.0f,  0.0f,  0.f}, up},
6936         {{ 0.0f,  0.0f,  0.f}, {0.2f, 0.4f, 0.4f}},
6937     };
6938     const DWORD exp_indices13[] = {0, 1, 2, 1, 3, 4};
6939     const DWORD exp_face_remap13[] = {0, 1};
6940     const DWORD exp_vertex_remap13[] = {0, 1, 2, 4, 5, -1};
6941     const DWORD exp_new_num_vertices13 = ARRAY_SIZE(exp_vertices13);
6942     /* Test 14. Another test for welding with color. */
6943     const struct vertex_color vertices14[] =
6944     {
6945         {{ 0.0f,  3.0f,  0.f}, 0xFFFFFFFF},
6946         {{ 2.0f,  3.0f,  0.f}, 0xFFFFFFFF},
6947         {{ 0.0f,  0.0f,  0.f}, 0xFFFFFFFF},
6948
6949         {{ 3.0f,  3.0f,  0.f}, 0x00000000},
6950         {{ 3.0f,  0.0f,  0.f}, 0xFFFFFFFF},
6951         {{ 1.0f,  0.0f,  0.f}, 0x01010101},
6952     };
6953     const DWORD indices14[] = {0, 1, 2, 3, 4, 5};
6954     const DWORD attributes14[] = {0, 0};
6955     const UINT num_vertices14 = ARRAY_SIZE(vertices14);
6956     const UINT num_faces14 = ARRAY_SIZE(indices14) / VERTS_PER_FACE;
6957     DWORD flags14 = D3DXWELDEPSILONS_WELDPARTIALMATCHES;
6958     const D3DXWELDEPSILONS epsilons14 = {1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 254.0f/255.0f, {0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, 0.0f, 0.0f, 0.0f};
6959     const DWORD adjacency14[] = {-1, 1, -1, -1, -1, 0};
6960     const struct vertex_color exp_vertices14[] =
6961     {
6962         {{ 0.0f,  3.0f,  0.f}, 0xFFFFFFFF},
6963         {{ 2.0f,  3.0f,  0.f}, 0xFFFFFFFF},
6964         {{ 0.0f,  0.0f,  0.f}, 0xFFFFFFFF},
6965
6966         {{ 2.0f,  3.0f,  0.f}, 0x00000000},
6967         {{ 3.0f,  0.0f,  0.f}, 0xFFFFFFFF},
6968     };
6969     const DWORD exp_indices14[] = {0, 1, 2, 3, 4, 2};
6970     const DWORD exp_face_remap14[] = {0, 1};
6971     const DWORD exp_vertex_remap14[] = {0, 1, 2, 3, 4, -1};
6972     const DWORD exp_new_num_vertices14 = ARRAY_SIZE(exp_vertices14);
6973     /* Test 15. Weld with color, but as UBYTE4N instead of D3DCOLOR. It shows
6974      * that UBYTE4N and D3DCOLOR are compared the same way.
6975      */
6976     const struct vertex_color_ubyte4 vertices15[] =
6977     {
6978         {{ 0.0f,  3.0f,  0.f}, {255, 255, 255, 255}},
6979         {{ 2.0f,  3.0f,  0.f}, {255, 255, 255, 255}},
6980         {{ 0.0f,  0.0f,  0.f}, {255, 255, 255, 255}},
6981
6982         {{ 3.0f,  3.0f,  0.f}, {  0,   0,   0,   0}},
6983         {{ 3.0f,  0.0f,  0.f}, {255, 255, 255, 255}},
6984         {{ 1.0f,  0.0f,  0.f}, {  1,   1,   1,   1}},
6985     };
6986     const DWORD indices15[] = {0, 1, 2, 3, 4, 5};
6987     const DWORD attributes15[] = {0, 0};
6988     const UINT num_vertices15 = ARRAY_SIZE(vertices15);
6989     const UINT num_faces15 = ARRAY_SIZE(indices15) / VERTS_PER_FACE;
6990     DWORD flags15 = D3DXWELDEPSILONS_WELDPARTIALMATCHES;
6991     const D3DXWELDEPSILONS epsilons15 = {1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 254.0f/255.0f, {0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, 0.0f, 0.0f, 0.0f};
6992     const DWORD adjacency15[] = {-1, 1, -1, -1, -1, 0};
6993     const struct vertex_color_ubyte4 exp_vertices15[] =
6994     {
6995         {{ 0.0f,  3.0f,  0.f}, {255, 255, 255, 255}},
6996         {{ 2.0f,  3.0f,  0.f}, {255, 255, 255, 255}},
6997         {{ 0.0f,  0.0f,  0.f}, {255, 255, 255, 255}},
6998
6999         {{ 2.0f,  3.0f,  0.f}, {  0,   0,   0,   0}},
7000         {{ 3.0f,  0.0f,  0.f}, {255, 255, 255, 255}},
7001     };
7002     const DWORD exp_indices15[] = {0, 1, 2, 3, 4, 2};
7003     const DWORD exp_face_remap15[] = {0, 1};
7004     const DWORD exp_vertex_remap15[] = {0, 1, 2, 3, 4, -1};
7005     const DWORD exp_new_num_vertices15 = ARRAY_SIZE(exp_vertices15);
7006     /* Test 16. Weld with color, but as UBYTE4 instead of D3DCOLOR. It shows
7007      * that UBYTE4 is not normalized and that epsilon is truncated and compared
7008      * directly to each of the four bytes.
7009      */
7010     const struct vertex_color_ubyte4 vertices16[] =
7011     {
7012         {{ 0.0f,  3.0f,  0.f}, {255, 255, 255, 255}},
7013         {{ 2.0f,  3.0f,  0.f}, {255, 255, 255, 255}},
7014         {{ 0.0f,  0.0f,  0.f}, {255, 255, 255, 255}},
7015
7016         {{ 3.0f,  3.0f,  0.f}, {  0,   0,   0,   0}},
7017         {{ 3.0f,  0.0f,  0.f}, {255, 255, 255, 255}},
7018         {{ 1.0f,  0.0f,  0.f}, {  1,   1,   1,   1}},
7019     };
7020     const DWORD indices16[] = {0, 1, 2, 3, 4, 5};
7021     const DWORD attributes16[] = {0, 0};
7022     const UINT num_vertices16 = ARRAY_SIZE(vertices16);
7023     const UINT num_faces16 = ARRAY_SIZE(indices16) / VERTS_PER_FACE;
7024     DWORD flags16 = D3DXWELDEPSILONS_WELDPARTIALMATCHES;
7025     const D3DXWELDEPSILONS epsilons16 = {1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 254.9f, {0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, 0.0f, 0.0f, 0.0f};
7026     const DWORD adjacency16[] = {-1, 1, -1, -1, -1, 0};
7027     const struct vertex_color_ubyte4 exp_vertices16[] =
7028     {
7029         {{ 0.0f,  3.0f,  0.f}, {255, 255, 255, 255}},
7030         {{ 2.0f,  3.0f,  0.f}, {255, 255, 255, 255}},
7031         {{ 0.0f,  0.0f,  0.f}, {255, 255, 255, 255}},
7032
7033         {{ 2.0f,  3.0f,  0.f}, {  0,   0,   0,   0}},
7034         {{ 3.0f,  0.0f,  0.f}, {255, 255, 255, 255}},
7035     };
7036     const DWORD exp_indices16[] = {0, 1, 2, 3, 4, 2};
7037     const DWORD exp_face_remap16[] = {0, 1};
7038     const DWORD exp_vertex_remap16[] = {0, 1, 2, 3, 4, -1};
7039     const DWORD exp_new_num_vertices16 = ARRAY_SIZE(exp_vertices16);
7040     /* Test 17. Weld texture coordinates but as SHORT2 instead of D3DXVECTOR2.*/
7041     const struct vertex_texcoord_short2 vertices17[] =
7042     {
7043         {{ 0.0f,  3.0f,  0.f}, { 0, 0}},
7044         {{ 2.0f,  3.0f,  0.f}, { 0, 0}},
7045         {{ 0.0f,  0.0f,  0.f}, { 0, 0}},
7046
7047         {{ 3.0f,  3.0f,  0.f}, {32767, 32767}},
7048         {{ 3.0f,  0.0f,  0.f}, {0, 0}},
7049         {{ 1.0f,  0.0f,  0.f}, {32766, 32766}},
7050     };
7051     const DWORD indices17[] = {0, 1, 2, 3, 4, 5};
7052     const DWORD attributes17[] = {0, 0};
7053     const UINT num_vertices17 = ARRAY_SIZE(vertices17);
7054     const UINT num_faces17 = ARRAY_SIZE(indices17) / VERTS_PER_FACE;
7055     DWORD flags17 = D3DXWELDEPSILONS_WELDPARTIALMATCHES;
7056     const D3DXWELDEPSILONS epsilons17 = {1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, {32766.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, 0.0f, 0.0f, 0.0f};
7057     const DWORD adjacency17[] = {-1, 1, -1, -1, -1, 0};
7058     const struct vertex_texcoord_short2 exp_vertices17[] =
7059     {
7060         {{ 0.0f,  3.0f,  0.f}, { 0, 0}},
7061         {{ 2.0f,  3.0f,  0.f}, { 0, 0}},
7062         {{ 0.0f,  0.0f,  0.f}, { 0, 0}},
7063
7064         {{ 2.0f,  3.0f,  0.f}, {32767, 32767}},
7065         {{ 3.0f,  0.0f,  0.f}, {0, 0}},
7066     };
7067     const DWORD exp_indices17[] = {0, 1, 2, 3, 4, 2};
7068     const DWORD exp_face_remap17[] = {0, 1};
7069     const DWORD exp_vertex_remap17[] = {0, 1, 2, 3, 4, -1};
7070     const DWORD exp_new_num_vertices17 = ARRAY_SIZE(exp_vertices17);
7071     /* Test 18. Weld texture coordinates but as SHORT2N instead of D3DXVECTOR2. */
7072     const struct vertex_texcoord_short2 vertices18[] =
7073     {
7074         {{ 0.0f,  3.0f,  0.f}, { 0, 0}},
7075         {{ 2.0f,  3.0f,  0.f}, { 0, 0}},
7076         {{ 0.0f,  0.0f,  0.f}, { 0, 0}},
7077
7078         {{ 3.0f,  3.0f,  0.f}, {32767, 32767}},
7079         {{ 3.0f,  0.0f,  0.f}, {0, 0}},
7080         {{ 1.0f,  0.0f,  0.f}, {32766, 32766}},
7081     };
7082     const DWORD indices18[] = {0, 1, 2, 3, 4, 5};
7083     const DWORD attributes18[] = {0, 0};
7084     const UINT num_vertices18 = ARRAY_SIZE(vertices18);
7085     const UINT num_faces18 = ARRAY_SIZE(indices18) / VERTS_PER_FACE;
7086     DWORD flags18 = D3DXWELDEPSILONS_WELDPARTIALMATCHES;
7087     const D3DXWELDEPSILONS epsilons18 = {1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, {32766.0f/32767.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, 0.0f, 0.0f, 0.0f};
7088     const DWORD adjacency18[] = {-1, 1, -1, -1, -1, 0};
7089     const struct vertex_texcoord_short2 exp_vertices18[] =
7090     {
7091         {{ 0.0f,  3.0f,  0.f}, { 0, 0}},
7092         {{ 2.0f,  3.0f,  0.f}, { 0, 0}},
7093         {{ 0.0f,  0.0f,  0.f}, { 0, 0}},
7094
7095         {{ 2.0f,  3.0f,  0.f}, {32767, 32767}},
7096         {{ 3.0f,  0.0f,  0.f}, {0, 0}},
7097     };
7098     const DWORD exp_indices18[] = {0, 1, 2, 3, 4, 2};
7099     const DWORD exp_face_remap18[] = {0, 1};
7100     const DWORD exp_vertex_remap18[] = {0, 1, 2, 3, 4, -1};
7101     const DWORD exp_new_num_vertices18 = ARRAY_SIZE(exp_vertices18);
7102     /* Test 19.  Weld texture coordinates but as USHORT2N instead of D3DXVECTOR2. */
7103     const struct vertex_texcoord_ushort2n vertices19[] =
7104     {
7105         {{ 0.0f,  3.0f,  0.f}, { 0, 0}},
7106         {{ 2.0f,  3.0f,  0.f}, { 0, 0}},
7107         {{ 0.0f,  0.0f,  0.f}, { 0, 0}},
7108
7109         {{ 3.0f,  3.0f,  0.f}, {65535, 65535}},
7110         {{ 3.0f,  0.0f,  0.f}, {0, 0}},
7111         {{ 1.0f,  0.0f,  0.f}, {65534, 65534}},
7112     };
7113     const DWORD indices19[] = {0, 1, 2, 3, 4, 5};
7114     const DWORD attributes19[] = {0, 0};
7115     const UINT num_vertices19 = ARRAY_SIZE(vertices19);
7116     const UINT num_faces19 = ARRAY_SIZE(indices19) / VERTS_PER_FACE;
7117     DWORD flags19 = D3DXWELDEPSILONS_WELDPARTIALMATCHES;
7118     const D3DXWELDEPSILONS epsilons19 = {1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, {65534.0f/65535.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, 0.0f, 0.0f, 0.0f};
7119     const DWORD adjacency19[] = {-1, 1, -1, -1, -1, 0};
7120     const struct vertex_texcoord_ushort2n exp_vertices19[] =
7121     {
7122         {{ 0.0f,  3.0f,  0.f}, { 0, 0}},
7123         {{ 2.0f,  3.0f,  0.f}, { 0, 0}},
7124         {{ 0.0f,  0.0f,  0.f}, { 0, 0}},
7125
7126         {{ 2.0f,  3.0f,  0.f}, {65535, 65535}},
7127         {{ 3.0f,  0.0f,  0.f}, {0, 0}},
7128     };
7129     const DWORD exp_indices19[] = {0, 1, 2, 3, 4, 2};
7130     const DWORD exp_face_remap19[] = {0, 1};
7131     const DWORD exp_vertex_remap19[] = {0, 1, 2, 3, 4, -1};
7132     const DWORD exp_new_num_vertices19 = ARRAY_SIZE(exp_vertices19);
7133     /* Test 20.  Weld normal as SHORT4 instead of D3DXVECTOR3. */
7134     const struct vertex_normal_short4 vertices20[] =
7135     {
7136         {{ 0.0f,  3.0f,  0.f}, {0, 0, 0, 0}},
7137         {{ 2.0f,  3.0f,  0.f}, {0, 0, 0, 0}},
7138         {{ 0.0f,  0.0f,  0.f}, {0, 0, 0, 0}},
7139
7140         {{ 3.0f,  3.0f,  0.f}, {32767, 32767, 32767, 32767}},
7141         {{ 3.0f,  0.0f,  0.f}, {0, 0, 0, 0}},
7142         {{ 1.0f,  0.0f,  0.f}, {32766, 32766, 32766, 32766}},
7143     };
7144     const DWORD indices20[] = {0, 1, 2, 3, 4, 5};
7145     const DWORD attributes20[] = {0, 0};
7146     const UINT num_vertices20 = ARRAY_SIZE(vertices20);
7147     const UINT num_faces20 = ARRAY_SIZE(indices20) / VERTS_PER_FACE;
7148     DWORD flags20 = D3DXWELDEPSILONS_WELDPARTIALMATCHES;
7149     const D3DXWELDEPSILONS epsilons20 = {1.0f, 0.0f, 32766.0f, 0.0f, 0.0f, 0.0f, {0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, 0.0f, 0.0f, 0.0f};
7150     const DWORD adjacency20[] = {-1, 1, -1, -1, -1, 0};
7151     const struct vertex_normal_short4 exp_vertices20[] =
7152     {
7153         {{ 0.0f,  3.0f,  0.f}, {0, 0, 0, 0}},
7154         {{ 2.0f,  3.0f,  0.f}, {0, 0, 0, 0}},
7155         {{ 0.0f,  0.0f,  0.f}, {0, 0, 0, 0}},
7156
7157         {{ 2.0f,  3.0f,  0.f}, {32767, 32767, 32767, 32767}},
7158         {{ 3.0f,  0.0f,  0.f}, {0, 0, 0, 0}},
7159     };
7160     const DWORD exp_indices20[] = {0, 1, 2, 3, 4, 2};
7161     const DWORD exp_face_remap20[] = {0, 1};
7162     const DWORD exp_vertex_remap20[] = {0, 1, 2, 3, 4, -1};
7163     const DWORD exp_new_num_vertices20 = ARRAY_SIZE(exp_vertices20);
7164     /* Test 21.  Weld normal as SHORT4N instead of D3DXVECTOR3. */
7165     const struct vertex_normal_short4 vertices21[] =
7166     {
7167         {{ 0.0f,  3.0f,  0.f}, {0, 0, 0, 0}},
7168         {{ 2.0f,  3.0f,  0.f}, {0, 0, 0, 0}},
7169         {{ 0.0f,  0.0f,  0.f}, {0, 0, 0, 0}},
7170
7171         {{ 3.0f,  3.0f,  0.f}, {32767, 32767, 32767, 32767}},
7172         {{ 3.0f,  0.0f,  0.f}, {0, 0, 0, 0}},
7173         {{ 1.0f,  0.0f,  0.f}, {32766, 32766, 32766, 32766}},
7174     };
7175     const DWORD indices21[] = {0, 1, 2, 3, 4, 5};
7176     const DWORD attributes21[] = {0, 0};
7177     const UINT num_vertices21 = ARRAY_SIZE(vertices21);
7178     const UINT num_faces21 = ARRAY_SIZE(indices21) / VERTS_PER_FACE;
7179     DWORD flags21 = D3DXWELDEPSILONS_WELDPARTIALMATCHES;
7180     const D3DXWELDEPSILONS epsilons21 = {1.0f, 0.0f, 32766.0f/32767.0f, 0.0f, 0.0f, 0.0f, {0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, 0.0f, 0.0f, 0.0f};
7181     const DWORD adjacency21[] = {-1, 1, -1, -1, -1, 0};
7182     const struct vertex_normal_short4 exp_vertices21[] =
7183     {
7184         {{ 0.0f,  3.0f,  0.f}, {0, 0, 0, 0}},
7185         {{ 2.0f,  3.0f,  0.f}, {0, 0, 0, 0}},
7186         {{ 0.0f,  0.0f,  0.f}, {0, 0, 0, 0}},
7187
7188         {{ 2.0f,  3.0f,  0.f}, {32767, 32767, 32767, 32767}},
7189         {{ 3.0f,  0.0f,  0.f}, {0, 0, 0, 0}},
7190     };
7191     const DWORD exp_indices21[] = {0, 1, 2, 3, 4, 2};
7192     const DWORD exp_face_remap21[] = {0, 1};
7193     const DWORD exp_vertex_remap21[] = {0, 1, 2, 3, 4, -1};
7194     const DWORD exp_new_num_vertices21 = ARRAY_SIZE(exp_vertices21);
7195     /* Test 22.  Weld normal as USHORT4N instead of D3DXVECTOR3. */
7196     const struct vertex_normal_short4 vertices22[] =
7197     {
7198         {{ 0.0f,  3.0f,  0.f}, {0, 0, 0, 0}},
7199         {{ 2.0f,  3.0f,  0.f}, {0, 0, 0, 0}},
7200         {{ 0.0f,  0.0f,  0.f}, {0, 0, 0, 0}},
7201
7202         {{ 3.0f,  3.0f,  0.f}, {65535, 65535, 65535, 65535}},
7203         {{ 3.0f,  0.0f,  0.f}, {0, 0, 0, 0}},
7204         {{ 1.0f,  0.0f,  0.f}, {65534, 65534, 65534, 65534}},
7205     };
7206     const DWORD indices22[] = {0, 1, 2, 3, 4, 5};
7207     const DWORD attributes22[] = {0, 0};
7208     const UINT num_vertices22 = ARRAY_SIZE(vertices22);
7209     const UINT num_faces22 = ARRAY_SIZE(indices22) / VERTS_PER_FACE;
7210     DWORD flags22 = D3DXWELDEPSILONS_WELDPARTIALMATCHES;
7211     const D3DXWELDEPSILONS epsilons22 = {1.0f, 0.0f, 65534.0f/65535.0f, 0.0f, 0.0f, 0.0f, {0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, 0.0f, 0.0f, 0.0f};
7212     const DWORD adjacency22[] = {-1, 1, -1, -1, -1, 0};
7213     const struct vertex_normal_short4 exp_vertices22[] =
7214     {
7215         {{ 0.0f,  3.0f,  0.f}, {0, 0, 0, 0}},
7216         {{ 2.0f,  3.0f,  0.f}, {0, 0, 0, 0}},
7217         {{ 0.0f,  0.0f,  0.f}, {0, 0, 0, 0}},
7218
7219         {{ 2.0f,  3.0f,  0.f}, {65535, 65535, 65535, 65535}},
7220         {{ 3.0f,  0.0f,  0.f}, {0, 0, 0, 0}},
7221     };
7222     const DWORD exp_indices22[] = {0, 1, 2, 3, 4, 2};
7223     const DWORD exp_face_remap22[] = {0, 1};
7224     const DWORD exp_vertex_remap22[] = {0, 1, 2, 3, 4, -1};
7225     const DWORD exp_new_num_vertices22 = ARRAY_SIZE(exp_vertices22);
7226     /* Test 23. Weld texture coordinates as FLOAT16_2. Similar to test 11, but
7227      * with texture coordinates converted to float16 in hex. */
7228     const struct vertex_texcoord_float16_2 vertices23[] =
7229     {
7230         {{ 0.0f,  3.0f,  0.f}, {0x3c00, 0x3c00}}, /* {1.0f, 1.0f} */
7231         {{ 2.0f,  3.0f,  0.f}, {0x3800, 0x399a}}, /* {0.5f, 0.7f} */
7232         {{ 0.0f,  0.0f,  0.f}, {0xb266, 0xb4cd}}, /* {-0.2f, -0.3f} */
7233
7234         {{ 3.0f,  3.0f,  0.f}, {0x3266, 0x34cd}}, /* {0.2f, 0.3f} */
7235         {{ 3.0f,  0.0f,  0.f}, {0x3c00, 0x3c00}}, /* {1.0f, 1.0f} */
7236         {{ 1.0f,  0.0f,  0.f}, {0x2e66, 0x3266}}, /* {0.1f, 0.2f} */
7237     };
7238     const DWORD indices23[] = {0, 1, 2, 3, 4, 5};
7239     const DWORD attributes23[] = {0, 0};
7240     const UINT num_vertices23 = ARRAY_SIZE(vertices23);
7241     const UINT num_faces23 = ARRAY_SIZE(indices23) / VERTS_PER_FACE;
7242     DWORD flags23 = D3DXWELDEPSILONS_WELDPARTIALMATCHES;
7243     const D3DXWELDEPSILONS epsilons23 = {1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, {0.41f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, 0.0f, 0.0f, 0.0f};
7244     const DWORD adjacency23[] = {-1, 1, -1, -1, -1, 0};
7245     const struct vertex_texcoord_float16_2 exp_vertices23[] =
7246     {
7247         {{ 0.0f,  3.0f,  0.f}, {0x3c00, 0x3c00}}, /* {1.0f, 1.0f} */
7248         {{ 2.0f,  3.0f,  0.f}, {0x3800, 0x399a}}, /* {0.5f, 0.7f} */
7249         {{ 0.0f,  0.0f,  0.f}, {0xb266, 0xb4cd}}, /* {-0.2f, -0.3f} */
7250
7251         {{ 3.0f,  0.0f,  0.f}, {0x3c00, 0x3c00}}, /* {1.0f, 1.0f} */
7252         {{ 0.0f,  0.0f,  0.f}, {0x2e66, 0x3266}}, /* {0.1f, 0.2f} */
7253     };
7254     const DWORD exp_indices23[] = {0, 1, 2, 1, 3, 4};
7255     const DWORD exp_face_remap23[] = {0, 1};
7256     const DWORD exp_vertex_remap23[] = {0, 1, 2, 4, 5, -1};
7257     const DWORD exp_new_num_vertices23 = ARRAY_SIZE(exp_vertices23);
7258     /* Test 24. Weld texture coordinates as FLOAT16_4. Similar to test 24. */
7259     const struct vertex_texcoord_float16_4 vertices24[] =
7260     {
7261         {{ 0.0f,  3.0f,  0.f}, {0x3c00, 0x3c00, 0x3c00, 0x3c00}},
7262         {{ 2.0f,  3.0f,  0.f}, {0x3800, 0x399a, 0x3800, 0x399a}},
7263         {{ 0.0f,  0.0f,  0.f}, {0xb266, 0xb4cd, 0xb266, 0xb4cd}},
7264
7265         {{ 3.0f,  3.0f,  0.f}, {0x3266, 0x34cd, 0x3266, 0x34cd}},
7266         {{ 3.0f,  0.0f,  0.f}, {0x3c00, 0x3c00, 0x3c00, 0x3c00}},
7267         {{ 1.0f,  0.0f,  0.f}, {0x2e66, 0x3266, 0x2e66, 0x3266}},
7268     };
7269     const DWORD indices24[] = {0, 1, 2, 3, 4, 5};
7270     const DWORD attributes24[] = {0, 0};
7271     const UINT num_vertices24 = ARRAY_SIZE(vertices24);
7272     const UINT num_faces24 = ARRAY_SIZE(indices24) / VERTS_PER_FACE;
7273     DWORD flags24 = D3DXWELDEPSILONS_WELDPARTIALMATCHES;
7274     const D3DXWELDEPSILONS epsilons24 = {1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, {0.41f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, 0.0f, 0.0f, 0.0f};
7275     const DWORD adjacency24[] = {-1, 1, -1, -1, -1, 0};
7276     const struct vertex_texcoord_float16_4 exp_vertices24[] =
7277     {
7278         {{ 0.0f,  3.0f,  0.f}, {0x3c00, 0x3c00, 0x3c00, 0x3c00}},
7279         {{ 2.0f,  3.0f,  0.f}, {0x3800, 0x399a, 0x3800, 0x399a}},
7280         {{ 0.0f,  0.0f,  0.f}, {0xb266, 0xb4cd, 0xb266, 0xb4cd}},
7281
7282         {{ 3.0f,  0.0f,  0.f}, {0x3c00, 0x3c00, 0x3c00, 0x3c00}},
7283         {{ 0.0f,  0.0f,  0.f}, {0x2e66, 0x3266, 0x2e66, 0x3266}},
7284     };
7285     const DWORD exp_indices24[] = {0, 1, 2, 1, 3, 4};
7286     const DWORD exp_face_remap24[] = {0, 1};
7287     const DWORD exp_vertex_remap24[] = {0, 1, 2, 4, 5, -1};
7288     const DWORD exp_new_num_vertices24 = ARRAY_SIZE(exp_vertices24);
7289     /* Test 25. Weld texture coordinates with usage index 10 (TEXCOORD10). The
7290      * usage index is capped at 7, so the epsilon for TEXCOORD7 is used instead.
7291      */
7292     const struct vertex_texcoord vertices25[] =
7293     {
7294         {{ 0.0f,  3.0f,  0.f}, {1.0f, 1.0f}},
7295         {{ 2.0f,  3.0f,  0.f}, {0.5f, 0.7f}},
7296         {{ 0.0f,  0.0f,  0.f}, {-0.2f, -0.3f}},
7297
7298         {{ 3.0f,  3.0f,  0.f}, {0.2f, 0.3f}},
7299         {{ 3.0f,  0.0f,  0.f}, {1.0f, 1.0f}},
7300         {{ 1.0f,  0.0f,  0.f}, {0.1f, 0.2f}}
7301     };
7302     const DWORD indices25[] = {0, 1, 2, 3, 4, 5};
7303     const DWORD attributes25[] = {0, 0};
7304     const UINT num_vertices25 = ARRAY_SIZE(vertices25);
7305     const UINT num_faces25 = ARRAY_SIZE(indices25) / VERTS_PER_FACE;
7306     DWORD flags25 = D3DXWELDEPSILONS_WELDPARTIALMATCHES;
7307     const D3DXWELDEPSILONS epsilons25 = {1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, {0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.4f + FLT_EPSILON}, 0.0f, 0.0f, 0.0f};
7308     const DWORD adjacency25[] = {-1, 1, -1, -1, -1, 0};
7309     const struct vertex_texcoord exp_vertices25[] =
7310     {
7311         {{ 0.0f,  3.0f,  0.f}, {1.0f, 1.0f}},
7312         {{ 2.0f,  3.0f,  0.f}, {0.5f, 0.7f}},
7313         {{ 0.0f,  0.0f,  0.f}, {-0.2f, -0.3f}},
7314
7315         {{ 3.0f,  0.0f,  0.f}, {1.0f, 1.0f}},
7316         {{ 0.0f,  0.0f,  0.f}, {0.1f, 0.2f}},
7317     };
7318     const DWORD exp_indices25[] = {0, 1, 2, 1, 3, 4};
7319     const DWORD exp_face_remap25[] = {0, 1};
7320     const DWORD exp_vertex_remap25[] = {0, 1, 2, 4, 5, -1};
7321     const DWORD exp_new_num_vertices25 = ARRAY_SIZE(exp_vertices25);
7322     /* Test 26. Weld color with usage index larger than 1. Shows that none of
7323      * the epsilon values are used. */
7324     const struct vertex_color vertices26[] =
7325     {
7326         {{ 0.0f,  3.0f,  0.f}, 0xFFFFFFFF},
7327         {{ 2.0f,  3.0f,  0.f}, 0xFFFFFFFF},
7328         {{ 0.0f,  0.0f,  0.f}, 0xFFFFFFFF},
7329
7330         {{ 3.0f,  3.0f,  0.f}, 0x00000000},
7331         {{ 3.0f,  0.0f,  0.f}, 0xFFFFFFFF},
7332         {{ 1.0f,  0.0f,  0.f}, 0x01010101},
7333     };
7334     const DWORD indices26[] = {0, 1, 2, 3, 4, 5};
7335     const DWORD attributes26[] = {0, 0};
7336     const UINT num_vertices26 = ARRAY_SIZE(vertices26);
7337     const UINT num_faces26 = ARRAY_SIZE(indices26) / VERTS_PER_FACE;
7338     DWORD flags26 = D3DXWELDEPSILONS_WELDPARTIALMATCHES;
7339     const D3DXWELDEPSILONS epsilons26 = {1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, {1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f}, 1.0f, 1.0f, 1.0f};
7340     const DWORD adjacency26[] = {-1, 1, -1, -1, -1, 0};
7341     const struct vertex_color exp_vertices26[] =
7342     {
7343         {{ 0.0f,  3.0f,  0.f}, 0xFFFFFFFF},
7344         {{ 2.0f,  3.0f,  0.f}, 0xFFFFFFFF},
7345         {{ 0.0f,  0.0f,  0.f}, 0xFFFFFFFF},
7346
7347         {{ 2.0f,  3.0f,  0.f}, 0x00000000},
7348         {{ 3.0f,  0.0f,  0.f}, 0xFFFFFFFF},
7349         {{ 0.0f,  0.0f,  0.f}, 0x01010101},
7350     };
7351     const DWORD exp_indices26[] = {0, 1, 2, 3, 4, 5};
7352     const DWORD exp_face_remap26[] = {0, 1};
7353     const DWORD exp_vertex_remap26[] = {0, 1, 2, 3, 4, 5};
7354     const DWORD exp_new_num_vertices26 = ARRAY_SIZE(exp_vertices26);
7355     /* Test 27. Weld color with usage index larger than 1. Check that the
7356      * default epsilon of 1e-6f is used. */
7357     D3DXVECTOR4 zero_float4 = {0.0f, 0.0f, 0.0f, 0.0f};
7358     D3DXVECTOR4 almost_zero_float4 = {0.0f + FLT_EPSILON, 0.0f + FLT_EPSILON, 0.0f + FLT_EPSILON, 0.0f + FLT_EPSILON};
7359     const struct vertex_color_float4 vertices27[] =
7360     {
7361         {{ 0.0f,  3.0f,  0.f}, zero_float4},
7362         {{ 2.0f,  3.0f,  0.f}, zero_float4},
7363         {{ 0.0f,  0.0f,  0.f}, zero_float4},
7364
7365         {{ 3.0f,  3.0f,  0.f}, almost_zero_float4},
7366         {{ 3.0f,  0.0f,  0.f}, zero_float4},
7367         {{ 1.0f,  0.0f,  0.f}, almost_zero_float4},
7368     };
7369     const DWORD indices27[] = {0, 1, 2, 3, 4, 5};
7370     const DWORD attributes27[] = {0, 0};
7371     const UINT num_vertices27 = ARRAY_SIZE(vertices27);
7372     const UINT num_faces27 = ARRAY_SIZE(indices27) / VERTS_PER_FACE;
7373     DWORD flags27 = D3DXWELDEPSILONS_WELDPARTIALMATCHES;
7374     const D3DXWELDEPSILONS epsilons27 = {1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, {0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, 0.0f, 0.0f, 0.0f};
7375     const DWORD adjacency27[] = {-1, 1, -1, -1, -1, 0};
7376     const struct vertex_color_float4 exp_vertices27[] =
7377     {
7378         {{ 0.0f,  3.0f,  0.f}, zero_float4},
7379         {{ 2.0f,  3.0f,  0.f}, zero_float4},
7380         {{ 0.0f,  0.0f,  0.f}, zero_float4},
7381
7382         {{ 3.0f,  0.0f,  0.f}, zero_float4},
7383     };
7384     const DWORD exp_indices27[] = {0, 1, 2, 1, 3, 2};
7385     const DWORD exp_face_remap27[] = {0, 1};
7386     const DWORD exp_vertex_remap27[] = {0, 1, 2, 4, -1, -1};
7387     const DWORD exp_new_num_vertices27 = ARRAY_SIZE(exp_vertices27);
7388     /* Test 28. Weld one normal with UDEC3. */
7389     const DWORD dword_udec3_zero = init_udec3_dword(0, 0, 0, 1);
7390     const DWORD dword_udec3_1023 = init_udec3_dword(1023, 1023, 1023, 1);
7391     const DWORD dword_udec3_1022 = init_udec3_dword(1022, 1022, 1022, 1);
7392     const struct vertex_normal_udec3 vertices28[] =
7393     {
7394         {{ 0.0f,  3.0f,  0.f}, dword_udec3_zero},
7395         {{ 2.0f,  3.0f,  0.f}, dword_udec3_zero},
7396         {{ 0.0f,  0.0f,  0.f}, dword_udec3_zero},
7397
7398         {{ 3.0f,  3.0f,  0.f}, dword_udec3_1023},
7399         {{ 3.0f,  0.0f,  0.f}, dword_udec3_zero},
7400         {{ 1.0f,  0.0f,  0.f}, dword_udec3_1022},
7401     };
7402     const DWORD indices28[] = {0, 1, 2, 3, 4, 5};
7403     const DWORD attributes28[] = {0, 0};
7404     const UINT num_vertices28 = ARRAY_SIZE(vertices28);
7405     const UINT num_faces28 = ARRAY_SIZE(indices28) / VERTS_PER_FACE;
7406     DWORD flags28 = D3DXWELDEPSILONS_WELDPARTIALMATCHES;
7407     const D3DXWELDEPSILONS epsilons28 = {1.0f, 0.0f, 1022.0f, 0.0f, 0.0f, 0.0f, {0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, 0.0f, 0.0f, 0.0f};
7408     const DWORD adjacency28[] = {-1, 1, -1, -1, -1, 0};
7409     const struct vertex_normal_udec3 exp_vertices28[] =
7410     {
7411         {{ 0.0f,  3.0f,  0.f}, dword_udec3_zero},
7412         {{ 2.0f,  3.0f,  0.f}, dword_udec3_zero},
7413         {{ 0.0f,  0.0f,  0.f}, dword_udec3_zero},
7414
7415         {{ 2.0f,  3.0f,  0.f}, dword_udec3_1023},
7416         {{ 3.0f,  0.0f,  0.f}, dword_udec3_zero},
7417     };
7418     const DWORD exp_indices28[] = {0, 1, 2, 3, 4, 2};
7419     const DWORD exp_face_remap28[] = {0, 1};
7420     const DWORD exp_vertex_remap28[] = {0, 1, 2, 3, 4, -1};
7421     const DWORD exp_new_num_vertices28 = ARRAY_SIZE(exp_vertices28);
7422     /* Test 29. Weld one normal with DEC3N. */
7423     const DWORD dword_dec3n_zero = init_dec3n_dword(0, 0, 0, 1);
7424     const DWORD dword_dec3n_511 = init_dec3n_dword(511, 511, 511, 1);
7425     const DWORD dword_dec3n_510 = init_dec3n_dword(510, 510, 510, 1);
7426     const struct vertex_normal_dec3n vertices29[] =
7427     {
7428         {{ 0.0f,  3.0f,  0.f}, dword_dec3n_zero},
7429         {{ 2.0f,  3.0f,  0.f}, dword_dec3n_zero},
7430         {{ 0.0f,  0.0f,  0.f}, dword_dec3n_zero},
7431
7432         {{ 3.0f,  3.0f,  0.f}, dword_dec3n_511},
7433         {{ 3.0f,  0.0f,  0.f}, dword_dec3n_zero},
7434         {{ 1.0f,  0.0f,  0.f}, dword_dec3n_510},
7435     };
7436     const DWORD indices29[] = {0, 1, 2, 3, 4, 5};
7437     const DWORD attributes29[] = {0, 0};
7438     const UINT num_vertices29 = ARRAY_SIZE(vertices29);
7439     const UINT num_faces29 = ARRAY_SIZE(indices29) / VERTS_PER_FACE;
7440     DWORD flags29 = D3DXWELDEPSILONS_WELDPARTIALMATCHES;
7441     const D3DXWELDEPSILONS epsilons29 = {1.0f, 0.0f, 510.0f/511.0f, 0.0f, 0.0f, 0.0f, {0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, .0f}, 0.0f, 0.0f, 0.0f};
7442     const DWORD adjacency29[] = {-1, 1, -1, -1, -1, 0};
7443     const struct vertex_normal_dec3n exp_vertices29[] =
7444     {
7445         {{ 0.0f,  3.0f,  0.f}, dword_dec3n_zero},
7446         {{ 2.0f,  3.0f,  0.f}, dword_dec3n_zero},
7447         {{ 0.0f,  0.0f,  0.f}, dword_dec3n_zero},
7448
7449         {{ 2.0f,  3.0f,  0.f}, dword_dec3n_511},
7450         {{ 3.0f,  0.0f,  0.f}, dword_dec3n_zero},
7451     };
7452     const DWORD exp_indices29[] = {0, 1, 2, 3, 4, 2};
7453     const DWORD exp_face_remap29[] = {0, 1};
7454     const DWORD exp_vertex_remap29[] = {0, 1, 2, 3, 4, -1};
7455     const DWORD exp_new_num_vertices29 = ARRAY_SIZE(exp_vertices29);
7456     /* All mesh data */
7457     DWORD *adjacency_out = NULL;
7458     DWORD *face_remap = NULL;
7459     ID3DXMesh *mesh = NULL;
7460     ID3DXBuffer *vertex_remap = NULL;
7461     struct
7462     {
7463         const BYTE *vertices;
7464         const DWORD *indices;
7465         const DWORD *attributes;
7466         const DWORD num_vertices;
7467         const DWORD num_faces;
7468         const DWORD options;
7469         D3DVERTEXELEMENT9 *declaration;
7470         const UINT vertex_size;
7471         const DWORD flags;
7472         const D3DXWELDEPSILONS *epsilons;
7473         const DWORD *adjacency;
7474         const BYTE *exp_vertices;
7475         const DWORD *exp_indices;
7476         const DWORD *exp_face_remap;
7477         const DWORD *exp_vertex_remap;
7478         const DWORD exp_new_num_vertices;
7479     }
7480     tc[] =
7481     {
7482         {
7483             (BYTE*)vertices0,
7484             indices0,
7485             attributes0,
7486             num_vertices0,
7487             num_faces0,
7488             options,
7489             declaration_normal,
7490             vertex_size_normal,
7491             flags0,
7492             NULL,
7493             adjacency0,
7494             (BYTE*)exp_vertices0,
7495             exp_indices0,
7496             exp_face_remap0,
7497             exp_vertex_remap0,
7498             exp_new_num_vertices0
7499         },
7500         {
7501             (BYTE*)vertices1,
7502             indices1,
7503             attributes1,
7504             num_vertices1,
7505             num_faces1,
7506             options,
7507             declaration_normal,
7508             vertex_size_normal,
7509             flags1,
7510             NULL,
7511             adjacency1,
7512             (BYTE*)exp_vertices1,
7513             exp_indices1,
7514             exp_face_remap1,
7515             exp_vertex_remap1,
7516             exp_new_num_vertices1
7517         },
7518         {
7519             (BYTE*)vertices2,
7520             indices2,
7521             attributes2,
7522             num_vertices2,
7523             num_faces2,
7524             options,
7525             declaration_normal,
7526             vertex_size_normal,
7527             flags2,
7528             &epsilons2,
7529             adjacency2,
7530             (BYTE*)exp_vertices2,
7531             exp_indices2,
7532             exp_face_remap2,
7533             exp_vertex_remap2,
7534             exp_new_num_vertices2
7535         },
7536         {
7537             (BYTE*)vertices3,
7538             indices3,
7539             attributes3,
7540             num_vertices3,
7541             num_faces3,
7542             options,
7543             declaration_normal,
7544             vertex_size_normal,
7545             flags3,
7546             &epsilons3,
7547             adjacency3,
7548             (BYTE*)exp_vertices3,
7549             exp_indices3,
7550             exp_face_remap3,
7551             exp_vertex_remap3,
7552             exp_new_num_vertices3
7553         },
7554         {
7555             (BYTE*)vertices4,
7556             indices4,
7557             attributes4,
7558             num_vertices4,
7559             num_faces4,
7560             options,
7561             declaration_normal,
7562             vertex_size_normal,
7563             flags4,
7564             &epsilons4,
7565             adjacency4,
7566             (BYTE*)exp_vertices4,
7567             exp_indices4,
7568             exp_face_remap4,
7569             exp_vertex_remap4,
7570             exp_new_num_vertices4
7571         },
7572         /* Unusual ordering. */
7573         {
7574             (BYTE*)vertices5,
7575             indices5,
7576             attributes5,
7577             num_vertices5,
7578             num_faces5,
7579             options,
7580             declaration_normal,
7581             vertex_size_normal,
7582             flags5,
7583             NULL,
7584             adjacency5,
7585             (BYTE*)exp_vertices5,
7586             exp_indices5,
7587             exp_face_remap5,
7588             exp_vertex_remap5,
7589             exp_new_num_vertices5
7590         },
7591         {
7592             (BYTE*)vertices6,
7593             indices6,
7594             attributes6,
7595             num_vertices6,
7596             num_faces6,
7597             options,
7598             declaration_normal,
7599             vertex_size_normal,
7600             flags6,
7601             &epsilons6,
7602             adjacency6,
7603             (BYTE*)exp_vertices6,
7604             exp_indices6,
7605             exp_face_remap6,
7606             exp_vertex_remap6,
7607             exp_new_num_vertices6
7608         },
7609         {
7610             (BYTE*)vertices6,
7611             (DWORD*)indices6_16bit,
7612             attributes6,
7613             num_vertices6,
7614             num_faces6,
7615             options_16bit,
7616             declaration_normal,
7617             vertex_size_normal,
7618             flags6,
7619             &epsilons6,
7620             adjacency6,
7621             (BYTE*)exp_vertices6,
7622             exp_indices6,
7623             exp_face_remap6,
7624             exp_vertex_remap6,
7625             exp_new_num_vertices6
7626         },
7627         {
7628             (BYTE*)vertices8,
7629             indices8,
7630             attributes8,
7631             num_vertices8,
7632             num_faces8,
7633             options,
7634             declaration_normal,
7635             vertex_size_normal,
7636             flags8,
7637             &epsilons8,
7638             adjacency8,
7639             (BYTE*)exp_vertices8,
7640             exp_indices8,
7641             exp_face_remap8,
7642             exp_vertex_remap8,
7643             exp_new_num_vertices8
7644         },
7645         {
7646             (BYTE*)vertices9,
7647             indices9,
7648             attributes9,
7649             num_vertices9,
7650             num_faces9,
7651             options,
7652             declaration_normal,
7653             vertex_size_normal,
7654             flags9,
7655             &epsilons9,
7656             adjacency9,
7657             (BYTE*)exp_vertices9,
7658             exp_indices9,
7659             exp_face_remap9,
7660             exp_vertex_remap9,
7661             exp_new_num_vertices9
7662         },
7663         {
7664             (BYTE*)vertices10,
7665             indices10,
7666             attributes10,
7667             num_vertices10,
7668             num_faces10,
7669             options,
7670             declaration_blendweight,
7671             vertex_size_blendweight,
7672             flags10,
7673             &epsilons10,
7674             adjacency10,
7675             (BYTE*)exp_vertices10,
7676             exp_indices10,
7677             exp_face_remap10,
7678             exp_vertex_remap10,
7679             exp_new_num_vertices10
7680         },
7681         {
7682             (BYTE*)vertices11,
7683             indices11,
7684             attributes11,
7685             num_vertices11,
7686             num_faces11,
7687             options,
7688             declaration_texcoord,
7689             vertex_size_texcoord,
7690             flags11,
7691             &epsilons11,
7692             adjacency11,
7693             (BYTE*)exp_vertices11,
7694             exp_indices11,
7695             exp_face_remap11,
7696             exp_vertex_remap11,
7697             exp_new_num_vertices11
7698         },
7699         {
7700             (BYTE*)vertices12,
7701             indices12,
7702             attributes12,
7703             num_vertices12,
7704             num_faces12,
7705             options,
7706             declaration_color,
7707             vertex_size_color,
7708             flags12,
7709             &epsilons12,
7710             adjacency12,
7711             (BYTE*)exp_vertices12,
7712             exp_indices12,
7713             exp_face_remap12,
7714             exp_vertex_remap12,
7715             exp_new_num_vertices12
7716         },
7717         {
7718             (BYTE*)vertices13,
7719             indices13,
7720             attributes13,
7721             num_vertices13,
7722             num_faces13,
7723             options,
7724             declaration_normal3,
7725             vertex_size_normal,
7726             flags13,
7727             &epsilons13,
7728             adjacency13,
7729             (BYTE*)exp_vertices13,
7730             exp_indices13,
7731             exp_face_remap13,
7732             exp_vertex_remap13,
7733             exp_new_num_vertices13
7734         },
7735         {
7736             (BYTE*)vertices14,
7737             indices14,
7738             attributes14,
7739             num_vertices14,
7740             num_faces14,
7741             options,
7742             declaration_color,
7743             vertex_size_color,
7744             flags14,
7745             &epsilons14,
7746             adjacency14,
7747             (BYTE*)exp_vertices14,
7748             exp_indices14,
7749             exp_face_remap14,
7750             exp_vertex_remap14,
7751             exp_new_num_vertices14
7752         },
7753         {
7754             (BYTE*)vertices15,
7755             indices15,
7756             attributes15,
7757             num_vertices15,
7758             num_faces15,
7759             options,
7760             declaration_color_ubyte4n,
7761             vertex_size_color_ubyte4, /* UBYTE4 same size as UBYTE4N */
7762             flags15,
7763             &epsilons15,
7764             adjacency15,
7765             (BYTE*)exp_vertices15,
7766             exp_indices15,
7767             exp_face_remap15,
7768             exp_vertex_remap15,
7769             exp_new_num_vertices15
7770         },
7771         {
7772             (BYTE*)vertices16,
7773             indices16,
7774             attributes16,
7775             num_vertices16,
7776             num_faces16,
7777             options,
7778             declaration_color_ubyte4,
7779             vertex_size_color_ubyte4,
7780             flags16,
7781             &epsilons16,
7782             adjacency16,
7783             (BYTE*)exp_vertices16,
7784             exp_indices16,
7785             exp_face_remap16,
7786             exp_vertex_remap16,
7787             exp_new_num_vertices16
7788         },
7789         {
7790             (BYTE*)vertices17,
7791             indices17,
7792             attributes17,
7793             num_vertices17,
7794             num_faces17,
7795             options,
7796             declaration_texcoord_short2,
7797             vertex_size_texcoord_short2,
7798             flags17,
7799             &epsilons17,
7800             adjacency17,
7801             (BYTE*)exp_vertices17,
7802             exp_indices17,
7803             exp_face_remap17,
7804             exp_vertex_remap17,
7805             exp_new_num_vertices17
7806         },
7807         {
7808             (BYTE*)vertices18,
7809             indices18,
7810             attributes18,
7811             num_vertices18,
7812             num_faces18,
7813             options,
7814             declaration_texcoord_short2n,
7815             vertex_size_texcoord_short2, /* SHORT2 same size as SHORT2N */
7816             flags18,
7817             &epsilons18,
7818             adjacency18,
7819             (BYTE*)exp_vertices18,
7820             exp_indices18,
7821             exp_face_remap18,
7822             exp_vertex_remap18,
7823             exp_new_num_vertices18
7824         },
7825         {
7826             (BYTE*)vertices19,
7827             indices19,
7828             attributes19,
7829             num_vertices19,
7830             num_faces19,
7831             options,
7832             declaration_texcoord_ushort2n,
7833             vertex_size_texcoord_short2, /* SHORT2 same size as USHORT2N */
7834             flags19,
7835             &epsilons19,
7836             adjacency19,
7837             (BYTE*)exp_vertices19,
7838             exp_indices19,
7839             exp_face_remap19,
7840             exp_vertex_remap19,
7841             exp_new_num_vertices19
7842         },
7843         {
7844             (BYTE*)vertices20,
7845             indices20,
7846             attributes20,
7847             num_vertices20,
7848             num_faces20,
7849             options,
7850             declaration_normal_short4,
7851             vertex_size_normal_short4,
7852             flags20,
7853             &epsilons20,
7854             adjacency20,
7855             (BYTE*)exp_vertices20,
7856             exp_indices20,
7857             exp_face_remap20,
7858             exp_vertex_remap20,
7859             exp_new_num_vertices20
7860         },
7861         {
7862             (BYTE*)vertices21,
7863             indices21,
7864             attributes21,
7865             num_vertices21,
7866             num_faces21,
7867             options,
7868             declaration_normal_short4n,
7869             vertex_size_normal_short4, /* SHORT4 same size as SHORT4N */
7870             flags21,
7871             &epsilons21,
7872             adjacency21,
7873             (BYTE*)exp_vertices21,
7874             exp_indices21,
7875             exp_face_remap21,
7876             exp_vertex_remap21,
7877             exp_new_num_vertices21
7878         },
7879         {
7880             (BYTE*)vertices22,
7881             indices22,
7882             attributes22,
7883             num_vertices22,
7884             num_faces22,
7885             options,
7886             declaration_normal_ushort4n,
7887             vertex_size_normal_short4, /* SHORT4 same size as USHORT4N */
7888             flags22,
7889             &epsilons22,
7890             adjacency22,
7891             (BYTE*)exp_vertices22,
7892             exp_indices22,
7893             exp_face_remap22,
7894             exp_vertex_remap22,
7895             exp_new_num_vertices22
7896         },
7897         {
7898             (BYTE*)vertices23,
7899             indices23,
7900             attributes23,
7901             num_vertices23,
7902             num_faces23,
7903             options,
7904             declaration_texcoord_float16_2,
7905             vertex_size_texcoord_float16_2,
7906             flags23,
7907             &epsilons23,
7908             adjacency23,
7909             (BYTE*)exp_vertices23,
7910             exp_indices23,
7911             exp_face_remap23,
7912             exp_vertex_remap23,
7913             exp_new_num_vertices23
7914         },
7915         {
7916             (BYTE*)vertices24,
7917             indices24,
7918             attributes24,
7919             num_vertices24,
7920             num_faces24,
7921             options,
7922             declaration_texcoord_float16_4,
7923             vertex_size_texcoord_float16_4,
7924             flags24,
7925             &epsilons24,
7926             adjacency24,
7927             (BYTE*)exp_vertices24,
7928             exp_indices24,
7929             exp_face_remap24,
7930             exp_vertex_remap24,
7931             exp_new_num_vertices24
7932         },
7933         {
7934             (BYTE*)vertices25,
7935             indices25,
7936             attributes25,
7937             num_vertices25,
7938             num_faces25,
7939             options,
7940             declaration_texcoord10,
7941             vertex_size_texcoord,
7942             flags25,
7943             &epsilons25,
7944             adjacency25,
7945             (BYTE*)exp_vertices25,
7946             exp_indices25,
7947             exp_face_remap25,
7948             exp_vertex_remap25,
7949             exp_new_num_vertices25
7950         },
7951         {
7952             (BYTE*)vertices26,
7953             indices26,
7954             attributes26,
7955             num_vertices26,
7956             num_faces26,
7957             options,
7958             declaration_color2,
7959             vertex_size_color,
7960             flags26,
7961             &epsilons26,
7962             adjacency26,
7963             (BYTE*)exp_vertices26,
7964             exp_indices26,
7965             exp_face_remap26,
7966             exp_vertex_remap26,
7967             exp_new_num_vertices26
7968         },
7969         {
7970             (BYTE*)vertices27,
7971             indices27,
7972             attributes27,
7973             num_vertices27,
7974             num_faces27,
7975             options,
7976             declaration_color2_float4,
7977             vertex_size_color_float4,
7978             flags27,
7979             &epsilons27,
7980             adjacency27,
7981             (BYTE*)exp_vertices27,
7982             exp_indices27,
7983             exp_face_remap27,
7984             exp_vertex_remap27,
7985             exp_new_num_vertices27
7986         },
7987         {
7988             (BYTE*)vertices28,
7989             indices28,
7990             attributes28,
7991             num_vertices28,
7992             num_faces28,
7993             options,
7994             declaration_normal_udec3,
7995             vertex_size_normal_udec3,
7996             flags28,
7997             &epsilons28,
7998             adjacency28,
7999             (BYTE*)exp_vertices28,
8000             exp_indices28,
8001             exp_face_remap28,
8002             exp_vertex_remap28,
8003             exp_new_num_vertices28
8004         },
8005         {
8006             (BYTE*)vertices29,
8007             indices29,
8008             attributes29,
8009             num_vertices29,
8010             num_faces29,
8011             options,
8012             declaration_normal_dec3n,
8013             vertex_size_normal_dec3n,
8014             flags29,
8015             &epsilons29,
8016             adjacency29,
8017             (BYTE*)exp_vertices29,
8018             exp_indices29,
8019             exp_face_remap29,
8020             exp_vertex_remap29,
8021             exp_new_num_vertices29
8022         }
8023     };
8024
8025     test_context = new_test_context();
8026     if (!test_context)
8027     {
8028         skip("Couldn't create test context\n");
8029         goto cleanup;
8030     }
8031
8032     for (i = 0; i < ARRAY_SIZE(tc); i++)
8033     {
8034         DWORD j;
8035         DWORD *vertex_remap_ptr;
8036         DWORD new_num_vertices;
8037
8038         hr = init_test_mesh(tc[i].num_faces, tc[i].num_vertices, tc[i].options,
8039                             tc[i].declaration, test_context->device, &mesh,
8040                             tc[i].vertices, tc[i].vertex_size,
8041                             tc[i].indices, tc[i].attributes);
8042         if (FAILED(hr))
8043         {
8044             skip("Couldn't initialize test mesh %d.\n", i);
8045             goto cleanup;
8046         }
8047
8048         /* Allocate out parameters */
8049         adjacency_out = HeapAlloc(GetProcessHeap(), 0, VERTS_PER_FACE * tc[i].num_faces * sizeof(*adjacency_out));
8050         if (!adjacency_out)
8051         {
8052             skip("Couldn't allocate adjacency_out array.\n");
8053             goto cleanup;
8054         }
8055         face_remap = HeapAlloc(GetProcessHeap(), 0, tc[i].num_faces * sizeof(*face_remap));
8056         if (!adjacency_out)
8057         {
8058             skip("Couldn't allocate face_remap array.\n");
8059             goto cleanup;
8060         }
8061         hr = D3DXCreateBuffer(tc[i].num_vertices * sizeof(DWORD), &vertex_remap);
8062         if (FAILED(hr))
8063         {
8064             skip("Couldn't create vertex_remap buffer.\n");
8065             goto cleanup;
8066         }
8067
8068         hr = D3DXWeldVertices(mesh, tc[i].flags, tc[i].epsilons, tc[i].adjacency,
8069                               adjacency_out, face_remap, &vertex_remap);
8070         ok(hr == D3D_OK, "Expected D3D_OK, got %#x\n", hr);
8071         /* Check number of vertices*/
8072         new_num_vertices = mesh->lpVtbl->GetNumVertices(mesh);
8073         ok(new_num_vertices == tc[i].exp_new_num_vertices,
8074            "Mesh %d: new_num_vertices == %d, expected %d.\n",
8075            i, new_num_vertices, tc[i].exp_new_num_vertices);
8076         /* Check index buffer */
8077         if (tc[i].options & D3DXMESH_32BIT)
8078         {
8079             hr = mesh->lpVtbl->LockIndexBuffer(mesh, 0, (void**)&indices);
8080             if (FAILED(hr))
8081             {
8082                 skip("Couldn't lock index buffer.\n");
8083                 goto cleanup;
8084             }
8085             for (j = 0; j < VERTS_PER_FACE * tc[i].num_faces; j++)
8086             {
8087                 ok(indices[j] == tc[i].exp_indices[j],
8088                    "Mesh %d: indices[%d] == %d, expected %d\n",
8089                    i, j, indices[j], tc[i].exp_indices[j]);
8090             }
8091         }
8092         else
8093         {
8094             hr = mesh->lpVtbl->LockIndexBuffer(mesh, 0, (void**)&indices_16bit);
8095             if (FAILED(hr))
8096             {
8097                 skip("Couldn't lock index buffer.\n");
8098                 goto cleanup;
8099             }
8100             for (j = 0; j < VERTS_PER_FACE * tc[i].num_faces; j++)
8101             {
8102                 ok(indices_16bit[j] == tc[i].exp_indices[j],
8103                    "Mesh %d: indices_16bit[%d] == %d, expected %d\n",
8104                    i, j, indices_16bit[j], tc[i].exp_indices[j]);
8105             }
8106         }
8107         mesh->lpVtbl->UnlockIndexBuffer(mesh);
8108         indices = NULL;
8109         indices_16bit = NULL;
8110         /* Check adjacency_out */
8111         for (j = 0; j < VERTS_PER_FACE * tc[i].num_faces; j++)
8112         {
8113             ok(adjacency_out[j] == tc[i].adjacency[j],
8114                "Mesh %d: adjacency_out[%d] == %d, expected %d\n",
8115                i, j, adjacency_out[j], tc[i].adjacency[j]);
8116         }
8117         /* Check face_remap */
8118         for (j = 0; j < tc[i].num_faces; j++)
8119         {
8120             ok(face_remap[j] == tc[i].exp_face_remap[j],
8121                "Mesh %d: face_remap[%d] == %d, expected %d\n",
8122                i, j, face_remap[j], tc[i].exp_face_remap[j]);
8123         }
8124         /* Check vertex_remap */
8125         vertex_remap_ptr = vertex_remap->lpVtbl->GetBufferPointer(vertex_remap);
8126         for (j = 0; j < VERTS_PER_FACE * tc[i].num_faces; j++)
8127         {
8128             ok(vertex_remap_ptr[j] == tc[i].exp_vertex_remap[j],
8129                "Mesh %d: vertex_remap_ptr[%d] == %d, expected %d\n",
8130                i, j, vertex_remap_ptr[j], tc[i].exp_vertex_remap[j]);
8131         }
8132         /* Check vertex buffer */
8133         hr = mesh->lpVtbl->LockVertexBuffer(mesh, 0, (void*)&vertices);
8134         if (FAILED(hr))
8135         {
8136             skip("Couldn't lock vertex buffer.\n");
8137             goto cleanup;
8138         }
8139         /* Check contents of re-ordered vertex buffer */
8140         for (j = 0; j < tc[i].exp_new_num_vertices; j++)
8141         {
8142             int index = tc[i].vertex_size*j;
8143             check_vertex_components(__LINE__, i, j, &vertices[index], &tc[i].exp_vertices[index], tc[i].declaration);
8144         }
8145         mesh->lpVtbl->UnlockVertexBuffer(mesh);
8146         vertices = NULL;
8147
8148         /* Free mesh and output data */
8149         HeapFree(GetProcessHeap(), 0, adjacency_out);
8150         adjacency_out = NULL;
8151         HeapFree(GetProcessHeap(), 0, face_remap);
8152         face_remap = NULL;
8153         vertex_remap->lpVtbl->Release(vertex_remap);
8154         vertex_remap = NULL;
8155         mesh->lpVtbl->Release(mesh);
8156         mesh = NULL;
8157     }
8158
8159 cleanup:
8160     HeapFree(GetProcessHeap(), 0, adjacency_out);
8161     HeapFree(GetProcessHeap(), 0, face_remap);
8162     if (indices) mesh->lpVtbl->UnlockIndexBuffer(mesh);
8163     if (indices_16bit) mesh->lpVtbl->UnlockIndexBuffer(mesh);
8164     if (mesh) mesh->lpVtbl->Release(mesh);
8165     if (vertex_remap) vertex_remap->lpVtbl->Release(vertex_remap);
8166     if (vertices) mesh->lpVtbl->UnlockVertexBuffer(mesh);
8167     free_test_context(test_context);
8168 }
8169
8170 START_TEST(mesh)
8171 {
8172     D3DXBoundProbeTest();
8173     D3DXComputeBoundingBoxTest();
8174     D3DXComputeBoundingSphereTest();
8175     D3DXGetFVFVertexSizeTest();
8176     D3DXIntersectTriTest();
8177     D3DXCreateMeshTest();
8178     D3DXCreateMeshFVFTest();
8179     D3DXLoadMeshTest();
8180     D3DXCreateBoxTest();
8181     D3DXCreateSphereTest();
8182     D3DXCreateCylinderTest();
8183     D3DXCreateTextTest();
8184     test_get_decl_length();
8185     test_get_decl_vertex_size();
8186     test_fvf_decl_conversion();
8187     D3DXGenerateAdjacencyTest();
8188     test_update_semantics();
8189     test_create_skin_info();
8190     test_convert_adjacency_to_point_reps();
8191     test_convert_point_reps_to_adjacency();
8192     test_weld_vertices();
8193 }