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