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