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