d3dcompiler_43/tests: Fix the spelling of a couple of ok() messages.
[wine/zf.git] / dlls / d3dx9_36 / mesh.c
blob8f7c926c32a2b712977342d376714453b9efffed
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 int 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 %u.\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 %u.\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 %u.\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 %#x, fvf %#x, 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 %#x, 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 %#x, 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 %#x, 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 DWORD *indices, DWORD *new_indices, DWORD face, DWORD numfaces)
1052 const unsigned int VERTS_PER_FACE = 3;
1053 DWORD edge, opp_edge;
1054 DWORD face_base = VERTS_PER_FACE * face;
1056 for (edge = 0; edge < VERTS_PER_FACE; edge++)
1058 DWORD adj_face = adjacency[face_base + edge];
1059 DWORD adj_face_base;
1060 DWORD i;
1061 if (adj_face == -1) /* No adjacent face. */
1062 continue;
1063 else if (adj_face >= numfaces)
1065 /* This throws exception on Windows */
1066 WARN("Index out of bounds. Got %d expected less than %d.\n",
1067 adj_face, numfaces);
1068 return D3DERR_INVALIDCALL;
1070 adj_face_base = 3 * adj_face;
1072 /* Find opposite edge in adjacent face. */
1073 for (opp_edge = 0; opp_edge < VERTS_PER_FACE; opp_edge++)
1075 DWORD opp_edge_index = adj_face_base + opp_edge;
1076 if (adjacency[opp_edge_index] == face)
1077 break; /* Found opposite edge. */
1080 /* Replaces vertices in opposite edge with vertices from current edge. */
1081 for (i = 0; i < 2; i++)
1083 DWORD from = face_base + (edge + (1 - i)) % VERTS_PER_FACE;
1084 DWORD to = adj_face_base + (opp_edge + i) % VERTS_PER_FACE;
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 *This = impl_from_ID3DXMesh(iface);
1102 HRESULT hr;
1103 DWORD face;
1104 DWORD i;
1105 DWORD *indices = NULL;
1106 WORD *indices_16bit = NULL;
1107 DWORD *new_indices = NULL;
1108 const unsigned int VERTS_PER_FACE = 3;
1110 TRACE("iface %p, adjacency %p, point_reps %p.\n", iface, adjacency, point_reps);
1112 if (!adjacency)
1114 WARN("NULL adjacency.\n");
1115 hr = D3DERR_INVALIDCALL;
1116 goto cleanup;
1119 if (!point_reps)
1121 WARN("NULL point_reps.\n");
1122 hr = D3DERR_INVALIDCALL;
1123 goto cleanup;
1126 /* Should never happen as CreateMesh does not allow meshes with 0 faces */
1127 if (This->numfaces == 0)
1129 ERR("Number of faces was zero.\n");
1130 hr = D3DERR_INVALIDCALL;
1131 goto cleanup;
1134 new_indices = HeapAlloc(GetProcessHeap(), 0, VERTS_PER_FACE * This->numfaces * sizeof(*indices));
1135 if (!new_indices)
1137 hr = E_OUTOFMEMORY;
1138 goto cleanup;
1141 if (This->options & D3DXMESH_32BIT)
1143 hr = iface->lpVtbl->LockIndexBuffer(iface, D3DLOCK_READONLY, (void**)&indices);
1144 if (FAILED(hr)) goto cleanup;
1145 memcpy(new_indices, indices, VERTS_PER_FACE * This->numfaces * sizeof(*indices));
1147 else
1149 /* Make a widening copy of indices_16bit into indices and new_indices
1150 * in order to re-use the helper function */
1151 hr = iface->lpVtbl->LockIndexBuffer(iface, D3DLOCK_READONLY, (void**)&indices_16bit);
1152 if (FAILED(hr)) goto cleanup;
1153 indices = HeapAlloc(GetProcessHeap(), 0, VERTS_PER_FACE * This->numfaces * sizeof(*indices));
1154 if (!indices)
1156 hr = E_OUTOFMEMORY;
1157 goto cleanup;
1159 for (i = 0; i < VERTS_PER_FACE * This->numfaces; i++)
1161 new_indices[i] = indices_16bit[i];
1162 indices[i] = indices_16bit[i];
1166 /* Vertices are ordered sequentially in the point representation. */
1167 for (i = 0; i < This->numvertices; i++)
1169 point_reps[i] = i;
1172 /* Propagate vertices with low indices so as few vertices as possible
1173 * are used in the mesh.
1175 for (face = 0; face < This->numfaces; face++)
1177 hr = propagate_face_vertices(adjacency, point_reps, indices, new_indices, face, This->numfaces);
1178 if (FAILED(hr)) goto cleanup;
1180 /* Go in opposite direction to catch all face orderings */
1181 for (face = 0; face < This->numfaces; face++)
1183 hr = propagate_face_vertices(adjacency, point_reps,
1184 indices, new_indices,
1185 (This->numfaces - 1) - face, This->numfaces);
1186 if (FAILED(hr)) goto cleanup;
1189 hr = D3D_OK;
1190 cleanup:
1191 if (This->options & D3DXMESH_32BIT)
1193 if (indices) iface->lpVtbl->UnlockIndexBuffer(iface);
1195 else
1197 if (indices_16bit) iface->lpVtbl->UnlockIndexBuffer(iface);
1198 HeapFree(GetProcessHeap(), 0, indices);
1200 HeapFree(GetProcessHeap(), 0, new_indices);
1201 return hr;
1204 struct vertex_metadata {
1205 float key;
1206 DWORD vertex_index;
1207 DWORD first_shared_index;
1210 static int __cdecl compare_vertex_keys(const void *a, const void *b)
1212 const struct vertex_metadata *left = a;
1213 const struct vertex_metadata *right = b;
1214 if (left->key == right->key)
1215 return 0;
1216 return left->key < right->key ? -1 : 1;
1219 static HRESULT WINAPI d3dx9_mesh_GenerateAdjacency(ID3DXMesh *iface, float epsilon, DWORD *adjacency)
1221 struct d3dx9_mesh *This = impl_from_ID3DXMesh(iface);
1222 HRESULT hr;
1223 BYTE *vertices = NULL;
1224 const DWORD *indices = NULL;
1225 DWORD vertex_size;
1226 DWORD buffer_size;
1227 /* sort the vertices by (x + y + z) to quickly find coincident vertices */
1228 struct vertex_metadata *sorted_vertices;
1229 /* shared_indices links together identical indices in the index buffer so
1230 * that adjacency checks can be limited to faces sharing a vertex */
1231 DWORD *shared_indices = NULL;
1232 const FLOAT epsilon_sq = epsilon * epsilon;
1233 DWORD i;
1235 TRACE("iface %p, epsilon %.8e, adjacency %p.\n", iface, epsilon, adjacency);
1237 if (!adjacency)
1238 return D3DERR_INVALIDCALL;
1240 buffer_size = This->numfaces * 3 * sizeof(*shared_indices) + This->numvertices * sizeof(*sorted_vertices);
1241 if (!(This->options & D3DXMESH_32BIT))
1242 buffer_size += This->numfaces * 3 * sizeof(*indices);
1243 shared_indices = HeapAlloc(GetProcessHeap(), 0, buffer_size);
1244 if (!shared_indices)
1245 return E_OUTOFMEMORY;
1246 sorted_vertices = (struct vertex_metadata*)(shared_indices + This->numfaces * 3);
1248 hr = iface->lpVtbl->LockVertexBuffer(iface, D3DLOCK_READONLY, (void**)&vertices);
1249 if (FAILED(hr)) goto cleanup;
1250 hr = iface->lpVtbl->LockIndexBuffer(iface, D3DLOCK_READONLY, (void**)&indices);
1251 if (FAILED(hr)) goto cleanup;
1253 if (!(This->options & D3DXMESH_32BIT)) {
1254 const WORD *word_indices = (const WORD*)indices;
1255 DWORD *dword_indices = (DWORD*)(sorted_vertices + This->numvertices);
1256 indices = dword_indices;
1257 for (i = 0; i < This->numfaces * 3; i++)
1258 *dword_indices++ = *word_indices++;
1261 vertex_size = iface->lpVtbl->GetNumBytesPerVertex(iface);
1262 for (i = 0; i < This->numvertices; i++) {
1263 D3DXVECTOR3 *vertex = (D3DXVECTOR3*)(vertices + vertex_size * i);
1264 sorted_vertices[i].first_shared_index = -1;
1265 sorted_vertices[i].key = vertex->x + vertex->y + vertex->z;
1266 sorted_vertices[i].vertex_index = i;
1268 for (i = 0; i < This->numfaces * 3; i++) {
1269 DWORD *first_shared_index = &sorted_vertices[indices[i]].first_shared_index;
1270 shared_indices[i] = *first_shared_index;
1271 *first_shared_index = i;
1272 adjacency[i] = -1;
1274 qsort(sorted_vertices, This->numvertices, sizeof(*sorted_vertices), compare_vertex_keys);
1276 for (i = 0; i < This->numvertices; i++) {
1277 struct vertex_metadata *sorted_vertex_a = &sorted_vertices[i];
1278 D3DXVECTOR3 *vertex_a = (D3DXVECTOR3*)(vertices + sorted_vertex_a->vertex_index * vertex_size);
1279 DWORD shared_index_a = sorted_vertex_a->first_shared_index;
1281 while (shared_index_a != -1) {
1282 DWORD j = i;
1283 DWORD shared_index_b = shared_indices[shared_index_a];
1284 struct vertex_metadata *sorted_vertex_b = sorted_vertex_a;
1286 while (TRUE) {
1287 while (shared_index_b != -1) {
1288 /* faces are adjacent if they have another coincident vertex */
1289 DWORD base_a = (shared_index_a / 3) * 3;
1290 DWORD base_b = (shared_index_b / 3) * 3;
1291 BOOL adjacent = FALSE;
1292 int k;
1294 for (k = 0; k < 3; k++) {
1295 if (adjacency[base_b + k] == shared_index_a / 3) {
1296 adjacent = TRUE;
1297 break;
1300 if (!adjacent) {
1301 for (k = 1; k <= 2; k++) {
1302 DWORD vertex_index_a = base_a + (shared_index_a + k) % 3;
1303 DWORD vertex_index_b = base_b + (shared_index_b + (3 - k)) % 3;
1304 adjacent = indices[vertex_index_a] == indices[vertex_index_b];
1305 if (!adjacent && epsilon >= 0.0f) {
1306 D3DXVECTOR3 delta = {0.0f, 0.0f, 0.0f};
1307 FLOAT length_sq;
1309 D3DXVec3Subtract(&delta,
1310 (D3DXVECTOR3*)(vertices + indices[vertex_index_a] * vertex_size),
1311 (D3DXVECTOR3*)(vertices + indices[vertex_index_b] * vertex_size));
1312 length_sq = D3DXVec3LengthSq(&delta);
1313 adjacent = epsilon == 0.0f ? length_sq == 0.0f : length_sq < epsilon_sq;
1315 if (adjacent) {
1316 DWORD adj_a = base_a + 2 - (vertex_index_a + shared_index_a + 1) % 3;
1317 DWORD adj_b = base_b + 2 - (vertex_index_b + shared_index_b + 1) % 3;
1318 if (adjacency[adj_a] == -1 && adjacency[adj_b] == -1) {
1319 adjacency[adj_a] = base_b / 3;
1320 adjacency[adj_b] = base_a / 3;
1321 break;
1327 shared_index_b = shared_indices[shared_index_b];
1329 while (++j < This->numvertices) {
1330 D3DXVECTOR3 *vertex_b;
1332 sorted_vertex_b++;
1333 if (sorted_vertex_b->key - sorted_vertex_a->key > epsilon * 3.0f) {
1334 /* no more coincident vertices to try */
1335 j = This->numvertices;
1336 break;
1338 /* check for coincidence */
1339 vertex_b = (D3DXVECTOR3*)(vertices + sorted_vertex_b->vertex_index * vertex_size);
1340 if (fabsf(vertex_a->x - vertex_b->x) <= epsilon &&
1341 fabsf(vertex_a->y - vertex_b->y) <= epsilon &&
1342 fabsf(vertex_a->z - vertex_b->z) <= epsilon)
1344 break;
1347 if (j >= This->numvertices)
1348 break;
1349 shared_index_b = sorted_vertex_b->first_shared_index;
1352 sorted_vertex_a->first_shared_index = shared_indices[sorted_vertex_a->first_shared_index];
1353 shared_index_a = sorted_vertex_a->first_shared_index;
1357 hr = D3D_OK;
1358 cleanup:
1359 if (indices) iface->lpVtbl->UnlockIndexBuffer(iface);
1360 if (vertices) iface->lpVtbl->UnlockVertexBuffer(iface);
1361 HeapFree(GetProcessHeap(), 0, shared_indices);
1362 return hr;
1365 static HRESULT WINAPI d3dx9_mesh_UpdateSemantics(ID3DXMesh *iface, D3DVERTEXELEMENT9 declaration[MAX_FVF_DECL_SIZE])
1367 struct d3dx9_mesh *This = impl_from_ID3DXMesh(iface);
1368 HRESULT hr;
1369 UINT vertex_declaration_size;
1370 int i;
1372 TRACE("iface %p, declaration %p.\n", iface, declaration);
1374 if (!declaration)
1376 WARN("Invalid declaration. Can't use NULL declaration.\n");
1377 return D3DERR_INVALIDCALL;
1380 /* New declaration must be same size as original */
1381 vertex_declaration_size = D3DXGetDeclVertexSize(declaration, declaration[0].Stream);
1382 if (vertex_declaration_size != This->vertex_declaration_size)
1384 WARN("Invalid declaration. New vertex size does not match the original vertex size.\n");
1385 return D3DERR_INVALIDCALL;
1388 /* New declaration must not contain non-zero Stream value */
1389 for (i = 0; declaration[i].Stream != 0xff; i++)
1391 if (declaration[i].Stream != 0)
1393 WARN("Invalid declaration. New declaration contains non-zero Stream value.\n");
1394 return D3DERR_INVALIDCALL;
1398 This->num_elem = i + 1;
1399 copy_declaration(This->cached_declaration, declaration, This->num_elem);
1401 if (This->vertex_declaration)
1402 IDirect3DVertexDeclaration9_Release(This->vertex_declaration);
1404 /* An application can pass an invalid declaration to UpdateSemantics and
1405 * still expect D3D_OK (see tests). If the declaration is invalid, then
1406 * subsequent calls to DrawSubset will fail. This is handled by setting the
1407 * vertex declaration to NULL.
1408 * GetDeclaration, GetNumBytesPerVertex must, however, use the new
1409 * invalid declaration. This is handled by them using the cached vertex
1410 * declaration instead of the actual vertex declaration.
1412 hr = IDirect3DDevice9_CreateVertexDeclaration(This->device,
1413 declaration,
1414 &This->vertex_declaration);
1415 if (FAILED(hr))
1417 WARN("Using invalid declaration. Calls to DrawSubset will fail.\n");
1418 This->vertex_declaration = NULL;
1421 return D3D_OK;
1424 static HRESULT WINAPI d3dx9_mesh_LockAttributeBuffer(ID3DXMesh *iface, DWORD flags, DWORD **data)
1426 struct d3dx9_mesh *mesh = impl_from_ID3DXMesh(iface);
1428 TRACE("iface %p, flags %#x, data %p.\n", iface, flags, data);
1430 InterlockedIncrement(&mesh->attrib_buffer_lock_count);
1432 if (!(flags & D3DLOCK_READONLY))
1434 D3DXATTRIBUTERANGE *attrib_table = mesh->attrib_table;
1435 mesh->attrib_table_size = 0;
1436 mesh->attrib_table = NULL;
1437 HeapFree(GetProcessHeap(), 0, attrib_table);
1440 *data = mesh->attrib_buffer;
1442 return D3D_OK;
1445 static HRESULT WINAPI d3dx9_mesh_UnlockAttributeBuffer(ID3DXMesh *iface)
1447 struct d3dx9_mesh *mesh = impl_from_ID3DXMesh(iface);
1448 int lock_count;
1450 TRACE("iface %p.\n", iface);
1452 lock_count = InterlockedDecrement(&mesh->attrib_buffer_lock_count);
1453 if (lock_count < 0)
1455 InterlockedIncrement(&mesh->attrib_buffer_lock_count);
1456 return D3DERR_INVALIDCALL;
1459 return D3D_OK;
1462 static HRESULT WINAPI d3dx9_mesh_Optimize(ID3DXMesh *iface, DWORD flags, const DWORD *adjacency_in,
1463 DWORD *adjacency_out, DWORD *face_remap, ID3DXBuffer **vertex_remap, ID3DXMesh **opt_mesh)
1465 struct d3dx9_mesh *mesh = impl_from_ID3DXMesh(iface);
1466 HRESULT hr;
1467 D3DVERTEXELEMENT9 declaration[MAX_FVF_DECL_SIZE] = { D3DDECL_END() };
1468 ID3DXMesh *optimized_mesh;
1470 TRACE("iface %p, flags %#x, adjacency_in %p, adjacency_out %p, face_remap %p, vertex_remap %p, opt_mesh %p.\n",
1471 iface, flags, adjacency_in, adjacency_out, face_remap, vertex_remap, opt_mesh);
1473 if (!opt_mesh)
1474 return D3DERR_INVALIDCALL;
1476 hr = iface->lpVtbl->GetDeclaration(iface, declaration);
1477 if (FAILED(hr)) return hr;
1479 if (FAILED(hr = iface->lpVtbl->CloneMesh(iface, mesh->options, declaration, mesh->device, &optimized_mesh)))
1480 return hr;
1482 hr = optimized_mesh->lpVtbl->OptimizeInplace(optimized_mesh, flags, adjacency_in, adjacency_out, face_remap, vertex_remap);
1483 if (SUCCEEDED(hr))
1484 *opt_mesh = optimized_mesh;
1485 else
1486 IUnknown_Release(optimized_mesh);
1487 return hr;
1490 /* Creates a vertex_remap that removes unused vertices.
1491 * Indices are updated according to the vertex_remap. */
1492 static HRESULT compact_mesh(struct d3dx9_mesh *This, DWORD *indices,
1493 DWORD *new_num_vertices, ID3DXBuffer **vertex_remap)
1495 HRESULT hr;
1496 DWORD *vertex_remap_ptr;
1497 DWORD num_used_vertices;
1498 DWORD i;
1500 hr = D3DXCreateBuffer(This->numvertices * sizeof(DWORD), vertex_remap);
1501 if (FAILED(hr)) return hr;
1502 vertex_remap_ptr = ID3DXBuffer_GetBufferPointer(*vertex_remap);
1504 for (i = 0; i < This->numfaces * 3; i++)
1505 vertex_remap_ptr[indices[i]] = 1;
1507 /* create old->new vertex mapping */
1508 num_used_vertices = 0;
1509 for (i = 0; i < This->numvertices; i++) {
1510 if (vertex_remap_ptr[i])
1511 vertex_remap_ptr[i] = num_used_vertices++;
1512 else
1513 vertex_remap_ptr[i] = -1;
1515 /* convert indices */
1516 for (i = 0; i < This->numfaces * 3; i++)
1517 indices[i] = vertex_remap_ptr[indices[i]];
1519 /* create new->old vertex mapping */
1520 num_used_vertices = 0;
1521 for (i = 0; i < This->numvertices; i++) {
1522 if (vertex_remap_ptr[i] != -1)
1523 vertex_remap_ptr[num_used_vertices++] = i;
1525 for (i = num_used_vertices; i < This->numvertices; i++)
1526 vertex_remap_ptr[i] = -1;
1528 *new_num_vertices = num_used_vertices;
1530 return D3D_OK;
1533 /* count the number of unique attribute values in a sorted attribute buffer */
1534 static DWORD count_attributes(const DWORD *attrib_buffer, DWORD numfaces)
1536 DWORD last_attribute = attrib_buffer[0];
1537 DWORD attrib_table_size = 1;
1538 DWORD i;
1539 for (i = 1; i < numfaces; i++) {
1540 if (attrib_buffer[i] != last_attribute) {
1541 last_attribute = attrib_buffer[i];
1542 attrib_table_size++;
1545 return attrib_table_size;
1548 static void fill_attribute_table(DWORD *attrib_buffer, DWORD numfaces, void *indices,
1549 BOOL is_32bit_indices, D3DXATTRIBUTERANGE *attrib_table)
1551 DWORD attrib_table_size = 0;
1552 DWORD last_attribute = attrib_buffer[0];
1553 DWORD min_vertex, max_vertex;
1554 DWORD i;
1556 attrib_table[0].AttribId = last_attribute;
1557 attrib_table[0].FaceStart = 0;
1558 min_vertex = (DWORD)-1;
1559 max_vertex = 0;
1560 for (i = 0; i < numfaces; i++) {
1561 DWORD j;
1563 if (attrib_buffer[i] != last_attribute) {
1564 last_attribute = attrib_buffer[i];
1565 attrib_table[attrib_table_size].FaceCount = i - attrib_table[attrib_table_size].FaceStart;
1566 attrib_table[attrib_table_size].VertexStart = min_vertex;
1567 attrib_table[attrib_table_size].VertexCount = max_vertex - min_vertex + 1;
1568 attrib_table_size++;
1569 attrib_table[attrib_table_size].AttribId = attrib_buffer[i];
1570 attrib_table[attrib_table_size].FaceStart = i;
1571 min_vertex = (DWORD)-1;
1572 max_vertex = 0;
1574 for (j = 0; j < 3; j++) {
1575 DWORD vertex_index = is_32bit_indices ? ((DWORD*)indices)[i * 3 + j] : ((WORD*)indices)[i * 3 + j];
1576 if (vertex_index < min_vertex)
1577 min_vertex = vertex_index;
1578 if (vertex_index > max_vertex)
1579 max_vertex = vertex_index;
1582 attrib_table[attrib_table_size].FaceCount = i - attrib_table[attrib_table_size].FaceStart;
1583 attrib_table[attrib_table_size].VertexStart = min_vertex;
1584 attrib_table[attrib_table_size].VertexCount = max_vertex - min_vertex + 1;
1585 attrib_table_size++;
1588 static int __cdecl attrib_entry_compare(const void *a, const void *b)
1590 const DWORD *ptr_a = *(const DWORD **)a;
1591 const DWORD *ptr_b = *(const DWORD **)b;
1592 int delta = *ptr_a - *ptr_b;
1594 if (delta)
1595 return delta;
1597 delta = ptr_a - ptr_b; /* for stable sort */
1598 return delta;
1601 /* Create face_remap, a new attribute buffer for attribute sort optimization. */
1602 static HRESULT remap_faces_for_attrsort(struct d3dx9_mesh *This, const DWORD *indices,
1603 DWORD *attrib_buffer, DWORD **sorted_attrib_buffer, DWORD **face_remap)
1605 DWORD **sorted_attrib_ptr_buffer = NULL;
1606 DWORD i;
1608 sorted_attrib_ptr_buffer = HeapAlloc(GetProcessHeap(), 0, This->numfaces * sizeof(*sorted_attrib_ptr_buffer));
1609 if (!sorted_attrib_ptr_buffer)
1610 return E_OUTOFMEMORY;
1612 *face_remap = HeapAlloc(GetProcessHeap(), 0, This->numfaces * sizeof(**face_remap));
1613 if (!*face_remap)
1615 HeapFree(GetProcessHeap(), 0, sorted_attrib_ptr_buffer);
1616 return E_OUTOFMEMORY;
1619 for (i = 0; i < This->numfaces; i++)
1620 sorted_attrib_ptr_buffer[i] = &attrib_buffer[i];
1621 qsort(sorted_attrib_ptr_buffer, This->numfaces, sizeof(*sorted_attrib_ptr_buffer), attrib_entry_compare);
1623 for (i = 0; i < This->numfaces; i++)
1625 DWORD old_face = sorted_attrib_ptr_buffer[i] - attrib_buffer;
1626 (*face_remap)[old_face] = i;
1629 /* overwrite sorted_attrib_ptr_buffer with the values themselves */
1630 *sorted_attrib_buffer = (DWORD*)sorted_attrib_ptr_buffer;
1631 for (i = 0; i < This->numfaces; i++)
1632 (*sorted_attrib_buffer)[(*face_remap)[i]] = attrib_buffer[i];
1634 return D3D_OK;
1637 static HRESULT WINAPI d3dx9_mesh_OptimizeInplace(ID3DXMesh *iface, DWORD flags, const DWORD *adjacency_in,
1638 DWORD *adjacency_out, DWORD *face_remap_out, ID3DXBuffer **vertex_remap_out)
1640 struct d3dx9_mesh *This = impl_from_ID3DXMesh(iface);
1641 void *indices = NULL;
1642 DWORD *attrib_buffer = NULL;
1643 HRESULT hr;
1644 ID3DXBuffer *vertex_remap = NULL;
1645 DWORD *face_remap = NULL; /* old -> new mapping */
1646 DWORD *dword_indices = NULL;
1647 DWORD new_num_vertices = 0;
1648 DWORD new_num_alloc_vertices = 0;
1649 IDirect3DVertexBuffer9 *vertex_buffer = NULL;
1650 DWORD *sorted_attrib_buffer = NULL;
1651 DWORD i;
1653 TRACE("iface %p, flags %#x, adjacency_in %p, adjacency_out %p, face_remap_out %p, vertex_remap_out %p.\n",
1654 iface, flags, adjacency_in, adjacency_out, face_remap_out, vertex_remap_out);
1656 if (!flags)
1657 return D3DERR_INVALIDCALL;
1658 if (!adjacency_in && (flags & (D3DXMESHOPT_VERTEXCACHE | D3DXMESHOPT_STRIPREORDER)))
1659 return D3DERR_INVALIDCALL;
1660 if ((flags & (D3DXMESHOPT_VERTEXCACHE | D3DXMESHOPT_STRIPREORDER)) == (D3DXMESHOPT_VERTEXCACHE | D3DXMESHOPT_STRIPREORDER))
1661 return D3DERR_INVALIDCALL;
1663 if (flags & (D3DXMESHOPT_VERTEXCACHE | D3DXMESHOPT_STRIPREORDER))
1665 if (flags & D3DXMESHOPT_VERTEXCACHE)
1666 FIXME("D3DXMESHOPT_VERTEXCACHE not implemented.\n");
1667 if (flags & D3DXMESHOPT_STRIPREORDER)
1668 FIXME("D3DXMESHOPT_STRIPREORDER not implemented.\n");
1669 return E_NOTIMPL;
1672 hr = iface->lpVtbl->LockIndexBuffer(iface, 0, &indices);
1673 if (FAILED(hr)) goto cleanup;
1675 dword_indices = HeapAlloc(GetProcessHeap(), 0, This->numfaces * 3 * sizeof(DWORD));
1676 if (!dword_indices) return E_OUTOFMEMORY;
1677 if (This->options & D3DXMESH_32BIT) {
1678 memcpy(dword_indices, indices, This->numfaces * 3 * sizeof(DWORD));
1679 } else {
1680 WORD *word_indices = indices;
1681 for (i = 0; i < This->numfaces * 3; i++)
1682 dword_indices[i] = *word_indices++;
1685 if ((flags & (D3DXMESHOPT_COMPACT | D3DXMESHOPT_IGNOREVERTS | D3DXMESHOPT_ATTRSORT)) == D3DXMESHOPT_COMPACT)
1687 new_num_alloc_vertices = This->numvertices;
1688 hr = compact_mesh(This, dword_indices, &new_num_vertices, &vertex_remap);
1689 if (FAILED(hr)) goto cleanup;
1690 } else if (flags & D3DXMESHOPT_ATTRSORT) {
1691 if (!(flags & D3DXMESHOPT_IGNOREVERTS))
1692 FIXME("D3DXMESHOPT_ATTRSORT vertex reordering not implemented.\n");
1694 hr = iface->lpVtbl->LockAttributeBuffer(iface, 0, &attrib_buffer);
1695 if (FAILED(hr)) goto cleanup;
1697 hr = remap_faces_for_attrsort(This, dword_indices, attrib_buffer, &sorted_attrib_buffer, &face_remap);
1698 if (FAILED(hr)) goto cleanup;
1701 if (vertex_remap)
1703 /* reorder the vertices using vertex_remap */
1704 D3DVERTEXBUFFER_DESC vertex_desc;
1705 DWORD *vertex_remap_ptr = ID3DXBuffer_GetBufferPointer(vertex_remap);
1706 DWORD vertex_size = iface->lpVtbl->GetNumBytesPerVertex(iface);
1707 BYTE *orig_vertices;
1708 BYTE *new_vertices;
1710 hr = IDirect3DVertexBuffer9_GetDesc(This->vertex_buffer, &vertex_desc);
1711 if (FAILED(hr)) goto cleanup;
1713 hr = IDirect3DDevice9_CreateVertexBuffer(This->device, new_num_alloc_vertices * vertex_size,
1714 vertex_desc.Usage, This->fvf, vertex_desc.Pool, &vertex_buffer, NULL);
1715 if (FAILED(hr)) goto cleanup;
1717 hr = IDirect3DVertexBuffer9_Lock(This->vertex_buffer, 0, 0, (void**)&orig_vertices, D3DLOCK_READONLY);
1718 if (FAILED(hr)) goto cleanup;
1720 hr = IDirect3DVertexBuffer9_Lock(vertex_buffer, 0, 0, (void**)&new_vertices, 0);
1721 if (FAILED(hr)) {
1722 IDirect3DVertexBuffer9_Unlock(This->vertex_buffer);
1723 goto cleanup;
1726 for (i = 0; i < new_num_vertices; i++)
1727 memcpy(new_vertices + i * vertex_size, orig_vertices + vertex_remap_ptr[i] * vertex_size, vertex_size);
1729 IDirect3DVertexBuffer9_Unlock(This->vertex_buffer);
1730 IDirect3DVertexBuffer9_Unlock(vertex_buffer);
1731 } else if (vertex_remap_out) {
1732 DWORD *vertex_remap_ptr;
1734 hr = D3DXCreateBuffer(This->numvertices * sizeof(DWORD), &vertex_remap);
1735 if (FAILED(hr)) goto cleanup;
1736 vertex_remap_ptr = ID3DXBuffer_GetBufferPointer(vertex_remap);
1737 for (i = 0; i < This->numvertices; i++)
1738 *vertex_remap_ptr++ = i;
1741 if (flags & D3DXMESHOPT_ATTRSORT)
1743 D3DXATTRIBUTERANGE *attrib_table;
1744 DWORD attrib_table_size;
1746 attrib_table_size = count_attributes(sorted_attrib_buffer, This->numfaces);
1747 attrib_table = HeapAlloc(GetProcessHeap(), 0, attrib_table_size * sizeof(*attrib_table));
1748 if (!attrib_table) {
1749 hr = E_OUTOFMEMORY;
1750 goto cleanup;
1753 memcpy(attrib_buffer, sorted_attrib_buffer, This->numfaces * sizeof(*attrib_buffer));
1755 /* reorder the indices using face_remap */
1756 if (This->options & D3DXMESH_32BIT) {
1757 for (i = 0; i < This->numfaces; i++)
1758 memcpy((DWORD*)indices + face_remap[i] * 3, dword_indices + i * 3, 3 * sizeof(DWORD));
1759 } else {
1760 WORD *word_indices = indices;
1761 for (i = 0; i < This->numfaces; i++) {
1762 DWORD new_pos = face_remap[i] * 3;
1763 DWORD old_pos = i * 3;
1764 word_indices[new_pos++] = dword_indices[old_pos++];
1765 word_indices[new_pos++] = dword_indices[old_pos++];
1766 word_indices[new_pos] = dword_indices[old_pos];
1770 fill_attribute_table(attrib_buffer, This->numfaces, indices,
1771 This->options & D3DXMESH_32BIT, attrib_table);
1773 HeapFree(GetProcessHeap(), 0, This->attrib_table);
1774 This->attrib_table = attrib_table;
1775 This->attrib_table_size = attrib_table_size;
1776 } else {
1777 if (This->options & D3DXMESH_32BIT) {
1778 memcpy(indices, dword_indices, This->numfaces * 3 * sizeof(DWORD));
1779 } else {
1780 WORD *word_indices = indices;
1781 for (i = 0; i < This->numfaces * 3; i++)
1782 *word_indices++ = dword_indices[i];
1786 if (adjacency_out) {
1787 if (face_remap) {
1788 for (i = 0; i < This->numfaces; i++) {
1789 DWORD old_pos = i * 3;
1790 DWORD new_pos = face_remap[i] * 3;
1791 adjacency_out[new_pos++] = face_remap[adjacency_in[old_pos++]];
1792 adjacency_out[new_pos++] = face_remap[adjacency_in[old_pos++]];
1793 adjacency_out[new_pos++] = face_remap[adjacency_in[old_pos++]];
1795 } else {
1796 memcpy(adjacency_out, adjacency_in, This->numfaces * 3 * sizeof(*adjacency_out));
1799 if (face_remap_out) {
1800 if (face_remap) {
1801 for (i = 0; i < This->numfaces; i++)
1802 face_remap_out[face_remap[i]] = i;
1803 } else {
1804 for (i = 0; i < This->numfaces; i++)
1805 face_remap_out[i] = i;
1808 if (vertex_remap_out)
1809 *vertex_remap_out = vertex_remap;
1810 vertex_remap = NULL;
1812 if (vertex_buffer) {
1813 IDirect3DVertexBuffer9_Release(This->vertex_buffer);
1814 This->vertex_buffer = vertex_buffer;
1815 vertex_buffer = NULL;
1816 This->numvertices = new_num_vertices;
1819 hr = D3D_OK;
1820 cleanup:
1821 HeapFree(GetProcessHeap(), 0, sorted_attrib_buffer);
1822 HeapFree(GetProcessHeap(), 0, face_remap);
1823 HeapFree(GetProcessHeap(), 0, dword_indices);
1824 if (vertex_remap) ID3DXBuffer_Release(vertex_remap);
1825 if (vertex_buffer) IDirect3DVertexBuffer9_Release(vertex_buffer);
1826 if (attrib_buffer) iface->lpVtbl->UnlockAttributeBuffer(iface);
1827 if (indices) iface->lpVtbl->UnlockIndexBuffer(iface);
1828 return hr;
1831 static HRESULT WINAPI d3dx9_mesh_SetAttributeTable(ID3DXMesh *iface,
1832 const D3DXATTRIBUTERANGE *attrib_table, DWORD attrib_table_size)
1834 struct d3dx9_mesh *mesh = impl_from_ID3DXMesh(iface);
1835 D3DXATTRIBUTERANGE *new_table = NULL;
1837 TRACE("iface %p, attrib_table %p, attrib_table_size %u.\n", iface, attrib_table, attrib_table_size);
1839 if (attrib_table_size) {
1840 size_t size = attrib_table_size * sizeof(*attrib_table);
1842 new_table = HeapAlloc(GetProcessHeap(), 0, size);
1843 if (!new_table)
1844 return E_OUTOFMEMORY;
1846 CopyMemory(new_table, attrib_table, size);
1847 } else if (attrib_table) {
1848 return D3DERR_INVALIDCALL;
1850 HeapFree(GetProcessHeap(), 0, mesh->attrib_table);
1851 mesh->attrib_table = new_table;
1852 mesh->attrib_table_size = attrib_table_size;
1854 return D3D_OK;
1857 static const struct ID3DXMeshVtbl D3DXMesh_Vtbl =
1859 d3dx9_mesh_QueryInterface,
1860 d3dx9_mesh_AddRef,
1861 d3dx9_mesh_Release,
1862 d3dx9_mesh_DrawSubset,
1863 d3dx9_mesh_GetNumFaces,
1864 d3dx9_mesh_GetNumVertices,
1865 d3dx9_mesh_GetFVF,
1866 d3dx9_mesh_GetDeclaration,
1867 d3dx9_mesh_GetNumBytesPerVertex,
1868 d3dx9_mesh_GetOptions,
1869 d3dx9_mesh_GetDevice,
1870 d3dx9_mesh_CloneMeshFVF,
1871 d3dx9_mesh_CloneMesh,
1872 d3dx9_mesh_GetVertexBuffer,
1873 d3dx9_mesh_GetIndexBuffer,
1874 d3dx9_mesh_LockVertexBuffer,
1875 d3dx9_mesh_UnlockVertexBuffer,
1876 d3dx9_mesh_LockIndexBuffer,
1877 d3dx9_mesh_UnlockIndexBuffer,
1878 d3dx9_mesh_GetAttributeTable,
1879 d3dx9_mesh_ConvertPointRepsToAdjacency,
1880 d3dx9_mesh_ConvertAdjacencyToPointReps,
1881 d3dx9_mesh_GenerateAdjacency,
1882 d3dx9_mesh_UpdateSemantics,
1883 d3dx9_mesh_LockAttributeBuffer,
1884 d3dx9_mesh_UnlockAttributeBuffer,
1885 d3dx9_mesh_Optimize,
1886 d3dx9_mesh_OptimizeInplace,
1887 d3dx9_mesh_SetAttributeTable,
1891 /* Algorithm taken from the article: An Efficient and Robust Ray-Box Intersection Algorithm
1892 Amy Williams University of Utah
1893 Steve Barrus University of Utah
1894 R. Keith Morley University of Utah
1895 Peter Shirley University of Utah
1897 International Conference on Computer Graphics and Interactive Techniques archive
1898 ACM SIGGRAPH 2005 Courses
1899 Los Angeles, California
1901 This algorithm is free of patents or of copyrights, as confirmed by Peter Shirley himself.
1903 Algorithm: Consider the box as the intersection of three slabs. Clip the ray
1904 against each slab, if there's anything left of the ray after we're
1905 done we've got an intersection of the ray with the box. */
1906 BOOL WINAPI D3DXBoxBoundProbe(const D3DXVECTOR3 *pmin, const D3DXVECTOR3 *pmax,
1907 const D3DXVECTOR3 *prayposition, const D3DXVECTOR3 *praydirection)
1909 FLOAT div, tmin, tmax, tymin, tymax, tzmin, tzmax;
1911 div = 1.0f / praydirection->x;
1912 if ( div >= 0.0f )
1914 tmin = ( pmin->x - prayposition->x ) * div;
1915 tmax = ( pmax->x - prayposition->x ) * div;
1917 else
1919 tmin = ( pmax->x - prayposition->x ) * div;
1920 tmax = ( pmin->x - prayposition->x ) * div;
1923 if ( tmax < 0.0f ) return FALSE;
1925 div = 1.0f / praydirection->y;
1926 if ( div >= 0.0f )
1928 tymin = ( pmin->y - prayposition->y ) * div;
1929 tymax = ( pmax->y - prayposition->y ) * div;
1931 else
1933 tymin = ( pmax->y - prayposition->y ) * div;
1934 tymax = ( pmin->y - prayposition->y ) * div;
1937 if ( ( tymax < 0.0f ) || ( tmin > tymax ) || ( tymin > tmax ) ) return FALSE;
1939 if ( tymin > tmin ) tmin = tymin;
1940 if ( tymax < tmax ) tmax = tymax;
1942 div = 1.0f / praydirection->z;
1943 if ( div >= 0.0f )
1945 tzmin = ( pmin->z - prayposition->z ) * div;
1946 tzmax = ( pmax->z - prayposition->z ) * div;
1948 else
1950 tzmin = ( pmax->z - prayposition->z ) * div;
1951 tzmax = ( pmin->z - prayposition->z ) * div;
1954 if ( (tzmax < 0.0f ) || ( tmin > tzmax ) || ( tzmin > tmax ) ) return FALSE;
1956 return TRUE;
1959 HRESULT WINAPI D3DXComputeBoundingBox(const D3DXVECTOR3 *pfirstposition,
1960 DWORD numvertices, DWORD dwstride, D3DXVECTOR3 *pmin, D3DXVECTOR3 *pmax)
1962 D3DXVECTOR3 vec;
1963 unsigned int i;
1965 if( !pfirstposition || !pmin || !pmax ) return D3DERR_INVALIDCALL;
1967 *pmin = *pfirstposition;
1968 *pmax = *pmin;
1970 for(i=0; i<numvertices; i++)
1972 vec = *( (const D3DXVECTOR3*)((const char*)pfirstposition + dwstride * i) );
1974 if ( vec.x < pmin->x ) pmin->x = vec.x;
1975 if ( vec.x > pmax->x ) pmax->x = vec.x;
1977 if ( vec.y < pmin->y ) pmin->y = vec.y;
1978 if ( vec.y > pmax->y ) pmax->y = vec.y;
1980 if ( vec.z < pmin->z ) pmin->z = vec.z;
1981 if ( vec.z > pmax->z ) pmax->z = vec.z;
1984 return D3D_OK;
1987 HRESULT WINAPI D3DXComputeBoundingSphere(const D3DXVECTOR3 *pfirstposition,
1988 DWORD numvertices, DWORD dwstride, D3DXVECTOR3 *pcenter, float *pradius)
1990 D3DXVECTOR3 temp;
1991 FLOAT d;
1992 unsigned int i;
1994 if( !pfirstposition || !pcenter || !pradius ) return D3DERR_INVALIDCALL;
1996 temp.x = 0.0f;
1997 temp.y = 0.0f;
1998 temp.z = 0.0f;
1999 *pradius = 0.0f;
2001 for(i=0; i<numvertices; i++)
2002 D3DXVec3Add(&temp, &temp, (const D3DXVECTOR3*)((const char*)pfirstposition + dwstride * i));
2004 D3DXVec3Scale(pcenter, &temp, 1.0f / numvertices);
2006 for(i=0; i<numvertices; i++)
2008 d = D3DXVec3Length(D3DXVec3Subtract(&temp, (const D3DXVECTOR3*)((const char*)pfirstposition + dwstride * i), pcenter));
2009 if ( d > *pradius ) *pradius = d;
2011 return D3D_OK;
2014 static void append_decl_element(D3DVERTEXELEMENT9 *declaration, UINT *idx, UINT *offset,
2015 D3DDECLTYPE type, D3DDECLUSAGE usage, UINT usage_idx)
2017 declaration[*idx].Stream = 0;
2018 declaration[*idx].Offset = *offset;
2019 declaration[*idx].Type = type;
2020 declaration[*idx].Method = D3DDECLMETHOD_DEFAULT;
2021 declaration[*idx].Usage = usage;
2022 declaration[*idx].UsageIndex = usage_idx;
2024 *offset += d3dx_decltype_size[type];
2025 ++(*idx);
2028 /*************************************************************************
2029 * D3DXDeclaratorFromFVF
2031 HRESULT WINAPI D3DXDeclaratorFromFVF(DWORD fvf, D3DVERTEXELEMENT9 declaration[MAX_FVF_DECL_SIZE])
2033 static const D3DVERTEXELEMENT9 end_element = D3DDECL_END();
2034 DWORD tex_count = (fvf & D3DFVF_TEXCOUNT_MASK) >> D3DFVF_TEXCOUNT_SHIFT;
2035 unsigned int offset = 0;
2036 unsigned int idx = 0;
2037 unsigned int i;
2039 TRACE("fvf %#x, declaration %p.\n", fvf, declaration);
2041 if (fvf & (D3DFVF_RESERVED0 | D3DFVF_RESERVED2)) return D3DERR_INVALIDCALL;
2043 if (fvf & D3DFVF_POSITION_MASK)
2045 BOOL has_blend = (fvf & D3DFVF_XYZB5) >= D3DFVF_XYZB1;
2046 DWORD blend_count = 1 + (((fvf & D3DFVF_XYZB5) - D3DFVF_XYZB1) >> 1);
2047 BOOL has_blend_idx = (fvf & D3DFVF_LASTBETA_D3DCOLOR) || (fvf & D3DFVF_LASTBETA_UBYTE4);
2049 if (has_blend_idx) --blend_count;
2051 if ((fvf & D3DFVF_POSITION_MASK) == D3DFVF_XYZW
2052 || (has_blend && blend_count > 4))
2053 return D3DERR_INVALIDCALL;
2055 if ((fvf & D3DFVF_POSITION_MASK) == D3DFVF_XYZRHW)
2056 append_decl_element(declaration, &idx, &offset, D3DDECLTYPE_FLOAT4, D3DDECLUSAGE_POSITIONT, 0);
2057 else
2058 append_decl_element(declaration, &idx, &offset, D3DDECLTYPE_FLOAT3, D3DDECLUSAGE_POSITION, 0);
2060 if (has_blend)
2062 switch (blend_count)
2064 case 0:
2065 break;
2066 case 1:
2067 append_decl_element(declaration, &idx, &offset, D3DDECLTYPE_FLOAT1, D3DDECLUSAGE_BLENDWEIGHT, 0);
2068 break;
2069 case 2:
2070 append_decl_element(declaration, &idx, &offset, D3DDECLTYPE_FLOAT2, D3DDECLUSAGE_BLENDWEIGHT, 0);
2071 break;
2072 case 3:
2073 append_decl_element(declaration, &idx, &offset, D3DDECLTYPE_FLOAT3, D3DDECLUSAGE_BLENDWEIGHT, 0);
2074 break;
2075 case 4:
2076 append_decl_element(declaration, &idx, &offset, D3DDECLTYPE_FLOAT4, D3DDECLUSAGE_BLENDWEIGHT, 0);
2077 break;
2078 default:
2079 ERR("Invalid blend count %u.\n", blend_count);
2080 break;
2083 if (has_blend_idx)
2085 if (fvf & D3DFVF_LASTBETA_UBYTE4)
2086 append_decl_element(declaration, &idx, &offset, D3DDECLTYPE_UBYTE4, D3DDECLUSAGE_BLENDINDICES, 0);
2087 else if (fvf & D3DFVF_LASTBETA_D3DCOLOR)
2088 append_decl_element(declaration, &idx, &offset, D3DDECLTYPE_D3DCOLOR, D3DDECLUSAGE_BLENDINDICES, 0);
2093 if (fvf & D3DFVF_NORMAL)
2094 append_decl_element(declaration, &idx, &offset, D3DDECLTYPE_FLOAT3, D3DDECLUSAGE_NORMAL, 0);
2095 if (fvf & D3DFVF_PSIZE)
2096 append_decl_element(declaration, &idx, &offset, D3DDECLTYPE_FLOAT1, D3DDECLUSAGE_PSIZE, 0);
2097 if (fvf & D3DFVF_DIFFUSE)
2098 append_decl_element(declaration, &idx, &offset, D3DDECLTYPE_D3DCOLOR, D3DDECLUSAGE_COLOR, 0);
2099 if (fvf & D3DFVF_SPECULAR)
2100 append_decl_element(declaration, &idx, &offset, D3DDECLTYPE_D3DCOLOR, D3DDECLUSAGE_COLOR, 1);
2102 for (i = 0; i < tex_count; ++i)
2104 switch ((fvf >> (16 + 2 * i)) & 0x03)
2106 case D3DFVF_TEXTUREFORMAT1:
2107 append_decl_element(declaration, &idx, &offset, D3DDECLTYPE_FLOAT1, D3DDECLUSAGE_TEXCOORD, i);
2108 break;
2109 case D3DFVF_TEXTUREFORMAT2:
2110 append_decl_element(declaration, &idx, &offset, D3DDECLTYPE_FLOAT2, D3DDECLUSAGE_TEXCOORD, i);
2111 break;
2112 case D3DFVF_TEXTUREFORMAT3:
2113 append_decl_element(declaration, &idx, &offset, D3DDECLTYPE_FLOAT3, D3DDECLUSAGE_TEXCOORD, i);
2114 break;
2115 case D3DFVF_TEXTUREFORMAT4:
2116 append_decl_element(declaration, &idx, &offset, D3DDECLTYPE_FLOAT4, D3DDECLUSAGE_TEXCOORD, i);
2117 break;
2121 declaration[idx] = end_element;
2123 return D3D_OK;
2126 /*************************************************************************
2127 * D3DXFVFFromDeclarator
2129 HRESULT WINAPI D3DXFVFFromDeclarator(const D3DVERTEXELEMENT9 *declaration, DWORD *fvf)
2131 unsigned int i = 0, texture, offset;
2133 TRACE("(%p, %p)\n", declaration, fvf);
2135 *fvf = 0;
2136 if (declaration[0].Type == D3DDECLTYPE_FLOAT3 && declaration[0].Usage == D3DDECLUSAGE_POSITION)
2138 if ((declaration[1].Type == D3DDECLTYPE_FLOAT4 && declaration[1].Usage == D3DDECLUSAGE_BLENDWEIGHT &&
2139 declaration[1].UsageIndex == 0) &&
2140 (declaration[2].Type == D3DDECLTYPE_FLOAT1 && declaration[2].Usage == D3DDECLUSAGE_BLENDINDICES &&
2141 declaration[2].UsageIndex == 0))
2143 return D3DERR_INVALIDCALL;
2145 else if ((declaration[1].Type == D3DDECLTYPE_UBYTE4 || declaration[1].Type == D3DDECLTYPE_D3DCOLOR) &&
2146 declaration[1].Usage == D3DDECLUSAGE_BLENDINDICES && declaration[1].UsageIndex == 0)
2148 if (declaration[1].Type == D3DDECLTYPE_UBYTE4)
2150 *fvf |= D3DFVF_XYZB1 | D3DFVF_LASTBETA_UBYTE4;
2152 else
2154 *fvf |= D3DFVF_XYZB1 | D3DFVF_LASTBETA_D3DCOLOR;
2156 i = 2;
2158 else if (declaration[1].Type <= D3DDECLTYPE_FLOAT4 && declaration[1].Usage == D3DDECLUSAGE_BLENDWEIGHT &&
2159 declaration[1].UsageIndex == 0)
2161 if ((declaration[2].Type == D3DDECLTYPE_UBYTE4 || declaration[2].Type == D3DDECLTYPE_D3DCOLOR) &&
2162 declaration[2].Usage == D3DDECLUSAGE_BLENDINDICES && declaration[2].UsageIndex == 0)
2164 if (declaration[2].Type == D3DDECLTYPE_UBYTE4)
2166 *fvf |= D3DFVF_LASTBETA_UBYTE4;
2168 else
2170 *fvf |= D3DFVF_LASTBETA_D3DCOLOR;
2172 switch (declaration[1].Type)
2174 case D3DDECLTYPE_FLOAT1: *fvf |= D3DFVF_XYZB2; break;
2175 case D3DDECLTYPE_FLOAT2: *fvf |= D3DFVF_XYZB3; break;
2176 case D3DDECLTYPE_FLOAT3: *fvf |= D3DFVF_XYZB4; break;
2177 case D3DDECLTYPE_FLOAT4: *fvf |= D3DFVF_XYZB5; break;
2179 i = 3;
2181 else
2183 switch (declaration[1].Type)
2185 case D3DDECLTYPE_FLOAT1: *fvf |= D3DFVF_XYZB1; break;
2186 case D3DDECLTYPE_FLOAT2: *fvf |= D3DFVF_XYZB2; break;
2187 case D3DDECLTYPE_FLOAT3: *fvf |= D3DFVF_XYZB3; break;
2188 case D3DDECLTYPE_FLOAT4: *fvf |= D3DFVF_XYZB4; break;
2190 i = 2;
2193 else
2195 *fvf |= D3DFVF_XYZ;
2196 i = 1;
2199 else if (declaration[0].Type == D3DDECLTYPE_FLOAT4 && declaration[0].Usage == D3DDECLUSAGE_POSITIONT &&
2200 declaration[0].UsageIndex == 0)
2202 *fvf |= D3DFVF_XYZRHW;
2203 i = 1;
2206 if (declaration[i].Type == D3DDECLTYPE_FLOAT3 && declaration[i].Usage == D3DDECLUSAGE_NORMAL)
2208 *fvf |= D3DFVF_NORMAL;
2209 i++;
2211 if (declaration[i].Type == D3DDECLTYPE_FLOAT1 && declaration[i].Usage == D3DDECLUSAGE_PSIZE &&
2212 declaration[i].UsageIndex == 0)
2214 *fvf |= D3DFVF_PSIZE;
2215 i++;
2217 if (declaration[i].Type == D3DDECLTYPE_D3DCOLOR && declaration[i].Usage == D3DDECLUSAGE_COLOR &&
2218 declaration[i].UsageIndex == 0)
2220 *fvf |= D3DFVF_DIFFUSE;
2221 i++;
2223 if (declaration[i].Type == D3DDECLTYPE_D3DCOLOR && declaration[i].Usage == D3DDECLUSAGE_COLOR &&
2224 declaration[i].UsageIndex == 1)
2226 *fvf |= D3DFVF_SPECULAR;
2227 i++;
2230 for (texture = 0; texture < D3DDP_MAXTEXCOORD; i++, texture++)
2232 if (declaration[i].Stream == 0xFF)
2234 break;
2236 else if (declaration[i].Type == D3DDECLTYPE_FLOAT1 && declaration[i].Usage == D3DDECLUSAGE_TEXCOORD &&
2237 declaration[i].UsageIndex == texture)
2239 *fvf |= D3DFVF_TEXCOORDSIZE1(declaration[i].UsageIndex);
2241 else if (declaration[i].Type == D3DDECLTYPE_FLOAT2 && declaration[i].Usage == D3DDECLUSAGE_TEXCOORD &&
2242 declaration[i].UsageIndex == texture)
2244 *fvf |= D3DFVF_TEXCOORDSIZE2(declaration[i].UsageIndex);
2246 else if (declaration[i].Type == D3DDECLTYPE_FLOAT3 && declaration[i].Usage == D3DDECLUSAGE_TEXCOORD &&
2247 declaration[i].UsageIndex == texture)
2249 *fvf |= D3DFVF_TEXCOORDSIZE3(declaration[i].UsageIndex);
2251 else if (declaration[i].Type == D3DDECLTYPE_FLOAT4 && declaration[i].Usage == D3DDECLUSAGE_TEXCOORD &&
2252 declaration[i].UsageIndex == texture)
2254 *fvf |= D3DFVF_TEXCOORDSIZE4(declaration[i].UsageIndex);
2256 else
2258 return D3DERR_INVALIDCALL;
2262 *fvf |= (texture << D3DFVF_TEXCOUNT_SHIFT);
2264 for (offset = 0, i = 0; declaration[i].Stream != 0xFF;
2265 offset += d3dx_decltype_size[declaration[i].Type], i++)
2267 if (declaration[i].Offset != offset)
2269 return D3DERR_INVALIDCALL;
2273 return D3D_OK;
2276 /*************************************************************************
2277 * D3DXGetFVFVertexSize
2279 static UINT Get_TexCoord_Size_From_FVF(DWORD FVF, int tex_num)
2281 return (((((FVF) >> (16 + (2 * (tex_num)))) + 1) & 0x03) + 1);
2284 UINT WINAPI D3DXGetFVFVertexSize(DWORD FVF)
2286 DWORD size = 0;
2287 UINT i;
2288 UINT numTextures = (FVF & D3DFVF_TEXCOUNT_MASK) >> D3DFVF_TEXCOUNT_SHIFT;
2290 if (FVF & D3DFVF_NORMAL) size += sizeof(D3DXVECTOR3);
2291 if (FVF & D3DFVF_DIFFUSE) size += sizeof(DWORD);
2292 if (FVF & D3DFVF_SPECULAR) size += sizeof(DWORD);
2293 if (FVF & D3DFVF_PSIZE) size += sizeof(DWORD);
2295 switch (FVF & D3DFVF_POSITION_MASK)
2297 case D3DFVF_XYZ: size += sizeof(D3DXVECTOR3); break;
2298 case D3DFVF_XYZRHW: size += 4 * sizeof(FLOAT); break;
2299 case D3DFVF_XYZB1: size += 4 * sizeof(FLOAT); break;
2300 case D3DFVF_XYZB2: size += 5 * sizeof(FLOAT); break;
2301 case D3DFVF_XYZB3: size += 6 * sizeof(FLOAT); break;
2302 case D3DFVF_XYZB4: size += 7 * sizeof(FLOAT); break;
2303 case D3DFVF_XYZB5: size += 8 * sizeof(FLOAT); break;
2304 case D3DFVF_XYZW: size += 4 * sizeof(FLOAT); break;
2307 for (i = 0; i < numTextures; i++)
2309 size += Get_TexCoord_Size_From_FVF(FVF, i) * sizeof(FLOAT);
2312 return size;
2315 /*************************************************************************
2316 * D3DXGetDeclVertexSize
2318 UINT WINAPI D3DXGetDeclVertexSize(const D3DVERTEXELEMENT9 *decl, DWORD stream_idx)
2320 const D3DVERTEXELEMENT9 *element;
2321 UINT size = 0;
2323 TRACE("decl %p, stream_idx %u\n", decl, stream_idx);
2325 if (!decl) return 0;
2327 for (element = decl; element->Stream != 0xff; ++element)
2329 UINT type_size;
2331 if (element->Stream != stream_idx) continue;
2333 if (element->Type >= ARRAY_SIZE(d3dx_decltype_size))
2335 FIXME("Unhandled element type %#x, size will be incorrect.\n", element->Type);
2336 continue;
2339 type_size = d3dx_decltype_size[element->Type];
2340 if (element->Offset + type_size > size) size = element->Offset + type_size;
2343 return size;
2346 /*************************************************************************
2347 * D3DXGetDeclLength
2349 UINT WINAPI D3DXGetDeclLength(const D3DVERTEXELEMENT9 *decl)
2351 const D3DVERTEXELEMENT9 *element;
2353 TRACE("decl %p\n", decl);
2355 /* null decl results in exception on Windows XP */
2357 for (element = decl; element->Stream != 0xff; ++element);
2359 return element - decl;
2362 BOOL WINAPI D3DXIntersectTri(const D3DXVECTOR3 *p0, const D3DXVECTOR3 *p1, const D3DXVECTOR3 *p2,
2363 const D3DXVECTOR3 *praypos, const D3DXVECTOR3 *praydir, float *pu, float *pv, float *pdist)
2365 D3DXMATRIX m;
2366 D3DXVECTOR4 vec;
2368 TRACE("p0 %p, p1 %p, p2 %p, praypos %p, praydir %p, pu %p, pv %p, pdist %p.\n",
2369 p0, p1, p2, praypos, praydir, pu, pv, pdist);
2371 m.u.m[0][0] = p1->x - p0->x;
2372 m.u.m[1][0] = p2->x - p0->x;
2373 m.u.m[2][0] = -praydir->x;
2374 m.u.m[3][0] = 0.0f;
2375 m.u.m[0][1] = p1->y - p0->y;
2376 m.u.m[1][1] = p2->y - p0->y;
2377 m.u.m[2][1] = -praydir->y;
2378 m.u.m[3][1] = 0.0f;
2379 m.u.m[0][2] = p1->z - p0->z;
2380 m.u.m[1][2] = p2->z - p0->z;
2381 m.u.m[2][2] = -praydir->z;
2382 m.u.m[3][2] = 0.0f;
2383 m.u.m[0][3] = 0.0f;
2384 m.u.m[1][3] = 0.0f;
2385 m.u.m[2][3] = 0.0f;
2386 m.u.m[3][3] = 1.0f;
2388 vec.x = praypos->x - p0->x;
2389 vec.y = praypos->y - p0->y;
2390 vec.z = praypos->z - p0->z;
2391 vec.w = 0.0f;
2393 if ( D3DXMatrixInverse(&m, NULL, &m) )
2395 D3DXVec4Transform(&vec, &vec, &m);
2396 if ( (vec.x >= 0.0f) && (vec.y >= 0.0f) && (vec.x + vec.y <= 1.0f) && (vec.z >= 0.0f) )
2398 if (pu) *pu = vec.x;
2399 if (pv) *pv = vec.y;
2400 if (pdist) *pdist = fabsf( vec.z );
2401 return TRUE;
2405 return FALSE;
2408 BOOL WINAPI D3DXSphereBoundProbe(const D3DXVECTOR3 *center, float radius,
2409 const D3DXVECTOR3 *ray_position, const D3DXVECTOR3 *ray_direction)
2411 D3DXVECTOR3 difference = {0};
2412 float a, b, c, d;
2414 D3DXVec3Subtract(&difference, ray_position, center);
2415 c = D3DXVec3LengthSq(&difference) - radius * radius;
2416 if (c < 0.0f)
2417 return TRUE;
2418 a = D3DXVec3LengthSq(ray_direction);
2419 b = D3DXVec3Dot(&difference, ray_direction);
2420 d = b * b - a * c;
2422 return d >= 0.0f && (b <= 0.0f || d > b * b);
2425 /*************************************************************************
2426 * D3DXCreateMesh
2428 HRESULT WINAPI D3DXCreateMesh(DWORD numfaces, DWORD numvertices, DWORD options,
2429 const D3DVERTEXELEMENT9 *declaration, struct IDirect3DDevice9 *device, struct ID3DXMesh **mesh)
2431 HRESULT hr;
2432 DWORD fvf;
2433 IDirect3DVertexDeclaration9 *vertex_declaration;
2434 UINT vertex_declaration_size;
2435 UINT num_elem;
2436 IDirect3DVertexBuffer9 *vertex_buffer;
2437 IDirect3DIndexBuffer9 *index_buffer;
2438 DWORD *attrib_buffer;
2439 struct d3dx9_mesh *object;
2440 DWORD index_usage = 0;
2441 D3DPOOL index_pool = D3DPOOL_DEFAULT;
2442 D3DFORMAT index_format = D3DFMT_INDEX16;
2443 DWORD vertex_usage = 0;
2444 D3DPOOL vertex_pool = D3DPOOL_DEFAULT;
2445 int i;
2447 TRACE("numfaces %u, numvertices %u, options %#x, declaration %p, device %p, mesh %p.\n",
2448 numfaces, numvertices, options, declaration, device, mesh);
2450 if (numfaces == 0 || numvertices == 0 || declaration == NULL || device == NULL || mesh == NULL ||
2451 /* D3DXMESH_VB_SHARE is for cloning, and D3DXMESH_USEHWONLY is for ConvertToBlendedMesh */
2452 (options & (D3DXMESH_VB_SHARE | D3DXMESH_USEHWONLY | 0xfffe0000)))
2454 return D3DERR_INVALIDCALL;
2456 for (i = 0; declaration[i].Stream != 0xff; i++)
2457 if (declaration[i].Stream != 0)
2458 return D3DERR_INVALIDCALL;
2459 num_elem = i + 1;
2461 if (options & D3DXMESH_32BIT)
2462 index_format = D3DFMT_INDEX32;
2464 if (options & D3DXMESH_DONOTCLIP) {
2465 index_usage |= D3DUSAGE_DONOTCLIP;
2466 vertex_usage |= D3DUSAGE_DONOTCLIP;
2468 if (options & D3DXMESH_POINTS) {
2469 index_usage |= D3DUSAGE_POINTS;
2470 vertex_usage |= D3DUSAGE_POINTS;
2472 if (options & D3DXMESH_RTPATCHES) {
2473 index_usage |= D3DUSAGE_RTPATCHES;
2474 vertex_usage |= D3DUSAGE_RTPATCHES;
2476 if (options & D3DXMESH_NPATCHES) {
2477 index_usage |= D3DUSAGE_NPATCHES;
2478 vertex_usage |= D3DUSAGE_NPATCHES;
2481 if (options & D3DXMESH_VB_SYSTEMMEM)
2482 vertex_pool = D3DPOOL_SYSTEMMEM;
2483 else if (options & D3DXMESH_VB_MANAGED)
2484 vertex_pool = D3DPOOL_MANAGED;
2486 if (options & D3DXMESH_VB_WRITEONLY)
2487 vertex_usage |= D3DUSAGE_WRITEONLY;
2488 if (options & D3DXMESH_VB_DYNAMIC)
2489 vertex_usage |= D3DUSAGE_DYNAMIC;
2490 if (options & D3DXMESH_VB_SOFTWAREPROCESSING)
2491 vertex_usage |= D3DUSAGE_SOFTWAREPROCESSING;
2493 if (options & D3DXMESH_IB_SYSTEMMEM)
2494 index_pool = D3DPOOL_SYSTEMMEM;
2495 else if (options & D3DXMESH_IB_MANAGED)
2496 index_pool = D3DPOOL_MANAGED;
2498 if (options & D3DXMESH_IB_WRITEONLY)
2499 index_usage |= D3DUSAGE_WRITEONLY;
2500 if (options & D3DXMESH_IB_DYNAMIC)
2501 index_usage |= D3DUSAGE_DYNAMIC;
2502 if (options & D3DXMESH_IB_SOFTWAREPROCESSING)
2503 index_usage |= D3DUSAGE_SOFTWAREPROCESSING;
2505 hr = D3DXFVFFromDeclarator(declaration, &fvf);
2506 if (hr != D3D_OK)
2508 fvf = 0;
2511 /* Create vertex declaration */
2512 hr = IDirect3DDevice9_CreateVertexDeclaration(device,
2513 declaration,
2514 &vertex_declaration);
2515 if (FAILED(hr))
2517 WARN("Unexpected return value %x from IDirect3DDevice9_CreateVertexDeclaration.\n",hr);
2518 return hr;
2520 vertex_declaration_size = D3DXGetDeclVertexSize(declaration, declaration[0].Stream);
2522 /* Create vertex buffer */
2523 hr = IDirect3DDevice9_CreateVertexBuffer(device,
2524 numvertices * vertex_declaration_size,
2525 vertex_usage,
2526 fvf,
2527 vertex_pool,
2528 &vertex_buffer,
2529 NULL);
2530 if (FAILED(hr))
2532 WARN("Unexpected return value %x from IDirect3DDevice9_CreateVertexBuffer.\n",hr);
2533 IDirect3DVertexDeclaration9_Release(vertex_declaration);
2534 return hr;
2537 /* Create index buffer */
2538 hr = IDirect3DDevice9_CreateIndexBuffer(device,
2539 numfaces * 3 * ((index_format == D3DFMT_INDEX16) ? 2 : 4),
2540 index_usage,
2541 index_format,
2542 index_pool,
2543 &index_buffer,
2544 NULL);
2545 if (FAILED(hr))
2547 WARN("Unexpected return value %x from IDirect3DDevice9_CreateVertexBuffer.\n",hr);
2548 IDirect3DVertexBuffer9_Release(vertex_buffer);
2549 IDirect3DVertexDeclaration9_Release(vertex_declaration);
2550 return hr;
2553 attrib_buffer = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, numfaces * sizeof(*attrib_buffer));
2554 object = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(*object));
2555 if (object == NULL || attrib_buffer == NULL)
2557 HeapFree(GetProcessHeap(), 0, object);
2558 HeapFree(GetProcessHeap(), 0, attrib_buffer);
2559 IDirect3DIndexBuffer9_Release(index_buffer);
2560 IDirect3DVertexBuffer9_Release(vertex_buffer);
2561 IDirect3DVertexDeclaration9_Release(vertex_declaration);
2562 *mesh = NULL;
2563 return E_OUTOFMEMORY;
2565 object->ID3DXMesh_iface.lpVtbl = &D3DXMesh_Vtbl;
2566 object->ref = 1;
2568 object->numfaces = numfaces;
2569 object->numvertices = numvertices;
2570 object->options = options;
2571 object->fvf = fvf;
2572 object->device = device;
2573 IDirect3DDevice9_AddRef(device);
2575 copy_declaration(object->cached_declaration, declaration, num_elem);
2576 object->vertex_declaration = vertex_declaration;
2577 object->vertex_declaration_size = vertex_declaration_size;
2578 object->num_elem = num_elem;
2579 object->vertex_buffer = vertex_buffer;
2580 object->index_buffer = index_buffer;
2581 object->attrib_buffer = attrib_buffer;
2583 *mesh = &object->ID3DXMesh_iface;
2585 return D3D_OK;
2588 /*************************************************************************
2589 * D3DXCreateMeshFVF
2591 HRESULT WINAPI D3DXCreateMeshFVF(DWORD numfaces, DWORD numvertices, DWORD options,
2592 DWORD fvf, struct IDirect3DDevice9 *device, struct ID3DXMesh **mesh)
2594 HRESULT hr;
2595 D3DVERTEXELEMENT9 declaration[MAX_FVF_DECL_SIZE];
2597 TRACE("(%u, %u, %u, %u, %p, %p)\n", numfaces, numvertices, options, fvf, device, mesh);
2599 hr = D3DXDeclaratorFromFVF(fvf, declaration);
2600 if (FAILED(hr)) return hr;
2602 return D3DXCreateMesh(numfaces, numvertices, options, declaration, device, mesh);
2606 struct mesh_data {
2607 DWORD num_vertices;
2608 DWORD num_poly_faces;
2609 DWORD num_tri_faces;
2610 D3DXVECTOR3 *vertices;
2611 DWORD *num_tri_per_face;
2612 DWORD *indices;
2614 DWORD fvf;
2616 /* optional mesh data */
2618 DWORD num_normals;
2619 D3DXVECTOR3 *normals;
2620 DWORD *normal_indices;
2622 D3DXVECTOR2 *tex_coords;
2624 DWORD *vertex_colors;
2626 DWORD num_materials;
2627 D3DXMATERIAL *materials;
2628 DWORD *material_indices;
2630 struct ID3DXSkinInfo *skin_info;
2631 DWORD nb_bones;
2634 static HRESULT parse_texture_filename(ID3DXFileData *filedata, char **filename_out)
2636 HRESULT hr;
2637 SIZE_T data_size;
2638 BYTE *data;
2639 char *filename_in;
2640 char *filename = NULL;
2642 /* template TextureFilename {
2643 * STRING filename;
2647 HeapFree(GetProcessHeap(), 0, *filename_out);
2648 *filename_out = NULL;
2650 hr = filedata->lpVtbl->Lock(filedata, &data_size, (const void**)&data);
2651 if (FAILED(hr)) return hr;
2653 /* FIXME: String must be retrieved directly instead of through a pointer once ID3DXFILE is fixed */
2654 if (data_size < sizeof(filename_in))
2656 WARN("truncated data (%lu bytes)\n", data_size);
2657 filedata->lpVtbl->Unlock(filedata);
2658 return E_FAIL;
2660 filename_in = *(char **)data;
2662 filename = HeapAlloc(GetProcessHeap(), 0, strlen(filename_in) + 1);
2663 if (!filename) {
2664 filedata->lpVtbl->Unlock(filedata);
2665 return E_OUTOFMEMORY;
2668 strcpy(filename, filename_in);
2669 *filename_out = filename;
2671 filedata->lpVtbl->Unlock(filedata);
2673 return D3D_OK;
2676 static HRESULT parse_material(ID3DXFileData *filedata, D3DXMATERIAL *material)
2678 HRESULT hr;
2679 SIZE_T data_size;
2680 const BYTE *data;
2681 GUID type;
2682 ID3DXFileData *child;
2683 SIZE_T i, nb_children;
2685 material->pTextureFilename = NULL;
2687 hr = filedata->lpVtbl->Lock(filedata, &data_size, (const void**)&data);
2688 if (FAILED(hr)) return hr;
2691 * template ColorRGBA {
2692 * FLOAT red;
2693 * FLOAT green;
2694 * FLOAT blue;
2695 * FLOAT alpha;
2697 * template ColorRGB {
2698 * FLOAT red;
2699 * FLOAT green;
2700 * FLOAT blue;
2702 * template Material {
2703 * ColorRGBA faceColor;
2704 * FLOAT power;
2705 * ColorRGB specularColor;
2706 * ColorRGB emissiveColor;
2707 * [ ... ]
2710 if (data_size != sizeof(FLOAT) * 11) {
2711 WARN("incorrect data size (%ld bytes)\n", data_size);
2712 filedata->lpVtbl->Unlock(filedata);
2713 return E_FAIL;
2716 memcpy(&material->MatD3D.Diffuse, data, sizeof(D3DCOLORVALUE));
2717 data += sizeof(D3DCOLORVALUE);
2718 material->MatD3D.Power = *(FLOAT*)data;
2719 data += sizeof(FLOAT);
2720 memcpy(&material->MatD3D.Specular, data, sizeof(FLOAT) * 3);
2721 material->MatD3D.Specular.a = 1.0f;
2722 data += 3 * sizeof(FLOAT);
2723 memcpy(&material->MatD3D.Emissive, data, sizeof(FLOAT) * 3);
2724 material->MatD3D.Emissive.a = 1.0f;
2725 material->MatD3D.Ambient.r = 0.0f;
2726 material->MatD3D.Ambient.g = 0.0f;
2727 material->MatD3D.Ambient.b = 0.0f;
2728 material->MatD3D.Ambient.a = 1.0f;
2730 filedata->lpVtbl->Unlock(filedata);
2732 hr = filedata->lpVtbl->GetChildren(filedata, &nb_children);
2733 if (FAILED(hr))
2734 return hr;
2736 for (i = 0; i < nb_children; i++)
2738 hr = filedata->lpVtbl->GetChild(filedata, i, &child);
2739 if (FAILED(hr))
2740 return hr;
2741 hr = child->lpVtbl->GetType(child, &type);
2742 if (FAILED(hr))
2743 goto err;
2745 if (IsEqualGUID(&type, &TID_D3DRMTextureFilename)) {
2746 hr = parse_texture_filename(child, &material->pTextureFilename);
2747 if (FAILED(hr))
2748 goto err;
2750 IUnknown_Release(child);
2752 return D3D_OK;
2754 err:
2755 IUnknown_Release(child);
2756 return hr;
2759 static void destroy_materials(struct mesh_data *mesh)
2761 DWORD i;
2762 for (i = 0; i < mesh->num_materials; i++)
2763 HeapFree(GetProcessHeap(), 0, mesh->materials[i].pTextureFilename);
2764 HeapFree(GetProcessHeap(), 0, mesh->materials);
2765 HeapFree(GetProcessHeap(), 0, mesh->material_indices);
2766 mesh->num_materials = 0;
2767 mesh->materials = NULL;
2768 mesh->material_indices = NULL;
2771 static HRESULT parse_material_list(ID3DXFileData *filedata, struct mesh_data *mesh)
2773 HRESULT hr;
2774 SIZE_T data_size;
2775 const DWORD *data, *in_ptr;
2776 GUID type;
2777 ID3DXFileData *child = NULL;
2778 DWORD num_materials;
2779 DWORD i;
2780 SIZE_T nb_children;
2782 destroy_materials(mesh);
2784 hr = filedata->lpVtbl->Lock(filedata, &data_size, (const void**)&data);
2785 if (FAILED(hr)) return hr;
2787 /* template MeshMaterialList {
2788 * DWORD nMaterials;
2789 * DWORD nFaceIndexes;
2790 * array DWORD faceIndexes[nFaceIndexes];
2791 * [ Material ]
2795 in_ptr = data;
2796 hr = E_FAIL;
2798 if (data_size < sizeof(DWORD)) {
2799 WARN("truncated data (%ld bytes)\n", data_size);
2800 goto end;
2802 num_materials = *in_ptr++;
2803 if (!num_materials) {
2804 hr = D3D_OK;
2805 goto end;
2808 if (data_size < 2 * sizeof(DWORD)) {
2809 WARN("truncated data (%ld bytes)\n", data_size);
2810 goto end;
2812 if (*in_ptr++ != mesh->num_poly_faces) {
2813 WARN("number of material face indices (%u) doesn't match number of faces (%u)\n",
2814 *(in_ptr - 1), mesh->num_poly_faces);
2815 goto end;
2817 if (data_size < 2 * sizeof(DWORD) + mesh->num_poly_faces * sizeof(DWORD)) {
2818 WARN("truncated data (%ld bytes)\n", data_size);
2819 goto end;
2821 for (i = 0; i < mesh->num_poly_faces; i++) {
2822 if (*in_ptr++ >= num_materials) {
2823 WARN("face %u: reference to undefined material %u (only %u materials)\n",
2824 i, *(in_ptr - 1), num_materials);
2825 goto end;
2829 mesh->materials = HeapAlloc(GetProcessHeap(), 0, num_materials * sizeof(*mesh->materials));
2830 mesh->material_indices = HeapAlloc(GetProcessHeap(), 0, mesh->num_poly_faces * sizeof(*mesh->material_indices));
2831 if (!mesh->materials || !mesh->material_indices) {
2832 hr = E_OUTOFMEMORY;
2833 goto end;
2835 memcpy(mesh->material_indices, data + 2, mesh->num_poly_faces * sizeof(DWORD));
2837 hr = filedata->lpVtbl->GetChildren(filedata, &nb_children);
2838 if (FAILED(hr))
2839 goto end;
2841 for (i = 0; i < nb_children; i++)
2843 hr = filedata->lpVtbl->GetChild(filedata, i, &child);
2844 if (FAILED(hr))
2845 goto end;
2846 hr = child->lpVtbl->GetType(child, &type);
2847 if (FAILED(hr))
2848 goto end;
2850 if (IsEqualGUID(&type, &TID_D3DRMMaterial)) {
2851 if (mesh->num_materials >= num_materials) {
2852 WARN("more materials defined than declared\n");
2853 hr = E_FAIL;
2854 goto end;
2856 hr = parse_material(child, &mesh->materials[mesh->num_materials++]);
2857 if (FAILED(hr))
2858 goto end;
2861 IUnknown_Release(child);
2862 child = NULL;
2864 if (num_materials != mesh->num_materials) {
2865 WARN("only %u of %u materials defined\n", num_materials, mesh->num_materials);
2866 hr = E_FAIL;
2869 end:
2870 if (child)
2871 IUnknown_Release(child);
2872 filedata->lpVtbl->Unlock(filedata);
2873 return hr;
2876 static HRESULT parse_texture_coords(ID3DXFileData *filedata, struct mesh_data *mesh)
2878 HRESULT hr;
2879 SIZE_T data_size;
2880 const BYTE *data;
2882 HeapFree(GetProcessHeap(), 0, mesh->tex_coords);
2883 mesh->tex_coords = NULL;
2885 hr = filedata->lpVtbl->Lock(filedata, &data_size, (const void**)&data);
2886 if (FAILED(hr)) return hr;
2888 /* template Coords2d {
2889 * FLOAT u;
2890 * FLOAT v;
2892 * template MeshTextureCoords {
2893 * DWORD nTextureCoords;
2894 * array Coords2d textureCoords[nTextureCoords];
2898 hr = E_FAIL;
2900 if (data_size < sizeof(DWORD)) {
2901 WARN("truncated data (%ld bytes)\n", data_size);
2902 goto end;
2904 if (*(DWORD*)data != mesh->num_vertices) {
2905 WARN("number of texture coordinates (%u) doesn't match number of vertices (%u)\n",
2906 *(DWORD*)data, mesh->num_vertices);
2907 goto end;
2909 data += sizeof(DWORD);
2910 if (data_size < sizeof(DWORD) + mesh->num_vertices * sizeof(*mesh->tex_coords)) {
2911 WARN("truncated data (%ld bytes)\n", data_size);
2912 goto end;
2915 mesh->tex_coords = HeapAlloc(GetProcessHeap(), 0, mesh->num_vertices * sizeof(*mesh->tex_coords));
2916 if (!mesh->tex_coords) {
2917 hr = E_OUTOFMEMORY;
2918 goto end;
2920 memcpy(mesh->tex_coords, data, mesh->num_vertices * sizeof(*mesh->tex_coords));
2922 mesh->fvf |= D3DFVF_TEX1;
2924 hr = D3D_OK;
2926 end:
2927 filedata->lpVtbl->Unlock(filedata);
2928 return hr;
2931 static HRESULT parse_vertex_colors(ID3DXFileData *filedata, struct mesh_data *mesh)
2933 HRESULT hr;
2934 SIZE_T data_size;
2935 const BYTE *data;
2936 DWORD num_colors;
2937 DWORD i;
2939 HeapFree(GetProcessHeap(), 0, mesh->vertex_colors);
2940 mesh->vertex_colors = NULL;
2942 hr = filedata->lpVtbl->Lock(filedata, &data_size, (const void**)&data);
2943 if (FAILED(hr)) return hr;
2945 /* template IndexedColor {
2946 * DWORD index;
2947 * ColorRGBA indexColor;
2949 * template MeshVertexColors {
2950 * DWORD nVertexColors;
2951 * array IndexedColor vertexColors[nVertexColors];
2955 hr = E_FAIL;
2957 if (data_size < sizeof(DWORD)) {
2958 WARN("truncated data (%ld bytes)\n", data_size);
2959 goto end;
2961 num_colors = *(DWORD*)data;
2962 data += sizeof(DWORD);
2963 if (data_size < sizeof(DWORD) + num_colors * (sizeof(DWORD) + sizeof(D3DCOLORVALUE))) {
2964 WARN("truncated data (%ld bytes)\n", data_size);
2965 goto end;
2968 mesh->vertex_colors = HeapAlloc(GetProcessHeap(), 0, mesh->num_vertices * sizeof(DWORD));
2969 if (!mesh->vertex_colors) {
2970 hr = E_OUTOFMEMORY;
2971 goto end;
2974 for (i = 0; i < mesh->num_vertices; i++)
2975 mesh->vertex_colors[i] = D3DCOLOR_ARGB(0, 0xff, 0xff, 0xff);
2976 for (i = 0; i < num_colors; i++)
2978 D3DCOLORVALUE color;
2979 DWORD index = *(DWORD*)data;
2980 data += sizeof(DWORD);
2981 if (index >= mesh->num_vertices) {
2982 WARN("vertex color %u references undefined vertex %u (only %u vertices)\n",
2983 i, index, mesh->num_vertices);
2984 goto end;
2986 memcpy(&color, data, sizeof(color));
2987 data += sizeof(color);
2988 color.r = min(1.0f, max(0.0f, color.r));
2989 color.g = min(1.0f, max(0.0f, color.g));
2990 color.b = min(1.0f, max(0.0f, color.b));
2991 color.a = min(1.0f, max(0.0f, color.a));
2992 mesh->vertex_colors[index] = D3DCOLOR_ARGB((BYTE)(color.a * 255.0f + 0.5f),
2993 (BYTE)(color.r * 255.0f + 0.5f),
2994 (BYTE)(color.g * 255.0f + 0.5f),
2995 (BYTE)(color.b * 255.0f + 0.5f));
2998 mesh->fvf |= D3DFVF_DIFFUSE;
3000 hr = D3D_OK;
3002 end:
3003 filedata->lpVtbl->Unlock(filedata);
3004 return hr;
3007 static HRESULT parse_normals(ID3DXFileData *filedata, struct mesh_data *mesh)
3009 HRESULT hr;
3010 SIZE_T data_size;
3011 const BYTE *data;
3012 DWORD *index_out_ptr;
3013 DWORD i;
3014 DWORD num_face_indices = mesh->num_poly_faces * 2 + mesh->num_tri_faces;
3016 HeapFree(GetProcessHeap(), 0, mesh->normals);
3017 mesh->num_normals = 0;
3018 mesh->normals = NULL;
3019 mesh->normal_indices = NULL;
3020 mesh->fvf |= D3DFVF_NORMAL;
3022 hr = filedata->lpVtbl->Lock(filedata, &data_size, (const void**)&data);
3023 if (FAILED(hr)) return hr;
3025 /* template Vector {
3026 * FLOAT x;
3027 * FLOAT y;
3028 * FLOAT z;
3030 * template MeshFace {
3031 * DWORD nFaceVertexIndices;
3032 * array DWORD faceVertexIndices[nFaceVertexIndices];
3034 * template MeshNormals {
3035 * DWORD nNormals;
3036 * array Vector normals[nNormals];
3037 * DWORD nFaceNormals;
3038 * array MeshFace faceNormals[nFaceNormals];
3042 hr = E_FAIL;
3044 if (data_size < sizeof(DWORD) * 2) {
3045 WARN("truncated data (%ld bytes)\n", data_size);
3046 goto end;
3048 mesh->num_normals = *(DWORD*)data;
3049 data += sizeof(DWORD);
3050 if (data_size < sizeof(DWORD) * 2 + mesh->num_normals * sizeof(D3DXVECTOR3) +
3051 num_face_indices * sizeof(DWORD)) {
3052 WARN("truncated data (%ld bytes)\n", data_size);
3053 goto end;
3056 mesh->normals = HeapAlloc(GetProcessHeap(), 0, mesh->num_normals * sizeof(D3DXVECTOR3));
3057 mesh->normal_indices = HeapAlloc(GetProcessHeap(), 0, num_face_indices * sizeof(DWORD));
3058 if (!mesh->normals || !mesh->normal_indices) {
3059 hr = E_OUTOFMEMORY;
3060 goto end;
3063 memcpy(mesh->normals, data, mesh->num_normals * sizeof(D3DXVECTOR3));
3064 data += mesh->num_normals * sizeof(D3DXVECTOR3);
3065 for (i = 0; i < mesh->num_normals; i++)
3066 D3DXVec3Normalize(&mesh->normals[i], &mesh->normals[i]);
3068 if (*(DWORD*)data != mesh->num_poly_faces) {
3069 WARN("number of face normals (%u) doesn't match number of faces (%u)\n",
3070 *(DWORD*)data, mesh->num_poly_faces);
3071 goto end;
3073 data += sizeof(DWORD);
3074 index_out_ptr = mesh->normal_indices;
3075 for (i = 0; i < mesh->num_poly_faces; i++)
3077 DWORD j;
3078 DWORD count = *(DWORD*)data;
3079 if (count != mesh->num_tri_per_face[i] + 2) {
3080 WARN("face %u: number of normals (%u) doesn't match number of vertices (%u)\n",
3081 i, count, mesh->num_tri_per_face[i] + 2);
3082 goto end;
3084 data += sizeof(DWORD);
3086 for (j = 0; j < count; j++) {
3087 DWORD normal_index = *(DWORD*)data;
3088 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(DWORD);
3098 hr = D3D_OK;
3100 end:
3101 filedata->lpVtbl->Unlock(filedata);
3102 return hr;
3105 static HRESULT parse_skin_mesh_info(ID3DXFileData *filedata, struct mesh_data *mesh_data, DWORD index)
3107 HRESULT hr;
3108 SIZE_T data_size;
3109 const BYTE *data;
3111 TRACE("(%p, %p, %u)\n", filedata, mesh_data, index);
3113 hr = filedata->lpVtbl->Lock(filedata, &data_size, (const void**)&data);
3114 if (FAILED(hr)) return hr;
3116 hr = E_FAIL;
3118 if (!mesh_data->skin_info) {
3119 if (data_size < sizeof(WORD) * 3) {
3120 WARN("truncated data (%ld bytes)\n", data_size);
3121 goto end;
3123 /* Skip nMaxSkinWeightsPerVertex and nMaxSkinWeightsPerFace */
3124 data += 2 * sizeof(WORD);
3125 mesh_data->nb_bones = *(WORD*)data;
3126 hr = D3DXCreateSkinInfoFVF(mesh_data->num_vertices, mesh_data->fvf, mesh_data->nb_bones, &mesh_data->skin_info);
3127 } else {
3128 const char *name;
3129 DWORD nb_influences;
3131 /* FIXME: String must be retrieved directly instead of through a pointer once ID3DXFILE is fixed */
3132 name = *(const char**)data;
3133 data += sizeof(char*);
3135 nb_influences = *(DWORD*)data;
3136 data += sizeof(DWORD);
3138 if (data_size < (sizeof(char*) + sizeof(DWORD) + nb_influences * (sizeof(DWORD) + sizeof(FLOAT)) + 16 * sizeof(FLOAT))) {
3139 WARN("truncated data (%ld bytes)\n", data_size);
3140 goto end;
3143 hr = mesh_data->skin_info->lpVtbl->SetBoneName(mesh_data->skin_info, index, name);
3144 if (SUCCEEDED(hr))
3145 hr = mesh_data->skin_info->lpVtbl->SetBoneInfluence(mesh_data->skin_info, index, nb_influences,
3146 (const DWORD*)data, (const FLOAT*)(data + nb_influences * sizeof(DWORD)));
3147 if (SUCCEEDED(hr))
3148 hr = mesh_data->skin_info->lpVtbl->SetBoneOffsetMatrix(mesh_data->skin_info, index,
3149 (const D3DMATRIX*)(data + nb_influences * (sizeof(DWORD) + sizeof(FLOAT))));
3152 end:
3153 filedata->lpVtbl->Unlock(filedata);
3154 return hr;
3157 /* for provide_flags parameters */
3158 #define PROVIDE_MATERIALS 0x1
3159 #define PROVIDE_SKININFO 0x2
3160 #define PROVIDE_ADJACENCY 0x4
3162 static HRESULT parse_mesh(ID3DXFileData *filedata, struct mesh_data *mesh_data, DWORD provide_flags)
3164 HRESULT hr;
3165 SIZE_T data_size;
3166 const BYTE *data, *in_ptr;
3167 DWORD *index_out_ptr;
3168 GUID type;
3169 ID3DXFileData *child = NULL;
3170 DWORD i;
3171 SIZE_T nb_children;
3172 DWORD nb_skin_weights_info = 0;
3175 * template Mesh {
3176 * DWORD nVertices;
3177 * array Vector vertices[nVertices];
3178 * DWORD nFaces;
3179 * array MeshFace faces[nFaces];
3180 * [ ... ]
3184 hr = filedata->lpVtbl->Lock(filedata, &data_size, (const void**)&data);
3185 if (FAILED(hr)) return hr;
3187 in_ptr = data;
3188 hr = E_FAIL;
3190 if (data_size < sizeof(DWORD) * 2) {
3191 WARN("truncated data (%ld bytes)\n", data_size);
3192 goto end;
3194 mesh_data->num_vertices = *(DWORD*)in_ptr;
3195 if (data_size < sizeof(DWORD) * 2 + mesh_data->num_vertices * sizeof(D3DXVECTOR3)) {
3196 WARN("truncated data (%ld bytes)\n", data_size);
3197 goto end;
3199 in_ptr += sizeof(DWORD) + mesh_data->num_vertices * sizeof(D3DXVECTOR3);
3201 mesh_data->num_poly_faces = *(DWORD*)in_ptr;
3202 in_ptr += sizeof(DWORD);
3204 mesh_data->num_tri_faces = 0;
3205 for (i = 0; i < mesh_data->num_poly_faces; i++)
3207 DWORD num_poly_vertices;
3208 DWORD j;
3210 if (data_size - (in_ptr - data) < sizeof(DWORD)) {
3211 WARN("truncated data (%ld bytes)\n", data_size);
3212 goto end;
3214 num_poly_vertices = *(DWORD*)in_ptr;
3215 in_ptr += sizeof(DWORD);
3216 if (data_size - (in_ptr - data) < num_poly_vertices * sizeof(DWORD)) {
3217 WARN("truncated data (%ld bytes)\n", data_size);
3218 goto end;
3220 if (num_poly_vertices < 3) {
3221 WARN("face %u has only %u vertices\n", i, num_poly_vertices);
3222 goto end;
3224 for (j = 0; j < num_poly_vertices; j++) {
3225 if (*(DWORD*)in_ptr >= mesh_data->num_vertices) {
3226 WARN("face %u, index %u: undefined vertex %u (only %u vertices)\n",
3227 i, j, *(DWORD*)in_ptr, mesh_data->num_vertices);
3228 goto end;
3230 in_ptr += sizeof(DWORD);
3232 mesh_data->num_tri_faces += num_poly_vertices - 2;
3235 mesh_data->fvf = D3DFVF_XYZ;
3237 mesh_data->vertices = HeapAlloc(GetProcessHeap(), 0,
3238 mesh_data->num_vertices * sizeof(*mesh_data->vertices));
3239 mesh_data->num_tri_per_face = HeapAlloc(GetProcessHeap(), 0,
3240 mesh_data->num_poly_faces * sizeof(*mesh_data->num_tri_per_face));
3241 mesh_data->indices = HeapAlloc(GetProcessHeap(), 0,
3242 (mesh_data->num_tri_faces + mesh_data->num_poly_faces * 2) * sizeof(*mesh_data->indices));
3243 if (!mesh_data->vertices || !mesh_data->num_tri_per_face || !mesh_data->indices) {
3244 hr = E_OUTOFMEMORY;
3245 goto end;
3248 in_ptr = data + sizeof(DWORD);
3249 memcpy(mesh_data->vertices, in_ptr, mesh_data->num_vertices * sizeof(D3DXVECTOR3));
3250 in_ptr += mesh_data->num_vertices * sizeof(D3DXVECTOR3) + sizeof(DWORD);
3252 index_out_ptr = mesh_data->indices;
3253 for (i = 0; i < mesh_data->num_poly_faces; i++)
3255 DWORD count;
3257 count = *(DWORD*)in_ptr;
3258 in_ptr += sizeof(DWORD);
3259 mesh_data->num_tri_per_face[i] = count - 2;
3261 while (count--) {
3262 *index_out_ptr++ = *(DWORD*)in_ptr;
3263 in_ptr += sizeof(DWORD);
3267 hr = filedata->lpVtbl->GetChildren(filedata, &nb_children);
3268 if (FAILED(hr))
3269 goto end;
3271 for (i = 0; i < nb_children; i++)
3273 hr = filedata->lpVtbl->GetChild(filedata, i, &child);
3274 if (FAILED(hr))
3275 goto end;
3276 hr = child->lpVtbl->GetType(child, &type);
3277 if (FAILED(hr))
3278 goto end;
3280 if (IsEqualGUID(&type, &TID_D3DRMMeshNormals)) {
3281 hr = parse_normals(child, mesh_data);
3282 } else if (IsEqualGUID(&type, &TID_D3DRMMeshVertexColors)) {
3283 hr = parse_vertex_colors(child, mesh_data);
3284 } else if (IsEqualGUID(&type, &TID_D3DRMMeshTextureCoords)) {
3285 hr = parse_texture_coords(child, mesh_data);
3286 } else if (IsEqualGUID(&type, &TID_D3DRMMeshMaterialList) &&
3287 (provide_flags & PROVIDE_MATERIALS))
3289 hr = parse_material_list(child, mesh_data);
3290 } else if (provide_flags & PROVIDE_SKININFO) {
3291 if (IsEqualGUID(&type, &DXFILEOBJ_XSkinMeshHeader)) {
3292 if (mesh_data->skin_info) {
3293 WARN("Skin mesh header already encountered\n");
3294 hr = E_FAIL;
3295 goto end;
3297 hr = parse_skin_mesh_info(child, mesh_data, 0);
3298 if (FAILED(hr))
3299 goto end;
3300 } else if (IsEqualGUID(&type, &DXFILEOBJ_SkinWeights)) {
3301 if (!mesh_data->skin_info) {
3302 WARN("Skin weights found but skin mesh header not encountered yet\n");
3303 hr = E_FAIL;
3304 goto end;
3306 hr = parse_skin_mesh_info(child, mesh_data, nb_skin_weights_info);
3307 if (FAILED(hr))
3308 goto end;
3309 nb_skin_weights_info++;
3312 if (FAILED(hr))
3313 goto end;
3315 IUnknown_Release(child);
3316 child = NULL;
3319 if (mesh_data->skin_info && (nb_skin_weights_info != mesh_data->nb_bones)) {
3320 WARN("Mismatch between nb skin weights info %u encountered and nb bones %u from skin mesh header\n",
3321 nb_skin_weights_info, mesh_data->nb_bones);
3322 hr = E_FAIL;
3323 goto end;
3326 if ((provide_flags & PROVIDE_SKININFO) && !mesh_data->skin_info)
3328 if (FAILED(hr = D3DXCreateSkinInfoFVF(mesh_data->num_vertices, mesh_data->fvf,
3329 mesh_data->nb_bones, &mesh_data->skin_info)))
3330 goto end;
3333 hr = D3D_OK;
3335 end:
3336 if (child)
3337 IUnknown_Release(child);
3338 filedata->lpVtbl->Unlock(filedata);
3339 return hr;
3342 static HRESULT generate_effects(ID3DXBuffer *materials, DWORD num_materials,
3343 ID3DXBuffer **effects)
3345 HRESULT hr;
3346 D3DXEFFECTINSTANCE *effect_ptr;
3347 BYTE *out_ptr;
3348 const D3DXMATERIAL *material_ptr = ID3DXBuffer_GetBufferPointer(materials);
3349 static const struct {
3350 const char *param_name;
3351 DWORD name_size;
3352 DWORD num_bytes;
3353 DWORD value_offset;
3354 } material_effects[] = {
3355 #define EFFECT_TABLE_ENTRY(str, field) \
3356 {str, sizeof(str), sizeof(material_ptr->MatD3D.field), offsetof(D3DXMATERIAL, MatD3D.field)}
3357 EFFECT_TABLE_ENTRY("Diffuse", Diffuse),
3358 EFFECT_TABLE_ENTRY("Power", Power),
3359 EFFECT_TABLE_ENTRY("Specular", Specular),
3360 EFFECT_TABLE_ENTRY("Emissive", Emissive),
3361 EFFECT_TABLE_ENTRY("Ambient", Ambient),
3362 #undef EFFECT_TABLE_ENTRY
3364 static const char texture_paramname[] = "Texture0@Name";
3365 DWORD buffer_size;
3366 DWORD i;
3368 /* effects buffer layout:
3370 * D3DXEFFECTINSTANCE effects[num_materials];
3371 * for (effect in effects)
3373 * D3DXEFFECTDEFAULT defaults[effect.NumDefaults];
3374 * for (default in defaults)
3376 * *default.pParamName;
3377 * *default.pValue;
3381 buffer_size = sizeof(D3DXEFFECTINSTANCE);
3382 buffer_size += sizeof(D3DXEFFECTDEFAULT) * ARRAY_SIZE(material_effects);
3383 for (i = 0; i < ARRAY_SIZE(material_effects); i++) {
3384 buffer_size += material_effects[i].name_size;
3385 buffer_size += material_effects[i].num_bytes;
3387 buffer_size *= num_materials;
3388 for (i = 0; i < num_materials; i++) {
3389 if (material_ptr[i].pTextureFilename) {
3390 buffer_size += sizeof(D3DXEFFECTDEFAULT);
3391 buffer_size += sizeof(texture_paramname);
3392 buffer_size += strlen(material_ptr[i].pTextureFilename) + 1;
3396 hr = D3DXCreateBuffer(buffer_size, effects);
3397 if (FAILED(hr)) return hr;
3398 effect_ptr = ID3DXBuffer_GetBufferPointer(*effects);
3399 out_ptr = (BYTE*)(effect_ptr + num_materials);
3401 for (i = 0; i < num_materials; i++)
3403 DWORD j;
3404 D3DXEFFECTDEFAULT *defaults = (D3DXEFFECTDEFAULT*)out_ptr;
3406 effect_ptr->pDefaults = defaults;
3407 effect_ptr->NumDefaults = material_ptr->pTextureFilename ? 6 : 5;
3408 out_ptr = (BYTE*)(effect_ptr->pDefaults + effect_ptr->NumDefaults);
3410 for (j = 0; j < ARRAY_SIZE(material_effects); j++)
3412 defaults->pParamName = (char *)out_ptr;
3413 strcpy(defaults->pParamName, material_effects[j].param_name);
3414 defaults->pValue = defaults->pParamName + material_effects[j].name_size;
3415 defaults->Type = D3DXEDT_FLOATS;
3416 defaults->NumBytes = material_effects[j].num_bytes;
3417 memcpy(defaults->pValue, (BYTE*)material_ptr + material_effects[j].value_offset, defaults->NumBytes);
3418 out_ptr = (BYTE*)defaults->pValue + defaults->NumBytes;
3419 defaults++;
3422 if (material_ptr->pTextureFilename)
3424 defaults->pParamName = (char *)out_ptr;
3425 strcpy(defaults->pParamName, texture_paramname);
3426 defaults->pValue = defaults->pParamName + sizeof(texture_paramname);
3427 defaults->Type = D3DXEDT_STRING;
3428 defaults->NumBytes = strlen(material_ptr->pTextureFilename) + 1;
3429 strcpy(defaults->pValue, material_ptr->pTextureFilename);
3430 out_ptr = (BYTE*)defaults->pValue + defaults->NumBytes;
3432 material_ptr++;
3433 effect_ptr++;
3435 assert(out_ptr - (BYTE*)ID3DXBuffer_GetBufferPointer(*effects) == buffer_size);
3437 return D3D_OK;
3440 HRESULT WINAPI D3DXLoadSkinMeshFromXof(struct ID3DXFileData *filedata, DWORD options,
3441 struct IDirect3DDevice9 *device, struct ID3DXBuffer **adjacency_out, struct ID3DXBuffer **materials_out,
3442 struct ID3DXBuffer **effects_out, DWORD *num_materials_out, struct ID3DXSkinInfo **skin_info_out,
3443 struct ID3DXMesh **mesh_out)
3445 HRESULT hr;
3446 DWORD *index_in_ptr;
3447 struct mesh_data mesh_data;
3448 DWORD total_vertices;
3449 ID3DXMesh *d3dxmesh = NULL;
3450 ID3DXBuffer *adjacency = NULL;
3451 ID3DXBuffer *materials = NULL;
3452 ID3DXBuffer *effects = NULL;
3453 struct vertex_duplication {
3454 DWORD normal_index;
3455 struct list entry;
3456 } *duplications = NULL;
3457 DWORD i;
3458 void *vertices = NULL;
3459 void *indices = NULL;
3460 BYTE *out_ptr;
3461 DWORD provide_flags = 0;
3463 TRACE("(%p, %x, %p, %p, %p, %p, %p, %p, %p)\n", filedata, options, device, adjacency_out, materials_out,
3464 effects_out, num_materials_out, skin_info_out, mesh_out);
3466 ZeroMemory(&mesh_data, sizeof(mesh_data));
3468 if (num_materials_out || materials_out || effects_out)
3469 provide_flags |= PROVIDE_MATERIALS;
3470 if (skin_info_out)
3471 provide_flags |= PROVIDE_SKININFO;
3473 hr = parse_mesh(filedata, &mesh_data, provide_flags);
3474 if (FAILED(hr)) goto cleanup;
3476 total_vertices = mesh_data.num_vertices;
3477 if (mesh_data.fvf & D3DFVF_NORMAL) {
3478 /* duplicate vertices with multiple normals */
3479 DWORD num_face_indices = mesh_data.num_poly_faces * 2 + mesh_data.num_tri_faces;
3480 duplications = HeapAlloc(GetProcessHeap(), 0, (mesh_data.num_vertices + num_face_indices) * sizeof(*duplications));
3481 if (!duplications) {
3482 hr = E_OUTOFMEMORY;
3483 goto cleanup;
3485 for (i = 0; i < total_vertices; i++)
3487 duplications[i].normal_index = -1;
3488 list_init(&duplications[i].entry);
3490 for (i = 0; i < num_face_indices; i++) {
3491 DWORD vertex_index = mesh_data.indices[i];
3492 DWORD normal_index = mesh_data.normal_indices[i];
3493 struct vertex_duplication *dup_ptr = &duplications[vertex_index];
3495 if (dup_ptr->normal_index == -1) {
3496 dup_ptr->normal_index = normal_index;
3497 } else {
3498 D3DXVECTOR3 *new_normal = &mesh_data.normals[normal_index];
3499 struct list *dup_list = &dup_ptr->entry;
3500 while (TRUE) {
3501 D3DXVECTOR3 *cur_normal = &mesh_data.normals[dup_ptr->normal_index];
3502 if (new_normal->x == cur_normal->x &&
3503 new_normal->y == cur_normal->y &&
3504 new_normal->z == cur_normal->z)
3506 mesh_data.indices[i] = dup_ptr - duplications;
3507 break;
3508 } else if (!list_next(dup_list, &dup_ptr->entry)) {
3509 dup_ptr = &duplications[total_vertices++];
3510 dup_ptr->normal_index = normal_index;
3511 list_add_tail(dup_list, &dup_ptr->entry);
3512 mesh_data.indices[i] = dup_ptr - duplications;
3513 break;
3514 } else {
3515 dup_ptr = LIST_ENTRY(list_next(dup_list, &dup_ptr->entry),
3516 struct vertex_duplication, entry);
3523 hr = D3DXCreateMeshFVF(mesh_data.num_tri_faces, total_vertices, options, mesh_data.fvf, device, &d3dxmesh);
3524 if (FAILED(hr)) goto cleanup;
3526 hr = d3dxmesh->lpVtbl->LockVertexBuffer(d3dxmesh, 0, &vertices);
3527 if (FAILED(hr)) goto cleanup;
3529 out_ptr = vertices;
3530 for (i = 0; i < mesh_data.num_vertices; i++) {
3531 *(D3DXVECTOR3*)out_ptr = mesh_data.vertices[i];
3532 out_ptr += sizeof(D3DXVECTOR3);
3533 if (mesh_data.fvf & D3DFVF_NORMAL) {
3534 if (duplications[i].normal_index == -1)
3535 ZeroMemory(out_ptr, sizeof(D3DXVECTOR3));
3536 else
3537 *(D3DXVECTOR3*)out_ptr = mesh_data.normals[duplications[i].normal_index];
3538 out_ptr += sizeof(D3DXVECTOR3);
3540 if (mesh_data.fvf & D3DFVF_DIFFUSE) {
3541 *(DWORD*)out_ptr = mesh_data.vertex_colors[i];
3542 out_ptr += sizeof(DWORD);
3544 if (mesh_data.fvf & D3DFVF_TEX1) {
3545 *(D3DXVECTOR2*)out_ptr = mesh_data.tex_coords[i];
3546 out_ptr += sizeof(D3DXVECTOR2);
3549 if (mesh_data.fvf & D3DFVF_NORMAL) {
3550 DWORD vertex_size = D3DXGetFVFVertexSize(mesh_data.fvf);
3551 out_ptr = vertices;
3552 for (i = 0; i < mesh_data.num_vertices; i++) {
3553 struct vertex_duplication *dup_ptr;
3554 LIST_FOR_EACH_ENTRY(dup_ptr, &duplications[i].entry, struct vertex_duplication, entry)
3556 int j = dup_ptr - duplications;
3557 BYTE *dest_vertex = (BYTE*)vertices + j * vertex_size;
3559 memcpy(dest_vertex, out_ptr, vertex_size);
3560 dest_vertex += sizeof(D3DXVECTOR3);
3561 *(D3DXVECTOR3*)dest_vertex = mesh_data.normals[dup_ptr->normal_index];
3563 out_ptr += vertex_size;
3566 d3dxmesh->lpVtbl->UnlockVertexBuffer(d3dxmesh);
3568 hr = d3dxmesh->lpVtbl->LockIndexBuffer(d3dxmesh, 0, &indices);
3569 if (FAILED(hr)) goto cleanup;
3571 index_in_ptr = mesh_data.indices;
3572 #define FILL_INDEX_BUFFER(indices_var) \
3573 for (i = 0; i < mesh_data.num_poly_faces; i++) \
3575 DWORD count = mesh_data.num_tri_per_face[i]; \
3576 WORD first_index = *index_in_ptr++; \
3577 while (count--) { \
3578 *indices_var++ = first_index; \
3579 *indices_var++ = *index_in_ptr; \
3580 index_in_ptr++; \
3581 *indices_var++ = *index_in_ptr; \
3583 index_in_ptr++; \
3585 if (options & D3DXMESH_32BIT) {
3586 DWORD *dword_indices = indices;
3587 FILL_INDEX_BUFFER(dword_indices)
3588 } else {
3589 WORD *word_indices = indices;
3590 FILL_INDEX_BUFFER(word_indices)
3592 #undef FILL_INDEX_BUFFER
3593 d3dxmesh->lpVtbl->UnlockIndexBuffer(d3dxmesh);
3595 if (mesh_data.material_indices) {
3596 DWORD *attrib_buffer = NULL;
3597 hr = d3dxmesh->lpVtbl->LockAttributeBuffer(d3dxmesh, 0, &attrib_buffer);
3598 if (FAILED(hr)) goto cleanup;
3599 for (i = 0; i < mesh_data.num_poly_faces; i++)
3601 DWORD count = mesh_data.num_tri_per_face[i];
3602 while (count--)
3603 *attrib_buffer++ = mesh_data.material_indices[i];
3605 d3dxmesh->lpVtbl->UnlockAttributeBuffer(d3dxmesh);
3607 hr = d3dxmesh->lpVtbl->OptimizeInplace(d3dxmesh,
3608 D3DXMESHOPT_ATTRSORT | D3DXMESHOPT_IGNOREVERTS | D3DXMESHOPT_DONOTSPLIT,
3609 NULL, NULL, NULL, NULL);
3610 if (FAILED(hr)) goto cleanup;
3613 if (mesh_data.num_materials && (materials_out || effects_out)) {
3614 DWORD buffer_size = mesh_data.num_materials * sizeof(D3DXMATERIAL);
3615 char *strings_out_ptr;
3616 D3DXMATERIAL *materials_ptr;
3618 for (i = 0; i < mesh_data.num_materials; i++) {
3619 if (mesh_data.materials[i].pTextureFilename)
3620 buffer_size += strlen(mesh_data.materials[i].pTextureFilename) + 1;
3623 hr = D3DXCreateBuffer(buffer_size, &materials);
3624 if (FAILED(hr)) goto cleanup;
3626 materials_ptr = ID3DXBuffer_GetBufferPointer(materials);
3627 memcpy(materials_ptr, mesh_data.materials, mesh_data.num_materials * sizeof(D3DXMATERIAL));
3628 strings_out_ptr = (char*)(materials_ptr + mesh_data.num_materials);
3629 for (i = 0; i < mesh_data.num_materials; i++) {
3630 if (materials_ptr[i].pTextureFilename) {
3631 strcpy(strings_out_ptr, mesh_data.materials[i].pTextureFilename);
3632 materials_ptr[i].pTextureFilename = strings_out_ptr;
3633 strings_out_ptr += strlen(mesh_data.materials[i].pTextureFilename) + 1;
3638 if (mesh_data.num_materials && effects_out) {
3639 hr = generate_effects(materials, mesh_data.num_materials, &effects);
3640 if (FAILED(hr)) goto cleanup;
3642 if (!materials_out) {
3643 ID3DXBuffer_Release(materials);
3644 materials = NULL;
3648 if (adjacency_out) {
3649 hr = D3DXCreateBuffer(mesh_data.num_tri_faces * 3 * sizeof(DWORD), &adjacency);
3650 if (FAILED(hr)) goto cleanup;
3651 hr = d3dxmesh->lpVtbl->GenerateAdjacency(d3dxmesh, 0.0f, ID3DXBuffer_GetBufferPointer(adjacency));
3652 if (FAILED(hr)) goto cleanup;
3655 *mesh_out = d3dxmesh;
3656 if (adjacency_out) *adjacency_out = adjacency;
3657 if (num_materials_out) *num_materials_out = mesh_data.num_materials;
3658 if (materials_out) *materials_out = materials;
3659 if (effects_out) *effects_out = effects;
3660 if (skin_info_out) *skin_info_out = mesh_data.skin_info;
3662 hr = D3D_OK;
3663 cleanup:
3664 if (FAILED(hr)) {
3665 if (d3dxmesh) IUnknown_Release(d3dxmesh);
3666 if (adjacency) ID3DXBuffer_Release(adjacency);
3667 if (materials) ID3DXBuffer_Release(materials);
3668 if (effects) ID3DXBuffer_Release(effects);
3669 if (mesh_data.skin_info) mesh_data.skin_info->lpVtbl->Release(mesh_data.skin_info);
3670 if (skin_info_out) *skin_info_out = NULL;
3672 HeapFree(GetProcessHeap(), 0, mesh_data.vertices);
3673 HeapFree(GetProcessHeap(), 0, mesh_data.num_tri_per_face);
3674 HeapFree(GetProcessHeap(), 0, mesh_data.indices);
3675 HeapFree(GetProcessHeap(), 0, mesh_data.normals);
3676 HeapFree(GetProcessHeap(), 0, mesh_data.normal_indices);
3677 destroy_materials(&mesh_data);
3678 HeapFree(GetProcessHeap(), 0, mesh_data.tex_coords);
3679 HeapFree(GetProcessHeap(), 0, mesh_data.vertex_colors);
3680 HeapFree(GetProcessHeap(), 0, duplications);
3681 return hr;
3684 HRESULT WINAPI D3DXLoadMeshHierarchyFromXA(const char *filename, DWORD options, struct IDirect3DDevice9 *device,
3685 struct ID3DXAllocateHierarchy *alloc_hier, struct ID3DXLoadUserData *load_user_data,
3686 D3DXFRAME **frame_hierarchy, struct ID3DXAnimationController **anim_controller)
3688 WCHAR *filenameW;
3689 HRESULT hr;
3690 int len;
3692 TRACE("filename %s, options %#x, device %p, alloc_hier %p, "
3693 "load_user_data %p, frame_hierarchy %p, anim_controller %p.\n",
3694 debugstr_a(filename), options, device, alloc_hier,
3695 load_user_data, frame_hierarchy, anim_controller);
3697 if (!filename)
3698 return D3DERR_INVALIDCALL;
3700 len = MultiByteToWideChar(CP_ACP, 0, filename, -1, NULL, 0);
3701 filenameW = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
3702 if (!filenameW) return E_OUTOFMEMORY;
3703 MultiByteToWideChar(CP_ACP, 0, filename, -1, filenameW, len);
3705 hr = D3DXLoadMeshHierarchyFromXW(filenameW, options, device,
3706 alloc_hier, load_user_data, frame_hierarchy, anim_controller);
3707 HeapFree(GetProcessHeap(), 0, filenameW);
3709 return hr;
3712 HRESULT WINAPI D3DXLoadMeshHierarchyFromXW(const WCHAR *filename, DWORD options, struct IDirect3DDevice9 *device,
3713 struct ID3DXAllocateHierarchy *alloc_hier, struct ID3DXLoadUserData *load_user_data,
3714 D3DXFRAME **frame_hierarchy, struct ID3DXAnimationController **anim_controller)
3716 void *buffer;
3717 HRESULT hr;
3718 DWORD size;
3720 TRACE("filename %s, options %#x, device %p, alloc_hier %p, "
3721 "load_user_data %p, frame_hierarchy %p, anim_controller %p.\n",
3722 debugstr_w(filename), options, device, alloc_hier,
3723 load_user_data, frame_hierarchy, anim_controller);
3725 if (!filename)
3726 return D3DERR_INVALIDCALL;
3728 hr = map_view_of_file(filename, &buffer, &size);
3729 if (FAILED(hr))
3730 return D3DXERR_INVALIDDATA;
3732 hr = D3DXLoadMeshHierarchyFromXInMemory(buffer, size, options, device,
3733 alloc_hier, load_user_data, frame_hierarchy, anim_controller);
3735 UnmapViewOfFile(buffer);
3737 return hr;
3740 static HRESULT filedata_get_name(ID3DXFileData *filedata, char **name)
3742 HRESULT hr;
3743 SIZE_T name_len;
3745 hr = filedata->lpVtbl->GetName(filedata, NULL, &name_len);
3746 if (FAILED(hr)) return hr;
3748 if (!name_len)
3749 name_len++;
3750 *name = HeapAlloc(GetProcessHeap(), 0, name_len);
3751 if (!*name) return E_OUTOFMEMORY;
3753 hr = filedata->lpVtbl->GetName(filedata, *name, &name_len);
3754 if (FAILED(hr))
3755 HeapFree(GetProcessHeap(), 0, *name);
3756 else if (!name_len)
3757 (*name)[0] = 0;
3759 return hr;
3762 static HRESULT load_mesh_container(struct ID3DXFileData *filedata, DWORD options, struct IDirect3DDevice9 *device,
3763 struct ID3DXAllocateHierarchy *alloc_hier, D3DXMESHCONTAINER **mesh_container)
3765 HRESULT hr;
3766 ID3DXBuffer *adjacency = NULL;
3767 ID3DXBuffer *materials = NULL;
3768 ID3DXBuffer *effects = NULL;
3769 ID3DXSkinInfo *skin_info = NULL;
3770 D3DXMESHDATA mesh_data;
3771 DWORD num_materials = 0;
3772 char *name = NULL;
3774 mesh_data.Type = D3DXMESHTYPE_MESH;
3775 mesh_data.u.pMesh = NULL;
3777 hr = D3DXLoadSkinMeshFromXof(filedata, options, device,
3778 &adjacency, &materials, &effects, &num_materials,
3779 &skin_info, &mesh_data.u.pMesh);
3780 if (FAILED(hr)) return hr;
3782 hr = filedata_get_name(filedata, &name);
3783 if (FAILED(hr)) goto cleanup;
3785 hr = alloc_hier->lpVtbl->CreateMeshContainer(alloc_hier, name, &mesh_data,
3786 materials ? ID3DXBuffer_GetBufferPointer(materials) : NULL,
3787 effects ? ID3DXBuffer_GetBufferPointer(effects) : NULL,
3788 num_materials,
3789 adjacency ? ID3DXBuffer_GetBufferPointer(adjacency) : NULL,
3790 skin_info, mesh_container);
3792 cleanup:
3793 if (materials) ID3DXBuffer_Release(materials);
3794 if (effects) ID3DXBuffer_Release(effects);
3795 if (adjacency) ID3DXBuffer_Release(adjacency);
3796 if (skin_info) IUnknown_Release(skin_info);
3797 if (mesh_data.u.pMesh) IUnknown_Release(mesh_data.u.pMesh);
3798 HeapFree(GetProcessHeap(), 0, name);
3799 return hr;
3802 static HRESULT parse_transform_matrix(ID3DXFileData *filedata, D3DXMATRIX *transform)
3804 HRESULT hr;
3805 SIZE_T data_size;
3806 const BYTE *data;
3808 /* template Matrix4x4 {
3809 * array FLOAT matrix[16];
3811 * template FrameTransformMatrix {
3812 * Matrix4x4 frameMatrix;
3816 hr = filedata->lpVtbl->Lock(filedata, &data_size, (const void**)&data);
3817 if (FAILED(hr)) return hr;
3819 if (data_size != sizeof(D3DXMATRIX)) {
3820 WARN("incorrect data size (%ld bytes)\n", data_size);
3821 filedata->lpVtbl->Unlock(filedata);
3822 return E_FAIL;
3825 memcpy(transform, data, sizeof(D3DXMATRIX));
3827 filedata->lpVtbl->Unlock(filedata);
3828 return D3D_OK;
3831 static HRESULT load_frame(struct ID3DXFileData *filedata, DWORD options, struct IDirect3DDevice9 *device,
3832 struct ID3DXAllocateHierarchy *alloc_hier, D3DXFRAME **frame_out)
3834 HRESULT hr;
3835 GUID type;
3836 ID3DXFileData *child;
3837 char *name = NULL;
3838 D3DXFRAME *frame = NULL;
3839 D3DXMESHCONTAINER **next_container;
3840 D3DXFRAME **next_child;
3841 SIZE_T i, nb_children;
3843 hr = filedata_get_name(filedata, &name);
3844 if (FAILED(hr)) return hr;
3846 hr = alloc_hier->lpVtbl->CreateFrame(alloc_hier, name, frame_out);
3847 HeapFree(GetProcessHeap(), 0, name);
3848 if (FAILED(hr)) return E_FAIL;
3850 frame = *frame_out;
3851 D3DXMatrixIdentity(&frame->TransformationMatrix);
3852 next_child = &frame->pFrameFirstChild;
3853 next_container = &frame->pMeshContainer;
3855 hr = filedata->lpVtbl->GetChildren(filedata, &nb_children);
3856 if (FAILED(hr))
3857 return hr;
3859 for (i = 0; i < nb_children; i++)
3861 hr = filedata->lpVtbl->GetChild(filedata, i, &child);
3862 if (FAILED(hr))
3863 return hr;
3864 hr = child->lpVtbl->GetType(child, &type);
3865 if (FAILED(hr))
3866 goto err;
3868 if (IsEqualGUID(&type, &TID_D3DRMMesh)) {
3869 hr = load_mesh_container(child, options, device, alloc_hier, next_container);
3870 if (SUCCEEDED(hr))
3871 next_container = &(*next_container)->pNextMeshContainer;
3872 } else if (IsEqualGUID(&type, &TID_D3DRMFrameTransformMatrix)) {
3873 hr = parse_transform_matrix(child, &frame->TransformationMatrix);
3874 } else if (IsEqualGUID(&type, &TID_D3DRMFrame)) {
3875 hr = load_frame(child, options, device, alloc_hier, next_child);
3876 if (SUCCEEDED(hr))
3877 next_child = &(*next_child)->pFrameSibling;
3879 if (FAILED(hr))
3880 goto err;
3882 IUnknown_Release(child);
3884 return D3D_OK;
3886 err:
3887 IUnknown_Release(child);
3888 return hr;
3891 HRESULT WINAPI D3DXLoadMeshHierarchyFromXInMemory(const void *memory, DWORD memory_size, DWORD options,
3892 struct IDirect3DDevice9 *device, struct ID3DXAllocateHierarchy *alloc_hier,
3893 struct ID3DXLoadUserData *load_user_data, D3DXFRAME **frame_hierarchy,
3894 struct ID3DXAnimationController **anim_controller)
3896 HRESULT hr;
3897 ID3DXFile *d3dxfile = NULL;
3898 ID3DXFileEnumObject *enumobj = NULL;
3899 ID3DXFileData *filedata = NULL;
3900 D3DXF_FILELOADMEMORY source;
3901 D3DXFRAME *first_frame = NULL;
3902 D3DXFRAME **next_frame = &first_frame;
3903 SIZE_T i, nb_children;
3904 GUID guid;
3906 TRACE("(%p, %u, %x, %p, %p, %p, %p, %p)\n", memory, memory_size, options,
3907 device, alloc_hier, load_user_data, frame_hierarchy, anim_controller);
3909 if (!memory || !memory_size || !device || !frame_hierarchy || !alloc_hier)
3910 return D3DERR_INVALIDCALL;
3911 if (load_user_data)
3913 FIXME("Loading user data not implemented.\n");
3914 return E_NOTIMPL;
3917 hr = D3DXFileCreate(&d3dxfile);
3918 if (FAILED(hr)) goto cleanup;
3920 hr = d3dxfile->lpVtbl->RegisterTemplates(d3dxfile, D3DRM_XTEMPLATES, D3DRM_XTEMPLATE_BYTES);
3921 if (FAILED(hr)) goto cleanup;
3923 source.lpMemory = (void*)memory;
3924 source.dSize = memory_size;
3925 hr = d3dxfile->lpVtbl->CreateEnumObject(d3dxfile, &source, D3DXF_FILELOAD_FROMMEMORY, &enumobj);
3926 if (FAILED(hr)) goto cleanup;
3928 hr = enumobj->lpVtbl->GetChildren(enumobj, &nb_children);
3929 if (FAILED(hr))
3930 goto cleanup;
3932 for (i = 0; i < nb_children; i++)
3934 hr = enumobj->lpVtbl->GetChild(enumobj, i, &filedata);
3935 if (FAILED(hr))
3936 goto cleanup;
3938 hr = filedata->lpVtbl->GetType(filedata, &guid);
3939 if (SUCCEEDED(hr)) {
3940 if (IsEqualGUID(&guid, &TID_D3DRMMesh)) {
3941 hr = alloc_hier->lpVtbl->CreateFrame(alloc_hier, NULL, next_frame);
3942 if (FAILED(hr)) {
3943 hr = E_FAIL;
3944 goto cleanup;
3947 D3DXMatrixIdentity(&(*next_frame)->TransformationMatrix);
3949 hr = load_mesh_container(filedata, options, device, alloc_hier, &(*next_frame)->pMeshContainer);
3950 if (FAILED(hr)) goto cleanup;
3951 } else if (IsEqualGUID(&guid, &TID_D3DRMFrame)) {
3952 hr = load_frame(filedata, options, device, alloc_hier, next_frame);
3953 if (FAILED(hr)) goto cleanup;
3955 while (*next_frame)
3956 next_frame = &(*next_frame)->pFrameSibling;
3959 filedata->lpVtbl->Release(filedata);
3960 filedata = NULL;
3961 if (FAILED(hr))
3962 goto cleanup;
3965 if (!first_frame) {
3966 hr = E_FAIL;
3967 } else if (first_frame->pFrameSibling) {
3968 D3DXFRAME *root_frame = NULL;
3969 hr = alloc_hier->lpVtbl->CreateFrame(alloc_hier, NULL, &root_frame);
3970 if (FAILED(hr)) {
3971 hr = E_FAIL;
3972 goto cleanup;
3974 D3DXMatrixIdentity(&root_frame->TransformationMatrix);
3975 root_frame->pFrameFirstChild = first_frame;
3976 *frame_hierarchy = root_frame;
3977 hr = D3D_OK;
3978 } else {
3979 *frame_hierarchy = first_frame;
3980 hr = D3D_OK;
3983 if (anim_controller)
3985 *anim_controller = NULL;
3986 FIXME("Animation controller creation not implemented.\n");
3989 cleanup:
3990 if (FAILED(hr) && first_frame) D3DXFrameDestroy(first_frame, alloc_hier);
3991 if (filedata) filedata->lpVtbl->Release(filedata);
3992 if (enumobj) enumobj->lpVtbl->Release(enumobj);
3993 if (d3dxfile) d3dxfile->lpVtbl->Release(d3dxfile);
3994 return hr;
3997 HRESULT WINAPI D3DXCleanMesh(D3DXCLEANTYPE clean_type, ID3DXMesh *mesh_in, const DWORD *adjacency_in,
3998 ID3DXMesh **mesh_out, DWORD *adjacency_out, ID3DXBuffer **errors_and_warnings)
4000 FIXME("(%u, %p, %p, %p, %p, %p)\n", clean_type, mesh_in, adjacency_in, mesh_out, adjacency_out, errors_and_warnings);
4002 return E_NOTIMPL;
4005 HRESULT WINAPI D3DXFrameDestroy(D3DXFRAME *frame, ID3DXAllocateHierarchy *alloc_hier)
4007 HRESULT hr;
4008 BOOL last = FALSE;
4010 TRACE("(%p, %p)\n", frame, alloc_hier);
4012 if (!frame || !alloc_hier)
4013 return D3DERR_INVALIDCALL;
4015 while (!last) {
4016 D3DXMESHCONTAINER *container;
4017 D3DXFRAME *current_frame;
4019 if (frame->pFrameSibling) {
4020 current_frame = frame->pFrameSibling;
4021 frame->pFrameSibling = current_frame->pFrameSibling;
4022 current_frame->pFrameSibling = NULL;
4023 } else {
4024 current_frame = frame;
4025 last = TRUE;
4028 if (current_frame->pFrameFirstChild) {
4029 hr = D3DXFrameDestroy(current_frame->pFrameFirstChild, alloc_hier);
4030 if (FAILED(hr)) return hr;
4031 current_frame->pFrameFirstChild = NULL;
4034 container = current_frame->pMeshContainer;
4035 while (container) {
4036 D3DXMESHCONTAINER *next_container = container->pNextMeshContainer;
4037 hr = alloc_hier->lpVtbl->DestroyMeshContainer(alloc_hier, container);
4038 if (FAILED(hr)) return hr;
4039 container = next_container;
4041 hr = alloc_hier->lpVtbl->DestroyFrame(alloc_hier, current_frame);
4042 if (FAILED(hr)) return hr;
4044 return D3D_OK;
4047 HRESULT WINAPI D3DXLoadMeshFromXA(const char *filename, DWORD options, struct IDirect3DDevice9 *device,
4048 struct ID3DXBuffer **adjacency, struct ID3DXBuffer **materials, struct ID3DXBuffer **effect_instances,
4049 DWORD *num_materials, struct ID3DXMesh **mesh)
4051 WCHAR *filenameW;
4052 HRESULT hr;
4053 int len;
4055 TRACE("filename %s, options %#x, device %p, adjacency %p, materials %p, "
4056 "effect_instances %p, num_materials %p, mesh %p.\n",
4057 debugstr_a(filename), options, device, adjacency, materials,
4058 effect_instances, num_materials, mesh);
4060 if (!filename)
4061 return D3DERR_INVALIDCALL;
4063 len = MultiByteToWideChar(CP_ACP, 0, filename, -1, NULL, 0);
4064 filenameW = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
4065 if (!filenameW) return E_OUTOFMEMORY;
4066 MultiByteToWideChar(CP_ACP, 0, filename, -1, filenameW, len);
4068 hr = D3DXLoadMeshFromXW(filenameW, options, device, adjacency, materials,
4069 effect_instances, num_materials, mesh);
4070 HeapFree(GetProcessHeap(), 0, filenameW);
4072 return hr;
4075 HRESULT WINAPI D3DXLoadMeshFromXW(const WCHAR *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 void *buffer;
4080 HRESULT hr;
4081 DWORD size;
4083 TRACE("filename %s, options %#x, device %p, adjacency %p, materials %p, "
4084 "effect_instances %p, num_materials %p, mesh %p.\n",
4085 debugstr_w(filename), options, device, adjacency, materials,
4086 effect_instances, num_materials, mesh);
4088 if (!filename)
4089 return D3DERR_INVALIDCALL;
4091 hr = map_view_of_file(filename, &buffer, &size);
4092 if (FAILED(hr))
4093 return D3DXERR_INVALIDDATA;
4095 hr = D3DXLoadMeshFromXInMemory(buffer, size, options, device, adjacency,
4096 materials, effect_instances, num_materials, mesh);
4098 UnmapViewOfFile(buffer);
4100 return hr;
4103 HRESULT WINAPI D3DXLoadMeshFromXResource(HMODULE module, const char *name, const char *type, DWORD options,
4104 struct IDirect3DDevice9 *device, struct ID3DXBuffer **adjacency, struct ID3DXBuffer **materials,
4105 struct ID3DXBuffer **effect_instances, DWORD *num_materials, struct ID3DXMesh **mesh)
4107 HRESULT hr;
4108 HRSRC resinfo;
4109 void *buffer;
4110 DWORD size;
4112 TRACE("module %p, name %s, type %s, options %#x, device %p, adjacency %p, "
4113 "materials %p, effect_instances %p, num_materials %p, mesh %p.\n",
4114 module, debugstr_a(name), debugstr_a(type), options, device, adjacency,
4115 materials, effect_instances, num_materials, mesh);
4117 resinfo = FindResourceA(module, name, type);
4118 if (!resinfo) return D3DXERR_INVALIDDATA;
4120 hr = load_resource_into_memory(module, resinfo, &buffer, &size);
4121 if (FAILED(hr)) return D3DXERR_INVALIDDATA;
4123 return D3DXLoadMeshFromXInMemory(buffer, size, options, device, adjacency,
4124 materials, effect_instances, num_materials, mesh);
4127 struct mesh_container
4129 struct list entry;
4130 ID3DXMesh *mesh;
4131 ID3DXBuffer *adjacency;
4132 ID3DXBuffer *materials;
4133 ID3DXBuffer *effects;
4134 DWORD num_materials;
4135 D3DXMATRIX transform;
4138 static HRESULT parse_frame(struct ID3DXFileData *filedata, DWORD options, struct IDirect3DDevice9 *device,
4139 const D3DXMATRIX *parent_transform, struct list *container_list, DWORD provide_flags)
4141 HRESULT hr;
4142 D3DXMATRIX transform = *parent_transform;
4143 ID3DXFileData *child;
4144 GUID type;
4145 SIZE_T i, nb_children;
4147 hr = filedata->lpVtbl->GetChildren(filedata, &nb_children);
4148 if (FAILED(hr))
4149 return hr;
4151 for (i = 0; i < nb_children; i++)
4153 hr = filedata->lpVtbl->GetChild(filedata, i, &child);
4154 if (FAILED(hr))
4155 return hr;
4156 hr = child->lpVtbl->GetType(child, &type);
4157 if (FAILED(hr))
4158 goto err;
4160 if (IsEqualGUID(&type, &TID_D3DRMMesh)) {
4161 struct mesh_container *container = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(*container));
4162 if (!container)
4164 hr = E_OUTOFMEMORY;
4165 goto err;
4167 list_add_tail(container_list, &container->entry);
4168 container->transform = transform;
4170 hr = D3DXLoadSkinMeshFromXof(child, options, device,
4171 (provide_flags & PROVIDE_ADJACENCY) ? &container->adjacency : NULL,
4172 (provide_flags & PROVIDE_MATERIALS) ? &container->materials : NULL,
4173 NULL, &container->num_materials, NULL, &container->mesh);
4174 } else if (IsEqualGUID(&type, &TID_D3DRMFrameTransformMatrix)) {
4175 D3DXMATRIX new_transform;
4176 hr = parse_transform_matrix(child, &new_transform);
4177 D3DXMatrixMultiply(&transform, &transform, &new_transform);
4178 } else if (IsEqualGUID(&type, &TID_D3DRMFrame)) {
4179 hr = parse_frame(child, options, device, &transform, container_list, provide_flags);
4181 if (FAILED(hr))
4182 goto err;
4184 IUnknown_Release(child);
4186 return D3D_OK;
4188 err:
4189 IUnknown_Release(child);
4190 return hr;
4193 HRESULT WINAPI D3DXLoadMeshFromXInMemory(const void *memory, DWORD memory_size, DWORD options,
4194 struct IDirect3DDevice9 *device, struct ID3DXBuffer **adjacency_out, struct ID3DXBuffer **materials_out,
4195 struct ID3DXBuffer **effects_out, DWORD *num_materials_out, struct ID3DXMesh **mesh_out)
4197 HRESULT hr;
4198 ID3DXFile *d3dxfile = NULL;
4199 ID3DXFileEnumObject *enumobj = NULL;
4200 ID3DXFileData *filedata = NULL;
4201 D3DXF_FILELOADMEMORY source;
4202 ID3DXBuffer *materials = NULL;
4203 ID3DXBuffer *effects = NULL;
4204 ID3DXBuffer *adjacency = NULL;
4205 struct list container_list = LIST_INIT(container_list);
4206 struct mesh_container *container_ptr, *next_container_ptr;
4207 DWORD num_materials;
4208 DWORD num_faces, num_vertices;
4209 D3DXMATRIX identity;
4210 DWORD provide_flags = 0;
4211 DWORD fvf;
4212 ID3DXMesh *concat_mesh = NULL;
4213 D3DVERTEXELEMENT9 concat_decl[MAX_FVF_DECL_SIZE];
4214 BYTE *concat_vertices = NULL;
4215 void *concat_indices = NULL;
4216 DWORD index_offset;
4217 DWORD concat_vertex_size;
4218 SIZE_T i, nb_children;
4219 GUID guid;
4221 TRACE("(%p, %u, %x, %p, %p, %p, %p, %p, %p)\n", memory, memory_size, options,
4222 device, adjacency_out, materials_out, effects_out, num_materials_out, mesh_out);
4224 if (!memory || !memory_size || !device || !mesh_out)
4225 return D3DERR_INVALIDCALL;
4227 hr = D3DXFileCreate(&d3dxfile);
4228 if (FAILED(hr)) goto cleanup;
4230 hr = d3dxfile->lpVtbl->RegisterTemplates(d3dxfile, D3DRM_XTEMPLATES, D3DRM_XTEMPLATE_BYTES);
4231 if (FAILED(hr)) goto cleanup;
4233 source.lpMemory = (void*)memory;
4234 source.dSize = memory_size;
4235 hr = d3dxfile->lpVtbl->CreateEnumObject(d3dxfile, &source, D3DXF_FILELOAD_FROMMEMORY, &enumobj);
4236 if (FAILED(hr)) goto cleanup;
4238 D3DXMatrixIdentity(&identity);
4239 if (adjacency_out) provide_flags |= PROVIDE_ADJACENCY;
4240 if (materials_out || effects_out) provide_flags |= PROVIDE_MATERIALS;
4242 hr = enumobj->lpVtbl->GetChildren(enumobj, &nb_children);
4243 if (FAILED(hr))
4244 goto cleanup;
4246 for (i = 0; i < nb_children; i++)
4248 hr = enumobj->lpVtbl->GetChild(enumobj, i, &filedata);
4249 if (FAILED(hr))
4250 goto cleanup;
4252 hr = filedata->lpVtbl->GetType(filedata, &guid);
4253 if (SUCCEEDED(hr)) {
4254 if (IsEqualGUID(&guid, &TID_D3DRMMesh)) {
4255 container_ptr = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(*container_ptr));
4256 if (!container_ptr) {
4257 hr = E_OUTOFMEMORY;
4258 goto cleanup;
4260 list_add_tail(&container_list, &container_ptr->entry);
4261 D3DXMatrixIdentity(&container_ptr->transform);
4263 hr = D3DXLoadSkinMeshFromXof(filedata, options, device,
4264 (provide_flags & PROVIDE_ADJACENCY) ? &container_ptr->adjacency : NULL,
4265 (provide_flags & PROVIDE_MATERIALS) ? &container_ptr->materials : NULL,
4266 NULL, &container_ptr->num_materials, NULL, &container_ptr->mesh);
4267 } else if (IsEqualGUID(&guid, &TID_D3DRMFrame)) {
4268 hr = parse_frame(filedata, options, device, &identity, &container_list, provide_flags);
4270 if (FAILED(hr)) goto cleanup;
4272 filedata->lpVtbl->Release(filedata);
4273 filedata = NULL;
4274 if (FAILED(hr))
4275 goto cleanup;
4278 enumobj->lpVtbl->Release(enumobj);
4279 enumobj = NULL;
4280 d3dxfile->lpVtbl->Release(d3dxfile);
4281 d3dxfile = NULL;
4283 if (list_empty(&container_list)) {
4284 hr = E_FAIL;
4285 goto cleanup;
4288 fvf = D3DFVF_XYZ;
4289 num_faces = 0;
4290 num_vertices = 0;
4291 num_materials = 0;
4292 LIST_FOR_EACH_ENTRY(container_ptr, &container_list, struct mesh_container, entry)
4294 ID3DXMesh *mesh = container_ptr->mesh;
4295 fvf |= mesh->lpVtbl->GetFVF(mesh);
4296 num_faces += mesh->lpVtbl->GetNumFaces(mesh);
4297 num_vertices += mesh->lpVtbl->GetNumVertices(mesh);
4298 num_materials += container_ptr->num_materials;
4301 hr = D3DXCreateMeshFVF(num_faces, num_vertices, options, fvf, device, &concat_mesh);
4302 if (FAILED(hr)) goto cleanup;
4304 hr = concat_mesh->lpVtbl->GetDeclaration(concat_mesh, concat_decl);
4305 if (FAILED(hr)) goto cleanup;
4307 concat_vertex_size = D3DXGetDeclVertexSize(concat_decl, 0);
4309 hr = concat_mesh->lpVtbl->LockVertexBuffer(concat_mesh, 0, (void**)&concat_vertices);
4310 if (FAILED(hr)) goto cleanup;
4312 LIST_FOR_EACH_ENTRY(container_ptr, &container_list, struct mesh_container, entry)
4314 D3DVERTEXELEMENT9 mesh_decl[MAX_FVF_DECL_SIZE];
4315 ID3DXMesh *mesh = container_ptr->mesh;
4316 DWORD num_mesh_vertices = mesh->lpVtbl->GetNumVertices(mesh);
4317 DWORD mesh_vertex_size;
4318 const BYTE *mesh_vertices;
4319 DWORD i;
4321 hr = mesh->lpVtbl->GetDeclaration(mesh, mesh_decl);
4322 if (FAILED(hr)) goto cleanup;
4324 mesh_vertex_size = D3DXGetDeclVertexSize(mesh_decl, 0);
4326 hr = mesh->lpVtbl->LockVertexBuffer(mesh, D3DLOCK_READONLY, (void**)&mesh_vertices);
4327 if (FAILED(hr)) goto cleanup;
4329 for (i = 0; i < num_mesh_vertices; i++) {
4330 int j;
4331 int k = 1;
4333 D3DXVec3TransformCoord((D3DXVECTOR3*)concat_vertices,
4334 (D3DXVECTOR3*)mesh_vertices,
4335 &container_ptr->transform);
4336 for (j = 1; concat_decl[j].Stream != 0xff; j++)
4338 if (concat_decl[j].Usage == mesh_decl[k].Usage &&
4339 concat_decl[j].UsageIndex == mesh_decl[k].UsageIndex)
4341 if (concat_decl[j].Usage == D3DDECLUSAGE_NORMAL) {
4342 D3DXVec3TransformCoord((D3DXVECTOR3*)(concat_vertices + concat_decl[j].Offset),
4343 (D3DXVECTOR3*)(mesh_vertices + mesh_decl[k].Offset),
4344 &container_ptr->transform);
4345 } else {
4346 memcpy(concat_vertices + concat_decl[j].Offset,
4347 mesh_vertices + mesh_decl[k].Offset,
4348 d3dx_decltype_size[mesh_decl[k].Type]);
4350 k++;
4353 mesh_vertices += mesh_vertex_size;
4354 concat_vertices += concat_vertex_size;
4357 mesh->lpVtbl->UnlockVertexBuffer(mesh);
4360 concat_mesh->lpVtbl->UnlockVertexBuffer(concat_mesh);
4361 concat_vertices = NULL;
4363 hr = concat_mesh->lpVtbl->LockIndexBuffer(concat_mesh, 0, &concat_indices);
4364 if (FAILED(hr)) goto cleanup;
4366 index_offset = 0;
4367 LIST_FOR_EACH_ENTRY(container_ptr, &container_list, struct mesh_container, entry)
4369 ID3DXMesh *mesh = container_ptr->mesh;
4370 const void *mesh_indices;
4371 DWORD num_mesh_faces = mesh->lpVtbl->GetNumFaces(mesh);
4372 DWORD i;
4374 hr = mesh->lpVtbl->LockIndexBuffer(mesh, D3DLOCK_READONLY, (void**)&mesh_indices);
4375 if (FAILED(hr)) goto cleanup;
4377 if (options & D3DXMESH_32BIT) {
4378 DWORD *dest = concat_indices;
4379 const DWORD *src = mesh_indices;
4380 for (i = 0; i < num_mesh_faces * 3; i++)
4381 *dest++ = index_offset + *src++;
4382 concat_indices = dest;
4383 } else {
4384 WORD *dest = concat_indices;
4385 const WORD *src = mesh_indices;
4386 for (i = 0; i < num_mesh_faces * 3; i++)
4387 *dest++ = index_offset + *src++;
4388 concat_indices = dest;
4390 mesh->lpVtbl->UnlockIndexBuffer(mesh);
4392 index_offset += num_mesh_faces * 3;
4395 concat_mesh->lpVtbl->UnlockIndexBuffer(concat_mesh);
4396 concat_indices = NULL;
4398 if (num_materials) {
4399 DWORD *concat_attrib_buffer = NULL;
4400 DWORD offset = 0;
4402 hr = concat_mesh->lpVtbl->LockAttributeBuffer(concat_mesh, 0, &concat_attrib_buffer);
4403 if (FAILED(hr)) goto cleanup;
4405 LIST_FOR_EACH_ENTRY(container_ptr, &container_list, struct mesh_container, entry)
4407 ID3DXMesh *mesh = container_ptr->mesh;
4408 const DWORD *mesh_attrib_buffer = NULL;
4409 DWORD count = mesh->lpVtbl->GetNumFaces(mesh);
4411 hr = mesh->lpVtbl->LockAttributeBuffer(mesh, D3DLOCK_READONLY, (DWORD**)&mesh_attrib_buffer);
4412 if (FAILED(hr)) {
4413 concat_mesh->lpVtbl->UnlockAttributeBuffer(concat_mesh);
4414 goto cleanup;
4417 while (count--)
4418 *concat_attrib_buffer++ = offset + *mesh_attrib_buffer++;
4420 mesh->lpVtbl->UnlockAttributeBuffer(mesh);
4421 offset += container_ptr->num_materials;
4423 concat_mesh->lpVtbl->UnlockAttributeBuffer(concat_mesh);
4426 if (materials_out || effects_out) {
4427 D3DXMATERIAL *out_ptr;
4428 if (!num_materials) {
4429 /* create default material */
4430 hr = D3DXCreateBuffer(sizeof(D3DXMATERIAL), &materials);
4431 if (FAILED(hr)) goto cleanup;
4433 out_ptr = ID3DXBuffer_GetBufferPointer(materials);
4434 out_ptr->MatD3D.Diffuse.r = 0.5f;
4435 out_ptr->MatD3D.Diffuse.g = 0.5f;
4436 out_ptr->MatD3D.Diffuse.b = 0.5f;
4437 out_ptr->MatD3D.Specular.r = 0.5f;
4438 out_ptr->MatD3D.Specular.g = 0.5f;
4439 out_ptr->MatD3D.Specular.b = 0.5f;
4440 /* D3DXCreateBuffer initializes the rest to zero */
4441 } else {
4442 DWORD buffer_size = num_materials * sizeof(D3DXMATERIAL);
4443 char *strings_out_ptr;
4445 LIST_FOR_EACH_ENTRY(container_ptr, &container_list, struct mesh_container, entry)
4447 if (container_ptr->materials) {
4448 DWORD i;
4449 const D3DXMATERIAL *in_ptr = ID3DXBuffer_GetBufferPointer(container_ptr->materials);
4450 for (i = 0; i < container_ptr->num_materials; i++)
4452 if (in_ptr->pTextureFilename)
4453 buffer_size += strlen(in_ptr->pTextureFilename) + 1;
4454 in_ptr++;
4459 hr = D3DXCreateBuffer(buffer_size, &materials);
4460 if (FAILED(hr)) goto cleanup;
4461 out_ptr = ID3DXBuffer_GetBufferPointer(materials);
4462 strings_out_ptr = (char*)(out_ptr + num_materials);
4464 LIST_FOR_EACH_ENTRY(container_ptr, &container_list, struct mesh_container, entry)
4466 if (container_ptr->materials) {
4467 DWORD i;
4468 const D3DXMATERIAL *in_ptr = ID3DXBuffer_GetBufferPointer(container_ptr->materials);
4469 for (i = 0; i < container_ptr->num_materials; i++)
4471 out_ptr->MatD3D = in_ptr->MatD3D;
4472 if (in_ptr->pTextureFilename) {
4473 out_ptr->pTextureFilename = strings_out_ptr;
4474 strcpy(out_ptr->pTextureFilename, in_ptr->pTextureFilename);
4475 strings_out_ptr += strlen(in_ptr->pTextureFilename) + 1;
4477 in_ptr++;
4478 out_ptr++;
4484 if (!num_materials)
4485 num_materials = 1;
4487 if (effects_out) {
4488 generate_effects(materials, num_materials, &effects);
4489 if (!materials_out) {
4490 ID3DXBuffer_Release(materials);
4491 materials = NULL;
4495 if (adjacency_out) {
4496 if (!list_next(&container_list, list_head(&container_list))) {
4497 container_ptr = LIST_ENTRY(list_head(&container_list), struct mesh_container, entry);
4498 adjacency = container_ptr->adjacency;
4499 container_ptr->adjacency = NULL;
4500 } else {
4501 DWORD offset = 0;
4502 DWORD *out_ptr;
4504 hr = D3DXCreateBuffer(num_faces * 3 * sizeof(DWORD), &adjacency);
4505 if (FAILED(hr)) goto cleanup;
4507 out_ptr = ID3DXBuffer_GetBufferPointer(adjacency);
4508 LIST_FOR_EACH_ENTRY(container_ptr, &container_list, struct mesh_container, entry)
4510 DWORD i;
4511 DWORD count = 3 * container_ptr->mesh->lpVtbl->GetNumFaces(container_ptr->mesh);
4512 DWORD *in_ptr = ID3DXBuffer_GetBufferPointer(container_ptr->adjacency);
4514 for (i = 0; i < count; i++)
4515 *out_ptr++ = offset + *in_ptr++;
4517 offset += count;
4522 *mesh_out = concat_mesh;
4523 if (adjacency_out) *adjacency_out = adjacency;
4524 if (materials_out) *materials_out = materials;
4525 if (effects_out) *effects_out = effects;
4526 if (num_materials_out) *num_materials_out = num_materials;
4528 hr = D3D_OK;
4529 cleanup:
4530 if (concat_indices) concat_mesh->lpVtbl->UnlockIndexBuffer(concat_mesh);
4531 if (concat_vertices) concat_mesh->lpVtbl->UnlockVertexBuffer(concat_mesh);
4532 if (filedata) filedata->lpVtbl->Release(filedata);
4533 if (enumobj) enumobj->lpVtbl->Release(enumobj);
4534 if (d3dxfile) d3dxfile->lpVtbl->Release(d3dxfile);
4535 if (FAILED(hr)) {
4536 if (concat_mesh) IUnknown_Release(concat_mesh);
4537 if (materials) ID3DXBuffer_Release(materials);
4538 if (effects) ID3DXBuffer_Release(effects);
4539 if (adjacency) ID3DXBuffer_Release(adjacency);
4541 LIST_FOR_EACH_ENTRY_SAFE(container_ptr, next_container_ptr, &container_list, struct mesh_container, entry)
4543 if (container_ptr->mesh) IUnknown_Release(container_ptr->mesh);
4544 if (container_ptr->adjacency) ID3DXBuffer_Release(container_ptr->adjacency);
4545 if (container_ptr->materials) ID3DXBuffer_Release(container_ptr->materials);
4546 if (container_ptr->effects) ID3DXBuffer_Release(container_ptr->effects);
4547 HeapFree(GetProcessHeap(), 0, container_ptr);
4549 return hr;
4552 struct vertex
4554 D3DXVECTOR3 position;
4555 D3DXVECTOR3 normal;
4558 HRESULT WINAPI D3DXCreatePolygon(struct IDirect3DDevice9 *device, float length, UINT sides,
4559 struct ID3DXMesh **mesh, struct ID3DXBuffer **adjacency)
4561 HRESULT hr;
4562 ID3DXMesh *polygon;
4563 struct vertex *vertices;
4564 WORD (*faces)[3];
4565 DWORD (*adjacency_buf)[3];
4566 float angle, scale;
4567 unsigned int i;
4569 TRACE("device %p, length %f, sides %u, mesh %p, adjacency %p.\n",
4570 device, length, sides, mesh, adjacency);
4572 if (!device || length < 0.0f || sides < 3 || !mesh)
4573 return D3DERR_INVALIDCALL;
4575 if (FAILED(hr = D3DXCreateMeshFVF(sides, sides + 1, D3DXMESH_MANAGED,
4576 D3DFVF_XYZ | D3DFVF_NORMAL, device, &polygon)))
4578 return hr;
4581 if (FAILED(hr = polygon->lpVtbl->LockVertexBuffer(polygon, 0, (void **)&vertices)))
4583 polygon->lpVtbl->Release(polygon);
4584 return hr;
4587 if (FAILED(hr = polygon->lpVtbl->LockIndexBuffer(polygon, 0, (void **)&faces)))
4589 polygon->lpVtbl->UnlockVertexBuffer(polygon);
4590 polygon->lpVtbl->Release(polygon);
4591 return hr;
4594 angle = D3DX_PI / sides;
4595 scale = 0.5f * length / sinf(angle);
4596 angle *= 2.0f;
4598 vertices[0].position.x = 0.0f;
4599 vertices[0].position.y = 0.0f;
4600 vertices[0].position.z = 0.0f;
4601 vertices[0].normal.x = 0.0f;
4602 vertices[0].normal.y = 0.0f;
4603 vertices[0].normal.z = 1.0f;
4605 for (i = 0; i < sides; ++i)
4607 vertices[i + 1].position.x = cosf(angle * i) * scale;
4608 vertices[i + 1].position.y = sinf(angle * i) * scale;
4609 vertices[i + 1].position.z = 0.0f;
4610 vertices[i + 1].normal.x = 0.0f;
4611 vertices[i + 1].normal.y = 0.0f;
4612 vertices[i + 1].normal.z = 1.0f;
4614 faces[i][0] = 0;
4615 faces[i][1] = i + 1;
4616 faces[i][2] = i + 2;
4619 faces[sides - 1][2] = 1;
4621 polygon->lpVtbl->UnlockVertexBuffer(polygon);
4622 polygon->lpVtbl->UnlockIndexBuffer(polygon);
4624 if (adjacency)
4626 if (FAILED(hr = D3DXCreateBuffer(sides * sizeof(DWORD) * 3, adjacency)))
4628 polygon->lpVtbl->Release(polygon);
4629 return hr;
4632 adjacency_buf = ID3DXBuffer_GetBufferPointer(*adjacency);
4633 for (i = 0; i < sides; ++i)
4635 adjacency_buf[i][0] = i - 1;
4636 adjacency_buf[i][1] = ~0U;
4637 adjacency_buf[i][2] = i + 1;
4639 adjacency_buf[0][0] = sides - 1;
4640 adjacency_buf[sides - 1][2] = 0;
4643 *mesh = polygon;
4645 return D3D_OK;
4648 HRESULT WINAPI D3DXCreateBox(struct IDirect3DDevice9 *device, float width, float height,
4649 float depth, struct ID3DXMesh **mesh, struct ID3DXBuffer **adjacency)
4651 HRESULT hr;
4652 ID3DXMesh *box;
4653 struct vertex *vertices;
4654 WORD (*faces)[3];
4655 DWORD *adjacency_buf;
4656 unsigned int i, face;
4657 static const D3DXVECTOR3 unit_box[] =
4659 {-0.5f, -0.5f, -0.5f}, {-0.5f, -0.5f, 0.5f}, {-0.5f, 0.5f, 0.5f}, {-0.5f, 0.5f, -0.5f},
4660 {-0.5f, 0.5f, -0.5f}, {-0.5f, 0.5f, 0.5f}, { 0.5f, 0.5f, 0.5f}, { 0.5f, 0.5f, -0.5f},
4661 { 0.5f, 0.5f, -0.5f}, { 0.5f, 0.5f, 0.5f}, { 0.5f, -0.5f, 0.5f}, { 0.5f, -0.5f, -0.5f},
4662 {-0.5f, -0.5f, 0.5f}, {-0.5f, -0.5f, -0.5f}, { 0.5f, -0.5f, -0.5f}, { 0.5f, -0.5f, 0.5f},
4663 {-0.5f, -0.5f, 0.5f}, { 0.5f, -0.5f, 0.5f}, { 0.5f, 0.5f, 0.5f}, {-0.5f, 0.5f, 0.5f},
4664 {-0.5f, -0.5f, -0.5f}, {-0.5f, 0.5f, -0.5f}, { 0.5f, 0.5f, -0.5f}, { 0.5f, -0.5f, -0.5f}
4666 static const D3DXVECTOR3 normals[] =
4668 {-1.0f, 0.0f, 0.0f}, { 0.0f, 1.0f, 0.0f}, { 1.0f, 0.0f, 0.0f},
4669 { 0.0f, -1.0f, 0.0f}, { 0.0f, 0.0f, 1.0f}, { 0.0f, 0.0f, -1.0f}
4671 static const DWORD adjacency_table[] =
4673 6, 9, 1, 2, 10, 0, 1, 9, 3, 4, 10, 2,
4674 3, 8, 5, 7, 11, 4, 0, 11, 7, 5, 8, 6,
4675 7, 4, 9, 2, 0, 8, 1, 3, 11, 5, 6, 10
4678 TRACE("device %p, width %f, height %f, depth %f, mesh %p, adjacency %p\n",
4679 device, width, height, depth, mesh, adjacency);
4681 if (!device || width < 0.0f || height < 0.0f || depth < 0.0f || !mesh)
4683 return D3DERR_INVALIDCALL;
4686 if (FAILED(hr = D3DXCreateMeshFVF(12, 24, D3DXMESH_MANAGED, D3DFVF_XYZ | D3DFVF_NORMAL, device, &box)))
4688 return hr;
4691 if (FAILED(hr = box->lpVtbl->LockVertexBuffer(box, 0, (void **)&vertices)))
4693 box->lpVtbl->Release(box);
4694 return hr;
4697 if (FAILED(hr = box->lpVtbl->LockIndexBuffer(box, 0, (void **)&faces)))
4699 box->lpVtbl->UnlockVertexBuffer(box);
4700 box->lpVtbl->Release(box);
4701 return hr;
4704 for (i = 0; i < 24; i++)
4706 vertices[i].position.x = width * unit_box[i].x;
4707 vertices[i].position.y = height * unit_box[i].y;
4708 vertices[i].position.z = depth * unit_box[i].z;
4709 vertices[i].normal.x = normals[i / 4].x;
4710 vertices[i].normal.y = normals[i / 4].y;
4711 vertices[i].normal.z = normals[i / 4].z;
4714 face = 0;
4715 for (i = 0; i < 12; i++)
4717 faces[i][0] = face++;
4718 faces[i][1] = face++;
4719 faces[i][2] = (i % 2) ? face - 4 : face;
4722 box->lpVtbl->UnlockIndexBuffer(box);
4723 box->lpVtbl->UnlockVertexBuffer(box);
4725 if (adjacency)
4727 if (FAILED(hr = D3DXCreateBuffer(sizeof(adjacency_table), adjacency)))
4729 box->lpVtbl->Release(box);
4730 return hr;
4733 adjacency_buf = ID3DXBuffer_GetBufferPointer(*adjacency);
4734 memcpy(adjacency_buf, adjacency_table, sizeof(adjacency_table));
4737 *mesh = box;
4739 return D3D_OK;
4742 typedef WORD face[3];
4744 struct sincos_table
4746 float *sin;
4747 float *cos;
4750 static void free_sincos_table(struct sincos_table *sincos_table)
4752 HeapFree(GetProcessHeap(), 0, sincos_table->cos);
4753 HeapFree(GetProcessHeap(), 0, sincos_table->sin);
4756 /* pre compute sine and cosine tables; caller must free */
4757 static BOOL compute_sincos_table(struct sincos_table *sincos_table, float angle_start, float angle_step, int n)
4759 float angle;
4760 int i;
4762 sincos_table->sin = HeapAlloc(GetProcessHeap(), 0, n * sizeof(*sincos_table->sin));
4763 if (!sincos_table->sin)
4765 return FALSE;
4767 sincos_table->cos = HeapAlloc(GetProcessHeap(), 0, n * sizeof(*sincos_table->cos));
4768 if (!sincos_table->cos)
4770 HeapFree(GetProcessHeap(), 0, sincos_table->sin);
4771 return FALSE;
4774 angle = angle_start;
4775 for (i = 0; i < n; i++)
4777 sincos_table->sin[i] = sinf(angle);
4778 sincos_table->cos[i] = cosf(angle);
4779 angle += angle_step;
4782 return TRUE;
4785 static WORD vertex_index(UINT slices, int slice, int stack)
4787 return stack*slices+slice+1;
4790 HRESULT WINAPI D3DXCreateSphere(struct IDirect3DDevice9 *device, float radius, UINT slices,
4791 UINT stacks, struct ID3DXMesh **mesh, struct ID3DXBuffer **adjacency)
4793 DWORD number_of_vertices, number_of_faces;
4794 HRESULT hr;
4795 ID3DXMesh *sphere;
4796 struct vertex *vertices;
4797 face *faces;
4798 float phi_step, phi_start;
4799 struct sincos_table phi;
4800 float theta_step, theta, sin_theta, cos_theta;
4801 DWORD vertex, face, stack, slice;
4803 TRACE("(%p, %f, %u, %u, %p, %p)\n", device, radius, slices, stacks, mesh, adjacency);
4805 if (!device || radius < 0.0f || slices < 2 || stacks < 2 || !mesh)
4807 return D3DERR_INVALIDCALL;
4810 number_of_vertices = 2 + slices * (stacks-1);
4811 number_of_faces = 2 * slices + (stacks - 2) * (2 * slices);
4813 hr = D3DXCreateMeshFVF(number_of_faces, number_of_vertices, D3DXMESH_MANAGED,
4814 D3DFVF_XYZ | D3DFVF_NORMAL, device, &sphere);
4815 if (FAILED(hr))
4817 return hr;
4820 if (FAILED(hr = sphere->lpVtbl->LockVertexBuffer(sphere, 0, (void **)&vertices)))
4822 sphere->lpVtbl->Release(sphere);
4823 return hr;
4826 if (FAILED(hr = sphere->lpVtbl->LockIndexBuffer(sphere, 0, (void **)&faces)))
4828 sphere->lpVtbl->UnlockVertexBuffer(sphere);
4829 sphere->lpVtbl->Release(sphere);
4830 return hr;
4833 /* phi = angle on xz plane wrt z axis */
4834 phi_step = -2.0f * D3DX_PI / slices;
4835 phi_start = D3DX_PI / 2.0f;
4837 if (!compute_sincos_table(&phi, phi_start, phi_step, slices))
4839 sphere->lpVtbl->UnlockIndexBuffer(sphere);
4840 sphere->lpVtbl->UnlockVertexBuffer(sphere);
4841 sphere->lpVtbl->Release(sphere);
4842 return E_OUTOFMEMORY;
4845 /* theta = angle on xy plane wrt x axis */
4846 theta_step = D3DX_PI / stacks;
4847 theta = theta_step;
4849 vertex = 0;
4850 face = 0;
4852 vertices[vertex].normal.x = 0.0f;
4853 vertices[vertex].normal.y = 0.0f;
4854 vertices[vertex].normal.z = 1.0f;
4855 vertices[vertex].position.x = 0.0f;
4856 vertices[vertex].position.y = 0.0f;
4857 vertices[vertex].position.z = radius;
4858 vertex++;
4860 for (stack = 0; stack < stacks - 1; stack++)
4862 sin_theta = sinf(theta);
4863 cos_theta = cosf(theta);
4865 for (slice = 0; slice < slices; slice++)
4867 vertices[vertex].normal.x = sin_theta * phi.cos[slice];
4868 vertices[vertex].normal.y = sin_theta * phi.sin[slice];
4869 vertices[vertex].normal.z = cos_theta;
4870 vertices[vertex].position.x = radius * sin_theta * phi.cos[slice];
4871 vertices[vertex].position.y = radius * sin_theta * phi.sin[slice];
4872 vertices[vertex].position.z = radius * cos_theta;
4873 vertex++;
4875 if (slice > 0)
4877 if (stack == 0)
4879 /* top stack is triangle fan */
4880 faces[face][0] = 0;
4881 faces[face][1] = slice + 1;
4882 faces[face][2] = slice;
4883 face++;
4885 else
4887 /* stacks in between top and bottom are quad strips */
4888 faces[face][0] = vertex_index(slices, slice-1, stack-1);
4889 faces[face][1] = vertex_index(slices, slice, stack-1);
4890 faces[face][2] = vertex_index(slices, slice-1, stack);
4891 face++;
4893 faces[face][0] = vertex_index(slices, slice, stack-1);
4894 faces[face][1] = vertex_index(slices, slice, stack);
4895 faces[face][2] = vertex_index(slices, slice-1, stack);
4896 face++;
4901 theta += theta_step;
4903 if (stack == 0)
4905 faces[face][0] = 0;
4906 faces[face][1] = 1;
4907 faces[face][2] = slice;
4908 face++;
4910 else
4912 faces[face][0] = vertex_index(slices, slice-1, stack-1);
4913 faces[face][1] = vertex_index(slices, 0, stack-1);
4914 faces[face][2] = vertex_index(slices, slice-1, stack);
4915 face++;
4917 faces[face][0] = vertex_index(slices, 0, stack-1);
4918 faces[face][1] = vertex_index(slices, 0, stack);
4919 faces[face][2] = vertex_index(slices, slice-1, stack);
4920 face++;
4924 vertices[vertex].position.x = 0.0f;
4925 vertices[vertex].position.y = 0.0f;
4926 vertices[vertex].position.z = -radius;
4927 vertices[vertex].normal.x = 0.0f;
4928 vertices[vertex].normal.y = 0.0f;
4929 vertices[vertex].normal.z = -1.0f;
4931 /* bottom stack is triangle fan */
4932 for (slice = 1; slice < slices; slice++)
4934 faces[face][0] = vertex_index(slices, slice-1, stack-1);
4935 faces[face][1] = vertex_index(slices, slice, stack-1);
4936 faces[face][2] = vertex;
4937 face++;
4940 faces[face][0] = vertex_index(slices, slice-1, stack-1);
4941 faces[face][1] = vertex_index(slices, 0, stack-1);
4942 faces[face][2] = vertex;
4944 free_sincos_table(&phi);
4945 sphere->lpVtbl->UnlockIndexBuffer(sphere);
4946 sphere->lpVtbl->UnlockVertexBuffer(sphere);
4949 if (adjacency)
4951 if (FAILED(hr = D3DXCreateBuffer(number_of_faces * sizeof(DWORD) * 3, adjacency)))
4953 sphere->lpVtbl->Release(sphere);
4954 return hr;
4957 if (FAILED(hr = sphere->lpVtbl->GenerateAdjacency(sphere, 0.0f, (*adjacency)->lpVtbl->GetBufferPointer(*adjacency))))
4959 (*adjacency)->lpVtbl->Release(*adjacency);
4960 sphere->lpVtbl->Release(sphere);
4961 return hr;
4965 *mesh = sphere;
4967 return D3D_OK;
4970 HRESULT WINAPI D3DXCreateCylinder(struct IDirect3DDevice9 *device, float radius1, float radius2,
4971 float length, UINT slices, UINT stacks, struct ID3DXMesh **mesh, struct ID3DXBuffer **adjacency)
4973 DWORD number_of_vertices, number_of_faces;
4974 HRESULT hr;
4975 ID3DXMesh *cylinder;
4976 struct vertex *vertices;
4977 face *faces;
4978 float theta_step, theta_start;
4979 struct sincos_table theta;
4980 float delta_radius, radius, radius_step;
4981 float z, z_step, z_normal;
4982 DWORD vertex, face, slice, stack;
4984 TRACE("(%p, %f, %f, %f, %u, %u, %p, %p)\n", device, radius1, radius2, length, slices, stacks, mesh, adjacency);
4986 if (device == NULL || radius1 < 0.0f || radius2 < 0.0f || length < 0.0f || slices < 2 || stacks < 1 || mesh == NULL)
4988 return D3DERR_INVALIDCALL;
4991 number_of_vertices = 2 + (slices * (3 + stacks));
4992 number_of_faces = 2 * slices + stacks * (2 * slices);
4994 hr = D3DXCreateMeshFVF(number_of_faces, number_of_vertices, D3DXMESH_MANAGED,
4995 D3DFVF_XYZ | D3DFVF_NORMAL, device, &cylinder);
4996 if (FAILED(hr))
4998 return hr;
5001 if (FAILED(hr = cylinder->lpVtbl->LockVertexBuffer(cylinder, 0, (void **)&vertices)))
5003 cylinder->lpVtbl->Release(cylinder);
5004 return hr;
5007 if (FAILED(hr = cylinder->lpVtbl->LockIndexBuffer(cylinder, 0, (void **)&faces)))
5009 cylinder->lpVtbl->UnlockVertexBuffer(cylinder);
5010 cylinder->lpVtbl->Release(cylinder);
5011 return hr;
5014 /* theta = angle on xy plane wrt x axis */
5015 theta_step = -2.0f * D3DX_PI / slices;
5016 theta_start = D3DX_PI / 2.0f;
5018 if (!compute_sincos_table(&theta, theta_start, theta_step, slices))
5020 cylinder->lpVtbl->UnlockIndexBuffer(cylinder);
5021 cylinder->lpVtbl->UnlockVertexBuffer(cylinder);
5022 cylinder->lpVtbl->Release(cylinder);
5023 return E_OUTOFMEMORY;
5026 vertex = 0;
5027 face = 0;
5029 delta_radius = radius1 - radius2;
5030 radius = radius1;
5031 radius_step = delta_radius / stacks;
5033 z = -length / 2;
5034 z_step = length / stacks;
5035 z_normal = delta_radius / length;
5036 if (isnan(z_normal))
5038 z_normal = 0.0f;
5041 vertices[vertex].normal.x = 0.0f;
5042 vertices[vertex].normal.y = 0.0f;
5043 vertices[vertex].normal.z = -1.0f;
5044 vertices[vertex].position.x = 0.0f;
5045 vertices[vertex].position.y = 0.0f;
5046 vertices[vertex++].position.z = z;
5048 for (slice = 0; slice < slices; slice++, vertex++)
5050 vertices[vertex].normal.x = 0.0f;
5051 vertices[vertex].normal.y = 0.0f;
5052 vertices[vertex].normal.z = -1.0f;
5053 vertices[vertex].position.x = radius * theta.cos[slice];
5054 vertices[vertex].position.y = radius * theta.sin[slice];
5055 vertices[vertex].position.z = z;
5057 if (slice > 0)
5059 faces[face][0] = 0;
5060 faces[face][1] = slice;
5061 faces[face++][2] = slice + 1;
5065 faces[face][0] = 0;
5066 faces[face][1] = slice;
5067 faces[face++][2] = 1;
5069 for (stack = 1; stack <= stacks+1; stack++)
5071 for (slice = 0; slice < slices; slice++, vertex++)
5073 vertices[vertex].normal.x = theta.cos[slice];
5074 vertices[vertex].normal.y = theta.sin[slice];
5075 vertices[vertex].normal.z = z_normal;
5076 D3DXVec3Normalize(&vertices[vertex].normal, &vertices[vertex].normal);
5077 vertices[vertex].position.x = radius * theta.cos[slice];
5078 vertices[vertex].position.y = radius * theta.sin[slice];
5079 vertices[vertex].position.z = z;
5081 if (stack > 1 && slice > 0)
5083 faces[face][0] = vertex_index(slices, slice-1, stack-1);
5084 faces[face][1] = vertex_index(slices, slice-1, stack);
5085 faces[face++][2] = vertex_index(slices, slice, stack-1);
5087 faces[face][0] = vertex_index(slices, slice, stack-1);
5088 faces[face][1] = vertex_index(slices, slice-1, stack);
5089 faces[face++][2] = vertex_index(slices, slice, stack);
5093 if (stack > 1)
5095 faces[face][0] = vertex_index(slices, slice-1, stack-1);
5096 faces[face][1] = vertex_index(slices, slice-1, stack);
5097 faces[face++][2] = vertex_index(slices, 0, stack-1);
5099 faces[face][0] = vertex_index(slices, 0, stack-1);
5100 faces[face][1] = vertex_index(slices, slice-1, stack);
5101 faces[face++][2] = vertex_index(slices, 0, stack);
5104 if (stack < stacks + 1)
5106 z += z_step;
5107 radius -= radius_step;
5111 for (slice = 0; slice < slices; slice++, vertex++)
5113 vertices[vertex].normal.x = 0.0f;
5114 vertices[vertex].normal.y = 0.0f;
5115 vertices[vertex].normal.z = 1.0f;
5116 vertices[vertex].position.x = radius * theta.cos[slice];
5117 vertices[vertex].position.y = radius * theta.sin[slice];
5118 vertices[vertex].position.z = z;
5120 if (slice > 0)
5122 faces[face][0] = vertex_index(slices, slice-1, stack);
5123 faces[face][1] = number_of_vertices - 1;
5124 faces[face++][2] = vertex_index(slices, slice, stack);
5128 vertices[vertex].position.x = 0.0f;
5129 vertices[vertex].position.y = 0.0f;
5130 vertices[vertex].position.z = z;
5131 vertices[vertex].normal.x = 0.0f;
5132 vertices[vertex].normal.y = 0.0f;
5133 vertices[vertex].normal.z = 1.0f;
5135 faces[face][0] = vertex_index(slices, slice-1, stack);
5136 faces[face][1] = number_of_vertices - 1;
5137 faces[face][2] = vertex_index(slices, 0, stack);
5139 free_sincos_table(&theta);
5140 cylinder->lpVtbl->UnlockIndexBuffer(cylinder);
5141 cylinder->lpVtbl->UnlockVertexBuffer(cylinder);
5143 if (adjacency)
5145 if (FAILED(hr = D3DXCreateBuffer(number_of_faces * sizeof(DWORD) * 3, adjacency)))
5147 cylinder->lpVtbl->Release(cylinder);
5148 return hr;
5151 if (FAILED(hr = cylinder->lpVtbl->GenerateAdjacency(cylinder, 0.0f, (*adjacency)->lpVtbl->GetBufferPointer(*adjacency))))
5153 (*adjacency)->lpVtbl->Release(*adjacency);
5154 cylinder->lpVtbl->Release(cylinder);
5155 return hr;
5159 *mesh = cylinder;
5161 return D3D_OK;
5164 HRESULT WINAPI D3DXCreateTeapot(struct IDirect3DDevice9 *device,
5165 struct ID3DXMesh **mesh, struct ID3DXBuffer **adjacency)
5167 FIXME("device %p, mesh %p, adjacency %p semi-stub.\n", device, mesh, adjacency);
5169 return D3DXCreateSphere(device, 1.0f, 4, 4, mesh, adjacency);
5172 HRESULT WINAPI D3DXCreateTextA(struct IDirect3DDevice9 *device, HDC hdc, const char *text, float deviation,
5173 float extrusion, struct ID3DXMesh **mesh, struct ID3DXBuffer **adjacency, GLYPHMETRICSFLOAT *glyphmetrics)
5175 WCHAR *textW;
5176 HRESULT hr;
5177 int len;
5179 TRACE("device %p, hdc %p, text %s, deviation %.8e, extrusion %.8e, mesh %p, adjacency %p, glyphmetrics %p.\n",
5180 device, hdc, debugstr_a(text), deviation, extrusion, mesh, adjacency, glyphmetrics);
5182 if (!text)
5183 return D3DERR_INVALIDCALL;
5185 len = MultiByteToWideChar(CP_ACP, 0, text, -1, NULL, 0);
5186 textW = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
5187 MultiByteToWideChar(CP_ACP, 0, text, -1, textW, len);
5189 hr = D3DXCreateTextW(device, hdc, textW, deviation, extrusion,
5190 mesh, adjacency, glyphmetrics);
5191 HeapFree(GetProcessHeap(), 0, textW);
5193 return hr;
5196 HRESULT WINAPI D3DXCreateTorus(struct IDirect3DDevice9 *device,
5197 float innerradius, float outerradius, UINT sides, UINT rings, struct ID3DXMesh **mesh, ID3DXBuffer **adjacency)
5199 HRESULT hr;
5200 ID3DXMesh *torus;
5201 WORD (*faces)[3];
5202 struct vertex *vertices;
5203 float phi, phi_step, sin_phi, cos_phi;
5204 float theta, theta_step, sin_theta, cos_theta;
5205 unsigned int i, j, numvert, numfaces;
5207 TRACE("device %p, innerradius %.8e, outerradius %.8e, sides %u, rings %u, mesh %p, adjacency %p.\n",
5208 device, innerradius, outerradius, sides, rings, mesh, adjacency);
5210 numvert = sides * rings;
5211 numfaces = numvert * 2;
5213 if (!device || innerradius < 0.0f || outerradius < 0.0f || sides < 3 || rings < 3 || !mesh)
5215 WARN("Invalid arguments.\n");
5216 return D3DERR_INVALIDCALL;
5219 if (FAILED(hr = D3DXCreateMeshFVF(numfaces, numvert, D3DXMESH_MANAGED, D3DFVF_XYZ | D3DFVF_NORMAL, device, &torus)))
5220 return hr;
5222 if (FAILED(hr = torus->lpVtbl->LockVertexBuffer(torus, 0, (void **)&vertices)))
5224 torus->lpVtbl->Release(torus);
5225 return hr;
5228 if (FAILED(hr = torus->lpVtbl->LockIndexBuffer(torus, 0, (void **)&faces)))
5230 torus->lpVtbl->UnlockVertexBuffer(torus);
5231 torus->lpVtbl->Release(torus);
5232 return hr;
5235 phi_step = D3DX_PI / sides * 2.0f;
5236 theta_step = D3DX_PI / rings * -2.0f;
5238 theta = 0.0f;
5240 for (i = 0; i < rings; ++i)
5242 phi = 0.0f;
5244 sin_theta = sinf(theta);
5245 cos_theta = cosf(theta);
5247 for (j = 0; j < sides; ++j)
5249 sin_phi = sinf(phi);
5250 cos_phi = cosf(phi);
5252 vertices[i * sides + j].position.x = (innerradius * cos_phi + outerradius) * cos_theta;
5253 vertices[i * sides + j].position.y = (innerradius * cos_phi + outerradius) * sin_theta;
5254 vertices[i * sides + j].position.z = innerradius * sin_phi;
5255 vertices[i * sides + j].normal.x = cos_phi * cos_theta;
5256 vertices[i * sides + j].normal.y = cos_phi * sin_theta;
5257 vertices[i * sides + j].normal.z = sin_phi;
5259 phi += phi_step;
5262 theta += theta_step;
5265 for (i = 0; i < numfaces - sides * 2; ++i)
5267 faces[i][0] = i % 2 ? i / 2 + sides : i / 2;
5268 faces[i][1] = (i / 2 + 1) % sides ? i / 2 + 1 : i / 2 + 1 - sides;
5269 faces[i][2] = (i + 1) % (sides * 2) ? (i + 1) / 2 + sides : (i + 1) / 2;
5272 for (j = 0; i < numfaces; ++i, ++j)
5274 faces[i][0] = i % 2 ? j / 2 : i / 2;
5275 faces[i][1] = (i / 2 + 1) % sides ? i / 2 + 1 : i / 2 + 1 - sides;
5276 faces[i][2] = i == numfaces - 1 ? 0 : (j + 1) / 2;
5279 torus->lpVtbl->UnlockIndexBuffer(torus);
5280 torus->lpVtbl->UnlockVertexBuffer(torus);
5282 if (adjacency)
5284 if (FAILED(hr = D3DXCreateBuffer(numfaces * sizeof(DWORD) * 3, adjacency)))
5286 torus->lpVtbl->Release(torus);
5287 return hr;
5290 if (FAILED(hr = torus->lpVtbl->GenerateAdjacency(torus, 0.0f, (*adjacency)->lpVtbl->GetBufferPointer(*adjacency))))
5292 (*adjacency)->lpVtbl->Release(*adjacency);
5293 torus->lpVtbl->Release(torus);
5294 return hr;
5298 *mesh = torus;
5300 return D3D_OK;
5303 enum pointtype {
5304 POINTTYPE_CURVE = 0,
5305 POINTTYPE_CORNER,
5306 POINTTYPE_CURVE_START,
5307 POINTTYPE_CURVE_END,
5308 POINTTYPE_CURVE_MIDDLE,
5311 struct point2d
5313 D3DXVECTOR2 pos;
5314 enum pointtype corner;
5317 struct dynamic_array
5319 int count, capacity;
5320 void *items;
5323 /* is a dynamic_array */
5324 struct outline
5326 int count, capacity;
5327 struct point2d *items;
5330 /* is a dynamic_array */
5331 struct outline_array
5333 int count, capacity;
5334 struct outline *items;
5337 struct face_array
5339 int count;
5340 face *items;
5343 struct point2d_index
5345 struct outline *outline;
5346 int vertex;
5349 struct point2d_index_array
5351 int count;
5352 struct point2d_index *items;
5355 struct glyphinfo
5357 struct outline_array outlines;
5358 struct face_array faces;
5359 struct point2d_index_array ordered_vertices;
5360 float offset_x;
5363 /* is an dynamic_array */
5364 struct word_array
5366 int count, capacity;
5367 WORD *items;
5370 /* complex polygons are split into monotone polygons, which have
5371 * at most 2 intersections with the vertical sweep line */
5372 struct triangulation
5374 struct word_array vertex_stack;
5375 BOOL last_on_top, merging;
5378 /* is an dynamic_array */
5379 struct triangulation_array
5381 int count, capacity;
5382 struct triangulation *items;
5384 struct glyphinfo *glyph;
5387 static BOOL reserve(struct dynamic_array *array, int count, int itemsize)
5389 if (count > array->capacity) {
5390 void *new_buffer;
5391 int new_capacity;
5392 if (array->items && array->capacity) {
5393 new_capacity = max(array->capacity * 2, count);
5394 new_buffer = HeapReAlloc(GetProcessHeap(), 0, array->items, new_capacity * itemsize);
5395 } else {
5396 new_capacity = max(16, count);
5397 new_buffer = HeapAlloc(GetProcessHeap(), 0, new_capacity * itemsize);
5399 if (!new_buffer)
5400 return FALSE;
5401 array->items = new_buffer;
5402 array->capacity = new_capacity;
5404 return TRUE;
5407 static struct point2d *add_points(struct outline *array, int num)
5409 struct point2d *item;
5411 if (!reserve((struct dynamic_array *)array, array->count + num, sizeof(array->items[0])))
5412 return NULL;
5414 item = &array->items[array->count];
5415 array->count += num;
5416 return item;
5419 static struct outline *add_outline(struct outline_array *array)
5421 struct outline *item;
5423 if (!reserve((struct dynamic_array *)array, array->count + 1, sizeof(array->items[0])))
5424 return NULL;
5426 item = &array->items[array->count++];
5427 ZeroMemory(item, sizeof(*item));
5428 return item;
5431 static inline face *add_face(struct face_array *array)
5433 return &array->items[array->count++];
5436 static struct triangulation *add_triangulation(struct triangulation_array *array)
5438 struct triangulation *item;
5440 if (!reserve((struct dynamic_array *)array, array->count + 1, sizeof(array->items[0])))
5441 return NULL;
5443 item = &array->items[array->count++];
5444 ZeroMemory(item, sizeof(*item));
5445 return item;
5448 static HRESULT add_vertex_index(struct word_array *array, WORD vertex_index)
5450 if (!reserve((struct dynamic_array *)array, array->count + 1, sizeof(array->items[0])))
5451 return E_OUTOFMEMORY;
5453 array->items[array->count++] = vertex_index;
5454 return S_OK;
5457 /* assume fixed point numbers can be converted to float point in place */
5458 C_ASSERT(sizeof(FIXED) == sizeof(float));
5459 C_ASSERT(sizeof(POINTFX) == sizeof(D3DXVECTOR2));
5461 static inline D3DXVECTOR2 *convert_fixed_to_float(POINTFX *pt, int count, unsigned int emsquare)
5463 D3DXVECTOR2 *ret = (D3DXVECTOR2*)pt;
5464 while (count--) {
5465 D3DXVECTOR2 *pt_flt = (D3DXVECTOR2*)pt;
5466 pt_flt->x = (pt->x.value + pt->x.fract / (float)0x10000) / emsquare;
5467 pt_flt->y = (pt->y.value + pt->y.fract / (float)0x10000) / emsquare;
5468 pt++;
5470 return ret;
5473 static HRESULT add_bezier_points(struct outline *outline, const D3DXVECTOR2 *p1,
5474 const D3DXVECTOR2 *p2, const D3DXVECTOR2 *p3,
5475 float max_deviation_sq)
5477 D3DXVECTOR2 split1 = {0, 0}, split2 = {0, 0}, middle, vec;
5478 float deviation_sq;
5480 D3DXVec2Scale(&split1, D3DXVec2Add(&split1, p1, p2), 0.5f);
5481 D3DXVec2Scale(&split2, D3DXVec2Add(&split2, p2, p3), 0.5f);
5482 D3DXVec2Scale(&middle, D3DXVec2Add(&middle, &split1, &split2), 0.5f);
5484 deviation_sq = D3DXVec2LengthSq(D3DXVec2Subtract(&vec, &middle, p2));
5485 if (deviation_sq < max_deviation_sq) {
5486 struct point2d *pt = add_points(outline, 1);
5487 if (!pt) return E_OUTOFMEMORY;
5488 pt->pos = *p2;
5489 pt->corner = POINTTYPE_CURVE;
5490 /* the end point is omitted because the end line merges into the next segment of
5491 * the split bezier curve, and the end of the split bezier curve is added outside
5492 * this recursive function. */
5493 } else {
5494 HRESULT hr = add_bezier_points(outline, p1, &split1, &middle, max_deviation_sq);
5495 if (hr != S_OK) return hr;
5496 hr = add_bezier_points(outline, &middle, &split2, p3, max_deviation_sq);
5497 if (hr != S_OK) return hr;
5500 return S_OK;
5503 static inline BOOL is_direction_similar(D3DXVECTOR2 *dir1, D3DXVECTOR2 *dir2, float cos_theta)
5505 /* dot product = cos(theta) */
5506 return D3DXVec2Dot(dir1, dir2) > cos_theta;
5509 static inline D3DXVECTOR2 *unit_vec2(D3DXVECTOR2 *dir, const D3DXVECTOR2 *pt1, const D3DXVECTOR2 *pt2)
5511 return D3DXVec2Normalize(D3DXVec2Subtract(dir, pt2, pt1), dir);
5514 struct cos_table
5516 float cos_half;
5517 float cos_45;
5518 float cos_90;
5521 static BOOL attempt_line_merge(struct outline *outline,
5522 int pt_index,
5523 const D3DXVECTOR2 *nextpt,
5524 BOOL to_curve,
5525 const struct cos_table *table)
5527 D3DXVECTOR2 curdir, lastdir;
5528 struct point2d *prevpt, *pt;
5529 BOOL ret = FALSE;
5531 pt = &outline->items[pt_index];
5532 pt_index = (pt_index - 1 + outline->count) % outline->count;
5533 prevpt = &outline->items[pt_index];
5535 if (to_curve)
5536 pt->corner = pt->corner != POINTTYPE_CORNER ? POINTTYPE_CURVE_MIDDLE : POINTTYPE_CURVE_START;
5538 if (outline->count < 2)
5539 return FALSE;
5541 /* remove last point if the next line continues the last line */
5542 unit_vec2(&lastdir, &prevpt->pos, &pt->pos);
5543 unit_vec2(&curdir, &pt->pos, nextpt);
5544 if (is_direction_similar(&lastdir, &curdir, table->cos_half))
5546 outline->count--;
5547 if (pt->corner == POINTTYPE_CURVE_END)
5548 prevpt->corner = pt->corner;
5549 if (prevpt->corner == POINTTYPE_CURVE_END && to_curve)
5550 prevpt->corner = POINTTYPE_CURVE_MIDDLE;
5551 pt = prevpt;
5553 ret = TRUE;
5554 if (outline->count < 2)
5555 return ret;
5557 pt_index = (pt_index - 1 + outline->count) % outline->count;
5558 prevpt = &outline->items[pt_index];
5559 unit_vec2(&lastdir, &prevpt->pos, &pt->pos);
5560 unit_vec2(&curdir, &pt->pos, nextpt);
5562 return ret;
5565 static HRESULT create_outline(struct glyphinfo *glyph, void *raw_outline, int datasize,
5566 float max_deviation_sq, unsigned int emsquare,
5567 const struct cos_table *cos_table)
5569 TTPOLYGONHEADER *header = (TTPOLYGONHEADER *)raw_outline;
5571 while ((char *)header < (char *)raw_outline + datasize)
5573 TTPOLYCURVE *curve = (TTPOLYCURVE *)(header + 1);
5574 struct point2d *lastpt, *pt;
5575 D3DXVECTOR2 lastdir;
5576 D3DXVECTOR2 *pt_flt;
5577 int j;
5578 struct outline *outline = add_outline(&glyph->outlines);
5580 if (!outline)
5581 return E_OUTOFMEMORY;
5583 pt = add_points(outline, 1);
5584 if (!pt)
5585 return E_OUTOFMEMORY;
5586 pt_flt = convert_fixed_to_float(&header->pfxStart, 1, emsquare);
5587 pt->pos = *pt_flt;
5588 pt->corner = POINTTYPE_CORNER;
5590 if (header->dwType != TT_POLYGON_TYPE)
5591 FIXME("Unknown header type %d\n", header->dwType);
5593 while ((char *)curve < (char *)header + header->cb)
5595 D3DXVECTOR2 bezier_start = outline->items[outline->count - 1].pos;
5596 BOOL to_curve = curve->wType != TT_PRIM_LINE && curve->cpfx > 1;
5597 unsigned int j2 = 0;
5599 if (!curve->cpfx) {
5600 curve = (TTPOLYCURVE *)&curve->apfx[curve->cpfx];
5601 continue;
5604 pt_flt = convert_fixed_to_float(curve->apfx, curve->cpfx, emsquare);
5606 attempt_line_merge(outline, outline->count - 1, &pt_flt[0], to_curve, cos_table);
5608 if (to_curve)
5610 HRESULT hr;
5611 int count = curve->cpfx;
5613 while (count > 2)
5615 D3DXVECTOR2 bezier_end;
5617 D3DXVec2Scale(&bezier_end, D3DXVec2Add(&bezier_end, &pt_flt[j2], &pt_flt[j2+1]), 0.5f);
5618 hr = add_bezier_points(outline, &bezier_start, &pt_flt[j2], &bezier_end, max_deviation_sq);
5619 if (hr != S_OK)
5620 return hr;
5621 bezier_start = bezier_end;
5622 count--;
5623 j2++;
5625 hr = add_bezier_points(outline, &bezier_start, &pt_flt[j2], &pt_flt[j2+1], max_deviation_sq);
5626 if (hr != S_OK)
5627 return hr;
5629 pt = add_points(outline, 1);
5630 if (!pt)
5631 return E_OUTOFMEMORY;
5632 j2++;
5633 pt->pos = pt_flt[j2];
5634 pt->corner = POINTTYPE_CURVE_END;
5635 } else {
5636 pt = add_points(outline, curve->cpfx);
5637 if (!pt)
5638 return E_OUTOFMEMORY;
5639 for (j2 = 0; j2 < curve->cpfx; j2++)
5641 pt->pos = pt_flt[j2];
5642 pt->corner = POINTTYPE_CORNER;
5643 pt++;
5647 curve = (TTPOLYCURVE *)&curve->apfx[curve->cpfx];
5650 /* remove last point if the next line continues the last line */
5651 if (outline->count >= 3) {
5652 BOOL to_curve;
5654 lastpt = &outline->items[outline->count - 1];
5655 pt = &outline->items[0];
5656 if (pt->pos.x == lastpt->pos.x && pt->pos.y == lastpt->pos.y) {
5657 if (lastpt->corner == POINTTYPE_CURVE_END)
5659 if (pt->corner == POINTTYPE_CURVE_START)
5660 pt->corner = POINTTYPE_CURVE_MIDDLE;
5661 else
5662 pt->corner = POINTTYPE_CURVE_END;
5664 outline->count--;
5665 } else {
5666 /* outline closed with a line from end to start point */
5667 attempt_line_merge(outline, outline->count - 1, &pt->pos, FALSE, cos_table);
5669 lastpt = &outline->items[0];
5670 to_curve = lastpt->corner != POINTTYPE_CORNER && lastpt->corner != POINTTYPE_CURVE_END;
5671 if (lastpt->corner == POINTTYPE_CURVE_START)
5672 lastpt->corner = POINTTYPE_CORNER;
5673 pt = &outline->items[1];
5674 if (attempt_line_merge(outline, 0, &pt->pos, to_curve, cos_table))
5675 *lastpt = outline->items[outline->count];
5678 lastpt = &outline->items[outline->count - 1];
5679 pt = &outline->items[0];
5680 unit_vec2(&lastdir, &lastpt->pos, &pt->pos);
5681 for (j = 0; j < outline->count; j++)
5683 D3DXVECTOR2 curdir;
5685 lastpt = pt;
5686 pt = &outline->items[(j + 1) % outline->count];
5687 unit_vec2(&curdir, &lastpt->pos, &pt->pos);
5689 switch (lastpt->corner)
5691 case POINTTYPE_CURVE_START:
5692 case POINTTYPE_CURVE_END:
5693 if (!is_direction_similar(&lastdir, &curdir, cos_table->cos_45))
5694 lastpt->corner = POINTTYPE_CORNER;
5695 break;
5696 case POINTTYPE_CURVE_MIDDLE:
5697 if (!is_direction_similar(&lastdir, &curdir, cos_table->cos_90))
5698 lastpt->corner = POINTTYPE_CORNER;
5699 else
5700 lastpt->corner = POINTTYPE_CURVE;
5701 break;
5702 default:
5703 break;
5705 lastdir = curdir;
5708 header = (TTPOLYGONHEADER *)((char *)header + header->cb);
5710 return S_OK;
5713 /* Get the y-distance from a line to a point */
5714 static float get_line_to_point_y_distance(D3DXVECTOR2 *line_pt1,
5715 D3DXVECTOR2 *line_pt2,
5716 D3DXVECTOR2 *point)
5718 D3DXVECTOR2 line_vec = {0, 0};
5719 float line_pt_dx;
5720 float line_y;
5722 D3DXVec2Subtract(&line_vec, line_pt2, line_pt1);
5723 line_pt_dx = point->x - line_pt1->x;
5724 line_y = line_pt1->y + (line_vec.y * line_pt_dx) / line_vec.x;
5725 return point->y - line_y;
5728 static D3DXVECTOR2 *get_indexed_point(struct point2d_index *pt_idx)
5730 return &pt_idx->outline->items[pt_idx->vertex].pos;
5733 static D3DXVECTOR2 *get_ordered_vertex(struct glyphinfo *glyph, WORD index)
5735 return get_indexed_point(&glyph->ordered_vertices.items[index]);
5738 static void remove_triangulation(struct triangulation_array *array, struct triangulation *item)
5740 HeapFree(GetProcessHeap(), 0, item->vertex_stack.items);
5741 MoveMemory(item, item + 1, (char*)&array->items[array->count] - (char*)(item + 1));
5742 array->count--;
5745 static HRESULT triangulation_add_point(struct triangulation **t_ptr,
5746 struct triangulation_array *triangulations,
5747 WORD vtx_idx,
5748 BOOL to_top)
5750 struct glyphinfo *glyph = triangulations->glyph;
5751 struct triangulation *t = *t_ptr;
5752 HRESULT hr;
5753 face *face;
5754 int f1, f2;
5756 if (t->last_on_top) {
5757 f1 = 1;
5758 f2 = 2;
5759 } else {
5760 f1 = 2;
5761 f2 = 1;
5764 if (t->last_on_top != to_top && t->vertex_stack.count > 1) {
5765 /* consume all vertices on the stack */
5766 WORD last_pt = t->vertex_stack.items[0];
5767 int i;
5768 for (i = 1; i < t->vertex_stack.count; i++)
5770 face = add_face(&glyph->faces);
5771 if (!face) return E_OUTOFMEMORY;
5772 (*face)[0] = vtx_idx;
5773 (*face)[f1] = last_pt;
5774 (*face)[f2] = last_pt = t->vertex_stack.items[i];
5776 t->vertex_stack.items[0] = last_pt;
5777 t->vertex_stack.count = 1;
5778 } else if (t->vertex_stack.count > 1) {
5779 int i = t->vertex_stack.count - 1;
5780 D3DXVECTOR2 *point = get_ordered_vertex(glyph, vtx_idx);
5781 WORD top_idx = t->vertex_stack.items[i--];
5782 D3DXVECTOR2 *top_pt = get_ordered_vertex(glyph, top_idx);
5784 while (i >= 0)
5786 WORD prev_idx = t->vertex_stack.items[i--];
5787 D3DXVECTOR2 *prev_pt = get_ordered_vertex(glyph, prev_idx);
5789 if (prev_pt->x != top_pt->x &&
5790 ((to_top && get_line_to_point_y_distance(prev_pt, top_pt, point) > 0) ||
5791 (!to_top && get_line_to_point_y_distance(prev_pt, top_pt, point) < 0)))
5792 break;
5794 face = add_face(&glyph->faces);
5795 if (!face) return E_OUTOFMEMORY;
5796 (*face)[0] = vtx_idx;
5797 (*face)[f1] = prev_idx;
5798 (*face)[f2] = top_idx;
5800 top_pt = prev_pt;
5801 top_idx = prev_idx;
5802 t->vertex_stack.count--;
5805 t->last_on_top = to_top;
5807 hr = add_vertex_index(&t->vertex_stack, vtx_idx);
5809 if (hr == S_OK && t->merging) {
5810 struct triangulation *t2;
5812 t2 = to_top ? t - 1 : t + 1;
5813 t2->merging = FALSE;
5814 hr = triangulation_add_point(&t2, triangulations, vtx_idx, to_top);
5815 if (hr != S_OK) return hr;
5816 remove_triangulation(triangulations, t);
5817 if (t2 > t)
5818 t2--;
5819 *t_ptr = t2;
5821 return hr;
5824 /* check if the point is next on the outline for either the top or bottom */
5825 static D3DXVECTOR2 *triangulation_get_next_point(struct triangulation *t, struct glyphinfo *glyph, BOOL on_top)
5827 int i = t->last_on_top == on_top ? t->vertex_stack.count - 1 : 0;
5828 WORD idx = t->vertex_stack.items[i];
5829 struct point2d_index *pt_idx = &glyph->ordered_vertices.items[idx];
5830 struct outline *outline = pt_idx->outline;
5832 if (on_top)
5833 i = (pt_idx->vertex + outline->count - 1) % outline->count;
5834 else
5835 i = (pt_idx->vertex + 1) % outline->count;
5837 return &outline->items[i].pos;
5840 static int __cdecl compare_vertex_indices(const void *a, const void *b)
5842 const struct point2d_index *idx1 = a, *idx2 = b;
5843 const D3DXVECTOR2 *p1 = &idx1->outline->items[idx1->vertex].pos;
5844 const D3DXVECTOR2 *p2 = &idx2->outline->items[idx2->vertex].pos;
5845 float diff = p1->x - p2->x;
5847 if (diff == 0.0f)
5848 diff = p1->y - p2->y;
5850 return diff == 0.0f ? 0 : (diff > 0.0f ? -1 : 1);
5853 static HRESULT triangulate(struct triangulation_array *triangulations)
5855 int sweep_idx;
5856 HRESULT hr;
5857 struct glyphinfo *glyph = triangulations->glyph;
5858 int nb_vertices = 0;
5859 int i;
5860 struct point2d_index *idx_ptr;
5862 /* Glyphs without outlines do not generate any vertices. */
5863 if (!glyph->outlines.count)
5864 return D3D_OK;
5866 for (i = 0; i < glyph->outlines.count; i++)
5867 nb_vertices += glyph->outlines.items[i].count;
5869 glyph->ordered_vertices.items = HeapAlloc(GetProcessHeap(), 0,
5870 nb_vertices * sizeof(*glyph->ordered_vertices.items));
5871 if (!glyph->ordered_vertices.items)
5872 return E_OUTOFMEMORY;
5874 idx_ptr = glyph->ordered_vertices.items;
5875 for (i = 0; i < glyph->outlines.count; i++)
5877 struct outline *outline = &glyph->outlines.items[i];
5878 int j;
5880 idx_ptr->outline = outline;
5881 idx_ptr->vertex = 0;
5882 idx_ptr++;
5883 for (j = outline->count - 1; j > 0; j--)
5885 idx_ptr->outline = outline;
5886 idx_ptr->vertex = j;
5887 idx_ptr++;
5890 glyph->ordered_vertices.count = nb_vertices;
5892 /* Native implementation seems to try to create a triangle fan from
5893 * the first outline point if the glyph only has one outline. */
5894 if (glyph->outlines.count == 1)
5896 struct outline *outline = glyph->outlines.items;
5897 D3DXVECTOR2 *base = &outline->items[0].pos;
5898 D3DXVECTOR2 *last = &outline->items[1].pos;
5899 float ccw = 0;
5901 for (i = 2; i < outline->count; i++)
5903 D3DXVECTOR2 *next = &outline->items[i].pos;
5904 D3DXVECTOR2 v1 = {0.0f, 0.0f};
5905 D3DXVECTOR2 v2 = {0.0f, 0.0f};
5907 D3DXVec2Subtract(&v1, base, last);
5908 D3DXVec2Subtract(&v2, last, next);
5909 ccw = D3DXVec2CCW(&v1, &v2);
5910 if (ccw > 0.0f)
5911 break;
5913 last = next;
5915 if (ccw <= 0)
5917 glyph->faces.items = HeapAlloc(GetProcessHeap(), 0,
5918 (outline->count - 2) * sizeof(glyph->faces.items[0]));
5919 if (!glyph->faces.items)
5920 return E_OUTOFMEMORY;
5922 glyph->faces.count = outline->count - 2;
5923 for (i = 0; i < glyph->faces.count; i++)
5925 glyph->faces.items[i][0] = 0;
5926 glyph->faces.items[i][1] = i + 1;
5927 glyph->faces.items[i][2] = i + 2;
5929 return S_OK;
5933 /* Perform 2D polygon triangulation for complex glyphs.
5934 * Triangulation is performed using a sweep line concept, from right to left,
5935 * by processing vertices in sorted order. Complex polygons are split into
5936 * monotone polygons which are triangulated separately. */
5937 /* FIXME: The order of the faces is not consistent with the native implementation. */
5939 /* Reserve space for maximum possible faces from triangulation.
5940 * # faces for outer outlines = outline->count - 2
5941 * # faces for inner outlines = outline->count + 2
5942 * There must be at least 1 outer outline. */
5943 glyph->faces.items = HeapAlloc(GetProcessHeap(), 0,
5944 (nb_vertices + glyph->outlines.count * 2 - 4) * sizeof(glyph->faces.items[0]));
5945 if (!glyph->faces.items)
5946 return E_OUTOFMEMORY;
5948 qsort(glyph->ordered_vertices.items, nb_vertices,
5949 sizeof(glyph->ordered_vertices.items[0]), compare_vertex_indices);
5950 for (sweep_idx = 0; sweep_idx < glyph->ordered_vertices.count; sweep_idx++)
5952 int start = 0;
5953 int end = triangulations->count;
5955 while (start < end)
5957 D3DXVECTOR2 *sweep_vtx = get_ordered_vertex(glyph, sweep_idx);
5958 int current = (start + end) / 2;
5959 struct triangulation *t = &triangulations->items[current];
5960 BOOL on_top_outline = FALSE;
5961 D3DXVECTOR2 *top_next, *bottom_next;
5962 WORD top_idx, bottom_idx;
5964 if (t->merging && t->last_on_top)
5965 top_next = triangulation_get_next_point(t + 1, glyph, TRUE);
5966 else
5967 top_next = triangulation_get_next_point(t, glyph, TRUE);
5968 if (sweep_vtx == top_next)
5970 if (t->merging && t->last_on_top)
5971 t++;
5972 hr = triangulation_add_point(&t, triangulations, sweep_idx, TRUE);
5973 if (hr != S_OK) return hr;
5975 if (t + 1 < &triangulations->items[triangulations->count] &&
5976 triangulation_get_next_point(t + 1, glyph, FALSE) == sweep_vtx)
5978 /* point also on bottom outline of higher triangulation */
5979 struct triangulation *t2 = t + 1;
5980 hr = triangulation_add_point(&t2, triangulations, sweep_idx, FALSE);
5981 if (hr != S_OK) return hr;
5983 t->merging = TRUE;
5984 t2->merging = TRUE;
5986 on_top_outline = TRUE;
5989 if (t->merging && !t->last_on_top)
5990 bottom_next = triangulation_get_next_point(t - 1, glyph, FALSE);
5991 else
5992 bottom_next = triangulation_get_next_point(t, glyph, FALSE);
5993 if (sweep_vtx == bottom_next)
5995 if (t->merging && !t->last_on_top)
5996 t--;
5997 if (on_top_outline) {
5998 /* outline finished */
5999 remove_triangulation(triangulations, t);
6000 break;
6003 hr = triangulation_add_point(&t, triangulations, sweep_idx, FALSE);
6004 if (hr != S_OK) return hr;
6006 if (t > triangulations->items &&
6007 triangulation_get_next_point(t - 1, glyph, TRUE) == sweep_vtx)
6009 struct triangulation *t2 = t - 1;
6010 /* point also on top outline of lower triangulation */
6011 hr = triangulation_add_point(&t2, triangulations, sweep_idx, TRUE);
6012 if (hr != S_OK) return hr;
6013 t = t2 + 1; /* t may be invalidated by triangulation merging */
6015 t->merging = TRUE;
6016 t2->merging = TRUE;
6018 break;
6020 if (on_top_outline)
6021 break;
6023 if (t->last_on_top) {
6024 top_idx = t->vertex_stack.items[t->vertex_stack.count - 1];
6025 bottom_idx = t->vertex_stack.items[0];
6026 } else {
6027 top_idx = t->vertex_stack.items[0];
6028 bottom_idx = t->vertex_stack.items[t->vertex_stack.count - 1];
6031 /* check if the point is inside or outside this polygon */
6032 if (get_line_to_point_y_distance(get_ordered_vertex(glyph, top_idx),
6033 top_next, sweep_vtx) > 0)
6034 { /* above */
6035 start = current + 1;
6036 } else if (get_line_to_point_y_distance(get_ordered_vertex(glyph, bottom_idx),
6037 bottom_next, sweep_vtx) < 0)
6038 { /* below */
6039 end = current;
6040 } else if (t->merging) {
6041 /* inside, so cancel merging */
6042 struct triangulation *t2 = t->last_on_top ? t + 1 : t - 1;
6043 t->merging = FALSE;
6044 t2->merging = FALSE;
6045 hr = triangulation_add_point(&t, triangulations, sweep_idx, t->last_on_top);
6046 if (hr != S_OK) return hr;
6047 hr = triangulation_add_point(&t2, triangulations, sweep_idx, t2->last_on_top);
6048 if (hr != S_OK) return hr;
6049 break;
6050 } else {
6051 /* inside, so split polygon into two monotone parts */
6052 struct triangulation *t2 = add_triangulation(triangulations);
6053 if (!t2) return E_OUTOFMEMORY;
6054 MoveMemory(t + 1, t, (char*)(t2 + 1) - (char*)t);
6055 if (t->last_on_top) {
6056 t2 = t + 1;
6057 } else {
6058 t2 = t;
6059 t++;
6062 ZeroMemory(&t2->vertex_stack, sizeof(t2->vertex_stack));
6063 hr = add_vertex_index(&t2->vertex_stack, t->vertex_stack.items[t->vertex_stack.count - 1]);
6064 if (hr != S_OK) return hr;
6065 hr = add_vertex_index(&t2->vertex_stack, sweep_idx);
6066 if (hr != S_OK) return hr;
6067 t2->last_on_top = !t->last_on_top;
6069 hr = triangulation_add_point(&t, triangulations, sweep_idx, t->last_on_top);
6070 if (hr != S_OK) return hr;
6071 break;
6074 if (start >= end)
6076 struct triangulation *t;
6077 struct triangulation *t2 = add_triangulation(triangulations);
6078 if (!t2) return E_OUTOFMEMORY;
6079 t = &triangulations->items[start];
6080 MoveMemory(t + 1, t, (char*)(t2 + 1) - (char*)t);
6081 ZeroMemory(t, sizeof(*t));
6082 hr = add_vertex_index(&t->vertex_stack, sweep_idx);
6083 if (hr != S_OK) return hr;
6086 return S_OK;
6089 HRESULT WINAPI D3DXCreateTextW(struct IDirect3DDevice9 *device, HDC hdc, const WCHAR *text, float deviation,
6090 float extrusion, struct ID3DXMesh **mesh_ptr, struct ID3DXBuffer **adjacency, GLYPHMETRICSFLOAT *glyphmetrics)
6092 HRESULT hr;
6093 ID3DXMesh *mesh = NULL;
6094 DWORD nb_vertices, nb_faces;
6095 DWORD nb_front_faces, nb_corners, nb_outline_points;
6096 struct vertex *vertices = NULL;
6097 face *faces = NULL;
6098 int textlen = 0;
6099 float offset_x;
6100 LOGFONTW lf;
6101 OUTLINETEXTMETRICW otm;
6102 HFONT font = NULL, oldfont = NULL;
6103 const MAT2 identity = {{0, 1}, {0, 0}, {0, 0}, {0, 1}};
6104 void *raw_outline = NULL;
6105 int bufsize = 0;
6106 struct glyphinfo *glyphs = NULL;
6107 GLYPHMETRICS gm;
6108 struct triangulation_array triangulations = {0, 0, NULL};
6109 int i;
6110 struct vertex *vertex_ptr;
6111 face *face_ptr;
6112 float max_deviation_sq;
6113 const struct cos_table cos_table = {
6114 cosf(D3DXToRadian(0.5f)),
6115 cosf(D3DXToRadian(45.0f)),
6116 cosf(D3DXToRadian(90.0f)),
6118 int f1, f2;
6120 TRACE("(%p, %p, %s, %f, %f, %p, %p, %p)\n", device, hdc,
6121 debugstr_w(text), deviation, extrusion, mesh_ptr, adjacency, glyphmetrics);
6123 if (!device || !hdc || !text || !*text || deviation < 0.0f || extrusion < 0.0f || !mesh_ptr)
6124 return D3DERR_INVALIDCALL;
6126 if (adjacency)
6128 FIXME("Case of adjacency != NULL not implemented.\n");
6129 return E_NOTIMPL;
6132 if (!GetObjectW(GetCurrentObject(hdc, OBJ_FONT), sizeof(lf), &lf) ||
6133 !GetOutlineTextMetricsW(hdc, sizeof(otm), &otm))
6135 return D3DERR_INVALIDCALL;
6138 if (deviation == 0.0f)
6139 deviation = 1.0f / otm.otmEMSquare;
6140 max_deviation_sq = deviation * deviation;
6142 lf.lfHeight = otm.otmEMSquare;
6143 lf.lfWidth = 0;
6144 font = CreateFontIndirectW(&lf);
6145 if (!font) {
6146 hr = E_OUTOFMEMORY;
6147 goto error;
6149 oldfont = SelectObject(hdc, font);
6151 textlen = lstrlenW(text);
6152 for (i = 0; i < textlen; i++)
6154 int datasize = GetGlyphOutlineW(hdc, text[i], GGO_NATIVE, &gm, 0, NULL, &identity);
6155 if (datasize < 0)
6156 return D3DERR_INVALIDCALL;
6157 if (bufsize < datasize)
6158 bufsize = datasize;
6160 if (!bufsize) { /* e.g. text == " " */
6161 hr = D3DERR_INVALIDCALL;
6162 goto error;
6165 glyphs = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, textlen * sizeof(*glyphs));
6166 raw_outline = HeapAlloc(GetProcessHeap(), 0, bufsize);
6167 if (!glyphs || !raw_outline) {
6168 hr = E_OUTOFMEMORY;
6169 goto error;
6172 offset_x = 0.0f;
6173 for (i = 0; i < textlen; i++)
6175 /* get outline points from data returned from GetGlyphOutline */
6176 int datasize;
6178 glyphs[i].offset_x = offset_x;
6180 datasize = GetGlyphOutlineW(hdc, text[i], GGO_NATIVE, &gm, bufsize, raw_outline, &identity);
6181 hr = create_outline(&glyphs[i], raw_outline, datasize,
6182 max_deviation_sq, otm.otmEMSquare, &cos_table);
6183 if (hr != S_OK) goto error;
6185 triangulations.glyph = &glyphs[i];
6186 hr = triangulate(&triangulations);
6187 if (hr != S_OK) goto error;
6188 if (triangulations.count) {
6189 ERR("%d incomplete triangulations of glyph (%u).\n", triangulations.count, text[i]);
6190 triangulations.count = 0;
6193 if (glyphmetrics)
6195 glyphmetrics[i].gmfBlackBoxX = gm.gmBlackBoxX / (float)otm.otmEMSquare;
6196 glyphmetrics[i].gmfBlackBoxY = gm.gmBlackBoxY / (float)otm.otmEMSquare;
6197 glyphmetrics[i].gmfptGlyphOrigin.x = gm.gmptGlyphOrigin.x / (float)otm.otmEMSquare;
6198 glyphmetrics[i].gmfptGlyphOrigin.y = gm.gmptGlyphOrigin.y / (float)otm.otmEMSquare;
6199 glyphmetrics[i].gmfCellIncX = gm.gmCellIncX / (float)otm.otmEMSquare;
6200 glyphmetrics[i].gmfCellIncY = gm.gmCellIncY / (float)otm.otmEMSquare;
6202 offset_x += gm.gmCellIncX / (float)otm.otmEMSquare;
6205 /* corner points need an extra vertex for the different side faces normals */
6206 nb_corners = 0;
6207 nb_outline_points = 0;
6208 nb_front_faces = 0;
6209 for (i = 0; i < textlen; i++)
6211 int j;
6212 nb_outline_points += glyphs[i].ordered_vertices.count;
6213 nb_front_faces += glyphs[i].faces.count;
6214 for (j = 0; j < glyphs[i].outlines.count; j++)
6216 int k;
6217 struct outline *outline = &glyphs[i].outlines.items[j];
6218 nb_corners++; /* first outline point always repeated as a corner */
6219 for (k = 1; k < outline->count; k++)
6220 if (outline->items[k].corner)
6221 nb_corners++;
6225 nb_vertices = (nb_outline_points + nb_corners) * 2 + nb_outline_points * 2;
6226 nb_faces = nb_outline_points * 2 + nb_front_faces * 2;
6229 hr = D3DXCreateMeshFVF(nb_faces, nb_vertices, D3DXMESH_MANAGED,
6230 D3DFVF_XYZ | D3DFVF_NORMAL, device, &mesh);
6231 if (FAILED(hr))
6232 goto error;
6234 if (FAILED(hr = mesh->lpVtbl->LockVertexBuffer(mesh, 0, (void **)&vertices)))
6235 goto error;
6237 if (FAILED(hr = mesh->lpVtbl->LockIndexBuffer(mesh, 0, (void **)&faces)))
6238 goto error;
6240 /* convert 2D vertices and faces into 3D mesh */
6241 vertex_ptr = vertices;
6242 face_ptr = faces;
6243 if (extrusion == 0.0f) {
6244 f1 = 1;
6245 f2 = 2;
6246 } else {
6247 f1 = 2;
6248 f2 = 1;
6250 for (i = 0; i < textlen; i++)
6252 int j;
6253 int count;
6254 struct vertex *back_vertices;
6255 face *back_faces;
6257 /* side vertices and faces */
6258 for (j = 0; j < glyphs[i].outlines.count; j++)
6260 struct vertex *outline_vertices = vertex_ptr;
6261 struct outline *outline = &glyphs[i].outlines.items[j];
6262 int k;
6263 struct point2d *prevpt = &outline->items[outline->count - 1];
6264 struct point2d *pt = &outline->items[0];
6266 for (k = 1; k <= outline->count; k++)
6268 struct vertex vtx;
6269 struct point2d *nextpt = &outline->items[k % outline->count];
6270 WORD vtx_idx = vertex_ptr - vertices;
6271 D3DXVECTOR2 vec;
6273 if (pt->corner == POINTTYPE_CURVE_START)
6274 D3DXVec2Subtract(&vec, &pt->pos, &prevpt->pos);
6275 else if (pt->corner)
6276 D3DXVec2Subtract(&vec, &nextpt->pos, &pt->pos);
6277 else
6278 D3DXVec2Subtract(&vec, &nextpt->pos, &prevpt->pos);
6279 D3DXVec2Normalize(&vec, &vec);
6280 vtx.normal.x = -vec.y;
6281 vtx.normal.y = vec.x;
6282 vtx.normal.z = 0;
6284 vtx.position.x = pt->pos.x + glyphs[i].offset_x;
6285 vtx.position.y = pt->pos.y;
6286 vtx.position.z = 0;
6287 *vertex_ptr++ = vtx;
6289 vtx.position.z = -extrusion;
6290 *vertex_ptr++ = vtx;
6292 vtx.position.x = nextpt->pos.x + glyphs[i].offset_x;
6293 vtx.position.y = nextpt->pos.y;
6294 if (pt->corner && nextpt->corner && nextpt->corner != POINTTYPE_CURVE_END) {
6295 vtx.position.z = -extrusion;
6296 *vertex_ptr++ = vtx;
6297 vtx.position.z = 0;
6298 *vertex_ptr++ = vtx;
6300 (*face_ptr)[0] = vtx_idx;
6301 (*face_ptr)[1] = vtx_idx + 2;
6302 (*face_ptr)[2] = vtx_idx + 1;
6303 face_ptr++;
6305 (*face_ptr)[0] = vtx_idx;
6306 (*face_ptr)[1] = vtx_idx + 3;
6307 (*face_ptr)[2] = vtx_idx + 2;
6308 face_ptr++;
6309 } else {
6310 if (nextpt->corner) {
6311 if (nextpt->corner == POINTTYPE_CURVE_END) {
6312 D3DXVECTOR2 *nextpt2 = &outline->items[(k + 1) % outline->count].pos;
6313 D3DXVec2Subtract(&vec, nextpt2, &nextpt->pos);
6314 } else {
6315 D3DXVec2Subtract(&vec, &nextpt->pos, &pt->pos);
6317 D3DXVec2Normalize(&vec, &vec);
6318 vtx.normal.x = -vec.y;
6319 vtx.normal.y = vec.x;
6321 vtx.position.z = 0;
6322 *vertex_ptr++ = vtx;
6323 vtx.position.z = -extrusion;
6324 *vertex_ptr++ = vtx;
6327 (*face_ptr)[0] = vtx_idx;
6328 (*face_ptr)[1] = vtx_idx + 3;
6329 (*face_ptr)[2] = vtx_idx + 1;
6330 face_ptr++;
6332 (*face_ptr)[0] = vtx_idx;
6333 (*face_ptr)[1] = vtx_idx + 2;
6334 (*face_ptr)[2] = vtx_idx + 3;
6335 face_ptr++;
6338 prevpt = pt;
6339 pt = nextpt;
6341 if (!pt->corner) {
6342 *vertex_ptr++ = *outline_vertices++;
6343 *vertex_ptr++ = *outline_vertices++;
6347 /* back vertices and faces */
6348 back_faces = face_ptr;
6349 back_vertices = vertex_ptr;
6350 for (j = 0; j < glyphs[i].ordered_vertices.count; j++)
6352 D3DXVECTOR2 *pt = get_ordered_vertex(&glyphs[i], j);
6353 vertex_ptr->position.x = pt->x + glyphs[i].offset_x;
6354 vertex_ptr->position.y = pt->y;
6355 vertex_ptr->position.z = 0;
6356 vertex_ptr->normal.x = 0;
6357 vertex_ptr->normal.y = 0;
6358 vertex_ptr->normal.z = 1;
6359 vertex_ptr++;
6361 count = back_vertices - vertices;
6362 for (j = 0; j < glyphs[i].faces.count; j++)
6364 face *f = &glyphs[i].faces.items[j];
6365 (*face_ptr)[0] = (*f)[0] + count;
6366 (*face_ptr)[1] = (*f)[1] + count;
6367 (*face_ptr)[2] = (*f)[2] + count;
6368 face_ptr++;
6371 /* front vertices and faces */
6372 j = count = vertex_ptr - back_vertices;
6373 while (j--)
6375 vertex_ptr->position.x = back_vertices->position.x;
6376 vertex_ptr->position.y = back_vertices->position.y;
6377 vertex_ptr->position.z = -extrusion;
6378 vertex_ptr->normal.x = 0;
6379 vertex_ptr->normal.y = 0;
6380 vertex_ptr->normal.z = extrusion == 0.0f ? 1.0f : -1.0f;
6381 vertex_ptr++;
6382 back_vertices++;
6384 j = face_ptr - back_faces;
6385 while (j--)
6387 (*face_ptr)[0] = (*back_faces)[0] + count;
6388 (*face_ptr)[1] = (*back_faces)[f1] + count;
6389 (*face_ptr)[2] = (*back_faces)[f2] + count;
6390 face_ptr++;
6391 back_faces++;
6395 *mesh_ptr = mesh;
6396 hr = D3D_OK;
6397 error:
6398 if (mesh) {
6399 if (faces) mesh->lpVtbl->UnlockIndexBuffer(mesh);
6400 if (vertices) mesh->lpVtbl->UnlockVertexBuffer(mesh);
6401 if (hr != D3D_OK) mesh->lpVtbl->Release(mesh);
6403 if (glyphs) {
6404 for (i = 0; i < textlen; i++)
6406 int j;
6407 for (j = 0; j < glyphs[i].outlines.count; j++)
6408 HeapFree(GetProcessHeap(), 0, glyphs[i].outlines.items[j].items);
6409 HeapFree(GetProcessHeap(), 0, glyphs[i].outlines.items);
6410 HeapFree(GetProcessHeap(), 0, glyphs[i].faces.items);
6411 HeapFree(GetProcessHeap(), 0, glyphs[i].ordered_vertices.items);
6413 HeapFree(GetProcessHeap(), 0, glyphs);
6415 if (triangulations.items) {
6416 int i;
6417 for (i = 0; i < triangulations.count; i++)
6418 HeapFree(GetProcessHeap(), 0, triangulations.items[i].vertex_stack.items);
6419 HeapFree(GetProcessHeap(), 0, triangulations.items);
6421 HeapFree(GetProcessHeap(), 0, raw_outline);
6422 if (oldfont) SelectObject(hdc, oldfont);
6423 if (font) DeleteObject(font);
6425 return hr;
6428 HRESULT WINAPI D3DXValidMesh(ID3DXMesh *mesh, const DWORD *adjacency, ID3DXBuffer **errors_and_warnings)
6430 FIXME("(%p, %p, %p): stub\n", mesh, adjacency, *errors_and_warnings);
6432 return E_NOTIMPL;
6435 static BOOL weld_float1(void *to, void *from, FLOAT epsilon)
6437 FLOAT *v1 = to;
6438 FLOAT *v2 = from;
6440 if (fabsf(*v1 - *v2) <= epsilon)
6442 *v1 = *v2;
6444 return TRUE;
6447 return FALSE;
6450 static BOOL weld_float2(void *to, void *from, FLOAT epsilon)
6452 D3DXVECTOR2 *v1 = to;
6453 D3DXVECTOR2 *v2 = from;
6454 FLOAT diff_x = fabsf(v1->x - v2->x);
6455 FLOAT diff_y = fabsf(v1->y - v2->y);
6456 FLOAT max_abs_diff = max(diff_x, diff_y);
6458 if (max_abs_diff <= epsilon)
6460 memcpy(to, from, sizeof(D3DXVECTOR2));
6462 return TRUE;
6465 return FALSE;
6468 static BOOL weld_float3(void *to, void *from, FLOAT epsilon)
6470 D3DXVECTOR3 *v1 = to;
6471 D3DXVECTOR3 *v2 = from;
6472 FLOAT diff_x = fabsf(v1->x - v2->x);
6473 FLOAT diff_y = fabsf(v1->y - v2->y);
6474 FLOAT diff_z = fabsf(v1->z - v2->z);
6475 FLOAT max_abs_diff = max(diff_x, diff_y);
6476 max_abs_diff = max(diff_z, max_abs_diff);
6478 if (max_abs_diff <= epsilon)
6480 memcpy(to, from, sizeof(D3DXVECTOR3));
6482 return TRUE;
6485 return FALSE;
6488 static BOOL weld_float4(void *to, void *from, FLOAT epsilon)
6490 D3DXVECTOR4 *v1 = to;
6491 D3DXVECTOR4 *v2 = from;
6492 FLOAT diff_x = fabsf(v1->x - v2->x);
6493 FLOAT diff_y = fabsf(v1->y - v2->y);
6494 FLOAT diff_z = fabsf(v1->z - v2->z);
6495 FLOAT diff_w = fabsf(v1->w - v2->w);
6496 FLOAT max_abs_diff = max(diff_x, diff_y);
6497 max_abs_diff = max(diff_z, max_abs_diff);
6498 max_abs_diff = max(diff_w, max_abs_diff);
6500 if (max_abs_diff <= epsilon)
6502 memcpy(to, from, sizeof(D3DXVECTOR4));
6504 return TRUE;
6507 return FALSE;
6510 static BOOL weld_ubyte4(void *to, void *from, FLOAT epsilon)
6512 BYTE *b1 = to;
6513 BYTE *b2 = from;
6514 BYTE truncated_epsilon = (BYTE)epsilon;
6515 BYTE diff_x = b1[0] > b2[0] ? b1[0] - b2[0] : b2[0] - b1[0];
6516 BYTE diff_y = b1[1] > b2[1] ? b1[1] - b2[1] : b2[1] - b1[1];
6517 BYTE diff_z = b1[2] > b2[2] ? b1[2] - b2[2] : b2[2] - b1[2];
6518 BYTE diff_w = b1[3] > b2[3] ? b1[3] - b2[3] : b2[3] - b1[3];
6519 BYTE max_diff = max(diff_x, diff_y);
6520 max_diff = max(diff_z, max_diff);
6521 max_diff = max(diff_w, max_diff);
6523 if (max_diff <= truncated_epsilon)
6525 memcpy(to, from, 4 * sizeof(BYTE));
6527 return TRUE;
6530 return FALSE;
6533 static BOOL weld_ubyte4n(void *to, void *from, FLOAT epsilon)
6535 return weld_ubyte4(to, from, epsilon * UCHAR_MAX);
6538 static BOOL weld_d3dcolor(void *to, void *from, FLOAT epsilon)
6540 return weld_ubyte4n(to, from, epsilon);
6543 static BOOL weld_short2(void *to, void *from, FLOAT epsilon)
6545 SHORT *s1 = to;
6546 SHORT *s2 = from;
6547 SHORT truncated_epsilon = (SHORT)epsilon;
6548 SHORT diff_x = abs(s1[0] - s2[0]);
6549 SHORT diff_y = abs(s1[1] - s2[1]);
6550 SHORT max_abs_diff = max(diff_x, diff_y);
6552 if (max_abs_diff <= truncated_epsilon)
6554 memcpy(to, from, 2 * sizeof(SHORT));
6556 return TRUE;
6559 return FALSE;
6562 static BOOL weld_short2n(void *to, void *from, FLOAT epsilon)
6564 return weld_short2(to, from, epsilon * SHRT_MAX);
6567 static BOOL weld_short4(void *to, void *from, FLOAT epsilon)
6569 SHORT *s1 = to;
6570 SHORT *s2 = from;
6571 SHORT truncated_epsilon = (SHORT)epsilon;
6572 SHORT diff_x = abs(s1[0] - s2[0]);
6573 SHORT diff_y = abs(s1[1] - s2[1]);
6574 SHORT diff_z = abs(s1[2] - s2[2]);
6575 SHORT diff_w = abs(s1[3] - s2[3]);
6576 SHORT max_abs_diff = max(diff_x, diff_y);
6577 max_abs_diff = max(diff_z, max_abs_diff);
6578 max_abs_diff = max(diff_w, max_abs_diff);
6580 if (max_abs_diff <= truncated_epsilon)
6582 memcpy(to, from, 4 * sizeof(SHORT));
6584 return TRUE;
6587 return FALSE;
6590 static BOOL weld_short4n(void *to, void *from, FLOAT epsilon)
6592 return weld_short4(to, from, epsilon * SHRT_MAX);
6595 static BOOL weld_ushort2n(void *to, void *from, FLOAT epsilon)
6597 USHORT *s1 = to;
6598 USHORT *s2 = from;
6599 USHORT scaled_epsilon = (USHORT)(epsilon * USHRT_MAX);
6600 USHORT diff_x = s1[0] > s2[0] ? s1[0] - s2[0] : s2[0] - s1[0];
6601 USHORT diff_y = s1[1] > s2[1] ? s1[1] - s2[1] : s2[1] - s1[1];
6602 USHORT max_diff = max(diff_x, diff_y);
6604 if (max_diff <= scaled_epsilon)
6606 memcpy(to, from, 2 * sizeof(USHORT));
6608 return TRUE;
6611 return FALSE;
6614 static BOOL weld_ushort4n(void *to, void *from, FLOAT epsilon)
6616 USHORT *s1 = to;
6617 USHORT *s2 = from;
6618 USHORT scaled_epsilon = (USHORT)(epsilon * USHRT_MAX);
6619 USHORT diff_x = s1[0] > s2[0] ? s1[0] - s2[0] : s2[0] - s1[0];
6620 USHORT diff_y = s1[1] > s2[1] ? s1[1] - s2[1] : s2[1] - s1[1];
6621 USHORT diff_z = s1[2] > s2[2] ? s1[2] - s2[2] : s2[2] - s1[2];
6622 USHORT diff_w = s1[3] > s2[3] ? s1[3] - s2[3] : s2[3] - s1[3];
6623 USHORT max_diff = max(diff_x, diff_y);
6624 max_diff = max(diff_z, max_diff);
6625 max_diff = max(diff_w, max_diff);
6627 if (max_diff <= scaled_epsilon)
6629 memcpy(to, from, 4 * sizeof(USHORT));
6631 return TRUE;
6634 return FALSE;
6637 struct udec3
6639 UINT x;
6640 UINT y;
6641 UINT z;
6642 UINT w;
6645 static struct udec3 dword_to_udec3(DWORD d)
6647 struct udec3 v;
6649 v.x = d & 0x3ff;
6650 v.y = (d & 0xffc00) >> 10;
6651 v.z = (d & 0x3ff00000) >> 20;
6652 v.w = (d & 0xc0000000) >> 30;
6654 return v;
6657 static BOOL weld_udec3(void *to, void *from, FLOAT epsilon)
6659 DWORD *d1 = to;
6660 DWORD *d2 = from;
6661 struct udec3 v1 = dword_to_udec3(*d1);
6662 struct udec3 v2 = dword_to_udec3(*d2);
6663 UINT truncated_epsilon = (UINT)epsilon;
6664 UINT diff_x = v1.x > v2.x ? v1.x - v2.x : v2.x - v1.x;
6665 UINT diff_y = v1.y > v2.y ? v1.y - v2.y : v2.y - v1.y;
6666 UINT diff_z = v1.z > v2.z ? v1.z - v2.z : v2.z - v1.z;
6667 UINT diff_w = v1.w > v2.w ? v1.w - v2.w : v2.w - v1.w;
6668 UINT max_diff = max(diff_x, diff_y);
6669 max_diff = max(diff_z, max_diff);
6670 max_diff = max(diff_w, max_diff);
6672 if (max_diff <= truncated_epsilon)
6674 memcpy(to, from, sizeof(DWORD));
6676 return TRUE;
6679 return FALSE;
6682 struct dec3n
6684 INT x;
6685 INT y;
6686 INT z;
6687 INT w;
6690 static struct dec3n dword_to_dec3n(DWORD d)
6692 struct dec3n v;
6694 v.x = d & 0x3ff;
6695 v.y = (d & 0xffc00) >> 10;
6696 v.z = (d & 0x3ff00000) >> 20;
6697 v.w = (d & 0xc0000000) >> 30;
6699 return v;
6702 static BOOL weld_dec3n(void *to, void *from, FLOAT epsilon)
6704 const UINT MAX_DEC3N = 511;
6705 DWORD *d1 = to;
6706 DWORD *d2 = from;
6707 struct dec3n v1 = dword_to_dec3n(*d1);
6708 struct dec3n v2 = dword_to_dec3n(*d2);
6709 INT scaled_epsilon = (INT)(epsilon * MAX_DEC3N);
6710 INT diff_x = abs(v1.x - v2.x);
6711 INT diff_y = abs(v1.y - v2.y);
6712 INT diff_z = abs(v1.z - v2.z);
6713 INT diff_w = abs(v1.w - v2.w);
6714 INT max_abs_diff = max(diff_x, diff_y);
6715 max_abs_diff = max(diff_z, max_abs_diff);
6716 max_abs_diff = max(diff_w, max_abs_diff);
6718 if (max_abs_diff <= scaled_epsilon)
6720 memcpy(to, from, sizeof(DWORD));
6722 return TRUE;
6725 return FALSE;
6728 static BOOL weld_float16_2(void *to, void *from, FLOAT epsilon)
6730 D3DXFLOAT16 *v1_float16 = to;
6731 D3DXFLOAT16 *v2_float16 = from;
6732 FLOAT diff_x;
6733 FLOAT diff_y;
6734 FLOAT max_abs_diff;
6735 #define NUM_ELEM 2
6736 FLOAT v1[NUM_ELEM];
6737 FLOAT v2[NUM_ELEM];
6739 D3DXFloat16To32Array(v1, v1_float16, NUM_ELEM);
6740 D3DXFloat16To32Array(v2, v2_float16, NUM_ELEM);
6742 diff_x = fabsf(v1[0] - v2[0]);
6743 diff_y = fabsf(v1[1] - v2[1]);
6744 max_abs_diff = max(diff_x, diff_y);
6746 if (max_abs_diff <= epsilon)
6748 memcpy(to, from, NUM_ELEM * sizeof(D3DXFLOAT16));
6750 return TRUE;
6753 return FALSE;
6754 #undef NUM_ELEM
6757 static BOOL weld_float16_4(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 diff_z;
6764 FLOAT diff_w;
6765 FLOAT max_abs_diff;
6766 #define NUM_ELEM 4
6767 FLOAT v1[NUM_ELEM];
6768 FLOAT v2[NUM_ELEM];
6770 D3DXFloat16To32Array(v1, v1_float16, NUM_ELEM);
6771 D3DXFloat16To32Array(v2, v2_float16, NUM_ELEM);
6773 diff_x = fabsf(v1[0] - v2[0]);
6774 diff_y = fabsf(v1[1] - v2[1]);
6775 diff_z = fabsf(v1[2] - v2[2]);
6776 diff_w = fabsf(v1[3] - v2[3]);
6777 max_abs_diff = max(diff_x, diff_y);
6778 max_abs_diff = max(diff_z, max_abs_diff);
6779 max_abs_diff = max(diff_w, max_abs_diff);
6781 if (max_abs_diff <= epsilon)
6783 memcpy(to, from, NUM_ELEM * sizeof(D3DXFLOAT16));
6785 return TRUE;
6788 return FALSE;
6789 #undef NUM_ELEM
6792 /* Sets the vertex components to the same value if they are within epsilon. */
6793 static BOOL weld_component(void *to, void *from, D3DDECLTYPE type, FLOAT epsilon)
6795 /* Quiet FIXMEs as this is in a loop with potentially thousand of iterations. */
6796 BOOL fixme_once_unused = FALSE;
6797 BOOL fixme_once_unknown = FALSE;
6799 switch (type)
6801 case D3DDECLTYPE_FLOAT1:
6802 return weld_float1(to, from, epsilon);
6804 case D3DDECLTYPE_FLOAT2:
6805 return weld_float2(to, from, epsilon);
6807 case D3DDECLTYPE_FLOAT3:
6808 return weld_float3(to, from, epsilon);
6810 case D3DDECLTYPE_FLOAT4:
6811 return weld_float4(to, from, epsilon);
6813 case D3DDECLTYPE_D3DCOLOR:
6814 return weld_d3dcolor(to, from, epsilon);
6816 case D3DDECLTYPE_UBYTE4:
6817 return weld_ubyte4(to, from, epsilon);
6819 case D3DDECLTYPE_SHORT2:
6820 return weld_short2(to, from, epsilon);
6822 case D3DDECLTYPE_SHORT4:
6823 return weld_short4(to, from, epsilon);
6825 case D3DDECLTYPE_UBYTE4N:
6826 return weld_ubyte4n(to, from, epsilon);
6828 case D3DDECLTYPE_SHORT2N:
6829 return weld_short2n(to, from, epsilon);
6831 case D3DDECLTYPE_SHORT4N:
6832 return weld_short4n(to, from, epsilon);
6834 case D3DDECLTYPE_USHORT2N:
6835 return weld_ushort2n(to, from, epsilon);
6837 case D3DDECLTYPE_USHORT4N:
6838 return weld_ushort4n(to, from, epsilon);
6840 case D3DDECLTYPE_UDEC3:
6841 return weld_udec3(to, from, epsilon);
6843 case D3DDECLTYPE_DEC3N:
6844 return weld_dec3n(to, from, epsilon);
6846 case D3DDECLTYPE_FLOAT16_2:
6847 return weld_float16_2(to, from, epsilon);
6849 case D3DDECLTYPE_FLOAT16_4:
6850 return weld_float16_4(to, from, epsilon);
6852 case D3DDECLTYPE_UNUSED:
6853 if (!fixme_once_unused++)
6854 FIXME("D3DDECLTYPE_UNUSED welding not implemented.\n");
6855 break;
6857 default:
6858 if (!fixme_once_unknown++)
6859 FIXME("Welding of unknown declaration type %d is not implemented.\n", type);
6860 break;
6863 return FALSE;
6866 static FLOAT get_component_epsilon(const D3DVERTEXELEMENT9 *decl_ptr, const D3DXWELDEPSILONS *epsilons)
6868 FLOAT epsilon = 0.0f;
6869 /* Quiet FIXMEs as this is in a loop with potentially thousand of iterations. */
6870 static BOOL fixme_once_blendindices = FALSE;
6871 static BOOL fixme_once_positiont = FALSE;
6872 static BOOL fixme_once_fog = FALSE;
6873 static BOOL fixme_once_depth = FALSE;
6874 static BOOL fixme_once_sample = FALSE;
6875 static BOOL fixme_once_unknown = FALSE;
6877 switch (decl_ptr->Usage)
6879 case D3DDECLUSAGE_POSITION:
6880 epsilon = epsilons->Position;
6881 break;
6882 case D3DDECLUSAGE_BLENDWEIGHT:
6883 epsilon = epsilons->BlendWeights;
6884 break;
6885 case D3DDECLUSAGE_NORMAL:
6886 epsilon = epsilons->Normals;
6887 break;
6888 case D3DDECLUSAGE_PSIZE:
6889 epsilon = epsilons->PSize;
6890 break;
6891 case D3DDECLUSAGE_TEXCOORD:
6893 BYTE usage_index = decl_ptr->UsageIndex;
6894 if (usage_index > 7)
6895 usage_index = 7;
6896 epsilon = epsilons->Texcoords[usage_index];
6897 break;
6899 case D3DDECLUSAGE_TANGENT:
6900 epsilon = epsilons->Tangent;
6901 break;
6902 case D3DDECLUSAGE_BINORMAL:
6903 epsilon = epsilons->Binormal;
6904 break;
6905 case D3DDECLUSAGE_TESSFACTOR:
6906 epsilon = epsilons->TessFactor;
6907 break;
6908 case D3DDECLUSAGE_COLOR:
6909 if (decl_ptr->UsageIndex == 0)
6910 epsilon = epsilons->Diffuse;
6911 else if (decl_ptr->UsageIndex == 1)
6912 epsilon = epsilons->Specular;
6913 else
6914 epsilon = 1e-6f;
6915 break;
6916 case D3DDECLUSAGE_BLENDINDICES:
6917 if (!fixme_once_blendindices++)
6918 FIXME("D3DDECLUSAGE_BLENDINDICES welding not implemented.\n");
6919 break;
6920 case D3DDECLUSAGE_POSITIONT:
6921 if (!fixme_once_positiont++)
6922 FIXME("D3DDECLUSAGE_POSITIONT welding not implemented.\n");
6923 break;
6924 case D3DDECLUSAGE_FOG:
6925 if (!fixme_once_fog++)
6926 FIXME("D3DDECLUSAGE_FOG welding not implemented.\n");
6927 break;
6928 case D3DDECLUSAGE_DEPTH:
6929 if (!fixme_once_depth++)
6930 FIXME("D3DDECLUSAGE_DEPTH welding not implemented.\n");
6931 break;
6932 case D3DDECLUSAGE_SAMPLE:
6933 if (!fixme_once_sample++)
6934 FIXME("D3DDECLUSAGE_SAMPLE welding not implemented.\n");
6935 break;
6936 default:
6937 if (!fixme_once_unknown++)
6938 FIXME("Unknown usage %x\n", decl_ptr->Usage);
6939 break;
6942 return epsilon;
6945 /* Helper function for reading a 32-bit index buffer. */
6946 static inline DWORD read_ib(void *index_buffer, BOOL indices_are_32bit,
6947 DWORD index)
6949 if (indices_are_32bit)
6951 DWORD *indices = index_buffer;
6952 return indices[index];
6954 else
6956 WORD *indices = index_buffer;
6957 return indices[index];
6961 /* Helper function for writing to a 32-bit index buffer. */
6962 static inline void write_ib(void *index_buffer, BOOL indices_are_32bit,
6963 DWORD index, DWORD value)
6965 if (indices_are_32bit)
6967 DWORD *indices = index_buffer;
6968 indices[index] = value;
6970 else
6972 WORD *indices = index_buffer;
6973 indices[index] = value;
6977 /*************************************************************************
6978 * D3DXWeldVertices (D3DX9_36.@)
6980 * Welds together similar vertices. The similarity between vert-
6981 * ices can be the position and other components such as
6982 * normal and color.
6984 * PARAMS
6985 * mesh [I] Mesh which vertices will be welded together.
6986 * flags [I] D3DXWELDEPSILONSFLAGS specifying how to weld.
6987 * epsilons [I] How similar a component needs to be for welding.
6988 * adjacency [I] Which faces are adjacent to other faces.
6989 * adjacency_out [O] Updated adjacency after welding.
6990 * face_remap_out [O] Which faces the old faces have been mapped to.
6991 * vertex_remap_out [O] Which vertices the old vertices have been mapped to.
6993 * RETURNS
6994 * Success: D3D_OK.
6995 * Failure: D3DERR_INVALIDCALL, E_OUTOFMEMORY.
6997 * BUGS
6998 * Attribute sorting not implemented.
7001 HRESULT WINAPI D3DXWeldVertices(ID3DXMesh *mesh, DWORD flags, const D3DXWELDEPSILONS *epsilons,
7002 const DWORD *adjacency, DWORD *adjacency_out, DWORD *face_remap_out, ID3DXBuffer **vertex_remap_out)
7004 DWORD *adjacency_generated = NULL;
7005 const DWORD *adjacency_ptr;
7006 DWORD *attributes = NULL;
7007 const FLOAT DEFAULT_EPSILON = 1.0e-6f;
7008 HRESULT hr;
7009 DWORD i;
7010 void *indices = NULL;
7011 BOOL indices_are_32bit = mesh->lpVtbl->GetOptions(mesh) & D3DXMESH_32BIT;
7012 DWORD optimize_flags;
7013 DWORD *point_reps = NULL;
7014 struct d3dx9_mesh *This = impl_from_ID3DXMesh(mesh);
7015 DWORD *vertex_face_map = NULL;
7016 BYTE *vertices = NULL;
7018 TRACE("mesh %p, flags %#x, epsilons %p, adjacency %p, adjacency_out %p, face_remap_out %p, vertex_remap_out %p.\n",
7019 mesh, flags, epsilons, adjacency, adjacency_out, face_remap_out, vertex_remap_out);
7021 if (flags == 0)
7023 WARN("No flags are undefined. Using D3DXWELDEPSILONS_WELDPARTIALMATCHES instead.\n");
7024 flags = D3DXWELDEPSILONS_WELDPARTIALMATCHES;
7027 if (adjacency) /* Use supplied adjacency. */
7029 adjacency_ptr = adjacency;
7031 else /* Adjacency has to be generated. */
7033 adjacency_generated = HeapAlloc(GetProcessHeap(), 0, 3 * This->numfaces * sizeof(*adjacency_generated));
7034 if (!adjacency_generated)
7036 ERR("Couldn't allocate memory for adjacency_generated.\n");
7037 hr = E_OUTOFMEMORY;
7038 goto cleanup;
7040 hr = mesh->lpVtbl->GenerateAdjacency(mesh, DEFAULT_EPSILON, adjacency_generated);
7041 if (FAILED(hr))
7043 ERR("Couldn't generate adjacency.\n");
7044 goto cleanup;
7046 adjacency_ptr = adjacency_generated;
7049 /* Point representation says which vertices can be replaced. */
7050 point_reps = HeapAlloc(GetProcessHeap(), 0, This->numvertices * sizeof(*point_reps));
7051 if (!point_reps)
7053 hr = E_OUTOFMEMORY;
7054 ERR("Couldn't allocate memory for point_reps.\n");
7055 goto cleanup;
7057 hr = mesh->lpVtbl->ConvertAdjacencyToPointReps(mesh, adjacency_ptr, point_reps);
7058 if (FAILED(hr))
7060 ERR("ConvertAdjacencyToPointReps failed.\n");
7061 goto cleanup;
7064 hr = mesh->lpVtbl->LockIndexBuffer(mesh, 0, &indices);
7065 if (FAILED(hr))
7067 ERR("Couldn't lock index buffer.\n");
7068 goto cleanup;
7071 hr = mesh->lpVtbl->LockAttributeBuffer(mesh, 0, &attributes);
7072 if (FAILED(hr))
7074 ERR("Couldn't lock attribute buffer.\n");
7075 goto cleanup;
7077 vertex_face_map = HeapAlloc(GetProcessHeap(), 0, This->numvertices * sizeof(*vertex_face_map));
7078 if (!vertex_face_map)
7080 hr = E_OUTOFMEMORY;
7081 ERR("Couldn't allocate memory for vertex_face_map.\n");
7082 goto cleanup;
7084 /* Build vertex face map, so that a vertex's face can be looked up. */
7085 for (i = 0; i < This->numfaces; i++)
7087 DWORD j;
7088 for (j = 0; j < 3; j++)
7090 DWORD index = read_ib(indices, indices_are_32bit, 3*i + j);
7091 vertex_face_map[index] = i;
7095 if (flags & D3DXWELDEPSILONS_WELDPARTIALMATCHES)
7097 hr = mesh->lpVtbl->LockVertexBuffer(mesh, 0, (void**)&vertices);
7098 if (FAILED(hr))
7100 ERR("Couldn't lock vertex buffer.\n");
7101 goto cleanup;
7103 /* For each vertex that can be removed, compare its vertex components
7104 * with the vertex components from the vertex that can replace it. A
7105 * vertex is only fully replaced if all the components match and the
7106 * flag D3DXWELDEPSILONS_DONOTREMOVEVERTICES is not set, and they
7107 * belong to the same attribute group. Otherwise the vertex components
7108 * that are within epsilon are set to the same value.
7110 for (i = 0; i < 3 * This->numfaces; i++)
7112 D3DVERTEXELEMENT9 *decl_ptr;
7113 DWORD vertex_size = mesh->lpVtbl->GetNumBytesPerVertex(mesh);
7114 DWORD num_vertex_components;
7115 INT matches = 0;
7116 BOOL all_match;
7117 DWORD index = read_ib(indices, indices_are_32bit, i);
7119 for (decl_ptr = This->cached_declaration, num_vertex_components = 0; decl_ptr->Stream != 0xFF; decl_ptr++, num_vertex_components++)
7121 BYTE *to = &vertices[vertex_size*index + decl_ptr->Offset];
7122 BYTE *from = &vertices[vertex_size*point_reps[index] + decl_ptr->Offset];
7123 FLOAT epsilon = get_component_epsilon(decl_ptr, epsilons);
7125 /* Don't weld self */
7126 if (index == point_reps[index])
7128 matches++;
7129 continue;
7132 if (weld_component(to, from, decl_ptr->Type, epsilon))
7133 matches++;
7136 all_match = (num_vertex_components == matches);
7137 if (all_match && !(flags & D3DXWELDEPSILONS_DONOTREMOVEVERTICES))
7139 DWORD to_face = vertex_face_map[index];
7140 DWORD from_face = vertex_face_map[point_reps[index]];
7141 if(attributes[to_face] != attributes[from_face] && !(flags & D3DXWELDEPSILONS_DONOTSPLIT))
7142 continue;
7143 write_ib(indices, indices_are_32bit, i, point_reps[index]);
7146 mesh->lpVtbl->UnlockVertexBuffer(mesh);
7147 vertices = NULL;
7149 else if (flags & D3DXWELDEPSILONS_WELDALL)
7151 for (i = 0; i < 3 * This->numfaces; i++)
7153 DWORD index = read_ib(indices, indices_are_32bit, i);
7154 DWORD to_face = vertex_face_map[index];
7155 DWORD from_face = vertex_face_map[point_reps[index]];
7156 if(attributes[to_face] != attributes[from_face] && !(flags & D3DXWELDEPSILONS_DONOTSPLIT))
7157 continue;
7158 write_ib(indices, indices_are_32bit, i, point_reps[index]);
7161 mesh->lpVtbl->UnlockAttributeBuffer(mesh);
7162 attributes = NULL;
7163 mesh->lpVtbl->UnlockIndexBuffer(mesh);
7164 indices = NULL;
7166 /* Compact mesh using OptimizeInplace */
7167 optimize_flags = D3DXMESHOPT_COMPACT;
7168 hr = mesh->lpVtbl->OptimizeInplace(mesh, optimize_flags, adjacency_ptr, adjacency_out, face_remap_out, vertex_remap_out);
7169 if (FAILED(hr))
7171 ERR("Couldn't compact mesh.\n");
7172 goto cleanup;
7175 hr = D3D_OK;
7176 cleanup:
7177 HeapFree(GetProcessHeap(), 0, adjacency_generated);
7178 HeapFree(GetProcessHeap(), 0, point_reps);
7179 HeapFree(GetProcessHeap(), 0, vertex_face_map);
7180 if (attributes) mesh->lpVtbl->UnlockAttributeBuffer(mesh);
7181 if (indices) mesh->lpVtbl->UnlockIndexBuffer(mesh);
7182 if (vertices) mesh->lpVtbl->UnlockVertexBuffer(mesh);
7184 return hr;
7187 /*************************************************************************
7188 * D3DXOptimizeFaces (D3DX9_36.@)
7190 * Re-orders the faces so the vertex cache is used optimally.
7192 * PARAMS
7193 * indices [I] Pointer to an index buffer belonging to a mesh.
7194 * num_faces [I] Number of faces in the mesh.
7195 * num_vertices [I] Number of vertices in the mesh.
7196 * indices_are_32bit [I] Specifies whether indices are 32- or 16-bit.
7197 * face_remap [I/O] The new order the faces should be drawn in.
7199 * RETURNS
7200 * Success: D3D_OK.
7201 * Failure: D3DERR_INVALIDCALL.
7203 * BUGS
7204 * The face re-ordering does not use the vertex cache optimally.
7207 HRESULT WINAPI D3DXOptimizeFaces(const void *indices, UINT num_faces,
7208 UINT num_vertices, BOOL indices_are_32bit, DWORD *face_remap)
7210 UINT i;
7211 UINT j = num_faces - 1;
7212 UINT limit_16_bit = 2 << 15; /* According to MSDN */
7213 HRESULT hr = D3D_OK;
7215 FIXME("indices %p, num_faces %u, num_vertices %u, indices_are_32bit %#x, face_remap %p semi-stub. "
7216 "Face order will not be optimal.\n",
7217 indices, num_faces, num_vertices, indices_are_32bit, face_remap);
7219 if (!indices_are_32bit && num_faces >= limit_16_bit)
7221 WARN("Number of faces must be less than %d when using 16-bit indices.\n",
7222 limit_16_bit);
7223 hr = D3DERR_INVALIDCALL;
7224 goto error;
7227 if (!face_remap)
7229 WARN("Face remap pointer is NULL.\n");
7230 hr = D3DERR_INVALIDCALL;
7231 goto error;
7234 /* The faces are drawn in reverse order for simple meshes. This ordering
7235 * is not optimal for complicated meshes, but will not break anything
7236 * either. The ordering should be changed to take advantage of the vertex
7237 * cache on the graphics card.
7239 * TODO Re-order to take advantage of vertex cache.
7241 for (i = 0; i < num_faces; i++)
7243 face_remap[i] = j--;
7246 return D3D_OK;
7248 error:
7249 return hr;
7252 static D3DXVECTOR3 *vertex_element_vec3(BYTE *vertices, const D3DVERTEXELEMENT9 *declaration,
7253 DWORD vertex_stride, DWORD index)
7255 return (D3DXVECTOR3 *)(vertices + declaration->Offset + index * vertex_stride);
7258 static D3DXVECTOR3 read_vec3(BYTE *vertices, const D3DVERTEXELEMENT9 *declaration,
7259 DWORD vertex_stride, DWORD index)
7261 D3DXVECTOR3 vec3 = {0};
7262 const D3DXVECTOR3 *src = vertex_element_vec3(vertices, declaration, vertex_stride, index);
7264 switch (declaration->Type)
7266 case D3DDECLTYPE_FLOAT1:
7267 vec3.x = src->x;
7268 break;
7269 case D3DDECLTYPE_FLOAT2:
7270 vec3.x = src->x;
7271 vec3.y = src->y;
7272 break;
7273 case D3DDECLTYPE_FLOAT3:
7274 case D3DDECLTYPE_FLOAT4:
7275 vec3 = *src;
7276 break;
7277 default:
7278 ERR("Cannot read vec3\n");
7279 break;
7282 return vec3;
7285 /*************************************************************************
7286 * D3DXComputeTangentFrameEx (D3DX9_36.@)
7288 HRESULT WINAPI D3DXComputeTangentFrameEx(ID3DXMesh *mesh, DWORD texture_in_semantic, DWORD texture_in_index,
7289 DWORD u_partial_out_semantic, DWORD u_partial_out_index, DWORD v_partial_out_semantic,
7290 DWORD v_partial_out_index, DWORD normal_out_semantic, DWORD normal_out_index, DWORD options,
7291 const DWORD *adjacency, float partial_edge_threshold, float singular_point_threshold,
7292 float normal_edge_threshold, ID3DXMesh **mesh_out, ID3DXBuffer **vertex_mapping)
7294 HRESULT hr;
7295 void *indices = NULL;
7296 BYTE *vertices = NULL;
7297 DWORD *point_reps = NULL;
7298 size_t normal_size;
7299 BOOL indices_are_32bit;
7300 DWORD i, j, num_faces, num_vertices, vertex_stride;
7301 D3DVERTEXELEMENT9 declaration[MAX_FVF_DECL_SIZE] = {D3DDECL_END()};
7302 D3DVERTEXELEMENT9 *position_declaration = NULL, *normal_declaration = NULL;
7303 DWORD weighting_method = options & (D3DXTANGENT_WEIGHT_EQUAL | D3DXTANGENT_WEIGHT_BY_AREA);
7305 TRACE("mesh %p, texture_in_semantic %u, texture_in_index %u, u_partial_out_semantic %u, u_partial_out_index %u, "
7306 "v_partial_out_semantic %u, v_partial_out_index %u, normal_out_semantic %u, normal_out_index %u, "
7307 "options %#x, adjacency %p, partial_edge_threshold %f, singular_point_threshold %f, "
7308 "normal_edge_threshold %f, mesh_out %p, vertex_mapping %p\n",
7309 mesh, texture_in_semantic, texture_in_index, u_partial_out_semantic, u_partial_out_index,
7310 v_partial_out_semantic, v_partial_out_index, normal_out_semantic, normal_out_index, options, adjacency,
7311 partial_edge_threshold, singular_point_threshold, normal_edge_threshold, mesh_out, vertex_mapping);
7313 if (!mesh)
7315 WARN("mesh is NULL\n");
7316 return D3DERR_INVALIDCALL;
7319 if (weighting_method == (D3DXTANGENT_WEIGHT_EQUAL | D3DXTANGENT_WEIGHT_BY_AREA))
7321 WARN("D3DXTANGENT_WEIGHT_BY_AREA and D3DXTANGENT_WEIGHT_EQUAL are mutally exclusive\n");
7322 return D3DERR_INVALIDCALL;
7325 if (u_partial_out_semantic != D3DX_DEFAULT)
7327 FIXME("tangent vectors computation is not supported\n");
7328 return E_NOTIMPL;
7331 if (v_partial_out_semantic != D3DX_DEFAULT)
7333 FIXME("binormal vectors computation is not supported\n");
7334 return E_NOTIMPL;
7337 if (options & ~(D3DXTANGENT_GENERATE_IN_PLACE | D3DXTANGENT_CALCULATE_NORMALS | D3DXTANGENT_WEIGHT_EQUAL | D3DXTANGENT_WEIGHT_BY_AREA))
7339 FIXME("unsupported options %#x\n", options);
7340 return E_NOTIMPL;
7343 if (!(options & D3DXTANGENT_CALCULATE_NORMALS))
7345 FIXME("only normals computation is supported\n");
7346 return E_NOTIMPL;
7349 if (!(options & D3DXTANGENT_GENERATE_IN_PLACE) || mesh_out || vertex_mapping)
7351 FIXME("only D3DXTANGENT_GENERATE_IN_PLACE is supported\n");
7352 return E_NOTIMPL;
7355 if (FAILED(hr = mesh->lpVtbl->GetDeclaration(mesh, declaration)))
7356 return hr;
7358 for (i = 0; declaration[i].Stream != 0xff; i++)
7360 if (declaration[i].Usage == D3DDECLUSAGE_POSITION && !declaration[i].UsageIndex)
7361 position_declaration = &declaration[i];
7362 if (declaration[i].Usage == normal_out_semantic && declaration[i].UsageIndex == normal_out_index)
7363 normal_declaration = &declaration[i];
7366 if (!position_declaration || !normal_declaration)
7367 return D3DERR_INVALIDCALL;
7369 if (normal_declaration->Type == D3DDECLTYPE_FLOAT3)
7371 normal_size = sizeof(D3DXVECTOR3);
7373 else if (normal_declaration->Type == D3DDECLTYPE_FLOAT4)
7375 normal_size = sizeof(D3DXVECTOR4);
7377 else
7379 WARN("unsupported normals type %u\n", normal_declaration->Type);
7380 return D3DERR_INVALIDCALL;
7383 num_faces = mesh->lpVtbl->GetNumFaces(mesh);
7384 num_vertices = mesh->lpVtbl->GetNumVertices(mesh);
7385 vertex_stride = mesh->lpVtbl->GetNumBytesPerVertex(mesh);
7386 indices_are_32bit = mesh->lpVtbl->GetOptions(mesh) & D3DXMESH_32BIT;
7388 point_reps = HeapAlloc(GetProcessHeap(), 0, num_vertices * sizeof(*point_reps));
7389 if (!point_reps)
7391 hr = E_OUTOFMEMORY;
7392 goto done;
7395 if (adjacency)
7397 if (FAILED(hr = mesh->lpVtbl->ConvertAdjacencyToPointReps(mesh, adjacency, point_reps)))
7398 goto done;
7400 else
7402 for (i = 0; i < num_vertices; i++)
7403 point_reps[i] = i;
7406 if (FAILED(hr = mesh->lpVtbl->LockIndexBuffer(mesh, 0, &indices)))
7407 goto done;
7409 if (FAILED(hr = mesh->lpVtbl->LockVertexBuffer(mesh, 0, (void **)&vertices)))
7410 goto done;
7412 for (i = 0; i < num_vertices; i++)
7414 static const D3DXVECTOR4 default_vector = {0.0f, 0.0f, 0.0f, 1.0f};
7415 void *normal = vertices + normal_declaration->Offset + i * vertex_stride;
7417 memcpy(normal, &default_vector, normal_size);
7420 for (i = 0; i < num_faces; i++)
7422 float denominator, weights[3];
7423 D3DXVECTOR3 a, b, cross, face_normal;
7424 const DWORD face_indices[3] =
7426 read_ib(indices, indices_are_32bit, 3 * i + 0),
7427 read_ib(indices, indices_are_32bit, 3 * i + 1),
7428 read_ib(indices, indices_are_32bit, 3 * i + 2)
7430 const D3DXVECTOR3 v0 = read_vec3(vertices, position_declaration, vertex_stride, face_indices[0]);
7431 const D3DXVECTOR3 v1 = read_vec3(vertices, position_declaration, vertex_stride, face_indices[1]);
7432 const D3DXVECTOR3 v2 = read_vec3(vertices, position_declaration, vertex_stride, face_indices[2]);
7434 D3DXVec3Cross(&cross, D3DXVec3Subtract(&a, &v0, &v1), D3DXVec3Subtract(&b, &v0, &v2));
7436 switch (weighting_method)
7438 case D3DXTANGENT_WEIGHT_EQUAL:
7439 weights[0] = weights[1] = weights[2] = 1.0f;
7440 break;
7441 case D3DXTANGENT_WEIGHT_BY_AREA:
7442 weights[0] = weights[1] = weights[2] = D3DXVec3Length(&cross);
7443 break;
7444 default:
7445 /* weight by angle */
7446 denominator = D3DXVec3Length(&a) * D3DXVec3Length(&b);
7447 if (!denominator)
7448 weights[0] = 0.0f;
7449 else
7450 weights[0] = acosf(D3DXVec3Dot(&a, &b) / denominator);
7452 D3DXVec3Subtract(&a, &v1, &v0);
7453 D3DXVec3Subtract(&b, &v1, &v2);
7454 denominator = D3DXVec3Length(&a) * D3DXVec3Length(&b);
7455 if (!denominator)
7456 weights[1] = 0.0f;
7457 else
7458 weights[1] = acosf(D3DXVec3Dot(&a, &b) / denominator);
7460 D3DXVec3Subtract(&a, &v2, &v0);
7461 D3DXVec3Subtract(&b, &v2, &v1);
7462 denominator = D3DXVec3Length(&a) * D3DXVec3Length(&b);
7463 if (!denominator)
7464 weights[2] = 0.0f;
7465 else
7466 weights[2] = acosf(D3DXVec3Dot(&a, &b) / denominator);
7468 break;
7471 D3DXVec3Normalize(&face_normal, &cross);
7473 for (j = 0; j < 3; j++)
7475 D3DXVECTOR3 normal;
7476 DWORD rep_index = point_reps[face_indices[j]];
7477 D3DXVECTOR3 *rep_normal = vertex_element_vec3(vertices, normal_declaration, vertex_stride, rep_index);
7479 D3DXVec3Scale(&normal, &face_normal, weights[j]);
7480 D3DXVec3Add(rep_normal, rep_normal, &normal);
7484 for (i = 0; i < num_vertices; i++)
7486 DWORD rep_index = point_reps[i];
7487 D3DXVECTOR3 *normal = vertex_element_vec3(vertices, normal_declaration, vertex_stride, i);
7488 D3DXVECTOR3 *rep_normal = vertex_element_vec3(vertices, normal_declaration, vertex_stride, rep_index);
7490 if (i == rep_index)
7491 D3DXVec3Normalize(rep_normal, rep_normal);
7492 else
7493 *normal = *rep_normal;
7496 hr = D3D_OK;
7498 done:
7499 if (vertices)
7500 mesh->lpVtbl->UnlockVertexBuffer(mesh);
7502 if (indices)
7503 mesh->lpVtbl->UnlockIndexBuffer(mesh);
7505 HeapFree(GetProcessHeap(), 0, point_reps);
7507 return hr;
7510 /*************************************************************************
7511 * D3DXComputeNormals (D3DX9_36.@)
7513 HRESULT WINAPI D3DXComputeNormals(struct ID3DXBaseMesh *mesh, const DWORD *adjacency)
7515 TRACE("mesh %p, adjacency %p\n", mesh, adjacency);
7517 if (mesh && (ID3DXMeshVtbl *)mesh->lpVtbl != &D3DXMesh_Vtbl)
7519 ERR("Invalid virtual table\n");
7520 return D3DERR_INVALIDCALL;
7523 return D3DXComputeTangentFrameEx((ID3DXMesh *)mesh, D3DX_DEFAULT, 0,
7524 D3DX_DEFAULT, 0, D3DX_DEFAULT, 0, D3DDECLUSAGE_NORMAL, 0,
7525 D3DXTANGENT_GENERATE_IN_PLACE | D3DXTANGENT_CALCULATE_NORMALS,
7526 adjacency, -1.01f, -0.01f, -1.01f, NULL, NULL);
7529 /*************************************************************************
7530 * D3DXIntersect (D3DX9_36.@)
7532 HRESULT WINAPI D3DXIntersect(ID3DXBaseMesh *mesh, const D3DXVECTOR3 *ray_pos, const D3DXVECTOR3 *ray_dir,
7533 BOOL *hit, DWORD *face_index, float *u, float *v, float *distance, ID3DXBuffer **all_hits, DWORD *count_of_hits)
7535 FIXME("mesh %p, ray_pos %p, ray_dir %p, hit %p, face_index %p, u %p, v %p, distance %p, all_hits %p, "
7536 "count_of_hits %p stub!\n", mesh, ray_pos, ray_dir, hit, face_index, u, v, distance, all_hits, count_of_hits);
7538 return E_NOTIMPL;
7541 HRESULT WINAPI D3DXTessellateNPatches(ID3DXMesh *mesh, const DWORD *adjacency_in, float num_segs,
7542 BOOL quadratic_normals, ID3DXMesh **mesh_out, ID3DXBuffer **adjacency_out)
7544 FIXME("mesh %p, adjacency_in %p, num_segs %f, quadratic_normals %d, mesh_out %p, adjacency_out %p stub.\n",
7545 mesh, adjacency_in, num_segs, quadratic_normals, mesh_out, adjacency_out);
7547 return E_NOTIMPL;
7550 HRESULT WINAPI D3DXConvertMeshSubsetToSingleStrip(struct ID3DXBaseMesh *mesh_in, DWORD attribute_id,
7551 DWORD ib_flags, struct IDirect3DIndexBuffer9 **index_buffer, DWORD *index_count)
7553 FIXME("mesh_in %p, attribute_id %u, ib_flags %u, index_buffer %p, index_count %p stub.\n",
7554 mesh_in, attribute_id, ib_flags, index_buffer, index_count);
7556 return E_NOTIMPL;
7559 struct frame_node
7561 struct list entry;
7562 D3DXFRAME *frame;
7565 static BOOL queue_frame_node(struct list *queue, D3DXFRAME *frame)
7567 struct frame_node *node;
7569 if (!frame->pFrameFirstChild)
7570 return TRUE;
7572 node = HeapAlloc(GetProcessHeap(), 0, sizeof(*node));
7573 if (!node)
7574 return FALSE;
7576 node->frame = frame;
7577 list_add_tail(queue, &node->entry);
7579 return TRUE;
7582 static void empty_frame_queue(struct list *queue)
7584 struct frame_node *cur, *cur2;
7585 LIST_FOR_EACH_ENTRY_SAFE(cur, cur2, queue, struct frame_node, entry)
7587 list_remove(&cur->entry);
7588 HeapFree(GetProcessHeap(), 0, cur);
7592 D3DXFRAME * WINAPI D3DXFrameFind(const D3DXFRAME *root, const char *name)
7594 D3DXFRAME *found = NULL, *frame;
7595 struct list queue;
7597 TRACE("root frame %p, name %s.\n", root, debugstr_a(name));
7599 if (!root)
7600 return NULL;
7602 list_init(&queue);
7604 frame = (D3DXFRAME *)root;
7606 for (;;)
7608 struct frame_node *node;
7610 while (frame)
7612 if ((name && frame->Name && !strcmp(frame->Name, name)) || (!name && !frame->Name))
7614 found = frame;
7615 goto cleanup;
7618 if (!queue_frame_node(&queue, frame))
7619 goto cleanup;
7621 frame = frame->pFrameSibling;
7624 if (list_empty(&queue))
7625 break;
7627 node = LIST_ENTRY(list_head(&queue), struct frame_node, entry);
7628 list_remove(&node->entry);
7629 frame = node->frame->pFrameFirstChild;
7630 HeapFree(GetProcessHeap(), 0, node);
7633 cleanup:
7634 empty_frame_queue(&queue);
7636 return found;