d3dx9/tests: Test effect generation and material loading for X files.
[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  *
6  * This library is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Lesser General Public
8  * License as published by the Free Software Foundation; either
9  * version 2.1 of the License, or (at your option) any later version.
10  *
11  * This library is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  * Lesser General Public License for more details.
15  *
16  * You should have received a copy of the GNU Lesser General Public
17  * License along with this library; if not, write to the Free Software
18  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
19  */
20
21 #define COBJMACROS
22 #include <stdio.h>
23 #include <float.h>
24 #include "wine/test.h"
25 #include "d3dx9.h"
26
27 /* Set the WINETEST_DEBUG environment variable to be greater than 1 for verbose
28  * function call traces of ID3DXAllocateHierarchy callbacks. */
29 #define TRACECALLBACK if(winetest_debug > 1) trace
30
31 #define admitted_error 0.0001f
32
33 #define ARRAY_SIZE(array) (sizeof(array)/sizeof(*array))
34
35 #define compare_vertex_sizes(type, exp) \
36     got=D3DXGetFVFVertexSize(type); \
37     ok(got==exp, "Expected: %d, Got: %d\n", exp, got);
38
39 #define compare_float(got, exp) \
40     do { \
41         float _got = (got); \
42         float _exp = (exp); \
43         ok(_got == _exp, "Expected: %g, Got: %g\n", _exp, _got); \
44     } while (0)
45
46 static BOOL compare(FLOAT u, FLOAT v)
47 {
48     return (fabs(u-v) < admitted_error);
49 }
50
51 static BOOL compare_vec3(D3DXVECTOR3 u, D3DXVECTOR3 v)
52 {
53     return ( compare(u.x, v.x) && compare(u.y, v.y) && compare(u.z, v.z) );
54 }
55
56 #define check_floats(got, exp, dim) check_floats_(__LINE__, "", got, exp, dim)
57 static void check_floats_(int line, const char *prefix, const float *got, const float *exp, int dim)
58 {
59     int i;
60     char exp_buffer[256] = "";
61     char got_buffer[256] = "";
62     char *exp_buffer_ptr = exp_buffer;
63     char *got_buffer_ptr = got_buffer;
64     BOOL equal = TRUE;
65
66     for (i = 0; i < dim; i++) {
67         if (i) {
68             exp_buffer_ptr += sprintf(exp_buffer_ptr, ", ");
69             got_buffer_ptr += sprintf(got_buffer_ptr, ", ");
70         }
71         equal = equal && compare(*exp, *got);
72         exp_buffer_ptr += sprintf(exp_buffer_ptr, "%g", *exp);
73         got_buffer_ptr += sprintf(got_buffer_ptr, "%g", *got);
74         exp++, got++;
75     }
76     ok_(__FILE__,line)(equal, "%sExpected (%s), got (%s)", prefix, exp_buffer, got_buffer);
77 }
78
79 struct vertex
80 {
81     D3DXVECTOR3 position;
82     D3DXVECTOR3 normal;
83 };
84
85 typedef WORD face[3];
86
87 static BOOL compare_face(face a, face b)
88 {
89     return (a[0]==b[0] && a[1] == b[1] && a[2] == b[2]);
90 }
91
92 struct mesh
93 {
94     DWORD number_of_vertices;
95     struct vertex *vertices;
96
97     DWORD number_of_faces;
98     face *faces;
99
100     DWORD fvf;
101     UINT vertex_size;
102 };
103
104 static void free_mesh(struct mesh *mesh)
105 {
106     HeapFree(GetProcessHeap(), 0, mesh->faces);
107     HeapFree(GetProcessHeap(), 0, mesh->vertices);
108 }
109
110 static BOOL new_mesh(struct mesh *mesh, DWORD number_of_vertices, DWORD number_of_faces)
111 {
112     mesh->vertices = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, number_of_vertices * sizeof(*mesh->vertices));
113     if (!mesh->vertices)
114     {
115         return FALSE;
116     }
117     mesh->number_of_vertices = number_of_vertices;
118
119     mesh->faces = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, number_of_faces * sizeof(*mesh->faces));
120     if (!mesh->faces)
121     {
122         HeapFree(GetProcessHeap(), 0, mesh->vertices);
123         return FALSE;
124     }
125     mesh->number_of_faces = number_of_faces;
126
127     return TRUE;
128 }
129
130 static void compare_mesh(const char *name, ID3DXMesh *d3dxmesh, struct mesh *mesh)
131 {
132     HRESULT hr;
133     DWORD number_of_vertices, number_of_faces;
134     IDirect3DVertexBuffer9 *vertex_buffer;
135     IDirect3DIndexBuffer9 *index_buffer;
136     D3DVERTEXBUFFER_DESC vertex_buffer_description;
137     D3DINDEXBUFFER_DESC index_buffer_description;
138     struct vertex *vertices;
139     face *faces;
140     int expected, i;
141
142     number_of_vertices = d3dxmesh->lpVtbl->GetNumVertices(d3dxmesh);
143     ok(number_of_vertices == mesh->number_of_vertices, "Test %s, result %u, expected %d\n",
144        name, number_of_vertices, mesh->number_of_vertices);
145
146     number_of_faces = d3dxmesh->lpVtbl->GetNumFaces(d3dxmesh);
147     ok(number_of_faces == mesh->number_of_faces, "Test %s, result %u, expected %d\n",
148        name, number_of_faces, mesh->number_of_faces);
149
150     /* vertex buffer */
151     hr = d3dxmesh->lpVtbl->GetVertexBuffer(d3dxmesh, &vertex_buffer);
152     ok(hr == D3D_OK, "Test %s, result %x, expected 0 (D3D_OK)\n", name, hr);
153
154     if (hr != D3D_OK)
155     {
156         skip("Couldn't get vertex buffer\n");
157     }
158     else
159     {
160         hr = IDirect3DVertexBuffer9_GetDesc(vertex_buffer, &vertex_buffer_description);
161         ok(hr == D3D_OK, "Test %s, result %x, expected 0 (D3D_OK)\n", name, hr);
162
163         if (hr != D3D_OK)
164         {
165             skip("Couldn't get vertex buffer description\n");
166         }
167         else
168         {
169             ok(vertex_buffer_description.Format == D3DFMT_VERTEXDATA, "Test %s, result %x, expected %x (D3DFMT_VERTEXDATA)\n",
170                name, vertex_buffer_description.Format, D3DFMT_VERTEXDATA);
171             ok(vertex_buffer_description.Type == D3DRTYPE_VERTEXBUFFER, "Test %s, result %x, expected %x (D3DRTYPE_VERTEXBUFFER)\n",
172                name, vertex_buffer_description.Type, D3DRTYPE_VERTEXBUFFER);
173             ok(vertex_buffer_description.Usage == 0, "Test %s, result %x, expected %x\n", name, vertex_buffer_description.Usage, 0);
174             ok(vertex_buffer_description.Pool == D3DPOOL_MANAGED, "Test %s, result %x, expected %x (D3DPOOL_MANAGED)\n",
175                name, vertex_buffer_description.Pool, D3DPOOL_MANAGED);
176             ok(vertex_buffer_description.FVF == mesh->fvf, "Test %s, result %x, expected %x\n",
177                name, vertex_buffer_description.FVF, mesh->fvf);
178             if (mesh->fvf == 0)
179             {
180                 expected = number_of_vertices * mesh->vertex_size;
181             }
182             else
183             {
184                 expected = number_of_vertices * D3DXGetFVFVertexSize(mesh->fvf);
185             }
186             ok(vertex_buffer_description.Size == expected, "Test %s, result %x, expected %x\n",
187                name, vertex_buffer_description.Size, expected);
188         }
189
190         /* specify offset and size to avoid potential overruns */
191         hr = IDirect3DVertexBuffer9_Lock(vertex_buffer, 0, number_of_vertices * sizeof(D3DXVECTOR3) * 2,
192                                          (LPVOID *)&vertices, D3DLOCK_DISCARD);
193         ok(hr == D3D_OK, "Test %s, result %x, expected 0 (D3D_OK)\n", name, hr);
194
195         if (hr != D3D_OK)
196         {
197             skip("Couldn't lock vertex buffer\n");
198         }
199         else
200         {
201             for (i = 0; i < number_of_vertices; i++)
202             {
203                 ok(compare_vec3(vertices[i].position, mesh->vertices[i].position),
204                    "Test %s, vertex position %d, result (%g, %g, %g), expected (%g, %g, %g)\n", name, i,
205                    vertices[i].position.x, vertices[i].position.y, vertices[i].position.z,
206                    mesh->vertices[i].position.x, mesh->vertices[i].position.y, mesh->vertices[i].position.z);
207                 ok(compare_vec3(vertices[i].normal, mesh->vertices[i].normal),
208                    "Test %s, vertex normal %d, result (%g, %g, %g), expected (%g, %g, %g)\n", name, i,
209                    vertices[i].normal.x, vertices[i].normal.y, vertices[i].normal.z,
210                    mesh->vertices[i].normal.x, mesh->vertices[i].normal.y, mesh->vertices[i].normal.z);
211             }
212
213             IDirect3DVertexBuffer9_Unlock(vertex_buffer);
214         }
215
216         IDirect3DVertexBuffer9_Release(vertex_buffer);
217     }
218
219     /* index buffer */
220     hr = d3dxmesh->lpVtbl->GetIndexBuffer(d3dxmesh, &index_buffer);
221     ok(hr == D3D_OK, "Test %s, result %x, expected 0 (D3D_OK)\n", name, hr);
222
223     if (!index_buffer)
224     {
225         skip("Couldn't get index buffer\n");
226     }
227     else
228     {
229         hr = IDirect3DIndexBuffer9_GetDesc(index_buffer, &index_buffer_description);
230         ok(hr == D3D_OK, "Test %s, result %x, expected 0 (D3D_OK)\n", name, hr);
231
232         if (hr != D3D_OK)
233         {
234             skip("Couldn't get index buffer description\n");
235         }
236         else
237         {
238             ok(index_buffer_description.Format == D3DFMT_INDEX16, "Test %s, result %x, expected %x (D3DFMT_INDEX16)\n",
239                name, index_buffer_description.Format, D3DFMT_INDEX16);
240             ok(index_buffer_description.Type == D3DRTYPE_INDEXBUFFER, "Test %s, result %x, expected %x (D3DRTYPE_INDEXBUFFER)\n",
241                name, index_buffer_description.Type, D3DRTYPE_INDEXBUFFER);
242             todo_wine ok(index_buffer_description.Usage == 0, "Test %s, result %x, expected %x\n", name, index_buffer_description.Usage, 0);
243             ok(index_buffer_description.Pool == D3DPOOL_MANAGED, "Test %s, result %x, expected %x (D3DPOOL_MANAGED)\n",
244                name, index_buffer_description.Pool, D3DPOOL_MANAGED);
245             expected = number_of_faces * sizeof(WORD) * 3;
246             ok(index_buffer_description.Size == expected, "Test %s, result %x, expected %x\n",
247                name, index_buffer_description.Size, expected);
248         }
249
250         /* specify offset and size to avoid potential overruns */
251         hr = IDirect3DIndexBuffer9_Lock(index_buffer, 0, number_of_faces * sizeof(WORD) * 3,
252                                         (LPVOID *)&faces, D3DLOCK_DISCARD);
253         ok(hr == D3D_OK, "Test %s, result %x, expected 0 (D3D_OK)\n", name, hr);
254
255         if (hr != D3D_OK)
256         {
257             skip("Couldn't lock index buffer\n");
258         }
259         else
260         {
261             for (i = 0; i < number_of_faces; i++)
262             {
263                 ok(compare_face(faces[i], mesh->faces[i]),
264                    "Test %s, face %d, result (%u, %u, %u), expected (%u, %u, %u)\n", name, i,
265                    faces[i][0], faces[i][1], faces[i][2],
266                    mesh->faces[i][0], mesh->faces[i][1], mesh->faces[i][2]);
267             }
268
269             IDirect3DIndexBuffer9_Unlock(index_buffer);
270         }
271
272         IDirect3DIndexBuffer9_Release(index_buffer);
273     }
274 }
275
276 static void D3DXBoundProbeTest(void)
277 {
278     BOOL result;
279     D3DXVECTOR3 bottom_point, center, top_point, raydirection, rayposition;
280     FLOAT radius;
281
282 /*____________Test the Box case___________________________*/
283     bottom_point.x = -3.0f; bottom_point.y = -2.0f; bottom_point.z = -1.0f;
284     top_point.x = 7.0f; top_point.y = 8.0f; top_point.z = 9.0f;
285
286     raydirection.x = -4.0f; raydirection.y = -5.0f; raydirection.z = -6.0f;
287     rayposition.x = 5.0f; rayposition.y = 5.0f; rayposition.z = 11.0f;
288     result = D3DXBoxBoundProbe(&bottom_point, &top_point, &rayposition, &raydirection);
289     ok(result == TRUE, "expected TRUE, received FALSE\n");
290
291     raydirection.x = 4.0f; raydirection.y = 5.0f; raydirection.z = 6.0f;
292     rayposition.x = 5.0f; rayposition.y = 5.0f; rayposition.z = 11.0f;
293     result = D3DXBoxBoundProbe(&bottom_point, &top_point, &rayposition, &raydirection);
294     ok(result == FALSE, "expected FALSE, received TRUE\n");
295
296     rayposition.x = -4.0f; rayposition.y = 1.0f; rayposition.z = -2.0f;
297     result = D3DXBoxBoundProbe(&bottom_point, &top_point, &rayposition, &raydirection);
298     ok(result == TRUE, "expected TRUE, received FALSE\n");
299
300     bottom_point.x = 1.0f; bottom_point.y = 0.0f; bottom_point.z = 0.0f;
301     top_point.x = 1.0f; top_point.y = 0.0f; top_point.z = 0.0f;
302     rayposition.x = 0.0f; rayposition.y = 1.0f; rayposition.z = 0.0f;
303     raydirection.x = 0.0f; raydirection.y = 3.0f; raydirection.z = 0.0f;
304     result = D3DXBoxBoundProbe(&bottom_point, &top_point, &rayposition, &raydirection);
305     ok(result == FALSE, "expected FALSE, received TRUE\n");
306
307     bottom_point.x = 1.0f; bottom_point.y = 2.0f; bottom_point.z = 3.0f;
308     top_point.x = 10.0f; top_point.y = 15.0f; top_point.z = 20.0f;
309
310     raydirection.x = 7.0f; raydirection.y = 8.0f; raydirection.z = 9.0f;
311     rayposition.x = 3.0f; rayposition.y = 7.0f; rayposition.z = -6.0f;
312     result = D3DXBoxBoundProbe(&bottom_point, &top_point, &rayposition, &raydirection);
313     ok(result == TRUE, "expected TRUE, received FALSE\n");
314
315     bottom_point.x = 0.0f; bottom_point.y = 0.0f; bottom_point.z = 0.0f;
316     top_point.x = 1.0f; top_point.y = 1.0f; top_point.z = 1.0f;
317
318     raydirection.x = 0.0f; raydirection.y = 1.0f; raydirection.z = .0f;
319     rayposition.x = -3.0f; rayposition.y = 0.0f; rayposition.z = 0.0f;
320     result = D3DXBoxBoundProbe(&bottom_point, &top_point, &rayposition, &raydirection);
321     ok(result == FALSE, "expected FALSE, received TRUE\n");
322
323     raydirection.x = 1.0f; raydirection.y = 0.0f; raydirection.z = .0f;
324     rayposition.x = -3.0f; rayposition.y = 0.0f; rayposition.z = 0.0f;
325     result = D3DXBoxBoundProbe(&bottom_point, &top_point, &rayposition, &raydirection);
326     ok(result == TRUE, "expected TRUE, received FALSE\n");
327
328 /*____________Test the Sphere case________________________*/
329     radius = sqrt(77.0f);
330     center.x = 1.0f; center.y = 2.0f; center.z = 3.0f;
331     raydirection.x = 2.0f; raydirection.y = -4.0f; raydirection.z = 2.0f;
332
333     rayposition.x = 5.0f; rayposition.y = 5.0f; rayposition.z = 9.0f;
334     result = D3DXSphereBoundProbe(&center, radius, &rayposition, &raydirection);
335     ok(result == TRUE, "expected TRUE, received FALSE\n");
336
337     rayposition.x = 45.0f; rayposition.y = -75.0f; rayposition.z = 49.0f;
338     result = D3DXSphereBoundProbe(&center, radius, &rayposition, &raydirection);
339     ok(result == FALSE, "expected FALSE, received TRUE\n");
340
341     rayposition.x = 5.0f; rayposition.y = 11.0f; rayposition.z = 9.0f;
342     result = D3DXSphereBoundProbe(&center, radius, &rayposition, &raydirection);
343     ok(result == FALSE, "expected FALSE, received TRUE\n");
344 }
345
346 static void D3DXComputeBoundingBoxTest(void)
347 {
348     D3DXVECTOR3 exp_max, exp_min, got_max, got_min, vertex[5];
349     HRESULT hr;
350
351     vertex[0].x = 1.0f; vertex[0].y = 1.0f; vertex[0].z = 1.0f;
352     vertex[1].x = 1.0f; vertex[1].y = 1.0f; vertex[1].z = 1.0f;
353     vertex[2].x = 1.0f; vertex[2].y = 1.0f; vertex[2].z = 1.0f;
354     vertex[3].x = 1.0f; vertex[3].y = 1.0f; vertex[3].z = 1.0f;
355     vertex[4].x = 9.0f; vertex[4].y = 9.0f; vertex[4].z = 9.0f;
356
357     exp_min.x = 1.0f; exp_min.y = 1.0f; exp_min.z = 1.0f;
358     exp_max.x = 9.0f; exp_max.y = 9.0f; exp_max.z = 9.0f;
359
360     hr = D3DXComputeBoundingBox(&vertex[3],2,D3DXGetFVFVertexSize(D3DFVF_XYZ),&got_min,&got_max);
361
362     ok( hr == D3D_OK, "Expected D3D_OK, got %#x\n", hr);
363     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);
364     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);
365
366 /*________________________*/
367
368     vertex[0].x = 2.0f; vertex[0].y = 5.9f; vertex[0].z = -1.2f;
369     vertex[1].x = -1.87f; vertex[1].y = 7.9f; vertex[1].z = 7.4f;
370     vertex[2].x = 7.43f; vertex[2].y = -0.9f; vertex[2].z = 11.9f;
371     vertex[3].x = -6.92f; vertex[3].y = 6.3f; vertex[3].z = -3.8f;
372     vertex[4].x = 11.4f; vertex[4].y = -8.1f; vertex[4].z = 4.5f;
373
374     exp_min.x = -6.92f; exp_min.y = -8.1f; exp_min.z = -3.80f;
375     exp_max.x = 11.4f; exp_max.y = 7.90f; exp_max.z = 11.9f;
376
377     hr = D3DXComputeBoundingBox(&vertex[0],5,D3DXGetFVFVertexSize(D3DFVF_XYZ),&got_min,&got_max);
378
379     ok( hr == D3D_OK, "Expected D3D_OK, got %#x\n", hr);
380     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);
381     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);
382
383 /*________________________*/
384
385     vertex[0].x = 2.0f; vertex[0].y = 5.9f; vertex[0].z = -1.2f;
386     vertex[1].x = -1.87f; vertex[1].y = 7.9f; vertex[1].z = 7.4f;
387     vertex[2].x = 7.43f; vertex[2].y = -0.9f; vertex[2].z = 11.9f;
388     vertex[3].x = -6.92f; vertex[3].y = 6.3f; vertex[3].z = -3.8f;
389     vertex[4].x = 11.4f; vertex[4].y = -8.1f; vertex[4].z = 4.5f;
390
391     exp_min.x = -6.92f; exp_min.y = -0.9f; exp_min.z = -3.8f;
392     exp_max.x = 7.43f; exp_max.y = 7.90f; exp_max.z = 11.9f;
393
394     hr = D3DXComputeBoundingBox(&vertex[0],4,D3DXGetFVFVertexSize(D3DFVF_XYZ),&got_min,&got_max);
395
396     ok( hr == D3D_OK, "Expected D3D_OK, got %#x\n", hr);
397     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);
398     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);
399
400 /*________________________*/
401     hr = D3DXComputeBoundingBox(NULL,5,D3DXGetFVFVertexSize(D3DFVF_XYZ),&got_min,&got_max);
402     ok( hr == D3DERR_INVALIDCALL, "Expected D3DERR_INVALIDCALL, got %#x\n", hr);
403
404 /*________________________*/
405     hr = D3DXComputeBoundingBox(&vertex[3],5,D3DXGetFVFVertexSize(D3DFVF_XYZ),NULL,&got_max);
406     ok( hr == D3DERR_INVALIDCALL, "Expected D3DERR_INVALIDCALL, got %#x\n", hr);
407
408 /*________________________*/
409     hr = D3DXComputeBoundingBox(&vertex[3],5,D3DXGetFVFVertexSize(D3DFVF_XYZ),&got_min,NULL);
410     ok( hr == D3DERR_INVALIDCALL, "Expected D3DERR_INVALIDCALL, got %#x\n", hr);
411 }
412
413 static void D3DXComputeBoundingSphereTest(void)
414 {
415     D3DXVECTOR3 exp_cen, got_cen, vertex[5];
416     FLOAT exp_rad, got_rad;
417     HRESULT hr;
418
419     vertex[0].x = 1.0f; vertex[0].y = 1.0f; vertex[0].z = 1.0f;
420     vertex[1].x = 1.0f; vertex[1].y = 1.0f; vertex[1].z = 1.0f;
421     vertex[2].x = 1.0f; vertex[2].y = 1.0f; vertex[2].z = 1.0f;
422     vertex[3].x = 1.0f; vertex[3].y = 1.0f; vertex[3].z = 1.0f;
423     vertex[4].x = 9.0f; vertex[4].y = 9.0f; vertex[4].z = 9.0f;
424
425     exp_rad = 6.928203f;
426     exp_cen.x = 5.0; exp_cen.y = 5.0; exp_cen.z = 5.0;
427
428     hr = D3DXComputeBoundingSphere(&vertex[3],2,D3DXGetFVFVertexSize(D3DFVF_XYZ),&got_cen,&got_rad);
429
430     ok( hr == D3D_OK, "Expected D3D_OK, got %#x\n", hr);
431     ok( compare(exp_rad, got_rad), "Expected radius: %f, got radius: %f\n", exp_rad, got_rad);
432     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);
433
434 /*________________________*/
435
436     vertex[0].x = 2.0f; vertex[0].y = 5.9f; vertex[0].z = -1.2f;
437     vertex[1].x = -1.87f; vertex[1].y = 7.9f; vertex[1].z = 7.4f;
438     vertex[2].x = 7.43f; vertex[2].y = -0.9f; vertex[2].z = 11.9f;
439     vertex[3].x = -6.92f; vertex[3].y = 6.3f; vertex[3].z = -3.8f;
440     vertex[4].x = 11.4f; vertex[4].y = -8.1f; vertex[4].z = 4.5f;
441
442     exp_rad = 13.707883f;
443     exp_cen.x = 2.408f; exp_cen.y = 2.22f; exp_cen.z = 3.76f;
444
445     hr = D3DXComputeBoundingSphere(&vertex[0],5,D3DXGetFVFVertexSize(D3DFVF_XYZ),&got_cen,&got_rad);
446
447     ok( hr == D3D_OK, "Expected D3D_OK, got %#x\n", hr);
448     ok( compare(exp_rad, got_rad), "Expected radius: %f, got radius: %f\n", exp_rad, got_rad);
449     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);
450
451 /*________________________*/
452     hr = D3DXComputeBoundingSphere(NULL,5,D3DXGetFVFVertexSize(D3DFVF_XYZ),&got_cen,&got_rad);
453     ok( hr == D3DERR_INVALIDCALL, "Expected D3DERR_INVALIDCALL, got %#x\n", hr);
454
455 /*________________________*/
456     hr = D3DXComputeBoundingSphere(&vertex[3],5,D3DXGetFVFVertexSize(D3DFVF_XYZ),NULL,&got_rad);
457     ok( hr == D3DERR_INVALIDCALL, "Expected D3DERR_INVALIDCALL, got %#x\n", hr);
458
459 /*________________________*/
460     hr = D3DXComputeBoundingSphere(&vertex[3],5,D3DXGetFVFVertexSize(D3DFVF_XYZ),&got_cen,NULL);
461     ok( hr == D3DERR_INVALIDCALL, "Expected D3DERR_INVALIDCALL, got %#x\n", hr);
462 }
463
464 static void print_elements(const D3DVERTEXELEMENT9 *elements)
465 {
466     D3DVERTEXELEMENT9 last = D3DDECL_END();
467     const D3DVERTEXELEMENT9 *ptr = elements;
468     int count = 0;
469
470     while (memcmp(ptr, &last, sizeof(D3DVERTEXELEMENT9)))
471     {
472         trace(
473             "[Element %d] Stream = %d, Offset = %d, Type = %d, Method = %d, Usage = %d, UsageIndex = %d\n",
474              count, ptr->Stream, ptr->Offset, ptr->Type, ptr->Method, ptr->Usage, ptr->UsageIndex);
475         ptr++;
476         count++;
477     }
478 }
479
480 static void compare_elements(const D3DVERTEXELEMENT9 *elements, const D3DVERTEXELEMENT9 *expected_elements,
481         unsigned int line, unsigned int test_id)
482 {
483     D3DVERTEXELEMENT9 last = D3DDECL_END();
484     unsigned int i;
485
486     for (i = 0; i < MAX_FVF_DECL_SIZE; i++)
487     {
488         int end1 = memcmp(&elements[i], &last, sizeof(last));
489         int end2 = memcmp(&expected_elements[i], &last, sizeof(last));
490         int status;
491
492         if (!end1 && !end2) break;
493
494         status = !end1 ^ !end2;
495         ok(!status, "Line %u, test %u: Mismatch in size, test declaration is %s than expected.\n",
496                 line, test_id, end1 ? "shorter" : "longer");
497         if (status)
498         {
499             print_elements(elements);
500             break;
501         }
502
503         status = memcmp(&elements[i], &expected_elements[i], sizeof(D3DVERTEXELEMENT9));
504         ok(!status, "Line %u, test %u: Mismatch in element %u.\n", line, test_id, i);
505         if (status)
506         {
507             print_elements(elements);
508             break;
509         }
510     }
511 }
512
513 static void test_fvf_to_decl(DWORD test_fvf, const D3DVERTEXELEMENT9 expected_elements[],
514         HRESULT expected_hr, unsigned int line, unsigned int test_id)
515 {
516     HRESULT hr;
517     D3DVERTEXELEMENT9 decl[MAX_FVF_DECL_SIZE];
518
519     hr = D3DXDeclaratorFromFVF(test_fvf, decl);
520     ok(hr == expected_hr,
521             "Line %u, test %u: D3DXDeclaratorFromFVF returned %#x, expected %#x.\n",
522             line, test_id, hr, expected_hr);
523     if (SUCCEEDED(hr)) compare_elements(decl, expected_elements, line, test_id);
524 }
525
526 static void test_decl_to_fvf(const D3DVERTEXELEMENT9 *decl, DWORD expected_fvf,
527         HRESULT expected_hr, unsigned int line, unsigned int test_id)
528 {
529     HRESULT hr;
530     DWORD result_fvf = 0xdeadbeef;
531
532     hr = D3DXFVFFromDeclarator(decl, &result_fvf);
533     ok(hr == expected_hr,
534        "Line %u, test %u: D3DXFVFFromDeclarator returned %#x, expected %#x.\n",
535        line, test_id, hr, expected_hr);
536     if (SUCCEEDED(hr))
537     {
538         ok(expected_fvf == result_fvf, "Line %u, test %u: Got FVF %#x, expected %#x.\n",
539                 line, test_id, result_fvf, expected_fvf);
540     }
541 }
542
543 static void test_fvf_decl_conversion(void)
544 {
545     static const struct
546     {
547         D3DVERTEXELEMENT9 decl[MAXD3DDECLLENGTH + 1];
548         DWORD fvf;
549     }
550     test_data[] =
551     {
552         {{
553             D3DDECL_END(),
554         }, 0},
555         {{
556             {0, 0, D3DDECLTYPE_FLOAT3, 0, D3DDECLUSAGE_POSITION, 0},
557             D3DDECL_END(),
558         }, D3DFVF_XYZ},
559         {{
560             {0, 0, D3DDECLTYPE_FLOAT4, 0, D3DDECLUSAGE_POSITIONT, 0},
561             D3DDECL_END(),
562         }, D3DFVF_XYZRHW},
563         {{
564             {0, 0, D3DDECLTYPE_FLOAT4, 0, D3DDECLUSAGE_POSITIONT, 0},
565             D3DDECL_END(),
566         }, D3DFVF_XYZRHW},
567         {{
568             {0, 0, D3DDECLTYPE_FLOAT3, 0, D3DDECLUSAGE_POSITION, 0},
569             {0, 12, D3DDECLTYPE_FLOAT1, 0, D3DDECLUSAGE_BLENDWEIGHT, 0},
570             D3DDECL_END(),
571         }, D3DFVF_XYZB1},
572         {{
573             {0, 0, D3DDECLTYPE_FLOAT3, 0, D3DDECLUSAGE_POSITION, 0},
574             {0, 12, D3DDECLTYPE_UBYTE4, 0, D3DDECLUSAGE_BLENDINDICES, 0},
575             D3DDECL_END(),
576         }, D3DFVF_XYZB1 | D3DFVF_LASTBETA_UBYTE4},
577         {{
578             {0, 0, D3DDECLTYPE_FLOAT3, 0, D3DDECLUSAGE_POSITION, 0},
579             {0, 12, D3DDECLTYPE_D3DCOLOR, 0, D3DDECLUSAGE_BLENDINDICES, 0},
580             D3DDECL_END(),
581         }, D3DFVF_XYZB1 | D3DFVF_LASTBETA_D3DCOLOR},
582         {{
583             {0, 0, D3DDECLTYPE_FLOAT3, 0, D3DDECLUSAGE_POSITION, 0},
584             {0, 12, D3DDECLTYPE_FLOAT2, 0, D3DDECLUSAGE_BLENDWEIGHT, 0},
585             D3DDECL_END(),
586         }, D3DFVF_XYZB2},
587         {{
588             {0, 0, D3DDECLTYPE_FLOAT3, 0, D3DDECLUSAGE_POSITION, 0},
589             {0, 12, D3DDECLTYPE_FLOAT1, 0, D3DDECLUSAGE_BLENDWEIGHT, 0},
590             {0, 16, D3DDECLTYPE_UBYTE4, 0, D3DDECLUSAGE_BLENDINDICES, 0},
591             D3DDECL_END(),
592         }, D3DFVF_XYZB2 | D3DFVF_LASTBETA_UBYTE4},
593         {{
594             {0, 0, D3DDECLTYPE_FLOAT3, 0, D3DDECLUSAGE_POSITION, 0},
595             {0, 12, D3DDECLTYPE_FLOAT1, 0, D3DDECLUSAGE_BLENDWEIGHT, 0},
596             {0, 16, D3DDECLTYPE_D3DCOLOR, 0, D3DDECLUSAGE_BLENDINDICES, 0},
597             D3DDECL_END(),
598         }, D3DFVF_XYZB2 | D3DFVF_LASTBETA_D3DCOLOR},
599         {{
600             {0, 0, D3DDECLTYPE_FLOAT3, 0, D3DDECLUSAGE_POSITION, 0},
601             {0, 12, D3DDECLTYPE_FLOAT3, 0, D3DDECLUSAGE_BLENDWEIGHT, 0},
602             D3DDECL_END(),
603         }, D3DFVF_XYZB3},
604         {{
605             {0, 0, D3DDECLTYPE_FLOAT3, 0, D3DDECLUSAGE_POSITION, 0},
606             {0, 12, D3DDECLTYPE_FLOAT2, 0, D3DDECLUSAGE_BLENDWEIGHT, 0},
607             {0, 20, D3DDECLTYPE_UBYTE4, 0, D3DDECLUSAGE_BLENDINDICES, 0},
608             D3DDECL_END(),
609         }, D3DFVF_XYZB3 | D3DFVF_LASTBETA_UBYTE4},
610         {{
611             {0, 0, D3DDECLTYPE_FLOAT3, 0, D3DDECLUSAGE_POSITION, 0},
612             {0, 12, D3DDECLTYPE_FLOAT2, 0, D3DDECLUSAGE_BLENDWEIGHT, 0},
613             {0, 20, D3DDECLTYPE_D3DCOLOR, 0, D3DDECLUSAGE_BLENDINDICES, 0},
614             D3DDECL_END(),
615         }, D3DFVF_XYZB3 | D3DFVF_LASTBETA_D3DCOLOR},
616         {{
617             {0, 0, D3DDECLTYPE_FLOAT3, 0, D3DDECLUSAGE_POSITION, 0},
618             {0, 12, D3DDECLTYPE_FLOAT4, 0, D3DDECLUSAGE_BLENDWEIGHT, 0},
619             D3DDECL_END(),
620         }, D3DFVF_XYZB4},
621         {{
622             {0, 0, D3DDECLTYPE_FLOAT3, 0, D3DDECLUSAGE_POSITION, 0},
623             {0, 12, D3DDECLTYPE_FLOAT3, 0, D3DDECLUSAGE_BLENDWEIGHT, 0},
624             {0, 24, D3DDECLTYPE_UBYTE4, 0, D3DDECLUSAGE_BLENDINDICES, 0},
625             D3DDECL_END(),
626         }, D3DFVF_XYZB4 | D3DFVF_LASTBETA_UBYTE4},
627         {{
628             {0, 0, D3DDECLTYPE_FLOAT3, 0, D3DDECLUSAGE_POSITION, 0},
629             {0, 12, D3DDECLTYPE_FLOAT3, 0, D3DDECLUSAGE_BLENDWEIGHT, 0},
630             {0, 24, D3DDECLTYPE_D3DCOLOR, 0, D3DDECLUSAGE_BLENDINDICES, 0},
631             D3DDECL_END(),
632         }, D3DFVF_XYZB4 | D3DFVF_LASTBETA_D3DCOLOR},
633         {{
634             {0, 0, D3DDECLTYPE_FLOAT3, 0, D3DDECLUSAGE_POSITION, 0},
635             {0, 12, D3DDECLTYPE_FLOAT4, 0, D3DDECLUSAGE_BLENDWEIGHT, 0},
636             {0, 28, D3DDECLTYPE_UBYTE4, 0, D3DDECLUSAGE_BLENDINDICES, 0},
637             D3DDECL_END(),
638         }, D3DFVF_XYZB5 | D3DFVF_LASTBETA_UBYTE4},
639         {{
640             {0, 0, D3DDECLTYPE_FLOAT3, 0, D3DDECLUSAGE_POSITION, 0},
641             {0, 12, D3DDECLTYPE_FLOAT4, 0, D3DDECLUSAGE_BLENDWEIGHT, 0},
642             {0, 28, D3DDECLTYPE_D3DCOLOR, 0, D3DDECLUSAGE_BLENDINDICES, 0},
643             D3DDECL_END(),
644         }, D3DFVF_XYZB5 | D3DFVF_LASTBETA_D3DCOLOR},
645         {{
646             {0, 0, D3DDECLTYPE_FLOAT3, 0, D3DDECLUSAGE_NORMAL, 0},
647             D3DDECL_END(),
648         }, D3DFVF_NORMAL},
649         {{
650             {0, 0, D3DDECLTYPE_FLOAT3, 0, D3DDECLUSAGE_NORMAL, 0},
651             {0, 12, D3DDECLTYPE_D3DCOLOR, 0, D3DDECLUSAGE_COLOR, 0},
652             D3DDECL_END(),
653         }, D3DFVF_NORMAL | D3DFVF_DIFFUSE},
654         {{
655             {0, 0, D3DDECLTYPE_FLOAT1, 0, D3DDECLUSAGE_PSIZE, 0},
656             D3DDECL_END(),
657         }, D3DFVF_PSIZE},
658         {{
659             {0, 0, D3DDECLTYPE_D3DCOLOR, 0, D3DDECLUSAGE_COLOR, 0},
660             D3DDECL_END(),
661         }, D3DFVF_DIFFUSE},
662         {{
663             {0, 0, D3DDECLTYPE_D3DCOLOR, 0, D3DDECLUSAGE_COLOR, 1},
664             D3DDECL_END(),
665         }, D3DFVF_SPECULAR},
666         /* Make sure textures of different sizes work. */
667         {{
668             {0, 0, D3DDECLTYPE_FLOAT1, 0, D3DDECLUSAGE_TEXCOORD, 0},
669             D3DDECL_END(),
670         }, D3DFVF_TEXCOORDSIZE1(0) | D3DFVF_TEX1},
671         {{
672             {0, 0, D3DDECLTYPE_FLOAT2, 0, D3DDECLUSAGE_TEXCOORD, 0},
673             D3DDECL_END(),
674         }, D3DFVF_TEXCOORDSIZE2(0) | D3DFVF_TEX1},
675         {{
676             {0, 0, D3DDECLTYPE_FLOAT3, 0, D3DDECLUSAGE_TEXCOORD, 0},
677             D3DDECL_END(),
678         }, D3DFVF_TEXCOORDSIZE3(0) | D3DFVF_TEX1},
679         {{
680             {0, 0, D3DDECLTYPE_FLOAT4, 0, D3DDECLUSAGE_TEXCOORD, 0},
681             D3DDECL_END(),
682         }, D3DFVF_TEXCOORDSIZE4(0) | D3DFVF_TEX1},
683         /* Make sure the TEXCOORD index works correctly - try several textures. */
684         {{
685             {0, 0, D3DDECLTYPE_FLOAT1, 0, D3DDECLUSAGE_TEXCOORD, 0},
686             {0, 4, D3DDECLTYPE_FLOAT3, 0, D3DDECLUSAGE_TEXCOORD, 1},
687             {0, 16, D3DDECLTYPE_FLOAT2, 0, D3DDECLUSAGE_TEXCOORD, 2},
688             {0, 24, D3DDECLTYPE_FLOAT4, 0, D3DDECLUSAGE_TEXCOORD, 3},
689             D3DDECL_END(),
690         }, D3DFVF_TEX4 | D3DFVF_TEXCOORDSIZE1(0) | D3DFVF_TEXCOORDSIZE3(1)
691                 | D3DFVF_TEXCOORDSIZE2(2) | D3DFVF_TEXCOORDSIZE4(3)},
692         /* Now try some combination tests. */
693         {{
694             {0, 0, D3DDECLTYPE_FLOAT3, 0, D3DDECLUSAGE_POSITION, 0},
695             {0, 12, D3DDECLTYPE_FLOAT4, 0, D3DDECLUSAGE_BLENDWEIGHT, 0},
696             {0, 28, D3DDECLTYPE_D3DCOLOR, 0, D3DDECLUSAGE_COLOR, 0},
697             {0, 32, D3DDECLTYPE_D3DCOLOR, 0, D3DDECLUSAGE_COLOR, 1},
698             {0, 36, D3DDECLTYPE_FLOAT2, 0, D3DDECLUSAGE_TEXCOORD, 0},
699             {0, 44, D3DDECLTYPE_FLOAT3, 0, D3DDECLUSAGE_TEXCOORD, 1},
700             D3DDECL_END(),
701         }, D3DFVF_XYZB4 | D3DFVF_DIFFUSE | D3DFVF_SPECULAR | D3DFVF_TEX2
702                 | D3DFVF_TEXCOORDSIZE2(0) | D3DFVF_TEXCOORDSIZE3(1)},
703         {{
704             {0, 0, D3DDECLTYPE_FLOAT3, 0, D3DDECLUSAGE_POSITION, 0},
705             {0, 12, D3DDECLTYPE_FLOAT3, 0, D3DDECLUSAGE_NORMAL, 0},
706             {0, 24, D3DDECLTYPE_FLOAT1, 0, D3DDECLUSAGE_PSIZE, 0},
707             {0, 28, D3DDECLTYPE_D3DCOLOR, 0, D3DDECLUSAGE_COLOR, 1},
708             {0, 32, D3DDECLTYPE_FLOAT1, 0, D3DDECLUSAGE_TEXCOORD, 0},
709             {0, 36, D3DDECLTYPE_FLOAT4, 0, D3DDECLUSAGE_TEXCOORD, 1},
710             D3DDECL_END(),
711         }, D3DFVF_XYZ | D3DFVF_NORMAL | D3DFVF_PSIZE | D3DFVF_SPECULAR | D3DFVF_TEX2
712                 | D3DFVF_TEXCOORDSIZE1(0) | D3DFVF_TEXCOORDSIZE4(1)},
713     };
714     unsigned int i;
715
716     for (i = 0; i < sizeof(test_data) / sizeof(*test_data); ++i)
717     {
718         test_decl_to_fvf(test_data[i].decl, test_data[i].fvf, D3D_OK, __LINE__, i);
719         test_fvf_to_decl(test_data[i].fvf, test_data[i].decl, D3D_OK, __LINE__, i);
720     }
721
722     /* Usage indices for position and normal are apparently ignored. */
723     {
724         const D3DVERTEXELEMENT9 decl[] =
725         {
726             {0, 0, D3DDECLTYPE_FLOAT3, 0, D3DDECLUSAGE_POSITION, 1},
727             D3DDECL_END(),
728         };
729         test_decl_to_fvf(decl, D3DFVF_XYZ, D3D_OK, __LINE__, 0);
730     }
731     {
732         const D3DVERTEXELEMENT9 decl[] =
733         {
734             {0, 0, D3DDECLTYPE_FLOAT3, 0, D3DDECLUSAGE_NORMAL, 1},
735             D3DDECL_END(),
736         };
737         test_decl_to_fvf(decl, D3DFVF_NORMAL, D3D_OK, __LINE__, 0);
738     }
739     /* D3DFVF_LASTBETA_UBYTE4 and D3DFVF_LASTBETA_D3DCOLOR are ignored if
740      * there are no blend matrices. */
741     {
742         const D3DVERTEXELEMENT9 decl[] =
743         {
744             {0, 0, D3DDECLTYPE_FLOAT3, 0, D3DDECLUSAGE_POSITION, 0},
745             D3DDECL_END(),
746         };
747         test_fvf_to_decl(D3DFVF_XYZ | D3DFVF_LASTBETA_UBYTE4, decl, D3D_OK, __LINE__, 0);
748     }
749     {
750         const D3DVERTEXELEMENT9 decl[] =
751         {
752             {0, 0, D3DDECLTYPE_FLOAT3, 0, D3DDECLUSAGE_POSITION, 0},
753             D3DDECL_END(),
754         };
755         test_fvf_to_decl(D3DFVF_XYZ | D3DFVF_LASTBETA_D3DCOLOR, decl, D3D_OK, __LINE__, 0);
756     }
757     /* D3DFVF_LASTBETA_UBYTE4 takes precedence over D3DFVF_LASTBETA_D3DCOLOR. */
758     {
759         const D3DVERTEXELEMENT9 decl[] =
760         {
761             {0, 0, D3DDECLTYPE_FLOAT3, 0, D3DDECLUSAGE_POSITION, 0},
762             {0, 12, D3DDECLTYPE_FLOAT4, 0, D3DDECLUSAGE_BLENDWEIGHT, 0},
763             {0, 28, D3DDECLTYPE_UBYTE4, 0, D3DDECLUSAGE_BLENDINDICES, 0},
764             D3DDECL_END(),
765         };
766         test_fvf_to_decl(D3DFVF_XYZB5 | D3DFVF_LASTBETA_D3DCOLOR | D3DFVF_LASTBETA_UBYTE4,
767                 decl, D3D_OK, __LINE__, 0);
768     }
769     /* These are supposed to fail, both ways. */
770     {
771         const D3DVERTEXELEMENT9 decl[] =
772         {
773             {0, 0, D3DDECLTYPE_FLOAT4, 0, D3DDECLUSAGE_POSITION, 0},
774             D3DDECL_END(),
775         };
776         test_decl_to_fvf(decl, D3DFVF_XYZW, D3DERR_INVALIDCALL, __LINE__, 0);
777         test_fvf_to_decl(D3DFVF_XYZW, decl, D3DERR_INVALIDCALL, __LINE__, 0);
778     }
779     {
780         const D3DVERTEXELEMENT9 decl[] =
781         {
782             {0, 0, D3DDECLTYPE_FLOAT4, 0, D3DDECLUSAGE_POSITION, 0},
783             {0, 16, D3DDECLTYPE_FLOAT3, 0, D3DDECLUSAGE_NORMAL, 0},
784             D3DDECL_END(),
785         };
786         test_decl_to_fvf(decl, D3DFVF_XYZW | D3DFVF_NORMAL, D3DERR_INVALIDCALL, __LINE__, 0);
787         test_fvf_to_decl(D3DFVF_XYZW | D3DFVF_NORMAL, decl, D3DERR_INVALIDCALL, __LINE__, 0);
788     }
789     {
790         const D3DVERTEXELEMENT9 decl[] =
791         {
792             {0, 0, D3DDECLTYPE_FLOAT3, 0, D3DDECLUSAGE_POSITION, 0},
793             {0, 12, D3DDECLTYPE_FLOAT4, 0, D3DDECLUSAGE_BLENDWEIGHT, 0},
794             {0, 28, D3DDECLTYPE_FLOAT1, 0, D3DDECLUSAGE_BLENDINDICES, 0},
795             D3DDECL_END(),
796         };
797         test_decl_to_fvf(decl, D3DFVF_XYZB5, D3DERR_INVALIDCALL, __LINE__, 0);
798         test_fvf_to_decl(D3DFVF_XYZB5, decl, D3DERR_INVALIDCALL, __LINE__, 0);
799     }
800     /* Test a declaration that can't be converted to an FVF. */
801     {
802         const D3DVERTEXELEMENT9 decl[] =
803         {
804             {0, 0, D3DDECLTYPE_FLOAT3, 0, D3DDECLUSAGE_POSITION, 0},
805             {0, 12, D3DDECLTYPE_FLOAT3, 0, D3DDECLUSAGE_NORMAL, 0},
806             {0, 24, D3DDECLTYPE_FLOAT1, 0, D3DDECLUSAGE_PSIZE, 0},
807             {0, 28, D3DDECLTYPE_D3DCOLOR, 0, D3DDECLUSAGE_COLOR, 1},
808             {0, 32, D3DDECLTYPE_FLOAT1, 0, D3DDECLUSAGE_TEXCOORD, 0},
809             /* 8 bytes padding */
810             {0, 44, D3DDECLTYPE_FLOAT4, 0, D3DDECLUSAGE_TEXCOORD, 1},
811             D3DDECL_END(),
812         };
813         test_decl_to_fvf(decl, 0, D3DERR_INVALIDCALL, __LINE__, 0);
814     }
815     /* Elements must be ordered by offset. */
816     {
817         const D3DVERTEXELEMENT9 decl[] =
818         {
819             {0, 12, D3DDECLTYPE_D3DCOLOR, 0, D3DDECLUSAGE_COLOR, 0},
820             {0, 0, D3DDECLTYPE_FLOAT3, 0, D3DDECLUSAGE_POSITION, 0},
821             D3DDECL_END(),
822         };
823         test_decl_to_fvf(decl, 0, D3DERR_INVALIDCALL, __LINE__, 0);
824     }
825     /* Basic tests for element order. */
826     {
827         const D3DVERTEXELEMENT9 decl[] =
828         {
829             {0, 0, D3DDECLTYPE_FLOAT3, 0, D3DDECLUSAGE_POSITION, 0},
830             {0, 12, D3DDECLTYPE_D3DCOLOR, 0, D3DDECLUSAGE_COLOR, 0},
831             {0, 16, D3DDECLTYPE_FLOAT3, 0, D3DDECLUSAGE_NORMAL, 0},
832             D3DDECL_END(),
833         };
834         test_decl_to_fvf(decl, 0, D3DERR_INVALIDCALL, __LINE__, 0);
835     }
836     {
837         const D3DVERTEXELEMENT9 decl[] =
838         {
839             {0, 0, D3DDECLTYPE_D3DCOLOR, 0, D3DDECLUSAGE_COLOR, 0},
840             {0, 4, D3DDECLTYPE_FLOAT3, 0, D3DDECLUSAGE_POSITION, 0},
841             D3DDECL_END(),
842         };
843         test_decl_to_fvf(decl, 0, D3DERR_INVALIDCALL, __LINE__, 0);
844     }
845     {
846         const D3DVERTEXELEMENT9 decl[] =
847         {
848             {0, 0, D3DDECLTYPE_FLOAT3, 0, D3DDECLUSAGE_NORMAL, 0},
849             {0, 12, D3DDECLTYPE_FLOAT3, 0, D3DDECLUSAGE_POSITION, 0},
850             D3DDECL_END(),
851         };
852         test_decl_to_fvf(decl, 0, D3DERR_INVALIDCALL, __LINE__, 0);
853     }
854     /* Textures must be ordered by texcoords. */
855     {
856         const D3DVERTEXELEMENT9 decl[] =
857         {
858             {0, 0, D3DDECLTYPE_FLOAT1, 0, D3DDECLUSAGE_TEXCOORD, 0},
859             {0, 4, D3DDECLTYPE_FLOAT3, 0, D3DDECLUSAGE_TEXCOORD, 2},
860             {0, 16, D3DDECLTYPE_FLOAT2, 0, D3DDECLUSAGE_TEXCOORD, 1},
861             {0, 24, D3DDECLTYPE_FLOAT4, 0, D3DDECLUSAGE_TEXCOORD, 3},
862             D3DDECL_END(),
863         };
864         test_decl_to_fvf(decl, 0, D3DERR_INVALIDCALL, __LINE__, 0);
865     }
866     /* Duplicate elements are not allowed. */
867     {
868         const D3DVERTEXELEMENT9 decl[] =
869         {
870             {0, 0, D3DDECLTYPE_FLOAT3, 0, D3DDECLUSAGE_POSITION, 0},
871             {0, 12, D3DDECLTYPE_D3DCOLOR, 0, D3DDECLUSAGE_COLOR, 0},
872             {0, 16, D3DDECLTYPE_D3DCOLOR, 0, D3DDECLUSAGE_COLOR, 0},
873             D3DDECL_END(),
874         };
875         test_decl_to_fvf(decl, 0, D3DERR_INVALIDCALL, __LINE__, 0);
876     }
877     /* Invalid FVFs cannot be converted to a declarator. */
878     test_fvf_to_decl(0xdeadbeef, NULL, D3DERR_INVALIDCALL, __LINE__, 0);
879 }
880
881 static void D3DXGetFVFVertexSizeTest(void)
882 {
883     UINT got;
884
885     compare_vertex_sizes (D3DFVF_XYZ, 12);
886
887     compare_vertex_sizes (D3DFVF_XYZB3, 24);
888
889     compare_vertex_sizes (D3DFVF_XYZB5, 32);
890
891     compare_vertex_sizes (D3DFVF_XYZ | D3DFVF_NORMAL, 24);
892
893     compare_vertex_sizes (D3DFVF_XYZ | D3DFVF_DIFFUSE, 16);
894
895     compare_vertex_sizes (
896         D3DFVF_XYZ |
897         D3DFVF_TEX1 |
898         D3DFVF_TEXCOORDSIZE1(0), 16);
899     compare_vertex_sizes (
900         D3DFVF_XYZ |
901         D3DFVF_TEX2 |
902         D3DFVF_TEXCOORDSIZE1(0) |
903         D3DFVF_TEXCOORDSIZE1(1), 20);
904
905     compare_vertex_sizes (
906         D3DFVF_XYZ |
907         D3DFVF_TEX1 |
908         D3DFVF_TEXCOORDSIZE2(0), 20);
909
910     compare_vertex_sizes (
911         D3DFVF_XYZ |
912         D3DFVF_TEX2 |
913         D3DFVF_TEXCOORDSIZE2(0) |
914         D3DFVF_TEXCOORDSIZE2(1), 28);
915
916     compare_vertex_sizes (
917         D3DFVF_XYZ |
918         D3DFVF_TEX6 |
919         D3DFVF_TEXCOORDSIZE2(0) |
920         D3DFVF_TEXCOORDSIZE2(1) |
921         D3DFVF_TEXCOORDSIZE2(2) |
922         D3DFVF_TEXCOORDSIZE2(3) |
923         D3DFVF_TEXCOORDSIZE2(4) |
924         D3DFVF_TEXCOORDSIZE2(5), 60);
925
926     compare_vertex_sizes (
927         D3DFVF_XYZ |
928         D3DFVF_TEX8 |
929         D3DFVF_TEXCOORDSIZE2(0) |
930         D3DFVF_TEXCOORDSIZE2(1) |
931         D3DFVF_TEXCOORDSIZE2(2) |
932         D3DFVF_TEXCOORDSIZE2(3) |
933         D3DFVF_TEXCOORDSIZE2(4) |
934         D3DFVF_TEXCOORDSIZE2(5) |
935         D3DFVF_TEXCOORDSIZE2(6) |
936         D3DFVF_TEXCOORDSIZE2(7), 76);
937
938     compare_vertex_sizes (
939         D3DFVF_XYZ |
940         D3DFVF_TEX1 |
941         D3DFVF_TEXCOORDSIZE3(0), 24);
942
943     compare_vertex_sizes (
944         D3DFVF_XYZ |
945         D3DFVF_TEX4 |
946         D3DFVF_TEXCOORDSIZE3(0) |
947         D3DFVF_TEXCOORDSIZE3(1) |
948         D3DFVF_TEXCOORDSIZE3(2) |
949         D3DFVF_TEXCOORDSIZE3(3), 60);
950
951     compare_vertex_sizes (
952         D3DFVF_XYZ |
953         D3DFVF_TEX1 |
954         D3DFVF_TEXCOORDSIZE4(0), 28);
955
956     compare_vertex_sizes (
957         D3DFVF_XYZ |
958         D3DFVF_TEX2 |
959         D3DFVF_TEXCOORDSIZE4(0) |
960         D3DFVF_TEXCOORDSIZE4(1), 44);
961
962     compare_vertex_sizes (
963         D3DFVF_XYZ |
964         D3DFVF_TEX3 |
965         D3DFVF_TEXCOORDSIZE4(0) |
966         D3DFVF_TEXCOORDSIZE4(1) |
967         D3DFVF_TEXCOORDSIZE4(2), 60);
968
969     compare_vertex_sizes (
970         D3DFVF_XYZB5 |
971         D3DFVF_NORMAL |
972         D3DFVF_DIFFUSE |
973         D3DFVF_SPECULAR |
974         D3DFVF_TEX8 |
975         D3DFVF_TEXCOORDSIZE4(0) |
976         D3DFVF_TEXCOORDSIZE4(1) |
977         D3DFVF_TEXCOORDSIZE4(2) |
978         D3DFVF_TEXCOORDSIZE4(3) |
979         D3DFVF_TEXCOORDSIZE4(4) |
980         D3DFVF_TEXCOORDSIZE4(5) |
981         D3DFVF_TEXCOORDSIZE4(6) |
982         D3DFVF_TEXCOORDSIZE4(7), 180);
983 }
984
985 static void D3DXIntersectTriTest(void)
986 {
987     BOOL exp_res, got_res;
988     D3DXVECTOR3 position, ray, vertex[3];
989     FLOAT exp_dist, got_dist, exp_u, got_u, exp_v, got_v;
990
991     vertex[0].x = 1.0f; vertex[0].y = 0.0f; vertex[0].z = 0.0f;
992     vertex[1].x = 2.0f; vertex[1].y = 0.0f; vertex[1].z = 0.0f;
993     vertex[2].x = 1.0f; vertex[2].y = 1.0f; vertex[2].z = 0.0f;
994
995     position.x = -14.5f; position.y = -23.75f; position.z = -32.0f;
996
997     ray.x = 2.0f; ray.y = 3.0f; ray.z = 4.0f;
998
999     exp_res = TRUE; exp_u = 0.5f; exp_v = 0.25f; exp_dist = 8.0f;
1000
1001     got_res = D3DXIntersectTri(&vertex[0],&vertex[1],&vertex[2],&position,&ray,&got_u,&got_v,&got_dist);
1002     ok( got_res == exp_res, "Expected result = %d, got %d\n",exp_res,got_res);
1003     ok( compare(exp_u,got_u), "Expected u = %f, got %f\n",exp_u,got_u);
1004     ok( compare(exp_v,got_v), "Expected v = %f, got %f\n",exp_v,got_v);
1005     ok( compare(exp_dist,got_dist), "Expected distance = %f, got %f\n",exp_dist,got_dist);
1006
1007 /*Only positive ray is taken in account*/
1008
1009     vertex[0].x = 1.0f; vertex[0].y = 0.0f; vertex[0].z = 0.0f;
1010     vertex[1].x = 2.0f; vertex[1].y = 0.0f; vertex[1].z = 0.0f;
1011     vertex[2].x = 1.0f; vertex[2].y = 1.0f; vertex[2].z = 0.0f;
1012
1013     position.x = 17.5f; position.y = 24.25f; position.z = 32.0f;
1014
1015     ray.x = 2.0f; ray.y = 3.0f; ray.z = 4.0f;
1016
1017     exp_res = FALSE;
1018
1019     got_res = D3DXIntersectTri(&vertex[0],&vertex[1],&vertex[2],&position,&ray,&got_u,&got_v,&got_dist);
1020     ok( got_res == exp_res, "Expected result = %d, got %d\n",exp_res,got_res);
1021
1022 /*Intersection between ray and triangle in a same plane is considered as empty*/
1023
1024     vertex[0].x = 4.0f; vertex[0].y = 0.0f; vertex[0].z = 0.0f;
1025     vertex[1].x = 6.0f; vertex[1].y = 0.0f; vertex[1].z = 0.0f;
1026     vertex[2].x = 4.0f; vertex[2].y = 2.0f; vertex[2].z = 0.0f;
1027
1028     position.x = 1.0f; position.y = 1.0f; position.z = 0.0f;
1029
1030     ray.x = 1.0f; ray.y = 0.0f; ray.z = 0.0f;
1031
1032     exp_res = FALSE;
1033
1034     got_res = D3DXIntersectTri(&vertex[0],&vertex[1],&vertex[2],&position,&ray,&got_u,&got_v,&got_dist);
1035     ok( got_res == exp_res, "Expected result = %d, got %d\n",exp_res,got_res);
1036 }
1037
1038 static void D3DXCreateMeshTest(void)
1039 {
1040     HRESULT hr;
1041     HWND wnd;
1042     IDirect3D9 *d3d;
1043     IDirect3DDevice9 *device, *test_device;
1044     D3DPRESENT_PARAMETERS d3dpp;
1045     ID3DXMesh *d3dxmesh;
1046     int i, size;
1047     D3DVERTEXELEMENT9 test_decl[MAX_FVF_DECL_SIZE];
1048     DWORD options;
1049     struct mesh mesh;
1050
1051     static const D3DVERTEXELEMENT9 decl1[3] = {
1052         {0, 0, D3DDECLTYPE_FLOAT3, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_POSITION, 0},
1053         {0, 12, D3DDECLTYPE_FLOAT3, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_NORMAL, 0},
1054         D3DDECL_END(), };
1055
1056     static const D3DVERTEXELEMENT9 decl2[] = {
1057         {0, 0, D3DDECLTYPE_FLOAT3, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_POSITION, 0},
1058         {0, 12, D3DDECLTYPE_FLOAT3, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_NORMAL, 0},
1059         {0, 24, D3DDECLTYPE_FLOAT1, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_PSIZE, 0},
1060         {0, 28, D3DDECLTYPE_D3DCOLOR, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_COLOR, 1},
1061         {0, 32, D3DDECLTYPE_FLOAT1, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_TEXCOORD, 0},
1062         /* 8 bytes padding */
1063         {0, 44, D3DDECLTYPE_FLOAT4, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_TEXCOORD, 1},
1064         D3DDECL_END(),
1065     };
1066
1067     static const D3DVERTEXELEMENT9 decl3[] = {
1068         {0, 0, D3DDECLTYPE_FLOAT3, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_POSITION, 0},
1069         {1, 0, D3DDECLTYPE_FLOAT3, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_NORMAL, 0},
1070         D3DDECL_END(),
1071     };
1072
1073     hr = D3DXCreateMesh(0, 0, 0, NULL, NULL, NULL);
1074     ok(hr == D3DERR_INVALIDCALL, "Got result %x, expected %x (D3DERR_INVALIDCALL)\n", hr, D3DERR_INVALIDCALL);
1075
1076     hr = D3DXCreateMesh(1, 3, D3DXMESH_MANAGED, decl1, NULL, &d3dxmesh);
1077     ok(hr == D3DERR_INVALIDCALL, "Got result %x, expected %x (D3DERR_INVALIDCALL)\n", hr, D3DERR_INVALIDCALL);
1078
1079     wnd = CreateWindow("static", "d3dx9_test", 0, 0, 0, 0, 0, NULL, NULL, NULL, NULL);
1080     if (!wnd)
1081     {
1082         skip("Couldn't create application window\n");
1083         return;
1084     }
1085     d3d = Direct3DCreate9(D3D_SDK_VERSION);
1086     if (!d3d)
1087     {
1088         skip("Couldn't create IDirect3D9 object\n");
1089         DestroyWindow(wnd);
1090         return;
1091     }
1092
1093     ZeroMemory(&d3dpp, sizeof(d3dpp));
1094     d3dpp.Windowed = TRUE;
1095     d3dpp.SwapEffect = D3DSWAPEFFECT_DISCARD;
1096     hr = IDirect3D9_CreateDevice(d3d, D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, wnd, D3DCREATE_MIXED_VERTEXPROCESSING, &d3dpp, &device);
1097     if (FAILED(hr))
1098     {
1099         skip("Failed to create IDirect3DDevice9 object %#x\n", hr);
1100         IDirect3D9_Release(d3d);
1101         DestroyWindow(wnd);
1102         return;
1103     }
1104
1105     hr = D3DXCreateMesh(0, 3, D3DXMESH_MANAGED, decl1, device, &d3dxmesh);
1106     ok(hr == D3DERR_INVALIDCALL, "Got result %x, expected %x (D3DERR_INVALIDCALL)\n", hr, D3DERR_INVALIDCALL);
1107
1108     hr = D3DXCreateMesh(1, 0, D3DXMESH_MANAGED, decl1, device, &d3dxmesh);
1109     ok(hr == D3DERR_INVALIDCALL, "Got result %x, expected %x (D3DERR_INVALIDCALL)\n", hr, D3DERR_INVALIDCALL);
1110
1111     hr = D3DXCreateMesh(1, 3, 0, decl1, device, &d3dxmesh);
1112     ok(hr == D3D_OK, "Got result %x, expected %x (D3D_OK)\n", hr, D3D_OK);
1113
1114     if (hr == D3D_OK)
1115     {
1116         d3dxmesh->lpVtbl->Release(d3dxmesh);
1117     }
1118
1119     hr = D3DXCreateMesh(1, 3, D3DXMESH_MANAGED, 0, device, &d3dxmesh);
1120     ok(hr == D3DERR_INVALIDCALL, "Got result %x, expected %x (D3DERR_INVALIDCALL)\n", hr, D3DERR_INVALIDCALL);
1121
1122     hr = D3DXCreateMesh(1, 3, D3DXMESH_MANAGED, decl1, device, NULL);
1123     ok(hr == D3DERR_INVALIDCALL, "Got result %x, expected %x (D3DERR_INVALIDCALL)\n", hr, D3DERR_INVALIDCALL);
1124
1125     hr = D3DXCreateMesh(1, 3, D3DXMESH_MANAGED, decl1, device, &d3dxmesh);
1126     ok(hr == D3D_OK, "Got result %x, expected 0 (D3D_OK)\n", hr);
1127
1128     if (hr == D3D_OK)
1129     {
1130         /* device */
1131         hr = d3dxmesh->lpVtbl->GetDevice(d3dxmesh, NULL);
1132         ok(hr == D3DERR_INVALIDCALL, "Got result %x, expected %x (D3DERR_INVALIDCALL)\n", hr, D3DERR_INVALIDCALL);
1133
1134         hr = d3dxmesh->lpVtbl->GetDevice(d3dxmesh, &test_device);
1135         ok(hr == D3D_OK, "Got result %x, expected %x (D3D_OK)\n", hr, D3D_OK);
1136         ok(test_device == device, "Got result %p, expected %p\n", test_device, device);
1137
1138         if (hr == D3D_OK)
1139         {
1140             IDirect3DDevice9_Release(device);
1141         }
1142
1143         /* declaration */
1144         hr = d3dxmesh->lpVtbl->GetDeclaration(d3dxmesh, NULL);
1145         ok(hr == D3DERR_INVALIDCALL, "Got result %x, expected %x (D3DERR_INVALIDCALL)\n", hr, D3DERR_INVALIDCALL);
1146
1147         hr = d3dxmesh->lpVtbl->GetDeclaration(d3dxmesh, test_decl);
1148         ok(hr == D3D_OK, "Got result %x, expected 0 (D3D_OK)\n", hr);
1149
1150         if (hr == D3D_OK)
1151         {
1152             size = sizeof(decl1) / sizeof(decl1[0]);
1153             for (i = 0; i < size - 1; i++)
1154             {
1155                 ok(test_decl[i].Stream == decl1[i].Stream, "Returned stream %d, expected %d\n", test_decl[i].Stream, decl1[i].Stream);
1156                 ok(test_decl[i].Type == decl1[i].Type, "Returned type %d, expected %d\n", test_decl[i].Type, decl1[i].Type);
1157                 ok(test_decl[i].Method == decl1[i].Method, "Returned method %d, expected %d\n", test_decl[i].Method, decl1[i].Method);
1158                 ok(test_decl[i].Usage == decl1[i].Usage, "Returned usage %d, expected %d\n", test_decl[i].Usage, decl1[i].Usage);
1159                 ok(test_decl[i].UsageIndex == decl1[i].UsageIndex, "Returned usage index %d, expected %d\n", test_decl[i].UsageIndex, decl1[i].UsageIndex);
1160                 ok(test_decl[i].Offset == decl1[i].Offset, "Returned offset %d, expected %d\n", test_decl[i].Offset, decl1[i].Offset);
1161             }
1162             ok(decl1[size-1].Stream == 0xFF, "Returned too long vertex declaration\n"); /* end element */
1163         }
1164
1165         /* options */
1166         options = d3dxmesh->lpVtbl->GetOptions(d3dxmesh);
1167         ok(options == D3DXMESH_MANAGED, "Got result %x, expected %x (D3DXMESH_MANAGED)\n", options, D3DXMESH_MANAGED);
1168
1169         /* rest */
1170         if (!new_mesh(&mesh, 3, 1))
1171         {
1172             skip("Couldn't create mesh\n");
1173         }
1174         else
1175         {
1176             memset(mesh.vertices, 0, mesh.number_of_vertices * sizeof(*mesh.vertices));
1177             memset(mesh.faces, 0, mesh.number_of_faces * sizeof(*mesh.faces));
1178             mesh.fvf = D3DFVF_XYZ | D3DFVF_NORMAL;
1179
1180             compare_mesh("createmesh1", d3dxmesh, &mesh);
1181
1182             free_mesh(&mesh);
1183         }
1184
1185         d3dxmesh->lpVtbl->Release(d3dxmesh);
1186     }
1187
1188     /* Test a declaration that can't be converted to an FVF. */
1189     hr = D3DXCreateMesh(1, 3, D3DXMESH_MANAGED, decl2, device, &d3dxmesh);
1190     ok(hr == D3D_OK, "Got result %x, expected 0 (D3D_OK)\n", hr);
1191
1192     if (hr == D3D_OK)
1193     {
1194         /* device */
1195         hr = d3dxmesh->lpVtbl->GetDevice(d3dxmesh, NULL);
1196         ok(hr == D3DERR_INVALIDCALL, "Got result %x, expected %x (D3DERR_INVALIDCALL)\n", hr, D3DERR_INVALIDCALL);
1197
1198         hr = d3dxmesh->lpVtbl->GetDevice(d3dxmesh, &test_device);
1199         ok(hr == D3D_OK, "Got result %x, expected %x (D3D_OK)\n", hr, D3D_OK);
1200         ok(test_device == device, "Got result %p, expected %p\n", test_device, device);
1201
1202         if (hr == D3D_OK)
1203         {
1204             IDirect3DDevice9_Release(device);
1205         }
1206
1207         /* declaration */
1208         hr = d3dxmesh->lpVtbl->GetDeclaration(d3dxmesh, test_decl);
1209         ok(hr == D3D_OK, "Got result %x, expected 0 (D3D_OK)\n", hr);
1210
1211         if (hr == D3D_OK)
1212         {
1213             size = sizeof(decl2) / sizeof(decl2[0]);
1214             for (i = 0; i < size - 1; i++)
1215             {
1216                 ok(test_decl[i].Stream == decl2[i].Stream, "Returned stream %d, expected %d\n", test_decl[i].Stream, decl2[i].Stream);
1217                 ok(test_decl[i].Type == decl2[i].Type, "Returned type %d, expected %d\n", test_decl[i].Type, decl2[i].Type);
1218                 ok(test_decl[i].Method == decl2[i].Method, "Returned method %d, expected %d\n", test_decl[i].Method, decl2[i].Method);
1219                 ok(test_decl[i].Usage == decl2[i].Usage, "Returned usage %d, expected %d\n", test_decl[i].Usage, decl2[i].Usage);
1220                 ok(test_decl[i].UsageIndex == decl2[i].UsageIndex, "Returned usage index %d, expected %d\n", test_decl[i].UsageIndex, decl2[i].UsageIndex);
1221                 ok(test_decl[i].Offset == decl2[i].Offset, "Returned offset %d, expected %d\n", test_decl[i].Offset, decl2[i].Offset);
1222             }
1223             ok(decl2[size-1].Stream == 0xFF, "Returned too long vertex declaration\n"); /* end element */
1224         }
1225
1226         /* options */
1227         options = d3dxmesh->lpVtbl->GetOptions(d3dxmesh);
1228         ok(options == D3DXMESH_MANAGED, "Got result %x, expected %x (D3DXMESH_MANAGED)\n", options, D3DXMESH_MANAGED);
1229
1230         /* rest */
1231         if (!new_mesh(&mesh, 3, 1))
1232         {
1233             skip("Couldn't create mesh\n");
1234         }
1235         else
1236         {
1237             memset(mesh.vertices, 0, mesh.number_of_vertices * sizeof(*mesh.vertices));
1238             memset(mesh.faces, 0, mesh.number_of_faces * sizeof(*mesh.faces));
1239             mesh.fvf = 0;
1240             mesh.vertex_size = 60;
1241
1242             compare_mesh("createmesh2", d3dxmesh, &mesh);
1243
1244             free_mesh(&mesh);
1245         }
1246
1247         mesh.vertex_size = d3dxmesh->lpVtbl->GetNumBytesPerVertex(d3dxmesh);
1248         ok(mesh.vertex_size == 60, "Got vertex size %u, expected %u\n", mesh.vertex_size, 60);
1249
1250         d3dxmesh->lpVtbl->Release(d3dxmesh);
1251     }
1252
1253     /* Test a declaration with multiple streams. */
1254     hr = D3DXCreateMesh(1, 3, D3DXMESH_MANAGED, decl3, device, &d3dxmesh);
1255     ok(hr == D3DERR_INVALIDCALL, "Got result %x, expected %x (D3DERR_INVALIDCALL)\n", hr, D3DERR_INVALIDCALL);
1256
1257     IDirect3DDevice9_Release(device);
1258     IDirect3D9_Release(d3d);
1259     DestroyWindow(wnd);
1260 }
1261
1262 static void D3DXCreateMeshFVFTest(void)
1263 {
1264     HRESULT hr;
1265     HWND wnd;
1266     IDirect3D9 *d3d;
1267     IDirect3DDevice9 *device, *test_device;
1268     D3DPRESENT_PARAMETERS d3dpp;
1269     ID3DXMesh *d3dxmesh;
1270     int i, size;
1271     D3DVERTEXELEMENT9 test_decl[MAX_FVF_DECL_SIZE];
1272     DWORD options;
1273     struct mesh mesh;
1274
1275     static const D3DVERTEXELEMENT9 decl[3] = {
1276         {0, 0, D3DDECLTYPE_FLOAT3, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_POSITION, 0},
1277         {0, 12, D3DDECLTYPE_FLOAT3, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_NORMAL, 0},
1278         D3DDECL_END(), };
1279
1280     hr = D3DXCreateMeshFVF(0, 0, 0, 0, NULL, NULL);
1281     ok(hr == D3DERR_INVALIDCALL, "Got result %x, expected %x (D3DERR_INVALIDCALL)\n", hr, D3DERR_INVALIDCALL);
1282
1283     hr = D3DXCreateMeshFVF(1, 3, D3DXMESH_MANAGED, D3DFVF_XYZ | D3DFVF_NORMAL, NULL, &d3dxmesh);
1284     ok(hr == D3DERR_INVALIDCALL, "Got result %x, expected %x (D3DERR_INVALIDCALL)\n", hr, D3DERR_INVALIDCALL);
1285
1286     wnd = CreateWindow("static", "d3dx9_test", 0, 0, 0, 0, 0, NULL, NULL, NULL, NULL);
1287     if (!wnd)
1288     {
1289         skip("Couldn't create application window\n");
1290         return;
1291     }
1292     d3d = Direct3DCreate9(D3D_SDK_VERSION);
1293     if (!d3d)
1294     {
1295         skip("Couldn't create IDirect3D9 object\n");
1296         DestroyWindow(wnd);
1297         return;
1298     }
1299
1300     ZeroMemory(&d3dpp, sizeof(d3dpp));
1301     d3dpp.Windowed = TRUE;
1302     d3dpp.SwapEffect = D3DSWAPEFFECT_DISCARD;
1303     hr = IDirect3D9_CreateDevice(d3d, D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, wnd, D3DCREATE_MIXED_VERTEXPROCESSING, &d3dpp, &device);
1304     if (FAILED(hr))
1305     {
1306         skip("Failed to create IDirect3DDevice9 object %#x\n", hr);
1307         IDirect3D9_Release(d3d);
1308         DestroyWindow(wnd);
1309         return;
1310     }
1311
1312     hr = D3DXCreateMeshFVF(0, 3, D3DXMESH_MANAGED, D3DFVF_XYZ | D3DFVF_NORMAL, device, &d3dxmesh);
1313     ok(hr == D3DERR_INVALIDCALL, "Got result %x, expected %x (D3DERR_INVALIDCALL)\n", hr, D3DERR_INVALIDCALL);
1314
1315     hr = D3DXCreateMeshFVF(1, 0, D3DXMESH_MANAGED, D3DFVF_XYZ | D3DFVF_NORMAL, device, &d3dxmesh);
1316     ok(hr == D3DERR_INVALIDCALL, "Got result %x, expected %x (D3DERR_INVALIDCALL)\n", hr, D3DERR_INVALIDCALL);
1317
1318     hr = D3DXCreateMeshFVF(1, 3, 0, D3DFVF_XYZ | D3DFVF_NORMAL, device, &d3dxmesh);
1319     ok(hr == D3D_OK, "Got result %x, expected %x (D3D_OK)\n", hr, D3D_OK);
1320
1321     if (hr == D3D_OK)
1322     {
1323         d3dxmesh->lpVtbl->Release(d3dxmesh);
1324     }
1325
1326     hr = D3DXCreateMeshFVF(1, 3, D3DXMESH_MANAGED, 0xdeadbeef, device, &d3dxmesh);
1327     ok(hr == D3DERR_INVALIDCALL, "Got result %x, expected %x (D3DERR_INVALIDCALL)\n", hr, D3DERR_INVALIDCALL);
1328
1329     hr = D3DXCreateMeshFVF(1, 3, D3DXMESH_MANAGED, D3DFVF_XYZ | D3DFVF_NORMAL, device, NULL);
1330     ok(hr == D3DERR_INVALIDCALL, "Got result %x, expected %x (D3DERR_INVALIDCALL)\n", hr, D3DERR_INVALIDCALL);
1331
1332     hr = D3DXCreateMeshFVF(1, 3, D3DXMESH_MANAGED, D3DFVF_XYZ | D3DFVF_NORMAL, device, &d3dxmesh);
1333     ok(hr == D3D_OK, "Got result %x, expected 0 (D3D_OK)\n", hr);
1334
1335     if (hr == D3D_OK)
1336     {
1337         /* device */
1338         hr = d3dxmesh->lpVtbl->GetDevice(d3dxmesh, NULL);
1339         ok(hr == D3DERR_INVALIDCALL, "Got result %x, expected %x (D3DERR_INVALIDCALL)\n", hr, D3DERR_INVALIDCALL);
1340
1341         hr = d3dxmesh->lpVtbl->GetDevice(d3dxmesh, &test_device);
1342         ok(hr == D3D_OK, "Got result %x, expected %x (D3D_OK)\n", hr, D3D_OK);
1343         ok(test_device == device, "Got result %p, expected %p\n", test_device, device);
1344
1345         if (hr == D3D_OK)
1346         {
1347             IDirect3DDevice9_Release(device);
1348         }
1349
1350         /* declaration */
1351         hr = d3dxmesh->lpVtbl->GetDeclaration(d3dxmesh, NULL);
1352         ok(hr == D3DERR_INVALIDCALL, "Got result %x, expected %x (D3DERR_INVALIDCALL)\n", hr, D3DERR_INVALIDCALL);
1353
1354         hr = d3dxmesh->lpVtbl->GetDeclaration(d3dxmesh, test_decl);
1355         ok(hr == D3D_OK, "Got result %x, expected 0 (D3D_OK)\n", hr);
1356
1357         if (hr == D3D_OK)
1358         {
1359             size = sizeof(decl) / sizeof(decl[0]);
1360             for (i = 0; i < size - 1; i++)
1361             {
1362                 ok(test_decl[i].Stream == decl[i].Stream, "Returned stream %d, expected %d\n", test_decl[i].Stream, decl[i].Stream);
1363                 ok(test_decl[i].Type == decl[i].Type, "Returned type %d, expected %d\n", test_decl[i].Type, decl[i].Type);
1364                 ok(test_decl[i].Method == decl[i].Method, "Returned method %d, expected %d\n", test_decl[i].Method, decl[i].Method);
1365                 ok(test_decl[i].Usage == decl[i].Usage, "Returned usage %d, expected %d\n", test_decl[i].Usage, decl[i].Usage);
1366                 ok(test_decl[i].UsageIndex == decl[i].UsageIndex, "Returned usage index %d, expected %d\n",
1367                    test_decl[i].UsageIndex, decl[i].UsageIndex);
1368                 ok(test_decl[i].Offset == decl[i].Offset, "Returned offset %d, expected %d\n", test_decl[i].Offset, decl[i].Offset);
1369             }
1370             ok(decl[size-1].Stream == 0xFF, "Returned too long vertex declaration\n"); /* end element */
1371         }
1372
1373         /* options */
1374         options = d3dxmesh->lpVtbl->GetOptions(d3dxmesh);
1375         ok(options == D3DXMESH_MANAGED, "Got result %x, expected %x (D3DXMESH_MANAGED)\n", options, D3DXMESH_MANAGED);
1376
1377         /* rest */
1378         if (!new_mesh(&mesh, 3, 1))
1379         {
1380             skip("Couldn't create mesh\n");
1381         }
1382         else
1383         {
1384             memset(mesh.vertices, 0, mesh.number_of_vertices * sizeof(*mesh.vertices));
1385             memset(mesh.faces, 0, mesh.number_of_faces * sizeof(*mesh.faces));
1386             mesh.fvf = D3DFVF_XYZ | D3DFVF_NORMAL;
1387
1388             compare_mesh("createmeshfvf", d3dxmesh, &mesh);
1389
1390             free_mesh(&mesh);
1391         }
1392
1393         d3dxmesh->lpVtbl->Release(d3dxmesh);
1394     }
1395
1396     IDirect3DDevice9_Release(device);
1397     IDirect3D9_Release(d3d);
1398     DestroyWindow(wnd);
1399 }
1400
1401 #define check_vertex_buffer(mesh, vertices, num_vertices, fvf) \
1402     check_vertex_buffer_(__LINE__, mesh, vertices, num_vertices, fvf)
1403 static void check_vertex_buffer_(int line, ID3DXMesh *mesh, const void *vertices, DWORD num_vertices, DWORD fvf)
1404 {
1405     DWORD mesh_num_vertices = mesh->lpVtbl->GetNumVertices(mesh);
1406     DWORD mesh_fvf = mesh->lpVtbl->GetFVF(mesh);
1407     const void *mesh_vertices;
1408     HRESULT hr;
1409
1410     ok_(__FILE__,line)(fvf == mesh_fvf, "expected FVF %x, got %x\n", fvf, mesh_fvf);
1411     ok_(__FILE__,line)(num_vertices == mesh_num_vertices,
1412        "Expected %u vertices, got %u\n", num_vertices, mesh_num_vertices);
1413
1414     hr = mesh->lpVtbl->LockVertexBuffer(mesh, D3DLOCK_READONLY, (void**)&mesh_vertices);
1415     ok_(__FILE__,line)(hr == D3D_OK, "LockVertexBuffer returned %x, expected %x (D3D_OK)\n", hr, D3D_OK);
1416     if (FAILED(hr))
1417         return;
1418
1419     if (mesh_fvf == fvf) {
1420         DWORD vertex_size = D3DXGetFVFVertexSize(fvf);
1421         int i;
1422         for (i = 0; i < min(num_vertices, mesh_num_vertices); i++)
1423         {
1424             const FLOAT *exp_float = vertices;
1425             const FLOAT *got_float = mesh_vertices;
1426             DWORD texcount;
1427             DWORD pos_dim = 0;
1428             int j;
1429             BOOL last_beta_dword = FALSE;
1430             char prefix[128];
1431
1432             switch (fvf & D3DFVF_POSITION_MASK) {
1433                 case D3DFVF_XYZ: pos_dim = 3; break;
1434                 case D3DFVF_XYZRHW: pos_dim = 4; break;
1435                 case D3DFVF_XYZB1:
1436                 case D3DFVF_XYZB2:
1437                 case D3DFVF_XYZB3:
1438                 case D3DFVF_XYZB4:
1439                 case D3DFVF_XYZB5:
1440                     pos_dim = (fvf & D3DFVF_POSITION_MASK) - D3DFVF_XYZB1 + 1;
1441                     if (fvf & (D3DFVF_LASTBETA_UBYTE4 | D3DFVF_LASTBETA_D3DCOLOR))
1442                     {
1443                         pos_dim--;
1444                         last_beta_dword = TRUE;
1445                     }
1446                     break;
1447                 case D3DFVF_XYZW: pos_dim = 4; break;
1448             }
1449             sprintf(prefix, "vertex[%u] position, ", i);
1450             check_floats_(line, prefix, got_float, exp_float, pos_dim);
1451             exp_float += pos_dim;
1452             got_float += pos_dim;
1453
1454             if (last_beta_dword) {
1455                 ok_(__FILE__,line)(*(DWORD*)exp_float == *(DWORD*)got_float,
1456                     "Vertex[%u]: Expected last beta %08x, got %08x\n", i, *(DWORD*)exp_float, *(DWORD*)got_float);
1457                 exp_float++;
1458                 got_float++;
1459             }
1460
1461             if (fvf & D3DFVF_NORMAL) {
1462                 sprintf(prefix, "vertex[%u] normal, ", i);
1463                 check_floats_(line, prefix, got_float, exp_float, 3);
1464                 exp_float += 3;
1465                 got_float += 3;
1466             }
1467             if (fvf & D3DFVF_PSIZE) {
1468                 ok_(__FILE__,line)(compare(*exp_float, *got_float),
1469                         "Vertex[%u]: Expected psize %g, got %g\n", i, *exp_float, *got_float);
1470                 exp_float++;
1471                 got_float++;
1472             }
1473             if (fvf & D3DFVF_DIFFUSE) {
1474                 ok_(__FILE__,line)(*(DWORD*)exp_float == *(DWORD*)got_float,
1475                     "Vertex[%u]: Expected diffuse %08x, got %08x\n", i, *(DWORD*)exp_float, *(DWORD*)got_float);
1476                 exp_float++;
1477                 got_float++;
1478             }
1479             if (fvf & D3DFVF_SPECULAR) {
1480                 ok_(__FILE__,line)(*(DWORD*)exp_float == *(DWORD*)got_float,
1481                     "Vertex[%u]: Expected specular %08x, got %08x\n", i, *(DWORD*)exp_float, *(DWORD*)got_float);
1482                 exp_float++;
1483                 got_float++;
1484             }
1485
1486             texcount = (fvf & D3DFVF_TEXCOUNT_MASK) >> D3DFVF_TEXCOUNT_SHIFT;
1487             for (j = 0; j < texcount; j++) {
1488                 DWORD dim = (((fvf >> (16 + 2 * j)) + 1) & 0x03) + 1;
1489                 sprintf(prefix, "vertex[%u] texture, ", i);
1490                 check_floats_(line, prefix, got_float, exp_float, dim);
1491                 exp_float += dim;
1492                 got_float += dim;
1493             }
1494
1495             vertices = (BYTE*)vertices + vertex_size;
1496             mesh_vertices = (BYTE*)mesh_vertices + vertex_size;
1497         }
1498     }
1499
1500     mesh->lpVtbl->UnlockVertexBuffer(mesh);
1501 }
1502
1503 #define check_index_buffer(mesh, indices, num_indices, index_size) \
1504     check_index_buffer_(__LINE__, mesh, indices, num_indices, index_size)
1505 static void check_index_buffer_(int line, ID3DXMesh *mesh, const void *indices, DWORD num_indices, DWORD index_size)
1506 {
1507     DWORD mesh_index_size = (mesh->lpVtbl->GetOptions(mesh) & D3DXMESH_32BIT) ? 4 : 2;
1508     DWORD mesh_num_indices = mesh->lpVtbl->GetNumFaces(mesh) * 3;
1509     const void *mesh_indices;
1510     HRESULT hr;
1511     DWORD i;
1512
1513     ok_(__FILE__,line)(index_size == mesh_index_size,
1514         "Expected index size %u, got %u\n", index_size, mesh_index_size);
1515     ok_(__FILE__,line)(num_indices == mesh_num_indices,
1516         "Expected %u indices, got %u\n", num_indices, mesh_num_indices);
1517
1518     hr = mesh->lpVtbl->LockIndexBuffer(mesh, D3DLOCK_READONLY, (void**)&mesh_indices);
1519     ok_(__FILE__,line)(hr == D3D_OK, "LockIndexBuffer returned %x, expected %x (D3D_OK)\n", hr, D3D_OK);
1520     if (FAILED(hr))
1521         return;
1522
1523     if (mesh_index_size == index_size) {
1524         for (i = 0; i < min(num_indices, mesh_num_indices); i++)
1525         {
1526             if (index_size == 4)
1527                 ok_(__FILE__,line)(*(DWORD*)indices == *(DWORD*)mesh_indices,
1528                     "Index[%u]: expected %u, got %u\n", i, *(DWORD*)indices, *(DWORD*)mesh_indices);
1529             else
1530                 ok_(__FILE__,line)(*(WORD*)indices == *(WORD*)mesh_indices,
1531                     "Index[%u]: expected %u, got %u\n", i, *(WORD*)indices, *(WORD*)mesh_indices);
1532             indices = (BYTE*)indices + index_size;
1533             mesh_indices = (BYTE*)mesh_indices + index_size;
1534         }
1535     }
1536     mesh->lpVtbl->UnlockIndexBuffer(mesh);
1537 }
1538
1539 #define check_matrix(got, expected) check_matrix_(__LINE__, got, expected)
1540 static void check_matrix_(int line, const D3DXMATRIX *got, const D3DXMATRIX *expected)
1541 {
1542     int i, j;
1543     for (i = 0; i < 4; i++) {
1544         for (j = 0; j < 4; j++) {
1545             ok_(__FILE__,line)(compare(U(*expected).m[i][j], U(*got).m[i][j]),
1546                     "matrix[%u][%u]: expected %g, got %g\n",
1547                     i, j, U(*expected).m[i][j], U(*got).m[i][j]);
1548         }
1549     }
1550 }
1551
1552 static void check_colorvalue_(int line, const char *prefix, const D3DCOLORVALUE got, const D3DCOLORVALUE expected)
1553 {
1554     ok_(__FILE__,line)(expected.r == got.r && expected.g == got.g && expected.b == got.b && expected.a == got.a,
1555             "%sExpected (%g, %g, %g, %g), got (%g, %g, %g, %g)\n", prefix,
1556             expected.r, expected.g, expected.b, expected.a, got.r, got.g, got.b, got.a);
1557 }
1558
1559 #define check_materials(got, got_count, expected, expected_count) \
1560     check_materials_(__LINE__, got, got_count, expected, expected_count)
1561 static void check_materials_(int line, const D3DXMATERIAL *got, DWORD got_count, const D3DXMATERIAL *expected, DWORD expected_count)
1562 {
1563     int i;
1564     ok_(__FILE__,line)(expected_count == got_count, "Expected %u materials, got %u\n", expected_count, got_count);
1565     if (!expected) {
1566         ok_(__FILE__,line)(got == NULL, "Expected NULL material ptr, got %p\n", got);
1567         return;
1568     }
1569     for (i = 0; i < min(expected_count, got_count); i++)
1570     {
1571         if (!expected[i].pTextureFilename)
1572             ok_(__FILE__,line)(got[i].pTextureFilename == NULL,
1573                     "Expected NULL pTextureFilename, got %p\n", got[i].pTextureFilename);
1574         else
1575             ok_(__FILE__,line)(!strcmp(expected[i].pTextureFilename, got[i].pTextureFilename),
1576                     "Expected '%s' for pTextureFilename, got '%s'\n", expected[i].pTextureFilename, got[i].pTextureFilename);
1577         check_colorvalue_(line, "Diffuse: ", got[i].MatD3D.Diffuse, expected[i].MatD3D.Diffuse);
1578         check_colorvalue_(line, "Ambient: ", got[i].MatD3D.Ambient, expected[i].MatD3D.Ambient);
1579         check_colorvalue_(line, "Specular: ", got[i].MatD3D.Specular, expected[i].MatD3D.Specular);
1580         check_colorvalue_(line, "Emissive: ", got[i].MatD3D.Emissive, expected[i].MatD3D.Emissive);
1581         ok_(__FILE__,line)(expected[i].MatD3D.Power == got[i].MatD3D.Power,
1582                 "Power: Expected %g, got %g\n", expected[i].MatD3D.Power, got[i].MatD3D.Power);
1583     }
1584 }
1585
1586 #define check_generated_adjacency(mesh, got, epsilon) check_generated_adjacency_(__LINE__, mesh, got, epsilon)
1587 static void check_generated_adjacency_(int line, ID3DXMesh *mesh, const DWORD *got, FLOAT epsilon)
1588 {
1589     DWORD *expected;
1590     DWORD num_faces = mesh->lpVtbl->GetNumFaces(mesh);
1591     HRESULT hr;
1592
1593     expected = HeapAlloc(GetProcessHeap(), 0, num_faces * sizeof(DWORD) * 3);
1594     if (!expected) {
1595         skip_(__FILE__, line)("Out of memory\n");
1596         return;
1597     }
1598     hr = mesh->lpVtbl->GenerateAdjacency(mesh, epsilon, expected);
1599     ok_(__FILE__, line)(hr == D3D_OK, "Expected D3D_OK, got %#x\n", hr);
1600     if (SUCCEEDED(hr))
1601     {
1602         int i;
1603         for (i = 0; i < num_faces; i++)
1604         {
1605             ok_(__FILE__, line)(expected[i * 3] == got[i * 3] &&
1606                     expected[i * 3 + 1] == got[i * 3 + 1] &&
1607                     expected[i * 3 + 2] == got[i * 3 + 2],
1608                     "Face %u adjacencies: Expected (%u, %u, %u), got (%u, %u, %u)\n", i,
1609                     expected[i * 3], expected[i * 3 + 1], expected[i * 3 + 2],
1610                     got[i * 3], got[i * 3 + 1], got[i * 3 + 2]);
1611         }
1612     }
1613     HeapFree(GetProcessHeap(), 0, expected);
1614 }
1615
1616 #define check_generated_effects(materials, num_materials, effects) \
1617     check_generated_effects_(__LINE__, materials, num_materials, effects)
1618 static void check_generated_effects_(int line, const D3DXMATERIAL *materials, DWORD num_materials, const D3DXEFFECTINSTANCE *effects)
1619 {
1620     int i;
1621     static const struct {
1622         const char *name;
1623         DWORD name_size;
1624         DWORD num_bytes;
1625         DWORD value_offset;
1626     } params[] = {
1627 #define EFFECT_TABLE_ENTRY(str, field) \
1628     {str, sizeof(str), sizeof(materials->MatD3D.field), offsetof(D3DXMATERIAL, MatD3D.field)}
1629         EFFECT_TABLE_ENTRY("Diffuse", Diffuse),
1630         EFFECT_TABLE_ENTRY("Power", Power),
1631         EFFECT_TABLE_ENTRY("Specular", Specular),
1632         EFFECT_TABLE_ENTRY("Emissive", Emissive),
1633         EFFECT_TABLE_ENTRY("Ambient", Ambient),
1634 #undef EFFECT_TABLE_ENTRY
1635     };
1636
1637     if (!num_materials) {
1638         ok_(__FILE__, line)(effects == NULL, "Expected NULL effects, got %p\n", effects);
1639         return;
1640     }
1641     for (i = 0; i < num_materials; i++)
1642     {
1643         int j;
1644         DWORD expected_num_defaults = ARRAY_SIZE(params) + (materials[i].pTextureFilename ? 1 : 0);
1645
1646         ok_(__FILE__,line)(expected_num_defaults == effects[i].NumDefaults,
1647                 "effect[%u] NumDefaults: Expected %u, got %u\n", i,
1648                 expected_num_defaults, effects[i].NumDefaults);
1649         for (j = 0; j < min(ARRAY_SIZE(params), effects[i].NumDefaults); j++)
1650         {
1651             int k;
1652             D3DXEFFECTDEFAULT *got_param = &effects[i].pDefaults[j];
1653             ok_(__FILE__,line)(!strcmp(params[j].name, got_param->pParamName),
1654                "effect[%u].pDefaults[%u].pParamName: Expected '%s', got '%s'\n", i, j,
1655                params[j].name, got_param->pParamName);
1656             ok_(__FILE__,line)(D3DXEDT_FLOATS == got_param->Type,
1657                "effect[%u].pDefaults[%u].Type: Expected %u, got %u\n", i, j,
1658                D3DXEDT_FLOATS, got_param->Type);
1659             ok_(__FILE__,line)(params[j].num_bytes == got_param->NumBytes,
1660                "effect[%u].pDefaults[%u].NumBytes: Expected %u, got %u\n", i, j,
1661                params[j].num_bytes, got_param->NumBytes);
1662             for (k = 0; k < min(params[j].num_bytes, got_param->NumBytes) / 4; k++)
1663             {
1664                 FLOAT expected = ((FLOAT*)((BYTE*)&materials[i] + params[j].value_offset))[k];
1665                 FLOAT got = ((FLOAT*)got_param->pValue)[k];
1666                 ok_(__FILE__,line)(compare(expected, got),
1667                    "effect[%u].pDefaults[%u] float value %u: Expected %g, got %g\n", i, j, k, expected, got);
1668             }
1669         }
1670         if (effects[i].NumDefaults > ARRAY_SIZE(params)) {
1671             D3DXEFFECTDEFAULT *got_param = &effects[i].pDefaults[j];
1672             static const char *expected_name = "Texture0@Name";
1673
1674             ok_(__FILE__,line)(!strcmp(expected_name, got_param->pParamName),
1675                "effect[%u].pDefaults[%u].pParamName: Expected '%s', got '%s'\n", i, j,
1676                expected_name, got_param->pParamName);
1677             ok_(__FILE__,line)(D3DXEDT_STRING == got_param->Type,
1678                "effect[%u].pDefaults[%u].Type: Expected %u, got %u\n", i, j,
1679                D3DXEDT_STRING, got_param->Type);
1680             if (materials[i].pTextureFilename) {
1681                 ok_(__FILE__,line)(strlen(materials[i].pTextureFilename) + 1 == got_param->NumBytes,
1682                    "effect[%u] texture filename length: Expected %u, got %u\n", i,
1683                    (DWORD)strlen(materials[i].pTextureFilename) + 1, got_param->NumBytes);
1684                 ok_(__FILE__,line)(!strcmp(materials[i].pTextureFilename, got_param->pValue),
1685                    "effect[%u] texture filename: Expected '%s', got '%s'\n", i,
1686                    materials[i].pTextureFilename, (char*)got_param->pValue);
1687             }
1688         }
1689     }
1690 }
1691
1692 static LPSTR strdupA(LPCSTR p)
1693 {
1694     LPSTR ret;
1695     if (!p) return NULL;
1696     ret = HeapAlloc(GetProcessHeap(), 0, strlen(p) + 1);
1697     if (ret) strcpy(ret, p);
1698     return ret;
1699 }
1700
1701 static CALLBACK HRESULT ID3DXAllocateHierarchyImpl_DestroyFrame(ID3DXAllocateHierarchy *iface, LPD3DXFRAME frame)
1702 {
1703     TRACECALLBACK("ID3DXAllocateHierarchyImpl_DestroyFrame(%p, %p)\n", iface, frame);
1704     if (frame) {
1705         HeapFree(GetProcessHeap(), 0, frame->Name);
1706         HeapFree(GetProcessHeap(), 0, frame);
1707     }
1708     return D3D_OK;
1709 }
1710
1711 static CALLBACK HRESULT ID3DXAllocateHierarchyImpl_CreateFrame(ID3DXAllocateHierarchy *iface, LPCSTR name, LPD3DXFRAME *new_frame)
1712 {
1713     LPD3DXFRAME frame;
1714
1715     TRACECALLBACK("ID3DXAllocateHierarchyImpl_CreateFrame(%p, '%s', %p)\n", iface, name, new_frame);
1716     frame = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(*frame));
1717     if (!frame)
1718         return E_OUTOFMEMORY;
1719     if (name) {
1720         frame->Name = strdupA(name);
1721         if (!frame->Name) {
1722             HeapFree(GetProcessHeap(), 0, frame);
1723             return E_OUTOFMEMORY;
1724         }
1725     }
1726     *new_frame = frame;
1727     return D3D_OK;
1728 }
1729
1730 static HRESULT destroy_mesh_container(LPD3DXMESHCONTAINER mesh_container)
1731 {
1732     int i;
1733
1734     if (!mesh_container)
1735         return D3D_OK;
1736     HeapFree(GetProcessHeap(), 0, mesh_container->Name);
1737     if (U(mesh_container->MeshData).pMesh)
1738         IUnknown_Release(U(mesh_container->MeshData).pMesh);
1739     if (mesh_container->pMaterials) {
1740         for (i = 0; i < mesh_container->NumMaterials; i++)
1741             HeapFree(GetProcessHeap(), 0, mesh_container->pMaterials[i].pTextureFilename);
1742         HeapFree(GetProcessHeap(), 0, mesh_container->pMaterials);
1743     }
1744     if (mesh_container->pEffects) {
1745         for (i = 0; i < mesh_container->NumMaterials; i++) {
1746             HeapFree(GetProcessHeap(), 0, mesh_container->pEffects[i].pEffectFilename);
1747             if (mesh_container->pEffects[i].pDefaults) {
1748                 int j;
1749                 for (j = 0; j < mesh_container->pEffects[i].NumDefaults; j++) {
1750                     HeapFree(GetProcessHeap(), 0, mesh_container->pEffects[i].pDefaults[j].pParamName);
1751                     HeapFree(GetProcessHeap(), 0, mesh_container->pEffects[i].pDefaults[j].pValue);
1752                 }
1753                 HeapFree(GetProcessHeap(), 0, mesh_container->pEffects[i].pDefaults);
1754             }
1755         }
1756         HeapFree(GetProcessHeap(), 0, mesh_container->pEffects);
1757     }
1758     HeapFree(GetProcessHeap(), 0, mesh_container->pAdjacency);
1759     if (mesh_container->pSkinInfo)
1760         IUnknown_Release(mesh_container->pSkinInfo);
1761     HeapFree(GetProcessHeap(), 0, mesh_container);
1762     return D3D_OK;
1763 }
1764
1765 static CALLBACK HRESULT ID3DXAllocateHierarchyImpl_DestroyMeshContainer(ID3DXAllocateHierarchy *iface, LPD3DXMESHCONTAINER mesh_container)
1766 {
1767     TRACECALLBACK("ID3DXAllocateHierarchyImpl_DestroyMeshContainer(%p, %p)\n", iface, mesh_container);
1768     return destroy_mesh_container(mesh_container);
1769 }
1770
1771 static CALLBACK HRESULT ID3DXAllocateHierarchyImpl_CreateMeshContainer(ID3DXAllocateHierarchy *iface,
1772         LPCSTR name, CONST D3DXMESHDATA *mesh_data, CONST D3DXMATERIAL *materials,
1773         CONST D3DXEFFECTINSTANCE *effects, DWORD num_materials, CONST DWORD *adjacency,
1774         LPD3DXSKININFO skin_info, LPD3DXMESHCONTAINER *new_mesh_container)
1775 {
1776     LPD3DXMESHCONTAINER mesh_container = NULL;
1777     int i;
1778
1779     TRACECALLBACK("ID3DXAllocateHierarchyImpl_CreateMeshContainer(%p, '%s', %u, %p, %p, %p, %d, %p, %p, %p)\n",
1780             iface, name, mesh_data->Type, U(*mesh_data).pMesh, materials, effects,
1781             num_materials, adjacency, skin_info, *new_mesh_container);
1782
1783     mesh_container = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(*mesh_container));
1784     if (!mesh_container)
1785         return E_OUTOFMEMORY;
1786
1787     if (name) {
1788         mesh_container->Name = strdupA(name);
1789         if (!mesh_container->Name)
1790             goto error;
1791     }
1792
1793     mesh_container->NumMaterials = num_materials;
1794     if (num_materials) {
1795         mesh_container->pMaterials = HeapAlloc(GetProcessHeap(), 0, num_materials * sizeof(*materials));
1796         if (!mesh_container->pMaterials)
1797             goto error;
1798
1799         memcpy(mesh_container->pMaterials, materials, num_materials * sizeof(*materials));
1800         for (i = 0; i < num_materials; i++)
1801             mesh_container->pMaterials[i].pTextureFilename = NULL;
1802         for (i = 0; i < num_materials; i++) {
1803             if (materials[i].pTextureFilename) {
1804                 mesh_container->pMaterials[i].pTextureFilename = strdupA(materials[i].pTextureFilename);
1805                 if (!mesh_container->pMaterials[i].pTextureFilename)
1806                     goto error;
1807             }
1808         }
1809
1810         mesh_container->pEffects = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, num_materials * sizeof(*effects));
1811         if (!mesh_container->pEffects)
1812             goto error;
1813         for (i = 0; i < num_materials; i++) {
1814             int j;
1815             const D3DXEFFECTINSTANCE *effect_src = &effects[i];
1816             D3DXEFFECTINSTANCE *effect_dest = &mesh_container->pEffects[i];
1817
1818             if (effect_src->pEffectFilename) {
1819                 effect_dest->pEffectFilename = strdupA(effect_src->pEffectFilename);
1820                 if (!effect_dest->pEffectFilename)
1821                     goto error;
1822             }
1823             effect_dest->pDefaults = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY,
1824                     effect_src->NumDefaults * sizeof(*effect_src->pDefaults));
1825             if (!effect_dest->pDefaults)
1826                 goto error;
1827             effect_dest->NumDefaults = effect_src->NumDefaults;
1828             for (j = 0; j < effect_src->NumDefaults; j++) {
1829                 const D3DXEFFECTDEFAULT *default_src = &effect_src->pDefaults[j];
1830                 D3DXEFFECTDEFAULT *default_dest = &effect_dest->pDefaults[j];
1831
1832                 if (default_src->pParamName) {
1833                     default_dest->pParamName = strdupA(default_src->pParamName);
1834                     if (!default_dest->pParamName)
1835                         goto error;
1836                 }
1837                 default_dest->NumBytes = default_src->NumBytes;
1838                 default_dest->Type = default_src->Type;
1839                 default_dest->pValue = HeapAlloc(GetProcessHeap(), 0, default_src->NumBytes);
1840                 memcpy(default_dest->pValue, default_src->pValue, default_src->NumBytes);
1841             }
1842         }
1843     }
1844
1845     ok(adjacency != NULL, "Expected non-NULL adjacency, got NULL\n");
1846     if (adjacency) {
1847         if (mesh_data->Type == D3DXMESHTYPE_MESH || mesh_data->Type == D3DXMESHTYPE_PMESH) {
1848             ID3DXBaseMesh *basemesh = (ID3DXBaseMesh*)U(*mesh_data).pMesh;
1849             DWORD num_faces = basemesh->lpVtbl->GetNumFaces(basemesh);
1850             size_t size = num_faces * sizeof(DWORD) * 3;
1851             mesh_container->pAdjacency = HeapAlloc(GetProcessHeap(), 0, size);
1852             if (!mesh_container->pAdjacency)
1853                 goto error;
1854             memcpy(mesh_container->pAdjacency, adjacency, size);
1855         } else {
1856             ok(mesh_data->Type == D3DXMESHTYPE_PATCHMESH, "Unknown mesh type %u\n", mesh_data->Type);
1857             if (mesh_data->Type == D3DXMESHTYPE_PATCHMESH)
1858                 trace("FIXME: copying adjacency data for patch mesh not implemented");
1859         }
1860     }
1861
1862     memcpy(&mesh_container->MeshData, mesh_data, sizeof(*mesh_data));
1863     if (U(*mesh_data).pMesh)
1864         IUnknown_AddRef(U(*mesh_data).pMesh);
1865     if (skin_info) {
1866         mesh_container->pSkinInfo = skin_info;
1867         skin_info->lpVtbl->AddRef(skin_info);
1868     }
1869     *new_mesh_container = mesh_container;
1870
1871     return S_OK;
1872 error:
1873     destroy_mesh_container(mesh_container);
1874     return E_OUTOFMEMORY;
1875 }
1876
1877 static ID3DXAllocateHierarchyVtbl ID3DXAllocateHierarchyImpl_Vtbl = {
1878     ID3DXAllocateHierarchyImpl_CreateFrame,
1879     ID3DXAllocateHierarchyImpl_CreateMeshContainer,
1880     ID3DXAllocateHierarchyImpl_DestroyFrame,
1881     ID3DXAllocateHierarchyImpl_DestroyMeshContainer,
1882 };
1883 static ID3DXAllocateHierarchy alloc_hier = { &ID3DXAllocateHierarchyImpl_Vtbl };
1884
1885 static void D3DXLoadMeshTest(void)
1886 {
1887     static const char empty_xfile[] = "xof 0303txt 0032";
1888     /*________________________*/
1889     static const char simple_xfile[] =
1890         "xof 0303txt 0032"
1891         "Mesh {"
1892             "3;"
1893             "0.0; 0.0; 0.0;,"
1894             "0.0; 1.0; 0.0;,"
1895             "1.0; 1.0; 0.0;;"
1896             "1;"
1897             "3; 0, 1, 2;;"
1898         "}";
1899     static const WORD simple_index_buffer[] = {0, 1, 2};
1900     static const D3DXVECTOR3 simple_vertex_buffer[] = {
1901         {0.0, 0.0, 0.0}, {0.0, 1.0, 0.0}, {1.0, 1.0, 0.0}
1902     };
1903     const DWORD simple_fvf = D3DFVF_XYZ;
1904     static const char framed_xfile[] =
1905         "xof 0303txt 0032"
1906         "Frame {"
1907             "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;; }"
1908             "FrameTransformMatrix {" /* translation (0.0, 0.0, 2.0) */
1909               "1.0, 0.0, 0.0, 0.0,"
1910               "0.0, 1.0, 0.0, 0.0,"
1911               "0.0, 0.0, 1.0, 0.0,"
1912               "0.0, 0.0, 2.0, 1.0;;"
1913             "}"
1914             "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;; }"
1915             "FrameTransformMatrix {" /* translation (0.0, 0.0, 3.0) */
1916               "1.0, 0.0, 0.0, 0.0,"
1917               "0.0, 1.0, 0.0, 0.0,"
1918               "0.0, 0.0, 1.0, 0.0,"
1919               "0.0, 0.0, 3.0, 1.0;;"
1920             "}"
1921             "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;; }"
1922         "}";
1923     static const WORD framed_index_buffer[] = { 0, 1, 2 };
1924     static const D3DXVECTOR3 framed_vertex_buffers[3][3] = {
1925         {{0.0, 0.0, 0.0}, {0.0, 1.0, 0.0}, {1.0, 1.0, 0.0}},
1926         {{0.0, 0.0, 0.0}, {0.0, 1.0, 0.0}, {2.0, 1.0, 0.0}},
1927         {{0.0, 0.0, 0.0}, {0.0, 1.0, 0.0}, {3.0, 1.0, 0.0}},
1928     };
1929     const DWORD framed_fvf = D3DFVF_XYZ;
1930     /*________________________*/
1931     static const char box_xfile[] =
1932         "xof 0303txt 0032"
1933         "Mesh {"
1934             "8;" /* DWORD nVertices; */
1935             /* array Vector vertices[nVertices]; */
1936             "0.0; 0.0; 0.0;,"
1937             "0.0; 0.0; 1.0;,"
1938             "0.0; 1.0; 0.0;,"
1939             "0.0; 1.0; 1.0;,"
1940             "1.0; 0.0; 0.0;,"
1941             "1.0; 0.0; 1.0;,"
1942             "1.0; 1.0; 0.0;,"
1943             "1.0; 1.0; 1.0;;"
1944             "6;" /* DWORD nFaces; */
1945             /* array MeshFace faces[nFaces]; */
1946             "4; 0, 1, 3, 2;," /* (left side) */
1947             "4; 2, 3, 7, 6;," /* (top side) */
1948             "4; 6, 7, 5, 4;," /* (right side) */
1949             "4; 1, 0, 4, 5;," /* (bottom side) */
1950             "4; 1, 5, 7, 3;," /* (back side) */
1951             "4; 0, 2, 6, 4;;" /* (front side) */
1952             "MeshNormals {"
1953               "6;" /* DWORD nNormals; */
1954               /* array Vector normals[nNormals]; */
1955               "-1.0; 0.0; 0.0;,"
1956               "0.0; 1.0; 0.0;,"
1957               "1.0; 0.0; 0.0;,"
1958               "0.0; -1.0; 0.0;,"
1959               "0.0; 0.0; 1.0;,"
1960               "0.0; 0.0; -1.0;;"
1961               "6;" /* DWORD nFaceNormals; */
1962               /* array MeshFace faceNormals[nFaceNormals]; */
1963               "4; 0, 0, 0, 0;,"
1964               "4; 1, 1, 1, 1;,"
1965               "4; 2, 2, 2, 2;,"
1966               "4; 3, 3, 3, 3;,"
1967               "4; 4, 4, 4, 4;,"
1968               "4; 5, 5, 5, 5;;"
1969             "}"
1970             "MeshMaterialList materials {"
1971               "2;" /* DWORD nMaterials; */
1972               "6;" /* DWORD nFaceIndexes; */
1973               /* array DWORD faceIndexes[nFaceIndexes]; */
1974               "0, 0, 0, 1, 1, 1;;"
1975               "Material {"
1976                 /* ColorRGBA faceColor; */
1977                 "0.0; 0.0; 1.0; 1.0;;"
1978                 /* FLOAT power; */
1979                 "0.5;"
1980                 /* ColorRGB specularColor; */
1981                 "1.0; 1.0; 1.0;;"
1982                 /* ColorRGB emissiveColor; */
1983                 "0.0; 0.0; 0.0;;"
1984               "}"
1985               "Material {"
1986                 /* ColorRGBA faceColor; */
1987                 "1.0; 1.0; 1.0; 1.0;;"
1988                 /* FLOAT power; */
1989                 "1.0;"
1990                 /* ColorRGB specularColor; */
1991                 "1.0; 1.0; 1.0;;"
1992                 /* ColorRGB emissiveColor; */
1993                 "0.0; 0.0; 0.0;;"
1994                 "TextureFilename { \"texture.jpg\"; }"
1995               "}"
1996             "}"
1997           "}";
1998     static const WORD box_index_buffer[] = {
1999         0, 1, 3,
2000         0, 3, 2,
2001         8, 9, 7,
2002         8, 7, 6,
2003         10, 11, 5,
2004         10, 5, 4,
2005         12, 13, 14,
2006         12, 14, 15,
2007         16, 17, 18,
2008         16, 18, 19,
2009         20, 21, 22,
2010         20, 22, 23,
2011     };
2012     static const struct {
2013         D3DXVECTOR3 position;
2014         D3DXVECTOR3 normal;
2015     } box_vertex_buffer[] = {
2016         {{0.0, 0.0, 0.0}, {-1.0, 0.0, 0.0}},
2017         {{0.0, 0.0, 1.0}, {-1.0, 0.0, 0.0}},
2018         {{0.0, 1.0, 0.0}, {-1.0, 0.0, 0.0}},
2019         {{0.0, 1.0, 1.0}, {-1.0, 0.0, 0.0}},
2020         {{1.0, 0.0, 0.0}, {1.0, 0.0, 0.0}},
2021         {{1.0, 0.0, 1.0}, {1.0, 0.0, 0.0}},
2022         {{1.0, 1.0, 0.0}, {0.0, 1.0, 0.0}},
2023         {{1.0, 1.0, 1.0}, {0.0, 1.0, 0.0}},
2024         {{0.0, 1.0, 0.0}, {0.0, 1.0, 0.0}},
2025         {{0.0, 1.0, 1.0}, {0.0, 1.0, 0.0}},
2026         {{1.0, 1.0, 0.0}, {1.0, 0.0, 0.0}},
2027         {{1.0, 1.0, 1.0}, {1.0, 0.0, 0.0}},
2028         {{0.0, 0.0, 1.0}, {0.0, -1.0, 0.0}},
2029         {{0.0, 0.0, 0.0}, {0.0, -1.0, 0.0}},
2030         {{1.0, 0.0, 0.0}, {0.0, -1.0, 0.0}},
2031         {{1.0, 0.0, 1.0}, {0.0, -1.0, 0.0}},
2032         {{0.0, 0.0, 1.0}, {0.0, 0.0, 1.0}},
2033         {{1.0, 0.0, 1.0}, {0.0, 0.0, 1.0}},
2034         {{1.0, 1.0, 1.0}, {0.0, 0.0, 1.0}},
2035         {{0.0, 1.0, 1.0}, {0.0, 0.0, 1.0}},
2036         {{0.0, 0.0, 0.0}, {0.0, 0.0, -1.0}},
2037         {{0.0, 1.0, 0.0}, {0.0, 0.0, -1.0}},
2038         {{1.0, 1.0, 0.0}, {0.0, 0.0, -1.0}},
2039         {{1.0, 0.0, 0.0}, {0.0, 0.0, -1.0}},
2040     };
2041     static const D3DXMATERIAL box_materials[] = {
2042         {
2043             {
2044                 {0.0, 0.0, 1.0, 1.0}, /* Diffuse */
2045                 {0.0, 0.0, 0.0, 1.0}, /* Ambient */
2046                 {1.0, 1.0, 1.0, 1.0}, /* Specular */
2047                 {0.0, 0.0, 0.0, 1.0}, /* Emissive */
2048                 0.5, /* Power */
2049             },
2050             NULL, /* pTextureFilename */
2051         },
2052         {
2053             {
2054                 {1.0, 1.0, 1.0, 1.0}, /* Diffuse */
2055                 {0.0, 0.0, 0.0, 1.0}, /* Ambient */
2056                 {1.0, 1.0, 1.0, 1.0}, /* Specular */
2057                 {0.0, 0.0, 0.0, 1.0}, /* Emissive */
2058                 1.0, /* Power */
2059             },
2060             (char *)"texture.jpg", /* pTextureFilename */
2061         },
2062     };
2063     const DWORD box_fvf = D3DFVF_XYZ | D3DFVF_NORMAL;
2064     /*________________________*/
2065     HRESULT hr;
2066     HWND wnd = NULL;
2067     IDirect3D9 *d3d = NULL;
2068     IDirect3DDevice9 *device = NULL;
2069     D3DPRESENT_PARAMETERS d3dpp;
2070     ID3DXMesh *mesh = NULL;
2071     D3DXFRAME *frame_hier = NULL;
2072     D3DXMATRIX transform;
2073
2074     wnd = CreateWindow("static", "d3dx9_test", WS_POPUP, 0, 0, 1000, 1000, NULL, NULL, NULL, NULL);
2075     if (!wnd)
2076     {
2077         skip("Couldn't create application window\n");
2078         return;
2079     }
2080     d3d = Direct3DCreate9(D3D_SDK_VERSION);
2081     if (!d3d)
2082     {
2083         skip("Couldn't create IDirect3D9 object\n");
2084         goto cleanup;
2085     }
2086
2087     ZeroMemory(&d3dpp, sizeof(d3dpp));
2088     d3dpp.Windowed = TRUE;
2089     d3dpp.SwapEffect = D3DSWAPEFFECT_DISCARD;
2090     hr = IDirect3D9_CreateDevice(d3d, D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, wnd, D3DCREATE_MIXED_VERTEXPROCESSING, &d3dpp, &device);
2091     if (FAILED(hr))
2092     {
2093         skip("Failed to create IDirect3DDevice9 object %#x\n", hr);
2094         goto cleanup;
2095     }
2096
2097     hr = D3DXLoadMeshHierarchyFromXInMemory(NULL, sizeof(simple_xfile) - 1,
2098             D3DXMESH_MANAGED, device, &alloc_hier, NULL, &frame_hier, NULL);
2099     ok(hr == D3DERR_INVALIDCALL, "Expected D3DERR_INVALIDCALL, got %#x\n", hr);
2100
2101     hr = D3DXLoadMeshHierarchyFromXInMemory(simple_xfile, 0,
2102             D3DXMESH_MANAGED, device, &alloc_hier, NULL, &frame_hier, NULL);
2103     ok(hr == D3DERR_INVALIDCALL, "Expected D3DERR_INVALIDCALL, got %#x\n", hr);
2104
2105     hr = D3DXLoadMeshHierarchyFromXInMemory(simple_xfile, sizeof(simple_xfile) - 1,
2106             D3DXMESH_MANAGED, NULL, &alloc_hier, NULL, &frame_hier, NULL);
2107     ok(hr == D3DERR_INVALIDCALL, "Expected D3DERR_INVALIDCALL, got %#x\n", hr);
2108
2109     hr = D3DXLoadMeshHierarchyFromXInMemory(simple_xfile, sizeof(simple_xfile) - 1,
2110             D3DXMESH_MANAGED, device, NULL, NULL, &frame_hier, NULL);
2111     ok(hr == D3DERR_INVALIDCALL, "Expected D3DERR_INVALIDCALL, got %#x\n", hr);
2112
2113     hr = D3DXLoadMeshHierarchyFromXInMemory(empty_xfile, sizeof(empty_xfile) - 1,
2114             D3DXMESH_MANAGED, device, &alloc_hier, NULL, &frame_hier, NULL);
2115     ok(hr == E_FAIL, "Expected E_FAIL, got %#x\n", hr);
2116
2117     hr = D3DXLoadMeshHierarchyFromXInMemory(simple_xfile, sizeof(simple_xfile) - 1,
2118             D3DXMESH_MANAGED, device, &alloc_hier, NULL, NULL, NULL);
2119     ok(hr == D3DERR_INVALIDCALL, "Expected D3DERR_INVALIDCALL, got %#x\n", hr);
2120
2121     hr = D3DXLoadMeshHierarchyFromXInMemory(simple_xfile, sizeof(simple_xfile) - 1,
2122             D3DXMESH_MANAGED, device, &alloc_hier, NULL, &frame_hier, NULL);
2123     ok(hr == D3D_OK, "Expected D3D_OK, got %#x\n", hr);
2124     if (SUCCEEDED(hr)) {
2125         D3DXMESHCONTAINER *container = frame_hier->pMeshContainer;
2126
2127         ok(frame_hier->Name == NULL, "Expected NULL, got '%s'\n", frame_hier->Name);
2128         D3DXMatrixIdentity(&transform);
2129         check_matrix(&frame_hier->TransformationMatrix, &transform);
2130
2131         ok(!strcmp(container->Name, ""), "Expected '', got '%s'\n", container->Name);
2132         ok(container->MeshData.Type == D3DXMESHTYPE_MESH, "Expected %d, got %d\n",
2133            D3DXMESHTYPE_MESH, container->MeshData.Type);
2134         mesh = U(container->MeshData).pMesh;
2135         check_vertex_buffer(mesh, simple_vertex_buffer, ARRAY_SIZE(simple_vertex_buffer), simple_fvf);
2136         check_index_buffer(mesh, simple_index_buffer, ARRAY_SIZE(simple_index_buffer), sizeof(*simple_index_buffer));
2137         check_materials(container->pMaterials, container->NumMaterials, NULL, 0);
2138         check_generated_effects(container->pMaterials, container->NumMaterials, container->pEffects);
2139         check_generated_adjacency(mesh, container->pAdjacency, 0.0f);
2140         hr = D3DXFrameDestroy(frame_hier, &alloc_hier);
2141         ok(hr == D3D_OK, "Expected D3D_OK, got %#x\n", hr);
2142         frame_hier = NULL;
2143     }
2144
2145     hr = D3DXLoadMeshHierarchyFromXInMemory(box_xfile, sizeof(box_xfile) - 1,
2146             D3DXMESH_MANAGED, device, &alloc_hier, NULL, &frame_hier, NULL);
2147     ok(hr == D3D_OK, "Expected D3D_OK, got %#x\n", hr);
2148     if (SUCCEEDED(hr)) {
2149         D3DXMESHCONTAINER *container = frame_hier->pMeshContainer;
2150
2151         ok(frame_hier->Name == NULL, "Expected NULL, got '%s'\n", frame_hier->Name);
2152         D3DXMatrixIdentity(&transform);
2153         check_matrix(&frame_hier->TransformationMatrix, &transform);
2154
2155         ok(!strcmp(container->Name, ""), "Expected '', got '%s'\n", container->Name);
2156         ok(container->MeshData.Type == D3DXMESHTYPE_MESH, "Expected %d, got %d\n",
2157            D3DXMESHTYPE_MESH, container->MeshData.Type);
2158         mesh = U(container->MeshData).pMesh;
2159         check_vertex_buffer(mesh, box_vertex_buffer, ARRAY_SIZE(box_vertex_buffer), box_fvf);
2160         check_index_buffer(mesh, box_index_buffer, ARRAY_SIZE(box_index_buffer), sizeof(*box_index_buffer));
2161         check_materials(container->pMaterials, container->NumMaterials, box_materials, ARRAY_SIZE(box_materials));
2162         check_generated_effects(container->pMaterials, container->NumMaterials, container->pEffects);
2163         check_generated_adjacency(mesh, container->pAdjacency, 0.0f);
2164         hr = D3DXFrameDestroy(frame_hier, &alloc_hier);
2165         ok(hr == D3D_OK, "Expected D3D_OK, got %#x\n", hr);
2166         frame_hier = NULL;
2167     }
2168
2169     hr = D3DXLoadMeshHierarchyFromXInMemory(framed_xfile, sizeof(framed_xfile) - 1,
2170             D3DXMESH_MANAGED, device, &alloc_hier, NULL, &frame_hier, NULL);
2171     ok(hr == D3D_OK, "Expected D3D_OK, got %#x\n", hr);
2172     if (SUCCEEDED(hr)) {
2173         D3DXMESHCONTAINER *container = frame_hier->pMeshContainer;
2174         int i;
2175
2176         ok(!strcmp(frame_hier->Name, ""), "Expected '', got '%s'\n", frame_hier->Name);
2177         /* last frame transform replaces the first */
2178         D3DXMatrixIdentity(&transform);
2179         U(transform).m[3][2] = 3.0;
2180         check_matrix(&frame_hier->TransformationMatrix, &transform);
2181
2182         for (i = 0; i < 3; i++) {
2183             ok(!strcmp(container->Name, ""), "Expected '', got '%s'\n", container->Name);
2184             ok(container->MeshData.Type == D3DXMESHTYPE_MESH, "Expected %d, got %d\n",
2185                D3DXMESHTYPE_MESH, container->MeshData.Type);
2186             mesh = U(container->MeshData).pMesh;
2187             check_vertex_buffer(mesh, framed_vertex_buffers[i], ARRAY_SIZE(framed_vertex_buffers[0]), framed_fvf);
2188             check_index_buffer(mesh, framed_index_buffer, ARRAY_SIZE(framed_index_buffer), sizeof(*framed_index_buffer));
2189             check_materials(container->pMaterials, container->NumMaterials, NULL, 0);
2190             check_generated_effects(container->pMaterials, container->NumMaterials, container->pEffects);
2191             check_generated_adjacency(mesh, container->pAdjacency, 0.0f);
2192             container = container->pNextMeshContainer;
2193         }
2194         ok(container == NULL, "Expected NULL, got %p\n", container);
2195         hr = D3DXFrameDestroy(frame_hier, &alloc_hier);
2196         ok(hr == D3D_OK, "Expected D3D_OK, got %#x\n", hr);
2197         frame_hier = NULL;
2198     }
2199
2200 cleanup:
2201     if (device) IDirect3DDevice9_Release(device);
2202     if (d3d) IDirect3D9_Release(d3d);
2203     if (wnd) DestroyWindow(wnd);
2204 }
2205
2206 static void D3DXCreateBoxTest(void)
2207 {
2208     HRESULT hr;
2209     HWND wnd;
2210     WNDCLASS wc={0};
2211     IDirect3D9* d3d;
2212     IDirect3DDevice9* device;
2213     D3DPRESENT_PARAMETERS d3dpp;
2214     ID3DXMesh* box;
2215     ID3DXBuffer* ppBuffer;
2216     DWORD *buffer;
2217     static const DWORD adjacency[36]=
2218         {6, 9, 1, 2, 10, 0,
2219          1, 9, 3, 4, 10, 2,
2220          3, 8, 5, 7, 11, 4,
2221          0, 11, 7, 5, 8, 6,
2222          7, 4, 9, 2, 0, 8,
2223          1, 3, 11, 5, 6, 10};
2224     unsigned int i;
2225
2226     wc.lpfnWndProc = DefWindowProcA;
2227     wc.lpszClassName = "d3dx9_test_wc";
2228     if (!RegisterClass(&wc))
2229     {
2230         skip("RegisterClass failed\n");
2231         return;
2232     }
2233
2234     wnd = CreateWindow("d3dx9_test_wc", "d3dx9_test",
2235                         WS_SYSMENU | WS_POPUP , 0, 0, 640, 480, 0, 0, 0, 0);
2236     ok(wnd != NULL, "Expected to have a window, received NULL\n");
2237     if (!wnd)
2238     {
2239         skip("Couldn't create application window\n");
2240         return;
2241     }
2242
2243     d3d = Direct3DCreate9(D3D_SDK_VERSION);
2244     if (!d3d)
2245     {
2246         skip("Couldn't create IDirect3D9 object\n");
2247         DestroyWindow(wnd);
2248         return;
2249     }
2250
2251     memset(&d3dpp, 0, sizeof(d3dpp));
2252     d3dpp.Windowed = TRUE;
2253     d3dpp.SwapEffect = D3DSWAPEFFECT_DISCARD;
2254     hr = IDirect3D9_CreateDevice(d3d, D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, wnd, D3DCREATE_MIXED_VERTEXPROCESSING, &d3dpp, &device);
2255     if (FAILED(hr))
2256     {
2257         skip("Failed to create IDirect3DDevice9 object %#x\n", hr);
2258         IDirect3D9_Release(d3d);
2259         DestroyWindow(wnd);
2260         return;
2261     }
2262
2263     hr = D3DXCreateBuffer(36 * sizeof(DWORD), &ppBuffer);
2264     ok(hr==D3D_OK, "Expected D3D_OK, received %#x\n", hr);
2265     if (FAILED(hr)) goto end;
2266
2267     hr = D3DXCreateBox(device,2.0f,20.0f,4.9f,NULL, &ppBuffer);
2268     todo_wine ok(hr==D3DERR_INVALIDCALL, "Expected D3DERR_INVALIDCALL, received %#x\n", hr);
2269
2270     hr = D3DXCreateBox(NULL,22.0f,20.0f,4.9f,&box, &ppBuffer);
2271     todo_wine ok(hr==D3DERR_INVALIDCALL, "Expected D3DERR_INVALIDCALL, received %#x\n", hr);
2272
2273     hr = D3DXCreateBox(device,-2.0f,20.0f,4.9f,&box, &ppBuffer);
2274     todo_wine ok(hr==D3DERR_INVALIDCALL, "Expected D3DERR_INVALIDCALL, received %#x\n", hr);
2275
2276     hr = D3DXCreateBox(device,22.0f,-20.0f,4.9f,&box, &ppBuffer);
2277     todo_wine ok(hr==D3DERR_INVALIDCALL, "Expected D3DERR_INVALIDCALL, received %#x\n", hr);
2278
2279     hr = D3DXCreateBox(device,22.0f,20.0f,-4.9f,&box, &ppBuffer);
2280     todo_wine ok(hr==D3DERR_INVALIDCALL, "Expected D3DERR_INVALIDCALL, received %#x\n", hr);
2281
2282     hr = D3DXCreateBox(device,10.9f,20.0f,4.9f,&box, &ppBuffer);
2283     todo_wine ok(hr==D3D_OK, "Expected D3D_OK, received %#x\n", hr);
2284
2285     if (FAILED(hr))
2286     {
2287         skip("D3DXCreateBox failed\n");
2288         goto end;
2289     }
2290
2291     buffer = ID3DXBuffer_GetBufferPointer(ppBuffer);
2292     for(i=0; i<36; i++)
2293         todo_wine ok(adjacency[i]==buffer[i], "expected adjacency %d: %#x, received %#x\n",i,adjacency[i], buffer[i]);
2294
2295     box->lpVtbl->Release(box);
2296
2297 end:
2298     IDirect3DDevice9_Release(device);
2299     IDirect3D9_Release(d3d);
2300     ID3DXBuffer_Release(ppBuffer);
2301     DestroyWindow(wnd);
2302 }
2303
2304 struct sincos_table
2305 {
2306     float *sin;
2307     float *cos;
2308 };
2309
2310 static void free_sincos_table(struct sincos_table *sincos_table)
2311 {
2312     HeapFree(GetProcessHeap(), 0, sincos_table->cos);
2313     HeapFree(GetProcessHeap(), 0, sincos_table->sin);
2314 }
2315
2316 /* pre compute sine and cosine tables; caller must free */
2317 static BOOL compute_sincos_table(struct sincos_table *sincos_table, float angle_start, float angle_step, int n)
2318 {
2319     float angle;
2320     int i;
2321
2322     sincos_table->sin = HeapAlloc(GetProcessHeap(), 0, n * sizeof(*sincos_table->sin));
2323     if (!sincos_table->sin)
2324     {
2325         return FALSE;
2326     }
2327     sincos_table->cos = HeapAlloc(GetProcessHeap(), 0, n * sizeof(*sincos_table->cos));
2328     if (!sincos_table->cos)
2329     {
2330         HeapFree(GetProcessHeap(), 0, sincos_table->sin);
2331         return FALSE;
2332     }
2333
2334     angle = angle_start;
2335     for (i = 0; i < n; i++)
2336     {
2337         sincos_table->sin[i] = sin(angle);
2338         sincos_table->cos[i] = cos(angle);
2339         angle += angle_step;
2340     }
2341
2342     return TRUE;
2343 }
2344
2345 static WORD vertex_index(UINT slices, int slice, int stack)
2346 {
2347     return stack*slices+slice+1;
2348 }
2349
2350 /* slices = subdivisions along xy plane, stacks = subdivisions along z axis */
2351 static BOOL compute_sphere(struct mesh *mesh, FLOAT radius, UINT slices, UINT stacks)
2352 {
2353     float theta_step, theta_start;
2354     struct sincos_table theta;
2355     float phi_step, phi_start;
2356     struct sincos_table phi;
2357     DWORD number_of_vertices, number_of_faces;
2358     DWORD vertex, face;
2359     int slice, stack;
2360
2361     /* theta = angle on xy plane wrt x axis */
2362     theta_step = M_PI / stacks;
2363     theta_start = theta_step;
2364
2365     /* phi = angle on xz plane wrt z axis */
2366     phi_step = -2 * M_PI / slices;
2367     phi_start = M_PI / 2;
2368
2369     if (!compute_sincos_table(&theta, theta_start, theta_step, stacks))
2370     {
2371         return FALSE;
2372     }
2373     if (!compute_sincos_table(&phi, phi_start, phi_step, slices))
2374     {
2375         free_sincos_table(&theta);
2376         return FALSE;
2377     }
2378
2379     number_of_vertices = 2 + slices * (stacks-1);
2380     number_of_faces = 2 * slices + (stacks - 2) * (2 * slices);
2381
2382     if (!new_mesh(mesh, number_of_vertices, number_of_faces))
2383     {
2384         free_sincos_table(&phi);
2385         free_sincos_table(&theta);
2386         return FALSE;
2387     }
2388
2389     vertex = 0;
2390     face = 0;
2391
2392     mesh->vertices[vertex].normal.x = 0.0f;
2393     mesh->vertices[vertex].normal.y = 0.0f;
2394     mesh->vertices[vertex].normal.z = 1.0f;
2395     mesh->vertices[vertex].position.x = 0.0f;
2396     mesh->vertices[vertex].position.y = 0.0f;
2397     mesh->vertices[vertex].position.z = radius;
2398     vertex++;
2399
2400     for (stack = 0; stack < stacks - 1; stack++)
2401     {
2402         for (slice = 0; slice < slices; slice++)
2403         {
2404             mesh->vertices[vertex].normal.x = theta.sin[stack] * phi.cos[slice];
2405             mesh->vertices[vertex].normal.y = theta.sin[stack] * phi.sin[slice];
2406             mesh->vertices[vertex].normal.z = theta.cos[stack];
2407             mesh->vertices[vertex].position.x = radius * theta.sin[stack] * phi.cos[slice];
2408             mesh->vertices[vertex].position.y = radius * theta.sin[stack] * phi.sin[slice];
2409             mesh->vertices[vertex].position.z = radius * theta.cos[stack];
2410             vertex++;
2411
2412             if (slice > 0)
2413             {
2414                 if (stack == 0)
2415                 {
2416                     /* top stack is triangle fan */
2417                     mesh->faces[face][0] = 0;
2418                     mesh->faces[face][1] = slice + 1;
2419                     mesh->faces[face][2] = slice;
2420                     face++;
2421                 }
2422                 else
2423                 {
2424                     /* stacks in between top and bottom are quad strips */
2425                     mesh->faces[face][0] = vertex_index(slices, slice-1, stack-1);
2426                     mesh->faces[face][1] = vertex_index(slices, slice, stack-1);
2427                     mesh->faces[face][2] = vertex_index(slices, slice-1, stack);
2428                     face++;
2429
2430                     mesh->faces[face][0] = vertex_index(slices, slice, stack-1);
2431                     mesh->faces[face][1] = vertex_index(slices, slice, stack);
2432                     mesh->faces[face][2] = vertex_index(slices, slice-1, stack);
2433                     face++;
2434                 }
2435             }
2436         }
2437
2438         if (stack == 0)
2439         {
2440             mesh->faces[face][0] = 0;
2441             mesh->faces[face][1] = 1;
2442             mesh->faces[face][2] = slice;
2443             face++;
2444         }
2445         else
2446         {
2447             mesh->faces[face][0] = vertex_index(slices, slice-1, stack-1);
2448             mesh->faces[face][1] = vertex_index(slices, 0, stack-1);
2449             mesh->faces[face][2] = vertex_index(slices, slice-1, stack);
2450             face++;
2451
2452             mesh->faces[face][0] = vertex_index(slices, 0, stack-1);
2453             mesh->faces[face][1] = vertex_index(slices, 0, stack);
2454             mesh->faces[face][2] = vertex_index(slices, slice-1, stack);
2455             face++;
2456         }
2457     }
2458
2459     mesh->vertices[vertex].position.x = 0.0f;
2460     mesh->vertices[vertex].position.y = 0.0f;
2461     mesh->vertices[vertex].position.z = -radius;
2462     mesh->vertices[vertex].normal.x = 0.0f;
2463     mesh->vertices[vertex].normal.y = 0.0f;
2464     mesh->vertices[vertex].normal.z = -1.0f;
2465
2466     /* bottom stack is triangle fan */
2467     for (slice = 1; slice < slices; slice++)
2468     {
2469         mesh->faces[face][0] = vertex_index(slices, slice-1, stack-1);
2470         mesh->faces[face][1] = vertex_index(slices, slice, stack-1);
2471         mesh->faces[face][2] = vertex;
2472         face++;
2473     }
2474
2475     mesh->faces[face][0] = vertex_index(slices, slice-1, stack-1);
2476     mesh->faces[face][1] = vertex_index(slices, 0, stack-1);
2477     mesh->faces[face][2] = vertex;
2478
2479     free_sincos_table(&phi);
2480     free_sincos_table(&theta);
2481
2482     return TRUE;
2483 }
2484
2485 static void test_sphere(IDirect3DDevice9 *device, FLOAT radius, UINT slices, UINT stacks)
2486 {
2487     HRESULT hr;
2488     ID3DXMesh *sphere;
2489     struct mesh mesh;
2490     char name[256];
2491
2492     hr = D3DXCreateSphere(device, radius, slices, stacks, &sphere, NULL);
2493     ok(hr == D3D_OK, "Got result %x, expected 0 (D3D_OK)\n", hr);
2494     if (hr != D3D_OK)
2495     {
2496         skip("Couldn't create sphere\n");
2497         return;
2498     }
2499
2500     if (!compute_sphere(&mesh, radius, slices, stacks))
2501     {
2502         skip("Couldn't create mesh\n");
2503         sphere->lpVtbl->Release(sphere);
2504         return;
2505     }
2506
2507     mesh.fvf = D3DFVF_XYZ | D3DFVF_NORMAL;
2508
2509     sprintf(name, "sphere (%g, %u, %u)", radius, slices, stacks);
2510     compare_mesh(name, sphere, &mesh);
2511
2512     free_mesh(&mesh);
2513
2514     sphere->lpVtbl->Release(sphere);
2515 }
2516
2517 static void D3DXCreateSphereTest(void)
2518 {
2519     HRESULT hr;
2520     HWND wnd;
2521     IDirect3D9* d3d;
2522     IDirect3DDevice9* device;
2523     D3DPRESENT_PARAMETERS d3dpp;
2524     ID3DXMesh* sphere = NULL;
2525
2526     hr = D3DXCreateSphere(NULL, 0.0f, 0, 0, NULL, NULL);
2527     ok(hr == D3DERR_INVALIDCALL, "Got result %x, expected %x (D3DERR_INVALIDCALL)\n",hr,D3DERR_INVALIDCALL);
2528
2529     hr = D3DXCreateSphere(NULL, 0.1f, 0, 0, NULL, NULL);
2530     ok(hr == D3DERR_INVALIDCALL, "Got result %x, expected %x (D3DERR_INVALIDCALL)\n",hr,D3DERR_INVALIDCALL);
2531
2532     hr = D3DXCreateSphere(NULL, 0.0f, 1, 0, NULL, NULL);
2533     ok(hr == D3DERR_INVALIDCALL, "Got result %x, expected %x (D3DERR_INVALIDCALL)\n",hr,D3DERR_INVALIDCALL);
2534
2535     hr = D3DXCreateSphere(NULL, 0.0f, 0, 1, NULL, NULL);
2536     ok(hr == D3DERR_INVALIDCALL, "Got result %x, expected %x (D3DERR_INVALIDCALL)\n",hr,D3DERR_INVALIDCALL);
2537
2538     wnd = CreateWindow("static", "d3dx9_test", 0, 0, 0, 0, 0, NULL, NULL, NULL, NULL);
2539     d3d = Direct3DCreate9(D3D_SDK_VERSION);
2540     if (!wnd)
2541     {
2542         skip("Couldn't create application window\n");
2543         return;
2544     }
2545     if (!d3d)
2546     {
2547         skip("Couldn't create IDirect3D9 object\n");
2548         DestroyWindow(wnd);
2549         return;
2550     }
2551
2552     ZeroMemory(&d3dpp, sizeof(d3dpp));
2553     d3dpp.Windowed = TRUE;
2554     d3dpp.SwapEffect = D3DSWAPEFFECT_DISCARD;
2555     hr = IDirect3D9_CreateDevice(d3d, D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, wnd, D3DCREATE_MIXED_VERTEXPROCESSING, &d3dpp, &device);
2556     if (FAILED(hr))
2557     {
2558         skip("Failed to create IDirect3DDevice9 object %#x\n", hr);
2559         IDirect3D9_Release(d3d);
2560         DestroyWindow(wnd);
2561         return;
2562     }
2563
2564     hr = D3DXCreateSphere(device, 1.0f, 1, 1, &sphere, NULL);
2565     ok(hr == D3DERR_INVALIDCALL, "Got result %x, expected %x (D3DERR_INVALIDCALL)\n",hr,D3DERR_INVALIDCALL);
2566
2567     hr = D3DXCreateSphere(device, 1.0f, 2, 1, &sphere, NULL);
2568     ok(hr == D3DERR_INVALIDCALL, "Got result %x, expected %x (D3DERR_INVALIDCALL)\n", hr, D3DERR_INVALIDCALL);
2569
2570     hr = D3DXCreateSphere(device, 1.0f, 1, 2, &sphere, NULL);
2571     ok(hr == D3DERR_INVALIDCALL, "Got result %x, expected %x (D3DERR_INVALIDCALL)\n", hr, D3DERR_INVALIDCALL);
2572
2573     hr = D3DXCreateSphere(device, -0.1f, 1, 2, &sphere, NULL);
2574     ok(hr == D3DERR_INVALIDCALL, "Got result %x, expected %x (D3DERR_INVALIDCALL)\n", hr, D3DERR_INVALIDCALL);
2575
2576     test_sphere(device, 0.0f, 2, 2);
2577     test_sphere(device, 1.0f, 2, 2);
2578     test_sphere(device, 1.0f, 3, 2);
2579     test_sphere(device, 1.0f, 4, 4);
2580     test_sphere(device, 1.0f, 3, 4);
2581     test_sphere(device, 5.0f, 6, 7);
2582     test_sphere(device, 10.0f, 11, 12);
2583
2584     IDirect3DDevice9_Release(device);
2585     IDirect3D9_Release(d3d);
2586     DestroyWindow(wnd);
2587 }
2588
2589 static BOOL compute_cylinder(struct mesh *mesh, FLOAT radius1, FLOAT radius2, FLOAT length, UINT slices, UINT stacks)
2590 {
2591     float theta_step, theta_start;
2592     struct sincos_table theta;
2593     FLOAT delta_radius, radius, radius_step;
2594     FLOAT z, z_step, z_normal;
2595     DWORD number_of_vertices, number_of_faces;
2596     DWORD vertex, face;
2597     int slice, stack;
2598
2599     /* theta = angle on xy plane wrt x axis */
2600     theta_step = -2 * M_PI / slices;
2601     theta_start = M_PI / 2;
2602
2603     if (!compute_sincos_table(&theta, theta_start, theta_step, slices))
2604     {
2605         return FALSE;
2606     }
2607
2608     number_of_vertices = 2 + (slices * (3 + stacks));
2609     number_of_faces = 2 * slices + stacks * (2 * slices);
2610
2611     if (!new_mesh(mesh, number_of_vertices, number_of_faces))
2612     {
2613         free_sincos_table(&theta);
2614         return FALSE;
2615     }
2616
2617     vertex = 0;
2618     face = 0;
2619
2620     delta_radius = radius1 - radius2;
2621     radius = radius1;
2622     radius_step = delta_radius / stacks;
2623
2624     z = -length / 2;
2625     z_step = length / stacks;
2626     z_normal = delta_radius / length;
2627     if (isnan(z_normal))
2628     {
2629         z_normal = 0.0f;
2630     }
2631
2632     mesh->vertices[vertex].normal.x = 0.0f;
2633     mesh->vertices[vertex].normal.y = 0.0f;
2634     mesh->vertices[vertex].normal.z = -1.0f;
2635     mesh->vertices[vertex].position.x = 0.0f;
2636     mesh->vertices[vertex].position.y = 0.0f;
2637     mesh->vertices[vertex++].position.z = z;
2638
2639     for (slice = 0; slice < slices; slice++, vertex++)
2640     {
2641         mesh->vertices[vertex].normal.x = 0.0f;
2642         mesh->vertices[vertex].normal.y = 0.0f;
2643         mesh->vertices[vertex].normal.z = -1.0f;
2644         mesh->vertices[vertex].position.x = radius * theta.cos[slice];
2645         mesh->vertices[vertex].position.y = radius * theta.sin[slice];
2646         mesh->vertices[vertex].position.z = z;
2647
2648         if (slice > 0)
2649         {
2650             mesh->faces[face][0] = 0;
2651             mesh->faces[face][1] = slice;
2652             mesh->faces[face++][2] = slice + 1;
2653         }
2654     }
2655
2656     mesh->faces[face][0] = 0;
2657     mesh->faces[face][1] = slice;
2658     mesh->faces[face++][2] = 1;
2659
2660     for (stack = 1; stack <= stacks+1; stack++)
2661     {
2662         for (slice = 0; slice < slices; slice++, vertex++)
2663         {
2664             mesh->vertices[vertex].normal.x = theta.cos[slice];
2665             mesh->vertices[vertex].normal.y = theta.sin[slice];
2666             mesh->vertices[vertex].normal.z = z_normal;
2667             D3DXVec3Normalize(&mesh->vertices[vertex].normal, &mesh->vertices[vertex].normal);
2668             mesh->vertices[vertex].position.x = radius * theta.cos[slice];
2669             mesh->vertices[vertex].position.y = radius * theta.sin[slice];
2670             mesh->vertices[vertex].position.z = z;
2671
2672             if (stack > 1 && slice > 0)
2673             {
2674                 mesh->faces[face][0] = vertex_index(slices, slice-1, stack-1);
2675                 mesh->faces[face][1] = vertex_index(slices, slice-1, stack);
2676                 mesh->faces[face++][2] = vertex_index(slices, slice, stack-1);
2677
2678                 mesh->faces[face][0] = vertex_index(slices, slice, stack-1);
2679                 mesh->faces[face][1] = vertex_index(slices, slice-1, stack);
2680                 mesh->faces[face++][2] = vertex_index(slices, slice, stack);
2681             }
2682         }
2683
2684         if (stack > 1)
2685         {
2686             mesh->faces[face][0] = vertex_index(slices, slice-1, stack-1);
2687             mesh->faces[face][1] = vertex_index(slices, slice-1, stack);
2688             mesh->faces[face++][2] = vertex_index(slices, 0, stack-1);
2689
2690             mesh->faces[face][0] = vertex_index(slices, 0, stack-1);
2691             mesh->faces[face][1] = vertex_index(slices, slice-1, stack);
2692             mesh->faces[face++][2] = vertex_index(slices, 0, stack);
2693         }
2694
2695         if (stack < stacks + 1)
2696         {
2697             z += z_step;
2698             radius -= radius_step;
2699         }
2700     }
2701
2702     for (slice = 0; slice < slices; slice++, vertex++)
2703     {
2704         mesh->vertices[vertex].normal.x = 0.0f;
2705         mesh->vertices[vertex].normal.y = 0.0f;
2706         mesh->vertices[vertex].normal.z = 1.0f;
2707         mesh->vertices[vertex].position.x = radius * theta.cos[slice];
2708         mesh->vertices[vertex].position.y = radius * theta.sin[slice];
2709         mesh->vertices[vertex].position.z = z;
2710
2711         if (slice > 0)
2712         {
2713             mesh->faces[face][0] = vertex_index(slices, slice-1, stack);
2714             mesh->faces[face][1] = number_of_vertices - 1;
2715             mesh->faces[face++][2] = vertex_index(slices, slice, stack);
2716         }
2717     }
2718
2719     mesh->vertices[vertex].position.x = 0.0f;
2720     mesh->vertices[vertex].position.y = 0.0f;
2721     mesh->vertices[vertex].position.z = z;
2722     mesh->vertices[vertex].normal.x = 0.0f;
2723     mesh->vertices[vertex].normal.y = 0.0f;
2724     mesh->vertices[vertex].normal.z = 1.0f;
2725
2726     mesh->faces[face][0] = vertex_index(slices, slice-1, stack);
2727     mesh->faces[face][1] = number_of_vertices - 1;
2728     mesh->faces[face][2] = vertex_index(slices, 0, stack);
2729
2730     free_sincos_table(&theta);
2731
2732     return TRUE;
2733 }
2734
2735 static void test_cylinder(IDirect3DDevice9 *device, FLOAT radius1, FLOAT radius2, FLOAT length, UINT slices, UINT stacks)
2736 {
2737     HRESULT hr;
2738     ID3DXMesh *cylinder;
2739     struct mesh mesh;
2740     char name[256];
2741
2742     hr = D3DXCreateCylinder(device, radius1, radius2, length, slices, stacks, &cylinder, NULL);
2743     ok(hr == D3D_OK, "Got result %x, expected 0 (D3D_OK)\n", hr);
2744     if (hr != D3D_OK)
2745     {
2746         skip("Couldn't create cylinder\n");
2747         return;
2748     }
2749
2750     if (!compute_cylinder(&mesh, radius1, radius2, length, slices, stacks))
2751     {
2752         skip("Couldn't create mesh\n");
2753         cylinder->lpVtbl->Release(cylinder);
2754         return;
2755     }
2756
2757     mesh.fvf = D3DFVF_XYZ | D3DFVF_NORMAL;
2758
2759     sprintf(name, "cylinder (%g, %g, %g, %u, %u)", radius1, radius2, length, slices, stacks);
2760     compare_mesh(name, cylinder, &mesh);
2761
2762     free_mesh(&mesh);
2763
2764     cylinder->lpVtbl->Release(cylinder);
2765 }
2766
2767 static void D3DXCreateCylinderTest(void)
2768 {
2769     HRESULT hr;
2770     HWND wnd;
2771     IDirect3D9* d3d;
2772     IDirect3DDevice9* device;
2773     D3DPRESENT_PARAMETERS d3dpp;
2774     ID3DXMesh* cylinder = NULL;
2775
2776     hr = D3DXCreateCylinder(NULL, 0.0f, 0.0f, 0.0f, 0, 0, NULL, NULL);
2777     ok(hr == D3DERR_INVALIDCALL, "Got result %x, expected %x (D3DERR_INVALIDCALL)\n",hr,D3DERR_INVALIDCALL);
2778
2779     hr = D3DXCreateCylinder(NULL, 1.0f, 1.0f, 1.0f, 2, 1, &cylinder, NULL);
2780     ok(hr == D3DERR_INVALIDCALL, "Got result %x, expected %x (D3DERR_INVALIDCALL)\n",hr,D3DERR_INVALIDCALL);
2781
2782     wnd = CreateWindow("static", "d3dx9_test", 0, 0, 0, 0, 0, NULL, NULL, NULL, NULL);
2783     d3d = Direct3DCreate9(D3D_SDK_VERSION);
2784     if (!wnd)
2785     {
2786         skip("Couldn't create application window\n");
2787         return;
2788     }
2789     if (!d3d)
2790     {
2791         skip("Couldn't create IDirect3D9 object\n");
2792         DestroyWindow(wnd);
2793         return;
2794     }
2795
2796     ZeroMemory(&d3dpp, sizeof(d3dpp));
2797     d3dpp.Windowed = TRUE;
2798     d3dpp.SwapEffect = D3DSWAPEFFECT_DISCARD;
2799     hr = IDirect3D9_CreateDevice(d3d, D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, wnd, D3DCREATE_MIXED_VERTEXPROCESSING, &d3dpp, &device);
2800     if (FAILED(hr))
2801     {
2802         skip("Failed to create IDirect3DDevice9 object %#x\n", hr);
2803         IDirect3D9_Release(d3d);
2804         DestroyWindow(wnd);
2805         return;
2806     }
2807
2808     hr = D3DXCreateCylinder(device, -0.1f, 1.0f, 1.0f, 2, 1, &cylinder, NULL);
2809     ok(hr == D3DERR_INVALIDCALL, "Got result %x, expected %x (D3DERR_INVALIDCALL)\n",hr,D3DERR_INVALIDCALL);
2810
2811     hr = D3DXCreateCylinder(device, 0.0f, 1.0f, 1.0f, 2, 1, &cylinder, NULL);
2812     ok(hr == D3D_OK, "Got result %x, expected 0 (D3D_OK)\n",hr);
2813
2814     if (SUCCEEDED(hr) && cylinder)
2815     {
2816         cylinder->lpVtbl->Release(cylinder);
2817     }
2818
2819     hr = D3DXCreateCylinder(device, 1.0f, -0.1f, 1.0f, 2, 1, &cylinder, NULL);
2820     ok(hr == D3DERR_INVALIDCALL, "Got result %x, expected %x (D3DERR_INVALIDCALL)\n",hr,D3DERR_INVALIDCALL);
2821
2822     hr = D3DXCreateCylinder(device, 1.0f, 0.0f, 1.0f, 2, 1, &cylinder, NULL);
2823     ok(hr == D3D_OK, "Got result %x, expected 0 (D3D_OK)\n",hr);
2824
2825     if (SUCCEEDED(hr) && cylinder)
2826     {
2827         cylinder->lpVtbl->Release(cylinder);
2828     }
2829
2830     hr = D3DXCreateCylinder(device, 1.0f, 1.0f, -0.1f, 2, 1, &cylinder, NULL);
2831     ok(hr == D3DERR_INVALIDCALL, "Got result %x, expected %x (D3DERR_INVALIDCALL)\n",hr,D3DERR_INVALIDCALL);
2832
2833     /* Test with length == 0.0f succeeds */
2834     hr = D3DXCreateCylinder(device, 1.0f, 1.0f, 0.0f, 2, 1, &cylinder, NULL);
2835     ok(hr == D3D_OK, "Got result %x, expected 0 (D3D_OK)\n",hr);
2836
2837     if (SUCCEEDED(hr) && cylinder)
2838     {
2839         cylinder->lpVtbl->Release(cylinder);
2840     }
2841
2842     hr = D3DXCreateCylinder(device, 1.0f, 1.0f, 1.0f, 1, 1, &cylinder, NULL);
2843     ok(hr == D3DERR_INVALIDCALL, "Got result %x, expected %x (D3DERR_INVALIDCALL)\n",hr,D3DERR_INVALIDCALL);
2844
2845     hr = D3DXCreateCylinder(device, 1.0f, 1.0f, 1.0f, 2, 0, &cylinder, NULL);
2846     ok(hr == D3DERR_INVALIDCALL, "Got result %x, expected %x (D3DERR_INVALIDCALL)\n",hr,D3DERR_INVALIDCALL);
2847
2848     hr = D3DXCreateCylinder(device, 1.0f, 1.0f, 1.0f, 2, 1, NULL, NULL);
2849     ok(hr == D3DERR_INVALIDCALL, "Got result %x, expected %x (D3DERR_INVALIDCALL)\n",hr,D3DERR_INVALIDCALL);
2850
2851     test_cylinder(device, 0.0f, 0.0f, 0.0f, 2, 1);
2852     test_cylinder(device, 1.0f, 1.0f, 1.0f, 2, 1);
2853     test_cylinder(device, 1.0f, 1.0f, 2.0f, 3, 4);
2854     test_cylinder(device, 3.0f, 2.0f, 4.0f, 3, 4);
2855     test_cylinder(device, 2.0f, 3.0f, 4.0f, 3, 4);
2856     test_cylinder(device, 3.0f, 4.0f, 5.0f, 11, 20);
2857
2858     IDirect3DDevice9_Release(device);
2859     IDirect3D9_Release(d3d);
2860     DestroyWindow(wnd);
2861 }
2862
2863 struct dynamic_array
2864 {
2865     int count, capacity;
2866     void *items;
2867 };
2868
2869 enum pointtype {
2870     POINTTYPE_CURVE = 0,
2871     POINTTYPE_CORNER,
2872     POINTTYPE_CURVE_START,
2873     POINTTYPE_CURVE_END,
2874     POINTTYPE_CURVE_MIDDLE,
2875 };
2876
2877 struct point2d
2878 {
2879     D3DXVECTOR2 pos;
2880     enum pointtype corner;
2881 };
2882
2883 /* is a dynamic_array */
2884 struct outline
2885 {
2886     int count, capacity;
2887     struct point2d *items;
2888 };
2889
2890 /* is a dynamic_array */
2891 struct outline_array
2892 {
2893     int count, capacity;
2894     struct outline *items;
2895 };
2896
2897 struct glyphinfo
2898 {
2899     struct outline_array outlines;
2900     float offset_x;
2901 };
2902
2903 static BOOL reserve(struct dynamic_array *array, int count, int itemsize)
2904 {
2905     if (count > array->capacity) {
2906         void *new_buffer;
2907         int new_capacity;
2908         if (array->items && array->capacity) {
2909             new_capacity = max(array->capacity * 2, count);
2910             new_buffer = HeapReAlloc(GetProcessHeap(), 0, array->items, new_capacity * itemsize);
2911         } else {
2912             new_capacity = max(16, count);
2913             new_buffer = HeapAlloc(GetProcessHeap(), 0, new_capacity * itemsize);
2914         }
2915         if (!new_buffer)
2916             return FALSE;
2917         array->items = new_buffer;
2918         array->capacity = new_capacity;
2919     }
2920     return TRUE;
2921 }
2922
2923 static struct point2d *add_point(struct outline *array)
2924 {
2925     struct point2d *item;
2926
2927     if (!reserve((struct dynamic_array *)array, array->count + 1, sizeof(array->items[0])))
2928         return NULL;
2929
2930     item = &array->items[array->count++];
2931     ZeroMemory(item, sizeof(*item));
2932     return item;
2933 }
2934
2935 static struct outline *add_outline(struct outline_array *array)
2936 {
2937     struct outline *item;
2938
2939     if (!reserve((struct dynamic_array *)array, array->count + 1, sizeof(array->items[0])))
2940         return NULL;
2941
2942     item = &array->items[array->count++];
2943     ZeroMemory(item, sizeof(*item));
2944     return item;
2945 }
2946
2947 static inline D3DXVECTOR2 *convert_fixed_to_float(POINTFX *pt, int count, float emsquare)
2948 {
2949     D3DXVECTOR2 *ret = (D3DXVECTOR2*)pt;
2950     while (count--) {
2951         D3DXVECTOR2 *pt_flt = (D3DXVECTOR2*)pt;
2952         pt_flt->x = (pt->x.value + pt->x.fract / (float)0x10000) / emsquare;
2953         pt_flt->y = (pt->y.value + pt->y.fract / (float)0x10000) / emsquare;
2954         pt++;
2955     }
2956     return ret;
2957 }
2958
2959 static HRESULT add_bezier_points(struct outline *outline, const D3DXVECTOR2 *p1,
2960                                  const D3DXVECTOR2 *p2, const D3DXVECTOR2 *p3,
2961                                  float max_deviation)
2962 {
2963     D3DXVECTOR2 split1 = {0, 0}, split2 = {0, 0}, middle, vec;
2964     float deviation;
2965
2966     D3DXVec2Scale(&split1, D3DXVec2Add(&split1, p1, p2), 0.5f);
2967     D3DXVec2Scale(&split2, D3DXVec2Add(&split2, p2, p3), 0.5f);
2968     D3DXVec2Scale(&middle, D3DXVec2Add(&middle, &split1, &split2), 0.5f);
2969
2970     deviation = D3DXVec2Length(D3DXVec2Subtract(&vec, &middle, p2));
2971     if (deviation < max_deviation) {
2972         struct point2d *pt = add_point(outline);
2973         if (!pt) return E_OUTOFMEMORY;
2974         pt->pos = *p2;
2975         pt->corner = POINTTYPE_CURVE;
2976         /* the end point is omitted because the end line merges into the next segment of
2977          * the split bezier curve, and the end of the split bezier curve is added outside
2978          * this recursive function. */
2979     } else {
2980         HRESULT hr = add_bezier_points(outline, p1, &split1, &middle, max_deviation);
2981         if (hr != S_OK) return hr;
2982         hr = add_bezier_points(outline, &middle, &split2, p3, max_deviation);
2983         if (hr != S_OK) return hr;
2984     }
2985
2986     return S_OK;
2987 }
2988
2989 static inline BOOL is_direction_similar(D3DXVECTOR2 *dir1, D3DXVECTOR2 *dir2, float cos_theta)
2990 {
2991     /* dot product = cos(theta) */
2992     return D3DXVec2Dot(dir1, dir2) > cos_theta;
2993 }
2994
2995 static inline D3DXVECTOR2 *unit_vec2(D3DXVECTOR2 *dir, const D3DXVECTOR2 *pt1, const D3DXVECTOR2 *pt2)
2996 {
2997     return D3DXVec2Normalize(D3DXVec2Subtract(dir, pt2, pt1), dir);
2998 }
2999
3000 static BOOL attempt_line_merge(struct outline *outline,
3001                                int pt_index,
3002                                const D3DXVECTOR2 *nextpt,
3003                                BOOL to_curve)
3004 {
3005     D3DXVECTOR2 curdir, lastdir;
3006     struct point2d *prevpt, *pt;
3007     BOOL ret = FALSE;
3008     const float cos_half = cos(D3DXToRadian(0.5f));
3009
3010     pt = &outline->items[pt_index];
3011     pt_index = (pt_index - 1 + outline->count) % outline->count;
3012     prevpt = &outline->items[pt_index];
3013
3014     if (to_curve)
3015         pt->corner = pt->corner != POINTTYPE_CORNER ? POINTTYPE_CURVE_MIDDLE : POINTTYPE_CURVE_START;
3016
3017     if (outline->count < 2)
3018         return FALSE;
3019
3020     /* remove last point if the next line continues the last line */
3021     unit_vec2(&lastdir, &prevpt->pos, &pt->pos);
3022     unit_vec2(&curdir, &pt->pos, nextpt);
3023     if (is_direction_similar(&lastdir, &curdir, cos_half))
3024     {
3025         outline->count--;
3026         if (pt->corner == POINTTYPE_CURVE_END)
3027             prevpt->corner = pt->corner;
3028         if (prevpt->corner == POINTTYPE_CURVE_END && to_curve)
3029             prevpt->corner = POINTTYPE_CURVE_MIDDLE;
3030         pt = prevpt;
3031
3032         ret = TRUE;
3033         if (outline->count < 2)
3034             return ret;
3035
3036         pt_index = (pt_index - 1 + outline->count) % outline->count;
3037         prevpt = &outline->items[pt_index];
3038         unit_vec2(&lastdir, &prevpt->pos, &pt->pos);
3039         unit_vec2(&curdir, &pt->pos, nextpt);
3040     }
3041     return ret;
3042 }
3043
3044 static HRESULT create_outline(struct glyphinfo *glyph, void *raw_outline, int datasize,
3045                               float max_deviation, float emsquare)
3046 {
3047     const float cos_45 = cos(D3DXToRadian(45.0f));
3048     const float cos_90 = cos(D3DXToRadian(90.0f));
3049     TTPOLYGONHEADER *header = (TTPOLYGONHEADER *)raw_outline;
3050
3051     while ((char *)header < (char *)raw_outline + datasize)
3052     {
3053         TTPOLYCURVE *curve = (TTPOLYCURVE *)(header + 1);
3054         struct point2d *lastpt, *pt;
3055         D3DXVECTOR2 lastdir;
3056         D3DXVECTOR2 *pt_flt;
3057         int j;
3058         struct outline *outline = add_outline(&glyph->outlines);
3059
3060         if (!outline)
3061             return E_OUTOFMEMORY;
3062
3063         pt = add_point(outline);
3064         if (!pt)
3065             return E_OUTOFMEMORY;
3066         pt_flt = convert_fixed_to_float(&header->pfxStart, 1, emsquare);
3067         pt->pos = *pt_flt;
3068         pt->corner = POINTTYPE_CORNER;
3069
3070         if (header->dwType != TT_POLYGON_TYPE)
3071             trace("Unknown header type %d\n", header->dwType);
3072
3073         while ((char *)curve < (char *)header + header->cb)
3074         {
3075             D3DXVECTOR2 bezier_start = outline->items[outline->count - 1].pos;
3076             BOOL to_curve = curve->wType != TT_PRIM_LINE && curve->cpfx > 1;
3077
3078             if (!curve->cpfx) {
3079                 curve = (TTPOLYCURVE *)&curve->apfx[curve->cpfx];
3080                 continue;
3081             }
3082
3083             pt_flt = convert_fixed_to_float(curve->apfx, curve->cpfx, emsquare);
3084
3085             attempt_line_merge(outline, outline->count - 1, &pt_flt[0], to_curve);
3086
3087             if (to_curve)
3088             {
3089                 HRESULT hr;
3090                 int count = curve->cpfx;
3091                 j = 0;
3092
3093                 while (count > 2)
3094                 {
3095                     D3DXVECTOR2 bezier_end;
3096
3097                     D3DXVec2Scale(&bezier_end, D3DXVec2Add(&bezier_end, &pt_flt[j], &pt_flt[j+1]), 0.5f);
3098                     hr = add_bezier_points(outline, &bezier_start, &pt_flt[j], &bezier_end, max_deviation);
3099                     if (hr != S_OK)
3100                         return hr;
3101                     bezier_start = bezier_end;
3102                     count--;
3103                     j++;
3104                 }
3105                 hr = add_bezier_points(outline, &bezier_start, &pt_flt[j], &pt_flt[j+1], max_deviation);
3106                 if (hr != S_OK)
3107                     return hr;
3108
3109                 pt = add_point(outline);
3110                 if (!pt)
3111                     return E_OUTOFMEMORY;
3112                 j++;
3113                 pt->pos = pt_flt[j];
3114                 pt->corner = POINTTYPE_CURVE_END;
3115             } else {
3116                 for (j = 0; j < curve->cpfx; j++)
3117                 {
3118                     pt = add_point(outline);
3119                     if (!pt)
3120                         return E_OUTOFMEMORY;
3121                     pt->pos = pt_flt[j];
3122                     pt->corner = POINTTYPE_CORNER;
3123                 }
3124             }
3125
3126             curve = (TTPOLYCURVE *)&curve->apfx[curve->cpfx];
3127         }
3128
3129         /* remove last point if the next line continues the last line */
3130         if (outline->count >= 3) {
3131             BOOL to_curve;
3132
3133             lastpt = &outline->items[outline->count - 1];
3134             pt = &outline->items[0];
3135             if (pt->pos.x == lastpt->pos.x && pt->pos.y == lastpt->pos.y) {
3136                 if (lastpt->corner == POINTTYPE_CURVE_END)
3137                 {
3138                     if (pt->corner == POINTTYPE_CURVE_START)
3139                         pt->corner = POINTTYPE_CURVE_MIDDLE;
3140                     else
3141                         pt->corner = POINTTYPE_CURVE_END;
3142                 }
3143                 outline->count--;
3144                 lastpt = &outline->items[outline->count - 1];
3145             } else {
3146                 /* outline closed with a line from end to start point */
3147                 attempt_line_merge(outline, outline->count - 1, &pt->pos, FALSE);
3148             }
3149             lastpt = &outline->items[0];
3150             to_curve = lastpt->corner != POINTTYPE_CORNER && lastpt->corner != POINTTYPE_CURVE_END;
3151             if (lastpt->corner == POINTTYPE_CURVE_START)
3152                 lastpt->corner = POINTTYPE_CORNER;
3153             pt = &outline->items[1];
3154             if (attempt_line_merge(outline, 0, &pt->pos, to_curve))
3155                 *lastpt = outline->items[outline->count];
3156         }
3157
3158         lastpt = &outline->items[outline->count - 1];
3159         pt = &outline->items[0];
3160         unit_vec2(&lastdir, &lastpt->pos, &pt->pos);
3161         for (j = 0; j < outline->count; j++)
3162         {
3163             D3DXVECTOR2 curdir;
3164
3165             lastpt = pt;
3166             pt = &outline->items[(j + 1) % outline->count];
3167             unit_vec2(&curdir, &lastpt->pos, &pt->pos);
3168
3169             switch (lastpt->corner)
3170             {
3171                 case POINTTYPE_CURVE_START:
3172                 case POINTTYPE_CURVE_END:
3173                     if (!is_direction_similar(&lastdir, &curdir, cos_45))
3174                         lastpt->corner = POINTTYPE_CORNER;
3175                     break;
3176                 case POINTTYPE_CURVE_MIDDLE:
3177                     if (!is_direction_similar(&lastdir, &curdir, cos_90))
3178                         lastpt->corner = POINTTYPE_CORNER;
3179                     else
3180                         lastpt->corner = POINTTYPE_CURVE;
3181                     break;
3182                 default:
3183                     break;
3184             }
3185             lastdir = curdir;
3186         }
3187
3188         header = (TTPOLYGONHEADER *)((char *)header + header->cb);
3189     }
3190     return S_OK;
3191 }
3192
3193 static BOOL compute_text_mesh(struct mesh *mesh, HDC hdc, LPCSTR text, FLOAT deviation, FLOAT extrusion, FLOAT otmEMSquare)
3194 {
3195     HRESULT hr = E_FAIL;
3196     DWORD nb_vertices, nb_faces;
3197     DWORD nb_corners, nb_outline_points;
3198     int textlen = 0;
3199     float offset_x;
3200     char *raw_outline = NULL;
3201     struct glyphinfo *glyphs = NULL;
3202     GLYPHMETRICS gm;
3203     int i;
3204     struct vertex *vertex_ptr;
3205     face *face_ptr;
3206
3207     if (deviation == 0.0f)
3208         deviation = 1.0f / otmEMSquare;
3209
3210     textlen = strlen(text);
3211     glyphs = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, textlen * sizeof(*glyphs));
3212     if (!glyphs) {
3213         hr = E_OUTOFMEMORY;
3214         goto error;
3215     }
3216
3217     offset_x = 0.0f;
3218     for (i = 0; i < textlen; i++)
3219     {
3220         /* get outline points from data returned from GetGlyphOutline */
3221         const MAT2 identity = {{0, 1}, {0, 0}, {0, 0}, {0, 1}};
3222         int datasize;
3223
3224         glyphs[i].offset_x = offset_x;
3225
3226         datasize = GetGlyphOutline(hdc, text[i], GGO_NATIVE, &gm, 0, NULL, &identity);
3227         if (datasize < 0) {
3228             hr = E_FAIL;
3229             goto error;
3230         }
3231         HeapFree(GetProcessHeap(), 0, raw_outline);
3232         raw_outline = HeapAlloc(GetProcessHeap(), 0, datasize);
3233         if (!glyphs) {
3234             hr = E_OUTOFMEMORY;
3235             goto error;
3236         }
3237         datasize = GetGlyphOutline(hdc, text[i], GGO_NATIVE, &gm, datasize, raw_outline, &identity);
3238
3239         create_outline(&glyphs[i], raw_outline, datasize, deviation, otmEMSquare);
3240
3241         offset_x += gm.gmCellIncX / (float)otmEMSquare;
3242     }
3243
3244     /* corner points need an extra vertex for the different side faces normals */
3245     nb_corners = 0;
3246     nb_outline_points = 0;
3247     for (i = 0; i < textlen; i++)
3248     {
3249         int j;
3250         for (j = 0; j < glyphs[i].outlines.count; j++)
3251         {
3252             int k;
3253             struct outline *outline = &glyphs[i].outlines.items[j];
3254             nb_outline_points += outline->count;
3255             nb_corners++; /* first outline point always repeated as a corner */
3256             for (k = 1; k < outline->count; k++)
3257                 if (outline->items[k].corner)
3258                     nb_corners++;
3259         }
3260     }
3261
3262     nb_vertices = (nb_outline_points + nb_corners) * 2 + textlen;
3263     nb_faces = nb_outline_points * 2;
3264
3265     if (!new_mesh(mesh, nb_vertices, nb_faces))
3266         goto error;
3267
3268     /* convert 2D vertices and faces into 3D mesh */
3269     vertex_ptr = mesh->vertices;
3270     face_ptr = mesh->faces;
3271     for (i = 0; i < textlen; i++)
3272     {
3273         int j;
3274
3275         /* side vertices and faces */
3276         for (j = 0; j < glyphs[i].outlines.count; j++)
3277         {
3278             struct vertex *outline_vertices = vertex_ptr;
3279             struct outline *outline = &glyphs[i].outlines.items[j];
3280             int k;
3281             struct point2d *prevpt = &outline->items[outline->count - 1];
3282             struct point2d *pt = &outline->items[0];
3283
3284             for (k = 1; k <= outline->count; k++)
3285             {
3286                 struct vertex vtx;
3287                 struct point2d *nextpt = &outline->items[k % outline->count];
3288                 WORD vtx_idx = vertex_ptr - mesh->vertices;
3289                 D3DXVECTOR2 vec;
3290
3291                 if (pt->corner == POINTTYPE_CURVE_START)
3292                     D3DXVec2Subtract(&vec, &pt->pos, &prevpt->pos);
3293                 else if (pt->corner)
3294                     D3DXVec2Subtract(&vec, &nextpt->pos, &pt->pos);
3295                 else
3296                     D3DXVec2Subtract(&vec, &nextpt->pos, &prevpt->pos);
3297                 D3DXVec2Normalize(&vec, &vec);
3298                 vtx.normal.x = -vec.y;
3299                 vtx.normal.y = vec.x;
3300                 vtx.normal.z = 0;
3301
3302                 vtx.position.x = pt->pos.x + glyphs[i].offset_x;
3303                 vtx.position.y = pt->pos.y;
3304                 vtx.position.z = 0;
3305                 *vertex_ptr++ = vtx;
3306
3307                 vtx.position.z = -extrusion;
3308                 *vertex_ptr++ = vtx;
3309
3310                 vtx.position.x = nextpt->pos.x + glyphs[i].offset_x;
3311                 vtx.position.y = nextpt->pos.y;
3312                 if (pt->corner && nextpt->corner && nextpt->corner != POINTTYPE_CURVE_END) {
3313                     vtx.position.z = -extrusion;
3314                     *vertex_ptr++ = vtx;
3315                     vtx.position.z = 0;
3316                     *vertex_ptr++ = vtx;
3317
3318                     (*face_ptr)[0] = vtx_idx;
3319                     (*face_ptr)[1] = vtx_idx + 2;
3320                     (*face_ptr)[2] = vtx_idx + 1;
3321                     face_ptr++;
3322
3323                     (*face_ptr)[0] = vtx_idx;
3324                     (*face_ptr)[1] = vtx_idx + 3;
3325                     (*face_ptr)[2] = vtx_idx + 2;
3326                     face_ptr++;
3327                 } else {
3328                     if (nextpt->corner) {
3329                         if (nextpt->corner == POINTTYPE_CURVE_END) {
3330                             struct point2d *nextpt2 = &outline->items[(k + 1) % outline->count];
3331                             D3DXVec2Subtract(&vec, &nextpt2->pos, &nextpt->pos);
3332                         } else {
3333                             D3DXVec2Subtract(&vec, &nextpt->pos, &pt->pos);
3334                         }
3335                         D3DXVec2Normalize(&vec, &vec);
3336                         vtx.normal.x = -vec.y;
3337                         vtx.normal.y = vec.x;
3338
3339                         vtx.position.z = 0;
3340                         *vertex_ptr++ = vtx;
3341                         vtx.position.z = -extrusion;
3342                         *vertex_ptr++ = vtx;
3343                     }
3344
3345                     (*face_ptr)[0] = vtx_idx;
3346                     (*face_ptr)[1] = vtx_idx + 3;
3347                     (*face_ptr)[2] = vtx_idx + 1;
3348                     face_ptr++;
3349
3350                     (*face_ptr)[0] = vtx_idx;
3351                     (*face_ptr)[1] = vtx_idx + 2;
3352                     (*face_ptr)[2] = vtx_idx + 3;
3353                     face_ptr++;
3354                 }
3355
3356                 prevpt = pt;
3357                 pt = nextpt;
3358             }
3359             if (!pt->corner) {
3360                 *vertex_ptr++ = *outline_vertices++;
3361                 *vertex_ptr++ = *outline_vertices++;
3362             }
3363         }
3364
3365         /* FIXME: compute expected faces */
3366         /* Add placeholder to separate glyph outlines */
3367         vertex_ptr->position.x = 0;
3368         vertex_ptr->position.y = 0;
3369         vertex_ptr->position.z = 0;
3370         vertex_ptr->normal.x = 0;
3371         vertex_ptr->normal.y = 0;
3372         vertex_ptr->normal.z = 1;
3373         vertex_ptr++;
3374     }
3375
3376     hr = D3D_OK;
3377 error:
3378     if (glyphs) {
3379         for (i = 0; i < textlen; i++)
3380         {
3381             int j;
3382             for (j = 0; j < glyphs[i].outlines.count; j++)
3383                 HeapFree(GetProcessHeap(), 0, glyphs[i].outlines.items[j].items);
3384             HeapFree(GetProcessHeap(), 0, glyphs[i].outlines.items);
3385         }
3386         HeapFree(GetProcessHeap(), 0, glyphs);
3387     }
3388     HeapFree(GetProcessHeap(), 0, raw_outline);
3389
3390     return hr == D3D_OK;
3391 }
3392
3393 static void compare_text_outline_mesh(const char *name, ID3DXMesh *d3dxmesh, struct mesh *mesh, int textlen, float extrusion)
3394 {
3395     HRESULT hr;
3396     DWORD number_of_vertices, number_of_faces;
3397     IDirect3DVertexBuffer9 *vertex_buffer = NULL;
3398     IDirect3DIndexBuffer9 *index_buffer = NULL;
3399     D3DVERTEXBUFFER_DESC vertex_buffer_description;
3400     D3DINDEXBUFFER_DESC index_buffer_description;
3401     struct vertex *vertices = NULL;
3402     face *faces = NULL;
3403     int expected, i;
3404     int vtx_idx1, face_idx1, vtx_idx2, face_idx2;
3405
3406     number_of_vertices = d3dxmesh->lpVtbl->GetNumVertices(d3dxmesh);
3407     number_of_faces = d3dxmesh->lpVtbl->GetNumFaces(d3dxmesh);
3408
3409     /* vertex buffer */
3410     hr = d3dxmesh->lpVtbl->GetVertexBuffer(d3dxmesh, &vertex_buffer);
3411     ok(hr == D3D_OK, "Test %s, result %x, expected 0 (D3D_OK)\n", name, hr);
3412     if (hr != D3D_OK)
3413     {
3414         skip("Couldn't get vertex buffers\n");
3415         goto error;
3416     }
3417
3418     hr = IDirect3DVertexBuffer9_GetDesc(vertex_buffer, &vertex_buffer_description);
3419     ok(hr == D3D_OK, "Test %s, result %x, expected 0 (D3D_OK)\n", name, hr);
3420
3421     if (hr != D3D_OK)
3422     {
3423         skip("Couldn't get vertex buffer description\n");
3424     }
3425     else
3426     {
3427         ok(vertex_buffer_description.Format == D3DFMT_VERTEXDATA, "Test %s, result %x, expected %x (D3DFMT_VERTEXDATA)\n",
3428            name, vertex_buffer_description.Format, D3DFMT_VERTEXDATA);
3429         ok(vertex_buffer_description.Type == D3DRTYPE_VERTEXBUFFER, "Test %s, result %x, expected %x (D3DRTYPE_VERTEXBUFFER)\n",
3430            name, vertex_buffer_description.Type, D3DRTYPE_VERTEXBUFFER);
3431         ok(vertex_buffer_description.Usage == 0, "Test %s, result %x, expected %x\n", name, vertex_buffer_description.Usage, 0);
3432         ok(vertex_buffer_description.Pool == D3DPOOL_MANAGED, "Test %s, result %x, expected %x (D3DPOOL_MANAGED)\n",
3433            name, vertex_buffer_description.Pool, D3DPOOL_MANAGED);
3434         ok(vertex_buffer_description.FVF == mesh->fvf, "Test %s, result %x, expected %x\n",
3435            name, vertex_buffer_description.FVF, mesh->fvf);
3436         if (mesh->fvf == 0)
3437         {
3438             expected = number_of_vertices * mesh->vertex_size;
3439         }
3440         else
3441         {
3442             expected = number_of_vertices * D3DXGetFVFVertexSize(mesh->fvf);
3443         }
3444         ok(vertex_buffer_description.Size == expected, "Test %s, result %x, expected %x\n",
3445            name, vertex_buffer_description.Size, expected);
3446     }
3447
3448     hr = d3dxmesh->lpVtbl->GetIndexBuffer(d3dxmesh, &index_buffer);
3449     ok(hr == D3D_OK, "Test %s, result %x, expected 0 (D3D_OK)\n", name, hr);
3450     if (hr != D3D_OK)
3451     {
3452         skip("Couldn't get index buffer\n");
3453         goto error;
3454     }
3455
3456     hr = IDirect3DIndexBuffer9_GetDesc(index_buffer, &index_buffer_description);
3457     ok(hr == D3D_OK, "Test %s, result %x, expected 0 (D3D_OK)\n", name, hr);
3458
3459     if (hr != D3D_OK)
3460     {
3461         skip("Couldn't get index buffer description\n");
3462     }
3463     else
3464     {
3465         ok(index_buffer_description.Format == D3DFMT_INDEX16, "Test %s, result %x, expected %x (D3DFMT_INDEX16)\n",
3466            name, index_buffer_description.Format, D3DFMT_INDEX16);
3467         ok(index_buffer_description.Type == D3DRTYPE_INDEXBUFFER, "Test %s, result %x, expected %x (D3DRTYPE_INDEXBUFFER)\n",
3468            name, index_buffer_description.Type, D3DRTYPE_INDEXBUFFER);
3469         todo_wine ok(index_buffer_description.Usage == 0, "Test %s, result %x, expected %x\n", name, index_buffer_description.Usage, 0);
3470         ok(index_buffer_description.Pool == D3DPOOL_MANAGED, "Test %s, result %x, expected %x (D3DPOOL_MANAGED)\n",
3471            name, index_buffer_description.Pool, D3DPOOL_MANAGED);
3472         expected = number_of_faces * sizeof(WORD) * 3;
3473         ok(index_buffer_description.Size == expected, "Test %s, result %x, expected %x\n",
3474            name, index_buffer_description.Size, expected);
3475     }
3476
3477     /* specify offset and size to avoid potential overruns */
3478     hr = IDirect3DVertexBuffer9_Lock(vertex_buffer, 0, number_of_vertices * sizeof(D3DXVECTOR3) * 2,
3479                                      (LPVOID *)&vertices, D3DLOCK_DISCARD);
3480     ok(hr == D3D_OK, "Test %s, result %x, expected 0 (D3D_OK)\n", name, hr);
3481     if (hr != D3D_OK)
3482     {
3483         skip("Couldn't lock vertex buffer\n");
3484         goto error;
3485     }
3486     hr = IDirect3DIndexBuffer9_Lock(index_buffer, 0, number_of_faces * sizeof(WORD) * 3,
3487                                     (LPVOID *)&faces, D3DLOCK_DISCARD);
3488     ok(hr == D3D_OK, "Test %s, result %x, expected 0 (D3D_OK)\n", name, hr);
3489     if (hr != D3D_OK)
3490     {
3491         skip("Couldn't lock index buffer\n");
3492         goto error;
3493     }
3494
3495     face_idx1 = 0;
3496     vtx_idx2 = 0;
3497     face_idx2 = 0;
3498     vtx_idx1 = 0;
3499     for (i = 0; i < textlen; i++)
3500     {
3501         int nb_outline_vertices1, nb_outline_faces1;
3502         int nb_outline_vertices2, nb_outline_faces2;
3503         int nb_back_vertices, nb_back_faces;
3504         int first_vtx1, first_vtx2;
3505         int first_face1, first_face2;
3506         int j;
3507
3508         first_vtx1 = vtx_idx1;
3509         first_vtx2 = vtx_idx2;
3510         for (; vtx_idx1 < number_of_vertices; vtx_idx1++) {
3511             if (vertices[vtx_idx1].normal.z != 0)
3512                 break;
3513         }
3514         for (; vtx_idx2 < mesh->number_of_vertices; vtx_idx2++) {
3515             if (mesh->vertices[vtx_idx2].normal.z != 0)
3516                 break;
3517         }
3518         nb_outline_vertices1 = vtx_idx1 - first_vtx1;
3519         nb_outline_vertices2 = vtx_idx2 - first_vtx2;
3520         ok(nb_outline_vertices1 == nb_outline_vertices2,
3521            "Test %s, glyph %d, outline vertex count result %d, expected %d\n", name, i,
3522            nb_outline_vertices1, nb_outline_vertices2);
3523
3524         for (j = 0; j < min(nb_outline_vertices1, nb_outline_vertices2); j++)
3525         {
3526             vtx_idx1 = first_vtx1 + j;
3527             vtx_idx2 = first_vtx2 + j;
3528             ok(compare_vec3(vertices[vtx_idx1].position, mesh->vertices[vtx_idx2].position),
3529                "Test %s, glyph %d, vertex position %d, result (%g, %g, %g), expected (%g, %g, %g)\n", name, i, vtx_idx1,
3530                vertices[vtx_idx1].position.x, vertices[vtx_idx1].position.y, vertices[vtx_idx1].position.z,
3531                mesh->vertices[vtx_idx2].position.x, mesh->vertices[vtx_idx2].position.y, mesh->vertices[vtx_idx2].position.z);
3532             ok(compare_vec3(vertices[vtx_idx1].normal, mesh->vertices[first_vtx2 + j].normal),
3533                "Test %s, glyph %d, vertex normal %d, result (%g, %g, %g), expected (%g, %g, %g)\n", name, i, vtx_idx1,
3534                vertices[vtx_idx1].normal.x, vertices[vtx_idx1].normal.y, vertices[vtx_idx1].normal.z,
3535                mesh->vertices[vtx_idx2].normal.x, mesh->vertices[vtx_idx2].normal.y, mesh->vertices[vtx_idx2].normal.z);
3536         }
3537         vtx_idx1 = first_vtx1 + nb_outline_vertices1;
3538         vtx_idx2 = first_vtx2 + nb_outline_vertices2;
3539
3540         first_face1 = face_idx1;
3541         first_face2 = face_idx2;
3542         for (; face_idx1 < number_of_faces; face_idx1++)
3543         {
3544             if (faces[face_idx1][0] >= vtx_idx1 ||
3545                 faces[face_idx1][1] >= vtx_idx1 ||
3546                 faces[face_idx1][2] >= vtx_idx1)
3547                 break;
3548         }
3549         for (; face_idx2 < mesh->number_of_faces; face_idx2++)
3550         {
3551             if (mesh->faces[face_idx2][0] >= vtx_idx2 ||
3552                 mesh->faces[face_idx2][1] >= vtx_idx2 ||
3553                 mesh->faces[face_idx2][2] >= vtx_idx2)
3554                 break;
3555         }
3556         nb_outline_faces1 = face_idx1 - first_face1;
3557         nb_outline_faces2 = face_idx2 - first_face2;
3558         ok(nb_outline_faces1 == nb_outline_faces2,
3559            "Test %s, glyph %d, outline face count result %d, expected %d\n", name, i,
3560            nb_outline_faces1, nb_outline_faces2);
3561
3562         for (j = 0; j < min(nb_outline_faces1, nb_outline_faces2); j++)
3563         {
3564             face_idx1 = first_face1 + j;
3565             face_idx2 = first_face2 + j;
3566             ok(faces[face_idx1][0] - first_vtx1 == mesh->faces[face_idx2][0] - first_vtx2 &&
3567                faces[face_idx1][1] - first_vtx1 == mesh->faces[face_idx2][1] - first_vtx2 &&
3568                faces[face_idx1][2] - first_vtx1 == mesh->faces[face_idx2][2] - first_vtx2,
3569                "Test %s, glyph %d, face %d, result (%d, %d, %d), expected (%d, %d, %d)\n", name, i, face_idx1,
3570                faces[face_idx1][0], faces[face_idx1][1], faces[face_idx1][2],
3571                mesh->faces[face_idx2][0] - first_vtx2 + first_vtx1,
3572                mesh->faces[face_idx2][1] - first_vtx2 + first_vtx1,
3573                mesh->faces[face_idx2][2] - first_vtx2 + first_vtx1);
3574         }
3575         face_idx1 = first_face1 + nb_outline_faces1;
3576         face_idx2 = first_face2 + nb_outline_faces2;
3577
3578         /* partial test on back vertices and faces  */
3579         first_vtx1 = vtx_idx1;
3580         for (; vtx_idx1 < number_of_vertices; vtx_idx1++) {
3581             struct vertex vtx;
3582
3583             if (vertices[vtx_idx1].normal.z != 1.0f)
3584                 break;
3585
3586             vtx.position.z = 0.0f;
3587             vtx.normal.x = 0.0f;
3588             vtx.normal.y = 0.0f;
3589             vtx.normal.z = 1.0f;
3590             ok(compare(vertices[vtx_idx1].position.z, vtx.position.z),
3591                "Test %s, glyph %d, vertex position.z %d, result %g, expected %g\n", name, i, vtx_idx1,
3592                vertices[vtx_idx1].position.z, vtx.position.z);
3593             ok(compare_vec3(vertices[vtx_idx1].normal, vtx.normal),
3594                "Test %s, glyph %d, vertex normal %d, result (%g, %g, %g), expected (%g, %g, %g)\n", name, i, vtx_idx1,
3595                vertices[vtx_idx1].normal.x, vertices[vtx_idx1].normal.y, vertices[vtx_idx1].normal.z,
3596                vtx.normal.x, vtx.normal.y, vtx.normal.z);
3597         }
3598         nb_back_vertices = vtx_idx1 - first_vtx1;
3599         first_face1 = face_idx1;
3600         for (; face_idx1 < number_of_faces; face_idx1++)
3601         {
3602             const D3DXVECTOR3 *vtx1, *vtx2, *vtx3;
3603             D3DXVECTOR3 normal;
3604             D3DXVECTOR3 v1 = {0, 0, 0};
3605             D3DXVECTOR3 v2 = {0, 0, 0};
3606             D3DXVECTOR3 forward = {0.0f, 0.0f, 1.0f};
3607
3608             if (faces[face_idx1][0] >= vtx_idx1 ||
3609                 faces[face_idx1][1] >= vtx_idx1 ||
3610                 faces[face_idx1][2] >= vtx_idx1)
3611                 break;
3612
3613             vtx1 = &vertices[faces[face_idx1][0]].position;
3614             vtx2 = &vertices[faces[face_idx1][1]].position;
3615             vtx3 = &vertices[faces[face_idx1][2]].position;
3616
3617             D3DXVec3Subtract(&v1, vtx2, vtx1);
3618             D3DXVec3Subtract(&v2, vtx3, vtx2);
3619             D3DXVec3Cross(&normal, &v1, &v2);
3620             D3DXVec3Normalize(&normal, &normal);
3621             ok(!D3DXVec3Length(&normal) || compare_vec3(normal, forward),
3622                "Test %s, glyph %d, face %d normal, result (%g, %g, %g), expected (%g, %g, %g)\n", name, i, face_idx1,
3623                normal.x, normal.y, normal.z, forward.x, forward.y, forward.z);
3624         }
3625         nb_back_faces = face_idx1 - first_face1;
3626
3627         /* compare front and back faces & vertices */
3628         if (extrusion == 0.0f) {
3629             /* Oddly there are only back faces in this case */
3630             nb_back_vertices /= 2;
3631             nb_back_faces /= 2;
3632             face_idx1 -= nb_back_faces;
3633             vtx_idx1 -= nb_back_vertices;
3634         }
3635         for (j = 0; j < nb_back_vertices; j++)
3636         {
3637             struct vertex vtx = vertices[first_vtx1];
3638             vtx.position.z = -extrusion;
3639             vtx.normal.x = 0.0f;
3640             vtx.normal.y = 0.0f;
3641             vtx.normal.z = extrusion == 0.0f ? 1.0f : -1.0f;
3642             ok(compare_vec3(vertices[vtx_idx1].position, vtx.position),
3643                "Test %s, glyph %d, vertex position %d, result (%g, %g, %g), expected (%g, %g, %g)\n", name, i, vtx_idx1,
3644                vertices[vtx_idx1].position.x, vertices[vtx_idx1].position.y, vertices[vtx_idx1].position.z,
3645                vtx.position.x, vtx.position.y, vtx.position.z);
3646             ok(compare_vec3(vertices[vtx_idx1].normal, vtx.normal),
3647                "Test %s, glyph %d, vertex normal %d, result (%g, %g, %g), expected (%g, %g, %g)\n", name, i, vtx_idx1,
3648                vertices[vtx_idx1].normal.x, vertices[vtx_idx1].normal.y, vertices[vtx_idx1].normal.z,
3649                vtx.normal.x, vtx.normal.y, vtx.normal.z);
3650             vtx_idx1++;
3651             first_vtx1++;
3652         }
3653         for (j = 0; j < nb_back_faces; j++)
3654         {
3655             int f1, f2;
3656             if (extrusion == 0.0f) {
3657                 f1 = 1;
3658                 f2 = 2;
3659             } else {
3660                 f1 = 2;
3661                 f2 = 1;
3662             }
3663             ok(faces[face_idx1][0] == faces[first_face1][0] + nb_back_vertices &&
3664                faces[face_idx1][1] == faces[first_face1][f1] + nb_back_vertices &&
3665                faces[face_idx1][2] == faces[first_face1][f2] + nb_back_vertices,
3666                "Test %s, glyph %d, face %d, result (%d, %d, %d), expected (%d, %d, %d)\n", name, i, face_idx1,
3667                faces[face_idx1][0], faces[face_idx1][1], faces[face_idx1][2],
3668                faces[first_face1][0] - nb_back_faces,
3669                faces[first_face1][f1] - nb_back_faces,
3670                faces[first_face1][f2] - nb_back_faces);
3671             first_face1++;
3672             face_idx1++;
3673         }
3674
3675         /* skip to the outline for the next glyph */
3676         for (; vtx_idx2 < mesh->number_of_vertices; vtx_idx2++) {
3677             if (mesh->vertices[vtx_idx2].normal.z == 0)
3678                 break;
3679         }
3680         for (; face_idx2 < mesh->number_of_faces; face_idx2++)
3681         {
3682             if (mesh->faces[face_idx2][0] >= vtx_idx2 ||
3683                 mesh->faces[face_idx2][1] >= vtx_idx2 ||
3684                 mesh->faces[face_idx2][2] >= vtx_idx2) break;
3685         }
3686     }
3687
3688 error:
3689     if (vertices) IDirect3DVertexBuffer9_Unlock(vertex_buffer);
3690     if (faces) IDirect3DIndexBuffer9_Unlock(index_buffer);
3691     if (index_buffer) IDirect3DIndexBuffer9_Release(index_buffer);
3692     if (vertex_buffer) IDirect3DVertexBuffer9_Release(vertex_buffer);
3693 }
3694
3695 static void test_createtext(IDirect3DDevice9 *device, HDC hdc, LPCSTR text, FLOAT deviation, FLOAT extrusion)
3696 {
3697     HRESULT hr;
3698     ID3DXMesh *d3dxmesh;
3699     struct mesh mesh;
3700     char name[256];
3701     OUTLINETEXTMETRIC otm;
3702     GLYPHMETRICS gm;
3703     GLYPHMETRICSFLOAT *glyphmetrics_float = HeapAlloc(GetProcessHeap(), 0, sizeof(GLYPHMETRICSFLOAT) * strlen(text));
3704     int i;
3705     LOGFONT lf;
3706     HFONT font = NULL, oldfont = NULL;
3707
3708     sprintf(name, "text ('%s', %f, %f)", text, deviation, extrusion);
3709
3710     hr = D3DXCreateText(device, hdc, text, deviation, extrusion, &d3dxmesh, NULL, glyphmetrics_float);
3711     ok(hr == D3D_OK, "Got result %x, expected 0 (D3D_OK)\n", hr);
3712     if (hr != D3D_OK)
3713     {
3714         skip("Couldn't create text with D3DXCreateText\n");
3715         return;
3716     }
3717
3718     /* must select a modified font having lfHeight = otm.otmEMSquare before
3719      * calling GetGlyphOutline to get the expected values */
3720     if (!GetObject(GetCurrentObject(hdc, OBJ_FONT), sizeof(lf), &lf) ||
3721         !GetOutlineTextMetrics(hdc, sizeof(otm), &otm))
3722     {
3723         d3dxmesh->lpVtbl->Release(d3dxmesh);
3724         skip("Couldn't get text outline\n");
3725         return;
3726     }
3727     lf.lfHeight = otm.otmEMSquare;
3728     lf.lfWidth = 0;
3729     font = CreateFontIndirect(&lf);
3730     if (!font) {
3731         d3dxmesh->lpVtbl->Release(d3dxmesh);
3732         skip("Couldn't create the modified font\n");
3733         return;
3734     }
3735     oldfont = SelectObject(hdc, font);
3736
3737     for (i = 0; i < strlen(text); i++)
3738     {
3739         const MAT2 identity = {{0, 1}, {0, 0}, {0, 0}, {0, 1}};
3740         GetGlyphOutlineA(hdc, text[i], GGO_NATIVE, &gm, 0, NULL, &identity);
3741         compare_float(glyphmetrics_float[i].gmfBlackBoxX, gm.gmBlackBoxX / (float)otm.otmEMSquare);
3742         compare_float(glyphmetrics_float[i].gmfBlackBoxY, gm.gmBlackBoxY / (float)otm.otmEMSquare);
3743         compare_float(glyphmetrics_float[i].gmfptGlyphOrigin.x, gm.gmptGlyphOrigin.x / (float)otm.otmEMSquare);
3744         compare_float(glyphmetrics_float[i].gmfptGlyphOrigin.y, gm.gmptGlyphOrigin.y / (float)otm.otmEMSquare);
3745         compare_float(glyphmetrics_float[i].gmfCellIncX, gm.gmCellIncX / (float)otm.otmEMSquare);
3746         compare_float(glyphmetrics_float[i].gmfCellIncY, gm.gmCellIncY / (float)otm.otmEMSquare);
3747     }
3748
3749     ZeroMemory(&mesh, sizeof(mesh));
3750     if (!compute_text_mesh(&mesh, hdc, text, deviation, extrusion, otm.otmEMSquare))
3751     {
3752         skip("Couldn't create mesh\n");
3753         d3dxmesh->lpVtbl->Release(d3dxmesh);
3754         return;
3755     }
3756     mesh.fvf = D3DFVF_XYZ | D3DFVF_NORMAL;
3757
3758     compare_text_outline_mesh(name, d3dxmesh, &mesh, strlen(text), extrusion);
3759
3760     free_mesh(&mesh);
3761
3762     d3dxmesh->lpVtbl->Release(d3dxmesh);
3763     SelectObject(hdc, oldfont);
3764     HeapFree(GetProcessHeap(), 0, glyphmetrics_float);
3765 }
3766
3767 static void D3DXCreateTextTest(void)
3768 {
3769     HRESULT hr;
3770     HWND wnd;
3771     HDC hdc;
3772     IDirect3D9* d3d;
3773     IDirect3DDevice9* device;
3774     D3DPRESENT_PARAMETERS d3dpp;
3775     ID3DXMesh* d3dxmesh = NULL;
3776     HFONT hFont;
3777     OUTLINETEXTMETRIC otm;
3778     int number_of_vertices;
3779     int number_of_faces;
3780
3781     wnd = CreateWindow("static", "d3dx9_test", WS_POPUP, 0, 0, 1000, 1000, NULL, NULL, NULL, NULL);
3782     d3d = Direct3DCreate9(D3D_SDK_VERSION);
3783     if (!wnd)
3784     {
3785         skip("Couldn't create application window\n");
3786         return;
3787     }
3788     if (!d3d)
3789     {
3790         skip("Couldn't create IDirect3D9 object\n");
3791         DestroyWindow(wnd);
3792         return;
3793     }
3794
3795     ZeroMemory(&d3dpp, sizeof(d3dpp));
3796     d3dpp.Windowed = TRUE;
3797     d3dpp.SwapEffect = D3DSWAPEFFECT_DISCARD;
3798     hr = IDirect3D9_CreateDevice(d3d, D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, wnd, D3DCREATE_MIXED_VERTEXPROCESSING, &d3dpp, &device);
3799     if (FAILED(hr))
3800     {
3801         skip("Failed to create IDirect3DDevice9 object %#x\n", hr);
3802         IDirect3D9_Release(d3d);
3803         DestroyWindow(wnd);
3804         return;
3805     }
3806
3807     hdc = CreateCompatibleDC(NULL);
3808
3809     hFont = CreateFont(12, 0, 0, 0, FW_NORMAL, FALSE, FALSE, FALSE, DEFAULT_CHARSET,
3810                        OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, DEFAULT_PITCH | FF_DONTCARE,
3811                        "Arial");
3812     SelectObject(hdc, hFont);
3813     GetOutlineTextMetrics(hdc, sizeof(otm), &otm);
3814
3815     hr = D3DXCreateText(device, hdc, "wine", 0.001f, 0.4f, NULL, NULL, NULL);
3816     ok(hr == D3DERR_INVALIDCALL, "Got result %x, expected %x (D3DERR_INVALIDCALL)\n", hr, D3DERR_INVALIDCALL);
3817
3818     /* D3DXCreateTextA page faults from passing NULL text */
3819
3820     hr = D3DXCreateTextW(device, hdc, NULL, 0.001f, 0.4f, &d3dxmesh, NULL, NULL);
3821     ok(hr == D3DERR_INVALIDCALL, "Got result %x, expected %x (D3DERR_INVALIDCALL)\n", hr, D3DERR_INVALIDCALL);
3822
3823     hr = D3DXCreateText(device, hdc, "", 0.001f, 0.4f, &d3dxmesh, NULL, NULL);
3824     ok(hr == D3DERR_INVALIDCALL, "Got result %x, expected %x (D3DERR_INVALIDCALL)\n", hr, D3DERR_INVALIDCALL);
3825
3826     hr = D3DXCreateText(device, hdc, " ", 0.001f, 0.4f, &d3dxmesh, NULL, NULL);
3827     ok(hr == D3DERR_INVALIDCALL, "Got result %x, expected %x (D3DERR_INVALIDCALL)\n", hr, D3DERR_INVALIDCALL);
3828
3829     hr = D3DXCreateText(NULL, hdc, "wine", 0.001f, 0.4f, &d3dxmesh, NULL, NULL);
3830     ok(hr == D3DERR_INVALIDCALL, "Got result %x, expected %x (D3DERR_INVALIDCALL)\n", hr, D3DERR_INVALIDCALL);
3831
3832     hr = D3DXCreateText(device, NULL, "wine", 0.001f, 0.4f, &d3dxmesh, NULL, NULL);
3833     ok(hr == D3DERR_INVALIDCALL, "Got result %x, expected %x (D3DERR_INVALIDCALL)\n", hr, D3DERR_INVALIDCALL);
3834
3835     hr = D3DXCreateText(device, hdc, "wine", -FLT_MIN, 0.4f, &d3dxmesh, NULL, NULL);
3836     ok(hr == D3DERR_INVALIDCALL, "Got result %x, expected %x (D3DERR_INVALIDCALL)\n", hr, D3DERR_INVALIDCALL);
3837
3838     hr = D3DXCreateText(device, hdc, "wine", 0.001f, -FLT_MIN, &d3dxmesh, NULL, NULL);
3839     ok(hr == D3DERR_INVALIDCALL, "Got result %x, expected %x (D3DERR_INVALIDCALL)\n", hr, D3DERR_INVALIDCALL);
3840
3841     /* deviation = 0.0f treated as if deviation = 1.0f / otm.otmEMSquare */
3842     hr = D3DXCreateText(device, hdc, "wine", 1.0f / otm.otmEMSquare, 0.4f, &d3dxmesh, NULL, NULL);
3843     ok(hr == D3D_OK, "Got result %x, expected %x (D3D_OK)\n", hr, D3D_OK);
3844     number_of_vertices = d3dxmesh->lpVtbl->GetNumVertices(d3dxmesh);
3845     number_of_faces = d3dxmesh->lpVtbl->GetNumFaces(d3dxmesh);
3846     if (SUCCEEDED(hr) && d3dxmesh) d3dxmesh->lpVtbl->Release(d3dxmesh);
3847
3848     hr = D3DXCreateText(device, hdc, "wine", 0.0f, 0.4f, &d3dxmesh, NULL, NULL);
3849     ok(hr == D3D_OK, "Got result %x, expected %x (D3D_OK)\n", hr, D3D_OK);
3850     ok(number_of_vertices == d3dxmesh->lpVtbl->GetNumVertices(d3dxmesh),
3851        "Got %d vertices, expected %d\n",
3852        d3dxmesh->lpVtbl->GetNumVertices(d3dxmesh), number_of_vertices);
3853     ok(number_of_faces == d3dxmesh->lpVtbl->GetNumFaces(d3dxmesh),
3854        "Got %d faces, expected %d\n",
3855        d3dxmesh->lpVtbl->GetNumVertices(d3dxmesh), number_of_faces);
3856     if (SUCCEEDED(hr) && d3dxmesh) d3dxmesh->lpVtbl->Release(d3dxmesh);
3857
3858 #if 0
3859     /* too much detail requested, so will appear to hang */
3860     trace("Waiting for D3DXCreateText to finish with deviation = FLT_MIN ...\n");
3861     hr = D3DXCreateText(device, hdc, "wine", FLT_MIN, 0.4f, &d3dxmesh, NULL, NULL);
3862     ok(hr == D3D_OK, "Got result %x, expected %x (D3D_OK)\n", hr, D3D_OK);
3863     if (SUCCEEDED(hr) && d3dxmesh) d3dxmesh->lpVtbl->Release(d3dxmesh);
3864     trace("D3DXCreateText finish with deviation = FLT_MIN\n");
3865 #endif
3866
3867     hr = D3DXCreateText(device, hdc, "wine", 0.001f, 0.4f, &d3dxmesh, NULL, NULL);
3868     ok(hr == D3D_OK, "Got result %x, expected %x (D3D_OK)\n", hr, D3D_OK);
3869     if (SUCCEEDED(hr) && d3dxmesh) d3dxmesh->lpVtbl->Release(d3dxmesh);
3870
3871     test_createtext(device, hdc, "wine", FLT_MAX, 0.4f);
3872     test_createtext(device, hdc, "wine", 0.001f, FLT_MIN);
3873     test_createtext(device, hdc, "wine", 0.001f, 0.0f);
3874     test_createtext(device, hdc, "wine", 0.001f, FLT_MAX);
3875     test_createtext(device, hdc, "wine", 0.0f, 1.0f);
3876
3877     DeleteDC(hdc);
3878
3879     IDirect3DDevice9_Release(device);
3880     IDirect3D9_Release(d3d);
3881     DestroyWindow(wnd);
3882 }
3883
3884 static void test_get_decl_length(void)
3885 {
3886     static const D3DVERTEXELEMENT9 declaration1[] =
3887     {
3888         {0, 0, D3DDECLTYPE_FLOAT1, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_POSITION, 0},
3889         {1, 0, D3DDECLTYPE_FLOAT2, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_POSITION, 0},
3890         {2, 0, D3DDECLTYPE_FLOAT3, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_POSITION, 0},
3891         {3, 0, D3DDECLTYPE_FLOAT4, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_POSITION, 0},
3892         {4, 0, D3DDECLTYPE_D3DCOLOR, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_POSITION, 0},
3893         {5, 0, D3DDECLTYPE_UBYTE4, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_POSITION, 0},
3894         {6, 0, D3DDECLTYPE_SHORT2, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_POSITION, 0},
3895         {7, 0, D3DDECLTYPE_SHORT4, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_POSITION, 0},
3896         {8, 0, D3DDECLTYPE_UBYTE4N, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_POSITION, 0},
3897         {9, 0, D3DDECLTYPE_SHORT2N, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_POSITION, 0},
3898         {10, 0, D3DDECLTYPE_SHORT4N, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_POSITION, 0},
3899         {11, 0, D3DDECLTYPE_UDEC3, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_POSITION, 0},
3900         {12, 0, D3DDECLTYPE_DEC3N, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_POSITION, 0},
3901         {13, 0, D3DDECLTYPE_FLOAT16_2, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_POSITION, 0},
3902         {14, 0, D3DDECLTYPE_FLOAT16_4, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_POSITION, 0},
3903         D3DDECL_END(),
3904     };
3905     static const D3DVERTEXELEMENT9 declaration2[] =
3906     {
3907         {0, 8, D3DDECLTYPE_FLOAT1, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_POSITION, 0},
3908         {1, 8, D3DDECLTYPE_FLOAT2, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_POSITION, 0},
3909         {2, 8, D3DDECLTYPE_FLOAT3, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_POSITION, 0},
3910         {3, 8, D3DDECLTYPE_FLOAT4, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_POSITION, 0},
3911         {4, 8, D3DDECLTYPE_D3DCOLOR, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_POSITION, 0},
3912         {5, 8, D3DDECLTYPE_UBYTE4, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_POSITION, 0},
3913         {6, 8, D3DDECLTYPE_SHORT2, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_POSITION, 0},
3914         {7, 8, D3DDECLTYPE_SHORT4, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_POSITION, 0},
3915         {0, 8, D3DDECLTYPE_UBYTE4N, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_POSITION, 0},
3916         {1, 8, D3DDECLTYPE_SHORT2N, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_POSITION, 0},
3917         {2, 8, D3DDECLTYPE_SHORT4N, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_POSITION, 0},
3918         {3, 8, D3DDECLTYPE_UDEC3, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_POSITION, 0},
3919         {4, 8, D3DDECLTYPE_DEC3N, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_POSITION, 0},
3920         {5, 8, D3DDECLTYPE_FLOAT16_2, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_POSITION, 0},
3921         {6, 8, D3DDECLTYPE_FLOAT16_4, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_POSITION, 0},
3922         {7, 8, D3DDECLTYPE_FLOAT1, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_POSITION, 0},
3923         D3DDECL_END(),
3924     };
3925     UINT size;
3926
3927     size = D3DXGetDeclLength(declaration1);
3928     ok(size == 15, "Got size %u, expected 15.\n", size);
3929
3930     size = D3DXGetDeclLength(declaration2);
3931     ok(size == 16, "Got size %u, expected 16.\n", size);
3932 }
3933
3934 static void test_get_decl_vertex_size(void)
3935 {
3936     static const D3DVERTEXELEMENT9 declaration1[] =
3937     {
3938         {0, 0, D3DDECLTYPE_FLOAT1, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_POSITION, 0},
3939         {1, 0, D3DDECLTYPE_FLOAT2, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_POSITION, 0},
3940         {2, 0, D3DDECLTYPE_FLOAT3, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_POSITION, 0},
3941         {3, 0, D3DDECLTYPE_FLOAT4, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_POSITION, 0},
3942         {4, 0, D3DDECLTYPE_D3DCOLOR, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_POSITION, 0},
3943         {5, 0, D3DDECLTYPE_UBYTE4, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_POSITION, 0},
3944         {6, 0, D3DDECLTYPE_SHORT2, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_POSITION, 0},
3945         {7, 0, D3DDECLTYPE_SHORT4, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_POSITION, 0},
3946         {8, 0, D3DDECLTYPE_UBYTE4N, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_POSITION, 0},
3947         {9, 0, D3DDECLTYPE_SHORT2N, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_POSITION, 0},
3948         {10, 0, D3DDECLTYPE_SHORT4N, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_POSITION, 0},
3949         {11, 0, D3DDECLTYPE_UDEC3, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_POSITION, 0},
3950         {12, 0, D3DDECLTYPE_DEC3N, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_POSITION, 0},
3951         {13, 0, D3DDECLTYPE_FLOAT16_2, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_POSITION, 0},
3952         {14, 0, D3DDECLTYPE_FLOAT16_4, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_POSITION, 0},
3953         D3DDECL_END(),
3954     };
3955     static const D3DVERTEXELEMENT9 declaration2[] =
3956     {
3957         {0, 8, D3DDECLTYPE_FLOAT1, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_POSITION, 0},
3958         {1, 8, D3DDECLTYPE_FLOAT2, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_POSITION, 0},
3959         {2, 8, D3DDECLTYPE_FLOAT3, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_POSITION, 0},
3960         {3, 8, D3DDECLTYPE_FLOAT4, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_POSITION, 0},
3961         {4, 8, D3DDECLTYPE_D3DCOLOR, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_POSITION, 0},
3962         {5, 8, D3DDECLTYPE_UBYTE4, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_POSITION, 0},
3963         {6, 8, D3DDECLTYPE_SHORT2, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_POSITION, 0},
3964         {7, 8, D3DDECLTYPE_SHORT4, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_POSITION, 0},
3965         {0, 8, D3DDECLTYPE_UBYTE4N, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_POSITION, 0},
3966         {1, 8, D3DDECLTYPE_SHORT2N, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_POSITION, 0},
3967         {2, 8, D3DDECLTYPE_SHORT4N, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_POSITION, 0},
3968         {3, 8, D3DDECLTYPE_UDEC3, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_POSITION, 0},
3969         {4, 8, D3DDECLTYPE_DEC3N, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_POSITION, 0},
3970         {5, 8, D3DDECLTYPE_FLOAT16_2, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_POSITION, 0},
3971         {6, 8, D3DDECLTYPE_FLOAT16_4, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_POSITION, 0},
3972         {7, 8, D3DDECLTYPE_FLOAT1, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_POSITION, 0},
3973         D3DDECL_END(),
3974     };
3975     static const UINT sizes1[] =
3976     {
3977         4,  8,  12, 16,
3978         4,  4,  4,  8,
3979         4,  4,  8,  4,
3980         4,  4,  8,  0,
3981     };
3982     static const UINT sizes2[] =
3983     {
3984         12, 16, 20, 24,
3985         12, 12, 16, 16,
3986     };
3987     unsigned int i;
3988     UINT size;
3989
3990     size = D3DXGetDeclVertexSize(NULL, 0);
3991     ok(size == 0, "Got size %#x, expected 0.\n", size);
3992
3993     for (i = 0; i < 16; ++i)
3994     {
3995         size = D3DXGetDeclVertexSize(declaration1, i);
3996         ok(size == sizes1[i], "Got size %u for stream %u, expected %u.\n", size, i, sizes1[i]);
3997     }
3998
3999     for (i = 0; i < 8; ++i)
4000     {
4001         size = D3DXGetDeclVertexSize(declaration2, i);
4002         ok(size == sizes2[i], "Got size %u for stream %u, expected %u.\n", size, i, sizes2[i]);
4003     }
4004 }
4005
4006 static void D3DXGenerateAdjacencyTest(void)
4007 {
4008     HRESULT hr;
4009     HWND wnd;
4010     IDirect3D9 *d3d;
4011     IDirect3DDevice9 *device;
4012     D3DPRESENT_PARAMETERS d3dpp;
4013     ID3DXMesh *d3dxmesh = NULL;
4014     D3DXVECTOR3 *vertices = NULL;
4015     WORD *indices = NULL;
4016     int i;
4017     struct {
4018         DWORD num_vertices;
4019         D3DXVECTOR3 vertices[6];
4020         DWORD num_faces;
4021         WORD indices[3 * 3];
4022         FLOAT epsilon;
4023         DWORD adjacency[3 * 3];
4024     } test_data[] = {
4025         { /* for epsilon < 0, indices must match for faces to be adjacent */
4026             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}},
4027             2, {0, 1, 2,  0, 2, 3},
4028             -1.0,
4029             {-1, -1, 1,  0, -1, -1},
4030         },
4031         {
4032             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}},
4033             2, {0, 1, 2,  3, 4, 5},
4034             -1.0,
4035             {-1, -1, -1,  -1, -1, -1},
4036         },
4037         { /* for epsilon == 0, indices or vertices must match for faces to be adjacent */
4038             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}},
4039             2, {0, 1, 2,  3, 4, 5},
4040             0.0,
4041             {-1, -1, 1,  0, -1, -1},
4042         },
4043         { /* for epsilon > 0, vertices must be less than (but NOT equal to) epsilon distance away */
4044             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}},
4045             2, {0, 1, 2,  3, 4, 5},
4046             0.25,
4047             {-1, -1, -1,  -1, -1, -1},
4048         },
4049         { /* for epsilon > 0, vertices must be less than (but NOT equal to) epsilon distance away */
4050             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}},
4051             2, {0, 1, 2,  3, 4, 5},
4052             0.250001,
4053             {-1, -1, 1,  0, -1, -1},
4054         },
4055         { /* length between vertices are compared to epsilon, not the individual dimension deltas */
4056             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}},
4057             2, {0, 1, 2,  3, 4, 5},
4058             0.353, /* < sqrt(0.25*0.25 + 0.25*0.25) */
4059             {-1, -1, -1,  -1, -1, -1},
4060         },
4061         {
4062             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}},
4063             2, {0, 1, 2,  3, 4, 5},
4064             0.354, /* > sqrt(0.25*0.25 + 0.25*0.25) */
4065             {-1, -1, 1,  0, -1, -1},
4066         },
4067         { /* adjacent faces must have opposite winding orders at the shared edge */
4068             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}},
4069             2, {0, 1, 2,  0, 3, 2},
4070             0.0,
4071             {-1, -1, -1,  -1, -1, -1},
4072         },
4073     };
4074
4075     wnd = CreateWindow("static", "d3dx9_test", 0, 0, 0, 0, 0, NULL, NULL, NULL, NULL);
4076     if (!wnd)
4077     {
4078         skip("Couldn't create application window\n");
4079         return;
4080     }
4081     d3d = Direct3DCreate9(D3D_SDK_VERSION);
4082     if (!d3d)
4083     {
4084         skip("Couldn't create IDirect3D9 object\n");
4085         DestroyWindow(wnd);
4086         return;
4087     }
4088
4089     ZeroMemory(&d3dpp, sizeof(d3dpp));
4090     d3dpp.Windowed = TRUE;
4091     d3dpp.SwapEffect = D3DSWAPEFFECT_DISCARD;
4092     hr = IDirect3D9_CreateDevice(d3d, D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, wnd, D3DCREATE_MIXED_VERTEXPROCESSING, &d3dpp, &device);
4093     if (FAILED(hr))
4094     {
4095         skip("Failed to create IDirect3DDevice9 object %#x\n", hr);
4096         IDirect3D9_Release(d3d);
4097         DestroyWindow(wnd);
4098         return;
4099     }
4100
4101     for (i = 0; i < ARRAY_SIZE(test_data); i++)
4102     {
4103         DWORD adjacency[ARRAY_SIZE(test_data[0].adjacency)];
4104         int j;
4105
4106         if (d3dxmesh) d3dxmesh->lpVtbl->Release(d3dxmesh);
4107         d3dxmesh = NULL;
4108
4109         hr = D3DXCreateMeshFVF(test_data[i].num_faces, test_data[i].num_vertices, 0, D3DFVF_XYZ, device, &d3dxmesh);
4110         ok(hr == D3D_OK, "Got result %x, expected %x (D3D_OK)\n", hr, D3D_OK);
4111
4112         hr = d3dxmesh->lpVtbl->LockVertexBuffer(d3dxmesh, D3DLOCK_DISCARD, (void**)&vertices);
4113         ok(hr == D3D_OK, "test %d: Got result %x, expected %x (D3D_OK)\n", i, hr, D3D_OK);
4114         if (FAILED(hr)) continue;
4115         CopyMemory(vertices, test_data[i].vertices, test_data[i].num_vertices * sizeof(test_data[0].vertices[0]));
4116         d3dxmesh->lpVtbl->UnlockVertexBuffer(d3dxmesh);
4117
4118         hr = d3dxmesh->lpVtbl->LockIndexBuffer(d3dxmesh, D3DLOCK_DISCARD, (void**)&indices);
4119         ok(hr == D3D_OK, "test %d: Got result %x, expected %x (D3D_OK)\n", i, hr, D3D_OK);
4120         if (FAILED(hr)) continue;
4121         CopyMemory(indices, test_data[i].indices, test_data[i].num_faces * 3 * sizeof(test_data[0].indices[0]));
4122         d3dxmesh->lpVtbl->UnlockIndexBuffer(d3dxmesh);
4123
4124         if (i == 0) {
4125             hr = d3dxmesh->lpVtbl->GenerateAdjacency(d3dxmesh, 0.0f, NULL);
4126             ok(hr == D3DERR_INVALIDCALL, "Got result %x, expected %x (D3DERR_INVALIDCALL)\n", hr, D3DERR_INVALIDCALL);
4127         }
4128
4129         hr = d3dxmesh->lpVtbl->GenerateAdjacency(d3dxmesh, test_data[i].epsilon, adjacency);
4130         ok(hr == D3D_OK, "Got result %x, expected %x (D3D_OK)\n", hr, D3D_OK);
4131         if (FAILED(hr)) continue;
4132
4133         for (j = 0; j < test_data[i].num_faces * 3; j++)
4134             ok(adjacency[j] == test_data[i].adjacency[j],
4135                "Test %d adjacency %d: Got result %u, expected %u\n", i, j,
4136                adjacency[j], test_data[i].adjacency[j]);
4137     }
4138     if (d3dxmesh) d3dxmesh->lpVtbl->Release(d3dxmesh);
4139 }
4140
4141 START_TEST(mesh)
4142 {
4143     D3DXBoundProbeTest();
4144     D3DXComputeBoundingBoxTest();
4145     D3DXComputeBoundingSphereTest();
4146     D3DXGetFVFVertexSizeTest();
4147     D3DXIntersectTriTest();
4148     D3DXCreateMeshTest();
4149     D3DXCreateMeshFVFTest();
4150     D3DXLoadMeshTest();
4151     D3DXCreateBoxTest();
4152     D3DXCreateSphereTest();
4153     D3DXCreateCylinderTest();
4154     D3DXCreateTextTest();
4155     test_get_decl_length();
4156     test_get_decl_vertex_size();
4157     test_fvf_decl_conversion();
4158     D3DXGenerateAdjacencyTest();
4159 }