d3d8: Get rid of the format switching code in d3d8_device_CopyRects().
[wine.git] / dlls / d3dx9_36 / mesh.c
blobaecb45de45993a2072dd1e9886caa39b86ee96ee
1 /*
2 * Mesh operations specific to D3DX9.
4 * Copyright (C) 2005 Henri Verbeet
5 * Copyright (C) 2006 Ivan Gyurdiev
6 * Copyright (C) 2009 David Adam
7 * Copyright (C) 2010 Tony Wasserka
8 * Copyright (C) 2011 Dylan Smith
9 * Copyright (C) 2011 Michael Mc Donnell
10 * Copyright (C) 2013 Christian Costa
12 * This library is free software; you can redistribute it and/or
13 * modify it under the terms of the GNU Lesser General Public
14 * License as published by the Free Software Foundation; either
15 * version 2.1 of the License, or (at your option) any later version.
17 * This library is distributed in the hope that it will be useful,
18 * but WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
20 * Lesser General Public License for more details.
22 * You should have received a copy of the GNU Lesser General Public
23 * License along with this library; if not, write to the Free Software
24 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
27 #include "config.h"
28 #include "wine/port.h"
30 #define COBJMACROS
31 #define NONAMELESSUNION
32 #include <assert.h>
33 #ifdef HAVE_FLOAT_H
34 # include <float.h>
35 #endif
36 #include "windef.h"
37 #include "wingdi.h"
38 #include "d3dx9.h"
39 #undef MAKE_DDHRESULT
40 #include "dxfile.h"
41 #include "rmxfguid.h"
42 #include "rmxftmpl.h"
43 #include "wine/debug.h"
44 #include "wine/unicode.h"
45 #include "wine/list.h"
46 #include "d3dx9_36_private.h"
48 WINE_DEFAULT_DEBUG_CHANNEL(d3dx);
50 struct d3dx9_mesh
52 ID3DXMesh ID3DXMesh_iface;
53 LONG ref;
55 DWORD numfaces;
56 DWORD numvertices;
57 DWORD options;
58 DWORD fvf;
59 IDirect3DDevice9 *device;
60 D3DVERTEXELEMENT9 cached_declaration[MAX_FVF_DECL_SIZE];
61 IDirect3DVertexDeclaration9 *vertex_declaration;
62 UINT vertex_declaration_size;
63 UINT num_elem;
64 IDirect3DVertexBuffer9 *vertex_buffer;
65 IDirect3DIndexBuffer9 *index_buffer;
66 DWORD *attrib_buffer;
67 int attrib_buffer_lock_count;
68 DWORD attrib_table_size;
69 D3DXATTRIBUTERANGE *attrib_table;
72 const UINT d3dx_decltype_size[] =
74 /* D3DDECLTYPE_FLOAT1 */ sizeof(FLOAT),
75 /* D3DDECLTYPE_FLOAT2 */ sizeof(D3DXVECTOR2),
76 /* D3DDECLTYPE_FLOAT3 */ sizeof(D3DXVECTOR3),
77 /* D3DDECLTYPE_FLOAT4 */ sizeof(D3DXVECTOR4),
78 /* D3DDECLTYPE_D3DCOLOR */ sizeof(D3DCOLOR),
79 /* D3DDECLTYPE_UBYTE4 */ 4 * sizeof(BYTE),
80 /* D3DDECLTYPE_SHORT2 */ 2 * sizeof(SHORT),
81 /* D3DDECLTYPE_SHORT4 */ 4 * sizeof(SHORT),
82 /* D3DDECLTYPE_UBYTE4N */ 4 * sizeof(BYTE),
83 /* D3DDECLTYPE_SHORT2N */ 2 * sizeof(SHORT),
84 /* D3DDECLTYPE_SHORT4N */ 4 * sizeof(SHORT),
85 /* D3DDECLTYPE_USHORT2N */ 2 * sizeof(USHORT),
86 /* D3DDECLTYPE_USHORT4N */ 4 * sizeof(USHORT),
87 /* D3DDECLTYPE_UDEC3 */ 4, /* 3 * 10 bits + 2 padding */
88 /* D3DDECLTYPE_DEC3N */ 4,
89 /* D3DDECLTYPE_FLOAT16_2 */ 2 * sizeof(D3DXFLOAT16),
90 /* D3DDECLTYPE_FLOAT16_4 */ 4 * sizeof(D3DXFLOAT16),
93 static inline struct d3dx9_mesh *impl_from_ID3DXMesh(ID3DXMesh *iface)
95 return CONTAINING_RECORD(iface, struct d3dx9_mesh, ID3DXMesh_iface);
98 static HRESULT WINAPI d3dx9_mesh_QueryInterface(ID3DXMesh *iface, REFIID riid, void **out)
100 TRACE("iface %p, riid %s, out %p.\n", iface, debugstr_guid(riid), out);
102 if (IsEqualGUID(riid, &IID_IUnknown) ||
103 IsEqualGUID(riid, &IID_ID3DXBaseMesh) ||
104 IsEqualGUID(riid, &IID_ID3DXMesh))
106 iface->lpVtbl->AddRef(iface);
107 *out = iface;
108 return S_OK;
111 WARN("Interface %s not found.\n", debugstr_guid(riid));
113 return E_NOINTERFACE;
116 static ULONG WINAPI d3dx9_mesh_AddRef(ID3DXMesh *iface)
118 struct d3dx9_mesh *mesh = impl_from_ID3DXMesh(iface);
119 ULONG refcount = InterlockedIncrement(&mesh->ref);
121 TRACE("%p increasing refcount to %u.\n", mesh, refcount);
123 return refcount;
126 static ULONG WINAPI d3dx9_mesh_Release(ID3DXMesh *iface)
128 struct d3dx9_mesh *mesh = impl_from_ID3DXMesh(iface);
129 ULONG refcount = InterlockedDecrement(&mesh->ref);
131 TRACE("%p decreasing refcount to %u.\n", mesh, refcount);
133 if (!refcount)
135 IDirect3DIndexBuffer9_Release(mesh->index_buffer);
136 IDirect3DVertexBuffer9_Release(mesh->vertex_buffer);
137 if (mesh->vertex_declaration)
138 IDirect3DVertexDeclaration9_Release(mesh->vertex_declaration);
139 IDirect3DDevice9_Release(mesh->device);
140 HeapFree(GetProcessHeap(), 0, mesh->attrib_buffer);
141 HeapFree(GetProcessHeap(), 0, mesh->attrib_table);
142 HeapFree(GetProcessHeap(), 0, mesh);
145 return refcount;
148 static HRESULT WINAPI d3dx9_mesh_DrawSubset(ID3DXMesh *iface, DWORD attrib_id)
150 struct d3dx9_mesh *This = impl_from_ID3DXMesh(iface);
151 HRESULT hr;
152 DWORD face_start;
153 DWORD face_end = 0;
154 DWORD vertex_size;
156 TRACE("iface %p, attrib_id %u.\n", iface, attrib_id);
158 if (!This->vertex_declaration)
160 WARN("Can't draw a mesh with an invalid vertex declaration.\n");
161 return E_FAIL;
164 vertex_size = iface->lpVtbl->GetNumBytesPerVertex(iface);
166 hr = IDirect3DDevice9_SetVertexDeclaration(This->device, This->vertex_declaration);
167 if (FAILED(hr)) return hr;
168 hr = IDirect3DDevice9_SetStreamSource(This->device, 0, This->vertex_buffer, 0, vertex_size);
169 if (FAILED(hr)) return hr;
170 hr = IDirect3DDevice9_SetIndices(This->device, This->index_buffer);
171 if (FAILED(hr)) return hr;
173 while (face_end < This->numfaces)
175 for (face_start = face_end; face_start < This->numfaces; face_start++)
177 if (This->attrib_buffer[face_start] == attrib_id)
178 break;
180 if (face_start >= This->numfaces)
181 break;
182 for (face_end = face_start + 1; face_end < This->numfaces; face_end++)
184 if (This->attrib_buffer[face_end] != attrib_id)
185 break;
188 hr = IDirect3DDevice9_DrawIndexedPrimitive(This->device, D3DPT_TRIANGLELIST,
189 0, 0, This->numvertices, face_start * 3, face_end - face_start);
190 if (FAILED(hr)) return hr;
193 return D3D_OK;
196 static DWORD WINAPI d3dx9_mesh_GetNumFaces(ID3DXMesh *iface)
198 struct d3dx9_mesh *mesh = impl_from_ID3DXMesh(iface);
200 TRACE("iface %p.\n", iface);
202 return mesh->numfaces;
205 static DWORD WINAPI d3dx9_mesh_GetNumVertices(ID3DXMesh *iface)
207 struct d3dx9_mesh *mesh = impl_from_ID3DXMesh(iface);
209 TRACE("iface %p.\n", iface);
211 return mesh->numvertices;
214 static DWORD WINAPI d3dx9_mesh_GetFVF(ID3DXMesh *iface)
216 struct d3dx9_mesh *mesh = impl_from_ID3DXMesh(iface);
218 TRACE("iface %p.\n", iface);
220 return mesh->fvf;
223 static void copy_declaration(D3DVERTEXELEMENT9 *dst, const D3DVERTEXELEMENT9 *src, UINT num_elem)
225 memcpy(dst, src, num_elem * sizeof(*src));
228 static HRESULT WINAPI d3dx9_mesh_GetDeclaration(ID3DXMesh *iface, D3DVERTEXELEMENT9 declaration[MAX_FVF_DECL_SIZE])
230 struct d3dx9_mesh *mesh = impl_from_ID3DXMesh(iface);
232 TRACE("iface %p, declaration %p.\n", iface, declaration);
234 if (!declaration)
235 return D3DERR_INVALIDCALL;
237 copy_declaration(declaration, mesh->cached_declaration, mesh->num_elem);
239 return D3D_OK;
242 static DWORD WINAPI d3dx9_mesh_GetNumBytesPerVertex(ID3DXMesh *iface)
244 struct d3dx9_mesh *mesh = impl_from_ID3DXMesh(iface);
246 TRACE("iface %p.\n", iface);
248 return mesh->vertex_declaration_size;
251 static DWORD WINAPI d3dx9_mesh_GetOptions(ID3DXMesh *iface)
253 struct d3dx9_mesh *mesh = impl_from_ID3DXMesh(iface);
255 TRACE("iface %p.\n", iface);
257 return mesh->options;
260 static HRESULT WINAPI d3dx9_mesh_GetDevice(struct ID3DXMesh *iface, struct IDirect3DDevice9 **device)
262 struct d3dx9_mesh *mesh = impl_from_ID3DXMesh(iface);
264 TRACE("iface %p, device %p.\n", iface, device);
266 if (!device)
267 return D3DERR_INVALIDCALL;
268 *device = mesh->device;
269 IDirect3DDevice9_AddRef(mesh->device);
271 return D3D_OK;
274 static HRESULT WINAPI d3dx9_mesh_CloneMeshFVF(struct ID3DXMesh *iface, DWORD options, DWORD fvf,
275 struct IDirect3DDevice9 *device, struct ID3DXMesh **clone_mesh)
277 HRESULT hr;
278 D3DVERTEXELEMENT9 declaration[MAX_FVF_DECL_SIZE];
280 TRACE("iface %p, options %#x, fvf %#x, device %p, clone_mesh %p.\n",
281 iface, options, fvf, device, clone_mesh);
283 if (FAILED(hr = D3DXDeclaratorFromFVF(fvf, declaration)))
284 return hr;
286 return iface->lpVtbl->CloneMesh(iface, options, declaration, device, clone_mesh);
289 static FLOAT scale_clamp_ubyten(FLOAT value)
291 value = value * UCHAR_MAX;
293 if (value < 0.0f)
295 return 0.0f;
297 else
299 if (value > UCHAR_MAX) /* Clamp at 255 */
300 return UCHAR_MAX;
301 else
302 return value;
306 static FLOAT scale_clamp_shortn(FLOAT value)
308 value = value * SHRT_MAX;
310 /* The tests show that the range is SHRT_MIN + 1 to SHRT_MAX. */
311 if (value <= SHRT_MIN)
313 return SHRT_MIN + 1;
315 else if (value > SHRT_MAX)
317 return SHRT_MAX;
319 else
321 return value;
325 static FLOAT scale_clamp_ushortn(FLOAT value)
327 value = value * USHRT_MAX;
329 if (value < 0.0f)
331 return 0.0f;
333 else
335 if (value > USHRT_MAX) /* Clamp at 65535 */
336 return USHRT_MAX;
337 else
338 return value;
342 static INT simple_round(FLOAT value)
344 int res = (INT)(value + 0.5f);
346 return res;
349 static void convert_float4(BYTE *dst, const D3DXVECTOR4 *src, D3DDECLTYPE type_dst)
351 BOOL fixme_once = FALSE;
353 switch (type_dst)
355 case D3DDECLTYPE_FLOAT1:
357 FLOAT *dst_ptr = (FLOAT*)dst;
358 *dst_ptr = src->x;
359 break;
361 case D3DDECLTYPE_FLOAT2:
363 D3DXVECTOR2 *dst_ptr = (D3DXVECTOR2*)dst;
364 dst_ptr->x = src->x;
365 dst_ptr->y = src->y;
366 break;
368 case D3DDECLTYPE_FLOAT3:
370 D3DXVECTOR3 *dst_ptr = (D3DXVECTOR3*)dst;
371 dst_ptr->x = src->x;
372 dst_ptr->y = src->y;
373 dst_ptr->z = src->z;
374 break;
376 case D3DDECLTYPE_FLOAT4:
378 D3DXVECTOR4 *dst_ptr = (D3DXVECTOR4*)dst;
379 dst_ptr->x = src->x;
380 dst_ptr->y = src->y;
381 dst_ptr->z = src->z;
382 dst_ptr->w = src->w;
383 break;
385 case D3DDECLTYPE_D3DCOLOR:
387 dst[0] = (BYTE)simple_round(scale_clamp_ubyten(src->z));
388 dst[1] = (BYTE)simple_round(scale_clamp_ubyten(src->y));
389 dst[2] = (BYTE)simple_round(scale_clamp_ubyten(src->x));
390 dst[3] = (BYTE)simple_round(scale_clamp_ubyten(src->w));
391 break;
393 case D3DDECLTYPE_UBYTE4:
395 dst[0] = src->x < 0.0f ? 0 : (BYTE)simple_round(src->x);
396 dst[1] = src->y < 0.0f ? 0 : (BYTE)simple_round(src->y);
397 dst[2] = src->z < 0.0f ? 0 : (BYTE)simple_round(src->z);
398 dst[3] = src->w < 0.0f ? 0 : (BYTE)simple_round(src->w);
399 break;
401 case D3DDECLTYPE_SHORT2:
403 SHORT *dst_ptr = (SHORT*)dst;
404 dst_ptr[0] = (SHORT)simple_round(src->x);
405 dst_ptr[1] = (SHORT)simple_round(src->y);
406 break;
408 case D3DDECLTYPE_SHORT4:
410 SHORT *dst_ptr = (SHORT*)dst;
411 dst_ptr[0] = (SHORT)simple_round(src->x);
412 dst_ptr[1] = (SHORT)simple_round(src->y);
413 dst_ptr[2] = (SHORT)simple_round(src->z);
414 dst_ptr[3] = (SHORT)simple_round(src->w);
415 break;
417 case D3DDECLTYPE_UBYTE4N:
419 dst[0] = (BYTE)simple_round(scale_clamp_ubyten(src->x));
420 dst[1] = (BYTE)simple_round(scale_clamp_ubyten(src->y));
421 dst[2] = (BYTE)simple_round(scale_clamp_ubyten(src->z));
422 dst[3] = (BYTE)simple_round(scale_clamp_ubyten(src->w));
423 break;
425 case D3DDECLTYPE_SHORT2N:
427 SHORT *dst_ptr = (SHORT*)dst;
428 dst_ptr[0] = (SHORT)simple_round(scale_clamp_shortn(src->x));
429 dst_ptr[1] = (SHORT)simple_round(scale_clamp_shortn(src->y));
430 break;
432 case D3DDECLTYPE_SHORT4N:
434 SHORT *dst_ptr = (SHORT*)dst;
435 dst_ptr[0] = (SHORT)simple_round(scale_clamp_shortn(src->x));
436 dst_ptr[1] = (SHORT)simple_round(scale_clamp_shortn(src->y));
437 dst_ptr[2] = (SHORT)simple_round(scale_clamp_shortn(src->z));
438 dst_ptr[3] = (SHORT)simple_round(scale_clamp_shortn(src->w));
439 break;
441 case D3DDECLTYPE_USHORT2N:
443 USHORT *dst_ptr = (USHORT*)dst;
444 dst_ptr[0] = (USHORT)simple_round(scale_clamp_ushortn(src->x));
445 dst_ptr[1] = (USHORT)simple_round(scale_clamp_ushortn(src->y));
446 break;
448 case D3DDECLTYPE_USHORT4N:
450 USHORT *dst_ptr = (USHORT*)dst;
451 dst_ptr[0] = (USHORT)simple_round(scale_clamp_ushortn(src->x));
452 dst_ptr[1] = (USHORT)simple_round(scale_clamp_ushortn(src->y));
453 dst_ptr[2] = (USHORT)simple_round(scale_clamp_ushortn(src->z));
454 dst_ptr[3] = (USHORT)simple_round(scale_clamp_ushortn(src->w));
455 break;
457 case D3DDECLTYPE_FLOAT16_2:
459 D3DXFloat32To16Array((D3DXFLOAT16*)dst, (FLOAT*)src, 2);
460 break;
462 case D3DDECLTYPE_FLOAT16_4:
464 D3DXFloat32To16Array((D3DXFLOAT16*)dst, (FLOAT*)src, 4);
465 break;
467 default:
468 if (!fixme_once++)
469 FIXME("Conversion from D3DDECLTYPE_FLOAT4 to %d not implemented.\n", type_dst);
470 break;
474 static void convert_component(BYTE *dst, BYTE *src, D3DDECLTYPE type_dst, D3DDECLTYPE type_src)
476 BOOL fixme_once = FALSE;
478 switch (type_src)
480 case D3DDECLTYPE_FLOAT1:
482 FLOAT *src_ptr = (FLOAT*)src;
483 D3DXVECTOR4 src_float4 = {*src_ptr, 0.0f, 0.0f, 1.0f};
484 convert_float4(dst, &src_float4, type_dst);
485 break;
487 case D3DDECLTYPE_FLOAT2:
489 D3DXVECTOR2 *src_ptr = (D3DXVECTOR2*)src;
490 D3DXVECTOR4 src_float4 = {src_ptr->x, src_ptr->y, 0.0f, 1.0f};
491 convert_float4(dst, &src_float4, type_dst);
492 break;
494 case D3DDECLTYPE_FLOAT3:
496 D3DXVECTOR3 *src_ptr = (D3DXVECTOR3*)src;
497 D3DXVECTOR4 src_float4 = {src_ptr->x, src_ptr->y, src_ptr->z, 1.0f};
498 convert_float4(dst, &src_float4, type_dst);
499 break;
501 case D3DDECLTYPE_FLOAT4:
503 D3DXVECTOR4 *src_ptr = (D3DXVECTOR4*)src;
504 D3DXVECTOR4 src_float4 = {src_ptr->x, src_ptr->y, src_ptr->z, src_ptr->w};
505 convert_float4(dst, &src_float4, type_dst);
506 break;
508 case D3DDECLTYPE_D3DCOLOR:
510 D3DXVECTOR4 src_float4 =
512 (FLOAT)src[2]/UCHAR_MAX,
513 (FLOAT)src[1]/UCHAR_MAX,
514 (FLOAT)src[0]/UCHAR_MAX,
515 (FLOAT)src[3]/UCHAR_MAX
517 convert_float4(dst, &src_float4, type_dst);
518 break;
520 case D3DDECLTYPE_UBYTE4:
522 D3DXVECTOR4 src_float4 = {src[0], src[1], src[2], src[3]};
523 convert_float4(dst, &src_float4, type_dst);
524 break;
526 case D3DDECLTYPE_SHORT2:
528 SHORT *src_ptr = (SHORT*)src;
529 D3DXVECTOR4 src_float4 = {src_ptr[0], src_ptr[1], 0.0f, 1.0f};
530 convert_float4(dst, &src_float4, type_dst);
531 break;
533 case D3DDECLTYPE_SHORT4:
535 SHORT *src_ptr = (SHORT*)src;
536 D3DXVECTOR4 src_float4 = {src_ptr[0], src_ptr[1], src_ptr[2], src_ptr[3]};
537 convert_float4(dst, &src_float4, type_dst);
538 break;
540 case D3DDECLTYPE_UBYTE4N:
542 D3DXVECTOR4 src_float4 =
544 (FLOAT)src[0]/UCHAR_MAX,
545 (FLOAT)src[1]/UCHAR_MAX,
546 (FLOAT)src[2]/UCHAR_MAX,
547 (FLOAT)src[3]/UCHAR_MAX
549 convert_float4(dst, &src_float4, type_dst);
550 break;
552 case D3DDECLTYPE_SHORT2N:
554 SHORT *src_ptr = (SHORT*)src;
555 D3DXVECTOR4 src_float4 = {(FLOAT)src_ptr[0]/SHRT_MAX, (FLOAT)src_ptr[1]/SHRT_MAX, 0.0f, 1.0f};
556 convert_float4(dst, &src_float4, type_dst);
557 break;
559 case D3DDECLTYPE_SHORT4N:
561 SHORT *src_ptr = (SHORT*)src;
562 D3DXVECTOR4 src_float4 =
564 (FLOAT)src_ptr[0]/SHRT_MAX,
565 (FLOAT)src_ptr[1]/SHRT_MAX,
566 (FLOAT)src_ptr[2]/SHRT_MAX,
567 (FLOAT)src_ptr[3]/SHRT_MAX
569 convert_float4(dst, &src_float4, type_dst);
570 break;
572 case D3DDECLTYPE_FLOAT16_2:
574 D3DXVECTOR4 src_float4 = {0.0f, 0.0f, 0.0f, 1.0f};
575 D3DXFloat16To32Array((FLOAT*)&src_float4, (D3DXFLOAT16*)src, 2);
576 convert_float4(dst, &src_float4, type_dst);
577 break;
579 case D3DDECLTYPE_FLOAT16_4:
581 D3DXVECTOR4 src_float4;
582 D3DXFloat16To32Array((FLOAT*)&src_float4, (D3DXFLOAT16*)src, 4);
583 convert_float4(dst, &src_float4, type_dst);
584 break;
586 default:
587 if (!fixme_once++)
588 FIXME("Conversion of D3DDECLTYPE %d to %d not implemented.\n", type_src, type_dst);
589 break;
593 static INT get_equivalent_declaration_index(D3DVERTEXELEMENT9 orig_declaration, D3DVERTEXELEMENT9 *declaration)
595 INT i;
597 for (i = 0; declaration[i].Stream != 0xff; i++)
599 if (orig_declaration.Usage == declaration[i].Usage
600 && orig_declaration.UsageIndex == declaration[i].UsageIndex)
602 return i;
606 return -1;
609 static HRESULT convert_vertex_buffer(ID3DXMesh *mesh_dst, ID3DXMesh *mesh_src)
611 HRESULT hr;
612 D3DVERTEXELEMENT9 orig_declaration[MAX_FVF_DECL_SIZE] = {D3DDECL_END()};
613 D3DVERTEXELEMENT9 declaration[MAX_FVF_DECL_SIZE] = {D3DDECL_END()};
614 BYTE *vb_dst = NULL;
615 BYTE *vb_src = NULL;
616 UINT i;
617 UINT num_vertices = mesh_src->lpVtbl->GetNumVertices(mesh_src);
618 UINT dst_vertex_size = mesh_dst->lpVtbl->GetNumBytesPerVertex(mesh_dst);
619 UINT src_vertex_size = mesh_src->lpVtbl->GetNumBytesPerVertex(mesh_src);
621 hr = mesh_src->lpVtbl->GetDeclaration(mesh_src, orig_declaration);
622 if (FAILED(hr)) return hr;
623 hr = mesh_dst->lpVtbl->GetDeclaration(mesh_dst, declaration);
624 if (FAILED(hr)) return hr;
626 hr = mesh_src->lpVtbl->LockVertexBuffer(mesh_src, D3DLOCK_READONLY, (void**)&vb_src);
627 if (FAILED(hr)) goto cleanup;
628 hr = mesh_dst->lpVtbl->LockVertexBuffer(mesh_dst, 0, (void**)&vb_dst);
629 if (FAILED(hr)) goto cleanup;
631 /* Clear all new fields by clearing the entire vertex buffer. */
632 memset(vb_dst, 0, num_vertices * dst_vertex_size);
634 for (i = 0; orig_declaration[i].Stream != 0xff; i++)
636 INT eq_idx = get_equivalent_declaration_index(orig_declaration[i], declaration);
638 if (eq_idx >= 0)
640 UINT j;
641 for (j = 0; j < num_vertices; j++)
643 UINT idx_dst = dst_vertex_size * j + declaration[eq_idx].Offset;
644 UINT idx_src = src_vertex_size * j + orig_declaration[i].Offset;
645 UINT type_size = d3dx_decltype_size[orig_declaration[i].Type];
647 if (orig_declaration[i].Type == declaration[eq_idx].Type)
648 memcpy(&vb_dst[idx_dst], &vb_src[idx_src], type_size);
649 else
650 convert_component(&vb_dst[idx_dst], &vb_src[idx_src], declaration[eq_idx].Type, orig_declaration[i].Type);
655 hr = D3D_OK;
656 cleanup:
657 if (vb_dst) mesh_dst->lpVtbl->UnlockVertexBuffer(mesh_dst);
658 if (vb_src) mesh_src->lpVtbl->UnlockVertexBuffer(mesh_src);
660 return hr;
663 static BOOL declaration_equals(const D3DVERTEXELEMENT9 *declaration1, const D3DVERTEXELEMENT9 *declaration2)
665 UINT size1 = 0, size2 = 0;
667 /* Find the size of each declaration */
668 while (declaration1[size1].Stream != 0xff) size1++;
669 while (declaration2[size2].Stream != 0xff) size2++;
671 /* If not same size then they are definitely not equal */
672 if (size1 != size2)
673 return FALSE;
675 /* Check that all components are the same */
676 if (memcmp(declaration1, declaration2, size1*sizeof(*declaration1)) == 0)
677 return TRUE;
679 return FALSE;
682 static HRESULT WINAPI d3dx9_mesh_CloneMesh(struct ID3DXMesh *iface, DWORD options,
683 const D3DVERTEXELEMENT9 *declaration, struct IDirect3DDevice9 *device, struct ID3DXMesh **clone_mesh_out)
685 struct d3dx9_mesh *This = impl_from_ID3DXMesh(iface);
686 struct d3dx9_mesh *cloned_this;
687 ID3DXMesh *clone_mesh;
688 D3DVERTEXELEMENT9 orig_declaration[MAX_FVF_DECL_SIZE] = { D3DDECL_END() };
689 void *data_in, *data_out;
690 DWORD vertex_size;
691 HRESULT hr;
692 BOOL same_declaration;
694 TRACE("iface %p, options %#x, declaration %p, device %p, clone_mesh_out %p.\n",
695 iface, options, declaration, device, clone_mesh_out);
697 if (!clone_mesh_out)
698 return D3DERR_INVALIDCALL;
700 hr = iface->lpVtbl->GetDeclaration(iface, orig_declaration);
701 if (FAILED(hr)) return hr;
703 hr = D3DXCreateMesh(This->numfaces, This->numvertices, options & ~D3DXMESH_VB_SHARE,
704 declaration, device, &clone_mesh);
705 if (FAILED(hr)) return hr;
707 cloned_this = impl_from_ID3DXMesh(clone_mesh);
708 vertex_size = clone_mesh->lpVtbl->GetNumBytesPerVertex(clone_mesh);
709 same_declaration = declaration_equals(declaration, orig_declaration);
711 if (options & D3DXMESH_VB_SHARE) {
712 if (!same_declaration) {
713 hr = D3DERR_INVALIDCALL;
714 goto error;
716 IDirect3DVertexBuffer9_AddRef(This->vertex_buffer);
717 /* FIXME: refactor to avoid creating a new vertex buffer */
718 IDirect3DVertexBuffer9_Release(cloned_this->vertex_buffer);
719 cloned_this->vertex_buffer = This->vertex_buffer;
720 } else if (same_declaration) {
721 hr = iface->lpVtbl->LockVertexBuffer(iface, D3DLOCK_READONLY, &data_in);
722 if (FAILED(hr)) goto error;
723 hr = clone_mesh->lpVtbl->LockVertexBuffer(clone_mesh, 0, &data_out);
724 if (FAILED(hr)) {
725 iface->lpVtbl->UnlockVertexBuffer(iface);
726 goto error;
728 memcpy(data_out, data_in, This->numvertices * vertex_size);
729 clone_mesh->lpVtbl->UnlockVertexBuffer(clone_mesh);
730 iface->lpVtbl->UnlockVertexBuffer(iface);
731 } else {
732 hr = convert_vertex_buffer(clone_mesh, iface);
733 if (FAILED(hr)) goto error;
736 hr = iface->lpVtbl->LockIndexBuffer(iface, D3DLOCK_READONLY, &data_in);
737 if (FAILED(hr)) goto error;
738 hr = clone_mesh->lpVtbl->LockIndexBuffer(clone_mesh, 0, &data_out);
739 if (FAILED(hr)) {
740 iface->lpVtbl->UnlockIndexBuffer(iface);
741 goto error;
743 if ((options ^ This->options) & D3DXMESH_32BIT) {
744 DWORD i;
745 if (options & D3DXMESH_32BIT) {
746 for (i = 0; i < This->numfaces * 3; i++)
747 ((DWORD*)data_out)[i] = ((WORD*)data_in)[i];
748 } else {
749 for (i = 0; i < This->numfaces * 3; i++)
750 ((WORD*)data_out)[i] = ((DWORD*)data_in)[i];
752 } else {
753 memcpy(data_out, data_in, This->numfaces * 3 * (options & D3DXMESH_32BIT ? 4 : 2));
755 clone_mesh->lpVtbl->UnlockIndexBuffer(clone_mesh);
756 iface->lpVtbl->UnlockIndexBuffer(iface);
758 memcpy(cloned_this->attrib_buffer, This->attrib_buffer, This->numfaces * sizeof(*This->attrib_buffer));
760 if (This->attrib_table_size)
762 cloned_this->attrib_table_size = This->attrib_table_size;
763 cloned_this->attrib_table = HeapAlloc(GetProcessHeap(), 0, This->attrib_table_size * sizeof(*This->attrib_table));
764 if (!cloned_this->attrib_table) {
765 hr = E_OUTOFMEMORY;
766 goto error;
768 memcpy(cloned_this->attrib_table, This->attrib_table, This->attrib_table_size * sizeof(*This->attrib_table));
771 *clone_mesh_out = clone_mesh;
773 return D3D_OK;
774 error:
775 IUnknown_Release(clone_mesh);
776 return hr;
779 static HRESULT WINAPI d3dx9_mesh_GetVertexBuffer(struct ID3DXMesh *iface,
780 struct IDirect3DVertexBuffer9 **vertex_buffer)
782 struct d3dx9_mesh *mesh = impl_from_ID3DXMesh(iface);
784 TRACE("iface %p, vertex_buffer %p.\n", iface, vertex_buffer);
786 if (!vertex_buffer)
787 return D3DERR_INVALIDCALL;
788 *vertex_buffer = mesh->vertex_buffer;
789 IDirect3DVertexBuffer9_AddRef(mesh->vertex_buffer);
791 return D3D_OK;
794 static HRESULT WINAPI d3dx9_mesh_GetIndexBuffer(struct ID3DXMesh *iface,
795 struct IDirect3DIndexBuffer9 **index_buffer)
797 struct d3dx9_mesh *mesh = impl_from_ID3DXMesh(iface);
799 TRACE("iface %p, index_buffer %p.\n", iface, index_buffer);
801 if (!index_buffer)
802 return D3DERR_INVALIDCALL;
803 *index_buffer = mesh->index_buffer;
804 IDirect3DIndexBuffer9_AddRef(mesh->index_buffer);
806 return D3D_OK;
809 static HRESULT WINAPI d3dx9_mesh_LockVertexBuffer(ID3DXMesh *iface, DWORD flags, void **data)
811 struct d3dx9_mesh *mesh = impl_from_ID3DXMesh(iface);
813 TRACE("iface %p, flags %#x, data %p.\n", iface, flags, data);
815 return IDirect3DVertexBuffer9_Lock(mesh->vertex_buffer, 0, 0, data, flags);
818 static HRESULT WINAPI d3dx9_mesh_UnlockVertexBuffer(ID3DXMesh *iface)
820 struct d3dx9_mesh *mesh = impl_from_ID3DXMesh(iface);
822 TRACE("iface %p.\n", iface);
824 return IDirect3DVertexBuffer9_Unlock(mesh->vertex_buffer);
827 static HRESULT WINAPI d3dx9_mesh_LockIndexBuffer(ID3DXMesh *iface, DWORD flags, void **data)
829 struct d3dx9_mesh *mesh = impl_from_ID3DXMesh(iface);
831 TRACE("iface %p, flags %#x, data %p.\n", iface, flags, data);
833 return IDirect3DIndexBuffer9_Lock(mesh->index_buffer, 0, 0, data, flags);
836 static HRESULT WINAPI d3dx9_mesh_UnlockIndexBuffer(ID3DXMesh *iface)
838 struct d3dx9_mesh *mesh = impl_from_ID3DXMesh(iface);
840 TRACE("iface %p.\n", iface);
842 return IDirect3DIndexBuffer9_Unlock(mesh->index_buffer);
845 /* FIXME: This looks just wrong, we never check *attrib_table_size before
846 * copying the data. */
847 static HRESULT WINAPI d3dx9_mesh_GetAttributeTable(ID3DXMesh *iface,
848 D3DXATTRIBUTERANGE *attrib_table, DWORD *attrib_table_size)
850 struct d3dx9_mesh *mesh = impl_from_ID3DXMesh(iface);
852 TRACE("iface %p, attrib_table %p, attrib_table_size %p.\n",
853 iface, attrib_table, attrib_table_size);
855 if (attrib_table_size)
856 *attrib_table_size = mesh->attrib_table_size;
858 if (attrib_table)
859 memcpy(attrib_table, mesh->attrib_table, mesh->attrib_table_size * sizeof(*attrib_table));
861 return D3D_OK;
864 struct edge_face
866 struct list entry;
867 DWORD v2;
868 DWORD face;
871 struct edge_face_map
873 struct list *lists;
874 struct edge_face *entries;
877 /* Builds up a map of which face a new edge belongs to. That way the adjacency
878 * of another edge can be looked up. An edge has an adjacent face if there
879 * is an edge going in the opposite direction in the map. For example if the
880 * edge (v1, v2) belongs to face 4, and there is a mapping (v2, v1)->7, then
881 * face 4 and 7 are adjacent.
883 * Each edge might have been replaced with another edge, or none at all. There
884 * is at most one edge to face mapping, i.e. an edge can only belong to one
885 * face.
887 static HRESULT init_edge_face_map(struct edge_face_map *edge_face_map, const DWORD *index_buffer,
888 const DWORD *point_reps, DWORD num_faces)
890 DWORD face, edge;
891 DWORD i;
893 edge_face_map->lists = HeapAlloc(GetProcessHeap(), 0, 3 * num_faces * sizeof(*edge_face_map->lists));
894 if (!edge_face_map->lists) return E_OUTOFMEMORY;
896 edge_face_map->entries = HeapAlloc(GetProcessHeap(), 0, 3 * num_faces * sizeof(*edge_face_map->entries));
897 if (!edge_face_map->entries) return E_OUTOFMEMORY;
900 /* Initialize all lists */
901 for (i = 0; i < 3 * num_faces; i++)
903 list_init(&edge_face_map->lists[i]);
905 /* Build edge face mapping */
906 for (face = 0; face < num_faces; face++)
908 for (edge = 0; edge < 3; edge++)
910 DWORD v1 = index_buffer[3*face + edge];
911 DWORD v2 = index_buffer[3*face + (edge+1)%3];
912 DWORD new_v1 = point_reps[v1]; /* What v1 has been replaced with */
913 DWORD new_v2 = point_reps[v2];
915 if (v1 != v2) /* Only map non-collapsed edges */
917 i = 3*face + edge;
918 edge_face_map->entries[i].v2 = new_v2;
919 edge_face_map->entries[i].face = face;
920 list_add_head(&edge_face_map->lists[new_v1], &edge_face_map->entries[i].entry);
925 return D3D_OK;
928 static DWORD find_adjacent_face(struct edge_face_map *edge_face_map, DWORD vertex1, DWORD vertex2, DWORD num_faces)
930 struct edge_face *edge_face_ptr;
932 LIST_FOR_EACH_ENTRY(edge_face_ptr, &edge_face_map->lists[vertex2], struct edge_face, entry)
934 if (edge_face_ptr->v2 == vertex1)
935 return edge_face_ptr->face;
938 return -1;
941 static DWORD *generate_identity_point_reps(DWORD num_vertices)
943 DWORD *id_point_reps;
944 DWORD i;
946 id_point_reps = HeapAlloc(GetProcessHeap(), 0, num_vertices * sizeof(*id_point_reps));
947 if (!id_point_reps)
948 return NULL;
950 for (i = 0; i < num_vertices; i++)
952 id_point_reps[i] = i;
955 return id_point_reps;
958 static HRESULT WINAPI d3dx9_mesh_ConvertPointRepsToAdjacency(ID3DXMesh *iface,
959 const DWORD *point_reps, DWORD *adjacency)
961 HRESULT hr;
962 DWORD num_faces = iface->lpVtbl->GetNumFaces(iface);
963 DWORD num_vertices = iface->lpVtbl->GetNumVertices(iface);
964 DWORD options = iface->lpVtbl->GetOptions(iface);
965 BOOL indices_are_16_bit = !(options & D3DXMESH_32BIT);
966 DWORD *ib = NULL;
967 void *ib_ptr = NULL;
968 DWORD face;
969 DWORD edge;
970 struct edge_face_map edge_face_map = {0};
971 const DWORD *point_reps_ptr = NULL;
972 DWORD *id_point_reps = NULL;
974 TRACE("iface %p, point_reps %p, adjacency %p.\n", iface, point_reps, adjacency);
976 if (!adjacency) return D3DERR_INVALIDCALL;
978 if (!point_reps) /* Identity point reps */
980 id_point_reps = generate_identity_point_reps(num_vertices);
981 if (!id_point_reps)
983 hr = E_OUTOFMEMORY;
984 goto cleanup;
987 point_reps_ptr = id_point_reps;
989 else
991 point_reps_ptr = point_reps;
994 hr = iface->lpVtbl->LockIndexBuffer(iface, D3DLOCK_READONLY, &ib_ptr);
995 if (FAILED(hr)) goto cleanup;
997 if (indices_are_16_bit)
999 /* Widen 16 bit to 32 bit */
1000 DWORD i;
1001 WORD *ib_16bit = ib_ptr;
1002 ib = HeapAlloc(GetProcessHeap(), 0, 3 * num_faces * sizeof(DWORD));
1003 if (!ib)
1005 hr = E_OUTOFMEMORY;
1006 goto cleanup;
1008 for (i = 0; i < 3 * num_faces; i++)
1010 ib[i] = ib_16bit[i];
1013 else
1015 ib = ib_ptr;
1018 hr = init_edge_face_map(&edge_face_map, ib, point_reps_ptr, num_faces);
1019 if (FAILED(hr)) goto cleanup;
1021 /* Create adjacency */
1022 for (face = 0; face < num_faces; face++)
1024 for (edge = 0; edge < 3; edge++)
1026 DWORD v1 = ib[3*face + edge];
1027 DWORD v2 = ib[3*face + (edge+1)%3];
1028 DWORD new_v1 = point_reps_ptr[v1];
1029 DWORD new_v2 = point_reps_ptr[v2];
1030 DWORD adj_face;
1032 adj_face = find_adjacent_face(&edge_face_map, new_v1, new_v2, num_faces);
1033 adjacency[3*face + edge] = adj_face;
1037 hr = D3D_OK;
1038 cleanup:
1039 HeapFree(GetProcessHeap(), 0, id_point_reps);
1040 if (indices_are_16_bit) HeapFree(GetProcessHeap(), 0, ib);
1041 HeapFree(GetProcessHeap(), 0, edge_face_map.lists);
1042 HeapFree(GetProcessHeap(), 0, edge_face_map.entries);
1043 if(ib_ptr) iface->lpVtbl->UnlockIndexBuffer(iface);
1044 return hr;
1047 /* ConvertAdjacencyToPointReps helper function.
1049 * Goes around the edges of each face and replaces the vertices in any adjacent
1050 * face's edge with its own vertices(if its vertices have a lower index). This
1051 * way as few as possible low index vertices are shared among the faces. The
1052 * re-ordered index buffer is stored in new_indices.
1054 * The vertices in a point representation must be ordered sequentially, e.g.
1055 * index 5 holds the index of the vertex that replaces vertex 5, i.e. if
1056 * vertex 5 is replaced by vertex 3 then index 5 would contain 3. If no vertex
1057 * replaces it, then it contains the same number as the index itself, e.g.
1058 * index 5 would contain 5. */
1059 static HRESULT propagate_face_vertices(const DWORD *adjacency, DWORD *point_reps,
1060 const DWORD *indices, DWORD *new_indices, DWORD face, DWORD numfaces)
1062 const unsigned int VERTS_PER_FACE = 3;
1063 DWORD edge, opp_edge;
1064 DWORD face_base = VERTS_PER_FACE * face;
1066 for (edge = 0; edge < VERTS_PER_FACE; edge++)
1068 DWORD adj_face = adjacency[face_base + edge];
1069 DWORD adj_face_base;
1070 DWORD i;
1071 if (adj_face == -1) /* No adjacent face. */
1072 continue;
1073 else if (adj_face >= numfaces)
1075 /* This throws exception on Windows */
1076 WARN("Index out of bounds. Got %d expected less than %d.\n",
1077 adj_face, numfaces);
1078 return D3DERR_INVALIDCALL;
1080 adj_face_base = 3 * adj_face;
1082 /* Find opposite edge in adjacent face. */
1083 for (opp_edge = 0; opp_edge < VERTS_PER_FACE; opp_edge++)
1085 DWORD opp_edge_index = adj_face_base + opp_edge;
1086 if (adjacency[opp_edge_index] == face)
1087 break; /* Found opposite edge. */
1090 /* Replaces vertices in opposite edge with vertices from current edge. */
1091 for (i = 0; i < 2; i++)
1093 DWORD from = face_base + (edge + (1 - i)) % VERTS_PER_FACE;
1094 DWORD to = adj_face_base + (opp_edge + i) % VERTS_PER_FACE;
1096 /* Propagate lowest index. */
1097 if (new_indices[to] > new_indices[from])
1099 new_indices[to] = new_indices[from];
1100 point_reps[indices[to]] = new_indices[from];
1105 return D3D_OK;
1108 static HRESULT WINAPI d3dx9_mesh_ConvertAdjacencyToPointReps(ID3DXMesh *iface,
1109 const DWORD *adjacency, DWORD *point_reps)
1111 struct d3dx9_mesh *This = impl_from_ID3DXMesh(iface);
1112 HRESULT hr;
1113 DWORD face;
1114 DWORD i;
1115 DWORD *indices = NULL;
1116 WORD *indices_16bit = NULL;
1117 DWORD *new_indices = NULL;
1118 const unsigned int VERTS_PER_FACE = 3;
1120 TRACE("iface %p, adjacency %p, point_reps %p.\n", iface, adjacency, point_reps);
1122 if (!adjacency)
1124 WARN("NULL adjacency.\n");
1125 hr = D3DERR_INVALIDCALL;
1126 goto cleanup;
1129 if (!point_reps)
1131 WARN("NULL point_reps.\n");
1132 hr = D3DERR_INVALIDCALL;
1133 goto cleanup;
1136 /* Should never happen as CreateMesh does not allow meshes with 0 faces */
1137 if (This->numfaces == 0)
1139 ERR("Number of faces was zero.\n");
1140 hr = D3DERR_INVALIDCALL;
1141 goto cleanup;
1144 new_indices = HeapAlloc(GetProcessHeap(), 0, VERTS_PER_FACE * This->numfaces * sizeof(*indices));
1145 if (!new_indices)
1147 hr = E_OUTOFMEMORY;
1148 goto cleanup;
1151 if (This->options & D3DXMESH_32BIT)
1153 hr = iface->lpVtbl->LockIndexBuffer(iface, D3DLOCK_READONLY, (void**)&indices);
1154 if (FAILED(hr)) goto cleanup;
1155 memcpy(new_indices, indices, VERTS_PER_FACE * This->numfaces * sizeof(*indices));
1157 else
1159 /* Make a widening copy of indices_16bit into indices and new_indices
1160 * in order to re-use the helper function */
1161 hr = iface->lpVtbl->LockIndexBuffer(iface, D3DLOCK_READONLY, (void**)&indices_16bit);
1162 if (FAILED(hr)) goto cleanup;
1163 indices = HeapAlloc(GetProcessHeap(), 0, VERTS_PER_FACE * This->numfaces * sizeof(*indices));
1164 if (!indices)
1166 hr = E_OUTOFMEMORY;
1167 goto cleanup;
1169 for (i = 0; i < VERTS_PER_FACE * This->numfaces; i++)
1171 new_indices[i] = indices_16bit[i];
1172 indices[i] = indices_16bit[i];
1176 /* Vertices are ordered sequentially in the point representation. */
1177 for (i = 0; i < This->numvertices; i++)
1179 point_reps[i] = i;
1182 /* Propagate vertices with low indices so as few vertices as possible
1183 * are used in the mesh.
1185 for (face = 0; face < This->numfaces; face++)
1187 hr = propagate_face_vertices(adjacency, point_reps, indices, new_indices, face, This->numfaces);
1188 if (FAILED(hr)) goto cleanup;
1190 /* Go in opposite direction to catch all face orderings */
1191 for (face = 0; face < This->numfaces; face++)
1193 hr = propagate_face_vertices(adjacency, point_reps,
1194 indices, new_indices,
1195 (This->numfaces - 1) - face, This->numfaces);
1196 if (FAILED(hr)) goto cleanup;
1199 hr = D3D_OK;
1200 cleanup:
1201 if (This->options & D3DXMESH_32BIT)
1203 if (indices) iface->lpVtbl->UnlockIndexBuffer(iface);
1205 else
1207 if (indices_16bit) iface->lpVtbl->UnlockIndexBuffer(iface);
1208 HeapFree(GetProcessHeap(), 0, indices);
1210 HeapFree(GetProcessHeap(), 0, new_indices);
1211 return hr;
1214 struct vertex_metadata {
1215 float key;
1216 DWORD vertex_index;
1217 DWORD first_shared_index;
1220 static int compare_vertex_keys(const void *a, const void *b)
1222 const struct vertex_metadata *left = a;
1223 const struct vertex_metadata *right = b;
1224 if (left->key == right->key)
1225 return 0;
1226 return left->key < right->key ? -1 : 1;
1229 static HRESULT WINAPI d3dx9_mesh_GenerateAdjacency(ID3DXMesh *iface, float epsilon, DWORD *adjacency)
1231 struct d3dx9_mesh *This = impl_from_ID3DXMesh(iface);
1232 HRESULT hr;
1233 BYTE *vertices = NULL;
1234 const DWORD *indices = NULL;
1235 DWORD vertex_size;
1236 DWORD buffer_size;
1237 /* sort the vertices by (x + y + z) to quickly find coincident vertices */
1238 struct vertex_metadata *sorted_vertices;
1239 /* shared_indices links together identical indices in the index buffer so
1240 * that adjacency checks can be limited to faces sharing a vertex */
1241 DWORD *shared_indices = NULL;
1242 const FLOAT epsilon_sq = epsilon * epsilon;
1243 DWORD i;
1245 TRACE("iface %p, epsilon %.8e, adjacency %p.\n", iface, epsilon, adjacency);
1247 if (!adjacency)
1248 return D3DERR_INVALIDCALL;
1250 buffer_size = This->numfaces * 3 * sizeof(*shared_indices) + This->numvertices * sizeof(*sorted_vertices);
1251 if (!(This->options & D3DXMESH_32BIT))
1252 buffer_size += This->numfaces * 3 * sizeof(*indices);
1253 shared_indices = HeapAlloc(GetProcessHeap(), 0, buffer_size);
1254 if (!shared_indices)
1255 return E_OUTOFMEMORY;
1256 sorted_vertices = (struct vertex_metadata*)(shared_indices + This->numfaces * 3);
1258 hr = iface->lpVtbl->LockVertexBuffer(iface, D3DLOCK_READONLY, (void**)&vertices);
1259 if (FAILED(hr)) goto cleanup;
1260 hr = iface->lpVtbl->LockIndexBuffer(iface, D3DLOCK_READONLY, (void**)&indices);
1261 if (FAILED(hr)) goto cleanup;
1263 if (!(This->options & D3DXMESH_32BIT)) {
1264 const WORD *word_indices = (const WORD*)indices;
1265 DWORD *dword_indices = (DWORD*)(sorted_vertices + This->numvertices);
1266 indices = dword_indices;
1267 for (i = 0; i < This->numfaces * 3; i++)
1268 *dword_indices++ = *word_indices++;
1271 vertex_size = iface->lpVtbl->GetNumBytesPerVertex(iface);
1272 for (i = 0; i < This->numvertices; i++) {
1273 D3DXVECTOR3 *vertex = (D3DXVECTOR3*)(vertices + vertex_size * i);
1274 sorted_vertices[i].first_shared_index = -1;
1275 sorted_vertices[i].key = vertex->x + vertex->y + vertex->z;
1276 sorted_vertices[i].vertex_index = i;
1278 for (i = 0; i < This->numfaces * 3; i++) {
1279 DWORD *first_shared_index = &sorted_vertices[indices[i]].first_shared_index;
1280 shared_indices[i] = *first_shared_index;
1281 *first_shared_index = i;
1282 adjacency[i] = -1;
1284 qsort(sorted_vertices, This->numvertices, sizeof(*sorted_vertices), compare_vertex_keys);
1286 for (i = 0; i < This->numvertices; i++) {
1287 struct vertex_metadata *sorted_vertex_a = &sorted_vertices[i];
1288 D3DXVECTOR3 *vertex_a = (D3DXVECTOR3*)(vertices + sorted_vertex_a->vertex_index * vertex_size);
1289 DWORD shared_index_a = sorted_vertex_a->first_shared_index;
1291 while (shared_index_a != -1) {
1292 DWORD j = i;
1293 DWORD shared_index_b = shared_indices[shared_index_a];
1294 struct vertex_metadata *sorted_vertex_b = sorted_vertex_a;
1296 while (TRUE) {
1297 while (shared_index_b != -1) {
1298 /* faces are adjacent if they have another coincident vertex */
1299 DWORD base_a = (shared_index_a / 3) * 3;
1300 DWORD base_b = (shared_index_b / 3) * 3;
1301 BOOL adjacent = FALSE;
1302 int k;
1304 for (k = 0; k < 3; k++) {
1305 if (adjacency[base_b + k] == shared_index_a / 3) {
1306 adjacent = TRUE;
1307 break;
1310 if (!adjacent) {
1311 for (k = 1; k <= 2; k++) {
1312 DWORD vertex_index_a = base_a + (shared_index_a + k) % 3;
1313 DWORD vertex_index_b = base_b + (shared_index_b + (3 - k)) % 3;
1314 adjacent = indices[vertex_index_a] == indices[vertex_index_b];
1315 if (!adjacent && epsilon >= 0.0f) {
1316 D3DXVECTOR3 delta = {0.0f, 0.0f, 0.0f};
1317 FLOAT length_sq;
1319 D3DXVec3Subtract(&delta,
1320 (D3DXVECTOR3*)(vertices + indices[vertex_index_a] * vertex_size),
1321 (D3DXVECTOR3*)(vertices + indices[vertex_index_b] * vertex_size));
1322 length_sq = D3DXVec3LengthSq(&delta);
1323 adjacent = epsilon == 0.0f ? length_sq == 0.0f : length_sq < epsilon_sq;
1325 if (adjacent) {
1326 DWORD adj_a = base_a + 2 - (vertex_index_a + shared_index_a + 1) % 3;
1327 DWORD adj_b = base_b + 2 - (vertex_index_b + shared_index_b + 1) % 3;
1328 if (adjacency[adj_a] == -1 && adjacency[adj_b] == -1) {
1329 adjacency[adj_a] = base_b / 3;
1330 adjacency[adj_b] = base_a / 3;
1331 break;
1337 shared_index_b = shared_indices[shared_index_b];
1339 while (++j < This->numvertices) {
1340 D3DXVECTOR3 *vertex_b;
1342 sorted_vertex_b++;
1343 if (sorted_vertex_b->key - sorted_vertex_a->key > epsilon * 3.0f) {
1344 /* no more coincident vertices to try */
1345 j = This->numvertices;
1346 break;
1348 /* check for coincidence */
1349 vertex_b = (D3DXVECTOR3*)(vertices + sorted_vertex_b->vertex_index * vertex_size);
1350 if (fabsf(vertex_a->x - vertex_b->x) <= epsilon &&
1351 fabsf(vertex_a->y - vertex_b->y) <= epsilon &&
1352 fabsf(vertex_a->z - vertex_b->z) <= epsilon)
1354 break;
1357 if (j >= This->numvertices)
1358 break;
1359 shared_index_b = sorted_vertex_b->first_shared_index;
1362 sorted_vertex_a->first_shared_index = shared_indices[sorted_vertex_a->first_shared_index];
1363 shared_index_a = sorted_vertex_a->first_shared_index;
1367 hr = D3D_OK;
1368 cleanup:
1369 if (indices) iface->lpVtbl->UnlockIndexBuffer(iface);
1370 if (vertices) iface->lpVtbl->UnlockVertexBuffer(iface);
1371 HeapFree(GetProcessHeap(), 0, shared_indices);
1372 return hr;
1375 static HRESULT WINAPI d3dx9_mesh_UpdateSemantics(ID3DXMesh *iface, D3DVERTEXELEMENT9 declaration[MAX_FVF_DECL_SIZE])
1377 struct d3dx9_mesh *This = impl_from_ID3DXMesh(iface);
1378 HRESULT hr;
1379 UINT vertex_declaration_size;
1380 int i;
1382 TRACE("iface %p, declaration %p.\n", iface, declaration);
1384 if (!declaration)
1386 WARN("Invalid declaration. Can't use NULL declaration.\n");
1387 return D3DERR_INVALIDCALL;
1390 /* New declaration must be same size as original */
1391 vertex_declaration_size = D3DXGetDeclVertexSize(declaration, declaration[0].Stream);
1392 if (vertex_declaration_size != This->vertex_declaration_size)
1394 WARN("Invalid declaration. New vertex size does not match the original vertex size.\n");
1395 return D3DERR_INVALIDCALL;
1398 /* New declaration must not contain non-zero Stream value */
1399 for (i = 0; declaration[i].Stream != 0xff; i++)
1401 if (declaration[i].Stream != 0)
1403 WARN("Invalid declaration. New declaration contains non-zero Stream value.\n");
1404 return D3DERR_INVALIDCALL;
1408 This->num_elem = i + 1;
1409 copy_declaration(This->cached_declaration, declaration, This->num_elem);
1411 if (This->vertex_declaration)
1412 IDirect3DVertexDeclaration9_Release(This->vertex_declaration);
1414 /* An application can pass an invalid declaration to UpdateSemantics and
1415 * still expect D3D_OK (see tests). If the declaration is invalid, then
1416 * subsequent calls to DrawSubset will fail. This is handled by setting the
1417 * vertex declaration to NULL.
1418 * GetDeclaration, GetNumBytesPerVertex must, however, use the new
1419 * invalid declaration. This is handled by them using the cached vertex
1420 * declaration instead of the actual vertex declaration.
1422 hr = IDirect3DDevice9_CreateVertexDeclaration(This->device,
1423 declaration,
1424 &This->vertex_declaration);
1425 if (FAILED(hr))
1427 WARN("Using invalid declaration. Calls to DrawSubset will fail.\n");
1428 This->vertex_declaration = NULL;
1431 return D3D_OK;
1434 static HRESULT WINAPI d3dx9_mesh_LockAttributeBuffer(ID3DXMesh *iface, DWORD flags, DWORD **data)
1436 struct d3dx9_mesh *mesh = impl_from_ID3DXMesh(iface);
1438 TRACE("iface %p, flags %#x, data %p.\n", iface, flags, data);
1440 InterlockedIncrement(&mesh->attrib_buffer_lock_count);
1442 if (!(flags & D3DLOCK_READONLY))
1444 D3DXATTRIBUTERANGE *attrib_table = mesh->attrib_table;
1445 mesh->attrib_table_size = 0;
1446 mesh->attrib_table = NULL;
1447 HeapFree(GetProcessHeap(), 0, attrib_table);
1450 *data = mesh->attrib_buffer;
1452 return D3D_OK;
1455 static HRESULT WINAPI d3dx9_mesh_UnlockAttributeBuffer(ID3DXMesh *iface)
1457 struct d3dx9_mesh *mesh = impl_from_ID3DXMesh(iface);
1458 int lock_count;
1460 TRACE("iface %p.\n", iface);
1462 lock_count = InterlockedDecrement(&mesh->attrib_buffer_lock_count);
1463 if (lock_count < 0)
1465 InterlockedIncrement(&mesh->attrib_buffer_lock_count);
1466 return D3DERR_INVALIDCALL;
1469 return D3D_OK;
1472 static HRESULT WINAPI d3dx9_mesh_Optimize(ID3DXMesh *iface, DWORD flags, const DWORD *adjacency_in,
1473 DWORD *adjacency_out, DWORD *face_remap, ID3DXBuffer **vertex_remap, ID3DXMesh **opt_mesh)
1475 struct d3dx9_mesh *mesh = impl_from_ID3DXMesh(iface);
1476 HRESULT hr;
1477 D3DVERTEXELEMENT9 declaration[MAX_FVF_DECL_SIZE] = { D3DDECL_END() };
1478 ID3DXMesh *optimized_mesh;
1480 TRACE("iface %p, flags %#x, adjacency_in %p, adjacency_out %p, face_remap %p, vertex_remap %p, opt_mesh %p.\n",
1481 iface, flags, adjacency_in, adjacency_out, face_remap, vertex_remap, opt_mesh);
1483 if (!opt_mesh)
1484 return D3DERR_INVALIDCALL;
1486 hr = iface->lpVtbl->GetDeclaration(iface, declaration);
1487 if (FAILED(hr)) return hr;
1489 if (FAILED(hr = iface->lpVtbl->CloneMesh(iface, mesh->options, declaration, mesh->device, &optimized_mesh)))
1490 return hr;
1492 hr = optimized_mesh->lpVtbl->OptimizeInplace(optimized_mesh, flags, adjacency_in, adjacency_out, face_remap, vertex_remap);
1493 if (SUCCEEDED(hr))
1494 *opt_mesh = optimized_mesh;
1495 else
1496 IUnknown_Release(optimized_mesh);
1497 return hr;
1500 /* Creates a vertex_remap that removes unused vertices.
1501 * Indices are updated according to the vertex_remap. */
1502 static HRESULT compact_mesh(struct d3dx9_mesh *This, DWORD *indices,
1503 DWORD *new_num_vertices, ID3DXBuffer **vertex_remap)
1505 HRESULT hr;
1506 DWORD *vertex_remap_ptr;
1507 DWORD num_used_vertices;
1508 DWORD i;
1510 hr = D3DXCreateBuffer(This->numvertices * sizeof(DWORD), vertex_remap);
1511 if (FAILED(hr)) return hr;
1512 vertex_remap_ptr = ID3DXBuffer_GetBufferPointer(*vertex_remap);
1514 for (i = 0; i < This->numfaces * 3; i++)
1515 vertex_remap_ptr[indices[i]] = 1;
1517 /* create old->new vertex mapping */
1518 num_used_vertices = 0;
1519 for (i = 0; i < This->numvertices; i++) {
1520 if (vertex_remap_ptr[i])
1521 vertex_remap_ptr[i] = num_used_vertices++;
1522 else
1523 vertex_remap_ptr[i] = -1;
1525 /* convert indices */
1526 for (i = 0; i < This->numfaces * 3; i++)
1527 indices[i] = vertex_remap_ptr[indices[i]];
1529 /* create new->old vertex mapping */
1530 num_used_vertices = 0;
1531 for (i = 0; i < This->numvertices; i++) {
1532 if (vertex_remap_ptr[i] != -1)
1533 vertex_remap_ptr[num_used_vertices++] = i;
1535 for (i = num_used_vertices; i < This->numvertices; i++)
1536 vertex_remap_ptr[i] = -1;
1538 *new_num_vertices = num_used_vertices;
1540 return D3D_OK;
1543 /* count the number of unique attribute values in a sorted attribute buffer */
1544 static DWORD count_attributes(const DWORD *attrib_buffer, DWORD numfaces)
1546 DWORD last_attribute = attrib_buffer[0];
1547 DWORD attrib_table_size = 1;
1548 DWORD i;
1549 for (i = 1; i < numfaces; i++) {
1550 if (attrib_buffer[i] != last_attribute) {
1551 last_attribute = attrib_buffer[i];
1552 attrib_table_size++;
1555 return attrib_table_size;
1558 static void fill_attribute_table(DWORD *attrib_buffer, DWORD numfaces, void *indices,
1559 BOOL is_32bit_indices, D3DXATTRIBUTERANGE *attrib_table)
1561 DWORD attrib_table_size = 0;
1562 DWORD last_attribute = attrib_buffer[0];
1563 DWORD min_vertex, max_vertex;
1564 DWORD i;
1566 attrib_table[0].AttribId = last_attribute;
1567 attrib_table[0].FaceStart = 0;
1568 min_vertex = (DWORD)-1;
1569 max_vertex = 0;
1570 for (i = 0; i < numfaces; i++) {
1571 DWORD j;
1573 if (attrib_buffer[i] != last_attribute) {
1574 last_attribute = attrib_buffer[i];
1575 attrib_table[attrib_table_size].FaceCount = i - attrib_table[attrib_table_size].FaceStart;
1576 attrib_table[attrib_table_size].VertexStart = min_vertex;
1577 attrib_table[attrib_table_size].VertexCount = max_vertex - min_vertex + 1;
1578 attrib_table_size++;
1579 attrib_table[attrib_table_size].AttribId = attrib_buffer[i];
1580 attrib_table[attrib_table_size].FaceStart = i;
1581 min_vertex = (DWORD)-1;
1582 max_vertex = 0;
1584 for (j = 0; j < 3; j++) {
1585 DWORD vertex_index = is_32bit_indices ? ((DWORD*)indices)[i * 3 + j] : ((WORD*)indices)[i * 3 + j];
1586 if (vertex_index < min_vertex)
1587 min_vertex = vertex_index;
1588 if (vertex_index > max_vertex)
1589 max_vertex = vertex_index;
1592 attrib_table[attrib_table_size].FaceCount = i - attrib_table[attrib_table_size].FaceStart;
1593 attrib_table[attrib_table_size].VertexStart = min_vertex;
1594 attrib_table[attrib_table_size].VertexCount = max_vertex - min_vertex + 1;
1595 attrib_table_size++;
1598 static int attrib_entry_compare(const DWORD **a, const DWORD **b)
1600 const DWORD *ptr_a = *a;
1601 const DWORD *ptr_b = *b;
1602 int delta = *ptr_a - *ptr_b;
1604 if (delta)
1605 return delta;
1607 delta = ptr_a - ptr_b; /* for stable sort */
1608 return delta;
1611 /* Create face_remap, a new attribute buffer for attribute sort optimization. */
1612 static HRESULT remap_faces_for_attrsort(struct d3dx9_mesh *This, const DWORD *indices,
1613 DWORD *attrib_buffer, DWORD **sorted_attrib_buffer, DWORD **face_remap)
1615 DWORD **sorted_attrib_ptr_buffer = NULL;
1616 DWORD i;
1618 sorted_attrib_ptr_buffer = HeapAlloc(GetProcessHeap(), 0, This->numfaces * sizeof(*sorted_attrib_ptr_buffer));
1619 if (!sorted_attrib_ptr_buffer)
1620 return E_OUTOFMEMORY;
1622 *face_remap = HeapAlloc(GetProcessHeap(), 0, This->numfaces * sizeof(**face_remap));
1623 if (!*face_remap)
1625 HeapFree(GetProcessHeap(), 0, sorted_attrib_ptr_buffer);
1626 return E_OUTOFMEMORY;
1629 for (i = 0; i < This->numfaces; i++)
1630 sorted_attrib_ptr_buffer[i] = &attrib_buffer[i];
1631 qsort(sorted_attrib_ptr_buffer, This->numfaces, sizeof(*sorted_attrib_ptr_buffer),
1632 (int(*)(const void *, const void *))attrib_entry_compare);
1634 for (i = 0; i < This->numfaces; i++)
1636 DWORD old_face = sorted_attrib_ptr_buffer[i] - attrib_buffer;
1637 (*face_remap)[old_face] = i;
1640 /* overwrite sorted_attrib_ptr_buffer with the values themselves */
1641 *sorted_attrib_buffer = (DWORD*)sorted_attrib_ptr_buffer;
1642 for (i = 0; i < This->numfaces; i++)
1643 (*sorted_attrib_buffer)[(*face_remap)[i]] = attrib_buffer[i];
1645 return D3D_OK;
1648 static HRESULT WINAPI d3dx9_mesh_OptimizeInplace(ID3DXMesh *iface, DWORD flags, const DWORD *adjacency_in,
1649 DWORD *adjacency_out, DWORD *face_remap_out, ID3DXBuffer **vertex_remap_out)
1651 struct d3dx9_mesh *This = impl_from_ID3DXMesh(iface);
1652 void *indices = NULL;
1653 DWORD *attrib_buffer = NULL;
1654 HRESULT hr;
1655 ID3DXBuffer *vertex_remap = NULL;
1656 DWORD *face_remap = NULL; /* old -> new mapping */
1657 DWORD *dword_indices = NULL;
1658 DWORD new_num_vertices = 0;
1659 DWORD new_num_alloc_vertices = 0;
1660 IDirect3DVertexBuffer9 *vertex_buffer = NULL;
1661 DWORD *sorted_attrib_buffer = NULL;
1662 DWORD i;
1664 TRACE("iface %p, flags %#x, adjacency_in %p, adjacency_out %p, face_remap_out %p, vertex_remap_out %p.\n",
1665 iface, flags, adjacency_in, adjacency_out, face_remap_out, vertex_remap_out);
1667 if (!flags)
1668 return D3DERR_INVALIDCALL;
1669 if (!adjacency_in && (flags & (D3DXMESHOPT_VERTEXCACHE | D3DXMESHOPT_STRIPREORDER)))
1670 return D3DERR_INVALIDCALL;
1671 if ((flags & (D3DXMESHOPT_VERTEXCACHE | D3DXMESHOPT_STRIPREORDER)) == (D3DXMESHOPT_VERTEXCACHE | D3DXMESHOPT_STRIPREORDER))
1672 return D3DERR_INVALIDCALL;
1674 if (flags & (D3DXMESHOPT_VERTEXCACHE | D3DXMESHOPT_STRIPREORDER))
1676 if (flags & D3DXMESHOPT_VERTEXCACHE)
1677 FIXME("D3DXMESHOPT_VERTEXCACHE not implemented.\n");
1678 if (flags & D3DXMESHOPT_STRIPREORDER)
1679 FIXME("D3DXMESHOPT_STRIPREORDER not implemented.\n");
1680 return E_NOTIMPL;
1683 hr = iface->lpVtbl->LockIndexBuffer(iface, 0, &indices);
1684 if (FAILED(hr)) goto cleanup;
1686 dword_indices = HeapAlloc(GetProcessHeap(), 0, This->numfaces * 3 * sizeof(DWORD));
1687 if (!dword_indices) return E_OUTOFMEMORY;
1688 if (This->options & D3DXMESH_32BIT) {
1689 memcpy(dword_indices, indices, This->numfaces * 3 * sizeof(DWORD));
1690 } else {
1691 WORD *word_indices = indices;
1692 for (i = 0; i < This->numfaces * 3; i++)
1693 dword_indices[i] = *word_indices++;
1696 if ((flags & (D3DXMESHOPT_COMPACT | D3DXMESHOPT_IGNOREVERTS | D3DXMESHOPT_ATTRSORT)) == D3DXMESHOPT_COMPACT)
1698 new_num_alloc_vertices = This->numvertices;
1699 hr = compact_mesh(This, dword_indices, &new_num_vertices, &vertex_remap);
1700 if (FAILED(hr)) goto cleanup;
1701 } else if (flags & D3DXMESHOPT_ATTRSORT) {
1702 if (!(flags & D3DXMESHOPT_IGNOREVERTS))
1704 FIXME("D3DXMESHOPT_ATTRSORT vertex reordering not implemented.\n");
1705 hr = E_NOTIMPL;
1706 goto cleanup;
1709 hr = iface->lpVtbl->LockAttributeBuffer(iface, 0, &attrib_buffer);
1710 if (FAILED(hr)) goto cleanup;
1712 hr = remap_faces_for_attrsort(This, dword_indices, attrib_buffer, &sorted_attrib_buffer, &face_remap);
1713 if (FAILED(hr)) goto cleanup;
1716 if (vertex_remap)
1718 /* reorder the vertices using vertex_remap */
1719 D3DVERTEXBUFFER_DESC vertex_desc;
1720 DWORD *vertex_remap_ptr = ID3DXBuffer_GetBufferPointer(vertex_remap);
1721 DWORD vertex_size = iface->lpVtbl->GetNumBytesPerVertex(iface);
1722 BYTE *orig_vertices;
1723 BYTE *new_vertices;
1725 hr = IDirect3DVertexBuffer9_GetDesc(This->vertex_buffer, &vertex_desc);
1726 if (FAILED(hr)) goto cleanup;
1728 hr = IDirect3DDevice9_CreateVertexBuffer(This->device, new_num_alloc_vertices * vertex_size,
1729 vertex_desc.Usage, This->fvf, vertex_desc.Pool, &vertex_buffer, NULL);
1730 if (FAILED(hr)) goto cleanup;
1732 hr = IDirect3DVertexBuffer9_Lock(This->vertex_buffer, 0, 0, (void**)&orig_vertices, D3DLOCK_READONLY);
1733 if (FAILED(hr)) goto cleanup;
1735 hr = IDirect3DVertexBuffer9_Lock(vertex_buffer, 0, 0, (void**)&new_vertices, 0);
1736 if (FAILED(hr)) {
1737 IDirect3DVertexBuffer9_Unlock(This->vertex_buffer);
1738 goto cleanup;
1741 for (i = 0; i < new_num_vertices; i++)
1742 memcpy(new_vertices + i * vertex_size, orig_vertices + vertex_remap_ptr[i] * vertex_size, vertex_size);
1744 IDirect3DVertexBuffer9_Unlock(This->vertex_buffer);
1745 IDirect3DVertexBuffer9_Unlock(vertex_buffer);
1746 } else if (vertex_remap_out) {
1747 DWORD *vertex_remap_ptr;
1749 hr = D3DXCreateBuffer(This->numvertices * sizeof(DWORD), &vertex_remap);
1750 if (FAILED(hr)) goto cleanup;
1751 vertex_remap_ptr = ID3DXBuffer_GetBufferPointer(vertex_remap);
1752 for (i = 0; i < This->numvertices; i++)
1753 *vertex_remap_ptr++ = i;
1756 if (flags & D3DXMESHOPT_ATTRSORT)
1758 D3DXATTRIBUTERANGE *attrib_table;
1759 DWORD attrib_table_size;
1761 attrib_table_size = count_attributes(sorted_attrib_buffer, This->numfaces);
1762 attrib_table = HeapAlloc(GetProcessHeap(), 0, attrib_table_size * sizeof(*attrib_table));
1763 if (!attrib_table) {
1764 hr = E_OUTOFMEMORY;
1765 goto cleanup;
1768 memcpy(attrib_buffer, sorted_attrib_buffer, This->numfaces * sizeof(*attrib_buffer));
1770 /* reorder the indices using face_remap */
1771 if (This->options & D3DXMESH_32BIT) {
1772 for (i = 0; i < This->numfaces; i++)
1773 memcpy((DWORD*)indices + face_remap[i] * 3, dword_indices + i * 3, 3 * sizeof(DWORD));
1774 } else {
1775 WORD *word_indices = indices;
1776 for (i = 0; i < This->numfaces; i++) {
1777 DWORD new_pos = face_remap[i] * 3;
1778 DWORD old_pos = i * 3;
1779 word_indices[new_pos++] = dword_indices[old_pos++];
1780 word_indices[new_pos++] = dword_indices[old_pos++];
1781 word_indices[new_pos] = dword_indices[old_pos];
1785 fill_attribute_table(attrib_buffer, This->numfaces, indices,
1786 This->options & D3DXMESH_32BIT, attrib_table);
1788 HeapFree(GetProcessHeap(), 0, This->attrib_table);
1789 This->attrib_table = attrib_table;
1790 This->attrib_table_size = attrib_table_size;
1791 } else {
1792 if (This->options & D3DXMESH_32BIT) {
1793 memcpy(indices, dword_indices, This->numfaces * 3 * sizeof(DWORD));
1794 } else {
1795 WORD *word_indices = indices;
1796 for (i = 0; i < This->numfaces * 3; i++)
1797 *word_indices++ = dword_indices[i];
1801 if (adjacency_out) {
1802 if (face_remap) {
1803 for (i = 0; i < This->numfaces; i++) {
1804 DWORD old_pos = i * 3;
1805 DWORD new_pos = face_remap[i] * 3;
1806 adjacency_out[new_pos++] = face_remap[adjacency_in[old_pos++]];
1807 adjacency_out[new_pos++] = face_remap[adjacency_in[old_pos++]];
1808 adjacency_out[new_pos++] = face_remap[adjacency_in[old_pos++]];
1810 } else {
1811 memcpy(adjacency_out, adjacency_in, This->numfaces * 3 * sizeof(*adjacency_out));
1814 if (face_remap_out) {
1815 if (face_remap) {
1816 for (i = 0; i < This->numfaces; i++)
1817 face_remap_out[face_remap[i]] = i;
1818 } else {
1819 for (i = 0; i < This->numfaces; i++)
1820 face_remap_out[i] = i;
1823 if (vertex_remap_out)
1824 *vertex_remap_out = vertex_remap;
1825 vertex_remap = NULL;
1827 if (vertex_buffer) {
1828 IDirect3DVertexBuffer9_Release(This->vertex_buffer);
1829 This->vertex_buffer = vertex_buffer;
1830 vertex_buffer = NULL;
1831 This->numvertices = new_num_vertices;
1834 hr = D3D_OK;
1835 cleanup:
1836 HeapFree(GetProcessHeap(), 0, sorted_attrib_buffer);
1837 HeapFree(GetProcessHeap(), 0, face_remap);
1838 HeapFree(GetProcessHeap(), 0, dword_indices);
1839 if (vertex_remap) ID3DXBuffer_Release(vertex_remap);
1840 if (vertex_buffer) IDirect3DVertexBuffer9_Release(vertex_buffer);
1841 if (attrib_buffer) iface->lpVtbl->UnlockAttributeBuffer(iface);
1842 if (indices) iface->lpVtbl->UnlockIndexBuffer(iface);
1843 return hr;
1846 static HRESULT WINAPI d3dx9_mesh_SetAttributeTable(ID3DXMesh *iface,
1847 const D3DXATTRIBUTERANGE *attrib_table, DWORD attrib_table_size)
1849 struct d3dx9_mesh *mesh = impl_from_ID3DXMesh(iface);
1850 D3DXATTRIBUTERANGE *new_table = NULL;
1852 TRACE("iface %p, attrib_table %p, attrib_table_size %u.\n", iface, attrib_table, attrib_table_size);
1854 if (attrib_table_size) {
1855 size_t size = attrib_table_size * sizeof(*attrib_table);
1857 new_table = HeapAlloc(GetProcessHeap(), 0, size);
1858 if (!new_table)
1859 return E_OUTOFMEMORY;
1861 CopyMemory(new_table, attrib_table, size);
1862 } else if (attrib_table) {
1863 return D3DERR_INVALIDCALL;
1865 HeapFree(GetProcessHeap(), 0, mesh->attrib_table);
1866 mesh->attrib_table = new_table;
1867 mesh->attrib_table_size = attrib_table_size;
1869 return D3D_OK;
1872 static const struct ID3DXMeshVtbl D3DXMesh_Vtbl =
1874 d3dx9_mesh_QueryInterface,
1875 d3dx9_mesh_AddRef,
1876 d3dx9_mesh_Release,
1877 d3dx9_mesh_DrawSubset,
1878 d3dx9_mesh_GetNumFaces,
1879 d3dx9_mesh_GetNumVertices,
1880 d3dx9_mesh_GetFVF,
1881 d3dx9_mesh_GetDeclaration,
1882 d3dx9_mesh_GetNumBytesPerVertex,
1883 d3dx9_mesh_GetOptions,
1884 d3dx9_mesh_GetDevice,
1885 d3dx9_mesh_CloneMeshFVF,
1886 d3dx9_mesh_CloneMesh,
1887 d3dx9_mesh_GetVertexBuffer,
1888 d3dx9_mesh_GetIndexBuffer,
1889 d3dx9_mesh_LockVertexBuffer,
1890 d3dx9_mesh_UnlockVertexBuffer,
1891 d3dx9_mesh_LockIndexBuffer,
1892 d3dx9_mesh_UnlockIndexBuffer,
1893 d3dx9_mesh_GetAttributeTable,
1894 d3dx9_mesh_ConvertPointRepsToAdjacency,
1895 d3dx9_mesh_ConvertAdjacencyToPointReps,
1896 d3dx9_mesh_GenerateAdjacency,
1897 d3dx9_mesh_UpdateSemantics,
1898 d3dx9_mesh_LockAttributeBuffer,
1899 d3dx9_mesh_UnlockAttributeBuffer,
1900 d3dx9_mesh_Optimize,
1901 d3dx9_mesh_OptimizeInplace,
1902 d3dx9_mesh_SetAttributeTable,
1906 /* Algorithm taken from the article: An Efficient and Robust Ray-Box Intersection Algorithm
1907 Amy Williams University of Utah
1908 Steve Barrus University of Utah
1909 R. Keith Morley University of Utah
1910 Peter Shirley University of Utah
1912 International Conference on Computer Graphics and Interactive Techniques archive
1913 ACM SIGGRAPH 2005 Courses
1914 Los Angeles, California
1916 This algorithm is free of patents or of copyrights, as confirmed by Peter Shirley himself.
1918 Algorithm: Consider the box as the intersection of three slabs. Clip the ray
1919 against each slab, if there's anything left of the ray after we're
1920 done we've got an intersection of the ray with the box. */
1921 BOOL WINAPI D3DXBoxBoundProbe(const D3DXVECTOR3 *pmin, const D3DXVECTOR3 *pmax,
1922 const D3DXVECTOR3 *prayposition, const D3DXVECTOR3 *praydirection)
1924 FLOAT div, tmin, tmax, tymin, tymax, tzmin, tzmax;
1926 div = 1.0f / praydirection->x;
1927 if ( div >= 0.0f )
1929 tmin = ( pmin->x - prayposition->x ) * div;
1930 tmax = ( pmax->x - prayposition->x ) * div;
1932 else
1934 tmin = ( pmax->x - prayposition->x ) * div;
1935 tmax = ( pmin->x - prayposition->x ) * div;
1938 if ( tmax < 0.0f ) return FALSE;
1940 div = 1.0f / praydirection->y;
1941 if ( div >= 0.0f )
1943 tymin = ( pmin->y - prayposition->y ) * div;
1944 tymax = ( pmax->y - prayposition->y ) * div;
1946 else
1948 tymin = ( pmax->y - prayposition->y ) * div;
1949 tymax = ( pmin->y - prayposition->y ) * div;
1952 if ( ( tymax < 0.0f ) || ( tmin > tymax ) || ( tymin > tmax ) ) return FALSE;
1954 if ( tymin > tmin ) tmin = tymin;
1955 if ( tymax < tmax ) tmax = tymax;
1957 div = 1.0f / praydirection->z;
1958 if ( div >= 0.0f )
1960 tzmin = ( pmin->z - prayposition->z ) * div;
1961 tzmax = ( pmax->z - prayposition->z ) * div;
1963 else
1965 tzmin = ( pmax->z - prayposition->z ) * div;
1966 tzmax = ( pmin->z - prayposition->z ) * div;
1969 if ( (tzmax < 0.0f ) || ( tmin > tzmax ) || ( tzmin > tmax ) ) return FALSE;
1971 return TRUE;
1974 HRESULT WINAPI D3DXComputeBoundingBox(const D3DXVECTOR3 *pfirstposition,
1975 DWORD numvertices, DWORD dwstride, D3DXVECTOR3 *pmin, D3DXVECTOR3 *pmax)
1977 D3DXVECTOR3 vec;
1978 unsigned int i;
1980 if( !pfirstposition || !pmin || !pmax ) return D3DERR_INVALIDCALL;
1982 *pmin = *pfirstposition;
1983 *pmax = *pmin;
1985 for(i=0; i<numvertices; i++)
1987 vec = *( (const D3DXVECTOR3*)((const char*)pfirstposition + dwstride * i) );
1989 if ( vec.x < pmin->x ) pmin->x = vec.x;
1990 if ( vec.x > pmax->x ) pmax->x = vec.x;
1992 if ( vec.y < pmin->y ) pmin->y = vec.y;
1993 if ( vec.y > pmax->y ) pmax->y = vec.y;
1995 if ( vec.z < pmin->z ) pmin->z = vec.z;
1996 if ( vec.z > pmax->z ) pmax->z = vec.z;
1999 return D3D_OK;
2002 HRESULT WINAPI D3DXComputeBoundingSphere(const D3DXVECTOR3 *pfirstposition,
2003 DWORD numvertices, DWORD dwstride, D3DXVECTOR3 *pcenter, float *pradius)
2005 D3DXVECTOR3 temp;
2006 FLOAT d;
2007 unsigned int i;
2009 if( !pfirstposition || !pcenter || !pradius ) return D3DERR_INVALIDCALL;
2011 temp.x = 0.0f;
2012 temp.y = 0.0f;
2013 temp.z = 0.0f;
2014 *pradius = 0.0f;
2016 for(i=0; i<numvertices; i++)
2017 D3DXVec3Add(&temp, &temp, (const D3DXVECTOR3*)((const char*)pfirstposition + dwstride * i));
2019 D3DXVec3Scale(pcenter, &temp, 1.0f / numvertices);
2021 for(i=0; i<numvertices; i++)
2023 d = D3DXVec3Length(D3DXVec3Subtract(&temp, (const D3DXVECTOR3*)((const char*)pfirstposition + dwstride * i), pcenter));
2024 if ( d > *pradius ) *pradius = d;
2026 return D3D_OK;
2029 static void append_decl_element(D3DVERTEXELEMENT9 *declaration, UINT *idx, UINT *offset,
2030 D3DDECLTYPE type, D3DDECLUSAGE usage, UINT usage_idx)
2032 declaration[*idx].Stream = 0;
2033 declaration[*idx].Offset = *offset;
2034 declaration[*idx].Type = type;
2035 declaration[*idx].Method = D3DDECLMETHOD_DEFAULT;
2036 declaration[*idx].Usage = usage;
2037 declaration[*idx].UsageIndex = usage_idx;
2039 *offset += d3dx_decltype_size[type];
2040 ++(*idx);
2043 /*************************************************************************
2044 * D3DXDeclaratorFromFVF
2046 HRESULT WINAPI D3DXDeclaratorFromFVF(DWORD fvf, D3DVERTEXELEMENT9 declaration[MAX_FVF_DECL_SIZE])
2048 static const D3DVERTEXELEMENT9 end_element = D3DDECL_END();
2049 DWORD tex_count = (fvf & D3DFVF_TEXCOUNT_MASK) >> D3DFVF_TEXCOUNT_SHIFT;
2050 unsigned int offset = 0;
2051 unsigned int idx = 0;
2052 unsigned int i;
2054 TRACE("fvf %#x, declaration %p.\n", fvf, declaration);
2056 if (fvf & (D3DFVF_RESERVED0 | D3DFVF_RESERVED2)) return D3DERR_INVALIDCALL;
2058 if (fvf & D3DFVF_POSITION_MASK)
2060 BOOL has_blend = (fvf & D3DFVF_XYZB5) >= D3DFVF_XYZB1;
2061 DWORD blend_count = 1 + (((fvf & D3DFVF_XYZB5) - D3DFVF_XYZB1) >> 1);
2062 BOOL has_blend_idx = (fvf & D3DFVF_LASTBETA_D3DCOLOR) || (fvf & D3DFVF_LASTBETA_UBYTE4);
2064 if (has_blend_idx) --blend_count;
2066 if ((fvf & D3DFVF_POSITION_MASK) == D3DFVF_XYZW
2067 || (has_blend && blend_count > 4))
2068 return D3DERR_INVALIDCALL;
2070 if ((fvf & D3DFVF_POSITION_MASK) == D3DFVF_XYZRHW)
2071 append_decl_element(declaration, &idx, &offset, D3DDECLTYPE_FLOAT4, D3DDECLUSAGE_POSITIONT, 0);
2072 else
2073 append_decl_element(declaration, &idx, &offset, D3DDECLTYPE_FLOAT3, D3DDECLUSAGE_POSITION, 0);
2075 if (has_blend)
2077 switch (blend_count)
2079 case 0:
2080 break;
2081 case 1:
2082 append_decl_element(declaration, &idx, &offset, D3DDECLTYPE_FLOAT1, D3DDECLUSAGE_BLENDWEIGHT, 0);
2083 break;
2084 case 2:
2085 append_decl_element(declaration, &idx, &offset, D3DDECLTYPE_FLOAT2, D3DDECLUSAGE_BLENDWEIGHT, 0);
2086 break;
2087 case 3:
2088 append_decl_element(declaration, &idx, &offset, D3DDECLTYPE_FLOAT3, D3DDECLUSAGE_BLENDWEIGHT, 0);
2089 break;
2090 case 4:
2091 append_decl_element(declaration, &idx, &offset, D3DDECLTYPE_FLOAT4, D3DDECLUSAGE_BLENDWEIGHT, 0);
2092 break;
2093 default:
2094 ERR("Invalid blend count %u.\n", blend_count);
2095 break;
2098 if (has_blend_idx)
2100 if (fvf & D3DFVF_LASTBETA_UBYTE4)
2101 append_decl_element(declaration, &idx, &offset, D3DDECLTYPE_UBYTE4, D3DDECLUSAGE_BLENDINDICES, 0);
2102 else if (fvf & D3DFVF_LASTBETA_D3DCOLOR)
2103 append_decl_element(declaration, &idx, &offset, D3DDECLTYPE_D3DCOLOR, D3DDECLUSAGE_BLENDINDICES, 0);
2108 if (fvf & D3DFVF_NORMAL)
2109 append_decl_element(declaration, &idx, &offset, D3DDECLTYPE_FLOAT3, D3DDECLUSAGE_NORMAL, 0);
2110 if (fvf & D3DFVF_PSIZE)
2111 append_decl_element(declaration, &idx, &offset, D3DDECLTYPE_FLOAT1, D3DDECLUSAGE_PSIZE, 0);
2112 if (fvf & D3DFVF_DIFFUSE)
2113 append_decl_element(declaration, &idx, &offset, D3DDECLTYPE_D3DCOLOR, D3DDECLUSAGE_COLOR, 0);
2114 if (fvf & D3DFVF_SPECULAR)
2115 append_decl_element(declaration, &idx, &offset, D3DDECLTYPE_D3DCOLOR, D3DDECLUSAGE_COLOR, 1);
2117 for (i = 0; i < tex_count; ++i)
2119 switch ((fvf >> (16 + 2 * i)) & 0x03)
2121 case D3DFVF_TEXTUREFORMAT1:
2122 append_decl_element(declaration, &idx, &offset, D3DDECLTYPE_FLOAT1, D3DDECLUSAGE_TEXCOORD, i);
2123 break;
2124 case D3DFVF_TEXTUREFORMAT2:
2125 append_decl_element(declaration, &idx, &offset, D3DDECLTYPE_FLOAT2, D3DDECLUSAGE_TEXCOORD, i);
2126 break;
2127 case D3DFVF_TEXTUREFORMAT3:
2128 append_decl_element(declaration, &idx, &offset, D3DDECLTYPE_FLOAT3, D3DDECLUSAGE_TEXCOORD, i);
2129 break;
2130 case D3DFVF_TEXTUREFORMAT4:
2131 append_decl_element(declaration, &idx, &offset, D3DDECLTYPE_FLOAT4, D3DDECLUSAGE_TEXCOORD, i);
2132 break;
2136 declaration[idx] = end_element;
2138 return D3D_OK;
2141 /*************************************************************************
2142 * D3DXFVFFromDeclarator
2144 HRESULT WINAPI D3DXFVFFromDeclarator(const D3DVERTEXELEMENT9 *declaration, DWORD *fvf)
2146 unsigned int i = 0, texture, offset;
2148 TRACE("(%p, %p)\n", declaration, fvf);
2150 *fvf = 0;
2151 if (declaration[0].Type == D3DDECLTYPE_FLOAT3 && declaration[0].Usage == D3DDECLUSAGE_POSITION)
2153 if ((declaration[1].Type == D3DDECLTYPE_FLOAT4 && declaration[1].Usage == D3DDECLUSAGE_BLENDWEIGHT &&
2154 declaration[1].UsageIndex == 0) &&
2155 (declaration[2].Type == D3DDECLTYPE_FLOAT1 && declaration[2].Usage == D3DDECLUSAGE_BLENDINDICES &&
2156 declaration[2].UsageIndex == 0))
2158 return D3DERR_INVALIDCALL;
2160 else if ((declaration[1].Type == D3DDECLTYPE_UBYTE4 || declaration[1].Type == D3DDECLTYPE_D3DCOLOR) &&
2161 declaration[1].Usage == D3DDECLUSAGE_BLENDINDICES && declaration[1].UsageIndex == 0)
2163 if (declaration[1].Type == D3DDECLTYPE_UBYTE4)
2165 *fvf |= D3DFVF_XYZB1 | D3DFVF_LASTBETA_UBYTE4;
2167 else
2169 *fvf |= D3DFVF_XYZB1 | D3DFVF_LASTBETA_D3DCOLOR;
2171 i = 2;
2173 else if (declaration[1].Type <= D3DDECLTYPE_FLOAT4 && declaration[1].Usage == D3DDECLUSAGE_BLENDWEIGHT &&
2174 declaration[1].UsageIndex == 0)
2176 if ((declaration[2].Type == D3DDECLTYPE_UBYTE4 || declaration[2].Type == D3DDECLTYPE_D3DCOLOR) &&
2177 declaration[2].Usage == D3DDECLUSAGE_BLENDINDICES && declaration[2].UsageIndex == 0)
2179 if (declaration[2].Type == D3DDECLTYPE_UBYTE4)
2181 *fvf |= D3DFVF_LASTBETA_UBYTE4;
2183 else
2185 *fvf |= D3DFVF_LASTBETA_D3DCOLOR;
2187 switch (declaration[1].Type)
2189 case D3DDECLTYPE_FLOAT1: *fvf |= D3DFVF_XYZB2; break;
2190 case D3DDECLTYPE_FLOAT2: *fvf |= D3DFVF_XYZB3; break;
2191 case D3DDECLTYPE_FLOAT3: *fvf |= D3DFVF_XYZB4; break;
2192 case D3DDECLTYPE_FLOAT4: *fvf |= D3DFVF_XYZB5; break;
2194 i = 3;
2196 else
2198 switch (declaration[1].Type)
2200 case D3DDECLTYPE_FLOAT1: *fvf |= D3DFVF_XYZB1; break;
2201 case D3DDECLTYPE_FLOAT2: *fvf |= D3DFVF_XYZB2; break;
2202 case D3DDECLTYPE_FLOAT3: *fvf |= D3DFVF_XYZB3; break;
2203 case D3DDECLTYPE_FLOAT4: *fvf |= D3DFVF_XYZB4; break;
2205 i = 2;
2208 else
2210 *fvf |= D3DFVF_XYZ;
2211 i = 1;
2214 else if (declaration[0].Type == D3DDECLTYPE_FLOAT4 && declaration[0].Usage == D3DDECLUSAGE_POSITIONT &&
2215 declaration[0].UsageIndex == 0)
2217 *fvf |= D3DFVF_XYZRHW;
2218 i = 1;
2221 if (declaration[i].Type == D3DDECLTYPE_FLOAT3 && declaration[i].Usage == D3DDECLUSAGE_NORMAL)
2223 *fvf |= D3DFVF_NORMAL;
2224 i++;
2226 if (declaration[i].Type == D3DDECLTYPE_FLOAT1 && declaration[i].Usage == D3DDECLUSAGE_PSIZE &&
2227 declaration[i].UsageIndex == 0)
2229 *fvf |= D3DFVF_PSIZE;
2230 i++;
2232 if (declaration[i].Type == D3DDECLTYPE_D3DCOLOR && declaration[i].Usage == D3DDECLUSAGE_COLOR &&
2233 declaration[i].UsageIndex == 0)
2235 *fvf |= D3DFVF_DIFFUSE;
2236 i++;
2238 if (declaration[i].Type == D3DDECLTYPE_D3DCOLOR && declaration[i].Usage == D3DDECLUSAGE_COLOR &&
2239 declaration[i].UsageIndex == 1)
2241 *fvf |= D3DFVF_SPECULAR;
2242 i++;
2245 for (texture = 0; texture < D3DDP_MAXTEXCOORD; i++, texture++)
2247 if (declaration[i].Stream == 0xFF)
2249 break;
2251 else if (declaration[i].Type == D3DDECLTYPE_FLOAT1 && declaration[i].Usage == D3DDECLUSAGE_TEXCOORD &&
2252 declaration[i].UsageIndex == texture)
2254 *fvf |= D3DFVF_TEXCOORDSIZE1(declaration[i].UsageIndex);
2256 else if (declaration[i].Type == D3DDECLTYPE_FLOAT2 && declaration[i].Usage == D3DDECLUSAGE_TEXCOORD &&
2257 declaration[i].UsageIndex == texture)
2259 *fvf |= D3DFVF_TEXCOORDSIZE2(declaration[i].UsageIndex);
2261 else if (declaration[i].Type == D3DDECLTYPE_FLOAT3 && declaration[i].Usage == D3DDECLUSAGE_TEXCOORD &&
2262 declaration[i].UsageIndex == texture)
2264 *fvf |= D3DFVF_TEXCOORDSIZE3(declaration[i].UsageIndex);
2266 else if (declaration[i].Type == D3DDECLTYPE_FLOAT4 && declaration[i].Usage == D3DDECLUSAGE_TEXCOORD &&
2267 declaration[i].UsageIndex == texture)
2269 *fvf |= D3DFVF_TEXCOORDSIZE4(declaration[i].UsageIndex);
2271 else
2273 return D3DERR_INVALIDCALL;
2277 *fvf |= (texture << D3DFVF_TEXCOUNT_SHIFT);
2279 for (offset = 0, i = 0; declaration[i].Stream != 0xFF;
2280 offset += d3dx_decltype_size[declaration[i].Type], i++)
2282 if (declaration[i].Offset != offset)
2284 return D3DERR_INVALIDCALL;
2288 return D3D_OK;
2291 /*************************************************************************
2292 * D3DXGetFVFVertexSize
2294 static UINT Get_TexCoord_Size_From_FVF(DWORD FVF, int tex_num)
2296 return (((((FVF) >> (16 + (2 * (tex_num)))) + 1) & 0x03) + 1);
2299 UINT WINAPI D3DXGetFVFVertexSize(DWORD FVF)
2301 DWORD size = 0;
2302 UINT i;
2303 UINT numTextures = (FVF & D3DFVF_TEXCOUNT_MASK) >> D3DFVF_TEXCOUNT_SHIFT;
2305 if (FVF & D3DFVF_NORMAL) size += sizeof(D3DXVECTOR3);
2306 if (FVF & D3DFVF_DIFFUSE) size += sizeof(DWORD);
2307 if (FVF & D3DFVF_SPECULAR) size += sizeof(DWORD);
2308 if (FVF & D3DFVF_PSIZE) size += sizeof(DWORD);
2310 switch (FVF & D3DFVF_POSITION_MASK)
2312 case D3DFVF_XYZ: size += sizeof(D3DXVECTOR3); break;
2313 case D3DFVF_XYZRHW: size += 4 * sizeof(FLOAT); break;
2314 case D3DFVF_XYZB1: size += 4 * sizeof(FLOAT); break;
2315 case D3DFVF_XYZB2: size += 5 * sizeof(FLOAT); break;
2316 case D3DFVF_XYZB3: size += 6 * sizeof(FLOAT); break;
2317 case D3DFVF_XYZB4: size += 7 * sizeof(FLOAT); break;
2318 case D3DFVF_XYZB5: size += 8 * sizeof(FLOAT); break;
2319 case D3DFVF_XYZW: size += 4 * sizeof(FLOAT); break;
2322 for (i = 0; i < numTextures; i++)
2324 size += Get_TexCoord_Size_From_FVF(FVF, i) * sizeof(FLOAT);
2327 return size;
2330 /*************************************************************************
2331 * D3DXGetDeclVertexSize
2333 UINT WINAPI D3DXGetDeclVertexSize(const D3DVERTEXELEMENT9 *decl, DWORD stream_idx)
2335 const D3DVERTEXELEMENT9 *element;
2336 UINT size = 0;
2338 TRACE("decl %p, stream_idx %u\n", decl, stream_idx);
2340 if (!decl) return 0;
2342 for (element = decl; element->Stream != 0xff; ++element)
2344 UINT type_size;
2346 if (element->Stream != stream_idx) continue;
2348 if (element->Type >= sizeof(d3dx_decltype_size) / sizeof(*d3dx_decltype_size))
2350 FIXME("Unhandled element type %#x, size will be incorrect.\n", element->Type);
2351 continue;
2354 type_size = d3dx_decltype_size[element->Type];
2355 if (element->Offset + type_size > size) size = element->Offset + type_size;
2358 return size;
2361 /*************************************************************************
2362 * D3DXGetDeclLength
2364 UINT WINAPI D3DXGetDeclLength(const D3DVERTEXELEMENT9 *decl)
2366 const D3DVERTEXELEMENT9 *element;
2368 TRACE("decl %p\n", decl);
2370 /* null decl results in exception on Windows XP */
2372 for (element = decl; element->Stream != 0xff; ++element);
2374 return element - decl;
2377 BOOL WINAPI D3DXIntersectTri(const D3DXVECTOR3 *p0, const D3DXVECTOR3 *p1, const D3DXVECTOR3 *p2,
2378 const D3DXVECTOR3 *praypos, const D3DXVECTOR3 *praydir, float *pu, float *pv, float *pdist)
2380 D3DXMATRIX m;
2381 D3DXVECTOR4 vec;
2383 m.u.m[0][0] = p1->x - p0->x;
2384 m.u.m[1][0] = p2->x - p0->x;
2385 m.u.m[2][0] = -praydir->x;
2386 m.u.m[3][0] = 0.0f;
2387 m.u.m[0][1] = p1->y - p0->z;
2388 m.u.m[1][1] = p2->y - p0->z;
2389 m.u.m[2][1] = -praydir->y;
2390 m.u.m[3][1] = 0.0f;
2391 m.u.m[0][2] = p1->z - p0->z;
2392 m.u.m[1][2] = p2->z - p0->z;
2393 m.u.m[2][2] = -praydir->z;
2394 m.u.m[3][2] = 0.0f;
2395 m.u.m[0][3] = 0.0f;
2396 m.u.m[1][3] = 0.0f;
2397 m.u.m[2][3] = 0.0f;
2398 m.u.m[3][3] = 1.0f;
2400 vec.x = praypos->x - p0->x;
2401 vec.y = praypos->y - p0->y;
2402 vec.z = praypos->z - p0->z;
2403 vec.w = 0.0f;
2405 if ( D3DXMatrixInverse(&m, NULL, &m) )
2407 D3DXVec4Transform(&vec, &vec, &m);
2408 if ( (vec.x >= 0.0f) && (vec.y >= 0.0f) && (vec.x + vec.y <= 1.0f) && (vec.z >= 0.0f) )
2410 *pu = vec.x;
2411 *pv = vec.y;
2412 *pdist = fabsf( vec.z );
2413 return TRUE;
2417 return FALSE;
2420 BOOL WINAPI D3DXSphereBoundProbe(const D3DXVECTOR3 *pcenter, float radius,
2421 const D3DXVECTOR3 *prayposition, const D3DXVECTOR3 *praydirection)
2423 D3DXVECTOR3 difference;
2424 FLOAT a, b, c, d;
2426 a = D3DXVec3LengthSq(praydirection);
2427 if (!D3DXVec3Subtract(&difference, prayposition, pcenter)) return FALSE;
2428 b = D3DXVec3Dot(&difference, praydirection);
2429 c = D3DXVec3LengthSq(&difference) - radius * radius;
2430 d = b * b - a * c;
2432 if ( ( d <= 0.0f ) || ( sqrt(d) <= b ) ) return FALSE;
2433 return TRUE;
2436 /*************************************************************************
2437 * D3DXCreateMesh
2439 HRESULT WINAPI D3DXCreateMesh(DWORD numfaces, DWORD numvertices, DWORD options,
2440 const D3DVERTEXELEMENT9 *declaration, struct IDirect3DDevice9 *device, struct ID3DXMesh **mesh)
2442 HRESULT hr;
2443 DWORD fvf;
2444 IDirect3DVertexDeclaration9 *vertex_declaration;
2445 UINT vertex_declaration_size;
2446 UINT num_elem;
2447 IDirect3DVertexBuffer9 *vertex_buffer;
2448 IDirect3DIndexBuffer9 *index_buffer;
2449 DWORD *attrib_buffer;
2450 struct d3dx9_mesh *object;
2451 DWORD index_usage = 0;
2452 D3DPOOL index_pool = D3DPOOL_DEFAULT;
2453 D3DFORMAT index_format = D3DFMT_INDEX16;
2454 DWORD vertex_usage = 0;
2455 D3DPOOL vertex_pool = D3DPOOL_DEFAULT;
2456 int i;
2458 TRACE("numfaces %u, numvertices %u, options %#x, declaration %p, device %p, mesh %p.\n",
2459 numfaces, numvertices, options, declaration, device, mesh);
2461 if (numfaces == 0 || numvertices == 0 || declaration == NULL || device == NULL || mesh == NULL ||
2462 /* D3DXMESH_VB_SHARE is for cloning, and D3DXMESH_USEHWONLY is for ConvertToBlendedMesh */
2463 (options & (D3DXMESH_VB_SHARE | D3DXMESH_USEHWONLY | 0xfffe0000)))
2465 return D3DERR_INVALIDCALL;
2467 for (i = 0; declaration[i].Stream != 0xff; i++)
2468 if (declaration[i].Stream != 0)
2469 return D3DERR_INVALIDCALL;
2470 num_elem = i + 1;
2472 if (options & D3DXMESH_32BIT)
2473 index_format = D3DFMT_INDEX32;
2475 if (options & D3DXMESH_DONOTCLIP) {
2476 index_usage |= D3DUSAGE_DONOTCLIP;
2477 vertex_usage |= D3DUSAGE_DONOTCLIP;
2479 if (options & D3DXMESH_POINTS) {
2480 index_usage |= D3DUSAGE_POINTS;
2481 vertex_usage |= D3DUSAGE_POINTS;
2483 if (options & D3DXMESH_RTPATCHES) {
2484 index_usage |= D3DUSAGE_RTPATCHES;
2485 vertex_usage |= D3DUSAGE_RTPATCHES;
2487 if (options & D3DXMESH_NPATCHES) {
2488 index_usage |= D3DUSAGE_NPATCHES;
2489 vertex_usage |= D3DUSAGE_NPATCHES;
2492 if (options & D3DXMESH_VB_SYSTEMMEM)
2493 vertex_pool = D3DPOOL_SYSTEMMEM;
2494 else if (options & D3DXMESH_VB_MANAGED)
2495 vertex_pool = D3DPOOL_MANAGED;
2497 if (options & D3DXMESH_VB_WRITEONLY)
2498 vertex_usage |= D3DUSAGE_WRITEONLY;
2499 if (options & D3DXMESH_VB_DYNAMIC)
2500 vertex_usage |= D3DUSAGE_DYNAMIC;
2501 if (options & D3DXMESH_VB_SOFTWAREPROCESSING)
2502 vertex_usage |= D3DUSAGE_SOFTWAREPROCESSING;
2504 if (options & D3DXMESH_IB_SYSTEMMEM)
2505 index_pool = D3DPOOL_SYSTEMMEM;
2506 else if (options & D3DXMESH_IB_MANAGED)
2507 index_pool = D3DPOOL_MANAGED;
2509 if (options & D3DXMESH_IB_WRITEONLY)
2510 index_usage |= D3DUSAGE_WRITEONLY;
2511 if (options & D3DXMESH_IB_DYNAMIC)
2512 index_usage |= D3DUSAGE_DYNAMIC;
2513 if (options & D3DXMESH_IB_SOFTWAREPROCESSING)
2514 index_usage |= D3DUSAGE_SOFTWAREPROCESSING;
2516 hr = D3DXFVFFromDeclarator(declaration, &fvf);
2517 if (hr != D3D_OK)
2519 fvf = 0;
2522 /* Create vertex declaration */
2523 hr = IDirect3DDevice9_CreateVertexDeclaration(device,
2524 declaration,
2525 &vertex_declaration);
2526 if (FAILED(hr))
2528 WARN("Unexpected return value %x from IDirect3DDevice9_CreateVertexDeclaration.\n",hr);
2529 return hr;
2531 vertex_declaration_size = D3DXGetDeclVertexSize(declaration, declaration[0].Stream);
2533 /* Create vertex buffer */
2534 hr = IDirect3DDevice9_CreateVertexBuffer(device,
2535 numvertices * vertex_declaration_size,
2536 vertex_usage,
2537 fvf,
2538 vertex_pool,
2539 &vertex_buffer,
2540 NULL);
2541 if (FAILED(hr))
2543 WARN("Unexpected return value %x from IDirect3DDevice9_CreateVertexBuffer.\n",hr);
2544 IDirect3DVertexDeclaration9_Release(vertex_declaration);
2545 return hr;
2548 /* Create index buffer */
2549 hr = IDirect3DDevice9_CreateIndexBuffer(device,
2550 numfaces * 3 * ((index_format == D3DFMT_INDEX16) ? 2 : 4),
2551 index_usage,
2552 index_format,
2553 index_pool,
2554 &index_buffer,
2555 NULL);
2556 if (FAILED(hr))
2558 WARN("Unexpected return value %x from IDirect3DDevice9_CreateVertexBuffer.\n",hr);
2559 IDirect3DVertexBuffer9_Release(vertex_buffer);
2560 IDirect3DVertexDeclaration9_Release(vertex_declaration);
2561 return hr;
2564 attrib_buffer = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, numfaces * sizeof(*attrib_buffer));
2565 object = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(*object));
2566 if (object == NULL || attrib_buffer == NULL)
2568 HeapFree(GetProcessHeap(), 0, object);
2569 HeapFree(GetProcessHeap(), 0, attrib_buffer);
2570 IDirect3DIndexBuffer9_Release(index_buffer);
2571 IDirect3DVertexBuffer9_Release(vertex_buffer);
2572 IDirect3DVertexDeclaration9_Release(vertex_declaration);
2573 *mesh = NULL;
2574 return E_OUTOFMEMORY;
2576 object->ID3DXMesh_iface.lpVtbl = &D3DXMesh_Vtbl;
2577 object->ref = 1;
2579 object->numfaces = numfaces;
2580 object->numvertices = numvertices;
2581 object->options = options;
2582 object->fvf = fvf;
2583 object->device = device;
2584 IDirect3DDevice9_AddRef(device);
2586 copy_declaration(object->cached_declaration, declaration, num_elem);
2587 object->vertex_declaration = vertex_declaration;
2588 object->vertex_declaration_size = vertex_declaration_size;
2589 object->num_elem = num_elem;
2590 object->vertex_buffer = vertex_buffer;
2591 object->index_buffer = index_buffer;
2592 object->attrib_buffer = attrib_buffer;
2594 *mesh = &object->ID3DXMesh_iface;
2596 return D3D_OK;
2599 /*************************************************************************
2600 * D3DXCreateMeshFVF
2602 HRESULT WINAPI D3DXCreateMeshFVF(DWORD numfaces, DWORD numvertices, DWORD options,
2603 DWORD fvf, struct IDirect3DDevice9 *device, struct ID3DXMesh **mesh)
2605 HRESULT hr;
2606 D3DVERTEXELEMENT9 declaration[MAX_FVF_DECL_SIZE];
2608 TRACE("(%u, %u, %u, %u, %p, %p)\n", numfaces, numvertices, options, fvf, device, mesh);
2610 hr = D3DXDeclaratorFromFVF(fvf, declaration);
2611 if (FAILED(hr)) return hr;
2613 return D3DXCreateMesh(numfaces, numvertices, options, declaration, device, mesh);
2617 struct mesh_data {
2618 DWORD num_vertices;
2619 DWORD num_poly_faces;
2620 DWORD num_tri_faces;
2621 D3DXVECTOR3 *vertices;
2622 DWORD *num_tri_per_face;
2623 DWORD *indices;
2625 DWORD fvf;
2627 /* optional mesh data */
2629 DWORD num_normals;
2630 D3DXVECTOR3 *normals;
2631 DWORD *normal_indices;
2633 D3DXVECTOR2 *tex_coords;
2635 DWORD *vertex_colors;
2637 DWORD num_materials;
2638 D3DXMATERIAL *materials;
2639 DWORD *material_indices;
2641 struct ID3DXSkinInfo *skin_info;
2642 DWORD nb_bones;
2645 static HRESULT parse_texture_filename(ID3DXFileData *filedata, char **filename_out)
2647 HRESULT hr;
2648 SIZE_T data_size;
2649 BYTE *data;
2650 char *filename_in;
2651 char *filename = NULL;
2653 /* template TextureFilename {
2654 * STRING filename;
2658 HeapFree(GetProcessHeap(), 0, *filename_out);
2659 *filename_out = NULL;
2661 hr = filedata->lpVtbl->Lock(filedata, &data_size, (const void**)&data);
2662 if (FAILED(hr)) return hr;
2664 /* FIXME: String must be retrieved directly instead of through a pointer once ID3DXFILE is fixed */
2665 if (data_size < sizeof(filename_in))
2667 WARN("truncated data (%lu bytes)\n", data_size);
2668 filedata->lpVtbl->Unlock(filedata);
2669 return E_FAIL;
2671 filename_in = *(char **)data;
2673 filename = HeapAlloc(GetProcessHeap(), 0, strlen(filename_in) + 1);
2674 if (!filename) {
2675 filedata->lpVtbl->Unlock(filedata);
2676 return E_OUTOFMEMORY;
2679 strcpy(filename, filename_in);
2680 *filename_out = filename;
2682 filedata->lpVtbl->Unlock(filedata);
2684 return D3D_OK;
2687 static HRESULT parse_material(ID3DXFileData *filedata, D3DXMATERIAL *material)
2689 HRESULT hr;
2690 SIZE_T data_size;
2691 const BYTE *data;
2692 GUID type;
2693 ID3DXFileData *child;
2694 SIZE_T i, nb_children;
2696 material->pTextureFilename = NULL;
2698 hr = filedata->lpVtbl->Lock(filedata, &data_size, (const void**)&data);
2699 if (FAILED(hr)) return hr;
2702 * template ColorRGBA {
2703 * FLOAT red;
2704 * FLOAT green;
2705 * FLOAT blue;
2706 * FLOAT alpha;
2708 * template ColorRGB {
2709 * FLOAT red;
2710 * FLOAT green;
2711 * FLOAT blue;
2713 * template Material {
2714 * ColorRGBA faceColor;
2715 * FLOAT power;
2716 * ColorRGB specularColor;
2717 * ColorRGB emissiveColor;
2718 * [ ... ]
2721 if (data_size != sizeof(FLOAT) * 11) {
2722 WARN("incorrect data size (%ld bytes)\n", data_size);
2723 filedata->lpVtbl->Unlock(filedata);
2724 return E_FAIL;
2727 memcpy(&material->MatD3D.Diffuse, data, sizeof(D3DCOLORVALUE));
2728 data += sizeof(D3DCOLORVALUE);
2729 material->MatD3D.Power = *(FLOAT*)data;
2730 data += sizeof(FLOAT);
2731 memcpy(&material->MatD3D.Specular, data, sizeof(FLOAT) * 3);
2732 material->MatD3D.Specular.a = 1.0f;
2733 data += 3 * sizeof(FLOAT);
2734 memcpy(&material->MatD3D.Emissive, data, sizeof(FLOAT) * 3);
2735 material->MatD3D.Emissive.a = 1.0f;
2736 material->MatD3D.Ambient.r = 0.0f;
2737 material->MatD3D.Ambient.g = 0.0f;
2738 material->MatD3D.Ambient.b = 0.0f;
2739 material->MatD3D.Ambient.a = 1.0f;
2741 filedata->lpVtbl->Unlock(filedata);
2743 hr = filedata->lpVtbl->GetChildren(filedata, &nb_children);
2744 if (FAILED(hr))
2745 return hr;
2747 for (i = 0; i < nb_children; i++)
2749 hr = filedata->lpVtbl->GetChild(filedata, i, &child);
2750 if (FAILED(hr))
2751 return hr;
2752 hr = child->lpVtbl->GetType(child, &type);
2753 if (FAILED(hr))
2754 goto err;
2756 if (IsEqualGUID(&type, &TID_D3DRMTextureFilename)) {
2757 hr = parse_texture_filename(child, &material->pTextureFilename);
2758 if (FAILED(hr))
2759 goto err;
2761 IUnknown_Release(child);
2763 return D3D_OK;
2765 err:
2766 IUnknown_Release(child);
2767 return hr;
2770 static void destroy_materials(struct mesh_data *mesh)
2772 DWORD i;
2773 for (i = 0; i < mesh->num_materials; i++)
2774 HeapFree(GetProcessHeap(), 0, mesh->materials[i].pTextureFilename);
2775 HeapFree(GetProcessHeap(), 0, mesh->materials);
2776 HeapFree(GetProcessHeap(), 0, mesh->material_indices);
2777 mesh->num_materials = 0;
2778 mesh->materials = NULL;
2779 mesh->material_indices = NULL;
2782 static HRESULT parse_material_list(ID3DXFileData *filedata, struct mesh_data *mesh)
2784 HRESULT hr;
2785 SIZE_T data_size;
2786 const DWORD *data, *in_ptr;
2787 GUID type;
2788 ID3DXFileData *child = NULL;
2789 DWORD num_materials;
2790 DWORD i;
2791 SIZE_T nb_children;
2793 destroy_materials(mesh);
2795 hr = filedata->lpVtbl->Lock(filedata, &data_size, (const void**)&data);
2796 if (FAILED(hr)) return hr;
2798 /* template MeshMaterialList {
2799 * DWORD nMaterials;
2800 * DWORD nFaceIndexes;
2801 * array DWORD faceIndexes[nFaceIndexes];
2802 * [ Material ]
2806 in_ptr = data;
2807 hr = E_FAIL;
2809 if (data_size < sizeof(DWORD)) {
2810 WARN("truncated data (%ld bytes)\n", data_size);
2811 goto end;
2813 num_materials = *in_ptr++;
2814 if (!num_materials) {
2815 hr = D3D_OK;
2816 goto end;
2819 if (data_size < 2 * sizeof(DWORD)) {
2820 WARN("truncated data (%ld bytes)\n", data_size);
2821 goto end;
2823 if (*in_ptr++ != mesh->num_poly_faces) {
2824 WARN("number of material face indices (%u) doesn't match number of faces (%u)\n",
2825 *(in_ptr - 1), mesh->num_poly_faces);
2826 goto end;
2828 if (data_size < 2 * sizeof(DWORD) + mesh->num_poly_faces * sizeof(DWORD)) {
2829 WARN("truncated data (%ld bytes)\n", data_size);
2830 goto end;
2832 for (i = 0; i < mesh->num_poly_faces; i++) {
2833 if (*in_ptr++ >= num_materials) {
2834 WARN("face %u: reference to undefined material %u (only %u materials)\n",
2835 i, *(in_ptr - 1), num_materials);
2836 goto end;
2840 mesh->materials = HeapAlloc(GetProcessHeap(), 0, num_materials * sizeof(*mesh->materials));
2841 mesh->material_indices = HeapAlloc(GetProcessHeap(), 0, mesh->num_poly_faces * sizeof(*mesh->material_indices));
2842 if (!mesh->materials || !mesh->material_indices) {
2843 hr = E_OUTOFMEMORY;
2844 goto end;
2846 memcpy(mesh->material_indices, data + 2, mesh->num_poly_faces * sizeof(DWORD));
2848 hr = filedata->lpVtbl->GetChildren(filedata, &nb_children);
2849 if (FAILED(hr))
2850 goto end;
2852 for (i = 0; i < nb_children; i++)
2854 hr = filedata->lpVtbl->GetChild(filedata, i, &child);
2855 if (FAILED(hr))
2856 goto end;
2857 hr = child->lpVtbl->GetType(child, &type);
2858 if (FAILED(hr))
2859 goto end;
2861 if (IsEqualGUID(&type, &TID_D3DRMMaterial)) {
2862 if (mesh->num_materials >= num_materials) {
2863 WARN("more materials defined than declared\n");
2864 hr = E_FAIL;
2865 goto end;
2867 hr = parse_material(child, &mesh->materials[mesh->num_materials++]);
2868 if (FAILED(hr))
2869 goto end;
2872 IUnknown_Release(child);
2873 child = NULL;
2875 if (num_materials != mesh->num_materials) {
2876 WARN("only %u of %u materials defined\n", num_materials, mesh->num_materials);
2877 hr = E_FAIL;
2880 end:
2881 if (child)
2882 IUnknown_Release(child);
2883 filedata->lpVtbl->Unlock(filedata);
2884 return hr;
2887 static HRESULT parse_texture_coords(ID3DXFileData *filedata, struct mesh_data *mesh)
2889 HRESULT hr;
2890 SIZE_T data_size;
2891 const BYTE *data;
2893 HeapFree(GetProcessHeap(), 0, mesh->tex_coords);
2894 mesh->tex_coords = NULL;
2896 hr = filedata->lpVtbl->Lock(filedata, &data_size, (const void**)&data);
2897 if (FAILED(hr)) return hr;
2899 /* template Coords2d {
2900 * FLOAT u;
2901 * FLOAT v;
2903 * template MeshTextureCoords {
2904 * DWORD nTextureCoords;
2905 * array Coords2d textureCoords[nTextureCoords];
2909 hr = E_FAIL;
2911 if (data_size < sizeof(DWORD)) {
2912 WARN("truncated data (%ld bytes)\n", data_size);
2913 goto end;
2915 if (*(DWORD*)data != mesh->num_vertices) {
2916 WARN("number of texture coordinates (%u) doesn't match number of vertices (%u)\n",
2917 *(DWORD*)data, mesh->num_vertices);
2918 goto end;
2920 data += sizeof(DWORD);
2921 if (data_size < sizeof(DWORD) + mesh->num_vertices * sizeof(*mesh->tex_coords)) {
2922 WARN("truncated data (%ld bytes)\n", data_size);
2923 goto end;
2926 mesh->tex_coords = HeapAlloc(GetProcessHeap(), 0, mesh->num_vertices * sizeof(*mesh->tex_coords));
2927 if (!mesh->tex_coords) {
2928 hr = E_OUTOFMEMORY;
2929 goto end;
2931 memcpy(mesh->tex_coords, data, mesh->num_vertices * sizeof(*mesh->tex_coords));
2933 mesh->fvf |= D3DFVF_TEX1;
2935 hr = D3D_OK;
2937 end:
2938 filedata->lpVtbl->Unlock(filedata);
2939 return hr;
2942 static HRESULT parse_vertex_colors(ID3DXFileData *filedata, struct mesh_data *mesh)
2944 HRESULT hr;
2945 SIZE_T data_size;
2946 const BYTE *data;
2947 DWORD num_colors;
2948 DWORD i;
2950 HeapFree(GetProcessHeap(), 0, mesh->vertex_colors);
2951 mesh->vertex_colors = NULL;
2953 hr = filedata->lpVtbl->Lock(filedata, &data_size, (const void**)&data);
2954 if (FAILED(hr)) return hr;
2956 /* template IndexedColor {
2957 * DWORD index;
2958 * ColorRGBA indexColor;
2960 * template MeshVertexColors {
2961 * DWORD nVertexColors;
2962 * array IndexedColor vertexColors[nVertexColors];
2966 hr = E_FAIL;
2968 if (data_size < sizeof(DWORD)) {
2969 WARN("truncated data (%ld bytes)\n", data_size);
2970 goto end;
2972 num_colors = *(DWORD*)data;
2973 data += sizeof(DWORD);
2974 if (data_size < sizeof(DWORD) + num_colors * (sizeof(DWORD) + sizeof(D3DCOLORVALUE))) {
2975 WARN("truncated data (%ld bytes)\n", data_size);
2976 goto end;
2979 mesh->vertex_colors = HeapAlloc(GetProcessHeap(), 0, mesh->num_vertices * sizeof(DWORD));
2980 if (!mesh->vertex_colors) {
2981 hr = E_OUTOFMEMORY;
2982 goto end;
2985 for (i = 0; i < mesh->num_vertices; i++)
2986 mesh->vertex_colors[i] = D3DCOLOR_ARGB(0, 0xff, 0xff, 0xff);
2987 for (i = 0; i < num_colors; i++)
2989 D3DCOLORVALUE color;
2990 DWORD index = *(DWORD*)data;
2991 data += sizeof(DWORD);
2992 if (index >= mesh->num_vertices) {
2993 WARN("vertex color %u references undefined vertex %u (only %u vertices)\n",
2994 i, index, mesh->num_vertices);
2995 goto end;
2997 memcpy(&color, data, sizeof(color));
2998 data += sizeof(color);
2999 color.r = min(1.0f, max(0.0f, color.r));
3000 color.g = min(1.0f, max(0.0f, color.g));
3001 color.b = min(1.0f, max(0.0f, color.b));
3002 color.a = min(1.0f, max(0.0f, color.a));
3003 mesh->vertex_colors[index] = D3DCOLOR_ARGB((BYTE)(color.a * 255.0f + 0.5f),
3004 (BYTE)(color.r * 255.0f + 0.5f),
3005 (BYTE)(color.g * 255.0f + 0.5f),
3006 (BYTE)(color.b * 255.0f + 0.5f));
3009 mesh->fvf |= D3DFVF_DIFFUSE;
3011 hr = D3D_OK;
3013 end:
3014 filedata->lpVtbl->Unlock(filedata);
3015 return hr;
3018 static HRESULT parse_normals(ID3DXFileData *filedata, struct mesh_data *mesh)
3020 HRESULT hr;
3021 SIZE_T data_size;
3022 const BYTE *data;
3023 DWORD *index_out_ptr;
3024 DWORD i;
3025 DWORD num_face_indices = mesh->num_poly_faces * 2 + mesh->num_tri_faces;
3027 HeapFree(GetProcessHeap(), 0, mesh->normals);
3028 mesh->num_normals = 0;
3029 mesh->normals = NULL;
3030 mesh->normal_indices = NULL;
3031 mesh->fvf |= D3DFVF_NORMAL;
3033 hr = filedata->lpVtbl->Lock(filedata, &data_size, (const void**)&data);
3034 if (FAILED(hr)) return hr;
3036 /* template Vector {
3037 * FLOAT x;
3038 * FLOAT y;
3039 * FLOAT z;
3041 * template MeshFace {
3042 * DWORD nFaceVertexIndices;
3043 * array DWORD faceVertexIndices[nFaceVertexIndices];
3045 * template MeshNormals {
3046 * DWORD nNormals;
3047 * array Vector normals[nNormals];
3048 * DWORD nFaceNormals;
3049 * array MeshFace faceNormals[nFaceNormals];
3053 hr = E_FAIL;
3055 if (data_size < sizeof(DWORD) * 2) {
3056 WARN("truncated data (%ld bytes)\n", data_size);
3057 goto end;
3059 mesh->num_normals = *(DWORD*)data;
3060 data += sizeof(DWORD);
3061 if (data_size < sizeof(DWORD) * 2 + mesh->num_normals * sizeof(D3DXVECTOR3) +
3062 num_face_indices * sizeof(DWORD)) {
3063 WARN("truncated data (%ld bytes)\n", data_size);
3064 goto end;
3067 mesh->normals = HeapAlloc(GetProcessHeap(), 0, mesh->num_normals * sizeof(D3DXVECTOR3));
3068 mesh->normal_indices = HeapAlloc(GetProcessHeap(), 0, num_face_indices * sizeof(DWORD));
3069 if (!mesh->normals || !mesh->normal_indices) {
3070 hr = E_OUTOFMEMORY;
3071 goto end;
3074 memcpy(mesh->normals, data, mesh->num_normals * sizeof(D3DXVECTOR3));
3075 data += mesh->num_normals * sizeof(D3DXVECTOR3);
3076 for (i = 0; i < mesh->num_normals; i++)
3077 D3DXVec3Normalize(&mesh->normals[i], &mesh->normals[i]);
3079 if (*(DWORD*)data != mesh->num_poly_faces) {
3080 WARN("number of face normals (%u) doesn't match number of faces (%u)\n",
3081 *(DWORD*)data, mesh->num_poly_faces);
3082 goto end;
3084 data += sizeof(DWORD);
3085 index_out_ptr = mesh->normal_indices;
3086 for (i = 0; i < mesh->num_poly_faces; i++)
3088 DWORD j;
3089 DWORD count = *(DWORD*)data;
3090 if (count != mesh->num_tri_per_face[i] + 2) {
3091 WARN("face %u: number of normals (%u) doesn't match number of vertices (%u)\n",
3092 i, count, mesh->num_tri_per_face[i] + 2);
3093 goto end;
3095 data += sizeof(DWORD);
3097 for (j = 0; j < count; j++) {
3098 DWORD normal_index = *(DWORD*)data;
3099 if (normal_index >= mesh->num_normals) {
3100 WARN("face %u, normal index %u: reference to undefined normal %u (only %u normals)\n",
3101 i, j, normal_index, mesh->num_normals);
3102 goto end;
3104 *index_out_ptr++ = normal_index;
3105 data += sizeof(DWORD);
3109 hr = D3D_OK;
3111 end:
3112 filedata->lpVtbl->Unlock(filedata);
3113 return hr;
3116 static HRESULT parse_skin_mesh_info(ID3DXFileData *filedata, struct mesh_data *mesh_data, DWORD index)
3118 HRESULT hr;
3119 SIZE_T data_size;
3120 const BYTE *data;
3122 TRACE("(%p, %p, %u)\n", filedata, mesh_data, index);
3124 hr = filedata->lpVtbl->Lock(filedata, &data_size, (const void**)&data);
3125 if (FAILED(hr)) return hr;
3127 hr = E_FAIL;
3129 if (!mesh_data->skin_info) {
3130 if (data_size < sizeof(WORD) * 3) {
3131 WARN("truncated data (%ld bytes)\n", data_size);
3132 goto end;
3134 /* Skip nMaxSkinWeightsPerVertex and nMaxSkinWeightsPerFace */
3135 data += 2 * sizeof(WORD);
3136 mesh_data->nb_bones = *(WORD*)data;
3137 hr = D3DXCreateSkinInfoFVF(mesh_data->num_vertices, mesh_data->fvf, mesh_data->nb_bones, &mesh_data->skin_info);
3138 } else {
3139 const char *name;
3140 DWORD nb_influences;
3142 /* FIXME: String must be retrieved directly instead of through a pointer once ID3DXFILE is fixed */
3143 name = *(const char**)data;
3144 data += sizeof(char*);
3146 nb_influences = *(DWORD*)data;
3147 data += sizeof(DWORD);
3149 if (data_size < (sizeof(char*) + sizeof(DWORD) + nb_influences * (sizeof(DWORD) + sizeof(FLOAT)) + 16 * sizeof(FLOAT))) {
3150 WARN("truncated data (%ld bytes)\n", data_size);
3151 goto end;
3154 hr = mesh_data->skin_info->lpVtbl->SetBoneName(mesh_data->skin_info, index, name);
3155 if (SUCCEEDED(hr))
3156 hr = mesh_data->skin_info->lpVtbl->SetBoneInfluence(mesh_data->skin_info, index, nb_influences,
3157 (const DWORD*)data, (const FLOAT*)(data + nb_influences * sizeof(DWORD)));
3158 if (SUCCEEDED(hr))
3159 hr = mesh_data->skin_info->lpVtbl->SetBoneOffsetMatrix(mesh_data->skin_info, index,
3160 (const D3DMATRIX*)(data + nb_influences * (sizeof(DWORD) + sizeof(FLOAT))));
3163 end:
3164 filedata->lpVtbl->Unlock(filedata);
3165 return hr;
3168 /* for provide_flags parameters */
3169 #define PROVIDE_MATERIALS 0x1
3170 #define PROVIDE_SKININFO 0x2
3171 #define PROVIDE_ADJACENCY 0x4
3173 static HRESULT parse_mesh(ID3DXFileData *filedata, struct mesh_data *mesh_data, DWORD provide_flags)
3175 HRESULT hr;
3176 SIZE_T data_size;
3177 const BYTE *data, *in_ptr;
3178 DWORD *index_out_ptr;
3179 GUID type;
3180 ID3DXFileData *child = NULL;
3181 DWORD i;
3182 SIZE_T nb_children;
3183 DWORD nb_skin_weigths_info = 0;
3186 * template Mesh {
3187 * DWORD nVertices;
3188 * array Vector vertices[nVertices];
3189 * DWORD nFaces;
3190 * array MeshFace faces[nFaces];
3191 * [ ... ]
3195 hr = filedata->lpVtbl->Lock(filedata, &data_size, (const void**)&data);
3196 if (FAILED(hr)) return hr;
3198 in_ptr = data;
3199 hr = E_FAIL;
3201 if (data_size < sizeof(DWORD) * 2) {
3202 WARN("truncated data (%ld bytes)\n", data_size);
3203 goto end;
3205 mesh_data->num_vertices = *(DWORD*)in_ptr;
3206 if (data_size < sizeof(DWORD) * 2 + mesh_data->num_vertices * sizeof(D3DXVECTOR3)) {
3207 WARN("truncated data (%ld bytes)\n", data_size);
3208 goto end;
3210 in_ptr += sizeof(DWORD) + mesh_data->num_vertices * sizeof(D3DXVECTOR3);
3212 mesh_data->num_poly_faces = *(DWORD*)in_ptr;
3213 in_ptr += sizeof(DWORD);
3215 mesh_data->num_tri_faces = 0;
3216 for (i = 0; i < mesh_data->num_poly_faces; i++)
3218 DWORD num_poly_vertices;
3219 DWORD j;
3221 if (data_size - (in_ptr - data) < sizeof(DWORD)) {
3222 WARN("truncated data (%ld bytes)\n", data_size);
3223 goto end;
3225 num_poly_vertices = *(DWORD*)in_ptr;
3226 in_ptr += sizeof(DWORD);
3227 if (data_size - (in_ptr - data) < num_poly_vertices * sizeof(DWORD)) {
3228 WARN("truncated data (%ld bytes)\n", data_size);
3229 goto end;
3231 if (num_poly_vertices < 3) {
3232 WARN("face %u has only %u vertices\n", i, num_poly_vertices);
3233 goto end;
3235 for (j = 0; j < num_poly_vertices; j++) {
3236 if (*(DWORD*)in_ptr >= mesh_data->num_vertices) {
3237 WARN("face %u, index %u: undefined vertex %u (only %u vertices)\n",
3238 i, j, *(DWORD*)in_ptr, mesh_data->num_vertices);
3239 goto end;
3241 in_ptr += sizeof(DWORD);
3243 mesh_data->num_tri_faces += num_poly_vertices - 2;
3246 mesh_data->fvf = D3DFVF_XYZ;
3248 mesh_data->vertices = HeapAlloc(GetProcessHeap(), 0,
3249 mesh_data->num_vertices * sizeof(*mesh_data->vertices));
3250 mesh_data->num_tri_per_face = HeapAlloc(GetProcessHeap(), 0,
3251 mesh_data->num_poly_faces * sizeof(*mesh_data->num_tri_per_face));
3252 mesh_data->indices = HeapAlloc(GetProcessHeap(), 0,
3253 (mesh_data->num_tri_faces + mesh_data->num_poly_faces * 2) * sizeof(*mesh_data->indices));
3254 if (!mesh_data->vertices || !mesh_data->num_tri_per_face || !mesh_data->indices) {
3255 hr = E_OUTOFMEMORY;
3256 goto end;
3259 in_ptr = data + sizeof(DWORD);
3260 memcpy(mesh_data->vertices, in_ptr, mesh_data->num_vertices * sizeof(D3DXVECTOR3));
3261 in_ptr += mesh_data->num_vertices * sizeof(D3DXVECTOR3) + sizeof(DWORD);
3263 index_out_ptr = mesh_data->indices;
3264 for (i = 0; i < mesh_data->num_poly_faces; i++)
3266 DWORD count;
3268 count = *(DWORD*)in_ptr;
3269 in_ptr += sizeof(DWORD);
3270 mesh_data->num_tri_per_face[i] = count - 2;
3272 while (count--) {
3273 *index_out_ptr++ = *(DWORD*)in_ptr;
3274 in_ptr += sizeof(DWORD);
3278 hr = filedata->lpVtbl->GetChildren(filedata, &nb_children);
3279 if (FAILED(hr))
3280 goto end;
3282 for (i = 0; i < nb_children; i++)
3284 hr = filedata->lpVtbl->GetChild(filedata, i, &child);
3285 if (FAILED(hr))
3286 goto end;
3287 hr = child->lpVtbl->GetType(child, &type);
3288 if (FAILED(hr))
3289 goto end;
3291 if (IsEqualGUID(&type, &TID_D3DRMMeshNormals)) {
3292 hr = parse_normals(child, mesh_data);
3293 } else if (IsEqualGUID(&type, &TID_D3DRMMeshVertexColors)) {
3294 hr = parse_vertex_colors(child, mesh_data);
3295 } else if (IsEqualGUID(&type, &TID_D3DRMMeshTextureCoords)) {
3296 hr = parse_texture_coords(child, mesh_data);
3297 } else if (IsEqualGUID(&type, &TID_D3DRMMeshMaterialList) &&
3298 (provide_flags & PROVIDE_MATERIALS))
3300 hr = parse_material_list(child, mesh_data);
3301 } else if (provide_flags & PROVIDE_SKININFO) {
3302 if (IsEqualGUID(&type, &DXFILEOBJ_XSkinMeshHeader)) {
3303 if (mesh_data->skin_info) {
3304 WARN("Skin mesh header already encountered\n");
3305 hr = E_FAIL;
3306 goto end;
3308 hr = parse_skin_mesh_info(child, mesh_data, 0);
3309 if (FAILED(hr))
3310 goto end;
3311 } else if (IsEqualGUID(&type, &DXFILEOBJ_SkinWeights)) {
3312 if (!mesh_data->skin_info) {
3313 WARN("Skin weigths found but skin mesh header not encountered yet\n");
3314 hr = E_FAIL;
3315 goto end;
3317 hr = parse_skin_mesh_info(child, mesh_data, nb_skin_weigths_info);
3318 if (FAILED(hr))
3319 goto end;
3320 nb_skin_weigths_info++;
3323 if (FAILED(hr))
3324 goto end;
3326 IUnknown_Release(child);
3327 child = NULL;
3330 if (mesh_data->skin_info && (nb_skin_weigths_info != mesh_data->nb_bones)) {
3331 WARN("Mismatch between nb skin weights info %u encountered and nb bones %u from skin mesh header\n",
3332 nb_skin_weigths_info, mesh_data->nb_bones);
3333 hr = E_FAIL;
3334 goto end;
3337 hr = D3D_OK;
3339 end:
3340 if (child)
3341 IUnknown_Release(child);
3342 filedata->lpVtbl->Unlock(filedata);
3343 return hr;
3346 static HRESULT generate_effects(ID3DXBuffer *materials, DWORD num_materials,
3347 ID3DXBuffer **effects)
3349 HRESULT hr;
3350 D3DXEFFECTINSTANCE *effect_ptr;
3351 BYTE *out_ptr;
3352 const D3DXMATERIAL *material_ptr = ID3DXBuffer_GetBufferPointer(materials);
3353 static const struct {
3354 const char *param_name;
3355 DWORD name_size;
3356 DWORD num_bytes;
3357 DWORD value_offset;
3358 } material_effects[] = {
3359 #define EFFECT_TABLE_ENTRY(str, field) \
3360 {str, sizeof(str), sizeof(material_ptr->MatD3D.field), offsetof(D3DXMATERIAL, MatD3D.field)}
3361 EFFECT_TABLE_ENTRY("Diffuse", Diffuse),
3362 EFFECT_TABLE_ENTRY("Power", Power),
3363 EFFECT_TABLE_ENTRY("Specular", Specular),
3364 EFFECT_TABLE_ENTRY("Emissive", Emissive),
3365 EFFECT_TABLE_ENTRY("Ambient", Ambient),
3366 #undef EFFECT_TABLE_ENTRY
3368 static const char texture_paramname[] = "Texture0@Name";
3369 DWORD buffer_size;
3370 DWORD i;
3372 /* effects buffer layout:
3374 * D3DXEFFECTINSTANCE effects[num_materials];
3375 * for (effect in effects)
3377 * D3DXEFFECTDEFAULT defaults[effect.NumDefaults];
3378 * for (default in defaults)
3380 * *default.pParamName;
3381 * *default.pValue;
3385 buffer_size = sizeof(D3DXEFFECTINSTANCE);
3386 buffer_size += sizeof(D3DXEFFECTDEFAULT) * ARRAY_SIZE(material_effects);
3387 for (i = 0; i < ARRAY_SIZE(material_effects); i++) {
3388 buffer_size += material_effects[i].name_size;
3389 buffer_size += material_effects[i].num_bytes;
3391 buffer_size *= num_materials;
3392 for (i = 0; i < num_materials; i++) {
3393 if (material_ptr[i].pTextureFilename) {
3394 buffer_size += sizeof(D3DXEFFECTDEFAULT);
3395 buffer_size += sizeof(texture_paramname);
3396 buffer_size += strlen(material_ptr[i].pTextureFilename) + 1;
3400 hr = D3DXCreateBuffer(buffer_size, effects);
3401 if (FAILED(hr)) return hr;
3402 effect_ptr = ID3DXBuffer_GetBufferPointer(*effects);
3403 out_ptr = (BYTE*)(effect_ptr + num_materials);
3405 for (i = 0; i < num_materials; i++)
3407 DWORD j;
3408 D3DXEFFECTDEFAULT *defaults = (D3DXEFFECTDEFAULT*)out_ptr;
3410 effect_ptr->pDefaults = defaults;
3411 effect_ptr->NumDefaults = material_ptr->pTextureFilename ? 6 : 5;
3412 out_ptr = (BYTE*)(effect_ptr->pDefaults + effect_ptr->NumDefaults);
3414 for (j = 0; j < ARRAY_SIZE(material_effects); j++)
3416 defaults->pParamName = (char *)out_ptr;
3417 strcpy(defaults->pParamName, material_effects[j].param_name);
3418 defaults->pValue = defaults->pParamName + material_effects[j].name_size;
3419 defaults->Type = D3DXEDT_FLOATS;
3420 defaults->NumBytes = material_effects[j].num_bytes;
3421 memcpy(defaults->pValue, (BYTE*)material_ptr + material_effects[j].value_offset, defaults->NumBytes);
3422 out_ptr = (BYTE*)defaults->pValue + defaults->NumBytes;
3423 defaults++;
3426 if (material_ptr->pTextureFilename)
3428 defaults->pParamName = (char *)out_ptr;
3429 strcpy(defaults->pParamName, texture_paramname);
3430 defaults->pValue = defaults->pParamName + sizeof(texture_paramname);
3431 defaults->Type = D3DXEDT_STRING;
3432 defaults->NumBytes = strlen(material_ptr->pTextureFilename) + 1;
3433 strcpy(defaults->pValue, material_ptr->pTextureFilename);
3434 out_ptr = (BYTE*)defaults->pValue + defaults->NumBytes;
3436 material_ptr++;
3437 effect_ptr++;
3439 assert(out_ptr - (BYTE*)ID3DXBuffer_GetBufferPointer(*effects) == buffer_size);
3441 return D3D_OK;
3444 HRESULT WINAPI D3DXLoadSkinMeshFromXof(struct ID3DXFileData *filedata, DWORD options,
3445 struct IDirect3DDevice9 *device, struct ID3DXBuffer **adjacency_out, struct ID3DXBuffer **materials_out,
3446 struct ID3DXBuffer **effects_out, DWORD *num_materials_out, struct ID3DXSkinInfo **skin_info_out,
3447 struct ID3DXMesh **mesh_out)
3449 HRESULT hr;
3450 DWORD *index_in_ptr;
3451 struct mesh_data mesh_data;
3452 DWORD total_vertices;
3453 ID3DXMesh *d3dxmesh = NULL;
3454 ID3DXBuffer *adjacency = NULL;
3455 ID3DXBuffer *materials = NULL;
3456 ID3DXBuffer *effects = NULL;
3457 struct vertex_duplication {
3458 DWORD normal_index;
3459 struct list entry;
3460 } *duplications = NULL;
3461 DWORD i;
3462 void *vertices = NULL;
3463 void *indices = NULL;
3464 BYTE *out_ptr;
3465 DWORD provide_flags = 0;
3467 TRACE("(%p, %x, %p, %p, %p, %p, %p, %p, %p)\n", filedata, options, device, adjacency_out, materials_out,
3468 effects_out, num_materials_out, skin_info_out, mesh_out);
3470 ZeroMemory(&mesh_data, sizeof(mesh_data));
3472 if (num_materials_out || materials_out || effects_out)
3473 provide_flags |= PROVIDE_MATERIALS;
3474 if (skin_info_out)
3475 provide_flags |= PROVIDE_SKININFO;
3477 hr = parse_mesh(filedata, &mesh_data, provide_flags);
3478 if (FAILED(hr)) goto cleanup;
3480 total_vertices = mesh_data.num_vertices;
3481 if (mesh_data.fvf & D3DFVF_NORMAL) {
3482 /* duplicate vertices with multiple normals */
3483 DWORD num_face_indices = mesh_data.num_poly_faces * 2 + mesh_data.num_tri_faces;
3484 duplications = HeapAlloc(GetProcessHeap(), 0, (mesh_data.num_vertices + num_face_indices) * sizeof(*duplications));
3485 if (!duplications) {
3486 hr = E_OUTOFMEMORY;
3487 goto cleanup;
3489 for (i = 0; i < total_vertices; i++)
3491 duplications[i].normal_index = -1;
3492 list_init(&duplications[i].entry);
3494 for (i = 0; i < num_face_indices; i++) {
3495 DWORD vertex_index = mesh_data.indices[i];
3496 DWORD normal_index = mesh_data.normal_indices[i];
3497 struct vertex_duplication *dup_ptr = &duplications[vertex_index];
3499 if (dup_ptr->normal_index == -1) {
3500 dup_ptr->normal_index = normal_index;
3501 } else {
3502 D3DXVECTOR3 *new_normal = &mesh_data.normals[normal_index];
3503 struct list *dup_list = &dup_ptr->entry;
3504 while (TRUE) {
3505 D3DXVECTOR3 *cur_normal = &mesh_data.normals[dup_ptr->normal_index];
3506 if (new_normal->x == cur_normal->x &&
3507 new_normal->y == cur_normal->y &&
3508 new_normal->z == cur_normal->z)
3510 mesh_data.indices[i] = dup_ptr - duplications;
3511 break;
3512 } else if (!list_next(dup_list, &dup_ptr->entry)) {
3513 dup_ptr = &duplications[total_vertices++];
3514 dup_ptr->normal_index = normal_index;
3515 list_add_tail(dup_list, &dup_ptr->entry);
3516 mesh_data.indices[i] = dup_ptr - duplications;
3517 break;
3518 } else {
3519 dup_ptr = LIST_ENTRY(list_next(dup_list, &dup_ptr->entry),
3520 struct vertex_duplication, entry);
3527 hr = D3DXCreateMeshFVF(mesh_data.num_tri_faces, total_vertices, options, mesh_data.fvf, device, &d3dxmesh);
3528 if (FAILED(hr)) goto cleanup;
3530 hr = d3dxmesh->lpVtbl->LockVertexBuffer(d3dxmesh, 0, &vertices);
3531 if (FAILED(hr)) goto cleanup;
3533 out_ptr = vertices;
3534 for (i = 0; i < mesh_data.num_vertices; i++) {
3535 *(D3DXVECTOR3*)out_ptr = mesh_data.vertices[i];
3536 out_ptr += sizeof(D3DXVECTOR3);
3537 if (mesh_data.fvf & D3DFVF_NORMAL) {
3538 if (duplications[i].normal_index == -1)
3539 ZeroMemory(out_ptr, sizeof(D3DXVECTOR3));
3540 else
3541 *(D3DXVECTOR3*)out_ptr = mesh_data.normals[duplications[i].normal_index];
3542 out_ptr += sizeof(D3DXVECTOR3);
3544 if (mesh_data.fvf & D3DFVF_DIFFUSE) {
3545 *(DWORD*)out_ptr = mesh_data.vertex_colors[i];
3546 out_ptr += sizeof(DWORD);
3548 if (mesh_data.fvf & D3DFVF_TEX1) {
3549 *(D3DXVECTOR2*)out_ptr = mesh_data.tex_coords[i];
3550 out_ptr += sizeof(D3DXVECTOR2);
3553 if (mesh_data.fvf & D3DFVF_NORMAL) {
3554 DWORD vertex_size = D3DXGetFVFVertexSize(mesh_data.fvf);
3555 out_ptr = vertices;
3556 for (i = 0; i < mesh_data.num_vertices; i++) {
3557 struct vertex_duplication *dup_ptr;
3558 LIST_FOR_EACH_ENTRY(dup_ptr, &duplications[i].entry, struct vertex_duplication, entry)
3560 int j = dup_ptr - duplications;
3561 BYTE *dest_vertex = (BYTE*)vertices + j * vertex_size;
3563 memcpy(dest_vertex, out_ptr, vertex_size);
3564 dest_vertex += sizeof(D3DXVECTOR3);
3565 *(D3DXVECTOR3*)dest_vertex = mesh_data.normals[dup_ptr->normal_index];
3567 out_ptr += vertex_size;
3570 d3dxmesh->lpVtbl->UnlockVertexBuffer(d3dxmesh);
3572 hr = d3dxmesh->lpVtbl->LockIndexBuffer(d3dxmesh, 0, &indices);
3573 if (FAILED(hr)) goto cleanup;
3575 index_in_ptr = mesh_data.indices;
3576 #define FILL_INDEX_BUFFER(indices_var) \
3577 for (i = 0; i < mesh_data.num_poly_faces; i++) \
3579 DWORD count = mesh_data.num_tri_per_face[i]; \
3580 WORD first_index = *index_in_ptr++; \
3581 while (count--) { \
3582 *indices_var++ = first_index; \
3583 *indices_var++ = *index_in_ptr; \
3584 index_in_ptr++; \
3585 *indices_var++ = *index_in_ptr; \
3587 index_in_ptr++; \
3589 if (options & D3DXMESH_32BIT) {
3590 DWORD *dword_indices = indices;
3591 FILL_INDEX_BUFFER(dword_indices)
3592 } else {
3593 WORD *word_indices = indices;
3594 FILL_INDEX_BUFFER(word_indices)
3596 #undef FILL_INDEX_BUFFER
3597 d3dxmesh->lpVtbl->UnlockIndexBuffer(d3dxmesh);
3599 if (mesh_data.material_indices) {
3600 DWORD *attrib_buffer = NULL;
3601 hr = d3dxmesh->lpVtbl->LockAttributeBuffer(d3dxmesh, 0, &attrib_buffer);
3602 if (FAILED(hr)) goto cleanup;
3603 for (i = 0; i < mesh_data.num_poly_faces; i++)
3605 DWORD count = mesh_data.num_tri_per_face[i];
3606 while (count--)
3607 *attrib_buffer++ = mesh_data.material_indices[i];
3609 d3dxmesh->lpVtbl->UnlockAttributeBuffer(d3dxmesh);
3611 hr = d3dxmesh->lpVtbl->OptimizeInplace(d3dxmesh,
3612 D3DXMESHOPT_ATTRSORT | D3DXMESHOPT_IGNOREVERTS | D3DXMESHOPT_DONOTSPLIT,
3613 NULL, NULL, NULL, NULL);
3614 if (FAILED(hr)) goto cleanup;
3617 if (mesh_data.num_materials && (materials_out || effects_out)) {
3618 DWORD buffer_size = mesh_data.num_materials * sizeof(D3DXMATERIAL);
3619 char *strings_out_ptr;
3620 D3DXMATERIAL *materials_ptr;
3622 for (i = 0; i < mesh_data.num_materials; i++) {
3623 if (mesh_data.materials[i].pTextureFilename)
3624 buffer_size += strlen(mesh_data.materials[i].pTextureFilename) + 1;
3627 hr = D3DXCreateBuffer(buffer_size, &materials);
3628 if (FAILED(hr)) goto cleanup;
3630 materials_ptr = ID3DXBuffer_GetBufferPointer(materials);
3631 memcpy(materials_ptr, mesh_data.materials, mesh_data.num_materials * sizeof(D3DXMATERIAL));
3632 strings_out_ptr = (char*)(materials_ptr + mesh_data.num_materials);
3633 for (i = 0; i < mesh_data.num_materials; i++) {
3634 if (materials_ptr[i].pTextureFilename) {
3635 strcpy(strings_out_ptr, mesh_data.materials[i].pTextureFilename);
3636 materials_ptr[i].pTextureFilename = strings_out_ptr;
3637 strings_out_ptr += strlen(mesh_data.materials[i].pTextureFilename) + 1;
3642 if (mesh_data.num_materials && effects_out) {
3643 hr = generate_effects(materials, mesh_data.num_materials, &effects);
3644 if (FAILED(hr)) goto cleanup;
3646 if (!materials_out) {
3647 ID3DXBuffer_Release(materials);
3648 materials = NULL;
3652 if (adjacency_out) {
3653 hr = D3DXCreateBuffer(mesh_data.num_tri_faces * 3 * sizeof(DWORD), &adjacency);
3654 if (FAILED(hr)) goto cleanup;
3655 hr = d3dxmesh->lpVtbl->GenerateAdjacency(d3dxmesh, 0.0f, ID3DXBuffer_GetBufferPointer(adjacency));
3656 if (FAILED(hr)) goto cleanup;
3659 *mesh_out = d3dxmesh;
3660 if (adjacency_out) *adjacency_out = adjacency;
3661 if (num_materials_out) *num_materials_out = mesh_data.num_materials;
3662 if (materials_out) *materials_out = materials;
3663 if (effects_out) *effects_out = effects;
3664 if (skin_info_out) *skin_info_out = mesh_data.skin_info;
3666 hr = D3D_OK;
3667 cleanup:
3668 if (FAILED(hr)) {
3669 if (d3dxmesh) IUnknown_Release(d3dxmesh);
3670 if (adjacency) ID3DXBuffer_Release(adjacency);
3671 if (materials) ID3DXBuffer_Release(materials);
3672 if (effects) ID3DXBuffer_Release(effects);
3673 if (mesh_data.skin_info) mesh_data.skin_info->lpVtbl->Release(mesh_data.skin_info);
3674 if (skin_info_out) *skin_info_out = NULL;
3676 HeapFree(GetProcessHeap(), 0, mesh_data.vertices);
3677 HeapFree(GetProcessHeap(), 0, mesh_data.num_tri_per_face);
3678 HeapFree(GetProcessHeap(), 0, mesh_data.indices);
3679 HeapFree(GetProcessHeap(), 0, mesh_data.normals);
3680 HeapFree(GetProcessHeap(), 0, mesh_data.normal_indices);
3681 destroy_materials(&mesh_data);
3682 HeapFree(GetProcessHeap(), 0, mesh_data.tex_coords);
3683 HeapFree(GetProcessHeap(), 0, mesh_data.vertex_colors);
3684 HeapFree(GetProcessHeap(), 0, duplications);
3685 return hr;
3688 HRESULT WINAPI D3DXLoadMeshHierarchyFromXA(const char *filename, DWORD options, struct IDirect3DDevice9 *device,
3689 struct ID3DXAllocateHierarchy *alloc_hier, struct ID3DXLoadUserData *load_user_data,
3690 D3DXFRAME **frame_hierarchy, struct ID3DXAnimationController **anim_controller)
3692 WCHAR *filenameW;
3693 HRESULT hr;
3694 int len;
3696 TRACE("filename %s, options %#x, device %p, alloc_hier %p, "
3697 "load_user_data %p, frame_hierarchy %p, anim_controller %p.\n",
3698 debugstr_a(filename), options, device, alloc_hier,
3699 load_user_data, frame_hierarchy, anim_controller);
3701 if (!filename)
3702 return D3DERR_INVALIDCALL;
3704 len = MultiByteToWideChar(CP_ACP, 0, filename, -1, NULL, 0);
3705 filenameW = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
3706 if (!filenameW) return E_OUTOFMEMORY;
3707 MultiByteToWideChar(CP_ACP, 0, filename, -1, filenameW, len);
3709 hr = D3DXLoadMeshHierarchyFromXW(filenameW, options, device,
3710 alloc_hier, load_user_data, frame_hierarchy, anim_controller);
3711 HeapFree(GetProcessHeap(), 0, filenameW);
3713 return hr;
3716 HRESULT WINAPI D3DXLoadMeshHierarchyFromXW(const WCHAR *filename, DWORD options, struct IDirect3DDevice9 *device,
3717 struct ID3DXAllocateHierarchy *alloc_hier, struct ID3DXLoadUserData *load_user_data,
3718 D3DXFRAME **frame_hierarchy, struct ID3DXAnimationController **anim_controller)
3720 void *buffer;
3721 HRESULT hr;
3722 DWORD size;
3724 TRACE("filename %s, options %#x, device %p, alloc_hier %p, "
3725 "load_user_data %p, frame_hierarchy %p, anim_controller %p.\n",
3726 debugstr_w(filename), options, device, alloc_hier,
3727 load_user_data, frame_hierarchy, anim_controller);
3729 if (!filename)
3730 return D3DERR_INVALIDCALL;
3732 hr = map_view_of_file(filename, &buffer, &size);
3733 if (FAILED(hr))
3734 return D3DXERR_INVALIDDATA;
3736 hr = D3DXLoadMeshHierarchyFromXInMemory(buffer, size, options, device,
3737 alloc_hier, load_user_data, frame_hierarchy, anim_controller);
3739 UnmapViewOfFile(buffer);
3741 return hr;
3744 static HRESULT filedata_get_name(ID3DXFileData *filedata, char **name)
3746 HRESULT hr;
3747 SIZE_T name_len;
3749 hr = filedata->lpVtbl->GetName(filedata, NULL, &name_len);
3750 if (FAILED(hr)) return hr;
3752 if (!name_len)
3753 name_len++;
3754 *name = HeapAlloc(GetProcessHeap(), 0, name_len);
3755 if (!*name) return E_OUTOFMEMORY;
3757 hr = filedata->lpVtbl->GetName(filedata, *name, &name_len);
3758 if (FAILED(hr))
3759 HeapFree(GetProcessHeap(), 0, *name);
3760 else if (!name_len)
3761 (*name)[0] = 0;
3763 return hr;
3766 static HRESULT load_mesh_container(struct ID3DXFileData *filedata, DWORD options, struct IDirect3DDevice9 *device,
3767 struct ID3DXAllocateHierarchy *alloc_hier, D3DXMESHCONTAINER **mesh_container)
3769 HRESULT hr;
3770 ID3DXBuffer *adjacency = NULL;
3771 ID3DXBuffer *materials = NULL;
3772 ID3DXBuffer *effects = NULL;
3773 ID3DXSkinInfo *skin_info = NULL;
3774 D3DXMESHDATA mesh_data;
3775 DWORD num_materials = 0;
3776 char *name = NULL;
3778 mesh_data.Type = D3DXMESHTYPE_MESH;
3779 mesh_data.u.pMesh = NULL;
3781 hr = D3DXLoadSkinMeshFromXof(filedata, options, device,
3782 &adjacency, &materials, &effects, &num_materials,
3783 &skin_info, &mesh_data.u.pMesh);
3784 if (FAILED(hr)) return hr;
3786 hr = filedata_get_name(filedata, &name);
3787 if (FAILED(hr)) goto cleanup;
3789 hr = alloc_hier->lpVtbl->CreateMeshContainer(alloc_hier, name, &mesh_data,
3790 materials ? ID3DXBuffer_GetBufferPointer(materials) : NULL,
3791 effects ? ID3DXBuffer_GetBufferPointer(effects) : NULL,
3792 num_materials,
3793 adjacency ? ID3DXBuffer_GetBufferPointer(adjacency) : NULL,
3794 skin_info, mesh_container);
3796 cleanup:
3797 if (materials) ID3DXBuffer_Release(materials);
3798 if (effects) ID3DXBuffer_Release(effects);
3799 if (adjacency) ID3DXBuffer_Release(adjacency);
3800 if (skin_info) IUnknown_Release(skin_info);
3801 if (mesh_data.u.pMesh) IUnknown_Release(mesh_data.u.pMesh);
3802 HeapFree(GetProcessHeap(), 0, name);
3803 return hr;
3806 static HRESULT parse_transform_matrix(ID3DXFileData *filedata, D3DXMATRIX *transform)
3808 HRESULT hr;
3809 SIZE_T data_size;
3810 const BYTE *data;
3812 /* template Matrix4x4 {
3813 * array FLOAT matrix[16];
3815 * template FrameTransformMatrix {
3816 * Matrix4x4 frameMatrix;
3820 hr = filedata->lpVtbl->Lock(filedata, &data_size, (const void**)&data);
3821 if (FAILED(hr)) return hr;
3823 if (data_size != sizeof(D3DXMATRIX)) {
3824 WARN("incorrect data size (%ld bytes)\n", data_size);
3825 filedata->lpVtbl->Unlock(filedata);
3826 return E_FAIL;
3829 memcpy(transform, data, sizeof(D3DXMATRIX));
3831 filedata->lpVtbl->Unlock(filedata);
3832 return D3D_OK;
3835 static HRESULT load_frame(struct ID3DXFileData *filedata, DWORD options, struct IDirect3DDevice9 *device,
3836 struct ID3DXAllocateHierarchy *alloc_hier, D3DXFRAME **frame_out)
3838 HRESULT hr;
3839 GUID type;
3840 ID3DXFileData *child;
3841 char *name = NULL;
3842 D3DXFRAME *frame = NULL;
3843 D3DXMESHCONTAINER **next_container;
3844 D3DXFRAME **next_child;
3845 SIZE_T i, nb_children;
3847 hr = filedata_get_name(filedata, &name);
3848 if (FAILED(hr)) return hr;
3850 hr = alloc_hier->lpVtbl->CreateFrame(alloc_hier, name, frame_out);
3851 HeapFree(GetProcessHeap(), 0, name);
3852 if (FAILED(hr)) return E_FAIL;
3854 frame = *frame_out;
3855 D3DXMatrixIdentity(&frame->TransformationMatrix);
3856 next_child = &frame->pFrameFirstChild;
3857 next_container = &frame->pMeshContainer;
3859 hr = filedata->lpVtbl->GetChildren(filedata, &nb_children);
3860 if (FAILED(hr))
3861 return hr;
3863 for (i = 0; i < nb_children; i++)
3865 hr = filedata->lpVtbl->GetChild(filedata, i, &child);
3866 if (FAILED(hr))
3867 return hr;
3868 hr = child->lpVtbl->GetType(child, &type);
3869 if (FAILED(hr))
3870 goto err;
3872 if (IsEqualGUID(&type, &TID_D3DRMMesh)) {
3873 hr = load_mesh_container(child, options, device, alloc_hier, next_container);
3874 if (SUCCEEDED(hr))
3875 next_container = &(*next_container)->pNextMeshContainer;
3876 } else if (IsEqualGUID(&type, &TID_D3DRMFrameTransformMatrix)) {
3877 hr = parse_transform_matrix(child, &frame->TransformationMatrix);
3878 } else if (IsEqualGUID(&type, &TID_D3DRMFrame)) {
3879 hr = load_frame(child, options, device, alloc_hier, next_child);
3880 if (SUCCEEDED(hr))
3881 next_child = &(*next_child)->pFrameSibling;
3883 if (FAILED(hr))
3884 goto err;
3886 IUnknown_Release(child);
3888 return D3D_OK;
3890 err:
3891 IUnknown_Release(child);
3892 return hr;
3895 HRESULT WINAPI D3DXLoadMeshHierarchyFromXInMemory(const void *memory, DWORD memory_size, DWORD options,
3896 struct IDirect3DDevice9 *device, struct ID3DXAllocateHierarchy *alloc_hier,
3897 struct ID3DXLoadUserData *load_user_data, D3DXFRAME **frame_hierarchy,
3898 struct ID3DXAnimationController **anim_controller)
3900 HRESULT hr;
3901 ID3DXFile *d3dxfile = NULL;
3902 ID3DXFileEnumObject *enumobj = NULL;
3903 ID3DXFileData *filedata = NULL;
3904 D3DXF_FILELOADMEMORY source;
3905 D3DXFRAME *first_frame = NULL;
3906 D3DXFRAME **next_frame = &first_frame;
3907 SIZE_T i, nb_children;
3908 GUID guid;
3910 TRACE("(%p, %u, %x, %p, %p, %p, %p, %p)\n", memory, memory_size, options,
3911 device, alloc_hier, load_user_data, frame_hierarchy, anim_controller);
3913 if (!memory || !memory_size || !device || !frame_hierarchy || !alloc_hier)
3914 return D3DERR_INVALIDCALL;
3915 if (load_user_data || anim_controller) {
3916 if (load_user_data)
3917 FIXME("Loading user data not implemented\n");
3918 if (anim_controller)
3919 FIXME("Animation controller creation not implemented\n");
3920 return E_NOTIMPL;
3923 hr = D3DXFileCreate(&d3dxfile);
3924 if (FAILED(hr)) goto cleanup;
3926 hr = d3dxfile->lpVtbl->RegisterTemplates(d3dxfile, D3DRM_XTEMPLATES, D3DRM_XTEMPLATE_BYTES);
3927 if (FAILED(hr)) goto cleanup;
3929 source.lpMemory = (void*)memory;
3930 source.dSize = memory_size;
3931 hr = d3dxfile->lpVtbl->CreateEnumObject(d3dxfile, &source, D3DXF_FILELOAD_FROMMEMORY, &enumobj);
3932 if (FAILED(hr)) goto cleanup;
3934 hr = enumobj->lpVtbl->GetChildren(enumobj, &nb_children);
3935 if (FAILED(hr))
3936 goto cleanup;
3938 for (i = 0; i < nb_children; i++)
3940 hr = enumobj->lpVtbl->GetChild(enumobj, i, &filedata);
3941 if (FAILED(hr))
3942 goto cleanup;
3944 hr = filedata->lpVtbl->GetType(filedata, &guid);
3945 if (SUCCEEDED(hr)) {
3946 if (IsEqualGUID(&guid, &TID_D3DRMMesh)) {
3947 hr = alloc_hier->lpVtbl->CreateFrame(alloc_hier, NULL, next_frame);
3948 if (FAILED(hr)) {
3949 hr = E_FAIL;
3950 goto cleanup;
3953 D3DXMatrixIdentity(&(*next_frame)->TransformationMatrix);
3955 hr = load_mesh_container(filedata, options, device, alloc_hier, &(*next_frame)->pMeshContainer);
3956 if (FAILED(hr)) goto cleanup;
3957 } else if (IsEqualGUID(&guid, &TID_D3DRMFrame)) {
3958 hr = load_frame(filedata, options, device, alloc_hier, next_frame);
3959 if (FAILED(hr)) goto cleanup;
3961 while (*next_frame)
3962 next_frame = &(*next_frame)->pFrameSibling;
3965 filedata->lpVtbl->Release(filedata);
3966 filedata = NULL;
3967 if (FAILED(hr))
3968 goto cleanup;
3971 if (!first_frame) {
3972 hr = E_FAIL;
3973 } else if (first_frame->pFrameSibling) {
3974 D3DXFRAME *root_frame = NULL;
3975 hr = alloc_hier->lpVtbl->CreateFrame(alloc_hier, NULL, &root_frame);
3976 if (FAILED(hr)) {
3977 hr = E_FAIL;
3978 goto cleanup;
3980 D3DXMatrixIdentity(&root_frame->TransformationMatrix);
3981 root_frame->pFrameFirstChild = first_frame;
3982 *frame_hierarchy = root_frame;
3983 hr = D3D_OK;
3984 } else {
3985 *frame_hierarchy = first_frame;
3986 hr = D3D_OK;
3989 cleanup:
3990 if (FAILED(hr) && first_frame) D3DXFrameDestroy(first_frame, alloc_hier);
3991 if (filedata) filedata->lpVtbl->Release(filedata);
3992 if (enumobj) enumobj->lpVtbl->Release(enumobj);
3993 if (d3dxfile) d3dxfile->lpVtbl->Release(d3dxfile);
3994 return hr;
3997 HRESULT WINAPI D3DXCleanMesh(D3DXCLEANTYPE clean_type, ID3DXMesh *mesh_in, const DWORD *adjacency_in,
3998 ID3DXMesh **mesh_out, DWORD *adjacency_out, ID3DXBuffer **errors_and_warnings)
4000 FIXME("(%u, %p, %p, %p, %p, %p)\n", clean_type, mesh_in, adjacency_in, mesh_out, adjacency_out, errors_and_warnings);
4002 return E_NOTIMPL;
4005 HRESULT WINAPI D3DXFrameDestroy(D3DXFRAME *frame, ID3DXAllocateHierarchy *alloc_hier)
4007 HRESULT hr;
4008 BOOL last = FALSE;
4010 TRACE("(%p, %p)\n", frame, alloc_hier);
4012 if (!frame || !alloc_hier)
4013 return D3DERR_INVALIDCALL;
4015 while (!last) {
4016 D3DXMESHCONTAINER *container;
4017 D3DXFRAME *current_frame;
4019 if (frame->pFrameSibling) {
4020 current_frame = frame->pFrameSibling;
4021 frame->pFrameSibling = current_frame->pFrameSibling;
4022 current_frame->pFrameSibling = NULL;
4023 } else {
4024 current_frame = frame;
4025 last = TRUE;
4028 if (current_frame->pFrameFirstChild) {
4029 hr = D3DXFrameDestroy(current_frame->pFrameFirstChild, alloc_hier);
4030 if (FAILED(hr)) return hr;
4031 current_frame->pFrameFirstChild = NULL;
4034 container = current_frame->pMeshContainer;
4035 while (container) {
4036 D3DXMESHCONTAINER *next_container = container->pNextMeshContainer;
4037 hr = alloc_hier->lpVtbl->DestroyMeshContainer(alloc_hier, container);
4038 if (FAILED(hr)) return hr;
4039 container = next_container;
4041 hr = alloc_hier->lpVtbl->DestroyFrame(alloc_hier, current_frame);
4042 if (FAILED(hr)) return hr;
4044 return D3D_OK;
4047 HRESULT WINAPI D3DXLoadMeshFromXA(const char *filename, DWORD options, struct IDirect3DDevice9 *device,
4048 struct ID3DXBuffer **adjacency, struct ID3DXBuffer **materials, struct ID3DXBuffer **effect_instances,
4049 DWORD *num_materials, struct ID3DXMesh **mesh)
4051 WCHAR *filenameW;
4052 HRESULT hr;
4053 int len;
4055 TRACE("filename %s, options %#x, device %p, adjacency %p, materials %p, "
4056 "effect_instances %p, num_materials %p, mesh %p.\n",
4057 debugstr_a(filename), options, device, adjacency, materials,
4058 effect_instances, num_materials, mesh);
4060 if (!filename)
4061 return D3DERR_INVALIDCALL;
4063 len = MultiByteToWideChar(CP_ACP, 0, filename, -1, NULL, 0);
4064 filenameW = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
4065 if (!filenameW) return E_OUTOFMEMORY;
4066 MultiByteToWideChar(CP_ACP, 0, filename, -1, filenameW, len);
4068 hr = D3DXLoadMeshFromXW(filenameW, options, device, adjacency, materials,
4069 effect_instances, num_materials, mesh);
4070 HeapFree(GetProcessHeap(), 0, filenameW);
4072 return hr;
4075 HRESULT WINAPI D3DXLoadMeshFromXW(const WCHAR *filename, DWORD options, struct IDirect3DDevice9 *device,
4076 struct ID3DXBuffer **adjacency, struct ID3DXBuffer **materials, struct ID3DXBuffer **effect_instances,
4077 DWORD *num_materials, struct ID3DXMesh **mesh)
4079 void *buffer;
4080 HRESULT hr;
4081 DWORD size;
4083 TRACE("filename %s, options %#x, device %p, adjacency %p, materials %p, "
4084 "effect_instances %p, num_materials %p, mesh %p.\n",
4085 debugstr_w(filename), options, device, adjacency, materials,
4086 effect_instances, num_materials, mesh);
4088 if (!filename)
4089 return D3DERR_INVALIDCALL;
4091 hr = map_view_of_file(filename, &buffer, &size);
4092 if (FAILED(hr))
4093 return D3DXERR_INVALIDDATA;
4095 hr = D3DXLoadMeshFromXInMemory(buffer, size, options, device, adjacency,
4096 materials, effect_instances, num_materials, mesh);
4098 UnmapViewOfFile(buffer);
4100 return hr;
4103 HRESULT WINAPI D3DXLoadMeshFromXResource(HMODULE module, const char *name, const char *type, DWORD options,
4104 struct IDirect3DDevice9 *device, struct ID3DXBuffer **adjacency, struct ID3DXBuffer **materials,
4105 struct ID3DXBuffer **effect_instances, DWORD *num_materials, struct ID3DXMesh **mesh)
4107 HRESULT hr;
4108 HRSRC resinfo;
4109 void *buffer;
4110 DWORD size;
4112 TRACE("module %p, name %s, type %s, options %#x, device %p, adjacency %p, "
4113 "materials %p, effect_instances %p, num_materials %p, mesh %p.\n",
4114 module, debugstr_a(name), debugstr_a(type), options, device, adjacency,
4115 materials, effect_instances, num_materials, mesh);
4117 resinfo = FindResourceA(module, name, type);
4118 if (!resinfo) return D3DXERR_INVALIDDATA;
4120 hr = load_resource_into_memory(module, resinfo, &buffer, &size);
4121 if (FAILED(hr)) return D3DXERR_INVALIDDATA;
4123 return D3DXLoadMeshFromXInMemory(buffer, size, options, device, adjacency,
4124 materials, effect_instances, num_materials, mesh);
4127 struct mesh_container
4129 struct list entry;
4130 ID3DXMesh *mesh;
4131 ID3DXBuffer *adjacency;
4132 ID3DXBuffer *materials;
4133 ID3DXBuffer *effects;
4134 DWORD num_materials;
4135 D3DXMATRIX transform;
4138 static HRESULT parse_frame(struct ID3DXFileData *filedata, DWORD options, struct IDirect3DDevice9 *device,
4139 const D3DXMATRIX *parent_transform, struct list *container_list, DWORD provide_flags)
4141 HRESULT hr;
4142 D3DXMATRIX transform = *parent_transform;
4143 ID3DXFileData *child;
4144 GUID type;
4145 SIZE_T i, nb_children;
4147 hr = filedata->lpVtbl->GetChildren(filedata, &nb_children);
4148 if (FAILED(hr))
4149 return hr;
4151 for (i = 0; i < nb_children; i++)
4153 hr = filedata->lpVtbl->GetChild(filedata, i, &child);
4154 if (FAILED(hr))
4155 return hr;
4156 hr = child->lpVtbl->GetType(child, &type);
4157 if (FAILED(hr))
4158 goto err;
4160 if (IsEqualGUID(&type, &TID_D3DRMMesh)) {
4161 struct mesh_container *container = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(*container));
4162 if (!container)
4164 hr = E_OUTOFMEMORY;
4165 goto err;
4167 list_add_tail(container_list, &container->entry);
4168 container->transform = transform;
4170 hr = D3DXLoadSkinMeshFromXof(child, options, device,
4171 (provide_flags & PROVIDE_ADJACENCY) ? &container->adjacency : NULL,
4172 (provide_flags & PROVIDE_MATERIALS) ? &container->materials : NULL,
4173 NULL, &container->num_materials, NULL, &container->mesh);
4174 } else if (IsEqualGUID(&type, &TID_D3DRMFrameTransformMatrix)) {
4175 D3DXMATRIX new_transform;
4176 hr = parse_transform_matrix(child, &new_transform);
4177 D3DXMatrixMultiply(&transform, &transform, &new_transform);
4178 } else if (IsEqualGUID(&type, &TID_D3DRMFrame)) {
4179 hr = parse_frame(child, options, device, &transform, container_list, provide_flags);
4181 if (FAILED(hr))
4182 goto err;
4184 IUnknown_Release(child);
4186 return D3D_OK;
4188 err:
4189 IUnknown_Release(child);
4190 return hr;
4193 HRESULT WINAPI D3DXLoadMeshFromXInMemory(const void *memory, DWORD memory_size, DWORD options,
4194 struct IDirect3DDevice9 *device, struct ID3DXBuffer **adjacency_out, struct ID3DXBuffer **materials_out,
4195 struct ID3DXBuffer **effects_out, DWORD *num_materials_out, struct ID3DXMesh **mesh_out)
4197 HRESULT hr;
4198 ID3DXFile *d3dxfile = NULL;
4199 ID3DXFileEnumObject *enumobj = NULL;
4200 ID3DXFileData *filedata = NULL;
4201 D3DXF_FILELOADMEMORY source;
4202 ID3DXBuffer *materials = NULL;
4203 ID3DXBuffer *effects = NULL;
4204 ID3DXBuffer *adjacency = NULL;
4205 struct list container_list = LIST_INIT(container_list);
4206 struct mesh_container *container_ptr, *next_container_ptr;
4207 DWORD num_materials;
4208 DWORD num_faces, num_vertices;
4209 D3DXMATRIX identity;
4210 DWORD provide_flags = 0;
4211 DWORD fvf;
4212 ID3DXMesh *concat_mesh = NULL;
4213 D3DVERTEXELEMENT9 concat_decl[MAX_FVF_DECL_SIZE];
4214 BYTE *concat_vertices = NULL;
4215 void *concat_indices = NULL;
4216 DWORD index_offset;
4217 DWORD concat_vertex_size;
4218 SIZE_T i, nb_children;
4219 GUID guid;
4221 TRACE("(%p, %u, %x, %p, %p, %p, %p, %p, %p)\n", memory, memory_size, options,
4222 device, adjacency_out, materials_out, effects_out, num_materials_out, mesh_out);
4224 if (!memory || !memory_size || !device || !mesh_out)
4225 return D3DERR_INVALIDCALL;
4227 hr = D3DXFileCreate(&d3dxfile);
4228 if (FAILED(hr)) goto cleanup;
4230 hr = d3dxfile->lpVtbl->RegisterTemplates(d3dxfile, D3DRM_XTEMPLATES, D3DRM_XTEMPLATE_BYTES);
4231 if (FAILED(hr)) goto cleanup;
4233 source.lpMemory = (void*)memory;
4234 source.dSize = memory_size;
4235 hr = d3dxfile->lpVtbl->CreateEnumObject(d3dxfile, &source, D3DXF_FILELOAD_FROMMEMORY, &enumobj);
4236 if (FAILED(hr)) goto cleanup;
4238 D3DXMatrixIdentity(&identity);
4239 if (adjacency_out) provide_flags |= PROVIDE_ADJACENCY;
4240 if (materials_out || effects_out) provide_flags |= PROVIDE_MATERIALS;
4242 hr = enumobj->lpVtbl->GetChildren(enumobj, &nb_children);
4243 if (FAILED(hr))
4244 goto cleanup;
4246 for (i = 0; i < nb_children; i++)
4248 hr = enumobj->lpVtbl->GetChild(enumobj, i, &filedata);
4249 if (FAILED(hr))
4250 goto cleanup;
4252 hr = filedata->lpVtbl->GetType(filedata, &guid);
4253 if (SUCCEEDED(hr)) {
4254 if (IsEqualGUID(&guid, &TID_D3DRMMesh)) {
4255 container_ptr = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(*container_ptr));
4256 if (!container_ptr) {
4257 hr = E_OUTOFMEMORY;
4258 goto cleanup;
4260 list_add_tail(&container_list, &container_ptr->entry);
4261 D3DXMatrixIdentity(&container_ptr->transform);
4263 hr = D3DXLoadSkinMeshFromXof(filedata, options, device,
4264 (provide_flags & PROVIDE_ADJACENCY) ? &container_ptr->adjacency : NULL,
4265 (provide_flags & PROVIDE_MATERIALS) ? &container_ptr->materials : NULL,
4266 NULL, &container_ptr->num_materials, NULL, &container_ptr->mesh);
4267 } else if (IsEqualGUID(&guid, &TID_D3DRMFrame)) {
4268 hr = parse_frame(filedata, options, device, &identity, &container_list, provide_flags);
4270 if (FAILED(hr)) goto cleanup;
4272 filedata->lpVtbl->Release(filedata);
4273 filedata = NULL;
4274 if (FAILED(hr))
4275 goto cleanup;
4278 enumobj->lpVtbl->Release(enumobj);
4279 enumobj = NULL;
4280 d3dxfile->lpVtbl->Release(d3dxfile);
4281 d3dxfile = NULL;
4283 if (list_empty(&container_list)) {
4284 hr = E_FAIL;
4285 goto cleanup;
4288 fvf = D3DFVF_XYZ;
4289 num_faces = 0;
4290 num_vertices = 0;
4291 num_materials = 0;
4292 LIST_FOR_EACH_ENTRY(container_ptr, &container_list, struct mesh_container, entry)
4294 ID3DXMesh *mesh = container_ptr->mesh;
4295 fvf |= mesh->lpVtbl->GetFVF(mesh);
4296 num_faces += mesh->lpVtbl->GetNumFaces(mesh);
4297 num_vertices += mesh->lpVtbl->GetNumVertices(mesh);
4298 num_materials += container_ptr->num_materials;
4301 hr = D3DXCreateMeshFVF(num_faces, num_vertices, options, fvf, device, &concat_mesh);
4302 if (FAILED(hr)) goto cleanup;
4304 hr = concat_mesh->lpVtbl->GetDeclaration(concat_mesh, concat_decl);
4305 if (FAILED(hr)) goto cleanup;
4307 concat_vertex_size = D3DXGetDeclVertexSize(concat_decl, 0);
4309 hr = concat_mesh->lpVtbl->LockVertexBuffer(concat_mesh, 0, (void**)&concat_vertices);
4310 if (FAILED(hr)) goto cleanup;
4312 LIST_FOR_EACH_ENTRY(container_ptr, &container_list, struct mesh_container, entry)
4314 D3DVERTEXELEMENT9 mesh_decl[MAX_FVF_DECL_SIZE];
4315 ID3DXMesh *mesh = container_ptr->mesh;
4316 DWORD num_mesh_vertices = mesh->lpVtbl->GetNumVertices(mesh);
4317 DWORD mesh_vertex_size;
4318 const BYTE *mesh_vertices;
4319 DWORD i;
4321 hr = mesh->lpVtbl->GetDeclaration(mesh, mesh_decl);
4322 if (FAILED(hr)) goto cleanup;
4324 mesh_vertex_size = D3DXGetDeclVertexSize(mesh_decl, 0);
4326 hr = mesh->lpVtbl->LockVertexBuffer(mesh, D3DLOCK_READONLY, (void**)&mesh_vertices);
4327 if (FAILED(hr)) goto cleanup;
4329 for (i = 0; i < num_mesh_vertices; i++) {
4330 int j;
4331 int k = 1;
4333 D3DXVec3TransformCoord((D3DXVECTOR3*)concat_vertices,
4334 (D3DXVECTOR3*)mesh_vertices,
4335 &container_ptr->transform);
4336 for (j = 1; concat_decl[j].Stream != 0xff; j++)
4338 if (concat_decl[j].Usage == mesh_decl[k].Usage &&
4339 concat_decl[j].UsageIndex == mesh_decl[k].UsageIndex)
4341 if (concat_decl[j].Usage == D3DDECLUSAGE_NORMAL) {
4342 D3DXVec3TransformCoord((D3DXVECTOR3*)(concat_vertices + concat_decl[j].Offset),
4343 (D3DXVECTOR3*)(mesh_vertices + mesh_decl[k].Offset),
4344 &container_ptr->transform);
4345 } else {
4346 memcpy(concat_vertices + concat_decl[j].Offset,
4347 mesh_vertices + mesh_decl[k].Offset,
4348 d3dx_decltype_size[mesh_decl[k].Type]);
4350 k++;
4353 mesh_vertices += mesh_vertex_size;
4354 concat_vertices += concat_vertex_size;
4357 mesh->lpVtbl->UnlockVertexBuffer(mesh);
4360 concat_mesh->lpVtbl->UnlockVertexBuffer(concat_mesh);
4361 concat_vertices = NULL;
4363 hr = concat_mesh->lpVtbl->LockIndexBuffer(concat_mesh, 0, &concat_indices);
4364 if (FAILED(hr)) goto cleanup;
4366 index_offset = 0;
4367 LIST_FOR_EACH_ENTRY(container_ptr, &container_list, struct mesh_container, entry)
4369 ID3DXMesh *mesh = container_ptr->mesh;
4370 const void *mesh_indices;
4371 DWORD num_mesh_faces = mesh->lpVtbl->GetNumFaces(mesh);
4372 DWORD i;
4374 hr = mesh->lpVtbl->LockIndexBuffer(mesh, D3DLOCK_READONLY, (void**)&mesh_indices);
4375 if (FAILED(hr)) goto cleanup;
4377 if (options & D3DXMESH_32BIT) {
4378 DWORD *dest = concat_indices;
4379 const DWORD *src = mesh_indices;
4380 for (i = 0; i < num_mesh_faces * 3; i++)
4381 *dest++ = index_offset + *src++;
4382 concat_indices = dest;
4383 } else {
4384 WORD *dest = concat_indices;
4385 const WORD *src = mesh_indices;
4386 for (i = 0; i < num_mesh_faces * 3; i++)
4387 *dest++ = index_offset + *src++;
4388 concat_indices = dest;
4390 mesh->lpVtbl->UnlockIndexBuffer(mesh);
4392 index_offset += num_mesh_faces * 3;
4395 concat_mesh->lpVtbl->UnlockIndexBuffer(concat_mesh);
4396 concat_indices = NULL;
4398 if (num_materials) {
4399 DWORD *concat_attrib_buffer = NULL;
4400 DWORD offset = 0;
4402 hr = concat_mesh->lpVtbl->LockAttributeBuffer(concat_mesh, 0, &concat_attrib_buffer);
4403 if (FAILED(hr)) goto cleanup;
4405 LIST_FOR_EACH_ENTRY(container_ptr, &container_list, struct mesh_container, entry)
4407 ID3DXMesh *mesh = container_ptr->mesh;
4408 const DWORD *mesh_attrib_buffer = NULL;
4409 DWORD count = mesh->lpVtbl->GetNumFaces(mesh);
4411 hr = mesh->lpVtbl->LockAttributeBuffer(mesh, D3DLOCK_READONLY, (DWORD**)&mesh_attrib_buffer);
4412 if (FAILED(hr)) {
4413 concat_mesh->lpVtbl->UnlockAttributeBuffer(concat_mesh);
4414 goto cleanup;
4417 while (count--)
4418 *concat_attrib_buffer++ = offset + *mesh_attrib_buffer++;
4420 mesh->lpVtbl->UnlockAttributeBuffer(mesh);
4421 offset += container_ptr->num_materials;
4423 concat_mesh->lpVtbl->UnlockAttributeBuffer(concat_mesh);
4426 if (materials_out || effects_out) {
4427 D3DXMATERIAL *out_ptr;
4428 if (!num_materials) {
4429 /* create default material */
4430 hr = D3DXCreateBuffer(sizeof(D3DXMATERIAL), &materials);
4431 if (FAILED(hr)) goto cleanup;
4433 out_ptr = ID3DXBuffer_GetBufferPointer(materials);
4434 out_ptr->MatD3D.Diffuse.r = 0.5f;
4435 out_ptr->MatD3D.Diffuse.g = 0.5f;
4436 out_ptr->MatD3D.Diffuse.b = 0.5f;
4437 out_ptr->MatD3D.Specular.r = 0.5f;
4438 out_ptr->MatD3D.Specular.g = 0.5f;
4439 out_ptr->MatD3D.Specular.b = 0.5f;
4440 /* D3DXCreateBuffer initializes the rest to zero */
4441 } else {
4442 DWORD buffer_size = num_materials * sizeof(D3DXMATERIAL);
4443 char *strings_out_ptr;
4445 LIST_FOR_EACH_ENTRY(container_ptr, &container_list, struct mesh_container, entry)
4447 if (container_ptr->materials) {
4448 DWORD i;
4449 const D3DXMATERIAL *in_ptr = ID3DXBuffer_GetBufferPointer(container_ptr->materials);
4450 for (i = 0; i < container_ptr->num_materials; i++)
4452 if (in_ptr->pTextureFilename)
4453 buffer_size += strlen(in_ptr->pTextureFilename) + 1;
4454 in_ptr++;
4459 hr = D3DXCreateBuffer(buffer_size, &materials);
4460 if (FAILED(hr)) goto cleanup;
4461 out_ptr = ID3DXBuffer_GetBufferPointer(materials);
4462 strings_out_ptr = (char*)(out_ptr + num_materials);
4464 LIST_FOR_EACH_ENTRY(container_ptr, &container_list, struct mesh_container, entry)
4466 if (container_ptr->materials) {
4467 DWORD i;
4468 const D3DXMATERIAL *in_ptr = ID3DXBuffer_GetBufferPointer(container_ptr->materials);
4469 for (i = 0; i < container_ptr->num_materials; i++)
4471 out_ptr->MatD3D = in_ptr->MatD3D;
4472 if (in_ptr->pTextureFilename) {
4473 out_ptr->pTextureFilename = strings_out_ptr;
4474 strcpy(out_ptr->pTextureFilename, in_ptr->pTextureFilename);
4475 strings_out_ptr += strlen(in_ptr->pTextureFilename) + 1;
4477 in_ptr++;
4478 out_ptr++;
4484 if (!num_materials)
4485 num_materials = 1;
4487 if (effects_out) {
4488 generate_effects(materials, num_materials, &effects);
4489 if (!materials_out) {
4490 ID3DXBuffer_Release(materials);
4491 materials = NULL;
4495 if (adjacency_out) {
4496 if (!list_next(&container_list, list_head(&container_list))) {
4497 container_ptr = LIST_ENTRY(list_head(&container_list), struct mesh_container, entry);
4498 adjacency = container_ptr->adjacency;
4499 container_ptr->adjacency = NULL;
4500 } else {
4501 DWORD offset = 0;
4502 DWORD *out_ptr;
4504 hr = D3DXCreateBuffer(num_faces * 3 * sizeof(DWORD), &adjacency);
4505 if (FAILED(hr)) goto cleanup;
4507 out_ptr = ID3DXBuffer_GetBufferPointer(adjacency);
4508 LIST_FOR_EACH_ENTRY(container_ptr, &container_list, struct mesh_container, entry)
4510 DWORD i;
4511 DWORD count = 3 * container_ptr->mesh->lpVtbl->GetNumFaces(container_ptr->mesh);
4512 DWORD *in_ptr = ID3DXBuffer_GetBufferPointer(container_ptr->adjacency);
4514 for (i = 0; i < count; i++)
4515 *out_ptr++ = offset + *in_ptr++;
4517 offset += count;
4522 *mesh_out = concat_mesh;
4523 if (adjacency_out) *adjacency_out = adjacency;
4524 if (materials_out) *materials_out = materials;
4525 if (effects_out) *effects_out = effects;
4526 if (num_materials_out) *num_materials_out = num_materials;
4528 hr = D3D_OK;
4529 cleanup:
4530 if (concat_indices) concat_mesh->lpVtbl->UnlockIndexBuffer(concat_mesh);
4531 if (concat_vertices) concat_mesh->lpVtbl->UnlockVertexBuffer(concat_mesh);
4532 if (filedata) filedata->lpVtbl->Release(filedata);
4533 if (enumobj) enumobj->lpVtbl->Release(enumobj);
4534 if (d3dxfile) d3dxfile->lpVtbl->Release(d3dxfile);
4535 if (FAILED(hr)) {
4536 if (concat_mesh) IUnknown_Release(concat_mesh);
4537 if (materials) ID3DXBuffer_Release(materials);
4538 if (effects) ID3DXBuffer_Release(effects);
4539 if (adjacency) ID3DXBuffer_Release(adjacency);
4541 LIST_FOR_EACH_ENTRY_SAFE(container_ptr, next_container_ptr, &container_list, struct mesh_container, entry)
4543 if (container_ptr->mesh) IUnknown_Release(container_ptr->mesh);
4544 if (container_ptr->adjacency) ID3DXBuffer_Release(container_ptr->adjacency);
4545 if (container_ptr->materials) ID3DXBuffer_Release(container_ptr->materials);
4546 if (container_ptr->effects) ID3DXBuffer_Release(container_ptr->effects);
4547 HeapFree(GetProcessHeap(), 0, container_ptr);
4549 return hr;
4552 struct vertex
4554 D3DXVECTOR3 position;
4555 D3DXVECTOR3 normal;
4558 HRESULT WINAPI D3DXCreateBox(struct IDirect3DDevice9 *device, float width, float height,
4559 float depth, struct ID3DXMesh **mesh, struct ID3DXBuffer **adjacency)
4561 HRESULT hr;
4562 ID3DXMesh *box;
4563 struct vertex *vertices;
4564 WORD (*faces)[3];
4565 DWORD *adjacency_buf;
4566 unsigned int i, face;
4567 static const D3DXVECTOR3 unit_box[] =
4569 {-0.5f, -0.5f, -0.5f}, {-0.5f, -0.5f, 0.5f}, {-0.5f, 0.5f, 0.5f}, {-0.5f, 0.5f, -0.5f},
4570 {-0.5f, 0.5f, -0.5f}, {-0.5f, 0.5f, 0.5f}, { 0.5f, 0.5f, 0.5f}, { 0.5f, 0.5f, -0.5f},
4571 { 0.5f, 0.5f, -0.5f}, { 0.5f, 0.5f, 0.5f}, { 0.5f, -0.5f, 0.5f}, { 0.5f, -0.5f, -0.5f},
4572 {-0.5f, -0.5f, 0.5f}, {-0.5f, -0.5f, -0.5f}, { 0.5f, -0.5f, -0.5f}, { 0.5f, -0.5f, 0.5f},
4573 {-0.5f, -0.5f, 0.5f}, { 0.5f, -0.5f, 0.5f}, { 0.5f, 0.5f, 0.5f}, {-0.5f, 0.5f, 0.5f},
4574 {-0.5f, -0.5f, -0.5f}, {-0.5f, 0.5f, -0.5f}, { 0.5f, 0.5f, -0.5f}, { 0.5f, -0.5f, -0.5f}
4576 static const D3DXVECTOR3 normals[] =
4578 {-1.0f, 0.0f, 0.0f}, { 0.0f, 1.0f, 0.0f}, { 1.0f, 0.0f, 0.0f},
4579 { 0.0f, -1.0f, 0.0f}, { 0.0f, 0.0f, 1.0f}, { 0.0f, 0.0f, -1.0f}
4581 static const DWORD adjacency_table[] =
4583 6, 9, 1, 2, 10, 0, 1, 9, 3, 4, 10, 2,
4584 3, 8, 5, 7, 11, 4, 0, 11, 7, 5, 8, 6,
4585 7, 4, 9, 2, 0, 8, 1, 3, 11, 5, 6, 10
4588 TRACE("device %p, width %f, height %f, depth %f, mesh %p, adjacency %p\n",
4589 device, width, height, depth, mesh, adjacency);
4591 if (!device || width < 0.0f || height < 0.0f || depth < 0.0f || !mesh)
4593 return D3DERR_INVALIDCALL;
4596 if (FAILED(hr = D3DXCreateMeshFVF(12, 24, D3DXMESH_MANAGED, D3DFVF_XYZ | D3DFVF_NORMAL, device, &box)))
4598 return hr;
4601 if (FAILED(hr = box->lpVtbl->LockVertexBuffer(box, 0, (void **)&vertices)))
4603 box->lpVtbl->Release(box);
4604 return hr;
4607 if (FAILED(hr = box->lpVtbl->LockIndexBuffer(box, 0, (void **)&faces)))
4609 box->lpVtbl->UnlockVertexBuffer(box);
4610 box->lpVtbl->Release(box);
4611 return hr;
4614 for (i = 0; i < 24; i++)
4616 vertices[i].position.x = width * unit_box[i].x;
4617 vertices[i].position.y = height * unit_box[i].y;
4618 vertices[i].position.z = depth * unit_box[i].z;
4619 vertices[i].normal.x = normals[i / 4].x;
4620 vertices[i].normal.y = normals[i / 4].y;
4621 vertices[i].normal.z = normals[i / 4].z;
4624 face = 0;
4625 for (i = 0; i < 12; i++)
4627 faces[i][0] = face++;
4628 faces[i][1] = face++;
4629 faces[i][2] = (i % 2) ? face - 4 : face;
4632 box->lpVtbl->UnlockIndexBuffer(box);
4633 box->lpVtbl->UnlockVertexBuffer(box);
4635 if (adjacency)
4637 if (FAILED(hr = D3DXCreateBuffer(sizeof(adjacency_table), adjacency)))
4639 box->lpVtbl->Release(box);
4640 return hr;
4643 adjacency_buf = ID3DXBuffer_GetBufferPointer(*adjacency);
4644 memcpy(adjacency_buf, adjacency_table, sizeof(adjacency_table));
4647 *mesh = box;
4649 return D3D_OK;
4652 typedef WORD face[3];
4654 struct sincos_table
4656 float *sin;
4657 float *cos;
4660 static void free_sincos_table(struct sincos_table *sincos_table)
4662 HeapFree(GetProcessHeap(), 0, sincos_table->cos);
4663 HeapFree(GetProcessHeap(), 0, sincos_table->sin);
4666 /* pre compute sine and cosine tables; caller must free */
4667 static BOOL compute_sincos_table(struct sincos_table *sincos_table, float angle_start, float angle_step, int n)
4669 float angle;
4670 int i;
4672 sincos_table->sin = HeapAlloc(GetProcessHeap(), 0, n * sizeof(*sincos_table->sin));
4673 if (!sincos_table->sin)
4675 return FALSE;
4677 sincos_table->cos = HeapAlloc(GetProcessHeap(), 0, n * sizeof(*sincos_table->cos));
4678 if (!sincos_table->cos)
4680 HeapFree(GetProcessHeap(), 0, sincos_table->sin);
4681 return FALSE;
4684 angle = angle_start;
4685 for (i = 0; i < n; i++)
4687 sincos_table->sin[i] = sinf(angle);
4688 sincos_table->cos[i] = cosf(angle);
4689 angle += angle_step;
4692 return TRUE;
4695 static WORD vertex_index(UINT slices, int slice, int stack)
4697 return stack*slices+slice+1;
4700 HRESULT WINAPI D3DXCreateSphere(struct IDirect3DDevice9 *device, float radius, UINT slices,
4701 UINT stacks, struct ID3DXMesh **mesh, struct ID3DXBuffer **adjacency)
4703 DWORD number_of_vertices, number_of_faces;
4704 HRESULT hr;
4705 ID3DXMesh *sphere;
4706 struct vertex *vertices;
4707 face *faces;
4708 float phi_step, phi_start;
4709 struct sincos_table phi;
4710 float theta_step, theta, sin_theta, cos_theta;
4711 DWORD vertex, face, stack, slice;
4713 TRACE("(%p, %f, %u, %u, %p, %p)\n", device, radius, slices, stacks, mesh, adjacency);
4715 if (!device || radius < 0.0f || slices < 2 || stacks < 2 || !mesh)
4717 return D3DERR_INVALIDCALL;
4720 number_of_vertices = 2 + slices * (stacks-1);
4721 number_of_faces = 2 * slices + (stacks - 2) * (2 * slices);
4723 hr = D3DXCreateMeshFVF(number_of_faces, number_of_vertices, D3DXMESH_MANAGED,
4724 D3DFVF_XYZ | D3DFVF_NORMAL, device, &sphere);
4725 if (FAILED(hr))
4727 return hr;
4730 if (FAILED(hr = sphere->lpVtbl->LockVertexBuffer(sphere, 0, (void **)&vertices)))
4732 sphere->lpVtbl->Release(sphere);
4733 return hr;
4736 if (FAILED(hr = sphere->lpVtbl->LockIndexBuffer(sphere, 0, (void **)&faces)))
4738 sphere->lpVtbl->UnlockVertexBuffer(sphere);
4739 sphere->lpVtbl->Release(sphere);
4740 return hr;
4743 /* phi = angle on xz plane wrt z axis */
4744 phi_step = -2.0f * D3DX_PI / slices;
4745 phi_start = D3DX_PI / 2.0f;
4747 if (!compute_sincos_table(&phi, phi_start, phi_step, slices))
4749 sphere->lpVtbl->UnlockIndexBuffer(sphere);
4750 sphere->lpVtbl->UnlockVertexBuffer(sphere);
4751 sphere->lpVtbl->Release(sphere);
4752 return E_OUTOFMEMORY;
4755 /* theta = angle on xy plane wrt x axis */
4756 theta_step = D3DX_PI / stacks;
4757 theta = theta_step;
4759 vertex = 0;
4760 face = 0;
4762 vertices[vertex].normal.x = 0.0f;
4763 vertices[vertex].normal.y = 0.0f;
4764 vertices[vertex].normal.z = 1.0f;
4765 vertices[vertex].position.x = 0.0f;
4766 vertices[vertex].position.y = 0.0f;
4767 vertices[vertex].position.z = radius;
4768 vertex++;
4770 for (stack = 0; stack < stacks - 1; stack++)
4772 sin_theta = sinf(theta);
4773 cos_theta = cosf(theta);
4775 for (slice = 0; slice < slices; slice++)
4777 vertices[vertex].normal.x = sin_theta * phi.cos[slice];
4778 vertices[vertex].normal.y = sin_theta * phi.sin[slice];
4779 vertices[vertex].normal.z = cos_theta;
4780 vertices[vertex].position.x = radius * sin_theta * phi.cos[slice];
4781 vertices[vertex].position.y = radius * sin_theta * phi.sin[slice];
4782 vertices[vertex].position.z = radius * cos_theta;
4783 vertex++;
4785 if (slice > 0)
4787 if (stack == 0)
4789 /* top stack is triangle fan */
4790 faces[face][0] = 0;
4791 faces[face][1] = slice + 1;
4792 faces[face][2] = slice;
4793 face++;
4795 else
4797 /* stacks in between top and bottom are quad strips */
4798 faces[face][0] = vertex_index(slices, slice-1, stack-1);
4799 faces[face][1] = vertex_index(slices, slice, stack-1);
4800 faces[face][2] = vertex_index(slices, slice-1, stack);
4801 face++;
4803 faces[face][0] = vertex_index(slices, slice, stack-1);
4804 faces[face][1] = vertex_index(slices, slice, stack);
4805 faces[face][2] = vertex_index(slices, slice-1, stack);
4806 face++;
4811 theta += theta_step;
4813 if (stack == 0)
4815 faces[face][0] = 0;
4816 faces[face][1] = 1;
4817 faces[face][2] = slice;
4818 face++;
4820 else
4822 faces[face][0] = vertex_index(slices, slice-1, stack-1);
4823 faces[face][1] = vertex_index(slices, 0, stack-1);
4824 faces[face][2] = vertex_index(slices, slice-1, stack);
4825 face++;
4827 faces[face][0] = vertex_index(slices, 0, stack-1);
4828 faces[face][1] = vertex_index(slices, 0, stack);
4829 faces[face][2] = vertex_index(slices, slice-1, stack);
4830 face++;
4834 vertices[vertex].position.x = 0.0f;
4835 vertices[vertex].position.y = 0.0f;
4836 vertices[vertex].position.z = -radius;
4837 vertices[vertex].normal.x = 0.0f;
4838 vertices[vertex].normal.y = 0.0f;
4839 vertices[vertex].normal.z = -1.0f;
4841 /* bottom stack is triangle fan */
4842 for (slice = 1; slice < slices; slice++)
4844 faces[face][0] = vertex_index(slices, slice-1, stack-1);
4845 faces[face][1] = vertex_index(slices, slice, stack-1);
4846 faces[face][2] = vertex;
4847 face++;
4850 faces[face][0] = vertex_index(slices, slice-1, stack-1);
4851 faces[face][1] = vertex_index(slices, 0, stack-1);
4852 faces[face][2] = vertex;
4854 free_sincos_table(&phi);
4855 sphere->lpVtbl->UnlockIndexBuffer(sphere);
4856 sphere->lpVtbl->UnlockVertexBuffer(sphere);
4859 if (adjacency)
4861 if (FAILED(hr = D3DXCreateBuffer(number_of_faces * sizeof(DWORD) * 3, adjacency)))
4863 sphere->lpVtbl->Release(sphere);
4864 return hr;
4867 if (FAILED(hr = sphere->lpVtbl->GenerateAdjacency(sphere, 0.0f, (*adjacency)->lpVtbl->GetBufferPointer(*adjacency))))
4869 (*adjacency)->lpVtbl->Release(*adjacency);
4870 sphere->lpVtbl->Release(sphere);
4871 return hr;
4875 *mesh = sphere;
4877 return D3D_OK;
4880 HRESULT WINAPI D3DXCreateCylinder(struct IDirect3DDevice9 *device, float radius1, float radius2,
4881 float length, UINT slices, UINT stacks, struct ID3DXMesh **mesh, struct ID3DXBuffer **adjacency)
4883 DWORD number_of_vertices, number_of_faces;
4884 HRESULT hr;
4885 ID3DXMesh *cylinder;
4886 struct vertex *vertices;
4887 face *faces;
4888 float theta_step, theta_start;
4889 struct sincos_table theta;
4890 float delta_radius, radius, radius_step;
4891 float z, z_step, z_normal;
4892 DWORD vertex, face, slice, stack;
4894 TRACE("(%p, %f, %f, %f, %u, %u, %p, %p)\n", device, radius1, radius2, length, slices, stacks, mesh, adjacency);
4896 if (device == NULL || radius1 < 0.0f || radius2 < 0.0f || length < 0.0f || slices < 2 || stacks < 1 || mesh == NULL)
4898 return D3DERR_INVALIDCALL;
4901 if (adjacency)
4903 FIXME("Case of adjacency != NULL not implemented.\n");
4904 return E_NOTIMPL;
4907 number_of_vertices = 2 + (slices * (3 + stacks));
4908 number_of_faces = 2 * slices + stacks * (2 * slices);
4910 hr = D3DXCreateMeshFVF(number_of_faces, number_of_vertices, D3DXMESH_MANAGED,
4911 D3DFVF_XYZ | D3DFVF_NORMAL, device, &cylinder);
4912 if (FAILED(hr))
4914 return hr;
4917 if (FAILED(hr = cylinder->lpVtbl->LockVertexBuffer(cylinder, 0, (void **)&vertices)))
4919 cylinder->lpVtbl->Release(cylinder);
4920 return hr;
4923 if (FAILED(hr = cylinder->lpVtbl->LockIndexBuffer(cylinder, 0, (void **)&faces)))
4925 cylinder->lpVtbl->UnlockVertexBuffer(cylinder);
4926 cylinder->lpVtbl->Release(cylinder);
4927 return hr;
4930 /* theta = angle on xy plane wrt x axis */
4931 theta_step = -2.0f * D3DX_PI / slices;
4932 theta_start = D3DX_PI / 2.0f;
4934 if (!compute_sincos_table(&theta, theta_start, theta_step, slices))
4936 cylinder->lpVtbl->UnlockIndexBuffer(cylinder);
4937 cylinder->lpVtbl->UnlockVertexBuffer(cylinder);
4938 cylinder->lpVtbl->Release(cylinder);
4939 return E_OUTOFMEMORY;
4942 vertex = 0;
4943 face = 0;
4945 delta_radius = radius1 - radius2;
4946 radius = radius1;
4947 radius_step = delta_radius / stacks;
4949 z = -length / 2;
4950 z_step = length / stacks;
4951 z_normal = delta_radius / length;
4952 if (isnan(z_normal))
4954 z_normal = 0.0f;
4957 vertices[vertex].normal.x = 0.0f;
4958 vertices[vertex].normal.y = 0.0f;
4959 vertices[vertex].normal.z = -1.0f;
4960 vertices[vertex].position.x = 0.0f;
4961 vertices[vertex].position.y = 0.0f;
4962 vertices[vertex++].position.z = z;
4964 for (slice = 0; slice < slices; slice++, vertex++)
4966 vertices[vertex].normal.x = 0.0f;
4967 vertices[vertex].normal.y = 0.0f;
4968 vertices[vertex].normal.z = -1.0f;
4969 vertices[vertex].position.x = radius * theta.cos[slice];
4970 vertices[vertex].position.y = radius * theta.sin[slice];
4971 vertices[vertex].position.z = z;
4973 if (slice > 0)
4975 faces[face][0] = 0;
4976 faces[face][1] = slice;
4977 faces[face++][2] = slice + 1;
4981 faces[face][0] = 0;
4982 faces[face][1] = slice;
4983 faces[face++][2] = 1;
4985 for (stack = 1; stack <= stacks+1; stack++)
4987 for (slice = 0; slice < slices; slice++, vertex++)
4989 vertices[vertex].normal.x = theta.cos[slice];
4990 vertices[vertex].normal.y = theta.sin[slice];
4991 vertices[vertex].normal.z = z_normal;
4992 D3DXVec3Normalize(&vertices[vertex].normal, &vertices[vertex].normal);
4993 vertices[vertex].position.x = radius * theta.cos[slice];
4994 vertices[vertex].position.y = radius * theta.sin[slice];
4995 vertices[vertex].position.z = z;
4997 if (stack > 1 && slice > 0)
4999 faces[face][0] = vertex_index(slices, slice-1, stack-1);
5000 faces[face][1] = vertex_index(slices, slice-1, stack);
5001 faces[face++][2] = vertex_index(slices, slice, stack-1);
5003 faces[face][0] = vertex_index(slices, slice, stack-1);
5004 faces[face][1] = vertex_index(slices, slice-1, stack);
5005 faces[face++][2] = vertex_index(slices, slice, stack);
5009 if (stack > 1)
5011 faces[face][0] = vertex_index(slices, slice-1, stack-1);
5012 faces[face][1] = vertex_index(slices, slice-1, stack);
5013 faces[face++][2] = vertex_index(slices, 0, stack-1);
5015 faces[face][0] = vertex_index(slices, 0, stack-1);
5016 faces[face][1] = vertex_index(slices, slice-1, stack);
5017 faces[face++][2] = vertex_index(slices, 0, stack);
5020 if (stack < stacks + 1)
5022 z += z_step;
5023 radius -= radius_step;
5027 for (slice = 0; slice < slices; slice++, vertex++)
5029 vertices[vertex].normal.x = 0.0f;
5030 vertices[vertex].normal.y = 0.0f;
5031 vertices[vertex].normal.z = 1.0f;
5032 vertices[vertex].position.x = radius * theta.cos[slice];
5033 vertices[vertex].position.y = radius * theta.sin[slice];
5034 vertices[vertex].position.z = z;
5036 if (slice > 0)
5038 faces[face][0] = vertex_index(slices, slice-1, stack);
5039 faces[face][1] = number_of_vertices - 1;
5040 faces[face++][2] = vertex_index(slices, slice, stack);
5044 vertices[vertex].position.x = 0.0f;
5045 vertices[vertex].position.y = 0.0f;
5046 vertices[vertex].position.z = z;
5047 vertices[vertex].normal.x = 0.0f;
5048 vertices[vertex].normal.y = 0.0f;
5049 vertices[vertex].normal.z = 1.0f;
5051 faces[face][0] = vertex_index(slices, slice-1, stack);
5052 faces[face][1] = number_of_vertices - 1;
5053 faces[face][2] = vertex_index(slices, 0, stack);
5055 free_sincos_table(&theta);
5056 cylinder->lpVtbl->UnlockIndexBuffer(cylinder);
5057 cylinder->lpVtbl->UnlockVertexBuffer(cylinder);
5058 *mesh = cylinder;
5060 return D3D_OK;
5063 HRESULT WINAPI D3DXCreateTeapot(struct IDirect3DDevice9 *device,
5064 struct ID3DXMesh **mesh, struct ID3DXBuffer **adjacency)
5066 FIXME("(%p, %p, %p): stub\n", device, mesh, adjacency);
5068 return E_NOTIMPL;
5071 HRESULT WINAPI D3DXCreateTextA(struct IDirect3DDevice9 *device, HDC hdc, const char *text, float deviation,
5072 float extrusion, struct ID3DXMesh **mesh, struct ID3DXBuffer **adjacency, GLYPHMETRICSFLOAT *glyphmetrics)
5074 WCHAR *textW;
5075 HRESULT hr;
5076 int len;
5078 TRACE("device %p, hdc %p, text %s, deviation %.8e, extrusion %.8e, mesh %p, adjacency %p, glyphmetrics %p.\n",
5079 device, hdc, debugstr_a(text), deviation, extrusion, mesh, adjacency, glyphmetrics);
5081 if (!text)
5082 return D3DERR_INVALIDCALL;
5084 len = MultiByteToWideChar(CP_ACP, 0, text, -1, NULL, 0);
5085 textW = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
5086 MultiByteToWideChar(CP_ACP, 0, text, -1, textW, len);
5088 hr = D3DXCreateTextW(device, hdc, textW, deviation, extrusion,
5089 mesh, adjacency, glyphmetrics);
5090 HeapFree(GetProcessHeap(), 0, textW);
5092 return hr;
5095 HRESULT WINAPI D3DXCreateTorus(struct IDirect3DDevice9 *device,
5096 float innerradius, float outerradius, UINT sides, UINT rings, struct ID3DXMesh **mesh, ID3DXBuffer **adjacency)
5098 HRESULT hr;
5099 ID3DXMesh *torus;
5100 WORD (*faces)[3];
5101 struct vertex *vertices;
5102 float phi, phi_step, sin_phi, cos_phi;
5103 float theta, theta_step, sin_theta, cos_theta;
5104 unsigned int i, j, numvert, numfaces;
5106 TRACE("device %p, innerradius %.8e, outerradius %.8e, sides %u, rings %u, mesh %p, adjacency %p.\n",
5107 device, innerradius, outerradius, sides, rings, mesh, adjacency);
5109 numvert = sides * rings;
5110 numfaces = numvert * 2;
5112 if (!device || innerradius < 0.0f || outerradius < 0.0f || sides < 3 || rings < 3 || !mesh)
5114 WARN("Invalid arguments.\n");
5115 return D3DERR_INVALIDCALL;
5118 if (FAILED(hr = D3DXCreateMeshFVF(numfaces, numvert, D3DXMESH_MANAGED, D3DFVF_XYZ | D3DFVF_NORMAL, device, &torus)))
5119 return hr;
5121 if (FAILED(hr = torus->lpVtbl->LockVertexBuffer(torus, 0, (void **)&vertices)))
5123 torus->lpVtbl->Release(torus);
5124 return hr;
5127 if (FAILED(hr = torus->lpVtbl->LockIndexBuffer(torus, 0, (void **)&faces)))
5129 torus->lpVtbl->UnlockVertexBuffer(torus);
5130 torus->lpVtbl->Release(torus);
5131 return hr;
5134 phi_step = D3DX_PI / sides * 2.0f;
5135 theta_step = D3DX_PI / rings * -2.0f;
5137 theta = 0.0f;
5139 for (i = 0; i < rings; ++i)
5141 phi = 0.0f;
5143 sin_theta = sinf(theta);
5144 cos_theta = cosf(theta);
5146 for (j = 0; j < sides; ++j)
5148 sin_phi = sinf(phi);
5149 cos_phi = cosf(phi);
5151 vertices[i * sides + j].position.x = (innerradius * cos_phi + outerradius) * cos_theta;
5152 vertices[i * sides + j].position.y = (innerradius * cos_phi + outerradius) * sin_theta;
5153 vertices[i * sides + j].position.z = innerradius * sin_phi;
5154 vertices[i * sides + j].normal.x = cos_phi * cos_theta;
5155 vertices[i * sides + j].normal.y = cos_phi * sin_theta;
5156 vertices[i * sides + j].normal.z = sin_phi;
5158 phi += phi_step;
5161 theta += theta_step;
5164 for (i = 0; i < numfaces - sides * 2; ++i)
5166 faces[i][0] = i % 2 ? i / 2 + sides : i / 2;
5167 faces[i][1] = (i / 2 + 1) % sides ? i / 2 + 1 : i / 2 + 1 - sides;
5168 faces[i][2] = (i + 1) % (sides * 2) ? (i + 1) / 2 + sides : (i + 1) / 2;
5171 for (j = 0; i < numfaces; ++i, ++j)
5173 faces[i][0] = i % 2 ? j / 2 : i / 2;
5174 faces[i][1] = (i / 2 + 1) % sides ? i / 2 + 1 : i / 2 + 1 - sides;
5175 faces[i][2] = i == numfaces - 1 ? 0 : (j + 1) / 2;
5178 torus->lpVtbl->UnlockIndexBuffer(torus);
5179 torus->lpVtbl->UnlockVertexBuffer(torus);
5181 if (adjacency)
5183 if (FAILED(hr = D3DXCreateBuffer(numfaces * sizeof(DWORD) * 3, adjacency)))
5185 torus->lpVtbl->Release(torus);
5186 return hr;
5189 if (FAILED(hr = torus->lpVtbl->GenerateAdjacency(torus, 0.0f, (*adjacency)->lpVtbl->GetBufferPointer(*adjacency))))
5191 (*adjacency)->lpVtbl->Release(*adjacency);
5192 torus->lpVtbl->Release(torus);
5193 return hr;
5197 *mesh = torus;
5199 return D3D_OK;
5202 enum pointtype {
5203 POINTTYPE_CURVE = 0,
5204 POINTTYPE_CORNER,
5205 POINTTYPE_CURVE_START,
5206 POINTTYPE_CURVE_END,
5207 POINTTYPE_CURVE_MIDDLE,
5210 struct point2d
5212 D3DXVECTOR2 pos;
5213 enum pointtype corner;
5216 struct dynamic_array
5218 int count, capacity;
5219 void *items;
5222 /* is a dynamic_array */
5223 struct outline
5225 int count, capacity;
5226 struct point2d *items;
5229 /* is a dynamic_array */
5230 struct outline_array
5232 int count, capacity;
5233 struct outline *items;
5236 struct face_array
5238 int count;
5239 face *items;
5242 struct point2d_index
5244 struct outline *outline;
5245 int vertex;
5248 struct point2d_index_array
5250 int count;
5251 struct point2d_index *items;
5254 struct glyphinfo
5256 struct outline_array outlines;
5257 struct face_array faces;
5258 struct point2d_index_array ordered_vertices;
5259 float offset_x;
5262 /* is an dynamic_array */
5263 struct word_array
5265 int count, capacity;
5266 WORD *items;
5269 /* complex polygons are split into monotone polygons, which have
5270 * at most 2 intersections with the vertical sweep line */
5271 struct triangulation
5273 struct word_array vertex_stack;
5274 BOOL last_on_top, merging;
5277 /* is an dynamic_array */
5278 struct triangulation_array
5280 int count, capacity;
5281 struct triangulation *items;
5283 struct glyphinfo *glyph;
5286 static BOOL reserve(struct dynamic_array *array, int count, int itemsize)
5288 if (count > array->capacity) {
5289 void *new_buffer;
5290 int new_capacity;
5291 if (array->items && array->capacity) {
5292 new_capacity = max(array->capacity * 2, count);
5293 new_buffer = HeapReAlloc(GetProcessHeap(), 0, array->items, new_capacity * itemsize);
5294 } else {
5295 new_capacity = max(16, count);
5296 new_buffer = HeapAlloc(GetProcessHeap(), 0, new_capacity * itemsize);
5298 if (!new_buffer)
5299 return FALSE;
5300 array->items = new_buffer;
5301 array->capacity = new_capacity;
5303 return TRUE;
5306 static struct point2d *add_points(struct outline *array, int num)
5308 struct point2d *item;
5310 if (!reserve((struct dynamic_array *)array, array->count + num, sizeof(array->items[0])))
5311 return NULL;
5313 item = &array->items[array->count];
5314 array->count += num;
5315 return item;
5318 static struct outline *add_outline(struct outline_array *array)
5320 struct outline *item;
5322 if (!reserve((struct dynamic_array *)array, array->count + 1, sizeof(array->items[0])))
5323 return NULL;
5325 item = &array->items[array->count++];
5326 ZeroMemory(item, sizeof(*item));
5327 return item;
5330 static inline face *add_face(struct face_array *array)
5332 return &array->items[array->count++];
5335 static struct triangulation *add_triangulation(struct triangulation_array *array)
5337 struct triangulation *item;
5339 if (!reserve((struct dynamic_array *)array, array->count + 1, sizeof(array->items[0])))
5340 return NULL;
5342 item = &array->items[array->count++];
5343 ZeroMemory(item, sizeof(*item));
5344 return item;
5347 static HRESULT add_vertex_index(struct word_array *array, WORD vertex_index)
5349 if (!reserve((struct dynamic_array *)array, array->count + 1, sizeof(array->items[0])))
5350 return E_OUTOFMEMORY;
5352 array->items[array->count++] = vertex_index;
5353 return S_OK;
5356 /* assume fixed point numbers can be converted to float point in place */
5357 C_ASSERT(sizeof(FIXED) == sizeof(float));
5358 C_ASSERT(sizeof(POINTFX) == sizeof(D3DXVECTOR2));
5360 static inline D3DXVECTOR2 *convert_fixed_to_float(POINTFX *pt, int count, unsigned int emsquare)
5362 D3DXVECTOR2 *ret = (D3DXVECTOR2*)pt;
5363 while (count--) {
5364 D3DXVECTOR2 *pt_flt = (D3DXVECTOR2*)pt;
5365 pt_flt->x = (pt->x.value + pt->x.fract / (float)0x10000) / emsquare;
5366 pt_flt->y = (pt->y.value + pt->y.fract / (float)0x10000) / emsquare;
5367 pt++;
5369 return ret;
5372 static HRESULT add_bezier_points(struct outline *outline, const D3DXVECTOR2 *p1,
5373 const D3DXVECTOR2 *p2, const D3DXVECTOR2 *p3,
5374 float max_deviation_sq)
5376 D3DXVECTOR2 split1 = {0, 0}, split2 = {0, 0}, middle, vec;
5377 float deviation_sq;
5379 D3DXVec2Scale(&split1, D3DXVec2Add(&split1, p1, p2), 0.5f);
5380 D3DXVec2Scale(&split2, D3DXVec2Add(&split2, p2, p3), 0.5f);
5381 D3DXVec2Scale(&middle, D3DXVec2Add(&middle, &split1, &split2), 0.5f);
5383 deviation_sq = D3DXVec2LengthSq(D3DXVec2Subtract(&vec, &middle, p2));
5384 if (deviation_sq < max_deviation_sq) {
5385 struct point2d *pt = add_points(outline, 1);
5386 if (!pt) return E_OUTOFMEMORY;
5387 pt->pos = *p2;
5388 pt->corner = POINTTYPE_CURVE;
5389 /* the end point is omitted because the end line merges into the next segment of
5390 * the split bezier curve, and the end of the split bezier curve is added outside
5391 * this recursive function. */
5392 } else {
5393 HRESULT hr = add_bezier_points(outline, p1, &split1, &middle, max_deviation_sq);
5394 if (hr != S_OK) return hr;
5395 hr = add_bezier_points(outline, &middle, &split2, p3, max_deviation_sq);
5396 if (hr != S_OK) return hr;
5399 return S_OK;
5402 static inline BOOL is_direction_similar(D3DXVECTOR2 *dir1, D3DXVECTOR2 *dir2, float cos_theta)
5404 /* dot product = cos(theta) */
5405 return D3DXVec2Dot(dir1, dir2) > cos_theta;
5408 static inline D3DXVECTOR2 *unit_vec2(D3DXVECTOR2 *dir, const D3DXVECTOR2 *pt1, const D3DXVECTOR2 *pt2)
5410 return D3DXVec2Normalize(D3DXVec2Subtract(dir, pt2, pt1), dir);
5413 struct cos_table
5415 float cos_half;
5416 float cos_45;
5417 float cos_90;
5420 static BOOL attempt_line_merge(struct outline *outline,
5421 int pt_index,
5422 const D3DXVECTOR2 *nextpt,
5423 BOOL to_curve,
5424 const struct cos_table *table)
5426 D3DXVECTOR2 curdir, lastdir;
5427 struct point2d *prevpt, *pt;
5428 BOOL ret = FALSE;
5430 pt = &outline->items[pt_index];
5431 pt_index = (pt_index - 1 + outline->count) % outline->count;
5432 prevpt = &outline->items[pt_index];
5434 if (to_curve)
5435 pt->corner = pt->corner != POINTTYPE_CORNER ? POINTTYPE_CURVE_MIDDLE : POINTTYPE_CURVE_START;
5437 if (outline->count < 2)
5438 return FALSE;
5440 /* remove last point if the next line continues the last line */
5441 unit_vec2(&lastdir, &prevpt->pos, &pt->pos);
5442 unit_vec2(&curdir, &pt->pos, nextpt);
5443 if (is_direction_similar(&lastdir, &curdir, table->cos_half))
5445 outline->count--;
5446 if (pt->corner == POINTTYPE_CURVE_END)
5447 prevpt->corner = pt->corner;
5448 if (prevpt->corner == POINTTYPE_CURVE_END && to_curve)
5449 prevpt->corner = POINTTYPE_CURVE_MIDDLE;
5450 pt = prevpt;
5452 ret = TRUE;
5453 if (outline->count < 2)
5454 return ret;
5456 pt_index = (pt_index - 1 + outline->count) % outline->count;
5457 prevpt = &outline->items[pt_index];
5458 unit_vec2(&lastdir, &prevpt->pos, &pt->pos);
5459 unit_vec2(&curdir, &pt->pos, nextpt);
5461 return ret;
5464 static HRESULT create_outline(struct glyphinfo *glyph, void *raw_outline, int datasize,
5465 float max_deviation_sq, unsigned int emsquare,
5466 const struct cos_table *cos_table)
5468 TTPOLYGONHEADER *header = (TTPOLYGONHEADER *)raw_outline;
5470 while ((char *)header < (char *)raw_outline + datasize)
5472 TTPOLYCURVE *curve = (TTPOLYCURVE *)(header + 1);
5473 struct point2d *lastpt, *pt;
5474 D3DXVECTOR2 lastdir;
5475 D3DXVECTOR2 *pt_flt;
5476 int j;
5477 struct outline *outline = add_outline(&glyph->outlines);
5479 if (!outline)
5480 return E_OUTOFMEMORY;
5482 pt = add_points(outline, 1);
5483 if (!pt)
5484 return E_OUTOFMEMORY;
5485 pt_flt = convert_fixed_to_float(&header->pfxStart, 1, emsquare);
5486 pt->pos = *pt_flt;
5487 pt->corner = POINTTYPE_CORNER;
5489 if (header->dwType != TT_POLYGON_TYPE)
5490 FIXME("Unknown header type %d\n", header->dwType);
5492 while ((char *)curve < (char *)header + header->cb)
5494 D3DXVECTOR2 bezier_start = outline->items[outline->count - 1].pos;
5495 BOOL to_curve = curve->wType != TT_PRIM_LINE && curve->cpfx > 1;
5496 unsigned int j2 = 0;
5498 if (!curve->cpfx) {
5499 curve = (TTPOLYCURVE *)&curve->apfx[curve->cpfx];
5500 continue;
5503 pt_flt = convert_fixed_to_float(curve->apfx, curve->cpfx, emsquare);
5505 attempt_line_merge(outline, outline->count - 1, &pt_flt[0], to_curve, cos_table);
5507 if (to_curve)
5509 HRESULT hr;
5510 int count = curve->cpfx;
5512 while (count > 2)
5514 D3DXVECTOR2 bezier_end;
5516 D3DXVec2Scale(&bezier_end, D3DXVec2Add(&bezier_end, &pt_flt[j2], &pt_flt[j2+1]), 0.5f);
5517 hr = add_bezier_points(outline, &bezier_start, &pt_flt[j2], &bezier_end, max_deviation_sq);
5518 if (hr != S_OK)
5519 return hr;
5520 bezier_start = bezier_end;
5521 count--;
5522 j2++;
5524 hr = add_bezier_points(outline, &bezier_start, &pt_flt[j2], &pt_flt[j2+1], max_deviation_sq);
5525 if (hr != S_OK)
5526 return hr;
5528 pt = add_points(outline, 1);
5529 if (!pt)
5530 return E_OUTOFMEMORY;
5531 j2++;
5532 pt->pos = pt_flt[j2];
5533 pt->corner = POINTTYPE_CURVE_END;
5534 } else {
5535 pt = add_points(outline, curve->cpfx);
5536 if (!pt)
5537 return E_OUTOFMEMORY;
5538 for (j2 = 0; j2 < curve->cpfx; j2++)
5540 pt->pos = pt_flt[j2];
5541 pt->corner = POINTTYPE_CORNER;
5542 pt++;
5546 curve = (TTPOLYCURVE *)&curve->apfx[curve->cpfx];
5549 /* remove last point if the next line continues the last line */
5550 if (outline->count >= 3) {
5551 BOOL to_curve;
5553 lastpt = &outline->items[outline->count - 1];
5554 pt = &outline->items[0];
5555 if (pt->pos.x == lastpt->pos.x && pt->pos.y == lastpt->pos.y) {
5556 if (lastpt->corner == POINTTYPE_CURVE_END)
5558 if (pt->corner == POINTTYPE_CURVE_START)
5559 pt->corner = POINTTYPE_CURVE_MIDDLE;
5560 else
5561 pt->corner = POINTTYPE_CURVE_END;
5563 outline->count--;
5564 lastpt = &outline->items[outline->count - 1];
5565 } else {
5566 /* outline closed with a line from end to start point */
5567 attempt_line_merge(outline, outline->count - 1, &pt->pos, FALSE, cos_table);
5569 lastpt = &outline->items[0];
5570 to_curve = lastpt->corner != POINTTYPE_CORNER && lastpt->corner != POINTTYPE_CURVE_END;
5571 if (lastpt->corner == POINTTYPE_CURVE_START)
5572 lastpt->corner = POINTTYPE_CORNER;
5573 pt = &outline->items[1];
5574 if (attempt_line_merge(outline, 0, &pt->pos, to_curve, cos_table))
5575 *lastpt = outline->items[outline->count];
5578 lastpt = &outline->items[outline->count - 1];
5579 pt = &outline->items[0];
5580 unit_vec2(&lastdir, &lastpt->pos, &pt->pos);
5581 for (j = 0; j < outline->count; j++)
5583 D3DXVECTOR2 curdir;
5585 lastpt = pt;
5586 pt = &outline->items[(j + 1) % outline->count];
5587 unit_vec2(&curdir, &lastpt->pos, &pt->pos);
5589 switch (lastpt->corner)
5591 case POINTTYPE_CURVE_START:
5592 case POINTTYPE_CURVE_END:
5593 if (!is_direction_similar(&lastdir, &curdir, cos_table->cos_45))
5594 lastpt->corner = POINTTYPE_CORNER;
5595 break;
5596 case POINTTYPE_CURVE_MIDDLE:
5597 if (!is_direction_similar(&lastdir, &curdir, cos_table->cos_90))
5598 lastpt->corner = POINTTYPE_CORNER;
5599 else
5600 lastpt->corner = POINTTYPE_CURVE;
5601 break;
5602 default:
5603 break;
5605 lastdir = curdir;
5608 header = (TTPOLYGONHEADER *)((char *)header + header->cb);
5610 return S_OK;
5613 /* Get the y-distance from a line to a point */
5614 static float get_line_to_point_y_distance(D3DXVECTOR2 *line_pt1,
5615 D3DXVECTOR2 *line_pt2,
5616 D3DXVECTOR2 *point)
5618 D3DXVECTOR2 line_vec = {0, 0};
5619 float line_pt_dx;
5620 float line_y;
5622 D3DXVec2Subtract(&line_vec, line_pt2, line_pt1);
5623 line_pt_dx = point->x - line_pt1->x;
5624 line_y = line_pt1->y + (line_vec.y * line_pt_dx) / line_vec.x;
5625 return point->y - line_y;
5628 static D3DXVECTOR2 *get_indexed_point(struct point2d_index *pt_idx)
5630 return &pt_idx->outline->items[pt_idx->vertex].pos;
5633 static D3DXVECTOR2 *get_ordered_vertex(struct glyphinfo *glyph, WORD index)
5635 return get_indexed_point(&glyph->ordered_vertices.items[index]);
5638 static void remove_triangulation(struct triangulation_array *array, struct triangulation *item)
5640 HeapFree(GetProcessHeap(), 0, item->vertex_stack.items);
5641 MoveMemory(item, item + 1, (char*)&array->items[array->count] - (char*)(item + 1));
5642 array->count--;
5645 static HRESULT triangulation_add_point(struct triangulation **t_ptr,
5646 struct triangulation_array *triangulations,
5647 WORD vtx_idx,
5648 BOOL to_top)
5650 struct glyphinfo *glyph = triangulations->glyph;
5651 struct triangulation *t = *t_ptr;
5652 HRESULT hr;
5653 face *face;
5654 int f1, f2;
5656 if (t->last_on_top) {
5657 f1 = 1;
5658 f2 = 2;
5659 } else {
5660 f1 = 2;
5661 f2 = 1;
5664 if (t->last_on_top != to_top && t->vertex_stack.count > 1) {
5665 /* consume all vertices on the stack */
5666 WORD last_pt = t->vertex_stack.items[0];
5667 int i;
5668 for (i = 1; i < t->vertex_stack.count; i++)
5670 face = add_face(&glyph->faces);
5671 if (!face) return E_OUTOFMEMORY;
5672 (*face)[0] = vtx_idx;
5673 (*face)[f1] = last_pt;
5674 (*face)[f2] = last_pt = t->vertex_stack.items[i];
5676 t->vertex_stack.items[0] = last_pt;
5677 t->vertex_stack.count = 1;
5678 } else if (t->vertex_stack.count > 1) {
5679 int i = t->vertex_stack.count - 1;
5680 D3DXVECTOR2 *point = get_ordered_vertex(glyph, vtx_idx);
5681 WORD top_idx = t->vertex_stack.items[i--];
5682 D3DXVECTOR2 *top_pt = get_ordered_vertex(glyph, top_idx);
5684 while (i >= 0)
5686 WORD prev_idx = t->vertex_stack.items[i--];
5687 D3DXVECTOR2 *prev_pt = get_ordered_vertex(glyph, prev_idx);
5689 if (prev_pt->x != top_pt->x &&
5690 ((to_top && get_line_to_point_y_distance(prev_pt, top_pt, point) > 0) ||
5691 (!to_top && get_line_to_point_y_distance(prev_pt, top_pt, point) < 0)))
5692 break;
5694 face = add_face(&glyph->faces);
5695 if (!face) return E_OUTOFMEMORY;
5696 (*face)[0] = vtx_idx;
5697 (*face)[f1] = prev_idx;
5698 (*face)[f2] = top_idx;
5700 top_pt = prev_pt;
5701 top_idx = prev_idx;
5702 t->vertex_stack.count--;
5705 t->last_on_top = to_top;
5707 hr = add_vertex_index(&t->vertex_stack, vtx_idx);
5709 if (hr == S_OK && t->merging) {
5710 struct triangulation *t2;
5712 t2 = to_top ? t - 1 : t + 1;
5713 t2->merging = FALSE;
5714 hr = triangulation_add_point(&t2, triangulations, vtx_idx, to_top);
5715 if (hr != S_OK) return hr;
5716 remove_triangulation(triangulations, t);
5717 if (t2 > t)
5718 t2--;
5719 *t_ptr = t2;
5721 return hr;
5724 /* check if the point is next on the outline for either the top or bottom */
5725 static D3DXVECTOR2 *triangulation_get_next_point(struct triangulation *t, struct glyphinfo *glyph, BOOL on_top)
5727 int i = t->last_on_top == on_top ? t->vertex_stack.count - 1 : 0;
5728 WORD idx = t->vertex_stack.items[i];
5729 struct point2d_index *pt_idx = &glyph->ordered_vertices.items[idx];
5730 struct outline *outline = pt_idx->outline;
5732 if (on_top)
5733 i = (pt_idx->vertex + outline->count - 1) % outline->count;
5734 else
5735 i = (pt_idx->vertex + 1) % outline->count;
5737 return &outline->items[i].pos;
5740 static int compare_vertex_indices(const void *a, const void *b)
5742 const struct point2d_index *idx1 = a, *idx2 = b;
5743 const D3DXVECTOR2 *p1 = &idx1->outline->items[idx1->vertex].pos;
5744 const D3DXVECTOR2 *p2 = &idx2->outline->items[idx2->vertex].pos;
5745 float diff = p1->x - p2->x;
5747 if (diff == 0.0f)
5748 diff = p1->y - p2->y;
5750 return diff == 0.0f ? 0 : (diff > 0.0f ? -1 : 1);
5753 static HRESULT triangulate(struct triangulation_array *triangulations)
5755 int sweep_idx;
5756 HRESULT hr;
5757 struct glyphinfo *glyph = triangulations->glyph;
5758 int nb_vertices = 0;
5759 int i;
5760 struct point2d_index *idx_ptr;
5762 for (i = 0; i < glyph->outlines.count; i++)
5763 nb_vertices += glyph->outlines.items[i].count;
5765 glyph->ordered_vertices.items = HeapAlloc(GetProcessHeap(), 0,
5766 nb_vertices * sizeof(*glyph->ordered_vertices.items));
5767 if (!glyph->ordered_vertices.items)
5768 return E_OUTOFMEMORY;
5770 idx_ptr = glyph->ordered_vertices.items;
5771 for (i = 0; i < glyph->outlines.count; i++)
5773 struct outline *outline = &glyph->outlines.items[i];
5774 int j;
5776 idx_ptr->outline = outline;
5777 idx_ptr->vertex = 0;
5778 idx_ptr++;
5779 for (j = outline->count - 1; j > 0; j--)
5781 idx_ptr->outline = outline;
5782 idx_ptr->vertex = j;
5783 idx_ptr++;
5786 glyph->ordered_vertices.count = nb_vertices;
5788 /* Native implementation seems to try to create a triangle fan from
5789 * the first outline point if the glyph only has one outline. */
5790 if (glyph->outlines.count == 1)
5792 struct outline *outline = glyph->outlines.items;
5793 D3DXVECTOR2 *base = &outline->items[0].pos;
5794 D3DXVECTOR2 *last = &outline->items[1].pos;
5795 float ccw = 0;
5797 for (i = 2; i < outline->count; i++)
5799 D3DXVECTOR2 *next = &outline->items[i].pos;
5800 D3DXVECTOR2 v1 = {0.0f, 0.0f};
5801 D3DXVECTOR2 v2 = {0.0f, 0.0f};
5803 D3DXVec2Subtract(&v1, base, last);
5804 D3DXVec2Subtract(&v2, last, next);
5805 ccw = D3DXVec2CCW(&v1, &v2);
5806 if (ccw > 0.0f)
5807 break;
5809 last = next;
5811 if (ccw <= 0)
5813 glyph->faces.items = HeapAlloc(GetProcessHeap(), 0,
5814 (outline->count - 2) * sizeof(glyph->faces.items[0]));
5815 if (!glyph->faces.items)
5816 return E_OUTOFMEMORY;
5818 glyph->faces.count = outline->count - 2;
5819 for (i = 0; i < glyph->faces.count; i++)
5821 glyph->faces.items[i][0] = 0;
5822 glyph->faces.items[i][1] = i + 1;
5823 glyph->faces.items[i][2] = i + 2;
5825 return S_OK;
5829 /* Perform 2D polygon triangulation for complex glyphs.
5830 * Triangulation is performed using a sweep line concept, from right to left,
5831 * by processing vertices in sorted order. Complex polygons are split into
5832 * monotone polygons which are triangulated separately. */
5833 /* FIXME: The order of the faces is not consistent with the native implementation. */
5835 /* Reserve space for maximum possible faces from triangulation.
5836 * # faces for outer outlines = outline->count - 2
5837 * # faces for inner outlines = outline->count + 2
5838 * There must be at least 1 outer outline. */
5839 glyph->faces.items = HeapAlloc(GetProcessHeap(), 0,
5840 (nb_vertices + glyph->outlines.count * 2 - 4) * sizeof(glyph->faces.items[0]));
5841 if (!glyph->faces.items)
5842 return E_OUTOFMEMORY;
5844 qsort(glyph->ordered_vertices.items, nb_vertices,
5845 sizeof(glyph->ordered_vertices.items[0]), compare_vertex_indices);
5846 for (sweep_idx = 0; sweep_idx < glyph->ordered_vertices.count; sweep_idx++)
5848 int start = 0;
5849 int end = triangulations->count;
5851 while (start < end)
5853 D3DXVECTOR2 *sweep_vtx = get_ordered_vertex(glyph, sweep_idx);
5854 int current = (start + end) / 2;
5855 struct triangulation *t = &triangulations->items[current];
5856 BOOL on_top_outline = FALSE;
5857 D3DXVECTOR2 *top_next, *bottom_next;
5858 WORD top_idx, bottom_idx;
5860 if (t->merging && t->last_on_top)
5861 top_next = triangulation_get_next_point(t + 1, glyph, TRUE);
5862 else
5863 top_next = triangulation_get_next_point(t, glyph, TRUE);
5864 if (sweep_vtx == top_next)
5866 if (t->merging && t->last_on_top)
5867 t++;
5868 hr = triangulation_add_point(&t, triangulations, sweep_idx, TRUE);
5869 if (hr != S_OK) return hr;
5871 if (t + 1 < &triangulations->items[triangulations->count] &&
5872 triangulation_get_next_point(t + 1, glyph, FALSE) == sweep_vtx)
5874 /* point also on bottom outline of higher triangulation */
5875 struct triangulation *t2 = t + 1;
5876 hr = triangulation_add_point(&t2, triangulations, sweep_idx, FALSE);
5877 if (hr != S_OK) return hr;
5879 t->merging = TRUE;
5880 t2->merging = TRUE;
5882 on_top_outline = TRUE;
5885 if (t->merging && !t->last_on_top)
5886 bottom_next = triangulation_get_next_point(t - 1, glyph, FALSE);
5887 else
5888 bottom_next = triangulation_get_next_point(t, glyph, FALSE);
5889 if (sweep_vtx == bottom_next)
5891 if (t->merging && !t->last_on_top)
5892 t--;
5893 if (on_top_outline) {
5894 /* outline finished */
5895 remove_triangulation(triangulations, t);
5896 break;
5899 hr = triangulation_add_point(&t, triangulations, sweep_idx, FALSE);
5900 if (hr != S_OK) return hr;
5902 if (t > triangulations->items &&
5903 triangulation_get_next_point(t - 1, glyph, TRUE) == sweep_vtx)
5905 struct triangulation *t2 = t - 1;
5906 /* point also on top outline of lower triangulation */
5907 hr = triangulation_add_point(&t2, triangulations, sweep_idx, TRUE);
5908 if (hr != S_OK) return hr;
5909 t = t2 + 1; /* t may be invalidated by triangulation merging */
5911 t->merging = TRUE;
5912 t2->merging = TRUE;
5914 break;
5916 if (on_top_outline)
5917 break;
5919 if (t->last_on_top) {
5920 top_idx = t->vertex_stack.items[t->vertex_stack.count - 1];
5921 bottom_idx = t->vertex_stack.items[0];
5922 } else {
5923 top_idx = t->vertex_stack.items[0];
5924 bottom_idx = t->vertex_stack.items[t->vertex_stack.count - 1];
5927 /* check if the point is inside or outside this polygon */
5928 if (get_line_to_point_y_distance(get_ordered_vertex(glyph, top_idx),
5929 top_next, sweep_vtx) > 0)
5930 { /* above */
5931 start = current + 1;
5932 } else if (get_line_to_point_y_distance(get_ordered_vertex(glyph, bottom_idx),
5933 bottom_next, sweep_vtx) < 0)
5934 { /* below */
5935 end = current;
5936 } else if (t->merging) {
5937 /* inside, so cancel merging */
5938 struct triangulation *t2 = t->last_on_top ? t + 1 : t - 1;
5939 t->merging = FALSE;
5940 t2->merging = FALSE;
5941 hr = triangulation_add_point(&t, triangulations, sweep_idx, t->last_on_top);
5942 if (hr != S_OK) return hr;
5943 hr = triangulation_add_point(&t2, triangulations, sweep_idx, t2->last_on_top);
5944 if (hr != S_OK) return hr;
5945 break;
5946 } else {
5947 /* inside, so split polygon into two monotone parts */
5948 struct triangulation *t2 = add_triangulation(triangulations);
5949 if (!t2) return E_OUTOFMEMORY;
5950 MoveMemory(t + 1, t, (char*)(t2 + 1) - (char*)t);
5951 if (t->last_on_top) {
5952 t2 = t + 1;
5953 } else {
5954 t2 = t;
5955 t++;
5958 ZeroMemory(&t2->vertex_stack, sizeof(t2->vertex_stack));
5959 hr = add_vertex_index(&t2->vertex_stack, t->vertex_stack.items[t->vertex_stack.count - 1]);
5960 if (hr != S_OK) return hr;
5961 hr = add_vertex_index(&t2->vertex_stack, sweep_idx);
5962 if (hr != S_OK) return hr;
5963 t2->last_on_top = !t->last_on_top;
5965 hr = triangulation_add_point(&t, triangulations, sweep_idx, t->last_on_top);
5966 if (hr != S_OK) return hr;
5967 break;
5970 if (start >= end)
5972 struct triangulation *t;
5973 struct triangulation *t2 = add_triangulation(triangulations);
5974 if (!t2) return E_OUTOFMEMORY;
5975 t = &triangulations->items[start];
5976 MoveMemory(t + 1, t, (char*)(t2 + 1) - (char*)t);
5977 ZeroMemory(t, sizeof(*t));
5978 hr = add_vertex_index(&t->vertex_stack, sweep_idx);
5979 if (hr != S_OK) return hr;
5982 return S_OK;
5985 HRESULT WINAPI D3DXCreateTextW(struct IDirect3DDevice9 *device, HDC hdc, const WCHAR *text, float deviation,
5986 float extrusion, struct ID3DXMesh **mesh_ptr, struct ID3DXBuffer **adjacency, GLYPHMETRICSFLOAT *glyphmetrics)
5988 HRESULT hr;
5989 ID3DXMesh *mesh = NULL;
5990 DWORD nb_vertices, nb_faces;
5991 DWORD nb_front_faces, nb_corners, nb_outline_points;
5992 struct vertex *vertices = NULL;
5993 face *faces = NULL;
5994 int textlen = 0;
5995 float offset_x;
5996 LOGFONTW lf;
5997 OUTLINETEXTMETRICW otm;
5998 HFONT font = NULL, oldfont = NULL;
5999 const MAT2 identity = {{0, 1}, {0, 0}, {0, 0}, {0, 1}};
6000 void *raw_outline = NULL;
6001 int bufsize = 0;
6002 struct glyphinfo *glyphs = NULL;
6003 GLYPHMETRICS gm;
6004 struct triangulation_array triangulations = {0, 0, NULL};
6005 int i;
6006 struct vertex *vertex_ptr;
6007 face *face_ptr;
6008 float max_deviation_sq;
6009 const struct cos_table cos_table = {
6010 cosf(D3DXToRadian(0.5f)),
6011 cosf(D3DXToRadian(45.0f)),
6012 cosf(D3DXToRadian(90.0f)),
6014 int f1, f2;
6016 TRACE("(%p, %p, %s, %f, %f, %p, %p, %p)\n", device, hdc,
6017 debugstr_w(text), deviation, extrusion, mesh_ptr, adjacency, glyphmetrics);
6019 if (!device || !hdc || !text || !*text || deviation < 0.0f || extrusion < 0.0f || !mesh_ptr)
6020 return D3DERR_INVALIDCALL;
6022 if (adjacency)
6024 FIXME("Case of adjacency != NULL not implemented.\n");
6025 return E_NOTIMPL;
6028 if (!GetObjectW(GetCurrentObject(hdc, OBJ_FONT), sizeof(lf), &lf) ||
6029 !GetOutlineTextMetricsW(hdc, sizeof(otm), &otm))
6031 return D3DERR_INVALIDCALL;
6034 if (deviation == 0.0f)
6035 deviation = 1.0f / otm.otmEMSquare;
6036 max_deviation_sq = deviation * deviation;
6038 lf.lfHeight = otm.otmEMSquare;
6039 lf.lfWidth = 0;
6040 font = CreateFontIndirectW(&lf);
6041 if (!font) {
6042 hr = E_OUTOFMEMORY;
6043 goto error;
6045 oldfont = SelectObject(hdc, font);
6047 textlen = strlenW(text);
6048 for (i = 0; i < textlen; i++)
6050 int datasize = GetGlyphOutlineW(hdc, text[i], GGO_NATIVE, &gm, 0, NULL, &identity);
6051 if (datasize < 0)
6052 return D3DERR_INVALIDCALL;
6053 if (bufsize < datasize)
6054 bufsize = datasize;
6056 if (!bufsize) { /* e.g. text == " " */
6057 hr = D3DERR_INVALIDCALL;
6058 goto error;
6061 glyphs = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, textlen * sizeof(*glyphs));
6062 raw_outline = HeapAlloc(GetProcessHeap(), 0, bufsize);
6063 if (!glyphs || !raw_outline) {
6064 hr = E_OUTOFMEMORY;
6065 goto error;
6068 offset_x = 0.0f;
6069 for (i = 0; i < textlen; i++)
6071 /* get outline points from data returned from GetGlyphOutline */
6072 int datasize;
6074 glyphs[i].offset_x = offset_x;
6076 datasize = GetGlyphOutlineW(hdc, text[i], GGO_NATIVE, &gm, bufsize, raw_outline, &identity);
6077 hr = create_outline(&glyphs[i], raw_outline, datasize,
6078 max_deviation_sq, otm.otmEMSquare, &cos_table);
6079 if (hr != S_OK) goto error;
6081 triangulations.glyph = &glyphs[i];
6082 hr = triangulate(&triangulations);
6083 if (hr != S_OK) goto error;
6084 if (triangulations.count) {
6085 ERR("%d incomplete triangulations of glyph (%u).\n", triangulations.count, text[i]);
6086 triangulations.count = 0;
6089 if (glyphmetrics)
6091 glyphmetrics[i].gmfBlackBoxX = gm.gmBlackBoxX / (float)otm.otmEMSquare;
6092 glyphmetrics[i].gmfBlackBoxY = gm.gmBlackBoxY / (float)otm.otmEMSquare;
6093 glyphmetrics[i].gmfptGlyphOrigin.x = gm.gmptGlyphOrigin.x / (float)otm.otmEMSquare;
6094 glyphmetrics[i].gmfptGlyphOrigin.y = gm.gmptGlyphOrigin.y / (float)otm.otmEMSquare;
6095 glyphmetrics[i].gmfCellIncX = gm.gmCellIncX / (float)otm.otmEMSquare;
6096 glyphmetrics[i].gmfCellIncY = gm.gmCellIncY / (float)otm.otmEMSquare;
6098 offset_x += gm.gmCellIncX / (float)otm.otmEMSquare;
6101 /* corner points need an extra vertex for the different side faces normals */
6102 nb_corners = 0;
6103 nb_outline_points = 0;
6104 nb_front_faces = 0;
6105 for (i = 0; i < textlen; i++)
6107 int j;
6108 nb_outline_points += glyphs[i].ordered_vertices.count;
6109 nb_front_faces += glyphs[i].faces.count;
6110 for (j = 0; j < glyphs[i].outlines.count; j++)
6112 int k;
6113 struct outline *outline = &glyphs[i].outlines.items[j];
6114 nb_corners++; /* first outline point always repeated as a corner */
6115 for (k = 1; k < outline->count; k++)
6116 if (outline->items[k].corner)
6117 nb_corners++;
6121 nb_vertices = (nb_outline_points + nb_corners) * 2 + nb_outline_points * 2;
6122 nb_faces = nb_outline_points * 2 + nb_front_faces * 2;
6125 hr = D3DXCreateMeshFVF(nb_faces, nb_vertices, D3DXMESH_MANAGED,
6126 D3DFVF_XYZ | D3DFVF_NORMAL, device, &mesh);
6127 if (FAILED(hr))
6128 goto error;
6130 if (FAILED(hr = mesh->lpVtbl->LockVertexBuffer(mesh, 0, (void **)&vertices)))
6131 goto error;
6133 if (FAILED(hr = mesh->lpVtbl->LockIndexBuffer(mesh, 0, (void **)&faces)))
6134 goto error;
6136 /* convert 2D vertices and faces into 3D mesh */
6137 vertex_ptr = vertices;
6138 face_ptr = faces;
6139 if (extrusion == 0.0f) {
6140 f1 = 1;
6141 f2 = 2;
6142 } else {
6143 f1 = 2;
6144 f2 = 1;
6146 for (i = 0; i < textlen; i++)
6148 int j;
6149 int count;
6150 struct vertex *back_vertices;
6151 face *back_faces;
6153 /* side vertices and faces */
6154 for (j = 0; j < glyphs[i].outlines.count; j++)
6156 struct vertex *outline_vertices = vertex_ptr;
6157 struct outline *outline = &glyphs[i].outlines.items[j];
6158 int k;
6159 struct point2d *prevpt = &outline->items[outline->count - 1];
6160 struct point2d *pt = &outline->items[0];
6162 for (k = 1; k <= outline->count; k++)
6164 struct vertex vtx;
6165 struct point2d *nextpt = &outline->items[k % outline->count];
6166 WORD vtx_idx = vertex_ptr - vertices;
6167 D3DXVECTOR2 vec;
6169 if (pt->corner == POINTTYPE_CURVE_START)
6170 D3DXVec2Subtract(&vec, &pt->pos, &prevpt->pos);
6171 else if (pt->corner)
6172 D3DXVec2Subtract(&vec, &nextpt->pos, &pt->pos);
6173 else
6174 D3DXVec2Subtract(&vec, &nextpt->pos, &prevpt->pos);
6175 D3DXVec2Normalize(&vec, &vec);
6176 vtx.normal.x = -vec.y;
6177 vtx.normal.y = vec.x;
6178 vtx.normal.z = 0;
6180 vtx.position.x = pt->pos.x + glyphs[i].offset_x;
6181 vtx.position.y = pt->pos.y;
6182 vtx.position.z = 0;
6183 *vertex_ptr++ = vtx;
6185 vtx.position.z = -extrusion;
6186 *vertex_ptr++ = vtx;
6188 vtx.position.x = nextpt->pos.x + glyphs[i].offset_x;
6189 vtx.position.y = nextpt->pos.y;
6190 if (pt->corner && nextpt->corner && nextpt->corner != POINTTYPE_CURVE_END) {
6191 vtx.position.z = -extrusion;
6192 *vertex_ptr++ = vtx;
6193 vtx.position.z = 0;
6194 *vertex_ptr++ = vtx;
6196 (*face_ptr)[0] = vtx_idx;
6197 (*face_ptr)[1] = vtx_idx + 2;
6198 (*face_ptr)[2] = vtx_idx + 1;
6199 face_ptr++;
6201 (*face_ptr)[0] = vtx_idx;
6202 (*face_ptr)[1] = vtx_idx + 3;
6203 (*face_ptr)[2] = vtx_idx + 2;
6204 face_ptr++;
6205 } else {
6206 if (nextpt->corner) {
6207 if (nextpt->corner == POINTTYPE_CURVE_END) {
6208 D3DXVECTOR2 *nextpt2 = &outline->items[(k + 1) % outline->count].pos;
6209 D3DXVec2Subtract(&vec, nextpt2, &nextpt->pos);
6210 } else {
6211 D3DXVec2Subtract(&vec, &nextpt->pos, &pt->pos);
6213 D3DXVec2Normalize(&vec, &vec);
6214 vtx.normal.x = -vec.y;
6215 vtx.normal.y = vec.x;
6217 vtx.position.z = 0;
6218 *vertex_ptr++ = vtx;
6219 vtx.position.z = -extrusion;
6220 *vertex_ptr++ = vtx;
6223 (*face_ptr)[0] = vtx_idx;
6224 (*face_ptr)[1] = vtx_idx + 3;
6225 (*face_ptr)[2] = vtx_idx + 1;
6226 face_ptr++;
6228 (*face_ptr)[0] = vtx_idx;
6229 (*face_ptr)[1] = vtx_idx + 2;
6230 (*face_ptr)[2] = vtx_idx + 3;
6231 face_ptr++;
6234 prevpt = pt;
6235 pt = nextpt;
6237 if (!pt->corner) {
6238 *vertex_ptr++ = *outline_vertices++;
6239 *vertex_ptr++ = *outline_vertices++;
6243 /* back vertices and faces */
6244 back_faces = face_ptr;
6245 back_vertices = vertex_ptr;
6246 for (j = 0; j < glyphs[i].ordered_vertices.count; j++)
6248 D3DXVECTOR2 *pt = get_ordered_vertex(&glyphs[i], j);
6249 vertex_ptr->position.x = pt->x + glyphs[i].offset_x;
6250 vertex_ptr->position.y = pt->y;
6251 vertex_ptr->position.z = 0;
6252 vertex_ptr->normal.x = 0;
6253 vertex_ptr->normal.y = 0;
6254 vertex_ptr->normal.z = 1;
6255 vertex_ptr++;
6257 count = back_vertices - vertices;
6258 for (j = 0; j < glyphs[i].faces.count; j++)
6260 face *f = &glyphs[i].faces.items[j];
6261 (*face_ptr)[0] = (*f)[0] + count;
6262 (*face_ptr)[1] = (*f)[1] + count;
6263 (*face_ptr)[2] = (*f)[2] + count;
6264 face_ptr++;
6267 /* front vertices and faces */
6268 j = count = vertex_ptr - back_vertices;
6269 while (j--)
6271 vertex_ptr->position.x = back_vertices->position.x;
6272 vertex_ptr->position.y = back_vertices->position.y;
6273 vertex_ptr->position.z = -extrusion;
6274 vertex_ptr->normal.x = 0;
6275 vertex_ptr->normal.y = 0;
6276 vertex_ptr->normal.z = extrusion == 0.0f ? 1.0f : -1.0f;
6277 vertex_ptr++;
6278 back_vertices++;
6280 j = face_ptr - back_faces;
6281 while (j--)
6283 (*face_ptr)[0] = (*back_faces)[0] + count;
6284 (*face_ptr)[1] = (*back_faces)[f1] + count;
6285 (*face_ptr)[2] = (*back_faces)[f2] + count;
6286 face_ptr++;
6287 back_faces++;
6291 *mesh_ptr = mesh;
6292 hr = D3D_OK;
6293 error:
6294 if (mesh) {
6295 if (faces) mesh->lpVtbl->UnlockIndexBuffer(mesh);
6296 if (vertices) mesh->lpVtbl->UnlockVertexBuffer(mesh);
6297 if (hr != D3D_OK) mesh->lpVtbl->Release(mesh);
6299 if (glyphs) {
6300 for (i = 0; i < textlen; i++)
6302 int j;
6303 for (j = 0; j < glyphs[i].outlines.count; j++)
6304 HeapFree(GetProcessHeap(), 0, glyphs[i].outlines.items[j].items);
6305 HeapFree(GetProcessHeap(), 0, glyphs[i].outlines.items);
6306 HeapFree(GetProcessHeap(), 0, glyphs[i].faces.items);
6307 HeapFree(GetProcessHeap(), 0, glyphs[i].ordered_vertices.items);
6309 HeapFree(GetProcessHeap(), 0, glyphs);
6311 if (triangulations.items) {
6312 int i;
6313 for (i = 0; i < triangulations.count; i++)
6314 HeapFree(GetProcessHeap(), 0, triangulations.items[i].vertex_stack.items);
6315 HeapFree(GetProcessHeap(), 0, triangulations.items);
6317 HeapFree(GetProcessHeap(), 0, raw_outline);
6318 if (oldfont) SelectObject(hdc, oldfont);
6319 if (font) DeleteObject(font);
6321 return hr;
6324 HRESULT WINAPI D3DXValidMesh(ID3DXMesh *mesh, const DWORD *adjacency, ID3DXBuffer **errors_and_warnings)
6326 FIXME("(%p, %p, %p): stub\n", mesh, adjacency, *errors_and_warnings);
6328 return E_NOTIMPL;
6331 static BOOL weld_float1(void *to, void *from, FLOAT epsilon)
6333 FLOAT *v1 = to;
6334 FLOAT *v2 = from;
6336 if (fabsf(*v1 - *v2) <= epsilon)
6338 *v1 = *v2;
6340 return TRUE;
6343 return FALSE;
6346 static BOOL weld_float2(void *to, void *from, FLOAT epsilon)
6348 D3DXVECTOR2 *v1 = to;
6349 D3DXVECTOR2 *v2 = from;
6350 FLOAT diff_x = fabsf(v1->x - v2->x);
6351 FLOAT diff_y = fabsf(v1->y - v2->y);
6352 FLOAT max_abs_diff = max(diff_x, diff_y);
6354 if (max_abs_diff <= epsilon)
6356 memcpy(to, from, sizeof(D3DXVECTOR2));
6358 return TRUE;
6361 return FALSE;
6364 static BOOL weld_float3(void *to, void *from, FLOAT epsilon)
6366 D3DXVECTOR3 *v1 = to;
6367 D3DXVECTOR3 *v2 = from;
6368 FLOAT diff_x = fabsf(v1->x - v2->x);
6369 FLOAT diff_y = fabsf(v1->y - v2->y);
6370 FLOAT diff_z = fabsf(v1->z - v2->z);
6371 FLOAT max_abs_diff = max(diff_x, diff_y);
6372 max_abs_diff = max(diff_z, max_abs_diff);
6374 if (max_abs_diff <= epsilon)
6376 memcpy(to, from, sizeof(D3DXVECTOR3));
6378 return TRUE;
6381 return FALSE;
6384 static BOOL weld_float4(void *to, void *from, FLOAT epsilon)
6386 D3DXVECTOR4 *v1 = to;
6387 D3DXVECTOR4 *v2 = from;
6388 FLOAT diff_x = fabsf(v1->x - v2->x);
6389 FLOAT diff_y = fabsf(v1->y - v2->y);
6390 FLOAT diff_z = fabsf(v1->z - v2->z);
6391 FLOAT diff_w = fabsf(v1->w - v2->w);
6392 FLOAT max_abs_diff = max(diff_x, diff_y);
6393 max_abs_diff = max(diff_z, max_abs_diff);
6394 max_abs_diff = max(diff_w, max_abs_diff);
6396 if (max_abs_diff <= epsilon)
6398 memcpy(to, from, sizeof(D3DXVECTOR4));
6400 return TRUE;
6403 return FALSE;
6406 static BOOL weld_ubyte4(void *to, void *from, FLOAT epsilon)
6408 BYTE *b1 = to;
6409 BYTE *b2 = from;
6410 BYTE truncated_epsilon = (BYTE)epsilon;
6411 BYTE diff_x = b1[0] > b2[0] ? b1[0] - b2[0] : b2[0] - b1[0];
6412 BYTE diff_y = b1[1] > b2[1] ? b1[1] - b2[1] : b2[1] - b1[1];
6413 BYTE diff_z = b1[2] > b2[2] ? b1[2] - b2[2] : b2[2] - b1[2];
6414 BYTE diff_w = b1[3] > b2[3] ? b1[3] - b2[3] : b2[3] - b1[3];
6415 BYTE max_diff = max(diff_x, diff_y);
6416 max_diff = max(diff_z, max_diff);
6417 max_diff = max(diff_w, max_diff);
6419 if (max_diff <= truncated_epsilon)
6421 memcpy(to, from, 4 * sizeof(BYTE));
6423 return TRUE;
6426 return FALSE;
6429 static BOOL weld_ubyte4n(void *to, void *from, FLOAT epsilon)
6431 return weld_ubyte4(to, from, epsilon * UCHAR_MAX);
6434 static BOOL weld_d3dcolor(void *to, void *from, FLOAT epsilon)
6436 return weld_ubyte4n(to, from, epsilon);
6439 static BOOL weld_short2(void *to, void *from, FLOAT epsilon)
6441 SHORT *s1 = to;
6442 SHORT *s2 = from;
6443 SHORT truncated_epsilon = (SHORT)epsilon;
6444 SHORT diff_x = abs(s1[0] - s2[0]);
6445 SHORT diff_y = abs(s1[1] - s2[1]);
6446 SHORT max_abs_diff = max(diff_x, diff_y);
6448 if (max_abs_diff <= truncated_epsilon)
6450 memcpy(to, from, 2 * sizeof(SHORT));
6452 return TRUE;
6455 return FALSE;
6458 static BOOL weld_short2n(void *to, void *from, FLOAT epsilon)
6460 return weld_short2(to, from, epsilon * SHRT_MAX);
6463 static BOOL weld_short4(void *to, void *from, FLOAT epsilon)
6465 SHORT *s1 = to;
6466 SHORT *s2 = from;
6467 SHORT truncated_epsilon = (SHORT)epsilon;
6468 SHORT diff_x = abs(s1[0] - s2[0]);
6469 SHORT diff_y = abs(s1[1] - s2[1]);
6470 SHORT diff_z = abs(s1[2] - s2[2]);
6471 SHORT diff_w = abs(s1[3] - s2[3]);
6472 SHORT max_abs_diff = max(diff_x, diff_y);
6473 max_abs_diff = max(diff_z, max_abs_diff);
6474 max_abs_diff = max(diff_w, max_abs_diff);
6476 if (max_abs_diff <= truncated_epsilon)
6478 memcpy(to, from, 4 * sizeof(SHORT));
6480 return TRUE;
6483 return FALSE;
6486 static BOOL weld_short4n(void *to, void *from, FLOAT epsilon)
6488 return weld_short4(to, from, epsilon * SHRT_MAX);
6491 static BOOL weld_ushort2n(void *to, void *from, FLOAT epsilon)
6493 USHORT *s1 = to;
6494 USHORT *s2 = from;
6495 USHORT scaled_epsilon = (USHORT)(epsilon * USHRT_MAX);
6496 USHORT diff_x = s1[0] > s2[0] ? s1[0] - s2[0] : s2[0] - s1[0];
6497 USHORT diff_y = s1[1] > s2[1] ? s1[1] - s2[1] : s2[1] - s1[1];
6498 USHORT max_diff = max(diff_x, diff_y);
6500 if (max_diff <= scaled_epsilon)
6502 memcpy(to, from, 2 * sizeof(USHORT));
6504 return TRUE;
6507 return FALSE;
6510 static BOOL weld_ushort4n(void *to, void *from, FLOAT epsilon)
6512 USHORT *s1 = to;
6513 USHORT *s2 = from;
6514 USHORT scaled_epsilon = (USHORT)(epsilon * USHRT_MAX);
6515 USHORT diff_x = s1[0] > s2[0] ? s1[0] - s2[0] : s2[0] - s1[0];
6516 USHORT diff_y = s1[1] > s2[1] ? s1[1] - s2[1] : s2[1] - s1[1];
6517 USHORT diff_z = s1[2] > s2[2] ? s1[2] - s2[2] : s2[2] - s1[2];
6518 USHORT diff_w = s1[3] > s2[3] ? s1[3] - s2[3] : s2[3] - s1[3];
6519 USHORT max_diff = max(diff_x, diff_y);
6520 max_diff = max(diff_z, max_diff);
6521 max_diff = max(diff_w, max_diff);
6523 if (max_diff <= scaled_epsilon)
6525 memcpy(to, from, 4 * sizeof(USHORT));
6527 return TRUE;
6530 return FALSE;
6533 struct udec3
6535 UINT x;
6536 UINT y;
6537 UINT z;
6538 UINT w;
6541 static struct udec3 dword_to_udec3(DWORD d)
6543 struct udec3 v;
6545 v.x = d & 0x3ff;
6546 v.y = (d & 0xffc00) >> 10;
6547 v.z = (d & 0x3ff00000) >> 20;
6548 v.w = (d & 0xc0000000) >> 30;
6550 return v;
6553 static BOOL weld_udec3(void *to, void *from, FLOAT epsilon)
6555 DWORD *d1 = to;
6556 DWORD *d2 = from;
6557 struct udec3 v1 = dword_to_udec3(*d1);
6558 struct udec3 v2 = dword_to_udec3(*d2);
6559 UINT truncated_epsilon = (UINT)epsilon;
6560 UINT diff_x = v1.x > v2.x ? v1.x - v2.x : v2.x - v1.x;
6561 UINT diff_y = v1.y > v2.y ? v1.y - v2.y : v2.y - v1.y;
6562 UINT diff_z = v1.z > v2.z ? v1.z - v2.z : v2.z - v1.z;
6563 UINT diff_w = v1.w > v2.w ? v1.w - v2.w : v2.w - v1.w;
6564 UINT max_diff = max(diff_x, diff_y);
6565 max_diff = max(diff_z, max_diff);
6566 max_diff = max(diff_w, max_diff);
6568 if (max_diff <= truncated_epsilon)
6570 memcpy(to, from, sizeof(DWORD));
6572 return TRUE;
6575 return FALSE;
6578 struct dec3n
6580 INT x;
6581 INT y;
6582 INT z;
6583 INT w;
6586 static struct dec3n dword_to_dec3n(DWORD d)
6588 struct dec3n v;
6590 v.x = d & 0x3ff;
6591 v.y = (d & 0xffc00) >> 10;
6592 v.z = (d & 0x3ff00000) >> 20;
6593 v.w = (d & 0xc0000000) >> 30;
6595 return v;
6598 static BOOL weld_dec3n(void *to, void *from, FLOAT epsilon)
6600 const UINT MAX_DEC3N = 511;
6601 DWORD *d1 = to;
6602 DWORD *d2 = from;
6603 struct dec3n v1 = dword_to_dec3n(*d1);
6604 struct dec3n v2 = dword_to_dec3n(*d2);
6605 INT scaled_epsilon = (INT)(epsilon * MAX_DEC3N);
6606 INT diff_x = abs(v1.x - v2.x);
6607 INT diff_y = abs(v1.y - v2.y);
6608 INT diff_z = abs(v1.z - v2.z);
6609 INT diff_w = abs(v1.w - v2.w);
6610 INT max_abs_diff = max(diff_x, diff_y);
6611 max_abs_diff = max(diff_z, max_abs_diff);
6612 max_abs_diff = max(diff_w, max_abs_diff);
6614 if (max_abs_diff <= scaled_epsilon)
6616 memcpy(to, from, sizeof(DWORD));
6618 return TRUE;
6621 return FALSE;
6624 static BOOL weld_float16_2(void *to, void *from, FLOAT epsilon)
6626 D3DXFLOAT16 *v1_float16 = to;
6627 D3DXFLOAT16 *v2_float16 = from;
6628 FLOAT diff_x;
6629 FLOAT diff_y;
6630 FLOAT max_abs_diff;
6631 #define NUM_ELEM 2
6632 FLOAT v1[NUM_ELEM];
6633 FLOAT v2[NUM_ELEM];
6635 D3DXFloat16To32Array(v1, v1_float16, NUM_ELEM);
6636 D3DXFloat16To32Array(v2, v2_float16, NUM_ELEM);
6638 diff_x = fabsf(v1[0] - v2[0]);
6639 diff_y = fabsf(v1[1] - v2[1]);
6640 max_abs_diff = max(diff_x, diff_y);
6642 if (max_abs_diff <= epsilon)
6644 memcpy(to, from, NUM_ELEM * sizeof(D3DXFLOAT16));
6646 return TRUE;
6649 return FALSE;
6650 #undef NUM_ELEM
6653 static BOOL weld_float16_4(void *to, void *from, FLOAT epsilon)
6655 D3DXFLOAT16 *v1_float16 = to;
6656 D3DXFLOAT16 *v2_float16 = from;
6657 FLOAT diff_x;
6658 FLOAT diff_y;
6659 FLOAT diff_z;
6660 FLOAT diff_w;
6661 FLOAT max_abs_diff;
6662 #define NUM_ELEM 4
6663 FLOAT v1[NUM_ELEM];
6664 FLOAT v2[NUM_ELEM];
6666 D3DXFloat16To32Array(v1, v1_float16, NUM_ELEM);
6667 D3DXFloat16To32Array(v2, v2_float16, NUM_ELEM);
6669 diff_x = fabsf(v1[0] - v2[0]);
6670 diff_y = fabsf(v1[1] - v2[1]);
6671 diff_z = fabsf(v1[2] - v2[2]);
6672 diff_w = fabsf(v1[3] - v2[3]);
6673 max_abs_diff = max(diff_x, diff_y);
6674 max_abs_diff = max(diff_z, max_abs_diff);
6675 max_abs_diff = max(diff_w, max_abs_diff);
6677 if (max_abs_diff <= epsilon)
6679 memcpy(to, from, NUM_ELEM * sizeof(D3DXFLOAT16));
6681 return TRUE;
6684 return FALSE;
6685 #undef NUM_ELEM
6688 /* Sets the vertex components to the same value if they are within epsilon. */
6689 static BOOL weld_component(void *to, void *from, D3DDECLTYPE type, FLOAT epsilon)
6691 /* Quiet FIXMEs as this is in a loop with potentially thousand of iterations. */
6692 BOOL fixme_once_unused = FALSE;
6693 BOOL fixme_once_unknown = FALSE;
6695 switch (type)
6697 case D3DDECLTYPE_FLOAT1:
6698 return weld_float1(to, from, epsilon);
6700 case D3DDECLTYPE_FLOAT2:
6701 return weld_float2(to, from, epsilon);
6703 case D3DDECLTYPE_FLOAT3:
6704 return weld_float3(to, from, epsilon);
6706 case D3DDECLTYPE_FLOAT4:
6707 return weld_float4(to, from, epsilon);
6709 case D3DDECLTYPE_D3DCOLOR:
6710 return weld_d3dcolor(to, from, epsilon);
6712 case D3DDECLTYPE_UBYTE4:
6713 return weld_ubyte4(to, from, epsilon);
6715 case D3DDECLTYPE_SHORT2:
6716 return weld_short2(to, from, epsilon);
6718 case D3DDECLTYPE_SHORT4:
6719 return weld_short4(to, from, epsilon);
6721 case D3DDECLTYPE_UBYTE4N:
6722 return weld_ubyte4n(to, from, epsilon);
6724 case D3DDECLTYPE_SHORT2N:
6725 return weld_short2n(to, from, epsilon);
6727 case D3DDECLTYPE_SHORT4N:
6728 return weld_short4n(to, from, epsilon);
6730 case D3DDECLTYPE_USHORT2N:
6731 return weld_ushort2n(to, from, epsilon);
6733 case D3DDECLTYPE_USHORT4N:
6734 return weld_ushort4n(to, from, epsilon);
6736 case D3DDECLTYPE_UDEC3:
6737 return weld_udec3(to, from, epsilon);
6739 case D3DDECLTYPE_DEC3N:
6740 return weld_dec3n(to, from, epsilon);
6742 case D3DDECLTYPE_FLOAT16_2:
6743 return weld_float16_2(to, from, epsilon);
6745 case D3DDECLTYPE_FLOAT16_4:
6746 return weld_float16_4(to, from, epsilon);
6748 case D3DDECLTYPE_UNUSED:
6749 if (!fixme_once_unused++)
6750 FIXME("D3DDECLTYPE_UNUSED welding not implemented.\n");
6751 break;
6753 default:
6754 if (!fixme_once_unknown++)
6755 FIXME("Welding of unknown declaration type %d is not implemented.\n", type);
6756 break;
6759 return FALSE;
6762 static FLOAT get_component_epsilon(const D3DVERTEXELEMENT9 *decl_ptr, const D3DXWELDEPSILONS *epsilons)
6764 FLOAT epsilon = 0.0f;
6765 /* Quiet FIXMEs as this is in a loop with potentially thousand of iterations. */
6766 static BOOL fixme_once_blendindices = FALSE;
6767 static BOOL fixme_once_positiont = FALSE;
6768 static BOOL fixme_once_fog = FALSE;
6769 static BOOL fixme_once_depth = FALSE;
6770 static BOOL fixme_once_sample = FALSE;
6771 static BOOL fixme_once_unknown = FALSE;
6773 switch (decl_ptr->Usage)
6775 case D3DDECLUSAGE_POSITION:
6776 epsilon = epsilons->Position;
6777 break;
6778 case D3DDECLUSAGE_BLENDWEIGHT:
6779 epsilon = epsilons->BlendWeights;
6780 break;
6781 case D3DDECLUSAGE_NORMAL:
6782 epsilon = epsilons->Normals;
6783 break;
6784 case D3DDECLUSAGE_PSIZE:
6785 epsilon = epsilons->PSize;
6786 break;
6787 case D3DDECLUSAGE_TEXCOORD:
6789 BYTE usage_index = decl_ptr->UsageIndex;
6790 if (usage_index > 7)
6791 usage_index = 7;
6792 epsilon = epsilons->Texcoords[usage_index];
6793 break;
6795 case D3DDECLUSAGE_TANGENT:
6796 epsilon = epsilons->Tangent;
6797 break;
6798 case D3DDECLUSAGE_BINORMAL:
6799 epsilon = epsilons->Binormal;
6800 break;
6801 case D3DDECLUSAGE_TESSFACTOR:
6802 epsilon = epsilons->TessFactor;
6803 break;
6804 case D3DDECLUSAGE_COLOR:
6805 if (decl_ptr->UsageIndex == 0)
6806 epsilon = epsilons->Diffuse;
6807 else if (decl_ptr->UsageIndex == 1)
6808 epsilon = epsilons->Specular;
6809 else
6810 epsilon = 1e-6f;
6811 break;
6812 case D3DDECLUSAGE_BLENDINDICES:
6813 if (!fixme_once_blendindices++)
6814 FIXME("D3DDECLUSAGE_BLENDINDICES welding not implemented.\n");
6815 break;
6816 case D3DDECLUSAGE_POSITIONT:
6817 if (!fixme_once_positiont++)
6818 FIXME("D3DDECLUSAGE_POSITIONT welding not implemented.\n");
6819 break;
6820 case D3DDECLUSAGE_FOG:
6821 if (!fixme_once_fog++)
6822 FIXME("D3DDECLUSAGE_FOG welding not implemented.\n");
6823 break;
6824 case D3DDECLUSAGE_DEPTH:
6825 if (!fixme_once_depth++)
6826 FIXME("D3DDECLUSAGE_DEPTH welding not implemented.\n");
6827 break;
6828 case D3DDECLUSAGE_SAMPLE:
6829 if (!fixme_once_sample++)
6830 FIXME("D3DDECLUSAGE_SAMPLE welding not implemented.\n");
6831 break;
6832 default:
6833 if (!fixme_once_unknown++)
6834 FIXME("Unknown usage %x\n", decl_ptr->Usage);
6835 break;
6838 return epsilon;
6841 /* Helper function for reading a 32-bit index buffer. */
6842 static inline DWORD read_ib(void *index_buffer, BOOL indices_are_32bit,
6843 DWORD index)
6845 if (indices_are_32bit)
6847 DWORD *indices = index_buffer;
6848 return indices[index];
6850 else
6852 WORD *indices = index_buffer;
6853 return indices[index];
6857 /* Helper function for writing to a 32-bit index buffer. */
6858 static inline void write_ib(void *index_buffer, BOOL indices_are_32bit,
6859 DWORD index, DWORD value)
6861 if (indices_are_32bit)
6863 DWORD *indices = index_buffer;
6864 indices[index] = value;
6866 else
6868 WORD *indices = index_buffer;
6869 indices[index] = value;
6873 /*************************************************************************
6874 * D3DXWeldVertices (D3DX9_36.@)
6876 * Welds together similar vertices. The similarity between vert-
6877 * ices can be the position and other components such as
6878 * normal and color.
6880 * PARAMS
6881 * mesh [I] Mesh which vertices will be welded together.
6882 * flags [I] D3DXWELDEPSILONSFLAGS specifying how to weld.
6883 * epsilons [I] How similar a component needs to be for welding.
6884 * adjacency [I] Which faces are adjacent to other faces.
6885 * adjacency_out [O] Updated adjacency after welding.
6886 * face_remap_out [O] Which faces the old faces have been mapped to.
6887 * vertex_remap_out [O] Which vertices the old vertices have been mapped to.
6889 * RETURNS
6890 * Success: D3D_OK.
6891 * Failure: D3DERR_INVALIDCALL, E_OUTOFMEMORY.
6893 * BUGS
6894 * Attribute sorting not implemented.
6897 HRESULT WINAPI D3DXWeldVertices(ID3DXMesh *mesh, DWORD flags, const D3DXWELDEPSILONS *epsilons,
6898 const DWORD *adjacency, DWORD *adjacency_out, DWORD *face_remap_out, ID3DXBuffer **vertex_remap_out)
6900 DWORD *adjacency_generated = NULL;
6901 const DWORD *adjacency_ptr;
6902 DWORD *attributes = NULL;
6903 const FLOAT DEFAULT_EPSILON = 1.0e-6f;
6904 HRESULT hr;
6905 DWORD i;
6906 void *indices = NULL;
6907 BOOL indices_are_32bit = mesh->lpVtbl->GetOptions(mesh) & D3DXMESH_32BIT;
6908 DWORD optimize_flags;
6909 DWORD *point_reps = NULL;
6910 struct d3dx9_mesh *This = impl_from_ID3DXMesh(mesh);
6911 DWORD *vertex_face_map = NULL;
6912 ID3DXBuffer *vertex_remap = NULL;
6913 BYTE *vertices = NULL;
6915 TRACE("mesh %p, flags %#x, epsilons %p, adjacency %p, adjacency_out %p, face_remap_out %p, vertex_remap_out %p.\n",
6916 mesh, flags, epsilons, adjacency, adjacency_out, face_remap_out, vertex_remap_out);
6918 if (flags == 0)
6920 WARN("No flags is undefined. Using D3DXWELDEPSILONS_WELDPARTIALMATCHES instead.\n");
6921 flags = D3DXWELDEPSILONS_WELDPARTIALMATCHES;
6924 if (adjacency) /* Use supplied adjacency. */
6926 adjacency_ptr = adjacency;
6928 else /* Adjacency has to be generated. */
6930 adjacency_generated = HeapAlloc(GetProcessHeap(), 0, 3 * This->numfaces * sizeof(*adjacency_generated));
6931 if (!adjacency_generated)
6933 ERR("Couldn't allocate memory for adjacency_generated.\n");
6934 hr = E_OUTOFMEMORY;
6935 goto cleanup;
6937 hr = mesh->lpVtbl->GenerateAdjacency(mesh, DEFAULT_EPSILON, adjacency_generated);
6938 if (FAILED(hr))
6940 ERR("Couldn't generate adjacency.\n");
6941 goto cleanup;
6943 adjacency_ptr = adjacency_generated;
6946 /* Point representation says which vertices can be replaced. */
6947 point_reps = HeapAlloc(GetProcessHeap(), 0, This->numvertices * sizeof(*point_reps));
6948 if (!point_reps)
6950 hr = E_OUTOFMEMORY;
6951 ERR("Couldn't allocate memory for point_reps.\n");
6952 goto cleanup;
6954 hr = mesh->lpVtbl->ConvertAdjacencyToPointReps(mesh, adjacency_ptr, point_reps);
6955 if (FAILED(hr))
6957 ERR("ConvertAdjacencyToPointReps failed.\n");
6958 goto cleanup;
6961 hr = mesh->lpVtbl->LockIndexBuffer(mesh, 0, &indices);
6962 if (FAILED(hr))
6964 ERR("Couldn't lock index buffer.\n");
6965 goto cleanup;
6968 hr = mesh->lpVtbl->LockAttributeBuffer(mesh, 0, &attributes);
6969 if (FAILED(hr))
6971 ERR("Couldn't lock attribute buffer.\n");
6972 goto cleanup;
6974 vertex_face_map = HeapAlloc(GetProcessHeap(), 0, This->numvertices * sizeof(*vertex_face_map));
6975 if (!vertex_face_map)
6977 hr = E_OUTOFMEMORY;
6978 ERR("Couldn't allocate memory for vertex_face_map.\n");
6979 goto cleanup;
6981 /* Build vertex face map, so that a vertex's face can be looked up. */
6982 for (i = 0; i < This->numfaces; i++)
6984 DWORD j;
6985 for (j = 0; j < 3; j++)
6987 DWORD index = read_ib(indices, indices_are_32bit, 3*i + j);
6988 vertex_face_map[index] = i;
6992 if (flags & D3DXWELDEPSILONS_WELDPARTIALMATCHES)
6994 hr = mesh->lpVtbl->LockVertexBuffer(mesh, 0, (void**)&vertices);
6995 if (FAILED(hr))
6997 ERR("Couldn't lock vertex buffer.\n");
6998 goto cleanup;
7000 /* For each vertex that can be removed, compare its vertex components
7001 * with the vertex components from the vertex that can replace it. A
7002 * vertex is only fully replaced if all the components match and the
7003 * flag D3DXWELDEPSILONS_DONOTREMOVEVERTICES is not set, and they
7004 * belong to the same attribute group. Otherwise the vertex components
7005 * that are within epsilon are set to the same value.
7007 for (i = 0; i < 3 * This->numfaces; i++)
7009 D3DVERTEXELEMENT9 *decl_ptr;
7010 DWORD vertex_size = mesh->lpVtbl->GetNumBytesPerVertex(mesh);
7011 DWORD num_vertex_components;
7012 INT matches = 0;
7013 BOOL all_match;
7014 DWORD index = read_ib(indices, indices_are_32bit, i);
7016 for (decl_ptr = This->cached_declaration, num_vertex_components = 0; decl_ptr->Stream != 0xFF; decl_ptr++, num_vertex_components++)
7018 BYTE *to = &vertices[vertex_size*index + decl_ptr->Offset];
7019 BYTE *from = &vertices[vertex_size*point_reps[index] + decl_ptr->Offset];
7020 FLOAT epsilon = get_component_epsilon(decl_ptr, epsilons);
7022 /* Don't weld self */
7023 if (index == point_reps[index])
7025 matches++;
7026 continue;
7029 if (weld_component(to, from, decl_ptr->Type, epsilon))
7030 matches++;
7033 all_match = (num_vertex_components == matches);
7034 if (all_match && !(flags & D3DXWELDEPSILONS_DONOTREMOVEVERTICES))
7036 DWORD to_face = vertex_face_map[index];
7037 DWORD from_face = vertex_face_map[point_reps[index]];
7038 if(attributes[to_face] != attributes[from_face] && !(flags & D3DXWELDEPSILONS_DONOTSPLIT))
7039 continue;
7040 write_ib(indices, indices_are_32bit, i, point_reps[index]);
7043 mesh->lpVtbl->UnlockVertexBuffer(mesh);
7044 vertices = NULL;
7046 else if (flags & D3DXWELDEPSILONS_WELDALL)
7048 for (i = 0; i < 3 * This->numfaces; i++)
7050 DWORD index = read_ib(indices, indices_are_32bit, i);
7051 DWORD to_face = vertex_face_map[index];
7052 DWORD from_face = vertex_face_map[point_reps[index]];
7053 if(attributes[to_face] != attributes[from_face] && !(flags & D3DXWELDEPSILONS_DONOTSPLIT))
7054 continue;
7055 write_ib(indices, indices_are_32bit, i, point_reps[index]);
7058 mesh->lpVtbl->UnlockAttributeBuffer(mesh);
7059 attributes = NULL;
7060 mesh->lpVtbl->UnlockIndexBuffer(mesh);
7061 indices = NULL;
7063 /* Compact mesh using OptimizeInplace */
7064 optimize_flags = D3DXMESHOPT_COMPACT;
7065 hr = mesh->lpVtbl->OptimizeInplace(mesh, optimize_flags, adjacency_ptr, adjacency_out, face_remap_out, vertex_remap_out);
7066 if (FAILED(hr))
7068 ERR("Couldn't compact mesh.\n");
7069 goto cleanup;
7072 hr = D3D_OK;
7073 cleanup:
7074 HeapFree(GetProcessHeap(), 0, adjacency_generated);
7075 HeapFree(GetProcessHeap(), 0, point_reps);
7076 HeapFree(GetProcessHeap(), 0, vertex_face_map);
7077 if (attributes) mesh->lpVtbl->UnlockAttributeBuffer(mesh);
7078 if (indices) mesh->lpVtbl->UnlockIndexBuffer(mesh);
7079 if (vertex_remap) ID3DXBuffer_Release(vertex_remap);
7080 if (vertices) mesh->lpVtbl->UnlockVertexBuffer(mesh);
7082 return hr;
7085 /*************************************************************************
7086 * D3DXOptimizeFaces (D3DX9_36.@)
7088 * Re-orders the faces so the vertex cache is used optimally.
7090 * PARAMS
7091 * indices [I] Pointer to an index buffer belonging to a mesh.
7092 * num_faces [I] Number of faces in the mesh.
7093 * num_vertices [I] Number of vertices in the mesh.
7094 * indices_are_32bit [I] Specifies whether indices are 32- or 16-bit.
7095 * face_remap [I/O] The new order the faces should be drawn in.
7097 * RETURNS
7098 * Success: D3D_OK.
7099 * Failure: D3DERR_INVALIDCALL.
7101 * BUGS
7102 * The face re-ordering does not use the vertex cache optimally.
7105 HRESULT WINAPI D3DXOptimizeFaces(const void *indices, UINT num_faces,
7106 UINT num_vertices, BOOL indices_are_32bit, DWORD *face_remap)
7108 UINT i;
7109 UINT j = num_faces - 1;
7110 UINT limit_16_bit = 2 << 15; /* According to MSDN */
7111 HRESULT hr = D3D_OK;
7113 FIXME("indices %p, num_faces %u, num_vertices %u, indices_are_32bit %#x, face_remap %p semi-stub. "
7114 "Face order will not be optimal.\n",
7115 indices, num_faces, num_vertices, indices_are_32bit, face_remap);
7117 if (!indices_are_32bit && num_faces >= limit_16_bit)
7119 WARN("Number of faces must be less than %d when using 16-bit indices.\n",
7120 limit_16_bit);
7121 hr = D3DERR_INVALIDCALL;
7122 goto error;
7125 if (!face_remap)
7127 WARN("Face remap pointer is NULL.\n");
7128 hr = D3DERR_INVALIDCALL;
7129 goto error;
7132 /* The faces are drawn in reverse order for simple meshes. This ordering
7133 * is not optimal for complicated meshes, but will not break anything
7134 * either. The ordering should be changed to take advantage of the vertex
7135 * cache on the graphics card.
7137 * TODO Re-order to take advantage of vertex cache.
7139 for (i = 0; i < num_faces; i++)
7141 face_remap[i] = j--;
7144 return D3D_OK;
7146 error:
7147 return hr;