winedbg: Use BOOL type where appropriate.
[wine/multimedia.git] / dlls / d3dx9_36 / mesh.c
blobe4dceeb7d147634b9aba6c7908883657d435a85c
1 /*
2 * Mesh operations specific to D3DX9.
4 * Copyright (C) 2005 Henri Verbeet
5 * Copyright (C) 2006 Ivan Gyurdiev
6 * Copyright (C) 2009 David Adam
7 * Copyright (C) 2010 Tony Wasserka
8 * Copyright (C) 2011 Dylan Smith
9 * Copyright (C) 2011 Michael Mc Donnell
10 * Copyright (C) 2013 Christian Costa
12 * This library is free software; you can redistribute it and/or
13 * modify it under the terms of the GNU Lesser General Public
14 * License as published by the Free Software Foundation; either
15 * version 2.1 of the License, or (at your option) any later version.
17 * This library is distributed in the hope that it will be useful,
18 * but WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
20 * Lesser General Public License for more details.
22 * You should have received a copy of the GNU Lesser General Public
23 * License along with this library; if not, write to the Free Software
24 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
27 #include "config.h"
28 #include "wine/port.h"
30 #define COBJMACROS
31 #define NONAMELESSUNION
32 #include <assert.h>
33 #ifdef HAVE_FLOAT_H
34 # include <float.h>
35 #endif
36 #include "windef.h"
37 #include "wingdi.h"
38 #include "d3dx9.h"
39 #undef MAKE_DDHRESULT
40 #include "dxfile.h"
41 #include "rmxfguid.h"
42 #include "rmxftmpl.h"
43 #include "wine/debug.h"
44 #include "wine/unicode.h"
45 #include "wine/list.h"
46 #include "d3dx9_36_private.h"
48 WINE_DEFAULT_DEBUG_CHANNEL(d3dx);
50 struct d3dx9_mesh
52 ID3DXMesh ID3DXMesh_iface;
53 LONG ref;
55 DWORD numfaces;
56 DWORD numvertices;
57 DWORD options;
58 DWORD fvf;
59 IDirect3DDevice9 *device;
60 D3DVERTEXELEMENT9 cached_declaration[MAX_FVF_DECL_SIZE];
61 IDirect3DVertexDeclaration9 *vertex_declaration;
62 UINT vertex_declaration_size;
63 UINT num_elem;
64 IDirect3DVertexBuffer9 *vertex_buffer;
65 IDirect3DIndexBuffer9 *index_buffer;
66 DWORD *attrib_buffer;
67 int attrib_buffer_lock_count;
68 DWORD attrib_table_size;
69 D3DXATTRIBUTERANGE *attrib_table;
72 const UINT d3dx_decltype_size[] =
74 /* D3DDECLTYPE_FLOAT1 */ sizeof(FLOAT),
75 /* D3DDECLTYPE_FLOAT2 */ sizeof(D3DXVECTOR2),
76 /* D3DDECLTYPE_FLOAT3 */ sizeof(D3DXVECTOR3),
77 /* D3DDECLTYPE_FLOAT4 */ sizeof(D3DXVECTOR4),
78 /* D3DDECLTYPE_D3DCOLOR */ sizeof(D3DCOLOR),
79 /* D3DDECLTYPE_UBYTE4 */ 4 * sizeof(BYTE),
80 /* D3DDECLTYPE_SHORT2 */ 2 * sizeof(SHORT),
81 /* D3DDECLTYPE_SHORT4 */ 4 * sizeof(SHORT),
82 /* D3DDECLTYPE_UBYTE4N */ 4 * sizeof(BYTE),
83 /* D3DDECLTYPE_SHORT2N */ 2 * sizeof(SHORT),
84 /* D3DDECLTYPE_SHORT4N */ 4 * sizeof(SHORT),
85 /* D3DDECLTYPE_USHORT2N */ 2 * sizeof(USHORT),
86 /* D3DDECLTYPE_USHORT4N */ 4 * sizeof(USHORT),
87 /* D3DDECLTYPE_UDEC3 */ 4, /* 3 * 10 bits + 2 padding */
88 /* D3DDECLTYPE_DEC3N */ 4,
89 /* D3DDECLTYPE_FLOAT16_2 */ 2 * sizeof(D3DXFLOAT16),
90 /* D3DDECLTYPE_FLOAT16_4 */ 4 * sizeof(D3DXFLOAT16),
93 static inline struct d3dx9_mesh *impl_from_ID3DXMesh(ID3DXMesh *iface)
95 return CONTAINING_RECORD(iface, struct d3dx9_mesh, ID3DXMesh_iface);
98 static HRESULT WINAPI d3dx9_mesh_QueryInterface(ID3DXMesh *iface, REFIID riid, void **out)
100 TRACE("iface %p, riid %s, out %p.\n", iface, debugstr_guid(riid), out);
102 if (IsEqualGUID(riid, &IID_IUnknown) ||
103 IsEqualGUID(riid, &IID_ID3DXBaseMesh) ||
104 IsEqualGUID(riid, &IID_ID3DXMesh))
106 iface->lpVtbl->AddRef(iface);
107 *out = iface;
108 return S_OK;
111 WARN("Interface %s not found.\n", debugstr_guid(riid));
113 return E_NOINTERFACE;
116 static ULONG WINAPI d3dx9_mesh_AddRef(ID3DXMesh *iface)
118 struct d3dx9_mesh *mesh = impl_from_ID3DXMesh(iface);
119 ULONG refcount = InterlockedIncrement(&mesh->ref);
121 TRACE("%p increasing refcount to %u.\n", mesh, refcount);
123 return refcount;
126 static ULONG WINAPI d3dx9_mesh_Release(ID3DXMesh *iface)
128 struct d3dx9_mesh *mesh = impl_from_ID3DXMesh(iface);
129 ULONG refcount = InterlockedDecrement(&mesh->ref);
131 TRACE("%p decreasing refcount to %u.\n", mesh, refcount);
133 if (!refcount)
135 IDirect3DIndexBuffer9_Release(mesh->index_buffer);
136 IDirect3DVertexBuffer9_Release(mesh->vertex_buffer);
137 if (mesh->vertex_declaration)
138 IDirect3DVertexDeclaration9_Release(mesh->vertex_declaration);
139 IDirect3DDevice9_Release(mesh->device);
140 HeapFree(GetProcessHeap(), 0, mesh->attrib_buffer);
141 HeapFree(GetProcessHeap(), 0, mesh->attrib_table);
142 HeapFree(GetProcessHeap(), 0, mesh);
145 return refcount;
148 static HRESULT WINAPI d3dx9_mesh_DrawSubset(ID3DXMesh *iface, DWORD attrib_id)
150 struct d3dx9_mesh *This = impl_from_ID3DXMesh(iface);
151 HRESULT hr;
152 DWORD face_start;
153 DWORD face_end = 0;
154 DWORD vertex_size;
156 TRACE("iface %p, attrib_id %u.\n", iface, attrib_id);
158 if (!This->vertex_declaration)
160 WARN("Can't draw a mesh with an invalid vertex declaration.\n");
161 return E_FAIL;
164 vertex_size = iface->lpVtbl->GetNumBytesPerVertex(iface);
166 hr = IDirect3DDevice9_SetVertexDeclaration(This->device, This->vertex_declaration);
167 if (FAILED(hr)) return hr;
168 hr = IDirect3DDevice9_SetStreamSource(This->device, 0, This->vertex_buffer, 0, vertex_size);
169 if (FAILED(hr)) return hr;
170 hr = IDirect3DDevice9_SetIndices(This->device, This->index_buffer);
171 if (FAILED(hr)) return hr;
173 while (face_end < This->numfaces)
175 for (face_start = face_end; face_start < This->numfaces; face_start++)
177 if (This->attrib_buffer[face_start] == attrib_id)
178 break;
180 if (face_start >= This->numfaces)
181 break;
182 for (face_end = face_start + 1; face_end < This->numfaces; face_end++)
184 if (This->attrib_buffer[face_end] != attrib_id)
185 break;
188 hr = IDirect3DDevice9_DrawIndexedPrimitive(This->device, D3DPT_TRIANGLELIST,
189 0, 0, This->numvertices, face_start * 3, face_end - face_start);
190 if (FAILED(hr)) return hr;
193 return D3D_OK;
196 static DWORD WINAPI d3dx9_mesh_GetNumFaces(ID3DXMesh *iface)
198 struct d3dx9_mesh *mesh = impl_from_ID3DXMesh(iface);
200 TRACE("iface %p.\n", iface);
202 return mesh->numfaces;
205 static DWORD WINAPI d3dx9_mesh_GetNumVertices(ID3DXMesh *iface)
207 struct d3dx9_mesh *mesh = impl_from_ID3DXMesh(iface);
209 TRACE("iface %p.\n", iface);
211 return mesh->numvertices;
214 static DWORD WINAPI d3dx9_mesh_GetFVF(ID3DXMesh *iface)
216 struct d3dx9_mesh *mesh = impl_from_ID3DXMesh(iface);
218 TRACE("iface %p.\n", iface);
220 return mesh->fvf;
223 static void copy_declaration(D3DVERTEXELEMENT9 *dst, const D3DVERTEXELEMENT9 *src, UINT num_elem)
225 memcpy(dst, src, num_elem * sizeof(*src));
228 static HRESULT WINAPI d3dx9_mesh_GetDeclaration(ID3DXMesh *iface, D3DVERTEXELEMENT9 declaration[MAX_FVF_DECL_SIZE])
230 struct d3dx9_mesh *mesh = impl_from_ID3DXMesh(iface);
232 TRACE("iface %p, declaration %p.\n", iface, declaration);
234 if (!declaration)
235 return D3DERR_INVALIDCALL;
237 copy_declaration(declaration, mesh->cached_declaration, mesh->num_elem);
239 return D3D_OK;
242 static DWORD WINAPI d3dx9_mesh_GetNumBytesPerVertex(ID3DXMesh *iface)
244 struct d3dx9_mesh *mesh = impl_from_ID3DXMesh(iface);
246 TRACE("iface %p.\n", iface);
248 return mesh->vertex_declaration_size;
251 static DWORD WINAPI d3dx9_mesh_GetOptions(ID3DXMesh *iface)
253 struct d3dx9_mesh *mesh = impl_from_ID3DXMesh(iface);
255 TRACE("iface %p.\n", iface);
257 return mesh->options;
260 static HRESULT WINAPI d3dx9_mesh_GetDevice(struct ID3DXMesh *iface, struct IDirect3DDevice9 **device)
262 struct d3dx9_mesh *mesh = impl_from_ID3DXMesh(iface);
264 TRACE("iface %p, device %p.\n", iface, device);
266 if (!device)
267 return D3DERR_INVALIDCALL;
268 *device = mesh->device;
269 IDirect3DDevice9_AddRef(mesh->device);
271 return D3D_OK;
274 static HRESULT WINAPI d3dx9_mesh_CloneMeshFVF(struct ID3DXMesh *iface, DWORD options, DWORD fvf,
275 struct IDirect3DDevice9 *device, struct ID3DXMesh **clone_mesh)
277 HRESULT hr;
278 D3DVERTEXELEMENT9 declaration[MAX_FVF_DECL_SIZE];
280 TRACE("iface %p, options %#x, fvf %#x, device %p, clone_mesh %p.\n",
281 iface, options, fvf, device, clone_mesh);
283 if (FAILED(hr = D3DXDeclaratorFromFVF(fvf, declaration)))
284 return hr;
286 return iface->lpVtbl->CloneMesh(iface, options, declaration, device, clone_mesh);
289 static FLOAT scale_clamp_ubyten(FLOAT value)
291 value = value * UCHAR_MAX;
293 if (value < 0.0f)
295 return 0.0f;
297 else
299 if (value > UCHAR_MAX) /* Clamp at 255 */
300 return UCHAR_MAX;
301 else
302 return value;
306 static FLOAT scale_clamp_shortn(FLOAT value)
308 value = value * SHRT_MAX;
310 /* The tests show that the range is SHRT_MIN + 1 to SHRT_MAX. */
311 if (value <= SHRT_MIN)
313 return SHRT_MIN + 1;
315 else if (value > SHRT_MAX)
317 return SHRT_MAX;
319 else
321 return value;
325 static FLOAT scale_clamp_ushortn(FLOAT value)
327 value = value * USHRT_MAX;
329 if (value < 0.0f)
331 return 0.0f;
333 else
335 if (value > USHRT_MAX) /* Clamp at 65535 */
336 return USHRT_MAX;
337 else
338 return value;
342 static INT simple_round(FLOAT value)
344 int res = (INT)(value + 0.5f);
346 return res;
349 static void convert_float4(BYTE *dst, const D3DXVECTOR4 *src, D3DDECLTYPE type_dst)
351 BOOL fixme_once = FALSE;
353 switch (type_dst)
355 case D3DDECLTYPE_FLOAT1:
357 FLOAT *dst_ptr = (FLOAT*)dst;
358 *dst_ptr = src->x;
359 break;
361 case D3DDECLTYPE_FLOAT2:
363 D3DXVECTOR2 *dst_ptr = (D3DXVECTOR2*)dst;
364 dst_ptr->x = src->x;
365 dst_ptr->y = src->y;
366 break;
368 case D3DDECLTYPE_FLOAT3:
370 D3DXVECTOR3 *dst_ptr = (D3DXVECTOR3*)dst;
371 dst_ptr->x = src->x;
372 dst_ptr->y = src->y;
373 dst_ptr->z = src->z;
374 break;
376 case D3DDECLTYPE_FLOAT4:
378 D3DXVECTOR4 *dst_ptr = (D3DXVECTOR4*)dst;
379 dst_ptr->x = src->x;
380 dst_ptr->y = src->y;
381 dst_ptr->z = src->z;
382 dst_ptr->w = src->w;
383 break;
385 case D3DDECLTYPE_D3DCOLOR:
387 dst[0] = (BYTE)simple_round(scale_clamp_ubyten(src->z));
388 dst[1] = (BYTE)simple_round(scale_clamp_ubyten(src->y));
389 dst[2] = (BYTE)simple_round(scale_clamp_ubyten(src->x));
390 dst[3] = (BYTE)simple_round(scale_clamp_ubyten(src->w));
391 break;
393 case D3DDECLTYPE_UBYTE4:
395 dst[0] = src->x < 0.0f ? 0 : (BYTE)simple_round(src->x);
396 dst[1] = src->y < 0.0f ? 0 : (BYTE)simple_round(src->y);
397 dst[2] = src->z < 0.0f ? 0 : (BYTE)simple_round(src->z);
398 dst[3] = src->w < 0.0f ? 0 : (BYTE)simple_round(src->w);
399 break;
401 case D3DDECLTYPE_SHORT2:
403 SHORT *dst_ptr = (SHORT*)dst;
404 dst_ptr[0] = (SHORT)simple_round(src->x);
405 dst_ptr[1] = (SHORT)simple_round(src->y);
406 break;
408 case D3DDECLTYPE_SHORT4:
410 SHORT *dst_ptr = (SHORT*)dst;
411 dst_ptr[0] = (SHORT)simple_round(src->x);
412 dst_ptr[1] = (SHORT)simple_round(src->y);
413 dst_ptr[2] = (SHORT)simple_round(src->z);
414 dst_ptr[3] = (SHORT)simple_round(src->w);
415 break;
417 case D3DDECLTYPE_UBYTE4N:
419 dst[0] = (BYTE)simple_round(scale_clamp_ubyten(src->x));
420 dst[1] = (BYTE)simple_round(scale_clamp_ubyten(src->y));
421 dst[2] = (BYTE)simple_round(scale_clamp_ubyten(src->z));
422 dst[3] = (BYTE)simple_round(scale_clamp_ubyten(src->w));
423 break;
425 case D3DDECLTYPE_SHORT2N:
427 SHORT *dst_ptr = (SHORT*)dst;
428 dst_ptr[0] = (SHORT)simple_round(scale_clamp_shortn(src->x));
429 dst_ptr[1] = (SHORT)simple_round(scale_clamp_shortn(src->y));
430 break;
432 case D3DDECLTYPE_SHORT4N:
434 SHORT *dst_ptr = (SHORT*)dst;
435 dst_ptr[0] = (SHORT)simple_round(scale_clamp_shortn(src->x));
436 dst_ptr[1] = (SHORT)simple_round(scale_clamp_shortn(src->y));
437 dst_ptr[2] = (SHORT)simple_round(scale_clamp_shortn(src->z));
438 dst_ptr[3] = (SHORT)simple_round(scale_clamp_shortn(src->w));
439 break;
441 case D3DDECLTYPE_USHORT2N:
443 USHORT *dst_ptr = (USHORT*)dst;
444 dst_ptr[0] = (USHORT)simple_round(scale_clamp_ushortn(src->x));
445 dst_ptr[1] = (USHORT)simple_round(scale_clamp_ushortn(src->y));
446 break;
448 case D3DDECLTYPE_USHORT4N:
450 USHORT *dst_ptr = (USHORT*)dst;
451 dst_ptr[0] = (USHORT)simple_round(scale_clamp_ushortn(src->x));
452 dst_ptr[1] = (USHORT)simple_round(scale_clamp_ushortn(src->y));
453 dst_ptr[2] = (USHORT)simple_round(scale_clamp_ushortn(src->z));
454 dst_ptr[3] = (USHORT)simple_round(scale_clamp_ushortn(src->w));
455 break;
457 case D3DDECLTYPE_FLOAT16_2:
459 D3DXFloat32To16Array((D3DXFLOAT16*)dst, (FLOAT*)src, 2);
460 break;
462 case D3DDECLTYPE_FLOAT16_4:
464 D3DXFloat32To16Array((D3DXFLOAT16*)dst, (FLOAT*)src, 4);
465 break;
467 default:
468 if (!fixme_once++)
469 FIXME("Conversion from D3DDECLTYPE_FLOAT4 to %d not implemented.\n", type_dst);
470 break;
474 static void convert_component(BYTE *dst, BYTE *src, D3DDECLTYPE type_dst, D3DDECLTYPE type_src)
476 BOOL fixme_once = FALSE;
478 switch (type_src)
480 case D3DDECLTYPE_FLOAT1:
482 FLOAT *src_ptr = (FLOAT*)src;
483 D3DXVECTOR4 src_float4 = {*src_ptr, 0.0f, 0.0f, 1.0f};
484 convert_float4(dst, &src_float4, type_dst);
485 break;
487 case D3DDECLTYPE_FLOAT2:
489 D3DXVECTOR2 *src_ptr = (D3DXVECTOR2*)src;
490 D3DXVECTOR4 src_float4 = {src_ptr->x, src_ptr->y, 0.0f, 1.0f};
491 convert_float4(dst, &src_float4, type_dst);
492 break;
494 case D3DDECLTYPE_FLOAT3:
496 D3DXVECTOR3 *src_ptr = (D3DXVECTOR3*)src;
497 D3DXVECTOR4 src_float4 = {src_ptr->x, src_ptr->y, src_ptr->z, 1.0f};
498 convert_float4(dst, &src_float4, type_dst);
499 break;
501 case D3DDECLTYPE_FLOAT4:
503 D3DXVECTOR4 *src_ptr = (D3DXVECTOR4*)src;
504 D3DXVECTOR4 src_float4 = {src_ptr->x, src_ptr->y, src_ptr->z, src_ptr->w};
505 convert_float4(dst, &src_float4, type_dst);
506 break;
508 case D3DDECLTYPE_D3DCOLOR:
510 D3DXVECTOR4 src_float4 =
512 (FLOAT)src[2]/UCHAR_MAX,
513 (FLOAT)src[1]/UCHAR_MAX,
514 (FLOAT)src[0]/UCHAR_MAX,
515 (FLOAT)src[3]/UCHAR_MAX
517 convert_float4(dst, &src_float4, type_dst);
518 break;
520 case D3DDECLTYPE_UBYTE4:
522 D3DXVECTOR4 src_float4 = {src[0], src[1], src[2], src[3]};
523 convert_float4(dst, &src_float4, type_dst);
524 break;
526 case D3DDECLTYPE_SHORT2:
528 SHORT *src_ptr = (SHORT*)src;
529 D3DXVECTOR4 src_float4 = {src_ptr[0], src_ptr[1], 0.0f, 1.0f};
530 convert_float4(dst, &src_float4, type_dst);
531 break;
533 case D3DDECLTYPE_SHORT4:
535 SHORT *src_ptr = (SHORT*)src;
536 D3DXVECTOR4 src_float4 = {src_ptr[0], src_ptr[1], src_ptr[2], src_ptr[3]};
537 convert_float4(dst, &src_float4, type_dst);
538 break;
540 case D3DDECLTYPE_UBYTE4N:
542 D3DXVECTOR4 src_float4 =
544 (FLOAT)src[0]/UCHAR_MAX,
545 (FLOAT)src[1]/UCHAR_MAX,
546 (FLOAT)src[2]/UCHAR_MAX,
547 (FLOAT)src[3]/UCHAR_MAX
549 convert_float4(dst, &src_float4, type_dst);
550 break;
552 case D3DDECLTYPE_SHORT2N:
554 SHORT *src_ptr = (SHORT*)src;
555 D3DXVECTOR4 src_float4 = {(FLOAT)src_ptr[0]/SHRT_MAX, (FLOAT)src_ptr[1]/SHRT_MAX, 0.0f, 1.0f};
556 convert_float4(dst, &src_float4, type_dst);
557 break;
559 case D3DDECLTYPE_SHORT4N:
561 SHORT *src_ptr = (SHORT*)src;
562 D3DXVECTOR4 src_float4 =
564 (FLOAT)src_ptr[0]/SHRT_MAX,
565 (FLOAT)src_ptr[1]/SHRT_MAX,
566 (FLOAT)src_ptr[2]/SHRT_MAX,
567 (FLOAT)src_ptr[3]/SHRT_MAX
569 convert_float4(dst, &src_float4, type_dst);
570 break;
572 case D3DDECLTYPE_FLOAT16_2:
574 D3DXVECTOR4 src_float4 = {0.0f, 0.0f, 0.0f, 1.0f};
575 D3DXFloat16To32Array((FLOAT*)&src_float4, (D3DXFLOAT16*)src, 2);
576 convert_float4(dst, &src_float4, type_dst);
577 break;
579 case D3DDECLTYPE_FLOAT16_4:
581 D3DXVECTOR4 src_float4;
582 D3DXFloat16To32Array((FLOAT*)&src_float4, (D3DXFLOAT16*)src, 4);
583 convert_float4(dst, &src_float4, type_dst);
584 break;
586 default:
587 if (!fixme_once++)
588 FIXME("Conversion of D3DDECLTYPE %d to %d not implemented.\n", type_src, type_dst);
589 break;
593 static INT get_equivalent_declaration_index(D3DVERTEXELEMENT9 orig_declaration, D3DVERTEXELEMENT9 *declaration)
595 INT i;
597 for (i = 0; declaration[i].Stream != 0xff; i++)
599 if (orig_declaration.Usage == declaration[i].Usage
600 && orig_declaration.UsageIndex == declaration[i].UsageIndex)
602 return i;
606 return -1;
609 static HRESULT convert_vertex_buffer(ID3DXMesh *mesh_dst, ID3DXMesh *mesh_src)
611 HRESULT hr;
612 D3DVERTEXELEMENT9 orig_declaration[MAX_FVF_DECL_SIZE] = {D3DDECL_END()};
613 D3DVERTEXELEMENT9 declaration[MAX_FVF_DECL_SIZE] = {D3DDECL_END()};
614 BYTE *vb_dst = NULL;
615 BYTE *vb_src = NULL;
616 UINT i;
617 UINT num_vertices = mesh_src->lpVtbl->GetNumVertices(mesh_src);
618 UINT dst_vertex_size = mesh_dst->lpVtbl->GetNumBytesPerVertex(mesh_dst);
619 UINT src_vertex_size = mesh_src->lpVtbl->GetNumBytesPerVertex(mesh_src);
621 hr = mesh_src->lpVtbl->GetDeclaration(mesh_src, orig_declaration);
622 if (FAILED(hr)) return hr;
623 hr = mesh_dst->lpVtbl->GetDeclaration(mesh_dst, declaration);
624 if (FAILED(hr)) return hr;
626 hr = mesh_src->lpVtbl->LockVertexBuffer(mesh_src, D3DLOCK_READONLY, (void**)&vb_src);
627 if (FAILED(hr)) goto cleanup;
628 hr = mesh_dst->lpVtbl->LockVertexBuffer(mesh_dst, 0, (void**)&vb_dst);
629 if (FAILED(hr)) goto cleanup;
631 /* Clear all new fields by clearing the entire vertex buffer. */
632 memset(vb_dst, 0, num_vertices * dst_vertex_size);
634 for (i = 0; orig_declaration[i].Stream != 0xff; i++)
636 INT eq_idx = get_equivalent_declaration_index(orig_declaration[i], declaration);
638 if (eq_idx >= 0)
640 UINT j;
641 for (j = 0; j < num_vertices; j++)
643 UINT idx_dst = dst_vertex_size * j + declaration[eq_idx].Offset;
644 UINT idx_src = src_vertex_size * j + orig_declaration[i].Offset;
645 UINT type_size = d3dx_decltype_size[orig_declaration[i].Type];
647 if (orig_declaration[i].Type == declaration[eq_idx].Type)
648 memcpy(&vb_dst[idx_dst], &vb_src[idx_src], type_size);
649 else
650 convert_component(&vb_dst[idx_dst], &vb_src[idx_src], declaration[eq_idx].Type, orig_declaration[i].Type);
655 hr = D3D_OK;
656 cleanup:
657 if (vb_dst) mesh_dst->lpVtbl->UnlockVertexBuffer(mesh_dst);
658 if (vb_src) mesh_src->lpVtbl->UnlockVertexBuffer(mesh_src);
660 return hr;
663 static BOOL declaration_equals(const D3DVERTEXELEMENT9 *declaration1, const D3DVERTEXELEMENT9 *declaration2)
665 UINT size1 = 0, size2 = 0;
667 /* Find the size of each declaration */
668 while (declaration1[size1].Stream != 0xff) size1++;
669 while (declaration2[size2].Stream != 0xff) size2++;
671 /* If not same size then they are definitely not equal */
672 if (size1 != size2)
673 return FALSE;
675 /* Check that all components are the same */
676 if (memcmp(declaration1, declaration2, size1*sizeof(*declaration1)) == 0)
677 return TRUE;
679 return FALSE;
682 static HRESULT WINAPI d3dx9_mesh_CloneMesh(struct ID3DXMesh *iface, DWORD options,
683 const D3DVERTEXELEMENT9 *declaration, struct IDirect3DDevice9 *device, struct ID3DXMesh **clone_mesh_out)
685 struct d3dx9_mesh *This = impl_from_ID3DXMesh(iface);
686 struct d3dx9_mesh *cloned_this;
687 ID3DXMesh *clone_mesh;
688 D3DVERTEXELEMENT9 orig_declaration[MAX_FVF_DECL_SIZE] = { D3DDECL_END() };
689 void *data_in, *data_out;
690 DWORD vertex_size;
691 HRESULT hr;
692 BOOL same_declaration;
694 TRACE("iface %p, options %#x, declaration %p, device %p, clone_mesh_out %p.\n",
695 iface, options, declaration, device, clone_mesh_out);
697 if (!clone_mesh_out)
698 return D3DERR_INVALIDCALL;
700 hr = iface->lpVtbl->GetDeclaration(iface, orig_declaration);
701 if (FAILED(hr)) return hr;
703 hr = D3DXCreateMesh(This->numfaces, This->numvertices, options & ~D3DXMESH_VB_SHARE,
704 declaration, device, &clone_mesh);
705 if (FAILED(hr)) return hr;
707 cloned_this = impl_from_ID3DXMesh(clone_mesh);
708 vertex_size = clone_mesh->lpVtbl->GetNumBytesPerVertex(clone_mesh);
709 same_declaration = declaration_equals(declaration, orig_declaration);
711 if (options & D3DXMESH_VB_SHARE) {
712 if (!same_declaration) {
713 hr = D3DERR_INVALIDCALL;
714 goto error;
716 IDirect3DVertexBuffer9_AddRef(This->vertex_buffer);
717 /* FIXME: refactor to avoid creating a new vertex buffer */
718 IDirect3DVertexBuffer9_Release(cloned_this->vertex_buffer);
719 cloned_this->vertex_buffer = This->vertex_buffer;
720 } else if (same_declaration) {
721 hr = iface->lpVtbl->LockVertexBuffer(iface, D3DLOCK_READONLY, &data_in);
722 if (FAILED(hr)) goto error;
723 hr = clone_mesh->lpVtbl->LockVertexBuffer(clone_mesh, 0, &data_out);
724 if (FAILED(hr)) {
725 iface->lpVtbl->UnlockVertexBuffer(iface);
726 goto error;
728 memcpy(data_out, data_in, This->numvertices * vertex_size);
729 clone_mesh->lpVtbl->UnlockVertexBuffer(clone_mesh);
730 iface->lpVtbl->UnlockVertexBuffer(iface);
731 } else {
732 hr = convert_vertex_buffer(clone_mesh, iface);
733 if (FAILED(hr)) goto error;
736 hr = iface->lpVtbl->LockIndexBuffer(iface, D3DLOCK_READONLY, &data_in);
737 if (FAILED(hr)) goto error;
738 hr = clone_mesh->lpVtbl->LockIndexBuffer(clone_mesh, 0, &data_out);
739 if (FAILED(hr)) {
740 iface->lpVtbl->UnlockIndexBuffer(iface);
741 goto error;
743 if ((options ^ This->options) & D3DXMESH_32BIT) {
744 DWORD i;
745 if (options & D3DXMESH_32BIT) {
746 for (i = 0; i < This->numfaces * 3; i++)
747 ((DWORD*)data_out)[i] = ((WORD*)data_in)[i];
748 } else {
749 for (i = 0; i < This->numfaces * 3; i++)
750 ((WORD*)data_out)[i] = ((DWORD*)data_in)[i];
752 } else {
753 memcpy(data_out, data_in, This->numfaces * 3 * (options & D3DXMESH_32BIT ? 4 : 2));
755 clone_mesh->lpVtbl->UnlockIndexBuffer(clone_mesh);
756 iface->lpVtbl->UnlockIndexBuffer(iface);
758 memcpy(cloned_this->attrib_buffer, This->attrib_buffer, This->numfaces * sizeof(*This->attrib_buffer));
760 if (This->attrib_table_size)
762 cloned_this->attrib_table_size = This->attrib_table_size;
763 cloned_this->attrib_table = HeapAlloc(GetProcessHeap(), 0, This->attrib_table_size * sizeof(*This->attrib_table));
764 if (!cloned_this->attrib_table) {
765 hr = E_OUTOFMEMORY;
766 goto error;
768 memcpy(cloned_this->attrib_table, This->attrib_table, This->attrib_table_size * sizeof(*This->attrib_table));
771 *clone_mesh_out = clone_mesh;
773 return D3D_OK;
774 error:
775 IUnknown_Release(clone_mesh);
776 return hr;
779 static HRESULT WINAPI d3dx9_mesh_GetVertexBuffer(struct ID3DXMesh *iface,
780 struct IDirect3DVertexBuffer9 **vertex_buffer)
782 struct d3dx9_mesh *mesh = impl_from_ID3DXMesh(iface);
784 TRACE("iface %p, vertex_buffer %p.\n", iface, vertex_buffer);
786 if (!vertex_buffer)
787 return D3DERR_INVALIDCALL;
788 *vertex_buffer = mesh->vertex_buffer;
789 IDirect3DVertexBuffer9_AddRef(mesh->vertex_buffer);
791 return D3D_OK;
794 static HRESULT WINAPI d3dx9_mesh_GetIndexBuffer(struct ID3DXMesh *iface,
795 struct IDirect3DIndexBuffer9 **index_buffer)
797 struct d3dx9_mesh *mesh = impl_from_ID3DXMesh(iface);
799 TRACE("iface %p, index_buffer %p.\n", iface, index_buffer);
801 if (!index_buffer)
802 return D3DERR_INVALIDCALL;
803 *index_buffer = mesh->index_buffer;
804 IDirect3DIndexBuffer9_AddRef(mesh->index_buffer);
806 return D3D_OK;
809 static HRESULT WINAPI d3dx9_mesh_LockVertexBuffer(ID3DXMesh *iface, DWORD flags, void **data)
811 struct d3dx9_mesh *mesh = impl_from_ID3DXMesh(iface);
813 TRACE("iface %p, flags %#x, data %p.\n", iface, flags, data);
815 return IDirect3DVertexBuffer9_Lock(mesh->vertex_buffer, 0, 0, data, flags);
818 static HRESULT WINAPI d3dx9_mesh_UnlockVertexBuffer(ID3DXMesh *iface)
820 struct d3dx9_mesh *mesh = impl_from_ID3DXMesh(iface);
822 TRACE("iface %p.\n", iface);
824 return IDirect3DVertexBuffer9_Unlock(mesh->vertex_buffer);
827 static HRESULT WINAPI d3dx9_mesh_LockIndexBuffer(ID3DXMesh *iface, DWORD flags, void **data)
829 struct d3dx9_mesh *mesh = impl_from_ID3DXMesh(iface);
831 TRACE("iface %p, flags %#x, data %p.\n", iface, flags, data);
833 return IDirect3DIndexBuffer9_Lock(mesh->index_buffer, 0, 0, data, flags);
836 static HRESULT WINAPI d3dx9_mesh_UnlockIndexBuffer(ID3DXMesh *iface)
838 struct d3dx9_mesh *mesh = impl_from_ID3DXMesh(iface);
840 TRACE("iface %p.\n", iface);
842 return IDirect3DIndexBuffer9_Unlock(mesh->index_buffer);
845 /* FIXME: This looks just wrong, we never check *attrib_table_size before
846 * copying the data. */
847 static HRESULT WINAPI d3dx9_mesh_GetAttributeTable(ID3DXMesh *iface,
848 D3DXATTRIBUTERANGE *attrib_table, DWORD *attrib_table_size)
850 struct d3dx9_mesh *mesh = impl_from_ID3DXMesh(iface);
852 TRACE("iface %p, attrib_table %p, attrib_table_size %p.\n",
853 iface, attrib_table, attrib_table_size);
855 if (attrib_table_size)
856 *attrib_table_size = mesh->attrib_table_size;
858 if (attrib_table)
859 memcpy(attrib_table, mesh->attrib_table, mesh->attrib_table_size * sizeof(*attrib_table));
861 return D3D_OK;
864 struct edge_face
866 struct list entry;
867 DWORD v2;
868 DWORD face;
871 struct edge_face_map
873 struct list *lists;
874 struct edge_face *entries;
877 /* Builds up a map of which face a new edge belongs to. That way the adjacency
878 * of another edge can be looked up. An edge has an adjacent face if there
879 * is an edge going in the opposite direction in the map. For example if the
880 * edge (v1, v2) belongs to face 4, and there is a mapping (v2, v1)->7, then
881 * face 4 and 7 are adjacent.
883 * Each edge might have been replaced with another edge, or none at all. There
884 * is at most one edge to face mapping, i.e. an edge can only belong to one
885 * face.
887 static HRESULT init_edge_face_map(struct edge_face_map *edge_face_map, const DWORD *index_buffer,
888 const DWORD *point_reps, DWORD num_faces)
890 DWORD face, edge;
891 DWORD i;
893 edge_face_map->lists = HeapAlloc(GetProcessHeap(), 0, 3 * num_faces * sizeof(*edge_face_map->lists));
894 if (!edge_face_map->lists) return E_OUTOFMEMORY;
896 edge_face_map->entries = HeapAlloc(GetProcessHeap(), 0, 3 * num_faces * sizeof(*edge_face_map->entries));
897 if (!edge_face_map->entries) return E_OUTOFMEMORY;
900 /* Initialize all lists */
901 for (i = 0; i < 3 * num_faces; i++)
903 list_init(&edge_face_map->lists[i]);
905 /* Build edge face mapping */
906 for (face = 0; face < num_faces; face++)
908 for (edge = 0; edge < 3; edge++)
910 DWORD v1 = index_buffer[3*face + edge];
911 DWORD v2 = index_buffer[3*face + (edge+1)%3];
912 DWORD new_v1 = point_reps[v1]; /* What v1 has been replaced with */
913 DWORD new_v2 = point_reps[v2];
915 if (v1 != v2) /* Only map non-collapsed edges */
917 i = 3*face + edge;
918 edge_face_map->entries[i].v2 = new_v2;
919 edge_face_map->entries[i].face = face;
920 list_add_head(&edge_face_map->lists[new_v1], &edge_face_map->entries[i].entry);
925 return D3D_OK;
928 static DWORD find_adjacent_face(struct edge_face_map *edge_face_map, DWORD vertex1, DWORD vertex2, DWORD num_faces)
930 struct edge_face *edge_face_ptr;
932 LIST_FOR_EACH_ENTRY(edge_face_ptr, &edge_face_map->lists[vertex2], struct edge_face, entry)
934 if (edge_face_ptr->v2 == vertex1)
935 return edge_face_ptr->face;
938 return -1;
941 static DWORD *generate_identity_point_reps(DWORD num_vertices)
943 DWORD *id_point_reps;
944 DWORD i;
946 id_point_reps = HeapAlloc(GetProcessHeap(), 0, num_vertices * sizeof(*id_point_reps));
947 if (!id_point_reps)
948 return NULL;
950 for (i = 0; i < num_vertices; i++)
952 id_point_reps[i] = i;
955 return id_point_reps;
958 static HRESULT WINAPI d3dx9_mesh_ConvertPointRepsToAdjacency(ID3DXMesh *iface,
959 const DWORD *point_reps, DWORD *adjacency)
961 HRESULT hr;
962 DWORD num_faces = iface->lpVtbl->GetNumFaces(iface);
963 DWORD num_vertices = iface->lpVtbl->GetNumVertices(iface);
964 DWORD options = iface->lpVtbl->GetOptions(iface);
965 BOOL indices_are_16_bit = !(options & D3DXMESH_32BIT);
966 DWORD *ib = NULL;
967 void *ib_ptr = NULL;
968 DWORD face;
969 DWORD edge;
970 struct edge_face_map edge_face_map = {0};
971 const DWORD *point_reps_ptr = NULL;
972 DWORD *id_point_reps = NULL;
974 TRACE("iface %p, point_reps %p, adjacency %p.\n", iface, point_reps, adjacency);
976 if (!adjacency) return D3DERR_INVALIDCALL;
978 if (!point_reps) /* Identity point reps */
980 id_point_reps = generate_identity_point_reps(num_vertices);
981 if (!id_point_reps)
983 hr = E_OUTOFMEMORY;
984 goto cleanup;
987 point_reps_ptr = id_point_reps;
989 else
991 point_reps_ptr = point_reps;
994 hr = iface->lpVtbl->LockIndexBuffer(iface, D3DLOCK_READONLY, &ib_ptr);
995 if (FAILED(hr)) goto cleanup;
997 if (indices_are_16_bit)
999 /* Widen 16 bit to 32 bit */
1000 DWORD i;
1001 WORD *ib_16bit = ib_ptr;
1002 ib = HeapAlloc(GetProcessHeap(), 0, 3 * num_faces * sizeof(DWORD));
1003 if (!ib)
1005 hr = E_OUTOFMEMORY;
1006 goto cleanup;
1008 for (i = 0; i < 3 * num_faces; i++)
1010 ib[i] = ib_16bit[i];
1013 else
1015 ib = ib_ptr;
1018 hr = init_edge_face_map(&edge_face_map, ib, point_reps_ptr, num_faces);
1019 if (FAILED(hr)) goto cleanup;
1021 /* Create adjacency */
1022 for (face = 0; face < num_faces; face++)
1024 for (edge = 0; edge < 3; edge++)
1026 DWORD v1 = ib[3*face + edge];
1027 DWORD v2 = ib[3*face + (edge+1)%3];
1028 DWORD new_v1 = point_reps_ptr[v1];
1029 DWORD new_v2 = point_reps_ptr[v2];
1030 DWORD adj_face;
1032 adj_face = find_adjacent_face(&edge_face_map, new_v1, new_v2, num_faces);
1033 adjacency[3*face + edge] = adj_face;
1037 hr = D3D_OK;
1038 cleanup:
1039 HeapFree(GetProcessHeap(), 0, id_point_reps);
1040 if (indices_are_16_bit) HeapFree(GetProcessHeap(), 0, ib);
1041 HeapFree(GetProcessHeap(), 0, edge_face_map.lists);
1042 HeapFree(GetProcessHeap(), 0, edge_face_map.entries);
1043 if(ib_ptr) iface->lpVtbl->UnlockIndexBuffer(iface);
1044 return hr;
1047 /* ConvertAdjacencyToPointReps helper function.
1049 * Goes around the edges of each face and replaces the vertices in any adjacent
1050 * face's edge with its own vertices(if its vertices have a lower index). This
1051 * way as few as possible low index vertices are shared among the faces. The
1052 * re-ordered index buffer is stored in new_indices.
1054 * The vertices in a point representation must be ordered sequentially, e.g.
1055 * index 5 holds the index of the vertex that replaces vertex 5, i.e. if
1056 * vertex 5 is replaced by vertex 3 then index 5 would contain 3. If no vertex
1057 * replaces it, then it contains the same number as the index itself, e.g.
1058 * index 5 would contain 5. */
1059 static HRESULT propagate_face_vertices(const DWORD *adjacency, DWORD *point_reps,
1060 const DWORD *indices, DWORD *new_indices, DWORD face, DWORD numfaces)
1062 const unsigned int VERTS_PER_FACE = 3;
1063 DWORD edge, opp_edge;
1064 DWORD face_base = VERTS_PER_FACE * face;
1066 for (edge = 0; edge < VERTS_PER_FACE; edge++)
1068 DWORD adj_face = adjacency[face_base + edge];
1069 DWORD adj_face_base;
1070 DWORD i;
1071 if (adj_face == -1) /* No adjacent face. */
1072 continue;
1073 else if (adj_face >= numfaces)
1075 /* This throws exception on Windows */
1076 WARN("Index out of bounds. Got %d expected less than %d.\n",
1077 adj_face, numfaces);
1078 return D3DERR_INVALIDCALL;
1080 adj_face_base = 3 * adj_face;
1082 /* Find opposite edge in adjacent face. */
1083 for (opp_edge = 0; opp_edge < VERTS_PER_FACE; opp_edge++)
1085 DWORD opp_edge_index = adj_face_base + opp_edge;
1086 if (adjacency[opp_edge_index] == face)
1087 break; /* Found opposite edge. */
1090 /* Replaces vertices in opposite edge with vertices from current edge. */
1091 for (i = 0; i < 2; i++)
1093 DWORD from = face_base + (edge + (1 - i)) % VERTS_PER_FACE;
1094 DWORD to = adj_face_base + (opp_edge + i) % VERTS_PER_FACE;
1096 /* Propagate lowest index. */
1097 if (new_indices[to] > new_indices[from])
1099 new_indices[to] = new_indices[from];
1100 point_reps[indices[to]] = new_indices[from];
1105 return D3D_OK;
1108 static HRESULT WINAPI d3dx9_mesh_ConvertAdjacencyToPointReps(ID3DXMesh *iface,
1109 const DWORD *adjacency, DWORD *point_reps)
1111 struct d3dx9_mesh *This = impl_from_ID3DXMesh(iface);
1112 HRESULT hr;
1113 DWORD face;
1114 DWORD i;
1115 DWORD *indices = NULL;
1116 WORD *indices_16bit = NULL;
1117 DWORD *new_indices = NULL;
1118 const unsigned int VERTS_PER_FACE = 3;
1120 TRACE("iface %p, adjacency %p, point_reps %p.\n", iface, adjacency, point_reps);
1122 if (!adjacency)
1124 WARN("NULL adjacency.\n");
1125 hr = D3DERR_INVALIDCALL;
1126 goto cleanup;
1129 if (!point_reps)
1131 WARN("NULL point_reps.\n");
1132 hr = D3DERR_INVALIDCALL;
1133 goto cleanup;
1136 /* Should never happen as CreateMesh does not allow meshes with 0 faces */
1137 if (This->numfaces == 0)
1139 ERR("Number of faces was zero.\n");
1140 hr = D3DERR_INVALIDCALL;
1141 goto cleanup;
1144 new_indices = HeapAlloc(GetProcessHeap(), 0, VERTS_PER_FACE * This->numfaces * sizeof(*indices));
1145 if (!new_indices)
1147 hr = E_OUTOFMEMORY;
1148 goto cleanup;
1151 if (This->options & D3DXMESH_32BIT)
1153 hr = iface->lpVtbl->LockIndexBuffer(iface, D3DLOCK_READONLY, (void**)&indices);
1154 if (FAILED(hr)) goto cleanup;
1155 memcpy(new_indices, indices, VERTS_PER_FACE * This->numfaces * sizeof(*indices));
1157 else
1159 /* Make a widening copy of indices_16bit into indices and new_indices
1160 * in order to re-use the helper function */
1161 hr = iface->lpVtbl->LockIndexBuffer(iface, D3DLOCK_READONLY, (void**)&indices_16bit);
1162 if (FAILED(hr)) goto cleanup;
1163 indices = HeapAlloc(GetProcessHeap(), 0, VERTS_PER_FACE * This->numfaces * sizeof(*indices));
1164 if (!indices)
1166 hr = E_OUTOFMEMORY;
1167 goto cleanup;
1169 for (i = 0; i < VERTS_PER_FACE * This->numfaces; i++)
1171 new_indices[i] = indices_16bit[i];
1172 indices[i] = indices_16bit[i];
1176 /* Vertices are ordered sequentially in the point representation. */
1177 for (i = 0; i < This->numvertices; i++)
1179 point_reps[i] = i;
1182 /* Propagate vertices with low indices so as few vertices as possible
1183 * are used in the mesh.
1185 for (face = 0; face < This->numfaces; face++)
1187 hr = propagate_face_vertices(adjacency, point_reps, indices, new_indices, face, This->numfaces);
1188 if (FAILED(hr)) goto cleanup;
1190 /* Go in opposite direction to catch all face orderings */
1191 for (face = 0; face < This->numfaces; face++)
1193 hr = propagate_face_vertices(adjacency, point_reps,
1194 indices, new_indices,
1195 (This->numfaces - 1) - face, This->numfaces);
1196 if (FAILED(hr)) goto cleanup;
1199 hr = D3D_OK;
1200 cleanup:
1201 if (This->options & D3DXMESH_32BIT)
1203 if (indices) iface->lpVtbl->UnlockIndexBuffer(iface);
1205 else
1207 if (indices_16bit) iface->lpVtbl->UnlockIndexBuffer(iface);
1208 HeapFree(GetProcessHeap(), 0, indices);
1210 HeapFree(GetProcessHeap(), 0, new_indices);
1211 return hr;
1214 struct vertex_metadata {
1215 float key;
1216 DWORD vertex_index;
1217 DWORD first_shared_index;
1220 static int compare_vertex_keys(const void *a, const void *b)
1222 const struct vertex_metadata *left = a;
1223 const struct vertex_metadata *right = b;
1224 if (left->key == right->key)
1225 return 0;
1226 return left->key < right->key ? -1 : 1;
1229 static HRESULT WINAPI d3dx9_mesh_GenerateAdjacency(ID3DXMesh *iface, float epsilon, DWORD *adjacency)
1231 struct d3dx9_mesh *This = impl_from_ID3DXMesh(iface);
1232 HRESULT hr;
1233 BYTE *vertices = NULL;
1234 const DWORD *indices = NULL;
1235 DWORD vertex_size;
1236 DWORD buffer_size;
1237 /* sort the vertices by (x + y + z) to quickly find coincident vertices */
1238 struct vertex_metadata *sorted_vertices;
1239 /* shared_indices links together identical indices in the index buffer so
1240 * that adjacency checks can be limited to faces sharing a vertex */
1241 DWORD *shared_indices = NULL;
1242 const FLOAT epsilon_sq = epsilon * epsilon;
1243 DWORD i;
1245 TRACE("iface %p, epsilon %.8e, adjacency %p.\n", iface, epsilon, adjacency);
1247 if (!adjacency)
1248 return D3DERR_INVALIDCALL;
1250 buffer_size = This->numfaces * 3 * sizeof(*shared_indices) + This->numvertices * sizeof(*sorted_vertices);
1251 if (!(This->options & D3DXMESH_32BIT))
1252 buffer_size += This->numfaces * 3 * sizeof(*indices);
1253 shared_indices = HeapAlloc(GetProcessHeap(), 0, buffer_size);
1254 if (!shared_indices)
1255 return E_OUTOFMEMORY;
1256 sorted_vertices = (struct vertex_metadata*)(shared_indices + This->numfaces * 3);
1258 hr = iface->lpVtbl->LockVertexBuffer(iface, D3DLOCK_READONLY, (void**)&vertices);
1259 if (FAILED(hr)) goto cleanup;
1260 hr = iface->lpVtbl->LockIndexBuffer(iface, D3DLOCK_READONLY, (void**)&indices);
1261 if (FAILED(hr)) goto cleanup;
1263 if (!(This->options & D3DXMESH_32BIT)) {
1264 const WORD *word_indices = (const WORD*)indices;
1265 DWORD *dword_indices = (DWORD*)(sorted_vertices + This->numvertices);
1266 indices = dword_indices;
1267 for (i = 0; i < This->numfaces * 3; i++)
1268 *dword_indices++ = *word_indices++;
1271 vertex_size = iface->lpVtbl->GetNumBytesPerVertex(iface);
1272 for (i = 0; i < This->numvertices; i++) {
1273 D3DXVECTOR3 *vertex = (D3DXVECTOR3*)(vertices + vertex_size * i);
1274 sorted_vertices[i].first_shared_index = -1;
1275 sorted_vertices[i].key = vertex->x + vertex->y + vertex->z;
1276 sorted_vertices[i].vertex_index = i;
1278 for (i = 0; i < This->numfaces * 3; i++) {
1279 DWORD *first_shared_index = &sorted_vertices[indices[i]].first_shared_index;
1280 shared_indices[i] = *first_shared_index;
1281 *first_shared_index = i;
1282 adjacency[i] = -1;
1284 qsort(sorted_vertices, This->numvertices, sizeof(*sorted_vertices), compare_vertex_keys);
1286 for (i = 0; i < This->numvertices; i++) {
1287 struct vertex_metadata *sorted_vertex_a = &sorted_vertices[i];
1288 D3DXVECTOR3 *vertex_a = (D3DXVECTOR3*)(vertices + sorted_vertex_a->vertex_index * vertex_size);
1289 DWORD shared_index_a = sorted_vertex_a->first_shared_index;
1291 while (shared_index_a != -1) {
1292 DWORD j = i;
1293 DWORD shared_index_b = shared_indices[shared_index_a];
1294 struct vertex_metadata *sorted_vertex_b = sorted_vertex_a;
1296 while (TRUE) {
1297 while (shared_index_b != -1) {
1298 /* faces are adjacent if they have another coincident vertex */
1299 DWORD base_a = (shared_index_a / 3) * 3;
1300 DWORD base_b = (shared_index_b / 3) * 3;
1301 BOOL adjacent = FALSE;
1302 int k;
1304 for (k = 0; k < 3; k++) {
1305 if (adjacency[base_b + k] == shared_index_a / 3) {
1306 adjacent = TRUE;
1307 break;
1310 if (!adjacent) {
1311 for (k = 1; k <= 2; k++) {
1312 DWORD vertex_index_a = base_a + (shared_index_a + k) % 3;
1313 DWORD vertex_index_b = base_b + (shared_index_b + (3 - k)) % 3;
1314 adjacent = indices[vertex_index_a] == indices[vertex_index_b];
1315 if (!adjacent && epsilon >= 0.0f) {
1316 D3DXVECTOR3 delta = {0.0f, 0.0f, 0.0f};
1317 FLOAT length_sq;
1319 D3DXVec3Subtract(&delta,
1320 (D3DXVECTOR3*)(vertices + indices[vertex_index_a] * vertex_size),
1321 (D3DXVECTOR3*)(vertices + indices[vertex_index_b] * vertex_size));
1322 length_sq = D3DXVec3LengthSq(&delta);
1323 adjacent = epsilon == 0.0f ? length_sq == 0.0f : length_sq < epsilon_sq;
1325 if (adjacent) {
1326 DWORD adj_a = base_a + 2 - (vertex_index_a + shared_index_a + 1) % 3;
1327 DWORD adj_b = base_b + 2 - (vertex_index_b + shared_index_b + 1) % 3;
1328 if (adjacency[adj_a] == -1 && adjacency[adj_b] == -1) {
1329 adjacency[adj_a] = base_b / 3;
1330 adjacency[adj_b] = base_a / 3;
1331 break;
1337 shared_index_b = shared_indices[shared_index_b];
1339 while (++j < This->numvertices) {
1340 D3DXVECTOR3 *vertex_b;
1342 sorted_vertex_b++;
1343 if (sorted_vertex_b->key - sorted_vertex_a->key > epsilon * 3.0f) {
1344 /* no more coincident vertices to try */
1345 j = This->numvertices;
1346 break;
1348 /* check for coincidence */
1349 vertex_b = (D3DXVECTOR3*)(vertices + sorted_vertex_b->vertex_index * vertex_size);
1350 if (fabsf(vertex_a->x - vertex_b->x) <= epsilon &&
1351 fabsf(vertex_a->y - vertex_b->y) <= epsilon &&
1352 fabsf(vertex_a->z - vertex_b->z) <= epsilon)
1354 break;
1357 if (j >= This->numvertices)
1358 break;
1359 shared_index_b = sorted_vertex_b->first_shared_index;
1362 sorted_vertex_a->first_shared_index = shared_indices[sorted_vertex_a->first_shared_index];
1363 shared_index_a = sorted_vertex_a->first_shared_index;
1367 hr = D3D_OK;
1368 cleanup:
1369 if (indices) iface->lpVtbl->UnlockIndexBuffer(iface);
1370 if (vertices) iface->lpVtbl->UnlockVertexBuffer(iface);
1371 HeapFree(GetProcessHeap(), 0, shared_indices);
1372 return hr;
1375 static HRESULT WINAPI d3dx9_mesh_UpdateSemantics(ID3DXMesh *iface, D3DVERTEXELEMENT9 declaration[MAX_FVF_DECL_SIZE])
1377 struct d3dx9_mesh *This = impl_from_ID3DXMesh(iface);
1378 HRESULT hr;
1379 UINT vertex_declaration_size;
1380 int i;
1382 TRACE("iface %p, declaration %p.\n", iface, declaration);
1384 if (!declaration)
1386 WARN("Invalid declaration. Can't use NULL declaration.\n");
1387 return D3DERR_INVALIDCALL;
1390 /* New declaration must be same size as original */
1391 vertex_declaration_size = D3DXGetDeclVertexSize(declaration, declaration[0].Stream);
1392 if (vertex_declaration_size != This->vertex_declaration_size)
1394 WARN("Invalid declaration. New vertex size does not match the original vertex size.\n");
1395 return D3DERR_INVALIDCALL;
1398 /* New declaration must not contain non-zero Stream value */
1399 for (i = 0; declaration[i].Stream != 0xff; i++)
1401 if (declaration[i].Stream != 0)
1403 WARN("Invalid declaration. New declaration contains non-zero Stream value.\n");
1404 return D3DERR_INVALIDCALL;
1408 This->num_elem = i + 1;
1409 copy_declaration(This->cached_declaration, declaration, This->num_elem);
1411 if (This->vertex_declaration)
1412 IDirect3DVertexDeclaration9_Release(This->vertex_declaration);
1414 /* An application can pass an invalid declaration to UpdateSemantics and
1415 * still expect D3D_OK (see tests). If the declaration is invalid, then
1416 * subsequent calls to DrawSubset will fail. This is handled by setting the
1417 * vertex declaration to NULL.
1418 * GetDeclaration, GetNumBytesPerVertex must, however, use the new
1419 * invalid declaration. This is handled by them using the cached vertex
1420 * declaration instead of the actual vertex declaration.
1422 hr = IDirect3DDevice9_CreateVertexDeclaration(This->device,
1423 declaration,
1424 &This->vertex_declaration);
1425 if (FAILED(hr))
1427 WARN("Using invalid declaration. Calls to DrawSubset will fail.\n");
1428 This->vertex_declaration = NULL;
1431 return D3D_OK;
1434 static HRESULT WINAPI d3dx9_mesh_LockAttributeBuffer(ID3DXMesh *iface, DWORD flags, DWORD **data)
1436 struct d3dx9_mesh *mesh = impl_from_ID3DXMesh(iface);
1438 TRACE("iface %p, flags %#x, data %p.\n", iface, flags, data);
1440 InterlockedIncrement(&mesh->attrib_buffer_lock_count);
1442 if (!(flags & D3DLOCK_READONLY))
1444 D3DXATTRIBUTERANGE *attrib_table = mesh->attrib_table;
1445 mesh->attrib_table_size = 0;
1446 mesh->attrib_table = NULL;
1447 HeapFree(GetProcessHeap(), 0, attrib_table);
1450 *data = mesh->attrib_buffer;
1452 return D3D_OK;
1455 static HRESULT WINAPI d3dx9_mesh_UnlockAttributeBuffer(ID3DXMesh *iface)
1457 struct d3dx9_mesh *mesh = impl_from_ID3DXMesh(iface);
1458 int lock_count;
1460 TRACE("iface %p.\n", iface);
1462 lock_count = InterlockedDecrement(&mesh->attrib_buffer_lock_count);
1463 if (lock_count < 0)
1465 InterlockedIncrement(&mesh->attrib_buffer_lock_count);
1466 return D3DERR_INVALIDCALL;
1469 return D3D_OK;
1472 static HRESULT WINAPI d3dx9_mesh_Optimize(ID3DXMesh *iface, DWORD flags, const DWORD *adjacency_in,
1473 DWORD *adjacency_out, DWORD *face_remap, ID3DXBuffer **vertex_remap, ID3DXMesh **opt_mesh)
1475 struct d3dx9_mesh *mesh = impl_from_ID3DXMesh(iface);
1476 HRESULT hr;
1477 D3DVERTEXELEMENT9 declaration[MAX_FVF_DECL_SIZE] = { D3DDECL_END() };
1478 ID3DXMesh *optimized_mesh;
1480 TRACE("iface %p, flags %#x, adjacency_in %p, adjacency_out %p, face_remap %p, vertex_remap %p, opt_mesh %p.\n",
1481 iface, flags, adjacency_in, adjacency_out, face_remap, vertex_remap, opt_mesh);
1483 if (!opt_mesh)
1484 return D3DERR_INVALIDCALL;
1486 hr = iface->lpVtbl->GetDeclaration(iface, declaration);
1487 if (FAILED(hr)) return hr;
1489 if (FAILED(hr = iface->lpVtbl->CloneMesh(iface, mesh->options, declaration, mesh->device, &optimized_mesh)))
1490 return hr;
1492 hr = optimized_mesh->lpVtbl->OptimizeInplace(optimized_mesh, flags, adjacency_in, adjacency_out, face_remap, vertex_remap);
1493 if (SUCCEEDED(hr))
1494 *opt_mesh = optimized_mesh;
1495 else
1496 IUnknown_Release(optimized_mesh);
1497 return hr;
1500 /* Creates a vertex_remap that removes unused vertices.
1501 * Indices are updated according to the vertex_remap. */
1502 static HRESULT compact_mesh(struct d3dx9_mesh *This, DWORD *indices,
1503 DWORD *new_num_vertices, ID3DXBuffer **vertex_remap)
1505 HRESULT hr;
1506 DWORD *vertex_remap_ptr;
1507 DWORD num_used_vertices;
1508 DWORD i;
1510 hr = D3DXCreateBuffer(This->numvertices * sizeof(DWORD), vertex_remap);
1511 if (FAILED(hr)) return hr;
1512 vertex_remap_ptr = ID3DXBuffer_GetBufferPointer(*vertex_remap);
1514 for (i = 0; i < This->numfaces * 3; i++)
1515 vertex_remap_ptr[indices[i]] = 1;
1517 /* create old->new vertex mapping */
1518 num_used_vertices = 0;
1519 for (i = 0; i < This->numvertices; i++) {
1520 if (vertex_remap_ptr[i])
1521 vertex_remap_ptr[i] = num_used_vertices++;
1522 else
1523 vertex_remap_ptr[i] = -1;
1525 /* convert indices */
1526 for (i = 0; i < This->numfaces * 3; i++)
1527 indices[i] = vertex_remap_ptr[indices[i]];
1529 /* create new->old vertex mapping */
1530 num_used_vertices = 0;
1531 for (i = 0; i < This->numvertices; i++) {
1532 if (vertex_remap_ptr[i] != -1)
1533 vertex_remap_ptr[num_used_vertices++] = i;
1535 for (i = num_used_vertices; i < This->numvertices; i++)
1536 vertex_remap_ptr[i] = -1;
1538 *new_num_vertices = num_used_vertices;
1540 return D3D_OK;
1543 /* count the number of unique attribute values in a sorted attribute buffer */
1544 static DWORD count_attributes(const DWORD *attrib_buffer, DWORD numfaces)
1546 DWORD last_attribute = attrib_buffer[0];
1547 DWORD attrib_table_size = 1;
1548 DWORD i;
1549 for (i = 1; i < numfaces; i++) {
1550 if (attrib_buffer[i] != last_attribute) {
1551 last_attribute = attrib_buffer[i];
1552 attrib_table_size++;
1555 return attrib_table_size;
1558 static void fill_attribute_table(DWORD *attrib_buffer, DWORD numfaces, void *indices,
1559 BOOL is_32bit_indices, D3DXATTRIBUTERANGE *attrib_table)
1561 DWORD attrib_table_size = 0;
1562 DWORD last_attribute = attrib_buffer[0];
1563 DWORD min_vertex, max_vertex;
1564 DWORD i;
1566 attrib_table[0].AttribId = last_attribute;
1567 attrib_table[0].FaceStart = 0;
1568 min_vertex = (DWORD)-1;
1569 max_vertex = 0;
1570 for (i = 0; i < numfaces; i++) {
1571 DWORD j;
1573 if (attrib_buffer[i] != last_attribute) {
1574 last_attribute = attrib_buffer[i];
1575 attrib_table[attrib_table_size].FaceCount = i - attrib_table[attrib_table_size].FaceStart;
1576 attrib_table[attrib_table_size].VertexStart = min_vertex;
1577 attrib_table[attrib_table_size].VertexCount = max_vertex - min_vertex + 1;
1578 attrib_table_size++;
1579 attrib_table[attrib_table_size].AttribId = attrib_buffer[i];
1580 attrib_table[attrib_table_size].FaceStart = i;
1581 min_vertex = (DWORD)-1;
1582 max_vertex = 0;
1584 for (j = 0; j < 3; j++) {
1585 DWORD vertex_index = is_32bit_indices ? ((DWORD*)indices)[i * 3 + j] : ((WORD*)indices)[i * 3 + j];
1586 if (vertex_index < min_vertex)
1587 min_vertex = vertex_index;
1588 if (vertex_index > max_vertex)
1589 max_vertex = vertex_index;
1592 attrib_table[attrib_table_size].FaceCount = i - attrib_table[attrib_table_size].FaceStart;
1593 attrib_table[attrib_table_size].VertexStart = min_vertex;
1594 attrib_table[attrib_table_size].VertexCount = max_vertex - min_vertex + 1;
1595 attrib_table_size++;
1598 static int attrib_entry_compare(const DWORD **a, const DWORD **b)
1600 const DWORD *ptr_a = *a;
1601 const DWORD *ptr_b = *b;
1602 int delta = *ptr_a - *ptr_b;
1604 if (delta)
1605 return delta;
1607 delta = ptr_a - ptr_b; /* for stable sort */
1608 return delta;
1611 /* Create face_remap, a new attribute buffer for attribute sort optimization. */
1612 static HRESULT remap_faces_for_attrsort(struct d3dx9_mesh *This, const DWORD *indices,
1613 const DWORD *attrib_buffer, DWORD **sorted_attrib_buffer, DWORD **face_remap)
1615 const DWORD **sorted_attrib_ptr_buffer = NULL;
1616 DWORD i;
1618 *face_remap = HeapAlloc(GetProcessHeap(), 0, This->numfaces * sizeof(**face_remap));
1619 sorted_attrib_ptr_buffer = HeapAlloc(GetProcessHeap(), 0, This->numfaces * sizeof(*sorted_attrib_ptr_buffer));
1620 if (!*face_remap || !sorted_attrib_ptr_buffer) {
1621 HeapFree(GetProcessHeap(), 0, sorted_attrib_ptr_buffer);
1622 return E_OUTOFMEMORY;
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),
1627 (int(*)(const void *, const void *))attrib_entry_compare);
1629 for (i = 0; i < This->numfaces; i++)
1631 DWORD old_face = sorted_attrib_ptr_buffer[i] - attrib_buffer;
1632 (*face_remap)[old_face] = i;
1635 /* overwrite sorted_attrib_ptr_buffer with the values themselves */
1636 *sorted_attrib_buffer = (DWORD*)sorted_attrib_ptr_buffer;
1637 for (i = 0; i < This->numfaces; i++)
1638 (*sorted_attrib_buffer)[(*face_remap)[i]] = attrib_buffer[i];
1640 return D3D_OK;
1643 static HRESULT WINAPI d3dx9_mesh_OptimizeInplace(ID3DXMesh *iface, DWORD flags, const DWORD *adjacency_in,
1644 DWORD *adjacency_out, DWORD *face_remap_out, ID3DXBuffer **vertex_remap_out)
1646 struct d3dx9_mesh *This = impl_from_ID3DXMesh(iface);
1647 void *indices = NULL;
1648 DWORD *attrib_buffer = NULL;
1649 HRESULT hr;
1650 ID3DXBuffer *vertex_remap = NULL;
1651 DWORD *face_remap = NULL; /* old -> new mapping */
1652 DWORD *dword_indices = NULL;
1653 DWORD new_num_vertices = 0;
1654 DWORD new_num_alloc_vertices = 0;
1655 IDirect3DVertexBuffer9 *vertex_buffer = NULL;
1656 DWORD *sorted_attrib_buffer = NULL;
1657 DWORD i;
1659 TRACE("iface %p, flags %#x, adjacency_in %p, adjacency_out %p, face_remap_out %p, vertex_remap_out %p.\n",
1660 iface, flags, adjacency_in, adjacency_out, face_remap_out, vertex_remap_out);
1662 if (!flags)
1663 return D3DERR_INVALIDCALL;
1664 if (!adjacency_in && (flags & (D3DXMESHOPT_VERTEXCACHE | D3DXMESHOPT_STRIPREORDER)))
1665 return D3DERR_INVALIDCALL;
1666 if ((flags & (D3DXMESHOPT_VERTEXCACHE | D3DXMESHOPT_STRIPREORDER)) == (D3DXMESHOPT_VERTEXCACHE | D3DXMESHOPT_STRIPREORDER))
1667 return D3DERR_INVALIDCALL;
1669 if (flags & (D3DXMESHOPT_VERTEXCACHE | D3DXMESHOPT_STRIPREORDER))
1671 if (flags & D3DXMESHOPT_VERTEXCACHE)
1672 FIXME("D3DXMESHOPT_VERTEXCACHE not implemented.\n");
1673 if (flags & D3DXMESHOPT_STRIPREORDER)
1674 FIXME("D3DXMESHOPT_STRIPREORDER not implemented.\n");
1675 return E_NOTIMPL;
1678 hr = iface->lpVtbl->LockIndexBuffer(iface, 0, &indices);
1679 if (FAILED(hr)) goto cleanup;
1681 dword_indices = HeapAlloc(GetProcessHeap(), 0, This->numfaces * 3 * sizeof(DWORD));
1682 if (!dword_indices) return E_OUTOFMEMORY;
1683 if (This->options & D3DXMESH_32BIT) {
1684 memcpy(dword_indices, indices, This->numfaces * 3 * sizeof(DWORD));
1685 } else {
1686 WORD *word_indices = indices;
1687 for (i = 0; i < This->numfaces * 3; i++)
1688 dword_indices[i] = *word_indices++;
1691 if ((flags & (D3DXMESHOPT_COMPACT | D3DXMESHOPT_IGNOREVERTS | D3DXMESHOPT_ATTRSORT)) == D3DXMESHOPT_COMPACT)
1693 new_num_alloc_vertices = This->numvertices;
1694 hr = compact_mesh(This, dword_indices, &new_num_vertices, &vertex_remap);
1695 if (FAILED(hr)) goto cleanup;
1696 } else if (flags & D3DXMESHOPT_ATTRSORT) {
1697 if (!(flags & D3DXMESHOPT_IGNOREVERTS))
1699 FIXME("D3DXMESHOPT_ATTRSORT vertex reordering not implemented.\n");
1700 hr = E_NOTIMPL;
1701 goto cleanup;
1704 hr = iface->lpVtbl->LockAttributeBuffer(iface, 0, &attrib_buffer);
1705 if (FAILED(hr)) goto cleanup;
1707 hr = remap_faces_for_attrsort(This, dword_indices, attrib_buffer, &sorted_attrib_buffer, &face_remap);
1708 if (FAILED(hr)) goto cleanup;
1711 if (vertex_remap)
1713 /* reorder the vertices using vertex_remap */
1714 D3DVERTEXBUFFER_DESC vertex_desc;
1715 DWORD *vertex_remap_ptr = ID3DXBuffer_GetBufferPointer(vertex_remap);
1716 DWORD vertex_size = iface->lpVtbl->GetNumBytesPerVertex(iface);
1717 BYTE *orig_vertices;
1718 BYTE *new_vertices;
1720 hr = IDirect3DVertexBuffer9_GetDesc(This->vertex_buffer, &vertex_desc);
1721 if (FAILED(hr)) goto cleanup;
1723 hr = IDirect3DDevice9_CreateVertexBuffer(This->device, new_num_alloc_vertices * vertex_size,
1724 vertex_desc.Usage, This->fvf, vertex_desc.Pool, &vertex_buffer, NULL);
1725 if (FAILED(hr)) goto cleanup;
1727 hr = IDirect3DVertexBuffer9_Lock(This->vertex_buffer, 0, 0, (void**)&orig_vertices, D3DLOCK_READONLY);
1728 if (FAILED(hr)) goto cleanup;
1730 hr = IDirect3DVertexBuffer9_Lock(vertex_buffer, 0, 0, (void**)&new_vertices, 0);
1731 if (FAILED(hr)) {
1732 IDirect3DVertexBuffer9_Unlock(This->vertex_buffer);
1733 goto cleanup;
1736 for (i = 0; i < new_num_vertices; i++)
1737 memcpy(new_vertices + i * vertex_size, orig_vertices + vertex_remap_ptr[i] * vertex_size, vertex_size);
1739 IDirect3DVertexBuffer9_Unlock(This->vertex_buffer);
1740 IDirect3DVertexBuffer9_Unlock(vertex_buffer);
1741 } else if (vertex_remap_out) {
1742 DWORD *vertex_remap_ptr;
1744 hr = D3DXCreateBuffer(This->numvertices * sizeof(DWORD), &vertex_remap);
1745 if (FAILED(hr)) goto cleanup;
1746 vertex_remap_ptr = ID3DXBuffer_GetBufferPointer(vertex_remap);
1747 for (i = 0; i < This->numvertices; i++)
1748 *vertex_remap_ptr++ = i;
1751 if (flags & D3DXMESHOPT_ATTRSORT)
1753 D3DXATTRIBUTERANGE *attrib_table;
1754 DWORD attrib_table_size;
1756 attrib_table_size = count_attributes(sorted_attrib_buffer, This->numfaces);
1757 attrib_table = HeapAlloc(GetProcessHeap(), 0, attrib_table_size * sizeof(*attrib_table));
1758 if (!attrib_table) {
1759 hr = E_OUTOFMEMORY;
1760 goto cleanup;
1763 memcpy(attrib_buffer, sorted_attrib_buffer, This->numfaces * sizeof(*attrib_buffer));
1765 /* reorder the indices using face_remap */
1766 if (This->options & D3DXMESH_32BIT) {
1767 for (i = 0; i < This->numfaces; i++)
1768 memcpy((DWORD*)indices + face_remap[i] * 3, dword_indices + i * 3, 3 * sizeof(DWORD));
1769 } else {
1770 WORD *word_indices = indices;
1771 for (i = 0; i < This->numfaces; i++) {
1772 DWORD new_pos = face_remap[i] * 3;
1773 DWORD old_pos = i * 3;
1774 word_indices[new_pos++] = dword_indices[old_pos++];
1775 word_indices[new_pos++] = dword_indices[old_pos++];
1776 word_indices[new_pos] = dword_indices[old_pos];
1780 fill_attribute_table(attrib_buffer, This->numfaces, indices,
1781 This->options & D3DXMESH_32BIT, attrib_table);
1783 HeapFree(GetProcessHeap(), 0, This->attrib_table);
1784 This->attrib_table = attrib_table;
1785 This->attrib_table_size = attrib_table_size;
1786 } else {
1787 if (This->options & D3DXMESH_32BIT) {
1788 memcpy(indices, dword_indices, This->numfaces * 3 * sizeof(DWORD));
1789 } else {
1790 WORD *word_indices = indices;
1791 for (i = 0; i < This->numfaces * 3; i++)
1792 *word_indices++ = dword_indices[i];
1796 if (adjacency_out) {
1797 if (face_remap) {
1798 for (i = 0; i < This->numfaces; i++) {
1799 DWORD old_pos = i * 3;
1800 DWORD new_pos = face_remap[i] * 3;
1801 adjacency_out[new_pos++] = face_remap[adjacency_in[old_pos++]];
1802 adjacency_out[new_pos++] = face_remap[adjacency_in[old_pos++]];
1803 adjacency_out[new_pos++] = face_remap[adjacency_in[old_pos++]];
1805 } else {
1806 memcpy(adjacency_out, adjacency_in, This->numfaces * 3 * sizeof(*adjacency_out));
1809 if (face_remap_out) {
1810 if (face_remap) {
1811 for (i = 0; i < This->numfaces; i++)
1812 face_remap_out[face_remap[i]] = i;
1813 } else {
1814 for (i = 0; i < This->numfaces; i++)
1815 face_remap_out[i] = i;
1818 if (vertex_remap_out)
1819 *vertex_remap_out = vertex_remap;
1820 vertex_remap = NULL;
1822 if (vertex_buffer) {
1823 IDirect3DVertexBuffer9_Release(This->vertex_buffer);
1824 This->vertex_buffer = vertex_buffer;
1825 vertex_buffer = NULL;
1826 This->numvertices = new_num_vertices;
1829 hr = D3D_OK;
1830 cleanup:
1831 HeapFree(GetProcessHeap(), 0, sorted_attrib_buffer);
1832 HeapFree(GetProcessHeap(), 0, face_remap);
1833 HeapFree(GetProcessHeap(), 0, dword_indices);
1834 if (vertex_remap) ID3DXBuffer_Release(vertex_remap);
1835 if (vertex_buffer) IDirect3DVertexBuffer9_Release(vertex_buffer);
1836 if (attrib_buffer) iface->lpVtbl->UnlockAttributeBuffer(iface);
1837 if (indices) iface->lpVtbl->UnlockIndexBuffer(iface);
1838 return hr;
1841 static HRESULT WINAPI d3dx9_mesh_SetAttributeTable(ID3DXMesh *iface,
1842 const D3DXATTRIBUTERANGE *attrib_table, DWORD attrib_table_size)
1844 struct d3dx9_mesh *mesh = impl_from_ID3DXMesh(iface);
1845 D3DXATTRIBUTERANGE *new_table = NULL;
1847 TRACE("iface %p, attrib_table %p, attrib_table_size %u.\n", iface, attrib_table, attrib_table_size);
1849 if (attrib_table_size) {
1850 size_t size = attrib_table_size * sizeof(*attrib_table);
1852 new_table = HeapAlloc(GetProcessHeap(), 0, size);
1853 if (!new_table)
1854 return E_OUTOFMEMORY;
1856 CopyMemory(new_table, attrib_table, size);
1857 } else if (attrib_table) {
1858 return D3DERR_INVALIDCALL;
1860 HeapFree(GetProcessHeap(), 0, mesh->attrib_table);
1861 mesh->attrib_table = new_table;
1862 mesh->attrib_table_size = attrib_table_size;
1864 return D3D_OK;
1867 static const struct ID3DXMeshVtbl D3DXMesh_Vtbl =
1869 d3dx9_mesh_QueryInterface,
1870 d3dx9_mesh_AddRef,
1871 d3dx9_mesh_Release,
1872 d3dx9_mesh_DrawSubset,
1873 d3dx9_mesh_GetNumFaces,
1874 d3dx9_mesh_GetNumVertices,
1875 d3dx9_mesh_GetFVF,
1876 d3dx9_mesh_GetDeclaration,
1877 d3dx9_mesh_GetNumBytesPerVertex,
1878 d3dx9_mesh_GetOptions,
1879 d3dx9_mesh_GetDevice,
1880 d3dx9_mesh_CloneMeshFVF,
1881 d3dx9_mesh_CloneMesh,
1882 d3dx9_mesh_GetVertexBuffer,
1883 d3dx9_mesh_GetIndexBuffer,
1884 d3dx9_mesh_LockVertexBuffer,
1885 d3dx9_mesh_UnlockVertexBuffer,
1886 d3dx9_mesh_LockIndexBuffer,
1887 d3dx9_mesh_UnlockIndexBuffer,
1888 d3dx9_mesh_GetAttributeTable,
1889 d3dx9_mesh_ConvertPointRepsToAdjacency,
1890 d3dx9_mesh_ConvertAdjacencyToPointReps,
1891 d3dx9_mesh_GenerateAdjacency,
1892 d3dx9_mesh_UpdateSemantics,
1893 d3dx9_mesh_LockAttributeBuffer,
1894 d3dx9_mesh_UnlockAttributeBuffer,
1895 d3dx9_mesh_Optimize,
1896 d3dx9_mesh_OptimizeInplace,
1897 d3dx9_mesh_SetAttributeTable,
1901 /* Algorithm taken from the article: An Efficient and Robust Ray-Box Intersection Algorithm
1902 Amy Williams University of Utah
1903 Steve Barrus University of Utah
1904 R. Keith Morley University of Utah
1905 Peter Shirley University of Utah
1907 International Conference on Computer Graphics and Interactive Techniques archive
1908 ACM SIGGRAPH 2005 Courses
1909 Los Angeles, California
1911 This algorithm is free of patents or of copyrights, as confirmed by Peter Shirley himself.
1913 Algorithm: Consider the box as the intersection of three slabs. Clip the ray
1914 against each slab, if there's anything left of the ray after we're
1915 done we've got an intersection of the ray with the box. */
1916 BOOL WINAPI D3DXBoxBoundProbe(const D3DXVECTOR3 *pmin, const D3DXVECTOR3 *pmax,
1917 const D3DXVECTOR3 *prayposition, const D3DXVECTOR3 *praydirection)
1919 FLOAT div, tmin, tmax, tymin, tymax, tzmin, tzmax;
1921 div = 1.0f / praydirection->x;
1922 if ( div >= 0.0f )
1924 tmin = ( pmin->x - prayposition->x ) * div;
1925 tmax = ( pmax->x - prayposition->x ) * div;
1927 else
1929 tmin = ( pmax->x - prayposition->x ) * div;
1930 tmax = ( pmin->x - prayposition->x ) * div;
1933 if ( tmax < 0.0f ) return FALSE;
1935 div = 1.0f / praydirection->y;
1936 if ( div >= 0.0f )
1938 tymin = ( pmin->y - prayposition->y ) * div;
1939 tymax = ( pmax->y - prayposition->y ) * div;
1941 else
1943 tymin = ( pmax->y - prayposition->y ) * div;
1944 tymax = ( pmin->y - prayposition->y ) * div;
1947 if ( ( tymax < 0.0f ) || ( tmin > tymax ) || ( tymin > tmax ) ) return FALSE;
1949 if ( tymin > tmin ) tmin = tymin;
1950 if ( tymax < tmax ) tmax = tymax;
1952 div = 1.0f / praydirection->z;
1953 if ( div >= 0.0f )
1955 tzmin = ( pmin->z - prayposition->z ) * div;
1956 tzmax = ( pmax->z - prayposition->z ) * div;
1958 else
1960 tzmin = ( pmax->z - prayposition->z ) * div;
1961 tzmax = ( pmin->z - prayposition->z ) * div;
1964 if ( (tzmax < 0.0f ) || ( tmin > tzmax ) || ( tzmin > tmax ) ) return FALSE;
1966 return TRUE;
1969 HRESULT WINAPI D3DXComputeBoundingBox(const D3DXVECTOR3 *pfirstposition,
1970 DWORD numvertices, DWORD dwstride, D3DXVECTOR3 *pmin, D3DXVECTOR3 *pmax)
1972 D3DXVECTOR3 vec;
1973 unsigned int i;
1975 if( !pfirstposition || !pmin || !pmax ) return D3DERR_INVALIDCALL;
1977 *pmin = *pfirstposition;
1978 *pmax = *pmin;
1980 for(i=0; i<numvertices; i++)
1982 vec = *( (const D3DXVECTOR3*)((const char*)pfirstposition + dwstride * i) );
1984 if ( vec.x < pmin->x ) pmin->x = vec.x;
1985 if ( vec.x > pmax->x ) pmax->x = vec.x;
1987 if ( vec.y < pmin->y ) pmin->y = vec.y;
1988 if ( vec.y > pmax->y ) pmax->y = vec.y;
1990 if ( vec.z < pmin->z ) pmin->z = vec.z;
1991 if ( vec.z > pmax->z ) pmax->z = vec.z;
1994 return D3D_OK;
1997 HRESULT WINAPI D3DXComputeBoundingSphere(const D3DXVECTOR3 *pfirstposition,
1998 DWORD numvertices, DWORD dwstride, D3DXVECTOR3 *pcenter, float *pradius)
2000 D3DXVECTOR3 temp;
2001 FLOAT d;
2002 unsigned int i;
2004 if( !pfirstposition || !pcenter || !pradius ) return D3DERR_INVALIDCALL;
2006 temp.x = 0.0f;
2007 temp.y = 0.0f;
2008 temp.z = 0.0f;
2009 *pradius = 0.0f;
2011 for(i=0; i<numvertices; i++)
2012 D3DXVec3Add(&temp, &temp, (const D3DXVECTOR3*)((const char*)pfirstposition + dwstride * i));
2014 D3DXVec3Scale(pcenter, &temp, 1.0f / numvertices);
2016 for(i=0; i<numvertices; i++)
2018 d = D3DXVec3Length(D3DXVec3Subtract(&temp, (const D3DXVECTOR3*)((const char*)pfirstposition + dwstride * i), pcenter));
2019 if ( d > *pradius ) *pradius = d;
2021 return D3D_OK;
2024 static void append_decl_element(D3DVERTEXELEMENT9 *declaration, UINT *idx, UINT *offset,
2025 D3DDECLTYPE type, D3DDECLUSAGE usage, UINT usage_idx)
2027 declaration[*idx].Stream = 0;
2028 declaration[*idx].Offset = *offset;
2029 declaration[*idx].Type = type;
2030 declaration[*idx].Method = D3DDECLMETHOD_DEFAULT;
2031 declaration[*idx].Usage = usage;
2032 declaration[*idx].UsageIndex = usage_idx;
2034 *offset += d3dx_decltype_size[type];
2035 ++(*idx);
2038 /*************************************************************************
2039 * D3DXDeclaratorFromFVF
2041 HRESULT WINAPI D3DXDeclaratorFromFVF(DWORD fvf, D3DVERTEXELEMENT9 declaration[MAX_FVF_DECL_SIZE])
2043 static const D3DVERTEXELEMENT9 end_element = D3DDECL_END();
2044 DWORD tex_count = (fvf & D3DFVF_TEXCOUNT_MASK) >> D3DFVF_TEXCOUNT_SHIFT;
2045 unsigned int offset = 0;
2046 unsigned int idx = 0;
2047 unsigned int i;
2049 TRACE("fvf %#x, declaration %p.\n", fvf, declaration);
2051 if (fvf & (D3DFVF_RESERVED0 | D3DFVF_RESERVED2)) return D3DERR_INVALIDCALL;
2053 if (fvf & D3DFVF_POSITION_MASK)
2055 BOOL has_blend = (fvf & D3DFVF_XYZB5) >= D3DFVF_XYZB1;
2056 DWORD blend_count = 1 + (((fvf & D3DFVF_XYZB5) - D3DFVF_XYZB1) >> 1);
2057 BOOL has_blend_idx = (fvf & D3DFVF_LASTBETA_D3DCOLOR) || (fvf & D3DFVF_LASTBETA_UBYTE4);
2059 if (has_blend_idx) --blend_count;
2061 if ((fvf & D3DFVF_POSITION_MASK) == D3DFVF_XYZW
2062 || (has_blend && blend_count > 4))
2063 return D3DERR_INVALIDCALL;
2065 if ((fvf & D3DFVF_POSITION_MASK) == D3DFVF_XYZRHW)
2066 append_decl_element(declaration, &idx, &offset, D3DDECLTYPE_FLOAT4, D3DDECLUSAGE_POSITIONT, 0);
2067 else
2068 append_decl_element(declaration, &idx, &offset, D3DDECLTYPE_FLOAT3, D3DDECLUSAGE_POSITION, 0);
2070 if (has_blend)
2072 switch (blend_count)
2074 case 0:
2075 break;
2076 case 1:
2077 append_decl_element(declaration, &idx, &offset, D3DDECLTYPE_FLOAT1, D3DDECLUSAGE_BLENDWEIGHT, 0);
2078 break;
2079 case 2:
2080 append_decl_element(declaration, &idx, &offset, D3DDECLTYPE_FLOAT2, D3DDECLUSAGE_BLENDWEIGHT, 0);
2081 break;
2082 case 3:
2083 append_decl_element(declaration, &idx, &offset, D3DDECLTYPE_FLOAT3, D3DDECLUSAGE_BLENDWEIGHT, 0);
2084 break;
2085 case 4:
2086 append_decl_element(declaration, &idx, &offset, D3DDECLTYPE_FLOAT4, D3DDECLUSAGE_BLENDWEIGHT, 0);
2087 break;
2088 default:
2089 ERR("Invalid blend count %u.\n", blend_count);
2090 break;
2093 if (has_blend_idx)
2095 if (fvf & D3DFVF_LASTBETA_UBYTE4)
2096 append_decl_element(declaration, &idx, &offset, D3DDECLTYPE_UBYTE4, D3DDECLUSAGE_BLENDINDICES, 0);
2097 else if (fvf & D3DFVF_LASTBETA_D3DCOLOR)
2098 append_decl_element(declaration, &idx, &offset, D3DDECLTYPE_D3DCOLOR, D3DDECLUSAGE_BLENDINDICES, 0);
2103 if (fvf & D3DFVF_NORMAL)
2104 append_decl_element(declaration, &idx, &offset, D3DDECLTYPE_FLOAT3, D3DDECLUSAGE_NORMAL, 0);
2105 if (fvf & D3DFVF_PSIZE)
2106 append_decl_element(declaration, &idx, &offset, D3DDECLTYPE_FLOAT1, D3DDECLUSAGE_PSIZE, 0);
2107 if (fvf & D3DFVF_DIFFUSE)
2108 append_decl_element(declaration, &idx, &offset, D3DDECLTYPE_D3DCOLOR, D3DDECLUSAGE_COLOR, 0);
2109 if (fvf & D3DFVF_SPECULAR)
2110 append_decl_element(declaration, &idx, &offset, D3DDECLTYPE_D3DCOLOR, D3DDECLUSAGE_COLOR, 1);
2112 for (i = 0; i < tex_count; ++i)
2114 switch ((fvf >> (16 + 2 * i)) & 0x03)
2116 case D3DFVF_TEXTUREFORMAT1:
2117 append_decl_element(declaration, &idx, &offset, D3DDECLTYPE_FLOAT1, D3DDECLUSAGE_TEXCOORD, i);
2118 break;
2119 case D3DFVF_TEXTUREFORMAT2:
2120 append_decl_element(declaration, &idx, &offset, D3DDECLTYPE_FLOAT2, D3DDECLUSAGE_TEXCOORD, i);
2121 break;
2122 case D3DFVF_TEXTUREFORMAT3:
2123 append_decl_element(declaration, &idx, &offset, D3DDECLTYPE_FLOAT3, D3DDECLUSAGE_TEXCOORD, i);
2124 break;
2125 case D3DFVF_TEXTUREFORMAT4:
2126 append_decl_element(declaration, &idx, &offset, D3DDECLTYPE_FLOAT4, D3DDECLUSAGE_TEXCOORD, i);
2127 break;
2131 declaration[idx] = end_element;
2133 return D3D_OK;
2136 /*************************************************************************
2137 * D3DXFVFFromDeclarator
2139 HRESULT WINAPI D3DXFVFFromDeclarator(const D3DVERTEXELEMENT9 *declaration, DWORD *fvf)
2141 unsigned int i = 0, texture, offset;
2143 TRACE("(%p, %p)\n", declaration, fvf);
2145 *fvf = 0;
2146 if (declaration[0].Type == D3DDECLTYPE_FLOAT3 && declaration[0].Usage == D3DDECLUSAGE_POSITION)
2148 if ((declaration[1].Type == D3DDECLTYPE_FLOAT4 && declaration[1].Usage == D3DDECLUSAGE_BLENDWEIGHT &&
2149 declaration[1].UsageIndex == 0) &&
2150 (declaration[2].Type == D3DDECLTYPE_FLOAT1 && declaration[2].Usage == D3DDECLUSAGE_BLENDINDICES &&
2151 declaration[2].UsageIndex == 0))
2153 return D3DERR_INVALIDCALL;
2155 else if ((declaration[1].Type == D3DDECLTYPE_UBYTE4 || declaration[1].Type == D3DDECLTYPE_D3DCOLOR) &&
2156 declaration[1].Usage == D3DDECLUSAGE_BLENDINDICES && declaration[1].UsageIndex == 0)
2158 if (declaration[1].Type == D3DDECLTYPE_UBYTE4)
2160 *fvf |= D3DFVF_XYZB1 | D3DFVF_LASTBETA_UBYTE4;
2162 else
2164 *fvf |= D3DFVF_XYZB1 | D3DFVF_LASTBETA_D3DCOLOR;
2166 i = 2;
2168 else if (declaration[1].Type <= D3DDECLTYPE_FLOAT4 && declaration[1].Usage == D3DDECLUSAGE_BLENDWEIGHT &&
2169 declaration[1].UsageIndex == 0)
2171 if ((declaration[2].Type == D3DDECLTYPE_UBYTE4 || declaration[2].Type == D3DDECLTYPE_D3DCOLOR) &&
2172 declaration[2].Usage == D3DDECLUSAGE_BLENDINDICES && declaration[2].UsageIndex == 0)
2174 if (declaration[2].Type == D3DDECLTYPE_UBYTE4)
2176 *fvf |= D3DFVF_LASTBETA_UBYTE4;
2178 else
2180 *fvf |= D3DFVF_LASTBETA_D3DCOLOR;
2182 switch (declaration[1].Type)
2184 case D3DDECLTYPE_FLOAT1: *fvf |= D3DFVF_XYZB2; break;
2185 case D3DDECLTYPE_FLOAT2: *fvf |= D3DFVF_XYZB3; break;
2186 case D3DDECLTYPE_FLOAT3: *fvf |= D3DFVF_XYZB4; break;
2187 case D3DDECLTYPE_FLOAT4: *fvf |= D3DFVF_XYZB5; break;
2189 i = 3;
2191 else
2193 switch (declaration[1].Type)
2195 case D3DDECLTYPE_FLOAT1: *fvf |= D3DFVF_XYZB1; break;
2196 case D3DDECLTYPE_FLOAT2: *fvf |= D3DFVF_XYZB2; break;
2197 case D3DDECLTYPE_FLOAT3: *fvf |= D3DFVF_XYZB3; break;
2198 case D3DDECLTYPE_FLOAT4: *fvf |= D3DFVF_XYZB4; break;
2200 i = 2;
2203 else
2205 *fvf |= D3DFVF_XYZ;
2206 i = 1;
2209 else if (declaration[0].Type == D3DDECLTYPE_FLOAT4 && declaration[0].Usage == D3DDECLUSAGE_POSITIONT &&
2210 declaration[0].UsageIndex == 0)
2212 *fvf |= D3DFVF_XYZRHW;
2213 i = 1;
2216 if (declaration[i].Type == D3DDECLTYPE_FLOAT3 && declaration[i].Usage == D3DDECLUSAGE_NORMAL)
2218 *fvf |= D3DFVF_NORMAL;
2219 i++;
2221 if (declaration[i].Type == D3DDECLTYPE_FLOAT1 && declaration[i].Usage == D3DDECLUSAGE_PSIZE &&
2222 declaration[i].UsageIndex == 0)
2224 *fvf |= D3DFVF_PSIZE;
2225 i++;
2227 if (declaration[i].Type == D3DDECLTYPE_D3DCOLOR && declaration[i].Usage == D3DDECLUSAGE_COLOR &&
2228 declaration[i].UsageIndex == 0)
2230 *fvf |= D3DFVF_DIFFUSE;
2231 i++;
2233 if (declaration[i].Type == D3DDECLTYPE_D3DCOLOR && declaration[i].Usage == D3DDECLUSAGE_COLOR &&
2234 declaration[i].UsageIndex == 1)
2236 *fvf |= D3DFVF_SPECULAR;
2237 i++;
2240 for (texture = 0; texture < D3DDP_MAXTEXCOORD; i++, texture++)
2242 if (declaration[i].Stream == 0xFF)
2244 break;
2246 else if (declaration[i].Type == D3DDECLTYPE_FLOAT1 && declaration[i].Usage == D3DDECLUSAGE_TEXCOORD &&
2247 declaration[i].UsageIndex == texture)
2249 *fvf |= D3DFVF_TEXCOORDSIZE1(declaration[i].UsageIndex);
2251 else if (declaration[i].Type == D3DDECLTYPE_FLOAT2 && declaration[i].Usage == D3DDECLUSAGE_TEXCOORD &&
2252 declaration[i].UsageIndex == texture)
2254 *fvf |= D3DFVF_TEXCOORDSIZE2(declaration[i].UsageIndex);
2256 else if (declaration[i].Type == D3DDECLTYPE_FLOAT3 && declaration[i].Usage == D3DDECLUSAGE_TEXCOORD &&
2257 declaration[i].UsageIndex == texture)
2259 *fvf |= D3DFVF_TEXCOORDSIZE3(declaration[i].UsageIndex);
2261 else if (declaration[i].Type == D3DDECLTYPE_FLOAT4 && declaration[i].Usage == D3DDECLUSAGE_TEXCOORD &&
2262 declaration[i].UsageIndex == texture)
2264 *fvf |= D3DFVF_TEXCOORDSIZE4(declaration[i].UsageIndex);
2266 else
2268 return D3DERR_INVALIDCALL;
2272 *fvf |= (texture << D3DFVF_TEXCOUNT_SHIFT);
2274 for (offset = 0, i = 0; declaration[i].Stream != 0xFF;
2275 offset += d3dx_decltype_size[declaration[i].Type], i++)
2277 if (declaration[i].Offset != offset)
2279 return D3DERR_INVALIDCALL;
2283 return D3D_OK;
2286 /*************************************************************************
2287 * D3DXGetFVFVertexSize
2289 static UINT Get_TexCoord_Size_From_FVF(DWORD FVF, int tex_num)
2291 return (((((FVF) >> (16 + (2 * (tex_num)))) + 1) & 0x03) + 1);
2294 UINT WINAPI D3DXGetFVFVertexSize(DWORD FVF)
2296 DWORD size = 0;
2297 UINT i;
2298 UINT numTextures = (FVF & D3DFVF_TEXCOUNT_MASK) >> D3DFVF_TEXCOUNT_SHIFT;
2300 if (FVF & D3DFVF_NORMAL) size += sizeof(D3DXVECTOR3);
2301 if (FVF & D3DFVF_DIFFUSE) size += sizeof(DWORD);
2302 if (FVF & D3DFVF_SPECULAR) size += sizeof(DWORD);
2303 if (FVF & D3DFVF_PSIZE) size += sizeof(DWORD);
2305 switch (FVF & D3DFVF_POSITION_MASK)
2307 case D3DFVF_XYZ: size += sizeof(D3DXVECTOR3); break;
2308 case D3DFVF_XYZRHW: size += 4 * sizeof(FLOAT); break;
2309 case D3DFVF_XYZB1: size += 4 * sizeof(FLOAT); break;
2310 case D3DFVF_XYZB2: size += 5 * sizeof(FLOAT); break;
2311 case D3DFVF_XYZB3: size += 6 * sizeof(FLOAT); break;
2312 case D3DFVF_XYZB4: size += 7 * sizeof(FLOAT); break;
2313 case D3DFVF_XYZB5: size += 8 * sizeof(FLOAT); break;
2314 case D3DFVF_XYZW: size += 4 * sizeof(FLOAT); break;
2317 for (i = 0; i < numTextures; i++)
2319 size += Get_TexCoord_Size_From_FVF(FVF, i) * sizeof(FLOAT);
2322 return size;
2325 /*************************************************************************
2326 * D3DXGetDeclVertexSize
2328 UINT WINAPI D3DXGetDeclVertexSize(const D3DVERTEXELEMENT9 *decl, DWORD stream_idx)
2330 const D3DVERTEXELEMENT9 *element;
2331 UINT size = 0;
2333 TRACE("decl %p, stream_idx %u\n", decl, stream_idx);
2335 if (!decl) return 0;
2337 for (element = decl; element->Stream != 0xff; ++element)
2339 UINT type_size;
2341 if (element->Stream != stream_idx) continue;
2343 if (element->Type >= sizeof(d3dx_decltype_size) / sizeof(*d3dx_decltype_size))
2345 FIXME("Unhandled element type %#x, size will be incorrect.\n", element->Type);
2346 continue;
2349 type_size = d3dx_decltype_size[element->Type];
2350 if (element->Offset + type_size > size) size = element->Offset + type_size;
2353 return size;
2356 /*************************************************************************
2357 * D3DXGetDeclLength
2359 UINT WINAPI D3DXGetDeclLength(const D3DVERTEXELEMENT9 *decl)
2361 const D3DVERTEXELEMENT9 *element;
2363 TRACE("decl %p\n", decl);
2365 /* null decl results in exception on Windows XP */
2367 for (element = decl; element->Stream != 0xff; ++element);
2369 return element - decl;
2372 BOOL WINAPI D3DXIntersectTri(const D3DXVECTOR3 *p0, const D3DXVECTOR3 *p1, const D3DXVECTOR3 *p2,
2373 const D3DXVECTOR3 *praypos, const D3DXVECTOR3 *praydir, float *pu, float *pv, float *pdist)
2375 D3DXMATRIX m;
2376 D3DXVECTOR4 vec;
2378 m.u.m[0][0] = p1->x - p0->x;
2379 m.u.m[1][0] = p2->x - p0->x;
2380 m.u.m[2][0] = -praydir->x;
2381 m.u.m[3][0] = 0.0f;
2382 m.u.m[0][1] = p1->y - p0->z;
2383 m.u.m[1][1] = p2->y - p0->z;
2384 m.u.m[2][1] = -praydir->y;
2385 m.u.m[3][1] = 0.0f;
2386 m.u.m[0][2] = p1->z - p0->z;
2387 m.u.m[1][2] = p2->z - p0->z;
2388 m.u.m[2][2] = -praydir->z;
2389 m.u.m[3][2] = 0.0f;
2390 m.u.m[0][3] = 0.0f;
2391 m.u.m[1][3] = 0.0f;
2392 m.u.m[2][3] = 0.0f;
2393 m.u.m[3][3] = 1.0f;
2395 vec.x = praypos->x - p0->x;
2396 vec.y = praypos->y - p0->y;
2397 vec.z = praypos->z - p0->z;
2398 vec.w = 0.0f;
2400 if ( D3DXMatrixInverse(&m, NULL, &m) )
2402 D3DXVec4Transform(&vec, &vec, &m);
2403 if ( (vec.x >= 0.0f) && (vec.y >= 0.0f) && (vec.x + vec.y <= 1.0f) && (vec.z >= 0.0f) )
2405 *pu = vec.x;
2406 *pv = vec.y;
2407 *pdist = fabs( vec.z );
2408 return TRUE;
2412 return FALSE;
2415 BOOL WINAPI D3DXSphereBoundProbe(const D3DXVECTOR3 *pcenter, float radius,
2416 const D3DXVECTOR3 *prayposition, const D3DXVECTOR3 *praydirection)
2418 D3DXVECTOR3 difference;
2419 FLOAT a, b, c, d;
2421 a = D3DXVec3LengthSq(praydirection);
2422 if (!D3DXVec3Subtract(&difference, prayposition, pcenter)) return FALSE;
2423 b = D3DXVec3Dot(&difference, praydirection);
2424 c = D3DXVec3LengthSq(&difference) - radius * radius;
2425 d = b * b - a * c;
2427 if ( ( d <= 0.0f ) || ( sqrt(d) <= b ) ) return FALSE;
2428 return TRUE;
2431 /*************************************************************************
2432 * D3DXCreateMesh
2434 HRESULT WINAPI D3DXCreateMesh(DWORD numfaces, DWORD numvertices, DWORD options,
2435 const D3DVERTEXELEMENT9 *declaration, struct IDirect3DDevice9 *device, struct ID3DXMesh **mesh)
2437 HRESULT hr;
2438 DWORD fvf;
2439 IDirect3DVertexDeclaration9 *vertex_declaration;
2440 UINT vertex_declaration_size;
2441 UINT num_elem;
2442 IDirect3DVertexBuffer9 *vertex_buffer;
2443 IDirect3DIndexBuffer9 *index_buffer;
2444 DWORD *attrib_buffer;
2445 struct d3dx9_mesh *object;
2446 DWORD index_usage = 0;
2447 D3DPOOL index_pool = D3DPOOL_DEFAULT;
2448 D3DFORMAT index_format = D3DFMT_INDEX16;
2449 DWORD vertex_usage = 0;
2450 D3DPOOL vertex_pool = D3DPOOL_DEFAULT;
2451 int i;
2453 TRACE("numfaces %u, numvertices %u, options %#x, declaration %p, device %p, mesh %p.\n",
2454 numfaces, numvertices, options, declaration, device, mesh);
2456 if (numfaces == 0 || numvertices == 0 || declaration == NULL || device == NULL || mesh == NULL ||
2457 /* D3DXMESH_VB_SHARE is for cloning, and D3DXMESH_USEHWONLY is for ConvertToBlendedMesh */
2458 (options & (D3DXMESH_VB_SHARE | D3DXMESH_USEHWONLY | 0xfffe0000)))
2460 return D3DERR_INVALIDCALL;
2462 for (i = 0; declaration[i].Stream != 0xff; i++)
2463 if (declaration[i].Stream != 0)
2464 return D3DERR_INVALIDCALL;
2465 num_elem = i + 1;
2467 if (options & D3DXMESH_32BIT)
2468 index_format = D3DFMT_INDEX32;
2470 if (options & D3DXMESH_DONOTCLIP) {
2471 index_usage |= D3DUSAGE_DONOTCLIP;
2472 vertex_usage |= D3DUSAGE_DONOTCLIP;
2474 if (options & D3DXMESH_POINTS) {
2475 index_usage |= D3DUSAGE_POINTS;
2476 vertex_usage |= D3DUSAGE_POINTS;
2478 if (options & D3DXMESH_RTPATCHES) {
2479 index_usage |= D3DUSAGE_RTPATCHES;
2480 vertex_usage |= D3DUSAGE_RTPATCHES;
2482 if (options & D3DXMESH_NPATCHES) {
2483 index_usage |= D3DUSAGE_NPATCHES;
2484 vertex_usage |= D3DUSAGE_NPATCHES;
2487 if (options & D3DXMESH_VB_SYSTEMMEM)
2488 vertex_pool = D3DPOOL_SYSTEMMEM;
2489 else if (options & D3DXMESH_VB_MANAGED)
2490 vertex_pool = D3DPOOL_MANAGED;
2492 if (options & D3DXMESH_VB_WRITEONLY)
2493 vertex_usage |= D3DUSAGE_WRITEONLY;
2494 if (options & D3DXMESH_VB_DYNAMIC)
2495 vertex_usage |= D3DUSAGE_DYNAMIC;
2496 if (options & D3DXMESH_VB_SOFTWAREPROCESSING)
2497 vertex_usage |= D3DUSAGE_SOFTWAREPROCESSING;
2499 if (options & D3DXMESH_IB_SYSTEMMEM)
2500 index_pool = D3DPOOL_SYSTEMMEM;
2501 else if (options & D3DXMESH_IB_MANAGED)
2502 index_pool = D3DPOOL_MANAGED;
2504 if (options & D3DXMESH_IB_WRITEONLY)
2505 index_usage |= D3DUSAGE_WRITEONLY;
2506 if (options & D3DXMESH_IB_DYNAMIC)
2507 index_usage |= D3DUSAGE_DYNAMIC;
2508 if (options & D3DXMESH_IB_SOFTWAREPROCESSING)
2509 index_usage |= D3DUSAGE_SOFTWAREPROCESSING;
2511 hr = D3DXFVFFromDeclarator(declaration, &fvf);
2512 if (hr != D3D_OK)
2514 fvf = 0;
2517 /* Create vertex declaration */
2518 hr = IDirect3DDevice9_CreateVertexDeclaration(device,
2519 declaration,
2520 &vertex_declaration);
2521 if (FAILED(hr))
2523 WARN("Unexpected return value %x from IDirect3DDevice9_CreateVertexDeclaration.\n",hr);
2524 return hr;
2526 vertex_declaration_size = D3DXGetDeclVertexSize(declaration, declaration[0].Stream);
2528 /* Create vertex buffer */
2529 hr = IDirect3DDevice9_CreateVertexBuffer(device,
2530 numvertices * vertex_declaration_size,
2531 vertex_usage,
2532 fvf,
2533 vertex_pool,
2534 &vertex_buffer,
2535 NULL);
2536 if (FAILED(hr))
2538 WARN("Unexpected return value %x from IDirect3DDevice9_CreateVertexBuffer.\n",hr);
2539 IDirect3DVertexDeclaration9_Release(vertex_declaration);
2540 return hr;
2543 /* Create index buffer */
2544 hr = IDirect3DDevice9_CreateIndexBuffer(device,
2545 numfaces * 3 * ((index_format == D3DFMT_INDEX16) ? 2 : 4),
2546 index_usage,
2547 index_format,
2548 index_pool,
2549 &index_buffer,
2550 NULL);
2551 if (FAILED(hr))
2553 WARN("Unexpected return value %x from IDirect3DDevice9_CreateVertexBuffer.\n",hr);
2554 IDirect3DVertexBuffer9_Release(vertex_buffer);
2555 IDirect3DVertexDeclaration9_Release(vertex_declaration);
2556 return hr;
2559 attrib_buffer = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, numfaces * sizeof(*attrib_buffer));
2560 object = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(*object));
2561 if (object == NULL || attrib_buffer == NULL)
2563 HeapFree(GetProcessHeap(), 0, object);
2564 HeapFree(GetProcessHeap(), 0, attrib_buffer);
2565 IDirect3DIndexBuffer9_Release(index_buffer);
2566 IDirect3DVertexBuffer9_Release(vertex_buffer);
2567 IDirect3DVertexDeclaration9_Release(vertex_declaration);
2568 *mesh = NULL;
2569 return E_OUTOFMEMORY;
2571 object->ID3DXMesh_iface.lpVtbl = &D3DXMesh_Vtbl;
2572 object->ref = 1;
2574 object->numfaces = numfaces;
2575 object->numvertices = numvertices;
2576 object->options = options;
2577 object->fvf = fvf;
2578 object->device = device;
2579 IDirect3DDevice9_AddRef(device);
2581 copy_declaration(object->cached_declaration, declaration, num_elem);
2582 object->vertex_declaration = vertex_declaration;
2583 object->vertex_declaration_size = vertex_declaration_size;
2584 object->num_elem = num_elem;
2585 object->vertex_buffer = vertex_buffer;
2586 object->index_buffer = index_buffer;
2587 object->attrib_buffer = attrib_buffer;
2589 *mesh = &object->ID3DXMesh_iface;
2591 return D3D_OK;
2594 /*************************************************************************
2595 * D3DXCreateMeshFVF
2597 HRESULT WINAPI D3DXCreateMeshFVF(DWORD numfaces, DWORD numvertices, DWORD options,
2598 DWORD fvf, struct IDirect3DDevice9 *device, struct ID3DXMesh **mesh)
2600 HRESULT hr;
2601 D3DVERTEXELEMENT9 declaration[MAX_FVF_DECL_SIZE];
2603 TRACE("(%u, %u, %u, %u, %p, %p)\n", numfaces, numvertices, options, fvf, device, mesh);
2605 hr = D3DXDeclaratorFromFVF(fvf, declaration);
2606 if (FAILED(hr)) return hr;
2608 return D3DXCreateMesh(numfaces, numvertices, options, declaration, device, mesh);
2612 struct mesh_data {
2613 DWORD num_vertices;
2614 DWORD num_poly_faces;
2615 DWORD num_tri_faces;
2616 D3DXVECTOR3 *vertices;
2617 DWORD *num_tri_per_face;
2618 DWORD *indices;
2620 DWORD fvf;
2622 /* optional mesh data */
2624 DWORD num_normals;
2625 D3DXVECTOR3 *normals;
2626 DWORD *normal_indices;
2628 D3DXVECTOR2 *tex_coords;
2630 DWORD *vertex_colors;
2632 DWORD num_materials;
2633 D3DXMATERIAL *materials;
2634 DWORD *material_indices;
2636 struct ID3DXSkinInfo *skin_info;
2637 DWORD nb_bones;
2640 static HRESULT parse_texture_filename(ID3DXFileData *filedata, char **filename_out)
2642 HRESULT hr;
2643 SIZE_T data_size;
2644 BYTE *data;
2645 char *filename_in;
2646 char *filename = NULL;
2648 /* template TextureFilename {
2649 * STRING filename;
2653 HeapFree(GetProcessHeap(), 0, *filename_out);
2654 *filename_out = NULL;
2656 hr = filedata->lpVtbl->Lock(filedata, &data_size, (const void**)&data);
2657 if (FAILED(hr)) return hr;
2659 /* FIXME: String must be retrieved directly instead of through a pointer once ID3DXFILE is fixed */
2660 if (data_size < sizeof(filename_in))
2662 WARN("truncated data (%lu bytes)\n", data_size);
2663 filedata->lpVtbl->Unlock(filedata);
2664 return E_FAIL;
2666 filename_in = *(char **)data;
2668 filename = HeapAlloc(GetProcessHeap(), 0, strlen(filename_in) + 1);
2669 if (!filename) {
2670 filedata->lpVtbl->Unlock(filedata);
2671 return E_OUTOFMEMORY;
2674 strcpy(filename, filename_in);
2675 *filename_out = filename;
2677 filedata->lpVtbl->Unlock(filedata);
2679 return D3D_OK;
2682 static HRESULT parse_material(ID3DXFileData *filedata, D3DXMATERIAL *material)
2684 HRESULT hr;
2685 SIZE_T data_size;
2686 const BYTE *data;
2687 GUID type;
2688 ID3DXFileData *child;
2689 SIZE_T nb_children;
2690 int i;
2692 material->pTextureFilename = NULL;
2694 hr = filedata->lpVtbl->Lock(filedata, &data_size, (const void**)&data);
2695 if (FAILED(hr)) return hr;
2698 * template ColorRGBA {
2699 * FLOAT red;
2700 * FLOAT green;
2701 * FLOAT blue;
2702 * FLOAT alpha;
2704 * template ColorRGB {
2705 * FLOAT red;
2706 * FLOAT green;
2707 * FLOAT blue;
2709 * template Material {
2710 * ColorRGBA faceColor;
2711 * FLOAT power;
2712 * ColorRGB specularColor;
2713 * ColorRGB emissiveColor;
2714 * [ ... ]
2717 if (data_size != sizeof(FLOAT) * 11) {
2718 WARN("incorrect data size (%ld bytes)\n", data_size);
2719 filedata->lpVtbl->Unlock(filedata);
2720 return E_FAIL;
2723 memcpy(&material->MatD3D.Diffuse, data, sizeof(D3DCOLORVALUE));
2724 data += sizeof(D3DCOLORVALUE);
2725 material->MatD3D.Power = *(FLOAT*)data;
2726 data += sizeof(FLOAT);
2727 memcpy(&material->MatD3D.Specular, data, sizeof(FLOAT) * 3);
2728 material->MatD3D.Specular.a = 1.0f;
2729 data += 3 * sizeof(FLOAT);
2730 memcpy(&material->MatD3D.Emissive, data, sizeof(FLOAT) * 3);
2731 material->MatD3D.Emissive.a = 1.0f;
2732 material->MatD3D.Ambient.r = 0.0f;
2733 material->MatD3D.Ambient.g = 0.0f;
2734 material->MatD3D.Ambient.b = 0.0f;
2735 material->MatD3D.Ambient.a = 1.0f;
2737 filedata->lpVtbl->Unlock(filedata);
2739 hr = filedata->lpVtbl->GetChildren(filedata, &nb_children);
2740 if (FAILED(hr))
2741 return hr;
2743 for (i = 0; i < nb_children; i++)
2745 hr = filedata->lpVtbl->GetChild(filedata, i, &child);
2746 if (FAILED(hr))
2747 return hr;
2748 hr = child->lpVtbl->GetType(child, &type);
2749 if (FAILED(hr))
2750 return hr;
2752 if (IsEqualGUID(&type, &TID_D3DRMTextureFilename)) {
2753 hr = parse_texture_filename(child, &material->pTextureFilename);
2754 if (FAILED(hr))
2755 return hr;
2759 return D3D_OK;
2762 static void destroy_materials(struct mesh_data *mesh)
2764 DWORD i;
2765 for (i = 0; i < mesh->num_materials; i++)
2766 HeapFree(GetProcessHeap(), 0, mesh->materials[i].pTextureFilename);
2767 HeapFree(GetProcessHeap(), 0, mesh->materials);
2768 HeapFree(GetProcessHeap(), 0, mesh->material_indices);
2769 mesh->num_materials = 0;
2770 mesh->materials = NULL;
2771 mesh->material_indices = NULL;
2774 static HRESULT parse_material_list(ID3DXFileData *filedata, struct mesh_data *mesh)
2776 HRESULT hr;
2777 SIZE_T data_size;
2778 const DWORD *data, *in_ptr;
2779 GUID type;
2780 ID3DXFileData *child;
2781 DWORD num_materials;
2782 DWORD i;
2783 SIZE_T nb_children;
2785 destroy_materials(mesh);
2787 hr = filedata->lpVtbl->Lock(filedata, &data_size, (const void**)&data);
2788 if (FAILED(hr)) return hr;
2790 /* template MeshMaterialList {
2791 * DWORD nMaterials;
2792 * DWORD nFaceIndexes;
2793 * array DWORD faceIndexes[nFaceIndexes];
2794 * [ Material ]
2798 in_ptr = data;
2799 hr = E_FAIL;
2801 if (data_size < sizeof(DWORD)) {
2802 WARN("truncated data (%ld bytes)\n", data_size);
2803 goto end;
2805 num_materials = *in_ptr++;
2806 if (!num_materials) {
2807 hr = D3D_OK;
2808 goto end;
2811 if (data_size < 2 * sizeof(DWORD)) {
2812 WARN("truncated data (%ld bytes)\n", data_size);
2813 goto end;
2815 if (*in_ptr++ != mesh->num_poly_faces) {
2816 WARN("number of material face indices (%u) doesn't match number of faces (%u)\n",
2817 *(in_ptr - 1), mesh->num_poly_faces);
2818 goto end;
2820 if (data_size < 2 * sizeof(DWORD) + mesh->num_poly_faces * sizeof(DWORD)) {
2821 WARN("truncated data (%ld bytes)\n", data_size);
2822 goto end;
2824 for (i = 0; i < mesh->num_poly_faces; i++) {
2825 if (*in_ptr++ >= num_materials) {
2826 WARN("face %u: reference to undefined material %u (only %u materials)\n",
2827 i, *(in_ptr - 1), num_materials);
2828 goto end;
2832 mesh->materials = HeapAlloc(GetProcessHeap(), 0, num_materials * sizeof(*mesh->materials));
2833 mesh->material_indices = HeapAlloc(GetProcessHeap(), 0, mesh->num_poly_faces * sizeof(*mesh->material_indices));
2834 if (!mesh->materials || !mesh->material_indices) {
2835 hr = E_OUTOFMEMORY;
2836 goto end;
2838 memcpy(mesh->material_indices, data + 2, mesh->num_poly_faces * sizeof(DWORD));
2840 hr = filedata->lpVtbl->GetChildren(filedata, &nb_children);
2841 if (FAILED(hr))
2842 goto end;
2844 for (i = 0; i < nb_children; i++)
2846 hr = filedata->lpVtbl->GetChild(filedata, i, &child);
2847 if (FAILED(hr))
2848 goto end;
2849 hr = child->lpVtbl->GetType(child, &type);
2850 if (FAILED(hr))
2851 goto end;
2853 if (IsEqualGUID(&type, &TID_D3DRMMaterial)) {
2854 if (mesh->num_materials >= num_materials) {
2855 WARN("more materials defined than declared\n");
2856 hr = E_FAIL;
2857 goto end;
2859 hr = parse_material(child, &mesh->materials[mesh->num_materials++]);
2860 if (FAILED(hr))
2861 goto end;
2864 if (num_materials != mesh->num_materials) {
2865 WARN("only %u of %u materials defined\n", num_materials, mesh->num_materials);
2866 hr = E_FAIL;
2869 end:
2870 filedata->lpVtbl->Unlock(filedata);
2871 return hr;
2874 static HRESULT parse_texture_coords(ID3DXFileData *filedata, struct mesh_data *mesh)
2876 HRESULT hr;
2877 SIZE_T data_size;
2878 const BYTE *data;
2880 HeapFree(GetProcessHeap(), 0, mesh->tex_coords);
2881 mesh->tex_coords = NULL;
2883 hr = filedata->lpVtbl->Lock(filedata, &data_size, (const void**)&data);
2884 if (FAILED(hr)) return hr;
2886 /* template Coords2d {
2887 * FLOAT u;
2888 * FLOAT v;
2890 * template MeshTextureCoords {
2891 * DWORD nTextureCoords;
2892 * array Coords2d textureCoords[nTextureCoords];
2896 hr = E_FAIL;
2898 if (data_size < sizeof(DWORD)) {
2899 WARN("truncated data (%ld bytes)\n", data_size);
2900 goto end;
2902 if (*(DWORD*)data != mesh->num_vertices) {
2903 WARN("number of texture coordinates (%u) doesn't match number of vertices (%u)\n",
2904 *(DWORD*)data, mesh->num_vertices);
2905 goto end;
2907 data += sizeof(DWORD);
2908 if (data_size < sizeof(DWORD) + mesh->num_vertices * sizeof(*mesh->tex_coords)) {
2909 WARN("truncated data (%ld bytes)\n", data_size);
2910 goto end;
2913 mesh->tex_coords = HeapAlloc(GetProcessHeap(), 0, mesh->num_vertices * sizeof(*mesh->tex_coords));
2914 if (!mesh->tex_coords) {
2915 hr = E_OUTOFMEMORY;
2916 goto end;
2918 memcpy(mesh->tex_coords, data, mesh->num_vertices * sizeof(*mesh->tex_coords));
2920 mesh->fvf |= D3DFVF_TEX1;
2922 hr = D3D_OK;
2924 end:
2925 filedata->lpVtbl->Unlock(filedata);
2926 return hr;
2929 static HRESULT parse_vertex_colors(ID3DXFileData *filedata, struct mesh_data *mesh)
2931 HRESULT hr;
2932 SIZE_T data_size;
2933 const BYTE *data;
2934 DWORD num_colors;
2935 DWORD i;
2937 HeapFree(GetProcessHeap(), 0, mesh->vertex_colors);
2938 mesh->vertex_colors = NULL;
2940 hr = filedata->lpVtbl->Lock(filedata, &data_size, (const void**)&data);
2941 if (FAILED(hr)) return hr;
2943 /* template IndexedColor {
2944 * DWORD index;
2945 * ColorRGBA indexColor;
2947 * template MeshVertexColors {
2948 * DWORD nVertexColors;
2949 * array IndexedColor vertexColors[nVertexColors];
2953 hr = E_FAIL;
2955 if (data_size < sizeof(DWORD)) {
2956 WARN("truncated data (%ld bytes)\n", data_size);
2957 goto end;
2959 num_colors = *(DWORD*)data;
2960 data += sizeof(DWORD);
2961 if (data_size < sizeof(DWORD) + num_colors * (sizeof(DWORD) + sizeof(D3DCOLORVALUE))) {
2962 WARN("truncated data (%ld bytes)\n", data_size);
2963 goto end;
2966 mesh->vertex_colors = HeapAlloc(GetProcessHeap(), 0, mesh->num_vertices * sizeof(DWORD));
2967 if (!mesh->vertex_colors) {
2968 hr = E_OUTOFMEMORY;
2969 goto end;
2972 for (i = 0; i < mesh->num_vertices; i++)
2973 mesh->vertex_colors[i] = D3DCOLOR_ARGB(0, 0xff, 0xff, 0xff);
2974 for (i = 0; i < num_colors; i++)
2976 D3DCOLORVALUE color;
2977 DWORD index = *(DWORD*)data;
2978 data += sizeof(DWORD);
2979 if (index >= mesh->num_vertices) {
2980 WARN("vertex color %u references undefined vertex %u (only %u vertices)\n",
2981 i, index, mesh->num_vertices);
2982 goto end;
2984 memcpy(&color, data, sizeof(color));
2985 data += sizeof(color);
2986 color.r = min(1.0f, max(0.0f, color.r));
2987 color.g = min(1.0f, max(0.0f, color.g));
2988 color.b = min(1.0f, max(0.0f, color.b));
2989 color.a = min(1.0f, max(0.0f, color.a));
2990 mesh->vertex_colors[index] = D3DCOLOR_ARGB((BYTE)(color.a * 255.0f + 0.5f),
2991 (BYTE)(color.r * 255.0f + 0.5f),
2992 (BYTE)(color.g * 255.0f + 0.5f),
2993 (BYTE)(color.b * 255.0f + 0.5f));
2996 mesh->fvf |= D3DFVF_DIFFUSE;
2998 hr = D3D_OK;
3000 end:
3001 filedata->lpVtbl->Unlock(filedata);
3002 return hr;
3005 static HRESULT parse_normals(ID3DXFileData *filedata, struct mesh_data *mesh)
3007 HRESULT hr;
3008 SIZE_T data_size;
3009 const BYTE *data;
3010 DWORD *index_out_ptr;
3011 DWORD i;
3012 DWORD num_face_indices = mesh->num_poly_faces * 2 + mesh->num_tri_faces;
3014 HeapFree(GetProcessHeap(), 0, mesh->normals);
3015 mesh->num_normals = 0;
3016 mesh->normals = NULL;
3017 mesh->normal_indices = NULL;
3018 mesh->fvf |= D3DFVF_NORMAL;
3020 hr = filedata->lpVtbl->Lock(filedata, &data_size, (const void**)&data);
3021 if (FAILED(hr)) return hr;
3023 /* template Vector {
3024 * FLOAT x;
3025 * FLOAT y;
3026 * FLOAT z;
3028 * template MeshFace {
3029 * DWORD nFaceVertexIndices;
3030 * array DWORD faceVertexIndices[nFaceVertexIndices];
3032 * template MeshNormals {
3033 * DWORD nNormals;
3034 * array Vector normals[nNormals];
3035 * DWORD nFaceNormals;
3036 * array MeshFace faceNormals[nFaceNormals];
3040 hr = E_FAIL;
3042 if (data_size < sizeof(DWORD) * 2) {
3043 WARN("truncated data (%ld bytes)\n", data_size);
3044 goto end;
3046 mesh->num_normals = *(DWORD*)data;
3047 data += sizeof(DWORD);
3048 if (data_size < sizeof(DWORD) * 2 + mesh->num_normals * sizeof(D3DXVECTOR3) +
3049 num_face_indices * sizeof(DWORD)) {
3050 WARN("truncated data (%ld bytes)\n", data_size);
3051 goto end;
3054 mesh->normals = HeapAlloc(GetProcessHeap(), 0, mesh->num_normals * sizeof(D3DXVECTOR3));
3055 mesh->normal_indices = HeapAlloc(GetProcessHeap(), 0, num_face_indices * sizeof(DWORD));
3056 if (!mesh->normals || !mesh->normal_indices) {
3057 hr = E_OUTOFMEMORY;
3058 goto end;
3061 memcpy(mesh->normals, data, mesh->num_normals * sizeof(D3DXVECTOR3));
3062 data += mesh->num_normals * sizeof(D3DXVECTOR3);
3063 for (i = 0; i < mesh->num_normals; i++)
3064 D3DXVec3Normalize(&mesh->normals[i], &mesh->normals[i]);
3066 if (*(DWORD*)data != mesh->num_poly_faces) {
3067 WARN("number of face normals (%u) doesn't match number of faces (%u)\n",
3068 *(DWORD*)data, mesh->num_poly_faces);
3069 goto end;
3071 data += sizeof(DWORD);
3072 index_out_ptr = mesh->normal_indices;
3073 for (i = 0; i < mesh->num_poly_faces; i++)
3075 DWORD j;
3076 DWORD count = *(DWORD*)data;
3077 if (count != mesh->num_tri_per_face[i] + 2) {
3078 WARN("face %u: number of normals (%u) doesn't match number of vertices (%u)\n",
3079 i, count, mesh->num_tri_per_face[i] + 2);
3080 goto end;
3082 data += sizeof(DWORD);
3084 for (j = 0; j < count; j++) {
3085 DWORD normal_index = *(DWORD*)data;
3086 if (normal_index >= mesh->num_normals) {
3087 WARN("face %u, normal index %u: reference to undefined normal %u (only %u normals)\n",
3088 i, j, normal_index, mesh->num_normals);
3089 goto end;
3091 *index_out_ptr++ = normal_index;
3092 data += sizeof(DWORD);
3096 hr = D3D_OK;
3098 end:
3099 filedata->lpVtbl->Unlock(filedata);
3100 return hr;
3103 static HRESULT parse_skin_mesh_info(ID3DXFileData *filedata, struct mesh_data *mesh_data, DWORD index)
3105 HRESULT hr;
3106 SIZE_T data_size;
3107 const BYTE *data;
3109 TRACE("(%p, %p, %u)\n", filedata, mesh_data, index);
3111 hr = filedata->lpVtbl->Lock(filedata, &data_size, (const void**)&data);
3112 if (FAILED(hr)) return hr;
3114 hr = E_FAIL;
3116 if (!mesh_data->skin_info) {
3117 if (data_size < sizeof(WORD) * 3) {
3118 WARN("truncated data (%ld bytes)\n", data_size);
3119 goto end;
3121 /* Skip nMaxSkinWeightsPerVertex and nMaxSkinWeightsPerFace */
3122 data += 2 * sizeof(WORD);
3123 mesh_data->nb_bones = *(WORD*)data;
3124 hr = D3DXCreateSkinInfoFVF(mesh_data->num_vertices, mesh_data->fvf, mesh_data->nb_bones, &mesh_data->skin_info);
3125 } else {
3126 const char *name;
3127 DWORD nb_influences;
3129 /* FIXME: String must be retrieved directly instead of through a pointer once ID3DXFILE is fixed */
3130 name = *(const char**)data;
3131 data += sizeof(char*);
3133 nb_influences = *(DWORD*)data;
3134 data += sizeof(DWORD);
3136 if (data_size < (sizeof(char*) + sizeof(DWORD) + nb_influences * (sizeof(DWORD) + sizeof(FLOAT)) + 16 * sizeof(FLOAT))) {
3137 WARN("truncated data (%ld bytes)\n", data_size);
3138 goto end;
3141 hr = mesh_data->skin_info->lpVtbl->SetBoneName(mesh_data->skin_info, index, name);
3142 if (SUCCEEDED(hr))
3143 hr = mesh_data->skin_info->lpVtbl->SetBoneInfluence(mesh_data->skin_info, index, nb_influences,
3144 (const DWORD*)data, (const FLOAT*)(data + nb_influences * sizeof(DWORD)));
3145 if (SUCCEEDED(hr))
3146 hr = mesh_data->skin_info->lpVtbl->SetBoneOffsetMatrix(mesh_data->skin_info, index,
3147 (const D3DMATRIX*)(data + nb_influences * (sizeof(DWORD) + sizeof(FLOAT))));
3150 end:
3151 filedata->lpVtbl->Unlock(filedata);
3152 return hr;
3155 /* for provide_flags parameters */
3156 #define PROVIDE_MATERIALS 0x1
3157 #define PROVIDE_SKININFO 0x2
3158 #define PROVIDE_ADJACENCY 0x4
3160 static HRESULT parse_mesh(ID3DXFileData *filedata, struct mesh_data *mesh_data, DWORD provide_flags)
3162 HRESULT hr;
3163 SIZE_T data_size;
3164 const BYTE *data, *in_ptr;
3165 DWORD *index_out_ptr;
3166 GUID type;
3167 ID3DXFileData *child;
3168 DWORD i;
3169 SIZE_T nb_children;
3170 DWORD nb_skin_weigths_info = 0;
3173 * template Mesh {
3174 * DWORD nVertices;
3175 * array Vector vertices[nVertices];
3176 * DWORD nFaces;
3177 * array MeshFace faces[nFaces];
3178 * [ ... ]
3182 hr = filedata->lpVtbl->Lock(filedata, &data_size, (const void**)&data);
3183 if (FAILED(hr)) return hr;
3185 in_ptr = data;
3186 hr = E_FAIL;
3188 if (data_size < sizeof(DWORD) * 2) {
3189 WARN("truncated data (%ld bytes)\n", data_size);
3190 goto end;
3192 mesh_data->num_vertices = *(DWORD*)in_ptr;
3193 if (data_size < sizeof(DWORD) * 2 + mesh_data->num_vertices * sizeof(D3DXVECTOR3)) {
3194 WARN("truncated data (%ld bytes)\n", data_size);
3195 goto end;
3197 in_ptr += sizeof(DWORD) + mesh_data->num_vertices * sizeof(D3DXVECTOR3);
3199 mesh_data->num_poly_faces = *(DWORD*)in_ptr;
3200 in_ptr += sizeof(DWORD);
3202 mesh_data->num_tri_faces = 0;
3203 for (i = 0; i < mesh_data->num_poly_faces; i++)
3205 DWORD num_poly_vertices;
3206 DWORD j;
3208 if (data_size - (in_ptr - data) < sizeof(DWORD)) {
3209 WARN("truncated data (%ld bytes)\n", data_size);
3210 goto end;
3212 num_poly_vertices = *(DWORD*)in_ptr;
3213 in_ptr += sizeof(DWORD);
3214 if (data_size - (in_ptr - data) < num_poly_vertices * sizeof(DWORD)) {
3215 WARN("truncated data (%ld bytes)\n", data_size);
3216 goto end;
3218 if (num_poly_vertices < 3) {
3219 WARN("face %u has only %u vertices\n", i, num_poly_vertices);
3220 goto end;
3222 for (j = 0; j < num_poly_vertices; j++) {
3223 if (*(DWORD*)in_ptr >= mesh_data->num_vertices) {
3224 WARN("face %u, index %u: undefined vertex %u (only %u vertices)\n",
3225 i, j, *(DWORD*)in_ptr, mesh_data->num_vertices);
3226 goto end;
3228 in_ptr += sizeof(DWORD);
3230 mesh_data->num_tri_faces += num_poly_vertices - 2;
3233 mesh_data->fvf = D3DFVF_XYZ;
3235 mesh_data->vertices = HeapAlloc(GetProcessHeap(), 0,
3236 mesh_data->num_vertices * sizeof(*mesh_data->vertices));
3237 mesh_data->num_tri_per_face = HeapAlloc(GetProcessHeap(), 0,
3238 mesh_data->num_poly_faces * sizeof(*mesh_data->num_tri_per_face));
3239 mesh_data->indices = HeapAlloc(GetProcessHeap(), 0,
3240 (mesh_data->num_tri_faces + mesh_data->num_poly_faces * 2) * sizeof(*mesh_data->indices));
3241 if (!mesh_data->vertices || !mesh_data->num_tri_per_face || !mesh_data->indices) {
3242 hr = E_OUTOFMEMORY;
3243 goto end;
3246 in_ptr = data + sizeof(DWORD);
3247 memcpy(mesh_data->vertices, in_ptr, mesh_data->num_vertices * sizeof(D3DXVECTOR3));
3248 in_ptr += mesh_data->num_vertices * sizeof(D3DXVECTOR3) + sizeof(DWORD);
3250 index_out_ptr = mesh_data->indices;
3251 for (i = 0; i < mesh_data->num_poly_faces; i++)
3253 DWORD count;
3255 count = *(DWORD*)in_ptr;
3256 in_ptr += sizeof(DWORD);
3257 mesh_data->num_tri_per_face[i] = count - 2;
3259 while (count--) {
3260 *index_out_ptr++ = *(DWORD*)in_ptr;
3261 in_ptr += sizeof(DWORD);
3265 hr = filedata->lpVtbl->GetChildren(filedata, &nb_children);
3266 if (FAILED(hr))
3267 goto end;
3269 for (i = 0; i < nb_children; i++)
3271 hr = filedata->lpVtbl->GetChild(filedata, i, &child);
3272 if (FAILED(hr))
3273 goto end;
3274 hr = child->lpVtbl->GetType(child, &type);
3275 if (FAILED(hr))
3276 goto end;
3278 if (IsEqualGUID(&type, &TID_D3DRMMeshNormals)) {
3279 hr = parse_normals(child, mesh_data);
3280 } else if (IsEqualGUID(&type, &TID_D3DRMMeshVertexColors)) {
3281 hr = parse_vertex_colors(child, mesh_data);
3282 } else if (IsEqualGUID(&type, &TID_D3DRMMeshTextureCoords)) {
3283 hr = parse_texture_coords(child, mesh_data);
3284 hr = filedata->lpVtbl->GetChild(filedata, i, &child);
3285 if (FAILED(hr))
3286 goto end;
3287 } else if (IsEqualGUID(&type, &TID_D3DRMMeshMaterialList) &&
3288 (provide_flags & PROVIDE_MATERIALS))
3290 hr = parse_material_list(child, mesh_data);
3291 } else if (provide_flags & PROVIDE_SKININFO) {
3292 if (IsEqualGUID(&type, &DXFILEOBJ_XSkinMeshHeader)) {
3293 if (mesh_data->skin_info) {
3294 WARN("Skin mesh header already encountered\n");
3295 hr = E_FAIL;
3296 goto end;
3298 hr = parse_skin_mesh_info(child, mesh_data, 0);
3299 if (FAILED(hr))
3300 goto end;
3301 } else if (IsEqualGUID(&type, &DXFILEOBJ_SkinWeights)) {
3302 if (!mesh_data->skin_info) {
3303 WARN("Skin weigths found but skin mesh header not encountered yet\n");
3304 hr = E_FAIL;
3305 goto end;
3307 hr = parse_skin_mesh_info(child, mesh_data, nb_skin_weigths_info);
3308 if (FAILED(hr))
3309 goto end;
3310 nb_skin_weigths_info++;
3313 if (FAILED(hr))
3314 goto end;
3317 if (mesh_data->skin_info && (nb_skin_weigths_info != mesh_data->nb_bones)) {
3318 WARN("Mismatch between nb skin weights info %u encountered and nb bones %u from skin mesh header\n",
3319 nb_skin_weigths_info, mesh_data->nb_bones);
3320 hr = E_FAIL;
3321 goto end;
3324 hr = D3D_OK;
3326 end:
3327 filedata->lpVtbl->Unlock(filedata);
3328 return hr;
3331 static HRESULT generate_effects(ID3DXBuffer *materials, DWORD num_materials,
3332 ID3DXBuffer **effects)
3334 HRESULT hr;
3335 D3DXEFFECTINSTANCE *effect_ptr;
3336 BYTE *out_ptr;
3337 const D3DXMATERIAL *material_ptr = ID3DXBuffer_GetBufferPointer(materials);
3338 static const struct {
3339 const char *param_name;
3340 DWORD name_size;
3341 DWORD num_bytes;
3342 DWORD value_offset;
3343 } material_effects[] = {
3344 #define EFFECT_TABLE_ENTRY(str, field) \
3345 {str, sizeof(str), sizeof(material_ptr->MatD3D.field), offsetof(D3DXMATERIAL, MatD3D.field)}
3346 EFFECT_TABLE_ENTRY("Diffuse", Diffuse),
3347 EFFECT_TABLE_ENTRY("Power", Power),
3348 EFFECT_TABLE_ENTRY("Specular", Specular),
3349 EFFECT_TABLE_ENTRY("Emissive", Emissive),
3350 EFFECT_TABLE_ENTRY("Ambient", Ambient),
3351 #undef EFFECT_TABLE_ENTRY
3353 static const char texture_paramname[] = "Texture0@Name";
3354 DWORD buffer_size;
3355 DWORD i;
3357 /* effects buffer layout:
3359 * D3DXEFFECTINSTANCE effects[num_materials];
3360 * for (effect in effects)
3362 * D3DXEFFECTDEFAULT defaults[effect.NumDefaults];
3363 * for (default in defaults)
3365 * *default.pParamName;
3366 * *default.pValue;
3370 buffer_size = sizeof(D3DXEFFECTINSTANCE);
3371 buffer_size += sizeof(D3DXEFFECTDEFAULT) * ARRAY_SIZE(material_effects);
3372 for (i = 0; i < ARRAY_SIZE(material_effects); i++) {
3373 buffer_size += material_effects[i].name_size;
3374 buffer_size += material_effects[i].num_bytes;
3376 buffer_size *= num_materials;
3377 for (i = 0; i < num_materials; i++) {
3378 if (material_ptr[i].pTextureFilename) {
3379 buffer_size += sizeof(D3DXEFFECTDEFAULT);
3380 buffer_size += sizeof(texture_paramname);
3381 buffer_size += strlen(material_ptr[i].pTextureFilename) + 1;
3385 hr = D3DXCreateBuffer(buffer_size, effects);
3386 if (FAILED(hr)) return hr;
3387 effect_ptr = ID3DXBuffer_GetBufferPointer(*effects);
3388 out_ptr = (BYTE*)(effect_ptr + num_materials);
3390 for (i = 0; i < num_materials; i++)
3392 DWORD j;
3393 D3DXEFFECTDEFAULT *defaults = (D3DXEFFECTDEFAULT*)out_ptr;
3395 effect_ptr->pDefaults = defaults;
3396 effect_ptr->NumDefaults = material_ptr->pTextureFilename ? 6 : 5;
3397 out_ptr = (BYTE*)(effect_ptr->pDefaults + effect_ptr->NumDefaults);
3399 for (j = 0; j < ARRAY_SIZE(material_effects); j++)
3401 defaults->pParamName = (char *)out_ptr;
3402 strcpy(defaults->pParamName, material_effects[j].param_name);
3403 defaults->pValue = defaults->pParamName + material_effects[j].name_size;
3404 defaults->Type = D3DXEDT_FLOATS;
3405 defaults->NumBytes = material_effects[j].num_bytes;
3406 memcpy(defaults->pValue, (BYTE*)material_ptr + material_effects[j].value_offset, defaults->NumBytes);
3407 out_ptr = (BYTE*)defaults->pValue + defaults->NumBytes;
3408 defaults++;
3411 if (material_ptr->pTextureFilename)
3413 defaults->pParamName = (char *)out_ptr;
3414 strcpy(defaults->pParamName, texture_paramname);
3415 defaults->pValue = defaults->pParamName + sizeof(texture_paramname);
3416 defaults->Type = D3DXEDT_STRING;
3417 defaults->NumBytes = strlen(material_ptr->pTextureFilename) + 1;
3418 strcpy(defaults->pValue, material_ptr->pTextureFilename);
3419 out_ptr = (BYTE*)defaults->pValue + defaults->NumBytes;
3421 material_ptr++;
3422 effect_ptr++;
3424 assert(out_ptr - (BYTE*)ID3DXBuffer_GetBufferPointer(*effects) == buffer_size);
3426 return D3D_OK;
3429 HRESULT WINAPI D3DXLoadSkinMeshFromXof(struct ID3DXFileData *filedata, DWORD options,
3430 struct IDirect3DDevice9 *device, struct ID3DXBuffer **adjacency_out, struct ID3DXBuffer **materials_out,
3431 struct ID3DXBuffer **effects_out, DWORD *num_materials_out, struct ID3DXSkinInfo **skin_info_out,
3432 struct ID3DXMesh **mesh_out)
3434 HRESULT hr;
3435 DWORD *index_in_ptr;
3436 struct mesh_data mesh_data;
3437 DWORD total_vertices;
3438 ID3DXMesh *d3dxmesh = NULL;
3439 ID3DXBuffer *adjacency = NULL;
3440 ID3DXBuffer *materials = NULL;
3441 ID3DXBuffer *effects = NULL;
3442 struct vertex_duplication {
3443 DWORD normal_index;
3444 struct list entry;
3445 } *duplications = NULL;
3446 DWORD i;
3447 void *vertices = NULL;
3448 void *indices = NULL;
3449 BYTE *out_ptr;
3450 DWORD provide_flags = 0;
3452 TRACE("(%p, %x, %p, %p, %p, %p, %p, %p, %p)\n", filedata, options, device, adjacency_out, materials_out,
3453 effects_out, num_materials_out, skin_info_out, mesh_out);
3455 ZeroMemory(&mesh_data, sizeof(mesh_data));
3457 if (num_materials_out || materials_out || effects_out)
3458 provide_flags |= PROVIDE_MATERIALS;
3459 if (skin_info_out)
3460 provide_flags |= PROVIDE_SKININFO;
3462 hr = parse_mesh(filedata, &mesh_data, provide_flags);
3463 if (FAILED(hr)) goto cleanup;
3465 total_vertices = mesh_data.num_vertices;
3466 if (mesh_data.fvf & D3DFVF_NORMAL) {
3467 /* duplicate vertices with multiple normals */
3468 DWORD num_face_indices = mesh_data.num_poly_faces * 2 + mesh_data.num_tri_faces;
3469 duplications = HeapAlloc(GetProcessHeap(), 0, (mesh_data.num_vertices + num_face_indices) * sizeof(*duplications));
3470 if (!duplications) {
3471 hr = E_OUTOFMEMORY;
3472 goto cleanup;
3474 for (i = 0; i < total_vertices; i++)
3476 duplications[i].normal_index = -1;
3477 list_init(&duplications[i].entry);
3479 for (i = 0; i < num_face_indices; i++) {
3480 DWORD vertex_index = mesh_data.indices[i];
3481 DWORD normal_index = mesh_data.normal_indices[i];
3482 struct vertex_duplication *dup_ptr = &duplications[vertex_index];
3484 if (dup_ptr->normal_index == -1) {
3485 dup_ptr->normal_index = normal_index;
3486 } else {
3487 D3DXVECTOR3 *new_normal = &mesh_data.normals[normal_index];
3488 struct list *dup_list = &dup_ptr->entry;
3489 while (TRUE) {
3490 D3DXVECTOR3 *cur_normal = &mesh_data.normals[dup_ptr->normal_index];
3491 if (new_normal->x == cur_normal->x &&
3492 new_normal->y == cur_normal->y &&
3493 new_normal->z == cur_normal->z)
3495 mesh_data.indices[i] = dup_ptr - duplications;
3496 break;
3497 } else if (!list_next(dup_list, &dup_ptr->entry)) {
3498 dup_ptr = &duplications[total_vertices++];
3499 dup_ptr->normal_index = normal_index;
3500 list_add_tail(dup_list, &dup_ptr->entry);
3501 mesh_data.indices[i] = dup_ptr - duplications;
3502 break;
3503 } else {
3504 dup_ptr = LIST_ENTRY(list_next(dup_list, &dup_ptr->entry),
3505 struct vertex_duplication, entry);
3512 hr = D3DXCreateMeshFVF(mesh_data.num_tri_faces, total_vertices, options, mesh_data.fvf, device, &d3dxmesh);
3513 if (FAILED(hr)) goto cleanup;
3515 hr = d3dxmesh->lpVtbl->LockVertexBuffer(d3dxmesh, 0, &vertices);
3516 if (FAILED(hr)) goto cleanup;
3518 out_ptr = vertices;
3519 for (i = 0; i < mesh_data.num_vertices; i++) {
3520 *(D3DXVECTOR3*)out_ptr = mesh_data.vertices[i];
3521 out_ptr += sizeof(D3DXVECTOR3);
3522 if (mesh_data.fvf & D3DFVF_NORMAL) {
3523 if (duplications[i].normal_index == -1)
3524 ZeroMemory(out_ptr, sizeof(D3DXVECTOR3));
3525 else
3526 *(D3DXVECTOR3*)out_ptr = mesh_data.normals[duplications[i].normal_index];
3527 out_ptr += sizeof(D3DXVECTOR3);
3529 if (mesh_data.fvf & D3DFVF_DIFFUSE) {
3530 *(DWORD*)out_ptr = mesh_data.vertex_colors[i];
3531 out_ptr += sizeof(DWORD);
3533 if (mesh_data.fvf & D3DFVF_TEX1) {
3534 *(D3DXVECTOR2*)out_ptr = mesh_data.tex_coords[i];
3535 out_ptr += sizeof(D3DXVECTOR2);
3538 if (mesh_data.fvf & D3DFVF_NORMAL) {
3539 DWORD vertex_size = D3DXGetFVFVertexSize(mesh_data.fvf);
3540 out_ptr = vertices;
3541 for (i = 0; i < mesh_data.num_vertices; i++) {
3542 struct vertex_duplication *dup_ptr;
3543 LIST_FOR_EACH_ENTRY(dup_ptr, &duplications[i].entry, struct vertex_duplication, entry)
3545 int j = dup_ptr - duplications;
3546 BYTE *dest_vertex = (BYTE*)vertices + j * vertex_size;
3548 memcpy(dest_vertex, out_ptr, vertex_size);
3549 dest_vertex += sizeof(D3DXVECTOR3);
3550 *(D3DXVECTOR3*)dest_vertex = mesh_data.normals[dup_ptr->normal_index];
3552 out_ptr += vertex_size;
3555 d3dxmesh->lpVtbl->UnlockVertexBuffer(d3dxmesh);
3557 hr = d3dxmesh->lpVtbl->LockIndexBuffer(d3dxmesh, 0, &indices);
3558 if (FAILED(hr)) goto cleanup;
3560 index_in_ptr = mesh_data.indices;
3561 #define FILL_INDEX_BUFFER(indices_var) \
3562 for (i = 0; i < mesh_data.num_poly_faces; i++) \
3564 DWORD count = mesh_data.num_tri_per_face[i]; \
3565 WORD first_index = *index_in_ptr++; \
3566 while (count--) { \
3567 *indices_var++ = first_index; \
3568 *indices_var++ = *index_in_ptr; \
3569 index_in_ptr++; \
3570 *indices_var++ = *index_in_ptr; \
3572 index_in_ptr++; \
3574 if (options & D3DXMESH_32BIT) {
3575 DWORD *dword_indices = indices;
3576 FILL_INDEX_BUFFER(dword_indices)
3577 } else {
3578 WORD *word_indices = indices;
3579 FILL_INDEX_BUFFER(word_indices)
3581 #undef FILL_INDEX_BUFFER
3582 d3dxmesh->lpVtbl->UnlockIndexBuffer(d3dxmesh);
3584 if (mesh_data.material_indices) {
3585 DWORD *attrib_buffer = NULL;
3586 hr = d3dxmesh->lpVtbl->LockAttributeBuffer(d3dxmesh, 0, &attrib_buffer);
3587 if (FAILED(hr)) goto cleanup;
3588 for (i = 0; i < mesh_data.num_poly_faces; i++)
3590 DWORD count = mesh_data.num_tri_per_face[i];
3591 while (count--)
3592 *attrib_buffer++ = mesh_data.material_indices[i];
3594 d3dxmesh->lpVtbl->UnlockAttributeBuffer(d3dxmesh);
3596 hr = d3dxmesh->lpVtbl->OptimizeInplace(d3dxmesh,
3597 D3DXMESHOPT_ATTRSORT | D3DXMESHOPT_IGNOREVERTS | D3DXMESHOPT_DONOTSPLIT,
3598 NULL, NULL, NULL, NULL);
3599 if (FAILED(hr)) goto cleanup;
3602 if (mesh_data.num_materials && (materials_out || effects_out)) {
3603 DWORD buffer_size = mesh_data.num_materials * sizeof(D3DXMATERIAL);
3604 char *strings_out_ptr;
3605 D3DXMATERIAL *materials_ptr;
3607 for (i = 0; i < mesh_data.num_materials; i++) {
3608 if (mesh_data.materials[i].pTextureFilename)
3609 buffer_size += strlen(mesh_data.materials[i].pTextureFilename) + 1;
3612 hr = D3DXCreateBuffer(buffer_size, &materials);
3613 if (FAILED(hr)) goto cleanup;
3615 materials_ptr = ID3DXBuffer_GetBufferPointer(materials);
3616 memcpy(materials_ptr, mesh_data.materials, mesh_data.num_materials * sizeof(D3DXMATERIAL));
3617 strings_out_ptr = (char*)(materials_ptr + mesh_data.num_materials);
3618 for (i = 0; i < mesh_data.num_materials; i++) {
3619 if (materials_ptr[i].pTextureFilename) {
3620 strcpy(strings_out_ptr, mesh_data.materials[i].pTextureFilename);
3621 materials_ptr[i].pTextureFilename = strings_out_ptr;
3622 strings_out_ptr += strlen(mesh_data.materials[i].pTextureFilename) + 1;
3627 if (mesh_data.num_materials && effects_out) {
3628 hr = generate_effects(materials, mesh_data.num_materials, &effects);
3629 if (FAILED(hr)) goto cleanup;
3631 if (!materials_out) {
3632 ID3DXBuffer_Release(materials);
3633 materials = NULL;
3637 if (adjacency_out) {
3638 hr = D3DXCreateBuffer(mesh_data.num_tri_faces * 3 * sizeof(DWORD), &adjacency);
3639 if (FAILED(hr)) goto cleanup;
3640 hr = d3dxmesh->lpVtbl->GenerateAdjacency(d3dxmesh, 0.0f, ID3DXBuffer_GetBufferPointer(adjacency));
3641 if (FAILED(hr)) goto cleanup;
3644 *mesh_out = d3dxmesh;
3645 if (adjacency_out) *adjacency_out = adjacency;
3646 if (num_materials_out) *num_materials_out = mesh_data.num_materials;
3647 if (materials_out) *materials_out = materials;
3648 if (effects_out) *effects_out = effects;
3649 if (skin_info_out) *skin_info_out = mesh_data.skin_info;
3651 hr = D3D_OK;
3652 cleanup:
3653 if (FAILED(hr)) {
3654 if (d3dxmesh) IUnknown_Release(d3dxmesh);
3655 if (adjacency) ID3DXBuffer_Release(adjacency);
3656 if (materials) ID3DXBuffer_Release(materials);
3657 if (effects) ID3DXBuffer_Release(effects);
3658 if (mesh_data.skin_info) mesh_data.skin_info->lpVtbl->Release(mesh_data.skin_info);
3659 if (skin_info_out) *skin_info_out = NULL;
3661 HeapFree(GetProcessHeap(), 0, mesh_data.vertices);
3662 HeapFree(GetProcessHeap(), 0, mesh_data.num_tri_per_face);
3663 HeapFree(GetProcessHeap(), 0, mesh_data.indices);
3664 HeapFree(GetProcessHeap(), 0, mesh_data.normals);
3665 HeapFree(GetProcessHeap(), 0, mesh_data.normal_indices);
3666 destroy_materials(&mesh_data);
3667 HeapFree(GetProcessHeap(), 0, mesh_data.tex_coords);
3668 HeapFree(GetProcessHeap(), 0, mesh_data.vertex_colors);
3669 HeapFree(GetProcessHeap(), 0, duplications);
3670 return hr;
3673 HRESULT WINAPI D3DXLoadMeshHierarchyFromXA(const char *filename, DWORD options, struct IDirect3DDevice9 *device,
3674 struct ID3DXAllocateHierarchy *alloc_hier, struct ID3DXLoadUserData *load_user_data,
3675 D3DXFRAME **frame_hierarchy, struct ID3DXAnimationController **anim_controller)
3677 WCHAR *filenameW;
3678 HRESULT hr;
3679 int len;
3681 TRACE("filename %s, options %#x, device %p, alloc_hier %p, "
3682 "load_user_data %p, frame_hierarchy %p, anim_controller %p.\n",
3683 debugstr_a(filename), options, device, alloc_hier,
3684 load_user_data, frame_hierarchy, anim_controller);
3686 if (!filename)
3687 return D3DERR_INVALIDCALL;
3689 len = MultiByteToWideChar(CP_ACP, 0, filename, -1, NULL, 0);
3690 filenameW = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
3691 if (!filenameW) return E_OUTOFMEMORY;
3692 MultiByteToWideChar(CP_ACP, 0, filename, -1, filenameW, len);
3694 hr = D3DXLoadMeshHierarchyFromXW(filenameW, options, device,
3695 alloc_hier, load_user_data, frame_hierarchy, anim_controller);
3696 HeapFree(GetProcessHeap(), 0, filenameW);
3698 return hr;
3701 HRESULT WINAPI D3DXLoadMeshHierarchyFromXW(const WCHAR *filename, DWORD options, struct IDirect3DDevice9 *device,
3702 struct ID3DXAllocateHierarchy *alloc_hier, struct ID3DXLoadUserData *load_user_data,
3703 D3DXFRAME **frame_hierarchy, struct ID3DXAnimationController **anim_controller)
3705 void *buffer;
3706 HRESULT hr;
3707 DWORD size;
3709 TRACE("filename %s, options %#x, device %p, alloc_hier %p, "
3710 "load_user_data %p, frame_hierarchy %p, anim_controller %p.\n",
3711 debugstr_w(filename), options, device, alloc_hier,
3712 load_user_data, frame_hierarchy, anim_controller);
3714 if (!filename)
3715 return D3DERR_INVALIDCALL;
3717 hr = map_view_of_file(filename, &buffer, &size);
3718 if (FAILED(hr))
3719 return D3DXERR_INVALIDDATA;
3721 hr = D3DXLoadMeshHierarchyFromXInMemory(buffer, size, options, device,
3722 alloc_hier, load_user_data, frame_hierarchy, anim_controller);
3724 UnmapViewOfFile(buffer);
3726 return hr;
3729 static HRESULT filedata_get_name(ID3DXFileData *filedata, char **name)
3731 HRESULT hr;
3732 SIZE_T name_len;
3734 hr = filedata->lpVtbl->GetName(filedata, NULL, &name_len);
3735 if (FAILED(hr)) return hr;
3737 if (!name_len)
3738 name_len++;
3739 *name = HeapAlloc(GetProcessHeap(), 0, name_len);
3740 if (!*name) return E_OUTOFMEMORY;
3742 hr = filedata->lpVtbl->GetName(filedata, *name, &name_len);
3743 if (FAILED(hr))
3744 HeapFree(GetProcessHeap(), 0, *name);
3745 else if (!name_len)
3746 (*name)[0] = 0;
3748 return hr;
3751 static HRESULT load_mesh_container(struct ID3DXFileData *filedata, DWORD options, struct IDirect3DDevice9 *device,
3752 struct ID3DXAllocateHierarchy *alloc_hier, D3DXMESHCONTAINER **mesh_container)
3754 HRESULT hr;
3755 ID3DXBuffer *adjacency = NULL;
3756 ID3DXBuffer *materials = NULL;
3757 ID3DXBuffer *effects = NULL;
3758 ID3DXSkinInfo *skin_info = NULL;
3759 D3DXMESHDATA mesh_data;
3760 DWORD num_materials = 0;
3761 char *name = NULL;
3763 mesh_data.Type = D3DXMESHTYPE_MESH;
3764 mesh_data.u.pMesh = NULL;
3766 hr = D3DXLoadSkinMeshFromXof(filedata, options, device,
3767 &adjacency, &materials, &effects, &num_materials,
3768 &skin_info, &mesh_data.u.pMesh);
3769 if (FAILED(hr)) return hr;
3771 hr = filedata_get_name(filedata, &name);
3772 if (FAILED(hr)) goto cleanup;
3774 hr = alloc_hier->lpVtbl->CreateMeshContainer(alloc_hier, name, &mesh_data,
3775 materials ? ID3DXBuffer_GetBufferPointer(materials) : NULL,
3776 effects ? ID3DXBuffer_GetBufferPointer(effects) : NULL,
3777 num_materials,
3778 adjacency ? ID3DXBuffer_GetBufferPointer(adjacency) : NULL,
3779 skin_info, mesh_container);
3781 cleanup:
3782 if (materials) ID3DXBuffer_Release(materials);
3783 if (effects) ID3DXBuffer_Release(effects);
3784 if (adjacency) ID3DXBuffer_Release(adjacency);
3785 if (skin_info) IUnknown_Release(skin_info);
3786 if (mesh_data.u.pMesh) IUnknown_Release(mesh_data.u.pMesh);
3787 HeapFree(GetProcessHeap(), 0, name);
3788 return hr;
3791 static HRESULT parse_transform_matrix(ID3DXFileData *filedata, D3DXMATRIX *transform)
3793 HRESULT hr;
3794 SIZE_T data_size;
3795 const BYTE *data;
3797 /* template Matrix4x4 {
3798 * array FLOAT matrix[16];
3800 * template FrameTransformMatrix {
3801 * Matrix4x4 frameMatrix;
3805 hr = filedata->lpVtbl->Lock(filedata, &data_size, (const void**)&data);
3806 if (FAILED(hr)) return hr;
3808 if (data_size != sizeof(D3DXMATRIX)) {
3809 WARN("incorrect data size (%ld bytes)\n", data_size);
3810 filedata->lpVtbl->Unlock(filedata);
3811 return E_FAIL;
3814 memcpy(transform, data, sizeof(D3DXMATRIX));
3816 filedata->lpVtbl->Unlock(filedata);
3817 return D3D_OK;
3820 static HRESULT load_frame(struct ID3DXFileData *filedata, DWORD options, struct IDirect3DDevice9 *device,
3821 struct ID3DXAllocateHierarchy *alloc_hier, D3DXFRAME **frame_out)
3823 HRESULT hr;
3824 GUID type;
3825 ID3DXFileData *child;
3826 char *name = NULL;
3827 D3DXFRAME *frame = NULL;
3828 D3DXMESHCONTAINER **next_container;
3829 D3DXFRAME **next_child;
3830 SIZE_T nb_children;
3831 int i;
3833 hr = filedata_get_name(filedata, &name);
3834 if (FAILED(hr)) return hr;
3836 hr = alloc_hier->lpVtbl->CreateFrame(alloc_hier, name, frame_out);
3837 HeapFree(GetProcessHeap(), 0, name);
3838 if (FAILED(hr)) return E_FAIL;
3840 frame = *frame_out;
3841 D3DXMatrixIdentity(&frame->TransformationMatrix);
3842 next_child = &frame->pFrameFirstChild;
3843 next_container = &frame->pMeshContainer;
3845 hr = filedata->lpVtbl->GetChildren(filedata, &nb_children);
3846 if (FAILED(hr))
3847 return hr;
3849 for (i = 0; i < nb_children; i++)
3851 hr = filedata->lpVtbl->GetChild(filedata, i, &child);
3852 if (FAILED(hr))
3853 return hr;
3854 hr = child->lpVtbl->GetType(child, &type);
3855 if (FAILED(hr))
3856 return hr;
3858 if (IsEqualGUID(&type, &TID_D3DRMMesh)) {
3859 hr = load_mesh_container(child, options, device, alloc_hier, next_container);
3860 if (SUCCEEDED(hr))
3861 next_container = &(*next_container)->pNextMeshContainer;
3862 } else if (IsEqualGUID(&type, &TID_D3DRMFrameTransformMatrix)) {
3863 hr = parse_transform_matrix(child, &frame->TransformationMatrix);
3864 } else if (IsEqualGUID(&type, &TID_D3DRMFrame)) {
3865 hr = load_frame(child, options, device, alloc_hier, next_child);
3866 if (SUCCEEDED(hr))
3867 next_child = &(*next_child)->pFrameSibling;
3869 if (FAILED(hr))
3870 return hr;
3873 return D3D_OK;
3876 HRESULT WINAPI D3DXLoadMeshHierarchyFromXInMemory(const void *memory, DWORD memory_size, DWORD options,
3877 struct IDirect3DDevice9 *device, struct ID3DXAllocateHierarchy *alloc_hier,
3878 struct ID3DXLoadUserData *load_user_data, D3DXFRAME **frame_hierarchy,
3879 struct ID3DXAnimationController **anim_controller)
3881 HRESULT hr;
3882 ID3DXFile *d3dxfile = NULL;
3883 ID3DXFileEnumObject *enumobj = NULL;
3884 ID3DXFileData *filedata = NULL;
3885 D3DXF_FILELOADMEMORY source;
3886 D3DXFRAME *first_frame = NULL;
3887 D3DXFRAME **next_frame = &first_frame;
3888 SIZE_T nb_children;
3889 GUID guid;
3890 int i;
3892 TRACE("(%p, %u, %x, %p, %p, %p, %p, %p)\n", memory, memory_size, options,
3893 device, alloc_hier, load_user_data, frame_hierarchy, anim_controller);
3895 if (!memory || !memory_size || !device || !frame_hierarchy || !alloc_hier)
3896 return D3DERR_INVALIDCALL;
3897 if (load_user_data || anim_controller) {
3898 if (load_user_data)
3899 FIXME("Loading user data not implemented\n");
3900 if (anim_controller)
3901 FIXME("Animation controller creation not implemented\n");
3902 return E_NOTIMPL;
3905 hr = D3DXFileCreate(&d3dxfile);
3906 if (FAILED(hr)) goto cleanup;
3908 hr = d3dxfile->lpVtbl->RegisterTemplates(d3dxfile, D3DRM_XTEMPLATES, D3DRM_XTEMPLATE_BYTES);
3909 if (FAILED(hr)) goto cleanup;
3911 source.lpMemory = (void*)memory;
3912 source.dSize = memory_size;
3913 hr = d3dxfile->lpVtbl->CreateEnumObject(d3dxfile, &source, D3DXF_FILELOAD_FROMMEMORY, &enumobj);
3914 if (FAILED(hr)) goto cleanup;
3916 hr = enumobj->lpVtbl->GetChildren(enumobj, &nb_children);
3917 if (FAILED(hr))
3918 goto cleanup;
3920 for (i = 0; i < nb_children; i++)
3922 hr = enumobj->lpVtbl->GetChild(enumobj, i, &filedata);
3923 if (FAILED(hr))
3924 goto cleanup;
3926 hr = filedata->lpVtbl->GetType(filedata, &guid);
3927 if (SUCCEEDED(hr)) {
3928 if (IsEqualGUID(&guid, &TID_D3DRMMesh)) {
3929 hr = alloc_hier->lpVtbl->CreateFrame(alloc_hier, NULL, next_frame);
3930 if (FAILED(hr)) {
3931 hr = E_FAIL;
3932 goto cleanup;
3935 D3DXMatrixIdentity(&(*next_frame)->TransformationMatrix);
3937 hr = load_mesh_container(filedata, options, device, alloc_hier, &(*next_frame)->pMeshContainer);
3938 if (FAILED(hr)) goto cleanup;
3939 } else if (IsEqualGUID(&guid, &TID_D3DRMFrame)) {
3940 hr = load_frame(filedata, options, device, alloc_hier, next_frame);
3941 if (FAILED(hr)) goto cleanup;
3943 while (*next_frame)
3944 next_frame = &(*next_frame)->pFrameSibling;
3947 filedata->lpVtbl->Release(filedata);
3948 filedata = NULL;
3949 if (FAILED(hr))
3950 goto cleanup;
3953 if (!first_frame) {
3954 hr = E_FAIL;
3955 } else if (first_frame->pFrameSibling) {
3956 D3DXFRAME *root_frame = NULL;
3957 hr = alloc_hier->lpVtbl->CreateFrame(alloc_hier, NULL, &root_frame);
3958 if (FAILED(hr)) {
3959 hr = E_FAIL;
3960 goto cleanup;
3962 D3DXMatrixIdentity(&root_frame->TransformationMatrix);
3963 root_frame->pFrameFirstChild = first_frame;
3964 *frame_hierarchy = root_frame;
3965 hr = D3D_OK;
3966 } else {
3967 *frame_hierarchy = first_frame;
3968 hr = D3D_OK;
3971 cleanup:
3972 if (FAILED(hr) && first_frame) D3DXFrameDestroy(first_frame, alloc_hier);
3973 if (filedata) filedata->lpVtbl->Release(filedata);
3974 if (enumobj) enumobj->lpVtbl->Release(enumobj);
3975 if (d3dxfile) d3dxfile->lpVtbl->Release(d3dxfile);
3976 return hr;
3979 HRESULT WINAPI D3DXCleanMesh(D3DXCLEANTYPE clean_type, ID3DXMesh *mesh_in, const DWORD *adjacency_in,
3980 ID3DXMesh **mesh_out, DWORD *adjacency_out, ID3DXBuffer **errors_and_warnings)
3982 FIXME("(%u, %p, %p, %p, %p, %p)\n", clean_type, mesh_in, adjacency_in, mesh_out, adjacency_out, errors_and_warnings);
3984 return E_NOTIMPL;
3987 HRESULT WINAPI D3DXFrameDestroy(D3DXFRAME *frame, ID3DXAllocateHierarchy *alloc_hier)
3989 HRESULT hr;
3990 BOOL last = FALSE;
3992 TRACE("(%p, %p)\n", frame, alloc_hier);
3994 if (!frame || !alloc_hier)
3995 return D3DERR_INVALIDCALL;
3997 while (!last) {
3998 D3DXMESHCONTAINER *container;
3999 D3DXFRAME *current_frame;
4001 if (frame->pFrameSibling) {
4002 current_frame = frame->pFrameSibling;
4003 frame->pFrameSibling = current_frame->pFrameSibling;
4004 current_frame->pFrameSibling = NULL;
4005 } else {
4006 current_frame = frame;
4007 last = TRUE;
4010 if (current_frame->pFrameFirstChild) {
4011 hr = D3DXFrameDestroy(current_frame->pFrameFirstChild, alloc_hier);
4012 if (FAILED(hr)) return hr;
4013 current_frame->pFrameFirstChild = NULL;
4016 container = current_frame->pMeshContainer;
4017 while (container) {
4018 D3DXMESHCONTAINER *next_container = container->pNextMeshContainer;
4019 hr = alloc_hier->lpVtbl->DestroyMeshContainer(alloc_hier, container);
4020 if (FAILED(hr)) return hr;
4021 container = next_container;
4023 hr = alloc_hier->lpVtbl->DestroyFrame(alloc_hier, current_frame);
4024 if (FAILED(hr)) return hr;
4026 return D3D_OK;
4029 HRESULT WINAPI D3DXLoadMeshFromXA(const char *filename, DWORD options, struct IDirect3DDevice9 *device,
4030 struct ID3DXBuffer **adjacency, struct ID3DXBuffer **materials, struct ID3DXBuffer **effect_instances,
4031 DWORD *num_materials, struct ID3DXMesh **mesh)
4033 WCHAR *filenameW;
4034 HRESULT hr;
4035 int len;
4037 TRACE("filename %s, options %#x, device %p, adjacency %p, materials %p, "
4038 "effect_instances %p, num_materials %p, mesh %p.\n",
4039 debugstr_a(filename), options, device, adjacency, materials,
4040 effect_instances, num_materials, mesh);
4042 if (!filename)
4043 return D3DERR_INVALIDCALL;
4045 len = MultiByteToWideChar(CP_ACP, 0, filename, -1, NULL, 0);
4046 filenameW = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
4047 if (!filenameW) return E_OUTOFMEMORY;
4048 MultiByteToWideChar(CP_ACP, 0, filename, -1, filenameW, len);
4050 hr = D3DXLoadMeshFromXW(filenameW, options, device, adjacency, materials,
4051 effect_instances, num_materials, mesh);
4052 HeapFree(GetProcessHeap(), 0, filenameW);
4054 return hr;
4057 HRESULT WINAPI D3DXLoadMeshFromXW(const WCHAR *filename, DWORD options, struct IDirect3DDevice9 *device,
4058 struct ID3DXBuffer **adjacency, struct ID3DXBuffer **materials, struct ID3DXBuffer **effect_instances,
4059 DWORD *num_materials, struct ID3DXMesh **mesh)
4061 void *buffer;
4062 HRESULT hr;
4063 DWORD size;
4065 TRACE("filename %s, options %#x, device %p, adjacency %p, materials %p, "
4066 "effect_instances %p, num_materials %p, mesh %p.\n",
4067 debugstr_w(filename), options, device, adjacency, materials,
4068 effect_instances, num_materials, mesh);
4070 if (!filename)
4071 return D3DERR_INVALIDCALL;
4073 hr = map_view_of_file(filename, &buffer, &size);
4074 if (FAILED(hr))
4075 return D3DXERR_INVALIDDATA;
4077 hr = D3DXLoadMeshFromXInMemory(buffer, size, options, device, adjacency,
4078 materials, effect_instances, num_materials, mesh);
4080 UnmapViewOfFile(buffer);
4082 return hr;
4085 HRESULT WINAPI D3DXLoadMeshFromXResource(HMODULE module, const char *name, const char *type, DWORD options,
4086 struct IDirect3DDevice9 *device, struct ID3DXBuffer **adjacency, struct ID3DXBuffer **materials,
4087 struct ID3DXBuffer **effect_instances, DWORD *num_materials, struct ID3DXMesh **mesh)
4089 HRESULT hr;
4090 HRSRC resinfo;
4091 void *buffer;
4092 DWORD size;
4094 TRACE("module %p, name %s, type %s, options %#x, device %p, adjacency %p, "
4095 "materials %p, effect_instances %p, num_materials %p, mesh %p.\n",
4096 module, debugstr_a(name), debugstr_a(type), options, device, adjacency,
4097 materials, effect_instances, num_materials, mesh);
4099 resinfo = FindResourceA(module, name, type);
4100 if (!resinfo) return D3DXERR_INVALIDDATA;
4102 hr = load_resource_into_memory(module, resinfo, &buffer, &size);
4103 if (FAILED(hr)) return D3DXERR_INVALIDDATA;
4105 return D3DXLoadMeshFromXInMemory(buffer, size, options, device, adjacency,
4106 materials, effect_instances, num_materials, mesh);
4109 struct mesh_container
4111 struct list entry;
4112 ID3DXMesh *mesh;
4113 ID3DXBuffer *adjacency;
4114 ID3DXBuffer *materials;
4115 ID3DXBuffer *effects;
4116 DWORD num_materials;
4117 D3DXMATRIX transform;
4120 static HRESULT parse_frame(struct ID3DXFileData *filedata, DWORD options, struct IDirect3DDevice9 *device,
4121 const D3DXMATRIX *parent_transform, struct list *container_list, DWORD provide_flags)
4123 HRESULT hr;
4124 D3DXMATRIX transform = *parent_transform;
4125 ID3DXFileData *child;
4126 GUID type;
4127 SIZE_T nb_children;
4128 int i;
4130 hr = filedata->lpVtbl->GetChildren(filedata, &nb_children);
4131 if (FAILED(hr))
4132 return hr;
4134 for (i = 0; i < nb_children; i++)
4136 hr = filedata->lpVtbl->GetChild(filedata, i, &child);
4137 if (FAILED(hr))
4138 return hr;
4139 hr = child->lpVtbl->GetType(child, &type);
4140 if (FAILED(hr))
4141 return hr;
4143 if (IsEqualGUID(&type, &TID_D3DRMMesh)) {
4144 struct mesh_container *container = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(*container));
4145 if (!container)
4146 return E_OUTOFMEMORY;
4147 list_add_tail(container_list, &container->entry);
4148 container->transform = transform;
4150 hr = D3DXLoadSkinMeshFromXof(child, options, device,
4151 (provide_flags & PROVIDE_ADJACENCY) ? &container->adjacency : NULL,
4152 (provide_flags & PROVIDE_MATERIALS) ? &container->materials : NULL,
4153 NULL, &container->num_materials, NULL, &container->mesh);
4154 } else if (IsEqualGUID(&type, &TID_D3DRMFrameTransformMatrix)) {
4155 D3DXMATRIX new_transform;
4156 hr = parse_transform_matrix(child, &new_transform);
4157 D3DXMatrixMultiply(&transform, &transform, &new_transform);
4158 } else if (IsEqualGUID(&type, &TID_D3DRMFrame)) {
4159 hr = parse_frame(child, options, device, &transform, container_list, provide_flags);
4161 if (FAILED(hr))
4162 return hr;
4165 return D3D_OK;
4168 HRESULT WINAPI D3DXLoadMeshFromXInMemory(const void *memory, DWORD memory_size, DWORD options,
4169 struct IDirect3DDevice9 *device, struct ID3DXBuffer **adjacency_out, struct ID3DXBuffer **materials_out,
4170 struct ID3DXBuffer **effects_out, DWORD *num_materials_out, struct ID3DXMesh **mesh_out)
4172 HRESULT hr;
4173 ID3DXFile *d3dxfile = NULL;
4174 ID3DXFileEnumObject *enumobj = NULL;
4175 ID3DXFileData *filedata = NULL;
4176 D3DXF_FILELOADMEMORY source;
4177 ID3DXBuffer *materials = NULL;
4178 ID3DXBuffer *effects = NULL;
4179 ID3DXBuffer *adjacency = NULL;
4180 struct list container_list = LIST_INIT(container_list);
4181 struct mesh_container *container_ptr, *next_container_ptr;
4182 DWORD num_materials;
4183 DWORD num_faces, num_vertices;
4184 D3DXMATRIX identity;
4185 DWORD provide_flags = 0;
4186 DWORD fvf;
4187 ID3DXMesh *concat_mesh = NULL;
4188 D3DVERTEXELEMENT9 concat_decl[MAX_FVF_DECL_SIZE];
4189 BYTE *concat_vertices = NULL;
4190 void *concat_indices = NULL;
4191 DWORD index_offset;
4192 DWORD concat_vertex_size;
4193 SIZE_T nb_children;
4194 GUID guid;
4195 int i;
4197 TRACE("(%p, %u, %x, %p, %p, %p, %p, %p, %p)\n", memory, memory_size, options,
4198 device, adjacency_out, materials_out, effects_out, num_materials_out, mesh_out);
4200 if (!memory || !memory_size || !device || !mesh_out)
4201 return D3DERR_INVALIDCALL;
4203 hr = D3DXFileCreate(&d3dxfile);
4204 if (FAILED(hr)) goto cleanup;
4206 hr = d3dxfile->lpVtbl->RegisterTemplates(d3dxfile, D3DRM_XTEMPLATES, D3DRM_XTEMPLATE_BYTES);
4207 if (FAILED(hr)) goto cleanup;
4209 source.lpMemory = (void*)memory;
4210 source.dSize = memory_size;
4211 hr = d3dxfile->lpVtbl->CreateEnumObject(d3dxfile, &source, D3DXF_FILELOAD_FROMMEMORY, &enumobj);
4212 if (FAILED(hr)) goto cleanup;
4214 D3DXMatrixIdentity(&identity);
4215 if (adjacency_out) provide_flags |= PROVIDE_ADJACENCY;
4216 if (materials_out || effects_out) provide_flags |= PROVIDE_MATERIALS;
4218 hr = enumobj->lpVtbl->GetChildren(enumobj, &nb_children);
4219 if (FAILED(hr))
4220 goto cleanup;
4222 for (i = 0; i < nb_children; i++)
4224 hr = enumobj->lpVtbl->GetChild(enumobj, i, &filedata);
4225 if (FAILED(hr))
4226 goto cleanup;
4228 hr = filedata->lpVtbl->GetType(filedata, &guid);
4229 if (SUCCEEDED(hr)) {
4230 if (IsEqualGUID(&guid, &TID_D3DRMMesh)) {
4231 container_ptr = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(*container_ptr));
4232 if (!container_ptr) {
4233 hr = E_OUTOFMEMORY;
4234 goto cleanup;
4236 list_add_tail(&container_list, &container_ptr->entry);
4237 D3DXMatrixIdentity(&container_ptr->transform);
4239 hr = D3DXLoadSkinMeshFromXof(filedata, options, device,
4240 (provide_flags & PROVIDE_ADJACENCY) ? &container_ptr->adjacency : NULL,
4241 (provide_flags & PROVIDE_MATERIALS) ? &container_ptr->materials : NULL,
4242 NULL, &container_ptr->num_materials, NULL, &container_ptr->mesh);
4243 } else if (IsEqualGUID(&guid, &TID_D3DRMFrame)) {
4244 hr = parse_frame(filedata, options, device, &identity, &container_list, provide_flags);
4246 if (FAILED(hr)) goto cleanup;
4248 filedata->lpVtbl->Release(filedata);
4249 filedata = NULL;
4250 if (FAILED(hr))
4251 goto cleanup;
4254 enumobj->lpVtbl->Release(enumobj);
4255 enumobj = NULL;
4256 d3dxfile->lpVtbl->Release(d3dxfile);
4257 d3dxfile = NULL;
4259 if (list_empty(&container_list)) {
4260 hr = E_FAIL;
4261 goto cleanup;
4264 fvf = D3DFVF_XYZ;
4265 num_faces = 0;
4266 num_vertices = 0;
4267 num_materials = 0;
4268 LIST_FOR_EACH_ENTRY(container_ptr, &container_list, struct mesh_container, entry)
4270 ID3DXMesh *mesh = container_ptr->mesh;
4271 fvf |= mesh->lpVtbl->GetFVF(mesh);
4272 num_faces += mesh->lpVtbl->GetNumFaces(mesh);
4273 num_vertices += mesh->lpVtbl->GetNumVertices(mesh);
4274 num_materials += container_ptr->num_materials;
4277 hr = D3DXCreateMeshFVF(num_faces, num_vertices, options, fvf, device, &concat_mesh);
4278 if (FAILED(hr)) goto cleanup;
4280 hr = concat_mesh->lpVtbl->GetDeclaration(concat_mesh, concat_decl);
4281 if (FAILED(hr)) goto cleanup;
4283 concat_vertex_size = D3DXGetDeclVertexSize(concat_decl, 0);
4285 hr = concat_mesh->lpVtbl->LockVertexBuffer(concat_mesh, 0, (void**)&concat_vertices);
4286 if (FAILED(hr)) goto cleanup;
4288 LIST_FOR_EACH_ENTRY(container_ptr, &container_list, struct mesh_container, entry)
4290 D3DVERTEXELEMENT9 mesh_decl[MAX_FVF_DECL_SIZE];
4291 ID3DXMesh *mesh = container_ptr->mesh;
4292 DWORD num_mesh_vertices = mesh->lpVtbl->GetNumVertices(mesh);
4293 DWORD mesh_vertex_size;
4294 const BYTE *mesh_vertices;
4295 DWORD i;
4297 hr = mesh->lpVtbl->GetDeclaration(mesh, mesh_decl);
4298 if (FAILED(hr)) goto cleanup;
4300 mesh_vertex_size = D3DXGetDeclVertexSize(mesh_decl, 0);
4302 hr = mesh->lpVtbl->LockVertexBuffer(mesh, D3DLOCK_READONLY, (void**)&mesh_vertices);
4303 if (FAILED(hr)) goto cleanup;
4305 for (i = 0; i < num_mesh_vertices; i++) {
4306 int j;
4307 int k = 1;
4309 D3DXVec3TransformCoord((D3DXVECTOR3*)concat_vertices,
4310 (D3DXVECTOR3*)mesh_vertices,
4311 &container_ptr->transform);
4312 for (j = 1; concat_decl[j].Stream != 0xff; j++)
4314 if (concat_decl[j].Usage == mesh_decl[k].Usage &&
4315 concat_decl[j].UsageIndex == mesh_decl[k].UsageIndex)
4317 if (concat_decl[j].Usage == D3DDECLUSAGE_NORMAL) {
4318 D3DXVec3TransformCoord((D3DXVECTOR3*)(concat_vertices + concat_decl[j].Offset),
4319 (D3DXVECTOR3*)(mesh_vertices + mesh_decl[k].Offset),
4320 &container_ptr->transform);
4321 } else {
4322 memcpy(concat_vertices + concat_decl[j].Offset,
4323 mesh_vertices + mesh_decl[k].Offset,
4324 d3dx_decltype_size[mesh_decl[k].Type]);
4326 k++;
4329 mesh_vertices += mesh_vertex_size;
4330 concat_vertices += concat_vertex_size;
4333 mesh->lpVtbl->UnlockVertexBuffer(mesh);
4336 concat_mesh->lpVtbl->UnlockVertexBuffer(concat_mesh);
4337 concat_vertices = NULL;
4339 hr = concat_mesh->lpVtbl->LockIndexBuffer(concat_mesh, 0, &concat_indices);
4340 if (FAILED(hr)) goto cleanup;
4342 index_offset = 0;
4343 LIST_FOR_EACH_ENTRY(container_ptr, &container_list, struct mesh_container, entry)
4345 ID3DXMesh *mesh = container_ptr->mesh;
4346 const void *mesh_indices;
4347 DWORD num_mesh_faces = mesh->lpVtbl->GetNumFaces(mesh);
4348 DWORD i;
4350 hr = mesh->lpVtbl->LockIndexBuffer(mesh, D3DLOCK_READONLY, (void**)&mesh_indices);
4351 if (FAILED(hr)) goto cleanup;
4353 if (options & D3DXMESH_32BIT) {
4354 DWORD *dest = concat_indices;
4355 const DWORD *src = mesh_indices;
4356 for (i = 0; i < num_mesh_faces * 3; i++)
4357 *dest++ = index_offset + *src++;
4358 concat_indices = dest;
4359 } else {
4360 WORD *dest = concat_indices;
4361 const WORD *src = mesh_indices;
4362 for (i = 0; i < num_mesh_faces * 3; i++)
4363 *dest++ = index_offset + *src++;
4364 concat_indices = dest;
4366 mesh->lpVtbl->UnlockIndexBuffer(mesh);
4368 index_offset += num_mesh_faces * 3;
4371 concat_mesh->lpVtbl->UnlockIndexBuffer(concat_mesh);
4372 concat_indices = NULL;
4374 if (num_materials) {
4375 DWORD *concat_attrib_buffer = NULL;
4376 DWORD offset = 0;
4378 hr = concat_mesh->lpVtbl->LockAttributeBuffer(concat_mesh, 0, &concat_attrib_buffer);
4379 if (FAILED(hr)) goto cleanup;
4381 LIST_FOR_EACH_ENTRY(container_ptr, &container_list, struct mesh_container, entry)
4383 ID3DXMesh *mesh = container_ptr->mesh;
4384 const DWORD *mesh_attrib_buffer = NULL;
4385 DWORD count = mesh->lpVtbl->GetNumFaces(mesh);
4387 hr = mesh->lpVtbl->LockAttributeBuffer(mesh, D3DLOCK_READONLY, (DWORD**)&mesh_attrib_buffer);
4388 if (FAILED(hr)) {
4389 concat_mesh->lpVtbl->UnlockAttributeBuffer(concat_mesh);
4390 goto cleanup;
4393 while (count--)
4394 *concat_attrib_buffer++ = offset + *mesh_attrib_buffer++;
4396 mesh->lpVtbl->UnlockAttributeBuffer(mesh);
4397 offset += container_ptr->num_materials;
4399 concat_mesh->lpVtbl->UnlockAttributeBuffer(concat_mesh);
4402 if (materials_out || effects_out) {
4403 D3DXMATERIAL *out_ptr;
4404 if (!num_materials) {
4405 /* create default material */
4406 hr = D3DXCreateBuffer(sizeof(D3DXMATERIAL), &materials);
4407 if (FAILED(hr)) goto cleanup;
4409 out_ptr = ID3DXBuffer_GetBufferPointer(materials);
4410 out_ptr->MatD3D.Diffuse.r = 0.5f;
4411 out_ptr->MatD3D.Diffuse.g = 0.5f;
4412 out_ptr->MatD3D.Diffuse.b = 0.5f;
4413 out_ptr->MatD3D.Specular.r = 0.5f;
4414 out_ptr->MatD3D.Specular.g = 0.5f;
4415 out_ptr->MatD3D.Specular.b = 0.5f;
4416 /* D3DXCreateBuffer initializes the rest to zero */
4417 } else {
4418 DWORD buffer_size = num_materials * sizeof(D3DXMATERIAL);
4419 char *strings_out_ptr;
4421 LIST_FOR_EACH_ENTRY(container_ptr, &container_list, struct mesh_container, entry)
4423 if (container_ptr->materials) {
4424 DWORD i;
4425 const D3DXMATERIAL *in_ptr = ID3DXBuffer_GetBufferPointer(container_ptr->materials);
4426 for (i = 0; i < container_ptr->num_materials; i++)
4428 if (in_ptr->pTextureFilename)
4429 buffer_size += strlen(in_ptr->pTextureFilename) + 1;
4430 in_ptr++;
4435 hr = D3DXCreateBuffer(buffer_size, &materials);
4436 if (FAILED(hr)) goto cleanup;
4437 out_ptr = ID3DXBuffer_GetBufferPointer(materials);
4438 strings_out_ptr = (char*)(out_ptr + num_materials);
4440 LIST_FOR_EACH_ENTRY(container_ptr, &container_list, struct mesh_container, entry)
4442 if (container_ptr->materials) {
4443 DWORD i;
4444 const D3DXMATERIAL *in_ptr = ID3DXBuffer_GetBufferPointer(container_ptr->materials);
4445 for (i = 0; i < container_ptr->num_materials; i++)
4447 out_ptr->MatD3D = in_ptr->MatD3D;
4448 if (in_ptr->pTextureFilename) {
4449 out_ptr->pTextureFilename = strings_out_ptr;
4450 strcpy(out_ptr->pTextureFilename, in_ptr->pTextureFilename);
4451 strings_out_ptr += strlen(in_ptr->pTextureFilename) + 1;
4453 in_ptr++;
4454 out_ptr++;
4460 if (!num_materials)
4461 num_materials = 1;
4463 if (effects_out) {
4464 generate_effects(materials, num_materials, &effects);
4465 if (!materials_out) {
4466 ID3DXBuffer_Release(materials);
4467 materials = NULL;
4471 if (adjacency_out) {
4472 if (!list_next(&container_list, list_head(&container_list))) {
4473 container_ptr = LIST_ENTRY(list_head(&container_list), struct mesh_container, entry);
4474 adjacency = container_ptr->adjacency;
4475 container_ptr->adjacency = NULL;
4476 } else {
4477 DWORD offset = 0;
4478 DWORD *out_ptr;
4480 hr = D3DXCreateBuffer(num_faces * 3 * sizeof(DWORD), &adjacency);
4481 if (FAILED(hr)) goto cleanup;
4483 out_ptr = ID3DXBuffer_GetBufferPointer(adjacency);
4484 LIST_FOR_EACH_ENTRY(container_ptr, &container_list, struct mesh_container, entry)
4486 DWORD i;
4487 DWORD count = 3 * container_ptr->mesh->lpVtbl->GetNumFaces(container_ptr->mesh);
4488 DWORD *in_ptr = ID3DXBuffer_GetBufferPointer(container_ptr->adjacency);
4490 for (i = 0; i < count; i++)
4491 *out_ptr++ = offset + *in_ptr++;
4493 offset += count;
4498 *mesh_out = concat_mesh;
4499 if (adjacency_out) *adjacency_out = adjacency;
4500 if (materials_out) *materials_out = materials;
4501 if (effects_out) *effects_out = effects;
4502 if (num_materials_out) *num_materials_out = num_materials;
4504 hr = D3D_OK;
4505 cleanup:
4506 if (concat_indices) concat_mesh->lpVtbl->UnlockIndexBuffer(concat_mesh);
4507 if (concat_vertices) concat_mesh->lpVtbl->UnlockVertexBuffer(concat_mesh);
4508 if (filedata) filedata->lpVtbl->Release(filedata);
4509 if (enumobj) enumobj->lpVtbl->Release(enumobj);
4510 if (d3dxfile) d3dxfile->lpVtbl->Release(d3dxfile);
4511 if (FAILED(hr)) {
4512 if (concat_mesh) IUnknown_Release(concat_mesh);
4513 if (materials) ID3DXBuffer_Release(materials);
4514 if (effects) ID3DXBuffer_Release(effects);
4515 if (adjacency) ID3DXBuffer_Release(adjacency);
4517 LIST_FOR_EACH_ENTRY_SAFE(container_ptr, next_container_ptr, &container_list, struct mesh_container, entry)
4519 if (container_ptr->mesh) IUnknown_Release(container_ptr->mesh);
4520 if (container_ptr->adjacency) ID3DXBuffer_Release(container_ptr->adjacency);
4521 if (container_ptr->materials) ID3DXBuffer_Release(container_ptr->materials);
4522 if (container_ptr->effects) ID3DXBuffer_Release(container_ptr->effects);
4523 HeapFree(GetProcessHeap(), 0, container_ptr);
4525 return hr;
4528 HRESULT WINAPI D3DXCreateBox(struct IDirect3DDevice9 *device, float width, float height,
4529 float depth, struct ID3DXMesh **mesh, struct ID3DXBuffer **adjacency)
4531 FIXME("(%p, %f, %f, %f, %p, %p): stub\n", device, width, height, depth, mesh, adjacency);
4533 return E_NOTIMPL;
4536 struct vertex
4538 D3DXVECTOR3 position;
4539 D3DXVECTOR3 normal;
4542 typedef WORD face[3];
4544 struct sincos_table
4546 float *sin;
4547 float *cos;
4550 static void free_sincos_table(struct sincos_table *sincos_table)
4552 HeapFree(GetProcessHeap(), 0, sincos_table->cos);
4553 HeapFree(GetProcessHeap(), 0, sincos_table->sin);
4556 /* pre compute sine and cosine tables; caller must free */
4557 static BOOL compute_sincos_table(struct sincos_table *sincos_table, float angle_start, float angle_step, int n)
4559 float angle;
4560 int i;
4562 sincos_table->sin = HeapAlloc(GetProcessHeap(), 0, n * sizeof(*sincos_table->sin));
4563 if (!sincos_table->sin)
4565 return FALSE;
4567 sincos_table->cos = HeapAlloc(GetProcessHeap(), 0, n * sizeof(*sincos_table->cos));
4568 if (!sincos_table->cos)
4570 HeapFree(GetProcessHeap(), 0, sincos_table->sin);
4571 return FALSE;
4574 angle = angle_start;
4575 for (i = 0; i < n; i++)
4577 sincos_table->sin[i] = sin(angle);
4578 sincos_table->cos[i] = cos(angle);
4579 angle += angle_step;
4582 return TRUE;
4585 static WORD vertex_index(UINT slices, int slice, int stack)
4587 return stack*slices+slice+1;
4590 HRESULT WINAPI D3DXCreateSphere(struct IDirect3DDevice9 *device, float radius, UINT slices,
4591 UINT stacks, struct ID3DXMesh **mesh, struct ID3DXBuffer **adjacency)
4593 DWORD number_of_vertices, number_of_faces;
4594 HRESULT hr;
4595 ID3DXMesh *sphere;
4596 struct vertex *vertices;
4597 face *faces;
4598 float phi_step, phi_start;
4599 struct sincos_table phi;
4600 float theta_step, theta, sin_theta, cos_theta;
4601 DWORD vertex, face, stack, slice;
4603 TRACE("(%p, %f, %u, %u, %p, %p)\n", device, radius, slices, stacks, mesh, adjacency);
4605 if (!device || radius < 0.0f || slices < 2 || stacks < 2 || !mesh)
4607 return D3DERR_INVALIDCALL;
4610 if (adjacency)
4612 FIXME("Case of adjacency != NULL not implemented.\n");
4613 return E_NOTIMPL;
4616 number_of_vertices = 2 + slices * (stacks-1);
4617 number_of_faces = 2 * slices + (stacks - 2) * (2 * slices);
4619 hr = D3DXCreateMeshFVF(number_of_faces, number_of_vertices, D3DXMESH_MANAGED,
4620 D3DFVF_XYZ | D3DFVF_NORMAL, device, &sphere);
4621 if (FAILED(hr))
4623 return hr;
4626 if (FAILED(hr = sphere->lpVtbl->LockVertexBuffer(sphere, 0, (void **)&vertices)))
4628 sphere->lpVtbl->Release(sphere);
4629 return hr;
4632 if (FAILED(hr = sphere->lpVtbl->LockIndexBuffer(sphere, 0, (void **)&faces)))
4634 sphere->lpVtbl->UnlockVertexBuffer(sphere);
4635 sphere->lpVtbl->Release(sphere);
4636 return hr;
4639 /* phi = angle on xz plane wrt z axis */
4640 phi_step = -2 * M_PI / slices;
4641 phi_start = M_PI / 2;
4643 if (!compute_sincos_table(&phi, phi_start, phi_step, slices))
4645 sphere->lpVtbl->UnlockIndexBuffer(sphere);
4646 sphere->lpVtbl->UnlockVertexBuffer(sphere);
4647 sphere->lpVtbl->Release(sphere);
4648 return E_OUTOFMEMORY;
4651 /* theta = angle on xy plane wrt x axis */
4652 theta_step = M_PI / stacks;
4653 theta = theta_step;
4655 vertex = 0;
4656 face = 0;
4658 vertices[vertex].normal.x = 0.0f;
4659 vertices[vertex].normal.y = 0.0f;
4660 vertices[vertex].normal.z = 1.0f;
4661 vertices[vertex].position.x = 0.0f;
4662 vertices[vertex].position.y = 0.0f;
4663 vertices[vertex].position.z = radius;
4664 vertex++;
4666 for (stack = 0; stack < stacks - 1; stack++)
4668 sin_theta = sin(theta);
4669 cos_theta = cos(theta);
4671 for (slice = 0; slice < slices; slice++)
4673 vertices[vertex].normal.x = sin_theta * phi.cos[slice];
4674 vertices[vertex].normal.y = sin_theta * phi.sin[slice];
4675 vertices[vertex].normal.z = cos_theta;
4676 vertices[vertex].position.x = radius * sin_theta * phi.cos[slice];
4677 vertices[vertex].position.y = radius * sin_theta * phi.sin[slice];
4678 vertices[vertex].position.z = radius * cos_theta;
4679 vertex++;
4681 if (slice > 0)
4683 if (stack == 0)
4685 /* top stack is triangle fan */
4686 faces[face][0] = 0;
4687 faces[face][1] = slice + 1;
4688 faces[face][2] = slice;
4689 face++;
4691 else
4693 /* stacks in between top and bottom are quad strips */
4694 faces[face][0] = vertex_index(slices, slice-1, stack-1);
4695 faces[face][1] = vertex_index(slices, slice, stack-1);
4696 faces[face][2] = vertex_index(slices, slice-1, stack);
4697 face++;
4699 faces[face][0] = vertex_index(slices, slice, stack-1);
4700 faces[face][1] = vertex_index(slices, slice, stack);
4701 faces[face][2] = vertex_index(slices, slice-1, stack);
4702 face++;
4707 theta += theta_step;
4709 if (stack == 0)
4711 faces[face][0] = 0;
4712 faces[face][1] = 1;
4713 faces[face][2] = slice;
4714 face++;
4716 else
4718 faces[face][0] = vertex_index(slices, slice-1, stack-1);
4719 faces[face][1] = vertex_index(slices, 0, stack-1);
4720 faces[face][2] = vertex_index(slices, slice-1, stack);
4721 face++;
4723 faces[face][0] = vertex_index(slices, 0, stack-1);
4724 faces[face][1] = vertex_index(slices, 0, stack);
4725 faces[face][2] = vertex_index(slices, slice-1, stack);
4726 face++;
4730 vertices[vertex].position.x = 0.0f;
4731 vertices[vertex].position.y = 0.0f;
4732 vertices[vertex].position.z = -radius;
4733 vertices[vertex].normal.x = 0.0f;
4734 vertices[vertex].normal.y = 0.0f;
4735 vertices[vertex].normal.z = -1.0f;
4737 /* bottom stack is triangle fan */
4738 for (slice = 1; slice < slices; slice++)
4740 faces[face][0] = vertex_index(slices, slice-1, stack-1);
4741 faces[face][1] = vertex_index(slices, slice, stack-1);
4742 faces[face][2] = vertex;
4743 face++;
4746 faces[face][0] = vertex_index(slices, slice-1, stack-1);
4747 faces[face][1] = vertex_index(slices, 0, stack-1);
4748 faces[face][2] = vertex;
4750 free_sincos_table(&phi);
4751 sphere->lpVtbl->UnlockIndexBuffer(sphere);
4752 sphere->lpVtbl->UnlockVertexBuffer(sphere);
4753 *mesh = sphere;
4755 return D3D_OK;
4758 HRESULT WINAPI D3DXCreateCylinder(struct IDirect3DDevice9 *device, float radius1, float radius2,
4759 float length, UINT slices, UINT stacks, struct ID3DXMesh **mesh, struct ID3DXBuffer **adjacency)
4761 DWORD number_of_vertices, number_of_faces;
4762 HRESULT hr;
4763 ID3DXMesh *cylinder;
4764 struct vertex *vertices;
4765 face *faces;
4766 float theta_step, theta_start;
4767 struct sincos_table theta;
4768 float delta_radius, radius, radius_step;
4769 float z, z_step, z_normal;
4770 DWORD vertex, face, slice, stack;
4772 TRACE("(%p, %f, %f, %f, %u, %u, %p, %p)\n", device, radius1, radius2, length, slices, stacks, mesh, adjacency);
4774 if (device == NULL || radius1 < 0.0f || radius2 < 0.0f || length < 0.0f || slices < 2 || stacks < 1 || mesh == NULL)
4776 return D3DERR_INVALIDCALL;
4779 if (adjacency)
4781 FIXME("Case of adjacency != NULL not implemented.\n");
4782 return E_NOTIMPL;
4785 number_of_vertices = 2 + (slices * (3 + stacks));
4786 number_of_faces = 2 * slices + stacks * (2 * slices);
4788 hr = D3DXCreateMeshFVF(number_of_faces, number_of_vertices, D3DXMESH_MANAGED,
4789 D3DFVF_XYZ | D3DFVF_NORMAL, device, &cylinder);
4790 if (FAILED(hr))
4792 return hr;
4795 if (FAILED(hr = cylinder->lpVtbl->LockVertexBuffer(cylinder, 0, (void **)&vertices)))
4797 cylinder->lpVtbl->Release(cylinder);
4798 return hr;
4801 if (FAILED(hr = cylinder->lpVtbl->LockIndexBuffer(cylinder, 0, (void **)&faces)))
4803 cylinder->lpVtbl->UnlockVertexBuffer(cylinder);
4804 cylinder->lpVtbl->Release(cylinder);
4805 return hr;
4808 /* theta = angle on xy plane wrt x axis */
4809 theta_step = -2 * M_PI / slices;
4810 theta_start = M_PI / 2;
4812 if (!compute_sincos_table(&theta, theta_start, theta_step, slices))
4814 cylinder->lpVtbl->UnlockIndexBuffer(cylinder);
4815 cylinder->lpVtbl->UnlockVertexBuffer(cylinder);
4816 cylinder->lpVtbl->Release(cylinder);
4817 return E_OUTOFMEMORY;
4820 vertex = 0;
4821 face = 0;
4823 delta_radius = radius1 - radius2;
4824 radius = radius1;
4825 radius_step = delta_radius / stacks;
4827 z = -length / 2;
4828 z_step = length / stacks;
4829 z_normal = delta_radius / length;
4830 if (isnan(z_normal))
4832 z_normal = 0.0f;
4835 vertices[vertex].normal.x = 0.0f;
4836 vertices[vertex].normal.y = 0.0f;
4837 vertices[vertex].normal.z = -1.0f;
4838 vertices[vertex].position.x = 0.0f;
4839 vertices[vertex].position.y = 0.0f;
4840 vertices[vertex++].position.z = z;
4842 for (slice = 0; slice < slices; slice++, vertex++)
4844 vertices[vertex].normal.x = 0.0f;
4845 vertices[vertex].normal.y = 0.0f;
4846 vertices[vertex].normal.z = -1.0f;
4847 vertices[vertex].position.x = radius * theta.cos[slice];
4848 vertices[vertex].position.y = radius * theta.sin[slice];
4849 vertices[vertex].position.z = z;
4851 if (slice > 0)
4853 faces[face][0] = 0;
4854 faces[face][1] = slice;
4855 faces[face++][2] = slice + 1;
4859 faces[face][0] = 0;
4860 faces[face][1] = slice;
4861 faces[face++][2] = 1;
4863 for (stack = 1; stack <= stacks+1; stack++)
4865 for (slice = 0; slice < slices; slice++, vertex++)
4867 vertices[vertex].normal.x = theta.cos[slice];
4868 vertices[vertex].normal.y = theta.sin[slice];
4869 vertices[vertex].normal.z = z_normal;
4870 D3DXVec3Normalize(&vertices[vertex].normal, &vertices[vertex].normal);
4871 vertices[vertex].position.x = radius * theta.cos[slice];
4872 vertices[vertex].position.y = radius * theta.sin[slice];
4873 vertices[vertex].position.z = z;
4875 if (stack > 1 && slice > 0)
4877 faces[face][0] = vertex_index(slices, slice-1, stack-1);
4878 faces[face][1] = vertex_index(slices, slice-1, stack);
4879 faces[face++][2] = vertex_index(slices, slice, stack-1);
4881 faces[face][0] = vertex_index(slices, slice, stack-1);
4882 faces[face][1] = vertex_index(slices, slice-1, stack);
4883 faces[face++][2] = vertex_index(slices, slice, stack);
4887 if (stack > 1)
4889 faces[face][0] = vertex_index(slices, slice-1, stack-1);
4890 faces[face][1] = vertex_index(slices, slice-1, stack);
4891 faces[face++][2] = vertex_index(slices, 0, stack-1);
4893 faces[face][0] = vertex_index(slices, 0, stack-1);
4894 faces[face][1] = vertex_index(slices, slice-1, stack);
4895 faces[face++][2] = vertex_index(slices, 0, stack);
4898 if (stack < stacks + 1)
4900 z += z_step;
4901 radius -= radius_step;
4905 for (slice = 0; slice < slices; slice++, vertex++)
4907 vertices[vertex].normal.x = 0.0f;
4908 vertices[vertex].normal.y = 0.0f;
4909 vertices[vertex].normal.z = 1.0f;
4910 vertices[vertex].position.x = radius * theta.cos[slice];
4911 vertices[vertex].position.y = radius * theta.sin[slice];
4912 vertices[vertex].position.z = z;
4914 if (slice > 0)
4916 faces[face][0] = vertex_index(slices, slice-1, stack);
4917 faces[face][1] = number_of_vertices - 1;
4918 faces[face++][2] = vertex_index(slices, slice, stack);
4922 vertices[vertex].position.x = 0.0f;
4923 vertices[vertex].position.y = 0.0f;
4924 vertices[vertex].position.z = z;
4925 vertices[vertex].normal.x = 0.0f;
4926 vertices[vertex].normal.y = 0.0f;
4927 vertices[vertex].normal.z = 1.0f;
4929 faces[face][0] = vertex_index(slices, slice-1, stack);
4930 faces[face][1] = number_of_vertices - 1;
4931 faces[face][2] = vertex_index(slices, 0, stack);
4933 free_sincos_table(&theta);
4934 cylinder->lpVtbl->UnlockIndexBuffer(cylinder);
4935 cylinder->lpVtbl->UnlockVertexBuffer(cylinder);
4936 *mesh = cylinder;
4938 return D3D_OK;
4941 HRESULT WINAPI D3DXCreateTeapot(struct IDirect3DDevice9 *device,
4942 struct ID3DXMesh **mesh, struct ID3DXBuffer **adjacency)
4944 FIXME("(%p, %p, %p): stub\n", device, mesh, adjacency);
4946 return E_NOTIMPL;
4949 HRESULT WINAPI D3DXCreateTextA(struct IDirect3DDevice9 *device, HDC hdc, const char *text, float deviation,
4950 float extrusion, struct ID3DXMesh **mesh, struct ID3DXBuffer **adjacency, GLYPHMETRICSFLOAT *glyphmetrics)
4952 WCHAR *textW;
4953 HRESULT hr;
4954 int len;
4956 TRACE("device %p, hdc %p, text %s, deviation %.8e, extrusion %.8e, mesh %p, adjacency %p, glyphmetrics %p.\n",
4957 device, hdc, debugstr_a(text), deviation, extrusion, mesh, adjacency, glyphmetrics);
4959 if (!text)
4960 return D3DERR_INVALIDCALL;
4962 len = MultiByteToWideChar(CP_ACP, 0, text, -1, NULL, 0);
4963 textW = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
4964 MultiByteToWideChar(CP_ACP, 0, text, -1, textW, len);
4966 hr = D3DXCreateTextW(device, hdc, textW, deviation, extrusion,
4967 mesh, adjacency, glyphmetrics);
4968 HeapFree(GetProcessHeap(), 0, textW);
4970 return hr;
4973 enum pointtype {
4974 POINTTYPE_CURVE = 0,
4975 POINTTYPE_CORNER,
4976 POINTTYPE_CURVE_START,
4977 POINTTYPE_CURVE_END,
4978 POINTTYPE_CURVE_MIDDLE,
4981 struct point2d
4983 D3DXVECTOR2 pos;
4984 enum pointtype corner;
4987 struct dynamic_array
4989 int count, capacity;
4990 void *items;
4993 /* is a dynamic_array */
4994 struct outline
4996 int count, capacity;
4997 struct point2d *items;
5000 /* is a dynamic_array */
5001 struct outline_array
5003 int count, capacity;
5004 struct outline *items;
5007 struct face_array
5009 int count;
5010 face *items;
5013 struct point2d_index
5015 struct outline *outline;
5016 int vertex;
5019 struct point2d_index_array
5021 int count;
5022 struct point2d_index *items;
5025 struct glyphinfo
5027 struct outline_array outlines;
5028 struct face_array faces;
5029 struct point2d_index_array ordered_vertices;
5030 float offset_x;
5033 /* is an dynamic_array */
5034 struct word_array
5036 int count, capacity;
5037 WORD *items;
5040 /* complex polygons are split into monotone polygons, which have
5041 * at most 2 intersections with the vertical sweep line */
5042 struct triangulation
5044 struct word_array vertex_stack;
5045 BOOL last_on_top, merging;
5048 /* is an dynamic_array */
5049 struct triangulation_array
5051 int count, capacity;
5052 struct triangulation *items;
5054 struct glyphinfo *glyph;
5057 static BOOL reserve(struct dynamic_array *array, int count, int itemsize)
5059 if (count > array->capacity) {
5060 void *new_buffer;
5061 int new_capacity;
5062 if (array->items && array->capacity) {
5063 new_capacity = max(array->capacity * 2, count);
5064 new_buffer = HeapReAlloc(GetProcessHeap(), 0, array->items, new_capacity * itemsize);
5065 } else {
5066 new_capacity = max(16, count);
5067 new_buffer = HeapAlloc(GetProcessHeap(), 0, new_capacity * itemsize);
5069 if (!new_buffer)
5070 return FALSE;
5071 array->items = new_buffer;
5072 array->capacity = new_capacity;
5074 return TRUE;
5077 static struct point2d *add_points(struct outline *array, int num)
5079 struct point2d *item;
5081 if (!reserve((struct dynamic_array *)array, array->count + num, sizeof(array->items[0])))
5082 return NULL;
5084 item = &array->items[array->count];
5085 array->count += num;
5086 return item;
5089 static struct outline *add_outline(struct outline_array *array)
5091 struct outline *item;
5093 if (!reserve((struct dynamic_array *)array, array->count + 1, sizeof(array->items[0])))
5094 return NULL;
5096 item = &array->items[array->count++];
5097 ZeroMemory(item, sizeof(*item));
5098 return item;
5101 static inline face *add_face(struct face_array *array)
5103 return &array->items[array->count++];
5106 static struct triangulation *add_triangulation(struct triangulation_array *array)
5108 struct triangulation *item;
5110 if (!reserve((struct dynamic_array *)array, array->count + 1, sizeof(array->items[0])))
5111 return NULL;
5113 item = &array->items[array->count++];
5114 ZeroMemory(item, sizeof(*item));
5115 return item;
5118 static HRESULT add_vertex_index(struct word_array *array, WORD vertex_index)
5120 if (!reserve((struct dynamic_array *)array, array->count + 1, sizeof(array->items[0])))
5121 return E_OUTOFMEMORY;
5123 array->items[array->count++] = vertex_index;
5124 return S_OK;
5127 /* assume fixed point numbers can be converted to float point in place */
5128 C_ASSERT(sizeof(FIXED) == sizeof(float));
5129 C_ASSERT(sizeof(POINTFX) == sizeof(D3DXVECTOR2));
5131 static inline D3DXVECTOR2 *convert_fixed_to_float(POINTFX *pt, int count, float emsquare)
5133 D3DXVECTOR2 *ret = (D3DXVECTOR2*)pt;
5134 while (count--) {
5135 D3DXVECTOR2 *pt_flt = (D3DXVECTOR2*)pt;
5136 pt_flt->x = (pt->x.value + pt->x.fract / (float)0x10000) / emsquare;
5137 pt_flt->y = (pt->y.value + pt->y.fract / (float)0x10000) / emsquare;
5138 pt++;
5140 return ret;
5143 static HRESULT add_bezier_points(struct outline *outline, const D3DXVECTOR2 *p1,
5144 const D3DXVECTOR2 *p2, const D3DXVECTOR2 *p3,
5145 float max_deviation_sq)
5147 D3DXVECTOR2 split1 = {0, 0}, split2 = {0, 0}, middle, vec;
5148 float deviation_sq;
5150 D3DXVec2Scale(&split1, D3DXVec2Add(&split1, p1, p2), 0.5f);
5151 D3DXVec2Scale(&split2, D3DXVec2Add(&split2, p2, p3), 0.5f);
5152 D3DXVec2Scale(&middle, D3DXVec2Add(&middle, &split1, &split2), 0.5f);
5154 deviation_sq = D3DXVec2LengthSq(D3DXVec2Subtract(&vec, &middle, p2));
5155 if (deviation_sq < max_deviation_sq) {
5156 struct point2d *pt = add_points(outline, 1);
5157 if (!pt) return E_OUTOFMEMORY;
5158 pt->pos = *p2;
5159 pt->corner = POINTTYPE_CURVE;
5160 /* the end point is omitted because the end line merges into the next segment of
5161 * the split bezier curve, and the end of the split bezier curve is added outside
5162 * this recursive function. */
5163 } else {
5164 HRESULT hr = add_bezier_points(outline, p1, &split1, &middle, max_deviation_sq);
5165 if (hr != S_OK) return hr;
5166 hr = add_bezier_points(outline, &middle, &split2, p3, max_deviation_sq);
5167 if (hr != S_OK) return hr;
5170 return S_OK;
5173 static inline BOOL is_direction_similar(D3DXVECTOR2 *dir1, D3DXVECTOR2 *dir2, float cos_theta)
5175 /* dot product = cos(theta) */
5176 return D3DXVec2Dot(dir1, dir2) > cos_theta;
5179 static inline D3DXVECTOR2 *unit_vec2(D3DXVECTOR2 *dir, const D3DXVECTOR2 *pt1, const D3DXVECTOR2 *pt2)
5181 return D3DXVec2Normalize(D3DXVec2Subtract(dir, pt2, pt1), dir);
5184 struct cos_table
5186 float cos_half;
5187 float cos_45;
5188 float cos_90;
5191 static BOOL attempt_line_merge(struct outline *outline,
5192 int pt_index,
5193 const D3DXVECTOR2 *nextpt,
5194 BOOL to_curve,
5195 const struct cos_table *table)
5197 D3DXVECTOR2 curdir, lastdir;
5198 struct point2d *prevpt, *pt;
5199 BOOL ret = FALSE;
5201 pt = &outline->items[pt_index];
5202 pt_index = (pt_index - 1 + outline->count) % outline->count;
5203 prevpt = &outline->items[pt_index];
5205 if (to_curve)
5206 pt->corner = pt->corner != POINTTYPE_CORNER ? POINTTYPE_CURVE_MIDDLE : POINTTYPE_CURVE_START;
5208 if (outline->count < 2)
5209 return FALSE;
5211 /* remove last point if the next line continues the last line */
5212 unit_vec2(&lastdir, &prevpt->pos, &pt->pos);
5213 unit_vec2(&curdir, &pt->pos, nextpt);
5214 if (is_direction_similar(&lastdir, &curdir, table->cos_half))
5216 outline->count--;
5217 if (pt->corner == POINTTYPE_CURVE_END)
5218 prevpt->corner = pt->corner;
5219 if (prevpt->corner == POINTTYPE_CURVE_END && to_curve)
5220 prevpt->corner = POINTTYPE_CURVE_MIDDLE;
5221 pt = prevpt;
5223 ret = TRUE;
5224 if (outline->count < 2)
5225 return ret;
5227 pt_index = (pt_index - 1 + outline->count) % outline->count;
5228 prevpt = &outline->items[pt_index];
5229 unit_vec2(&lastdir, &prevpt->pos, &pt->pos);
5230 unit_vec2(&curdir, &pt->pos, nextpt);
5232 return ret;
5235 static HRESULT create_outline(struct glyphinfo *glyph, void *raw_outline, int datasize,
5236 float max_deviation_sq, float emsquare, const struct cos_table *cos_table)
5238 TTPOLYGONHEADER *header = (TTPOLYGONHEADER *)raw_outline;
5240 while ((char *)header < (char *)raw_outline + datasize)
5242 TTPOLYCURVE *curve = (TTPOLYCURVE *)(header + 1);
5243 struct point2d *lastpt, *pt;
5244 D3DXVECTOR2 lastdir;
5245 D3DXVECTOR2 *pt_flt;
5246 int j;
5247 struct outline *outline = add_outline(&glyph->outlines);
5249 if (!outline)
5250 return E_OUTOFMEMORY;
5252 pt = add_points(outline, 1);
5253 if (!pt)
5254 return E_OUTOFMEMORY;
5255 pt_flt = convert_fixed_to_float(&header->pfxStart, 1, emsquare);
5256 pt->pos = *pt_flt;
5257 pt->corner = POINTTYPE_CORNER;
5259 if (header->dwType != TT_POLYGON_TYPE)
5260 FIXME("Unknown header type %d\n", header->dwType);
5262 while ((char *)curve < (char *)header + header->cb)
5264 D3DXVECTOR2 bezier_start = outline->items[outline->count - 1].pos;
5265 BOOL to_curve = curve->wType != TT_PRIM_LINE && curve->cpfx > 1;
5266 unsigned int j2 = 0;
5268 if (!curve->cpfx) {
5269 curve = (TTPOLYCURVE *)&curve->apfx[curve->cpfx];
5270 continue;
5273 pt_flt = convert_fixed_to_float(curve->apfx, curve->cpfx, emsquare);
5275 attempt_line_merge(outline, outline->count - 1, &pt_flt[0], to_curve, cos_table);
5277 if (to_curve)
5279 HRESULT hr;
5280 int count = curve->cpfx;
5282 while (count > 2)
5284 D3DXVECTOR2 bezier_end;
5286 D3DXVec2Scale(&bezier_end, D3DXVec2Add(&bezier_end, &pt_flt[j2], &pt_flt[j2+1]), 0.5f);
5287 hr = add_bezier_points(outline, &bezier_start, &pt_flt[j2], &bezier_end, max_deviation_sq);
5288 if (hr != S_OK)
5289 return hr;
5290 bezier_start = bezier_end;
5291 count--;
5292 j2++;
5294 hr = add_bezier_points(outline, &bezier_start, &pt_flt[j2], &pt_flt[j2+1], max_deviation_sq);
5295 if (hr != S_OK)
5296 return hr;
5298 pt = add_points(outline, 1);
5299 if (!pt)
5300 return E_OUTOFMEMORY;
5301 j2++;
5302 pt->pos = pt_flt[j2];
5303 pt->corner = POINTTYPE_CURVE_END;
5304 } else {
5305 pt = add_points(outline, curve->cpfx);
5306 if (!pt)
5307 return E_OUTOFMEMORY;
5308 for (j2 = 0; j2 < curve->cpfx; j2++)
5310 pt->pos = pt_flt[j2];
5311 pt->corner = POINTTYPE_CORNER;
5312 pt++;
5316 curve = (TTPOLYCURVE *)&curve->apfx[curve->cpfx];
5319 /* remove last point if the next line continues the last line */
5320 if (outline->count >= 3) {
5321 BOOL to_curve;
5323 lastpt = &outline->items[outline->count - 1];
5324 pt = &outline->items[0];
5325 if (pt->pos.x == lastpt->pos.x && pt->pos.y == lastpt->pos.y) {
5326 if (lastpt->corner == POINTTYPE_CURVE_END)
5328 if (pt->corner == POINTTYPE_CURVE_START)
5329 pt->corner = POINTTYPE_CURVE_MIDDLE;
5330 else
5331 pt->corner = POINTTYPE_CURVE_END;
5333 outline->count--;
5334 lastpt = &outline->items[outline->count - 1];
5335 } else {
5336 /* outline closed with a line from end to start point */
5337 attempt_line_merge(outline, outline->count - 1, &pt->pos, FALSE, cos_table);
5339 lastpt = &outline->items[0];
5340 to_curve = lastpt->corner != POINTTYPE_CORNER && lastpt->corner != POINTTYPE_CURVE_END;
5341 if (lastpt->corner == POINTTYPE_CURVE_START)
5342 lastpt->corner = POINTTYPE_CORNER;
5343 pt = &outline->items[1];
5344 if (attempt_line_merge(outline, 0, &pt->pos, to_curve, cos_table))
5345 *lastpt = outline->items[outline->count];
5348 lastpt = &outline->items[outline->count - 1];
5349 pt = &outline->items[0];
5350 unit_vec2(&lastdir, &lastpt->pos, &pt->pos);
5351 for (j = 0; j < outline->count; j++)
5353 D3DXVECTOR2 curdir;
5355 lastpt = pt;
5356 pt = &outline->items[(j + 1) % outline->count];
5357 unit_vec2(&curdir, &lastpt->pos, &pt->pos);
5359 switch (lastpt->corner)
5361 case POINTTYPE_CURVE_START:
5362 case POINTTYPE_CURVE_END:
5363 if (!is_direction_similar(&lastdir, &curdir, cos_table->cos_45))
5364 lastpt->corner = POINTTYPE_CORNER;
5365 break;
5366 case POINTTYPE_CURVE_MIDDLE:
5367 if (!is_direction_similar(&lastdir, &curdir, cos_table->cos_90))
5368 lastpt->corner = POINTTYPE_CORNER;
5369 else
5370 lastpt->corner = POINTTYPE_CURVE;
5371 break;
5372 default:
5373 break;
5375 lastdir = curdir;
5378 header = (TTPOLYGONHEADER *)((char *)header + header->cb);
5380 return S_OK;
5383 /* Get the y-distance from a line to a point */
5384 static float get_line_to_point_y_distance(D3DXVECTOR2 *line_pt1,
5385 D3DXVECTOR2 *line_pt2,
5386 D3DXVECTOR2 *point)
5388 D3DXVECTOR2 line_vec = {0, 0};
5389 float line_pt_dx;
5390 float line_y;
5392 D3DXVec2Subtract(&line_vec, line_pt2, line_pt1);
5393 line_pt_dx = point->x - line_pt1->x;
5394 line_y = line_pt1->y + (line_vec.y * line_pt_dx) / line_vec.x;
5395 return point->y - line_y;
5398 static D3DXVECTOR2 *get_indexed_point(struct point2d_index *pt_idx)
5400 return &pt_idx->outline->items[pt_idx->vertex].pos;
5403 static D3DXVECTOR2 *get_ordered_vertex(struct glyphinfo *glyph, WORD index)
5405 return get_indexed_point(&glyph->ordered_vertices.items[index]);
5408 static void remove_triangulation(struct triangulation_array *array, struct triangulation *item)
5410 HeapFree(GetProcessHeap(), 0, item->vertex_stack.items);
5411 MoveMemory(item, item + 1, (char*)&array->items[array->count] - (char*)(item + 1));
5412 array->count--;
5415 static HRESULT triangulation_add_point(struct triangulation **t_ptr,
5416 struct triangulation_array *triangulations,
5417 WORD vtx_idx,
5418 BOOL to_top)
5420 struct glyphinfo *glyph = triangulations->glyph;
5421 struct triangulation *t = *t_ptr;
5422 HRESULT hr;
5423 face *face;
5424 int f1, f2;
5426 if (t->last_on_top) {
5427 f1 = 1;
5428 f2 = 2;
5429 } else {
5430 f1 = 2;
5431 f2 = 1;
5434 if (t->last_on_top != to_top && t->vertex_stack.count > 1) {
5435 /* consume all vertices on the stack */
5436 WORD last_pt = t->vertex_stack.items[0];
5437 int i;
5438 for (i = 1; i < t->vertex_stack.count; i++)
5440 face = add_face(&glyph->faces);
5441 if (!face) return E_OUTOFMEMORY;
5442 (*face)[0] = vtx_idx;
5443 (*face)[f1] = last_pt;
5444 (*face)[f2] = last_pt = t->vertex_stack.items[i];
5446 t->vertex_stack.items[0] = last_pt;
5447 t->vertex_stack.count = 1;
5448 } else if (t->vertex_stack.count > 1) {
5449 int i = t->vertex_stack.count - 1;
5450 D3DXVECTOR2 *point = get_ordered_vertex(glyph, vtx_idx);
5451 WORD top_idx = t->vertex_stack.items[i--];
5452 D3DXVECTOR2 *top_pt = get_ordered_vertex(glyph, top_idx);
5454 while (i >= 0)
5456 WORD prev_idx = t->vertex_stack.items[i--];
5457 D3DXVECTOR2 *prev_pt = get_ordered_vertex(glyph, prev_idx);
5459 if (prev_pt->x != top_pt->x &&
5460 ((to_top && get_line_to_point_y_distance(prev_pt, top_pt, point) > 0) ||
5461 (!to_top && get_line_to_point_y_distance(prev_pt, top_pt, point) < 0)))
5462 break;
5464 face = add_face(&glyph->faces);
5465 if (!face) return E_OUTOFMEMORY;
5466 (*face)[0] = vtx_idx;
5467 (*face)[f1] = prev_idx;
5468 (*face)[f2] = top_idx;
5470 top_pt = prev_pt;
5471 top_idx = prev_idx;
5472 t->vertex_stack.count--;
5475 t->last_on_top = to_top;
5477 hr = add_vertex_index(&t->vertex_stack, vtx_idx);
5479 if (hr == S_OK && t->merging) {
5480 struct triangulation *t2;
5482 t2 = to_top ? t - 1 : t + 1;
5483 t2->merging = FALSE;
5484 hr = triangulation_add_point(&t2, triangulations, vtx_idx, to_top);
5485 if (hr != S_OK) return hr;
5486 remove_triangulation(triangulations, t);
5487 if (t2 > t)
5488 t2--;
5489 *t_ptr = t2;
5491 return hr;
5494 /* check if the point is next on the outline for either the top or bottom */
5495 static D3DXVECTOR2 *triangulation_get_next_point(struct triangulation *t, struct glyphinfo *glyph, BOOL on_top)
5497 int i = t->last_on_top == on_top ? t->vertex_stack.count - 1 : 0;
5498 WORD idx = t->vertex_stack.items[i];
5499 struct point2d_index *pt_idx = &glyph->ordered_vertices.items[idx];
5500 struct outline *outline = pt_idx->outline;
5502 if (on_top)
5503 i = (pt_idx->vertex + outline->count - 1) % outline->count;
5504 else
5505 i = (pt_idx->vertex + 1) % outline->count;
5507 return &outline->items[i].pos;
5510 static int compare_vertex_indices(const void *a, const void *b)
5512 const struct point2d_index *idx1 = a, *idx2 = b;
5513 const D3DXVECTOR2 *p1 = &idx1->outline->items[idx1->vertex].pos;
5514 const D3DXVECTOR2 *p2 = &idx2->outline->items[idx2->vertex].pos;
5515 float diff = p1->x - p2->x;
5517 if (diff == 0.0f)
5518 diff = p1->y - p2->y;
5520 return diff == 0.0f ? 0 : (diff > 0.0f ? -1 : 1);
5523 static HRESULT triangulate(struct triangulation_array *triangulations)
5525 int sweep_idx;
5526 HRESULT hr;
5527 struct glyphinfo *glyph = triangulations->glyph;
5528 int nb_vertices = 0;
5529 int i;
5530 struct point2d_index *idx_ptr;
5532 for (i = 0; i < glyph->outlines.count; i++)
5533 nb_vertices += glyph->outlines.items[i].count;
5535 glyph->ordered_vertices.items = HeapAlloc(GetProcessHeap(), 0,
5536 nb_vertices * sizeof(*glyph->ordered_vertices.items));
5537 if (!glyph->ordered_vertices.items)
5538 return E_OUTOFMEMORY;
5540 idx_ptr = glyph->ordered_vertices.items;
5541 for (i = 0; i < glyph->outlines.count; i++)
5543 struct outline *outline = &glyph->outlines.items[i];
5544 int j;
5546 idx_ptr->outline = outline;
5547 idx_ptr->vertex = 0;
5548 idx_ptr++;
5549 for (j = outline->count - 1; j > 0; j--)
5551 idx_ptr->outline = outline;
5552 idx_ptr->vertex = j;
5553 idx_ptr++;
5556 glyph->ordered_vertices.count = nb_vertices;
5558 /* Native implementation seems to try to create a triangle fan from
5559 * the first outline point if the glyph only has one outline. */
5560 if (glyph->outlines.count == 1)
5562 struct outline *outline = glyph->outlines.items;
5563 D3DXVECTOR2 *base = &outline->items[0].pos;
5564 D3DXVECTOR2 *last = &outline->items[1].pos;
5565 float ccw = 0;
5567 for (i = 2; i < outline->count; i++)
5569 D3DXVECTOR2 *next = &outline->items[i].pos;
5570 D3DXVECTOR2 v1 = {0.0f, 0.0f};
5571 D3DXVECTOR2 v2 = {0.0f, 0.0f};
5573 D3DXVec2Subtract(&v1, base, last);
5574 D3DXVec2Subtract(&v2, last, next);
5575 ccw = D3DXVec2CCW(&v1, &v2);
5576 if (ccw > 0.0f)
5577 break;
5579 last = next;
5581 if (ccw <= 0)
5583 glyph->faces.items = HeapAlloc(GetProcessHeap(), 0,
5584 (outline->count - 2) * sizeof(glyph->faces.items[0]));
5585 if (!glyph->faces.items)
5586 return E_OUTOFMEMORY;
5588 glyph->faces.count = outline->count - 2;
5589 for (i = 0; i < glyph->faces.count; i++)
5591 glyph->faces.items[i][0] = 0;
5592 glyph->faces.items[i][1] = i + 1;
5593 glyph->faces.items[i][2] = i + 2;
5595 return S_OK;
5599 /* Perform 2D polygon triangulation for complex glyphs.
5600 * Triangulation is performed using a sweep line concept, from right to left,
5601 * by processing vertices in sorted order. Complex polygons are split into
5602 * monotone polygons which are triangulated separately. */
5603 /* FIXME: The order of the faces is not consistent with the native implementation. */
5605 /* Reserve space for maximum possible faces from triangulation.
5606 * # faces for outer outlines = outline->count - 2
5607 * # faces for inner outlines = outline->count + 2
5608 * There must be at least 1 outer outline. */
5609 glyph->faces.items = HeapAlloc(GetProcessHeap(), 0,
5610 (nb_vertices + glyph->outlines.count * 2 - 4) * sizeof(glyph->faces.items[0]));
5611 if (!glyph->faces.items)
5612 return E_OUTOFMEMORY;
5614 qsort(glyph->ordered_vertices.items, nb_vertices,
5615 sizeof(glyph->ordered_vertices.items[0]), compare_vertex_indices);
5616 for (sweep_idx = 0; sweep_idx < glyph->ordered_vertices.count; sweep_idx++)
5618 int start = 0;
5619 int end = triangulations->count;
5621 while (start < end)
5623 D3DXVECTOR2 *sweep_vtx = get_ordered_vertex(glyph, sweep_idx);
5624 int current = (start + end) / 2;
5625 struct triangulation *t = &triangulations->items[current];
5626 BOOL on_top_outline = FALSE;
5627 D3DXVECTOR2 *top_next, *bottom_next;
5628 WORD top_idx, bottom_idx;
5630 if (t->merging && t->last_on_top)
5631 top_next = triangulation_get_next_point(t + 1, glyph, TRUE);
5632 else
5633 top_next = triangulation_get_next_point(t, glyph, TRUE);
5634 if (sweep_vtx == top_next)
5636 if (t->merging && t->last_on_top)
5637 t++;
5638 hr = triangulation_add_point(&t, triangulations, sweep_idx, TRUE);
5639 if (hr != S_OK) return hr;
5641 if (t + 1 < &triangulations->items[triangulations->count] &&
5642 triangulation_get_next_point(t + 1, glyph, FALSE) == sweep_vtx)
5644 /* point also on bottom outline of higher triangulation */
5645 struct triangulation *t2 = t + 1;
5646 hr = triangulation_add_point(&t2, triangulations, sweep_idx, FALSE);
5647 if (hr != S_OK) return hr;
5649 t->merging = TRUE;
5650 t2->merging = TRUE;
5652 on_top_outline = TRUE;
5655 if (t->merging && !t->last_on_top)
5656 bottom_next = triangulation_get_next_point(t - 1, glyph, FALSE);
5657 else
5658 bottom_next = triangulation_get_next_point(t, glyph, FALSE);
5659 if (sweep_vtx == bottom_next)
5661 if (t->merging && !t->last_on_top)
5662 t--;
5663 if (on_top_outline) {
5664 /* outline finished */
5665 remove_triangulation(triangulations, t);
5666 break;
5669 hr = triangulation_add_point(&t, triangulations, sweep_idx, FALSE);
5670 if (hr != S_OK) return hr;
5672 if (t > triangulations->items &&
5673 triangulation_get_next_point(t - 1, glyph, TRUE) == sweep_vtx)
5675 struct triangulation *t2 = t - 1;
5676 /* point also on top outline of lower triangulation */
5677 hr = triangulation_add_point(&t2, triangulations, sweep_idx, TRUE);
5678 if (hr != S_OK) return hr;
5679 t = t2 + 1; /* t may be invalidated by triangulation merging */
5681 t->merging = TRUE;
5682 t2->merging = TRUE;
5684 break;
5686 if (on_top_outline)
5687 break;
5689 if (t->last_on_top) {
5690 top_idx = t->vertex_stack.items[t->vertex_stack.count - 1];
5691 bottom_idx = t->vertex_stack.items[0];
5692 } else {
5693 top_idx = t->vertex_stack.items[0];
5694 bottom_idx = t->vertex_stack.items[t->vertex_stack.count - 1];
5697 /* check if the point is inside or outside this polygon */
5698 if (get_line_to_point_y_distance(get_ordered_vertex(glyph, top_idx),
5699 top_next, sweep_vtx) > 0)
5700 { /* above */
5701 start = current + 1;
5702 } else if (get_line_to_point_y_distance(get_ordered_vertex(glyph, bottom_idx),
5703 bottom_next, sweep_vtx) < 0)
5704 { /* below */
5705 end = current;
5706 } else if (t->merging) {
5707 /* inside, so cancel merging */
5708 struct triangulation *t2 = t->last_on_top ? t + 1 : t - 1;
5709 t->merging = FALSE;
5710 t2->merging = FALSE;
5711 hr = triangulation_add_point(&t, triangulations, sweep_idx, t->last_on_top);
5712 if (hr != S_OK) return hr;
5713 hr = triangulation_add_point(&t2, triangulations, sweep_idx, t2->last_on_top);
5714 if (hr != S_OK) return hr;
5715 break;
5716 } else {
5717 /* inside, so split polygon into two monotone parts */
5718 struct triangulation *t2 = add_triangulation(triangulations);
5719 if (!t2) return E_OUTOFMEMORY;
5720 MoveMemory(t + 1, t, (char*)(t2 + 1) - (char*)t);
5721 if (t->last_on_top) {
5722 t2 = t + 1;
5723 } else {
5724 t2 = t;
5725 t++;
5728 ZeroMemory(&t2->vertex_stack, sizeof(t2->vertex_stack));
5729 hr = add_vertex_index(&t2->vertex_stack, t->vertex_stack.items[t->vertex_stack.count - 1]);
5730 if (hr != S_OK) return hr;
5731 hr = add_vertex_index(&t2->vertex_stack, sweep_idx);
5732 if (hr != S_OK) return hr;
5733 t2->last_on_top = !t->last_on_top;
5735 hr = triangulation_add_point(&t, triangulations, sweep_idx, t->last_on_top);
5736 if (hr != S_OK) return hr;
5737 break;
5740 if (start >= end)
5742 struct triangulation *t;
5743 struct triangulation *t2 = add_triangulation(triangulations);
5744 if (!t2) return E_OUTOFMEMORY;
5745 t = &triangulations->items[start];
5746 MoveMemory(t + 1, t, (char*)(t2 + 1) - (char*)t);
5747 ZeroMemory(t, sizeof(*t));
5748 hr = add_vertex_index(&t->vertex_stack, sweep_idx);
5749 if (hr != S_OK) return hr;
5752 return S_OK;
5755 HRESULT WINAPI D3DXCreateTextW(struct IDirect3DDevice9 *device, HDC hdc, const WCHAR *text, float deviation,
5756 float extrusion, struct ID3DXMesh **mesh_ptr, struct ID3DXBuffer **adjacency, GLYPHMETRICSFLOAT *glyphmetrics)
5758 HRESULT hr;
5759 ID3DXMesh *mesh = NULL;
5760 DWORD nb_vertices, nb_faces;
5761 DWORD nb_front_faces, nb_corners, nb_outline_points;
5762 struct vertex *vertices = NULL;
5763 face *faces = NULL;
5764 int textlen = 0;
5765 float offset_x;
5766 LOGFONTW lf;
5767 OUTLINETEXTMETRICW otm;
5768 HFONT font = NULL, oldfont = NULL;
5769 const MAT2 identity = {{0, 1}, {0, 0}, {0, 0}, {0, 1}};
5770 void *raw_outline = NULL;
5771 int bufsize = 0;
5772 struct glyphinfo *glyphs = NULL;
5773 GLYPHMETRICS gm;
5774 struct triangulation_array triangulations = {0, 0, NULL};
5775 int i;
5776 struct vertex *vertex_ptr;
5777 face *face_ptr;
5778 float max_deviation_sq;
5779 const struct cos_table cos_table = {
5780 cos(D3DXToRadian(0.5f)),
5781 cos(D3DXToRadian(45.0f)),
5782 cos(D3DXToRadian(90.0f)),
5784 int f1, f2;
5786 TRACE("(%p, %p, %s, %f, %f, %p, %p, %p)\n", device, hdc,
5787 debugstr_w(text), deviation, extrusion, mesh_ptr, adjacency, glyphmetrics);
5789 if (!device || !hdc || !text || !*text || deviation < 0.0f || extrusion < 0.0f || !mesh_ptr)
5790 return D3DERR_INVALIDCALL;
5792 if (adjacency)
5794 FIXME("Case of adjacency != NULL not implemented.\n");
5795 return E_NOTIMPL;
5798 if (!GetObjectW(GetCurrentObject(hdc, OBJ_FONT), sizeof(lf), &lf) ||
5799 !GetOutlineTextMetricsW(hdc, sizeof(otm), &otm))
5801 return D3DERR_INVALIDCALL;
5804 if (deviation == 0.0f)
5805 deviation = 1.0f / otm.otmEMSquare;
5806 max_deviation_sq = deviation * deviation;
5808 lf.lfHeight = otm.otmEMSquare;
5809 lf.lfWidth = 0;
5810 font = CreateFontIndirectW(&lf);
5811 if (!font) {
5812 hr = E_OUTOFMEMORY;
5813 goto error;
5815 oldfont = SelectObject(hdc, font);
5817 textlen = strlenW(text);
5818 for (i = 0; i < textlen; i++)
5820 int datasize = GetGlyphOutlineW(hdc, text[i], GGO_NATIVE, &gm, 0, NULL, &identity);
5821 if (datasize < 0)
5822 return D3DERR_INVALIDCALL;
5823 if (bufsize < datasize)
5824 bufsize = datasize;
5826 if (!bufsize) { /* e.g. text == " " */
5827 hr = D3DERR_INVALIDCALL;
5828 goto error;
5831 glyphs = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, textlen * sizeof(*glyphs));
5832 raw_outline = HeapAlloc(GetProcessHeap(), 0, bufsize);
5833 if (!glyphs || !raw_outline) {
5834 hr = E_OUTOFMEMORY;
5835 goto error;
5838 offset_x = 0.0f;
5839 for (i = 0; i < textlen; i++)
5841 /* get outline points from data returned from GetGlyphOutline */
5842 int datasize;
5844 glyphs[i].offset_x = offset_x;
5846 datasize = GetGlyphOutlineW(hdc, text[i], GGO_NATIVE, &gm, bufsize, raw_outline, &identity);
5847 hr = create_outline(&glyphs[i], raw_outline, datasize,
5848 max_deviation_sq, otm.otmEMSquare, &cos_table);
5849 if (hr != S_OK) goto error;
5851 triangulations.glyph = &glyphs[i];
5852 hr = triangulate(&triangulations);
5853 if (hr != S_OK) goto error;
5854 if (triangulations.count) {
5855 ERR("%d incomplete triangulations of glyph (%u).\n", triangulations.count, text[i]);
5856 triangulations.count = 0;
5859 if (glyphmetrics)
5861 glyphmetrics[i].gmfBlackBoxX = gm.gmBlackBoxX / (float)otm.otmEMSquare;
5862 glyphmetrics[i].gmfBlackBoxY = gm.gmBlackBoxY / (float)otm.otmEMSquare;
5863 glyphmetrics[i].gmfptGlyphOrigin.x = gm.gmptGlyphOrigin.x / (float)otm.otmEMSquare;
5864 glyphmetrics[i].gmfptGlyphOrigin.y = gm.gmptGlyphOrigin.y / (float)otm.otmEMSquare;
5865 glyphmetrics[i].gmfCellIncX = gm.gmCellIncX / (float)otm.otmEMSquare;
5866 glyphmetrics[i].gmfCellIncY = gm.gmCellIncY / (float)otm.otmEMSquare;
5868 offset_x += gm.gmCellIncX / (float)otm.otmEMSquare;
5871 /* corner points need an extra vertex for the different side faces normals */
5872 nb_corners = 0;
5873 nb_outline_points = 0;
5874 nb_front_faces = 0;
5875 for (i = 0; i < textlen; i++)
5877 int j;
5878 nb_outline_points += glyphs[i].ordered_vertices.count;
5879 nb_front_faces += glyphs[i].faces.count;
5880 for (j = 0; j < glyphs[i].outlines.count; j++)
5882 int k;
5883 struct outline *outline = &glyphs[i].outlines.items[j];
5884 nb_corners++; /* first outline point always repeated as a corner */
5885 for (k = 1; k < outline->count; k++)
5886 if (outline->items[k].corner)
5887 nb_corners++;
5891 nb_vertices = (nb_outline_points + nb_corners) * 2 + nb_outline_points * 2;
5892 nb_faces = nb_outline_points * 2 + nb_front_faces * 2;
5895 hr = D3DXCreateMeshFVF(nb_faces, nb_vertices, D3DXMESH_MANAGED,
5896 D3DFVF_XYZ | D3DFVF_NORMAL, device, &mesh);
5897 if (FAILED(hr))
5898 goto error;
5900 if (FAILED(hr = mesh->lpVtbl->LockVertexBuffer(mesh, 0, (void **)&vertices)))
5901 goto error;
5903 if (FAILED(hr = mesh->lpVtbl->LockIndexBuffer(mesh, 0, (void **)&faces)))
5904 goto error;
5906 /* convert 2D vertices and faces into 3D mesh */
5907 vertex_ptr = vertices;
5908 face_ptr = faces;
5909 if (extrusion == 0.0f) {
5910 f1 = 1;
5911 f2 = 2;
5912 } else {
5913 f1 = 2;
5914 f2 = 1;
5916 for (i = 0; i < textlen; i++)
5918 int j;
5919 int count;
5920 struct vertex *back_vertices;
5921 face *back_faces;
5923 /* side vertices and faces */
5924 for (j = 0; j < glyphs[i].outlines.count; j++)
5926 struct vertex *outline_vertices = vertex_ptr;
5927 struct outline *outline = &glyphs[i].outlines.items[j];
5928 int k;
5929 struct point2d *prevpt = &outline->items[outline->count - 1];
5930 struct point2d *pt = &outline->items[0];
5932 for (k = 1; k <= outline->count; k++)
5934 struct vertex vtx;
5935 struct point2d *nextpt = &outline->items[k % outline->count];
5936 WORD vtx_idx = vertex_ptr - vertices;
5937 D3DXVECTOR2 vec;
5939 if (pt->corner == POINTTYPE_CURVE_START)
5940 D3DXVec2Subtract(&vec, &pt->pos, &prevpt->pos);
5941 else if (pt->corner)
5942 D3DXVec2Subtract(&vec, &nextpt->pos, &pt->pos);
5943 else
5944 D3DXVec2Subtract(&vec, &nextpt->pos, &prevpt->pos);
5945 D3DXVec2Normalize(&vec, &vec);
5946 vtx.normal.x = -vec.y;
5947 vtx.normal.y = vec.x;
5948 vtx.normal.z = 0;
5950 vtx.position.x = pt->pos.x + glyphs[i].offset_x;
5951 vtx.position.y = pt->pos.y;
5952 vtx.position.z = 0;
5953 *vertex_ptr++ = vtx;
5955 vtx.position.z = -extrusion;
5956 *vertex_ptr++ = vtx;
5958 vtx.position.x = nextpt->pos.x + glyphs[i].offset_x;
5959 vtx.position.y = nextpt->pos.y;
5960 if (pt->corner && nextpt->corner && nextpt->corner != POINTTYPE_CURVE_END) {
5961 vtx.position.z = -extrusion;
5962 *vertex_ptr++ = vtx;
5963 vtx.position.z = 0;
5964 *vertex_ptr++ = vtx;
5966 (*face_ptr)[0] = vtx_idx;
5967 (*face_ptr)[1] = vtx_idx + 2;
5968 (*face_ptr)[2] = vtx_idx + 1;
5969 face_ptr++;
5971 (*face_ptr)[0] = vtx_idx;
5972 (*face_ptr)[1] = vtx_idx + 3;
5973 (*face_ptr)[2] = vtx_idx + 2;
5974 face_ptr++;
5975 } else {
5976 if (nextpt->corner) {
5977 if (nextpt->corner == POINTTYPE_CURVE_END) {
5978 D3DXVECTOR2 *nextpt2 = &outline->items[(k + 1) % outline->count].pos;
5979 D3DXVec2Subtract(&vec, nextpt2, &nextpt->pos);
5980 } else {
5981 D3DXVec2Subtract(&vec, &nextpt->pos, &pt->pos);
5983 D3DXVec2Normalize(&vec, &vec);
5984 vtx.normal.x = -vec.y;
5985 vtx.normal.y = vec.x;
5987 vtx.position.z = 0;
5988 *vertex_ptr++ = vtx;
5989 vtx.position.z = -extrusion;
5990 *vertex_ptr++ = vtx;
5993 (*face_ptr)[0] = vtx_idx;
5994 (*face_ptr)[1] = vtx_idx + 3;
5995 (*face_ptr)[2] = vtx_idx + 1;
5996 face_ptr++;
5998 (*face_ptr)[0] = vtx_idx;
5999 (*face_ptr)[1] = vtx_idx + 2;
6000 (*face_ptr)[2] = vtx_idx + 3;
6001 face_ptr++;
6004 prevpt = pt;
6005 pt = nextpt;
6007 if (!pt->corner) {
6008 *vertex_ptr++ = *outline_vertices++;
6009 *vertex_ptr++ = *outline_vertices++;
6013 /* back vertices and faces */
6014 back_faces = face_ptr;
6015 back_vertices = vertex_ptr;
6016 for (j = 0; j < glyphs[i].ordered_vertices.count; j++)
6018 D3DXVECTOR2 *pt = get_ordered_vertex(&glyphs[i], j);
6019 vertex_ptr->position.x = pt->x + glyphs[i].offset_x;
6020 vertex_ptr->position.y = pt->y;
6021 vertex_ptr->position.z = 0;
6022 vertex_ptr->normal.x = 0;
6023 vertex_ptr->normal.y = 0;
6024 vertex_ptr->normal.z = 1;
6025 vertex_ptr++;
6027 count = back_vertices - vertices;
6028 for (j = 0; j < glyphs[i].faces.count; j++)
6030 face *f = &glyphs[i].faces.items[j];
6031 (*face_ptr)[0] = (*f)[0] + count;
6032 (*face_ptr)[1] = (*f)[1] + count;
6033 (*face_ptr)[2] = (*f)[2] + count;
6034 face_ptr++;
6037 /* front vertices and faces */
6038 j = count = vertex_ptr - back_vertices;
6039 while (j--)
6041 vertex_ptr->position.x = back_vertices->position.x;
6042 vertex_ptr->position.y = back_vertices->position.y;
6043 vertex_ptr->position.z = -extrusion;
6044 vertex_ptr->normal.x = 0;
6045 vertex_ptr->normal.y = 0;
6046 vertex_ptr->normal.z = extrusion == 0.0f ? 1.0f : -1.0f;
6047 vertex_ptr++;
6048 back_vertices++;
6050 j = face_ptr - back_faces;
6051 while (j--)
6053 (*face_ptr)[0] = (*back_faces)[0] + count;
6054 (*face_ptr)[1] = (*back_faces)[f1] + count;
6055 (*face_ptr)[2] = (*back_faces)[f2] + count;
6056 face_ptr++;
6057 back_faces++;
6061 *mesh_ptr = mesh;
6062 hr = D3D_OK;
6063 error:
6064 if (mesh) {
6065 if (faces) mesh->lpVtbl->UnlockIndexBuffer(mesh);
6066 if (vertices) mesh->lpVtbl->UnlockVertexBuffer(mesh);
6067 if (hr != D3D_OK) mesh->lpVtbl->Release(mesh);
6069 if (glyphs) {
6070 for (i = 0; i < textlen; i++)
6072 int j;
6073 for (j = 0; j < glyphs[i].outlines.count; j++)
6074 HeapFree(GetProcessHeap(), 0, glyphs[i].outlines.items[j].items);
6075 HeapFree(GetProcessHeap(), 0, glyphs[i].outlines.items);
6076 HeapFree(GetProcessHeap(), 0, glyphs[i].faces.items);
6077 HeapFree(GetProcessHeap(), 0, glyphs[i].ordered_vertices.items);
6079 HeapFree(GetProcessHeap(), 0, glyphs);
6081 if (triangulations.items) {
6082 int i;
6083 for (i = 0; i < triangulations.count; i++)
6084 HeapFree(GetProcessHeap(), 0, triangulations.items[i].vertex_stack.items);
6085 HeapFree(GetProcessHeap(), 0, triangulations.items);
6087 HeapFree(GetProcessHeap(), 0, raw_outline);
6088 if (oldfont) SelectObject(hdc, oldfont);
6089 if (font) DeleteObject(font);
6091 return hr;
6094 HRESULT WINAPI D3DXValidMesh(ID3DXMesh *mesh, const DWORD *adjacency, ID3DXBuffer **errors_and_warnings)
6096 FIXME("(%p, %p, %p): stub\n", mesh, adjacency, *errors_and_warnings);
6098 return E_NOTIMPL;
6101 static BOOL weld_float1(void *to, void *from, FLOAT epsilon)
6103 FLOAT *v1 = to;
6104 FLOAT *v2 = from;
6106 if (fabsf(*v1 - *v2) <= epsilon)
6108 *v1 = *v2;
6110 return TRUE;
6113 return FALSE;
6116 static BOOL weld_float2(void *to, void *from, FLOAT epsilon)
6118 D3DXVECTOR2 *v1 = to;
6119 D3DXVECTOR2 *v2 = from;
6120 FLOAT diff_x = fabsf(v1->x - v2->x);
6121 FLOAT diff_y = fabsf(v1->y - v2->y);
6122 FLOAT max_abs_diff = max(diff_x, diff_y);
6124 if (max_abs_diff <= epsilon)
6126 memcpy(to, from, sizeof(D3DXVECTOR2));
6128 return TRUE;
6131 return FALSE;
6134 static BOOL weld_float3(void *to, void *from, FLOAT epsilon)
6136 D3DXVECTOR3 *v1 = to;
6137 D3DXVECTOR3 *v2 = from;
6138 FLOAT diff_x = fabsf(v1->x - v2->x);
6139 FLOAT diff_y = fabsf(v1->y - v2->y);
6140 FLOAT diff_z = fabsf(v1->z - v2->z);
6141 FLOAT max_abs_diff = max(diff_x, diff_y);
6142 max_abs_diff = max(diff_z, max_abs_diff);
6144 if (max_abs_diff <= epsilon)
6146 memcpy(to, from, sizeof(D3DXVECTOR3));
6148 return TRUE;
6151 return FALSE;
6154 static BOOL weld_float4(void *to, void *from, FLOAT epsilon)
6156 D3DXVECTOR4 *v1 = to;
6157 D3DXVECTOR4 *v2 = from;
6158 FLOAT diff_x = fabsf(v1->x - v2->x);
6159 FLOAT diff_y = fabsf(v1->y - v2->y);
6160 FLOAT diff_z = fabsf(v1->z - v2->z);
6161 FLOAT diff_w = fabsf(v1->w - v2->w);
6162 FLOAT max_abs_diff = fmax(diff_x, diff_y);
6163 max_abs_diff = max(diff_z, max_abs_diff);
6164 max_abs_diff = max(diff_w, max_abs_diff);
6166 if (max_abs_diff <= epsilon)
6168 memcpy(to, from, sizeof(D3DXVECTOR4));
6170 return TRUE;
6173 return FALSE;
6176 static BOOL weld_ubyte4(void *to, void *from, FLOAT epsilon)
6178 BYTE *b1 = to;
6179 BYTE *b2 = from;
6180 BYTE truncated_epsilon = (BYTE)epsilon;
6181 BYTE diff_x = b1[0] > b2[0] ? b1[0] - b2[0] : b2[0] - b1[0];
6182 BYTE diff_y = b1[1] > b2[1] ? b1[1] - b2[1] : b2[1] - b1[1];
6183 BYTE diff_z = b1[2] > b2[2] ? b1[2] - b2[2] : b2[2] - b1[2];
6184 BYTE diff_w = b1[3] > b2[3] ? b1[3] - b2[3] : b2[3] - b1[3];
6185 BYTE max_diff = max(diff_x, diff_y);
6186 max_diff = max(diff_z, max_diff);
6187 max_diff = max(diff_w, max_diff);
6189 if (max_diff <= truncated_epsilon)
6191 memcpy(to, from, 4 * sizeof(BYTE));
6193 return TRUE;
6196 return FALSE;
6199 static BOOL weld_ubyte4n(void *to, void *from, FLOAT epsilon)
6201 return weld_ubyte4(to, from, epsilon * UCHAR_MAX);
6204 static BOOL weld_d3dcolor(void *to, void *from, FLOAT epsilon)
6206 return weld_ubyte4n(to, from, epsilon);
6209 static BOOL weld_short2(void *to, void *from, FLOAT epsilon)
6211 SHORT *s1 = to;
6212 SHORT *s2 = from;
6213 SHORT truncated_epsilon = (SHORT)epsilon;
6214 SHORT diff_x = abs(s1[0] - s2[0]);
6215 SHORT diff_y = abs(s1[1] - s2[1]);
6216 SHORT max_abs_diff = max(diff_x, diff_y);
6218 if (max_abs_diff <= truncated_epsilon)
6220 memcpy(to, from, 2 * sizeof(SHORT));
6222 return TRUE;
6225 return FALSE;
6228 static BOOL weld_short2n(void *to, void *from, FLOAT epsilon)
6230 return weld_short2(to, from, epsilon * SHRT_MAX);
6233 static BOOL weld_short4(void *to, void *from, FLOAT epsilon)
6235 SHORT *s1 = to;
6236 SHORT *s2 = from;
6237 SHORT truncated_epsilon = (SHORT)epsilon;
6238 SHORT diff_x = abs(s1[0] - s2[0]);
6239 SHORT diff_y = abs(s1[1] - s2[1]);
6240 SHORT diff_z = abs(s1[2] - s2[2]);
6241 SHORT diff_w = abs(s1[3] - s2[3]);
6242 SHORT max_abs_diff = max(diff_x, diff_y);
6243 max_abs_diff = max(diff_z, max_abs_diff);
6244 max_abs_diff = max(diff_w, max_abs_diff);
6246 if (max_abs_diff <= truncated_epsilon)
6248 memcpy(to, from, 4 * sizeof(SHORT));
6250 return TRUE;
6253 return FALSE;
6256 static BOOL weld_short4n(void *to, void *from, FLOAT epsilon)
6258 return weld_short4(to, from, epsilon * SHRT_MAX);
6261 static BOOL weld_ushort2n(void *to, void *from, FLOAT epsilon)
6263 USHORT *s1 = to;
6264 USHORT *s2 = from;
6265 USHORT scaled_epsilon = (USHORT)(epsilon * USHRT_MAX);
6266 USHORT diff_x = s1[0] > s2[0] ? s1[0] - s2[0] : s2[0] - s1[0];
6267 USHORT diff_y = s1[1] > s2[1] ? s1[1] - s2[1] : s2[1] - s1[1];
6268 USHORT max_diff = max(diff_x, diff_y);
6270 if (max_diff <= scaled_epsilon)
6272 memcpy(to, from, 2 * sizeof(USHORT));
6274 return TRUE;
6277 return FALSE;
6280 static BOOL weld_ushort4n(void *to, void *from, FLOAT epsilon)
6282 USHORT *s1 = to;
6283 USHORT *s2 = from;
6284 USHORT scaled_epsilon = (USHORT)(epsilon * USHRT_MAX);
6285 USHORT diff_x = s1[0] > s2[0] ? s1[0] - s2[0] : s2[0] - s1[0];
6286 USHORT diff_y = s1[1] > s2[1] ? s1[1] - s2[1] : s2[1] - s1[1];
6287 USHORT diff_z = s1[2] > s2[2] ? s1[2] - s2[2] : s2[2] - s1[2];
6288 USHORT diff_w = s1[3] > s2[3] ? s1[3] - s2[3] : s2[3] - s1[3];
6289 USHORT max_diff = max(diff_x, diff_y);
6290 max_diff = max(diff_z, max_diff);
6291 max_diff = max(diff_w, max_diff);
6293 if (max_diff <= scaled_epsilon)
6295 memcpy(to, from, 4 * sizeof(USHORT));
6297 return TRUE;
6300 return FALSE;
6303 struct udec3
6305 UINT x;
6306 UINT y;
6307 UINT z;
6308 UINT w;
6311 static struct udec3 dword_to_udec3(DWORD d)
6313 struct udec3 v;
6315 v.x = d & 0x3ff;
6316 v.y = (d & 0xffc00) >> 10;
6317 v.z = (d & 0x3ff00000) >> 20;
6318 v.w = (d & 0xc0000000) >> 30;
6320 return v;
6323 static BOOL weld_udec3(void *to, void *from, FLOAT epsilon)
6325 DWORD *d1 = to;
6326 DWORD *d2 = from;
6327 struct udec3 v1 = dword_to_udec3(*d1);
6328 struct udec3 v2 = dword_to_udec3(*d2);
6329 UINT truncated_epsilon = (UINT)epsilon;
6330 UINT diff_x = v1.x > v2.x ? v1.x - v2.x : v2.x - v1.x;
6331 UINT diff_y = v1.y > v2.y ? v1.y - v2.y : v2.y - v1.y;
6332 UINT diff_z = v1.z > v2.z ? v1.z - v2.z : v2.z - v1.z;
6333 UINT diff_w = v1.w > v2.w ? v1.w - v2.w : v2.w - v1.w;
6334 UINT max_diff = max(diff_x, diff_y);
6335 max_diff = max(diff_z, max_diff);
6336 max_diff = max(diff_w, max_diff);
6338 if (max_diff <= truncated_epsilon)
6340 memcpy(to, from, sizeof(DWORD));
6342 return TRUE;
6345 return FALSE;
6348 struct dec3n
6350 INT x;
6351 INT y;
6352 INT z;
6353 INT w;
6356 static struct dec3n dword_to_dec3n(DWORD d)
6358 struct dec3n v;
6360 v.x = d & 0x3ff;
6361 v.y = (d & 0xffc00) >> 10;
6362 v.z = (d & 0x3ff00000) >> 20;
6363 v.w = (d & 0xc0000000) >> 30;
6365 return v;
6368 static BOOL weld_dec3n(void *to, void *from, FLOAT epsilon)
6370 const UINT MAX_DEC3N = 511;
6371 DWORD *d1 = to;
6372 DWORD *d2 = from;
6373 struct dec3n v1 = dword_to_dec3n(*d1);
6374 struct dec3n v2 = dword_to_dec3n(*d2);
6375 INT scaled_epsilon = (INT)(epsilon * MAX_DEC3N);
6376 INT diff_x = abs(v1.x - v2.x);
6377 INT diff_y = abs(v1.y - v2.y);
6378 INT diff_z = abs(v1.z - v2.z);
6379 INT diff_w = abs(v1.w - v2.w);
6380 INT max_abs_diff = max(diff_x, diff_y);
6381 max_abs_diff = max(diff_z, max_abs_diff);
6382 max_abs_diff = max(diff_w, max_abs_diff);
6384 if (max_abs_diff <= scaled_epsilon)
6386 memcpy(to, from, sizeof(DWORD));
6388 return TRUE;
6391 return FALSE;
6394 static BOOL weld_float16_2(void *to, void *from, FLOAT epsilon)
6396 D3DXFLOAT16 *v1_float16 = to;
6397 D3DXFLOAT16 *v2_float16 = from;
6398 FLOAT diff_x;
6399 FLOAT diff_y;
6400 FLOAT max_abs_diff;
6401 const UINT NUM_ELEM = 2;
6402 FLOAT v1[NUM_ELEM];
6403 FLOAT v2[NUM_ELEM];
6405 D3DXFloat16To32Array(v1, v1_float16, NUM_ELEM);
6406 D3DXFloat16To32Array(v2, v2_float16, NUM_ELEM);
6408 diff_x = fabsf(v1[0] - v2[0]);
6409 diff_y = fabsf(v1[1] - v2[1]);
6410 max_abs_diff = max(diff_x, diff_y);
6412 if (max_abs_diff <= epsilon)
6414 memcpy(to, from, NUM_ELEM * sizeof(D3DXFLOAT16));
6416 return TRUE;
6419 return FALSE;
6422 static BOOL weld_float16_4(void *to, void *from, FLOAT epsilon)
6424 D3DXFLOAT16 *v1_float16 = to;
6425 D3DXFLOAT16 *v2_float16 = from;
6426 FLOAT diff_x;
6427 FLOAT diff_y;
6428 FLOAT diff_z;
6429 FLOAT diff_w;
6430 FLOAT max_abs_diff;
6431 const UINT NUM_ELEM = 4;
6432 FLOAT v1[NUM_ELEM];
6433 FLOAT v2[NUM_ELEM];
6435 D3DXFloat16To32Array(v1, v1_float16, NUM_ELEM);
6436 D3DXFloat16To32Array(v2, v2_float16, NUM_ELEM);
6438 diff_x = fabsf(v1[0] - v2[0]);
6439 diff_y = fabsf(v1[1] - v2[1]);
6440 diff_z = fabsf(v1[2] - v2[2]);
6441 diff_w = fabsf(v1[3] - v2[3]);
6442 max_abs_diff = max(diff_x, diff_y);
6443 max_abs_diff = max(diff_z, max_abs_diff);
6444 max_abs_diff = max(diff_w, max_abs_diff);
6446 if (max_abs_diff <= epsilon)
6448 memcpy(to, from, NUM_ELEM * sizeof(D3DXFLOAT16));
6450 return TRUE;
6453 return FALSE;
6456 /* Sets the vertex components to the same value if they are within epsilon. */
6457 static BOOL weld_component(void *to, void *from, D3DDECLTYPE type, FLOAT epsilon)
6459 /* Quiet FIXMEs as this is in a loop with potentially thousand of iterations. */
6460 BOOL fixme_once_unused = FALSE;
6461 BOOL fixme_once_unknown = FALSE;
6463 switch (type)
6465 case D3DDECLTYPE_FLOAT1:
6466 return weld_float1(to, from, epsilon);
6468 case D3DDECLTYPE_FLOAT2:
6469 return weld_float2(to, from, epsilon);
6471 case D3DDECLTYPE_FLOAT3:
6472 return weld_float3(to, from, epsilon);
6474 case D3DDECLTYPE_FLOAT4:
6475 return weld_float4(to, from, epsilon);
6477 case D3DDECLTYPE_D3DCOLOR:
6478 return weld_d3dcolor(to, from, epsilon);
6480 case D3DDECLTYPE_UBYTE4:
6481 return weld_ubyte4(to, from, epsilon);
6483 case D3DDECLTYPE_SHORT2:
6484 return weld_short2(to, from, epsilon);
6486 case D3DDECLTYPE_SHORT4:
6487 return weld_short4(to, from, epsilon);
6489 case D3DDECLTYPE_UBYTE4N:
6490 return weld_ubyte4n(to, from, epsilon);
6492 case D3DDECLTYPE_SHORT2N:
6493 return weld_short2n(to, from, epsilon);
6495 case D3DDECLTYPE_SHORT4N:
6496 return weld_short4n(to, from, epsilon);
6498 case D3DDECLTYPE_USHORT2N:
6499 return weld_ushort2n(to, from, epsilon);
6501 case D3DDECLTYPE_USHORT4N:
6502 return weld_ushort4n(to, from, epsilon);
6504 case D3DDECLTYPE_UDEC3:
6505 return weld_udec3(to, from, epsilon);
6507 case D3DDECLTYPE_DEC3N:
6508 return weld_dec3n(to, from, epsilon);
6510 case D3DDECLTYPE_FLOAT16_2:
6511 return weld_float16_2(to, from, epsilon);
6513 case D3DDECLTYPE_FLOAT16_4:
6514 return weld_float16_4(to, from, epsilon);
6516 case D3DDECLTYPE_UNUSED:
6517 if (!fixme_once_unused++)
6518 FIXME("D3DDECLTYPE_UNUSED welding not implemented.\n");
6519 break;
6521 default:
6522 if (!fixme_once_unknown++)
6523 FIXME("Welding of unknown declaration type %d is not implemented.\n", type);
6524 break;
6527 return FALSE;
6530 static FLOAT get_component_epsilon(const D3DVERTEXELEMENT9 *decl_ptr, const D3DXWELDEPSILONS *epsilons)
6532 FLOAT epsilon = 0.0f;
6533 /* Quiet FIXMEs as this is in a loop with potentially thousand of iterations. */
6534 static BOOL fixme_once_blendindices = FALSE;
6535 static BOOL fixme_once_positiont = FALSE;
6536 static BOOL fixme_once_fog = FALSE;
6537 static BOOL fixme_once_depth = FALSE;
6538 static BOOL fixme_once_sample = FALSE;
6539 static BOOL fixme_once_unknown = FALSE;
6541 switch (decl_ptr->Usage)
6543 case D3DDECLUSAGE_POSITION:
6544 epsilon = epsilons->Position;
6545 break;
6546 case D3DDECLUSAGE_BLENDWEIGHT:
6547 epsilon = epsilons->BlendWeights;
6548 break;
6549 case D3DDECLUSAGE_NORMAL:
6550 epsilon = epsilons->Normals;
6551 break;
6552 case D3DDECLUSAGE_PSIZE:
6553 epsilon = epsilons->PSize;
6554 break;
6555 case D3DDECLUSAGE_TEXCOORD:
6557 BYTE usage_index = decl_ptr->UsageIndex;
6558 if (usage_index > 7)
6559 usage_index = 7;
6560 epsilon = epsilons->Texcoords[usage_index];
6561 break;
6563 case D3DDECLUSAGE_TANGENT:
6564 epsilon = epsilons->Tangent;
6565 break;
6566 case D3DDECLUSAGE_BINORMAL:
6567 epsilon = epsilons->Binormal;
6568 break;
6569 case D3DDECLUSAGE_TESSFACTOR:
6570 epsilon = epsilons->TessFactor;
6571 break;
6572 case D3DDECLUSAGE_COLOR:
6573 if (decl_ptr->UsageIndex == 0)
6574 epsilon = epsilons->Diffuse;
6575 else if (decl_ptr->UsageIndex == 1)
6576 epsilon = epsilons->Specular;
6577 else
6578 epsilon = 1e-6f;
6579 break;
6580 case D3DDECLUSAGE_BLENDINDICES:
6581 if (!fixme_once_blendindices++)
6582 FIXME("D3DDECLUSAGE_BLENDINDICES welding not implemented.\n");
6583 break;
6584 case D3DDECLUSAGE_POSITIONT:
6585 if (!fixme_once_positiont++)
6586 FIXME("D3DDECLUSAGE_POSITIONT welding not implemented.\n");
6587 break;
6588 case D3DDECLUSAGE_FOG:
6589 if (!fixme_once_fog++)
6590 FIXME("D3DDECLUSAGE_FOG welding not implemented.\n");
6591 break;
6592 case D3DDECLUSAGE_DEPTH:
6593 if (!fixme_once_depth++)
6594 FIXME("D3DDECLUSAGE_DEPTH welding not implemented.\n");
6595 break;
6596 case D3DDECLUSAGE_SAMPLE:
6597 if (!fixme_once_sample++)
6598 FIXME("D3DDECLUSAGE_SAMPLE welding not implemented.\n");
6599 break;
6600 default:
6601 if (!fixme_once_unknown++)
6602 FIXME("Unknown usage %x\n", decl_ptr->Usage);
6603 break;
6606 return epsilon;
6609 /* Helper function for reading a 32-bit index buffer. */
6610 static inline DWORD read_ib(void *index_buffer, BOOL indices_are_32bit,
6611 DWORD index)
6613 if (indices_are_32bit)
6615 DWORD *indices = index_buffer;
6616 return indices[index];
6618 else
6620 WORD *indices = index_buffer;
6621 return indices[index];
6625 /* Helper function for writing to a 32-bit index buffer. */
6626 static inline void write_ib(void *index_buffer, BOOL indices_are_32bit,
6627 DWORD index, DWORD value)
6629 if (indices_are_32bit)
6631 DWORD *indices = index_buffer;
6632 indices[index] = value;
6634 else
6636 WORD *indices = index_buffer;
6637 indices[index] = value;
6641 /*************************************************************************
6642 * D3DXWeldVertices (D3DX9_36.@)
6644 * Welds together similar vertices. The similarity between vert-
6645 * ices can be the position and other components such as
6646 * normal and color.
6648 * PARAMS
6649 * mesh [I] Mesh which vertices will be welded together.
6650 * flags [I] D3DXWELDEPSILONSFLAGS specifying how to weld.
6651 * epsilons [I] How similar a component needs to be for welding.
6652 * adjacency [I] Which faces are adjacent to other faces.
6653 * adjacency_out [O] Updated adjacency after welding.
6654 * face_remap_out [O] Which faces the old faces have been mapped to.
6655 * vertex_remap_out [O] Which vertices the old vertices have been mapped to.
6657 * RETURNS
6658 * Success: D3D_OK.
6659 * Failure: D3DERR_INVALIDCALL, E_OUTOFMEMORY.
6661 * BUGS
6662 * Attribute sorting not implemented.
6665 HRESULT WINAPI D3DXWeldVertices(ID3DXMesh *mesh, DWORD flags, const D3DXWELDEPSILONS *epsilons,
6666 const DWORD *adjacency, DWORD *adjacency_out, DWORD *face_remap_out, ID3DXBuffer **vertex_remap_out)
6668 DWORD *adjacency_generated = NULL;
6669 const DWORD *adjacency_ptr;
6670 DWORD *attributes = NULL;
6671 const FLOAT DEFAULT_EPSILON = 1.0e-6f;
6672 HRESULT hr;
6673 DWORD i;
6674 void *indices = NULL;
6675 BOOL indices_are_32bit = mesh->lpVtbl->GetOptions(mesh) & D3DXMESH_32BIT;
6676 DWORD optimize_flags;
6677 DWORD *point_reps = NULL;
6678 struct d3dx9_mesh *This = impl_from_ID3DXMesh(mesh);
6679 DWORD *vertex_face_map = NULL;
6680 ID3DXBuffer *vertex_remap = NULL;
6681 BYTE *vertices = NULL;
6683 TRACE("mesh %p, flags %#x, epsilons %p, adjacency %p, adjacency_out %p, face_remap_out %p, vertex_remap_out %p.\n",
6684 mesh, flags, epsilons, adjacency, adjacency_out, face_remap_out, vertex_remap_out);
6686 if (flags == 0)
6688 WARN("No flags is undefined. Using D3DXWELDEPSILONS_WELDPARTIALMATCHES instead.\n");
6689 flags = D3DXWELDEPSILONS_WELDPARTIALMATCHES;
6692 if (adjacency) /* Use supplied adjacency. */
6694 adjacency_ptr = adjacency;
6696 else /* Adjacency has to be generated. */
6698 adjacency_generated = HeapAlloc(GetProcessHeap(), 0, 3 * This->numfaces * sizeof(*adjacency_generated));
6699 if (!adjacency_generated)
6701 ERR("Couldn't allocate memory for adjacency_generated.\n");
6702 hr = E_OUTOFMEMORY;
6703 goto cleanup;
6705 hr = mesh->lpVtbl->GenerateAdjacency(mesh, DEFAULT_EPSILON, adjacency_generated);
6706 if (FAILED(hr))
6708 ERR("Couldn't generate adjacency.\n");
6709 goto cleanup;
6711 adjacency_ptr = adjacency_generated;
6714 /* Point representation says which vertices can be replaced. */
6715 point_reps = HeapAlloc(GetProcessHeap(), 0, This->numvertices * sizeof(*point_reps));
6716 if (!point_reps)
6718 hr = E_OUTOFMEMORY;
6719 ERR("Couldn't allocate memory for point_reps.\n");
6720 goto cleanup;
6722 hr = mesh->lpVtbl->ConvertAdjacencyToPointReps(mesh, adjacency_ptr, point_reps);
6723 if (FAILED(hr))
6725 ERR("ConvertAdjacencyToPointReps failed.\n");
6726 goto cleanup;
6729 hr = mesh->lpVtbl->LockIndexBuffer(mesh, 0, &indices);
6730 if (FAILED(hr))
6732 ERR("Couldn't lock index buffer.\n");
6733 goto cleanup;
6736 hr = mesh->lpVtbl->LockAttributeBuffer(mesh, 0, &attributes);
6737 if (FAILED(hr))
6739 ERR("Couldn't lock attribute buffer.\n");
6740 goto cleanup;
6742 vertex_face_map = HeapAlloc(GetProcessHeap(), 0, This->numvertices * sizeof(*vertex_face_map));
6743 if (!vertex_face_map)
6745 hr = E_OUTOFMEMORY;
6746 ERR("Couldn't allocate memory for vertex_face_map.\n");
6747 goto cleanup;
6749 /* Build vertex face map, so that a vertex's face can be looked up. */
6750 for (i = 0; i < This->numfaces; i++)
6752 DWORD j;
6753 for (j = 0; j < 3; j++)
6755 DWORD index = read_ib(indices, indices_are_32bit, 3*i + j);
6756 vertex_face_map[index] = i;
6760 if (flags & D3DXWELDEPSILONS_WELDPARTIALMATCHES)
6762 hr = mesh->lpVtbl->LockVertexBuffer(mesh, 0, (void**)&vertices);
6763 if (FAILED(hr))
6765 ERR("Couldn't lock vertex buffer.\n");
6766 goto cleanup;
6768 /* For each vertex that can be removed, compare its vertex components
6769 * with the vertex components from the vertex that can replace it. A
6770 * vertex is only fully replaced if all the components match and the
6771 * flag D3DXWELDEPSILONS_DONOTREMOVEVERTICES is not set, and they
6772 * belong to the same attribute group. Otherwise the vertex components
6773 * that are within epsilon are set to the same value.
6775 for (i = 0; i < 3 * This->numfaces; i++)
6777 D3DVERTEXELEMENT9 *decl_ptr;
6778 DWORD vertex_size = mesh->lpVtbl->GetNumBytesPerVertex(mesh);
6779 DWORD num_vertex_components;
6780 INT matches = 0;
6781 BOOL all_match;
6782 DWORD index = read_ib(indices, indices_are_32bit, i);
6784 for (decl_ptr = This->cached_declaration, num_vertex_components = 0; decl_ptr->Stream != 0xFF; decl_ptr++, num_vertex_components++)
6786 BYTE *to = &vertices[vertex_size*index + decl_ptr->Offset];
6787 BYTE *from = &vertices[vertex_size*point_reps[index] + decl_ptr->Offset];
6788 FLOAT epsilon = get_component_epsilon(decl_ptr, epsilons);
6790 /* Don't weld self */
6791 if (index == point_reps[index])
6793 matches++;
6794 continue;
6797 if (weld_component(to, from, decl_ptr->Type, epsilon))
6798 matches++;
6801 all_match = (num_vertex_components == matches);
6802 if (all_match && !(flags & D3DXWELDEPSILONS_DONOTREMOVEVERTICES))
6804 DWORD to_face = vertex_face_map[index];
6805 DWORD from_face = vertex_face_map[point_reps[index]];
6806 if(attributes[to_face] != attributes[from_face] && !(flags & D3DXWELDEPSILONS_DONOTSPLIT))
6807 continue;
6808 write_ib(indices, indices_are_32bit, i, point_reps[index]);
6811 mesh->lpVtbl->UnlockVertexBuffer(mesh);
6812 vertices = NULL;
6814 else if (flags & D3DXWELDEPSILONS_WELDALL)
6816 for (i = 0; i < 3 * This->numfaces; i++)
6818 DWORD index = read_ib(indices, indices_are_32bit, i);
6819 DWORD to_face = vertex_face_map[index];
6820 DWORD from_face = vertex_face_map[point_reps[index]];
6821 if(attributes[to_face] != attributes[from_face] && !(flags & D3DXWELDEPSILONS_DONOTSPLIT))
6822 continue;
6823 write_ib(indices, indices_are_32bit, i, point_reps[index]);
6826 mesh->lpVtbl->UnlockAttributeBuffer(mesh);
6827 attributes = NULL;
6828 mesh->lpVtbl->UnlockIndexBuffer(mesh);
6829 indices = NULL;
6831 /* Compact mesh using OptimizeInplace */
6832 optimize_flags = D3DXMESHOPT_COMPACT;
6833 hr = mesh->lpVtbl->OptimizeInplace(mesh, optimize_flags, adjacency_ptr, adjacency_out, face_remap_out, vertex_remap_out);
6834 if (FAILED(hr))
6836 ERR("Couldn't compact mesh.\n");
6837 goto cleanup;
6840 hr = D3D_OK;
6841 cleanup:
6842 HeapFree(GetProcessHeap(), 0, adjacency_generated);
6843 HeapFree(GetProcessHeap(), 0, point_reps);
6844 HeapFree(GetProcessHeap(), 0, vertex_face_map);
6845 if (attributes) mesh->lpVtbl->UnlockAttributeBuffer(mesh);
6846 if (indices) mesh->lpVtbl->UnlockIndexBuffer(mesh);
6847 if (vertex_remap) ID3DXBuffer_Release(vertex_remap);
6848 if (vertices) mesh->lpVtbl->UnlockVertexBuffer(mesh);
6850 return hr;
6853 /*************************************************************************
6854 * D3DXOptimizeFaces (D3DX9_36.@)
6856 * Re-orders the faces so the vertex cache is used optimally.
6858 * PARAMS
6859 * indices [I] Pointer to an index buffer belonging to a mesh.
6860 * num_faces [I] Number of faces in the mesh.
6861 * num_vertices [I] Number of vertices in the mesh.
6862 * indices_are_32bit [I] Specifies whether indices are 32- or 16-bit.
6863 * face_remap [I/O] The new order the faces should be drawn in.
6865 * RETURNS
6866 * Success: D3D_OK.
6867 * Failure: D3DERR_INVALIDCALL.
6869 * BUGS
6870 * The face re-ordering does not use the vertex cache optimally.
6873 HRESULT WINAPI D3DXOptimizeFaces(const void *indices, UINT num_faces,
6874 UINT num_vertices, BOOL indices_are_32bit, DWORD *face_remap)
6876 UINT i;
6877 UINT j = num_faces - 1;
6878 UINT limit_16_bit = 2 << 15; /* According to MSDN */
6879 HRESULT hr = D3D_OK;
6881 FIXME("indices %p, num_faces %u, num_vertices %u, indices_are_32bit %#x, face_remap %p semi-stub. "
6882 "Face order will not be optimal.\n",
6883 indices, num_faces, num_vertices, indices_are_32bit, face_remap);
6885 if (!indices_are_32bit && num_faces >= limit_16_bit)
6887 WARN("Number of faces must be less than %d when using 16-bit indices.\n",
6888 limit_16_bit);
6889 hr = D3DERR_INVALIDCALL;
6890 goto error;
6893 if (!face_remap)
6895 WARN("Face remap pointer is NULL.\n");
6896 hr = D3DERR_INVALIDCALL;
6897 goto error;
6900 /* The faces are drawn in reverse order for simple meshes. This ordering
6901 * is not optimal for complicated meshes, but will not break anything
6902 * either. The ordering should be changed to take advantage of the vertex
6903 * cache on the graphics card.
6905 * TODO Re-order to take advantage of vertex cache.
6907 for (i = 0; i < num_faces; i++)
6909 face_remap[i] = j--;
6912 return D3D_OK;
6914 error:
6915 return hr;