cmd: DIR command outputs free space for the path.
[wine.git] / dlls / d3dx9_36 / mesh.c
blobf033ac14cb7205d036dbb5ed81f2d9e7812d1890
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
28 #include <assert.h>
29 #include <float.h>
31 #include "d3dx9_private.h"
32 #undef MAKE_DDHRESULT
33 #include "dxfile.h"
34 #include "rmxfguid.h"
35 #include "rmxftmpl.h"
36 #include "wine/list.h"
38 WINE_DEFAULT_DEBUG_CHANNEL(d3dx);
40 struct d3dx9_mesh
42 ID3DXMesh ID3DXMesh_iface;
43 LONG ref;
45 DWORD numfaces;
46 DWORD numvertices;
47 DWORD options;
48 DWORD fvf;
49 IDirect3DDevice9 *device;
50 D3DVERTEXELEMENT9 cached_declaration[MAX_FVF_DECL_SIZE];
51 IDirect3DVertexDeclaration9 *vertex_declaration;
52 UINT vertex_declaration_size;
53 UINT num_elem;
54 IDirect3DVertexBuffer9 *vertex_buffer;
55 IDirect3DIndexBuffer9 *index_buffer;
56 DWORD *attrib_buffer;
57 LONG attrib_buffer_lock_count;
58 DWORD attrib_table_size;
59 D3DXATTRIBUTERANGE *attrib_table;
62 static const UINT d3dx_decltype_size[] =
64 /* D3DDECLTYPE_FLOAT1 */ sizeof(FLOAT),
65 /* D3DDECLTYPE_FLOAT2 */ sizeof(D3DXVECTOR2),
66 /* D3DDECLTYPE_FLOAT3 */ sizeof(D3DXVECTOR3),
67 /* D3DDECLTYPE_FLOAT4 */ sizeof(D3DXVECTOR4),
68 /* D3DDECLTYPE_D3DCOLOR */ sizeof(D3DCOLOR),
69 /* D3DDECLTYPE_UBYTE4 */ 4 * sizeof(BYTE),
70 /* D3DDECLTYPE_SHORT2 */ 2 * sizeof(SHORT),
71 /* D3DDECLTYPE_SHORT4 */ 4 * sizeof(SHORT),
72 /* D3DDECLTYPE_UBYTE4N */ 4 * sizeof(BYTE),
73 /* D3DDECLTYPE_SHORT2N */ 2 * sizeof(SHORT),
74 /* D3DDECLTYPE_SHORT4N */ 4 * sizeof(SHORT),
75 /* D3DDECLTYPE_USHORT2N */ 2 * sizeof(USHORT),
76 /* D3DDECLTYPE_USHORT4N */ 4 * sizeof(USHORT),
77 /* D3DDECLTYPE_UDEC3 */ 4, /* 3 * 10 bits + 2 padding */
78 /* D3DDECLTYPE_DEC3N */ 4,
79 /* D3DDECLTYPE_FLOAT16_2 */ 2 * sizeof(D3DXFLOAT16),
80 /* D3DDECLTYPE_FLOAT16_4 */ 4 * sizeof(D3DXFLOAT16),
83 static inline struct d3dx9_mesh *impl_from_ID3DXMesh(ID3DXMesh *iface)
85 return CONTAINING_RECORD(iface, struct d3dx9_mesh, ID3DXMesh_iface);
88 static HRESULT WINAPI d3dx9_mesh_QueryInterface(ID3DXMesh *iface, REFIID riid, void **out)
90 TRACE("iface %p, riid %s, out %p.\n", iface, debugstr_guid(riid), out);
92 if (IsEqualGUID(riid, &IID_IUnknown) ||
93 IsEqualGUID(riid, &IID_ID3DXBaseMesh) ||
94 IsEqualGUID(riid, &IID_ID3DXMesh))
96 iface->lpVtbl->AddRef(iface);
97 *out = iface;
98 return S_OK;
101 WARN("Interface %s not found.\n", debugstr_guid(riid));
103 return E_NOINTERFACE;
106 static ULONG WINAPI d3dx9_mesh_AddRef(ID3DXMesh *iface)
108 struct d3dx9_mesh *mesh = impl_from_ID3DXMesh(iface);
109 ULONG refcount = InterlockedIncrement(&mesh->ref);
111 TRACE("%p increasing refcount to %lu.\n", mesh, refcount);
113 return refcount;
116 static ULONG WINAPI d3dx9_mesh_Release(ID3DXMesh *iface)
118 struct d3dx9_mesh *mesh = impl_from_ID3DXMesh(iface);
119 ULONG refcount = InterlockedDecrement(&mesh->ref);
121 TRACE("%p decreasing refcount to %lu.\n", mesh, refcount);
123 if (!refcount)
125 IDirect3DIndexBuffer9_Release(mesh->index_buffer);
126 IDirect3DVertexBuffer9_Release(mesh->vertex_buffer);
127 if (mesh->vertex_declaration)
128 IDirect3DVertexDeclaration9_Release(mesh->vertex_declaration);
129 IDirect3DDevice9_Release(mesh->device);
130 HeapFree(GetProcessHeap(), 0, mesh->attrib_buffer);
131 HeapFree(GetProcessHeap(), 0, mesh->attrib_table);
132 HeapFree(GetProcessHeap(), 0, mesh);
135 return refcount;
138 static HRESULT WINAPI d3dx9_mesh_DrawSubset(ID3DXMesh *iface, DWORD attrib_id)
140 struct d3dx9_mesh *This = impl_from_ID3DXMesh(iface);
141 HRESULT hr;
142 DWORD face_start;
143 DWORD face_end = 0;
144 DWORD vertex_size;
146 TRACE("iface %p, attrib_id %lu.\n", iface, attrib_id);
148 if (!This->vertex_declaration)
150 WARN("Can't draw a mesh with an invalid vertex declaration.\n");
151 return E_FAIL;
154 vertex_size = iface->lpVtbl->GetNumBytesPerVertex(iface);
156 hr = IDirect3DDevice9_SetVertexDeclaration(This->device, This->vertex_declaration);
157 if (FAILED(hr)) return hr;
158 hr = IDirect3DDevice9_SetStreamSource(This->device, 0, This->vertex_buffer, 0, vertex_size);
159 if (FAILED(hr)) return hr;
160 hr = IDirect3DDevice9_SetIndices(This->device, This->index_buffer);
161 if (FAILED(hr)) return hr;
163 while (face_end < This->numfaces)
165 for (face_start = face_end; face_start < This->numfaces; face_start++)
167 if (This->attrib_buffer[face_start] == attrib_id)
168 break;
170 if (face_start >= This->numfaces)
171 break;
172 for (face_end = face_start + 1; face_end < This->numfaces; face_end++)
174 if (This->attrib_buffer[face_end] != attrib_id)
175 break;
178 hr = IDirect3DDevice9_DrawIndexedPrimitive(This->device, D3DPT_TRIANGLELIST,
179 0, 0, This->numvertices, face_start * 3, face_end - face_start);
180 if (FAILED(hr)) return hr;
183 return D3D_OK;
186 static DWORD WINAPI d3dx9_mesh_GetNumFaces(ID3DXMesh *iface)
188 struct d3dx9_mesh *mesh = impl_from_ID3DXMesh(iface);
190 TRACE("iface %p.\n", iface);
192 return mesh->numfaces;
195 static DWORD WINAPI d3dx9_mesh_GetNumVertices(ID3DXMesh *iface)
197 struct d3dx9_mesh *mesh = impl_from_ID3DXMesh(iface);
199 TRACE("iface %p.\n", iface);
201 return mesh->numvertices;
204 static DWORD WINAPI d3dx9_mesh_GetFVF(ID3DXMesh *iface)
206 struct d3dx9_mesh *mesh = impl_from_ID3DXMesh(iface);
208 TRACE("iface %p.\n", iface);
210 return mesh->fvf;
213 static void copy_declaration(D3DVERTEXELEMENT9 *dst, const D3DVERTEXELEMENT9 *src, UINT num_elem)
215 memcpy(dst, src, num_elem * sizeof(*src));
218 static HRESULT WINAPI d3dx9_mesh_GetDeclaration(ID3DXMesh *iface, D3DVERTEXELEMENT9 declaration[MAX_FVF_DECL_SIZE])
220 struct d3dx9_mesh *mesh = impl_from_ID3DXMesh(iface);
222 TRACE("iface %p, declaration %p.\n", iface, declaration);
224 if (!declaration)
225 return D3DERR_INVALIDCALL;
227 copy_declaration(declaration, mesh->cached_declaration, mesh->num_elem);
229 return D3D_OK;
232 static DWORD WINAPI d3dx9_mesh_GetNumBytesPerVertex(ID3DXMesh *iface)
234 struct d3dx9_mesh *mesh = impl_from_ID3DXMesh(iface);
236 TRACE("iface %p.\n", iface);
238 return mesh->vertex_declaration_size;
241 static DWORD WINAPI d3dx9_mesh_GetOptions(ID3DXMesh *iface)
243 struct d3dx9_mesh *mesh = impl_from_ID3DXMesh(iface);
245 TRACE("iface %p.\n", iface);
247 return mesh->options;
250 static HRESULT WINAPI d3dx9_mesh_GetDevice(struct ID3DXMesh *iface, struct IDirect3DDevice9 **device)
252 struct d3dx9_mesh *mesh = impl_from_ID3DXMesh(iface);
254 TRACE("iface %p, device %p.\n", iface, device);
256 if (!device)
257 return D3DERR_INVALIDCALL;
258 *device = mesh->device;
259 IDirect3DDevice9_AddRef(mesh->device);
261 return D3D_OK;
264 static HRESULT WINAPI d3dx9_mesh_CloneMeshFVF(struct ID3DXMesh *iface, DWORD options, DWORD fvf,
265 struct IDirect3DDevice9 *device, struct ID3DXMesh **clone_mesh)
267 HRESULT hr;
268 D3DVERTEXELEMENT9 declaration[MAX_FVF_DECL_SIZE];
270 TRACE("iface %p, options %#lx, fvf %#lx, device %p, clone_mesh %p.\n",
271 iface, options, fvf, device, clone_mesh);
273 if (FAILED(hr = D3DXDeclaratorFromFVF(fvf, declaration)))
274 return hr;
276 return iface->lpVtbl->CloneMesh(iface, options, declaration, device, clone_mesh);
279 static FLOAT scale_clamp_ubyten(FLOAT value)
281 value = value * UCHAR_MAX;
283 if (value < 0.0f)
285 return 0.0f;
287 else
289 if (value > UCHAR_MAX) /* Clamp at 255 */
290 return UCHAR_MAX;
291 else
292 return value;
296 static FLOAT scale_clamp_shortn(FLOAT value)
298 value = value * SHRT_MAX;
300 /* The tests show that the range is SHRT_MIN + 1 to SHRT_MAX. */
301 if (value <= SHRT_MIN)
303 return SHRT_MIN + 1;
305 else if (value > SHRT_MAX)
307 return SHRT_MAX;
309 else
311 return value;
315 static FLOAT scale_clamp_ushortn(FLOAT value)
317 value = value * USHRT_MAX;
319 if (value < 0.0f)
321 return 0.0f;
323 else
325 if (value > USHRT_MAX) /* Clamp at 65535 */
326 return USHRT_MAX;
327 else
328 return value;
332 static INT simple_round(FLOAT value)
334 int res = (INT)(value + 0.5f);
336 return res;
339 static void convert_float4(BYTE *dst, const D3DXVECTOR4 *src, D3DDECLTYPE type_dst)
341 BOOL fixme_once = FALSE;
343 switch (type_dst)
345 case D3DDECLTYPE_FLOAT1:
347 FLOAT *dst_ptr = (FLOAT*)dst;
348 *dst_ptr = src->x;
349 break;
351 case D3DDECLTYPE_FLOAT2:
353 D3DXVECTOR2 *dst_ptr = (D3DXVECTOR2*)dst;
354 dst_ptr->x = src->x;
355 dst_ptr->y = src->y;
356 break;
358 case D3DDECLTYPE_FLOAT3:
360 D3DXVECTOR3 *dst_ptr = (D3DXVECTOR3*)dst;
361 dst_ptr->x = src->x;
362 dst_ptr->y = src->y;
363 dst_ptr->z = src->z;
364 break;
366 case D3DDECLTYPE_FLOAT4:
368 D3DXVECTOR4 *dst_ptr = (D3DXVECTOR4*)dst;
369 dst_ptr->x = src->x;
370 dst_ptr->y = src->y;
371 dst_ptr->z = src->z;
372 dst_ptr->w = src->w;
373 break;
375 case D3DDECLTYPE_D3DCOLOR:
377 dst[0] = (BYTE)simple_round(scale_clamp_ubyten(src->z));
378 dst[1] = (BYTE)simple_round(scale_clamp_ubyten(src->y));
379 dst[2] = (BYTE)simple_round(scale_clamp_ubyten(src->x));
380 dst[3] = (BYTE)simple_round(scale_clamp_ubyten(src->w));
381 break;
383 case D3DDECLTYPE_UBYTE4:
385 dst[0] = src->x < 0.0f ? 0 : (BYTE)simple_round(src->x);
386 dst[1] = src->y < 0.0f ? 0 : (BYTE)simple_round(src->y);
387 dst[2] = src->z < 0.0f ? 0 : (BYTE)simple_round(src->z);
388 dst[3] = src->w < 0.0f ? 0 : (BYTE)simple_round(src->w);
389 break;
391 case D3DDECLTYPE_SHORT2:
393 SHORT *dst_ptr = (SHORT*)dst;
394 dst_ptr[0] = (SHORT)simple_round(src->x);
395 dst_ptr[1] = (SHORT)simple_round(src->y);
396 break;
398 case D3DDECLTYPE_SHORT4:
400 SHORT *dst_ptr = (SHORT*)dst;
401 dst_ptr[0] = (SHORT)simple_round(src->x);
402 dst_ptr[1] = (SHORT)simple_round(src->y);
403 dst_ptr[2] = (SHORT)simple_round(src->z);
404 dst_ptr[3] = (SHORT)simple_round(src->w);
405 break;
407 case D3DDECLTYPE_UBYTE4N:
409 dst[0] = (BYTE)simple_round(scale_clamp_ubyten(src->x));
410 dst[1] = (BYTE)simple_round(scale_clamp_ubyten(src->y));
411 dst[2] = (BYTE)simple_round(scale_clamp_ubyten(src->z));
412 dst[3] = (BYTE)simple_round(scale_clamp_ubyten(src->w));
413 break;
415 case D3DDECLTYPE_SHORT2N:
417 SHORT *dst_ptr = (SHORT*)dst;
418 dst_ptr[0] = (SHORT)simple_round(scale_clamp_shortn(src->x));
419 dst_ptr[1] = (SHORT)simple_round(scale_clamp_shortn(src->y));
420 break;
422 case D3DDECLTYPE_SHORT4N:
424 SHORT *dst_ptr = (SHORT*)dst;
425 dst_ptr[0] = (SHORT)simple_round(scale_clamp_shortn(src->x));
426 dst_ptr[1] = (SHORT)simple_round(scale_clamp_shortn(src->y));
427 dst_ptr[2] = (SHORT)simple_round(scale_clamp_shortn(src->z));
428 dst_ptr[3] = (SHORT)simple_round(scale_clamp_shortn(src->w));
429 break;
431 case D3DDECLTYPE_USHORT2N:
433 USHORT *dst_ptr = (USHORT*)dst;
434 dst_ptr[0] = (USHORT)simple_round(scale_clamp_ushortn(src->x));
435 dst_ptr[1] = (USHORT)simple_round(scale_clamp_ushortn(src->y));
436 break;
438 case D3DDECLTYPE_USHORT4N:
440 USHORT *dst_ptr = (USHORT*)dst;
441 dst_ptr[0] = (USHORT)simple_round(scale_clamp_ushortn(src->x));
442 dst_ptr[1] = (USHORT)simple_round(scale_clamp_ushortn(src->y));
443 dst_ptr[2] = (USHORT)simple_round(scale_clamp_ushortn(src->z));
444 dst_ptr[3] = (USHORT)simple_round(scale_clamp_ushortn(src->w));
445 break;
447 case D3DDECLTYPE_FLOAT16_2:
449 D3DXFloat32To16Array((D3DXFLOAT16*)dst, (FLOAT*)src, 2);
450 break;
452 case D3DDECLTYPE_FLOAT16_4:
454 D3DXFloat32To16Array((D3DXFLOAT16*)dst, (FLOAT*)src, 4);
455 break;
457 default:
458 if (!fixme_once++)
459 FIXME("Conversion from D3DDECLTYPE_FLOAT4 to %d not implemented.\n", type_dst);
460 break;
464 static void convert_component(BYTE *dst, BYTE *src, D3DDECLTYPE type_dst, D3DDECLTYPE type_src)
466 BOOL fixme_once = FALSE;
468 switch (type_src)
470 case D3DDECLTYPE_FLOAT1:
472 FLOAT *src_ptr = (FLOAT*)src;
473 D3DXVECTOR4 src_float4 = {*src_ptr, 0.0f, 0.0f, 1.0f};
474 convert_float4(dst, &src_float4, type_dst);
475 break;
477 case D3DDECLTYPE_FLOAT2:
479 D3DXVECTOR2 *src_ptr = (D3DXVECTOR2*)src;
480 D3DXVECTOR4 src_float4 = {src_ptr->x, src_ptr->y, 0.0f, 1.0f};
481 convert_float4(dst, &src_float4, type_dst);
482 break;
484 case D3DDECLTYPE_FLOAT3:
486 D3DXVECTOR3 *src_ptr = (D3DXVECTOR3*)src;
487 D3DXVECTOR4 src_float4 = {src_ptr->x, src_ptr->y, src_ptr->z, 1.0f};
488 convert_float4(dst, &src_float4, type_dst);
489 break;
491 case D3DDECLTYPE_FLOAT4:
493 D3DXVECTOR4 *src_ptr = (D3DXVECTOR4*)src;
494 D3DXVECTOR4 src_float4 = {src_ptr->x, src_ptr->y, src_ptr->z, src_ptr->w};
495 convert_float4(dst, &src_float4, type_dst);
496 break;
498 case D3DDECLTYPE_D3DCOLOR:
500 D3DXVECTOR4 src_float4 =
502 (FLOAT)src[2]/UCHAR_MAX,
503 (FLOAT)src[1]/UCHAR_MAX,
504 (FLOAT)src[0]/UCHAR_MAX,
505 (FLOAT)src[3]/UCHAR_MAX
507 convert_float4(dst, &src_float4, type_dst);
508 break;
510 case D3DDECLTYPE_UBYTE4:
512 D3DXVECTOR4 src_float4 = {src[0], src[1], src[2], src[3]};
513 convert_float4(dst, &src_float4, type_dst);
514 break;
516 case D3DDECLTYPE_SHORT2:
518 SHORT *src_ptr = (SHORT*)src;
519 D3DXVECTOR4 src_float4 = {src_ptr[0], src_ptr[1], 0.0f, 1.0f};
520 convert_float4(dst, &src_float4, type_dst);
521 break;
523 case D3DDECLTYPE_SHORT4:
525 SHORT *src_ptr = (SHORT*)src;
526 D3DXVECTOR4 src_float4 = {src_ptr[0], src_ptr[1], src_ptr[2], src_ptr[3]};
527 convert_float4(dst, &src_float4, type_dst);
528 break;
530 case D3DDECLTYPE_UBYTE4N:
532 D3DXVECTOR4 src_float4 =
534 (FLOAT)src[0]/UCHAR_MAX,
535 (FLOAT)src[1]/UCHAR_MAX,
536 (FLOAT)src[2]/UCHAR_MAX,
537 (FLOAT)src[3]/UCHAR_MAX
539 convert_float4(dst, &src_float4, type_dst);
540 break;
542 case D3DDECLTYPE_SHORT2N:
544 SHORT *src_ptr = (SHORT*)src;
545 D3DXVECTOR4 src_float4 = {(FLOAT)src_ptr[0]/SHRT_MAX, (FLOAT)src_ptr[1]/SHRT_MAX, 0.0f, 1.0f};
546 convert_float4(dst, &src_float4, type_dst);
547 break;
549 case D3DDECLTYPE_SHORT4N:
551 SHORT *src_ptr = (SHORT*)src;
552 D3DXVECTOR4 src_float4 =
554 (FLOAT)src_ptr[0]/SHRT_MAX,
555 (FLOAT)src_ptr[1]/SHRT_MAX,
556 (FLOAT)src_ptr[2]/SHRT_MAX,
557 (FLOAT)src_ptr[3]/SHRT_MAX
559 convert_float4(dst, &src_float4, type_dst);
560 break;
562 case D3DDECLTYPE_FLOAT16_2:
564 D3DXVECTOR4 src_float4 = {0.0f, 0.0f, 0.0f, 1.0f};
565 D3DXFloat16To32Array((FLOAT*)&src_float4, (D3DXFLOAT16*)src, 2);
566 convert_float4(dst, &src_float4, type_dst);
567 break;
569 case D3DDECLTYPE_FLOAT16_4:
571 D3DXVECTOR4 src_float4;
572 D3DXFloat16To32Array((FLOAT*)&src_float4, (D3DXFLOAT16*)src, 4);
573 convert_float4(dst, &src_float4, type_dst);
574 break;
576 default:
577 if (!fixme_once++)
578 FIXME("Conversion of D3DDECLTYPE %d to %d not implemented.\n", type_src, type_dst);
579 break;
583 static INT get_equivalent_declaration_index(D3DVERTEXELEMENT9 orig_declaration, D3DVERTEXELEMENT9 *declaration)
585 INT i;
587 for (i = 0; declaration[i].Stream != 0xff; i++)
589 if (orig_declaration.Usage == declaration[i].Usage
590 && orig_declaration.UsageIndex == declaration[i].UsageIndex)
592 return i;
596 return -1;
599 static HRESULT convert_vertex_buffer(ID3DXMesh *mesh_dst, ID3DXMesh *mesh_src)
601 HRESULT hr;
602 D3DVERTEXELEMENT9 orig_declaration[MAX_FVF_DECL_SIZE] = {D3DDECL_END()};
603 D3DVERTEXELEMENT9 declaration[MAX_FVF_DECL_SIZE] = {D3DDECL_END()};
604 BYTE *vb_dst = NULL;
605 BYTE *vb_src = NULL;
606 UINT i;
607 UINT num_vertices = mesh_src->lpVtbl->GetNumVertices(mesh_src);
608 UINT dst_vertex_size = mesh_dst->lpVtbl->GetNumBytesPerVertex(mesh_dst);
609 UINT src_vertex_size = mesh_src->lpVtbl->GetNumBytesPerVertex(mesh_src);
611 hr = mesh_src->lpVtbl->GetDeclaration(mesh_src, orig_declaration);
612 if (FAILED(hr)) return hr;
613 hr = mesh_dst->lpVtbl->GetDeclaration(mesh_dst, declaration);
614 if (FAILED(hr)) return hr;
616 hr = mesh_src->lpVtbl->LockVertexBuffer(mesh_src, D3DLOCK_READONLY, (void**)&vb_src);
617 if (FAILED(hr)) goto cleanup;
618 hr = mesh_dst->lpVtbl->LockVertexBuffer(mesh_dst, 0, (void**)&vb_dst);
619 if (FAILED(hr)) goto cleanup;
621 /* Clear all new fields by clearing the entire vertex buffer. */
622 memset(vb_dst, 0, num_vertices * dst_vertex_size);
624 for (i = 0; orig_declaration[i].Stream != 0xff; i++)
626 INT eq_idx = get_equivalent_declaration_index(orig_declaration[i], declaration);
628 if (eq_idx >= 0)
630 UINT j;
631 for (j = 0; j < num_vertices; j++)
633 UINT idx_dst = dst_vertex_size * j + declaration[eq_idx].Offset;
634 UINT idx_src = src_vertex_size * j + orig_declaration[i].Offset;
635 UINT type_size = d3dx_decltype_size[orig_declaration[i].Type];
637 if (orig_declaration[i].Type == declaration[eq_idx].Type)
638 memcpy(&vb_dst[idx_dst], &vb_src[idx_src], type_size);
639 else
640 convert_component(&vb_dst[idx_dst], &vb_src[idx_src], declaration[eq_idx].Type, orig_declaration[i].Type);
645 hr = D3D_OK;
646 cleanup:
647 if (vb_dst) mesh_dst->lpVtbl->UnlockVertexBuffer(mesh_dst);
648 if (vb_src) mesh_src->lpVtbl->UnlockVertexBuffer(mesh_src);
650 return hr;
653 static BOOL declaration_equals(const D3DVERTEXELEMENT9 *declaration1, const D3DVERTEXELEMENT9 *declaration2)
655 UINT size1 = 0, size2 = 0;
657 /* Find the size of each declaration */
658 while (declaration1[size1].Stream != 0xff) size1++;
659 while (declaration2[size2].Stream != 0xff) size2++;
661 /* If not same size then they are definitely not equal */
662 if (size1 != size2)
663 return FALSE;
665 /* Check that all components are the same */
666 if (memcmp(declaration1, declaration2, size1*sizeof(*declaration1)) == 0)
667 return TRUE;
669 return FALSE;
672 static HRESULT WINAPI d3dx9_mesh_CloneMesh(struct ID3DXMesh *iface, DWORD options,
673 const D3DVERTEXELEMENT9 *declaration, struct IDirect3DDevice9 *device, struct ID3DXMesh **clone_mesh_out)
675 struct d3dx9_mesh *This = impl_from_ID3DXMesh(iface);
676 struct d3dx9_mesh *cloned_this;
677 ID3DXMesh *clone_mesh;
678 D3DVERTEXELEMENT9 orig_declaration[MAX_FVF_DECL_SIZE] = { D3DDECL_END() };
679 void *data_in, *data_out;
680 DWORD vertex_size;
681 HRESULT hr;
682 BOOL same_declaration;
684 TRACE("iface %p, options %#lx, declaration %p, device %p, clone_mesh_out %p.\n",
685 iface, options, declaration, device, clone_mesh_out);
687 if (!clone_mesh_out)
688 return D3DERR_INVALIDCALL;
690 hr = iface->lpVtbl->GetDeclaration(iface, orig_declaration);
691 if (FAILED(hr)) return hr;
693 hr = D3DXCreateMesh(This->numfaces, This->numvertices, options & ~D3DXMESH_VB_SHARE,
694 declaration, device, &clone_mesh);
695 if (FAILED(hr)) return hr;
697 cloned_this = impl_from_ID3DXMesh(clone_mesh);
698 vertex_size = clone_mesh->lpVtbl->GetNumBytesPerVertex(clone_mesh);
699 same_declaration = declaration_equals(declaration, orig_declaration);
701 if (options & D3DXMESH_VB_SHARE) {
702 if (!same_declaration) {
703 hr = D3DERR_INVALIDCALL;
704 goto error;
706 IDirect3DVertexBuffer9_AddRef(This->vertex_buffer);
707 /* FIXME: refactor to avoid creating a new vertex buffer */
708 IDirect3DVertexBuffer9_Release(cloned_this->vertex_buffer);
709 cloned_this->vertex_buffer = This->vertex_buffer;
710 } else if (same_declaration) {
711 hr = iface->lpVtbl->LockVertexBuffer(iface, D3DLOCK_READONLY, &data_in);
712 if (FAILED(hr)) goto error;
713 hr = clone_mesh->lpVtbl->LockVertexBuffer(clone_mesh, 0, &data_out);
714 if (FAILED(hr)) {
715 iface->lpVtbl->UnlockVertexBuffer(iface);
716 goto error;
718 memcpy(data_out, data_in, This->numvertices * vertex_size);
719 clone_mesh->lpVtbl->UnlockVertexBuffer(clone_mesh);
720 iface->lpVtbl->UnlockVertexBuffer(iface);
721 } else {
722 hr = convert_vertex_buffer(clone_mesh, iface);
723 if (FAILED(hr)) goto error;
726 hr = iface->lpVtbl->LockIndexBuffer(iface, D3DLOCK_READONLY, &data_in);
727 if (FAILED(hr)) goto error;
728 hr = clone_mesh->lpVtbl->LockIndexBuffer(clone_mesh, 0, &data_out);
729 if (FAILED(hr)) {
730 iface->lpVtbl->UnlockIndexBuffer(iface);
731 goto error;
733 if ((options ^ This->options) & D3DXMESH_32BIT) {
734 DWORD i;
735 if (options & D3DXMESH_32BIT) {
736 for (i = 0; i < This->numfaces * 3; i++)
737 ((DWORD*)data_out)[i] = ((WORD*)data_in)[i];
738 } else {
739 for (i = 0; i < This->numfaces * 3; i++)
740 ((WORD*)data_out)[i] = ((DWORD*)data_in)[i];
742 } else {
743 memcpy(data_out, data_in, This->numfaces * 3 * (options & D3DXMESH_32BIT ? 4 : 2));
745 clone_mesh->lpVtbl->UnlockIndexBuffer(clone_mesh);
746 iface->lpVtbl->UnlockIndexBuffer(iface);
748 memcpy(cloned_this->attrib_buffer, This->attrib_buffer, This->numfaces * sizeof(*This->attrib_buffer));
750 if (This->attrib_table_size)
752 cloned_this->attrib_table_size = This->attrib_table_size;
753 cloned_this->attrib_table = HeapAlloc(GetProcessHeap(), 0, This->attrib_table_size * sizeof(*This->attrib_table));
754 if (!cloned_this->attrib_table) {
755 hr = E_OUTOFMEMORY;
756 goto error;
758 memcpy(cloned_this->attrib_table, This->attrib_table, This->attrib_table_size * sizeof(*This->attrib_table));
761 *clone_mesh_out = clone_mesh;
763 return D3D_OK;
764 error:
765 IUnknown_Release(clone_mesh);
766 return hr;
769 static HRESULT WINAPI d3dx9_mesh_GetVertexBuffer(struct ID3DXMesh *iface,
770 struct IDirect3DVertexBuffer9 **vertex_buffer)
772 struct d3dx9_mesh *mesh = impl_from_ID3DXMesh(iface);
774 TRACE("iface %p, vertex_buffer %p.\n", iface, vertex_buffer);
776 if (!vertex_buffer)
777 return D3DERR_INVALIDCALL;
778 *vertex_buffer = mesh->vertex_buffer;
779 IDirect3DVertexBuffer9_AddRef(mesh->vertex_buffer);
781 return D3D_OK;
784 static HRESULT WINAPI d3dx9_mesh_GetIndexBuffer(struct ID3DXMesh *iface,
785 struct IDirect3DIndexBuffer9 **index_buffer)
787 struct d3dx9_mesh *mesh = impl_from_ID3DXMesh(iface);
789 TRACE("iface %p, index_buffer %p.\n", iface, index_buffer);
791 if (!index_buffer)
792 return D3DERR_INVALIDCALL;
793 *index_buffer = mesh->index_buffer;
794 IDirect3DIndexBuffer9_AddRef(mesh->index_buffer);
796 return D3D_OK;
799 static HRESULT WINAPI d3dx9_mesh_LockVertexBuffer(ID3DXMesh *iface, DWORD flags, void **data)
801 struct d3dx9_mesh *mesh = impl_from_ID3DXMesh(iface);
803 TRACE("iface %p, flags %#lx, data %p.\n", iface, flags, data);
805 return IDirect3DVertexBuffer9_Lock(mesh->vertex_buffer, 0, 0, data, flags);
808 static HRESULT WINAPI d3dx9_mesh_UnlockVertexBuffer(ID3DXMesh *iface)
810 struct d3dx9_mesh *mesh = impl_from_ID3DXMesh(iface);
812 TRACE("iface %p.\n", iface);
814 return IDirect3DVertexBuffer9_Unlock(mesh->vertex_buffer);
817 static HRESULT WINAPI d3dx9_mesh_LockIndexBuffer(ID3DXMesh *iface, DWORD flags, void **data)
819 struct d3dx9_mesh *mesh = impl_from_ID3DXMesh(iface);
821 TRACE("iface %p, flags %#lx, data %p.\n", iface, flags, data);
823 return IDirect3DIndexBuffer9_Lock(mesh->index_buffer, 0, 0, data, flags);
826 static HRESULT WINAPI d3dx9_mesh_UnlockIndexBuffer(ID3DXMesh *iface)
828 struct d3dx9_mesh *mesh = impl_from_ID3DXMesh(iface);
830 TRACE("iface %p.\n", iface);
832 return IDirect3DIndexBuffer9_Unlock(mesh->index_buffer);
835 /* FIXME: This looks just wrong, we never check *attrib_table_size before
836 * copying the data. */
837 static HRESULT WINAPI d3dx9_mesh_GetAttributeTable(ID3DXMesh *iface,
838 D3DXATTRIBUTERANGE *attrib_table, DWORD *attrib_table_size)
840 struct d3dx9_mesh *mesh = impl_from_ID3DXMesh(iface);
842 TRACE("iface %p, attrib_table %p, attrib_table_size %p.\n",
843 iface, attrib_table, attrib_table_size);
845 if (attrib_table_size)
846 *attrib_table_size = mesh->attrib_table_size;
848 if (attrib_table)
849 memcpy(attrib_table, mesh->attrib_table, mesh->attrib_table_size * sizeof(*attrib_table));
851 return D3D_OK;
854 struct edge_face
856 struct list entry;
857 DWORD v2;
858 DWORD face;
861 struct edge_face_map
863 struct list *lists;
864 struct edge_face *entries;
867 /* Builds up a map of which face a new edge belongs to. That way the adjacency
868 * of another edge can be looked up. An edge has an adjacent face if there
869 * is an edge going in the opposite direction in the map. For example if the
870 * edge (v1, v2) belongs to face 4, and there is a mapping (v2, v1)->7, then
871 * face 4 and 7 are adjacent.
873 * Each edge might have been replaced with another edge, or none at all. There
874 * is at most one edge to face mapping, i.e. an edge can only belong to one
875 * face.
877 static HRESULT init_edge_face_map(struct edge_face_map *edge_face_map, const DWORD *index_buffer,
878 const DWORD *point_reps, DWORD num_faces)
880 DWORD face, edge;
881 DWORD i;
883 edge_face_map->lists = HeapAlloc(GetProcessHeap(), 0, 3 * num_faces * sizeof(*edge_face_map->lists));
884 if (!edge_face_map->lists) return E_OUTOFMEMORY;
886 edge_face_map->entries = HeapAlloc(GetProcessHeap(), 0, 3 * num_faces * sizeof(*edge_face_map->entries));
887 if (!edge_face_map->entries) return E_OUTOFMEMORY;
890 /* Initialize all lists */
891 for (i = 0; i < 3 * num_faces; i++)
893 list_init(&edge_face_map->lists[i]);
895 /* Build edge face mapping */
896 for (face = 0; face < num_faces; face++)
898 for (edge = 0; edge < 3; edge++)
900 DWORD v1 = index_buffer[3*face + edge];
901 DWORD v2 = index_buffer[3*face + (edge+1)%3];
902 DWORD new_v1 = point_reps[v1]; /* What v1 has been replaced with */
903 DWORD new_v2 = point_reps[v2];
905 if (v1 != v2) /* Only map non-collapsed edges */
907 i = 3*face + edge;
908 edge_face_map->entries[i].v2 = new_v2;
909 edge_face_map->entries[i].face = face;
910 list_add_head(&edge_face_map->lists[new_v1], &edge_face_map->entries[i].entry);
915 return D3D_OK;
918 static DWORD find_adjacent_face(struct edge_face_map *edge_face_map, DWORD vertex1, DWORD vertex2, DWORD num_faces)
920 struct edge_face *edge_face_ptr;
922 LIST_FOR_EACH_ENTRY(edge_face_ptr, &edge_face_map->lists[vertex2], struct edge_face, entry)
924 if (edge_face_ptr->v2 == vertex1)
925 return edge_face_ptr->face;
928 return -1;
931 static DWORD *generate_identity_point_reps(DWORD num_vertices)
933 DWORD *id_point_reps;
934 DWORD i;
936 id_point_reps = HeapAlloc(GetProcessHeap(), 0, num_vertices * sizeof(*id_point_reps));
937 if (!id_point_reps)
938 return NULL;
940 for (i = 0; i < num_vertices; i++)
942 id_point_reps[i] = i;
945 return id_point_reps;
948 static HRESULT WINAPI d3dx9_mesh_ConvertPointRepsToAdjacency(ID3DXMesh *iface,
949 const DWORD *point_reps, DWORD *adjacency)
951 HRESULT hr;
952 DWORD num_faces = iface->lpVtbl->GetNumFaces(iface);
953 DWORD num_vertices = iface->lpVtbl->GetNumVertices(iface);
954 DWORD options = iface->lpVtbl->GetOptions(iface);
955 BOOL indices_are_16_bit = !(options & D3DXMESH_32BIT);
956 DWORD *ib = NULL;
957 void *ib_ptr = NULL;
958 DWORD face;
959 DWORD edge;
960 struct edge_face_map edge_face_map = {0};
961 const DWORD *point_reps_ptr = NULL;
962 DWORD *id_point_reps = NULL;
964 TRACE("iface %p, point_reps %p, adjacency %p.\n", iface, point_reps, adjacency);
966 if (!adjacency) return D3DERR_INVALIDCALL;
968 if (!point_reps) /* Identity point reps */
970 id_point_reps = generate_identity_point_reps(num_vertices);
971 if (!id_point_reps)
973 hr = E_OUTOFMEMORY;
974 goto cleanup;
977 point_reps_ptr = id_point_reps;
979 else
981 point_reps_ptr = point_reps;
984 hr = iface->lpVtbl->LockIndexBuffer(iface, D3DLOCK_READONLY, &ib_ptr);
985 if (FAILED(hr)) goto cleanup;
987 if (indices_are_16_bit)
989 /* Widen 16 bit to 32 bit */
990 DWORD i;
991 WORD *ib_16bit = ib_ptr;
992 ib = HeapAlloc(GetProcessHeap(), 0, 3 * num_faces * sizeof(DWORD));
993 if (!ib)
995 hr = E_OUTOFMEMORY;
996 goto cleanup;
998 for (i = 0; i < 3 * num_faces; i++)
1000 ib[i] = ib_16bit[i];
1003 else
1005 ib = ib_ptr;
1008 hr = init_edge_face_map(&edge_face_map, ib, point_reps_ptr, num_faces);
1009 if (FAILED(hr)) goto cleanup;
1011 /* Create adjacency */
1012 for (face = 0; face < num_faces; face++)
1014 for (edge = 0; edge < 3; edge++)
1016 DWORD v1 = ib[3*face + edge];
1017 DWORD v2 = ib[3*face + (edge+1)%3];
1018 DWORD new_v1 = point_reps_ptr[v1];
1019 DWORD new_v2 = point_reps_ptr[v2];
1020 DWORD adj_face;
1022 adj_face = find_adjacent_face(&edge_face_map, new_v1, new_v2, num_faces);
1023 adjacency[3*face + edge] = adj_face;
1027 hr = D3D_OK;
1028 cleanup:
1029 HeapFree(GetProcessHeap(), 0, id_point_reps);
1030 if (indices_are_16_bit) HeapFree(GetProcessHeap(), 0, ib);
1031 HeapFree(GetProcessHeap(), 0, edge_face_map.lists);
1032 HeapFree(GetProcessHeap(), 0, edge_face_map.entries);
1033 if(ib_ptr) iface->lpVtbl->UnlockIndexBuffer(iface);
1034 return hr;
1037 /* ConvertAdjacencyToPointReps helper function.
1039 * Goes around the edges of each face and replaces the vertices in any adjacent
1040 * face's edge with its own vertices(if its vertices have a lower index). This
1041 * way as few as possible low index vertices are shared among the faces. The
1042 * re-ordered index buffer is stored in new_indices.
1044 * The vertices in a point representation must be ordered sequentially, e.g.
1045 * index 5 holds the index of the vertex that replaces vertex 5, i.e. if
1046 * vertex 5 is replaced by vertex 3 then index 5 would contain 3. If no vertex
1047 * replaces it, then it contains the same number as the index itself, e.g.
1048 * index 5 would contain 5. */
1049 static HRESULT propagate_face_vertices(const DWORD *adjacency, DWORD *point_reps,
1050 const uint32_t *indices, uint32_t *new_indices, unsigned int face_idx, unsigned int face_count)
1052 unsigned int face_base = 3 * face_idx;
1053 unsigned int edge, opp_edge;
1055 for (edge = 0; edge < 3; ++edge)
1057 unsigned int adj_face = adjacency[face_base + edge];
1058 unsigned int adj_face_base;
1059 unsigned int i;
1061 if (adj_face == ~0u) /* No adjacent face. */
1062 continue;
1063 else if (adj_face >= face_count)
1065 /* This crashes on Windows. */
1066 WARN("Index out of bounds. Got %u, expected less than %u.\n", adj_face, face_count);
1067 return D3DERR_INVALIDCALL;
1069 adj_face_base = 3 * adj_face;
1071 /* Find opposite edge in adjacent face. */
1072 for (opp_edge = 0; opp_edge < 3; ++opp_edge)
1074 unsigned int opp_edge_index = adj_face_base + opp_edge;
1076 if (adjacency[opp_edge_index] == face_idx)
1077 break; /* Found opposite edge. */
1080 /* Replaces vertices in opposite edge with vertices from current edge. */
1081 for (i = 0; i < 2; ++i)
1083 unsigned int from = face_base + (edge + (1 - i)) % 3;
1084 unsigned int to = adj_face_base + (opp_edge + i) % 3;
1086 /* Propagate lowest index. */
1087 if (new_indices[to] > new_indices[from])
1089 new_indices[to] = new_indices[from];
1090 point_reps[indices[to]] = new_indices[from];
1095 return D3D_OK;
1098 static HRESULT WINAPI d3dx9_mesh_ConvertAdjacencyToPointReps(ID3DXMesh *iface,
1099 const DWORD *adjacency, DWORD *point_reps)
1101 struct d3dx9_mesh *mesh = impl_from_ID3DXMesh(iface);
1102 uint16_t *indices_16bit = NULL;
1103 uint32_t *new_indices = NULL;
1104 uint32_t *indices = NULL;
1105 unsigned int face, i;
1106 HRESULT hr;
1108 TRACE("iface %p, adjacency %p, point_reps %p.\n", iface, adjacency, point_reps);
1110 if (!adjacency)
1112 WARN("NULL adjacency.\n");
1113 return D3DERR_INVALIDCALL;
1116 if (!point_reps)
1118 WARN("NULL point_reps.\n");
1119 return D3DERR_INVALIDCALL;
1122 /* Should never happen as CreateMesh does not allow meshes with 0 faces. */
1123 if (!mesh->numfaces)
1125 ERR("Number of faces was zero.\n");
1126 return D3DERR_INVALIDCALL;
1129 if (!(new_indices = HeapAlloc(GetProcessHeap(), 0, 3 * mesh->numfaces * sizeof(*indices))))
1130 return E_OUTOFMEMORY;
1132 if (mesh->options & D3DXMESH_32BIT)
1134 if (FAILED(hr = iface->lpVtbl->LockIndexBuffer(iface, D3DLOCK_READONLY, (void **)&indices)))
1135 goto cleanup;
1136 memcpy(new_indices, indices, 3 * mesh->numfaces * sizeof(*indices));
1138 else
1140 /* Make a widening copy of indices_16bit into indices and new_indices
1141 * in order to re-use the helper function. */
1142 if (FAILED(hr = iface->lpVtbl->LockIndexBuffer(iface, D3DLOCK_READONLY, (void **)&indices_16bit)))
1143 goto cleanup;
1145 if (!(indices = HeapAlloc(GetProcessHeap(), 0, 3 * mesh->numfaces * sizeof(*indices))))
1147 hr = E_OUTOFMEMORY;
1148 goto cleanup;
1150 for (i = 0; i < 3 * mesh->numfaces; ++i)
1152 new_indices[i] = indices_16bit[i];
1153 indices[i] = indices_16bit[i];
1157 /* Vertices are ordered sequentially in the point representation. */
1158 for (i = 0; i < mesh->numvertices; ++i)
1159 point_reps[i] = i;
1161 /* Propagate vertices with low indices so as few vertices as possible are
1162 * used in the mesh. */
1163 for (face = 0; face < mesh->numfaces; ++face)
1165 if (FAILED(hr = propagate_face_vertices(adjacency, point_reps, indices, new_indices, face, mesh->numfaces)))
1166 goto cleanup;
1168 /* Go in opposite direction to catch all face orderings. */
1169 for (face = 0; face < mesh->numfaces; ++face)
1171 if (FAILED(hr = propagate_face_vertices(adjacency, point_reps, indices, new_indices,
1172 (mesh->numfaces - 1) - face, mesh->numfaces)))
1173 goto cleanup;
1176 hr = D3D_OK;
1178 cleanup:
1179 if (mesh->options & D3DXMESH_32BIT)
1181 if (indices)
1182 iface->lpVtbl->UnlockIndexBuffer(iface);
1184 else
1186 if (indices_16bit)
1187 iface->lpVtbl->UnlockIndexBuffer(iface);
1188 HeapFree(GetProcessHeap(), 0, indices);
1190 HeapFree(GetProcessHeap(), 0, new_indices);
1191 return hr;
1194 struct vertex_metadata {
1195 float key;
1196 DWORD vertex_index;
1197 DWORD first_shared_index;
1200 static int __cdecl compare_vertex_keys(const void *a, const void *b)
1202 const struct vertex_metadata *left = a;
1203 const struct vertex_metadata *right = b;
1204 if (left->key == right->key)
1205 return 0;
1206 return left->key < right->key ? -1 : 1;
1209 static HRESULT WINAPI d3dx9_mesh_GenerateAdjacency(ID3DXMesh *iface, float epsilon, DWORD *adjacency)
1211 struct d3dx9_mesh *This = impl_from_ID3DXMesh(iface);
1212 HRESULT hr;
1213 BYTE *vertices = NULL;
1214 const DWORD *indices = NULL;
1215 DWORD vertex_size;
1216 DWORD buffer_size;
1217 /* sort the vertices by (x + y + z) to quickly find coincident vertices */
1218 struct vertex_metadata *sorted_vertices;
1219 /* shared_indices links together identical indices in the index buffer so
1220 * that adjacency checks can be limited to faces sharing a vertex */
1221 DWORD *shared_indices = NULL;
1222 const FLOAT epsilon_sq = epsilon * epsilon;
1223 DWORD i;
1225 TRACE("iface %p, epsilon %.8e, adjacency %p.\n", iface, epsilon, adjacency);
1227 if (!adjacency)
1228 return D3DERR_INVALIDCALL;
1230 buffer_size = This->numfaces * 3 * sizeof(*shared_indices) + This->numvertices * sizeof(*sorted_vertices);
1231 if (!(This->options & D3DXMESH_32BIT))
1232 buffer_size += This->numfaces * 3 * sizeof(*indices);
1233 shared_indices = HeapAlloc(GetProcessHeap(), 0, buffer_size);
1234 if (!shared_indices)
1235 return E_OUTOFMEMORY;
1236 sorted_vertices = (struct vertex_metadata*)(shared_indices + This->numfaces * 3);
1238 hr = iface->lpVtbl->LockVertexBuffer(iface, D3DLOCK_READONLY, (void**)&vertices);
1239 if (FAILED(hr)) goto cleanup;
1240 hr = iface->lpVtbl->LockIndexBuffer(iface, D3DLOCK_READONLY, (void**)&indices);
1241 if (FAILED(hr)) goto cleanup;
1243 if (!(This->options & D3DXMESH_32BIT)) {
1244 const WORD *word_indices = (const WORD*)indices;
1245 DWORD *dword_indices = (DWORD*)(sorted_vertices + This->numvertices);
1246 indices = dword_indices;
1247 for (i = 0; i < This->numfaces * 3; i++)
1248 *dword_indices++ = *word_indices++;
1251 vertex_size = iface->lpVtbl->GetNumBytesPerVertex(iface);
1252 for (i = 0; i < This->numvertices; i++) {
1253 D3DXVECTOR3 *vertex = (D3DXVECTOR3*)(vertices + vertex_size * i);
1254 sorted_vertices[i].first_shared_index = -1;
1255 sorted_vertices[i].key = vertex->x + vertex->y + vertex->z;
1256 sorted_vertices[i].vertex_index = i;
1258 for (i = 0; i < This->numfaces * 3; i++) {
1259 DWORD *first_shared_index = &sorted_vertices[indices[i]].first_shared_index;
1260 shared_indices[i] = *first_shared_index;
1261 *first_shared_index = i;
1262 adjacency[i] = -1;
1264 qsort(sorted_vertices, This->numvertices, sizeof(*sorted_vertices), compare_vertex_keys);
1266 for (i = 0; i < This->numvertices; i++) {
1267 struct vertex_metadata *sorted_vertex_a = &sorted_vertices[i];
1268 D3DXVECTOR3 *vertex_a = (D3DXVECTOR3*)(vertices + sorted_vertex_a->vertex_index * vertex_size);
1269 DWORD shared_index_a = sorted_vertex_a->first_shared_index;
1271 while (shared_index_a != -1) {
1272 DWORD j = i;
1273 DWORD shared_index_b = shared_indices[shared_index_a];
1274 struct vertex_metadata *sorted_vertex_b = sorted_vertex_a;
1276 while (TRUE) {
1277 while (shared_index_b != -1) {
1278 /* faces are adjacent if they have another coincident vertex */
1279 DWORD base_a = (shared_index_a / 3) * 3;
1280 DWORD base_b = (shared_index_b / 3) * 3;
1281 BOOL adjacent = FALSE;
1282 int k;
1284 for (k = 0; k < 3; k++) {
1285 if (adjacency[base_b + k] == shared_index_a / 3) {
1286 adjacent = TRUE;
1287 break;
1290 if (!adjacent) {
1291 for (k = 1; k <= 2; k++) {
1292 DWORD vertex_index_a = base_a + (shared_index_a + k) % 3;
1293 DWORD vertex_index_b = base_b + (shared_index_b + (3 - k)) % 3;
1294 adjacent = indices[vertex_index_a] == indices[vertex_index_b];
1295 if (!adjacent && epsilon >= 0.0f) {
1296 D3DXVECTOR3 delta = {0.0f, 0.0f, 0.0f};
1297 FLOAT length_sq;
1299 D3DXVec3Subtract(&delta,
1300 (D3DXVECTOR3*)(vertices + indices[vertex_index_a] * vertex_size),
1301 (D3DXVECTOR3*)(vertices + indices[vertex_index_b] * vertex_size));
1302 length_sq = D3DXVec3LengthSq(&delta);
1303 adjacent = epsilon == 0.0f ? length_sq == 0.0f : length_sq < epsilon_sq;
1305 if (adjacent) {
1306 DWORD adj_a = base_a + 2 - (vertex_index_a + shared_index_a + 1) % 3;
1307 DWORD adj_b = base_b + 2 - (vertex_index_b + shared_index_b + 1) % 3;
1308 if (adjacency[adj_a] == -1 && adjacency[adj_b] == -1) {
1309 adjacency[adj_a] = base_b / 3;
1310 adjacency[adj_b] = base_a / 3;
1311 break;
1317 shared_index_b = shared_indices[shared_index_b];
1319 while (++j < This->numvertices) {
1320 D3DXVECTOR3 *vertex_b;
1322 sorted_vertex_b++;
1323 if (sorted_vertex_b->key - sorted_vertex_a->key > epsilon * 3.0f) {
1324 /* no more coincident vertices to try */
1325 j = This->numvertices;
1326 break;
1328 /* check for coincidence */
1329 vertex_b = (D3DXVECTOR3*)(vertices + sorted_vertex_b->vertex_index * vertex_size);
1330 if (fabsf(vertex_a->x - vertex_b->x) <= epsilon &&
1331 fabsf(vertex_a->y - vertex_b->y) <= epsilon &&
1332 fabsf(vertex_a->z - vertex_b->z) <= epsilon)
1334 break;
1337 if (j >= This->numvertices)
1338 break;
1339 shared_index_b = sorted_vertex_b->first_shared_index;
1342 sorted_vertex_a->first_shared_index = shared_indices[sorted_vertex_a->first_shared_index];
1343 shared_index_a = sorted_vertex_a->first_shared_index;
1347 hr = D3D_OK;
1348 cleanup:
1349 if (indices) iface->lpVtbl->UnlockIndexBuffer(iface);
1350 if (vertices) iface->lpVtbl->UnlockVertexBuffer(iface);
1351 HeapFree(GetProcessHeap(), 0, shared_indices);
1352 return hr;
1355 static HRESULT WINAPI d3dx9_mesh_UpdateSemantics(ID3DXMesh *iface, D3DVERTEXELEMENT9 declaration[MAX_FVF_DECL_SIZE])
1357 struct d3dx9_mesh *This = impl_from_ID3DXMesh(iface);
1358 HRESULT hr;
1359 UINT vertex_declaration_size;
1360 int i;
1362 TRACE("iface %p, declaration %p.\n", iface, declaration);
1364 if (!declaration)
1366 WARN("Invalid declaration. Can't use NULL declaration.\n");
1367 return D3DERR_INVALIDCALL;
1370 /* New declaration must be same size as original */
1371 vertex_declaration_size = D3DXGetDeclVertexSize(declaration, declaration[0].Stream);
1372 if (vertex_declaration_size != This->vertex_declaration_size)
1374 WARN("Invalid declaration. New vertex size does not match the original vertex size.\n");
1375 return D3DERR_INVALIDCALL;
1378 /* New declaration must not contain non-zero Stream value */
1379 for (i = 0; declaration[i].Stream != 0xff; i++)
1381 if (declaration[i].Stream != 0)
1383 WARN("Invalid declaration. New declaration contains non-zero Stream value.\n");
1384 return D3DERR_INVALIDCALL;
1388 This->num_elem = i + 1;
1389 copy_declaration(This->cached_declaration, declaration, This->num_elem);
1391 if (This->vertex_declaration)
1392 IDirect3DVertexDeclaration9_Release(This->vertex_declaration);
1394 /* An application can pass an invalid declaration to UpdateSemantics and
1395 * still expect D3D_OK (see tests). If the declaration is invalid, then
1396 * subsequent calls to DrawSubset will fail. This is handled by setting the
1397 * vertex declaration to NULL.
1398 * GetDeclaration, GetNumBytesPerVertex must, however, use the new
1399 * invalid declaration. This is handled by them using the cached vertex
1400 * declaration instead of the actual vertex declaration.
1402 hr = IDirect3DDevice9_CreateVertexDeclaration(This->device,
1403 declaration,
1404 &This->vertex_declaration);
1405 if (FAILED(hr))
1407 WARN("Using invalid declaration. Calls to DrawSubset will fail.\n");
1408 This->vertex_declaration = NULL;
1411 return D3D_OK;
1414 static HRESULT WINAPI d3dx9_mesh_LockAttributeBuffer(ID3DXMesh *iface, DWORD flags, DWORD **data)
1416 struct d3dx9_mesh *mesh = impl_from_ID3DXMesh(iface);
1418 TRACE("iface %p, flags %#lx, data %p.\n", iface, flags, data);
1420 InterlockedIncrement(&mesh->attrib_buffer_lock_count);
1422 if (!(flags & D3DLOCK_READONLY))
1424 D3DXATTRIBUTERANGE *attrib_table = mesh->attrib_table;
1425 mesh->attrib_table_size = 0;
1426 mesh->attrib_table = NULL;
1427 HeapFree(GetProcessHeap(), 0, attrib_table);
1430 *data = mesh->attrib_buffer;
1432 return D3D_OK;
1435 static HRESULT WINAPI d3dx9_mesh_UnlockAttributeBuffer(ID3DXMesh *iface)
1437 struct d3dx9_mesh *mesh = impl_from_ID3DXMesh(iface);
1438 int lock_count;
1440 TRACE("iface %p.\n", iface);
1442 lock_count = InterlockedDecrement(&mesh->attrib_buffer_lock_count);
1443 if (lock_count < 0)
1445 InterlockedIncrement(&mesh->attrib_buffer_lock_count);
1446 return D3DERR_INVALIDCALL;
1449 return D3D_OK;
1452 static HRESULT WINAPI d3dx9_mesh_Optimize(ID3DXMesh *iface, DWORD flags, const DWORD *adjacency_in,
1453 DWORD *adjacency_out, DWORD *face_remap, ID3DXBuffer **vertex_remap, ID3DXMesh **opt_mesh)
1455 struct d3dx9_mesh *mesh = impl_from_ID3DXMesh(iface);
1456 HRESULT hr;
1457 D3DVERTEXELEMENT9 declaration[MAX_FVF_DECL_SIZE] = { D3DDECL_END() };
1458 ID3DXMesh *optimized_mesh;
1460 TRACE("iface %p, flags %#lx, adjacency_in %p, adjacency_out %p, face_remap %p, vertex_remap %p, opt_mesh %p.\n",
1461 iface, flags, adjacency_in, adjacency_out, face_remap, vertex_remap, opt_mesh);
1463 if (!opt_mesh)
1464 return D3DERR_INVALIDCALL;
1466 hr = iface->lpVtbl->GetDeclaration(iface, declaration);
1467 if (FAILED(hr)) return hr;
1469 if (FAILED(hr = iface->lpVtbl->CloneMesh(iface, mesh->options, declaration, mesh->device, &optimized_mesh)))
1470 return hr;
1472 hr = optimized_mesh->lpVtbl->OptimizeInplace(optimized_mesh, flags, adjacency_in, adjacency_out, face_remap, vertex_remap);
1473 if (SUCCEEDED(hr))
1474 *opt_mesh = optimized_mesh;
1475 else
1476 IUnknown_Release(optimized_mesh);
1477 return hr;
1480 /* Creates a vertex_remap that removes unused vertices.
1481 * Indices are updated according to the vertex_remap. */
1482 static HRESULT compact_mesh(struct d3dx9_mesh *This, DWORD *indices,
1483 DWORD *new_num_vertices, ID3DXBuffer **vertex_remap)
1485 HRESULT hr;
1486 DWORD *vertex_remap_ptr;
1487 DWORD num_used_vertices;
1488 DWORD i;
1490 hr = D3DXCreateBuffer(This->numvertices * sizeof(DWORD), vertex_remap);
1491 if (FAILED(hr)) return hr;
1492 vertex_remap_ptr = ID3DXBuffer_GetBufferPointer(*vertex_remap);
1494 for (i = 0; i < This->numfaces * 3; i++)
1495 vertex_remap_ptr[indices[i]] = 1;
1497 /* create old->new vertex mapping */
1498 num_used_vertices = 0;
1499 for (i = 0; i < This->numvertices; i++) {
1500 if (vertex_remap_ptr[i])
1501 vertex_remap_ptr[i] = num_used_vertices++;
1502 else
1503 vertex_remap_ptr[i] = -1;
1505 /* convert indices */
1506 for (i = 0; i < This->numfaces * 3; i++)
1507 indices[i] = vertex_remap_ptr[indices[i]];
1509 /* create new->old vertex mapping */
1510 num_used_vertices = 0;
1511 for (i = 0; i < This->numvertices; i++) {
1512 if (vertex_remap_ptr[i] != -1)
1513 vertex_remap_ptr[num_used_vertices++] = i;
1515 for (i = num_used_vertices; i < This->numvertices; i++)
1516 vertex_remap_ptr[i] = -1;
1518 *new_num_vertices = num_used_vertices;
1520 return D3D_OK;
1523 /* count the number of unique attribute values in a sorted attribute buffer */
1524 static DWORD count_attributes(const DWORD *attrib_buffer, DWORD numfaces)
1526 DWORD last_attribute = attrib_buffer[0];
1527 DWORD attrib_table_size = 1;
1528 DWORD i;
1529 for (i = 1; i < numfaces; i++) {
1530 if (attrib_buffer[i] != last_attribute) {
1531 last_attribute = attrib_buffer[i];
1532 attrib_table_size++;
1535 return attrib_table_size;
1538 static void fill_attribute_table(DWORD *attrib_buffer, DWORD numfaces, void *indices,
1539 BOOL is_32bit_indices, D3DXATTRIBUTERANGE *attrib_table)
1541 DWORD attrib_table_size = 0;
1542 DWORD last_attribute = attrib_buffer[0];
1543 DWORD min_vertex, max_vertex;
1544 DWORD i;
1546 attrib_table[0].AttribId = last_attribute;
1547 attrib_table[0].FaceStart = 0;
1548 min_vertex = (DWORD)-1;
1549 max_vertex = 0;
1550 for (i = 0; i < numfaces; i++) {
1551 DWORD j;
1553 if (attrib_buffer[i] != last_attribute) {
1554 last_attribute = attrib_buffer[i];
1555 attrib_table[attrib_table_size].FaceCount = i - attrib_table[attrib_table_size].FaceStart;
1556 attrib_table[attrib_table_size].VertexStart = min_vertex;
1557 attrib_table[attrib_table_size].VertexCount = max_vertex - min_vertex + 1;
1558 attrib_table_size++;
1559 attrib_table[attrib_table_size].AttribId = attrib_buffer[i];
1560 attrib_table[attrib_table_size].FaceStart = i;
1561 min_vertex = (DWORD)-1;
1562 max_vertex = 0;
1564 for (j = 0; j < 3; j++) {
1565 DWORD vertex_index = is_32bit_indices ? ((DWORD*)indices)[i * 3 + j] : ((WORD*)indices)[i * 3 + j];
1566 if (vertex_index < min_vertex)
1567 min_vertex = vertex_index;
1568 if (vertex_index > max_vertex)
1569 max_vertex = vertex_index;
1572 attrib_table[attrib_table_size].FaceCount = i - attrib_table[attrib_table_size].FaceStart;
1573 attrib_table[attrib_table_size].VertexStart = min_vertex;
1574 attrib_table[attrib_table_size].VertexCount = max_vertex - min_vertex + 1;
1575 attrib_table_size++;
1578 static int __cdecl attrib_entry_compare(const void *a, const void *b)
1580 const DWORD *ptr_a = *(const DWORD **)a;
1581 const DWORD *ptr_b = *(const DWORD **)b;
1582 int delta = *ptr_a - *ptr_b;
1584 if (delta)
1585 return delta;
1587 delta = ptr_a - ptr_b; /* for stable sort */
1588 return delta;
1591 /* Create face_remap, a new attribute buffer for attribute sort optimization. */
1592 static HRESULT remap_faces_for_attrsort(struct d3dx9_mesh *This, const DWORD *indices,
1593 DWORD *attrib_buffer, DWORD **sorted_attrib_buffer, DWORD **face_remap)
1595 DWORD **sorted_attrib_ptr_buffer = NULL;
1596 DWORD i;
1598 sorted_attrib_ptr_buffer = HeapAlloc(GetProcessHeap(), 0, This->numfaces * sizeof(*sorted_attrib_ptr_buffer));
1599 if (!sorted_attrib_ptr_buffer)
1600 return E_OUTOFMEMORY;
1602 *face_remap = HeapAlloc(GetProcessHeap(), 0, This->numfaces * sizeof(**face_remap));
1603 if (!*face_remap)
1605 HeapFree(GetProcessHeap(), 0, sorted_attrib_ptr_buffer);
1606 return E_OUTOFMEMORY;
1609 for (i = 0; i < This->numfaces; i++)
1610 sorted_attrib_ptr_buffer[i] = &attrib_buffer[i];
1611 qsort(sorted_attrib_ptr_buffer, This->numfaces, sizeof(*sorted_attrib_ptr_buffer), attrib_entry_compare);
1613 for (i = 0; i < This->numfaces; i++)
1615 DWORD old_face = sorted_attrib_ptr_buffer[i] - attrib_buffer;
1616 (*face_remap)[old_face] = i;
1619 /* overwrite sorted_attrib_ptr_buffer with the values themselves */
1620 *sorted_attrib_buffer = (DWORD*)sorted_attrib_ptr_buffer;
1621 for (i = 0; i < This->numfaces; i++)
1622 (*sorted_attrib_buffer)[(*face_remap)[i]] = attrib_buffer[i];
1624 return D3D_OK;
1627 static HRESULT WINAPI d3dx9_mesh_OptimizeInplace(ID3DXMesh *iface, DWORD flags, const DWORD *adjacency_in,
1628 DWORD *adjacency_out, DWORD *face_remap_out, ID3DXBuffer **vertex_remap_out)
1630 struct d3dx9_mesh *This = impl_from_ID3DXMesh(iface);
1631 void *indices = NULL;
1632 DWORD *attrib_buffer = NULL;
1633 HRESULT hr;
1634 ID3DXBuffer *vertex_remap = NULL;
1635 DWORD *face_remap = NULL; /* old -> new mapping */
1636 DWORD *dword_indices = NULL;
1637 DWORD new_num_vertices = 0;
1638 DWORD new_num_alloc_vertices = 0;
1639 IDirect3DVertexBuffer9 *vertex_buffer = NULL;
1640 DWORD *sorted_attrib_buffer = NULL;
1641 DWORD i;
1643 TRACE("iface %p, flags %#lx, adjacency_in %p, adjacency_out %p, face_remap_out %p, vertex_remap_out %p.\n",
1644 iface, flags, adjacency_in, adjacency_out, face_remap_out, vertex_remap_out);
1646 if (!flags)
1647 return D3DERR_INVALIDCALL;
1648 if (!adjacency_in && (flags & (D3DXMESHOPT_VERTEXCACHE | D3DXMESHOPT_STRIPREORDER)))
1649 return D3DERR_INVALIDCALL;
1650 if ((flags & (D3DXMESHOPT_VERTEXCACHE | D3DXMESHOPT_STRIPREORDER)) == (D3DXMESHOPT_VERTEXCACHE | D3DXMESHOPT_STRIPREORDER))
1651 return D3DERR_INVALIDCALL;
1653 if (flags & (D3DXMESHOPT_VERTEXCACHE | D3DXMESHOPT_STRIPREORDER))
1655 if (flags & D3DXMESHOPT_VERTEXCACHE)
1656 FIXME("D3DXMESHOPT_VERTEXCACHE not implemented.\n");
1657 if (flags & D3DXMESHOPT_STRIPREORDER)
1658 FIXME("D3DXMESHOPT_STRIPREORDER not implemented.\n");
1659 return E_NOTIMPL;
1662 hr = iface->lpVtbl->LockIndexBuffer(iface, 0, &indices);
1663 if (FAILED(hr)) goto cleanup;
1665 dword_indices = HeapAlloc(GetProcessHeap(), 0, This->numfaces * 3 * sizeof(DWORD));
1666 if (!dword_indices) return E_OUTOFMEMORY;
1667 if (This->options & D3DXMESH_32BIT) {
1668 memcpy(dword_indices, indices, This->numfaces * 3 * sizeof(DWORD));
1669 } else {
1670 WORD *word_indices = indices;
1671 for (i = 0; i < This->numfaces * 3; i++)
1672 dword_indices[i] = *word_indices++;
1675 if ((flags & (D3DXMESHOPT_COMPACT | D3DXMESHOPT_IGNOREVERTS | D3DXMESHOPT_ATTRSORT)) == D3DXMESHOPT_COMPACT)
1677 new_num_alloc_vertices = This->numvertices;
1678 hr = compact_mesh(This, dword_indices, &new_num_vertices, &vertex_remap);
1679 if (FAILED(hr)) goto cleanup;
1680 } else if (flags & D3DXMESHOPT_ATTRSORT) {
1681 if (!(flags & D3DXMESHOPT_IGNOREVERTS))
1682 FIXME("D3DXMESHOPT_ATTRSORT vertex reordering not implemented.\n");
1684 hr = iface->lpVtbl->LockAttributeBuffer(iface, 0, &attrib_buffer);
1685 if (FAILED(hr)) goto cleanup;
1687 hr = remap_faces_for_attrsort(This, dword_indices, attrib_buffer, &sorted_attrib_buffer, &face_remap);
1688 if (FAILED(hr)) goto cleanup;
1691 if (vertex_remap)
1693 /* reorder the vertices using vertex_remap */
1694 D3DVERTEXBUFFER_DESC vertex_desc;
1695 DWORD *vertex_remap_ptr = ID3DXBuffer_GetBufferPointer(vertex_remap);
1696 DWORD vertex_size = iface->lpVtbl->GetNumBytesPerVertex(iface);
1697 BYTE *orig_vertices;
1698 BYTE *new_vertices;
1700 hr = IDirect3DVertexBuffer9_GetDesc(This->vertex_buffer, &vertex_desc);
1701 if (FAILED(hr)) goto cleanup;
1703 hr = IDirect3DDevice9_CreateVertexBuffer(This->device, new_num_alloc_vertices * vertex_size,
1704 vertex_desc.Usage, This->fvf, vertex_desc.Pool, &vertex_buffer, NULL);
1705 if (FAILED(hr)) goto cleanup;
1707 hr = IDirect3DVertexBuffer9_Lock(This->vertex_buffer, 0, 0, (void**)&orig_vertices, D3DLOCK_READONLY);
1708 if (FAILED(hr)) goto cleanup;
1710 hr = IDirect3DVertexBuffer9_Lock(vertex_buffer, 0, 0, (void**)&new_vertices, 0);
1711 if (FAILED(hr)) {
1712 IDirect3DVertexBuffer9_Unlock(This->vertex_buffer);
1713 goto cleanup;
1716 for (i = 0; i < new_num_vertices; i++)
1717 memcpy(new_vertices + i * vertex_size, orig_vertices + vertex_remap_ptr[i] * vertex_size, vertex_size);
1719 IDirect3DVertexBuffer9_Unlock(This->vertex_buffer);
1720 IDirect3DVertexBuffer9_Unlock(vertex_buffer);
1721 } else if (vertex_remap_out) {
1722 DWORD *vertex_remap_ptr;
1724 hr = D3DXCreateBuffer(This->numvertices * sizeof(DWORD), &vertex_remap);
1725 if (FAILED(hr)) goto cleanup;
1726 vertex_remap_ptr = ID3DXBuffer_GetBufferPointer(vertex_remap);
1727 for (i = 0; i < This->numvertices; i++)
1728 *vertex_remap_ptr++ = i;
1731 if (flags & D3DXMESHOPT_ATTRSORT)
1733 D3DXATTRIBUTERANGE *attrib_table;
1734 DWORD attrib_table_size;
1736 attrib_table_size = count_attributes(sorted_attrib_buffer, This->numfaces);
1737 attrib_table = HeapAlloc(GetProcessHeap(), 0, attrib_table_size * sizeof(*attrib_table));
1738 if (!attrib_table) {
1739 hr = E_OUTOFMEMORY;
1740 goto cleanup;
1743 memcpy(attrib_buffer, sorted_attrib_buffer, This->numfaces * sizeof(*attrib_buffer));
1745 /* reorder the indices using face_remap */
1746 if (This->options & D3DXMESH_32BIT) {
1747 for (i = 0; i < This->numfaces; i++)
1748 memcpy((DWORD*)indices + face_remap[i] * 3, dword_indices + i * 3, 3 * sizeof(DWORD));
1749 } else {
1750 WORD *word_indices = indices;
1751 for (i = 0; i < This->numfaces; i++) {
1752 DWORD new_pos = face_remap[i] * 3;
1753 DWORD old_pos = i * 3;
1754 word_indices[new_pos++] = dword_indices[old_pos++];
1755 word_indices[new_pos++] = dword_indices[old_pos++];
1756 word_indices[new_pos] = dword_indices[old_pos];
1760 fill_attribute_table(attrib_buffer, This->numfaces, indices,
1761 This->options & D3DXMESH_32BIT, attrib_table);
1763 HeapFree(GetProcessHeap(), 0, This->attrib_table);
1764 This->attrib_table = attrib_table;
1765 This->attrib_table_size = attrib_table_size;
1766 } else {
1767 if (This->options & D3DXMESH_32BIT) {
1768 memcpy(indices, dword_indices, This->numfaces * 3 * sizeof(DWORD));
1769 } else {
1770 WORD *word_indices = indices;
1771 for (i = 0; i < This->numfaces * 3; i++)
1772 *word_indices++ = dword_indices[i];
1776 if (adjacency_out) {
1777 if (face_remap) {
1778 for (i = 0; i < This->numfaces; i++) {
1779 DWORD old_pos = i * 3;
1780 DWORD new_pos = face_remap[i] * 3;
1781 adjacency_out[new_pos++] = face_remap[adjacency_in[old_pos++]];
1782 adjacency_out[new_pos++] = face_remap[adjacency_in[old_pos++]];
1783 adjacency_out[new_pos++] = face_remap[adjacency_in[old_pos++]];
1785 } else {
1786 memcpy(adjacency_out, adjacency_in, This->numfaces * 3 * sizeof(*adjacency_out));
1789 if (face_remap_out) {
1790 if (face_remap) {
1791 for (i = 0; i < This->numfaces; i++)
1792 face_remap_out[face_remap[i]] = i;
1793 } else {
1794 for (i = 0; i < This->numfaces; i++)
1795 face_remap_out[i] = i;
1798 if (vertex_remap_out)
1799 *vertex_remap_out = vertex_remap;
1800 vertex_remap = NULL;
1802 if (vertex_buffer) {
1803 IDirect3DVertexBuffer9_Release(This->vertex_buffer);
1804 This->vertex_buffer = vertex_buffer;
1805 vertex_buffer = NULL;
1806 This->numvertices = new_num_vertices;
1809 hr = D3D_OK;
1810 cleanup:
1811 HeapFree(GetProcessHeap(), 0, sorted_attrib_buffer);
1812 HeapFree(GetProcessHeap(), 0, face_remap);
1813 HeapFree(GetProcessHeap(), 0, dword_indices);
1814 if (vertex_remap) ID3DXBuffer_Release(vertex_remap);
1815 if (vertex_buffer) IDirect3DVertexBuffer9_Release(vertex_buffer);
1816 if (attrib_buffer) iface->lpVtbl->UnlockAttributeBuffer(iface);
1817 if (indices) iface->lpVtbl->UnlockIndexBuffer(iface);
1818 return hr;
1821 static HRESULT WINAPI d3dx9_mesh_SetAttributeTable(ID3DXMesh *iface,
1822 const D3DXATTRIBUTERANGE *attrib_table, DWORD attrib_table_size)
1824 struct d3dx9_mesh *mesh = impl_from_ID3DXMesh(iface);
1825 D3DXATTRIBUTERANGE *new_table = NULL;
1827 TRACE("iface %p, attrib_table %p, attrib_table_size %lu.\n", iface, attrib_table, attrib_table_size);
1829 if (attrib_table_size) {
1830 size_t size = attrib_table_size * sizeof(*attrib_table);
1832 new_table = HeapAlloc(GetProcessHeap(), 0, size);
1833 if (!new_table)
1834 return E_OUTOFMEMORY;
1836 CopyMemory(new_table, attrib_table, size);
1837 } else if (attrib_table) {
1838 return D3DERR_INVALIDCALL;
1840 HeapFree(GetProcessHeap(), 0, mesh->attrib_table);
1841 mesh->attrib_table = new_table;
1842 mesh->attrib_table_size = attrib_table_size;
1844 return D3D_OK;
1847 static const struct ID3DXMeshVtbl D3DXMesh_Vtbl =
1849 d3dx9_mesh_QueryInterface,
1850 d3dx9_mesh_AddRef,
1851 d3dx9_mesh_Release,
1852 d3dx9_mesh_DrawSubset,
1853 d3dx9_mesh_GetNumFaces,
1854 d3dx9_mesh_GetNumVertices,
1855 d3dx9_mesh_GetFVF,
1856 d3dx9_mesh_GetDeclaration,
1857 d3dx9_mesh_GetNumBytesPerVertex,
1858 d3dx9_mesh_GetOptions,
1859 d3dx9_mesh_GetDevice,
1860 d3dx9_mesh_CloneMeshFVF,
1861 d3dx9_mesh_CloneMesh,
1862 d3dx9_mesh_GetVertexBuffer,
1863 d3dx9_mesh_GetIndexBuffer,
1864 d3dx9_mesh_LockVertexBuffer,
1865 d3dx9_mesh_UnlockVertexBuffer,
1866 d3dx9_mesh_LockIndexBuffer,
1867 d3dx9_mesh_UnlockIndexBuffer,
1868 d3dx9_mesh_GetAttributeTable,
1869 d3dx9_mesh_ConvertPointRepsToAdjacency,
1870 d3dx9_mesh_ConvertAdjacencyToPointReps,
1871 d3dx9_mesh_GenerateAdjacency,
1872 d3dx9_mesh_UpdateSemantics,
1873 d3dx9_mesh_LockAttributeBuffer,
1874 d3dx9_mesh_UnlockAttributeBuffer,
1875 d3dx9_mesh_Optimize,
1876 d3dx9_mesh_OptimizeInplace,
1877 d3dx9_mesh_SetAttributeTable,
1881 /* Algorithm taken from the article: An Efficient and Robust Ray-Box Intersection Algorithm
1882 Amy Williams University of Utah
1883 Steve Barrus University of Utah
1884 R. Keith Morley University of Utah
1885 Peter Shirley University of Utah
1887 International Conference on Computer Graphics and Interactive Techniques archive
1888 ACM SIGGRAPH 2005 Courses
1889 Los Angeles, California
1891 This algorithm is free of patents or of copyrights, as confirmed by Peter Shirley himself.
1893 Algorithm: Consider the box as the intersection of three slabs. Clip the ray
1894 against each slab, if there's anything left of the ray after we're
1895 done we've got an intersection of the ray with the box. */
1896 BOOL WINAPI D3DXBoxBoundProbe(const D3DXVECTOR3 *pmin, const D3DXVECTOR3 *pmax,
1897 const D3DXVECTOR3 *prayposition, const D3DXVECTOR3 *praydirection)
1899 FLOAT div, tmin, tmax, tymin, tymax, tzmin, tzmax;
1901 div = 1.0f / praydirection->x;
1902 if ( div >= 0.0f )
1904 tmin = ( pmin->x - prayposition->x ) * div;
1905 tmax = ( pmax->x - prayposition->x ) * div;
1907 else
1909 tmin = ( pmax->x - prayposition->x ) * div;
1910 tmax = ( pmin->x - prayposition->x ) * div;
1913 if ( tmax < 0.0f ) return FALSE;
1915 div = 1.0f / praydirection->y;
1916 if ( div >= 0.0f )
1918 tymin = ( pmin->y - prayposition->y ) * div;
1919 tymax = ( pmax->y - prayposition->y ) * div;
1921 else
1923 tymin = ( pmax->y - prayposition->y ) * div;
1924 tymax = ( pmin->y - prayposition->y ) * div;
1927 if ( ( tymax < 0.0f ) || ( tmin > tymax ) || ( tymin > tmax ) ) return FALSE;
1929 if ( tymin > tmin ) tmin = tymin;
1930 if ( tymax < tmax ) tmax = tymax;
1932 div = 1.0f / praydirection->z;
1933 if ( div >= 0.0f )
1935 tzmin = ( pmin->z - prayposition->z ) * div;
1936 tzmax = ( pmax->z - prayposition->z ) * div;
1938 else
1940 tzmin = ( pmax->z - prayposition->z ) * div;
1941 tzmax = ( pmin->z - prayposition->z ) * div;
1944 if ( (tzmax < 0.0f ) || ( tmin > tzmax ) || ( tzmin > tmax ) ) return FALSE;
1946 return TRUE;
1949 HRESULT WINAPI D3DXComputeBoundingBox(const D3DXVECTOR3 *pfirstposition,
1950 DWORD numvertices, DWORD dwstride, D3DXVECTOR3 *pmin, D3DXVECTOR3 *pmax)
1952 D3DXVECTOR3 vec;
1953 unsigned int i;
1955 if( !pfirstposition || !pmin || !pmax ) return D3DERR_INVALIDCALL;
1957 *pmin = *pfirstposition;
1958 *pmax = *pmin;
1960 for(i=0; i<numvertices; i++)
1962 vec = *( (const D3DXVECTOR3*)((const char*)pfirstposition + dwstride * i) );
1964 if ( vec.x < pmin->x ) pmin->x = vec.x;
1965 if ( vec.x > pmax->x ) pmax->x = vec.x;
1967 if ( vec.y < pmin->y ) pmin->y = vec.y;
1968 if ( vec.y > pmax->y ) pmax->y = vec.y;
1970 if ( vec.z < pmin->z ) pmin->z = vec.z;
1971 if ( vec.z > pmax->z ) pmax->z = vec.z;
1974 return D3D_OK;
1977 HRESULT WINAPI D3DXComputeBoundingSphere(const D3DXVECTOR3 *pfirstposition,
1978 DWORD numvertices, DWORD dwstride, D3DXVECTOR3 *pcenter, float *pradius)
1980 D3DXVECTOR3 temp;
1981 FLOAT d;
1982 unsigned int i;
1984 if( !pfirstposition || !pcenter || !pradius ) return D3DERR_INVALIDCALL;
1986 temp.x = 0.0f;
1987 temp.y = 0.0f;
1988 temp.z = 0.0f;
1989 *pradius = 0.0f;
1991 for(i=0; i<numvertices; i++)
1992 D3DXVec3Add(&temp, &temp, (const D3DXVECTOR3*)((const char*)pfirstposition + dwstride * i));
1994 D3DXVec3Scale(pcenter, &temp, 1.0f / numvertices);
1996 for(i=0; i<numvertices; i++)
1998 d = D3DXVec3Length(D3DXVec3Subtract(&temp, (const D3DXVECTOR3*)((const char*)pfirstposition + dwstride * i), pcenter));
1999 if ( d > *pradius ) *pradius = d;
2001 return D3D_OK;
2004 static void append_decl_element(D3DVERTEXELEMENT9 *declaration, UINT *idx, UINT *offset,
2005 D3DDECLTYPE type, D3DDECLUSAGE usage, UINT usage_idx)
2007 declaration[*idx].Stream = 0;
2008 declaration[*idx].Offset = *offset;
2009 declaration[*idx].Type = type;
2010 declaration[*idx].Method = D3DDECLMETHOD_DEFAULT;
2011 declaration[*idx].Usage = usage;
2012 declaration[*idx].UsageIndex = usage_idx;
2014 *offset += d3dx_decltype_size[type];
2015 ++(*idx);
2018 /*************************************************************************
2019 * D3DXDeclaratorFromFVF
2021 HRESULT WINAPI D3DXDeclaratorFromFVF(DWORD fvf, D3DVERTEXELEMENT9 declaration[MAX_FVF_DECL_SIZE])
2023 static const D3DVERTEXELEMENT9 end_element = D3DDECL_END();
2024 DWORD tex_count = (fvf & D3DFVF_TEXCOUNT_MASK) >> D3DFVF_TEXCOUNT_SHIFT;
2025 unsigned int offset = 0;
2026 unsigned int idx = 0;
2027 unsigned int i;
2029 TRACE("fvf %#lx, declaration %p.\n", fvf, declaration);
2031 if (fvf & (D3DFVF_RESERVED0 | D3DFVF_RESERVED2)) return D3DERR_INVALIDCALL;
2033 if (fvf & D3DFVF_POSITION_MASK)
2035 BOOL has_blend = (fvf & D3DFVF_XYZB5) >= D3DFVF_XYZB1;
2036 DWORD blend_count = 1 + (((fvf & D3DFVF_XYZB5) - D3DFVF_XYZB1) >> 1);
2037 BOOL has_blend_idx = (fvf & D3DFVF_LASTBETA_D3DCOLOR) || (fvf & D3DFVF_LASTBETA_UBYTE4);
2039 if (has_blend_idx) --blend_count;
2041 if ((fvf & D3DFVF_POSITION_MASK) == D3DFVF_XYZW
2042 || (has_blend && blend_count > 4))
2043 return D3DERR_INVALIDCALL;
2045 if ((fvf & D3DFVF_POSITION_MASK) == D3DFVF_XYZRHW)
2046 append_decl_element(declaration, &idx, &offset, D3DDECLTYPE_FLOAT4, D3DDECLUSAGE_POSITIONT, 0);
2047 else
2048 append_decl_element(declaration, &idx, &offset, D3DDECLTYPE_FLOAT3, D3DDECLUSAGE_POSITION, 0);
2050 if (has_blend)
2052 switch (blend_count)
2054 case 0:
2055 break;
2056 case 1:
2057 append_decl_element(declaration, &idx, &offset, D3DDECLTYPE_FLOAT1, D3DDECLUSAGE_BLENDWEIGHT, 0);
2058 break;
2059 case 2:
2060 append_decl_element(declaration, &idx, &offset, D3DDECLTYPE_FLOAT2, D3DDECLUSAGE_BLENDWEIGHT, 0);
2061 break;
2062 case 3:
2063 append_decl_element(declaration, &idx, &offset, D3DDECLTYPE_FLOAT3, D3DDECLUSAGE_BLENDWEIGHT, 0);
2064 break;
2065 case 4:
2066 append_decl_element(declaration, &idx, &offset, D3DDECLTYPE_FLOAT4, D3DDECLUSAGE_BLENDWEIGHT, 0);
2067 break;
2068 default:
2069 ERR("Invalid blend count %lu.\n", blend_count);
2070 break;
2073 if (has_blend_idx)
2075 if (fvf & D3DFVF_LASTBETA_UBYTE4)
2076 append_decl_element(declaration, &idx, &offset, D3DDECLTYPE_UBYTE4, D3DDECLUSAGE_BLENDINDICES, 0);
2077 else if (fvf & D3DFVF_LASTBETA_D3DCOLOR)
2078 append_decl_element(declaration, &idx, &offset, D3DDECLTYPE_D3DCOLOR, D3DDECLUSAGE_BLENDINDICES, 0);
2083 if (fvf & D3DFVF_NORMAL)
2084 append_decl_element(declaration, &idx, &offset, D3DDECLTYPE_FLOAT3, D3DDECLUSAGE_NORMAL, 0);
2085 if (fvf & D3DFVF_PSIZE)
2086 append_decl_element(declaration, &idx, &offset, D3DDECLTYPE_FLOAT1, D3DDECLUSAGE_PSIZE, 0);
2087 if (fvf & D3DFVF_DIFFUSE)
2088 append_decl_element(declaration, &idx, &offset, D3DDECLTYPE_D3DCOLOR, D3DDECLUSAGE_COLOR, 0);
2089 if (fvf & D3DFVF_SPECULAR)
2090 append_decl_element(declaration, &idx, &offset, D3DDECLTYPE_D3DCOLOR, D3DDECLUSAGE_COLOR, 1);
2092 for (i = 0; i < tex_count; ++i)
2094 switch ((fvf >> (16 + 2 * i)) & 0x03)
2096 case D3DFVF_TEXTUREFORMAT1:
2097 append_decl_element(declaration, &idx, &offset, D3DDECLTYPE_FLOAT1, D3DDECLUSAGE_TEXCOORD, i);
2098 break;
2099 case D3DFVF_TEXTUREFORMAT2:
2100 append_decl_element(declaration, &idx, &offset, D3DDECLTYPE_FLOAT2, D3DDECLUSAGE_TEXCOORD, i);
2101 break;
2102 case D3DFVF_TEXTUREFORMAT3:
2103 append_decl_element(declaration, &idx, &offset, D3DDECLTYPE_FLOAT3, D3DDECLUSAGE_TEXCOORD, i);
2104 break;
2105 case D3DFVF_TEXTUREFORMAT4:
2106 append_decl_element(declaration, &idx, &offset, D3DDECLTYPE_FLOAT4, D3DDECLUSAGE_TEXCOORD, i);
2107 break;
2111 declaration[idx] = end_element;
2113 return D3D_OK;
2116 /*************************************************************************
2117 * D3DXFVFFromDeclarator
2119 HRESULT WINAPI D3DXFVFFromDeclarator(const D3DVERTEXELEMENT9 *declaration, DWORD *fvf)
2121 unsigned int i = 0, texture, offset;
2123 TRACE("(%p, %p)\n", declaration, fvf);
2125 *fvf = 0;
2126 if (declaration[0].Type == D3DDECLTYPE_FLOAT3 && declaration[0].Usage == D3DDECLUSAGE_POSITION)
2128 if ((declaration[1].Type == D3DDECLTYPE_FLOAT4 && declaration[1].Usage == D3DDECLUSAGE_BLENDWEIGHT &&
2129 declaration[1].UsageIndex == 0) &&
2130 (declaration[2].Type == D3DDECLTYPE_FLOAT1 && declaration[2].Usage == D3DDECLUSAGE_BLENDINDICES &&
2131 declaration[2].UsageIndex == 0))
2133 return D3DERR_INVALIDCALL;
2135 else if ((declaration[1].Type == D3DDECLTYPE_UBYTE4 || declaration[1].Type == D3DDECLTYPE_D3DCOLOR) &&
2136 declaration[1].Usage == D3DDECLUSAGE_BLENDINDICES && declaration[1].UsageIndex == 0)
2138 if (declaration[1].Type == D3DDECLTYPE_UBYTE4)
2140 *fvf |= D3DFVF_XYZB1 | D3DFVF_LASTBETA_UBYTE4;
2142 else
2144 *fvf |= D3DFVF_XYZB1 | D3DFVF_LASTBETA_D3DCOLOR;
2146 i = 2;
2148 else if (declaration[1].Type <= D3DDECLTYPE_FLOAT4 && declaration[1].Usage == D3DDECLUSAGE_BLENDWEIGHT &&
2149 declaration[1].UsageIndex == 0)
2151 if ((declaration[2].Type == D3DDECLTYPE_UBYTE4 || declaration[2].Type == D3DDECLTYPE_D3DCOLOR) &&
2152 declaration[2].Usage == D3DDECLUSAGE_BLENDINDICES && declaration[2].UsageIndex == 0)
2154 if (declaration[2].Type == D3DDECLTYPE_UBYTE4)
2156 *fvf |= D3DFVF_LASTBETA_UBYTE4;
2158 else
2160 *fvf |= D3DFVF_LASTBETA_D3DCOLOR;
2162 switch (declaration[1].Type)
2164 case D3DDECLTYPE_FLOAT1: *fvf |= D3DFVF_XYZB2; break;
2165 case D3DDECLTYPE_FLOAT2: *fvf |= D3DFVF_XYZB3; break;
2166 case D3DDECLTYPE_FLOAT3: *fvf |= D3DFVF_XYZB4; break;
2167 case D3DDECLTYPE_FLOAT4: *fvf |= D3DFVF_XYZB5; break;
2169 i = 3;
2171 else
2173 switch (declaration[1].Type)
2175 case D3DDECLTYPE_FLOAT1: *fvf |= D3DFVF_XYZB1; break;
2176 case D3DDECLTYPE_FLOAT2: *fvf |= D3DFVF_XYZB2; break;
2177 case D3DDECLTYPE_FLOAT3: *fvf |= D3DFVF_XYZB3; break;
2178 case D3DDECLTYPE_FLOAT4: *fvf |= D3DFVF_XYZB4; break;
2180 i = 2;
2183 else
2185 *fvf |= D3DFVF_XYZ;
2186 i = 1;
2189 else if (declaration[0].Type == D3DDECLTYPE_FLOAT4 && declaration[0].Usage == D3DDECLUSAGE_POSITIONT &&
2190 declaration[0].UsageIndex == 0)
2192 *fvf |= D3DFVF_XYZRHW;
2193 i = 1;
2196 if (declaration[i].Type == D3DDECLTYPE_FLOAT3 && declaration[i].Usage == D3DDECLUSAGE_NORMAL)
2198 *fvf |= D3DFVF_NORMAL;
2199 i++;
2201 if (declaration[i].Type == D3DDECLTYPE_FLOAT1 && declaration[i].Usage == D3DDECLUSAGE_PSIZE &&
2202 declaration[i].UsageIndex == 0)
2204 *fvf |= D3DFVF_PSIZE;
2205 i++;
2207 if (declaration[i].Type == D3DDECLTYPE_D3DCOLOR && declaration[i].Usage == D3DDECLUSAGE_COLOR &&
2208 declaration[i].UsageIndex == 0)
2210 *fvf |= D3DFVF_DIFFUSE;
2211 i++;
2213 if (declaration[i].Type == D3DDECLTYPE_D3DCOLOR && declaration[i].Usage == D3DDECLUSAGE_COLOR &&
2214 declaration[i].UsageIndex == 1)
2216 *fvf |= D3DFVF_SPECULAR;
2217 i++;
2220 for (texture = 0; texture < D3DDP_MAXTEXCOORD; i++, texture++)
2222 if (declaration[i].Stream == 0xFF)
2224 break;
2226 else if (declaration[i].Type == D3DDECLTYPE_FLOAT1 && declaration[i].Usage == D3DDECLUSAGE_TEXCOORD &&
2227 declaration[i].UsageIndex == texture)
2229 *fvf |= D3DFVF_TEXCOORDSIZE1(declaration[i].UsageIndex);
2231 else if (declaration[i].Type == D3DDECLTYPE_FLOAT2 && declaration[i].Usage == D3DDECLUSAGE_TEXCOORD &&
2232 declaration[i].UsageIndex == texture)
2234 *fvf |= D3DFVF_TEXCOORDSIZE2(declaration[i].UsageIndex);
2236 else if (declaration[i].Type == D3DDECLTYPE_FLOAT3 && declaration[i].Usage == D3DDECLUSAGE_TEXCOORD &&
2237 declaration[i].UsageIndex == texture)
2239 *fvf |= D3DFVF_TEXCOORDSIZE3(declaration[i].UsageIndex);
2241 else if (declaration[i].Type == D3DDECLTYPE_FLOAT4 && declaration[i].Usage == D3DDECLUSAGE_TEXCOORD &&
2242 declaration[i].UsageIndex == texture)
2244 *fvf |= D3DFVF_TEXCOORDSIZE4(declaration[i].UsageIndex);
2246 else
2248 return D3DERR_INVALIDCALL;
2252 *fvf |= (texture << D3DFVF_TEXCOUNT_SHIFT);
2254 for (offset = 0, i = 0; declaration[i].Stream != 0xFF;
2255 offset += d3dx_decltype_size[declaration[i].Type], i++)
2257 if (declaration[i].Offset != offset)
2259 return D3DERR_INVALIDCALL;
2263 return D3D_OK;
2266 /*************************************************************************
2267 * D3DXGetFVFVertexSize
2269 static UINT Get_TexCoord_Size_From_FVF(DWORD FVF, int tex_num)
2271 return (((((FVF) >> (16 + (2 * (tex_num)))) + 1) & 0x03) + 1);
2274 UINT WINAPI D3DXGetFVFVertexSize(DWORD FVF)
2276 DWORD size = 0;
2277 UINT i;
2278 UINT numTextures = (FVF & D3DFVF_TEXCOUNT_MASK) >> D3DFVF_TEXCOUNT_SHIFT;
2280 if (FVF & D3DFVF_NORMAL) size += sizeof(D3DXVECTOR3);
2281 if (FVF & D3DFVF_DIFFUSE) size += sizeof(DWORD);
2282 if (FVF & D3DFVF_SPECULAR) size += sizeof(DWORD);
2283 if (FVF & D3DFVF_PSIZE) size += sizeof(DWORD);
2285 switch (FVF & D3DFVF_POSITION_MASK)
2287 case D3DFVF_XYZ: size += sizeof(D3DXVECTOR3); break;
2288 case D3DFVF_XYZRHW: size += 4 * sizeof(FLOAT); break;
2289 case D3DFVF_XYZB1: size += 4 * sizeof(FLOAT); break;
2290 case D3DFVF_XYZB2: size += 5 * sizeof(FLOAT); break;
2291 case D3DFVF_XYZB3: size += 6 * sizeof(FLOAT); break;
2292 case D3DFVF_XYZB4: size += 7 * sizeof(FLOAT); break;
2293 case D3DFVF_XYZB5: size += 8 * sizeof(FLOAT); break;
2294 case D3DFVF_XYZW: size += 4 * sizeof(FLOAT); break;
2297 for (i = 0; i < numTextures; i++)
2299 size += Get_TexCoord_Size_From_FVF(FVF, i) * sizeof(FLOAT);
2302 return size;
2305 /*************************************************************************
2306 * D3DXGetDeclVertexSize
2308 UINT WINAPI D3DXGetDeclVertexSize(const D3DVERTEXELEMENT9 *decl, DWORD stream_idx)
2310 const D3DVERTEXELEMENT9 *element;
2311 UINT size = 0;
2313 TRACE("decl %p, stream_idx %lu.\n", decl, stream_idx);
2315 if (!decl) return 0;
2317 for (element = decl; element->Stream != 0xff; ++element)
2319 UINT type_size;
2321 if (element->Stream != stream_idx) continue;
2323 if (element->Type >= ARRAY_SIZE(d3dx_decltype_size))
2325 FIXME("Unhandled element type %#x, size will be incorrect.\n", element->Type);
2326 continue;
2329 type_size = d3dx_decltype_size[element->Type];
2330 if (element->Offset + type_size > size) size = element->Offset + type_size;
2333 return size;
2336 /*************************************************************************
2337 * D3DXGetDeclLength
2339 UINT WINAPI D3DXGetDeclLength(const D3DVERTEXELEMENT9 *decl)
2341 const D3DVERTEXELEMENT9 *element;
2343 TRACE("decl %p\n", decl);
2345 /* null decl results in exception on Windows XP */
2347 for (element = decl; element->Stream != 0xff; ++element);
2349 return element - decl;
2352 BOOL WINAPI D3DXIntersectTri(const D3DXVECTOR3 *p0, const D3DXVECTOR3 *p1, const D3DXVECTOR3 *p2,
2353 const D3DXVECTOR3 *praypos, const D3DXVECTOR3 *praydir, float *pu, float *pv, float *pdist)
2355 D3DXMATRIX m;
2356 D3DXVECTOR4 vec;
2358 TRACE("p0 %p, p1 %p, p2 %p, praypos %p, praydir %p, pu %p, pv %p, pdist %p.\n",
2359 p0, p1, p2, praypos, praydir, pu, pv, pdist);
2361 m.m[0][0] = p1->x - p0->x;
2362 m.m[1][0] = p2->x - p0->x;
2363 m.m[2][0] = -praydir->x;
2364 m.m[3][0] = 0.0f;
2365 m.m[0][1] = p1->y - p0->y;
2366 m.m[1][1] = p2->y - p0->y;
2367 m.m[2][1] = -praydir->y;
2368 m.m[3][1] = 0.0f;
2369 m.m[0][2] = p1->z - p0->z;
2370 m.m[1][2] = p2->z - p0->z;
2371 m.m[2][2] = -praydir->z;
2372 m.m[3][2] = 0.0f;
2373 m.m[0][3] = 0.0f;
2374 m.m[1][3] = 0.0f;
2375 m.m[2][3] = 0.0f;
2376 m.m[3][3] = 1.0f;
2378 vec.x = praypos->x - p0->x;
2379 vec.y = praypos->y - p0->y;
2380 vec.z = praypos->z - p0->z;
2381 vec.w = 0.0f;
2383 if ( D3DXMatrixInverse(&m, NULL, &m) )
2385 D3DXVec4Transform(&vec, &vec, &m);
2386 if ( (vec.x >= 0.0f) && (vec.y >= 0.0f) && (vec.x + vec.y <= 1.0f) && (vec.z >= 0.0f) )
2388 if (pu) *pu = vec.x;
2389 if (pv) *pv = vec.y;
2390 if (pdist) *pdist = fabsf( vec.z );
2391 return TRUE;
2395 return FALSE;
2398 BOOL WINAPI D3DXSphereBoundProbe(const D3DXVECTOR3 *center, float radius,
2399 const D3DXVECTOR3 *ray_position, const D3DXVECTOR3 *ray_direction)
2401 D3DXVECTOR3 difference = {0};
2402 float a, b, c, d;
2404 D3DXVec3Subtract(&difference, ray_position, center);
2405 c = D3DXVec3LengthSq(&difference) - radius * radius;
2406 if (c < 0.0f)
2407 return TRUE;
2408 a = D3DXVec3LengthSq(ray_direction);
2409 b = D3DXVec3Dot(&difference, ray_direction);
2410 d = b * b - a * c;
2412 return d >= 0.0f && (b <= 0.0f || d > b * b);
2415 /*************************************************************************
2416 * D3DXCreateMesh
2418 HRESULT WINAPI D3DXCreateMesh(DWORD numfaces, DWORD numvertices, DWORD options,
2419 const D3DVERTEXELEMENT9 *declaration, struct IDirect3DDevice9 *device, struct ID3DXMesh **mesh)
2421 HRESULT hr;
2422 DWORD fvf;
2423 IDirect3DVertexDeclaration9 *vertex_declaration;
2424 UINT vertex_declaration_size;
2425 UINT num_elem;
2426 IDirect3DVertexBuffer9 *vertex_buffer;
2427 IDirect3DIndexBuffer9 *index_buffer;
2428 DWORD *attrib_buffer;
2429 struct d3dx9_mesh *object;
2430 DWORD index_usage = 0;
2431 D3DPOOL index_pool = D3DPOOL_DEFAULT;
2432 D3DFORMAT index_format = D3DFMT_INDEX16;
2433 DWORD vertex_usage = 0;
2434 D3DPOOL vertex_pool = D3DPOOL_DEFAULT;
2435 int i;
2437 TRACE("numfaces %lu, numvertices %lu, options %#lx, declaration %p, device %p, mesh %p.\n",
2438 numfaces, numvertices, options, declaration, device, mesh);
2440 if (numfaces == 0 || numvertices == 0 || declaration == NULL || device == NULL || mesh == NULL ||
2441 /* D3DXMESH_VB_SHARE is for cloning, and D3DXMESH_USEHWONLY is for ConvertToBlendedMesh */
2442 (options & (D3DXMESH_VB_SHARE | D3DXMESH_USEHWONLY | 0xfffe0000)))
2444 return D3DERR_INVALIDCALL;
2446 for (i = 0; declaration[i].Stream != 0xff; i++)
2447 if (declaration[i].Stream != 0)
2448 return D3DERR_INVALIDCALL;
2449 num_elem = i + 1;
2451 if (options & D3DXMESH_32BIT)
2452 index_format = D3DFMT_INDEX32;
2454 if (options & D3DXMESH_DONOTCLIP) {
2455 index_usage |= D3DUSAGE_DONOTCLIP;
2456 vertex_usage |= D3DUSAGE_DONOTCLIP;
2458 if (options & D3DXMESH_POINTS) {
2459 index_usage |= D3DUSAGE_POINTS;
2460 vertex_usage |= D3DUSAGE_POINTS;
2462 if (options & D3DXMESH_RTPATCHES) {
2463 index_usage |= D3DUSAGE_RTPATCHES;
2464 vertex_usage |= D3DUSAGE_RTPATCHES;
2466 if (options & D3DXMESH_NPATCHES) {
2467 index_usage |= D3DUSAGE_NPATCHES;
2468 vertex_usage |= D3DUSAGE_NPATCHES;
2471 if (options & D3DXMESH_VB_SYSTEMMEM)
2472 vertex_pool = D3DPOOL_SYSTEMMEM;
2473 else if (options & D3DXMESH_VB_MANAGED)
2474 vertex_pool = D3DPOOL_MANAGED;
2476 if (options & D3DXMESH_VB_WRITEONLY)
2477 vertex_usage |= D3DUSAGE_WRITEONLY;
2478 if (options & D3DXMESH_VB_DYNAMIC)
2479 vertex_usage |= D3DUSAGE_DYNAMIC;
2480 if (options & D3DXMESH_VB_SOFTWAREPROCESSING)
2481 vertex_usage |= D3DUSAGE_SOFTWAREPROCESSING;
2483 if (options & D3DXMESH_IB_SYSTEMMEM)
2484 index_pool = D3DPOOL_SYSTEMMEM;
2485 else if (options & D3DXMESH_IB_MANAGED)
2486 index_pool = D3DPOOL_MANAGED;
2488 if (options & D3DXMESH_IB_WRITEONLY)
2489 index_usage |= D3DUSAGE_WRITEONLY;
2490 if (options & D3DXMESH_IB_DYNAMIC)
2491 index_usage |= D3DUSAGE_DYNAMIC;
2492 if (options & D3DXMESH_IB_SOFTWAREPROCESSING)
2493 index_usage |= D3DUSAGE_SOFTWAREPROCESSING;
2495 hr = D3DXFVFFromDeclarator(declaration, &fvf);
2496 if (hr != D3D_OK)
2498 fvf = 0;
2501 if (FAILED(hr = IDirect3DDevice9_CreateVertexDeclaration(device, declaration, &vertex_declaration)))
2503 WARN("Failed to create vertex declaration, hr %#lx.\n", hr);
2504 return hr;
2506 vertex_declaration_size = D3DXGetDeclVertexSize(declaration, declaration[0].Stream);
2508 if (FAILED(hr = IDirect3DDevice9_CreateVertexBuffer(device, numvertices * vertex_declaration_size,
2509 vertex_usage, fvf, vertex_pool, &vertex_buffer, NULL)))
2511 WARN("Failed to create vertex buffer, hr %#lx.\n", hr);
2512 IDirect3DVertexDeclaration9_Release(vertex_declaration);
2513 return hr;
2516 if (FAILED(hr = IDirect3DDevice9_CreateIndexBuffer(device,
2517 numfaces * 3 * ((index_format == D3DFMT_INDEX16) ? 2 : 4), index_usage, index_format,
2518 index_pool, &index_buffer, NULL)))
2520 WARN("Failed to create index buffer, hr %#lx.\n", hr);
2521 IDirect3DVertexBuffer9_Release(vertex_buffer);
2522 IDirect3DVertexDeclaration9_Release(vertex_declaration);
2523 return hr;
2526 attrib_buffer = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, numfaces * sizeof(*attrib_buffer));
2527 object = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(*object));
2528 if (object == NULL || attrib_buffer == NULL)
2530 HeapFree(GetProcessHeap(), 0, object);
2531 HeapFree(GetProcessHeap(), 0, attrib_buffer);
2532 IDirect3DIndexBuffer9_Release(index_buffer);
2533 IDirect3DVertexBuffer9_Release(vertex_buffer);
2534 IDirect3DVertexDeclaration9_Release(vertex_declaration);
2535 *mesh = NULL;
2536 return E_OUTOFMEMORY;
2538 object->ID3DXMesh_iface.lpVtbl = &D3DXMesh_Vtbl;
2539 object->ref = 1;
2541 object->numfaces = numfaces;
2542 object->numvertices = numvertices;
2543 object->options = options;
2544 object->fvf = fvf;
2545 object->device = device;
2546 IDirect3DDevice9_AddRef(device);
2548 copy_declaration(object->cached_declaration, declaration, num_elem);
2549 object->vertex_declaration = vertex_declaration;
2550 object->vertex_declaration_size = vertex_declaration_size;
2551 object->num_elem = num_elem;
2552 object->vertex_buffer = vertex_buffer;
2553 object->index_buffer = index_buffer;
2554 object->attrib_buffer = attrib_buffer;
2556 *mesh = &object->ID3DXMesh_iface;
2558 return D3D_OK;
2561 /*************************************************************************
2562 * D3DXCreateMeshFVF
2564 HRESULT WINAPI D3DXCreateMeshFVF(DWORD face_count, DWORD vertex_count, DWORD options,
2565 DWORD fvf, struct IDirect3DDevice9 *device, struct ID3DXMesh **mesh)
2567 HRESULT hr;
2568 D3DVERTEXELEMENT9 declaration[MAX_FVF_DECL_SIZE];
2570 TRACE("face_count %lu, vertex_count %lu, options %#lx, fvf %#lx, device %p, mesh %p.\n",
2571 face_count, vertex_count, options, fvf, device, mesh);
2573 hr = D3DXDeclaratorFromFVF(fvf, declaration);
2574 if (FAILED(hr)) return hr;
2576 return D3DXCreateMesh(face_count, vertex_count, options, declaration, device, mesh);
2580 struct mesh_data {
2581 unsigned int num_vertices;
2582 unsigned int num_poly_faces;
2583 unsigned int num_tri_faces;
2584 D3DXVECTOR3 *vertices;
2585 unsigned int *num_tri_per_face;
2586 DWORD *indices;
2588 DWORD fvf;
2590 /* optional mesh data */
2592 unsigned int num_normals;
2593 D3DXVECTOR3 *normals;
2594 DWORD *normal_indices;
2596 D3DXVECTOR2 *tex_coords;
2598 DWORD *vertex_colors;
2600 unsigned int num_materials;
2601 D3DXMATERIAL *materials;
2602 DWORD *material_indices;
2604 struct ID3DXSkinInfo *skin_info;
2605 unsigned int bone_count;
2608 static HRESULT parse_texture_filename(ID3DXFileData *filedata, char **filename_out)
2610 HRESULT hr;
2611 SIZE_T data_size;
2612 BYTE *data;
2613 char *filename_in;
2614 char *filename = NULL;
2616 /* template TextureFilename {
2617 * STRING filename;
2621 HeapFree(GetProcessHeap(), 0, *filename_out);
2622 *filename_out = NULL;
2624 hr = filedata->lpVtbl->Lock(filedata, &data_size, (const void**)&data);
2625 if (FAILED(hr)) return hr;
2627 /* FIXME: String must be retrieved directly instead of through a pointer once ID3DXFILE is fixed */
2628 if (data_size < sizeof(filename_in))
2630 WARN("Truncated data (%Iu bytes).\n", data_size);
2631 filedata->lpVtbl->Unlock(filedata);
2632 return E_FAIL;
2634 filename_in = *(char **)data;
2636 filename = HeapAlloc(GetProcessHeap(), 0, strlen(filename_in) + 1);
2637 if (!filename) {
2638 filedata->lpVtbl->Unlock(filedata);
2639 return E_OUTOFMEMORY;
2642 strcpy(filename, filename_in);
2643 *filename_out = filename;
2645 filedata->lpVtbl->Unlock(filedata);
2647 return D3D_OK;
2650 static HRESULT parse_material(ID3DXFileData *filedata, D3DXMATERIAL *material)
2652 HRESULT hr;
2653 SIZE_T data_size;
2654 const BYTE *data;
2655 GUID type;
2656 ID3DXFileData *child;
2657 SIZE_T i, nb_children;
2659 material->pTextureFilename = NULL;
2661 hr = filedata->lpVtbl->Lock(filedata, &data_size, (const void**)&data);
2662 if (FAILED(hr)) return hr;
2665 * template ColorRGBA {
2666 * FLOAT red;
2667 * FLOAT green;
2668 * FLOAT blue;
2669 * FLOAT alpha;
2671 * template ColorRGB {
2672 * FLOAT red;
2673 * FLOAT green;
2674 * FLOAT blue;
2676 * template Material {
2677 * ColorRGBA faceColor;
2678 * FLOAT power;
2679 * ColorRGB specularColor;
2680 * ColorRGB emissiveColor;
2681 * [ ... ]
2684 if (data_size != sizeof(float) * 11)
2686 WARN("Incorrect data size (%Id bytes).\n", data_size);
2687 filedata->lpVtbl->Unlock(filedata);
2688 return E_FAIL;
2691 memcpy(&material->MatD3D.Diffuse, data, sizeof(D3DCOLORVALUE));
2692 data += sizeof(D3DCOLORVALUE);
2693 material->MatD3D.Power = *(FLOAT*)data;
2694 data += sizeof(FLOAT);
2695 memcpy(&material->MatD3D.Specular, data, sizeof(FLOAT) * 3);
2696 material->MatD3D.Specular.a = 1.0f;
2697 data += 3 * sizeof(FLOAT);
2698 memcpy(&material->MatD3D.Emissive, data, sizeof(FLOAT) * 3);
2699 material->MatD3D.Emissive.a = 1.0f;
2700 material->MatD3D.Ambient.r = 0.0f;
2701 material->MatD3D.Ambient.g = 0.0f;
2702 material->MatD3D.Ambient.b = 0.0f;
2703 material->MatD3D.Ambient.a = 1.0f;
2705 filedata->lpVtbl->Unlock(filedata);
2707 hr = filedata->lpVtbl->GetChildren(filedata, &nb_children);
2708 if (FAILED(hr))
2709 return hr;
2711 for (i = 0; i < nb_children; i++)
2713 hr = filedata->lpVtbl->GetChild(filedata, i, &child);
2714 if (FAILED(hr))
2715 return hr;
2716 hr = child->lpVtbl->GetType(child, &type);
2717 if (FAILED(hr))
2718 goto err;
2720 if (IsEqualGUID(&type, &TID_D3DRMTextureFilename)) {
2721 hr = parse_texture_filename(child, &material->pTextureFilename);
2722 if (FAILED(hr))
2723 goto err;
2725 IUnknown_Release(child);
2727 return D3D_OK;
2729 err:
2730 IUnknown_Release(child);
2731 return hr;
2734 static void destroy_materials(struct mesh_data *mesh)
2736 unsigned int i;
2738 for (i = 0; i < mesh->num_materials; ++i)
2739 HeapFree(GetProcessHeap(), 0, mesh->materials[i].pTextureFilename);
2740 HeapFree(GetProcessHeap(), 0, mesh->materials);
2741 HeapFree(GetProcessHeap(), 0, mesh->material_indices);
2742 mesh->num_materials = 0;
2743 mesh->materials = NULL;
2744 mesh->material_indices = NULL;
2747 static HRESULT parse_material_list(ID3DXFileData *filedata, struct mesh_data *mesh)
2749 ID3DXFileData *child = NULL;
2750 unsigned int material_count;
2751 const uint32_t *in_ptr;
2752 SIZE_T nb_children;
2753 SIZE_T data_size;
2754 const void *data;
2755 unsigned int i;
2756 HRESULT hr;
2757 GUID type;
2759 destroy_materials(mesh);
2761 hr = filedata->lpVtbl->Lock(filedata, &data_size, &data);
2762 if (FAILED(hr)) return hr;
2764 /* template MeshMaterialList {
2765 * DWORD nMaterials;
2766 * DWORD nFaceIndexes;
2767 * array DWORD faceIndexes[nFaceIndexes];
2768 * [ Material ]
2772 in_ptr = data;
2773 hr = E_FAIL;
2775 if (data_size < sizeof(uint32_t))
2777 WARN("Truncated data (%Id bytes).\n", data_size);
2778 goto end;
2780 material_count = *in_ptr++;
2781 if (!material_count) {
2782 hr = D3D_OK;
2783 goto end;
2786 if (data_size < 2 * sizeof(uint32_t))
2788 WARN("Truncated data (%Id bytes).\n", data_size);
2789 goto end;
2791 if (*in_ptr++ != mesh->num_poly_faces)
2793 WARN("Number of material face indices (%u) doesn't match number of faces (%u).\n",
2794 *(in_ptr - 1), mesh->num_poly_faces);
2795 goto end;
2797 if (data_size < 2 * sizeof(uint32_t) + mesh->num_poly_faces * sizeof(uint32_t))
2799 WARN("Truncated data (%Id bytes).\n", data_size);
2800 goto end;
2802 for (i = 0; i < mesh->num_poly_faces; ++i)
2804 if (*in_ptr++ >= material_count)
2806 WARN("Face %u: reference to undefined material %u (only %u materials).\n",
2807 i, *(in_ptr - 1), material_count);
2808 goto end;
2812 mesh->materials = HeapAlloc(GetProcessHeap(), 0, material_count * sizeof(*mesh->materials));
2813 mesh->material_indices = HeapAlloc(GetProcessHeap(), 0, mesh->num_poly_faces * sizeof(*mesh->material_indices));
2814 if (!mesh->materials || !mesh->material_indices) {
2815 hr = E_OUTOFMEMORY;
2816 goto end;
2818 memcpy(mesh->material_indices, (const uint32_t *)data + 2, mesh->num_poly_faces * sizeof(uint32_t));
2820 hr = filedata->lpVtbl->GetChildren(filedata, &nb_children);
2821 if (FAILED(hr))
2822 goto end;
2824 for (i = 0; i < nb_children; i++)
2826 hr = filedata->lpVtbl->GetChild(filedata, i, &child);
2827 if (FAILED(hr))
2828 goto end;
2829 hr = child->lpVtbl->GetType(child, &type);
2830 if (FAILED(hr))
2831 goto end;
2833 if (IsEqualGUID(&type, &TID_D3DRMMaterial))
2835 if (mesh->num_materials >= material_count)
2837 WARN("%u materials defined, only %u declared.\n", mesh->num_materials, material_count);
2838 hr = E_FAIL;
2839 goto end;
2841 hr = parse_material(child, &mesh->materials[mesh->num_materials++]);
2842 if (FAILED(hr))
2843 goto end;
2846 IUnknown_Release(child);
2847 child = NULL;
2849 if (material_count != mesh->num_materials)
2851 WARN("Only %u of %u materials defined.\n", material_count, mesh->num_materials);
2852 hr = E_FAIL;
2855 end:
2856 if (child)
2857 IUnknown_Release(child);
2858 filedata->lpVtbl->Unlock(filedata);
2859 return hr;
2862 static HRESULT parse_texture_coords(ID3DXFileData *filedata, struct mesh_data *mesh)
2864 const uint32_t *data;
2865 SIZE_T data_size;
2866 HRESULT hr;
2868 HeapFree(GetProcessHeap(), 0, mesh->tex_coords);
2869 mesh->tex_coords = NULL;
2871 hr = filedata->lpVtbl->Lock(filedata, &data_size, (const void **)&data);
2872 if (FAILED(hr)) return hr;
2874 /* template Coords2d {
2875 * FLOAT u;
2876 * FLOAT v;
2878 * template MeshTextureCoords {
2879 * DWORD nTextureCoords;
2880 * array Coords2d textureCoords[nTextureCoords];
2884 hr = E_FAIL;
2886 if (data_size < sizeof(uint32_t))
2888 WARN("Truncated data (%Id bytes).\n", data_size);
2889 goto end;
2891 if (*data != mesh->num_vertices)
2893 WARN("Number of texture coordinates (%u) doesn't match number of vertices (%u).\n",
2894 *data, mesh->num_vertices);
2895 goto end;
2897 ++data;
2898 if (data_size < sizeof(uint32_t) + mesh->num_vertices * sizeof(*mesh->tex_coords))
2900 WARN("Truncated data (%Id bytes).\n", data_size);
2901 goto end;
2904 mesh->tex_coords = HeapAlloc(GetProcessHeap(), 0, mesh->num_vertices * sizeof(*mesh->tex_coords));
2905 if (!mesh->tex_coords) {
2906 hr = E_OUTOFMEMORY;
2907 goto end;
2909 memcpy(mesh->tex_coords, data, mesh->num_vertices * sizeof(*mesh->tex_coords));
2911 mesh->fvf |= D3DFVF_TEX1;
2913 hr = D3D_OK;
2915 end:
2916 filedata->lpVtbl->Unlock(filedata);
2917 return hr;
2920 static HRESULT parse_vertex_colors(ID3DXFileData *filedata, struct mesh_data *mesh)
2922 unsigned int color_count, i;
2923 const uint32_t *data;
2924 SIZE_T data_size;
2925 HRESULT hr;
2927 HeapFree(GetProcessHeap(), 0, mesh->vertex_colors);
2928 mesh->vertex_colors = NULL;
2930 hr = filedata->lpVtbl->Lock(filedata, &data_size, (const void **)&data);
2931 if (FAILED(hr)) return hr;
2933 /* template IndexedColor {
2934 * DWORD index;
2935 * ColorRGBA indexColor;
2937 * template MeshVertexColors {
2938 * DWORD nVertexColors;
2939 * array IndexedColor vertexColors[nVertexColors];
2943 hr = E_FAIL;
2945 if (data_size < sizeof(uint32_t))
2947 WARN("Truncated data (%Id bytes).\n", data_size);
2948 goto end;
2950 color_count = *data;
2951 ++data;
2952 if (data_size < sizeof(uint32_t) + color_count * (sizeof(uint32_t) + sizeof(D3DCOLORVALUE)))
2954 WARN("Truncated data (%Id bytes).\n", data_size);
2955 goto end;
2958 mesh->vertex_colors = HeapAlloc(GetProcessHeap(), 0, mesh->num_vertices * sizeof(uint32_t));
2959 if (!mesh->vertex_colors) {
2960 hr = E_OUTOFMEMORY;
2961 goto end;
2964 for (i = 0; i < mesh->num_vertices; i++)
2965 mesh->vertex_colors[i] = D3DCOLOR_ARGB(0, 0xff, 0xff, 0xff);
2966 for (i = 0; i < color_count; ++i)
2968 D3DCOLORVALUE color;
2969 unsigned int index = *data;
2971 ++data;
2972 if (index >= mesh->num_vertices)
2974 WARN("Vertex color %u references undefined vertex %u (only %u vertices).\n",
2975 i, index, mesh->num_vertices);
2976 goto end;
2978 memcpy(&color, data, sizeof(color));
2979 data += sizeof(color) / sizeof(*data);
2980 color.r = min(1.0f, max(0.0f, color.r));
2981 color.g = min(1.0f, max(0.0f, color.g));
2982 color.b = min(1.0f, max(0.0f, color.b));
2983 color.a = min(1.0f, max(0.0f, color.a));
2984 mesh->vertex_colors[index] = D3DCOLOR_ARGB((BYTE)(color.a * 255.0f + 0.5f),
2985 (BYTE)(color.r * 255.0f + 0.5f),
2986 (BYTE)(color.g * 255.0f + 0.5f),
2987 (BYTE)(color.b * 255.0f + 0.5f));
2990 mesh->fvf |= D3DFVF_DIFFUSE;
2992 hr = D3D_OK;
2994 end:
2995 filedata->lpVtbl->Unlock(filedata);
2996 return hr;
2999 static HRESULT parse_normals(ID3DXFileData *filedata, struct mesh_data *mesh)
3001 unsigned int num_face_indices = mesh->num_poly_faces * 2 + mesh->num_tri_faces;
3002 DWORD *index_out_ptr;
3003 SIZE_T data_size;
3004 const BYTE *data;
3005 unsigned int i;
3006 HRESULT hr;
3008 HeapFree(GetProcessHeap(), 0, mesh->normals);
3009 mesh->num_normals = 0;
3010 mesh->normals = NULL;
3011 mesh->normal_indices = NULL;
3012 mesh->fvf |= D3DFVF_NORMAL;
3014 hr = filedata->lpVtbl->Lock(filedata, &data_size, (const void **)&data);
3015 if (FAILED(hr)) return hr;
3017 /* template Vector {
3018 * FLOAT x;
3019 * FLOAT y;
3020 * FLOAT z;
3022 * template MeshFace {
3023 * DWORD nFaceVertexIndices;
3024 * array DWORD faceVertexIndices[nFaceVertexIndices];
3026 * template MeshNormals {
3027 * DWORD nNormals;
3028 * array Vector normals[nNormals];
3029 * DWORD nFaceNormals;
3030 * array MeshFace faceNormals[nFaceNormals];
3034 hr = E_FAIL;
3036 if (data_size < sizeof(uint32_t) * 2)
3038 WARN("Truncated data (%Id bytes).\n", data_size);
3039 goto end;
3041 mesh->num_normals = *(uint32_t *)data;
3042 data += sizeof(uint32_t);
3043 if (data_size < sizeof(uint32_t) * 2 + mesh->num_normals * sizeof(D3DXVECTOR3) +
3044 num_face_indices * sizeof(uint32_t))
3046 WARN("Truncated data (%Id bytes).\n", data_size);
3047 goto end;
3050 mesh->normals = HeapAlloc(GetProcessHeap(), 0, mesh->num_normals * sizeof(D3DXVECTOR3));
3051 mesh->normal_indices = HeapAlloc(GetProcessHeap(), 0, num_face_indices * sizeof(uint32_t));
3052 if (!mesh->normals || !mesh->normal_indices) {
3053 hr = E_OUTOFMEMORY;
3054 goto end;
3057 memcpy(mesh->normals, data, mesh->num_normals * sizeof(D3DXVECTOR3));
3058 data += mesh->num_normals * sizeof(D3DXVECTOR3);
3059 for (i = 0; i < mesh->num_normals; i++)
3060 D3DXVec3Normalize(&mesh->normals[i], &mesh->normals[i]);
3062 if (*(uint32_t *)data != mesh->num_poly_faces)
3064 WARN("Number of face normals (%u) doesn't match number of faces (%u).\n",
3065 *(uint32_t *)data, mesh->num_poly_faces);
3066 goto end;
3068 data += sizeof(uint32_t);
3069 index_out_ptr = mesh->normal_indices;
3070 for (i = 0; i < mesh->num_poly_faces; i++)
3072 unsigned int count = *(uint32_t *)data;
3073 unsigned int j;
3075 if (count != mesh->num_tri_per_face[i] + 2)
3077 WARN("Face %u: number of normals (%u) doesn't match number of vertices (%u).\n",
3078 i, count, mesh->num_tri_per_face[i] + 2);
3079 goto end;
3081 data += sizeof(uint32_t);
3083 for (j = 0; j < count; j++)
3085 uint32_t normal_index = *(uint32_t *)data;
3087 if (normal_index >= mesh->num_normals)
3089 WARN("Face %u, normal index %u: reference to undefined normal %u (only %u normals).\n",
3090 i, j, normal_index, mesh->num_normals);
3091 goto end;
3093 *index_out_ptr++ = normal_index;
3094 data += sizeof(uint32_t);
3098 hr = D3D_OK;
3100 end:
3101 filedata->lpVtbl->Unlock(filedata);
3102 return hr;
3105 static HRESULT parse_skin_mesh_header(ID3DXFileData *filedata, struct mesh_data *mesh_data)
3107 const BYTE *data;
3108 SIZE_T data_size;
3109 HRESULT hr;
3111 TRACE("filedata %p, mesh_data %p.\n", filedata, mesh_data);
3113 if (FAILED(hr = filedata->lpVtbl->Lock(filedata, &data_size, (const void **)&data)))
3114 return hr;
3116 if (data_size < sizeof(WORD) * 3)
3118 WARN("Truncated data (%Id bytes).\n", data_size);
3119 filedata->lpVtbl->Unlock(filedata);
3120 return E_FAIL;
3122 /* Skip nMaxSkinWeightsPerVertex and nMaxSkinWeightsPerFace */
3123 data += 2 * sizeof(WORD);
3124 mesh_data->bone_count = *(WORD *)data;
3125 hr = D3DXCreateSkinInfoFVF(mesh_data->num_vertices, mesh_data->fvf, mesh_data->bone_count,
3126 &mesh_data->skin_info);
3128 return hr;
3131 static HRESULT parse_skin_weights_info(ID3DXFileData *filedata, struct mesh_data *mesh_data, unsigned int index)
3133 unsigned int influence_count;
3134 const char *name;
3135 const BYTE *data;
3136 SIZE_T data_size;
3137 HRESULT hr;
3139 TRACE("filedata %p, mesh_data %p, index %u.\n", filedata, mesh_data, index);
3141 if (FAILED(hr = filedata->lpVtbl->Lock(filedata, &data_size, (const void **)&data)))
3142 return hr;
3144 /* FIXME: String will have to be retrieved directly instead of through a
3145 * pointer once our ID3DXFileData implementation is fixed. */
3146 name = *(const char **)data;
3147 data += sizeof(char *);
3149 influence_count = *(uint32_t *)data;
3150 data += sizeof(uint32_t);
3152 if (data_size < (sizeof(char *) + sizeof(uint32_t) + influence_count * (sizeof(uint32_t) + sizeof(float))
3153 + 16 * sizeof(float)))
3155 WARN("Truncated data (%Id bytes).\n", data_size);
3156 filedata->lpVtbl->Unlock(filedata);
3157 return E_FAIL;
3160 hr = mesh_data->skin_info->lpVtbl->SetBoneName(mesh_data->skin_info, index, name);
3161 if (SUCCEEDED(hr))
3162 hr = mesh_data->skin_info->lpVtbl->SetBoneInfluence(mesh_data->skin_info, index, influence_count,
3163 (const DWORD *)data, (const float *)(data + influence_count * sizeof(uint32_t)));
3164 if (SUCCEEDED(hr))
3165 hr = mesh_data->skin_info->lpVtbl->SetBoneOffsetMatrix(mesh_data->skin_info, index,
3166 (const D3DMATRIX *)(data + influence_count * (sizeof(uint32_t) + sizeof(float))));
3168 return hr;
3171 /* for provide_flags parameters */
3172 #define PROVIDE_MATERIALS 0x1
3173 #define PROVIDE_SKININFO 0x2
3174 #define PROVIDE_ADJACENCY 0x4
3176 static HRESULT parse_mesh(ID3DXFileData *filedata, struct mesh_data *mesh_data, DWORD provide_flags)
3178 unsigned int skin_weights_info_count = 0;
3179 ID3DXFileData *child = NULL;
3180 const BYTE *data, *in_ptr;
3181 DWORD *index_out_ptr;
3182 SIZE_T child_count;
3183 SIZE_T data_size;
3184 unsigned int i;
3185 HRESULT hr;
3186 GUID type;
3189 * template Mesh {
3190 * DWORD nVertices;
3191 * array Vector vertices[nVertices];
3192 * DWORD nFaces;
3193 * array MeshFace faces[nFaces];
3194 * [ ... ]
3198 hr = filedata->lpVtbl->Lock(filedata, &data_size, (const void **)&data);
3199 if (FAILED(hr)) return hr;
3201 in_ptr = data;
3202 hr = E_FAIL;
3204 if (data_size < sizeof(uint32_t) * 2)
3206 WARN("Truncated data (%Id bytes).\n", data_size);
3207 goto end;
3209 mesh_data->num_vertices = *(uint32_t *)in_ptr;
3210 if (data_size < sizeof(uint32_t) * 2 + mesh_data->num_vertices * sizeof(D3DXVECTOR3))
3212 WARN("Truncated data (%Id bytes).\n", data_size);
3213 goto end;
3215 in_ptr += sizeof(uint32_t) + mesh_data->num_vertices * sizeof(D3DXVECTOR3);
3217 mesh_data->num_poly_faces = *(uint32_t *)in_ptr;
3218 in_ptr += sizeof(uint32_t);
3220 mesh_data->num_tri_faces = 0;
3221 for (i = 0; i < mesh_data->num_poly_faces; i++)
3223 unsigned int poly_vertices_count;
3224 unsigned int j;
3226 if (data_size - (in_ptr - data) < sizeof(uint32_t))
3228 WARN("Truncated data (%Id bytes).\n", data_size);
3229 goto end;
3231 poly_vertices_count = *(uint32_t *)in_ptr;
3232 in_ptr += sizeof(uint32_t);
3233 if (data_size - (in_ptr - data) < poly_vertices_count * sizeof(uint32_t))
3235 WARN("Truncated data (%Id bytes).\n", data_size);
3236 goto end;
3238 if (poly_vertices_count < 3)
3240 WARN("Face %u has only %u vertices.\n", i, poly_vertices_count);
3241 goto end;
3243 for (j = 0; j < poly_vertices_count; j++)
3245 if (*(uint32_t *)in_ptr >= mesh_data->num_vertices)
3247 WARN("Face %u, index %u: undefined vertex %u (only %u vertices).\n",
3248 i, j, *(uint32_t *)in_ptr, mesh_data->num_vertices);
3249 goto end;
3251 in_ptr += sizeof(uint32_t);
3253 mesh_data->num_tri_faces += poly_vertices_count - 2;
3256 mesh_data->fvf = D3DFVF_XYZ;
3258 mesh_data->vertices = HeapAlloc(GetProcessHeap(), 0,
3259 mesh_data->num_vertices * sizeof(*mesh_data->vertices));
3260 mesh_data->num_tri_per_face = HeapAlloc(GetProcessHeap(), 0,
3261 mesh_data->num_poly_faces * sizeof(*mesh_data->num_tri_per_face));
3262 mesh_data->indices = HeapAlloc(GetProcessHeap(), 0,
3263 (mesh_data->num_tri_faces + mesh_data->num_poly_faces * 2) * sizeof(*mesh_data->indices));
3264 if (!mesh_data->vertices || !mesh_data->num_tri_per_face || !mesh_data->indices) {
3265 hr = E_OUTOFMEMORY;
3266 goto end;
3269 in_ptr = data + sizeof(uint32_t);
3270 memcpy(mesh_data->vertices, in_ptr, mesh_data->num_vertices * sizeof(D3DXVECTOR3));
3271 in_ptr += mesh_data->num_vertices * sizeof(D3DXVECTOR3) + sizeof(uint32_t);
3273 index_out_ptr = mesh_data->indices;
3274 for (i = 0; i < mesh_data->num_poly_faces; i++)
3276 unsigned int count;
3278 count = *(uint32_t *)in_ptr;
3279 in_ptr += sizeof(uint32_t);
3280 mesh_data->num_tri_per_face[i] = count - 2;
3282 while (count--)
3284 *index_out_ptr++ = *(uint32_t *)in_ptr;
3285 in_ptr += sizeof(uint32_t);
3289 hr = filedata->lpVtbl->GetChildren(filedata, &child_count);
3290 if (FAILED(hr))
3291 goto end;
3293 for (i = 0; i < child_count; i++)
3295 hr = filedata->lpVtbl->GetChild(filedata, i, &child);
3296 if (FAILED(hr))
3297 goto end;
3298 hr = child->lpVtbl->GetType(child, &type);
3299 if (FAILED(hr))
3300 goto end;
3302 if (IsEqualGUID(&type, &TID_D3DRMMeshNormals)) {
3303 hr = parse_normals(child, mesh_data);
3304 } else if (IsEqualGUID(&type, &TID_D3DRMMeshVertexColors)) {
3305 hr = parse_vertex_colors(child, mesh_data);
3306 } else if (IsEqualGUID(&type, &TID_D3DRMMeshTextureCoords)) {
3307 hr = parse_texture_coords(child, mesh_data);
3308 } else if (IsEqualGUID(&type, &TID_D3DRMMeshMaterialList) &&
3309 (provide_flags & PROVIDE_MATERIALS))
3311 hr = parse_material_list(child, mesh_data);
3312 } else if (provide_flags & PROVIDE_SKININFO) {
3313 if (IsEqualGUID(&type, &DXFILEOBJ_XSkinMeshHeader)) {
3314 if (mesh_data->skin_info) {
3315 WARN("Skin mesh header already encountered\n");
3316 hr = E_FAIL;
3317 goto end;
3319 hr = parse_skin_mesh_header(child, mesh_data);
3320 if (FAILED(hr))
3321 goto end;
3322 } else if (IsEqualGUID(&type, &DXFILEOBJ_SkinWeights)) {
3323 if (!mesh_data->skin_info) {
3324 WARN("Skin weights found but skin mesh header not encountered yet\n");
3325 hr = E_FAIL;
3326 goto end;
3328 hr = parse_skin_weights_info(child, mesh_data, skin_weights_info_count);
3329 if (FAILED(hr))
3330 goto end;
3331 skin_weights_info_count++;
3334 if (FAILED(hr))
3335 goto end;
3337 IUnknown_Release(child);
3338 child = NULL;
3341 if (mesh_data->skin_info && (skin_weights_info_count != mesh_data->bone_count))
3343 WARN("Mismatch between skin weights info count %u and bones count %u from skin mesh header.\n",
3344 skin_weights_info_count, mesh_data->bone_count);
3345 hr = E_FAIL;
3346 goto end;
3349 if ((provide_flags & PROVIDE_SKININFO) && !mesh_data->skin_info)
3351 if (FAILED(hr = D3DXCreateSkinInfoFVF(mesh_data->num_vertices, mesh_data->fvf,
3352 mesh_data->bone_count, &mesh_data->skin_info)))
3353 goto end;
3356 hr = D3D_OK;
3358 end:
3359 if (child)
3360 IUnknown_Release(child);
3361 filedata->lpVtbl->Unlock(filedata);
3362 return hr;
3365 static HRESULT generate_effects(ID3DXBuffer *materials, DWORD num_materials,
3366 ID3DXBuffer **effects)
3368 HRESULT hr;
3369 D3DXEFFECTINSTANCE *effect_ptr;
3370 BYTE *out_ptr;
3371 const D3DXMATERIAL *material_ptr = ID3DXBuffer_GetBufferPointer(materials);
3372 static const struct {
3373 const char *param_name;
3374 DWORD name_size;
3375 DWORD num_bytes;
3376 DWORD value_offset;
3377 } material_effects[] = {
3378 #define EFFECT_TABLE_ENTRY(str, field) \
3379 {str, sizeof(str), sizeof(material_ptr->MatD3D.field), offsetof(D3DXMATERIAL, MatD3D.field)}
3380 EFFECT_TABLE_ENTRY("Diffuse", Diffuse),
3381 EFFECT_TABLE_ENTRY("Power", Power),
3382 EFFECT_TABLE_ENTRY("Specular", Specular),
3383 EFFECT_TABLE_ENTRY("Emissive", Emissive),
3384 EFFECT_TABLE_ENTRY("Ambient", Ambient),
3385 #undef EFFECT_TABLE_ENTRY
3387 static const char texture_paramname[] = "Texture0@Name";
3388 DWORD buffer_size;
3389 DWORD i;
3391 /* effects buffer layout:
3393 * D3DXEFFECTINSTANCE effects[num_materials];
3394 * for (effect in effects)
3396 * D3DXEFFECTDEFAULT defaults[effect.NumDefaults];
3397 * for (default in defaults)
3399 * *default.pParamName;
3400 * *default.pValue;
3404 buffer_size = sizeof(D3DXEFFECTINSTANCE);
3405 buffer_size += sizeof(D3DXEFFECTDEFAULT) * ARRAY_SIZE(material_effects);
3406 for (i = 0; i < ARRAY_SIZE(material_effects); i++) {
3407 buffer_size += material_effects[i].name_size;
3408 buffer_size += material_effects[i].num_bytes;
3410 buffer_size *= num_materials;
3411 for (i = 0; i < num_materials; i++) {
3412 if (material_ptr[i].pTextureFilename) {
3413 buffer_size += sizeof(D3DXEFFECTDEFAULT);
3414 buffer_size += sizeof(texture_paramname);
3415 buffer_size += strlen(material_ptr[i].pTextureFilename) + 1;
3419 hr = D3DXCreateBuffer(buffer_size, effects);
3420 if (FAILED(hr)) return hr;
3421 effect_ptr = ID3DXBuffer_GetBufferPointer(*effects);
3422 out_ptr = (BYTE*)(effect_ptr + num_materials);
3424 for (i = 0; i < num_materials; i++)
3426 DWORD j;
3427 D3DXEFFECTDEFAULT *defaults = (D3DXEFFECTDEFAULT*)out_ptr;
3429 effect_ptr->pDefaults = defaults;
3430 effect_ptr->NumDefaults = material_ptr->pTextureFilename ? 6 : 5;
3431 out_ptr = (BYTE*)(effect_ptr->pDefaults + effect_ptr->NumDefaults);
3433 for (j = 0; j < ARRAY_SIZE(material_effects); j++)
3435 defaults->pParamName = (char *)out_ptr;
3436 strcpy(defaults->pParamName, material_effects[j].param_name);
3437 defaults->pValue = defaults->pParamName + material_effects[j].name_size;
3438 defaults->Type = D3DXEDT_FLOATS;
3439 defaults->NumBytes = material_effects[j].num_bytes;
3440 memcpy(defaults->pValue, (BYTE*)material_ptr + material_effects[j].value_offset, defaults->NumBytes);
3441 out_ptr = (BYTE*)defaults->pValue + defaults->NumBytes;
3442 defaults++;
3445 if (material_ptr->pTextureFilename)
3447 defaults->pParamName = (char *)out_ptr;
3448 strcpy(defaults->pParamName, texture_paramname);
3449 defaults->pValue = defaults->pParamName + sizeof(texture_paramname);
3450 defaults->Type = D3DXEDT_STRING;
3451 defaults->NumBytes = strlen(material_ptr->pTextureFilename) + 1;
3452 strcpy(defaults->pValue, material_ptr->pTextureFilename);
3453 out_ptr = (BYTE*)defaults->pValue + defaults->NumBytes;
3455 material_ptr++;
3456 effect_ptr++;
3458 assert(out_ptr - (BYTE*)ID3DXBuffer_GetBufferPointer(*effects) == buffer_size);
3460 return D3D_OK;
3463 HRESULT WINAPI D3DXLoadSkinMeshFromXof(struct ID3DXFileData *filedata, DWORD options,
3464 struct IDirect3DDevice9 *device, struct ID3DXBuffer **adjacency_out, struct ID3DXBuffer **materials_out,
3465 struct ID3DXBuffer **effects_out, DWORD *num_materials_out, struct ID3DXSkinInfo **skin_info_out,
3466 struct ID3DXMesh **mesh_out)
3468 HRESULT hr;
3469 DWORD *index_in_ptr;
3470 struct mesh_data mesh_data;
3471 DWORD total_vertices;
3472 ID3DXMesh *d3dxmesh = NULL;
3473 ID3DXBuffer *adjacency = NULL;
3474 ID3DXBuffer *materials = NULL;
3475 ID3DXBuffer *effects = NULL;
3476 struct vertex_duplication {
3477 DWORD normal_index;
3478 struct list entry;
3479 } *duplications = NULL;
3480 DWORD i;
3481 void *vertices = NULL;
3482 void *indices = NULL;
3483 BYTE *out_ptr;
3484 DWORD provide_flags = 0;
3486 TRACE("filedata %p, options %#lx, device %p, adjacency_out %p, materials_out %p, "
3487 "effects_out %p, num_materials_out %p, skin_info_out %p, mesh_out %p.\n",
3488 filedata, options, device, adjacency_out, materials_out,
3489 effects_out, num_materials_out, skin_info_out, mesh_out);
3491 ZeroMemory(&mesh_data, sizeof(mesh_data));
3493 if (num_materials_out || materials_out || effects_out)
3494 provide_flags |= PROVIDE_MATERIALS;
3495 if (skin_info_out)
3496 provide_flags |= PROVIDE_SKININFO;
3498 hr = parse_mesh(filedata, &mesh_data, provide_flags);
3499 if (FAILED(hr)) goto cleanup;
3501 total_vertices = mesh_data.num_vertices;
3502 if (mesh_data.fvf & D3DFVF_NORMAL) {
3503 /* duplicate vertices with multiple normals */
3504 DWORD num_face_indices = mesh_data.num_poly_faces * 2 + mesh_data.num_tri_faces;
3505 duplications = HeapAlloc(GetProcessHeap(), 0, (mesh_data.num_vertices + num_face_indices) * sizeof(*duplications));
3506 if (!duplications) {
3507 hr = E_OUTOFMEMORY;
3508 goto cleanup;
3510 for (i = 0; i < total_vertices; i++)
3512 duplications[i].normal_index = -1;
3513 list_init(&duplications[i].entry);
3515 for (i = 0; i < num_face_indices; i++) {
3516 DWORD vertex_index = mesh_data.indices[i];
3517 DWORD normal_index = mesh_data.normal_indices[i];
3518 struct vertex_duplication *dup_ptr = &duplications[vertex_index];
3520 if (dup_ptr->normal_index == -1) {
3521 dup_ptr->normal_index = normal_index;
3522 } else {
3523 D3DXVECTOR3 *new_normal = &mesh_data.normals[normal_index];
3524 struct list *dup_list = &dup_ptr->entry;
3525 while (TRUE) {
3526 D3DXVECTOR3 *cur_normal = &mesh_data.normals[dup_ptr->normal_index];
3527 if (new_normal->x == cur_normal->x &&
3528 new_normal->y == cur_normal->y &&
3529 new_normal->z == cur_normal->z)
3531 mesh_data.indices[i] = dup_ptr - duplications;
3532 break;
3533 } else if (!list_next(dup_list, &dup_ptr->entry)) {
3534 dup_ptr = &duplications[total_vertices++];
3535 dup_ptr->normal_index = normal_index;
3536 list_add_tail(dup_list, &dup_ptr->entry);
3537 mesh_data.indices[i] = dup_ptr - duplications;
3538 break;
3539 } else {
3540 dup_ptr = LIST_ENTRY(list_next(dup_list, &dup_ptr->entry),
3541 struct vertex_duplication, entry);
3548 hr = D3DXCreateMeshFVF(mesh_data.num_tri_faces, total_vertices, options, mesh_data.fvf, device, &d3dxmesh);
3549 if (FAILED(hr)) goto cleanup;
3551 hr = d3dxmesh->lpVtbl->LockVertexBuffer(d3dxmesh, 0, &vertices);
3552 if (FAILED(hr)) goto cleanup;
3554 out_ptr = vertices;
3555 for (i = 0; i < mesh_data.num_vertices; i++) {
3556 *(D3DXVECTOR3*)out_ptr = mesh_data.vertices[i];
3557 out_ptr += sizeof(D3DXVECTOR3);
3558 if (mesh_data.fvf & D3DFVF_NORMAL) {
3559 if (duplications[i].normal_index == -1)
3560 ZeroMemory(out_ptr, sizeof(D3DXVECTOR3));
3561 else
3562 *(D3DXVECTOR3*)out_ptr = mesh_data.normals[duplications[i].normal_index];
3563 out_ptr += sizeof(D3DXVECTOR3);
3565 if (mesh_data.fvf & D3DFVF_DIFFUSE) {
3566 *(DWORD*)out_ptr = mesh_data.vertex_colors[i];
3567 out_ptr += sizeof(DWORD);
3569 if (mesh_data.fvf & D3DFVF_TEX1) {
3570 *(D3DXVECTOR2*)out_ptr = mesh_data.tex_coords[i];
3571 out_ptr += sizeof(D3DXVECTOR2);
3574 if (mesh_data.fvf & D3DFVF_NORMAL) {
3575 DWORD vertex_size = D3DXGetFVFVertexSize(mesh_data.fvf);
3576 out_ptr = vertices;
3577 for (i = 0; i < mesh_data.num_vertices; i++) {
3578 struct vertex_duplication *dup_ptr;
3579 LIST_FOR_EACH_ENTRY(dup_ptr, &duplications[i].entry, struct vertex_duplication, entry)
3581 int j = dup_ptr - duplications;
3582 BYTE *dest_vertex = (BYTE*)vertices + j * vertex_size;
3584 memcpy(dest_vertex, out_ptr, vertex_size);
3585 dest_vertex += sizeof(D3DXVECTOR3);
3586 *(D3DXVECTOR3*)dest_vertex = mesh_data.normals[dup_ptr->normal_index];
3588 out_ptr += vertex_size;
3591 d3dxmesh->lpVtbl->UnlockVertexBuffer(d3dxmesh);
3593 hr = d3dxmesh->lpVtbl->LockIndexBuffer(d3dxmesh, 0, &indices);
3594 if (FAILED(hr)) goto cleanup;
3596 index_in_ptr = mesh_data.indices;
3597 #define FILL_INDEX_BUFFER(indices_var) \
3598 for (i = 0; i < mesh_data.num_poly_faces; i++) \
3600 DWORD count = mesh_data.num_tri_per_face[i]; \
3601 WORD first_index = *index_in_ptr++; \
3602 while (count--) { \
3603 *indices_var++ = first_index; \
3604 *indices_var++ = *index_in_ptr; \
3605 index_in_ptr++; \
3606 *indices_var++ = *index_in_ptr; \
3608 index_in_ptr++; \
3610 if (options & D3DXMESH_32BIT) {
3611 DWORD *dword_indices = indices;
3612 FILL_INDEX_BUFFER(dword_indices)
3613 } else {
3614 WORD *word_indices = indices;
3615 FILL_INDEX_BUFFER(word_indices)
3617 #undef FILL_INDEX_BUFFER
3618 d3dxmesh->lpVtbl->UnlockIndexBuffer(d3dxmesh);
3620 if (mesh_data.material_indices) {
3621 DWORD *attrib_buffer = NULL;
3622 hr = d3dxmesh->lpVtbl->LockAttributeBuffer(d3dxmesh, 0, &attrib_buffer);
3623 if (FAILED(hr)) goto cleanup;
3624 for (i = 0; i < mesh_data.num_poly_faces; i++)
3626 DWORD count = mesh_data.num_tri_per_face[i];
3627 while (count--)
3628 *attrib_buffer++ = mesh_data.material_indices[i];
3630 d3dxmesh->lpVtbl->UnlockAttributeBuffer(d3dxmesh);
3632 hr = d3dxmesh->lpVtbl->OptimizeInplace(d3dxmesh,
3633 D3DXMESHOPT_ATTRSORT | D3DXMESHOPT_IGNOREVERTS | D3DXMESHOPT_DONOTSPLIT,
3634 NULL, NULL, NULL, NULL);
3635 if (FAILED(hr)) goto cleanup;
3638 if (mesh_data.num_materials && (materials_out || effects_out)) {
3639 DWORD buffer_size = mesh_data.num_materials * sizeof(D3DXMATERIAL);
3640 char *strings_out_ptr;
3641 D3DXMATERIAL *materials_ptr;
3643 for (i = 0; i < mesh_data.num_materials; i++) {
3644 if (mesh_data.materials[i].pTextureFilename)
3645 buffer_size += strlen(mesh_data.materials[i].pTextureFilename) + 1;
3648 hr = D3DXCreateBuffer(buffer_size, &materials);
3649 if (FAILED(hr)) goto cleanup;
3651 materials_ptr = ID3DXBuffer_GetBufferPointer(materials);
3652 memcpy(materials_ptr, mesh_data.materials, mesh_data.num_materials * sizeof(D3DXMATERIAL));
3653 strings_out_ptr = (char*)(materials_ptr + mesh_data.num_materials);
3654 for (i = 0; i < mesh_data.num_materials; i++) {
3655 if (materials_ptr[i].pTextureFilename) {
3656 strcpy(strings_out_ptr, mesh_data.materials[i].pTextureFilename);
3657 materials_ptr[i].pTextureFilename = strings_out_ptr;
3658 strings_out_ptr += strlen(mesh_data.materials[i].pTextureFilename) + 1;
3663 if (mesh_data.num_materials && effects_out) {
3664 hr = generate_effects(materials, mesh_data.num_materials, &effects);
3665 if (FAILED(hr)) goto cleanup;
3667 if (!materials_out) {
3668 ID3DXBuffer_Release(materials);
3669 materials = NULL;
3673 if (adjacency_out) {
3674 hr = D3DXCreateBuffer(mesh_data.num_tri_faces * 3 * sizeof(DWORD), &adjacency);
3675 if (FAILED(hr)) goto cleanup;
3676 hr = d3dxmesh->lpVtbl->GenerateAdjacency(d3dxmesh, 0.0f, ID3DXBuffer_GetBufferPointer(adjacency));
3677 if (FAILED(hr)) goto cleanup;
3680 *mesh_out = d3dxmesh;
3681 if (adjacency_out) *adjacency_out = adjacency;
3682 if (num_materials_out) *num_materials_out = mesh_data.num_materials;
3683 if (materials_out) *materials_out = materials;
3684 if (effects_out) *effects_out = effects;
3685 if (skin_info_out) *skin_info_out = mesh_data.skin_info;
3687 hr = D3D_OK;
3688 cleanup:
3689 if (FAILED(hr)) {
3690 if (d3dxmesh) IUnknown_Release(d3dxmesh);
3691 if (adjacency) ID3DXBuffer_Release(adjacency);
3692 if (materials) ID3DXBuffer_Release(materials);
3693 if (effects) ID3DXBuffer_Release(effects);
3694 if (mesh_data.skin_info) mesh_data.skin_info->lpVtbl->Release(mesh_data.skin_info);
3695 if (skin_info_out) *skin_info_out = NULL;
3697 HeapFree(GetProcessHeap(), 0, mesh_data.vertices);
3698 HeapFree(GetProcessHeap(), 0, mesh_data.num_tri_per_face);
3699 HeapFree(GetProcessHeap(), 0, mesh_data.indices);
3700 HeapFree(GetProcessHeap(), 0, mesh_data.normals);
3701 HeapFree(GetProcessHeap(), 0, mesh_data.normal_indices);
3702 destroy_materials(&mesh_data);
3703 HeapFree(GetProcessHeap(), 0, mesh_data.tex_coords);
3704 HeapFree(GetProcessHeap(), 0, mesh_data.vertex_colors);
3705 HeapFree(GetProcessHeap(), 0, duplications);
3706 return hr;
3709 HRESULT WINAPI D3DXLoadMeshHierarchyFromXA(const char *filename, DWORD options, struct IDirect3DDevice9 *device,
3710 struct ID3DXAllocateHierarchy *alloc_hier, struct ID3DXLoadUserData *load_user_data,
3711 D3DXFRAME **frame_hierarchy, struct ID3DXAnimationController **anim_controller)
3713 WCHAR *filenameW;
3714 HRESULT hr;
3715 int len;
3717 TRACE("filename %s, options %#lx, device %p, alloc_hier %p, "
3718 "load_user_data %p, frame_hierarchy %p, anim_controller %p.\n",
3719 debugstr_a(filename), options, device, alloc_hier,
3720 load_user_data, frame_hierarchy, anim_controller);
3722 if (!filename)
3723 return D3DERR_INVALIDCALL;
3725 len = MultiByteToWideChar(CP_ACP, 0, filename, -1, NULL, 0);
3726 filenameW = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
3727 if (!filenameW) return E_OUTOFMEMORY;
3728 MultiByteToWideChar(CP_ACP, 0, filename, -1, filenameW, len);
3730 hr = D3DXLoadMeshHierarchyFromXW(filenameW, options, device,
3731 alloc_hier, load_user_data, frame_hierarchy, anim_controller);
3732 HeapFree(GetProcessHeap(), 0, filenameW);
3734 return hr;
3737 HRESULT WINAPI D3DXLoadMeshHierarchyFromXW(const WCHAR *filename, DWORD options, struct IDirect3DDevice9 *device,
3738 struct ID3DXAllocateHierarchy *alloc_hier, struct ID3DXLoadUserData *load_user_data,
3739 D3DXFRAME **frame_hierarchy, struct ID3DXAnimationController **anim_controller)
3741 void *buffer;
3742 HRESULT hr;
3743 DWORD size;
3745 TRACE("filename %s, options %#lx, device %p, alloc_hier %p, "
3746 "load_user_data %p, frame_hierarchy %p, anim_controller %p.\n",
3747 debugstr_w(filename), options, device, alloc_hier,
3748 load_user_data, frame_hierarchy, anim_controller);
3750 if (!filename)
3751 return D3DERR_INVALIDCALL;
3753 hr = map_view_of_file(filename, &buffer, &size);
3754 if (FAILED(hr))
3755 return D3DXERR_INVALIDDATA;
3757 hr = D3DXLoadMeshHierarchyFromXInMemory(buffer, size, options, device,
3758 alloc_hier, load_user_data, frame_hierarchy, anim_controller);
3760 UnmapViewOfFile(buffer);
3762 return hr;
3765 static HRESULT filedata_get_name(ID3DXFileData *filedata, char **name)
3767 HRESULT hr;
3768 SIZE_T name_len;
3770 hr = filedata->lpVtbl->GetName(filedata, NULL, &name_len);
3771 if (FAILED(hr)) return hr;
3773 if (!name_len)
3774 name_len++;
3775 *name = HeapAlloc(GetProcessHeap(), 0, name_len);
3776 if (!*name) return E_OUTOFMEMORY;
3778 hr = filedata->lpVtbl->GetName(filedata, *name, &name_len);
3779 if (FAILED(hr))
3780 HeapFree(GetProcessHeap(), 0, *name);
3781 else if (!name_len)
3782 (*name)[0] = 0;
3784 return hr;
3787 static HRESULT load_mesh_container(struct ID3DXFileData *filedata, DWORD options, struct IDirect3DDevice9 *device,
3788 struct ID3DXAllocateHierarchy *alloc_hier, D3DXMESHCONTAINER **mesh_container)
3790 HRESULT hr;
3791 ID3DXBuffer *adjacency = NULL;
3792 ID3DXBuffer *materials = NULL;
3793 ID3DXBuffer *effects = NULL;
3794 ID3DXSkinInfo *skin_info = NULL;
3795 D3DXMESHDATA mesh_data;
3796 DWORD num_materials = 0;
3797 char *name = NULL;
3799 mesh_data.Type = D3DXMESHTYPE_MESH;
3800 mesh_data.pMesh = NULL;
3802 hr = D3DXLoadSkinMeshFromXof(filedata, options, device,
3803 &adjacency, &materials, &effects, &num_materials,
3804 &skin_info, &mesh_data.pMesh);
3805 if (FAILED(hr)) return hr;
3807 hr = filedata_get_name(filedata, &name);
3808 if (FAILED(hr)) goto cleanup;
3810 hr = alloc_hier->lpVtbl->CreateMeshContainer(alloc_hier, name, &mesh_data,
3811 materials ? ID3DXBuffer_GetBufferPointer(materials) : NULL,
3812 effects ? ID3DXBuffer_GetBufferPointer(effects) : NULL,
3813 num_materials,
3814 adjacency ? ID3DXBuffer_GetBufferPointer(adjacency) : NULL,
3815 skin_info, mesh_container);
3817 cleanup:
3818 if (materials) ID3DXBuffer_Release(materials);
3819 if (effects) ID3DXBuffer_Release(effects);
3820 if (adjacency) ID3DXBuffer_Release(adjacency);
3821 if (skin_info) IUnknown_Release(skin_info);
3822 if (mesh_data.pMesh) IUnknown_Release(mesh_data.pMesh);
3823 HeapFree(GetProcessHeap(), 0, name);
3824 return hr;
3827 static HRESULT parse_transform_matrix(ID3DXFileData *filedata, D3DXMATRIX *transform)
3829 SIZE_T data_size;
3830 const BYTE *data;
3831 HRESULT hr;
3833 /* template Matrix4x4 {
3834 * array FLOAT matrix[16];
3836 * template FrameTransformMatrix {
3837 * Matrix4x4 frameMatrix;
3841 hr = filedata->lpVtbl->Lock(filedata, &data_size, (const void **)&data);
3842 if (FAILED(hr))
3843 return hr;
3845 if (data_size != sizeof(D3DXMATRIX))
3847 WARN("Incorrect data size (%Id bytes).\n", data_size);
3848 filedata->lpVtbl->Unlock(filedata);
3849 return E_FAIL;
3852 memcpy(transform, data, sizeof(D3DXMATRIX));
3854 filedata->lpVtbl->Unlock(filedata);
3855 return D3D_OK;
3858 static HRESULT load_frame(struct ID3DXFileData *filedata, DWORD options, struct IDirect3DDevice9 *device,
3859 struct ID3DXAllocateHierarchy *alloc_hier, D3DXFRAME **frame_out)
3861 HRESULT hr;
3862 GUID type;
3863 ID3DXFileData *child;
3864 char *name = NULL;
3865 D3DXFRAME *frame = NULL;
3866 D3DXMESHCONTAINER **next_container;
3867 D3DXFRAME **next_child;
3868 SIZE_T i, nb_children;
3870 hr = filedata_get_name(filedata, &name);
3871 if (FAILED(hr)) return hr;
3873 hr = alloc_hier->lpVtbl->CreateFrame(alloc_hier, name, frame_out);
3874 HeapFree(GetProcessHeap(), 0, name);
3875 if (FAILED(hr)) return E_FAIL;
3877 frame = *frame_out;
3878 D3DXMatrixIdentity(&frame->TransformationMatrix);
3879 next_child = &frame->pFrameFirstChild;
3880 next_container = &frame->pMeshContainer;
3882 hr = filedata->lpVtbl->GetChildren(filedata, &nb_children);
3883 if (FAILED(hr))
3884 return hr;
3886 for (i = 0; i < nb_children; i++)
3888 hr = filedata->lpVtbl->GetChild(filedata, i, &child);
3889 if (FAILED(hr))
3890 return hr;
3891 hr = child->lpVtbl->GetType(child, &type);
3892 if (FAILED(hr))
3893 goto err;
3895 if (IsEqualGUID(&type, &TID_D3DRMMesh)) {
3896 hr = load_mesh_container(child, options, device, alloc_hier, next_container);
3897 if (SUCCEEDED(hr))
3898 next_container = &(*next_container)->pNextMeshContainer;
3899 } else if (IsEqualGUID(&type, &TID_D3DRMFrameTransformMatrix)) {
3900 hr = parse_transform_matrix(child, &frame->TransformationMatrix);
3901 } else if (IsEqualGUID(&type, &TID_D3DRMFrame)) {
3902 hr = load_frame(child, options, device, alloc_hier, next_child);
3903 if (SUCCEEDED(hr))
3904 next_child = &(*next_child)->pFrameSibling;
3906 if (FAILED(hr))
3907 goto err;
3909 IUnknown_Release(child);
3911 return D3D_OK;
3913 err:
3914 IUnknown_Release(child);
3915 return hr;
3918 HRESULT WINAPI D3DXLoadMeshHierarchyFromXInMemory(const void *memory, DWORD memory_size, DWORD options,
3919 struct IDirect3DDevice9 *device, struct ID3DXAllocateHierarchy *alloc_hier,
3920 struct ID3DXLoadUserData *load_user_data, D3DXFRAME **frame_hierarchy,
3921 struct ID3DXAnimationController **anim_controller)
3923 HRESULT hr;
3924 ID3DXFile *d3dxfile = NULL;
3925 ID3DXFileEnumObject *enumobj = NULL;
3926 ID3DXFileData *filedata = NULL;
3927 D3DXF_FILELOADMEMORY source;
3928 D3DXFRAME *first_frame = NULL;
3929 D3DXFRAME **next_frame = &first_frame;
3930 SIZE_T i, nb_children;
3931 GUID guid;
3933 TRACE("memory %p, memory_size %lu, options %#lx, device %p, alloc_hier %p, load_user_data %p, "
3934 "frame_hierarchy %p, anim_controller %p.\n", memory, memory_size, options,
3935 device, alloc_hier, load_user_data, frame_hierarchy, anim_controller);
3937 if (!memory || !memory_size || !device || !frame_hierarchy || !alloc_hier)
3938 return D3DERR_INVALIDCALL;
3939 if (load_user_data)
3941 FIXME("Loading user data not implemented.\n");
3942 return E_NOTIMPL;
3945 hr = D3DXFileCreate(&d3dxfile);
3946 if (FAILED(hr)) goto cleanup;
3948 hr = d3dxfile->lpVtbl->RegisterTemplates(d3dxfile, D3DRM_XTEMPLATES, D3DRM_XTEMPLATE_BYTES);
3949 if (FAILED(hr)) goto cleanup;
3951 source.lpMemory = (void*)memory;
3952 source.dSize = memory_size;
3953 hr = d3dxfile->lpVtbl->CreateEnumObject(d3dxfile, &source, D3DXF_FILELOAD_FROMMEMORY, &enumobj);
3954 if (FAILED(hr)) goto cleanup;
3956 hr = enumobj->lpVtbl->GetChildren(enumobj, &nb_children);
3957 if (FAILED(hr))
3958 goto cleanup;
3960 for (i = 0; i < nb_children; i++)
3962 hr = enumobj->lpVtbl->GetChild(enumobj, i, &filedata);
3963 if (FAILED(hr))
3964 goto cleanup;
3966 hr = filedata->lpVtbl->GetType(filedata, &guid);
3967 if (SUCCEEDED(hr)) {
3968 if (IsEqualGUID(&guid, &TID_D3DRMMesh)) {
3969 hr = alloc_hier->lpVtbl->CreateFrame(alloc_hier, NULL, next_frame);
3970 if (FAILED(hr)) {
3971 hr = E_FAIL;
3972 goto cleanup;
3975 D3DXMatrixIdentity(&(*next_frame)->TransformationMatrix);
3977 hr = load_mesh_container(filedata, options, device, alloc_hier, &(*next_frame)->pMeshContainer);
3978 if (FAILED(hr)) goto cleanup;
3979 } else if (IsEqualGUID(&guid, &TID_D3DRMFrame)) {
3980 hr = load_frame(filedata, options, device, alloc_hier, next_frame);
3981 if (FAILED(hr)) goto cleanup;
3983 while (*next_frame)
3984 next_frame = &(*next_frame)->pFrameSibling;
3987 filedata->lpVtbl->Release(filedata);
3988 filedata = NULL;
3989 if (FAILED(hr))
3990 goto cleanup;
3993 if (!first_frame) {
3994 hr = E_FAIL;
3995 } else if (first_frame->pFrameSibling) {
3996 D3DXFRAME *root_frame = NULL;
3997 hr = alloc_hier->lpVtbl->CreateFrame(alloc_hier, NULL, &root_frame);
3998 if (FAILED(hr)) {
3999 hr = E_FAIL;
4000 goto cleanup;
4002 D3DXMatrixIdentity(&root_frame->TransformationMatrix);
4003 root_frame->pFrameFirstChild = first_frame;
4004 *frame_hierarchy = root_frame;
4005 hr = D3D_OK;
4006 } else {
4007 *frame_hierarchy = first_frame;
4008 hr = D3D_OK;
4011 if (anim_controller)
4013 *anim_controller = NULL;
4014 FIXME("Animation controller creation not implemented.\n");
4017 cleanup:
4018 if (FAILED(hr) && first_frame) D3DXFrameDestroy(first_frame, alloc_hier);
4019 if (filedata) filedata->lpVtbl->Release(filedata);
4020 if (enumobj) enumobj->lpVtbl->Release(enumobj);
4021 if (d3dxfile) d3dxfile->lpVtbl->Release(d3dxfile);
4022 return hr;
4025 HRESULT WINAPI D3DXCleanMesh(D3DXCLEANTYPE clean_type, ID3DXMesh *mesh_in, const DWORD *adjacency_in,
4026 ID3DXMesh **mesh_out, DWORD *adjacency_out, ID3DXBuffer **errors_and_warnings)
4028 FIXME("(%u, %p, %p, %p, %p, %p)\n", clean_type, mesh_in, adjacency_in, mesh_out, adjacency_out, errors_and_warnings);
4030 return E_NOTIMPL;
4033 HRESULT WINAPI D3DXFrameDestroy(D3DXFRAME *frame, ID3DXAllocateHierarchy *alloc_hier)
4035 HRESULT hr;
4036 BOOL last = FALSE;
4038 TRACE("(%p, %p)\n", frame, alloc_hier);
4040 if (!frame || !alloc_hier)
4041 return D3DERR_INVALIDCALL;
4043 while (!last) {
4044 D3DXMESHCONTAINER *container;
4045 D3DXFRAME *current_frame;
4047 if (frame->pFrameSibling) {
4048 current_frame = frame->pFrameSibling;
4049 frame->pFrameSibling = current_frame->pFrameSibling;
4050 current_frame->pFrameSibling = NULL;
4051 } else {
4052 current_frame = frame;
4053 last = TRUE;
4056 if (current_frame->pFrameFirstChild) {
4057 hr = D3DXFrameDestroy(current_frame->pFrameFirstChild, alloc_hier);
4058 if (FAILED(hr)) return hr;
4059 current_frame->pFrameFirstChild = NULL;
4062 container = current_frame->pMeshContainer;
4063 while (container) {
4064 D3DXMESHCONTAINER *next_container = container->pNextMeshContainer;
4065 hr = alloc_hier->lpVtbl->DestroyMeshContainer(alloc_hier, container);
4066 if (FAILED(hr)) return hr;
4067 container = next_container;
4069 hr = alloc_hier->lpVtbl->DestroyFrame(alloc_hier, current_frame);
4070 if (FAILED(hr)) return hr;
4072 return D3D_OK;
4075 HRESULT WINAPI D3DXLoadMeshFromXA(const char *filename, DWORD options, struct IDirect3DDevice9 *device,
4076 struct ID3DXBuffer **adjacency, struct ID3DXBuffer **materials, struct ID3DXBuffer **effect_instances,
4077 DWORD *num_materials, struct ID3DXMesh **mesh)
4079 WCHAR *filenameW;
4080 HRESULT hr;
4081 int len;
4083 TRACE("filename %s, options %#lx, device %p, adjacency %p, materials %p, "
4084 "effect_instances %p, num_materials %p, mesh %p.\n",
4085 debugstr_a(filename), options, device, adjacency, materials,
4086 effect_instances, num_materials, mesh);
4088 if (!filename)
4089 return D3DERR_INVALIDCALL;
4091 len = MultiByteToWideChar(CP_ACP, 0, filename, -1, NULL, 0);
4092 filenameW = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
4093 if (!filenameW) return E_OUTOFMEMORY;
4094 MultiByteToWideChar(CP_ACP, 0, filename, -1, filenameW, len);
4096 hr = D3DXLoadMeshFromXW(filenameW, options, device, adjacency, materials,
4097 effect_instances, num_materials, mesh);
4098 HeapFree(GetProcessHeap(), 0, filenameW);
4100 return hr;
4103 HRESULT WINAPI D3DXLoadMeshFromXW(const WCHAR *filename, DWORD options, struct IDirect3DDevice9 *device,
4104 struct ID3DXBuffer **adjacency, struct ID3DXBuffer **materials, struct ID3DXBuffer **effect_instances,
4105 DWORD *num_materials, struct ID3DXMesh **mesh)
4107 void *buffer;
4108 HRESULT hr;
4109 DWORD size;
4111 TRACE("filename %s, options %#lx, device %p, adjacency %p, materials %p, "
4112 "effect_instances %p, num_materials %p, mesh %p.\n",
4113 debugstr_w(filename), options, device, adjacency, materials,
4114 effect_instances, num_materials, mesh);
4116 if (!filename)
4117 return D3DERR_INVALIDCALL;
4119 hr = map_view_of_file(filename, &buffer, &size);
4120 if (FAILED(hr))
4121 return D3DXERR_INVALIDDATA;
4123 hr = D3DXLoadMeshFromXInMemory(buffer, size, options, device, adjacency,
4124 materials, effect_instances, num_materials, mesh);
4126 UnmapViewOfFile(buffer);
4128 return hr;
4131 HRESULT WINAPI D3DXLoadMeshFromXResource(HMODULE module, const char *name, const char *type, DWORD options,
4132 struct IDirect3DDevice9 *device, struct ID3DXBuffer **adjacency, struct ID3DXBuffer **materials,
4133 struct ID3DXBuffer **effect_instances, DWORD *num_materials, struct ID3DXMesh **mesh)
4135 HRESULT hr;
4136 HRSRC resinfo;
4137 void *buffer;
4138 DWORD size;
4140 TRACE("module %p, name %s, type %s, options %#lx, device %p, adjacency %p, "
4141 "materials %p, effect_instances %p, num_materials %p, mesh %p.\n",
4142 module, debugstr_a(name), debugstr_a(type), options, device, adjacency,
4143 materials, effect_instances, num_materials, mesh);
4145 resinfo = FindResourceA(module, name, type);
4146 if (!resinfo) return D3DXERR_INVALIDDATA;
4148 hr = load_resource_into_memory(module, resinfo, &buffer, &size);
4149 if (FAILED(hr)) return D3DXERR_INVALIDDATA;
4151 return D3DXLoadMeshFromXInMemory(buffer, size, options, device, adjacency,
4152 materials, effect_instances, num_materials, mesh);
4155 struct mesh_container
4157 struct list entry;
4158 ID3DXMesh *mesh;
4159 ID3DXBuffer *adjacency;
4160 ID3DXBuffer *materials;
4161 ID3DXBuffer *effects;
4162 DWORD num_materials;
4163 D3DXMATRIX transform;
4166 static HRESULT parse_frame(struct ID3DXFileData *filedata, DWORD options, struct IDirect3DDevice9 *device,
4167 const D3DXMATRIX *parent_transform, struct list *container_list, DWORD provide_flags)
4169 HRESULT hr;
4170 D3DXMATRIX transform = *parent_transform;
4171 ID3DXFileData *child;
4172 GUID type;
4173 SIZE_T i, nb_children;
4175 hr = filedata->lpVtbl->GetChildren(filedata, &nb_children);
4176 if (FAILED(hr))
4177 return hr;
4179 for (i = 0; i < nb_children; i++)
4181 hr = filedata->lpVtbl->GetChild(filedata, i, &child);
4182 if (FAILED(hr))
4183 return hr;
4184 hr = child->lpVtbl->GetType(child, &type);
4185 if (FAILED(hr))
4186 goto err;
4188 if (IsEqualGUID(&type, &TID_D3DRMMesh)) {
4189 struct mesh_container *container = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(*container));
4190 if (!container)
4192 hr = E_OUTOFMEMORY;
4193 goto err;
4195 list_add_tail(container_list, &container->entry);
4196 container->transform = transform;
4198 hr = D3DXLoadSkinMeshFromXof(child, options, device,
4199 (provide_flags & PROVIDE_ADJACENCY) ? &container->adjacency : NULL,
4200 (provide_flags & PROVIDE_MATERIALS) ? &container->materials : NULL,
4201 NULL, &container->num_materials, NULL, &container->mesh);
4202 } else if (IsEqualGUID(&type, &TID_D3DRMFrameTransformMatrix)) {
4203 D3DXMATRIX new_transform;
4204 hr = parse_transform_matrix(child, &new_transform);
4205 D3DXMatrixMultiply(&transform, &transform, &new_transform);
4206 } else if (IsEqualGUID(&type, &TID_D3DRMFrame)) {
4207 hr = parse_frame(child, options, device, &transform, container_list, provide_flags);
4209 if (FAILED(hr))
4210 goto err;
4212 IUnknown_Release(child);
4214 return D3D_OK;
4216 err:
4217 IUnknown_Release(child);
4218 return hr;
4221 HRESULT WINAPI D3DXLoadMeshFromXInMemory(const void *memory, DWORD memory_size, DWORD options,
4222 struct IDirect3DDevice9 *device, struct ID3DXBuffer **adjacency_out, struct ID3DXBuffer **materials_out,
4223 struct ID3DXBuffer **effects_out, DWORD *num_materials_out, struct ID3DXMesh **mesh_out)
4225 HRESULT hr;
4226 ID3DXFile *d3dxfile = NULL;
4227 ID3DXFileEnumObject *enumobj = NULL;
4228 ID3DXFileData *filedata = NULL;
4229 D3DXF_FILELOADMEMORY source;
4230 ID3DXBuffer *materials = NULL;
4231 ID3DXBuffer *effects = NULL;
4232 ID3DXBuffer *adjacency = NULL;
4233 struct list container_list = LIST_INIT(container_list);
4234 struct mesh_container *container_ptr, *next_container_ptr;
4235 DWORD num_materials;
4236 DWORD num_faces, num_vertices;
4237 D3DXMATRIX identity;
4238 DWORD provide_flags = 0;
4239 DWORD fvf;
4240 ID3DXMesh *concat_mesh = NULL;
4241 D3DVERTEXELEMENT9 concat_decl[MAX_FVF_DECL_SIZE];
4242 BYTE *concat_vertices = NULL;
4243 void *concat_indices = NULL;
4244 DWORD index_offset;
4245 DWORD concat_vertex_size;
4246 SIZE_T i, nb_children;
4247 GUID guid;
4249 TRACE("memory %p, memory_size %lu, options %#lx, device %p, adjacency_out %p, materials_out %p, "
4250 "effects_out %p, num_materials_out %p, mesh_out %p.\n", memory, memory_size, options,
4251 device, adjacency_out, materials_out, effects_out, num_materials_out, mesh_out);
4253 if (!memory || !memory_size || !device || !mesh_out)
4254 return D3DERR_INVALIDCALL;
4256 hr = D3DXFileCreate(&d3dxfile);
4257 if (FAILED(hr)) goto cleanup;
4259 hr = d3dxfile->lpVtbl->RegisterTemplates(d3dxfile, D3DRM_XTEMPLATES, D3DRM_XTEMPLATE_BYTES);
4260 if (FAILED(hr)) goto cleanup;
4262 source.lpMemory = (void*)memory;
4263 source.dSize = memory_size;
4264 hr = d3dxfile->lpVtbl->CreateEnumObject(d3dxfile, &source, D3DXF_FILELOAD_FROMMEMORY, &enumobj);
4265 if (FAILED(hr)) goto cleanup;
4267 D3DXMatrixIdentity(&identity);
4268 if (adjacency_out) provide_flags |= PROVIDE_ADJACENCY;
4269 if (materials_out || effects_out) provide_flags |= PROVIDE_MATERIALS;
4271 hr = enumobj->lpVtbl->GetChildren(enumobj, &nb_children);
4272 if (FAILED(hr))
4273 goto cleanup;
4275 for (i = 0; i < nb_children; i++)
4277 hr = enumobj->lpVtbl->GetChild(enumobj, i, &filedata);
4278 if (FAILED(hr))
4279 goto cleanup;
4281 hr = filedata->lpVtbl->GetType(filedata, &guid);
4282 if (SUCCEEDED(hr)) {
4283 if (IsEqualGUID(&guid, &TID_D3DRMMesh)) {
4284 container_ptr = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(*container_ptr));
4285 if (!container_ptr) {
4286 hr = E_OUTOFMEMORY;
4287 goto cleanup;
4289 list_add_tail(&container_list, &container_ptr->entry);
4290 D3DXMatrixIdentity(&container_ptr->transform);
4292 hr = D3DXLoadSkinMeshFromXof(filedata, options, device,
4293 (provide_flags & PROVIDE_ADJACENCY) ? &container_ptr->adjacency : NULL,
4294 (provide_flags & PROVIDE_MATERIALS) ? &container_ptr->materials : NULL,
4295 NULL, &container_ptr->num_materials, NULL, &container_ptr->mesh);
4296 } else if (IsEqualGUID(&guid, &TID_D3DRMFrame)) {
4297 hr = parse_frame(filedata, options, device, &identity, &container_list, provide_flags);
4299 if (FAILED(hr)) goto cleanup;
4301 filedata->lpVtbl->Release(filedata);
4302 filedata = NULL;
4303 if (FAILED(hr))
4304 goto cleanup;
4307 enumobj->lpVtbl->Release(enumobj);
4308 enumobj = NULL;
4309 d3dxfile->lpVtbl->Release(d3dxfile);
4310 d3dxfile = NULL;
4312 if (list_empty(&container_list)) {
4313 hr = E_FAIL;
4314 goto cleanup;
4317 fvf = D3DFVF_XYZ;
4318 num_faces = 0;
4319 num_vertices = 0;
4320 num_materials = 0;
4321 LIST_FOR_EACH_ENTRY(container_ptr, &container_list, struct mesh_container, entry)
4323 ID3DXMesh *mesh = container_ptr->mesh;
4324 fvf |= mesh->lpVtbl->GetFVF(mesh);
4325 num_faces += mesh->lpVtbl->GetNumFaces(mesh);
4326 num_vertices += mesh->lpVtbl->GetNumVertices(mesh);
4327 num_materials += container_ptr->num_materials;
4330 hr = D3DXCreateMeshFVF(num_faces, num_vertices, options, fvf, device, &concat_mesh);
4331 if (FAILED(hr)) goto cleanup;
4333 hr = concat_mesh->lpVtbl->GetDeclaration(concat_mesh, concat_decl);
4334 if (FAILED(hr)) goto cleanup;
4336 concat_vertex_size = D3DXGetDeclVertexSize(concat_decl, 0);
4338 hr = concat_mesh->lpVtbl->LockVertexBuffer(concat_mesh, 0, (void**)&concat_vertices);
4339 if (FAILED(hr)) goto cleanup;
4341 LIST_FOR_EACH_ENTRY(container_ptr, &container_list, struct mesh_container, entry)
4343 D3DVERTEXELEMENT9 mesh_decl[MAX_FVF_DECL_SIZE];
4344 ID3DXMesh *mesh = container_ptr->mesh;
4345 DWORD num_mesh_vertices = mesh->lpVtbl->GetNumVertices(mesh);
4346 DWORD mesh_vertex_size;
4347 const BYTE *mesh_vertices;
4348 DWORD i;
4350 hr = mesh->lpVtbl->GetDeclaration(mesh, mesh_decl);
4351 if (FAILED(hr)) goto cleanup;
4353 mesh_vertex_size = D3DXGetDeclVertexSize(mesh_decl, 0);
4355 hr = mesh->lpVtbl->LockVertexBuffer(mesh, D3DLOCK_READONLY, (void**)&mesh_vertices);
4356 if (FAILED(hr)) goto cleanup;
4358 for (i = 0; i < num_mesh_vertices; i++) {
4359 int j;
4360 int k = 1;
4362 D3DXVec3TransformCoord((D3DXVECTOR3*)concat_vertices,
4363 (D3DXVECTOR3*)mesh_vertices,
4364 &container_ptr->transform);
4365 for (j = 1; concat_decl[j].Stream != 0xff; j++)
4367 if (concat_decl[j].Usage == mesh_decl[k].Usage &&
4368 concat_decl[j].UsageIndex == mesh_decl[k].UsageIndex)
4370 if (concat_decl[j].Usage == D3DDECLUSAGE_NORMAL) {
4371 D3DXVec3TransformCoord((D3DXVECTOR3*)(concat_vertices + concat_decl[j].Offset),
4372 (D3DXVECTOR3*)(mesh_vertices + mesh_decl[k].Offset),
4373 &container_ptr->transform);
4374 } else {
4375 memcpy(concat_vertices + concat_decl[j].Offset,
4376 mesh_vertices + mesh_decl[k].Offset,
4377 d3dx_decltype_size[mesh_decl[k].Type]);
4379 k++;
4382 mesh_vertices += mesh_vertex_size;
4383 concat_vertices += concat_vertex_size;
4386 mesh->lpVtbl->UnlockVertexBuffer(mesh);
4389 concat_mesh->lpVtbl->UnlockVertexBuffer(concat_mesh);
4390 concat_vertices = NULL;
4392 hr = concat_mesh->lpVtbl->LockIndexBuffer(concat_mesh, 0, &concat_indices);
4393 if (FAILED(hr)) goto cleanup;
4395 index_offset = 0;
4396 LIST_FOR_EACH_ENTRY(container_ptr, &container_list, struct mesh_container, entry)
4398 ID3DXMesh *mesh = container_ptr->mesh;
4399 const void *mesh_indices;
4400 DWORD num_mesh_faces = mesh->lpVtbl->GetNumFaces(mesh);
4401 DWORD i;
4403 hr = mesh->lpVtbl->LockIndexBuffer(mesh, D3DLOCK_READONLY, (void**)&mesh_indices);
4404 if (FAILED(hr)) goto cleanup;
4406 if (options & D3DXMESH_32BIT) {
4407 DWORD *dest = concat_indices;
4408 const DWORD *src = mesh_indices;
4409 for (i = 0; i < num_mesh_faces * 3; i++)
4410 *dest++ = index_offset + *src++;
4411 concat_indices = dest;
4412 } else {
4413 WORD *dest = concat_indices;
4414 const WORD *src = mesh_indices;
4415 for (i = 0; i < num_mesh_faces * 3; i++)
4416 *dest++ = index_offset + *src++;
4417 concat_indices = dest;
4419 mesh->lpVtbl->UnlockIndexBuffer(mesh);
4421 index_offset += num_mesh_faces * 3;
4424 concat_mesh->lpVtbl->UnlockIndexBuffer(concat_mesh);
4425 concat_indices = NULL;
4427 if (num_materials) {
4428 DWORD *concat_attrib_buffer = NULL;
4429 DWORD offset = 0;
4431 hr = concat_mesh->lpVtbl->LockAttributeBuffer(concat_mesh, 0, &concat_attrib_buffer);
4432 if (FAILED(hr)) goto cleanup;
4434 LIST_FOR_EACH_ENTRY(container_ptr, &container_list, struct mesh_container, entry)
4436 ID3DXMesh *mesh = container_ptr->mesh;
4437 const DWORD *mesh_attrib_buffer = NULL;
4438 DWORD count = mesh->lpVtbl->GetNumFaces(mesh);
4440 hr = mesh->lpVtbl->LockAttributeBuffer(mesh, D3DLOCK_READONLY, (DWORD**)&mesh_attrib_buffer);
4441 if (FAILED(hr)) {
4442 concat_mesh->lpVtbl->UnlockAttributeBuffer(concat_mesh);
4443 goto cleanup;
4446 while (count--)
4447 *concat_attrib_buffer++ = offset + *mesh_attrib_buffer++;
4449 mesh->lpVtbl->UnlockAttributeBuffer(mesh);
4450 offset += container_ptr->num_materials;
4452 concat_mesh->lpVtbl->UnlockAttributeBuffer(concat_mesh);
4455 if (materials_out || effects_out) {
4456 D3DXMATERIAL *out_ptr;
4457 if (!num_materials) {
4458 /* create default material */
4459 hr = D3DXCreateBuffer(sizeof(D3DXMATERIAL), &materials);
4460 if (FAILED(hr)) goto cleanup;
4462 out_ptr = ID3DXBuffer_GetBufferPointer(materials);
4463 out_ptr->MatD3D.Diffuse.r = 0.5f;
4464 out_ptr->MatD3D.Diffuse.g = 0.5f;
4465 out_ptr->MatD3D.Diffuse.b = 0.5f;
4466 out_ptr->MatD3D.Specular.r = 0.5f;
4467 out_ptr->MatD3D.Specular.g = 0.5f;
4468 out_ptr->MatD3D.Specular.b = 0.5f;
4469 /* D3DXCreateBuffer initializes the rest to zero */
4470 } else {
4471 DWORD buffer_size = num_materials * sizeof(D3DXMATERIAL);
4472 char *strings_out_ptr;
4474 LIST_FOR_EACH_ENTRY(container_ptr, &container_list, struct mesh_container, entry)
4476 if (container_ptr->materials) {
4477 DWORD i;
4478 const D3DXMATERIAL *in_ptr = ID3DXBuffer_GetBufferPointer(container_ptr->materials);
4479 for (i = 0; i < container_ptr->num_materials; i++)
4481 if (in_ptr->pTextureFilename)
4482 buffer_size += strlen(in_ptr->pTextureFilename) + 1;
4483 in_ptr++;
4488 hr = D3DXCreateBuffer(buffer_size, &materials);
4489 if (FAILED(hr)) goto cleanup;
4490 out_ptr = ID3DXBuffer_GetBufferPointer(materials);
4491 strings_out_ptr = (char*)(out_ptr + num_materials);
4493 LIST_FOR_EACH_ENTRY(container_ptr, &container_list, struct mesh_container, entry)
4495 if (container_ptr->materials) {
4496 DWORD i;
4497 const D3DXMATERIAL *in_ptr = ID3DXBuffer_GetBufferPointer(container_ptr->materials);
4498 for (i = 0; i < container_ptr->num_materials; i++)
4500 out_ptr->MatD3D = in_ptr->MatD3D;
4501 if (in_ptr->pTextureFilename) {
4502 out_ptr->pTextureFilename = strings_out_ptr;
4503 strcpy(out_ptr->pTextureFilename, in_ptr->pTextureFilename);
4504 strings_out_ptr += strlen(in_ptr->pTextureFilename) + 1;
4506 in_ptr++;
4507 out_ptr++;
4513 if (!num_materials)
4514 num_materials = 1;
4516 if (effects_out) {
4517 generate_effects(materials, num_materials, &effects);
4518 if (!materials_out) {
4519 ID3DXBuffer_Release(materials);
4520 materials = NULL;
4524 if (adjacency_out) {
4525 if (!list_next(&container_list, list_head(&container_list))) {
4526 container_ptr = LIST_ENTRY(list_head(&container_list), struct mesh_container, entry);
4527 adjacency = container_ptr->adjacency;
4528 container_ptr->adjacency = NULL;
4529 } else {
4530 DWORD offset = 0;
4531 DWORD *out_ptr;
4533 hr = D3DXCreateBuffer(num_faces * 3 * sizeof(DWORD), &adjacency);
4534 if (FAILED(hr)) goto cleanup;
4536 out_ptr = ID3DXBuffer_GetBufferPointer(adjacency);
4537 LIST_FOR_EACH_ENTRY(container_ptr, &container_list, struct mesh_container, entry)
4539 DWORD i;
4540 DWORD count = 3 * container_ptr->mesh->lpVtbl->GetNumFaces(container_ptr->mesh);
4541 DWORD *in_ptr = ID3DXBuffer_GetBufferPointer(container_ptr->adjacency);
4543 for (i = 0; i < count; i++)
4544 *out_ptr++ = offset + *in_ptr++;
4546 offset += count;
4551 *mesh_out = concat_mesh;
4552 if (adjacency_out) *adjacency_out = adjacency;
4553 if (materials_out) *materials_out = materials;
4554 if (effects_out) *effects_out = effects;
4555 if (num_materials_out) *num_materials_out = num_materials;
4557 hr = D3D_OK;
4558 cleanup:
4559 if (concat_indices) concat_mesh->lpVtbl->UnlockIndexBuffer(concat_mesh);
4560 if (concat_vertices) concat_mesh->lpVtbl->UnlockVertexBuffer(concat_mesh);
4561 if (filedata) filedata->lpVtbl->Release(filedata);
4562 if (enumobj) enumobj->lpVtbl->Release(enumobj);
4563 if (d3dxfile) d3dxfile->lpVtbl->Release(d3dxfile);
4564 if (FAILED(hr)) {
4565 if (concat_mesh) IUnknown_Release(concat_mesh);
4566 if (materials) ID3DXBuffer_Release(materials);
4567 if (effects) ID3DXBuffer_Release(effects);
4568 if (adjacency) ID3DXBuffer_Release(adjacency);
4570 LIST_FOR_EACH_ENTRY_SAFE(container_ptr, next_container_ptr, &container_list, struct mesh_container, entry)
4572 if (container_ptr->mesh) IUnknown_Release(container_ptr->mesh);
4573 if (container_ptr->adjacency) ID3DXBuffer_Release(container_ptr->adjacency);
4574 if (container_ptr->materials) ID3DXBuffer_Release(container_ptr->materials);
4575 if (container_ptr->effects) ID3DXBuffer_Release(container_ptr->effects);
4576 HeapFree(GetProcessHeap(), 0, container_ptr);
4578 return hr;
4581 struct vertex
4583 D3DXVECTOR3 position;
4584 D3DXVECTOR3 normal;
4587 HRESULT WINAPI D3DXCreatePolygon(struct IDirect3DDevice9 *device, float length, UINT sides,
4588 struct ID3DXMesh **mesh, struct ID3DXBuffer **adjacency)
4590 HRESULT hr;
4591 ID3DXMesh *polygon;
4592 struct vertex *vertices;
4593 WORD (*faces)[3];
4594 DWORD (*adjacency_buf)[3];
4595 float angle, scale;
4596 unsigned int i;
4598 TRACE("device %p, length %f, sides %u, mesh %p, adjacency %p.\n",
4599 device, length, sides, mesh, adjacency);
4601 if (!device || length < 0.0f || sides < 3 || !mesh)
4602 return D3DERR_INVALIDCALL;
4604 if (FAILED(hr = D3DXCreateMeshFVF(sides, sides + 1, D3DXMESH_MANAGED,
4605 D3DFVF_XYZ | D3DFVF_NORMAL, device, &polygon)))
4607 return hr;
4610 if (FAILED(hr = polygon->lpVtbl->LockVertexBuffer(polygon, 0, (void **)&vertices)))
4612 polygon->lpVtbl->Release(polygon);
4613 return hr;
4616 if (FAILED(hr = polygon->lpVtbl->LockIndexBuffer(polygon, 0, (void **)&faces)))
4618 polygon->lpVtbl->UnlockVertexBuffer(polygon);
4619 polygon->lpVtbl->Release(polygon);
4620 return hr;
4623 angle = D3DX_PI / sides;
4624 scale = 0.5f * length / sinf(angle);
4625 angle *= 2.0f;
4627 vertices[0].position.x = 0.0f;
4628 vertices[0].position.y = 0.0f;
4629 vertices[0].position.z = 0.0f;
4630 vertices[0].normal.x = 0.0f;
4631 vertices[0].normal.y = 0.0f;
4632 vertices[0].normal.z = 1.0f;
4634 for (i = 0; i < sides; ++i)
4636 vertices[i + 1].position.x = cosf(angle * i) * scale;
4637 vertices[i + 1].position.y = sinf(angle * i) * scale;
4638 vertices[i + 1].position.z = 0.0f;
4639 vertices[i + 1].normal.x = 0.0f;
4640 vertices[i + 1].normal.y = 0.0f;
4641 vertices[i + 1].normal.z = 1.0f;
4643 faces[i][0] = 0;
4644 faces[i][1] = i + 1;
4645 faces[i][2] = i + 2;
4648 faces[sides - 1][2] = 1;
4650 polygon->lpVtbl->UnlockVertexBuffer(polygon);
4651 polygon->lpVtbl->UnlockIndexBuffer(polygon);
4653 if (adjacency)
4655 if (FAILED(hr = D3DXCreateBuffer(sides * sizeof(DWORD) * 3, adjacency)))
4657 polygon->lpVtbl->Release(polygon);
4658 return hr;
4661 adjacency_buf = ID3DXBuffer_GetBufferPointer(*adjacency);
4662 for (i = 0; i < sides; ++i)
4664 adjacency_buf[i][0] = i - 1;
4665 adjacency_buf[i][1] = ~0U;
4666 adjacency_buf[i][2] = i + 1;
4668 adjacency_buf[0][0] = sides - 1;
4669 adjacency_buf[sides - 1][2] = 0;
4672 *mesh = polygon;
4674 return D3D_OK;
4677 HRESULT WINAPI D3DXCreateBox(struct IDirect3DDevice9 *device, float width, float height,
4678 float depth, struct ID3DXMesh **mesh, struct ID3DXBuffer **adjacency)
4680 HRESULT hr;
4681 ID3DXMesh *box;
4682 struct vertex *vertices;
4683 WORD (*faces)[3];
4684 DWORD *adjacency_buf;
4685 unsigned int i, face;
4686 static const D3DXVECTOR3 unit_box[] =
4688 {-0.5f, -0.5f, -0.5f}, {-0.5f, -0.5f, 0.5f}, {-0.5f, 0.5f, 0.5f}, {-0.5f, 0.5f, -0.5f},
4689 {-0.5f, 0.5f, -0.5f}, {-0.5f, 0.5f, 0.5f}, { 0.5f, 0.5f, 0.5f}, { 0.5f, 0.5f, -0.5f},
4690 { 0.5f, 0.5f, -0.5f}, { 0.5f, 0.5f, 0.5f}, { 0.5f, -0.5f, 0.5f}, { 0.5f, -0.5f, -0.5f},
4691 {-0.5f, -0.5f, 0.5f}, {-0.5f, -0.5f, -0.5f}, { 0.5f, -0.5f, -0.5f}, { 0.5f, -0.5f, 0.5f},
4692 {-0.5f, -0.5f, 0.5f}, { 0.5f, -0.5f, 0.5f}, { 0.5f, 0.5f, 0.5f}, {-0.5f, 0.5f, 0.5f},
4693 {-0.5f, -0.5f, -0.5f}, {-0.5f, 0.5f, -0.5f}, { 0.5f, 0.5f, -0.5f}, { 0.5f, -0.5f, -0.5f}
4695 static const D3DXVECTOR3 normals[] =
4697 {-1.0f, 0.0f, 0.0f}, { 0.0f, 1.0f, 0.0f}, { 1.0f, 0.0f, 0.0f},
4698 { 0.0f, -1.0f, 0.0f}, { 0.0f, 0.0f, 1.0f}, { 0.0f, 0.0f, -1.0f}
4700 static const DWORD adjacency_table[] =
4702 6, 9, 1, 2, 10, 0, 1, 9, 3, 4, 10, 2,
4703 3, 8, 5, 7, 11, 4, 0, 11, 7, 5, 8, 6,
4704 7, 4, 9, 2, 0, 8, 1, 3, 11, 5, 6, 10
4707 TRACE("device %p, width %f, height %f, depth %f, mesh %p, adjacency %p\n",
4708 device, width, height, depth, mesh, adjacency);
4710 if (!device || width < 0.0f || height < 0.0f || depth < 0.0f || !mesh)
4712 return D3DERR_INVALIDCALL;
4715 if (FAILED(hr = D3DXCreateMeshFVF(12, 24, D3DXMESH_MANAGED, D3DFVF_XYZ | D3DFVF_NORMAL, device, &box)))
4717 return hr;
4720 if (FAILED(hr = box->lpVtbl->LockVertexBuffer(box, 0, (void **)&vertices)))
4722 box->lpVtbl->Release(box);
4723 return hr;
4726 if (FAILED(hr = box->lpVtbl->LockIndexBuffer(box, 0, (void **)&faces)))
4728 box->lpVtbl->UnlockVertexBuffer(box);
4729 box->lpVtbl->Release(box);
4730 return hr;
4733 for (i = 0; i < 24; i++)
4735 vertices[i].position.x = width * unit_box[i].x;
4736 vertices[i].position.y = height * unit_box[i].y;
4737 vertices[i].position.z = depth * unit_box[i].z;
4738 vertices[i].normal.x = normals[i / 4].x;
4739 vertices[i].normal.y = normals[i / 4].y;
4740 vertices[i].normal.z = normals[i / 4].z;
4743 face = 0;
4744 for (i = 0; i < 12; i++)
4746 faces[i][0] = face++;
4747 faces[i][1] = face++;
4748 faces[i][2] = (i % 2) ? face - 4 : face;
4751 box->lpVtbl->UnlockIndexBuffer(box);
4752 box->lpVtbl->UnlockVertexBuffer(box);
4754 if (adjacency)
4756 if (FAILED(hr = D3DXCreateBuffer(sizeof(adjacency_table), adjacency)))
4758 box->lpVtbl->Release(box);
4759 return hr;
4762 adjacency_buf = ID3DXBuffer_GetBufferPointer(*adjacency);
4763 memcpy(adjacency_buf, adjacency_table, sizeof(adjacency_table));
4766 *mesh = box;
4768 return D3D_OK;
4771 typedef WORD face[3];
4773 struct sincos_table
4775 float *sin;
4776 float *cos;
4779 static void free_sincos_table(struct sincos_table *sincos_table)
4781 HeapFree(GetProcessHeap(), 0, sincos_table->cos);
4782 HeapFree(GetProcessHeap(), 0, sincos_table->sin);
4785 /* pre compute sine and cosine tables; caller must free */
4786 static BOOL compute_sincos_table(struct sincos_table *sincos_table, float angle_start, float angle_step, int n)
4788 float angle;
4789 int i;
4791 sincos_table->sin = HeapAlloc(GetProcessHeap(), 0, n * sizeof(*sincos_table->sin));
4792 if (!sincos_table->sin)
4794 return FALSE;
4796 sincos_table->cos = HeapAlloc(GetProcessHeap(), 0, n * sizeof(*sincos_table->cos));
4797 if (!sincos_table->cos)
4799 HeapFree(GetProcessHeap(), 0, sincos_table->sin);
4800 return FALSE;
4803 angle = angle_start;
4804 for (i = 0; i < n; i++)
4806 sincos_table->sin[i] = sinf(angle);
4807 sincos_table->cos[i] = cosf(angle);
4808 angle += angle_step;
4811 return TRUE;
4814 static WORD vertex_index(UINT slices, int slice, int stack)
4816 return stack*slices+slice+1;
4819 HRESULT WINAPI D3DXCreateSphere(struct IDirect3DDevice9 *device, float radius, UINT slices,
4820 UINT stacks, struct ID3DXMesh **mesh, struct ID3DXBuffer **adjacency)
4822 DWORD number_of_vertices, number_of_faces;
4823 HRESULT hr;
4824 ID3DXMesh *sphere;
4825 struct vertex *vertices;
4826 face *faces;
4827 float phi_step, phi_start;
4828 struct sincos_table phi;
4829 float theta_step, theta, sin_theta, cos_theta;
4830 DWORD vertex, face, stack, slice;
4832 TRACE("(%p, %f, %u, %u, %p, %p)\n", device, radius, slices, stacks, mesh, adjacency);
4834 if (!device || radius < 0.0f || slices < 2 || stacks < 2 || !mesh)
4836 return D3DERR_INVALIDCALL;
4839 number_of_vertices = 2 + slices * (stacks-1);
4840 number_of_faces = 2 * slices + (stacks - 2) * (2 * slices);
4842 hr = D3DXCreateMeshFVF(number_of_faces, number_of_vertices, D3DXMESH_MANAGED,
4843 D3DFVF_XYZ | D3DFVF_NORMAL, device, &sphere);
4844 if (FAILED(hr))
4846 return hr;
4849 if (FAILED(hr = sphere->lpVtbl->LockVertexBuffer(sphere, 0, (void **)&vertices)))
4851 sphere->lpVtbl->Release(sphere);
4852 return hr;
4855 if (FAILED(hr = sphere->lpVtbl->LockIndexBuffer(sphere, 0, (void **)&faces)))
4857 sphere->lpVtbl->UnlockVertexBuffer(sphere);
4858 sphere->lpVtbl->Release(sphere);
4859 return hr;
4862 /* phi = angle on xz plane wrt z axis */
4863 phi_step = -2.0f * D3DX_PI / slices;
4864 phi_start = D3DX_PI / 2.0f;
4866 if (!compute_sincos_table(&phi, phi_start, phi_step, slices))
4868 sphere->lpVtbl->UnlockIndexBuffer(sphere);
4869 sphere->lpVtbl->UnlockVertexBuffer(sphere);
4870 sphere->lpVtbl->Release(sphere);
4871 return E_OUTOFMEMORY;
4874 /* theta = angle on xy plane wrt x axis */
4875 theta_step = D3DX_PI / stacks;
4876 theta = theta_step;
4878 vertex = 0;
4879 face = 0;
4881 vertices[vertex].normal.x = 0.0f;
4882 vertices[vertex].normal.y = 0.0f;
4883 vertices[vertex].normal.z = 1.0f;
4884 vertices[vertex].position.x = 0.0f;
4885 vertices[vertex].position.y = 0.0f;
4886 vertices[vertex].position.z = radius;
4887 vertex++;
4889 for (stack = 0; stack < stacks - 1; stack++)
4891 sin_theta = sinf(theta);
4892 cos_theta = cosf(theta);
4894 for (slice = 0; slice < slices; slice++)
4896 vertices[vertex].normal.x = sin_theta * phi.cos[slice];
4897 vertices[vertex].normal.y = sin_theta * phi.sin[slice];
4898 vertices[vertex].normal.z = cos_theta;
4899 vertices[vertex].position.x = radius * sin_theta * phi.cos[slice];
4900 vertices[vertex].position.y = radius * sin_theta * phi.sin[slice];
4901 vertices[vertex].position.z = radius * cos_theta;
4902 vertex++;
4904 if (slice > 0)
4906 if (stack == 0)
4908 /* top stack is triangle fan */
4909 faces[face][0] = 0;
4910 faces[face][1] = slice + 1;
4911 faces[face][2] = slice;
4912 face++;
4914 else
4916 /* stacks in between top and bottom are quad strips */
4917 faces[face][0] = vertex_index(slices, slice-1, stack-1);
4918 faces[face][1] = vertex_index(slices, slice, stack-1);
4919 faces[face][2] = vertex_index(slices, slice-1, stack);
4920 face++;
4922 faces[face][0] = vertex_index(slices, slice, stack-1);
4923 faces[face][1] = vertex_index(slices, slice, stack);
4924 faces[face][2] = vertex_index(slices, slice-1, stack);
4925 face++;
4930 theta += theta_step;
4932 if (stack == 0)
4934 faces[face][0] = 0;
4935 faces[face][1] = 1;
4936 faces[face][2] = slice;
4937 face++;
4939 else
4941 faces[face][0] = vertex_index(slices, slice-1, stack-1);
4942 faces[face][1] = vertex_index(slices, 0, stack-1);
4943 faces[face][2] = vertex_index(slices, slice-1, stack);
4944 face++;
4946 faces[face][0] = vertex_index(slices, 0, stack-1);
4947 faces[face][1] = vertex_index(slices, 0, stack);
4948 faces[face][2] = vertex_index(slices, slice-1, stack);
4949 face++;
4953 vertices[vertex].position.x = 0.0f;
4954 vertices[vertex].position.y = 0.0f;
4955 vertices[vertex].position.z = -radius;
4956 vertices[vertex].normal.x = 0.0f;
4957 vertices[vertex].normal.y = 0.0f;
4958 vertices[vertex].normal.z = -1.0f;
4960 /* bottom stack is triangle fan */
4961 for (slice = 1; slice < slices; slice++)
4963 faces[face][0] = vertex_index(slices, slice-1, stack-1);
4964 faces[face][1] = vertex_index(slices, slice, stack-1);
4965 faces[face][2] = vertex;
4966 face++;
4969 faces[face][0] = vertex_index(slices, slice-1, stack-1);
4970 faces[face][1] = vertex_index(slices, 0, stack-1);
4971 faces[face][2] = vertex;
4973 free_sincos_table(&phi);
4974 sphere->lpVtbl->UnlockIndexBuffer(sphere);
4975 sphere->lpVtbl->UnlockVertexBuffer(sphere);
4978 if (adjacency)
4980 if (FAILED(hr = D3DXCreateBuffer(number_of_faces * sizeof(DWORD) * 3, adjacency)))
4982 sphere->lpVtbl->Release(sphere);
4983 return hr;
4986 if (FAILED(hr = sphere->lpVtbl->GenerateAdjacency(sphere, 0.0f, (*adjacency)->lpVtbl->GetBufferPointer(*adjacency))))
4988 (*adjacency)->lpVtbl->Release(*adjacency);
4989 sphere->lpVtbl->Release(sphere);
4990 return hr;
4994 *mesh = sphere;
4996 return D3D_OK;
4999 HRESULT WINAPI D3DXCreateCylinder(struct IDirect3DDevice9 *device, float radius1, float radius2,
5000 float length, UINT slices, UINT stacks, struct ID3DXMesh **mesh, struct ID3DXBuffer **adjacency)
5002 DWORD number_of_vertices, number_of_faces;
5003 HRESULT hr;
5004 ID3DXMesh *cylinder;
5005 struct vertex *vertices;
5006 face *faces;
5007 float theta_step, theta_start;
5008 struct sincos_table theta;
5009 float delta_radius, radius, radius_step;
5010 float z, z_step, z_normal;
5011 DWORD vertex, face, slice, stack;
5013 TRACE("(%p, %f, %f, %f, %u, %u, %p, %p)\n", device, radius1, radius2, length, slices, stacks, mesh, adjacency);
5015 if (device == NULL || radius1 < 0.0f || radius2 < 0.0f || length < 0.0f || slices < 2 || stacks < 1 || mesh == NULL)
5017 return D3DERR_INVALIDCALL;
5020 number_of_vertices = 2 + (slices * (3 + stacks));
5021 number_of_faces = 2 * slices + stacks * (2 * slices);
5023 hr = D3DXCreateMeshFVF(number_of_faces, number_of_vertices, D3DXMESH_MANAGED,
5024 D3DFVF_XYZ | D3DFVF_NORMAL, device, &cylinder);
5025 if (FAILED(hr))
5027 return hr;
5030 if (FAILED(hr = cylinder->lpVtbl->LockVertexBuffer(cylinder, 0, (void **)&vertices)))
5032 cylinder->lpVtbl->Release(cylinder);
5033 return hr;
5036 if (FAILED(hr = cylinder->lpVtbl->LockIndexBuffer(cylinder, 0, (void **)&faces)))
5038 cylinder->lpVtbl->UnlockVertexBuffer(cylinder);
5039 cylinder->lpVtbl->Release(cylinder);
5040 return hr;
5043 /* theta = angle on xy plane wrt x axis */
5044 theta_step = -2.0f * D3DX_PI / slices;
5045 theta_start = D3DX_PI / 2.0f;
5047 if (!compute_sincos_table(&theta, theta_start, theta_step, slices))
5049 cylinder->lpVtbl->UnlockIndexBuffer(cylinder);
5050 cylinder->lpVtbl->UnlockVertexBuffer(cylinder);
5051 cylinder->lpVtbl->Release(cylinder);
5052 return E_OUTOFMEMORY;
5055 vertex = 0;
5056 face = 0;
5058 delta_radius = radius1 - radius2;
5059 radius = radius1;
5060 radius_step = delta_radius / stacks;
5062 z = -length / 2;
5063 z_step = length / stacks;
5064 z_normal = delta_radius / length;
5065 if (isnan(z_normal))
5067 z_normal = 0.0f;
5070 vertices[vertex].normal.x = 0.0f;
5071 vertices[vertex].normal.y = 0.0f;
5072 vertices[vertex].normal.z = -1.0f;
5073 vertices[vertex].position.x = 0.0f;
5074 vertices[vertex].position.y = 0.0f;
5075 vertices[vertex++].position.z = z;
5077 for (slice = 0; slice < slices; slice++, vertex++)
5079 vertices[vertex].normal.x = 0.0f;
5080 vertices[vertex].normal.y = 0.0f;
5081 vertices[vertex].normal.z = -1.0f;
5082 vertices[vertex].position.x = radius * theta.cos[slice];
5083 vertices[vertex].position.y = radius * theta.sin[slice];
5084 vertices[vertex].position.z = z;
5086 if (slice > 0)
5088 faces[face][0] = 0;
5089 faces[face][1] = slice;
5090 faces[face++][2] = slice + 1;
5094 faces[face][0] = 0;
5095 faces[face][1] = slice;
5096 faces[face++][2] = 1;
5098 for (stack = 1; stack <= stacks+1; stack++)
5100 for (slice = 0; slice < slices; slice++, vertex++)
5102 vertices[vertex].normal.x = theta.cos[slice];
5103 vertices[vertex].normal.y = theta.sin[slice];
5104 vertices[vertex].normal.z = z_normal;
5105 D3DXVec3Normalize(&vertices[vertex].normal, &vertices[vertex].normal);
5106 vertices[vertex].position.x = radius * theta.cos[slice];
5107 vertices[vertex].position.y = radius * theta.sin[slice];
5108 vertices[vertex].position.z = z;
5110 if (stack > 1 && slice > 0)
5112 faces[face][0] = vertex_index(slices, slice-1, stack-1);
5113 faces[face][1] = vertex_index(slices, slice-1, stack);
5114 faces[face++][2] = vertex_index(slices, slice, stack-1);
5116 faces[face][0] = vertex_index(slices, slice, stack-1);
5117 faces[face][1] = vertex_index(slices, slice-1, stack);
5118 faces[face++][2] = vertex_index(slices, slice, stack);
5122 if (stack > 1)
5124 faces[face][0] = vertex_index(slices, slice-1, stack-1);
5125 faces[face][1] = vertex_index(slices, slice-1, stack);
5126 faces[face++][2] = vertex_index(slices, 0, stack-1);
5128 faces[face][0] = vertex_index(slices, 0, stack-1);
5129 faces[face][1] = vertex_index(slices, slice-1, stack);
5130 faces[face++][2] = vertex_index(slices, 0, stack);
5133 if (stack < stacks + 1)
5135 z += z_step;
5136 radius -= radius_step;
5140 for (slice = 0; slice < slices; slice++, vertex++)
5142 vertices[vertex].normal.x = 0.0f;
5143 vertices[vertex].normal.y = 0.0f;
5144 vertices[vertex].normal.z = 1.0f;
5145 vertices[vertex].position.x = radius * theta.cos[slice];
5146 vertices[vertex].position.y = radius * theta.sin[slice];
5147 vertices[vertex].position.z = z;
5149 if (slice > 0)
5151 faces[face][0] = vertex_index(slices, slice-1, stack);
5152 faces[face][1] = number_of_vertices - 1;
5153 faces[face++][2] = vertex_index(slices, slice, stack);
5157 vertices[vertex].position.x = 0.0f;
5158 vertices[vertex].position.y = 0.0f;
5159 vertices[vertex].position.z = z;
5160 vertices[vertex].normal.x = 0.0f;
5161 vertices[vertex].normal.y = 0.0f;
5162 vertices[vertex].normal.z = 1.0f;
5164 faces[face][0] = vertex_index(slices, slice-1, stack);
5165 faces[face][1] = number_of_vertices - 1;
5166 faces[face][2] = vertex_index(slices, 0, stack);
5168 free_sincos_table(&theta);
5169 cylinder->lpVtbl->UnlockIndexBuffer(cylinder);
5170 cylinder->lpVtbl->UnlockVertexBuffer(cylinder);
5172 if (adjacency)
5174 if (FAILED(hr = D3DXCreateBuffer(number_of_faces * sizeof(DWORD) * 3, adjacency)))
5176 cylinder->lpVtbl->Release(cylinder);
5177 return hr;
5180 if (FAILED(hr = cylinder->lpVtbl->GenerateAdjacency(cylinder, 0.0f, (*adjacency)->lpVtbl->GetBufferPointer(*adjacency))))
5182 (*adjacency)->lpVtbl->Release(*adjacency);
5183 cylinder->lpVtbl->Release(cylinder);
5184 return hr;
5188 *mesh = cylinder;
5190 return D3D_OK;
5193 HRESULT WINAPI D3DXCreateTeapot(struct IDirect3DDevice9 *device,
5194 struct ID3DXMesh **mesh, struct ID3DXBuffer **adjacency)
5196 FIXME("device %p, mesh %p, adjacency %p semi-stub.\n", device, mesh, adjacency);
5198 return D3DXCreateSphere(device, 1.0f, 4, 4, mesh, adjacency);
5201 HRESULT WINAPI D3DXCreateTextA(struct IDirect3DDevice9 *device, HDC hdc, const char *text, float deviation,
5202 float extrusion, struct ID3DXMesh **mesh, struct ID3DXBuffer **adjacency, GLYPHMETRICSFLOAT *glyphmetrics)
5204 WCHAR *textW;
5205 HRESULT hr;
5206 int len;
5208 TRACE("device %p, hdc %p, text %s, deviation %.8e, extrusion %.8e, mesh %p, adjacency %p, glyphmetrics %p.\n",
5209 device, hdc, debugstr_a(text), deviation, extrusion, mesh, adjacency, glyphmetrics);
5211 if (!text)
5212 return D3DERR_INVALIDCALL;
5214 len = MultiByteToWideChar(CP_ACP, 0, text, -1, NULL, 0);
5215 textW = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
5216 MultiByteToWideChar(CP_ACP, 0, text, -1, textW, len);
5218 hr = D3DXCreateTextW(device, hdc, textW, deviation, extrusion,
5219 mesh, adjacency, glyphmetrics);
5220 HeapFree(GetProcessHeap(), 0, textW);
5222 return hr;
5225 HRESULT WINAPI D3DXCreateTorus(struct IDirect3DDevice9 *device,
5226 float innerradius, float outerradius, UINT sides, UINT rings, struct ID3DXMesh **mesh, ID3DXBuffer **adjacency)
5228 HRESULT hr;
5229 ID3DXMesh *torus;
5230 WORD (*faces)[3];
5231 struct vertex *vertices;
5232 float phi, phi_step, sin_phi, cos_phi;
5233 float theta, theta_step, sin_theta, cos_theta;
5234 unsigned int i, j, numvert, numfaces;
5236 TRACE("device %p, innerradius %.8e, outerradius %.8e, sides %u, rings %u, mesh %p, adjacency %p.\n",
5237 device, innerradius, outerradius, sides, rings, mesh, adjacency);
5239 numvert = sides * rings;
5240 numfaces = numvert * 2;
5242 if (!device || innerradius < 0.0f || outerradius < 0.0f || sides < 3 || rings < 3 || !mesh)
5244 WARN("Invalid arguments.\n");
5245 return D3DERR_INVALIDCALL;
5248 if (FAILED(hr = D3DXCreateMeshFVF(numfaces, numvert, D3DXMESH_MANAGED, D3DFVF_XYZ | D3DFVF_NORMAL, device, &torus)))
5249 return hr;
5251 if (FAILED(hr = torus->lpVtbl->LockVertexBuffer(torus, 0, (void **)&vertices)))
5253 torus->lpVtbl->Release(torus);
5254 return hr;
5257 if (FAILED(hr = torus->lpVtbl->LockIndexBuffer(torus, 0, (void **)&faces)))
5259 torus->lpVtbl->UnlockVertexBuffer(torus);
5260 torus->lpVtbl->Release(torus);
5261 return hr;
5264 phi_step = D3DX_PI / sides * 2.0f;
5265 theta_step = D3DX_PI / rings * -2.0f;
5267 theta = 0.0f;
5269 for (i = 0; i < rings; ++i)
5271 phi = 0.0f;
5273 sin_theta = sinf(theta);
5274 cos_theta = cosf(theta);
5276 for (j = 0; j < sides; ++j)
5278 sin_phi = sinf(phi);
5279 cos_phi = cosf(phi);
5281 vertices[i * sides + j].position.x = (innerradius * cos_phi + outerradius) * cos_theta;
5282 vertices[i * sides + j].position.y = (innerradius * cos_phi + outerradius) * sin_theta;
5283 vertices[i * sides + j].position.z = innerradius * sin_phi;
5284 vertices[i * sides + j].normal.x = cos_phi * cos_theta;
5285 vertices[i * sides + j].normal.y = cos_phi * sin_theta;
5286 vertices[i * sides + j].normal.z = sin_phi;
5288 phi += phi_step;
5291 theta += theta_step;
5294 for (i = 0; i < numfaces - sides * 2; ++i)
5296 faces[i][0] = i % 2 ? i / 2 + sides : i / 2;
5297 faces[i][1] = (i / 2 + 1) % sides ? i / 2 + 1 : i / 2 + 1 - sides;
5298 faces[i][2] = (i + 1) % (sides * 2) ? (i + 1) / 2 + sides : (i + 1) / 2;
5301 for (j = 0; i < numfaces; ++i, ++j)
5303 faces[i][0] = i % 2 ? j / 2 : i / 2;
5304 faces[i][1] = (i / 2 + 1) % sides ? i / 2 + 1 : i / 2 + 1 - sides;
5305 faces[i][2] = i == numfaces - 1 ? 0 : (j + 1) / 2;
5308 torus->lpVtbl->UnlockIndexBuffer(torus);
5309 torus->lpVtbl->UnlockVertexBuffer(torus);
5311 if (adjacency)
5313 if (FAILED(hr = D3DXCreateBuffer(numfaces * sizeof(DWORD) * 3, adjacency)))
5315 torus->lpVtbl->Release(torus);
5316 return hr;
5319 if (FAILED(hr = torus->lpVtbl->GenerateAdjacency(torus, 0.0f, (*adjacency)->lpVtbl->GetBufferPointer(*adjacency))))
5321 (*adjacency)->lpVtbl->Release(*adjacency);
5322 torus->lpVtbl->Release(torus);
5323 return hr;
5327 *mesh = torus;
5329 return D3D_OK;
5332 enum pointtype {
5333 POINTTYPE_CURVE = 0,
5334 POINTTYPE_CORNER,
5335 POINTTYPE_CURVE_START,
5336 POINTTYPE_CURVE_END,
5337 POINTTYPE_CURVE_MIDDLE,
5340 struct point2d
5342 D3DXVECTOR2 pos;
5343 enum pointtype corner;
5346 struct dynamic_array
5348 int count, capacity;
5349 void *items;
5352 /* is a dynamic_array */
5353 struct outline
5355 int count, capacity;
5356 struct point2d *items;
5359 /* is a dynamic_array */
5360 struct outline_array
5362 int count, capacity;
5363 struct outline *items;
5366 struct face_array
5368 int count;
5369 face *items;
5372 struct point2d_index
5374 struct outline *outline;
5375 int vertex;
5378 struct point2d_index_array
5380 int count;
5381 struct point2d_index *items;
5384 struct glyphinfo
5386 struct outline_array outlines;
5387 struct face_array faces;
5388 struct point2d_index_array ordered_vertices;
5389 float offset_x;
5392 /* is an dynamic_array */
5393 struct word_array
5395 int count, capacity;
5396 WORD *items;
5399 /* complex polygons are split into monotone polygons, which have
5400 * at most 2 intersections with the vertical sweep line */
5401 struct triangulation
5403 struct word_array vertex_stack;
5404 BOOL last_on_top, merging;
5407 /* is an dynamic_array */
5408 struct triangulation_array
5410 int count, capacity;
5411 struct triangulation *items;
5413 struct glyphinfo *glyph;
5416 static BOOL reserve(struct dynamic_array *array, int count, int itemsize)
5418 if (count > array->capacity) {
5419 void *new_buffer;
5420 int new_capacity;
5421 if (array->items && array->capacity) {
5422 new_capacity = max(array->capacity * 2, count);
5423 new_buffer = HeapReAlloc(GetProcessHeap(), 0, array->items, new_capacity * itemsize);
5424 } else {
5425 new_capacity = max(16, count);
5426 new_buffer = HeapAlloc(GetProcessHeap(), 0, new_capacity * itemsize);
5428 if (!new_buffer)
5429 return FALSE;
5430 array->items = new_buffer;
5431 array->capacity = new_capacity;
5433 return TRUE;
5436 static struct point2d *add_points(struct outline *array, int num)
5438 struct point2d *item;
5440 if (!reserve((struct dynamic_array *)array, array->count + num, sizeof(array->items[0])))
5441 return NULL;
5443 item = &array->items[array->count];
5444 array->count += num;
5445 return item;
5448 static struct outline *add_outline(struct outline_array *array)
5450 struct outline *item;
5452 if (!reserve((struct dynamic_array *)array, array->count + 1, sizeof(array->items[0])))
5453 return NULL;
5455 item = &array->items[array->count++];
5456 ZeroMemory(item, sizeof(*item));
5457 return item;
5460 static inline face *add_face(struct face_array *array)
5462 return &array->items[array->count++];
5465 static struct triangulation *add_triangulation(struct triangulation_array *array)
5467 struct triangulation *item;
5469 if (!reserve((struct dynamic_array *)array, array->count + 1, sizeof(array->items[0])))
5470 return NULL;
5472 item = &array->items[array->count++];
5473 ZeroMemory(item, sizeof(*item));
5474 return item;
5477 static HRESULT add_vertex_index(struct word_array *array, WORD vertex_index)
5479 if (!reserve((struct dynamic_array *)array, array->count + 1, sizeof(array->items[0])))
5480 return E_OUTOFMEMORY;
5482 array->items[array->count++] = vertex_index;
5483 return S_OK;
5486 /* assume fixed point numbers can be converted to float point in place */
5487 C_ASSERT(sizeof(FIXED) == sizeof(float));
5488 C_ASSERT(sizeof(POINTFX) == sizeof(D3DXVECTOR2));
5490 static inline D3DXVECTOR2 *convert_fixed_to_float(POINTFX *pt, int count, unsigned int emsquare)
5492 D3DXVECTOR2 *ret = (D3DXVECTOR2*)pt;
5493 while (count--) {
5494 D3DXVECTOR2 *pt_flt = (D3DXVECTOR2*)pt;
5495 pt_flt->x = (pt->x.value + pt->x.fract / (float)0x10000) / emsquare;
5496 pt_flt->y = (pt->y.value + pt->y.fract / (float)0x10000) / emsquare;
5497 pt++;
5499 return ret;
5502 static HRESULT add_bezier_points(struct outline *outline, const D3DXVECTOR2 *p1,
5503 const D3DXVECTOR2 *p2, const D3DXVECTOR2 *p3,
5504 float max_deviation_sq)
5506 D3DXVECTOR2 split1 = {0, 0}, split2 = {0, 0}, middle, vec;
5507 float deviation_sq;
5509 D3DXVec2Scale(&split1, D3DXVec2Add(&split1, p1, p2), 0.5f);
5510 D3DXVec2Scale(&split2, D3DXVec2Add(&split2, p2, p3), 0.5f);
5511 D3DXVec2Scale(&middle, D3DXVec2Add(&middle, &split1, &split2), 0.5f);
5513 deviation_sq = D3DXVec2LengthSq(D3DXVec2Subtract(&vec, &middle, p2));
5514 if (deviation_sq < max_deviation_sq) {
5515 struct point2d *pt = add_points(outline, 1);
5516 if (!pt) return E_OUTOFMEMORY;
5517 pt->pos = *p2;
5518 pt->corner = POINTTYPE_CURVE;
5519 /* the end point is omitted because the end line merges into the next segment of
5520 * the split bezier curve, and the end of the split bezier curve is added outside
5521 * this recursive function. */
5522 } else {
5523 HRESULT hr = add_bezier_points(outline, p1, &split1, &middle, max_deviation_sq);
5524 if (hr != S_OK) return hr;
5525 hr = add_bezier_points(outline, &middle, &split2, p3, max_deviation_sq);
5526 if (hr != S_OK) return hr;
5529 return S_OK;
5532 static inline BOOL is_direction_similar(D3DXVECTOR2 *dir1, D3DXVECTOR2 *dir2, float cos_theta)
5534 /* dot product = cos(theta) */
5535 return D3DXVec2Dot(dir1, dir2) > cos_theta;
5538 static inline D3DXVECTOR2 *unit_vec2(D3DXVECTOR2 *dir, const D3DXVECTOR2 *pt1, const D3DXVECTOR2 *pt2)
5540 return D3DXVec2Normalize(dir, D3DXVec2Subtract(dir, pt2, pt1));
5543 struct cos_table
5545 float cos_half;
5546 float cos_45;
5547 float cos_90;
5550 static BOOL attempt_line_merge(struct outline *outline,
5551 int pt_index,
5552 const D3DXVECTOR2 *nextpt,
5553 BOOL to_curve,
5554 const struct cos_table *table)
5556 D3DXVECTOR2 curdir, lastdir;
5557 struct point2d *prevpt, *pt;
5558 BOOL ret = FALSE;
5560 pt = &outline->items[pt_index];
5561 pt_index = (pt_index - 1 + outline->count) % outline->count;
5562 prevpt = &outline->items[pt_index];
5564 if (to_curve)
5565 pt->corner = pt->corner != POINTTYPE_CORNER ? POINTTYPE_CURVE_MIDDLE : POINTTYPE_CURVE_START;
5567 if (outline->count < 2)
5568 return FALSE;
5570 /* remove last point if the next line continues the last line */
5571 unit_vec2(&lastdir, &prevpt->pos, &pt->pos);
5572 unit_vec2(&curdir, &pt->pos, nextpt);
5573 if (is_direction_similar(&lastdir, &curdir, table->cos_half))
5575 outline->count--;
5576 if (pt->corner == POINTTYPE_CURVE_END)
5577 prevpt->corner = pt->corner;
5578 if (prevpt->corner == POINTTYPE_CURVE_END && to_curve)
5579 prevpt->corner = POINTTYPE_CURVE_MIDDLE;
5580 pt = prevpt;
5582 ret = TRUE;
5583 if (outline->count < 2)
5584 return ret;
5586 pt_index = (pt_index - 1 + outline->count) % outline->count;
5587 prevpt = &outline->items[pt_index];
5588 unit_vec2(&lastdir, &prevpt->pos, &pt->pos);
5589 unit_vec2(&curdir, &pt->pos, nextpt);
5591 return ret;
5594 static HRESULT create_outline(struct glyphinfo *glyph, void *raw_outline, int datasize,
5595 float max_deviation_sq, unsigned int emsquare,
5596 const struct cos_table *cos_table)
5598 TTPOLYGONHEADER *header = (TTPOLYGONHEADER *)raw_outline;
5600 while ((char *)header < (char *)raw_outline + datasize)
5602 TTPOLYCURVE *curve = (TTPOLYCURVE *)(header + 1);
5603 struct point2d *lastpt, *pt;
5604 D3DXVECTOR2 lastdir;
5605 D3DXVECTOR2 *pt_flt;
5606 int j;
5607 struct outline *outline = add_outline(&glyph->outlines);
5609 if (!outline)
5610 return E_OUTOFMEMORY;
5612 pt = add_points(outline, 1);
5613 if (!pt)
5614 return E_OUTOFMEMORY;
5615 pt_flt = convert_fixed_to_float(&header->pfxStart, 1, emsquare);
5616 pt->pos = *pt_flt;
5617 pt->corner = POINTTYPE_CORNER;
5619 if (header->dwType != TT_POLYGON_TYPE)
5620 FIXME("Unknown header type %lu.\n", header->dwType);
5622 while ((char *)curve < (char *)header + header->cb)
5624 D3DXVECTOR2 bezier_start = outline->items[outline->count - 1].pos;
5625 BOOL to_curve = curve->wType != TT_PRIM_LINE && curve->cpfx > 1;
5626 unsigned int j2 = 0;
5628 if (!curve->cpfx) {
5629 curve = (TTPOLYCURVE *)&curve->apfx[curve->cpfx];
5630 continue;
5633 pt_flt = convert_fixed_to_float(curve->apfx, curve->cpfx, emsquare);
5635 attempt_line_merge(outline, outline->count - 1, &pt_flt[0], to_curve, cos_table);
5637 if (to_curve)
5639 HRESULT hr;
5640 int count = curve->cpfx;
5642 while (count > 2)
5644 D3DXVECTOR2 bezier_end;
5646 D3DXVec2Scale(&bezier_end, D3DXVec2Add(&bezier_end, &pt_flt[j2], &pt_flt[j2+1]), 0.5f);
5647 hr = add_bezier_points(outline, &bezier_start, &pt_flt[j2], &bezier_end, max_deviation_sq);
5648 if (hr != S_OK)
5649 return hr;
5650 bezier_start = bezier_end;
5651 count--;
5652 j2++;
5654 hr = add_bezier_points(outline, &bezier_start, &pt_flt[j2], &pt_flt[j2+1], max_deviation_sq);
5655 if (hr != S_OK)
5656 return hr;
5658 pt = add_points(outline, 1);
5659 if (!pt)
5660 return E_OUTOFMEMORY;
5661 j2++;
5662 pt->pos = pt_flt[j2];
5663 pt->corner = POINTTYPE_CURVE_END;
5664 } else {
5665 pt = add_points(outline, curve->cpfx);
5666 if (!pt)
5667 return E_OUTOFMEMORY;
5668 for (j2 = 0; j2 < curve->cpfx; j2++)
5670 pt->pos = pt_flt[j2];
5671 pt->corner = POINTTYPE_CORNER;
5672 pt++;
5676 curve = (TTPOLYCURVE *)&curve->apfx[curve->cpfx];
5679 /* remove last point if the next line continues the last line */
5680 if (outline->count >= 3) {
5681 BOOL to_curve;
5683 lastpt = &outline->items[outline->count - 1];
5684 pt = &outline->items[0];
5685 if (pt->pos.x == lastpt->pos.x && pt->pos.y == lastpt->pos.y) {
5686 if (lastpt->corner == POINTTYPE_CURVE_END)
5688 if (pt->corner == POINTTYPE_CURVE_START)
5689 pt->corner = POINTTYPE_CURVE_MIDDLE;
5690 else
5691 pt->corner = POINTTYPE_CURVE_END;
5693 outline->count--;
5694 } else {
5695 /* outline closed with a line from end to start point */
5696 attempt_line_merge(outline, outline->count - 1, &pt->pos, FALSE, cos_table);
5698 lastpt = &outline->items[0];
5699 to_curve = lastpt->corner != POINTTYPE_CORNER && lastpt->corner != POINTTYPE_CURVE_END;
5700 if (lastpt->corner == POINTTYPE_CURVE_START)
5701 lastpt->corner = POINTTYPE_CORNER;
5702 pt = &outline->items[1];
5703 if (attempt_line_merge(outline, 0, &pt->pos, to_curve, cos_table))
5704 *lastpt = outline->items[outline->count];
5707 lastpt = &outline->items[outline->count - 1];
5708 pt = &outline->items[0];
5709 unit_vec2(&lastdir, &lastpt->pos, &pt->pos);
5710 for (j = 0; j < outline->count; j++)
5712 D3DXVECTOR2 curdir;
5714 lastpt = pt;
5715 pt = &outline->items[(j + 1) % outline->count];
5716 unit_vec2(&curdir, &lastpt->pos, &pt->pos);
5718 switch (lastpt->corner)
5720 case POINTTYPE_CURVE_START:
5721 case POINTTYPE_CURVE_END:
5722 if (!is_direction_similar(&lastdir, &curdir, cos_table->cos_45))
5723 lastpt->corner = POINTTYPE_CORNER;
5724 break;
5725 case POINTTYPE_CURVE_MIDDLE:
5726 if (!is_direction_similar(&lastdir, &curdir, cos_table->cos_90))
5727 lastpt->corner = POINTTYPE_CORNER;
5728 else
5729 lastpt->corner = POINTTYPE_CURVE;
5730 break;
5731 default:
5732 break;
5734 lastdir = curdir;
5737 header = (TTPOLYGONHEADER *)((char *)header + header->cb);
5739 return S_OK;
5742 /* Get the y-distance from a line to a point */
5743 static float get_line_to_point_y_distance(D3DXVECTOR2 *line_pt1,
5744 D3DXVECTOR2 *line_pt2,
5745 D3DXVECTOR2 *point)
5747 D3DXVECTOR2 line_vec = {0, 0};
5748 float line_pt_dx;
5749 float line_y;
5751 D3DXVec2Subtract(&line_vec, line_pt2, line_pt1);
5752 line_pt_dx = point->x - line_pt1->x;
5753 line_y = line_pt1->y + (line_vec.y * line_pt_dx) / line_vec.x;
5754 return point->y - line_y;
5757 static D3DXVECTOR2 *get_indexed_point(struct point2d_index *pt_idx)
5759 return &pt_idx->outline->items[pt_idx->vertex].pos;
5762 static D3DXVECTOR2 *get_ordered_vertex(struct glyphinfo *glyph, WORD index)
5764 return get_indexed_point(&glyph->ordered_vertices.items[index]);
5767 static void remove_triangulation(struct triangulation_array *array, struct triangulation *item)
5769 HeapFree(GetProcessHeap(), 0, item->vertex_stack.items);
5770 MoveMemory(item, item + 1, (char*)&array->items[array->count] - (char*)(item + 1));
5771 array->count--;
5774 static HRESULT triangulation_add_point(struct triangulation **t_ptr,
5775 struct triangulation_array *triangulations,
5776 WORD vtx_idx,
5777 BOOL to_top)
5779 struct glyphinfo *glyph = triangulations->glyph;
5780 struct triangulation *t = *t_ptr;
5781 HRESULT hr;
5782 face *face;
5783 int f1, f2;
5785 if (t->last_on_top) {
5786 f1 = 1;
5787 f2 = 2;
5788 } else {
5789 f1 = 2;
5790 f2 = 1;
5793 if (t->last_on_top != to_top && t->vertex_stack.count > 1) {
5794 /* consume all vertices on the stack */
5795 WORD last_pt = t->vertex_stack.items[0];
5796 int i;
5797 for (i = 1; i < t->vertex_stack.count; i++)
5799 face = add_face(&glyph->faces);
5800 if (!face) return E_OUTOFMEMORY;
5801 (*face)[0] = vtx_idx;
5802 (*face)[f1] = last_pt;
5803 (*face)[f2] = last_pt = t->vertex_stack.items[i];
5805 t->vertex_stack.items[0] = last_pt;
5806 t->vertex_stack.count = 1;
5807 } else if (t->vertex_stack.count > 1) {
5808 int i = t->vertex_stack.count - 1;
5809 D3DXVECTOR2 *point = get_ordered_vertex(glyph, vtx_idx);
5810 WORD top_idx = t->vertex_stack.items[i--];
5811 D3DXVECTOR2 *top_pt = get_ordered_vertex(glyph, top_idx);
5813 while (i >= 0)
5815 WORD prev_idx = t->vertex_stack.items[i--];
5816 D3DXVECTOR2 *prev_pt = get_ordered_vertex(glyph, prev_idx);
5818 if (prev_pt->x != top_pt->x &&
5819 ((to_top && get_line_to_point_y_distance(prev_pt, top_pt, point) > 0) ||
5820 (!to_top && get_line_to_point_y_distance(prev_pt, top_pt, point) < 0)))
5821 break;
5823 face = add_face(&glyph->faces);
5824 if (!face) return E_OUTOFMEMORY;
5825 (*face)[0] = vtx_idx;
5826 (*face)[f1] = prev_idx;
5827 (*face)[f2] = top_idx;
5829 top_pt = prev_pt;
5830 top_idx = prev_idx;
5831 t->vertex_stack.count--;
5834 t->last_on_top = to_top;
5836 hr = add_vertex_index(&t->vertex_stack, vtx_idx);
5838 if (hr == S_OK && t->merging) {
5839 struct triangulation *t2;
5841 t2 = to_top ? t - 1 : t + 1;
5842 t2->merging = FALSE;
5843 hr = triangulation_add_point(&t2, triangulations, vtx_idx, to_top);
5844 if (hr != S_OK) return hr;
5845 remove_triangulation(triangulations, t);
5846 if (t2 > t)
5847 t2--;
5848 *t_ptr = t2;
5850 return hr;
5853 /* check if the point is next on the outline for either the top or bottom */
5854 static D3DXVECTOR2 *triangulation_get_next_point(struct triangulation *t, struct glyphinfo *glyph, BOOL on_top)
5856 int i = t->last_on_top == on_top ? t->vertex_stack.count - 1 : 0;
5857 WORD idx = t->vertex_stack.items[i];
5858 struct point2d_index *pt_idx = &glyph->ordered_vertices.items[idx];
5859 struct outline *outline = pt_idx->outline;
5861 if (on_top)
5862 i = (pt_idx->vertex + outline->count - 1) % outline->count;
5863 else
5864 i = (pt_idx->vertex + 1) % outline->count;
5866 return &outline->items[i].pos;
5869 static int __cdecl compare_vertex_indices(const void *a, const void *b)
5871 const struct point2d_index *idx1 = a, *idx2 = b;
5872 const D3DXVECTOR2 *p1 = &idx1->outline->items[idx1->vertex].pos;
5873 const D3DXVECTOR2 *p2 = &idx2->outline->items[idx2->vertex].pos;
5874 float diff = p1->x - p2->x;
5876 if (diff == 0.0f)
5877 diff = p1->y - p2->y;
5879 return diff == 0.0f ? 0 : (diff > 0.0f ? -1 : 1);
5882 static HRESULT triangulate(struct triangulation_array *triangulations)
5884 int sweep_idx;
5885 HRESULT hr;
5886 struct glyphinfo *glyph = triangulations->glyph;
5887 int nb_vertices = 0;
5888 int i;
5889 struct point2d_index *idx_ptr;
5891 /* Glyphs without outlines do not generate any vertices. */
5892 if (!glyph->outlines.count)
5893 return D3D_OK;
5895 for (i = 0; i < glyph->outlines.count; i++)
5896 nb_vertices += glyph->outlines.items[i].count;
5898 glyph->ordered_vertices.items = HeapAlloc(GetProcessHeap(), 0,
5899 nb_vertices * sizeof(*glyph->ordered_vertices.items));
5900 if (!glyph->ordered_vertices.items)
5901 return E_OUTOFMEMORY;
5903 idx_ptr = glyph->ordered_vertices.items;
5904 for (i = 0; i < glyph->outlines.count; i++)
5906 struct outline *outline = &glyph->outlines.items[i];
5907 int j;
5909 idx_ptr->outline = outline;
5910 idx_ptr->vertex = 0;
5911 idx_ptr++;
5912 for (j = outline->count - 1; j > 0; j--)
5914 idx_ptr->outline = outline;
5915 idx_ptr->vertex = j;
5916 idx_ptr++;
5919 glyph->ordered_vertices.count = nb_vertices;
5921 /* Native implementation seems to try to create a triangle fan from
5922 * the first outline point if the glyph only has one outline. */
5923 if (glyph->outlines.count == 1)
5925 struct outline *outline = glyph->outlines.items;
5926 D3DXVECTOR2 *base = &outline->items[0].pos;
5927 D3DXVECTOR2 *last = &outline->items[1].pos;
5928 float ccw = 0;
5930 for (i = 2; i < outline->count; i++)
5932 D3DXVECTOR2 *next = &outline->items[i].pos;
5933 D3DXVECTOR2 v1 = {0.0f, 0.0f};
5934 D3DXVECTOR2 v2 = {0.0f, 0.0f};
5936 D3DXVec2Subtract(&v1, base, last);
5937 D3DXVec2Subtract(&v2, last, next);
5938 ccw = D3DXVec2CCW(&v1, &v2);
5939 if (ccw > 0.0f)
5940 break;
5942 last = next;
5944 if (ccw <= 0)
5946 glyph->faces.items = HeapAlloc(GetProcessHeap(), 0,
5947 (outline->count - 2) * sizeof(glyph->faces.items[0]));
5948 if (!glyph->faces.items)
5949 return E_OUTOFMEMORY;
5951 glyph->faces.count = outline->count - 2;
5952 for (i = 0; i < glyph->faces.count; i++)
5954 glyph->faces.items[i][0] = 0;
5955 glyph->faces.items[i][1] = i + 1;
5956 glyph->faces.items[i][2] = i + 2;
5958 return S_OK;
5962 /* Perform 2D polygon triangulation for complex glyphs.
5963 * Triangulation is performed using a sweep line concept, from right to left,
5964 * by processing vertices in sorted order. Complex polygons are split into
5965 * monotone polygons which are triangulated separately. */
5966 /* FIXME: The order of the faces is not consistent with the native implementation. */
5968 /* Reserve space for maximum possible faces from triangulation.
5969 * # faces for outer outlines = outline->count - 2
5970 * # faces for inner outlines = outline->count + 2
5971 * There must be at least 1 outer outline. */
5972 glyph->faces.items = HeapAlloc(GetProcessHeap(), 0,
5973 (nb_vertices + glyph->outlines.count * 2 - 4) * sizeof(glyph->faces.items[0]));
5974 if (!glyph->faces.items)
5975 return E_OUTOFMEMORY;
5977 qsort(glyph->ordered_vertices.items, nb_vertices,
5978 sizeof(glyph->ordered_vertices.items[0]), compare_vertex_indices);
5979 for (sweep_idx = 0; sweep_idx < glyph->ordered_vertices.count; sweep_idx++)
5981 int start = 0;
5982 int end = triangulations->count;
5984 while (start < end)
5986 D3DXVECTOR2 *sweep_vtx = get_ordered_vertex(glyph, sweep_idx);
5987 int current = (start + end) / 2;
5988 struct triangulation *t = &triangulations->items[current];
5989 BOOL on_top_outline = FALSE;
5990 D3DXVECTOR2 *top_next, *bottom_next;
5991 WORD top_idx, bottom_idx;
5993 if (t->merging && t->last_on_top)
5994 top_next = triangulation_get_next_point(t + 1, glyph, TRUE);
5995 else
5996 top_next = triangulation_get_next_point(t, glyph, TRUE);
5997 if (sweep_vtx == top_next)
5999 if (t->merging && t->last_on_top)
6000 t++;
6001 hr = triangulation_add_point(&t, triangulations, sweep_idx, TRUE);
6002 if (hr != S_OK) return hr;
6004 if (t + 1 < &triangulations->items[triangulations->count] &&
6005 triangulation_get_next_point(t + 1, glyph, FALSE) == sweep_vtx)
6007 /* point also on bottom outline of higher triangulation */
6008 struct triangulation *t2 = t + 1;
6009 hr = triangulation_add_point(&t2, triangulations, sweep_idx, FALSE);
6010 if (hr != S_OK) return hr;
6012 t->merging = TRUE;
6013 t2->merging = TRUE;
6015 on_top_outline = TRUE;
6018 if (t->merging && !t->last_on_top)
6019 bottom_next = triangulation_get_next_point(t - 1, glyph, FALSE);
6020 else
6021 bottom_next = triangulation_get_next_point(t, glyph, FALSE);
6022 if (sweep_vtx == bottom_next)
6024 if (t->merging && !t->last_on_top)
6025 t--;
6026 if (on_top_outline) {
6027 /* outline finished */
6028 remove_triangulation(triangulations, t);
6029 break;
6032 hr = triangulation_add_point(&t, triangulations, sweep_idx, FALSE);
6033 if (hr != S_OK) return hr;
6035 if (t > triangulations->items &&
6036 triangulation_get_next_point(t - 1, glyph, TRUE) == sweep_vtx)
6038 struct triangulation *t2 = t - 1;
6039 /* point also on top outline of lower triangulation */
6040 hr = triangulation_add_point(&t2, triangulations, sweep_idx, TRUE);
6041 if (hr != S_OK) return hr;
6042 t = t2 + 1; /* t may be invalidated by triangulation merging */
6044 t->merging = TRUE;
6045 t2->merging = TRUE;
6047 break;
6049 if (on_top_outline)
6050 break;
6052 if (t->last_on_top) {
6053 top_idx = t->vertex_stack.items[t->vertex_stack.count - 1];
6054 bottom_idx = t->vertex_stack.items[0];
6055 } else {
6056 top_idx = t->vertex_stack.items[0];
6057 bottom_idx = t->vertex_stack.items[t->vertex_stack.count - 1];
6060 /* check if the point is inside or outside this polygon */
6061 if (get_line_to_point_y_distance(get_ordered_vertex(glyph, top_idx),
6062 top_next, sweep_vtx) > 0)
6063 { /* above */
6064 start = current + 1;
6065 } else if (get_line_to_point_y_distance(get_ordered_vertex(glyph, bottom_idx),
6066 bottom_next, sweep_vtx) < 0)
6067 { /* below */
6068 end = current;
6069 } else if (t->merging) {
6070 /* inside, so cancel merging */
6071 struct triangulation *t2 = t->last_on_top ? t + 1 : t - 1;
6072 t->merging = FALSE;
6073 t2->merging = FALSE;
6074 hr = triangulation_add_point(&t, triangulations, sweep_idx, t->last_on_top);
6075 if (hr != S_OK) return hr;
6076 hr = triangulation_add_point(&t2, triangulations, sweep_idx, t2->last_on_top);
6077 if (hr != S_OK) return hr;
6078 break;
6079 } else {
6080 /* inside, so split polygon into two monotone parts */
6081 struct triangulation *t2 = add_triangulation(triangulations);
6082 if (!t2) return E_OUTOFMEMORY;
6083 MoveMemory(t + 1, t, (char*)(t2 + 1) - (char*)t);
6084 if (t->last_on_top) {
6085 t2 = t + 1;
6086 } else {
6087 t2 = t;
6088 t++;
6091 ZeroMemory(&t2->vertex_stack, sizeof(t2->vertex_stack));
6092 hr = add_vertex_index(&t2->vertex_stack, t->vertex_stack.items[t->vertex_stack.count - 1]);
6093 if (hr != S_OK) return hr;
6094 hr = add_vertex_index(&t2->vertex_stack, sweep_idx);
6095 if (hr != S_OK) return hr;
6096 t2->last_on_top = !t->last_on_top;
6098 hr = triangulation_add_point(&t, triangulations, sweep_idx, t->last_on_top);
6099 if (hr != S_OK) return hr;
6100 break;
6103 if (start >= end)
6105 struct triangulation *t;
6106 struct triangulation *t2 = add_triangulation(triangulations);
6107 if (!t2) return E_OUTOFMEMORY;
6108 t = &triangulations->items[start];
6109 MoveMemory(t + 1, t, (char*)(t2 + 1) - (char*)t);
6110 ZeroMemory(t, sizeof(*t));
6111 hr = add_vertex_index(&t->vertex_stack, sweep_idx);
6112 if (hr != S_OK) return hr;
6115 return S_OK;
6118 HRESULT WINAPI D3DXCreateTextW(struct IDirect3DDevice9 *device, HDC hdc, const WCHAR *text, float deviation,
6119 float extrusion, struct ID3DXMesh **mesh_ptr, struct ID3DXBuffer **adjacency, GLYPHMETRICSFLOAT *glyphmetrics)
6121 HRESULT hr;
6122 ID3DXMesh *mesh = NULL;
6123 DWORD nb_vertices, nb_faces;
6124 DWORD nb_front_faces, nb_corners, nb_outline_points;
6125 struct vertex *vertices = NULL;
6126 face *faces = NULL;
6127 int textlen = 0;
6128 float offset_x;
6129 LOGFONTW lf;
6130 OUTLINETEXTMETRICW otm;
6131 HFONT font = NULL, oldfont = NULL;
6132 const MAT2 identity = {{0, 1}, {0, 0}, {0, 0}, {0, 1}};
6133 void *raw_outline = NULL;
6134 int bufsize = 0;
6135 struct glyphinfo *glyphs = NULL;
6136 GLYPHMETRICS gm;
6137 struct triangulation_array triangulations = {0, 0, NULL};
6138 int i;
6139 struct vertex *vertex_ptr;
6140 face *face_ptr;
6141 float max_deviation_sq;
6142 const struct cos_table cos_table = {
6143 cosf(D3DXToRadian(0.5f)),
6144 cosf(D3DXToRadian(45.0f)),
6145 cosf(D3DXToRadian(90.0f)),
6147 int f1, f2;
6149 TRACE("(%p, %p, %s, %f, %f, %p, %p, %p)\n", device, hdc,
6150 debugstr_w(text), deviation, extrusion, mesh_ptr, adjacency, glyphmetrics);
6152 if (!device || !hdc || !text || !*text || deviation < 0.0f || extrusion < 0.0f || !mesh_ptr)
6153 return D3DERR_INVALIDCALL;
6155 if (adjacency)
6157 FIXME("Case of adjacency != NULL not implemented.\n");
6158 return E_NOTIMPL;
6161 if (!GetObjectW(GetCurrentObject(hdc, OBJ_FONT), sizeof(lf), &lf) ||
6162 !GetOutlineTextMetricsW(hdc, sizeof(otm), &otm))
6164 return D3DERR_INVALIDCALL;
6167 if (deviation == 0.0f)
6168 deviation = 1.0f / otm.otmEMSquare;
6169 max_deviation_sq = deviation * deviation;
6171 lf.lfHeight = otm.otmEMSquare;
6172 lf.lfWidth = 0;
6173 font = CreateFontIndirectW(&lf);
6174 if (!font) {
6175 hr = E_OUTOFMEMORY;
6176 goto error;
6178 oldfont = SelectObject(hdc, font);
6180 textlen = lstrlenW(text);
6181 for (i = 0; i < textlen; i++)
6183 int datasize = GetGlyphOutlineW(hdc, text[i], GGO_NATIVE, &gm, 0, NULL, &identity);
6184 if (datasize < 0)
6185 return D3DERR_INVALIDCALL;
6186 if (bufsize < datasize)
6187 bufsize = datasize;
6189 if (!bufsize) { /* e.g. text == " " */
6190 hr = D3DERR_INVALIDCALL;
6191 goto error;
6194 glyphs = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, textlen * sizeof(*glyphs));
6195 raw_outline = HeapAlloc(GetProcessHeap(), 0, bufsize);
6196 if (!glyphs || !raw_outline) {
6197 hr = E_OUTOFMEMORY;
6198 goto error;
6201 offset_x = 0.0f;
6202 for (i = 0; i < textlen; i++)
6204 /* get outline points from data returned from GetGlyphOutline */
6205 int datasize;
6207 glyphs[i].offset_x = offset_x;
6209 datasize = GetGlyphOutlineW(hdc, text[i], GGO_NATIVE, &gm, bufsize, raw_outline, &identity);
6210 hr = create_outline(&glyphs[i], raw_outline, datasize,
6211 max_deviation_sq, otm.otmEMSquare, &cos_table);
6212 if (hr != S_OK) goto error;
6214 triangulations.glyph = &glyphs[i];
6215 hr = triangulate(&triangulations);
6216 if (hr != S_OK) goto error;
6217 if (triangulations.count) {
6218 ERR("%d incomplete triangulations of glyph (%u).\n", triangulations.count, text[i]);
6219 triangulations.count = 0;
6222 if (glyphmetrics)
6224 glyphmetrics[i].gmfBlackBoxX = gm.gmBlackBoxX / (float)otm.otmEMSquare;
6225 glyphmetrics[i].gmfBlackBoxY = gm.gmBlackBoxY / (float)otm.otmEMSquare;
6226 glyphmetrics[i].gmfptGlyphOrigin.x = gm.gmptGlyphOrigin.x / (float)otm.otmEMSquare;
6227 glyphmetrics[i].gmfptGlyphOrigin.y = gm.gmptGlyphOrigin.y / (float)otm.otmEMSquare;
6228 glyphmetrics[i].gmfCellIncX = gm.gmCellIncX / (float)otm.otmEMSquare;
6229 glyphmetrics[i].gmfCellIncY = gm.gmCellIncY / (float)otm.otmEMSquare;
6231 offset_x += gm.gmCellIncX / (float)otm.otmEMSquare;
6234 /* corner points need an extra vertex for the different side faces normals */
6235 nb_corners = 0;
6236 nb_outline_points = 0;
6237 nb_front_faces = 0;
6238 for (i = 0; i < textlen; i++)
6240 int j;
6241 nb_outline_points += glyphs[i].ordered_vertices.count;
6242 nb_front_faces += glyphs[i].faces.count;
6243 for (j = 0; j < glyphs[i].outlines.count; j++)
6245 int k;
6246 struct outline *outline = &glyphs[i].outlines.items[j];
6247 nb_corners++; /* first outline point always repeated as a corner */
6248 for (k = 1; k < outline->count; k++)
6249 if (outline->items[k].corner)
6250 nb_corners++;
6254 nb_vertices = (nb_outline_points + nb_corners) * 2 + nb_outline_points * 2;
6255 nb_faces = nb_outline_points * 2 + nb_front_faces * 2;
6258 hr = D3DXCreateMeshFVF(nb_faces, nb_vertices, D3DXMESH_MANAGED,
6259 D3DFVF_XYZ | D3DFVF_NORMAL, device, &mesh);
6260 if (FAILED(hr))
6261 goto error;
6263 if (FAILED(hr = mesh->lpVtbl->LockVertexBuffer(mesh, 0, (void **)&vertices)))
6264 goto error;
6266 if (FAILED(hr = mesh->lpVtbl->LockIndexBuffer(mesh, 0, (void **)&faces)))
6267 goto error;
6269 /* convert 2D vertices and faces into 3D mesh */
6270 vertex_ptr = vertices;
6271 face_ptr = faces;
6272 if (extrusion == 0.0f) {
6273 f1 = 1;
6274 f2 = 2;
6275 } else {
6276 f1 = 2;
6277 f2 = 1;
6279 for (i = 0; i < textlen; i++)
6281 int j;
6282 int count;
6283 struct vertex *back_vertices;
6284 face *back_faces;
6286 /* side vertices and faces */
6287 for (j = 0; j < glyphs[i].outlines.count; j++)
6289 struct vertex *outline_vertices = vertex_ptr;
6290 struct outline *outline = &glyphs[i].outlines.items[j];
6291 int k;
6292 struct point2d *prevpt = &outline->items[outline->count - 1];
6293 struct point2d *pt = &outline->items[0];
6295 for (k = 1; k <= outline->count; k++)
6297 struct vertex vtx;
6298 struct point2d *nextpt = &outline->items[k % outline->count];
6299 WORD vtx_idx = vertex_ptr - vertices;
6300 D3DXVECTOR2 vec;
6302 if (pt->corner == POINTTYPE_CURVE_START)
6303 D3DXVec2Subtract(&vec, &pt->pos, &prevpt->pos);
6304 else if (pt->corner)
6305 D3DXVec2Subtract(&vec, &nextpt->pos, &pt->pos);
6306 else
6307 D3DXVec2Subtract(&vec, &nextpt->pos, &prevpt->pos);
6308 D3DXVec2Normalize(&vec, &vec);
6309 vtx.normal.x = -vec.y;
6310 vtx.normal.y = vec.x;
6311 vtx.normal.z = 0;
6313 vtx.position.x = pt->pos.x + glyphs[i].offset_x;
6314 vtx.position.y = pt->pos.y;
6315 vtx.position.z = 0;
6316 *vertex_ptr++ = vtx;
6318 vtx.position.z = -extrusion;
6319 *vertex_ptr++ = vtx;
6321 vtx.position.x = nextpt->pos.x + glyphs[i].offset_x;
6322 vtx.position.y = nextpt->pos.y;
6323 if (pt->corner && nextpt->corner && nextpt->corner != POINTTYPE_CURVE_END) {
6324 vtx.position.z = -extrusion;
6325 *vertex_ptr++ = vtx;
6326 vtx.position.z = 0;
6327 *vertex_ptr++ = vtx;
6329 (*face_ptr)[0] = vtx_idx;
6330 (*face_ptr)[1] = vtx_idx + 2;
6331 (*face_ptr)[2] = vtx_idx + 1;
6332 face_ptr++;
6334 (*face_ptr)[0] = vtx_idx;
6335 (*face_ptr)[1] = vtx_idx + 3;
6336 (*face_ptr)[2] = vtx_idx + 2;
6337 face_ptr++;
6338 } else {
6339 if (nextpt->corner) {
6340 if (nextpt->corner == POINTTYPE_CURVE_END) {
6341 D3DXVECTOR2 *nextpt2 = &outline->items[(k + 1) % outline->count].pos;
6342 D3DXVec2Subtract(&vec, nextpt2, &nextpt->pos);
6343 } else {
6344 D3DXVec2Subtract(&vec, &nextpt->pos, &pt->pos);
6346 D3DXVec2Normalize(&vec, &vec);
6347 vtx.normal.x = -vec.y;
6348 vtx.normal.y = vec.x;
6350 vtx.position.z = 0;
6351 *vertex_ptr++ = vtx;
6352 vtx.position.z = -extrusion;
6353 *vertex_ptr++ = vtx;
6356 (*face_ptr)[0] = vtx_idx;
6357 (*face_ptr)[1] = vtx_idx + 3;
6358 (*face_ptr)[2] = vtx_idx + 1;
6359 face_ptr++;
6361 (*face_ptr)[0] = vtx_idx;
6362 (*face_ptr)[1] = vtx_idx + 2;
6363 (*face_ptr)[2] = vtx_idx + 3;
6364 face_ptr++;
6367 prevpt = pt;
6368 pt = nextpt;
6370 if (!pt->corner) {
6371 *vertex_ptr++ = *outline_vertices++;
6372 *vertex_ptr++ = *outline_vertices++;
6376 /* back vertices and faces */
6377 back_faces = face_ptr;
6378 back_vertices = vertex_ptr;
6379 for (j = 0; j < glyphs[i].ordered_vertices.count; j++)
6381 D3DXVECTOR2 *pt = get_ordered_vertex(&glyphs[i], j);
6382 vertex_ptr->position.x = pt->x + glyphs[i].offset_x;
6383 vertex_ptr->position.y = pt->y;
6384 vertex_ptr->position.z = 0;
6385 vertex_ptr->normal.x = 0;
6386 vertex_ptr->normal.y = 0;
6387 vertex_ptr->normal.z = 1;
6388 vertex_ptr++;
6390 count = back_vertices - vertices;
6391 for (j = 0; j < glyphs[i].faces.count; j++)
6393 face *f = &glyphs[i].faces.items[j];
6394 (*face_ptr)[0] = (*f)[0] + count;
6395 (*face_ptr)[1] = (*f)[1] + count;
6396 (*face_ptr)[2] = (*f)[2] + count;
6397 face_ptr++;
6400 /* front vertices and faces */
6401 j = count = vertex_ptr - back_vertices;
6402 while (j--)
6404 vertex_ptr->position.x = back_vertices->position.x;
6405 vertex_ptr->position.y = back_vertices->position.y;
6406 vertex_ptr->position.z = -extrusion;
6407 vertex_ptr->normal.x = 0;
6408 vertex_ptr->normal.y = 0;
6409 vertex_ptr->normal.z = extrusion == 0.0f ? 1.0f : -1.0f;
6410 vertex_ptr++;
6411 back_vertices++;
6413 j = face_ptr - back_faces;
6414 while (j--)
6416 (*face_ptr)[0] = (*back_faces)[0] + count;
6417 (*face_ptr)[1] = (*back_faces)[f1] + count;
6418 (*face_ptr)[2] = (*back_faces)[f2] + count;
6419 face_ptr++;
6420 back_faces++;
6424 *mesh_ptr = mesh;
6425 hr = D3D_OK;
6426 error:
6427 if (mesh) {
6428 if (faces) mesh->lpVtbl->UnlockIndexBuffer(mesh);
6429 if (vertices) mesh->lpVtbl->UnlockVertexBuffer(mesh);
6430 if (hr != D3D_OK) mesh->lpVtbl->Release(mesh);
6432 if (glyphs) {
6433 for (i = 0; i < textlen; i++)
6435 int j;
6436 for (j = 0; j < glyphs[i].outlines.count; j++)
6437 HeapFree(GetProcessHeap(), 0, glyphs[i].outlines.items[j].items);
6438 HeapFree(GetProcessHeap(), 0, glyphs[i].outlines.items);
6439 HeapFree(GetProcessHeap(), 0, glyphs[i].faces.items);
6440 HeapFree(GetProcessHeap(), 0, glyphs[i].ordered_vertices.items);
6442 HeapFree(GetProcessHeap(), 0, glyphs);
6444 if (triangulations.items) {
6445 int i;
6446 for (i = 0; i < triangulations.count; i++)
6447 HeapFree(GetProcessHeap(), 0, triangulations.items[i].vertex_stack.items);
6448 HeapFree(GetProcessHeap(), 0, triangulations.items);
6450 HeapFree(GetProcessHeap(), 0, raw_outline);
6451 if (oldfont) SelectObject(hdc, oldfont);
6452 if (font) DeleteObject(font);
6454 return hr;
6457 HRESULT WINAPI D3DXValidMesh(ID3DXMesh *mesh, const DWORD *adjacency, ID3DXBuffer **errors_and_warnings)
6459 FIXME("(%p, %p, %p): stub\n", mesh, adjacency, *errors_and_warnings);
6461 return E_NOTIMPL;
6464 static BOOL weld_float1(void *to, void *from, FLOAT epsilon)
6466 FLOAT *v1 = to;
6467 FLOAT *v2 = from;
6469 if (fabsf(*v1 - *v2) <= epsilon)
6471 *v1 = *v2;
6473 return TRUE;
6476 return FALSE;
6479 static BOOL weld_float2(void *to, void *from, FLOAT epsilon)
6481 D3DXVECTOR2 *v1 = to;
6482 D3DXVECTOR2 *v2 = from;
6483 FLOAT diff_x = fabsf(v1->x - v2->x);
6484 FLOAT diff_y = fabsf(v1->y - v2->y);
6485 FLOAT max_abs_diff = max(diff_x, diff_y);
6487 if (max_abs_diff <= epsilon)
6489 memcpy(to, from, sizeof(D3DXVECTOR2));
6491 return TRUE;
6494 return FALSE;
6497 static BOOL weld_float3(void *to, void *from, FLOAT epsilon)
6499 D3DXVECTOR3 *v1 = to;
6500 D3DXVECTOR3 *v2 = from;
6501 FLOAT diff_x = fabsf(v1->x - v2->x);
6502 FLOAT diff_y = fabsf(v1->y - v2->y);
6503 FLOAT diff_z = fabsf(v1->z - v2->z);
6504 FLOAT max_abs_diff = max(diff_x, diff_y);
6505 max_abs_diff = max(diff_z, max_abs_diff);
6507 if (max_abs_diff <= epsilon)
6509 memcpy(to, from, sizeof(D3DXVECTOR3));
6511 return TRUE;
6514 return FALSE;
6517 static BOOL weld_float4(void *to, void *from, FLOAT epsilon)
6519 D3DXVECTOR4 *v1 = to;
6520 D3DXVECTOR4 *v2 = from;
6521 FLOAT diff_x = fabsf(v1->x - v2->x);
6522 FLOAT diff_y = fabsf(v1->y - v2->y);
6523 FLOAT diff_z = fabsf(v1->z - v2->z);
6524 FLOAT diff_w = fabsf(v1->w - v2->w);
6525 FLOAT max_abs_diff = max(diff_x, diff_y);
6526 max_abs_diff = max(diff_z, max_abs_diff);
6527 max_abs_diff = max(diff_w, max_abs_diff);
6529 if (max_abs_diff <= epsilon)
6531 memcpy(to, from, sizeof(D3DXVECTOR4));
6533 return TRUE;
6536 return FALSE;
6539 static BOOL weld_ubyte4(void *to, void *from, FLOAT epsilon)
6541 BYTE *b1 = to;
6542 BYTE *b2 = from;
6543 BYTE truncated_epsilon = (BYTE)epsilon;
6544 BYTE diff_x = b1[0] > b2[0] ? b1[0] - b2[0] : b2[0] - b1[0];
6545 BYTE diff_y = b1[1] > b2[1] ? b1[1] - b2[1] : b2[1] - b1[1];
6546 BYTE diff_z = b1[2] > b2[2] ? b1[2] - b2[2] : b2[2] - b1[2];
6547 BYTE diff_w = b1[3] > b2[3] ? b1[3] - b2[3] : b2[3] - b1[3];
6548 BYTE max_diff = max(diff_x, diff_y);
6549 max_diff = max(diff_z, max_diff);
6550 max_diff = max(diff_w, max_diff);
6552 if (max_diff <= truncated_epsilon)
6554 memcpy(to, from, 4 * sizeof(BYTE));
6556 return TRUE;
6559 return FALSE;
6562 static BOOL weld_ubyte4n(void *to, void *from, FLOAT epsilon)
6564 return weld_ubyte4(to, from, epsilon * UCHAR_MAX);
6567 static BOOL weld_d3dcolor(void *to, void *from, FLOAT epsilon)
6569 return weld_ubyte4n(to, from, epsilon);
6572 static BOOL weld_short2(void *to, void *from, FLOAT epsilon)
6574 SHORT *s1 = to;
6575 SHORT *s2 = from;
6576 SHORT truncated_epsilon = (SHORT)epsilon;
6577 SHORT diff_x = abs(s1[0] - s2[0]);
6578 SHORT diff_y = abs(s1[1] - s2[1]);
6579 SHORT max_abs_diff = max(diff_x, diff_y);
6581 if (max_abs_diff <= truncated_epsilon)
6583 memcpy(to, from, 2 * sizeof(SHORT));
6585 return TRUE;
6588 return FALSE;
6591 static BOOL weld_short2n(void *to, void *from, FLOAT epsilon)
6593 return weld_short2(to, from, epsilon * SHRT_MAX);
6596 static BOOL weld_short4(void *to, void *from, FLOAT epsilon)
6598 SHORT *s1 = to;
6599 SHORT *s2 = from;
6600 SHORT truncated_epsilon = (SHORT)epsilon;
6601 SHORT diff_x = abs(s1[0] - s2[0]);
6602 SHORT diff_y = abs(s1[1] - s2[1]);
6603 SHORT diff_z = abs(s1[2] - s2[2]);
6604 SHORT diff_w = abs(s1[3] - s2[3]);
6605 SHORT max_abs_diff = max(diff_x, diff_y);
6606 max_abs_diff = max(diff_z, max_abs_diff);
6607 max_abs_diff = max(diff_w, max_abs_diff);
6609 if (max_abs_diff <= truncated_epsilon)
6611 memcpy(to, from, 4 * sizeof(SHORT));
6613 return TRUE;
6616 return FALSE;
6619 static BOOL weld_short4n(void *to, void *from, FLOAT epsilon)
6621 return weld_short4(to, from, epsilon * SHRT_MAX);
6624 static BOOL weld_ushort2n(void *to, void *from, FLOAT epsilon)
6626 USHORT *s1 = to;
6627 USHORT *s2 = from;
6628 USHORT scaled_epsilon = (USHORT)(epsilon * USHRT_MAX);
6629 USHORT diff_x = s1[0] > s2[0] ? s1[0] - s2[0] : s2[0] - s1[0];
6630 USHORT diff_y = s1[1] > s2[1] ? s1[1] - s2[1] : s2[1] - s1[1];
6631 USHORT max_diff = max(diff_x, diff_y);
6633 if (max_diff <= scaled_epsilon)
6635 memcpy(to, from, 2 * sizeof(USHORT));
6637 return TRUE;
6640 return FALSE;
6643 static BOOL weld_ushort4n(void *to, void *from, FLOAT epsilon)
6645 USHORT *s1 = to;
6646 USHORT *s2 = from;
6647 USHORT scaled_epsilon = (USHORT)(epsilon * USHRT_MAX);
6648 USHORT diff_x = s1[0] > s2[0] ? s1[0] - s2[0] : s2[0] - s1[0];
6649 USHORT diff_y = s1[1] > s2[1] ? s1[1] - s2[1] : s2[1] - s1[1];
6650 USHORT diff_z = s1[2] > s2[2] ? s1[2] - s2[2] : s2[2] - s1[2];
6651 USHORT diff_w = s1[3] > s2[3] ? s1[3] - s2[3] : s2[3] - s1[3];
6652 USHORT max_diff = max(diff_x, diff_y);
6653 max_diff = max(diff_z, max_diff);
6654 max_diff = max(diff_w, max_diff);
6656 if (max_diff <= scaled_epsilon)
6658 memcpy(to, from, 4 * sizeof(USHORT));
6660 return TRUE;
6663 return FALSE;
6666 struct udec3
6668 UINT x;
6669 UINT y;
6670 UINT z;
6671 UINT w;
6674 static struct udec3 dword_to_udec3(DWORD d)
6676 struct udec3 v;
6678 v.x = d & 0x3ff;
6679 v.y = (d & 0xffc00) >> 10;
6680 v.z = (d & 0x3ff00000) >> 20;
6681 v.w = (d & 0xc0000000) >> 30;
6683 return v;
6686 static BOOL weld_udec3(void *to, void *from, FLOAT epsilon)
6688 DWORD *d1 = to;
6689 DWORD *d2 = from;
6690 struct udec3 v1 = dword_to_udec3(*d1);
6691 struct udec3 v2 = dword_to_udec3(*d2);
6692 UINT truncated_epsilon = (UINT)epsilon;
6693 UINT diff_x = v1.x > v2.x ? v1.x - v2.x : v2.x - v1.x;
6694 UINT diff_y = v1.y > v2.y ? v1.y - v2.y : v2.y - v1.y;
6695 UINT diff_z = v1.z > v2.z ? v1.z - v2.z : v2.z - v1.z;
6696 UINT diff_w = v1.w > v2.w ? v1.w - v2.w : v2.w - v1.w;
6697 UINT max_diff = max(diff_x, diff_y);
6698 max_diff = max(diff_z, max_diff);
6699 max_diff = max(diff_w, max_diff);
6701 if (max_diff <= truncated_epsilon)
6703 memcpy(to, from, sizeof(DWORD));
6705 return TRUE;
6708 return FALSE;
6711 struct dec3n
6713 INT x;
6714 INT y;
6715 INT z;
6716 INT w;
6719 static struct dec3n dword_to_dec3n(DWORD d)
6721 struct dec3n v;
6723 v.x = d & 0x3ff;
6724 v.y = (d & 0xffc00) >> 10;
6725 v.z = (d & 0x3ff00000) >> 20;
6726 v.w = (d & 0xc0000000) >> 30;
6728 return v;
6731 static BOOL weld_dec3n(void *to, void *from, FLOAT epsilon)
6733 const UINT MAX_DEC3N = 511;
6734 DWORD *d1 = to;
6735 DWORD *d2 = from;
6736 struct dec3n v1 = dword_to_dec3n(*d1);
6737 struct dec3n v2 = dword_to_dec3n(*d2);
6738 INT scaled_epsilon = (INT)(epsilon * MAX_DEC3N);
6739 INT diff_x = abs(v1.x - v2.x);
6740 INT diff_y = abs(v1.y - v2.y);
6741 INT diff_z = abs(v1.z - v2.z);
6742 INT diff_w = abs(v1.w - v2.w);
6743 INT max_abs_diff = max(diff_x, diff_y);
6744 max_abs_diff = max(diff_z, max_abs_diff);
6745 max_abs_diff = max(diff_w, max_abs_diff);
6747 if (max_abs_diff <= scaled_epsilon)
6749 memcpy(to, from, sizeof(DWORD));
6751 return TRUE;
6754 return FALSE;
6757 static BOOL weld_float16_2(void *to, void *from, FLOAT epsilon)
6759 D3DXFLOAT16 *v1_float16 = to;
6760 D3DXFLOAT16 *v2_float16 = from;
6761 FLOAT diff_x;
6762 FLOAT diff_y;
6763 FLOAT max_abs_diff;
6764 #define NUM_ELEM 2
6765 FLOAT v1[NUM_ELEM];
6766 FLOAT v2[NUM_ELEM];
6768 D3DXFloat16To32Array(v1, v1_float16, NUM_ELEM);
6769 D3DXFloat16To32Array(v2, v2_float16, NUM_ELEM);
6771 diff_x = fabsf(v1[0] - v2[0]);
6772 diff_y = fabsf(v1[1] - v2[1]);
6773 max_abs_diff = max(diff_x, diff_y);
6775 if (max_abs_diff <= epsilon)
6777 memcpy(to, from, NUM_ELEM * sizeof(D3DXFLOAT16));
6779 return TRUE;
6782 return FALSE;
6783 #undef NUM_ELEM
6786 static BOOL weld_float16_4(void *to, void *from, FLOAT epsilon)
6788 D3DXFLOAT16 *v1_float16 = to;
6789 D3DXFLOAT16 *v2_float16 = from;
6790 FLOAT diff_x;
6791 FLOAT diff_y;
6792 FLOAT diff_z;
6793 FLOAT diff_w;
6794 FLOAT max_abs_diff;
6795 #define NUM_ELEM 4
6796 FLOAT v1[NUM_ELEM];
6797 FLOAT v2[NUM_ELEM];
6799 D3DXFloat16To32Array(v1, v1_float16, NUM_ELEM);
6800 D3DXFloat16To32Array(v2, v2_float16, NUM_ELEM);
6802 diff_x = fabsf(v1[0] - v2[0]);
6803 diff_y = fabsf(v1[1] - v2[1]);
6804 diff_z = fabsf(v1[2] - v2[2]);
6805 diff_w = fabsf(v1[3] - v2[3]);
6806 max_abs_diff = max(diff_x, diff_y);
6807 max_abs_diff = max(diff_z, max_abs_diff);
6808 max_abs_diff = max(diff_w, max_abs_diff);
6810 if (max_abs_diff <= epsilon)
6812 memcpy(to, from, NUM_ELEM * sizeof(D3DXFLOAT16));
6814 return TRUE;
6817 return FALSE;
6818 #undef NUM_ELEM
6821 /* Sets the vertex components to the same value if they are within epsilon. */
6822 static BOOL weld_component(void *to, void *from, D3DDECLTYPE type, FLOAT epsilon)
6824 /* Quiet FIXMEs as this is in a loop with potentially thousand of iterations. */
6825 BOOL fixme_once_unused = FALSE;
6826 BOOL fixme_once_unknown = FALSE;
6828 switch (type)
6830 case D3DDECLTYPE_FLOAT1:
6831 return weld_float1(to, from, epsilon);
6833 case D3DDECLTYPE_FLOAT2:
6834 return weld_float2(to, from, epsilon);
6836 case D3DDECLTYPE_FLOAT3:
6837 return weld_float3(to, from, epsilon);
6839 case D3DDECLTYPE_FLOAT4:
6840 return weld_float4(to, from, epsilon);
6842 case D3DDECLTYPE_D3DCOLOR:
6843 return weld_d3dcolor(to, from, epsilon);
6845 case D3DDECLTYPE_UBYTE4:
6846 return weld_ubyte4(to, from, epsilon);
6848 case D3DDECLTYPE_SHORT2:
6849 return weld_short2(to, from, epsilon);
6851 case D3DDECLTYPE_SHORT4:
6852 return weld_short4(to, from, epsilon);
6854 case D3DDECLTYPE_UBYTE4N:
6855 return weld_ubyte4n(to, from, epsilon);
6857 case D3DDECLTYPE_SHORT2N:
6858 return weld_short2n(to, from, epsilon);
6860 case D3DDECLTYPE_SHORT4N:
6861 return weld_short4n(to, from, epsilon);
6863 case D3DDECLTYPE_USHORT2N:
6864 return weld_ushort2n(to, from, epsilon);
6866 case D3DDECLTYPE_USHORT4N:
6867 return weld_ushort4n(to, from, epsilon);
6869 case D3DDECLTYPE_UDEC3:
6870 return weld_udec3(to, from, epsilon);
6872 case D3DDECLTYPE_DEC3N:
6873 return weld_dec3n(to, from, epsilon);
6875 case D3DDECLTYPE_FLOAT16_2:
6876 return weld_float16_2(to, from, epsilon);
6878 case D3DDECLTYPE_FLOAT16_4:
6879 return weld_float16_4(to, from, epsilon);
6881 case D3DDECLTYPE_UNUSED:
6882 if (!fixme_once_unused++)
6883 FIXME("D3DDECLTYPE_UNUSED welding not implemented.\n");
6884 break;
6886 default:
6887 if (!fixme_once_unknown++)
6888 FIXME("Welding of unknown declaration type %d is not implemented.\n", type);
6889 break;
6892 return FALSE;
6895 static FLOAT get_component_epsilon(const D3DVERTEXELEMENT9 *decl_ptr, const D3DXWELDEPSILONS *epsilons)
6897 FLOAT epsilon = 0.0f;
6898 /* Quiet FIXMEs as this is in a loop with potentially thousand of iterations. */
6899 static BOOL fixme_once_blendindices = FALSE;
6900 static BOOL fixme_once_positiont = FALSE;
6901 static BOOL fixme_once_fog = FALSE;
6902 static BOOL fixme_once_depth = FALSE;
6903 static BOOL fixme_once_sample = FALSE;
6904 static BOOL fixme_once_unknown = FALSE;
6906 switch (decl_ptr->Usage)
6908 case D3DDECLUSAGE_POSITION:
6909 epsilon = epsilons->Position;
6910 break;
6911 case D3DDECLUSAGE_BLENDWEIGHT:
6912 epsilon = epsilons->BlendWeights;
6913 break;
6914 case D3DDECLUSAGE_NORMAL:
6915 epsilon = epsilons->Normals;
6916 break;
6917 case D3DDECLUSAGE_PSIZE:
6918 epsilon = epsilons->PSize;
6919 break;
6920 case D3DDECLUSAGE_TEXCOORD:
6922 BYTE usage_index = decl_ptr->UsageIndex;
6923 if (usage_index > 7)
6924 usage_index = 7;
6925 epsilon = epsilons->Texcoords[usage_index];
6926 break;
6928 case D3DDECLUSAGE_TANGENT:
6929 epsilon = epsilons->Tangent;
6930 break;
6931 case D3DDECLUSAGE_BINORMAL:
6932 epsilon = epsilons->Binormal;
6933 break;
6934 case D3DDECLUSAGE_TESSFACTOR:
6935 epsilon = epsilons->TessFactor;
6936 break;
6937 case D3DDECLUSAGE_COLOR:
6938 if (decl_ptr->UsageIndex == 0)
6939 epsilon = epsilons->Diffuse;
6940 else if (decl_ptr->UsageIndex == 1)
6941 epsilon = epsilons->Specular;
6942 else
6943 epsilon = 1e-6f;
6944 break;
6945 case D3DDECLUSAGE_BLENDINDICES:
6946 if (!fixme_once_blendindices++)
6947 FIXME("D3DDECLUSAGE_BLENDINDICES welding not implemented.\n");
6948 break;
6949 case D3DDECLUSAGE_POSITIONT:
6950 if (!fixme_once_positiont++)
6951 FIXME("D3DDECLUSAGE_POSITIONT welding not implemented.\n");
6952 break;
6953 case D3DDECLUSAGE_FOG:
6954 if (!fixme_once_fog++)
6955 FIXME("D3DDECLUSAGE_FOG welding not implemented.\n");
6956 break;
6957 case D3DDECLUSAGE_DEPTH:
6958 if (!fixme_once_depth++)
6959 FIXME("D3DDECLUSAGE_DEPTH welding not implemented.\n");
6960 break;
6961 case D3DDECLUSAGE_SAMPLE:
6962 if (!fixme_once_sample++)
6963 FIXME("D3DDECLUSAGE_SAMPLE welding not implemented.\n");
6964 break;
6965 default:
6966 if (!fixme_once_unknown++)
6967 FIXME("Unknown usage %x\n", decl_ptr->Usage);
6968 break;
6971 return epsilon;
6974 /* Helper function for reading a 32-bit index buffer. */
6975 static inline DWORD read_ib(void *index_buffer, BOOL indices_are_32bit,
6976 DWORD index)
6978 if (indices_are_32bit)
6980 DWORD *indices = index_buffer;
6981 return indices[index];
6983 else
6985 WORD *indices = index_buffer;
6986 return indices[index];
6990 /* Helper function for writing to a 32-bit index buffer. */
6991 static inline void write_ib(void *index_buffer, BOOL indices_are_32bit,
6992 DWORD index, DWORD value)
6994 if (indices_are_32bit)
6996 DWORD *indices = index_buffer;
6997 indices[index] = value;
6999 else
7001 WORD *indices = index_buffer;
7002 indices[index] = value;
7006 /*************************************************************************
7007 * D3DXWeldVertices (D3DX9_36.@)
7009 * Welds together similar vertices. The similarity between vert-
7010 * ices can be the position and other components such as
7011 * normal and color.
7013 * PARAMS
7014 * mesh [I] Mesh which vertices will be welded together.
7015 * flags [I] D3DXWELDEPSILONSFLAGS specifying how to weld.
7016 * epsilons [I] How similar a component needs to be for welding.
7017 * adjacency [I] Which faces are adjacent to other faces.
7018 * adjacency_out [O] Updated adjacency after welding.
7019 * face_remap_out [O] Which faces the old faces have been mapped to.
7020 * vertex_remap_out [O] Which vertices the old vertices have been mapped to.
7022 * RETURNS
7023 * Success: D3D_OK.
7024 * Failure: D3DERR_INVALIDCALL, E_OUTOFMEMORY.
7026 * BUGS
7027 * Attribute sorting not implemented.
7030 HRESULT WINAPI D3DXWeldVertices(ID3DXMesh *mesh, DWORD flags, const D3DXWELDEPSILONS *epsilons,
7031 const DWORD *adjacency, DWORD *adjacency_out, DWORD *face_remap_out, ID3DXBuffer **vertex_remap_out)
7033 DWORD *adjacency_generated = NULL;
7034 const DWORD *adjacency_ptr;
7035 DWORD *attributes = NULL;
7036 const FLOAT DEFAULT_EPSILON = 1.0e-6f;
7037 HRESULT hr;
7038 DWORD i;
7039 void *indices = NULL;
7040 BOOL indices_are_32bit = mesh->lpVtbl->GetOptions(mesh) & D3DXMESH_32BIT;
7041 DWORD optimize_flags;
7042 DWORD *point_reps = NULL;
7043 struct d3dx9_mesh *This = impl_from_ID3DXMesh(mesh);
7044 DWORD *vertex_face_map = NULL;
7045 BYTE *vertices = NULL;
7047 TRACE("mesh %p, flags %#lx, epsilons %p, adjacency %p, adjacency_out %p, face_remap_out %p, "
7048 "vertex_remap_out %p.\n",
7049 mesh, flags, epsilons, adjacency, adjacency_out, face_remap_out, vertex_remap_out);
7051 if (flags == 0)
7053 WARN("No flags are undefined. Using D3DXWELDEPSILONS_WELDPARTIALMATCHES instead.\n");
7054 flags = D3DXWELDEPSILONS_WELDPARTIALMATCHES;
7057 if (adjacency) /* Use supplied adjacency. */
7059 adjacency_ptr = adjacency;
7061 else /* Adjacency has to be generated. */
7063 adjacency_generated = HeapAlloc(GetProcessHeap(), 0, 3 * This->numfaces * sizeof(*adjacency_generated));
7064 if (!adjacency_generated)
7066 ERR("Couldn't allocate memory for adjacency_generated.\n");
7067 hr = E_OUTOFMEMORY;
7068 goto cleanup;
7070 hr = mesh->lpVtbl->GenerateAdjacency(mesh, DEFAULT_EPSILON, adjacency_generated);
7071 if (FAILED(hr))
7073 ERR("Couldn't generate adjacency.\n");
7074 goto cleanup;
7076 adjacency_ptr = adjacency_generated;
7079 /* Point representation says which vertices can be replaced. */
7080 point_reps = HeapAlloc(GetProcessHeap(), 0, This->numvertices * sizeof(*point_reps));
7081 if (!point_reps)
7083 hr = E_OUTOFMEMORY;
7084 ERR("Couldn't allocate memory for point_reps.\n");
7085 goto cleanup;
7087 hr = mesh->lpVtbl->ConvertAdjacencyToPointReps(mesh, adjacency_ptr, point_reps);
7088 if (FAILED(hr))
7090 ERR("ConvertAdjacencyToPointReps failed.\n");
7091 goto cleanup;
7094 hr = mesh->lpVtbl->LockIndexBuffer(mesh, 0, &indices);
7095 if (FAILED(hr))
7097 ERR("Couldn't lock index buffer.\n");
7098 goto cleanup;
7101 hr = mesh->lpVtbl->LockAttributeBuffer(mesh, 0, &attributes);
7102 if (FAILED(hr))
7104 ERR("Couldn't lock attribute buffer.\n");
7105 goto cleanup;
7107 vertex_face_map = HeapAlloc(GetProcessHeap(), 0, This->numvertices * sizeof(*vertex_face_map));
7108 if (!vertex_face_map)
7110 hr = E_OUTOFMEMORY;
7111 ERR("Couldn't allocate memory for vertex_face_map.\n");
7112 goto cleanup;
7114 /* Build vertex face map, so that a vertex's face can be looked up. */
7115 for (i = 0; i < This->numfaces; i++)
7117 DWORD j;
7118 for (j = 0; j < 3; j++)
7120 DWORD index = read_ib(indices, indices_are_32bit, 3*i + j);
7121 vertex_face_map[index] = i;
7125 if (flags & D3DXWELDEPSILONS_WELDPARTIALMATCHES)
7127 hr = mesh->lpVtbl->LockVertexBuffer(mesh, 0, (void**)&vertices);
7128 if (FAILED(hr))
7130 ERR("Couldn't lock vertex buffer.\n");
7131 goto cleanup;
7133 /* For each vertex that can be removed, compare its vertex components
7134 * with the vertex components from the vertex that can replace it. A
7135 * vertex is only fully replaced if all the components match and the
7136 * flag D3DXWELDEPSILONS_DONOTREMOVEVERTICES is not set, and they
7137 * belong to the same attribute group. Otherwise the vertex components
7138 * that are within epsilon are set to the same value.
7140 for (i = 0; i < 3 * This->numfaces; i++)
7142 D3DVERTEXELEMENT9 *decl_ptr;
7143 DWORD vertex_size = mesh->lpVtbl->GetNumBytesPerVertex(mesh);
7144 DWORD num_vertex_components;
7145 INT matches = 0;
7146 BOOL all_match;
7147 DWORD index = read_ib(indices, indices_are_32bit, i);
7149 for (decl_ptr = This->cached_declaration, num_vertex_components = 0; decl_ptr->Stream != 0xFF; decl_ptr++, num_vertex_components++)
7151 BYTE *to = &vertices[vertex_size*index + decl_ptr->Offset];
7152 BYTE *from = &vertices[vertex_size*point_reps[index] + decl_ptr->Offset];
7153 FLOAT epsilon = get_component_epsilon(decl_ptr, epsilons);
7155 /* Don't weld self */
7156 if (index == point_reps[index])
7158 matches++;
7159 continue;
7162 if (weld_component(to, from, decl_ptr->Type, epsilon))
7163 matches++;
7166 all_match = (num_vertex_components == matches);
7167 if (all_match && !(flags & D3DXWELDEPSILONS_DONOTREMOVEVERTICES))
7169 DWORD to_face = vertex_face_map[index];
7170 DWORD from_face = vertex_face_map[point_reps[index]];
7171 if(attributes[to_face] != attributes[from_face] && !(flags & D3DXWELDEPSILONS_DONOTSPLIT))
7172 continue;
7173 write_ib(indices, indices_are_32bit, i, point_reps[index]);
7176 mesh->lpVtbl->UnlockVertexBuffer(mesh);
7177 vertices = NULL;
7179 else if (flags & D3DXWELDEPSILONS_WELDALL)
7181 for (i = 0; i < 3 * This->numfaces; i++)
7183 DWORD index = read_ib(indices, indices_are_32bit, i);
7184 DWORD to_face = vertex_face_map[index];
7185 DWORD from_face = vertex_face_map[point_reps[index]];
7186 if(attributes[to_face] != attributes[from_face] && !(flags & D3DXWELDEPSILONS_DONOTSPLIT))
7187 continue;
7188 write_ib(indices, indices_are_32bit, i, point_reps[index]);
7191 mesh->lpVtbl->UnlockAttributeBuffer(mesh);
7192 attributes = NULL;
7193 mesh->lpVtbl->UnlockIndexBuffer(mesh);
7194 indices = NULL;
7196 /* Compact mesh using OptimizeInplace */
7197 optimize_flags = D3DXMESHOPT_COMPACT;
7198 hr = mesh->lpVtbl->OptimizeInplace(mesh, optimize_flags, adjacency_ptr, adjacency_out, face_remap_out, vertex_remap_out);
7199 if (FAILED(hr))
7201 ERR("Couldn't compact mesh.\n");
7202 goto cleanup;
7205 hr = D3D_OK;
7206 cleanup:
7207 HeapFree(GetProcessHeap(), 0, adjacency_generated);
7208 HeapFree(GetProcessHeap(), 0, point_reps);
7209 HeapFree(GetProcessHeap(), 0, vertex_face_map);
7210 if (attributes) mesh->lpVtbl->UnlockAttributeBuffer(mesh);
7211 if (indices) mesh->lpVtbl->UnlockIndexBuffer(mesh);
7212 if (vertices) mesh->lpVtbl->UnlockVertexBuffer(mesh);
7214 return hr;
7217 /*************************************************************************
7218 * D3DXOptimizeFaces (D3DX9_36.@)
7220 * Re-orders the faces so the vertex cache is used optimally.
7222 * PARAMS
7223 * indices [I] Pointer to an index buffer belonging to a mesh.
7224 * num_faces [I] Number of faces in the mesh.
7225 * num_vertices [I] Number of vertices in the mesh.
7226 * indices_are_32bit [I] Specifies whether indices are 32- or 16-bit.
7227 * face_remap [I/O] The new order the faces should be drawn in.
7229 * RETURNS
7230 * Success: D3D_OK.
7231 * Failure: D3DERR_INVALIDCALL.
7233 * BUGS
7234 * The face re-ordering does not use the vertex cache optimally.
7237 HRESULT WINAPI D3DXOptimizeFaces(const void *indices, UINT num_faces,
7238 UINT num_vertices, BOOL indices_are_32bit, DWORD *face_remap)
7240 UINT i;
7241 UINT j = num_faces - 1;
7242 UINT limit_16_bit = 2 << 15; /* According to MSDN */
7243 HRESULT hr = D3D_OK;
7245 FIXME("indices %p, num_faces %u, num_vertices %u, indices_are_32bit %#x, face_remap %p semi-stub. "
7246 "Face order will not be optimal.\n",
7247 indices, num_faces, num_vertices, indices_are_32bit, face_remap);
7249 if (!indices_are_32bit && num_faces >= limit_16_bit)
7251 WARN("Number of faces must be less than %d when using 16-bit indices.\n",
7252 limit_16_bit);
7253 hr = D3DERR_INVALIDCALL;
7254 goto error;
7257 if (!face_remap)
7259 WARN("Face remap pointer is NULL.\n");
7260 hr = D3DERR_INVALIDCALL;
7261 goto error;
7264 /* The faces are drawn in reverse order for simple meshes. This ordering
7265 * is not optimal for complicated meshes, but will not break anything
7266 * either. The ordering should be changed to take advantage of the vertex
7267 * cache on the graphics card.
7269 * TODO Re-order to take advantage of vertex cache.
7271 for (i = 0; i < num_faces; i++)
7273 face_remap[i] = j--;
7276 return D3D_OK;
7278 error:
7279 return hr;
7282 static D3DXVECTOR3 *vertex_element_vec3(BYTE *vertices, const D3DVERTEXELEMENT9 *declaration,
7283 DWORD vertex_stride, DWORD index)
7285 return (D3DXVECTOR3 *)(vertices + declaration->Offset + index * vertex_stride);
7288 static D3DXVECTOR3 read_vec3(BYTE *vertices, const D3DVERTEXELEMENT9 *declaration,
7289 DWORD vertex_stride, DWORD index)
7291 D3DXVECTOR3 vec3 = {0};
7292 const D3DXVECTOR3 *src = vertex_element_vec3(vertices, declaration, vertex_stride, index);
7294 switch (declaration->Type)
7296 case D3DDECLTYPE_FLOAT1:
7297 vec3.x = src->x;
7298 break;
7299 case D3DDECLTYPE_FLOAT2:
7300 vec3.x = src->x;
7301 vec3.y = src->y;
7302 break;
7303 case D3DDECLTYPE_FLOAT3:
7304 case D3DDECLTYPE_FLOAT4:
7305 vec3 = *src;
7306 break;
7307 default:
7308 ERR("Cannot read vec3\n");
7309 break;
7312 return vec3;
7315 /*************************************************************************
7316 * D3DXComputeTangentFrameEx (D3DX9_36.@)
7318 HRESULT WINAPI D3DXComputeTangentFrameEx(ID3DXMesh *mesh, DWORD texture_in_semantic, DWORD texture_in_index,
7319 DWORD u_partial_out_semantic, DWORD u_partial_out_index, DWORD v_partial_out_semantic,
7320 DWORD v_partial_out_index, DWORD normal_out_semantic, DWORD normal_out_index, DWORD options,
7321 const DWORD *adjacency, float partial_edge_threshold, float singular_point_threshold,
7322 float normal_edge_threshold, ID3DXMesh **mesh_out, ID3DXBuffer **vertex_mapping)
7324 HRESULT hr;
7325 void *indices = NULL;
7326 BYTE *vertices = NULL;
7327 DWORD *point_reps = NULL;
7328 size_t normal_size;
7329 BOOL indices_are_32bit;
7330 DWORD i, j, num_faces, num_vertices, vertex_stride;
7331 D3DVERTEXELEMENT9 declaration[MAX_FVF_DECL_SIZE] = {D3DDECL_END()};
7332 D3DVERTEXELEMENT9 *position_declaration = NULL, *normal_declaration = NULL;
7333 DWORD weighting_method = options & (D3DXTANGENT_WEIGHT_EQUAL | D3DXTANGENT_WEIGHT_BY_AREA);
7335 TRACE("mesh %p, texture_in_semantic %lu, texture_in_index %lu, u_partial_out_semantic %lu, "
7336 "u_partial_out_index %lu, v_partial_out_semantic %lu, v_partial_out_index %lu, "
7337 "normal_out_semantic %lu, normal_out_index %lu, options %#lx, adjacency %p, "
7338 "partial_edge_threshold %.8e, singular_point_threshold %.8e, normal_edge_threshold %.8e, "
7339 "mesh_out %p, vertex_mapping %p.\n",
7340 mesh, texture_in_semantic, texture_in_index, u_partial_out_semantic, u_partial_out_index,
7341 v_partial_out_semantic, v_partial_out_index, normal_out_semantic, normal_out_index,
7342 options, adjacency, partial_edge_threshold, singular_point_threshold,
7343 normal_edge_threshold, mesh_out, vertex_mapping);
7345 if (!mesh)
7347 WARN("mesh is NULL\n");
7348 return D3DERR_INVALIDCALL;
7351 if (weighting_method == (D3DXTANGENT_WEIGHT_EQUAL | D3DXTANGENT_WEIGHT_BY_AREA))
7353 WARN("D3DXTANGENT_WEIGHT_BY_AREA and D3DXTANGENT_WEIGHT_EQUAL are mutally exclusive\n");
7354 return D3DERR_INVALIDCALL;
7357 if (u_partial_out_semantic != D3DX_DEFAULT)
7359 FIXME("tangent vectors computation is not supported\n");
7360 return E_NOTIMPL;
7363 if (v_partial_out_semantic != D3DX_DEFAULT)
7365 FIXME("binormal vectors computation is not supported\n");
7366 return E_NOTIMPL;
7369 if (options & ~(D3DXTANGENT_GENERATE_IN_PLACE | D3DXTANGENT_CALCULATE_NORMALS
7370 | D3DXTANGENT_WEIGHT_EQUAL | D3DXTANGENT_WEIGHT_BY_AREA))
7372 FIXME("Unsupported options %#lx.\n", options);
7373 return E_NOTIMPL;
7376 if (!(options & D3DXTANGENT_CALCULATE_NORMALS))
7378 FIXME("only normals computation is supported\n");
7379 return E_NOTIMPL;
7382 if (!(options & D3DXTANGENT_GENERATE_IN_PLACE) || mesh_out || vertex_mapping)
7384 FIXME("only D3DXTANGENT_GENERATE_IN_PLACE is supported\n");
7385 return E_NOTIMPL;
7388 if (FAILED(hr = mesh->lpVtbl->GetDeclaration(mesh, declaration)))
7389 return hr;
7391 for (i = 0; declaration[i].Stream != 0xff; i++)
7393 if (declaration[i].Usage == D3DDECLUSAGE_POSITION && !declaration[i].UsageIndex)
7394 position_declaration = &declaration[i];
7395 if (declaration[i].Usage == normal_out_semantic && declaration[i].UsageIndex == normal_out_index)
7396 normal_declaration = &declaration[i];
7399 if (!position_declaration || !normal_declaration)
7400 return D3DERR_INVALIDCALL;
7402 if (normal_declaration->Type == D3DDECLTYPE_FLOAT3)
7404 normal_size = sizeof(D3DXVECTOR3);
7406 else if (normal_declaration->Type == D3DDECLTYPE_FLOAT4)
7408 normal_size = sizeof(D3DXVECTOR4);
7410 else
7412 WARN("unsupported normals type %u\n", normal_declaration->Type);
7413 return D3DERR_INVALIDCALL;
7416 num_faces = mesh->lpVtbl->GetNumFaces(mesh);
7417 num_vertices = mesh->lpVtbl->GetNumVertices(mesh);
7418 vertex_stride = mesh->lpVtbl->GetNumBytesPerVertex(mesh);
7419 indices_are_32bit = mesh->lpVtbl->GetOptions(mesh) & D3DXMESH_32BIT;
7421 point_reps = HeapAlloc(GetProcessHeap(), 0, num_vertices * sizeof(*point_reps));
7422 if (!point_reps)
7424 hr = E_OUTOFMEMORY;
7425 goto done;
7428 if (adjacency)
7430 if (FAILED(hr = mesh->lpVtbl->ConvertAdjacencyToPointReps(mesh, adjacency, point_reps)))
7431 goto done;
7433 else
7435 for (i = 0; i < num_vertices; i++)
7436 point_reps[i] = i;
7439 if (FAILED(hr = mesh->lpVtbl->LockIndexBuffer(mesh, 0, &indices)))
7440 goto done;
7442 if (FAILED(hr = mesh->lpVtbl->LockVertexBuffer(mesh, 0, (void **)&vertices)))
7443 goto done;
7445 for (i = 0; i < num_vertices; i++)
7447 static const D3DXVECTOR4 default_vector = {0.0f, 0.0f, 0.0f, 1.0f};
7448 void *normal = vertices + normal_declaration->Offset + i * vertex_stride;
7450 memcpy(normal, &default_vector, normal_size);
7453 for (i = 0; i < num_faces; i++)
7455 float denominator, weights[3];
7456 D3DXVECTOR3 a, b, cross, face_normal;
7457 const DWORD face_indices[3] =
7459 read_ib(indices, indices_are_32bit, 3 * i + 0),
7460 read_ib(indices, indices_are_32bit, 3 * i + 1),
7461 read_ib(indices, indices_are_32bit, 3 * i + 2)
7463 const D3DXVECTOR3 v0 = read_vec3(vertices, position_declaration, vertex_stride, face_indices[0]);
7464 const D3DXVECTOR3 v1 = read_vec3(vertices, position_declaration, vertex_stride, face_indices[1]);
7465 const D3DXVECTOR3 v2 = read_vec3(vertices, position_declaration, vertex_stride, face_indices[2]);
7467 D3DXVec3Cross(&cross, D3DXVec3Subtract(&a, &v0, &v1), D3DXVec3Subtract(&b, &v0, &v2));
7469 switch (weighting_method)
7471 case D3DXTANGENT_WEIGHT_EQUAL:
7472 weights[0] = weights[1] = weights[2] = 1.0f;
7473 break;
7474 case D3DXTANGENT_WEIGHT_BY_AREA:
7475 weights[0] = weights[1] = weights[2] = D3DXVec3Length(&cross);
7476 break;
7477 default:
7478 /* weight by angle */
7479 denominator = D3DXVec3Length(&a) * D3DXVec3Length(&b);
7480 if (!denominator)
7481 weights[0] = 0.0f;
7482 else
7483 weights[0] = acosf(D3DXVec3Dot(&a, &b) / denominator);
7485 D3DXVec3Subtract(&a, &v1, &v0);
7486 D3DXVec3Subtract(&b, &v1, &v2);
7487 denominator = D3DXVec3Length(&a) * D3DXVec3Length(&b);
7488 if (!denominator)
7489 weights[1] = 0.0f;
7490 else
7491 weights[1] = acosf(D3DXVec3Dot(&a, &b) / denominator);
7493 D3DXVec3Subtract(&a, &v2, &v0);
7494 D3DXVec3Subtract(&b, &v2, &v1);
7495 denominator = D3DXVec3Length(&a) * D3DXVec3Length(&b);
7496 if (!denominator)
7497 weights[2] = 0.0f;
7498 else
7499 weights[2] = acosf(D3DXVec3Dot(&a, &b) / denominator);
7501 break;
7504 D3DXVec3Normalize(&face_normal, &cross);
7506 for (j = 0; j < 3; j++)
7508 D3DXVECTOR3 normal;
7509 DWORD rep_index = point_reps[face_indices[j]];
7510 D3DXVECTOR3 *rep_normal = vertex_element_vec3(vertices, normal_declaration, vertex_stride, rep_index);
7512 D3DXVec3Scale(&normal, &face_normal, weights[j]);
7513 D3DXVec3Add(rep_normal, rep_normal, &normal);
7517 for (i = 0; i < num_vertices; i++)
7519 DWORD rep_index = point_reps[i];
7520 D3DXVECTOR3 *normal = vertex_element_vec3(vertices, normal_declaration, vertex_stride, i);
7521 D3DXVECTOR3 *rep_normal = vertex_element_vec3(vertices, normal_declaration, vertex_stride, rep_index);
7523 if (i == rep_index)
7524 D3DXVec3Normalize(rep_normal, rep_normal);
7525 else
7526 *normal = *rep_normal;
7529 hr = D3D_OK;
7531 done:
7532 if (vertices)
7533 mesh->lpVtbl->UnlockVertexBuffer(mesh);
7535 if (indices)
7536 mesh->lpVtbl->UnlockIndexBuffer(mesh);
7538 HeapFree(GetProcessHeap(), 0, point_reps);
7540 return hr;
7543 /*************************************************************************
7544 * D3DXComputeNormals (D3DX9_36.@)
7546 HRESULT WINAPI D3DXComputeNormals(struct ID3DXBaseMesh *mesh, const DWORD *adjacency)
7548 TRACE("mesh %p, adjacency %p\n", mesh, adjacency);
7550 if (mesh && (ID3DXMeshVtbl *)mesh->lpVtbl != &D3DXMesh_Vtbl)
7552 ERR("Invalid virtual table\n");
7553 return D3DERR_INVALIDCALL;
7556 return D3DXComputeTangentFrameEx((ID3DXMesh *)mesh, D3DX_DEFAULT, 0,
7557 D3DX_DEFAULT, 0, D3DX_DEFAULT, 0, D3DDECLUSAGE_NORMAL, 0,
7558 D3DXTANGENT_GENERATE_IN_PLACE | D3DXTANGENT_CALCULATE_NORMALS,
7559 adjacency, -1.01f, -0.01f, -1.01f, NULL, NULL);
7562 /*************************************************************************
7563 * D3DXIntersect (D3DX9_36.@)
7565 HRESULT WINAPI D3DXIntersect(ID3DXBaseMesh *mesh, const D3DXVECTOR3 *ray_pos, const D3DXVECTOR3 *ray_dir,
7566 BOOL *hit, DWORD *face_index, float *u, float *v, float *distance, ID3DXBuffer **all_hits, DWORD *count_of_hits)
7568 FIXME("mesh %p, ray_pos %p, ray_dir %p, hit %p, face_index %p, u %p, v %p, distance %p, all_hits %p, "
7569 "count_of_hits %p stub!\n", mesh, ray_pos, ray_dir, hit, face_index, u, v, distance, all_hits, count_of_hits);
7571 return E_NOTIMPL;
7574 HRESULT WINAPI D3DXTessellateNPatches(ID3DXMesh *mesh, const DWORD *adjacency_in, float num_segs,
7575 BOOL quadratic_normals, ID3DXMesh **mesh_out, ID3DXBuffer **adjacency_out)
7577 FIXME("mesh %p, adjacency_in %p, num_segs %f, quadratic_normals %d, mesh_out %p, adjacency_out %p stub.\n",
7578 mesh, adjacency_in, num_segs, quadratic_normals, mesh_out, adjacency_out);
7580 return E_NOTIMPL;
7583 HRESULT WINAPI D3DXConvertMeshSubsetToSingleStrip(struct ID3DXBaseMesh *mesh_in, DWORD attribute_id,
7584 DWORD ib_flags, struct IDirect3DIndexBuffer9 **index_buffer, DWORD *index_count)
7586 FIXME("mesh_in %p, attribute_id %lu, ib_flags %#lx, index_buffer %p, index_count %p stub.\n",
7587 mesh_in, attribute_id, ib_flags, index_buffer, index_count);
7589 return E_NOTIMPL;
7592 struct frame_node
7594 struct list entry;
7595 D3DXFRAME *frame;
7598 static BOOL queue_frame_node(struct list *queue, D3DXFRAME *frame)
7600 struct frame_node *node;
7602 if (!frame->pFrameFirstChild)
7603 return TRUE;
7605 node = HeapAlloc(GetProcessHeap(), 0, sizeof(*node));
7606 if (!node)
7607 return FALSE;
7609 node->frame = frame;
7610 list_add_tail(queue, &node->entry);
7612 return TRUE;
7615 static void empty_frame_queue(struct list *queue)
7617 struct frame_node *cur, *cur2;
7618 LIST_FOR_EACH_ENTRY_SAFE(cur, cur2, queue, struct frame_node, entry)
7620 list_remove(&cur->entry);
7621 HeapFree(GetProcessHeap(), 0, cur);
7625 D3DXFRAME * WINAPI D3DXFrameFind(const D3DXFRAME *root, const char *name)
7627 D3DXFRAME *found = NULL, *frame;
7628 struct list queue;
7630 TRACE("root frame %p, name %s.\n", root, debugstr_a(name));
7632 if (!root)
7633 return NULL;
7635 list_init(&queue);
7637 frame = (D3DXFRAME *)root;
7639 for (;;)
7641 struct frame_node *node;
7643 while (frame)
7645 if ((name && frame->Name && !strcmp(frame->Name, name)) || (!name && !frame->Name))
7647 found = frame;
7648 goto cleanup;
7651 if (!queue_frame_node(&queue, frame))
7652 goto cleanup;
7654 frame = frame->pFrameSibling;
7657 if (list_empty(&queue))
7658 break;
7660 node = LIST_ENTRY(list_head(&queue), struct frame_node, entry);
7661 list_remove(&node->entry);
7662 frame = node->frame->pFrameFirstChild;
7663 HeapFree(GetProcessHeap(), 0, node);
7666 cleanup:
7667 empty_frame_queue(&queue);
7669 return found;