~ [ source navigation ] ~ [ diff markup ] ~ [ identifier search ] ~ [ freetext search ] ~ [ file search ] ~

Wine Cross Reference
wine/dlls/d3dx9_36/mesh.c

Version: ~ [ wine-1.5.4 ] ~ [ wine-1.5.3 ] ~ [ wine-1.5.2 ] ~ [ wine-1.5.1 ] ~ [ wine-1.5.0 ] ~ [ wine-1.4 ] ~ [ wine-1.4-rc6 ] ~ [ wine-1.4-rc5 ] ~ [ wine-1.4-rc4 ] ~ [ wine-1.4-rc3 ] ~ [ wine-1.4-rc2 ] ~ [ wine-1.4-rc1 ] ~ [ wine-1.3.37 ] ~ [ wine-1.3.36 ] ~ [ wine-1.3.35 ] ~ [ wine-1.3.34 ] ~ [ wine-1.3.33 ] ~ [ wine-1.3.32 ] ~ [ wine-1.3.31 ] ~ [ wine-1.3.30 ] ~ [ wine-1.3.29 ] ~ [ wine-1.3.28 ] ~ [ wine-1.3.27 ] ~ [ wine-1.3.26 ] ~ [ wine-1.3.25 ] ~ [ wine-1.3.24 ] ~ [ wine-1.3.23 ] ~ [ wine-1.3.22 ] ~ [ wine-1.3.21 ] ~ [ wine-1.3.20 ] ~ [ wine-1.3.19 ] ~ [ wine-1.3.18 ] ~ [ wine-1.2.3 ] ~ [ wine-1.3.17 ] ~ [ wine-1.3.16 ] ~ [ wine-1.3.15 ] ~ [ wine-1.3.14 ] ~ [ wine-1.3.13 ] ~ [ wine-1.3.12 ] ~ [ wine-1.3.11 ] ~ [ wine-1.3.10 ] ~ [ wine-1.3.9 ] ~ [ wine-1.2.2 ] ~ [ wine-1.3.8 ] ~ [ wine-1.3.7 ] ~ [ wine-1.3.6 ] ~ [ wine-1.3.5 ] ~ [ wine-1.2.1 ] ~ [ wine-1.3.4 ] ~ [ wine-1.3.3 ] ~ [ wine-1.3.2 ] ~ [ wine-1.3.1 ] ~ [ wine-1.3.0 ] ~ [ wine-1.2 ] ~ [ wine-1.2-rc7 ] ~ [ wine-1.2-rc6 ] ~ [ wine-1.2-rc5 ] ~ [ wine-1.2-rc4 ] ~ [ wine-1.2-rc3 ] ~ [ wine-1.2-rc2 ] ~ [ wine-1.2-rc1 ] ~ [ wine-1.1.44 ] ~ [ wine-1.1.43 ] ~ [ wine-1.1.42 ] ~ [ wine-1.1.41 ] ~ [ wine-1.1.40 ] ~ [ wine-1.1.39 ] ~ [ wine-1.1.38 ] ~ [ wine-1.1.37 ] ~ [ wine-1.1.36 ] ~ [ wine-1.1.35 ] ~ [ wine-1.1.34 ] ~ [ wine-1.1.33 ] ~ [ wine-1.1.32 ] ~ [ wine-1.1.31 ] ~ [ wine-1.1.30 ] ~ [ wine-1.1.29 ] ~ [ wine-1.1.28 ] ~ [ wine-1.1.27 ] ~ [ wine-1.1.26 ] ~ [ wine-1.1.25 ] ~ [ wine-1.1.24 ] ~ [ wine-1.1.23 ] ~ [ wine-1.1.22 ] ~ [ wine-1.1.21 ] ~ [ wine-1.1.20 ] ~ [ wine-1.1.19 ] ~ [ wine-1.1.18 ] ~ [ wine-1.1.17 ] ~ [ wine-1.1.16 ] ~ [ wine-1.1.15 ] ~ [ wine-1.1.14 ] ~ [ wine-1.1.13 ] ~ [ wine-1.1.12 ] ~ [ wine-1.1.11 ] ~ [ wine-1.1.10 ] ~ [ wine-1.1.9 ] ~ [ wine-1.1.8 ] ~ [ wine-1.1.7 ] ~ [ wine-1.0.1 ] ~ [ wine-1.1.6 ] ~ [ wine-1.1.5 ] ~ [ wine-1.1.4 ] ~ [ wine-1.1.3 ] ~ [ wine-1.1.2 ] ~ [ wine-1.1.1 ] ~ [ wine-1.1.0 ] ~ [ wine-1.0 ] ~

  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 Algoritm
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 }
6771 

~ [ source navigation ] ~ [ diff markup ] ~ [ identifier search ] ~ [ freetext search ] ~ [ file search ] ~

This page was automatically generated by the LXR engine.
Visit the LXR main site for more information.