d3dx9/tests: Fix comparison that expects NAN, since NAN != NAN.
[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 START_TEST(mesh)
4908 {
4909     D3DXBoundProbeTest();
4910     D3DXComputeBoundingBoxTest();
4911     D3DXComputeBoundingSphereTest();
4912     D3DXGetFVFVertexSizeTest();
4913     D3DXIntersectTriTest();
4914     D3DXCreateMeshTest();
4915     D3DXCreateMeshFVFTest();
4916     D3DXLoadMeshTest();
4917     D3DXCreateBoxTest();
4918     D3DXCreateSphereTest();
4919     D3DXCreateCylinderTest();
4920     D3DXCreateTextTest();
4921     test_get_decl_length();
4922     test_get_decl_vertex_size();
4923     test_fvf_decl_conversion();
4924     D3DXGenerateAdjacencyTest();
4925     test_update_semantics();
4926     test_create_skin_info();
4927 }