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