xref: /reactos/dll/directx/wine/d3dx9_36/mesh.c (revision 89c23f17)
1 #ifdef __REACTOS__
2 #include "precomp.h"
3 #include <rmxftmpl.h>
4 #else
5  /*
6  * Mesh operations specific to D3DX9.
7  *
8  * Copyright (C) 2005 Henri Verbeet
9  * Copyright (C) 2006 Ivan Gyurdiev
10  * Copyright (C) 2009 David Adam
11  * Copyright (C) 2010 Tony Wasserka
12  * Copyright (C) 2011 Dylan Smith
13  * Copyright (C) 2011 Michael Mc Donnell
14  * Copyright (C) 2013 Christian Costa
15  *
16  * This library is free software; you can redistribute it and/or
17  * modify it under the terms of the GNU Lesser General Public
18  * License as published by the Free Software Foundation; either
19  * version 2.1 of the License, or (at your option) any later version.
20  *
21  * This library is distributed in the hope that it will be useful,
22  * but WITHOUT ANY WARRANTY; without even the implied warranty of
23  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
24  * Lesser General Public License for more details.
25  *
26  * You should have received a copy of the GNU Lesser General Public
27  * License along with this library; if not, write to the Free Software
28  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
29  */
30 
31 
32 #include <assert.h>
33 #include <float.h>
34 
35 #include "d3dx9_private.h"
36 #undef MAKE_DDHRESULT
37 #include "dxfile.h"
38 #include "rmxfguid.h"
39 #include "rmxftmpl.h"
40 #include "wine/list.h"
41 #endif /* __REACTOS__ */
42 
43 WINE_DEFAULT_DEBUG_CHANNEL(d3dx);
44 
45 struct d3dx9_mesh
46 {
47     ID3DXMesh ID3DXMesh_iface;
48     LONG ref;
49 
50     DWORD numfaces;
51     DWORD numvertices;
52     DWORD options;
53     DWORD fvf;
54     IDirect3DDevice9 *device;
55     D3DVERTEXELEMENT9 cached_declaration[MAX_FVF_DECL_SIZE];
56     IDirect3DVertexDeclaration9 *vertex_declaration;
57     UINT vertex_declaration_size;
58     UINT num_elem;
59     IDirect3DVertexBuffer9 *vertex_buffer;
60     IDirect3DIndexBuffer9 *index_buffer;
61     DWORD *attrib_buffer;
62     int attrib_buffer_lock_count;
63     DWORD attrib_table_size;
64     D3DXATTRIBUTERANGE *attrib_table;
65 };
66 
67 static const UINT d3dx_decltype_size[] =
68 {
69    /* D3DDECLTYPE_FLOAT1    */ sizeof(FLOAT),
70    /* D3DDECLTYPE_FLOAT2    */ sizeof(D3DXVECTOR2),
71    /* D3DDECLTYPE_FLOAT3    */ sizeof(D3DXVECTOR3),
72    /* D3DDECLTYPE_FLOAT4    */ sizeof(D3DXVECTOR4),
73    /* D3DDECLTYPE_D3DCOLOR  */ sizeof(D3DCOLOR),
74    /* D3DDECLTYPE_UBYTE4    */ 4 * sizeof(BYTE),
75    /* D3DDECLTYPE_SHORT2    */ 2 * sizeof(SHORT),
76    /* D3DDECLTYPE_SHORT4    */ 4 * sizeof(SHORT),
77    /* D3DDECLTYPE_UBYTE4N   */ 4 * sizeof(BYTE),
78    /* D3DDECLTYPE_SHORT2N   */ 2 * sizeof(SHORT),
79    /* D3DDECLTYPE_SHORT4N   */ 4 * sizeof(SHORT),
80    /* D3DDECLTYPE_USHORT2N  */ 2 * sizeof(USHORT),
81    /* D3DDECLTYPE_USHORT4N  */ 4 * sizeof(USHORT),
82    /* D3DDECLTYPE_UDEC3     */ 4, /* 3 * 10 bits + 2 padding */
83    /* D3DDECLTYPE_DEC3N     */ 4,
84    /* D3DDECLTYPE_FLOAT16_2 */ 2 * sizeof(D3DXFLOAT16),
85    /* D3DDECLTYPE_FLOAT16_4 */ 4 * sizeof(D3DXFLOAT16),
86 };
87 
88 static inline struct d3dx9_mesh *impl_from_ID3DXMesh(ID3DXMesh *iface)
89 {
90     return CONTAINING_RECORD(iface, struct d3dx9_mesh, ID3DXMesh_iface);
91 }
92 
93 static HRESULT WINAPI d3dx9_mesh_QueryInterface(ID3DXMesh *iface, REFIID riid, void **out)
94 {
95     TRACE("iface %p, riid %s, out %p.\n", iface, debugstr_guid(riid), out);
96 
97     if (IsEqualGUID(riid, &IID_IUnknown) ||
98         IsEqualGUID(riid, &IID_ID3DXBaseMesh) ||
99         IsEqualGUID(riid, &IID_ID3DXMesh))
100     {
101         iface->lpVtbl->AddRef(iface);
102         *out = iface;
103         return S_OK;
104     }
105 
106     WARN("Interface %s not found.\n", debugstr_guid(riid));
107 
108     return E_NOINTERFACE;
109 }
110 
111 static ULONG WINAPI d3dx9_mesh_AddRef(ID3DXMesh *iface)
112 {
113     struct d3dx9_mesh *mesh = impl_from_ID3DXMesh(iface);
114     ULONG refcount = InterlockedIncrement(&mesh->ref);
115 
116     TRACE("%p increasing refcount to %u.\n", mesh, refcount);
117 
118     return refcount;
119 }
120 
121 static ULONG WINAPI d3dx9_mesh_Release(ID3DXMesh *iface)
122 {
123     struct d3dx9_mesh *mesh = impl_from_ID3DXMesh(iface);
124     ULONG refcount = InterlockedDecrement(&mesh->ref);
125 
126     TRACE("%p decreasing refcount to %u.\n", mesh, refcount);
127 
128     if (!refcount)
129     {
130         IDirect3DIndexBuffer9_Release(mesh->index_buffer);
131         IDirect3DVertexBuffer9_Release(mesh->vertex_buffer);
132         if (mesh->vertex_declaration)
133             IDirect3DVertexDeclaration9_Release(mesh->vertex_declaration);
134         IDirect3DDevice9_Release(mesh->device);
135         HeapFree(GetProcessHeap(), 0, mesh->attrib_buffer);
136         HeapFree(GetProcessHeap(), 0, mesh->attrib_table);
137         HeapFree(GetProcessHeap(), 0, mesh);
138     }
139 
140     return refcount;
141 }
142 
143 static HRESULT WINAPI d3dx9_mesh_DrawSubset(ID3DXMesh *iface, DWORD attrib_id)
144 {
145     struct d3dx9_mesh *This = impl_from_ID3DXMesh(iface);
146     HRESULT hr;
147     DWORD face_start;
148     DWORD face_end = 0;
149     DWORD vertex_size;
150 
151     TRACE("iface %p, attrib_id %u.\n", iface, attrib_id);
152 
153     if (!This->vertex_declaration)
154     {
155         WARN("Can't draw a mesh with an invalid vertex declaration.\n");
156         return E_FAIL;
157     }
158 
159     vertex_size = iface->lpVtbl->GetNumBytesPerVertex(iface);
160 
161     hr = IDirect3DDevice9_SetVertexDeclaration(This->device, This->vertex_declaration);
162     if (FAILED(hr)) return hr;
163     hr = IDirect3DDevice9_SetStreamSource(This->device, 0, This->vertex_buffer, 0, vertex_size);
164     if (FAILED(hr)) return hr;
165     hr = IDirect3DDevice9_SetIndices(This->device, This->index_buffer);
166     if (FAILED(hr)) return hr;
167 
168     while (face_end < This->numfaces)
169     {
170         for (face_start = face_end; face_start < This->numfaces; face_start++)
171         {
172             if (This->attrib_buffer[face_start] == attrib_id)
173                 break;
174         }
175         if (face_start >= This->numfaces)
176             break;
177         for (face_end = face_start + 1; face_end < This->numfaces; face_end++)
178         {
179             if (This->attrib_buffer[face_end] != attrib_id)
180                 break;
181         }
182 
183         hr = IDirect3DDevice9_DrawIndexedPrimitive(This->device, D3DPT_TRIANGLELIST,
184                 0, 0, This->numvertices, face_start * 3, face_end - face_start);
185         if (FAILED(hr)) return hr;
186     }
187 
188     return D3D_OK;
189 }
190 
191 static DWORD WINAPI d3dx9_mesh_GetNumFaces(ID3DXMesh *iface)
192 {
193     struct d3dx9_mesh *mesh = impl_from_ID3DXMesh(iface);
194 
195     TRACE("iface %p.\n", iface);
196 
197     return mesh->numfaces;
198 }
199 
200 static DWORD WINAPI d3dx9_mesh_GetNumVertices(ID3DXMesh *iface)
201 {
202     struct d3dx9_mesh *mesh = impl_from_ID3DXMesh(iface);
203 
204     TRACE("iface %p.\n", iface);
205 
206     return mesh->numvertices;
207 }
208 
209 static DWORD WINAPI d3dx9_mesh_GetFVF(ID3DXMesh *iface)
210 {
211     struct d3dx9_mesh *mesh = impl_from_ID3DXMesh(iface);
212 
213     TRACE("iface %p.\n", iface);
214 
215     return mesh->fvf;
216 }
217 
218 static void copy_declaration(D3DVERTEXELEMENT9 *dst, const D3DVERTEXELEMENT9 *src, UINT num_elem)
219 {
220     memcpy(dst, src, num_elem * sizeof(*src));
221 }
222 
223 static HRESULT WINAPI d3dx9_mesh_GetDeclaration(ID3DXMesh *iface, D3DVERTEXELEMENT9 declaration[MAX_FVF_DECL_SIZE])
224 {
225     struct d3dx9_mesh *mesh = impl_from_ID3DXMesh(iface);
226 
227     TRACE("iface %p, declaration %p.\n", iface, declaration);
228 
229     if (!declaration)
230         return D3DERR_INVALIDCALL;
231 
232     copy_declaration(declaration, mesh->cached_declaration, mesh->num_elem);
233 
234     return D3D_OK;
235 }
236 
237 static DWORD WINAPI d3dx9_mesh_GetNumBytesPerVertex(ID3DXMesh *iface)
238 {
239     struct d3dx9_mesh *mesh = impl_from_ID3DXMesh(iface);
240 
241     TRACE("iface %p.\n", iface);
242 
243     return mesh->vertex_declaration_size;
244 }
245 
246 static DWORD WINAPI d3dx9_mesh_GetOptions(ID3DXMesh *iface)
247 {
248     struct d3dx9_mesh *mesh = impl_from_ID3DXMesh(iface);
249 
250     TRACE("iface %p.\n", iface);
251 
252     return mesh->options;
253 }
254 
255 static HRESULT WINAPI d3dx9_mesh_GetDevice(struct ID3DXMesh *iface, struct IDirect3DDevice9 **device)
256 {
257     struct d3dx9_mesh *mesh = impl_from_ID3DXMesh(iface);
258 
259     TRACE("iface %p, device %p.\n", iface, device);
260 
261     if (!device)
262         return D3DERR_INVALIDCALL;
263     *device = mesh->device;
264     IDirect3DDevice9_AddRef(mesh->device);
265 
266     return D3D_OK;
267 }
268 
269 static HRESULT WINAPI d3dx9_mesh_CloneMeshFVF(struct ID3DXMesh *iface, DWORD options, DWORD fvf,
270         struct IDirect3DDevice9 *device, struct ID3DXMesh **clone_mesh)
271 {
272     HRESULT hr;
273     D3DVERTEXELEMENT9 declaration[MAX_FVF_DECL_SIZE];
274 
275     TRACE("iface %p, options %#x, fvf %#x, device %p, clone_mesh %p.\n",
276             iface, options, fvf, device, clone_mesh);
277 
278     if (FAILED(hr = D3DXDeclaratorFromFVF(fvf, declaration)))
279         return hr;
280 
281     return iface->lpVtbl->CloneMesh(iface, options, declaration, device, clone_mesh);
282 }
283 
284 static FLOAT scale_clamp_ubyten(FLOAT value)
285 {
286     value = value * UCHAR_MAX;
287 
288     if (value < 0.0f)
289     {
290         return 0.0f;
291     }
292     else
293     {
294         if (value > UCHAR_MAX) /* Clamp at 255 */
295             return UCHAR_MAX;
296         else
297             return value;
298     }
299 }
300 
301 static FLOAT scale_clamp_shortn(FLOAT value)
302 {
303     value = value * SHRT_MAX;
304 
305     /* The tests show that the range is SHRT_MIN + 1 to SHRT_MAX. */
306     if (value <= SHRT_MIN)
307     {
308         return SHRT_MIN + 1;
309     }
310     else if (value > SHRT_MAX)
311     {
312          return SHRT_MAX;
313     }
314     else
315     {
316         return value;
317     }
318 }
319 
320 static FLOAT scale_clamp_ushortn(FLOAT value)
321 {
322     value = value * USHRT_MAX;
323 
324     if (value < 0.0f)
325     {
326         return 0.0f;
327     }
328     else
329     {
330         if (value > USHRT_MAX) /* Clamp at 65535 */
331             return USHRT_MAX;
332         else
333             return value;
334     }
335 }
336 
337 static INT simple_round(FLOAT value)
338 {
339     int res = (INT)(value + 0.5f);
340 
341     return res;
342 }
343 
344 static void convert_float4(BYTE *dst, const D3DXVECTOR4 *src, D3DDECLTYPE type_dst)
345 {
346     BOOL fixme_once = FALSE;
347 
348     switch (type_dst)
349     {
350         case D3DDECLTYPE_FLOAT1:
351         {
352             FLOAT *dst_ptr = (FLOAT*)dst;
353             *dst_ptr = src->x;
354             break;
355         }
356         case D3DDECLTYPE_FLOAT2:
357         {
358             D3DXVECTOR2 *dst_ptr = (D3DXVECTOR2*)dst;
359             dst_ptr->x = src->x;
360             dst_ptr->y = src->y;
361             break;
362         }
363         case D3DDECLTYPE_FLOAT3:
364         {
365             D3DXVECTOR3 *dst_ptr = (D3DXVECTOR3*)dst;
366             dst_ptr->x = src->x;
367             dst_ptr->y = src->y;
368             dst_ptr->z = src->z;
369             break;
370         }
371         case D3DDECLTYPE_FLOAT4:
372         {
373             D3DXVECTOR4 *dst_ptr = (D3DXVECTOR4*)dst;
374             dst_ptr->x = src->x;
375             dst_ptr->y = src->y;
376             dst_ptr->z = src->z;
377             dst_ptr->w = src->w;
378             break;
379         }
380         case D3DDECLTYPE_D3DCOLOR:
381         {
382             dst[0] = (BYTE)simple_round(scale_clamp_ubyten(src->z));
383             dst[1] = (BYTE)simple_round(scale_clamp_ubyten(src->y));
384             dst[2] = (BYTE)simple_round(scale_clamp_ubyten(src->x));
385             dst[3] = (BYTE)simple_round(scale_clamp_ubyten(src->w));
386             break;
387         }
388         case D3DDECLTYPE_UBYTE4:
389         {
390             dst[0] = src->x < 0.0f ? 0 : (BYTE)simple_round(src->x);
391             dst[1] = src->y < 0.0f ? 0 : (BYTE)simple_round(src->y);
392             dst[2] = src->z < 0.0f ? 0 : (BYTE)simple_round(src->z);
393             dst[3] = src->w < 0.0f ? 0 : (BYTE)simple_round(src->w);
394             break;
395         }
396         case D3DDECLTYPE_SHORT2:
397         {
398             SHORT *dst_ptr = (SHORT*)dst;
399             dst_ptr[0] = (SHORT)simple_round(src->x);
400             dst_ptr[1] = (SHORT)simple_round(src->y);
401             break;
402         }
403         case D3DDECLTYPE_SHORT4:
404         {
405             SHORT *dst_ptr = (SHORT*)dst;
406             dst_ptr[0] = (SHORT)simple_round(src->x);
407             dst_ptr[1] = (SHORT)simple_round(src->y);
408             dst_ptr[2] = (SHORT)simple_round(src->z);
409             dst_ptr[3] = (SHORT)simple_round(src->w);
410             break;
411         }
412         case D3DDECLTYPE_UBYTE4N:
413         {
414             dst[0] = (BYTE)simple_round(scale_clamp_ubyten(src->x));
415             dst[1] = (BYTE)simple_round(scale_clamp_ubyten(src->y));
416             dst[2] = (BYTE)simple_round(scale_clamp_ubyten(src->z));
417             dst[3] = (BYTE)simple_round(scale_clamp_ubyten(src->w));
418             break;
419         }
420         case D3DDECLTYPE_SHORT2N:
421         {
422             SHORT *dst_ptr = (SHORT*)dst;
423             dst_ptr[0] = (SHORT)simple_round(scale_clamp_shortn(src->x));
424             dst_ptr[1] = (SHORT)simple_round(scale_clamp_shortn(src->y));
425             break;
426         }
427         case D3DDECLTYPE_SHORT4N:
428         {
429             SHORT *dst_ptr = (SHORT*)dst;
430             dst_ptr[0] = (SHORT)simple_round(scale_clamp_shortn(src->x));
431             dst_ptr[1] = (SHORT)simple_round(scale_clamp_shortn(src->y));
432             dst_ptr[2] = (SHORT)simple_round(scale_clamp_shortn(src->z));
433             dst_ptr[3] = (SHORT)simple_round(scale_clamp_shortn(src->w));
434             break;
435         }
436         case D3DDECLTYPE_USHORT2N:
437         {
438             USHORT *dst_ptr = (USHORT*)dst;
439             dst_ptr[0] = (USHORT)simple_round(scale_clamp_ushortn(src->x));
440             dst_ptr[1] = (USHORT)simple_round(scale_clamp_ushortn(src->y));
441             break;
442         }
443         case D3DDECLTYPE_USHORT4N:
444         {
445             USHORT *dst_ptr = (USHORT*)dst;
446             dst_ptr[0] = (USHORT)simple_round(scale_clamp_ushortn(src->x));
447             dst_ptr[1] = (USHORT)simple_round(scale_clamp_ushortn(src->y));
448             dst_ptr[2] = (USHORT)simple_round(scale_clamp_ushortn(src->z));
449             dst_ptr[3] = (USHORT)simple_round(scale_clamp_ushortn(src->w));
450             break;
451         }
452         case D3DDECLTYPE_FLOAT16_2:
453         {
454             D3DXFloat32To16Array((D3DXFLOAT16*)dst, (FLOAT*)src, 2);
455             break;
456         }
457         case D3DDECLTYPE_FLOAT16_4:
458         {
459             D3DXFloat32To16Array((D3DXFLOAT16*)dst, (FLOAT*)src, 4);
460             break;
461         }
462         default:
463             if (!fixme_once++)
464                 FIXME("Conversion from D3DDECLTYPE_FLOAT4 to %d not implemented.\n", type_dst);
465             break;
466     }
467 }
468 
469 static void convert_component(BYTE *dst, BYTE *src, D3DDECLTYPE type_dst, D3DDECLTYPE type_src)
470 {
471     BOOL fixme_once = FALSE;
472 
473     switch (type_src)
474     {
475         case D3DDECLTYPE_FLOAT1:
476         {
477             FLOAT *src_ptr = (FLOAT*)src;
478             D3DXVECTOR4 src_float4 = {*src_ptr, 0.0f, 0.0f, 1.0f};
479             convert_float4(dst, &src_float4, type_dst);
480             break;
481         }
482         case D3DDECLTYPE_FLOAT2:
483         {
484             D3DXVECTOR2 *src_ptr = (D3DXVECTOR2*)src;
485             D3DXVECTOR4 src_float4 = {src_ptr->x, src_ptr->y, 0.0f, 1.0f};
486             convert_float4(dst, &src_float4, type_dst);
487             break;
488         }
489         case D3DDECLTYPE_FLOAT3:
490         {
491             D3DXVECTOR3 *src_ptr = (D3DXVECTOR3*)src;
492             D3DXVECTOR4 src_float4 = {src_ptr->x, src_ptr->y, src_ptr->z, 1.0f};
493             convert_float4(dst, &src_float4, type_dst);
494             break;
495         }
496         case D3DDECLTYPE_FLOAT4:
497         {
498             D3DXVECTOR4 *src_ptr = (D3DXVECTOR4*)src;
499             D3DXVECTOR4 src_float4 = {src_ptr->x, src_ptr->y, src_ptr->z, src_ptr->w};
500             convert_float4(dst, &src_float4, type_dst);
501             break;
502         }
503         case D3DDECLTYPE_D3DCOLOR:
504         {
505             D3DXVECTOR4 src_float4 =
506             {
507                 (FLOAT)src[2]/UCHAR_MAX,
508                 (FLOAT)src[1]/UCHAR_MAX,
509                 (FLOAT)src[0]/UCHAR_MAX,
510                 (FLOAT)src[3]/UCHAR_MAX
511             };
512             convert_float4(dst, &src_float4, type_dst);
513             break;
514         }
515         case D3DDECLTYPE_UBYTE4:
516         {
517             D3DXVECTOR4 src_float4 = {src[0], src[1], src[2], src[3]};
518             convert_float4(dst, &src_float4, type_dst);
519             break;
520         }
521         case D3DDECLTYPE_SHORT2:
522         {
523             SHORT *src_ptr = (SHORT*)src;
524             D3DXVECTOR4 src_float4 = {src_ptr[0], src_ptr[1], 0.0f, 1.0f};
525             convert_float4(dst, &src_float4, type_dst);
526             break;
527         }
528         case D3DDECLTYPE_SHORT4:
529         {
530             SHORT *src_ptr = (SHORT*)src;
531             D3DXVECTOR4 src_float4 = {src_ptr[0], src_ptr[1], src_ptr[2], src_ptr[3]};
532             convert_float4(dst, &src_float4, type_dst);
533             break;
534         }
535         case D3DDECLTYPE_UBYTE4N:
536         {
537             D3DXVECTOR4 src_float4 =
538             {
539                 (FLOAT)src[0]/UCHAR_MAX,
540                 (FLOAT)src[1]/UCHAR_MAX,
541                 (FLOAT)src[2]/UCHAR_MAX,
542                 (FLOAT)src[3]/UCHAR_MAX
543             };
544             convert_float4(dst, &src_float4, type_dst);
545             break;
546         }
547         case D3DDECLTYPE_SHORT2N:
548         {
549             SHORT *src_ptr = (SHORT*)src;
550             D3DXVECTOR4 src_float4 = {(FLOAT)src_ptr[0]/SHRT_MAX, (FLOAT)src_ptr[1]/SHRT_MAX, 0.0f, 1.0f};
551             convert_float4(dst, &src_float4, type_dst);
552             break;
553         }
554         case D3DDECLTYPE_SHORT4N:
555         {
556             SHORT *src_ptr = (SHORT*)src;
557             D3DXVECTOR4 src_float4 =
558             {
559                 (FLOAT)src_ptr[0]/SHRT_MAX,
560                 (FLOAT)src_ptr[1]/SHRT_MAX,
561                 (FLOAT)src_ptr[2]/SHRT_MAX,
562                 (FLOAT)src_ptr[3]/SHRT_MAX
563             };
564             convert_float4(dst, &src_float4, type_dst);
565             break;
566         }
567         case D3DDECLTYPE_FLOAT16_2:
568         {
569             D3DXVECTOR4 src_float4 = {0.0f, 0.0f, 0.0f, 1.0f};
570             D3DXFloat16To32Array((FLOAT*)&src_float4, (D3DXFLOAT16*)src, 2);
571             convert_float4(dst, &src_float4, type_dst);
572             break;
573         }
574         case D3DDECLTYPE_FLOAT16_4:
575         {
576             D3DXVECTOR4 src_float4;
577             D3DXFloat16To32Array((FLOAT*)&src_float4, (D3DXFLOAT16*)src, 4);
578             convert_float4(dst, &src_float4, type_dst);
579             break;
580         }
581         default:
582             if (!fixme_once++)
583                 FIXME("Conversion of D3DDECLTYPE %d to %d not implemented.\n", type_src, type_dst);
584             break;
585     }
586 }
587 
588 static INT get_equivalent_declaration_index(D3DVERTEXELEMENT9 orig_declaration, D3DVERTEXELEMENT9 *declaration)
589 {
590     INT i;
591 
592     for (i = 0; declaration[i].Stream != 0xff; i++)
593     {
594         if (orig_declaration.Usage == declaration[i].Usage
595             && orig_declaration.UsageIndex == declaration[i].UsageIndex)
596         {
597             return i;
598         }
599     }
600 
601     return -1;
602 }
603 
604 static HRESULT convert_vertex_buffer(ID3DXMesh *mesh_dst, ID3DXMesh *mesh_src)
605 {
606     HRESULT hr;
607     D3DVERTEXELEMENT9 orig_declaration[MAX_FVF_DECL_SIZE] = {D3DDECL_END()};
608     D3DVERTEXELEMENT9 declaration[MAX_FVF_DECL_SIZE] = {D3DDECL_END()};
609     BYTE *vb_dst = NULL;
610     BYTE *vb_src = NULL;
611     UINT i;
612     UINT num_vertices = mesh_src->lpVtbl->GetNumVertices(mesh_src);
613     UINT dst_vertex_size = mesh_dst->lpVtbl->GetNumBytesPerVertex(mesh_dst);
614     UINT src_vertex_size = mesh_src->lpVtbl->GetNumBytesPerVertex(mesh_src);
615 
616     hr = mesh_src->lpVtbl->GetDeclaration(mesh_src, orig_declaration);
617     if (FAILED(hr)) return hr;
618     hr = mesh_dst->lpVtbl->GetDeclaration(mesh_dst, declaration);
619     if (FAILED(hr)) return hr;
620 
621     hr = mesh_src->lpVtbl->LockVertexBuffer(mesh_src, D3DLOCK_READONLY, (void**)&vb_src);
622     if (FAILED(hr)) goto cleanup;
623     hr = mesh_dst->lpVtbl->LockVertexBuffer(mesh_dst, 0, (void**)&vb_dst);
624     if (FAILED(hr)) goto cleanup;
625 
626     /* Clear all new fields by clearing the entire vertex buffer. */
627     memset(vb_dst, 0, num_vertices * dst_vertex_size);
628 
629     for (i = 0; orig_declaration[i].Stream != 0xff; i++)
630     {
631         INT eq_idx = get_equivalent_declaration_index(orig_declaration[i], declaration);
632 
633         if (eq_idx >= 0)
634         {
635             UINT j;
636             for (j = 0; j < num_vertices; j++)
637             {
638                 UINT idx_dst = dst_vertex_size * j + declaration[eq_idx].Offset;
639                 UINT idx_src = src_vertex_size * j + orig_declaration[i].Offset;
640                 UINT type_size = d3dx_decltype_size[orig_declaration[i].Type];
641 
642                 if (orig_declaration[i].Type == declaration[eq_idx].Type)
643                     memcpy(&vb_dst[idx_dst], &vb_src[idx_src], type_size);
644                 else
645                    convert_component(&vb_dst[idx_dst], &vb_src[idx_src], declaration[eq_idx].Type, orig_declaration[i].Type);
646             }
647         }
648     }
649 
650     hr = D3D_OK;
651 cleanup:
652     if (vb_dst) mesh_dst->lpVtbl->UnlockVertexBuffer(mesh_dst);
653     if (vb_src) mesh_src->lpVtbl->UnlockVertexBuffer(mesh_src);
654 
655     return hr;
656 }
657 
658 static BOOL declaration_equals(const D3DVERTEXELEMENT9 *declaration1, const D3DVERTEXELEMENT9 *declaration2)
659 {
660     UINT size1 = 0, size2 = 0;
661 
662     /* Find the size of each declaration */
663     while (declaration1[size1].Stream != 0xff) size1++;
664     while (declaration2[size2].Stream != 0xff) size2++;
665 
666     /* If not same size then they are definitely not equal */
667     if (size1 != size2)
668         return FALSE;
669 
670     /* Check that all components are the same */
671     if (memcmp(declaration1, declaration2, size1*sizeof(*declaration1)) == 0)
672         return TRUE;
673 
674     return FALSE;
675 }
676 
677 static HRESULT WINAPI d3dx9_mesh_CloneMesh(struct ID3DXMesh *iface, DWORD options,
678         const D3DVERTEXELEMENT9 *declaration, struct IDirect3DDevice9 *device, struct ID3DXMesh **clone_mesh_out)
679 {
680     struct d3dx9_mesh *This = impl_from_ID3DXMesh(iface);
681     struct d3dx9_mesh *cloned_this;
682     ID3DXMesh *clone_mesh;
683     D3DVERTEXELEMENT9 orig_declaration[MAX_FVF_DECL_SIZE] = { D3DDECL_END() };
684     void *data_in, *data_out;
685     DWORD vertex_size;
686     HRESULT hr;
687     BOOL same_declaration;
688 
689     TRACE("iface %p, options %#x, declaration %p, device %p, clone_mesh_out %p.\n",
690             iface, options, declaration, device, clone_mesh_out);
691 
692     if (!clone_mesh_out)
693         return D3DERR_INVALIDCALL;
694 
695     hr = iface->lpVtbl->GetDeclaration(iface, orig_declaration);
696     if (FAILED(hr)) return hr;
697 
698     hr = D3DXCreateMesh(This->numfaces, This->numvertices, options & ~D3DXMESH_VB_SHARE,
699                         declaration, device, &clone_mesh);
700     if (FAILED(hr)) return hr;
701 
702     cloned_this = impl_from_ID3DXMesh(clone_mesh);
703     vertex_size = clone_mesh->lpVtbl->GetNumBytesPerVertex(clone_mesh);
704     same_declaration = declaration_equals(declaration, orig_declaration);
705 
706     if (options & D3DXMESH_VB_SHARE) {
707         if (!same_declaration) {
708             hr = D3DERR_INVALIDCALL;
709             goto error;
710         }
711         IDirect3DVertexBuffer9_AddRef(This->vertex_buffer);
712         /* FIXME: refactor to avoid creating a new vertex buffer */
713         IDirect3DVertexBuffer9_Release(cloned_this->vertex_buffer);
714         cloned_this->vertex_buffer = This->vertex_buffer;
715     } else if (same_declaration) {
716         hr = iface->lpVtbl->LockVertexBuffer(iface, D3DLOCK_READONLY, &data_in);
717         if (FAILED(hr)) goto error;
718         hr = clone_mesh->lpVtbl->LockVertexBuffer(clone_mesh, 0, &data_out);
719         if (FAILED(hr)) {
720             iface->lpVtbl->UnlockVertexBuffer(iface);
721             goto error;
722         }
723         memcpy(data_out, data_in, This->numvertices * vertex_size);
724         clone_mesh->lpVtbl->UnlockVertexBuffer(clone_mesh);
725         iface->lpVtbl->UnlockVertexBuffer(iface);
726     } else {
727         hr = convert_vertex_buffer(clone_mesh, iface);
728         if (FAILED(hr)) goto error;
729     }
730 
731     hr = iface->lpVtbl->LockIndexBuffer(iface, D3DLOCK_READONLY, &data_in);
732     if (FAILED(hr)) goto error;
733     hr = clone_mesh->lpVtbl->LockIndexBuffer(clone_mesh, 0, &data_out);
734     if (FAILED(hr)) {
735         iface->lpVtbl->UnlockIndexBuffer(iface);
736         goto error;
737     }
738     if ((options ^ This->options) & D3DXMESH_32BIT) {
739         DWORD i;
740         if (options & D3DXMESH_32BIT) {
741             for (i = 0; i < This->numfaces * 3; i++)
742                 ((DWORD*)data_out)[i] = ((WORD*)data_in)[i];
743         } else {
744             for (i = 0; i < This->numfaces * 3; i++)
745                 ((WORD*)data_out)[i] = ((DWORD*)data_in)[i];
746         }
747     } else {
748         memcpy(data_out, data_in, This->numfaces * 3 * (options & D3DXMESH_32BIT ? 4 : 2));
749     }
750     clone_mesh->lpVtbl->UnlockIndexBuffer(clone_mesh);
751     iface->lpVtbl->UnlockIndexBuffer(iface);
752 
753     memcpy(cloned_this->attrib_buffer, This->attrib_buffer, This->numfaces * sizeof(*This->attrib_buffer));
754 
755     if (This->attrib_table_size)
756     {
757         cloned_this->attrib_table_size = This->attrib_table_size;
758         cloned_this->attrib_table = HeapAlloc(GetProcessHeap(), 0, This->attrib_table_size * sizeof(*This->attrib_table));
759         if (!cloned_this->attrib_table) {
760             hr = E_OUTOFMEMORY;
761             goto error;
762         }
763         memcpy(cloned_this->attrib_table, This->attrib_table, This->attrib_table_size * sizeof(*This->attrib_table));
764     }
765 
766     *clone_mesh_out = clone_mesh;
767 
768     return D3D_OK;
769 error:
770     IUnknown_Release(clone_mesh);
771     return hr;
772 }
773 
774 static HRESULT WINAPI d3dx9_mesh_GetVertexBuffer(struct ID3DXMesh *iface,
775         struct IDirect3DVertexBuffer9 **vertex_buffer)
776 {
777     struct d3dx9_mesh *mesh = impl_from_ID3DXMesh(iface);
778 
779     TRACE("iface %p, vertex_buffer %p.\n", iface, vertex_buffer);
780 
781     if (!vertex_buffer)
782         return D3DERR_INVALIDCALL;
783     *vertex_buffer = mesh->vertex_buffer;
784     IDirect3DVertexBuffer9_AddRef(mesh->vertex_buffer);
785 
786     return D3D_OK;
787 }
788 
789 static HRESULT WINAPI d3dx9_mesh_GetIndexBuffer(struct ID3DXMesh *iface,
790         struct IDirect3DIndexBuffer9 **index_buffer)
791 {
792     struct d3dx9_mesh *mesh = impl_from_ID3DXMesh(iface);
793 
794     TRACE("iface %p, index_buffer %p.\n", iface, index_buffer);
795 
796     if (!index_buffer)
797         return D3DERR_INVALIDCALL;
798     *index_buffer = mesh->index_buffer;
799     IDirect3DIndexBuffer9_AddRef(mesh->index_buffer);
800 
801     return D3D_OK;
802 }
803 
804 static HRESULT WINAPI d3dx9_mesh_LockVertexBuffer(ID3DXMesh *iface, DWORD flags, void **data)
805 {
806     struct d3dx9_mesh *mesh = impl_from_ID3DXMesh(iface);
807 
808     TRACE("iface %p, flags %#x, data %p.\n", iface, flags, data);
809 
810     return IDirect3DVertexBuffer9_Lock(mesh->vertex_buffer, 0, 0, data, flags);
811 }
812 
813 static HRESULT WINAPI d3dx9_mesh_UnlockVertexBuffer(ID3DXMesh *iface)
814 {
815     struct d3dx9_mesh *mesh = impl_from_ID3DXMesh(iface);
816 
817     TRACE("iface %p.\n", iface);
818 
819     return IDirect3DVertexBuffer9_Unlock(mesh->vertex_buffer);
820 }
821 
822 static HRESULT WINAPI d3dx9_mesh_LockIndexBuffer(ID3DXMesh *iface, DWORD flags, void **data)
823 {
824     struct d3dx9_mesh *mesh = impl_from_ID3DXMesh(iface);
825 
826     TRACE("iface %p, flags %#x, data %p.\n", iface, flags, data);
827 
828     return IDirect3DIndexBuffer9_Lock(mesh->index_buffer, 0, 0, data, flags);
829 }
830 
831 static HRESULT WINAPI d3dx9_mesh_UnlockIndexBuffer(ID3DXMesh *iface)
832 {
833     struct d3dx9_mesh *mesh = impl_from_ID3DXMesh(iface);
834 
835     TRACE("iface %p.\n", iface);
836 
837     return IDirect3DIndexBuffer9_Unlock(mesh->index_buffer);
838 }
839 
840 /* FIXME: This looks just wrong, we never check *attrib_table_size before
841  * copying the data. */
842 static HRESULT WINAPI d3dx9_mesh_GetAttributeTable(ID3DXMesh *iface,
843         D3DXATTRIBUTERANGE *attrib_table, DWORD *attrib_table_size)
844 {
845     struct d3dx9_mesh *mesh = impl_from_ID3DXMesh(iface);
846 
847     TRACE("iface %p, attrib_table %p, attrib_table_size %p.\n",
848             iface, attrib_table, attrib_table_size);
849 
850     if (attrib_table_size)
851         *attrib_table_size = mesh->attrib_table_size;
852 
853     if (attrib_table)
854         memcpy(attrib_table, mesh->attrib_table, mesh->attrib_table_size * sizeof(*attrib_table));
855 
856     return D3D_OK;
857 }
858 
859 struct edge_face
860 {
861     struct list entry;
862     DWORD v2;
863     DWORD face;
864 };
865 
866 struct edge_face_map
867 {
868     struct list *lists;
869     struct edge_face *entries;
870 };
871 
872 /* Builds up a map of which face a new edge belongs to. That way the adjacency
873  * of another edge can be looked up. An edge has an adjacent face if there
874  * is an edge going in the opposite direction in the map. For example if the
875  * edge (v1, v2) belongs to face 4, and there is a mapping (v2, v1)->7, then
876  * face 4 and 7 are adjacent.
877  *
878  * Each edge might have been replaced with another edge, or none at all. There
879  * is at most one edge to face mapping, i.e. an edge can only belong to one
880  * face.
881  */
882 static HRESULT init_edge_face_map(struct edge_face_map *edge_face_map, const DWORD *index_buffer,
883         const DWORD *point_reps, DWORD num_faces)
884 {
885     DWORD face, edge;
886     DWORD i;
887 
888     edge_face_map->lists = HeapAlloc(GetProcessHeap(), 0, 3 * num_faces * sizeof(*edge_face_map->lists));
889     if (!edge_face_map->lists) return E_OUTOFMEMORY;
890 
891     edge_face_map->entries = HeapAlloc(GetProcessHeap(), 0, 3 * num_faces * sizeof(*edge_face_map->entries));
892     if (!edge_face_map->entries) return E_OUTOFMEMORY;
893 
894 
895     /* Initialize all lists */
896     for (i = 0; i < 3 * num_faces; i++)
897     {
898         list_init(&edge_face_map->lists[i]);
899     }
900     /* Build edge face mapping */
901     for (face = 0; face < num_faces; face++)
902     {
903         for (edge = 0; edge < 3; edge++)
904         {
905             DWORD v1 = index_buffer[3*face + edge];
906             DWORD v2 = index_buffer[3*face + (edge+1)%3];
907             DWORD new_v1 = point_reps[v1]; /* What v1 has been replaced with */
908             DWORD new_v2 = point_reps[v2];
909 
910             if (v1 != v2) /* Only map non-collapsed edges */
911             {
912                 i = 3*face + edge;
913                 edge_face_map->entries[i].v2 = new_v2;
914                 edge_face_map->entries[i].face = face;
915                 list_add_head(&edge_face_map->lists[new_v1], &edge_face_map->entries[i].entry);
916             }
917         }
918     }
919 
920     return D3D_OK;
921 }
922 
923 static DWORD find_adjacent_face(struct edge_face_map *edge_face_map, DWORD vertex1, DWORD vertex2, DWORD num_faces)
924 {
925     struct edge_face *edge_face_ptr;
926 
927     LIST_FOR_EACH_ENTRY(edge_face_ptr, &edge_face_map->lists[vertex2], struct edge_face, entry)
928     {
929         if (edge_face_ptr->v2 == vertex1)
930             return edge_face_ptr->face;
931     }
932 
933     return -1;
934 }
935 
936 static DWORD *generate_identity_point_reps(DWORD num_vertices)
937 {
938         DWORD *id_point_reps;
939         DWORD i;
940 
941         id_point_reps = HeapAlloc(GetProcessHeap(), 0, num_vertices * sizeof(*id_point_reps));
942         if (!id_point_reps)
943             return NULL;
944 
945         for (i = 0; i < num_vertices; i++)
946         {
947             id_point_reps[i] = i;
948         }
949 
950         return id_point_reps;
951 }
952 
953 static HRESULT WINAPI d3dx9_mesh_ConvertPointRepsToAdjacency(ID3DXMesh *iface,
954         const DWORD *point_reps, DWORD *adjacency)
955 {
956     HRESULT hr;
957     DWORD num_faces = iface->lpVtbl->GetNumFaces(iface);
958     DWORD num_vertices = iface->lpVtbl->GetNumVertices(iface);
959     DWORD options = iface->lpVtbl->GetOptions(iface);
960     BOOL indices_are_16_bit = !(options & D3DXMESH_32BIT);
961     DWORD *ib = NULL;
962     void *ib_ptr = NULL;
963     DWORD face;
964     DWORD edge;
965     struct edge_face_map edge_face_map = {0};
966     const DWORD *point_reps_ptr = NULL;
967     DWORD *id_point_reps = NULL;
968 
969     TRACE("iface %p, point_reps %p, adjacency %p.\n", iface, point_reps, adjacency);
970 
971     if (!adjacency) return D3DERR_INVALIDCALL;
972 
973     if (!point_reps) /* Identity point reps */
974     {
975         id_point_reps = generate_identity_point_reps(num_vertices);
976         if (!id_point_reps)
977         {
978             hr = E_OUTOFMEMORY;
979             goto cleanup;
980         }
981 
982         point_reps_ptr = id_point_reps;
983     }
984     else
985     {
986         point_reps_ptr = point_reps;
987     }
988 
989     hr = iface->lpVtbl->LockIndexBuffer(iface, D3DLOCK_READONLY, &ib_ptr);
990     if (FAILED(hr)) goto cleanup;
991 
992     if (indices_are_16_bit)
993     {
994         /* Widen 16 bit to 32 bit */
995         DWORD i;
996         WORD *ib_16bit = ib_ptr;
997         ib = HeapAlloc(GetProcessHeap(), 0, 3 * num_faces * sizeof(DWORD));
998         if (!ib)
999         {
1000             hr = E_OUTOFMEMORY;
1001             goto cleanup;
1002         }
1003         for (i = 0; i < 3 * num_faces; i++)
1004         {
1005             ib[i] = ib_16bit[i];
1006         }
1007     }
1008     else
1009     {
1010         ib = ib_ptr;
1011     }
1012 
1013     hr = init_edge_face_map(&edge_face_map, ib, point_reps_ptr, num_faces);
1014     if (FAILED(hr)) goto cleanup;
1015 
1016     /* Create adjacency */
1017     for (face = 0; face < num_faces; face++)
1018     {
1019         for (edge = 0; edge < 3; edge++)
1020         {
1021             DWORD v1 = ib[3*face + edge];
1022             DWORD v2 = ib[3*face + (edge+1)%3];
1023             DWORD new_v1 = point_reps_ptr[v1];
1024             DWORD new_v2 = point_reps_ptr[v2];
1025             DWORD adj_face;
1026 
1027             adj_face = find_adjacent_face(&edge_face_map, new_v1, new_v2, num_faces);
1028             adjacency[3*face + edge] = adj_face;
1029         }
1030     }
1031 
1032     hr = D3D_OK;
1033 cleanup:
1034     HeapFree(GetProcessHeap(), 0, id_point_reps);
1035     if (indices_are_16_bit) HeapFree(GetProcessHeap(), 0, ib);
1036     HeapFree(GetProcessHeap(), 0, edge_face_map.lists);
1037     HeapFree(GetProcessHeap(), 0, edge_face_map.entries);
1038     if(ib_ptr) iface->lpVtbl->UnlockIndexBuffer(iface);
1039     return hr;
1040 }
1041 
1042 /* ConvertAdjacencyToPointReps helper function.
1043  *
1044  * Goes around the edges of each face and replaces the vertices in any adjacent
1045  * face's edge with its own vertices(if its vertices have a lower index). This
1046  * way as few as possible low index vertices are shared among the faces. The
1047  * re-ordered index buffer is stored in new_indices.
1048  *
1049  * The vertices in a point representation must be ordered sequentially, e.g.
1050  * index 5 holds the index of the vertex that replaces vertex 5, i.e. if
1051  * vertex 5 is replaced by vertex 3 then index 5 would contain 3. If no vertex
1052  * replaces it, then it contains the same number as the index itself, e.g.
1053  * index 5 would contain 5. */
1054 static HRESULT propagate_face_vertices(const DWORD *adjacency, DWORD *point_reps,
1055         const DWORD *indices, DWORD *new_indices, DWORD face, DWORD numfaces)
1056 {
1057     const unsigned int VERTS_PER_FACE = 3;
1058     DWORD edge, opp_edge;
1059     DWORD face_base = VERTS_PER_FACE * face;
1060 
1061     for (edge = 0; edge < VERTS_PER_FACE; edge++)
1062     {
1063         DWORD adj_face = adjacency[face_base + edge];
1064         DWORD adj_face_base;
1065         DWORD i;
1066         if (adj_face == -1) /* No adjacent face. */
1067             continue;
1068         else if (adj_face >= numfaces)
1069         {
1070             /* This throws exception on Windows */
1071             WARN("Index out of bounds. Got %d expected less than %d.\n",
1072                 adj_face, numfaces);
1073             return D3DERR_INVALIDCALL;
1074         }
1075         adj_face_base = 3 * adj_face;
1076 
1077         /* Find opposite edge in adjacent face. */
1078         for (opp_edge = 0; opp_edge < VERTS_PER_FACE; opp_edge++)
1079         {
1080             DWORD opp_edge_index = adj_face_base + opp_edge;
1081             if (adjacency[opp_edge_index] == face)
1082                 break; /* Found opposite edge. */
1083         }
1084 
1085         /* Replaces vertices in opposite edge with vertices from current edge. */
1086         for (i = 0; i < 2; i++)
1087         {
1088             DWORD from = face_base + (edge + (1 - i)) % VERTS_PER_FACE;
1089             DWORD to = adj_face_base + (opp_edge + i) % VERTS_PER_FACE;
1090 
1091             /* Propagate lowest index. */
1092             if (new_indices[to] > new_indices[from])
1093             {
1094                 new_indices[to] = new_indices[from];
1095                 point_reps[indices[to]] = new_indices[from];
1096             }
1097         }
1098     }
1099 
1100     return D3D_OK;
1101 }
1102 
1103 static HRESULT WINAPI d3dx9_mesh_ConvertAdjacencyToPointReps(ID3DXMesh *iface,
1104         const DWORD *adjacency, DWORD *point_reps)
1105 {
1106     struct d3dx9_mesh *This = impl_from_ID3DXMesh(iface);
1107     HRESULT hr;
1108     DWORD face;
1109     DWORD i;
1110     DWORD *indices = NULL;
1111     WORD *indices_16bit = NULL;
1112     DWORD *new_indices = NULL;
1113     const unsigned int VERTS_PER_FACE = 3;
1114 
1115     TRACE("iface %p, adjacency %p, point_reps %p.\n", iface, adjacency, point_reps);
1116 
1117     if (!adjacency)
1118     {
1119         WARN("NULL adjacency.\n");
1120         hr = D3DERR_INVALIDCALL;
1121         goto cleanup;
1122     }
1123 
1124     if (!point_reps)
1125     {
1126         WARN("NULL point_reps.\n");
1127         hr = D3DERR_INVALIDCALL;
1128         goto cleanup;
1129     }
1130 
1131     /* Should never happen as CreateMesh does not allow meshes with 0 faces */
1132     if (This->numfaces == 0)
1133     {
1134         ERR("Number of faces was zero.\n");
1135         hr = D3DERR_INVALIDCALL;
1136         goto cleanup;
1137     }
1138 
1139     new_indices = HeapAlloc(GetProcessHeap(), 0, VERTS_PER_FACE * This->numfaces * sizeof(*indices));
1140     if (!new_indices)
1141     {
1142         hr = E_OUTOFMEMORY;
1143         goto cleanup;
1144     }
1145 
1146     if (This->options & D3DXMESH_32BIT)
1147     {
1148         hr = iface->lpVtbl->LockIndexBuffer(iface, D3DLOCK_READONLY, (void**)&indices);
1149         if (FAILED(hr)) goto cleanup;
1150         memcpy(new_indices, indices, VERTS_PER_FACE * This->numfaces * sizeof(*indices));
1151     }
1152     else
1153     {
1154         /* Make a widening copy of indices_16bit into indices and new_indices
1155          * in order to re-use the helper function */
1156         hr = iface->lpVtbl->LockIndexBuffer(iface, D3DLOCK_READONLY, (void**)&indices_16bit);
1157         if (FAILED(hr)) goto cleanup;
1158         indices = HeapAlloc(GetProcessHeap(), 0, VERTS_PER_FACE * This->numfaces * sizeof(*indices));
1159         if (!indices)
1160         {
1161             hr = E_OUTOFMEMORY;
1162             goto cleanup;
1163         }
1164         for (i = 0; i < VERTS_PER_FACE * This->numfaces; i++)
1165         {
1166             new_indices[i] = indices_16bit[i];
1167             indices[i] = indices_16bit[i];
1168         }
1169     }
1170 
1171     /* Vertices are ordered sequentially in the point representation. */
1172     for (i = 0; i < This->numvertices; i++)
1173     {
1174         point_reps[i] = i;
1175     }
1176 
1177     /* Propagate vertices with low indices so as few vertices as possible
1178      * are used in the mesh.
1179      */
1180     for (face = 0; face < This->numfaces; face++)
1181     {
1182         hr = propagate_face_vertices(adjacency, point_reps, indices, new_indices, face, This->numfaces);
1183         if (FAILED(hr)) goto cleanup;
1184     }
1185     /* Go in opposite direction to catch all face orderings */
1186     for (face = 0; face < This->numfaces; face++)
1187     {
1188         hr = propagate_face_vertices(adjacency, point_reps,
1189                                      indices, new_indices,
1190                                      (This->numfaces - 1) - face, This->numfaces);
1191         if (FAILED(hr)) goto cleanup;
1192     }
1193 
1194     hr = D3D_OK;
1195 cleanup:
1196     if (This->options & D3DXMESH_32BIT)
1197     {
1198         if (indices) iface->lpVtbl->UnlockIndexBuffer(iface);
1199     }
1200     else
1201     {
1202         if (indices_16bit) iface->lpVtbl->UnlockIndexBuffer(iface);
1203         HeapFree(GetProcessHeap(), 0, indices);
1204     }
1205     HeapFree(GetProcessHeap(), 0, new_indices);
1206     return hr;
1207 }
1208 
1209 struct vertex_metadata {
1210   float key;
1211   DWORD vertex_index;
1212   DWORD first_shared_index;
1213 };
1214 
1215 static int __cdecl compare_vertex_keys(const void *a, const void *b)
1216 {
1217     const struct vertex_metadata *left = a;
1218     const struct vertex_metadata *right = b;
1219     if (left->key == right->key)
1220         return 0;
1221     return left->key < right->key ? -1 : 1;
1222 }
1223 
1224 static HRESULT WINAPI d3dx9_mesh_GenerateAdjacency(ID3DXMesh *iface, float epsilon, DWORD *adjacency)
1225 {
1226     struct d3dx9_mesh *This = impl_from_ID3DXMesh(iface);
1227     HRESULT hr;
1228     BYTE *vertices = NULL;
1229     const DWORD *indices = NULL;
1230     DWORD vertex_size;
1231     DWORD buffer_size;
1232     /* sort the vertices by (x + y + z) to quickly find coincident vertices */
1233     struct vertex_metadata *sorted_vertices;
1234     /* shared_indices links together identical indices in the index buffer so
1235      * that adjacency checks can be limited to faces sharing a vertex */
1236     DWORD *shared_indices = NULL;
1237     const FLOAT epsilon_sq = epsilon * epsilon;
1238     DWORD i;
1239 
1240     TRACE("iface %p, epsilon %.8e, adjacency %p.\n", iface, epsilon, adjacency);
1241 
1242     if (!adjacency)
1243         return D3DERR_INVALIDCALL;
1244 
1245     buffer_size = This->numfaces * 3 * sizeof(*shared_indices) + This->numvertices * sizeof(*sorted_vertices);
1246     if (!(This->options & D3DXMESH_32BIT))
1247         buffer_size += This->numfaces * 3 * sizeof(*indices);
1248     shared_indices = HeapAlloc(GetProcessHeap(), 0, buffer_size);
1249     if (!shared_indices)
1250         return E_OUTOFMEMORY;
1251     sorted_vertices = (struct vertex_metadata*)(shared_indices + This->numfaces * 3);
1252 
1253     hr = iface->lpVtbl->LockVertexBuffer(iface, D3DLOCK_READONLY, (void**)&vertices);
1254     if (FAILED(hr)) goto cleanup;
1255     hr = iface->lpVtbl->LockIndexBuffer(iface, D3DLOCK_READONLY, (void**)&indices);
1256     if (FAILED(hr)) goto cleanup;
1257 
1258     if (!(This->options & D3DXMESH_32BIT)) {
1259         const WORD *word_indices = (const WORD*)indices;
1260         DWORD *dword_indices = (DWORD*)(sorted_vertices + This->numvertices);
1261         indices = dword_indices;
1262         for (i = 0; i < This->numfaces * 3; i++)
1263             *dword_indices++ = *word_indices++;
1264     }
1265 
1266     vertex_size = iface->lpVtbl->GetNumBytesPerVertex(iface);
1267     for (i = 0; i < This->numvertices; i++) {
1268         D3DXVECTOR3 *vertex = (D3DXVECTOR3*)(vertices + vertex_size * i);
1269         sorted_vertices[i].first_shared_index = -1;
1270         sorted_vertices[i].key = vertex->x + vertex->y + vertex->z;
1271         sorted_vertices[i].vertex_index = i;
1272     }
1273     for (i = 0; i < This->numfaces * 3; i++) {
1274         DWORD *first_shared_index = &sorted_vertices[indices[i]].first_shared_index;
1275         shared_indices[i] = *first_shared_index;
1276         *first_shared_index = i;
1277         adjacency[i] = -1;
1278     }
1279     qsort(sorted_vertices, This->numvertices, sizeof(*sorted_vertices), compare_vertex_keys);
1280 
1281     for (i = 0; i < This->numvertices; i++) {
1282         struct vertex_metadata *sorted_vertex_a = &sorted_vertices[i];
1283         D3DXVECTOR3 *vertex_a = (D3DXVECTOR3*)(vertices + sorted_vertex_a->vertex_index * vertex_size);
1284         DWORD shared_index_a = sorted_vertex_a->first_shared_index;
1285 
1286         while (shared_index_a != -1) {
1287             DWORD j = i;
1288             DWORD shared_index_b = shared_indices[shared_index_a];
1289             struct vertex_metadata *sorted_vertex_b = sorted_vertex_a;
1290 
1291             while (TRUE) {
1292                 while (shared_index_b != -1) {
1293                     /* faces are adjacent if they have another coincident vertex */
1294                     DWORD base_a = (shared_index_a / 3) * 3;
1295                     DWORD base_b = (shared_index_b / 3) * 3;
1296                     BOOL adjacent = FALSE;
1297                     int k;
1298 
1299                     for (k = 0; k < 3; k++) {
1300                         if (adjacency[base_b + k] == shared_index_a / 3) {
1301                             adjacent = TRUE;
1302                             break;
1303                         }
1304                     }
1305                     if (!adjacent) {
1306                         for (k = 1; k <= 2; k++) {
1307                             DWORD vertex_index_a = base_a + (shared_index_a + k) % 3;
1308                             DWORD vertex_index_b = base_b + (shared_index_b + (3 - k)) % 3;
1309                             adjacent = indices[vertex_index_a] == indices[vertex_index_b];
1310                             if (!adjacent && epsilon >= 0.0f) {
1311                                 D3DXVECTOR3 delta = {0.0f, 0.0f, 0.0f};
1312                                 FLOAT length_sq;
1313 
1314                                 D3DXVec3Subtract(&delta,
1315                                                  (D3DXVECTOR3*)(vertices + indices[vertex_index_a] * vertex_size),
1316                                                  (D3DXVECTOR3*)(vertices + indices[vertex_index_b] * vertex_size));
1317                                 length_sq = D3DXVec3LengthSq(&delta);
1318                                 adjacent = epsilon == 0.0f ? length_sq == 0.0f : length_sq < epsilon_sq;
1319                             }
1320                             if (adjacent) {
1321                                 DWORD adj_a = base_a + 2 - (vertex_index_a + shared_index_a + 1) % 3;
1322                                 DWORD adj_b = base_b + 2 - (vertex_index_b + shared_index_b + 1) % 3;
1323                                 if (adjacency[adj_a] == -1 && adjacency[adj_b] == -1) {
1324                                     adjacency[adj_a] = base_b / 3;
1325                                     adjacency[adj_b] = base_a / 3;
1326                                     break;
1327                                 }
1328                             }
1329                         }
1330                     }
1331 
1332                     shared_index_b = shared_indices[shared_index_b];
1333                 }
1334                 while (++j < This->numvertices) {
1335                     D3DXVECTOR3 *vertex_b;
1336 
1337                     sorted_vertex_b++;
1338                     if (sorted_vertex_b->key - sorted_vertex_a->key > epsilon * 3.0f) {
1339                         /* no more coincident vertices to try */
1340                         j = This->numvertices;
1341                         break;
1342                     }
1343                     /* check for coincidence */
1344                     vertex_b = (D3DXVECTOR3*)(vertices + sorted_vertex_b->vertex_index * vertex_size);
1345                     if (fabsf(vertex_a->x - vertex_b->x) <= epsilon &&
1346                         fabsf(vertex_a->y - vertex_b->y) <= epsilon &&
1347                         fabsf(vertex_a->z - vertex_b->z) <= epsilon)
1348                     {
1349                         break;
1350                     }
1351                 }
1352                 if (j >= This->numvertices)
1353                     break;
1354                 shared_index_b = sorted_vertex_b->first_shared_index;
1355             }
1356 
1357             sorted_vertex_a->first_shared_index = shared_indices[sorted_vertex_a->first_shared_index];
1358             shared_index_a = sorted_vertex_a->first_shared_index;
1359         }
1360     }
1361 
1362     hr = D3D_OK;
1363 cleanup:
1364     if (indices) iface->lpVtbl->UnlockIndexBuffer(iface);
1365     if (vertices) iface->lpVtbl->UnlockVertexBuffer(iface);
1366     HeapFree(GetProcessHeap(), 0, shared_indices);
1367     return hr;
1368 }
1369 
1370 static HRESULT WINAPI d3dx9_mesh_UpdateSemantics(ID3DXMesh *iface, D3DVERTEXELEMENT9 declaration[MAX_FVF_DECL_SIZE])
1371 {
1372     struct d3dx9_mesh *This = impl_from_ID3DXMesh(iface);
1373     HRESULT hr;
1374     UINT vertex_declaration_size;
1375     int i;
1376 
1377     TRACE("iface %p, declaration %p.\n", iface, declaration);
1378 
1379     if (!declaration)
1380     {
1381         WARN("Invalid declaration. Can't use NULL declaration.\n");
1382         return D3DERR_INVALIDCALL;
1383     }
1384 
1385     /* New declaration must be same size as original */
1386     vertex_declaration_size = D3DXGetDeclVertexSize(declaration, declaration[0].Stream);
1387     if (vertex_declaration_size != This->vertex_declaration_size)
1388     {
1389         WARN("Invalid declaration. New vertex size does not match the original vertex size.\n");
1390         return D3DERR_INVALIDCALL;
1391     }
1392 
1393     /* New declaration must not contain non-zero Stream value  */
1394     for (i = 0; declaration[i].Stream != 0xff; i++)
1395     {
1396         if (declaration[i].Stream != 0)
1397         {
1398             WARN("Invalid declaration. New declaration contains non-zero Stream value.\n");
1399             return D3DERR_INVALIDCALL;
1400         }
1401     }
1402 
1403     This->num_elem = i + 1;
1404     copy_declaration(This->cached_declaration, declaration, This->num_elem);
1405 
1406     if (This->vertex_declaration)
1407         IDirect3DVertexDeclaration9_Release(This->vertex_declaration);
1408 
1409     /* An application can pass an invalid declaration to UpdateSemantics and
1410      * still expect D3D_OK (see tests). If the declaration is invalid, then
1411      * subsequent calls to DrawSubset will fail. This is handled by setting the
1412      * vertex declaration to NULL.
1413      *     GetDeclaration, GetNumBytesPerVertex must, however, use the new
1414      * invalid declaration. This is handled by them using the cached vertex
1415      * declaration instead of the actual vertex declaration.
1416      */
1417     hr = IDirect3DDevice9_CreateVertexDeclaration(This->device,
1418                                                   declaration,
1419                                                   &This->vertex_declaration);
1420     if (FAILED(hr))
1421     {
1422         WARN("Using invalid declaration. Calls to DrawSubset will fail.\n");
1423         This->vertex_declaration = NULL;
1424     }
1425 
1426     return D3D_OK;
1427 }
1428 
1429 static HRESULT WINAPI d3dx9_mesh_LockAttributeBuffer(ID3DXMesh *iface, DWORD flags, DWORD **data)
1430 {
1431     struct d3dx9_mesh *mesh = impl_from_ID3DXMesh(iface);
1432 
1433     TRACE("iface %p, flags %#x, data %p.\n", iface, flags, data);
1434 
1435     InterlockedIncrement(&mesh->attrib_buffer_lock_count);
1436 
1437     if (!(flags & D3DLOCK_READONLY))
1438     {
1439         D3DXATTRIBUTERANGE *attrib_table = mesh->attrib_table;
1440         mesh->attrib_table_size = 0;
1441         mesh->attrib_table = NULL;
1442         HeapFree(GetProcessHeap(), 0, attrib_table);
1443     }
1444 
1445     *data = mesh->attrib_buffer;
1446 
1447     return D3D_OK;
1448 }
1449 
1450 static HRESULT WINAPI d3dx9_mesh_UnlockAttributeBuffer(ID3DXMesh *iface)
1451 {
1452     struct d3dx9_mesh *mesh = impl_from_ID3DXMesh(iface);
1453     int lock_count;
1454 
1455     TRACE("iface %p.\n", iface);
1456 
1457     lock_count = InterlockedDecrement(&mesh->attrib_buffer_lock_count);
1458     if (lock_count < 0)
1459     {
1460         InterlockedIncrement(&mesh->attrib_buffer_lock_count);
1461         return D3DERR_INVALIDCALL;
1462     }
1463 
1464     return D3D_OK;
1465 }
1466 
1467 static HRESULT WINAPI d3dx9_mesh_Optimize(ID3DXMesh *iface, DWORD flags, const DWORD *adjacency_in,
1468         DWORD *adjacency_out, DWORD *face_remap, ID3DXBuffer **vertex_remap, ID3DXMesh **opt_mesh)
1469 {
1470     struct d3dx9_mesh *mesh = impl_from_ID3DXMesh(iface);
1471     HRESULT hr;
1472     D3DVERTEXELEMENT9 declaration[MAX_FVF_DECL_SIZE] = { D3DDECL_END() };
1473     ID3DXMesh *optimized_mesh;
1474 
1475     TRACE("iface %p, flags %#x, adjacency_in %p, adjacency_out %p, face_remap %p, vertex_remap %p, opt_mesh %p.\n",
1476             iface, flags, adjacency_in, adjacency_out, face_remap, vertex_remap, opt_mesh);
1477 
1478     if (!opt_mesh)
1479         return D3DERR_INVALIDCALL;
1480 
1481     hr = iface->lpVtbl->GetDeclaration(iface, declaration);
1482     if (FAILED(hr)) return hr;
1483 
1484     if (FAILED(hr = iface->lpVtbl->CloneMesh(iface, mesh->options, declaration, mesh->device, &optimized_mesh)))
1485         return hr;
1486 
1487     hr = optimized_mesh->lpVtbl->OptimizeInplace(optimized_mesh, flags, adjacency_in, adjacency_out, face_remap, vertex_remap);
1488     if (SUCCEEDED(hr))
1489         *opt_mesh = optimized_mesh;
1490     else
1491         IUnknown_Release(optimized_mesh);
1492     return hr;
1493 }
1494 
1495 /* Creates a vertex_remap that removes unused vertices.
1496  * Indices are updated according to the vertex_remap. */
1497 static HRESULT compact_mesh(struct d3dx9_mesh *This, DWORD *indices,
1498         DWORD *new_num_vertices, ID3DXBuffer **vertex_remap)
1499 {
1500     HRESULT hr;
1501     DWORD *vertex_remap_ptr;
1502     DWORD num_used_vertices;
1503     DWORD i;
1504 
1505     hr = D3DXCreateBuffer(This->numvertices * sizeof(DWORD), vertex_remap);
1506     if (FAILED(hr)) return hr;
1507     vertex_remap_ptr = ID3DXBuffer_GetBufferPointer(*vertex_remap);
1508 
1509     for (i = 0; i < This->numfaces * 3; i++)
1510         vertex_remap_ptr[indices[i]] = 1;
1511 
1512     /* create old->new vertex mapping */
1513     num_used_vertices = 0;
1514     for (i = 0; i < This->numvertices; i++) {
1515         if (vertex_remap_ptr[i])
1516             vertex_remap_ptr[i] = num_used_vertices++;
1517         else
1518             vertex_remap_ptr[i] = -1;
1519     }
1520     /* convert indices */
1521     for (i = 0; i < This->numfaces * 3; i++)
1522         indices[i] = vertex_remap_ptr[indices[i]];
1523 
1524     /* create new->old vertex mapping */
1525     num_used_vertices = 0;
1526     for (i = 0; i < This->numvertices; i++) {
1527         if (vertex_remap_ptr[i] != -1)
1528             vertex_remap_ptr[num_used_vertices++] = i;
1529     }
1530     for (i = num_used_vertices; i < This->numvertices; i++)
1531         vertex_remap_ptr[i] = -1;
1532 
1533     *new_num_vertices = num_used_vertices;
1534 
1535     return D3D_OK;
1536 }
1537 
1538 /* count the number of unique attribute values in a sorted attribute buffer */
1539 static DWORD count_attributes(const DWORD *attrib_buffer, DWORD numfaces)
1540 {
1541     DWORD last_attribute = attrib_buffer[0];
1542     DWORD attrib_table_size = 1;
1543     DWORD i;
1544     for (i = 1; i < numfaces; i++) {
1545         if (attrib_buffer[i] != last_attribute) {
1546             last_attribute = attrib_buffer[i];
1547             attrib_table_size++;
1548         }
1549     }
1550     return attrib_table_size;
1551 }
1552 
1553 static void fill_attribute_table(DWORD *attrib_buffer, DWORD numfaces, void *indices,
1554                                  BOOL is_32bit_indices, D3DXATTRIBUTERANGE *attrib_table)
1555 {
1556     DWORD attrib_table_size = 0;
1557     DWORD last_attribute = attrib_buffer[0];
1558     DWORD min_vertex, max_vertex;
1559     DWORD i;
1560 
1561     attrib_table[0].AttribId = last_attribute;
1562     attrib_table[0].FaceStart = 0;
1563     min_vertex = (DWORD)-1;
1564     max_vertex = 0;
1565     for (i = 0; i < numfaces; i++) {
1566         DWORD j;
1567 
1568         if (attrib_buffer[i] != last_attribute) {
1569             last_attribute = attrib_buffer[i];
1570             attrib_table[attrib_table_size].FaceCount = i - attrib_table[attrib_table_size].FaceStart;
1571             attrib_table[attrib_table_size].VertexStart = min_vertex;
1572             attrib_table[attrib_table_size].VertexCount = max_vertex - min_vertex + 1;
1573             attrib_table_size++;
1574             attrib_table[attrib_table_size].AttribId = attrib_buffer[i];
1575             attrib_table[attrib_table_size].FaceStart = i;
1576             min_vertex = (DWORD)-1;
1577             max_vertex = 0;
1578         }
1579         for (j = 0; j < 3; j++) {
1580             DWORD vertex_index = is_32bit_indices ? ((DWORD*)indices)[i * 3 + j] : ((WORD*)indices)[i * 3 + j];
1581             if (vertex_index < min_vertex)
1582                 min_vertex = vertex_index;
1583             if (vertex_index > max_vertex)
1584                 max_vertex = vertex_index;
1585         }
1586     }
1587     attrib_table[attrib_table_size].FaceCount = i - attrib_table[attrib_table_size].FaceStart;
1588     attrib_table[attrib_table_size].VertexStart = min_vertex;
1589     attrib_table[attrib_table_size].VertexCount = max_vertex - min_vertex + 1;
1590     attrib_table_size++;
1591 }
1592 
1593 static int __cdecl attrib_entry_compare(const void *a, const void *b)
1594 {
1595     const DWORD *ptr_a = *(const DWORD **)a;
1596     const DWORD *ptr_b = *(const DWORD **)b;
1597     int delta = *ptr_a - *ptr_b;
1598 
1599     if (delta)
1600         return delta;
1601 
1602     delta = ptr_a - ptr_b; /* for stable sort */
1603     return delta;
1604 }
1605 
1606 /* Create face_remap, a new attribute buffer for attribute sort optimization. */
1607 static HRESULT remap_faces_for_attrsort(struct d3dx9_mesh *This, const DWORD *indices,
1608         DWORD *attrib_buffer, DWORD **sorted_attrib_buffer, DWORD **face_remap)
1609 {
1610     DWORD **sorted_attrib_ptr_buffer = NULL;
1611     DWORD i;
1612 
1613     sorted_attrib_ptr_buffer = HeapAlloc(GetProcessHeap(), 0, This->numfaces * sizeof(*sorted_attrib_ptr_buffer));
1614     if (!sorted_attrib_ptr_buffer)
1615         return E_OUTOFMEMORY;
1616 
1617     *face_remap = HeapAlloc(GetProcessHeap(), 0, This->numfaces * sizeof(**face_remap));
1618     if (!*face_remap)
1619     {
1620         HeapFree(GetProcessHeap(), 0, sorted_attrib_ptr_buffer);
1621         return E_OUTOFMEMORY;
1622     }
1623 
1624     for (i = 0; i < This->numfaces; i++)
1625         sorted_attrib_ptr_buffer[i] = &attrib_buffer[i];
1626     qsort(sorted_attrib_ptr_buffer, This->numfaces, sizeof(*sorted_attrib_ptr_buffer), attrib_entry_compare);
1627 
1628     for (i = 0; i < This->numfaces; i++)
1629     {
1630         DWORD old_face = sorted_attrib_ptr_buffer[i] - attrib_buffer;
1631         (*face_remap)[old_face] = i;
1632     }
1633 
1634     /* overwrite sorted_attrib_ptr_buffer with the values themselves */
1635     *sorted_attrib_buffer = (DWORD*)sorted_attrib_ptr_buffer;
1636     for (i = 0; i < This->numfaces; i++)
1637         (*sorted_attrib_buffer)[(*face_remap)[i]] = attrib_buffer[i];
1638 
1639     return D3D_OK;
1640 }
1641 
1642 static HRESULT WINAPI d3dx9_mesh_OptimizeInplace(ID3DXMesh *iface, DWORD flags, const DWORD *adjacency_in,
1643         DWORD *adjacency_out, DWORD *face_remap_out, ID3DXBuffer **vertex_remap_out)
1644 {
1645     struct d3dx9_mesh *This = impl_from_ID3DXMesh(iface);
1646     void *indices = NULL;
1647     DWORD *attrib_buffer = NULL;
1648     HRESULT hr;
1649     ID3DXBuffer *vertex_remap = NULL;
1650     DWORD *face_remap = NULL; /* old -> new mapping */
1651     DWORD *dword_indices = NULL;
1652     DWORD new_num_vertices = 0;
1653     DWORD new_num_alloc_vertices = 0;
1654     IDirect3DVertexBuffer9 *vertex_buffer = NULL;
1655     DWORD *sorted_attrib_buffer = NULL;
1656     DWORD i;
1657 
1658     TRACE("iface %p, flags %#x, adjacency_in %p, adjacency_out %p, face_remap_out %p, vertex_remap_out %p.\n",
1659             iface, flags, adjacency_in, adjacency_out, face_remap_out, vertex_remap_out);
1660 
1661     if (!flags)
1662         return D3DERR_INVALIDCALL;
1663     if (!adjacency_in && (flags & (D3DXMESHOPT_VERTEXCACHE | D3DXMESHOPT_STRIPREORDER)))
1664         return D3DERR_INVALIDCALL;
1665     if ((flags & (D3DXMESHOPT_VERTEXCACHE | D3DXMESHOPT_STRIPREORDER)) == (D3DXMESHOPT_VERTEXCACHE | D3DXMESHOPT_STRIPREORDER))
1666         return D3DERR_INVALIDCALL;
1667 
1668     if (flags & (D3DXMESHOPT_VERTEXCACHE | D3DXMESHOPT_STRIPREORDER))
1669     {
1670         if (flags & D3DXMESHOPT_VERTEXCACHE)
1671             FIXME("D3DXMESHOPT_VERTEXCACHE not implemented.\n");
1672         if (flags & D3DXMESHOPT_STRIPREORDER)
1673             FIXME("D3DXMESHOPT_STRIPREORDER not implemented.\n");
1674         return E_NOTIMPL;
1675     }
1676 
1677     hr = iface->lpVtbl->LockIndexBuffer(iface, 0, &indices);
1678     if (FAILED(hr)) goto cleanup;
1679 
1680     dword_indices = HeapAlloc(GetProcessHeap(), 0, This->numfaces * 3 * sizeof(DWORD));
1681     if (!dword_indices) return E_OUTOFMEMORY;
1682     if (This->options & D3DXMESH_32BIT) {
1683         memcpy(dword_indices, indices, This->numfaces * 3 * sizeof(DWORD));
1684     } else {
1685         WORD *word_indices = indices;
1686         for (i = 0; i < This->numfaces * 3; i++)
1687             dword_indices[i] = *word_indices++;
1688     }
1689 
1690     if ((flags & (D3DXMESHOPT_COMPACT | D3DXMESHOPT_IGNOREVERTS | D3DXMESHOPT_ATTRSORT)) == D3DXMESHOPT_COMPACT)
1691     {
1692         new_num_alloc_vertices = This->numvertices;
1693         hr = compact_mesh(This, dword_indices, &new_num_vertices, &vertex_remap);
1694         if (FAILED(hr)) goto cleanup;
1695     } else if (flags & D3DXMESHOPT_ATTRSORT) {
1696         if (!(flags & D3DXMESHOPT_IGNOREVERTS))
1697         {
1698             FIXME("D3DXMESHOPT_ATTRSORT vertex reordering not implemented.\n");
1699             hr = E_NOTIMPL;
1700             goto cleanup;
1701         }
1702 
1703         hr = iface->lpVtbl->LockAttributeBuffer(iface, 0, &attrib_buffer);
1704         if (FAILED(hr)) goto cleanup;
1705 
1706         hr = remap_faces_for_attrsort(This, dword_indices, attrib_buffer, &sorted_attrib_buffer, &face_remap);
1707         if (FAILED(hr)) goto cleanup;
1708     }
1709 
1710     if (vertex_remap)
1711     {
1712         /* reorder the vertices using vertex_remap */
1713         D3DVERTEXBUFFER_DESC vertex_desc;
1714         DWORD *vertex_remap_ptr = ID3DXBuffer_GetBufferPointer(vertex_remap);
1715         DWORD vertex_size = iface->lpVtbl->GetNumBytesPerVertex(iface);
1716         BYTE *orig_vertices;
1717         BYTE *new_vertices;
1718 
1719         hr = IDirect3DVertexBuffer9_GetDesc(This->vertex_buffer, &vertex_desc);
1720         if (FAILED(hr)) goto cleanup;
1721 
1722         hr = IDirect3DDevice9_CreateVertexBuffer(This->device, new_num_alloc_vertices * vertex_size,
1723                 vertex_desc.Usage, This->fvf, vertex_desc.Pool, &vertex_buffer, NULL);
1724         if (FAILED(hr)) goto cleanup;
1725 
1726         hr = IDirect3DVertexBuffer9_Lock(This->vertex_buffer, 0, 0, (void**)&orig_vertices, D3DLOCK_READONLY);
1727         if (FAILED(hr)) goto cleanup;
1728 
1729         hr = IDirect3DVertexBuffer9_Lock(vertex_buffer, 0, 0, (void**)&new_vertices, 0);
1730         if (FAILED(hr)) {
1731             IDirect3DVertexBuffer9_Unlock(This->vertex_buffer);
1732             goto cleanup;
1733         }
1734 
1735         for (i = 0; i < new_num_vertices; i++)
1736             memcpy(new_vertices + i * vertex_size, orig_vertices + vertex_remap_ptr[i] * vertex_size, vertex_size);
1737 
1738         IDirect3DVertexBuffer9_Unlock(This->vertex_buffer);
1739         IDirect3DVertexBuffer9_Unlock(vertex_buffer);
1740     } else if (vertex_remap_out) {
1741         DWORD *vertex_remap_ptr;
1742 
1743         hr = D3DXCreateBuffer(This->numvertices * sizeof(DWORD), &vertex_remap);
1744         if (FAILED(hr)) goto cleanup;
1745         vertex_remap_ptr = ID3DXBuffer_GetBufferPointer(vertex_remap);
1746         for (i = 0; i < This->numvertices; i++)
1747             *vertex_remap_ptr++ = i;
1748     }
1749 
1750     if (flags & D3DXMESHOPT_ATTRSORT)
1751     {
1752         D3DXATTRIBUTERANGE *attrib_table;
1753         DWORD attrib_table_size;
1754 
1755         attrib_table_size = count_attributes(sorted_attrib_buffer, This->numfaces);
1756         attrib_table = HeapAlloc(GetProcessHeap(), 0, attrib_table_size * sizeof(*attrib_table));
1757         if (!attrib_table) {
1758             hr = E_OUTOFMEMORY;
1759             goto cleanup;
1760         }
1761 
1762         memcpy(attrib_buffer, sorted_attrib_buffer, This->numfaces * sizeof(*attrib_buffer));
1763 
1764         /* reorder the indices using face_remap */
1765         if (This->options & D3DXMESH_32BIT) {
1766             for (i = 0; i < This->numfaces; i++)
1767                 memcpy((DWORD*)indices + face_remap[i] * 3, dword_indices + i * 3, 3 * sizeof(DWORD));
1768         } else {
1769             WORD *word_indices = indices;
1770             for (i = 0; i < This->numfaces; i++) {
1771                 DWORD new_pos = face_remap[i] * 3;
1772                 DWORD old_pos = i * 3;
1773                 word_indices[new_pos++] = dword_indices[old_pos++];
1774                 word_indices[new_pos++] = dword_indices[old_pos++];
1775                 word_indices[new_pos] = dword_indices[old_pos];
1776             }
1777         }
1778 
1779         fill_attribute_table(attrib_buffer, This->numfaces, indices,
1780                              This->options & D3DXMESH_32BIT, attrib_table);
1781 
1782         HeapFree(GetProcessHeap(), 0, This->attrib_table);
1783         This->attrib_table = attrib_table;
1784         This->attrib_table_size = attrib_table_size;
1785     } else {
1786         if (This->options & D3DXMESH_32BIT) {
1787             memcpy(indices, dword_indices, This->numfaces * 3 * sizeof(DWORD));
1788         } else {
1789             WORD *word_indices = indices;
1790             for (i = 0; i < This->numfaces * 3; i++)
1791                 *word_indices++ = dword_indices[i];
1792         }
1793     }
1794 
1795     if (adjacency_out) {
1796         if (face_remap) {
1797             for (i = 0; i < This->numfaces; i++) {
1798                 DWORD old_pos = i * 3;
1799                 DWORD new_pos = face_remap[i] * 3;
1800                 adjacency_out[new_pos++] = face_remap[adjacency_in[old_pos++]];
1801                 adjacency_out[new_pos++] = face_remap[adjacency_in[old_pos++]];
1802                 adjacency_out[new_pos++] = face_remap[adjacency_in[old_pos++]];
1803             }
1804         } else {
1805             memcpy(adjacency_out, adjacency_in, This->numfaces * 3 * sizeof(*adjacency_out));
1806         }
1807     }
1808     if (face_remap_out) {
1809         if (face_remap) {
1810             for (i = 0; i < This->numfaces; i++)
1811                 face_remap_out[face_remap[i]] = i;
1812         } else {
1813             for (i = 0; i < This->numfaces; i++)
1814                 face_remap_out[i] = i;
1815         }
1816     }
1817     if (vertex_remap_out)
1818         *vertex_remap_out = vertex_remap;
1819     vertex_remap = NULL;
1820 
1821     if (vertex_buffer) {
1822         IDirect3DVertexBuffer9_Release(This->vertex_buffer);
1823         This->vertex_buffer = vertex_buffer;
1824         vertex_buffer = NULL;
1825         This->numvertices = new_num_vertices;
1826     }
1827 
1828     hr = D3D_OK;
1829 cleanup:
1830     HeapFree(GetProcessHeap(), 0, sorted_attrib_buffer);
1831     HeapFree(GetProcessHeap(), 0, face_remap);
1832     HeapFree(GetProcessHeap(), 0, dword_indices);
1833     if (vertex_remap) ID3DXBuffer_Release(vertex_remap);
1834     if (vertex_buffer) IDirect3DVertexBuffer9_Release(vertex_buffer);
1835     if (attrib_buffer) iface->lpVtbl->UnlockAttributeBuffer(iface);
1836     if (indices) iface->lpVtbl->UnlockIndexBuffer(iface);
1837     return hr;
1838 }
1839 
1840 static HRESULT WINAPI d3dx9_mesh_SetAttributeTable(ID3DXMesh *iface,
1841         const D3DXATTRIBUTERANGE *attrib_table, DWORD attrib_table_size)
1842 {
1843     struct d3dx9_mesh *mesh = impl_from_ID3DXMesh(iface);
1844     D3DXATTRIBUTERANGE *new_table = NULL;
1845 
1846     TRACE("iface %p, attrib_table %p, attrib_table_size %u.\n", iface, attrib_table, attrib_table_size);
1847 
1848     if (attrib_table_size) {
1849         size_t size = attrib_table_size * sizeof(*attrib_table);
1850 
1851         new_table = HeapAlloc(GetProcessHeap(), 0, size);
1852         if (!new_table)
1853             return E_OUTOFMEMORY;
1854 
1855         CopyMemory(new_table, attrib_table, size);
1856     } else if (attrib_table) {
1857         return D3DERR_INVALIDCALL;
1858     }
1859     HeapFree(GetProcessHeap(), 0, mesh->attrib_table);
1860     mesh->attrib_table = new_table;
1861     mesh->attrib_table_size = attrib_table_size;
1862 
1863     return D3D_OK;
1864 }
1865 
1866 static const struct ID3DXMeshVtbl D3DXMesh_Vtbl =
1867 {
1868     d3dx9_mesh_QueryInterface,
1869     d3dx9_mesh_AddRef,
1870     d3dx9_mesh_Release,
1871     d3dx9_mesh_DrawSubset,
1872     d3dx9_mesh_GetNumFaces,
1873     d3dx9_mesh_GetNumVertices,
1874     d3dx9_mesh_GetFVF,
1875     d3dx9_mesh_GetDeclaration,
1876     d3dx9_mesh_GetNumBytesPerVertex,
1877     d3dx9_mesh_GetOptions,
1878     d3dx9_mesh_GetDevice,
1879     d3dx9_mesh_CloneMeshFVF,
1880     d3dx9_mesh_CloneMesh,
1881     d3dx9_mesh_GetVertexBuffer,
1882     d3dx9_mesh_GetIndexBuffer,
1883     d3dx9_mesh_LockVertexBuffer,
1884     d3dx9_mesh_UnlockVertexBuffer,
1885     d3dx9_mesh_LockIndexBuffer,
1886     d3dx9_mesh_UnlockIndexBuffer,
1887     d3dx9_mesh_GetAttributeTable,
1888     d3dx9_mesh_ConvertPointRepsToAdjacency,
1889     d3dx9_mesh_ConvertAdjacencyToPointReps,
1890     d3dx9_mesh_GenerateAdjacency,
1891     d3dx9_mesh_UpdateSemantics,
1892     d3dx9_mesh_LockAttributeBuffer,
1893     d3dx9_mesh_UnlockAttributeBuffer,
1894     d3dx9_mesh_Optimize,
1895     d3dx9_mesh_OptimizeInplace,
1896     d3dx9_mesh_SetAttributeTable,
1897 };
1898 
1899 
1900 /* Algorithm taken from the article: An Efficient and Robust Ray-Box Intersection Algorithm
1901 Amy Williams             University of Utah
1902 Steve Barrus             University of Utah
1903 R. Keith Morley          University of Utah
1904 Peter Shirley            University of Utah
1905 
1906 International Conference on Computer Graphics and Interactive Techniques  archive
1907 ACM SIGGRAPH 2005 Courses
1908 Los Angeles, California
1909 
1910 This algorithm is free of patents or of copyrights, as confirmed by Peter Shirley himself.
1911 
1912 Algorithm: Consider the box as the intersection of three slabs. Clip the ray
1913 against each slab, if there's anything left of the ray after we're
1914 done we've got an intersection of the ray with the box. */
1915 BOOL WINAPI D3DXBoxBoundProbe(const D3DXVECTOR3 *pmin, const D3DXVECTOR3 *pmax,
1916         const D3DXVECTOR3 *prayposition, const D3DXVECTOR3 *praydirection)
1917 {
1918     FLOAT div, tmin, tmax, tymin, tymax, tzmin, tzmax;
1919 
1920     div = 1.0f / praydirection->x;
1921     if ( div >= 0.0f )
1922     {
1923         tmin = ( pmin->x - prayposition->x ) * div;
1924         tmax = ( pmax->x - prayposition->x ) * div;
1925     }
1926     else
1927     {
1928         tmin = ( pmax->x - prayposition->x ) * div;
1929         tmax = ( pmin->x - prayposition->x ) * div;
1930     }
1931 
1932     if ( tmax < 0.0f ) return FALSE;
1933 
1934     div = 1.0f / praydirection->y;
1935     if ( div >= 0.0f )
1936     {
1937         tymin = ( pmin->y - prayposition->y ) * div;
1938         tymax = ( pmax->y - prayposition->y ) * div;
1939     }
1940     else
1941     {
1942         tymin = ( pmax->y - prayposition->y ) * div;
1943         tymax = ( pmin->y - prayposition->y ) * div;
1944     }
1945 
1946     if ( ( tymax < 0.0f ) || ( tmin > tymax ) || ( tymin > tmax ) ) return FALSE;
1947 
1948     if ( tymin > tmin ) tmin = tymin;
1949     if ( tymax < tmax ) tmax = tymax;
1950 
1951     div = 1.0f / praydirection->z;
1952     if ( div >= 0.0f )
1953     {
1954         tzmin = ( pmin->z - prayposition->z ) * div;
1955         tzmax = ( pmax->z - prayposition->z ) * div;
1956     }
1957     else
1958     {
1959         tzmin = ( pmax->z - prayposition->z ) * div;
1960         tzmax = ( pmin->z - prayposition->z ) * div;
1961     }
1962 
1963     if ( (tzmax < 0.0f ) || ( tmin > tzmax ) || ( tzmin > tmax ) ) return FALSE;
1964 
1965     return TRUE;
1966 }
1967 
1968 HRESULT WINAPI D3DXComputeBoundingBox(const D3DXVECTOR3 *pfirstposition,
1969         DWORD numvertices, DWORD dwstride, D3DXVECTOR3 *pmin, D3DXVECTOR3 *pmax)
1970 {
1971     D3DXVECTOR3 vec;
1972     unsigned int i;
1973 
1974     if( !pfirstposition || !pmin || !pmax ) return D3DERR_INVALIDCALL;
1975 
1976     *pmin = *pfirstposition;
1977     *pmax = *pmin;
1978 
1979     for(i=0; i<numvertices; i++)
1980     {
1981         vec = *( (const D3DXVECTOR3*)((const char*)pfirstposition + dwstride * i) );
1982 
1983         if ( vec.x < pmin->x ) pmin->x = vec.x;
1984         if ( vec.x > pmax->x ) pmax->x = vec.x;
1985 
1986         if ( vec.y < pmin->y ) pmin->y = vec.y;
1987         if ( vec.y > pmax->y ) pmax->y = vec.y;
1988 
1989         if ( vec.z < pmin->z ) pmin->z = vec.z;
1990         if ( vec.z > pmax->z ) pmax->z = vec.z;
1991     }
1992 
1993     return D3D_OK;
1994 }
1995 
1996 HRESULT WINAPI D3DXComputeBoundingSphere(const D3DXVECTOR3 *pfirstposition,
1997         DWORD numvertices, DWORD dwstride, D3DXVECTOR3 *pcenter, float *pradius)
1998 {
1999     D3DXVECTOR3 temp;
2000     FLOAT d;
2001     unsigned int i;
2002 
2003     if( !pfirstposition || !pcenter || !pradius ) return D3DERR_INVALIDCALL;
2004 
2005     temp.x = 0.0f;
2006     temp.y = 0.0f;
2007     temp.z = 0.0f;
2008     *pradius = 0.0f;
2009 
2010     for(i=0; i<numvertices; i++)
2011         D3DXVec3Add(&temp, &temp, (const D3DXVECTOR3*)((const char*)pfirstposition + dwstride * i));
2012 
2013     D3DXVec3Scale(pcenter, &temp, 1.0f / numvertices);
2014 
2015     for(i=0; i<numvertices; i++)
2016     {
2017         d = D3DXVec3Length(D3DXVec3Subtract(&temp, (const D3DXVECTOR3*)((const char*)pfirstposition + dwstride * i), pcenter));
2018         if ( d > *pradius ) *pradius = d;
2019     }
2020     return D3D_OK;
2021 }
2022 
2023 static void append_decl_element(D3DVERTEXELEMENT9 *declaration, UINT *idx, UINT *offset,
2024         D3DDECLTYPE type, D3DDECLUSAGE usage, UINT usage_idx)
2025 {
2026     declaration[*idx].Stream = 0;
2027     declaration[*idx].Offset = *offset;
2028     declaration[*idx].Type = type;
2029     declaration[*idx].Method = D3DDECLMETHOD_DEFAULT;
2030     declaration[*idx].Usage = usage;
2031     declaration[*idx].UsageIndex = usage_idx;
2032 
2033     *offset += d3dx_decltype_size[type];
2034     ++(*idx);
2035 }
2036 
2037 /*************************************************************************
2038  * D3DXDeclaratorFromFVF
2039  */
2040 HRESULT WINAPI D3DXDeclaratorFromFVF(DWORD fvf, D3DVERTEXELEMENT9 declaration[MAX_FVF_DECL_SIZE])
2041 {
2042     static const D3DVERTEXELEMENT9 end_element = D3DDECL_END();
2043     DWORD tex_count = (fvf & D3DFVF_TEXCOUNT_MASK) >> D3DFVF_TEXCOUNT_SHIFT;
2044     unsigned int offset = 0;
2045     unsigned int idx = 0;
2046     unsigned int i;
2047 
2048     TRACE("fvf %#x, declaration %p.\n", fvf, declaration);
2049 
2050     if (fvf & (D3DFVF_RESERVED0 | D3DFVF_RESERVED2)) return D3DERR_INVALIDCALL;
2051 
2052     if (fvf & D3DFVF_POSITION_MASK)
2053     {
2054         BOOL has_blend = (fvf & D3DFVF_XYZB5) >= D3DFVF_XYZB1;
2055         DWORD blend_count = 1 + (((fvf & D3DFVF_XYZB5) - D3DFVF_XYZB1) >> 1);
2056         BOOL has_blend_idx = (fvf & D3DFVF_LASTBETA_D3DCOLOR) || (fvf & D3DFVF_LASTBETA_UBYTE4);
2057 
2058         if (has_blend_idx) --blend_count;
2059 
2060         if ((fvf & D3DFVF_POSITION_MASK) == D3DFVF_XYZW
2061                 || (has_blend && blend_count > 4))
2062             return D3DERR_INVALIDCALL;
2063 
2064         if ((fvf & D3DFVF_POSITION_MASK) == D3DFVF_XYZRHW)
2065             append_decl_element(declaration, &idx, &offset, D3DDECLTYPE_FLOAT4, D3DDECLUSAGE_POSITIONT, 0);
2066         else
2067             append_decl_element(declaration, &idx, &offset, D3DDECLTYPE_FLOAT3, D3DDECLUSAGE_POSITION, 0);
2068 
2069         if (has_blend)
2070         {
2071             switch (blend_count)
2072             {
2073                  case 0:
2074                     break;
2075                  case 1:
2076                     append_decl_element(declaration, &idx, &offset, D3DDECLTYPE_FLOAT1, D3DDECLUSAGE_BLENDWEIGHT, 0);
2077                     break;
2078                  case 2:
2079                     append_decl_element(declaration, &idx, &offset, D3DDECLTYPE_FLOAT2, D3DDECLUSAGE_BLENDWEIGHT, 0);
2080                     break;
2081                  case 3:
2082                     append_decl_element(declaration, &idx, &offset, D3DDECLTYPE_FLOAT3, D3DDECLUSAGE_BLENDWEIGHT, 0);
2083                     break;
2084                  case 4:
2085                     append_decl_element(declaration, &idx, &offset, D3DDECLTYPE_FLOAT4, D3DDECLUSAGE_BLENDWEIGHT, 0);
2086                     break;
2087                  default:
2088                      ERR("Invalid blend count %u.\n", blend_count);
2089                      break;
2090             }
2091 
2092             if (has_blend_idx)
2093             {
2094                 if (fvf & D3DFVF_LASTBETA_UBYTE4)
2095                     append_decl_element(declaration, &idx, &offset, D3DDECLTYPE_UBYTE4, D3DDECLUSAGE_BLENDINDICES, 0);
2096                 else if (fvf & D3DFVF_LASTBETA_D3DCOLOR)
2097                     append_decl_element(declaration, &idx, &offset, D3DDECLTYPE_D3DCOLOR, D3DDECLUSAGE_BLENDINDICES, 0);
2098             }
2099         }
2100     }
2101 
2102     if (fvf & D3DFVF_NORMAL)
2103         append_decl_element(declaration, &idx, &offset, D3DDECLTYPE_FLOAT3, D3DDECLUSAGE_NORMAL, 0);
2104     if (fvf & D3DFVF_PSIZE)
2105         append_decl_element(declaration, &idx, &offset, D3DDECLTYPE_FLOAT1, D3DDECLUSAGE_PSIZE, 0);
2106     if (fvf & D3DFVF_DIFFUSE)
2107         append_decl_element(declaration, &idx, &offset, D3DDECLTYPE_D3DCOLOR, D3DDECLUSAGE_COLOR, 0);
2108     if (fvf & D3DFVF_SPECULAR)
2109         append_decl_element(declaration, &idx, &offset, D3DDECLTYPE_D3DCOLOR, D3DDECLUSAGE_COLOR, 1);
2110 
2111     for (i = 0; i < tex_count; ++i)
2112     {
2113         switch ((fvf >> (16 + 2 * i)) & 0x03)
2114         {
2115             case D3DFVF_TEXTUREFORMAT1:
2116                 append_decl_element(declaration, &idx, &offset, D3DDECLTYPE_FLOAT1, D3DDECLUSAGE_TEXCOORD, i);
2117                 break;
2118             case D3DFVF_TEXTUREFORMAT2:
2119                 append_decl_element(declaration, &idx, &offset, D3DDECLTYPE_FLOAT2, D3DDECLUSAGE_TEXCOORD, i);
2120                 break;
2121             case D3DFVF_TEXTUREFORMAT3:
2122                 append_decl_element(declaration, &idx, &offset, D3DDECLTYPE_FLOAT3, D3DDECLUSAGE_TEXCOORD, i);
2123                 break;
2124             case D3DFVF_TEXTUREFORMAT4:
2125                 append_decl_element(declaration, &idx, &offset, D3DDECLTYPE_FLOAT4, D3DDECLUSAGE_TEXCOORD, i);
2126                 break;
2127         }
2128     }
2129 
2130     declaration[idx] = end_element;
2131 
2132     return D3D_OK;
2133 }
2134 
2135 /*************************************************************************
2136  * D3DXFVFFromDeclarator
2137  */
2138 HRESULT WINAPI D3DXFVFFromDeclarator(const D3DVERTEXELEMENT9 *declaration, DWORD *fvf)
2139 {
2140     unsigned int i = 0, texture, offset;
2141 
2142     TRACE("(%p, %p)\n", declaration, fvf);
2143 
2144     *fvf = 0;
2145     if (declaration[0].Type == D3DDECLTYPE_FLOAT3 && declaration[0].Usage == D3DDECLUSAGE_POSITION)
2146     {
2147         if ((declaration[1].Type == D3DDECLTYPE_FLOAT4 && declaration[1].Usage == D3DDECLUSAGE_BLENDWEIGHT &&
2148              declaration[1].UsageIndex == 0) &&
2149             (declaration[2].Type == D3DDECLTYPE_FLOAT1 && declaration[2].Usage == D3DDECLUSAGE_BLENDINDICES &&
2150              declaration[2].UsageIndex == 0))
2151         {
2152             return D3DERR_INVALIDCALL;
2153         }
2154         else if ((declaration[1].Type == D3DDECLTYPE_UBYTE4 || declaration[1].Type == D3DDECLTYPE_D3DCOLOR) &&
2155                  declaration[1].Usage == D3DDECLUSAGE_BLENDINDICES && declaration[1].UsageIndex == 0)
2156         {
2157             if (declaration[1].Type == D3DDECLTYPE_UBYTE4)
2158             {
2159                 *fvf |= D3DFVF_XYZB1 | D3DFVF_LASTBETA_UBYTE4;
2160             }
2161             else
2162             {
2163                 *fvf |= D3DFVF_XYZB1 | D3DFVF_LASTBETA_D3DCOLOR;
2164             }
2165             i = 2;
2166         }
2167         else if (declaration[1].Type <= D3DDECLTYPE_FLOAT4 && declaration[1].Usage == D3DDECLUSAGE_BLENDWEIGHT &&
2168                  declaration[1].UsageIndex == 0)
2169         {
2170             if ((declaration[2].Type == D3DDECLTYPE_UBYTE4 || declaration[2].Type == D3DDECLTYPE_D3DCOLOR) &&
2171                 declaration[2].Usage == D3DDECLUSAGE_BLENDINDICES && declaration[2].UsageIndex == 0)
2172             {
2173                 if (declaration[2].Type == D3DDECLTYPE_UBYTE4)
2174                 {
2175                     *fvf |= D3DFVF_LASTBETA_UBYTE4;
2176                 }
2177                 else
2178                 {
2179                     *fvf |= D3DFVF_LASTBETA_D3DCOLOR;
2180                 }
2181                 switch (declaration[1].Type)
2182                 {
2183                     case D3DDECLTYPE_FLOAT1: *fvf |= D3DFVF_XYZB2; break;
2184                     case D3DDECLTYPE_FLOAT2: *fvf |= D3DFVF_XYZB3; break;
2185                     case D3DDECLTYPE_FLOAT3: *fvf |= D3DFVF_XYZB4; break;
2186                     case D3DDECLTYPE_FLOAT4: *fvf |= D3DFVF_XYZB5; break;
2187                 }
2188                 i = 3;
2189             }
2190             else
2191             {
2192                 switch (declaration[1].Type)
2193                 {
2194                     case D3DDECLTYPE_FLOAT1: *fvf |= D3DFVF_XYZB1; break;
2195                     case D3DDECLTYPE_FLOAT2: *fvf |= D3DFVF_XYZB2; break;
2196                     case D3DDECLTYPE_FLOAT3: *fvf |= D3DFVF_XYZB3; break;
2197                     case D3DDECLTYPE_FLOAT4: *fvf |= D3DFVF_XYZB4; break;
2198                 }
2199                 i = 2;
2200             }
2201         }
2202         else
2203         {
2204             *fvf |= D3DFVF_XYZ;
2205             i = 1;
2206         }
2207     }
2208     else if (declaration[0].Type == D3DDECLTYPE_FLOAT4 && declaration[0].Usage == D3DDECLUSAGE_POSITIONT &&
2209              declaration[0].UsageIndex == 0)
2210     {
2211         *fvf |= D3DFVF_XYZRHW;
2212         i = 1;
2213     }
2214 
2215     if (declaration[i].Type == D3DDECLTYPE_FLOAT3 && declaration[i].Usage == D3DDECLUSAGE_NORMAL)
2216     {
2217         *fvf |= D3DFVF_NORMAL;
2218         i++;
2219     }
2220     if (declaration[i].Type == D3DDECLTYPE_FLOAT1 && declaration[i].Usage == D3DDECLUSAGE_PSIZE &&
2221         declaration[i].UsageIndex == 0)
2222     {
2223         *fvf |= D3DFVF_PSIZE;
2224         i++;
2225     }
2226     if (declaration[i].Type == D3DDECLTYPE_D3DCOLOR && declaration[i].Usage == D3DDECLUSAGE_COLOR &&
2227         declaration[i].UsageIndex == 0)
2228     {
2229         *fvf |= D3DFVF_DIFFUSE;
2230         i++;
2231     }
2232     if (declaration[i].Type == D3DDECLTYPE_D3DCOLOR && declaration[i].Usage == D3DDECLUSAGE_COLOR &&
2233         declaration[i].UsageIndex == 1)
2234     {
2235         *fvf |= D3DFVF_SPECULAR;
2236         i++;
2237     }
2238 
2239     for (texture = 0; texture < D3DDP_MAXTEXCOORD; i++, texture++)
2240     {
2241         if (declaration[i].Stream == 0xFF)
2242         {
2243             break;
2244         }
2245         else if (declaration[i].Type == D3DDECLTYPE_FLOAT1 && declaration[i].Usage == D3DDECLUSAGE_TEXCOORD &&
2246                  declaration[i].UsageIndex == texture)
2247         {
2248             *fvf |= D3DFVF_TEXCOORDSIZE1(declaration[i].UsageIndex);
2249         }
2250         else if (declaration[i].Type == D3DDECLTYPE_FLOAT2 && declaration[i].Usage == D3DDECLUSAGE_TEXCOORD &&
2251                  declaration[i].UsageIndex == texture)
2252         {
2253             *fvf |= D3DFVF_TEXCOORDSIZE2(declaration[i].UsageIndex);
2254         }
2255         else if (declaration[i].Type == D3DDECLTYPE_FLOAT3 && declaration[i].Usage == D3DDECLUSAGE_TEXCOORD &&
2256                  declaration[i].UsageIndex == texture)
2257         {
2258             *fvf |= D3DFVF_TEXCOORDSIZE3(declaration[i].UsageIndex);
2259         }
2260         else if (declaration[i].Type == D3DDECLTYPE_FLOAT4 && declaration[i].Usage == D3DDECLUSAGE_TEXCOORD &&
2261                  declaration[i].UsageIndex == texture)
2262         {
2263             *fvf |= D3DFVF_TEXCOORDSIZE4(declaration[i].UsageIndex);
2264         }
2265         else
2266         {
2267             return D3DERR_INVALIDCALL;
2268         }
2269     }
2270 
2271     *fvf |= (texture << D3DFVF_TEXCOUNT_SHIFT);
2272 
2273     for (offset = 0, i = 0; declaration[i].Stream != 0xFF;
2274          offset += d3dx_decltype_size[declaration[i].Type], i++)
2275     {
2276         if (declaration[i].Offset != offset)
2277         {
2278             return D3DERR_INVALIDCALL;
2279         }
2280     }
2281 
2282     return D3D_OK;
2283 }
2284 
2285 /*************************************************************************
2286  * D3DXGetFVFVertexSize
2287  */
2288 static UINT Get_TexCoord_Size_From_FVF(DWORD FVF, int tex_num)
2289 {
2290     return (((((FVF) >> (16 + (2 * (tex_num)))) + 1) & 0x03) + 1);
2291 }
2292 
2293 UINT WINAPI D3DXGetFVFVertexSize(DWORD FVF)
2294 {
2295     DWORD size = 0;
2296     UINT i;
2297     UINT numTextures = (FVF & D3DFVF_TEXCOUNT_MASK) >> D3DFVF_TEXCOUNT_SHIFT;
2298 
2299     if (FVF & D3DFVF_NORMAL) size += sizeof(D3DXVECTOR3);
2300     if (FVF & D3DFVF_DIFFUSE) size += sizeof(DWORD);
2301     if (FVF & D3DFVF_SPECULAR) size += sizeof(DWORD);
2302     if (FVF & D3DFVF_PSIZE) size += sizeof(DWORD);
2303 
2304     switch (FVF & D3DFVF_POSITION_MASK)
2305     {
2306         case D3DFVF_XYZ:    size += sizeof(D3DXVECTOR3); break;
2307         case D3DFVF_XYZRHW: size += 4 * sizeof(FLOAT); break;
2308         case D3DFVF_XYZB1:  size += 4 * sizeof(FLOAT); break;
2309         case D3DFVF_XYZB2:  size += 5 * sizeof(FLOAT); break;
2310         case D3DFVF_XYZB3:  size += 6 * sizeof(FLOAT); break;
2311         case D3DFVF_XYZB4:  size += 7 * sizeof(FLOAT); break;
2312         case D3DFVF_XYZB5:  size += 8 * sizeof(FLOAT); break;
2313         case D3DFVF_XYZW:   size += 4 * sizeof(FLOAT); break;
2314     }
2315 
2316     for (i = 0; i < numTextures; i++)
2317     {
2318         size += Get_TexCoord_Size_From_FVF(FVF, i) * sizeof(FLOAT);
2319     }
2320 
2321     return size;
2322 }
2323 
2324 /*************************************************************************
2325  * D3DXGetDeclVertexSize
2326  */
2327 UINT WINAPI D3DXGetDeclVertexSize(const D3DVERTEXELEMENT9 *decl, DWORD stream_idx)
2328 {
2329     const D3DVERTEXELEMENT9 *element;
2330     UINT size = 0;
2331 
2332     TRACE("decl %p, stream_idx %u\n", decl, stream_idx);
2333 
2334     if (!decl) return 0;
2335 
2336     for (element = decl; element->Stream != 0xff; ++element)
2337     {
2338         UINT type_size;
2339 
2340         if (element->Stream != stream_idx) continue;
2341 
2342         if (element->Type >= ARRAY_SIZE(d3dx_decltype_size))
2343         {
2344             FIXME("Unhandled element type %#x, size will be incorrect.\n", element->Type);
2345             continue;
2346         }
2347 
2348         type_size = d3dx_decltype_size[element->Type];
2349         if (element->Offset + type_size > size) size = element->Offset + type_size;
2350     }
2351 
2352     return size;
2353 }
2354 
2355 /*************************************************************************
2356  * D3DXGetDeclLength
2357  */
2358 UINT WINAPI D3DXGetDeclLength(const D3DVERTEXELEMENT9 *decl)
2359 {
2360     const D3DVERTEXELEMENT9 *element;
2361 
2362     TRACE("decl %p\n", decl);
2363 
2364     /* null decl results in exception on Windows XP */
2365 
2366     for (element = decl; element->Stream != 0xff; ++element);
2367 
2368     return element - decl;
2369 }
2370 
2371 BOOL WINAPI D3DXIntersectTri(const D3DXVECTOR3 *p0, const D3DXVECTOR3 *p1, const D3DXVECTOR3 *p2,
2372         const D3DXVECTOR3 *praypos, const D3DXVECTOR3 *praydir, float *pu, float *pv, float *pdist)
2373 {
2374     D3DXMATRIX m;
2375     D3DXVECTOR4 vec;
2376 
2377     TRACE("p0 %p, p1 %p, p2 %p, praypos %p, praydir %p, pu %p, pv %p, pdist %p.\n",
2378             p0, p1, p2, praypos, praydir, pu, pv, pdist);
2379 
2380     m.u.m[0][0] = p1->x - p0->x;
2381     m.u.m[1][0] = p2->x - p0->x;
2382     m.u.m[2][0] = -praydir->x;
2383     m.u.m[3][0] = 0.0f;
2384     m.u.m[0][1] = p1->y - p0->y;
2385     m.u.m[1][1] = p2->y - p0->y;
2386     m.u.m[2][1] = -praydir->y;
2387     m.u.m[3][1] = 0.0f;
2388     m.u.m[0][2] = p1->z - p0->z;
2389     m.u.m[1][2] = p2->z - p0->z;
2390     m.u.m[2][2] = -praydir->z;
2391     m.u.m[3][2] = 0.0f;
2392     m.u.m[0][3] = 0.0f;
2393     m.u.m[1][3] = 0.0f;
2394     m.u.m[2][3] = 0.0f;
2395     m.u.m[3][3] = 1.0f;
2396 
2397     vec.x = praypos->x - p0->x;
2398     vec.y = praypos->y - p0->y;
2399     vec.z = praypos->z - p0->z;
2400     vec.w = 0.0f;
2401 
2402     if ( D3DXMatrixInverse(&m, NULL, &m) )
2403     {
2404         D3DXVec4Transform(&vec, &vec, &m);
2405         if ( (vec.x >= 0.0f) && (vec.y >= 0.0f) && (vec.x + vec.y <= 1.0f) && (vec.z >= 0.0f) )
2406         {
2407             if (pu) *pu = vec.x;
2408             if (pv) *pv = vec.y;
2409             if (pdist) *pdist = fabsf( vec.z );
2410             return TRUE;
2411         }
2412     }
2413 
2414     return FALSE;
2415 }
2416 
2417 BOOL WINAPI D3DXSphereBoundProbe(const D3DXVECTOR3 *pcenter, float radius,
2418         const D3DXVECTOR3 *prayposition, const D3DXVECTOR3 *praydirection)
2419 {
2420     D3DXVECTOR3 difference;
2421     FLOAT a, b, c, d;
2422 
2423     a = D3DXVec3LengthSq(praydirection);
2424     if (!D3DXVec3Subtract(&difference, prayposition, pcenter)) return FALSE;
2425     b = D3DXVec3Dot(&difference, praydirection);
2426     c = D3DXVec3LengthSq(&difference) - radius * radius;
2427     d = b * b - a * c;
2428 
2429     if ( ( d <= 0.0f ) || ( sqrt(d) <= b ) ) return FALSE;
2430     return TRUE;
2431 }
2432 
2433 /*************************************************************************
2434  * D3DXCreateMesh
2435  */
2436 HRESULT WINAPI D3DXCreateMesh(DWORD numfaces, DWORD numvertices, DWORD options,
2437         const D3DVERTEXELEMENT9 *declaration, struct IDirect3DDevice9 *device, struct ID3DXMesh **mesh)
2438 {
2439     HRESULT hr;
2440     DWORD fvf;
2441     IDirect3DVertexDeclaration9 *vertex_declaration;
2442     UINT vertex_declaration_size;
2443     UINT num_elem;
2444     IDirect3DVertexBuffer9 *vertex_buffer;
2445     IDirect3DIndexBuffer9 *index_buffer;
2446     DWORD *attrib_buffer;
2447     struct d3dx9_mesh *object;
2448     DWORD index_usage = 0;
2449     D3DPOOL index_pool = D3DPOOL_DEFAULT;
2450     D3DFORMAT index_format = D3DFMT_INDEX16;
2451     DWORD vertex_usage = 0;
2452     D3DPOOL vertex_pool = D3DPOOL_DEFAULT;
2453     int i;
2454 
2455     TRACE("numfaces %u, numvertices %u, options %#x, declaration %p, device %p, mesh %p.\n",
2456             numfaces, numvertices, options, declaration, device, mesh);
2457 
2458     if (numfaces == 0 || numvertices == 0 || declaration == NULL || device == NULL || mesh == NULL ||
2459         /* D3DXMESH_VB_SHARE is for cloning, and D3DXMESH_USEHWONLY is for ConvertToBlendedMesh */
2460         (options & (D3DXMESH_VB_SHARE | D3DXMESH_USEHWONLY | 0xfffe0000)))
2461     {
2462         return D3DERR_INVALIDCALL;
2463     }
2464     for (i = 0; declaration[i].Stream != 0xff; i++)
2465         if (declaration[i].Stream != 0)
2466             return D3DERR_INVALIDCALL;
2467     num_elem = i + 1;
2468 
2469     if (options & D3DXMESH_32BIT)
2470         index_format = D3DFMT_INDEX32;
2471 
2472     if (options & D3DXMESH_DONOTCLIP) {
2473         index_usage |= D3DUSAGE_DONOTCLIP;
2474         vertex_usage |= D3DUSAGE_DONOTCLIP;
2475     }
2476     if (options & D3DXMESH_POINTS) {
2477         index_usage |= D3DUSAGE_POINTS;
2478         vertex_usage |= D3DUSAGE_POINTS;
2479     }
2480     if (options & D3DXMESH_RTPATCHES) {
2481         index_usage |= D3DUSAGE_RTPATCHES;
2482         vertex_usage |= D3DUSAGE_RTPATCHES;
2483     }
2484     if (options & D3DXMESH_NPATCHES) {
2485         index_usage |= D3DUSAGE_NPATCHES;
2486         vertex_usage |= D3DUSAGE_NPATCHES;
2487     }
2488 
2489     if (options & D3DXMESH_VB_SYSTEMMEM)
2490         vertex_pool = D3DPOOL_SYSTEMMEM;
2491     else if (options & D3DXMESH_VB_MANAGED)
2492         vertex_pool = D3DPOOL_MANAGED;
2493 
2494     if (options & D3DXMESH_VB_WRITEONLY)
2495         vertex_usage |= D3DUSAGE_WRITEONLY;
2496     if (options & D3DXMESH_VB_DYNAMIC)
2497         vertex_usage |= D3DUSAGE_DYNAMIC;
2498     if (options & D3DXMESH_VB_SOFTWAREPROCESSING)
2499         vertex_usage |= D3DUSAGE_SOFTWAREPROCESSING;
2500 
2501     if (options & D3DXMESH_IB_SYSTEMMEM)
2502         index_pool = D3DPOOL_SYSTEMMEM;
2503     else if (options & D3DXMESH_IB_MANAGED)
2504         index_pool = D3DPOOL_MANAGED;
2505 
2506     if (options & D3DXMESH_IB_WRITEONLY)
2507         index_usage |= D3DUSAGE_WRITEONLY;
2508     if (options & D3DXMESH_IB_DYNAMIC)
2509         index_usage |= D3DUSAGE_DYNAMIC;
2510     if (options & D3DXMESH_IB_SOFTWAREPROCESSING)
2511         index_usage |= D3DUSAGE_SOFTWAREPROCESSING;
2512 
2513     hr = D3DXFVFFromDeclarator(declaration, &fvf);
2514     if (hr != D3D_OK)
2515     {
2516         fvf = 0;
2517     }
2518 
2519     /* Create vertex declaration */
2520     hr = IDirect3DDevice9_CreateVertexDeclaration(device,
2521                                                   declaration,
2522                                                   &vertex_declaration);
2523     if (FAILED(hr))
2524     {
2525         WARN("Unexpected return value %x from IDirect3DDevice9_CreateVertexDeclaration.\n",hr);
2526         return hr;
2527     }
2528     vertex_declaration_size = D3DXGetDeclVertexSize(declaration, declaration[0].Stream);
2529 
2530     /* Create vertex buffer */
2531     hr = IDirect3DDevice9_CreateVertexBuffer(device,
2532                                              numvertices * vertex_declaration_size,
2533                                              vertex_usage,
2534                                              fvf,
2535                                              vertex_pool,
2536                                              &vertex_buffer,
2537                                              NULL);
2538     if (FAILED(hr))
2539     {
2540         WARN("Unexpected return value %x from IDirect3DDevice9_CreateVertexBuffer.\n",hr);
2541         IDirect3DVertexDeclaration9_Release(vertex_declaration);
2542         return hr;
2543     }
2544 
2545     /* Create index buffer */
2546     hr = IDirect3DDevice9_CreateIndexBuffer(device,
2547                                             numfaces * 3 * ((index_format == D3DFMT_INDEX16) ? 2 : 4),
2548                                             index_usage,
2549                                             index_format,
2550                                             index_pool,
2551                                             &index_buffer,
2552                                             NULL);
2553     if (FAILED(hr))
2554     {
2555         WARN("Unexpected return value %x from IDirect3DDevice9_CreateVertexBuffer.\n",hr);
2556         IDirect3DVertexBuffer9_Release(vertex_buffer);
2557         IDirect3DVertexDeclaration9_Release(vertex_declaration);
2558         return hr;
2559     }
2560 
2561     attrib_buffer = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, numfaces * sizeof(*attrib_buffer));
2562     object = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(*object));
2563     if (object == NULL || attrib_buffer == NULL)
2564     {
2565         HeapFree(GetProcessHeap(), 0, object);
2566         HeapFree(GetProcessHeap(), 0, attrib_buffer);
2567         IDirect3DIndexBuffer9_Release(index_buffer);
2568         IDirect3DVertexBuffer9_Release(vertex_buffer);
2569         IDirect3DVertexDeclaration9_Release(vertex_declaration);
2570         *mesh = NULL;
2571         return E_OUTOFMEMORY;
2572     }
2573     object->ID3DXMesh_iface.lpVtbl = &D3DXMesh_Vtbl;
2574     object->ref = 1;
2575 
2576     object->numfaces = numfaces;
2577     object->numvertices = numvertices;
2578     object->options = options;
2579     object->fvf = fvf;
2580     object->device = device;
2581     IDirect3DDevice9_AddRef(device);
2582 
2583     copy_declaration(object->cached_declaration, declaration, num_elem);
2584     object->vertex_declaration = vertex_declaration;
2585     object->vertex_declaration_size = vertex_declaration_size;
2586     object->num_elem = num_elem;
2587     object->vertex_buffer = vertex_buffer;
2588     object->index_buffer = index_buffer;
2589     object->attrib_buffer = attrib_buffer;
2590 
2591     *mesh = &object->ID3DXMesh_iface;
2592 
2593     return D3D_OK;
2594 }
2595 
2596 /*************************************************************************
2597  * D3DXCreateMeshFVF
2598  */
2599 HRESULT WINAPI D3DXCreateMeshFVF(DWORD numfaces, DWORD numvertices, DWORD options,
2600         DWORD fvf, struct IDirect3DDevice9 *device, struct ID3DXMesh **mesh)
2601 {
2602     HRESULT hr;
2603     D3DVERTEXELEMENT9 declaration[MAX_FVF_DECL_SIZE];
2604 
2605     TRACE("(%u, %u, %u, %u, %p, %p)\n", numfaces, numvertices, options, fvf, device, mesh);
2606 
2607     hr = D3DXDeclaratorFromFVF(fvf, declaration);
2608     if (FAILED(hr)) return hr;
2609 
2610     return D3DXCreateMesh(numfaces, numvertices, options, declaration, device, mesh);
2611 }
2612 
2613 
2614 struct mesh_data {
2615     DWORD num_vertices;
2616     DWORD num_poly_faces;
2617     DWORD num_tri_faces;
2618     D3DXVECTOR3 *vertices;
2619     DWORD *num_tri_per_face;
2620     DWORD *indices;
2621 
2622     DWORD fvf;
2623 
2624     /* optional mesh data */
2625 
2626     DWORD num_normals;
2627     D3DXVECTOR3 *normals;
2628     DWORD *normal_indices;
2629 
2630     D3DXVECTOR2 *tex_coords;
2631 
2632     DWORD *vertex_colors;
2633 
2634     DWORD num_materials;
2635     D3DXMATERIAL *materials;
2636     DWORD *material_indices;
2637 
2638     struct ID3DXSkinInfo *skin_info;
2639     DWORD nb_bones;
2640 };
2641 
2642 static HRESULT parse_texture_filename(ID3DXFileData *filedata, char **filename_out)
2643 {
2644     HRESULT hr;
2645     SIZE_T data_size;
2646     BYTE *data;
2647     char *filename_in;
2648     char *filename = NULL;
2649 
2650     /* template TextureFilename {
2651      *     STRING filename;
2652      * }
2653      */
2654 
2655     HeapFree(GetProcessHeap(), 0, *filename_out);
2656     *filename_out = NULL;
2657 
2658     hr = filedata->lpVtbl->Lock(filedata, &data_size, (const void**)&data);
2659     if (FAILED(hr)) return hr;
2660 
2661     /* FIXME: String must be retrieved directly instead of through a pointer once ID3DXFILE is fixed */
2662     if (data_size < sizeof(filename_in))
2663     {
2664         WARN("truncated data (%lu bytes)\n", data_size);
2665         filedata->lpVtbl->Unlock(filedata);
2666         return E_FAIL;
2667     }
2668     filename_in = *(char **)data;
2669 
2670     filename = HeapAlloc(GetProcessHeap(), 0, strlen(filename_in) + 1);
2671     if (!filename) {
2672         filedata->lpVtbl->Unlock(filedata);
2673         return E_OUTOFMEMORY;
2674     }
2675 
2676     strcpy(filename, filename_in);
2677     *filename_out = filename;
2678 
2679     filedata->lpVtbl->Unlock(filedata);
2680 
2681     return D3D_OK;
2682 }
2683 
2684 static HRESULT parse_material(ID3DXFileData *filedata, D3DXMATERIAL *material)
2685 {
2686     HRESULT hr;
2687     SIZE_T data_size;
2688     const BYTE *data;
2689     GUID type;
2690     ID3DXFileData *child;
2691     SIZE_T i, nb_children;
2692 
2693     material->pTextureFilename = NULL;
2694 
2695     hr = filedata->lpVtbl->Lock(filedata, &data_size, (const void**)&data);
2696     if (FAILED(hr)) return hr;
2697 
2698     /*
2699      * template ColorRGBA {
2700      *     FLOAT red;
2701      *     FLOAT green;
2702      *     FLOAT blue;
2703      *     FLOAT alpha;
2704      * }
2705      * template ColorRGB {
2706      *     FLOAT red;
2707      *     FLOAT green;
2708      *     FLOAT blue;
2709      * }
2710      * template Material {
2711      *     ColorRGBA faceColor;
2712      *     FLOAT power;
2713      *     ColorRGB specularColor;
2714      *     ColorRGB emissiveColor;
2715      *     [ ... ]
2716      * }
2717      */
2718     if (data_size != sizeof(FLOAT) * 11) {
2719         WARN("incorrect data size (%ld bytes)\n", data_size);
2720         filedata->lpVtbl->Unlock(filedata);
2721         return E_FAIL;
2722     }
2723 
2724     memcpy(&material->MatD3D.Diffuse, data, sizeof(D3DCOLORVALUE));
2725     data += sizeof(D3DCOLORVALUE);
2726     material->MatD3D.Power = *(FLOAT*)data;
2727     data += sizeof(FLOAT);
2728     memcpy(&material->MatD3D.Specular, data, sizeof(FLOAT) * 3);
2729     material->MatD3D.Specular.a = 1.0f;
2730     data += 3 * sizeof(FLOAT);
2731     memcpy(&material->MatD3D.Emissive, data, sizeof(FLOAT) * 3);
2732     material->MatD3D.Emissive.a = 1.0f;
2733     material->MatD3D.Ambient.r = 0.0f;
2734     material->MatD3D.Ambient.g = 0.0f;
2735     material->MatD3D.Ambient.b = 0.0f;
2736     material->MatD3D.Ambient.a = 1.0f;
2737 
2738     filedata->lpVtbl->Unlock(filedata);
2739 
2740     hr = filedata->lpVtbl->GetChildren(filedata, &nb_children);
2741     if (FAILED(hr))
2742         return hr;
2743 
2744     for (i = 0; i < nb_children; i++)
2745     {
2746         hr = filedata->lpVtbl->GetChild(filedata, i, &child);
2747         if (FAILED(hr))
2748             return hr;
2749         hr = child->lpVtbl->GetType(child, &type);
2750         if (FAILED(hr))
2751             goto err;
2752 
2753         if (IsEqualGUID(&type, &TID_D3DRMTextureFilename)) {
2754             hr = parse_texture_filename(child, &material->pTextureFilename);
2755             if (FAILED(hr))
2756                 goto err;
2757         }
2758         IUnknown_Release(child);
2759     }
2760     return D3D_OK;
2761 
2762 err:
2763     IUnknown_Release(child);
2764     return hr;
2765 }
2766 
2767 static void destroy_materials(struct mesh_data *mesh)
2768 {
2769     DWORD i;
2770     for (i = 0; i < mesh->num_materials; i++)
2771         HeapFree(GetProcessHeap(), 0, mesh->materials[i].pTextureFilename);
2772     HeapFree(GetProcessHeap(), 0, mesh->materials);
2773     HeapFree(GetProcessHeap(), 0, mesh->material_indices);
2774     mesh->num_materials = 0;
2775     mesh->materials = NULL;
2776     mesh->material_indices = NULL;
2777 }
2778 
2779 static HRESULT parse_material_list(ID3DXFileData *filedata, struct mesh_data *mesh)
2780 {
2781     HRESULT hr;
2782     SIZE_T data_size;
2783     const DWORD *data, *in_ptr;
2784     GUID type;
2785     ID3DXFileData *child = NULL;
2786     DWORD num_materials;
2787     DWORD i;
2788     SIZE_T nb_children;
2789 
2790     destroy_materials(mesh);
2791 
2792     hr = filedata->lpVtbl->Lock(filedata, &data_size, (const void**)&data);
2793     if (FAILED(hr)) return hr;
2794 
2795     /* template MeshMaterialList {
2796      *     DWORD nMaterials;
2797      *     DWORD nFaceIndexes;
2798      *     array DWORD faceIndexes[nFaceIndexes];
2799      *     [ Material ]
2800      * }
2801      */
2802 
2803     in_ptr = data;
2804     hr = E_FAIL;
2805 
2806     if (data_size < sizeof(DWORD)) {
2807         WARN("truncated data (%ld bytes)\n", data_size);
2808         goto end;
2809     }
2810     num_materials = *in_ptr++;
2811     if (!num_materials) {
2812         hr = D3D_OK;
2813         goto end;
2814     }
2815 
2816     if (data_size < 2 * sizeof(DWORD)) {
2817         WARN("truncated data (%ld bytes)\n", data_size);
2818         goto end;
2819     }
2820     if (*in_ptr++ != mesh->num_poly_faces) {
2821         WARN("number of material face indices (%u) doesn't match number of faces (%u)\n",
2822              *(in_ptr - 1), mesh->num_poly_faces);
2823         goto end;
2824     }
2825     if (data_size < 2 * sizeof(DWORD) + mesh->num_poly_faces * sizeof(DWORD)) {
2826         WARN("truncated data (%ld bytes)\n", data_size);
2827         goto end;
2828     }
2829     for (i = 0; i < mesh->num_poly_faces; i++) {
2830         if (*in_ptr++ >= num_materials) {
2831             WARN("face %u: reference to undefined material %u (only %u materials)\n",
2832                  i, *(in_ptr - 1), num_materials);
2833             goto end;
2834         }
2835     }
2836 
2837     mesh->materials = HeapAlloc(GetProcessHeap(), 0, num_materials * sizeof(*mesh->materials));
2838     mesh->material_indices = HeapAlloc(GetProcessHeap(), 0, mesh->num_poly_faces * sizeof(*mesh->material_indices));
2839     if (!mesh->materials || !mesh->material_indices) {
2840         hr = E_OUTOFMEMORY;
2841         goto end;
2842     }
2843     memcpy(mesh->material_indices, data + 2, mesh->num_poly_faces * sizeof(DWORD));
2844 
2845     hr = filedata->lpVtbl->GetChildren(filedata, &nb_children);
2846     if (FAILED(hr))
2847         goto end;
2848 
2849     for (i = 0; i < nb_children; i++)
2850     {
2851         hr = filedata->lpVtbl->GetChild(filedata, i, &child);
2852         if (FAILED(hr))
2853             goto end;
2854         hr = child->lpVtbl->GetType(child, &type);
2855         if (FAILED(hr))
2856             goto end;
2857 
2858         if (IsEqualGUID(&type, &TID_D3DRMMaterial)) {
2859             if (mesh->num_materials >= num_materials) {
2860                 WARN("more materials defined than declared\n");
2861                 hr = E_FAIL;
2862                 goto end;
2863             }
2864             hr = parse_material(child, &mesh->materials[mesh->num_materials++]);
2865             if (FAILED(hr))
2866                 goto end;
2867         }
2868 
2869         IUnknown_Release(child);
2870         child = NULL;
2871     }
2872     if (num_materials != mesh->num_materials) {
2873         WARN("only %u of %u materials defined\n", num_materials, mesh->num_materials);
2874         hr = E_FAIL;
2875     }
2876 
2877 end:
2878     if (child)
2879         IUnknown_Release(child);
2880     filedata->lpVtbl->Unlock(filedata);
2881     return hr;
2882 }
2883 
2884 static HRESULT parse_texture_coords(ID3DXFileData *filedata, struct mesh_data *mesh)
2885 {
2886     HRESULT hr;
2887     SIZE_T data_size;
2888     const BYTE *data;
2889 
2890     HeapFree(GetProcessHeap(), 0, mesh->tex_coords);
2891     mesh->tex_coords = NULL;
2892 
2893     hr = filedata->lpVtbl->Lock(filedata, &data_size, (const void**)&data);
2894     if (FAILED(hr)) return hr;
2895 
2896     /* template Coords2d {
2897      *     FLOAT u;
2898      *     FLOAT v;
2899      * }
2900      * template MeshTextureCoords {
2901      *     DWORD nTextureCoords;
2902      *     array Coords2d textureCoords[nTextureCoords];
2903      * }
2904      */
2905 
2906     hr = E_FAIL;
2907 
2908     if (data_size < sizeof(DWORD)) {
2909         WARN("truncated data (%ld bytes)\n", data_size);
2910         goto end;
2911     }
2912     if (*(DWORD*)data != mesh->num_vertices) {
2913         WARN("number of texture coordinates (%u) doesn't match number of vertices (%u)\n",
2914              *(DWORD*)data, mesh->num_vertices);
2915         goto end;
2916     }
2917     data += sizeof(DWORD);
2918     if (data_size < sizeof(DWORD) + mesh->num_vertices * sizeof(*mesh->tex_coords)) {
2919         WARN("truncated data (%ld bytes)\n", data_size);
2920         goto end;
2921     }
2922 
2923     mesh->tex_coords = HeapAlloc(GetProcessHeap(), 0, mesh->num_vertices * sizeof(*mesh->tex_coords));
2924     if (!mesh->tex_coords) {
2925         hr = E_OUTOFMEMORY;
2926         goto end;
2927     }
2928     memcpy(mesh->tex_coords, data, mesh->num_vertices * sizeof(*mesh->tex_coords));
2929 
2930     mesh->fvf |= D3DFVF_TEX1;
2931 
2932     hr = D3D_OK;
2933 
2934 end:
2935     filedata->lpVtbl->Unlock(filedata);
2936     return hr;
2937 }
2938 
2939 static HRESULT parse_vertex_colors(ID3DXFileData *filedata, struct mesh_data *mesh)
2940 {
2941     HRESULT hr;
2942     SIZE_T data_size;
2943     const BYTE *data;
2944     DWORD num_colors;
2945     DWORD i;
2946 
2947     HeapFree(GetProcessHeap(), 0, mesh->vertex_colors);
2948     mesh->vertex_colors = NULL;
2949 
2950     hr = filedata->lpVtbl->Lock(filedata, &data_size, (const void**)&data);
2951     if (FAILED(hr)) return hr;
2952 
2953     /* template IndexedColor {
2954      *     DWORD index;
2955      *     ColorRGBA indexColor;
2956      * }
2957      * template MeshVertexColors {
2958      *     DWORD nVertexColors;
2959      *     array IndexedColor vertexColors[nVertexColors];
2960      * }
2961      */
2962 
2963     hr = E_FAIL;
2964 
2965     if (data_size < sizeof(DWORD)) {
2966         WARN("truncated data (%ld bytes)\n", data_size);
2967         goto end;
2968     }
2969     num_colors = *(DWORD*)data;
2970     data += sizeof(DWORD);
2971     if (data_size < sizeof(DWORD) + num_colors * (sizeof(DWORD) + sizeof(D3DCOLORVALUE))) {
2972         WARN("truncated data (%ld bytes)\n", data_size);
2973         goto end;
2974     }
2975 
2976     mesh->vertex_colors = HeapAlloc(GetProcessHeap(), 0, mesh->num_vertices * sizeof(DWORD));
2977     if (!mesh->vertex_colors) {
2978         hr = E_OUTOFMEMORY;
2979         goto end;
2980     }
2981 
2982     for (i = 0; i < mesh->num_vertices; i++)
2983         mesh->vertex_colors[i] = D3DCOLOR_ARGB(0, 0xff, 0xff, 0xff);
2984     for (i = 0; i < num_colors; i++)
2985     {
2986         D3DCOLORVALUE color;
2987         DWORD index = *(DWORD*)data;
2988         data += sizeof(DWORD);
2989         if (index >= mesh->num_vertices) {
2990             WARN("vertex color %u references undefined vertex %u (only %u vertices)\n",
2991                  i, index, mesh->num_vertices);
2992             goto end;
2993         }
2994         memcpy(&color, data, sizeof(color));
2995         data += sizeof(color);
2996         color.r = min(1.0f, max(0.0f, color.r));
2997         color.g = min(1.0f, max(0.0f, color.g));
2998         color.b = min(1.0f, max(0.0f, color.b));
2999         color.a = min(1.0f, max(0.0f, color.a));
3000         mesh->vertex_colors[index] = D3DCOLOR_ARGB((BYTE)(color.a * 255.0f + 0.5f),
3001                                                    (BYTE)(color.r * 255.0f + 0.5f),
3002                                                    (BYTE)(color.g * 255.0f + 0.5f),
3003                                                    (BYTE)(color.b * 255.0f + 0.5f));
3004     }
3005 
3006     mesh->fvf |= D3DFVF_DIFFUSE;
3007 
3008     hr = D3D_OK;
3009 
3010 end:
3011     filedata->lpVtbl->Unlock(filedata);
3012     return hr;
3013 }
3014 
3015 static HRESULT parse_normals(ID3DXFileData *filedata, struct mesh_data *mesh)
3016 {
3017     HRESULT hr;
3018     SIZE_T data_size;
3019     const BYTE *data;
3020     DWORD *index_out_ptr;
3021     DWORD i;
3022     DWORD num_face_indices = mesh->num_poly_faces * 2 + mesh->num_tri_faces;
3023 
3024     HeapFree(GetProcessHeap(), 0, mesh->normals);
3025     mesh->num_normals = 0;
3026     mesh->normals = NULL;
3027     mesh->normal_indices = NULL;
3028     mesh->fvf |= D3DFVF_NORMAL;
3029 
3030     hr = filedata->lpVtbl->Lock(filedata, &data_size, (const void**)&data);
3031     if (FAILED(hr)) return hr;
3032 
3033     /* template Vector {
3034      *     FLOAT x;
3035      *     FLOAT y;
3036      *     FLOAT z;
3037      * }
3038      * template MeshFace {
3039      *     DWORD nFaceVertexIndices;
3040      *     array DWORD faceVertexIndices[nFaceVertexIndices];
3041      * }
3042      * template MeshNormals {
3043      *     DWORD nNormals;
3044      *     array Vector normals[nNormals];
3045      *     DWORD nFaceNormals;
3046      *     array MeshFace faceNormals[nFaceNormals];
3047      * }
3048      */
3049 
3050     hr = E_FAIL;
3051 
3052     if (data_size < sizeof(DWORD) * 2) {
3053         WARN("truncated data (%ld bytes)\n", data_size);
3054         goto end;
3055     }
3056     mesh->num_normals = *(DWORD*)data;
3057     data += sizeof(DWORD);
3058     if (data_size < sizeof(DWORD) * 2 + mesh->num_normals * sizeof(D3DXVECTOR3) +
3059                     num_face_indices * sizeof(DWORD)) {
3060         WARN("truncated data (%ld bytes)\n", data_size);
3061         goto end;
3062     }
3063 
3064     mesh->normals = HeapAlloc(GetProcessHeap(), 0, mesh->num_normals * sizeof(D3DXVECTOR3));
3065     mesh->normal_indices = HeapAlloc(GetProcessHeap(), 0, num_face_indices * sizeof(DWORD));
3066     if (!mesh->normals || !mesh->normal_indices) {
3067         hr = E_OUTOFMEMORY;
3068         goto end;
3069     }
3070 
3071     memcpy(mesh->normals, data, mesh->num_normals * sizeof(D3DXVECTOR3));
3072     data += mesh->num_normals * sizeof(D3DXVECTOR3);
3073     for (i = 0; i < mesh->num_normals; i++)
3074         D3DXVec3Normalize(&mesh->normals[i], &mesh->normals[i]);
3075 
3076     if (*(DWORD*)data != mesh->num_poly_faces) {
3077         WARN("number of face normals (%u) doesn't match number of faces (%u)\n",
3078              *(DWORD*)data, mesh->num_poly_faces);
3079         goto end;
3080     }
3081     data += sizeof(DWORD);
3082     index_out_ptr = mesh->normal_indices;
3083     for (i = 0; i < mesh->num_poly_faces; i++)
3084     {
3085         DWORD j;
3086         DWORD count = *(DWORD*)data;
3087         if (count != mesh->num_tri_per_face[i] + 2) {
3088             WARN("face %u: number of normals (%u) doesn't match number of vertices (%u)\n",
3089                  i, count, mesh->num_tri_per_face[i] + 2);
3090             goto end;
3091         }
3092         data += sizeof(DWORD);
3093 
3094         for (j = 0; j < count; j++) {
3095             DWORD normal_index = *(DWORD*)data;
3096             if (normal_index >= mesh->num_normals) {
3097                 WARN("face %u, normal index %u: reference to undefined normal %u (only %u normals)\n",
3098                      i, j, normal_index, mesh->num_normals);
3099                 goto end;
3100             }
3101             *index_out_ptr++ = normal_index;
3102             data += sizeof(DWORD);
3103         }
3104     }
3105 
3106     hr =  D3D_OK;
3107 
3108 end:
3109     filedata->lpVtbl->Unlock(filedata);
3110     return hr;
3111 }
3112 
3113 static HRESULT parse_skin_mesh_info(ID3DXFileData *filedata, struct mesh_data *mesh_data, DWORD index)
3114 {
3115     HRESULT hr;
3116     SIZE_T data_size;
3117     const BYTE *data;
3118 
3119     TRACE("(%p, %p, %u)\n", filedata, mesh_data, index);
3120 
3121     hr = filedata->lpVtbl->Lock(filedata, &data_size, (const void**)&data);
3122     if (FAILED(hr)) return hr;
3123 
3124     hr = E_FAIL;
3125 
3126     if (!mesh_data->skin_info) {
3127         if (data_size < sizeof(WORD) * 3) {
3128             WARN("truncated data (%ld bytes)\n", data_size);
3129             goto end;
3130         }
3131         /* Skip nMaxSkinWeightsPerVertex and nMaxSkinWeightsPerFace */
3132         data += 2 * sizeof(WORD);
3133         mesh_data->nb_bones = *(WORD*)data;
3134         hr = D3DXCreateSkinInfoFVF(mesh_data->num_vertices, mesh_data->fvf, mesh_data->nb_bones, &mesh_data->skin_info);
3135     } else {
3136         const char *name;
3137         DWORD nb_influences;
3138 
3139         /* FIXME: String must be retrieved directly instead of through a pointer once ID3DXFILE is fixed */
3140         name = *(const char**)data;
3141         data += sizeof(char*);
3142 
3143         nb_influences = *(DWORD*)data;
3144         data += sizeof(DWORD);
3145 
3146         if (data_size < (sizeof(char*) + sizeof(DWORD) + nb_influences * (sizeof(DWORD) + sizeof(FLOAT)) + 16 * sizeof(FLOAT))) {
3147             WARN("truncated data (%ld bytes)\n", data_size);
3148             goto end;
3149         }
3150 
3151         hr = mesh_data->skin_info->lpVtbl->SetBoneName(mesh_data->skin_info, index, name);
3152         if (SUCCEEDED(hr))
3153             hr = mesh_data->skin_info->lpVtbl->SetBoneInfluence(mesh_data->skin_info, index, nb_influences,
3154                      (const DWORD*)data, (const FLOAT*)(data + nb_influences * sizeof(DWORD)));
3155         if (SUCCEEDED(hr))
3156             hr = mesh_data->skin_info->lpVtbl->SetBoneOffsetMatrix(mesh_data->skin_info, index,
3157                      (const D3DMATRIX*)(data + nb_influences * (sizeof(DWORD) + sizeof(FLOAT))));
3158     }
3159 
3160 end:
3161     filedata->lpVtbl->Unlock(filedata);
3162     return hr;
3163 }
3164 
3165 /* for provide_flags parameters */
3166 #define PROVIDE_MATERIALS 0x1
3167 #define PROVIDE_SKININFO  0x2
3168 #define PROVIDE_ADJACENCY 0x4
3169 
3170 static HRESULT parse_mesh(ID3DXFileData *filedata, struct mesh_data *mesh_data, DWORD provide_flags)
3171 {
3172     HRESULT hr;
3173     SIZE_T data_size;
3174     const BYTE *data, *in_ptr;
3175     DWORD *index_out_ptr;
3176     GUID type;
3177     ID3DXFileData *child = NULL;
3178     DWORD i;
3179     SIZE_T nb_children;
3180     DWORD nb_skin_weights_info = 0;
3181 
3182     /*
3183      * template Mesh {
3184      *     DWORD nVertices;
3185      *     array Vector vertices[nVertices];
3186      *     DWORD nFaces;
3187      *     array MeshFace faces[nFaces];
3188      *     [ ... ]
3189      * }
3190      */
3191 
3192     hr = filedata->lpVtbl->Lock(filedata, &data_size, (const void**)&data);
3193     if (FAILED(hr)) return hr;
3194 
3195     in_ptr = data;
3196     hr = E_FAIL;
3197 
3198     if (data_size < sizeof(DWORD) * 2) {
3199         WARN("truncated data (%ld bytes)\n", data_size);
3200         goto end;
3201     }
3202     mesh_data->num_vertices = *(DWORD*)in_ptr;
3203     if (data_size < sizeof(DWORD) * 2 + mesh_data->num_vertices * sizeof(D3DXVECTOR3)) {
3204         WARN("truncated data (%ld bytes)\n", data_size);
3205         goto end;
3206     }
3207     in_ptr += sizeof(DWORD) + mesh_data->num_vertices * sizeof(D3DXVECTOR3);
3208 
3209     mesh_data->num_poly_faces = *(DWORD*)in_ptr;
3210     in_ptr += sizeof(DWORD);
3211 
3212     mesh_data->num_tri_faces = 0;
3213     for (i = 0; i < mesh_data->num_poly_faces; i++)
3214     {
3215         DWORD num_poly_vertices;
3216         DWORD j;
3217 
3218         if (data_size - (in_ptr - data) < sizeof(DWORD)) {
3219             WARN("truncated data (%ld bytes)\n", data_size);
3220             goto end;
3221         }
3222         num_poly_vertices = *(DWORD*)in_ptr;
3223         in_ptr += sizeof(DWORD);
3224         if (data_size - (in_ptr - data) < num_poly_vertices * sizeof(DWORD)) {
3225             WARN("truncated data (%ld bytes)\n", data_size);
3226             goto end;
3227         }
3228         if (num_poly_vertices < 3) {
3229             WARN("face %u has only %u vertices\n", i, num_poly_vertices);
3230             goto end;
3231         }
3232         for (j = 0; j < num_poly_vertices; j++) {
3233             if (*(DWORD*)in_ptr >= mesh_data->num_vertices) {
3234                 WARN("face %u, index %u: undefined vertex %u (only %u vertices)\n",
3235                      i, j, *(DWORD*)in_ptr, mesh_data->num_vertices);
3236                 goto end;
3237             }
3238             in_ptr += sizeof(DWORD);
3239         }
3240         mesh_data->num_tri_faces += num_poly_vertices - 2;
3241     }
3242 
3243     mesh_data->fvf = D3DFVF_XYZ;
3244 
3245     mesh_data->vertices = HeapAlloc(GetProcessHeap(), 0,
3246             mesh_data->num_vertices * sizeof(*mesh_data->vertices));
3247     mesh_data->num_tri_per_face = HeapAlloc(GetProcessHeap(), 0,
3248             mesh_data->num_poly_faces * sizeof(*mesh_data->num_tri_per_face));
3249     mesh_data->indices = HeapAlloc(GetProcessHeap(), 0,
3250             (mesh_data->num_tri_faces + mesh_data->num_poly_faces * 2) * sizeof(*mesh_data->indices));
3251     if (!mesh_data->vertices || !mesh_data->num_tri_per_face || !mesh_data->indices) {
3252         hr = E_OUTOFMEMORY;
3253         goto end;
3254     }
3255 
3256     in_ptr = data + sizeof(DWORD);
3257     memcpy(mesh_data->vertices, in_ptr, mesh_data->num_vertices * sizeof(D3DXVECTOR3));
3258     in_ptr += mesh_data->num_vertices * sizeof(D3DXVECTOR3) + sizeof(DWORD);
3259 
3260     index_out_ptr = mesh_data->indices;
3261     for (i = 0; i < mesh_data->num_poly_faces; i++)
3262     {
3263         DWORD count;
3264 
3265         count = *(DWORD*)in_ptr;
3266         in_ptr += sizeof(DWORD);
3267         mesh_data->num_tri_per_face[i] = count - 2;
3268 
3269         while (count--) {
3270             *index_out_ptr++ = *(DWORD*)in_ptr;
3271             in_ptr += sizeof(DWORD);
3272         }
3273     }
3274 
3275     hr = filedata->lpVtbl->GetChildren(filedata, &nb_children);
3276     if (FAILED(hr))
3277         goto end;
3278 
3279     for (i = 0; i < nb_children; i++)
3280     {
3281         hr = filedata->lpVtbl->GetChild(filedata, i, &child);
3282         if (FAILED(hr))
3283             goto end;
3284         hr = child->lpVtbl->GetType(child, &type);
3285         if (FAILED(hr))
3286             goto end;
3287 
3288         if (IsEqualGUID(&type, &TID_D3DRMMeshNormals)) {
3289             hr = parse_normals(child, mesh_data);
3290         } else if (IsEqualGUID(&type, &TID_D3DRMMeshVertexColors)) {
3291             hr = parse_vertex_colors(child, mesh_data);
3292         } else if (IsEqualGUID(&type, &TID_D3DRMMeshTextureCoords)) {
3293             hr = parse_texture_coords(child, mesh_data);
3294         } else if (IsEqualGUID(&type, &TID_D3DRMMeshMaterialList) &&
3295                    (provide_flags & PROVIDE_MATERIALS))
3296         {
3297             hr = parse_material_list(child, mesh_data);
3298         } else if (provide_flags & PROVIDE_SKININFO) {
3299             if (IsEqualGUID(&type, &DXFILEOBJ_XSkinMeshHeader)) {
3300                 if (mesh_data->skin_info) {
3301                     WARN("Skin mesh header already encountered\n");
3302                     hr = E_FAIL;
3303                     goto end;
3304                 }
3305                 hr = parse_skin_mesh_info(child, mesh_data, 0);
3306                 if (FAILED(hr))
3307                     goto end;
3308             } else if (IsEqualGUID(&type, &DXFILEOBJ_SkinWeights)) {
3309                 if (!mesh_data->skin_info) {
3310                     WARN("Skin weights found but skin mesh header not encountered yet\n");
3311                     hr = E_FAIL;
3312                     goto end;
3313                 }
3314                 hr = parse_skin_mesh_info(child, mesh_data, nb_skin_weights_info);
3315                 if (FAILED(hr))
3316                     goto end;
3317                 nb_skin_weights_info++;
3318             }
3319         }
3320         if (FAILED(hr))
3321             goto end;
3322 
3323         IUnknown_Release(child);
3324         child = NULL;
3325     }
3326 
3327     if (mesh_data->skin_info && (nb_skin_weights_info != mesh_data->nb_bones)) {
3328         WARN("Mismatch between nb skin weights info %u encountered and nb bones %u from skin mesh header\n",
3329              nb_skin_weights_info, mesh_data->nb_bones);
3330         hr = E_FAIL;
3331         goto end;
3332     }
3333 
3334     if ((provide_flags & PROVIDE_SKININFO) && !mesh_data->skin_info)
3335     {
3336         if (FAILED(hr = D3DXCreateSkinInfoFVF(mesh_data->num_vertices, mesh_data->fvf,
3337                 mesh_data->nb_bones, &mesh_data->skin_info)))
3338             goto end;
3339     }
3340 
3341     hr = D3D_OK;
3342 
3343 end:
3344     if (child)
3345         IUnknown_Release(child);
3346     filedata->lpVtbl->Unlock(filedata);
3347     return hr;
3348 }
3349 
3350 static HRESULT generate_effects(ID3DXBuffer *materials, DWORD num_materials,
3351                                 ID3DXBuffer **effects)
3352 {
3353     HRESULT hr;
3354     D3DXEFFECTINSTANCE *effect_ptr;
3355     BYTE *out_ptr;
3356     const D3DXMATERIAL *material_ptr = ID3DXBuffer_GetBufferPointer(materials);
3357     static const struct {
3358         const char *param_name;
3359         DWORD name_size;
3360         DWORD num_bytes;
3361         DWORD value_offset;
3362     } material_effects[] = {
3363 #define EFFECT_TABLE_ENTRY(str, field) \
3364     {str, sizeof(str), sizeof(material_ptr->MatD3D.field), offsetof(D3DXMATERIAL, MatD3D.field)}
3365         EFFECT_TABLE_ENTRY("Diffuse", Diffuse),
3366         EFFECT_TABLE_ENTRY("Power", Power),
3367         EFFECT_TABLE_ENTRY("Specular", Specular),
3368         EFFECT_TABLE_ENTRY("Emissive", Emissive),
3369         EFFECT_TABLE_ENTRY("Ambient", Ambient),
3370 #undef EFFECT_TABLE_ENTRY
3371     };
3372     static const char texture_paramname[] = "Texture0@Name";
3373     DWORD buffer_size;
3374     DWORD i;
3375 
3376     /* effects buffer layout:
3377      *
3378      * D3DXEFFECTINSTANCE effects[num_materials];
3379      * for (effect in effects)
3380      * {
3381      *     D3DXEFFECTDEFAULT defaults[effect.NumDefaults];
3382      *     for (default in defaults)
3383      *     {
3384      *         *default.pParamName;
3385      *         *default.pValue;
3386      *     }
3387      * }
3388      */
3389     buffer_size = sizeof(D3DXEFFECTINSTANCE);
3390     buffer_size += sizeof(D3DXEFFECTDEFAULT) * ARRAY_SIZE(material_effects);
3391     for (i = 0; i < ARRAY_SIZE(material_effects); i++) {
3392         buffer_size += material_effects[i].name_size;
3393         buffer_size += material_effects[i].num_bytes;
3394     }
3395     buffer_size *= num_materials;
3396     for (i = 0; i < num_materials; i++) {
3397         if (material_ptr[i].pTextureFilename) {
3398             buffer_size += sizeof(D3DXEFFECTDEFAULT);
3399             buffer_size += sizeof(texture_paramname);
3400             buffer_size += strlen(material_ptr[i].pTextureFilename) + 1;
3401         }
3402     }
3403 
3404     hr = D3DXCreateBuffer(buffer_size, effects);
3405     if (FAILED(hr)) return hr;
3406     effect_ptr = ID3DXBuffer_GetBufferPointer(*effects);
3407     out_ptr = (BYTE*)(effect_ptr + num_materials);
3408 
3409     for (i = 0; i < num_materials; i++)
3410     {
3411         DWORD j;
3412         D3DXEFFECTDEFAULT *defaults = (D3DXEFFECTDEFAULT*)out_ptr;
3413 
3414         effect_ptr->pDefaults = defaults;
3415         effect_ptr->NumDefaults = material_ptr->pTextureFilename ? 6 : 5;
3416         out_ptr = (BYTE*)(effect_ptr->pDefaults + effect_ptr->NumDefaults);
3417 
3418         for (j = 0; j < ARRAY_SIZE(material_effects); j++)
3419         {
3420             defaults->pParamName = (char *)out_ptr;
3421             strcpy(defaults->pParamName, material_effects[j].param_name);
3422             defaults->pValue = defaults->pParamName + material_effects[j].name_size;
3423             defaults->Type = D3DXEDT_FLOATS;
3424             defaults->NumBytes = material_effects[j].num_bytes;
3425             memcpy(defaults->pValue, (BYTE*)material_ptr + material_effects[j].value_offset, defaults->NumBytes);
3426             out_ptr = (BYTE*)defaults->pValue + defaults->NumBytes;
3427             defaults++;
3428         }
3429 
3430         if (material_ptr->pTextureFilename)
3431         {
3432             defaults->pParamName = (char *)out_ptr;
3433             strcpy(defaults->pParamName, texture_paramname);
3434             defaults->pValue = defaults->pParamName + sizeof(texture_paramname);
3435             defaults->Type = D3DXEDT_STRING;
3436             defaults->NumBytes = strlen(material_ptr->pTextureFilename) + 1;
3437             strcpy(defaults->pValue, material_ptr->pTextureFilename);
3438             out_ptr = (BYTE*)defaults->pValue + defaults->NumBytes;
3439         }
3440         material_ptr++;
3441         effect_ptr++;
3442     }
3443     assert(out_ptr - (BYTE*)ID3DXBuffer_GetBufferPointer(*effects) == buffer_size);
3444 
3445     return D3D_OK;
3446 }
3447 
3448 HRESULT WINAPI D3DXLoadSkinMeshFromXof(struct ID3DXFileData *filedata, DWORD options,
3449         struct IDirect3DDevice9 *device, struct ID3DXBuffer **adjacency_out, struct ID3DXBuffer **materials_out,
3450         struct ID3DXBuffer **effects_out, DWORD *num_materials_out, struct ID3DXSkinInfo **skin_info_out,
3451         struct ID3DXMesh **mesh_out)
3452 {
3453     HRESULT hr;
3454     DWORD *index_in_ptr;
3455     struct mesh_data mesh_data;
3456     DWORD total_vertices;
3457     ID3DXMesh *d3dxmesh = NULL;
3458     ID3DXBuffer *adjacency = NULL;
3459     ID3DXBuffer *materials = NULL;
3460     ID3DXBuffer *effects = NULL;
3461     struct vertex_duplication {
3462         DWORD normal_index;
3463         struct list entry;
3464     } *duplications = NULL;
3465     DWORD i;
3466     void *vertices = NULL;
3467     void *indices = NULL;
3468     BYTE *out_ptr;
3469     DWORD provide_flags = 0;
3470 
3471     TRACE("(%p, %x, %p, %p, %p, %p, %p, %p, %p)\n", filedata, options, device, adjacency_out, materials_out,
3472           effects_out, num_materials_out, skin_info_out, mesh_out);
3473 
3474     ZeroMemory(&mesh_data, sizeof(mesh_data));
3475 
3476     if (num_materials_out || materials_out || effects_out)
3477         provide_flags |= PROVIDE_MATERIALS;
3478     if (skin_info_out)
3479         provide_flags |= PROVIDE_SKININFO;
3480 
3481     hr = parse_mesh(filedata, &mesh_data, provide_flags);
3482     if (FAILED(hr)) goto cleanup;
3483 
3484     total_vertices = mesh_data.num_vertices;
3485     if (mesh_data.fvf & D3DFVF_NORMAL) {
3486         /* duplicate vertices with multiple normals */
3487         DWORD num_face_indices = mesh_data.num_poly_faces * 2 + mesh_data.num_tri_faces;
3488         duplications = HeapAlloc(GetProcessHeap(), 0, (mesh_data.num_vertices + num_face_indices) * sizeof(*duplications));
3489         if (!duplications) {
3490             hr = E_OUTOFMEMORY;
3491             goto cleanup;
3492         }
3493         for (i = 0; i < total_vertices; i++)
3494         {
3495             duplications[i].normal_index = -1;
3496             list_init(&duplications[i].entry);
3497         }
3498         for (i = 0; i < num_face_indices; i++) {
3499             DWORD vertex_index = mesh_data.indices[i];
3500             DWORD normal_index = mesh_data.normal_indices[i];
3501             struct vertex_duplication *dup_ptr = &duplications[vertex_index];
3502 
3503             if (dup_ptr->normal_index == -1) {
3504                 dup_ptr->normal_index = normal_index;
3505             } else {
3506                 D3DXVECTOR3 *new_normal = &mesh_data.normals[normal_index];
3507                 struct list *dup_list = &dup_ptr->entry;
3508                 while (TRUE) {
3509                     D3DXVECTOR3 *cur_normal = &mesh_data.normals[dup_ptr->normal_index];
3510                     if (new_normal->x == cur_normal->x &&
3511                         new_normal->y == cur_normal->y &&
3512                         new_normal->z == cur_normal->z)
3513                     {
3514                         mesh_data.indices[i] = dup_ptr - duplications;
3515                         break;
3516                     } else if (!list_next(dup_list, &dup_ptr->entry)) {
3517                         dup_ptr = &duplications[total_vertices++];
3518                         dup_ptr->normal_index = normal_index;
3519                         list_add_tail(dup_list, &dup_ptr->entry);
3520                         mesh_data.indices[i] = dup_ptr - duplications;
3521                         break;
3522                     } else {
3523                         dup_ptr = LIST_ENTRY(list_next(dup_list, &dup_ptr->entry),
3524                                              struct vertex_duplication, entry);
3525                     }
3526                 }
3527             }
3528         }
3529     }
3530 
3531     hr = D3DXCreateMeshFVF(mesh_data.num_tri_faces, total_vertices, options, mesh_data.fvf, device, &d3dxmesh);
3532     if (FAILED(hr)) goto cleanup;
3533 
3534     hr = d3dxmesh->lpVtbl->LockVertexBuffer(d3dxmesh, 0, &vertices);
3535     if (FAILED(hr)) goto cleanup;
3536 
3537     out_ptr = vertices;
3538     for (i = 0; i < mesh_data.num_vertices; i++) {
3539         *(D3DXVECTOR3*)out_ptr = mesh_data.vertices[i];
3540         out_ptr += sizeof(D3DXVECTOR3);
3541         if (mesh_data.fvf & D3DFVF_NORMAL) {
3542             if (duplications[i].normal_index == -1)
3543                 ZeroMemory(out_ptr, sizeof(D3DXVECTOR3));
3544             else
3545                 *(D3DXVECTOR3*)out_ptr = mesh_data.normals[duplications[i].normal_index];
3546             out_ptr += sizeof(D3DXVECTOR3);
3547         }
3548         if (mesh_data.fvf & D3DFVF_DIFFUSE) {
3549             *(DWORD*)out_ptr = mesh_data.vertex_colors[i];
3550             out_ptr += sizeof(DWORD);
3551         }
3552         if (mesh_data.fvf & D3DFVF_TEX1) {
3553             *(D3DXVECTOR2*)out_ptr = mesh_data.tex_coords[i];
3554             out_ptr += sizeof(D3DXVECTOR2);
3555         }
3556     }
3557     if (mesh_data.fvf & D3DFVF_NORMAL) {
3558         DWORD vertex_size = D3DXGetFVFVertexSize(mesh_data.fvf);
3559         out_ptr = vertices;
3560         for (i = 0; i < mesh_data.num_vertices; i++) {
3561             struct vertex_duplication *dup_ptr;
3562             LIST_FOR_EACH_ENTRY(dup_ptr, &duplications[i].entry, struct vertex_duplication, entry)
3563             {
3564                 int j = dup_ptr - duplications;
3565                 BYTE *dest_vertex = (BYTE*)vertices + j * vertex_size;
3566 
3567                 memcpy(dest_vertex, out_ptr, vertex_size);
3568                 dest_vertex += sizeof(D3DXVECTOR3);
3569                 *(D3DXVECTOR3*)dest_vertex = mesh_data.normals[dup_ptr->normal_index];
3570             }
3571             out_ptr += vertex_size;
3572         }
3573     }
3574     d3dxmesh->lpVtbl->UnlockVertexBuffer(d3dxmesh);
3575 
3576     hr = d3dxmesh->lpVtbl->LockIndexBuffer(d3dxmesh, 0, &indices);
3577     if (FAILED(hr)) goto cleanup;
3578 
3579     index_in_ptr = mesh_data.indices;
3580 #define FILL_INDEX_BUFFER(indices_var) \
3581         for (i = 0; i < mesh_data.num_poly_faces; i++) \
3582         { \
3583             DWORD count = mesh_data.num_tri_per_face[i]; \
3584             WORD first_index = *index_in_ptr++; \
3585             while (count--) { \
3586                 *indices_var++ = first_index; \
3587                 *indices_var++ = *index_in_ptr; \
3588                 index_in_ptr++; \
3589                 *indices_var++ = *index_in_ptr; \
3590             } \
3591             index_in_ptr++; \
3592         }
3593     if (options & D3DXMESH_32BIT) {
3594         DWORD *dword_indices = indices;
3595         FILL_INDEX_BUFFER(dword_indices)
3596     } else {
3597         WORD *word_indices = indices;
3598         FILL_INDEX_BUFFER(word_indices)
3599     }
3600 #undef FILL_INDEX_BUFFER
3601     d3dxmesh->lpVtbl->UnlockIndexBuffer(d3dxmesh);
3602 
3603     if (mesh_data.material_indices) {
3604         DWORD *attrib_buffer = NULL;
3605         hr = d3dxmesh->lpVtbl->LockAttributeBuffer(d3dxmesh, 0, &attrib_buffer);
3606         if (FAILED(hr)) goto cleanup;
3607         for (i = 0; i < mesh_data.num_poly_faces; i++)
3608         {
3609             DWORD count = mesh_data.num_tri_per_face[i];
3610             while (count--)
3611                 *attrib_buffer++ = mesh_data.material_indices[i];
3612         }
3613         d3dxmesh->lpVtbl->UnlockAttributeBuffer(d3dxmesh);
3614 
3615         hr = d3dxmesh->lpVtbl->OptimizeInplace(d3dxmesh,
3616                 D3DXMESHOPT_ATTRSORT | D3DXMESHOPT_IGNOREVERTS | D3DXMESHOPT_DONOTSPLIT,
3617                 NULL, NULL, NULL, NULL);
3618         if (FAILED(hr)) goto cleanup;
3619     }
3620 
3621     if (mesh_data.num_materials && (materials_out || effects_out)) {
3622         DWORD buffer_size = mesh_data.num_materials * sizeof(D3DXMATERIAL);
3623         char *strings_out_ptr;
3624         D3DXMATERIAL *materials_ptr;
3625 
3626         for (i = 0; i < mesh_data.num_materials; i++) {
3627             if (mesh_data.materials[i].pTextureFilename)
3628                 buffer_size += strlen(mesh_data.materials[i].pTextureFilename) + 1;
3629         }
3630 
3631         hr = D3DXCreateBuffer(buffer_size, &materials);
3632         if (FAILED(hr)) goto cleanup;
3633 
3634         materials_ptr = ID3DXBuffer_GetBufferPointer(materials);
3635         memcpy(materials_ptr, mesh_data.materials, mesh_data.num_materials * sizeof(D3DXMATERIAL));
3636         strings_out_ptr = (char*)(materials_ptr + mesh_data.num_materials);
3637         for (i = 0; i < mesh_data.num_materials; i++) {
3638             if (materials_ptr[i].pTextureFilename) {
3639                 strcpy(strings_out_ptr, mesh_data.materials[i].pTextureFilename);
3640                 materials_ptr[i].pTextureFilename = strings_out_ptr;
3641                 strings_out_ptr += strlen(mesh_data.materials[i].pTextureFilename) + 1;
3642             }
3643         }
3644     }
3645 
3646     if (mesh_data.num_materials && effects_out) {
3647         hr = generate_effects(materials, mesh_data.num_materials, &effects);
3648         if (FAILED(hr)) goto cleanup;
3649 
3650         if (!materials_out) {
3651             ID3DXBuffer_Release(materials);
3652             materials = NULL;
3653         }
3654     }
3655 
3656     if (adjacency_out) {
3657         hr = D3DXCreateBuffer(mesh_data.num_tri_faces * 3 * sizeof(DWORD), &adjacency);
3658         if (FAILED(hr)) goto cleanup;
3659         hr = d3dxmesh->lpVtbl->GenerateAdjacency(d3dxmesh, 0.0f, ID3DXBuffer_GetBufferPointer(adjacency));
3660         if (FAILED(hr)) goto cleanup;
3661     }
3662 
3663     *mesh_out = d3dxmesh;
3664     if (adjacency_out) *adjacency_out = adjacency;
3665     if (num_materials_out) *num_materials_out = mesh_data.num_materials;
3666     if (materials_out) *materials_out = materials;
3667     if (effects_out) *effects_out = effects;
3668     if (skin_info_out) *skin_info_out = mesh_data.skin_info;
3669 
3670     hr = D3D_OK;
3671 cleanup:
3672     if (FAILED(hr)) {
3673         if (d3dxmesh) IUnknown_Release(d3dxmesh);
3674         if (adjacency) ID3DXBuffer_Release(adjacency);
3675         if (materials) ID3DXBuffer_Release(materials);
3676         if (effects) ID3DXBuffer_Release(effects);
3677         if (mesh_data.skin_info) mesh_data.skin_info->lpVtbl->Release(mesh_data.skin_info);
3678         if (skin_info_out) *skin_info_out = NULL;
3679     }
3680     HeapFree(GetProcessHeap(), 0, mesh_data.vertices);
3681     HeapFree(GetProcessHeap(), 0, mesh_data.num_tri_per_face);
3682     HeapFree(GetProcessHeap(), 0, mesh_data.indices);
3683     HeapFree(GetProcessHeap(), 0, mesh_data.normals);
3684     HeapFree(GetProcessHeap(), 0, mesh_data.normal_indices);
3685     destroy_materials(&mesh_data);
3686     HeapFree(GetProcessHeap(), 0, mesh_data.tex_coords);
3687     HeapFree(GetProcessHeap(), 0, mesh_data.vertex_colors);
3688     HeapFree(GetProcessHeap(), 0, duplications);
3689     return hr;
3690 }
3691 
3692 HRESULT WINAPI D3DXLoadMeshHierarchyFromXA(const char *filename, DWORD options, struct IDirect3DDevice9 *device,
3693         struct ID3DXAllocateHierarchy *alloc_hier, struct ID3DXLoadUserData *load_user_data,
3694         D3DXFRAME **frame_hierarchy, struct ID3DXAnimationController **anim_controller)
3695 {
3696     WCHAR *filenameW;
3697     HRESULT hr;
3698     int len;
3699 
3700     TRACE("filename %s, options %#x, device %p, alloc_hier %p, "
3701             "load_user_data %p, frame_hierarchy %p, anim_controller %p.\n",
3702             debugstr_a(filename), options, device, alloc_hier,
3703             load_user_data, frame_hierarchy, anim_controller);
3704 
3705     if (!filename)
3706         return D3DERR_INVALIDCALL;
3707 
3708     len = MultiByteToWideChar(CP_ACP, 0, filename, -1, NULL, 0);
3709     filenameW = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
3710     if (!filenameW) return E_OUTOFMEMORY;
3711     MultiByteToWideChar(CP_ACP, 0, filename, -1, filenameW, len);
3712 
3713     hr = D3DXLoadMeshHierarchyFromXW(filenameW, options, device,
3714             alloc_hier, load_user_data, frame_hierarchy, anim_controller);
3715     HeapFree(GetProcessHeap(), 0, filenameW);
3716 
3717     return hr;
3718 }
3719 
3720 HRESULT WINAPI D3DXLoadMeshHierarchyFromXW(const WCHAR *filename, DWORD options, struct IDirect3DDevice9 *device,
3721         struct ID3DXAllocateHierarchy *alloc_hier, struct ID3DXLoadUserData *load_user_data,
3722         D3DXFRAME **frame_hierarchy, struct ID3DXAnimationController **anim_controller)
3723 {
3724     void *buffer;
3725     HRESULT hr;
3726     DWORD size;
3727 
3728     TRACE("filename %s, options %#x, device %p, alloc_hier %p, "
3729             "load_user_data %p, frame_hierarchy %p, anim_controller %p.\n",
3730             debugstr_w(filename), options, device, alloc_hier,
3731             load_user_data, frame_hierarchy, anim_controller);
3732 
3733     if (!filename)
3734         return D3DERR_INVALIDCALL;
3735 
3736     hr = map_view_of_file(filename, &buffer, &size);
3737     if (FAILED(hr))
3738         return D3DXERR_INVALIDDATA;
3739 
3740     hr = D3DXLoadMeshHierarchyFromXInMemory(buffer, size, options, device,
3741             alloc_hier, load_user_data, frame_hierarchy, anim_controller);
3742 
3743     UnmapViewOfFile(buffer);
3744 
3745     return hr;
3746 }
3747 
3748 static HRESULT filedata_get_name(ID3DXFileData *filedata, char **name)
3749 {
3750     HRESULT hr;
3751     SIZE_T name_len;
3752 
3753     hr = filedata->lpVtbl->GetName(filedata, NULL, &name_len);
3754     if (FAILED(hr)) return hr;
3755 
3756     if (!name_len)
3757         name_len++;
3758     *name = HeapAlloc(GetProcessHeap(), 0, name_len);
3759     if (!*name) return E_OUTOFMEMORY;
3760 
3761     hr = filedata->lpVtbl->GetName(filedata, *name, &name_len);
3762     if (FAILED(hr))
3763         HeapFree(GetProcessHeap(), 0, *name);
3764     else if (!name_len)
3765         (*name)[0] = 0;
3766 
3767     return hr;
3768 }
3769 
3770 static HRESULT load_mesh_container(struct ID3DXFileData *filedata, DWORD options, struct IDirect3DDevice9 *device,
3771         struct ID3DXAllocateHierarchy *alloc_hier, D3DXMESHCONTAINER **mesh_container)
3772 {
3773     HRESULT hr;
3774     ID3DXBuffer *adjacency = NULL;
3775     ID3DXBuffer *materials = NULL;
3776     ID3DXBuffer *effects = NULL;
3777     ID3DXSkinInfo *skin_info = NULL;
3778     D3DXMESHDATA mesh_data;
3779     DWORD num_materials = 0;
3780     char *name = NULL;
3781 
3782     mesh_data.Type = D3DXMESHTYPE_MESH;
3783     mesh_data.u.pMesh = NULL;
3784 
3785     hr = D3DXLoadSkinMeshFromXof(filedata, options, device,
3786             &adjacency, &materials, &effects, &num_materials,
3787             &skin_info, &mesh_data.u.pMesh);
3788     if (FAILED(hr)) return hr;
3789 
3790     hr = filedata_get_name(filedata, &name);
3791     if (FAILED(hr)) goto cleanup;
3792 
3793     hr = alloc_hier->lpVtbl->CreateMeshContainer(alloc_hier, name, &mesh_data,
3794             materials ? ID3DXBuffer_GetBufferPointer(materials) : NULL,
3795             effects ? ID3DXBuffer_GetBufferPointer(effects) : NULL,
3796             num_materials,
3797             adjacency ? ID3DXBuffer_GetBufferPointer(adjacency) : NULL,
3798             skin_info, mesh_container);
3799 
3800 cleanup:
3801     if (materials) ID3DXBuffer_Release(materials);
3802     if (effects) ID3DXBuffer_Release(effects);
3803     if (adjacency) ID3DXBuffer_Release(adjacency);
3804     if (skin_info) IUnknown_Release(skin_info);
3805     if (mesh_data.u.pMesh) IUnknown_Release(mesh_data.u.pMesh);
3806     HeapFree(GetProcessHeap(), 0, name);
3807     return hr;
3808 }
3809 
3810 static HRESULT parse_transform_matrix(ID3DXFileData *filedata, D3DXMATRIX *transform)
3811 {
3812     HRESULT hr;
3813     SIZE_T data_size;
3814     const BYTE *data;
3815 
3816     /* template Matrix4x4 {
3817      *     array FLOAT matrix[16];
3818      * }
3819      * template FrameTransformMatrix {
3820      *     Matrix4x4 frameMatrix;
3821      * }
3822      */
3823 
3824     hr = filedata->lpVtbl->Lock(filedata, &data_size, (const void**)&data);
3825     if (FAILED(hr)) return hr;
3826 
3827     if (data_size != sizeof(D3DXMATRIX)) {
3828         WARN("incorrect data size (%ld bytes)\n", data_size);
3829         filedata->lpVtbl->Unlock(filedata);
3830         return E_FAIL;
3831     }
3832 
3833     memcpy(transform, data, sizeof(D3DXMATRIX));
3834 
3835     filedata->lpVtbl->Unlock(filedata);
3836     return D3D_OK;
3837 }
3838 
3839 static HRESULT load_frame(struct ID3DXFileData *filedata, DWORD options, struct IDirect3DDevice9 *device,
3840         struct ID3DXAllocateHierarchy *alloc_hier, D3DXFRAME **frame_out)
3841 {
3842     HRESULT hr;
3843     GUID type;
3844     ID3DXFileData *child;
3845     char *name = NULL;
3846     D3DXFRAME *frame = NULL;
3847     D3DXMESHCONTAINER **next_container;
3848     D3DXFRAME **next_child;
3849     SIZE_T i, nb_children;
3850 
3851     hr = filedata_get_name(filedata, &name);
3852     if (FAILED(hr)) return hr;
3853 
3854     hr = alloc_hier->lpVtbl->CreateFrame(alloc_hier, name, frame_out);
3855     HeapFree(GetProcessHeap(), 0, name);
3856     if (FAILED(hr)) return E_FAIL;
3857 
3858     frame = *frame_out;
3859     D3DXMatrixIdentity(&frame->TransformationMatrix);
3860     next_child = &frame->pFrameFirstChild;
3861     next_container = &frame->pMeshContainer;
3862 
3863     hr = filedata->lpVtbl->GetChildren(filedata, &nb_children);
3864     if (FAILED(hr))
3865         return hr;
3866 
3867     for (i = 0; i < nb_children; i++)
3868     {
3869         hr = filedata->lpVtbl->GetChild(filedata, i, &child);
3870         if (FAILED(hr))
3871             return hr;
3872         hr = child->lpVtbl->GetType(child, &type);
3873         if (FAILED(hr))
3874             goto err;
3875 
3876         if (IsEqualGUID(&type, &TID_D3DRMMesh)) {
3877             hr = load_mesh_container(child, options, device, alloc_hier, next_container);
3878             if (SUCCEEDED(hr))
3879                 next_container = &(*next_container)->pNextMeshContainer;
3880         } else if (IsEqualGUID(&type, &TID_D3DRMFrameTransformMatrix)) {
3881             hr = parse_transform_matrix(child, &frame->TransformationMatrix);
3882         } else if (IsEqualGUID(&type, &TID_D3DRMFrame)) {
3883             hr = load_frame(child, options, device, alloc_hier, next_child);
3884             if (SUCCEEDED(hr))
3885                 next_child = &(*next_child)->pFrameSibling;
3886         }
3887         if (FAILED(hr))
3888             goto err;
3889 
3890         IUnknown_Release(child);
3891     }
3892     return D3D_OK;
3893 
3894 err:
3895     IUnknown_Release(child);
3896     return hr;
3897 }
3898 
3899 HRESULT WINAPI D3DXLoadMeshHierarchyFromXInMemory(const void *memory, DWORD memory_size, DWORD options,
3900         struct IDirect3DDevice9 *device, struct ID3DXAllocateHierarchy *alloc_hier,
3901         struct ID3DXLoadUserData *load_user_data, D3DXFRAME **frame_hierarchy,
3902         struct ID3DXAnimationController **anim_controller)
3903 {
3904     HRESULT hr;
3905     ID3DXFile *d3dxfile = NULL;
3906     ID3DXFileEnumObject *enumobj = NULL;
3907     ID3DXFileData *filedata = NULL;
3908     D3DXF_FILELOADMEMORY source;
3909     D3DXFRAME *first_frame = NULL;
3910     D3DXFRAME **next_frame = &first_frame;
3911     SIZE_T i, nb_children;
3912     GUID guid;
3913 
3914     TRACE("(%p, %u, %x, %p, %p, %p, %p, %p)\n", memory, memory_size, options,
3915           device, alloc_hier, load_user_data, frame_hierarchy, anim_controller);
3916 
3917     if (!memory || !memory_size || !device || !frame_hierarchy || !alloc_hier)
3918         return D3DERR_INVALIDCALL;
3919     if (load_user_data)
3920     {
3921         FIXME("Loading user data not implemented.\n");
3922         return E_NOTIMPL;
3923     }
3924 
3925     hr = D3DXFileCreate(&d3dxfile);
3926     if (FAILED(hr)) goto cleanup;
3927 
3928     hr = d3dxfile->lpVtbl->RegisterTemplates(d3dxfile, D3DRM_XTEMPLATES, D3DRM_XTEMPLATE_BYTES);
3929     if (FAILED(hr)) goto cleanup;
3930 
3931     source.lpMemory = (void*)memory;
3932     source.dSize = memory_size;
3933     hr = d3dxfile->lpVtbl->CreateEnumObject(d3dxfile, &source, D3DXF_FILELOAD_FROMMEMORY, &enumobj);
3934     if (FAILED(hr)) goto cleanup;
3935 
3936     hr = enumobj->lpVtbl->GetChildren(enumobj, &nb_children);
3937     if (FAILED(hr))
3938         goto cleanup;
3939 
3940     for (i = 0; i < nb_children; i++)
3941     {
3942         hr = enumobj->lpVtbl->GetChild(enumobj, i, &filedata);
3943         if (FAILED(hr))
3944             goto cleanup;
3945 
3946         hr = filedata->lpVtbl->GetType(filedata, &guid);
3947         if (SUCCEEDED(hr)) {
3948             if (IsEqualGUID(&guid, &TID_D3DRMMesh)) {
3949                 hr = alloc_hier->lpVtbl->CreateFrame(alloc_hier, NULL, next_frame);
3950                 if (FAILED(hr)) {
3951                     hr = E_FAIL;
3952                     goto cleanup;
3953                 }
3954 
3955                 D3DXMatrixIdentity(&(*next_frame)->TransformationMatrix);
3956 
3957                 hr = load_mesh_container(filedata, options, device, alloc_hier, &(*next_frame)->pMeshContainer);
3958                 if (FAILED(hr)) goto cleanup;
3959             } else if (IsEqualGUID(&guid, &TID_D3DRMFrame)) {
3960                 hr = load_frame(filedata, options, device, alloc_hier, next_frame);
3961                 if (FAILED(hr)) goto cleanup;
3962             }
3963             while (*next_frame)
3964                 next_frame = &(*next_frame)->pFrameSibling;
3965         }
3966 
3967         filedata->lpVtbl->Release(filedata);
3968         filedata = NULL;
3969         if (FAILED(hr))
3970             goto cleanup;
3971     }
3972 
3973     if (!first_frame) {
3974         hr = E_FAIL;
3975     } else if (first_frame->pFrameSibling) {
3976         D3DXFRAME *root_frame = NULL;
3977         hr = alloc_hier->lpVtbl->CreateFrame(alloc_hier, NULL, &root_frame);
3978         if (FAILED(hr)) {
3979             hr = E_FAIL;
3980             goto cleanup;
3981         }
3982         D3DXMatrixIdentity(&root_frame->TransformationMatrix);
3983         root_frame->pFrameFirstChild = first_frame;
3984         *frame_hierarchy = root_frame;
3985         hr = D3D_OK;
3986     } else {
3987         *frame_hierarchy = first_frame;
3988         hr = D3D_OK;
3989     }
3990 
3991     if (anim_controller)
3992     {
3993         *anim_controller = NULL;
3994         FIXME("Animation controller creation not implemented.\n");
3995     }
3996 
3997 cleanup:
3998     if (FAILED(hr) && first_frame) D3DXFrameDestroy(first_frame, alloc_hier);
3999     if (filedata) filedata->lpVtbl->Release(filedata);
4000     if (enumobj) enumobj->lpVtbl->Release(enumobj);
4001     if (d3dxfile) d3dxfile->lpVtbl->Release(d3dxfile);
4002     return hr;
4003 }
4004 
4005 HRESULT WINAPI D3DXCleanMesh(D3DXCLEANTYPE clean_type, ID3DXMesh *mesh_in, const DWORD *adjacency_in,
4006         ID3DXMesh **mesh_out, DWORD *adjacency_out, ID3DXBuffer **errors_and_warnings)
4007 {
4008     FIXME("(%u, %p, %p, %p, %p, %p)\n", clean_type, mesh_in, adjacency_in, mesh_out, adjacency_out, errors_and_warnings);
4009 
4010     return E_NOTIMPL;
4011 }
4012 
4013 HRESULT WINAPI D3DXFrameDestroy(D3DXFRAME *frame, ID3DXAllocateHierarchy *alloc_hier)
4014 {
4015     HRESULT hr;
4016     BOOL last = FALSE;
4017 
4018     TRACE("(%p, %p)\n", frame, alloc_hier);
4019 
4020     if (!frame || !alloc_hier)
4021         return D3DERR_INVALIDCALL;
4022 
4023     while (!last) {
4024         D3DXMESHCONTAINER *container;
4025         D3DXFRAME *current_frame;
4026 
4027         if (frame->pFrameSibling) {
4028             current_frame = frame->pFrameSibling;
4029             frame->pFrameSibling = current_frame->pFrameSibling;
4030             current_frame->pFrameSibling = NULL;
4031         } else {
4032             current_frame = frame;
4033             last = TRUE;
4034         }
4035 
4036         if (current_frame->pFrameFirstChild) {
4037             hr = D3DXFrameDestroy(current_frame->pFrameFirstChild, alloc_hier);
4038             if (FAILED(hr)) return hr;
4039             current_frame->pFrameFirstChild = NULL;
4040         }
4041 
4042         container = current_frame->pMeshContainer;
4043         while (container) {
4044             D3DXMESHCONTAINER *next_container = container->pNextMeshContainer;
4045             hr = alloc_hier->lpVtbl->DestroyMeshContainer(alloc_hier, container);
4046             if (FAILED(hr)) return hr;
4047             container = next_container;
4048         }
4049         hr = alloc_hier->lpVtbl->DestroyFrame(alloc_hier, current_frame);
4050         if (FAILED(hr)) return hr;
4051     }
4052     return D3D_OK;
4053 }
4054 
4055 HRESULT WINAPI D3DXLoadMeshFromXA(const char *filename, DWORD options, struct IDirect3DDevice9 *device,
4056         struct ID3DXBuffer **adjacency, struct ID3DXBuffer **materials, struct ID3DXBuffer **effect_instances,
4057         DWORD *num_materials, struct ID3DXMesh **mesh)
4058 {
4059     WCHAR *filenameW;
4060     HRESULT hr;
4061     int len;
4062 
4063     TRACE("filename %s, options %#x, device %p, adjacency %p, materials %p, "
4064             "effect_instances %p, num_materials %p, mesh %p.\n",
4065             debugstr_a(filename), options, device, adjacency, materials,
4066             effect_instances, num_materials, mesh);
4067 
4068     if (!filename)
4069         return D3DERR_INVALIDCALL;
4070 
4071     len = MultiByteToWideChar(CP_ACP, 0, filename, -1, NULL, 0);
4072     filenameW = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
4073     if (!filenameW) return E_OUTOFMEMORY;
4074     MultiByteToWideChar(CP_ACP, 0, filename, -1, filenameW, len);
4075 
4076     hr = D3DXLoadMeshFromXW(filenameW, options, device, adjacency, materials,
4077                             effect_instances, num_materials, mesh);
4078     HeapFree(GetProcessHeap(), 0, filenameW);
4079 
4080     return hr;
4081 }
4082 
4083 HRESULT WINAPI D3DXLoadMeshFromXW(const WCHAR *filename, DWORD options, struct IDirect3DDevice9 *device,
4084         struct ID3DXBuffer **adjacency, struct ID3DXBuffer **materials, struct ID3DXBuffer **effect_instances,
4085         DWORD *num_materials, struct ID3DXMesh **mesh)
4086 {
4087     void *buffer;
4088     HRESULT hr;
4089     DWORD size;
4090 
4091     TRACE("filename %s, options %#x, device %p, adjacency %p, materials %p, "
4092             "effect_instances %p, num_materials %p, mesh %p.\n",
4093             debugstr_w(filename), options, device, adjacency, materials,
4094             effect_instances, num_materials, mesh);
4095 
4096     if (!filename)
4097         return D3DERR_INVALIDCALL;
4098 
4099     hr = map_view_of_file(filename, &buffer, &size);
4100     if (FAILED(hr))
4101         return D3DXERR_INVALIDDATA;
4102 
4103     hr = D3DXLoadMeshFromXInMemory(buffer, size, options, device, adjacency,
4104             materials, effect_instances, num_materials, mesh);
4105 
4106     UnmapViewOfFile(buffer);
4107 
4108     return hr;
4109 }
4110 
4111 HRESULT WINAPI D3DXLoadMeshFromXResource(HMODULE module, const char *name, const char *type, DWORD options,
4112         struct IDirect3DDevice9 *device, struct ID3DXBuffer **adjacency, struct ID3DXBuffer **materials,
4113         struct ID3DXBuffer **effect_instances, DWORD *num_materials, struct ID3DXMesh **mesh)
4114 {
4115     HRESULT hr;
4116     HRSRC resinfo;
4117     void *buffer;
4118     DWORD size;
4119 
4120     TRACE("module %p, name %s, type %s, options %#x, device %p, adjacency %p, "
4121             "materials %p, effect_instances %p, num_materials %p, mesh %p.\n",
4122             module, debugstr_a(name), debugstr_a(type), options, device, adjacency,
4123             materials, effect_instances, num_materials, mesh);
4124 
4125     resinfo = FindResourceA(module, name, type);
4126     if (!resinfo) return D3DXERR_INVALIDDATA;
4127 
4128     hr = load_resource_into_memory(module, resinfo, &buffer, &size);
4129     if (FAILED(hr)) return D3DXERR_INVALIDDATA;
4130 
4131     return D3DXLoadMeshFromXInMemory(buffer, size, options, device, adjacency,
4132             materials, effect_instances, num_materials, mesh);
4133 }
4134 
4135 struct mesh_container
4136 {
4137     struct list entry;
4138     ID3DXMesh *mesh;
4139     ID3DXBuffer *adjacency;
4140     ID3DXBuffer *materials;
4141     ID3DXBuffer *effects;
4142     DWORD num_materials;
4143     D3DXMATRIX transform;
4144 };
4145 
4146 static HRESULT parse_frame(struct ID3DXFileData *filedata, DWORD options, struct IDirect3DDevice9 *device,
4147         const D3DXMATRIX *parent_transform, struct list *container_list, DWORD provide_flags)
4148 {
4149     HRESULT hr;
4150     D3DXMATRIX transform = *parent_transform;
4151     ID3DXFileData *child;
4152     GUID type;
4153     SIZE_T i, nb_children;
4154 
4155     hr = filedata->lpVtbl->GetChildren(filedata, &nb_children);
4156     if (FAILED(hr))
4157         return hr;
4158 
4159     for (i = 0; i < nb_children; i++)
4160     {
4161         hr = filedata->lpVtbl->GetChild(filedata, i, &child);
4162         if (FAILED(hr))
4163             return hr;
4164         hr = child->lpVtbl->GetType(child, &type);
4165         if (FAILED(hr))
4166             goto err;
4167 
4168         if (IsEqualGUID(&type, &TID_D3DRMMesh)) {
4169             struct mesh_container *container = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(*container));
4170             if (!container)
4171             {
4172                 hr = E_OUTOFMEMORY;
4173                 goto err;
4174             }
4175             list_add_tail(container_list, &container->entry);
4176             container->transform = transform;
4177 
4178             hr = D3DXLoadSkinMeshFromXof(child, options, device,
4179                     (provide_flags & PROVIDE_ADJACENCY) ? &container->adjacency : NULL,
4180                     (provide_flags & PROVIDE_MATERIALS) ? &container->materials : NULL,
4181                     NULL, &container->num_materials, NULL, &container->mesh);
4182         } else if (IsEqualGUID(&type, &TID_D3DRMFrameTransformMatrix)) {
4183             D3DXMATRIX new_transform;
4184             hr = parse_transform_matrix(child, &new_transform);
4185             D3DXMatrixMultiply(&transform, &transform, &new_transform);
4186         } else if (IsEqualGUID(&type, &TID_D3DRMFrame)) {
4187             hr = parse_frame(child, options, device, &transform, container_list, provide_flags);
4188         }
4189         if (FAILED(hr))
4190             goto err;
4191 
4192         IUnknown_Release(child);
4193     }
4194     return D3D_OK;
4195 
4196 err:
4197     IUnknown_Release(child);
4198     return hr;
4199 }
4200 
4201 HRESULT WINAPI D3DXLoadMeshFromXInMemory(const void *memory, DWORD memory_size, DWORD options,
4202         struct IDirect3DDevice9 *device, struct ID3DXBuffer **adjacency_out, struct ID3DXBuffer **materials_out,
4203         struct ID3DXBuffer **effects_out, DWORD *num_materials_out, struct ID3DXMesh **mesh_out)
4204 {
4205     HRESULT hr;
4206     ID3DXFile *d3dxfile = NULL;
4207     ID3DXFileEnumObject *enumobj = NULL;
4208     ID3DXFileData *filedata = NULL;
4209     D3DXF_FILELOADMEMORY source;
4210     ID3DXBuffer *materials = NULL;
4211     ID3DXBuffer *effects = NULL;
4212     ID3DXBuffer *adjacency = NULL;
4213     struct list container_list = LIST_INIT(container_list);
4214     struct mesh_container *container_ptr, *next_container_ptr;
4215     DWORD num_materials;
4216     DWORD num_faces, num_vertices;
4217     D3DXMATRIX identity;
4218     DWORD provide_flags = 0;
4219     DWORD fvf;
4220     ID3DXMesh *concat_mesh = NULL;
4221     D3DVERTEXELEMENT9 concat_decl[MAX_FVF_DECL_SIZE];
4222     BYTE *concat_vertices = NULL;
4223     void *concat_indices = NULL;
4224     DWORD index_offset;
4225     DWORD concat_vertex_size;
4226     SIZE_T i, nb_children;
4227     GUID guid;
4228 
4229     TRACE("(%p, %u, %x, %p, %p, %p, %p, %p, %p)\n", memory, memory_size, options,
4230           device, adjacency_out, materials_out, effects_out, num_materials_out, mesh_out);
4231 
4232     if (!memory || !memory_size || !device || !mesh_out)
4233         return D3DERR_INVALIDCALL;
4234 
4235     hr = D3DXFileCreate(&d3dxfile);
4236     if (FAILED(hr)) goto cleanup;
4237 
4238     hr = d3dxfile->lpVtbl->RegisterTemplates(d3dxfile, D3DRM_XTEMPLATES, D3DRM_XTEMPLATE_BYTES);
4239     if (FAILED(hr)) goto cleanup;
4240 
4241     source.lpMemory = (void*)memory;
4242     source.dSize = memory_size;
4243     hr = d3dxfile->lpVtbl->CreateEnumObject(d3dxfile, &source, D3DXF_FILELOAD_FROMMEMORY, &enumobj);
4244     if (FAILED(hr)) goto cleanup;
4245 
4246     D3DXMatrixIdentity(&identity);
4247     if (adjacency_out) provide_flags |= PROVIDE_ADJACENCY;
4248     if (materials_out || effects_out) provide_flags |= PROVIDE_MATERIALS;
4249 
4250     hr = enumobj->lpVtbl->GetChildren(enumobj, &nb_children);
4251     if (FAILED(hr))
4252         goto cleanup;
4253 
4254     for (i = 0; i < nb_children; i++)
4255     {
4256         hr = enumobj->lpVtbl->GetChild(enumobj, i, &filedata);
4257         if (FAILED(hr))
4258             goto cleanup;
4259 
4260         hr = filedata->lpVtbl->GetType(filedata, &guid);
4261         if (SUCCEEDED(hr)) {
4262             if (IsEqualGUID(&guid, &TID_D3DRMMesh)) {
4263                 container_ptr = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(*container_ptr));
4264                 if (!container_ptr) {
4265                     hr = E_OUTOFMEMORY;
4266                     goto cleanup;
4267                 }
4268                 list_add_tail(&container_list, &container_ptr->entry);
4269                 D3DXMatrixIdentity(&container_ptr->transform);
4270 
4271                 hr = D3DXLoadSkinMeshFromXof(filedata, options, device,
4272                         (provide_flags & PROVIDE_ADJACENCY) ? &container_ptr->adjacency : NULL,
4273                         (provide_flags & PROVIDE_MATERIALS) ? &container_ptr->materials : NULL,
4274                         NULL, &container_ptr->num_materials, NULL, &container_ptr->mesh);
4275             } else if (IsEqualGUID(&guid, &TID_D3DRMFrame)) {
4276                 hr = parse_frame(filedata, options, device, &identity, &container_list, provide_flags);
4277             }
4278             if (FAILED(hr)) goto cleanup;
4279         }
4280         filedata->lpVtbl->Release(filedata);
4281         filedata = NULL;
4282         if (FAILED(hr))
4283             goto cleanup;
4284     }
4285 
4286     enumobj->lpVtbl->Release(enumobj);
4287     enumobj = NULL;
4288     d3dxfile->lpVtbl->Release(d3dxfile);
4289     d3dxfile = NULL;
4290 
4291     if (list_empty(&container_list)) {
4292         hr = E_FAIL;
4293         goto cleanup;
4294     }
4295 
4296     fvf = D3DFVF_XYZ;
4297     num_faces = 0;
4298     num_vertices = 0;
4299     num_materials = 0;
4300     LIST_FOR_EACH_ENTRY(container_ptr, &container_list, struct mesh_container, entry)
4301     {
4302         ID3DXMesh *mesh = container_ptr->mesh;
4303         fvf |= mesh->lpVtbl->GetFVF(mesh);
4304         num_faces += mesh->lpVtbl->GetNumFaces(mesh);
4305         num_vertices += mesh->lpVtbl->GetNumVertices(mesh);
4306         num_materials += container_ptr->num_materials;
4307     }
4308 
4309     hr = D3DXCreateMeshFVF(num_faces, num_vertices, options, fvf, device, &concat_mesh);
4310     if (FAILED(hr)) goto cleanup;
4311 
4312     hr = concat_mesh->lpVtbl->GetDeclaration(concat_mesh, concat_decl);
4313     if (FAILED(hr)) goto cleanup;
4314 
4315     concat_vertex_size = D3DXGetDeclVertexSize(concat_decl, 0);
4316 
4317     hr = concat_mesh->lpVtbl->LockVertexBuffer(concat_mesh, 0, (void**)&concat_vertices);
4318     if (FAILED(hr)) goto cleanup;
4319 
4320     LIST_FOR_EACH_ENTRY(container_ptr, &container_list, struct mesh_container, entry)
4321     {
4322         D3DVERTEXELEMENT9 mesh_decl[MAX_FVF_DECL_SIZE];
4323         ID3DXMesh *mesh = container_ptr->mesh;
4324         DWORD num_mesh_vertices = mesh->lpVtbl->GetNumVertices(mesh);
4325         DWORD mesh_vertex_size;
4326         const BYTE *mesh_vertices;
4327         DWORD i;
4328 
4329         hr = mesh->lpVtbl->GetDeclaration(mesh, mesh_decl);
4330         if (FAILED(hr)) goto cleanup;
4331 
4332         mesh_vertex_size = D3DXGetDeclVertexSize(mesh_decl, 0);
4333 
4334         hr = mesh->lpVtbl->LockVertexBuffer(mesh, D3DLOCK_READONLY, (void**)&mesh_vertices);
4335         if (FAILED(hr)) goto cleanup;
4336 
4337         for (i = 0; i < num_mesh_vertices; i++) {
4338             int j;
4339             int k = 1;
4340 
4341             D3DXVec3TransformCoord((D3DXVECTOR3*)concat_vertices,
4342                                    (D3DXVECTOR3*)mesh_vertices,
4343                                    &container_ptr->transform);
4344             for (j = 1; concat_decl[j].Stream != 0xff; j++)
4345             {
4346                 if (concat_decl[j].Usage == mesh_decl[k].Usage &&
4347                     concat_decl[j].UsageIndex == mesh_decl[k].UsageIndex)
4348                 {
4349                     if (concat_decl[j].Usage == D3DDECLUSAGE_NORMAL) {
4350                         D3DXVec3TransformCoord((D3DXVECTOR3*)(concat_vertices + concat_decl[j].Offset),
4351                                                (D3DXVECTOR3*)(mesh_vertices + mesh_decl[k].Offset),
4352                                                &container_ptr->transform);
4353                     } else {
4354                         memcpy(concat_vertices + concat_decl[j].Offset,
4355                                mesh_vertices + mesh_decl[k].Offset,
4356                                d3dx_decltype_size[mesh_decl[k].Type]);
4357                     }
4358                     k++;
4359                 }
4360             }
4361             mesh_vertices += mesh_vertex_size;
4362             concat_vertices += concat_vertex_size;
4363         }
4364 
4365         mesh->lpVtbl->UnlockVertexBuffer(mesh);
4366     }
4367 
4368     concat_mesh->lpVtbl->UnlockVertexBuffer(concat_mesh);
4369     concat_vertices = NULL;
4370 
4371     hr = concat_mesh->lpVtbl->LockIndexBuffer(concat_mesh, 0, &concat_indices);
4372     if (FAILED(hr)) goto cleanup;
4373 
4374     index_offset = 0;
4375     LIST_FOR_EACH_ENTRY(container_ptr, &container_list, struct mesh_container, entry)
4376     {
4377         ID3DXMesh *mesh = container_ptr->mesh;
4378         const void *mesh_indices;
4379         DWORD num_mesh_faces = mesh->lpVtbl->GetNumFaces(mesh);
4380         DWORD i;
4381 
4382         hr = mesh->lpVtbl->LockIndexBuffer(mesh, D3DLOCK_READONLY, (void**)&mesh_indices);
4383         if (FAILED(hr)) goto cleanup;
4384 
4385         if (options & D3DXMESH_32BIT) {
4386             DWORD *dest = concat_indices;
4387             const DWORD *src = mesh_indices;
4388             for (i = 0; i < num_mesh_faces * 3; i++)
4389                 *dest++ = index_offset + *src++;
4390             concat_indices = dest;
4391         } else {
4392             WORD *dest = concat_indices;
4393             const WORD *src = mesh_indices;
4394             for (i = 0; i < num_mesh_faces * 3; i++)
4395                 *dest++ = index_offset + *src++;
4396             concat_indices = dest;
4397         }
4398         mesh->lpVtbl->UnlockIndexBuffer(mesh);
4399 
4400         index_offset += num_mesh_faces * 3;
4401     }
4402 
4403     concat_mesh->lpVtbl->UnlockIndexBuffer(concat_mesh);
4404     concat_indices = NULL;
4405 
4406     if (num_materials) {
4407         DWORD *concat_attrib_buffer = NULL;
4408         DWORD offset = 0;
4409 
4410         hr = concat_mesh->lpVtbl->LockAttributeBuffer(concat_mesh, 0, &concat_attrib_buffer);
4411         if (FAILED(hr)) goto cleanup;
4412 
4413         LIST_FOR_EACH_ENTRY(container_ptr, &container_list, struct mesh_container, entry)
4414         {
4415             ID3DXMesh *mesh = container_ptr->mesh;
4416             const DWORD *mesh_attrib_buffer = NULL;
4417             DWORD count = mesh->lpVtbl->GetNumFaces(mesh);
4418 
4419             hr = mesh->lpVtbl->LockAttributeBuffer(mesh, D3DLOCK_READONLY, (DWORD**)&mesh_attrib_buffer);
4420             if (FAILED(hr)) {
4421                 concat_mesh->lpVtbl->UnlockAttributeBuffer(concat_mesh);
4422                 goto cleanup;
4423             }
4424 
4425             while (count--)
4426                 *concat_attrib_buffer++ = offset + *mesh_attrib_buffer++;
4427 
4428             mesh->lpVtbl->UnlockAttributeBuffer(mesh);
4429             offset += container_ptr->num_materials;
4430         }
4431         concat_mesh->lpVtbl->UnlockAttributeBuffer(concat_mesh);
4432     }
4433 
4434     if (materials_out || effects_out) {
4435         D3DXMATERIAL *out_ptr;
4436         if (!num_materials) {
4437             /* create default material */
4438             hr = D3DXCreateBuffer(sizeof(D3DXMATERIAL), &materials);
4439             if (FAILED(hr)) goto cleanup;
4440 
4441             out_ptr = ID3DXBuffer_GetBufferPointer(materials);
4442             out_ptr->MatD3D.Diffuse.r = 0.5f;
4443             out_ptr->MatD3D.Diffuse.g = 0.5f;
4444             out_ptr->MatD3D.Diffuse.b = 0.5f;
4445             out_ptr->MatD3D.Specular.r = 0.5f;
4446             out_ptr->MatD3D.Specular.g = 0.5f;
4447             out_ptr->MatD3D.Specular.b = 0.5f;
4448             /* D3DXCreateBuffer initializes the rest to zero */
4449         } else {
4450             DWORD buffer_size = num_materials * sizeof(D3DXMATERIAL);
4451             char *strings_out_ptr;
4452 
4453             LIST_FOR_EACH_ENTRY(container_ptr, &container_list, struct mesh_container, entry)
4454             {
4455                 if (container_ptr->materials) {
4456                     DWORD i;
4457                     const D3DXMATERIAL *in_ptr = ID3DXBuffer_GetBufferPointer(container_ptr->materials);
4458                     for (i = 0; i < container_ptr->num_materials; i++)
4459                     {
4460                         if (in_ptr->pTextureFilename)
4461                             buffer_size += strlen(in_ptr->pTextureFilename) + 1;
4462                         in_ptr++;
4463                     }
4464                 }
4465             }
4466 
4467             hr = D3DXCreateBuffer(buffer_size, &materials);
4468             if (FAILED(hr)) goto cleanup;
4469             out_ptr = ID3DXBuffer_GetBufferPointer(materials);
4470             strings_out_ptr = (char*)(out_ptr + num_materials);
4471 
4472             LIST_FOR_EACH_ENTRY(container_ptr, &container_list, struct mesh_container, entry)
4473             {
4474                 if (container_ptr->materials) {
4475                     DWORD i;
4476                     const D3DXMATERIAL *in_ptr = ID3DXBuffer_GetBufferPointer(container_ptr->materials);
4477                     for (i = 0; i < container_ptr->num_materials; i++)
4478                     {
4479                         out_ptr->MatD3D = in_ptr->MatD3D;
4480                         if (in_ptr->pTextureFilename) {
4481                             out_ptr->pTextureFilename = strings_out_ptr;
4482                             strcpy(out_ptr->pTextureFilename, in_ptr->pTextureFilename);
4483                             strings_out_ptr += strlen(in_ptr->pTextureFilename) + 1;
4484                         }
4485                         in_ptr++;
4486                         out_ptr++;
4487                     }
4488                 }
4489             }
4490         }
4491     }
4492     if (!num_materials)
4493         num_materials = 1;
4494 
4495     if (effects_out) {
4496         generate_effects(materials, num_materials, &effects);
4497         if (!materials_out) {
4498             ID3DXBuffer_Release(materials);
4499             materials = NULL;
4500         }
4501     }
4502 
4503     if (adjacency_out) {
4504         if (!list_next(&container_list, list_head(&container_list))) {
4505             container_ptr = LIST_ENTRY(list_head(&container_list), struct mesh_container, entry);
4506             adjacency = container_ptr->adjacency;
4507             container_ptr->adjacency = NULL;
4508         } else {
4509             DWORD offset = 0;
4510             DWORD *out_ptr;
4511 
4512             hr = D3DXCreateBuffer(num_faces * 3 * sizeof(DWORD), &adjacency);
4513             if (FAILED(hr)) goto cleanup;
4514 
4515             out_ptr = ID3DXBuffer_GetBufferPointer(adjacency);
4516             LIST_FOR_EACH_ENTRY(container_ptr, &container_list, struct mesh_container, entry)
4517             {
4518                 DWORD i;
4519                 DWORD count = 3 * container_ptr->mesh->lpVtbl->GetNumFaces(container_ptr->mesh);
4520                 DWORD *in_ptr = ID3DXBuffer_GetBufferPointer(container_ptr->adjacency);
4521 
4522                 for (i = 0; i < count; i++)
4523                     *out_ptr++ = offset + *in_ptr++;
4524 
4525                 offset += count;
4526             }
4527         }
4528     }
4529 
4530     *mesh_out = concat_mesh;
4531     if (adjacency_out) *adjacency_out = adjacency;
4532     if (materials_out) *materials_out = materials;
4533     if (effects_out) *effects_out = effects;
4534     if (num_materials_out) *num_materials_out = num_materials;
4535 
4536     hr = D3D_OK;
4537 cleanup:
4538     if (concat_indices) concat_mesh->lpVtbl->UnlockIndexBuffer(concat_mesh);
4539     if (concat_vertices) concat_mesh->lpVtbl->UnlockVertexBuffer(concat_mesh);
4540     if (filedata) filedata->lpVtbl->Release(filedata);
4541     if (enumobj) enumobj->lpVtbl->Release(enumobj);
4542     if (d3dxfile) d3dxfile->lpVtbl->Release(d3dxfile);
4543     if (FAILED(hr)) {
4544         if (concat_mesh) IUnknown_Release(concat_mesh);
4545         if (materials) ID3DXBuffer_Release(materials);
4546         if (effects) ID3DXBuffer_Release(effects);
4547         if (adjacency) ID3DXBuffer_Release(adjacency);
4548     }
4549     LIST_FOR_EACH_ENTRY_SAFE(container_ptr, next_container_ptr, &container_list, struct mesh_container, entry)
4550     {
4551         if (container_ptr->mesh) IUnknown_Release(container_ptr->mesh);
4552         if (container_ptr->adjacency) ID3DXBuffer_Release(container_ptr->adjacency);
4553         if (container_ptr->materials) ID3DXBuffer_Release(container_ptr->materials);
4554         if (container_ptr->effects) ID3DXBuffer_Release(container_ptr->effects);
4555         HeapFree(GetProcessHeap(), 0, container_ptr);
4556     }
4557     return hr;
4558 }
4559 
4560 struct vertex
4561 {
4562     D3DXVECTOR3 position;
4563     D3DXVECTOR3 normal;
4564 };
4565 
4566 HRESULT WINAPI D3DXCreatePolygon(struct IDirect3DDevice9 *device, float length, UINT sides,
4567         struct ID3DXMesh **mesh, struct ID3DXBuffer **adjacency)
4568 {
4569     HRESULT hr;
4570     ID3DXMesh *polygon;
4571     struct vertex *vertices;
4572     WORD (*faces)[3];
4573     DWORD (*adjacency_buf)[3];
4574     float angle, scale;
4575     unsigned int i;
4576 
4577     TRACE("device %p, length %f, sides %u, mesh %p, adjacency %p.\n",
4578             device, length, sides, mesh, adjacency);
4579 
4580     if (!device || length < 0.0f || sides < 3 || !mesh)
4581         return D3DERR_INVALIDCALL;
4582 
4583     if (FAILED(hr = D3DXCreateMeshFVF(sides, sides + 1, D3DXMESH_MANAGED,
4584             D3DFVF_XYZ | D3DFVF_NORMAL, device, &polygon)))
4585     {
4586         return hr;
4587     }
4588 
4589     if (FAILED(hr = polygon->lpVtbl->LockVertexBuffer(polygon, 0, (void **)&vertices)))
4590     {
4591         polygon->lpVtbl->Release(polygon);
4592         return hr;
4593     }
4594 
4595     if (FAILED(hr = polygon->lpVtbl->LockIndexBuffer(polygon, 0, (void **)&faces)))
4596     {
4597         polygon->lpVtbl->UnlockVertexBuffer(polygon);
4598         polygon->lpVtbl->Release(polygon);
4599         return hr;
4600     }
4601 
4602     angle = D3DX_PI / sides;
4603     scale = 0.5f * length / sinf(angle);
4604     angle *= 2.0f;
4605 
4606     vertices[0].position.x = 0.0f;
4607     vertices[0].position.y = 0.0f;
4608     vertices[0].position.z = 0.0f;
4609     vertices[0].normal.x = 0.0f;
4610     vertices[0].normal.y = 0.0f;
4611     vertices[0].normal.z = 1.0f;
4612 
4613     for (i = 0; i < sides; ++i)
4614     {
4615         vertices[i + 1].position.x = cosf(angle * i) * scale;
4616         vertices[i + 1].position.y = sinf(angle * i) * scale;
4617         vertices[i + 1].position.z = 0.0f;
4618         vertices[i + 1].normal.x = 0.0f;
4619         vertices[i + 1].normal.y = 0.0f;
4620         vertices[i + 1].normal.z = 1.0f;
4621 
4622         faces[i][0] = 0;
4623         faces[i][1] = i + 1;
4624         faces[i][2] = i + 2;
4625     }
4626 
4627     faces[sides - 1][2] = 1;
4628 
4629     polygon->lpVtbl->UnlockVertexBuffer(polygon);
4630     polygon->lpVtbl->UnlockIndexBuffer(polygon);
4631 
4632     if (adjacency)
4633     {
4634         if (FAILED(hr = D3DXCreateBuffer(sides * sizeof(DWORD) * 3, adjacency)))
4635         {
4636             polygon->lpVtbl->Release(polygon);
4637             return hr;
4638         }
4639 
4640         adjacency_buf = ID3DXBuffer_GetBufferPointer(*adjacency);
4641         for (i = 0; i < sides; ++i)
4642         {
4643             adjacency_buf[i][0] = i - 1;
4644             adjacency_buf[i][1] = ~0U;
4645             adjacency_buf[i][2] = i + 1;
4646         }
4647         adjacency_buf[0][0] = sides - 1;
4648         adjacency_buf[sides - 1][2] = 0;
4649     }
4650 
4651     *mesh = polygon;
4652 
4653     return D3D_OK;
4654 }
4655 
4656 HRESULT WINAPI D3DXCreateBox(struct IDirect3DDevice9 *device, float width, float height,
4657         float depth, struct ID3DXMesh **mesh, struct ID3DXBuffer **adjacency)
4658 {
4659     HRESULT hr;
4660     ID3DXMesh *box;
4661     struct vertex *vertices;
4662     WORD (*faces)[3];
4663     DWORD *adjacency_buf;
4664     unsigned int i, face;
4665     static const D3DXVECTOR3 unit_box[] =
4666     {
4667         {-0.5f, -0.5f, -0.5f}, {-0.5f, -0.5f,  0.5f}, {-0.5f,  0.5f,  0.5f}, {-0.5f,  0.5f, -0.5f},
4668         {-0.5f,  0.5f, -0.5f}, {-0.5f,  0.5f,  0.5f}, { 0.5f,  0.5f,  0.5f}, { 0.5f,  0.5f, -0.5f},
4669         { 0.5f,  0.5f, -0.5f}, { 0.5f,  0.5f,  0.5f}, { 0.5f, -0.5f,  0.5f}, { 0.5f, -0.5f, -0.5f},
4670         {-0.5f, -0.5f,  0.5f}, {-0.5f, -0.5f, -0.5f}, { 0.5f, -0.5f, -0.5f}, { 0.5f, -0.5f,  0.5f},
4671         {-0.5f, -0.5f,  0.5f}, { 0.5f, -0.5f,  0.5f}, { 0.5f,  0.5f,  0.5f}, {-0.5f,  0.5f,  0.5f},
4672         {-0.5f, -0.5f, -0.5f}, {-0.5f,  0.5f, -0.5f}, { 0.5f,  0.5f, -0.5f}, { 0.5f, -0.5f, -0.5f}
4673     };
4674     static const D3DXVECTOR3 normals[] =
4675     {
4676         {-1.0f,  0.0f, 0.0f}, { 0.0f, 1.0f, 0.0f}, { 1.0f, 0.0f,  0.0f},
4677         { 0.0f, -1.0f, 0.0f}, { 0.0f, 0.0f, 1.0f}, { 0.0f, 0.0f, -1.0f}
4678     };
4679     static const DWORD adjacency_table[] =
4680     {
4681         6, 9, 1, 2, 10, 0, 1,  9,  3, 4, 10,  2,
4682         3, 8, 5, 7, 11, 4, 0, 11,  7, 5,  8,  6,
4683         7, 4, 9, 2,  0, 8, 1,  3, 11, 5,  6, 10
4684     };
4685 
4686     TRACE("device %p, width %f, height %f, depth %f, mesh %p, adjacency %p\n",
4687                     device, width, height, depth, mesh, adjacency);
4688 
4689     if (!device || width < 0.0f || height < 0.0f || depth < 0.0f || !mesh)
4690     {
4691         return D3DERR_INVALIDCALL;
4692     }
4693 
4694     if (FAILED(hr = D3DXCreateMeshFVF(12, 24, D3DXMESH_MANAGED, D3DFVF_XYZ | D3DFVF_NORMAL, device, &box)))
4695     {
4696         return hr;
4697     }
4698 
4699     if (FAILED(hr = box->lpVtbl->LockVertexBuffer(box, 0, (void **)&vertices)))
4700     {
4701         box->lpVtbl->Release(box);
4702         return hr;
4703     }
4704 
4705     if (FAILED(hr = box->lpVtbl->LockIndexBuffer(box, 0, (void **)&faces)))
4706     {
4707         box->lpVtbl->UnlockVertexBuffer(box);
4708         box->lpVtbl->Release(box);
4709         return hr;
4710     }
4711 
4712     for (i = 0; i < 24; i++)
4713     {
4714         vertices[i].position.x = width * unit_box[i].x;
4715         vertices[i].position.y = height * unit_box[i].y;
4716         vertices[i].position.z = depth * unit_box[i].z;
4717         vertices[i].normal.x = normals[i / 4].x;
4718         vertices[i].normal.y = normals[i / 4].y;
4719         vertices[i].normal.z = normals[i / 4].z;
4720     }
4721 
4722     face = 0;
4723     for (i = 0; i < 12; i++)
4724     {
4725         faces[i][0] = face++;
4726         faces[i][1] = face++;
4727         faces[i][2] = (i % 2) ? face - 4 : face;
4728     }
4729 
4730     box->lpVtbl->UnlockIndexBuffer(box);
4731     box->lpVtbl->UnlockVertexBuffer(box);
4732 
4733     if (adjacency)
4734     {
4735         if (FAILED(hr = D3DXCreateBuffer(sizeof(adjacency_table), adjacency)))
4736         {
4737             box->lpVtbl->Release(box);
4738             return hr;
4739         }
4740 
4741         adjacency_buf = ID3DXBuffer_GetBufferPointer(*adjacency);
4742         memcpy(adjacency_buf, adjacency_table, sizeof(adjacency_table));
4743     }
4744 
4745     *mesh = box;
4746 
4747     return D3D_OK;
4748 }
4749 
4750 typedef WORD face[3];
4751 
4752 struct sincos_table
4753 {
4754     float *sin;
4755     float *cos;
4756 };
4757 
4758 static void free_sincos_table(struct sincos_table *sincos_table)
4759 {
4760     HeapFree(GetProcessHeap(), 0, sincos_table->cos);
4761     HeapFree(GetProcessHeap(), 0, sincos_table->sin);
4762 }
4763 
4764 /* pre compute sine and cosine tables; caller must free */
4765 static BOOL compute_sincos_table(struct sincos_table *sincos_table, float angle_start, float angle_step, int n)
4766 {
4767     float angle;
4768     int i;
4769 
4770     sincos_table->sin = HeapAlloc(GetProcessHeap(), 0, n * sizeof(*sincos_table->sin));
4771     if (!sincos_table->sin)
4772     {
4773         return FALSE;
4774     }
4775     sincos_table->cos = HeapAlloc(GetProcessHeap(), 0, n * sizeof(*sincos_table->cos));
4776     if (!sincos_table->cos)
4777     {
4778         HeapFree(GetProcessHeap(), 0, sincos_table->sin);
4779         return FALSE;
4780     }
4781 
4782     angle = angle_start;
4783     for (i = 0; i < n; i++)
4784     {
4785         sincos_table->sin[i] = sinf(angle);
4786         sincos_table->cos[i] = cosf(angle);
4787         angle += angle_step;
4788     }
4789 
4790     return TRUE;
4791 }
4792 
4793 static WORD vertex_index(UINT slices, int slice, int stack)
4794 {
4795     return stack*slices+slice+1;
4796 }
4797 
4798 HRESULT WINAPI D3DXCreateSphere(struct IDirect3DDevice9 *device, float radius, UINT slices,
4799         UINT stacks, struct ID3DXMesh **mesh, struct ID3DXBuffer **adjacency)
4800 {
4801     DWORD number_of_vertices, number_of_faces;
4802     HRESULT hr;
4803     ID3DXMesh *sphere;
4804     struct vertex *vertices;
4805     face *faces;
4806     float phi_step, phi_start;
4807     struct sincos_table phi;
4808     float theta_step, theta, sin_theta, cos_theta;
4809     DWORD vertex, face, stack, slice;
4810 
4811     TRACE("(%p, %f, %u, %u, %p, %p)\n", device, radius, slices, stacks, mesh, adjacency);
4812 
4813     if (!device || radius < 0.0f || slices < 2 || stacks < 2 || !mesh)
4814     {
4815         return D3DERR_INVALIDCALL;
4816     }
4817 
4818     number_of_vertices = 2 + slices * (stacks-1);
4819     number_of_faces = 2 * slices + (stacks - 2) * (2 * slices);
4820 
4821     hr = D3DXCreateMeshFVF(number_of_faces, number_of_vertices, D3DXMESH_MANAGED,
4822                            D3DFVF_XYZ | D3DFVF_NORMAL, device, &sphere);
4823     if (FAILED(hr))
4824     {
4825         return hr;
4826     }
4827 
4828     if (FAILED(hr = sphere->lpVtbl->LockVertexBuffer(sphere, 0, (void **)&vertices)))
4829     {
4830         sphere->lpVtbl->Release(sphere);
4831         return hr;
4832     }
4833 
4834     if (FAILED(hr = sphere->lpVtbl->LockIndexBuffer(sphere, 0, (void **)&faces)))
4835     {
4836         sphere->lpVtbl->UnlockVertexBuffer(sphere);
4837         sphere->lpVtbl->Release(sphere);
4838         return hr;
4839     }
4840 
4841     /* phi = angle on xz plane wrt z axis */
4842     phi_step = -2.0f * D3DX_PI / slices;
4843     phi_start = D3DX_PI / 2.0f;
4844 
4845     if (!compute_sincos_table(&phi, phi_start, phi_step, slices))
4846     {
4847         sphere->lpVtbl->UnlockIndexBuffer(sphere);
4848         sphere->lpVtbl->UnlockVertexBuffer(sphere);
4849         sphere->lpVtbl->Release(sphere);
4850         return E_OUTOFMEMORY;
4851     }
4852 
4853     /* theta = angle on xy plane wrt x axis */
4854     theta_step = D3DX_PI / stacks;
4855     theta = theta_step;
4856 
4857     vertex = 0;
4858     face = 0;
4859 
4860     vertices[vertex].normal.x = 0.0f;
4861     vertices[vertex].normal.y = 0.0f;
4862     vertices[vertex].normal.z = 1.0f;
4863     vertices[vertex].position.x = 0.0f;
4864     vertices[vertex].position.y = 0.0f;
4865     vertices[vertex].position.z = radius;
4866     vertex++;
4867 
4868     for (stack = 0; stack < stacks - 1; stack++)
4869     {
4870         sin_theta = sinf(theta);
4871         cos_theta = cosf(theta);
4872 
4873         for (slice = 0; slice < slices; slice++)
4874         {
4875             vertices[vertex].normal.x = sin_theta * phi.cos[slice];
4876             vertices[vertex].normal.y = sin_theta * phi.sin[slice];
4877             vertices[vertex].normal.z = cos_theta;
4878             vertices[vertex].position.x = radius * sin_theta * phi.cos[slice];
4879             vertices[vertex].position.y = radius * sin_theta * phi.sin[slice];
4880             vertices[vertex].position.z = radius * cos_theta;
4881             vertex++;
4882 
4883             if (slice > 0)
4884             {
4885                 if (stack == 0)
4886                 {
4887                     /* top stack is triangle fan */
4888                     faces[face][0] = 0;
4889                     faces[face][1] = slice + 1;
4890                     faces[face][2] = slice;
4891                     face++;
4892                 }
4893                 else
4894                 {
4895                     /* stacks in between top and bottom are quad strips */
4896                     faces[face][0] = vertex_index(slices, slice-1, stack-1);
4897                     faces[face][1] = vertex_index(slices, slice, stack-1);
4898                     faces[face][2] = vertex_index(slices, slice-1, stack);
4899                     face++;
4900 
4901                     faces[face][0] = vertex_index(slices, slice, stack-1);
4902                     faces[face][1] = vertex_index(slices, slice, stack);
4903                     faces[face][2] = vertex_index(slices, slice-1, stack);
4904                     face++;
4905                 }
4906             }
4907         }
4908 
4909         theta += theta_step;
4910 
4911         if (stack == 0)
4912         {
4913             faces[face][0] = 0;
4914             faces[face][1] = 1;
4915             faces[face][2] = slice;
4916             face++;
4917         }
4918         else
4919         {
4920             faces[face][0] = vertex_index(slices, slice-1, stack-1);
4921             faces[face][1] = vertex_index(slices, 0, stack-1);
4922             faces[face][2] = vertex_index(slices, slice-1, stack);
4923             face++;
4924 
4925             faces[face][0] = vertex_index(slices, 0, stack-1);
4926             faces[face][1] = vertex_index(slices, 0, stack);
4927             faces[face][2] = vertex_index(slices, slice-1, stack);
4928             face++;
4929         }
4930     }
4931 
4932     vertices[vertex].position.x = 0.0f;
4933     vertices[vertex].position.y = 0.0f;
4934     vertices[vertex].position.z = -radius;
4935     vertices[vertex].normal.x = 0.0f;
4936     vertices[vertex].normal.y = 0.0f;
4937     vertices[vertex].normal.z = -1.0f;
4938 
4939     /* bottom stack is triangle fan */
4940     for (slice = 1; slice < slices; slice++)
4941     {
4942         faces[face][0] = vertex_index(slices, slice-1, stack-1);
4943         faces[face][1] = vertex_index(slices, slice, stack-1);
4944         faces[face][2] = vertex;
4945         face++;
4946     }
4947 
4948     faces[face][0] = vertex_index(slices, slice-1, stack-1);
4949     faces[face][1] = vertex_index(slices, 0, stack-1);
4950     faces[face][2] = vertex;
4951 
4952     free_sincos_table(&phi);
4953     sphere->lpVtbl->UnlockIndexBuffer(sphere);
4954     sphere->lpVtbl->UnlockVertexBuffer(sphere);
4955 
4956 
4957     if (adjacency)
4958     {
4959         if (FAILED(hr = D3DXCreateBuffer(number_of_faces * sizeof(DWORD) * 3, adjacency)))
4960         {
4961             sphere->lpVtbl->Release(sphere);
4962             return hr;
4963         }
4964 
4965         if (FAILED(hr = sphere->lpVtbl->GenerateAdjacency(sphere, 0.0f, (*adjacency)->lpVtbl->GetBufferPointer(*adjacency))))
4966         {
4967             (*adjacency)->lpVtbl->Release(*adjacency);
4968             sphere->lpVtbl->Release(sphere);
4969             return hr;
4970         }
4971     }
4972 
4973     *mesh = sphere;
4974 
4975     return D3D_OK;
4976 }
4977 
4978 HRESULT WINAPI D3DXCreateCylinder(struct IDirect3DDevice9 *device, float radius1, float radius2,
4979         float length, UINT slices, UINT stacks, struct ID3DXMesh **mesh, struct ID3DXBuffer **adjacency)
4980 {
4981     DWORD number_of_vertices, number_of_faces;
4982     HRESULT hr;
4983     ID3DXMesh *cylinder;
4984     struct vertex *vertices;
4985     face *faces;
4986     float theta_step, theta_start;
4987     struct sincos_table theta;
4988     float delta_radius, radius, radius_step;
4989     float z, z_step, z_normal;
4990     DWORD vertex, face, slice, stack;
4991 
4992     TRACE("(%p, %f, %f, %f, %u, %u, %p, %p)\n", device, radius1, radius2, length, slices, stacks, mesh, adjacency);
4993 
4994     if (device == NULL || radius1 < 0.0f || radius2 < 0.0f || length < 0.0f || slices < 2 || stacks < 1 || mesh == NULL)
4995     {
4996         return D3DERR_INVALIDCALL;
4997     }
4998 
4999     number_of_vertices = 2 + (slices * (3 + stacks));
5000     number_of_faces = 2 * slices + stacks * (2 * slices);
5001 
5002     hr = D3DXCreateMeshFVF(number_of_faces, number_of_vertices, D3DXMESH_MANAGED,
5003                            D3DFVF_XYZ | D3DFVF_NORMAL, device, &cylinder);
5004     if (FAILED(hr))
5005     {
5006         return hr;
5007     }
5008 
5009     if (FAILED(hr = cylinder->lpVtbl->LockVertexBuffer(cylinder, 0, (void **)&vertices)))
5010     {
5011         cylinder->lpVtbl->Release(cylinder);
5012         return hr;
5013     }
5014 
5015     if (FAILED(hr = cylinder->lpVtbl->LockIndexBuffer(cylinder, 0, (void **)&faces)))
5016     {
5017         cylinder->lpVtbl->UnlockVertexBuffer(cylinder);
5018         cylinder->lpVtbl->Release(cylinder);
5019         return hr;
5020     }
5021 
5022     /* theta = angle on xy plane wrt x axis */
5023     theta_step = -2.0f * D3DX_PI / slices;
5024     theta_start = D3DX_PI / 2.0f;
5025 
5026     if (!compute_sincos_table(&theta, theta_start, theta_step, slices))
5027     {
5028         cylinder->lpVtbl->UnlockIndexBuffer(cylinder);
5029         cylinder->lpVtbl->UnlockVertexBuffer(cylinder);
5030         cylinder->lpVtbl->Release(cylinder);
5031         return E_OUTOFMEMORY;
5032     }
5033 
5034     vertex = 0;
5035     face = 0;
5036 
5037     delta_radius = radius1 - radius2;
5038     radius = radius1;
5039     radius_step = delta_radius / stacks;
5040 
5041     z = -length / 2;
5042     z_step = length / stacks;
5043     z_normal = delta_radius / length;
5044     if (isnan(z_normal))
5045     {
5046         z_normal = 0.0f;
5047     }
5048 
5049     vertices[vertex].normal.x = 0.0f;
5050     vertices[vertex].normal.y = 0.0f;
5051     vertices[vertex].normal.z = -1.0f;
5052     vertices[vertex].position.x = 0.0f;
5053     vertices[vertex].position.y = 0.0f;
5054     vertices[vertex++].position.z = z;
5055 
5056     for (slice = 0; slice < slices; slice++, vertex++)
5057     {
5058         vertices[vertex].normal.x = 0.0f;
5059         vertices[vertex].normal.y = 0.0f;
5060         vertices[vertex].normal.z = -1.0f;
5061         vertices[vertex].position.x = radius * theta.cos[slice];
5062         vertices[vertex].position.y = radius * theta.sin[slice];
5063         vertices[vertex].position.z = z;
5064 
5065         if (slice > 0)
5066         {
5067             faces[face][0] = 0;
5068             faces[face][1] = slice;
5069             faces[face++][2] = slice + 1;
5070         }
5071     }
5072 
5073     faces[face][0] = 0;
5074     faces[face][1] = slice;
5075     faces[face++][2] = 1;
5076 
5077     for (stack = 1; stack <= stacks+1; stack++)
5078     {
5079         for (slice = 0; slice < slices; slice++, vertex++)
5080         {
5081             vertices[vertex].normal.x = theta.cos[slice];
5082             vertices[vertex].normal.y = theta.sin[slice];
5083             vertices[vertex].normal.z = z_normal;
5084             D3DXVec3Normalize(&vertices[vertex].normal, &vertices[vertex].normal);
5085             vertices[vertex].position.x = radius * theta.cos[slice];
5086             vertices[vertex].position.y = radius * theta.sin[slice];
5087             vertices[vertex].position.z = z;
5088 
5089             if (stack > 1 && slice > 0)
5090             {
5091                 faces[face][0] = vertex_index(slices, slice-1, stack-1);
5092                 faces[face][1] = vertex_index(slices, slice-1, stack);
5093                 faces[face++][2] = vertex_index(slices, slice, stack-1);
5094 
5095                 faces[face][0] = vertex_index(slices, slice, stack-1);
5096                 faces[face][1] = vertex_index(slices, slice-1, stack);
5097                 faces[face++][2] = vertex_index(slices, slice, stack);
5098             }
5099         }
5100 
5101         if (stack > 1)
5102         {
5103             faces[face][0] = vertex_index(slices, slice-1, stack-1);
5104             faces[face][1] = vertex_index(slices, slice-1, stack);
5105             faces[face++][2] = vertex_index(slices, 0, stack-1);
5106 
5107             faces[face][0] = vertex_index(slices, 0, stack-1);
5108             faces[face][1] = vertex_index(slices, slice-1, stack);
5109             faces[face++][2] = vertex_index(slices, 0, stack);
5110         }
5111 
5112         if (stack < stacks + 1)
5113         {
5114             z += z_step;
5115             radius -= radius_step;
5116         }
5117     }
5118 
5119     for (slice = 0; slice < slices; slice++, vertex++)
5120     {
5121         vertices[vertex].normal.x = 0.0f;
5122         vertices[vertex].normal.y = 0.0f;
5123         vertices[vertex].normal.z = 1.0f;
5124         vertices[vertex].position.x = radius * theta.cos[slice];
5125         vertices[vertex].position.y = radius * theta.sin[slice];
5126         vertices[vertex].position.z = z;
5127 
5128         if (slice > 0)
5129         {
5130             faces[face][0] = vertex_index(slices, slice-1, stack);
5131             faces[face][1] = number_of_vertices - 1;
5132             faces[face++][2] = vertex_index(slices, slice, stack);
5133         }
5134     }
5135 
5136     vertices[vertex].position.x = 0.0f;
5137     vertices[vertex].position.y = 0.0f;
5138     vertices[vertex].position.z = z;
5139     vertices[vertex].normal.x = 0.0f;
5140     vertices[vertex].normal.y = 0.0f;
5141     vertices[vertex].normal.z = 1.0f;
5142 
5143     faces[face][0] = vertex_index(slices, slice-1, stack);
5144     faces[face][1] = number_of_vertices - 1;
5145     faces[face][2] = vertex_index(slices, 0, stack);
5146 
5147     free_sincos_table(&theta);
5148     cylinder->lpVtbl->UnlockIndexBuffer(cylinder);
5149     cylinder->lpVtbl->UnlockVertexBuffer(cylinder);
5150 
5151     if (adjacency)
5152     {
5153         if (FAILED(hr = D3DXCreateBuffer(number_of_faces * sizeof(DWORD) * 3, adjacency)))
5154         {
5155             cylinder->lpVtbl->Release(cylinder);
5156             return hr;
5157         }
5158 
5159         if (FAILED(hr = cylinder->lpVtbl->GenerateAdjacency(cylinder, 0.0f, (*adjacency)->lpVtbl->GetBufferPointer(*adjacency))))
5160         {
5161             (*adjacency)->lpVtbl->Release(*adjacency);
5162             cylinder->lpVtbl->Release(cylinder);
5163             return hr;
5164         }
5165     }
5166 
5167     *mesh = cylinder;
5168 
5169     return D3D_OK;
5170 }
5171 
5172 HRESULT WINAPI D3DXCreateTeapot(struct IDirect3DDevice9 *device,
5173         struct ID3DXMesh **mesh, struct ID3DXBuffer **adjacency)
5174 {
5175     FIXME("device %p, mesh %p, adjacency %p semi-stub.\n", device, mesh, adjacency);
5176 
5177     return D3DXCreateSphere(device, 1.0f, 4, 4, mesh, adjacency);
5178 }
5179 
5180 HRESULT WINAPI D3DXCreateTextA(struct IDirect3DDevice9 *device, HDC hdc, const char *text, float deviation,
5181         float extrusion, struct ID3DXMesh **mesh, struct ID3DXBuffer **adjacency, GLYPHMETRICSFLOAT *glyphmetrics)
5182 {
5183     WCHAR *textW;
5184     HRESULT hr;
5185     int len;
5186 
5187     TRACE("device %p, hdc %p, text %s, deviation %.8e, extrusion %.8e, mesh %p, adjacency %p, glyphmetrics %p.\n",
5188             device, hdc, debugstr_a(text), deviation, extrusion, mesh, adjacency, glyphmetrics);
5189 
5190     if (!text)
5191         return D3DERR_INVALIDCALL;
5192 
5193     len = MultiByteToWideChar(CP_ACP, 0, text, -1, NULL, 0);
5194     textW = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
5195     MultiByteToWideChar(CP_ACP, 0, text, -1, textW, len);
5196 
5197     hr = D3DXCreateTextW(device, hdc, textW, deviation, extrusion,
5198                          mesh, adjacency, glyphmetrics);
5199     HeapFree(GetProcessHeap(), 0, textW);
5200 
5201     return hr;
5202 }
5203 
5204 HRESULT WINAPI D3DXCreateTorus(struct IDirect3DDevice9 *device,
5205         float innerradius, float outerradius, UINT sides, UINT rings, struct ID3DXMesh **mesh, ID3DXBuffer **adjacency)
5206 {
5207     HRESULT hr;
5208     ID3DXMesh *torus;
5209     WORD (*faces)[3];
5210     struct vertex *vertices;
5211     float phi, phi_step, sin_phi, cos_phi;
5212     float theta, theta_step, sin_theta, cos_theta;
5213     unsigned int i, j, numvert, numfaces;
5214 
5215     TRACE("device %p, innerradius %.8e, outerradius %.8e, sides %u, rings %u, mesh %p, adjacency %p.\n",
5216             device, innerradius, outerradius, sides, rings, mesh, adjacency);
5217 
5218     numvert = sides * rings;
5219     numfaces = numvert * 2;
5220 
5221     if (!device || innerradius < 0.0f || outerradius < 0.0f || sides < 3 || rings < 3 || !mesh)
5222     {
5223         WARN("Invalid arguments.\n");
5224         return D3DERR_INVALIDCALL;
5225     }
5226 
5227     if (FAILED(hr = D3DXCreateMeshFVF(numfaces, numvert, D3DXMESH_MANAGED, D3DFVF_XYZ | D3DFVF_NORMAL, device, &torus)))
5228         return hr;
5229 
5230     if (FAILED(hr = torus->lpVtbl->LockVertexBuffer(torus, 0, (void **)&vertices)))
5231     {
5232         torus->lpVtbl->Release(torus);
5233         return hr;
5234     }
5235 
5236     if (FAILED(hr = torus->lpVtbl->LockIndexBuffer(torus, 0, (void **)&faces)))
5237     {
5238         torus->lpVtbl->UnlockVertexBuffer(torus);
5239         torus->lpVtbl->Release(torus);
5240         return hr;
5241     }
5242 
5243     phi_step = D3DX_PI / sides * 2.0f;
5244     theta_step = D3DX_PI / rings * -2.0f;
5245 
5246     theta = 0.0f;
5247 
5248     for (i = 0; i < rings; ++i)
5249     {
5250         phi = 0.0f;
5251 
5252         sin_theta = sinf(theta);
5253         cos_theta = cosf(theta);
5254 
5255         for (j = 0; j < sides; ++j)
5256         {
5257             sin_phi = sinf(phi);
5258             cos_phi = cosf(phi);
5259 
5260             vertices[i * sides + j].position.x = (innerradius * cos_phi + outerradius) * cos_theta;
5261             vertices[i * sides + j].position.y = (innerradius * cos_phi + outerradius) * sin_theta;
5262             vertices[i * sides + j].position.z = innerradius * sin_phi;
5263             vertices[i * sides + j].normal.x = cos_phi * cos_theta;
5264             vertices[i * sides + j].normal.y = cos_phi * sin_theta;
5265             vertices[i * sides + j].normal.z = sin_phi;
5266 
5267             phi += phi_step;
5268         }
5269 
5270         theta += theta_step;
5271     }
5272 
5273     for (i = 0; i < numfaces - sides * 2; ++i)
5274     {
5275         faces[i][0] = i % 2 ? i / 2 + sides : i / 2;
5276         faces[i][1] = (i / 2 + 1) % sides ? i / 2 + 1 : i / 2 + 1 - sides;
5277         faces[i][2] = (i + 1) % (sides * 2) ? (i + 1) / 2 + sides : (i + 1) / 2;
5278     }
5279 
5280     for (j = 0; i < numfaces; ++i, ++j)
5281     {
5282         faces[i][0] = i % 2 ? j / 2 : i / 2;
5283         faces[i][1] = (i / 2 + 1) % sides ? i / 2 + 1 : i / 2 + 1 - sides;
5284         faces[i][2] = i == numfaces - 1 ? 0 : (j + 1) / 2;
5285     }
5286 
5287     torus->lpVtbl->UnlockIndexBuffer(torus);
5288     torus->lpVtbl->UnlockVertexBuffer(torus);
5289 
5290     if (adjacency)
5291     {
5292         if (FAILED(hr = D3DXCreateBuffer(numfaces * sizeof(DWORD) * 3, adjacency)))
5293         {
5294             torus->lpVtbl->Release(torus);
5295             return hr;
5296         }
5297 
5298         if (FAILED(hr = torus->lpVtbl->GenerateAdjacency(torus, 0.0f, (*adjacency)->lpVtbl->GetBufferPointer(*adjacency))))
5299         {
5300             (*adjacency)->lpVtbl->Release(*adjacency);
5301             torus->lpVtbl->Release(torus);
5302             return hr;
5303         }
5304     }
5305 
5306     *mesh = torus;
5307 
5308     return D3D_OK;
5309 }
5310 
5311 enum pointtype {
5312     POINTTYPE_CURVE = 0,
5313     POINTTYPE_CORNER,
5314     POINTTYPE_CURVE_START,
5315     POINTTYPE_CURVE_END,
5316     POINTTYPE_CURVE_MIDDLE,
5317 };
5318 
5319 struct point2d
5320 {
5321     D3DXVECTOR2 pos;
5322     enum pointtype corner;
5323 };
5324 
5325 struct dynamic_array
5326 {
5327     int count, capacity;
5328     void *items;
5329 };
5330 
5331 /* is a dynamic_array */
5332 struct outline
5333 {
5334     int count, capacity;
5335     struct point2d *items;
5336 };
5337 
5338 /* is a dynamic_array */
5339 struct outline_array
5340 {
5341     int count, capacity;
5342     struct outline *items;
5343 };
5344 
5345 struct face_array
5346 {
5347     int count;
5348     face *items;
5349 };
5350 
5351 struct point2d_index
5352 {
5353     struct outline *outline;
5354     int vertex;
5355 };
5356 
5357 struct point2d_index_array
5358 {
5359     int count;
5360     struct point2d_index *items;
5361 };
5362 
5363 struct glyphinfo
5364 {
5365     struct outline_array outlines;
5366     struct face_array faces;
5367     struct point2d_index_array ordered_vertices;
5368     float offset_x;
5369 };
5370 
5371 /* is an dynamic_array */
5372 struct word_array
5373 {
5374     int count, capacity;
5375     WORD *items;
5376 };
5377 
5378 /* complex polygons are split into monotone polygons, which have
5379  * at most 2 intersections with the vertical sweep line */
5380 struct triangulation
5381 {
5382     struct word_array vertex_stack;
5383     BOOL last_on_top, merging;
5384 };
5385 
5386 /* is an dynamic_array */
5387 struct triangulation_array
5388 {
5389     int count, capacity;
5390     struct triangulation *items;
5391 
5392     struct glyphinfo *glyph;
5393 };
5394 
5395 static BOOL reserve(struct dynamic_array *array, int count, int itemsize)
5396 {
5397     if (count > array->capacity) {
5398         void *new_buffer;
5399         int new_capacity;
5400         if (array->items && array->capacity) {
5401             new_capacity = max(array->capacity * 2, count);
5402             new_buffer = HeapReAlloc(GetProcessHeap(), 0, array->items, new_capacity * itemsize);
5403         } else {
5404             new_capacity = max(16, count);
5405             new_buffer = HeapAlloc(GetProcessHeap(), 0, new_capacity * itemsize);
5406         }
5407         if (!new_buffer)
5408             return FALSE;
5409         array->items = new_buffer;
5410         array->capacity = new_capacity;
5411     }
5412     return TRUE;
5413 }
5414 
5415 static struct point2d *add_points(struct outline *array, int num)
5416 {
5417     struct point2d *item;
5418 
5419     if (!reserve((struct dynamic_array *)array, array->count + num, sizeof(array->items[0])))
5420         return NULL;
5421 
5422     item = &array->items[array->count];
5423     array->count += num;
5424     return item;
5425 }
5426 
5427 static struct outline *add_outline(struct outline_array *array)
5428 {
5429     struct outline *item;
5430 
5431     if (!reserve((struct dynamic_array *)array, array->count + 1, sizeof(array->items[0])))
5432         return NULL;
5433 
5434     item = &array->items[array->count++];
5435     ZeroMemory(item, sizeof(*item));
5436     return item;
5437 }
5438 
5439 static inline face *add_face(struct face_array *array)
5440 {
5441     return &array->items[array->count++];
5442 }
5443 
5444 static struct triangulation *add_triangulation(struct triangulation_array *array)
5445 {
5446     struct triangulation *item;
5447 
5448     if (!reserve((struct dynamic_array *)array, array->count + 1, sizeof(array->items[0])))
5449         return NULL;
5450 
5451     item = &array->items[array->count++];
5452     ZeroMemory(item, sizeof(*item));
5453     return item;
5454 }
5455 
5456 static HRESULT add_vertex_index(struct word_array *array, WORD vertex_index)
5457 {
5458     if (!reserve((struct dynamic_array *)array, array->count + 1, sizeof(array->items[0])))
5459         return E_OUTOFMEMORY;
5460 
5461     array->items[array->count++] = vertex_index;
5462     return S_OK;
5463 }
5464 
5465 /* assume fixed point numbers can be converted to float point in place */
5466 C_ASSERT(sizeof(FIXED) == sizeof(float));
5467 C_ASSERT(sizeof(POINTFX) == sizeof(D3DXVECTOR2));
5468 
5469 static inline D3DXVECTOR2 *convert_fixed_to_float(POINTFX *pt, int count, unsigned int emsquare)
5470 {
5471     D3DXVECTOR2 *ret = (D3DXVECTOR2*)pt;
5472     while (count--) {
5473         D3DXVECTOR2 *pt_flt = (D3DXVECTOR2*)pt;
5474         pt_flt->x = (pt->x.value + pt->x.fract / (float)0x10000) / emsquare;
5475         pt_flt->y = (pt->y.value + pt->y.fract / (float)0x10000) / emsquare;
5476         pt++;
5477     }
5478     return ret;
5479 }
5480 
5481 static HRESULT add_bezier_points(struct outline *outline, const D3DXVECTOR2 *p1,
5482                                  const D3DXVECTOR2 *p2, const D3DXVECTOR2 *p3,
5483                                  float max_deviation_sq)
5484 {
5485     D3DXVECTOR2 split1 = {0, 0}, split2 = {0, 0}, middle, vec;
5486     float deviation_sq;
5487 
5488     D3DXVec2Scale(&split1, D3DXVec2Add(&split1, p1, p2), 0.5f);
5489     D3DXVec2Scale(&split2, D3DXVec2Add(&split2, p2, p3), 0.5f);
5490     D3DXVec2Scale(&middle, D3DXVec2Add(&middle, &split1, &split2), 0.5f);
5491 
5492     deviation_sq = D3DXVec2LengthSq(D3DXVec2Subtract(&vec, &middle, p2));
5493     if (deviation_sq < max_deviation_sq) {
5494         struct point2d *pt = add_points(outline, 1);
5495         if (!pt) return E_OUTOFMEMORY;
5496         pt->pos = *p2;
5497         pt->corner = POINTTYPE_CURVE;
5498         /* the end point is omitted because the end line merges into the next segment of
5499          * the split bezier curve, and the end of the split bezier curve is added outside
5500          * this recursive function. */
5501     } else {
5502         HRESULT hr = add_bezier_points(outline, p1, &split1, &middle, max_deviation_sq);
5503         if (hr != S_OK) return hr;
5504         hr = add_bezier_points(outline, &middle, &split2, p3, max_deviation_sq);
5505         if (hr != S_OK) return hr;
5506     }
5507 
5508     return S_OK;
5509 }
5510 
5511 static inline BOOL is_direction_similar(D3DXVECTOR2 *dir1, D3DXVECTOR2 *dir2, float cos_theta)
5512 {
5513     /* dot product = cos(theta) */
5514     return D3DXVec2Dot(dir1, dir2) > cos_theta;
5515 }
5516 
5517 static inline D3DXVECTOR2 *unit_vec2(D3DXVECTOR2 *dir, const D3DXVECTOR2 *pt1, const D3DXVECTOR2 *pt2)
5518 {
5519     return D3DXVec2Normalize(D3DXVec2Subtract(dir, pt2, pt1), dir);
5520 }
5521 
5522 struct cos_table
5523 {
5524     float cos_half;
5525     float cos_45;
5526     float cos_90;
5527 };
5528 
5529 static BOOL attempt_line_merge(struct outline *outline,
5530                                int pt_index,
5531                                const D3DXVECTOR2 *nextpt,
5532                                BOOL to_curve,
5533                                const struct cos_table *table)
5534 {
5535     D3DXVECTOR2 curdir, lastdir;
5536     struct point2d *prevpt, *pt;
5537     BOOL ret = FALSE;
5538 
5539     pt = &outline->items[pt_index];
5540     pt_index = (pt_index - 1 + outline->count) % outline->count;
5541     prevpt = &outline->items[pt_index];
5542 
5543     if (to_curve)
5544         pt->corner = pt->corner != POINTTYPE_CORNER ? POINTTYPE_CURVE_MIDDLE : POINTTYPE_CURVE_START;
5545 
5546     if (outline->count < 2)
5547         return FALSE;
5548 
5549     /* remove last point if the next line continues the last line */
5550     unit_vec2(&lastdir, &prevpt->pos, &pt->pos);
5551     unit_vec2(&curdir, &pt->pos, nextpt);
5552     if (is_direction_similar(&lastdir, &curdir, table->cos_half))
5553     {
5554         outline->count--;
5555         if (pt->corner == POINTTYPE_CURVE_END)
5556             prevpt->corner = pt->corner;
5557         if (prevpt->corner == POINTTYPE_CURVE_END && to_curve)
5558             prevpt->corner = POINTTYPE_CURVE_MIDDLE;
5559         pt = prevpt;
5560 
5561         ret = TRUE;
5562         if (outline->count < 2)
5563             return ret;
5564 
5565         pt_index = (pt_index - 1 + outline->count) % outline->count;
5566         prevpt = &outline->items[pt_index];
5567         unit_vec2(&lastdir, &prevpt->pos, &pt->pos);
5568         unit_vec2(&curdir, &pt->pos, nextpt);
5569     }
5570     return ret;
5571 }
5572 
5573 static HRESULT create_outline(struct glyphinfo *glyph, void *raw_outline, int datasize,
5574                               float max_deviation_sq, unsigned int emsquare,
5575                               const struct cos_table *cos_table)
5576 {
5577     TTPOLYGONHEADER *header = (TTPOLYGONHEADER *)raw_outline;
5578 
5579     while ((char *)header < (char *)raw_outline + datasize)
5580     {
5581         TTPOLYCURVE *curve = (TTPOLYCURVE *)(header + 1);
5582         struct point2d *lastpt, *pt;
5583         D3DXVECTOR2 lastdir;
5584         D3DXVECTOR2 *pt_flt;
5585         int j;
5586         struct outline *outline = add_outline(&glyph->outlines);
5587 
5588         if (!outline)
5589             return E_OUTOFMEMORY;
5590 
5591         pt = add_points(outline, 1);
5592         if (!pt)
5593             return E_OUTOFMEMORY;
5594         pt_flt = convert_fixed_to_float(&header->pfxStart, 1, emsquare);
5595         pt->pos = *pt_flt;
5596         pt->corner = POINTTYPE_CORNER;
5597 
5598         if (header->dwType != TT_POLYGON_TYPE)
5599             FIXME("Unknown header type %d\n", header->dwType);
5600 
5601         while ((char *)curve < (char *)header + header->cb)
5602         {
5603             D3DXVECTOR2 bezier_start = outline->items[outline->count - 1].pos;
5604             BOOL to_curve = curve->wType != TT_PRIM_LINE && curve->cpfx > 1;
5605             unsigned int j2 = 0;
5606 
5607             if (!curve->cpfx) {
5608                 curve = (TTPOLYCURVE *)&curve->apfx[curve->cpfx];
5609                 continue;
5610             }
5611 
5612             pt_flt = convert_fixed_to_float(curve->apfx, curve->cpfx, emsquare);
5613 
5614             attempt_line_merge(outline, outline->count - 1, &pt_flt[0], to_curve, cos_table);
5615 
5616             if (to_curve)
5617             {
5618                 HRESULT hr;
5619                 int count = curve->cpfx;
5620 
5621                 while (count > 2)
5622                 {
5623                     D3DXVECTOR2 bezier_end;
5624 
5625                     D3DXVec2Scale(&bezier_end, D3DXVec2Add(&bezier_end, &pt_flt[j2], &pt_flt[j2+1]), 0.5f);
5626                     hr = add_bezier_points(outline, &bezier_start, &pt_flt[j2], &bezier_end, max_deviation_sq);
5627                     if (hr != S_OK)
5628                         return hr;
5629                     bezier_start = bezier_end;
5630                     count--;
5631                     j2++;
5632                 }
5633                 hr = add_bezier_points(outline, &bezier_start, &pt_flt[j2], &pt_flt[j2+1], max_deviation_sq);
5634                 if (hr != S_OK)
5635                     return hr;
5636 
5637                 pt = add_points(outline, 1);
5638                 if (!pt)
5639                     return E_OUTOFMEMORY;
5640                 j2++;
5641                 pt->pos = pt_flt[j2];
5642                 pt->corner = POINTTYPE_CURVE_END;
5643             } else {
5644                 pt = add_points(outline, curve->cpfx);
5645                 if (!pt)
5646                     return E_OUTOFMEMORY;
5647                 for (j2 = 0; j2 < curve->cpfx; j2++)
5648                 {
5649                     pt->pos = pt_flt[j2];
5650                     pt->corner = POINTTYPE_CORNER;
5651                     pt++;
5652                 }
5653             }
5654 
5655             curve = (TTPOLYCURVE *)&curve->apfx[curve->cpfx];
5656         }
5657 
5658         /* remove last point if the next line continues the last line */
5659         if (outline->count >= 3) {
5660             BOOL to_curve;
5661 
5662             lastpt = &outline->items[outline->count - 1];
5663             pt = &outline->items[0];
5664             if (pt->pos.x == lastpt->pos.x && pt->pos.y == lastpt->pos.y) {
5665                 if (lastpt->corner == POINTTYPE_CURVE_END)
5666                 {
5667                     if (pt->corner == POINTTYPE_CURVE_START)
5668                         pt->corner = POINTTYPE_CURVE_MIDDLE;
5669                     else
5670                         pt->corner = POINTTYPE_CURVE_END;
5671                 }
5672                 outline->count--;
5673             } else {
5674                 /* outline closed with a line from end to start point */
5675                 attempt_line_merge(outline, outline->count - 1, &pt->pos, FALSE, cos_table);
5676             }
5677             lastpt = &outline->items[0];
5678             to_curve = lastpt->corner != POINTTYPE_CORNER && lastpt->corner != POINTTYPE_CURVE_END;
5679             if (lastpt->corner == POINTTYPE_CURVE_START)
5680                 lastpt->corner = POINTTYPE_CORNER;
5681             pt = &outline->items[1];
5682             if (attempt_line_merge(outline, 0, &pt->pos, to_curve, cos_table))
5683                 *lastpt = outline->items[outline->count];
5684         }
5685 
5686         lastpt = &outline->items[outline->count - 1];
5687         pt = &outline->items[0];
5688         unit_vec2(&lastdir, &lastpt->pos, &pt->pos);
5689         for (j = 0; j < outline->count; j++)
5690         {
5691             D3DXVECTOR2 curdir;
5692 
5693             lastpt = pt;
5694             pt = &outline->items[(j + 1) % outline->count];
5695             unit_vec2(&curdir, &lastpt->pos, &pt->pos);
5696 
5697             switch (lastpt->corner)
5698             {
5699                 case POINTTYPE_CURVE_START:
5700                 case POINTTYPE_CURVE_END:
5701                     if (!is_direction_similar(&lastdir, &curdir, cos_table->cos_45))
5702                         lastpt->corner = POINTTYPE_CORNER;
5703                     break;
5704                 case POINTTYPE_CURVE_MIDDLE:
5705                     if (!is_direction_similar(&lastdir, &curdir, cos_table->cos_90))
5706                         lastpt->corner = POINTTYPE_CORNER;
5707                     else
5708                         lastpt->corner = POINTTYPE_CURVE;
5709                     break;
5710                 default:
5711                     break;
5712             }
5713             lastdir = curdir;
5714         }
5715 
5716         header = (TTPOLYGONHEADER *)((char *)header + header->cb);
5717     }
5718     return S_OK;
5719 }
5720 
5721 /* Get the y-distance from a line to a point */
5722 static float get_line_to_point_y_distance(D3DXVECTOR2 *line_pt1,
5723                                           D3DXVECTOR2 *line_pt2,
5724                                           D3DXVECTOR2 *point)
5725 {
5726     D3DXVECTOR2 line_vec = {0, 0};
5727     float line_pt_dx;
5728     float line_y;
5729 
5730     D3DXVec2Subtract(&line_vec, line_pt2, line_pt1);
5731     line_pt_dx = point->x - line_pt1->x;
5732     line_y = line_pt1->y + (line_vec.y * line_pt_dx) / line_vec.x;
5733     return point->y - line_y;
5734 }
5735 
5736 static D3DXVECTOR2 *get_indexed_point(struct point2d_index *pt_idx)
5737 {
5738     return &pt_idx->outline->items[pt_idx->vertex].pos;
5739 }
5740 
5741 static D3DXVECTOR2 *get_ordered_vertex(struct glyphinfo *glyph, WORD index)
5742 {
5743     return get_indexed_point(&glyph->ordered_vertices.items[index]);
5744 }
5745 
5746 static void remove_triangulation(struct triangulation_array *array, struct triangulation *item)
5747 {
5748     HeapFree(GetProcessHeap(), 0, item->vertex_stack.items);
5749     MoveMemory(item, item + 1, (char*)&array->items[array->count] - (char*)(item + 1));
5750     array->count--;
5751 }
5752 
5753 static HRESULT triangulation_add_point(struct triangulation **t_ptr,
5754                                        struct triangulation_array *triangulations,
5755                                        WORD vtx_idx,
5756                                        BOOL to_top)
5757 {
5758     struct glyphinfo *glyph = triangulations->glyph;
5759     struct triangulation *t = *t_ptr;
5760     HRESULT hr;
5761     face *face;
5762     int f1, f2;
5763 
5764     if (t->last_on_top) {
5765         f1 = 1;
5766         f2 = 2;
5767     } else {
5768         f1 = 2;
5769         f2 = 1;
5770     }
5771 
5772     if (t->last_on_top != to_top && t->vertex_stack.count > 1) {
5773         /* consume all vertices on the stack */
5774         WORD last_pt = t->vertex_stack.items[0];
5775         int i;
5776         for (i = 1; i < t->vertex_stack.count; i++)
5777         {
5778             face = add_face(&glyph->faces);
5779             if (!face) return E_OUTOFMEMORY;
5780             (*face)[0] = vtx_idx;
5781             (*face)[f1] = last_pt;
5782             (*face)[f2] = last_pt = t->vertex_stack.items[i];
5783         }
5784         t->vertex_stack.items[0] = last_pt;
5785         t->vertex_stack.count = 1;
5786     } else if (t->vertex_stack.count > 1) {
5787         int i = t->vertex_stack.count - 1;
5788         D3DXVECTOR2 *point = get_ordered_vertex(glyph, vtx_idx);
5789         WORD top_idx = t->vertex_stack.items[i--];
5790         D3DXVECTOR2 *top_pt = get_ordered_vertex(glyph, top_idx);
5791 
5792         while (i >= 0)
5793         {
5794             WORD prev_idx = t->vertex_stack.items[i--];
5795             D3DXVECTOR2 *prev_pt = get_ordered_vertex(glyph, prev_idx);
5796 
5797             if (prev_pt->x != top_pt->x &&
5798                 ((to_top && get_line_to_point_y_distance(prev_pt, top_pt, point) > 0) ||
5799                  (!to_top && get_line_to_point_y_distance(prev_pt, top_pt, point) < 0)))
5800                 break;
5801 
5802             face = add_face(&glyph->faces);
5803             if (!face) return E_OUTOFMEMORY;
5804             (*face)[0] = vtx_idx;
5805             (*face)[f1] = prev_idx;
5806             (*face)[f2] = top_idx;
5807 
5808             top_pt = prev_pt;
5809             top_idx = prev_idx;
5810             t->vertex_stack.count--;
5811         }
5812     }
5813     t->last_on_top = to_top;
5814 
5815     hr = add_vertex_index(&t->vertex_stack, vtx_idx);
5816 
5817     if (hr == S_OK && t->merging) {
5818         struct triangulation *t2;
5819 
5820         t2 = to_top ? t - 1 : t + 1;
5821         t2->merging = FALSE;
5822         hr = triangulation_add_point(&t2, triangulations, vtx_idx, to_top);
5823         if (hr != S_OK) return hr;
5824         remove_triangulation(triangulations, t);
5825         if (t2 > t)
5826             t2--;
5827         *t_ptr = t2;
5828     }
5829     return hr;
5830 }
5831 
5832 /* check if the point is next on the outline for either the top or bottom */
5833 static D3DXVECTOR2 *triangulation_get_next_point(struct triangulation *t, struct glyphinfo *glyph, BOOL on_top)
5834 {
5835     int i = t->last_on_top == on_top ? t->vertex_stack.count - 1 : 0;
5836     WORD idx = t->vertex_stack.items[i];
5837     struct point2d_index *pt_idx = &glyph->ordered_vertices.items[idx];
5838     struct outline *outline = pt_idx->outline;
5839 
5840     if (on_top)
5841         i = (pt_idx->vertex + outline->count - 1) % outline->count;
5842     else
5843         i = (pt_idx->vertex + 1) % outline->count;
5844 
5845     return &outline->items[i].pos;
5846 }
5847 
5848 static int __cdecl compare_vertex_indices(const void *a, const void *b)
5849 {
5850     const struct point2d_index *idx1 = a, *idx2 = b;
5851     const D3DXVECTOR2 *p1 = &idx1->outline->items[idx1->vertex].pos;
5852     const D3DXVECTOR2 *p2 = &idx2->outline->items[idx2->vertex].pos;
5853     float diff = p1->x - p2->x;
5854 
5855     if (diff == 0.0f)
5856         diff = p1->y - p2->y;
5857 
5858     return diff == 0.0f ? 0 : (diff > 0.0f ? -1 : 1);
5859 }
5860 
5861 static HRESULT triangulate(struct triangulation_array *triangulations)
5862 {
5863     int sweep_idx;
5864     HRESULT hr;
5865     struct glyphinfo *glyph = triangulations->glyph;
5866     int nb_vertices = 0;
5867     int i;
5868     struct point2d_index *idx_ptr;
5869 
5870     /* Glyphs without outlines do not generate any vertices. */
5871     if (!glyph->outlines.count)
5872         return D3D_OK;
5873 
5874     for (i = 0; i < glyph->outlines.count; i++)
5875         nb_vertices += glyph->outlines.items[i].count;
5876 
5877     glyph->ordered_vertices.items = HeapAlloc(GetProcessHeap(), 0,
5878             nb_vertices * sizeof(*glyph->ordered_vertices.items));
5879     if (!glyph->ordered_vertices.items)
5880         return E_OUTOFMEMORY;
5881 
5882     idx_ptr = glyph->ordered_vertices.items;
5883     for (i = 0; i < glyph->outlines.count; i++)
5884     {
5885         struct outline *outline = &glyph->outlines.items[i];
5886         int j;
5887 
5888         idx_ptr->outline = outline;
5889         idx_ptr->vertex = 0;
5890         idx_ptr++;
5891         for (j = outline->count - 1; j > 0; j--)
5892         {
5893             idx_ptr->outline = outline;
5894             idx_ptr->vertex = j;
5895             idx_ptr++;
5896         }
5897     }
5898     glyph->ordered_vertices.count = nb_vertices;
5899 
5900     /* Native implementation seems to try to create a triangle fan from
5901      * the first outline point if the glyph only has one outline. */
5902     if (glyph->outlines.count == 1)
5903     {
5904         struct outline *outline = glyph->outlines.items;
5905         D3DXVECTOR2 *base = &outline->items[0].pos;
5906         D3DXVECTOR2 *last = &outline->items[1].pos;
5907         float ccw = 0;
5908 
5909         for (i = 2; i < outline->count; i++)
5910         {
5911             D3DXVECTOR2 *next = &outline->items[i].pos;
5912             D3DXVECTOR2 v1 = {0.0f, 0.0f};
5913             D3DXVECTOR2 v2 = {0.0f, 0.0f};
5914 
5915             D3DXVec2Subtract(&v1, base, last);
5916             D3DXVec2Subtract(&v2, last, next);
5917             ccw = D3DXVec2CCW(&v1, &v2);
5918             if (ccw > 0.0f)
5919                 break;
5920 
5921             last = next;
5922         }
5923         if (ccw <= 0)
5924         {
5925             glyph->faces.items = HeapAlloc(GetProcessHeap(), 0,
5926                     (outline->count - 2) * sizeof(glyph->faces.items[0]));
5927             if (!glyph->faces.items)
5928                 return E_OUTOFMEMORY;
5929 
5930             glyph->faces.count = outline->count - 2;
5931             for (i = 0; i < glyph->faces.count; i++)
5932             {
5933                 glyph->faces.items[i][0] = 0;
5934                 glyph->faces.items[i][1] = i + 1;
5935                 glyph->faces.items[i][2] = i + 2;
5936             }
5937             return S_OK;
5938         }
5939     }
5940 
5941     /* Perform 2D polygon triangulation for complex glyphs.
5942      * Triangulation is performed using a sweep line concept, from right to left,
5943      * by processing vertices in sorted order. Complex polygons are split into
5944      * monotone polygons which are triangulated separately. */
5945     /* FIXME: The order of the faces is not consistent with the native implementation. */
5946 
5947     /* Reserve space for maximum possible faces from triangulation.
5948      * # faces for outer outlines = outline->count - 2
5949      * # faces for inner outlines = outline->count + 2
5950      * There must be at least 1 outer outline. */
5951     glyph->faces.items = HeapAlloc(GetProcessHeap(), 0,
5952             (nb_vertices + glyph->outlines.count * 2 - 4) * sizeof(glyph->faces.items[0]));
5953     if (!glyph->faces.items)
5954         return E_OUTOFMEMORY;
5955 
5956     qsort(glyph->ordered_vertices.items, nb_vertices,
5957           sizeof(glyph->ordered_vertices.items[0]), compare_vertex_indices);
5958     for (sweep_idx = 0; sweep_idx < glyph->ordered_vertices.count; sweep_idx++)
5959     {
5960         int start = 0;
5961         int end = triangulations->count;
5962 
5963         while (start < end)
5964         {
5965             D3DXVECTOR2 *sweep_vtx = get_ordered_vertex(glyph, sweep_idx);
5966             int current = (start + end) / 2;
5967             struct triangulation *t = &triangulations->items[current];
5968             BOOL on_top_outline = FALSE;
5969             D3DXVECTOR2 *top_next, *bottom_next;
5970             WORD top_idx, bottom_idx;
5971 
5972             if (t->merging && t->last_on_top)
5973                 top_next = triangulation_get_next_point(t + 1, glyph, TRUE);
5974             else
5975                 top_next = triangulation_get_next_point(t, glyph, TRUE);
5976             if (sweep_vtx == top_next)
5977             {
5978                 if (t->merging && t->last_on_top)
5979                     t++;
5980                 hr = triangulation_add_point(&t, triangulations, sweep_idx, TRUE);
5981                 if (hr != S_OK) return hr;
5982 
5983                 if (t + 1 < &triangulations->items[triangulations->count] &&
5984                     triangulation_get_next_point(t + 1, glyph, FALSE) == sweep_vtx)
5985                 {
5986                     /* point also on bottom outline of higher triangulation */
5987                     struct triangulation *t2 = t + 1;
5988                     hr = triangulation_add_point(&t2, triangulations, sweep_idx, FALSE);
5989                     if (hr != S_OK) return hr;
5990 
5991                     t->merging = TRUE;
5992                     t2->merging = TRUE;
5993                 }
5994                 on_top_outline = TRUE;
5995             }
5996 
5997             if (t->merging && !t->last_on_top)
5998                 bottom_next = triangulation_get_next_point(t - 1, glyph, FALSE);
5999             else
6000                 bottom_next = triangulation_get_next_point(t, glyph, FALSE);
6001             if (sweep_vtx == bottom_next)
6002             {
6003                 if (t->merging && !t->last_on_top)
6004                     t--;
6005                 if (on_top_outline) {
6006                     /* outline finished */
6007                     remove_triangulation(triangulations, t);
6008                     break;
6009                 }
6010 
6011                 hr = triangulation_add_point(&t, triangulations, sweep_idx, FALSE);
6012                 if (hr != S_OK) return hr;
6013 
6014                 if (t > triangulations->items &&
6015                     triangulation_get_next_point(t - 1, glyph, TRUE) == sweep_vtx)
6016                 {
6017                     struct triangulation *t2 = t - 1;
6018                     /* point also on top outline of lower triangulation */
6019                     hr = triangulation_add_point(&t2, triangulations, sweep_idx, TRUE);
6020                     if (hr != S_OK) return hr;
6021                     t = t2 + 1; /* t may be invalidated by triangulation merging */
6022 
6023                     t->merging = TRUE;
6024                     t2->merging = TRUE;
6025                 }
6026                 break;
6027             }
6028             if (on_top_outline)
6029                 break;
6030 
6031             if (t->last_on_top) {
6032                 top_idx = t->vertex_stack.items[t->vertex_stack.count - 1];
6033                 bottom_idx = t->vertex_stack.items[0];
6034             } else {
6035                 top_idx = t->vertex_stack.items[0];
6036                 bottom_idx = t->vertex_stack.items[t->vertex_stack.count - 1];
6037             }
6038 
6039             /* check if the point is inside or outside this polygon */
6040             if (get_line_to_point_y_distance(get_ordered_vertex(glyph, top_idx),
6041                                              top_next, sweep_vtx) > 0)
6042             { /* above */
6043                 start = current + 1;
6044             } else if (get_line_to_point_y_distance(get_ordered_vertex(glyph, bottom_idx),
6045                                                     bottom_next, sweep_vtx) < 0)
6046             { /* below */
6047                 end = current;
6048             } else if (t->merging) {
6049                 /* inside, so cancel merging */
6050                 struct triangulation *t2 = t->last_on_top ? t + 1 : t - 1;
6051                 t->merging = FALSE;
6052                 t2->merging = FALSE;
6053                 hr = triangulation_add_point(&t, triangulations, sweep_idx, t->last_on_top);
6054                 if (hr != S_OK) return hr;
6055                 hr = triangulation_add_point(&t2, triangulations, sweep_idx, t2->last_on_top);
6056                 if (hr != S_OK) return hr;
6057                 break;
6058             } else {
6059                 /* inside, so split polygon into two monotone parts */
6060                 struct triangulation *t2 = add_triangulation(triangulations);
6061                 if (!t2) return E_OUTOFMEMORY;
6062                 MoveMemory(t + 1, t, (char*)(t2 + 1) - (char*)t);
6063                 if (t->last_on_top) {
6064                     t2 = t + 1;
6065                 } else {
6066                     t2 = t;
6067                     t++;
6068                 }
6069 
6070                 ZeroMemory(&t2->vertex_stack, sizeof(t2->vertex_stack));
6071                 hr = add_vertex_index(&t2->vertex_stack, t->vertex_stack.items[t->vertex_stack.count - 1]);
6072                 if (hr != S_OK) return hr;
6073                 hr = add_vertex_index(&t2->vertex_stack, sweep_idx);
6074                 if (hr != S_OK) return hr;
6075                 t2->last_on_top = !t->last_on_top;
6076 
6077                 hr = triangulation_add_point(&t, triangulations, sweep_idx, t->last_on_top);
6078                 if (hr != S_OK) return hr;
6079                 break;
6080             }
6081         }
6082         if (start >= end)
6083         {
6084             struct triangulation *t;
6085             struct triangulation *t2 = add_triangulation(triangulations);
6086             if (!t2) return E_OUTOFMEMORY;
6087             t = &triangulations->items[start];
6088             MoveMemory(t + 1, t, (char*)(t2 + 1) - (char*)t);
6089             ZeroMemory(t, sizeof(*t));
6090             hr = add_vertex_index(&t->vertex_stack, sweep_idx);
6091             if (hr != S_OK) return hr;
6092         }
6093     }
6094     return S_OK;
6095 }
6096 
6097 HRESULT WINAPI D3DXCreateTextW(struct IDirect3DDevice9 *device, HDC hdc, const WCHAR *text, float deviation,
6098         float extrusion, struct ID3DXMesh **mesh_ptr, struct ID3DXBuffer **adjacency, GLYPHMETRICSFLOAT *glyphmetrics)
6099 {
6100     HRESULT hr;
6101     ID3DXMesh *mesh = NULL;
6102     DWORD nb_vertices, nb_faces;
6103     DWORD nb_front_faces, nb_corners, nb_outline_points;
6104     struct vertex *vertices = NULL;
6105     face *faces = NULL;
6106     int textlen = 0;
6107     float offset_x;
6108     LOGFONTW lf;
6109     OUTLINETEXTMETRICW otm;
6110     HFONT font = NULL, oldfont = NULL;
6111     const MAT2 identity = {{0, 1}, {0, 0}, {0, 0}, {0, 1}};
6112     void *raw_outline = NULL;
6113     int bufsize = 0;
6114     struct glyphinfo *glyphs = NULL;
6115     GLYPHMETRICS gm;
6116     struct triangulation_array triangulations = {0, 0, NULL};
6117     int i;
6118     struct vertex *vertex_ptr;
6119     face *face_ptr;
6120     float max_deviation_sq;
6121     const struct cos_table cos_table = {
6122         cosf(D3DXToRadian(0.5f)),
6123         cosf(D3DXToRadian(45.0f)),
6124         cosf(D3DXToRadian(90.0f)),
6125     };
6126     int f1, f2;
6127 
6128     TRACE("(%p, %p, %s, %f, %f, %p, %p, %p)\n", device, hdc,
6129           debugstr_w(text), deviation, extrusion, mesh_ptr, adjacency, glyphmetrics);
6130 
6131     if (!device || !hdc || !text || !*text || deviation < 0.0f || extrusion < 0.0f || !mesh_ptr)
6132         return D3DERR_INVALIDCALL;
6133 
6134     if (adjacency)
6135     {
6136         FIXME("Case of adjacency != NULL not implemented.\n");
6137         return E_NOTIMPL;
6138     }
6139 
6140     if (!GetObjectW(GetCurrentObject(hdc, OBJ_FONT), sizeof(lf), &lf) ||
6141         !GetOutlineTextMetricsW(hdc, sizeof(otm), &otm))
6142     {
6143         return D3DERR_INVALIDCALL;
6144     }
6145 
6146     if (deviation == 0.0f)
6147         deviation = 1.0f / otm.otmEMSquare;
6148     max_deviation_sq = deviation * deviation;
6149 
6150     lf.lfHeight = otm.otmEMSquare;
6151     lf.lfWidth = 0;
6152     font = CreateFontIndirectW(&lf);
6153     if (!font) {
6154         hr = E_OUTOFMEMORY;
6155         goto error;
6156     }
6157     oldfont = SelectObject(hdc, font);
6158 
6159     textlen = lstrlenW(text);
6160     for (i = 0; i < textlen; i++)
6161     {
6162         int datasize = GetGlyphOutlineW(hdc, text[i], GGO_NATIVE, &gm, 0, NULL, &identity);
6163         if (datasize < 0)
6164             return D3DERR_INVALIDCALL;
6165         if (bufsize < datasize)
6166             bufsize = datasize;
6167     }
6168     if (!bufsize) { /* e.g. text == " " */
6169         hr = D3DERR_INVALIDCALL;
6170         goto error;
6171     }
6172 
6173     glyphs = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, textlen * sizeof(*glyphs));
6174     raw_outline = HeapAlloc(GetProcessHeap(), 0, bufsize);
6175     if (!glyphs || !raw_outline) {
6176         hr = E_OUTOFMEMORY;
6177         goto error;
6178     }
6179 
6180     offset_x = 0.0f;
6181     for (i = 0; i < textlen; i++)
6182     {
6183         /* get outline points from data returned from GetGlyphOutline */
6184         int datasize;
6185 
6186         glyphs[i].offset_x = offset_x;
6187 
6188         datasize = GetGlyphOutlineW(hdc, text[i], GGO_NATIVE, &gm, bufsize, raw_outline, &identity);
6189         hr = create_outline(&glyphs[i], raw_outline, datasize,
6190                             max_deviation_sq, otm.otmEMSquare, &cos_table);
6191         if (hr != S_OK) goto error;
6192 
6193         triangulations.glyph = &glyphs[i];
6194         hr = triangulate(&triangulations);
6195         if (hr != S_OK) goto error;
6196         if (triangulations.count) {
6197             ERR("%d incomplete triangulations of glyph (%u).\n", triangulations.count, text[i]);
6198             triangulations.count = 0;
6199         }
6200 
6201         if (glyphmetrics)
6202         {
6203             glyphmetrics[i].gmfBlackBoxX = gm.gmBlackBoxX / (float)otm.otmEMSquare;
6204             glyphmetrics[i].gmfBlackBoxY = gm.gmBlackBoxY / (float)otm.otmEMSquare;
6205             glyphmetrics[i].gmfptGlyphOrigin.x = gm.gmptGlyphOrigin.x / (float)otm.otmEMSquare;
6206             glyphmetrics[i].gmfptGlyphOrigin.y = gm.gmptGlyphOrigin.y / (float)otm.otmEMSquare;
6207             glyphmetrics[i].gmfCellIncX = gm.gmCellIncX / (float)otm.otmEMSquare;
6208             glyphmetrics[i].gmfCellIncY = gm.gmCellIncY / (float)otm.otmEMSquare;
6209         }
6210         offset_x += gm.gmCellIncX / (float)otm.otmEMSquare;
6211     }
6212 
6213     /* corner points need an extra vertex for the different side faces normals */
6214     nb_corners = 0;
6215     nb_outline_points = 0;
6216     nb_front_faces = 0;
6217     for (i = 0; i < textlen; i++)
6218     {
6219         int j;
6220         nb_outline_points += glyphs[i].ordered_vertices.count;
6221         nb_front_faces += glyphs[i].faces.count;
6222         for (j = 0; j < glyphs[i].outlines.count; j++)
6223         {
6224             int k;
6225             struct outline *outline = &glyphs[i].outlines.items[j];
6226             nb_corners++; /* first outline point always repeated as a corner */
6227             for (k = 1; k < outline->count; k++)
6228                 if (outline->items[k].corner)
6229                     nb_corners++;
6230         }
6231     }
6232 
6233     nb_vertices = (nb_outline_points + nb_corners) * 2 + nb_outline_points * 2;
6234     nb_faces = nb_outline_points * 2 + nb_front_faces * 2;
6235 
6236 
6237     hr = D3DXCreateMeshFVF(nb_faces, nb_vertices, D3DXMESH_MANAGED,
6238                            D3DFVF_XYZ | D3DFVF_NORMAL, device, &mesh);
6239     if (FAILED(hr))
6240         goto error;
6241 
6242     if (FAILED(hr = mesh->lpVtbl->LockVertexBuffer(mesh, 0, (void **)&vertices)))
6243         goto error;
6244 
6245     if (FAILED(hr = mesh->lpVtbl->LockIndexBuffer(mesh, 0, (void **)&faces)))
6246         goto error;
6247 
6248     /* convert 2D vertices and faces into 3D mesh */
6249     vertex_ptr = vertices;
6250     face_ptr = faces;
6251     if (extrusion == 0.0f) {
6252         f1 = 1;
6253         f2 = 2;
6254     } else {
6255         f1 = 2;
6256         f2 = 1;
6257     }
6258     for (i = 0; i < textlen; i++)
6259     {
6260         int j;
6261         int count;
6262         struct vertex *back_vertices;
6263         face *back_faces;
6264 
6265         /* side vertices and faces */
6266         for (j = 0; j < glyphs[i].outlines.count; j++)
6267         {
6268             struct vertex *outline_vertices = vertex_ptr;
6269             struct outline *outline = &glyphs[i].outlines.items[j];
6270             int k;
6271             struct point2d *prevpt = &outline->items[outline->count - 1];
6272             struct point2d *pt = &outline->items[0];
6273 
6274             for (k = 1; k <= outline->count; k++)
6275             {
6276                 struct vertex vtx;
6277                 struct point2d *nextpt = &outline->items[k % outline->count];
6278                 WORD vtx_idx = vertex_ptr - vertices;
6279                 D3DXVECTOR2 vec;
6280 
6281                 if (pt->corner == POINTTYPE_CURVE_START)
6282                     D3DXVec2Subtract(&vec, &pt->pos, &prevpt->pos);
6283                 else if (pt->corner)
6284                     D3DXVec2Subtract(&vec, &nextpt->pos, &pt->pos);
6285                 else
6286                     D3DXVec2Subtract(&vec, &nextpt->pos, &prevpt->pos);
6287                 D3DXVec2Normalize(&vec, &vec);
6288                 vtx.normal.x = -vec.y;
6289                 vtx.normal.y = vec.x;
6290                 vtx.normal.z = 0;
6291 
6292                 vtx.position.x = pt->pos.x + glyphs[i].offset_x;
6293                 vtx.position.y = pt->pos.y;
6294                 vtx.position.z = 0;
6295                 *vertex_ptr++ = vtx;
6296 
6297                 vtx.position.z = -extrusion;
6298                 *vertex_ptr++ = vtx;
6299 
6300                 vtx.position.x = nextpt->pos.x + glyphs[i].offset_x;
6301                 vtx.position.y = nextpt->pos.y;
6302                 if (pt->corner && nextpt->corner && nextpt->corner != POINTTYPE_CURVE_END) {
6303                     vtx.position.z = -extrusion;
6304                     *vertex_ptr++ = vtx;
6305                     vtx.position.z = 0;
6306                     *vertex_ptr++ = vtx;
6307 
6308                     (*face_ptr)[0] = vtx_idx;
6309                     (*face_ptr)[1] = vtx_idx + 2;
6310                     (*face_ptr)[2] = vtx_idx + 1;
6311                     face_ptr++;
6312 
6313                     (*face_ptr)[0] = vtx_idx;
6314                     (*face_ptr)[1] = vtx_idx + 3;
6315                     (*face_ptr)[2] = vtx_idx + 2;
6316                     face_ptr++;
6317                 } else {
6318                     if (nextpt->corner) {
6319                         if (nextpt->corner == POINTTYPE_CURVE_END) {
6320                             D3DXVECTOR2 *nextpt2 = &outline->items[(k + 1) % outline->count].pos;
6321                             D3DXVec2Subtract(&vec, nextpt2, &nextpt->pos);
6322                         } else {
6323                             D3DXVec2Subtract(&vec, &nextpt->pos, &pt->pos);
6324                         }
6325                         D3DXVec2Normalize(&vec, &vec);
6326                         vtx.normal.x = -vec.y;
6327                         vtx.normal.y = vec.x;
6328 
6329                         vtx.position.z = 0;
6330                         *vertex_ptr++ = vtx;
6331                         vtx.position.z = -extrusion;
6332                         *vertex_ptr++ = vtx;
6333                     }
6334 
6335                     (*face_ptr)[0] = vtx_idx;
6336                     (*face_ptr)[1] = vtx_idx + 3;
6337                     (*face_ptr)[2] = vtx_idx + 1;
6338                     face_ptr++;
6339 
6340                     (*face_ptr)[0] = vtx_idx;
6341                     (*face_ptr)[1] = vtx_idx + 2;
6342                     (*face_ptr)[2] = vtx_idx + 3;
6343                     face_ptr++;
6344                 }
6345 
6346                 prevpt = pt;
6347                 pt = nextpt;
6348             }
6349             if (!pt->corner) {
6350                 *vertex_ptr++ = *outline_vertices++;
6351                 *vertex_ptr++ = *outline_vertices++;
6352             }
6353         }
6354 
6355         /* back vertices and faces */
6356         back_faces = face_ptr;
6357         back_vertices = vertex_ptr;
6358         for (j = 0; j < glyphs[i].ordered_vertices.count; j++)
6359         {
6360             D3DXVECTOR2 *pt = get_ordered_vertex(&glyphs[i], j);
6361             vertex_ptr->position.x = pt->x + glyphs[i].offset_x;
6362             vertex_ptr->position.y = pt->y;
6363             vertex_ptr->position.z = 0;
6364             vertex_ptr->normal.x = 0;
6365             vertex_ptr->normal.y = 0;
6366             vertex_ptr->normal.z = 1;
6367             vertex_ptr++;
6368         }
6369         count = back_vertices - vertices;
6370         for (j = 0; j < glyphs[i].faces.count; j++)
6371         {
6372             face *f = &glyphs[i].faces.items[j];
6373             (*face_ptr)[0] = (*f)[0] + count;
6374             (*face_ptr)[1] = (*f)[1] + count;
6375             (*face_ptr)[2] = (*f)[2] + count;
6376             face_ptr++;
6377         }
6378 
6379         /* front vertices and faces */
6380         j = count = vertex_ptr - back_vertices;
6381         while (j--)
6382         {
6383             vertex_ptr->position.x = back_vertices->position.x;
6384             vertex_ptr->position.y = back_vertices->position.y;
6385             vertex_ptr->position.z = -extrusion;
6386             vertex_ptr->normal.x = 0;
6387             vertex_ptr->normal.y = 0;
6388             vertex_ptr->normal.z = extrusion == 0.0f ? 1.0f : -1.0f;
6389             vertex_ptr++;
6390             back_vertices++;
6391         }
6392         j = face_ptr - back_faces;
6393         while (j--)
6394         {
6395             (*face_ptr)[0] = (*back_faces)[0] + count;
6396             (*face_ptr)[1] = (*back_faces)[f1] + count;
6397             (*face_ptr)[2] = (*back_faces)[f2] + count;
6398             face_ptr++;
6399             back_faces++;
6400         }
6401     }
6402 
6403     *mesh_ptr = mesh;
6404     hr = D3D_OK;
6405 error:
6406     if (mesh) {
6407         if (faces) mesh->lpVtbl->UnlockIndexBuffer(mesh);
6408         if (vertices) mesh->lpVtbl->UnlockVertexBuffer(mesh);
6409         if (hr != D3D_OK) mesh->lpVtbl->Release(mesh);
6410     }
6411     if (glyphs) {
6412         for (i = 0; i < textlen; i++)
6413         {
6414             int j;
6415             for (j = 0; j < glyphs[i].outlines.count; j++)
6416                 HeapFree(GetProcessHeap(), 0, glyphs[i].outlines.items[j].items);
6417             HeapFree(GetProcessHeap(), 0, glyphs[i].outlines.items);
6418             HeapFree(GetProcessHeap(), 0, glyphs[i].faces.items);
6419             HeapFree(GetProcessHeap(), 0, glyphs[i].ordered_vertices.items);
6420         }
6421         HeapFree(GetProcessHeap(), 0, glyphs);
6422     }
6423     if (triangulations.items) {
6424         int i;
6425         for (i = 0; i < triangulations.count; i++)
6426             HeapFree(GetProcessHeap(), 0, triangulations.items[i].vertex_stack.items);
6427         HeapFree(GetProcessHeap(), 0, triangulations.items);
6428     }
6429     HeapFree(GetProcessHeap(), 0, raw_outline);
6430     if (oldfont) SelectObject(hdc, oldfont);
6431     if (font) DeleteObject(font);
6432 
6433     return hr;
6434 }
6435 
6436 HRESULT WINAPI D3DXValidMesh(ID3DXMesh *mesh, const DWORD *adjacency, ID3DXBuffer **errors_and_warnings)
6437 {
6438     FIXME("(%p, %p, %p): stub\n", mesh, adjacency, *errors_and_warnings);
6439 
6440     return E_NOTIMPL;
6441 }
6442 
6443 static BOOL weld_float1(void *to, void *from, FLOAT epsilon)
6444 {
6445     FLOAT *v1 = to;
6446     FLOAT *v2 = from;
6447 
6448     if (fabsf(*v1 - *v2) <= epsilon)
6449     {
6450         *v1 = *v2;
6451 
6452         return TRUE;
6453     }
6454 
6455     return FALSE;
6456 }
6457 
6458 static BOOL weld_float2(void *to, void *from, FLOAT epsilon)
6459 {
6460     D3DXVECTOR2 *v1 = to;
6461     D3DXVECTOR2 *v2 = from;
6462     FLOAT diff_x = fabsf(v1->x - v2->x);
6463     FLOAT diff_y = fabsf(v1->y - v2->y);
6464     FLOAT max_abs_diff = max(diff_x, diff_y);
6465 
6466     if (max_abs_diff <= epsilon)
6467     {
6468         memcpy(to, from, sizeof(D3DXVECTOR2));
6469 
6470         return TRUE;
6471     }
6472 
6473     return FALSE;
6474 }
6475 
6476 static BOOL weld_float3(void *to, void *from, FLOAT epsilon)
6477 {
6478     D3DXVECTOR3 *v1 = to;
6479     D3DXVECTOR3 *v2 = from;
6480     FLOAT diff_x = fabsf(v1->x - v2->x);
6481     FLOAT diff_y = fabsf(v1->y - v2->y);
6482     FLOAT diff_z = fabsf(v1->z - v2->z);
6483     FLOAT max_abs_diff = max(diff_x, diff_y);
6484     max_abs_diff = max(diff_z, max_abs_diff);
6485 
6486     if (max_abs_diff <= epsilon)
6487     {
6488         memcpy(to, from, sizeof(D3DXVECTOR3));
6489 
6490         return TRUE;
6491     }
6492 
6493     return FALSE;
6494 }
6495 
6496 static BOOL weld_float4(void *to, void *from, FLOAT epsilon)
6497 {
6498     D3DXVECTOR4 *v1 = to;
6499     D3DXVECTOR4 *v2 = from;
6500     FLOAT diff_x = fabsf(v1->x - v2->x);
6501     FLOAT diff_y = fabsf(v1->y - v2->y);
6502     FLOAT diff_z = fabsf(v1->z - v2->z);
6503     FLOAT diff_w = fabsf(v1->w - v2->w);
6504     FLOAT max_abs_diff = max(diff_x, diff_y);
6505     max_abs_diff = max(diff_z, max_abs_diff);
6506     max_abs_diff = max(diff_w, max_abs_diff);
6507 
6508     if (max_abs_diff <= epsilon)
6509     {
6510         memcpy(to, from, sizeof(D3DXVECTOR4));
6511 
6512         return TRUE;
6513     }
6514 
6515     return FALSE;
6516 }
6517 
6518 static BOOL weld_ubyte4(void *to, void *from, FLOAT epsilon)
6519 {
6520     BYTE *b1 = to;
6521     BYTE *b2 = from;
6522     BYTE truncated_epsilon = (BYTE)epsilon;
6523     BYTE diff_x = b1[0] > b2[0] ? b1[0] - b2[0] : b2[0] - b1[0];
6524     BYTE diff_y = b1[1] > b2[1] ? b1[1] - b2[1] : b2[1] - b1[1];
6525     BYTE diff_z = b1[2] > b2[2] ? b1[2] - b2[2] : b2[2] - b1[2];
6526     BYTE diff_w = b1[3] > b2[3] ? b1[3] - b2[3] : b2[3] - b1[3];
6527     BYTE max_diff = max(diff_x, diff_y);
6528     max_diff = max(diff_z, max_diff);
6529     max_diff = max(diff_w, max_diff);
6530 
6531     if (max_diff <= truncated_epsilon)
6532     {
6533         memcpy(to, from, 4 * sizeof(BYTE));
6534 
6535         return TRUE;
6536     }
6537 
6538     return FALSE;
6539 }
6540 
6541 static BOOL weld_ubyte4n(void *to, void *from, FLOAT epsilon)
6542 {
6543     return weld_ubyte4(to, from, epsilon * UCHAR_MAX);
6544 }
6545 
6546 static BOOL weld_d3dcolor(void *to, void *from, FLOAT epsilon)
6547 {
6548     return weld_ubyte4n(to, from, epsilon);
6549 }
6550 
6551 static BOOL weld_short2(void *to, void *from, FLOAT epsilon)
6552 {
6553     SHORT *s1 = to;
6554     SHORT *s2 = from;
6555     SHORT truncated_epsilon = (SHORT)epsilon;
6556     SHORT diff_x = abs(s1[0] - s2[0]);
6557     SHORT diff_y = abs(s1[1] - s2[1]);
6558     SHORT max_abs_diff = max(diff_x, diff_y);
6559 
6560     if (max_abs_diff <= truncated_epsilon)
6561     {
6562         memcpy(to, from, 2 * sizeof(SHORT));
6563 
6564         return TRUE;
6565     }
6566 
6567     return FALSE;
6568 }
6569 
6570 static BOOL weld_short2n(void *to, void *from, FLOAT epsilon)
6571 {
6572     return weld_short2(to, from, epsilon * SHRT_MAX);
6573 }
6574 
6575 static BOOL weld_short4(void *to, void *from, FLOAT epsilon)
6576 {
6577     SHORT *s1 = to;
6578     SHORT *s2 = from;
6579     SHORT truncated_epsilon = (SHORT)epsilon;
6580     SHORT diff_x = abs(s1[0] - s2[0]);
6581     SHORT diff_y = abs(s1[1] - s2[1]);
6582     SHORT diff_z = abs(s1[2] - s2[2]);
6583     SHORT diff_w = abs(s1[3] - s2[3]);
6584     SHORT max_abs_diff = max(diff_x, diff_y);
6585     max_abs_diff = max(diff_z, max_abs_diff);
6586     max_abs_diff = max(diff_w, max_abs_diff);
6587 
6588     if (max_abs_diff <= truncated_epsilon)
6589     {
6590         memcpy(to, from, 4 * sizeof(SHORT));
6591 
6592         return TRUE;
6593     }
6594 
6595     return FALSE;
6596 }
6597 
6598 static BOOL weld_short4n(void *to, void *from, FLOAT epsilon)
6599 {
6600     return weld_short4(to, from, epsilon * SHRT_MAX);
6601 }
6602 
6603 static BOOL weld_ushort2n(void *to, void *from, FLOAT epsilon)
6604 {
6605     USHORT *s1 = to;
6606     USHORT *s2 = from;
6607     USHORT scaled_epsilon = (USHORT)(epsilon * USHRT_MAX);
6608     USHORT diff_x = s1[0] > s2[0] ? s1[0] - s2[0] : s2[0] - s1[0];
6609     USHORT diff_y = s1[1] > s2[1] ? s1[1] - s2[1] : s2[1] - s1[1];
6610     USHORT max_diff = max(diff_x, diff_y);
6611 
6612     if (max_diff <= scaled_epsilon)
6613     {
6614         memcpy(to, from, 2 * sizeof(USHORT));
6615 
6616         return TRUE;
6617     }
6618 
6619     return FALSE;
6620 }
6621 
6622 static BOOL weld_ushort4n(void *to, void *from, FLOAT epsilon)
6623 {
6624     USHORT *s1 = to;
6625     USHORT *s2 = from;
6626     USHORT scaled_epsilon = (USHORT)(epsilon * USHRT_MAX);
6627     USHORT diff_x = s1[0] > s2[0] ? s1[0] - s2[0] : s2[0] - s1[0];
6628     USHORT diff_y = s1[1] > s2[1] ? s1[1] - s2[1] : s2[1] - s1[1];
6629     USHORT diff_z = s1[2] > s2[2] ? s1[2] - s2[2] : s2[2] - s1[2];
6630     USHORT diff_w = s1[3] > s2[3] ? s1[3] - s2[3] : s2[3] - s1[3];
6631     USHORT max_diff = max(diff_x, diff_y);
6632     max_diff = max(diff_z, max_diff);
6633     max_diff = max(diff_w, max_diff);
6634 
6635     if (max_diff <= scaled_epsilon)
6636     {
6637         memcpy(to, from, 4 * sizeof(USHORT));
6638 
6639         return TRUE;
6640     }
6641 
6642     return FALSE;
6643 }
6644 
6645 struct udec3
6646 {
6647     UINT x;
6648     UINT y;
6649     UINT z;
6650     UINT w;
6651 };
6652 
6653 static struct udec3 dword_to_udec3(DWORD d)
6654 {
6655     struct udec3 v;
6656 
6657     v.x = d & 0x3ff;
6658     v.y = (d & 0xffc00) >> 10;
6659     v.z = (d & 0x3ff00000) >> 20;
6660     v.w = (d & 0xc0000000) >> 30;
6661 
6662     return v;
6663 }
6664 
6665 static BOOL weld_udec3(void *to, void *from, FLOAT epsilon)
6666 {
6667     DWORD *d1 = to;
6668     DWORD *d2 = from;
6669     struct udec3 v1 = dword_to_udec3(*d1);
6670     struct udec3 v2 = dword_to_udec3(*d2);
6671     UINT truncated_epsilon = (UINT)epsilon;
6672     UINT diff_x = v1.x > v2.x ? v1.x - v2.x : v2.x - v1.x;
6673     UINT diff_y = v1.y > v2.y ? v1.y - v2.y : v2.y - v1.y;
6674     UINT diff_z = v1.z > v2.z ? v1.z - v2.z : v2.z - v1.z;
6675     UINT diff_w = v1.w > v2.w ? v1.w - v2.w : v2.w - v1.w;
6676     UINT max_diff = max(diff_x, diff_y);
6677     max_diff = max(diff_z, max_diff);
6678     max_diff = max(diff_w, max_diff);
6679 
6680     if (max_diff <= truncated_epsilon)
6681     {
6682         memcpy(to, from, sizeof(DWORD));
6683 
6684         return TRUE;
6685     }
6686 
6687     return FALSE;
6688 }
6689 
6690 struct dec3n
6691 {
6692     INT x;
6693     INT y;
6694     INT z;
6695     INT w;
6696 };
6697 
6698 static struct dec3n dword_to_dec3n(DWORD d)
6699 {
6700     struct dec3n v;
6701 
6702     v.x = d & 0x3ff;
6703     v.y = (d & 0xffc00) >> 10;
6704     v.z = (d & 0x3ff00000) >> 20;
6705     v.w = (d & 0xc0000000) >> 30;
6706 
6707     return v;
6708 }
6709 
6710 static BOOL weld_dec3n(void *to, void *from, FLOAT epsilon)
6711 {
6712     const UINT MAX_DEC3N = 511;
6713     DWORD *d1 = to;
6714     DWORD *d2 = from;
6715     struct dec3n v1 = dword_to_dec3n(*d1);
6716     struct dec3n v2 = dword_to_dec3n(*d2);
6717     INT scaled_epsilon = (INT)(epsilon * MAX_DEC3N);
6718     INT diff_x = abs(v1.x - v2.x);
6719     INT diff_y = abs(v1.y - v2.y);
6720     INT diff_z = abs(v1.z - v2.z);
6721     INT diff_w = abs(v1.w - v2.w);
6722     INT max_abs_diff = max(diff_x, diff_y);
6723     max_abs_diff = max(diff_z, max_abs_diff);
6724     max_abs_diff = max(diff_w, max_abs_diff);
6725 
6726     if (max_abs_diff <= scaled_epsilon)
6727     {
6728         memcpy(to, from, sizeof(DWORD));
6729 
6730         return TRUE;
6731     }
6732 
6733     return FALSE;
6734 }
6735 
6736 static BOOL weld_float16_2(void *to, void *from, FLOAT epsilon)
6737 {
6738     D3DXFLOAT16 *v1_float16 = to;
6739     D3DXFLOAT16 *v2_float16 = from;
6740     FLOAT diff_x;
6741     FLOAT diff_y;
6742     FLOAT max_abs_diff;
6743 #define NUM_ELEM 2
6744     FLOAT v1[NUM_ELEM];
6745     FLOAT v2[NUM_ELEM];
6746 
6747     D3DXFloat16To32Array(v1, v1_float16, NUM_ELEM);
6748     D3DXFloat16To32Array(v2, v2_float16, NUM_ELEM);
6749 
6750     diff_x = fabsf(v1[0] - v2[0]);
6751     diff_y = fabsf(v1[1] - v2[1]);
6752     max_abs_diff = max(diff_x, diff_y);
6753 
6754     if (max_abs_diff <= epsilon)
6755     {
6756         memcpy(to, from, NUM_ELEM * sizeof(D3DXFLOAT16));
6757 
6758         return TRUE;
6759     }
6760 
6761     return FALSE;
6762 #undef NUM_ELEM
6763 }
6764 
6765 static BOOL weld_float16_4(void *to, void *from, FLOAT epsilon)
6766 {
6767     D3DXFLOAT16 *v1_float16 = to;
6768     D3DXFLOAT16 *v2_float16 = from;
6769     FLOAT diff_x;
6770     FLOAT diff_y;
6771     FLOAT diff_z;
6772     FLOAT diff_w;
6773     FLOAT max_abs_diff;
6774 #define NUM_ELEM 4
6775     FLOAT v1[NUM_ELEM];
6776     FLOAT v2[NUM_ELEM];
6777 
6778     D3DXFloat16To32Array(v1, v1_float16, NUM_ELEM);
6779     D3DXFloat16To32Array(v2, v2_float16, NUM_ELEM);
6780 
6781     diff_x = fabsf(v1[0] - v2[0]);
6782     diff_y = fabsf(v1[1] - v2[1]);
6783     diff_z = fabsf(v1[2] - v2[2]);
6784     diff_w = fabsf(v1[3] - v2[3]);
6785     max_abs_diff = max(diff_x, diff_y);
6786     max_abs_diff = max(diff_z, max_abs_diff);
6787     max_abs_diff = max(diff_w, max_abs_diff);
6788 
6789     if (max_abs_diff <= epsilon)
6790     {
6791         memcpy(to, from, NUM_ELEM * sizeof(D3DXFLOAT16));
6792 
6793         return TRUE;
6794     }
6795 
6796     return FALSE;
6797 #undef NUM_ELEM
6798 }
6799 
6800 /* Sets the vertex components to the same value if they are within epsilon. */
6801 static BOOL weld_component(void *to, void *from, D3DDECLTYPE type, FLOAT epsilon)
6802 {
6803     /* Quiet FIXMEs as this is in a loop with potentially thousand of iterations. */
6804     BOOL fixme_once_unused = FALSE;
6805     BOOL fixme_once_unknown = FALSE;
6806 
6807     switch (type)
6808     {
6809         case D3DDECLTYPE_FLOAT1:
6810             return weld_float1(to, from, epsilon);
6811 
6812         case D3DDECLTYPE_FLOAT2:
6813             return weld_float2(to, from, epsilon);
6814 
6815         case D3DDECLTYPE_FLOAT3:
6816             return weld_float3(to, from, epsilon);
6817 
6818         case D3DDECLTYPE_FLOAT4:
6819             return weld_float4(to, from, epsilon);
6820 
6821         case D3DDECLTYPE_D3DCOLOR:
6822             return weld_d3dcolor(to, from, epsilon);
6823 
6824         case D3DDECLTYPE_UBYTE4:
6825             return weld_ubyte4(to, from, epsilon);
6826 
6827         case D3DDECLTYPE_SHORT2:
6828             return weld_short2(to, from, epsilon);
6829 
6830         case D3DDECLTYPE_SHORT4:
6831             return weld_short4(to, from, epsilon);
6832 
6833         case D3DDECLTYPE_UBYTE4N:
6834             return weld_ubyte4n(to, from, epsilon);
6835 
6836         case D3DDECLTYPE_SHORT2N:
6837             return weld_short2n(to, from, epsilon);
6838 
6839         case D3DDECLTYPE_SHORT4N:
6840             return weld_short4n(to, from, epsilon);
6841 
6842         case D3DDECLTYPE_USHORT2N:
6843             return weld_ushort2n(to, from, epsilon);
6844 
6845         case D3DDECLTYPE_USHORT4N:
6846             return weld_ushort4n(to, from, epsilon);
6847 
6848         case D3DDECLTYPE_UDEC3:
6849             return weld_udec3(to, from, epsilon);
6850 
6851         case D3DDECLTYPE_DEC3N:
6852             return weld_dec3n(to, from, epsilon);
6853 
6854         case D3DDECLTYPE_FLOAT16_2:
6855             return weld_float16_2(to, from, epsilon);
6856 
6857         case D3DDECLTYPE_FLOAT16_4:
6858             return weld_float16_4(to, from, epsilon);
6859 
6860         case D3DDECLTYPE_UNUSED:
6861             if (!fixme_once_unused++)
6862                 FIXME("D3DDECLTYPE_UNUSED welding not implemented.\n");
6863             break;
6864 
6865         default:
6866             if (!fixme_once_unknown++)
6867                 FIXME("Welding of unknown declaration type %d is not implemented.\n", type);
6868             break;
6869     }
6870 
6871     return FALSE;
6872 }
6873 
6874 static FLOAT get_component_epsilon(const D3DVERTEXELEMENT9 *decl_ptr, const D3DXWELDEPSILONS *epsilons)
6875 {
6876     FLOAT epsilon = 0.0f;
6877     /* Quiet FIXMEs as this is in a loop with potentially thousand of iterations. */
6878     static BOOL fixme_once_blendindices = FALSE;
6879     static BOOL fixme_once_positiont = FALSE;
6880     static BOOL fixme_once_fog = FALSE;
6881     static BOOL fixme_once_depth = FALSE;
6882     static BOOL fixme_once_sample = FALSE;
6883     static BOOL fixme_once_unknown = FALSE;
6884 
6885     switch (decl_ptr->Usage)
6886     {
6887         case D3DDECLUSAGE_POSITION:
6888             epsilon = epsilons->Position;
6889             break;
6890         case D3DDECLUSAGE_BLENDWEIGHT:
6891             epsilon = epsilons->BlendWeights;
6892             break;
6893         case D3DDECLUSAGE_NORMAL:
6894             epsilon = epsilons->Normals;
6895             break;
6896         case D3DDECLUSAGE_PSIZE:
6897             epsilon = epsilons->PSize;
6898             break;
6899         case D3DDECLUSAGE_TEXCOORD:
6900         {
6901             BYTE usage_index = decl_ptr->UsageIndex;
6902             if (usage_index > 7)
6903                 usage_index = 7;
6904             epsilon = epsilons->Texcoords[usage_index];
6905             break;
6906         }
6907         case D3DDECLUSAGE_TANGENT:
6908             epsilon = epsilons->Tangent;
6909             break;
6910         case D3DDECLUSAGE_BINORMAL:
6911             epsilon = epsilons->Binormal;
6912             break;
6913         case D3DDECLUSAGE_TESSFACTOR:
6914             epsilon = epsilons->TessFactor;
6915             break;
6916         case D3DDECLUSAGE_COLOR:
6917             if (decl_ptr->UsageIndex == 0)
6918                 epsilon = epsilons->Diffuse;
6919             else if (decl_ptr->UsageIndex == 1)
6920                 epsilon = epsilons->Specular;
6921             else
6922                 epsilon = 1e-6f;
6923             break;
6924         case D3DDECLUSAGE_BLENDINDICES:
6925             if (!fixme_once_blendindices++)
6926                 FIXME("D3DDECLUSAGE_BLENDINDICES welding not implemented.\n");
6927             break;
6928         case D3DDECLUSAGE_POSITIONT:
6929             if (!fixme_once_positiont++)
6930                 FIXME("D3DDECLUSAGE_POSITIONT welding not implemented.\n");
6931             break;
6932         case D3DDECLUSAGE_FOG:
6933             if (!fixme_once_fog++)
6934                 FIXME("D3DDECLUSAGE_FOG welding not implemented.\n");
6935             break;
6936         case D3DDECLUSAGE_DEPTH:
6937             if (!fixme_once_depth++)
6938                 FIXME("D3DDECLUSAGE_DEPTH welding not implemented.\n");
6939             break;
6940         case D3DDECLUSAGE_SAMPLE:
6941             if (!fixme_once_sample++)
6942                 FIXME("D3DDECLUSAGE_SAMPLE welding not implemented.\n");
6943             break;
6944         default:
6945             if (!fixme_once_unknown++)
6946                 FIXME("Unknown usage %x\n", decl_ptr->Usage);
6947             break;
6948     }
6949 
6950     return epsilon;
6951 }
6952 
6953 /* Helper function for reading a 32-bit index buffer. */
6954 static inline DWORD read_ib(void *index_buffer, BOOL indices_are_32bit,
6955                             DWORD index)
6956 {
6957     if (indices_are_32bit)
6958     {
6959         DWORD *indices = index_buffer;
6960         return indices[index];
6961     }
6962     else
6963     {
6964         WORD *indices = index_buffer;
6965         return indices[index];
6966     }
6967 }
6968 
6969 /* Helper function for writing to a 32-bit index buffer. */
6970 static inline void write_ib(void *index_buffer, BOOL indices_are_32bit,
6971                             DWORD index, DWORD value)
6972 {
6973     if (indices_are_32bit)
6974     {
6975         DWORD *indices = index_buffer;
6976         indices[index] = value;
6977     }
6978     else
6979     {
6980         WORD *indices = index_buffer;
6981         indices[index] = value;
6982     }
6983 }
6984 
6985 /*************************************************************************
6986  * D3DXWeldVertices    (D3DX9_36.@)
6987  *
6988  * Welds together similar vertices. The similarity between vert-
6989  * ices can be the position and other components such as
6990  * normal and color.
6991  *
6992  * PARAMS
6993  *   mesh             [I] Mesh which vertices will be welded together.
6994  *   flags            [I] D3DXWELDEPSILONSFLAGS specifying how to weld.
6995  *   epsilons         [I] How similar a component needs to be for welding.
6996  *   adjacency        [I] Which faces are adjacent to other faces.
6997  *   adjacency_out    [O] Updated adjacency after welding.
6998  *   face_remap_out   [O] Which faces the old faces have been mapped to.
6999  *   vertex_remap_out [O] Which vertices the old vertices have been mapped to.
7000  *
7001  * RETURNS
7002  *   Success: D3D_OK.
7003  *   Failure: D3DERR_INVALIDCALL, E_OUTOFMEMORY.
7004  *
7005  * BUGS
7006  *   Attribute sorting not implemented.
7007  *
7008  */
7009 HRESULT WINAPI D3DXWeldVertices(ID3DXMesh *mesh, DWORD flags, const D3DXWELDEPSILONS *epsilons,
7010         const DWORD *adjacency, DWORD *adjacency_out, DWORD *face_remap_out, ID3DXBuffer **vertex_remap_out)
7011 {
7012     DWORD *adjacency_generated = NULL;
7013     const DWORD *adjacency_ptr;
7014     DWORD *attributes = NULL;
7015     const FLOAT DEFAULT_EPSILON = 1.0e-6f;
7016     HRESULT hr;
7017     DWORD i;
7018     void *indices = NULL;
7019     BOOL indices_are_32bit = mesh->lpVtbl->GetOptions(mesh) & D3DXMESH_32BIT;
7020     DWORD optimize_flags;
7021     DWORD *point_reps = NULL;
7022     struct d3dx9_mesh *This = impl_from_ID3DXMesh(mesh);
7023     DWORD *vertex_face_map = NULL;
7024     BYTE *vertices = NULL;
7025 
7026     TRACE("mesh %p, flags %#x, epsilons %p, adjacency %p, adjacency_out %p, face_remap_out %p, vertex_remap_out %p.\n",
7027             mesh, flags, epsilons, adjacency, adjacency_out, face_remap_out, vertex_remap_out);
7028 
7029     if (flags == 0)
7030     {
7031         WARN("No flags are undefined. Using D3DXWELDEPSILONS_WELDPARTIALMATCHES instead.\n");
7032         flags = D3DXWELDEPSILONS_WELDPARTIALMATCHES;
7033     }
7034 
7035     if (adjacency) /* Use supplied adjacency. */
7036     {
7037         adjacency_ptr = adjacency;
7038     }
7039     else /* Adjacency has to be generated. */
7040     {
7041         adjacency_generated = HeapAlloc(GetProcessHeap(), 0, 3 * This->numfaces * sizeof(*adjacency_generated));
7042         if (!adjacency_generated)
7043         {
7044             ERR("Couldn't allocate memory for adjacency_generated.\n");
7045             hr = E_OUTOFMEMORY;
7046             goto cleanup;
7047         }
7048         hr = mesh->lpVtbl->GenerateAdjacency(mesh, DEFAULT_EPSILON, adjacency_generated);
7049         if (FAILED(hr))
7050         {
7051             ERR("Couldn't generate adjacency.\n");
7052             goto cleanup;
7053         }
7054         adjacency_ptr = adjacency_generated;
7055     }
7056 
7057     /* Point representation says which vertices can be replaced. */
7058     point_reps = HeapAlloc(GetProcessHeap(), 0, This->numvertices * sizeof(*point_reps));
7059     if (!point_reps)
7060     {
7061         hr = E_OUTOFMEMORY;
7062         ERR("Couldn't allocate memory for point_reps.\n");
7063         goto cleanup;
7064     }
7065     hr = mesh->lpVtbl->ConvertAdjacencyToPointReps(mesh, adjacency_ptr, point_reps);
7066     if (FAILED(hr))
7067     {
7068         ERR("ConvertAdjacencyToPointReps failed.\n");
7069         goto cleanup;
7070     }
7071 
7072     hr = mesh->lpVtbl->LockIndexBuffer(mesh, 0, &indices);
7073     if (FAILED(hr))
7074     {
7075         ERR("Couldn't lock index buffer.\n");
7076         goto cleanup;
7077     }
7078 
7079     hr = mesh->lpVtbl->LockAttributeBuffer(mesh, 0, &attributes);
7080     if (FAILED(hr))
7081     {
7082         ERR("Couldn't lock attribute buffer.\n");
7083         goto cleanup;
7084     }
7085     vertex_face_map = HeapAlloc(GetProcessHeap(), 0, This->numvertices * sizeof(*vertex_face_map));
7086     if (!vertex_face_map)
7087     {
7088         hr = E_OUTOFMEMORY;
7089         ERR("Couldn't allocate memory for vertex_face_map.\n");
7090         goto cleanup;
7091     }
7092     /* Build vertex face map, so that a vertex's face can be looked up. */
7093     for (i = 0; i < This->numfaces; i++)
7094     {
7095         DWORD j;
7096         for (j = 0; j < 3; j++)
7097         {
7098             DWORD index = read_ib(indices, indices_are_32bit, 3*i + j);
7099             vertex_face_map[index] = i;
7100         }
7101     }
7102 
7103     if (flags & D3DXWELDEPSILONS_WELDPARTIALMATCHES)
7104     {
7105         hr = mesh->lpVtbl->LockVertexBuffer(mesh, 0, (void**)&vertices);
7106         if (FAILED(hr))
7107         {
7108             ERR("Couldn't lock vertex buffer.\n");
7109             goto cleanup;
7110         }
7111         /* For each vertex that can be removed, compare its vertex components
7112          * with the vertex components from the vertex that can replace it. A
7113          * vertex is only fully replaced if all the components match and the
7114          * flag D3DXWELDEPSILONS_DONOTREMOVEVERTICES is not set, and they
7115          * belong to the same attribute group. Otherwise the vertex components
7116          * that are within epsilon are set to the same value.
7117          */
7118         for (i = 0; i < 3 * This->numfaces; i++)
7119         {
7120             D3DVERTEXELEMENT9 *decl_ptr;
7121             DWORD vertex_size = mesh->lpVtbl->GetNumBytesPerVertex(mesh);
7122             DWORD num_vertex_components;
7123             INT matches = 0;
7124             BOOL all_match;
7125             DWORD index = read_ib(indices, indices_are_32bit, i);
7126 
7127             for (decl_ptr = This->cached_declaration, num_vertex_components = 0; decl_ptr->Stream != 0xFF; decl_ptr++, num_vertex_components++)
7128             {
7129                 BYTE *to = &vertices[vertex_size*index + decl_ptr->Offset];
7130                 BYTE *from = &vertices[vertex_size*point_reps[index] + decl_ptr->Offset];
7131                 FLOAT epsilon = get_component_epsilon(decl_ptr, epsilons);
7132 
7133                 /* Don't weld self */
7134                 if (index == point_reps[index])
7135                 {
7136                     matches++;
7137                     continue;
7138                 }
7139 
7140                 if (weld_component(to, from, decl_ptr->Type, epsilon))
7141                     matches++;
7142             }
7143 
7144             all_match = (num_vertex_components == matches);
7145             if (all_match && !(flags & D3DXWELDEPSILONS_DONOTREMOVEVERTICES))
7146             {
7147                 DWORD to_face = vertex_face_map[index];
7148                 DWORD from_face = vertex_face_map[point_reps[index]];
7149                 if(attributes[to_face] != attributes[from_face] && !(flags & D3DXWELDEPSILONS_DONOTSPLIT))
7150                     continue;
7151                 write_ib(indices, indices_are_32bit, i, point_reps[index]);
7152             }
7153         }
7154         mesh->lpVtbl->UnlockVertexBuffer(mesh);
7155         vertices = NULL;
7156     }
7157     else if (flags & D3DXWELDEPSILONS_WELDALL)
7158     {
7159         for (i = 0; i < 3 * This->numfaces; i++)
7160         {
7161             DWORD index = read_ib(indices, indices_are_32bit, i);
7162             DWORD to_face = vertex_face_map[index];
7163             DWORD from_face = vertex_face_map[point_reps[index]];
7164             if(attributes[to_face] != attributes[from_face] && !(flags & D3DXWELDEPSILONS_DONOTSPLIT))
7165                 continue;
7166             write_ib(indices, indices_are_32bit, i, point_reps[index]);
7167         }
7168     }
7169     mesh->lpVtbl->UnlockAttributeBuffer(mesh);
7170     attributes = NULL;
7171     mesh->lpVtbl->UnlockIndexBuffer(mesh);
7172     indices = NULL;
7173 
7174     /* Compact mesh using OptimizeInplace */
7175     optimize_flags = D3DXMESHOPT_COMPACT;
7176     hr = mesh->lpVtbl->OptimizeInplace(mesh, optimize_flags, adjacency_ptr, adjacency_out, face_remap_out, vertex_remap_out);
7177     if (FAILED(hr))
7178     {
7179         ERR("Couldn't compact mesh.\n");
7180         goto cleanup;
7181     }
7182 
7183     hr = D3D_OK;
7184 cleanup:
7185     HeapFree(GetProcessHeap(), 0, adjacency_generated);
7186     HeapFree(GetProcessHeap(), 0, point_reps);
7187     HeapFree(GetProcessHeap(), 0, vertex_face_map);
7188     if (attributes) mesh->lpVtbl->UnlockAttributeBuffer(mesh);
7189     if (indices) mesh->lpVtbl->UnlockIndexBuffer(mesh);
7190     if (vertices) mesh->lpVtbl->UnlockVertexBuffer(mesh);
7191 
7192     return hr;
7193 }
7194 
7195 /*************************************************************************
7196  * D3DXOptimizeFaces    (D3DX9_36.@)
7197  *
7198  * Re-orders the faces so the vertex cache is used optimally.
7199  *
7200  * PARAMS
7201  *   indices           [I] Pointer to an index buffer belonging to a mesh.
7202  *   num_faces         [I] Number of faces in the mesh.
7203  *   num_vertices      [I] Number of vertices in the mesh.
7204  *   indices_are_32bit [I] Specifies whether indices are 32- or 16-bit.
7205  *   face_remap        [I/O] The new order the faces should be drawn in.
7206  *
7207  * RETURNS
7208  *   Success: D3D_OK.
7209  *   Failure: D3DERR_INVALIDCALL.
7210  *
7211  * BUGS
7212  *   The face re-ordering does not use the vertex cache optimally.
7213  *
7214  */
7215 HRESULT WINAPI D3DXOptimizeFaces(const void *indices, UINT num_faces,
7216         UINT num_vertices, BOOL indices_are_32bit, DWORD *face_remap)
7217 {
7218     UINT i;
7219     UINT j = num_faces - 1;
7220     UINT limit_16_bit = 2 << 15; /* According to MSDN */
7221     HRESULT hr = D3D_OK;
7222 
7223     FIXME("indices %p, num_faces %u, num_vertices %u, indices_are_32bit %#x, face_remap %p semi-stub. "
7224             "Face order will not be optimal.\n",
7225             indices, num_faces, num_vertices, indices_are_32bit, face_remap);
7226 
7227     if (!indices_are_32bit && num_faces >= limit_16_bit)
7228     {
7229         WARN("Number of faces must be less than %d when using 16-bit indices.\n",
7230              limit_16_bit);
7231         hr = D3DERR_INVALIDCALL;
7232         goto error;
7233     }
7234 
7235     if (!face_remap)
7236     {
7237         WARN("Face remap pointer is NULL.\n");
7238         hr = D3DERR_INVALIDCALL;
7239         goto error;
7240     }
7241 
7242     /* The faces are drawn in reverse order for simple meshes. This ordering
7243      * is not optimal for complicated meshes, but will not break anything
7244      * either. The ordering should be changed to take advantage of the vertex
7245      * cache on the graphics card.
7246      *
7247      * TODO Re-order to take advantage of vertex cache.
7248      */
7249     for (i = 0; i < num_faces; i++)
7250     {
7251         face_remap[i] = j--;
7252     }
7253 
7254     return D3D_OK;
7255 
7256 error:
7257     return hr;
7258 }
7259 
7260 static D3DXVECTOR3 *vertex_element_vec3(BYTE *vertices, const D3DVERTEXELEMENT9 *declaration,
7261         DWORD vertex_stride, DWORD index)
7262 {
7263     return (D3DXVECTOR3 *)(vertices + declaration->Offset + index * vertex_stride);
7264 }
7265 
7266 static D3DXVECTOR3 read_vec3(BYTE *vertices, const D3DVERTEXELEMENT9 *declaration,
7267         DWORD vertex_stride, DWORD index)
7268 {
7269     D3DXVECTOR3 vec3 = {0};
7270     const D3DXVECTOR3 *src = vertex_element_vec3(vertices, declaration, vertex_stride, index);
7271 
7272     switch (declaration->Type)
7273     {
7274         case D3DDECLTYPE_FLOAT1:
7275             vec3.x = src->x;
7276             break;
7277         case D3DDECLTYPE_FLOAT2:
7278             vec3.x = src->x;
7279             vec3.y = src->y;
7280             break;
7281         case D3DDECLTYPE_FLOAT3:
7282         case D3DDECLTYPE_FLOAT4:
7283             vec3 = *src;
7284             break;
7285         default:
7286             ERR("Cannot read vec3\n");
7287             break;
7288     }
7289 
7290     return vec3;
7291 }
7292 
7293 /*************************************************************************
7294  * D3DXComputeTangentFrameEx    (D3DX9_36.@)
7295  */
7296 HRESULT WINAPI D3DXComputeTangentFrameEx(ID3DXMesh *mesh, DWORD texture_in_semantic, DWORD texture_in_index,
7297         DWORD u_partial_out_semantic, DWORD u_partial_out_index, DWORD v_partial_out_semantic,
7298         DWORD v_partial_out_index, DWORD normal_out_semantic, DWORD normal_out_index, DWORD options,
7299         const DWORD *adjacency, float partial_edge_threshold, float singular_point_threshold,
7300         float normal_edge_threshold, ID3DXMesh **mesh_out, ID3DXBuffer **vertex_mapping)
7301 {
7302     HRESULT hr;
7303     void *indices = NULL;
7304     BYTE *vertices = NULL;
7305     DWORD *point_reps = NULL;
7306     size_t normal_size;
7307     BOOL indices_are_32bit;
7308     DWORD i, j, num_faces, num_vertices, vertex_stride;
7309     D3DVERTEXELEMENT9 declaration[MAX_FVF_DECL_SIZE] = {D3DDECL_END()};
7310     D3DVERTEXELEMENT9 *position_declaration = NULL, *normal_declaration = NULL;
7311     DWORD weighting_method = options & (D3DXTANGENT_WEIGHT_EQUAL | D3DXTANGENT_WEIGHT_BY_AREA);
7312 
7313     TRACE("mesh %p, texture_in_semantic %u, texture_in_index %u, u_partial_out_semantic %u, u_partial_out_index %u, "
7314             "v_partial_out_semantic %u, v_partial_out_index %u, normal_out_semantic %u, normal_out_index %u, "
7315             "options %#x, adjacency %p, partial_edge_threshold %f, singular_point_threshold %f, "
7316             "normal_edge_threshold %f, mesh_out %p, vertex_mapping %p\n",
7317             mesh, texture_in_semantic, texture_in_index, u_partial_out_semantic, u_partial_out_index,
7318             v_partial_out_semantic, v_partial_out_index, normal_out_semantic, normal_out_index, options, adjacency,
7319             partial_edge_threshold, singular_point_threshold, normal_edge_threshold, mesh_out, vertex_mapping);
7320 
7321     if (!mesh)
7322     {
7323         WARN("mesh is NULL\n");
7324         return D3DERR_INVALIDCALL;
7325     }
7326 
7327     if (weighting_method == (D3DXTANGENT_WEIGHT_EQUAL | D3DXTANGENT_WEIGHT_BY_AREA))
7328     {
7329         WARN("D3DXTANGENT_WEIGHT_BY_AREA and D3DXTANGENT_WEIGHT_EQUAL are mutally exclusive\n");
7330         return D3DERR_INVALIDCALL;
7331     }
7332 
7333     if (u_partial_out_semantic != D3DX_DEFAULT)
7334     {
7335         FIXME("tangent vectors computation is not supported\n");
7336         return E_NOTIMPL;
7337     }
7338 
7339     if (v_partial_out_semantic != D3DX_DEFAULT)
7340     {
7341         FIXME("binormal vectors computation is not supported\n");
7342         return E_NOTIMPL;
7343     }
7344 
7345     if (options & ~(D3DXTANGENT_GENERATE_IN_PLACE | D3DXTANGENT_CALCULATE_NORMALS | D3DXTANGENT_WEIGHT_EQUAL | D3DXTANGENT_WEIGHT_BY_AREA))
7346     {
7347         FIXME("unsupported options %#x\n", options);
7348         return E_NOTIMPL;
7349     }
7350 
7351     if (!(options & D3DXTANGENT_CALCULATE_NORMALS))
7352     {
7353         FIXME("only normals computation is supported\n");
7354         return E_NOTIMPL;
7355     }
7356 
7357     if (!(options & D3DXTANGENT_GENERATE_IN_PLACE) || mesh_out || vertex_mapping)
7358     {
7359         FIXME("only D3DXTANGENT_GENERATE_IN_PLACE is supported\n");
7360         return E_NOTIMPL;
7361     }
7362 
7363     if (FAILED(hr = mesh->lpVtbl->GetDeclaration(mesh, declaration)))
7364         return hr;
7365 
7366     for (i = 0; declaration[i].Stream != 0xff; i++)
7367     {
7368         if (declaration[i].Usage == D3DDECLUSAGE_POSITION && !declaration[i].UsageIndex)
7369             position_declaration = &declaration[i];
7370         if (declaration[i].Usage == normal_out_semantic && declaration[i].UsageIndex == normal_out_index)
7371             normal_declaration = &declaration[i];
7372     }
7373 
7374     if (!position_declaration || !normal_declaration)
7375         return D3DERR_INVALIDCALL;
7376 
7377     if (normal_declaration->Type == D3DDECLTYPE_FLOAT3)
7378     {
7379         normal_size = sizeof(D3DXVECTOR3);
7380     }
7381     else if (normal_declaration->Type == D3DDECLTYPE_FLOAT4)
7382     {
7383         normal_size = sizeof(D3DXVECTOR4);
7384     }
7385     else
7386     {
7387         WARN("unsupported normals type %u\n", normal_declaration->Type);
7388         return D3DERR_INVALIDCALL;
7389     }
7390 
7391     num_faces = mesh->lpVtbl->GetNumFaces(mesh);
7392     num_vertices = mesh->lpVtbl->GetNumVertices(mesh);
7393     vertex_stride = mesh->lpVtbl->GetNumBytesPerVertex(mesh);
7394     indices_are_32bit = mesh->lpVtbl->GetOptions(mesh) & D3DXMESH_32BIT;
7395 
7396     point_reps = HeapAlloc(GetProcessHeap(), 0, num_vertices * sizeof(*point_reps));
7397     if (!point_reps)
7398     {
7399         hr = E_OUTOFMEMORY;
7400         goto done;
7401     }
7402 
7403     if (adjacency)
7404     {
7405         if (FAILED(hr = mesh->lpVtbl->ConvertAdjacencyToPointReps(mesh, adjacency, point_reps)))
7406             goto done;
7407     }
7408     else
7409     {
7410         for (i = 0; i < num_vertices; i++)
7411             point_reps[i] = i;
7412     }
7413 
7414     if (FAILED(hr = mesh->lpVtbl->LockIndexBuffer(mesh, 0, &indices)))
7415         goto done;
7416 
7417     if (FAILED(hr = mesh->lpVtbl->LockVertexBuffer(mesh, 0, (void **)&vertices)))
7418         goto done;
7419 
7420     for (i = 0; i < num_vertices; i++)
7421     {
7422         static const D3DXVECTOR4 default_vector = {0.0f, 0.0f, 0.0f, 1.0f};
7423         void *normal = vertices + normal_declaration->Offset + i * vertex_stride;
7424 
7425         memcpy(normal, &default_vector, normal_size);
7426     }
7427 
7428     for (i = 0; i < num_faces; i++)
7429     {
7430         float denominator, weights[3];
7431         D3DXVECTOR3 a, b, cross, face_normal;
7432         const DWORD face_indices[3] =
7433         {
7434             read_ib(indices, indices_are_32bit, 3 * i + 0),
7435             read_ib(indices, indices_are_32bit, 3 * i + 1),
7436             read_ib(indices, indices_are_32bit, 3 * i + 2)
7437         };
7438         const D3DXVECTOR3 v0 = read_vec3(vertices, position_declaration, vertex_stride, face_indices[0]);
7439         const D3DXVECTOR3 v1 = read_vec3(vertices, position_declaration, vertex_stride, face_indices[1]);
7440         const D3DXVECTOR3 v2 = read_vec3(vertices, position_declaration, vertex_stride, face_indices[2]);
7441 
7442         D3DXVec3Cross(&cross, D3DXVec3Subtract(&a, &v0, &v1), D3DXVec3Subtract(&b, &v0, &v2));
7443 
7444         switch (weighting_method)
7445         {
7446             case D3DXTANGENT_WEIGHT_EQUAL:
7447                 weights[0] = weights[1] = weights[2] = 1.0f;
7448                 break;
7449             case D3DXTANGENT_WEIGHT_BY_AREA:
7450                 weights[0] = weights[1] = weights[2] = D3DXVec3Length(&cross);
7451                 break;
7452             default:
7453                 /* weight by angle */
7454                 denominator = D3DXVec3Length(&a) * D3DXVec3Length(&b);
7455                 if (!denominator)
7456                     weights[0] = 0.0f;
7457                 else
7458                     weights[0] = acosf(D3DXVec3Dot(&a, &b) / denominator);
7459 
7460                 D3DXVec3Subtract(&a, &v1, &v0);
7461                 D3DXVec3Subtract(&b, &v1, &v2);
7462                 denominator = D3DXVec3Length(&a) * D3DXVec3Length(&b);
7463                 if (!denominator)
7464                     weights[1] = 0.0f;
7465                 else
7466                     weights[1] = acosf(D3DXVec3Dot(&a, &b) / denominator);
7467 
7468                 D3DXVec3Subtract(&a, &v2, &v0);
7469                 D3DXVec3Subtract(&b, &v2, &v1);
7470                 denominator = D3DXVec3Length(&a) * D3DXVec3Length(&b);
7471                 if (!denominator)
7472                     weights[2] = 0.0f;
7473                 else
7474                     weights[2] = acosf(D3DXVec3Dot(&a, &b) / denominator);
7475 
7476                 break;
7477         }
7478 
7479         D3DXVec3Normalize(&face_normal, &cross);
7480 
7481         for (j = 0; j < 3; j++)
7482         {
7483             D3DXVECTOR3 normal;
7484             DWORD rep_index = point_reps[face_indices[j]];
7485             D3DXVECTOR3 *rep_normal = vertex_element_vec3(vertices, normal_declaration, vertex_stride, rep_index);
7486 
7487             D3DXVec3Scale(&normal, &face_normal, weights[j]);
7488             D3DXVec3Add(rep_normal, rep_normal, &normal);
7489         }
7490     }
7491 
7492     for (i = 0; i < num_vertices; i++)
7493     {
7494         DWORD rep_index = point_reps[i];
7495         D3DXVECTOR3 *normal = vertex_element_vec3(vertices, normal_declaration, vertex_stride, i);
7496         D3DXVECTOR3 *rep_normal = vertex_element_vec3(vertices, normal_declaration, vertex_stride, rep_index);
7497 
7498         if (i == rep_index)
7499             D3DXVec3Normalize(rep_normal, rep_normal);
7500         else
7501             *normal = *rep_normal;
7502     }
7503 
7504     hr = D3D_OK;
7505 
7506 done:
7507     if (vertices)
7508         mesh->lpVtbl->UnlockVertexBuffer(mesh);
7509 
7510     if (indices)
7511         mesh->lpVtbl->UnlockIndexBuffer(mesh);
7512 
7513     HeapFree(GetProcessHeap(), 0, point_reps);
7514 
7515     return hr;
7516 }
7517 
7518 /*************************************************************************
7519  * D3DXComputeNormals    (D3DX9_36.@)
7520  */
7521 HRESULT WINAPI D3DXComputeNormals(struct ID3DXBaseMesh *mesh, const DWORD *adjacency)
7522 {
7523     TRACE("mesh %p, adjacency %p\n", mesh, adjacency);
7524 
7525     if (mesh && (ID3DXMeshVtbl *)mesh->lpVtbl != &D3DXMesh_Vtbl)
7526     {
7527         ERR("Invalid virtual table\n");
7528         return D3DERR_INVALIDCALL;
7529     }
7530 
7531     return D3DXComputeTangentFrameEx((ID3DXMesh *)mesh, D3DX_DEFAULT, 0,
7532             D3DX_DEFAULT, 0, D3DX_DEFAULT, 0, D3DDECLUSAGE_NORMAL, 0,
7533             D3DXTANGENT_GENERATE_IN_PLACE | D3DXTANGENT_CALCULATE_NORMALS,
7534             adjacency, -1.01f, -0.01f, -1.01f, NULL, NULL);
7535 }
7536 
7537 /*************************************************************************
7538  * D3DXIntersect    (D3DX9_36.@)
7539  */
7540 HRESULT WINAPI D3DXIntersect(ID3DXBaseMesh *mesh, const D3DXVECTOR3 *ray_pos, const D3DXVECTOR3 *ray_dir,
7541         BOOL *hit, DWORD *face_index, float *u, float *v, float *distance, ID3DXBuffer **all_hits, DWORD *count_of_hits)
7542 {
7543     FIXME("mesh %p, ray_pos %p, ray_dir %p, hit %p, face_index %p, u %p, v %p, distance %p, all_hits %p, "
7544             "count_of_hits %p stub!\n", mesh, ray_pos, ray_dir, hit, face_index, u, v, distance, all_hits, count_of_hits);
7545 
7546     return E_NOTIMPL;
7547 }
7548 
7549 HRESULT WINAPI D3DXTessellateNPatches(ID3DXMesh *mesh, const DWORD *adjacency_in, float num_segs,
7550         BOOL quadratic_normals, ID3DXMesh **mesh_out, ID3DXBuffer **adjacency_out)
7551 {
7552     FIXME("mesh %p, adjacency_in %p, num_segs %f, quadratic_normals %d, mesh_out %p, adjacency_out %p stub.\n",
7553             mesh, adjacency_in, num_segs, quadratic_normals, mesh_out, adjacency_out);
7554 
7555     return E_NOTIMPL;
7556 }
7557 
7558 HRESULT WINAPI D3DXConvertMeshSubsetToSingleStrip(struct ID3DXBaseMesh *mesh_in, DWORD attribute_id,
7559         DWORD ib_flags, struct IDirect3DIndexBuffer9 **index_buffer, DWORD *index_count)
7560 {
7561     FIXME("mesh_in %p, attribute_id %u, ib_flags %u, index_buffer %p, index_count %p stub.\n",
7562             mesh_in, attribute_id, ib_flags, index_buffer, index_count);
7563 
7564     return E_NOTIMPL;
7565 }
7566 
7567 struct frame_node
7568 {
7569     struct list entry;
7570     D3DXFRAME *frame;
7571 };
7572 
7573 static BOOL queue_frame_node(struct list *queue, D3DXFRAME *frame)
7574 {
7575     struct frame_node *node;
7576 
7577     if (!frame->pFrameFirstChild)
7578         return TRUE;
7579 
7580     node = HeapAlloc(GetProcessHeap(), 0, sizeof(*node));
7581     if (!node)
7582         return FALSE;
7583 
7584     node->frame = frame;
7585     list_add_tail(queue, &node->entry);
7586 
7587     return TRUE;
7588 }
7589 
7590 static void empty_frame_queue(struct list *queue)
7591 {
7592     struct frame_node *cur, *cur2;
7593     LIST_FOR_EACH_ENTRY_SAFE(cur, cur2, queue, struct frame_node, entry)
7594     {
7595         list_remove(&cur->entry);
7596         HeapFree(GetProcessHeap(), 0, cur);
7597     }
7598 }
7599 
7600 D3DXFRAME * WINAPI D3DXFrameFind(const D3DXFRAME *root, const char *name)
7601 {
7602     D3DXFRAME *found = NULL, *frame;
7603     struct list queue;
7604 
7605     TRACE("root frame %p, name %s.\n", root, debugstr_a(name));
7606 
7607     if (!root)
7608         return NULL;
7609 
7610     list_init(&queue);
7611 
7612     frame = (D3DXFRAME *)root;
7613 
7614     for (;;)
7615     {
7616         struct frame_node *node;
7617 
7618         while (frame)
7619         {
7620             if ((name && frame->Name && !strcmp(frame->Name, name)) || (!name && !frame->Name))
7621             {
7622                 found = frame;
7623                 goto cleanup;
7624             }
7625 
7626             if (!queue_frame_node(&queue, frame))
7627                 goto cleanup;
7628 
7629             frame = frame->pFrameSibling;
7630         }
7631 
7632         if (list_empty(&queue))
7633             break;
7634 
7635         node = LIST_ENTRY(list_head(&queue), struct frame_node, entry);
7636         list_remove(&node->entry);
7637         frame = node->frame->pFrameFirstChild;
7638         HeapFree(GetProcessHeap(), 0, node);
7639     }
7640 
7641 cleanup:
7642     empty_frame_queue(&queue);
7643 
7644     return found;
7645 }
7646