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