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