d3d8/tests: Use a separate device for alpha_test().
[wine.git] / dlls / d3dx9_36 / mesh.c
blob1a2b020f1243703e8c1c729ffaf6e2a650a959e9
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 DWORD *attrib_buffer, DWORD **sorted_attrib_buffer, DWORD **face_remap)
1615 DWORD **sorted_attrib_ptr_buffer = NULL;
1616 DWORD i;
1618 sorted_attrib_ptr_buffer = HeapAlloc(GetProcessHeap(), 0, This->numfaces * sizeof(*sorted_attrib_ptr_buffer));
1619 if (!sorted_attrib_ptr_buffer)
1620 return E_OUTOFMEMORY;
1622 *face_remap = HeapAlloc(GetProcessHeap(), 0, This->numfaces * sizeof(**face_remap));
1623 if (!*face_remap)
1625 HeapFree(GetProcessHeap(), 0, sorted_attrib_ptr_buffer);
1626 return E_OUTOFMEMORY;
1629 for (i = 0; i < This->numfaces; i++)
1630 sorted_attrib_ptr_buffer[i] = &attrib_buffer[i];
1631 qsort(sorted_attrib_ptr_buffer, This->numfaces, sizeof(*sorted_attrib_ptr_buffer),
1632 (int(*)(const void *, const void *))attrib_entry_compare);
1634 for (i = 0; i < This->numfaces; i++)
1636 DWORD old_face = sorted_attrib_ptr_buffer[i] - attrib_buffer;
1637 (*face_remap)[old_face] = i;
1640 /* overwrite sorted_attrib_ptr_buffer with the values themselves */
1641 *sorted_attrib_buffer = (DWORD*)sorted_attrib_ptr_buffer;
1642 for (i = 0; i < This->numfaces; i++)
1643 (*sorted_attrib_buffer)[(*face_remap)[i]] = attrib_buffer[i];
1645 return D3D_OK;
1648 static HRESULT WINAPI d3dx9_mesh_OptimizeInplace(ID3DXMesh *iface, DWORD flags, const DWORD *adjacency_in,
1649 DWORD *adjacency_out, DWORD *face_remap_out, ID3DXBuffer **vertex_remap_out)
1651 struct d3dx9_mesh *This = impl_from_ID3DXMesh(iface);
1652 void *indices = NULL;
1653 DWORD *attrib_buffer = NULL;
1654 HRESULT hr;
1655 ID3DXBuffer *vertex_remap = NULL;
1656 DWORD *face_remap = NULL; /* old -> new mapping */
1657 DWORD *dword_indices = NULL;
1658 DWORD new_num_vertices = 0;
1659 DWORD new_num_alloc_vertices = 0;
1660 IDirect3DVertexBuffer9 *vertex_buffer = NULL;
1661 DWORD *sorted_attrib_buffer = NULL;
1662 DWORD i;
1664 TRACE("iface %p, flags %#x, adjacency_in %p, adjacency_out %p, face_remap_out %p, vertex_remap_out %p.\n",
1665 iface, flags, adjacency_in, adjacency_out, face_remap_out, vertex_remap_out);
1667 if (!flags)
1668 return D3DERR_INVALIDCALL;
1669 if (!adjacency_in && (flags & (D3DXMESHOPT_VERTEXCACHE | D3DXMESHOPT_STRIPREORDER)))
1670 return D3DERR_INVALIDCALL;
1671 if ((flags & (D3DXMESHOPT_VERTEXCACHE | D3DXMESHOPT_STRIPREORDER)) == (D3DXMESHOPT_VERTEXCACHE | D3DXMESHOPT_STRIPREORDER))
1672 return D3DERR_INVALIDCALL;
1674 if (flags & (D3DXMESHOPT_VERTEXCACHE | D3DXMESHOPT_STRIPREORDER))
1676 if (flags & D3DXMESHOPT_VERTEXCACHE)
1677 FIXME("D3DXMESHOPT_VERTEXCACHE not implemented.\n");
1678 if (flags & D3DXMESHOPT_STRIPREORDER)
1679 FIXME("D3DXMESHOPT_STRIPREORDER not implemented.\n");
1680 return E_NOTIMPL;
1683 hr = iface->lpVtbl->LockIndexBuffer(iface, 0, &indices);
1684 if (FAILED(hr)) goto cleanup;
1686 dword_indices = HeapAlloc(GetProcessHeap(), 0, This->numfaces * 3 * sizeof(DWORD));
1687 if (!dword_indices) return E_OUTOFMEMORY;
1688 if (This->options & D3DXMESH_32BIT) {
1689 memcpy(dword_indices, indices, This->numfaces * 3 * sizeof(DWORD));
1690 } else {
1691 WORD *word_indices = indices;
1692 for (i = 0; i < This->numfaces * 3; i++)
1693 dword_indices[i] = *word_indices++;
1696 if ((flags & (D3DXMESHOPT_COMPACT | D3DXMESHOPT_IGNOREVERTS | D3DXMESHOPT_ATTRSORT)) == D3DXMESHOPT_COMPACT)
1698 new_num_alloc_vertices = This->numvertices;
1699 hr = compact_mesh(This, dword_indices, &new_num_vertices, &vertex_remap);
1700 if (FAILED(hr)) goto cleanup;
1701 } else if (flags & D3DXMESHOPT_ATTRSORT) {
1702 if (!(flags & D3DXMESHOPT_IGNOREVERTS))
1704 FIXME("D3DXMESHOPT_ATTRSORT vertex reordering not implemented.\n");
1705 hr = E_NOTIMPL;
1706 goto cleanup;
1709 hr = iface->lpVtbl->LockAttributeBuffer(iface, 0, &attrib_buffer);
1710 if (FAILED(hr)) goto cleanup;
1712 hr = remap_faces_for_attrsort(This, dword_indices, attrib_buffer, &sorted_attrib_buffer, &face_remap);
1713 if (FAILED(hr)) goto cleanup;
1716 if (vertex_remap)
1718 /* reorder the vertices using vertex_remap */
1719 D3DVERTEXBUFFER_DESC vertex_desc;
1720 DWORD *vertex_remap_ptr = ID3DXBuffer_GetBufferPointer(vertex_remap);
1721 DWORD vertex_size = iface->lpVtbl->GetNumBytesPerVertex(iface);
1722 BYTE *orig_vertices;
1723 BYTE *new_vertices;
1725 hr = IDirect3DVertexBuffer9_GetDesc(This->vertex_buffer, &vertex_desc);
1726 if (FAILED(hr)) goto cleanup;
1728 hr = IDirect3DDevice9_CreateVertexBuffer(This->device, new_num_alloc_vertices * vertex_size,
1729 vertex_desc.Usage, This->fvf, vertex_desc.Pool, &vertex_buffer, NULL);
1730 if (FAILED(hr)) goto cleanup;
1732 hr = IDirect3DVertexBuffer9_Lock(This->vertex_buffer, 0, 0, (void**)&orig_vertices, D3DLOCK_READONLY);
1733 if (FAILED(hr)) goto cleanup;
1735 hr = IDirect3DVertexBuffer9_Lock(vertex_buffer, 0, 0, (void**)&new_vertices, 0);
1736 if (FAILED(hr)) {
1737 IDirect3DVertexBuffer9_Unlock(This->vertex_buffer);
1738 goto cleanup;
1741 for (i = 0; i < new_num_vertices; i++)
1742 memcpy(new_vertices + i * vertex_size, orig_vertices + vertex_remap_ptr[i] * vertex_size, vertex_size);
1744 IDirect3DVertexBuffer9_Unlock(This->vertex_buffer);
1745 IDirect3DVertexBuffer9_Unlock(vertex_buffer);
1746 } else if (vertex_remap_out) {
1747 DWORD *vertex_remap_ptr;
1749 hr = D3DXCreateBuffer(This->numvertices * sizeof(DWORD), &vertex_remap);
1750 if (FAILED(hr)) goto cleanup;
1751 vertex_remap_ptr = ID3DXBuffer_GetBufferPointer(vertex_remap);
1752 for (i = 0; i < This->numvertices; i++)
1753 *vertex_remap_ptr++ = i;
1756 if (flags & D3DXMESHOPT_ATTRSORT)
1758 D3DXATTRIBUTERANGE *attrib_table;
1759 DWORD attrib_table_size;
1761 attrib_table_size = count_attributes(sorted_attrib_buffer, This->numfaces);
1762 attrib_table = HeapAlloc(GetProcessHeap(), 0, attrib_table_size * sizeof(*attrib_table));
1763 if (!attrib_table) {
1764 hr = E_OUTOFMEMORY;
1765 goto cleanup;
1768 memcpy(attrib_buffer, sorted_attrib_buffer, This->numfaces * sizeof(*attrib_buffer));
1770 /* reorder the indices using face_remap */
1771 if (This->options & D3DXMESH_32BIT) {
1772 for (i = 0; i < This->numfaces; i++)
1773 memcpy((DWORD*)indices + face_remap[i] * 3, dword_indices + i * 3, 3 * sizeof(DWORD));
1774 } else {
1775 WORD *word_indices = indices;
1776 for (i = 0; i < This->numfaces; i++) {
1777 DWORD new_pos = face_remap[i] * 3;
1778 DWORD old_pos = i * 3;
1779 word_indices[new_pos++] = dword_indices[old_pos++];
1780 word_indices[new_pos++] = dword_indices[old_pos++];
1781 word_indices[new_pos] = dword_indices[old_pos];
1785 fill_attribute_table(attrib_buffer, This->numfaces, indices,
1786 This->options & D3DXMESH_32BIT, attrib_table);
1788 HeapFree(GetProcessHeap(), 0, This->attrib_table);
1789 This->attrib_table = attrib_table;
1790 This->attrib_table_size = attrib_table_size;
1791 } else {
1792 if (This->options & D3DXMESH_32BIT) {
1793 memcpy(indices, dword_indices, This->numfaces * 3 * sizeof(DWORD));
1794 } else {
1795 WORD *word_indices = indices;
1796 for (i = 0; i < This->numfaces * 3; i++)
1797 *word_indices++ = dword_indices[i];
1801 if (adjacency_out) {
1802 if (face_remap) {
1803 for (i = 0; i < This->numfaces; i++) {
1804 DWORD old_pos = i * 3;
1805 DWORD new_pos = face_remap[i] * 3;
1806 adjacency_out[new_pos++] = face_remap[adjacency_in[old_pos++]];
1807 adjacency_out[new_pos++] = face_remap[adjacency_in[old_pos++]];
1808 adjacency_out[new_pos++] = face_remap[adjacency_in[old_pos++]];
1810 } else {
1811 memcpy(adjacency_out, adjacency_in, This->numfaces * 3 * sizeof(*adjacency_out));
1814 if (face_remap_out) {
1815 if (face_remap) {
1816 for (i = 0; i < This->numfaces; i++)
1817 face_remap_out[face_remap[i]] = i;
1818 } else {
1819 for (i = 0; i < This->numfaces; i++)
1820 face_remap_out[i] = i;
1823 if (vertex_remap_out)
1824 *vertex_remap_out = vertex_remap;
1825 vertex_remap = NULL;
1827 if (vertex_buffer) {
1828 IDirect3DVertexBuffer9_Release(This->vertex_buffer);
1829 This->vertex_buffer = vertex_buffer;
1830 vertex_buffer = NULL;
1831 This->numvertices = new_num_vertices;
1834 hr = D3D_OK;
1835 cleanup:
1836 HeapFree(GetProcessHeap(), 0, sorted_attrib_buffer);
1837 HeapFree(GetProcessHeap(), 0, face_remap);
1838 HeapFree(GetProcessHeap(), 0, dword_indices);
1839 if (vertex_remap) ID3DXBuffer_Release(vertex_remap);
1840 if (vertex_buffer) IDirect3DVertexBuffer9_Release(vertex_buffer);
1841 if (attrib_buffer) iface->lpVtbl->UnlockAttributeBuffer(iface);
1842 if (indices) iface->lpVtbl->UnlockIndexBuffer(iface);
1843 return hr;
1846 static HRESULT WINAPI d3dx9_mesh_SetAttributeTable(ID3DXMesh *iface,
1847 const D3DXATTRIBUTERANGE *attrib_table, DWORD attrib_table_size)
1849 struct d3dx9_mesh *mesh = impl_from_ID3DXMesh(iface);
1850 D3DXATTRIBUTERANGE *new_table = NULL;
1852 TRACE("iface %p, attrib_table %p, attrib_table_size %u.\n", iface, attrib_table, attrib_table_size);
1854 if (attrib_table_size) {
1855 size_t size = attrib_table_size * sizeof(*attrib_table);
1857 new_table = HeapAlloc(GetProcessHeap(), 0, size);
1858 if (!new_table)
1859 return E_OUTOFMEMORY;
1861 CopyMemory(new_table, attrib_table, size);
1862 } else if (attrib_table) {
1863 return D3DERR_INVALIDCALL;
1865 HeapFree(GetProcessHeap(), 0, mesh->attrib_table);
1866 mesh->attrib_table = new_table;
1867 mesh->attrib_table_size = attrib_table_size;
1869 return D3D_OK;
1872 static const struct ID3DXMeshVtbl D3DXMesh_Vtbl =
1874 d3dx9_mesh_QueryInterface,
1875 d3dx9_mesh_AddRef,
1876 d3dx9_mesh_Release,
1877 d3dx9_mesh_DrawSubset,
1878 d3dx9_mesh_GetNumFaces,
1879 d3dx9_mesh_GetNumVertices,
1880 d3dx9_mesh_GetFVF,
1881 d3dx9_mesh_GetDeclaration,
1882 d3dx9_mesh_GetNumBytesPerVertex,
1883 d3dx9_mesh_GetOptions,
1884 d3dx9_mesh_GetDevice,
1885 d3dx9_mesh_CloneMeshFVF,
1886 d3dx9_mesh_CloneMesh,
1887 d3dx9_mesh_GetVertexBuffer,
1888 d3dx9_mesh_GetIndexBuffer,
1889 d3dx9_mesh_LockVertexBuffer,
1890 d3dx9_mesh_UnlockVertexBuffer,
1891 d3dx9_mesh_LockIndexBuffer,
1892 d3dx9_mesh_UnlockIndexBuffer,
1893 d3dx9_mesh_GetAttributeTable,
1894 d3dx9_mesh_ConvertPointRepsToAdjacency,
1895 d3dx9_mesh_ConvertAdjacencyToPointReps,
1896 d3dx9_mesh_GenerateAdjacency,
1897 d3dx9_mesh_UpdateSemantics,
1898 d3dx9_mesh_LockAttributeBuffer,
1899 d3dx9_mesh_UnlockAttributeBuffer,
1900 d3dx9_mesh_Optimize,
1901 d3dx9_mesh_OptimizeInplace,
1902 d3dx9_mesh_SetAttributeTable,
1906 /* Algorithm taken from the article: An Efficient and Robust Ray-Box Intersection Algorithm
1907 Amy Williams University of Utah
1908 Steve Barrus University of Utah
1909 R. Keith Morley University of Utah
1910 Peter Shirley University of Utah
1912 International Conference on Computer Graphics and Interactive Techniques archive
1913 ACM SIGGRAPH 2005 Courses
1914 Los Angeles, California
1916 This algorithm is free of patents or of copyrights, as confirmed by Peter Shirley himself.
1918 Algorithm: Consider the box as the intersection of three slabs. Clip the ray
1919 against each slab, if there's anything left of the ray after we're
1920 done we've got an intersection of the ray with the box. */
1921 BOOL WINAPI D3DXBoxBoundProbe(const D3DXVECTOR3 *pmin, const D3DXVECTOR3 *pmax,
1922 const D3DXVECTOR3 *prayposition, const D3DXVECTOR3 *praydirection)
1924 FLOAT div, tmin, tmax, tymin, tymax, tzmin, tzmax;
1926 div = 1.0f / praydirection->x;
1927 if ( div >= 0.0f )
1929 tmin = ( pmin->x - prayposition->x ) * div;
1930 tmax = ( pmax->x - prayposition->x ) * div;
1932 else
1934 tmin = ( pmax->x - prayposition->x ) * div;
1935 tmax = ( pmin->x - prayposition->x ) * div;
1938 if ( tmax < 0.0f ) return FALSE;
1940 div = 1.0f / praydirection->y;
1941 if ( div >= 0.0f )
1943 tymin = ( pmin->y - prayposition->y ) * div;
1944 tymax = ( pmax->y - prayposition->y ) * div;
1946 else
1948 tymin = ( pmax->y - prayposition->y ) * div;
1949 tymax = ( pmin->y - prayposition->y ) * div;
1952 if ( ( tymax < 0.0f ) || ( tmin > tymax ) || ( tymin > tmax ) ) return FALSE;
1954 if ( tymin > tmin ) tmin = tymin;
1955 if ( tymax < tmax ) tmax = tymax;
1957 div = 1.0f / praydirection->z;
1958 if ( div >= 0.0f )
1960 tzmin = ( pmin->z - prayposition->z ) * div;
1961 tzmax = ( pmax->z - prayposition->z ) * div;
1963 else
1965 tzmin = ( pmax->z - prayposition->z ) * div;
1966 tzmax = ( pmin->z - prayposition->z ) * div;
1969 if ( (tzmax < 0.0f ) || ( tmin > tzmax ) || ( tzmin > tmax ) ) return FALSE;
1971 return TRUE;
1974 HRESULT WINAPI D3DXComputeBoundingBox(const D3DXVECTOR3 *pfirstposition,
1975 DWORD numvertices, DWORD dwstride, D3DXVECTOR3 *pmin, D3DXVECTOR3 *pmax)
1977 D3DXVECTOR3 vec;
1978 unsigned int i;
1980 if( !pfirstposition || !pmin || !pmax ) return D3DERR_INVALIDCALL;
1982 *pmin = *pfirstposition;
1983 *pmax = *pmin;
1985 for(i=0; i<numvertices; i++)
1987 vec = *( (const D3DXVECTOR3*)((const char*)pfirstposition + dwstride * i) );
1989 if ( vec.x < pmin->x ) pmin->x = vec.x;
1990 if ( vec.x > pmax->x ) pmax->x = vec.x;
1992 if ( vec.y < pmin->y ) pmin->y = vec.y;
1993 if ( vec.y > pmax->y ) pmax->y = vec.y;
1995 if ( vec.z < pmin->z ) pmin->z = vec.z;
1996 if ( vec.z > pmax->z ) pmax->z = vec.z;
1999 return D3D_OK;
2002 HRESULT WINAPI D3DXComputeBoundingSphere(const D3DXVECTOR3 *pfirstposition,
2003 DWORD numvertices, DWORD dwstride, D3DXVECTOR3 *pcenter, float *pradius)
2005 D3DXVECTOR3 temp;
2006 FLOAT d;
2007 unsigned int i;
2009 if( !pfirstposition || !pcenter || !pradius ) return D3DERR_INVALIDCALL;
2011 temp.x = 0.0f;
2012 temp.y = 0.0f;
2013 temp.z = 0.0f;
2014 *pradius = 0.0f;
2016 for(i=0; i<numvertices; i++)
2017 D3DXVec3Add(&temp, &temp, (const D3DXVECTOR3*)((const char*)pfirstposition + dwstride * i));
2019 D3DXVec3Scale(pcenter, &temp, 1.0f / numvertices);
2021 for(i=0; i<numvertices; i++)
2023 d = D3DXVec3Length(D3DXVec3Subtract(&temp, (const D3DXVECTOR3*)((const char*)pfirstposition + dwstride * i), pcenter));
2024 if ( d > *pradius ) *pradius = d;
2026 return D3D_OK;
2029 static void append_decl_element(D3DVERTEXELEMENT9 *declaration, UINT *idx, UINT *offset,
2030 D3DDECLTYPE type, D3DDECLUSAGE usage, UINT usage_idx)
2032 declaration[*idx].Stream = 0;
2033 declaration[*idx].Offset = *offset;
2034 declaration[*idx].Type = type;
2035 declaration[*idx].Method = D3DDECLMETHOD_DEFAULT;
2036 declaration[*idx].Usage = usage;
2037 declaration[*idx].UsageIndex = usage_idx;
2039 *offset += d3dx_decltype_size[type];
2040 ++(*idx);
2043 /*************************************************************************
2044 * D3DXDeclaratorFromFVF
2046 HRESULT WINAPI D3DXDeclaratorFromFVF(DWORD fvf, D3DVERTEXELEMENT9 declaration[MAX_FVF_DECL_SIZE])
2048 static const D3DVERTEXELEMENT9 end_element = D3DDECL_END();
2049 DWORD tex_count = (fvf & D3DFVF_TEXCOUNT_MASK) >> D3DFVF_TEXCOUNT_SHIFT;
2050 unsigned int offset = 0;
2051 unsigned int idx = 0;
2052 unsigned int i;
2054 TRACE("fvf %#x, declaration %p.\n", fvf, declaration);
2056 if (fvf & (D3DFVF_RESERVED0 | D3DFVF_RESERVED2)) return D3DERR_INVALIDCALL;
2058 if (fvf & D3DFVF_POSITION_MASK)
2060 BOOL has_blend = (fvf & D3DFVF_XYZB5) >= D3DFVF_XYZB1;
2061 DWORD blend_count = 1 + (((fvf & D3DFVF_XYZB5) - D3DFVF_XYZB1) >> 1);
2062 BOOL has_blend_idx = (fvf & D3DFVF_LASTBETA_D3DCOLOR) || (fvf & D3DFVF_LASTBETA_UBYTE4);
2064 if (has_blend_idx) --blend_count;
2066 if ((fvf & D3DFVF_POSITION_MASK) == D3DFVF_XYZW
2067 || (has_blend && blend_count > 4))
2068 return D3DERR_INVALIDCALL;
2070 if ((fvf & D3DFVF_POSITION_MASK) == D3DFVF_XYZRHW)
2071 append_decl_element(declaration, &idx, &offset, D3DDECLTYPE_FLOAT4, D3DDECLUSAGE_POSITIONT, 0);
2072 else
2073 append_decl_element(declaration, &idx, &offset, D3DDECLTYPE_FLOAT3, D3DDECLUSAGE_POSITION, 0);
2075 if (has_blend)
2077 switch (blend_count)
2079 case 0:
2080 break;
2081 case 1:
2082 append_decl_element(declaration, &idx, &offset, D3DDECLTYPE_FLOAT1, D3DDECLUSAGE_BLENDWEIGHT, 0);
2083 break;
2084 case 2:
2085 append_decl_element(declaration, &idx, &offset, D3DDECLTYPE_FLOAT2, D3DDECLUSAGE_BLENDWEIGHT, 0);
2086 break;
2087 case 3:
2088 append_decl_element(declaration, &idx, &offset, D3DDECLTYPE_FLOAT3, D3DDECLUSAGE_BLENDWEIGHT, 0);
2089 break;
2090 case 4:
2091 append_decl_element(declaration, &idx, &offset, D3DDECLTYPE_FLOAT4, D3DDECLUSAGE_BLENDWEIGHT, 0);
2092 break;
2093 default:
2094 ERR("Invalid blend count %u.\n", blend_count);
2095 break;
2098 if (has_blend_idx)
2100 if (fvf & D3DFVF_LASTBETA_UBYTE4)
2101 append_decl_element(declaration, &idx, &offset, D3DDECLTYPE_UBYTE4, D3DDECLUSAGE_BLENDINDICES, 0);
2102 else if (fvf & D3DFVF_LASTBETA_D3DCOLOR)
2103 append_decl_element(declaration, &idx, &offset, D3DDECLTYPE_D3DCOLOR, D3DDECLUSAGE_BLENDINDICES, 0);
2108 if (fvf & D3DFVF_NORMAL)
2109 append_decl_element(declaration, &idx, &offset, D3DDECLTYPE_FLOAT3, D3DDECLUSAGE_NORMAL, 0);
2110 if (fvf & D3DFVF_PSIZE)
2111 append_decl_element(declaration, &idx, &offset, D3DDECLTYPE_FLOAT1, D3DDECLUSAGE_PSIZE, 0);
2112 if (fvf & D3DFVF_DIFFUSE)
2113 append_decl_element(declaration, &idx, &offset, D3DDECLTYPE_D3DCOLOR, D3DDECLUSAGE_COLOR, 0);
2114 if (fvf & D3DFVF_SPECULAR)
2115 append_decl_element(declaration, &idx, &offset, D3DDECLTYPE_D3DCOLOR, D3DDECLUSAGE_COLOR, 1);
2117 for (i = 0; i < tex_count; ++i)
2119 switch ((fvf >> (16 + 2 * i)) & 0x03)
2121 case D3DFVF_TEXTUREFORMAT1:
2122 append_decl_element(declaration, &idx, &offset, D3DDECLTYPE_FLOAT1, D3DDECLUSAGE_TEXCOORD, i);
2123 break;
2124 case D3DFVF_TEXTUREFORMAT2:
2125 append_decl_element(declaration, &idx, &offset, D3DDECLTYPE_FLOAT2, D3DDECLUSAGE_TEXCOORD, i);
2126 break;
2127 case D3DFVF_TEXTUREFORMAT3:
2128 append_decl_element(declaration, &idx, &offset, D3DDECLTYPE_FLOAT3, D3DDECLUSAGE_TEXCOORD, i);
2129 break;
2130 case D3DFVF_TEXTUREFORMAT4:
2131 append_decl_element(declaration, &idx, &offset, D3DDECLTYPE_FLOAT4, D3DDECLUSAGE_TEXCOORD, i);
2132 break;
2136 declaration[idx] = end_element;
2138 return D3D_OK;
2141 /*************************************************************************
2142 * D3DXFVFFromDeclarator
2144 HRESULT WINAPI D3DXFVFFromDeclarator(const D3DVERTEXELEMENT9 *declaration, DWORD *fvf)
2146 unsigned int i = 0, texture, offset;
2148 TRACE("(%p, %p)\n", declaration, fvf);
2150 *fvf = 0;
2151 if (declaration[0].Type == D3DDECLTYPE_FLOAT3 && declaration[0].Usage == D3DDECLUSAGE_POSITION)
2153 if ((declaration[1].Type == D3DDECLTYPE_FLOAT4 && declaration[1].Usage == D3DDECLUSAGE_BLENDWEIGHT &&
2154 declaration[1].UsageIndex == 0) &&
2155 (declaration[2].Type == D3DDECLTYPE_FLOAT1 && declaration[2].Usage == D3DDECLUSAGE_BLENDINDICES &&
2156 declaration[2].UsageIndex == 0))
2158 return D3DERR_INVALIDCALL;
2160 else if ((declaration[1].Type == D3DDECLTYPE_UBYTE4 || declaration[1].Type == D3DDECLTYPE_D3DCOLOR) &&
2161 declaration[1].Usage == D3DDECLUSAGE_BLENDINDICES && declaration[1].UsageIndex == 0)
2163 if (declaration[1].Type == D3DDECLTYPE_UBYTE4)
2165 *fvf |= D3DFVF_XYZB1 | D3DFVF_LASTBETA_UBYTE4;
2167 else
2169 *fvf |= D3DFVF_XYZB1 | D3DFVF_LASTBETA_D3DCOLOR;
2171 i = 2;
2173 else if (declaration[1].Type <= D3DDECLTYPE_FLOAT4 && declaration[1].Usage == D3DDECLUSAGE_BLENDWEIGHT &&
2174 declaration[1].UsageIndex == 0)
2176 if ((declaration[2].Type == D3DDECLTYPE_UBYTE4 || declaration[2].Type == D3DDECLTYPE_D3DCOLOR) &&
2177 declaration[2].Usage == D3DDECLUSAGE_BLENDINDICES && declaration[2].UsageIndex == 0)
2179 if (declaration[2].Type == D3DDECLTYPE_UBYTE4)
2181 *fvf |= D3DFVF_LASTBETA_UBYTE4;
2183 else
2185 *fvf |= D3DFVF_LASTBETA_D3DCOLOR;
2187 switch (declaration[1].Type)
2189 case D3DDECLTYPE_FLOAT1: *fvf |= D3DFVF_XYZB2; break;
2190 case D3DDECLTYPE_FLOAT2: *fvf |= D3DFVF_XYZB3; break;
2191 case D3DDECLTYPE_FLOAT3: *fvf |= D3DFVF_XYZB4; break;
2192 case D3DDECLTYPE_FLOAT4: *fvf |= D3DFVF_XYZB5; break;
2194 i = 3;
2196 else
2198 switch (declaration[1].Type)
2200 case D3DDECLTYPE_FLOAT1: *fvf |= D3DFVF_XYZB1; break;
2201 case D3DDECLTYPE_FLOAT2: *fvf |= D3DFVF_XYZB2; break;
2202 case D3DDECLTYPE_FLOAT3: *fvf |= D3DFVF_XYZB3; break;
2203 case D3DDECLTYPE_FLOAT4: *fvf |= D3DFVF_XYZB4; break;
2205 i = 2;
2208 else
2210 *fvf |= D3DFVF_XYZ;
2211 i = 1;
2214 else if (declaration[0].Type == D3DDECLTYPE_FLOAT4 && declaration[0].Usage == D3DDECLUSAGE_POSITIONT &&
2215 declaration[0].UsageIndex == 0)
2217 *fvf |= D3DFVF_XYZRHW;
2218 i = 1;
2221 if (declaration[i].Type == D3DDECLTYPE_FLOAT3 && declaration[i].Usage == D3DDECLUSAGE_NORMAL)
2223 *fvf |= D3DFVF_NORMAL;
2224 i++;
2226 if (declaration[i].Type == D3DDECLTYPE_FLOAT1 && declaration[i].Usage == D3DDECLUSAGE_PSIZE &&
2227 declaration[i].UsageIndex == 0)
2229 *fvf |= D3DFVF_PSIZE;
2230 i++;
2232 if (declaration[i].Type == D3DDECLTYPE_D3DCOLOR && declaration[i].Usage == D3DDECLUSAGE_COLOR &&
2233 declaration[i].UsageIndex == 0)
2235 *fvf |= D3DFVF_DIFFUSE;
2236 i++;
2238 if (declaration[i].Type == D3DDECLTYPE_D3DCOLOR && declaration[i].Usage == D3DDECLUSAGE_COLOR &&
2239 declaration[i].UsageIndex == 1)
2241 *fvf |= D3DFVF_SPECULAR;
2242 i++;
2245 for (texture = 0; texture < D3DDP_MAXTEXCOORD; i++, texture++)
2247 if (declaration[i].Stream == 0xFF)
2249 break;
2251 else if (declaration[i].Type == D3DDECLTYPE_FLOAT1 && declaration[i].Usage == D3DDECLUSAGE_TEXCOORD &&
2252 declaration[i].UsageIndex == texture)
2254 *fvf |= D3DFVF_TEXCOORDSIZE1(declaration[i].UsageIndex);
2256 else if (declaration[i].Type == D3DDECLTYPE_FLOAT2 && declaration[i].Usage == D3DDECLUSAGE_TEXCOORD &&
2257 declaration[i].UsageIndex == texture)
2259 *fvf |= D3DFVF_TEXCOORDSIZE2(declaration[i].UsageIndex);
2261 else if (declaration[i].Type == D3DDECLTYPE_FLOAT3 && declaration[i].Usage == D3DDECLUSAGE_TEXCOORD &&
2262 declaration[i].UsageIndex == texture)
2264 *fvf |= D3DFVF_TEXCOORDSIZE3(declaration[i].UsageIndex);
2266 else if (declaration[i].Type == D3DDECLTYPE_FLOAT4 && declaration[i].Usage == D3DDECLUSAGE_TEXCOORD &&
2267 declaration[i].UsageIndex == texture)
2269 *fvf |= D3DFVF_TEXCOORDSIZE4(declaration[i].UsageIndex);
2271 else
2273 return D3DERR_INVALIDCALL;
2277 *fvf |= (texture << D3DFVF_TEXCOUNT_SHIFT);
2279 for (offset = 0, i = 0; declaration[i].Stream != 0xFF;
2280 offset += d3dx_decltype_size[declaration[i].Type], i++)
2282 if (declaration[i].Offset != offset)
2284 return D3DERR_INVALIDCALL;
2288 return D3D_OK;
2291 /*************************************************************************
2292 * D3DXGetFVFVertexSize
2294 static UINT Get_TexCoord_Size_From_FVF(DWORD FVF, int tex_num)
2296 return (((((FVF) >> (16 + (2 * (tex_num)))) + 1) & 0x03) + 1);
2299 UINT WINAPI D3DXGetFVFVertexSize(DWORD FVF)
2301 DWORD size = 0;
2302 UINT i;
2303 UINT numTextures = (FVF & D3DFVF_TEXCOUNT_MASK) >> D3DFVF_TEXCOUNT_SHIFT;
2305 if (FVF & D3DFVF_NORMAL) size += sizeof(D3DXVECTOR3);
2306 if (FVF & D3DFVF_DIFFUSE) size += sizeof(DWORD);
2307 if (FVF & D3DFVF_SPECULAR) size += sizeof(DWORD);
2308 if (FVF & D3DFVF_PSIZE) size += sizeof(DWORD);
2310 switch (FVF & D3DFVF_POSITION_MASK)
2312 case D3DFVF_XYZ: size += sizeof(D3DXVECTOR3); break;
2313 case D3DFVF_XYZRHW: size += 4 * sizeof(FLOAT); break;
2314 case D3DFVF_XYZB1: size += 4 * sizeof(FLOAT); break;
2315 case D3DFVF_XYZB2: size += 5 * sizeof(FLOAT); break;
2316 case D3DFVF_XYZB3: size += 6 * sizeof(FLOAT); break;
2317 case D3DFVF_XYZB4: size += 7 * sizeof(FLOAT); break;
2318 case D3DFVF_XYZB5: size += 8 * sizeof(FLOAT); break;
2319 case D3DFVF_XYZW: size += 4 * sizeof(FLOAT); break;
2322 for (i = 0; i < numTextures; i++)
2324 size += Get_TexCoord_Size_From_FVF(FVF, i) * sizeof(FLOAT);
2327 return size;
2330 /*************************************************************************
2331 * D3DXGetDeclVertexSize
2333 UINT WINAPI D3DXGetDeclVertexSize(const D3DVERTEXELEMENT9 *decl, DWORD stream_idx)
2335 const D3DVERTEXELEMENT9 *element;
2336 UINT size = 0;
2338 TRACE("decl %p, stream_idx %u\n", decl, stream_idx);
2340 if (!decl) return 0;
2342 for (element = decl; element->Stream != 0xff; ++element)
2344 UINT type_size;
2346 if (element->Stream != stream_idx) continue;
2348 if (element->Type >= sizeof(d3dx_decltype_size) / sizeof(*d3dx_decltype_size))
2350 FIXME("Unhandled element type %#x, size will be incorrect.\n", element->Type);
2351 continue;
2354 type_size = d3dx_decltype_size[element->Type];
2355 if (element->Offset + type_size > size) size = element->Offset + type_size;
2358 return size;
2361 /*************************************************************************
2362 * D3DXGetDeclLength
2364 UINT WINAPI D3DXGetDeclLength(const D3DVERTEXELEMENT9 *decl)
2366 const D3DVERTEXELEMENT9 *element;
2368 TRACE("decl %p\n", decl);
2370 /* null decl results in exception on Windows XP */
2372 for (element = decl; element->Stream != 0xff; ++element);
2374 return element - decl;
2377 BOOL WINAPI D3DXIntersectTri(const D3DXVECTOR3 *p0, const D3DXVECTOR3 *p1, const D3DXVECTOR3 *p2,
2378 const D3DXVECTOR3 *praypos, const D3DXVECTOR3 *praydir, float *pu, float *pv, float *pdist)
2380 D3DXMATRIX m;
2381 D3DXVECTOR4 vec;
2383 m.u.m[0][0] = p1->x - p0->x;
2384 m.u.m[1][0] = p2->x - p0->x;
2385 m.u.m[2][0] = -praydir->x;
2386 m.u.m[3][0] = 0.0f;
2387 m.u.m[0][1] = p1->y - p0->z;
2388 m.u.m[1][1] = p2->y - p0->z;
2389 m.u.m[2][1] = -praydir->y;
2390 m.u.m[3][1] = 0.0f;
2391 m.u.m[0][2] = p1->z - p0->z;
2392 m.u.m[1][2] = p2->z - p0->z;
2393 m.u.m[2][2] = -praydir->z;
2394 m.u.m[3][2] = 0.0f;
2395 m.u.m[0][3] = 0.0f;
2396 m.u.m[1][3] = 0.0f;
2397 m.u.m[2][3] = 0.0f;
2398 m.u.m[3][3] = 1.0f;
2400 vec.x = praypos->x - p0->x;
2401 vec.y = praypos->y - p0->y;
2402 vec.z = praypos->z - p0->z;
2403 vec.w = 0.0f;
2405 if ( D3DXMatrixInverse(&m, NULL, &m) )
2407 D3DXVec4Transform(&vec, &vec, &m);
2408 if ( (vec.x >= 0.0f) && (vec.y >= 0.0f) && (vec.x + vec.y <= 1.0f) && (vec.z >= 0.0f) )
2410 *pu = vec.x;
2411 *pv = vec.y;
2412 *pdist = fabsf( vec.z );
2413 return TRUE;
2417 return FALSE;
2420 BOOL WINAPI D3DXSphereBoundProbe(const D3DXVECTOR3 *pcenter, float radius,
2421 const D3DXVECTOR3 *prayposition, const D3DXVECTOR3 *praydirection)
2423 D3DXVECTOR3 difference;
2424 FLOAT a, b, c, d;
2426 a = D3DXVec3LengthSq(praydirection);
2427 if (!D3DXVec3Subtract(&difference, prayposition, pcenter)) return FALSE;
2428 b = D3DXVec3Dot(&difference, praydirection);
2429 c = D3DXVec3LengthSq(&difference) - radius * radius;
2430 d = b * b - a * c;
2432 if ( ( d <= 0.0f ) || ( sqrt(d) <= b ) ) return FALSE;
2433 return TRUE;
2436 /*************************************************************************
2437 * D3DXCreateMesh
2439 HRESULT WINAPI D3DXCreateMesh(DWORD numfaces, DWORD numvertices, DWORD options,
2440 const D3DVERTEXELEMENT9 *declaration, struct IDirect3DDevice9 *device, struct ID3DXMesh **mesh)
2442 HRESULT hr;
2443 DWORD fvf;
2444 IDirect3DVertexDeclaration9 *vertex_declaration;
2445 UINT vertex_declaration_size;
2446 UINT num_elem;
2447 IDirect3DVertexBuffer9 *vertex_buffer;
2448 IDirect3DIndexBuffer9 *index_buffer;
2449 DWORD *attrib_buffer;
2450 struct d3dx9_mesh *object;
2451 DWORD index_usage = 0;
2452 D3DPOOL index_pool = D3DPOOL_DEFAULT;
2453 D3DFORMAT index_format = D3DFMT_INDEX16;
2454 DWORD vertex_usage = 0;
2455 D3DPOOL vertex_pool = D3DPOOL_DEFAULT;
2456 int i;
2458 TRACE("numfaces %u, numvertices %u, options %#x, declaration %p, device %p, mesh %p.\n",
2459 numfaces, numvertices, options, declaration, device, mesh);
2461 if (numfaces == 0 || numvertices == 0 || declaration == NULL || device == NULL || mesh == NULL ||
2462 /* D3DXMESH_VB_SHARE is for cloning, and D3DXMESH_USEHWONLY is for ConvertToBlendedMesh */
2463 (options & (D3DXMESH_VB_SHARE | D3DXMESH_USEHWONLY | 0xfffe0000)))
2465 return D3DERR_INVALIDCALL;
2467 for (i = 0; declaration[i].Stream != 0xff; i++)
2468 if (declaration[i].Stream != 0)
2469 return D3DERR_INVALIDCALL;
2470 num_elem = i + 1;
2472 if (options & D3DXMESH_32BIT)
2473 index_format = D3DFMT_INDEX32;
2475 if (options & D3DXMESH_DONOTCLIP) {
2476 index_usage |= D3DUSAGE_DONOTCLIP;
2477 vertex_usage |= D3DUSAGE_DONOTCLIP;
2479 if (options & D3DXMESH_POINTS) {
2480 index_usage |= D3DUSAGE_POINTS;
2481 vertex_usage |= D3DUSAGE_POINTS;
2483 if (options & D3DXMESH_RTPATCHES) {
2484 index_usage |= D3DUSAGE_RTPATCHES;
2485 vertex_usage |= D3DUSAGE_RTPATCHES;
2487 if (options & D3DXMESH_NPATCHES) {
2488 index_usage |= D3DUSAGE_NPATCHES;
2489 vertex_usage |= D3DUSAGE_NPATCHES;
2492 if (options & D3DXMESH_VB_SYSTEMMEM)
2493 vertex_pool = D3DPOOL_SYSTEMMEM;
2494 else if (options & D3DXMESH_VB_MANAGED)
2495 vertex_pool = D3DPOOL_MANAGED;
2497 if (options & D3DXMESH_VB_WRITEONLY)
2498 vertex_usage |= D3DUSAGE_WRITEONLY;
2499 if (options & D3DXMESH_VB_DYNAMIC)
2500 vertex_usage |= D3DUSAGE_DYNAMIC;
2501 if (options & D3DXMESH_VB_SOFTWAREPROCESSING)
2502 vertex_usage |= D3DUSAGE_SOFTWAREPROCESSING;
2504 if (options & D3DXMESH_IB_SYSTEMMEM)
2505 index_pool = D3DPOOL_SYSTEMMEM;
2506 else if (options & D3DXMESH_IB_MANAGED)
2507 index_pool = D3DPOOL_MANAGED;
2509 if (options & D3DXMESH_IB_WRITEONLY)
2510 index_usage |= D3DUSAGE_WRITEONLY;
2511 if (options & D3DXMESH_IB_DYNAMIC)
2512 index_usage |= D3DUSAGE_DYNAMIC;
2513 if (options & D3DXMESH_IB_SOFTWAREPROCESSING)
2514 index_usage |= D3DUSAGE_SOFTWAREPROCESSING;
2516 hr = D3DXFVFFromDeclarator(declaration, &fvf);
2517 if (hr != D3D_OK)
2519 fvf = 0;
2522 /* Create vertex declaration */
2523 hr = IDirect3DDevice9_CreateVertexDeclaration(device,
2524 declaration,
2525 &vertex_declaration);
2526 if (FAILED(hr))
2528 WARN("Unexpected return value %x from IDirect3DDevice9_CreateVertexDeclaration.\n",hr);
2529 return hr;
2531 vertex_declaration_size = D3DXGetDeclVertexSize(declaration, declaration[0].Stream);
2533 /* Create vertex buffer */
2534 hr = IDirect3DDevice9_CreateVertexBuffer(device,
2535 numvertices * vertex_declaration_size,
2536 vertex_usage,
2537 fvf,
2538 vertex_pool,
2539 &vertex_buffer,
2540 NULL);
2541 if (FAILED(hr))
2543 WARN("Unexpected return value %x from IDirect3DDevice9_CreateVertexBuffer.\n",hr);
2544 IDirect3DVertexDeclaration9_Release(vertex_declaration);
2545 return hr;
2548 /* Create index buffer */
2549 hr = IDirect3DDevice9_CreateIndexBuffer(device,
2550 numfaces * 3 * ((index_format == D3DFMT_INDEX16) ? 2 : 4),
2551 index_usage,
2552 index_format,
2553 index_pool,
2554 &index_buffer,
2555 NULL);
2556 if (FAILED(hr))
2558 WARN("Unexpected return value %x from IDirect3DDevice9_CreateVertexBuffer.\n",hr);
2559 IDirect3DVertexBuffer9_Release(vertex_buffer);
2560 IDirect3DVertexDeclaration9_Release(vertex_declaration);
2561 return hr;
2564 attrib_buffer = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, numfaces * sizeof(*attrib_buffer));
2565 object = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(*object));
2566 if (object == NULL || attrib_buffer == NULL)
2568 HeapFree(GetProcessHeap(), 0, object);
2569 HeapFree(GetProcessHeap(), 0, attrib_buffer);
2570 IDirect3DIndexBuffer9_Release(index_buffer);
2571 IDirect3DVertexBuffer9_Release(vertex_buffer);
2572 IDirect3DVertexDeclaration9_Release(vertex_declaration);
2573 *mesh = NULL;
2574 return E_OUTOFMEMORY;
2576 object->ID3DXMesh_iface.lpVtbl = &D3DXMesh_Vtbl;
2577 object->ref = 1;
2579 object->numfaces = numfaces;
2580 object->numvertices = numvertices;
2581 object->options = options;
2582 object->fvf = fvf;
2583 object->device = device;
2584 IDirect3DDevice9_AddRef(device);
2586 copy_declaration(object->cached_declaration, declaration, num_elem);
2587 object->vertex_declaration = vertex_declaration;
2588 object->vertex_declaration_size = vertex_declaration_size;
2589 object->num_elem = num_elem;
2590 object->vertex_buffer = vertex_buffer;
2591 object->index_buffer = index_buffer;
2592 object->attrib_buffer = attrib_buffer;
2594 *mesh = &object->ID3DXMesh_iface;
2596 return D3D_OK;
2599 /*************************************************************************
2600 * D3DXCreateMeshFVF
2602 HRESULT WINAPI D3DXCreateMeshFVF(DWORD numfaces, DWORD numvertices, DWORD options,
2603 DWORD fvf, struct IDirect3DDevice9 *device, struct ID3DXMesh **mesh)
2605 HRESULT hr;
2606 D3DVERTEXELEMENT9 declaration[MAX_FVF_DECL_SIZE];
2608 TRACE("(%u, %u, %u, %u, %p, %p)\n", numfaces, numvertices, options, fvf, device, mesh);
2610 hr = D3DXDeclaratorFromFVF(fvf, declaration);
2611 if (FAILED(hr)) return hr;
2613 return D3DXCreateMesh(numfaces, numvertices, options, declaration, device, mesh);
2617 struct mesh_data {
2618 DWORD num_vertices;
2619 DWORD num_poly_faces;
2620 DWORD num_tri_faces;
2621 D3DXVECTOR3 *vertices;
2622 DWORD *num_tri_per_face;
2623 DWORD *indices;
2625 DWORD fvf;
2627 /* optional mesh data */
2629 DWORD num_normals;
2630 D3DXVECTOR3 *normals;
2631 DWORD *normal_indices;
2633 D3DXVECTOR2 *tex_coords;
2635 DWORD *vertex_colors;
2637 DWORD num_materials;
2638 D3DXMATERIAL *materials;
2639 DWORD *material_indices;
2641 struct ID3DXSkinInfo *skin_info;
2642 DWORD nb_bones;
2645 static HRESULT parse_texture_filename(ID3DXFileData *filedata, char **filename_out)
2647 HRESULT hr;
2648 SIZE_T data_size;
2649 BYTE *data;
2650 char *filename_in;
2651 char *filename = NULL;
2653 /* template TextureFilename {
2654 * STRING filename;
2658 HeapFree(GetProcessHeap(), 0, *filename_out);
2659 *filename_out = NULL;
2661 hr = filedata->lpVtbl->Lock(filedata, &data_size, (const void**)&data);
2662 if (FAILED(hr)) return hr;
2664 /* FIXME: String must be retrieved directly instead of through a pointer once ID3DXFILE is fixed */
2665 if (data_size < sizeof(filename_in))
2667 WARN("truncated data (%lu bytes)\n", data_size);
2668 filedata->lpVtbl->Unlock(filedata);
2669 return E_FAIL;
2671 filename_in = *(char **)data;
2673 filename = HeapAlloc(GetProcessHeap(), 0, strlen(filename_in) + 1);
2674 if (!filename) {
2675 filedata->lpVtbl->Unlock(filedata);
2676 return E_OUTOFMEMORY;
2679 strcpy(filename, filename_in);
2680 *filename_out = filename;
2682 filedata->lpVtbl->Unlock(filedata);
2684 return D3D_OK;
2687 static HRESULT parse_material(ID3DXFileData *filedata, D3DXMATERIAL *material)
2689 HRESULT hr;
2690 SIZE_T data_size;
2691 const BYTE *data;
2692 GUID type;
2693 ID3DXFileData *child;
2694 SIZE_T i, nb_children;
2696 material->pTextureFilename = NULL;
2698 hr = filedata->lpVtbl->Lock(filedata, &data_size, (const void**)&data);
2699 if (FAILED(hr)) return hr;
2702 * template ColorRGBA {
2703 * FLOAT red;
2704 * FLOAT green;
2705 * FLOAT blue;
2706 * FLOAT alpha;
2708 * template ColorRGB {
2709 * FLOAT red;
2710 * FLOAT green;
2711 * FLOAT blue;
2713 * template Material {
2714 * ColorRGBA faceColor;
2715 * FLOAT power;
2716 * ColorRGB specularColor;
2717 * ColorRGB emissiveColor;
2718 * [ ... ]
2721 if (data_size != sizeof(FLOAT) * 11) {
2722 WARN("incorrect data size (%ld bytes)\n", data_size);
2723 filedata->lpVtbl->Unlock(filedata);
2724 return E_FAIL;
2727 memcpy(&material->MatD3D.Diffuse, data, sizeof(D3DCOLORVALUE));
2728 data += sizeof(D3DCOLORVALUE);
2729 material->MatD3D.Power = *(FLOAT*)data;
2730 data += sizeof(FLOAT);
2731 memcpy(&material->MatD3D.Specular, data, sizeof(FLOAT) * 3);
2732 material->MatD3D.Specular.a = 1.0f;
2733 data += 3 * sizeof(FLOAT);
2734 memcpy(&material->MatD3D.Emissive, data, sizeof(FLOAT) * 3);
2735 material->MatD3D.Emissive.a = 1.0f;
2736 material->MatD3D.Ambient.r = 0.0f;
2737 material->MatD3D.Ambient.g = 0.0f;
2738 material->MatD3D.Ambient.b = 0.0f;
2739 material->MatD3D.Ambient.a = 1.0f;
2741 filedata->lpVtbl->Unlock(filedata);
2743 hr = filedata->lpVtbl->GetChildren(filedata, &nb_children);
2744 if (FAILED(hr))
2745 return hr;
2747 for (i = 0; i < nb_children; i++)
2749 hr = filedata->lpVtbl->GetChild(filedata, i, &child);
2750 if (FAILED(hr))
2751 return hr;
2752 hr = child->lpVtbl->GetType(child, &type);
2753 if (FAILED(hr))
2754 return hr;
2756 if (IsEqualGUID(&type, &TID_D3DRMTextureFilename)) {
2757 hr = parse_texture_filename(child, &material->pTextureFilename);
2758 if (FAILED(hr))
2759 return hr;
2763 return D3D_OK;
2766 static void destroy_materials(struct mesh_data *mesh)
2768 DWORD i;
2769 for (i = 0; i < mesh->num_materials; i++)
2770 HeapFree(GetProcessHeap(), 0, mesh->materials[i].pTextureFilename);
2771 HeapFree(GetProcessHeap(), 0, mesh->materials);
2772 HeapFree(GetProcessHeap(), 0, mesh->material_indices);
2773 mesh->num_materials = 0;
2774 mesh->materials = NULL;
2775 mesh->material_indices = NULL;
2778 static HRESULT parse_material_list(ID3DXFileData *filedata, struct mesh_data *mesh)
2780 HRESULT hr;
2781 SIZE_T data_size;
2782 const DWORD *data, *in_ptr;
2783 GUID type;
2784 ID3DXFileData *child;
2785 DWORD num_materials;
2786 DWORD i;
2787 SIZE_T nb_children;
2789 destroy_materials(mesh);
2791 hr = filedata->lpVtbl->Lock(filedata, &data_size, (const void**)&data);
2792 if (FAILED(hr)) return hr;
2794 /* template MeshMaterialList {
2795 * DWORD nMaterials;
2796 * DWORD nFaceIndexes;
2797 * array DWORD faceIndexes[nFaceIndexes];
2798 * [ Material ]
2802 in_ptr = data;
2803 hr = E_FAIL;
2805 if (data_size < sizeof(DWORD)) {
2806 WARN("truncated data (%ld bytes)\n", data_size);
2807 goto end;
2809 num_materials = *in_ptr++;
2810 if (!num_materials) {
2811 hr = D3D_OK;
2812 goto end;
2815 if (data_size < 2 * sizeof(DWORD)) {
2816 WARN("truncated data (%ld bytes)\n", data_size);
2817 goto end;
2819 if (*in_ptr++ != mesh->num_poly_faces) {
2820 WARN("number of material face indices (%u) doesn't match number of faces (%u)\n",
2821 *(in_ptr - 1), mesh->num_poly_faces);
2822 goto end;
2824 if (data_size < 2 * sizeof(DWORD) + mesh->num_poly_faces * sizeof(DWORD)) {
2825 WARN("truncated data (%ld bytes)\n", data_size);
2826 goto end;
2828 for (i = 0; i < mesh->num_poly_faces; i++) {
2829 if (*in_ptr++ >= num_materials) {
2830 WARN("face %u: reference to undefined material %u (only %u materials)\n",
2831 i, *(in_ptr - 1), num_materials);
2832 goto end;
2836 mesh->materials = HeapAlloc(GetProcessHeap(), 0, num_materials * sizeof(*mesh->materials));
2837 mesh->material_indices = HeapAlloc(GetProcessHeap(), 0, mesh->num_poly_faces * sizeof(*mesh->material_indices));
2838 if (!mesh->materials || !mesh->material_indices) {
2839 hr = E_OUTOFMEMORY;
2840 goto end;
2842 memcpy(mesh->material_indices, data + 2, mesh->num_poly_faces * sizeof(DWORD));
2844 hr = filedata->lpVtbl->GetChildren(filedata, &nb_children);
2845 if (FAILED(hr))
2846 goto end;
2848 for (i = 0; i < nb_children; i++)
2850 hr = filedata->lpVtbl->GetChild(filedata, i, &child);
2851 if (FAILED(hr))
2852 goto end;
2853 hr = child->lpVtbl->GetType(child, &type);
2854 if (FAILED(hr))
2855 goto end;
2857 if (IsEqualGUID(&type, &TID_D3DRMMaterial)) {
2858 if (mesh->num_materials >= num_materials) {
2859 WARN("more materials defined than declared\n");
2860 hr = E_FAIL;
2861 goto end;
2863 hr = parse_material(child, &mesh->materials[mesh->num_materials++]);
2864 if (FAILED(hr))
2865 goto end;
2868 if (num_materials != mesh->num_materials) {
2869 WARN("only %u of %u materials defined\n", num_materials, mesh->num_materials);
2870 hr = E_FAIL;
2873 end:
2874 filedata->lpVtbl->Unlock(filedata);
2875 return hr;
2878 static HRESULT parse_texture_coords(ID3DXFileData *filedata, struct mesh_data *mesh)
2880 HRESULT hr;
2881 SIZE_T data_size;
2882 const BYTE *data;
2884 HeapFree(GetProcessHeap(), 0, mesh->tex_coords);
2885 mesh->tex_coords = NULL;
2887 hr = filedata->lpVtbl->Lock(filedata, &data_size, (const void**)&data);
2888 if (FAILED(hr)) return hr;
2890 /* template Coords2d {
2891 * FLOAT u;
2892 * FLOAT v;
2894 * template MeshTextureCoords {
2895 * DWORD nTextureCoords;
2896 * array Coords2d textureCoords[nTextureCoords];
2900 hr = E_FAIL;
2902 if (data_size < sizeof(DWORD)) {
2903 WARN("truncated data (%ld bytes)\n", data_size);
2904 goto end;
2906 if (*(DWORD*)data != mesh->num_vertices) {
2907 WARN("number of texture coordinates (%u) doesn't match number of vertices (%u)\n",
2908 *(DWORD*)data, mesh->num_vertices);
2909 goto end;
2911 data += sizeof(DWORD);
2912 if (data_size < sizeof(DWORD) + mesh->num_vertices * sizeof(*mesh->tex_coords)) {
2913 WARN("truncated data (%ld bytes)\n", data_size);
2914 goto end;
2917 mesh->tex_coords = HeapAlloc(GetProcessHeap(), 0, mesh->num_vertices * sizeof(*mesh->tex_coords));
2918 if (!mesh->tex_coords) {
2919 hr = E_OUTOFMEMORY;
2920 goto end;
2922 memcpy(mesh->tex_coords, data, mesh->num_vertices * sizeof(*mesh->tex_coords));
2924 mesh->fvf |= D3DFVF_TEX1;
2926 hr = D3D_OK;
2928 end:
2929 filedata->lpVtbl->Unlock(filedata);
2930 return hr;
2933 static HRESULT parse_vertex_colors(ID3DXFileData *filedata, struct mesh_data *mesh)
2935 HRESULT hr;
2936 SIZE_T data_size;
2937 const BYTE *data;
2938 DWORD num_colors;
2939 DWORD i;
2941 HeapFree(GetProcessHeap(), 0, mesh->vertex_colors);
2942 mesh->vertex_colors = NULL;
2944 hr = filedata->lpVtbl->Lock(filedata, &data_size, (const void**)&data);
2945 if (FAILED(hr)) return hr;
2947 /* template IndexedColor {
2948 * DWORD index;
2949 * ColorRGBA indexColor;
2951 * template MeshVertexColors {
2952 * DWORD nVertexColors;
2953 * array IndexedColor vertexColors[nVertexColors];
2957 hr = E_FAIL;
2959 if (data_size < sizeof(DWORD)) {
2960 WARN("truncated data (%ld bytes)\n", data_size);
2961 goto end;
2963 num_colors = *(DWORD*)data;
2964 data += sizeof(DWORD);
2965 if (data_size < sizeof(DWORD) + num_colors * (sizeof(DWORD) + sizeof(D3DCOLORVALUE))) {
2966 WARN("truncated data (%ld bytes)\n", data_size);
2967 goto end;
2970 mesh->vertex_colors = HeapAlloc(GetProcessHeap(), 0, mesh->num_vertices * sizeof(DWORD));
2971 if (!mesh->vertex_colors) {
2972 hr = E_OUTOFMEMORY;
2973 goto end;
2976 for (i = 0; i < mesh->num_vertices; i++)
2977 mesh->vertex_colors[i] = D3DCOLOR_ARGB(0, 0xff, 0xff, 0xff);
2978 for (i = 0; i < num_colors; i++)
2980 D3DCOLORVALUE color;
2981 DWORD index = *(DWORD*)data;
2982 data += sizeof(DWORD);
2983 if (index >= mesh->num_vertices) {
2984 WARN("vertex color %u references undefined vertex %u (only %u vertices)\n",
2985 i, index, mesh->num_vertices);
2986 goto end;
2988 memcpy(&color, data, sizeof(color));
2989 data += sizeof(color);
2990 color.r = min(1.0f, max(0.0f, color.r));
2991 color.g = min(1.0f, max(0.0f, color.g));
2992 color.b = min(1.0f, max(0.0f, color.b));
2993 color.a = min(1.0f, max(0.0f, color.a));
2994 mesh->vertex_colors[index] = D3DCOLOR_ARGB((BYTE)(color.a * 255.0f + 0.5f),
2995 (BYTE)(color.r * 255.0f + 0.5f),
2996 (BYTE)(color.g * 255.0f + 0.5f),
2997 (BYTE)(color.b * 255.0f + 0.5f));
3000 mesh->fvf |= D3DFVF_DIFFUSE;
3002 hr = D3D_OK;
3004 end:
3005 filedata->lpVtbl->Unlock(filedata);
3006 return hr;
3009 static HRESULT parse_normals(ID3DXFileData *filedata, struct mesh_data *mesh)
3011 HRESULT hr;
3012 SIZE_T data_size;
3013 const BYTE *data;
3014 DWORD *index_out_ptr;
3015 DWORD i;
3016 DWORD num_face_indices = mesh->num_poly_faces * 2 + mesh->num_tri_faces;
3018 HeapFree(GetProcessHeap(), 0, mesh->normals);
3019 mesh->num_normals = 0;
3020 mesh->normals = NULL;
3021 mesh->normal_indices = NULL;
3022 mesh->fvf |= D3DFVF_NORMAL;
3024 hr = filedata->lpVtbl->Lock(filedata, &data_size, (const void**)&data);
3025 if (FAILED(hr)) return hr;
3027 /* template Vector {
3028 * FLOAT x;
3029 * FLOAT y;
3030 * FLOAT z;
3032 * template MeshFace {
3033 * DWORD nFaceVertexIndices;
3034 * array DWORD faceVertexIndices[nFaceVertexIndices];
3036 * template MeshNormals {
3037 * DWORD nNormals;
3038 * array Vector normals[nNormals];
3039 * DWORD nFaceNormals;
3040 * array MeshFace faceNormals[nFaceNormals];
3044 hr = E_FAIL;
3046 if (data_size < sizeof(DWORD) * 2) {
3047 WARN("truncated data (%ld bytes)\n", data_size);
3048 goto end;
3050 mesh->num_normals = *(DWORD*)data;
3051 data += sizeof(DWORD);
3052 if (data_size < sizeof(DWORD) * 2 + mesh->num_normals * sizeof(D3DXVECTOR3) +
3053 num_face_indices * sizeof(DWORD)) {
3054 WARN("truncated data (%ld bytes)\n", data_size);
3055 goto end;
3058 mesh->normals = HeapAlloc(GetProcessHeap(), 0, mesh->num_normals * sizeof(D3DXVECTOR3));
3059 mesh->normal_indices = HeapAlloc(GetProcessHeap(), 0, num_face_indices * sizeof(DWORD));
3060 if (!mesh->normals || !mesh->normal_indices) {
3061 hr = E_OUTOFMEMORY;
3062 goto end;
3065 memcpy(mesh->normals, data, mesh->num_normals * sizeof(D3DXVECTOR3));
3066 data += mesh->num_normals * sizeof(D3DXVECTOR3);
3067 for (i = 0; i < mesh->num_normals; i++)
3068 D3DXVec3Normalize(&mesh->normals[i], &mesh->normals[i]);
3070 if (*(DWORD*)data != mesh->num_poly_faces) {
3071 WARN("number of face normals (%u) doesn't match number of faces (%u)\n",
3072 *(DWORD*)data, mesh->num_poly_faces);
3073 goto end;
3075 data += sizeof(DWORD);
3076 index_out_ptr = mesh->normal_indices;
3077 for (i = 0; i < mesh->num_poly_faces; i++)
3079 DWORD j;
3080 DWORD count = *(DWORD*)data;
3081 if (count != mesh->num_tri_per_face[i] + 2) {
3082 WARN("face %u: number of normals (%u) doesn't match number of vertices (%u)\n",
3083 i, count, mesh->num_tri_per_face[i] + 2);
3084 goto end;
3086 data += sizeof(DWORD);
3088 for (j = 0; j < count; j++) {
3089 DWORD normal_index = *(DWORD*)data;
3090 if (normal_index >= mesh->num_normals) {
3091 WARN("face %u, normal index %u: reference to undefined normal %u (only %u normals)\n",
3092 i, j, normal_index, mesh->num_normals);
3093 goto end;
3095 *index_out_ptr++ = normal_index;
3096 data += sizeof(DWORD);
3100 hr = D3D_OK;
3102 end:
3103 filedata->lpVtbl->Unlock(filedata);
3104 return hr;
3107 static HRESULT parse_skin_mesh_info(ID3DXFileData *filedata, struct mesh_data *mesh_data, DWORD index)
3109 HRESULT hr;
3110 SIZE_T data_size;
3111 const BYTE *data;
3113 TRACE("(%p, %p, %u)\n", filedata, mesh_data, index);
3115 hr = filedata->lpVtbl->Lock(filedata, &data_size, (const void**)&data);
3116 if (FAILED(hr)) return hr;
3118 hr = E_FAIL;
3120 if (!mesh_data->skin_info) {
3121 if (data_size < sizeof(WORD) * 3) {
3122 WARN("truncated data (%ld bytes)\n", data_size);
3123 goto end;
3125 /* Skip nMaxSkinWeightsPerVertex and nMaxSkinWeightsPerFace */
3126 data += 2 * sizeof(WORD);
3127 mesh_data->nb_bones = *(WORD*)data;
3128 hr = D3DXCreateSkinInfoFVF(mesh_data->num_vertices, mesh_data->fvf, mesh_data->nb_bones, &mesh_data->skin_info);
3129 } else {
3130 const char *name;
3131 DWORD nb_influences;
3133 /* FIXME: String must be retrieved directly instead of through a pointer once ID3DXFILE is fixed */
3134 name = *(const char**)data;
3135 data += sizeof(char*);
3137 nb_influences = *(DWORD*)data;
3138 data += sizeof(DWORD);
3140 if (data_size < (sizeof(char*) + sizeof(DWORD) + nb_influences * (sizeof(DWORD) + sizeof(FLOAT)) + 16 * sizeof(FLOAT))) {
3141 WARN("truncated data (%ld bytes)\n", data_size);
3142 goto end;
3145 hr = mesh_data->skin_info->lpVtbl->SetBoneName(mesh_data->skin_info, index, name);
3146 if (SUCCEEDED(hr))
3147 hr = mesh_data->skin_info->lpVtbl->SetBoneInfluence(mesh_data->skin_info, index, nb_influences,
3148 (const DWORD*)data, (const FLOAT*)(data + nb_influences * sizeof(DWORD)));
3149 if (SUCCEEDED(hr))
3150 hr = mesh_data->skin_info->lpVtbl->SetBoneOffsetMatrix(mesh_data->skin_info, index,
3151 (const D3DMATRIX*)(data + nb_influences * (sizeof(DWORD) + sizeof(FLOAT))));
3154 end:
3155 filedata->lpVtbl->Unlock(filedata);
3156 return hr;
3159 /* for provide_flags parameters */
3160 #define PROVIDE_MATERIALS 0x1
3161 #define PROVIDE_SKININFO 0x2
3162 #define PROVIDE_ADJACENCY 0x4
3164 static HRESULT parse_mesh(ID3DXFileData *filedata, struct mesh_data *mesh_data, DWORD provide_flags)
3166 HRESULT hr;
3167 SIZE_T data_size;
3168 const BYTE *data, *in_ptr;
3169 DWORD *index_out_ptr;
3170 GUID type;
3171 ID3DXFileData *child;
3172 DWORD i;
3173 SIZE_T nb_children;
3174 DWORD nb_skin_weigths_info = 0;
3177 * template Mesh {
3178 * DWORD nVertices;
3179 * array Vector vertices[nVertices];
3180 * DWORD nFaces;
3181 * array MeshFace faces[nFaces];
3182 * [ ... ]
3186 hr = filedata->lpVtbl->Lock(filedata, &data_size, (const void**)&data);
3187 if (FAILED(hr)) return hr;
3189 in_ptr = data;
3190 hr = E_FAIL;
3192 if (data_size < sizeof(DWORD) * 2) {
3193 WARN("truncated data (%ld bytes)\n", data_size);
3194 goto end;
3196 mesh_data->num_vertices = *(DWORD*)in_ptr;
3197 if (data_size < sizeof(DWORD) * 2 + mesh_data->num_vertices * sizeof(D3DXVECTOR3)) {
3198 WARN("truncated data (%ld bytes)\n", data_size);
3199 goto end;
3201 in_ptr += sizeof(DWORD) + mesh_data->num_vertices * sizeof(D3DXVECTOR3);
3203 mesh_data->num_poly_faces = *(DWORD*)in_ptr;
3204 in_ptr += sizeof(DWORD);
3206 mesh_data->num_tri_faces = 0;
3207 for (i = 0; i < mesh_data->num_poly_faces; i++)
3209 DWORD num_poly_vertices;
3210 DWORD j;
3212 if (data_size - (in_ptr - data) < sizeof(DWORD)) {
3213 WARN("truncated data (%ld bytes)\n", data_size);
3214 goto end;
3216 num_poly_vertices = *(DWORD*)in_ptr;
3217 in_ptr += sizeof(DWORD);
3218 if (data_size - (in_ptr - data) < num_poly_vertices * sizeof(DWORD)) {
3219 WARN("truncated data (%ld bytes)\n", data_size);
3220 goto end;
3222 if (num_poly_vertices < 3) {
3223 WARN("face %u has only %u vertices\n", i, num_poly_vertices);
3224 goto end;
3226 for (j = 0; j < num_poly_vertices; j++) {
3227 if (*(DWORD*)in_ptr >= mesh_data->num_vertices) {
3228 WARN("face %u, index %u: undefined vertex %u (only %u vertices)\n",
3229 i, j, *(DWORD*)in_ptr, mesh_data->num_vertices);
3230 goto end;
3232 in_ptr += sizeof(DWORD);
3234 mesh_data->num_tri_faces += num_poly_vertices - 2;
3237 mesh_data->fvf = D3DFVF_XYZ;
3239 mesh_data->vertices = HeapAlloc(GetProcessHeap(), 0,
3240 mesh_data->num_vertices * sizeof(*mesh_data->vertices));
3241 mesh_data->num_tri_per_face = HeapAlloc(GetProcessHeap(), 0,
3242 mesh_data->num_poly_faces * sizeof(*mesh_data->num_tri_per_face));
3243 mesh_data->indices = HeapAlloc(GetProcessHeap(), 0,
3244 (mesh_data->num_tri_faces + mesh_data->num_poly_faces * 2) * sizeof(*mesh_data->indices));
3245 if (!mesh_data->vertices || !mesh_data->num_tri_per_face || !mesh_data->indices) {
3246 hr = E_OUTOFMEMORY;
3247 goto end;
3250 in_ptr = data + sizeof(DWORD);
3251 memcpy(mesh_data->vertices, in_ptr, mesh_data->num_vertices * sizeof(D3DXVECTOR3));
3252 in_ptr += mesh_data->num_vertices * sizeof(D3DXVECTOR3) + sizeof(DWORD);
3254 index_out_ptr = mesh_data->indices;
3255 for (i = 0; i < mesh_data->num_poly_faces; i++)
3257 DWORD count;
3259 count = *(DWORD*)in_ptr;
3260 in_ptr += sizeof(DWORD);
3261 mesh_data->num_tri_per_face[i] = count - 2;
3263 while (count--) {
3264 *index_out_ptr++ = *(DWORD*)in_ptr;
3265 in_ptr += sizeof(DWORD);
3269 hr = filedata->lpVtbl->GetChildren(filedata, &nb_children);
3270 if (FAILED(hr))
3271 goto end;
3273 for (i = 0; i < nb_children; i++)
3275 hr = filedata->lpVtbl->GetChild(filedata, i, &child);
3276 if (FAILED(hr))
3277 goto end;
3278 hr = child->lpVtbl->GetType(child, &type);
3279 if (FAILED(hr))
3280 goto end;
3282 if (IsEqualGUID(&type, &TID_D3DRMMeshNormals)) {
3283 hr = parse_normals(child, mesh_data);
3284 } else if (IsEqualGUID(&type, &TID_D3DRMMeshVertexColors)) {
3285 hr = parse_vertex_colors(child, mesh_data);
3286 } else if (IsEqualGUID(&type, &TID_D3DRMMeshTextureCoords)) {
3287 hr = parse_texture_coords(child, mesh_data);
3288 hr = filedata->lpVtbl->GetChild(filedata, i, &child);
3289 if (FAILED(hr))
3290 goto end;
3291 } else if (IsEqualGUID(&type, &TID_D3DRMMeshMaterialList) &&
3292 (provide_flags & PROVIDE_MATERIALS))
3294 hr = parse_material_list(child, mesh_data);
3295 } else if (provide_flags & PROVIDE_SKININFO) {
3296 if (IsEqualGUID(&type, &DXFILEOBJ_XSkinMeshHeader)) {
3297 if (mesh_data->skin_info) {
3298 WARN("Skin mesh header already encountered\n");
3299 hr = E_FAIL;
3300 goto end;
3302 hr = parse_skin_mesh_info(child, mesh_data, 0);
3303 if (FAILED(hr))
3304 goto end;
3305 } else if (IsEqualGUID(&type, &DXFILEOBJ_SkinWeights)) {
3306 if (!mesh_data->skin_info) {
3307 WARN("Skin weigths found but skin mesh header not encountered yet\n");
3308 hr = E_FAIL;
3309 goto end;
3311 hr = parse_skin_mesh_info(child, mesh_data, nb_skin_weigths_info);
3312 if (FAILED(hr))
3313 goto end;
3314 nb_skin_weigths_info++;
3317 if (FAILED(hr))
3318 goto end;
3321 if (mesh_data->skin_info && (nb_skin_weigths_info != mesh_data->nb_bones)) {
3322 WARN("Mismatch between nb skin weights info %u encountered and nb bones %u from skin mesh header\n",
3323 nb_skin_weigths_info, mesh_data->nb_bones);
3324 hr = E_FAIL;
3325 goto end;
3328 hr = D3D_OK;
3330 end:
3331 filedata->lpVtbl->Unlock(filedata);
3332 return hr;
3335 static HRESULT generate_effects(ID3DXBuffer *materials, DWORD num_materials,
3336 ID3DXBuffer **effects)
3338 HRESULT hr;
3339 D3DXEFFECTINSTANCE *effect_ptr;
3340 BYTE *out_ptr;
3341 const D3DXMATERIAL *material_ptr = ID3DXBuffer_GetBufferPointer(materials);
3342 static const struct {
3343 const char *param_name;
3344 DWORD name_size;
3345 DWORD num_bytes;
3346 DWORD value_offset;
3347 } material_effects[] = {
3348 #define EFFECT_TABLE_ENTRY(str, field) \
3349 {str, sizeof(str), sizeof(material_ptr->MatD3D.field), offsetof(D3DXMATERIAL, MatD3D.field)}
3350 EFFECT_TABLE_ENTRY("Diffuse", Diffuse),
3351 EFFECT_TABLE_ENTRY("Power", Power),
3352 EFFECT_TABLE_ENTRY("Specular", Specular),
3353 EFFECT_TABLE_ENTRY("Emissive", Emissive),
3354 EFFECT_TABLE_ENTRY("Ambient", Ambient),
3355 #undef EFFECT_TABLE_ENTRY
3357 static const char texture_paramname[] = "Texture0@Name";
3358 DWORD buffer_size;
3359 DWORD i;
3361 /* effects buffer layout:
3363 * D3DXEFFECTINSTANCE effects[num_materials];
3364 * for (effect in effects)
3366 * D3DXEFFECTDEFAULT defaults[effect.NumDefaults];
3367 * for (default in defaults)
3369 * *default.pParamName;
3370 * *default.pValue;
3374 buffer_size = sizeof(D3DXEFFECTINSTANCE);
3375 buffer_size += sizeof(D3DXEFFECTDEFAULT) * ARRAY_SIZE(material_effects);
3376 for (i = 0; i < ARRAY_SIZE(material_effects); i++) {
3377 buffer_size += material_effects[i].name_size;
3378 buffer_size += material_effects[i].num_bytes;
3380 buffer_size *= num_materials;
3381 for (i = 0; i < num_materials; i++) {
3382 if (material_ptr[i].pTextureFilename) {
3383 buffer_size += sizeof(D3DXEFFECTDEFAULT);
3384 buffer_size += sizeof(texture_paramname);
3385 buffer_size += strlen(material_ptr[i].pTextureFilename) + 1;
3389 hr = D3DXCreateBuffer(buffer_size, effects);
3390 if (FAILED(hr)) return hr;
3391 effect_ptr = ID3DXBuffer_GetBufferPointer(*effects);
3392 out_ptr = (BYTE*)(effect_ptr + num_materials);
3394 for (i = 0; i < num_materials; i++)
3396 DWORD j;
3397 D3DXEFFECTDEFAULT *defaults = (D3DXEFFECTDEFAULT*)out_ptr;
3399 effect_ptr->pDefaults = defaults;
3400 effect_ptr->NumDefaults = material_ptr->pTextureFilename ? 6 : 5;
3401 out_ptr = (BYTE*)(effect_ptr->pDefaults + effect_ptr->NumDefaults);
3403 for (j = 0; j < ARRAY_SIZE(material_effects); j++)
3405 defaults->pParamName = (char *)out_ptr;
3406 strcpy(defaults->pParamName, material_effects[j].param_name);
3407 defaults->pValue = defaults->pParamName + material_effects[j].name_size;
3408 defaults->Type = D3DXEDT_FLOATS;
3409 defaults->NumBytes = material_effects[j].num_bytes;
3410 memcpy(defaults->pValue, (BYTE*)material_ptr + material_effects[j].value_offset, defaults->NumBytes);
3411 out_ptr = (BYTE*)defaults->pValue + defaults->NumBytes;
3412 defaults++;
3415 if (material_ptr->pTextureFilename)
3417 defaults->pParamName = (char *)out_ptr;
3418 strcpy(defaults->pParamName, texture_paramname);
3419 defaults->pValue = defaults->pParamName + sizeof(texture_paramname);
3420 defaults->Type = D3DXEDT_STRING;
3421 defaults->NumBytes = strlen(material_ptr->pTextureFilename) + 1;
3422 strcpy(defaults->pValue, material_ptr->pTextureFilename);
3423 out_ptr = (BYTE*)defaults->pValue + defaults->NumBytes;
3425 material_ptr++;
3426 effect_ptr++;
3428 assert(out_ptr - (BYTE*)ID3DXBuffer_GetBufferPointer(*effects) == buffer_size);
3430 return D3D_OK;
3433 HRESULT WINAPI D3DXLoadSkinMeshFromXof(struct ID3DXFileData *filedata, DWORD options,
3434 struct IDirect3DDevice9 *device, struct ID3DXBuffer **adjacency_out, struct ID3DXBuffer **materials_out,
3435 struct ID3DXBuffer **effects_out, DWORD *num_materials_out, struct ID3DXSkinInfo **skin_info_out,
3436 struct ID3DXMesh **mesh_out)
3438 HRESULT hr;
3439 DWORD *index_in_ptr;
3440 struct mesh_data mesh_data;
3441 DWORD total_vertices;
3442 ID3DXMesh *d3dxmesh = NULL;
3443 ID3DXBuffer *adjacency = NULL;
3444 ID3DXBuffer *materials = NULL;
3445 ID3DXBuffer *effects = NULL;
3446 struct vertex_duplication {
3447 DWORD normal_index;
3448 struct list entry;
3449 } *duplications = NULL;
3450 DWORD i;
3451 void *vertices = NULL;
3452 void *indices = NULL;
3453 BYTE *out_ptr;
3454 DWORD provide_flags = 0;
3456 TRACE("(%p, %x, %p, %p, %p, %p, %p, %p, %p)\n", filedata, options, device, adjacency_out, materials_out,
3457 effects_out, num_materials_out, skin_info_out, mesh_out);
3459 ZeroMemory(&mesh_data, sizeof(mesh_data));
3461 if (num_materials_out || materials_out || effects_out)
3462 provide_flags |= PROVIDE_MATERIALS;
3463 if (skin_info_out)
3464 provide_flags |= PROVIDE_SKININFO;
3466 hr = parse_mesh(filedata, &mesh_data, provide_flags);
3467 if (FAILED(hr)) goto cleanup;
3469 total_vertices = mesh_data.num_vertices;
3470 if (mesh_data.fvf & D3DFVF_NORMAL) {
3471 /* duplicate vertices with multiple normals */
3472 DWORD num_face_indices = mesh_data.num_poly_faces * 2 + mesh_data.num_tri_faces;
3473 duplications = HeapAlloc(GetProcessHeap(), 0, (mesh_data.num_vertices + num_face_indices) * sizeof(*duplications));
3474 if (!duplications) {
3475 hr = E_OUTOFMEMORY;
3476 goto cleanup;
3478 for (i = 0; i < total_vertices; i++)
3480 duplications[i].normal_index = -1;
3481 list_init(&duplications[i].entry);
3483 for (i = 0; i < num_face_indices; i++) {
3484 DWORD vertex_index = mesh_data.indices[i];
3485 DWORD normal_index = mesh_data.normal_indices[i];
3486 struct vertex_duplication *dup_ptr = &duplications[vertex_index];
3488 if (dup_ptr->normal_index == -1) {
3489 dup_ptr->normal_index = normal_index;
3490 } else {
3491 D3DXVECTOR3 *new_normal = &mesh_data.normals[normal_index];
3492 struct list *dup_list = &dup_ptr->entry;
3493 while (TRUE) {
3494 D3DXVECTOR3 *cur_normal = &mesh_data.normals[dup_ptr->normal_index];
3495 if (new_normal->x == cur_normal->x &&
3496 new_normal->y == cur_normal->y &&
3497 new_normal->z == cur_normal->z)
3499 mesh_data.indices[i] = dup_ptr - duplications;
3500 break;
3501 } else if (!list_next(dup_list, &dup_ptr->entry)) {
3502 dup_ptr = &duplications[total_vertices++];
3503 dup_ptr->normal_index = normal_index;
3504 list_add_tail(dup_list, &dup_ptr->entry);
3505 mesh_data.indices[i] = dup_ptr - duplications;
3506 break;
3507 } else {
3508 dup_ptr = LIST_ENTRY(list_next(dup_list, &dup_ptr->entry),
3509 struct vertex_duplication, entry);
3516 hr = D3DXCreateMeshFVF(mesh_data.num_tri_faces, total_vertices, options, mesh_data.fvf, device, &d3dxmesh);
3517 if (FAILED(hr)) goto cleanup;
3519 hr = d3dxmesh->lpVtbl->LockVertexBuffer(d3dxmesh, 0, &vertices);
3520 if (FAILED(hr)) goto cleanup;
3522 out_ptr = vertices;
3523 for (i = 0; i < mesh_data.num_vertices; i++) {
3524 *(D3DXVECTOR3*)out_ptr = mesh_data.vertices[i];
3525 out_ptr += sizeof(D3DXVECTOR3);
3526 if (mesh_data.fvf & D3DFVF_NORMAL) {
3527 if (duplications[i].normal_index == -1)
3528 ZeroMemory(out_ptr, sizeof(D3DXVECTOR3));
3529 else
3530 *(D3DXVECTOR3*)out_ptr = mesh_data.normals[duplications[i].normal_index];
3531 out_ptr += sizeof(D3DXVECTOR3);
3533 if (mesh_data.fvf & D3DFVF_DIFFUSE) {
3534 *(DWORD*)out_ptr = mesh_data.vertex_colors[i];
3535 out_ptr += sizeof(DWORD);
3537 if (mesh_data.fvf & D3DFVF_TEX1) {
3538 *(D3DXVECTOR2*)out_ptr = mesh_data.tex_coords[i];
3539 out_ptr += sizeof(D3DXVECTOR2);
3542 if (mesh_data.fvf & D3DFVF_NORMAL) {
3543 DWORD vertex_size = D3DXGetFVFVertexSize(mesh_data.fvf);
3544 out_ptr = vertices;
3545 for (i = 0; i < mesh_data.num_vertices; i++) {
3546 struct vertex_duplication *dup_ptr;
3547 LIST_FOR_EACH_ENTRY(dup_ptr, &duplications[i].entry, struct vertex_duplication, entry)
3549 int j = dup_ptr - duplications;
3550 BYTE *dest_vertex = (BYTE*)vertices + j * vertex_size;
3552 memcpy(dest_vertex, out_ptr, vertex_size);
3553 dest_vertex += sizeof(D3DXVECTOR3);
3554 *(D3DXVECTOR3*)dest_vertex = mesh_data.normals[dup_ptr->normal_index];
3556 out_ptr += vertex_size;
3559 d3dxmesh->lpVtbl->UnlockVertexBuffer(d3dxmesh);
3561 hr = d3dxmesh->lpVtbl->LockIndexBuffer(d3dxmesh, 0, &indices);
3562 if (FAILED(hr)) goto cleanup;
3564 index_in_ptr = mesh_data.indices;
3565 #define FILL_INDEX_BUFFER(indices_var) \
3566 for (i = 0; i < mesh_data.num_poly_faces; i++) \
3568 DWORD count = mesh_data.num_tri_per_face[i]; \
3569 WORD first_index = *index_in_ptr++; \
3570 while (count--) { \
3571 *indices_var++ = first_index; \
3572 *indices_var++ = *index_in_ptr; \
3573 index_in_ptr++; \
3574 *indices_var++ = *index_in_ptr; \
3576 index_in_ptr++; \
3578 if (options & D3DXMESH_32BIT) {
3579 DWORD *dword_indices = indices;
3580 FILL_INDEX_BUFFER(dword_indices)
3581 } else {
3582 WORD *word_indices = indices;
3583 FILL_INDEX_BUFFER(word_indices)
3585 #undef FILL_INDEX_BUFFER
3586 d3dxmesh->lpVtbl->UnlockIndexBuffer(d3dxmesh);
3588 if (mesh_data.material_indices) {
3589 DWORD *attrib_buffer = NULL;
3590 hr = d3dxmesh->lpVtbl->LockAttributeBuffer(d3dxmesh, 0, &attrib_buffer);
3591 if (FAILED(hr)) goto cleanup;
3592 for (i = 0; i < mesh_data.num_poly_faces; i++)
3594 DWORD count = mesh_data.num_tri_per_face[i];
3595 while (count--)
3596 *attrib_buffer++ = mesh_data.material_indices[i];
3598 d3dxmesh->lpVtbl->UnlockAttributeBuffer(d3dxmesh);
3600 hr = d3dxmesh->lpVtbl->OptimizeInplace(d3dxmesh,
3601 D3DXMESHOPT_ATTRSORT | D3DXMESHOPT_IGNOREVERTS | D3DXMESHOPT_DONOTSPLIT,
3602 NULL, NULL, NULL, NULL);
3603 if (FAILED(hr)) goto cleanup;
3606 if (mesh_data.num_materials && (materials_out || effects_out)) {
3607 DWORD buffer_size = mesh_data.num_materials * sizeof(D3DXMATERIAL);
3608 char *strings_out_ptr;
3609 D3DXMATERIAL *materials_ptr;
3611 for (i = 0; i < mesh_data.num_materials; i++) {
3612 if (mesh_data.materials[i].pTextureFilename)
3613 buffer_size += strlen(mesh_data.materials[i].pTextureFilename) + 1;
3616 hr = D3DXCreateBuffer(buffer_size, &materials);
3617 if (FAILED(hr)) goto cleanup;
3619 materials_ptr = ID3DXBuffer_GetBufferPointer(materials);
3620 memcpy(materials_ptr, mesh_data.materials, mesh_data.num_materials * sizeof(D3DXMATERIAL));
3621 strings_out_ptr = (char*)(materials_ptr + mesh_data.num_materials);
3622 for (i = 0; i < mesh_data.num_materials; i++) {
3623 if (materials_ptr[i].pTextureFilename) {
3624 strcpy(strings_out_ptr, mesh_data.materials[i].pTextureFilename);
3625 materials_ptr[i].pTextureFilename = strings_out_ptr;
3626 strings_out_ptr += strlen(mesh_data.materials[i].pTextureFilename) + 1;
3631 if (mesh_data.num_materials && effects_out) {
3632 hr = generate_effects(materials, mesh_data.num_materials, &effects);
3633 if (FAILED(hr)) goto cleanup;
3635 if (!materials_out) {
3636 ID3DXBuffer_Release(materials);
3637 materials = NULL;
3641 if (adjacency_out) {
3642 hr = D3DXCreateBuffer(mesh_data.num_tri_faces * 3 * sizeof(DWORD), &adjacency);
3643 if (FAILED(hr)) goto cleanup;
3644 hr = d3dxmesh->lpVtbl->GenerateAdjacency(d3dxmesh, 0.0f, ID3DXBuffer_GetBufferPointer(adjacency));
3645 if (FAILED(hr)) goto cleanup;
3648 *mesh_out = d3dxmesh;
3649 if (adjacency_out) *adjacency_out = adjacency;
3650 if (num_materials_out) *num_materials_out = mesh_data.num_materials;
3651 if (materials_out) *materials_out = materials;
3652 if (effects_out) *effects_out = effects;
3653 if (skin_info_out) *skin_info_out = mesh_data.skin_info;
3655 hr = D3D_OK;
3656 cleanup:
3657 if (FAILED(hr)) {
3658 if (d3dxmesh) IUnknown_Release(d3dxmesh);
3659 if (adjacency) ID3DXBuffer_Release(adjacency);
3660 if (materials) ID3DXBuffer_Release(materials);
3661 if (effects) ID3DXBuffer_Release(effects);
3662 if (mesh_data.skin_info) mesh_data.skin_info->lpVtbl->Release(mesh_data.skin_info);
3663 if (skin_info_out) *skin_info_out = NULL;
3665 HeapFree(GetProcessHeap(), 0, mesh_data.vertices);
3666 HeapFree(GetProcessHeap(), 0, mesh_data.num_tri_per_face);
3667 HeapFree(GetProcessHeap(), 0, mesh_data.indices);
3668 HeapFree(GetProcessHeap(), 0, mesh_data.normals);
3669 HeapFree(GetProcessHeap(), 0, mesh_data.normal_indices);
3670 destroy_materials(&mesh_data);
3671 HeapFree(GetProcessHeap(), 0, mesh_data.tex_coords);
3672 HeapFree(GetProcessHeap(), 0, mesh_data.vertex_colors);
3673 HeapFree(GetProcessHeap(), 0, duplications);
3674 return hr;
3677 HRESULT WINAPI D3DXLoadMeshHierarchyFromXA(const char *filename, DWORD options, struct IDirect3DDevice9 *device,
3678 struct ID3DXAllocateHierarchy *alloc_hier, struct ID3DXLoadUserData *load_user_data,
3679 D3DXFRAME **frame_hierarchy, struct ID3DXAnimationController **anim_controller)
3681 WCHAR *filenameW;
3682 HRESULT hr;
3683 int len;
3685 TRACE("filename %s, options %#x, device %p, alloc_hier %p, "
3686 "load_user_data %p, frame_hierarchy %p, anim_controller %p.\n",
3687 debugstr_a(filename), options, device, alloc_hier,
3688 load_user_data, frame_hierarchy, anim_controller);
3690 if (!filename)
3691 return D3DERR_INVALIDCALL;
3693 len = MultiByteToWideChar(CP_ACP, 0, filename, -1, NULL, 0);
3694 filenameW = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
3695 if (!filenameW) return E_OUTOFMEMORY;
3696 MultiByteToWideChar(CP_ACP, 0, filename, -1, filenameW, len);
3698 hr = D3DXLoadMeshHierarchyFromXW(filenameW, options, device,
3699 alloc_hier, load_user_data, frame_hierarchy, anim_controller);
3700 HeapFree(GetProcessHeap(), 0, filenameW);
3702 return hr;
3705 HRESULT WINAPI D3DXLoadMeshHierarchyFromXW(const WCHAR *filename, DWORD options, struct IDirect3DDevice9 *device,
3706 struct ID3DXAllocateHierarchy *alloc_hier, struct ID3DXLoadUserData *load_user_data,
3707 D3DXFRAME **frame_hierarchy, struct ID3DXAnimationController **anim_controller)
3709 void *buffer;
3710 HRESULT hr;
3711 DWORD size;
3713 TRACE("filename %s, options %#x, device %p, alloc_hier %p, "
3714 "load_user_data %p, frame_hierarchy %p, anim_controller %p.\n",
3715 debugstr_w(filename), options, device, alloc_hier,
3716 load_user_data, frame_hierarchy, anim_controller);
3718 if (!filename)
3719 return D3DERR_INVALIDCALL;
3721 hr = map_view_of_file(filename, &buffer, &size);
3722 if (FAILED(hr))
3723 return D3DXERR_INVALIDDATA;
3725 hr = D3DXLoadMeshHierarchyFromXInMemory(buffer, size, options, device,
3726 alloc_hier, load_user_data, frame_hierarchy, anim_controller);
3728 UnmapViewOfFile(buffer);
3730 return hr;
3733 static HRESULT filedata_get_name(ID3DXFileData *filedata, char **name)
3735 HRESULT hr;
3736 SIZE_T name_len;
3738 hr = filedata->lpVtbl->GetName(filedata, NULL, &name_len);
3739 if (FAILED(hr)) return hr;
3741 if (!name_len)
3742 name_len++;
3743 *name = HeapAlloc(GetProcessHeap(), 0, name_len);
3744 if (!*name) return E_OUTOFMEMORY;
3746 hr = filedata->lpVtbl->GetName(filedata, *name, &name_len);
3747 if (FAILED(hr))
3748 HeapFree(GetProcessHeap(), 0, *name);
3749 else if (!name_len)
3750 (*name)[0] = 0;
3752 return hr;
3755 static HRESULT load_mesh_container(struct ID3DXFileData *filedata, DWORD options, struct IDirect3DDevice9 *device,
3756 struct ID3DXAllocateHierarchy *alloc_hier, D3DXMESHCONTAINER **mesh_container)
3758 HRESULT hr;
3759 ID3DXBuffer *adjacency = NULL;
3760 ID3DXBuffer *materials = NULL;
3761 ID3DXBuffer *effects = NULL;
3762 ID3DXSkinInfo *skin_info = NULL;
3763 D3DXMESHDATA mesh_data;
3764 DWORD num_materials = 0;
3765 char *name = NULL;
3767 mesh_data.Type = D3DXMESHTYPE_MESH;
3768 mesh_data.u.pMesh = NULL;
3770 hr = D3DXLoadSkinMeshFromXof(filedata, options, device,
3771 &adjacency, &materials, &effects, &num_materials,
3772 &skin_info, &mesh_data.u.pMesh);
3773 if (FAILED(hr)) return hr;
3775 hr = filedata_get_name(filedata, &name);
3776 if (FAILED(hr)) goto cleanup;
3778 hr = alloc_hier->lpVtbl->CreateMeshContainer(alloc_hier, name, &mesh_data,
3779 materials ? ID3DXBuffer_GetBufferPointer(materials) : NULL,
3780 effects ? ID3DXBuffer_GetBufferPointer(effects) : NULL,
3781 num_materials,
3782 adjacency ? ID3DXBuffer_GetBufferPointer(adjacency) : NULL,
3783 skin_info, mesh_container);
3785 cleanup:
3786 if (materials) ID3DXBuffer_Release(materials);
3787 if (effects) ID3DXBuffer_Release(effects);
3788 if (adjacency) ID3DXBuffer_Release(adjacency);
3789 if (skin_info) IUnknown_Release(skin_info);
3790 if (mesh_data.u.pMesh) IUnknown_Release(mesh_data.u.pMesh);
3791 HeapFree(GetProcessHeap(), 0, name);
3792 return hr;
3795 static HRESULT parse_transform_matrix(ID3DXFileData *filedata, D3DXMATRIX *transform)
3797 HRESULT hr;
3798 SIZE_T data_size;
3799 const BYTE *data;
3801 /* template Matrix4x4 {
3802 * array FLOAT matrix[16];
3804 * template FrameTransformMatrix {
3805 * Matrix4x4 frameMatrix;
3809 hr = filedata->lpVtbl->Lock(filedata, &data_size, (const void**)&data);
3810 if (FAILED(hr)) return hr;
3812 if (data_size != sizeof(D3DXMATRIX)) {
3813 WARN("incorrect data size (%ld bytes)\n", data_size);
3814 filedata->lpVtbl->Unlock(filedata);
3815 return E_FAIL;
3818 memcpy(transform, data, sizeof(D3DXMATRIX));
3820 filedata->lpVtbl->Unlock(filedata);
3821 return D3D_OK;
3824 static HRESULT load_frame(struct ID3DXFileData *filedata, DWORD options, struct IDirect3DDevice9 *device,
3825 struct ID3DXAllocateHierarchy *alloc_hier, D3DXFRAME **frame_out)
3827 HRESULT hr;
3828 GUID type;
3829 ID3DXFileData *child;
3830 char *name = NULL;
3831 D3DXFRAME *frame = NULL;
3832 D3DXMESHCONTAINER **next_container;
3833 D3DXFRAME **next_child;
3834 SIZE_T i, nb_children;
3836 hr = filedata_get_name(filedata, &name);
3837 if (FAILED(hr)) return hr;
3839 hr = alloc_hier->lpVtbl->CreateFrame(alloc_hier, name, frame_out);
3840 HeapFree(GetProcessHeap(), 0, name);
3841 if (FAILED(hr)) return E_FAIL;
3843 frame = *frame_out;
3844 D3DXMatrixIdentity(&frame->TransformationMatrix);
3845 next_child = &frame->pFrameFirstChild;
3846 next_container = &frame->pMeshContainer;
3848 hr = filedata->lpVtbl->GetChildren(filedata, &nb_children);
3849 if (FAILED(hr))
3850 return hr;
3852 for (i = 0; i < nb_children; i++)
3854 hr = filedata->lpVtbl->GetChild(filedata, i, &child);
3855 if (FAILED(hr))
3856 return hr;
3857 hr = child->lpVtbl->GetType(child, &type);
3858 if (FAILED(hr))
3859 return hr;
3861 if (IsEqualGUID(&type, &TID_D3DRMMesh)) {
3862 hr = load_mesh_container(child, options, device, alloc_hier, next_container);
3863 if (SUCCEEDED(hr))
3864 next_container = &(*next_container)->pNextMeshContainer;
3865 } else if (IsEqualGUID(&type, &TID_D3DRMFrameTransformMatrix)) {
3866 hr = parse_transform_matrix(child, &frame->TransformationMatrix);
3867 } else if (IsEqualGUID(&type, &TID_D3DRMFrame)) {
3868 hr = load_frame(child, options, device, alloc_hier, next_child);
3869 if (SUCCEEDED(hr))
3870 next_child = &(*next_child)->pFrameSibling;
3872 if (FAILED(hr))
3873 return hr;
3876 return D3D_OK;
3879 HRESULT WINAPI D3DXLoadMeshHierarchyFromXInMemory(const void *memory, DWORD memory_size, DWORD options,
3880 struct IDirect3DDevice9 *device, struct ID3DXAllocateHierarchy *alloc_hier,
3881 struct ID3DXLoadUserData *load_user_data, D3DXFRAME **frame_hierarchy,
3882 struct ID3DXAnimationController **anim_controller)
3884 HRESULT hr;
3885 ID3DXFile *d3dxfile = NULL;
3886 ID3DXFileEnumObject *enumobj = NULL;
3887 ID3DXFileData *filedata = NULL;
3888 D3DXF_FILELOADMEMORY source;
3889 D3DXFRAME *first_frame = NULL;
3890 D3DXFRAME **next_frame = &first_frame;
3891 SIZE_T i, nb_children;
3892 GUID guid;
3894 TRACE("(%p, %u, %x, %p, %p, %p, %p, %p)\n", memory, memory_size, options,
3895 device, alloc_hier, load_user_data, frame_hierarchy, anim_controller);
3897 if (!memory || !memory_size || !device || !frame_hierarchy || !alloc_hier)
3898 return D3DERR_INVALIDCALL;
3899 if (load_user_data || anim_controller) {
3900 if (load_user_data)
3901 FIXME("Loading user data not implemented\n");
3902 if (anim_controller)
3903 FIXME("Animation controller creation not implemented\n");
3904 return E_NOTIMPL;
3907 hr = D3DXFileCreate(&d3dxfile);
3908 if (FAILED(hr)) goto cleanup;
3910 hr = d3dxfile->lpVtbl->RegisterTemplates(d3dxfile, D3DRM_XTEMPLATES, D3DRM_XTEMPLATE_BYTES);
3911 if (FAILED(hr)) goto cleanup;
3913 source.lpMemory = (void*)memory;
3914 source.dSize = memory_size;
3915 hr = d3dxfile->lpVtbl->CreateEnumObject(d3dxfile, &source, D3DXF_FILELOAD_FROMMEMORY, &enumobj);
3916 if (FAILED(hr)) goto cleanup;
3918 hr = enumobj->lpVtbl->GetChildren(enumobj, &nb_children);
3919 if (FAILED(hr))
3920 goto cleanup;
3922 for (i = 0; i < nb_children; i++)
3924 hr = enumobj->lpVtbl->GetChild(enumobj, i, &filedata);
3925 if (FAILED(hr))
3926 goto cleanup;
3928 hr = filedata->lpVtbl->GetType(filedata, &guid);
3929 if (SUCCEEDED(hr)) {
3930 if (IsEqualGUID(&guid, &TID_D3DRMMesh)) {
3931 hr = alloc_hier->lpVtbl->CreateFrame(alloc_hier, NULL, next_frame);
3932 if (FAILED(hr)) {
3933 hr = E_FAIL;
3934 goto cleanup;
3937 D3DXMatrixIdentity(&(*next_frame)->TransformationMatrix);
3939 hr = load_mesh_container(filedata, options, device, alloc_hier, &(*next_frame)->pMeshContainer);
3940 if (FAILED(hr)) goto cleanup;
3941 } else if (IsEqualGUID(&guid, &TID_D3DRMFrame)) {
3942 hr = load_frame(filedata, options, device, alloc_hier, next_frame);
3943 if (FAILED(hr)) goto cleanup;
3945 while (*next_frame)
3946 next_frame = &(*next_frame)->pFrameSibling;
3949 filedata->lpVtbl->Release(filedata);
3950 filedata = NULL;
3951 if (FAILED(hr))
3952 goto cleanup;
3955 if (!first_frame) {
3956 hr = E_FAIL;
3957 } else if (first_frame->pFrameSibling) {
3958 D3DXFRAME *root_frame = NULL;
3959 hr = alloc_hier->lpVtbl->CreateFrame(alloc_hier, NULL, &root_frame);
3960 if (FAILED(hr)) {
3961 hr = E_FAIL;
3962 goto cleanup;
3964 D3DXMatrixIdentity(&root_frame->TransformationMatrix);
3965 root_frame->pFrameFirstChild = first_frame;
3966 *frame_hierarchy = root_frame;
3967 hr = D3D_OK;
3968 } else {
3969 *frame_hierarchy = first_frame;
3970 hr = D3D_OK;
3973 cleanup:
3974 if (FAILED(hr) && first_frame) D3DXFrameDestroy(first_frame, alloc_hier);
3975 if (filedata) filedata->lpVtbl->Release(filedata);
3976 if (enumobj) enumobj->lpVtbl->Release(enumobj);
3977 if (d3dxfile) d3dxfile->lpVtbl->Release(d3dxfile);
3978 return hr;
3981 HRESULT WINAPI D3DXCleanMesh(D3DXCLEANTYPE clean_type, ID3DXMesh *mesh_in, const DWORD *adjacency_in,
3982 ID3DXMesh **mesh_out, DWORD *adjacency_out, ID3DXBuffer **errors_and_warnings)
3984 FIXME("(%u, %p, %p, %p, %p, %p)\n", clean_type, mesh_in, adjacency_in, mesh_out, adjacency_out, errors_and_warnings);
3986 return E_NOTIMPL;
3989 HRESULT WINAPI D3DXFrameDestroy(D3DXFRAME *frame, ID3DXAllocateHierarchy *alloc_hier)
3991 HRESULT hr;
3992 BOOL last = FALSE;
3994 TRACE("(%p, %p)\n", frame, alloc_hier);
3996 if (!frame || !alloc_hier)
3997 return D3DERR_INVALIDCALL;
3999 while (!last) {
4000 D3DXMESHCONTAINER *container;
4001 D3DXFRAME *current_frame;
4003 if (frame->pFrameSibling) {
4004 current_frame = frame->pFrameSibling;
4005 frame->pFrameSibling = current_frame->pFrameSibling;
4006 current_frame->pFrameSibling = NULL;
4007 } else {
4008 current_frame = frame;
4009 last = TRUE;
4012 if (current_frame->pFrameFirstChild) {
4013 hr = D3DXFrameDestroy(current_frame->pFrameFirstChild, alloc_hier);
4014 if (FAILED(hr)) return hr;
4015 current_frame->pFrameFirstChild = NULL;
4018 container = current_frame->pMeshContainer;
4019 while (container) {
4020 D3DXMESHCONTAINER *next_container = container->pNextMeshContainer;
4021 hr = alloc_hier->lpVtbl->DestroyMeshContainer(alloc_hier, container);
4022 if (FAILED(hr)) return hr;
4023 container = next_container;
4025 hr = alloc_hier->lpVtbl->DestroyFrame(alloc_hier, current_frame);
4026 if (FAILED(hr)) return hr;
4028 return D3D_OK;
4031 HRESULT WINAPI D3DXLoadMeshFromXA(const char *filename, DWORD options, struct IDirect3DDevice9 *device,
4032 struct ID3DXBuffer **adjacency, struct ID3DXBuffer **materials, struct ID3DXBuffer **effect_instances,
4033 DWORD *num_materials, struct ID3DXMesh **mesh)
4035 WCHAR *filenameW;
4036 HRESULT hr;
4037 int len;
4039 TRACE("filename %s, options %#x, device %p, adjacency %p, materials %p, "
4040 "effect_instances %p, num_materials %p, mesh %p.\n",
4041 debugstr_a(filename), options, device, adjacency, materials,
4042 effect_instances, num_materials, mesh);
4044 if (!filename)
4045 return D3DERR_INVALIDCALL;
4047 len = MultiByteToWideChar(CP_ACP, 0, filename, -1, NULL, 0);
4048 filenameW = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
4049 if (!filenameW) return E_OUTOFMEMORY;
4050 MultiByteToWideChar(CP_ACP, 0, filename, -1, filenameW, len);
4052 hr = D3DXLoadMeshFromXW(filenameW, options, device, adjacency, materials,
4053 effect_instances, num_materials, mesh);
4054 HeapFree(GetProcessHeap(), 0, filenameW);
4056 return hr;
4059 HRESULT WINAPI D3DXLoadMeshFromXW(const WCHAR *filename, DWORD options, struct IDirect3DDevice9 *device,
4060 struct ID3DXBuffer **adjacency, struct ID3DXBuffer **materials, struct ID3DXBuffer **effect_instances,
4061 DWORD *num_materials, struct ID3DXMesh **mesh)
4063 void *buffer;
4064 HRESULT hr;
4065 DWORD size;
4067 TRACE("filename %s, options %#x, device %p, adjacency %p, materials %p, "
4068 "effect_instances %p, num_materials %p, mesh %p.\n",
4069 debugstr_w(filename), options, device, adjacency, materials,
4070 effect_instances, num_materials, mesh);
4072 if (!filename)
4073 return D3DERR_INVALIDCALL;
4075 hr = map_view_of_file(filename, &buffer, &size);
4076 if (FAILED(hr))
4077 return D3DXERR_INVALIDDATA;
4079 hr = D3DXLoadMeshFromXInMemory(buffer, size, options, device, adjacency,
4080 materials, effect_instances, num_materials, mesh);
4082 UnmapViewOfFile(buffer);
4084 return hr;
4087 HRESULT WINAPI D3DXLoadMeshFromXResource(HMODULE module, const char *name, const char *type, DWORD options,
4088 struct IDirect3DDevice9 *device, struct ID3DXBuffer **adjacency, struct ID3DXBuffer **materials,
4089 struct ID3DXBuffer **effect_instances, DWORD *num_materials, struct ID3DXMesh **mesh)
4091 HRESULT hr;
4092 HRSRC resinfo;
4093 void *buffer;
4094 DWORD size;
4096 TRACE("module %p, name %s, type %s, options %#x, device %p, adjacency %p, "
4097 "materials %p, effect_instances %p, num_materials %p, mesh %p.\n",
4098 module, debugstr_a(name), debugstr_a(type), options, device, adjacency,
4099 materials, effect_instances, num_materials, mesh);
4101 resinfo = FindResourceA(module, name, type);
4102 if (!resinfo) return D3DXERR_INVALIDDATA;
4104 hr = load_resource_into_memory(module, resinfo, &buffer, &size);
4105 if (FAILED(hr)) return D3DXERR_INVALIDDATA;
4107 return D3DXLoadMeshFromXInMemory(buffer, size, options, device, adjacency,
4108 materials, effect_instances, num_materials, mesh);
4111 struct mesh_container
4113 struct list entry;
4114 ID3DXMesh *mesh;
4115 ID3DXBuffer *adjacency;
4116 ID3DXBuffer *materials;
4117 ID3DXBuffer *effects;
4118 DWORD num_materials;
4119 D3DXMATRIX transform;
4122 static HRESULT parse_frame(struct ID3DXFileData *filedata, DWORD options, struct IDirect3DDevice9 *device,
4123 const D3DXMATRIX *parent_transform, struct list *container_list, DWORD provide_flags)
4125 HRESULT hr;
4126 D3DXMATRIX transform = *parent_transform;
4127 ID3DXFileData *child;
4128 GUID type;
4129 SIZE_T i, nb_children;
4131 hr = filedata->lpVtbl->GetChildren(filedata, &nb_children);
4132 if (FAILED(hr))
4133 return hr;
4135 for (i = 0; i < nb_children; i++)
4137 hr = filedata->lpVtbl->GetChild(filedata, i, &child);
4138 if (FAILED(hr))
4139 return hr;
4140 hr = child->lpVtbl->GetType(child, &type);
4141 if (FAILED(hr))
4142 return hr;
4144 if (IsEqualGUID(&type, &TID_D3DRMMesh)) {
4145 struct mesh_container *container = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(*container));
4146 if (!container)
4147 return E_OUTOFMEMORY;
4148 list_add_tail(container_list, &container->entry);
4149 container->transform = transform;
4151 hr = D3DXLoadSkinMeshFromXof(child, options, device,
4152 (provide_flags & PROVIDE_ADJACENCY) ? &container->adjacency : NULL,
4153 (provide_flags & PROVIDE_MATERIALS) ? &container->materials : NULL,
4154 NULL, &container->num_materials, NULL, &container->mesh);
4155 } else if (IsEqualGUID(&type, &TID_D3DRMFrameTransformMatrix)) {
4156 D3DXMATRIX new_transform;
4157 hr = parse_transform_matrix(child, &new_transform);
4158 D3DXMatrixMultiply(&transform, &transform, &new_transform);
4159 } else if (IsEqualGUID(&type, &TID_D3DRMFrame)) {
4160 hr = parse_frame(child, options, device, &transform, container_list, provide_flags);
4162 if (FAILED(hr))
4163 return hr;
4166 return D3D_OK;
4169 HRESULT WINAPI D3DXLoadMeshFromXInMemory(const void *memory, DWORD memory_size, DWORD options,
4170 struct IDirect3DDevice9 *device, struct ID3DXBuffer **adjacency_out, struct ID3DXBuffer **materials_out,
4171 struct ID3DXBuffer **effects_out, DWORD *num_materials_out, struct ID3DXMesh **mesh_out)
4173 HRESULT hr;
4174 ID3DXFile *d3dxfile = NULL;
4175 ID3DXFileEnumObject *enumobj = NULL;
4176 ID3DXFileData *filedata = NULL;
4177 D3DXF_FILELOADMEMORY source;
4178 ID3DXBuffer *materials = NULL;
4179 ID3DXBuffer *effects = NULL;
4180 ID3DXBuffer *adjacency = NULL;
4181 struct list container_list = LIST_INIT(container_list);
4182 struct mesh_container *container_ptr, *next_container_ptr;
4183 DWORD num_materials;
4184 DWORD num_faces, num_vertices;
4185 D3DXMATRIX identity;
4186 DWORD provide_flags = 0;
4187 DWORD fvf;
4188 ID3DXMesh *concat_mesh = NULL;
4189 D3DVERTEXELEMENT9 concat_decl[MAX_FVF_DECL_SIZE];
4190 BYTE *concat_vertices = NULL;
4191 void *concat_indices = NULL;
4192 DWORD index_offset;
4193 DWORD concat_vertex_size;
4194 SIZE_T i, nb_children;
4195 GUID guid;
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 struct vertex
4530 D3DXVECTOR3 position;
4531 D3DXVECTOR3 normal;
4534 HRESULT WINAPI D3DXCreateBox(struct IDirect3DDevice9 *device, float width, float height,
4535 float depth, struct ID3DXMesh **mesh, struct ID3DXBuffer **adjacency)
4537 HRESULT hr;
4538 ID3DXMesh *box;
4539 struct vertex *vertices;
4540 WORD (*faces)[3];
4541 DWORD *adjacency_buf;
4542 unsigned int i, face;
4543 static const D3DXVECTOR3 unit_box[] =
4545 {-0.5f, -0.5f, -0.5f}, {-0.5f, -0.5f, 0.5f}, {-0.5f, 0.5f, 0.5f}, {-0.5f, 0.5f, -0.5f},
4546 {-0.5f, 0.5f, -0.5f}, {-0.5f, 0.5f, 0.5f}, { 0.5f, 0.5f, 0.5f}, { 0.5f, 0.5f, -0.5f},
4547 { 0.5f, 0.5f, -0.5f}, { 0.5f, 0.5f, 0.5f}, { 0.5f, -0.5f, 0.5f}, { 0.5f, -0.5f, -0.5f},
4548 {-0.5f, -0.5f, 0.5f}, {-0.5f, -0.5f, -0.5f}, { 0.5f, -0.5f, -0.5f}, { 0.5f, -0.5f, 0.5f},
4549 {-0.5f, -0.5f, 0.5f}, { 0.5f, -0.5f, 0.5f}, { 0.5f, 0.5f, 0.5f}, {-0.5f, 0.5f, 0.5f},
4550 {-0.5f, -0.5f, -0.5f}, {-0.5f, 0.5f, -0.5f}, { 0.5f, 0.5f, -0.5f}, { 0.5f, -0.5f, -0.5f}
4552 static const D3DXVECTOR3 normals[] =
4554 {-1.0f, 0.0f, 0.0f}, { 0.0f, 1.0f, 0.0f}, { 1.0f, 0.0f, 0.0f},
4555 { 0.0f, -1.0f, 0.0f}, { 0.0f, 0.0f, 1.0f}, { 0.0f, 0.0f, -1.0f}
4557 static const DWORD adjacency_table[] =
4559 6, 9, 1, 2, 10, 0, 1, 9, 3, 4, 10, 2,
4560 3, 8, 5, 7, 11, 4, 0, 11, 7, 5, 8, 6,
4561 7, 4, 9, 2, 0, 8, 1, 3, 11, 5, 6, 10
4564 TRACE("device %p, width %f, height %f, depth %f, mesh %p, adjacency %p\n",
4565 device, width, height, depth, mesh, adjacency);
4567 if (!device || width < 0.0f || height < 0.0f || depth < 0.0f || !mesh)
4569 return D3DERR_INVALIDCALL;
4572 if (FAILED(hr = D3DXCreateMeshFVF(12, 24, D3DXMESH_MANAGED, D3DFVF_XYZ | D3DFVF_NORMAL, device, &box)))
4574 return hr;
4577 if (FAILED(hr = box->lpVtbl->LockVertexBuffer(box, 0, (void **)&vertices)))
4579 box->lpVtbl->Release(box);
4580 return hr;
4583 if (FAILED(hr = box->lpVtbl->LockIndexBuffer(box, 0, (void **)&faces)))
4585 box->lpVtbl->UnlockVertexBuffer(box);
4586 box->lpVtbl->Release(box);
4587 return hr;
4590 for (i = 0; i < 24; i++)
4592 vertices[i].position.x = width * unit_box[i].x;
4593 vertices[i].position.y = height * unit_box[i].y;
4594 vertices[i].position.z = depth * unit_box[i].z;
4595 vertices[i].normal.x = normals[i / 4].x;
4596 vertices[i].normal.y = normals[i / 4].y;
4597 vertices[i].normal.z = normals[i / 4].z;
4600 face = 0;
4601 for (i = 0; i < 12; i++)
4603 faces[i][0] = face++;
4604 faces[i][1] = face++;
4605 faces[i][2] = (i % 2) ? face - 4 : face;
4608 box->lpVtbl->UnlockIndexBuffer(box);
4609 box->lpVtbl->UnlockVertexBuffer(box);
4611 if (adjacency)
4613 if (FAILED(hr = D3DXCreateBuffer(sizeof(adjacency_table), adjacency)))
4615 box->lpVtbl->Release(box);
4616 return hr;
4619 adjacency_buf = ID3DXBuffer_GetBufferPointer(*adjacency);
4620 memcpy(adjacency_buf, adjacency_table, sizeof(adjacency_table));
4623 *mesh = box;
4625 return D3D_OK;
4628 typedef WORD face[3];
4630 struct sincos_table
4632 float *sin;
4633 float *cos;
4636 static void free_sincos_table(struct sincos_table *sincos_table)
4638 HeapFree(GetProcessHeap(), 0, sincos_table->cos);
4639 HeapFree(GetProcessHeap(), 0, sincos_table->sin);
4642 /* pre compute sine and cosine tables; caller must free */
4643 static BOOL compute_sincos_table(struct sincos_table *sincos_table, float angle_start, float angle_step, int n)
4645 float angle;
4646 int i;
4648 sincos_table->sin = HeapAlloc(GetProcessHeap(), 0, n * sizeof(*sincos_table->sin));
4649 if (!sincos_table->sin)
4651 return FALSE;
4653 sincos_table->cos = HeapAlloc(GetProcessHeap(), 0, n * sizeof(*sincos_table->cos));
4654 if (!sincos_table->cos)
4656 HeapFree(GetProcessHeap(), 0, sincos_table->sin);
4657 return FALSE;
4660 angle = angle_start;
4661 for (i = 0; i < n; i++)
4663 sincos_table->sin[i] = sinf(angle);
4664 sincos_table->cos[i] = cosf(angle);
4665 angle += angle_step;
4668 return TRUE;
4671 static WORD vertex_index(UINT slices, int slice, int stack)
4673 return stack*slices+slice+1;
4676 HRESULT WINAPI D3DXCreateSphere(struct IDirect3DDevice9 *device, float radius, UINT slices,
4677 UINT stacks, struct ID3DXMesh **mesh, struct ID3DXBuffer **adjacency)
4679 DWORD number_of_vertices, number_of_faces;
4680 HRESULT hr;
4681 ID3DXMesh *sphere;
4682 struct vertex *vertices;
4683 face *faces;
4684 float phi_step, phi_start;
4685 struct sincos_table phi;
4686 float theta_step, theta, sin_theta, cos_theta;
4687 DWORD vertex, face, stack, slice;
4689 TRACE("(%p, %f, %u, %u, %p, %p)\n", device, radius, slices, stacks, mesh, adjacency);
4691 if (!device || radius < 0.0f || slices < 2 || stacks < 2 || !mesh)
4693 return D3DERR_INVALIDCALL;
4696 if (adjacency)
4698 FIXME("Case of adjacency != NULL not implemented.\n");
4699 return E_NOTIMPL;
4702 number_of_vertices = 2 + slices * (stacks-1);
4703 number_of_faces = 2 * slices + (stacks - 2) * (2 * slices);
4705 hr = D3DXCreateMeshFVF(number_of_faces, number_of_vertices, D3DXMESH_MANAGED,
4706 D3DFVF_XYZ | D3DFVF_NORMAL, device, &sphere);
4707 if (FAILED(hr))
4709 return hr;
4712 if (FAILED(hr = sphere->lpVtbl->LockVertexBuffer(sphere, 0, (void **)&vertices)))
4714 sphere->lpVtbl->Release(sphere);
4715 return hr;
4718 if (FAILED(hr = sphere->lpVtbl->LockIndexBuffer(sphere, 0, (void **)&faces)))
4720 sphere->lpVtbl->UnlockVertexBuffer(sphere);
4721 sphere->lpVtbl->Release(sphere);
4722 return hr;
4725 /* phi = angle on xz plane wrt z axis */
4726 phi_step = -2.0f * D3DX_PI / slices;
4727 phi_start = D3DX_PI / 2.0f;
4729 if (!compute_sincos_table(&phi, phi_start, phi_step, slices))
4731 sphere->lpVtbl->UnlockIndexBuffer(sphere);
4732 sphere->lpVtbl->UnlockVertexBuffer(sphere);
4733 sphere->lpVtbl->Release(sphere);
4734 return E_OUTOFMEMORY;
4737 /* theta = angle on xy plane wrt x axis */
4738 theta_step = D3DX_PI / stacks;
4739 theta = theta_step;
4741 vertex = 0;
4742 face = 0;
4744 vertices[vertex].normal.x = 0.0f;
4745 vertices[vertex].normal.y = 0.0f;
4746 vertices[vertex].normal.z = 1.0f;
4747 vertices[vertex].position.x = 0.0f;
4748 vertices[vertex].position.y = 0.0f;
4749 vertices[vertex].position.z = radius;
4750 vertex++;
4752 for (stack = 0; stack < stacks - 1; stack++)
4754 sin_theta = sinf(theta);
4755 cos_theta = cosf(theta);
4757 for (slice = 0; slice < slices; slice++)
4759 vertices[vertex].normal.x = sin_theta * phi.cos[slice];
4760 vertices[vertex].normal.y = sin_theta * phi.sin[slice];
4761 vertices[vertex].normal.z = cos_theta;
4762 vertices[vertex].position.x = radius * sin_theta * phi.cos[slice];
4763 vertices[vertex].position.y = radius * sin_theta * phi.sin[slice];
4764 vertices[vertex].position.z = radius * cos_theta;
4765 vertex++;
4767 if (slice > 0)
4769 if (stack == 0)
4771 /* top stack is triangle fan */
4772 faces[face][0] = 0;
4773 faces[face][1] = slice + 1;
4774 faces[face][2] = slice;
4775 face++;
4777 else
4779 /* stacks in between top and bottom are quad strips */
4780 faces[face][0] = vertex_index(slices, slice-1, stack-1);
4781 faces[face][1] = vertex_index(slices, slice, stack-1);
4782 faces[face][2] = vertex_index(slices, slice-1, stack);
4783 face++;
4785 faces[face][0] = vertex_index(slices, slice, stack-1);
4786 faces[face][1] = vertex_index(slices, slice, stack);
4787 faces[face][2] = vertex_index(slices, slice-1, stack);
4788 face++;
4793 theta += theta_step;
4795 if (stack == 0)
4797 faces[face][0] = 0;
4798 faces[face][1] = 1;
4799 faces[face][2] = slice;
4800 face++;
4802 else
4804 faces[face][0] = vertex_index(slices, slice-1, stack-1);
4805 faces[face][1] = vertex_index(slices, 0, stack-1);
4806 faces[face][2] = vertex_index(slices, slice-1, stack);
4807 face++;
4809 faces[face][0] = vertex_index(slices, 0, stack-1);
4810 faces[face][1] = vertex_index(slices, 0, stack);
4811 faces[face][2] = vertex_index(slices, slice-1, stack);
4812 face++;
4816 vertices[vertex].position.x = 0.0f;
4817 vertices[vertex].position.y = 0.0f;
4818 vertices[vertex].position.z = -radius;
4819 vertices[vertex].normal.x = 0.0f;
4820 vertices[vertex].normal.y = 0.0f;
4821 vertices[vertex].normal.z = -1.0f;
4823 /* bottom stack is triangle fan */
4824 for (slice = 1; slice < slices; slice++)
4826 faces[face][0] = vertex_index(slices, slice-1, stack-1);
4827 faces[face][1] = vertex_index(slices, slice, stack-1);
4828 faces[face][2] = vertex;
4829 face++;
4832 faces[face][0] = vertex_index(slices, slice-1, stack-1);
4833 faces[face][1] = vertex_index(slices, 0, stack-1);
4834 faces[face][2] = vertex;
4836 free_sincos_table(&phi);
4837 sphere->lpVtbl->UnlockIndexBuffer(sphere);
4838 sphere->lpVtbl->UnlockVertexBuffer(sphere);
4839 *mesh = sphere;
4841 return D3D_OK;
4844 HRESULT WINAPI D3DXCreateCylinder(struct IDirect3DDevice9 *device, float radius1, float radius2,
4845 float length, UINT slices, UINT stacks, struct ID3DXMesh **mesh, struct ID3DXBuffer **adjacency)
4847 DWORD number_of_vertices, number_of_faces;
4848 HRESULT hr;
4849 ID3DXMesh *cylinder;
4850 struct vertex *vertices;
4851 face *faces;
4852 float theta_step, theta_start;
4853 struct sincos_table theta;
4854 float delta_radius, radius, radius_step;
4855 float z, z_step, z_normal;
4856 DWORD vertex, face, slice, stack;
4858 TRACE("(%p, %f, %f, %f, %u, %u, %p, %p)\n", device, radius1, radius2, length, slices, stacks, mesh, adjacency);
4860 if (device == NULL || radius1 < 0.0f || radius2 < 0.0f || length < 0.0f || slices < 2 || stacks < 1 || mesh == NULL)
4862 return D3DERR_INVALIDCALL;
4865 if (adjacency)
4867 FIXME("Case of adjacency != NULL not implemented.\n");
4868 return E_NOTIMPL;
4871 number_of_vertices = 2 + (slices * (3 + stacks));
4872 number_of_faces = 2 * slices + stacks * (2 * slices);
4874 hr = D3DXCreateMeshFVF(number_of_faces, number_of_vertices, D3DXMESH_MANAGED,
4875 D3DFVF_XYZ | D3DFVF_NORMAL, device, &cylinder);
4876 if (FAILED(hr))
4878 return hr;
4881 if (FAILED(hr = cylinder->lpVtbl->LockVertexBuffer(cylinder, 0, (void **)&vertices)))
4883 cylinder->lpVtbl->Release(cylinder);
4884 return hr;
4887 if (FAILED(hr = cylinder->lpVtbl->LockIndexBuffer(cylinder, 0, (void **)&faces)))
4889 cylinder->lpVtbl->UnlockVertexBuffer(cylinder);
4890 cylinder->lpVtbl->Release(cylinder);
4891 return hr;
4894 /* theta = angle on xy plane wrt x axis */
4895 theta_step = -2.0f * D3DX_PI / slices;
4896 theta_start = D3DX_PI / 2.0f;
4898 if (!compute_sincos_table(&theta, theta_start, theta_step, slices))
4900 cylinder->lpVtbl->UnlockIndexBuffer(cylinder);
4901 cylinder->lpVtbl->UnlockVertexBuffer(cylinder);
4902 cylinder->lpVtbl->Release(cylinder);
4903 return E_OUTOFMEMORY;
4906 vertex = 0;
4907 face = 0;
4909 delta_radius = radius1 - radius2;
4910 radius = radius1;
4911 radius_step = delta_radius / stacks;
4913 z = -length / 2;
4914 z_step = length / stacks;
4915 z_normal = delta_radius / length;
4916 if (isnan(z_normal))
4918 z_normal = 0.0f;
4921 vertices[vertex].normal.x = 0.0f;
4922 vertices[vertex].normal.y = 0.0f;
4923 vertices[vertex].normal.z = -1.0f;
4924 vertices[vertex].position.x = 0.0f;
4925 vertices[vertex].position.y = 0.0f;
4926 vertices[vertex++].position.z = z;
4928 for (slice = 0; slice < slices; slice++, vertex++)
4930 vertices[vertex].normal.x = 0.0f;
4931 vertices[vertex].normal.y = 0.0f;
4932 vertices[vertex].normal.z = -1.0f;
4933 vertices[vertex].position.x = radius * theta.cos[slice];
4934 vertices[vertex].position.y = radius * theta.sin[slice];
4935 vertices[vertex].position.z = z;
4937 if (slice > 0)
4939 faces[face][0] = 0;
4940 faces[face][1] = slice;
4941 faces[face++][2] = slice + 1;
4945 faces[face][0] = 0;
4946 faces[face][1] = slice;
4947 faces[face++][2] = 1;
4949 for (stack = 1; stack <= stacks+1; stack++)
4951 for (slice = 0; slice < slices; slice++, vertex++)
4953 vertices[vertex].normal.x = theta.cos[slice];
4954 vertices[vertex].normal.y = theta.sin[slice];
4955 vertices[vertex].normal.z = z_normal;
4956 D3DXVec3Normalize(&vertices[vertex].normal, &vertices[vertex].normal);
4957 vertices[vertex].position.x = radius * theta.cos[slice];
4958 vertices[vertex].position.y = radius * theta.sin[slice];
4959 vertices[vertex].position.z = z;
4961 if (stack > 1 && slice > 0)
4963 faces[face][0] = vertex_index(slices, slice-1, stack-1);
4964 faces[face][1] = vertex_index(slices, slice-1, stack);
4965 faces[face++][2] = vertex_index(slices, slice, stack-1);
4967 faces[face][0] = vertex_index(slices, slice, stack-1);
4968 faces[face][1] = vertex_index(slices, slice-1, stack);
4969 faces[face++][2] = vertex_index(slices, slice, stack);
4973 if (stack > 1)
4975 faces[face][0] = vertex_index(slices, slice-1, stack-1);
4976 faces[face][1] = vertex_index(slices, slice-1, stack);
4977 faces[face++][2] = vertex_index(slices, 0, stack-1);
4979 faces[face][0] = vertex_index(slices, 0, stack-1);
4980 faces[face][1] = vertex_index(slices, slice-1, stack);
4981 faces[face++][2] = vertex_index(slices, 0, stack);
4984 if (stack < stacks + 1)
4986 z += z_step;
4987 radius -= radius_step;
4991 for (slice = 0; slice < slices; slice++, vertex++)
4993 vertices[vertex].normal.x = 0.0f;
4994 vertices[vertex].normal.y = 0.0f;
4995 vertices[vertex].normal.z = 1.0f;
4996 vertices[vertex].position.x = radius * theta.cos[slice];
4997 vertices[vertex].position.y = radius * theta.sin[slice];
4998 vertices[vertex].position.z = z;
5000 if (slice > 0)
5002 faces[face][0] = vertex_index(slices, slice-1, stack);
5003 faces[face][1] = number_of_vertices - 1;
5004 faces[face++][2] = vertex_index(slices, slice, stack);
5008 vertices[vertex].position.x = 0.0f;
5009 vertices[vertex].position.y = 0.0f;
5010 vertices[vertex].position.z = z;
5011 vertices[vertex].normal.x = 0.0f;
5012 vertices[vertex].normal.y = 0.0f;
5013 vertices[vertex].normal.z = 1.0f;
5015 faces[face][0] = vertex_index(slices, slice-1, stack);
5016 faces[face][1] = number_of_vertices - 1;
5017 faces[face][2] = vertex_index(slices, 0, stack);
5019 free_sincos_table(&theta);
5020 cylinder->lpVtbl->UnlockIndexBuffer(cylinder);
5021 cylinder->lpVtbl->UnlockVertexBuffer(cylinder);
5022 *mesh = cylinder;
5024 return D3D_OK;
5027 HRESULT WINAPI D3DXCreateTeapot(struct IDirect3DDevice9 *device,
5028 struct ID3DXMesh **mesh, struct ID3DXBuffer **adjacency)
5030 FIXME("(%p, %p, %p): stub\n", device, mesh, adjacency);
5032 return E_NOTIMPL;
5035 HRESULT WINAPI D3DXCreateTextA(struct IDirect3DDevice9 *device, HDC hdc, const char *text, float deviation,
5036 float extrusion, struct ID3DXMesh **mesh, struct ID3DXBuffer **adjacency, GLYPHMETRICSFLOAT *glyphmetrics)
5038 WCHAR *textW;
5039 HRESULT hr;
5040 int len;
5042 TRACE("device %p, hdc %p, text %s, deviation %.8e, extrusion %.8e, mesh %p, adjacency %p, glyphmetrics %p.\n",
5043 device, hdc, debugstr_a(text), deviation, extrusion, mesh, adjacency, glyphmetrics);
5045 if (!text)
5046 return D3DERR_INVALIDCALL;
5048 len = MultiByteToWideChar(CP_ACP, 0, text, -1, NULL, 0);
5049 textW = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
5050 MultiByteToWideChar(CP_ACP, 0, text, -1, textW, len);
5052 hr = D3DXCreateTextW(device, hdc, textW, deviation, extrusion,
5053 mesh, adjacency, glyphmetrics);
5054 HeapFree(GetProcessHeap(), 0, textW);
5056 return hr;
5059 enum pointtype {
5060 POINTTYPE_CURVE = 0,
5061 POINTTYPE_CORNER,
5062 POINTTYPE_CURVE_START,
5063 POINTTYPE_CURVE_END,
5064 POINTTYPE_CURVE_MIDDLE,
5067 struct point2d
5069 D3DXVECTOR2 pos;
5070 enum pointtype corner;
5073 struct dynamic_array
5075 int count, capacity;
5076 void *items;
5079 /* is a dynamic_array */
5080 struct outline
5082 int count, capacity;
5083 struct point2d *items;
5086 /* is a dynamic_array */
5087 struct outline_array
5089 int count, capacity;
5090 struct outline *items;
5093 struct face_array
5095 int count;
5096 face *items;
5099 struct point2d_index
5101 struct outline *outline;
5102 int vertex;
5105 struct point2d_index_array
5107 int count;
5108 struct point2d_index *items;
5111 struct glyphinfo
5113 struct outline_array outlines;
5114 struct face_array faces;
5115 struct point2d_index_array ordered_vertices;
5116 float offset_x;
5119 /* is an dynamic_array */
5120 struct word_array
5122 int count, capacity;
5123 WORD *items;
5126 /* complex polygons are split into monotone polygons, which have
5127 * at most 2 intersections with the vertical sweep line */
5128 struct triangulation
5130 struct word_array vertex_stack;
5131 BOOL last_on_top, merging;
5134 /* is an dynamic_array */
5135 struct triangulation_array
5137 int count, capacity;
5138 struct triangulation *items;
5140 struct glyphinfo *glyph;
5143 static BOOL reserve(struct dynamic_array *array, int count, int itemsize)
5145 if (count > array->capacity) {
5146 void *new_buffer;
5147 int new_capacity;
5148 if (array->items && array->capacity) {
5149 new_capacity = max(array->capacity * 2, count);
5150 new_buffer = HeapReAlloc(GetProcessHeap(), 0, array->items, new_capacity * itemsize);
5151 } else {
5152 new_capacity = max(16, count);
5153 new_buffer = HeapAlloc(GetProcessHeap(), 0, new_capacity * itemsize);
5155 if (!new_buffer)
5156 return FALSE;
5157 array->items = new_buffer;
5158 array->capacity = new_capacity;
5160 return TRUE;
5163 static struct point2d *add_points(struct outline *array, int num)
5165 struct point2d *item;
5167 if (!reserve((struct dynamic_array *)array, array->count + num, sizeof(array->items[0])))
5168 return NULL;
5170 item = &array->items[array->count];
5171 array->count += num;
5172 return item;
5175 static struct outline *add_outline(struct outline_array *array)
5177 struct outline *item;
5179 if (!reserve((struct dynamic_array *)array, array->count + 1, sizeof(array->items[0])))
5180 return NULL;
5182 item = &array->items[array->count++];
5183 ZeroMemory(item, sizeof(*item));
5184 return item;
5187 static inline face *add_face(struct face_array *array)
5189 return &array->items[array->count++];
5192 static struct triangulation *add_triangulation(struct triangulation_array *array)
5194 struct triangulation *item;
5196 if (!reserve((struct dynamic_array *)array, array->count + 1, sizeof(array->items[0])))
5197 return NULL;
5199 item = &array->items[array->count++];
5200 ZeroMemory(item, sizeof(*item));
5201 return item;
5204 static HRESULT add_vertex_index(struct word_array *array, WORD vertex_index)
5206 if (!reserve((struct dynamic_array *)array, array->count + 1, sizeof(array->items[0])))
5207 return E_OUTOFMEMORY;
5209 array->items[array->count++] = vertex_index;
5210 return S_OK;
5213 /* assume fixed point numbers can be converted to float point in place */
5214 C_ASSERT(sizeof(FIXED) == sizeof(float));
5215 C_ASSERT(sizeof(POINTFX) == sizeof(D3DXVECTOR2));
5217 static inline D3DXVECTOR2 *convert_fixed_to_float(POINTFX *pt, int count, unsigned int emsquare)
5219 D3DXVECTOR2 *ret = (D3DXVECTOR2*)pt;
5220 while (count--) {
5221 D3DXVECTOR2 *pt_flt = (D3DXVECTOR2*)pt;
5222 pt_flt->x = (pt->x.value + pt->x.fract / (float)0x10000) / emsquare;
5223 pt_flt->y = (pt->y.value + pt->y.fract / (float)0x10000) / emsquare;
5224 pt++;
5226 return ret;
5229 static HRESULT add_bezier_points(struct outline *outline, const D3DXVECTOR2 *p1,
5230 const D3DXVECTOR2 *p2, const D3DXVECTOR2 *p3,
5231 float max_deviation_sq)
5233 D3DXVECTOR2 split1 = {0, 0}, split2 = {0, 0}, middle, vec;
5234 float deviation_sq;
5236 D3DXVec2Scale(&split1, D3DXVec2Add(&split1, p1, p2), 0.5f);
5237 D3DXVec2Scale(&split2, D3DXVec2Add(&split2, p2, p3), 0.5f);
5238 D3DXVec2Scale(&middle, D3DXVec2Add(&middle, &split1, &split2), 0.5f);
5240 deviation_sq = D3DXVec2LengthSq(D3DXVec2Subtract(&vec, &middle, p2));
5241 if (deviation_sq < max_deviation_sq) {
5242 struct point2d *pt = add_points(outline, 1);
5243 if (!pt) return E_OUTOFMEMORY;
5244 pt->pos = *p2;
5245 pt->corner = POINTTYPE_CURVE;
5246 /* the end point is omitted because the end line merges into the next segment of
5247 * the split bezier curve, and the end of the split bezier curve is added outside
5248 * this recursive function. */
5249 } else {
5250 HRESULT hr = add_bezier_points(outline, p1, &split1, &middle, max_deviation_sq);
5251 if (hr != S_OK) return hr;
5252 hr = add_bezier_points(outline, &middle, &split2, p3, max_deviation_sq);
5253 if (hr != S_OK) return hr;
5256 return S_OK;
5259 static inline BOOL is_direction_similar(D3DXVECTOR2 *dir1, D3DXVECTOR2 *dir2, float cos_theta)
5261 /* dot product = cos(theta) */
5262 return D3DXVec2Dot(dir1, dir2) > cos_theta;
5265 static inline D3DXVECTOR2 *unit_vec2(D3DXVECTOR2 *dir, const D3DXVECTOR2 *pt1, const D3DXVECTOR2 *pt2)
5267 return D3DXVec2Normalize(D3DXVec2Subtract(dir, pt2, pt1), dir);
5270 struct cos_table
5272 float cos_half;
5273 float cos_45;
5274 float cos_90;
5277 static BOOL attempt_line_merge(struct outline *outline,
5278 int pt_index,
5279 const D3DXVECTOR2 *nextpt,
5280 BOOL to_curve,
5281 const struct cos_table *table)
5283 D3DXVECTOR2 curdir, lastdir;
5284 struct point2d *prevpt, *pt;
5285 BOOL ret = FALSE;
5287 pt = &outline->items[pt_index];
5288 pt_index = (pt_index - 1 + outline->count) % outline->count;
5289 prevpt = &outline->items[pt_index];
5291 if (to_curve)
5292 pt->corner = pt->corner != POINTTYPE_CORNER ? POINTTYPE_CURVE_MIDDLE : POINTTYPE_CURVE_START;
5294 if (outline->count < 2)
5295 return FALSE;
5297 /* remove last point if the next line continues the last line */
5298 unit_vec2(&lastdir, &prevpt->pos, &pt->pos);
5299 unit_vec2(&curdir, &pt->pos, nextpt);
5300 if (is_direction_similar(&lastdir, &curdir, table->cos_half))
5302 outline->count--;
5303 if (pt->corner == POINTTYPE_CURVE_END)
5304 prevpt->corner = pt->corner;
5305 if (prevpt->corner == POINTTYPE_CURVE_END && to_curve)
5306 prevpt->corner = POINTTYPE_CURVE_MIDDLE;
5307 pt = prevpt;
5309 ret = TRUE;
5310 if (outline->count < 2)
5311 return ret;
5313 pt_index = (pt_index - 1 + outline->count) % outline->count;
5314 prevpt = &outline->items[pt_index];
5315 unit_vec2(&lastdir, &prevpt->pos, &pt->pos);
5316 unit_vec2(&curdir, &pt->pos, nextpt);
5318 return ret;
5321 static HRESULT create_outline(struct glyphinfo *glyph, void *raw_outline, int datasize,
5322 float max_deviation_sq, unsigned int emsquare,
5323 const struct cos_table *cos_table)
5325 TTPOLYGONHEADER *header = (TTPOLYGONHEADER *)raw_outline;
5327 while ((char *)header < (char *)raw_outline + datasize)
5329 TTPOLYCURVE *curve = (TTPOLYCURVE *)(header + 1);
5330 struct point2d *lastpt, *pt;
5331 D3DXVECTOR2 lastdir;
5332 D3DXVECTOR2 *pt_flt;
5333 int j;
5334 struct outline *outline = add_outline(&glyph->outlines);
5336 if (!outline)
5337 return E_OUTOFMEMORY;
5339 pt = add_points(outline, 1);
5340 if (!pt)
5341 return E_OUTOFMEMORY;
5342 pt_flt = convert_fixed_to_float(&header->pfxStart, 1, emsquare);
5343 pt->pos = *pt_flt;
5344 pt->corner = POINTTYPE_CORNER;
5346 if (header->dwType != TT_POLYGON_TYPE)
5347 FIXME("Unknown header type %d\n", header->dwType);
5349 while ((char *)curve < (char *)header + header->cb)
5351 D3DXVECTOR2 bezier_start = outline->items[outline->count - 1].pos;
5352 BOOL to_curve = curve->wType != TT_PRIM_LINE && curve->cpfx > 1;
5353 unsigned int j2 = 0;
5355 if (!curve->cpfx) {
5356 curve = (TTPOLYCURVE *)&curve->apfx[curve->cpfx];
5357 continue;
5360 pt_flt = convert_fixed_to_float(curve->apfx, curve->cpfx, emsquare);
5362 attempt_line_merge(outline, outline->count - 1, &pt_flt[0], to_curve, cos_table);
5364 if (to_curve)
5366 HRESULT hr;
5367 int count = curve->cpfx;
5369 while (count > 2)
5371 D3DXVECTOR2 bezier_end;
5373 D3DXVec2Scale(&bezier_end, D3DXVec2Add(&bezier_end, &pt_flt[j2], &pt_flt[j2+1]), 0.5f);
5374 hr = add_bezier_points(outline, &bezier_start, &pt_flt[j2], &bezier_end, max_deviation_sq);
5375 if (hr != S_OK)
5376 return hr;
5377 bezier_start = bezier_end;
5378 count--;
5379 j2++;
5381 hr = add_bezier_points(outline, &bezier_start, &pt_flt[j2], &pt_flt[j2+1], max_deviation_sq);
5382 if (hr != S_OK)
5383 return hr;
5385 pt = add_points(outline, 1);
5386 if (!pt)
5387 return E_OUTOFMEMORY;
5388 j2++;
5389 pt->pos = pt_flt[j2];
5390 pt->corner = POINTTYPE_CURVE_END;
5391 } else {
5392 pt = add_points(outline, curve->cpfx);
5393 if (!pt)
5394 return E_OUTOFMEMORY;
5395 for (j2 = 0; j2 < curve->cpfx; j2++)
5397 pt->pos = pt_flt[j2];
5398 pt->corner = POINTTYPE_CORNER;
5399 pt++;
5403 curve = (TTPOLYCURVE *)&curve->apfx[curve->cpfx];
5406 /* remove last point if the next line continues the last line */
5407 if (outline->count >= 3) {
5408 BOOL to_curve;
5410 lastpt = &outline->items[outline->count - 1];
5411 pt = &outline->items[0];
5412 if (pt->pos.x == lastpt->pos.x && pt->pos.y == lastpt->pos.y) {
5413 if (lastpt->corner == POINTTYPE_CURVE_END)
5415 if (pt->corner == POINTTYPE_CURVE_START)
5416 pt->corner = POINTTYPE_CURVE_MIDDLE;
5417 else
5418 pt->corner = POINTTYPE_CURVE_END;
5420 outline->count--;
5421 lastpt = &outline->items[outline->count - 1];
5422 } else {
5423 /* outline closed with a line from end to start point */
5424 attempt_line_merge(outline, outline->count - 1, &pt->pos, FALSE, cos_table);
5426 lastpt = &outline->items[0];
5427 to_curve = lastpt->corner != POINTTYPE_CORNER && lastpt->corner != POINTTYPE_CURVE_END;
5428 if (lastpt->corner == POINTTYPE_CURVE_START)
5429 lastpt->corner = POINTTYPE_CORNER;
5430 pt = &outline->items[1];
5431 if (attempt_line_merge(outline, 0, &pt->pos, to_curve, cos_table))
5432 *lastpt = outline->items[outline->count];
5435 lastpt = &outline->items[outline->count - 1];
5436 pt = &outline->items[0];
5437 unit_vec2(&lastdir, &lastpt->pos, &pt->pos);
5438 for (j = 0; j < outline->count; j++)
5440 D3DXVECTOR2 curdir;
5442 lastpt = pt;
5443 pt = &outline->items[(j + 1) % outline->count];
5444 unit_vec2(&curdir, &lastpt->pos, &pt->pos);
5446 switch (lastpt->corner)
5448 case POINTTYPE_CURVE_START:
5449 case POINTTYPE_CURVE_END:
5450 if (!is_direction_similar(&lastdir, &curdir, cos_table->cos_45))
5451 lastpt->corner = POINTTYPE_CORNER;
5452 break;
5453 case POINTTYPE_CURVE_MIDDLE:
5454 if (!is_direction_similar(&lastdir, &curdir, cos_table->cos_90))
5455 lastpt->corner = POINTTYPE_CORNER;
5456 else
5457 lastpt->corner = POINTTYPE_CURVE;
5458 break;
5459 default:
5460 break;
5462 lastdir = curdir;
5465 header = (TTPOLYGONHEADER *)((char *)header + header->cb);
5467 return S_OK;
5470 /* Get the y-distance from a line to a point */
5471 static float get_line_to_point_y_distance(D3DXVECTOR2 *line_pt1,
5472 D3DXVECTOR2 *line_pt2,
5473 D3DXVECTOR2 *point)
5475 D3DXVECTOR2 line_vec = {0, 0};
5476 float line_pt_dx;
5477 float line_y;
5479 D3DXVec2Subtract(&line_vec, line_pt2, line_pt1);
5480 line_pt_dx = point->x - line_pt1->x;
5481 line_y = line_pt1->y + (line_vec.y * line_pt_dx) / line_vec.x;
5482 return point->y - line_y;
5485 static D3DXVECTOR2 *get_indexed_point(struct point2d_index *pt_idx)
5487 return &pt_idx->outline->items[pt_idx->vertex].pos;
5490 static D3DXVECTOR2 *get_ordered_vertex(struct glyphinfo *glyph, WORD index)
5492 return get_indexed_point(&glyph->ordered_vertices.items[index]);
5495 static void remove_triangulation(struct triangulation_array *array, struct triangulation *item)
5497 HeapFree(GetProcessHeap(), 0, item->vertex_stack.items);
5498 MoveMemory(item, item + 1, (char*)&array->items[array->count] - (char*)(item + 1));
5499 array->count--;
5502 static HRESULT triangulation_add_point(struct triangulation **t_ptr,
5503 struct triangulation_array *triangulations,
5504 WORD vtx_idx,
5505 BOOL to_top)
5507 struct glyphinfo *glyph = triangulations->glyph;
5508 struct triangulation *t = *t_ptr;
5509 HRESULT hr;
5510 face *face;
5511 int f1, f2;
5513 if (t->last_on_top) {
5514 f1 = 1;
5515 f2 = 2;
5516 } else {
5517 f1 = 2;
5518 f2 = 1;
5521 if (t->last_on_top != to_top && t->vertex_stack.count > 1) {
5522 /* consume all vertices on the stack */
5523 WORD last_pt = t->vertex_stack.items[0];
5524 int i;
5525 for (i = 1; i < t->vertex_stack.count; i++)
5527 face = add_face(&glyph->faces);
5528 if (!face) return E_OUTOFMEMORY;
5529 (*face)[0] = vtx_idx;
5530 (*face)[f1] = last_pt;
5531 (*face)[f2] = last_pt = t->vertex_stack.items[i];
5533 t->vertex_stack.items[0] = last_pt;
5534 t->vertex_stack.count = 1;
5535 } else if (t->vertex_stack.count > 1) {
5536 int i = t->vertex_stack.count - 1;
5537 D3DXVECTOR2 *point = get_ordered_vertex(glyph, vtx_idx);
5538 WORD top_idx = t->vertex_stack.items[i--];
5539 D3DXVECTOR2 *top_pt = get_ordered_vertex(glyph, top_idx);
5541 while (i >= 0)
5543 WORD prev_idx = t->vertex_stack.items[i--];
5544 D3DXVECTOR2 *prev_pt = get_ordered_vertex(glyph, prev_idx);
5546 if (prev_pt->x != top_pt->x &&
5547 ((to_top && get_line_to_point_y_distance(prev_pt, top_pt, point) > 0) ||
5548 (!to_top && get_line_to_point_y_distance(prev_pt, top_pt, point) < 0)))
5549 break;
5551 face = add_face(&glyph->faces);
5552 if (!face) return E_OUTOFMEMORY;
5553 (*face)[0] = vtx_idx;
5554 (*face)[f1] = prev_idx;
5555 (*face)[f2] = top_idx;
5557 top_pt = prev_pt;
5558 top_idx = prev_idx;
5559 t->vertex_stack.count--;
5562 t->last_on_top = to_top;
5564 hr = add_vertex_index(&t->vertex_stack, vtx_idx);
5566 if (hr == S_OK && t->merging) {
5567 struct triangulation *t2;
5569 t2 = to_top ? t - 1 : t + 1;
5570 t2->merging = FALSE;
5571 hr = triangulation_add_point(&t2, triangulations, vtx_idx, to_top);
5572 if (hr != S_OK) return hr;
5573 remove_triangulation(triangulations, t);
5574 if (t2 > t)
5575 t2--;
5576 *t_ptr = t2;
5578 return hr;
5581 /* check if the point is next on the outline for either the top or bottom */
5582 static D3DXVECTOR2 *triangulation_get_next_point(struct triangulation *t, struct glyphinfo *glyph, BOOL on_top)
5584 int i = t->last_on_top == on_top ? t->vertex_stack.count - 1 : 0;
5585 WORD idx = t->vertex_stack.items[i];
5586 struct point2d_index *pt_idx = &glyph->ordered_vertices.items[idx];
5587 struct outline *outline = pt_idx->outline;
5589 if (on_top)
5590 i = (pt_idx->vertex + outline->count - 1) % outline->count;
5591 else
5592 i = (pt_idx->vertex + 1) % outline->count;
5594 return &outline->items[i].pos;
5597 static int compare_vertex_indices(const void *a, const void *b)
5599 const struct point2d_index *idx1 = a, *idx2 = b;
5600 const D3DXVECTOR2 *p1 = &idx1->outline->items[idx1->vertex].pos;
5601 const D3DXVECTOR2 *p2 = &idx2->outline->items[idx2->vertex].pos;
5602 float diff = p1->x - p2->x;
5604 if (diff == 0.0f)
5605 diff = p1->y - p2->y;
5607 return diff == 0.0f ? 0 : (diff > 0.0f ? -1 : 1);
5610 static HRESULT triangulate(struct triangulation_array *triangulations)
5612 int sweep_idx;
5613 HRESULT hr;
5614 struct glyphinfo *glyph = triangulations->glyph;
5615 int nb_vertices = 0;
5616 int i;
5617 struct point2d_index *idx_ptr;
5619 for (i = 0; i < glyph->outlines.count; i++)
5620 nb_vertices += glyph->outlines.items[i].count;
5622 glyph->ordered_vertices.items = HeapAlloc(GetProcessHeap(), 0,
5623 nb_vertices * sizeof(*glyph->ordered_vertices.items));
5624 if (!glyph->ordered_vertices.items)
5625 return E_OUTOFMEMORY;
5627 idx_ptr = glyph->ordered_vertices.items;
5628 for (i = 0; i < glyph->outlines.count; i++)
5630 struct outline *outline = &glyph->outlines.items[i];
5631 int j;
5633 idx_ptr->outline = outline;
5634 idx_ptr->vertex = 0;
5635 idx_ptr++;
5636 for (j = outline->count - 1; j > 0; j--)
5638 idx_ptr->outline = outline;
5639 idx_ptr->vertex = j;
5640 idx_ptr++;
5643 glyph->ordered_vertices.count = nb_vertices;
5645 /* Native implementation seems to try to create a triangle fan from
5646 * the first outline point if the glyph only has one outline. */
5647 if (glyph->outlines.count == 1)
5649 struct outline *outline = glyph->outlines.items;
5650 D3DXVECTOR2 *base = &outline->items[0].pos;
5651 D3DXVECTOR2 *last = &outline->items[1].pos;
5652 float ccw = 0;
5654 for (i = 2; i < outline->count; i++)
5656 D3DXVECTOR2 *next = &outline->items[i].pos;
5657 D3DXVECTOR2 v1 = {0.0f, 0.0f};
5658 D3DXVECTOR2 v2 = {0.0f, 0.0f};
5660 D3DXVec2Subtract(&v1, base, last);
5661 D3DXVec2Subtract(&v2, last, next);
5662 ccw = D3DXVec2CCW(&v1, &v2);
5663 if (ccw > 0.0f)
5664 break;
5666 last = next;
5668 if (ccw <= 0)
5670 glyph->faces.items = HeapAlloc(GetProcessHeap(), 0,
5671 (outline->count - 2) * sizeof(glyph->faces.items[0]));
5672 if (!glyph->faces.items)
5673 return E_OUTOFMEMORY;
5675 glyph->faces.count = outline->count - 2;
5676 for (i = 0; i < glyph->faces.count; i++)
5678 glyph->faces.items[i][0] = 0;
5679 glyph->faces.items[i][1] = i + 1;
5680 glyph->faces.items[i][2] = i + 2;
5682 return S_OK;
5686 /* Perform 2D polygon triangulation for complex glyphs.
5687 * Triangulation is performed using a sweep line concept, from right to left,
5688 * by processing vertices in sorted order. Complex polygons are split into
5689 * monotone polygons which are triangulated separately. */
5690 /* FIXME: The order of the faces is not consistent with the native implementation. */
5692 /* Reserve space for maximum possible faces from triangulation.
5693 * # faces for outer outlines = outline->count - 2
5694 * # faces for inner outlines = outline->count + 2
5695 * There must be at least 1 outer outline. */
5696 glyph->faces.items = HeapAlloc(GetProcessHeap(), 0,
5697 (nb_vertices + glyph->outlines.count * 2 - 4) * sizeof(glyph->faces.items[0]));
5698 if (!glyph->faces.items)
5699 return E_OUTOFMEMORY;
5701 qsort(glyph->ordered_vertices.items, nb_vertices,
5702 sizeof(glyph->ordered_vertices.items[0]), compare_vertex_indices);
5703 for (sweep_idx = 0; sweep_idx < glyph->ordered_vertices.count; sweep_idx++)
5705 int start = 0;
5706 int end = triangulations->count;
5708 while (start < end)
5710 D3DXVECTOR2 *sweep_vtx = get_ordered_vertex(glyph, sweep_idx);
5711 int current = (start + end) / 2;
5712 struct triangulation *t = &triangulations->items[current];
5713 BOOL on_top_outline = FALSE;
5714 D3DXVECTOR2 *top_next, *bottom_next;
5715 WORD top_idx, bottom_idx;
5717 if (t->merging && t->last_on_top)
5718 top_next = triangulation_get_next_point(t + 1, glyph, TRUE);
5719 else
5720 top_next = triangulation_get_next_point(t, glyph, TRUE);
5721 if (sweep_vtx == top_next)
5723 if (t->merging && t->last_on_top)
5724 t++;
5725 hr = triangulation_add_point(&t, triangulations, sweep_idx, TRUE);
5726 if (hr != S_OK) return hr;
5728 if (t + 1 < &triangulations->items[triangulations->count] &&
5729 triangulation_get_next_point(t + 1, glyph, FALSE) == sweep_vtx)
5731 /* point also on bottom outline of higher triangulation */
5732 struct triangulation *t2 = t + 1;
5733 hr = triangulation_add_point(&t2, triangulations, sweep_idx, FALSE);
5734 if (hr != S_OK) return hr;
5736 t->merging = TRUE;
5737 t2->merging = TRUE;
5739 on_top_outline = TRUE;
5742 if (t->merging && !t->last_on_top)
5743 bottom_next = triangulation_get_next_point(t - 1, glyph, FALSE);
5744 else
5745 bottom_next = triangulation_get_next_point(t, glyph, FALSE);
5746 if (sweep_vtx == bottom_next)
5748 if (t->merging && !t->last_on_top)
5749 t--;
5750 if (on_top_outline) {
5751 /* outline finished */
5752 remove_triangulation(triangulations, t);
5753 break;
5756 hr = triangulation_add_point(&t, triangulations, sweep_idx, FALSE);
5757 if (hr != S_OK) return hr;
5759 if (t > triangulations->items &&
5760 triangulation_get_next_point(t - 1, glyph, TRUE) == sweep_vtx)
5762 struct triangulation *t2 = t - 1;
5763 /* point also on top outline of lower triangulation */
5764 hr = triangulation_add_point(&t2, triangulations, sweep_idx, TRUE);
5765 if (hr != S_OK) return hr;
5766 t = t2 + 1; /* t may be invalidated by triangulation merging */
5768 t->merging = TRUE;
5769 t2->merging = TRUE;
5771 break;
5773 if (on_top_outline)
5774 break;
5776 if (t->last_on_top) {
5777 top_idx = t->vertex_stack.items[t->vertex_stack.count - 1];
5778 bottom_idx = t->vertex_stack.items[0];
5779 } else {
5780 top_idx = t->vertex_stack.items[0];
5781 bottom_idx = t->vertex_stack.items[t->vertex_stack.count - 1];
5784 /* check if the point is inside or outside this polygon */
5785 if (get_line_to_point_y_distance(get_ordered_vertex(glyph, top_idx),
5786 top_next, sweep_vtx) > 0)
5787 { /* above */
5788 start = current + 1;
5789 } else if (get_line_to_point_y_distance(get_ordered_vertex(glyph, bottom_idx),
5790 bottom_next, sweep_vtx) < 0)
5791 { /* below */
5792 end = current;
5793 } else if (t->merging) {
5794 /* inside, so cancel merging */
5795 struct triangulation *t2 = t->last_on_top ? t + 1 : t - 1;
5796 t->merging = FALSE;
5797 t2->merging = FALSE;
5798 hr = triangulation_add_point(&t, triangulations, sweep_idx, t->last_on_top);
5799 if (hr != S_OK) return hr;
5800 hr = triangulation_add_point(&t2, triangulations, sweep_idx, t2->last_on_top);
5801 if (hr != S_OK) return hr;
5802 break;
5803 } else {
5804 /* inside, so split polygon into two monotone parts */
5805 struct triangulation *t2 = add_triangulation(triangulations);
5806 if (!t2) return E_OUTOFMEMORY;
5807 MoveMemory(t + 1, t, (char*)(t2 + 1) - (char*)t);
5808 if (t->last_on_top) {
5809 t2 = t + 1;
5810 } else {
5811 t2 = t;
5812 t++;
5815 ZeroMemory(&t2->vertex_stack, sizeof(t2->vertex_stack));
5816 hr = add_vertex_index(&t2->vertex_stack, t->vertex_stack.items[t->vertex_stack.count - 1]);
5817 if (hr != S_OK) return hr;
5818 hr = add_vertex_index(&t2->vertex_stack, sweep_idx);
5819 if (hr != S_OK) return hr;
5820 t2->last_on_top = !t->last_on_top;
5822 hr = triangulation_add_point(&t, triangulations, sweep_idx, t->last_on_top);
5823 if (hr != S_OK) return hr;
5824 break;
5827 if (start >= end)
5829 struct triangulation *t;
5830 struct triangulation *t2 = add_triangulation(triangulations);
5831 if (!t2) return E_OUTOFMEMORY;
5832 t = &triangulations->items[start];
5833 MoveMemory(t + 1, t, (char*)(t2 + 1) - (char*)t);
5834 ZeroMemory(t, sizeof(*t));
5835 hr = add_vertex_index(&t->vertex_stack, sweep_idx);
5836 if (hr != S_OK) return hr;
5839 return S_OK;
5842 HRESULT WINAPI D3DXCreateTextW(struct IDirect3DDevice9 *device, HDC hdc, const WCHAR *text, float deviation,
5843 float extrusion, struct ID3DXMesh **mesh_ptr, struct ID3DXBuffer **adjacency, GLYPHMETRICSFLOAT *glyphmetrics)
5845 HRESULT hr;
5846 ID3DXMesh *mesh = NULL;
5847 DWORD nb_vertices, nb_faces;
5848 DWORD nb_front_faces, nb_corners, nb_outline_points;
5849 struct vertex *vertices = NULL;
5850 face *faces = NULL;
5851 int textlen = 0;
5852 float offset_x;
5853 LOGFONTW lf;
5854 OUTLINETEXTMETRICW otm;
5855 HFONT font = NULL, oldfont = NULL;
5856 const MAT2 identity = {{0, 1}, {0, 0}, {0, 0}, {0, 1}};
5857 void *raw_outline = NULL;
5858 int bufsize = 0;
5859 struct glyphinfo *glyphs = NULL;
5860 GLYPHMETRICS gm;
5861 struct triangulation_array triangulations = {0, 0, NULL};
5862 int i;
5863 struct vertex *vertex_ptr;
5864 face *face_ptr;
5865 float max_deviation_sq;
5866 const struct cos_table cos_table = {
5867 cosf(D3DXToRadian(0.5f)),
5868 cosf(D3DXToRadian(45.0f)),
5869 cosf(D3DXToRadian(90.0f)),
5871 int f1, f2;
5873 TRACE("(%p, %p, %s, %f, %f, %p, %p, %p)\n", device, hdc,
5874 debugstr_w(text), deviation, extrusion, mesh_ptr, adjacency, glyphmetrics);
5876 if (!device || !hdc || !text || !*text || deviation < 0.0f || extrusion < 0.0f || !mesh_ptr)
5877 return D3DERR_INVALIDCALL;
5879 if (adjacency)
5881 FIXME("Case of adjacency != NULL not implemented.\n");
5882 return E_NOTIMPL;
5885 if (!GetObjectW(GetCurrentObject(hdc, OBJ_FONT), sizeof(lf), &lf) ||
5886 !GetOutlineTextMetricsW(hdc, sizeof(otm), &otm))
5888 return D3DERR_INVALIDCALL;
5891 if (deviation == 0.0f)
5892 deviation = 1.0f / otm.otmEMSquare;
5893 max_deviation_sq = deviation * deviation;
5895 lf.lfHeight = otm.otmEMSquare;
5896 lf.lfWidth = 0;
5897 font = CreateFontIndirectW(&lf);
5898 if (!font) {
5899 hr = E_OUTOFMEMORY;
5900 goto error;
5902 oldfont = SelectObject(hdc, font);
5904 textlen = strlenW(text);
5905 for (i = 0; i < textlen; i++)
5907 int datasize = GetGlyphOutlineW(hdc, text[i], GGO_NATIVE, &gm, 0, NULL, &identity);
5908 if (datasize < 0)
5909 return D3DERR_INVALIDCALL;
5910 if (bufsize < datasize)
5911 bufsize = datasize;
5913 if (!bufsize) { /* e.g. text == " " */
5914 hr = D3DERR_INVALIDCALL;
5915 goto error;
5918 glyphs = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, textlen * sizeof(*glyphs));
5919 raw_outline = HeapAlloc(GetProcessHeap(), 0, bufsize);
5920 if (!glyphs || !raw_outline) {
5921 hr = E_OUTOFMEMORY;
5922 goto error;
5925 offset_x = 0.0f;
5926 for (i = 0; i < textlen; i++)
5928 /* get outline points from data returned from GetGlyphOutline */
5929 int datasize;
5931 glyphs[i].offset_x = offset_x;
5933 datasize = GetGlyphOutlineW(hdc, text[i], GGO_NATIVE, &gm, bufsize, raw_outline, &identity);
5934 hr = create_outline(&glyphs[i], raw_outline, datasize,
5935 max_deviation_sq, otm.otmEMSquare, &cos_table);
5936 if (hr != S_OK) goto error;
5938 triangulations.glyph = &glyphs[i];
5939 hr = triangulate(&triangulations);
5940 if (hr != S_OK) goto error;
5941 if (triangulations.count) {
5942 ERR("%d incomplete triangulations of glyph (%u).\n", triangulations.count, text[i]);
5943 triangulations.count = 0;
5946 if (glyphmetrics)
5948 glyphmetrics[i].gmfBlackBoxX = gm.gmBlackBoxX / (float)otm.otmEMSquare;
5949 glyphmetrics[i].gmfBlackBoxY = gm.gmBlackBoxY / (float)otm.otmEMSquare;
5950 glyphmetrics[i].gmfptGlyphOrigin.x = gm.gmptGlyphOrigin.x / (float)otm.otmEMSquare;
5951 glyphmetrics[i].gmfptGlyphOrigin.y = gm.gmptGlyphOrigin.y / (float)otm.otmEMSquare;
5952 glyphmetrics[i].gmfCellIncX = gm.gmCellIncX / (float)otm.otmEMSquare;
5953 glyphmetrics[i].gmfCellIncY = gm.gmCellIncY / (float)otm.otmEMSquare;
5955 offset_x += gm.gmCellIncX / (float)otm.otmEMSquare;
5958 /* corner points need an extra vertex for the different side faces normals */
5959 nb_corners = 0;
5960 nb_outline_points = 0;
5961 nb_front_faces = 0;
5962 for (i = 0; i < textlen; i++)
5964 int j;
5965 nb_outline_points += glyphs[i].ordered_vertices.count;
5966 nb_front_faces += glyphs[i].faces.count;
5967 for (j = 0; j < glyphs[i].outlines.count; j++)
5969 int k;
5970 struct outline *outline = &glyphs[i].outlines.items[j];
5971 nb_corners++; /* first outline point always repeated as a corner */
5972 for (k = 1; k < outline->count; k++)
5973 if (outline->items[k].corner)
5974 nb_corners++;
5978 nb_vertices = (nb_outline_points + nb_corners) * 2 + nb_outline_points * 2;
5979 nb_faces = nb_outline_points * 2 + nb_front_faces * 2;
5982 hr = D3DXCreateMeshFVF(nb_faces, nb_vertices, D3DXMESH_MANAGED,
5983 D3DFVF_XYZ | D3DFVF_NORMAL, device, &mesh);
5984 if (FAILED(hr))
5985 goto error;
5987 if (FAILED(hr = mesh->lpVtbl->LockVertexBuffer(mesh, 0, (void **)&vertices)))
5988 goto error;
5990 if (FAILED(hr = mesh->lpVtbl->LockIndexBuffer(mesh, 0, (void **)&faces)))
5991 goto error;
5993 /* convert 2D vertices and faces into 3D mesh */
5994 vertex_ptr = vertices;
5995 face_ptr = faces;
5996 if (extrusion == 0.0f) {
5997 f1 = 1;
5998 f2 = 2;
5999 } else {
6000 f1 = 2;
6001 f2 = 1;
6003 for (i = 0; i < textlen; i++)
6005 int j;
6006 int count;
6007 struct vertex *back_vertices;
6008 face *back_faces;
6010 /* side vertices and faces */
6011 for (j = 0; j < glyphs[i].outlines.count; j++)
6013 struct vertex *outline_vertices = vertex_ptr;
6014 struct outline *outline = &glyphs[i].outlines.items[j];
6015 int k;
6016 struct point2d *prevpt = &outline->items[outline->count - 1];
6017 struct point2d *pt = &outline->items[0];
6019 for (k = 1; k <= outline->count; k++)
6021 struct vertex vtx;
6022 struct point2d *nextpt = &outline->items[k % outline->count];
6023 WORD vtx_idx = vertex_ptr - vertices;
6024 D3DXVECTOR2 vec;
6026 if (pt->corner == POINTTYPE_CURVE_START)
6027 D3DXVec2Subtract(&vec, &pt->pos, &prevpt->pos);
6028 else if (pt->corner)
6029 D3DXVec2Subtract(&vec, &nextpt->pos, &pt->pos);
6030 else
6031 D3DXVec2Subtract(&vec, &nextpt->pos, &prevpt->pos);
6032 D3DXVec2Normalize(&vec, &vec);
6033 vtx.normal.x = -vec.y;
6034 vtx.normal.y = vec.x;
6035 vtx.normal.z = 0;
6037 vtx.position.x = pt->pos.x + glyphs[i].offset_x;
6038 vtx.position.y = pt->pos.y;
6039 vtx.position.z = 0;
6040 *vertex_ptr++ = vtx;
6042 vtx.position.z = -extrusion;
6043 *vertex_ptr++ = vtx;
6045 vtx.position.x = nextpt->pos.x + glyphs[i].offset_x;
6046 vtx.position.y = nextpt->pos.y;
6047 if (pt->corner && nextpt->corner && nextpt->corner != POINTTYPE_CURVE_END) {
6048 vtx.position.z = -extrusion;
6049 *vertex_ptr++ = vtx;
6050 vtx.position.z = 0;
6051 *vertex_ptr++ = vtx;
6053 (*face_ptr)[0] = vtx_idx;
6054 (*face_ptr)[1] = vtx_idx + 2;
6055 (*face_ptr)[2] = vtx_idx + 1;
6056 face_ptr++;
6058 (*face_ptr)[0] = vtx_idx;
6059 (*face_ptr)[1] = vtx_idx + 3;
6060 (*face_ptr)[2] = vtx_idx + 2;
6061 face_ptr++;
6062 } else {
6063 if (nextpt->corner) {
6064 if (nextpt->corner == POINTTYPE_CURVE_END) {
6065 D3DXVECTOR2 *nextpt2 = &outline->items[(k + 1) % outline->count].pos;
6066 D3DXVec2Subtract(&vec, nextpt2, &nextpt->pos);
6067 } else {
6068 D3DXVec2Subtract(&vec, &nextpt->pos, &pt->pos);
6070 D3DXVec2Normalize(&vec, &vec);
6071 vtx.normal.x = -vec.y;
6072 vtx.normal.y = vec.x;
6074 vtx.position.z = 0;
6075 *vertex_ptr++ = vtx;
6076 vtx.position.z = -extrusion;
6077 *vertex_ptr++ = vtx;
6080 (*face_ptr)[0] = vtx_idx;
6081 (*face_ptr)[1] = vtx_idx + 3;
6082 (*face_ptr)[2] = vtx_idx + 1;
6083 face_ptr++;
6085 (*face_ptr)[0] = vtx_idx;
6086 (*face_ptr)[1] = vtx_idx + 2;
6087 (*face_ptr)[2] = vtx_idx + 3;
6088 face_ptr++;
6091 prevpt = pt;
6092 pt = nextpt;
6094 if (!pt->corner) {
6095 *vertex_ptr++ = *outline_vertices++;
6096 *vertex_ptr++ = *outline_vertices++;
6100 /* back vertices and faces */
6101 back_faces = face_ptr;
6102 back_vertices = vertex_ptr;
6103 for (j = 0; j < glyphs[i].ordered_vertices.count; j++)
6105 D3DXVECTOR2 *pt = get_ordered_vertex(&glyphs[i], j);
6106 vertex_ptr->position.x = pt->x + glyphs[i].offset_x;
6107 vertex_ptr->position.y = pt->y;
6108 vertex_ptr->position.z = 0;
6109 vertex_ptr->normal.x = 0;
6110 vertex_ptr->normal.y = 0;
6111 vertex_ptr->normal.z = 1;
6112 vertex_ptr++;
6114 count = back_vertices - vertices;
6115 for (j = 0; j < glyphs[i].faces.count; j++)
6117 face *f = &glyphs[i].faces.items[j];
6118 (*face_ptr)[0] = (*f)[0] + count;
6119 (*face_ptr)[1] = (*f)[1] + count;
6120 (*face_ptr)[2] = (*f)[2] + count;
6121 face_ptr++;
6124 /* front vertices and faces */
6125 j = count = vertex_ptr - back_vertices;
6126 while (j--)
6128 vertex_ptr->position.x = back_vertices->position.x;
6129 vertex_ptr->position.y = back_vertices->position.y;
6130 vertex_ptr->position.z = -extrusion;
6131 vertex_ptr->normal.x = 0;
6132 vertex_ptr->normal.y = 0;
6133 vertex_ptr->normal.z = extrusion == 0.0f ? 1.0f : -1.0f;
6134 vertex_ptr++;
6135 back_vertices++;
6137 j = face_ptr - back_faces;
6138 while (j--)
6140 (*face_ptr)[0] = (*back_faces)[0] + count;
6141 (*face_ptr)[1] = (*back_faces)[f1] + count;
6142 (*face_ptr)[2] = (*back_faces)[f2] + count;
6143 face_ptr++;
6144 back_faces++;
6148 *mesh_ptr = mesh;
6149 hr = D3D_OK;
6150 error:
6151 if (mesh) {
6152 if (faces) mesh->lpVtbl->UnlockIndexBuffer(mesh);
6153 if (vertices) mesh->lpVtbl->UnlockVertexBuffer(mesh);
6154 if (hr != D3D_OK) mesh->lpVtbl->Release(mesh);
6156 if (glyphs) {
6157 for (i = 0; i < textlen; i++)
6159 int j;
6160 for (j = 0; j < glyphs[i].outlines.count; j++)
6161 HeapFree(GetProcessHeap(), 0, glyphs[i].outlines.items[j].items);
6162 HeapFree(GetProcessHeap(), 0, glyphs[i].outlines.items);
6163 HeapFree(GetProcessHeap(), 0, glyphs[i].faces.items);
6164 HeapFree(GetProcessHeap(), 0, glyphs[i].ordered_vertices.items);
6166 HeapFree(GetProcessHeap(), 0, glyphs);
6168 if (triangulations.items) {
6169 int i;
6170 for (i = 0; i < triangulations.count; i++)
6171 HeapFree(GetProcessHeap(), 0, triangulations.items[i].vertex_stack.items);
6172 HeapFree(GetProcessHeap(), 0, triangulations.items);
6174 HeapFree(GetProcessHeap(), 0, raw_outline);
6175 if (oldfont) SelectObject(hdc, oldfont);
6176 if (font) DeleteObject(font);
6178 return hr;
6181 HRESULT WINAPI D3DXValidMesh(ID3DXMesh *mesh, const DWORD *adjacency, ID3DXBuffer **errors_and_warnings)
6183 FIXME("(%p, %p, %p): stub\n", mesh, adjacency, *errors_and_warnings);
6185 return E_NOTIMPL;
6188 static BOOL weld_float1(void *to, void *from, FLOAT epsilon)
6190 FLOAT *v1 = to;
6191 FLOAT *v2 = from;
6193 if (fabsf(*v1 - *v2) <= epsilon)
6195 *v1 = *v2;
6197 return TRUE;
6200 return FALSE;
6203 static BOOL weld_float2(void *to, void *from, FLOAT epsilon)
6205 D3DXVECTOR2 *v1 = to;
6206 D3DXVECTOR2 *v2 = from;
6207 FLOAT diff_x = fabsf(v1->x - v2->x);
6208 FLOAT diff_y = fabsf(v1->y - v2->y);
6209 FLOAT max_abs_diff = max(diff_x, diff_y);
6211 if (max_abs_diff <= epsilon)
6213 memcpy(to, from, sizeof(D3DXVECTOR2));
6215 return TRUE;
6218 return FALSE;
6221 static BOOL weld_float3(void *to, void *from, FLOAT epsilon)
6223 D3DXVECTOR3 *v1 = to;
6224 D3DXVECTOR3 *v2 = from;
6225 FLOAT diff_x = fabsf(v1->x - v2->x);
6226 FLOAT diff_y = fabsf(v1->y - v2->y);
6227 FLOAT diff_z = fabsf(v1->z - v2->z);
6228 FLOAT max_abs_diff = max(diff_x, diff_y);
6229 max_abs_diff = max(diff_z, max_abs_diff);
6231 if (max_abs_diff <= epsilon)
6233 memcpy(to, from, sizeof(D3DXVECTOR3));
6235 return TRUE;
6238 return FALSE;
6241 static BOOL weld_float4(void *to, void *from, FLOAT epsilon)
6243 D3DXVECTOR4 *v1 = to;
6244 D3DXVECTOR4 *v2 = from;
6245 FLOAT diff_x = fabsf(v1->x - v2->x);
6246 FLOAT diff_y = fabsf(v1->y - v2->y);
6247 FLOAT diff_z = fabsf(v1->z - v2->z);
6248 FLOAT diff_w = fabsf(v1->w - v2->w);
6249 FLOAT max_abs_diff = max(diff_x, diff_y);
6250 max_abs_diff = max(diff_z, max_abs_diff);
6251 max_abs_diff = max(diff_w, max_abs_diff);
6253 if (max_abs_diff <= epsilon)
6255 memcpy(to, from, sizeof(D3DXVECTOR4));
6257 return TRUE;
6260 return FALSE;
6263 static BOOL weld_ubyte4(void *to, void *from, FLOAT epsilon)
6265 BYTE *b1 = to;
6266 BYTE *b2 = from;
6267 BYTE truncated_epsilon = (BYTE)epsilon;
6268 BYTE diff_x = b1[0] > b2[0] ? b1[0] - b2[0] : b2[0] - b1[0];
6269 BYTE diff_y = b1[1] > b2[1] ? b1[1] - b2[1] : b2[1] - b1[1];
6270 BYTE diff_z = b1[2] > b2[2] ? b1[2] - b2[2] : b2[2] - b1[2];
6271 BYTE diff_w = b1[3] > b2[3] ? b1[3] - b2[3] : b2[3] - b1[3];
6272 BYTE max_diff = max(diff_x, diff_y);
6273 max_diff = max(diff_z, max_diff);
6274 max_diff = max(diff_w, max_diff);
6276 if (max_diff <= truncated_epsilon)
6278 memcpy(to, from, 4 * sizeof(BYTE));
6280 return TRUE;
6283 return FALSE;
6286 static BOOL weld_ubyte4n(void *to, void *from, FLOAT epsilon)
6288 return weld_ubyte4(to, from, epsilon * UCHAR_MAX);
6291 static BOOL weld_d3dcolor(void *to, void *from, FLOAT epsilon)
6293 return weld_ubyte4n(to, from, epsilon);
6296 static BOOL weld_short2(void *to, void *from, FLOAT epsilon)
6298 SHORT *s1 = to;
6299 SHORT *s2 = from;
6300 SHORT truncated_epsilon = (SHORT)epsilon;
6301 SHORT diff_x = abs(s1[0] - s2[0]);
6302 SHORT diff_y = abs(s1[1] - s2[1]);
6303 SHORT max_abs_diff = max(diff_x, diff_y);
6305 if (max_abs_diff <= truncated_epsilon)
6307 memcpy(to, from, 2 * sizeof(SHORT));
6309 return TRUE;
6312 return FALSE;
6315 static BOOL weld_short2n(void *to, void *from, FLOAT epsilon)
6317 return weld_short2(to, from, epsilon * SHRT_MAX);
6320 static BOOL weld_short4(void *to, void *from, FLOAT epsilon)
6322 SHORT *s1 = to;
6323 SHORT *s2 = from;
6324 SHORT truncated_epsilon = (SHORT)epsilon;
6325 SHORT diff_x = abs(s1[0] - s2[0]);
6326 SHORT diff_y = abs(s1[1] - s2[1]);
6327 SHORT diff_z = abs(s1[2] - s2[2]);
6328 SHORT diff_w = abs(s1[3] - s2[3]);
6329 SHORT max_abs_diff = max(diff_x, diff_y);
6330 max_abs_diff = max(diff_z, max_abs_diff);
6331 max_abs_diff = max(diff_w, max_abs_diff);
6333 if (max_abs_diff <= truncated_epsilon)
6335 memcpy(to, from, 4 * sizeof(SHORT));
6337 return TRUE;
6340 return FALSE;
6343 static BOOL weld_short4n(void *to, void *from, FLOAT epsilon)
6345 return weld_short4(to, from, epsilon * SHRT_MAX);
6348 static BOOL weld_ushort2n(void *to, void *from, FLOAT epsilon)
6350 USHORT *s1 = to;
6351 USHORT *s2 = from;
6352 USHORT scaled_epsilon = (USHORT)(epsilon * USHRT_MAX);
6353 USHORT diff_x = s1[0] > s2[0] ? s1[0] - s2[0] : s2[0] - s1[0];
6354 USHORT diff_y = s1[1] > s2[1] ? s1[1] - s2[1] : s2[1] - s1[1];
6355 USHORT max_diff = max(diff_x, diff_y);
6357 if (max_diff <= scaled_epsilon)
6359 memcpy(to, from, 2 * sizeof(USHORT));
6361 return TRUE;
6364 return FALSE;
6367 static BOOL weld_ushort4n(void *to, void *from, FLOAT epsilon)
6369 USHORT *s1 = to;
6370 USHORT *s2 = from;
6371 USHORT scaled_epsilon = (USHORT)(epsilon * USHRT_MAX);
6372 USHORT diff_x = s1[0] > s2[0] ? s1[0] - s2[0] : s2[0] - s1[0];
6373 USHORT diff_y = s1[1] > s2[1] ? s1[1] - s2[1] : s2[1] - s1[1];
6374 USHORT diff_z = s1[2] > s2[2] ? s1[2] - s2[2] : s2[2] - s1[2];
6375 USHORT diff_w = s1[3] > s2[3] ? s1[3] - s2[3] : s2[3] - s1[3];
6376 USHORT max_diff = max(diff_x, diff_y);
6377 max_diff = max(diff_z, max_diff);
6378 max_diff = max(diff_w, max_diff);
6380 if (max_diff <= scaled_epsilon)
6382 memcpy(to, from, 4 * sizeof(USHORT));
6384 return TRUE;
6387 return FALSE;
6390 struct udec3
6392 UINT x;
6393 UINT y;
6394 UINT z;
6395 UINT w;
6398 static struct udec3 dword_to_udec3(DWORD d)
6400 struct udec3 v;
6402 v.x = d & 0x3ff;
6403 v.y = (d & 0xffc00) >> 10;
6404 v.z = (d & 0x3ff00000) >> 20;
6405 v.w = (d & 0xc0000000) >> 30;
6407 return v;
6410 static BOOL weld_udec3(void *to, void *from, FLOAT epsilon)
6412 DWORD *d1 = to;
6413 DWORD *d2 = from;
6414 struct udec3 v1 = dword_to_udec3(*d1);
6415 struct udec3 v2 = dword_to_udec3(*d2);
6416 UINT truncated_epsilon = (UINT)epsilon;
6417 UINT diff_x = v1.x > v2.x ? v1.x - v2.x : v2.x - v1.x;
6418 UINT diff_y = v1.y > v2.y ? v1.y - v2.y : v2.y - v1.y;
6419 UINT diff_z = v1.z > v2.z ? v1.z - v2.z : v2.z - v1.z;
6420 UINT diff_w = v1.w > v2.w ? v1.w - v2.w : v2.w - v1.w;
6421 UINT max_diff = max(diff_x, diff_y);
6422 max_diff = max(diff_z, max_diff);
6423 max_diff = max(diff_w, max_diff);
6425 if (max_diff <= truncated_epsilon)
6427 memcpy(to, from, sizeof(DWORD));
6429 return TRUE;
6432 return FALSE;
6435 struct dec3n
6437 INT x;
6438 INT y;
6439 INT z;
6440 INT w;
6443 static struct dec3n dword_to_dec3n(DWORD d)
6445 struct dec3n v;
6447 v.x = d & 0x3ff;
6448 v.y = (d & 0xffc00) >> 10;
6449 v.z = (d & 0x3ff00000) >> 20;
6450 v.w = (d & 0xc0000000) >> 30;
6452 return v;
6455 static BOOL weld_dec3n(void *to, void *from, FLOAT epsilon)
6457 const UINT MAX_DEC3N = 511;
6458 DWORD *d1 = to;
6459 DWORD *d2 = from;
6460 struct dec3n v1 = dword_to_dec3n(*d1);
6461 struct dec3n v2 = dword_to_dec3n(*d2);
6462 INT scaled_epsilon = (INT)(epsilon * MAX_DEC3N);
6463 INT diff_x = abs(v1.x - v2.x);
6464 INT diff_y = abs(v1.y - v2.y);
6465 INT diff_z = abs(v1.z - v2.z);
6466 INT diff_w = abs(v1.w - v2.w);
6467 INT max_abs_diff = max(diff_x, diff_y);
6468 max_abs_diff = max(diff_z, max_abs_diff);
6469 max_abs_diff = max(diff_w, max_abs_diff);
6471 if (max_abs_diff <= scaled_epsilon)
6473 memcpy(to, from, sizeof(DWORD));
6475 return TRUE;
6478 return FALSE;
6481 static BOOL weld_float16_2(void *to, void *from, FLOAT epsilon)
6483 D3DXFLOAT16 *v1_float16 = to;
6484 D3DXFLOAT16 *v2_float16 = from;
6485 FLOAT diff_x;
6486 FLOAT diff_y;
6487 FLOAT max_abs_diff;
6488 #define NUM_ELEM 2
6489 FLOAT v1[NUM_ELEM];
6490 FLOAT v2[NUM_ELEM];
6492 D3DXFloat16To32Array(v1, v1_float16, NUM_ELEM);
6493 D3DXFloat16To32Array(v2, v2_float16, NUM_ELEM);
6495 diff_x = fabsf(v1[0] - v2[0]);
6496 diff_y = fabsf(v1[1] - v2[1]);
6497 max_abs_diff = max(diff_x, diff_y);
6499 if (max_abs_diff <= epsilon)
6501 memcpy(to, from, NUM_ELEM * sizeof(D3DXFLOAT16));
6503 return TRUE;
6506 return FALSE;
6507 #undef NUM_ELEM
6510 static BOOL weld_float16_4(void *to, void *from, FLOAT epsilon)
6512 D3DXFLOAT16 *v1_float16 = to;
6513 D3DXFLOAT16 *v2_float16 = from;
6514 FLOAT diff_x;
6515 FLOAT diff_y;
6516 FLOAT diff_z;
6517 FLOAT diff_w;
6518 FLOAT max_abs_diff;
6519 #define NUM_ELEM 4
6520 FLOAT v1[NUM_ELEM];
6521 FLOAT v2[NUM_ELEM];
6523 D3DXFloat16To32Array(v1, v1_float16, NUM_ELEM);
6524 D3DXFloat16To32Array(v2, v2_float16, NUM_ELEM);
6526 diff_x = fabsf(v1[0] - v2[0]);
6527 diff_y = fabsf(v1[1] - v2[1]);
6528 diff_z = fabsf(v1[2] - v2[2]);
6529 diff_w = fabsf(v1[3] - v2[3]);
6530 max_abs_diff = max(diff_x, diff_y);
6531 max_abs_diff = max(diff_z, max_abs_diff);
6532 max_abs_diff = max(diff_w, max_abs_diff);
6534 if (max_abs_diff <= epsilon)
6536 memcpy(to, from, NUM_ELEM * sizeof(D3DXFLOAT16));
6538 return TRUE;
6541 return FALSE;
6542 #undef NUM_ELEM
6545 /* Sets the vertex components to the same value if they are within epsilon. */
6546 static BOOL weld_component(void *to, void *from, D3DDECLTYPE type, FLOAT epsilon)
6548 /* Quiet FIXMEs as this is in a loop with potentially thousand of iterations. */
6549 BOOL fixme_once_unused = FALSE;
6550 BOOL fixme_once_unknown = FALSE;
6552 switch (type)
6554 case D3DDECLTYPE_FLOAT1:
6555 return weld_float1(to, from, epsilon);
6557 case D3DDECLTYPE_FLOAT2:
6558 return weld_float2(to, from, epsilon);
6560 case D3DDECLTYPE_FLOAT3:
6561 return weld_float3(to, from, epsilon);
6563 case D3DDECLTYPE_FLOAT4:
6564 return weld_float4(to, from, epsilon);
6566 case D3DDECLTYPE_D3DCOLOR:
6567 return weld_d3dcolor(to, from, epsilon);
6569 case D3DDECLTYPE_UBYTE4:
6570 return weld_ubyte4(to, from, epsilon);
6572 case D3DDECLTYPE_SHORT2:
6573 return weld_short2(to, from, epsilon);
6575 case D3DDECLTYPE_SHORT4:
6576 return weld_short4(to, from, epsilon);
6578 case D3DDECLTYPE_UBYTE4N:
6579 return weld_ubyte4n(to, from, epsilon);
6581 case D3DDECLTYPE_SHORT2N:
6582 return weld_short2n(to, from, epsilon);
6584 case D3DDECLTYPE_SHORT4N:
6585 return weld_short4n(to, from, epsilon);
6587 case D3DDECLTYPE_USHORT2N:
6588 return weld_ushort2n(to, from, epsilon);
6590 case D3DDECLTYPE_USHORT4N:
6591 return weld_ushort4n(to, from, epsilon);
6593 case D3DDECLTYPE_UDEC3:
6594 return weld_udec3(to, from, epsilon);
6596 case D3DDECLTYPE_DEC3N:
6597 return weld_dec3n(to, from, epsilon);
6599 case D3DDECLTYPE_FLOAT16_2:
6600 return weld_float16_2(to, from, epsilon);
6602 case D3DDECLTYPE_FLOAT16_4:
6603 return weld_float16_4(to, from, epsilon);
6605 case D3DDECLTYPE_UNUSED:
6606 if (!fixme_once_unused++)
6607 FIXME("D3DDECLTYPE_UNUSED welding not implemented.\n");
6608 break;
6610 default:
6611 if (!fixme_once_unknown++)
6612 FIXME("Welding of unknown declaration type %d is not implemented.\n", type);
6613 break;
6616 return FALSE;
6619 static FLOAT get_component_epsilon(const D3DVERTEXELEMENT9 *decl_ptr, const D3DXWELDEPSILONS *epsilons)
6621 FLOAT epsilon = 0.0f;
6622 /* Quiet FIXMEs as this is in a loop with potentially thousand of iterations. */
6623 static BOOL fixme_once_blendindices = FALSE;
6624 static BOOL fixme_once_positiont = FALSE;
6625 static BOOL fixme_once_fog = FALSE;
6626 static BOOL fixme_once_depth = FALSE;
6627 static BOOL fixme_once_sample = FALSE;
6628 static BOOL fixme_once_unknown = FALSE;
6630 switch (decl_ptr->Usage)
6632 case D3DDECLUSAGE_POSITION:
6633 epsilon = epsilons->Position;
6634 break;
6635 case D3DDECLUSAGE_BLENDWEIGHT:
6636 epsilon = epsilons->BlendWeights;
6637 break;
6638 case D3DDECLUSAGE_NORMAL:
6639 epsilon = epsilons->Normals;
6640 break;
6641 case D3DDECLUSAGE_PSIZE:
6642 epsilon = epsilons->PSize;
6643 break;
6644 case D3DDECLUSAGE_TEXCOORD:
6646 BYTE usage_index = decl_ptr->UsageIndex;
6647 if (usage_index > 7)
6648 usage_index = 7;
6649 epsilon = epsilons->Texcoords[usage_index];
6650 break;
6652 case D3DDECLUSAGE_TANGENT:
6653 epsilon = epsilons->Tangent;
6654 break;
6655 case D3DDECLUSAGE_BINORMAL:
6656 epsilon = epsilons->Binormal;
6657 break;
6658 case D3DDECLUSAGE_TESSFACTOR:
6659 epsilon = epsilons->TessFactor;
6660 break;
6661 case D3DDECLUSAGE_COLOR:
6662 if (decl_ptr->UsageIndex == 0)
6663 epsilon = epsilons->Diffuse;
6664 else if (decl_ptr->UsageIndex == 1)
6665 epsilon = epsilons->Specular;
6666 else
6667 epsilon = 1e-6f;
6668 break;
6669 case D3DDECLUSAGE_BLENDINDICES:
6670 if (!fixme_once_blendindices++)
6671 FIXME("D3DDECLUSAGE_BLENDINDICES welding not implemented.\n");
6672 break;
6673 case D3DDECLUSAGE_POSITIONT:
6674 if (!fixme_once_positiont++)
6675 FIXME("D3DDECLUSAGE_POSITIONT welding not implemented.\n");
6676 break;
6677 case D3DDECLUSAGE_FOG:
6678 if (!fixme_once_fog++)
6679 FIXME("D3DDECLUSAGE_FOG welding not implemented.\n");
6680 break;
6681 case D3DDECLUSAGE_DEPTH:
6682 if (!fixme_once_depth++)
6683 FIXME("D3DDECLUSAGE_DEPTH welding not implemented.\n");
6684 break;
6685 case D3DDECLUSAGE_SAMPLE:
6686 if (!fixme_once_sample++)
6687 FIXME("D3DDECLUSAGE_SAMPLE welding not implemented.\n");
6688 break;
6689 default:
6690 if (!fixme_once_unknown++)
6691 FIXME("Unknown usage %x\n", decl_ptr->Usage);
6692 break;
6695 return epsilon;
6698 /* Helper function for reading a 32-bit index buffer. */
6699 static inline DWORD read_ib(void *index_buffer, BOOL indices_are_32bit,
6700 DWORD index)
6702 if (indices_are_32bit)
6704 DWORD *indices = index_buffer;
6705 return indices[index];
6707 else
6709 WORD *indices = index_buffer;
6710 return indices[index];
6714 /* Helper function for writing to a 32-bit index buffer. */
6715 static inline void write_ib(void *index_buffer, BOOL indices_are_32bit,
6716 DWORD index, DWORD value)
6718 if (indices_are_32bit)
6720 DWORD *indices = index_buffer;
6721 indices[index] = value;
6723 else
6725 WORD *indices = index_buffer;
6726 indices[index] = value;
6730 /*************************************************************************
6731 * D3DXWeldVertices (D3DX9_36.@)
6733 * Welds together similar vertices. The similarity between vert-
6734 * ices can be the position and other components such as
6735 * normal and color.
6737 * PARAMS
6738 * mesh [I] Mesh which vertices will be welded together.
6739 * flags [I] D3DXWELDEPSILONSFLAGS specifying how to weld.
6740 * epsilons [I] How similar a component needs to be for welding.
6741 * adjacency [I] Which faces are adjacent to other faces.
6742 * adjacency_out [O] Updated adjacency after welding.
6743 * face_remap_out [O] Which faces the old faces have been mapped to.
6744 * vertex_remap_out [O] Which vertices the old vertices have been mapped to.
6746 * RETURNS
6747 * Success: D3D_OK.
6748 * Failure: D3DERR_INVALIDCALL, E_OUTOFMEMORY.
6750 * BUGS
6751 * Attribute sorting not implemented.
6754 HRESULT WINAPI D3DXWeldVertices(ID3DXMesh *mesh, DWORD flags, const D3DXWELDEPSILONS *epsilons,
6755 const DWORD *adjacency, DWORD *adjacency_out, DWORD *face_remap_out, ID3DXBuffer **vertex_remap_out)
6757 DWORD *adjacency_generated = NULL;
6758 const DWORD *adjacency_ptr;
6759 DWORD *attributes = NULL;
6760 const FLOAT DEFAULT_EPSILON = 1.0e-6f;
6761 HRESULT hr;
6762 DWORD i;
6763 void *indices = NULL;
6764 BOOL indices_are_32bit = mesh->lpVtbl->GetOptions(mesh) & D3DXMESH_32BIT;
6765 DWORD optimize_flags;
6766 DWORD *point_reps = NULL;
6767 struct d3dx9_mesh *This = impl_from_ID3DXMesh(mesh);
6768 DWORD *vertex_face_map = NULL;
6769 ID3DXBuffer *vertex_remap = NULL;
6770 BYTE *vertices = NULL;
6772 TRACE("mesh %p, flags %#x, epsilons %p, adjacency %p, adjacency_out %p, face_remap_out %p, vertex_remap_out %p.\n",
6773 mesh, flags, epsilons, adjacency, adjacency_out, face_remap_out, vertex_remap_out);
6775 if (flags == 0)
6777 WARN("No flags is undefined. Using D3DXWELDEPSILONS_WELDPARTIALMATCHES instead.\n");
6778 flags = D3DXWELDEPSILONS_WELDPARTIALMATCHES;
6781 if (adjacency) /* Use supplied adjacency. */
6783 adjacency_ptr = adjacency;
6785 else /* Adjacency has to be generated. */
6787 adjacency_generated = HeapAlloc(GetProcessHeap(), 0, 3 * This->numfaces * sizeof(*adjacency_generated));
6788 if (!adjacency_generated)
6790 ERR("Couldn't allocate memory for adjacency_generated.\n");
6791 hr = E_OUTOFMEMORY;
6792 goto cleanup;
6794 hr = mesh->lpVtbl->GenerateAdjacency(mesh, DEFAULT_EPSILON, adjacency_generated);
6795 if (FAILED(hr))
6797 ERR("Couldn't generate adjacency.\n");
6798 goto cleanup;
6800 adjacency_ptr = adjacency_generated;
6803 /* Point representation says which vertices can be replaced. */
6804 point_reps = HeapAlloc(GetProcessHeap(), 0, This->numvertices * sizeof(*point_reps));
6805 if (!point_reps)
6807 hr = E_OUTOFMEMORY;
6808 ERR("Couldn't allocate memory for point_reps.\n");
6809 goto cleanup;
6811 hr = mesh->lpVtbl->ConvertAdjacencyToPointReps(mesh, adjacency_ptr, point_reps);
6812 if (FAILED(hr))
6814 ERR("ConvertAdjacencyToPointReps failed.\n");
6815 goto cleanup;
6818 hr = mesh->lpVtbl->LockIndexBuffer(mesh, 0, &indices);
6819 if (FAILED(hr))
6821 ERR("Couldn't lock index buffer.\n");
6822 goto cleanup;
6825 hr = mesh->lpVtbl->LockAttributeBuffer(mesh, 0, &attributes);
6826 if (FAILED(hr))
6828 ERR("Couldn't lock attribute buffer.\n");
6829 goto cleanup;
6831 vertex_face_map = HeapAlloc(GetProcessHeap(), 0, This->numvertices * sizeof(*vertex_face_map));
6832 if (!vertex_face_map)
6834 hr = E_OUTOFMEMORY;
6835 ERR("Couldn't allocate memory for vertex_face_map.\n");
6836 goto cleanup;
6838 /* Build vertex face map, so that a vertex's face can be looked up. */
6839 for (i = 0; i < This->numfaces; i++)
6841 DWORD j;
6842 for (j = 0; j < 3; j++)
6844 DWORD index = read_ib(indices, indices_are_32bit, 3*i + j);
6845 vertex_face_map[index] = i;
6849 if (flags & D3DXWELDEPSILONS_WELDPARTIALMATCHES)
6851 hr = mesh->lpVtbl->LockVertexBuffer(mesh, 0, (void**)&vertices);
6852 if (FAILED(hr))
6854 ERR("Couldn't lock vertex buffer.\n");
6855 goto cleanup;
6857 /* For each vertex that can be removed, compare its vertex components
6858 * with the vertex components from the vertex that can replace it. A
6859 * vertex is only fully replaced if all the components match and the
6860 * flag D3DXWELDEPSILONS_DONOTREMOVEVERTICES is not set, and they
6861 * belong to the same attribute group. Otherwise the vertex components
6862 * that are within epsilon are set to the same value.
6864 for (i = 0; i < 3 * This->numfaces; i++)
6866 D3DVERTEXELEMENT9 *decl_ptr;
6867 DWORD vertex_size = mesh->lpVtbl->GetNumBytesPerVertex(mesh);
6868 DWORD num_vertex_components;
6869 INT matches = 0;
6870 BOOL all_match;
6871 DWORD index = read_ib(indices, indices_are_32bit, i);
6873 for (decl_ptr = This->cached_declaration, num_vertex_components = 0; decl_ptr->Stream != 0xFF; decl_ptr++, num_vertex_components++)
6875 BYTE *to = &vertices[vertex_size*index + decl_ptr->Offset];
6876 BYTE *from = &vertices[vertex_size*point_reps[index] + decl_ptr->Offset];
6877 FLOAT epsilon = get_component_epsilon(decl_ptr, epsilons);
6879 /* Don't weld self */
6880 if (index == point_reps[index])
6882 matches++;
6883 continue;
6886 if (weld_component(to, from, decl_ptr->Type, epsilon))
6887 matches++;
6890 all_match = (num_vertex_components == matches);
6891 if (all_match && !(flags & D3DXWELDEPSILONS_DONOTREMOVEVERTICES))
6893 DWORD to_face = vertex_face_map[index];
6894 DWORD from_face = vertex_face_map[point_reps[index]];
6895 if(attributes[to_face] != attributes[from_face] && !(flags & D3DXWELDEPSILONS_DONOTSPLIT))
6896 continue;
6897 write_ib(indices, indices_are_32bit, i, point_reps[index]);
6900 mesh->lpVtbl->UnlockVertexBuffer(mesh);
6901 vertices = NULL;
6903 else if (flags & D3DXWELDEPSILONS_WELDALL)
6905 for (i = 0; i < 3 * This->numfaces; i++)
6907 DWORD index = read_ib(indices, indices_are_32bit, i);
6908 DWORD to_face = vertex_face_map[index];
6909 DWORD from_face = vertex_face_map[point_reps[index]];
6910 if(attributes[to_face] != attributes[from_face] && !(flags & D3DXWELDEPSILONS_DONOTSPLIT))
6911 continue;
6912 write_ib(indices, indices_are_32bit, i, point_reps[index]);
6915 mesh->lpVtbl->UnlockAttributeBuffer(mesh);
6916 attributes = NULL;
6917 mesh->lpVtbl->UnlockIndexBuffer(mesh);
6918 indices = NULL;
6920 /* Compact mesh using OptimizeInplace */
6921 optimize_flags = D3DXMESHOPT_COMPACT;
6922 hr = mesh->lpVtbl->OptimizeInplace(mesh, optimize_flags, adjacency_ptr, adjacency_out, face_remap_out, vertex_remap_out);
6923 if (FAILED(hr))
6925 ERR("Couldn't compact mesh.\n");
6926 goto cleanup;
6929 hr = D3D_OK;
6930 cleanup:
6931 HeapFree(GetProcessHeap(), 0, adjacency_generated);
6932 HeapFree(GetProcessHeap(), 0, point_reps);
6933 HeapFree(GetProcessHeap(), 0, vertex_face_map);
6934 if (attributes) mesh->lpVtbl->UnlockAttributeBuffer(mesh);
6935 if (indices) mesh->lpVtbl->UnlockIndexBuffer(mesh);
6936 if (vertex_remap) ID3DXBuffer_Release(vertex_remap);
6937 if (vertices) mesh->lpVtbl->UnlockVertexBuffer(mesh);
6939 return hr;
6942 /*************************************************************************
6943 * D3DXOptimizeFaces (D3DX9_36.@)
6945 * Re-orders the faces so the vertex cache is used optimally.
6947 * PARAMS
6948 * indices [I] Pointer to an index buffer belonging to a mesh.
6949 * num_faces [I] Number of faces in the mesh.
6950 * num_vertices [I] Number of vertices in the mesh.
6951 * indices_are_32bit [I] Specifies whether indices are 32- or 16-bit.
6952 * face_remap [I/O] The new order the faces should be drawn in.
6954 * RETURNS
6955 * Success: D3D_OK.
6956 * Failure: D3DERR_INVALIDCALL.
6958 * BUGS
6959 * The face re-ordering does not use the vertex cache optimally.
6962 HRESULT WINAPI D3DXOptimizeFaces(const void *indices, UINT num_faces,
6963 UINT num_vertices, BOOL indices_are_32bit, DWORD *face_remap)
6965 UINT i;
6966 UINT j = num_faces - 1;
6967 UINT limit_16_bit = 2 << 15; /* According to MSDN */
6968 HRESULT hr = D3D_OK;
6970 FIXME("indices %p, num_faces %u, num_vertices %u, indices_are_32bit %#x, face_remap %p semi-stub. "
6971 "Face order will not be optimal.\n",
6972 indices, num_faces, num_vertices, indices_are_32bit, face_remap);
6974 if (!indices_are_32bit && num_faces >= limit_16_bit)
6976 WARN("Number of faces must be less than %d when using 16-bit indices.\n",
6977 limit_16_bit);
6978 hr = D3DERR_INVALIDCALL;
6979 goto error;
6982 if (!face_remap)
6984 WARN("Face remap pointer is NULL.\n");
6985 hr = D3DERR_INVALIDCALL;
6986 goto error;
6989 /* The faces are drawn in reverse order for simple meshes. This ordering
6990 * is not optimal for complicated meshes, but will not break anything
6991 * either. The ordering should be changed to take advantage of the vertex
6992 * cache on the graphics card.
6994 * TODO Re-order to take advantage of vertex cache.
6996 for (i = 0; i < num_faces; i++)
6998 face_remap[i] = j--;
7001 return D3D_OK;
7003 error:
7004 return hr;