d3dx9/tests: Added outline comparison tests for D3DXCreateText.
[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 #include <stdio.h>
22 #include <float.h>
23 #include "wine/test.h"
24 #include "d3dx9.h"
25
26 #define admitted_error 0.0001f
27
28 #define compare_vertex_sizes(type, exp) \
29     got=D3DXGetFVFVertexSize(type); \
30     ok(got==exp, "Expected: %d, Got: %d\n", exp, got);
31
32 #define compare_float(got, exp) \
33     do { \
34         float _got = (got); \
35         float _exp = (exp); \
36         ok(_got == _exp, "Expected: %g, Got: %g\n", _exp, _got); \
37     } while (0)
38
39 static BOOL compare(FLOAT u, FLOAT v)
40 {
41     return (fabs(u-v) < admitted_error);
42 }
43
44 static BOOL compare_vec3(D3DXVECTOR3 u, D3DXVECTOR3 v)
45 {
46     return ( compare(u.x, v.x) && compare(u.y, v.y) && compare(u.z, v.z) );
47 }
48
49 struct vertex
50 {
51     D3DXVECTOR3 position;
52     D3DXVECTOR3 normal;
53 };
54
55 typedef WORD face[3];
56
57 static BOOL compare_face(face a, face b)
58 {
59     return (a[0]==b[0] && a[1] == b[1] && a[2] == b[2]);
60 }
61
62 struct mesh
63 {
64     DWORD number_of_vertices;
65     struct vertex *vertices;
66
67     DWORD number_of_faces;
68     face *faces;
69
70     DWORD fvf;
71     UINT vertex_size;
72 };
73
74 static void free_mesh(struct mesh *mesh)
75 {
76     HeapFree(GetProcessHeap(), 0, mesh->faces);
77     HeapFree(GetProcessHeap(), 0, mesh->vertices);
78 }
79
80 static BOOL new_mesh(struct mesh *mesh, DWORD number_of_vertices, DWORD number_of_faces)
81 {
82     mesh->vertices = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, number_of_vertices * sizeof(*mesh->vertices));
83     if (!mesh->vertices)
84     {
85         return FALSE;
86     }
87     mesh->number_of_vertices = number_of_vertices;
88
89     mesh->faces = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, number_of_faces * sizeof(*mesh->faces));
90     if (!mesh->faces)
91     {
92         HeapFree(GetProcessHeap(), 0, mesh->vertices);
93         return FALSE;
94     }
95     mesh->number_of_faces = number_of_faces;
96
97     return TRUE;
98 }
99
100 static void compare_mesh(const char *name, ID3DXMesh *d3dxmesh, struct mesh *mesh)
101 {
102     HRESULT hr;
103     DWORD number_of_vertices, number_of_faces;
104     IDirect3DVertexBuffer9 *vertex_buffer;
105     IDirect3DIndexBuffer9 *index_buffer;
106     D3DVERTEXBUFFER_DESC vertex_buffer_description;
107     D3DINDEXBUFFER_DESC index_buffer_description;
108     struct vertex *vertices;
109     face *faces;
110     int expected, i;
111
112     number_of_vertices = d3dxmesh->lpVtbl->GetNumVertices(d3dxmesh);
113     ok(number_of_vertices == mesh->number_of_vertices, "Test %s, result %u, expected %d\n",
114        name, number_of_vertices, mesh->number_of_vertices);
115
116     number_of_faces = d3dxmesh->lpVtbl->GetNumFaces(d3dxmesh);
117     ok(number_of_faces == mesh->number_of_faces, "Test %s, result %u, expected %d\n",
118        name, number_of_faces, mesh->number_of_faces);
119
120     /* vertex buffer */
121     hr = d3dxmesh->lpVtbl->GetVertexBuffer(d3dxmesh, &vertex_buffer);
122     ok(hr == D3D_OK, "Test %s, result %x, expected 0 (D3D_OK)\n", name, hr);
123
124     if (hr != D3D_OK)
125     {
126         skip("Couldn't get vertex buffer\n");
127     }
128     else
129     {
130         hr = IDirect3DVertexBuffer9_GetDesc(vertex_buffer, &vertex_buffer_description);
131         ok(hr == D3D_OK, "Test %s, result %x, expected 0 (D3D_OK)\n", name, hr);
132
133         if (hr != D3D_OK)
134         {
135             skip("Couldn't get vertex buffer description\n");
136         }
137         else
138         {
139             ok(vertex_buffer_description.Format == D3DFMT_VERTEXDATA, "Test %s, result %x, expected %x (D3DFMT_VERTEXDATA)\n",
140                name, vertex_buffer_description.Format, D3DFMT_VERTEXDATA);
141             ok(vertex_buffer_description.Type == D3DRTYPE_VERTEXBUFFER, "Test %s, result %x, expected %x (D3DRTYPE_VERTEXBUFFER)\n",
142                name, vertex_buffer_description.Type, D3DRTYPE_VERTEXBUFFER);
143             ok(vertex_buffer_description.Usage == 0, "Test %s, result %x, expected %x\n", name, vertex_buffer_description.Usage, 0);
144             ok(vertex_buffer_description.Pool == D3DPOOL_MANAGED, "Test %s, result %x, expected %x (D3DPOOL_DEFAULT)\n",
145                name, vertex_buffer_description.Pool, D3DPOOL_DEFAULT);
146             ok(vertex_buffer_description.FVF == mesh->fvf, "Test %s, result %x, expected %x\n",
147                name, vertex_buffer_description.FVF, mesh->fvf);
148             if (mesh->fvf == 0)
149             {
150                 expected = number_of_vertices * mesh->vertex_size;
151             }
152             else
153             {
154                 expected = number_of_vertices * D3DXGetFVFVertexSize(mesh->fvf);
155             }
156             ok(vertex_buffer_description.Size == expected, "Test %s, result %x, expected %x\n",
157                name, vertex_buffer_description.Size, expected);
158         }
159
160         /* specify offset and size to avoid potential overruns */
161         hr = IDirect3DVertexBuffer9_Lock(vertex_buffer, 0, number_of_vertices * sizeof(D3DXVECTOR3) * 2,
162                                          (LPVOID *)&vertices, D3DLOCK_DISCARD);
163         ok(hr == D3D_OK, "Test %s, result %x, expected 0 (D3D_OK)\n", name, hr);
164
165         if (hr != D3D_OK)
166         {
167             skip("Couldn't lock vertex buffer\n");
168         }
169         else
170         {
171             for (i = 0; i < number_of_vertices; i++)
172             {
173                 ok(compare_vec3(vertices[i].position, mesh->vertices[i].position),
174                    "Test %s, vertex position %d, result (%g, %g, %g), expected (%g, %g, %g)\n", name, i,
175                    vertices[i].position.x, vertices[i].position.y, vertices[i].position.z,
176                    mesh->vertices[i].position.x, mesh->vertices[i].position.y, mesh->vertices[i].position.z);
177                 ok(compare_vec3(vertices[i].normal, mesh->vertices[i].normal),
178                    "Test %s, vertex normal %d, result (%g, %g, %g), expected (%g, %g, %g)\n", name, i,
179                    vertices[i].normal.x, vertices[i].normal.y, vertices[i].normal.z,
180                    mesh->vertices[i].normal.x, mesh->vertices[i].normal.y, mesh->vertices[i].normal.z);
181             }
182
183             IDirect3DVertexBuffer9_Unlock(vertex_buffer);
184         }
185
186         IDirect3DVertexBuffer9_Release(vertex_buffer);
187     }
188
189     /* index buffer */
190     hr = d3dxmesh->lpVtbl->GetIndexBuffer(d3dxmesh, &index_buffer);
191     ok(hr == D3D_OK, "Test %s, result %x, expected 0 (D3D_OK)\n", name, hr);
192
193     if (!index_buffer)
194     {
195         skip("Couldn't get index buffer\n");
196     }
197     else
198     {
199         hr = IDirect3DIndexBuffer9_GetDesc(index_buffer, &index_buffer_description);
200         ok(hr == D3D_OK, "Test %s, result %x, expected 0 (D3D_OK)\n", name, hr);
201
202         if (hr != D3D_OK)
203         {
204             skip("Couldn't get index buffer description\n");
205         }
206         else
207         {
208             ok(index_buffer_description.Format == D3DFMT_INDEX16, "Test %s, result %x, expected %x (D3DFMT_INDEX16)\n",
209                name, index_buffer_description.Format, D3DFMT_INDEX16);
210             ok(index_buffer_description.Type == D3DRTYPE_INDEXBUFFER, "Test %s, result %x, expected %x (D3DRTYPE_INDEXBUFFER)\n",
211                name, index_buffer_description.Type, D3DRTYPE_INDEXBUFFER);
212             todo_wine ok(index_buffer_description.Usage == 0, "Test %s, result %x, expected %x\n", name, index_buffer_description.Usage, 0);
213             ok(index_buffer_description.Pool == D3DPOOL_MANAGED, "Test %s, result %x, expected %x (D3DPOOL_DEFAULT)\n",
214                name, index_buffer_description.Pool, D3DPOOL_DEFAULT);
215             expected = number_of_faces * sizeof(WORD) * 3;
216             ok(index_buffer_description.Size == expected, "Test %s, result %x, expected %x\n",
217                name, index_buffer_description.Size, expected);
218         }
219
220         /* specify offset and size to avoid potential overruns */
221         hr = IDirect3DIndexBuffer9_Lock(index_buffer, 0, number_of_faces * sizeof(WORD) * 3,
222                                         (LPVOID *)&faces, D3DLOCK_DISCARD);
223         ok(hr == D3D_OK, "Test %s, result %x, expected 0 (D3D_OK)\n", name, hr);
224
225         if (hr != D3D_OK)
226         {
227             skip("Couldn't lock index buffer\n");
228         }
229         else
230         {
231             for (i = 0; i < number_of_faces; i++)
232             {
233                 ok(compare_face(faces[i], mesh->faces[i]),
234                    "Test %s, face %d, result (%u, %u, %u), expected (%u, %u, %u)\n", name, i,
235                    faces[i][0], faces[i][1], faces[i][2],
236                    mesh->faces[i][0], mesh->faces[i][1], mesh->faces[i][2]);
237             }
238
239             IDirect3DIndexBuffer9_Unlock(index_buffer);
240         }
241
242         IDirect3DIndexBuffer9_Release(index_buffer);
243     }
244 }
245
246 static void D3DXBoundProbeTest(void)
247 {
248     BOOL result;
249     D3DXVECTOR3 bottom_point, center, top_point, raydirection, rayposition;
250     FLOAT radius;
251
252 /*____________Test the Box case___________________________*/
253     bottom_point.x = -3.0f; bottom_point.y = -2.0f; bottom_point.z = -1.0f;
254     top_point.x = 7.0f; top_point.y = 8.0f; top_point.z = 9.0f;
255
256     raydirection.x = -4.0f; raydirection.y = -5.0f; raydirection.z = -6.0f;
257     rayposition.x = 5.0f; rayposition.y = 5.0f; rayposition.z = 11.0f;
258     result = D3DXBoxBoundProbe(&bottom_point, &top_point, &rayposition, &raydirection);
259     ok(result == TRUE, "expected TRUE, received FALSE\n");
260
261     raydirection.x = 4.0f; raydirection.y = 5.0f; raydirection.z = 6.0f;
262     rayposition.x = 5.0f; rayposition.y = 5.0f; rayposition.z = 11.0f;
263     result = D3DXBoxBoundProbe(&bottom_point, &top_point, &rayposition, &raydirection);
264     ok(result == FALSE, "expected FALSE, received TRUE\n");
265
266     rayposition.x = -4.0f; rayposition.y = 1.0f; rayposition.z = -2.0f;
267     result = D3DXBoxBoundProbe(&bottom_point, &top_point, &rayposition, &raydirection);
268     ok(result == TRUE, "expected TRUE, received FALSE\n");
269
270     bottom_point.x = 1.0f; bottom_point.y = 0.0f; bottom_point.z = 0.0f;
271     top_point.x = 1.0f; top_point.y = 0.0f; top_point.z = 0.0f;
272     rayposition.x = 0.0f; rayposition.y = 1.0f; rayposition.z = 0.0f;
273     raydirection.x = 0.0f; raydirection.y = 3.0f; raydirection.z = 0.0f;
274     result = D3DXBoxBoundProbe(&bottom_point, &top_point, &rayposition, &raydirection);
275     ok(result == FALSE, "expected FALSE, received TRUE\n");
276
277     bottom_point.x = 1.0f; bottom_point.y = 2.0f; bottom_point.z = 3.0f;
278     top_point.x = 10.0f; top_point.y = 15.0f; top_point.z = 20.0f;
279
280     raydirection.x = 7.0f; raydirection.y = 8.0f; raydirection.z = 9.0f;
281     rayposition.x = 3.0f; rayposition.y = 7.0f; rayposition.z = -6.0f;
282     result = D3DXBoxBoundProbe(&bottom_point, &top_point, &rayposition, &raydirection);
283     ok(result == TRUE, "expected TRUE, received FALSE\n");
284
285     bottom_point.x = 0.0f; bottom_point.y = 0.0f; bottom_point.z = 0.0f;
286     top_point.x = 1.0f; top_point.y = 1.0f; top_point.z = 1.0f;
287
288     raydirection.x = 0.0f; raydirection.y = 1.0f; raydirection.z = .0f;
289     rayposition.x = -3.0f; rayposition.y = 0.0f; rayposition.z = 0.0f;
290     result = D3DXBoxBoundProbe(&bottom_point, &top_point, &rayposition, &raydirection);
291     ok(result == FALSE, "expected FALSE, received TRUE\n");
292
293     raydirection.x = 1.0f; raydirection.y = 0.0f; raydirection.z = .0f;
294     rayposition.x = -3.0f; rayposition.y = 0.0f; rayposition.z = 0.0f;
295     result = D3DXBoxBoundProbe(&bottom_point, &top_point, &rayposition, &raydirection);
296     ok(result == TRUE, "expected TRUE, received FALSE\n");
297
298 /*____________Test the Sphere case________________________*/
299     radius = sqrt(77.0f);
300     center.x = 1.0f; center.y = 2.0f; center.z = 3.0f;
301     raydirection.x = 2.0f; raydirection.y = -4.0f; raydirection.z = 2.0f;
302
303     rayposition.x = 5.0f; rayposition.y = 5.0f; rayposition.z = 9.0f;
304     result = D3DXSphereBoundProbe(&center, radius, &rayposition, &raydirection);
305     ok(result == TRUE, "expected TRUE, received FALSE\n");
306
307     rayposition.x = 45.0f; rayposition.y = -75.0f; rayposition.z = 49.0f;
308     result = D3DXSphereBoundProbe(&center, radius, &rayposition, &raydirection);
309     ok(result == FALSE, "expected FALSE, received TRUE\n");
310
311     rayposition.x = 5.0f; rayposition.y = 11.0f; rayposition.z = 9.0f;
312     result = D3DXSphereBoundProbe(&center, radius, &rayposition, &raydirection);
313     ok(result == FALSE, "expected FALSE, received TRUE\n");
314 }
315
316 static void D3DXComputeBoundingBoxTest(void)
317 {
318     D3DXVECTOR3 exp_max, exp_min, got_max, got_min, vertex[5];
319     HRESULT hr;
320
321     vertex[0].x = 1.0f; vertex[0].y = 1.0f; vertex[0].z = 1.0f;
322     vertex[1].x = 1.0f; vertex[1].y = 1.0f; vertex[1].z = 1.0f;
323     vertex[2].x = 1.0f; vertex[2].y = 1.0f; vertex[2].z = 1.0f;
324     vertex[3].x = 1.0f; vertex[3].y = 1.0f; vertex[3].z = 1.0f;
325     vertex[4].x = 9.0f; vertex[4].y = 9.0f; vertex[4].z = 9.0f;
326
327     exp_min.x = 1.0f; exp_min.y = 1.0f; exp_min.z = 1.0f;
328     exp_max.x = 9.0f; exp_max.y = 9.0f; exp_max.z = 9.0f;
329
330     hr = D3DXComputeBoundingBox(&vertex[3],2,D3DXGetFVFVertexSize(D3DFVF_XYZ),&got_min,&got_max);
331
332     ok( hr == D3D_OK, "Expected D3D_OK, got %#x\n", hr);
333     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);
334     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);
335
336 /*________________________*/
337
338     vertex[0].x = 2.0f; vertex[0].y = 5.9f; vertex[0].z = -1.2f;
339     vertex[1].x = -1.87f; vertex[1].y = 7.9f; vertex[1].z = 7.4f;
340     vertex[2].x = 7.43f; vertex[2].y = -0.9f; vertex[2].z = 11.9f;
341     vertex[3].x = -6.92f; vertex[3].y = 6.3f; vertex[3].z = -3.8f;
342     vertex[4].x = 11.4f; vertex[4].y = -8.1f; vertex[4].z = 4.5f;
343
344     exp_min.x = -6.92f; exp_min.y = -8.1f; exp_min.z = -3.80f;
345     exp_max.x = 11.4f; exp_max.y = 7.90f; exp_max.z = 11.9f;
346
347     hr = D3DXComputeBoundingBox(&vertex[0],5,D3DXGetFVFVertexSize(D3DFVF_XYZ),&got_min,&got_max);
348
349     ok( hr == D3D_OK, "Expected D3D_OK, got %#x\n", hr);
350     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);
351     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);
352
353 /*________________________*/
354
355     vertex[0].x = 2.0f; vertex[0].y = 5.9f; vertex[0].z = -1.2f;
356     vertex[1].x = -1.87f; vertex[1].y = 7.9f; vertex[1].z = 7.4f;
357     vertex[2].x = 7.43f; vertex[2].y = -0.9f; vertex[2].z = 11.9f;
358     vertex[3].x = -6.92f; vertex[3].y = 6.3f; vertex[3].z = -3.8f;
359     vertex[4].x = 11.4f; vertex[4].y = -8.1f; vertex[4].z = 4.5f;
360
361     exp_min.x = -6.92f; exp_min.y = -0.9f; exp_min.z = -3.8f;
362     exp_max.x = 7.43f; exp_max.y = 7.90f; exp_max.z = 11.9f;
363
364     hr = D3DXComputeBoundingBox(&vertex[0],4,D3DXGetFVFVertexSize(D3DFVF_XYZ),&got_min,&got_max);
365
366     ok( hr == D3D_OK, "Expected D3D_OK, got %#x\n", hr);
367     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);
368     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);
369
370 /*________________________*/
371     hr = D3DXComputeBoundingBox(NULL,5,D3DXGetFVFVertexSize(D3DFVF_XYZ),&got_min,&got_max);
372     ok( hr == D3DERR_INVALIDCALL, "Expected D3DERR_INVALIDCALL, got %#x\n", hr);
373
374 /*________________________*/
375     hr = D3DXComputeBoundingBox(&vertex[3],5,D3DXGetFVFVertexSize(D3DFVF_XYZ),NULL,&got_max);
376     ok( hr == D3DERR_INVALIDCALL, "Expected D3DERR_INVALIDCALL, got %#x\n", hr);
377
378 /*________________________*/
379     hr = D3DXComputeBoundingBox(&vertex[3],5,D3DXGetFVFVertexSize(D3DFVF_XYZ),&got_min,NULL);
380     ok( hr == D3DERR_INVALIDCALL, "Expected D3DERR_INVALIDCALL, got %#x\n", hr);
381 }
382
383 static void D3DXComputeBoundingSphereTest(void)
384 {
385     D3DXVECTOR3 exp_cen, got_cen, vertex[5];
386     FLOAT exp_rad, got_rad;
387     HRESULT hr;
388
389     vertex[0].x = 1.0f; vertex[0].y = 1.0f; vertex[0].z = 1.0f;
390     vertex[1].x = 1.0f; vertex[1].y = 1.0f; vertex[1].z = 1.0f;
391     vertex[2].x = 1.0f; vertex[2].y = 1.0f; vertex[2].z = 1.0f;
392     vertex[3].x = 1.0f; vertex[3].y = 1.0f; vertex[3].z = 1.0f;
393     vertex[4].x = 9.0f; vertex[4].y = 9.0f; vertex[4].z = 9.0f;
394
395     exp_rad = 6.928203f;
396     exp_cen.x = 5.0; exp_cen.y = 5.0; exp_cen.z = 5.0;
397
398     hr = D3DXComputeBoundingSphere(&vertex[3],2,D3DXGetFVFVertexSize(D3DFVF_XYZ),&got_cen,&got_rad);
399
400     ok( hr == D3D_OK, "Expected D3D_OK, got %#x\n", hr);
401     ok( compare(exp_rad, got_rad), "Expected radius: %f, got radius: %f\n", exp_rad, got_rad);
402     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);
403
404 /*________________________*/
405
406     vertex[0].x = 2.0f; vertex[0].y = 5.9f; vertex[0].z = -1.2f;
407     vertex[1].x = -1.87f; vertex[1].y = 7.9f; vertex[1].z = 7.4f;
408     vertex[2].x = 7.43f; vertex[2].y = -0.9f; vertex[2].z = 11.9f;
409     vertex[3].x = -6.92f; vertex[3].y = 6.3f; vertex[3].z = -3.8f;
410     vertex[4].x = 11.4f; vertex[4].y = -8.1f; vertex[4].z = 4.5f;
411
412     exp_rad = 13.707883f;
413     exp_cen.x = 2.408f; exp_cen.y = 2.22f; exp_cen.z = 3.76f;
414
415     hr = D3DXComputeBoundingSphere(&vertex[0],5,D3DXGetFVFVertexSize(D3DFVF_XYZ),&got_cen,&got_rad);
416
417     ok( hr == D3D_OK, "Expected D3D_OK, got %#x\n", hr);
418     ok( compare(exp_rad, got_rad), "Expected radius: %f, got radius: %f\n", exp_rad, got_rad);
419     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);
420
421 /*________________________*/
422     hr = D3DXComputeBoundingSphere(NULL,5,D3DXGetFVFVertexSize(D3DFVF_XYZ),&got_cen,&got_rad);
423     ok( hr == D3DERR_INVALIDCALL, "Expected D3DERR_INVALIDCALL, got %#x\n", hr);
424
425 /*________________________*/
426     hr = D3DXComputeBoundingSphere(&vertex[3],5,D3DXGetFVFVertexSize(D3DFVF_XYZ),NULL,&got_rad);
427     ok( hr == D3DERR_INVALIDCALL, "Expected D3DERR_INVALIDCALL, got %#x\n", hr);
428
429 /*________________________*/
430     hr = D3DXComputeBoundingSphere(&vertex[3],5,D3DXGetFVFVertexSize(D3DFVF_XYZ),&got_cen,NULL);
431     ok( hr == D3DERR_INVALIDCALL, "Expected D3DERR_INVALIDCALL, got %#x\n", hr);
432 }
433
434 static void print_elements(const D3DVERTEXELEMENT9 *elements)
435 {
436     D3DVERTEXELEMENT9 last = D3DDECL_END();
437     const D3DVERTEXELEMENT9 *ptr = elements;
438     int count = 0;
439
440     while (memcmp(ptr, &last, sizeof(D3DVERTEXELEMENT9)))
441     {
442         trace(
443             "[Element %d] Stream = %d, Offset = %d, Type = %d, Method = %d, Usage = %d, UsageIndex = %d\n",
444              count, ptr->Stream, ptr->Offset, ptr->Type, ptr->Method, ptr->Usage, ptr->UsageIndex);
445         ptr++;
446         count++;
447     }
448 }
449
450 static void compare_elements(const D3DVERTEXELEMENT9 *elements, const D3DVERTEXELEMENT9 *expected_elements,
451         unsigned int line, unsigned int test_id)
452 {
453     D3DVERTEXELEMENT9 last = D3DDECL_END();
454     unsigned int i;
455
456     for (i = 0; i < MAX_FVF_DECL_SIZE; i++)
457     {
458         int end1 = memcmp(&elements[i], &last, sizeof(last));
459         int end2 = memcmp(&expected_elements[i], &last, sizeof(last));
460         int status;
461
462         if (!end1 && !end2) break;
463
464         status = !end1 ^ !end2;
465         ok(!status, "Line %u, test %u: Mismatch in size, test declaration is %s than expected.\n",
466                 line, test_id, end1 ? "shorter" : "longer");
467         if (status)
468         {
469             print_elements(elements);
470             break;
471         }
472
473         status = memcmp(&elements[i], &expected_elements[i], sizeof(D3DVERTEXELEMENT9));
474         ok(!status, "Line %u, test %u: Mismatch in element %u.\n", line, test_id, i);
475         if (status)
476         {
477             print_elements(elements);
478             break;
479         }
480     }
481 }
482
483 static void test_fvf_to_decl(DWORD test_fvf, const D3DVERTEXELEMENT9 expected_elements[],
484         HRESULT expected_hr, unsigned int line, unsigned int test_id)
485 {
486     HRESULT hr;
487     D3DVERTEXELEMENT9 decl[MAX_FVF_DECL_SIZE];
488
489     hr = D3DXDeclaratorFromFVF(test_fvf, decl);
490     ok(hr == expected_hr,
491             "Line %u, test %u: D3DXDeclaratorFromFVF returned %#x, expected %#x.\n",
492             line, test_id, hr, expected_hr);
493     if (SUCCEEDED(hr)) compare_elements(decl, expected_elements, line, test_id);
494 }
495
496 static void test_decl_to_fvf(const D3DVERTEXELEMENT9 *decl, DWORD expected_fvf,
497         HRESULT expected_hr, unsigned int line, unsigned int test_id)
498 {
499     HRESULT hr;
500     DWORD result_fvf = 0xdeadbeef;
501
502     hr = D3DXFVFFromDeclarator(decl, &result_fvf);
503     ok(hr == expected_hr,
504        "Line %u, test %u: D3DXFVFFromDeclarator returned %#x, expected %#x.\n",
505        line, test_id, hr, expected_hr);
506     if (SUCCEEDED(hr))
507     {
508         ok(expected_fvf == result_fvf, "Line %u, test %u: Got FVF %#x, expected %#x.\n",
509                 line, test_id, result_fvf, expected_fvf);
510     }
511 }
512
513 static void test_fvf_decl_conversion(void)
514 {
515     static const struct
516     {
517         D3DVERTEXELEMENT9 decl[MAXD3DDECLLENGTH + 1];
518         DWORD fvf;
519     }
520     test_data[] =
521     {
522         {{
523             D3DDECL_END(),
524         }, 0},
525         {{
526             {0, 0, D3DDECLTYPE_FLOAT3, 0, D3DDECLUSAGE_POSITION, 0},
527             D3DDECL_END(),
528         }, D3DFVF_XYZ},
529         {{
530             {0, 0, D3DDECLTYPE_FLOAT4, 0, D3DDECLUSAGE_POSITIONT, 0},
531             D3DDECL_END(),
532         }, D3DFVF_XYZRHW},
533         {{
534             {0, 0, D3DDECLTYPE_FLOAT4, 0, D3DDECLUSAGE_POSITIONT, 0},
535             D3DDECL_END(),
536         }, D3DFVF_XYZRHW},
537         {{
538             {0, 0, D3DDECLTYPE_FLOAT3, 0, D3DDECLUSAGE_POSITION, 0},
539             {0, 12, D3DDECLTYPE_FLOAT1, 0, D3DDECLUSAGE_BLENDWEIGHT, 0},
540             D3DDECL_END(),
541         }, D3DFVF_XYZB1},
542         {{
543             {0, 0, D3DDECLTYPE_FLOAT3, 0, D3DDECLUSAGE_POSITION, 0},
544             {0, 12, D3DDECLTYPE_UBYTE4, 0, D3DDECLUSAGE_BLENDINDICES, 0},
545             D3DDECL_END(),
546         }, D3DFVF_XYZB1 | D3DFVF_LASTBETA_UBYTE4},
547         {{
548             {0, 0, D3DDECLTYPE_FLOAT3, 0, D3DDECLUSAGE_POSITION, 0},
549             {0, 12, D3DDECLTYPE_D3DCOLOR, 0, D3DDECLUSAGE_BLENDINDICES, 0},
550             D3DDECL_END(),
551         }, D3DFVF_XYZB1 | D3DFVF_LASTBETA_D3DCOLOR},
552         {{
553             {0, 0, D3DDECLTYPE_FLOAT3, 0, D3DDECLUSAGE_POSITION, 0},
554             {0, 12, D3DDECLTYPE_FLOAT2, 0, D3DDECLUSAGE_BLENDWEIGHT, 0},
555             D3DDECL_END(),
556         }, D3DFVF_XYZB2},
557         {{
558             {0, 0, D3DDECLTYPE_FLOAT3, 0, D3DDECLUSAGE_POSITION, 0},
559             {0, 12, D3DDECLTYPE_FLOAT1, 0, D3DDECLUSAGE_BLENDWEIGHT, 0},
560             {0, 16, D3DDECLTYPE_UBYTE4, 0, D3DDECLUSAGE_BLENDINDICES, 0},
561             D3DDECL_END(),
562         }, D3DFVF_XYZB2 | D3DFVF_LASTBETA_UBYTE4},
563         {{
564             {0, 0, D3DDECLTYPE_FLOAT3, 0, D3DDECLUSAGE_POSITION, 0},
565             {0, 12, D3DDECLTYPE_FLOAT1, 0, D3DDECLUSAGE_BLENDWEIGHT, 0},
566             {0, 16, D3DDECLTYPE_D3DCOLOR, 0, D3DDECLUSAGE_BLENDINDICES, 0},
567             D3DDECL_END(),
568         }, D3DFVF_XYZB2 | D3DFVF_LASTBETA_D3DCOLOR},
569         {{
570             {0, 0, D3DDECLTYPE_FLOAT3, 0, D3DDECLUSAGE_POSITION, 0},
571             {0, 12, D3DDECLTYPE_FLOAT3, 0, D3DDECLUSAGE_BLENDWEIGHT, 0},
572             D3DDECL_END(),
573         }, D3DFVF_XYZB3},
574         {{
575             {0, 0, D3DDECLTYPE_FLOAT3, 0, D3DDECLUSAGE_POSITION, 0},
576             {0, 12, D3DDECLTYPE_FLOAT2, 0, D3DDECLUSAGE_BLENDWEIGHT, 0},
577             {0, 20, D3DDECLTYPE_UBYTE4, 0, D3DDECLUSAGE_BLENDINDICES, 0},
578             D3DDECL_END(),
579         }, D3DFVF_XYZB3 | D3DFVF_LASTBETA_UBYTE4},
580         {{
581             {0, 0, D3DDECLTYPE_FLOAT3, 0, D3DDECLUSAGE_POSITION, 0},
582             {0, 12, D3DDECLTYPE_FLOAT2, 0, D3DDECLUSAGE_BLENDWEIGHT, 0},
583             {0, 20, D3DDECLTYPE_D3DCOLOR, 0, D3DDECLUSAGE_BLENDINDICES, 0},
584             D3DDECL_END(),
585         }, D3DFVF_XYZB3 | D3DFVF_LASTBETA_D3DCOLOR},
586         {{
587             {0, 0, D3DDECLTYPE_FLOAT3, 0, D3DDECLUSAGE_POSITION, 0},
588             {0, 12, D3DDECLTYPE_FLOAT4, 0, D3DDECLUSAGE_BLENDWEIGHT, 0},
589             D3DDECL_END(),
590         }, D3DFVF_XYZB4},
591         {{
592             {0, 0, D3DDECLTYPE_FLOAT3, 0, D3DDECLUSAGE_POSITION, 0},
593             {0, 12, D3DDECLTYPE_FLOAT3, 0, D3DDECLUSAGE_BLENDWEIGHT, 0},
594             {0, 24, D3DDECLTYPE_UBYTE4, 0, D3DDECLUSAGE_BLENDINDICES, 0},
595             D3DDECL_END(),
596         }, D3DFVF_XYZB4 | D3DFVF_LASTBETA_UBYTE4},
597         {{
598             {0, 0, D3DDECLTYPE_FLOAT3, 0, D3DDECLUSAGE_POSITION, 0},
599             {0, 12, D3DDECLTYPE_FLOAT3, 0, D3DDECLUSAGE_BLENDWEIGHT, 0},
600             {0, 24, D3DDECLTYPE_D3DCOLOR, 0, D3DDECLUSAGE_BLENDINDICES, 0},
601             D3DDECL_END(),
602         }, D3DFVF_XYZB4 | D3DFVF_LASTBETA_D3DCOLOR},
603         {{
604             {0, 0, D3DDECLTYPE_FLOAT3, 0, D3DDECLUSAGE_POSITION, 0},
605             {0, 12, D3DDECLTYPE_FLOAT4, 0, D3DDECLUSAGE_BLENDWEIGHT, 0},
606             {0, 28, D3DDECLTYPE_UBYTE4, 0, D3DDECLUSAGE_BLENDINDICES, 0},
607             D3DDECL_END(),
608         }, D3DFVF_XYZB5 | D3DFVF_LASTBETA_UBYTE4},
609         {{
610             {0, 0, D3DDECLTYPE_FLOAT3, 0, D3DDECLUSAGE_POSITION, 0},
611             {0, 12, D3DDECLTYPE_FLOAT4, 0, D3DDECLUSAGE_BLENDWEIGHT, 0},
612             {0, 28, D3DDECLTYPE_D3DCOLOR, 0, D3DDECLUSAGE_BLENDINDICES, 0},
613             D3DDECL_END(),
614         }, D3DFVF_XYZB5 | D3DFVF_LASTBETA_D3DCOLOR},
615         {{
616             {0, 0, D3DDECLTYPE_FLOAT3, 0, D3DDECLUSAGE_NORMAL, 0},
617             D3DDECL_END(),
618         }, D3DFVF_NORMAL},
619         {{
620             {0, 0, D3DDECLTYPE_FLOAT3, 0, D3DDECLUSAGE_NORMAL, 0},
621             {0, 12, D3DDECLTYPE_D3DCOLOR, 0, D3DDECLUSAGE_COLOR, 0},
622             D3DDECL_END(),
623         }, D3DFVF_NORMAL | D3DFVF_DIFFUSE},
624         {{
625             {0, 0, D3DDECLTYPE_FLOAT1, 0, D3DDECLUSAGE_PSIZE, 0},
626             D3DDECL_END(),
627         }, D3DFVF_PSIZE},
628         {{
629             {0, 0, D3DDECLTYPE_D3DCOLOR, 0, D3DDECLUSAGE_COLOR, 0},
630             D3DDECL_END(),
631         }, D3DFVF_DIFFUSE},
632         {{
633             {0, 0, D3DDECLTYPE_D3DCOLOR, 0, D3DDECLUSAGE_COLOR, 1},
634             D3DDECL_END(),
635         }, D3DFVF_SPECULAR},
636         /* Make sure textures of different sizes work. */
637         {{
638             {0, 0, D3DDECLTYPE_FLOAT1, 0, D3DDECLUSAGE_TEXCOORD, 0},
639             D3DDECL_END(),
640         }, D3DFVF_TEXCOORDSIZE1(0) | D3DFVF_TEX1},
641         {{
642             {0, 0, D3DDECLTYPE_FLOAT2, 0, D3DDECLUSAGE_TEXCOORD, 0},
643             D3DDECL_END(),
644         }, D3DFVF_TEXCOORDSIZE2(0) | D3DFVF_TEX1},
645         {{
646             {0, 0, D3DDECLTYPE_FLOAT3, 0, D3DDECLUSAGE_TEXCOORD, 0},
647             D3DDECL_END(),
648         }, D3DFVF_TEXCOORDSIZE3(0) | D3DFVF_TEX1},
649         {{
650             {0, 0, D3DDECLTYPE_FLOAT4, 0, D3DDECLUSAGE_TEXCOORD, 0},
651             D3DDECL_END(),
652         }, D3DFVF_TEXCOORDSIZE4(0) | D3DFVF_TEX1},
653         /* Make sure the TEXCOORD index works correctly - try several textures. */
654         {{
655             {0, 0, D3DDECLTYPE_FLOAT1, 0, D3DDECLUSAGE_TEXCOORD, 0},
656             {0, 4, D3DDECLTYPE_FLOAT3, 0, D3DDECLUSAGE_TEXCOORD, 1},
657             {0, 16, D3DDECLTYPE_FLOAT2, 0, D3DDECLUSAGE_TEXCOORD, 2},
658             {0, 24, D3DDECLTYPE_FLOAT4, 0, D3DDECLUSAGE_TEXCOORD, 3},
659             D3DDECL_END(),
660         }, D3DFVF_TEX4 | D3DFVF_TEXCOORDSIZE1(0) | D3DFVF_TEXCOORDSIZE3(1)
661                 | D3DFVF_TEXCOORDSIZE2(2) | D3DFVF_TEXCOORDSIZE4(3)},
662         /* Now try some combination tests. */
663         {{
664             {0, 0, D3DDECLTYPE_FLOAT3, 0, D3DDECLUSAGE_POSITION, 0},
665             {0, 12, D3DDECLTYPE_FLOAT4, 0, D3DDECLUSAGE_BLENDWEIGHT, 0},
666             {0, 28, D3DDECLTYPE_D3DCOLOR, 0, D3DDECLUSAGE_COLOR, 0},
667             {0, 32, D3DDECLTYPE_D3DCOLOR, 0, D3DDECLUSAGE_COLOR, 1},
668             {0, 36, D3DDECLTYPE_FLOAT2, 0, D3DDECLUSAGE_TEXCOORD, 0},
669             {0, 44, D3DDECLTYPE_FLOAT3, 0, D3DDECLUSAGE_TEXCOORD, 1},
670             D3DDECL_END(),
671         }, D3DFVF_XYZB4 | D3DFVF_DIFFUSE | D3DFVF_SPECULAR | D3DFVF_TEX2
672                 | D3DFVF_TEXCOORDSIZE2(0) | D3DFVF_TEXCOORDSIZE3(1)},
673         {{
674             {0, 0, D3DDECLTYPE_FLOAT3, 0, D3DDECLUSAGE_POSITION, 0},
675             {0, 12, D3DDECLTYPE_FLOAT3, 0, D3DDECLUSAGE_NORMAL, 0},
676             {0, 24, D3DDECLTYPE_FLOAT1, 0, D3DDECLUSAGE_PSIZE, 0},
677             {0, 28, D3DDECLTYPE_D3DCOLOR, 0, D3DDECLUSAGE_COLOR, 1},
678             {0, 32, D3DDECLTYPE_FLOAT1, 0, D3DDECLUSAGE_TEXCOORD, 0},
679             {0, 36, D3DDECLTYPE_FLOAT4, 0, D3DDECLUSAGE_TEXCOORD, 1},
680             D3DDECL_END(),
681         }, D3DFVF_XYZ | D3DFVF_NORMAL | D3DFVF_PSIZE | D3DFVF_SPECULAR | D3DFVF_TEX2
682                 | D3DFVF_TEXCOORDSIZE1(0) | D3DFVF_TEXCOORDSIZE4(1)},
683     };
684     unsigned int i;
685
686     for (i = 0; i < sizeof(test_data) / sizeof(*test_data); ++i)
687     {
688         test_decl_to_fvf(test_data[i].decl, test_data[i].fvf, D3D_OK, __LINE__, i);
689         test_fvf_to_decl(test_data[i].fvf, test_data[i].decl, D3D_OK, __LINE__, i);
690     }
691
692     /* Usage indices for position and normal are apparently ignored. */
693     {
694         const D3DVERTEXELEMENT9 decl[] =
695         {
696             {0, 0, D3DDECLTYPE_FLOAT3, 0, D3DDECLUSAGE_POSITION, 1},
697             D3DDECL_END(),
698         };
699         test_decl_to_fvf(decl, D3DFVF_XYZ, D3D_OK, __LINE__, 0);
700     }
701     {
702         const D3DVERTEXELEMENT9 decl[] =
703         {
704             {0, 0, D3DDECLTYPE_FLOAT3, 0, D3DDECLUSAGE_NORMAL, 1},
705             D3DDECL_END(),
706         };
707         test_decl_to_fvf(decl, D3DFVF_NORMAL, D3D_OK, __LINE__, 0);
708     }
709     /* D3DFVF_LASTBETA_UBYTE4 and D3DFVF_LASTBETA_D3DCOLOR are ignored if
710      * there are no blend matrices. */
711     {
712         const D3DVERTEXELEMENT9 decl[] =
713         {
714             {0, 0, D3DDECLTYPE_FLOAT3, 0, D3DDECLUSAGE_POSITION, 0},
715             D3DDECL_END(),
716         };
717         test_fvf_to_decl(D3DFVF_XYZ | D3DFVF_LASTBETA_UBYTE4, decl, D3D_OK, __LINE__, 0);
718     }
719     {
720         const D3DVERTEXELEMENT9 decl[] =
721         {
722             {0, 0, D3DDECLTYPE_FLOAT3, 0, D3DDECLUSAGE_POSITION, 0},
723             D3DDECL_END(),
724         };
725         test_fvf_to_decl(D3DFVF_XYZ | D3DFVF_LASTBETA_D3DCOLOR, decl, D3D_OK, __LINE__, 0);
726     }
727     /* D3DFVF_LASTBETA_UBYTE4 takes precedence over D3DFVF_LASTBETA_D3DCOLOR. */
728     {
729         const D3DVERTEXELEMENT9 decl[] =
730         {
731             {0, 0, D3DDECLTYPE_FLOAT3, 0, D3DDECLUSAGE_POSITION, 0},
732             {0, 12, D3DDECLTYPE_FLOAT4, 0, D3DDECLUSAGE_BLENDWEIGHT, 0},
733             {0, 28, D3DDECLTYPE_UBYTE4, 0, D3DDECLUSAGE_BLENDINDICES, 0},
734             D3DDECL_END(),
735         };
736         test_fvf_to_decl(D3DFVF_XYZB5 | D3DFVF_LASTBETA_D3DCOLOR | D3DFVF_LASTBETA_UBYTE4,
737                 decl, D3D_OK, __LINE__, 0);
738     }
739     /* These are supposed to fail, both ways. */
740     {
741         const D3DVERTEXELEMENT9 decl[] =
742         {
743             {0, 0, D3DDECLTYPE_FLOAT4, 0, D3DDECLUSAGE_POSITION, 0},
744             D3DDECL_END(),
745         };
746         test_decl_to_fvf(decl, D3DFVF_XYZW, D3DERR_INVALIDCALL, __LINE__, 0);
747         test_fvf_to_decl(D3DFVF_XYZW, decl, D3DERR_INVALIDCALL, __LINE__, 0);
748     }
749     {
750         const D3DVERTEXELEMENT9 decl[] =
751         {
752             {0, 0, D3DDECLTYPE_FLOAT4, 0, D3DDECLUSAGE_POSITION, 0},
753             {0, 16, D3DDECLTYPE_FLOAT3, 0, D3DDECLUSAGE_NORMAL, 0},
754             D3DDECL_END(),
755         };
756         test_decl_to_fvf(decl, D3DFVF_XYZW | D3DFVF_NORMAL, D3DERR_INVALIDCALL, __LINE__, 0);
757         test_fvf_to_decl(D3DFVF_XYZW | D3DFVF_NORMAL, decl, D3DERR_INVALIDCALL, __LINE__, 0);
758     }
759     {
760         const D3DVERTEXELEMENT9 decl[] =
761         {
762             {0, 0, D3DDECLTYPE_FLOAT3, 0, D3DDECLUSAGE_POSITION, 0},
763             {0, 12, D3DDECLTYPE_FLOAT4, 0, D3DDECLUSAGE_BLENDWEIGHT, 0},
764             {0, 28, D3DDECLTYPE_FLOAT1, 0, D3DDECLUSAGE_BLENDINDICES, 0},
765             D3DDECL_END(),
766         };
767         test_decl_to_fvf(decl, D3DFVF_XYZB5, D3DERR_INVALIDCALL, __LINE__, 0);
768         test_fvf_to_decl(D3DFVF_XYZB5, decl, D3DERR_INVALIDCALL, __LINE__, 0);
769     }
770     /* Test a declaration that can't be converted to an FVF. */
771     {
772         const D3DVERTEXELEMENT9 decl[] =
773         {
774             {0, 0, D3DDECLTYPE_FLOAT3, 0, D3DDECLUSAGE_POSITION, 0},
775             {0, 12, D3DDECLTYPE_FLOAT3, 0, D3DDECLUSAGE_NORMAL, 0},
776             {0, 24, D3DDECLTYPE_FLOAT1, 0, D3DDECLUSAGE_PSIZE, 0},
777             {0, 28, D3DDECLTYPE_D3DCOLOR, 0, D3DDECLUSAGE_COLOR, 1},
778             {0, 32, D3DDECLTYPE_FLOAT1, 0, D3DDECLUSAGE_TEXCOORD, 0},
779             /* 8 bytes padding */
780             {0, 44, D3DDECLTYPE_FLOAT4, 0, D3DDECLUSAGE_TEXCOORD, 1},
781             D3DDECL_END(),
782         };
783         test_decl_to_fvf(decl, 0, D3DERR_INVALIDCALL, __LINE__, 0);
784     }
785     /* Elements must be ordered by offset. */
786     {
787         const D3DVERTEXELEMENT9 decl[] =
788         {
789             {0, 12, D3DDECLTYPE_D3DCOLOR, 0, D3DDECLUSAGE_COLOR, 0},
790             {0, 0, D3DDECLTYPE_FLOAT3, 0, D3DDECLUSAGE_POSITION, 0},
791             D3DDECL_END(),
792         };
793         test_decl_to_fvf(decl, 0, D3DERR_INVALIDCALL, __LINE__, 0);
794     }
795     /* Basic tests for element order. */
796     {
797         const D3DVERTEXELEMENT9 decl[] =
798         {
799             {0, 0, D3DDECLTYPE_FLOAT3, 0, D3DDECLUSAGE_POSITION, 0},
800             {0, 12, D3DDECLTYPE_D3DCOLOR, 0, D3DDECLUSAGE_COLOR, 0},
801             {0, 16, D3DDECLTYPE_FLOAT3, 0, D3DDECLUSAGE_NORMAL, 0},
802             D3DDECL_END(),
803         };
804         test_decl_to_fvf(decl, 0, D3DERR_INVALIDCALL, __LINE__, 0);
805     }
806     {
807         const D3DVERTEXELEMENT9 decl[] =
808         {
809             {0, 0, D3DDECLTYPE_D3DCOLOR, 0, D3DDECLUSAGE_COLOR, 0},
810             {0, 4, D3DDECLTYPE_FLOAT3, 0, D3DDECLUSAGE_POSITION, 0},
811             D3DDECL_END(),
812         };
813         test_decl_to_fvf(decl, 0, D3DERR_INVALIDCALL, __LINE__, 0);
814     }
815     {
816         const D3DVERTEXELEMENT9 decl[] =
817         {
818             {0, 0, D3DDECLTYPE_FLOAT3, 0, D3DDECLUSAGE_NORMAL, 0},
819             {0, 12, D3DDECLTYPE_FLOAT3, 0, D3DDECLUSAGE_POSITION, 0},
820             D3DDECL_END(),
821         };
822         test_decl_to_fvf(decl, 0, D3DERR_INVALIDCALL, __LINE__, 0);
823     }
824     /* Textures must be ordered by texcoords. */
825     {
826         const D3DVERTEXELEMENT9 decl[] =
827         {
828             {0, 0, D3DDECLTYPE_FLOAT1, 0, D3DDECLUSAGE_TEXCOORD, 0},
829             {0, 4, D3DDECLTYPE_FLOAT3, 0, D3DDECLUSAGE_TEXCOORD, 2},
830             {0, 16, D3DDECLTYPE_FLOAT2, 0, D3DDECLUSAGE_TEXCOORD, 1},
831             {0, 24, D3DDECLTYPE_FLOAT4, 0, D3DDECLUSAGE_TEXCOORD, 3},
832             D3DDECL_END(),
833         };
834         test_decl_to_fvf(decl, 0, D3DERR_INVALIDCALL, __LINE__, 0);
835     }
836     /* Duplicate elements are not allowed. */
837     {
838         const D3DVERTEXELEMENT9 decl[] =
839         {
840             {0, 0, D3DDECLTYPE_FLOAT3, 0, D3DDECLUSAGE_POSITION, 0},
841             {0, 12, D3DDECLTYPE_D3DCOLOR, 0, D3DDECLUSAGE_COLOR, 0},
842             {0, 16, D3DDECLTYPE_D3DCOLOR, 0, D3DDECLUSAGE_COLOR, 0},
843             D3DDECL_END(),
844         };
845         test_decl_to_fvf(decl, 0, D3DERR_INVALIDCALL, __LINE__, 0);
846     }
847     /* Invalid FVFs cannot be converted to a declarator. */
848     test_fvf_to_decl(0xdeadbeef, NULL, D3DERR_INVALIDCALL, __LINE__, 0);
849 }
850
851 static void D3DXGetFVFVertexSizeTest(void)
852 {
853     UINT got;
854
855     compare_vertex_sizes (D3DFVF_XYZ, 12);
856
857     compare_vertex_sizes (D3DFVF_XYZB3, 24);
858
859     compare_vertex_sizes (D3DFVF_XYZB5, 32);
860
861     compare_vertex_sizes (D3DFVF_XYZ | D3DFVF_NORMAL, 24);
862
863     compare_vertex_sizes (D3DFVF_XYZ | D3DFVF_DIFFUSE, 16);
864
865     compare_vertex_sizes (
866         D3DFVF_XYZ |
867         D3DFVF_TEX1 |
868         D3DFVF_TEXCOORDSIZE1(0), 16);
869     compare_vertex_sizes (
870         D3DFVF_XYZ |
871         D3DFVF_TEX2 |
872         D3DFVF_TEXCOORDSIZE1(0) |
873         D3DFVF_TEXCOORDSIZE1(1), 20);
874
875     compare_vertex_sizes (
876         D3DFVF_XYZ |
877         D3DFVF_TEX1 |
878         D3DFVF_TEXCOORDSIZE2(0), 20);
879
880     compare_vertex_sizes (
881         D3DFVF_XYZ |
882         D3DFVF_TEX2 |
883         D3DFVF_TEXCOORDSIZE2(0) |
884         D3DFVF_TEXCOORDSIZE2(1), 28);
885
886     compare_vertex_sizes (
887         D3DFVF_XYZ |
888         D3DFVF_TEX6 |
889         D3DFVF_TEXCOORDSIZE2(0) |
890         D3DFVF_TEXCOORDSIZE2(1) |
891         D3DFVF_TEXCOORDSIZE2(2) |
892         D3DFVF_TEXCOORDSIZE2(3) |
893         D3DFVF_TEXCOORDSIZE2(4) |
894         D3DFVF_TEXCOORDSIZE2(5), 60);
895
896     compare_vertex_sizes (
897         D3DFVF_XYZ |
898         D3DFVF_TEX8 |
899         D3DFVF_TEXCOORDSIZE2(0) |
900         D3DFVF_TEXCOORDSIZE2(1) |
901         D3DFVF_TEXCOORDSIZE2(2) |
902         D3DFVF_TEXCOORDSIZE2(3) |
903         D3DFVF_TEXCOORDSIZE2(4) |
904         D3DFVF_TEXCOORDSIZE2(5) |
905         D3DFVF_TEXCOORDSIZE2(6) |
906         D3DFVF_TEXCOORDSIZE2(7), 76);
907
908     compare_vertex_sizes (
909         D3DFVF_XYZ |
910         D3DFVF_TEX1 |
911         D3DFVF_TEXCOORDSIZE3(0), 24);
912
913     compare_vertex_sizes (
914         D3DFVF_XYZ |
915         D3DFVF_TEX4 |
916         D3DFVF_TEXCOORDSIZE3(0) |
917         D3DFVF_TEXCOORDSIZE3(1) |
918         D3DFVF_TEXCOORDSIZE3(2) |
919         D3DFVF_TEXCOORDSIZE3(3), 60);
920
921     compare_vertex_sizes (
922         D3DFVF_XYZ |
923         D3DFVF_TEX1 |
924         D3DFVF_TEXCOORDSIZE4(0), 28);
925
926     compare_vertex_sizes (
927         D3DFVF_XYZ |
928         D3DFVF_TEX2 |
929         D3DFVF_TEXCOORDSIZE4(0) |
930         D3DFVF_TEXCOORDSIZE4(1), 44);
931
932     compare_vertex_sizes (
933         D3DFVF_XYZ |
934         D3DFVF_TEX3 |
935         D3DFVF_TEXCOORDSIZE4(0) |
936         D3DFVF_TEXCOORDSIZE4(1) |
937         D3DFVF_TEXCOORDSIZE4(2), 60);
938
939     compare_vertex_sizes (
940         D3DFVF_XYZB5 |
941         D3DFVF_NORMAL |
942         D3DFVF_DIFFUSE |
943         D3DFVF_SPECULAR |
944         D3DFVF_TEX8 |
945         D3DFVF_TEXCOORDSIZE4(0) |
946         D3DFVF_TEXCOORDSIZE4(1) |
947         D3DFVF_TEXCOORDSIZE4(2) |
948         D3DFVF_TEXCOORDSIZE4(3) |
949         D3DFVF_TEXCOORDSIZE4(4) |
950         D3DFVF_TEXCOORDSIZE4(5) |
951         D3DFVF_TEXCOORDSIZE4(6) |
952         D3DFVF_TEXCOORDSIZE4(7), 180);
953 }
954
955 static void D3DXIntersectTriTest(void)
956 {
957     BOOL exp_res, got_res;
958     D3DXVECTOR3 position, ray, vertex[3];
959     FLOAT exp_dist, got_dist, exp_u, got_u, exp_v, got_v;
960
961     vertex[0].x = 1.0f; vertex[0].y = 0.0f; vertex[0].z = 0.0f;
962     vertex[1].x = 2.0f; vertex[1].y = 0.0f; vertex[1].z = 0.0f;
963     vertex[2].x = 1.0f; vertex[2].y = 1.0f; vertex[2].z = 0.0f;
964
965     position.x = -14.5f; position.y = -23.75f; position.z = -32.0f;
966
967     ray.x = 2.0f; ray.y = 3.0f; ray.z = 4.0f;
968
969     exp_res = TRUE; exp_u = 0.5f; exp_v = 0.25f; exp_dist = 8.0f;
970
971     got_res = D3DXIntersectTri(&vertex[0],&vertex[1],&vertex[2],&position,&ray,&got_u,&got_v,&got_dist);
972     ok( got_res == exp_res, "Expected result = %d, got %d\n",exp_res,got_res);
973     ok( compare(exp_u,got_u), "Expected u = %f, got %f\n",exp_u,got_u);
974     ok( compare(exp_v,got_v), "Expected v = %f, got %f\n",exp_v,got_v);
975     ok( compare(exp_dist,got_dist), "Expected distance = %f, got %f\n",exp_dist,got_dist);
976
977 /*Only positive ray is taken in account*/
978
979     vertex[0].x = 1.0f; vertex[0].y = 0.0f; vertex[0].z = 0.0f;
980     vertex[1].x = 2.0f; vertex[1].y = 0.0f; vertex[1].z = 0.0f;
981     vertex[2].x = 1.0f; vertex[2].y = 1.0f; vertex[2].z = 0.0f;
982
983     position.x = 17.5f; position.y = 24.25f; position.z = 32.0f;
984
985     ray.x = 2.0f; ray.y = 3.0f; ray.z = 4.0f;
986
987     exp_res = FALSE;
988
989     got_res = D3DXIntersectTri(&vertex[0],&vertex[1],&vertex[2],&position,&ray,&got_u,&got_v,&got_dist);
990     ok( got_res == exp_res, "Expected result = %d, got %d\n",exp_res,got_res);
991
992 /*Intersection between ray and triangle in a same plane is considered as empty*/
993
994     vertex[0].x = 4.0f; vertex[0].y = 0.0f; vertex[0].z = 0.0f;
995     vertex[1].x = 6.0f; vertex[1].y = 0.0f; vertex[1].z = 0.0f;
996     vertex[2].x = 4.0f; vertex[2].y = 2.0f; vertex[2].z = 0.0f;
997
998     position.x = 1.0f; position.y = 1.0f; position.z = 0.0f;
999
1000     ray.x = 1.0f; ray.y = 0.0f; ray.z = 0.0f;
1001
1002     exp_res = FALSE;
1003
1004     got_res = D3DXIntersectTri(&vertex[0],&vertex[1],&vertex[2],&position,&ray,&got_u,&got_v,&got_dist);
1005     ok( got_res == exp_res, "Expected result = %d, got %d\n",exp_res,got_res);
1006 }
1007
1008 static void D3DXCreateMeshTest(void)
1009 {
1010     HRESULT hr;
1011     HWND wnd;
1012     IDirect3D9 *d3d;
1013     IDirect3DDevice9 *device, *test_device;
1014     D3DPRESENT_PARAMETERS d3dpp;
1015     ID3DXMesh *d3dxmesh;
1016     int i, size;
1017     D3DVERTEXELEMENT9 test_decl[MAX_FVF_DECL_SIZE];
1018     DWORD options;
1019     struct mesh mesh;
1020
1021     static const D3DVERTEXELEMENT9 decl1[3] = {
1022         {0, 0, D3DDECLTYPE_FLOAT3, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_POSITION, 0},
1023         {0, 12, D3DDECLTYPE_FLOAT3, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_NORMAL, 0},
1024         D3DDECL_END(), };
1025
1026     static const D3DVERTEXELEMENT9 decl2[] = {
1027         {0, 0, D3DDECLTYPE_FLOAT3, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_POSITION, 0},
1028         {0, 12, D3DDECLTYPE_FLOAT3, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_NORMAL, 0},
1029         {0, 24, D3DDECLTYPE_FLOAT1, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_PSIZE, 0},
1030         {0, 28, D3DDECLTYPE_D3DCOLOR, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_COLOR, 1},
1031         {0, 32, D3DDECLTYPE_FLOAT1, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_TEXCOORD, 0},
1032         /* 8 bytes padding */
1033         {0, 44, D3DDECLTYPE_FLOAT4, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_TEXCOORD, 1},
1034         D3DDECL_END(),
1035     };
1036
1037     hr = D3DXCreateMesh(0, 0, 0, NULL, NULL, NULL);
1038     ok(hr == D3DERR_INVALIDCALL, "Got result %x, expected %x (D3DERR_INVALIDCALL)\n", hr, D3DERR_INVALIDCALL);
1039
1040     hr = D3DXCreateMesh(1, 3, D3DXMESH_MANAGED, decl1, NULL, &d3dxmesh);
1041     ok(hr == D3DERR_INVALIDCALL, "Got result %x, expected %x (D3DERR_INVALIDCALL)\n", hr, D3DERR_INVALIDCALL);
1042
1043     wnd = CreateWindow("static", "d3dx9_test", 0, 0, 0, 0, 0, NULL, NULL, NULL, NULL);
1044     if (!wnd)
1045     {
1046         skip("Couldn't create application window\n");
1047         return;
1048     }
1049     d3d = Direct3DCreate9(D3D_SDK_VERSION);
1050     if (!d3d)
1051     {
1052         skip("Couldn't create IDirect3D9 object\n");
1053         DestroyWindow(wnd);
1054         return;
1055     }
1056
1057     ZeroMemory(&d3dpp, sizeof(d3dpp));
1058     d3dpp.Windowed = TRUE;
1059     d3dpp.SwapEffect = D3DSWAPEFFECT_DISCARD;
1060     hr = IDirect3D9_CreateDevice(d3d, D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, wnd, D3DCREATE_MIXED_VERTEXPROCESSING, &d3dpp, &device);
1061     if (FAILED(hr))
1062     {
1063         skip("Failed to create IDirect3DDevice9 object %#x\n", hr);
1064         IDirect3D9_Release(d3d);
1065         DestroyWindow(wnd);
1066         return;
1067     }
1068
1069     hr = D3DXCreateMesh(0, 3, D3DXMESH_MANAGED, decl1, device, &d3dxmesh);
1070     ok(hr == D3DERR_INVALIDCALL, "Got result %x, expected %x (D3DERR_INVALIDCALL)\n", hr, D3DERR_INVALIDCALL);
1071
1072     hr = D3DXCreateMesh(1, 0, D3DXMESH_MANAGED, decl1, device, &d3dxmesh);
1073     ok(hr == D3DERR_INVALIDCALL, "Got result %x, expected %x (D3DERR_INVALIDCALL)\n", hr, D3DERR_INVALIDCALL);
1074
1075     hr = D3DXCreateMesh(1, 3, 0, decl1, device, &d3dxmesh);
1076     ok(hr == D3D_OK, "Got result %x, expected %x (D3D_OK)\n", hr, D3D_OK);
1077
1078     if (hr == D3D_OK)
1079     {
1080         d3dxmesh->lpVtbl->Release(d3dxmesh);
1081     }
1082
1083     hr = D3DXCreateMesh(1, 3, D3DXMESH_MANAGED, 0, device, &d3dxmesh);
1084     ok(hr == D3DERR_INVALIDCALL, "Got result %x, expected %x (D3DERR_INVALIDCALL)\n", hr, D3DERR_INVALIDCALL);
1085
1086     hr = D3DXCreateMesh(1, 3, D3DXMESH_MANAGED, decl1, device, NULL);
1087     ok(hr == D3DERR_INVALIDCALL, "Got result %x, expected %x (D3DERR_INVALIDCALL)\n", hr, D3DERR_INVALIDCALL);
1088
1089     hr = D3DXCreateMesh(1, 3, D3DXMESH_MANAGED, decl1, device, &d3dxmesh);
1090     ok(hr == D3D_OK, "Got result %x, expected 0 (D3D_OK)\n", hr);
1091
1092     if (hr == D3D_OK)
1093     {
1094         /* device */
1095         hr = d3dxmesh->lpVtbl->GetDevice(d3dxmesh, NULL);
1096         ok(hr == D3DERR_INVALIDCALL, "Got result %x, expected %x (D3DERR_INVALIDCALL)\n", hr, D3DERR_INVALIDCALL);
1097
1098         hr = d3dxmesh->lpVtbl->GetDevice(d3dxmesh, &test_device);
1099         ok(hr == D3D_OK, "Got result %x, expected %x (D3D_OK)\n", hr, D3D_OK);
1100         ok(test_device == device, "Got result %p, expected %p\n", test_device, device);
1101
1102         if (hr == D3D_OK)
1103         {
1104             IDirect3DDevice9_Release(device);
1105         }
1106
1107         /* declaration */
1108         hr = d3dxmesh->lpVtbl->GetDeclaration(d3dxmesh, NULL);
1109         ok(hr == D3DERR_INVALIDCALL, "Got result %x, expected %x (D3DERR_INVALIDCALL)\n", hr, D3DERR_INVALIDCALL);
1110
1111         hr = d3dxmesh->lpVtbl->GetDeclaration(d3dxmesh, test_decl);
1112         ok(hr == D3D_OK, "Got result %x, expected 0 (D3D_OK)\n", hr);
1113
1114         if (hr == D3D_OK)
1115         {
1116             size = sizeof(decl1) / sizeof(decl1[0]);
1117             for (i = 0; i < size - 1; i++)
1118             {
1119                 ok(test_decl[i].Stream == decl1[i].Stream, "Returned stream %d, expected %d\n", test_decl[i].Stream, decl1[i].Stream);
1120                 ok(test_decl[i].Type == decl1[i].Type, "Returned type %d, expected %d\n", test_decl[i].Type, decl1[i].Type);
1121                 ok(test_decl[i].Method == decl1[i].Method, "Returned method %d, expected %d\n", test_decl[i].Method, decl1[i].Method);
1122                 ok(test_decl[i].Usage == decl1[i].Usage, "Returned usage %d, expected %d\n", test_decl[i].Usage, decl1[i].Usage);
1123                 ok(test_decl[i].UsageIndex == decl1[i].UsageIndex, "Returned usage index %d, expected %d\n", test_decl[i].UsageIndex, decl1[i].UsageIndex);
1124                 ok(test_decl[i].Offset == decl1[i].Offset, "Returned offset %d, expected %d\n", test_decl[i].Offset, decl1[i].Offset);
1125             }
1126             ok(decl1[size-1].Stream == 0xFF, "Returned too long vertex declaration\n"); /* end element */
1127         }
1128
1129         /* options */
1130         options = d3dxmesh->lpVtbl->GetOptions(d3dxmesh);
1131         ok(options == D3DXMESH_MANAGED, "Got result %x, expected %x (D3DXMESH_MANAGED)\n", options, D3DXMESH_MANAGED);
1132
1133         /* rest */
1134         if (!new_mesh(&mesh, 3, 1))
1135         {
1136             skip("Couldn't create mesh\n");
1137         }
1138         else
1139         {
1140             memset(mesh.vertices, 0, mesh.number_of_vertices * sizeof(*mesh.vertices));
1141             memset(mesh.faces, 0, mesh.number_of_faces * sizeof(*mesh.faces));
1142             mesh.fvf = D3DFVF_XYZ | D3DFVF_NORMAL;
1143
1144             compare_mesh("createmesh1", d3dxmesh, &mesh);
1145
1146             free_mesh(&mesh);
1147         }
1148
1149         d3dxmesh->lpVtbl->Release(d3dxmesh);
1150     }
1151
1152     /* Test a declaration that can't be converted to an FVF. */
1153     hr = D3DXCreateMesh(1, 3, D3DXMESH_MANAGED, decl2, device, &d3dxmesh);
1154     ok(hr == D3D_OK, "Got result %x, expected 0 (D3D_OK)\n", hr);
1155
1156     if (hr == D3D_OK)
1157     {
1158         /* device */
1159         hr = d3dxmesh->lpVtbl->GetDevice(d3dxmesh, NULL);
1160         ok(hr == D3DERR_INVALIDCALL, "Got result %x, expected %x (D3DERR_INVALIDCALL)\n", hr, D3DERR_INVALIDCALL);
1161
1162         hr = d3dxmesh->lpVtbl->GetDevice(d3dxmesh, &test_device);
1163         ok(hr == D3D_OK, "Got result %x, expected %x (D3D_OK)\n", hr, D3D_OK);
1164         ok(test_device == device, "Got result %p, expected %p\n", test_device, device);
1165
1166         if (hr == D3D_OK)
1167         {
1168             IDirect3DDevice9_Release(device);
1169         }
1170
1171         /* declaration */
1172         hr = d3dxmesh->lpVtbl->GetDeclaration(d3dxmesh, test_decl);
1173         ok(hr == D3D_OK, "Got result %x, expected 0 (D3D_OK)\n", hr);
1174
1175         if (hr == D3D_OK)
1176         {
1177             size = sizeof(decl2) / sizeof(decl2[0]);
1178             for (i = 0; i < size - 1; i++)
1179             {
1180                 ok(test_decl[i].Stream == decl2[i].Stream, "Returned stream %d, expected %d\n", test_decl[i].Stream, decl2[i].Stream);
1181                 ok(test_decl[i].Type == decl2[i].Type, "Returned type %d, expected %d\n", test_decl[i].Type, decl2[i].Type);
1182                 ok(test_decl[i].Method == decl2[i].Method, "Returned method %d, expected %d\n", test_decl[i].Method, decl2[i].Method);
1183                 ok(test_decl[i].Usage == decl2[i].Usage, "Returned usage %d, expected %d\n", test_decl[i].Usage, decl2[i].Usage);
1184                 ok(test_decl[i].UsageIndex == decl2[i].UsageIndex, "Returned usage index %d, expected %d\n", test_decl[i].UsageIndex, decl2[i].UsageIndex);
1185                 ok(test_decl[i].Offset == decl2[i].Offset, "Returned offset %d, expected %d\n", test_decl[i].Offset, decl2[i].Offset);
1186             }
1187             ok(decl2[size-1].Stream == 0xFF, "Returned too long vertex declaration\n"); /* end element */
1188         }
1189
1190         /* options */
1191         options = d3dxmesh->lpVtbl->GetOptions(d3dxmesh);
1192         ok(options == D3DXMESH_MANAGED, "Got result %x, expected %x (D3DXMESH_MANAGED)\n", options, D3DXMESH_MANAGED);
1193
1194         /* rest */
1195         if (!new_mesh(&mesh, 3, 1))
1196         {
1197             skip("Couldn't create mesh\n");
1198         }
1199         else
1200         {
1201             memset(mesh.vertices, 0, mesh.number_of_vertices * sizeof(*mesh.vertices));
1202             memset(mesh.faces, 0, mesh.number_of_faces * sizeof(*mesh.faces));
1203             mesh.fvf = 0;
1204             mesh.vertex_size = 60;
1205
1206             compare_mesh("createmesh2", d3dxmesh, &mesh);
1207
1208             free_mesh(&mesh);
1209         }
1210
1211         d3dxmesh->lpVtbl->Release(d3dxmesh);
1212     }
1213
1214     IDirect3DDevice9_Release(device);
1215     IDirect3D9_Release(d3d);
1216     DestroyWindow(wnd);
1217 }
1218
1219 static void D3DXCreateMeshFVFTest(void)
1220 {
1221     HRESULT hr;
1222     HWND wnd;
1223     IDirect3D9 *d3d;
1224     IDirect3DDevice9 *device, *test_device;
1225     D3DPRESENT_PARAMETERS d3dpp;
1226     ID3DXMesh *d3dxmesh;
1227     int i, size;
1228     D3DVERTEXELEMENT9 test_decl[MAX_FVF_DECL_SIZE];
1229     DWORD options;
1230     struct mesh mesh;
1231
1232     static const D3DVERTEXELEMENT9 decl[3] = {
1233         {0, 0, D3DDECLTYPE_FLOAT3, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_POSITION, 0},
1234         {0, 12, D3DDECLTYPE_FLOAT3, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_NORMAL, 0},
1235         D3DDECL_END(), };
1236
1237     hr = D3DXCreateMeshFVF(0, 0, 0, 0, NULL, NULL);
1238     ok(hr == D3DERR_INVALIDCALL, "Got result %x, expected %x (D3DERR_INVALIDCALL)\n", hr, D3DERR_INVALIDCALL);
1239
1240     hr = D3DXCreateMeshFVF(1, 3, D3DXMESH_MANAGED, D3DFVF_XYZ | D3DFVF_NORMAL, NULL, &d3dxmesh);
1241     ok(hr == D3DERR_INVALIDCALL, "Got result %x, expected %x (D3DERR_INVALIDCALL)\n", hr, D3DERR_INVALIDCALL);
1242
1243     wnd = CreateWindow("static", "d3dx9_test", 0, 0, 0, 0, 0, NULL, NULL, NULL, NULL);
1244     if (!wnd)
1245     {
1246         skip("Couldn't create application window\n");
1247         return;
1248     }
1249     d3d = Direct3DCreate9(D3D_SDK_VERSION);
1250     if (!d3d)
1251     {
1252         skip("Couldn't create IDirect3D9 object\n");
1253         DestroyWindow(wnd);
1254         return;
1255     }
1256
1257     ZeroMemory(&d3dpp, sizeof(d3dpp));
1258     d3dpp.Windowed = TRUE;
1259     d3dpp.SwapEffect = D3DSWAPEFFECT_DISCARD;
1260     hr = IDirect3D9_CreateDevice(d3d, D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, wnd, D3DCREATE_MIXED_VERTEXPROCESSING, &d3dpp, &device);
1261     if (FAILED(hr))
1262     {
1263         skip("Failed to create IDirect3DDevice9 object %#x\n", hr);
1264         IDirect3D9_Release(d3d);
1265         DestroyWindow(wnd);
1266         return;
1267     }
1268
1269     hr = D3DXCreateMeshFVF(0, 3, D3DXMESH_MANAGED, D3DFVF_XYZ | D3DFVF_NORMAL, device, &d3dxmesh);
1270     ok(hr == D3DERR_INVALIDCALL, "Got result %x, expected %x (D3DERR_INVALIDCALL)\n", hr, D3DERR_INVALIDCALL);
1271
1272     hr = D3DXCreateMeshFVF(1, 0, D3DXMESH_MANAGED, D3DFVF_XYZ | D3DFVF_NORMAL, device, &d3dxmesh);
1273     ok(hr == D3DERR_INVALIDCALL, "Got result %x, expected %x (D3DERR_INVALIDCALL)\n", hr, D3DERR_INVALIDCALL);
1274
1275     hr = D3DXCreateMeshFVF(1, 3, 0, D3DFVF_XYZ | D3DFVF_NORMAL, device, &d3dxmesh);
1276     ok(hr == D3D_OK, "Got result %x, expected %x (D3D_OK)\n", hr, D3D_OK);
1277
1278     if (hr == D3D_OK)
1279     {
1280         d3dxmesh->lpVtbl->Release(d3dxmesh);
1281     }
1282
1283     hr = D3DXCreateMeshFVF(1, 3, D3DXMESH_MANAGED, 0xdeadbeef, device, &d3dxmesh);
1284     ok(hr == D3DERR_INVALIDCALL, "Got result %x, expected %x (D3DERR_INVALIDCALL)\n", hr, D3DERR_INVALIDCALL);
1285
1286     hr = D3DXCreateMeshFVF(1, 3, D3DXMESH_MANAGED, D3DFVF_XYZ | D3DFVF_NORMAL, device, NULL);
1287     ok(hr == D3DERR_INVALIDCALL, "Got result %x, expected %x (D3DERR_INVALIDCALL)\n", hr, D3DERR_INVALIDCALL);
1288
1289     hr = D3DXCreateMeshFVF(1, 3, D3DXMESH_MANAGED, D3DFVF_XYZ | D3DFVF_NORMAL, device, &d3dxmesh);
1290     ok(hr == D3D_OK, "Got result %x, expected 0 (D3D_OK)\n", hr);
1291
1292     if (hr == D3D_OK)
1293     {
1294         /* device */
1295         hr = d3dxmesh->lpVtbl->GetDevice(d3dxmesh, NULL);
1296         ok(hr == D3DERR_INVALIDCALL, "Got result %x, expected %x (D3DERR_INVALIDCALL)\n", hr, D3DERR_INVALIDCALL);
1297
1298         hr = d3dxmesh->lpVtbl->GetDevice(d3dxmesh, &test_device);
1299         ok(hr == D3D_OK, "Got result %x, expected %x (D3D_OK)\n", hr, D3D_OK);
1300         ok(test_device == device, "Got result %p, expected %p\n", test_device, device);
1301
1302         if (hr == D3D_OK)
1303         {
1304             IDirect3DDevice9_Release(device);
1305         }
1306
1307         /* declaration */
1308         hr = d3dxmesh->lpVtbl->GetDeclaration(d3dxmesh, NULL);
1309         ok(hr == D3DERR_INVALIDCALL, "Got result %x, expected %x (D3DERR_INVALIDCALL)\n", hr, D3DERR_INVALIDCALL);
1310
1311         hr = d3dxmesh->lpVtbl->GetDeclaration(d3dxmesh, test_decl);
1312         ok(hr == D3D_OK, "Got result %x, expected 0 (D3D_OK)\n", hr);
1313
1314         if (hr == D3D_OK)
1315         {
1316             size = sizeof(decl) / sizeof(decl[0]);
1317             for (i = 0; i < size - 1; i++)
1318             {
1319                 ok(test_decl[i].Stream == decl[i].Stream, "Returned stream %d, expected %d\n", test_decl[i].Stream, decl[i].Stream);
1320                 ok(test_decl[i].Type == decl[i].Type, "Returned type %d, expected %d\n", test_decl[i].Type, decl[i].Type);
1321                 ok(test_decl[i].Method == decl[i].Method, "Returned method %d, expected %d\n", test_decl[i].Method, decl[i].Method);
1322                 ok(test_decl[i].Usage == decl[i].Usage, "Returned usage %d, expected %d\n", test_decl[i].Usage, decl[i].Usage);
1323                 ok(test_decl[i].UsageIndex == decl[i].UsageIndex, "Returned usage index %d, expected %d\n",
1324                    test_decl[i].UsageIndex, decl[i].UsageIndex);
1325                 ok(test_decl[i].Offset == decl[i].Offset, "Returned offset %d, expected %d\n", test_decl[i].Offset, decl[i].Offset);
1326             }
1327             ok(decl[size-1].Stream == 0xFF, "Returned too long vertex declaration\n"); /* end element */
1328         }
1329
1330         /* options */
1331         options = d3dxmesh->lpVtbl->GetOptions(d3dxmesh);
1332         ok(options == D3DXMESH_MANAGED, "Got result %x, expected %x (D3DXMESH_MANAGED)\n", options, D3DXMESH_MANAGED);
1333
1334         /* rest */
1335         if (!new_mesh(&mesh, 3, 1))
1336         {
1337             skip("Couldn't create mesh\n");
1338         }
1339         else
1340         {
1341             memset(mesh.vertices, 0, mesh.number_of_vertices * sizeof(*mesh.vertices));
1342             memset(mesh.faces, 0, mesh.number_of_faces * sizeof(*mesh.faces));
1343             mesh.fvf = D3DFVF_XYZ | D3DFVF_NORMAL;
1344
1345             compare_mesh("createmeshfvf", d3dxmesh, &mesh);
1346
1347             free_mesh(&mesh);
1348         }
1349
1350         d3dxmesh->lpVtbl->Release(d3dxmesh);
1351     }
1352
1353     IDirect3DDevice9_Release(device);
1354     IDirect3D9_Release(d3d);
1355     DestroyWindow(wnd);
1356 }
1357
1358 static void D3DXCreateBoxTest(void)
1359 {
1360     HRESULT hr;
1361     HWND wnd;
1362     WNDCLASS wc={0};
1363     IDirect3D9* d3d;
1364     IDirect3DDevice9* device;
1365     D3DPRESENT_PARAMETERS d3dpp;
1366     ID3DXMesh* box;
1367     ID3DXBuffer* ppBuffer;
1368     DWORD *buffer;
1369     static const DWORD adjacency[36]=
1370         {6, 9, 1, 2, 10, 0,
1371          1, 9, 3, 4, 10, 2,
1372          3, 8, 5, 7, 11, 4,
1373          0, 11, 7, 5, 8, 6,
1374          7, 4, 9, 2, 0, 8,
1375          1, 3, 11, 5, 6, 10};
1376     unsigned int i;
1377
1378     wc.lpfnWndProc = DefWindowProcA;
1379     wc.lpszClassName = "d3dx9_test_wc";
1380     if (!RegisterClass(&wc))
1381     {
1382         skip("RegisterClass failed\n");
1383         return;
1384     }
1385
1386     wnd = CreateWindow("d3dx9_test_wc", "d3dx9_test",
1387                         WS_SYSMENU | WS_POPUP , 0, 0, 640, 480, 0, 0, 0, 0);
1388     ok(wnd != NULL, "Expected to have a window, received NULL\n");
1389     if (!wnd)
1390     {
1391         skip("Couldn't create application window\n");
1392         return;
1393     }
1394
1395     d3d = Direct3DCreate9(D3D_SDK_VERSION);
1396     if (!d3d)
1397     {
1398         skip("Couldn't create IDirect3D9 object\n");
1399         DestroyWindow(wnd);
1400         return;
1401     }
1402
1403     memset(&d3dpp, 0, sizeof(d3dpp));
1404     d3dpp.Windowed = TRUE;
1405     d3dpp.SwapEffect = D3DSWAPEFFECT_DISCARD;
1406     hr = IDirect3D9_CreateDevice(d3d, D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, wnd, D3DCREATE_MIXED_VERTEXPROCESSING, &d3dpp, &device);
1407     if (FAILED(hr))
1408     {
1409         skip("Failed to create IDirect3DDevice9 object %#x\n", hr);
1410         IDirect3D9_Release(d3d);
1411         DestroyWindow(wnd);
1412         return;
1413     }
1414
1415     hr = D3DXCreateBuffer(36 * sizeof(DWORD), &ppBuffer);
1416     ok(hr==D3D_OK, "Expected D3D_OK, received %#x\n", hr);
1417     if (FAILED(hr)) goto end;
1418
1419     hr = D3DXCreateBox(device,2.0f,20.0f,4.9f,NULL, &ppBuffer);
1420     todo_wine ok(hr==D3DERR_INVALIDCALL, "Expected D3DERR_INVALIDCALL, received %#x\n", hr);
1421
1422     hr = D3DXCreateBox(NULL,22.0f,20.0f,4.9f,&box, &ppBuffer);
1423     todo_wine ok(hr==D3DERR_INVALIDCALL, "Expected D3DERR_INVALIDCALL, received %#x\n", hr);
1424
1425     hr = D3DXCreateBox(device,-2.0f,20.0f,4.9f,&box, &ppBuffer);
1426     todo_wine ok(hr==D3DERR_INVALIDCALL, "Expected D3DERR_INVALIDCALL, received %#x\n", hr);
1427
1428     hr = D3DXCreateBox(device,22.0f,-20.0f,4.9f,&box, &ppBuffer);
1429     todo_wine ok(hr==D3DERR_INVALIDCALL, "Expected D3DERR_INVALIDCALL, received %#x\n", hr);
1430
1431     hr = D3DXCreateBox(device,22.0f,20.0f,-4.9f,&box, &ppBuffer);
1432     todo_wine ok(hr==D3DERR_INVALIDCALL, "Expected D3DERR_INVALIDCALL, received %#x\n", hr);
1433
1434     hr = D3DXCreateBox(device,10.9f,20.0f,4.9f,&box, &ppBuffer);
1435     todo_wine ok(hr==D3D_OK, "Expected D3D_OK, received %#x\n", hr);
1436
1437     if (FAILED(hr))
1438     {
1439         skip("D3DXCreateBox failed\n");
1440         goto end;
1441     }
1442
1443     buffer = ID3DXBuffer_GetBufferPointer(ppBuffer);
1444     for(i=0; i<36; i++)
1445         todo_wine ok(adjacency[i]==buffer[i], "expected adjacency %d: %#x, received %#x\n",i,adjacency[i], buffer[i]);
1446
1447     box->lpVtbl->Release(box);
1448
1449 end:
1450     IDirect3DDevice9_Release(device);
1451     IDirect3D9_Release(d3d);
1452     ID3DXBuffer_Release(ppBuffer);
1453     DestroyWindow(wnd);
1454 }
1455
1456 struct sincos_table
1457 {
1458     float *sin;
1459     float *cos;
1460 };
1461
1462 static void free_sincos_table(struct sincos_table *sincos_table)
1463 {
1464     HeapFree(GetProcessHeap(), 0, sincos_table->cos);
1465     HeapFree(GetProcessHeap(), 0, sincos_table->sin);
1466 }
1467
1468 /* pre compute sine and cosine tables; caller must free */
1469 static BOOL compute_sincos_table(struct sincos_table *sincos_table, float angle_start, float angle_step, int n)
1470 {
1471     float angle;
1472     int i;
1473
1474     sincos_table->sin = HeapAlloc(GetProcessHeap(), 0, n * sizeof(*sincos_table->sin));
1475     if (!sincos_table->sin)
1476     {
1477         return FALSE;
1478     }
1479     sincos_table->cos = HeapAlloc(GetProcessHeap(), 0, n * sizeof(*sincos_table->cos));
1480     if (!sincos_table->cos)
1481     {
1482         HeapFree(GetProcessHeap(), 0, sincos_table->sin);
1483         return FALSE;
1484     }
1485
1486     angle = angle_start;
1487     for (i = 0; i < n; i++)
1488     {
1489         sincos_table->sin[i] = sin(angle);
1490         sincos_table->cos[i] = cos(angle);
1491         angle += angle_step;
1492     }
1493
1494     return TRUE;
1495 }
1496
1497 static WORD vertex_index(UINT slices, int slice, int stack)
1498 {
1499     return stack*slices+slice+1;
1500 }
1501
1502 /* slices = subdivisions along xy plane, stacks = subdivisions along z axis */
1503 static BOOL compute_sphere(struct mesh *mesh, FLOAT radius, UINT slices, UINT stacks)
1504 {
1505     float theta_step, theta_start;
1506     struct sincos_table theta;
1507     float phi_step, phi_start;
1508     struct sincos_table phi;
1509     DWORD number_of_vertices, number_of_faces;
1510     DWORD vertex, face;
1511     int slice, stack;
1512
1513     /* theta = angle on xy plane wrt x axis */
1514     theta_step = M_PI / stacks;
1515     theta_start = theta_step;
1516
1517     /* phi = angle on xz plane wrt z axis */
1518     phi_step = -2 * M_PI / slices;
1519     phi_start = M_PI / 2;
1520
1521     if (!compute_sincos_table(&theta, theta_start, theta_step, stacks))
1522     {
1523         return FALSE;
1524     }
1525     if (!compute_sincos_table(&phi, phi_start, phi_step, slices))
1526     {
1527         free_sincos_table(&theta);
1528         return FALSE;
1529     }
1530
1531     number_of_vertices = 2 + slices * (stacks-1);
1532     number_of_faces = 2 * slices + (stacks - 2) * (2 * slices);
1533
1534     if (!new_mesh(mesh, number_of_vertices, number_of_faces))
1535     {
1536         free_sincos_table(&phi);
1537         free_sincos_table(&theta);
1538         return FALSE;
1539     }
1540
1541     vertex = 0;
1542     face = 0;
1543
1544     mesh->vertices[vertex].normal.x = 0.0f;
1545     mesh->vertices[vertex].normal.y = 0.0f;
1546     mesh->vertices[vertex].normal.z = 1.0f;
1547     mesh->vertices[vertex].position.x = 0.0f;
1548     mesh->vertices[vertex].position.y = 0.0f;
1549     mesh->vertices[vertex].position.z = radius;
1550     vertex++;
1551
1552     for (stack = 0; stack < stacks - 1; stack++)
1553     {
1554         for (slice = 0; slice < slices; slice++)
1555         {
1556             mesh->vertices[vertex].normal.x = theta.sin[stack] * phi.cos[slice];
1557             mesh->vertices[vertex].normal.y = theta.sin[stack] * phi.sin[slice];
1558             mesh->vertices[vertex].normal.z = theta.cos[stack];
1559             mesh->vertices[vertex].position.x = radius * theta.sin[stack] * phi.cos[slice];
1560             mesh->vertices[vertex].position.y = radius * theta.sin[stack] * phi.sin[slice];
1561             mesh->vertices[vertex].position.z = radius * theta.cos[stack];
1562             vertex++;
1563
1564             if (slice > 0)
1565             {
1566                 if (stack == 0)
1567                 {
1568                     /* top stack is triangle fan */
1569                     mesh->faces[face][0] = 0;
1570                     mesh->faces[face][1] = slice + 1;
1571                     mesh->faces[face][2] = slice;
1572                     face++;
1573                 }
1574                 else
1575                 {
1576                     /* stacks in between top and bottom are quad strips */
1577                     mesh->faces[face][0] = vertex_index(slices, slice-1, stack-1);
1578                     mesh->faces[face][1] = vertex_index(slices, slice, stack-1);
1579                     mesh->faces[face][2] = vertex_index(slices, slice-1, stack);
1580                     face++;
1581
1582                     mesh->faces[face][0] = vertex_index(slices, slice, stack-1);
1583                     mesh->faces[face][1] = vertex_index(slices, slice, stack);
1584                     mesh->faces[face][2] = vertex_index(slices, slice-1, stack);
1585                     face++;
1586                 }
1587             }
1588         }
1589
1590         if (stack == 0)
1591         {
1592             mesh->faces[face][0] = 0;
1593             mesh->faces[face][1] = 1;
1594             mesh->faces[face][2] = slice;
1595             face++;
1596         }
1597         else
1598         {
1599             mesh->faces[face][0] = vertex_index(slices, slice-1, stack-1);
1600             mesh->faces[face][1] = vertex_index(slices, 0, stack-1);
1601             mesh->faces[face][2] = vertex_index(slices, slice-1, stack);
1602             face++;
1603
1604             mesh->faces[face][0] = vertex_index(slices, 0, stack-1);
1605             mesh->faces[face][1] = vertex_index(slices, 0, stack);
1606             mesh->faces[face][2] = vertex_index(slices, slice-1, stack);
1607             face++;
1608         }
1609     }
1610
1611     mesh->vertices[vertex].position.x = 0.0f;
1612     mesh->vertices[vertex].position.y = 0.0f;
1613     mesh->vertices[vertex].position.z = -radius;
1614     mesh->vertices[vertex].normal.x = 0.0f;
1615     mesh->vertices[vertex].normal.y = 0.0f;
1616     mesh->vertices[vertex].normal.z = -1.0f;
1617
1618     /* bottom stack is triangle fan */
1619     for (slice = 1; slice < slices; slice++)
1620     {
1621         mesh->faces[face][0] = vertex_index(slices, slice-1, stack-1);
1622         mesh->faces[face][1] = vertex_index(slices, slice, stack-1);
1623         mesh->faces[face][2] = vertex;
1624         face++;
1625     }
1626
1627     mesh->faces[face][0] = vertex_index(slices, slice-1, stack-1);
1628     mesh->faces[face][1] = vertex_index(slices, 0, stack-1);
1629     mesh->faces[face][2] = vertex;
1630
1631     free_sincos_table(&phi);
1632     free_sincos_table(&theta);
1633
1634     return TRUE;
1635 }
1636
1637 static void test_sphere(IDirect3DDevice9 *device, FLOAT radius, UINT slices, UINT stacks)
1638 {
1639     HRESULT hr;
1640     ID3DXMesh *sphere;
1641     struct mesh mesh;
1642     char name[256];
1643
1644     hr = D3DXCreateSphere(device, radius, slices, stacks, &sphere, NULL);
1645     ok(hr == D3D_OK, "Got result %x, expected 0 (D3D_OK)\n", hr);
1646     if (hr != D3D_OK)
1647     {
1648         skip("Couldn't create sphere\n");
1649         return;
1650     }
1651
1652     if (!compute_sphere(&mesh, radius, slices, stacks))
1653     {
1654         skip("Couldn't create mesh\n");
1655         sphere->lpVtbl->Release(sphere);
1656         return;
1657     }
1658
1659     mesh.fvf = D3DFVF_XYZ | D3DFVF_NORMAL;
1660
1661     sprintf(name, "sphere (%g, %u, %u)", radius, slices, stacks);
1662     compare_mesh(name, sphere, &mesh);
1663
1664     free_mesh(&mesh);
1665
1666     sphere->lpVtbl->Release(sphere);
1667 }
1668
1669 static void D3DXCreateSphereTest(void)
1670 {
1671     HRESULT hr;
1672     HWND wnd;
1673     IDirect3D9* d3d;
1674     IDirect3DDevice9* device;
1675     D3DPRESENT_PARAMETERS d3dpp;
1676     ID3DXMesh* sphere = NULL;
1677
1678     hr = D3DXCreateSphere(NULL, 0.0f, 0, 0, NULL, NULL);
1679     ok(hr == D3DERR_INVALIDCALL, "Got result %x, expected %x (D3DERR_INVALIDCALL)\n",hr,D3DERR_INVALIDCALL);
1680
1681     hr = D3DXCreateSphere(NULL, 0.1f, 0, 0, NULL, NULL);
1682     ok(hr == D3DERR_INVALIDCALL, "Got result %x, expected %x (D3DERR_INVALIDCALL)\n",hr,D3DERR_INVALIDCALL);
1683
1684     hr = D3DXCreateSphere(NULL, 0.0f, 1, 0, NULL, NULL);
1685     ok(hr == D3DERR_INVALIDCALL, "Got result %x, expected %x (D3DERR_INVALIDCALL)\n",hr,D3DERR_INVALIDCALL);
1686
1687     hr = D3DXCreateSphere(NULL, 0.0f, 0, 1, NULL, NULL);
1688     ok(hr == D3DERR_INVALIDCALL, "Got result %x, expected %x (D3DERR_INVALIDCALL)\n",hr,D3DERR_INVALIDCALL);
1689
1690     wnd = CreateWindow("static", "d3dx9_test", 0, 0, 0, 0, 0, NULL, NULL, NULL, NULL);
1691     d3d = Direct3DCreate9(D3D_SDK_VERSION);
1692     if (!wnd)
1693     {
1694         skip("Couldn't create application window\n");
1695         return;
1696     }
1697     if (!d3d)
1698     {
1699         skip("Couldn't create IDirect3D9 object\n");
1700         DestroyWindow(wnd);
1701         return;
1702     }
1703
1704     ZeroMemory(&d3dpp, sizeof(d3dpp));
1705     d3dpp.Windowed = TRUE;
1706     d3dpp.SwapEffect = D3DSWAPEFFECT_DISCARD;
1707     hr = IDirect3D9_CreateDevice(d3d, D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, wnd, D3DCREATE_MIXED_VERTEXPROCESSING, &d3dpp, &device);
1708     if (FAILED(hr))
1709     {
1710         skip("Failed to create IDirect3DDevice9 object %#x\n", hr);
1711         IDirect3D9_Release(d3d);
1712         DestroyWindow(wnd);
1713         return;
1714     }
1715
1716     hr = D3DXCreateSphere(device, 1.0f, 1, 1, &sphere, NULL);
1717     ok(hr == D3DERR_INVALIDCALL, "Got result %x, expected %x (D3DERR_INVALIDCALL)\n",hr,D3DERR_INVALIDCALL);
1718
1719     hr = D3DXCreateSphere(device, 1.0f, 2, 1, &sphere, NULL);
1720     ok(hr == D3DERR_INVALIDCALL, "Got result %x, expected %x (D3DERR_INVALIDCALL)\n", hr, D3DERR_INVALIDCALL);
1721
1722     hr = D3DXCreateSphere(device, 1.0f, 1, 2, &sphere, NULL);
1723     ok(hr == D3DERR_INVALIDCALL, "Got result %x, expected %x (D3DERR_INVALIDCALL)\n", hr, D3DERR_INVALIDCALL);
1724
1725     hr = D3DXCreateSphere(device, -0.1f, 1, 2, &sphere, NULL);
1726     ok(hr == D3DERR_INVALIDCALL, "Got result %x, expected %x (D3DERR_INVALIDCALL)\n", hr, D3DERR_INVALIDCALL);
1727
1728     test_sphere(device, 0.0f, 2, 2);
1729     test_sphere(device, 1.0f, 2, 2);
1730     test_sphere(device, 1.0f, 3, 2);
1731     test_sphere(device, 1.0f, 4, 4);
1732     test_sphere(device, 1.0f, 3, 4);
1733     test_sphere(device, 5.0f, 6, 7);
1734     test_sphere(device, 10.0f, 11, 12);
1735
1736     IDirect3DDevice9_Release(device);
1737     IDirect3D9_Release(d3d);
1738     DestroyWindow(wnd);
1739 }
1740
1741 static BOOL compute_cylinder(struct mesh *mesh, FLOAT radius1, FLOAT radius2, FLOAT length, UINT slices, UINT stacks)
1742 {
1743     float theta_step, theta_start;
1744     struct sincos_table theta;
1745     FLOAT delta_radius, radius, radius_step;
1746     FLOAT z, z_step, z_normal;
1747     DWORD number_of_vertices, number_of_faces;
1748     DWORD vertex, face;
1749     int slice, stack;
1750
1751     /* theta = angle on xy plane wrt x axis */
1752     theta_step = -2 * M_PI / slices;
1753     theta_start = M_PI / 2;
1754
1755     if (!compute_sincos_table(&theta, theta_start, theta_step, slices))
1756     {
1757         return FALSE;
1758     }
1759
1760     number_of_vertices = 2 + (slices * (3 + stacks));
1761     number_of_faces = 2 * slices + stacks * (2 * slices);
1762
1763     if (!new_mesh(mesh, number_of_vertices, number_of_faces))
1764     {
1765         free_sincos_table(&theta);
1766         return FALSE;
1767     }
1768
1769     vertex = 0;
1770     face = 0;
1771
1772     delta_radius = radius1 - radius2;
1773     radius = radius1;
1774     radius_step = delta_radius / stacks;
1775
1776     z = -length / 2;
1777     z_step = length / stacks;
1778     z_normal = delta_radius / length;
1779     if (isnan(z_normal))
1780     {
1781         z_normal = 0.0f;
1782     }
1783
1784     mesh->vertices[vertex].normal.x = 0.0f;
1785     mesh->vertices[vertex].normal.y = 0.0f;
1786     mesh->vertices[vertex].normal.z = -1.0f;
1787     mesh->vertices[vertex].position.x = 0.0f;
1788     mesh->vertices[vertex].position.y = 0.0f;
1789     mesh->vertices[vertex++].position.z = z;
1790
1791     for (slice = 0; slice < slices; slice++, vertex++)
1792     {
1793         mesh->vertices[vertex].normal.x = 0.0f;
1794         mesh->vertices[vertex].normal.y = 0.0f;
1795         mesh->vertices[vertex].normal.z = -1.0f;
1796         mesh->vertices[vertex].position.x = radius * theta.cos[slice];
1797         mesh->vertices[vertex].position.y = radius * theta.sin[slice];
1798         mesh->vertices[vertex].position.z = z;
1799
1800         if (slice > 0)
1801         {
1802             mesh->faces[face][0] = 0;
1803             mesh->faces[face][1] = slice;
1804             mesh->faces[face++][2] = slice + 1;
1805         }
1806     }
1807
1808     mesh->faces[face][0] = 0;
1809     mesh->faces[face][1] = slice;
1810     mesh->faces[face++][2] = 1;
1811
1812     for (stack = 1; stack <= stacks+1; stack++)
1813     {
1814         for (slice = 0; slice < slices; slice++, vertex++)
1815         {
1816             mesh->vertices[vertex].normal.x = theta.cos[slice];
1817             mesh->vertices[vertex].normal.y = theta.sin[slice];
1818             mesh->vertices[vertex].normal.z = z_normal;
1819             D3DXVec3Normalize(&mesh->vertices[vertex].normal, &mesh->vertices[vertex].normal);
1820             mesh->vertices[vertex].position.x = radius * theta.cos[slice];
1821             mesh->vertices[vertex].position.y = radius * theta.sin[slice];
1822             mesh->vertices[vertex].position.z = z;
1823
1824             if (stack > 1 && slice > 0)
1825             {
1826                 mesh->faces[face][0] = vertex_index(slices, slice-1, stack-1);
1827                 mesh->faces[face][1] = vertex_index(slices, slice-1, stack);
1828                 mesh->faces[face++][2] = vertex_index(slices, slice, stack-1);
1829
1830                 mesh->faces[face][0] = vertex_index(slices, slice, stack-1);
1831                 mesh->faces[face][1] = vertex_index(slices, slice-1, stack);
1832                 mesh->faces[face++][2] = vertex_index(slices, slice, stack);
1833             }
1834         }
1835
1836         if (stack > 1)
1837         {
1838             mesh->faces[face][0] = vertex_index(slices, slice-1, stack-1);
1839             mesh->faces[face][1] = vertex_index(slices, slice-1, stack);
1840             mesh->faces[face++][2] = vertex_index(slices, 0, stack-1);
1841
1842             mesh->faces[face][0] = vertex_index(slices, 0, stack-1);
1843             mesh->faces[face][1] = vertex_index(slices, slice-1, stack);
1844             mesh->faces[face++][2] = vertex_index(slices, 0, stack);
1845         }
1846
1847         if (stack < stacks + 1)
1848         {
1849             z += z_step;
1850             radius -= radius_step;
1851         }
1852     }
1853
1854     for (slice = 0; slice < slices; slice++, vertex++)
1855     {
1856         mesh->vertices[vertex].normal.x = 0.0f;
1857         mesh->vertices[vertex].normal.y = 0.0f;
1858         mesh->vertices[vertex].normal.z = 1.0f;
1859         mesh->vertices[vertex].position.x = radius * theta.cos[slice];
1860         mesh->vertices[vertex].position.y = radius * theta.sin[slice];
1861         mesh->vertices[vertex].position.z = z;
1862
1863         if (slice > 0)
1864         {
1865             mesh->faces[face][0] = vertex_index(slices, slice-1, stack);
1866             mesh->faces[face][1] = number_of_vertices - 1;
1867             mesh->faces[face++][2] = vertex_index(slices, slice, stack);
1868         }
1869     }
1870
1871     mesh->vertices[vertex].position.x = 0.0f;
1872     mesh->vertices[vertex].position.y = 0.0f;
1873     mesh->vertices[vertex].position.z = z;
1874     mesh->vertices[vertex].normal.x = 0.0f;
1875     mesh->vertices[vertex].normal.y = 0.0f;
1876     mesh->vertices[vertex].normal.z = 1.0f;
1877
1878     mesh->faces[face][0] = vertex_index(slices, slice-1, stack);
1879     mesh->faces[face][1] = number_of_vertices - 1;
1880     mesh->faces[face][2] = vertex_index(slices, 0, stack);
1881
1882     free_sincos_table(&theta);
1883
1884     return TRUE;
1885 }
1886
1887 static void test_cylinder(IDirect3DDevice9 *device, FLOAT radius1, FLOAT radius2, FLOAT length, UINT slices, UINT stacks)
1888 {
1889     HRESULT hr;
1890     ID3DXMesh *cylinder;
1891     struct mesh mesh;
1892     char name[256];
1893
1894     hr = D3DXCreateCylinder(device, radius1, radius2, length, slices, stacks, &cylinder, NULL);
1895     ok(hr == D3D_OK, "Got result %x, expected 0 (D3D_OK)\n", hr);
1896     if (hr != D3D_OK)
1897     {
1898         skip("Couldn't create cylinder\n");
1899         return;
1900     }
1901
1902     if (!compute_cylinder(&mesh, radius1, radius2, length, slices, stacks))
1903     {
1904         skip("Couldn't create mesh\n");
1905         cylinder->lpVtbl->Release(cylinder);
1906         return;
1907     }
1908
1909     mesh.fvf = D3DFVF_XYZ | D3DFVF_NORMAL;
1910
1911     sprintf(name, "cylinder (%g, %g, %g, %u, %u)", radius1, radius2, length, slices, stacks);
1912     compare_mesh(name, cylinder, &mesh);
1913
1914     free_mesh(&mesh);
1915
1916     cylinder->lpVtbl->Release(cylinder);
1917 }
1918
1919 static void D3DXCreateCylinderTest(void)
1920 {
1921     HRESULT hr;
1922     HWND wnd;
1923     IDirect3D9* d3d;
1924     IDirect3DDevice9* device;
1925     D3DPRESENT_PARAMETERS d3dpp;
1926     ID3DXMesh* cylinder = NULL;
1927
1928     hr = D3DXCreateCylinder(NULL, 0.0f, 0.0f, 0.0f, 0, 0, NULL, NULL);
1929     ok(hr == D3DERR_INVALIDCALL, "Got result %x, expected %x (D3DERR_INVALIDCALL)\n",hr,D3DERR_INVALIDCALL);
1930
1931     hr = D3DXCreateCylinder(NULL, 1.0f, 1.0f, 1.0f, 2, 1, &cylinder, NULL);
1932     ok(hr == D3DERR_INVALIDCALL, "Got result %x, expected %x (D3DERR_INVALIDCALL)\n",hr,D3DERR_INVALIDCALL);
1933
1934     wnd = CreateWindow("static", "d3dx9_test", 0, 0, 0, 0, 0, NULL, NULL, NULL, NULL);
1935     d3d = Direct3DCreate9(D3D_SDK_VERSION);
1936     if (!wnd)
1937     {
1938         skip("Couldn't create application window\n");
1939         return;
1940     }
1941     if (!d3d)
1942     {
1943         skip("Couldn't create IDirect3D9 object\n");
1944         DestroyWindow(wnd);
1945         return;
1946     }
1947
1948     ZeroMemory(&d3dpp, sizeof(d3dpp));
1949     d3dpp.Windowed = TRUE;
1950     d3dpp.SwapEffect = D3DSWAPEFFECT_DISCARD;
1951     hr = IDirect3D9_CreateDevice(d3d, D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, wnd, D3DCREATE_MIXED_VERTEXPROCESSING, &d3dpp, &device);
1952     if (FAILED(hr))
1953     {
1954         skip("Failed to create IDirect3DDevice9 object %#x\n", hr);
1955         IDirect3D9_Release(d3d);
1956         DestroyWindow(wnd);
1957         return;
1958     }
1959
1960     hr = D3DXCreateCylinder(device, -0.1f, 1.0f, 1.0f, 2, 1, &cylinder, NULL);
1961     ok(hr == D3DERR_INVALIDCALL, "Got result %x, expected %x (D3DERR_INVALIDCALL)\n",hr,D3DERR_INVALIDCALL);
1962
1963     hr = D3DXCreateCylinder(device, 0.0f, 1.0f, 1.0f, 2, 1, &cylinder, NULL);
1964     ok(hr == D3D_OK, "Got result %x, expected 0 (D3D_OK)\n",hr);
1965
1966     if (SUCCEEDED(hr) && cylinder)
1967     {
1968         cylinder->lpVtbl->Release(cylinder);
1969     }
1970
1971     hr = D3DXCreateCylinder(device, 1.0f, -0.1f, 1.0f, 2, 1, &cylinder, NULL);
1972     ok(hr == D3DERR_INVALIDCALL, "Got result %x, expected %x (D3DERR_INVALIDCALL)\n",hr,D3DERR_INVALIDCALL);
1973
1974     hr = D3DXCreateCylinder(device, 1.0f, 0.0f, 1.0f, 2, 1, &cylinder, NULL);
1975     ok(hr == D3D_OK, "Got result %x, expected 0 (D3D_OK)\n",hr);
1976
1977     if (SUCCEEDED(hr) && cylinder)
1978     {
1979         cylinder->lpVtbl->Release(cylinder);
1980     }
1981
1982     hr = D3DXCreateCylinder(device, 1.0f, 1.0f, -0.1f, 2, 1, &cylinder, NULL);
1983     ok(hr == D3DERR_INVALIDCALL, "Got result %x, expected %x (D3DERR_INVALIDCALL)\n",hr,D3DERR_INVALIDCALL);
1984
1985     /* Test with length == 0.0f succeeds */
1986     hr = D3DXCreateCylinder(device, 1.0f, 1.0f, 0.0f, 2, 1, &cylinder, NULL);
1987     ok(hr == D3D_OK, "Got result %x, expected 0 (D3D_OK)\n",hr);
1988
1989     if (SUCCEEDED(hr) && cylinder)
1990     {
1991         cylinder->lpVtbl->Release(cylinder);
1992     }
1993
1994     hr = D3DXCreateCylinder(device, 1.0f, 1.0f, 1.0f, 1, 1, &cylinder, NULL);
1995     ok(hr == D3DERR_INVALIDCALL, "Got result %x, expected %x (D3DERR_INVALIDCALL)\n",hr,D3DERR_INVALIDCALL);
1996
1997     hr = D3DXCreateCylinder(device, 1.0f, 1.0f, 1.0f, 2, 0, &cylinder, NULL);
1998     ok(hr == D3DERR_INVALIDCALL, "Got result %x, expected %x (D3DERR_INVALIDCALL)\n",hr,D3DERR_INVALIDCALL);
1999
2000     hr = D3DXCreateCylinder(device, 1.0f, 1.0f, 1.0f, 2, 1, NULL, NULL);
2001     ok(hr == D3DERR_INVALIDCALL, "Got result %x, expected %x (D3DERR_INVALIDCALL)\n",hr,D3DERR_INVALIDCALL);
2002
2003     test_cylinder(device, 0.0f, 0.0f, 0.0f, 2, 1);
2004     test_cylinder(device, 1.0f, 1.0f, 1.0f, 2, 1);
2005     test_cylinder(device, 1.0f, 1.0f, 2.0f, 3, 4);
2006     test_cylinder(device, 3.0f, 2.0f, 4.0f, 3, 4);
2007     test_cylinder(device, 2.0f, 3.0f, 4.0f, 3, 4);
2008     test_cylinder(device, 3.0f, 4.0f, 5.0f, 11, 20);
2009
2010     IDirect3DDevice9_Release(device);
2011     IDirect3D9_Release(d3d);
2012     DestroyWindow(wnd);
2013 }
2014
2015 struct dynamic_array
2016 {
2017     int count, capacity;
2018     void *items;
2019 };
2020
2021 enum pointtype {
2022     POINTTYPE_CURVE = 0,
2023     POINTTYPE_CORNER,
2024     POINTTYPE_CURVE_START,
2025     POINTTYPE_CURVE_END,
2026     POINTTYPE_CURVE_MIDDLE,
2027 };
2028
2029 struct point2d
2030 {
2031     D3DXVECTOR2 pos;
2032     enum pointtype corner;
2033 };
2034
2035 /* is a dynamic_array */
2036 struct outline
2037 {
2038     int count, capacity;
2039     struct point2d *items;
2040 };
2041
2042 /* is a dynamic_array */
2043 struct outline_array
2044 {
2045     int count, capacity;
2046     struct outline *items;
2047 };
2048
2049 struct glyphinfo
2050 {
2051     struct outline_array outlines;
2052     float offset_x;
2053 };
2054
2055 static BOOL reserve(struct dynamic_array *array, int count, int itemsize)
2056 {
2057     if (count > array->capacity) {
2058         void *new_buffer;
2059         int new_capacity;
2060         if (array->items && array->capacity) {
2061             new_capacity = max(array->capacity * 2, count);
2062             new_buffer = HeapReAlloc(GetProcessHeap(), 0, array->items, new_capacity * itemsize);
2063         } else {
2064             new_capacity = max(16, count);
2065             new_buffer = HeapAlloc(GetProcessHeap(), 0, new_capacity * itemsize);
2066         }
2067         if (!new_buffer)
2068             return FALSE;
2069         array->items = new_buffer;
2070         array->capacity = new_capacity;
2071     }
2072     return TRUE;
2073 }
2074
2075 static struct point2d *add_point(struct outline *array)
2076 {
2077     struct point2d *item;
2078
2079     if (!reserve((struct dynamic_array *)array, array->count + 1, sizeof(array->items[0])))
2080         return NULL;
2081
2082     item = &array->items[array->count++];
2083     ZeroMemory(item, sizeof(*item));
2084     return item;
2085 }
2086
2087 static struct outline *add_outline(struct outline_array *array)
2088 {
2089     struct outline *item;
2090
2091     if (!reserve((struct dynamic_array *)array, array->count + 1, sizeof(array->items[0])))
2092         return NULL;
2093
2094     item = &array->items[array->count++];
2095     ZeroMemory(item, sizeof(*item));
2096     return item;
2097 }
2098
2099 static inline D3DXVECTOR2 *convert_fixed_to_float(POINTFX *pt, int count, float emsquare)
2100 {
2101     D3DXVECTOR2 *ret = (D3DXVECTOR2*)pt;
2102     while (count--) {
2103         D3DXVECTOR2 *pt_flt = (D3DXVECTOR2*)pt;
2104         pt_flt->x = (pt->x.value + pt->x.fract / (float)0x10000) / emsquare;
2105         pt_flt->y = (pt->y.value + pt->y.fract / (float)0x10000) / emsquare;
2106         pt++;
2107     }
2108     return ret;
2109 }
2110
2111 static HRESULT add_bezier_points(struct outline *outline, const D3DXVECTOR2 *p1,
2112                                  const D3DXVECTOR2 *p2, const D3DXVECTOR2 *p3,
2113                                  float max_deviation)
2114 {
2115     D3DXVECTOR2 split1 = {0, 0}, split2 = {0, 0}, middle, vec;
2116     float deviation;
2117
2118     D3DXVec2Scale(&split1, D3DXVec2Add(&split1, p1, p2), 0.5f);
2119     D3DXVec2Scale(&split2, D3DXVec2Add(&split2, p2, p3), 0.5f);
2120     D3DXVec2Scale(&middle, D3DXVec2Add(&middle, &split1, &split2), 0.5f);
2121
2122     deviation = D3DXVec2Length(D3DXVec2Subtract(&vec, &middle, p2));
2123     if (deviation < max_deviation) {
2124         struct point2d *pt = add_point(outline);
2125         if (!pt) return E_OUTOFMEMORY;
2126         pt->pos = *p2;
2127         pt->corner = POINTTYPE_CURVE;
2128         /* the end point is omitted because the end line merges into the next segment of
2129          * the split bezier curve, and the end of the split bezier curve is added outside
2130          * this recursive function. */
2131     } else {
2132         HRESULT hr = add_bezier_points(outline, p1, &split1, &middle, max_deviation);
2133         if (hr != S_OK) return hr;
2134         hr = add_bezier_points(outline, &middle, &split2, p3, max_deviation);
2135         if (hr != S_OK) return hr;
2136     }
2137
2138     return S_OK;
2139 }
2140
2141 static inline BOOL is_direction_similar(D3DXVECTOR2 *dir1, D3DXVECTOR2 *dir2, float cos_theta)
2142 {
2143     /* dot product = cos(theta) */
2144     return D3DXVec2Dot(dir1, dir2) > cos_theta;
2145 }
2146
2147 static inline D3DXVECTOR2 *unit_vec2(D3DXVECTOR2 *dir, const D3DXVECTOR2 *pt1, const D3DXVECTOR2 *pt2)
2148 {
2149     return D3DXVec2Normalize(D3DXVec2Subtract(dir, pt2, pt1), dir);
2150 }
2151
2152 static BOOL attempt_line_merge(struct outline *outline,
2153                                int pt_index,
2154                                const D3DXVECTOR2 *nextpt,
2155                                BOOL to_curve)
2156 {
2157     D3DXVECTOR2 curdir, lastdir;
2158     struct point2d *prevpt, *pt;
2159     BOOL ret = FALSE;
2160     const float cos_half = cos(D3DXToRadian(0.5f));
2161
2162     pt = &outline->items[pt_index];
2163     pt_index = (pt_index - 1 + outline->count) % outline->count;
2164     prevpt = &outline->items[pt_index];
2165
2166     if (to_curve)
2167         pt->corner = pt->corner != POINTTYPE_CORNER ? POINTTYPE_CURVE_MIDDLE : POINTTYPE_CURVE_START;
2168
2169     if (outline->count < 2)
2170         return FALSE;
2171
2172     /* remove last point if the next line continues the last line */
2173     unit_vec2(&lastdir, &prevpt->pos, &pt->pos);
2174     unit_vec2(&curdir, &pt->pos, nextpt);
2175     if (is_direction_similar(&lastdir, &curdir, cos_half))
2176     {
2177         outline->count--;
2178         if (pt->corner == POINTTYPE_CURVE_END)
2179             prevpt->corner = pt->corner;
2180         if (prevpt->corner == POINTTYPE_CURVE_END && to_curve)
2181             prevpt->corner = POINTTYPE_CURVE_MIDDLE;
2182         pt = prevpt;
2183
2184         ret = TRUE;
2185         if (outline->count < 2)
2186             return ret;
2187
2188         pt_index = (pt_index - 1 + outline->count) % outline->count;
2189         prevpt = &outline->items[pt_index];
2190         unit_vec2(&lastdir, &prevpt->pos, &pt->pos);
2191         unit_vec2(&curdir, &pt->pos, nextpt);
2192     }
2193     return ret;
2194 }
2195
2196 static HRESULT create_outline(struct glyphinfo *glyph, void *raw_outline, int datasize,
2197                               float max_deviation, float emsquare)
2198 {
2199     const float cos_45 = cos(D3DXToRadian(45.0f));
2200     const float cos_90 = cos(D3DXToRadian(90.0f));
2201     TTPOLYGONHEADER *header = (TTPOLYGONHEADER *)raw_outline;
2202
2203     while ((char *)header < (char *)raw_outline + datasize)
2204     {
2205         TTPOLYCURVE *curve = (TTPOLYCURVE *)(header + 1);
2206         struct point2d *lastpt, *pt;
2207         D3DXVECTOR2 lastdir;
2208         D3DXVECTOR2 *pt_flt;
2209         int j;
2210         struct outline *outline = add_outline(&glyph->outlines);
2211
2212         if (!outline)
2213             return E_OUTOFMEMORY;
2214
2215         pt = add_point(outline);
2216         if (!pt)
2217             return E_OUTOFMEMORY;
2218         pt_flt = convert_fixed_to_float(&header->pfxStart, 1, emsquare);
2219         pt->pos = *pt_flt;
2220         pt->corner = POINTTYPE_CORNER;
2221
2222         if (header->dwType != TT_POLYGON_TYPE)
2223             trace("Unknown header type %d\n", header->dwType);
2224
2225         while ((char *)curve < (char *)header + header->cb)
2226         {
2227             D3DXVECTOR2 bezier_start = outline->items[outline->count - 1].pos;
2228             BOOL to_curve = curve->wType != TT_PRIM_LINE && curve->cpfx > 1;
2229
2230             if (!curve->cpfx) {
2231                 curve = (TTPOLYCURVE *)&curve->apfx[curve->cpfx];
2232                 continue;
2233             }
2234
2235             pt_flt = convert_fixed_to_float(curve->apfx, curve->cpfx, emsquare);
2236
2237             attempt_line_merge(outline, outline->count - 1, &pt_flt[0], to_curve);
2238
2239             if (to_curve)
2240             {
2241                 HRESULT hr;
2242                 int count = curve->cpfx;
2243                 j = 0;
2244
2245                 while (count > 2)
2246                 {
2247                     D3DXVECTOR2 bezier_end;
2248
2249                     D3DXVec2Scale(&bezier_end, D3DXVec2Add(&bezier_end, &pt_flt[j], &pt_flt[j+1]), 0.5f);
2250                     hr = add_bezier_points(outline, &bezier_start, &pt_flt[j], &bezier_end, max_deviation);
2251                     if (hr != S_OK)
2252                         return hr;
2253                     bezier_start = bezier_end;
2254                     count--;
2255                     j++;
2256                 }
2257                 hr = add_bezier_points(outline, &bezier_start, &pt_flt[j], &pt_flt[j+1], max_deviation);
2258                 if (hr != S_OK)
2259                     return hr;
2260
2261                 pt = add_point(outline);
2262                 if (!pt)
2263                     return E_OUTOFMEMORY;
2264                 j++;
2265                 pt->pos = pt_flt[j];
2266                 pt->corner = POINTTYPE_CURVE_END;
2267             } else {
2268                 for (j = 0; j < curve->cpfx; j++)
2269                 {
2270                     pt = add_point(outline);
2271                     if (!pt)
2272                         return E_OUTOFMEMORY;
2273                     pt->pos = pt_flt[j];
2274                     pt->corner = POINTTYPE_CORNER;
2275                 }
2276             }
2277
2278             curve = (TTPOLYCURVE *)&curve->apfx[curve->cpfx];
2279         }
2280
2281         /* remove last point if the next line continues the last line */
2282         if (outline->count >= 3) {
2283             BOOL to_curve;
2284
2285             lastpt = &outline->items[outline->count - 1];
2286             pt = &outline->items[0];
2287             if (pt->pos.x == lastpt->pos.x && pt->pos.y == lastpt->pos.y) {
2288                 if (lastpt->corner == POINTTYPE_CURVE_END)
2289                 {
2290                     if (pt->corner == POINTTYPE_CURVE_START)
2291                         pt->corner = POINTTYPE_CURVE_MIDDLE;
2292                     else
2293                         pt->corner = POINTTYPE_CURVE_END;
2294                 }
2295                 outline->count--;
2296                 lastpt = &outline->items[outline->count - 1];
2297             } else {
2298                 /* outline closed with a line from end to start point */
2299                 attempt_line_merge(outline, outline->count - 1, &pt->pos, FALSE);
2300             }
2301             lastpt = &outline->items[0];
2302             to_curve = lastpt->corner != POINTTYPE_CORNER && lastpt->corner != POINTTYPE_CURVE_END;
2303             if (lastpt->corner == POINTTYPE_CURVE_START)
2304                 lastpt->corner = POINTTYPE_CORNER;
2305             pt = &outline->items[1];
2306             if (attempt_line_merge(outline, 0, &pt->pos, to_curve))
2307                 *lastpt = outline->items[outline->count];
2308         }
2309
2310         lastpt = &outline->items[outline->count - 1];
2311         pt = &outline->items[0];
2312         unit_vec2(&lastdir, &lastpt->pos, &pt->pos);
2313         for (j = 0; j < outline->count; j++)
2314         {
2315             D3DXVECTOR2 curdir;
2316
2317             lastpt = pt;
2318             pt = &outline->items[(j + 1) % outline->count];
2319             unit_vec2(&curdir, &lastpt->pos, &pt->pos);
2320
2321             switch (lastpt->corner)
2322             {
2323                 case POINTTYPE_CURVE_START:
2324                 case POINTTYPE_CURVE_END:
2325                     if (!is_direction_similar(&lastdir, &curdir, cos_45))
2326                         lastpt->corner = POINTTYPE_CORNER;
2327                     break;
2328                 case POINTTYPE_CURVE_MIDDLE:
2329                     if (!is_direction_similar(&lastdir, &curdir, cos_90))
2330                         lastpt->corner = POINTTYPE_CORNER;
2331                     else
2332                         lastpt->corner = POINTTYPE_CURVE;
2333                     break;
2334                 default:
2335                     break;
2336             }
2337             lastdir = curdir;
2338         }
2339
2340         header = (TTPOLYGONHEADER *)((char *)header + header->cb);
2341     }
2342     return S_OK;
2343 }
2344
2345 static BOOL compute_text_mesh(struct mesh *mesh, HDC hdc, LPCSTR text, FLOAT deviation, FLOAT extrusion, FLOAT otmEMSquare)
2346 {
2347     HRESULT hr = E_FAIL;
2348     DWORD nb_vertices, nb_faces;
2349     DWORD nb_corners, nb_outline_points;
2350     int textlen = 0;
2351     float offset_x;
2352     char *raw_outline = NULL;
2353     struct glyphinfo *glyphs = NULL;
2354     GLYPHMETRICS gm;
2355     int i;
2356     struct vertex *vertex_ptr;
2357     face *face_ptr;
2358
2359     if (deviation == 0.0f)
2360         deviation = 1.0f / otmEMSquare;
2361
2362     textlen = strlen(text);
2363     glyphs = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, textlen * sizeof(*glyphs));
2364     if (!glyphs) {
2365         hr = E_OUTOFMEMORY;
2366         goto error;
2367     }
2368
2369     offset_x = 0.0f;
2370     for (i = 0; i < textlen; i++)
2371     {
2372         /* get outline points from data returned from GetGlyphOutline */
2373         const MAT2 identity = {{0, 1}, {0, 0}, {0, 0}, {0, 1}};
2374         int datasize;
2375
2376         glyphs[i].offset_x = offset_x;
2377
2378         datasize = GetGlyphOutline(hdc, text[i], GGO_NATIVE, &gm, 0, NULL, &identity);
2379         if (datasize < 0) {
2380             hr = E_FAIL;
2381             goto error;
2382         }
2383         HeapFree(GetProcessHeap(), 0, raw_outline);
2384         raw_outline = HeapAlloc(GetProcessHeap(), 0, datasize);
2385         if (!glyphs) {
2386             hr = E_OUTOFMEMORY;
2387             goto error;
2388         }
2389         datasize = GetGlyphOutline(hdc, text[i], GGO_NATIVE, &gm, datasize, raw_outline, &identity);
2390
2391         create_outline(&glyphs[i], raw_outline, datasize, deviation, otmEMSquare);
2392
2393         offset_x += gm.gmCellIncX / (float)otmEMSquare;
2394     }
2395
2396     /* corner points need an extra vertex for the different side faces normals */
2397     nb_corners = 0;
2398     nb_outline_points = 0;
2399     for (i = 0; i < textlen; i++)
2400     {
2401         int j;
2402         for (j = 0; j < glyphs[i].outlines.count; j++)
2403         {
2404             int k;
2405             struct outline *outline = &glyphs[i].outlines.items[j];
2406             nb_outline_points += outline->count;
2407             nb_corners++; /* first outline point always repeated as a corner */
2408             for (k = 1; k < outline->count; k++)
2409                 if (outline->items[k].corner)
2410                     nb_corners++;
2411         }
2412     }
2413
2414     nb_vertices = (nb_outline_points + nb_corners) * 2 + textlen;
2415     nb_faces = nb_outline_points * 2;
2416
2417     if (!new_mesh(mesh, nb_vertices, nb_faces))
2418         goto error;
2419
2420     /* convert 2D vertices and faces into 3D mesh */
2421     vertex_ptr = mesh->vertices;
2422     face_ptr = mesh->faces;
2423     for (i = 0; i < textlen; i++)
2424     {
2425         int j;
2426
2427         /* side vertices and faces */
2428         for (j = 0; j < glyphs[i].outlines.count; j++)
2429         {
2430             struct vertex *outline_vertices = vertex_ptr;
2431             struct outline *outline = &glyphs[i].outlines.items[j];
2432             int k;
2433             struct point2d *prevpt = &outline->items[outline->count - 1];
2434             struct point2d *pt = &outline->items[0];
2435
2436             for (k = 1; k <= outline->count; k++)
2437             {
2438                 struct vertex vtx;
2439                 struct point2d *nextpt = &outline->items[k % outline->count];
2440                 WORD vtx_idx = vertex_ptr - mesh->vertices;
2441                 D3DXVECTOR2 vec;
2442
2443                 if (pt->corner == POINTTYPE_CURVE_START)
2444                     D3DXVec2Subtract(&vec, &pt->pos, &prevpt->pos);
2445                 else if (pt->corner)
2446                     D3DXVec2Subtract(&vec, &nextpt->pos, &pt->pos);
2447                 else
2448                     D3DXVec2Subtract(&vec, &nextpt->pos, &prevpt->pos);
2449                 D3DXVec2Normalize(&vec, &vec);
2450                 vtx.normal.x = -vec.y;
2451                 vtx.normal.y = vec.x;
2452                 vtx.normal.z = 0;
2453
2454                 vtx.position.x = pt->pos.x + glyphs[i].offset_x;
2455                 vtx.position.y = pt->pos.y;
2456                 vtx.position.z = 0;
2457                 *vertex_ptr++ = vtx;
2458
2459                 vtx.position.z = -extrusion;
2460                 *vertex_ptr++ = vtx;
2461
2462                 vtx.position.x = nextpt->pos.x + glyphs[i].offset_x;
2463                 vtx.position.y = nextpt->pos.y;
2464                 if (pt->corner && nextpt->corner && nextpt->corner != POINTTYPE_CURVE_END) {
2465                     vtx.position.z = -extrusion;
2466                     *vertex_ptr++ = vtx;
2467                     vtx.position.z = 0;
2468                     *vertex_ptr++ = vtx;
2469
2470                     (*face_ptr)[0] = vtx_idx;
2471                     (*face_ptr)[1] = vtx_idx + 2;
2472                     (*face_ptr)[2] = vtx_idx + 1;
2473                     face_ptr++;
2474
2475                     (*face_ptr)[0] = vtx_idx;
2476                     (*face_ptr)[1] = vtx_idx + 3;
2477                     (*face_ptr)[2] = vtx_idx + 2;
2478                     face_ptr++;
2479                 } else {
2480                     if (nextpt->corner) {
2481                         if (nextpt->corner == POINTTYPE_CURVE_END) {
2482                             struct point2d *nextpt2 = &outline->items[(k + 1) % outline->count];
2483                             D3DXVec2Subtract(&vec, &nextpt2->pos, &nextpt->pos);
2484                         } else {
2485                             D3DXVec2Subtract(&vec, &nextpt->pos, &pt->pos);
2486                         }
2487                         D3DXVec2Normalize(&vec, &vec);
2488                         vtx.normal.x = -vec.y;
2489                         vtx.normal.y = vec.x;
2490
2491                         vtx.position.z = 0;
2492                         *vertex_ptr++ = vtx;
2493                         vtx.position.z = -extrusion;
2494                         *vertex_ptr++ = vtx;
2495                     }
2496
2497                     (*face_ptr)[0] = vtx_idx;
2498                     (*face_ptr)[1] = vtx_idx + 3;
2499                     (*face_ptr)[2] = vtx_idx + 1;
2500                     face_ptr++;
2501
2502                     (*face_ptr)[0] = vtx_idx;
2503                     (*face_ptr)[1] = vtx_idx + 2;
2504                     (*face_ptr)[2] = vtx_idx + 3;
2505                     face_ptr++;
2506                 }
2507
2508                 prevpt = pt;
2509                 pt = nextpt;
2510             }
2511             if (!pt->corner) {
2512                 *vertex_ptr++ = *outline_vertices++;
2513                 *vertex_ptr++ = *outline_vertices++;
2514             }
2515         }
2516
2517         /* FIXME: compute expected faces */
2518         /* Add placeholder to seperate glyph outlines */
2519         vertex_ptr->position.x = 0;
2520         vertex_ptr->position.y = 0;
2521         vertex_ptr->position.z = 0;
2522         vertex_ptr->normal.x = 0;
2523         vertex_ptr->normal.y = 0;
2524         vertex_ptr->normal.z = 1;
2525         vertex_ptr++;
2526     }
2527
2528     hr = D3D_OK;
2529 error:
2530     if (glyphs) {
2531         for (i = 0; i < textlen; i++)
2532         {
2533             int j;
2534             for (j = 0; j < glyphs[i].outlines.count; j++)
2535                 HeapFree(GetProcessHeap(), 0, glyphs[i].outlines.items[j].items);
2536             HeapFree(GetProcessHeap(), 0, glyphs[i].outlines.items);
2537         }
2538         HeapFree(GetProcessHeap(), 0, glyphs);
2539     }
2540     HeapFree(GetProcessHeap(), 0, raw_outline);
2541
2542     return hr == D3D_OK;
2543 }
2544
2545 static void compare_text_outline_mesh(const char *name, ID3DXMesh *d3dxmesh, struct mesh *mesh, int textlen)
2546 {
2547     HRESULT hr;
2548     DWORD number_of_vertices, number_of_faces;
2549     IDirect3DVertexBuffer9 *vertex_buffer = NULL;
2550     IDirect3DIndexBuffer9 *index_buffer = NULL;
2551     D3DVERTEXBUFFER_DESC vertex_buffer_description;
2552     D3DINDEXBUFFER_DESC index_buffer_description;
2553     struct vertex *vertices = NULL;
2554     face *faces = NULL;
2555     int expected, i;
2556     int vtx_idx1, face_idx1, vtx_idx2, face_idx2;
2557
2558     number_of_vertices = d3dxmesh->lpVtbl->GetNumVertices(d3dxmesh);
2559     number_of_faces = d3dxmesh->lpVtbl->GetNumFaces(d3dxmesh);
2560
2561     /* vertex buffer */
2562     hr = d3dxmesh->lpVtbl->GetVertexBuffer(d3dxmesh, &vertex_buffer);
2563     ok(hr == D3D_OK, "Test %s, result %x, expected 0 (D3D_OK)\n", name, hr);
2564     if (hr != D3D_OK)
2565     {
2566         skip("Couldn't get vertex buffers\n");
2567         goto error;
2568     }
2569
2570     hr = IDirect3DVertexBuffer9_GetDesc(vertex_buffer, &vertex_buffer_description);
2571     ok(hr == D3D_OK, "Test %s, result %x, expected 0 (D3D_OK)\n", name, hr);
2572
2573     if (hr != D3D_OK)
2574     {
2575         skip("Couldn't get vertex buffer description\n");
2576     }
2577     else
2578     {
2579         ok(vertex_buffer_description.Format == D3DFMT_VERTEXDATA, "Test %s, result %x, expected %x (D3DFMT_VERTEXDATA)\n",
2580            name, vertex_buffer_description.Format, D3DFMT_VERTEXDATA);
2581         ok(vertex_buffer_description.Type == D3DRTYPE_VERTEXBUFFER, "Test %s, result %x, expected %x (D3DRTYPE_VERTEXBUFFER)\n",
2582            name, vertex_buffer_description.Type, D3DRTYPE_VERTEXBUFFER);
2583         ok(vertex_buffer_description.Usage == 0, "Test %s, result %x, expected %x\n", name, vertex_buffer_description.Usage, 0);
2584         ok(vertex_buffer_description.Pool == D3DPOOL_MANAGED, "Test %s, result %x, expected %x (D3DPOOL_DEFAULT)\n",
2585            name, vertex_buffer_description.Pool, D3DPOOL_DEFAULT);
2586         ok(vertex_buffer_description.FVF == mesh->fvf, "Test %s, result %x, expected %x\n",
2587            name, vertex_buffer_description.FVF, mesh->fvf);
2588         if (mesh->fvf == 0)
2589         {
2590             expected = number_of_vertices * mesh->vertex_size;
2591         }
2592         else
2593         {
2594             expected = number_of_vertices * D3DXGetFVFVertexSize(mesh->fvf);
2595         }
2596         ok(vertex_buffer_description.Size == expected, "Test %s, result %x, expected %x\n",
2597            name, vertex_buffer_description.Size, expected);
2598     }
2599
2600     hr = d3dxmesh->lpVtbl->GetIndexBuffer(d3dxmesh, &index_buffer);
2601     ok(hr == D3D_OK, "Test %s, result %x, expected 0 (D3D_OK)\n", name, hr);
2602     if (hr != D3D_OK)
2603     {
2604         skip("Couldn't get index buffer\n");
2605         goto error;
2606     }
2607
2608     hr = IDirect3DIndexBuffer9_GetDesc(index_buffer, &index_buffer_description);
2609     ok(hr == D3D_OK, "Test %s, result %x, expected 0 (D3D_OK)\n", name, hr);
2610
2611     if (hr != D3D_OK)
2612     {
2613         skip("Couldn't get index buffer description\n");
2614     }
2615     else
2616     {
2617         ok(index_buffer_description.Format == D3DFMT_INDEX16, "Test %s, result %x, expected %x (D3DFMT_INDEX16)\n",
2618            name, index_buffer_description.Format, D3DFMT_INDEX16);
2619         ok(index_buffer_description.Type == D3DRTYPE_INDEXBUFFER, "Test %s, result %x, expected %x (D3DRTYPE_INDEXBUFFER)\n",
2620            name, index_buffer_description.Type, D3DRTYPE_INDEXBUFFER);
2621         todo_wine ok(index_buffer_description.Usage == 0, "Test %s, result %x, expected %x\n", name, index_buffer_description.Usage, 0);
2622         ok(index_buffer_description.Pool == D3DPOOL_MANAGED, "Test %s, result %x, expected %x (D3DPOOL_DEFAULT)\n",
2623            name, index_buffer_description.Pool, D3DPOOL_DEFAULT);
2624         expected = number_of_faces * sizeof(WORD) * 3;
2625         ok(index_buffer_description.Size == expected, "Test %s, result %x, expected %x\n",
2626            name, index_buffer_description.Size, expected);
2627     }
2628
2629     /* specify offset and size to avoid potential overruns */
2630     hr = IDirect3DVertexBuffer9_Lock(vertex_buffer, 0, number_of_vertices * sizeof(D3DXVECTOR3) * 2,
2631                                      (LPVOID *)&vertices, D3DLOCK_DISCARD);
2632     ok(hr == D3D_OK, "Test %s, result %x, expected 0 (D3D_OK)\n", name, hr);
2633     if (hr != D3D_OK)
2634     {
2635         skip("Couldn't lock vertex buffer\n");
2636         goto error;
2637     }
2638     hr = IDirect3DIndexBuffer9_Lock(index_buffer, 0, number_of_faces * sizeof(WORD) * 3,
2639                                     (LPVOID *)&faces, D3DLOCK_DISCARD);
2640     ok(hr == D3D_OK, "Test %s, result %x, expected 0 (D3D_OK)\n", name, hr);
2641     if (hr != D3D_OK)
2642     {
2643         skip("Couldn't lock index buffer\n");
2644         goto error;
2645     }
2646
2647     face_idx1 = 0;
2648     vtx_idx2 = 0;
2649     face_idx2 = 0;
2650     vtx_idx1 = 0;
2651     for (i = 0; i < textlen; i++)
2652     {
2653         int nb_outline_vertices1, nb_outline_faces1;
2654         int nb_outline_vertices2, nb_outline_faces2;
2655         int first_vtx1, first_vtx2;
2656         int first_face1, first_face2;
2657         int j;
2658
2659         first_vtx1 = vtx_idx1;
2660         first_vtx2 = vtx_idx2;
2661         for (; vtx_idx1 < number_of_vertices; vtx_idx1++) {
2662             if (vertices[vtx_idx1].normal.z != 0)
2663                 break;
2664         }
2665         for (; vtx_idx2 < mesh->number_of_vertices; vtx_idx2++) {
2666             if (mesh->vertices[vtx_idx2].normal.z != 0)
2667                 break;
2668         }
2669         nb_outline_vertices1 = vtx_idx1 - first_vtx1;
2670         nb_outline_vertices2 = vtx_idx2 - first_vtx2;
2671         ok(nb_outline_vertices1 == nb_outline_vertices2,
2672            "Test %s, glyph %d, outline vertex count result %d, expected %d\n", name, i,
2673            nb_outline_vertices1, nb_outline_vertices2);
2674
2675         for (j = 0; j < min(nb_outline_vertices1, nb_outline_vertices2); j++)
2676         {
2677             vtx_idx1 = first_vtx1 + j;
2678             vtx_idx2 = first_vtx2 + j;
2679             ok(compare_vec3(vertices[vtx_idx1].position, mesh->vertices[vtx_idx2].position),
2680                "Test %s, glyph %d, vertex position %d, result (%g, %g, %g), expected (%g, %g, %g)\n", name, i, vtx_idx1,
2681                vertices[vtx_idx1].position.x, vertices[vtx_idx1].position.y, vertices[vtx_idx1].position.z,
2682                mesh->vertices[vtx_idx2].position.x, mesh->vertices[vtx_idx2].position.y, mesh->vertices[vtx_idx2].position.z);
2683             ok(compare_vec3(vertices[vtx_idx1].normal, mesh->vertices[first_vtx2 + j].normal),
2684                "Test %s, glyph %d, vertex normal %d, result (%g, %g, %g), expected (%g, %g, %g)\n", name, i, vtx_idx1,
2685                vertices[vtx_idx1].normal.x, vertices[vtx_idx1].normal.y, vertices[vtx_idx1].normal.z,
2686                mesh->vertices[vtx_idx2].normal.x, mesh->vertices[vtx_idx2].normal.y, mesh->vertices[vtx_idx2].normal.z);
2687         }
2688         vtx_idx1 = first_vtx1 + nb_outline_vertices1;
2689         vtx_idx2 = first_vtx2 + nb_outline_vertices2;
2690
2691         first_face1 = face_idx1;
2692         first_face2 = face_idx2;
2693         for (; face_idx1 < number_of_faces; face_idx1++)
2694         {
2695             if (faces[face_idx1][0] >= vtx_idx1 ||
2696                 faces[face_idx1][1] >= vtx_idx1 ||
2697                 faces[face_idx1][2] >= vtx_idx1)
2698                 break;
2699         }
2700         for (; face_idx2 < mesh->number_of_faces; face_idx2++)
2701         {
2702             if (mesh->faces[face_idx2][0] >= vtx_idx2 ||
2703                 mesh->faces[face_idx2][1] >= vtx_idx2 ||
2704                 mesh->faces[face_idx2][2] >= vtx_idx2)
2705                 break;
2706         }
2707         nb_outline_faces1 = face_idx1 - first_face1;
2708         nb_outline_faces2 = face_idx2 - first_face2;
2709         ok(nb_outline_faces1 == nb_outline_faces2,
2710            "Test %s, glyph %d, outline face count result %d, expected %d\n", name, i,
2711            nb_outline_faces1, nb_outline_faces2);
2712
2713         for (j = 0; j < min(nb_outline_faces1, nb_outline_faces2); j++)
2714         {
2715             face_idx1 = first_face1 + j;
2716             face_idx2 = first_face2 + j;
2717             ok(faces[face_idx1][0] - first_vtx1 == mesh->faces[face_idx2][0] - first_vtx2 &&
2718                faces[face_idx1][1] - first_vtx1 == mesh->faces[face_idx2][1] - first_vtx2 &&
2719                faces[face_idx1][2] - first_vtx1 == mesh->faces[face_idx2][2] - first_vtx2,
2720                "Test %s, glyph %d, face %d, result (%d, %d, %d), expected (%d, %d, %d)\n", name, i, face_idx1,
2721                faces[face_idx1][0], faces[face_idx1][1], faces[face_idx1][2],
2722                mesh->faces[face_idx2][0] - first_vtx2 + first_vtx1,
2723                mesh->faces[face_idx2][1] - first_vtx2 + first_vtx1,
2724                mesh->faces[face_idx2][2] - first_vtx2 + first_vtx1);
2725         }
2726         face_idx1 = first_face1 + nb_outline_faces1;
2727         face_idx2 = first_face2 + nb_outline_faces2;
2728
2729         /* skip to the outline for the next glyph */
2730         for (; vtx_idx1 < number_of_vertices; vtx_idx1++) {
2731             if (vertices[vtx_idx1].normal.z == 0)
2732                 break;
2733         }
2734         for (; vtx_idx2 < mesh->number_of_vertices; vtx_idx2++) {
2735             if (mesh->vertices[vtx_idx2].normal.z == 0)
2736                 break;
2737         }
2738         for (; face_idx1 < number_of_faces; face_idx1++)
2739         {
2740             if (faces[face_idx1][0] >= vtx_idx1 ||
2741                 faces[face_idx1][1] >= vtx_idx1 ||
2742                 faces[face_idx1][2] >= vtx_idx1)
2743                 break;
2744         }
2745         for (; face_idx2 < mesh->number_of_faces; face_idx2++)
2746         {
2747             if (mesh->faces[face_idx2][0] >= vtx_idx2 ||
2748                 mesh->faces[face_idx2][1] >= vtx_idx2 ||
2749                 mesh->faces[face_idx2][2] >= vtx_idx2) break;
2750         }
2751     }
2752
2753 error:
2754     if (vertices) IDirect3DVertexBuffer9_Unlock(vertex_buffer);
2755     if (faces) IDirect3DIndexBuffer9_Unlock(index_buffer);
2756     if (index_buffer) IDirect3DIndexBuffer9_Release(index_buffer);
2757     if (vertex_buffer) IDirect3DVertexBuffer9_Release(vertex_buffer);
2758 }
2759
2760 static void test_createtext(IDirect3DDevice9 *device, HDC hdc, LPCSTR text, FLOAT deviation, FLOAT extrusion)
2761 {
2762     HRESULT hr;
2763     ID3DXMesh *d3dxmesh;
2764     struct mesh mesh;
2765     char name[256];
2766     OUTLINETEXTMETRIC otm;
2767     GLYPHMETRICS gm;
2768     GLYPHMETRICSFLOAT *glyphmetrics_float = malloc(sizeof(GLYPHMETRICSFLOAT) * strlen(text));
2769     int i;
2770     LOGFONT lf;
2771     HFONT font = NULL, oldfont = NULL;
2772
2773     sprintf(name, "text ('%s', %f, %f)", text, deviation, extrusion);
2774
2775     hr = D3DXCreateText(device, hdc, text, deviation, extrusion, &d3dxmesh, NULL, glyphmetrics_float);
2776     ok(hr == D3D_OK, "Got result %x, expected 0 (D3D_OK)\n", hr);
2777     if (hr != D3D_OK)
2778     {
2779         skip("Couldn't create text with D3DXCreateText\n");
2780         return;
2781     }
2782
2783     /* must select a modified font having lfHeight = otm.otmEMSquare before
2784      * calling GetGlyphOutline to get the expected values */
2785     if (!GetObject(GetCurrentObject(hdc, OBJ_FONT), sizeof(lf), &lf) ||
2786         !GetOutlineTextMetrics(hdc, sizeof(otm), &otm))
2787     {
2788         d3dxmesh->lpVtbl->Release(d3dxmesh);
2789         skip("Couldn't get text outline\n");
2790         return;
2791     }
2792     lf.lfHeight = otm.otmEMSquare;
2793     lf.lfWidth = 0;
2794     font = CreateFontIndirect(&lf);
2795     if (!font) {
2796         d3dxmesh->lpVtbl->Release(d3dxmesh);
2797         skip("Couldn't create the modified font\n");
2798         return;
2799     }
2800     oldfont = SelectObject(hdc, font);
2801
2802     for (i = 0; i < strlen(text); i++)
2803     {
2804         const MAT2 identity = {{0, 1}, {0, 0}, {0, 0}, {0, 1}};
2805         GetGlyphOutlineA(hdc, text[i], GGO_NATIVE, &gm, 0, NULL, &identity);
2806         compare_float(glyphmetrics_float[i].gmfBlackBoxX, gm.gmBlackBoxX / (float)otm.otmEMSquare);
2807         compare_float(glyphmetrics_float[i].gmfBlackBoxY, gm.gmBlackBoxY / (float)otm.otmEMSquare);
2808         compare_float(glyphmetrics_float[i].gmfptGlyphOrigin.x, gm.gmptGlyphOrigin.x / (float)otm.otmEMSquare);
2809         compare_float(glyphmetrics_float[i].gmfptGlyphOrigin.y, gm.gmptGlyphOrigin.y / (float)otm.otmEMSquare);
2810         compare_float(glyphmetrics_float[i].gmfCellIncX, gm.gmCellIncX / (float)otm.otmEMSquare);
2811         compare_float(glyphmetrics_float[i].gmfCellIncY, gm.gmCellIncY / (float)otm.otmEMSquare);
2812     }
2813
2814     ZeroMemory(&mesh, sizeof(mesh));
2815     if (!compute_text_mesh(&mesh, hdc, text, deviation, extrusion, otm.otmEMSquare))
2816     {
2817         skip("Couldn't create mesh\n");
2818         d3dxmesh->lpVtbl->Release(d3dxmesh);
2819         return;
2820     }
2821     mesh.fvf = D3DFVF_XYZ | D3DFVF_NORMAL;
2822
2823     compare_text_outline_mesh(name, d3dxmesh, &mesh, strlen(text));
2824
2825     free_mesh(&mesh);
2826
2827     d3dxmesh->lpVtbl->Release(d3dxmesh);
2828     SelectObject(hdc, oldfont);
2829 }
2830
2831 static void D3DXCreateTextTest(void)
2832 {
2833     HRESULT hr;
2834     HWND wnd;
2835     HDC hdc;
2836     IDirect3D9* d3d;
2837     IDirect3DDevice9* device;
2838     D3DPRESENT_PARAMETERS d3dpp;
2839     ID3DXMesh* d3dxmesh = NULL;
2840     HFONT hFont;
2841     OUTLINETEXTMETRIC otm;
2842     int number_of_vertices;
2843     int number_of_faces;
2844
2845     wnd = CreateWindow("static", "d3dx9_test", WS_POPUP, 0, 0, 1000, 1000, NULL, NULL, NULL, NULL);
2846     d3d = Direct3DCreate9(D3D_SDK_VERSION);
2847     if (!wnd)
2848     {
2849         skip("Couldn't create application window\n");
2850         return;
2851     }
2852     if (!d3d)
2853     {
2854         skip("Couldn't create IDirect3D9 object\n");
2855         DestroyWindow(wnd);
2856         return;
2857     }
2858
2859     ZeroMemory(&d3dpp, sizeof(d3dpp));
2860     d3dpp.Windowed = TRUE;
2861     d3dpp.SwapEffect = D3DSWAPEFFECT_DISCARD;
2862     hr = IDirect3D9_CreateDevice(d3d, D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, wnd, D3DCREATE_MIXED_VERTEXPROCESSING, &d3dpp, &device);
2863     if (FAILED(hr))
2864     {
2865         skip("Failed to create IDirect3DDevice9 object %#x\n", hr);
2866         IDirect3D9_Release(d3d);
2867         DestroyWindow(wnd);
2868         return;
2869     }
2870
2871     hdc = CreateCompatibleDC(NULL);
2872
2873     hFont = CreateFont(12, 0, 0, 0, FW_NORMAL, FALSE, FALSE, FALSE, DEFAULT_CHARSET,
2874                        OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, DEFAULT_PITCH | FF_DONTCARE,
2875                        "Arial");
2876     SelectObject(hdc, hFont);
2877     GetOutlineTextMetrics(hdc, sizeof(otm), &otm);
2878
2879     hr = D3DXCreateText(device, hdc, "wine", 0.001f, 0.4f, NULL, NULL, NULL);
2880     ok(hr == D3DERR_INVALIDCALL, "Got result %x, expected %x (D3DERR_INVALIDCALL)\n", hr, D3DERR_INVALIDCALL);
2881
2882     /* D3DXCreateTextA page faults from passing NULL text */
2883
2884     hr = D3DXCreateTextW(device, hdc, NULL, 0.001f, 0.4f, &d3dxmesh, NULL, NULL);
2885     ok(hr == D3DERR_INVALIDCALL, "Got result %x, expected %x (D3DERR_INVALIDCALL)\n", hr, D3DERR_INVALIDCALL);
2886
2887     hr = D3DXCreateText(device, hdc, "", 0.001f, 0.4f, &d3dxmesh, NULL, NULL);
2888     ok(hr == D3DERR_INVALIDCALL, "Got result %x, expected %x (D3DERR_INVALIDCALL)\n", hr, D3DERR_INVALIDCALL);
2889
2890     hr = D3DXCreateText(device, hdc, " ", 0.001f, 0.4f, &d3dxmesh, NULL, NULL);
2891     ok(hr == D3DERR_INVALIDCALL, "Got result %x, expected %x (D3DERR_INVALIDCALL)\n", hr, D3DERR_INVALIDCALL);
2892
2893     hr = D3DXCreateText(NULL, hdc, "wine", 0.001f, 0.4f, &d3dxmesh, NULL, NULL);
2894     ok(hr == D3DERR_INVALIDCALL, "Got result %x, expected %x (D3DERR_INVALIDCALL)\n", hr, D3DERR_INVALIDCALL);
2895
2896     hr = D3DXCreateText(device, NULL, "wine", 0.001f, 0.4f, &d3dxmesh, NULL, NULL);
2897     ok(hr == D3DERR_INVALIDCALL, "Got result %x, expected %x (D3DERR_INVALIDCALL)\n", hr, D3DERR_INVALIDCALL);
2898
2899     hr = D3DXCreateText(device, hdc, "wine", -FLT_MIN, 0.4f, &d3dxmesh, NULL, NULL);
2900     ok(hr == D3DERR_INVALIDCALL, "Got result %x, expected %x (D3DERR_INVALIDCALL)\n", hr, D3DERR_INVALIDCALL);
2901
2902     hr = D3DXCreateText(device, hdc, "wine", 0.001f, -FLT_MIN, &d3dxmesh, NULL, NULL);
2903     ok(hr == D3DERR_INVALIDCALL, "Got result %x, expected %x (D3DERR_INVALIDCALL)\n", hr, D3DERR_INVALIDCALL);
2904
2905     /* deviation = 0.0f treated as if deviation = 1.0f / otm.otmEMSquare */
2906     hr = D3DXCreateText(device, hdc, "wine", 1.0f / otm.otmEMSquare, 0.4f, &d3dxmesh, NULL, NULL);
2907     ok(hr == D3D_OK, "Got result %x, expected %x (D3D_OK)\n", hr, D3D_OK);
2908     number_of_vertices = d3dxmesh->lpVtbl->GetNumVertices(d3dxmesh);
2909     number_of_faces = d3dxmesh->lpVtbl->GetNumFaces(d3dxmesh);
2910     if (SUCCEEDED(hr) && d3dxmesh) d3dxmesh->lpVtbl->Release(d3dxmesh);
2911
2912     hr = D3DXCreateText(device, hdc, "wine", 0.0f, 0.4f, &d3dxmesh, NULL, NULL);
2913     ok(hr == D3D_OK, "Got result %x, expected %x (D3D_OK)\n", hr, D3D_OK);
2914     ok(number_of_vertices == d3dxmesh->lpVtbl->GetNumVertices(d3dxmesh),
2915        "Got %d vertices, expected %d\n",
2916        d3dxmesh->lpVtbl->GetNumVertices(d3dxmesh), number_of_vertices);
2917     ok(number_of_faces == d3dxmesh->lpVtbl->GetNumFaces(d3dxmesh),
2918        "Got %d faces, expected %d\n",
2919        d3dxmesh->lpVtbl->GetNumVertices(d3dxmesh), number_of_faces);
2920     if (SUCCEEDED(hr) && d3dxmesh) d3dxmesh->lpVtbl->Release(d3dxmesh);
2921
2922 #if 0
2923     /* too much detail requested, so will appear to hang */
2924     trace("Waiting for D3DXCreateText to finish with deviation = FLT_MIN ...\n");
2925     hr = D3DXCreateText(device, hdc, "wine", FLT_MIN, 0.4f, &d3dxmesh, NULL, NULL);
2926     ok(hr == D3D_OK, "Got result %x, expected %x (D3D_OK)\n", hr, D3D_OK);
2927     if (SUCCEEDED(hr) && d3dxmesh) d3dxmesh->lpVtbl->Release(d3dxmesh);
2928     trace("D3DXCreateText finish with deviation = FLT_MIN\n");
2929 #endif
2930
2931     hr = D3DXCreateText(device, hdc, "wine", 0.001f, 0.4f, &d3dxmesh, NULL, NULL);
2932     ok(hr == D3D_OK, "Got result %x, expected %x (D3D_OK)\n", hr, D3D_OK);
2933     if (SUCCEEDED(hr) && d3dxmesh) d3dxmesh->lpVtbl->Release(d3dxmesh);
2934
2935     test_createtext(device, hdc, "wine", FLT_MAX, 0.4f);
2936     test_createtext(device, hdc, "wine", 0.001f, FLT_MIN);
2937     test_createtext(device, hdc, "wine", 0.001f, 0.0f);
2938     test_createtext(device, hdc, "wine", 0.001f, FLT_MAX);
2939     test_createtext(device, hdc, "wine", 0.0f, 1.0f);
2940
2941     DeleteDC(hdc);
2942
2943     IDirect3DDevice9_Release(device);
2944     IDirect3D9_Release(d3d);
2945     DestroyWindow(wnd);
2946 }
2947
2948 static void test_get_decl_length(void)
2949 {
2950     static const D3DVERTEXELEMENT9 declaration1[] =
2951     {
2952         {0, 0, D3DDECLTYPE_FLOAT1, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_POSITION, 0},
2953         {1, 0, D3DDECLTYPE_FLOAT2, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_POSITION, 0},
2954         {2, 0, D3DDECLTYPE_FLOAT3, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_POSITION, 0},
2955         {3, 0, D3DDECLTYPE_FLOAT4, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_POSITION, 0},
2956         {4, 0, D3DDECLTYPE_D3DCOLOR, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_POSITION, 0},
2957         {5, 0, D3DDECLTYPE_UBYTE4, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_POSITION, 0},
2958         {6, 0, D3DDECLTYPE_SHORT2, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_POSITION, 0},
2959         {7, 0, D3DDECLTYPE_SHORT4, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_POSITION, 0},
2960         {8, 0, D3DDECLTYPE_UBYTE4N, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_POSITION, 0},
2961         {9, 0, D3DDECLTYPE_SHORT2N, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_POSITION, 0},
2962         {10, 0, D3DDECLTYPE_SHORT4N, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_POSITION, 0},
2963         {11, 0, D3DDECLTYPE_UDEC3, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_POSITION, 0},
2964         {12, 0, D3DDECLTYPE_DEC3N, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_POSITION, 0},
2965         {13, 0, D3DDECLTYPE_FLOAT16_2, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_POSITION, 0},
2966         {14, 0, D3DDECLTYPE_FLOAT16_4, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_POSITION, 0},
2967         D3DDECL_END(),
2968     };
2969     static const D3DVERTEXELEMENT9 declaration2[] =
2970     {
2971         {0, 8, D3DDECLTYPE_FLOAT1, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_POSITION, 0},
2972         {1, 8, D3DDECLTYPE_FLOAT2, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_POSITION, 0},
2973         {2, 8, D3DDECLTYPE_FLOAT3, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_POSITION, 0},
2974         {3, 8, D3DDECLTYPE_FLOAT4, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_POSITION, 0},
2975         {4, 8, D3DDECLTYPE_D3DCOLOR, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_POSITION, 0},
2976         {5, 8, D3DDECLTYPE_UBYTE4, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_POSITION, 0},
2977         {6, 8, D3DDECLTYPE_SHORT2, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_POSITION, 0},
2978         {7, 8, D3DDECLTYPE_SHORT4, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_POSITION, 0},
2979         {0, 8, D3DDECLTYPE_UBYTE4N, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_POSITION, 0},
2980         {1, 8, D3DDECLTYPE_SHORT2N, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_POSITION, 0},
2981         {2, 8, D3DDECLTYPE_SHORT4N, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_POSITION, 0},
2982         {3, 8, D3DDECLTYPE_UDEC3, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_POSITION, 0},
2983         {4, 8, D3DDECLTYPE_DEC3N, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_POSITION, 0},
2984         {5, 8, D3DDECLTYPE_FLOAT16_2, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_POSITION, 0},
2985         {6, 8, D3DDECLTYPE_FLOAT16_4, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_POSITION, 0},
2986         {7, 8, D3DDECLTYPE_FLOAT1, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_POSITION, 0},
2987         D3DDECL_END(),
2988     };
2989     UINT size;
2990
2991     size = D3DXGetDeclLength(declaration1);
2992     ok(size == 15, "Got size %u, expected 15.\n", size);
2993
2994     size = D3DXGetDeclLength(declaration2);
2995     ok(size == 16, "Got size %u, expected 16.\n", size);
2996 }
2997
2998 static void test_get_decl_vertex_size(void)
2999 {
3000     static const D3DVERTEXELEMENT9 declaration1[] =
3001     {
3002         {0, 0, D3DDECLTYPE_FLOAT1, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_POSITION, 0},
3003         {1, 0, D3DDECLTYPE_FLOAT2, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_POSITION, 0},
3004         {2, 0, D3DDECLTYPE_FLOAT3, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_POSITION, 0},
3005         {3, 0, D3DDECLTYPE_FLOAT4, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_POSITION, 0},
3006         {4, 0, D3DDECLTYPE_D3DCOLOR, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_POSITION, 0},
3007         {5, 0, D3DDECLTYPE_UBYTE4, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_POSITION, 0},
3008         {6, 0, D3DDECLTYPE_SHORT2, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_POSITION, 0},
3009         {7, 0, D3DDECLTYPE_SHORT4, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_POSITION, 0},
3010         {8, 0, D3DDECLTYPE_UBYTE4N, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_POSITION, 0},
3011         {9, 0, D3DDECLTYPE_SHORT2N, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_POSITION, 0},
3012         {10, 0, D3DDECLTYPE_SHORT4N, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_POSITION, 0},
3013         {11, 0, D3DDECLTYPE_UDEC3, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_POSITION, 0},
3014         {12, 0, D3DDECLTYPE_DEC3N, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_POSITION, 0},
3015         {13, 0, D3DDECLTYPE_FLOAT16_2, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_POSITION, 0},
3016         {14, 0, D3DDECLTYPE_FLOAT16_4, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_POSITION, 0},
3017         D3DDECL_END(),
3018     };
3019     static const D3DVERTEXELEMENT9 declaration2[] =
3020     {
3021         {0, 8, D3DDECLTYPE_FLOAT1, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_POSITION, 0},
3022         {1, 8, D3DDECLTYPE_FLOAT2, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_POSITION, 0},
3023         {2, 8, D3DDECLTYPE_FLOAT3, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_POSITION, 0},
3024         {3, 8, D3DDECLTYPE_FLOAT4, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_POSITION, 0},
3025         {4, 8, D3DDECLTYPE_D3DCOLOR, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_POSITION, 0},
3026         {5, 8, D3DDECLTYPE_UBYTE4, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_POSITION, 0},
3027         {6, 8, D3DDECLTYPE_SHORT2, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_POSITION, 0},
3028         {7, 8, D3DDECLTYPE_SHORT4, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_POSITION, 0},
3029         {0, 8, D3DDECLTYPE_UBYTE4N, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_POSITION, 0},
3030         {1, 8, D3DDECLTYPE_SHORT2N, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_POSITION, 0},
3031         {2, 8, D3DDECLTYPE_SHORT4N, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_POSITION, 0},
3032         {3, 8, D3DDECLTYPE_UDEC3, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_POSITION, 0},
3033         {4, 8, D3DDECLTYPE_DEC3N, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_POSITION, 0},
3034         {5, 8, D3DDECLTYPE_FLOAT16_2, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_POSITION, 0},
3035         {6, 8, D3DDECLTYPE_FLOAT16_4, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_POSITION, 0},
3036         {7, 8, D3DDECLTYPE_FLOAT1, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_POSITION, 0},
3037         D3DDECL_END(),
3038     };
3039     static const UINT sizes1[] =
3040     {
3041         4,  8,  12, 16,
3042         4,  4,  4,  8,
3043         4,  4,  8,  4,
3044         4,  4,  8,  0,
3045     };
3046     static const UINT sizes2[] =
3047     {
3048         12, 16, 20, 24,
3049         12, 12, 16, 16,
3050     };
3051     unsigned int i;
3052     UINT size;
3053
3054     size = D3DXGetDeclVertexSize(NULL, 0);
3055     ok(size == 0, "Got size %#x, expected 0.\n", size);
3056
3057     for (i = 0; i < 16; ++i)
3058     {
3059         size = D3DXGetDeclVertexSize(declaration1, i);
3060         ok(size == sizes1[i], "Got size %u for stream %u, expected %u.\n", size, i, sizes1[i]);
3061     }
3062
3063     for (i = 0; i < 8; ++i)
3064     {
3065         size = D3DXGetDeclVertexSize(declaration2, i);
3066         ok(size == sizes2[i], "Got size %u for stream %u, expected %u.\n", size, i, sizes2[i]);
3067     }
3068 }
3069
3070 START_TEST(mesh)
3071 {
3072     D3DXBoundProbeTest();
3073     D3DXComputeBoundingBoxTest();
3074     D3DXComputeBoundingSphereTest();
3075     D3DXGetFVFVertexSizeTest();
3076     D3DXIntersectTriTest();
3077     D3DXCreateMeshTest();
3078     D3DXCreateMeshFVFTest();
3079     D3DXCreateBoxTest();
3080     D3DXCreateSphereTest();
3081     D3DXCreateCylinderTest();
3082     D3DXCreateTextTest();
3083     test_get_decl_length();
3084     test_get_decl_vertex_size();
3085     test_fvf_decl_conversion();
3086 }