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