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