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