[AMSTREAM] We don't need to define WIDL_C_INLINE_WRAPPERS here anymore.
[reactos.git] / dll / directx / wine / d3dx9_36 / mesh.c
1 /*
2 * Mesh operations specific to D3DX9.
3 *
4 * Copyright (C) 2005 Henri Verbeet
5 * Copyright (C) 2006 Ivan Gyurdiev
6 * Copyright (C) 2009 David Adam
7 * Copyright (C) 2010 Tony Wasserka
8 * Copyright (C) 2011 Dylan Smith
9 * Copyright (C) 2011 Michael Mc Donnell
10 * Copyright (C) 2013 Christian Costa
11 *
12 * This library is free software; you can redistribute it and/or
13 * modify it under the terms of the GNU Lesser General Public
14 * License as published by the Free Software Foundation; either
15 * version 2.1 of the License, or (at your option) any later version.
16 *
17 * This library is distributed in the hope that it will be useful,
18 * but WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
20 * Lesser General Public License for more details.
21 *
22 * You should have received a copy of the GNU Lesser General Public
23 * License along with this library; if not, write to the Free Software
24 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
25 */
26
27 #include "d3dx9_36_private.h"
28
29 #include <assert.h>
30 #ifdef HAVE_FLOAT_H
31 # include <float.h>
32 #endif
33
34 #undef MAKE_DDHRESULT
35 #include "dxfile.h"
36 #include "rmxfguid.h"
37 #include "rmxftmpl.h"
38
39 #include "wine/list.h"
40
41 struct d3dx9_mesh
42 {
43 ID3DXMesh ID3DXMesh_iface;
44 LONG ref;
45
46 DWORD numfaces;
47 DWORD numvertices;
48 DWORD options;
49 DWORD fvf;
50 IDirect3DDevice9 *device;
51 D3DVERTEXELEMENT9 cached_declaration[MAX_FVF_DECL_SIZE];
52 IDirect3DVertexDeclaration9 *vertex_declaration;
53 UINT vertex_declaration_size;
54 UINT num_elem;
55 IDirect3DVertexBuffer9 *vertex_buffer;
56 IDirect3DIndexBuffer9 *index_buffer;
57 DWORD *attrib_buffer;
58 int attrib_buffer_lock_count;
59 DWORD attrib_table_size;
60 D3DXATTRIBUTERANGE *attrib_table;
61 };
62
63 static const UINT d3dx_decltype_size[] =
64 {
65 /* D3DDECLTYPE_FLOAT1 */ sizeof(FLOAT),
66 /* D3DDECLTYPE_FLOAT2 */ sizeof(D3DXVECTOR2),
67 /* D3DDECLTYPE_FLOAT3 */ sizeof(D3DXVECTOR3),
68 /* D3DDECLTYPE_FLOAT4 */ sizeof(D3DXVECTOR4),
69 /* D3DDECLTYPE_D3DCOLOR */ sizeof(D3DCOLOR),
70 /* D3DDECLTYPE_UBYTE4 */ 4 * sizeof(BYTE),
71 /* D3DDECLTYPE_SHORT2 */ 2 * sizeof(SHORT),
72 /* D3DDECLTYPE_SHORT4 */ 4 * sizeof(SHORT),
73 /* D3DDECLTYPE_UBYTE4N */ 4 * sizeof(BYTE),
74 /* D3DDECLTYPE_SHORT2N */ 2 * sizeof(SHORT),
75 /* D3DDECLTYPE_SHORT4N */ 4 * sizeof(SHORT),
76 /* D3DDECLTYPE_USHORT2N */ 2 * sizeof(USHORT),
77 /* D3DDECLTYPE_USHORT4N */ 4 * sizeof(USHORT),
78 /* D3DDECLTYPE_UDEC3 */ 4, /* 3 * 10 bits + 2 padding */
79 /* D3DDECLTYPE_DEC3N */ 4,
80 /* D3DDECLTYPE_FLOAT16_2 */ 2 * sizeof(D3DXFLOAT16),
81 /* D3DDECLTYPE_FLOAT16_4 */ 4 * sizeof(D3DXFLOAT16),
82 };
83
84 static inline struct d3dx9_mesh *impl_from_ID3DXMesh(ID3DXMesh *iface)
85 {
86 return CONTAINING_RECORD(iface, struct d3dx9_mesh, ID3DXMesh_iface);
87 }
88
89 static HRESULT WINAPI d3dx9_mesh_QueryInterface(ID3DXMesh *iface, REFIID riid, void **out)
90 {
91 TRACE("iface %p, riid %s, out %p.\n", iface, debugstr_guid(riid), out);
92
93 if (IsEqualGUID(riid, &IID_IUnknown) ||
94 IsEqualGUID(riid, &IID_ID3DXBaseMesh) ||
95 IsEqualGUID(riid, &IID_ID3DXMesh))
96 {
97 iface->lpVtbl->AddRef(iface);
98 *out = iface;
99 return S_OK;
100 }
101
102 WARN("Interface %s not found.\n", debugstr_guid(riid));
103
104 return E_NOINTERFACE;
105 }
106
107 static ULONG WINAPI d3dx9_mesh_AddRef(ID3DXMesh *iface)
108 {
109 struct d3dx9_mesh *mesh = impl_from_ID3DXMesh(iface);
110 ULONG refcount = InterlockedIncrement(&mesh->ref);
111
112 TRACE("%p increasing refcount to %u.\n", mesh, refcount);
113
114 return refcount;
115 }
116
117 static ULONG WINAPI d3dx9_mesh_Release(ID3DXMesh *iface)
118 {
119 struct d3dx9_mesh *mesh = impl_from_ID3DXMesh(iface);
120 ULONG refcount = InterlockedDecrement(&mesh->ref);
121
122 TRACE("%p decreasing refcount to %u.\n", mesh, refcount);
123
124 if (!refcount)
125 {
126 IDirect3DIndexBuffer9_Release(mesh->index_buffer);
127 IDirect3DVertexBuffer9_Release(mesh->vertex_buffer);
128 if (mesh->vertex_declaration)
129 IDirect3DVertexDeclaration9_Release(mesh->vertex_declaration);
130 IDirect3DDevice9_Release(mesh->device);
131 HeapFree(GetProcessHeap(), 0, mesh->attrib_buffer);
132 HeapFree(GetProcessHeap(), 0, mesh->attrib_table);
133 HeapFree(GetProcessHeap(), 0, mesh);
134 }
135
136 return refcount;
137 }
138
139 static HRESULT WINAPI d3dx9_mesh_DrawSubset(ID3DXMesh *iface, DWORD attrib_id)
140 {
141 struct d3dx9_mesh *This = impl_from_ID3DXMesh(iface);
142 HRESULT hr;
143 DWORD face_start;
144 DWORD face_end = 0;
145 DWORD vertex_size;
146
147 TRACE("iface %p, attrib_id %u.\n", iface, attrib_id);
148
149 if (!This->vertex_declaration)
150 {
151 WARN("Can't draw a mesh with an invalid vertex declaration.\n");
152 return E_FAIL;
153 }
154
155 vertex_size = iface->lpVtbl->GetNumBytesPerVertex(iface);
156
157 hr = IDirect3DDevice9_SetVertexDeclaration(This->device, This->vertex_declaration);
158 if (FAILED(hr)) return hr;
159 hr = IDirect3DDevice9_SetStreamSource(This->device, 0, This->vertex_buffer, 0, vertex_size);
160 if (FAILED(hr)) return hr;
161 hr = IDirect3DDevice9_SetIndices(This->device, This->index_buffer);
162 if (FAILED(hr)) return hr;
163
164 while (face_end < This->numfaces)
165 {
166 for (face_start = face_end; face_start < This->numfaces; face_start++)
167 {
168 if (This->attrib_buffer[face_start] == attrib_id)
169 break;
170 }
171 if (face_start >= This->numfaces)
172 break;
173 for (face_end = face_start + 1; face_end < This->numfaces; face_end++)
174 {
175 if (This->attrib_buffer[face_end] != attrib_id)
176 break;
177 }
178
179 hr = IDirect3DDevice9_DrawIndexedPrimitive(This->device, D3DPT_TRIANGLELIST,
180 0, 0, This->numvertices, face_start * 3, face_end - face_start);
181 if (FAILED(hr)) return hr;
182 }
183
184 return D3D_OK;
185 }
186
187 static DWORD WINAPI d3dx9_mesh_GetNumFaces(ID3DXMesh *iface)
188 {
189 struct d3dx9_mesh *mesh = impl_from_ID3DXMesh(iface);
190
191 TRACE("iface %p.\n", iface);
192
193 return mesh->numfaces;
194 }
195
196 static DWORD WINAPI d3dx9_mesh_GetNumVertices(ID3DXMesh *iface)
197 {
198 struct d3dx9_mesh *mesh = impl_from_ID3DXMesh(iface);
199
200 TRACE("iface %p.\n", iface);
201
202 return mesh->numvertices;
203 }
204
205 static DWORD WINAPI d3dx9_mesh_GetFVF(ID3DXMesh *iface)
206 {
207 struct d3dx9_mesh *mesh = impl_from_ID3DXMesh(iface);
208
209 TRACE("iface %p.\n", iface);
210
211 return mesh->fvf;
212 }
213
214 static void copy_declaration(D3DVERTEXELEMENT9 *dst, const D3DVERTEXELEMENT9 *src, UINT num_elem)
215 {
216 memcpy(dst, src, num_elem * sizeof(*src));
217 }
218
219 static HRESULT WINAPI d3dx9_mesh_GetDeclaration(ID3DXMesh *iface, D3DVERTEXELEMENT9 declaration[MAX_FVF_DECL_SIZE])
220 {
221 struct d3dx9_mesh *mesh = impl_from_ID3DXMesh(iface);
222
223 TRACE("iface %p, declaration %p.\n", iface, declaration);
224
225 if (!declaration)
226 return D3DERR_INVALIDCALL;
227
228 copy_declaration(declaration, mesh->cached_declaration, mesh->num_elem);
229
230 return D3D_OK;
231 }
232
233 static DWORD WINAPI d3dx9_mesh_GetNumBytesPerVertex(ID3DXMesh *iface)
234 {
235 struct d3dx9_mesh *mesh = impl_from_ID3DXMesh(iface);
236
237 TRACE("iface %p.\n", iface);
238
239 return mesh->vertex_declaration_size;
240 }
241
242 static DWORD WINAPI d3dx9_mesh_GetOptions(ID3DXMesh *iface)
243 {
244 struct d3dx9_mesh *mesh = impl_from_ID3DXMesh(iface);
245
246 TRACE("iface %p.\n", iface);
247
248 return mesh->options;
249 }
250
251 static HRESULT WINAPI d3dx9_mesh_GetDevice(struct ID3DXMesh *iface, struct IDirect3DDevice9 **device)
252 {
253 struct d3dx9_mesh *mesh = impl_from_ID3DXMesh(iface);
254
255 TRACE("iface %p, device %p.\n", iface, device);
256
257 if (!device)
258 return D3DERR_INVALIDCALL;
259 *device = mesh->device;
260 IDirect3DDevice9_AddRef(mesh->device);
261
262 return D3D_OK;
263 }
264
265 static HRESULT WINAPI d3dx9_mesh_CloneMeshFVF(struct ID3DXMesh *iface, DWORD options, DWORD fvf,
266 struct IDirect3DDevice9 *device, struct ID3DXMesh **clone_mesh)
267 {
268 HRESULT hr;
269 D3DVERTEXELEMENT9 declaration[MAX_FVF_DECL_SIZE];
270
271 TRACE("iface %p, options %#x, fvf %#x, device %p, clone_mesh %p.\n",
272 iface, options, fvf, device, clone_mesh);
273
274 if (FAILED(hr = D3DXDeclaratorFromFVF(fvf, declaration)))
275 return hr;
276
277 return iface->lpVtbl->CloneMesh(iface, options, declaration, device, clone_mesh);
278 }
279
280 static FLOAT scale_clamp_ubyten(FLOAT value)
281 {
282 value = value * UCHAR_MAX;
283
284 if (value < 0.0f)
285 {
286 return 0.0f;
287 }
288 else
289 {
290 if (value > UCHAR_MAX) /* Clamp at 255 */
291 return UCHAR_MAX;
292 else
293 return value;
294 }
295 }
296
297 static FLOAT scale_clamp_shortn(FLOAT value)
298 {
299 value = value * SHRT_MAX;
300
301 /* The tests show that the range is SHRT_MIN + 1 to SHRT_MAX. */
302 if (value <= SHRT_MIN)
303 {
304 return SHRT_MIN + 1;
305 }
306 else if (value > SHRT_MAX)
307 {
308 return SHRT_MAX;
309 }
310 else
311 {
312 return value;
313 }
314 }
315
316 static FLOAT scale_clamp_ushortn(FLOAT value)
317 {
318 value = value * USHRT_MAX;
319
320 if (value < 0.0f)
321 {
322 return 0.0f;
323 }
324 else
325 {
326 if (value > USHRT_MAX) /* Clamp at 65535 */
327 return USHRT_MAX;
328 else
329 return value;
330 }
331 }
332
333 static INT simple_round(FLOAT value)
334 {
335 int res = (INT)(value + 0.5f);
336
337 return res;
338 }
339
340 static void convert_float4(BYTE *dst, const D3DXVECTOR4 *src, D3DDECLTYPE type_dst)
341 {
342 BOOL fixme_once = FALSE;
343
344 switch (type_dst)
345 {
346 case D3DDECLTYPE_FLOAT1:
347 {
348 FLOAT *dst_ptr = (FLOAT*)dst;
349 *dst_ptr = src->x;
350 break;
351 }
352 case D3DDECLTYPE_FLOAT2:
353 {
354 D3DXVECTOR2 *dst_ptr = (D3DXVECTOR2*)dst;
355 dst_ptr->x = src->x;
356 dst_ptr->y = src->y;
357 break;
358 }
359 case D3DDECLTYPE_FLOAT3:
360 {
361 D3DXVECTOR3 *dst_ptr = (D3DXVECTOR3*)dst;
362 dst_ptr->x = src->x;
363 dst_ptr->y = src->y;
364 dst_ptr->z = src->z;
365 break;
366 }
367 case D3DDECLTYPE_FLOAT4:
368 {
369 D3DXVECTOR4 *dst_ptr = (D3DXVECTOR4*)dst;
370 dst_ptr->x = src->x;
371 dst_ptr->y = src->y;
372 dst_ptr->z = src->z;
373 dst_ptr->w = src->w;
374 break;
375 }
376 case D3DDECLTYPE_D3DCOLOR:
377 {
378 dst[0] = (BYTE)simple_round(scale_clamp_ubyten(src->z));
379 dst[1] = (BYTE)simple_round(scale_clamp_ubyten(src->y));
380 dst[2] = (BYTE)simple_round(scale_clamp_ubyten(src->x));
381 dst[3] = (BYTE)simple_round(scale_clamp_ubyten(src->w));
382 break;
383 }
384 case D3DDECLTYPE_UBYTE4:
385 {
386 dst[0] = src->x < 0.0f ? 0 : (BYTE)simple_round(src->x);
387 dst[1] = src->y < 0.0f ? 0 : (BYTE)simple_round(src->y);
388 dst[2] = src->z < 0.0f ? 0 : (BYTE)simple_round(src->z);
389 dst[3] = src->w < 0.0f ? 0 : (BYTE)simple_round(src->w);
390 break;
391 }
392 case D3DDECLTYPE_SHORT2:
393 {
394 SHORT *dst_ptr = (SHORT*)dst;
395 dst_ptr[0] = (SHORT)simple_round(src->x);
396 dst_ptr[1] = (SHORT)simple_round(src->y);
397 break;
398 }
399 case D3DDECLTYPE_SHORT4:
400 {
401 SHORT *dst_ptr = (SHORT*)dst;
402 dst_ptr[0] = (SHORT)simple_round(src->x);
403 dst_ptr[1] = (SHORT)simple_round(src->y);
404 dst_ptr[2] = (SHORT)simple_round(src->z);
405 dst_ptr[3] = (SHORT)simple_round(src->w);
406 break;
407 }
408 case D3DDECLTYPE_UBYTE4N:
409 {
410 dst[0] = (BYTE)simple_round(scale_clamp_ubyten(src->x));
411 dst[1] = (BYTE)simple_round(scale_clamp_ubyten(src->y));
412 dst[2] = (BYTE)simple_round(scale_clamp_ubyten(src->z));
413 dst[3] = (BYTE)simple_round(scale_clamp_ubyten(src->w));
414 break;
415 }
416 case D3DDECLTYPE_SHORT2N:
417 {
418 SHORT *dst_ptr = (SHORT*)dst;
419 dst_ptr[0] = (SHORT)simple_round(scale_clamp_shortn(src->x));
420 dst_ptr[1] = (SHORT)simple_round(scale_clamp_shortn(src->y));
421 break;
422 }
423 case D3DDECLTYPE_SHORT4N:
424 {
425 SHORT *dst_ptr = (SHORT*)dst;
426 dst_ptr[0] = (SHORT)simple_round(scale_clamp_shortn(src->x));
427 dst_ptr[1] = (SHORT)simple_round(scale_clamp_shortn(src->y));
428 dst_ptr[2] = (SHORT)simple_round(scale_clamp_shortn(src->z));
429 dst_ptr[3] = (SHORT)simple_round(scale_clamp_shortn(src->w));
430 break;
431 }
432 case D3DDECLTYPE_USHORT2N:
433 {
434 USHORT *dst_ptr = (USHORT*)dst;
435 dst_ptr[0] = (USHORT)simple_round(scale_clamp_ushortn(src->x));
436 dst_ptr[1] = (USHORT)simple_round(scale_clamp_ushortn(src->y));
437 break;
438 }
439 case D3DDECLTYPE_USHORT4N:
440 {
441 USHORT *dst_ptr = (USHORT*)dst;
442 dst_ptr[0] = (USHORT)simple_round(scale_clamp_ushortn(src->x));
443 dst_ptr[1] = (USHORT)simple_round(scale_clamp_ushortn(src->y));
444 dst_ptr[2] = (USHORT)simple_round(scale_clamp_ushortn(src->z));
445 dst_ptr[3] = (USHORT)simple_round(scale_clamp_ushortn(src->w));
446 break;
447 }
448 case D3DDECLTYPE_FLOAT16_2:
449 {
450 D3DXFloat32To16Array((D3DXFLOAT16*)dst, (FLOAT*)src, 2);
451 break;
452 }
453 case D3DDECLTYPE_FLOAT16_4:
454 {
455 D3DXFloat32To16Array((D3DXFLOAT16*)dst, (FLOAT*)src, 4);
456 break;
457 }
458 default:
459 if (!fixme_once++)
460 FIXME("Conversion from D3DDECLTYPE_FLOAT4 to %d not implemented.\n", type_dst);
461 break;
462 }
463 }
464
465 static void convert_component(BYTE *dst, BYTE *src, D3DDECLTYPE type_dst, D3DDECLTYPE type_src)
466 {
467 BOOL fixme_once = FALSE;
468
469 switch (type_src)
470 {
471 case D3DDECLTYPE_FLOAT1:
472 {
473 FLOAT *src_ptr = (FLOAT*)src;
474 D3DXVECTOR4 src_float4 = {*src_ptr, 0.0f, 0.0f, 1.0f};
475 convert_float4(dst, &src_float4, type_dst);
476 break;
477 }
478 case D3DDECLTYPE_FLOAT2:
479 {
480 D3DXVECTOR2 *src_ptr = (D3DXVECTOR2*)src;
481 D3DXVECTOR4 src_float4 = {src_ptr->x, src_ptr->y, 0.0f, 1.0f};
482 convert_float4(dst, &src_float4, type_dst);
483 break;
484 }
485 case D3DDECLTYPE_FLOAT3:
486 {
487 D3DXVECTOR3 *src_ptr = (D3DXVECTOR3*)src;
488 D3DXVECTOR4 src_float4 = {src_ptr->x, src_ptr->y, src_ptr->z, 1.0f};
489 convert_float4(dst, &src_float4, type_dst);
490 break;
491 }
492 case D3DDECLTYPE_FLOAT4:
493 {
494 D3DXVECTOR4 *src_ptr = (D3DXVECTOR4*)src;
495 D3DXVECTOR4 src_float4 = {src_ptr->x, src_ptr->y, src_ptr->z, src_ptr->w};
496 convert_float4(dst, &src_float4, type_dst);
497 break;
498 }
499 case D3DDECLTYPE_D3DCOLOR:
500 {
501 D3DXVECTOR4 src_float4 =
502 {
503 (FLOAT)src[2]/UCHAR_MAX,
504 (FLOAT)src[1]/UCHAR_MAX,
505 (FLOAT)src[0]/UCHAR_MAX,
506 (FLOAT)src[3]/UCHAR_MAX
507 };
508 convert_float4(dst, &src_float4, type_dst);
509 break;
510 }
511 case D3DDECLTYPE_UBYTE4:
512 {
513 D3DXVECTOR4 src_float4 = {src[0], src[1], src[2], src[3]};
514 convert_float4(dst, &src_float4, type_dst);
515 break;
516 }
517 case D3DDECLTYPE_SHORT2:
518 {
519 SHORT *src_ptr = (SHORT*)src;
520 D3DXVECTOR4 src_float4 = {src_ptr[0], src_ptr[1], 0.0f, 1.0f};
521 convert_float4(dst, &src_float4, type_dst);
522 break;
523 }
524 case D3DDECLTYPE_SHORT4:
525 {
526 SHORT *src_ptr = (SHORT*)src;
527 D3DXVECTOR4 src_float4 = {src_ptr[0], src_ptr[1], src_ptr[2], src_ptr[3]};
528 convert_float4(dst, &src_float4, type_dst);
529 break;
530 }
531 case D3DDECLTYPE_UBYTE4N:
532 {
533 D3DXVECTOR4 src_float4 =
534 {
535 (FLOAT)src[0]/UCHAR_MAX,
536 (FLOAT)src[1]/UCHAR_MAX,
537 (FLOAT)src[2]/UCHAR_MAX,
538 (FLOAT)src[3]/UCHAR_MAX
539 };
540 convert_float4(dst, &src_float4, type_dst);
541 break;
542 }
543 case D3DDECLTYPE_SHORT2N:
544 {
545 SHORT *src_ptr = (SHORT*)src;
546 D3DXVECTOR4 src_float4 = {(FLOAT)src_ptr[0]/SHRT_MAX, (FLOAT)src_ptr[1]/SHRT_MAX, 0.0f, 1.0f};
547 convert_float4(dst, &src_float4, type_dst);
548 break;
549 }
550 case D3DDECLTYPE_SHORT4N:
551 {
552 SHORT *src_ptr = (SHORT*)src;
553 D3DXVECTOR4 src_float4 =
554 {
555 (FLOAT)src_ptr[0]/SHRT_MAX,
556 (FLOAT)src_ptr[1]/SHRT_MAX,
557 (FLOAT)src_ptr[2]/SHRT_MAX,
558 (FLOAT)src_ptr[3]/SHRT_MAX
559 };
560 convert_float4(dst, &src_float4, type_dst);
561 break;
562 }
563 case D3DDECLTYPE_FLOAT16_2:
564 {
565 D3DXVECTOR4 src_float4 = {0.0f, 0.0f, 0.0f, 1.0f};
566 D3DXFloat16To32Array((FLOAT*)&src_float4, (D3DXFLOAT16*)src, 2);
567 convert_float4(dst, &src_float4, type_dst);
568 break;
569 }
570 case D3DDECLTYPE_FLOAT16_4:
571 {
572 D3DXVECTOR4 src_float4;
573 D3DXFloat16To32Array((FLOAT*)&src_float4, (D3DXFLOAT16*)src, 4);
574 convert_float4(dst, &src_float4, type_dst);
575 break;
576 }
577 default:
578 if (!fixme_once++)
579 FIXME("Conversion of D3DDECLTYPE %d to %d not implemented.\n", type_src, type_dst);
580 break;
581 }
582 }
583
584 static INT get_equivalent_declaration_index(D3DVERTEXELEMENT9 orig_declaration, D3DVERTEXELEMENT9 *declaration)
585 {
586 INT i;
587
588 for (i = 0; declaration[i].Stream != 0xff; i++)
589 {
590 if (orig_declaration.Usage == declaration[i].Usage
591 && orig_declaration.UsageIndex == declaration[i].UsageIndex)
592 {
593 return i;
594 }
595 }
596
597 return -1;
598 }
599
600 static HRESULT convert_vertex_buffer(ID3DXMesh *mesh_dst, ID3DXMesh *mesh_src)
601 {
602 HRESULT hr;
603 D3DVERTEXELEMENT9 orig_declaration[MAX_FVF_DECL_SIZE] = {D3DDECL_END()};
604 D3DVERTEXELEMENT9 declaration[MAX_FVF_DECL_SIZE] = {D3DDECL_END()};
605 BYTE *vb_dst = NULL;
606 BYTE *vb_src = NULL;
607 UINT i;
608 UINT num_vertices = mesh_src->lpVtbl->GetNumVertices(mesh_src);
609 UINT dst_vertex_size = mesh_dst->lpVtbl->GetNumBytesPerVertex(mesh_dst);
610 UINT src_vertex_size = mesh_src->lpVtbl->GetNumBytesPerVertex(mesh_src);
611
612 hr = mesh_src->lpVtbl->GetDeclaration(mesh_src, orig_declaration);
613 if (FAILED(hr)) return hr;
614 hr = mesh_dst->lpVtbl->GetDeclaration(mesh_dst, declaration);
615 if (FAILED(hr)) return hr;
616
617 hr = mesh_src->lpVtbl->LockVertexBuffer(mesh_src, D3DLOCK_READONLY, (void**)&vb_src);
618 if (FAILED(hr)) goto cleanup;
619 hr = mesh_dst->lpVtbl->LockVertexBuffer(mesh_dst, 0, (void**)&vb_dst);
620 if (FAILED(hr)) goto cleanup;
621
622 /* Clear all new fields by clearing the entire vertex buffer. */
623 memset(vb_dst, 0, num_vertices * dst_vertex_size);
624
625 for (i = 0; orig_declaration[i].Stream != 0xff; i++)
626 {
627 INT eq_idx = get_equivalent_declaration_index(orig_declaration[i], declaration);
628
629 if (eq_idx >= 0)
630 {
631 UINT j;
632 for (j = 0; j < num_vertices; j++)
633 {
634 UINT idx_dst = dst_vertex_size * j + declaration[eq_idx].Offset;
635 UINT idx_src = src_vertex_size * j + orig_declaration[i].Offset;
636 UINT type_size = d3dx_decltype_size[orig_declaration[i].Type];
637
638 if (orig_declaration[i].Type == declaration[eq_idx].Type)
639 memcpy(&vb_dst[idx_dst], &vb_src[idx_src], type_size);
640 else
641 convert_component(&vb_dst[idx_dst], &vb_src[idx_src], declaration[eq_idx].Type, orig_declaration[i].Type);
642 }
643 }
644 }
645
646 hr = D3D_OK;
647 cleanup:
648 if (vb_dst) mesh_dst->lpVtbl->UnlockVertexBuffer(mesh_dst);
649 if (vb_src) mesh_src->lpVtbl->UnlockVertexBuffer(mesh_src);
650
651 return hr;
652 }
653
654 static BOOL declaration_equals(const D3DVERTEXELEMENT9 *declaration1, const D3DVERTEXELEMENT9 *declaration2)
655 {
656 UINT size1 = 0, size2 = 0;
657
658 /* Find the size of each declaration */
659 while (declaration1[size1].Stream != 0xff) size1++;
660 while (declaration2[size2].Stream != 0xff) size2++;
661
662 /* If not same size then they are definitely not equal */
663 if (size1 != size2)
664 return FALSE;
665
666 /* Check that all components are the same */
667 if (memcmp(declaration1, declaration2, size1*sizeof(*declaration1)) == 0)
668 return TRUE;
669
670 return FALSE;
671 }
672
673 static HRESULT WINAPI d3dx9_mesh_CloneMesh(struct ID3DXMesh *iface, DWORD options,
674 const D3DVERTEXELEMENT9 *declaration, struct IDirect3DDevice9 *device, struct ID3DXMesh **clone_mesh_out)
675 {
676 struct d3dx9_mesh *This = impl_from_ID3DXMesh(iface);
677 struct d3dx9_mesh *cloned_this;
678 ID3DXMesh *clone_mesh;
679 D3DVERTEXELEMENT9 orig_declaration[MAX_FVF_DECL_SIZE] = { D3DDECL_END() };
680 void *data_in, *data_out;
681 DWORD vertex_size;
682 HRESULT hr;
683 BOOL same_declaration;
684
685 TRACE("iface %p, options %#x, declaration %p, device %p, clone_mesh_out %p.\n",
686 iface, options, declaration, device, clone_mesh_out);
687
688 if (!clone_mesh_out)
689 return D3DERR_INVALIDCALL;
690
691 hr = iface->lpVtbl->GetDeclaration(iface, orig_declaration);
692 if (FAILED(hr)) return hr;
693
694 hr = D3DXCreateMesh(This->numfaces, This->numvertices, options & ~D3DXMESH_VB_SHARE,
695 declaration, device, &clone_mesh);
696 if (FAILED(hr)) return hr;
697
698 cloned_this = impl_from_ID3DXMesh(clone_mesh);
699 vertex_size = clone_mesh->lpVtbl->GetNumBytesPerVertex(clone_mesh);
700 same_declaration = declaration_equals(declaration, orig_declaration);
701
702 if (options & D3DXMESH_VB_SHARE) {
703 if (!same_declaration) {
704 hr = D3DERR_INVALIDCALL;
705 goto error;
706 }
707 IDirect3DVertexBuffer9_AddRef(This->vertex_buffer);
708 /* FIXME: refactor to avoid creating a new vertex buffer */
709 IDirect3DVertexBuffer9_Release(cloned_this->vertex_buffer);
710 cloned_this->vertex_buffer = This->vertex_buffer;
711 } else if (same_declaration) {
712 hr = iface->lpVtbl->LockVertexBuffer(iface, D3DLOCK_READONLY, &data_in);
713 if (FAILED(hr)) goto error;
714 hr = clone_mesh->lpVtbl->LockVertexBuffer(clone_mesh, 0, &data_out);
715 if (FAILED(hr)) {
716 iface->lpVtbl->UnlockVertexBuffer(iface);
717 goto error;
718 }
719 memcpy(data_out, data_in, This->numvertices * vertex_size);
720 clone_mesh->lpVtbl->UnlockVertexBuffer(clone_mesh);
721 iface->lpVtbl->UnlockVertexBuffer(iface);
722 } else {
723 hr = convert_vertex_buffer(clone_mesh, iface);
724 if (FAILED(hr)) goto error;
725 }
726
727 hr = iface->lpVtbl->LockIndexBuffer(iface, D3DLOCK_READONLY, &data_in);
728 if (FAILED(hr)) goto error;
729 hr = clone_mesh->lpVtbl->LockIndexBuffer(clone_mesh, 0, &data_out);
730 if (FAILED(hr)) {
731 iface->lpVtbl->UnlockIndexBuffer(iface);
732 goto error;
733 }
734 if ((options ^ This->options) & D3DXMESH_32BIT) {
735 DWORD i;
736 if (options & D3DXMESH_32BIT) {
737 for (i = 0; i < This->numfaces * 3; i++)
738 ((DWORD*)data_out)[i] = ((WORD*)data_in)[i];
739 } else {
740 for (i = 0; i < This->numfaces * 3; i++)
741 ((WORD*)data_out)[i] = ((DWORD*)data_in)[i];
742 }
743 } else {
744 memcpy(data_out, data_in, This->numfaces * 3 * (options & D3DXMESH_32BIT ? 4 : 2));
745 }
746 clone_mesh->lpVtbl->UnlockIndexBuffer(clone_mesh);
747 iface->lpVtbl->UnlockIndexBuffer(iface);
748
749 memcpy(cloned_this->attrib_buffer, This->attrib_buffer, This->numfaces * sizeof(*This->attrib_buffer));
750
751 if (This->attrib_table_size)
752 {
753 cloned_this->attrib_table_size = This->attrib_table_size;
754 cloned_this->attrib_table = HeapAlloc(GetProcessHeap(), 0, This->attrib_table_size * sizeof(*This->attrib_table));
755 if (!cloned_this->attrib_table) {
756 hr = E_OUTOFMEMORY;
757 goto error;
758 }
759 memcpy(cloned_this->attrib_table, This->attrib_table, This->attrib_table_size * sizeof(*This->attrib_table));
760 }
761
762 *clone_mesh_out = clone_mesh;
763
764 return D3D_OK;
765 error:
766 IUnknown_Release(clone_mesh);
767 return hr;
768 }
769
770 static HRESULT WINAPI d3dx9_mesh_GetVertexBuffer(struct ID3DXMesh *iface,
771 struct IDirect3DVertexBuffer9 **vertex_buffer)
772 {
773 struct d3dx9_mesh *mesh = impl_from_ID3DXMesh(iface);
774
775 TRACE("iface %p, vertex_buffer %p.\n", iface, vertex_buffer);
776
777 if (!vertex_buffer)
778 return D3DERR_INVALIDCALL;
779 *vertex_buffer = mesh->vertex_buffer;
780 IDirect3DVertexBuffer9_AddRef(mesh->vertex_buffer);
781
782 return D3D_OK;
783 }
784
785 static HRESULT WINAPI d3dx9_mesh_GetIndexBuffer(struct ID3DXMesh *iface,
786 struct IDirect3DIndexBuffer9 **index_buffer)
787 {
788 struct d3dx9_mesh *mesh = impl_from_ID3DXMesh(iface);
789
790 TRACE("iface %p, index_buffer %p.\n", iface, index_buffer);
791
792 if (!index_buffer)
793 return D3DERR_INVALIDCALL;
794 *index_buffer = mesh->index_buffer;
795 IDirect3DIndexBuffer9_AddRef(mesh->index_buffer);
796
797 return D3D_OK;
798 }
799
800 static HRESULT WINAPI d3dx9_mesh_LockVertexBuffer(ID3DXMesh *iface, DWORD flags, void **data)
801 {
802 struct d3dx9_mesh *mesh = impl_from_ID3DXMesh(iface);
803
804 TRACE("iface %p, flags %#x, data %p.\n", iface, flags, data);
805
806 return IDirect3DVertexBuffer9_Lock(mesh->vertex_buffer, 0, 0, data, flags);
807 }
808
809 static HRESULT WINAPI d3dx9_mesh_UnlockVertexBuffer(ID3DXMesh *iface)
810 {
811 struct d3dx9_mesh *mesh = impl_from_ID3DXMesh(iface);
812
813 TRACE("iface %p.\n", iface);
814
815 return IDirect3DVertexBuffer9_Unlock(mesh->vertex_buffer);
816 }
817
818 static HRESULT WINAPI d3dx9_mesh_LockIndexBuffer(ID3DXMesh *iface, DWORD flags, void **data)
819 {
820 struct d3dx9_mesh *mesh = impl_from_ID3DXMesh(iface);
821
822 TRACE("iface %p, flags %#x, data %p.\n", iface, flags, data);
823
824 return IDirect3DIndexBuffer9_Lock(mesh->index_buffer, 0, 0, data, flags);
825 }
826
827 static HRESULT WINAPI d3dx9_mesh_UnlockIndexBuffer(ID3DXMesh *iface)
828 {
829 struct d3dx9_mesh *mesh = impl_from_ID3DXMesh(iface);
830
831 TRACE("iface %p.\n", iface);
832
833 return IDirect3DIndexBuffer9_Unlock(mesh->index_buffer);
834 }
835
836 /* FIXME: This looks just wrong, we never check *attrib_table_size before
837 * copying the data. */
838 static HRESULT WINAPI d3dx9_mesh_GetAttributeTable(ID3DXMesh *iface,
839 D3DXATTRIBUTERANGE *attrib_table, DWORD *attrib_table_size)
840 {
841 struct d3dx9_mesh *mesh = impl_from_ID3DXMesh(iface);
842
843 TRACE("iface %p, attrib_table %p, attrib_table_size %p.\n",
844 iface, attrib_table, attrib_table_size);
845
846 if (attrib_table_size)
847 *attrib_table_size = mesh->attrib_table_size;
848
849 if (attrib_table)
850 memcpy(attrib_table, mesh->attrib_table, mesh->attrib_table_size * sizeof(*attrib_table));
851
852 return D3D_OK;
853 }
854
855 struct edge_face
856 {
857 struct list entry;
858 DWORD v2;
859 DWORD face;
860 };
861
862 struct edge_face_map
863 {
864 struct list *lists;
865 struct edge_face *entries;
866 };
867
868 /* Builds up a map of which face a new edge belongs to. That way the adjacency
869 * of another edge can be looked up. An edge has an adjacent face if there
870 * is an edge going in the opposite direction in the map. For example if the
871 * edge (v1, v2) belongs to face 4, and there is a mapping (v2, v1)->7, then
872 * face 4 and 7 are adjacent.
873 *
874 * Each edge might have been replaced with another edge, or none at all. There
875 * is at most one edge to face mapping, i.e. an edge can only belong to one
876 * face.
877 */
878 static HRESULT init_edge_face_map(struct edge_face_map *edge_face_map, const DWORD *index_buffer,
879 const DWORD *point_reps, DWORD num_faces)
880 {
881 DWORD face, edge;
882 DWORD i;
883
884 edge_face_map->lists = HeapAlloc(GetProcessHeap(), 0, 3 * num_faces * sizeof(*edge_face_map->lists));
885 if (!edge_face_map->lists) return E_OUTOFMEMORY;
886
887 edge_face_map->entries = HeapAlloc(GetProcessHeap(), 0, 3 * num_faces * sizeof(*edge_face_map->entries));
888 if (!edge_face_map->entries) return E_OUTOFMEMORY;
889
890
891 /* Initialize all lists */
892 for (i = 0; i < 3 * num_faces; i++)
893 {
894 list_init(&edge_face_map->lists[i]);
895 }
896 /* Build edge face mapping */
897 for (face = 0; face < num_faces; face++)
898 {
899 for (edge = 0; edge < 3; edge++)
900 {
901 DWORD v1 = index_buffer[3*face + edge];
902 DWORD v2 = index_buffer[3*face + (edge+1)%3];
903 DWORD new_v1 = point_reps[v1]; /* What v1 has been replaced with */
904 DWORD new_v2 = point_reps[v2];
905
906 if (v1 != v2) /* Only map non-collapsed edges */
907 {
908 i = 3*face + edge;
909 edge_face_map->entries[i].v2 = new_v2;
910 edge_face_map->entries[i].face = face;
911 list_add_head(&edge_face_map->lists[new_v1], &edge_face_map->entries[i].entry);
912 }
913 }
914 }
915
916 return D3D_OK;
917 }
918
919 static DWORD find_adjacent_face(struct edge_face_map *edge_face_map, DWORD vertex1, DWORD vertex2, DWORD num_faces)
920 {
921 struct edge_face *edge_face_ptr;
922
923 LIST_FOR_EACH_ENTRY(edge_face_ptr, &edge_face_map->lists[vertex2], struct edge_face, entry)
924 {
925 if (edge_face_ptr->v2 == vertex1)
926 return edge_face_ptr->face;
927 }
928
929 return -1;
930 }
931
932 static DWORD *generate_identity_point_reps(DWORD num_vertices)
933 {
934 DWORD *id_point_reps;
935 DWORD i;
936
937 id_point_reps = HeapAlloc(GetProcessHeap(), 0, num_vertices * sizeof(*id_point_reps));
938 if (!id_point_reps)
939 return NULL;
940
941 for (i = 0; i < num_vertices; i++)
942 {
943 id_point_reps[i] = i;
944 }
945
946 return id_point_reps;
947 }
948
949 static HRESULT WINAPI d3dx9_mesh_ConvertPointRepsToAdjacency(ID3DXMesh *iface,
950 const DWORD *point_reps, DWORD *adjacency)
951 {
952 HRESULT hr;
953 DWORD num_faces = iface->lpVtbl->GetNumFaces(iface);
954 DWORD num_vertices = iface->lpVtbl->GetNumVertices(iface);
955 DWORD options = iface->lpVtbl->GetOptions(iface);
956 BOOL indices_are_16_bit = !(options & D3DXMESH_32BIT);
957 DWORD *ib = NULL;
958 void *ib_ptr = NULL;
959 DWORD face;
960 DWORD edge;
961 struct edge_face_map edge_face_map = {0};
962 const DWORD *point_reps_ptr = NULL;
963 DWORD *id_point_reps = NULL;
964
965 TRACE("iface %p, point_reps %p, adjacency %p.\n", iface, point_reps, adjacency);
966
967 if (!adjacency) return D3DERR_INVALIDCALL;
968
969 if (!point_reps) /* Identity point reps */
970 {
971 id_point_reps = generate_identity_point_reps(num_vertices);
972 if (!id_point_reps)
973 {
974 hr = E_OUTOFMEMORY;
975 goto cleanup;
976 }
977
978 point_reps_ptr = id_point_reps;
979 }
980 else
981 {
982 point_reps_ptr = point_reps;
983 }
984
985 hr = iface->lpVtbl->LockIndexBuffer(iface, D3DLOCK_READONLY, &ib_ptr);
986 if (FAILED(hr)) goto cleanup;
987
988 if (indices_are_16_bit)
989 {
990 /* Widen 16 bit to 32 bit */
991 DWORD i;
992 WORD *ib_16bit = ib_ptr;
993 ib = HeapAlloc(GetProcessHeap(), 0, 3 * num_faces * sizeof(DWORD));
994 if (!ib)
995 {
996 hr = E_OUTOFMEMORY;
997 goto cleanup;
998 }
999 for (i = 0; i < 3 * num_faces; i++)
1000 {
1001 ib[i] = ib_16bit[i];
1002 }
1003 }
1004 else
1005 {
1006 ib = ib_ptr;
1007 }
1008
1009 hr = init_edge_face_map(&edge_face_map, ib, point_reps_ptr, num_faces);
1010 if (FAILED(hr)) goto cleanup;
1011
1012 /* Create adjacency */
1013 for (face = 0; face < num_faces; face++)
1014 {
1015 for (edge = 0; edge < 3; edge++)
1016 {
1017 DWORD v1 = ib[3*face + edge];
1018 DWORD v2 = ib[3*face + (edge+1)%3];
1019 DWORD new_v1 = point_reps_ptr[v1];
1020 DWORD new_v2 = point_reps_ptr[v2];
1021 DWORD adj_face;
1022
1023 adj_face = find_adjacent_face(&edge_face_map, new_v1, new_v2, num_faces);
1024 adjacency[3*face + edge] = adj_face;
1025 }
1026 }
1027
1028 hr = D3D_OK;
1029 cleanup:
1030 HeapFree(GetProcessHeap(), 0, id_point_reps);
1031 if (indices_are_16_bit) HeapFree(GetProcessHeap(), 0, ib);
1032 HeapFree(GetProcessHeap(), 0, edge_face_map.lists);
1033 HeapFree(GetProcessHeap(), 0, edge_face_map.entries);
1034 if(ib_ptr) iface->lpVtbl->UnlockIndexBuffer(iface);
1035 return hr;
1036 }
1037
1038 /* ConvertAdjacencyToPointReps helper function.
1039 *
1040 * Goes around the edges of each face and replaces the vertices in any adjacent
1041 * face's edge with its own vertices(if its vertices have a lower index). This
1042 * way as few as possible low index vertices are shared among the faces. The
1043 * re-ordered index buffer is stored in new_indices.
1044 *
1045 * The vertices in a point representation must be ordered sequentially, e.g.
1046 * index 5 holds the index of the vertex that replaces vertex 5, i.e. if
1047 * vertex 5 is replaced by vertex 3 then index 5 would contain 3. If no vertex
1048 * replaces it, then it contains the same number as the index itself, e.g.
1049 * index 5 would contain 5. */
1050 static HRESULT propagate_face_vertices(const DWORD *adjacency, DWORD *point_reps,
1051 const DWORD *indices, DWORD *new_indices, DWORD face, DWORD numfaces)
1052 {
1053 const unsigned int VERTS_PER_FACE = 3;
1054 DWORD edge, opp_edge;
1055 DWORD face_base = VERTS_PER_FACE * face;
1056
1057 for (edge = 0; edge < VERTS_PER_FACE; edge++)
1058 {
1059 DWORD adj_face = adjacency[face_base + edge];
1060 DWORD adj_face_base;
1061 DWORD i;
1062 if (adj_face == -1) /* No adjacent face. */
1063 continue;
1064 else if (adj_face >= numfaces)
1065 {
1066 /* This throws exception on Windows */
1067 WARN("Index out of bounds. Got %d expected less than %d.\n",
1068 adj_face, numfaces);
1069 return D3DERR_INVALIDCALL;
1070 }
1071 adj_face_base = 3 * adj_face;
1072
1073 /* Find opposite edge in adjacent face. */
1074 for (opp_edge = 0; opp_edge < VERTS_PER_FACE; opp_edge++)
1075 {
1076 DWORD opp_edge_index = adj_face_base + opp_edge;
1077 if (adjacency[opp_edge_index] == face)
1078 break; /* Found opposite edge. */
1079 }
1080
1081 /* Replaces vertices in opposite edge with vertices from current edge. */
1082 for (i = 0; i < 2; i++)
1083 {
1084 DWORD from = face_base + (edge + (1 - i)) % VERTS_PER_FACE;
1085 DWORD to = adj_face_base + (opp_edge + i) % VERTS_PER_FACE;
1086
1087 /* Propagate lowest index. */
1088 if (new_indices[to] > new_indices[from])
1089 {
1090 new_indices[to] = new_indices[from];
1091 point_reps[indices[to]] = new_indices[from];
1092 }
1093 }
1094 }
1095
1096 return D3D_OK;
1097 }
1098
1099 static HRESULT WINAPI d3dx9_mesh_ConvertAdjacencyToPointReps(ID3DXMesh *iface,
1100 const DWORD *adjacency, DWORD *point_reps)
1101 {
1102 struct d3dx9_mesh *This = impl_from_ID3DXMesh(iface);
1103 HRESULT hr;
1104 DWORD face;
1105 DWORD i;
1106 DWORD *indices = NULL;
1107 WORD *indices_16bit = NULL;
1108 DWORD *new_indices = NULL;
1109 const unsigned int VERTS_PER_FACE = 3;
1110
1111 TRACE("iface %p, adjacency %p, point_reps %p.\n", iface, adjacency, point_reps);
1112
1113 if (!adjacency)
1114 {
1115 WARN("NULL adjacency.\n");
1116 hr = D3DERR_INVALIDCALL;
1117 goto cleanup;
1118 }
1119
1120 if (!point_reps)
1121 {
1122 WARN("NULL point_reps.\n");
1123 hr = D3DERR_INVALIDCALL;
1124 goto cleanup;
1125 }
1126
1127 /* Should never happen as CreateMesh does not allow meshes with 0 faces */
1128 if (This->numfaces == 0)
1129 {
1130 ERR("Number of faces was zero.\n");
1131 hr = D3DERR_INVALIDCALL;
1132 goto cleanup;
1133 }
1134
1135 new_indices = HeapAlloc(GetProcessHeap(), 0, VERTS_PER_FACE * This->numfaces * sizeof(*indices));
1136 if (!new_indices)
1137 {
1138 hr = E_OUTOFMEMORY;
1139 goto cleanup;
1140 }
1141
1142 if (This->options & D3DXMESH_32BIT)
1143 {
1144 hr = iface->lpVtbl->LockIndexBuffer(iface, D3DLOCK_READONLY, (void**)&indices);
1145 if (FAILED(hr)) goto cleanup;
1146 memcpy(new_indices, indices, VERTS_PER_FACE * This->numfaces * sizeof(*indices));
1147 }
1148 else
1149 {
1150 /* Make a widening copy of indices_16bit into indices and new_indices
1151 * in order to re-use the helper function */
1152 hr = iface->lpVtbl->LockIndexBuffer(iface, D3DLOCK_READONLY, (void**)&indices_16bit);
1153 if (FAILED(hr)) goto cleanup;
1154 indices = HeapAlloc(GetProcessHeap(), 0, VERTS_PER_FACE * This->numfaces * sizeof(*indices));
1155 if (!indices)
1156 {
1157 hr = E_OUTOFMEMORY;
1158 goto cleanup;
1159 }
1160 for (i = 0; i < VERTS_PER_FACE * This->numfaces; i++)
1161 {
1162 new_indices[i] = indices_16bit[i];
1163 indices[i] = indices_16bit[i];
1164 }
1165 }
1166
1167 /* Vertices are ordered sequentially in the point representation. */
1168 for (i = 0; i < This->numvertices; i++)
1169 {
1170 point_reps[i] = i;
1171 }
1172
1173 /* Propagate vertices with low indices so as few vertices as possible
1174 * are used in the mesh.
1175 */
1176 for (face = 0; face < This->numfaces; face++)
1177 {
1178 hr = propagate_face_vertices(adjacency, point_reps, indices, new_indices, face, This->numfaces);
1179 if (FAILED(hr)) goto cleanup;
1180 }
1181 /* Go in opposite direction to catch all face orderings */
1182 for (face = 0; face < This->numfaces; face++)
1183 {
1184 hr = propagate_face_vertices(adjacency, point_reps,
1185 indices, new_indices,
1186 (This->numfaces - 1) - face, This->numfaces);
1187 if (FAILED(hr)) goto cleanup;
1188 }
1189
1190 hr = D3D_OK;
1191 cleanup:
1192 if (This->options & D3DXMESH_32BIT)
1193 {
1194 if (indices) iface->lpVtbl->UnlockIndexBuffer(iface);
1195 }
1196 else
1197 {
1198 if (indices_16bit) iface->lpVtbl->UnlockIndexBuffer(iface);
1199 HeapFree(GetProcessHeap(), 0, indices);
1200 }
1201 HeapFree(GetProcessHeap(), 0, new_indices);
1202 return hr;
1203 }
1204
1205 struct vertex_metadata {
1206 float key;
1207 DWORD vertex_index;
1208 DWORD first_shared_index;
1209 };
1210
1211 static int compare_vertex_keys(const void *a, const void *b)
1212 {
1213 const struct vertex_metadata *left = a;
1214 const struct vertex_metadata *right = b;
1215 if (left->key == right->key)
1216 return 0;
1217 return left->key < right->key ? -1 : 1;
1218 }
1219
1220 static HRESULT WINAPI d3dx9_mesh_GenerateAdjacency(ID3DXMesh *iface, float epsilon, DWORD *adjacency)
1221 {
1222 struct d3dx9_mesh *This = impl_from_ID3DXMesh(iface);
1223 HRESULT hr;
1224 BYTE *vertices = NULL;
1225 const DWORD *indices = NULL;
1226 DWORD vertex_size;
1227 DWORD buffer_size;
1228 /* sort the vertices by (x + y + z) to quickly find coincident vertices */
1229 struct vertex_metadata *sorted_vertices;
1230 /* shared_indices links together identical indices in the index buffer so
1231 * that adjacency checks can be limited to faces sharing a vertex */
1232 DWORD *shared_indices = NULL;
1233 const FLOAT epsilon_sq = epsilon * epsilon;
1234 DWORD i;
1235
1236 TRACE("iface %p, epsilon %.8e, adjacency %p.\n", iface, epsilon, adjacency);
1237
1238 if (!adjacency)
1239 return D3DERR_INVALIDCALL;
1240
1241 buffer_size = This->numfaces * 3 * sizeof(*shared_indices) + This->numvertices * sizeof(*sorted_vertices);
1242 if (!(This->options & D3DXMESH_32BIT))
1243 buffer_size += This->numfaces * 3 * sizeof(*indices);
1244 shared_indices = HeapAlloc(GetProcessHeap(), 0, buffer_size);
1245 if (!shared_indices)
1246 return E_OUTOFMEMORY;
1247 sorted_vertices = (struct vertex_metadata*)(shared_indices + This->numfaces * 3);
1248
1249 hr = iface->lpVtbl->LockVertexBuffer(iface, D3DLOCK_READONLY, (void**)&vertices);
1250 if (FAILED(hr)) goto cleanup;
1251 hr = iface->lpVtbl->LockIndexBuffer(iface, D3DLOCK_READONLY, (void**)&indices);
1252 if (FAILED(hr)) goto cleanup;
1253
1254 if (!(This->options & D3DXMESH_32BIT)) {
1255 const WORD *word_indices = (const WORD*)indices;
1256 DWORD *dword_indices = (DWORD*)(sorted_vertices + This->numvertices);
1257 indices = dword_indices;
1258 for (i = 0; i < This->numfaces * 3; i++)
1259 *dword_indices++ = *word_indices++;
1260 }
1261
1262 vertex_size = iface->lpVtbl->GetNumBytesPerVertex(iface);
1263 for (i = 0; i < This->numvertices; i++) {
1264 D3DXVECTOR3 *vertex = (D3DXVECTOR3*)(vertices + vertex_size * i);
1265 sorted_vertices[i].first_shared_index = -1;
1266 sorted_vertices[i].key = vertex->x + vertex->y + vertex->z;
1267 sorted_vertices[i].vertex_index = i;
1268 }
1269 for (i = 0; i < This->numfaces * 3; i++) {
1270 DWORD *first_shared_index = &sorted_vertices[indices[i]].first_shared_index;
1271 shared_indices[i] = *first_shared_index;
1272 *first_shared_index = i;
1273 adjacency[i] = -1;
1274 }
1275 qsort(sorted_vertices, This->numvertices, sizeof(*sorted_vertices), compare_vertex_keys);
1276
1277 for (i = 0; i < This->numvertices; i++) {
1278 struct vertex_metadata *sorted_vertex_a = &sorted_vertices[i];
1279 D3DXVECTOR3 *vertex_a = (D3DXVECTOR3*)(vertices + sorted_vertex_a->vertex_index * vertex_size);
1280 DWORD shared_index_a = sorted_vertex_a->first_shared_index;
1281
1282 while (shared_index_a != -1) {
1283 DWORD j = i;
1284 DWORD shared_index_b = shared_indices[shared_index_a];
1285 struct vertex_metadata *sorted_vertex_b = sorted_vertex_a;
1286
1287 while (TRUE) {
1288 while (shared_index_b != -1) {
1289 /* faces are adjacent if they have another coincident vertex */
1290 DWORD base_a = (shared_index_a / 3) * 3;
1291 DWORD base_b = (shared_index_b / 3) * 3;
1292 BOOL adjacent = FALSE;
1293 int k;
1294
1295 for (k = 0; k < 3; k++) {
1296 if (adjacency[base_b + k] == shared_index_a / 3) {
1297 adjacent = TRUE;
1298 break;
1299 }
1300 }
1301 if (!adjacent) {
1302 for (k = 1; k <= 2; k++) {
1303 DWORD vertex_index_a = base_a + (shared_index_a + k) % 3;
1304 DWORD vertex_index_b = base_b + (shared_index_b + (3 - k)) % 3;
1305 adjacent = indices[vertex_index_a] == indices[vertex_index_b];
1306 if (!adjacent && epsilon >= 0.0f) {
1307 D3DXVECTOR3 delta = {0.0f, 0.0f, 0.0f};
1308 FLOAT length_sq;
1309
1310 D3DXVec3Subtract(&delta,
1311 (D3DXVECTOR3*)(vertices + indices[vertex_index_a] * vertex_size),
1312 (D3DXVECTOR3*)(vertices + indices[vertex_index_b] * vertex_size));
1313 length_sq = D3DXVec3LengthSq(&delta);
1314 adjacent = epsilon == 0.0f ? length_sq == 0.0f : length_sq < epsilon_sq;
1315 }
1316 if (adjacent) {
1317 DWORD adj_a = base_a + 2 - (vertex_index_a + shared_index_a + 1) % 3;
1318 DWORD adj_b = base_b + 2 - (vertex_index_b + shared_index_b + 1) % 3;
1319 if (adjacency[adj_a] == -1 && adjacency[adj_b] == -1) {
1320 adjacency[adj_a] = base_b / 3;
1321 adjacency[adj_b] = base_a / 3;
1322 break;
1323 }
1324 }
1325 }
1326 }
1327
1328 shared_index_b = shared_indices[shared_index_b];
1329 }
1330 while (++j < This->numvertices) {
1331 D3DXVECTOR3 *vertex_b;
1332
1333 sorted_vertex_b++;
1334 if (sorted_vertex_b->key - sorted_vertex_a->key > epsilon * 3.0f) {
1335 /* no more coincident vertices to try */
1336 j = This->numvertices;
1337 break;
1338 }
1339 /* check for coincidence */
1340 vertex_b = (D3DXVECTOR3*)(vertices + sorted_vertex_b->vertex_index * vertex_size);
1341 if (fabsf(vertex_a->x - vertex_b->x) <= epsilon &&
1342 fabsf(vertex_a->y - vertex_b->y) <= epsilon &&
1343 fabsf(vertex_a->z - vertex_b->z) <= epsilon)
1344 {
1345 break;
1346 }
1347 }
1348 if (j >= This->numvertices)
1349 break;
1350 shared_index_b = sorted_vertex_b->first_shared_index;
1351 }
1352
1353 sorted_vertex_a->first_shared_index = shared_indices[sorted_vertex_a->first_shared_index];
1354 shared_index_a = sorted_vertex_a->first_shared_index;
1355 }
1356 }
1357
1358 hr = D3D_OK;
1359 cleanup:
1360 if (indices) iface->lpVtbl->UnlockIndexBuffer(iface);
1361 if (vertices) iface->lpVtbl->UnlockVertexBuffer(iface);
1362 HeapFree(GetProcessHeap(), 0, shared_indices);
1363 return hr;
1364 }
1365
1366 static HRESULT WINAPI d3dx9_mesh_UpdateSemantics(ID3DXMesh *iface, D3DVERTEXELEMENT9 declaration[MAX_FVF_DECL_SIZE])
1367 {
1368 struct d3dx9_mesh *This = impl_from_ID3DXMesh(iface);
1369 HRESULT hr;
1370 UINT vertex_declaration_size;
1371 int i;
1372
1373 TRACE("iface %p, declaration %p.\n", iface, declaration);
1374
1375 if (!declaration)
1376 {
1377 WARN("Invalid declaration. Can't use NULL declaration.\n");
1378 return D3DERR_INVALIDCALL;
1379 }
1380
1381 /* New declaration must be same size as original */
1382 vertex_declaration_size = D3DXGetDeclVertexSize(declaration, declaration[0].Stream);
1383 if (vertex_declaration_size != This->vertex_declaration_size)
1384 {
1385 WARN("Invalid declaration. New vertex size does not match the original vertex size.\n");
1386 return D3DERR_INVALIDCALL;
1387 }
1388
1389 /* New declaration must not contain non-zero Stream value */
1390 for (i = 0; declaration[i].Stream != 0xff; i++)
1391 {
1392 if (declaration[i].Stream != 0)
1393 {
1394 WARN("Invalid declaration. New declaration contains non-zero Stream value.\n");
1395 return D3DERR_INVALIDCALL;
1396 }
1397 }
1398
1399 This->num_elem = i + 1;
1400 copy_declaration(This->cached_declaration, declaration, This->num_elem);
1401
1402 if (This->vertex_declaration)
1403 IDirect3DVertexDeclaration9_Release(This->vertex_declaration);
1404
1405 /* An application can pass an invalid declaration to UpdateSemantics and
1406 * still expect D3D_OK (see tests). If the declaration is invalid, then
1407 * subsequent calls to DrawSubset will fail. This is handled by setting the
1408 * vertex declaration to NULL.
1409 * GetDeclaration, GetNumBytesPerVertex must, however, use the new
1410 * invalid declaration. This is handled by them using the cached vertex
1411 * declaration instead of the actual vertex declaration.
1412 */
1413 hr = IDirect3DDevice9_CreateVertexDeclaration(This->device,
1414 declaration,
1415 &This->vertex_declaration);
1416 if (FAILED(hr))
1417 {
1418 WARN("Using invalid declaration. Calls to DrawSubset will fail.\n");
1419 This->vertex_declaration = NULL;
1420 }
1421
1422 return D3D_OK;
1423 }
1424
1425 static HRESULT WINAPI d3dx9_mesh_LockAttributeBuffer(ID3DXMesh *iface, DWORD flags, DWORD **data)
1426 {
1427 struct d3dx9_mesh *mesh = impl_from_ID3DXMesh(iface);
1428
1429 TRACE("iface %p, flags %#x, data %p.\n", iface, flags, data);
1430
1431 InterlockedIncrement(&mesh->attrib_buffer_lock_count);
1432
1433 if (!(flags & D3DLOCK_READONLY))
1434 {
1435 D3DXATTRIBUTERANGE *attrib_table = mesh->attrib_table;
1436 mesh->attrib_table_size = 0;
1437 mesh->attrib_table = NULL;
1438 HeapFree(GetProcessHeap(), 0, attrib_table);
1439 }
1440
1441 *data = mesh->attrib_buffer;
1442
1443 return D3D_OK;
1444 }
1445
1446 static HRESULT WINAPI d3dx9_mesh_UnlockAttributeBuffer(ID3DXMesh *iface)
1447 {
1448 struct d3dx9_mesh *mesh = impl_from_ID3DXMesh(iface);
1449 int lock_count;
1450
1451 TRACE("iface %p.\n", iface);
1452
1453 lock_count = InterlockedDecrement(&mesh->attrib_buffer_lock_count);
1454 if (lock_count < 0)
1455 {
1456 InterlockedIncrement(&mesh->attrib_buffer_lock_count);
1457 return D3DERR_INVALIDCALL;
1458 }
1459
1460 return D3D_OK;
1461 }
1462
1463 static HRESULT WINAPI d3dx9_mesh_Optimize(ID3DXMesh *iface, DWORD flags, const DWORD *adjacency_in,
1464 DWORD *adjacency_out, DWORD *face_remap, ID3DXBuffer **vertex_remap, ID3DXMesh **opt_mesh)
1465 {
1466 struct d3dx9_mesh *mesh = impl_from_ID3DXMesh(iface);
1467 HRESULT hr;
1468 D3DVERTEXELEMENT9 declaration[MAX_FVF_DECL_SIZE] = { D3DDECL_END() };
1469 ID3DXMesh *optimized_mesh;
1470
1471 TRACE("iface %p, flags %#x, adjacency_in %p, adjacency_out %p, face_remap %p, vertex_remap %p, opt_mesh %p.\n",
1472 iface, flags, adjacency_in, adjacency_out, face_remap, vertex_remap, opt_mesh);
1473
1474 if (!opt_mesh)
1475 return D3DERR_INVALIDCALL;
1476
1477 hr = iface->lpVtbl->GetDeclaration(iface, declaration);
1478 if (FAILED(hr)) return hr;
1479
1480 if (FAILED(hr = iface->lpVtbl->CloneMesh(iface, mesh->options, declaration, mesh->device, &optimized_mesh)))
1481 return hr;
1482
1483 hr = optimized_mesh->lpVtbl->OptimizeInplace(optimized_mesh, flags, adjacency_in, adjacency_out, face_remap, vertex_remap);
1484 if (SUCCEEDED(hr))
1485 *opt_mesh = optimized_mesh;
1486 else
1487 IUnknown_Release(optimized_mesh);
1488 return hr;
1489 }
1490
1491 /* Creates a vertex_remap that removes unused vertices.
1492 * Indices are updated according to the vertex_remap. */
1493 static HRESULT compact_mesh(struct d3dx9_mesh *This, DWORD *indices,
1494 DWORD *new_num_vertices, ID3DXBuffer **vertex_remap)
1495 {
1496 HRESULT hr;
1497 DWORD *vertex_remap_ptr;
1498 DWORD num_used_vertices;
1499 DWORD i;
1500
1501 hr = D3DXCreateBuffer(This->numvertices * sizeof(DWORD), vertex_remap);
1502 if (FAILED(hr)) return hr;
1503 vertex_remap_ptr = ID3DXBuffer_GetBufferPointer(*vertex_remap);
1504
1505 for (i = 0; i < This->numfaces * 3; i++)
1506 vertex_remap_ptr[indices[i]] = 1;
1507
1508 /* create old->new vertex mapping */
1509 num_used_vertices = 0;
1510 for (i = 0; i < This->numvertices; i++) {
1511 if (vertex_remap_ptr[i])
1512 vertex_remap_ptr[i] = num_used_vertices++;
1513 else
1514 vertex_remap_ptr[i] = -1;
1515 }
1516 /* convert indices */
1517 for (i = 0; i < This->numfaces * 3; i++)
1518 indices[i] = vertex_remap_ptr[indices[i]];
1519
1520 /* create new->old vertex mapping */
1521 num_used_vertices = 0;
1522 for (i = 0; i < This->numvertices; i++) {
1523 if (vertex_remap_ptr[i] != -1)
1524 vertex_remap_ptr[num_used_vertices++] = i;
1525 }
1526 for (i = num_used_vertices; i < This->numvertices; i++)
1527 vertex_remap_ptr[i] = -1;
1528
1529 *new_num_vertices = num_used_vertices;
1530
1531 return D3D_OK;
1532 }
1533
1534 /* count the number of unique attribute values in a sorted attribute buffer */
1535 static DWORD count_attributes(const DWORD *attrib_buffer, DWORD numfaces)
1536 {
1537 DWORD last_attribute = attrib_buffer[0];
1538 DWORD attrib_table_size = 1;
1539 DWORD i;
1540 for (i = 1; i < numfaces; i++) {
1541 if (attrib_buffer[i] != last_attribute) {
1542 last_attribute = attrib_buffer[i];
1543 attrib_table_size++;
1544 }
1545 }
1546 return attrib_table_size;
1547 }
1548
1549 static void fill_attribute_table(DWORD *attrib_buffer, DWORD numfaces, void *indices,
1550 BOOL is_32bit_indices, D3DXATTRIBUTERANGE *attrib_table)
1551 {
1552 DWORD attrib_table_size = 0;
1553 DWORD last_attribute = attrib_buffer[0];
1554 DWORD min_vertex, max_vertex;
1555 DWORD i;
1556
1557 attrib_table[0].AttribId = last_attribute;
1558 attrib_table[0].FaceStart = 0;
1559 min_vertex = (DWORD)-1;
1560 max_vertex = 0;
1561 for (i = 0; i < numfaces; i++) {
1562 DWORD j;
1563
1564 if (attrib_buffer[i] != last_attribute) {
1565 last_attribute = attrib_buffer[i];
1566 attrib_table[attrib_table_size].FaceCount = i - attrib_table[attrib_table_size].FaceStart;
1567 attrib_table[attrib_table_size].VertexStart = min_vertex;
1568 attrib_table[attrib_table_size].VertexCount = max_vertex - min_vertex + 1;
1569 attrib_table_size++;
1570 attrib_table[attrib_table_size].AttribId = attrib_buffer[i];
1571 attrib_table[attrib_table_size].FaceStart = i;
1572 min_vertex = (DWORD)-1;
1573 max_vertex = 0;
1574 }
1575 for (j = 0; j < 3; j++) {
1576 DWORD vertex_index = is_32bit_indices ? ((DWORD*)indices)[i * 3 + j] : ((WORD*)indices)[i * 3 + j];
1577 if (vertex_index < min_vertex)
1578 min_vertex = vertex_index;
1579 if (vertex_index > max_vertex)
1580 max_vertex = vertex_index;
1581 }
1582 }
1583 attrib_table[attrib_table_size].FaceCount = i - attrib_table[attrib_table_size].FaceStart;
1584 attrib_table[attrib_table_size].VertexStart = min_vertex;
1585 attrib_table[attrib_table_size].VertexCount = max_vertex - min_vertex + 1;
1586 attrib_table_size++;
1587 }
1588
1589 static int attrib_entry_compare(const DWORD **a, const DWORD **b)
1590 {
1591 const DWORD *ptr_a = *a;
1592 const DWORD *ptr_b = *b;
1593 int delta = *ptr_a - *ptr_b;
1594
1595 if (delta)
1596 return delta;
1597
1598 delta = ptr_a - ptr_b; /* for stable sort */
1599 return delta;
1600 }
1601
1602 /* Create face_remap, a new attribute buffer for attribute sort optimization. */
1603 static HRESULT remap_faces_for_attrsort(struct d3dx9_mesh *This, const DWORD *indices,
1604 DWORD *attrib_buffer, DWORD **sorted_attrib_buffer, DWORD **face_remap)
1605 {
1606 DWORD **sorted_attrib_ptr_buffer = NULL;
1607 DWORD i;
1608
1609 sorted_attrib_ptr_buffer = HeapAlloc(GetProcessHeap(), 0, This->numfaces * sizeof(*sorted_attrib_ptr_buffer));
1610 if (!sorted_attrib_ptr_buffer)
1611 return E_OUTOFMEMORY;
1612
1613 *face_remap = HeapAlloc(GetProcessHeap(), 0, This->numfaces * sizeof(**face_remap));
1614 if (!*face_remap)
1615 {
1616 HeapFree(GetProcessHeap(), 0, sorted_attrib_ptr_buffer);
1617 return E_OUTOFMEMORY;
1618 }
1619
1620 for (i = 0; i < This->numfaces; i++)
1621 sorted_attrib_ptr_buffer[i] = &attrib_buffer[i];
1622 qsort(sorted_attrib_ptr_buffer, This->numfaces, sizeof(*sorted_attrib_ptr_buffer),
1623 (int(*)(const void *, const void *))attrib_entry_compare);
1624
1625 for (i = 0; i < This->numfaces; i++)
1626 {
1627 DWORD old_face = sorted_attrib_ptr_buffer[i] - attrib_buffer;
1628 (*face_remap)[old_face] = i;
1629 }
1630
1631 /* overwrite sorted_attrib_ptr_buffer with the values themselves */
1632 *sorted_attrib_buffer = (DWORD*)sorted_attrib_ptr_buffer;
1633 for (i = 0; i < This->numfaces; i++)
1634 (*sorted_attrib_buffer)[(*face_remap)[i]] = attrib_buffer[i];
1635
1636 return D3D_OK;
1637 }
1638
1639 static HRESULT WINAPI d3dx9_mesh_OptimizeInplace(ID3DXMesh *iface, DWORD flags, const DWORD *adjacency_in,
1640 DWORD *adjacency_out, DWORD *face_remap_out, ID3DXBuffer **vertex_remap_out)
1641 {
1642 struct d3dx9_mesh *This = impl_from_ID3DXMesh(iface);
1643 void *indices = NULL;
1644 DWORD *attrib_buffer = NULL;
1645 HRESULT hr;
1646 ID3DXBuffer *vertex_remap = NULL;
1647 DWORD *face_remap = NULL; /* old -> new mapping */
1648 DWORD *dword_indices = NULL;
1649 DWORD new_num_vertices = 0;
1650 DWORD new_num_alloc_vertices = 0;
1651 IDirect3DVertexBuffer9 *vertex_buffer = NULL;
1652 DWORD *sorted_attrib_buffer = NULL;
1653 DWORD i;
1654
1655 TRACE("iface %p, flags %#x, adjacency_in %p, adjacency_out %p, face_remap_out %p, vertex_remap_out %p.\n",
1656 iface, flags, adjacency_in, adjacency_out, face_remap_out, vertex_remap_out);
1657
1658 if (!flags)
1659 return D3DERR_INVALIDCALL;
1660 if (!adjacency_in && (flags & (D3DXMESHOPT_VERTEXCACHE | D3DXMESHOPT_STRIPREORDER)))
1661 return D3DERR_INVALIDCALL;
1662 if ((flags & (D3DXMESHOPT_VERTEXCACHE | D3DXMESHOPT_STRIPREORDER)) == (D3DXMESHOPT_VERTEXCACHE | D3DXMESHOPT_STRIPREORDER))
1663 return D3DERR_INVALIDCALL;
1664
1665 if (flags & (D3DXMESHOPT_VERTEXCACHE | D3DXMESHOPT_STRIPREORDER))
1666 {
1667 if (flags & D3DXMESHOPT_VERTEXCACHE)
1668 FIXME("D3DXMESHOPT_VERTEXCACHE not implemented.\n");
1669 if (flags & D3DXMESHOPT_STRIPREORDER)
1670 FIXME("D3DXMESHOPT_STRIPREORDER not implemented.\n");
1671 return E_NOTIMPL;
1672 }
1673
1674 hr = iface->lpVtbl->LockIndexBuffer(iface, 0, &indices);
1675 if (FAILED(hr)) goto cleanup;
1676
1677 dword_indices = HeapAlloc(GetProcessHeap(), 0, This->numfaces * 3 * sizeof(DWORD));
1678 if (!dword_indices) return E_OUTOFMEMORY;
1679 if (This->options & D3DXMESH_32BIT) {
1680 memcpy(dword_indices, indices, This->numfaces * 3 * sizeof(DWORD));
1681 } else {
1682 WORD *word_indices = indices;
1683 for (i = 0; i < This->numfaces * 3; i++)
1684 dword_indices[i] = *word_indices++;
1685 }
1686
1687 if ((flags & (D3DXMESHOPT_COMPACT | D3DXMESHOPT_IGNOREVERTS | D3DXMESHOPT_ATTRSORT)) == D3DXMESHOPT_COMPACT)
1688 {
1689 new_num_alloc_vertices = This->numvertices;
1690 hr = compact_mesh(This, dword_indices, &new_num_vertices, &vertex_remap);
1691 if (FAILED(hr)) goto cleanup;
1692 } else if (flags & D3DXMESHOPT_ATTRSORT) {
1693 if (!(flags & D3DXMESHOPT_IGNOREVERTS))
1694 FIXME("D3DXMESHOPT_ATTRSORT vertex reordering not implemented.\n");
1695
1696 hr = iface->lpVtbl->LockAttributeBuffer(iface, 0, &attrib_buffer);
1697 if (FAILED(hr)) goto cleanup;
1698
1699 hr = remap_faces_for_attrsort(This, dword_indices, attrib_buffer, &sorted_attrib_buffer, &face_remap);
1700 if (FAILED(hr)) goto cleanup;
1701 }
1702
1703 if (vertex_remap)
1704 {
1705 /* reorder the vertices using vertex_remap */
1706 D3DVERTEXBUFFER_DESC vertex_desc;
1707 DWORD *vertex_remap_ptr = ID3DXBuffer_GetBufferPointer(vertex_remap);
1708 DWORD vertex_size = iface->lpVtbl->GetNumBytesPerVertex(iface);
1709 BYTE *orig_vertices;
1710 BYTE *new_vertices;
1711
1712 hr = IDirect3DVertexBuffer9_GetDesc(This->vertex_buffer, &vertex_desc);
1713 if (FAILED(hr)) goto cleanup;
1714
1715 hr = IDirect3DDevice9_CreateVertexBuffer(This->device, new_num_alloc_vertices * vertex_size,
1716 vertex_desc.Usage, This->fvf, vertex_desc.Pool, &vertex_buffer, NULL);
1717 if (FAILED(hr)) goto cleanup;
1718
1719 hr = IDirect3DVertexBuffer9_Lock(This->vertex_buffer, 0, 0, (void**)&orig_vertices, D3DLOCK_READONLY);
1720 if (FAILED(hr)) goto cleanup;
1721
1722 hr = IDirect3DVertexBuffer9_Lock(vertex_buffer, 0, 0, (void**)&new_vertices, 0);
1723 if (FAILED(hr)) {
1724 IDirect3DVertexBuffer9_Unlock(This->vertex_buffer);
1725 goto cleanup;
1726 }
1727
1728 for (i = 0; i < new_num_vertices; i++)
1729 memcpy(new_vertices + i * vertex_size, orig_vertices + vertex_remap_ptr[i] * vertex_size, vertex_size);
1730
1731 IDirect3DVertexBuffer9_Unlock(This->vertex_buffer);
1732 IDirect3DVertexBuffer9_Unlock(vertex_buffer);
1733 } else if (vertex_remap_out) {
1734 DWORD *vertex_remap_ptr;
1735
1736 hr = D3DXCreateBuffer(This->numvertices * sizeof(DWORD), &vertex_remap);
1737 if (FAILED(hr)) goto cleanup;
1738 vertex_remap_ptr = ID3DXBuffer_GetBufferPointer(vertex_remap);
1739 for (i = 0; i < This->numvertices; i++)
1740 *vertex_remap_ptr++ = i;
1741 }
1742
1743 if (flags & D3DXMESHOPT_ATTRSORT)
1744 {
1745 D3DXATTRIBUTERANGE *attrib_table;
1746 DWORD attrib_table_size;
1747
1748 attrib_table_size = count_attributes(sorted_attrib_buffer, This->numfaces);
1749 attrib_table = HeapAlloc(GetProcessHeap(), 0, attrib_table_size * sizeof(*attrib_table));
1750 if (!attrib_table) {
1751 hr = E_OUTOFMEMORY;
1752 goto cleanup;
1753 }
1754
1755 memcpy(attrib_buffer, sorted_attrib_buffer, This->numfaces * sizeof(*attrib_buffer));
1756
1757 /* reorder the indices using face_remap */
1758 if (This->options & D3DXMESH_32BIT) {
1759 for (i = 0; i < This->numfaces; i++)
1760 memcpy((DWORD*)indices + face_remap[i] * 3, dword_indices + i * 3, 3 * sizeof(DWORD));
1761 } else {
1762 WORD *word_indices = indices;
1763 for (i = 0; i < This->numfaces; i++) {
1764 DWORD new_pos = face_remap[i] * 3;
1765 DWORD old_pos = i * 3;
1766 word_indices[new_pos++] = dword_indices[old_pos++];
1767 word_indices[new_pos++] = dword_indices[old_pos++];
1768 word_indices[new_pos] = dword_indices[old_pos];
1769 }
1770 }
1771
1772 fill_attribute_table(attrib_buffer, This->numfaces, indices,
1773 This->options & D3DXMESH_32BIT, attrib_table);
1774
1775 HeapFree(GetProcessHeap(), 0, This->attrib_table);
1776 This->attrib_table = attrib_table;
1777 This->attrib_table_size = attrib_table_size;
1778 } else {
1779 if (This->options & D3DXMESH_32BIT) {
1780 memcpy(indices, dword_indices, This->numfaces * 3 * sizeof(DWORD));
1781 } else {
1782 WORD *word_indices = indices;
1783 for (i = 0; i < This->numfaces * 3; i++)
1784 *word_indices++ = dword_indices[i];
1785 }
1786 }
1787
1788 if (adjacency_out) {
1789 if (face_remap) {
1790 for (i = 0; i < This->numfaces; i++) {
1791 DWORD old_pos = i * 3;
1792 DWORD new_pos = face_remap[i] * 3;
1793 adjacency_out[new_pos++] = face_remap[adjacency_in[old_pos++]];
1794 adjacency_out[new_pos++] = face_remap[adjacency_in[old_pos++]];
1795 adjacency_out[new_pos++] = face_remap[adjacency_in[old_pos++]];
1796 }
1797 } else {
1798 memcpy(adjacency_out, adjacency_in, This->numfaces * 3 * sizeof(*adjacency_out));
1799 }
1800 }
1801 if (face_remap_out) {
1802 if (face_remap) {
1803 for (i = 0; i < This->numfaces; i++)
1804 face_remap_out[face_remap[i]] = i;
1805 } else {
1806 for (i = 0; i < This->numfaces; i++)
1807 face_remap_out[i] = i;
1808 }
1809 }
1810 if (vertex_remap_out)
1811 *vertex_remap_out = vertex_remap;
1812 vertex_remap = NULL;
1813
1814 if (vertex_buffer) {
1815 IDirect3DVertexBuffer9_Release(This->vertex_buffer);
1816 This->vertex_buffer = vertex_buffer;
1817 vertex_buffer = NULL;
1818 This->numvertices = new_num_vertices;
1819 }
1820
1821 hr = D3D_OK;
1822 cleanup:
1823 HeapFree(GetProcessHeap(), 0, sorted_attrib_buffer);
1824 HeapFree(GetProcessHeap(), 0, face_remap);
1825 HeapFree(GetProcessHeap(), 0, dword_indices);
1826 if (vertex_remap) ID3DXBuffer_Release(vertex_remap);
1827 if (vertex_buffer) IDirect3DVertexBuffer9_Release(vertex_buffer);
1828 if (attrib_buffer) iface->lpVtbl->UnlockAttributeBuffer(iface);
1829 if (indices) iface->lpVtbl->UnlockIndexBuffer(iface);
1830 return hr;
1831 }
1832
1833 static HRESULT WINAPI d3dx9_mesh_SetAttributeTable(ID3DXMesh *iface,
1834 const D3DXATTRIBUTERANGE *attrib_table, DWORD attrib_table_size)
1835 {
1836 struct d3dx9_mesh *mesh = impl_from_ID3DXMesh(iface);
1837 D3DXATTRIBUTERANGE *new_table = NULL;
1838
1839 TRACE("iface %p, attrib_table %p, attrib_table_size %u.\n", iface, attrib_table, attrib_table_size);
1840
1841 if (attrib_table_size) {
1842 size_t size = attrib_table_size * sizeof(*attrib_table);
1843
1844 new_table = HeapAlloc(GetProcessHeap(), 0, size);
1845 if (!new_table)
1846 return E_OUTOFMEMORY;
1847
1848 CopyMemory(new_table, attrib_table, size);
1849 } else if (attrib_table) {
1850 return D3DERR_INVALIDCALL;
1851 }
1852 HeapFree(GetProcessHeap(), 0, mesh->attrib_table);
1853 mesh->attrib_table = new_table;
1854 mesh->attrib_table_size = attrib_table_size;
1855
1856 return D3D_OK;
1857 }
1858
1859 static const struct ID3DXMeshVtbl D3DXMesh_Vtbl =
1860 {
1861 d3dx9_mesh_QueryInterface,
1862 d3dx9_mesh_AddRef,
1863 d3dx9_mesh_Release,
1864 d3dx9_mesh_DrawSubset,
1865 d3dx9_mesh_GetNumFaces,
1866 d3dx9_mesh_GetNumVertices,
1867 d3dx9_mesh_GetFVF,
1868 d3dx9_mesh_GetDeclaration,
1869 d3dx9_mesh_GetNumBytesPerVertex,
1870 d3dx9_mesh_GetOptions,
1871 d3dx9_mesh_GetDevice,
1872 d3dx9_mesh_CloneMeshFVF,
1873 d3dx9_mesh_CloneMesh,
1874 d3dx9_mesh_GetVertexBuffer,
1875 d3dx9_mesh_GetIndexBuffer,
1876 d3dx9_mesh_LockVertexBuffer,
1877 d3dx9_mesh_UnlockVertexBuffer,
1878 d3dx9_mesh_LockIndexBuffer,
1879 d3dx9_mesh_UnlockIndexBuffer,
1880 d3dx9_mesh_GetAttributeTable,
1881 d3dx9_mesh_ConvertPointRepsToAdjacency,
1882 d3dx9_mesh_ConvertAdjacencyToPointReps,
1883 d3dx9_mesh_GenerateAdjacency,
1884 d3dx9_mesh_UpdateSemantics,
1885 d3dx9_mesh_LockAttributeBuffer,
1886 d3dx9_mesh_UnlockAttributeBuffer,
1887 d3dx9_mesh_Optimize,
1888 d3dx9_mesh_OptimizeInplace,
1889 d3dx9_mesh_SetAttributeTable,
1890 };
1891
1892
1893 /* Algorithm taken from the article: An Efficient and Robust Ray-Box Intersection Algorithm
1894 Amy Williams University of Utah
1895 Steve Barrus University of Utah
1896 R. Keith Morley University of Utah
1897 Peter Shirley University of Utah
1898
1899 International Conference on Computer Graphics and Interactive Techniques archive
1900 ACM SIGGRAPH 2005 Courses
1901 Los Angeles, California
1902
1903 This algorithm is free of patents or of copyrights, as confirmed by Peter Shirley himself.
1904
1905 Algorithm: Consider the box as the intersection of three slabs. Clip the ray
1906 against each slab, if there's anything left of the ray after we're
1907 done we've got an intersection of the ray with the box. */
1908 BOOL WINAPI D3DXBoxBoundProbe(const D3DXVECTOR3 *pmin, const D3DXVECTOR3 *pmax,
1909 const D3DXVECTOR3 *prayposition, const D3DXVECTOR3 *praydirection)
1910 {
1911 FLOAT div, tmin, tmax, tymin, tymax, tzmin, tzmax;
1912
1913 div = 1.0f / praydirection->x;
1914 if ( div >= 0.0f )
1915 {
1916 tmin = ( pmin->x - prayposition->x ) * div;
1917 tmax = ( pmax->x - prayposition->x ) * div;
1918 }
1919 else
1920 {
1921 tmin = ( pmax->x - prayposition->x ) * div;
1922 tmax = ( pmin->x - prayposition->x ) * div;
1923 }
1924
1925 if ( tmax < 0.0f ) return FALSE;
1926
1927 div = 1.0f / praydirection->y;
1928 if ( div >= 0.0f )
1929 {
1930 tymin = ( pmin->y - prayposition->y ) * div;
1931 tymax = ( pmax->y - prayposition->y ) * div;
1932 }
1933 else
1934 {
1935 tymin = ( pmax->y - prayposition->y ) * div;
1936 tymax = ( pmin->y - prayposition->y ) * div;
1937 }
1938
1939 if ( ( tymax < 0.0f ) || ( tmin > tymax ) || ( tymin > tmax ) ) return FALSE;
1940
1941 if ( tymin > tmin ) tmin = tymin;
1942 if ( tymax < tmax ) tmax = tymax;
1943
1944 div = 1.0f / praydirection->z;
1945 if ( div >= 0.0f )
1946 {
1947 tzmin = ( pmin->z - prayposition->z ) * div;
1948 tzmax = ( pmax->z - prayposition->z ) * div;
1949 }
1950 else
1951 {
1952 tzmin = ( pmax->z - prayposition->z ) * div;
1953 tzmax = ( pmin->z - prayposition->z ) * div;
1954 }
1955
1956 if ( (tzmax < 0.0f ) || ( tmin > tzmax ) || ( tzmin > tmax ) ) return FALSE;
1957
1958 return TRUE;
1959 }
1960
1961 HRESULT WINAPI D3DXComputeBoundingBox(const D3DXVECTOR3 *pfirstposition,
1962 DWORD numvertices, DWORD dwstride, D3DXVECTOR3 *pmin, D3DXVECTOR3 *pmax)
1963 {
1964 D3DXVECTOR3 vec;
1965 unsigned int i;
1966
1967 if( !pfirstposition || !pmin || !pmax ) return D3DERR_INVALIDCALL;
1968
1969 *pmin = *pfirstposition;
1970 *pmax = *pmin;
1971
1972 for(i=0; i<numvertices; i++)
1973 {
1974 vec = *( (const D3DXVECTOR3*)((const char*)pfirstposition + dwstride * i) );
1975
1976 if ( vec.x < pmin->x ) pmin->x = vec.x;
1977 if ( vec.x > pmax->x ) pmax->x = vec.x;
1978
1979 if ( vec.y < pmin->y ) pmin->y = vec.y;
1980 if ( vec.y > pmax->y ) pmax->y = vec.y;
1981
1982 if ( vec.z < pmin->z ) pmin->z = vec.z;
1983 if ( vec.z > pmax->z ) pmax->z = vec.z;
1984 }
1985
1986 return D3D_OK;
1987 }
1988
1989 HRESULT WINAPI D3DXComputeBoundingSphere(const D3DXVECTOR3 *pfirstposition,
1990 DWORD numvertices, DWORD dwstride, D3DXVECTOR3 *pcenter, float *pradius)
1991 {
1992 D3DXVECTOR3 temp;
1993 FLOAT d;
1994 unsigned int i;
1995
1996 if( !pfirstposition || !pcenter || !pradius ) return D3DERR_INVALIDCALL;
1997
1998 temp.x = 0.0f;
1999 temp.y = 0.0f;
2000 temp.z = 0.0f;
2001 *pradius = 0.0f;
2002
2003 for(i=0; i<numvertices; i++)
2004 D3DXVec3Add(&temp, &temp, (const D3DXVECTOR3*)((const char*)pfirstposition + dwstride * i));
2005
2006 D3DXVec3Scale(pcenter, &temp, 1.0f / numvertices);
2007
2008 for(i=0; i<numvertices; i++)
2009 {
2010 d = D3DXVec3Length(D3DXVec3Subtract(&temp, (const D3DXVECTOR3*)((const char*)pfirstposition + dwstride * i), pcenter));
2011 if ( d > *pradius ) *pradius = d;
2012 }
2013 return D3D_OK;
2014 }
2015
2016 static void append_decl_element(D3DVERTEXELEMENT9 *declaration, UINT *idx, UINT *offset,
2017 D3DDECLTYPE type, D3DDECLUSAGE usage, UINT usage_idx)
2018 {
2019 declaration[*idx].Stream = 0;
2020 declaration[*idx].Offset = *offset;
2021 declaration[*idx].Type = type;
2022 declaration[*idx].Method = D3DDECLMETHOD_DEFAULT;
2023 declaration[*idx].Usage = usage;
2024 declaration[*idx].UsageIndex = usage_idx;
2025
2026 *offset += d3dx_decltype_size[type];
2027 ++(*idx);
2028 }
2029
2030 /*************************************************************************
2031 * D3DXDeclaratorFromFVF
2032 */
2033 HRESULT WINAPI D3DXDeclaratorFromFVF(DWORD fvf, D3DVERTEXELEMENT9 declaration[MAX_FVF_DECL_SIZE])
2034 {
2035 static const D3DVERTEXELEMENT9 end_element = D3DDECL_END();
2036 DWORD tex_count = (fvf & D3DFVF_TEXCOUNT_MASK) >> D3DFVF_TEXCOUNT_SHIFT;
2037 unsigned int offset = 0;
2038 unsigned int idx = 0;
2039 unsigned int i;
2040
2041 TRACE("fvf %#x, declaration %p.\n", fvf, declaration);
2042
2043 if (fvf & (D3DFVF_RESERVED0 | D3DFVF_RESERVED2)) return D3DERR_INVALIDCALL;
2044
2045 if (fvf & D3DFVF_POSITION_MASK)
2046 {
2047 BOOL has_blend = (fvf & D3DFVF_XYZB5) >= D3DFVF_XYZB1;
2048 DWORD blend_count = 1 + (((fvf & D3DFVF_XYZB5) - D3DFVF_XYZB1) >> 1);
2049 BOOL has_blend_idx = (fvf & D3DFVF_LASTBETA_D3DCOLOR) || (fvf & D3DFVF_LASTBETA_UBYTE4);
2050
2051 if (has_blend_idx) --blend_count;
2052
2053 if ((fvf & D3DFVF_POSITION_MASK) == D3DFVF_XYZW
2054 || (has_blend && blend_count > 4))
2055 return D3DERR_INVALIDCALL;
2056
2057 if ((fvf & D3DFVF_POSITION_MASK) == D3DFVF_XYZRHW)
2058 append_decl_element(declaration, &idx, &offset, D3DDECLTYPE_FLOAT4, D3DDECLUSAGE_POSITIONT, 0);
2059 else
2060 append_decl_element(declaration, &idx, &offset, D3DDECLTYPE_FLOAT3, D3DDECLUSAGE_POSITION, 0);
2061
2062 if (has_blend)
2063 {
2064 switch (blend_count)
2065 {
2066 case 0:
2067 break;
2068 case 1:
2069 append_decl_element(declaration, &idx, &offset, D3DDECLTYPE_FLOAT1, D3DDECLUSAGE_BLENDWEIGHT, 0);
2070 break;
2071 case 2:
2072 append_decl_element(declaration, &idx, &offset, D3DDECLTYPE_FLOAT2, D3DDECLUSAGE_BLENDWEIGHT, 0);
2073 break;
2074 case 3:
2075 append_decl_element(declaration, &idx, &offset, D3DDECLTYPE_FLOAT3, D3DDECLUSAGE_BLENDWEIGHT, 0);
2076 break;
2077 case 4:
2078 append_decl_element(declaration, &idx, &offset, D3DDECLTYPE_FLOAT4, D3DDECLUSAGE_BLENDWEIGHT, 0);
2079 break;
2080 default:
2081 ERR("Invalid blend count %u.\n", blend_count);
2082 break;
2083 }
2084
2085 if (has_blend_idx)
2086 {
2087 if (fvf & D3DFVF_LASTBETA_UBYTE4)
2088 append_decl_element(declaration, &idx, &offset, D3DDECLTYPE_UBYTE4, D3DDECLUSAGE_BLENDINDICES, 0);
2089 else if (fvf & D3DFVF_LASTBETA_D3DCOLOR)
2090 append_decl_element(declaration, &idx, &offset, D3DDECLTYPE_D3DCOLOR, D3DDECLUSAGE_BLENDINDICES, 0);
2091 }
2092 }
2093 }
2094
2095 if (fvf & D3DFVF_NORMAL)
2096 append_decl_element(declaration, &idx, &offset, D3DDECLTYPE_FLOAT3, D3DDECLUSAGE_NORMAL, 0);
2097 if (fvf & D3DFVF_PSIZE)
2098 append_decl_element(declaration, &idx, &offset, D3DDECLTYPE_FLOAT1, D3DDECLUSAGE_PSIZE, 0);
2099 if (fvf & D3DFVF_DIFFUSE)
2100 append_decl_element(declaration, &idx, &offset, D3DDECLTYPE_D3DCOLOR, D3DDECLUSAGE_COLOR, 0);
2101 if (fvf & D3DFVF_SPECULAR)
2102 append_decl_element(declaration, &idx, &offset, D3DDECLTYPE_D3DCOLOR, D3DDECLUSAGE_COLOR, 1);
2103
2104 for (i = 0; i < tex_count; ++i)
2105 {
2106 switch ((fvf >> (16 + 2 * i)) & 0x03)
2107 {
2108 case D3DFVF_TEXTUREFORMAT1:
2109 append_decl_element(declaration, &idx, &offset, D3DDECLTYPE_FLOAT1, D3DDECLUSAGE_TEXCOORD, i);
2110 break;
2111 case D3DFVF_TEXTUREFORMAT2:
2112 append_decl_element(declaration, &idx, &offset, D3DDECLTYPE_FLOAT2, D3DDECLUSAGE_TEXCOORD, i);
2113 break;
2114 case D3DFVF_TEXTUREFORMAT3:
2115 append_decl_element(declaration, &idx, &offset, D3DDECLTYPE_FLOAT3, D3DDECLUSAGE_TEXCOORD, i);
2116 break;
2117 case D3DFVF_TEXTUREFORMAT4:
2118 append_decl_element(declaration, &idx, &offset, D3DDECLTYPE_FLOAT4, D3DDECLUSAGE_TEXCOORD, i);
2119 break;
2120 }
2121 }
2122
2123 declaration[idx] = end_element;
2124
2125 return D3D_OK;
2126 }
2127
2128 /*************************************************************************
2129 * D3DXFVFFromDeclarator
2130 */
2131 HRESULT WINAPI D3DXFVFFromDeclarator(const D3DVERTEXELEMENT9 *declaration, DWORD *fvf)
2132 {
2133 unsigned int i = 0, texture, offset;
2134
2135 TRACE("(%p, %p)\n", declaration, fvf);
2136
2137 *fvf = 0;
2138 if (declaration[0].Type == D3DDECLTYPE_FLOAT3 && declaration[0].Usage == D3DDECLUSAGE_POSITION)
2139 {
2140 if ((declaration[1].Type == D3DDECLTYPE_FLOAT4 && declaration[1].Usage == D3DDECLUSAGE_BLENDWEIGHT &&
2141 declaration[1].UsageIndex == 0) &&
2142 (declaration[2].Type == D3DDECLTYPE_FLOAT1 && declaration[2].Usage == D3DDECLUSAGE_BLENDINDICES &&
2143 declaration[2].UsageIndex == 0))
2144 {
2145 return D3DERR_INVALIDCALL;
2146 }
2147 else if ((declaration[1].Type == D3DDECLTYPE_UBYTE4 || declaration[1].Type == D3DDECLTYPE_D3DCOLOR) &&
2148 declaration[1].Usage == D3DDECLUSAGE_BLENDINDICES && declaration[1].UsageIndex == 0)
2149 {
2150 if (declaration[1].Type == D3DDECLTYPE_UBYTE4)
2151 {
2152 *fvf |= D3DFVF_XYZB1 | D3DFVF_LASTBETA_UBYTE4;
2153 }
2154 else
2155 {
2156 *fvf |= D3DFVF_XYZB1 | D3DFVF_LASTBETA_D3DCOLOR;
2157 }
2158 i = 2;
2159 }
2160 else if (declaration[1].Type <= D3DDECLTYPE_FLOAT4 && declaration[1].Usage == D3DDECLUSAGE_BLENDWEIGHT &&
2161 declaration[1].UsageIndex == 0)
2162 {
2163 if ((declaration[2].Type == D3DDECLTYPE_UBYTE4 || declaration[2].Type == D3DDECLTYPE_D3DCOLOR) &&
2164 declaration[2].Usage == D3DDECLUSAGE_BLENDINDICES && declaration[2].UsageIndex == 0)
2165 {
2166 if (declaration[2].Type == D3DDECLTYPE_UBYTE4)
2167 {
2168 *fvf |= D3DFVF_LASTBETA_UBYTE4;
2169 }
2170 else
2171 {
2172 *fvf |= D3DFVF_LASTBETA_D3DCOLOR;
2173 }
2174 switch (declaration[1].Type)
2175 {
2176 case D3DDECLTYPE_FLOAT1: *fvf |= D3DFVF_XYZB2; break;
2177 case D3DDECLTYPE_FLOAT2: *fvf |= D3DFVF_XYZB3; break;
2178 case D3DDECLTYPE_FLOAT3: *fvf |= D3DFVF_XYZB4; break;
2179 case D3DDECLTYPE_FLOAT4: *fvf |= D3DFVF_XYZB5; break;
2180 }
2181 i = 3;
2182 }
2183 else
2184 {
2185 switch (declaration[1].Type)
2186 {
2187 case D3DDECLTYPE_FLOAT1: *fvf |= D3DFVF_XYZB1; break;
2188 case D3DDECLTYPE_FLOAT2: *fvf |= D3DFVF_XYZB2; break;
2189 case D3DDECLTYPE_FLOAT3: *fvf |= D3DFVF_XYZB3; break;
2190 case D3DDECLTYPE_FLOAT4: *fvf |= D3DFVF_XYZB4; break;
2191 }
2192 i = 2;
2193 }
2194 }
2195 else
2196 {
2197 *fvf |= D3DFVF_XYZ;
2198 i = 1;
2199 }
2200 }
2201 else if (declaration[0].Type == D3DDECLTYPE_FLOAT4 && declaration[0].Usage == D3DDECLUSAGE_POSITIONT &&
2202 declaration[0].UsageIndex == 0)
2203 {
2204 *fvf |= D3DFVF_XYZRHW;
2205 i = 1;
2206 }
2207
2208 if (declaration[i].Type == D3DDECLTYPE_FLOAT3 && declaration[i].Usage == D3DDECLUSAGE_NORMAL)
2209 {
2210 *fvf |= D3DFVF_NORMAL;
2211 i++;
2212 }
2213 if (declaration[i].Type == D3DDECLTYPE_FLOAT1 && declaration[i].Usage == D3DDECLUSAGE_PSIZE &&
2214 declaration[i].UsageIndex == 0)
2215 {
2216 *fvf |= D3DFVF_PSIZE;
2217 i++;
2218 }
2219 if (declaration[i].Type == D3DDECLTYPE_D3DCOLOR && declaration[i].Usage == D3DDECLUSAGE_COLOR &&
2220 declaration[i].UsageIndex == 0)
2221 {
2222 *fvf |= D3DFVF_DIFFUSE;
2223 i++;
2224 }
2225 if (declaration[i].Type == D3DDECLTYPE_D3DCOLOR && declaration[i].Usage == D3DDECLUSAGE_COLOR &&
2226 declaration[i].UsageIndex == 1)
2227 {
2228 *fvf |= D3DFVF_SPECULAR;
2229 i++;
2230 }
2231
2232 for (texture = 0; texture < D3DDP_MAXTEXCOORD; i++, texture++)
2233 {
2234 if (declaration[i].Stream == 0xFF)
2235 {
2236 break;
2237 }
2238 else if (declaration[i].Type == D3DDECLTYPE_FLOAT1 && declaration[i].Usage == D3DDECLUSAGE_TEXCOORD &&
2239 declaration[i].UsageIndex == texture)
2240 {
2241 *fvf |= D3DFVF_TEXCOORDSIZE1(declaration[i].UsageIndex);
2242 }
2243 else if (declaration[i].Type == D3DDECLTYPE_FLOAT2 && declaration[i].Usage == D3DDECLUSAGE_TEXCOORD &&
2244 declaration[i].UsageIndex == texture)
2245 {
2246 *fvf |= D3DFVF_TEXCOORDSIZE2(declaration[i].UsageIndex);
2247 }
2248 else if (declaration[i].Type == D3DDECLTYPE_FLOAT3 && declaration[i].Usage == D3DDECLUSAGE_TEXCOORD &&
2249 declaration[i].UsageIndex == texture)
2250 {
2251 *fvf |= D3DFVF_TEXCOORDSIZE3(declaration[i].UsageIndex);
2252 }
2253 else if (declaration[i].Type == D3DDECLTYPE_FLOAT4 && declaration[i].Usage == D3DDECLUSAGE_TEXCOORD &&
2254 declaration[i].UsageIndex == texture)
2255 {
2256 *fvf |= D3DFVF_TEXCOORDSIZE4(declaration[i].UsageIndex);
2257 }
2258 else
2259 {
2260 return D3DERR_INVALIDCALL;
2261 }
2262 }
2263
2264 *fvf |= (texture << D3DFVF_TEXCOUNT_SHIFT);
2265
2266 for (offset = 0, i = 0; declaration[i].Stream != 0xFF;
2267 offset += d3dx_decltype_size[declaration[i].Type], i++)
2268 {
2269 if (declaration[i].Offset != offset)
2270 {
2271 return D3DERR_INVALIDCALL;
2272 }
2273 }
2274
2275 return D3D_OK;
2276 }
2277
2278 /*************************************************************************
2279 * D3DXGetFVFVertexSize
2280 */
2281 static UINT Get_TexCoord_Size_From_FVF(DWORD FVF, int tex_num)
2282 {
2283 return (((((FVF) >> (16 + (2 * (tex_num)))) + 1) & 0x03) + 1);
2284 }
2285
2286 UINT WINAPI D3DXGetFVFVertexSize(DWORD FVF)
2287 {
2288 DWORD size = 0;
2289 UINT i;
2290 UINT numTextures = (FVF & D3DFVF_TEXCOUNT_MASK) >> D3DFVF_TEXCOUNT_SHIFT;
2291
2292 if (FVF & D3DFVF_NORMAL) size += sizeof(D3DXVECTOR3);
2293 if (FVF & D3DFVF_DIFFUSE) size += sizeof(DWORD);
2294 if (FVF & D3DFVF_SPECULAR) size += sizeof(DWORD);
2295 if (FVF & D3DFVF_PSIZE) size += sizeof(DWORD);
2296
2297 switch (FVF & D3DFVF_POSITION_MASK)
2298 {
2299 case D3DFVF_XYZ: size += sizeof(D3DXVECTOR3); break;
2300 case D3DFVF_XYZRHW: size += 4 * sizeof(FLOAT); break;
2301 case D3DFVF_XYZB1: size += 4 * sizeof(FLOAT); break;
2302 case D3DFVF_XYZB2: size += 5 * sizeof(FLOAT); break;
2303 case D3DFVF_XYZB3: size += 6 * sizeof(FLOAT); break;
2304 case D3DFVF_XYZB4: size += 7 * sizeof(FLOAT); break;
2305 case D3DFVF_XYZB5: size += 8 * sizeof(FLOAT); break;
2306 case D3DFVF_XYZW: size += 4 * sizeof(FLOAT); break;
2307 }
2308
2309 for (i = 0; i < numTextures; i++)
2310 {
2311 size += Get_TexCoord_Size_From_FVF(FVF, i) * sizeof(FLOAT);
2312 }
2313
2314 return size;
2315 }
2316
2317 /*************************************************************************
2318 * D3DXGetDeclVertexSize
2319 */
2320 UINT WINAPI D3DXGetDeclVertexSize(const D3DVERTEXELEMENT9 *decl, DWORD stream_idx)
2321 {
2322 const D3DVERTEXELEMENT9 *element;
2323 UINT size = 0;
2324
2325 TRACE("decl %p, stream_idx %u\n", decl, stream_idx);
2326
2327 if (!decl) return 0;
2328
2329 for (element = decl; element->Stream != 0xff; ++element)
2330 {
2331 UINT type_size;
2332
2333 if (element->Stream != stream_idx) continue;
2334
2335 if (element->Type >= sizeof(d3dx_decltype_size) / sizeof(*d3dx_decltype_size))
2336 {
2337 FIXME("Unhandled element type %#x, size will be incorrect.\n", element->Type);
2338 continue;
2339 }
2340
2341 type_size = d3dx_decltype_size[element->Type];
2342 if (element->Offset + type_size > size) size = element->Offset + type_size;
2343 }
2344
2345 return size;
2346 }
2347
2348 /*************************************************************************
2349 * D3DXGetDeclLength
2350 */
2351 UINT WINAPI D3DXGetDeclLength(const D3DVERTEXELEMENT9 *decl)
2352 {
2353 const D3DVERTEXELEMENT9 *element;
2354
2355 TRACE("decl %p\n", decl);
2356
2357 /* null decl results in exception on Windows XP */
2358
2359 for (element = decl; element->Stream != 0xff; ++element);
2360
2361 return element - decl;
2362 }
2363
2364 BOOL WINAPI D3DXIntersectTri(const D3DXVECTOR3 *p0, const D3DXVECTOR3 *p1, const D3DXVECTOR3 *p2,
2365 const D3DXVECTOR3 *praypos, const D3DXVECTOR3 *praydir, float *pu, float *pv, float *pdist)
2366 {
2367 D3DXMATRIX m;
2368 D3DXVECTOR4 vec;
2369
2370 TRACE("p0 %p, p1 %p, p2 %p, praypos %p, praydir %p, pu %p, pv %p, pdist %p.\n",
2371 p0, p1, p2, praypos, praydir, pu, pv, pdist);
2372
2373 m.u.m[0][0] = p1->x - p0->x;
2374 m.u.m[1][0] = p2->x - p0->x;
2375 m.u.m[2][0] = -praydir->x;
2376 m.u.m[3][0] = 0.0f;
2377 m.u.m[0][1] = p1->y - p0->y;
2378 m.u.m[1][1] = p2->y - p0->y;
2379 m.u.m[2][1] = -praydir->y;
2380 m.u.m[3][1] = 0.0f;
2381 m.u.m[0][2] = p1->z - p0->z;
2382 m.u.m[1][2] = p2->z - p0->z;
2383 m.u.m[2][2] = -praydir->z;
2384 m.u.m[3][2] = 0.0f;
2385 m.u.m[0][3] = 0.0f;
2386 m.u.m[1][3] = 0.0f;
2387 m.u.m[2][3] = 0.0f;
2388 m.u.m[3][3] = 1.0f;
2389
2390 vec.x = praypos->x - p0->x;
2391 vec.y = praypos->y - p0->y;
2392 vec.z = praypos->z - p0->z;
2393 vec.w = 0.0f;
2394
2395 if ( D3DXMatrixInverse(&m, NULL, &m) )
2396 {
2397 D3DXVec4Transform(&vec, &vec, &m);
2398 if ( (vec.x >= 0.0f) && (vec.y >= 0.0f) && (vec.x + vec.y <= 1.0f) && (vec.z >= 0.0f) )
2399 {
2400 if (pu) *pu = vec.x;
2401 if (pv) *pv = vec.y;
2402 if (pdist) *pdist = fabsf( vec.z );
2403 return TRUE;
2404 }
2405 }
2406
2407 return FALSE;
2408 }
2409
2410 BOOL WINAPI D3DXSphereBoundProbe(const D3DXVECTOR3 *pcenter, float radius,
2411 const D3DXVECTOR3 *prayposition, const D3DXVECTOR3 *praydirection)
2412 {
2413 D3DXVECTOR3 difference;
2414 FLOAT a, b, c, d;
2415
2416 a = D3DXVec3LengthSq(praydirection);
2417 if (!D3DXVec3Subtract(&difference, prayposition, pcenter)) return FALSE;
2418 b = D3DXVec3Dot(&difference, praydirection);
2419 c = D3DXVec3LengthSq(&difference) - radius * radius;
2420 d = b * b - a * c;
2421
2422 if ( ( d <= 0.0f ) || ( sqrt(d) <= b ) ) return FALSE;
2423 return TRUE;
2424 }
2425
2426 /*************************************************************************
2427 * D3DXCreateMesh
2428 */
2429 HRESULT WINAPI D3DXCreateMesh(DWORD numfaces, DWORD numvertices, DWORD options,
2430 const D3DVERTEXELEMENT9 *declaration, struct IDirect3DDevice9 *device, struct ID3DXMesh **mesh)
2431 {
2432 HRESULT hr;
2433 DWORD fvf;
2434 IDirect3DVertexDeclaration9 *vertex_declaration;
2435 UINT vertex_declaration_size;
2436 UINT num_elem;
2437 IDirect3DVertexBuffer9 *vertex_buffer;
2438 IDirect3DIndexBuffer9 *index_buffer;
2439 DWORD *attrib_buffer;
2440 struct d3dx9_mesh *object;
2441 DWORD index_usage = 0;
2442 D3DPOOL index_pool = D3DPOOL_DEFAULT;
2443 D3DFORMAT index_format = D3DFMT_INDEX16;
2444 DWORD vertex_usage = 0;
2445 D3DPOOL vertex_pool = D3DPOOL_DEFAULT;
2446 int i;
2447
2448 TRACE("numfaces %u, numvertices %u, options %#x, declaration %p, device %p, mesh %p.\n",
2449 numfaces, numvertices, options, declaration, device, mesh);
2450
2451 if (numfaces == 0 || numvertices == 0 || declaration == NULL || device == NULL || mesh == NULL ||
2452 /* D3DXMESH_VB_SHARE is for cloning, and D3DXMESH_USEHWONLY is for ConvertToBlendedMesh */
2453 (options & (D3DXMESH_VB_SHARE | D3DXMESH_USEHWONLY | 0xfffe0000)))
2454 {
2455 return D3DERR_INVALIDCALL;
2456 }
2457 for (i = 0; declaration[i].Stream != 0xff; i++)
2458 if (declaration[i].Stream != 0)
2459 return D3DERR_INVALIDCALL;
2460 num_elem = i + 1;
2461
2462 if (options & D3DXMESH_32BIT)
2463 index_format = D3DFMT_INDEX32;
2464
2465 if (options & D3DXMESH_DONOTCLIP) {
2466 index_usage |= D3DUSAGE_DONOTCLIP;
2467 vertex_usage |= D3DUSAGE_DONOTCLIP;
2468 }
2469 if (options & D3DXMESH_POINTS) {
2470 index_usage |= D3DUSAGE_POINTS;
2471 vertex_usage |= D3DUSAGE_POINTS;
2472 }
2473 if (options & D3DXMESH_RTPATCHES) {
2474 index_usage |= D3DUSAGE_RTPATCHES;
2475 vertex_usage |= D3DUSAGE_RTPATCHES;
2476 }
2477 if (options & D3DXMESH_NPATCHES) {
2478 index_usage |= D3DUSAGE_NPATCHES;
2479 vertex_usage |= D3DUSAGE_NPATCHES;
2480 }
2481
2482 if (options & D3DXMESH_VB_SYSTEMMEM)
2483 vertex_pool = D3DPOOL_SYSTEMMEM;
2484 else if (options & D3DXMESH_VB_MANAGED)
2485 vertex_pool = D3DPOOL_MANAGED;
2486
2487 if (options & D3DXMESH_VB_WRITEONLY)
2488 vertex_usage |= D3DUSAGE_WRITEONLY;
2489 if (options & D3DXMESH_VB_DYNAMIC)
2490 vertex_usage |= D3DUSAGE_DYNAMIC;
2491 if (options & D3DXMESH_VB_SOFTWAREPROCESSING)
2492 vertex_usage |= D3DUSAGE_SOFTWAREPROCESSING;
2493
2494 if (options & D3DXMESH_IB_SYSTEMMEM)
2495 index_pool = D3DPOOL_SYSTEMMEM;
2496 else if (options & D3DXMESH_IB_MANAGED)
2497 index_pool = D3DPOOL_MANAGED;
2498
2499 if (options & D3DXMESH_IB_WRITEONLY)
2500 index_usage |= D3DUSAGE_WRITEONLY;
2501 if (options & D3DXMESH_IB_DYNAMIC)
2502 index_usage |= D3DUSAGE_DYNAMIC;
2503 if (options & D3DXMESH_IB_SOFTWAREPROCESSING)
2504 index_usage |= D3DUSAGE_SOFTWAREPROCESSING;
2505
2506 hr = D3DXFVFFromDeclarator(declaration, &fvf);
2507 if (hr != D3D_OK)
2508 {
2509 fvf = 0;
2510 }
2511
2512 /* Create vertex declaration */
2513 hr = IDirect3DDevice9_CreateVertexDeclaration(device,
2514 declaration,
2515 &vertex_declaration);
2516 if (FAILED(hr))
2517 {
2518 WARN("Unexpected return value %x from IDirect3DDevice9_CreateVertexDeclaration.\n",hr);
2519 return hr;
2520 }
2521 vertex_declaration_size = D3DXGetDeclVertexSize(declaration, declaration[0].Stream);
2522
2523 /* Create vertex buffer */
2524 hr = IDirect3DDevice9_CreateVertexBuffer(device,
2525 numvertices * vertex_declaration_size,
2526 vertex_usage,
2527 fvf,
2528 vertex_pool,
2529 &vertex_buffer,
2530 NULL);
2531 if (FAILED(hr))
2532 {
2533 WARN("Unexpected return value %x from IDirect3DDevice9_CreateVertexBuffer.\n",hr);
2534 IDirect3DVertexDeclaration9_Release(vertex_declaration);
2535 return hr;
2536 }
2537
2538 /* Create index buffer */
2539 hr = IDirect3DDevice9_CreateIndexBuffer(device,
2540 numfaces * 3 * ((index_format == D3DFMT_INDEX16) ? 2 : 4),
2541 index_usage,
2542 index_format,
2543 index_pool,
2544 &index_buffer,
2545 NULL);
2546 if (FAILED(hr))
2547 {
2548 WARN("Unexpected return value %x from IDirect3DDevice9_CreateVertexBuffer.\n",hr);
2549 IDirect3DVertexBuffer9_Release(vertex_buffer);
2550 IDirect3DVertexDeclaration9_Release(vertex_declaration);
2551 return hr;
2552 }
2553
2554 attrib_buffer = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, numfaces * sizeof(*attrib_buffer));
2555 object = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(*object));
2556 if (object == NULL || attrib_buffer == NULL)
2557 {
2558 HeapFree(GetProcessHeap(), 0, object);
2559 HeapFree(GetProcessHeap(), 0, attrib_buffer);
2560 IDirect3DIndexBuffer9_Release(index_buffer);
2561 IDirect3DVertexBuffer9_Release(vertex_buffer);
2562 IDirect3DVertexDeclaration9_Release(vertex_declaration);
2563 *mesh = NULL;
2564 return E_OUTOFMEMORY;
2565 }
2566 object->ID3DXMesh_iface.lpVtbl = &D3DXMesh_Vtbl;
2567 object->ref = 1;
2568
2569 object->numfaces = numfaces;
2570 object->numvertices = numvertices;
2571 object->options = options;
2572 object->fvf = fvf;
2573 object->device = device;
2574 IDirect3DDevice9_AddRef(device);
2575
2576 copy_declaration(object->cached_declaration, declaration, num_elem);
2577 object->vertex_declaration = vertex_declaration;
2578 object->vertex_declaration_size = vertex_declaration_size;
2579 object->num_elem = num_elem;
2580 object->vertex_buffer = vertex_buffer;
2581 object->index_buffer = index_buffer;
2582 object->attrib_buffer = attrib_buffer;
2583
2584 *mesh = &object->ID3DXMesh_iface;
2585
2586 return D3D_OK;
2587 }
2588
2589 /*************************************************************************
2590 * D3DXCreateMeshFVF
2591 */
2592 HRESULT WINAPI D3DXCreateMeshFVF(DWORD numfaces, DWORD numvertices, DWORD options,
2593 DWORD fvf, struct IDirect3DDevice9 *device, struct ID3DXMesh **mesh)
2594 {
2595 HRESULT hr;
2596 D3DVERTEXELEMENT9 declaration[MAX_FVF_DECL_SIZE];
2597
2598 TRACE("(%u, %u, %u, %u, %p, %p)\n", numfaces, numvertices, options, fvf, device, mesh);
2599
2600 hr = D3DXDeclaratorFromFVF(fvf, declaration);
2601 if (FAILED(hr)) return hr;
2602
2603 return D3DXCreateMesh(numfaces, numvertices, options, declaration, device, mesh);
2604 }
2605
2606
2607 struct mesh_data {
2608 DWORD num_vertices;
2609 DWORD num_poly_faces;
2610 DWORD num_tri_faces;
2611 D3DXVECTOR3 *vertices;
2612 DWORD *num_tri_per_face;
2613 DWORD *indices;
2614
2615 DWORD fvf;
2616
2617 /* optional mesh data */
2618
2619 DWORD num_normals;
2620 D3DXVECTOR3 *normals;
2621 DWORD *normal_indices;
2622
2623 D3DXVECTOR2 *tex_coords;
2624
2625 DWORD *vertex_colors;
2626
2627 DWORD num_materials;
2628 D3DXMATERIAL *materials;
2629 DWORD *material_indices;
2630
2631 struct ID3DXSkinInfo *skin_info;
2632 DWORD nb_bones;
2633 };
2634
2635 static HRESULT parse_texture_filename(ID3DXFileData *filedata, char **filename_out)
2636 {
2637 HRESULT hr;
2638 SIZE_T data_size;
2639 BYTE *data;
2640 char *filename_in;
2641 char *filename = NULL;
2642
2643 /* template TextureFilename {
2644 * STRING filename;
2645 * }
2646 */
2647
2648 HeapFree(GetProcessHeap(), 0, *filename_out);
2649 *filename_out = NULL;
2650
2651 hr = filedata->lpVtbl->Lock(filedata, &data_size, (const void**)&data);
2652 if (FAILED(hr)) return hr;
2653
2654 /* FIXME: String must be retrieved directly instead of through a pointer once ID3DXFILE is fixed */
2655 if (data_size < sizeof(filename_in))
2656 {
2657 WARN("truncated data (%lu bytes)\n", data_size);
2658 filedata->lpVtbl->Unlock(filedata);
2659 return E_FAIL;
2660 }
2661 filename_in = *(char **)data;
2662
2663 filename = HeapAlloc(GetProcessHeap(), 0, strlen(filename_in) + 1);
2664 if (!filename) {
2665 filedata->lpVtbl->Unlock(filedata);
2666 return E_OUTOFMEMORY;
2667 }
2668
2669 strcpy(filename, filename_in);
2670 *filename_out = filename;
2671
2672 filedata->lpVtbl->Unlock(filedata);
2673
2674 return D3D_OK;
2675 }
2676
2677 static HRESULT parse_material(ID3DXFileData *filedata, D3DXMATERIAL *material)
2678 {
2679 HRESULT hr;
2680 SIZE_T data_size;
2681 const BYTE *data;
2682 GUID type;
2683 ID3DXFileData *child;
2684 SIZE_T i, nb_children;
2685
2686 material->pTextureFilename = NULL;
2687
2688 hr = filedata->lpVtbl->Lock(filedata, &data_size, (const void**)&data);
2689 if (FAILED(hr)) return hr;
2690
2691 /*
2692 * template ColorRGBA {
2693 * FLOAT red;
2694 * FLOAT green;
2695 * FLOAT blue;
2696 * FLOAT alpha;
2697 * }
2698 * template ColorRGB {
2699 * FLOAT red;
2700 * FLOAT green;
2701 * FLOAT blue;
2702 * }
2703 * template Material {
2704 * ColorRGBA faceColor;
2705 * FLOAT power;
2706 * ColorRGB specularColor;
2707 * ColorRGB emissiveColor;
2708 * [ ... ]
2709 * }
2710 */
2711 if (data_size != sizeof(FLOAT) * 11) {
2712 WARN("incorrect data size (%ld bytes)\n", data_size);
2713 filedata->lpVtbl->Unlock(filedata);
2714 return E_FAIL;
2715 }
2716
2717 memcpy(&material->MatD3D.Diffuse, data, sizeof(D3DCOLORVALUE));
2718 data += sizeof(D3DCOLORVALUE);
2719 material->MatD3D.Power = *(FLOAT*)data;
2720 data += sizeof(FLOAT);
2721 memcpy(&material->MatD3D.Specular, data, sizeof(FLOAT) * 3);
2722 material->MatD3D.Specular.a = 1.0f;
2723 data += 3 * sizeof(FLOAT);
2724 memcpy(&material->MatD3D.Emissive, data, sizeof(FLOAT) * 3);
2725 material->MatD3D.Emissive.a = 1.0f;
2726 material->MatD3D.Ambient.r = 0.0f;
2727 material->MatD3D.Ambient.g = 0.0f;
2728 material->MatD3D.Ambient.b = 0.0f;
2729 material->MatD3D.Ambient.a = 1.0f;
2730
2731 filedata->lpVtbl->Unlock(filedata);
2732
2733 hr = filedata->lpVtbl->GetChildren(filedata, &nb_children);
2734 if (FAILED(hr))
2735 return hr;
2736
2737 for (i = 0; i < nb_children; i++)
2738 {
2739 hr = filedata->lpVtbl->GetChild(filedata, i, &child);
2740 if (FAILED(hr))
2741 return hr;
2742 hr = child->lpVtbl->GetType(child, &type);
2743 if (FAILED(hr))
2744 goto err;
2745
2746 if (IsEqualGUID(&type, &TID_D3DRMTextureFilename)) {
2747 hr = parse_texture_filename(child, &material->pTextureFilename);
2748 if (FAILED(hr))
2749 goto err;
2750 }
2751 IUnknown_Release(child);
2752 }
2753 return D3D_OK;
2754
2755 err:
2756 IUnknown_Release(child);
2757 return hr;
2758 }
2759
2760 static void destroy_materials(struct mesh_data *mesh)
2761 {
2762 DWORD i;
2763 for (i = 0; i < mesh->num_materials; i++)
2764 HeapFree(GetProcessHeap(), 0, mesh->materials[i].pTextureFilename);
2765 HeapFree(GetProcessHeap(), 0, mesh->materials);
2766 HeapFree(GetProcessHeap(), 0, mesh->material_indices);
2767 mesh->num_materials = 0;
2768 mesh->materials = NULL;
2769 mesh->material_indices = NULL;
2770 }
2771
2772 static HRESULT parse_material_list(ID3DXFileData *filedata, struct mesh_data *mesh)
2773 {
2774 HRESULT hr;
2775 SIZE_T data_size;
2776 const DWORD *data, *in_ptr;
2777 GUID type;
2778 ID3DXFileData *child = NULL;
2779 DWORD num_materials;
2780 DWORD i;
2781 SIZE_T nb_children;
2782
2783 destroy_materials(mesh);
2784
2785 hr = filedata->lpVtbl->Lock(filedata, &data_size, (const void**)&data);
2786 if (FAILED(hr)) return hr;
2787
2788 /* template MeshMaterialList {
2789 * DWORD nMaterials;
2790 * DWORD nFaceIndexes;
2791 * array DWORD faceIndexes[nFaceIndexes];
2792 * [ Material ]
2793 * }
2794 */
2795
2796 in_ptr = data;
2797 hr = E_FAIL;
2798
2799 if (data_size < sizeof(DWORD)) {
2800 WARN("truncated data (%ld bytes)\n", data_size);
2801 goto end;
2802 }
2803 num_materials = *in_ptr++;
2804 if (!num_materials) {
2805 hr = D3D_OK;
2806 goto end;
2807 }
2808
2809 if (data_size < 2 * sizeof(DWORD)) {
2810 WARN("truncated data (%ld bytes)\n", data_size);
2811 goto end;
2812 }
2813 if (*in_ptr++ != mesh->num_poly_faces) {
2814 WARN("number of material face indices (%u) doesn't match number of faces (%u)\n",
2815 *(in_ptr - 1), mesh->num_poly_faces);
2816 goto end;
2817 }
2818 if (data_size < 2 * sizeof(DWORD) + mesh->num_poly_faces * sizeof(DWORD)) {
2819 WARN("truncated data (%ld bytes)\n", data_size);
2820 goto end;
2821 }
2822 for (i = 0; i < mesh->num_poly_faces; i++) {
2823 if (*in_ptr++ >= num_materials) {
2824 WARN("face %u: reference to undefined material %u (only %u materials)\n",
2825 i, *(in_ptr - 1), num_materials);
2826 goto end;
2827 }
2828 }
2829
2830 mesh->materials = HeapAlloc(GetProcessHeap(), 0, num_materials * sizeof(*mesh->materials));
2831 mesh->material_indices = HeapAlloc(GetProcessHeap(), 0, mesh->num_poly_faces * sizeof(*mesh->material_indices));
2832 if (!mesh->materials || !mesh->material_indices) {
2833 hr = E_OUTOFMEMORY;
2834 goto end;
2835 }
2836 memcpy(mesh->material_indices, data + 2, mesh->num_poly_faces * sizeof(DWORD));
2837
2838 hr = filedata->lpVtbl->GetChildren(filedata, &nb_children);
2839 if (FAILED(hr))
2840 goto end;
2841
2842 for (i = 0; i < nb_children; i++)
2843 {
2844 hr = filedata->lpVtbl->GetChild(filedata, i, &child);
2845 if (FAILED(hr))
2846 goto end;
2847 hr = child->lpVtbl->GetType(child, &type);
2848 if (FAILED(hr))
2849 goto end;
2850
2851 if (IsEqualGUID(&type, &TID_D3DRMMaterial)) {
2852 if (mesh->num_materials >= num_materials) {
2853 WARN("more materials defined than declared\n");
2854 hr = E_FAIL;
2855 goto end;
2856 }
2857 hr = parse_material(child, &mesh->materials[mesh->num_materials++]);
2858 if (FAILED(hr))
2859 goto end;
2860 }
2861
2862 IUnknown_Release(child);
2863 child = NULL;
2864 }
2865 if (num_materials != mesh->num_materials) {
2866 WARN("only %u of %u materials defined\n", num_materials, mesh->num_materials);
2867 hr = E_FAIL;
2868 }
2869
2870 end:
2871 if (child)
2872 IUnknown_Release(child);
2873 filedata->lpVtbl->Unlock(filedata);
2874 return hr;
2875 }
2876
2877 static HRESULT parse_texture_coords(ID3DXFileData *filedata, struct mesh_data *mesh)
2878 {
2879 HRESULT hr;
2880 SIZE_T data_size;
2881 const BYTE *data;
2882
2883 HeapFree(GetProcessHeap(), 0, mesh->tex_coords);
2884 mesh->tex_coords = NULL;
2885
2886 hr = filedata->lpVtbl->Lock(filedata, &data_size, (const void**)&data);
2887 if (FAILED(hr)) return hr;
2888
2889 /* template Coords2d {
2890 * FLOAT u;
2891 * FLOAT v;
2892 * }
2893 * template MeshTextureCoords {
2894 * DWORD nTextureCoords;
2895 * array Coords2d textureCoords[nTextureCoords];
2896 * }
2897 */
2898
2899 hr = E_FAIL;
2900
2901 if (data_size < sizeof(DWORD)) {
2902 WARN("truncated data (%ld bytes)\n", data_size);
2903 goto end;
2904 }
2905 if (*(DWORD*)data != mesh->num_vertices) {
2906 WARN("number of texture coordinates (%u) doesn't match number of vertices (%u)\n",
2907 *(DWORD*)data, mesh->num_vertices);
2908 goto end;
2909 }
2910 data += sizeof(DWORD);
2911 if (data_size < sizeof(DWORD) + mesh->num_vertices * sizeof(*mesh->tex_coords)) {
2912 WARN("truncated data (%ld bytes)\n", data_size);
2913 goto end;
2914 }
2915
2916 mesh->tex_coords = HeapAlloc(GetProcessHeap(), 0, mesh->num_vertices * sizeof(*mesh->tex_coords));
2917 if (!mesh->tex_coords) {
2918 hr = E_OUTOFMEMORY;
2919 goto end;
2920 }
2921 memcpy(mesh->tex_coords, data, mesh->num_vertices * sizeof(*mesh->tex_coords));
2922
2923 mesh->fvf |= D3DFVF_TEX1;
2924
2925 hr = D3D_OK;
2926
2927 end:
2928 filedata->lpVtbl->Unlock(filedata);
2929 return hr;
2930 }
2931
2932 static HRESULT parse_vertex_colors(ID3DXFileData *filedata, struct mesh_data *mesh)
2933 {
2934 HRESULT hr;
2935 SIZE_T data_size;
2936 const BYTE *data;
2937 DWORD num_colors;
2938 DWORD i;
2939
2940 HeapFree(GetProcessHeap(), 0, mesh->vertex_colors);
2941 mesh->vertex_colors = NULL;
2942
2943 hr = filedata->lpVtbl->Lock(filedata, &data_size, (const void**)&data);
2944 if (FAILED(hr)) return hr;
2945
2946 /* template IndexedColor {
2947 * DWORD index;
2948 * ColorRGBA indexColor;
2949 * }
2950 * template MeshVertexColors {
2951 * DWORD nVertexColors;
2952 * array IndexedColor vertexColors[nVertexColors];
2953 * }
2954 */
2955
2956 hr = E_FAIL;
2957
2958 if (data_size < sizeof(DWORD)) {
2959 WARN("truncated data (%ld bytes)\n", data_size);
2960 goto end;
2961 }
2962 num_colors = *(DWORD*)data;
2963 data += sizeof(DWORD);
2964 if (data_size < sizeof(DWORD) + num_colors * (sizeof(DWORD) + sizeof(D3DCOLORVALUE))) {
2965 WARN("truncated data (%ld bytes)\n", data_size);
2966 goto end;
2967 }
2968
2969 mesh->vertex_colors = HeapAlloc(GetProcessHeap(), 0, mesh->num_vertices * sizeof(DWORD));
2970 if (!mesh->vertex_colors) {
2971 hr = E_OUTOFMEMORY;
2972 goto end;
2973 }
2974
2975 for (i = 0; i < mesh->num_vertices; i++)
2976 mesh->vertex_colors[i] = D3DCOLOR_ARGB(0, 0xff, 0xff, 0xff);
2977 for (i = 0; i < num_colors; i++)
2978 {
2979 D3DCOLORVALUE color;
2980 DWORD index = *(DWORD*)data;
2981 data += sizeof(DWORD);
2982 if (index >= mesh->num_vertices) {
2983 WARN("vertex color %u references undefined vertex %u (only %u vertices)\n",
2984 i, index, mesh->num_vertices);
2985 goto end;
2986 }
2987 memcpy(&color, data, sizeof(color));
2988 data += sizeof(color);
2989 color.r = min(1.0f, max(0.0f, color.r));
2990 color.g = min(1.0f, max(0.0f, color.g));
2991 color.b = min(1.0f, max(0.0f, color.b));
2992 color.a = min(1.0f, max(0.0f, color.a));
2993 mesh->vertex_colors[index] = D3DCOLOR_ARGB((BYTE)(color.a * 255.0f + 0.5f),
2994 (BYTE)(color.r * 255.0f + 0.5f),
2995 (BYTE)(color.g * 255.0f + 0.5f),
2996 (BYTE)(color.b * 255.0f + 0.5f));
2997 }
2998
2999 mesh->fvf |= D3DFVF_DIFFUSE;
3000
3001 hr = D3D_OK;
3002
3003 end:
3004 filedata->lpVtbl->Unlock(filedata);
3005 return hr;
3006 }
3007
3008 static HRESULT parse_normals(ID3DXFileData *filedata, struct mesh_data *mesh)
3009 {
3010 HRESULT hr;
3011 SIZE_T data_size;
3012 const BYTE *data;
3013 DWORD *index_out_ptr;
3014 DWORD i;
3015 DWORD num_face_indices = mesh->num_poly_faces * 2 + mesh->num_tri_faces;
3016
3017 HeapFree(GetProcessHeap(), 0, mesh->normals);
3018 mesh->num_normals = 0;
3019 mesh->normals = NULL;
3020 mesh->normal_indices = NULL;
3021 mesh->fvf |= D3DFVF_NORMAL;
3022
3023 hr = filedata->lpVtbl->Lock(filedata, &data_size, (const void**)&data);
3024 if (FAILED(hr)) return hr;
3025
3026 /* template Vector {
3027 * FLOAT x;
3028 * FLOAT y;