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