include/mscvpdb.h: Use flexible array members for the rest of structures.
[wine.git] / dlls / d3dx9_36 / mesh.c
blob4fbb44b826b143c24a74f2c0afc015caf3bf8e48
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 /* for provide_flags parameters */
41 #define PROVIDE_MATERIALS 0x1
42 #define PROVIDE_SKININFO 0x2
43 #define PROVIDE_ADJACENCY 0x4
45 struct d3dx9_mesh
47 ID3DXMesh ID3DXMesh_iface;
48 LONG ref;
50 DWORD numfaces;
51 DWORD numvertices;
52 DWORD options;
53 DWORD fvf;
54 IDirect3DDevice9 *device;
55 D3DVERTEXELEMENT9 cached_declaration[MAX_FVF_DECL_SIZE];
56 IDirect3DVertexDeclaration9 *vertex_declaration;
57 UINT vertex_declaration_size;
58 UINT num_elem;
59 IDirect3DVertexBuffer9 *vertex_buffer;
60 IDirect3DIndexBuffer9 *index_buffer;
61 DWORD *attrib_buffer;
62 LONG attrib_buffer_lock_count;
63 DWORD attrib_table_size;
64 D3DXATTRIBUTERANGE *attrib_table;
67 static const UINT d3dx_decltype_size[] =
69 /* D3DDECLTYPE_FLOAT1 */ sizeof(FLOAT),
70 /* D3DDECLTYPE_FLOAT2 */ sizeof(D3DXVECTOR2),
71 /* D3DDECLTYPE_FLOAT3 */ sizeof(D3DXVECTOR3),
72 /* D3DDECLTYPE_FLOAT4 */ sizeof(D3DXVECTOR4),
73 /* D3DDECLTYPE_D3DCOLOR */ sizeof(D3DCOLOR),
74 /* D3DDECLTYPE_UBYTE4 */ 4 * sizeof(BYTE),
75 /* D3DDECLTYPE_SHORT2 */ 2 * sizeof(SHORT),
76 /* D3DDECLTYPE_SHORT4 */ 4 * sizeof(SHORT),
77 /* D3DDECLTYPE_UBYTE4N */ 4 * sizeof(BYTE),
78 /* D3DDECLTYPE_SHORT2N */ 2 * sizeof(SHORT),
79 /* D3DDECLTYPE_SHORT4N */ 4 * sizeof(SHORT),
80 /* D3DDECLTYPE_USHORT2N */ 2 * sizeof(USHORT),
81 /* D3DDECLTYPE_USHORT4N */ 4 * sizeof(USHORT),
82 /* D3DDECLTYPE_UDEC3 */ 4, /* 3 * 10 bits + 2 padding */
83 /* D3DDECLTYPE_DEC3N */ 4,
84 /* D3DDECLTYPE_FLOAT16_2 */ 2 * sizeof(D3DXFLOAT16),
85 /* D3DDECLTYPE_FLOAT16_4 */ 4 * sizeof(D3DXFLOAT16),
88 static inline struct d3dx9_mesh *impl_from_ID3DXMesh(ID3DXMesh *iface)
90 return CONTAINING_RECORD(iface, struct d3dx9_mesh, ID3DXMesh_iface);
93 static HRESULT WINAPI d3dx9_mesh_QueryInterface(ID3DXMesh *iface, REFIID riid, void **out)
95 TRACE("iface %p, riid %s, out %p.\n", iface, debugstr_guid(riid), out);
97 if (IsEqualGUID(riid, &IID_IUnknown) ||
98 IsEqualGUID(riid, &IID_ID3DXBaseMesh) ||
99 IsEqualGUID(riid, &IID_ID3DXMesh))
101 iface->lpVtbl->AddRef(iface);
102 *out = iface;
103 return S_OK;
106 WARN("Interface %s not found.\n", debugstr_guid(riid));
108 return E_NOINTERFACE;
111 static ULONG WINAPI d3dx9_mesh_AddRef(ID3DXMesh *iface)
113 struct d3dx9_mesh *mesh = impl_from_ID3DXMesh(iface);
114 ULONG refcount = InterlockedIncrement(&mesh->ref);
116 TRACE("%p increasing refcount to %lu.\n", mesh, refcount);
118 return refcount;
121 static ULONG WINAPI d3dx9_mesh_Release(ID3DXMesh *iface)
123 struct d3dx9_mesh *mesh = impl_from_ID3DXMesh(iface);
124 ULONG refcount = InterlockedDecrement(&mesh->ref);
126 TRACE("%p decreasing refcount to %lu.\n", mesh, refcount);
128 if (!refcount)
130 IDirect3DIndexBuffer9_Release(mesh->index_buffer);
131 IDirect3DVertexBuffer9_Release(mesh->vertex_buffer);
132 if (mesh->vertex_declaration)
133 IDirect3DVertexDeclaration9_Release(mesh->vertex_declaration);
134 IDirect3DDevice9_Release(mesh->device);
135 free(mesh->attrib_buffer);
136 free(mesh->attrib_table);
137 free(mesh);
140 return refcount;
143 static HRESULT WINAPI d3dx9_mesh_DrawSubset(ID3DXMesh *iface, DWORD attrib_id)
145 struct d3dx9_mesh *This = impl_from_ID3DXMesh(iface);
146 HRESULT hr;
147 DWORD face_start;
148 DWORD face_end = 0;
149 DWORD vertex_size;
151 TRACE("iface %p, attrib_id %lu.\n", iface, attrib_id);
153 if (!This->vertex_declaration)
155 WARN("Can't draw a mesh with an invalid vertex declaration.\n");
156 return E_FAIL;
159 vertex_size = iface->lpVtbl->GetNumBytesPerVertex(iface);
161 hr = IDirect3DDevice9_SetVertexDeclaration(This->device, This->vertex_declaration);
162 if (FAILED(hr)) return hr;
163 hr = IDirect3DDevice9_SetStreamSource(This->device, 0, This->vertex_buffer, 0, vertex_size);
164 if (FAILED(hr)) return hr;
165 hr = IDirect3DDevice9_SetIndices(This->device, This->index_buffer);
166 if (FAILED(hr)) return hr;
168 while (face_end < This->numfaces)
170 for (face_start = face_end; face_start < This->numfaces; face_start++)
172 if (This->attrib_buffer[face_start] == attrib_id)
173 break;
175 if (face_start >= This->numfaces)
176 break;
177 for (face_end = face_start + 1; face_end < This->numfaces; face_end++)
179 if (This->attrib_buffer[face_end] != attrib_id)
180 break;
183 hr = IDirect3DDevice9_DrawIndexedPrimitive(This->device, D3DPT_TRIANGLELIST,
184 0, 0, This->numvertices, face_start * 3, face_end - face_start);
185 if (FAILED(hr)) return hr;
188 return D3D_OK;
191 static DWORD WINAPI d3dx9_mesh_GetNumFaces(ID3DXMesh *iface)
193 struct d3dx9_mesh *mesh = impl_from_ID3DXMesh(iface);
195 TRACE("iface %p.\n", iface);
197 return mesh->numfaces;
200 static DWORD WINAPI d3dx9_mesh_GetNumVertices(ID3DXMesh *iface)
202 struct d3dx9_mesh *mesh = impl_from_ID3DXMesh(iface);
204 TRACE("iface %p.\n", iface);
206 return mesh->numvertices;
209 static DWORD WINAPI d3dx9_mesh_GetFVF(ID3DXMesh *iface)
211 struct d3dx9_mesh *mesh = impl_from_ID3DXMesh(iface);
213 TRACE("iface %p.\n", iface);
215 return mesh->fvf;
218 static void copy_declaration(D3DVERTEXELEMENT9 *dst, const D3DVERTEXELEMENT9 *src, UINT num_elem)
220 memcpy(dst, src, num_elem * sizeof(*src));
223 static HRESULT WINAPI d3dx9_mesh_GetDeclaration(ID3DXMesh *iface, D3DVERTEXELEMENT9 declaration[MAX_FVF_DECL_SIZE])
225 struct d3dx9_mesh *mesh = impl_from_ID3DXMesh(iface);
227 TRACE("iface %p, declaration %p.\n", iface, declaration);
229 if (!declaration)
230 return D3DERR_INVALIDCALL;
232 copy_declaration(declaration, mesh->cached_declaration, mesh->num_elem);
234 return D3D_OK;
237 static DWORD WINAPI d3dx9_mesh_GetNumBytesPerVertex(ID3DXMesh *iface)
239 struct d3dx9_mesh *mesh = impl_from_ID3DXMesh(iface);
241 TRACE("iface %p.\n", iface);
243 return mesh->vertex_declaration_size;
246 static DWORD WINAPI d3dx9_mesh_GetOptions(ID3DXMesh *iface)
248 struct d3dx9_mesh *mesh = impl_from_ID3DXMesh(iface);
250 TRACE("iface %p.\n", iface);
252 return mesh->options;
255 static HRESULT WINAPI d3dx9_mesh_GetDevice(struct ID3DXMesh *iface, struct IDirect3DDevice9 **device)
257 struct d3dx9_mesh *mesh = impl_from_ID3DXMesh(iface);
259 TRACE("iface %p, device %p.\n", iface, device);
261 if (!device)
262 return D3DERR_INVALIDCALL;
263 *device = mesh->device;
264 IDirect3DDevice9_AddRef(mesh->device);
266 return D3D_OK;
269 static HRESULT WINAPI d3dx9_mesh_CloneMeshFVF(struct ID3DXMesh *iface, DWORD options, DWORD fvf,
270 struct IDirect3DDevice9 *device, struct ID3DXMesh **clone_mesh)
272 HRESULT hr;
273 D3DVERTEXELEMENT9 declaration[MAX_FVF_DECL_SIZE];
275 TRACE("iface %p, options %#lx, fvf %#lx, device %p, clone_mesh %p.\n",
276 iface, options, fvf, device, clone_mesh);
278 if (FAILED(hr = D3DXDeclaratorFromFVF(fvf, declaration)))
279 return hr;
281 return iface->lpVtbl->CloneMesh(iface, options, declaration, device, clone_mesh);
284 static FLOAT scale_clamp_ubyten(FLOAT value)
286 value = value * UCHAR_MAX;
288 if (value < 0.0f)
290 return 0.0f;
292 else
294 if (value > UCHAR_MAX) /* Clamp at 255 */
295 return UCHAR_MAX;
296 else
297 return value;
301 static FLOAT scale_clamp_shortn(FLOAT value)
303 value = value * SHRT_MAX;
305 /* The tests show that the range is SHRT_MIN + 1 to SHRT_MAX. */
306 if (value <= SHRT_MIN)
308 return SHRT_MIN + 1;
310 else if (value > SHRT_MAX)
312 return SHRT_MAX;
314 else
316 return value;
320 static FLOAT scale_clamp_ushortn(FLOAT value)
322 value = value * USHRT_MAX;
324 if (value < 0.0f)
326 return 0.0f;
328 else
330 if (value > USHRT_MAX) /* Clamp at 65535 */
331 return USHRT_MAX;
332 else
333 return value;
337 static INT simple_round(FLOAT value)
339 int res = (INT)(value + 0.5f);
341 return res;
344 static void convert_float4(BYTE *dst, const D3DXVECTOR4 *src, D3DDECLTYPE type_dst)
346 BOOL fixme_once = FALSE;
348 switch (type_dst)
350 case D3DDECLTYPE_FLOAT1:
352 FLOAT *dst_ptr = (FLOAT*)dst;
353 *dst_ptr = src->x;
354 break;
356 case D3DDECLTYPE_FLOAT2:
358 D3DXVECTOR2 *dst_ptr = (D3DXVECTOR2*)dst;
359 dst_ptr->x = src->x;
360 dst_ptr->y = src->y;
361 break;
363 case D3DDECLTYPE_FLOAT3:
365 D3DXVECTOR3 *dst_ptr = (D3DXVECTOR3*)dst;
366 dst_ptr->x = src->x;
367 dst_ptr->y = src->y;
368 dst_ptr->z = src->z;
369 break;
371 case D3DDECLTYPE_FLOAT4:
373 D3DXVECTOR4 *dst_ptr = (D3DXVECTOR4*)dst;
374 dst_ptr->x = src->x;
375 dst_ptr->y = src->y;
376 dst_ptr->z = src->z;
377 dst_ptr->w = src->w;
378 break;
380 case D3DDECLTYPE_D3DCOLOR:
382 dst[0] = (BYTE)simple_round(scale_clamp_ubyten(src->z));
383 dst[1] = (BYTE)simple_round(scale_clamp_ubyten(src->y));
384 dst[2] = (BYTE)simple_round(scale_clamp_ubyten(src->x));
385 dst[3] = (BYTE)simple_round(scale_clamp_ubyten(src->w));
386 break;
388 case D3DDECLTYPE_UBYTE4:
390 dst[0] = src->x < 0.0f ? 0 : (BYTE)simple_round(src->x);
391 dst[1] = src->y < 0.0f ? 0 : (BYTE)simple_round(src->y);
392 dst[2] = src->z < 0.0f ? 0 : (BYTE)simple_round(src->z);
393 dst[3] = src->w < 0.0f ? 0 : (BYTE)simple_round(src->w);
394 break;
396 case D3DDECLTYPE_SHORT2:
398 SHORT *dst_ptr = (SHORT*)dst;
399 dst_ptr[0] = (SHORT)simple_round(src->x);
400 dst_ptr[1] = (SHORT)simple_round(src->y);
401 break;
403 case D3DDECLTYPE_SHORT4:
405 SHORT *dst_ptr = (SHORT*)dst;
406 dst_ptr[0] = (SHORT)simple_round(src->x);
407 dst_ptr[1] = (SHORT)simple_round(src->y);
408 dst_ptr[2] = (SHORT)simple_round(src->z);
409 dst_ptr[3] = (SHORT)simple_round(src->w);
410 break;
412 case D3DDECLTYPE_UBYTE4N:
414 dst[0] = (BYTE)simple_round(scale_clamp_ubyten(src->x));
415 dst[1] = (BYTE)simple_round(scale_clamp_ubyten(src->y));
416 dst[2] = (BYTE)simple_round(scale_clamp_ubyten(src->z));
417 dst[3] = (BYTE)simple_round(scale_clamp_ubyten(src->w));
418 break;
420 case D3DDECLTYPE_SHORT2N:
422 SHORT *dst_ptr = (SHORT*)dst;
423 dst_ptr[0] = (SHORT)simple_round(scale_clamp_shortn(src->x));
424 dst_ptr[1] = (SHORT)simple_round(scale_clamp_shortn(src->y));
425 break;
427 case D3DDECLTYPE_SHORT4N:
429 SHORT *dst_ptr = (SHORT*)dst;
430 dst_ptr[0] = (SHORT)simple_round(scale_clamp_shortn(src->x));
431 dst_ptr[1] = (SHORT)simple_round(scale_clamp_shortn(src->y));
432 dst_ptr[2] = (SHORT)simple_round(scale_clamp_shortn(src->z));
433 dst_ptr[3] = (SHORT)simple_round(scale_clamp_shortn(src->w));
434 break;
436 case D3DDECLTYPE_USHORT2N:
438 USHORT *dst_ptr = (USHORT*)dst;
439 dst_ptr[0] = (USHORT)simple_round(scale_clamp_ushortn(src->x));
440 dst_ptr[1] = (USHORT)simple_round(scale_clamp_ushortn(src->y));
441 break;
443 case D3DDECLTYPE_USHORT4N:
445 USHORT *dst_ptr = (USHORT*)dst;
446 dst_ptr[0] = (USHORT)simple_round(scale_clamp_ushortn(src->x));
447 dst_ptr[1] = (USHORT)simple_round(scale_clamp_ushortn(src->y));
448 dst_ptr[2] = (USHORT)simple_round(scale_clamp_ushortn(src->z));
449 dst_ptr[3] = (USHORT)simple_round(scale_clamp_ushortn(src->w));
450 break;
452 case D3DDECLTYPE_FLOAT16_2:
454 D3DXFloat32To16Array((D3DXFLOAT16*)dst, (FLOAT*)src, 2);
455 break;
457 case D3DDECLTYPE_FLOAT16_4:
459 D3DXFloat32To16Array((D3DXFLOAT16*)dst, (FLOAT*)src, 4);
460 break;
462 default:
463 if (!fixme_once++)
464 FIXME("Conversion from D3DDECLTYPE_FLOAT4 to %d not implemented.\n", type_dst);
465 break;
469 static void convert_component(BYTE *dst, BYTE *src, D3DDECLTYPE type_dst, D3DDECLTYPE type_src)
471 BOOL fixme_once = FALSE;
473 switch (type_src)
475 case D3DDECLTYPE_FLOAT1:
477 FLOAT *src_ptr = (FLOAT*)src;
478 D3DXVECTOR4 src_float4 = {*src_ptr, 0.0f, 0.0f, 1.0f};
479 convert_float4(dst, &src_float4, type_dst);
480 break;
482 case D3DDECLTYPE_FLOAT2:
484 D3DXVECTOR2 *src_ptr = (D3DXVECTOR2*)src;
485 D3DXVECTOR4 src_float4 = {src_ptr->x, src_ptr->y, 0.0f, 1.0f};
486 convert_float4(dst, &src_float4, type_dst);
487 break;
489 case D3DDECLTYPE_FLOAT3:
491 D3DXVECTOR3 *src_ptr = (D3DXVECTOR3*)src;
492 D3DXVECTOR4 src_float4 = {src_ptr->x, src_ptr->y, src_ptr->z, 1.0f};
493 convert_float4(dst, &src_float4, type_dst);
494 break;
496 case D3DDECLTYPE_FLOAT4:
498 D3DXVECTOR4 *src_ptr = (D3DXVECTOR4*)src;
499 D3DXVECTOR4 src_float4 = {src_ptr->x, src_ptr->y, src_ptr->z, src_ptr->w};
500 convert_float4(dst, &src_float4, type_dst);
501 break;
503 case D3DDECLTYPE_D3DCOLOR:
505 D3DXVECTOR4 src_float4 =
507 (FLOAT)src[2]/UCHAR_MAX,
508 (FLOAT)src[1]/UCHAR_MAX,
509 (FLOAT)src[0]/UCHAR_MAX,
510 (FLOAT)src[3]/UCHAR_MAX
512 convert_float4(dst, &src_float4, type_dst);
513 break;
515 case D3DDECLTYPE_UBYTE4:
517 D3DXVECTOR4 src_float4 = {src[0], src[1], src[2], src[3]};
518 convert_float4(dst, &src_float4, type_dst);
519 break;
521 case D3DDECLTYPE_SHORT2:
523 SHORT *src_ptr = (SHORT*)src;
524 D3DXVECTOR4 src_float4 = {src_ptr[0], src_ptr[1], 0.0f, 1.0f};
525 convert_float4(dst, &src_float4, type_dst);
526 break;
528 case D3DDECLTYPE_SHORT4:
530 SHORT *src_ptr = (SHORT*)src;
531 D3DXVECTOR4 src_float4 = {src_ptr[0], src_ptr[1], src_ptr[2], src_ptr[3]};
532 convert_float4(dst, &src_float4, type_dst);
533 break;
535 case D3DDECLTYPE_UBYTE4N:
537 D3DXVECTOR4 src_float4 =
539 (FLOAT)src[0]/UCHAR_MAX,
540 (FLOAT)src[1]/UCHAR_MAX,
541 (FLOAT)src[2]/UCHAR_MAX,
542 (FLOAT)src[3]/UCHAR_MAX
544 convert_float4(dst, &src_float4, type_dst);
545 break;
547 case D3DDECLTYPE_SHORT2N:
549 SHORT *src_ptr = (SHORT*)src;
550 D3DXVECTOR4 src_float4 = {(FLOAT)src_ptr[0]/SHRT_MAX, (FLOAT)src_ptr[1]/SHRT_MAX, 0.0f, 1.0f};
551 convert_float4(dst, &src_float4, type_dst);
552 break;
554 case D3DDECLTYPE_SHORT4N:
556 SHORT *src_ptr = (SHORT*)src;
557 D3DXVECTOR4 src_float4 =
559 (FLOAT)src_ptr[0]/SHRT_MAX,
560 (FLOAT)src_ptr[1]/SHRT_MAX,
561 (FLOAT)src_ptr[2]/SHRT_MAX,
562 (FLOAT)src_ptr[3]/SHRT_MAX
564 convert_float4(dst, &src_float4, type_dst);
565 break;
567 case D3DDECLTYPE_FLOAT16_2:
569 D3DXVECTOR4 src_float4 = {0.0f, 0.0f, 0.0f, 1.0f};
570 D3DXFloat16To32Array((FLOAT*)&src_float4, (D3DXFLOAT16*)src, 2);
571 convert_float4(dst, &src_float4, type_dst);
572 break;
574 case D3DDECLTYPE_FLOAT16_4:
576 D3DXVECTOR4 src_float4;
577 D3DXFloat16To32Array((FLOAT*)&src_float4, (D3DXFLOAT16*)src, 4);
578 convert_float4(dst, &src_float4, type_dst);
579 break;
581 default:
582 if (!fixme_once++)
583 FIXME("Conversion of D3DDECLTYPE %d to %d not implemented.\n", type_src, type_dst);
584 break;
588 static INT get_equivalent_declaration_index(D3DVERTEXELEMENT9 orig_declaration, D3DVERTEXELEMENT9 *declaration)
590 INT i;
592 for (i = 0; declaration[i].Stream != 0xff; i++)
594 if (orig_declaration.Usage == declaration[i].Usage
595 && orig_declaration.UsageIndex == declaration[i].UsageIndex)
597 return i;
601 return -1;
604 static HRESULT convert_vertex_buffer(ID3DXMesh *mesh_dst, ID3DXMesh *mesh_src)
606 HRESULT hr;
607 D3DVERTEXELEMENT9 orig_declaration[MAX_FVF_DECL_SIZE] = {D3DDECL_END()};
608 D3DVERTEXELEMENT9 declaration[MAX_FVF_DECL_SIZE] = {D3DDECL_END()};
609 BYTE *vb_dst = NULL;
610 BYTE *vb_src = NULL;
611 UINT i;
612 UINT num_vertices = mesh_src->lpVtbl->GetNumVertices(mesh_src);
613 UINT dst_vertex_size = mesh_dst->lpVtbl->GetNumBytesPerVertex(mesh_dst);
614 UINT src_vertex_size = mesh_src->lpVtbl->GetNumBytesPerVertex(mesh_src);
616 hr = mesh_src->lpVtbl->GetDeclaration(mesh_src, orig_declaration);
617 if (FAILED(hr)) return hr;
618 hr = mesh_dst->lpVtbl->GetDeclaration(mesh_dst, declaration);
619 if (FAILED(hr)) return hr;
621 hr = mesh_src->lpVtbl->LockVertexBuffer(mesh_src, D3DLOCK_READONLY, (void**)&vb_src);
622 if (FAILED(hr)) goto cleanup;
623 hr = mesh_dst->lpVtbl->LockVertexBuffer(mesh_dst, 0, (void**)&vb_dst);
624 if (FAILED(hr)) goto cleanup;
626 /* Clear all new fields by clearing the entire vertex buffer. */
627 memset(vb_dst, 0, num_vertices * dst_vertex_size);
629 for (i = 0; orig_declaration[i].Stream != 0xff; i++)
631 INT eq_idx = get_equivalent_declaration_index(orig_declaration[i], declaration);
633 if (eq_idx >= 0)
635 UINT j;
636 for (j = 0; j < num_vertices; j++)
638 UINT idx_dst = dst_vertex_size * j + declaration[eq_idx].Offset;
639 UINT idx_src = src_vertex_size * j + orig_declaration[i].Offset;
640 UINT type_size = d3dx_decltype_size[orig_declaration[i].Type];
642 if (orig_declaration[i].Type == declaration[eq_idx].Type)
643 memcpy(&vb_dst[idx_dst], &vb_src[idx_src], type_size);
644 else
645 convert_component(&vb_dst[idx_dst], &vb_src[idx_src], declaration[eq_idx].Type, orig_declaration[i].Type);
650 hr = D3D_OK;
651 cleanup:
652 if (vb_dst) mesh_dst->lpVtbl->UnlockVertexBuffer(mesh_dst);
653 if (vb_src) mesh_src->lpVtbl->UnlockVertexBuffer(mesh_src);
655 return hr;
658 static BOOL declaration_equals(const D3DVERTEXELEMENT9 *declaration1, const D3DVERTEXELEMENT9 *declaration2)
660 UINT size1 = 0, size2 = 0;
662 /* Find the size of each declaration */
663 while (declaration1[size1].Stream != 0xff) size1++;
664 while (declaration2[size2].Stream != 0xff) size2++;
666 /* If not same size then they are definitely not equal */
667 if (size1 != size2)
668 return FALSE;
670 /* Check that all components are the same */
671 if (memcmp(declaration1, declaration2, size1*sizeof(*declaration1)) == 0)
672 return TRUE;
674 return FALSE;
677 static HRESULT WINAPI d3dx9_mesh_CloneMesh(struct ID3DXMesh *iface, DWORD options,
678 const D3DVERTEXELEMENT9 *declaration, struct IDirect3DDevice9 *device, struct ID3DXMesh **clone_mesh_out)
680 struct d3dx9_mesh *This = impl_from_ID3DXMesh(iface);
681 struct d3dx9_mesh *cloned_this;
682 ID3DXMesh *clone_mesh;
683 D3DVERTEXELEMENT9 orig_declaration[MAX_FVF_DECL_SIZE] = { D3DDECL_END() };
684 void *data_in, *data_out;
685 DWORD vertex_size;
686 HRESULT hr;
687 BOOL same_declaration;
689 TRACE("iface %p, options %#lx, declaration %p, device %p, clone_mesh_out %p.\n",
690 iface, options, declaration, device, clone_mesh_out);
692 if (!clone_mesh_out)
693 return D3DERR_INVALIDCALL;
695 hr = iface->lpVtbl->GetDeclaration(iface, orig_declaration);
696 if (FAILED(hr)) return hr;
698 hr = D3DXCreateMesh(This->numfaces, This->numvertices, options & ~D3DXMESH_VB_SHARE,
699 declaration, device, &clone_mesh);
700 if (FAILED(hr)) return hr;
702 cloned_this = impl_from_ID3DXMesh(clone_mesh);
703 vertex_size = clone_mesh->lpVtbl->GetNumBytesPerVertex(clone_mesh);
704 same_declaration = declaration_equals(declaration, orig_declaration);
706 if (options & D3DXMESH_VB_SHARE) {
707 if (!same_declaration) {
708 hr = D3DERR_INVALIDCALL;
709 goto error;
711 IDirect3DVertexBuffer9_AddRef(This->vertex_buffer);
712 /* FIXME: refactor to avoid creating a new vertex buffer */
713 IDirect3DVertexBuffer9_Release(cloned_this->vertex_buffer);
714 cloned_this->vertex_buffer = This->vertex_buffer;
715 } else if (same_declaration) {
716 hr = iface->lpVtbl->LockVertexBuffer(iface, D3DLOCK_READONLY, &data_in);
717 if (FAILED(hr)) goto error;
718 hr = clone_mesh->lpVtbl->LockVertexBuffer(clone_mesh, 0, &data_out);
719 if (FAILED(hr)) {
720 iface->lpVtbl->UnlockVertexBuffer(iface);
721 goto error;
723 memcpy(data_out, data_in, This->numvertices * vertex_size);
724 clone_mesh->lpVtbl->UnlockVertexBuffer(clone_mesh);
725 iface->lpVtbl->UnlockVertexBuffer(iface);
726 } else {
727 hr = convert_vertex_buffer(clone_mesh, iface);
728 if (FAILED(hr)) goto error;
731 hr = iface->lpVtbl->LockIndexBuffer(iface, D3DLOCK_READONLY, &data_in);
732 if (FAILED(hr)) goto error;
733 hr = clone_mesh->lpVtbl->LockIndexBuffer(clone_mesh, 0, &data_out);
734 if (FAILED(hr)) {
735 iface->lpVtbl->UnlockIndexBuffer(iface);
736 goto error;
738 if ((options ^ This->options) & D3DXMESH_32BIT) {
739 DWORD i;
740 if (options & D3DXMESH_32BIT) {
741 for (i = 0; i < This->numfaces * 3; i++)
742 ((DWORD*)data_out)[i] = ((WORD*)data_in)[i];
743 } else {
744 for (i = 0; i < This->numfaces * 3; i++)
745 ((WORD*)data_out)[i] = ((DWORD*)data_in)[i];
747 } else {
748 memcpy(data_out, data_in, This->numfaces * 3 * (options & D3DXMESH_32BIT ? 4 : 2));
750 clone_mesh->lpVtbl->UnlockIndexBuffer(clone_mesh);
751 iface->lpVtbl->UnlockIndexBuffer(iface);
753 memcpy(cloned_this->attrib_buffer, This->attrib_buffer, This->numfaces * sizeof(*This->attrib_buffer));
755 if (This->attrib_table_size)
757 cloned_this->attrib_table_size = This->attrib_table_size;
758 cloned_this->attrib_table = malloc(This->attrib_table_size * sizeof(*This->attrib_table));
759 if (!cloned_this->attrib_table) {
760 hr = E_OUTOFMEMORY;
761 goto error;
763 memcpy(cloned_this->attrib_table, This->attrib_table, This->attrib_table_size * sizeof(*This->attrib_table));
766 *clone_mesh_out = clone_mesh;
768 return D3D_OK;
769 error:
770 IUnknown_Release(clone_mesh);
771 return hr;
774 static HRESULT WINAPI d3dx9_mesh_GetVertexBuffer(struct ID3DXMesh *iface,
775 struct IDirect3DVertexBuffer9 **vertex_buffer)
777 struct d3dx9_mesh *mesh = impl_from_ID3DXMesh(iface);
779 TRACE("iface %p, vertex_buffer %p.\n", iface, vertex_buffer);
781 if (!vertex_buffer)
782 return D3DERR_INVALIDCALL;
783 *vertex_buffer = mesh->vertex_buffer;
784 IDirect3DVertexBuffer9_AddRef(mesh->vertex_buffer);
786 return D3D_OK;
789 static HRESULT WINAPI d3dx9_mesh_GetIndexBuffer(struct ID3DXMesh *iface,
790 struct IDirect3DIndexBuffer9 **index_buffer)
792 struct d3dx9_mesh *mesh = impl_from_ID3DXMesh(iface);
794 TRACE("iface %p, index_buffer %p.\n", iface, index_buffer);
796 if (!index_buffer)
797 return D3DERR_INVALIDCALL;
798 *index_buffer = mesh->index_buffer;
799 IDirect3DIndexBuffer9_AddRef(mesh->index_buffer);
801 return D3D_OK;
804 static HRESULT WINAPI d3dx9_mesh_LockVertexBuffer(ID3DXMesh *iface, DWORD flags, void **data)
806 struct d3dx9_mesh *mesh = impl_from_ID3DXMesh(iface);
808 TRACE("iface %p, flags %#lx, data %p.\n", iface, flags, data);
810 return IDirect3DVertexBuffer9_Lock(mesh->vertex_buffer, 0, 0, data, flags);
813 static HRESULT WINAPI d3dx9_mesh_UnlockVertexBuffer(ID3DXMesh *iface)
815 struct d3dx9_mesh *mesh = impl_from_ID3DXMesh(iface);
817 TRACE("iface %p.\n", iface);
819 return IDirect3DVertexBuffer9_Unlock(mesh->vertex_buffer);
822 static HRESULT WINAPI d3dx9_mesh_LockIndexBuffer(ID3DXMesh *iface, DWORD flags, void **data)
824 struct d3dx9_mesh *mesh = impl_from_ID3DXMesh(iface);
826 TRACE("iface %p, flags %#lx, data %p.\n", iface, flags, data);
828 return IDirect3DIndexBuffer9_Lock(mesh->index_buffer, 0, 0, data, flags);
831 static HRESULT WINAPI d3dx9_mesh_UnlockIndexBuffer(ID3DXMesh *iface)
833 struct d3dx9_mesh *mesh = impl_from_ID3DXMesh(iface);
835 TRACE("iface %p.\n", iface);
837 return IDirect3DIndexBuffer9_Unlock(mesh->index_buffer);
840 /* FIXME: This looks just wrong, we never check *attrib_table_size before
841 * copying the data. */
842 static HRESULT WINAPI d3dx9_mesh_GetAttributeTable(ID3DXMesh *iface,
843 D3DXATTRIBUTERANGE *attrib_table, DWORD *attrib_table_size)
845 struct d3dx9_mesh *mesh = impl_from_ID3DXMesh(iface);
847 TRACE("iface %p, attrib_table %p, attrib_table_size %p.\n",
848 iface, attrib_table, attrib_table_size);
850 if (attrib_table_size)
851 *attrib_table_size = mesh->attrib_table_size;
853 if (attrib_table)
854 memcpy(attrib_table, mesh->attrib_table, mesh->attrib_table_size * sizeof(*attrib_table));
856 return D3D_OK;
859 struct edge_face
861 struct list entry;
862 DWORD v2;
863 DWORD face;
866 struct edge_face_map
868 struct list *lists;
869 struct edge_face *entries;
872 /* Builds up a map of which face a new edge belongs to. That way the adjacency
873 * of another edge can be looked up. An edge has an adjacent face if there
874 * is an edge going in the opposite direction in the map. For example if the
875 * edge (v1, v2) belongs to face 4, and there is a mapping (v2, v1)->7, then
876 * face 4 and 7 are adjacent.
878 * Each edge might have been replaced with another edge, or none at all. There
879 * is at most one edge to face mapping, i.e. an edge can only belong to one
880 * face.
882 static HRESULT init_edge_face_map(struct edge_face_map *edge_face_map, const DWORD *index_buffer,
883 const DWORD *point_reps, DWORD num_faces)
885 DWORD face, edge;
886 DWORD i;
888 edge_face_map->lists = malloc(3 * num_faces * sizeof(*edge_face_map->lists));
889 if (!edge_face_map->lists) return E_OUTOFMEMORY;
891 edge_face_map->entries = malloc(3 * num_faces * sizeof(*edge_face_map->entries));
892 if (!edge_face_map->entries) return E_OUTOFMEMORY;
895 /* Initialize all lists */
896 for (i = 0; i < 3 * num_faces; i++)
898 list_init(&edge_face_map->lists[i]);
900 /* Build edge face mapping */
901 for (face = 0; face < num_faces; face++)
903 for (edge = 0; edge < 3; edge++)
905 DWORD v1 = index_buffer[3*face + edge];
906 DWORD v2 = index_buffer[3*face + (edge+1)%3];
907 DWORD new_v1 = point_reps[v1]; /* What v1 has been replaced with */
908 DWORD new_v2 = point_reps[v2];
910 if (v1 != v2) /* Only map non-collapsed edges */
912 i = 3*face + edge;
913 edge_face_map->entries[i].v2 = new_v2;
914 edge_face_map->entries[i].face = face;
915 list_add_head(&edge_face_map->lists[new_v1], &edge_face_map->entries[i].entry);
920 return D3D_OK;
923 static DWORD find_adjacent_face(struct edge_face_map *edge_face_map, DWORD vertex1, DWORD vertex2, DWORD num_faces)
925 struct edge_face *edge_face_ptr;
927 LIST_FOR_EACH_ENTRY(edge_face_ptr, &edge_face_map->lists[vertex2], struct edge_face, entry)
929 if (edge_face_ptr->v2 == vertex1)
930 return edge_face_ptr->face;
933 return -1;
936 static DWORD *generate_identity_point_reps(DWORD num_vertices)
938 DWORD *id_point_reps;
939 DWORD i;
941 id_point_reps = malloc(num_vertices * sizeof(*id_point_reps));
942 if (!id_point_reps)
943 return NULL;
945 for (i = 0; i < num_vertices; i++)
947 id_point_reps[i] = i;
950 return id_point_reps;
953 static HRESULT WINAPI d3dx9_mesh_ConvertPointRepsToAdjacency(ID3DXMesh *iface,
954 const DWORD *point_reps, DWORD *adjacency)
956 HRESULT hr;
957 DWORD num_faces = iface->lpVtbl->GetNumFaces(iface);
958 DWORD num_vertices = iface->lpVtbl->GetNumVertices(iface);
959 DWORD options = iface->lpVtbl->GetOptions(iface);
960 BOOL indices_are_16_bit = !(options & D3DXMESH_32BIT);
961 DWORD *ib = NULL;
962 void *ib_ptr = NULL;
963 DWORD face;
964 DWORD edge;
965 struct edge_face_map edge_face_map = {0};
966 const DWORD *point_reps_ptr = NULL;
967 DWORD *id_point_reps = NULL;
969 TRACE("iface %p, point_reps %p, adjacency %p.\n", iface, point_reps, adjacency);
971 if (!adjacency) return D3DERR_INVALIDCALL;
973 if (!point_reps) /* Identity point reps */
975 id_point_reps = generate_identity_point_reps(num_vertices);
976 if (!id_point_reps)
978 hr = E_OUTOFMEMORY;
979 goto cleanup;
982 point_reps_ptr = id_point_reps;
984 else
986 point_reps_ptr = point_reps;
989 hr = iface->lpVtbl->LockIndexBuffer(iface, D3DLOCK_READONLY, &ib_ptr);
990 if (FAILED(hr)) goto cleanup;
992 if (indices_are_16_bit)
994 /* Widen 16 bit to 32 bit */
995 DWORD i;
996 WORD *ib_16bit = ib_ptr;
997 ib = malloc(3 * num_faces * sizeof(DWORD));
998 if (!ib)
1000 hr = E_OUTOFMEMORY;
1001 goto cleanup;
1003 for (i = 0; i < 3 * num_faces; i++)
1005 ib[i] = ib_16bit[i];
1008 else
1010 ib = ib_ptr;
1013 hr = init_edge_face_map(&edge_face_map, ib, point_reps_ptr, num_faces);
1014 if (FAILED(hr)) goto cleanup;
1016 /* Create adjacency */
1017 for (face = 0; face < num_faces; face++)
1019 for (edge = 0; edge < 3; edge++)
1021 DWORD v1 = ib[3*face + edge];
1022 DWORD v2 = ib[3*face + (edge+1)%3];
1023 DWORD new_v1 = point_reps_ptr[v1];
1024 DWORD new_v2 = point_reps_ptr[v2];
1025 DWORD adj_face;
1027 adj_face = find_adjacent_face(&edge_face_map, new_v1, new_v2, num_faces);
1028 adjacency[3*face + edge] = adj_face;
1032 hr = D3D_OK;
1033 cleanup:
1034 free(id_point_reps);
1035 if (indices_are_16_bit) free(ib);
1036 free(edge_face_map.lists);
1037 free(edge_face_map.entries);
1038 if(ib_ptr) iface->lpVtbl->UnlockIndexBuffer(iface);
1039 return hr;
1042 /* ConvertAdjacencyToPointReps helper function.
1044 * Goes around the edges of each face and replaces the vertices in any adjacent
1045 * face's edge with its own vertices(if its vertices have a lower index). This
1046 * way as few as possible low index vertices are shared among the faces. The
1047 * re-ordered index buffer is stored in new_indices.
1049 * The vertices in a point representation must be ordered sequentially, e.g.
1050 * index 5 holds the index of the vertex that replaces vertex 5, i.e. if
1051 * vertex 5 is replaced by vertex 3 then index 5 would contain 3. If no vertex
1052 * replaces it, then it contains the same number as the index itself, e.g.
1053 * index 5 would contain 5. */
1054 static HRESULT propagate_face_vertices(const DWORD *adjacency, DWORD *point_reps,
1055 const uint32_t *indices, uint32_t *new_indices, unsigned int face_idx, unsigned int face_count)
1057 unsigned int face_base = 3 * face_idx;
1058 unsigned int edge, opp_edge;
1060 for (edge = 0; edge < 3; ++edge)
1062 unsigned int adj_face = adjacency[face_base + edge];
1063 unsigned int adj_face_base;
1064 unsigned int i;
1066 if (adj_face == ~0u) /* No adjacent face. */
1067 continue;
1068 else if (adj_face >= face_count)
1070 /* This crashes on Windows. */
1071 WARN("Index out of bounds. Got %u, expected less than %u.\n", adj_face, face_count);
1072 return D3DERR_INVALIDCALL;
1074 adj_face_base = 3 * adj_face;
1076 /* Find opposite edge in adjacent face. */
1077 for (opp_edge = 0; opp_edge < 3; ++opp_edge)
1079 unsigned int opp_edge_index = adj_face_base + opp_edge;
1081 if (adjacency[opp_edge_index] == face_idx)
1082 break; /* Found opposite edge. */
1085 /* Replaces vertices in opposite edge with vertices from current edge. */
1086 for (i = 0; i < 2; ++i)
1088 unsigned int from = face_base + (edge + (1 - i)) % 3;
1089 unsigned int to = adj_face_base + (opp_edge + i) % 3;
1091 /* Propagate lowest index. */
1092 if (new_indices[to] > new_indices[from])
1094 new_indices[to] = new_indices[from];
1095 point_reps[indices[to]] = new_indices[from];
1100 return D3D_OK;
1103 static HRESULT WINAPI d3dx9_mesh_ConvertAdjacencyToPointReps(ID3DXMesh *iface,
1104 const DWORD *adjacency, DWORD *point_reps)
1106 struct d3dx9_mesh *mesh = impl_from_ID3DXMesh(iface);
1107 uint16_t *indices_16bit = NULL;
1108 uint32_t *new_indices = NULL;
1109 uint32_t *indices = NULL;
1110 unsigned int face, i;
1111 HRESULT hr;
1113 TRACE("iface %p, adjacency %p, point_reps %p.\n", iface, adjacency, point_reps);
1115 if (!adjacency)
1117 WARN("NULL adjacency.\n");
1118 return D3DERR_INVALIDCALL;
1121 if (!point_reps)
1123 WARN("NULL point_reps.\n");
1124 return D3DERR_INVALIDCALL;
1127 /* Should never happen as CreateMesh does not allow meshes with 0 faces. */
1128 if (!mesh->numfaces)
1130 ERR("Number of faces was zero.\n");
1131 return D3DERR_INVALIDCALL;
1134 if (!(new_indices = malloc(3 * mesh->numfaces * sizeof(*indices))))
1135 return E_OUTOFMEMORY;
1137 if (mesh->options & D3DXMESH_32BIT)
1139 if (FAILED(hr = iface->lpVtbl->LockIndexBuffer(iface, D3DLOCK_READONLY, (void **)&indices)))
1140 goto cleanup;
1141 memcpy(new_indices, indices, 3 * mesh->numfaces * sizeof(*indices));
1143 else
1145 /* Make a widening copy of indices_16bit into indices and new_indices
1146 * in order to re-use the helper function. */
1147 if (FAILED(hr = iface->lpVtbl->LockIndexBuffer(iface, D3DLOCK_READONLY, (void **)&indices_16bit)))
1148 goto cleanup;
1150 if (!(indices = malloc(3 * mesh->numfaces * sizeof(*indices))))
1152 hr = E_OUTOFMEMORY;
1153 goto cleanup;
1155 for (i = 0; i < 3 * mesh->numfaces; ++i)
1157 new_indices[i] = indices_16bit[i];
1158 indices[i] = indices_16bit[i];
1162 /* Vertices are ordered sequentially in the point representation. */
1163 for (i = 0; i < mesh->numvertices; ++i)
1164 point_reps[i] = i;
1166 /* Propagate vertices with low indices so as few vertices as possible are
1167 * used in the mesh. */
1168 for (face = 0; face < mesh->numfaces; ++face)
1170 if (FAILED(hr = propagate_face_vertices(adjacency, point_reps, indices, new_indices, face, mesh->numfaces)))
1171 goto cleanup;
1173 /* Go in opposite direction to catch all face orderings. */
1174 for (face = 0; face < mesh->numfaces; ++face)
1176 if (FAILED(hr = propagate_face_vertices(adjacency, point_reps, indices, new_indices,
1177 (mesh->numfaces - 1) - face, mesh->numfaces)))
1178 goto cleanup;
1181 hr = D3D_OK;
1183 cleanup:
1184 if (mesh->options & D3DXMESH_32BIT)
1186 if (indices)
1187 iface->lpVtbl->UnlockIndexBuffer(iface);
1189 else
1191 if (indices_16bit)
1192 iface->lpVtbl->UnlockIndexBuffer(iface);
1193 free(indices);
1195 free(new_indices);
1196 return hr;
1199 struct vertex_metadata {
1200 float key;
1201 DWORD vertex_index;
1202 DWORD first_shared_index;
1205 static int __cdecl compare_vertex_keys(const void *a, const void *b)
1207 const struct vertex_metadata *left = a;
1208 const struct vertex_metadata *right = b;
1209 if (left->key == right->key)
1210 return 0;
1211 return left->key < right->key ? -1 : 1;
1214 static HRESULT WINAPI d3dx9_mesh_GenerateAdjacency(ID3DXMesh *iface, float epsilon, DWORD *adjacency)
1216 struct d3dx9_mesh *This = impl_from_ID3DXMesh(iface);
1217 HRESULT hr;
1218 BYTE *vertices = NULL;
1219 const DWORD *indices = NULL;
1220 DWORD vertex_size;
1221 DWORD buffer_size;
1222 /* sort the vertices by (x + y + z) to quickly find coincident vertices */
1223 struct vertex_metadata *sorted_vertices;
1224 /* shared_indices links together identical indices in the index buffer so
1225 * that adjacency checks can be limited to faces sharing a vertex */
1226 DWORD *shared_indices = NULL;
1227 const FLOAT epsilon_sq = epsilon * epsilon;
1228 DWORD i;
1230 TRACE("iface %p, epsilon %.8e, adjacency %p.\n", iface, epsilon, adjacency);
1232 if (!adjacency)
1233 return D3DERR_INVALIDCALL;
1235 buffer_size = This->numfaces * 3 * sizeof(*shared_indices) + This->numvertices * sizeof(*sorted_vertices);
1236 if (!(This->options & D3DXMESH_32BIT))
1237 buffer_size += This->numfaces * 3 * sizeof(*indices);
1238 shared_indices = malloc(buffer_size);
1239 if (!shared_indices)
1240 return E_OUTOFMEMORY;
1241 sorted_vertices = (struct vertex_metadata*)(shared_indices + This->numfaces * 3);
1243 hr = iface->lpVtbl->LockVertexBuffer(iface, D3DLOCK_READONLY, (void**)&vertices);
1244 if (FAILED(hr)) goto cleanup;
1245 hr = iface->lpVtbl->LockIndexBuffer(iface, D3DLOCK_READONLY, (void**)&indices);
1246 if (FAILED(hr)) goto cleanup;
1248 if (!(This->options & D3DXMESH_32BIT)) {
1249 const WORD *word_indices = (const WORD*)indices;
1250 DWORD *dword_indices = (DWORD*)(sorted_vertices + This->numvertices);
1251 indices = dword_indices;
1252 for (i = 0; i < This->numfaces * 3; i++)
1253 *dword_indices++ = *word_indices++;
1256 vertex_size = iface->lpVtbl->GetNumBytesPerVertex(iface);
1257 for (i = 0; i < This->numvertices; i++) {
1258 D3DXVECTOR3 *vertex = (D3DXVECTOR3*)(vertices + vertex_size * i);
1259 sorted_vertices[i].first_shared_index = -1;
1260 sorted_vertices[i].key = vertex->x + vertex->y + vertex->z;
1261 sorted_vertices[i].vertex_index = i;
1263 for (i = 0; i < This->numfaces * 3; i++) {
1264 DWORD *first_shared_index = &sorted_vertices[indices[i]].first_shared_index;
1265 shared_indices[i] = *first_shared_index;
1266 *first_shared_index = i;
1267 adjacency[i] = -1;
1269 qsort(sorted_vertices, This->numvertices, sizeof(*sorted_vertices), compare_vertex_keys);
1271 for (i = 0; i < This->numvertices; i++) {
1272 struct vertex_metadata *sorted_vertex_a = &sorted_vertices[i];
1273 D3DXVECTOR3 *vertex_a = (D3DXVECTOR3*)(vertices + sorted_vertex_a->vertex_index * vertex_size);
1274 DWORD shared_index_a = sorted_vertex_a->first_shared_index;
1276 while (shared_index_a != -1) {
1277 DWORD j = i;
1278 DWORD shared_index_b = shared_indices[shared_index_a];
1279 struct vertex_metadata *sorted_vertex_b = sorted_vertex_a;
1281 while (TRUE) {
1282 while (shared_index_b != -1) {
1283 /* faces are adjacent if they have another coincident vertex */
1284 DWORD base_a = (shared_index_a / 3) * 3;
1285 DWORD base_b = (shared_index_b / 3) * 3;
1286 BOOL adjacent = FALSE;
1287 int k;
1289 for (k = 0; k < 3; k++) {
1290 if (adjacency[base_b + k] == shared_index_a / 3) {
1291 adjacent = TRUE;
1292 break;
1295 if (!adjacent) {
1296 for (k = 1; k <= 2; k++) {
1297 DWORD vertex_index_a = base_a + (shared_index_a + k) % 3;
1298 DWORD vertex_index_b = base_b + (shared_index_b + (3 - k)) % 3;
1299 adjacent = indices[vertex_index_a] == indices[vertex_index_b];
1300 if (!adjacent && epsilon >= 0.0f) {
1301 D3DXVECTOR3 delta = {0.0f, 0.0f, 0.0f};
1302 FLOAT length_sq;
1304 D3DXVec3Subtract(&delta,
1305 (D3DXVECTOR3*)(vertices + indices[vertex_index_a] * vertex_size),
1306 (D3DXVECTOR3*)(vertices + indices[vertex_index_b] * vertex_size));
1307 length_sq = D3DXVec3LengthSq(&delta);
1308 adjacent = epsilon == 0.0f ? length_sq == 0.0f : length_sq < epsilon_sq;
1310 if (adjacent) {
1311 DWORD adj_a = base_a + 2 - (vertex_index_a + shared_index_a + 1) % 3;
1312 DWORD adj_b = base_b + 2 - (vertex_index_b + shared_index_b + 1) % 3;
1313 if (adjacency[adj_a] == -1 && adjacency[adj_b] == -1) {
1314 adjacency[adj_a] = base_b / 3;
1315 adjacency[adj_b] = base_a / 3;
1316 break;
1322 shared_index_b = shared_indices[shared_index_b];
1324 while (++j < This->numvertices) {
1325 D3DXVECTOR3 *vertex_b;
1327 sorted_vertex_b++;
1328 if (sorted_vertex_b->key - sorted_vertex_a->key > epsilon * 3.0f) {
1329 /* no more coincident vertices to try */
1330 j = This->numvertices;
1331 break;
1333 /* check for coincidence */
1334 vertex_b = (D3DXVECTOR3*)(vertices + sorted_vertex_b->vertex_index * vertex_size);
1335 if (fabsf(vertex_a->x - vertex_b->x) <= epsilon &&
1336 fabsf(vertex_a->y - vertex_b->y) <= epsilon &&
1337 fabsf(vertex_a->z - vertex_b->z) <= epsilon)
1339 break;
1342 if (j >= This->numvertices)
1343 break;
1344 shared_index_b = sorted_vertex_b->first_shared_index;
1347 sorted_vertex_a->first_shared_index = shared_indices[sorted_vertex_a->first_shared_index];
1348 shared_index_a = sorted_vertex_a->first_shared_index;
1352 hr = D3D_OK;
1353 cleanup:
1354 if (indices) iface->lpVtbl->UnlockIndexBuffer(iface);
1355 if (vertices) iface->lpVtbl->UnlockVertexBuffer(iface);
1356 free(shared_indices);
1357 return hr;
1360 static HRESULT WINAPI d3dx9_mesh_UpdateSemantics(ID3DXMesh *iface, D3DVERTEXELEMENT9 declaration[MAX_FVF_DECL_SIZE])
1362 struct d3dx9_mesh *This = impl_from_ID3DXMesh(iface);
1363 HRESULT hr;
1364 UINT vertex_declaration_size;
1365 int i;
1367 TRACE("iface %p, declaration %p.\n", iface, declaration);
1369 if (!declaration)
1371 WARN("Invalid declaration. Can't use NULL declaration.\n");
1372 return D3DERR_INVALIDCALL;
1375 /* New declaration must be same size as original */
1376 vertex_declaration_size = D3DXGetDeclVertexSize(declaration, declaration[0].Stream);
1377 if (vertex_declaration_size != This->vertex_declaration_size)
1379 WARN("Invalid declaration. New vertex size does not match the original vertex size.\n");
1380 return D3DERR_INVALIDCALL;
1383 /* New declaration must not contain non-zero Stream value */
1384 for (i = 0; declaration[i].Stream != 0xff; i++)
1386 if (declaration[i].Stream != 0)
1388 WARN("Invalid declaration. New declaration contains non-zero Stream value.\n");
1389 return D3DERR_INVALIDCALL;
1393 This->num_elem = i + 1;
1394 copy_declaration(This->cached_declaration, declaration, This->num_elem);
1396 if (This->vertex_declaration)
1397 IDirect3DVertexDeclaration9_Release(This->vertex_declaration);
1399 /* An application can pass an invalid declaration to UpdateSemantics and
1400 * still expect D3D_OK (see tests). If the declaration is invalid, then
1401 * subsequent calls to DrawSubset will fail. This is handled by setting the
1402 * vertex declaration to NULL.
1403 * GetDeclaration, GetNumBytesPerVertex must, however, use the new
1404 * invalid declaration. This is handled by them using the cached vertex
1405 * declaration instead of the actual vertex declaration.
1407 hr = IDirect3DDevice9_CreateVertexDeclaration(This->device,
1408 declaration,
1409 &This->vertex_declaration);
1410 if (FAILED(hr))
1412 WARN("Using invalid declaration. Calls to DrawSubset will fail.\n");
1413 This->vertex_declaration = NULL;
1416 return D3D_OK;
1419 static HRESULT WINAPI d3dx9_mesh_LockAttributeBuffer(ID3DXMesh *iface, DWORD flags, DWORD **data)
1421 struct d3dx9_mesh *mesh = impl_from_ID3DXMesh(iface);
1423 TRACE("iface %p, flags %#lx, data %p.\n", iface, flags, data);
1425 InterlockedIncrement(&mesh->attrib_buffer_lock_count);
1427 if (!(flags & D3DLOCK_READONLY))
1429 D3DXATTRIBUTERANGE *attrib_table = mesh->attrib_table;
1430 mesh->attrib_table_size = 0;
1431 mesh->attrib_table = NULL;
1432 free(attrib_table);
1435 *data = mesh->attrib_buffer;
1437 return D3D_OK;
1440 static HRESULT WINAPI d3dx9_mesh_UnlockAttributeBuffer(ID3DXMesh *iface)
1442 struct d3dx9_mesh *mesh = impl_from_ID3DXMesh(iface);
1443 int lock_count;
1445 TRACE("iface %p.\n", iface);
1447 lock_count = InterlockedDecrement(&mesh->attrib_buffer_lock_count);
1448 if (lock_count < 0)
1450 InterlockedIncrement(&mesh->attrib_buffer_lock_count);
1451 return D3DERR_INVALIDCALL;
1454 return D3D_OK;
1457 static HRESULT WINAPI d3dx9_mesh_Optimize(ID3DXMesh *iface, DWORD flags, const DWORD *adjacency_in,
1458 DWORD *adjacency_out, DWORD *face_remap, ID3DXBuffer **vertex_remap, ID3DXMesh **opt_mesh)
1460 struct d3dx9_mesh *mesh = impl_from_ID3DXMesh(iface);
1461 HRESULT hr;
1462 D3DVERTEXELEMENT9 declaration[MAX_FVF_DECL_SIZE] = { D3DDECL_END() };
1463 ID3DXMesh *optimized_mesh;
1465 TRACE("iface %p, flags %#lx, adjacency_in %p, adjacency_out %p, face_remap %p, vertex_remap %p, opt_mesh %p.\n",
1466 iface, flags, adjacency_in, adjacency_out, face_remap, vertex_remap, opt_mesh);
1468 if (!opt_mesh)
1469 return D3DERR_INVALIDCALL;
1471 hr = iface->lpVtbl->GetDeclaration(iface, declaration);
1472 if (FAILED(hr)) return hr;
1474 if (FAILED(hr = iface->lpVtbl->CloneMesh(iface, mesh->options, declaration, mesh->device, &optimized_mesh)))
1475 return hr;
1477 hr = optimized_mesh->lpVtbl->OptimizeInplace(optimized_mesh, flags, adjacency_in, adjacency_out, face_remap, vertex_remap);
1478 if (SUCCEEDED(hr))
1479 *opt_mesh = optimized_mesh;
1480 else
1481 IUnknown_Release(optimized_mesh);
1482 return hr;
1485 /* Creates a vertex_remap that removes unused vertices.
1486 * Indices are updated according to the vertex_remap. */
1487 static HRESULT compact_mesh(struct d3dx9_mesh *This, DWORD *indices,
1488 DWORD *new_num_vertices, ID3DXBuffer **vertex_remap)
1490 HRESULT hr;
1491 DWORD *vertex_remap_ptr;
1492 DWORD num_used_vertices;
1493 DWORD i;
1495 hr = D3DXCreateBuffer(This->numvertices * sizeof(DWORD), vertex_remap);
1496 if (FAILED(hr)) return hr;
1497 vertex_remap_ptr = ID3DXBuffer_GetBufferPointer(*vertex_remap);
1499 for (i = 0; i < This->numfaces * 3; i++)
1500 vertex_remap_ptr[indices[i]] = 1;
1502 /* create old->new vertex mapping */
1503 num_used_vertices = 0;
1504 for (i = 0; i < This->numvertices; i++) {
1505 if (vertex_remap_ptr[i])
1506 vertex_remap_ptr[i] = num_used_vertices++;
1507 else
1508 vertex_remap_ptr[i] = -1;
1510 /* convert indices */
1511 for (i = 0; i < This->numfaces * 3; i++)
1512 indices[i] = vertex_remap_ptr[indices[i]];
1514 /* create new->old vertex mapping */
1515 num_used_vertices = 0;
1516 for (i = 0; i < This->numvertices; i++) {
1517 if (vertex_remap_ptr[i] != -1)
1518 vertex_remap_ptr[num_used_vertices++] = i;
1520 for (i = num_used_vertices; i < This->numvertices; i++)
1521 vertex_remap_ptr[i] = -1;
1523 *new_num_vertices = num_used_vertices;
1525 return D3D_OK;
1528 /* count the number of unique attribute values in a sorted attribute buffer */
1529 static DWORD count_attributes(const DWORD *attrib_buffer, DWORD numfaces)
1531 DWORD last_attribute = attrib_buffer[0];
1532 DWORD attrib_table_size = 1;
1533 DWORD i;
1534 for (i = 1; i < numfaces; i++) {
1535 if (attrib_buffer[i] != last_attribute) {
1536 last_attribute = attrib_buffer[i];
1537 attrib_table_size++;
1540 return attrib_table_size;
1543 static void fill_attribute_table(DWORD *attrib_buffer, DWORD numfaces, void *indices,
1544 BOOL is_32bit_indices, D3DXATTRIBUTERANGE *attrib_table)
1546 DWORD attrib_table_size = 0;
1547 DWORD last_attribute = attrib_buffer[0];
1548 DWORD min_vertex, max_vertex;
1549 DWORD i;
1551 attrib_table[0].AttribId = last_attribute;
1552 attrib_table[0].FaceStart = 0;
1553 min_vertex = (DWORD)-1;
1554 max_vertex = 0;
1555 for (i = 0; i < numfaces; i++) {
1556 DWORD j;
1558 if (attrib_buffer[i] != last_attribute) {
1559 last_attribute = attrib_buffer[i];
1560 attrib_table[attrib_table_size].FaceCount = i - attrib_table[attrib_table_size].FaceStart;
1561 attrib_table[attrib_table_size].VertexStart = min_vertex;
1562 attrib_table[attrib_table_size].VertexCount = max_vertex - min_vertex + 1;
1563 attrib_table_size++;
1564 attrib_table[attrib_table_size].AttribId = attrib_buffer[i];
1565 attrib_table[attrib_table_size].FaceStart = i;
1566 min_vertex = (DWORD)-1;
1567 max_vertex = 0;
1569 for (j = 0; j < 3; j++) {
1570 DWORD vertex_index = is_32bit_indices ? ((DWORD*)indices)[i * 3 + j] : ((WORD*)indices)[i * 3 + j];
1571 if (vertex_index < min_vertex)
1572 min_vertex = vertex_index;
1573 if (vertex_index > max_vertex)
1574 max_vertex = vertex_index;
1577 attrib_table[attrib_table_size].FaceCount = i - attrib_table[attrib_table_size].FaceStart;
1578 attrib_table[attrib_table_size].VertexStart = min_vertex;
1579 attrib_table[attrib_table_size].VertexCount = max_vertex - min_vertex + 1;
1580 attrib_table_size++;
1583 static int __cdecl attrib_entry_compare(const void *a, const void *b)
1585 const DWORD *ptr_a = *(const DWORD **)a;
1586 const DWORD *ptr_b = *(const DWORD **)b;
1587 int delta = *ptr_a - *ptr_b;
1589 if (delta)
1590 return delta;
1592 delta = ptr_a - ptr_b; /* for stable sort */
1593 return delta;
1596 /* Create face_remap, a new attribute buffer for attribute sort optimization. */
1597 static HRESULT remap_faces_for_attrsort(struct d3dx9_mesh *This, const DWORD *indices,
1598 DWORD *attrib_buffer, DWORD **sorted_attrib_buffer, DWORD **face_remap)
1600 DWORD **sorted_attrib_ptr_buffer = NULL;
1601 DWORD i;
1603 sorted_attrib_ptr_buffer = malloc(This->numfaces * sizeof(*sorted_attrib_ptr_buffer));
1604 if (!sorted_attrib_ptr_buffer)
1605 return E_OUTOFMEMORY;
1607 *face_remap = malloc(This->numfaces * sizeof(**face_remap));
1608 if (!*face_remap)
1610 free(sorted_attrib_ptr_buffer);
1611 return E_OUTOFMEMORY;
1614 for (i = 0; i < This->numfaces; i++)
1615 sorted_attrib_ptr_buffer[i] = &attrib_buffer[i];
1616 qsort(sorted_attrib_ptr_buffer, This->numfaces, sizeof(*sorted_attrib_ptr_buffer), attrib_entry_compare);
1618 for (i = 0; i < This->numfaces; i++)
1620 DWORD old_face = sorted_attrib_ptr_buffer[i] - attrib_buffer;
1621 (*face_remap)[old_face] = i;
1624 /* overwrite sorted_attrib_ptr_buffer with the values themselves */
1625 *sorted_attrib_buffer = (DWORD*)sorted_attrib_ptr_buffer;
1626 for (i = 0; i < This->numfaces; i++)
1627 (*sorted_attrib_buffer)[(*face_remap)[i]] = attrib_buffer[i];
1629 return D3D_OK;
1632 static DWORD adjacency_remap(DWORD *face_remap, DWORD index)
1634 if (index == 0xffffffff)
1635 return index;
1636 return face_remap[index];
1639 static HRESULT WINAPI d3dx9_mesh_OptimizeInplace(ID3DXMesh *iface, DWORD flags, const DWORD *adjacency_in,
1640 DWORD *adjacency_out, DWORD *face_remap_out, ID3DXBuffer **vertex_remap_out)
1642 struct d3dx9_mesh *This = impl_from_ID3DXMesh(iface);
1643 void *indices = NULL;
1644 DWORD *attrib_buffer = NULL;
1645 HRESULT hr;
1646 ID3DXBuffer *vertex_remap = NULL;
1647 DWORD *face_remap = NULL; /* old -> new mapping */
1648 DWORD *dword_indices = NULL;
1649 DWORD new_num_vertices = 0;
1650 DWORD new_num_alloc_vertices = 0;
1651 IDirect3DVertexBuffer9 *vertex_buffer = NULL;
1652 DWORD *sorted_attrib_buffer = NULL;
1653 DWORD i;
1655 TRACE("iface %p, flags %#lx, adjacency_in %p, adjacency_out %p, face_remap_out %p, vertex_remap_out %p.\n",
1656 iface, flags, adjacency_in, adjacency_out, face_remap_out, vertex_remap_out);
1658 if (!flags)
1659 return D3DERR_INVALIDCALL;
1660 if (!adjacency_in && (flags & (D3DXMESHOPT_VERTEXCACHE | D3DXMESHOPT_STRIPREORDER)))
1661 return D3DERR_INVALIDCALL;
1662 if ((flags & (D3DXMESHOPT_VERTEXCACHE | D3DXMESHOPT_STRIPREORDER)) == (D3DXMESHOPT_VERTEXCACHE | D3DXMESHOPT_STRIPREORDER))
1663 return D3DERR_INVALIDCALL;
1665 if (flags & (D3DXMESHOPT_VERTEXCACHE | D3DXMESHOPT_STRIPREORDER))
1667 if (flags & D3DXMESHOPT_VERTEXCACHE)
1668 FIXME("D3DXMESHOPT_VERTEXCACHE not implemented.\n");
1669 if (flags & D3DXMESHOPT_STRIPREORDER)
1670 FIXME("D3DXMESHOPT_STRIPREORDER not implemented.\n");
1671 return E_NOTIMPL;
1674 hr = iface->lpVtbl->LockIndexBuffer(iface, 0, &indices);
1675 if (FAILED(hr)) goto cleanup;
1677 dword_indices = malloc(This->numfaces * 3 * sizeof(DWORD));
1678 if (!dword_indices) return E_OUTOFMEMORY;
1679 if (This->options & D3DXMESH_32BIT) {
1680 memcpy(dword_indices, indices, This->numfaces * 3 * sizeof(DWORD));
1681 } else {
1682 WORD *word_indices = indices;
1683 for (i = 0; i < This->numfaces * 3; i++)
1684 dword_indices[i] = *word_indices++;
1687 if ((flags & (D3DXMESHOPT_COMPACT | D3DXMESHOPT_IGNOREVERTS | D3DXMESHOPT_ATTRSORT)) == D3DXMESHOPT_COMPACT)
1689 new_num_alloc_vertices = This->numvertices;
1690 hr = compact_mesh(This, dword_indices, &new_num_vertices, &vertex_remap);
1691 if (FAILED(hr)) goto cleanup;
1692 } else if (flags & D3DXMESHOPT_ATTRSORT) {
1693 if (!(flags & D3DXMESHOPT_IGNOREVERTS))
1694 FIXME("D3DXMESHOPT_ATTRSORT vertex reordering not implemented.\n");
1696 hr = iface->lpVtbl->LockAttributeBuffer(iface, 0, &attrib_buffer);
1697 if (FAILED(hr)) goto cleanup;
1699 hr = remap_faces_for_attrsort(This, dword_indices, attrib_buffer, &sorted_attrib_buffer, &face_remap);
1700 if (FAILED(hr)) goto cleanup;
1703 if (vertex_remap)
1705 /* reorder the vertices using vertex_remap */
1706 D3DVERTEXBUFFER_DESC vertex_desc;
1707 DWORD *vertex_remap_ptr = ID3DXBuffer_GetBufferPointer(vertex_remap);
1708 DWORD vertex_size = iface->lpVtbl->GetNumBytesPerVertex(iface);
1709 BYTE *orig_vertices;
1710 BYTE *new_vertices;
1712 hr = IDirect3DVertexBuffer9_GetDesc(This->vertex_buffer, &vertex_desc);
1713 if (FAILED(hr)) goto cleanup;
1715 hr = IDirect3DDevice9_CreateVertexBuffer(This->device, new_num_alloc_vertices * vertex_size,
1716 vertex_desc.Usage, This->fvf, vertex_desc.Pool, &vertex_buffer, NULL);
1717 if (FAILED(hr)) goto cleanup;
1719 hr = IDirect3DVertexBuffer9_Lock(This->vertex_buffer, 0, 0, (void**)&orig_vertices, D3DLOCK_READONLY);
1720 if (FAILED(hr)) goto cleanup;
1722 hr = IDirect3DVertexBuffer9_Lock(vertex_buffer, 0, 0, (void**)&new_vertices, 0);
1723 if (FAILED(hr)) {
1724 IDirect3DVertexBuffer9_Unlock(This->vertex_buffer);
1725 goto cleanup;
1728 for (i = 0; i < new_num_vertices; i++)
1729 memcpy(new_vertices + i * vertex_size, orig_vertices + vertex_remap_ptr[i] * vertex_size, vertex_size);
1731 IDirect3DVertexBuffer9_Unlock(This->vertex_buffer);
1732 IDirect3DVertexBuffer9_Unlock(vertex_buffer);
1733 } else if (vertex_remap_out) {
1734 DWORD *vertex_remap_ptr;
1736 hr = D3DXCreateBuffer(This->numvertices * sizeof(DWORD), &vertex_remap);
1737 if (FAILED(hr)) goto cleanup;
1738 vertex_remap_ptr = ID3DXBuffer_GetBufferPointer(vertex_remap);
1739 for (i = 0; i < This->numvertices; i++)
1740 *vertex_remap_ptr++ = i;
1743 if (flags & D3DXMESHOPT_ATTRSORT)
1745 D3DXATTRIBUTERANGE *attrib_table;
1746 DWORD attrib_table_size;
1748 attrib_table_size = count_attributes(sorted_attrib_buffer, This->numfaces);
1749 attrib_table = malloc(attrib_table_size * sizeof(*attrib_table));
1750 if (!attrib_table) {
1751 hr = E_OUTOFMEMORY;
1752 goto cleanup;
1755 memcpy(attrib_buffer, sorted_attrib_buffer, This->numfaces * sizeof(*attrib_buffer));
1757 /* reorder the indices using face_remap */
1758 if (This->options & D3DXMESH_32BIT) {
1759 for (i = 0; i < This->numfaces; i++)
1760 memcpy((DWORD*)indices + face_remap[i] * 3, dword_indices + i * 3, 3 * sizeof(DWORD));
1761 } else {
1762 WORD *word_indices = indices;
1763 for (i = 0; i < This->numfaces; i++) {
1764 DWORD new_pos = face_remap[i] * 3;
1765 DWORD old_pos = i * 3;
1766 word_indices[new_pos++] = dword_indices[old_pos++];
1767 word_indices[new_pos++] = dword_indices[old_pos++];
1768 word_indices[new_pos] = dword_indices[old_pos];
1772 fill_attribute_table(attrib_buffer, This->numfaces, indices,
1773 This->options & D3DXMESH_32BIT, attrib_table);
1775 free(This->attrib_table);
1776 This->attrib_table = attrib_table;
1777 This->attrib_table_size = attrib_table_size;
1778 } else {
1779 if (This->options & D3DXMESH_32BIT) {
1780 memcpy(indices, dword_indices, This->numfaces * 3 * sizeof(DWORD));
1781 } else {
1782 WORD *word_indices = indices;
1783 for (i = 0; i < This->numfaces * 3; i++)
1784 *word_indices++ = dword_indices[i];
1788 if (adjacency_out) {
1789 if (face_remap) {
1790 for (i = 0; i < This->numfaces; i++) {
1791 DWORD old_pos = i * 3;
1792 DWORD new_pos = face_remap[i] * 3;
1794 adjacency_out[new_pos++] = adjacency_remap(face_remap, adjacency_in[old_pos++]);
1795 adjacency_out[new_pos++] = adjacency_remap(face_remap, adjacency_in[old_pos++]);
1796 adjacency_out[new_pos] = adjacency_remap(face_remap, adjacency_in[old_pos]);
1798 } else {
1799 memcpy(adjacency_out, adjacency_in, This->numfaces * 3 * sizeof(*adjacency_out));
1802 if (face_remap_out) {
1803 if (face_remap) {
1804 for (i = 0; i < This->numfaces; i++)
1805 face_remap_out[face_remap[i]] = i;
1806 } else {
1807 for (i = 0; i < This->numfaces; i++)
1808 face_remap_out[i] = i;
1811 if (vertex_remap_out)
1812 *vertex_remap_out = vertex_remap;
1813 vertex_remap = NULL;
1815 if (vertex_buffer) {
1816 IDirect3DVertexBuffer9_Release(This->vertex_buffer);
1817 This->vertex_buffer = vertex_buffer;
1818 vertex_buffer = NULL;
1819 This->numvertices = new_num_vertices;
1822 hr = D3D_OK;
1823 cleanup:
1824 free(sorted_attrib_buffer);
1825 free(face_remap);
1826 free(dword_indices);
1827 if (vertex_remap) ID3DXBuffer_Release(vertex_remap);
1828 if (vertex_buffer) IDirect3DVertexBuffer9_Release(vertex_buffer);
1829 if (attrib_buffer) iface->lpVtbl->UnlockAttributeBuffer(iface);
1830 if (indices) iface->lpVtbl->UnlockIndexBuffer(iface);
1831 return hr;
1834 static HRESULT WINAPI d3dx9_mesh_SetAttributeTable(ID3DXMesh *iface,
1835 const D3DXATTRIBUTERANGE *attrib_table, DWORD attrib_table_size)
1837 struct d3dx9_mesh *mesh = impl_from_ID3DXMesh(iface);
1838 D3DXATTRIBUTERANGE *new_table = NULL;
1840 TRACE("iface %p, attrib_table %p, attrib_table_size %lu.\n", iface, attrib_table, attrib_table_size);
1842 if (attrib_table_size) {
1843 size_t size = attrib_table_size * sizeof(*attrib_table);
1845 new_table = malloc(size);
1846 if (!new_table)
1847 return E_OUTOFMEMORY;
1849 CopyMemory(new_table, attrib_table, size);
1850 } else if (attrib_table) {
1851 return D3DERR_INVALIDCALL;
1853 free(mesh->attrib_table);
1854 mesh->attrib_table = new_table;
1855 mesh->attrib_table_size = attrib_table_size;
1857 return D3D_OK;
1860 static const struct ID3DXMeshVtbl D3DXMesh_Vtbl =
1862 d3dx9_mesh_QueryInterface,
1863 d3dx9_mesh_AddRef,
1864 d3dx9_mesh_Release,
1865 d3dx9_mesh_DrawSubset,
1866 d3dx9_mesh_GetNumFaces,
1867 d3dx9_mesh_GetNumVertices,
1868 d3dx9_mesh_GetFVF,
1869 d3dx9_mesh_GetDeclaration,
1870 d3dx9_mesh_GetNumBytesPerVertex,
1871 d3dx9_mesh_GetOptions,
1872 d3dx9_mesh_GetDevice,
1873 d3dx9_mesh_CloneMeshFVF,
1874 d3dx9_mesh_CloneMesh,
1875 d3dx9_mesh_GetVertexBuffer,
1876 d3dx9_mesh_GetIndexBuffer,
1877 d3dx9_mesh_LockVertexBuffer,
1878 d3dx9_mesh_UnlockVertexBuffer,
1879 d3dx9_mesh_LockIndexBuffer,
1880 d3dx9_mesh_UnlockIndexBuffer,
1881 d3dx9_mesh_GetAttributeTable,
1882 d3dx9_mesh_ConvertPointRepsToAdjacency,
1883 d3dx9_mesh_ConvertAdjacencyToPointReps,
1884 d3dx9_mesh_GenerateAdjacency,
1885 d3dx9_mesh_UpdateSemantics,
1886 d3dx9_mesh_LockAttributeBuffer,
1887 d3dx9_mesh_UnlockAttributeBuffer,
1888 d3dx9_mesh_Optimize,
1889 d3dx9_mesh_OptimizeInplace,
1890 d3dx9_mesh_SetAttributeTable,
1894 /* Algorithm taken from the article: An Efficient and Robust Ray-Box Intersection Algorithm
1895 Amy Williams University of Utah
1896 Steve Barrus University of Utah
1897 R. Keith Morley University of Utah
1898 Peter Shirley University of Utah
1900 International Conference on Computer Graphics and Interactive Techniques archive
1901 ACM SIGGRAPH 2005 Courses
1902 Los Angeles, California
1904 This algorithm is free of patents or of copyrights, as confirmed by Peter Shirley himself.
1906 Algorithm: Consider the box as the intersection of three slabs. Clip the ray
1907 against each slab, if there's anything left of the ray after we're
1908 done we've got an intersection of the ray with the box. */
1909 BOOL WINAPI D3DXBoxBoundProbe(const D3DXVECTOR3 *pmin, const D3DXVECTOR3 *pmax,
1910 const D3DXVECTOR3 *prayposition, const D3DXVECTOR3 *praydirection)
1912 FLOAT div, tmin, tmax, tymin, tymax, tzmin, tzmax;
1914 div = 1.0f / praydirection->x;
1915 if ( div >= 0.0f )
1917 tmin = ( pmin->x - prayposition->x ) * div;
1918 tmax = ( pmax->x - prayposition->x ) * div;
1920 else
1922 tmin = ( pmax->x - prayposition->x ) * div;
1923 tmax = ( pmin->x - prayposition->x ) * div;
1926 if ( tmax < 0.0f ) return FALSE;
1928 div = 1.0f / praydirection->y;
1929 if ( div >= 0.0f )
1931 tymin = ( pmin->y - prayposition->y ) * div;
1932 tymax = ( pmax->y - prayposition->y ) * div;
1934 else
1936 tymin = ( pmax->y - prayposition->y ) * div;
1937 tymax = ( pmin->y - prayposition->y ) * div;
1940 if ( ( tymax < 0.0f ) || ( tmin > tymax ) || ( tymin > tmax ) ) return FALSE;
1942 if ( tymin > tmin ) tmin = tymin;
1943 if ( tymax < tmax ) tmax = tymax;
1945 div = 1.0f / praydirection->z;
1946 if ( div >= 0.0f )
1948 tzmin = ( pmin->z - prayposition->z ) * div;
1949 tzmax = ( pmax->z - prayposition->z ) * div;
1951 else
1953 tzmin = ( pmax->z - prayposition->z ) * div;
1954 tzmax = ( pmin->z - prayposition->z ) * div;
1957 if ( (tzmax < 0.0f ) || ( tmin > tzmax ) || ( tzmin > tmax ) ) return FALSE;
1959 return TRUE;
1962 HRESULT WINAPI D3DXComputeBoundingBox(const D3DXVECTOR3 *pfirstposition,
1963 DWORD numvertices, DWORD dwstride, D3DXVECTOR3 *pmin, D3DXVECTOR3 *pmax)
1965 D3DXVECTOR3 vec;
1966 unsigned int i;
1968 if( !pfirstposition || !pmin || !pmax ) return D3DERR_INVALIDCALL;
1970 *pmin = *pfirstposition;
1971 *pmax = *pmin;
1973 for(i=0; i<numvertices; i++)
1975 vec = *( (const D3DXVECTOR3*)((const char*)pfirstposition + dwstride * i) );
1977 if ( vec.x < pmin->x ) pmin->x = vec.x;
1978 if ( vec.x > pmax->x ) pmax->x = vec.x;
1980 if ( vec.y < pmin->y ) pmin->y = vec.y;
1981 if ( vec.y > pmax->y ) pmax->y = vec.y;
1983 if ( vec.z < pmin->z ) pmin->z = vec.z;
1984 if ( vec.z > pmax->z ) pmax->z = vec.z;
1987 return D3D_OK;
1990 HRESULT WINAPI D3DXComputeBoundingSphere(const D3DXVECTOR3 *pfirstposition,
1991 DWORD numvertices, DWORD dwstride, D3DXVECTOR3 *pcenter, float *pradius)
1993 D3DXVECTOR3 temp;
1994 FLOAT d;
1995 unsigned int i;
1997 if( !pfirstposition || !pcenter || !pradius ) return D3DERR_INVALIDCALL;
1999 temp.x = 0.0f;
2000 temp.y = 0.0f;
2001 temp.z = 0.0f;
2002 *pradius = 0.0f;
2004 for(i=0; i<numvertices; i++)
2005 D3DXVec3Add(&temp, &temp, (const D3DXVECTOR3*)((const char*)pfirstposition + dwstride * i));
2007 D3DXVec3Scale(pcenter, &temp, 1.0f / numvertices);
2009 for(i=0; i<numvertices; i++)
2011 d = D3DXVec3Length(D3DXVec3Subtract(&temp, (const D3DXVECTOR3*)((const char*)pfirstposition + dwstride * i), pcenter));
2012 if ( d > *pradius ) *pradius = d;
2014 return D3D_OK;
2017 static void append_decl_element(D3DVERTEXELEMENT9 *declaration, UINT *idx, UINT *offset,
2018 D3DDECLTYPE type, D3DDECLUSAGE usage, UINT usage_idx)
2020 declaration[*idx].Stream = 0;
2021 declaration[*idx].Offset = *offset;
2022 declaration[*idx].Type = type;
2023 declaration[*idx].Method = D3DDECLMETHOD_DEFAULT;
2024 declaration[*idx].Usage = usage;
2025 declaration[*idx].UsageIndex = usage_idx;
2027 *offset += d3dx_decltype_size[type];
2028 ++(*idx);
2031 /*************************************************************************
2032 * D3DXDeclaratorFromFVF
2034 HRESULT WINAPI D3DXDeclaratorFromFVF(DWORD fvf, D3DVERTEXELEMENT9 declaration[MAX_FVF_DECL_SIZE])
2036 static const D3DVERTEXELEMENT9 end_element = D3DDECL_END();
2037 DWORD tex_count = (fvf & D3DFVF_TEXCOUNT_MASK) >> D3DFVF_TEXCOUNT_SHIFT;
2038 unsigned int offset = 0;
2039 unsigned int idx = 0;
2040 unsigned int i;
2042 TRACE("fvf %#lx, declaration %p.\n", fvf, declaration);
2044 if (fvf & (D3DFVF_RESERVED0 | D3DFVF_RESERVED2)) return D3DERR_INVALIDCALL;
2046 if (fvf & D3DFVF_POSITION_MASK)
2048 BOOL has_blend = (fvf & D3DFVF_XYZB5) >= D3DFVF_XYZB1;
2049 DWORD blend_count = 1 + (((fvf & D3DFVF_XYZB5) - D3DFVF_XYZB1) >> 1);
2050 BOOL has_blend_idx = (fvf & D3DFVF_LASTBETA_D3DCOLOR) || (fvf & D3DFVF_LASTBETA_UBYTE4);
2052 if (has_blend_idx) --blend_count;
2054 if ((fvf & D3DFVF_POSITION_MASK) == D3DFVF_XYZW
2055 || (has_blend && blend_count > 4))
2056 return D3DERR_INVALIDCALL;
2058 if ((fvf & D3DFVF_POSITION_MASK) == D3DFVF_XYZRHW)
2059 append_decl_element(declaration, &idx, &offset, D3DDECLTYPE_FLOAT4, D3DDECLUSAGE_POSITIONT, 0);
2060 else
2061 append_decl_element(declaration, &idx, &offset, D3DDECLTYPE_FLOAT3, D3DDECLUSAGE_POSITION, 0);
2063 if (has_blend)
2065 switch (blend_count)
2067 case 0:
2068 break;
2069 case 1:
2070 append_decl_element(declaration, &idx, &offset, D3DDECLTYPE_FLOAT1, D3DDECLUSAGE_BLENDWEIGHT, 0);
2071 break;
2072 case 2:
2073 append_decl_element(declaration, &idx, &offset, D3DDECLTYPE_FLOAT2, D3DDECLUSAGE_BLENDWEIGHT, 0);
2074 break;
2075 case 3:
2076 append_decl_element(declaration, &idx, &offset, D3DDECLTYPE_FLOAT3, D3DDECLUSAGE_BLENDWEIGHT, 0);
2077 break;
2078 case 4:
2079 append_decl_element(declaration, &idx, &offset, D3DDECLTYPE_FLOAT4, D3DDECLUSAGE_BLENDWEIGHT, 0);
2080 break;
2081 default:
2082 ERR("Invalid blend count %lu.\n", blend_count);
2083 break;
2086 if (has_blend_idx)
2088 if (fvf & D3DFVF_LASTBETA_UBYTE4)
2089 append_decl_element(declaration, &idx, &offset, D3DDECLTYPE_UBYTE4, D3DDECLUSAGE_BLENDINDICES, 0);
2090 else if (fvf & D3DFVF_LASTBETA_D3DCOLOR)
2091 append_decl_element(declaration, &idx, &offset, D3DDECLTYPE_D3DCOLOR, D3DDECLUSAGE_BLENDINDICES, 0);
2096 if (fvf & D3DFVF_NORMAL)
2097 append_decl_element(declaration, &idx, &offset, D3DDECLTYPE_FLOAT3, D3DDECLUSAGE_NORMAL, 0);
2098 if (fvf & D3DFVF_PSIZE)
2099 append_decl_element(declaration, &idx, &offset, D3DDECLTYPE_FLOAT1, D3DDECLUSAGE_PSIZE, 0);
2100 if (fvf & D3DFVF_DIFFUSE)
2101 append_decl_element(declaration, &idx, &offset, D3DDECLTYPE_D3DCOLOR, D3DDECLUSAGE_COLOR, 0);
2102 if (fvf & D3DFVF_SPECULAR)
2103 append_decl_element(declaration, &idx, &offset, D3DDECLTYPE_D3DCOLOR, D3DDECLUSAGE_COLOR, 1);
2105 for (i = 0; i < tex_count; ++i)
2107 switch ((fvf >> (16 + 2 * i)) & 0x03)
2109 case D3DFVF_TEXTUREFORMAT1:
2110 append_decl_element(declaration, &idx, &offset, D3DDECLTYPE_FLOAT1, D3DDECLUSAGE_TEXCOORD, i);
2111 break;
2112 case D3DFVF_TEXTUREFORMAT2:
2113 append_decl_element(declaration, &idx, &offset, D3DDECLTYPE_FLOAT2, D3DDECLUSAGE_TEXCOORD, i);
2114 break;
2115 case D3DFVF_TEXTUREFORMAT3:
2116 append_decl_element(declaration, &idx, &offset, D3DDECLTYPE_FLOAT3, D3DDECLUSAGE_TEXCOORD, i);
2117 break;
2118 case D3DFVF_TEXTUREFORMAT4:
2119 append_decl_element(declaration, &idx, &offset, D3DDECLTYPE_FLOAT4, D3DDECLUSAGE_TEXCOORD, i);
2120 break;
2124 declaration[idx] = end_element;
2126 return D3D_OK;
2129 /*************************************************************************
2130 * D3DXFVFFromDeclarator
2132 HRESULT WINAPI D3DXFVFFromDeclarator(const D3DVERTEXELEMENT9 *declaration, DWORD *fvf)
2134 unsigned int i = 0, texture, offset;
2136 TRACE("(%p, %p)\n", declaration, fvf);
2138 *fvf = 0;
2139 if (declaration[0].Type == D3DDECLTYPE_FLOAT3 && declaration[0].Usage == D3DDECLUSAGE_POSITION)
2141 if ((declaration[1].Type == D3DDECLTYPE_FLOAT4 && declaration[1].Usage == D3DDECLUSAGE_BLENDWEIGHT &&
2142 declaration[1].UsageIndex == 0) &&
2143 (declaration[2].Type == D3DDECLTYPE_FLOAT1 && declaration[2].Usage == D3DDECLUSAGE_BLENDINDICES &&
2144 declaration[2].UsageIndex == 0))
2146 return D3DERR_INVALIDCALL;
2148 else if ((declaration[1].Type == D3DDECLTYPE_UBYTE4 || declaration[1].Type == D3DDECLTYPE_D3DCOLOR) &&
2149 declaration[1].Usage == D3DDECLUSAGE_BLENDINDICES && declaration[1].UsageIndex == 0)
2151 if (declaration[1].Type == D3DDECLTYPE_UBYTE4)
2153 *fvf |= D3DFVF_XYZB1 | D3DFVF_LASTBETA_UBYTE4;
2155 else
2157 *fvf |= D3DFVF_XYZB1 | D3DFVF_LASTBETA_D3DCOLOR;
2159 i = 2;
2161 else if (declaration[1].Type <= D3DDECLTYPE_FLOAT4 && declaration[1].Usage == D3DDECLUSAGE_BLENDWEIGHT &&
2162 declaration[1].UsageIndex == 0)
2164 if ((declaration[2].Type == D3DDECLTYPE_UBYTE4 || declaration[2].Type == D3DDECLTYPE_D3DCOLOR) &&
2165 declaration[2].Usage == D3DDECLUSAGE_BLENDINDICES && declaration[2].UsageIndex == 0)
2167 if (declaration[2].Type == D3DDECLTYPE_UBYTE4)
2169 *fvf |= D3DFVF_LASTBETA_UBYTE4;
2171 else
2173 *fvf |= D3DFVF_LASTBETA_D3DCOLOR;
2175 switch (declaration[1].Type)
2177 case D3DDECLTYPE_FLOAT1: *fvf |= D3DFVF_XYZB2; break;
2178 case D3DDECLTYPE_FLOAT2: *fvf |= D3DFVF_XYZB3; break;
2179 case D3DDECLTYPE_FLOAT3: *fvf |= D3DFVF_XYZB4; break;
2180 case D3DDECLTYPE_FLOAT4: *fvf |= D3DFVF_XYZB5; break;
2182 i = 3;
2184 else
2186 switch (declaration[1].Type)
2188 case D3DDECLTYPE_FLOAT1: *fvf |= D3DFVF_XYZB1; break;
2189 case D3DDECLTYPE_FLOAT2: *fvf |= D3DFVF_XYZB2; break;
2190 case D3DDECLTYPE_FLOAT3: *fvf |= D3DFVF_XYZB3; break;
2191 case D3DDECLTYPE_FLOAT4: *fvf |= D3DFVF_XYZB4; break;
2193 i = 2;
2196 else
2198 *fvf |= D3DFVF_XYZ;
2199 i = 1;
2202 else if (declaration[0].Type == D3DDECLTYPE_FLOAT4 && declaration[0].Usage == D3DDECLUSAGE_POSITIONT &&
2203 declaration[0].UsageIndex == 0)
2205 *fvf |= D3DFVF_XYZRHW;
2206 i = 1;
2209 if (declaration[i].Type == D3DDECLTYPE_FLOAT3 && declaration[i].Usage == D3DDECLUSAGE_NORMAL)
2211 *fvf |= D3DFVF_NORMAL;
2212 i++;
2214 if (declaration[i].Type == D3DDECLTYPE_FLOAT1 && declaration[i].Usage == D3DDECLUSAGE_PSIZE &&
2215 declaration[i].UsageIndex == 0)
2217 *fvf |= D3DFVF_PSIZE;
2218 i++;
2220 if (declaration[i].Type == D3DDECLTYPE_D3DCOLOR && declaration[i].Usage == D3DDECLUSAGE_COLOR &&
2221 declaration[i].UsageIndex == 0)
2223 *fvf |= D3DFVF_DIFFUSE;
2224 i++;
2226 if (declaration[i].Type == D3DDECLTYPE_D3DCOLOR && declaration[i].Usage == D3DDECLUSAGE_COLOR &&
2227 declaration[i].UsageIndex == 1)
2229 *fvf |= D3DFVF_SPECULAR;
2230 i++;
2233 for (texture = 0; texture < D3DDP_MAXTEXCOORD; i++, texture++)
2235 if (declaration[i].Stream == 0xFF)
2237 break;
2239 else if (declaration[i].Type == D3DDECLTYPE_FLOAT1 && declaration[i].Usage == D3DDECLUSAGE_TEXCOORD &&
2240 declaration[i].UsageIndex == texture)
2242 *fvf |= D3DFVF_TEXCOORDSIZE1(declaration[i].UsageIndex);
2244 else if (declaration[i].Type == D3DDECLTYPE_FLOAT2 && declaration[i].Usage == D3DDECLUSAGE_TEXCOORD &&
2245 declaration[i].UsageIndex == texture)
2247 *fvf |= D3DFVF_TEXCOORDSIZE2(declaration[i].UsageIndex);
2249 else if (declaration[i].Type == D3DDECLTYPE_FLOAT3 && declaration[i].Usage == D3DDECLUSAGE_TEXCOORD &&
2250 declaration[i].UsageIndex == texture)
2252 *fvf |= D3DFVF_TEXCOORDSIZE3(declaration[i].UsageIndex);
2254 else if (declaration[i].Type == D3DDECLTYPE_FLOAT4 && declaration[i].Usage == D3DDECLUSAGE_TEXCOORD &&
2255 declaration[i].UsageIndex == texture)
2257 *fvf |= D3DFVF_TEXCOORDSIZE4(declaration[i].UsageIndex);
2259 else
2261 return D3DERR_INVALIDCALL;
2265 *fvf |= (texture << D3DFVF_TEXCOUNT_SHIFT);
2267 for (offset = 0, i = 0; declaration[i].Stream != 0xFF;
2268 offset += d3dx_decltype_size[declaration[i].Type], i++)
2270 if (declaration[i].Offset != offset)
2272 return D3DERR_INVALIDCALL;
2276 return D3D_OK;
2279 /*************************************************************************
2280 * D3DXGetFVFVertexSize
2282 static UINT Get_TexCoord_Size_From_FVF(DWORD FVF, int tex_num)
2284 return (((((FVF) >> (16 + (2 * (tex_num)))) + 1) & 0x03) + 1);
2287 UINT WINAPI D3DXGetFVFVertexSize(DWORD FVF)
2289 DWORD size = 0;
2290 UINT i;
2291 UINT numTextures = (FVF & D3DFVF_TEXCOUNT_MASK) >> D3DFVF_TEXCOUNT_SHIFT;
2293 if (FVF & D3DFVF_NORMAL) size += sizeof(D3DXVECTOR3);
2294 if (FVF & D3DFVF_DIFFUSE) size += sizeof(DWORD);
2295 if (FVF & D3DFVF_SPECULAR) size += sizeof(DWORD);
2296 if (FVF & D3DFVF_PSIZE) size += sizeof(DWORD);
2298 switch (FVF & D3DFVF_POSITION_MASK)
2300 case D3DFVF_XYZ: size += sizeof(D3DXVECTOR3); break;
2301 case D3DFVF_XYZRHW: size += 4 * sizeof(FLOAT); break;
2302 case D3DFVF_XYZB1: size += 4 * sizeof(FLOAT); break;
2303 case D3DFVF_XYZB2: size += 5 * sizeof(FLOAT); break;
2304 case D3DFVF_XYZB3: size += 6 * sizeof(FLOAT); break;
2305 case D3DFVF_XYZB4: size += 7 * sizeof(FLOAT); break;
2306 case D3DFVF_XYZB5: size += 8 * sizeof(FLOAT); break;
2307 case D3DFVF_XYZW: size += 4 * sizeof(FLOAT); break;
2310 for (i = 0; i < numTextures; i++)
2312 size += Get_TexCoord_Size_From_FVF(FVF, i) * sizeof(FLOAT);
2315 return size;
2318 /*************************************************************************
2319 * D3DXGetDeclVertexSize
2321 UINT WINAPI D3DXGetDeclVertexSize(const D3DVERTEXELEMENT9 *decl, DWORD stream_idx)
2323 const D3DVERTEXELEMENT9 *element;
2324 UINT size = 0;
2326 TRACE("decl %p, stream_idx %lu.\n", decl, stream_idx);
2328 if (!decl) return 0;
2330 for (element = decl; element->Stream != 0xff; ++element)
2332 UINT type_size;
2334 if (element->Stream != stream_idx) continue;
2336 if (element->Type >= ARRAY_SIZE(d3dx_decltype_size))
2338 FIXME("Unhandled element type %#x, size will be incorrect.\n", element->Type);
2339 continue;
2342 type_size = d3dx_decltype_size[element->Type];
2343 if (element->Offset + type_size > size) size = element->Offset + type_size;
2346 return size;
2349 /*************************************************************************
2350 * D3DXGetDeclLength
2352 UINT WINAPI D3DXGetDeclLength(const D3DVERTEXELEMENT9 *decl)
2354 const D3DVERTEXELEMENT9 *element;
2356 TRACE("decl %p\n", decl);
2358 /* null decl results in exception on Windows XP */
2360 for (element = decl; element->Stream != 0xff; ++element);
2362 return element - decl;
2365 BOOL WINAPI D3DXIntersectTri(const D3DXVECTOR3 *p0, const D3DXVECTOR3 *p1, const D3DXVECTOR3 *p2,
2366 const D3DXVECTOR3 *praypos, const D3DXVECTOR3 *praydir, float *pu, float *pv, float *pdist)
2368 D3DXMATRIX m;
2369 D3DXVECTOR4 vec;
2371 TRACE("p0 %p, p1 %p, p2 %p, praypos %p, praydir %p, pu %p, pv %p, pdist %p.\n",
2372 p0, p1, p2, praypos, praydir, pu, pv, pdist);
2374 m.m[0][0] = p1->x - p0->x;
2375 m.m[1][0] = p2->x - p0->x;
2376 m.m[2][0] = -praydir->x;
2377 m.m[3][0] = 0.0f;
2378 m.m[0][1] = p1->y - p0->y;
2379 m.m[1][1] = p2->y - p0->y;
2380 m.m[2][1] = -praydir->y;
2381 m.m[3][1] = 0.0f;
2382 m.m[0][2] = p1->z - p0->z;
2383 m.m[1][2] = p2->z - p0->z;
2384 m.m[2][2] = -praydir->z;
2385 m.m[3][2] = 0.0f;
2386 m.m[0][3] = 0.0f;
2387 m.m[1][3] = 0.0f;
2388 m.m[2][3] = 0.0f;
2389 m.m[3][3] = 1.0f;
2391 vec.x = praypos->x - p0->x;
2392 vec.y = praypos->y - p0->y;
2393 vec.z = praypos->z - p0->z;
2394 vec.w = 0.0f;
2396 if ( D3DXMatrixInverse(&m, NULL, &m) )
2398 D3DXVec4Transform(&vec, &vec, &m);
2399 if ( (vec.x >= 0.0f) && (vec.y >= 0.0f) && (vec.x + vec.y <= 1.0f) && (vec.z >= 0.0f) )
2401 if (pu) *pu = vec.x;
2402 if (pv) *pv = vec.y;
2403 if (pdist) *pdist = fabsf( vec.z );
2404 return TRUE;
2408 return FALSE;
2411 BOOL WINAPI D3DXSphereBoundProbe(const D3DXVECTOR3 *center, float radius,
2412 const D3DXVECTOR3 *ray_position, const D3DXVECTOR3 *ray_direction)
2414 D3DXVECTOR3 difference = {0};
2415 float a, b, c, d;
2417 D3DXVec3Subtract(&difference, ray_position, center);
2418 c = D3DXVec3LengthSq(&difference) - radius * radius;
2419 if (c < 0.0f)
2420 return TRUE;
2421 a = D3DXVec3LengthSq(ray_direction);
2422 b = D3DXVec3Dot(&difference, ray_direction);
2423 d = b * b - a * c;
2425 return d >= 0.0f && (b <= 0.0f || d > b * b);
2428 /*************************************************************************
2429 * D3DXCreateMesh
2431 HRESULT WINAPI D3DXCreateMesh(DWORD numfaces, DWORD numvertices, DWORD options,
2432 const D3DVERTEXELEMENT9 *declaration, struct IDirect3DDevice9 *device, struct ID3DXMesh **mesh)
2434 HRESULT hr;
2435 DWORD fvf;
2436 IDirect3DVertexDeclaration9 *vertex_declaration;
2437 UINT vertex_declaration_size;
2438 UINT num_elem;
2439 IDirect3DVertexBuffer9 *vertex_buffer;
2440 IDirect3DIndexBuffer9 *index_buffer;
2441 DWORD *attrib_buffer;
2442 struct d3dx9_mesh *object;
2443 DWORD index_usage = 0;
2444 D3DPOOL index_pool = D3DPOOL_DEFAULT;
2445 D3DFORMAT index_format = D3DFMT_INDEX16;
2446 DWORD vertex_usage = 0;
2447 D3DPOOL vertex_pool = D3DPOOL_DEFAULT;
2448 int i;
2450 TRACE("numfaces %lu, numvertices %lu, options %#lx, declaration %p, device %p, mesh %p.\n",
2451 numfaces, numvertices, options, declaration, device, mesh);
2453 if (numfaces == 0 || numvertices == 0 || declaration == NULL || device == NULL || mesh == NULL ||
2454 /* D3DXMESH_VB_SHARE is for cloning, and D3DXMESH_USEHWONLY is for ConvertToBlendedMesh */
2455 (options & (D3DXMESH_VB_SHARE | D3DXMESH_USEHWONLY | 0xfffe0000)))
2457 return D3DERR_INVALIDCALL;
2459 for (i = 0; declaration[i].Stream != 0xff; i++)
2460 if (declaration[i].Stream != 0)
2461 return D3DERR_INVALIDCALL;
2462 num_elem = i + 1;
2464 if (options & D3DXMESH_32BIT)
2465 index_format = D3DFMT_INDEX32;
2467 if (options & D3DXMESH_DONOTCLIP) {
2468 index_usage |= D3DUSAGE_DONOTCLIP;
2469 vertex_usage |= D3DUSAGE_DONOTCLIP;
2471 if (options & D3DXMESH_POINTS) {
2472 index_usage |= D3DUSAGE_POINTS;
2473 vertex_usage |= D3DUSAGE_POINTS;
2475 if (options & D3DXMESH_RTPATCHES) {
2476 index_usage |= D3DUSAGE_RTPATCHES;
2477 vertex_usage |= D3DUSAGE_RTPATCHES;
2479 if (options & D3DXMESH_NPATCHES) {
2480 index_usage |= D3DUSAGE_NPATCHES;
2481 vertex_usage |= D3DUSAGE_NPATCHES;
2484 if (options & D3DXMESH_VB_SYSTEMMEM)
2485 vertex_pool = D3DPOOL_SYSTEMMEM;
2486 else if (options & D3DXMESH_VB_MANAGED)
2487 vertex_pool = D3DPOOL_MANAGED;
2489 if (options & D3DXMESH_VB_WRITEONLY)
2490 vertex_usage |= D3DUSAGE_WRITEONLY;
2491 if (options & D3DXMESH_VB_DYNAMIC)
2492 vertex_usage |= D3DUSAGE_DYNAMIC;
2493 if (options & D3DXMESH_VB_SOFTWAREPROCESSING)
2494 vertex_usage |= D3DUSAGE_SOFTWAREPROCESSING;
2496 if (options & D3DXMESH_IB_SYSTEMMEM)
2497 index_pool = D3DPOOL_SYSTEMMEM;
2498 else if (options & D3DXMESH_IB_MANAGED)
2499 index_pool = D3DPOOL_MANAGED;
2501 if (options & D3DXMESH_IB_WRITEONLY)
2502 index_usage |= D3DUSAGE_WRITEONLY;
2503 if (options & D3DXMESH_IB_DYNAMIC)
2504 index_usage |= D3DUSAGE_DYNAMIC;
2505 if (options & D3DXMESH_IB_SOFTWAREPROCESSING)
2506 index_usage |= D3DUSAGE_SOFTWAREPROCESSING;
2508 hr = D3DXFVFFromDeclarator(declaration, &fvf);
2509 if (hr != D3D_OK)
2511 fvf = 0;
2514 if (FAILED(hr = IDirect3DDevice9_CreateVertexDeclaration(device, declaration, &vertex_declaration)))
2516 WARN("Failed to create vertex declaration, hr %#lx.\n", hr);
2517 return hr;
2519 vertex_declaration_size = D3DXGetDeclVertexSize(declaration, declaration[0].Stream);
2521 if (FAILED(hr = IDirect3DDevice9_CreateVertexBuffer(device, numvertices * vertex_declaration_size,
2522 vertex_usage, fvf, vertex_pool, &vertex_buffer, NULL)))
2524 WARN("Failed to create vertex buffer, hr %#lx.\n", hr);
2525 IDirect3DVertexDeclaration9_Release(vertex_declaration);
2526 return hr;
2529 if (FAILED(hr = IDirect3DDevice9_CreateIndexBuffer(device,
2530 numfaces * 3 * ((index_format == D3DFMT_INDEX16) ? 2 : 4), index_usage, index_format,
2531 index_pool, &index_buffer, NULL)))
2533 WARN("Failed to create index buffer, hr %#lx.\n", hr);
2534 IDirect3DVertexBuffer9_Release(vertex_buffer);
2535 IDirect3DVertexDeclaration9_Release(vertex_declaration);
2536 return hr;
2539 attrib_buffer = calloc(numfaces, sizeof(*attrib_buffer));
2540 object = calloc(1, sizeof(*object));
2541 if (object == NULL || attrib_buffer == NULL)
2543 free(object);
2544 free(attrib_buffer);
2545 IDirect3DIndexBuffer9_Release(index_buffer);
2546 IDirect3DVertexBuffer9_Release(vertex_buffer);
2547 IDirect3DVertexDeclaration9_Release(vertex_declaration);
2548 *mesh = NULL;
2549 return E_OUTOFMEMORY;
2551 object->ID3DXMesh_iface.lpVtbl = &D3DXMesh_Vtbl;
2552 object->ref = 1;
2554 object->numfaces = numfaces;
2555 object->numvertices = numvertices;
2556 object->options = options;
2557 object->fvf = fvf;
2558 object->device = device;
2559 IDirect3DDevice9_AddRef(device);
2561 copy_declaration(object->cached_declaration, declaration, num_elem);
2562 object->vertex_declaration = vertex_declaration;
2563 object->vertex_declaration_size = vertex_declaration_size;
2564 object->num_elem = num_elem;
2565 object->vertex_buffer = vertex_buffer;
2566 object->index_buffer = index_buffer;
2567 object->attrib_buffer = attrib_buffer;
2569 *mesh = &object->ID3DXMesh_iface;
2571 return D3D_OK;
2574 /*************************************************************************
2575 * D3DXCreateMeshFVF
2577 HRESULT WINAPI D3DXCreateMeshFVF(DWORD face_count, DWORD vertex_count, DWORD options,
2578 DWORD fvf, struct IDirect3DDevice9 *device, struct ID3DXMesh **mesh)
2580 HRESULT hr;
2581 D3DVERTEXELEMENT9 declaration[MAX_FVF_DECL_SIZE];
2583 TRACE("face_count %lu, vertex_count %lu, options %#lx, fvf %#lx, device %p, mesh %p.\n",
2584 face_count, vertex_count, options, fvf, device, mesh);
2586 hr = D3DXDeclaratorFromFVF(fvf, declaration);
2587 if (FAILED(hr)) return hr;
2589 return D3DXCreateMesh(face_count, vertex_count, options, declaration, device, mesh);
2593 struct mesh_data {
2594 unsigned int num_vertices;
2595 unsigned int num_poly_faces;
2596 unsigned int num_tri_faces;
2597 D3DXVECTOR3 *vertices;
2598 unsigned int *num_tri_per_face;
2599 DWORD *indices;
2601 DWORD fvf;
2603 /* optional mesh data */
2605 unsigned int num_normals;
2606 D3DXVECTOR3 *normals;
2607 DWORD *normal_indices;
2609 D3DXVECTOR2 *tex_coords;
2611 DWORD *vertex_colors;
2613 unsigned int num_materials;
2614 D3DXMATERIAL *materials;
2615 DWORD *material_indices;
2617 struct ID3DXSkinInfo *skin_info;
2618 unsigned int bone_count;
2619 unsigned int skin_weights_info_count;
2622 static HRESULT parse_texture_filename(ID3DXFileData *filedata, char **filename_out)
2624 HRESULT hr;
2625 SIZE_T data_size;
2626 BYTE *data;
2627 char *filename_in;
2629 /* template TextureFilename {
2630 * STRING filename;
2634 free(*filename_out);
2635 *filename_out = NULL;
2637 hr = filedata->lpVtbl->Lock(filedata, &data_size, (const void**)&data);
2638 if (FAILED(hr)) return hr;
2640 /* FIXME: String must be retrieved directly instead of through a pointer once ID3DXFILE is fixed */
2641 if (data_size < sizeof(filename_in))
2643 WARN("Truncated data (%Iu bytes).\n", data_size);
2644 filedata->lpVtbl->Unlock(filedata);
2645 return E_FAIL;
2647 filename_in = *(char **)data;
2649 if (!(*filename_out = strdup(filename_in))) {
2650 filedata->lpVtbl->Unlock(filedata);
2651 return E_OUTOFMEMORY;
2654 filedata->lpVtbl->Unlock(filedata);
2656 return D3D_OK;
2659 static HRESULT parse_material(ID3DXFileData *filedata, D3DXMATERIAL *material)
2661 HRESULT hr;
2662 SIZE_T data_size;
2663 const BYTE *data;
2664 GUID type;
2665 ID3DXFileData *child;
2666 SIZE_T i, nb_children;
2668 material->pTextureFilename = NULL;
2670 hr = filedata->lpVtbl->Lock(filedata, &data_size, (const void**)&data);
2671 if (FAILED(hr)) return hr;
2674 * template ColorRGBA {
2675 * FLOAT red;
2676 * FLOAT green;
2677 * FLOAT blue;
2678 * FLOAT alpha;
2680 * template ColorRGB {
2681 * FLOAT red;
2682 * FLOAT green;
2683 * FLOAT blue;
2685 * template Material {
2686 * ColorRGBA faceColor;
2687 * FLOAT power;
2688 * ColorRGB specularColor;
2689 * ColorRGB emissiveColor;
2690 * [ ... ]
2693 if (data_size != sizeof(float) * 11)
2695 WARN("Incorrect data size (%Id bytes).\n", data_size);
2696 filedata->lpVtbl->Unlock(filedata);
2697 return E_FAIL;
2700 memcpy(&material->MatD3D.Diffuse, data, sizeof(D3DCOLORVALUE));
2701 data += sizeof(D3DCOLORVALUE);
2702 material->MatD3D.Power = *(FLOAT*)data;
2703 data += sizeof(FLOAT);
2704 memcpy(&material->MatD3D.Specular, data, sizeof(FLOAT) * 3);
2705 material->MatD3D.Specular.a = 1.0f;
2706 data += 3 * sizeof(FLOAT);
2707 memcpy(&material->MatD3D.Emissive, data, sizeof(FLOAT) * 3);
2708 material->MatD3D.Emissive.a = 1.0f;
2709 material->MatD3D.Ambient.r = 0.0f;
2710 material->MatD3D.Ambient.g = 0.0f;
2711 material->MatD3D.Ambient.b = 0.0f;
2712 material->MatD3D.Ambient.a = 1.0f;
2714 filedata->lpVtbl->Unlock(filedata);
2716 hr = filedata->lpVtbl->GetChildren(filedata, &nb_children);
2717 if (FAILED(hr))
2718 return hr;
2720 for (i = 0; i < nb_children; i++)
2722 hr = filedata->lpVtbl->GetChild(filedata, i, &child);
2723 if (FAILED(hr))
2724 return hr;
2725 hr = child->lpVtbl->GetType(child, &type);
2726 if (FAILED(hr))
2727 goto err;
2729 if (IsEqualGUID(&type, &TID_D3DRMTextureFilename)) {
2730 hr = parse_texture_filename(child, &material->pTextureFilename);
2731 if (FAILED(hr))
2732 goto err;
2734 IUnknown_Release(child);
2736 return D3D_OK;
2738 err:
2739 IUnknown_Release(child);
2740 return hr;
2743 static void destroy_materials(struct mesh_data *mesh)
2745 unsigned int i;
2747 for (i = 0; i < mesh->num_materials; ++i)
2748 free(mesh->materials[i].pTextureFilename);
2749 free(mesh->materials);
2750 free(mesh->material_indices);
2751 mesh->num_materials = 0;
2752 mesh->materials = NULL;
2753 mesh->material_indices = NULL;
2756 static HRESULT parse_material_list(ID3DXFileData *filedata, struct mesh_data *mesh, DWORD flags)
2758 ID3DXFileData *child = NULL;
2759 unsigned int material_count;
2760 const uint32_t *in_ptr;
2761 SIZE_T nb_children;
2762 SIZE_T data_size;
2763 const void *data;
2764 unsigned int i;
2765 HRESULT hr;
2766 GUID type;
2768 if (!(flags & PROVIDE_MATERIALS))
2769 return S_OK;
2771 destroy_materials(mesh);
2773 hr = filedata->lpVtbl->Lock(filedata, &data_size, &data);
2774 if (FAILED(hr)) return hr;
2776 /* template MeshMaterialList {
2777 * DWORD nMaterials;
2778 * DWORD nFaceIndexes;
2779 * array DWORD faceIndexes[nFaceIndexes];
2780 * [ Material ]
2784 in_ptr = data;
2785 hr = E_FAIL;
2787 if (data_size < sizeof(uint32_t))
2789 WARN("Truncated data (%Id bytes).\n", data_size);
2790 goto end;
2792 material_count = *in_ptr++;
2793 if (!material_count) {
2794 hr = D3D_OK;
2795 goto end;
2798 if (data_size < 2 * sizeof(uint32_t))
2800 WARN("Truncated data (%Id bytes).\n", data_size);
2801 goto end;
2803 if (*in_ptr++ != mesh->num_poly_faces)
2805 WARN("Number of material face indices (%u) doesn't match number of faces (%u).\n",
2806 *(in_ptr - 1), mesh->num_poly_faces);
2807 goto end;
2809 if (data_size < 2 * sizeof(uint32_t) + mesh->num_poly_faces * sizeof(uint32_t))
2811 WARN("Truncated data (%Id bytes).\n", data_size);
2812 goto end;
2814 for (i = 0; i < mesh->num_poly_faces; ++i)
2816 if (*in_ptr++ >= material_count)
2818 WARN("Face %u: reference to undefined material %u (only %u materials).\n",
2819 i, *(in_ptr - 1), material_count);
2820 goto end;
2824 mesh->materials = malloc(material_count * sizeof(*mesh->materials));
2825 mesh->material_indices = malloc(mesh->num_poly_faces * sizeof(*mesh->material_indices));
2826 if (!mesh->materials || !mesh->material_indices) {
2827 hr = E_OUTOFMEMORY;
2828 goto end;
2830 memcpy(mesh->material_indices, (const uint32_t *)data + 2, mesh->num_poly_faces * sizeof(uint32_t));
2832 hr = filedata->lpVtbl->GetChildren(filedata, &nb_children);
2833 if (FAILED(hr))
2834 goto end;
2836 for (i = 0; i < nb_children; i++)
2838 hr = filedata->lpVtbl->GetChild(filedata, i, &child);
2839 if (FAILED(hr))
2840 goto end;
2841 hr = child->lpVtbl->GetType(child, &type);
2842 if (FAILED(hr))
2843 goto end;
2845 if (IsEqualGUID(&type, &TID_D3DRMMaterial))
2847 if (mesh->num_materials >= material_count)
2849 WARN("%u materials defined, only %u declared.\n", mesh->num_materials, material_count);
2850 hr = E_FAIL;
2851 goto end;
2853 hr = parse_material(child, &mesh->materials[mesh->num_materials++]);
2854 if (FAILED(hr))
2855 goto end;
2858 IUnknown_Release(child);
2859 child = NULL;
2861 if (material_count != mesh->num_materials)
2863 WARN("Only %u of %u materials defined.\n", material_count, mesh->num_materials);
2864 hr = E_FAIL;
2867 end:
2868 if (child)
2869 IUnknown_Release(child);
2870 filedata->lpVtbl->Unlock(filedata);
2871 return hr;
2874 static HRESULT parse_texture_coords(ID3DXFileData *filedata, struct mesh_data *mesh, DWORD flags)
2876 const uint32_t *data;
2877 SIZE_T data_size;
2878 HRESULT hr;
2880 free(mesh->tex_coords);
2881 mesh->tex_coords = NULL;
2883 hr = filedata->lpVtbl->Lock(filedata, &data_size, (const void **)&data);
2884 if (FAILED(hr)) return hr;
2886 /* template Coords2d {
2887 * FLOAT u;
2888 * FLOAT v;
2890 * template MeshTextureCoords {
2891 * DWORD nTextureCoords;
2892 * array Coords2d textureCoords[nTextureCoords];
2896 hr = E_FAIL;
2898 if (data_size < sizeof(uint32_t))
2900 WARN("Truncated data (%Id bytes).\n", data_size);
2901 goto end;
2903 if (*data != mesh->num_vertices)
2905 WARN("Number of texture coordinates (%u) doesn't match number of vertices (%u).\n",
2906 *data, mesh->num_vertices);
2907 goto end;
2909 ++data;
2910 if (data_size < sizeof(uint32_t) + mesh->num_vertices * sizeof(*mesh->tex_coords))
2912 WARN("Truncated data (%Id bytes).\n", data_size);
2913 goto end;
2916 mesh->tex_coords = malloc(mesh->num_vertices * sizeof(*mesh->tex_coords));
2917 if (!mesh->tex_coords) {
2918 hr = E_OUTOFMEMORY;
2919 goto end;
2921 memcpy(mesh->tex_coords, data, mesh->num_vertices * sizeof(*mesh->tex_coords));
2923 mesh->fvf |= D3DFVF_TEX1;
2925 hr = D3D_OK;
2927 end:
2928 filedata->lpVtbl->Unlock(filedata);
2929 return hr;
2932 static HRESULT parse_vertex_colors(ID3DXFileData *filedata, struct mesh_data *mesh, DWORD flags)
2934 unsigned int color_count, i;
2935 const uint32_t *data;
2936 SIZE_T data_size;
2937 HRESULT hr;
2939 free(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(uint32_t))
2959 WARN("Truncated data (%Id bytes).\n", data_size);
2960 goto end;
2962 color_count = *data;
2963 ++data;
2964 if (data_size < sizeof(uint32_t) + color_count * (sizeof(uint32_t) + sizeof(D3DCOLORVALUE)))
2966 WARN("Truncated data (%Id bytes).\n", data_size);
2967 goto end;
2970 mesh->vertex_colors = malloc(mesh->num_vertices * sizeof(uint32_t));
2971 if (!mesh->vertex_colors) {
2972 hr = E_OUTOFMEMORY;
2973 goto end;
2976 for (i = 0; i < mesh->num_vertices; i++)
2977 mesh->vertex_colors[i] = D3DCOLOR_ARGB(0, 0xff, 0xff, 0xff);
2978 for (i = 0; i < color_count; ++i)
2980 D3DCOLORVALUE color;
2981 unsigned int index = *data;
2983 ++data;
2984 if (index >= mesh->num_vertices)
2986 WARN("Vertex color %u references undefined vertex %u (only %u vertices).\n",
2987 i, index, mesh->num_vertices);
2988 goto end;
2990 memcpy(&color, data, sizeof(color));
2991 data += sizeof(color) / sizeof(*data);
2992 color.r = min(1.0f, max(0.0f, color.r));
2993 color.g = min(1.0f, max(0.0f, color.g));
2994 color.b = min(1.0f, max(0.0f, color.b));
2995 color.a = min(1.0f, max(0.0f, color.a));
2996 mesh->vertex_colors[index] = D3DCOLOR_ARGB((BYTE)(color.a * 255.0f + 0.5f),
2997 (BYTE)(color.r * 255.0f + 0.5f),
2998 (BYTE)(color.g * 255.0f + 0.5f),
2999 (BYTE)(color.b * 255.0f + 0.5f));
3002 mesh->fvf |= D3DFVF_DIFFUSE;
3004 hr = D3D_OK;
3006 end:
3007 filedata->lpVtbl->Unlock(filedata);
3008 return hr;
3011 static HRESULT parse_normals(ID3DXFileData *filedata, struct mesh_data *mesh, DWORD flags)
3013 unsigned int num_face_indices = mesh->num_poly_faces * 2 + mesh->num_tri_faces;
3014 DWORD *index_out_ptr;
3015 SIZE_T data_size;
3016 const BYTE *data;
3017 unsigned int i;
3018 HRESULT hr;
3020 free(mesh->normals);
3021 mesh->num_normals = 0;
3022 mesh->normals = NULL;
3023 mesh->normal_indices = NULL;
3024 mesh->fvf |= D3DFVF_NORMAL;
3026 hr = filedata->lpVtbl->Lock(filedata, &data_size, (const void **)&data);
3027 if (FAILED(hr)) return hr;
3029 /* template Vector {
3030 * FLOAT x;
3031 * FLOAT y;
3032 * FLOAT z;
3034 * template MeshFace {
3035 * DWORD nFaceVertexIndices;
3036 * array DWORD faceVertexIndices[nFaceVertexIndices];
3038 * template MeshNormals {
3039 * DWORD nNormals;
3040 * array Vector normals[nNormals];
3041 * DWORD nFaceNormals;
3042 * array MeshFace faceNormals[nFaceNormals];
3046 hr = E_FAIL;
3048 if (data_size < sizeof(uint32_t) * 2)
3050 WARN("Truncated data (%Id bytes).\n", data_size);
3051 goto end;
3053 mesh->num_normals = *(uint32_t *)data;
3054 data += sizeof(uint32_t);
3055 if (data_size < sizeof(uint32_t) * 2 + mesh->num_normals * sizeof(D3DXVECTOR3) +
3056 num_face_indices * sizeof(uint32_t))
3058 WARN("Truncated data (%Id bytes).\n", data_size);
3059 goto end;
3062 mesh->normals = malloc(mesh->num_normals * sizeof(D3DXVECTOR3));
3063 mesh->normal_indices = malloc(num_face_indices * sizeof(uint32_t));
3064 if (!mesh->normals || !mesh->normal_indices) {
3065 hr = E_OUTOFMEMORY;
3066 goto end;
3069 memcpy(mesh->normals, data, mesh->num_normals * sizeof(D3DXVECTOR3));
3070 data += mesh->num_normals * sizeof(D3DXVECTOR3);
3071 for (i = 0; i < mesh->num_normals; i++)
3072 D3DXVec3Normalize(&mesh->normals[i], &mesh->normals[i]);
3074 if (*(uint32_t *)data != mesh->num_poly_faces)
3076 WARN("Number of face normals (%u) doesn't match number of faces (%u).\n",
3077 *(uint32_t *)data, mesh->num_poly_faces);
3078 goto end;
3080 data += sizeof(uint32_t);
3081 index_out_ptr = mesh->normal_indices;
3082 for (i = 0; i < mesh->num_poly_faces; i++)
3084 unsigned int count = *(uint32_t *)data;
3085 unsigned int j;
3087 if (count != mesh->num_tri_per_face[i] + 2)
3089 WARN("Face %u: number of normals (%u) doesn't match number of vertices (%u).\n",
3090 i, count, mesh->num_tri_per_face[i] + 2);
3091 goto end;
3093 data += sizeof(uint32_t);
3095 for (j = 0; j < count; j++)
3097 uint32_t normal_index = *(uint32_t *)data;
3099 if (normal_index >= mesh->num_normals)
3101 WARN("Face %u, normal index %u: reference to undefined normal %u (only %u normals).\n",
3102 i, j, normal_index, mesh->num_normals);
3103 goto end;
3105 *index_out_ptr++ = normal_index;
3106 data += sizeof(uint32_t);
3110 hr = D3D_OK;
3112 end:
3113 filedata->lpVtbl->Unlock(filedata);
3114 return hr;
3117 static HRESULT parse_skin_mesh_header(ID3DXFileData *filedata, struct mesh_data *mesh_data, DWORD flags)
3119 const BYTE *data;
3120 SIZE_T data_size;
3121 HRESULT hr;
3123 TRACE("filedata %p, mesh_data %p.\n", filedata, mesh_data);
3125 if (!(flags & PROVIDE_SKININFO))
3126 return S_OK;
3128 if (mesh_data->skin_info)
3130 WARN("Skin mesh header already encountered\n");
3131 return E_FAIL;
3134 if (FAILED(hr = filedata->lpVtbl->Lock(filedata, &data_size, (const void **)&data)))
3135 return hr;
3137 if (data_size < sizeof(WORD) * 3)
3139 WARN("Truncated data (%Id bytes).\n", data_size);
3140 filedata->lpVtbl->Unlock(filedata);
3141 return E_FAIL;
3143 /* Skip nMaxSkinWeightsPerVertex and nMaxSkinWeightsPerFace */
3144 data += 2 * sizeof(WORD);
3145 mesh_data->bone_count = *(WORD *)data;
3146 hr = D3DXCreateSkinInfoFVF(mesh_data->num_vertices, mesh_data->fvf, mesh_data->bone_count,
3147 &mesh_data->skin_info);
3149 return hr;
3152 static HRESULT parse_skin_weights_info(ID3DXFileData *filedata, struct mesh_data *mesh_data, DWORD flags)
3154 unsigned int index = mesh_data->skin_weights_info_count;
3155 unsigned int influence_count;
3156 const char *name;
3157 const BYTE *data;
3158 SIZE_T data_size;
3159 HRESULT hr;
3161 TRACE("filedata %p, mesh_data %p, index %u.\n", filedata, mesh_data, index);
3163 if (!(flags & PROVIDE_SKININFO))
3164 return S_OK;
3166 if (!mesh_data->skin_info)
3168 WARN("Skin weights found but skin mesh header not encountered yet.\n");
3169 return E_FAIL;
3172 if (FAILED(hr = filedata->lpVtbl->Lock(filedata, &data_size, (const void **)&data)))
3173 return hr;
3175 /* FIXME: String will have to be retrieved directly instead of through a
3176 * pointer once our ID3DXFileData implementation is fixed. */
3177 name = *(const char **)data;
3178 data += sizeof(char *);
3180 influence_count = *(uint32_t *)data;
3181 data += sizeof(uint32_t);
3183 if (data_size < (sizeof(char *) + sizeof(uint32_t) + influence_count * (sizeof(uint32_t) + sizeof(float))
3184 + 16 * sizeof(float)))
3186 WARN("Truncated data (%Id bytes).\n", data_size);
3187 filedata->lpVtbl->Unlock(filedata);
3188 return E_FAIL;
3191 hr = mesh_data->skin_info->lpVtbl->SetBoneName(mesh_data->skin_info, index, name);
3192 if (SUCCEEDED(hr))
3193 hr = mesh_data->skin_info->lpVtbl->SetBoneInfluence(mesh_data->skin_info, index, influence_count,
3194 (const DWORD *)data, (const float *)(data + influence_count * sizeof(uint32_t)));
3195 if (SUCCEEDED(hr))
3196 hr = mesh_data->skin_info->lpVtbl->SetBoneOffsetMatrix(mesh_data->skin_info, index,
3197 (const D3DMATRIX *)(data + influence_count * (sizeof(uint32_t) + sizeof(float))));
3199 if (SUCCEEDED(hr))
3200 ++mesh_data->skin_weights_info_count;
3201 return hr;
3204 typedef HRESULT (*mesh_parse_func)(ID3DXFileData *, struct mesh_data *, DWORD);
3206 static mesh_parse_func mesh_get_parse_func(const GUID *type)
3208 static const struct
3210 const GUID *type;
3211 mesh_parse_func func;
3213 funcs[] =
3215 {&TID_D3DRMMeshNormals, parse_normals},
3216 {&TID_D3DRMMeshVertexColors, parse_vertex_colors},
3217 {&TID_D3DRMMeshTextureCoords, parse_texture_coords},
3218 {&TID_D3DRMMeshMaterialList, parse_material_list},
3219 {&DXFILEOBJ_XSkinMeshHeader, parse_skin_mesh_header},
3220 {&DXFILEOBJ_SkinWeights, parse_skin_weights_info},
3222 unsigned int i;
3224 for (i = 0; i < ARRAY_SIZE(funcs); ++i)
3225 if (IsEqualGUID(type, funcs[i].type))
3226 return funcs[i].func;
3228 return NULL;
3231 static HRESULT parse_mesh(ID3DXFileData *filedata, struct mesh_data *mesh_data, DWORD provide_flags)
3233 ID3DXFileData *child = NULL;
3234 mesh_parse_func parse_func;
3235 const BYTE *data, *in_ptr;
3236 DWORD *index_out_ptr;
3237 SIZE_T child_count;
3238 SIZE_T data_size;
3239 unsigned int i;
3240 HRESULT hr;
3241 GUID type;
3244 * template Mesh {
3245 * DWORD nVertices;
3246 * array Vector vertices[nVertices];
3247 * DWORD nFaces;
3248 * array MeshFace faces[nFaces];
3249 * [ ... ]
3253 mesh_data->skin_weights_info_count = 0;
3255 hr = filedata->lpVtbl->Lock(filedata, &data_size, (const void **)&data);
3256 if (FAILED(hr)) return hr;
3258 in_ptr = data;
3259 hr = E_FAIL;
3261 if (data_size < sizeof(uint32_t) * 2)
3263 WARN("Truncated data (%Id bytes).\n", data_size);
3264 goto end;
3266 mesh_data->num_vertices = *(uint32_t *)in_ptr;
3267 if (data_size < sizeof(uint32_t) * 2 + mesh_data->num_vertices * sizeof(D3DXVECTOR3))
3269 WARN("Truncated data (%Id bytes).\n", data_size);
3270 goto end;
3272 in_ptr += sizeof(uint32_t) + mesh_data->num_vertices * sizeof(D3DXVECTOR3);
3274 mesh_data->num_poly_faces = *(uint32_t *)in_ptr;
3275 in_ptr += sizeof(uint32_t);
3277 mesh_data->num_tri_faces = 0;
3278 for (i = 0; i < mesh_data->num_poly_faces; i++)
3280 unsigned int poly_vertices_count;
3281 unsigned int j;
3283 if (data_size - (in_ptr - data) < sizeof(uint32_t))
3285 WARN("Truncated data (%Id bytes).\n", data_size);
3286 goto end;
3288 poly_vertices_count = *(uint32_t *)in_ptr;
3289 in_ptr += sizeof(uint32_t);
3290 if (data_size - (in_ptr - data) < poly_vertices_count * sizeof(uint32_t))
3292 WARN("Truncated data (%Id bytes).\n", data_size);
3293 goto end;
3295 if (poly_vertices_count < 3)
3297 WARN("Face %u has only %u vertices.\n", i, poly_vertices_count);
3298 goto end;
3300 for (j = 0; j < poly_vertices_count; j++)
3302 if (*(uint32_t *)in_ptr >= mesh_data->num_vertices)
3304 WARN("Face %u, index %u: undefined vertex %u (only %u vertices).\n",
3305 i, j, *(uint32_t *)in_ptr, mesh_data->num_vertices);
3306 goto end;
3308 in_ptr += sizeof(uint32_t);
3310 mesh_data->num_tri_faces += poly_vertices_count - 2;
3313 mesh_data->fvf = D3DFVF_XYZ;
3315 mesh_data->vertices = malloc(mesh_data->num_vertices * sizeof(*mesh_data->vertices));
3316 mesh_data->num_tri_per_face = malloc(mesh_data->num_poly_faces * sizeof(*mesh_data->num_tri_per_face));
3317 mesh_data->indices = malloc((mesh_data->num_tri_faces + mesh_data->num_poly_faces * 2) * sizeof(*mesh_data->indices));
3318 if (!mesh_data->vertices || !mesh_data->num_tri_per_face || !mesh_data->indices) {
3319 hr = E_OUTOFMEMORY;
3320 goto end;
3323 in_ptr = data + sizeof(uint32_t);
3324 memcpy(mesh_data->vertices, in_ptr, mesh_data->num_vertices * sizeof(D3DXVECTOR3));
3325 in_ptr += mesh_data->num_vertices * sizeof(D3DXVECTOR3) + sizeof(uint32_t);
3327 index_out_ptr = mesh_data->indices;
3328 for (i = 0; i < mesh_data->num_poly_faces; i++)
3330 unsigned int count;
3332 count = *(uint32_t *)in_ptr;
3333 in_ptr += sizeof(uint32_t);
3334 mesh_data->num_tri_per_face[i] = count - 2;
3336 while (count--)
3338 *index_out_ptr++ = *(uint32_t *)in_ptr;
3339 in_ptr += sizeof(uint32_t);
3343 hr = filedata->lpVtbl->GetChildren(filedata, &child_count);
3344 if (FAILED(hr))
3345 goto end;
3347 for (i = 0; i < child_count; i++)
3349 hr = filedata->lpVtbl->GetChild(filedata, i, &child);
3350 if (FAILED(hr))
3351 goto end;
3352 hr = child->lpVtbl->GetType(child, &type);
3353 if (FAILED(hr))
3354 goto end;
3356 if ((parse_func = mesh_get_parse_func(&type)))
3357 hr = parse_func(child, mesh_data, provide_flags);
3358 if (FAILED(hr))
3359 goto end;
3361 IUnknown_Release(child);
3362 child = NULL;
3365 if (mesh_data->skin_info && (mesh_data->skin_weights_info_count != mesh_data->bone_count))
3367 WARN("Mismatch between skin weights info count %u and bones count %u from skin mesh header.\n",
3368 mesh_data->skin_weights_info_count, mesh_data->bone_count);
3369 hr = E_FAIL;
3370 goto end;
3373 if ((provide_flags & PROVIDE_SKININFO) && !mesh_data->skin_info)
3375 if (FAILED(hr = D3DXCreateSkinInfoFVF(mesh_data->num_vertices, mesh_data->fvf,
3376 mesh_data->bone_count, &mesh_data->skin_info)))
3377 goto end;
3380 hr = D3D_OK;
3382 end:
3383 if (child)
3384 IUnknown_Release(child);
3385 filedata->lpVtbl->Unlock(filedata);
3386 return hr;
3389 static HRESULT generate_effects(ID3DXBuffer *materials, DWORD num_materials,
3390 ID3DXBuffer **effects)
3392 HRESULT hr;
3393 D3DXEFFECTINSTANCE *effect_ptr;
3394 BYTE *out_ptr;
3395 const D3DXMATERIAL *material_ptr = ID3DXBuffer_GetBufferPointer(materials);
3396 static const struct {
3397 const char *param_name;
3398 DWORD name_size;
3399 DWORD num_bytes;
3400 DWORD value_offset;
3401 } material_effects[] = {
3402 #define EFFECT_TABLE_ENTRY(str, field) \
3403 {str, sizeof(str), sizeof(material_ptr->MatD3D.field), offsetof(D3DXMATERIAL, MatD3D.field)}
3404 EFFECT_TABLE_ENTRY("Diffuse", Diffuse),
3405 EFFECT_TABLE_ENTRY("Power", Power),
3406 EFFECT_TABLE_ENTRY("Specular", Specular),
3407 EFFECT_TABLE_ENTRY("Emissive", Emissive),
3408 EFFECT_TABLE_ENTRY("Ambient", Ambient),
3409 #undef EFFECT_TABLE_ENTRY
3411 static const char texture_paramname[] = "Texture0@Name";
3412 DWORD buffer_size;
3413 DWORD i;
3415 /* effects buffer layout:
3417 * D3DXEFFECTINSTANCE effects[num_materials];
3418 * for (effect in effects)
3420 * D3DXEFFECTDEFAULT defaults[effect.NumDefaults];
3421 * for (default in defaults)
3423 * *default.pParamName;
3424 * *default.pValue;
3428 buffer_size = sizeof(D3DXEFFECTINSTANCE);
3429 buffer_size += sizeof(D3DXEFFECTDEFAULT) * ARRAY_SIZE(material_effects);
3430 for (i = 0; i < ARRAY_SIZE(material_effects); i++) {
3431 buffer_size += material_effects[i].name_size;
3432 buffer_size += material_effects[i].num_bytes;
3434 buffer_size *= num_materials;
3435 for (i = 0; i < num_materials; i++) {
3436 if (material_ptr[i].pTextureFilename) {
3437 buffer_size += sizeof(D3DXEFFECTDEFAULT);
3438 buffer_size += sizeof(texture_paramname);
3439 buffer_size += strlen(material_ptr[i].pTextureFilename) + 1;
3443 hr = D3DXCreateBuffer(buffer_size, effects);
3444 if (FAILED(hr)) return hr;
3445 effect_ptr = ID3DXBuffer_GetBufferPointer(*effects);
3446 out_ptr = (BYTE*)(effect_ptr + num_materials);
3448 for (i = 0; i < num_materials; i++)
3450 DWORD j;
3451 D3DXEFFECTDEFAULT *defaults = (D3DXEFFECTDEFAULT*)out_ptr;
3453 effect_ptr->pDefaults = defaults;
3454 effect_ptr->NumDefaults = material_ptr->pTextureFilename ? 6 : 5;
3455 out_ptr = (BYTE*)(effect_ptr->pDefaults + effect_ptr->NumDefaults);
3457 for (j = 0; j < ARRAY_SIZE(material_effects); j++)
3459 defaults->pParamName = (char *)out_ptr;
3460 strcpy(defaults->pParamName, material_effects[j].param_name);
3461 defaults->pValue = defaults->pParamName + material_effects[j].name_size;
3462 defaults->Type = D3DXEDT_FLOATS;
3463 defaults->NumBytes = material_effects[j].num_bytes;
3464 memcpy(defaults->pValue, (BYTE*)material_ptr + material_effects[j].value_offset, defaults->NumBytes);
3465 out_ptr = (BYTE*)defaults->pValue + defaults->NumBytes;
3466 defaults++;
3469 if (material_ptr->pTextureFilename)
3471 defaults->pParamName = (char *)out_ptr;
3472 strcpy(defaults->pParamName, texture_paramname);
3473 defaults->pValue = defaults->pParamName + sizeof(texture_paramname);
3474 defaults->Type = D3DXEDT_STRING;
3475 defaults->NumBytes = strlen(material_ptr->pTextureFilename) + 1;
3476 strcpy(defaults->pValue, material_ptr->pTextureFilename);
3477 out_ptr = (BYTE*)defaults->pValue + defaults->NumBytes;
3479 material_ptr++;
3480 effect_ptr++;
3482 assert(out_ptr - (BYTE*)ID3DXBuffer_GetBufferPointer(*effects) == buffer_size);
3484 return D3D_OK;
3487 HRESULT WINAPI D3DXLoadSkinMeshFromXof(struct ID3DXFileData *filedata, DWORD options,
3488 struct IDirect3DDevice9 *device, struct ID3DXBuffer **adjacency_out, struct ID3DXBuffer **materials_out,
3489 struct ID3DXBuffer **effects_out, DWORD *num_materials_out, struct ID3DXSkinInfo **skin_info_out,
3490 struct ID3DXMesh **mesh_out)
3492 HRESULT hr;
3493 DWORD *index_in_ptr;
3494 struct mesh_data mesh_data;
3495 DWORD total_vertices;
3496 ID3DXMesh *d3dxmesh = NULL;
3497 ID3DXBuffer *adjacency = NULL;
3498 ID3DXBuffer *materials = NULL;
3499 ID3DXBuffer *effects = NULL;
3500 struct vertex_duplication {
3501 DWORD normal_index;
3502 struct list entry;
3503 } *duplications = NULL;
3504 DWORD i;
3505 void *vertices = NULL;
3506 void *indices = NULL;
3507 BYTE *out_ptr;
3508 DWORD provide_flags = 0;
3510 TRACE("filedata %p, options %#lx, device %p, adjacency_out %p, materials_out %p, "
3511 "effects_out %p, num_materials_out %p, skin_info_out %p, mesh_out %p.\n",
3512 filedata, options, device, adjacency_out, materials_out,
3513 effects_out, num_materials_out, skin_info_out, mesh_out);
3515 ZeroMemory(&mesh_data, sizeof(mesh_data));
3517 if (num_materials_out || materials_out || effects_out)
3518 provide_flags |= PROVIDE_MATERIALS;
3519 if (skin_info_out)
3520 provide_flags |= PROVIDE_SKININFO;
3522 hr = parse_mesh(filedata, &mesh_data, provide_flags);
3523 if (FAILED(hr)) goto cleanup;
3525 if (!mesh_data.num_vertices)
3527 if (adjacency_out)
3528 *adjacency_out = NULL;
3529 if (materials_out)
3530 *materials_out = NULL;
3531 if (effects_out)
3532 *effects_out = NULL;
3533 *mesh_out = NULL;
3534 hr = D3D_OK;
3535 goto cleanup;
3538 total_vertices = mesh_data.num_vertices;
3539 if (mesh_data.fvf & D3DFVF_NORMAL) {
3540 /* duplicate vertices with multiple normals */
3541 DWORD num_face_indices = mesh_data.num_poly_faces * 2 + mesh_data.num_tri_faces;
3542 duplications = malloc((mesh_data.num_vertices + num_face_indices) * sizeof(*duplications));
3543 if (!duplications) {
3544 hr = E_OUTOFMEMORY;
3545 goto cleanup;
3547 for (i = 0; i < total_vertices; i++)
3549 duplications[i].normal_index = -1;
3550 list_init(&duplications[i].entry);
3552 for (i = 0; i < num_face_indices; i++) {
3553 DWORD vertex_index = mesh_data.indices[i];
3554 DWORD normal_index = mesh_data.normal_indices[i];
3555 struct vertex_duplication *dup_ptr = &duplications[vertex_index];
3557 if (dup_ptr->normal_index == -1) {
3558 dup_ptr->normal_index = normal_index;
3559 } else {
3560 D3DXVECTOR3 *new_normal = &mesh_data.normals[normal_index];
3561 struct list *dup_list = &dup_ptr->entry;
3562 while (TRUE) {
3563 D3DXVECTOR3 *cur_normal = &mesh_data.normals[dup_ptr->normal_index];
3564 if (new_normal->x == cur_normal->x &&
3565 new_normal->y == cur_normal->y &&
3566 new_normal->z == cur_normal->z)
3568 mesh_data.indices[i] = dup_ptr - duplications;
3569 break;
3570 } else if (!list_next(dup_list, &dup_ptr->entry)) {
3571 dup_ptr = &duplications[total_vertices++];
3572 dup_ptr->normal_index = normal_index;
3573 list_add_tail(dup_list, &dup_ptr->entry);
3574 mesh_data.indices[i] = dup_ptr - duplications;
3575 break;
3576 } else {
3577 dup_ptr = LIST_ENTRY(list_next(dup_list, &dup_ptr->entry),
3578 struct vertex_duplication, entry);
3585 hr = D3DXCreateMeshFVF(mesh_data.num_tri_faces, total_vertices, options, mesh_data.fvf, device, &d3dxmesh);
3586 if (FAILED(hr)) goto cleanup;
3588 hr = d3dxmesh->lpVtbl->LockVertexBuffer(d3dxmesh, 0, &vertices);
3589 if (FAILED(hr)) goto cleanup;
3591 out_ptr = vertices;
3592 for (i = 0; i < mesh_data.num_vertices; i++) {
3593 *(D3DXVECTOR3*)out_ptr = mesh_data.vertices[i];
3594 out_ptr += sizeof(D3DXVECTOR3);
3595 if (mesh_data.fvf & D3DFVF_NORMAL) {
3596 if (duplications[i].normal_index == -1)
3597 ZeroMemory(out_ptr, sizeof(D3DXVECTOR3));
3598 else
3599 *(D3DXVECTOR3*)out_ptr = mesh_data.normals[duplications[i].normal_index];
3600 out_ptr += sizeof(D3DXVECTOR3);
3602 if (mesh_data.fvf & D3DFVF_DIFFUSE) {
3603 *(DWORD*)out_ptr = mesh_data.vertex_colors[i];
3604 out_ptr += sizeof(DWORD);
3606 if (mesh_data.fvf & D3DFVF_TEX1) {
3607 *(D3DXVECTOR2*)out_ptr = mesh_data.tex_coords[i];
3608 out_ptr += sizeof(D3DXVECTOR2);
3611 if (mesh_data.fvf & D3DFVF_NORMAL) {
3612 DWORD vertex_size = D3DXGetFVFVertexSize(mesh_data.fvf);
3613 out_ptr = vertices;
3614 for (i = 0; i < mesh_data.num_vertices; i++) {
3615 struct vertex_duplication *dup_ptr;
3616 LIST_FOR_EACH_ENTRY(dup_ptr, &duplications[i].entry, struct vertex_duplication, entry)
3618 int j = dup_ptr - duplications;
3619 BYTE *dest_vertex = (BYTE*)vertices + j * vertex_size;
3621 memcpy(dest_vertex, out_ptr, vertex_size);
3622 dest_vertex += sizeof(D3DXVECTOR3);
3623 *(D3DXVECTOR3*)dest_vertex = mesh_data.normals[dup_ptr->normal_index];
3625 out_ptr += vertex_size;
3628 d3dxmesh->lpVtbl->UnlockVertexBuffer(d3dxmesh);
3630 hr = d3dxmesh->lpVtbl->LockIndexBuffer(d3dxmesh, 0, &indices);
3631 if (FAILED(hr)) goto cleanup;
3633 index_in_ptr = mesh_data.indices;
3634 #define FILL_INDEX_BUFFER(indices_var) \
3635 for (i = 0; i < mesh_data.num_poly_faces; i++) \
3637 DWORD count = mesh_data.num_tri_per_face[i]; \
3638 WORD first_index = *index_in_ptr++; \
3639 while (count--) { \
3640 *indices_var++ = first_index; \
3641 *indices_var++ = *index_in_ptr; \
3642 index_in_ptr++; \
3643 *indices_var++ = *index_in_ptr; \
3645 index_in_ptr++; \
3647 if (options & D3DXMESH_32BIT) {
3648 DWORD *dword_indices = indices;
3649 FILL_INDEX_BUFFER(dword_indices)
3650 } else {
3651 WORD *word_indices = indices;
3652 FILL_INDEX_BUFFER(word_indices)
3654 #undef FILL_INDEX_BUFFER
3655 d3dxmesh->lpVtbl->UnlockIndexBuffer(d3dxmesh);
3657 if (mesh_data.material_indices) {
3658 DWORD *attrib_buffer = NULL;
3659 hr = d3dxmesh->lpVtbl->LockAttributeBuffer(d3dxmesh, 0, &attrib_buffer);
3660 if (FAILED(hr)) goto cleanup;
3661 for (i = 0; i < mesh_data.num_poly_faces; i++)
3663 DWORD count = mesh_data.num_tri_per_face[i];
3664 while (count--)
3665 *attrib_buffer++ = mesh_data.material_indices[i];
3667 d3dxmesh->lpVtbl->UnlockAttributeBuffer(d3dxmesh);
3669 hr = d3dxmesh->lpVtbl->OptimizeInplace(d3dxmesh,
3670 D3DXMESHOPT_ATTRSORT | D3DXMESHOPT_IGNOREVERTS | D3DXMESHOPT_DONOTSPLIT,
3671 NULL, NULL, NULL, NULL);
3672 if (FAILED(hr)) goto cleanup;
3675 if (mesh_data.num_materials && (materials_out || effects_out)) {
3676 DWORD buffer_size = mesh_data.num_materials * sizeof(D3DXMATERIAL);
3677 char *strings_out_ptr;
3678 D3DXMATERIAL *materials_ptr;
3680 for (i = 0; i < mesh_data.num_materials; i++) {
3681 if (mesh_data.materials[i].pTextureFilename)
3682 buffer_size += strlen(mesh_data.materials[i].pTextureFilename) + 1;
3685 hr = D3DXCreateBuffer(buffer_size, &materials);
3686 if (FAILED(hr)) goto cleanup;
3688 materials_ptr = ID3DXBuffer_GetBufferPointer(materials);
3689 memcpy(materials_ptr, mesh_data.materials, mesh_data.num_materials * sizeof(D3DXMATERIAL));
3690 strings_out_ptr = (char*)(materials_ptr + mesh_data.num_materials);
3691 for (i = 0; i < mesh_data.num_materials; i++) {
3692 if (materials_ptr[i].pTextureFilename) {
3693 strcpy(strings_out_ptr, mesh_data.materials[i].pTextureFilename);
3694 materials_ptr[i].pTextureFilename = strings_out_ptr;
3695 strings_out_ptr += strlen(mesh_data.materials[i].pTextureFilename) + 1;
3700 if (mesh_data.num_materials && effects_out) {
3701 hr = generate_effects(materials, mesh_data.num_materials, &effects);
3702 if (FAILED(hr)) goto cleanup;
3704 if (!materials_out) {
3705 ID3DXBuffer_Release(materials);
3706 materials = NULL;
3710 if (adjacency_out) {
3711 hr = D3DXCreateBuffer(mesh_data.num_tri_faces * 3 * sizeof(DWORD), &adjacency);
3712 if (FAILED(hr)) goto cleanup;
3713 hr = d3dxmesh->lpVtbl->GenerateAdjacency(d3dxmesh, 0.0f, ID3DXBuffer_GetBufferPointer(adjacency));
3714 if (FAILED(hr)) goto cleanup;
3717 *mesh_out = d3dxmesh;
3718 if (adjacency_out) *adjacency_out = adjacency;
3719 if (num_materials_out) *num_materials_out = mesh_data.num_materials;
3720 if (materials_out) *materials_out = materials;
3721 if (effects_out) *effects_out = effects;
3722 if (skin_info_out) *skin_info_out = mesh_data.skin_info;
3724 hr = D3D_OK;
3725 cleanup:
3726 if (FAILED(hr)) {
3727 if (d3dxmesh) IUnknown_Release(d3dxmesh);
3728 if (adjacency) ID3DXBuffer_Release(adjacency);
3729 if (materials) ID3DXBuffer_Release(materials);
3730 if (effects) ID3DXBuffer_Release(effects);
3731 if (mesh_data.skin_info) mesh_data.skin_info->lpVtbl->Release(mesh_data.skin_info);
3732 if (skin_info_out) *skin_info_out = NULL;
3734 free(mesh_data.vertices);
3735 free(mesh_data.num_tri_per_face);
3736 free(mesh_data.indices);
3737 free(mesh_data.normals);
3738 free(mesh_data.normal_indices);
3739 destroy_materials(&mesh_data);
3740 free(mesh_data.tex_coords);
3741 free(mesh_data.vertex_colors);
3742 free(duplications);
3743 return hr;
3746 HRESULT WINAPI D3DXLoadMeshHierarchyFromXA(const char *filename, DWORD options, struct IDirect3DDevice9 *device,
3747 struct ID3DXAllocateHierarchy *alloc_hier, struct ID3DXLoadUserData *load_user_data,
3748 D3DXFRAME **frame_hierarchy, struct ID3DXAnimationController **anim_controller)
3750 WCHAR *filenameW;
3751 HRESULT hr;
3752 int len;
3754 TRACE("filename %s, options %#lx, device %p, alloc_hier %p, "
3755 "load_user_data %p, frame_hierarchy %p, anim_controller %p.\n",
3756 debugstr_a(filename), options, device, alloc_hier,
3757 load_user_data, frame_hierarchy, anim_controller);
3759 if (!filename)
3760 return D3DERR_INVALIDCALL;
3762 len = MultiByteToWideChar(CP_ACP, 0, filename, -1, NULL, 0);
3763 filenameW = malloc(len * sizeof(WCHAR));
3764 if (!filenameW) return E_OUTOFMEMORY;
3765 MultiByteToWideChar(CP_ACP, 0, filename, -1, filenameW, len);
3767 hr = D3DXLoadMeshHierarchyFromXW(filenameW, options, device,
3768 alloc_hier, load_user_data, frame_hierarchy, anim_controller);
3769 free(filenameW);
3771 return hr;
3774 HRESULT WINAPI D3DXLoadMeshHierarchyFromXW(const WCHAR *filename, DWORD options, struct IDirect3DDevice9 *device,
3775 struct ID3DXAllocateHierarchy *alloc_hier, struct ID3DXLoadUserData *load_user_data,
3776 D3DXFRAME **frame_hierarchy, struct ID3DXAnimationController **anim_controller)
3778 void *buffer;
3779 HRESULT hr;
3780 DWORD size;
3782 TRACE("filename %s, options %#lx, device %p, alloc_hier %p, "
3783 "load_user_data %p, frame_hierarchy %p, anim_controller %p.\n",
3784 debugstr_w(filename), options, device, alloc_hier,
3785 load_user_data, frame_hierarchy, anim_controller);
3787 if (!filename)
3788 return D3DERR_INVALIDCALL;
3790 hr = map_view_of_file(filename, &buffer, &size);
3791 if (FAILED(hr))
3792 return D3DXERR_INVALIDDATA;
3794 hr = D3DXLoadMeshHierarchyFromXInMemory(buffer, size, options, device,
3795 alloc_hier, load_user_data, frame_hierarchy, anim_controller);
3797 UnmapViewOfFile(buffer);
3799 return hr;
3802 static HRESULT filedata_get_name(ID3DXFileData *filedata, char **name)
3804 HRESULT hr;
3805 SIZE_T name_len;
3807 hr = filedata->lpVtbl->GetName(filedata, NULL, &name_len);
3808 if (FAILED(hr)) return hr;
3810 if (!name_len)
3811 name_len++;
3812 *name = malloc(name_len);
3813 if (!*name) return E_OUTOFMEMORY;
3815 hr = filedata->lpVtbl->GetName(filedata, *name, &name_len);
3816 if (FAILED(hr))
3817 free(*name);
3818 else if (!name_len)
3819 (*name)[0] = 0;
3821 return hr;
3824 static HRESULT load_mesh_container(struct ID3DXFileData *filedata, DWORD options, struct IDirect3DDevice9 *device,
3825 struct ID3DXAllocateHierarchy *alloc_hier, D3DXMESHCONTAINER **mesh_container,
3826 struct ID3DXLoadUserData *load_user_data)
3828 HRESULT hr;
3829 ID3DXBuffer *adjacency = NULL;
3830 ID3DXBuffer *materials = NULL;
3831 ID3DXBuffer *effects = NULL;
3832 ID3DXSkinInfo *skin_info = NULL;
3833 D3DXMESHDATA mesh_data;
3834 DWORD num_materials = 0;
3835 char *name = NULL;
3836 SIZE_T child_count;
3837 ID3DXFileData *child = NULL;
3838 GUID type;
3839 unsigned int i;
3841 mesh_data.Type = D3DXMESHTYPE_MESH;
3842 mesh_data.pMesh = NULL;
3844 hr = D3DXLoadSkinMeshFromXof(filedata, options, device,
3845 &adjacency, &materials, &effects, &num_materials,
3846 &skin_info, &mesh_data.pMesh);
3847 if (FAILED(hr)) return hr;
3849 hr = filedata_get_name(filedata, &name);
3850 if (FAILED(hr)) goto cleanup;
3852 if (!mesh_data.pMesh)
3853 goto cleanup;
3854 hr = alloc_hier->lpVtbl->CreateMeshContainer(alloc_hier, name, &mesh_data,
3855 materials ? ID3DXBuffer_GetBufferPointer(materials) : NULL,
3856 effects ? ID3DXBuffer_GetBufferPointer(effects) : NULL,
3857 num_materials,
3858 adjacency ? ID3DXBuffer_GetBufferPointer(adjacency) : NULL,
3859 skin_info, mesh_container);
3860 if (FAILED(hr) || !load_user_data)
3861 goto cleanup;
3863 hr = filedata->lpVtbl->GetChildren(filedata, &child_count);
3864 if (FAILED(hr))
3865 goto cleanup;
3867 for (i = 0; i < child_count; i++)
3869 if (FAILED(hr = filedata->lpVtbl->GetChild(filedata, i, &child)))
3870 goto cleanup;
3871 if (FAILED(hr = child->lpVtbl->GetType(child, &type)))
3872 goto cleanup;
3874 if (!mesh_get_parse_func(&type)
3875 && FAILED(hr = load_user_data->lpVtbl->LoadMeshChildData(load_user_data, *mesh_container, child)))
3876 goto cleanup;
3878 IUnknown_Release(child);
3879 child = NULL;
3882 cleanup:
3883 if (child) IUnknown_Release(child);
3884 if (materials) ID3DXBuffer_Release(materials);
3885 if (effects) ID3DXBuffer_Release(effects);
3886 if (adjacency) ID3DXBuffer_Release(adjacency);
3887 if (skin_info) IUnknown_Release(skin_info);
3888 if (mesh_data.pMesh) IUnknown_Release(mesh_data.pMesh);
3889 free(name);
3890 return hr;
3893 static HRESULT parse_transform_matrix(ID3DXFileData *filedata, D3DXMATRIX *transform)
3895 SIZE_T data_size;
3896 const BYTE *data;
3897 HRESULT hr;
3899 /* template Matrix4x4 {
3900 * array FLOAT matrix[16];
3902 * template FrameTransformMatrix {
3903 * Matrix4x4 frameMatrix;
3907 hr = filedata->lpVtbl->Lock(filedata, &data_size, (const void **)&data);
3908 if (FAILED(hr))
3909 return hr;
3911 if (data_size != sizeof(D3DXMATRIX))
3913 WARN("Incorrect data size (%Id bytes).\n", data_size);
3914 filedata->lpVtbl->Unlock(filedata);
3915 return E_FAIL;
3918 memcpy(transform, data, sizeof(D3DXMATRIX));
3920 filedata->lpVtbl->Unlock(filedata);
3921 return D3D_OK;
3924 static HRESULT load_frame(struct ID3DXFileData *filedata, DWORD options, struct IDirect3DDevice9 *device,
3925 struct ID3DXAllocateHierarchy *alloc_hier, D3DXFRAME **frame_out, struct ID3DXLoadUserData *load_user_data)
3927 HRESULT hr;
3928 GUID type;
3929 ID3DXFileData *child;
3930 char *name = NULL;
3931 D3DXFRAME *frame = NULL;
3932 D3DXMESHCONTAINER **next_container;
3933 D3DXFRAME **next_child;
3934 SIZE_T i, nb_children;
3936 hr = filedata_get_name(filedata, &name);
3937 if (FAILED(hr)) return hr;
3939 hr = alloc_hier->lpVtbl->CreateFrame(alloc_hier, name, frame_out);
3940 free(name);
3941 if (FAILED(hr)) return E_FAIL;
3943 frame = *frame_out;
3944 D3DXMatrixIdentity(&frame->TransformationMatrix);
3945 next_child = &frame->pFrameFirstChild;
3946 next_container = &frame->pMeshContainer;
3948 hr = filedata->lpVtbl->GetChildren(filedata, &nb_children);
3949 if (FAILED(hr))
3950 return hr;
3952 for (i = 0; i < nb_children; i++)
3954 hr = filedata->lpVtbl->GetChild(filedata, i, &child);
3955 if (FAILED(hr))
3956 return hr;
3957 hr = child->lpVtbl->GetType(child, &type);
3958 if (FAILED(hr))
3959 goto err;
3961 if (IsEqualGUID(&type, &TID_D3DRMMesh)) {
3962 hr = load_mesh_container(child, options, device, alloc_hier, next_container, load_user_data);
3963 if (SUCCEEDED(hr))
3964 next_container = &(*next_container)->pNextMeshContainer;
3965 } else if (IsEqualGUID(&type, &TID_D3DRMFrameTransformMatrix)) {
3966 hr = parse_transform_matrix(child, &frame->TransformationMatrix);
3967 } else if (IsEqualGUID(&type, &TID_D3DRMFrame)) {
3968 hr = load_frame(child, options, device, alloc_hier, next_child, load_user_data);
3969 if (SUCCEEDED(hr))
3970 next_child = &(*next_child)->pFrameSibling;
3971 } else if (load_user_data) {
3972 TRACE("Loading %s as user data.\n", debugstr_guid(&type));
3973 hr = load_user_data->lpVtbl->LoadFrameChildData(load_user_data, frame, child);
3975 if (FAILED(hr))
3976 goto err;
3978 IUnknown_Release(child);
3980 return D3D_OK;
3982 err:
3983 IUnknown_Release(child);
3984 return hr;
3987 HRESULT WINAPI D3DXLoadMeshHierarchyFromXInMemory(const void *memory, DWORD memory_size, DWORD options,
3988 struct IDirect3DDevice9 *device, struct ID3DXAllocateHierarchy *alloc_hier,
3989 struct ID3DXLoadUserData *load_user_data, D3DXFRAME **frame_hierarchy,
3990 struct ID3DXAnimationController **anim_controller)
3992 HRESULT hr;
3993 ID3DXFile *d3dxfile = NULL;
3994 ID3DXFileEnumObject *enumobj = NULL;
3995 ID3DXFileData *filedata = NULL;
3996 D3DXF_FILELOADMEMORY source;
3997 D3DXFRAME *first_frame = NULL;
3998 D3DXFRAME **next_frame = &first_frame;
3999 SIZE_T i, nb_children;
4000 GUID guid;
4002 TRACE("memory %p, memory_size %lu, options %#lx, device %p, alloc_hier %p, load_user_data %p, "
4003 "frame_hierarchy %p, anim_controller %p.\n", memory, memory_size, options,
4004 device, alloc_hier, load_user_data, frame_hierarchy, anim_controller);
4006 if (!memory || !memory_size || !device || !frame_hierarchy || !alloc_hier)
4007 return D3DERR_INVALIDCALL;
4009 hr = D3DXFileCreate(&d3dxfile);
4010 if (FAILED(hr)) goto cleanup;
4012 hr = d3dxfile->lpVtbl->RegisterTemplates(d3dxfile, D3DRM_XTEMPLATES, D3DRM_XTEMPLATE_BYTES);
4013 if (FAILED(hr)) goto cleanup;
4015 source.lpMemory = (void*)memory;
4016 source.dSize = memory_size;
4017 hr = d3dxfile->lpVtbl->CreateEnumObject(d3dxfile, &source, D3DXF_FILELOAD_FROMMEMORY, &enumobj);
4018 if (FAILED(hr)) goto cleanup;
4020 hr = enumobj->lpVtbl->GetChildren(enumobj, &nb_children);
4021 if (FAILED(hr))
4022 goto cleanup;
4024 for (i = 0; i < nb_children; i++)
4026 hr = enumobj->lpVtbl->GetChild(enumobj, i, &filedata);
4027 if (FAILED(hr))
4028 goto cleanup;
4030 hr = filedata->lpVtbl->GetType(filedata, &guid);
4031 if (SUCCEEDED(hr)) {
4032 if (IsEqualGUID(&guid, &TID_D3DRMMesh)) {
4033 hr = alloc_hier->lpVtbl->CreateFrame(alloc_hier, NULL, next_frame);
4034 if (FAILED(hr)) {
4035 hr = E_FAIL;
4036 goto cleanup;
4039 D3DXMatrixIdentity(&(*next_frame)->TransformationMatrix);
4041 hr = load_mesh_container(filedata, options, device, alloc_hier, &(*next_frame)->pMeshContainer,
4042 load_user_data);
4043 if (FAILED(hr)) goto cleanup;
4044 } else if (IsEqualGUID(&guid, &TID_D3DRMFrame)) {
4045 hr = load_frame(filedata, options, device, alloc_hier, next_frame, load_user_data);
4046 if (FAILED(hr)) goto cleanup;
4047 } else if (load_user_data) {
4048 TRACE("Loading %s as user data.\n", debugstr_guid(&guid));
4049 hr = load_user_data->lpVtbl->LoadTopLevelData(load_user_data, filedata);
4051 while (*next_frame)
4052 next_frame = &(*next_frame)->pFrameSibling;
4055 filedata->lpVtbl->Release(filedata);
4056 filedata = NULL;
4057 if (FAILED(hr))
4058 goto cleanup;
4061 if (!first_frame) {
4062 hr = E_FAIL;
4063 } else if (first_frame->pFrameSibling) {
4064 D3DXFRAME *root_frame = NULL;
4065 hr = alloc_hier->lpVtbl->CreateFrame(alloc_hier, NULL, &root_frame);
4066 if (FAILED(hr)) {
4067 hr = E_FAIL;
4068 goto cleanup;
4070 D3DXMatrixIdentity(&root_frame->TransformationMatrix);
4071 root_frame->pFrameFirstChild = first_frame;
4072 *frame_hierarchy = root_frame;
4073 hr = D3D_OK;
4074 } else {
4075 *frame_hierarchy = first_frame;
4076 hr = D3D_OK;
4079 if (anim_controller)
4081 *anim_controller = NULL;
4082 FIXME("Animation controller creation not implemented.\n");
4085 cleanup:
4086 if (FAILED(hr) && first_frame) D3DXFrameDestroy(first_frame, alloc_hier);
4087 if (filedata) filedata->lpVtbl->Release(filedata);
4088 if (enumobj) enumobj->lpVtbl->Release(enumobj);
4089 if (d3dxfile) d3dxfile->lpVtbl->Release(d3dxfile);
4090 return hr;
4093 HRESULT WINAPI D3DXCleanMesh(D3DXCLEANTYPE clean_type, ID3DXMesh *mesh_in, const DWORD *adjacency_in,
4094 ID3DXMesh **mesh_out, DWORD *adjacency_out, ID3DXBuffer **errors_and_warnings)
4096 FIXME("(%u, %p, %p, %p, %p, %p)\n", clean_type, mesh_in, adjacency_in, mesh_out, adjacency_out, errors_and_warnings);
4098 return E_NOTIMPL;
4101 HRESULT WINAPI D3DXFrameDestroy(D3DXFRAME *frame, ID3DXAllocateHierarchy *alloc_hier)
4103 HRESULT hr;
4104 BOOL last = FALSE;
4106 TRACE("(%p, %p)\n", frame, alloc_hier);
4108 if (!frame || !alloc_hier)
4109 return D3DERR_INVALIDCALL;
4111 while (!last) {
4112 D3DXMESHCONTAINER *container;
4113 D3DXFRAME *current_frame;
4115 if (frame->pFrameSibling) {
4116 current_frame = frame->pFrameSibling;
4117 frame->pFrameSibling = current_frame->pFrameSibling;
4118 current_frame->pFrameSibling = NULL;
4119 } else {
4120 current_frame = frame;
4121 last = TRUE;
4124 if (current_frame->pFrameFirstChild) {
4125 hr = D3DXFrameDestroy(current_frame->pFrameFirstChild, alloc_hier);
4126 if (FAILED(hr)) return hr;
4127 current_frame->pFrameFirstChild = NULL;
4130 container = current_frame->pMeshContainer;
4131 while (container) {
4132 D3DXMESHCONTAINER *next_container = container->pNextMeshContainer;
4133 hr = alloc_hier->lpVtbl->DestroyMeshContainer(alloc_hier, container);
4134 if (FAILED(hr)) return hr;
4135 container = next_container;
4137 hr = alloc_hier->lpVtbl->DestroyFrame(alloc_hier, current_frame);
4138 if (FAILED(hr)) return hr;
4140 return D3D_OK;
4143 HRESULT WINAPI D3DXLoadMeshFromXA(const char *filename, DWORD options, struct IDirect3DDevice9 *device,
4144 struct ID3DXBuffer **adjacency, struct ID3DXBuffer **materials, struct ID3DXBuffer **effect_instances,
4145 DWORD *num_materials, struct ID3DXMesh **mesh)
4147 WCHAR *filenameW;
4148 HRESULT hr;
4149 int len;
4151 TRACE("filename %s, options %#lx, device %p, adjacency %p, materials %p, "
4152 "effect_instances %p, num_materials %p, mesh %p.\n",
4153 debugstr_a(filename), options, device, adjacency, materials,
4154 effect_instances, num_materials, mesh);
4156 if (!filename)
4157 return D3DERR_INVALIDCALL;
4159 len = MultiByteToWideChar(CP_ACP, 0, filename, -1, NULL, 0);
4160 filenameW = malloc(len * sizeof(WCHAR));
4161 if (!filenameW) return E_OUTOFMEMORY;
4162 MultiByteToWideChar(CP_ACP, 0, filename, -1, filenameW, len);
4164 hr = D3DXLoadMeshFromXW(filenameW, options, device, adjacency, materials,
4165 effect_instances, num_materials, mesh);
4166 free(filenameW);
4168 return hr;
4171 HRESULT WINAPI D3DXLoadMeshFromXW(const WCHAR *filename, DWORD options, struct IDirect3DDevice9 *device,
4172 struct ID3DXBuffer **adjacency, struct ID3DXBuffer **materials, struct ID3DXBuffer **effect_instances,
4173 DWORD *num_materials, struct ID3DXMesh **mesh)
4175 void *buffer;
4176 HRESULT hr;
4177 DWORD size;
4179 TRACE("filename %s, options %#lx, device %p, adjacency %p, materials %p, "
4180 "effect_instances %p, num_materials %p, mesh %p.\n",
4181 debugstr_w(filename), options, device, adjacency, materials,
4182 effect_instances, num_materials, mesh);
4184 if (!filename)
4185 return D3DERR_INVALIDCALL;
4187 hr = map_view_of_file(filename, &buffer, &size);
4188 if (FAILED(hr))
4189 return D3DXERR_INVALIDDATA;
4191 hr = D3DXLoadMeshFromXInMemory(buffer, size, options, device, adjacency,
4192 materials, effect_instances, num_materials, mesh);
4194 UnmapViewOfFile(buffer);
4196 return hr;
4199 HRESULT WINAPI D3DXLoadMeshFromXResource(HMODULE module, const char *name, const char *type, DWORD options,
4200 struct IDirect3DDevice9 *device, struct ID3DXBuffer **adjacency, struct ID3DXBuffer **materials,
4201 struct ID3DXBuffer **effect_instances, DWORD *num_materials, struct ID3DXMesh **mesh)
4203 HRESULT hr;
4204 HRSRC resinfo;
4205 void *buffer;
4206 DWORD size;
4208 TRACE("module %p, name %s, type %s, options %#lx, device %p, adjacency %p, "
4209 "materials %p, effect_instances %p, num_materials %p, mesh %p.\n",
4210 module, debugstr_a(name), debugstr_a(type), options, device, adjacency,
4211 materials, effect_instances, num_materials, mesh);
4213 resinfo = FindResourceA(module, name, type);
4214 if (!resinfo) return D3DXERR_INVALIDDATA;
4216 hr = load_resource_into_memory(module, resinfo, &buffer, &size);
4217 if (FAILED(hr)) return D3DXERR_INVALIDDATA;
4219 return D3DXLoadMeshFromXInMemory(buffer, size, options, device, adjacency,
4220 materials, effect_instances, num_materials, mesh);
4223 struct mesh_container
4225 struct list entry;
4226 ID3DXMesh *mesh;
4227 ID3DXBuffer *adjacency;
4228 ID3DXBuffer *materials;
4229 ID3DXBuffer *effects;
4230 DWORD num_materials;
4231 D3DXMATRIX transform;
4234 static HRESULT parse_frame(struct ID3DXFileData *filedata, DWORD options, struct IDirect3DDevice9 *device,
4235 const D3DXMATRIX *parent_transform, struct list *container_list, DWORD provide_flags)
4237 HRESULT hr;
4238 D3DXMATRIX transform = *parent_transform;
4239 ID3DXFileData *child;
4240 GUID type;
4241 SIZE_T i, nb_children;
4243 hr = filedata->lpVtbl->GetChildren(filedata, &nb_children);
4244 if (FAILED(hr))
4245 return hr;
4247 for (i = 0; i < nb_children; i++)
4249 hr = filedata->lpVtbl->GetChild(filedata, i, &child);
4250 if (FAILED(hr))
4251 return hr;
4252 hr = child->lpVtbl->GetType(child, &type);
4253 if (FAILED(hr))
4254 goto err;
4256 if (IsEqualGUID(&type, &TID_D3DRMMesh)) {
4257 struct mesh_container *container = calloc(1, sizeof(*container));
4258 if (!container)
4260 hr = E_OUTOFMEMORY;
4261 goto err;
4264 hr = D3DXLoadSkinMeshFromXof(child, options, device,
4265 (provide_flags & PROVIDE_ADJACENCY) ? &container->adjacency : NULL,
4266 (provide_flags & PROVIDE_MATERIALS) ? &container->materials : NULL,
4267 NULL, &container->num_materials, NULL, &container->mesh);
4269 if (container->mesh)
4271 list_add_tail(container_list, &container->entry);
4272 container->transform = transform;
4274 else
4276 free(container);
4278 } else if (IsEqualGUID(&type, &TID_D3DRMFrameTransformMatrix)) {
4279 D3DXMATRIX new_transform;
4280 hr = parse_transform_matrix(child, &new_transform);
4281 D3DXMatrixMultiply(&transform, &transform, &new_transform);
4282 } else if (IsEqualGUID(&type, &TID_D3DRMFrame)) {
4283 hr = parse_frame(child, options, device, &transform, container_list, provide_flags);
4285 if (FAILED(hr))
4286 goto err;
4288 IUnknown_Release(child);
4290 return D3D_OK;
4292 err:
4293 IUnknown_Release(child);
4294 return hr;
4297 HRESULT WINAPI D3DXLoadMeshFromXInMemory(const void *memory, DWORD memory_size, DWORD options,
4298 struct IDirect3DDevice9 *device, struct ID3DXBuffer **adjacency_out, struct ID3DXBuffer **materials_out,
4299 struct ID3DXBuffer **effects_out, DWORD *num_materials_out, struct ID3DXMesh **mesh_out)
4301 HRESULT hr;
4302 ID3DXFile *d3dxfile = NULL;
4303 ID3DXFileEnumObject *enumobj = NULL;
4304 ID3DXFileData *filedata = NULL;
4305 D3DXF_FILELOADMEMORY source;
4306 ID3DXBuffer *materials = NULL;
4307 ID3DXBuffer *effects = NULL;
4308 ID3DXBuffer *adjacency = NULL;
4309 struct list container_list = LIST_INIT(container_list);
4310 struct mesh_container *container_ptr, *next_container_ptr;
4311 DWORD num_materials;
4312 DWORD num_faces, num_vertices;
4313 D3DXMATRIX identity;
4314 DWORD provide_flags = 0;
4315 DWORD fvf;
4316 ID3DXMesh *concat_mesh = NULL;
4317 D3DVERTEXELEMENT9 concat_decl[MAX_FVF_DECL_SIZE];
4318 BYTE *concat_vertices = NULL;
4319 void *concat_indices = NULL;
4320 DWORD index_offset;
4321 DWORD concat_vertex_size;
4322 SIZE_T i, nb_children;
4323 GUID guid;
4325 TRACE("memory %p, memory_size %lu, options %#lx, device %p, adjacency_out %p, materials_out %p, "
4326 "effects_out %p, num_materials_out %p, mesh_out %p.\n", memory, memory_size, options,
4327 device, adjacency_out, materials_out, effects_out, num_materials_out, mesh_out);
4329 if (!memory || !memory_size || !device || !mesh_out)
4330 return D3DERR_INVALIDCALL;
4332 hr = D3DXFileCreate(&d3dxfile);
4333 if (FAILED(hr)) goto cleanup;
4335 hr = d3dxfile->lpVtbl->RegisterTemplates(d3dxfile, D3DRM_XTEMPLATES, D3DRM_XTEMPLATE_BYTES);
4336 if (FAILED(hr)) goto cleanup;
4338 source.lpMemory = (void*)memory;
4339 source.dSize = memory_size;
4340 hr = d3dxfile->lpVtbl->CreateEnumObject(d3dxfile, &source, D3DXF_FILELOAD_FROMMEMORY, &enumobj);
4341 if (FAILED(hr)) goto cleanup;
4343 D3DXMatrixIdentity(&identity);
4344 if (adjacency_out) provide_flags |= PROVIDE_ADJACENCY;
4345 if (materials_out || effects_out) provide_flags |= PROVIDE_MATERIALS;
4347 hr = enumobj->lpVtbl->GetChildren(enumobj, &nb_children);
4348 if (FAILED(hr))
4349 goto cleanup;
4351 for (i = 0; i < nb_children; i++)
4353 hr = enumobj->lpVtbl->GetChild(enumobj, i, &filedata);
4354 if (FAILED(hr))
4355 goto cleanup;
4357 hr = filedata->lpVtbl->GetType(filedata, &guid);
4358 if (SUCCEEDED(hr)) {
4359 if (IsEqualGUID(&guid, &TID_D3DRMMesh)) {
4360 container_ptr = calloc(1, sizeof(*container_ptr));
4361 if (!container_ptr) {
4362 hr = E_OUTOFMEMORY;
4363 goto cleanup;
4366 hr = D3DXLoadSkinMeshFromXof(filedata, options, device,
4367 (provide_flags & PROVIDE_ADJACENCY) ? &container_ptr->adjacency : NULL,
4368 (provide_flags & PROVIDE_MATERIALS) ? &container_ptr->materials : NULL,
4369 NULL, &container_ptr->num_materials, NULL, &container_ptr->mesh);
4370 if (container_ptr->mesh)
4372 list_add_tail(&container_list, &container_ptr->entry);
4373 D3DXMatrixIdentity(&container_ptr->transform);
4375 else
4377 free(container_ptr);
4379 } else if (IsEqualGUID(&guid, &TID_D3DRMFrame)) {
4380 hr = parse_frame(filedata, options, device, &identity, &container_list, provide_flags);
4382 if (FAILED(hr)) goto cleanup;
4384 filedata->lpVtbl->Release(filedata);
4385 filedata = NULL;
4386 if (FAILED(hr))
4387 goto cleanup;
4390 enumobj->lpVtbl->Release(enumobj);
4391 enumobj = NULL;
4392 d3dxfile->lpVtbl->Release(d3dxfile);
4393 d3dxfile = NULL;
4395 if (list_empty(&container_list)) {
4396 hr = E_FAIL;
4397 goto cleanup;
4400 fvf = D3DFVF_XYZ;
4401 num_faces = 0;
4402 num_vertices = 0;
4403 num_materials = 0;
4404 LIST_FOR_EACH_ENTRY(container_ptr, &container_list, struct mesh_container, entry)
4406 ID3DXMesh *mesh = container_ptr->mesh;
4407 fvf |= mesh->lpVtbl->GetFVF(mesh);
4408 num_faces += mesh->lpVtbl->GetNumFaces(mesh);
4409 num_vertices += mesh->lpVtbl->GetNumVertices(mesh);
4410 num_materials += container_ptr->num_materials;
4413 hr = D3DXCreateMeshFVF(num_faces, num_vertices, options, fvf, device, &concat_mesh);
4414 if (FAILED(hr)) goto cleanup;
4416 hr = concat_mesh->lpVtbl->GetDeclaration(concat_mesh, concat_decl);
4417 if (FAILED(hr)) goto cleanup;
4419 concat_vertex_size = D3DXGetDeclVertexSize(concat_decl, 0);
4421 hr = concat_mesh->lpVtbl->LockVertexBuffer(concat_mesh, 0, (void**)&concat_vertices);
4422 if (FAILED(hr)) goto cleanup;
4424 LIST_FOR_EACH_ENTRY(container_ptr, &container_list, struct mesh_container, entry)
4426 D3DVERTEXELEMENT9 mesh_decl[MAX_FVF_DECL_SIZE];
4427 ID3DXMesh *mesh = container_ptr->mesh;
4428 DWORD num_mesh_vertices = mesh->lpVtbl->GetNumVertices(mesh);
4429 DWORD mesh_vertex_size;
4430 const BYTE *mesh_vertices;
4431 DWORD i;
4433 hr = mesh->lpVtbl->GetDeclaration(mesh, mesh_decl);
4434 if (FAILED(hr)) goto cleanup;
4436 mesh_vertex_size = D3DXGetDeclVertexSize(mesh_decl, 0);
4438 hr = mesh->lpVtbl->LockVertexBuffer(mesh, D3DLOCK_READONLY, (void**)&mesh_vertices);
4439 if (FAILED(hr)) goto cleanup;
4441 for (i = 0; i < num_mesh_vertices; i++) {
4442 int j;
4443 int k = 1;
4445 D3DXVec3TransformCoord((D3DXVECTOR3*)concat_vertices,
4446 (D3DXVECTOR3*)mesh_vertices,
4447 &container_ptr->transform);
4448 for (j = 1; concat_decl[j].Stream != 0xff; j++)
4450 if (concat_decl[j].Usage == mesh_decl[k].Usage &&
4451 concat_decl[j].UsageIndex == mesh_decl[k].UsageIndex)
4453 if (concat_decl[j].Usage == D3DDECLUSAGE_NORMAL) {
4454 D3DXVec3TransformCoord((D3DXVECTOR3*)(concat_vertices + concat_decl[j].Offset),
4455 (D3DXVECTOR3*)(mesh_vertices + mesh_decl[k].Offset),
4456 &container_ptr->transform);
4457 } else {
4458 memcpy(concat_vertices + concat_decl[j].Offset,
4459 mesh_vertices + mesh_decl[k].Offset,
4460 d3dx_decltype_size[mesh_decl[k].Type]);
4462 k++;
4465 mesh_vertices += mesh_vertex_size;
4466 concat_vertices += concat_vertex_size;
4469 mesh->lpVtbl->UnlockVertexBuffer(mesh);
4472 concat_mesh->lpVtbl->UnlockVertexBuffer(concat_mesh);
4473 concat_vertices = NULL;
4475 hr = concat_mesh->lpVtbl->LockIndexBuffer(concat_mesh, 0, &concat_indices);
4476 if (FAILED(hr)) goto cleanup;
4478 index_offset = 0;
4479 LIST_FOR_EACH_ENTRY(container_ptr, &container_list, struct mesh_container, entry)
4481 ID3DXMesh *mesh = container_ptr->mesh;
4482 const void *mesh_indices;
4483 DWORD num_mesh_faces = mesh->lpVtbl->GetNumFaces(mesh);
4484 DWORD i;
4486 hr = mesh->lpVtbl->LockIndexBuffer(mesh, D3DLOCK_READONLY, (void**)&mesh_indices);
4487 if (FAILED(hr)) goto cleanup;
4489 if (options & D3DXMESH_32BIT) {
4490 DWORD *dest = concat_indices;
4491 const DWORD *src = mesh_indices;
4492 for (i = 0; i < num_mesh_faces * 3; i++)
4493 *dest++ = index_offset + *src++;
4494 concat_indices = dest;
4495 } else {
4496 WORD *dest = concat_indices;
4497 const WORD *src = mesh_indices;
4498 for (i = 0; i < num_mesh_faces * 3; i++)
4499 *dest++ = index_offset + *src++;
4500 concat_indices = dest;
4502 mesh->lpVtbl->UnlockIndexBuffer(mesh);
4504 index_offset += num_mesh_faces * 3;
4507 concat_mesh->lpVtbl->UnlockIndexBuffer(concat_mesh);
4508 concat_indices = NULL;
4510 if (num_materials) {
4511 DWORD *concat_attrib_buffer = NULL;
4512 DWORD offset = 0;
4514 hr = concat_mesh->lpVtbl->LockAttributeBuffer(concat_mesh, 0, &concat_attrib_buffer);
4515 if (FAILED(hr)) goto cleanup;
4517 LIST_FOR_EACH_ENTRY(container_ptr, &container_list, struct mesh_container, entry)
4519 ID3DXMesh *mesh = container_ptr->mesh;
4520 const DWORD *mesh_attrib_buffer = NULL;
4521 DWORD count = mesh->lpVtbl->GetNumFaces(mesh);
4523 hr = mesh->lpVtbl->LockAttributeBuffer(mesh, D3DLOCK_READONLY, (DWORD**)&mesh_attrib_buffer);
4524 if (FAILED(hr)) {
4525 concat_mesh->lpVtbl->UnlockAttributeBuffer(concat_mesh);
4526 goto cleanup;
4529 while (count--)
4530 *concat_attrib_buffer++ = offset + *mesh_attrib_buffer++;
4532 mesh->lpVtbl->UnlockAttributeBuffer(mesh);
4533 offset += container_ptr->num_materials;
4535 concat_mesh->lpVtbl->UnlockAttributeBuffer(concat_mesh);
4538 if (materials_out || effects_out) {
4539 D3DXMATERIAL *out_ptr;
4540 if (!num_materials) {
4541 /* create default material */
4542 hr = D3DXCreateBuffer(sizeof(D3DXMATERIAL), &materials);
4543 if (FAILED(hr)) goto cleanup;
4545 out_ptr = ID3DXBuffer_GetBufferPointer(materials);
4546 out_ptr->MatD3D.Diffuse.r = 0.5f;
4547 out_ptr->MatD3D.Diffuse.g = 0.5f;
4548 out_ptr->MatD3D.Diffuse.b = 0.5f;
4549 out_ptr->MatD3D.Specular.r = 0.5f;
4550 out_ptr->MatD3D.Specular.g = 0.5f;
4551 out_ptr->MatD3D.Specular.b = 0.5f;
4552 /* D3DXCreateBuffer initializes the rest to zero */
4553 } else {
4554 DWORD buffer_size = num_materials * sizeof(D3DXMATERIAL);
4555 char *strings_out_ptr;
4557 LIST_FOR_EACH_ENTRY(container_ptr, &container_list, struct mesh_container, entry)
4559 if (container_ptr->materials) {
4560 DWORD i;
4561 const D3DXMATERIAL *in_ptr = ID3DXBuffer_GetBufferPointer(container_ptr->materials);
4562 for (i = 0; i < container_ptr->num_materials; i++)
4564 if (in_ptr->pTextureFilename)
4565 buffer_size += strlen(in_ptr->pTextureFilename) + 1;
4566 in_ptr++;
4571 hr = D3DXCreateBuffer(buffer_size, &materials);
4572 if (FAILED(hr)) goto cleanup;
4573 out_ptr = ID3DXBuffer_GetBufferPointer(materials);
4574 strings_out_ptr = (char*)(out_ptr + num_materials);
4576 LIST_FOR_EACH_ENTRY(container_ptr, &container_list, struct mesh_container, entry)
4578 if (container_ptr->materials) {
4579 DWORD i;
4580 const D3DXMATERIAL *in_ptr = ID3DXBuffer_GetBufferPointer(container_ptr->materials);
4581 for (i = 0; i < container_ptr->num_materials; i++)
4583 out_ptr->MatD3D = in_ptr->MatD3D;
4584 if (in_ptr->pTextureFilename) {
4585 out_ptr->pTextureFilename = strings_out_ptr;
4586 strcpy(out_ptr->pTextureFilename, in_ptr->pTextureFilename);
4587 strings_out_ptr += strlen(in_ptr->pTextureFilename) + 1;
4589 in_ptr++;
4590 out_ptr++;
4596 if (!num_materials)
4597 num_materials = 1;
4599 if (effects_out) {
4600 generate_effects(materials, num_materials, &effects);
4601 if (!materials_out) {
4602 ID3DXBuffer_Release(materials);
4603 materials = NULL;
4607 if (adjacency_out) {
4608 if (!list_next(&container_list, list_head(&container_list))) {
4609 container_ptr = LIST_ENTRY(list_head(&container_list), struct mesh_container, entry);
4610 adjacency = container_ptr->adjacency;
4611 container_ptr->adjacency = NULL;
4612 } else {
4613 DWORD offset = 0;
4614 DWORD *out_ptr;
4616 hr = D3DXCreateBuffer(num_faces * 3 * sizeof(DWORD), &adjacency);
4617 if (FAILED(hr)) goto cleanup;
4619 out_ptr = ID3DXBuffer_GetBufferPointer(adjacency);
4620 LIST_FOR_EACH_ENTRY(container_ptr, &container_list, struct mesh_container, entry)
4622 DWORD i;
4623 DWORD count = 3 * container_ptr->mesh->lpVtbl->GetNumFaces(container_ptr->mesh);
4624 DWORD *in_ptr = ID3DXBuffer_GetBufferPointer(container_ptr->adjacency);
4626 for (i = 0; i < count; i++)
4627 *out_ptr++ = offset + *in_ptr++;
4629 offset += count;
4634 *mesh_out = concat_mesh;
4635 if (adjacency_out) *adjacency_out = adjacency;
4636 if (materials_out) *materials_out = materials;
4637 if (effects_out) *effects_out = effects;
4638 if (num_materials_out) *num_materials_out = num_materials;
4640 hr = D3D_OK;
4641 cleanup:
4642 if (concat_indices) concat_mesh->lpVtbl->UnlockIndexBuffer(concat_mesh);
4643 if (concat_vertices) concat_mesh->lpVtbl->UnlockVertexBuffer(concat_mesh);
4644 if (filedata) filedata->lpVtbl->Release(filedata);
4645 if (enumobj) enumobj->lpVtbl->Release(enumobj);
4646 if (d3dxfile) d3dxfile->lpVtbl->Release(d3dxfile);
4647 if (FAILED(hr)) {
4648 if (concat_mesh) IUnknown_Release(concat_mesh);
4649 if (materials) ID3DXBuffer_Release(materials);
4650 if (effects) ID3DXBuffer_Release(effects);
4651 if (adjacency) ID3DXBuffer_Release(adjacency);
4653 LIST_FOR_EACH_ENTRY_SAFE(container_ptr, next_container_ptr, &container_list, struct mesh_container, entry)
4655 if (container_ptr->mesh) IUnknown_Release(container_ptr->mesh);
4656 if (container_ptr->adjacency) ID3DXBuffer_Release(container_ptr->adjacency);
4657 if (container_ptr->materials) ID3DXBuffer_Release(container_ptr->materials);
4658 if (container_ptr->effects) ID3DXBuffer_Release(container_ptr->effects);
4659 free(container_ptr);
4661 return hr;
4664 struct vertex
4666 D3DXVECTOR3 position;
4667 D3DXVECTOR3 normal;
4670 HRESULT WINAPI D3DXCreatePolygon(struct IDirect3DDevice9 *device, float length, UINT sides,
4671 struct ID3DXMesh **mesh, struct ID3DXBuffer **adjacency)
4673 HRESULT hr;
4674 ID3DXMesh *polygon;
4675 struct vertex *vertices;
4676 WORD (*faces)[3];
4677 DWORD (*adjacency_buf)[3];
4678 float angle, scale;
4679 unsigned int i;
4681 TRACE("device %p, length %f, sides %u, mesh %p, adjacency %p.\n",
4682 device, length, sides, mesh, adjacency);
4684 if (!device || length < 0.0f || sides < 3 || !mesh)
4685 return D3DERR_INVALIDCALL;
4687 if (FAILED(hr = D3DXCreateMeshFVF(sides, sides + 1, D3DXMESH_MANAGED,
4688 D3DFVF_XYZ | D3DFVF_NORMAL, device, &polygon)))
4690 return hr;
4693 if (FAILED(hr = polygon->lpVtbl->LockVertexBuffer(polygon, 0, (void **)&vertices)))
4695 polygon->lpVtbl->Release(polygon);
4696 return hr;
4699 if (FAILED(hr = polygon->lpVtbl->LockIndexBuffer(polygon, 0, (void **)&faces)))
4701 polygon->lpVtbl->UnlockVertexBuffer(polygon);
4702 polygon->lpVtbl->Release(polygon);
4703 return hr;
4706 angle = D3DX_PI / sides;
4707 scale = 0.5f * length / sinf(angle);
4708 angle *= 2.0f;
4710 vertices[0].position.x = 0.0f;
4711 vertices[0].position.y = 0.0f;
4712 vertices[0].position.z = 0.0f;
4713 vertices[0].normal.x = 0.0f;
4714 vertices[0].normal.y = 0.0f;
4715 vertices[0].normal.z = 1.0f;
4717 for (i = 0; i < sides; ++i)
4719 vertices[i + 1].position.x = cosf(angle * i) * scale;
4720 vertices[i + 1].position.y = sinf(angle * i) * scale;
4721 vertices[i + 1].position.z = 0.0f;
4722 vertices[i + 1].normal.x = 0.0f;
4723 vertices[i + 1].normal.y = 0.0f;
4724 vertices[i + 1].normal.z = 1.0f;
4726 faces[i][0] = 0;
4727 faces[i][1] = i + 1;
4728 faces[i][2] = i + 2;
4731 faces[sides - 1][2] = 1;
4733 polygon->lpVtbl->UnlockVertexBuffer(polygon);
4734 polygon->lpVtbl->UnlockIndexBuffer(polygon);
4736 if (adjacency)
4738 if (FAILED(hr = D3DXCreateBuffer(sides * sizeof(DWORD) * 3, adjacency)))
4740 polygon->lpVtbl->Release(polygon);
4741 return hr;
4744 adjacency_buf = ID3DXBuffer_GetBufferPointer(*adjacency);
4745 for (i = 0; i < sides; ++i)
4747 adjacency_buf[i][0] = i - 1;
4748 adjacency_buf[i][1] = ~0U;
4749 adjacency_buf[i][2] = i + 1;
4751 adjacency_buf[0][0] = sides - 1;
4752 adjacency_buf[sides - 1][2] = 0;
4755 *mesh = polygon;
4757 return D3D_OK;
4760 HRESULT WINAPI D3DXCreateBox(struct IDirect3DDevice9 *device, float width, float height,
4761 float depth, struct ID3DXMesh **mesh, struct ID3DXBuffer **adjacency)
4763 HRESULT hr;
4764 ID3DXMesh *box;
4765 struct vertex *vertices;
4766 WORD (*faces)[3];
4767 DWORD *adjacency_buf;
4768 unsigned int i, face;
4769 static const D3DXVECTOR3 unit_box[] =
4771 {-0.5f, -0.5f, -0.5f}, {-0.5f, -0.5f, 0.5f}, {-0.5f, 0.5f, 0.5f}, {-0.5f, 0.5f, -0.5f},
4772 {-0.5f, 0.5f, -0.5f}, {-0.5f, 0.5f, 0.5f}, { 0.5f, 0.5f, 0.5f}, { 0.5f, 0.5f, -0.5f},
4773 { 0.5f, 0.5f, -0.5f}, { 0.5f, 0.5f, 0.5f}, { 0.5f, -0.5f, 0.5f}, { 0.5f, -0.5f, -0.5f},
4774 {-0.5f, -0.5f, 0.5f}, {-0.5f, -0.5f, -0.5f}, { 0.5f, -0.5f, -0.5f}, { 0.5f, -0.5f, 0.5f},
4775 {-0.5f, -0.5f, 0.5f}, { 0.5f, -0.5f, 0.5f}, { 0.5f, 0.5f, 0.5f}, {-0.5f, 0.5f, 0.5f},
4776 {-0.5f, -0.5f, -0.5f}, {-0.5f, 0.5f, -0.5f}, { 0.5f, 0.5f, -0.5f}, { 0.5f, -0.5f, -0.5f}
4778 static const D3DXVECTOR3 normals[] =
4780 {-1.0f, 0.0f, 0.0f}, { 0.0f, 1.0f, 0.0f}, { 1.0f, 0.0f, 0.0f},
4781 { 0.0f, -1.0f, 0.0f}, { 0.0f, 0.0f, 1.0f}, { 0.0f, 0.0f, -1.0f}
4783 static const DWORD adjacency_table[] =
4785 6, 9, 1, 2, 10, 0, 1, 9, 3, 4, 10, 2,
4786 3, 8, 5, 7, 11, 4, 0, 11, 7, 5, 8, 6,
4787 7, 4, 9, 2, 0, 8, 1, 3, 11, 5, 6, 10
4790 TRACE("device %p, width %f, height %f, depth %f, mesh %p, adjacency %p\n",
4791 device, width, height, depth, mesh, adjacency);
4793 if (!device || width < 0.0f || height < 0.0f || depth < 0.0f || !mesh)
4795 return D3DERR_INVALIDCALL;
4798 if (FAILED(hr = D3DXCreateMeshFVF(12, 24, D3DXMESH_MANAGED, D3DFVF_XYZ | D3DFVF_NORMAL, device, &box)))
4800 return hr;
4803 if (FAILED(hr = box->lpVtbl->LockVertexBuffer(box, 0, (void **)&vertices)))
4805 box->lpVtbl->Release(box);
4806 return hr;
4809 if (FAILED(hr = box->lpVtbl->LockIndexBuffer(box, 0, (void **)&faces)))
4811 box->lpVtbl->UnlockVertexBuffer(box);
4812 box->lpVtbl->Release(box);
4813 return hr;
4816 for (i = 0; i < 24; i++)
4818 vertices[i].position.x = width * unit_box[i].x;
4819 vertices[i].position.y = height * unit_box[i].y;
4820 vertices[i].position.z = depth * unit_box[i].z;
4821 vertices[i].normal.x = normals[i / 4].x;
4822 vertices[i].normal.y = normals[i / 4].y;
4823 vertices[i].normal.z = normals[i / 4].z;
4826 face = 0;
4827 for (i = 0; i < 12; i++)
4829 faces[i][0] = face++;
4830 faces[i][1] = face++;
4831 faces[i][2] = (i % 2) ? face - 4 : face;
4834 box->lpVtbl->UnlockIndexBuffer(box);
4835 box->lpVtbl->UnlockVertexBuffer(box);
4837 if (adjacency)
4839 if (FAILED(hr = D3DXCreateBuffer(sizeof(adjacency_table), adjacency)))
4841 box->lpVtbl->Release(box);
4842 return hr;
4845 adjacency_buf = ID3DXBuffer_GetBufferPointer(*adjacency);
4846 memcpy(adjacency_buf, adjacency_table, sizeof(adjacency_table));
4849 *mesh = box;
4851 return D3D_OK;
4854 typedef WORD face[3];
4856 struct sincos_table
4858 float *sin;
4859 float *cos;
4862 static void free_sincos_table(struct sincos_table *sincos_table)
4864 free(sincos_table->cos);
4865 free(sincos_table->sin);
4868 /* pre compute sine and cosine tables; caller must free */
4869 static BOOL compute_sincos_table(struct sincos_table *sincos_table, float angle_start, float angle_step, int n)
4871 float angle;
4872 int i;
4874 sincos_table->sin = malloc(n * sizeof(*sincos_table->sin));
4875 if (!sincos_table->sin)
4877 return FALSE;
4879 sincos_table->cos = malloc(n * sizeof(*sincos_table->cos));
4880 if (!sincos_table->cos)
4882 free(sincos_table->sin);
4883 return FALSE;
4886 angle = angle_start;
4887 for (i = 0; i < n; i++)
4889 sincos_table->sin[i] = sinf(angle);
4890 sincos_table->cos[i] = cosf(angle);
4891 angle += angle_step;
4894 return TRUE;
4897 static WORD vertex_index(UINT slices, int slice, int stack)
4899 return stack*slices+slice+1;
4902 HRESULT WINAPI D3DXCreateSphere(struct IDirect3DDevice9 *device, float radius, UINT slices,
4903 UINT stacks, struct ID3DXMesh **mesh, struct ID3DXBuffer **adjacency)
4905 DWORD number_of_vertices, number_of_faces;
4906 HRESULT hr;
4907 ID3DXMesh *sphere;
4908 struct vertex *vertices;
4909 face *faces;
4910 float phi_step, phi_start;
4911 struct sincos_table phi;
4912 float theta_step, theta, sin_theta, cos_theta;
4913 DWORD vertex, face, stack, slice;
4915 TRACE("(%p, %f, %u, %u, %p, %p)\n", device, radius, slices, stacks, mesh, adjacency);
4917 if (!device || radius < 0.0f || slices < 2 || stacks < 2 || !mesh)
4919 return D3DERR_INVALIDCALL;
4922 number_of_vertices = 2 + slices * (stacks-1);
4923 number_of_faces = 2 * slices + (stacks - 2) * (2 * slices);
4925 hr = D3DXCreateMeshFVF(number_of_faces, number_of_vertices, D3DXMESH_MANAGED,
4926 D3DFVF_XYZ | D3DFVF_NORMAL, device, &sphere);
4927 if (FAILED(hr))
4929 return hr;
4932 if (FAILED(hr = sphere->lpVtbl->LockVertexBuffer(sphere, 0, (void **)&vertices)))
4934 sphere->lpVtbl->Release(sphere);
4935 return hr;
4938 if (FAILED(hr = sphere->lpVtbl->LockIndexBuffer(sphere, 0, (void **)&faces)))
4940 sphere->lpVtbl->UnlockVertexBuffer(sphere);
4941 sphere->lpVtbl->Release(sphere);
4942 return hr;
4945 /* phi = angle on xz plane wrt z axis */
4946 phi_step = -2.0f * D3DX_PI / slices;
4947 phi_start = D3DX_PI / 2.0f;
4949 if (!compute_sincos_table(&phi, phi_start, phi_step, slices))
4951 sphere->lpVtbl->UnlockIndexBuffer(sphere);
4952 sphere->lpVtbl->UnlockVertexBuffer(sphere);
4953 sphere->lpVtbl->Release(sphere);
4954 return E_OUTOFMEMORY;
4957 /* theta = angle on xy plane wrt x axis */
4958 theta_step = D3DX_PI / stacks;
4959 theta = theta_step;
4961 vertex = 0;
4962 face = 0;
4964 vertices[vertex].normal.x = 0.0f;
4965 vertices[vertex].normal.y = 0.0f;
4966 vertices[vertex].normal.z = 1.0f;
4967 vertices[vertex].position.x = 0.0f;
4968 vertices[vertex].position.y = 0.0f;
4969 vertices[vertex].position.z = radius;
4970 vertex++;
4972 for (stack = 0; stack < stacks - 1; stack++)
4974 sin_theta = sinf(theta);
4975 cos_theta = cosf(theta);
4977 for (slice = 0; slice < slices; slice++)
4979 vertices[vertex].normal.x = sin_theta * phi.cos[slice];
4980 vertices[vertex].normal.y = sin_theta * phi.sin[slice];
4981 vertices[vertex].normal.z = cos_theta;
4982 vertices[vertex].position.x = radius * sin_theta * phi.cos[slice];
4983 vertices[vertex].position.y = radius * sin_theta * phi.sin[slice];
4984 vertices[vertex].position.z = radius * cos_theta;
4985 vertex++;
4987 if (slice > 0)
4989 if (stack == 0)
4991 /* top stack is triangle fan */
4992 faces[face][0] = 0;
4993 faces[face][1] = slice + 1;
4994 faces[face][2] = slice;
4995 face++;
4997 else
4999 /* stacks in between top and bottom are quad strips */
5000 faces[face][0] = vertex_index(slices, slice-1, stack-1);
5001 faces[face][1] = vertex_index(slices, slice, stack-1);
5002 faces[face][2] = vertex_index(slices, slice-1, stack);
5003 face++;
5005 faces[face][0] = vertex_index(slices, slice, stack-1);
5006 faces[face][1] = vertex_index(slices, slice, stack);
5007 faces[face][2] = vertex_index(slices, slice-1, stack);
5008 face++;
5013 theta += theta_step;
5015 if (stack == 0)
5017 faces[face][0] = 0;
5018 faces[face][1] = 1;
5019 faces[face][2] = slice;
5020 face++;
5022 else
5024 faces[face][0] = vertex_index(slices, slice-1, stack-1);
5025 faces[face][1] = vertex_index(slices, 0, stack-1);
5026 faces[face][2] = vertex_index(slices, slice-1, stack);
5027 face++;
5029 faces[face][0] = vertex_index(slices, 0, stack-1);
5030 faces[face][1] = vertex_index(slices, 0, stack);
5031 faces[face][2] = vertex_index(slices, slice-1, stack);
5032 face++;
5036 vertices[vertex].position.x = 0.0f;
5037 vertices[vertex].position.y = 0.0f;
5038 vertices[vertex].position.z = -radius;
5039 vertices[vertex].normal.x = 0.0f;
5040 vertices[vertex].normal.y = 0.0f;
5041 vertices[vertex].normal.z = -1.0f;
5043 /* bottom stack is triangle fan */
5044 for (slice = 1; slice < slices; slice++)
5046 faces[face][0] = vertex_index(slices, slice-1, stack-1);
5047 faces[face][1] = vertex_index(slices, slice, stack-1);
5048 faces[face][2] = vertex;
5049 face++;
5052 faces[face][0] = vertex_index(slices, slice-1, stack-1);
5053 faces[face][1] = vertex_index(slices, 0, stack-1);
5054 faces[face][2] = vertex;
5056 free_sincos_table(&phi);
5057 sphere->lpVtbl->UnlockIndexBuffer(sphere);
5058 sphere->lpVtbl->UnlockVertexBuffer(sphere);
5061 if (adjacency)
5063 if (FAILED(hr = D3DXCreateBuffer(number_of_faces * sizeof(DWORD) * 3, adjacency)))
5065 sphere->lpVtbl->Release(sphere);
5066 return hr;
5069 if (FAILED(hr = sphere->lpVtbl->GenerateAdjacency(sphere, 0.0f, (*adjacency)->lpVtbl->GetBufferPointer(*adjacency))))
5071 (*adjacency)->lpVtbl->Release(*adjacency);
5072 sphere->lpVtbl->Release(sphere);
5073 return hr;
5077 *mesh = sphere;
5079 return D3D_OK;
5082 HRESULT WINAPI D3DXCreateCylinder(struct IDirect3DDevice9 *device, float radius1, float radius2,
5083 float length, UINT slices, UINT stacks, struct ID3DXMesh **mesh, struct ID3DXBuffer **adjacency)
5085 DWORD number_of_vertices, number_of_faces;
5086 HRESULT hr;
5087 ID3DXMesh *cylinder;
5088 struct vertex *vertices;
5089 face *faces;
5090 float theta_step, theta_start;
5091 struct sincos_table theta;
5092 float delta_radius, radius, radius_step;
5093 float z, z_step, z_normal;
5094 DWORD vertex, face, slice, stack;
5096 TRACE("(%p, %f, %f, %f, %u, %u, %p, %p)\n", device, radius1, radius2, length, slices, stacks, mesh, adjacency);
5098 if (device == NULL || radius1 < 0.0f || radius2 < 0.0f || length < 0.0f || slices < 2 || stacks < 1 || mesh == NULL)
5100 return D3DERR_INVALIDCALL;
5103 number_of_vertices = 2 + (slices * (3 + stacks));
5104 number_of_faces = 2 * slices + stacks * (2 * slices);
5106 hr = D3DXCreateMeshFVF(number_of_faces, number_of_vertices, D3DXMESH_MANAGED,
5107 D3DFVF_XYZ | D3DFVF_NORMAL, device, &cylinder);
5108 if (FAILED(hr))
5110 return hr;
5113 if (FAILED(hr = cylinder->lpVtbl->LockVertexBuffer(cylinder, 0, (void **)&vertices)))
5115 cylinder->lpVtbl->Release(cylinder);
5116 return hr;
5119 if (FAILED(hr = cylinder->lpVtbl->LockIndexBuffer(cylinder, 0, (void **)&faces)))
5121 cylinder->lpVtbl->UnlockVertexBuffer(cylinder);
5122 cylinder->lpVtbl->Release(cylinder);
5123 return hr;
5126 /* theta = angle on xy plane wrt x axis */
5127 theta_step = -2.0f * D3DX_PI / slices;
5128 theta_start = D3DX_PI / 2.0f;
5130 if (!compute_sincos_table(&theta, theta_start, theta_step, slices))
5132 cylinder->lpVtbl->UnlockIndexBuffer(cylinder);
5133 cylinder->lpVtbl->UnlockVertexBuffer(cylinder);
5134 cylinder->lpVtbl->Release(cylinder);
5135 return E_OUTOFMEMORY;
5138 vertex = 0;
5139 face = 0;
5141 delta_radius = radius1 - radius2;
5142 radius = radius1;
5143 radius_step = delta_radius / stacks;
5145 z = -length / 2;
5146 z_step = length / stacks;
5147 z_normal = delta_radius / length;
5148 if (isnan(z_normal))
5150 z_normal = 0.0f;
5153 vertices[vertex].normal.x = 0.0f;
5154 vertices[vertex].normal.y = 0.0f;
5155 vertices[vertex].normal.z = -1.0f;
5156 vertices[vertex].position.x = 0.0f;
5157 vertices[vertex].position.y = 0.0f;
5158 vertices[vertex++].position.z = z;
5160 for (slice = 0; slice < slices; slice++, vertex++)
5162 vertices[vertex].normal.x = 0.0f;
5163 vertices[vertex].normal.y = 0.0f;
5164 vertices[vertex].normal.z = -1.0f;
5165 vertices[vertex].position.x = radius * theta.cos[slice];
5166 vertices[vertex].position.y = radius * theta.sin[slice];
5167 vertices[vertex].position.z = z;
5169 if (slice > 0)
5171 faces[face][0] = 0;
5172 faces[face][1] = slice;
5173 faces[face++][2] = slice + 1;
5177 faces[face][0] = 0;
5178 faces[face][1] = slice;
5179 faces[face++][2] = 1;
5181 for (stack = 1; stack <= stacks+1; stack++)
5183 for (slice = 0; slice < slices; slice++, vertex++)
5185 vertices[vertex].normal.x = theta.cos[slice];
5186 vertices[vertex].normal.y = theta.sin[slice];
5187 vertices[vertex].normal.z = z_normal;
5188 D3DXVec3Normalize(&vertices[vertex].normal, &vertices[vertex].normal);
5189 vertices[vertex].position.x = radius * theta.cos[slice];
5190 vertices[vertex].position.y = radius * theta.sin[slice];
5191 vertices[vertex].position.z = z;
5193 if (stack > 1 && slice > 0)
5195 faces[face][0] = vertex_index(slices, slice-1, stack-1);
5196 faces[face][1] = vertex_index(slices, slice-1, stack);
5197 faces[face++][2] = vertex_index(slices, slice, stack-1);
5199 faces[face][0] = vertex_index(slices, slice, stack-1);
5200 faces[face][1] = vertex_index(slices, slice-1, stack);
5201 faces[face++][2] = vertex_index(slices, slice, stack);
5205 if (stack > 1)
5207 faces[face][0] = vertex_index(slices, slice-1, stack-1);
5208 faces[face][1] = vertex_index(slices, slice-1, stack);
5209 faces[face++][2] = vertex_index(slices, 0, stack-1);
5211 faces[face][0] = vertex_index(slices, 0, stack-1);
5212 faces[face][1] = vertex_index(slices, slice-1, stack);
5213 faces[face++][2] = vertex_index(slices, 0, stack);
5216 if (stack < stacks + 1)
5218 z += z_step;
5219 radius -= radius_step;
5223 for (slice = 0; slice < slices; slice++, vertex++)
5225 vertices[vertex].normal.x = 0.0f;
5226 vertices[vertex].normal.y = 0.0f;
5227 vertices[vertex].normal.z = 1.0f;
5228 vertices[vertex].position.x = radius * theta.cos[slice];
5229 vertices[vertex].position.y = radius * theta.sin[slice];
5230 vertices[vertex].position.z = z;
5232 if (slice > 0)
5234 faces[face][0] = vertex_index(slices, slice-1, stack);
5235 faces[face][1] = number_of_vertices - 1;
5236 faces[face++][2] = vertex_index(slices, slice, stack);
5240 vertices[vertex].position.x = 0.0f;
5241 vertices[vertex].position.y = 0.0f;
5242 vertices[vertex].position.z = z;
5243 vertices[vertex].normal.x = 0.0f;
5244 vertices[vertex].normal.y = 0.0f;
5245 vertices[vertex].normal.z = 1.0f;
5247 faces[face][0] = vertex_index(slices, slice-1, stack);
5248 faces[face][1] = number_of_vertices - 1;
5249 faces[face][2] = vertex_index(slices, 0, stack);
5251 free_sincos_table(&theta);
5252 cylinder->lpVtbl->UnlockIndexBuffer(cylinder);
5253 cylinder->lpVtbl->UnlockVertexBuffer(cylinder);
5255 if (adjacency)
5257 if (FAILED(hr = D3DXCreateBuffer(number_of_faces * sizeof(DWORD) * 3, adjacency)))
5259 cylinder->lpVtbl->Release(cylinder);
5260 return hr;
5263 if (FAILED(hr = cylinder->lpVtbl->GenerateAdjacency(cylinder, 0.0f, (*adjacency)->lpVtbl->GetBufferPointer(*adjacency))))
5265 (*adjacency)->lpVtbl->Release(*adjacency);
5266 cylinder->lpVtbl->Release(cylinder);
5267 return hr;
5271 *mesh = cylinder;
5273 return D3D_OK;
5276 HRESULT WINAPI D3DXCreateTeapot(struct IDirect3DDevice9 *device,
5277 struct ID3DXMesh **mesh, struct ID3DXBuffer **adjacency)
5279 FIXME("device %p, mesh %p, adjacency %p semi-stub.\n", device, mesh, adjacency);
5281 return D3DXCreateSphere(device, 1.0f, 4, 4, mesh, adjacency);
5284 HRESULT WINAPI D3DXCreateTextA(struct IDirect3DDevice9 *device, HDC hdc, const char *text, float deviation,
5285 float extrusion, struct ID3DXMesh **mesh, struct ID3DXBuffer **adjacency, GLYPHMETRICSFLOAT *glyphmetrics)
5287 WCHAR *textW;
5288 HRESULT hr;
5289 int len;
5291 TRACE("device %p, hdc %p, text %s, deviation %.8e, extrusion %.8e, mesh %p, adjacency %p, glyphmetrics %p.\n",
5292 device, hdc, debugstr_a(text), deviation, extrusion, mesh, adjacency, glyphmetrics);
5294 if (!text)
5295 return D3DERR_INVALIDCALL;
5297 len = MultiByteToWideChar(CP_ACP, 0, text, -1, NULL, 0);
5298 textW = malloc(len * sizeof(WCHAR));
5299 MultiByteToWideChar(CP_ACP, 0, text, -1, textW, len);
5301 hr = D3DXCreateTextW(device, hdc, textW, deviation, extrusion,
5302 mesh, adjacency, glyphmetrics);
5303 free(textW);
5305 return hr;
5308 HRESULT WINAPI D3DXCreateTorus(struct IDirect3DDevice9 *device,
5309 float innerradius, float outerradius, UINT sides, UINT rings, struct ID3DXMesh **mesh, ID3DXBuffer **adjacency)
5311 HRESULT hr;
5312 ID3DXMesh *torus;
5313 WORD (*faces)[3];
5314 struct vertex *vertices;
5315 float phi, phi_step, sin_phi, cos_phi;
5316 float theta, theta_step, sin_theta, cos_theta;
5317 unsigned int i, j, numvert, numfaces;
5319 TRACE("device %p, innerradius %.8e, outerradius %.8e, sides %u, rings %u, mesh %p, adjacency %p.\n",
5320 device, innerradius, outerradius, sides, rings, mesh, adjacency);
5322 numvert = sides * rings;
5323 numfaces = numvert * 2;
5325 if (!device || innerradius < 0.0f || outerradius < 0.0f || sides < 3 || rings < 3 || !mesh)
5327 WARN("Invalid arguments.\n");
5328 return D3DERR_INVALIDCALL;
5331 if (FAILED(hr = D3DXCreateMeshFVF(numfaces, numvert, D3DXMESH_MANAGED, D3DFVF_XYZ | D3DFVF_NORMAL, device, &torus)))
5332 return hr;
5334 if (FAILED(hr = torus->lpVtbl->LockVertexBuffer(torus, 0, (void **)&vertices)))
5336 torus->lpVtbl->Release(torus);
5337 return hr;
5340 if (FAILED(hr = torus->lpVtbl->LockIndexBuffer(torus, 0, (void **)&faces)))
5342 torus->lpVtbl->UnlockVertexBuffer(torus);
5343 torus->lpVtbl->Release(torus);
5344 return hr;
5347 phi_step = D3DX_PI / sides * 2.0f;
5348 theta_step = D3DX_PI / rings * -2.0f;
5350 theta = 0.0f;
5352 for (i = 0; i < rings; ++i)
5354 phi = 0.0f;
5356 sin_theta = sinf(theta);
5357 cos_theta = cosf(theta);
5359 for (j = 0; j < sides; ++j)
5361 sin_phi = sinf(phi);
5362 cos_phi = cosf(phi);
5364 vertices[i * sides + j].position.x = (innerradius * cos_phi + outerradius) * cos_theta;
5365 vertices[i * sides + j].position.y = (innerradius * cos_phi + outerradius) * sin_theta;
5366 vertices[i * sides + j].position.z = innerradius * sin_phi;
5367 vertices[i * sides + j].normal.x = cos_phi * cos_theta;
5368 vertices[i * sides + j].normal.y = cos_phi * sin_theta;
5369 vertices[i * sides + j].normal.z = sin_phi;
5371 phi += phi_step;
5374 theta += theta_step;
5377 for (i = 0; i < numfaces - sides * 2; ++i)
5379 faces[i][0] = i % 2 ? i / 2 + sides : i / 2;
5380 faces[i][1] = (i / 2 + 1) % sides ? i / 2 + 1 : i / 2 + 1 - sides;
5381 faces[i][2] = (i + 1) % (sides * 2) ? (i + 1) / 2 + sides : (i + 1) / 2;
5384 for (j = 0; i < numfaces; ++i, ++j)
5386 faces[i][0] = i % 2 ? j / 2 : i / 2;
5387 faces[i][1] = (i / 2 + 1) % sides ? i / 2 + 1 : i / 2 + 1 - sides;
5388 faces[i][2] = i == numfaces - 1 ? 0 : (j + 1) / 2;
5391 torus->lpVtbl->UnlockIndexBuffer(torus);
5392 torus->lpVtbl->UnlockVertexBuffer(torus);
5394 if (adjacency)
5396 if (FAILED(hr = D3DXCreateBuffer(numfaces * sizeof(DWORD) * 3, adjacency)))
5398 torus->lpVtbl->Release(torus);
5399 return hr;
5402 if (FAILED(hr = torus->lpVtbl->GenerateAdjacency(torus, 0.0f, (*adjacency)->lpVtbl->GetBufferPointer(*adjacency))))
5404 (*adjacency)->lpVtbl->Release(*adjacency);
5405 torus->lpVtbl->Release(torus);
5406 return hr;
5410 *mesh = torus;
5412 return D3D_OK;
5415 enum pointtype {
5416 POINTTYPE_CURVE = 0,
5417 POINTTYPE_CORNER,
5418 POINTTYPE_CURVE_START,
5419 POINTTYPE_CURVE_END,
5420 POINTTYPE_CURVE_MIDDLE,
5423 struct point2d
5425 D3DXVECTOR2 pos;
5426 enum pointtype corner;
5429 struct dynamic_array
5431 int count, capacity;
5432 void *items;
5435 /* is a dynamic_array */
5436 struct outline
5438 int count, capacity;
5439 struct point2d *items;
5442 /* is a dynamic_array */
5443 struct outline_array
5445 int count, capacity;
5446 struct outline *items;
5449 struct face_array
5451 int count;
5452 face *items;
5455 struct point2d_index
5457 struct outline *outline;
5458 int vertex;
5461 struct point2d_index_array
5463 int count;
5464 struct point2d_index *items;
5467 struct glyphinfo
5469 struct outline_array outlines;
5470 struct face_array faces;
5471 struct point2d_index_array ordered_vertices;
5472 float offset_x;
5475 /* is an dynamic_array */
5476 struct word_array
5478 int count, capacity;
5479 WORD *items;
5482 /* complex polygons are split into monotone polygons, which have
5483 * at most 2 intersections with the vertical sweep line */
5484 struct triangulation
5486 struct word_array vertex_stack;
5487 BOOL last_on_top, merging;
5490 /* is an dynamic_array */
5491 struct triangulation_array
5493 int count, capacity;
5494 struct triangulation *items;
5496 struct glyphinfo *glyph;
5499 static BOOL reserve(struct dynamic_array *array, int count, int itemsize)
5501 if (count > array->capacity) {
5502 void *new_buffer;
5503 int new_capacity = max(array->capacity ? array->capacity * 2 : 16, count);
5504 new_buffer = realloc(array->items, new_capacity * itemsize);
5505 if (!new_buffer)
5506 return FALSE;
5507 array->items = new_buffer;
5508 array->capacity = new_capacity;
5510 return TRUE;
5513 static struct point2d *add_points(struct outline *array, int num)
5515 struct point2d *item;
5517 if (!reserve((struct dynamic_array *)array, array->count + num, sizeof(array->items[0])))
5518 return NULL;
5520 item = &array->items[array->count];
5521 array->count += num;
5522 return item;
5525 static struct outline *add_outline(struct outline_array *array)
5527 struct outline *item;
5529 if (!reserve((struct dynamic_array *)array, array->count + 1, sizeof(array->items[0])))
5530 return NULL;
5532 item = &array->items[array->count++];
5533 ZeroMemory(item, sizeof(*item));
5534 return item;
5537 static inline face *add_face(struct face_array *array)
5539 return &array->items[array->count++];
5542 static struct triangulation *add_triangulation(struct triangulation_array *array)
5544 struct triangulation *item;
5546 if (!reserve((struct dynamic_array *)array, array->count + 1, sizeof(array->items[0])))
5547 return NULL;
5549 item = &array->items[array->count++];
5550 ZeroMemory(item, sizeof(*item));
5551 return item;
5554 static HRESULT add_vertex_index(struct word_array *array, WORD vertex_index)
5556 if (!reserve((struct dynamic_array *)array, array->count + 1, sizeof(array->items[0])))
5557 return E_OUTOFMEMORY;
5559 array->items[array->count++] = vertex_index;
5560 return S_OK;
5563 /* assume fixed point numbers can be converted to float point in place */
5564 C_ASSERT(sizeof(FIXED) == sizeof(float));
5565 C_ASSERT(sizeof(POINTFX) == sizeof(D3DXVECTOR2));
5567 static inline D3DXVECTOR2 *convert_fixed_to_float(POINTFX *pt, int count, unsigned int emsquare)
5569 D3DXVECTOR2 *ret = (D3DXVECTOR2*)pt;
5570 while (count--) {
5571 D3DXVECTOR2 *pt_flt = (D3DXVECTOR2*)pt;
5572 pt_flt->x = (pt->x.value + pt->x.fract / (float)0x10000) / emsquare;
5573 pt_flt->y = (pt->y.value + pt->y.fract / (float)0x10000) / emsquare;
5574 pt++;
5576 return ret;
5579 static HRESULT add_bezier_points(struct outline *outline, const D3DXVECTOR2 *p1,
5580 const D3DXVECTOR2 *p2, const D3DXVECTOR2 *p3,
5581 float max_deviation_sq)
5583 D3DXVECTOR2 split1 = {0, 0}, split2 = {0, 0}, middle, vec;
5584 float deviation_sq;
5586 D3DXVec2Scale(&split1, D3DXVec2Add(&split1, p1, p2), 0.5f);
5587 D3DXVec2Scale(&split2, D3DXVec2Add(&split2, p2, p3), 0.5f);
5588 D3DXVec2Scale(&middle, D3DXVec2Add(&middle, &split1, &split2), 0.5f);
5590 deviation_sq = D3DXVec2LengthSq(D3DXVec2Subtract(&vec, &middle, p2));
5591 if (deviation_sq < max_deviation_sq) {
5592 struct point2d *pt = add_points(outline, 1);
5593 if (!pt) return E_OUTOFMEMORY;
5594 pt->pos = *p2;
5595 pt->corner = POINTTYPE_CURVE;
5596 /* the end point is omitted because the end line merges into the next segment of
5597 * the split bezier curve, and the end of the split bezier curve is added outside
5598 * this recursive function. */
5599 } else {
5600 HRESULT hr = add_bezier_points(outline, p1, &split1, &middle, max_deviation_sq);
5601 if (hr != S_OK) return hr;
5602 hr = add_bezier_points(outline, &middle, &split2, p3, max_deviation_sq);
5603 if (hr != S_OK) return hr;
5606 return S_OK;
5609 static inline BOOL is_direction_similar(D3DXVECTOR2 *dir1, D3DXVECTOR2 *dir2, float cos_theta)
5611 /* dot product = cos(theta) */
5612 return D3DXVec2Dot(dir1, dir2) > cos_theta;
5615 static inline D3DXVECTOR2 *unit_vec2(D3DXVECTOR2 *dir, const D3DXVECTOR2 *pt1, const D3DXVECTOR2 *pt2)
5617 return D3DXVec2Normalize(dir, D3DXVec2Subtract(dir, pt2, pt1));
5620 struct cos_table
5622 float cos_half;
5623 float cos_45;
5624 float cos_90;
5627 static BOOL attempt_line_merge(struct outline *outline,
5628 int pt_index,
5629 const D3DXVECTOR2 *nextpt,
5630 BOOL to_curve,
5631 const struct cos_table *table)
5633 D3DXVECTOR2 curdir, lastdir;
5634 struct point2d *prevpt, *pt;
5635 BOOL ret = FALSE;
5637 pt = &outline->items[pt_index];
5638 pt_index = (pt_index - 1 + outline->count) % outline->count;
5639 prevpt = &outline->items[pt_index];
5641 if (to_curve)
5642 pt->corner = pt->corner != POINTTYPE_CORNER ? POINTTYPE_CURVE_MIDDLE : POINTTYPE_CURVE_START;
5644 if (outline->count < 2)
5645 return FALSE;
5647 /* remove last point if the next line continues the last line */
5648 unit_vec2(&lastdir, &prevpt->pos, &pt->pos);
5649 unit_vec2(&curdir, &pt->pos, nextpt);
5650 if (is_direction_similar(&lastdir, &curdir, table->cos_half))
5652 outline->count--;
5653 if (pt->corner == POINTTYPE_CURVE_END)
5654 prevpt->corner = pt->corner;
5655 if (prevpt->corner == POINTTYPE_CURVE_END && to_curve)
5656 prevpt->corner = POINTTYPE_CURVE_MIDDLE;
5657 pt = prevpt;
5659 ret = TRUE;
5660 if (outline->count < 2)
5661 return ret;
5663 pt_index = (pt_index - 1 + outline->count) % outline->count;
5664 prevpt = &outline->items[pt_index];
5665 unit_vec2(&lastdir, &prevpt->pos, &pt->pos);
5666 unit_vec2(&curdir, &pt->pos, nextpt);
5668 return ret;
5671 static HRESULT create_outline(struct glyphinfo *glyph, void *raw_outline, int datasize,
5672 float max_deviation_sq, unsigned int emsquare,
5673 const struct cos_table *cos_table)
5675 TTPOLYGONHEADER *header = (TTPOLYGONHEADER *)raw_outline;
5677 while ((char *)header < (char *)raw_outline + datasize)
5679 TTPOLYCURVE *curve = (TTPOLYCURVE *)(header + 1);
5680 struct point2d *lastpt, *pt;
5681 D3DXVECTOR2 lastdir;
5682 D3DXVECTOR2 *pt_flt;
5683 int j;
5684 struct outline *outline = add_outline(&glyph->outlines);
5686 if (!outline)
5687 return E_OUTOFMEMORY;
5689 pt = add_points(outline, 1);
5690 if (!pt)
5691 return E_OUTOFMEMORY;
5692 pt_flt = convert_fixed_to_float(&header->pfxStart, 1, emsquare);
5693 pt->pos = *pt_flt;
5694 pt->corner = POINTTYPE_CORNER;
5696 if (header->dwType != TT_POLYGON_TYPE)
5697 FIXME("Unknown header type %lu.\n", header->dwType);
5699 while ((char *)curve < (char *)header + header->cb)
5701 D3DXVECTOR2 bezier_start = outline->items[outline->count - 1].pos;
5702 BOOL to_curve = curve->wType != TT_PRIM_LINE && curve->cpfx > 1;
5703 unsigned int j2 = 0;
5705 if (!curve->cpfx) {
5706 curve = (TTPOLYCURVE *)&curve->apfx[curve->cpfx];
5707 continue;
5710 pt_flt = convert_fixed_to_float(curve->apfx, curve->cpfx, emsquare);
5712 attempt_line_merge(outline, outline->count - 1, &pt_flt[0], to_curve, cos_table);
5714 if (to_curve)
5716 HRESULT hr;
5717 int count = curve->cpfx;
5719 while (count > 2)
5721 D3DXVECTOR2 bezier_end;
5723 D3DXVec2Scale(&bezier_end, D3DXVec2Add(&bezier_end, &pt_flt[j2], &pt_flt[j2+1]), 0.5f);
5724 hr = add_bezier_points(outline, &bezier_start, &pt_flt[j2], &bezier_end, max_deviation_sq);
5725 if (hr != S_OK)
5726 return hr;
5727 bezier_start = bezier_end;
5728 count--;
5729 j2++;
5731 hr = add_bezier_points(outline, &bezier_start, &pt_flt[j2], &pt_flt[j2+1], max_deviation_sq);
5732 if (hr != S_OK)
5733 return hr;
5735 pt = add_points(outline, 1);
5736 if (!pt)
5737 return E_OUTOFMEMORY;
5738 j2++;
5739 pt->pos = pt_flt[j2];
5740 pt->corner = POINTTYPE_CURVE_END;
5741 } else {
5742 pt = add_points(outline, curve->cpfx);
5743 if (!pt)
5744 return E_OUTOFMEMORY;
5745 for (j2 = 0; j2 < curve->cpfx; j2++)
5747 pt->pos = pt_flt[j2];
5748 pt->corner = POINTTYPE_CORNER;
5749 pt++;
5753 curve = (TTPOLYCURVE *)&curve->apfx[curve->cpfx];
5756 /* remove last point if the next line continues the last line */
5757 if (outline->count >= 3) {
5758 BOOL to_curve;
5760 lastpt = &outline->items[outline->count - 1];
5761 pt = &outline->items[0];
5762 if (pt->pos.x == lastpt->pos.x && pt->pos.y == lastpt->pos.y) {
5763 if (lastpt->corner == POINTTYPE_CURVE_END)
5765 if (pt->corner == POINTTYPE_CURVE_START)
5766 pt->corner = POINTTYPE_CURVE_MIDDLE;
5767 else
5768 pt->corner = POINTTYPE_CURVE_END;
5770 outline->count--;
5771 } else {
5772 /* outline closed with a line from end to start point */
5773 attempt_line_merge(outline, outline->count - 1, &pt->pos, FALSE, cos_table);
5775 lastpt = &outline->items[0];
5776 to_curve = lastpt->corner != POINTTYPE_CORNER && lastpt->corner != POINTTYPE_CURVE_END;
5777 if (lastpt->corner == POINTTYPE_CURVE_START)
5778 lastpt->corner = POINTTYPE_CORNER;
5779 pt = &outline->items[1];
5780 if (attempt_line_merge(outline, 0, &pt->pos, to_curve, cos_table))
5781 *lastpt = outline->items[outline->count];
5784 lastpt = &outline->items[outline->count - 1];
5785 pt = &outline->items[0];
5786 unit_vec2(&lastdir, &lastpt->pos, &pt->pos);
5787 for (j = 0; j < outline->count; j++)
5789 D3DXVECTOR2 curdir;
5791 lastpt = pt;
5792 pt = &outline->items[(j + 1) % outline->count];
5793 unit_vec2(&curdir, &lastpt->pos, &pt->pos);
5795 switch (lastpt->corner)
5797 case POINTTYPE_CURVE_START:
5798 case POINTTYPE_CURVE_END:
5799 if (!is_direction_similar(&lastdir, &curdir, cos_table->cos_45))
5800 lastpt->corner = POINTTYPE_CORNER;
5801 break;
5802 case POINTTYPE_CURVE_MIDDLE:
5803 if (!is_direction_similar(&lastdir, &curdir, cos_table->cos_90))
5804 lastpt->corner = POINTTYPE_CORNER;
5805 else
5806 lastpt->corner = POINTTYPE_CURVE;
5807 break;
5808 default:
5809 break;
5811 lastdir = curdir;
5814 header = (TTPOLYGONHEADER *)((char *)header + header->cb);
5816 return S_OK;
5819 /* Get the y-distance from a line to a point */
5820 static float get_line_to_point_y_distance(D3DXVECTOR2 *line_pt1,
5821 D3DXVECTOR2 *line_pt2,
5822 D3DXVECTOR2 *point)
5824 D3DXVECTOR2 line_vec = {0, 0};
5825 float line_pt_dx;
5826 float line_y;
5828 D3DXVec2Subtract(&line_vec, line_pt2, line_pt1);
5829 line_pt_dx = point->x - line_pt1->x;
5830 line_y = line_pt1->y + (line_vec.y * line_pt_dx) / line_vec.x;
5831 return point->y - line_y;
5834 static D3DXVECTOR2 *get_indexed_point(struct point2d_index *pt_idx)
5836 return &pt_idx->outline->items[pt_idx->vertex].pos;
5839 static D3DXVECTOR2 *get_ordered_vertex(struct glyphinfo *glyph, WORD index)
5841 return get_indexed_point(&glyph->ordered_vertices.items[index]);
5844 static void remove_triangulation(struct triangulation_array *array, struct triangulation *item)
5846 free(item->vertex_stack.items);
5847 MoveMemory(item, item + 1, (char*)&array->items[array->count] - (char*)(item + 1));
5848 array->count--;
5851 static HRESULT triangulation_add_point(struct triangulation **t_ptr,
5852 struct triangulation_array *triangulations,
5853 WORD vtx_idx,
5854 BOOL to_top)
5856 struct glyphinfo *glyph = triangulations->glyph;
5857 struct triangulation *t = *t_ptr;
5858 HRESULT hr;
5859 face *face;
5860 int f1, f2;
5862 if (t->last_on_top) {
5863 f1 = 1;
5864 f2 = 2;
5865 } else {
5866 f1 = 2;
5867 f2 = 1;
5870 if (t->last_on_top != to_top && t->vertex_stack.count > 1) {
5871 /* consume all vertices on the stack */
5872 WORD last_pt = t->vertex_stack.items[0];
5873 int i;
5874 for (i = 1; i < t->vertex_stack.count; i++)
5876 face = add_face(&glyph->faces);
5877 if (!face) return E_OUTOFMEMORY;
5878 (*face)[0] = vtx_idx;
5879 (*face)[f1] = last_pt;
5880 (*face)[f2] = last_pt = t->vertex_stack.items[i];
5882 t->vertex_stack.items[0] = last_pt;
5883 t->vertex_stack.count = 1;
5884 } else if (t->vertex_stack.count > 1) {
5885 int i = t->vertex_stack.count - 1;
5886 D3DXVECTOR2 *point = get_ordered_vertex(glyph, vtx_idx);
5887 WORD top_idx = t->vertex_stack.items[i--];
5888 D3DXVECTOR2 *top_pt = get_ordered_vertex(glyph, top_idx);
5890 while (i >= 0)
5892 WORD prev_idx = t->vertex_stack.items[i--];
5893 D3DXVECTOR2 *prev_pt = get_ordered_vertex(glyph, prev_idx);
5895 if (prev_pt->x != top_pt->x &&
5896 ((to_top && get_line_to_point_y_distance(prev_pt, top_pt, point) > 0) ||
5897 (!to_top && get_line_to_point_y_distance(prev_pt, top_pt, point) < 0)))
5898 break;
5900 face = add_face(&glyph->faces);
5901 if (!face) return E_OUTOFMEMORY;
5902 (*face)[0] = vtx_idx;
5903 (*face)[f1] = prev_idx;
5904 (*face)[f2] = top_idx;
5906 top_pt = prev_pt;
5907 top_idx = prev_idx;
5908 t->vertex_stack.count--;
5911 t->last_on_top = to_top;
5913 hr = add_vertex_index(&t->vertex_stack, vtx_idx);
5915 if (hr == S_OK && t->merging) {
5916 struct triangulation *t2;
5918 t2 = to_top ? t - 1 : t + 1;
5919 t2->merging = FALSE;
5920 hr = triangulation_add_point(&t2, triangulations, vtx_idx, to_top);
5921 if (hr != S_OK) return hr;
5922 remove_triangulation(triangulations, t);
5923 if (t2 > t)
5924 t2--;
5925 *t_ptr = t2;
5927 return hr;
5930 /* check if the point is next on the outline for either the top or bottom */
5931 static D3DXVECTOR2 *triangulation_get_next_point(struct triangulation *t, struct glyphinfo *glyph, BOOL on_top)
5933 int i = t->last_on_top == on_top ? t->vertex_stack.count - 1 : 0;
5934 WORD idx = t->vertex_stack.items[i];
5935 struct point2d_index *pt_idx = &glyph->ordered_vertices.items[idx];
5936 struct outline *outline = pt_idx->outline;
5938 if (on_top)
5939 i = (pt_idx->vertex + outline->count - 1) % outline->count;
5940 else
5941 i = (pt_idx->vertex + 1) % outline->count;
5943 return &outline->items[i].pos;
5946 static int __cdecl compare_vertex_indices(const void *a, const void *b)
5948 const struct point2d_index *idx1 = a, *idx2 = b;
5949 const D3DXVECTOR2 *p1 = &idx1->outline->items[idx1->vertex].pos;
5950 const D3DXVECTOR2 *p2 = &idx2->outline->items[idx2->vertex].pos;
5951 float diff = p1->x - p2->x;
5953 if (diff == 0.0f)
5954 diff = p1->y - p2->y;
5956 return diff == 0.0f ? 0 : (diff > 0.0f ? -1 : 1);
5959 static HRESULT triangulate(struct triangulation_array *triangulations)
5961 int sweep_idx;
5962 HRESULT hr;
5963 struct glyphinfo *glyph = triangulations->glyph;
5964 int nb_vertices = 0;
5965 int i;
5966 struct point2d_index *idx_ptr;
5968 /* Glyphs without outlines do not generate any vertices. */
5969 if (!glyph->outlines.count)
5970 return D3D_OK;
5972 for (i = 0; i < glyph->outlines.count; i++)
5973 nb_vertices += glyph->outlines.items[i].count;
5975 glyph->ordered_vertices.items = malloc(nb_vertices * sizeof(*glyph->ordered_vertices.items));
5976 if (!glyph->ordered_vertices.items)
5977 return E_OUTOFMEMORY;
5979 idx_ptr = glyph->ordered_vertices.items;
5980 for (i = 0; i < glyph->outlines.count; i++)
5982 struct outline *outline = &glyph->outlines.items[i];
5983 int j;
5985 idx_ptr->outline = outline;
5986 idx_ptr->vertex = 0;
5987 idx_ptr++;
5988 for (j = outline->count - 1; j > 0; j--)
5990 idx_ptr->outline = outline;
5991 idx_ptr->vertex = j;
5992 idx_ptr++;
5995 glyph->ordered_vertices.count = nb_vertices;
5997 /* Native implementation seems to try to create a triangle fan from
5998 * the first outline point if the glyph only has one outline. */
5999 if (glyph->outlines.count == 1)
6001 struct outline *outline = glyph->outlines.items;
6002 D3DXVECTOR2 *base = &outline->items[0].pos;
6003 D3DXVECTOR2 *last = &outline->items[1].pos;
6004 float ccw = 0;
6006 for (i = 2; i < outline->count; i++)
6008 D3DXVECTOR2 *next = &outline->items[i].pos;
6009 D3DXVECTOR2 v1 = {0.0f, 0.0f};
6010 D3DXVECTOR2 v2 = {0.0f, 0.0f};
6012 D3DXVec2Subtract(&v1, base, last);
6013 D3DXVec2Subtract(&v2, last, next);
6014 ccw = D3DXVec2CCW(&v1, &v2);
6015 if (ccw > 0.0f)
6016 break;
6018 last = next;
6020 if (ccw <= 0)
6022 glyph->faces.items = malloc((outline->count - 2) * sizeof(glyph->faces.items[0]));
6023 if (!glyph->faces.items)
6024 return E_OUTOFMEMORY;
6026 glyph->faces.count = outline->count - 2;
6027 for (i = 0; i < glyph->faces.count; i++)
6029 glyph->faces.items[i][0] = 0;
6030 glyph->faces.items[i][1] = i + 1;
6031 glyph->faces.items[i][2] = i + 2;
6033 return S_OK;
6037 /* Perform 2D polygon triangulation for complex glyphs.
6038 * Triangulation is performed using a sweep line concept, from right to left,
6039 * by processing vertices in sorted order. Complex polygons are split into
6040 * monotone polygons which are triangulated separately. */
6041 /* FIXME: The order of the faces is not consistent with the native implementation. */
6043 /* Reserve space for maximum possible faces from triangulation.
6044 * # faces for outer outlines = outline->count - 2
6045 * # faces for inner outlines = outline->count + 2
6046 * There must be at least 1 outer outline. */
6047 glyph->faces.items = malloc((nb_vertices + glyph->outlines.count * 2 - 4) * sizeof(glyph->faces.items[0]));
6048 if (!glyph->faces.items)
6049 return E_OUTOFMEMORY;
6051 qsort(glyph->ordered_vertices.items, nb_vertices,
6052 sizeof(glyph->ordered_vertices.items[0]), compare_vertex_indices);
6053 for (sweep_idx = 0; sweep_idx < glyph->ordered_vertices.count; sweep_idx++)
6055 int start = 0;
6056 int end = triangulations->count;
6058 while (start < end)
6060 D3DXVECTOR2 *sweep_vtx = get_ordered_vertex(glyph, sweep_idx);
6061 int current = (start + end) / 2;
6062 struct triangulation *t = &triangulations->items[current];
6063 BOOL on_top_outline = FALSE;
6064 D3DXVECTOR2 *top_next, *bottom_next;
6065 WORD top_idx, bottom_idx;
6067 if (t->merging && t->last_on_top)
6068 top_next = triangulation_get_next_point(t + 1, glyph, TRUE);
6069 else
6070 top_next = triangulation_get_next_point(t, glyph, TRUE);
6071 if (sweep_vtx == top_next)
6073 if (t->merging && t->last_on_top)
6074 t++;
6075 hr = triangulation_add_point(&t, triangulations, sweep_idx, TRUE);
6076 if (hr != S_OK) return hr;
6078 if (t + 1 < &triangulations->items[triangulations->count] &&
6079 triangulation_get_next_point(t + 1, glyph, FALSE) == sweep_vtx)
6081 /* point also on bottom outline of higher triangulation */
6082 struct triangulation *t2 = t + 1;
6083 hr = triangulation_add_point(&t2, triangulations, sweep_idx, FALSE);
6084 if (hr != S_OK) return hr;
6086 t->merging = TRUE;
6087 t2->merging = TRUE;
6089 on_top_outline = TRUE;
6092 if (t->merging && !t->last_on_top)
6093 bottom_next = triangulation_get_next_point(t - 1, glyph, FALSE);
6094 else
6095 bottom_next = triangulation_get_next_point(t, glyph, FALSE);
6096 if (sweep_vtx == bottom_next)
6098 if (t->merging && !t->last_on_top)
6099 t--;
6100 if (on_top_outline) {
6101 /* outline finished */
6102 remove_triangulation(triangulations, t);
6103 break;
6106 hr = triangulation_add_point(&t, triangulations, sweep_idx, FALSE);
6107 if (hr != S_OK) return hr;
6109 if (t > triangulations->items &&
6110 triangulation_get_next_point(t - 1, glyph, TRUE) == sweep_vtx)
6112 struct triangulation *t2 = t - 1;
6113 /* point also on top outline of lower triangulation */
6114 hr = triangulation_add_point(&t2, triangulations, sweep_idx, TRUE);
6115 if (hr != S_OK) return hr;
6116 t = t2 + 1; /* t may be invalidated by triangulation merging */
6118 t->merging = TRUE;
6119 t2->merging = TRUE;
6121 break;
6123 if (on_top_outline)
6124 break;
6126 if (t->last_on_top) {
6127 top_idx = t->vertex_stack.items[t->vertex_stack.count - 1];
6128 bottom_idx = t->vertex_stack.items[0];
6129 } else {
6130 top_idx = t->vertex_stack.items[0];
6131 bottom_idx = t->vertex_stack.items[t->vertex_stack.count - 1];
6134 /* check if the point is inside or outside this polygon */
6135 if (get_line_to_point_y_distance(get_ordered_vertex(glyph, top_idx),
6136 top_next, sweep_vtx) > 0)
6137 { /* above */
6138 start = current + 1;
6139 } else if (get_line_to_point_y_distance(get_ordered_vertex(glyph, bottom_idx),
6140 bottom_next, sweep_vtx) < 0)
6141 { /* below */
6142 end = current;
6143 } else if (t->merging) {
6144 /* inside, so cancel merging */
6145 struct triangulation *t2 = t->last_on_top ? t + 1 : t - 1;
6146 t->merging = FALSE;
6147 t2->merging = FALSE;
6148 hr = triangulation_add_point(&t, triangulations, sweep_idx, t->last_on_top);
6149 if (hr != S_OK) return hr;
6150 hr = triangulation_add_point(&t2, triangulations, sweep_idx, t2->last_on_top);
6151 if (hr != S_OK) return hr;
6152 break;
6153 } else {
6154 /* inside, so split polygon into two monotone parts */
6155 struct triangulation *t2 = add_triangulation(triangulations);
6156 if (!t2) return E_OUTOFMEMORY;
6157 MoveMemory(t + 1, t, (char*)(t2 + 1) - (char*)t);
6158 if (t->last_on_top) {
6159 t2 = t + 1;
6160 } else {
6161 t2 = t;
6162 t++;
6165 ZeroMemory(&t2->vertex_stack, sizeof(t2->vertex_stack));
6166 hr = add_vertex_index(&t2->vertex_stack, t->vertex_stack.items[t->vertex_stack.count - 1]);
6167 if (hr != S_OK) return hr;
6168 hr = add_vertex_index(&t2->vertex_stack, sweep_idx);
6169 if (hr != S_OK) return hr;
6170 t2->last_on_top = !t->last_on_top;
6172 hr = triangulation_add_point(&t, triangulations, sweep_idx, t->last_on_top);
6173 if (hr != S_OK) return hr;
6174 break;
6177 if (start >= end)
6179 struct triangulation *t;
6180 struct triangulation *t2 = add_triangulation(triangulations);
6181 if (!t2) return E_OUTOFMEMORY;
6182 t = &triangulations->items[start];
6183 MoveMemory(t + 1, t, (char*)(t2 + 1) - (char*)t);
6184 ZeroMemory(t, sizeof(*t));
6185 hr = add_vertex_index(&t->vertex_stack, sweep_idx);
6186 if (hr != S_OK) return hr;
6189 return S_OK;
6192 HRESULT WINAPI D3DXCreateTextW(struct IDirect3DDevice9 *device, HDC hdc, const WCHAR *text, float deviation,
6193 float extrusion, struct ID3DXMesh **mesh_ptr, struct ID3DXBuffer **adjacency, GLYPHMETRICSFLOAT *glyphmetrics)
6195 HRESULT hr;
6196 ID3DXMesh *mesh = NULL;
6197 DWORD nb_vertices, nb_faces;
6198 DWORD nb_front_faces, nb_corners, nb_outline_points;
6199 struct vertex *vertices = NULL;
6200 face *faces = NULL;
6201 int textlen = 0;
6202 float offset_x;
6203 LOGFONTW lf;
6204 OUTLINETEXTMETRICW otm;
6205 HFONT font = NULL, oldfont = NULL;
6206 const MAT2 identity = {{0, 1}, {0, 0}, {0, 0}, {0, 1}};
6207 void *raw_outline = NULL;
6208 int bufsize = 0;
6209 struct glyphinfo *glyphs = NULL;
6210 GLYPHMETRICS gm;
6211 struct triangulation_array triangulations = {0, 0, NULL};
6212 int i;
6213 struct vertex *vertex_ptr;
6214 face *face_ptr;
6215 float max_deviation_sq;
6216 const struct cos_table cos_table = {
6217 cosf(D3DXToRadian(0.5f)),
6218 cosf(D3DXToRadian(45.0f)),
6219 cosf(D3DXToRadian(90.0f)),
6221 int f1, f2;
6223 TRACE("(%p, %p, %s, %f, %f, %p, %p, %p)\n", device, hdc,
6224 debugstr_w(text), deviation, extrusion, mesh_ptr, adjacency, glyphmetrics);
6226 if (!device || !hdc || !text || !*text || deviation < 0.0f || extrusion < 0.0f || !mesh_ptr)
6227 return D3DERR_INVALIDCALL;
6229 if (adjacency)
6231 FIXME("Case of adjacency != NULL not implemented.\n");
6232 return E_NOTIMPL;
6235 if (!GetObjectW(GetCurrentObject(hdc, OBJ_FONT), sizeof(lf), &lf) ||
6236 !GetOutlineTextMetricsW(hdc, sizeof(otm), &otm))
6238 return D3DERR_INVALIDCALL;
6241 if (deviation == 0.0f)
6242 deviation = 1.0f / otm.otmEMSquare;
6243 max_deviation_sq = deviation * deviation;
6245 lf.lfHeight = otm.otmEMSquare;
6246 lf.lfWidth = 0;
6247 font = CreateFontIndirectW(&lf);
6248 if (!font) {
6249 hr = E_OUTOFMEMORY;
6250 goto error;
6252 oldfont = SelectObject(hdc, font);
6254 textlen = lstrlenW(text);
6255 for (i = 0; i < textlen; i++)
6257 int datasize = GetGlyphOutlineW(hdc, text[i], GGO_NATIVE, &gm, 0, NULL, &identity);
6258 if (datasize < 0)
6259 return D3DERR_INVALIDCALL;
6260 if (bufsize < datasize)
6261 bufsize = datasize;
6263 if (!bufsize) { /* e.g. text == " " */
6264 hr = D3DERR_INVALIDCALL;
6265 goto error;
6268 glyphs = calloc(textlen, sizeof(*glyphs));
6269 raw_outline = malloc(bufsize);
6270 if (!glyphs || !raw_outline) {
6271 hr = E_OUTOFMEMORY;
6272 goto error;
6275 offset_x = 0.0f;
6276 for (i = 0; i < textlen; i++)
6278 /* get outline points from data returned from GetGlyphOutline */
6279 int datasize;
6281 glyphs[i].offset_x = offset_x;
6283 datasize = GetGlyphOutlineW(hdc, text[i], GGO_NATIVE, &gm, bufsize, raw_outline, &identity);
6284 hr = create_outline(&glyphs[i], raw_outline, datasize,
6285 max_deviation_sq, otm.otmEMSquare, &cos_table);
6286 if (hr != S_OK) goto error;
6288 triangulations.glyph = &glyphs[i];
6289 hr = triangulate(&triangulations);
6290 if (hr != S_OK) goto error;
6291 if (triangulations.count) {
6292 ERR("%d incomplete triangulations of glyph (%u).\n", triangulations.count, text[i]);
6293 triangulations.count = 0;
6296 if (glyphmetrics)
6298 glyphmetrics[i].gmfBlackBoxX = gm.gmBlackBoxX / (float)otm.otmEMSquare;
6299 glyphmetrics[i].gmfBlackBoxY = gm.gmBlackBoxY / (float)otm.otmEMSquare;
6300 glyphmetrics[i].gmfptGlyphOrigin.x = gm.gmptGlyphOrigin.x / (float)otm.otmEMSquare;
6301 glyphmetrics[i].gmfptGlyphOrigin.y = gm.gmptGlyphOrigin.y / (float)otm.otmEMSquare;
6302 glyphmetrics[i].gmfCellIncX = gm.gmCellIncX / (float)otm.otmEMSquare;
6303 glyphmetrics[i].gmfCellIncY = gm.gmCellIncY / (float)otm.otmEMSquare;
6305 offset_x += gm.gmCellIncX / (float)otm.otmEMSquare;
6308 /* corner points need an extra vertex for the different side faces normals */
6309 nb_corners = 0;
6310 nb_outline_points = 0;
6311 nb_front_faces = 0;
6312 for (i = 0; i < textlen; i++)
6314 int j;
6315 nb_outline_points += glyphs[i].ordered_vertices.count;
6316 nb_front_faces += glyphs[i].faces.count;
6317 for (j = 0; j < glyphs[i].outlines.count; j++)
6319 int k;
6320 struct outline *outline = &glyphs[i].outlines.items[j];
6321 nb_corners++; /* first outline point always repeated as a corner */
6322 for (k = 1; k < outline->count; k++)
6323 if (outline->items[k].corner)
6324 nb_corners++;
6328 nb_vertices = (nb_outline_points + nb_corners) * 2 + nb_outline_points * 2;
6329 nb_faces = nb_outline_points * 2 + nb_front_faces * 2;
6332 hr = D3DXCreateMeshFVF(nb_faces, nb_vertices, D3DXMESH_MANAGED,
6333 D3DFVF_XYZ | D3DFVF_NORMAL, device, &mesh);
6334 if (FAILED(hr))
6335 goto error;
6337 if (FAILED(hr = mesh->lpVtbl->LockVertexBuffer(mesh, 0, (void **)&vertices)))
6338 goto error;
6340 if (FAILED(hr = mesh->lpVtbl->LockIndexBuffer(mesh, 0, (void **)&faces)))
6341 goto error;
6343 /* convert 2D vertices and faces into 3D mesh */
6344 vertex_ptr = vertices;
6345 face_ptr = faces;
6346 if (extrusion == 0.0f) {
6347 f1 = 1;
6348 f2 = 2;
6349 } else {
6350 f1 = 2;
6351 f2 = 1;
6353 for (i = 0; i < textlen; i++)
6355 int j;
6356 int count;
6357 struct vertex *back_vertices;
6358 face *back_faces;
6360 /* side vertices and faces */
6361 for (j = 0; j < glyphs[i].outlines.count; j++)
6363 struct vertex *outline_vertices = vertex_ptr;
6364 struct outline *outline = &glyphs[i].outlines.items[j];
6365 int k;
6366 struct point2d *prevpt = &outline->items[outline->count - 1];
6367 struct point2d *pt = &outline->items[0];
6369 for (k = 1; k <= outline->count; k++)
6371 struct vertex vtx;
6372 struct point2d *nextpt = &outline->items[k % outline->count];
6373 WORD vtx_idx = vertex_ptr - vertices;
6374 D3DXVECTOR2 vec;
6376 if (pt->corner == POINTTYPE_CURVE_START)
6377 D3DXVec2Subtract(&vec, &pt->pos, &prevpt->pos);
6378 else if (pt->corner)
6379 D3DXVec2Subtract(&vec, &nextpt->pos, &pt->pos);
6380 else
6381 D3DXVec2Subtract(&vec, &nextpt->pos, &prevpt->pos);
6382 D3DXVec2Normalize(&vec, &vec);
6383 vtx.normal.x = -vec.y;
6384 vtx.normal.y = vec.x;
6385 vtx.normal.z = 0;
6387 vtx.position.x = pt->pos.x + glyphs[i].offset_x;
6388 vtx.position.y = pt->pos.y;
6389 vtx.position.z = 0;
6390 *vertex_ptr++ = vtx;
6392 vtx.position.z = -extrusion;
6393 *vertex_ptr++ = vtx;
6395 vtx.position.x = nextpt->pos.x + glyphs[i].offset_x;
6396 vtx.position.y = nextpt->pos.y;
6397 if (pt->corner && nextpt->corner && nextpt->corner != POINTTYPE_CURVE_END) {
6398 vtx.position.z = -extrusion;
6399 *vertex_ptr++ = vtx;
6400 vtx.position.z = 0;
6401 *vertex_ptr++ = vtx;
6403 (*face_ptr)[0] = vtx_idx;
6404 (*face_ptr)[1] = vtx_idx + 2;
6405 (*face_ptr)[2] = vtx_idx + 1;
6406 face_ptr++;
6408 (*face_ptr)[0] = vtx_idx;
6409 (*face_ptr)[1] = vtx_idx + 3;
6410 (*face_ptr)[2] = vtx_idx + 2;
6411 face_ptr++;
6412 } else {
6413 if (nextpt->corner) {
6414 if (nextpt->corner == POINTTYPE_CURVE_END) {
6415 D3DXVECTOR2 *nextpt2 = &outline->items[(k + 1) % outline->count].pos;
6416 D3DXVec2Subtract(&vec, nextpt2, &nextpt->pos);
6417 } else {
6418 D3DXVec2Subtract(&vec, &nextpt->pos, &pt->pos);
6420 D3DXVec2Normalize(&vec, &vec);
6421 vtx.normal.x = -vec.y;
6422 vtx.normal.y = vec.x;
6424 vtx.position.z = 0;
6425 *vertex_ptr++ = vtx;
6426 vtx.position.z = -extrusion;
6427 *vertex_ptr++ = vtx;
6430 (*face_ptr)[0] = vtx_idx;
6431 (*face_ptr)[1] = vtx_idx + 3;
6432 (*face_ptr)[2] = vtx_idx + 1;
6433 face_ptr++;
6435 (*face_ptr)[0] = vtx_idx;
6436 (*face_ptr)[1] = vtx_idx + 2;
6437 (*face_ptr)[2] = vtx_idx + 3;
6438 face_ptr++;
6441 prevpt = pt;
6442 pt = nextpt;
6444 if (!pt->corner) {
6445 *vertex_ptr++ = *outline_vertices++;
6446 *vertex_ptr++ = *outline_vertices++;
6450 /* back vertices and faces */
6451 back_faces = face_ptr;
6452 back_vertices = vertex_ptr;
6453 for (j = 0; j < glyphs[i].ordered_vertices.count; j++)
6455 D3DXVECTOR2 *pt = get_ordered_vertex(&glyphs[i], j);
6456 vertex_ptr->position.x = pt->x + glyphs[i].offset_x;
6457 vertex_ptr->position.y = pt->y;
6458 vertex_ptr->position.z = 0;
6459 vertex_ptr->normal.x = 0;
6460 vertex_ptr->normal.y = 0;
6461 vertex_ptr->normal.z = 1;
6462 vertex_ptr++;
6464 count = back_vertices - vertices;
6465 for (j = 0; j < glyphs[i].faces.count; j++)
6467 face *f = &glyphs[i].faces.items[j];
6468 (*face_ptr)[0] = (*f)[0] + count;
6469 (*face_ptr)[1] = (*f)[1] + count;
6470 (*face_ptr)[2] = (*f)[2] + count;
6471 face_ptr++;
6474 /* front vertices and faces */
6475 j = count = vertex_ptr - back_vertices;
6476 while (j--)
6478 vertex_ptr->position.x = back_vertices->position.x;
6479 vertex_ptr->position.y = back_vertices->position.y;
6480 vertex_ptr->position.z = -extrusion;
6481 vertex_ptr->normal.x = 0;
6482 vertex_ptr->normal.y = 0;
6483 vertex_ptr->normal.z = extrusion == 0.0f ? 1.0f : -1.0f;
6484 vertex_ptr++;
6485 back_vertices++;
6487 j = face_ptr - back_faces;
6488 while (j--)
6490 (*face_ptr)[0] = (*back_faces)[0] + count;
6491 (*face_ptr)[1] = (*back_faces)[f1] + count;
6492 (*face_ptr)[2] = (*back_faces)[f2] + count;
6493 face_ptr++;
6494 back_faces++;
6498 *mesh_ptr = mesh;
6499 hr = D3D_OK;
6500 error:
6501 if (mesh) {
6502 if (faces) mesh->lpVtbl->UnlockIndexBuffer(mesh);
6503 if (vertices) mesh->lpVtbl->UnlockVertexBuffer(mesh);
6504 if (hr != D3D_OK) mesh->lpVtbl->Release(mesh);
6506 if (glyphs) {
6507 for (i = 0; i < textlen; i++)
6509 int j;
6510 for (j = 0; j < glyphs[i].outlines.count; j++)
6511 free(glyphs[i].outlines.items[j].items);
6512 free(glyphs[i].outlines.items);
6513 free(glyphs[i].faces.items);
6514 free(glyphs[i].ordered_vertices.items);
6516 free(glyphs);
6518 if (triangulations.items) {
6519 int i;
6520 for (i = 0; i < triangulations.count; i++)
6521 free(triangulations.items[i].vertex_stack.items);
6522 free(triangulations.items);
6524 free(raw_outline);
6525 if (oldfont) SelectObject(hdc, oldfont);
6526 if (font) DeleteObject(font);
6528 return hr;
6531 HRESULT WINAPI D3DXValidMesh(ID3DXMesh *mesh, const DWORD *adjacency, ID3DXBuffer **errors_and_warnings)
6533 FIXME("mesh %p, adjacency %p, errors_and_warnings %p stub.\n", mesh, adjacency, errors_and_warnings);
6535 return E_NOTIMPL;
6538 static BOOL weld_float1(void *to, void *from, FLOAT epsilon)
6540 FLOAT *v1 = to;
6541 FLOAT *v2 = from;
6543 if (fabsf(*v1 - *v2) <= epsilon)
6545 *v1 = *v2;
6547 return TRUE;
6550 return FALSE;
6553 static BOOL weld_float2(void *to, void *from, FLOAT epsilon)
6555 D3DXVECTOR2 *v1 = to;
6556 D3DXVECTOR2 *v2 = from;
6557 FLOAT diff_x = fabsf(v1->x - v2->x);
6558 FLOAT diff_y = fabsf(v1->y - v2->y);
6559 FLOAT max_abs_diff = max(diff_x, diff_y);
6561 if (max_abs_diff <= epsilon)
6563 memcpy(to, from, sizeof(D3DXVECTOR2));
6565 return TRUE;
6568 return FALSE;
6571 static BOOL weld_float3(void *to, void *from, FLOAT epsilon)
6573 D3DXVECTOR3 *v1 = to;
6574 D3DXVECTOR3 *v2 = from;
6575 FLOAT diff_x = fabsf(v1->x - v2->x);
6576 FLOAT diff_y = fabsf(v1->y - v2->y);
6577 FLOAT diff_z = fabsf(v1->z - v2->z);
6578 FLOAT max_abs_diff = max(diff_x, diff_y);
6579 max_abs_diff = max(diff_z, max_abs_diff);
6581 if (max_abs_diff <= epsilon)
6583 memcpy(to, from, sizeof(D3DXVECTOR3));
6585 return TRUE;
6588 return FALSE;
6591 static BOOL weld_float4(void *to, void *from, FLOAT epsilon)
6593 D3DXVECTOR4 *v1 = to;
6594 D3DXVECTOR4 *v2 = from;
6595 FLOAT diff_x = fabsf(v1->x - v2->x);
6596 FLOAT diff_y = fabsf(v1->y - v2->y);
6597 FLOAT diff_z = fabsf(v1->z - v2->z);
6598 FLOAT diff_w = fabsf(v1->w - v2->w);
6599 FLOAT max_abs_diff = max(diff_x, diff_y);
6600 max_abs_diff = max(diff_z, max_abs_diff);
6601 max_abs_diff = max(diff_w, max_abs_diff);
6603 if (max_abs_diff <= epsilon)
6605 memcpy(to, from, sizeof(D3DXVECTOR4));
6607 return TRUE;
6610 return FALSE;
6613 static BOOL weld_ubyte4(void *to, void *from, FLOAT epsilon)
6615 BYTE *b1 = to;
6616 BYTE *b2 = from;
6617 BYTE truncated_epsilon = (BYTE)epsilon;
6618 BYTE diff_x = b1[0] > b2[0] ? b1[0] - b2[0] : b2[0] - b1[0];
6619 BYTE diff_y = b1[1] > b2[1] ? b1[1] - b2[1] : b2[1] - b1[1];
6620 BYTE diff_z = b1[2] > b2[2] ? b1[2] - b2[2] : b2[2] - b1[2];
6621 BYTE diff_w = b1[3] > b2[3] ? b1[3] - b2[3] : b2[3] - b1[3];
6622 BYTE max_diff = max(diff_x, diff_y);
6623 max_diff = max(diff_z, max_diff);
6624 max_diff = max(diff_w, max_diff);
6626 if (max_diff <= truncated_epsilon)
6628 memcpy(to, from, 4 * sizeof(BYTE));
6630 return TRUE;
6633 return FALSE;
6636 static BOOL weld_ubyte4n(void *to, void *from, FLOAT epsilon)
6638 return weld_ubyte4(to, from, epsilon * UCHAR_MAX);
6641 static BOOL weld_d3dcolor(void *to, void *from, FLOAT epsilon)
6643 return weld_ubyte4n(to, from, epsilon);
6646 static BOOL weld_short2(void *to, void *from, FLOAT epsilon)
6648 SHORT *s1 = to;
6649 SHORT *s2 = from;
6650 SHORT truncated_epsilon = (SHORT)epsilon;
6651 SHORT diff_x = abs(s1[0] - s2[0]);
6652 SHORT diff_y = abs(s1[1] - s2[1]);
6653 SHORT max_abs_diff = max(diff_x, diff_y);
6655 if (max_abs_diff <= truncated_epsilon)
6657 memcpy(to, from, 2 * sizeof(SHORT));
6659 return TRUE;
6662 return FALSE;
6665 static BOOL weld_short2n(void *to, void *from, FLOAT epsilon)
6667 return weld_short2(to, from, epsilon * SHRT_MAX);
6670 static BOOL weld_short4(void *to, void *from, FLOAT epsilon)
6672 SHORT *s1 = to;
6673 SHORT *s2 = from;
6674 SHORT truncated_epsilon = (SHORT)epsilon;
6675 SHORT diff_x = abs(s1[0] - s2[0]);
6676 SHORT diff_y = abs(s1[1] - s2[1]);
6677 SHORT diff_z = abs(s1[2] - s2[2]);
6678 SHORT diff_w = abs(s1[3] - s2[3]);
6679 SHORT max_abs_diff = max(diff_x, diff_y);
6680 max_abs_diff = max(diff_z, max_abs_diff);
6681 max_abs_diff = max(diff_w, max_abs_diff);
6683 if (max_abs_diff <= truncated_epsilon)
6685 memcpy(to, from, 4 * sizeof(SHORT));
6687 return TRUE;
6690 return FALSE;
6693 static BOOL weld_short4n(void *to, void *from, FLOAT epsilon)
6695 return weld_short4(to, from, epsilon * SHRT_MAX);
6698 static BOOL weld_ushort2n(void *to, void *from, FLOAT epsilon)
6700 USHORT *s1 = to;
6701 USHORT *s2 = from;
6702 USHORT scaled_epsilon = (USHORT)(epsilon * USHRT_MAX);
6703 USHORT diff_x = s1[0] > s2[0] ? s1[0] - s2[0] : s2[0] - s1[0];
6704 USHORT diff_y = s1[1] > s2[1] ? s1[1] - s2[1] : s2[1] - s1[1];
6705 USHORT max_diff = max(diff_x, diff_y);
6707 if (max_diff <= scaled_epsilon)
6709 memcpy(to, from, 2 * sizeof(USHORT));
6711 return TRUE;
6714 return FALSE;
6717 static BOOL weld_ushort4n(void *to, void *from, FLOAT epsilon)
6719 USHORT *s1 = to;
6720 USHORT *s2 = from;
6721 USHORT scaled_epsilon = (USHORT)(epsilon * USHRT_MAX);
6722 USHORT diff_x = s1[0] > s2[0] ? s1[0] - s2[0] : s2[0] - s1[0];
6723 USHORT diff_y = s1[1] > s2[1] ? s1[1] - s2[1] : s2[1] - s1[1];
6724 USHORT diff_z = s1[2] > s2[2] ? s1[2] - s2[2] : s2[2] - s1[2];
6725 USHORT diff_w = s1[3] > s2[3] ? s1[3] - s2[3] : s2[3] - s1[3];
6726 USHORT max_diff = max(diff_x, diff_y);
6727 max_diff = max(diff_z, max_diff);
6728 max_diff = max(diff_w, max_diff);
6730 if (max_diff <= scaled_epsilon)
6732 memcpy(to, from, 4 * sizeof(USHORT));
6734 return TRUE;
6737 return FALSE;
6740 struct udec3
6742 UINT x;
6743 UINT y;
6744 UINT z;
6745 UINT w;
6748 static struct udec3 dword_to_udec3(DWORD d)
6750 struct udec3 v;
6752 v.x = d & 0x3ff;
6753 v.y = (d & 0xffc00) >> 10;
6754 v.z = (d & 0x3ff00000) >> 20;
6755 v.w = (d & 0xc0000000) >> 30;
6757 return v;
6760 static BOOL weld_udec3(void *to, void *from, FLOAT epsilon)
6762 DWORD *d1 = to;
6763 DWORD *d2 = from;
6764 struct udec3 v1 = dword_to_udec3(*d1);
6765 struct udec3 v2 = dword_to_udec3(*d2);
6766 UINT truncated_epsilon = (UINT)epsilon;
6767 UINT diff_x = v1.x > v2.x ? v1.x - v2.x : v2.x - v1.x;
6768 UINT diff_y = v1.y > v2.y ? v1.y - v2.y : v2.y - v1.y;
6769 UINT diff_z = v1.z > v2.z ? v1.z - v2.z : v2.z - v1.z;
6770 UINT diff_w = v1.w > v2.w ? v1.w - v2.w : v2.w - v1.w;
6771 UINT max_diff = max(diff_x, diff_y);
6772 max_diff = max(diff_z, max_diff);
6773 max_diff = max(diff_w, max_diff);
6775 if (max_diff <= truncated_epsilon)
6777 memcpy(to, from, sizeof(DWORD));
6779 return TRUE;
6782 return FALSE;
6785 struct dec3n
6787 INT x;
6788 INT y;
6789 INT z;
6790 INT w;
6793 static struct dec3n dword_to_dec3n(DWORD d)
6795 struct dec3n v;
6797 v.x = d & 0x3ff;
6798 v.y = (d & 0xffc00) >> 10;
6799 v.z = (d & 0x3ff00000) >> 20;
6800 v.w = (d & 0xc0000000) >> 30;
6802 return v;
6805 static BOOL weld_dec3n(void *to, void *from, FLOAT epsilon)
6807 const UINT MAX_DEC3N = 511;
6808 DWORD *d1 = to;
6809 DWORD *d2 = from;
6810 struct dec3n v1 = dword_to_dec3n(*d1);
6811 struct dec3n v2 = dword_to_dec3n(*d2);
6812 INT scaled_epsilon = (INT)(epsilon * MAX_DEC3N);
6813 INT diff_x = abs(v1.x - v2.x);
6814 INT diff_y = abs(v1.y - v2.y);
6815 INT diff_z = abs(v1.z - v2.z);
6816 INT diff_w = abs(v1.w - v2.w);
6817 INT max_abs_diff = max(diff_x, diff_y);
6818 max_abs_diff = max(diff_z, max_abs_diff);
6819 max_abs_diff = max(diff_w, max_abs_diff);
6821 if (max_abs_diff <= scaled_epsilon)
6823 memcpy(to, from, sizeof(DWORD));
6825 return TRUE;
6828 return FALSE;
6831 static BOOL weld_float16_2(void *to, void *from, FLOAT epsilon)
6833 D3DXFLOAT16 *v1_float16 = to;
6834 D3DXFLOAT16 *v2_float16 = from;
6835 FLOAT diff_x;
6836 FLOAT diff_y;
6837 FLOAT max_abs_diff;
6838 #define NUM_ELEM 2
6839 FLOAT v1[NUM_ELEM];
6840 FLOAT v2[NUM_ELEM];
6842 D3DXFloat16To32Array(v1, v1_float16, NUM_ELEM);
6843 D3DXFloat16To32Array(v2, v2_float16, NUM_ELEM);
6845 diff_x = fabsf(v1[0] - v2[0]);
6846 diff_y = fabsf(v1[1] - v2[1]);
6847 max_abs_diff = max(diff_x, diff_y);
6849 if (max_abs_diff <= epsilon)
6851 memcpy(to, from, NUM_ELEM * sizeof(D3DXFLOAT16));
6853 return TRUE;
6856 return FALSE;
6857 #undef NUM_ELEM
6860 static BOOL weld_float16_4(void *to, void *from, FLOAT epsilon)
6862 D3DXFLOAT16 *v1_float16 = to;
6863 D3DXFLOAT16 *v2_float16 = from;
6864 FLOAT diff_x;
6865 FLOAT diff_y;
6866 FLOAT diff_z;
6867 FLOAT diff_w;
6868 FLOAT max_abs_diff;
6869 #define NUM_ELEM 4
6870 FLOAT v1[NUM_ELEM];
6871 FLOAT v2[NUM_ELEM];
6873 D3DXFloat16To32Array(v1, v1_float16, NUM_ELEM);
6874 D3DXFloat16To32Array(v2, v2_float16, NUM_ELEM);
6876 diff_x = fabsf(v1[0] - v2[0]);
6877 diff_y = fabsf(v1[1] - v2[1]);
6878 diff_z = fabsf(v1[2] - v2[2]);
6879 diff_w = fabsf(v1[3] - v2[3]);
6880 max_abs_diff = max(diff_x, diff_y);
6881 max_abs_diff = max(diff_z, max_abs_diff);
6882 max_abs_diff = max(diff_w, max_abs_diff);
6884 if (max_abs_diff <= epsilon)
6886 memcpy(to, from, NUM_ELEM * sizeof(D3DXFLOAT16));
6888 return TRUE;
6891 return FALSE;
6892 #undef NUM_ELEM
6895 /* Sets the vertex components to the same value if they are within epsilon. */
6896 static BOOL weld_component(void *to, void *from, D3DDECLTYPE type, FLOAT epsilon)
6898 /* Quiet FIXMEs as this is in a loop with potentially thousand of iterations. */
6899 BOOL fixme_once_unused = FALSE;
6900 BOOL fixme_once_unknown = FALSE;
6902 switch (type)
6904 case D3DDECLTYPE_FLOAT1:
6905 return weld_float1(to, from, epsilon);
6907 case D3DDECLTYPE_FLOAT2:
6908 return weld_float2(to, from, epsilon);
6910 case D3DDECLTYPE_FLOAT3:
6911 return weld_float3(to, from, epsilon);
6913 case D3DDECLTYPE_FLOAT4:
6914 return weld_float4(to, from, epsilon);
6916 case D3DDECLTYPE_D3DCOLOR:
6917 return weld_d3dcolor(to, from, epsilon);
6919 case D3DDECLTYPE_UBYTE4:
6920 return weld_ubyte4(to, from, epsilon);
6922 case D3DDECLTYPE_SHORT2:
6923 return weld_short2(to, from, epsilon);
6925 case D3DDECLTYPE_SHORT4:
6926 return weld_short4(to, from, epsilon);
6928 case D3DDECLTYPE_UBYTE4N:
6929 return weld_ubyte4n(to, from, epsilon);
6931 case D3DDECLTYPE_SHORT2N:
6932 return weld_short2n(to, from, epsilon);
6934 case D3DDECLTYPE_SHORT4N:
6935 return weld_short4n(to, from, epsilon);
6937 case D3DDECLTYPE_USHORT2N:
6938 return weld_ushort2n(to, from, epsilon);
6940 case D3DDECLTYPE_USHORT4N:
6941 return weld_ushort4n(to, from, epsilon);
6943 case D3DDECLTYPE_UDEC3:
6944 return weld_udec3(to, from, epsilon);
6946 case D3DDECLTYPE_DEC3N:
6947 return weld_dec3n(to, from, epsilon);
6949 case D3DDECLTYPE_FLOAT16_2:
6950 return weld_float16_2(to, from, epsilon);
6952 case D3DDECLTYPE_FLOAT16_4:
6953 return weld_float16_4(to, from, epsilon);
6955 case D3DDECLTYPE_UNUSED:
6956 if (!fixme_once_unused++)
6957 FIXME("D3DDECLTYPE_UNUSED welding not implemented.\n");
6958 break;
6960 default:
6961 if (!fixme_once_unknown++)
6962 FIXME("Welding of unknown declaration type %d is not implemented.\n", type);
6963 break;
6966 return FALSE;
6969 static FLOAT get_component_epsilon(const D3DVERTEXELEMENT9 *decl_ptr, const D3DXWELDEPSILONS *epsilons)
6971 FLOAT epsilon = 0.0f;
6972 /* Quiet FIXMEs as this is in a loop with potentially thousand of iterations. */
6973 static BOOL fixme_once_blendindices = FALSE;
6974 static BOOL fixme_once_positiont = FALSE;
6975 static BOOL fixme_once_fog = FALSE;
6976 static BOOL fixme_once_depth = FALSE;
6977 static BOOL fixme_once_sample = FALSE;
6978 static BOOL fixme_once_unknown = FALSE;
6980 switch (decl_ptr->Usage)
6982 case D3DDECLUSAGE_POSITION:
6983 epsilon = epsilons->Position;
6984 break;
6985 case D3DDECLUSAGE_BLENDWEIGHT:
6986 epsilon = epsilons->BlendWeights;
6987 break;
6988 case D3DDECLUSAGE_NORMAL:
6989 epsilon = epsilons->Normals;
6990 break;
6991 case D3DDECLUSAGE_PSIZE:
6992 epsilon = epsilons->PSize;
6993 break;
6994 case D3DDECLUSAGE_TEXCOORD:
6996 BYTE usage_index = decl_ptr->UsageIndex;
6997 if (usage_index > 7)
6998 usage_index = 7;
6999 epsilon = epsilons->Texcoords[usage_index];
7000 break;
7002 case D3DDECLUSAGE_TANGENT:
7003 epsilon = epsilons->Tangent;
7004 break;
7005 case D3DDECLUSAGE_BINORMAL:
7006 epsilon = epsilons->Binormal;
7007 break;
7008 case D3DDECLUSAGE_TESSFACTOR:
7009 epsilon = epsilons->TessFactor;
7010 break;
7011 case D3DDECLUSAGE_COLOR:
7012 if (decl_ptr->UsageIndex == 0)
7013 epsilon = epsilons->Diffuse;
7014 else if (decl_ptr->UsageIndex == 1)
7015 epsilon = epsilons->Specular;
7016 else
7017 epsilon = 1e-6f;
7018 break;
7019 case D3DDECLUSAGE_BLENDINDICES:
7020 if (!fixme_once_blendindices++)
7021 FIXME("D3DDECLUSAGE_BLENDINDICES welding not implemented.\n");
7022 break;
7023 case D3DDECLUSAGE_POSITIONT:
7024 if (!fixme_once_positiont++)
7025 FIXME("D3DDECLUSAGE_POSITIONT welding not implemented.\n");
7026 break;
7027 case D3DDECLUSAGE_FOG:
7028 if (!fixme_once_fog++)
7029 FIXME("D3DDECLUSAGE_FOG welding not implemented.\n");
7030 break;
7031 case D3DDECLUSAGE_DEPTH:
7032 if (!fixme_once_depth++)
7033 FIXME("D3DDECLUSAGE_DEPTH welding not implemented.\n");
7034 break;
7035 case D3DDECLUSAGE_SAMPLE:
7036 if (!fixme_once_sample++)
7037 FIXME("D3DDECLUSAGE_SAMPLE welding not implemented.\n");
7038 break;
7039 default:
7040 if (!fixme_once_unknown++)
7041 FIXME("Unknown usage %x\n", decl_ptr->Usage);
7042 break;
7045 return epsilon;
7048 /* Helper function for reading a 32-bit index buffer. */
7049 static inline DWORD read_ib(void *index_buffer, BOOL indices_are_32bit,
7050 DWORD index)
7052 if (indices_are_32bit)
7054 DWORD *indices = index_buffer;
7055 return indices[index];
7057 else
7059 WORD *indices = index_buffer;
7060 return indices[index];
7064 /* Helper function for writing to a 32-bit index buffer. */
7065 static inline void write_ib(void *index_buffer, BOOL indices_are_32bit,
7066 DWORD index, DWORD value)
7068 if (indices_are_32bit)
7070 DWORD *indices = index_buffer;
7071 indices[index] = value;
7073 else
7075 WORD *indices = index_buffer;
7076 indices[index] = value;
7080 /*************************************************************************
7081 * D3DXWeldVertices (D3DX9_36.@)
7083 * Welds together similar vertices. The similarity between vert-
7084 * ices can be the position and other components such as
7085 * normal and color.
7087 * PARAMS
7088 * mesh [I] Mesh which vertices will be welded together.
7089 * flags [I] D3DXWELDEPSILONSFLAGS specifying how to weld.
7090 * epsilons [I] How similar a component needs to be for welding.
7091 * adjacency [I] Which faces are adjacent to other faces.
7092 * adjacency_out [O] Updated adjacency after welding.
7093 * face_remap_out [O] Which faces the old faces have been mapped to.
7094 * vertex_remap_out [O] Which vertices the old vertices have been mapped to.
7096 * RETURNS
7097 * Success: D3D_OK.
7098 * Failure: D3DERR_INVALIDCALL, E_OUTOFMEMORY.
7100 * BUGS
7101 * Attribute sorting not implemented.
7104 HRESULT WINAPI D3DXWeldVertices(ID3DXMesh *mesh, DWORD flags, const D3DXWELDEPSILONS *epsilons,
7105 const DWORD *adjacency, DWORD *adjacency_out, DWORD *face_remap_out, ID3DXBuffer **vertex_remap_out)
7107 DWORD *adjacency_generated = NULL;
7108 const DWORD *adjacency_ptr;
7109 DWORD *attributes = NULL;
7110 const FLOAT DEFAULT_EPSILON = 1.0e-6f;
7111 HRESULT hr;
7112 DWORD i;
7113 void *indices = NULL;
7114 BOOL indices_are_32bit = mesh->lpVtbl->GetOptions(mesh) & D3DXMESH_32BIT;
7115 DWORD optimize_flags;
7116 DWORD *point_reps = NULL;
7117 struct d3dx9_mesh *This = impl_from_ID3DXMesh(mesh);
7118 DWORD *vertex_face_map = NULL;
7119 BYTE *vertices = NULL;
7121 TRACE("mesh %p, flags %#lx, epsilons %p, adjacency %p, adjacency_out %p, face_remap_out %p, "
7122 "vertex_remap_out %p.\n",
7123 mesh, flags, epsilons, adjacency, adjacency_out, face_remap_out, vertex_remap_out);
7125 if (flags == 0)
7127 WARN("No flags are undefined. Using D3DXWELDEPSILONS_WELDPARTIALMATCHES instead.\n");
7128 flags = D3DXWELDEPSILONS_WELDPARTIALMATCHES;
7131 if (adjacency) /* Use supplied adjacency. */
7133 adjacency_ptr = adjacency;
7135 else /* Adjacency has to be generated. */
7137 adjacency_generated = malloc(3 * This->numfaces * sizeof(*adjacency_generated));
7138 if (!adjacency_generated)
7140 ERR("Couldn't allocate memory for adjacency_generated.\n");
7141 hr = E_OUTOFMEMORY;
7142 goto cleanup;
7144 hr = mesh->lpVtbl->GenerateAdjacency(mesh, DEFAULT_EPSILON, adjacency_generated);
7145 if (FAILED(hr))
7147 ERR("Couldn't generate adjacency.\n");
7148 goto cleanup;
7150 adjacency_ptr = adjacency_generated;
7153 /* Point representation says which vertices can be replaced. */
7154 point_reps = malloc(This->numvertices * sizeof(*point_reps));
7155 if (!point_reps)
7157 hr = E_OUTOFMEMORY;
7158 ERR("Couldn't allocate memory for point_reps.\n");
7159 goto cleanup;
7161 hr = mesh->lpVtbl->ConvertAdjacencyToPointReps(mesh, adjacency_ptr, point_reps);
7162 if (FAILED(hr))
7164 ERR("ConvertAdjacencyToPointReps failed.\n");
7165 goto cleanup;
7168 hr = mesh->lpVtbl->LockIndexBuffer(mesh, 0, &indices);
7169 if (FAILED(hr))
7171 ERR("Couldn't lock index buffer.\n");
7172 goto cleanup;
7175 hr = mesh->lpVtbl->LockAttributeBuffer(mesh, 0, &attributes);
7176 if (FAILED(hr))
7178 ERR("Couldn't lock attribute buffer.\n");
7179 goto cleanup;
7181 vertex_face_map = malloc(This->numvertices * sizeof(*vertex_face_map));
7182 if (!vertex_face_map)
7184 hr = E_OUTOFMEMORY;
7185 ERR("Couldn't allocate memory for vertex_face_map.\n");
7186 goto cleanup;
7188 /* Build vertex face map, so that a vertex's face can be looked up. */
7189 for (i = 0; i < This->numfaces; i++)
7191 DWORD j;
7192 for (j = 0; j < 3; j++)
7194 DWORD index = read_ib(indices, indices_are_32bit, 3*i + j);
7195 vertex_face_map[index] = i;
7199 if (flags & D3DXWELDEPSILONS_WELDPARTIALMATCHES)
7201 hr = mesh->lpVtbl->LockVertexBuffer(mesh, 0, (void**)&vertices);
7202 if (FAILED(hr))
7204 ERR("Couldn't lock vertex buffer.\n");
7205 goto cleanup;
7207 /* For each vertex that can be removed, compare its vertex components
7208 * with the vertex components from the vertex that can replace it. A
7209 * vertex is only fully replaced if all the components match and the
7210 * flag D3DXWELDEPSILONS_DONOTREMOVEVERTICES is not set, and they
7211 * belong to the same attribute group. Otherwise the vertex components
7212 * that are within epsilon are set to the same value.
7214 for (i = 0; i < 3 * This->numfaces; i++)
7216 D3DVERTEXELEMENT9 *decl_ptr;
7217 DWORD vertex_size = mesh->lpVtbl->GetNumBytesPerVertex(mesh);
7218 DWORD num_vertex_components;
7219 INT matches = 0;
7220 BOOL all_match;
7221 DWORD index = read_ib(indices, indices_are_32bit, i);
7223 for (decl_ptr = This->cached_declaration, num_vertex_components = 0; decl_ptr->Stream != 0xFF; decl_ptr++, num_vertex_components++)
7225 BYTE *to = &vertices[vertex_size*index + decl_ptr->Offset];
7226 BYTE *from = &vertices[vertex_size*point_reps[index] + decl_ptr->Offset];
7227 FLOAT epsilon = get_component_epsilon(decl_ptr, epsilons);
7229 /* Don't weld self */
7230 if (index == point_reps[index])
7232 matches++;
7233 continue;
7236 if (weld_component(to, from, decl_ptr->Type, epsilon))
7237 matches++;
7240 all_match = (num_vertex_components == matches);
7241 if (all_match && !(flags & D3DXWELDEPSILONS_DONOTREMOVEVERTICES))
7243 DWORD to_face = vertex_face_map[index];
7244 DWORD from_face = vertex_face_map[point_reps[index]];
7245 if(attributes[to_face] != attributes[from_face] && !(flags & D3DXWELDEPSILONS_DONOTSPLIT))
7246 continue;
7247 write_ib(indices, indices_are_32bit, i, point_reps[index]);
7250 mesh->lpVtbl->UnlockVertexBuffer(mesh);
7251 vertices = NULL;
7253 else if (flags & D3DXWELDEPSILONS_WELDALL)
7255 for (i = 0; i < 3 * This->numfaces; i++)
7257 DWORD index = read_ib(indices, indices_are_32bit, i);
7258 DWORD to_face = vertex_face_map[index];
7259 DWORD from_face = vertex_face_map[point_reps[index]];
7260 if(attributes[to_face] != attributes[from_face] && !(flags & D3DXWELDEPSILONS_DONOTSPLIT))
7261 continue;
7262 write_ib(indices, indices_are_32bit, i, point_reps[index]);
7265 mesh->lpVtbl->UnlockAttributeBuffer(mesh);
7266 attributes = NULL;
7267 mesh->lpVtbl->UnlockIndexBuffer(mesh);
7268 indices = NULL;
7270 /* Compact mesh using OptimizeInplace */
7271 optimize_flags = D3DXMESHOPT_COMPACT;
7272 hr = mesh->lpVtbl->OptimizeInplace(mesh, optimize_flags, adjacency_ptr, adjacency_out, face_remap_out, vertex_remap_out);
7273 if (FAILED(hr))
7275 ERR("Couldn't compact mesh.\n");
7276 goto cleanup;
7279 hr = D3D_OK;
7280 cleanup:
7281 free(adjacency_generated);
7282 free(point_reps);
7283 free(vertex_face_map);
7284 if (attributes) mesh->lpVtbl->UnlockAttributeBuffer(mesh);
7285 if (indices) mesh->lpVtbl->UnlockIndexBuffer(mesh);
7286 if (vertices) mesh->lpVtbl->UnlockVertexBuffer(mesh);
7288 return hr;
7291 /*************************************************************************
7292 * D3DXOptimizeFaces (D3DX9_36.@)
7294 * Re-orders the faces so the vertex cache is used optimally.
7296 * PARAMS
7297 * indices [I] Pointer to an index buffer belonging to a mesh.
7298 * num_faces [I] Number of faces in the mesh.
7299 * num_vertices [I] Number of vertices in the mesh.
7300 * indices_are_32bit [I] Specifies whether indices are 32- or 16-bit.
7301 * face_remap [I/O] The new order the faces should be drawn in.
7303 * RETURNS
7304 * Success: D3D_OK.
7305 * Failure: D3DERR_INVALIDCALL.
7307 * BUGS
7308 * The face re-ordering does not use the vertex cache optimally.
7311 HRESULT WINAPI D3DXOptimizeFaces(const void *indices, UINT num_faces,
7312 UINT num_vertices, BOOL indices_are_32bit, DWORD *face_remap)
7314 UINT i;
7315 UINT j = num_faces - 1;
7316 UINT limit_16_bit = 2 << 15; /* According to MSDN */
7317 HRESULT hr = D3D_OK;
7319 FIXME("indices %p, num_faces %u, num_vertices %u, indices_are_32bit %#x, face_remap %p semi-stub. "
7320 "Face order will not be optimal.\n",
7321 indices, num_faces, num_vertices, indices_are_32bit, face_remap);
7323 if (!indices_are_32bit && num_faces >= limit_16_bit)
7325 WARN("Number of faces must be less than %d when using 16-bit indices.\n",
7326 limit_16_bit);
7327 hr = D3DERR_INVALIDCALL;
7328 goto error;
7331 if (!face_remap)
7333 WARN("Face remap pointer is NULL.\n");
7334 hr = D3DERR_INVALIDCALL;
7335 goto error;
7338 /* The faces are drawn in reverse order for simple meshes. This ordering
7339 * is not optimal for complicated meshes, but will not break anything
7340 * either. The ordering should be changed to take advantage of the vertex
7341 * cache on the graphics card.
7343 * TODO Re-order to take advantage of vertex cache.
7345 for (i = 0; i < num_faces; i++)
7347 face_remap[i] = j--;
7350 return D3D_OK;
7352 error:
7353 return hr;
7356 static D3DXVECTOR3 *vertex_element_vec3(BYTE *vertices, const D3DVERTEXELEMENT9 *declaration,
7357 DWORD vertex_stride, DWORD index)
7359 return (D3DXVECTOR3 *)(vertices + declaration->Offset + index * vertex_stride);
7362 static D3DXVECTOR3 read_vec3(BYTE *vertices, const D3DVERTEXELEMENT9 *declaration,
7363 DWORD vertex_stride, DWORD index)
7365 D3DXVECTOR3 vec3 = {0};
7366 const D3DXVECTOR3 *src = vertex_element_vec3(vertices, declaration, vertex_stride, index);
7368 switch (declaration->Type)
7370 case D3DDECLTYPE_FLOAT1:
7371 vec3.x = src->x;
7372 break;
7373 case D3DDECLTYPE_FLOAT2:
7374 vec3.x = src->x;
7375 vec3.y = src->y;
7376 break;
7377 case D3DDECLTYPE_FLOAT3:
7378 case D3DDECLTYPE_FLOAT4:
7379 vec3 = *src;
7380 break;
7381 default:
7382 ERR("Cannot read vec3\n");
7383 break;
7386 return vec3;
7389 /*************************************************************************
7390 * D3DXComputeTangentFrameEx (D3DX9_36.@)
7392 HRESULT WINAPI D3DXComputeTangentFrameEx(ID3DXMesh *mesh, DWORD texture_in_semantic, DWORD texture_in_index,
7393 DWORD u_partial_out_semantic, DWORD u_partial_out_index, DWORD v_partial_out_semantic,
7394 DWORD v_partial_out_index, DWORD normal_out_semantic, DWORD normal_out_index, DWORD options,
7395 const DWORD *adjacency, float partial_edge_threshold, float singular_point_threshold,
7396 float normal_edge_threshold, ID3DXMesh **mesh_out, ID3DXBuffer **vertex_mapping)
7398 HRESULT hr;
7399 void *indices = NULL;
7400 BYTE *vertices = NULL;
7401 DWORD *point_reps = NULL;
7402 size_t normal_size;
7403 BOOL indices_are_32bit;
7404 DWORD i, j, num_faces, num_vertices, vertex_stride;
7405 D3DVERTEXELEMENT9 declaration[MAX_FVF_DECL_SIZE] = {D3DDECL_END()};
7406 D3DVERTEXELEMENT9 *position_declaration = NULL, *normal_declaration = NULL;
7407 DWORD weighting_method = options & (D3DXTANGENT_WEIGHT_EQUAL | D3DXTANGENT_WEIGHT_BY_AREA);
7409 TRACE("mesh %p, texture_in_semantic %lu, texture_in_index %lu, u_partial_out_semantic %lu, "
7410 "u_partial_out_index %lu, v_partial_out_semantic %lu, v_partial_out_index %lu, "
7411 "normal_out_semantic %lu, normal_out_index %lu, options %#lx, adjacency %p, "
7412 "partial_edge_threshold %.8e, singular_point_threshold %.8e, normal_edge_threshold %.8e, "
7413 "mesh_out %p, vertex_mapping %p.\n",
7414 mesh, texture_in_semantic, texture_in_index, u_partial_out_semantic, u_partial_out_index,
7415 v_partial_out_semantic, v_partial_out_index, normal_out_semantic, normal_out_index,
7416 options, adjacency, partial_edge_threshold, singular_point_threshold,
7417 normal_edge_threshold, mesh_out, vertex_mapping);
7419 if (!mesh)
7421 WARN("mesh is NULL\n");
7422 return D3DERR_INVALIDCALL;
7425 if (weighting_method == (D3DXTANGENT_WEIGHT_EQUAL | D3DXTANGENT_WEIGHT_BY_AREA))
7427 WARN("D3DXTANGENT_WEIGHT_BY_AREA and D3DXTANGENT_WEIGHT_EQUAL are mutally exclusive\n");
7428 return D3DERR_INVALIDCALL;
7431 if (u_partial_out_semantic != D3DX_DEFAULT)
7433 FIXME("tangent vectors computation is not supported\n");
7434 return E_NOTIMPL;
7437 if (v_partial_out_semantic != D3DX_DEFAULT)
7439 FIXME("binormal vectors computation is not supported\n");
7440 return E_NOTIMPL;
7443 if (options & ~(D3DXTANGENT_GENERATE_IN_PLACE | D3DXTANGENT_CALCULATE_NORMALS
7444 | D3DXTANGENT_WEIGHT_EQUAL | D3DXTANGENT_WEIGHT_BY_AREA))
7446 FIXME("Unsupported options %#lx.\n", options);
7447 return E_NOTIMPL;
7450 if (!(options & D3DXTANGENT_CALCULATE_NORMALS))
7452 FIXME("only normals computation is supported\n");
7453 return E_NOTIMPL;
7456 if (!(options & D3DXTANGENT_GENERATE_IN_PLACE) || mesh_out || vertex_mapping)
7458 FIXME("only D3DXTANGENT_GENERATE_IN_PLACE is supported\n");
7459 return E_NOTIMPL;
7462 if (FAILED(hr = mesh->lpVtbl->GetDeclaration(mesh, declaration)))
7463 return hr;
7465 for (i = 0; declaration[i].Stream != 0xff; i++)
7467 if (declaration[i].Usage == D3DDECLUSAGE_POSITION && !declaration[i].UsageIndex)
7468 position_declaration = &declaration[i];
7469 if (declaration[i].Usage == normal_out_semantic && declaration[i].UsageIndex == normal_out_index)
7470 normal_declaration = &declaration[i];
7473 if (!position_declaration || !normal_declaration)
7474 return D3DERR_INVALIDCALL;
7476 if (normal_declaration->Type == D3DDECLTYPE_FLOAT3)
7478 normal_size = sizeof(D3DXVECTOR3);
7480 else if (normal_declaration->Type == D3DDECLTYPE_FLOAT4)
7482 normal_size = sizeof(D3DXVECTOR4);
7484 else
7486 WARN("unsupported normals type %u\n", normal_declaration->Type);
7487 return D3DERR_INVALIDCALL;
7490 num_faces = mesh->lpVtbl->GetNumFaces(mesh);
7491 num_vertices = mesh->lpVtbl->GetNumVertices(mesh);
7492 vertex_stride = mesh->lpVtbl->GetNumBytesPerVertex(mesh);
7493 indices_are_32bit = mesh->lpVtbl->GetOptions(mesh) & D3DXMESH_32BIT;
7495 point_reps = malloc(num_vertices * sizeof(*point_reps));
7496 if (!point_reps)
7498 hr = E_OUTOFMEMORY;
7499 goto done;
7502 if (adjacency)
7504 if (FAILED(hr = mesh->lpVtbl->ConvertAdjacencyToPointReps(mesh, adjacency, point_reps)))
7505 goto done;
7507 else
7509 for (i = 0; i < num_vertices; i++)
7510 point_reps[i] = i;
7513 if (FAILED(hr = mesh->lpVtbl->LockIndexBuffer(mesh, 0, &indices)))
7514 goto done;
7516 if (FAILED(hr = mesh->lpVtbl->LockVertexBuffer(mesh, 0, (void **)&vertices)))
7517 goto done;
7519 for (i = 0; i < num_vertices; i++)
7521 static const D3DXVECTOR4 default_vector = {0.0f, 0.0f, 0.0f, 1.0f};
7522 void *normal = vertices + normal_declaration->Offset + i * vertex_stride;
7524 memcpy(normal, &default_vector, normal_size);
7527 for (i = 0; i < num_faces; i++)
7529 float denominator, weights[3];
7530 D3DXVECTOR3 a, b, cross, face_normal;
7531 const DWORD face_indices[3] =
7533 read_ib(indices, indices_are_32bit, 3 * i + 0),
7534 read_ib(indices, indices_are_32bit, 3 * i + 1),
7535 read_ib(indices, indices_are_32bit, 3 * i + 2)
7537 const D3DXVECTOR3 v0 = read_vec3(vertices, position_declaration, vertex_stride, face_indices[0]);
7538 const D3DXVECTOR3 v1 = read_vec3(vertices, position_declaration, vertex_stride, face_indices[1]);
7539 const D3DXVECTOR3 v2 = read_vec3(vertices, position_declaration, vertex_stride, face_indices[2]);
7541 D3DXVec3Cross(&cross, D3DXVec3Subtract(&a, &v0, &v1), D3DXVec3Subtract(&b, &v0, &v2));
7543 switch (weighting_method)
7545 case D3DXTANGENT_WEIGHT_EQUAL:
7546 weights[0] = weights[1] = weights[2] = 1.0f;
7547 break;
7548 case D3DXTANGENT_WEIGHT_BY_AREA:
7549 weights[0] = weights[1] = weights[2] = D3DXVec3Length(&cross);
7550 break;
7551 default:
7552 /* weight by angle */
7553 denominator = D3DXVec3Length(&a) * D3DXVec3Length(&b);
7554 if (!denominator)
7555 weights[0] = 0.0f;
7556 else
7557 weights[0] = acosf(D3DXVec3Dot(&a, &b) / denominator);
7559 D3DXVec3Subtract(&a, &v1, &v0);
7560 D3DXVec3Subtract(&b, &v1, &v2);
7561 denominator = D3DXVec3Length(&a) * D3DXVec3Length(&b);
7562 if (!denominator)
7563 weights[1] = 0.0f;
7564 else
7565 weights[1] = acosf(D3DXVec3Dot(&a, &b) / denominator);
7567 D3DXVec3Subtract(&a, &v2, &v0);
7568 D3DXVec3Subtract(&b, &v2, &v1);
7569 denominator = D3DXVec3Length(&a) * D3DXVec3Length(&b);
7570 if (!denominator)
7571 weights[2] = 0.0f;
7572 else
7573 weights[2] = acosf(D3DXVec3Dot(&a, &b) / denominator);
7575 break;
7578 D3DXVec3Normalize(&face_normal, &cross);
7580 for (j = 0; j < 3; j++)
7582 D3DXVECTOR3 normal;
7583 DWORD rep_index = point_reps[face_indices[j]];
7584 D3DXVECTOR3 *rep_normal = vertex_element_vec3(vertices, normal_declaration, vertex_stride, rep_index);
7586 D3DXVec3Scale(&normal, &face_normal, weights[j]);
7587 D3DXVec3Add(rep_normal, rep_normal, &normal);
7591 for (i = 0; i < num_vertices; i++)
7593 DWORD rep_index = point_reps[i];
7594 D3DXVECTOR3 *normal = vertex_element_vec3(vertices, normal_declaration, vertex_stride, i);
7595 D3DXVECTOR3 *rep_normal = vertex_element_vec3(vertices, normal_declaration, vertex_stride, rep_index);
7597 if (i == rep_index)
7598 D3DXVec3Normalize(rep_normal, rep_normal);
7599 else
7600 *normal = *rep_normal;
7603 hr = D3D_OK;
7605 done:
7606 if (vertices)
7607 mesh->lpVtbl->UnlockVertexBuffer(mesh);
7609 if (indices)
7610 mesh->lpVtbl->UnlockIndexBuffer(mesh);
7612 free(point_reps);
7614 return hr;
7617 /*************************************************************************
7618 * D3DXComputeNormals (D3DX9_36.@)
7620 HRESULT WINAPI D3DXComputeNormals(struct ID3DXBaseMesh *mesh, const DWORD *adjacency)
7622 TRACE("mesh %p, adjacency %p\n", mesh, adjacency);
7624 if (mesh && (ID3DXMeshVtbl *)mesh->lpVtbl != &D3DXMesh_Vtbl)
7626 ERR("Invalid virtual table\n");
7627 return D3DERR_INVALIDCALL;
7630 return D3DXComputeTangentFrameEx((ID3DXMesh *)mesh, D3DX_DEFAULT, 0,
7631 D3DX_DEFAULT, 0, D3DX_DEFAULT, 0, D3DDECLUSAGE_NORMAL, 0,
7632 D3DXTANGENT_GENERATE_IN_PLACE | D3DXTANGENT_CALCULATE_NORMALS,
7633 adjacency, -1.01f, -0.01f, -1.01f, NULL, NULL);
7636 /*************************************************************************
7637 * D3DXIntersect (D3DX9_36.@)
7639 HRESULT WINAPI D3DXIntersect(ID3DXBaseMesh *mesh, const D3DXVECTOR3 *ray_pos, const D3DXVECTOR3 *ray_dir,
7640 BOOL *hit, DWORD *face_index, float *u, float *v, float *distance, ID3DXBuffer **all_hits, DWORD *count_of_hits)
7642 FIXME("mesh %p, ray_pos %p, ray_dir %p, hit %p, face_index %p, u %p, v %p, distance %p, all_hits %p, "
7643 "count_of_hits %p stub!\n", mesh, ray_pos, ray_dir, hit, face_index, u, v, distance, all_hits, count_of_hits);
7645 return E_NOTIMPL;
7648 HRESULT WINAPI D3DXTessellateNPatches(ID3DXMesh *mesh, const DWORD *adjacency_in, float num_segs,
7649 BOOL quadratic_normals, ID3DXMesh **mesh_out, ID3DXBuffer **adjacency_out)
7651 FIXME("mesh %p, adjacency_in %p, num_segs %f, quadratic_normals %d, mesh_out %p, adjacency_out %p stub.\n",
7652 mesh, adjacency_in, num_segs, quadratic_normals, mesh_out, adjacency_out);
7654 return E_NOTIMPL;
7657 HRESULT WINAPI D3DXConvertMeshSubsetToSingleStrip(struct ID3DXBaseMesh *mesh_in, DWORD attribute_id,
7658 DWORD ib_flags, struct IDirect3DIndexBuffer9 **index_buffer, DWORD *index_count)
7660 FIXME("mesh_in %p, attribute_id %lu, ib_flags %#lx, index_buffer %p, index_count %p stub.\n",
7661 mesh_in, attribute_id, ib_flags, index_buffer, index_count);
7663 return E_NOTIMPL;
7666 struct frame_node
7668 struct list entry;
7669 D3DXFRAME *frame;
7672 static BOOL queue_frame_node(struct list *queue, D3DXFRAME *frame)
7674 struct frame_node *node;
7676 if (!frame->pFrameFirstChild)
7677 return TRUE;
7679 node = malloc(sizeof(*node));
7680 if (!node)
7681 return FALSE;
7683 node->frame = frame;
7684 list_add_tail(queue, &node->entry);
7686 return TRUE;
7689 static void empty_frame_queue(struct list *queue)
7691 struct frame_node *cur, *cur2;
7692 LIST_FOR_EACH_ENTRY_SAFE(cur, cur2, queue, struct frame_node, entry)
7694 list_remove(&cur->entry);
7695 free(cur);
7699 D3DXFRAME * WINAPI D3DXFrameFind(const D3DXFRAME *root, const char *name)
7701 D3DXFRAME *found = NULL, *frame;
7702 struct list queue;
7704 TRACE("root frame %p, name %s.\n", root, debugstr_a(name));
7706 if (!root)
7707 return NULL;
7709 list_init(&queue);
7711 frame = (D3DXFRAME *)root;
7713 for (;;)
7715 struct frame_node *node;
7717 while (frame)
7719 if ((name && frame->Name && !strcmp(frame->Name, name)) || (!name && !frame->Name))
7721 found = frame;
7722 goto cleanup;
7725 if (!queue_frame_node(&queue, frame))
7726 goto cleanup;
7728 frame = frame->pFrameSibling;
7731 if (list_empty(&queue))
7732 break;
7734 node = LIST_ENTRY(list_head(&queue), struct frame_node, entry);
7735 list_remove(&node->entry);
7736 frame = node->frame->pFrameFirstChild;
7737 free(node);
7740 cleanup:
7741 empty_frame_queue(&queue);
7743 return found;