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