d3drm: Avoid LPDIRECT3DRMPROGRESSIVEMESH.
[wine/wine-gecko.git] / dlls / d3dx9_36 / mesh.c
blobf38a79ee285d528e307650da662a8319c5929faf
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 typedef struct ID3DXMeshImpl
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;
70 } ID3DXMeshImpl;
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 ID3DXMeshImpl *impl_from_ID3DXMesh(ID3DXMesh *iface)
95 return CONTAINING_RECORD(iface, ID3DXMeshImpl, ID3DXMesh_iface);
98 static HRESULT WINAPI ID3DXMeshImpl_QueryInterface(ID3DXMesh *iface, REFIID riid, LPVOID *object)
100 TRACE("(%p)->(%s, %p)\n", iface, debugstr_guid(riid), object);
102 if (IsEqualGUID(riid, &IID_IUnknown) ||
103 IsEqualGUID(riid, &IID_ID3DXBaseMesh) ||
104 IsEqualGUID(riid, &IID_ID3DXMesh))
106 iface->lpVtbl->AddRef(iface);
107 *object = iface;
108 return S_OK;
111 WARN("Interface %s not found.\n", debugstr_guid(riid));
113 return E_NOINTERFACE;
116 static ULONG WINAPI ID3DXMeshImpl_AddRef(ID3DXMesh *iface)
118 ID3DXMeshImpl *This = impl_from_ID3DXMesh(iface);
120 TRACE("(%p)->(): AddRef from %d\n", This, This->ref);
122 return InterlockedIncrement(&This->ref);
125 static ULONG WINAPI ID3DXMeshImpl_Release(ID3DXMesh *iface)
127 ID3DXMeshImpl *This = impl_from_ID3DXMesh(iface);
128 ULONG ref = InterlockedDecrement(&This->ref);
130 TRACE("(%p)->(): Release from %d\n", This, ref + 1);
132 if (!ref)
134 IDirect3DIndexBuffer9_Release(This->index_buffer);
135 IDirect3DVertexBuffer9_Release(This->vertex_buffer);
136 if (This->vertex_declaration)
137 IDirect3DVertexDeclaration9_Release(This->vertex_declaration);
138 IDirect3DDevice9_Release(This->device);
139 HeapFree(GetProcessHeap(), 0, This->attrib_buffer);
140 HeapFree(GetProcessHeap(), 0, This->attrib_table);
141 HeapFree(GetProcessHeap(), 0, This);
144 return ref;
147 /*** ID3DXBaseMesh ***/
148 static HRESULT WINAPI ID3DXMeshImpl_DrawSubset(ID3DXMesh *iface, DWORD attrib_id)
150 ID3DXMeshImpl *This = impl_from_ID3DXMesh(iface);
151 HRESULT hr;
152 DWORD face_start;
153 DWORD face_end = 0;
154 DWORD vertex_size;
156 TRACE("(%p)->(%u)\n", This, 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 ID3DXMeshImpl_GetNumFaces(ID3DXMesh *iface)
198 ID3DXMeshImpl *This = impl_from_ID3DXMesh(iface);
200 TRACE("(%p)\n", This);
202 return This->numfaces;
205 static DWORD WINAPI ID3DXMeshImpl_GetNumVertices(ID3DXMesh *iface)
207 ID3DXMeshImpl *This = impl_from_ID3DXMesh(iface);
209 TRACE("(%p)\n", This);
211 return This->numvertices;
214 static DWORD WINAPI ID3DXMeshImpl_GetFVF(ID3DXMesh *iface)
216 ID3DXMeshImpl *This = impl_from_ID3DXMesh(iface);
218 TRACE("(%p)\n", This);
220 return This->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 ID3DXMeshImpl_GetDeclaration(ID3DXMesh *iface, D3DVERTEXELEMENT9 declaration[MAX_FVF_DECL_SIZE])
230 ID3DXMeshImpl *This = impl_from_ID3DXMesh(iface);
232 TRACE("(%p)\n", This);
234 if (declaration == NULL) return D3DERR_INVALIDCALL;
236 copy_declaration(declaration, This->cached_declaration, This->num_elem);
238 return D3D_OK;
241 static DWORD WINAPI ID3DXMeshImpl_GetNumBytesPerVertex(ID3DXMesh *iface)
243 ID3DXMeshImpl *This = impl_from_ID3DXMesh(iface);
245 TRACE("iface (%p)\n", This);
247 return This->vertex_declaration_size;
250 static DWORD WINAPI ID3DXMeshImpl_GetOptions(ID3DXMesh *iface)
252 ID3DXMeshImpl *This = impl_from_ID3DXMesh(iface);
254 TRACE("(%p)\n", This);
256 return This->options;
259 static HRESULT WINAPI ID3DXMeshImpl_GetDevice(struct ID3DXMesh *iface, struct IDirect3DDevice9 **device)
261 ID3DXMeshImpl *This = impl_from_ID3DXMesh(iface);
263 TRACE("(%p)->(%p)\n", This, device);
265 if (device == NULL) return D3DERR_INVALIDCALL;
266 *device = This->device;
267 IDirect3DDevice9_AddRef(This->device);
269 return D3D_OK;
272 static HRESULT WINAPI ID3DXMeshImpl_CloneMeshFVF(struct ID3DXMesh *iface, DWORD options, DWORD fvf,
273 struct IDirect3DDevice9 *device, struct ID3DXMesh **clone_mesh)
275 ID3DXMeshImpl *This = impl_from_ID3DXMesh(iface);
276 HRESULT hr;
277 D3DVERTEXELEMENT9 declaration[MAX_FVF_DECL_SIZE];
279 TRACE("(%p)->(%x,%x,%p,%p)\n", This, options, fvf, device, clone_mesh);
281 hr = D3DXDeclaratorFromFVF(fvf, declaration);
282 if (FAILED(hr)) return hr;
284 return iface->lpVtbl->CloneMesh(iface, options, declaration, device, clone_mesh);
287 static FLOAT scale_clamp_ubyten(FLOAT value)
289 value = value * UCHAR_MAX;
291 if (value < 0.0f)
293 return 0.0f;
295 else
297 if (value > UCHAR_MAX) /* Clamp at 255 */
298 return UCHAR_MAX;
299 else
300 return value;
304 static FLOAT scale_clamp_shortn(FLOAT value)
306 value = value * SHRT_MAX;
308 /* The tests show that the range is SHRT_MIN + 1 to SHRT_MAX. */
309 if (value <= SHRT_MIN)
311 return SHRT_MIN + 1;
313 else if (value > SHRT_MAX)
315 return SHRT_MAX;
317 else
319 return value;
323 static FLOAT scale_clamp_ushortn(FLOAT value)
325 value = value * USHRT_MAX;
327 if (value < 0.0f)
329 return 0.0f;
331 else
333 if (value > USHRT_MAX) /* Clamp at 65535 */
334 return USHRT_MAX;
335 else
336 return value;
340 static INT simple_round(FLOAT value)
342 int res = (INT)(value + 0.5f);
344 return res;
347 static void convert_float4(BYTE *dst, CONST D3DXVECTOR4 *src, D3DDECLTYPE type_dst)
349 BOOL fixme_once = FALSE;
351 switch (type_dst)
353 case D3DDECLTYPE_FLOAT1:
355 FLOAT *dst_ptr = (FLOAT*)dst;
356 *dst_ptr = src->x;
357 break;
359 case D3DDECLTYPE_FLOAT2:
361 D3DXVECTOR2 *dst_ptr = (D3DXVECTOR2*)dst;
362 dst_ptr->x = src->x;
363 dst_ptr->y = src->y;
364 break;
366 case D3DDECLTYPE_FLOAT3:
368 D3DXVECTOR3 *dst_ptr = (D3DXVECTOR3*)dst;
369 dst_ptr->x = src->x;
370 dst_ptr->y = src->y;
371 dst_ptr->z = src->z;
372 break;
374 case D3DDECLTYPE_FLOAT4:
376 D3DXVECTOR4 *dst_ptr = (D3DXVECTOR4*)dst;
377 dst_ptr->x = src->x;
378 dst_ptr->y = src->y;
379 dst_ptr->z = src->z;
380 dst_ptr->w = src->w;
381 break;
383 case D3DDECLTYPE_D3DCOLOR:
385 dst[0] = (BYTE)simple_round(scale_clamp_ubyten(src->z));
386 dst[1] = (BYTE)simple_round(scale_clamp_ubyten(src->y));
387 dst[2] = (BYTE)simple_round(scale_clamp_ubyten(src->x));
388 dst[3] = (BYTE)simple_round(scale_clamp_ubyten(src->w));
389 break;
391 case D3DDECLTYPE_UBYTE4:
393 dst[0] = src->x < 0.0f ? 0 : (BYTE)simple_round(src->x);
394 dst[1] = src->y < 0.0f ? 0 : (BYTE)simple_round(src->y);
395 dst[2] = src->z < 0.0f ? 0 : (BYTE)simple_round(src->z);
396 dst[3] = src->w < 0.0f ? 0 : (BYTE)simple_round(src->w);
397 break;
399 case D3DDECLTYPE_SHORT2:
401 SHORT *dst_ptr = (SHORT*)dst;
402 dst_ptr[0] = (SHORT)simple_round(src->x);
403 dst_ptr[1] = (SHORT)simple_round(src->y);
404 break;
406 case D3DDECLTYPE_SHORT4:
408 SHORT *dst_ptr = (SHORT*)dst;
409 dst_ptr[0] = (SHORT)simple_round(src->x);
410 dst_ptr[1] = (SHORT)simple_round(src->y);
411 dst_ptr[2] = (SHORT)simple_round(src->z);
412 dst_ptr[3] = (SHORT)simple_round(src->w);
413 break;
415 case D3DDECLTYPE_UBYTE4N:
417 dst[0] = (BYTE)simple_round(scale_clamp_ubyten(src->x));
418 dst[1] = (BYTE)simple_round(scale_clamp_ubyten(src->y));
419 dst[2] = (BYTE)simple_round(scale_clamp_ubyten(src->z));
420 dst[3] = (BYTE)simple_round(scale_clamp_ubyten(src->w));
421 break;
423 case D3DDECLTYPE_SHORT2N:
425 SHORT *dst_ptr = (SHORT*)dst;
426 dst_ptr[0] = (SHORT)simple_round(scale_clamp_shortn(src->x));
427 dst_ptr[1] = (SHORT)simple_round(scale_clamp_shortn(src->y));
428 break;
430 case D3DDECLTYPE_SHORT4N:
432 SHORT *dst_ptr = (SHORT*)dst;
433 dst_ptr[0] = (SHORT)simple_round(scale_clamp_shortn(src->x));
434 dst_ptr[1] = (SHORT)simple_round(scale_clamp_shortn(src->y));
435 dst_ptr[2] = (SHORT)simple_round(scale_clamp_shortn(src->z));
436 dst_ptr[3] = (SHORT)simple_round(scale_clamp_shortn(src->w));
437 break;
439 case D3DDECLTYPE_USHORT2N:
441 USHORT *dst_ptr = (USHORT*)dst;
442 dst_ptr[0] = (USHORT)simple_round(scale_clamp_ushortn(src->x));
443 dst_ptr[1] = (USHORT)simple_round(scale_clamp_ushortn(src->y));
444 break;
446 case D3DDECLTYPE_USHORT4N:
448 USHORT *dst_ptr = (USHORT*)dst;
449 dst_ptr[0] = (USHORT)simple_round(scale_clamp_ushortn(src->x));
450 dst_ptr[1] = (USHORT)simple_round(scale_clamp_ushortn(src->y));
451 dst_ptr[2] = (USHORT)simple_round(scale_clamp_ushortn(src->z));
452 dst_ptr[3] = (USHORT)simple_round(scale_clamp_ushortn(src->w));
453 break;
455 case D3DDECLTYPE_FLOAT16_2:
457 D3DXFloat32To16Array((D3DXFLOAT16*)dst, (FLOAT*)src, 2);
458 break;
460 case D3DDECLTYPE_FLOAT16_4:
462 D3DXFloat32To16Array((D3DXFLOAT16*)dst, (FLOAT*)src, 4);
463 break;
465 default:
466 if (!fixme_once++)
467 FIXME("Conversion from D3DDECLTYPE_FLOAT4 to %d not implemented.\n", type_dst);
468 break;
472 static void convert_component(BYTE *dst, BYTE *src, D3DDECLTYPE type_dst, D3DDECLTYPE type_src)
474 BOOL fixme_once = FALSE;
476 switch (type_src)
478 case D3DDECLTYPE_FLOAT1:
480 FLOAT *src_ptr = (FLOAT*)src;
481 D3DXVECTOR4 src_float4 = {*src_ptr, 0.0f, 0.0f, 1.0f};
482 convert_float4(dst, &src_float4, type_dst);
483 break;
485 case D3DDECLTYPE_FLOAT2:
487 D3DXVECTOR2 *src_ptr = (D3DXVECTOR2*)src;
488 D3DXVECTOR4 src_float4 = {src_ptr->x, src_ptr->y, 0.0f, 1.0f};
489 convert_float4(dst, &src_float4, type_dst);
490 break;
492 case D3DDECLTYPE_FLOAT3:
494 D3DXVECTOR3 *src_ptr = (D3DXVECTOR3*)src;
495 D3DXVECTOR4 src_float4 = {src_ptr->x, src_ptr->y, src_ptr->z, 1.0f};
496 convert_float4(dst, &src_float4, type_dst);
497 break;
499 case D3DDECLTYPE_FLOAT4:
501 D3DXVECTOR4 *src_ptr = (D3DXVECTOR4*)src;
502 D3DXVECTOR4 src_float4 = {src_ptr->x, src_ptr->y, src_ptr->z, src_ptr->w};
503 convert_float4(dst, &src_float4, type_dst);
504 break;
506 case D3DDECLTYPE_D3DCOLOR:
508 D3DXVECTOR4 src_float4 =
510 (FLOAT)src[2]/UCHAR_MAX,
511 (FLOAT)src[1]/UCHAR_MAX,
512 (FLOAT)src[0]/UCHAR_MAX,
513 (FLOAT)src[3]/UCHAR_MAX
515 convert_float4(dst, &src_float4, type_dst);
516 break;
518 case D3DDECLTYPE_UBYTE4:
520 D3DXVECTOR4 src_float4 = {src[0], src[1], src[2], src[3]};
521 convert_float4(dst, &src_float4, type_dst);
522 break;
524 case D3DDECLTYPE_SHORT2:
526 SHORT *src_ptr = (SHORT*)src;
527 D3DXVECTOR4 src_float4 = {src_ptr[0], src_ptr[1], 0.0f, 1.0f};
528 convert_float4(dst, &src_float4, type_dst);
529 break;
531 case D3DDECLTYPE_SHORT4:
533 SHORT *src_ptr = (SHORT*)src;
534 D3DXVECTOR4 src_float4 = {src_ptr[0], src_ptr[1], src_ptr[2], src_ptr[3]};
535 convert_float4(dst, &src_float4, type_dst);
536 break;
538 case D3DDECLTYPE_UBYTE4N:
540 D3DXVECTOR4 src_float4 =
542 (FLOAT)src[0]/UCHAR_MAX,
543 (FLOAT)src[1]/UCHAR_MAX,
544 (FLOAT)src[2]/UCHAR_MAX,
545 (FLOAT)src[3]/UCHAR_MAX
547 convert_float4(dst, &src_float4, type_dst);
548 break;
550 case D3DDECLTYPE_SHORT2N:
552 SHORT *src_ptr = (SHORT*)src;
553 D3DXVECTOR4 src_float4 = {(FLOAT)src_ptr[0]/SHRT_MAX, (FLOAT)src_ptr[1]/SHRT_MAX, 0.0f, 1.0f};
554 convert_float4(dst, &src_float4, type_dst);
555 break;
557 case D3DDECLTYPE_SHORT4N:
559 SHORT *src_ptr = (SHORT*)src;
560 D3DXVECTOR4 src_float4 =
562 (FLOAT)src_ptr[0]/SHRT_MAX,
563 (FLOAT)src_ptr[1]/SHRT_MAX,
564 (FLOAT)src_ptr[2]/SHRT_MAX,
565 (FLOAT)src_ptr[3]/SHRT_MAX
567 convert_float4(dst, &src_float4, type_dst);
568 break;
570 case D3DDECLTYPE_FLOAT16_2:
572 D3DXVECTOR4 src_float4 = {0.0f, 0.0f, 0.0f, 1.0f};
573 D3DXFloat16To32Array((FLOAT*)&src_float4, (D3DXFLOAT16*)src, 2);
574 convert_float4(dst, &src_float4, type_dst);
575 break;
577 case D3DDECLTYPE_FLOAT16_4:
579 D3DXVECTOR4 src_float4;
580 D3DXFloat16To32Array((FLOAT*)&src_float4, (D3DXFLOAT16*)src, 4);
581 convert_float4(dst, &src_float4, type_dst);
582 break;
584 default:
585 if (!fixme_once++)
586 FIXME("Conversion of D3DDECLTYPE %d to %d not implemented.\n", type_src, type_dst);
587 break;
591 static INT get_equivalent_declaration_index(D3DVERTEXELEMENT9 orig_declaration, D3DVERTEXELEMENT9 *declaration)
593 INT i;
595 for (i = 0; declaration[i].Stream != 0xff; i++)
597 if (orig_declaration.Usage == declaration[i].Usage
598 && orig_declaration.UsageIndex == declaration[i].UsageIndex)
600 return i;
604 return -1;
607 static HRESULT convert_vertex_buffer(ID3DXMesh *mesh_dst, ID3DXMesh *mesh_src)
609 HRESULT hr;
610 D3DVERTEXELEMENT9 orig_declaration[MAX_FVF_DECL_SIZE] = {D3DDECL_END()};
611 D3DVERTEXELEMENT9 declaration[MAX_FVF_DECL_SIZE] = {D3DDECL_END()};
612 BYTE *vb_dst = NULL;
613 BYTE *vb_src = NULL;
614 UINT i;
615 UINT num_vertices = mesh_src->lpVtbl->GetNumVertices(mesh_src);
616 UINT dst_vertex_size = mesh_dst->lpVtbl->GetNumBytesPerVertex(mesh_dst);
617 UINT src_vertex_size = mesh_src->lpVtbl->GetNumBytesPerVertex(mesh_src);
619 hr = mesh_src->lpVtbl->GetDeclaration(mesh_src, orig_declaration);
620 if (FAILED(hr)) return hr;
621 hr = mesh_dst->lpVtbl->GetDeclaration(mesh_dst, declaration);
622 if (FAILED(hr)) return hr;
624 hr = mesh_src->lpVtbl->LockVertexBuffer(mesh_src, D3DLOCK_READONLY, (void**)&vb_src);
625 if (FAILED(hr)) goto cleanup;
626 hr = mesh_dst->lpVtbl->LockVertexBuffer(mesh_dst, 0, (void**)&vb_dst);
627 if (FAILED(hr)) goto cleanup;
629 /* Clear all new fields by clearing the entire vertex buffer. */
630 memset(vb_dst, 0, num_vertices * dst_vertex_size);
632 for (i = 0; orig_declaration[i].Stream != 0xff; i++)
634 INT eq_idx = get_equivalent_declaration_index(orig_declaration[i], declaration);
636 if (eq_idx >= 0)
638 UINT j;
639 for (j = 0; j < num_vertices; j++)
641 UINT idx_dst = dst_vertex_size * j + declaration[eq_idx].Offset;
642 UINT idx_src = src_vertex_size * j + orig_declaration[i].Offset;
643 UINT type_size = d3dx_decltype_size[orig_declaration[i].Type];
645 if (orig_declaration[i].Type == declaration[eq_idx].Type)
646 memcpy(&vb_dst[idx_dst], &vb_src[idx_src], type_size);
647 else
648 convert_component(&vb_dst[idx_dst], &vb_src[idx_src], declaration[eq_idx].Type, orig_declaration[i].Type);
653 hr = D3D_OK;
654 cleanup:
655 if (vb_dst) mesh_dst->lpVtbl->UnlockVertexBuffer(mesh_dst);
656 if (vb_src) mesh_src->lpVtbl->UnlockVertexBuffer(mesh_src);
658 return hr;
661 static BOOL declaration_equals(CONST D3DVERTEXELEMENT9 *declaration1, CONST D3DVERTEXELEMENT9 *declaration2)
663 UINT size1 = 0, size2 = 0;
665 /* Find the size of each declaration */
666 while (declaration1[size1].Stream != 0xff) size1++;
667 while (declaration2[size2].Stream != 0xff) size2++;
669 /* If not same size then they are definitely not equal */
670 if (size1 != size2)
671 return FALSE;
673 /* Check that all components are the same */
674 if (memcmp(declaration1, declaration2, size1*sizeof(*declaration1)) == 0)
675 return TRUE;
677 return FALSE;
680 static HRESULT WINAPI ID3DXMeshImpl_CloneMesh(struct ID3DXMesh *iface, DWORD options,
681 const D3DVERTEXELEMENT9 *declaration, struct IDirect3DDevice9 *device, struct ID3DXMesh **clone_mesh_out)
683 ID3DXMeshImpl *This = impl_from_ID3DXMesh(iface);
684 ID3DXMeshImpl *cloned_this;
685 ID3DXMesh *clone_mesh;
686 D3DVERTEXELEMENT9 orig_declaration[MAX_FVF_DECL_SIZE] = { D3DDECL_END() };
687 void *data_in, *data_out;
688 DWORD vertex_size;
689 HRESULT hr;
690 BOOL same_declaration;
692 TRACE("(%p)->(%x,%p,%p,%p)\n", This, options, declaration, device, clone_mesh_out);
694 if (!clone_mesh_out)
695 return D3DERR_INVALIDCALL;
697 hr = iface->lpVtbl->GetDeclaration(iface, orig_declaration);
698 if (FAILED(hr)) return hr;
700 hr = D3DXCreateMesh(This->numfaces, This->numvertices, options & ~D3DXMESH_VB_SHARE,
701 declaration, device, &clone_mesh);
702 if (FAILED(hr)) return hr;
704 cloned_this = impl_from_ID3DXMesh(clone_mesh);
705 vertex_size = clone_mesh->lpVtbl->GetNumBytesPerVertex(clone_mesh);
706 same_declaration = declaration_equals(declaration, orig_declaration);
708 if (options & D3DXMESH_VB_SHARE) {
709 if (!same_declaration) {
710 hr = D3DERR_INVALIDCALL;
711 goto error;
713 IDirect3DVertexBuffer9_AddRef(This->vertex_buffer);
714 /* FIXME: refactor to avoid creating a new vertex buffer */
715 IDirect3DVertexBuffer9_Release(cloned_this->vertex_buffer);
716 cloned_this->vertex_buffer = This->vertex_buffer;
717 } else if (same_declaration) {
718 hr = iface->lpVtbl->LockVertexBuffer(iface, D3DLOCK_READONLY, &data_in);
719 if (FAILED(hr)) goto error;
720 hr = clone_mesh->lpVtbl->LockVertexBuffer(clone_mesh, 0, &data_out);
721 if (FAILED(hr)) {
722 iface->lpVtbl->UnlockVertexBuffer(iface);
723 goto error;
725 memcpy(data_out, data_in, This->numvertices * vertex_size);
726 clone_mesh->lpVtbl->UnlockVertexBuffer(clone_mesh);
727 iface->lpVtbl->UnlockVertexBuffer(iface);
728 } else {
729 hr = convert_vertex_buffer(clone_mesh, iface);
730 if (FAILED(hr)) goto error;
733 hr = iface->lpVtbl->LockIndexBuffer(iface, D3DLOCK_READONLY, &data_in);
734 if (FAILED(hr)) goto error;
735 hr = clone_mesh->lpVtbl->LockIndexBuffer(clone_mesh, 0, &data_out);
736 if (FAILED(hr)) {
737 iface->lpVtbl->UnlockIndexBuffer(iface);
738 goto error;
740 if ((options ^ This->options) & D3DXMESH_32BIT) {
741 DWORD i;
742 if (options & D3DXMESH_32BIT) {
743 for (i = 0; i < This->numfaces * 3; i++)
744 ((DWORD*)data_out)[i] = ((WORD*)data_in)[i];
745 } else {
746 for (i = 0; i < This->numfaces * 3; i++)
747 ((WORD*)data_out)[i] = ((DWORD*)data_in)[i];
749 } else {
750 memcpy(data_out, data_in, This->numfaces * 3 * (options & D3DXMESH_32BIT ? 4 : 2));
752 clone_mesh->lpVtbl->UnlockIndexBuffer(clone_mesh);
753 iface->lpVtbl->UnlockIndexBuffer(iface);
755 memcpy(cloned_this->attrib_buffer, This->attrib_buffer, This->numfaces * sizeof(*This->attrib_buffer));
757 if (This->attrib_table_size)
759 cloned_this->attrib_table_size = This->attrib_table_size;
760 cloned_this->attrib_table = HeapAlloc(GetProcessHeap(), 0, This->attrib_table_size * sizeof(*This->attrib_table));
761 if (!cloned_this->attrib_table) {
762 hr = E_OUTOFMEMORY;
763 goto error;
765 memcpy(cloned_this->attrib_table, This->attrib_table, This->attrib_table_size * sizeof(*This->attrib_table));
768 *clone_mesh_out = clone_mesh;
770 return D3D_OK;
771 error:
772 IUnknown_Release(clone_mesh);
773 return hr;
776 static HRESULT WINAPI ID3DXMeshImpl_GetVertexBuffer(struct ID3DXMesh *iface,
777 struct IDirect3DVertexBuffer9 **vertex_buffer)
779 ID3DXMeshImpl *This = impl_from_ID3DXMesh(iface);
781 TRACE("(%p)->(%p)\n", This, vertex_buffer);
783 if (vertex_buffer == NULL) return D3DERR_INVALIDCALL;
784 *vertex_buffer = This->vertex_buffer;
785 IDirect3DVertexBuffer9_AddRef(This->vertex_buffer);
787 return D3D_OK;
790 static HRESULT WINAPI ID3DXMeshImpl_GetIndexBuffer(struct ID3DXMesh *iface,
791 struct IDirect3DIndexBuffer9 **index_buffer)
793 ID3DXMeshImpl *This = impl_from_ID3DXMesh(iface);
795 TRACE("(%p)->(%p)\n", This, index_buffer);
797 if (index_buffer == NULL) return D3DERR_INVALIDCALL;
798 *index_buffer = This->index_buffer;
799 IDirect3DIndexBuffer9_AddRef(This->index_buffer);
801 return D3D_OK;
804 static HRESULT WINAPI ID3DXMeshImpl_LockVertexBuffer(ID3DXMesh *iface, DWORD flags, LPVOID *data)
806 ID3DXMeshImpl *This = impl_from_ID3DXMesh(iface);
808 TRACE("(%p)->(%u,%p)\n", This, flags, data);
810 return IDirect3DVertexBuffer9_Lock(This->vertex_buffer, 0, 0, data, flags);
813 static HRESULT WINAPI ID3DXMeshImpl_UnlockVertexBuffer(ID3DXMesh *iface)
815 ID3DXMeshImpl *This = impl_from_ID3DXMesh(iface);
817 TRACE("(%p)\n", This);
819 return IDirect3DVertexBuffer9_Unlock(This->vertex_buffer);
822 static HRESULT WINAPI ID3DXMeshImpl_LockIndexBuffer(ID3DXMesh *iface, DWORD flags, LPVOID *data)
824 ID3DXMeshImpl *This = impl_from_ID3DXMesh(iface);
826 TRACE("(%p)->(%u,%p)\n", This, flags, data);
828 return IDirect3DIndexBuffer9_Lock(This->index_buffer, 0, 0, data, flags);
831 static HRESULT WINAPI ID3DXMeshImpl_UnlockIndexBuffer(ID3DXMesh *iface)
833 ID3DXMeshImpl *This = impl_from_ID3DXMesh(iface);
835 TRACE("(%p)\n", This);
837 return IDirect3DIndexBuffer9_Unlock(This->index_buffer);
840 static HRESULT WINAPI ID3DXMeshImpl_GetAttributeTable(ID3DXMesh *iface, D3DXATTRIBUTERANGE *attrib_table, DWORD *attrib_table_size)
842 ID3DXMeshImpl *This = impl_from_ID3DXMesh(iface);
844 TRACE("(%p)->(%p,%p)\n", This, attrib_table, attrib_table_size);
846 if (attrib_table_size)
847 *attrib_table_size = This->attrib_table_size;
849 if (attrib_table)
850 CopyMemory(attrib_table, This->attrib_table, This->attrib_table_size * sizeof(*attrib_table));
852 return D3D_OK;
855 struct edge_face
857 struct list entry;
858 DWORD v2;
859 DWORD face;
862 struct edge_face_map
864 struct list *lists;
865 struct edge_face *entries;
868 /* Builds up a map of which face a new edge belongs to. That way the adjacency
869 * of another edge can be looked up. An edge has an adjacent face if there
870 * is an edge going in the opposite direction in the map. For example if the
871 * edge (v1, v2) belongs to face 4, and there is a mapping (v2, v1)->7, then
872 * face 4 and 7 are adjacent.
874 * Each edge might have been replaced with another edge, or none at all. There
875 * is at most one edge to face mapping, i.e. an edge can only belong to one
876 * face.
878 static HRESULT init_edge_face_map(struct edge_face_map *edge_face_map, CONST DWORD *index_buffer, CONST DWORD *point_reps, CONST DWORD num_faces)
880 DWORD face, edge;
881 DWORD i;
883 edge_face_map->lists = HeapAlloc(GetProcessHeap(), 0, 3 * num_faces * sizeof(*edge_face_map->lists));
884 if (!edge_face_map->lists) return E_OUTOFMEMORY;
886 edge_face_map->entries = HeapAlloc(GetProcessHeap(), 0, 3 * num_faces * sizeof(*edge_face_map->entries));
887 if (!edge_face_map->entries) return E_OUTOFMEMORY;
890 /* Initialize all lists */
891 for (i = 0; i < 3 * num_faces; i++)
893 list_init(&edge_face_map->lists[i]);
895 /* Build edge face mapping */
896 for (face = 0; face < num_faces; face++)
898 for (edge = 0; edge < 3; edge++)
900 DWORD v1 = index_buffer[3*face + edge];
901 DWORD v2 = index_buffer[3*face + (edge+1)%3];
902 DWORD new_v1 = point_reps[v1]; /* What v1 has been replaced with */
903 DWORD new_v2 = point_reps[v2];
905 if (v1 != v2) /* Only map non-collapsed edges */
907 i = 3*face + edge;
908 edge_face_map->entries[i].v2 = new_v2;
909 edge_face_map->entries[i].face = face;
910 list_add_head(&edge_face_map->lists[new_v1], &edge_face_map->entries[i].entry);
915 return D3D_OK;
918 static DWORD find_adjacent_face(struct edge_face_map *edge_face_map, DWORD vertex1, DWORD vertex2, CONST DWORD num_faces)
920 struct edge_face *edge_face_ptr;
922 LIST_FOR_EACH_ENTRY(edge_face_ptr, &edge_face_map->lists[vertex2], struct edge_face, entry)
924 if (edge_face_ptr->v2 == vertex1)
925 return edge_face_ptr->face;
928 return -1;
931 static DWORD *generate_identity_point_reps(DWORD num_vertices)
933 DWORD *id_point_reps;
934 DWORD i;
936 id_point_reps = HeapAlloc(GetProcessHeap(), 0, num_vertices * sizeof(*id_point_reps));
937 if (!id_point_reps)
938 return NULL;
940 for (i = 0; i < num_vertices; i++)
942 id_point_reps[i] = i;
945 return id_point_reps;
948 static HRESULT WINAPI ID3DXMeshImpl_ConvertPointRepsToAdjacency(ID3DXMesh *iface, CONST DWORD *point_reps, DWORD *adjacency)
950 ID3DXMeshImpl *This = impl_from_ID3DXMesh(iface);
951 HRESULT hr;
952 DWORD num_faces = iface->lpVtbl->GetNumFaces(iface);
953 DWORD num_vertices = iface->lpVtbl->GetNumVertices(iface);
954 DWORD options = iface->lpVtbl->GetOptions(iface);
955 BOOL indices_are_16_bit = !(options & D3DXMESH_32BIT);
956 DWORD *ib = NULL;
957 void *ib_ptr = NULL;
958 DWORD face;
959 DWORD edge;
960 struct edge_face_map edge_face_map = {0};
961 CONST DWORD *point_reps_ptr = NULL;
962 DWORD *id_point_reps = NULL;
964 TRACE("(%p)->(%p,%p)\n", This, point_reps, adjacency);
966 if (!adjacency) return D3DERR_INVALIDCALL;
968 if (!point_reps) /* Identity point reps */
970 id_point_reps = generate_identity_point_reps(num_vertices);
971 if (!id_point_reps)
973 hr = E_OUTOFMEMORY;
974 goto cleanup;
977 point_reps_ptr = id_point_reps;
979 else
981 point_reps_ptr = point_reps;
984 hr = iface->lpVtbl->LockIndexBuffer(iface, D3DLOCK_READONLY, &ib_ptr);
985 if (FAILED(hr)) goto cleanup;
987 if (indices_are_16_bit)
989 /* Widen 16 bit to 32 bit */
990 DWORD i;
991 WORD *ib_16bit = ib_ptr;
992 ib = HeapAlloc(GetProcessHeap(), 0, 3 * num_faces * sizeof(DWORD));
993 if (!ib)
995 hr = E_OUTOFMEMORY;
996 goto cleanup;
998 for (i = 0; i < 3 * num_faces; i++)
1000 ib[i] = ib_16bit[i];
1003 else
1005 ib = ib_ptr;
1008 hr = init_edge_face_map(&edge_face_map, ib, point_reps_ptr, num_faces);
1009 if (FAILED(hr)) goto cleanup;
1011 /* Create adjacency */
1012 for (face = 0; face < num_faces; face++)
1014 for (edge = 0; edge < 3; edge++)
1016 DWORD v1 = ib[3*face + edge];
1017 DWORD v2 = ib[3*face + (edge+1)%3];
1018 DWORD new_v1 = point_reps_ptr[v1];
1019 DWORD new_v2 = point_reps_ptr[v2];
1020 DWORD adj_face;
1022 adj_face = find_adjacent_face(&edge_face_map, new_v1, new_v2, num_faces);
1023 adjacency[3*face + edge] = adj_face;
1027 hr = D3D_OK;
1028 cleanup:
1029 HeapFree(GetProcessHeap(), 0, id_point_reps);
1030 if (indices_are_16_bit) HeapFree(GetProcessHeap(), 0, ib);
1031 HeapFree(GetProcessHeap(), 0, edge_face_map.lists);
1032 HeapFree(GetProcessHeap(), 0, edge_face_map.entries);
1033 if(ib_ptr) iface->lpVtbl->UnlockIndexBuffer(iface);
1034 return hr;
1037 /* ConvertAdjacencyToPointReps helper function.
1039 * Goes around the edges of each face and replaces the vertices in any adjacent
1040 * face's edge with its own vertices(if its vertices have a lower index). This
1041 * way as few as possible low index vertices are shared among the faces. The
1042 * re-ordered index buffer is stored in new_indices.
1044 * The vertices in a point representation must be ordered sequentially, e.g.
1045 * index 5 holds the index of the vertex that replaces vertex 5, i.e. if
1046 * vertex 5 is replaced by vertex 3 then index 5 would contain 3. If no vertex
1047 * replaces it, then it contains the same number as the index itself, e.g.
1048 * index 5 would contain 5. */
1049 static HRESULT propagate_face_vertices(CONST DWORD *adjacency, DWORD *point_reps,
1050 CONST DWORD *indices, DWORD *new_indices,
1051 CONST DWORD face, CONST DWORD numfaces)
1053 const unsigned int VERTS_PER_FACE = 3;
1054 DWORD edge, opp_edge;
1055 DWORD face_base = VERTS_PER_FACE * face;
1057 for (edge = 0; edge < VERTS_PER_FACE; edge++)
1059 DWORD adj_face = adjacency[face_base + edge];
1060 DWORD adj_face_base;
1061 DWORD i;
1062 if (adj_face == -1) /* No adjacent face. */
1063 continue;
1064 else if (adj_face >= numfaces)
1066 /* This throws exception on Windows */
1067 WARN("Index out of bounds. Got %d expected less than %d.\n",
1068 adj_face, numfaces);
1069 return D3DERR_INVALIDCALL;
1071 adj_face_base = 3 * adj_face;
1073 /* Find opposite edge in adjacent face. */
1074 for (opp_edge = 0; opp_edge < VERTS_PER_FACE; opp_edge++)
1076 DWORD opp_edge_index = adj_face_base + opp_edge;
1077 if (adjacency[opp_edge_index] == face)
1078 break; /* Found opposite edge. */
1081 /* Replaces vertices in opposite edge with vertices from current edge. */
1082 for (i = 0; i < 2; i++)
1084 DWORD from = face_base + (edge + (1 - i)) % VERTS_PER_FACE;
1085 DWORD to = adj_face_base + (opp_edge + i) % VERTS_PER_FACE;
1087 /* Propagate lowest index. */
1088 if (new_indices[to] > new_indices[from])
1090 new_indices[to] = new_indices[from];
1091 point_reps[indices[to]] = new_indices[from];
1096 return D3D_OK;
1099 static HRESULT WINAPI ID3DXMeshImpl_ConvertAdjacencyToPointReps(ID3DXMesh *iface, CONST DWORD *adjacency, DWORD *point_reps)
1101 HRESULT hr;
1102 DWORD face;
1103 DWORD i;
1104 DWORD *indices = NULL;
1105 WORD *indices_16bit = NULL;
1106 DWORD *new_indices = NULL;
1107 const unsigned int VERTS_PER_FACE = 3;
1109 ID3DXMeshImpl *This = impl_from_ID3DXMesh(iface);
1111 TRACE("(%p)->(%p,%p)\n", This, adjacency, point_reps);
1113 if (!adjacency)
1115 WARN("NULL adjacency.\n");
1116 hr = D3DERR_INVALIDCALL;
1117 goto cleanup;
1120 if (!point_reps)
1122 WARN("NULL point_reps.\n");
1123 hr = D3DERR_INVALIDCALL;
1124 goto cleanup;
1127 /* Should never happen as CreateMesh does not allow meshes with 0 faces */
1128 if (This->numfaces == 0)
1130 ERR("Number of faces was zero.\n");
1131 hr = D3DERR_INVALIDCALL;
1132 goto cleanup;
1135 new_indices = HeapAlloc(GetProcessHeap(), 0, VERTS_PER_FACE * This->numfaces * sizeof(*indices));
1136 if (!new_indices)
1138 hr = E_OUTOFMEMORY;
1139 goto cleanup;
1142 if (This->options & D3DXMESH_32BIT)
1144 hr = iface->lpVtbl->LockIndexBuffer(iface, D3DLOCK_READONLY, (void**)&indices);
1145 if (FAILED(hr)) goto cleanup;
1146 memcpy(new_indices, indices, VERTS_PER_FACE * This->numfaces * sizeof(*indices));
1148 else
1150 /* Make a widening copy of indices_16bit into indices and new_indices
1151 * in order to re-use the helper function */
1152 hr = iface->lpVtbl->LockIndexBuffer(iface, D3DLOCK_READONLY, (void**)&indices_16bit);
1153 if (FAILED(hr)) goto cleanup;
1154 indices = HeapAlloc(GetProcessHeap(), 0, VERTS_PER_FACE * This->numfaces * sizeof(*indices));
1155 if (!indices)
1157 hr = E_OUTOFMEMORY;
1158 goto cleanup;
1160 for (i = 0; i < VERTS_PER_FACE * This->numfaces; i++)
1162 new_indices[i] = indices_16bit[i];
1163 indices[i] = indices_16bit[i];
1167 /* Vertices are ordered sequentially in the point representation. */
1168 for (i = 0; i < This->numvertices; i++)
1170 point_reps[i] = i;
1173 /* Propagate vertices with low indices so as few vertices as possible
1174 * are used in the mesh.
1176 for (face = 0; face < This->numfaces; face++)
1178 hr = propagate_face_vertices(adjacency, point_reps, indices, new_indices, face, This->numfaces);
1179 if (FAILED(hr)) goto cleanup;
1181 /* Go in opposite direction to catch all face orderings */
1182 for (face = 0; face < This->numfaces; face++)
1184 hr = propagate_face_vertices(adjacency, point_reps,
1185 indices, new_indices,
1186 (This->numfaces - 1) - face, This->numfaces);
1187 if (FAILED(hr)) goto cleanup;
1190 hr = D3D_OK;
1191 cleanup:
1192 if (This->options & D3DXMESH_32BIT)
1194 if (indices) iface->lpVtbl->UnlockIndexBuffer(iface);
1196 else
1198 if (indices_16bit) iface->lpVtbl->UnlockIndexBuffer(iface);
1199 HeapFree(GetProcessHeap(), 0, indices);
1201 HeapFree(GetProcessHeap(), 0, new_indices);
1202 return hr;
1205 struct vertex_metadata {
1206 float key;
1207 DWORD vertex_index;
1208 DWORD first_shared_index;
1211 static int compare_vertex_keys(const void *a, const void *b)
1213 const struct vertex_metadata *left = a;
1214 const struct vertex_metadata *right = b;
1215 if (left->key == right->key)
1216 return 0;
1217 return left->key < right->key ? -1 : 1;
1220 static HRESULT WINAPI ID3DXMeshImpl_GenerateAdjacency(ID3DXMesh *iface, FLOAT epsilon, DWORD *adjacency)
1222 ID3DXMeshImpl *This = impl_from_ID3DXMesh(iface);
1223 HRESULT hr;
1224 BYTE *vertices = NULL;
1225 const DWORD *indices = NULL;
1226 DWORD vertex_size;
1227 DWORD buffer_size;
1228 /* sort the vertices by (x + y + z) to quickly find coincident vertices */
1229 struct vertex_metadata *sorted_vertices;
1230 /* shared_indices links together identical indices in the index buffer so
1231 * that adjacency checks can be limited to faces sharing a vertex */
1232 DWORD *shared_indices = NULL;
1233 const FLOAT epsilon_sq = epsilon * epsilon;
1234 DWORD i;
1236 TRACE("(%p)->(%f,%p)\n", This, epsilon, adjacency);
1238 if (!adjacency)
1239 return D3DERR_INVALIDCALL;
1241 buffer_size = This->numfaces * 3 * sizeof(*shared_indices) + This->numvertices * sizeof(*sorted_vertices);
1242 if (!(This->options & D3DXMESH_32BIT))
1243 buffer_size += This->numfaces * 3 * sizeof(*indices);
1244 shared_indices = HeapAlloc(GetProcessHeap(), 0, buffer_size);
1245 if (!shared_indices)
1246 return E_OUTOFMEMORY;
1247 sorted_vertices = (struct vertex_metadata*)(shared_indices + This->numfaces * 3);
1249 hr = iface->lpVtbl->LockVertexBuffer(iface, D3DLOCK_READONLY, (void**)&vertices);
1250 if (FAILED(hr)) goto cleanup;
1251 hr = iface->lpVtbl->LockIndexBuffer(iface, D3DLOCK_READONLY, (void**)&indices);
1252 if (FAILED(hr)) goto cleanup;
1254 if (!(This->options & D3DXMESH_32BIT)) {
1255 const WORD *word_indices = (const WORD*)indices;
1256 DWORD *dword_indices = (DWORD*)(sorted_vertices + This->numvertices);
1257 indices = dword_indices;
1258 for (i = 0; i < This->numfaces * 3; i++)
1259 *dword_indices++ = *word_indices++;
1262 vertex_size = iface->lpVtbl->GetNumBytesPerVertex(iface);
1263 for (i = 0; i < This->numvertices; i++) {
1264 D3DXVECTOR3 *vertex = (D3DXVECTOR3*)(vertices + vertex_size * i);
1265 sorted_vertices[i].first_shared_index = -1;
1266 sorted_vertices[i].key = vertex->x + vertex->y + vertex->z;
1267 sorted_vertices[i].vertex_index = i;
1269 for (i = 0; i < This->numfaces * 3; i++) {
1270 DWORD *first_shared_index = &sorted_vertices[indices[i]].first_shared_index;
1271 shared_indices[i] = *first_shared_index;
1272 *first_shared_index = i;
1273 adjacency[i] = -1;
1275 qsort(sorted_vertices, This->numvertices, sizeof(*sorted_vertices), compare_vertex_keys);
1277 for (i = 0; i < This->numvertices; i++) {
1278 struct vertex_metadata *sorted_vertex_a = &sorted_vertices[i];
1279 D3DXVECTOR3 *vertex_a = (D3DXVECTOR3*)(vertices + sorted_vertex_a->vertex_index * vertex_size);
1280 DWORD shared_index_a = sorted_vertex_a->first_shared_index;
1282 while (shared_index_a != -1) {
1283 DWORD j = i;
1284 DWORD shared_index_b = shared_indices[shared_index_a];
1285 struct vertex_metadata *sorted_vertex_b = sorted_vertex_a;
1287 while (TRUE) {
1288 while (shared_index_b != -1) {
1289 /* faces are adjacent if they have another coincident vertex */
1290 DWORD base_a = (shared_index_a / 3) * 3;
1291 DWORD base_b = (shared_index_b / 3) * 3;
1292 BOOL adjacent = FALSE;
1293 int k;
1295 for (k = 0; k < 3; k++) {
1296 if (adjacency[base_b + k] == shared_index_a / 3) {
1297 adjacent = TRUE;
1298 break;
1301 if (!adjacent) {
1302 for (k = 1; k <= 2; k++) {
1303 DWORD vertex_index_a = base_a + (shared_index_a + k) % 3;
1304 DWORD vertex_index_b = base_b + (shared_index_b + (3 - k)) % 3;
1305 adjacent = indices[vertex_index_a] == indices[vertex_index_b];
1306 if (!adjacent && epsilon >= 0.0f) {
1307 D3DXVECTOR3 delta = {0.0f, 0.0f, 0.0f};
1308 FLOAT length_sq;
1310 D3DXVec3Subtract(&delta,
1311 (D3DXVECTOR3*)(vertices + indices[vertex_index_a] * vertex_size),
1312 (D3DXVECTOR3*)(vertices + indices[vertex_index_b] * vertex_size));
1313 length_sq = D3DXVec3LengthSq(&delta);
1314 adjacent = epsilon == 0.0f ? length_sq == 0.0f : length_sq < epsilon_sq;
1316 if (adjacent) {
1317 DWORD adj_a = base_a + 2 - (vertex_index_a + shared_index_a + 1) % 3;
1318 DWORD adj_b = base_b + 2 - (vertex_index_b + shared_index_b + 1) % 3;
1319 if (adjacency[adj_a] == -1 && adjacency[adj_b] == -1) {
1320 adjacency[adj_a] = base_b / 3;
1321 adjacency[adj_b] = base_a / 3;
1322 break;
1328 shared_index_b = shared_indices[shared_index_b];
1330 while (++j < This->numvertices) {
1331 D3DXVECTOR3 *vertex_b;
1333 sorted_vertex_b++;
1334 if (sorted_vertex_b->key - sorted_vertex_a->key > epsilon * 3.0f) {
1335 /* no more coincident vertices to try */
1336 j = This->numvertices;
1337 break;
1339 /* check for coincidence */
1340 vertex_b = (D3DXVECTOR3*)(vertices + sorted_vertex_b->vertex_index * vertex_size);
1341 if (fabsf(vertex_a->x - vertex_b->x) <= epsilon &&
1342 fabsf(vertex_a->y - vertex_b->y) <= epsilon &&
1343 fabsf(vertex_a->z - vertex_b->z) <= epsilon)
1345 break;
1348 if (j >= This->numvertices)
1349 break;
1350 shared_index_b = sorted_vertex_b->first_shared_index;
1353 sorted_vertex_a->first_shared_index = shared_indices[sorted_vertex_a->first_shared_index];
1354 shared_index_a = sorted_vertex_a->first_shared_index;
1358 hr = D3D_OK;
1359 cleanup:
1360 if (indices) iface->lpVtbl->UnlockIndexBuffer(iface);
1361 if (vertices) iface->lpVtbl->UnlockVertexBuffer(iface);
1362 HeapFree(GetProcessHeap(), 0, shared_indices);
1363 return hr;
1366 static HRESULT WINAPI ID3DXMeshImpl_UpdateSemantics(ID3DXMesh *iface, D3DVERTEXELEMENT9 declaration[MAX_FVF_DECL_SIZE])
1368 HRESULT hr;
1369 ID3DXMeshImpl *This = impl_from_ID3DXMesh(iface);
1370 UINT vertex_declaration_size;
1371 int i;
1373 TRACE("(%p)->(%p)\n", This, declaration);
1375 if (!declaration)
1377 WARN("Invalid declaration. Can't use NULL declaration.\n");
1378 return D3DERR_INVALIDCALL;
1381 /* New declaration must be same size as original */
1382 vertex_declaration_size = D3DXGetDeclVertexSize(declaration, declaration[0].Stream);
1383 if (vertex_declaration_size != This->vertex_declaration_size)
1385 WARN("Invalid declaration. New vertex size does not match the original vertex size.\n");
1386 return D3DERR_INVALIDCALL;
1389 /* New declaration must not contain non-zero Stream value */
1390 for (i = 0; declaration[i].Stream != 0xff; i++)
1392 if (declaration[i].Stream != 0)
1394 WARN("Invalid declaration. New declaration contains non-zero Stream value.\n");
1395 return D3DERR_INVALIDCALL;
1399 This->num_elem = i + 1;
1400 copy_declaration(This->cached_declaration, declaration, This->num_elem);
1402 if (This->vertex_declaration)
1403 IDirect3DVertexDeclaration9_Release(This->vertex_declaration);
1405 /* An application can pass an invalid declaration to UpdateSemantics and
1406 * still expect D3D_OK (see tests). If the declaration is invalid, then
1407 * subsequent calls to DrawSubset will fail. This is handled by setting the
1408 * vertex declaration to NULL.
1409 * GetDeclaration, GetNumBytesPerVertex must, however, use the new
1410 * invalid declaration. This is handled by them using the cached vertex
1411 * declaration instead of the actual vertex declaration.
1413 hr = IDirect3DDevice9_CreateVertexDeclaration(This->device,
1414 declaration,
1415 &This->vertex_declaration);
1416 if (FAILED(hr))
1418 WARN("Using invalid declaration. Calls to DrawSubset will fail.\n");
1419 This->vertex_declaration = NULL;
1422 return D3D_OK;
1425 /*** ID3DXMesh ***/
1426 static HRESULT WINAPI ID3DXMeshImpl_LockAttributeBuffer(ID3DXMesh *iface, DWORD flags, DWORD **data)
1428 ID3DXMeshImpl *This = impl_from_ID3DXMesh(iface);
1430 TRACE("(%p)->(%u,%p)\n", This, flags, data);
1432 InterlockedIncrement(&This->attrib_buffer_lock_count);
1434 if (!(flags & D3DLOCK_READONLY)) {
1435 D3DXATTRIBUTERANGE *attrib_table = This->attrib_table;
1436 This->attrib_table_size = 0;
1437 This->attrib_table = NULL;
1438 HeapFree(GetProcessHeap(), 0, attrib_table);
1441 *data = This->attrib_buffer;
1443 return D3D_OK;
1446 static HRESULT WINAPI ID3DXMeshImpl_UnlockAttributeBuffer(ID3DXMesh *iface)
1448 ID3DXMeshImpl *This = impl_from_ID3DXMesh(iface);
1449 int lock_count;
1451 TRACE("(%p)\n", This);
1453 lock_count = InterlockedDecrement(&This->attrib_buffer_lock_count);
1455 if (lock_count < 0) {
1456 InterlockedIncrement(&This->attrib_buffer_lock_count);
1457 return D3DERR_INVALIDCALL;
1460 return D3D_OK;
1463 static HRESULT WINAPI ID3DXMeshImpl_Optimize(ID3DXMesh *iface, DWORD flags, const DWORD *adjacency_in,
1464 DWORD *adjacency_out, DWORD *face_remap, ID3DXBuffer **vertex_remap, ID3DXMesh **opt_mesh)
1466 ID3DXMeshImpl *This = impl_from_ID3DXMesh(iface);
1467 HRESULT hr;
1468 D3DVERTEXELEMENT9 declaration[MAX_FVF_DECL_SIZE] = { D3DDECL_END() };
1469 ID3DXMesh *optimized_mesh;
1471 TRACE("(%p)->(%x,%p,%p,%p,%p,%p)\n", This, flags, adjacency_in, adjacency_out, face_remap, vertex_remap, opt_mesh);
1473 if (!opt_mesh)
1474 return D3DERR_INVALIDCALL;
1476 hr = iface->lpVtbl->GetDeclaration(iface, declaration);
1477 if (FAILED(hr)) return hr;
1479 hr = iface->lpVtbl->CloneMesh(iface, This->options, declaration, This->device, &optimized_mesh);
1480 if (FAILED(hr)) return hr;
1482 hr = optimized_mesh->lpVtbl->OptimizeInplace(optimized_mesh, flags, adjacency_in, adjacency_out, face_remap, vertex_remap);
1483 if (SUCCEEDED(hr))
1484 *opt_mesh = optimized_mesh;
1485 else
1486 IUnknown_Release(optimized_mesh);
1487 return hr;
1490 /* Creates a vertex_remap that removes unused vertices.
1491 * Indices are updated according to the vertex_remap. */
1492 static HRESULT compact_mesh(ID3DXMeshImpl *This, DWORD *indices, DWORD *new_num_vertices, ID3DXBuffer **vertex_remap)
1494 HRESULT hr;
1495 DWORD *vertex_remap_ptr;
1496 DWORD num_used_vertices;
1497 DWORD i;
1499 hr = D3DXCreateBuffer(This->numvertices * sizeof(DWORD), vertex_remap);
1500 if (FAILED(hr)) return hr;
1501 vertex_remap_ptr = ID3DXBuffer_GetBufferPointer(*vertex_remap);
1503 for (i = 0; i < This->numfaces * 3; i++)
1504 vertex_remap_ptr[indices[i]] = 1;
1506 /* create old->new vertex mapping */
1507 num_used_vertices = 0;
1508 for (i = 0; i < This->numvertices; i++) {
1509 if (vertex_remap_ptr[i])
1510 vertex_remap_ptr[i] = num_used_vertices++;
1511 else
1512 vertex_remap_ptr[i] = -1;
1514 /* convert indices */
1515 for (i = 0; i < This->numfaces * 3; i++)
1516 indices[i] = vertex_remap_ptr[indices[i]];
1518 /* create new->old vertex mapping */
1519 num_used_vertices = 0;
1520 for (i = 0; i < This->numvertices; i++) {
1521 if (vertex_remap_ptr[i] != -1)
1522 vertex_remap_ptr[num_used_vertices++] = i;
1524 for (i = num_used_vertices; i < This->numvertices; i++)
1525 vertex_remap_ptr[i] = -1;
1527 *new_num_vertices = num_used_vertices;
1529 return D3D_OK;
1532 /* count the number of unique attribute values in a sorted attribute buffer */
1533 static DWORD count_attributes(const DWORD *attrib_buffer, DWORD numfaces)
1535 DWORD last_attribute = attrib_buffer[0];
1536 DWORD attrib_table_size = 1;
1537 DWORD i;
1538 for (i = 1; i < numfaces; i++) {
1539 if (attrib_buffer[i] != last_attribute) {
1540 last_attribute = attrib_buffer[i];
1541 attrib_table_size++;
1544 return attrib_table_size;
1547 static void fill_attribute_table(DWORD *attrib_buffer, DWORD numfaces, void *indices,
1548 BOOL is_32bit_indices, D3DXATTRIBUTERANGE *attrib_table)
1550 DWORD attrib_table_size = 0;
1551 DWORD last_attribute = attrib_buffer[0];
1552 DWORD min_vertex, max_vertex;
1553 DWORD i;
1555 attrib_table[0].AttribId = last_attribute;
1556 attrib_table[0].FaceStart = 0;
1557 min_vertex = (DWORD)-1;
1558 max_vertex = 0;
1559 for (i = 0; i < numfaces; i++) {
1560 DWORD j;
1562 if (attrib_buffer[i] != last_attribute) {
1563 last_attribute = attrib_buffer[i];
1564 attrib_table[attrib_table_size].FaceCount = i - attrib_table[attrib_table_size].FaceStart;
1565 attrib_table[attrib_table_size].VertexStart = min_vertex;
1566 attrib_table[attrib_table_size].VertexCount = max_vertex - min_vertex + 1;
1567 attrib_table_size++;
1568 attrib_table[attrib_table_size].AttribId = attrib_buffer[i];
1569 attrib_table[attrib_table_size].FaceStart = i;
1570 min_vertex = (DWORD)-1;
1571 max_vertex = 0;
1573 for (j = 0; j < 3; j++) {
1574 DWORD vertex_index = is_32bit_indices ? ((DWORD*)indices)[i * 3 + j] : ((WORD*)indices)[i * 3 + j];
1575 if (vertex_index < min_vertex)
1576 min_vertex = vertex_index;
1577 if (vertex_index > max_vertex)
1578 max_vertex = vertex_index;
1581 attrib_table[attrib_table_size].FaceCount = i - attrib_table[attrib_table_size].FaceStart;
1582 attrib_table[attrib_table_size].VertexStart = min_vertex;
1583 attrib_table[attrib_table_size].VertexCount = max_vertex - min_vertex + 1;
1584 attrib_table_size++;
1587 static int attrib_entry_compare(const DWORD **a, const DWORD **b)
1589 const DWORD *ptr_a = *a;
1590 const DWORD *ptr_b = *b;
1591 int delta = *ptr_a - *ptr_b;
1593 if (delta)
1594 return delta;
1596 delta = ptr_a - ptr_b; /* for stable sort */
1597 return delta;
1600 /* Create face_remap, a new attribute buffer for attribute sort optimization. */
1601 static HRESULT remap_faces_for_attrsort(ID3DXMeshImpl *This, const DWORD *indices,
1602 const DWORD *attrib_buffer, DWORD **sorted_attrib_buffer, DWORD **face_remap)
1604 const DWORD **sorted_attrib_ptr_buffer = NULL;
1605 DWORD i;
1607 *face_remap = HeapAlloc(GetProcessHeap(), 0, This->numfaces * sizeof(**face_remap));
1608 sorted_attrib_ptr_buffer = HeapAlloc(GetProcessHeap(), 0, This->numfaces * sizeof(*sorted_attrib_ptr_buffer));
1609 if (!*face_remap || !sorted_attrib_ptr_buffer) {
1610 HeapFree(GetProcessHeap(), 0, sorted_attrib_ptr_buffer);
1611 return E_OUTOFMEMORY;
1613 for (i = 0; i < This->numfaces; i++)
1614 sorted_attrib_ptr_buffer[i] = &attrib_buffer[i];
1615 qsort(sorted_attrib_ptr_buffer, This->numfaces, sizeof(*sorted_attrib_ptr_buffer),
1616 (int(*)(const void *, const void *))attrib_entry_compare);
1618 for (i = 0; i < This->numfaces; i++)
1620 DWORD old_face = sorted_attrib_ptr_buffer[i] - attrib_buffer;
1621 (*face_remap)[old_face] = i;
1624 /* overwrite sorted_attrib_ptr_buffer with the values themselves */
1625 *sorted_attrib_buffer = (DWORD*)sorted_attrib_ptr_buffer;
1626 for (i = 0; i < This->numfaces; i++)
1627 (*sorted_attrib_buffer)[(*face_remap)[i]] = attrib_buffer[i];
1629 return D3D_OK;
1632 static HRESULT WINAPI ID3DXMeshImpl_OptimizeInplace(ID3DXMesh *iface, DWORD flags, const DWORD *adjacency_in,
1633 DWORD *adjacency_out, DWORD *face_remap_out, ID3DXBuffer **vertex_remap_out)
1635 ID3DXMeshImpl *This = impl_from_ID3DXMesh(iface);
1636 void *indices = NULL;
1637 DWORD *attrib_buffer = NULL;
1638 HRESULT hr;
1639 ID3DXBuffer *vertex_remap = NULL;
1640 DWORD *face_remap = NULL; /* old -> new mapping */
1641 DWORD *dword_indices = NULL;
1642 DWORD new_num_vertices = 0;
1643 DWORD new_num_alloc_vertices = 0;
1644 IDirect3DVertexBuffer9 *vertex_buffer = NULL;
1645 DWORD *sorted_attrib_buffer = NULL;
1646 DWORD i;
1648 TRACE("(%p)->(%x,%p,%p,%p,%p)\n", This, flags, adjacency_in, adjacency_out, face_remap_out, vertex_remap_out);
1650 if (!flags)
1651 return D3DERR_INVALIDCALL;
1652 if (!adjacency_in && (flags & (D3DXMESHOPT_VERTEXCACHE | D3DXMESHOPT_STRIPREORDER)))
1653 return D3DERR_INVALIDCALL;
1654 if ((flags & (D3DXMESHOPT_VERTEXCACHE | D3DXMESHOPT_STRIPREORDER)) == (D3DXMESHOPT_VERTEXCACHE | D3DXMESHOPT_STRIPREORDER))
1655 return D3DERR_INVALIDCALL;
1657 if (flags & (D3DXMESHOPT_VERTEXCACHE | D3DXMESHOPT_STRIPREORDER))
1659 if (flags & D3DXMESHOPT_VERTEXCACHE)
1660 FIXME("D3DXMESHOPT_VERTEXCACHE not implemented.\n");
1661 if (flags & D3DXMESHOPT_STRIPREORDER)
1662 FIXME("D3DXMESHOPT_STRIPREORDER not implemented.\n");
1663 return E_NOTIMPL;
1666 hr = iface->lpVtbl->LockIndexBuffer(iface, 0, &indices);
1667 if (FAILED(hr)) goto cleanup;
1669 dword_indices = HeapAlloc(GetProcessHeap(), 0, This->numfaces * 3 * sizeof(DWORD));
1670 if (!dword_indices) return E_OUTOFMEMORY;
1671 if (This->options & D3DXMESH_32BIT) {
1672 memcpy(dword_indices, indices, This->numfaces * 3 * sizeof(DWORD));
1673 } else {
1674 WORD *word_indices = indices;
1675 for (i = 0; i < This->numfaces * 3; i++)
1676 dword_indices[i] = *word_indices++;
1679 if ((flags & (D3DXMESHOPT_COMPACT | D3DXMESHOPT_IGNOREVERTS | D3DXMESHOPT_ATTRSORT)) == D3DXMESHOPT_COMPACT)
1681 new_num_alloc_vertices = This->numvertices;
1682 hr = compact_mesh(This, dword_indices, &new_num_vertices, &vertex_remap);
1683 if (FAILED(hr)) goto cleanup;
1684 } else if (flags & D3DXMESHOPT_ATTRSORT) {
1685 if (!(flags & D3DXMESHOPT_IGNOREVERTS))
1687 FIXME("D3DXMESHOPT_ATTRSORT vertex reordering not implemented.\n");
1688 hr = E_NOTIMPL;
1689 goto cleanup;
1692 hr = iface->lpVtbl->LockAttributeBuffer(iface, 0, &attrib_buffer);
1693 if (FAILED(hr)) goto cleanup;
1695 hr = remap_faces_for_attrsort(This, dword_indices, attrib_buffer, &sorted_attrib_buffer, &face_remap);
1696 if (FAILED(hr)) goto cleanup;
1699 if (vertex_remap)
1701 /* reorder the vertices using vertex_remap */
1702 D3DVERTEXBUFFER_DESC vertex_desc;
1703 DWORD *vertex_remap_ptr = ID3DXBuffer_GetBufferPointer(vertex_remap);
1704 DWORD vertex_size = iface->lpVtbl->GetNumBytesPerVertex(iface);
1705 BYTE *orig_vertices;
1706 BYTE *new_vertices;
1708 hr = IDirect3DVertexBuffer9_GetDesc(This->vertex_buffer, &vertex_desc);
1709 if (FAILED(hr)) goto cleanup;
1711 hr = IDirect3DDevice9_CreateVertexBuffer(This->device, new_num_alloc_vertices * vertex_size,
1712 vertex_desc.Usage, This->fvf, vertex_desc.Pool, &vertex_buffer, NULL);
1713 if (FAILED(hr)) goto cleanup;
1715 hr = IDirect3DVertexBuffer9_Lock(This->vertex_buffer, 0, 0, (void**)&orig_vertices, D3DLOCK_READONLY);
1716 if (FAILED(hr)) goto cleanup;
1718 hr = IDirect3DVertexBuffer9_Lock(vertex_buffer, 0, 0, (void**)&new_vertices, 0);
1719 if (FAILED(hr)) {
1720 IDirect3DVertexBuffer9_Unlock(This->vertex_buffer);
1721 goto cleanup;
1724 for (i = 0; i < new_num_vertices; i++)
1725 memcpy(new_vertices + i * vertex_size, orig_vertices + vertex_remap_ptr[i] * vertex_size, vertex_size);
1727 IDirect3DVertexBuffer9_Unlock(This->vertex_buffer);
1728 IDirect3DVertexBuffer9_Unlock(vertex_buffer);
1729 } else if (vertex_remap_out) {
1730 DWORD *vertex_remap_ptr;
1732 hr = D3DXCreateBuffer(This->numvertices * sizeof(DWORD), &vertex_remap);
1733 if (FAILED(hr)) goto cleanup;
1734 vertex_remap_ptr = ID3DXBuffer_GetBufferPointer(vertex_remap);
1735 for (i = 0; i < This->numvertices; i++)
1736 *vertex_remap_ptr++ = i;
1739 if (flags & D3DXMESHOPT_ATTRSORT)
1741 D3DXATTRIBUTERANGE *attrib_table;
1742 DWORD attrib_table_size;
1744 attrib_table_size = count_attributes(sorted_attrib_buffer, This->numfaces);
1745 attrib_table = HeapAlloc(GetProcessHeap(), 0, attrib_table_size * sizeof(*attrib_table));
1746 if (!attrib_table) {
1747 hr = E_OUTOFMEMORY;
1748 goto cleanup;
1751 memcpy(attrib_buffer, sorted_attrib_buffer, This->numfaces * sizeof(*attrib_buffer));
1753 /* reorder the indices using face_remap */
1754 if (This->options & D3DXMESH_32BIT) {
1755 for (i = 0; i < This->numfaces; i++)
1756 memcpy((DWORD*)indices + face_remap[i] * 3, dword_indices + i * 3, 3 * sizeof(DWORD));
1757 } else {
1758 WORD *word_indices = indices;
1759 for (i = 0; i < This->numfaces; i++) {
1760 DWORD new_pos = face_remap[i] * 3;
1761 DWORD old_pos = i * 3;
1762 word_indices[new_pos++] = dword_indices[old_pos++];
1763 word_indices[new_pos++] = dword_indices[old_pos++];
1764 word_indices[new_pos] = dword_indices[old_pos];
1768 fill_attribute_table(attrib_buffer, This->numfaces, indices,
1769 This->options & D3DXMESH_32BIT, attrib_table);
1771 HeapFree(GetProcessHeap(), 0, This->attrib_table);
1772 This->attrib_table = attrib_table;
1773 This->attrib_table_size = attrib_table_size;
1774 } else {
1775 if (This->options & D3DXMESH_32BIT) {
1776 memcpy(indices, dword_indices, This->numfaces * 3 * sizeof(DWORD));
1777 } else {
1778 WORD *word_indices = indices;
1779 for (i = 0; i < This->numfaces * 3; i++)
1780 *word_indices++ = dword_indices[i];
1784 if (adjacency_out) {
1785 if (face_remap) {
1786 for (i = 0; i < This->numfaces; i++) {
1787 DWORD old_pos = i * 3;
1788 DWORD new_pos = face_remap[i] * 3;
1789 adjacency_out[new_pos++] = face_remap[adjacency_in[old_pos++]];
1790 adjacency_out[new_pos++] = face_remap[adjacency_in[old_pos++]];
1791 adjacency_out[new_pos++] = face_remap[adjacency_in[old_pos++]];
1793 } else {
1794 memcpy(adjacency_out, adjacency_in, This->numfaces * 3 * sizeof(*adjacency_out));
1797 if (face_remap_out) {
1798 if (face_remap) {
1799 for (i = 0; i < This->numfaces; i++)
1800 face_remap_out[face_remap[i]] = i;
1801 } else {
1802 for (i = 0; i < This->numfaces; i++)
1803 face_remap_out[i] = i;
1806 if (vertex_remap_out)
1807 *vertex_remap_out = vertex_remap;
1808 vertex_remap = NULL;
1810 if (vertex_buffer) {
1811 IDirect3DVertexBuffer9_Release(This->vertex_buffer);
1812 This->vertex_buffer = vertex_buffer;
1813 vertex_buffer = NULL;
1814 This->numvertices = new_num_vertices;
1817 hr = D3D_OK;
1818 cleanup:
1819 HeapFree(GetProcessHeap(), 0, sorted_attrib_buffer);
1820 HeapFree(GetProcessHeap(), 0, face_remap);
1821 HeapFree(GetProcessHeap(), 0, dword_indices);
1822 if (vertex_remap) ID3DXBuffer_Release(vertex_remap);
1823 if (vertex_buffer) IDirect3DVertexBuffer9_Release(vertex_buffer);
1824 if (attrib_buffer) iface->lpVtbl->UnlockAttributeBuffer(iface);
1825 if (indices) iface->lpVtbl->UnlockIndexBuffer(iface);
1826 return hr;
1829 static HRESULT WINAPI ID3DXMeshImpl_SetAttributeTable(ID3DXMesh *iface, CONST D3DXATTRIBUTERANGE *attrib_table, DWORD attrib_table_size)
1831 ID3DXMeshImpl *This = impl_from_ID3DXMesh(iface);
1832 D3DXATTRIBUTERANGE *new_table = NULL;
1834 TRACE("(%p)->(%p,%u)\n", This, attrib_table, attrib_table_size);
1836 if (attrib_table_size) {
1837 size_t size = attrib_table_size * sizeof(*attrib_table);
1839 new_table = HeapAlloc(GetProcessHeap(), 0, size);
1840 if (!new_table)
1841 return E_OUTOFMEMORY;
1843 CopyMemory(new_table, attrib_table, size);
1844 } else if (attrib_table) {
1845 return D3DERR_INVALIDCALL;
1847 HeapFree(GetProcessHeap(), 0, This->attrib_table);
1848 This->attrib_table = new_table;
1849 This->attrib_table_size = attrib_table_size;
1851 return D3D_OK;
1854 static const struct ID3DXMeshVtbl D3DXMesh_Vtbl =
1856 /*** IUnknown methods ***/
1857 ID3DXMeshImpl_QueryInterface,
1858 ID3DXMeshImpl_AddRef,
1859 ID3DXMeshImpl_Release,
1860 /*** ID3DXBaseMesh ***/
1861 ID3DXMeshImpl_DrawSubset,
1862 ID3DXMeshImpl_GetNumFaces,
1863 ID3DXMeshImpl_GetNumVertices,
1864 ID3DXMeshImpl_GetFVF,
1865 ID3DXMeshImpl_GetDeclaration,
1866 ID3DXMeshImpl_GetNumBytesPerVertex,
1867 ID3DXMeshImpl_GetOptions,
1868 ID3DXMeshImpl_GetDevice,
1869 ID3DXMeshImpl_CloneMeshFVF,
1870 ID3DXMeshImpl_CloneMesh,
1871 ID3DXMeshImpl_GetVertexBuffer,
1872 ID3DXMeshImpl_GetIndexBuffer,
1873 ID3DXMeshImpl_LockVertexBuffer,
1874 ID3DXMeshImpl_UnlockVertexBuffer,
1875 ID3DXMeshImpl_LockIndexBuffer,
1876 ID3DXMeshImpl_UnlockIndexBuffer,
1877 ID3DXMeshImpl_GetAttributeTable,
1878 ID3DXMeshImpl_ConvertPointRepsToAdjacency,
1879 ID3DXMeshImpl_ConvertAdjacencyToPointReps,
1880 ID3DXMeshImpl_GenerateAdjacency,
1881 ID3DXMeshImpl_UpdateSemantics,
1882 /*** ID3DXMesh ***/
1883 ID3DXMeshImpl_LockAttributeBuffer,
1884 ID3DXMeshImpl_UnlockAttributeBuffer,
1885 ID3DXMeshImpl_Optimize,
1886 ID3DXMeshImpl_OptimizeInplace,
1887 ID3DXMeshImpl_SetAttributeTable
1890 /*************************************************************************
1891 * D3DXBoxBoundProbe
1893 BOOL WINAPI D3DXBoxBoundProbe(CONST D3DXVECTOR3 *pmin, CONST D3DXVECTOR3 *pmax, CONST D3DXVECTOR3 *prayposition, CONST D3DXVECTOR3 *praydirection)
1895 /* Algorithm taken from the article: An Efficient and Robust Ray-Box Intersection Algorithm
1896 Amy Williams University of Utah
1897 Steve Barrus University of Utah
1898 R. Keith Morley University of Utah
1899 Peter Shirley University of Utah
1901 International Conference on Computer Graphics and Interactive Techniques archive
1902 ACM SIGGRAPH 2005 Courses
1903 Los Angeles, California
1905 This algorithm is free of patents or of copyrights, as confirmed by Peter Shirley himself.
1907 Algorithm: Consider the box as the intersection of three slabs. Clip the ray
1908 against each slab, if there's anything left of the ray after we're
1909 done we've got an intersection of the ray with the box.
1913 FLOAT div, tmin, tmax, tymin, tymax, tzmin, tzmax;
1915 div = 1.0f / praydirection->x;
1916 if ( div >= 0.0f )
1918 tmin = ( pmin->x - prayposition->x ) * div;
1919 tmax = ( pmax->x - prayposition->x ) * div;
1921 else
1923 tmin = ( pmax->x - prayposition->x ) * div;
1924 tmax = ( pmin->x - prayposition->x ) * div;
1927 if ( tmax < 0.0f ) return FALSE;
1929 div = 1.0f / praydirection->y;
1930 if ( div >= 0.0f )
1932 tymin = ( pmin->y - prayposition->y ) * div;
1933 tymax = ( pmax->y - prayposition->y ) * div;
1935 else
1937 tymin = ( pmax->y - prayposition->y ) * div;
1938 tymax = ( pmin->y - prayposition->y ) * div;
1941 if ( ( tymax < 0.0f ) || ( tmin > tymax ) || ( tymin > tmax ) ) return FALSE;
1943 if ( tymin > tmin ) tmin = tymin;
1944 if ( tymax < tmax ) tmax = tymax;
1946 div = 1.0f / praydirection->z;
1947 if ( div >= 0.0f )
1949 tzmin = ( pmin->z - prayposition->z ) * div;
1950 tzmax = ( pmax->z - prayposition->z ) * div;
1952 else
1954 tzmin = ( pmax->z - prayposition->z ) * div;
1955 tzmax = ( pmin->z - prayposition->z ) * div;
1958 if ( (tzmax < 0.0f ) || ( tmin > tzmax ) || ( tzmin > tmax ) ) return FALSE;
1960 return TRUE;
1963 /*************************************************************************
1964 * D3DXComputeBoundingBox
1966 HRESULT WINAPI D3DXComputeBoundingBox(CONST D3DXVECTOR3 *pfirstposition, DWORD numvertices, DWORD dwstride, D3DXVECTOR3 *pmin, D3DXVECTOR3 *pmax)
1968 D3DXVECTOR3 vec;
1969 unsigned int i;
1971 if( !pfirstposition || !pmin || !pmax ) return D3DERR_INVALIDCALL;
1973 *pmin = *pfirstposition;
1974 *pmax = *pmin;
1976 for(i=0; i<numvertices; i++)
1978 vec = *( (const D3DXVECTOR3*)((const char*)pfirstposition + dwstride * i) );
1980 if ( vec.x < pmin->x ) pmin->x = vec.x;
1981 if ( vec.x > pmax->x ) pmax->x = vec.x;
1983 if ( vec.y < pmin->y ) pmin->y = vec.y;
1984 if ( vec.y > pmax->y ) pmax->y = vec.y;
1986 if ( vec.z < pmin->z ) pmin->z = vec.z;
1987 if ( vec.z > pmax->z ) pmax->z = vec.z;
1990 return D3D_OK;
1993 /*************************************************************************
1994 * D3DXComputeBoundingSphere
1996 HRESULT WINAPI D3DXComputeBoundingSphere(CONST D3DXVECTOR3* pfirstposition, DWORD numvertices, DWORD dwstride, D3DXVECTOR3 *pcenter, FLOAT *pradius)
1998 D3DXVECTOR3 temp;
1999 FLOAT d;
2000 unsigned int i;
2002 if( !pfirstposition || !pcenter || !pradius ) return D3DERR_INVALIDCALL;
2004 temp.x = 0.0f;
2005 temp.y = 0.0f;
2006 temp.z = 0.0f;
2007 *pradius = 0.0f;
2009 for(i=0; i<numvertices; i++)
2010 D3DXVec3Add(&temp, &temp, (const D3DXVECTOR3*)((const char*)pfirstposition + dwstride * i));
2012 D3DXVec3Scale(pcenter, &temp, 1.0f / numvertices);
2014 for(i=0; i<numvertices; i++)
2016 d = D3DXVec3Length(D3DXVec3Subtract(&temp, (const D3DXVECTOR3*)((const char*)pfirstposition + dwstride * i), pcenter));
2017 if ( d > *pradius ) *pradius = d;
2019 return D3D_OK;
2022 static void append_decl_element(D3DVERTEXELEMENT9 *declaration, UINT *idx, UINT *offset,
2023 D3DDECLTYPE type, D3DDECLUSAGE usage, UINT usage_idx)
2025 declaration[*idx].Stream = 0;
2026 declaration[*idx].Offset = *offset;
2027 declaration[*idx].Type = type;
2028 declaration[*idx].Method = D3DDECLMETHOD_DEFAULT;
2029 declaration[*idx].Usage = usage;
2030 declaration[*idx].UsageIndex = usage_idx;
2032 *offset += d3dx_decltype_size[type];
2033 ++(*idx);
2036 /*************************************************************************
2037 * D3DXDeclaratorFromFVF
2039 HRESULT WINAPI D3DXDeclaratorFromFVF(DWORD fvf, D3DVERTEXELEMENT9 declaration[MAX_FVF_DECL_SIZE])
2041 static const D3DVERTEXELEMENT9 end_element = D3DDECL_END();
2042 DWORD tex_count = (fvf & D3DFVF_TEXCOUNT_MASK) >> D3DFVF_TEXCOUNT_SHIFT;
2043 unsigned int offset = 0;
2044 unsigned int idx = 0;
2045 unsigned int i;
2047 TRACE("fvf %#x, declaration %p.\n", fvf, declaration);
2049 if (fvf & (D3DFVF_RESERVED0 | D3DFVF_RESERVED2)) return D3DERR_INVALIDCALL;
2051 if (fvf & D3DFVF_POSITION_MASK)
2053 BOOL has_blend = (fvf & D3DFVF_XYZB5) >= D3DFVF_XYZB1;
2054 DWORD blend_count = 1 + (((fvf & D3DFVF_XYZB5) - D3DFVF_XYZB1) >> 1);
2055 BOOL has_blend_idx = (fvf & D3DFVF_LASTBETA_D3DCOLOR) || (fvf & D3DFVF_LASTBETA_UBYTE4);
2057 if (has_blend_idx) --blend_count;
2059 if ((fvf & D3DFVF_POSITION_MASK) == D3DFVF_XYZW
2060 || (has_blend && blend_count > 4))
2061 return D3DERR_INVALIDCALL;
2063 if ((fvf & D3DFVF_POSITION_MASK) == D3DFVF_XYZRHW)
2064 append_decl_element(declaration, &idx, &offset, D3DDECLTYPE_FLOAT4, D3DDECLUSAGE_POSITIONT, 0);
2065 else
2066 append_decl_element(declaration, &idx, &offset, D3DDECLTYPE_FLOAT3, D3DDECLUSAGE_POSITION, 0);
2068 if (has_blend)
2070 switch (blend_count)
2072 case 0:
2073 break;
2074 case 1:
2075 append_decl_element(declaration, &idx, &offset, D3DDECLTYPE_FLOAT1, D3DDECLUSAGE_BLENDWEIGHT, 0);
2076 break;
2077 case 2:
2078 append_decl_element(declaration, &idx, &offset, D3DDECLTYPE_FLOAT2, D3DDECLUSAGE_BLENDWEIGHT, 0);
2079 break;
2080 case 3:
2081 append_decl_element(declaration, &idx, &offset, D3DDECLTYPE_FLOAT3, D3DDECLUSAGE_BLENDWEIGHT, 0);
2082 break;
2083 case 4:
2084 append_decl_element(declaration, &idx, &offset, D3DDECLTYPE_FLOAT4, D3DDECLUSAGE_BLENDWEIGHT, 0);
2085 break;
2086 default:
2087 ERR("Invalid blend count %u.\n", blend_count);
2088 break;
2091 if (has_blend_idx)
2093 if (fvf & D3DFVF_LASTBETA_UBYTE4)
2094 append_decl_element(declaration, &idx, &offset, D3DDECLTYPE_UBYTE4, D3DDECLUSAGE_BLENDINDICES, 0);
2095 else if (fvf & D3DFVF_LASTBETA_D3DCOLOR)
2096 append_decl_element(declaration, &idx, &offset, D3DDECLTYPE_D3DCOLOR, D3DDECLUSAGE_BLENDINDICES, 0);
2101 if (fvf & D3DFVF_NORMAL)
2102 append_decl_element(declaration, &idx, &offset, D3DDECLTYPE_FLOAT3, D3DDECLUSAGE_NORMAL, 0);
2103 if (fvf & D3DFVF_PSIZE)
2104 append_decl_element(declaration, &idx, &offset, D3DDECLTYPE_FLOAT1, D3DDECLUSAGE_PSIZE, 0);
2105 if (fvf & D3DFVF_DIFFUSE)
2106 append_decl_element(declaration, &idx, &offset, D3DDECLTYPE_D3DCOLOR, D3DDECLUSAGE_COLOR, 0);
2107 if (fvf & D3DFVF_SPECULAR)
2108 append_decl_element(declaration, &idx, &offset, D3DDECLTYPE_D3DCOLOR, D3DDECLUSAGE_COLOR, 1);
2110 for (i = 0; i < tex_count; ++i)
2112 switch ((fvf >> (16 + 2 * i)) & 0x03)
2114 case D3DFVF_TEXTUREFORMAT1:
2115 append_decl_element(declaration, &idx, &offset, D3DDECLTYPE_FLOAT1, D3DDECLUSAGE_TEXCOORD, i);
2116 break;
2117 case D3DFVF_TEXTUREFORMAT2:
2118 append_decl_element(declaration, &idx, &offset, D3DDECLTYPE_FLOAT2, D3DDECLUSAGE_TEXCOORD, i);
2119 break;
2120 case D3DFVF_TEXTUREFORMAT3:
2121 append_decl_element(declaration, &idx, &offset, D3DDECLTYPE_FLOAT3, D3DDECLUSAGE_TEXCOORD, i);
2122 break;
2123 case D3DFVF_TEXTUREFORMAT4:
2124 append_decl_element(declaration, &idx, &offset, D3DDECLTYPE_FLOAT4, D3DDECLUSAGE_TEXCOORD, i);
2125 break;
2129 declaration[idx] = end_element;
2131 return D3D_OK;
2134 /*************************************************************************
2135 * D3DXFVFFromDeclarator
2137 HRESULT WINAPI D3DXFVFFromDeclarator(const D3DVERTEXELEMENT9 *declaration, DWORD *fvf)
2139 unsigned int i = 0, texture, offset;
2141 TRACE("(%p, %p)\n", declaration, fvf);
2143 *fvf = 0;
2144 if (declaration[0].Type == D3DDECLTYPE_FLOAT3 && declaration[0].Usage == D3DDECLUSAGE_POSITION)
2146 if ((declaration[1].Type == D3DDECLTYPE_FLOAT4 && declaration[1].Usage == D3DDECLUSAGE_BLENDWEIGHT &&
2147 declaration[1].UsageIndex == 0) &&
2148 (declaration[2].Type == D3DDECLTYPE_FLOAT1 && declaration[2].Usage == D3DDECLUSAGE_BLENDINDICES &&
2149 declaration[2].UsageIndex == 0))
2151 return D3DERR_INVALIDCALL;
2153 else if ((declaration[1].Type == D3DDECLTYPE_UBYTE4 || declaration[1].Type == D3DDECLTYPE_D3DCOLOR) &&
2154 declaration[1].Usage == D3DDECLUSAGE_BLENDINDICES && declaration[1].UsageIndex == 0)
2156 if (declaration[1].Type == D3DDECLTYPE_UBYTE4)
2158 *fvf |= D3DFVF_XYZB1 | D3DFVF_LASTBETA_UBYTE4;
2160 else
2162 *fvf |= D3DFVF_XYZB1 | D3DFVF_LASTBETA_D3DCOLOR;
2164 i = 2;
2166 else if (declaration[1].Type <= D3DDECLTYPE_FLOAT4 && declaration[1].Usage == D3DDECLUSAGE_BLENDWEIGHT &&
2167 declaration[1].UsageIndex == 0)
2169 if ((declaration[2].Type == D3DDECLTYPE_UBYTE4 || declaration[2].Type == D3DDECLTYPE_D3DCOLOR) &&
2170 declaration[2].Usage == D3DDECLUSAGE_BLENDINDICES && declaration[2].UsageIndex == 0)
2172 if (declaration[2].Type == D3DDECLTYPE_UBYTE4)
2174 *fvf |= D3DFVF_LASTBETA_UBYTE4;
2176 else
2178 *fvf |= D3DFVF_LASTBETA_D3DCOLOR;
2180 switch (declaration[1].Type)
2182 case D3DDECLTYPE_FLOAT1: *fvf |= D3DFVF_XYZB2; break;
2183 case D3DDECLTYPE_FLOAT2: *fvf |= D3DFVF_XYZB3; break;
2184 case D3DDECLTYPE_FLOAT3: *fvf |= D3DFVF_XYZB4; break;
2185 case D3DDECLTYPE_FLOAT4: *fvf |= D3DFVF_XYZB5; break;
2187 i = 3;
2189 else
2191 switch (declaration[1].Type)
2193 case D3DDECLTYPE_FLOAT1: *fvf |= D3DFVF_XYZB1; break;
2194 case D3DDECLTYPE_FLOAT2: *fvf |= D3DFVF_XYZB2; break;
2195 case D3DDECLTYPE_FLOAT3: *fvf |= D3DFVF_XYZB3; break;
2196 case D3DDECLTYPE_FLOAT4: *fvf |= D3DFVF_XYZB4; break;
2198 i = 2;
2201 else
2203 *fvf |= D3DFVF_XYZ;
2204 i = 1;
2207 else if (declaration[0].Type == D3DDECLTYPE_FLOAT4 && declaration[0].Usage == D3DDECLUSAGE_POSITIONT &&
2208 declaration[0].UsageIndex == 0)
2210 *fvf |= D3DFVF_XYZRHW;
2211 i = 1;
2214 if (declaration[i].Type == D3DDECLTYPE_FLOAT3 && declaration[i].Usage == D3DDECLUSAGE_NORMAL)
2216 *fvf |= D3DFVF_NORMAL;
2217 i++;
2219 if (declaration[i].Type == D3DDECLTYPE_FLOAT1 && declaration[i].Usage == D3DDECLUSAGE_PSIZE &&
2220 declaration[i].UsageIndex == 0)
2222 *fvf |= D3DFVF_PSIZE;
2223 i++;
2225 if (declaration[i].Type == D3DDECLTYPE_D3DCOLOR && declaration[i].Usage == D3DDECLUSAGE_COLOR &&
2226 declaration[i].UsageIndex == 0)
2228 *fvf |= D3DFVF_DIFFUSE;
2229 i++;
2231 if (declaration[i].Type == D3DDECLTYPE_D3DCOLOR && declaration[i].Usage == D3DDECLUSAGE_COLOR &&
2232 declaration[i].UsageIndex == 1)
2234 *fvf |= D3DFVF_SPECULAR;
2235 i++;
2238 for (texture = 0; texture < D3DDP_MAXTEXCOORD; i++, texture++)
2240 if (declaration[i].Stream == 0xFF)
2242 break;
2244 else if (declaration[i].Type == D3DDECLTYPE_FLOAT1 && declaration[i].Usage == D3DDECLUSAGE_TEXCOORD &&
2245 declaration[i].UsageIndex == texture)
2247 *fvf |= D3DFVF_TEXCOORDSIZE1(declaration[i].UsageIndex);
2249 else if (declaration[i].Type == D3DDECLTYPE_FLOAT2 && declaration[i].Usage == D3DDECLUSAGE_TEXCOORD &&
2250 declaration[i].UsageIndex == texture)
2252 *fvf |= D3DFVF_TEXCOORDSIZE2(declaration[i].UsageIndex);
2254 else if (declaration[i].Type == D3DDECLTYPE_FLOAT3 && declaration[i].Usage == D3DDECLUSAGE_TEXCOORD &&
2255 declaration[i].UsageIndex == texture)
2257 *fvf |= D3DFVF_TEXCOORDSIZE3(declaration[i].UsageIndex);
2259 else if (declaration[i].Type == D3DDECLTYPE_FLOAT4 && declaration[i].Usage == D3DDECLUSAGE_TEXCOORD &&
2260 declaration[i].UsageIndex == texture)
2262 *fvf |= D3DFVF_TEXCOORDSIZE4(declaration[i].UsageIndex);
2264 else
2266 return D3DERR_INVALIDCALL;
2270 *fvf |= (texture << D3DFVF_TEXCOUNT_SHIFT);
2272 for (offset = 0, i = 0; declaration[i].Stream != 0xFF;
2273 offset += d3dx_decltype_size[declaration[i].Type], i++)
2275 if (declaration[i].Offset != offset)
2277 return D3DERR_INVALIDCALL;
2281 return D3D_OK;
2284 /*************************************************************************
2285 * D3DXGetFVFVertexSize
2287 static UINT Get_TexCoord_Size_From_FVF(DWORD FVF, int tex_num)
2289 return (((((FVF) >> (16 + (2 * (tex_num)))) + 1) & 0x03) + 1);
2292 UINT WINAPI D3DXGetFVFVertexSize(DWORD FVF)
2294 DWORD size = 0;
2295 UINT i;
2296 UINT numTextures = (FVF & D3DFVF_TEXCOUNT_MASK) >> D3DFVF_TEXCOUNT_SHIFT;
2298 if (FVF & D3DFVF_NORMAL) size += sizeof(D3DXVECTOR3);
2299 if (FVF & D3DFVF_DIFFUSE) size += sizeof(DWORD);
2300 if (FVF & D3DFVF_SPECULAR) size += sizeof(DWORD);
2301 if (FVF & D3DFVF_PSIZE) size += sizeof(DWORD);
2303 switch (FVF & D3DFVF_POSITION_MASK)
2305 case D3DFVF_XYZ: size += sizeof(D3DXVECTOR3); break;
2306 case D3DFVF_XYZRHW: size += 4 * sizeof(FLOAT); break;
2307 case D3DFVF_XYZB1: size += 4 * sizeof(FLOAT); break;
2308 case D3DFVF_XYZB2: size += 5 * sizeof(FLOAT); break;
2309 case D3DFVF_XYZB3: size += 6 * sizeof(FLOAT); break;
2310 case D3DFVF_XYZB4: size += 7 * sizeof(FLOAT); break;
2311 case D3DFVF_XYZB5: size += 8 * sizeof(FLOAT); break;
2312 case D3DFVF_XYZW: size += 4 * sizeof(FLOAT); break;
2315 for (i = 0; i < numTextures; i++)
2317 size += Get_TexCoord_Size_From_FVF(FVF, i) * sizeof(FLOAT);
2320 return size;
2323 /*************************************************************************
2324 * D3DXGetDeclVertexSize
2326 UINT WINAPI D3DXGetDeclVertexSize(const D3DVERTEXELEMENT9 *decl, DWORD stream_idx)
2328 const D3DVERTEXELEMENT9 *element;
2329 UINT size = 0;
2331 TRACE("decl %p, stream_idx %u\n", decl, stream_idx);
2333 if (!decl) return 0;
2335 for (element = decl; element->Stream != 0xff; ++element)
2337 UINT type_size;
2339 if (element->Stream != stream_idx) continue;
2341 if (element->Type >= sizeof(d3dx_decltype_size) / sizeof(*d3dx_decltype_size))
2343 FIXME("Unhandled element type %#x, size will be incorrect.\n", element->Type);
2344 continue;
2347 type_size = d3dx_decltype_size[element->Type];
2348 if (element->Offset + type_size > size) size = element->Offset + type_size;
2351 return size;
2354 /*************************************************************************
2355 * D3DXGetDeclLength
2357 UINT WINAPI D3DXGetDeclLength(const D3DVERTEXELEMENT9 *decl)
2359 const D3DVERTEXELEMENT9 *element;
2361 TRACE("decl %p\n", decl);
2363 /* null decl results in exception on Windows XP */
2365 for (element = decl; element->Stream != 0xff; ++element);
2367 return element - decl;
2370 /*************************************************************************
2371 * D3DXIntersectTri
2373 BOOL WINAPI D3DXIntersectTri(CONST D3DXVECTOR3 *p0, CONST D3DXVECTOR3 *p1, CONST D3DXVECTOR3 *p2, CONST D3DXVECTOR3 *praypos, CONST D3DXVECTOR3 *praydir, FLOAT *pu, FLOAT *pv, FLOAT *pdist)
2375 D3DXMATRIX m;
2376 D3DXVECTOR4 vec;
2378 m.u.m[0][0] = p1->x - p0->x;
2379 m.u.m[1][0] = p2->x - p0->x;
2380 m.u.m[2][0] = -praydir->x;
2381 m.u.m[3][0] = 0.0f;
2382 m.u.m[0][1] = p1->y - p0->z;
2383 m.u.m[1][1] = p2->y - p0->z;
2384 m.u.m[2][1] = -praydir->y;
2385 m.u.m[3][1] = 0.0f;
2386 m.u.m[0][2] = p1->z - p0->z;
2387 m.u.m[1][2] = p2->z - p0->z;
2388 m.u.m[2][2] = -praydir->z;
2389 m.u.m[3][2] = 0.0f;
2390 m.u.m[0][3] = 0.0f;
2391 m.u.m[1][3] = 0.0f;
2392 m.u.m[2][3] = 0.0f;
2393 m.u.m[3][3] = 1.0f;
2395 vec.x = praypos->x - p0->x;
2396 vec.y = praypos->y - p0->y;
2397 vec.z = praypos->z - p0->z;
2398 vec.w = 0.0f;
2400 if ( D3DXMatrixInverse(&m, NULL, &m) )
2402 D3DXVec4Transform(&vec, &vec, &m);
2403 if ( (vec.x >= 0.0f) && (vec.y >= 0.0f) && (vec.x + vec.y <= 1.0f) && (vec.z >= 0.0f) )
2405 *pu = vec.x;
2406 *pv = vec.y;
2407 *pdist = fabs( vec.z );
2408 return TRUE;
2412 return FALSE;
2415 /*************************************************************************
2416 * D3DXSphereBoundProbe
2418 BOOL WINAPI D3DXSphereBoundProbe(CONST D3DXVECTOR3 *pcenter, FLOAT radius, CONST D3DXVECTOR3 *prayposition, CONST D3DXVECTOR3 *praydirection)
2420 D3DXVECTOR3 difference;
2421 FLOAT a, b, c, d;
2423 a = D3DXVec3LengthSq(praydirection);
2424 if (!D3DXVec3Subtract(&difference, prayposition, pcenter)) return FALSE;
2425 b = D3DXVec3Dot(&difference, praydirection);
2426 c = D3DXVec3LengthSq(&difference) - radius * radius;
2427 d = b * b - a * c;
2429 if ( ( d <= 0.0f ) || ( sqrt(d) <= b ) ) return FALSE;
2430 return TRUE;
2433 /*************************************************************************
2434 * D3DXCreateMesh
2436 HRESULT WINAPI D3DXCreateMesh(DWORD numfaces, DWORD numvertices, DWORD options,
2437 const D3DVERTEXELEMENT9 *declaration, struct IDirect3DDevice9 *device, struct ID3DXMesh **mesh)
2439 HRESULT hr;
2440 DWORD fvf;
2441 IDirect3DVertexDeclaration9 *vertex_declaration;
2442 UINT vertex_declaration_size;
2443 UINT num_elem;
2444 IDirect3DVertexBuffer9 *vertex_buffer;
2445 IDirect3DIndexBuffer9 *index_buffer;
2446 DWORD *attrib_buffer;
2447 ID3DXMeshImpl *object;
2448 DWORD index_usage = 0;
2449 D3DPOOL index_pool = D3DPOOL_DEFAULT;
2450 D3DFORMAT index_format = D3DFMT_INDEX16;
2451 DWORD vertex_usage = 0;
2452 D3DPOOL vertex_pool = D3DPOOL_DEFAULT;
2453 int i;
2455 TRACE("(%d, %d, %x, %p, %p, %p)\n", numfaces, numvertices, options, declaration, device, mesh);
2457 if (numfaces == 0 || numvertices == 0 || declaration == NULL || device == NULL || mesh == NULL ||
2458 /* D3DXMESH_VB_SHARE is for cloning, and D3DXMESH_USEHWONLY is for ConvertToBlendedMesh */
2459 (options & (D3DXMESH_VB_SHARE | D3DXMESH_USEHWONLY | 0xfffe0000)))
2461 return D3DERR_INVALIDCALL;
2463 for (i = 0; declaration[i].Stream != 0xff; i++)
2464 if (declaration[i].Stream != 0)
2465 return D3DERR_INVALIDCALL;
2466 num_elem = i + 1;
2468 if (options & D3DXMESH_32BIT)
2469 index_format = D3DFMT_INDEX32;
2471 if (options & D3DXMESH_DONOTCLIP) {
2472 index_usage |= D3DUSAGE_DONOTCLIP;
2473 vertex_usage |= D3DUSAGE_DONOTCLIP;
2475 if (options & D3DXMESH_POINTS) {
2476 index_usage |= D3DUSAGE_POINTS;
2477 vertex_usage |= D3DUSAGE_POINTS;
2479 if (options & D3DXMESH_RTPATCHES) {
2480 index_usage |= D3DUSAGE_RTPATCHES;
2481 vertex_usage |= D3DUSAGE_RTPATCHES;
2483 if (options & D3DXMESH_NPATCHES) {
2484 index_usage |= D3DUSAGE_NPATCHES;
2485 vertex_usage |= D3DUSAGE_NPATCHES;
2488 if (options & D3DXMESH_VB_SYSTEMMEM)
2489 vertex_pool = D3DPOOL_SYSTEMMEM;
2490 else if (options & D3DXMESH_VB_MANAGED)
2491 vertex_pool = D3DPOOL_MANAGED;
2493 if (options & D3DXMESH_VB_WRITEONLY)
2494 vertex_usage |= D3DUSAGE_WRITEONLY;
2495 if (options & D3DXMESH_VB_DYNAMIC)
2496 vertex_usage |= D3DUSAGE_DYNAMIC;
2497 if (options & D3DXMESH_VB_SOFTWAREPROCESSING)
2498 vertex_usage |= D3DUSAGE_SOFTWAREPROCESSING;
2500 if (options & D3DXMESH_IB_SYSTEMMEM)
2501 index_pool = D3DPOOL_SYSTEMMEM;
2502 else if (options & D3DXMESH_IB_MANAGED)
2503 index_pool = D3DPOOL_MANAGED;
2505 if (options & D3DXMESH_IB_WRITEONLY)
2506 index_usage |= D3DUSAGE_WRITEONLY;
2507 if (options & D3DXMESH_IB_DYNAMIC)
2508 index_usage |= D3DUSAGE_DYNAMIC;
2509 if (options & D3DXMESH_IB_SOFTWAREPROCESSING)
2510 index_usage |= D3DUSAGE_SOFTWAREPROCESSING;
2512 hr = D3DXFVFFromDeclarator(declaration, &fvf);
2513 if (hr != D3D_OK)
2515 fvf = 0;
2518 /* Create vertex declaration */
2519 hr = IDirect3DDevice9_CreateVertexDeclaration(device,
2520 declaration,
2521 &vertex_declaration);
2522 if (FAILED(hr))
2524 WARN("Unexpected return value %x from IDirect3DDevice9_CreateVertexDeclaration.\n",hr);
2525 return hr;
2527 vertex_declaration_size = D3DXGetDeclVertexSize(declaration, declaration[0].Stream);
2529 /* Create vertex buffer */
2530 hr = IDirect3DDevice9_CreateVertexBuffer(device,
2531 numvertices * vertex_declaration_size,
2532 vertex_usage,
2533 fvf,
2534 vertex_pool,
2535 &vertex_buffer,
2536 NULL);
2537 if (FAILED(hr))
2539 WARN("Unexpected return value %x from IDirect3DDevice9_CreateVertexBuffer.\n",hr);
2540 IDirect3DVertexDeclaration9_Release(vertex_declaration);
2541 return hr;
2544 /* Create index buffer */
2545 hr = IDirect3DDevice9_CreateIndexBuffer(device,
2546 numfaces * 3 * ((index_format == D3DFMT_INDEX16) ? 2 : 4),
2547 index_usage,
2548 index_format,
2549 index_pool,
2550 &index_buffer,
2551 NULL);
2552 if (FAILED(hr))
2554 WARN("Unexpected return value %x from IDirect3DDevice9_CreateVertexBuffer.\n",hr);
2555 IDirect3DVertexBuffer9_Release(vertex_buffer);
2556 IDirect3DVertexDeclaration9_Release(vertex_declaration);
2557 return hr;
2560 attrib_buffer = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, numfaces * sizeof(*attrib_buffer));
2561 object = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(ID3DXMeshImpl));
2562 if (object == NULL || attrib_buffer == NULL)
2564 HeapFree(GetProcessHeap(), 0, object);
2565 HeapFree(GetProcessHeap(), 0, attrib_buffer);
2566 IDirect3DIndexBuffer9_Release(index_buffer);
2567 IDirect3DVertexBuffer9_Release(vertex_buffer);
2568 IDirect3DVertexDeclaration9_Release(vertex_declaration);
2569 *mesh = NULL;
2570 return E_OUTOFMEMORY;
2572 object->ID3DXMesh_iface.lpVtbl = &D3DXMesh_Vtbl;
2573 object->ref = 1;
2575 object->numfaces = numfaces;
2576 object->numvertices = numvertices;
2577 object->options = options;
2578 object->fvf = fvf;
2579 object->device = device;
2580 IDirect3DDevice9_AddRef(device);
2582 copy_declaration(object->cached_declaration, declaration, num_elem);
2583 object->vertex_declaration = vertex_declaration;
2584 object->vertex_declaration_size = vertex_declaration_size;
2585 object->num_elem = num_elem;
2586 object->vertex_buffer = vertex_buffer;
2587 object->index_buffer = index_buffer;
2588 object->attrib_buffer = attrib_buffer;
2590 *mesh = &object->ID3DXMesh_iface;
2592 return D3D_OK;
2595 /*************************************************************************
2596 * D3DXCreateMeshFVF
2598 HRESULT WINAPI D3DXCreateMeshFVF(DWORD numfaces, DWORD numvertices, DWORD options,
2599 DWORD fvf, struct IDirect3DDevice9 *device, struct ID3DXMesh **mesh)
2601 HRESULT hr;
2602 D3DVERTEXELEMENT9 declaration[MAX_FVF_DECL_SIZE];
2604 TRACE("(%u, %u, %u, %u, %p, %p)\n", numfaces, numvertices, options, fvf, device, mesh);
2606 hr = D3DXDeclaratorFromFVF(fvf, declaration);
2607 if (FAILED(hr)) return hr;
2609 return D3DXCreateMesh(numfaces, numvertices, options, declaration, device, mesh);
2613 struct mesh_data {
2614 DWORD num_vertices;
2615 DWORD num_poly_faces;
2616 DWORD num_tri_faces;
2617 D3DXVECTOR3 *vertices;
2618 DWORD *num_tri_per_face;
2619 DWORD *indices;
2621 DWORD fvf;
2623 /* optional mesh data */
2625 DWORD num_normals;
2626 D3DXVECTOR3 *normals;
2627 DWORD *normal_indices;
2629 D3DXVECTOR2 *tex_coords;
2631 DWORD *vertex_colors;
2633 DWORD num_materials;
2634 D3DXMATERIAL *materials;
2635 DWORD *material_indices;
2637 struct ID3DXSkinInfo *skin_info;
2638 DWORD nb_bones;
2641 static HRESULT parse_texture_filename(ID3DXFileData *filedata, LPSTR *filename_out)
2643 HRESULT hr;
2644 SIZE_T data_size;
2645 BYTE *data;
2646 char *filename_in;
2647 char *filename = NULL;
2649 /* template TextureFilename {
2650 * STRING filename;
2654 HeapFree(GetProcessHeap(), 0, *filename_out);
2655 *filename_out = NULL;
2657 hr = filedata->lpVtbl->Lock(filedata, &data_size, (const void**)&data);
2658 if (FAILED(hr)) return hr;
2660 /* FIXME: String must be retrieved directly instead of through a pointer once ID3DXFILE is fixed */
2661 if (data_size < sizeof(LPSTR)) {
2662 WARN("truncated data (%lu bytes)\n", data_size);
2663 filedata->lpVtbl->Unlock(filedata);
2664 return E_FAIL;
2666 filename_in = *(LPSTR*)data;
2668 filename = HeapAlloc(GetProcessHeap(), 0, strlen(filename_in) + 1);
2669 if (!filename) {
2670 filedata->lpVtbl->Unlock(filedata);
2671 return E_OUTOFMEMORY;
2674 strcpy(filename, filename_in);
2675 *filename_out = filename;
2677 filedata->lpVtbl->Unlock(filedata);
2679 return D3D_OK;
2682 static HRESULT parse_material(ID3DXFileData *filedata, D3DXMATERIAL *material)
2684 HRESULT hr;
2685 SIZE_T data_size;
2686 const BYTE *data;
2687 GUID type;
2688 ID3DXFileData *child;
2689 SIZE_T nb_children;
2690 int i;
2692 material->pTextureFilename = NULL;
2694 hr = filedata->lpVtbl->Lock(filedata, &data_size, (const void**)&data);
2695 if (FAILED(hr)) return hr;
2698 * template ColorRGBA {
2699 * FLOAT red;
2700 * FLOAT green;
2701 * FLOAT blue;
2702 * FLOAT alpha;
2704 * template ColorRGB {
2705 * FLOAT red;
2706 * FLOAT green;
2707 * FLOAT blue;
2709 * template Material {
2710 * ColorRGBA faceColor;
2711 * FLOAT power;
2712 * ColorRGB specularColor;
2713 * ColorRGB emissiveColor;
2714 * [ ... ]
2717 if (data_size != sizeof(FLOAT) * 11) {
2718 WARN("incorrect data size (%ld bytes)\n", data_size);
2719 filedata->lpVtbl->Unlock(filedata);
2720 return E_FAIL;
2723 memcpy(&material->MatD3D.Diffuse, data, sizeof(D3DCOLORVALUE));
2724 data += sizeof(D3DCOLORVALUE);
2725 material->MatD3D.Power = *(FLOAT*)data;
2726 data += sizeof(FLOAT);
2727 memcpy(&material->MatD3D.Specular, data, sizeof(FLOAT) * 3);
2728 material->MatD3D.Specular.a = 1.0f;
2729 data += 3 * sizeof(FLOAT);
2730 memcpy(&material->MatD3D.Emissive, data, sizeof(FLOAT) * 3);
2731 material->MatD3D.Emissive.a = 1.0f;
2732 material->MatD3D.Ambient.r = 0.0f;
2733 material->MatD3D.Ambient.g = 0.0f;
2734 material->MatD3D.Ambient.b = 0.0f;
2735 material->MatD3D.Ambient.a = 1.0f;
2737 filedata->lpVtbl->Unlock(filedata);
2739 hr = filedata->lpVtbl->GetChildren(filedata, &nb_children);
2740 if (FAILED(hr))
2741 return hr;
2743 for (i = 0; i < nb_children; i++)
2745 hr = filedata->lpVtbl->GetChild(filedata, i, &child);
2746 if (FAILED(hr))
2747 return hr;
2748 hr = child->lpVtbl->GetType(child, &type);
2749 if (FAILED(hr))
2750 return hr;
2752 if (IsEqualGUID(&type, &TID_D3DRMTextureFilename)) {
2753 hr = parse_texture_filename(child, &material->pTextureFilename);
2754 if (FAILED(hr))
2755 return hr;
2759 return D3D_OK;
2762 static void destroy_materials(struct mesh_data *mesh)
2764 DWORD i;
2765 for (i = 0; i < mesh->num_materials; i++)
2766 HeapFree(GetProcessHeap(), 0, mesh->materials[i].pTextureFilename);
2767 HeapFree(GetProcessHeap(), 0, mesh->materials);
2768 HeapFree(GetProcessHeap(), 0, mesh->material_indices);
2769 mesh->num_materials = 0;
2770 mesh->materials = NULL;
2771 mesh->material_indices = NULL;
2774 static HRESULT parse_material_list(ID3DXFileData *filedata, struct mesh_data *mesh)
2776 HRESULT hr;
2777 SIZE_T data_size;
2778 const DWORD *data, *in_ptr;
2779 GUID type;
2780 ID3DXFileData *child;
2781 DWORD num_materials;
2782 DWORD i;
2783 SIZE_T nb_children;
2785 destroy_materials(mesh);
2787 hr = filedata->lpVtbl->Lock(filedata, &data_size, (const void**)&data);
2788 if (FAILED(hr)) return hr;
2790 /* template MeshMaterialList {
2791 * DWORD nMaterials;
2792 * DWORD nFaceIndexes;
2793 * array DWORD faceIndexes[nFaceIndexes];
2794 * [ Material ]
2798 in_ptr = data;
2799 hr = E_FAIL;
2801 if (data_size < sizeof(DWORD)) {
2802 WARN("truncated data (%ld bytes)\n", data_size);
2803 goto end;
2805 num_materials = *in_ptr++;
2806 if (!num_materials) {
2807 hr = D3D_OK;
2808 goto end;
2811 if (data_size < 2 * sizeof(DWORD)) {
2812 WARN("truncated data (%ld bytes)\n", data_size);
2813 goto end;
2815 if (*in_ptr++ != mesh->num_poly_faces) {
2816 WARN("number of material face indices (%u) doesn't match number of faces (%u)\n",
2817 *(in_ptr - 1), mesh->num_poly_faces);
2818 goto end;
2820 if (data_size < 2 * sizeof(DWORD) + mesh->num_poly_faces * sizeof(DWORD)) {
2821 WARN("truncated data (%ld bytes)\n", data_size);
2822 goto end;
2824 for (i = 0; i < mesh->num_poly_faces; i++) {
2825 if (*in_ptr++ >= num_materials) {
2826 WARN("face %u: reference to undefined material %u (only %u materials)\n",
2827 i, *(in_ptr - 1), num_materials);
2828 goto end;
2832 mesh->materials = HeapAlloc(GetProcessHeap(), 0, num_materials * sizeof(*mesh->materials));
2833 mesh->material_indices = HeapAlloc(GetProcessHeap(), 0, mesh->num_poly_faces * sizeof(*mesh->material_indices));
2834 if (!mesh->materials || !mesh->material_indices) {
2835 hr = E_OUTOFMEMORY;
2836 goto end;
2838 memcpy(mesh->material_indices, data + 2, mesh->num_poly_faces * sizeof(DWORD));
2840 hr = filedata->lpVtbl->GetChildren(filedata, &nb_children);
2841 if (FAILED(hr))
2842 goto end;
2844 for (i = 0; i < nb_children; i++)
2846 hr = filedata->lpVtbl->GetChild(filedata, i, &child);
2847 if (FAILED(hr))
2848 goto end;
2849 hr = child->lpVtbl->GetType(child, &type);
2850 if (FAILED(hr))
2851 goto end;
2853 if (IsEqualGUID(&type, &TID_D3DRMMaterial)) {
2854 if (mesh->num_materials >= num_materials) {
2855 WARN("more materials defined than declared\n");
2856 hr = E_FAIL;
2857 goto end;
2859 hr = parse_material(child, &mesh->materials[mesh->num_materials++]);
2860 if (FAILED(hr))
2861 goto end;
2864 if (num_materials != mesh->num_materials) {
2865 WARN("only %u of %u materials defined\n", num_materials, mesh->num_materials);
2866 hr = E_FAIL;
2869 end:
2870 filedata->lpVtbl->Unlock(filedata);
2871 return hr;
2874 static HRESULT parse_texture_coords(ID3DXFileData *filedata, struct mesh_data *mesh)
2876 HRESULT hr;
2877 SIZE_T data_size;
2878 const BYTE *data;
2880 HeapFree(GetProcessHeap(), 0, mesh->tex_coords);
2881 mesh->tex_coords = NULL;
2883 hr = filedata->lpVtbl->Lock(filedata, &data_size, (const void**)&data);
2884 if (FAILED(hr)) return hr;
2886 /* template Coords2d {
2887 * FLOAT u;
2888 * FLOAT v;
2890 * template MeshTextureCoords {
2891 * DWORD nTextureCoords;
2892 * array Coords2d textureCoords[nTextureCoords];
2896 hr = E_FAIL;
2898 if (data_size < sizeof(DWORD)) {
2899 WARN("truncated data (%ld bytes)\n", data_size);
2900 goto end;
2902 if (*(DWORD*)data != mesh->num_vertices) {
2903 WARN("number of texture coordinates (%u) doesn't match number of vertices (%u)\n",
2904 *(DWORD*)data, mesh->num_vertices);
2905 goto end;
2907 data += sizeof(DWORD);
2908 if (data_size < sizeof(DWORD) + mesh->num_vertices * sizeof(*mesh->tex_coords)) {
2909 WARN("truncated data (%ld bytes)\n", data_size);
2910 goto end;
2913 mesh->tex_coords = HeapAlloc(GetProcessHeap(), 0, mesh->num_vertices * sizeof(*mesh->tex_coords));
2914 if (!mesh->tex_coords) {
2915 hr = E_OUTOFMEMORY;
2916 goto end;
2918 memcpy(mesh->tex_coords, data, mesh->num_vertices * sizeof(*mesh->tex_coords));
2920 mesh->fvf |= D3DFVF_TEX1;
2922 hr = D3D_OK;
2924 end:
2925 filedata->lpVtbl->Unlock(filedata);
2926 return hr;
2929 static HRESULT parse_vertex_colors(ID3DXFileData *filedata, struct mesh_data *mesh)
2931 HRESULT hr;
2932 SIZE_T data_size;
2933 const BYTE *data;
2934 DWORD num_colors;
2935 DWORD i;
2937 HeapFree(GetProcessHeap(), 0, mesh->vertex_colors);
2938 mesh->vertex_colors = NULL;
2940 hr = filedata->lpVtbl->Lock(filedata, &data_size, (const void**)&data);
2941 if (FAILED(hr)) return hr;
2943 /* template IndexedColor {
2944 * DWORD index;
2945 * ColorRGBA indexColor;
2947 * template MeshVertexColors {
2948 * DWORD nVertexColors;
2949 * array IndexedColor vertexColors[nVertexColors];
2953 hr = E_FAIL;
2955 if (data_size < sizeof(DWORD)) {
2956 WARN("truncated data (%ld bytes)\n", data_size);
2957 goto end;
2959 num_colors = *(DWORD*)data;
2960 data += sizeof(DWORD);
2961 if (data_size < sizeof(DWORD) + num_colors * (sizeof(DWORD) + sizeof(D3DCOLORVALUE))) {
2962 WARN("truncated data (%ld bytes)\n", data_size);
2963 goto end;
2966 mesh->vertex_colors = HeapAlloc(GetProcessHeap(), 0, mesh->num_vertices * sizeof(DWORD));
2967 if (!mesh->vertex_colors) {
2968 hr = E_OUTOFMEMORY;
2969 goto end;
2972 for (i = 0; i < mesh->num_vertices; i++)
2973 mesh->vertex_colors[i] = D3DCOLOR_ARGB(0, 0xff, 0xff, 0xff);
2974 for (i = 0; i < num_colors; i++)
2976 D3DCOLORVALUE color;
2977 DWORD index = *(DWORD*)data;
2978 data += sizeof(DWORD);
2979 if (index >= mesh->num_vertices) {
2980 WARN("vertex color %u references undefined vertex %u (only %u vertices)\n",
2981 i, index, mesh->num_vertices);
2982 goto end;
2984 memcpy(&color, data, sizeof(color));
2985 data += sizeof(color);
2986 color.r = min(1.0f, max(0.0f, color.r));
2987 color.g = min(1.0f, max(0.0f, color.g));
2988 color.b = min(1.0f, max(0.0f, color.b));
2989 color.a = min(1.0f, max(0.0f, color.a));
2990 mesh->vertex_colors[index] = D3DCOLOR_ARGB((BYTE)(color.a * 255.0f + 0.5f),
2991 (BYTE)(color.r * 255.0f + 0.5f),
2992 (BYTE)(color.g * 255.0f + 0.5f),
2993 (BYTE)(color.b * 255.0f + 0.5f));
2996 mesh->fvf |= D3DFVF_DIFFUSE;
2998 hr = D3D_OK;
3000 end:
3001 filedata->lpVtbl->Unlock(filedata);
3002 return hr;
3005 static HRESULT parse_normals(ID3DXFileData *filedata, struct mesh_data *mesh)
3007 HRESULT hr;
3008 SIZE_T data_size;
3009 const BYTE *data;
3010 DWORD *index_out_ptr;
3011 DWORD i;
3012 DWORD num_face_indices = mesh->num_poly_faces * 2 + mesh->num_tri_faces;
3014 HeapFree(GetProcessHeap(), 0, mesh->normals);
3015 mesh->num_normals = 0;
3016 mesh->normals = NULL;
3017 mesh->normal_indices = NULL;
3018 mesh->fvf |= D3DFVF_NORMAL;
3020 hr = filedata->lpVtbl->Lock(filedata, &data_size, (const void**)&data);
3021 if (FAILED(hr)) return hr;
3023 /* template Vector {
3024 * FLOAT x;
3025 * FLOAT y;
3026 * FLOAT z;
3028 * template MeshFace {
3029 * DWORD nFaceVertexIndices;
3030 * array DWORD faceVertexIndices[nFaceVertexIndices];
3032 * template MeshNormals {
3033 * DWORD nNormals;
3034 * array Vector normals[nNormals];
3035 * DWORD nFaceNormals;
3036 * array MeshFace faceNormals[nFaceNormals];
3040 hr = E_FAIL;
3042 if (data_size < sizeof(DWORD) * 2) {
3043 WARN("truncated data (%ld bytes)\n", data_size);
3044 goto end;
3046 mesh->num_normals = *(DWORD*)data;
3047 data += sizeof(DWORD);
3048 if (data_size < sizeof(DWORD) * 2 + mesh->num_normals * sizeof(D3DXVECTOR3) +
3049 num_face_indices * sizeof(DWORD)) {
3050 WARN("truncated data (%ld bytes)\n", data_size);
3051 goto end;
3054 mesh->normals = HeapAlloc(GetProcessHeap(), 0, mesh->num_normals * sizeof(D3DXVECTOR3));
3055 mesh->normal_indices = HeapAlloc(GetProcessHeap(), 0, num_face_indices * sizeof(DWORD));
3056 if (!mesh->normals || !mesh->normal_indices) {
3057 hr = E_OUTOFMEMORY;
3058 goto end;
3061 memcpy(mesh->normals, data, mesh->num_normals * sizeof(D3DXVECTOR3));
3062 data += mesh->num_normals * sizeof(D3DXVECTOR3);
3063 for (i = 0; i < mesh->num_normals; i++)
3064 D3DXVec3Normalize(&mesh->normals[i], &mesh->normals[i]);
3066 if (*(DWORD*)data != mesh->num_poly_faces) {
3067 WARN("number of face normals (%u) doesn't match number of faces (%u)\n",
3068 *(DWORD*)data, mesh->num_poly_faces);
3069 goto end;
3071 data += sizeof(DWORD);
3072 index_out_ptr = mesh->normal_indices;
3073 for (i = 0; i < mesh->num_poly_faces; i++)
3075 DWORD j;
3076 DWORD count = *(DWORD*)data;
3077 if (count != mesh->num_tri_per_face[i] + 2) {
3078 WARN("face %u: number of normals (%u) doesn't match number of vertices (%u)\n",
3079 i, count, mesh->num_tri_per_face[i] + 2);
3080 goto end;
3082 data += sizeof(DWORD);
3084 for (j = 0; j < count; j++) {
3085 DWORD normal_index = *(DWORD*)data;
3086 if (normal_index >= mesh->num_normals) {
3087 WARN("face %u, normal index %u: reference to undefined normal %u (only %u normals)\n",
3088 i, j, normal_index, mesh->num_normals);
3089 goto end;
3091 *index_out_ptr++ = normal_index;
3092 data += sizeof(DWORD);
3096 hr = D3D_OK;
3098 end:
3099 filedata->lpVtbl->Unlock(filedata);
3100 return hr;
3103 static HRESULT parse_skin_mesh_info(ID3DXFileData *filedata, struct mesh_data *mesh_data, DWORD index)
3105 HRESULT hr;
3106 SIZE_T data_size;
3107 const BYTE *data;
3109 TRACE("(%p, %p, %u)\n", filedata, mesh_data, index);
3111 hr = filedata->lpVtbl->Lock(filedata, &data_size, (const void**)&data);
3112 if (FAILED(hr)) return hr;
3114 hr = E_FAIL;
3116 if (!mesh_data->skin_info) {
3117 if (data_size < sizeof(WORD) * 3) {
3118 WARN("truncated data (%ld bytes)\n", data_size);
3119 goto end;
3121 /* Skip nMaxSkinWeightsPerVertex and nMaxSkinWeightsPerFace */
3122 data += 2 * sizeof(WORD);
3123 mesh_data->nb_bones = *(WORD*)data;
3124 hr = D3DXCreateSkinInfoFVF(mesh_data->num_vertices, mesh_data->fvf, mesh_data->nb_bones, &mesh_data->skin_info);
3125 } else {
3126 const char *name;
3127 DWORD nb_influences;
3129 /* FIXME: String must be retrieved directly instead of through a pointer once ID3DXFILE is fixed */
3130 name = *(const char**)data;
3131 data += sizeof(char*);
3133 nb_influences = *(DWORD*)data;
3134 data += sizeof(DWORD);
3136 if (data_size < (sizeof(char*) + sizeof(DWORD) + nb_influences * (sizeof(DWORD) + sizeof(FLOAT)) + 16 * sizeof(FLOAT))) {
3137 WARN("truncated data (%ld bytes)\n", data_size);
3138 goto end;
3141 hr = mesh_data->skin_info->lpVtbl->SetBoneName(mesh_data->skin_info, index, name);
3142 if (SUCCEEDED(hr))
3143 hr = mesh_data->skin_info->lpVtbl->SetBoneInfluence(mesh_data->skin_info, index, nb_influences,
3144 (const DWORD*)data, (const FLOAT*)(data + nb_influences * sizeof(DWORD)));
3145 if (SUCCEEDED(hr))
3146 hr = mesh_data->skin_info->lpVtbl->SetBoneOffsetMatrix(mesh_data->skin_info, index,
3147 (const D3DMATRIX*)(data + nb_influences * (sizeof(DWORD) + sizeof(FLOAT))));
3150 end:
3151 filedata->lpVtbl->Unlock(filedata);
3152 return hr;
3155 /* for provide_flags parameters */
3156 #define PROVIDE_MATERIALS 0x1
3157 #define PROVIDE_SKININFO 0x2
3158 #define PROVIDE_ADJACENCY 0x4
3160 static HRESULT parse_mesh(ID3DXFileData *filedata, struct mesh_data *mesh_data, DWORD provide_flags)
3162 HRESULT hr;
3163 SIZE_T data_size;
3164 const BYTE *data, *in_ptr;
3165 DWORD *index_out_ptr;
3166 GUID type;
3167 ID3DXFileData *child;
3168 DWORD i;
3169 SIZE_T nb_children;
3170 DWORD nb_skin_weigths_info = 0;
3173 * template Mesh {
3174 * DWORD nVertices;
3175 * array Vector vertices[nVertices];
3176 * DWORD nFaces;
3177 * array MeshFace faces[nFaces];
3178 * [ ... ]
3182 hr = filedata->lpVtbl->Lock(filedata, &data_size, (const void**)&data);
3183 if (FAILED(hr)) return hr;
3185 in_ptr = data;
3186 hr = E_FAIL;
3188 if (data_size < sizeof(DWORD) * 2) {
3189 WARN("truncated data (%ld bytes)\n", data_size);
3190 goto end;
3192 mesh_data->num_vertices = *(DWORD*)in_ptr;
3193 if (data_size < sizeof(DWORD) * 2 + mesh_data->num_vertices * sizeof(D3DXVECTOR3)) {
3194 WARN("truncated data (%ld bytes)\n", data_size);
3195 goto end;
3197 in_ptr += sizeof(DWORD) + mesh_data->num_vertices * sizeof(D3DXVECTOR3);
3199 mesh_data->num_poly_faces = *(DWORD*)in_ptr;
3200 in_ptr += sizeof(DWORD);
3202 mesh_data->num_tri_faces = 0;
3203 for (i = 0; i < mesh_data->num_poly_faces; i++)
3205 DWORD num_poly_vertices;
3206 DWORD j;
3208 if (data_size - (in_ptr - data) < sizeof(DWORD)) {
3209 WARN("truncated data (%ld bytes)\n", data_size);
3210 goto end;
3212 num_poly_vertices = *(DWORD*)in_ptr;
3213 in_ptr += sizeof(DWORD);
3214 if (data_size - (in_ptr - data) < num_poly_vertices * sizeof(DWORD)) {
3215 WARN("truncated data (%ld bytes)\n", data_size);
3216 goto end;
3218 if (num_poly_vertices < 3) {
3219 WARN("face %u has only %u vertices\n", i, num_poly_vertices);
3220 goto end;
3222 for (j = 0; j < num_poly_vertices; j++) {
3223 if (*(DWORD*)in_ptr >= mesh_data->num_vertices) {
3224 WARN("face %u, index %u: undefined vertex %u (only %u vertices)\n",
3225 i, j, *(DWORD*)in_ptr, mesh_data->num_vertices);
3226 goto end;
3228 in_ptr += sizeof(DWORD);
3230 mesh_data->num_tri_faces += num_poly_vertices - 2;
3233 mesh_data->fvf = D3DFVF_XYZ;
3235 mesh_data->vertices = HeapAlloc(GetProcessHeap(), 0,
3236 mesh_data->num_vertices * sizeof(*mesh_data->vertices));
3237 mesh_data->num_tri_per_face = HeapAlloc(GetProcessHeap(), 0,
3238 mesh_data->num_poly_faces * sizeof(*mesh_data->num_tri_per_face));
3239 mesh_data->indices = HeapAlloc(GetProcessHeap(), 0,
3240 (mesh_data->num_tri_faces + mesh_data->num_poly_faces * 2) * sizeof(*mesh_data->indices));
3241 if (!mesh_data->vertices || !mesh_data->num_tri_per_face || !mesh_data->indices) {
3242 hr = E_OUTOFMEMORY;
3243 goto end;
3246 in_ptr = data + sizeof(DWORD);
3247 memcpy(mesh_data->vertices, in_ptr, mesh_data->num_vertices * sizeof(D3DXVECTOR3));
3248 in_ptr += mesh_data->num_vertices * sizeof(D3DXVECTOR3) + sizeof(DWORD);
3250 index_out_ptr = mesh_data->indices;
3251 for (i = 0; i < mesh_data->num_poly_faces; i++)
3253 DWORD count;
3255 count = *(DWORD*)in_ptr;
3256 in_ptr += sizeof(DWORD);
3257 mesh_data->num_tri_per_face[i] = count - 2;
3259 while (count--) {
3260 *index_out_ptr++ = *(DWORD*)in_ptr;
3261 in_ptr += sizeof(DWORD);
3265 hr = filedata->lpVtbl->GetChildren(filedata, &nb_children);
3266 if (FAILED(hr))
3267 goto end;
3269 for (i = 0; i < nb_children; i++)
3271 hr = filedata->lpVtbl->GetChild(filedata, i, &child);
3272 if (FAILED(hr))
3273 goto end;
3274 hr = child->lpVtbl->GetType(child, &type);
3275 if (FAILED(hr))
3276 goto end;
3278 if (IsEqualGUID(&type, &TID_D3DRMMeshNormals)) {
3279 hr = parse_normals(child, mesh_data);
3280 } else if (IsEqualGUID(&type, &TID_D3DRMMeshVertexColors)) {
3281 hr = parse_vertex_colors(child, mesh_data);
3282 } else if (IsEqualGUID(&type, &TID_D3DRMMeshTextureCoords)) {
3283 hr = parse_texture_coords(child, mesh_data);
3284 hr = filedata->lpVtbl->GetChild(filedata, i, &child);
3285 if (FAILED(hr))
3286 goto end;
3287 } else if (IsEqualGUID(&type, &TID_D3DRMMeshMaterialList) &&
3288 (provide_flags & PROVIDE_MATERIALS))
3290 hr = parse_material_list(child, mesh_data);
3291 } else if (provide_flags & PROVIDE_SKININFO) {
3292 if (IsEqualGUID(&type, &DXFILEOBJ_XSkinMeshHeader)) {
3293 if (mesh_data->skin_info) {
3294 WARN("Skin mesh header already encountered\n");
3295 hr = E_FAIL;
3296 goto end;
3298 hr = parse_skin_mesh_info(child, mesh_data, 0);
3299 if (FAILED(hr))
3300 goto end;
3301 } else if (IsEqualGUID(&type, &DXFILEOBJ_SkinWeights)) {
3302 if (!mesh_data->skin_info) {
3303 WARN("Skin weigths found but skin mesh header not encountered yet\n");
3304 hr = E_FAIL;
3305 goto end;
3307 hr = parse_skin_mesh_info(child, mesh_data, nb_skin_weigths_info);
3308 if (FAILED(hr))
3309 goto end;
3310 nb_skin_weigths_info++;
3313 if (FAILED(hr))
3314 goto end;
3317 if (mesh_data->skin_info && (nb_skin_weigths_info != mesh_data->nb_bones)) {
3318 WARN("Mismatch between nb skin weights info %u encountered and nb bones %u from skin mesh header\n",
3319 nb_skin_weigths_info, mesh_data->nb_bones);
3320 hr = E_FAIL;
3321 goto end;
3324 hr = D3D_OK;
3326 end:
3327 filedata->lpVtbl->Unlock(filedata);
3328 return hr;
3331 static HRESULT generate_effects(ID3DXBuffer *materials, DWORD num_materials,
3332 ID3DXBuffer **effects)
3334 HRESULT hr;
3335 D3DXEFFECTINSTANCE *effect_ptr;
3336 BYTE *out_ptr;
3337 const D3DXMATERIAL *material_ptr = ID3DXBuffer_GetBufferPointer(materials);
3338 static const struct {
3339 const char *param_name;
3340 DWORD name_size;
3341 DWORD num_bytes;
3342 DWORD value_offset;
3343 } material_effects[] = {
3344 #define EFFECT_TABLE_ENTRY(str, field) \
3345 {str, sizeof(str), sizeof(material_ptr->MatD3D.field), offsetof(D3DXMATERIAL, MatD3D.field)}
3346 EFFECT_TABLE_ENTRY("Diffuse", Diffuse),
3347 EFFECT_TABLE_ENTRY("Power", Power),
3348 EFFECT_TABLE_ENTRY("Specular", Specular),
3349 EFFECT_TABLE_ENTRY("Emissive", Emissive),
3350 EFFECT_TABLE_ENTRY("Ambient", Ambient),
3351 #undef EFFECT_TABLE_ENTRY
3353 static const char texture_paramname[] = "Texture0@Name";
3354 DWORD buffer_size;
3355 DWORD i;
3357 /* effects buffer layout:
3359 * D3DXEFFECTINSTANCE effects[num_materials];
3360 * for (effect in effects)
3362 * D3DXEFFECTDEFAULT defaults[effect.NumDefaults];
3363 * for (default in defaults)
3365 * *default.pParamName;
3366 * *default.pValue;
3370 buffer_size = sizeof(D3DXEFFECTINSTANCE);
3371 buffer_size += sizeof(D3DXEFFECTDEFAULT) * ARRAY_SIZE(material_effects);
3372 for (i = 0; i < ARRAY_SIZE(material_effects); i++) {
3373 buffer_size += material_effects[i].name_size;
3374 buffer_size += material_effects[i].num_bytes;
3376 buffer_size *= num_materials;
3377 for (i = 0; i < num_materials; i++) {
3378 if (material_ptr[i].pTextureFilename) {
3379 buffer_size += sizeof(D3DXEFFECTDEFAULT);
3380 buffer_size += sizeof(texture_paramname);
3381 buffer_size += strlen(material_ptr[i].pTextureFilename) + 1;
3385 hr = D3DXCreateBuffer(buffer_size, effects);
3386 if (FAILED(hr)) return hr;
3387 effect_ptr = ID3DXBuffer_GetBufferPointer(*effects);
3388 out_ptr = (BYTE*)(effect_ptr + num_materials);
3390 for (i = 0; i < num_materials; i++)
3392 DWORD j;
3393 D3DXEFFECTDEFAULT *defaults = (D3DXEFFECTDEFAULT*)out_ptr;
3395 effect_ptr->pDefaults = defaults;
3396 effect_ptr->NumDefaults = material_ptr->pTextureFilename ? 6 : 5;
3397 out_ptr = (BYTE*)(effect_ptr->pDefaults + effect_ptr->NumDefaults);
3399 for (j = 0; j < ARRAY_SIZE(material_effects); j++)
3401 defaults->pParamName = (LPSTR)out_ptr;
3402 strcpy(defaults->pParamName, material_effects[j].param_name);
3403 defaults->pValue = defaults->pParamName + material_effects[j].name_size;
3404 defaults->Type = D3DXEDT_FLOATS;
3405 defaults->NumBytes = material_effects[j].num_bytes;
3406 memcpy(defaults->pValue, (BYTE*)material_ptr + material_effects[j].value_offset, defaults->NumBytes);
3407 out_ptr = (BYTE*)defaults->pValue + defaults->NumBytes;
3408 defaults++;
3411 if (material_ptr->pTextureFilename) {
3412 defaults->pParamName = (LPSTR)out_ptr;
3413 strcpy(defaults->pParamName, texture_paramname);
3414 defaults->pValue = defaults->pParamName + sizeof(texture_paramname);
3415 defaults->Type = D3DXEDT_STRING;
3416 defaults->NumBytes = strlen(material_ptr->pTextureFilename) + 1;
3417 strcpy(defaults->pValue, material_ptr->pTextureFilename);
3418 out_ptr = (BYTE*)defaults->pValue + defaults->NumBytes;
3420 material_ptr++;
3421 effect_ptr++;
3423 assert(out_ptr - (BYTE*)ID3DXBuffer_GetBufferPointer(*effects) == buffer_size);
3425 return D3D_OK;
3428 HRESULT WINAPI D3DXLoadSkinMeshFromXof(struct ID3DXFileData *filedata, DWORD options,
3429 struct IDirect3DDevice9 *device, struct ID3DXBuffer **adjacency_out, struct ID3DXBuffer **materials_out,
3430 struct ID3DXBuffer **effects_out, DWORD *num_materials_out, struct ID3DXSkinInfo **skin_info_out,
3431 struct ID3DXMesh **mesh_out)
3433 HRESULT hr;
3434 DWORD *index_in_ptr;
3435 struct mesh_data mesh_data;
3436 DWORD total_vertices;
3437 ID3DXMesh *d3dxmesh = NULL;
3438 ID3DXBuffer *adjacency = NULL;
3439 ID3DXBuffer *materials = NULL;
3440 ID3DXBuffer *effects = NULL;
3441 struct vertex_duplication {
3442 DWORD normal_index;
3443 struct list entry;
3444 } *duplications = NULL;
3445 DWORD i;
3446 void *vertices = NULL;
3447 void *indices = NULL;
3448 BYTE *out_ptr;
3449 DWORD provide_flags = 0;
3451 TRACE("(%p, %x, %p, %p, %p, %p, %p, %p, %p)\n", filedata, options, device, adjacency_out, materials_out,
3452 effects_out, num_materials_out, skin_info_out, mesh_out);
3454 ZeroMemory(&mesh_data, sizeof(mesh_data));
3456 if (num_materials_out || materials_out || effects_out)
3457 provide_flags |= PROVIDE_MATERIALS;
3458 if (skin_info_out)
3459 provide_flags |= PROVIDE_SKININFO;
3461 hr = parse_mesh(filedata, &mesh_data, provide_flags);
3462 if (FAILED(hr)) goto cleanup;
3464 total_vertices = mesh_data.num_vertices;
3465 if (mesh_data.fvf & D3DFVF_NORMAL) {
3466 /* duplicate vertices with multiple normals */
3467 DWORD num_face_indices = mesh_data.num_poly_faces * 2 + mesh_data.num_tri_faces;
3468 duplications = HeapAlloc(GetProcessHeap(), 0, (mesh_data.num_vertices + num_face_indices) * sizeof(*duplications));
3469 if (!duplications) {
3470 hr = E_OUTOFMEMORY;
3471 goto cleanup;
3473 for (i = 0; i < total_vertices; i++)
3475 duplications[i].normal_index = -1;
3476 list_init(&duplications[i].entry);
3478 for (i = 0; i < num_face_indices; i++) {
3479 DWORD vertex_index = mesh_data.indices[i];
3480 DWORD normal_index = mesh_data.normal_indices[i];
3481 struct vertex_duplication *dup_ptr = &duplications[vertex_index];
3483 if (dup_ptr->normal_index == -1) {
3484 dup_ptr->normal_index = normal_index;
3485 } else {
3486 D3DXVECTOR3 *new_normal = &mesh_data.normals[normal_index];
3487 struct list *dup_list = &dup_ptr->entry;
3488 while (TRUE) {
3489 D3DXVECTOR3 *cur_normal = &mesh_data.normals[dup_ptr->normal_index];
3490 if (new_normal->x == cur_normal->x &&
3491 new_normal->y == cur_normal->y &&
3492 new_normal->z == cur_normal->z)
3494 mesh_data.indices[i] = dup_ptr - duplications;
3495 break;
3496 } else if (!list_next(dup_list, &dup_ptr->entry)) {
3497 dup_ptr = &duplications[total_vertices++];
3498 dup_ptr->normal_index = normal_index;
3499 list_add_tail(dup_list, &dup_ptr->entry);
3500 mesh_data.indices[i] = dup_ptr - duplications;
3501 break;
3502 } else {
3503 dup_ptr = LIST_ENTRY(list_next(dup_list, &dup_ptr->entry),
3504 struct vertex_duplication, entry);
3511 hr = D3DXCreateMeshFVF(mesh_data.num_tri_faces, total_vertices, options, mesh_data.fvf, device, &d3dxmesh);
3512 if (FAILED(hr)) goto cleanup;
3514 hr = d3dxmesh->lpVtbl->LockVertexBuffer(d3dxmesh, 0, &vertices);
3515 if (FAILED(hr)) goto cleanup;
3517 out_ptr = vertices;
3518 for (i = 0; i < mesh_data.num_vertices; i++) {
3519 *(D3DXVECTOR3*)out_ptr = mesh_data.vertices[i];
3520 out_ptr += sizeof(D3DXVECTOR3);
3521 if (mesh_data.fvf & D3DFVF_NORMAL) {
3522 if (duplications[i].normal_index == -1)
3523 ZeroMemory(out_ptr, sizeof(D3DXVECTOR3));
3524 else
3525 *(D3DXVECTOR3*)out_ptr = mesh_data.normals[duplications[i].normal_index];
3526 out_ptr += sizeof(D3DXVECTOR3);
3528 if (mesh_data.fvf & D3DFVF_DIFFUSE) {
3529 *(DWORD*)out_ptr = mesh_data.vertex_colors[i];
3530 out_ptr += sizeof(DWORD);
3532 if (mesh_data.fvf & D3DFVF_TEX1) {
3533 *(D3DXVECTOR2*)out_ptr = mesh_data.tex_coords[i];
3534 out_ptr += sizeof(D3DXVECTOR2);
3537 if (mesh_data.fvf & D3DFVF_NORMAL) {
3538 DWORD vertex_size = D3DXGetFVFVertexSize(mesh_data.fvf);
3539 out_ptr = vertices;
3540 for (i = 0; i < mesh_data.num_vertices; i++) {
3541 struct vertex_duplication *dup_ptr;
3542 LIST_FOR_EACH_ENTRY(dup_ptr, &duplications[i].entry, struct vertex_duplication, entry)
3544 int j = dup_ptr - duplications;
3545 BYTE *dest_vertex = (BYTE*)vertices + j * vertex_size;
3547 memcpy(dest_vertex, out_ptr, vertex_size);
3548 dest_vertex += sizeof(D3DXVECTOR3);
3549 *(D3DXVECTOR3*)dest_vertex = mesh_data.normals[dup_ptr->normal_index];
3551 out_ptr += vertex_size;
3554 d3dxmesh->lpVtbl->UnlockVertexBuffer(d3dxmesh);
3556 hr = d3dxmesh->lpVtbl->LockIndexBuffer(d3dxmesh, 0, &indices);
3557 if (FAILED(hr)) goto cleanup;
3559 index_in_ptr = mesh_data.indices;
3560 #define FILL_INDEX_BUFFER(indices_var) \
3561 for (i = 0; i < mesh_data.num_poly_faces; i++) \
3563 DWORD count = mesh_data.num_tri_per_face[i]; \
3564 WORD first_index = *index_in_ptr++; \
3565 while (count--) { \
3566 *indices_var++ = first_index; \
3567 *indices_var++ = *index_in_ptr; \
3568 index_in_ptr++; \
3569 *indices_var++ = *index_in_ptr; \
3571 index_in_ptr++; \
3573 if (options & D3DXMESH_32BIT) {
3574 DWORD *dword_indices = indices;
3575 FILL_INDEX_BUFFER(dword_indices)
3576 } else {
3577 WORD *word_indices = indices;
3578 FILL_INDEX_BUFFER(word_indices)
3580 #undef FILL_INDEX_BUFFER
3581 d3dxmesh->lpVtbl->UnlockIndexBuffer(d3dxmesh);
3583 if (mesh_data.material_indices) {
3584 DWORD *attrib_buffer = NULL;
3585 hr = d3dxmesh->lpVtbl->LockAttributeBuffer(d3dxmesh, 0, &attrib_buffer);
3586 if (FAILED(hr)) goto cleanup;
3587 for (i = 0; i < mesh_data.num_poly_faces; i++)
3589 DWORD count = mesh_data.num_tri_per_face[i];
3590 while (count--)
3591 *attrib_buffer++ = mesh_data.material_indices[i];
3593 d3dxmesh->lpVtbl->UnlockAttributeBuffer(d3dxmesh);
3595 hr = d3dxmesh->lpVtbl->OptimizeInplace(d3dxmesh,
3596 D3DXMESHOPT_ATTRSORT | D3DXMESHOPT_IGNOREVERTS | D3DXMESHOPT_DONOTSPLIT,
3597 NULL, NULL, NULL, NULL);
3598 if (FAILED(hr)) goto cleanup;
3601 if (mesh_data.num_materials && (materials_out || effects_out)) {
3602 DWORD buffer_size = mesh_data.num_materials * sizeof(D3DXMATERIAL);
3603 char *strings_out_ptr;
3604 D3DXMATERIAL *materials_ptr;
3606 for (i = 0; i < mesh_data.num_materials; i++) {
3607 if (mesh_data.materials[i].pTextureFilename)
3608 buffer_size += strlen(mesh_data.materials[i].pTextureFilename) + 1;
3611 hr = D3DXCreateBuffer(buffer_size, &materials);
3612 if (FAILED(hr)) goto cleanup;
3614 materials_ptr = ID3DXBuffer_GetBufferPointer(materials);
3615 memcpy(materials_ptr, mesh_data.materials, mesh_data.num_materials * sizeof(D3DXMATERIAL));
3616 strings_out_ptr = (char*)(materials_ptr + mesh_data.num_materials);
3617 for (i = 0; i < mesh_data.num_materials; i++) {
3618 if (materials_ptr[i].pTextureFilename) {
3619 strcpy(strings_out_ptr, mesh_data.materials[i].pTextureFilename);
3620 materials_ptr[i].pTextureFilename = strings_out_ptr;
3621 strings_out_ptr += strlen(mesh_data.materials[i].pTextureFilename) + 1;
3626 if (mesh_data.num_materials && effects_out) {
3627 hr = generate_effects(materials, mesh_data.num_materials, &effects);
3628 if (FAILED(hr)) goto cleanup;
3630 if (!materials_out) {
3631 ID3DXBuffer_Release(materials);
3632 materials = NULL;
3636 if (adjacency_out) {
3637 hr = D3DXCreateBuffer(mesh_data.num_tri_faces * 3 * sizeof(DWORD), &adjacency);
3638 if (FAILED(hr)) goto cleanup;
3639 hr = d3dxmesh->lpVtbl->GenerateAdjacency(d3dxmesh, 0.0f, ID3DXBuffer_GetBufferPointer(adjacency));
3640 if (FAILED(hr)) goto cleanup;
3643 *mesh_out = d3dxmesh;
3644 if (adjacency_out) *adjacency_out = adjacency;
3645 if (num_materials_out) *num_materials_out = mesh_data.num_materials;
3646 if (materials_out) *materials_out = materials;
3647 if (effects_out) *effects_out = effects;
3648 if (skin_info_out) *skin_info_out = mesh_data.skin_info;
3650 hr = D3D_OK;
3651 cleanup:
3652 if (FAILED(hr)) {
3653 if (d3dxmesh) IUnknown_Release(d3dxmesh);
3654 if (adjacency) ID3DXBuffer_Release(adjacency);
3655 if (materials) ID3DXBuffer_Release(materials);
3656 if (effects) ID3DXBuffer_Release(effects);
3657 if (mesh_data.skin_info) mesh_data.skin_info->lpVtbl->Release(mesh_data.skin_info);
3658 if (skin_info_out) *skin_info_out = NULL;
3660 HeapFree(GetProcessHeap(), 0, mesh_data.vertices);
3661 HeapFree(GetProcessHeap(), 0, mesh_data.num_tri_per_face);
3662 HeapFree(GetProcessHeap(), 0, mesh_data.indices);
3663 HeapFree(GetProcessHeap(), 0, mesh_data.normals);
3664 HeapFree(GetProcessHeap(), 0, mesh_data.normal_indices);
3665 destroy_materials(&mesh_data);
3666 HeapFree(GetProcessHeap(), 0, mesh_data.tex_coords);
3667 HeapFree(GetProcessHeap(), 0, mesh_data.vertex_colors);
3668 HeapFree(GetProcessHeap(), 0, duplications);
3669 return hr;
3672 HRESULT WINAPI D3DXLoadMeshHierarchyFromXA(const char *filename, DWORD options, struct IDirect3DDevice9 *device,
3673 struct ID3DXAllocateHierarchy *alloc_hier, struct ID3DXLoadUserData *load_user_data,
3674 D3DXFRAME **frame_hierarchy, struct ID3DXAnimationController **anim_controller)
3676 HRESULT hr;
3677 int len;
3678 LPWSTR filenameW;
3680 TRACE("(%s, %x, %p, %p, %p, %p, %p)\n", debugstr_a(filename), options,
3681 device, alloc_hier, load_user_data, frame_hierarchy, anim_controller);
3683 if (!filename)
3684 return D3DERR_INVALIDCALL;
3686 len = MultiByteToWideChar(CP_ACP, 0, filename, -1, NULL, 0);
3687 filenameW = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
3688 if (!filenameW) return E_OUTOFMEMORY;
3689 MultiByteToWideChar(CP_ACP, 0, filename, -1, filenameW, len);
3691 hr = D3DXLoadMeshHierarchyFromXW(filenameW, options, device,
3692 alloc_hier, load_user_data, frame_hierarchy, anim_controller);
3693 HeapFree(GetProcessHeap(), 0, filenameW);
3695 return hr;
3698 HRESULT WINAPI D3DXLoadMeshHierarchyFromXW(const WCHAR *filename, DWORD options, struct IDirect3DDevice9 *device,
3699 struct ID3DXAllocateHierarchy *alloc_hier, struct ID3DXLoadUserData *load_user_data,
3700 D3DXFRAME **frame_hierarchy, struct ID3DXAnimationController **anim_controller)
3702 HRESULT hr;
3703 DWORD size;
3704 LPVOID buffer;
3706 TRACE("(%s, %x, %p, %p, %p, %p, %p)\n", debugstr_w(filename), options,
3707 device, alloc_hier, load_user_data, frame_hierarchy, anim_controller);
3709 if (!filename)
3710 return D3DERR_INVALIDCALL;
3712 hr = map_view_of_file(filename, &buffer, &size);
3713 if (FAILED(hr))
3714 return D3DXERR_INVALIDDATA;
3716 hr = D3DXLoadMeshHierarchyFromXInMemory(buffer, size, options, device,
3717 alloc_hier, load_user_data, frame_hierarchy, anim_controller);
3719 UnmapViewOfFile(buffer);
3721 return hr;
3724 static HRESULT filedata_get_name(ID3DXFileData *filedata, char **name)
3726 HRESULT hr;
3727 SIZE_T name_len;
3729 hr = filedata->lpVtbl->GetName(filedata, NULL, &name_len);
3730 if (FAILED(hr)) return hr;
3732 if (!name_len)
3733 name_len++;
3734 *name = HeapAlloc(GetProcessHeap(), 0, name_len);
3735 if (!*name) return E_OUTOFMEMORY;
3737 hr = filedata->lpVtbl->GetName(filedata, *name, &name_len);
3738 if (FAILED(hr))
3739 HeapFree(GetProcessHeap(), 0, *name);
3740 else if (!name_len)
3741 (*name)[0] = 0;
3743 return hr;
3746 static HRESULT load_mesh_container(struct ID3DXFileData *filedata, DWORD options, struct IDirect3DDevice9 *device,
3747 struct ID3DXAllocateHierarchy *alloc_hier, D3DXMESHCONTAINER **mesh_container)
3749 HRESULT hr;
3750 ID3DXBuffer *adjacency = NULL;
3751 ID3DXBuffer *materials = NULL;
3752 ID3DXBuffer *effects = NULL;
3753 ID3DXSkinInfo *skin_info = NULL;
3754 D3DXMESHDATA mesh_data;
3755 DWORD num_materials = 0;
3756 char *name = NULL;
3758 mesh_data.Type = D3DXMESHTYPE_MESH;
3759 mesh_data.u.pMesh = NULL;
3761 hr = D3DXLoadSkinMeshFromXof(filedata, options, device,
3762 &adjacency, &materials, &effects, &num_materials,
3763 &skin_info, &mesh_data.u.pMesh);
3764 if (FAILED(hr)) return hr;
3766 hr = filedata_get_name(filedata, &name);
3767 if (FAILED(hr)) goto cleanup;
3769 hr = alloc_hier->lpVtbl->CreateMeshContainer(alloc_hier, name, &mesh_data,
3770 materials ? ID3DXBuffer_GetBufferPointer(materials) : NULL,
3771 effects ? ID3DXBuffer_GetBufferPointer(effects) : NULL,
3772 num_materials,
3773 adjacency ? ID3DXBuffer_GetBufferPointer(adjacency) : NULL,
3774 skin_info, mesh_container);
3776 cleanup:
3777 if (materials) ID3DXBuffer_Release(materials);
3778 if (effects) ID3DXBuffer_Release(effects);
3779 if (adjacency) ID3DXBuffer_Release(adjacency);
3780 if (skin_info) IUnknown_Release(skin_info);
3781 if (mesh_data.u.pMesh) IUnknown_Release(mesh_data.u.pMesh);
3782 HeapFree(GetProcessHeap(), 0, name);
3783 return hr;
3786 static HRESULT parse_transform_matrix(ID3DXFileData *filedata, D3DXMATRIX *transform)
3788 HRESULT hr;
3789 SIZE_T data_size;
3790 const BYTE *data;
3792 /* template Matrix4x4 {
3793 * array FLOAT matrix[16];
3795 * template FrameTransformMatrix {
3796 * Matrix4x4 frameMatrix;
3800 hr = filedata->lpVtbl->Lock(filedata, &data_size, (const void**)&data);
3801 if (FAILED(hr)) return hr;
3803 if (data_size != sizeof(D3DXMATRIX)) {
3804 WARN("incorrect data size (%ld bytes)\n", data_size);
3805 filedata->lpVtbl->Unlock(filedata);
3806 return E_FAIL;
3809 memcpy(transform, data, sizeof(D3DXMATRIX));
3811 filedata->lpVtbl->Unlock(filedata);
3812 return D3D_OK;
3815 static HRESULT load_frame(struct ID3DXFileData *filedata, DWORD options, struct IDirect3DDevice9 *device,
3816 struct ID3DXAllocateHierarchy *alloc_hier, D3DXFRAME **frame_out)
3818 HRESULT hr;
3819 GUID type;
3820 ID3DXFileData *child;
3821 char *name = NULL;
3822 D3DXFRAME *frame = NULL;
3823 D3DXMESHCONTAINER **next_container;
3824 D3DXFRAME **next_child;
3825 SIZE_T nb_children;
3826 int i;
3828 hr = filedata_get_name(filedata, &name);
3829 if (FAILED(hr)) return hr;
3831 hr = alloc_hier->lpVtbl->CreateFrame(alloc_hier, name, frame_out);
3832 HeapFree(GetProcessHeap(), 0, name);
3833 if (FAILED(hr)) return E_FAIL;
3835 frame = *frame_out;
3836 D3DXMatrixIdentity(&frame->TransformationMatrix);
3837 next_child = &frame->pFrameFirstChild;
3838 next_container = &frame->pMeshContainer;
3840 hr = filedata->lpVtbl->GetChildren(filedata, &nb_children);
3841 if (FAILED(hr))
3842 return hr;
3844 for (i = 0; i < nb_children; i++)
3846 hr = filedata->lpVtbl->GetChild(filedata, i, &child);
3847 if (FAILED(hr))
3848 return hr;
3849 hr = child->lpVtbl->GetType(child, &type);
3850 if (FAILED(hr))
3851 return hr;
3853 if (IsEqualGUID(&type, &TID_D3DRMMesh)) {
3854 hr = load_mesh_container(child, options, device, alloc_hier, next_container);
3855 if (SUCCEEDED(hr))
3856 next_container = &(*next_container)->pNextMeshContainer;
3857 } else if (IsEqualGUID(&type, &TID_D3DRMFrameTransformMatrix)) {
3858 hr = parse_transform_matrix(child, &frame->TransformationMatrix);
3859 } else if (IsEqualGUID(&type, &TID_D3DRMFrame)) {
3860 hr = load_frame(child, options, device, alloc_hier, next_child);
3861 if (SUCCEEDED(hr))
3862 next_child = &(*next_child)->pFrameSibling;
3864 if (FAILED(hr))
3865 return hr;
3868 return D3D_OK;
3871 HRESULT WINAPI D3DXLoadMeshHierarchyFromXInMemory(const void *memory, DWORD memory_size, DWORD options,
3872 struct IDirect3DDevice9 *device, struct ID3DXAllocateHierarchy *alloc_hier,
3873 struct ID3DXLoadUserData *load_user_data, D3DXFRAME **frame_hierarchy,
3874 struct ID3DXAnimationController **anim_controller)
3876 HRESULT hr;
3877 ID3DXFile *d3dxfile = NULL;
3878 ID3DXFileEnumObject *enumobj = NULL;
3879 ID3DXFileData *filedata = NULL;
3880 D3DXF_FILELOADMEMORY source;
3881 D3DXFRAME *first_frame = NULL;
3882 D3DXFRAME **next_frame = &first_frame;
3883 SIZE_T nb_children;
3884 GUID guid;
3885 int i;
3887 TRACE("(%p, %u, %x, %p, %p, %p, %p, %p)\n", memory, memory_size, options,
3888 device, alloc_hier, load_user_data, frame_hierarchy, anim_controller);
3890 if (!memory || !memory_size || !device || !frame_hierarchy || !alloc_hier)
3891 return D3DERR_INVALIDCALL;
3892 if (load_user_data || anim_controller) {
3893 if (load_user_data)
3894 FIXME("Loading user data not implemented\n");
3895 if (anim_controller)
3896 FIXME("Animation controller creation not implemented\n");
3897 return E_NOTIMPL;
3900 hr = D3DXFileCreate(&d3dxfile);
3901 if (FAILED(hr)) goto cleanup;
3903 hr = d3dxfile->lpVtbl->RegisterTemplates(d3dxfile, D3DRM_XTEMPLATES, D3DRM_XTEMPLATE_BYTES);
3904 if (FAILED(hr)) goto cleanup;
3906 source.lpMemory = (void*)memory;
3907 source.dSize = memory_size;
3908 hr = d3dxfile->lpVtbl->CreateEnumObject(d3dxfile, &source, D3DXF_FILELOAD_FROMMEMORY, &enumobj);
3909 if (FAILED(hr)) goto cleanup;
3911 hr = enumobj->lpVtbl->GetChildren(enumobj, &nb_children);
3912 if (FAILED(hr))
3913 goto cleanup;
3915 for (i = 0; i < nb_children; i++)
3917 hr = enumobj->lpVtbl->GetChild(enumobj, i, &filedata);
3918 if (FAILED(hr))
3919 goto cleanup;
3921 hr = filedata->lpVtbl->GetType(filedata, &guid);
3922 if (SUCCEEDED(hr)) {
3923 if (IsEqualGUID(&guid, &TID_D3DRMMesh)) {
3924 hr = alloc_hier->lpVtbl->CreateFrame(alloc_hier, NULL, next_frame);
3925 if (FAILED(hr)) {
3926 hr = E_FAIL;
3927 goto cleanup;
3930 D3DXMatrixIdentity(&(*next_frame)->TransformationMatrix);
3932 hr = load_mesh_container(filedata, options, device, alloc_hier, &(*next_frame)->pMeshContainer);
3933 if (FAILED(hr)) goto cleanup;
3934 } else if (IsEqualGUID(&guid, &TID_D3DRMFrame)) {
3935 hr = load_frame(filedata, options, device, alloc_hier, next_frame);
3936 if (FAILED(hr)) goto cleanup;
3938 while (*next_frame)
3939 next_frame = &(*next_frame)->pFrameSibling;
3942 filedata->lpVtbl->Release(filedata);
3943 filedata = NULL;
3944 if (FAILED(hr))
3945 goto cleanup;
3948 if (!first_frame) {
3949 hr = E_FAIL;
3950 } else if (first_frame->pFrameSibling) {
3951 D3DXFRAME *root_frame = NULL;
3952 hr = alloc_hier->lpVtbl->CreateFrame(alloc_hier, NULL, &root_frame);
3953 if (FAILED(hr)) {
3954 hr = E_FAIL;
3955 goto cleanup;
3957 D3DXMatrixIdentity(&root_frame->TransformationMatrix);
3958 root_frame->pFrameFirstChild = first_frame;
3959 *frame_hierarchy = root_frame;
3960 hr = D3D_OK;
3961 } else {
3962 *frame_hierarchy = first_frame;
3963 hr = D3D_OK;
3966 cleanup:
3967 if (FAILED(hr) && first_frame) D3DXFrameDestroy(first_frame, alloc_hier);
3968 if (filedata) filedata->lpVtbl->Release(filedata);
3969 if (enumobj) enumobj->lpVtbl->Release(enumobj);
3970 if (d3dxfile) d3dxfile->lpVtbl->Release(d3dxfile);
3971 return hr;
3974 HRESULT WINAPI D3DXCleanMesh(D3DXCLEANTYPE clean_type, ID3DXMesh *mesh_in, const DWORD *adjacency_in,
3975 ID3DXMesh **mesh_out, DWORD *adjacency_out, ID3DXBuffer **errors_and_warnings)
3977 FIXME("(%u, %p, %p, %p, %p, %p)\n", clean_type, mesh_in, adjacency_in, mesh_out, adjacency_out, errors_and_warnings);
3979 return E_NOTIMPL;
3982 HRESULT WINAPI D3DXFrameDestroy(D3DXFRAME *frame, ID3DXAllocateHierarchy *alloc_hier)
3984 HRESULT hr;
3985 BOOL last = FALSE;
3987 TRACE("(%p, %p)\n", frame, alloc_hier);
3989 if (!frame || !alloc_hier)
3990 return D3DERR_INVALIDCALL;
3992 while (!last) {
3993 D3DXMESHCONTAINER *container;
3994 D3DXFRAME *current_frame;
3996 if (frame->pFrameSibling) {
3997 current_frame = frame->pFrameSibling;
3998 frame->pFrameSibling = current_frame->pFrameSibling;
3999 current_frame->pFrameSibling = NULL;
4000 } else {
4001 current_frame = frame;
4002 last = TRUE;
4005 if (current_frame->pFrameFirstChild) {
4006 hr = D3DXFrameDestroy(current_frame->pFrameFirstChild, alloc_hier);
4007 if (FAILED(hr)) return hr;
4008 current_frame->pFrameFirstChild = NULL;
4011 container = current_frame->pMeshContainer;
4012 while (container) {
4013 D3DXMESHCONTAINER *next_container = container->pNextMeshContainer;
4014 hr = alloc_hier->lpVtbl->DestroyMeshContainer(alloc_hier, container);
4015 if (FAILED(hr)) return hr;
4016 container = next_container;
4018 hr = alloc_hier->lpVtbl->DestroyFrame(alloc_hier, current_frame);
4019 if (FAILED(hr)) return hr;
4021 return D3D_OK;
4024 HRESULT WINAPI D3DXLoadMeshFromXA(const char *filename, DWORD options, struct IDirect3DDevice9 *device,
4025 struct ID3DXBuffer **adjacency, struct ID3DXBuffer **materials, struct ID3DXBuffer **effect_instances,
4026 DWORD *num_materials, struct ID3DXMesh **mesh)
4028 HRESULT hr;
4029 int len;
4030 LPWSTR filenameW;
4032 TRACE("(%s, %x, %p, %p, %p, %p, %p, %p)\n", debugstr_a(filename), options,
4033 device, adjacency, materials, effect_instances, num_materials, mesh);
4035 if (!filename)
4036 return D3DERR_INVALIDCALL;
4038 len = MultiByteToWideChar(CP_ACP, 0, filename, -1, NULL, 0);
4039 filenameW = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
4040 if (!filenameW) return E_OUTOFMEMORY;
4041 MultiByteToWideChar(CP_ACP, 0, filename, -1, filenameW, len);
4043 hr = D3DXLoadMeshFromXW(filenameW, options, device, adjacency, materials,
4044 effect_instances, num_materials, mesh);
4045 HeapFree(GetProcessHeap(), 0, filenameW);
4047 return hr;
4050 HRESULT WINAPI D3DXLoadMeshFromXW(const WCHAR *filename, DWORD options, struct IDirect3DDevice9 *device,
4051 struct ID3DXBuffer **adjacency, struct ID3DXBuffer **materials, struct ID3DXBuffer **effect_instances,
4052 DWORD *num_materials, struct ID3DXMesh **mesh)
4054 HRESULT hr;
4055 DWORD size;
4056 LPVOID buffer;
4058 TRACE("(%s, %x, %p, %p, %p, %p, %p, %p)\n", debugstr_w(filename), options,
4059 device, adjacency, materials, effect_instances, num_materials, mesh);
4061 if (!filename)
4062 return D3DERR_INVALIDCALL;
4064 hr = map_view_of_file(filename, &buffer, &size);
4065 if (FAILED(hr))
4066 return D3DXERR_INVALIDDATA;
4068 hr = D3DXLoadMeshFromXInMemory(buffer, size, options, device, adjacency,
4069 materials, effect_instances, num_materials, mesh);
4071 UnmapViewOfFile(buffer);
4073 return hr;
4076 HRESULT WINAPI D3DXLoadMeshFromXResource(HMODULE module, const char *name, const char *type, DWORD options,
4077 struct IDirect3DDevice9 *device, struct ID3DXBuffer **adjacency, struct ID3DXBuffer **materials,
4078 struct ID3DXBuffer **effect_instances, DWORD *num_materials, struct ID3DXMesh **mesh)
4080 HRESULT hr;
4081 HRSRC resinfo;
4082 DWORD size;
4083 LPVOID buffer;
4085 TRACE("(%p, %s, %s, %x, %p, %p, %p, %p, %p, %p)\n",
4086 module, debugstr_a(name), debugstr_a(type), options, device,
4087 adjacency, materials, effect_instances, num_materials, mesh);
4089 resinfo = FindResourceA(module, name, type);
4090 if (!resinfo) return D3DXERR_INVALIDDATA;
4092 hr = load_resource_into_memory(module, resinfo, &buffer, &size);
4093 if (FAILED(hr)) return D3DXERR_INVALIDDATA;
4095 return D3DXLoadMeshFromXInMemory(buffer, size, options, device, adjacency,
4096 materials, effect_instances, num_materials, mesh);
4099 struct mesh_container
4101 struct list entry;
4102 ID3DXMesh *mesh;
4103 ID3DXBuffer *adjacency;
4104 ID3DXBuffer *materials;
4105 ID3DXBuffer *effects;
4106 DWORD num_materials;
4107 D3DXMATRIX transform;
4110 static HRESULT parse_frame(struct ID3DXFileData *filedata, DWORD options, struct IDirect3DDevice9 *device,
4111 const D3DXMATRIX *parent_transform, struct list *container_list, DWORD provide_flags)
4113 HRESULT hr;
4114 D3DXMATRIX transform = *parent_transform;
4115 ID3DXFileData *child;
4116 GUID type;
4117 SIZE_T nb_children;
4118 int i;
4120 hr = filedata->lpVtbl->GetChildren(filedata, &nb_children);
4121 if (FAILED(hr))
4122 return hr;
4124 for (i = 0; i < nb_children; i++)
4126 hr = filedata->lpVtbl->GetChild(filedata, i, &child);
4127 if (FAILED(hr))
4128 return hr;
4129 hr = child->lpVtbl->GetType(child, &type);
4130 if (FAILED(hr))
4131 return hr;
4133 if (IsEqualGUID(&type, &TID_D3DRMMesh)) {
4134 struct mesh_container *container = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(*container));
4135 if (!container)
4136 return E_OUTOFMEMORY;
4137 list_add_tail(container_list, &container->entry);
4138 container->transform = transform;
4140 hr = D3DXLoadSkinMeshFromXof(child, options, device,
4141 (provide_flags & PROVIDE_ADJACENCY) ? &container->adjacency : NULL,
4142 (provide_flags & PROVIDE_MATERIALS) ? &container->materials : NULL,
4143 NULL, &container->num_materials, NULL, &container->mesh);
4144 } else if (IsEqualGUID(&type, &TID_D3DRMFrameTransformMatrix)) {
4145 D3DXMATRIX new_transform;
4146 hr = parse_transform_matrix(child, &new_transform);
4147 D3DXMatrixMultiply(&transform, &transform, &new_transform);
4148 } else if (IsEqualGUID(&type, &TID_D3DRMFrame)) {
4149 hr = parse_frame(child, options, device, &transform, container_list, provide_flags);
4151 if (FAILED(hr))
4152 return hr;
4155 return D3D_OK;
4158 HRESULT WINAPI D3DXLoadMeshFromXInMemory(const void *memory, DWORD memory_size, DWORD options,
4159 struct IDirect3DDevice9 *device, struct ID3DXBuffer **adjacency_out, struct ID3DXBuffer **materials_out,
4160 struct ID3DXBuffer **effects_out, DWORD *num_materials_out, struct ID3DXMesh **mesh_out)
4162 HRESULT hr;
4163 ID3DXFile *d3dxfile = NULL;
4164 ID3DXFileEnumObject *enumobj = NULL;
4165 ID3DXFileData *filedata = NULL;
4166 D3DXF_FILELOADMEMORY source;
4167 ID3DXBuffer *materials = NULL;
4168 ID3DXBuffer *effects = NULL;
4169 ID3DXBuffer *adjacency = NULL;
4170 struct list container_list = LIST_INIT(container_list);
4171 struct mesh_container *container_ptr, *next_container_ptr;
4172 DWORD num_materials;
4173 DWORD num_faces, num_vertices;
4174 D3DXMATRIX identity;
4175 DWORD provide_flags = 0;
4176 DWORD fvf;
4177 ID3DXMesh *concat_mesh = NULL;
4178 D3DVERTEXELEMENT9 concat_decl[MAX_FVF_DECL_SIZE];
4179 BYTE *concat_vertices = NULL;
4180 void *concat_indices = NULL;
4181 DWORD index_offset;
4182 DWORD concat_vertex_size;
4183 SIZE_T nb_children;
4184 GUID guid;
4185 int i;
4187 TRACE("(%p, %u, %x, %p, %p, %p, %p, %p, %p)\n", memory, memory_size, options,
4188 device, adjacency_out, materials_out, effects_out, num_materials_out, mesh_out);
4190 if (!memory || !memory_size || !device || !mesh_out)
4191 return D3DERR_INVALIDCALL;
4193 hr = D3DXFileCreate(&d3dxfile);
4194 if (FAILED(hr)) goto cleanup;
4196 hr = d3dxfile->lpVtbl->RegisterTemplates(d3dxfile, D3DRM_XTEMPLATES, D3DRM_XTEMPLATE_BYTES);
4197 if (FAILED(hr)) goto cleanup;
4199 source.lpMemory = (void*)memory;
4200 source.dSize = memory_size;
4201 hr = d3dxfile->lpVtbl->CreateEnumObject(d3dxfile, &source, D3DXF_FILELOAD_FROMMEMORY, &enumobj);
4202 if (FAILED(hr)) goto cleanup;
4204 D3DXMatrixIdentity(&identity);
4205 if (adjacency_out) provide_flags |= PROVIDE_ADJACENCY;
4206 if (materials_out || effects_out) provide_flags |= PROVIDE_MATERIALS;
4208 hr = enumobj->lpVtbl->GetChildren(enumobj, &nb_children);
4209 if (FAILED(hr))
4210 goto cleanup;
4212 for (i = 0; i < nb_children; i++)
4214 hr = enumobj->lpVtbl->GetChild(enumobj, i, &filedata);
4215 if (FAILED(hr))
4216 goto cleanup;
4218 hr = filedata->lpVtbl->GetType(filedata, &guid);
4219 if (SUCCEEDED(hr)) {
4220 if (IsEqualGUID(&guid, &TID_D3DRMMesh)) {
4221 container_ptr = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(*container_ptr));
4222 if (!container_ptr) {
4223 hr = E_OUTOFMEMORY;
4224 goto cleanup;
4226 list_add_tail(&container_list, &container_ptr->entry);
4227 D3DXMatrixIdentity(&container_ptr->transform);
4229 hr = D3DXLoadSkinMeshFromXof(filedata, options, device,
4230 (provide_flags & PROVIDE_ADJACENCY) ? &container_ptr->adjacency : NULL,
4231 (provide_flags & PROVIDE_MATERIALS) ? &container_ptr->materials : NULL,
4232 NULL, &container_ptr->num_materials, NULL, &container_ptr->mesh);
4233 } else if (IsEqualGUID(&guid, &TID_D3DRMFrame)) {
4234 hr = parse_frame(filedata, options, device, &identity, &container_list, provide_flags);
4236 if (FAILED(hr)) goto cleanup;
4238 filedata->lpVtbl->Release(filedata);
4239 filedata = NULL;
4240 if (FAILED(hr))
4241 goto cleanup;
4244 enumobj->lpVtbl->Release(enumobj);
4245 enumobj = NULL;
4246 d3dxfile->lpVtbl->Release(d3dxfile);
4247 d3dxfile = NULL;
4249 if (list_empty(&container_list)) {
4250 hr = E_FAIL;
4251 goto cleanup;
4254 fvf = D3DFVF_XYZ;
4255 num_faces = 0;
4256 num_vertices = 0;
4257 num_materials = 0;
4258 LIST_FOR_EACH_ENTRY(container_ptr, &container_list, struct mesh_container, entry)
4260 ID3DXMesh *mesh = container_ptr->mesh;
4261 fvf |= mesh->lpVtbl->GetFVF(mesh);
4262 num_faces += mesh->lpVtbl->GetNumFaces(mesh);
4263 num_vertices += mesh->lpVtbl->GetNumVertices(mesh);
4264 num_materials += container_ptr->num_materials;
4267 hr = D3DXCreateMeshFVF(num_faces, num_vertices, options, fvf, device, &concat_mesh);
4268 if (FAILED(hr)) goto cleanup;
4270 hr = concat_mesh->lpVtbl->GetDeclaration(concat_mesh, concat_decl);
4271 if (FAILED(hr)) goto cleanup;
4273 concat_vertex_size = D3DXGetDeclVertexSize(concat_decl, 0);
4275 hr = concat_mesh->lpVtbl->LockVertexBuffer(concat_mesh, 0, (void**)&concat_vertices);
4276 if (FAILED(hr)) goto cleanup;
4278 LIST_FOR_EACH_ENTRY(container_ptr, &container_list, struct mesh_container, entry)
4280 D3DVERTEXELEMENT9 mesh_decl[MAX_FVF_DECL_SIZE];
4281 ID3DXMesh *mesh = container_ptr->mesh;
4282 DWORD num_mesh_vertices = mesh->lpVtbl->GetNumVertices(mesh);
4283 DWORD mesh_vertex_size;
4284 const BYTE *mesh_vertices;
4285 DWORD i;
4287 hr = mesh->lpVtbl->GetDeclaration(mesh, mesh_decl);
4288 if (FAILED(hr)) goto cleanup;
4290 mesh_vertex_size = D3DXGetDeclVertexSize(mesh_decl, 0);
4292 hr = mesh->lpVtbl->LockVertexBuffer(mesh, D3DLOCK_READONLY, (void**)&mesh_vertices);
4293 if (FAILED(hr)) goto cleanup;
4295 for (i = 0; i < num_mesh_vertices; i++) {
4296 int j;
4297 int k = 1;
4299 D3DXVec3TransformCoord((D3DXVECTOR3*)concat_vertices,
4300 (D3DXVECTOR3*)mesh_vertices,
4301 &container_ptr->transform);
4302 for (j = 1; concat_decl[j].Stream != 0xff; j++)
4304 if (concat_decl[j].Usage == mesh_decl[k].Usage &&
4305 concat_decl[j].UsageIndex == mesh_decl[k].UsageIndex)
4307 if (concat_decl[j].Usage == D3DDECLUSAGE_NORMAL) {
4308 D3DXVec3TransformCoord((D3DXVECTOR3*)(concat_vertices + concat_decl[j].Offset),
4309 (D3DXVECTOR3*)(mesh_vertices + mesh_decl[k].Offset),
4310 &container_ptr->transform);
4311 } else {
4312 memcpy(concat_vertices + concat_decl[j].Offset,
4313 mesh_vertices + mesh_decl[k].Offset,
4314 d3dx_decltype_size[mesh_decl[k].Type]);
4316 k++;
4319 mesh_vertices += mesh_vertex_size;
4320 concat_vertices += concat_vertex_size;
4323 mesh->lpVtbl->UnlockVertexBuffer(mesh);
4326 concat_mesh->lpVtbl->UnlockVertexBuffer(concat_mesh);
4327 concat_vertices = NULL;
4329 hr = concat_mesh->lpVtbl->LockIndexBuffer(concat_mesh, 0, &concat_indices);
4330 if (FAILED(hr)) goto cleanup;
4332 index_offset = 0;
4333 LIST_FOR_EACH_ENTRY(container_ptr, &container_list, struct mesh_container, entry)
4335 ID3DXMesh *mesh = container_ptr->mesh;
4336 const void *mesh_indices;
4337 DWORD num_mesh_faces = mesh->lpVtbl->GetNumFaces(mesh);
4338 DWORD i;
4340 hr = mesh->lpVtbl->LockIndexBuffer(mesh, D3DLOCK_READONLY, (void**)&mesh_indices);
4341 if (FAILED(hr)) goto cleanup;
4343 if (options & D3DXMESH_32BIT) {
4344 DWORD *dest = concat_indices;
4345 const DWORD *src = mesh_indices;
4346 for (i = 0; i < num_mesh_faces * 3; i++)
4347 *dest++ = index_offset + *src++;
4348 concat_indices = dest;
4349 } else {
4350 WORD *dest = concat_indices;
4351 const WORD *src = mesh_indices;
4352 for (i = 0; i < num_mesh_faces * 3; i++)
4353 *dest++ = index_offset + *src++;
4354 concat_indices = dest;
4356 mesh->lpVtbl->UnlockIndexBuffer(mesh);
4358 index_offset += num_mesh_faces * 3;
4361 concat_mesh->lpVtbl->UnlockIndexBuffer(concat_mesh);
4362 concat_indices = NULL;
4364 if (num_materials) {
4365 DWORD *concat_attrib_buffer = NULL;
4366 DWORD offset = 0;
4368 hr = concat_mesh->lpVtbl->LockAttributeBuffer(concat_mesh, 0, &concat_attrib_buffer);
4369 if (FAILED(hr)) goto cleanup;
4371 LIST_FOR_EACH_ENTRY(container_ptr, &container_list, struct mesh_container, entry)
4373 ID3DXMesh *mesh = container_ptr->mesh;
4374 const DWORD *mesh_attrib_buffer = NULL;
4375 DWORD count = mesh->lpVtbl->GetNumFaces(mesh);
4377 hr = mesh->lpVtbl->LockAttributeBuffer(mesh, D3DLOCK_READONLY, (DWORD**)&mesh_attrib_buffer);
4378 if (FAILED(hr)) {
4379 concat_mesh->lpVtbl->UnlockAttributeBuffer(concat_mesh);
4380 goto cleanup;
4383 while (count--)
4384 *concat_attrib_buffer++ = offset + *mesh_attrib_buffer++;
4386 mesh->lpVtbl->UnlockAttributeBuffer(mesh);
4387 offset += container_ptr->num_materials;
4389 concat_mesh->lpVtbl->UnlockAttributeBuffer(concat_mesh);
4392 if (materials_out || effects_out) {
4393 D3DXMATERIAL *out_ptr;
4394 if (!num_materials) {
4395 /* create default material */
4396 hr = D3DXCreateBuffer(sizeof(D3DXMATERIAL), &materials);
4397 if (FAILED(hr)) goto cleanup;
4399 out_ptr = ID3DXBuffer_GetBufferPointer(materials);
4400 out_ptr->MatD3D.Diffuse.r = 0.5f;
4401 out_ptr->MatD3D.Diffuse.g = 0.5f;
4402 out_ptr->MatD3D.Diffuse.b = 0.5f;
4403 out_ptr->MatD3D.Specular.r = 0.5f;
4404 out_ptr->MatD3D.Specular.g = 0.5f;
4405 out_ptr->MatD3D.Specular.b = 0.5f;
4406 /* D3DXCreateBuffer initializes the rest to zero */
4407 } else {
4408 DWORD buffer_size = num_materials * sizeof(D3DXMATERIAL);
4409 char *strings_out_ptr;
4411 LIST_FOR_EACH_ENTRY(container_ptr, &container_list, struct mesh_container, entry)
4413 if (container_ptr->materials) {
4414 DWORD i;
4415 const D3DXMATERIAL *in_ptr = ID3DXBuffer_GetBufferPointer(container_ptr->materials);
4416 for (i = 0; i < container_ptr->num_materials; i++)
4418 if (in_ptr->pTextureFilename)
4419 buffer_size += strlen(in_ptr->pTextureFilename) + 1;
4420 in_ptr++;
4425 hr = D3DXCreateBuffer(buffer_size, &materials);
4426 if (FAILED(hr)) goto cleanup;
4427 out_ptr = ID3DXBuffer_GetBufferPointer(materials);
4428 strings_out_ptr = (char*)(out_ptr + num_materials);
4430 LIST_FOR_EACH_ENTRY(container_ptr, &container_list, struct mesh_container, entry)
4432 if (container_ptr->materials) {
4433 DWORD i;
4434 const D3DXMATERIAL *in_ptr = ID3DXBuffer_GetBufferPointer(container_ptr->materials);
4435 for (i = 0; i < container_ptr->num_materials; i++)
4437 out_ptr->MatD3D = in_ptr->MatD3D;
4438 if (in_ptr->pTextureFilename) {
4439 out_ptr->pTextureFilename = strings_out_ptr;
4440 strcpy(out_ptr->pTextureFilename, in_ptr->pTextureFilename);
4441 strings_out_ptr += strlen(in_ptr->pTextureFilename) + 1;
4443 in_ptr++;
4444 out_ptr++;
4450 if (!num_materials)
4451 num_materials = 1;
4453 if (effects_out) {
4454 generate_effects(materials, num_materials, &effects);
4455 if (!materials_out) {
4456 ID3DXBuffer_Release(materials);
4457 materials = NULL;
4461 if (adjacency_out) {
4462 if (!list_next(&container_list, list_head(&container_list))) {
4463 container_ptr = LIST_ENTRY(list_head(&container_list), struct mesh_container, entry);
4464 adjacency = container_ptr->adjacency;
4465 container_ptr->adjacency = NULL;
4466 } else {
4467 DWORD offset = 0;
4468 DWORD *out_ptr;
4470 hr = D3DXCreateBuffer(num_faces * 3 * sizeof(DWORD), &adjacency);
4471 if (FAILED(hr)) goto cleanup;
4473 out_ptr = ID3DXBuffer_GetBufferPointer(adjacency);
4474 LIST_FOR_EACH_ENTRY(container_ptr, &container_list, struct mesh_container, entry)
4476 DWORD i;
4477 DWORD count = 3 * container_ptr->mesh->lpVtbl->GetNumFaces(container_ptr->mesh);
4478 DWORD *in_ptr = ID3DXBuffer_GetBufferPointer(container_ptr->adjacency);
4480 for (i = 0; i < count; i++)
4481 *out_ptr++ = offset + *in_ptr++;
4483 offset += count;
4488 *mesh_out = concat_mesh;
4489 if (adjacency_out) *adjacency_out = adjacency;
4490 if (materials_out) *materials_out = materials;
4491 if (effects_out) *effects_out = effects;
4492 if (num_materials_out) *num_materials_out = num_materials;
4494 hr = D3D_OK;
4495 cleanup:
4496 if (concat_indices) concat_mesh->lpVtbl->UnlockIndexBuffer(concat_mesh);
4497 if (concat_vertices) concat_mesh->lpVtbl->UnlockVertexBuffer(concat_mesh);
4498 if (filedata) filedata->lpVtbl->Release(filedata);
4499 if (enumobj) enumobj->lpVtbl->Release(enumobj);
4500 if (d3dxfile) d3dxfile->lpVtbl->Release(d3dxfile);
4501 if (FAILED(hr)) {
4502 if (concat_mesh) IUnknown_Release(concat_mesh);
4503 if (materials) ID3DXBuffer_Release(materials);
4504 if (effects) ID3DXBuffer_Release(effects);
4505 if (adjacency) ID3DXBuffer_Release(adjacency);
4507 LIST_FOR_EACH_ENTRY_SAFE(container_ptr, next_container_ptr, &container_list, struct mesh_container, entry)
4509 if (container_ptr->mesh) IUnknown_Release(container_ptr->mesh);
4510 if (container_ptr->adjacency) ID3DXBuffer_Release(container_ptr->adjacency);
4511 if (container_ptr->materials) ID3DXBuffer_Release(container_ptr->materials);
4512 if (container_ptr->effects) ID3DXBuffer_Release(container_ptr->effects);
4513 HeapFree(GetProcessHeap(), 0, container_ptr);
4515 return hr;
4518 HRESULT WINAPI D3DXCreateBox(struct IDirect3DDevice9 *device, float width, float height,
4519 float depth, struct ID3DXMesh **mesh, struct ID3DXBuffer **adjacency)
4521 FIXME("(%p, %f, %f, %f, %p, %p): stub\n", device, width, height, depth, mesh, adjacency);
4523 return E_NOTIMPL;
4526 struct vertex
4528 D3DXVECTOR3 position;
4529 D3DXVECTOR3 normal;
4532 typedef WORD face[3];
4534 struct sincos_table
4536 float *sin;
4537 float *cos;
4540 static void free_sincos_table(struct sincos_table *sincos_table)
4542 HeapFree(GetProcessHeap(), 0, sincos_table->cos);
4543 HeapFree(GetProcessHeap(), 0, sincos_table->sin);
4546 /* pre compute sine and cosine tables; caller must free */
4547 static BOOL compute_sincos_table(struct sincos_table *sincos_table, float angle_start, float angle_step, int n)
4549 float angle;
4550 int i;
4552 sincos_table->sin = HeapAlloc(GetProcessHeap(), 0, n * sizeof(*sincos_table->sin));
4553 if (!sincos_table->sin)
4555 return FALSE;
4557 sincos_table->cos = HeapAlloc(GetProcessHeap(), 0, n * sizeof(*sincos_table->cos));
4558 if (!sincos_table->cos)
4560 HeapFree(GetProcessHeap(), 0, sincos_table->sin);
4561 return FALSE;
4564 angle = angle_start;
4565 for (i = 0; i < n; i++)
4567 sincos_table->sin[i] = sin(angle);
4568 sincos_table->cos[i] = cos(angle);
4569 angle += angle_step;
4572 return TRUE;
4575 static WORD vertex_index(UINT slices, int slice, int stack)
4577 return stack*slices+slice+1;
4580 HRESULT WINAPI D3DXCreateSphere(struct IDirect3DDevice9 *device, float radius, UINT slices,
4581 UINT stacks, struct ID3DXMesh **mesh, struct ID3DXBuffer **adjacency)
4583 DWORD number_of_vertices, number_of_faces;
4584 HRESULT hr;
4585 ID3DXMesh *sphere;
4586 struct vertex *vertices;
4587 face *faces;
4588 float phi_step, phi_start;
4589 struct sincos_table phi;
4590 float theta_step, theta, sin_theta, cos_theta;
4591 DWORD vertex, face, stack, slice;
4593 TRACE("(%p, %f, %u, %u, %p, %p)\n", device, radius, slices, stacks, mesh, adjacency);
4595 if (!device || radius < 0.0f || slices < 2 || stacks < 2 || !mesh)
4597 return D3DERR_INVALIDCALL;
4600 if (adjacency)
4602 FIXME("Case of adjacency != NULL not implemented.\n");
4603 return E_NOTIMPL;
4606 number_of_vertices = 2 + slices * (stacks-1);
4607 number_of_faces = 2 * slices + (stacks - 2) * (2 * slices);
4609 hr = D3DXCreateMeshFVF(number_of_faces, number_of_vertices, D3DXMESH_MANAGED,
4610 D3DFVF_XYZ | D3DFVF_NORMAL, device, &sphere);
4611 if (FAILED(hr))
4613 return hr;
4616 hr = sphere->lpVtbl->LockVertexBuffer(sphere, 0, (LPVOID *)&vertices);
4617 if (FAILED(hr))
4619 sphere->lpVtbl->Release(sphere);
4620 return hr;
4623 hr = sphere->lpVtbl->LockIndexBuffer(sphere, 0, (LPVOID *)&faces);
4624 if (FAILED(hr))
4626 sphere->lpVtbl->UnlockVertexBuffer(sphere);
4627 sphere->lpVtbl->Release(sphere);
4628 return hr;
4631 /* phi = angle on xz plane wrt z axis */
4632 phi_step = -2 * M_PI / slices;
4633 phi_start = M_PI / 2;
4635 if (!compute_sincos_table(&phi, phi_start, phi_step, slices))
4637 sphere->lpVtbl->UnlockIndexBuffer(sphere);
4638 sphere->lpVtbl->UnlockVertexBuffer(sphere);
4639 sphere->lpVtbl->Release(sphere);
4640 return E_OUTOFMEMORY;
4643 /* theta = angle on xy plane wrt x axis */
4644 theta_step = M_PI / stacks;
4645 theta = theta_step;
4647 vertex = 0;
4648 face = 0;
4650 vertices[vertex].normal.x = 0.0f;
4651 vertices[vertex].normal.y = 0.0f;
4652 vertices[vertex].normal.z = 1.0f;
4653 vertices[vertex].position.x = 0.0f;
4654 vertices[vertex].position.y = 0.0f;
4655 vertices[vertex].position.z = radius;
4656 vertex++;
4658 for (stack = 0; stack < stacks - 1; stack++)
4660 sin_theta = sin(theta);
4661 cos_theta = cos(theta);
4663 for (slice = 0; slice < slices; slice++)
4665 vertices[vertex].normal.x = sin_theta * phi.cos[slice];
4666 vertices[vertex].normal.y = sin_theta * phi.sin[slice];
4667 vertices[vertex].normal.z = cos_theta;
4668 vertices[vertex].position.x = radius * sin_theta * phi.cos[slice];
4669 vertices[vertex].position.y = radius * sin_theta * phi.sin[slice];
4670 vertices[vertex].position.z = radius * cos_theta;
4671 vertex++;
4673 if (slice > 0)
4675 if (stack == 0)
4677 /* top stack is triangle fan */
4678 faces[face][0] = 0;
4679 faces[face][1] = slice + 1;
4680 faces[face][2] = slice;
4681 face++;
4683 else
4685 /* stacks in between top and bottom are quad strips */
4686 faces[face][0] = vertex_index(slices, slice-1, stack-1);
4687 faces[face][1] = vertex_index(slices, slice, stack-1);
4688 faces[face][2] = vertex_index(slices, slice-1, stack);
4689 face++;
4691 faces[face][0] = vertex_index(slices, slice, stack-1);
4692 faces[face][1] = vertex_index(slices, slice, stack);
4693 faces[face][2] = vertex_index(slices, slice-1, stack);
4694 face++;
4699 theta += theta_step;
4701 if (stack == 0)
4703 faces[face][0] = 0;
4704 faces[face][1] = 1;
4705 faces[face][2] = slice;
4706 face++;
4708 else
4710 faces[face][0] = vertex_index(slices, slice-1, stack-1);
4711 faces[face][1] = vertex_index(slices, 0, stack-1);
4712 faces[face][2] = vertex_index(slices, slice-1, stack);
4713 face++;
4715 faces[face][0] = vertex_index(slices, 0, stack-1);
4716 faces[face][1] = vertex_index(slices, 0, stack);
4717 faces[face][2] = vertex_index(slices, slice-1, stack);
4718 face++;
4722 vertices[vertex].position.x = 0.0f;
4723 vertices[vertex].position.y = 0.0f;
4724 vertices[vertex].position.z = -radius;
4725 vertices[vertex].normal.x = 0.0f;
4726 vertices[vertex].normal.y = 0.0f;
4727 vertices[vertex].normal.z = -1.0f;
4729 /* bottom stack is triangle fan */
4730 for (slice = 1; slice < slices; slice++)
4732 faces[face][0] = vertex_index(slices, slice-1, stack-1);
4733 faces[face][1] = vertex_index(slices, slice, stack-1);
4734 faces[face][2] = vertex;
4735 face++;
4738 faces[face][0] = vertex_index(slices, slice-1, stack-1);
4739 faces[face][1] = vertex_index(slices, 0, stack-1);
4740 faces[face][2] = vertex;
4742 free_sincos_table(&phi);
4743 sphere->lpVtbl->UnlockIndexBuffer(sphere);
4744 sphere->lpVtbl->UnlockVertexBuffer(sphere);
4745 *mesh = sphere;
4747 return D3D_OK;
4750 HRESULT WINAPI D3DXCreateCylinder(struct IDirect3DDevice9 *device, float radius1, float radius2,
4751 float length, UINT slices, UINT stacks, struct ID3DXMesh **mesh, struct ID3DXBuffer **adjacency)
4753 DWORD number_of_vertices, number_of_faces;
4754 HRESULT hr;
4755 ID3DXMesh *cylinder;
4756 struct vertex *vertices;
4757 face *faces;
4758 float theta_step, theta_start;
4759 struct sincos_table theta;
4760 float delta_radius, radius, radius_step;
4761 float z, z_step, z_normal;
4762 DWORD vertex, face, slice, stack;
4764 TRACE("(%p, %f, %f, %f, %u, %u, %p, %p)\n", device, radius1, radius2, length, slices, stacks, mesh, adjacency);
4766 if (device == NULL || radius1 < 0.0f || radius2 < 0.0f || length < 0.0f || slices < 2 || stacks < 1 || mesh == NULL)
4768 return D3DERR_INVALIDCALL;
4771 if (adjacency)
4773 FIXME("Case of adjacency != NULL not implemented.\n");
4774 return E_NOTIMPL;
4777 number_of_vertices = 2 + (slices * (3 + stacks));
4778 number_of_faces = 2 * slices + stacks * (2 * slices);
4780 hr = D3DXCreateMeshFVF(number_of_faces, number_of_vertices, D3DXMESH_MANAGED,
4781 D3DFVF_XYZ | D3DFVF_NORMAL, device, &cylinder);
4782 if (FAILED(hr))
4784 return hr;
4787 hr = cylinder->lpVtbl->LockVertexBuffer(cylinder, 0, (LPVOID *)&vertices);
4788 if (FAILED(hr))
4790 cylinder->lpVtbl->Release(cylinder);
4791 return hr;
4794 hr = cylinder->lpVtbl->LockIndexBuffer(cylinder, 0, (LPVOID *)&faces);
4795 if (FAILED(hr))
4797 cylinder->lpVtbl->UnlockVertexBuffer(cylinder);
4798 cylinder->lpVtbl->Release(cylinder);
4799 return hr;
4802 /* theta = angle on xy plane wrt x axis */
4803 theta_step = -2 * M_PI / slices;
4804 theta_start = M_PI / 2;
4806 if (!compute_sincos_table(&theta, theta_start, theta_step, slices))
4808 cylinder->lpVtbl->UnlockIndexBuffer(cylinder);
4809 cylinder->lpVtbl->UnlockVertexBuffer(cylinder);
4810 cylinder->lpVtbl->Release(cylinder);
4811 return E_OUTOFMEMORY;
4814 vertex = 0;
4815 face = 0;
4817 delta_radius = radius1 - radius2;
4818 radius = radius1;
4819 radius_step = delta_radius / stacks;
4821 z = -length / 2;
4822 z_step = length / stacks;
4823 z_normal = delta_radius / length;
4824 if (isnan(z_normal))
4826 z_normal = 0.0f;
4829 vertices[vertex].normal.x = 0.0f;
4830 vertices[vertex].normal.y = 0.0f;
4831 vertices[vertex].normal.z = -1.0f;
4832 vertices[vertex].position.x = 0.0f;
4833 vertices[vertex].position.y = 0.0f;
4834 vertices[vertex++].position.z = z;
4836 for (slice = 0; slice < slices; slice++, vertex++)
4838 vertices[vertex].normal.x = 0.0f;
4839 vertices[vertex].normal.y = 0.0f;
4840 vertices[vertex].normal.z = -1.0f;
4841 vertices[vertex].position.x = radius * theta.cos[slice];
4842 vertices[vertex].position.y = radius * theta.sin[slice];
4843 vertices[vertex].position.z = z;
4845 if (slice > 0)
4847 faces[face][0] = 0;
4848 faces[face][1] = slice;
4849 faces[face++][2] = slice + 1;
4853 faces[face][0] = 0;
4854 faces[face][1] = slice;
4855 faces[face++][2] = 1;
4857 for (stack = 1; stack <= stacks+1; stack++)
4859 for (slice = 0; slice < slices; slice++, vertex++)
4861 vertices[vertex].normal.x = theta.cos[slice];
4862 vertices[vertex].normal.y = theta.sin[slice];
4863 vertices[vertex].normal.z = z_normal;
4864 D3DXVec3Normalize(&vertices[vertex].normal, &vertices[vertex].normal);
4865 vertices[vertex].position.x = radius * theta.cos[slice];
4866 vertices[vertex].position.y = radius * theta.sin[slice];
4867 vertices[vertex].position.z = z;
4869 if (stack > 1 && slice > 0)
4871 faces[face][0] = vertex_index(slices, slice-1, stack-1);
4872 faces[face][1] = vertex_index(slices, slice-1, stack);
4873 faces[face++][2] = vertex_index(slices, slice, stack-1);
4875 faces[face][0] = vertex_index(slices, slice, stack-1);
4876 faces[face][1] = vertex_index(slices, slice-1, stack);
4877 faces[face++][2] = vertex_index(slices, slice, stack);
4881 if (stack > 1)
4883 faces[face][0] = vertex_index(slices, slice-1, stack-1);
4884 faces[face][1] = vertex_index(slices, slice-1, stack);
4885 faces[face++][2] = vertex_index(slices, 0, stack-1);
4887 faces[face][0] = vertex_index(slices, 0, stack-1);
4888 faces[face][1] = vertex_index(slices, slice-1, stack);
4889 faces[face++][2] = vertex_index(slices, 0, stack);
4892 if (stack < stacks + 1)
4894 z += z_step;
4895 radius -= radius_step;
4899 for (slice = 0; slice < slices; slice++, vertex++)
4901 vertices[vertex].normal.x = 0.0f;
4902 vertices[vertex].normal.y = 0.0f;
4903 vertices[vertex].normal.z = 1.0f;
4904 vertices[vertex].position.x = radius * theta.cos[slice];
4905 vertices[vertex].position.y = radius * theta.sin[slice];
4906 vertices[vertex].position.z = z;
4908 if (slice > 0)
4910 faces[face][0] = vertex_index(slices, slice-1, stack);
4911 faces[face][1] = number_of_vertices - 1;
4912 faces[face++][2] = vertex_index(slices, slice, stack);
4916 vertices[vertex].position.x = 0.0f;
4917 vertices[vertex].position.y = 0.0f;
4918 vertices[vertex].position.z = z;
4919 vertices[vertex].normal.x = 0.0f;
4920 vertices[vertex].normal.y = 0.0f;
4921 vertices[vertex].normal.z = 1.0f;
4923 faces[face][0] = vertex_index(slices, slice-1, stack);
4924 faces[face][1] = number_of_vertices - 1;
4925 faces[face][2] = vertex_index(slices, 0, stack);
4927 free_sincos_table(&theta);
4928 cylinder->lpVtbl->UnlockIndexBuffer(cylinder);
4929 cylinder->lpVtbl->UnlockVertexBuffer(cylinder);
4930 *mesh = cylinder;
4932 return D3D_OK;
4935 HRESULT WINAPI D3DXCreateTeapot(struct IDirect3DDevice9 *device,
4936 struct ID3DXMesh **mesh, struct ID3DXBuffer **adjacency)
4938 FIXME("(%p, %p, %p): stub\n", device, mesh, adjacency);
4940 return E_NOTIMPL;
4943 HRESULT WINAPI D3DXCreateTextA(struct IDirect3DDevice9 *device, HDC hdc, const char *text, float deviation,
4944 float extrusion, struct ID3DXMesh **mesh, struct ID3DXBuffer **adjacency, GLYPHMETRICSFLOAT *glyphmetrics)
4946 HRESULT hr;
4947 int len;
4948 LPWSTR textW;
4950 TRACE("(%p, %p, %s, %f, %f, %p, %p, %p)\n", device, hdc,
4951 debugstr_a(text), deviation, extrusion, mesh, adjacency, glyphmetrics);
4953 if (!text)
4954 return D3DERR_INVALIDCALL;
4956 len = MultiByteToWideChar(CP_ACP, 0, text, -1, NULL, 0);
4957 textW = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
4958 MultiByteToWideChar(CP_ACP, 0, text, -1, textW, len);
4960 hr = D3DXCreateTextW(device, hdc, textW, deviation, extrusion,
4961 mesh, adjacency, glyphmetrics);
4962 HeapFree(GetProcessHeap(), 0, textW);
4964 return hr;
4967 enum pointtype {
4968 POINTTYPE_CURVE = 0,
4969 POINTTYPE_CORNER,
4970 POINTTYPE_CURVE_START,
4971 POINTTYPE_CURVE_END,
4972 POINTTYPE_CURVE_MIDDLE,
4975 struct point2d
4977 D3DXVECTOR2 pos;
4978 enum pointtype corner;
4981 struct dynamic_array
4983 int count, capacity;
4984 void *items;
4987 /* is a dynamic_array */
4988 struct outline
4990 int count, capacity;
4991 struct point2d *items;
4994 /* is a dynamic_array */
4995 struct outline_array
4997 int count, capacity;
4998 struct outline *items;
5001 struct face_array
5003 int count;
5004 face *items;
5007 struct point2d_index
5009 struct outline *outline;
5010 int vertex;
5013 struct point2d_index_array
5015 int count;
5016 struct point2d_index *items;
5019 struct glyphinfo
5021 struct outline_array outlines;
5022 struct face_array faces;
5023 struct point2d_index_array ordered_vertices;
5024 float offset_x;
5027 /* is an dynamic_array */
5028 struct word_array
5030 int count, capacity;
5031 WORD *items;
5034 /* complex polygons are split into monotone polygons, which have
5035 * at most 2 intersections with the vertical sweep line */
5036 struct triangulation
5038 struct word_array vertex_stack;
5039 BOOL last_on_top, merging;
5042 /* is an dynamic_array */
5043 struct triangulation_array
5045 int count, capacity;
5046 struct triangulation *items;
5048 struct glyphinfo *glyph;
5051 static BOOL reserve(struct dynamic_array *array, int count, int itemsize)
5053 if (count > array->capacity) {
5054 void *new_buffer;
5055 int new_capacity;
5056 if (array->items && array->capacity) {
5057 new_capacity = max(array->capacity * 2, count);
5058 new_buffer = HeapReAlloc(GetProcessHeap(), 0, array->items, new_capacity * itemsize);
5059 } else {
5060 new_capacity = max(16, count);
5061 new_buffer = HeapAlloc(GetProcessHeap(), 0, new_capacity * itemsize);
5063 if (!new_buffer)
5064 return FALSE;
5065 array->items = new_buffer;
5066 array->capacity = new_capacity;
5068 return TRUE;
5071 static struct point2d *add_points(struct outline *array, int num)
5073 struct point2d *item;
5075 if (!reserve((struct dynamic_array *)array, array->count + num, sizeof(array->items[0])))
5076 return NULL;
5078 item = &array->items[array->count];
5079 array->count += num;
5080 return item;
5083 static struct outline *add_outline(struct outline_array *array)
5085 struct outline *item;
5087 if (!reserve((struct dynamic_array *)array, array->count + 1, sizeof(array->items[0])))
5088 return NULL;
5090 item = &array->items[array->count++];
5091 ZeroMemory(item, sizeof(*item));
5092 return item;
5095 static inline face *add_face(struct face_array *array)
5097 return &array->items[array->count++];
5100 static struct triangulation *add_triangulation(struct triangulation_array *array)
5102 struct triangulation *item;
5104 if (!reserve((struct dynamic_array *)array, array->count + 1, sizeof(array->items[0])))
5105 return NULL;
5107 item = &array->items[array->count++];
5108 ZeroMemory(item, sizeof(*item));
5109 return item;
5112 static HRESULT add_vertex_index(struct word_array *array, WORD vertex_index)
5114 if (!reserve((struct dynamic_array *)array, array->count + 1, sizeof(array->items[0])))
5115 return E_OUTOFMEMORY;
5117 array->items[array->count++] = vertex_index;
5118 return S_OK;
5121 /* assume fixed point numbers can be converted to float point in place */
5122 C_ASSERT(sizeof(FIXED) == sizeof(float));
5123 C_ASSERT(sizeof(POINTFX) == sizeof(D3DXVECTOR2));
5125 static inline D3DXVECTOR2 *convert_fixed_to_float(POINTFX *pt, int count, float emsquare)
5127 D3DXVECTOR2 *ret = (D3DXVECTOR2*)pt;
5128 while (count--) {
5129 D3DXVECTOR2 *pt_flt = (D3DXVECTOR2*)pt;
5130 pt_flt->x = (pt->x.value + pt->x.fract / (float)0x10000) / emsquare;
5131 pt_flt->y = (pt->y.value + pt->y.fract / (float)0x10000) / emsquare;
5132 pt++;
5134 return ret;
5137 static HRESULT add_bezier_points(struct outline *outline, const D3DXVECTOR2 *p1,
5138 const D3DXVECTOR2 *p2, const D3DXVECTOR2 *p3,
5139 float max_deviation_sq)
5141 D3DXVECTOR2 split1 = {0, 0}, split2 = {0, 0}, middle, vec;
5142 float deviation_sq;
5144 D3DXVec2Scale(&split1, D3DXVec2Add(&split1, p1, p2), 0.5f);
5145 D3DXVec2Scale(&split2, D3DXVec2Add(&split2, p2, p3), 0.5f);
5146 D3DXVec2Scale(&middle, D3DXVec2Add(&middle, &split1, &split2), 0.5f);
5148 deviation_sq = D3DXVec2LengthSq(D3DXVec2Subtract(&vec, &middle, p2));
5149 if (deviation_sq < max_deviation_sq) {
5150 struct point2d *pt = add_points(outline, 1);
5151 if (!pt) return E_OUTOFMEMORY;
5152 pt->pos = *p2;
5153 pt->corner = POINTTYPE_CURVE;
5154 /* the end point is omitted because the end line merges into the next segment of
5155 * the split bezier curve, and the end of the split bezier curve is added outside
5156 * this recursive function. */
5157 } else {
5158 HRESULT hr = add_bezier_points(outline, p1, &split1, &middle, max_deviation_sq);
5159 if (hr != S_OK) return hr;
5160 hr = add_bezier_points(outline, &middle, &split2, p3, max_deviation_sq);
5161 if (hr != S_OK) return hr;
5164 return S_OK;
5167 static inline BOOL is_direction_similar(D3DXVECTOR2 *dir1, D3DXVECTOR2 *dir2, float cos_theta)
5169 /* dot product = cos(theta) */
5170 return D3DXVec2Dot(dir1, dir2) > cos_theta;
5173 static inline D3DXVECTOR2 *unit_vec2(D3DXVECTOR2 *dir, const D3DXVECTOR2 *pt1, const D3DXVECTOR2 *pt2)
5175 return D3DXVec2Normalize(D3DXVec2Subtract(dir, pt2, pt1), dir);
5178 struct cos_table
5180 float cos_half;
5181 float cos_45;
5182 float cos_90;
5185 static BOOL attempt_line_merge(struct outline *outline,
5186 int pt_index,
5187 const D3DXVECTOR2 *nextpt,
5188 BOOL to_curve,
5189 const struct cos_table *table)
5191 D3DXVECTOR2 curdir, lastdir;
5192 struct point2d *prevpt, *pt;
5193 BOOL ret = FALSE;
5195 pt = &outline->items[pt_index];
5196 pt_index = (pt_index - 1 + outline->count) % outline->count;
5197 prevpt = &outline->items[pt_index];
5199 if (to_curve)
5200 pt->corner = pt->corner != POINTTYPE_CORNER ? POINTTYPE_CURVE_MIDDLE : POINTTYPE_CURVE_START;
5202 if (outline->count < 2)
5203 return FALSE;
5205 /* remove last point if the next line continues the last line */
5206 unit_vec2(&lastdir, &prevpt->pos, &pt->pos);
5207 unit_vec2(&curdir, &pt->pos, nextpt);
5208 if (is_direction_similar(&lastdir, &curdir, table->cos_half))
5210 outline->count--;
5211 if (pt->corner == POINTTYPE_CURVE_END)
5212 prevpt->corner = pt->corner;
5213 if (prevpt->corner == POINTTYPE_CURVE_END && to_curve)
5214 prevpt->corner = POINTTYPE_CURVE_MIDDLE;
5215 pt = prevpt;
5217 ret = TRUE;
5218 if (outline->count < 2)
5219 return ret;
5221 pt_index = (pt_index - 1 + outline->count) % outline->count;
5222 prevpt = &outline->items[pt_index];
5223 unit_vec2(&lastdir, &prevpt->pos, &pt->pos);
5224 unit_vec2(&curdir, &pt->pos, nextpt);
5226 return ret;
5229 static HRESULT create_outline(struct glyphinfo *glyph, void *raw_outline, int datasize,
5230 float max_deviation_sq, float emsquare, const struct cos_table *cos_table)
5232 TTPOLYGONHEADER *header = (TTPOLYGONHEADER *)raw_outline;
5234 while ((char *)header < (char *)raw_outline + datasize)
5236 TTPOLYCURVE *curve = (TTPOLYCURVE *)(header + 1);
5237 struct point2d *lastpt, *pt;
5238 D3DXVECTOR2 lastdir;
5239 D3DXVECTOR2 *pt_flt;
5240 int j;
5241 struct outline *outline = add_outline(&glyph->outlines);
5243 if (!outline)
5244 return E_OUTOFMEMORY;
5246 pt = add_points(outline, 1);
5247 if (!pt)
5248 return E_OUTOFMEMORY;
5249 pt_flt = convert_fixed_to_float(&header->pfxStart, 1, emsquare);
5250 pt->pos = *pt_flt;
5251 pt->corner = POINTTYPE_CORNER;
5253 if (header->dwType != TT_POLYGON_TYPE)
5254 FIXME("Unknown header type %d\n", header->dwType);
5256 while ((char *)curve < (char *)header + header->cb)
5258 D3DXVECTOR2 bezier_start = outline->items[outline->count - 1].pos;
5259 BOOL to_curve = curve->wType != TT_PRIM_LINE && curve->cpfx > 1;
5260 unsigned int j2 = 0;
5262 if (!curve->cpfx) {
5263 curve = (TTPOLYCURVE *)&curve->apfx[curve->cpfx];
5264 continue;
5267 pt_flt = convert_fixed_to_float(curve->apfx, curve->cpfx, emsquare);
5269 attempt_line_merge(outline, outline->count - 1, &pt_flt[0], to_curve, cos_table);
5271 if (to_curve)
5273 HRESULT hr;
5274 int count = curve->cpfx;
5276 while (count > 2)
5278 D3DXVECTOR2 bezier_end;
5280 D3DXVec2Scale(&bezier_end, D3DXVec2Add(&bezier_end, &pt_flt[j2], &pt_flt[j2+1]), 0.5f);
5281 hr = add_bezier_points(outline, &bezier_start, &pt_flt[j2], &bezier_end, max_deviation_sq);
5282 if (hr != S_OK)
5283 return hr;
5284 bezier_start = bezier_end;
5285 count--;
5286 j2++;
5288 hr = add_bezier_points(outline, &bezier_start, &pt_flt[j2], &pt_flt[j2+1], max_deviation_sq);
5289 if (hr != S_OK)
5290 return hr;
5292 pt = add_points(outline, 1);
5293 if (!pt)
5294 return E_OUTOFMEMORY;
5295 j2++;
5296 pt->pos = pt_flt[j2];
5297 pt->corner = POINTTYPE_CURVE_END;
5298 } else {
5299 pt = add_points(outline, curve->cpfx);
5300 if (!pt)
5301 return E_OUTOFMEMORY;
5302 for (j2 = 0; j2 < curve->cpfx; j2++)
5304 pt->pos = pt_flt[j2];
5305 pt->corner = POINTTYPE_CORNER;
5306 pt++;
5310 curve = (TTPOLYCURVE *)&curve->apfx[curve->cpfx];
5313 /* remove last point if the next line continues the last line */
5314 if (outline->count >= 3) {
5315 BOOL to_curve;
5317 lastpt = &outline->items[outline->count - 1];
5318 pt = &outline->items[0];
5319 if (pt->pos.x == lastpt->pos.x && pt->pos.y == lastpt->pos.y) {
5320 if (lastpt->corner == POINTTYPE_CURVE_END)
5322 if (pt->corner == POINTTYPE_CURVE_START)
5323 pt->corner = POINTTYPE_CURVE_MIDDLE;
5324 else
5325 pt->corner = POINTTYPE_CURVE_END;
5327 outline->count--;
5328 lastpt = &outline->items[outline->count - 1];
5329 } else {
5330 /* outline closed with a line from end to start point */
5331 attempt_line_merge(outline, outline->count - 1, &pt->pos, FALSE, cos_table);
5333 lastpt = &outline->items[0];
5334 to_curve = lastpt->corner != POINTTYPE_CORNER && lastpt->corner != POINTTYPE_CURVE_END;
5335 if (lastpt->corner == POINTTYPE_CURVE_START)
5336 lastpt->corner = POINTTYPE_CORNER;
5337 pt = &outline->items[1];
5338 if (attempt_line_merge(outline, 0, &pt->pos, to_curve, cos_table))
5339 *lastpt = outline->items[outline->count];
5342 lastpt = &outline->items[outline->count - 1];
5343 pt = &outline->items[0];
5344 unit_vec2(&lastdir, &lastpt->pos, &pt->pos);
5345 for (j = 0; j < outline->count; j++)
5347 D3DXVECTOR2 curdir;
5349 lastpt = pt;
5350 pt = &outline->items[(j + 1) % outline->count];
5351 unit_vec2(&curdir, &lastpt->pos, &pt->pos);
5353 switch (lastpt->corner)
5355 case POINTTYPE_CURVE_START:
5356 case POINTTYPE_CURVE_END:
5357 if (!is_direction_similar(&lastdir, &curdir, cos_table->cos_45))
5358 lastpt->corner = POINTTYPE_CORNER;
5359 break;
5360 case POINTTYPE_CURVE_MIDDLE:
5361 if (!is_direction_similar(&lastdir, &curdir, cos_table->cos_90))
5362 lastpt->corner = POINTTYPE_CORNER;
5363 else
5364 lastpt->corner = POINTTYPE_CURVE;
5365 break;
5366 default:
5367 break;
5369 lastdir = curdir;
5372 header = (TTPOLYGONHEADER *)((char *)header + header->cb);
5374 return S_OK;
5377 /* Get the y-distance from a line to a point */
5378 static float get_line_to_point_y_distance(D3DXVECTOR2 *line_pt1,
5379 D3DXVECTOR2 *line_pt2,
5380 D3DXVECTOR2 *point)
5382 D3DXVECTOR2 line_vec = {0, 0};
5383 float line_pt_dx;
5384 float line_y;
5386 D3DXVec2Subtract(&line_vec, line_pt2, line_pt1);
5387 line_pt_dx = point->x - line_pt1->x;
5388 line_y = line_pt1->y + (line_vec.y * line_pt_dx) / line_vec.x;
5389 return point->y - line_y;
5392 static D3DXVECTOR2 *get_indexed_point(struct point2d_index *pt_idx)
5394 return &pt_idx->outline->items[pt_idx->vertex].pos;
5397 static D3DXVECTOR2 *get_ordered_vertex(struct glyphinfo *glyph, WORD index)
5399 return get_indexed_point(&glyph->ordered_vertices.items[index]);
5402 static void remove_triangulation(struct triangulation_array *array, struct triangulation *item)
5404 HeapFree(GetProcessHeap(), 0, item->vertex_stack.items);
5405 MoveMemory(item, item + 1, (char*)&array->items[array->count] - (char*)(item + 1));
5406 array->count--;
5409 static HRESULT triangulation_add_point(struct triangulation **t_ptr,
5410 struct triangulation_array *triangulations,
5411 WORD vtx_idx,
5412 BOOL to_top)
5414 struct glyphinfo *glyph = triangulations->glyph;
5415 struct triangulation *t = *t_ptr;
5416 HRESULT hr;
5417 face *face;
5418 int f1, f2;
5420 if (t->last_on_top) {
5421 f1 = 1;
5422 f2 = 2;
5423 } else {
5424 f1 = 2;
5425 f2 = 1;
5428 if (t->last_on_top != to_top && t->vertex_stack.count > 1) {
5429 /* consume all vertices on the stack */
5430 WORD last_pt = t->vertex_stack.items[0];
5431 int i;
5432 for (i = 1; i < t->vertex_stack.count; i++)
5434 face = add_face(&glyph->faces);
5435 if (!face) return E_OUTOFMEMORY;
5436 (*face)[0] = vtx_idx;
5437 (*face)[f1] = last_pt;
5438 (*face)[f2] = last_pt = t->vertex_stack.items[i];
5440 t->vertex_stack.items[0] = last_pt;
5441 t->vertex_stack.count = 1;
5442 } else if (t->vertex_stack.count > 1) {
5443 int i = t->vertex_stack.count - 1;
5444 D3DXVECTOR2 *point = get_ordered_vertex(glyph, vtx_idx);
5445 WORD top_idx = t->vertex_stack.items[i--];
5446 D3DXVECTOR2 *top_pt = get_ordered_vertex(glyph, top_idx);
5448 while (i >= 0)
5450 WORD prev_idx = t->vertex_stack.items[i--];
5451 D3DXVECTOR2 *prev_pt = get_ordered_vertex(glyph, prev_idx);
5453 if (prev_pt->x != top_pt->x &&
5454 ((to_top && get_line_to_point_y_distance(prev_pt, top_pt, point) > 0) ||
5455 (!to_top && get_line_to_point_y_distance(prev_pt, top_pt, point) < 0)))
5456 break;
5458 face = add_face(&glyph->faces);
5459 if (!face) return E_OUTOFMEMORY;
5460 (*face)[0] = vtx_idx;
5461 (*face)[f1] = prev_idx;
5462 (*face)[f2] = top_idx;
5464 top_pt = prev_pt;
5465 top_idx = prev_idx;
5466 t->vertex_stack.count--;
5469 t->last_on_top = to_top;
5471 hr = add_vertex_index(&t->vertex_stack, vtx_idx);
5473 if (hr == S_OK && t->merging) {
5474 struct triangulation *t2;
5476 t2 = to_top ? t - 1 : t + 1;
5477 t2->merging = FALSE;
5478 hr = triangulation_add_point(&t2, triangulations, vtx_idx, to_top);
5479 if (hr != S_OK) return hr;
5480 remove_triangulation(triangulations, t);
5481 if (t2 > t)
5482 t2--;
5483 *t_ptr = t2;
5485 return hr;
5488 /* check if the point is next on the outline for either the top or bottom */
5489 static D3DXVECTOR2 *triangulation_get_next_point(struct triangulation *t, struct glyphinfo *glyph, BOOL on_top)
5491 int i = t->last_on_top == on_top ? t->vertex_stack.count - 1 : 0;
5492 WORD idx = t->vertex_stack.items[i];
5493 struct point2d_index *pt_idx = &glyph->ordered_vertices.items[idx];
5494 struct outline *outline = pt_idx->outline;
5496 if (on_top)
5497 i = (pt_idx->vertex + outline->count - 1) % outline->count;
5498 else
5499 i = (pt_idx->vertex + 1) % outline->count;
5501 return &outline->items[i].pos;
5504 static int compare_vertex_indices(const void *a, const void *b)
5506 const struct point2d_index *idx1 = a, *idx2 = b;
5507 const D3DXVECTOR2 *p1 = &idx1->outline->items[idx1->vertex].pos;
5508 const D3DXVECTOR2 *p2 = &idx2->outline->items[idx2->vertex].pos;
5509 float diff = p1->x - p2->x;
5511 if (diff == 0.0f)
5512 diff = p1->y - p2->y;
5514 return diff == 0.0f ? 0 : (diff > 0.0f ? -1 : 1);
5517 static HRESULT triangulate(struct triangulation_array *triangulations)
5519 int sweep_idx;
5520 HRESULT hr;
5521 struct glyphinfo *glyph = triangulations->glyph;
5522 int nb_vertices = 0;
5523 int i;
5524 struct point2d_index *idx_ptr;
5526 for (i = 0; i < glyph->outlines.count; i++)
5527 nb_vertices += glyph->outlines.items[i].count;
5529 glyph->ordered_vertices.items = HeapAlloc(GetProcessHeap(), 0,
5530 nb_vertices * sizeof(*glyph->ordered_vertices.items));
5531 if (!glyph->ordered_vertices.items)
5532 return E_OUTOFMEMORY;
5534 idx_ptr = glyph->ordered_vertices.items;
5535 for (i = 0; i < glyph->outlines.count; i++)
5537 struct outline *outline = &glyph->outlines.items[i];
5538 int j;
5540 idx_ptr->outline = outline;
5541 idx_ptr->vertex = 0;
5542 idx_ptr++;
5543 for (j = outline->count - 1; j > 0; j--)
5545 idx_ptr->outline = outline;
5546 idx_ptr->vertex = j;
5547 idx_ptr++;
5550 glyph->ordered_vertices.count = nb_vertices;
5552 /* Native implementation seems to try to create a triangle fan from
5553 * the first outline point if the glyph only has one outline. */
5554 if (glyph->outlines.count == 1)
5556 struct outline *outline = glyph->outlines.items;
5557 D3DXVECTOR2 *base = &outline->items[0].pos;
5558 D3DXVECTOR2 *last = &outline->items[1].pos;
5559 float ccw = 0;
5561 for (i = 2; i < outline->count; i++)
5563 D3DXVECTOR2 *next = &outline->items[i].pos;
5564 D3DXVECTOR2 v1 = {0.0f, 0.0f};
5565 D3DXVECTOR2 v2 = {0.0f, 0.0f};
5567 D3DXVec2Subtract(&v1, base, last);
5568 D3DXVec2Subtract(&v2, last, next);
5569 ccw = D3DXVec2CCW(&v1, &v2);
5570 if (ccw > 0.0f)
5571 break;
5573 last = next;
5575 if (ccw <= 0)
5577 glyph->faces.items = HeapAlloc(GetProcessHeap(), 0,
5578 (outline->count - 2) * sizeof(glyph->faces.items[0]));
5579 if (!glyph->faces.items)
5580 return E_OUTOFMEMORY;
5582 glyph->faces.count = outline->count - 2;
5583 for (i = 0; i < glyph->faces.count; i++)
5585 glyph->faces.items[i][0] = 0;
5586 glyph->faces.items[i][1] = i + 1;
5587 glyph->faces.items[i][2] = i + 2;
5589 return S_OK;
5593 /* Perform 2D polygon triangulation for complex glyphs.
5594 * Triangulation is performed using a sweep line concept, from right to left,
5595 * by processing vertices in sorted order. Complex polygons are split into
5596 * monotone polygons which are triangulated separately. */
5597 /* FIXME: The order of the faces is not consistent with the native implementation. */
5599 /* Reserve space for maximum possible faces from triangulation.
5600 * # faces for outer outlines = outline->count - 2
5601 * # faces for inner outlines = outline->count + 2
5602 * There must be at least 1 outer outline. */
5603 glyph->faces.items = HeapAlloc(GetProcessHeap(), 0,
5604 (nb_vertices + glyph->outlines.count * 2 - 4) * sizeof(glyph->faces.items[0]));
5605 if (!glyph->faces.items)
5606 return E_OUTOFMEMORY;
5608 qsort(glyph->ordered_vertices.items, nb_vertices,
5609 sizeof(glyph->ordered_vertices.items[0]), compare_vertex_indices);
5610 for (sweep_idx = 0; sweep_idx < glyph->ordered_vertices.count; sweep_idx++)
5612 int start = 0;
5613 int end = triangulations->count;
5615 while (start < end)
5617 D3DXVECTOR2 *sweep_vtx = get_ordered_vertex(glyph, sweep_idx);
5618 int current = (start + end) / 2;
5619 struct triangulation *t = &triangulations->items[current];
5620 BOOL on_top_outline = FALSE;
5621 D3DXVECTOR2 *top_next, *bottom_next;
5622 WORD top_idx, bottom_idx;
5624 if (t->merging && t->last_on_top)
5625 top_next = triangulation_get_next_point(t + 1, glyph, TRUE);
5626 else
5627 top_next = triangulation_get_next_point(t, glyph, TRUE);
5628 if (sweep_vtx == top_next)
5630 if (t->merging && t->last_on_top)
5631 t++;
5632 hr = triangulation_add_point(&t, triangulations, sweep_idx, TRUE);
5633 if (hr != S_OK) return hr;
5635 if (t + 1 < &triangulations->items[triangulations->count] &&
5636 triangulation_get_next_point(t + 1, glyph, FALSE) == sweep_vtx)
5638 /* point also on bottom outline of higher triangulation */
5639 struct triangulation *t2 = t + 1;
5640 hr = triangulation_add_point(&t2, triangulations, sweep_idx, FALSE);
5641 if (hr != S_OK) return hr;
5643 t->merging = TRUE;
5644 t2->merging = TRUE;
5646 on_top_outline = TRUE;
5649 if (t->merging && !t->last_on_top)
5650 bottom_next = triangulation_get_next_point(t - 1, glyph, FALSE);
5651 else
5652 bottom_next = triangulation_get_next_point(t, glyph, FALSE);
5653 if (sweep_vtx == bottom_next)
5655 if (t->merging && !t->last_on_top)
5656 t--;
5657 if (on_top_outline) {
5658 /* outline finished */
5659 remove_triangulation(triangulations, t);
5660 break;
5663 hr = triangulation_add_point(&t, triangulations, sweep_idx, FALSE);
5664 if (hr != S_OK) return hr;
5666 if (t > triangulations->items &&
5667 triangulation_get_next_point(t - 1, glyph, TRUE) == sweep_vtx)
5669 struct triangulation *t2 = t - 1;
5670 /* point also on top outline of lower triangulation */
5671 hr = triangulation_add_point(&t2, triangulations, sweep_idx, TRUE);
5672 if (hr != S_OK) return hr;
5673 t = t2 + 1; /* t may be invalidated by triangulation merging */
5675 t->merging = TRUE;
5676 t2->merging = TRUE;
5678 break;
5680 if (on_top_outline)
5681 break;
5683 if (t->last_on_top) {
5684 top_idx = t->vertex_stack.items[t->vertex_stack.count - 1];
5685 bottom_idx = t->vertex_stack.items[0];
5686 } else {
5687 top_idx = t->vertex_stack.items[0];
5688 bottom_idx = t->vertex_stack.items[t->vertex_stack.count - 1];
5691 /* check if the point is inside or outside this polygon */
5692 if (get_line_to_point_y_distance(get_ordered_vertex(glyph, top_idx),
5693 top_next, sweep_vtx) > 0)
5694 { /* above */
5695 start = current + 1;
5696 } else if (get_line_to_point_y_distance(get_ordered_vertex(glyph, bottom_idx),
5697 bottom_next, sweep_vtx) < 0)
5698 { /* below */
5699 end = current;
5700 } else if (t->merging) {
5701 /* inside, so cancel merging */
5702 struct triangulation *t2 = t->last_on_top ? t + 1 : t - 1;
5703 t->merging = FALSE;
5704 t2->merging = FALSE;
5705 hr = triangulation_add_point(&t, triangulations, sweep_idx, t->last_on_top);
5706 if (hr != S_OK) return hr;
5707 hr = triangulation_add_point(&t2, triangulations, sweep_idx, t2->last_on_top);
5708 if (hr != S_OK) return hr;
5709 break;
5710 } else {
5711 /* inside, so split polygon into two monotone parts */
5712 struct triangulation *t2 = add_triangulation(triangulations);
5713 if (!t2) return E_OUTOFMEMORY;
5714 MoveMemory(t + 1, t, (char*)(t2 + 1) - (char*)t);
5715 if (t->last_on_top) {
5716 t2 = t + 1;
5717 } else {
5718 t2 = t;
5719 t++;
5722 ZeroMemory(&t2->vertex_stack, sizeof(t2->vertex_stack));
5723 hr = add_vertex_index(&t2->vertex_stack, t->vertex_stack.items[t->vertex_stack.count - 1]);
5724 if (hr != S_OK) return hr;
5725 hr = add_vertex_index(&t2->vertex_stack, sweep_idx);
5726 if (hr != S_OK) return hr;
5727 t2->last_on_top = !t->last_on_top;
5729 hr = triangulation_add_point(&t, triangulations, sweep_idx, t->last_on_top);
5730 if (hr != S_OK) return hr;
5731 break;
5734 if (start >= end)
5736 struct triangulation *t;
5737 struct triangulation *t2 = add_triangulation(triangulations);
5738 if (!t2) return E_OUTOFMEMORY;
5739 t = &triangulations->items[start];
5740 MoveMemory(t + 1, t, (char*)(t2 + 1) - (char*)t);
5741 ZeroMemory(t, sizeof(*t));
5742 hr = add_vertex_index(&t->vertex_stack, sweep_idx);
5743 if (hr != S_OK) return hr;
5746 return S_OK;
5749 HRESULT WINAPI D3DXCreateTextW(struct IDirect3DDevice9 *device, HDC hdc, const WCHAR *text, float deviation,
5750 float extrusion, struct ID3DXMesh **mesh_ptr, struct ID3DXBuffer **adjacency, GLYPHMETRICSFLOAT *glyphmetrics)
5752 HRESULT hr;
5753 ID3DXMesh *mesh = NULL;
5754 DWORD nb_vertices, nb_faces;
5755 DWORD nb_front_faces, nb_corners, nb_outline_points;
5756 struct vertex *vertices = NULL;
5757 face *faces = NULL;
5758 int textlen = 0;
5759 float offset_x;
5760 LOGFONTW lf;
5761 OUTLINETEXTMETRICW otm;
5762 HFONT font = NULL, oldfont = NULL;
5763 const MAT2 identity = {{0, 1}, {0, 0}, {0, 0}, {0, 1}};
5764 void *raw_outline = NULL;
5765 int bufsize = 0;
5766 struct glyphinfo *glyphs = NULL;
5767 GLYPHMETRICS gm;
5768 struct triangulation_array triangulations = {0, 0, NULL};
5769 int i;
5770 struct vertex *vertex_ptr;
5771 face *face_ptr;
5772 float max_deviation_sq;
5773 const struct cos_table cos_table = {
5774 cos(D3DXToRadian(0.5f)),
5775 cos(D3DXToRadian(45.0f)),
5776 cos(D3DXToRadian(90.0f)),
5778 int f1, f2;
5780 TRACE("(%p, %p, %s, %f, %f, %p, %p, %p)\n", device, hdc,
5781 debugstr_w(text), deviation, extrusion, mesh_ptr, adjacency, glyphmetrics);
5783 if (!device || !hdc || !text || !*text || deviation < 0.0f || extrusion < 0.0f || !mesh_ptr)
5784 return D3DERR_INVALIDCALL;
5786 if (adjacency)
5788 FIXME("Case of adjacency != NULL not implemented.\n");
5789 return E_NOTIMPL;
5792 if (!GetObjectW(GetCurrentObject(hdc, OBJ_FONT), sizeof(lf), &lf) ||
5793 !GetOutlineTextMetricsW(hdc, sizeof(otm), &otm))
5795 return D3DERR_INVALIDCALL;
5798 if (deviation == 0.0f)
5799 deviation = 1.0f / otm.otmEMSquare;
5800 max_deviation_sq = deviation * deviation;
5802 lf.lfHeight = otm.otmEMSquare;
5803 lf.lfWidth = 0;
5804 font = CreateFontIndirectW(&lf);
5805 if (!font) {
5806 hr = E_OUTOFMEMORY;
5807 goto error;
5809 oldfont = SelectObject(hdc, font);
5811 textlen = strlenW(text);
5812 for (i = 0; i < textlen; i++)
5814 int datasize = GetGlyphOutlineW(hdc, text[i], GGO_NATIVE, &gm, 0, NULL, &identity);
5815 if (datasize < 0)
5816 return D3DERR_INVALIDCALL;
5817 if (bufsize < datasize)
5818 bufsize = datasize;
5820 if (!bufsize) { /* e.g. text == " " */
5821 hr = D3DERR_INVALIDCALL;
5822 goto error;
5825 glyphs = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, textlen * sizeof(*glyphs));
5826 raw_outline = HeapAlloc(GetProcessHeap(), 0, bufsize);
5827 if (!glyphs || !raw_outline) {
5828 hr = E_OUTOFMEMORY;
5829 goto error;
5832 offset_x = 0.0f;
5833 for (i = 0; i < textlen; i++)
5835 /* get outline points from data returned from GetGlyphOutline */
5836 int datasize;
5838 glyphs[i].offset_x = offset_x;
5840 datasize = GetGlyphOutlineW(hdc, text[i], GGO_NATIVE, &gm, bufsize, raw_outline, &identity);
5841 hr = create_outline(&glyphs[i], raw_outline, datasize,
5842 max_deviation_sq, otm.otmEMSquare, &cos_table);
5843 if (hr != S_OK) goto error;
5845 triangulations.glyph = &glyphs[i];
5846 hr = triangulate(&triangulations);
5847 if (hr != S_OK) goto error;
5848 if (triangulations.count) {
5849 ERR("%d incomplete triangulations of glyph (%u).\n", triangulations.count, text[i]);
5850 triangulations.count = 0;
5853 if (glyphmetrics)
5855 glyphmetrics[i].gmfBlackBoxX = gm.gmBlackBoxX / (float)otm.otmEMSquare;
5856 glyphmetrics[i].gmfBlackBoxY = gm.gmBlackBoxY / (float)otm.otmEMSquare;
5857 glyphmetrics[i].gmfptGlyphOrigin.x = gm.gmptGlyphOrigin.x / (float)otm.otmEMSquare;
5858 glyphmetrics[i].gmfptGlyphOrigin.y = gm.gmptGlyphOrigin.y / (float)otm.otmEMSquare;
5859 glyphmetrics[i].gmfCellIncX = gm.gmCellIncX / (float)otm.otmEMSquare;
5860 glyphmetrics[i].gmfCellIncY = gm.gmCellIncY / (float)otm.otmEMSquare;
5862 offset_x += gm.gmCellIncX / (float)otm.otmEMSquare;
5865 /* corner points need an extra vertex for the different side faces normals */
5866 nb_corners = 0;
5867 nb_outline_points = 0;
5868 nb_front_faces = 0;
5869 for (i = 0; i < textlen; i++)
5871 int j;
5872 nb_outline_points += glyphs[i].ordered_vertices.count;
5873 nb_front_faces += glyphs[i].faces.count;
5874 for (j = 0; j < glyphs[i].outlines.count; j++)
5876 int k;
5877 struct outline *outline = &glyphs[i].outlines.items[j];
5878 nb_corners++; /* first outline point always repeated as a corner */
5879 for (k = 1; k < outline->count; k++)
5880 if (outline->items[k].corner)
5881 nb_corners++;
5885 nb_vertices = (nb_outline_points + nb_corners) * 2 + nb_outline_points * 2;
5886 nb_faces = nb_outline_points * 2 + nb_front_faces * 2;
5889 hr = D3DXCreateMeshFVF(nb_faces, nb_vertices, D3DXMESH_MANAGED,
5890 D3DFVF_XYZ | D3DFVF_NORMAL, device, &mesh);
5891 if (FAILED(hr))
5892 goto error;
5894 hr = mesh->lpVtbl->LockVertexBuffer(mesh, 0, (LPVOID *)&vertices);
5895 if (FAILED(hr))
5896 goto error;
5898 hr = mesh->lpVtbl->LockIndexBuffer(mesh, 0, (LPVOID *)&faces);
5899 if (FAILED(hr))
5900 goto error;
5902 /* convert 2D vertices and faces into 3D mesh */
5903 vertex_ptr = vertices;
5904 face_ptr = faces;
5905 if (extrusion == 0.0f) {
5906 f1 = 1;
5907 f2 = 2;
5908 } else {
5909 f1 = 2;
5910 f2 = 1;
5912 for (i = 0; i < textlen; i++)
5914 int j;
5915 int count;
5916 struct vertex *back_vertices;
5917 face *back_faces;
5919 /* side vertices and faces */
5920 for (j = 0; j < glyphs[i].outlines.count; j++)
5922 struct vertex *outline_vertices = vertex_ptr;
5923 struct outline *outline = &glyphs[i].outlines.items[j];
5924 int k;
5925 struct point2d *prevpt = &outline->items[outline->count - 1];
5926 struct point2d *pt = &outline->items[0];
5928 for (k = 1; k <= outline->count; k++)
5930 struct vertex vtx;
5931 struct point2d *nextpt = &outline->items[k % outline->count];
5932 WORD vtx_idx = vertex_ptr - vertices;
5933 D3DXVECTOR2 vec;
5935 if (pt->corner == POINTTYPE_CURVE_START)
5936 D3DXVec2Subtract(&vec, &pt->pos, &prevpt->pos);
5937 else if (pt->corner)
5938 D3DXVec2Subtract(&vec, &nextpt->pos, &pt->pos);
5939 else
5940 D3DXVec2Subtract(&vec, &nextpt->pos, &prevpt->pos);
5941 D3DXVec2Normalize(&vec, &vec);
5942 vtx.normal.x = -vec.y;
5943 vtx.normal.y = vec.x;
5944 vtx.normal.z = 0;
5946 vtx.position.x = pt->pos.x + glyphs[i].offset_x;
5947 vtx.position.y = pt->pos.y;
5948 vtx.position.z = 0;
5949 *vertex_ptr++ = vtx;
5951 vtx.position.z = -extrusion;
5952 *vertex_ptr++ = vtx;
5954 vtx.position.x = nextpt->pos.x + glyphs[i].offset_x;
5955 vtx.position.y = nextpt->pos.y;
5956 if (pt->corner && nextpt->corner && nextpt->corner != POINTTYPE_CURVE_END) {
5957 vtx.position.z = -extrusion;
5958 *vertex_ptr++ = vtx;
5959 vtx.position.z = 0;
5960 *vertex_ptr++ = vtx;
5962 (*face_ptr)[0] = vtx_idx;
5963 (*face_ptr)[1] = vtx_idx + 2;
5964 (*face_ptr)[2] = vtx_idx + 1;
5965 face_ptr++;
5967 (*face_ptr)[0] = vtx_idx;
5968 (*face_ptr)[1] = vtx_idx + 3;
5969 (*face_ptr)[2] = vtx_idx + 2;
5970 face_ptr++;
5971 } else {
5972 if (nextpt->corner) {
5973 if (nextpt->corner == POINTTYPE_CURVE_END) {
5974 D3DXVECTOR2 *nextpt2 = &outline->items[(k + 1) % outline->count].pos;
5975 D3DXVec2Subtract(&vec, nextpt2, &nextpt->pos);
5976 } else {
5977 D3DXVec2Subtract(&vec, &nextpt->pos, &pt->pos);
5979 D3DXVec2Normalize(&vec, &vec);
5980 vtx.normal.x = -vec.y;
5981 vtx.normal.y = vec.x;
5983 vtx.position.z = 0;
5984 *vertex_ptr++ = vtx;
5985 vtx.position.z = -extrusion;
5986 *vertex_ptr++ = vtx;
5989 (*face_ptr)[0] = vtx_idx;
5990 (*face_ptr)[1] = vtx_idx + 3;
5991 (*face_ptr)[2] = vtx_idx + 1;
5992 face_ptr++;
5994 (*face_ptr)[0] = vtx_idx;
5995 (*face_ptr)[1] = vtx_idx + 2;
5996 (*face_ptr)[2] = vtx_idx + 3;
5997 face_ptr++;
6000 prevpt = pt;
6001 pt = nextpt;
6003 if (!pt->corner) {
6004 *vertex_ptr++ = *outline_vertices++;
6005 *vertex_ptr++ = *outline_vertices++;
6009 /* back vertices and faces */
6010 back_faces = face_ptr;
6011 back_vertices = vertex_ptr;
6012 for (j = 0; j < glyphs[i].ordered_vertices.count; j++)
6014 D3DXVECTOR2 *pt = get_ordered_vertex(&glyphs[i], j);
6015 vertex_ptr->position.x = pt->x + glyphs[i].offset_x;
6016 vertex_ptr->position.y = pt->y;
6017 vertex_ptr->position.z = 0;
6018 vertex_ptr->normal.x = 0;
6019 vertex_ptr->normal.y = 0;
6020 vertex_ptr->normal.z = 1;
6021 vertex_ptr++;
6023 count = back_vertices - vertices;
6024 for (j = 0; j < glyphs[i].faces.count; j++)
6026 face *f = &glyphs[i].faces.items[j];
6027 (*face_ptr)[0] = (*f)[0] + count;
6028 (*face_ptr)[1] = (*f)[1] + count;
6029 (*face_ptr)[2] = (*f)[2] + count;
6030 face_ptr++;
6033 /* front vertices and faces */
6034 j = count = vertex_ptr - back_vertices;
6035 while (j--)
6037 vertex_ptr->position.x = back_vertices->position.x;
6038 vertex_ptr->position.y = back_vertices->position.y;
6039 vertex_ptr->position.z = -extrusion;
6040 vertex_ptr->normal.x = 0;
6041 vertex_ptr->normal.y = 0;
6042 vertex_ptr->normal.z = extrusion == 0.0f ? 1.0f : -1.0f;
6043 vertex_ptr++;
6044 back_vertices++;
6046 j = face_ptr - back_faces;
6047 while (j--)
6049 (*face_ptr)[0] = (*back_faces)[0] + count;
6050 (*face_ptr)[1] = (*back_faces)[f1] + count;
6051 (*face_ptr)[2] = (*back_faces)[f2] + count;
6052 face_ptr++;
6053 back_faces++;
6057 *mesh_ptr = mesh;
6058 hr = D3D_OK;
6059 error:
6060 if (mesh) {
6061 if (faces) mesh->lpVtbl->UnlockIndexBuffer(mesh);
6062 if (vertices) mesh->lpVtbl->UnlockVertexBuffer(mesh);
6063 if (hr != D3D_OK) mesh->lpVtbl->Release(mesh);
6065 if (glyphs) {
6066 for (i = 0; i < textlen; i++)
6068 int j;
6069 for (j = 0; j < glyphs[i].outlines.count; j++)
6070 HeapFree(GetProcessHeap(), 0, glyphs[i].outlines.items[j].items);
6071 HeapFree(GetProcessHeap(), 0, glyphs[i].outlines.items);
6072 HeapFree(GetProcessHeap(), 0, glyphs[i].faces.items);
6073 HeapFree(GetProcessHeap(), 0, glyphs[i].ordered_vertices.items);
6075 HeapFree(GetProcessHeap(), 0, glyphs);
6077 if (triangulations.items) {
6078 int i;
6079 for (i = 0; i < triangulations.count; i++)
6080 HeapFree(GetProcessHeap(), 0, triangulations.items[i].vertex_stack.items);
6081 HeapFree(GetProcessHeap(), 0, triangulations.items);
6083 HeapFree(GetProcessHeap(), 0, raw_outline);
6084 if (oldfont) SelectObject(hdc, oldfont);
6085 if (font) DeleteObject(font);
6087 return hr;
6090 HRESULT WINAPI D3DXValidMesh(ID3DXMesh *mesh, const DWORD *adjacency, ID3DXBuffer **errors_and_warnings)
6092 FIXME("(%p, %p, %p): stub\n", mesh, adjacency, *errors_and_warnings);
6094 return E_NOTIMPL;
6097 static BOOL weld_float1(void *to, void *from, FLOAT epsilon)
6099 FLOAT *v1 = to;
6100 FLOAT *v2 = from;
6102 if (fabsf(*v1 - *v2) <= epsilon)
6104 *v1 = *v2;
6106 return TRUE;
6109 return FALSE;
6112 static BOOL weld_float2(void *to, void *from, FLOAT epsilon)
6114 D3DXVECTOR2 *v1 = to;
6115 D3DXVECTOR2 *v2 = from;
6116 FLOAT diff_x = fabsf(v1->x - v2->x);
6117 FLOAT diff_y = fabsf(v1->y - v2->y);
6118 FLOAT max_abs_diff = max(diff_x, diff_y);
6120 if (max_abs_diff <= epsilon)
6122 memcpy(to, from, sizeof(D3DXVECTOR2));
6124 return TRUE;
6127 return FALSE;
6130 static BOOL weld_float3(void *to, void *from, FLOAT epsilon)
6132 D3DXVECTOR3 *v1 = to;
6133 D3DXVECTOR3 *v2 = from;
6134 FLOAT diff_x = fabsf(v1->x - v2->x);
6135 FLOAT diff_y = fabsf(v1->y - v2->y);
6136 FLOAT diff_z = fabsf(v1->z - v2->z);
6137 FLOAT max_abs_diff = max(diff_x, diff_y);
6138 max_abs_diff = max(diff_z, max_abs_diff);
6140 if (max_abs_diff <= epsilon)
6142 memcpy(to, from, sizeof(D3DXVECTOR3));
6144 return TRUE;
6147 return FALSE;
6150 static BOOL weld_float4(void *to, void *from, FLOAT epsilon)
6152 D3DXVECTOR4 *v1 = to;
6153 D3DXVECTOR4 *v2 = from;
6154 FLOAT diff_x = fabsf(v1->x - v2->x);
6155 FLOAT diff_y = fabsf(v1->y - v2->y);
6156 FLOAT diff_z = fabsf(v1->z - v2->z);
6157 FLOAT diff_w = fabsf(v1->w - v2->w);
6158 FLOAT max_abs_diff = fmax(diff_x, diff_y);
6159 max_abs_diff = max(diff_z, max_abs_diff);
6160 max_abs_diff = max(diff_w, max_abs_diff);
6162 if (max_abs_diff <= epsilon)
6164 memcpy(to, from, sizeof(D3DXVECTOR4));
6166 return TRUE;
6169 return FALSE;
6172 static BOOL weld_ubyte4(void *to, void *from, FLOAT epsilon)
6174 BYTE *b1 = to;
6175 BYTE *b2 = from;
6176 BYTE truncated_epsilon = (BYTE)epsilon;
6177 BYTE diff_x = b1[0] > b2[0] ? b1[0] - b2[0] : b2[0] - b1[0];
6178 BYTE diff_y = b1[1] > b2[1] ? b1[1] - b2[1] : b2[1] - b1[1];
6179 BYTE diff_z = b1[2] > b2[2] ? b1[2] - b2[2] : b2[2] - b1[2];
6180 BYTE diff_w = b1[3] > b2[3] ? b1[3] - b2[3] : b2[3] - b1[3];
6181 BYTE max_diff = max(diff_x, diff_y);
6182 max_diff = max(diff_z, max_diff);
6183 max_diff = max(diff_w, max_diff);
6185 if (max_diff <= truncated_epsilon)
6187 memcpy(to, from, 4 * sizeof(BYTE));
6189 return TRUE;
6192 return FALSE;
6195 static BOOL weld_ubyte4n(void *to, void *from, FLOAT epsilon)
6197 return weld_ubyte4(to, from, epsilon * UCHAR_MAX);
6200 static BOOL weld_d3dcolor(void *to, void *from, FLOAT epsilon)
6202 return weld_ubyte4n(to, from, epsilon);
6205 static BOOL weld_short2(void *to, void *from, FLOAT epsilon)
6207 SHORT *s1 = to;
6208 SHORT *s2 = from;
6209 SHORT truncated_epsilon = (SHORT)epsilon;
6210 SHORT diff_x = abs(s1[0] - s2[0]);
6211 SHORT diff_y = abs(s1[1] - s2[1]);
6212 SHORT max_abs_diff = max(diff_x, diff_y);
6214 if (max_abs_diff <= truncated_epsilon)
6216 memcpy(to, from, 2 * sizeof(SHORT));
6218 return TRUE;
6221 return FALSE;
6224 static BOOL weld_short2n(void *to, void *from, FLOAT epsilon)
6226 return weld_short2(to, from, epsilon * SHRT_MAX);
6229 static BOOL weld_short4(void *to, void *from, FLOAT epsilon)
6231 SHORT *s1 = to;
6232 SHORT *s2 = from;
6233 SHORT truncated_epsilon = (SHORT)epsilon;
6234 SHORT diff_x = abs(s1[0] - s2[0]);
6235 SHORT diff_y = abs(s1[1] - s2[1]);
6236 SHORT diff_z = abs(s1[2] - s2[2]);
6237 SHORT diff_w = abs(s1[3] - s2[3]);
6238 SHORT max_abs_diff = max(diff_x, diff_y);
6239 max_abs_diff = max(diff_z, max_abs_diff);
6240 max_abs_diff = max(diff_w, max_abs_diff);
6242 if (max_abs_diff <= truncated_epsilon)
6244 memcpy(to, from, 4 * sizeof(SHORT));
6246 return TRUE;
6249 return FALSE;
6252 static BOOL weld_short4n(void *to, void *from, FLOAT epsilon)
6254 return weld_short4(to, from, epsilon * SHRT_MAX);
6257 static BOOL weld_ushort2n(void *to, void *from, FLOAT epsilon)
6259 USHORT *s1 = to;
6260 USHORT *s2 = from;
6261 USHORT scaled_epsilon = (USHORT)(epsilon * USHRT_MAX);
6262 USHORT diff_x = s1[0] > s2[0] ? s1[0] - s2[0] : s2[0] - s1[0];
6263 USHORT diff_y = s1[1] > s2[1] ? s1[1] - s2[1] : s2[1] - s1[1];
6264 USHORT max_diff = max(diff_x, diff_y);
6266 if (max_diff <= scaled_epsilon)
6268 memcpy(to, from, 2 * sizeof(USHORT));
6270 return TRUE;
6273 return FALSE;
6276 static BOOL weld_ushort4n(void *to, void *from, FLOAT epsilon)
6278 USHORT *s1 = to;
6279 USHORT *s2 = from;
6280 USHORT scaled_epsilon = (USHORT)(epsilon * USHRT_MAX);
6281 USHORT diff_x = s1[0] > s2[0] ? s1[0] - s2[0] : s2[0] - s1[0];
6282 USHORT diff_y = s1[1] > s2[1] ? s1[1] - s2[1] : s2[1] - s1[1];
6283 USHORT diff_z = s1[2] > s2[2] ? s1[2] - s2[2] : s2[2] - s1[2];
6284 USHORT diff_w = s1[3] > s2[3] ? s1[3] - s2[3] : s2[3] - s1[3];
6285 USHORT max_diff = max(diff_x, diff_y);
6286 max_diff = max(diff_z, max_diff);
6287 max_diff = max(diff_w, max_diff);
6289 if (max_diff <= scaled_epsilon)
6291 memcpy(to, from, 4 * sizeof(USHORT));
6293 return TRUE;
6296 return FALSE;
6299 struct udec3
6301 UINT x;
6302 UINT y;
6303 UINT z;
6304 UINT w;
6307 static struct udec3 dword_to_udec3(DWORD d)
6309 struct udec3 v;
6311 v.x = d & 0x3ff;
6312 v.y = (d & 0xffc00) >> 10;
6313 v.z = (d & 0x3ff00000) >> 20;
6314 v.w = (d & 0xc0000000) >> 30;
6316 return v;
6319 static BOOL weld_udec3(void *to, void *from, FLOAT epsilon)
6321 DWORD *d1 = to;
6322 DWORD *d2 = from;
6323 struct udec3 v1 = dword_to_udec3(*d1);
6324 struct udec3 v2 = dword_to_udec3(*d2);
6325 UINT truncated_epsilon = (UINT)epsilon;
6326 UINT diff_x = v1.x > v2.x ? v1.x - v2.x : v2.x - v1.x;
6327 UINT diff_y = v1.y > v2.y ? v1.y - v2.y : v2.y - v1.y;
6328 UINT diff_z = v1.z > v2.z ? v1.z - v2.z : v2.z - v1.z;
6329 UINT diff_w = v1.w > v2.w ? v1.w - v2.w : v2.w - v1.w;
6330 UINT max_diff = max(diff_x, diff_y);
6331 max_diff = max(diff_z, max_diff);
6332 max_diff = max(diff_w, max_diff);
6334 if (max_diff <= truncated_epsilon)
6336 memcpy(to, from, sizeof(DWORD));
6338 return TRUE;
6341 return FALSE;
6344 struct dec3n
6346 INT x;
6347 INT y;
6348 INT z;
6349 INT w;
6352 static struct dec3n dword_to_dec3n(DWORD d)
6354 struct dec3n v;
6356 v.x = d & 0x3ff;
6357 v.y = (d & 0xffc00) >> 10;
6358 v.z = (d & 0x3ff00000) >> 20;
6359 v.w = (d & 0xc0000000) >> 30;
6361 return v;
6364 static BOOL weld_dec3n(void *to, void *from, FLOAT epsilon)
6366 const UINT MAX_DEC3N = 511;
6367 DWORD *d1 = to;
6368 DWORD *d2 = from;
6369 struct dec3n v1 = dword_to_dec3n(*d1);
6370 struct dec3n v2 = dword_to_dec3n(*d2);
6371 INT scaled_epsilon = (INT)(epsilon * MAX_DEC3N);
6372 INT diff_x = abs(v1.x - v2.x);
6373 INT diff_y = abs(v1.y - v2.y);
6374 INT diff_z = abs(v1.z - v2.z);
6375 INT diff_w = abs(v1.w - v2.w);
6376 INT max_abs_diff = max(diff_x, diff_y);
6377 max_abs_diff = max(diff_z, max_abs_diff);
6378 max_abs_diff = max(diff_w, max_abs_diff);
6380 if (max_abs_diff <= scaled_epsilon)
6382 memcpy(to, from, sizeof(DWORD));
6384 return TRUE;
6387 return FALSE;
6390 static BOOL weld_float16_2(void *to, void *from, FLOAT epsilon)
6392 D3DXFLOAT16 *v1_float16 = to;
6393 D3DXFLOAT16 *v2_float16 = from;
6394 FLOAT diff_x;
6395 FLOAT diff_y;
6396 FLOAT max_abs_diff;
6397 const UINT NUM_ELEM = 2;
6398 FLOAT v1[NUM_ELEM];
6399 FLOAT v2[NUM_ELEM];
6401 D3DXFloat16To32Array(v1, v1_float16, NUM_ELEM);
6402 D3DXFloat16To32Array(v2, v2_float16, NUM_ELEM);
6404 diff_x = fabsf(v1[0] - v2[0]);
6405 diff_y = fabsf(v1[1] - v2[1]);
6406 max_abs_diff = max(diff_x, diff_y);
6408 if (max_abs_diff <= epsilon)
6410 memcpy(to, from, NUM_ELEM * sizeof(D3DXFLOAT16));
6412 return TRUE;
6415 return FALSE;
6418 static BOOL weld_float16_4(void *to, void *from, FLOAT epsilon)
6420 D3DXFLOAT16 *v1_float16 = to;
6421 D3DXFLOAT16 *v2_float16 = from;
6422 FLOAT diff_x;
6423 FLOAT diff_y;
6424 FLOAT diff_z;
6425 FLOAT diff_w;
6426 FLOAT max_abs_diff;
6427 const UINT NUM_ELEM = 4;
6428 FLOAT v1[NUM_ELEM];
6429 FLOAT v2[NUM_ELEM];
6431 D3DXFloat16To32Array(v1, v1_float16, NUM_ELEM);
6432 D3DXFloat16To32Array(v2, v2_float16, NUM_ELEM);
6434 diff_x = fabsf(v1[0] - v2[0]);
6435 diff_y = fabsf(v1[1] - v2[1]);
6436 diff_z = fabsf(v1[2] - v2[2]);
6437 diff_w = fabsf(v1[3] - v2[3]);
6438 max_abs_diff = max(diff_x, diff_y);
6439 max_abs_diff = max(diff_z, max_abs_diff);
6440 max_abs_diff = max(diff_w, max_abs_diff);
6442 if (max_abs_diff <= epsilon)
6444 memcpy(to, from, NUM_ELEM * sizeof(D3DXFLOAT16));
6446 return TRUE;
6449 return FALSE;
6452 /* Sets the vertex components to the same value if they are within epsilon. */
6453 static BOOL weld_component(void *to, void *from, D3DDECLTYPE type, FLOAT epsilon)
6455 /* Quiet FIXMEs as this is in a loop with potentially thousand of iterations. */
6456 BOOL fixme_once_unused = FALSE;
6457 BOOL fixme_once_unknown = FALSE;
6459 switch (type)
6461 case D3DDECLTYPE_FLOAT1:
6462 return weld_float1(to, from, epsilon);
6464 case D3DDECLTYPE_FLOAT2:
6465 return weld_float2(to, from, epsilon);
6467 case D3DDECLTYPE_FLOAT3:
6468 return weld_float3(to, from, epsilon);
6470 case D3DDECLTYPE_FLOAT4:
6471 return weld_float4(to, from, epsilon);
6473 case D3DDECLTYPE_D3DCOLOR:
6474 return weld_d3dcolor(to, from, epsilon);
6476 case D3DDECLTYPE_UBYTE4:
6477 return weld_ubyte4(to, from, epsilon);
6479 case D3DDECLTYPE_SHORT2:
6480 return weld_short2(to, from, epsilon);
6482 case D3DDECLTYPE_SHORT4:
6483 return weld_short4(to, from, epsilon);
6485 case D3DDECLTYPE_UBYTE4N:
6486 return weld_ubyte4n(to, from, epsilon);
6488 case D3DDECLTYPE_SHORT2N:
6489 return weld_short2n(to, from, epsilon);
6491 case D3DDECLTYPE_SHORT4N:
6492 return weld_short4n(to, from, epsilon);
6494 case D3DDECLTYPE_USHORT2N:
6495 return weld_ushort2n(to, from, epsilon);
6497 case D3DDECLTYPE_USHORT4N:
6498 return weld_ushort4n(to, from, epsilon);
6500 case D3DDECLTYPE_UDEC3:
6501 return weld_udec3(to, from, epsilon);
6503 case D3DDECLTYPE_DEC3N:
6504 return weld_dec3n(to, from, epsilon);
6506 case D3DDECLTYPE_FLOAT16_2:
6507 return weld_float16_2(to, from, epsilon);
6509 case D3DDECLTYPE_FLOAT16_4:
6510 return weld_float16_4(to, from, epsilon);
6512 case D3DDECLTYPE_UNUSED:
6513 if (!fixme_once_unused++)
6514 FIXME("D3DDECLTYPE_UNUSED welding not implemented.\n");
6515 break;
6517 default:
6518 if (!fixme_once_unknown++)
6519 FIXME("Welding of unknown declaration type %d is not implemented.\n", type);
6520 break;
6523 return FALSE;
6526 static FLOAT get_component_epsilon(const D3DVERTEXELEMENT9 *decl_ptr, const D3DXWELDEPSILONS *epsilons)
6528 FLOAT epsilon = 0.0f;
6529 /* Quiet FIXMEs as this is in a loop with potentially thousand of iterations. */
6530 static BOOL fixme_once_blendindices = FALSE;
6531 static BOOL fixme_once_positiont = FALSE;
6532 static BOOL fixme_once_fog = FALSE;
6533 static BOOL fixme_once_depth = FALSE;
6534 static BOOL fixme_once_sample = FALSE;
6535 static BOOL fixme_once_unknown = FALSE;
6537 switch (decl_ptr->Usage)
6539 case D3DDECLUSAGE_POSITION:
6540 epsilon = epsilons->Position;
6541 break;
6542 case D3DDECLUSAGE_BLENDWEIGHT:
6543 epsilon = epsilons->BlendWeights;
6544 break;
6545 case D3DDECLUSAGE_NORMAL:
6546 epsilon = epsilons->Normals;
6547 break;
6548 case D3DDECLUSAGE_PSIZE:
6549 epsilon = epsilons->PSize;
6550 break;
6551 case D3DDECLUSAGE_TEXCOORD:
6553 BYTE usage_index = decl_ptr->UsageIndex;
6554 if (usage_index > 7)
6555 usage_index = 7;
6556 epsilon = epsilons->Texcoords[usage_index];
6557 break;
6559 case D3DDECLUSAGE_TANGENT:
6560 epsilon = epsilons->Tangent;
6561 break;
6562 case D3DDECLUSAGE_BINORMAL:
6563 epsilon = epsilons->Binormal;
6564 break;
6565 case D3DDECLUSAGE_TESSFACTOR:
6566 epsilon = epsilons->TessFactor;
6567 break;
6568 case D3DDECLUSAGE_COLOR:
6569 if (decl_ptr->UsageIndex == 0)
6570 epsilon = epsilons->Diffuse;
6571 else if (decl_ptr->UsageIndex == 1)
6572 epsilon = epsilons->Specular;
6573 else
6574 epsilon = 1e-6f;
6575 break;
6576 case D3DDECLUSAGE_BLENDINDICES:
6577 if (!fixme_once_blendindices++)
6578 FIXME("D3DDECLUSAGE_BLENDINDICES welding not implemented.\n");
6579 break;
6580 case D3DDECLUSAGE_POSITIONT:
6581 if (!fixme_once_positiont++)
6582 FIXME("D3DDECLUSAGE_POSITIONT welding not implemented.\n");
6583 break;
6584 case D3DDECLUSAGE_FOG:
6585 if (!fixme_once_fog++)
6586 FIXME("D3DDECLUSAGE_FOG welding not implemented.\n");
6587 break;
6588 case D3DDECLUSAGE_DEPTH:
6589 if (!fixme_once_depth++)
6590 FIXME("D3DDECLUSAGE_DEPTH welding not implemented.\n");
6591 break;
6592 case D3DDECLUSAGE_SAMPLE:
6593 if (!fixme_once_sample++)
6594 FIXME("D3DDECLUSAGE_SAMPLE welding not implemented.\n");
6595 break;
6596 default:
6597 if (!fixme_once_unknown++)
6598 FIXME("Unknown usage %x\n", decl_ptr->Usage);
6599 break;
6602 return epsilon;
6605 /* Helper function for reading a 32-bit index buffer. */
6606 static inline DWORD read_ib(void *index_buffer, BOOL indices_are_32bit,
6607 DWORD index)
6609 if (indices_are_32bit)
6611 DWORD *indices = index_buffer;
6612 return indices[index];
6614 else
6616 WORD *indices = index_buffer;
6617 return indices[index];
6621 /* Helper function for writing to a 32-bit index buffer. */
6622 static inline void write_ib(void *index_buffer, BOOL indices_are_32bit,
6623 DWORD index, DWORD value)
6625 if (indices_are_32bit)
6627 DWORD *indices = index_buffer;
6628 indices[index] = value;
6630 else
6632 WORD *indices = index_buffer;
6633 indices[index] = value;
6637 /*************************************************************************
6638 * D3DXWeldVertices (D3DX9_36.@)
6640 * Welds together similar vertices. The similarity between vert-
6641 * ices can be the position and other components such as
6642 * normal and color.
6644 * PARAMS
6645 * mesh [I] Mesh which vertices will be welded together.
6646 * flags [I] D3DXWELDEPSILONSFLAGS specifying how to weld.
6647 * epsilons [I] How similar a component needs to be for welding.
6648 * adjacency [I] Which faces are adjacent to other faces.
6649 * adjacency_out [O] Updated adjacency after welding.
6650 * face_remap_out [O] Which faces the old faces have been mapped to.
6651 * vertex_remap_out [O] Which vertices the old vertices have been mapped to.
6653 * RETURNS
6654 * Success: D3D_OK.
6655 * Failure: D3DERR_INVALIDCALL, E_OUTOFMEMORY.
6657 * BUGS
6658 * Attribute sorting not implemented.
6661 HRESULT WINAPI D3DXWeldVertices(ID3DXMesh *mesh, DWORD flags, const D3DXWELDEPSILONS *epsilons,
6662 const DWORD *adjacency, DWORD *adjacency_out, DWORD *face_remap_out, ID3DXBuffer **vertex_remap_out)
6664 DWORD *adjacency_generated = NULL;
6665 const DWORD *adjacency_ptr;
6666 DWORD *attributes = NULL;
6667 const FLOAT DEFAULT_EPSILON = 1.0e-6f;
6668 HRESULT hr;
6669 DWORD i;
6670 void *indices = NULL;
6671 BOOL indices_are_32bit = mesh->lpVtbl->GetOptions(mesh) & D3DXMESH_32BIT;
6672 DWORD optimize_flags;
6673 DWORD *point_reps = NULL;
6674 ID3DXMeshImpl *This = impl_from_ID3DXMesh(mesh);
6675 DWORD *vertex_face_map = NULL;
6676 ID3DXBuffer *vertex_remap = NULL;
6677 BYTE *vertices = NULL;
6679 TRACE("(%p, %x, %p, %p, %p, %p, %p)\n", mesh, flags, epsilons,
6680 adjacency, adjacency_out, face_remap_out, vertex_remap_out);
6682 if (flags == 0)
6684 WARN("No flags is undefined. Using D3DXWELDEPSILONS_WELDPARTIALMATCHES instead.\n");
6685 flags = D3DXWELDEPSILONS_WELDPARTIALMATCHES;
6688 if (adjacency) /* Use supplied adjacency. */
6690 adjacency_ptr = adjacency;
6692 else /* Adjacency has to be generated. */
6694 adjacency_generated = HeapAlloc(GetProcessHeap(), 0, 3 * This->numfaces * sizeof(*adjacency_generated));
6695 if (!adjacency_generated)
6697 ERR("Couldn't allocate memory for adjacency_generated.\n");
6698 hr = E_OUTOFMEMORY;
6699 goto cleanup;
6701 hr = mesh->lpVtbl->GenerateAdjacency(mesh, DEFAULT_EPSILON, adjacency_generated);
6702 if (FAILED(hr))
6704 ERR("Couldn't generate adjacency.\n");
6705 goto cleanup;
6707 adjacency_ptr = adjacency_generated;
6710 /* Point representation says which vertices can be replaced. */
6711 point_reps = HeapAlloc(GetProcessHeap(), 0, This->numvertices * sizeof(*point_reps));
6712 if (!point_reps)
6714 hr = E_OUTOFMEMORY;
6715 ERR("Couldn't allocate memory for point_reps.\n");
6716 goto cleanup;
6718 hr = mesh->lpVtbl->ConvertAdjacencyToPointReps(mesh, adjacency_ptr, point_reps);
6719 if (FAILED(hr))
6721 ERR("ConvertAdjacencyToPointReps failed.\n");
6722 goto cleanup;
6725 hr = mesh->lpVtbl->LockIndexBuffer(mesh, 0, &indices);
6726 if (FAILED(hr))
6728 ERR("Couldn't lock index buffer.\n");
6729 goto cleanup;
6732 hr = mesh->lpVtbl->LockAttributeBuffer(mesh, 0, &attributes);
6733 if (FAILED(hr))
6735 ERR("Couldn't lock attribute buffer.\n");
6736 goto cleanup;
6738 vertex_face_map = HeapAlloc(GetProcessHeap(), 0, This->numvertices * sizeof(*vertex_face_map));
6739 if (!vertex_face_map)
6741 hr = E_OUTOFMEMORY;
6742 ERR("Couldn't allocate memory for vertex_face_map.\n");
6743 goto cleanup;
6745 /* Build vertex face map, so that a vertex's face can be looked up. */
6746 for (i = 0; i < This->numfaces; i++)
6748 DWORD j;
6749 for (j = 0; j < 3; j++)
6751 DWORD index = read_ib(indices, indices_are_32bit, 3*i + j);
6752 vertex_face_map[index] = i;
6756 if (flags & D3DXWELDEPSILONS_WELDPARTIALMATCHES)
6758 hr = mesh->lpVtbl->LockVertexBuffer(mesh, 0, (void**)&vertices);
6759 if (FAILED(hr))
6761 ERR("Couldn't lock vertex buffer.\n");
6762 goto cleanup;
6764 /* For each vertex that can be removed, compare its vertex components
6765 * with the vertex components from the vertex that can replace it. A
6766 * vertex is only fully replaced if all the components match and the
6767 * flag D3DXWELDEPSILONS_DONOTREMOVEVERTICES is not set, and they
6768 * belong to the same attribute group. Otherwise the vertex components
6769 * that are within epsilon are set to the same value.
6771 for (i = 0; i < 3 * This->numfaces; i++)
6773 D3DVERTEXELEMENT9 *decl_ptr;
6774 DWORD vertex_size = mesh->lpVtbl->GetNumBytesPerVertex(mesh);
6775 DWORD num_vertex_components;
6776 INT matches = 0;
6777 BOOL all_match;
6778 DWORD index = read_ib(indices, indices_are_32bit, i);
6780 for (decl_ptr = This->cached_declaration, num_vertex_components = 0; decl_ptr->Stream != 0xFF; decl_ptr++, num_vertex_components++)
6782 BYTE *to = &vertices[vertex_size*index + decl_ptr->Offset];
6783 BYTE *from = &vertices[vertex_size*point_reps[index] + decl_ptr->Offset];
6784 FLOAT epsilon = get_component_epsilon(decl_ptr, epsilons);
6786 /* Don't weld self */
6787 if (index == point_reps[index])
6789 matches++;
6790 continue;
6793 if (weld_component(to, from, decl_ptr->Type, epsilon))
6794 matches++;
6797 all_match = (num_vertex_components == matches);
6798 if (all_match && !(flags & D3DXWELDEPSILONS_DONOTREMOVEVERTICES))
6800 DWORD to_face = vertex_face_map[index];
6801 DWORD from_face = vertex_face_map[point_reps[index]];
6802 if(attributes[to_face] != attributes[from_face] && !(flags & D3DXWELDEPSILONS_DONOTSPLIT))
6803 continue;
6804 write_ib(indices, indices_are_32bit, i, point_reps[index]);
6807 mesh->lpVtbl->UnlockVertexBuffer(mesh);
6808 vertices = NULL;
6810 else if (flags & D3DXWELDEPSILONS_WELDALL)
6812 for (i = 0; i < 3 * This->numfaces; i++)
6814 DWORD index = read_ib(indices, indices_are_32bit, i);
6815 DWORD to_face = vertex_face_map[index];
6816 DWORD from_face = vertex_face_map[point_reps[index]];
6817 if(attributes[to_face] != attributes[from_face] && !(flags & D3DXWELDEPSILONS_DONOTSPLIT))
6818 continue;
6819 write_ib(indices, indices_are_32bit, i, point_reps[index]);
6822 mesh->lpVtbl->UnlockAttributeBuffer(mesh);
6823 attributes = NULL;
6824 mesh->lpVtbl->UnlockIndexBuffer(mesh);
6825 indices = NULL;
6827 /* Compact mesh using OptimizeInplace */
6828 optimize_flags = D3DXMESHOPT_COMPACT;
6829 hr = mesh->lpVtbl->OptimizeInplace(mesh, optimize_flags, adjacency_ptr, adjacency_out, face_remap_out, vertex_remap_out);
6830 if (FAILED(hr))
6832 ERR("Couldn't compact mesh.\n");
6833 goto cleanup;
6836 hr = D3D_OK;
6837 cleanup:
6838 HeapFree(GetProcessHeap(), 0, adjacency_generated);
6839 HeapFree(GetProcessHeap(), 0, point_reps);
6840 HeapFree(GetProcessHeap(), 0, vertex_face_map);
6841 if (attributes) mesh->lpVtbl->UnlockAttributeBuffer(mesh);
6842 if (indices) mesh->lpVtbl->UnlockIndexBuffer(mesh);
6843 if (vertex_remap) ID3DXBuffer_Release(vertex_remap);
6844 if (vertices) mesh->lpVtbl->UnlockVertexBuffer(mesh);
6846 return hr;
6849 /*************************************************************************
6850 * D3DXOptimizeFaces (D3DX9_36.@)
6852 * Re-orders the faces so the vertex cache is used optimally.
6854 * PARAMS
6855 * indices [I] Pointer to an index buffer belonging to a mesh.
6856 * num_faces [I] Number of faces in the mesh.
6857 * num_vertices [I] Number of vertices in the mesh.
6858 * indices_are_32bit [I] Specifies whether indices are 32- or 16-bit.
6859 * face_remap [I/O] The new order the faces should be drawn in.
6861 * RETURNS
6862 * Success: D3D_OK.
6863 * Failure: D3DERR_INVALIDCALL.
6865 * BUGS
6866 * The face re-ordering does not use the vertex cache optimally.
6869 HRESULT WINAPI D3DXOptimizeFaces(LPCVOID indices,
6870 UINT num_faces,
6871 UINT num_vertices,
6872 BOOL indices_are_32bit,
6873 DWORD *face_remap)
6875 UINT i;
6876 UINT j = num_faces - 1;
6877 UINT limit_16_bit = 2 << 15; /* According to MSDN */
6878 HRESULT hr = D3D_OK;
6880 FIXME("(%p, %u, %u, %s, %p): semi-stub. Face order will not be optimal.\n",
6881 indices, num_faces, num_vertices,
6882 indices_are_32bit ? "TRUE" : "FALSE", face_remap);
6884 if (!indices_are_32bit && num_faces >= limit_16_bit)
6886 WARN("Number of faces must be less than %d when using 16-bit indices.\n",
6887 limit_16_bit);
6888 hr = D3DERR_INVALIDCALL;
6889 goto error;
6892 if (!face_remap)
6894 WARN("Face remap pointer is NULL.\n");
6895 hr = D3DERR_INVALIDCALL;
6896 goto error;
6899 /* The faces are drawn in reverse order for simple meshes. This ordering
6900 * is not optimal for complicated meshes, but will not break anything
6901 * either. The ordering should be changed to take advantage of the vertex
6902 * cache on the graphics card.
6904 * TODO Re-order to take advantage of vertex cache.
6906 for (i = 0; i < num_faces; i++)
6908 face_remap[i] = j--;
6911 return D3D_OK;
6913 error:
6914 return hr;