d3dx9_36/tests: Add tests for D3DXCreateCylinder.
[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 "wine/test.h"
23 #include "d3dx9.h"
24
25 #define admitted_error 0.0001f
26
27 #define compare_vertex_sizes(type, exp) \
28     got=D3DXGetFVFVertexSize(type); \
29     ok(got==exp, "Expected: %d, Got: %d\n", exp, got);
30
31 static BOOL compare(FLOAT u, FLOAT v)
32 {
33     return (fabs(u-v) < admitted_error);
34 }
35
36 static BOOL compare_vec3(D3DXVECTOR3 u, D3DXVECTOR3 v)
37 {
38     return ( compare(u.x, v.x) && compare(u.y, v.y) && compare(u.z, v.z) );
39 }
40
41 struct vertex
42 {
43     D3DXVECTOR3 position;
44     D3DXVECTOR3 normal;
45 };
46
47 typedef WORD face[3];
48
49 static BOOL compare_face(face a, face b)
50 {
51     return (a[0]==b[0] && a[1] == b[1] && a[2] == b[2]);
52 }
53
54 struct mesh
55 {
56     DWORD number_of_vertices;
57     struct vertex *vertices;
58
59     DWORD number_of_faces;
60     face *faces;
61
62     DWORD fvf;
63     UINT vertex_size;
64 };
65
66 static void free_mesh(struct mesh *mesh)
67 {
68     HeapFree(GetProcessHeap(), 0, mesh->faces);
69     HeapFree(GetProcessHeap(), 0, mesh->vertices);
70 }
71
72 static BOOL new_mesh(struct mesh *mesh, DWORD number_of_vertices, DWORD number_of_faces)
73 {
74     mesh->vertices = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, number_of_vertices * sizeof(*mesh->vertices));
75     if (!mesh->vertices)
76     {
77         return FALSE;
78     }
79     mesh->number_of_vertices = number_of_vertices;
80
81     mesh->faces = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, number_of_faces * sizeof(*mesh->faces));
82     if (!mesh->faces)
83     {
84         HeapFree(GetProcessHeap(), 0, mesh->vertices);
85         return FALSE;
86     }
87     mesh->number_of_faces = number_of_faces;
88
89     return TRUE;
90 }
91
92 static void compare_mesh(const char *name, ID3DXMesh *d3dxmesh, struct mesh *mesh)
93 {
94     HRESULT hr;
95     DWORD number_of_vertices, number_of_faces;
96     IDirect3DVertexBuffer9 *vertex_buffer;
97     IDirect3DIndexBuffer9 *index_buffer;
98     D3DVERTEXBUFFER_DESC vertex_buffer_description;
99     D3DINDEXBUFFER_DESC index_buffer_description;
100     struct vertex *vertices;
101     face *faces;
102     int expected, i;
103
104     number_of_vertices = d3dxmesh->lpVtbl->GetNumVertices(d3dxmesh);
105     ok(number_of_vertices == mesh->number_of_vertices, "Test %s, result %u, expected %d\n",
106        name, number_of_vertices, mesh->number_of_vertices);
107
108     number_of_faces = d3dxmesh->lpVtbl->GetNumFaces(d3dxmesh);
109     ok(number_of_faces == mesh->number_of_faces, "Test %s, result %u, expected %d\n",
110        name, number_of_faces, mesh->number_of_faces);
111
112     /* vertex buffer */
113     hr = d3dxmesh->lpVtbl->GetVertexBuffer(d3dxmesh, &vertex_buffer);
114     ok(hr == D3D_OK, "Test %s, result %x, expected 0 (D3D_OK)\n", name, hr);
115
116     if (hr != D3D_OK)
117     {
118         skip("Couldn't get vertex buffer\n");
119     }
120     else
121     {
122         hr = IDirect3DVertexBuffer9_GetDesc(vertex_buffer, &vertex_buffer_description);
123         ok(hr == D3D_OK, "Test %s, result %x, expected 0 (D3D_OK)\n", name, hr);
124
125         if (hr != D3D_OK)
126         {
127             skip("Couldn't get vertex buffer description\n");
128         }
129         else
130         {
131             ok(vertex_buffer_description.Format == D3DFMT_VERTEXDATA, "Test %s, result %x, expected %x (D3DFMT_VERTEXDATA)\n",
132                name, vertex_buffer_description.Format, D3DFMT_VERTEXDATA);
133             ok(vertex_buffer_description.Type == D3DRTYPE_VERTEXBUFFER, "Test %s, result %x, expected %x (D3DRTYPE_VERTEXBUFFER)\n",
134                name, vertex_buffer_description.Type, D3DRTYPE_VERTEXBUFFER);
135             ok(vertex_buffer_description.Usage == 0, "Test %s, result %x, expected %x\n", name, vertex_buffer_description.Usage, 0);
136             ok(vertex_buffer_description.Pool == D3DPOOL_MANAGED, "Test %s, result %x, expected %x (D3DPOOL_DEFAULT)\n",
137                name, vertex_buffer_description.Pool, D3DPOOL_DEFAULT);
138             ok(vertex_buffer_description.FVF == mesh->fvf, "Test %s, result %x, expected %x\n",
139                name, vertex_buffer_description.FVF, mesh->fvf);
140             if (mesh->fvf == 0)
141             {
142                 expected = number_of_vertices * mesh->vertex_size;
143             }
144             else
145             {
146                 expected = number_of_vertices * D3DXGetFVFVertexSize(mesh->fvf);
147             }
148             ok(vertex_buffer_description.Size == expected, "Test %s, result %x, expected %x\n",
149                name, vertex_buffer_description.Size, expected);
150         }
151
152         /* specify offset and size to avoid potential overruns */
153         hr = IDirect3DVertexBuffer9_Lock(vertex_buffer, 0, number_of_vertices * sizeof(D3DXVECTOR3) * 2,
154                                          (LPVOID *)&vertices, D3DLOCK_DISCARD);
155         ok(hr == D3D_OK, "Test %s, result %x, expected 0 (D3D_OK)\n", name, hr);
156
157         if (hr != D3D_OK)
158         {
159             skip("Couldn't lock vertex buffer\n");
160         }
161         else
162         {
163             for (i = 0; i < number_of_vertices; i++)
164             {
165                 ok(compare_vec3(vertices[i].position, mesh->vertices[i].position),
166                    "Test %s, vertex position %d, result (%g, %g, %g), expected (%g, %g, %g)\n", name, i,
167                    vertices[i].position.x, vertices[i].position.y, vertices[i].position.z,
168                    mesh->vertices[i].position.x, mesh->vertices[i].position.y, mesh->vertices[i].position.z);
169                 ok(compare_vec3(vertices[i].normal, mesh->vertices[i].normal),
170                    "Test %s, vertex normal %d, result (%g, %g, %g), expected (%g, %g, %g)\n", name, i,
171                    vertices[i].normal.x, vertices[i].normal.y, vertices[i].normal.z,
172                    mesh->vertices[i].normal.x, mesh->vertices[i].normal.y, mesh->vertices[i].normal.z);
173             }
174
175             IDirect3DVertexBuffer9_Unlock(vertex_buffer);
176         }
177
178         IDirect3DVertexBuffer9_Release(vertex_buffer);
179     }
180
181     /* index buffer */
182     hr = d3dxmesh->lpVtbl->GetIndexBuffer(d3dxmesh, &index_buffer);
183     ok(hr == D3D_OK, "Test %s, result %x, expected 0 (D3D_OK)\n", name, hr);
184
185     if (!index_buffer)
186     {
187         skip("Couldn't get index buffer\n");
188     }
189     else
190     {
191         hr = IDirect3DIndexBuffer9_GetDesc(index_buffer, &index_buffer_description);
192         ok(hr == D3D_OK, "Test %s, result %x, expected 0 (D3D_OK)\n", name, hr);
193
194         if (hr != D3D_OK)
195         {
196             skip("Couldn't get index buffer description\n");
197         }
198         else
199         {
200             ok(index_buffer_description.Format == D3DFMT_INDEX16, "Test %s, result %x, expected %x (D3DFMT_INDEX16)\n",
201                name, index_buffer_description.Format, D3DFMT_INDEX16);
202             ok(index_buffer_description.Type == D3DRTYPE_INDEXBUFFER, "Test %s, result %x, expected %x (D3DRTYPE_INDEXBUFFER)\n",
203                name, index_buffer_description.Type, D3DRTYPE_INDEXBUFFER);
204             todo_wine ok(index_buffer_description.Usage == 0, "Test %s, result %x, expected %x\n", name, index_buffer_description.Usage, 0);
205             ok(index_buffer_description.Pool == D3DPOOL_MANAGED, "Test %s, result %x, expected %x (D3DPOOL_DEFAULT)\n",
206                name, index_buffer_description.Pool, D3DPOOL_DEFAULT);
207             expected = number_of_faces * sizeof(WORD) * 3;
208             ok(index_buffer_description.Size == expected, "Test %s, result %x, expected %x\n",
209                name, index_buffer_description.Size, expected);
210         }
211
212         /* specify offset and size to avoid potential overruns */
213         hr = IDirect3DIndexBuffer9_Lock(index_buffer, 0, number_of_faces * sizeof(WORD) * 3,
214                                         (LPVOID *)&faces, D3DLOCK_DISCARD);
215         ok(hr == D3D_OK, "Test %s, result %x, expected 0 (D3D_OK)\n", name, hr);
216
217         if (hr != D3D_OK)
218         {
219             skip("Couldn't lock index buffer\n");
220         }
221         else
222         {
223             for (i = 0; i < number_of_faces; i++)
224             {
225                 ok(compare_face(faces[i], mesh->faces[i]),
226                    "Test %s, face %d, result (%u, %u, %u), expected (%u, %u, %u)\n", name, i,
227                    faces[i][0], faces[i][1], faces[i][2],
228                    mesh->faces[i][0], mesh->faces[i][1], mesh->faces[i][2]);
229             }
230
231             IDirect3DIndexBuffer9_Unlock(index_buffer);
232         }
233
234         IDirect3DIndexBuffer9_Release(index_buffer);
235     }
236 }
237
238 static void D3DXBoundProbeTest(void)
239 {
240     BOOL result;
241     D3DXVECTOR3 bottom_point, center, top_point, raydirection, rayposition;
242     FLOAT radius;
243
244 /*____________Test the Box case___________________________*/
245     bottom_point.x = -3.0f; bottom_point.y = -2.0f; bottom_point.z = -1.0f;
246     top_point.x = 7.0f; top_point.y = 8.0f; top_point.z = 9.0f;
247
248     raydirection.x = -4.0f; raydirection.y = -5.0f; raydirection.z = -6.0f;
249     rayposition.x = 5.0f; rayposition.y = 5.0f; rayposition.z = 11.0f;
250     result = D3DXBoxBoundProbe(&bottom_point, &top_point, &rayposition, &raydirection);
251     ok(result == TRUE, "expected TRUE, received FALSE\n");
252
253     raydirection.x = 4.0f; raydirection.y = 5.0f; raydirection.z = 6.0f;
254     rayposition.x = 5.0f; rayposition.y = 5.0f; rayposition.z = 11.0f;
255     result = D3DXBoxBoundProbe(&bottom_point, &top_point, &rayposition, &raydirection);
256     ok(result == FALSE, "expected FALSE, received TRUE\n");
257
258     rayposition.x = -4.0f; rayposition.y = 1.0f; rayposition.z = -2.0f;
259     result = D3DXBoxBoundProbe(&bottom_point, &top_point, &rayposition, &raydirection);
260     ok(result == TRUE, "expected TRUE, received FALSE\n");
261
262     bottom_point.x = 1.0f; bottom_point.y = 0.0f; bottom_point.z = 0.0f;
263     top_point.x = 1.0f; top_point.y = 0.0f; top_point.z = 0.0f;
264     rayposition.x = 0.0f; rayposition.y = 1.0f; rayposition.z = 0.0f;
265     raydirection.x = 0.0f; raydirection.y = 3.0f; raydirection.z = 0.0f;
266     result = D3DXBoxBoundProbe(&bottom_point, &top_point, &rayposition, &raydirection);
267     ok(result == FALSE, "expected FALSE, received TRUE\n");
268
269     bottom_point.x = 1.0f; bottom_point.y = 2.0f; bottom_point.z = 3.0f;
270     top_point.x = 10.0f; top_point.y = 15.0f; top_point.z = 20.0f;
271
272     raydirection.x = 7.0f; raydirection.y = 8.0f; raydirection.z = 9.0f;
273     rayposition.x = 3.0f; rayposition.y = 7.0f; rayposition.z = -6.0f;
274     result = D3DXBoxBoundProbe(&bottom_point, &top_point, &rayposition, &raydirection);
275     ok(result == TRUE, "expected TRUE, received FALSE\n");
276
277     bottom_point.x = 0.0f; bottom_point.y = 0.0f; bottom_point.z = 0.0f;
278     top_point.x = 1.0f; top_point.y = 1.0f; top_point.z = 1.0f;
279
280     raydirection.x = 0.0f; raydirection.y = 1.0f; raydirection.z = .0f;
281     rayposition.x = -3.0f; rayposition.y = 0.0f; rayposition.z = 0.0f;
282     result = D3DXBoxBoundProbe(&bottom_point, &top_point, &rayposition, &raydirection);
283     ok(result == FALSE, "expected FALSE, received TRUE\n");
284
285     raydirection.x = 1.0f; raydirection.y = 0.0f; raydirection.z = .0f;
286     rayposition.x = -3.0f; rayposition.y = 0.0f; rayposition.z = 0.0f;
287     result = D3DXBoxBoundProbe(&bottom_point, &top_point, &rayposition, &raydirection);
288     ok(result == TRUE, "expected TRUE, received FALSE\n");
289
290 /*____________Test the Sphere case________________________*/
291     radius = sqrt(77.0f);
292     center.x = 1.0f; center.y = 2.0f; center.z = 3.0f;
293     raydirection.x = 2.0f; raydirection.y = -4.0f; raydirection.z = 2.0f;
294
295     rayposition.x = 5.0f; rayposition.y = 5.0f; rayposition.z = 9.0f;
296     result = D3DXSphereBoundProbe(&center, radius, &rayposition, &raydirection);
297     ok(result == TRUE, "expected TRUE, received FALSE\n");
298
299     rayposition.x = 45.0f; rayposition.y = -75.0f; rayposition.z = 49.0f;
300     result = D3DXSphereBoundProbe(&center, radius, &rayposition, &raydirection);
301     ok(result == FALSE, "expected FALSE, received TRUE\n");
302
303     rayposition.x = 5.0f; rayposition.y = 11.0f; rayposition.z = 9.0f;
304     result = D3DXSphereBoundProbe(&center, radius, &rayposition, &raydirection);
305     ok(result == FALSE, "expected FALSE, received TRUE\n");
306 }
307
308 static void D3DXComputeBoundingBoxTest(void)
309 {
310     D3DXVECTOR3 exp_max, exp_min, got_max, got_min, vertex[5];
311     HRESULT hr;
312
313     vertex[0].x = 1.0f; vertex[0].y = 1.0f; vertex[0].z = 1.0f;
314     vertex[1].x = 1.0f; vertex[1].y = 1.0f; vertex[1].z = 1.0f;
315     vertex[2].x = 1.0f; vertex[2].y = 1.0f; vertex[2].z = 1.0f;
316     vertex[3].x = 1.0f; vertex[3].y = 1.0f; vertex[3].z = 1.0f;
317     vertex[4].x = 9.0f; vertex[4].y = 9.0f; vertex[4].z = 9.0f;
318
319     exp_min.x = 1.0f; exp_min.y = 1.0f; exp_min.z = 1.0f;
320     exp_max.x = 9.0f; exp_max.y = 9.0f; exp_max.z = 9.0f;
321
322     hr = D3DXComputeBoundingBox(&vertex[3],2,D3DXGetFVFVertexSize(D3DFVF_XYZ),&got_min,&got_max);
323
324     ok( hr == D3D_OK, "Expected D3D_OK, got %#x\n", hr);
325     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);
326     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);
327
328 /*________________________*/
329
330     vertex[0].x = 2.0f; vertex[0].y = 5.9f; vertex[0].z = -1.2f;
331     vertex[1].x = -1.87f; vertex[1].y = 7.9f; vertex[1].z = 7.4f;
332     vertex[2].x = 7.43f; vertex[2].y = -0.9f; vertex[2].z = 11.9f;
333     vertex[3].x = -6.92f; vertex[3].y = 6.3f; vertex[3].z = -3.8f;
334     vertex[4].x = 11.4f; vertex[4].y = -8.1f; vertex[4].z = 4.5f;
335
336     exp_min.x = -6.92f; exp_min.y = -8.1f; exp_min.z = -3.80f;
337     exp_max.x = 11.4f; exp_max.y = 7.90f; exp_max.z = 11.9f;
338
339     hr = D3DXComputeBoundingBox(&vertex[0],5,D3DXGetFVFVertexSize(D3DFVF_XYZ),&got_min,&got_max);
340
341     ok( hr == D3D_OK, "Expected D3D_OK, got %#x\n", hr);
342     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);
343     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);
344
345 /*________________________*/
346
347     vertex[0].x = 2.0f; vertex[0].y = 5.9f; vertex[0].z = -1.2f;
348     vertex[1].x = -1.87f; vertex[1].y = 7.9f; vertex[1].z = 7.4f;
349     vertex[2].x = 7.43f; vertex[2].y = -0.9f; vertex[2].z = 11.9f;
350     vertex[3].x = -6.92f; vertex[3].y = 6.3f; vertex[3].z = -3.8f;
351     vertex[4].x = 11.4f; vertex[4].y = -8.1f; vertex[4].z = 4.5f;
352
353     exp_min.x = -6.92f; exp_min.y = -0.9f; exp_min.z = -3.8f;
354     exp_max.x = 7.43f; exp_max.y = 7.90f; exp_max.z = 11.9f;
355
356     hr = D3DXComputeBoundingBox(&vertex[0],4,D3DXGetFVFVertexSize(D3DFVF_XYZ),&got_min,&got_max);
357
358     ok( hr == D3D_OK, "Expected D3D_OK, got %#x\n", hr);
359     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);
360     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);
361
362 /*________________________*/
363     hr = D3DXComputeBoundingBox(NULL,5,D3DXGetFVFVertexSize(D3DFVF_XYZ),&got_min,&got_max);
364     ok( hr == D3DERR_INVALIDCALL, "Expected D3DERR_INVALIDCALL, got %#x\n", hr);
365
366 /*________________________*/
367     hr = D3DXComputeBoundingBox(&vertex[3],5,D3DXGetFVFVertexSize(D3DFVF_XYZ),NULL,&got_max);
368     ok( hr == D3DERR_INVALIDCALL, "Expected D3DERR_INVALIDCALL, got %#x\n", hr);
369
370 /*________________________*/
371     hr = D3DXComputeBoundingBox(&vertex[3],5,D3DXGetFVFVertexSize(D3DFVF_XYZ),&got_min,NULL);
372     ok( hr == D3DERR_INVALIDCALL, "Expected D3DERR_INVALIDCALL, got %#x\n", hr);
373 }
374
375 static void D3DXComputeBoundingSphereTest(void)
376 {
377     D3DXVECTOR3 exp_cen, got_cen, vertex[5];
378     FLOAT exp_rad, got_rad;
379     HRESULT hr;
380
381     vertex[0].x = 1.0f; vertex[0].y = 1.0f; vertex[0].z = 1.0f;
382     vertex[1].x = 1.0f; vertex[1].y = 1.0f; vertex[1].z = 1.0f;
383     vertex[2].x = 1.0f; vertex[2].y = 1.0f; vertex[2].z = 1.0f;
384     vertex[3].x = 1.0f; vertex[3].y = 1.0f; vertex[3].z = 1.0f;
385     vertex[4].x = 9.0f; vertex[4].y = 9.0f; vertex[4].z = 9.0f;
386
387     exp_rad = 6.928203f;
388     exp_cen.x = 5.0; exp_cen.y = 5.0; exp_cen.z = 5.0;
389
390     hr = D3DXComputeBoundingSphere(&vertex[3],2,D3DXGetFVFVertexSize(D3DFVF_XYZ),&got_cen,&got_rad);
391
392     ok( hr == D3D_OK, "Expected D3D_OK, got %#x\n", hr);
393     ok( compare(exp_rad, got_rad), "Expected radius: %f, got radius: %f\n", exp_rad, got_rad);
394     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);
395
396 /*________________________*/
397
398     vertex[0].x = 2.0f; vertex[0].y = 5.9f; vertex[0].z = -1.2f;
399     vertex[1].x = -1.87f; vertex[1].y = 7.9f; vertex[1].z = 7.4f;
400     vertex[2].x = 7.43f; vertex[2].y = -0.9f; vertex[2].z = 11.9f;
401     vertex[3].x = -6.92f; vertex[3].y = 6.3f; vertex[3].z = -3.8f;
402     vertex[4].x = 11.4f; vertex[4].y = -8.1f; vertex[4].z = 4.5f;
403
404     exp_rad = 13.707883f;
405     exp_cen.x = 2.408f; exp_cen.y = 2.22f; exp_cen.z = 3.76f;
406
407     hr = D3DXComputeBoundingSphere(&vertex[0],5,D3DXGetFVFVertexSize(D3DFVF_XYZ),&got_cen,&got_rad);
408
409     ok( hr == D3D_OK, "Expected D3D_OK, got %#x\n", hr);
410     ok( compare(exp_rad, got_rad), "Expected radius: %f, got radius: %f\n", exp_rad, got_rad);
411     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);
412
413 /*________________________*/
414     hr = D3DXComputeBoundingSphere(NULL,5,D3DXGetFVFVertexSize(D3DFVF_XYZ),&got_cen,&got_rad);
415     ok( hr == D3DERR_INVALIDCALL, "Expected D3DERR_INVALIDCALL, got %#x\n", hr);
416
417 /*________________________*/
418     hr = D3DXComputeBoundingSphere(&vertex[3],5,D3DXGetFVFVertexSize(D3DFVF_XYZ),NULL,&got_rad);
419     ok( hr == D3DERR_INVALIDCALL, "Expected D3DERR_INVALIDCALL, got %#x\n", hr);
420
421 /*________________________*/
422     hr = D3DXComputeBoundingSphere(&vertex[3],5,D3DXGetFVFVertexSize(D3DFVF_XYZ),&got_cen,NULL);
423     ok( hr == D3DERR_INVALIDCALL, "Expected D3DERR_INVALIDCALL, got %#x\n", hr);
424 }
425
426 static void print_elements(const D3DVERTEXELEMENT9 *elements)
427 {
428     D3DVERTEXELEMENT9 last = D3DDECL_END();
429     const D3DVERTEXELEMENT9 *ptr = elements;
430     int count = 0;
431
432     while (memcmp(ptr, &last, sizeof(D3DVERTEXELEMENT9)))
433     {
434         trace(
435             "[Element %d] Stream = %d, Offset = %d, Type = %d, Method = %d, Usage = %d, UsageIndex = %d\n",
436              count, ptr->Stream, ptr->Offset, ptr->Type, ptr->Method, ptr->Usage, ptr->UsageIndex);
437         ptr++;
438         count++;
439     }
440 }
441
442 static void compare_elements(const D3DVERTEXELEMENT9 *elements, const D3DVERTEXELEMENT9 *expected_elements,
443         unsigned int line, unsigned int test_id)
444 {
445     D3DVERTEXELEMENT9 last = D3DDECL_END();
446     unsigned int i;
447
448     for (i = 0; i < MAX_FVF_DECL_SIZE; i++)
449     {
450         int end1 = memcmp(&elements[i], &last, sizeof(last));
451         int end2 = memcmp(&expected_elements[i], &last, sizeof(last));
452         int status;
453
454         if (!end1 && !end2) break;
455
456         status = !end1 ^ !end2;
457         ok(!status, "Line %u, test %u: Mismatch in size, test declaration is %s than expected.\n",
458                 line, test_id, end1 ? "shorter" : "longer");
459         if (status)
460         {
461             print_elements(elements);
462             break;
463         }
464
465         status = memcmp(&elements[i], &expected_elements[i], sizeof(D3DVERTEXELEMENT9));
466         ok(!status, "Line %u, test %u: Mismatch in element %u.\n", line, test_id, i);
467         if (status)
468         {
469             print_elements(elements);
470             break;
471         }
472     }
473 }
474
475 static void test_fvf_to_decl(DWORD test_fvf, const D3DVERTEXELEMENT9 expected_elements[],
476         HRESULT expected_hr, unsigned int line, unsigned int test_id)
477 {
478     HRESULT hr;
479     D3DVERTEXELEMENT9 decl[MAX_FVF_DECL_SIZE];
480
481     hr = D3DXDeclaratorFromFVF(test_fvf, decl);
482     ok(hr == expected_hr,
483             "Line %u, test %u: D3DXDeclaratorFromFVF returned %#x, expected %#x.\n",
484             line, test_id, hr, expected_hr);
485     if (SUCCEEDED(hr)) compare_elements(decl, expected_elements, line, test_id);
486 }
487
488 static void test_decl_to_fvf(const D3DVERTEXELEMENT9 *decl, DWORD expected_fvf,
489         HRESULT expected_hr, unsigned int line, unsigned int test_id)
490 {
491     HRESULT hr;
492     DWORD result_fvf = 0xdeadbeef;
493
494     hr = D3DXFVFFromDeclarator(decl, &result_fvf);
495     ok(hr == expected_hr,
496        "Line %u, test %u: D3DXFVFFromDeclarator returned %#x, expected %#x.\n",
497        line, test_id, hr, expected_hr);
498     if (SUCCEEDED(hr))
499     {
500         ok(expected_fvf == result_fvf, "Line %u, test %u: Got FVF %#x, expected %#x.\n",
501                 line, test_id, result_fvf, expected_fvf);
502     }
503 }
504
505 static void test_fvf_decl_conversion(void)
506 {
507     static const struct
508     {
509         D3DVERTEXELEMENT9 decl[MAXD3DDECLLENGTH + 1];
510         DWORD fvf;
511     }
512     test_data[] =
513     {
514         {{
515             D3DDECL_END(),
516         }, 0},
517         {{
518             {0, 0, D3DDECLTYPE_FLOAT3, 0, D3DDECLUSAGE_POSITION, 0},
519             D3DDECL_END(),
520         }, D3DFVF_XYZ},
521         {{
522             {0, 0, D3DDECLTYPE_FLOAT4, 0, D3DDECLUSAGE_POSITIONT, 0},
523             D3DDECL_END(),
524         }, D3DFVF_XYZRHW},
525         {{
526             {0, 0, D3DDECLTYPE_FLOAT4, 0, D3DDECLUSAGE_POSITIONT, 0},
527             D3DDECL_END(),
528         }, D3DFVF_XYZRHW},
529         {{
530             {0, 0, D3DDECLTYPE_FLOAT3, 0, D3DDECLUSAGE_POSITION, 0},
531             {0, 12, D3DDECLTYPE_FLOAT1, 0, D3DDECLUSAGE_BLENDWEIGHT, 0},
532             D3DDECL_END(),
533         }, D3DFVF_XYZB1},
534         {{
535             {0, 0, D3DDECLTYPE_FLOAT3, 0, D3DDECLUSAGE_POSITION, 0},
536             {0, 12, D3DDECLTYPE_UBYTE4, 0, D3DDECLUSAGE_BLENDINDICES, 0},
537             D3DDECL_END(),
538         }, D3DFVF_XYZB1 | D3DFVF_LASTBETA_UBYTE4},
539         {{
540             {0, 0, D3DDECLTYPE_FLOAT3, 0, D3DDECLUSAGE_POSITION, 0},
541             {0, 12, D3DDECLTYPE_D3DCOLOR, 0, D3DDECLUSAGE_BLENDINDICES, 0},
542             D3DDECL_END(),
543         }, D3DFVF_XYZB1 | D3DFVF_LASTBETA_D3DCOLOR},
544         {{
545             {0, 0, D3DDECLTYPE_FLOAT3, 0, D3DDECLUSAGE_POSITION, 0},
546             {0, 12, D3DDECLTYPE_FLOAT2, 0, D3DDECLUSAGE_BLENDWEIGHT, 0},
547             D3DDECL_END(),
548         }, D3DFVF_XYZB2},
549         {{
550             {0, 0, D3DDECLTYPE_FLOAT3, 0, D3DDECLUSAGE_POSITION, 0},
551             {0, 12, D3DDECLTYPE_FLOAT1, 0, D3DDECLUSAGE_BLENDWEIGHT, 0},
552             {0, 16, D3DDECLTYPE_UBYTE4, 0, D3DDECLUSAGE_BLENDINDICES, 0},
553             D3DDECL_END(),
554         }, D3DFVF_XYZB2 | D3DFVF_LASTBETA_UBYTE4},
555         {{
556             {0, 0, D3DDECLTYPE_FLOAT3, 0, D3DDECLUSAGE_POSITION, 0},
557             {0, 12, D3DDECLTYPE_FLOAT1, 0, D3DDECLUSAGE_BLENDWEIGHT, 0},
558             {0, 16, D3DDECLTYPE_D3DCOLOR, 0, D3DDECLUSAGE_BLENDINDICES, 0},
559             D3DDECL_END(),
560         }, D3DFVF_XYZB2 | D3DFVF_LASTBETA_D3DCOLOR},
561         {{
562             {0, 0, D3DDECLTYPE_FLOAT3, 0, D3DDECLUSAGE_POSITION, 0},
563             {0, 12, D3DDECLTYPE_FLOAT3, 0, D3DDECLUSAGE_BLENDWEIGHT, 0},
564             D3DDECL_END(),
565         }, D3DFVF_XYZB3},
566         {{
567             {0, 0, D3DDECLTYPE_FLOAT3, 0, D3DDECLUSAGE_POSITION, 0},
568             {0, 12, D3DDECLTYPE_FLOAT2, 0, D3DDECLUSAGE_BLENDWEIGHT, 0},
569             {0, 20, D3DDECLTYPE_UBYTE4, 0, D3DDECLUSAGE_BLENDINDICES, 0},
570             D3DDECL_END(),
571         }, D3DFVF_XYZB3 | D3DFVF_LASTBETA_UBYTE4},
572         {{
573             {0, 0, D3DDECLTYPE_FLOAT3, 0, D3DDECLUSAGE_POSITION, 0},
574             {0, 12, D3DDECLTYPE_FLOAT2, 0, D3DDECLUSAGE_BLENDWEIGHT, 0},
575             {0, 20, D3DDECLTYPE_D3DCOLOR, 0, D3DDECLUSAGE_BLENDINDICES, 0},
576             D3DDECL_END(),
577         }, D3DFVF_XYZB3 | D3DFVF_LASTBETA_D3DCOLOR},
578         {{
579             {0, 0, D3DDECLTYPE_FLOAT3, 0, D3DDECLUSAGE_POSITION, 0},
580             {0, 12, D3DDECLTYPE_FLOAT4, 0, D3DDECLUSAGE_BLENDWEIGHT, 0},
581             D3DDECL_END(),
582         }, D3DFVF_XYZB4},
583         {{
584             {0, 0, D3DDECLTYPE_FLOAT3, 0, D3DDECLUSAGE_POSITION, 0},
585             {0, 12, D3DDECLTYPE_FLOAT3, 0, D3DDECLUSAGE_BLENDWEIGHT, 0},
586             {0, 24, D3DDECLTYPE_UBYTE4, 0, D3DDECLUSAGE_BLENDINDICES, 0},
587             D3DDECL_END(),
588         }, D3DFVF_XYZB4 | D3DFVF_LASTBETA_UBYTE4},
589         {{
590             {0, 0, D3DDECLTYPE_FLOAT3, 0, D3DDECLUSAGE_POSITION, 0},
591             {0, 12, D3DDECLTYPE_FLOAT3, 0, D3DDECLUSAGE_BLENDWEIGHT, 0},
592             {0, 24, D3DDECLTYPE_D3DCOLOR, 0, D3DDECLUSAGE_BLENDINDICES, 0},
593             D3DDECL_END(),
594         }, D3DFVF_XYZB4 | D3DFVF_LASTBETA_D3DCOLOR},
595         {{
596             {0, 0, D3DDECLTYPE_FLOAT3, 0, D3DDECLUSAGE_POSITION, 0},
597             {0, 12, D3DDECLTYPE_FLOAT4, 0, D3DDECLUSAGE_BLENDWEIGHT, 0},
598             {0, 28, D3DDECLTYPE_UBYTE4, 0, D3DDECLUSAGE_BLENDINDICES, 0},
599             D3DDECL_END(),
600         }, D3DFVF_XYZB5 | D3DFVF_LASTBETA_UBYTE4},
601         {{
602             {0, 0, D3DDECLTYPE_FLOAT3, 0, D3DDECLUSAGE_POSITION, 0},
603             {0, 12, D3DDECLTYPE_FLOAT4, 0, D3DDECLUSAGE_BLENDWEIGHT, 0},
604             {0, 28, D3DDECLTYPE_D3DCOLOR, 0, D3DDECLUSAGE_BLENDINDICES, 0},
605             D3DDECL_END(),
606         }, D3DFVF_XYZB5 | D3DFVF_LASTBETA_D3DCOLOR},
607         {{
608             {0, 0, D3DDECLTYPE_FLOAT3, 0, D3DDECLUSAGE_NORMAL, 0},
609             D3DDECL_END(),
610         }, D3DFVF_NORMAL},
611         {{
612             {0, 0, D3DDECLTYPE_FLOAT3, 0, D3DDECLUSAGE_NORMAL, 0},
613             {0, 12, D3DDECLTYPE_D3DCOLOR, 0, D3DDECLUSAGE_COLOR, 0},
614             D3DDECL_END(),
615         }, D3DFVF_NORMAL | D3DFVF_DIFFUSE},
616         {{
617             {0, 0, D3DDECLTYPE_FLOAT1, 0, D3DDECLUSAGE_PSIZE, 0},
618             D3DDECL_END(),
619         }, D3DFVF_PSIZE},
620         {{
621             {0, 0, D3DDECLTYPE_D3DCOLOR, 0, D3DDECLUSAGE_COLOR, 0},
622             D3DDECL_END(),
623         }, D3DFVF_DIFFUSE},
624         {{
625             {0, 0, D3DDECLTYPE_D3DCOLOR, 0, D3DDECLUSAGE_COLOR, 1},
626             D3DDECL_END(),
627         }, D3DFVF_SPECULAR},
628         /* Make sure textures of different sizes work. */
629         {{
630             {0, 0, D3DDECLTYPE_FLOAT1, 0, D3DDECLUSAGE_TEXCOORD, 0},
631             D3DDECL_END(),
632         }, D3DFVF_TEXCOORDSIZE1(0) | D3DFVF_TEX1},
633         {{
634             {0, 0, D3DDECLTYPE_FLOAT2, 0, D3DDECLUSAGE_TEXCOORD, 0},
635             D3DDECL_END(),
636         }, D3DFVF_TEXCOORDSIZE2(0) | D3DFVF_TEX1},
637         {{
638             {0, 0, D3DDECLTYPE_FLOAT3, 0, D3DDECLUSAGE_TEXCOORD, 0},
639             D3DDECL_END(),
640         }, D3DFVF_TEXCOORDSIZE3(0) | D3DFVF_TEX1},
641         {{
642             {0, 0, D3DDECLTYPE_FLOAT4, 0, D3DDECLUSAGE_TEXCOORD, 0},
643             D3DDECL_END(),
644         }, D3DFVF_TEXCOORDSIZE4(0) | D3DFVF_TEX1},
645         /* Make sure the TEXCOORD index works correctly - try several textures. */
646         {{
647             {0, 0, D3DDECLTYPE_FLOAT1, 0, D3DDECLUSAGE_TEXCOORD, 0},
648             {0, 4, D3DDECLTYPE_FLOAT3, 0, D3DDECLUSAGE_TEXCOORD, 1},
649             {0, 16, D3DDECLTYPE_FLOAT2, 0, D3DDECLUSAGE_TEXCOORD, 2},
650             {0, 24, D3DDECLTYPE_FLOAT4, 0, D3DDECLUSAGE_TEXCOORD, 3},
651             D3DDECL_END(),
652         }, D3DFVF_TEX4 | D3DFVF_TEXCOORDSIZE1(0) | D3DFVF_TEXCOORDSIZE3(1)
653                 | D3DFVF_TEXCOORDSIZE2(2) | D3DFVF_TEXCOORDSIZE4(3)},
654         /* Now try some combination tests. */
655         {{
656             {0, 0, D3DDECLTYPE_FLOAT3, 0, D3DDECLUSAGE_POSITION, 0},
657             {0, 12, D3DDECLTYPE_FLOAT4, 0, D3DDECLUSAGE_BLENDWEIGHT, 0},
658             {0, 28, D3DDECLTYPE_D3DCOLOR, 0, D3DDECLUSAGE_COLOR, 0},
659             {0, 32, D3DDECLTYPE_D3DCOLOR, 0, D3DDECLUSAGE_COLOR, 1},
660             {0, 36, D3DDECLTYPE_FLOAT2, 0, D3DDECLUSAGE_TEXCOORD, 0},
661             {0, 44, D3DDECLTYPE_FLOAT3, 0, D3DDECLUSAGE_TEXCOORD, 1},
662             D3DDECL_END(),
663         }, D3DFVF_XYZB4 | D3DFVF_DIFFUSE | D3DFVF_SPECULAR | D3DFVF_TEX2
664                 | D3DFVF_TEXCOORDSIZE2(0) | D3DFVF_TEXCOORDSIZE3(1)},
665         {{
666             {0, 0, D3DDECLTYPE_FLOAT3, 0, D3DDECLUSAGE_POSITION, 0},
667             {0, 12, D3DDECLTYPE_FLOAT3, 0, D3DDECLUSAGE_NORMAL, 0},
668             {0, 24, D3DDECLTYPE_FLOAT1, 0, D3DDECLUSAGE_PSIZE, 0},
669             {0, 28, D3DDECLTYPE_D3DCOLOR, 0, D3DDECLUSAGE_COLOR, 1},
670             {0, 32, D3DDECLTYPE_FLOAT1, 0, D3DDECLUSAGE_TEXCOORD, 0},
671             {0, 36, D3DDECLTYPE_FLOAT4, 0, D3DDECLUSAGE_TEXCOORD, 1},
672             D3DDECL_END(),
673         }, D3DFVF_XYZ | D3DFVF_NORMAL | D3DFVF_PSIZE | D3DFVF_SPECULAR | D3DFVF_TEX2
674                 | D3DFVF_TEXCOORDSIZE1(0) | D3DFVF_TEXCOORDSIZE4(1)},
675     };
676     unsigned int i;
677
678     for (i = 0; i < sizeof(test_data) / sizeof(*test_data); ++i)
679     {
680         test_decl_to_fvf(test_data[i].decl, test_data[i].fvf, D3D_OK, __LINE__, i);
681         test_fvf_to_decl(test_data[i].fvf, test_data[i].decl, D3D_OK, __LINE__, i);
682     }
683
684     /* Usage indices for position and normal are apparently ignored. */
685     {
686         const D3DVERTEXELEMENT9 decl[] =
687         {
688             {0, 0, D3DDECLTYPE_FLOAT3, 0, D3DDECLUSAGE_POSITION, 1},
689             D3DDECL_END(),
690         };
691         test_decl_to_fvf(decl, D3DFVF_XYZ, D3D_OK, __LINE__, 0);
692     }
693     {
694         const D3DVERTEXELEMENT9 decl[] =
695         {
696             {0, 0, D3DDECLTYPE_FLOAT3, 0, D3DDECLUSAGE_NORMAL, 1},
697             D3DDECL_END(),
698         };
699         test_decl_to_fvf(decl, D3DFVF_NORMAL, D3D_OK, __LINE__, 0);
700     }
701     /* D3DFVF_LASTBETA_UBYTE4 and D3DFVF_LASTBETA_D3DCOLOR are ignored if
702      * there are no blend matrices. */
703     {
704         const D3DVERTEXELEMENT9 decl[] =
705         {
706             {0, 0, D3DDECLTYPE_FLOAT3, 0, D3DDECLUSAGE_POSITION, 0},
707             D3DDECL_END(),
708         };
709         test_fvf_to_decl(D3DFVF_XYZ | D3DFVF_LASTBETA_UBYTE4, decl, D3D_OK, __LINE__, 0);
710     }
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_D3DCOLOR, decl, D3D_OK, __LINE__, 0);
718     }
719     /* D3DFVF_LASTBETA_UBYTE4 takes precedence over D3DFVF_LASTBETA_D3DCOLOR. */
720     {
721         const D3DVERTEXELEMENT9 decl[] =
722         {
723             {0, 0, D3DDECLTYPE_FLOAT3, 0, D3DDECLUSAGE_POSITION, 0},
724             {0, 12, D3DDECLTYPE_FLOAT4, 0, D3DDECLUSAGE_BLENDWEIGHT, 0},
725             {0, 28, D3DDECLTYPE_UBYTE4, 0, D3DDECLUSAGE_BLENDINDICES, 0},
726             D3DDECL_END(),
727         };
728         test_fvf_to_decl(D3DFVF_XYZB5 | D3DFVF_LASTBETA_D3DCOLOR | D3DFVF_LASTBETA_UBYTE4,
729                 decl, D3D_OK, __LINE__, 0);
730     }
731     /* These are supposed to fail, both ways. */
732     {
733         const D3DVERTEXELEMENT9 decl[] =
734         {
735             {0, 0, D3DDECLTYPE_FLOAT4, 0, D3DDECLUSAGE_POSITION, 0},
736             D3DDECL_END(),
737         };
738         test_decl_to_fvf(decl, D3DFVF_XYZW, D3DERR_INVALIDCALL, __LINE__, 0);
739         test_fvf_to_decl(D3DFVF_XYZW, decl, D3DERR_INVALIDCALL, __LINE__, 0);
740     }
741     {
742         const D3DVERTEXELEMENT9 decl[] =
743         {
744             {0, 0, D3DDECLTYPE_FLOAT4, 0, D3DDECLUSAGE_POSITION, 0},
745             {0, 16, D3DDECLTYPE_FLOAT3, 0, D3DDECLUSAGE_NORMAL, 0},
746             D3DDECL_END(),
747         };
748         test_decl_to_fvf(decl, D3DFVF_XYZW | D3DFVF_NORMAL, D3DERR_INVALIDCALL, __LINE__, 0);
749         test_fvf_to_decl(D3DFVF_XYZW | D3DFVF_NORMAL, decl, D3DERR_INVALIDCALL, __LINE__, 0);
750     }
751     {
752         const D3DVERTEXELEMENT9 decl[] =
753         {
754             {0, 0, D3DDECLTYPE_FLOAT3, 0, D3DDECLUSAGE_POSITION, 0},
755             {0, 12, D3DDECLTYPE_FLOAT4, 0, D3DDECLUSAGE_BLENDWEIGHT, 0},
756             {0, 28, D3DDECLTYPE_FLOAT1, 0, D3DDECLUSAGE_BLENDINDICES, 0},
757             D3DDECL_END(),
758         };
759         test_decl_to_fvf(decl, D3DFVF_XYZB5, D3DERR_INVALIDCALL, __LINE__, 0);
760         test_fvf_to_decl(D3DFVF_XYZB5, decl, D3DERR_INVALIDCALL, __LINE__, 0);
761     }
762     /* Test a declaration that can't be converted to an FVF. */
763     {
764         const D3DVERTEXELEMENT9 decl[] =
765         {
766             {0, 0, D3DDECLTYPE_FLOAT3, 0, D3DDECLUSAGE_POSITION, 0},
767             {0, 12, D3DDECLTYPE_FLOAT3, 0, D3DDECLUSAGE_NORMAL, 0},
768             {0, 24, D3DDECLTYPE_FLOAT1, 0, D3DDECLUSAGE_PSIZE, 0},
769             {0, 28, D3DDECLTYPE_D3DCOLOR, 0, D3DDECLUSAGE_COLOR, 1},
770             {0, 32, D3DDECLTYPE_FLOAT1, 0, D3DDECLUSAGE_TEXCOORD, 0},
771             /* 8 bytes padding */
772             {0, 44, D3DDECLTYPE_FLOAT4, 0, D3DDECLUSAGE_TEXCOORD, 1},
773             D3DDECL_END(),
774         };
775         test_decl_to_fvf(decl, 0, D3DERR_INVALIDCALL, __LINE__, 0);
776     }
777     /* Elements must be ordered by offset. */
778     {
779         const D3DVERTEXELEMENT9 decl[] =
780         {
781             {0, 12, D3DDECLTYPE_D3DCOLOR, 0, D3DDECLUSAGE_COLOR, 0},
782             {0, 0, D3DDECLTYPE_FLOAT3, 0, D3DDECLUSAGE_POSITION, 0},
783             D3DDECL_END(),
784         };
785         test_decl_to_fvf(decl, 0, D3DERR_INVALIDCALL, __LINE__, 0);
786     }
787     /* Basic tests for element order. */
788     {
789         const D3DVERTEXELEMENT9 decl[] =
790         {
791             {0, 0, D3DDECLTYPE_FLOAT3, 0, D3DDECLUSAGE_POSITION, 0},
792             {0, 12, D3DDECLTYPE_D3DCOLOR, 0, D3DDECLUSAGE_COLOR, 0},
793             {0, 16, D3DDECLTYPE_FLOAT3, 0, D3DDECLUSAGE_NORMAL, 0},
794             D3DDECL_END(),
795         };
796         test_decl_to_fvf(decl, 0, D3DERR_INVALIDCALL, __LINE__, 0);
797     }
798     {
799         const D3DVERTEXELEMENT9 decl[] =
800         {
801             {0, 0, D3DDECLTYPE_D3DCOLOR, 0, D3DDECLUSAGE_COLOR, 0},
802             {0, 4, D3DDECLTYPE_FLOAT3, 0, D3DDECLUSAGE_POSITION, 0},
803             D3DDECL_END(),
804         };
805         test_decl_to_fvf(decl, 0, D3DERR_INVALIDCALL, __LINE__, 0);
806     }
807     {
808         const D3DVERTEXELEMENT9 decl[] =
809         {
810             {0, 0, D3DDECLTYPE_FLOAT3, 0, D3DDECLUSAGE_NORMAL, 0},
811             {0, 12, D3DDECLTYPE_FLOAT3, 0, D3DDECLUSAGE_POSITION, 0},
812             D3DDECL_END(),
813         };
814         test_decl_to_fvf(decl, 0, D3DERR_INVALIDCALL, __LINE__, 0);
815     }
816     /* Textures must be ordered by texcoords. */
817     {
818         const D3DVERTEXELEMENT9 decl[] =
819         {
820             {0, 0, D3DDECLTYPE_FLOAT1, 0, D3DDECLUSAGE_TEXCOORD, 0},
821             {0, 4, D3DDECLTYPE_FLOAT3, 0, D3DDECLUSAGE_TEXCOORD, 2},
822             {0, 16, D3DDECLTYPE_FLOAT2, 0, D3DDECLUSAGE_TEXCOORD, 1},
823             {0, 24, D3DDECLTYPE_FLOAT4, 0, D3DDECLUSAGE_TEXCOORD, 3},
824             D3DDECL_END(),
825         };
826         test_decl_to_fvf(decl, 0, D3DERR_INVALIDCALL, __LINE__, 0);
827     }
828     /* Duplicate elements are not allowed. */
829     {
830         const D3DVERTEXELEMENT9 decl[] =
831         {
832             {0, 0, D3DDECLTYPE_FLOAT3, 0, D3DDECLUSAGE_POSITION, 0},
833             {0, 12, D3DDECLTYPE_D3DCOLOR, 0, D3DDECLUSAGE_COLOR, 0},
834             {0, 16, D3DDECLTYPE_D3DCOLOR, 0, D3DDECLUSAGE_COLOR, 0},
835             D3DDECL_END(),
836         };
837         test_decl_to_fvf(decl, 0, D3DERR_INVALIDCALL, __LINE__, 0);
838     }
839     /* Invalid FVFs cannot be converted to a declarator. */
840     test_fvf_to_decl(0xdeadbeef, NULL, D3DERR_INVALIDCALL, __LINE__, 0);
841 }
842
843 static void D3DXGetFVFVertexSizeTest(void)
844 {
845     UINT got;
846
847     compare_vertex_sizes (D3DFVF_XYZ, 12);
848
849     compare_vertex_sizes (D3DFVF_XYZB3, 24);
850
851     compare_vertex_sizes (D3DFVF_XYZB5, 32);
852
853     compare_vertex_sizes (D3DFVF_XYZ | D3DFVF_NORMAL, 24);
854
855     compare_vertex_sizes (D3DFVF_XYZ | D3DFVF_DIFFUSE, 16);
856
857     compare_vertex_sizes (
858         D3DFVF_XYZ |
859         D3DFVF_TEX1 |
860         D3DFVF_TEXCOORDSIZE1(0), 16);
861     compare_vertex_sizes (
862         D3DFVF_XYZ |
863         D3DFVF_TEX2 |
864         D3DFVF_TEXCOORDSIZE1(0) |
865         D3DFVF_TEXCOORDSIZE1(1), 20);
866
867     compare_vertex_sizes (
868         D3DFVF_XYZ |
869         D3DFVF_TEX1 |
870         D3DFVF_TEXCOORDSIZE2(0), 20);
871
872     compare_vertex_sizes (
873         D3DFVF_XYZ |
874         D3DFVF_TEX2 |
875         D3DFVF_TEXCOORDSIZE2(0) |
876         D3DFVF_TEXCOORDSIZE2(1), 28);
877
878     compare_vertex_sizes (
879         D3DFVF_XYZ |
880         D3DFVF_TEX6 |
881         D3DFVF_TEXCOORDSIZE2(0) |
882         D3DFVF_TEXCOORDSIZE2(1) |
883         D3DFVF_TEXCOORDSIZE2(2) |
884         D3DFVF_TEXCOORDSIZE2(3) |
885         D3DFVF_TEXCOORDSIZE2(4) |
886         D3DFVF_TEXCOORDSIZE2(5), 60);
887
888     compare_vertex_sizes (
889         D3DFVF_XYZ |
890         D3DFVF_TEX8 |
891         D3DFVF_TEXCOORDSIZE2(0) |
892         D3DFVF_TEXCOORDSIZE2(1) |
893         D3DFVF_TEXCOORDSIZE2(2) |
894         D3DFVF_TEXCOORDSIZE2(3) |
895         D3DFVF_TEXCOORDSIZE2(4) |
896         D3DFVF_TEXCOORDSIZE2(5) |
897         D3DFVF_TEXCOORDSIZE2(6) |
898         D3DFVF_TEXCOORDSIZE2(7), 76);
899
900     compare_vertex_sizes (
901         D3DFVF_XYZ |
902         D3DFVF_TEX1 |
903         D3DFVF_TEXCOORDSIZE3(0), 24);
904
905     compare_vertex_sizes (
906         D3DFVF_XYZ |
907         D3DFVF_TEX4 |
908         D3DFVF_TEXCOORDSIZE3(0) |
909         D3DFVF_TEXCOORDSIZE3(1) |
910         D3DFVF_TEXCOORDSIZE3(2) |
911         D3DFVF_TEXCOORDSIZE3(3), 60);
912
913     compare_vertex_sizes (
914         D3DFVF_XYZ |
915         D3DFVF_TEX1 |
916         D3DFVF_TEXCOORDSIZE4(0), 28);
917
918     compare_vertex_sizes (
919         D3DFVF_XYZ |
920         D3DFVF_TEX2 |
921         D3DFVF_TEXCOORDSIZE4(0) |
922         D3DFVF_TEXCOORDSIZE4(1), 44);
923
924     compare_vertex_sizes (
925         D3DFVF_XYZ |
926         D3DFVF_TEX3 |
927         D3DFVF_TEXCOORDSIZE4(0) |
928         D3DFVF_TEXCOORDSIZE4(1) |
929         D3DFVF_TEXCOORDSIZE4(2), 60);
930
931     compare_vertex_sizes (
932         D3DFVF_XYZB5 |
933         D3DFVF_NORMAL |
934         D3DFVF_DIFFUSE |
935         D3DFVF_SPECULAR |
936         D3DFVF_TEX8 |
937         D3DFVF_TEXCOORDSIZE4(0) |
938         D3DFVF_TEXCOORDSIZE4(1) |
939         D3DFVF_TEXCOORDSIZE4(2) |
940         D3DFVF_TEXCOORDSIZE4(3) |
941         D3DFVF_TEXCOORDSIZE4(4) |
942         D3DFVF_TEXCOORDSIZE4(5) |
943         D3DFVF_TEXCOORDSIZE4(6) |
944         D3DFVF_TEXCOORDSIZE4(7), 180);
945 }
946
947 static void D3DXIntersectTriTest(void)
948 {
949     BOOL exp_res, got_res;
950     D3DXVECTOR3 position, ray, vertex[3];
951     FLOAT exp_dist, got_dist, exp_u, got_u, exp_v, got_v;
952
953     vertex[0].x = 1.0f; vertex[0].y = 0.0f; vertex[0].z = 0.0f;
954     vertex[1].x = 2.0f; vertex[1].y = 0.0f; vertex[1].z = 0.0f;
955     vertex[2].x = 1.0f; vertex[2].y = 1.0f; vertex[2].z = 0.0f;
956
957     position.x = -14.5f; position.y = -23.75f; position.z = -32.0f;
958
959     ray.x = 2.0f; ray.y = 3.0f; ray.z = 4.0f;
960
961     exp_res = TRUE; exp_u = 0.5f; exp_v = 0.25f; exp_dist = 8.0f;
962
963     got_res = D3DXIntersectTri(&vertex[0],&vertex[1],&vertex[2],&position,&ray,&got_u,&got_v,&got_dist);
964     ok( got_res == exp_res, "Expected result = %d, got %d\n",exp_res,got_res);
965     ok( compare(exp_u,got_u), "Expected u = %f, got %f\n",exp_u,got_u);
966     ok( compare(exp_v,got_v), "Expected v = %f, got %f\n",exp_v,got_v);
967     ok( compare(exp_dist,got_dist), "Expected distance = %f, got %f\n",exp_dist,got_dist);
968
969 /*Only positive ray is taken in account*/
970
971     vertex[0].x = 1.0f; vertex[0].y = 0.0f; vertex[0].z = 0.0f;
972     vertex[1].x = 2.0f; vertex[1].y = 0.0f; vertex[1].z = 0.0f;
973     vertex[2].x = 1.0f; vertex[2].y = 1.0f; vertex[2].z = 0.0f;
974
975     position.x = 17.5f; position.y = 24.25f; position.z = 32.0f;
976
977     ray.x = 2.0f; ray.y = 3.0f; ray.z = 4.0f;
978
979     exp_res = FALSE;
980
981     got_res = D3DXIntersectTri(&vertex[0],&vertex[1],&vertex[2],&position,&ray,&got_u,&got_v,&got_dist);
982     ok( got_res == exp_res, "Expected result = %d, got %d\n",exp_res,got_res);
983
984 /*Intersection between ray and triangle in a same plane is considered as empty*/
985
986     vertex[0].x = 4.0f; vertex[0].y = 0.0f; vertex[0].z = 0.0f;
987     vertex[1].x = 6.0f; vertex[1].y = 0.0f; vertex[1].z = 0.0f;
988     vertex[2].x = 4.0f; vertex[2].y = 2.0f; vertex[2].z = 0.0f;
989
990     position.x = 1.0f; position.y = 1.0f; position.z = 0.0f;
991
992     ray.x = 1.0f; ray.y = 0.0f; ray.z = 0.0f;
993
994     exp_res = FALSE;
995
996     got_res = D3DXIntersectTri(&vertex[0],&vertex[1],&vertex[2],&position,&ray,&got_u,&got_v,&got_dist);
997     ok( got_res == exp_res, "Expected result = %d, got %d\n",exp_res,got_res);
998 }
999
1000 static void D3DXCreateMeshTest(void)
1001 {
1002     HRESULT hr;
1003     HWND wnd;
1004     IDirect3D9 *d3d;
1005     IDirect3DDevice9 *device, *test_device;
1006     D3DPRESENT_PARAMETERS d3dpp;
1007     ID3DXMesh *d3dxmesh;
1008     int i, size;
1009     D3DVERTEXELEMENT9 test_decl[MAX_FVF_DECL_SIZE];
1010     DWORD options;
1011     struct mesh mesh;
1012
1013     static const D3DVERTEXELEMENT9 decl1[3] = {
1014         {0, 0, D3DDECLTYPE_FLOAT3, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_POSITION, 0},
1015         {0, 12, D3DDECLTYPE_FLOAT3, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_NORMAL, 0},
1016         D3DDECL_END(), };
1017
1018     static const D3DVERTEXELEMENT9 decl2[] = {
1019         {0, 0, D3DDECLTYPE_FLOAT3, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_POSITION, 0},
1020         {0, 12, D3DDECLTYPE_FLOAT3, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_NORMAL, 0},
1021         {0, 24, D3DDECLTYPE_FLOAT1, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_PSIZE, 0},
1022         {0, 28, D3DDECLTYPE_D3DCOLOR, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_COLOR, 1},
1023         {0, 32, D3DDECLTYPE_FLOAT1, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_TEXCOORD, 0},
1024         /* 8 bytes padding */
1025         {0, 44, D3DDECLTYPE_FLOAT4, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_TEXCOORD, 1},
1026         D3DDECL_END(),
1027     };
1028
1029     hr = D3DXCreateMesh(0, 0, 0, NULL, NULL, NULL);
1030     ok(hr == D3DERR_INVALIDCALL, "Got result %x, expected %x (D3DERR_INVALIDCALL)\n", hr, D3DERR_INVALIDCALL);
1031
1032     hr = D3DXCreateMesh(1, 3, D3DXMESH_MANAGED, decl1, NULL, &d3dxmesh);
1033     ok(hr == D3DERR_INVALIDCALL, "Got result %x, expected %x (D3DERR_INVALIDCALL)\n", hr, D3DERR_INVALIDCALL);
1034
1035     wnd = CreateWindow("static", "d3dx9_test", 0, 0, 0, 0, 0, NULL, NULL, NULL, NULL);
1036     if (!wnd)
1037     {
1038         skip("Couldn't create application window\n");
1039         return;
1040     }
1041     d3d = Direct3DCreate9(D3D_SDK_VERSION);
1042     if (!d3d)
1043     {
1044         skip("Couldn't create IDirect3D9 object\n");
1045         DestroyWindow(wnd);
1046         return;
1047     }
1048
1049     ZeroMemory(&d3dpp, sizeof(d3dpp));
1050     d3dpp.Windowed = TRUE;
1051     d3dpp.SwapEffect = D3DSWAPEFFECT_DISCARD;
1052     hr = IDirect3D9_CreateDevice(d3d, D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, wnd, D3DCREATE_MIXED_VERTEXPROCESSING, &d3dpp, &device);
1053     if (FAILED(hr))
1054     {
1055         skip("Failed to create IDirect3DDevice9 object %#x\n", hr);
1056         IDirect3D9_Release(d3d);
1057         DestroyWindow(wnd);
1058         return;
1059     }
1060
1061     hr = D3DXCreateMesh(0, 3, D3DXMESH_MANAGED, decl1, device, &d3dxmesh);
1062     ok(hr == D3DERR_INVALIDCALL, "Got result %x, expected %x (D3DERR_INVALIDCALL)\n", hr, D3DERR_INVALIDCALL);
1063
1064     hr = D3DXCreateMesh(1, 0, D3DXMESH_MANAGED, decl1, device, &d3dxmesh);
1065     ok(hr == D3DERR_INVALIDCALL, "Got result %x, expected %x (D3DERR_INVALIDCALL)\n", hr, D3DERR_INVALIDCALL);
1066
1067     hr = D3DXCreateMesh(1, 3, 0, decl1, device, &d3dxmesh);
1068     ok(hr == D3D_OK, "Got result %x, expected %x (D3D_OK)\n", hr, D3D_OK);
1069
1070     if (hr == D3D_OK)
1071     {
1072         d3dxmesh->lpVtbl->Release(d3dxmesh);
1073     }
1074
1075     hr = D3DXCreateMesh(1, 3, D3DXMESH_MANAGED, 0, device, &d3dxmesh);
1076     ok(hr == D3DERR_INVALIDCALL, "Got result %x, expected %x (D3DERR_INVALIDCALL)\n", hr, D3DERR_INVALIDCALL);
1077
1078     hr = D3DXCreateMesh(1, 3, D3DXMESH_MANAGED, decl1, device, NULL);
1079     ok(hr == D3DERR_INVALIDCALL, "Got result %x, expected %x (D3DERR_INVALIDCALL)\n", hr, D3DERR_INVALIDCALL);
1080
1081     hr = D3DXCreateMesh(1, 3, D3DXMESH_MANAGED, decl1, device, &d3dxmesh);
1082     ok(hr == D3D_OK, "Got result %x, expected 0 (D3D_OK)\n", hr);
1083
1084     if (hr == D3D_OK)
1085     {
1086         /* device */
1087         hr = d3dxmesh->lpVtbl->GetDevice(d3dxmesh, NULL);
1088         ok(hr == D3DERR_INVALIDCALL, "Got result %x, expected %x (D3DERR_INVALIDCALL)\n", hr, D3DERR_INVALIDCALL);
1089
1090         hr = d3dxmesh->lpVtbl->GetDevice(d3dxmesh, &test_device);
1091         ok(hr == D3D_OK, "Got result %x, expected %x (D3D_OK)\n", hr, D3D_OK);
1092         ok(test_device == device, "Got result %p, expected %p\n", test_device, device);
1093
1094         if (hr == D3D_OK)
1095         {
1096             IDirect3DDevice9_Release(device);
1097         }
1098
1099         /* declaration */
1100         hr = d3dxmesh->lpVtbl->GetDeclaration(d3dxmesh, NULL);
1101         ok(hr == D3DERR_INVALIDCALL, "Got result %x, expected %x (D3DERR_INVALIDCALL)\n", hr, D3DERR_INVALIDCALL);
1102
1103         hr = d3dxmesh->lpVtbl->GetDeclaration(d3dxmesh, test_decl);
1104         ok(hr == D3D_OK, "Got result %x, expected 0 (D3D_OK)\n", hr);
1105
1106         if (hr == D3D_OK)
1107         {
1108             size = sizeof(decl1) / sizeof(decl1[0]);
1109             for (i = 0; i < size - 1; i++)
1110             {
1111                 ok(test_decl[i].Stream == decl1[i].Stream, "Returned stream %d, expected %d\n", test_decl[i].Stream, decl1[i].Stream);
1112                 ok(test_decl[i].Type == decl1[i].Type, "Returned type %d, expected %d\n", test_decl[i].Type, decl1[i].Type);
1113                 ok(test_decl[i].Method == decl1[i].Method, "Returned method %d, expected %d\n", test_decl[i].Method, decl1[i].Method);
1114                 ok(test_decl[i].Usage == decl1[i].Usage, "Returned usage %d, expected %d\n", test_decl[i].Usage, decl1[i].Usage);
1115                 ok(test_decl[i].UsageIndex == decl1[i].UsageIndex, "Returned usage index %d, expected %d\n", test_decl[i].UsageIndex, decl1[i].UsageIndex);
1116                 ok(test_decl[i].Offset == decl1[i].Offset, "Returned offset %d, expected %d\n", test_decl[i].Offset, decl1[i].Offset);
1117             }
1118             ok(decl1[size-1].Stream == 0xFF, "Returned too long vertex declaration\n"); /* end element */
1119         }
1120
1121         /* options */
1122         options = d3dxmesh->lpVtbl->GetOptions(d3dxmesh);
1123         ok(options == D3DXMESH_MANAGED, "Got result %x, expected %x (D3DXMESH_MANAGED)\n", options, D3DXMESH_MANAGED);
1124
1125         /* rest */
1126         if (!new_mesh(&mesh, 3, 1))
1127         {
1128             skip("Couldn't create mesh\n");
1129         }
1130         else
1131         {
1132             memset(mesh.vertices, 0, mesh.number_of_vertices * sizeof(*mesh.vertices));
1133             memset(mesh.faces, 0, mesh.number_of_faces * sizeof(*mesh.faces));
1134             mesh.fvf = D3DFVF_XYZ | D3DFVF_NORMAL;
1135
1136             compare_mesh("createmesh1", d3dxmesh, &mesh);
1137
1138             free_mesh(&mesh);
1139         }
1140
1141         d3dxmesh->lpVtbl->Release(d3dxmesh);
1142     }
1143
1144     /* Test a declaration that can't be converted to an FVF. */
1145     hr = D3DXCreateMesh(1, 3, D3DXMESH_MANAGED, decl2, device, &d3dxmesh);
1146     ok(hr == D3D_OK, "Got result %x, expected 0 (D3D_OK)\n", hr);
1147
1148     if (hr == D3D_OK)
1149     {
1150         /* device */
1151         hr = d3dxmesh->lpVtbl->GetDevice(d3dxmesh, NULL);
1152         ok(hr == D3DERR_INVALIDCALL, "Got result %x, expected %x (D3DERR_INVALIDCALL)\n", hr, D3DERR_INVALIDCALL);
1153
1154         hr = d3dxmesh->lpVtbl->GetDevice(d3dxmesh, &test_device);
1155         ok(hr == D3D_OK, "Got result %x, expected %x (D3D_OK)\n", hr, D3D_OK);
1156         ok(test_device == device, "Got result %p, expected %p\n", test_device, device);
1157
1158         if (hr == D3D_OK)
1159         {
1160             IDirect3DDevice9_Release(device);
1161         }
1162
1163         /* declaration */
1164         hr = d3dxmesh->lpVtbl->GetDeclaration(d3dxmesh, test_decl);
1165         ok(hr == D3D_OK, "Got result %x, expected 0 (D3D_OK)\n", hr);
1166
1167         if (hr == D3D_OK)
1168         {
1169             size = sizeof(decl2) / sizeof(decl2[0]);
1170             for (i = 0; i < size - 1; i++)
1171             {
1172                 ok(test_decl[i].Stream == decl2[i].Stream, "Returned stream %d, expected %d\n", test_decl[i].Stream, decl2[i].Stream);
1173                 ok(test_decl[i].Type == decl2[i].Type, "Returned type %d, expected %d\n", test_decl[i].Type, decl2[i].Type);
1174                 ok(test_decl[i].Method == decl2[i].Method, "Returned method %d, expected %d\n", test_decl[i].Method, decl2[i].Method);
1175                 ok(test_decl[i].Usage == decl2[i].Usage, "Returned usage %d, expected %d\n", test_decl[i].Usage, decl2[i].Usage);
1176                 ok(test_decl[i].UsageIndex == decl2[i].UsageIndex, "Returned usage index %d, expected %d\n", test_decl[i].UsageIndex, decl2[i].UsageIndex);
1177                 ok(test_decl[i].Offset == decl2[i].Offset, "Returned offset %d, expected %d\n", test_decl[i].Offset, decl2[i].Offset);
1178             }
1179             ok(decl2[size-1].Stream == 0xFF, "Returned too long vertex declaration\n"); /* end element */
1180         }
1181
1182         /* options */
1183         options = d3dxmesh->lpVtbl->GetOptions(d3dxmesh);
1184         ok(options == D3DXMESH_MANAGED, "Got result %x, expected %x (D3DXMESH_MANAGED)\n", options, D3DXMESH_MANAGED);
1185
1186         /* rest */
1187         if (!new_mesh(&mesh, 3, 1))
1188         {
1189             skip("Couldn't create mesh\n");
1190         }
1191         else
1192         {
1193             memset(mesh.vertices, 0, mesh.number_of_vertices * sizeof(*mesh.vertices));
1194             memset(mesh.faces, 0, mesh.number_of_faces * sizeof(*mesh.faces));
1195             mesh.fvf = 0;
1196             mesh.vertex_size = 60;
1197
1198             compare_mesh("createmesh2", d3dxmesh, &mesh);
1199
1200             free_mesh(&mesh);
1201         }
1202
1203         d3dxmesh->lpVtbl->Release(d3dxmesh);
1204     }
1205
1206     IDirect3DDevice9_Release(device);
1207     IDirect3D9_Release(d3d);
1208     DestroyWindow(wnd);
1209 }
1210
1211 static void D3DXCreateMeshFVFTest(void)
1212 {
1213     HRESULT hr;
1214     HWND wnd;
1215     IDirect3D9 *d3d;
1216     IDirect3DDevice9 *device, *test_device;
1217     D3DPRESENT_PARAMETERS d3dpp;
1218     ID3DXMesh *d3dxmesh;
1219     int i, size;
1220     D3DVERTEXELEMENT9 test_decl[MAX_FVF_DECL_SIZE];
1221     DWORD options;
1222     struct mesh mesh;
1223
1224     static const D3DVERTEXELEMENT9 decl[3] = {
1225         {0, 0, D3DDECLTYPE_FLOAT3, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_POSITION, 0},
1226         {0, 12, D3DDECLTYPE_FLOAT3, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_NORMAL, 0},
1227         D3DDECL_END(), };
1228
1229     hr = D3DXCreateMeshFVF(0, 0, 0, 0, NULL, NULL);
1230     ok(hr == D3DERR_INVALIDCALL, "Got result %x, expected %x (D3DERR_INVALIDCALL)\n", hr, D3DERR_INVALIDCALL);
1231
1232     hr = D3DXCreateMeshFVF(1, 3, D3DXMESH_MANAGED, D3DFVF_XYZ | D3DFVF_NORMAL, NULL, &d3dxmesh);
1233     ok(hr == D3DERR_INVALIDCALL, "Got result %x, expected %x (D3DERR_INVALIDCALL)\n", hr, D3DERR_INVALIDCALL);
1234
1235     wnd = CreateWindow("static", "d3dx9_test", 0, 0, 0, 0, 0, NULL, NULL, NULL, NULL);
1236     if (!wnd)
1237     {
1238         skip("Couldn't create application window\n");
1239         return;
1240     }
1241     d3d = Direct3DCreate9(D3D_SDK_VERSION);
1242     if (!d3d)
1243     {
1244         skip("Couldn't create IDirect3D9 object\n");
1245         DestroyWindow(wnd);
1246         return;
1247     }
1248
1249     ZeroMemory(&d3dpp, sizeof(d3dpp));
1250     d3dpp.Windowed = TRUE;
1251     d3dpp.SwapEffect = D3DSWAPEFFECT_DISCARD;
1252     hr = IDirect3D9_CreateDevice(d3d, D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, wnd, D3DCREATE_MIXED_VERTEXPROCESSING, &d3dpp, &device);
1253     if (FAILED(hr))
1254     {
1255         skip("Failed to create IDirect3DDevice9 object %#x\n", hr);
1256         IDirect3D9_Release(d3d);
1257         DestroyWindow(wnd);
1258         return;
1259     }
1260
1261     hr = D3DXCreateMeshFVF(0, 3, D3DXMESH_MANAGED, D3DFVF_XYZ | D3DFVF_NORMAL, device, &d3dxmesh);
1262     ok(hr == D3DERR_INVALIDCALL, "Got result %x, expected %x (D3DERR_INVALIDCALL)\n", hr, D3DERR_INVALIDCALL);
1263
1264     hr = D3DXCreateMeshFVF(1, 0, D3DXMESH_MANAGED, D3DFVF_XYZ | D3DFVF_NORMAL, device, &d3dxmesh);
1265     ok(hr == D3DERR_INVALIDCALL, "Got result %x, expected %x (D3DERR_INVALIDCALL)\n", hr, D3DERR_INVALIDCALL);
1266
1267     hr = D3DXCreateMeshFVF(1, 3, 0, D3DFVF_XYZ | D3DFVF_NORMAL, device, &d3dxmesh);
1268     ok(hr == D3D_OK, "Got result %x, expected %x (D3D_OK)\n", hr, D3D_OK);
1269
1270     if (hr == D3D_OK)
1271     {
1272         d3dxmesh->lpVtbl->Release(d3dxmesh);
1273     }
1274
1275     hr = D3DXCreateMeshFVF(1, 3, D3DXMESH_MANAGED, 0xdeadbeef, device, &d3dxmesh);
1276     ok(hr == D3DERR_INVALIDCALL, "Got result %x, expected %x (D3DERR_INVALIDCALL)\n", hr, D3DERR_INVALIDCALL);
1277
1278     hr = D3DXCreateMeshFVF(1, 3, D3DXMESH_MANAGED, D3DFVF_XYZ | D3DFVF_NORMAL, device, NULL);
1279     ok(hr == D3DERR_INVALIDCALL, "Got result %x, expected %x (D3DERR_INVALIDCALL)\n", hr, D3DERR_INVALIDCALL);
1280
1281     hr = D3DXCreateMeshFVF(1, 3, D3DXMESH_MANAGED, D3DFVF_XYZ | D3DFVF_NORMAL, device, &d3dxmesh);
1282     ok(hr == D3D_OK, "Got result %x, expected 0 (D3D_OK)\n", hr);
1283
1284     if (hr == D3D_OK)
1285     {
1286         /* device */
1287         hr = d3dxmesh->lpVtbl->GetDevice(d3dxmesh, NULL);
1288         ok(hr == D3DERR_INVALIDCALL, "Got result %x, expected %x (D3DERR_INVALIDCALL)\n", hr, D3DERR_INVALIDCALL);
1289
1290         hr = d3dxmesh->lpVtbl->GetDevice(d3dxmesh, &test_device);
1291         ok(hr == D3D_OK, "Got result %x, expected %x (D3D_OK)\n", hr, D3D_OK);
1292         ok(test_device == device, "Got result %p, expected %p\n", test_device, device);
1293
1294         if (hr == D3D_OK)
1295         {
1296             IDirect3DDevice9_Release(device);
1297         }
1298
1299         /* declaration */
1300         hr = d3dxmesh->lpVtbl->GetDeclaration(d3dxmesh, NULL);
1301         ok(hr == D3DERR_INVALIDCALL, "Got result %x, expected %x (D3DERR_INVALIDCALL)\n", hr, D3DERR_INVALIDCALL);
1302
1303         hr = d3dxmesh->lpVtbl->GetDeclaration(d3dxmesh, test_decl);
1304         ok(hr == D3D_OK, "Got result %x, expected 0 (D3D_OK)\n", hr);
1305
1306         if (hr == D3D_OK)
1307         {
1308             size = sizeof(decl) / sizeof(decl[0]);
1309             for (i = 0; i < size - 1; i++)
1310             {
1311                 ok(test_decl[i].Stream == decl[i].Stream, "Returned stream %d, expected %d\n", test_decl[i].Stream, decl[i].Stream);
1312                 ok(test_decl[i].Type == decl[i].Type, "Returned type %d, expected %d\n", test_decl[i].Type, decl[i].Type);
1313                 ok(test_decl[i].Method == decl[i].Method, "Returned method %d, expected %d\n", test_decl[i].Method, decl[i].Method);
1314                 ok(test_decl[i].Usage == decl[i].Usage, "Returned usage %d, expected %d\n", test_decl[i].Usage, decl[i].Usage);
1315                 ok(test_decl[i].UsageIndex == decl[i].UsageIndex, "Returned usage index %d, expected %d\n",
1316                    test_decl[i].UsageIndex, decl[i].UsageIndex);
1317                 ok(test_decl[i].Offset == decl[i].Offset, "Returned offset %d, expected %d\n", test_decl[i].Offset, decl[i].Offset);
1318             }
1319             ok(decl[size-1].Stream == 0xFF, "Returned too long vertex declaration\n"); /* end element */
1320         }
1321
1322         /* options */
1323         options = d3dxmesh->lpVtbl->GetOptions(d3dxmesh);
1324         ok(options == D3DXMESH_MANAGED, "Got result %x, expected %x (D3DXMESH_MANAGED)\n", options, D3DXMESH_MANAGED);
1325
1326         /* rest */
1327         if (!new_mesh(&mesh, 3, 1))
1328         {
1329             skip("Couldn't create mesh\n");
1330         }
1331         else
1332         {
1333             memset(mesh.vertices, 0, mesh.number_of_vertices * sizeof(*mesh.vertices));
1334             memset(mesh.faces, 0, mesh.number_of_faces * sizeof(*mesh.faces));
1335             mesh.fvf = D3DFVF_XYZ | D3DFVF_NORMAL;
1336
1337             compare_mesh("createmeshfvf", d3dxmesh, &mesh);
1338
1339             free_mesh(&mesh);
1340         }
1341
1342         d3dxmesh->lpVtbl->Release(d3dxmesh);
1343     }
1344
1345     IDirect3DDevice9_Release(device);
1346     IDirect3D9_Release(d3d);
1347     DestroyWindow(wnd);
1348 }
1349
1350 struct sincos_table
1351 {
1352     float *sin;
1353     float *cos;
1354 };
1355
1356 static void free_sincos_table(struct sincos_table *sincos_table)
1357 {
1358     HeapFree(GetProcessHeap(), 0, sincos_table->cos);
1359     HeapFree(GetProcessHeap(), 0, sincos_table->sin);
1360 }
1361
1362 /* pre compute sine and cosine tables; caller must free */
1363 static BOOL compute_sincos_table(struct sincos_table *sincos_table, float angle_start, float angle_step, int n)
1364 {
1365     float angle;
1366     int i;
1367
1368     sincos_table->sin = HeapAlloc(GetProcessHeap(), 0, n * sizeof(*sincos_table->sin));
1369     if (!sincos_table->sin)
1370     {
1371         return FALSE;
1372     }
1373     sincos_table->cos = HeapAlloc(GetProcessHeap(), 0, n * sizeof(*sincos_table->cos));
1374     if (!sincos_table->cos)
1375     {
1376         HeapFree(GetProcessHeap(), 0, sincos_table->sin);
1377         return FALSE;
1378     }
1379
1380     angle = angle_start;
1381     for (i = 0; i < n; i++)
1382     {
1383         sincos_table->sin[i] = sin(angle);
1384         sincos_table->cos[i] = cos(angle);
1385         angle += angle_step;
1386     }
1387
1388     return TRUE;
1389 }
1390
1391 static WORD vertex_index(UINT slices, int slice, int stack)
1392 {
1393     return stack*slices+slice+1;
1394 }
1395
1396 /* slices = subdivisions along xy plane, stacks = subdivisions along z axis */
1397 static BOOL compute_sphere(struct mesh *mesh, FLOAT radius, UINT slices, UINT stacks)
1398 {
1399     float theta_step, theta_start;
1400     struct sincos_table theta;
1401     float phi_step, phi_start;
1402     struct sincos_table phi;
1403     DWORD number_of_vertices, number_of_faces;
1404     DWORD vertex, face;
1405     int slice, stack;
1406
1407     /* theta = angle on xy plane wrt x axis */
1408     theta_step = M_PI / stacks;
1409     theta_start = theta_step;
1410
1411     /* phi = angle on xz plane wrt z axis */
1412     phi_step = -2 * M_PI / slices;
1413     phi_start = M_PI / 2;
1414
1415     if (!compute_sincos_table(&theta, theta_start, theta_step, stacks))
1416     {
1417         return FALSE;
1418     }
1419     if (!compute_sincos_table(&phi, phi_start, phi_step, slices))
1420     {
1421         free_sincos_table(&theta);
1422         return FALSE;
1423     }
1424
1425     number_of_vertices = 2 + slices * (stacks-1);
1426     number_of_faces = 2 * slices + (stacks - 2) * (2 * slices);
1427
1428     if (!new_mesh(mesh, number_of_vertices, number_of_faces))
1429     {
1430         free_sincos_table(&phi);
1431         free_sincos_table(&theta);
1432         return FALSE;
1433     }
1434
1435     vertex = 0;
1436     face = 0;
1437     stack = 0;
1438
1439     mesh->vertices[vertex].normal.x = 0.0f;
1440     mesh->vertices[vertex].normal.y = 0.0f;
1441     mesh->vertices[vertex].normal.z = 1.0f;
1442     mesh->vertices[vertex].position.x = 0.0f;
1443     mesh->vertices[vertex].position.y = 0.0f;
1444     mesh->vertices[vertex].position.z = radius;
1445     vertex++;
1446
1447     for (stack = 0; stack < stacks - 1; stack++)
1448     {
1449         for (slice = 0; slice < slices; slice++)
1450         {
1451             mesh->vertices[vertex].normal.x = theta.sin[stack] * phi.cos[slice];
1452             mesh->vertices[vertex].normal.y = theta.sin[stack] * phi.sin[slice];
1453             mesh->vertices[vertex].normal.z = theta.cos[stack];
1454             mesh->vertices[vertex].position.x = radius * theta.sin[stack] * phi.cos[slice];
1455             mesh->vertices[vertex].position.y = radius * theta.sin[stack] * phi.sin[slice];
1456             mesh->vertices[vertex].position.z = radius * theta.cos[stack];
1457             vertex++;
1458
1459             if (slice > 0)
1460             {
1461                 if (stack == 0)
1462                 {
1463                     /* top stack is triangle fan */
1464                     mesh->faces[face][0] = 0;
1465                     mesh->faces[face][1] = slice + 1;
1466                     mesh->faces[face][2] = slice;
1467                     face++;
1468                 }
1469                 else
1470                 {
1471                     /* stacks in between top and bottom are quad strips */
1472                     mesh->faces[face][0] = vertex_index(slices, slice-1, stack-1);
1473                     mesh->faces[face][1] = vertex_index(slices, slice, stack-1);
1474                     mesh->faces[face][2] = vertex_index(slices, slice-1, stack);
1475                     face++;
1476
1477                     mesh->faces[face][0] = vertex_index(slices, slice, stack-1);
1478                     mesh->faces[face][1] = vertex_index(slices, slice, stack);
1479                     mesh->faces[face][2] = vertex_index(slices, slice-1, stack);
1480                     face++;
1481                 }
1482             }
1483         }
1484
1485         if (stack == 0)
1486         {
1487             mesh->faces[face][0] = 0;
1488             mesh->faces[face][1] = 1;
1489             mesh->faces[face][2] = slice;
1490             face++;
1491         }
1492         else
1493         {
1494             mesh->faces[face][0] = vertex_index(slices, slice-1, stack-1);
1495             mesh->faces[face][1] = vertex_index(slices, 0, stack-1);
1496             mesh->faces[face][2] = vertex_index(slices, slice-1, stack);
1497             face++;
1498
1499             mesh->faces[face][0] = vertex_index(slices, 0, stack-1);
1500             mesh->faces[face][1] = vertex_index(slices, 0, stack);
1501             mesh->faces[face][2] = vertex_index(slices, slice-1, stack);
1502             face++;
1503         }
1504     }
1505
1506     mesh->vertices[vertex].position.x = 0.0f;
1507     mesh->vertices[vertex].position.y = 0.0f;
1508     mesh->vertices[vertex].position.z = -radius;
1509     mesh->vertices[vertex].normal.x = 0.0f;
1510     mesh->vertices[vertex].normal.y = 0.0f;
1511     mesh->vertices[vertex].normal.z = -1.0f;
1512
1513     /* bottom stack is triangle fan */
1514     for (slice = 1; slice < slices; slice++)
1515     {
1516         mesh->faces[face][0] = vertex_index(slices, slice-1, stack-1);
1517         mesh->faces[face][1] = vertex_index(slices, slice, stack-1);
1518         mesh->faces[face][2] = vertex;
1519         face++;
1520     }
1521
1522     mesh->faces[face][0] = vertex_index(slices, slice-1, stack-1);
1523     mesh->faces[face][1] = vertex_index(slices, 0, stack-1);
1524     mesh->faces[face][2] = vertex;
1525
1526     free_sincos_table(&phi);
1527     free_sincos_table(&theta);
1528
1529     return TRUE;
1530 }
1531
1532 static void test_sphere(IDirect3DDevice9 *device, FLOAT radius, UINT slices, UINT stacks)
1533 {
1534     HRESULT hr;
1535     ID3DXMesh *sphere;
1536     struct mesh mesh;
1537     char name[256];
1538
1539     hr = D3DXCreateSphere(device, radius, slices, stacks, &sphere, NULL);
1540     ok(hr == D3D_OK, "Got result %x, expected 0 (D3D_OK)\n", hr);
1541     if (hr != D3D_OK)
1542     {
1543         skip("Couldn't create sphere\n");
1544         return;
1545     }
1546
1547     if (!compute_sphere(&mesh, radius, slices, stacks))
1548     {
1549         skip("Couldn't create mesh\n");
1550         sphere->lpVtbl->Release(sphere);
1551         return;
1552     }
1553
1554     mesh.fvf = D3DFVF_XYZ | D3DFVF_NORMAL;
1555
1556     sprintf(name, "sphere (%g, %u, %u)", radius, slices, stacks);
1557     compare_mesh(name, sphere, &mesh);
1558
1559     free_mesh(&mesh);
1560
1561     sphere->lpVtbl->Release(sphere);
1562 }
1563
1564 static void D3DXCreateSphereTest(void)
1565 {
1566     HRESULT hr;
1567     HWND wnd;
1568     IDirect3D9* d3d;
1569     IDirect3DDevice9* device;
1570     D3DPRESENT_PARAMETERS d3dpp;
1571     ID3DXMesh* sphere = NULL;
1572
1573     hr = D3DXCreateSphere(NULL, 0.0f, 0, 0, NULL, NULL);
1574     ok(hr == D3DERR_INVALIDCALL, "Got result %x, expected %x (D3DERR_INVALIDCALL)\n",hr,D3DERR_INVALIDCALL);
1575
1576     hr = D3DXCreateSphere(NULL, 0.1f, 0, 0, NULL, NULL);
1577     ok(hr == D3DERR_INVALIDCALL, "Got result %x, expected %x (D3DERR_INVALIDCALL)\n",hr,D3DERR_INVALIDCALL);
1578
1579     hr = D3DXCreateSphere(NULL, 0.0f, 1, 0, NULL, NULL);
1580     ok(hr == D3DERR_INVALIDCALL, "Got result %x, expected %x (D3DERR_INVALIDCALL)\n",hr,D3DERR_INVALIDCALL);
1581
1582     hr = D3DXCreateSphere(NULL, 0.0f, 0, 1, NULL, NULL);
1583     ok(hr == D3DERR_INVALIDCALL, "Got result %x, expected %x (D3DERR_INVALIDCALL)\n",hr,D3DERR_INVALIDCALL);
1584
1585     wnd = CreateWindow("static", "d3dx9_test", 0, 0, 0, 0, 0, NULL, NULL, NULL, NULL);
1586     d3d = Direct3DCreate9(D3D_SDK_VERSION);
1587     if (!wnd)
1588     {
1589         skip("Couldn't create application window\n");
1590         return;
1591     }
1592     if (!d3d)
1593     {
1594         skip("Couldn't create IDirect3D9 object\n");
1595         DestroyWindow(wnd);
1596         return;
1597     }
1598
1599     ZeroMemory(&d3dpp, sizeof(d3dpp));
1600     d3dpp.Windowed = TRUE;
1601     d3dpp.SwapEffect = D3DSWAPEFFECT_DISCARD;
1602     hr = IDirect3D9_CreateDevice(d3d, D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, wnd, D3DCREATE_MIXED_VERTEXPROCESSING, &d3dpp, &device);
1603     if (FAILED(hr))
1604     {
1605         skip("Failed to create IDirect3DDevice9 object %#x\n", hr);
1606         IDirect3D9_Release(d3d);
1607         DestroyWindow(wnd);
1608         return;
1609     }
1610
1611     hr = D3DXCreateSphere(device, 1.0f, 1, 1, &sphere, NULL);
1612     ok(hr == D3DERR_INVALIDCALL, "Got result %x, expected %x (D3DERR_INVALIDCALL)\n",hr,D3DERR_INVALIDCALL);
1613
1614     hr = D3DXCreateSphere(device, 1.0f, 2, 1, &sphere, NULL);
1615     ok(hr == D3DERR_INVALIDCALL, "Got result %x, expected %x (D3DERR_INVALIDCALL)\n", hr, D3DERR_INVALIDCALL);
1616
1617     hr = D3DXCreateSphere(device, 1.0f, 1, 2, &sphere, NULL);
1618     ok(hr == D3DERR_INVALIDCALL, "Got result %x, expected %x (D3DERR_INVALIDCALL)\n", hr, D3DERR_INVALIDCALL);
1619
1620     hr = D3DXCreateSphere(device, -0.1f, 1, 2, &sphere, NULL);
1621     ok(hr == D3DERR_INVALIDCALL, "Got result %x, expected %x (D3DERR_INVALIDCALL)\n", hr, D3DERR_INVALIDCALL);
1622
1623     test_sphere(device, 0.0f, 2, 2);
1624     test_sphere(device, 1.0f, 2, 2);
1625     test_sphere(device, 1.0f, 3, 2);
1626     test_sphere(device, 1.0f, 4, 4);
1627     test_sphere(device, 1.0f, 3, 4);
1628     test_sphere(device, 5.0f, 6, 7);
1629     test_sphere(device, 10.0f, 11, 12);
1630
1631     IDirect3DDevice9_Release(device);
1632     IDirect3D9_Release(d3d);
1633     DestroyWindow(wnd);
1634 }
1635
1636 static BOOL compute_cylinder(struct mesh *mesh, FLOAT radius1, FLOAT radius2, FLOAT length, UINT slices, UINT stacks)
1637 {
1638     float theta_step, theta_start;
1639     struct sincos_table theta;
1640     FLOAT delta_radius, radius, radius_step;
1641     FLOAT z, z_step, z_normal;
1642     DWORD number_of_vertices, number_of_faces;
1643     DWORD vertex, face;
1644     int slice, stack;
1645
1646     /* theta = angle on xy plane wrt x axis */
1647     theta_step = -2 * M_PI / slices;
1648     theta_start = M_PI / 2;
1649
1650     if (!compute_sincos_table(&theta, theta_start, theta_step, slices))
1651     {
1652         return FALSE;
1653     }
1654
1655     number_of_vertices = 2 + (slices * (3 + stacks));
1656     number_of_faces = 2 * slices + stacks * (2 * slices);
1657
1658     if (!new_mesh(mesh, number_of_vertices, number_of_faces))
1659     {
1660         free_sincos_table(&theta);
1661         return FALSE;
1662     }
1663
1664     vertex = 0;
1665     face = 0;
1666     stack = 0;
1667
1668     delta_radius = radius1 - radius2;
1669     radius = radius1;
1670     radius_step = delta_radius / stacks;
1671
1672     z = -length / 2;
1673     z_step = length / stacks;
1674     z_normal = delta_radius / length;
1675     if (isnan(z_normal))
1676     {
1677         z_normal = 0.0f;
1678     }
1679
1680     mesh->vertices[vertex].normal.x = 0.0f;
1681     mesh->vertices[vertex].normal.y = 0.0f;
1682     mesh->vertices[vertex].normal.z = -1.0f;
1683     mesh->vertices[vertex].position.x = 0.0f;
1684     mesh->vertices[vertex].position.y = 0.0f;
1685     mesh->vertices[vertex++].position.z = z;
1686
1687     for (slice = 0; slice < slices; slice++, vertex++)
1688     {
1689         mesh->vertices[vertex].normal.x = 0.0f;
1690         mesh->vertices[vertex].normal.y = 0.0f;
1691         mesh->vertices[vertex].normal.z = -1.0f;
1692         mesh->vertices[vertex].position.x = radius * theta.cos[slice];
1693         mesh->vertices[vertex].position.y = radius * theta.sin[slice];
1694         mesh->vertices[vertex].position.z = z;
1695
1696         if (slice > 0)
1697         {
1698             mesh->faces[face][0] = 0;
1699             mesh->faces[face][1] = slice;
1700             mesh->faces[face++][2] = slice + 1;
1701         }
1702     }
1703
1704     mesh->faces[face][0] = 0;
1705     mesh->faces[face][1] = slice;
1706     mesh->faces[face++][2] = 1;
1707
1708     for (stack = 1; stack <= stacks+1; stack++)
1709     {
1710         for (slice = 0; slice < slices; slice++, vertex++)
1711         {
1712             mesh->vertices[vertex].normal.x = theta.cos[slice];
1713             mesh->vertices[vertex].normal.y = theta.sin[slice];
1714             mesh->vertices[vertex].normal.z = z_normal;
1715             D3DXVec3Normalize(&mesh->vertices[vertex].normal, &mesh->vertices[vertex].normal);
1716             mesh->vertices[vertex].position.x = radius * theta.cos[slice];
1717             mesh->vertices[vertex].position.y = radius * theta.sin[slice];
1718             mesh->vertices[vertex].position.z = z;
1719
1720             if (stack > 1 && slice > 0)
1721             {
1722                 mesh->faces[face][0] = vertex_index(slices, slice-1, stack-1);
1723                 mesh->faces[face][1] = vertex_index(slices, slice-1, stack);
1724                 mesh->faces[face++][2] = vertex_index(slices, slice, stack-1);
1725
1726                 mesh->faces[face][0] = vertex_index(slices, slice, stack-1);
1727                 mesh->faces[face][1] = vertex_index(slices, slice-1, stack);
1728                 mesh->faces[face++][2] = vertex_index(slices, slice, stack);
1729             }
1730         }
1731
1732         if (stack > 1)
1733         {
1734             mesh->faces[face][0] = vertex_index(slices, slice-1, stack-1);
1735             mesh->faces[face][1] = vertex_index(slices, slice-1, stack);
1736             mesh->faces[face++][2] = vertex_index(slices, 0, stack-1);
1737
1738             mesh->faces[face][0] = vertex_index(slices, 0, stack-1);
1739             mesh->faces[face][1] = vertex_index(slices, slice-1, stack);
1740             mesh->faces[face++][2] = vertex_index(slices, 0, stack);
1741         }
1742
1743         if (stack < stacks + 1)
1744         {
1745             z += z_step;
1746             radius -= radius_step;
1747         }
1748     }
1749
1750     for (slice = 0; slice < slices; slice++, vertex++)
1751     {
1752         mesh->vertices[vertex].normal.x = 0.0f;
1753         mesh->vertices[vertex].normal.y = 0.0f;
1754         mesh->vertices[vertex].normal.z = 1.0f;
1755         mesh->vertices[vertex].position.x = radius * theta.cos[slice];
1756         mesh->vertices[vertex].position.y = radius * theta.sin[slice];
1757         mesh->vertices[vertex].position.z = z;
1758
1759         if (slice > 0)
1760         {
1761             mesh->faces[face][0] = vertex_index(slices, slice-1, stack);
1762             mesh->faces[face][1] = number_of_vertices - 1;
1763             mesh->faces[face++][2] = vertex_index(slices, slice, stack);
1764         }
1765     }
1766
1767     mesh->vertices[vertex].position.x = 0.0f;
1768     mesh->vertices[vertex].position.y = 0.0f;
1769     mesh->vertices[vertex].position.z = z;
1770     mesh->vertices[vertex].normal.x = 0.0f;
1771     mesh->vertices[vertex].normal.y = 0.0f;
1772     mesh->vertices[vertex].normal.z = 1.0f;
1773
1774     mesh->faces[face][0] = vertex_index(slices, slice-1, stack);
1775     mesh->faces[face][1] = number_of_vertices - 1;
1776     mesh->faces[face][2] = vertex_index(slices, 0, stack);
1777
1778     free_sincos_table(&theta);
1779
1780     return TRUE;
1781 }
1782
1783 static void test_cylinder(IDirect3DDevice9 *device, FLOAT radius1, FLOAT radius2, FLOAT length, UINT slices, UINT stacks)
1784 {
1785     HRESULT hr;
1786     ID3DXMesh *cylinder;
1787     struct mesh mesh;
1788     char name[256];
1789
1790     hr = D3DXCreateCylinder(device, radius1, radius2, length, slices, stacks, &cylinder, NULL);
1791     todo_wine ok(hr == D3D_OK, "Got result %x, expected 0 (D3D_OK)\n", hr);
1792     if (hr != D3D_OK)
1793     {
1794         skip("Couldn't create cylinder\n");
1795         return;
1796     }
1797
1798     if (!compute_cylinder(&mesh, radius1, radius2, length, slices, stacks))
1799     {
1800         skip("Couldn't create mesh\n");
1801         cylinder->lpVtbl->Release(cylinder);
1802         return;
1803     }
1804
1805     mesh.fvf = D3DFVF_XYZ | D3DFVF_NORMAL;
1806
1807     sprintf(name, "cylinder (%g, %g, %g, %u, %u)", radius1, radius2, length, slices, stacks);
1808     compare_mesh(name, cylinder, &mesh);
1809
1810     free_mesh(&mesh);
1811
1812     cylinder->lpVtbl->Release(cylinder);
1813 }
1814
1815 static void D3DXCreateCylinderTest(void)
1816 {
1817     HRESULT hr;
1818     HWND wnd;
1819     IDirect3D9* d3d;
1820     IDirect3DDevice9* device;
1821     D3DPRESENT_PARAMETERS d3dpp;
1822     ID3DXMesh* cylinder = NULL;
1823
1824     hr = D3DXCreateCylinder(NULL, 0.0f, 0.0f, 0.0f, 0, 0, NULL, NULL);
1825     todo_wine ok(hr == D3DERR_INVALIDCALL, "Got result %x, expected %x (D3DERR_INVALIDCALL)\n",hr,D3DERR_INVALIDCALL);
1826
1827     hr = D3DXCreateCylinder(NULL, 1.0f, 1.0f, 1.0f, 2, 1, &cylinder, NULL);
1828     todo_wine ok(hr == D3DERR_INVALIDCALL, "Got result %x, expected %x (D3DERR_INVALIDCALL)\n",hr,D3DERR_INVALIDCALL);
1829
1830     wnd = CreateWindow("static", "d3dx9_test", 0, 0, 0, 0, 0, NULL, NULL, NULL, NULL);
1831     d3d = Direct3DCreate9(D3D_SDK_VERSION);
1832     if (!wnd)
1833     {
1834         skip("Couldn't create application window\n");
1835         return;
1836     }
1837     if (!d3d)
1838     {
1839         skip("Couldn't create IDirect3D9 object\n");
1840         DestroyWindow(wnd);
1841         return;
1842     }
1843
1844     ZeroMemory(&d3dpp, sizeof(d3dpp));
1845     d3dpp.Windowed = TRUE;
1846     d3dpp.SwapEffect = D3DSWAPEFFECT_DISCARD;
1847     hr = IDirect3D9_CreateDevice(d3d, D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, wnd, D3DCREATE_MIXED_VERTEXPROCESSING, &d3dpp, &device);
1848     if (FAILED(hr))
1849     {
1850         skip("Failed to create IDirect3DDevice9 object %#x\n", hr);
1851         IDirect3D9_Release(d3d);
1852         DestroyWindow(wnd);
1853         return;
1854     }
1855
1856     hr = D3DXCreateCylinder(device, -0.1f, 1.0f, 1.0f, 2, 1, &cylinder, NULL);
1857     todo_wine ok(hr == D3DERR_INVALIDCALL, "Got result %x, expected %x (D3DERR_INVALIDCALL)\n",hr,D3DERR_INVALIDCALL);
1858
1859     hr = D3DXCreateCylinder(device, 0.0f, 1.0f, 1.0f, 2, 1, &cylinder, NULL);
1860     todo_wine ok(hr == D3D_OK, "Got result %x, expected 0 (D3D_OK)\n",hr);
1861
1862     if (SUCCEEDED(hr) && cylinder)
1863     {
1864         cylinder->lpVtbl->Release(cylinder);
1865     }
1866
1867     hr = D3DXCreateCylinder(device, 1.0f, -0.1f, 1.0f, 2, 1, &cylinder, NULL);
1868     todo_wine ok(hr == D3DERR_INVALIDCALL, "Got result %x, expected %x (D3DERR_INVALIDCALL)\n",hr,D3DERR_INVALIDCALL);
1869
1870     hr = D3DXCreateCylinder(device, 1.0f, 0.0f, 1.0f, 2, 1, &cylinder, NULL);
1871     todo_wine ok(hr == D3D_OK, "Got result %x, expected 0 (D3D_OK)\n",hr);
1872
1873     if (SUCCEEDED(hr) && cylinder)
1874     {
1875         cylinder->lpVtbl->Release(cylinder);
1876     }
1877
1878     hr = D3DXCreateCylinder(device, 1.0f, 1.0f, -0.1f, 2, 1, &cylinder, NULL);
1879     todo_wine ok(hr == D3DERR_INVALIDCALL, "Got result %x, expected %x (D3DERR_INVALIDCALL)\n",hr,D3DERR_INVALIDCALL);
1880
1881     /* Test with length == 0.0f succeeds */
1882     hr = D3DXCreateCylinder(device, 1.0f, 1.0f, 0.0f, 2, 1, &cylinder, NULL);
1883     todo_wine ok(hr == D3D_OK, "Got result %x, expected 0 (D3D_OK)\n",hr);
1884
1885     if (SUCCEEDED(hr) && cylinder)
1886     {
1887         cylinder->lpVtbl->Release(cylinder);
1888     }
1889
1890     hr = D3DXCreateCylinder(device, 1.0f, 1.0f, 1.0f, 1, 1, &cylinder, NULL);
1891     todo_wine ok(hr == D3DERR_INVALIDCALL, "Got result %x, expected %x (D3DERR_INVALIDCALL)\n",hr,D3DERR_INVALIDCALL);
1892
1893     hr = D3DXCreateCylinder(device, 1.0f, 1.0f, 1.0f, 2, 0, &cylinder, NULL);
1894     todo_wine ok(hr == D3DERR_INVALIDCALL, "Got result %x, expected %x (D3DERR_INVALIDCALL)\n",hr,D3DERR_INVALIDCALL);
1895
1896     hr = D3DXCreateCylinder(device, 1.0f, 1.0f, 1.0f, 2, 1, NULL, NULL);
1897     todo_wine ok(hr == D3DERR_INVALIDCALL, "Got result %x, expected %x (D3DERR_INVALIDCALL)\n",hr,D3DERR_INVALIDCALL);
1898
1899     test_cylinder(device, 0.0f, 0.0f, 0.0f, 2, 1);
1900     test_cylinder(device, 1.0f, 1.0f, 1.0f, 2, 1);
1901     test_cylinder(device, 1.0f, 1.0f, 2.0f, 3, 4);
1902     test_cylinder(device, 3.0f, 2.0f, 4.0f, 3, 4);
1903     test_cylinder(device, 2.0f, 3.0f, 4.0f, 3, 4);
1904     test_cylinder(device, 3.0f, 4.0f, 5.0f, 11, 20);
1905
1906     IDirect3DDevice9_Release(device);
1907     IDirect3D9_Release(d3d);
1908     DestroyWindow(wnd);
1909 }
1910
1911 static void test_get_decl_length(void)
1912 {
1913     static const D3DVERTEXELEMENT9 declaration1[] =
1914     {
1915         {0, 0, D3DDECLTYPE_FLOAT1, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_POSITION, 0},
1916         {1, 0, D3DDECLTYPE_FLOAT2, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_POSITION, 0},
1917         {2, 0, D3DDECLTYPE_FLOAT3, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_POSITION, 0},
1918         {3, 0, D3DDECLTYPE_FLOAT4, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_POSITION, 0},
1919         {4, 0, D3DDECLTYPE_D3DCOLOR, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_POSITION, 0},
1920         {5, 0, D3DDECLTYPE_UBYTE4, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_POSITION, 0},
1921         {6, 0, D3DDECLTYPE_SHORT2, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_POSITION, 0},
1922         {7, 0, D3DDECLTYPE_SHORT4, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_POSITION, 0},
1923         {8, 0, D3DDECLTYPE_UBYTE4N, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_POSITION, 0},
1924         {9, 0, D3DDECLTYPE_SHORT2N, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_POSITION, 0},
1925         {10, 0, D3DDECLTYPE_SHORT4N, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_POSITION, 0},
1926         {11, 0, D3DDECLTYPE_UDEC3, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_POSITION, 0},
1927         {12, 0, D3DDECLTYPE_DEC3N, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_POSITION, 0},
1928         {13, 0, D3DDECLTYPE_FLOAT16_2, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_POSITION, 0},
1929         {14, 0, D3DDECLTYPE_FLOAT16_4, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_POSITION, 0},
1930         D3DDECL_END(),
1931     };
1932     static const D3DVERTEXELEMENT9 declaration2[] =
1933     {
1934         {0, 8, D3DDECLTYPE_FLOAT1, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_POSITION, 0},
1935         {1, 8, D3DDECLTYPE_FLOAT2, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_POSITION, 0},
1936         {2, 8, D3DDECLTYPE_FLOAT3, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_POSITION, 0},
1937         {3, 8, D3DDECLTYPE_FLOAT4, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_POSITION, 0},
1938         {4, 8, D3DDECLTYPE_D3DCOLOR, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_POSITION, 0},
1939         {5, 8, D3DDECLTYPE_UBYTE4, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_POSITION, 0},
1940         {6, 8, D3DDECLTYPE_SHORT2, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_POSITION, 0},
1941         {7, 8, D3DDECLTYPE_SHORT4, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_POSITION, 0},
1942         {0, 8, D3DDECLTYPE_UBYTE4N, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_POSITION, 0},
1943         {1, 8, D3DDECLTYPE_SHORT2N, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_POSITION, 0},
1944         {2, 8, D3DDECLTYPE_SHORT4N, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_POSITION, 0},
1945         {3, 8, D3DDECLTYPE_UDEC3, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_POSITION, 0},
1946         {4, 8, D3DDECLTYPE_DEC3N, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_POSITION, 0},
1947         {5, 8, D3DDECLTYPE_FLOAT16_2, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_POSITION, 0},
1948         {6, 8, D3DDECLTYPE_FLOAT16_4, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_POSITION, 0},
1949         {7, 8, D3DDECLTYPE_FLOAT1, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_POSITION, 0},
1950         D3DDECL_END(),
1951     };
1952     UINT size;
1953
1954     size = D3DXGetDeclLength(declaration1);
1955     ok(size == 15, "Got size %u, expected 15.\n", size);
1956
1957     size = D3DXGetDeclLength(declaration2);
1958     ok(size == 16, "Got size %u, expected 16.\n", size);
1959 }
1960
1961 static void test_get_decl_vertex_size(void)
1962 {
1963     static const D3DVERTEXELEMENT9 declaration1[] =
1964     {
1965         {0, 0, D3DDECLTYPE_FLOAT1, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_POSITION, 0},
1966         {1, 0, D3DDECLTYPE_FLOAT2, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_POSITION, 0},
1967         {2, 0, D3DDECLTYPE_FLOAT3, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_POSITION, 0},
1968         {3, 0, D3DDECLTYPE_FLOAT4, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_POSITION, 0},
1969         {4, 0, D3DDECLTYPE_D3DCOLOR, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_POSITION, 0},
1970         {5, 0, D3DDECLTYPE_UBYTE4, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_POSITION, 0},
1971         {6, 0, D3DDECLTYPE_SHORT2, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_POSITION, 0},
1972         {7, 0, D3DDECLTYPE_SHORT4, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_POSITION, 0},
1973         {8, 0, D3DDECLTYPE_UBYTE4N, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_POSITION, 0},
1974         {9, 0, D3DDECLTYPE_SHORT2N, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_POSITION, 0},
1975         {10, 0, D3DDECLTYPE_SHORT4N, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_POSITION, 0},
1976         {11, 0, D3DDECLTYPE_UDEC3, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_POSITION, 0},
1977         {12, 0, D3DDECLTYPE_DEC3N, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_POSITION, 0},
1978         {13, 0, D3DDECLTYPE_FLOAT16_2, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_POSITION, 0},
1979         {14, 0, D3DDECLTYPE_FLOAT16_4, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_POSITION, 0},
1980         D3DDECL_END(),
1981     };
1982     static const D3DVERTEXELEMENT9 declaration2[] =
1983     {
1984         {0, 8, D3DDECLTYPE_FLOAT1, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_POSITION, 0},
1985         {1, 8, D3DDECLTYPE_FLOAT2, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_POSITION, 0},
1986         {2, 8, D3DDECLTYPE_FLOAT3, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_POSITION, 0},
1987         {3, 8, D3DDECLTYPE_FLOAT4, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_POSITION, 0},
1988         {4, 8, D3DDECLTYPE_D3DCOLOR, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_POSITION, 0},
1989         {5, 8, D3DDECLTYPE_UBYTE4, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_POSITION, 0},
1990         {6, 8, D3DDECLTYPE_SHORT2, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_POSITION, 0},
1991         {7, 8, D3DDECLTYPE_SHORT4, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_POSITION, 0},
1992         {0, 8, D3DDECLTYPE_UBYTE4N, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_POSITION, 0},
1993         {1, 8, D3DDECLTYPE_SHORT2N, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_POSITION, 0},
1994         {2, 8, D3DDECLTYPE_SHORT4N, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_POSITION, 0},
1995         {3, 8, D3DDECLTYPE_UDEC3, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_POSITION, 0},
1996         {4, 8, D3DDECLTYPE_DEC3N, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_POSITION, 0},
1997         {5, 8, D3DDECLTYPE_FLOAT16_2, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_POSITION, 0},
1998         {6, 8, D3DDECLTYPE_FLOAT16_4, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_POSITION, 0},
1999         {7, 8, D3DDECLTYPE_FLOAT1, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_POSITION, 0},
2000         D3DDECL_END(),
2001     };
2002     static const UINT sizes1[] =
2003     {
2004         4,  8,  12, 16,
2005         4,  4,  4,  8,
2006         4,  4,  8,  4,
2007         4,  4,  8,  0,
2008     };
2009     static const UINT sizes2[] =
2010     {
2011         12, 16, 20, 24,
2012         12, 12, 16, 16,
2013     };
2014     unsigned int i;
2015     UINT size;
2016
2017     size = D3DXGetDeclVertexSize(NULL, 0);
2018     ok(size == 0, "Got size %#x, expected 0.\n", size);
2019
2020     for (i = 0; i < 16; ++i)
2021     {
2022         size = D3DXGetDeclVertexSize(declaration1, i);
2023         ok(size == sizes1[i], "Got size %u for stream %u, expected %u.\n", size, i, sizes1[i]);
2024     }
2025
2026     for (i = 0; i < 8; ++i)
2027     {
2028         size = D3DXGetDeclVertexSize(declaration2, i);
2029         ok(size == sizes2[i], "Got size %u for stream %u, expected %u.\n", size, i, sizes2[i]);
2030     }
2031 }
2032
2033 START_TEST(mesh)
2034 {
2035     D3DXBoundProbeTest();
2036     D3DXComputeBoundingBoxTest();
2037     D3DXComputeBoundingSphereTest();
2038     D3DXGetFVFVertexSizeTest();
2039     D3DXIntersectTriTest();
2040     D3DXCreateMeshTest();
2041     D3DXCreateMeshFVFTest();
2042     D3DXCreateSphereTest();
2043     D3DXCreateCylinderTest();
2044     test_get_decl_length();
2045     test_get_decl_vertex_size();
2046     test_fvf_decl_conversion();
2047 }