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
11 * This library is free software; you can redistribute it and/or
12 * modify it under the terms of the GNU Lesser General Public
13 * License as published by the Free Software Foundation; either
14 * version 2.1 of the License, or (at your option) any later version.
16 * This library is distributed in the hope that it will be useful,
17 * but WITHOUT ANY WARRANTY; without even the implied warranty of
18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
19 * Lesser General Public License for more details.
21 * You should have received a copy of the GNU Lesser General Public
22 * License along with this library; if not, write to the Free Software
23 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
27 #include "wine/port.h"
30 #define NONAMELESSUNION
42 #include "wine/debug.h"
43 #include "wine/unicode.h"
44 #include "wine/list.h"
45 #include "d3dx9_36_private.h"
47 WINE_DEFAULT_DEBUG_CHANNEL(d3dx);
49 typedef struct ID3DXMeshImpl
51 ID3DXMesh ID3DXMesh_iface;
58 IDirect3DDevice9 *device;
59 D3DVERTEXELEMENT9 cached_declaration[MAX_FVF_DECL_SIZE];
60 IDirect3DVertexDeclaration9 *vertex_declaration;
61 UINT vertex_declaration_size;
63 IDirect3DVertexBuffer9 *vertex_buffer;
64 IDirect3DIndexBuffer9 *index_buffer;
66 int attrib_buffer_lock_count;
67 DWORD attrib_table_size;
68 D3DXATTRIBUTERANGE *attrib_table;
71 const UINT d3dx_decltype_size[] =
73 /* D3DDECLTYPE_FLOAT1 */ sizeof(FLOAT),
74 /* D3DDECLTYPE_FLOAT2 */ sizeof(D3DXVECTOR2),
75 /* D3DDECLTYPE_FLOAT3 */ sizeof(D3DXVECTOR3),
76 /* D3DDECLTYPE_FLOAT4 */ sizeof(D3DXVECTOR4),
77 /* D3DDECLTYPE_D3DCOLOR */ sizeof(D3DCOLOR),
78 /* D3DDECLTYPE_UBYTE4 */ 4 * sizeof(BYTE),
79 /* D3DDECLTYPE_SHORT2 */ 2 * sizeof(SHORT),
80 /* D3DDECLTYPE_SHORT4 */ 4 * sizeof(SHORT),
81 /* D3DDECLTYPE_UBYTE4N */ 4 * sizeof(BYTE),
82 /* D3DDECLTYPE_SHORT2N */ 2 * sizeof(SHORT),
83 /* D3DDECLTYPE_SHORT4N */ 4 * sizeof(SHORT),
84 /* D3DDECLTYPE_USHORT2N */ 2 * sizeof(USHORT),
85 /* D3DDECLTYPE_USHORT4N */ 4 * sizeof(USHORT),
86 /* D3DDECLTYPE_UDEC3 */ 4, /* 3 * 10 bits + 2 padding */
87 /* D3DDECLTYPE_DEC3N */ 4,
88 /* D3DDECLTYPE_FLOAT16_2 */ 2 * sizeof(D3DXFLOAT16),
89 /* D3DDECLTYPE_FLOAT16_4 */ 4 * sizeof(D3DXFLOAT16),
92 static inline ID3DXMeshImpl *impl_from_ID3DXMesh(ID3DXMesh *iface)
94 return CONTAINING_RECORD(iface, ID3DXMeshImpl, ID3DXMesh_iface);
97 static HRESULT WINAPI ID3DXMeshImpl_QueryInterface(ID3DXMesh *iface, REFIID riid, LPVOID *object)
99 TRACE("(%p)->(%s, %p)\n", iface, debugstr_guid(riid), object);
101 if (IsEqualGUID(riid, &IID_IUnknown) ||
102 IsEqualGUID(riid, &IID_ID3DXBaseMesh) ||
103 IsEqualGUID(riid, &IID_ID3DXMesh))
105 iface->lpVtbl->AddRef(iface);
110 WARN("Interface %s not found.\n", debugstr_guid(riid));
112 return E_NOINTERFACE;
115 static ULONG WINAPI ID3DXMeshImpl_AddRef(ID3DXMesh *iface)
117 ID3DXMeshImpl *This = impl_from_ID3DXMesh(iface);
119 TRACE("(%p)->(): AddRef from %d\n", This, This->ref);
121 return InterlockedIncrement(&This->ref);
124 static ULONG WINAPI ID3DXMeshImpl_Release(ID3DXMesh *iface)
126 ID3DXMeshImpl *This = impl_from_ID3DXMesh(iface);
127 ULONG ref = InterlockedDecrement(&This->ref);
129 TRACE("(%p)->(): Release from %d\n", This, ref + 1);
133 IDirect3DIndexBuffer9_Release(This->index_buffer);
134 IDirect3DVertexBuffer9_Release(This->vertex_buffer);
135 if (This->vertex_declaration)
136 IDirect3DVertexDeclaration9_Release(This->vertex_declaration);
137 IDirect3DDevice9_Release(This->device);
138 HeapFree(GetProcessHeap(), 0, This->attrib_buffer);
139 HeapFree(GetProcessHeap(), 0, This->attrib_table);
140 HeapFree(GetProcessHeap(), 0, This);
146 /*** ID3DXBaseMesh ***/
147 static HRESULT WINAPI ID3DXMeshImpl_DrawSubset(ID3DXMesh *iface, DWORD attrib_id)
149 ID3DXMeshImpl *This = impl_from_ID3DXMesh(iface);
155 TRACE("(%p)->(%u)\n", This, attrib_id);
157 if (!This->vertex_declaration)
159 WARN("Can't draw a mesh with an invalid vertex declaration.\n");
163 vertex_size = iface->lpVtbl->GetNumBytesPerVertex(iface);
165 hr = IDirect3DDevice9_SetVertexDeclaration(This->device, This->vertex_declaration);
166 if (FAILED(hr)) return hr;
167 hr = IDirect3DDevice9_SetStreamSource(This->device, 0, This->vertex_buffer, 0, vertex_size);
168 if (FAILED(hr)) return hr;
169 hr = IDirect3DDevice9_SetIndices(This->device, This->index_buffer);
170 if (FAILED(hr)) return hr;
172 while (face_end < This->numfaces)
174 for (face_start = face_end; face_start < This->numfaces; face_start++)
176 if (This->attrib_buffer[face_start] == attrib_id)
179 if (face_start >= This->numfaces)
181 for (face_end = face_start + 1; face_end < This->numfaces; face_end++)
183 if (This->attrib_buffer[face_end] != attrib_id)
187 hr = IDirect3DDevice9_DrawIndexedPrimitive(This->device, D3DPT_TRIANGLELIST,
188 0, 0, This->numvertices, face_start * 3, face_end - face_start);
189 if (FAILED(hr)) return hr;
195 static DWORD WINAPI ID3DXMeshImpl_GetNumFaces(ID3DXMesh *iface)
197 ID3DXMeshImpl *This = impl_from_ID3DXMesh(iface);
199 TRACE("(%p)\n", This);
201 return This->numfaces;
204 static DWORD WINAPI ID3DXMeshImpl_GetNumVertices(ID3DXMesh *iface)
206 ID3DXMeshImpl *This = impl_from_ID3DXMesh(iface);
208 TRACE("(%p)\n", This);
210 return This->numvertices;
213 static DWORD WINAPI ID3DXMeshImpl_GetFVF(ID3DXMesh *iface)
215 ID3DXMeshImpl *This = impl_from_ID3DXMesh(iface);
217 TRACE("(%p)\n", This);
222 static void copy_declaration(D3DVERTEXELEMENT9 *dst, const D3DVERTEXELEMENT9 *src, UINT num_elem)
224 memcpy(dst, src, num_elem * sizeof(*src));
227 static HRESULT WINAPI ID3DXMeshImpl_GetDeclaration(ID3DXMesh *iface, D3DVERTEXELEMENT9 declaration[MAX_FVF_DECL_SIZE])
229 ID3DXMeshImpl *This = impl_from_ID3DXMesh(iface);
231 TRACE("(%p)\n", This);
233 if (declaration == NULL) return D3DERR_INVALIDCALL;
235 copy_declaration(declaration, This->cached_declaration, This->num_elem);
240 static DWORD WINAPI ID3DXMeshImpl_GetNumBytesPerVertex(ID3DXMesh *iface)
242 ID3DXMeshImpl *This = impl_from_ID3DXMesh(iface);
244 TRACE("iface (%p)\n", This);
246 return This->vertex_declaration_size;
249 static DWORD WINAPI ID3DXMeshImpl_GetOptions(ID3DXMesh *iface)
251 ID3DXMeshImpl *This = impl_from_ID3DXMesh(iface);
253 TRACE("(%p)\n", This);
255 return This->options;
258 static HRESULT WINAPI ID3DXMeshImpl_GetDevice(ID3DXMesh *iface, LPDIRECT3DDEVICE9 *device)
260 ID3DXMeshImpl *This = impl_from_ID3DXMesh(iface);
262 TRACE("(%p)->(%p)\n", This, device);
264 if (device == NULL) return D3DERR_INVALIDCALL;
265 *device = This->device;
266 IDirect3DDevice9_AddRef(This->device);
271 static HRESULT WINAPI ID3DXMeshImpl_CloneMeshFVF(ID3DXMesh *iface, DWORD options, DWORD fvf, LPDIRECT3DDEVICE9 device, LPD3DXMESH *clone_mesh)
273 ID3DXMeshImpl *This = impl_from_ID3DXMesh(iface);
275 D3DVERTEXELEMENT9 declaration[MAX_FVF_DECL_SIZE];
277 TRACE("(%p)->(%x,%x,%p,%p)\n", This, options, fvf, device, clone_mesh);
279 hr = D3DXDeclaratorFromFVF(fvf, declaration);
280 if (FAILED(hr)) return hr;
282 return iface->lpVtbl->CloneMesh(iface, options, declaration, device, clone_mesh);
285 static FLOAT scale_clamp_ubyten(FLOAT value)
287 value = value * UCHAR_MAX;
295 if (value > UCHAR_MAX) /* Clamp at 255 */
302 static FLOAT scale_clamp_shortn(FLOAT value)
304 value = value * SHRT_MAX;
306 /* The tests show that the range is SHRT_MIN + 1 to SHRT_MAX. */
307 if (value <= SHRT_MIN)
311 else if (value > SHRT_MAX)
321 static FLOAT scale_clamp_ushortn(FLOAT value)
323 value = value * USHRT_MAX;
331 if (value > USHRT_MAX) /* Clamp at 65535 */
338 static INT simple_round(FLOAT value)
340 int res = (INT)(value + 0.5f);
345 static void convert_float4(BYTE *dst, CONST D3DXVECTOR4 *src, D3DDECLTYPE type_dst)
347 BOOL fixme_once = FALSE;
351 case D3DDECLTYPE_FLOAT1:
353 FLOAT *dst_ptr = (FLOAT*)dst;
357 case D3DDECLTYPE_FLOAT2:
359 D3DXVECTOR2 *dst_ptr = (D3DXVECTOR2*)dst;
364 case D3DDECLTYPE_FLOAT3:
366 D3DXVECTOR3 *dst_ptr = (D3DXVECTOR3*)dst;
372 case D3DDECLTYPE_FLOAT4:
374 D3DXVECTOR4 *dst_ptr = (D3DXVECTOR4*)dst;
381 case D3DDECLTYPE_D3DCOLOR:
383 dst[0] = (BYTE)simple_round(scale_clamp_ubyten(src->z));
384 dst[1] = (BYTE)simple_round(scale_clamp_ubyten(src->y));
385 dst[2] = (BYTE)simple_round(scale_clamp_ubyten(src->x));
386 dst[3] = (BYTE)simple_round(scale_clamp_ubyten(src->w));
389 case D3DDECLTYPE_UBYTE4:
391 dst[0] = src->x < 0.0f ? 0 : (BYTE)simple_round(src->x);
392 dst[1] = src->y < 0.0f ? 0 : (BYTE)simple_round(src->y);
393 dst[2] = src->z < 0.0f ? 0 : (BYTE)simple_round(src->z);
394 dst[3] = src->w < 0.0f ? 0 : (BYTE)simple_round(src->w);
397 case D3DDECLTYPE_SHORT2:
399 SHORT *dst_ptr = (SHORT*)dst;
400 dst_ptr[0] = (SHORT)simple_round(src->x);
401 dst_ptr[1] = (SHORT)simple_round(src->y);
404 case D3DDECLTYPE_SHORT4:
406 SHORT *dst_ptr = (SHORT*)dst;
407 dst_ptr[0] = (SHORT)simple_round(src->x);
408 dst_ptr[1] = (SHORT)simple_round(src->y);
409 dst_ptr[2] = (SHORT)simple_round(src->z);
410 dst_ptr[3] = (SHORT)simple_round(src->w);
413 case D3DDECLTYPE_UBYTE4N:
415 dst[0] = (BYTE)simple_round(scale_clamp_ubyten(src->x));
416 dst[1] = (BYTE)simple_round(scale_clamp_ubyten(src->y));
417 dst[2] = (BYTE)simple_round(scale_clamp_ubyten(src->z));
418 dst[3] = (BYTE)simple_round(scale_clamp_ubyten(src->w));
421 case D3DDECLTYPE_SHORT2N:
423 SHORT *dst_ptr = (SHORT*)dst;
424 dst_ptr[0] = (SHORT)simple_round(scale_clamp_shortn(src->x));
425 dst_ptr[1] = (SHORT)simple_round(scale_clamp_shortn(src->y));
428 case D3DDECLTYPE_SHORT4N:
430 SHORT *dst_ptr = (SHORT*)dst;
431 dst_ptr[0] = (SHORT)simple_round(scale_clamp_shortn(src->x));
432 dst_ptr[1] = (SHORT)simple_round(scale_clamp_shortn(src->y));
433 dst_ptr[2] = (SHORT)simple_round(scale_clamp_shortn(src->z));
434 dst_ptr[3] = (SHORT)simple_round(scale_clamp_shortn(src->w));
437 case D3DDECLTYPE_USHORT2N:
439 USHORT *dst_ptr = (USHORT*)dst;
440 dst_ptr[0] = (USHORT)simple_round(scale_clamp_ushortn(src->x));
441 dst_ptr[1] = (USHORT)simple_round(scale_clamp_ushortn(src->y));
444 case D3DDECLTYPE_USHORT4N:
446 USHORT *dst_ptr = (USHORT*)dst;
447 dst_ptr[0] = (USHORT)simple_round(scale_clamp_ushortn(src->x));
448 dst_ptr[1] = (USHORT)simple_round(scale_clamp_ushortn(src->y));
449 dst_ptr[2] = (USHORT)simple_round(scale_clamp_ushortn(src->z));
450 dst_ptr[3] = (USHORT)simple_round(scale_clamp_ushortn(src->w));
453 case D3DDECLTYPE_FLOAT16_2:
455 D3DXFloat32To16Array((D3DXFLOAT16*)dst, (FLOAT*)src, 2);
458 case D3DDECLTYPE_FLOAT16_4:
460 D3DXFloat32To16Array((D3DXFLOAT16*)dst, (FLOAT*)src, 4);
465 FIXME("Conversion from D3DDECLTYPE_FLOAT4 to %d not implemented.\n", type_dst);
470 static void convert_component(BYTE *dst, BYTE *src, D3DDECLTYPE type_dst, D3DDECLTYPE type_src)
472 BOOL fixme_once = FALSE;
476 case D3DDECLTYPE_FLOAT1:
478 FLOAT *src_ptr = (FLOAT*)src;
479 D3DXVECTOR4 src_float4 = {*src_ptr, 0.0f, 0.0f, 1.0f};
480 convert_float4(dst, &src_float4, type_dst);
483 case D3DDECLTYPE_FLOAT2:
485 D3DXVECTOR2 *src_ptr = (D3DXVECTOR2*)src;
486 D3DXVECTOR4 src_float4 = {src_ptr->x, src_ptr->y, 0.0f, 1.0f};
487 convert_float4(dst, &src_float4, type_dst);
490 case D3DDECLTYPE_FLOAT3:
492 D3DXVECTOR3 *src_ptr = (D3DXVECTOR3*)src;
493 D3DXVECTOR4 src_float4 = {src_ptr->x, src_ptr->y, src_ptr->z, 1.0f};
494 convert_float4(dst, &src_float4, type_dst);
497 case D3DDECLTYPE_FLOAT4:
499 D3DXVECTOR4 *src_ptr = (D3DXVECTOR4*)src;
500 D3DXVECTOR4 src_float4 = {src_ptr->x, src_ptr->y, src_ptr->z, src_ptr->w};
501 convert_float4(dst, &src_float4, type_dst);
504 case D3DDECLTYPE_D3DCOLOR:
506 D3DXVECTOR4 src_float4 =
508 (FLOAT)src[2]/UCHAR_MAX,
509 (FLOAT)src[1]/UCHAR_MAX,
510 (FLOAT)src[0]/UCHAR_MAX,
511 (FLOAT)src[3]/UCHAR_MAX
513 convert_float4(dst, &src_float4, type_dst);
516 case D3DDECLTYPE_UBYTE4:
518 D3DXVECTOR4 src_float4 = {src[0], src[1], src[2], src[3]};
519 convert_float4(dst, &src_float4, type_dst);
522 case D3DDECLTYPE_SHORT2:
524 SHORT *src_ptr = (SHORT*)src;
525 D3DXVECTOR4 src_float4 = {src_ptr[0], src_ptr[1], 0.0f, 1.0f};
526 convert_float4(dst, &src_float4, type_dst);
529 case D3DDECLTYPE_SHORT4:
531 SHORT *src_ptr = (SHORT*)src;
532 D3DXVECTOR4 src_float4 = {src_ptr[0], src_ptr[1], src_ptr[2], src_ptr[3]};
533 convert_float4(dst, &src_float4, type_dst);
536 case D3DDECLTYPE_UBYTE4N:
538 D3DXVECTOR4 src_float4 =
540 (FLOAT)src[0]/UCHAR_MAX,
541 (FLOAT)src[1]/UCHAR_MAX,
542 (FLOAT)src[2]/UCHAR_MAX,
543 (FLOAT)src[3]/UCHAR_MAX
545 convert_float4(dst, &src_float4, type_dst);
548 case D3DDECLTYPE_SHORT2N:
550 SHORT *src_ptr = (SHORT*)src;
551 D3DXVECTOR4 src_float4 = {(FLOAT)src_ptr[0]/SHRT_MAX, (FLOAT)src_ptr[1]/SHRT_MAX, 0.0f, 1.0f};
552 convert_float4(dst, &src_float4, type_dst);
555 case D3DDECLTYPE_SHORT4N:
557 SHORT *src_ptr = (SHORT*)src;
558 D3DXVECTOR4 src_float4 =
560 (FLOAT)src_ptr[0]/SHRT_MAX,
561 (FLOAT)src_ptr[1]/SHRT_MAX,
562 (FLOAT)src_ptr[2]/SHRT_MAX,
563 (FLOAT)src_ptr[3]/SHRT_MAX
565 convert_float4(dst, &src_float4, type_dst);
568 case D3DDECLTYPE_FLOAT16_2:
570 D3DXVECTOR4 src_float4 = {0.0f, 0.0f, 0.0f, 1.0f};
571 D3DXFloat16To32Array((FLOAT*)&src_float4, (D3DXFLOAT16*)src, 2);
572 convert_float4(dst, &src_float4, type_dst);
575 case D3DDECLTYPE_FLOAT16_4:
577 D3DXVECTOR4 src_float4;
578 D3DXFloat16To32Array((FLOAT*)&src_float4, (D3DXFLOAT16*)src, 4);
579 convert_float4(dst, &src_float4, type_dst);
584 FIXME("Conversion of D3DDECLTYPE %d to %d not implemented.\n", type_src, type_dst);
589 static INT get_equivalent_declaration_index(D3DVERTEXELEMENT9 orig_declaration, D3DVERTEXELEMENT9 *declaration)
593 for (i = 0; declaration[i].Stream != 0xff; i++)
595 if (orig_declaration.Usage == declaration[i].Usage
596 && orig_declaration.UsageIndex == declaration[i].UsageIndex)
605 static HRESULT convert_vertex_buffer(ID3DXMesh *mesh_dst, ID3DXMesh *mesh_src)
608 D3DVERTEXELEMENT9 orig_declaration[MAX_FVF_DECL_SIZE] = {D3DDECL_END()};
609 D3DVERTEXELEMENT9 declaration[MAX_FVF_DECL_SIZE] = {D3DDECL_END()};
613 UINT num_vertices = mesh_src->lpVtbl->GetNumVertices(mesh_src);
614 UINT dst_vertex_size = mesh_dst->lpVtbl->GetNumBytesPerVertex(mesh_dst);
615 UINT src_vertex_size = mesh_src->lpVtbl->GetNumBytesPerVertex(mesh_src);
617 hr = mesh_src->lpVtbl->GetDeclaration(mesh_src, orig_declaration);
618 if (FAILED(hr)) return hr;
619 hr = mesh_dst->lpVtbl->GetDeclaration(mesh_dst, declaration);
620 if (FAILED(hr)) return hr;
622 hr = mesh_src->lpVtbl->LockVertexBuffer(mesh_src, D3DLOCK_READONLY, (void**)&vb_src);
623 if (FAILED(hr)) goto cleanup;
624 hr = mesh_dst->lpVtbl->LockVertexBuffer(mesh_dst, 0, (void**)&vb_dst);
625 if (FAILED(hr)) goto cleanup;
627 /* Clear all new fields by clearing the entire vertex buffer. */
628 memset(vb_dst, 0, num_vertices * dst_vertex_size);
630 for (i = 0; orig_declaration[i].Stream != 0xff; i++)
632 INT eq_idx = get_equivalent_declaration_index(orig_declaration[i], declaration);
637 for (j = 0; j < num_vertices; j++)
639 UINT idx_dst = dst_vertex_size * j + declaration[eq_idx].Offset;
640 UINT idx_src = src_vertex_size * j + orig_declaration[i].Offset;
641 UINT type_size = d3dx_decltype_size[orig_declaration[i].Type];
643 if (orig_declaration[i].Type == declaration[eq_idx].Type)
644 memcpy(&vb_dst[idx_dst], &vb_src[idx_src], type_size);
646 convert_component(&vb_dst[idx_dst], &vb_src[idx_src], declaration[eq_idx].Type, orig_declaration[i].Type);
653 if (vb_dst) mesh_dst->lpVtbl->UnlockVertexBuffer(mesh_dst);
654 if (vb_src) mesh_src->lpVtbl->UnlockVertexBuffer(mesh_src);
659 static BOOL declaration_equals(CONST D3DVERTEXELEMENT9 *declaration1, CONST D3DVERTEXELEMENT9 *declaration2)
661 UINT size1 = 0, size2 = 0;
663 /* Find the size of each declaration */
664 while (declaration1[size1].Stream != 0xff) size1++;
665 while (declaration2[size2].Stream != 0xff) size2++;
667 /* If not same size then they are definitely not equal */
671 /* Check that all components are the same */
672 if (memcmp(declaration1, declaration2, size1*sizeof(*declaration1)) == 0)
678 static HRESULT WINAPI ID3DXMeshImpl_CloneMesh(ID3DXMesh *iface, DWORD options, CONST D3DVERTEXELEMENT9 *declaration, LPDIRECT3DDEVICE9 device,
679 LPD3DXMESH *clone_mesh_out)
681 ID3DXMeshImpl *This = impl_from_ID3DXMesh(iface);
682 ID3DXMeshImpl *cloned_this;
683 ID3DXMesh *clone_mesh;
684 D3DVERTEXELEMENT9 orig_declaration[MAX_FVF_DECL_SIZE] = { D3DDECL_END() };
685 void *data_in, *data_out;
689 BOOL same_declaration;
691 TRACE("(%p)->(%x,%p,%p,%p)\n", This, options, declaration, device, clone_mesh_out);
694 return D3DERR_INVALIDCALL;
696 hr = iface->lpVtbl->GetDeclaration(iface, orig_declaration);
697 if (FAILED(hr)) return hr;
699 hr = D3DXCreateMesh(This->numfaces, This->numvertices, options & ~D3DXMESH_VB_SHARE,
700 declaration, device, &clone_mesh);
701 if (FAILED(hr)) return hr;
703 cloned_this = impl_from_ID3DXMesh(clone_mesh);
704 vertex_size = clone_mesh->lpVtbl->GetNumBytesPerVertex(clone_mesh);
705 same_declaration = declaration_equals(declaration, orig_declaration);
707 if (options & D3DXMESH_VB_SHARE) {
708 if (!same_declaration) {
709 hr = D3DERR_INVALIDCALL;
712 IDirect3DVertexBuffer9_AddRef(This->vertex_buffer);
713 /* FIXME: refactor to avoid creating a new vertex buffer */
714 IDirect3DVertexBuffer9_Release(cloned_this->vertex_buffer);
715 cloned_this->vertex_buffer = This->vertex_buffer;
716 } else if (same_declaration) {
717 hr = iface->lpVtbl->LockVertexBuffer(iface, D3DLOCK_READONLY, &data_in);
718 if (FAILED(hr)) goto error;
719 hr = clone_mesh->lpVtbl->LockVertexBuffer(clone_mesh, 0, &data_out);
721 iface->lpVtbl->UnlockVertexBuffer(iface);
724 memcpy(data_out, data_in, This->numvertices * vertex_size);
725 clone_mesh->lpVtbl->UnlockVertexBuffer(clone_mesh);
726 iface->lpVtbl->UnlockVertexBuffer(iface);
728 hr = convert_vertex_buffer(clone_mesh, iface);
729 if (FAILED(hr)) goto error;
732 hr = iface->lpVtbl->LockIndexBuffer(iface, D3DLOCK_READONLY, &data_in);
733 if (FAILED(hr)) goto error;
734 hr = clone_mesh->lpVtbl->LockIndexBuffer(clone_mesh, 0, &data_out);
736 iface->lpVtbl->UnlockIndexBuffer(iface);
739 if ((options ^ This->options) & D3DXMESH_32BIT) {
740 if (options & D3DXMESH_32BIT) {
741 for (i = 0; i < This->numfaces * 3; i++)
742 ((DWORD*)data_out)[i] = ((WORD*)data_in)[i];
744 for (i = 0; i < This->numfaces * 3; i++)
745 ((WORD*)data_out)[i] = ((DWORD*)data_in)[i];
748 memcpy(data_out, data_in, This->numfaces * 3 * (options & D3DXMESH_32BIT ? 4 : 2));
750 clone_mesh->lpVtbl->UnlockIndexBuffer(clone_mesh);
751 iface->lpVtbl->UnlockIndexBuffer(iface);
753 memcpy(cloned_this->attrib_buffer, This->attrib_buffer, This->numfaces * sizeof(*This->attrib_buffer));
755 if (This->attrib_table_size)
757 cloned_this->attrib_table_size = This->attrib_table_size;
758 cloned_this->attrib_table = HeapAlloc(GetProcessHeap(), 0, This->attrib_table_size * sizeof(*This->attrib_table));
759 if (!cloned_this->attrib_table) {
763 memcpy(cloned_this->attrib_table, This->attrib_table, This->attrib_table_size * sizeof(*This->attrib_table));
766 *clone_mesh_out = clone_mesh;
770 IUnknown_Release(clone_mesh);
774 static HRESULT WINAPI ID3DXMeshImpl_GetVertexBuffer(ID3DXMesh *iface, LPDIRECT3DVERTEXBUFFER9 *vertex_buffer)
776 ID3DXMeshImpl *This = impl_from_ID3DXMesh(iface);
778 TRACE("(%p)->(%p)\n", This, vertex_buffer);
780 if (vertex_buffer == NULL) return D3DERR_INVALIDCALL;
781 *vertex_buffer = This->vertex_buffer;
782 IDirect3DVertexBuffer9_AddRef(This->vertex_buffer);
787 static HRESULT WINAPI ID3DXMeshImpl_GetIndexBuffer(ID3DXMesh *iface, LPDIRECT3DINDEXBUFFER9 *index_buffer)
789 ID3DXMeshImpl *This = impl_from_ID3DXMesh(iface);
791 TRACE("(%p)->(%p)\n", This, index_buffer);
793 if (index_buffer == NULL) return D3DERR_INVALIDCALL;
794 *index_buffer = This->index_buffer;
795 IDirect3DIndexBuffer9_AddRef(This->index_buffer);
800 static HRESULT WINAPI ID3DXMeshImpl_LockVertexBuffer(ID3DXMesh *iface, DWORD flags, LPVOID *data)
802 ID3DXMeshImpl *This = impl_from_ID3DXMesh(iface);
804 TRACE("(%p)->(%u,%p)\n", This, flags, data);
806 return IDirect3DVertexBuffer9_Lock(This->vertex_buffer, 0, 0, data, flags);
809 static HRESULT WINAPI ID3DXMeshImpl_UnlockVertexBuffer(ID3DXMesh *iface)
811 ID3DXMeshImpl *This = impl_from_ID3DXMesh(iface);
813 TRACE("(%p)\n", This);
815 return IDirect3DVertexBuffer9_Unlock(This->vertex_buffer);
818 static HRESULT WINAPI ID3DXMeshImpl_LockIndexBuffer(ID3DXMesh *iface, DWORD flags, LPVOID *data)
820 ID3DXMeshImpl *This = impl_from_ID3DXMesh(iface);
822 TRACE("(%p)->(%u,%p)\n", This, flags, data);
824 return IDirect3DIndexBuffer9_Lock(This->index_buffer, 0, 0, data, flags);
827 static HRESULT WINAPI ID3DXMeshImpl_UnlockIndexBuffer(ID3DXMesh *iface)
829 ID3DXMeshImpl *This = impl_from_ID3DXMesh(iface);
831 TRACE("(%p)\n", This);
833 return IDirect3DIndexBuffer9_Unlock(This->index_buffer);
836 static HRESULT WINAPI ID3DXMeshImpl_GetAttributeTable(ID3DXMesh *iface, D3DXATTRIBUTERANGE *attrib_table, DWORD *attrib_table_size)
838 ID3DXMeshImpl *This = impl_from_ID3DXMesh(iface);
840 TRACE("(%p)->(%p,%p)\n", This, attrib_table, attrib_table_size);
842 if (attrib_table_size)
843 *attrib_table_size = This->attrib_table_size;
846 CopyMemory(attrib_table, This->attrib_table, This->attrib_table_size * sizeof(*attrib_table));
861 struct edge_face *entries;
864 /* Builds up a map of which face a new edge belongs to. That way the adjacency
865 * of another edge can be looked up. An edge has an adjacent face if there
866 * is an edge going in the opposite direction in the map. For example if the
867 * edge (v1, v2) belongs to face 4, and there is a mapping (v2, v1)->7, then
868 * face 4 and 7 are adjacent.
870 * Each edge might have been replaced with another edge, or none at all. There
871 * is at most one edge to face mapping, i.e. an edge can only belong to one
874 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)
879 edge_face_map->lists = HeapAlloc(GetProcessHeap(), 0, 3 * num_faces * sizeof(*edge_face_map->lists));
880 if (!edge_face_map->lists) return E_OUTOFMEMORY;
882 edge_face_map->entries = HeapAlloc(GetProcessHeap(), 0, 3 * num_faces * sizeof(*edge_face_map->entries));
883 if (!edge_face_map->entries) return E_OUTOFMEMORY;
886 /* Initialize all lists */
887 for (i = 0; i < 3 * num_faces; i++)
889 list_init(&edge_face_map->lists[i]);
891 /* Build edge face mapping */
892 for (face = 0; face < num_faces; face++)
894 for (edge = 0; edge < 3; edge++)
896 DWORD v1 = index_buffer[3*face + edge];
897 DWORD v2 = index_buffer[3*face + (edge+1)%3];
898 DWORD new_v1 = point_reps[v1]; /* What v1 has been replaced with */
899 DWORD new_v2 = point_reps[v2];
901 if (v1 != v2) /* Only map non-collapsed edges */
904 edge_face_map->entries[i].v2 = new_v2;
905 edge_face_map->entries[i].face = face;
906 list_add_head(&edge_face_map->lists[new_v1], &edge_face_map->entries[i].entry);
914 static DWORD find_adjacent_face(struct edge_face_map *edge_face_map, DWORD vertex1, DWORD vertex2, CONST DWORD num_faces)
916 struct edge_face *edge_face_ptr;
918 LIST_FOR_EACH_ENTRY(edge_face_ptr, &edge_face_map->lists[vertex2], struct edge_face, entry)
920 if (edge_face_ptr->v2 == vertex1)
921 return edge_face_ptr->face;
927 static DWORD *generate_identity_point_reps(DWORD num_vertices)
929 DWORD *id_point_reps;
932 id_point_reps = HeapAlloc(GetProcessHeap(), 0, num_vertices * sizeof(*id_point_reps));
936 for (i = 0; i < num_vertices; i++)
938 id_point_reps[i] = i;
941 return id_point_reps;
944 static HRESULT WINAPI ID3DXMeshImpl_ConvertPointRepsToAdjacency(ID3DXMesh *iface, CONST DWORD *point_reps, DWORD *adjacency)
946 ID3DXMeshImpl *This = impl_from_ID3DXMesh(iface);
948 DWORD num_faces = iface->lpVtbl->GetNumFaces(iface);
949 DWORD num_vertices = iface->lpVtbl->GetNumVertices(iface);
950 DWORD options = iface->lpVtbl->GetOptions(iface);
951 BOOL indices_are_16_bit = !(options & D3DXMESH_32BIT);
956 struct edge_face_map edge_face_map = {0};
957 CONST DWORD *point_reps_ptr = NULL;
958 DWORD *id_point_reps = NULL;
960 TRACE("(%p)->(%p,%p)\n", This, point_reps, adjacency);
962 if (!adjacency) return D3DERR_INVALIDCALL;
964 if (!point_reps) /* Identity point reps */
966 id_point_reps = generate_identity_point_reps(num_vertices);
973 point_reps_ptr = id_point_reps;
977 point_reps_ptr = point_reps;
980 hr = iface->lpVtbl->LockIndexBuffer(iface, D3DLOCK_READONLY, &ib_ptr);
981 if (FAILED(hr)) goto cleanup;
983 if (indices_are_16_bit)
985 /* Widen 16 bit to 32 bit */
987 WORD *ib_16bit = ib_ptr;
988 ib = HeapAlloc(GetProcessHeap(), 0, 3 * num_faces * sizeof(DWORD));
994 for (i = 0; i < 3 * num_faces; i++)
1004 hr = init_edge_face_map(&edge_face_map, ib, point_reps_ptr, num_faces);
1005 if (FAILED(hr)) goto cleanup;
1007 /* Create adjacency */
1008 for (face = 0; face < num_faces; face++)
1010 for (edge = 0; edge < 3; edge++)
1012 DWORD v1 = ib[3*face + edge];
1013 DWORD v2 = ib[3*face + (edge+1)%3];
1014 DWORD new_v1 = point_reps_ptr[v1];
1015 DWORD new_v2 = point_reps_ptr[v2];
1018 adj_face = find_adjacent_face(&edge_face_map, new_v1, new_v2, num_faces);
1019 adjacency[3*face + edge] = adj_face;
1025 HeapFree(GetProcessHeap(), 0, id_point_reps);
1026 if (indices_are_16_bit) HeapFree(GetProcessHeap(), 0, ib);
1027 HeapFree(GetProcessHeap(), 0, edge_face_map.lists);
1028 HeapFree(GetProcessHeap(), 0, edge_face_map.entries);
1029 if(ib_ptr) iface->lpVtbl->UnlockIndexBuffer(iface);
1033 /* ConvertAdjacencyToPointReps helper function.
1035 * Goes around the edges of each face and replaces the vertices in any adjacent
1036 * face's edge with its own vertices(if its vertices have a lower index). This
1037 * way as few as possible low index vertices are shared among the faces. The
1038 * re-ordered index buffer is stored in new_indices.
1040 * The vertices in a point representation must be ordered sequentially, e.g.
1041 * index 5 holds the index of the vertex that replaces vertex 5, i.e. if
1042 * vertex 5 is replaced by vertex 3 then index 5 would contain 3. If no vertex
1043 * replaces it, then it contains the same number as the index itself, e.g.
1044 * index 5 would contain 5. */
1045 static HRESULT propagate_face_vertices(CONST DWORD *adjacency, DWORD *point_reps,
1046 CONST DWORD *indices, DWORD *new_indices,
1047 CONST DWORD face, CONST DWORD numfaces)
1049 const unsigned int VERTS_PER_FACE = 3;
1050 DWORD edge, opp_edge;
1051 DWORD face_base = VERTS_PER_FACE * face;
1053 for (edge = 0; edge < VERTS_PER_FACE; edge++)
1055 DWORD adj_face = adjacency[face_base + edge];
1056 DWORD adj_face_base;
1058 if (adj_face == -1) /* No adjacent face. */
1060 else if (adj_face >= numfaces)
1062 /* This throws exception on Windows */
1063 WARN("Index out of bounds. Got %d expected less than %d.\n",
1064 adj_face, numfaces);
1065 return D3DERR_INVALIDCALL;
1067 adj_face_base = 3 * adj_face;
1069 /* Find opposite edge in adjacent face. */
1070 for (opp_edge = 0; opp_edge < VERTS_PER_FACE; opp_edge++)
1072 DWORD opp_edge_index = adj_face_base + opp_edge;
1073 if (adjacency[opp_edge_index] == face)
1074 break; /* Found opposite edge. */
1077 /* Replaces vertices in opposite edge with vertices from current edge. */
1078 for (i = 0; i < 2; i++)
1080 DWORD from = face_base + (edge + (1 - i)) % VERTS_PER_FACE;
1081 DWORD to = adj_face_base + (opp_edge + i) % VERTS_PER_FACE;
1083 /* Propagate lowest index. */
1084 if (new_indices[to] > new_indices[from])
1086 new_indices[to] = new_indices[from];
1087 point_reps[indices[to]] = new_indices[from];
1095 static HRESULT WINAPI ID3DXMeshImpl_ConvertAdjacencyToPointReps(ID3DXMesh *iface, CONST DWORD *adjacency, DWORD *point_reps)
1100 DWORD *indices = NULL;
1101 WORD *indices_16bit = NULL;
1102 DWORD *new_indices = NULL;
1103 const unsigned int VERTS_PER_FACE = 3;
1105 ID3DXMeshImpl *This = impl_from_ID3DXMesh(iface);
1107 TRACE("(%p)->(%p,%p)\n", This, adjacency, point_reps);
1111 WARN("NULL adjacency.\n");
1112 hr = D3DERR_INVALIDCALL;
1118 WARN("NULL point_reps.\n");
1119 hr = D3DERR_INVALIDCALL;
1123 /* Should never happen as CreateMesh does not allow meshes with 0 faces */
1124 if (This->numfaces == 0)
1126 ERR("Number of faces was zero.\n");
1127 hr = D3DERR_INVALIDCALL;
1131 new_indices = HeapAlloc(GetProcessHeap(), 0, VERTS_PER_FACE * This->numfaces * sizeof(*indices));
1138 if (This->options & D3DXMESH_32BIT)
1140 hr = iface->lpVtbl->LockIndexBuffer(iface, D3DLOCK_READONLY, (void**)&indices);
1141 if (FAILED(hr)) goto cleanup;
1142 memcpy(new_indices, indices, VERTS_PER_FACE * This->numfaces * sizeof(*indices));
1146 /* Make a widening copy of indices_16bit into indices and new_indices
1147 * in order to re-use the helper function */
1148 hr = iface->lpVtbl->LockIndexBuffer(iface, D3DLOCK_READONLY, (void**)&indices_16bit);
1149 if (FAILED(hr)) goto cleanup;
1150 indices = HeapAlloc(GetProcessHeap(), 0, VERTS_PER_FACE * This->numfaces * sizeof(*indices));
1156 for (i = 0; i < VERTS_PER_FACE * This->numfaces; i++)
1158 new_indices[i] = indices_16bit[i];
1159 indices[i] = indices_16bit[i];
1163 /* Vertices are ordered sequentially in the point representation. */
1164 for (i = 0; i < This->numvertices; i++)
1169 /* Propagate vertices with low indices so as few vertices as possible
1170 * are used in the mesh.
1172 for (face = 0; face < This->numfaces; face++)
1174 hr = propagate_face_vertices(adjacency, point_reps, indices, new_indices, face, This->numfaces);
1175 if (FAILED(hr)) goto cleanup;
1177 /* Go in opposite direction to catch all face orderings */
1178 for (face = 0; face < This->numfaces; face++)
1180 hr = propagate_face_vertices(adjacency, point_reps,
1181 indices, new_indices,
1182 (This->numfaces - 1) - face, This->numfaces);
1183 if (FAILED(hr)) goto cleanup;
1188 if (This->options & D3DXMESH_32BIT)
1190 if (indices) iface->lpVtbl->UnlockIndexBuffer(iface);
1194 if (indices_16bit) iface->lpVtbl->UnlockIndexBuffer(iface);
1195 HeapFree(GetProcessHeap(), 0, indices);
1197 HeapFree(GetProcessHeap(), 0, new_indices);
1201 struct vertex_metadata {
1204 DWORD first_shared_index;
1207 static int compare_vertex_keys(const void *a, const void *b)
1209 const struct vertex_metadata *left = a;
1210 const struct vertex_metadata *right = b;
1211 if (left->key == right->key)
1213 return left->key < right->key ? -1 : 1;
1216 static HRESULT WINAPI ID3DXMeshImpl_GenerateAdjacency(ID3DXMesh *iface, FLOAT epsilon, DWORD *adjacency)
1218 ID3DXMeshImpl *This = impl_from_ID3DXMesh(iface);
1220 BYTE *vertices = NULL;
1221 const DWORD *indices = NULL;
1224 /* sort the vertices by (x + y + z) to quickly find coincident vertices */
1225 struct vertex_metadata *sorted_vertices;
1226 /* shared_indices links together identical indices in the index buffer so
1227 * that adjacency checks can be limited to faces sharing a vertex */
1228 DWORD *shared_indices = NULL;
1229 const FLOAT epsilon_sq = epsilon * epsilon;
1232 TRACE("(%p)->(%f,%p)\n", This, epsilon, adjacency);
1235 return D3DERR_INVALIDCALL;
1237 buffer_size = This->numfaces * 3 * sizeof(*shared_indices) + This->numvertices * sizeof(*sorted_vertices);
1238 if (!(This->options & D3DXMESH_32BIT))
1239 buffer_size += This->numfaces * 3 * sizeof(*indices);
1240 shared_indices = HeapAlloc(GetProcessHeap(), 0, buffer_size);
1241 if (!shared_indices)
1242 return E_OUTOFMEMORY;
1243 sorted_vertices = (struct vertex_metadata*)(shared_indices + This->numfaces * 3);
1245 hr = iface->lpVtbl->LockVertexBuffer(iface, D3DLOCK_READONLY, (void**)&vertices);
1246 if (FAILED(hr)) goto cleanup;
1247 hr = iface->lpVtbl->LockIndexBuffer(iface, D3DLOCK_READONLY, (void**)&indices);
1248 if (FAILED(hr)) goto cleanup;
1250 if (!(This->options & D3DXMESH_32BIT)) {
1251 const WORD *word_indices = (const WORD*)indices;
1252 DWORD *dword_indices = (DWORD*)(sorted_vertices + This->numvertices);
1253 indices = dword_indices;
1254 for (i = 0; i < This->numfaces * 3; i++)
1255 *dword_indices++ = *word_indices++;
1258 vertex_size = iface->lpVtbl->GetNumBytesPerVertex(iface);
1259 for (i = 0; i < This->numvertices; i++) {
1260 D3DXVECTOR3 *vertex = (D3DXVECTOR3*)(vertices + vertex_size * i);
1261 sorted_vertices[i].first_shared_index = -1;
1262 sorted_vertices[i].key = vertex->x + vertex->y + vertex->z;
1263 sorted_vertices[i].vertex_index = i;
1265 for (i = 0; i < This->numfaces * 3; i++) {
1266 DWORD *first_shared_index = &sorted_vertices[indices[i]].first_shared_index;
1267 shared_indices[i] = *first_shared_index;
1268 *first_shared_index = i;
1271 qsort(sorted_vertices, This->numvertices, sizeof(*sorted_vertices), compare_vertex_keys);
1273 for (i = 0; i < This->numvertices; i++) {
1274 struct vertex_metadata *sorted_vertex_a = &sorted_vertices[i];
1275 D3DXVECTOR3 *vertex_a = (D3DXVECTOR3*)(vertices + sorted_vertex_a->vertex_index * vertex_size);
1276 DWORD shared_index_a = sorted_vertex_a->first_shared_index;
1278 while (shared_index_a != -1) {
1280 DWORD shared_index_b = shared_indices[shared_index_a];
1281 struct vertex_metadata *sorted_vertex_b = sorted_vertex_a;
1284 while (shared_index_b != -1) {
1285 /* faces are adjacent if they have another coincident vertex */
1286 DWORD base_a = (shared_index_a / 3) * 3;
1287 DWORD base_b = (shared_index_b / 3) * 3;
1288 BOOL adjacent = FALSE;
1291 for (k = 0; k < 3; k++) {
1292 if (adjacency[base_b + k] == shared_index_a / 3) {
1298 for (k = 1; k <= 2; k++) {
1299 DWORD vertex_index_a = base_a + (shared_index_a + k) % 3;
1300 DWORD vertex_index_b = base_b + (shared_index_b + (3 - k)) % 3;
1301 adjacent = indices[vertex_index_a] == indices[vertex_index_b];
1302 if (!adjacent && epsilon >= 0.0f) {
1303 D3DXVECTOR3 delta = {0.0f, 0.0f, 0.0f};
1306 D3DXVec3Subtract(&delta,
1307 (D3DXVECTOR3*)(vertices + indices[vertex_index_a] * vertex_size),
1308 (D3DXVECTOR3*)(vertices + indices[vertex_index_b] * vertex_size));
1309 length_sq = D3DXVec3LengthSq(&delta);
1310 adjacent = epsilon == 0.0f ? length_sq == 0.0f : length_sq < epsilon_sq;
1313 DWORD adj_a = base_a + 2 - (vertex_index_a + shared_index_a + 1) % 3;
1314 DWORD adj_b = base_b + 2 - (vertex_index_b + shared_index_b + 1) % 3;
1315 if (adjacency[adj_a] == -1 && adjacency[adj_b] == -1) {
1316 adjacency[adj_a] = base_b / 3;
1317 adjacency[adj_b] = base_a / 3;
1324 shared_index_b = shared_indices[shared_index_b];
1326 while (++j < This->numvertices) {
1327 D3DXVECTOR3 *vertex_b;
1330 if (sorted_vertex_b->key - sorted_vertex_a->key > epsilon * 3.0f) {
1331 /* no more coincident vertices to try */
1332 j = This->numvertices;
1335 /* check for coincidence */
1336 vertex_b = (D3DXVECTOR3*)(vertices + sorted_vertex_b->vertex_index * vertex_size);
1337 if (fabsf(vertex_a->x - vertex_b->x) <= epsilon &&
1338 fabsf(vertex_a->y - vertex_b->y) <= epsilon &&
1339 fabsf(vertex_a->z - vertex_b->z) <= epsilon)
1344 if (j >= This->numvertices)
1346 shared_index_b = sorted_vertex_b->first_shared_index;
1349 sorted_vertex_a->first_shared_index = shared_indices[sorted_vertex_a->first_shared_index];
1350 shared_index_a = sorted_vertex_a->first_shared_index;
1356 if (indices) iface->lpVtbl->UnlockIndexBuffer(iface);
1357 if (vertices) iface->lpVtbl->UnlockVertexBuffer(iface);
1358 HeapFree(GetProcessHeap(), 0, shared_indices);
1362 static HRESULT WINAPI ID3DXMeshImpl_UpdateSemantics(ID3DXMesh *iface, D3DVERTEXELEMENT9 declaration[MAX_FVF_DECL_SIZE])
1365 ID3DXMeshImpl *This = impl_from_ID3DXMesh(iface);
1366 UINT vertex_declaration_size;
1369 TRACE("(%p)->(%p)\n", This, declaration);
1373 WARN("Invalid declaration. Can't use NULL declaration.\n");
1374 return D3DERR_INVALIDCALL;
1377 /* New declaration must be same size as original */
1378 vertex_declaration_size = D3DXGetDeclVertexSize(declaration, declaration[0].Stream);
1379 if (vertex_declaration_size != This->vertex_declaration_size)
1381 WARN("Invalid declaration. New vertex size does not match the original vertex size.\n");
1382 return D3DERR_INVALIDCALL;
1385 /* New declaration must not contain non-zero Stream value */
1386 for (i = 0; declaration[i].Stream != 0xff; i++)
1388 if (declaration[i].Stream != 0)
1390 WARN("Invalid declaration. New declaration contains non-zero Stream value.\n");
1391 return D3DERR_INVALIDCALL;
1395 This->num_elem = i + 1;
1396 copy_declaration(This->cached_declaration, declaration, This->num_elem);
1398 if (This->vertex_declaration)
1399 IDirect3DVertexDeclaration9_Release(This->vertex_declaration);
1401 /* An application can pass an invalid declaration to UpdateSemantics and
1402 * still expect D3D_OK (see tests). If the declaration is invalid, then
1403 * subsequent calls to DrawSubset will fail. This is handled by setting the
1404 * vertex declaration to NULL.
1405 * GetDeclaration, GetNumBytesPerVertex must, however, use the new
1406 * invalid declaration. This is handled by them using the cached vertex
1407 * declaration instead of the actual vertex declaration.
1409 hr = IDirect3DDevice9_CreateVertexDeclaration(This->device,
1411 &This->vertex_declaration);
1414 WARN("Using invalid declaration. Calls to DrawSubset will fail.\n");
1415 This->vertex_declaration = NULL;
1422 static HRESULT WINAPI ID3DXMeshImpl_LockAttributeBuffer(ID3DXMesh *iface, DWORD flags, DWORD **data)
1424 ID3DXMeshImpl *This = impl_from_ID3DXMesh(iface);
1426 TRACE("(%p)->(%u,%p)\n", This, flags, data);
1428 InterlockedIncrement(&This->attrib_buffer_lock_count);
1430 if (!(flags & D3DLOCK_READONLY)) {
1431 D3DXATTRIBUTERANGE *attrib_table = This->attrib_table;
1432 This->attrib_table_size = 0;
1433 This->attrib_table = NULL;
1434 HeapFree(GetProcessHeap(), 0, attrib_table);
1437 *data = This->attrib_buffer;
1442 static HRESULT WINAPI ID3DXMeshImpl_UnlockAttributeBuffer(ID3DXMesh *iface)
1444 ID3DXMeshImpl *This = impl_from_ID3DXMesh(iface);
1447 TRACE("(%p)\n", This);
1449 lock_count = InterlockedDecrement(&This->attrib_buffer_lock_count);
1451 if (lock_count < 0) {
1452 InterlockedIncrement(&This->attrib_buffer_lock_count);
1453 return D3DERR_INVALIDCALL;
1459 static HRESULT WINAPI ID3DXMeshImpl_Optimize(ID3DXMesh *iface, DWORD flags, CONST DWORD *adjacency_in, DWORD *adjacency_out,
1460 DWORD *face_remap, LPD3DXBUFFER *vertex_remap, LPD3DXMESH *opt_mesh)
1462 ID3DXMeshImpl *This = impl_from_ID3DXMesh(iface);
1464 D3DVERTEXELEMENT9 declaration[MAX_FVF_DECL_SIZE] = { D3DDECL_END() };
1465 ID3DXMesh *optimized_mesh;
1467 TRACE("(%p)->(%x,%p,%p,%p,%p,%p)\n", This, flags, adjacency_in, adjacency_out, face_remap, vertex_remap, opt_mesh);
1470 return D3DERR_INVALIDCALL;
1472 hr = iface->lpVtbl->GetDeclaration(iface, declaration);
1473 if (FAILED(hr)) return hr;
1475 hr = iface->lpVtbl->CloneMesh(iface, This->options, declaration, This->device, &optimized_mesh);
1476 if (FAILED(hr)) return hr;
1478 hr = optimized_mesh->lpVtbl->OptimizeInplace(optimized_mesh, flags, adjacency_in, adjacency_out, face_remap, vertex_remap);
1480 *opt_mesh = optimized_mesh;
1482 IUnknown_Release(optimized_mesh);
1486 /* Creates a vertex_remap that removes unused vertices.
1487 * Indices are updated according to the vertex_remap. */
1488 static HRESULT compact_mesh(ID3DXMeshImpl *This, DWORD *indices, DWORD *new_num_vertices, ID3DXBuffer **vertex_remap)
1491 DWORD *vertex_remap_ptr;
1492 DWORD num_used_vertices;
1495 hr = D3DXCreateBuffer(This->numvertices * sizeof(DWORD), vertex_remap);
1496 if (FAILED(hr)) return hr;
1497 vertex_remap_ptr = ID3DXBuffer_GetBufferPointer(*vertex_remap);
1499 for (i = 0; i < This->numfaces * 3; i++)
1500 vertex_remap_ptr[indices[i]] = 1;
1502 /* create old->new vertex mapping */
1503 num_used_vertices = 0;
1504 for (i = 0; i < This->numvertices; i++) {
1505 if (vertex_remap_ptr[i])
1506 vertex_remap_ptr[i] = num_used_vertices++;
1508 vertex_remap_ptr[i] = -1;
1510 /* convert indices */
1511 for (i = 0; i < This->numfaces * 3; i++)
1512 indices[i] = vertex_remap_ptr[indices[i]];
1514 /* create new->old vertex mapping */
1515 num_used_vertices = 0;
1516 for (i = 0; i < This->numvertices; i++) {
1517 if (vertex_remap_ptr[i] != -1)
1518 vertex_remap_ptr[num_used_vertices++] = i;
1520 for (i = num_used_vertices; i < This->numvertices; i++)
1521 vertex_remap_ptr[i] = -1;
1523 *new_num_vertices = num_used_vertices;
1528 /* count the number of unique attribute values in a sorted attribute buffer */
1529 static DWORD count_attributes(const DWORD *attrib_buffer, DWORD numfaces)
1531 DWORD last_attribute = attrib_buffer[0];
1532 DWORD attrib_table_size = 1;
1534 for (i = 1; i < numfaces; i++) {
1535 if (attrib_buffer[i] != last_attribute) {
1536 last_attribute = attrib_buffer[i];
1537 attrib_table_size++;
1540 return attrib_table_size;
1543 static void fill_attribute_table(DWORD *attrib_buffer, DWORD numfaces, void *indices,
1544 BOOL is_32bit_indices, D3DXATTRIBUTERANGE *attrib_table)
1546 DWORD attrib_table_size = 0;
1547 DWORD last_attribute = attrib_buffer[0];
1548 DWORD min_vertex, max_vertex;
1551 attrib_table[0].AttribId = last_attribute;
1552 attrib_table[0].FaceStart = 0;
1553 min_vertex = (DWORD)-1;
1555 for (i = 0; i < numfaces; i++) {
1558 if (attrib_buffer[i] != last_attribute) {
1559 last_attribute = attrib_buffer[i];
1560 attrib_table[attrib_table_size].FaceCount = i - attrib_table[attrib_table_size].FaceStart;
1561 attrib_table[attrib_table_size].VertexStart = min_vertex;
1562 attrib_table[attrib_table_size].VertexCount = max_vertex - min_vertex + 1;
1563 attrib_table_size++;
1564 attrib_table[attrib_table_size].AttribId = attrib_buffer[i];
1565 attrib_table[attrib_table_size].FaceStart = i;
1566 min_vertex = (DWORD)-1;
1569 for (j = 0; j < 3; j++) {
1570 DWORD vertex_index = is_32bit_indices ? ((DWORD*)indices)[i * 3 + j] : ((WORD*)indices)[i * 3 + j];
1571 if (vertex_index < min_vertex)
1572 min_vertex = vertex_index;
1573 if (vertex_index > max_vertex)
1574 max_vertex = vertex_index;
1577 attrib_table[attrib_table_size].FaceCount = i - attrib_table[attrib_table_size].FaceStart;
1578 attrib_table[attrib_table_size].VertexStart = min_vertex;
1579 attrib_table[attrib_table_size].VertexCount = max_vertex - min_vertex + 1;
1580 attrib_table_size++;
1583 static int attrib_entry_compare(const DWORD **a, const DWORD **b)
1585 const DWORD *ptr_a = *a;
1586 const DWORD *ptr_b = *b;
1587 int delta = *ptr_a - *ptr_b;
1592 delta = ptr_a - ptr_b; /* for stable sort */
1596 /* Create face_remap, a new attribute buffer for attribute sort optimization. */
1597 static HRESULT remap_faces_for_attrsort(ID3DXMeshImpl *This, const DWORD *indices,
1598 const DWORD *attrib_buffer, DWORD **sorted_attrib_buffer, DWORD **face_remap)
1600 const DWORD **sorted_attrib_ptr_buffer = NULL;
1603 *face_remap = HeapAlloc(GetProcessHeap(), 0, This->numfaces * sizeof(**face_remap));
1604 sorted_attrib_ptr_buffer = HeapAlloc(GetProcessHeap(), 0, This->numfaces * sizeof(*sorted_attrib_ptr_buffer));
1605 if (!*face_remap || !sorted_attrib_ptr_buffer) {
1606 HeapFree(GetProcessHeap(), 0, sorted_attrib_ptr_buffer);
1607 return E_OUTOFMEMORY;
1609 for (i = 0; i < This->numfaces; i++)
1610 sorted_attrib_ptr_buffer[i] = &attrib_buffer[i];
1611 qsort(sorted_attrib_ptr_buffer, This->numfaces, sizeof(*sorted_attrib_ptr_buffer),
1612 (int(*)(const void *, const void *))attrib_entry_compare);
1614 for (i = 0; i < This->numfaces; i++)
1616 DWORD old_face = sorted_attrib_ptr_buffer[i] - attrib_buffer;
1617 (*face_remap)[old_face] = i;
1620 /* overwrite sorted_attrib_ptr_buffer with the values themselves */
1621 *sorted_attrib_buffer = (DWORD*)sorted_attrib_ptr_buffer;
1622 for (i = 0; i < This->numfaces; i++)
1623 (*sorted_attrib_buffer)[(*face_remap)[i]] = attrib_buffer[i];
1628 static HRESULT WINAPI ID3DXMeshImpl_OptimizeInplace(ID3DXMesh *iface, DWORD flags, CONST DWORD *adjacency_in, DWORD *adjacency_out,
1629 DWORD *face_remap_out, LPD3DXBUFFER *vertex_remap_out)
1631 ID3DXMeshImpl *This = impl_from_ID3DXMesh(iface);
1632 void *indices = NULL;
1633 DWORD *attrib_buffer = NULL;
1635 ID3DXBuffer *vertex_remap = NULL;
1636 DWORD *face_remap = NULL; /* old -> new mapping */
1637 DWORD *dword_indices = NULL;
1638 DWORD new_num_vertices = 0;
1639 DWORD new_num_alloc_vertices = 0;
1640 IDirect3DVertexBuffer9 *vertex_buffer = NULL;
1641 DWORD *sorted_attrib_buffer = NULL;
1644 TRACE("(%p)->(%x,%p,%p,%p,%p)\n", This, flags, adjacency_in, adjacency_out, face_remap_out, vertex_remap_out);
1647 return D3DERR_INVALIDCALL;
1648 if (!adjacency_in && (flags & (D3DXMESHOPT_VERTEXCACHE | D3DXMESHOPT_STRIPREORDER)))
1649 return D3DERR_INVALIDCALL;
1650 if ((flags & (D3DXMESHOPT_VERTEXCACHE | D3DXMESHOPT_STRIPREORDER)) == (D3DXMESHOPT_VERTEXCACHE | D3DXMESHOPT_STRIPREORDER))
1651 return D3DERR_INVALIDCALL;
1653 if (flags & (D3DXMESHOPT_VERTEXCACHE | D3DXMESHOPT_STRIPREORDER))
1655 if (flags & D3DXMESHOPT_VERTEXCACHE)
1656 FIXME("D3DXMESHOPT_VERTEXCACHE not implemented.\n");
1657 if (flags & D3DXMESHOPT_STRIPREORDER)
1658 FIXME("D3DXMESHOPT_STRIPREORDER not implemented.\n");
1662 hr = iface->lpVtbl->LockIndexBuffer(iface, 0, &indices);
1663 if (FAILED(hr)) goto cleanup;
1665 dword_indices = HeapAlloc(GetProcessHeap(), 0, This->numfaces * 3 * sizeof(DWORD));
1666 if (!dword_indices) return E_OUTOFMEMORY;
1667 if (This->options & D3DXMESH_32BIT) {
1668 memcpy(dword_indices, indices, This->numfaces * 3 * sizeof(DWORD));
1670 WORD *word_indices = indices;
1671 for (i = 0; i < This->numfaces * 3; i++)
1672 dword_indices[i] = *word_indices++;
1675 if ((flags & (D3DXMESHOPT_COMPACT | D3DXMESHOPT_IGNOREVERTS | D3DXMESHOPT_ATTRSORT)) == D3DXMESHOPT_COMPACT)
1677 new_num_alloc_vertices = This->numvertices;
1678 hr = compact_mesh(This, dword_indices, &new_num_vertices, &vertex_remap);
1679 if (FAILED(hr)) goto cleanup;
1680 } else if (flags & D3DXMESHOPT_ATTRSORT) {
1681 if (!(flags & D3DXMESHOPT_IGNOREVERTS))
1683 FIXME("D3DXMESHOPT_ATTRSORT vertex reordering not implemented.\n");
1688 hr = iface->lpVtbl->LockAttributeBuffer(iface, 0, &attrib_buffer);
1689 if (FAILED(hr)) goto cleanup;
1691 hr = remap_faces_for_attrsort(This, dword_indices, attrib_buffer, &sorted_attrib_buffer, &face_remap);
1692 if (FAILED(hr)) goto cleanup;
1697 /* reorder the vertices using vertex_remap */
1698 D3DVERTEXBUFFER_DESC vertex_desc;
1699 DWORD *vertex_remap_ptr = ID3DXBuffer_GetBufferPointer(vertex_remap);
1700 DWORD vertex_size = iface->lpVtbl->GetNumBytesPerVertex(iface);
1701 BYTE *orig_vertices;
1704 hr = IDirect3DVertexBuffer9_GetDesc(This->vertex_buffer, &vertex_desc);
1705 if (FAILED(hr)) goto cleanup;
1707 hr = IDirect3DDevice9_CreateVertexBuffer(This->device, new_num_alloc_vertices * vertex_size,
1708 vertex_desc.Usage, This->fvf, vertex_desc.Pool, &vertex_buffer, NULL);
1709 if (FAILED(hr)) goto cleanup;
1711 hr = IDirect3DVertexBuffer9_Lock(This->vertex_buffer, 0, 0, (void**)&orig_vertices, D3DLOCK_READONLY);
1712 if (FAILED(hr)) goto cleanup;
1714 hr = IDirect3DVertexBuffer9_Lock(vertex_buffer, 0, 0, (void**)&new_vertices, 0);
1716 IDirect3DVertexBuffer9_Unlock(This->vertex_buffer);
1720 for (i = 0; i < new_num_vertices; i++)
1721 memcpy(new_vertices + i * vertex_size, orig_vertices + vertex_remap_ptr[i] * vertex_size, vertex_size);
1723 IDirect3DVertexBuffer9_Unlock(This->vertex_buffer);
1724 IDirect3DVertexBuffer9_Unlock(vertex_buffer);
1725 } else if (vertex_remap_out) {
1726 DWORD *vertex_remap_ptr;
1728 hr = D3DXCreateBuffer(This->numvertices * sizeof(DWORD), &vertex_remap);
1729 if (FAILED(hr)) goto cleanup;
1730 vertex_remap_ptr = ID3DXBuffer_GetBufferPointer(vertex_remap);
1731 for (i = 0; i < This->numvertices; i++)
1732 *vertex_remap_ptr++ = i;
1735 if (flags & D3DXMESHOPT_ATTRSORT)
1737 D3DXATTRIBUTERANGE *attrib_table;
1738 DWORD attrib_table_size;
1740 attrib_table_size = count_attributes(sorted_attrib_buffer, This->numfaces);
1741 attrib_table = HeapAlloc(GetProcessHeap(), 0, attrib_table_size * sizeof(*attrib_table));
1742 if (!attrib_table) {
1747 memcpy(attrib_buffer, sorted_attrib_buffer, This->numfaces * sizeof(*attrib_buffer));
1749 /* reorder the indices using face_remap */
1750 if (This->options & D3DXMESH_32BIT) {
1751 for (i = 0; i < This->numfaces; i++)
1752 memcpy((DWORD*)indices + face_remap[i] * 3, dword_indices + i * 3, 3 * sizeof(DWORD));
1754 WORD *word_indices = indices;
1755 for (i = 0; i < This->numfaces; i++) {
1756 DWORD new_pos = face_remap[i] * 3;
1757 DWORD old_pos = i * 3;
1758 word_indices[new_pos++] = dword_indices[old_pos++];
1759 word_indices[new_pos++] = dword_indices[old_pos++];
1760 word_indices[new_pos] = dword_indices[old_pos];
1764 fill_attribute_table(attrib_buffer, This->numfaces, indices,
1765 This->options & D3DXMESH_32BIT, attrib_table);
1767 HeapFree(GetProcessHeap(), 0, This->attrib_table);
1768 This->attrib_table = attrib_table;
1769 This->attrib_table_size = attrib_table_size;
1771 if (This->options & D3DXMESH_32BIT) {
1772 memcpy(indices, dword_indices, This->numfaces * 3 * sizeof(DWORD));
1774 WORD *word_indices = indices;
1775 for (i = 0; i < This->numfaces * 3; i++)
1776 *word_indices++ = dword_indices[i];
1780 if (adjacency_out) {
1782 for (i = 0; i < This->numfaces; i++) {
1783 DWORD old_pos = i * 3;
1784 DWORD new_pos = face_remap[i] * 3;
1785 adjacency_out[new_pos++] = face_remap[adjacency_in[old_pos++]];
1786 adjacency_out[new_pos++] = face_remap[adjacency_in[old_pos++]];
1787 adjacency_out[new_pos++] = face_remap[adjacency_in[old_pos++]];
1790 memcpy(adjacency_out, adjacency_in, This->numfaces * 3 * sizeof(*adjacency_out));
1793 if (face_remap_out) {
1795 for (i = 0; i < This->numfaces; i++)
1796 face_remap_out[face_remap[i]] = i;
1798 for (i = 0; i < This->numfaces; i++)
1799 face_remap_out[i] = i;
1802 if (vertex_remap_out)
1803 *vertex_remap_out = vertex_remap;
1804 vertex_remap = NULL;
1806 if (vertex_buffer) {
1807 IDirect3DVertexBuffer9_Release(This->vertex_buffer);
1808 This->vertex_buffer = vertex_buffer;
1809 vertex_buffer = NULL;
1810 This->numvertices = new_num_vertices;
1815 HeapFree(GetProcessHeap(), 0, sorted_attrib_buffer);
1816 HeapFree(GetProcessHeap(), 0, face_remap);
1817 HeapFree(GetProcessHeap(), 0, dword_indices);
1818 if (vertex_remap) ID3DXBuffer_Release(vertex_remap);
1819 if (vertex_buffer) IDirect3DVertexBuffer9_Release(vertex_buffer);
1820 if (attrib_buffer) iface->lpVtbl->UnlockAttributeBuffer(iface);
1821 if (indices) iface->lpVtbl->UnlockIndexBuffer(iface);
1825 static HRESULT WINAPI ID3DXMeshImpl_SetAttributeTable(ID3DXMesh *iface, CONST D3DXATTRIBUTERANGE *attrib_table, DWORD attrib_table_size)
1827 ID3DXMeshImpl *This = impl_from_ID3DXMesh(iface);
1828 D3DXATTRIBUTERANGE *new_table = NULL;
1830 TRACE("(%p)->(%p,%u)\n", This, attrib_table, attrib_table_size);
1832 if (attrib_table_size) {
1833 size_t size = attrib_table_size * sizeof(*attrib_table);
1835 new_table = HeapAlloc(GetProcessHeap(), 0, size);
1837 return E_OUTOFMEMORY;
1839 CopyMemory(new_table, attrib_table, size);
1840 } else if (attrib_table) {
1841 return D3DERR_INVALIDCALL;
1843 HeapFree(GetProcessHeap(), 0, This->attrib_table);
1844 This->attrib_table = new_table;
1845 This->attrib_table_size = attrib_table_size;
1850 static const struct ID3DXMeshVtbl D3DXMesh_Vtbl =
1852 /*** IUnknown methods ***/
1853 ID3DXMeshImpl_QueryInterface,
1854 ID3DXMeshImpl_AddRef,
1855 ID3DXMeshImpl_Release,
1856 /*** ID3DXBaseMesh ***/
1857 ID3DXMeshImpl_DrawSubset,
1858 ID3DXMeshImpl_GetNumFaces,
1859 ID3DXMeshImpl_GetNumVertices,
1860 ID3DXMeshImpl_GetFVF,
1861 ID3DXMeshImpl_GetDeclaration,
1862 ID3DXMeshImpl_GetNumBytesPerVertex,
1863 ID3DXMeshImpl_GetOptions,
1864 ID3DXMeshImpl_GetDevice,
1865 ID3DXMeshImpl_CloneMeshFVF,
1866 ID3DXMeshImpl_CloneMesh,
1867 ID3DXMeshImpl_GetVertexBuffer,
1868 ID3DXMeshImpl_GetIndexBuffer,
1869 ID3DXMeshImpl_LockVertexBuffer,
1870 ID3DXMeshImpl_UnlockVertexBuffer,
1871 ID3DXMeshImpl_LockIndexBuffer,
1872 ID3DXMeshImpl_UnlockIndexBuffer,
1873 ID3DXMeshImpl_GetAttributeTable,
1874 ID3DXMeshImpl_ConvertPointRepsToAdjacency,
1875 ID3DXMeshImpl_ConvertAdjacencyToPointReps,
1876 ID3DXMeshImpl_GenerateAdjacency,
1877 ID3DXMeshImpl_UpdateSemantics,
1879 ID3DXMeshImpl_LockAttributeBuffer,
1880 ID3DXMeshImpl_UnlockAttributeBuffer,
1881 ID3DXMeshImpl_Optimize,
1882 ID3DXMeshImpl_OptimizeInplace,
1883 ID3DXMeshImpl_SetAttributeTable
1886 /*************************************************************************
1889 BOOL WINAPI D3DXBoxBoundProbe(CONST D3DXVECTOR3 *pmin, CONST D3DXVECTOR3 *pmax, CONST D3DXVECTOR3 *prayposition, CONST D3DXVECTOR3 *praydirection)
1891 /* Algorithm taken from the article: An Efficient and Robust Ray-Box Intersection Algorithm
1892 Amy Williams University of Utah
1893 Steve Barrus University of Utah
1894 R. Keith Morley University of Utah
1895 Peter Shirley University of Utah
1897 International Conference on Computer Graphics and Interactive Techniques archive
1898 ACM SIGGRAPH 2005 Courses
1899 Los Angeles, California
1901 This algorithm is free of patents or of copyrights, as confirmed by Peter Shirley himself.
1903 Algorithm: Consider the box as the intersection of three slabs. Clip the ray
1904 against each slab, if there's anything left of the ray after we're
1905 done we've got an intersection of the ray with the box.
1909 FLOAT div, tmin, tmax, tymin, tymax, tzmin, tzmax;
1911 div = 1.0f / praydirection->x;
1914 tmin = ( pmin->x - prayposition->x ) * div;
1915 tmax = ( pmax->x - prayposition->x ) * div;
1919 tmin = ( pmax->x - prayposition->x ) * div;
1920 tmax = ( pmin->x - prayposition->x ) * div;
1923 if ( tmax < 0.0f ) return FALSE;
1925 div = 1.0f / praydirection->y;
1928 tymin = ( pmin->y - prayposition->y ) * div;
1929 tymax = ( pmax->y - prayposition->y ) * div;
1933 tymin = ( pmax->y - prayposition->y ) * div;
1934 tymax = ( pmin->y - prayposition->y ) * div;
1937 if ( ( tymax < 0.0f ) || ( tmin > tymax ) || ( tymin > tmax ) ) return FALSE;
1939 if ( tymin > tmin ) tmin = tymin;
1940 if ( tymax < tmax ) tmax = tymax;
1942 div = 1.0f / praydirection->z;
1945 tzmin = ( pmin->z - prayposition->z ) * div;
1946 tzmax = ( pmax->z - prayposition->z ) * div;
1950 tzmin = ( pmax->z - prayposition->z ) * div;
1951 tzmax = ( pmin->z - prayposition->z ) * div;
1954 if ( (tzmax < 0.0f ) || ( tmin > tzmax ) || ( tzmin > tmax ) ) return FALSE;
1959 /*************************************************************************
1960 * D3DXComputeBoundingBox
1962 HRESULT WINAPI D3DXComputeBoundingBox(CONST D3DXVECTOR3 *pfirstposition, DWORD numvertices, DWORD dwstride, D3DXVECTOR3 *pmin, D3DXVECTOR3 *pmax)
1967 if( !pfirstposition || !pmin || !pmax ) return D3DERR_INVALIDCALL;
1969 *pmin = *pfirstposition;
1972 for(i=0; i<numvertices; i++)
1974 vec = *( (const D3DXVECTOR3*)((const char*)pfirstposition + dwstride * i) );
1976 if ( vec.x < pmin->x ) pmin->x = vec.x;
1977 if ( vec.x > pmax->x ) pmax->x = vec.x;
1979 if ( vec.y < pmin->y ) pmin->y = vec.y;
1980 if ( vec.y > pmax->y ) pmax->y = vec.y;
1982 if ( vec.z < pmin->z ) pmin->z = vec.z;
1983 if ( vec.z > pmax->z ) pmax->z = vec.z;
1989 /*************************************************************************
1990 * D3DXComputeBoundingSphere
1992 HRESULT WINAPI D3DXComputeBoundingSphere(CONST D3DXVECTOR3* pfirstposition, DWORD numvertices, DWORD dwstride, D3DXVECTOR3 *pcenter, FLOAT *pradius)
1994 D3DXVECTOR3 temp, temp1;
1998 if( !pfirstposition || !pcenter || !pradius ) return D3DERR_INVALIDCALL;
2006 for(i=0; i<numvertices; i++)
2008 D3DXVec3Add(&temp1, &temp, (const D3DXVECTOR3*)((const char*)pfirstposition + dwstride * i));
2012 D3DXVec3Scale(pcenter, &temp, 1.0f/((FLOAT)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;
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];
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;
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);
2066 append_decl_element(declaration, &idx, &offset, D3DDECLTYPE_FLOAT3, D3DDECLUSAGE_POSITION, 0);
2070 switch (blend_count)
2075 append_decl_element(declaration, &idx, &offset, D3DDECLTYPE_FLOAT1, D3DDECLUSAGE_BLENDWEIGHT, 0);
2078 append_decl_element(declaration, &idx, &offset, D3DDECLTYPE_FLOAT2, D3DDECLUSAGE_BLENDWEIGHT, 0);
2081 append_decl_element(declaration, &idx, &offset, D3DDECLTYPE_FLOAT3, D3DDECLUSAGE_BLENDWEIGHT, 0);
2084 append_decl_element(declaration, &idx, &offset, D3DDECLTYPE_FLOAT4, D3DDECLUSAGE_BLENDWEIGHT, 0);
2087 ERR("Invalid blend count %u.\n", blend_count);
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);
2117 case D3DFVF_TEXTUREFORMAT2:
2118 append_decl_element(declaration, &idx, &offset, D3DDECLTYPE_FLOAT2, D3DDECLUSAGE_TEXCOORD, i);
2120 case D3DFVF_TEXTUREFORMAT3:
2121 append_decl_element(declaration, &idx, &offset, D3DDECLTYPE_FLOAT3, D3DDECLUSAGE_TEXCOORD, i);
2123 case D3DFVF_TEXTUREFORMAT4:
2124 append_decl_element(declaration, &idx, &offset, D3DDECLTYPE_FLOAT4, D3DDECLUSAGE_TEXCOORD, i);
2129 declaration[idx] = end_element;
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);
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;
2162 *fvf |= D3DFVF_XYZB1 | D3DFVF_LASTBETA_D3DCOLOR;
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;
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;
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;
2207 else if (declaration[0].Type == D3DDECLTYPE_FLOAT4 && declaration[0].Usage == D3DDECLUSAGE_POSITIONT &&
2208 declaration[0].UsageIndex == 0)
2210 *fvf |= D3DFVF_XYZRHW;
2214 if (declaration[i].Type == D3DDECLTYPE_FLOAT3 && declaration[i].Usage == D3DDECLUSAGE_NORMAL)
2216 *fvf |= D3DFVF_NORMAL;
2219 if (declaration[i].Type == D3DDECLTYPE_FLOAT1 && declaration[i].Usage == D3DDECLUSAGE_PSIZE &&
2220 declaration[i].UsageIndex == 0)
2222 *fvf |= D3DFVF_PSIZE;
2225 if (declaration[i].Type == D3DDECLTYPE_D3DCOLOR && declaration[i].Usage == D3DDECLUSAGE_COLOR &&
2226 declaration[i].UsageIndex == 0)
2228 *fvf |= D3DFVF_DIFFUSE;
2231 if (declaration[i].Type == D3DDECLTYPE_D3DCOLOR && declaration[i].Usage == D3DDECLUSAGE_COLOR &&
2232 declaration[i].UsageIndex == 1)
2234 *fvf |= D3DFVF_SPECULAR;
2238 for (texture = 0; texture < D3DDP_MAXTEXCOORD; i++, texture++)
2240 if (declaration[i].Stream == 0xFF)
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);
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;
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)
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);
2323 /*************************************************************************
2324 * D3DXGetDeclVertexSize
2326 UINT WINAPI D3DXGetDeclVertexSize(const D3DVERTEXELEMENT9 *decl, DWORD stream_idx)
2328 const D3DVERTEXELEMENT9 *element;
2331 TRACE("decl %p, stream_idx %u\n", decl, stream_idx);
2333 if (!decl) return 0;
2335 for (element = decl; element->Stream != 0xff; ++element)
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);
2347 type_size = d3dx_decltype_size[element->Type];
2348 if (element->Offset + type_size > size) size = element->Offset + type_size;
2354 /*************************************************************************
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 /*************************************************************************
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)
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;
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;
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;
2395 vec.x = praypos->x - p0->x;
2396 vec.y = praypos->y - p0->y;
2397 vec.z = praypos->z - p0->z;
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) )
2407 *pdist = fabs( vec.z );
2415 /*************************************************************************
2416 * D3DXSphereBoundProbe
2418 BOOL WINAPI D3DXSphereBoundProbe(CONST D3DXVECTOR3 *pcenter, FLOAT radius, CONST D3DXVECTOR3 *prayposition, CONST D3DXVECTOR3 *praydirection)
2420 D3DXVECTOR3 difference;
2423 a = D3DXVec3LengthSq(praydirection);
2424 if (!D3DXVec3Subtract(&difference, prayposition, pcenter)) return FALSE;
2425 b = D3DXVec3Dot(&difference, praydirection);
2426 c = D3DXVec3LengthSq(&difference) - radius * radius;
2429 if ( ( d <= 0.0f ) || ( sqrt(d) <= b ) ) return FALSE;
2433 /*************************************************************************
2436 HRESULT WINAPI D3DXCreateMesh(DWORD numfaces, DWORD numvertices, DWORD options, CONST D3DVERTEXELEMENT9 *declaration,
2437 LPDIRECT3DDEVICE9 device, LPD3DXMESH *mesh)
2441 IDirect3DVertexDeclaration9 *vertex_declaration;
2442 UINT vertex_declaration_size;
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;
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;
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);
2518 /* Create vertex declaration */
2519 hr = IDirect3DDevice9_CreateVertexDeclaration(device,
2521 &vertex_declaration);
2524 WARN("Unexpected return value %x from IDirect3DDevice9_CreateVertexDeclaration.\n",hr);
2527 vertex_declaration_size = D3DXGetDeclVertexSize(declaration, declaration[0].Stream);
2529 /* Create vertex buffer */
2530 hr = IDirect3DDevice9_CreateVertexBuffer(device,
2531 numvertices * vertex_declaration_size,
2539 WARN("Unexpected return value %x from IDirect3DDevice9_CreateVertexBuffer.\n",hr);
2540 IDirect3DVertexDeclaration9_Release(vertex_declaration);
2544 /* Create index buffer */
2545 hr = IDirect3DDevice9_CreateIndexBuffer(device,
2546 numfaces * 3 * ((index_format == D3DFMT_INDEX16) ? 2 : 4),
2554 WARN("Unexpected return value %x from IDirect3DDevice9_CreateVertexBuffer.\n",hr);
2555 IDirect3DVertexBuffer9_Release(vertex_buffer);
2556 IDirect3DVertexDeclaration9_Release(vertex_declaration);
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, attrib_buffer);
2565 IDirect3DIndexBuffer9_Release(index_buffer);
2566 IDirect3DVertexBuffer9_Release(vertex_buffer);
2567 IDirect3DVertexDeclaration9_Release(vertex_declaration);
2569 return E_OUTOFMEMORY;
2571 object->ID3DXMesh_iface.lpVtbl = &D3DXMesh_Vtbl;
2574 object->numfaces = numfaces;
2575 object->numvertices = numvertices;
2576 object->options = options;
2578 object->device = device;
2579 IDirect3DDevice9_AddRef(device);
2581 copy_declaration(object->cached_declaration, declaration, num_elem);
2582 object->vertex_declaration = vertex_declaration;
2583 object->vertex_declaration_size = vertex_declaration_size;
2584 object->num_elem = num_elem;
2585 object->vertex_buffer = vertex_buffer;
2586 object->index_buffer = index_buffer;
2587 object->attrib_buffer = attrib_buffer;
2589 *mesh = &object->ID3DXMesh_iface;
2594 /*************************************************************************
2597 HRESULT WINAPI D3DXCreateMeshFVF(DWORD numfaces, DWORD numvertices, DWORD options, DWORD fvf,
2598 LPDIRECT3DDEVICE9 device, LPD3DXMESH *mesh)
2601 D3DVERTEXELEMENT9 declaration[MAX_FVF_DECL_SIZE];
2603 TRACE("(%u, %u, %u, %u, %p, %p)\n", numfaces, numvertices, options, fvf, device, mesh);
2605 hr = D3DXDeclaratorFromFVF(fvf, declaration);
2606 if (FAILED(hr)) return hr;
2608 return D3DXCreateMesh(numfaces, numvertices, options, declaration, device, mesh);
2614 DWORD num_poly_faces;
2615 DWORD num_tri_faces;
2616 D3DXVECTOR3 *vertices;
2617 DWORD *num_tri_per_face;
2622 /* optional mesh data */
2625 D3DXVECTOR3 *normals;
2626 DWORD *normal_indices;
2628 D3DXVECTOR2 *tex_coords;
2630 DWORD *vertex_colors;
2632 DWORD num_materials;
2633 D3DXMATERIAL *materials;
2634 DWORD *material_indices;
2637 static HRESULT get_next_child(IDirectXFileData *filedata, IDirectXFileData **child, const GUID **type)
2640 IDirectXFileDataReference *child_ref = NULL;
2641 IDirectXFileObject *child_obj = NULL;
2642 IDirectXFileData *child_data = NULL;
2644 hr = IDirectXFileData_GetNextObject(filedata, &child_obj);
2645 if (FAILED(hr)) return hr;
2647 hr = IDirectXFileObject_QueryInterface(child_obj, &IID_IDirectXFileDataReference, (void**)&child_ref);
2648 if (SUCCEEDED(hr)) {
2649 hr = IDirectXFileDataReference_Resolve(child_ref, &child_data);
2650 IDirectXFileDataReference_Release(child_ref);
2652 hr = IDirectXFileObject_QueryInterface(child_obj, &IID_IDirectXFileData, (void**)&child_data);
2654 IDirectXFileObject_Release(child_obj);
2658 hr = IDirectXFileData_GetType(child_data, type);
2660 IDirectXFileData_Release(child_data);
2662 *child = child_data;
2668 static HRESULT parse_texture_filename(IDirectXFileData *filedata, LPSTR *filename_out)
2674 char *filename = NULL;
2676 /* template TextureFilename {
2681 HeapFree(GetProcessHeap(), 0, *filename_out);
2682 *filename_out = NULL;
2684 hr = IDirectXFileData_GetData(filedata, NULL, &data_size, (void**)&data);
2685 if (FAILED(hr)) return hr;
2687 if (data_size < sizeof(LPSTR)) {
2688 WARN("truncated data (%u bytes)\n", data_size);
2691 filename_in = *(LPSTR*)data;
2693 filename = HeapAlloc(GetProcessHeap(), 0, strlen(filename_in) + 1);
2694 if (!filename) return E_OUTOFMEMORY;
2696 strcpy(filename, filename_in);
2697 *filename_out = filename;
2702 static HRESULT parse_material(IDirectXFileData *filedata, D3DXMATERIAL *material)
2708 IDirectXFileData *child;
2710 material->pTextureFilename = NULL;
2712 hr = IDirectXFileData_GetData(filedata, NULL, &data_size, (void**)&data);
2713 if (FAILED(hr)) return hr;
2716 * template ColorRGBA {
2722 * template ColorRGB {
2727 * template Material {
2728 * ColorRGBA faceColor;
2730 * ColorRGB specularColor;
2731 * ColorRGB emissiveColor;
2735 if (data_size != sizeof(FLOAT) * 11) {
2736 WARN("incorrect data size (%u bytes)\n", data_size);
2740 memcpy(&material->MatD3D.Diffuse, data, sizeof(D3DCOLORVALUE));
2741 data += sizeof(D3DCOLORVALUE);
2742 material->MatD3D.Power = *(FLOAT*)data;
2743 data += sizeof(FLOAT);
2744 memcpy(&material->MatD3D.Specular, data, sizeof(FLOAT) * 3);
2745 material->MatD3D.Specular.a = 1.0f;
2746 data += 3 * sizeof(FLOAT);
2747 memcpy(&material->MatD3D.Emissive, data, sizeof(FLOAT) * 3);
2748 material->MatD3D.Emissive.a = 1.0f;
2749 material->MatD3D.Ambient.r = 0.0f;
2750 material->MatD3D.Ambient.g = 0.0f;
2751 material->MatD3D.Ambient.b = 0.0f;
2752 material->MatD3D.Ambient.a = 1.0f;
2754 while (SUCCEEDED(hr = get_next_child(filedata, &child, &type)))
2756 if (IsEqualGUID(type, &TID_D3DRMTextureFilename)) {
2757 hr = parse_texture_filename(child, &material->pTextureFilename);
2758 if (FAILED(hr)) break;
2761 return hr == DXFILEERR_NOMOREOBJECTS ? D3D_OK : hr;
2764 static void destroy_materials(struct mesh_data *mesh)
2767 for (i = 0; i < mesh->num_materials; i++)
2768 HeapFree(GetProcessHeap(), 0, mesh->materials[i].pTextureFilename);
2769 HeapFree(GetProcessHeap(), 0, mesh->materials);
2770 HeapFree(GetProcessHeap(), 0, mesh->material_indices);
2771 mesh->num_materials = 0;
2772 mesh->materials = NULL;
2773 mesh->material_indices = NULL;
2776 static HRESULT parse_material_list(IDirectXFileData *filedata, struct mesh_data *mesh)
2780 DWORD *data, *in_ptr;
2782 IDirectXFileData *child;
2783 DWORD num_materials;
2786 destroy_materials(mesh);
2788 hr = IDirectXFileData_GetData(filedata, NULL, &data_size, (void**)&data);
2789 if (FAILED(hr)) return hr;
2791 /* template MeshMaterialList {
2793 * DWORD nFaceIndexes;
2794 * array DWORD faceIndexes[nFaceIndexes];
2801 if (data_size < sizeof(DWORD))
2802 goto truncated_data_error;
2803 num_materials = *in_ptr++;
2807 if (data_size < 2 * sizeof(DWORD))
2808 goto truncated_data_error;
2809 if (*in_ptr++ != mesh->num_poly_faces) {
2810 WARN("number of material face indices (%u) doesn't match number of faces (%u)\n",
2811 *(in_ptr - 1), mesh->num_poly_faces);
2814 if (data_size < 2 * sizeof(DWORD) + mesh->num_poly_faces * sizeof(DWORD))
2815 goto truncated_data_error;
2816 for (i = 0; i < mesh->num_poly_faces; i++) {
2817 if (*in_ptr++ >= num_materials) {
2818 WARN("face %u: reference to undefined material %u (only %u materials)\n",
2819 i, *(in_ptr - 1), num_materials);
2824 mesh->materials = HeapAlloc(GetProcessHeap(), 0, num_materials * sizeof(*mesh->materials));
2825 mesh->material_indices = HeapAlloc(GetProcessHeap(), 0, mesh->num_poly_faces * sizeof(*mesh->material_indices));
2826 if (!mesh->materials || !mesh->material_indices)
2827 return E_OUTOFMEMORY;
2828 memcpy(mesh->material_indices, data + 2, mesh->num_poly_faces * sizeof(DWORD));
2830 while (SUCCEEDED(hr = get_next_child(filedata, &child, &type)))
2832 if (IsEqualGUID(type, &TID_D3DRMMaterial)) {
2833 if (mesh->num_materials >= num_materials) {
2834 WARN("more materials defined than declared\n");
2837 hr = parse_material(child, &mesh->materials[mesh->num_materials++]);
2838 if (FAILED(hr)) break;
2841 if (hr != DXFILEERR_NOMOREOBJECTS)
2843 if (num_materials != mesh->num_materials) {
2844 WARN("only %u of %u materials defined\n", num_materials, mesh->num_materials);
2849 truncated_data_error:
2850 WARN("truncated data (%u bytes)\n", data_size);
2854 static HRESULT parse_texture_coords(IDirectXFileData *filedata, struct mesh_data *mesh)
2860 HeapFree(GetProcessHeap(), 0, mesh->tex_coords);
2861 mesh->tex_coords = NULL;
2863 hr = IDirectXFileData_GetData(filedata, NULL, &data_size, (void**)&data);
2864 if (FAILED(hr)) return hr;
2866 /* template Coords2d {
2870 * template MeshTextureCoords {
2871 * DWORD nTextureCoords;
2872 * array Coords2d textureCoords[nTextureCoords];
2876 if (data_size < sizeof(DWORD))
2877 goto truncated_data_error;
2878 if (*(DWORD*)data != mesh->num_vertices) {
2879 WARN("number of texture coordinates (%u) doesn't match number of vertices (%u)\n",
2880 *(DWORD*)data, mesh->num_vertices);
2883 data += sizeof(DWORD);
2884 if (data_size < sizeof(DWORD) + mesh->num_vertices * sizeof(*mesh->tex_coords))
2885 goto truncated_data_error;
2887 mesh->tex_coords = HeapAlloc(GetProcessHeap(), 0, mesh->num_vertices * sizeof(*mesh->tex_coords));
2888 if (!mesh->tex_coords) return E_OUTOFMEMORY;
2889 memcpy(mesh->tex_coords, data, mesh->num_vertices * sizeof(*mesh->tex_coords));
2891 mesh->fvf |= D3DFVF_TEX1;
2894 truncated_data_error:
2895 WARN("truncated data (%u bytes)\n", data_size);
2899 static HRESULT parse_vertex_colors(IDirectXFileData *filedata, struct mesh_data *mesh)
2907 HeapFree(GetProcessHeap(), 0, mesh->vertex_colors);
2908 mesh->vertex_colors = NULL;
2910 hr = IDirectXFileData_GetData(filedata, NULL, &data_size, (void**)&data);
2911 if (FAILED(hr)) return hr;
2913 /* template IndexedColor {
2915 * ColorRGBA indexColor;
2917 * template MeshVertexColors {
2918 * DWORD nVertexColors;
2919 * array IndexedColor vertexColors[nVertexColors];
2923 if (data_size < sizeof(DWORD))
2924 goto truncated_data_error;
2925 num_colors = *(DWORD*)data;
2926 data += sizeof(DWORD);
2927 if (data_size < sizeof(DWORD) + num_colors * (sizeof(DWORD) + sizeof(D3DCOLORVALUE)))
2928 goto truncated_data_error;
2930 mesh->vertex_colors = HeapAlloc(GetProcessHeap(), 0, mesh->num_vertices * sizeof(DWORD));
2931 if (!mesh->vertex_colors)
2932 return E_OUTOFMEMORY;
2934 for (i = 0; i < mesh->num_vertices; i++)
2935 mesh->vertex_colors[i] = D3DCOLOR_ARGB(0, 0xff, 0xff, 0xff);
2936 for (i = 0; i < num_colors; i++)
2938 D3DCOLORVALUE color;
2939 DWORD index = *(DWORD*)data;
2940 data += sizeof(DWORD);
2941 if (index >= mesh->num_vertices) {
2942 WARN("vertex color %u references undefined vertex %u (only %u vertices)\n",
2943 i, index, mesh->num_vertices);
2946 memcpy(&color, data, sizeof(color));
2947 data += sizeof(color);
2948 color.r = min(1.0f, max(0.0f, color.r));
2949 color.g = min(1.0f, max(0.0f, color.g));
2950 color.b = min(1.0f, max(0.0f, color.b));
2951 color.a = min(1.0f, max(0.0f, color.a));
2952 mesh->vertex_colors[index] = D3DCOLOR_ARGB((BYTE)(color.a * 255.0f + 0.5f),
2953 (BYTE)(color.r * 255.0f + 0.5f),
2954 (BYTE)(color.g * 255.0f + 0.5f),
2955 (BYTE)(color.b * 255.0f + 0.5f));
2958 mesh->fvf |= D3DFVF_DIFFUSE;
2961 truncated_data_error:
2962 WARN("truncated data (%u bytes)\n", data_size);
2966 static HRESULT parse_normals(IDirectXFileData *filedata, struct mesh_data *mesh)
2971 DWORD *index_out_ptr;
2973 DWORD num_face_indices = mesh->num_poly_faces * 2 + mesh->num_tri_faces;
2975 HeapFree(GetProcessHeap(), 0, mesh->normals);
2976 mesh->num_normals = 0;
2977 mesh->normals = NULL;
2978 mesh->normal_indices = NULL;
2979 mesh->fvf |= D3DFVF_NORMAL;
2981 hr = IDirectXFileData_GetData(filedata, NULL, &data_size, (void**)&data);
2982 if (FAILED(hr)) return hr;
2984 /* template Vector {
2989 * template MeshFace {
2990 * DWORD nFaceVertexIndices;
2991 * array DWORD faceVertexIndices[nFaceVertexIndices];
2993 * template MeshNormals {
2995 * array Vector normals[nNormals];
2996 * DWORD nFaceNormals;
2997 * array MeshFace faceNormals[nFaceNormals];
3001 if (data_size < sizeof(DWORD) * 2)
3002 goto truncated_data_error;
3003 mesh->num_normals = *(DWORD*)data;
3004 data += sizeof(DWORD);
3005 if (data_size < sizeof(DWORD) * 2 + mesh->num_normals * sizeof(D3DXVECTOR3) +
3006 num_face_indices * sizeof(DWORD))
3007 goto truncated_data_error;
3009 mesh->normals = HeapAlloc(GetProcessHeap(), 0, mesh->num_normals * sizeof(D3DXVECTOR3));
3010 mesh->normal_indices = HeapAlloc(GetProcessHeap(), 0, num_face_indices * sizeof(DWORD));
3011 if (!mesh->normals || !mesh->normal_indices)
3012 return E_OUTOFMEMORY;
3014 memcpy(mesh->normals, data, mesh->num_normals * sizeof(D3DXVECTOR3));
3015 data += mesh->num_normals * sizeof(D3DXVECTOR3);
3016 for (i = 0; i < mesh->num_normals; i++)
3017 D3DXVec3Normalize(&mesh->normals[i], &mesh->normals[i]);
3019 if (*(DWORD*)data != mesh->num_poly_faces) {
3020 WARN("number of face normals (%u) doesn't match number of faces (%u)\n",
3021 *(DWORD*)data, mesh->num_poly_faces);
3024 data += sizeof(DWORD);
3025 index_out_ptr = mesh->normal_indices;
3026 for (i = 0; i < mesh->num_poly_faces; i++)
3029 DWORD count = *(DWORD*)data;
3030 if (count != mesh->num_tri_per_face[i] + 2) {
3031 WARN("face %u: number of normals (%u) doesn't match number of vertices (%u)\n",
3032 i, count, mesh->num_tri_per_face[i] + 2);
3035 data += sizeof(DWORD);
3037 for (j = 0; j < count; j++) {
3038 DWORD normal_index = *(DWORD*)data;
3039 if (normal_index >= mesh->num_normals) {
3040 WARN("face %u, normal index %u: reference to undefined normal %u (only %u normals)\n",
3041 i, j, normal_index, mesh->num_normals);
3044 *index_out_ptr++ = normal_index;
3045 data += sizeof(DWORD);
3050 truncated_data_error:
3051 WARN("truncated data (%u bytes)\n", data_size);
3055 /* for provide_flags parameters */
3056 #define PROVIDE_MATERIALS 0x1
3057 #define PROVIDE_SKININFO 0x2
3058 #define PROVIDE_ADJACENCY 0x4
3060 static HRESULT parse_mesh(IDirectXFileData *filedata, struct mesh_data *mesh_data, DWORD provide_flags)
3064 BYTE *data, *in_ptr;
3065 DWORD *index_out_ptr;
3067 IDirectXFileData *child;
3073 * array Vector vertices[nVertices];
3075 * array MeshFace faces[nFaces];
3080 hr = IDirectXFileData_GetData(filedata, NULL, &data_size, (void**)&data);
3081 if (FAILED(hr)) return hr;
3084 if (data_size < sizeof(DWORD) * 2)
3085 goto truncated_data_error;
3086 mesh_data->num_vertices = *(DWORD*)in_ptr;
3087 if (data_size < sizeof(DWORD) * 2 + mesh_data->num_vertices * sizeof(D3DXVECTOR3))
3088 goto truncated_data_error;
3089 in_ptr += sizeof(DWORD) + mesh_data->num_vertices * sizeof(D3DXVECTOR3);
3091 mesh_data->num_poly_faces = *(DWORD*)in_ptr;
3092 in_ptr += sizeof(DWORD);
3094 mesh_data->num_tri_faces = 0;
3095 for (i = 0; i < mesh_data->num_poly_faces; i++)
3097 DWORD num_poly_vertices;
3100 if (data_size - (in_ptr - data) < sizeof(DWORD))
3101 goto truncated_data_error;
3102 num_poly_vertices = *(DWORD*)in_ptr;
3103 in_ptr += sizeof(DWORD);
3104 if (data_size - (in_ptr - data) < num_poly_vertices * sizeof(DWORD))
3105 goto truncated_data_error;
3106 if (num_poly_vertices < 3) {
3107 WARN("face %u has only %u vertices\n", i, num_poly_vertices);
3110 for (j = 0; j < num_poly_vertices; j++) {
3111 if (*(DWORD*)in_ptr >= mesh_data->num_vertices) {
3112 WARN("face %u, index %u: undefined vertex %u (only %u vertices)\n",
3113 i, j, *(DWORD*)in_ptr, mesh_data->num_vertices);
3116 in_ptr += sizeof(DWORD);
3118 mesh_data->num_tri_faces += num_poly_vertices - 2;
3121 mesh_data->fvf = D3DFVF_XYZ;
3123 mesh_data->vertices = HeapAlloc(GetProcessHeap(), 0,
3124 mesh_data->num_vertices * sizeof(*mesh_data->vertices));
3125 mesh_data->num_tri_per_face = HeapAlloc(GetProcessHeap(), 0,
3126 mesh_data->num_poly_faces * sizeof(*mesh_data->num_tri_per_face));
3127 mesh_data->indices = HeapAlloc(GetProcessHeap(), 0,
3128 (mesh_data->num_tri_faces + mesh_data->num_poly_faces * 2) * sizeof(*mesh_data->indices));
3129 if (!mesh_data->vertices || !mesh_data->num_tri_per_face || !mesh_data->indices)
3130 return E_OUTOFMEMORY;
3132 in_ptr = data + sizeof(DWORD);
3133 memcpy(mesh_data->vertices, in_ptr, mesh_data->num_vertices * sizeof(D3DXVECTOR3));
3134 in_ptr += mesh_data->num_vertices * sizeof(D3DXVECTOR3) + sizeof(DWORD);
3136 index_out_ptr = mesh_data->indices;
3137 for (i = 0; i < mesh_data->num_poly_faces; i++)
3141 count = *(DWORD*)in_ptr;
3142 in_ptr += sizeof(DWORD);
3143 mesh_data->num_tri_per_face[i] = count - 2;
3146 *index_out_ptr++ = *(DWORD*)in_ptr;
3147 in_ptr += sizeof(DWORD);
3151 while (SUCCEEDED(hr = get_next_child(filedata, &child, &type)))
3153 if (IsEqualGUID(type, &TID_D3DRMMeshNormals)) {
3154 hr = parse_normals(child, mesh_data);
3155 } else if (IsEqualGUID(type, &TID_D3DRMMeshVertexColors)) {
3156 hr = parse_vertex_colors(child, mesh_data);
3157 } else if (IsEqualGUID(type, &TID_D3DRMMeshTextureCoords)) {
3158 hr = parse_texture_coords(child, mesh_data);
3159 } else if (IsEqualGUID(type, &TID_D3DRMMeshMaterialList) &&
3160 (provide_flags & PROVIDE_MATERIALS))
3162 hr = parse_material_list(child, mesh_data);
3163 } else if (provide_flags & PROVIDE_SKININFO) {
3164 if (IsEqualGUID(type, &DXFILEOBJ_XSkinMeshHeader)) {
3165 FIXME("Skin mesh loading not implemented.\n");
3167 } else if (IsEqualGUID(type, &DXFILEOBJ_SkinWeights)) {
3168 /* ignored without XSkinMeshHeader */
3174 return hr == DXFILEERR_NOMOREOBJECTS ? D3D_OK : hr;
3175 truncated_data_error:
3176 WARN("truncated data (%u bytes)\n", data_size);
3180 static HRESULT generate_effects(ID3DXBuffer *materials, DWORD num_materials,
3181 ID3DXBuffer **effects)
3184 D3DXEFFECTINSTANCE *effect_ptr;
3186 const D3DXMATERIAL *material_ptr = ID3DXBuffer_GetBufferPointer(materials);
3187 static const struct {
3188 const char *param_name;
3192 } material_effects[] = {
3193 #define EFFECT_TABLE_ENTRY(str, field) \
3194 {str, sizeof(str), sizeof(material_ptr->MatD3D.field), offsetof(D3DXMATERIAL, MatD3D.field)}
3195 EFFECT_TABLE_ENTRY("Diffuse", Diffuse),
3196 EFFECT_TABLE_ENTRY("Power", Power),
3197 EFFECT_TABLE_ENTRY("Specular", Specular),
3198 EFFECT_TABLE_ENTRY("Emissive", Emissive),
3199 EFFECT_TABLE_ENTRY("Ambient", Ambient),
3200 #undef EFFECT_TABLE_ENTRY
3202 static const char texture_paramname[] = "Texture0@Name";
3206 /* effects buffer layout:
3208 * D3DXEFFECTINSTANCE effects[num_materials];
3209 * for (effect in effects)
3211 * D3DXEFFECTDEFAULT defaults[effect.NumDefaults];
3212 * for (default in defaults)
3214 * *default.pParamName;
3219 buffer_size = sizeof(D3DXEFFECTINSTANCE);
3220 buffer_size += sizeof(D3DXEFFECTDEFAULT) * ARRAY_SIZE(material_effects);
3221 for (i = 0; i < ARRAY_SIZE(material_effects); i++) {
3222 buffer_size += material_effects[i].name_size;
3223 buffer_size += material_effects[i].num_bytes;
3225 buffer_size *= num_materials;
3226 for (i = 0; i < num_materials; i++) {
3227 if (material_ptr[i].pTextureFilename) {
3228 buffer_size += sizeof(D3DXEFFECTDEFAULT);
3229 buffer_size += sizeof(texture_paramname);
3230 buffer_size += strlen(material_ptr[i].pTextureFilename) + 1;
3234 hr = D3DXCreateBuffer(buffer_size, effects);
3235 if (FAILED(hr)) return hr;
3236 effect_ptr = ID3DXBuffer_GetBufferPointer(*effects);
3237 out_ptr = (BYTE*)(effect_ptr + num_materials);
3239 for (i = 0; i < num_materials; i++)
3242 D3DXEFFECTDEFAULT *defaults = (D3DXEFFECTDEFAULT*)out_ptr;
3244 effect_ptr->pDefaults = defaults;
3245 effect_ptr->NumDefaults = material_ptr->pTextureFilename ? 6 : 5;
3246 out_ptr = (BYTE*)(effect_ptr->pDefaults + effect_ptr->NumDefaults);
3248 for (j = 0; j < ARRAY_SIZE(material_effects); j++)
3250 defaults->pParamName = (LPSTR)out_ptr;
3251 strcpy(defaults->pParamName, material_effects[j].param_name);
3252 defaults->pValue = defaults->pParamName + material_effects[j].name_size;
3253 defaults->Type = D3DXEDT_FLOATS;
3254 defaults->NumBytes = material_effects[j].num_bytes;
3255 memcpy(defaults->pValue, (BYTE*)material_ptr + material_effects[j].value_offset, defaults->NumBytes);
3256 out_ptr = (BYTE*)defaults->pValue + defaults->NumBytes;
3260 if (material_ptr->pTextureFilename) {
3261 defaults->pParamName = (LPSTR)out_ptr;
3262 strcpy(defaults->pParamName, texture_paramname);
3263 defaults->pValue = defaults->pParamName + sizeof(texture_paramname);
3264 defaults->Type = D3DXEDT_STRING;
3265 defaults->NumBytes = strlen(material_ptr->pTextureFilename) + 1;
3266 strcpy(defaults->pValue, material_ptr->pTextureFilename);
3267 out_ptr = (BYTE*)defaults->pValue + defaults->NumBytes;
3272 assert(out_ptr - (BYTE*)ID3DXBuffer_GetBufferPointer(*effects) == buffer_size);
3277 /* change to D3DXLoadSkinMeshFromXof when ID3DXFileData is implemented */
3278 static HRESULT load_skin_mesh_from_xof(IDirectXFileData *filedata,
3280 LPDIRECT3DDEVICE9 device,
3281 LPD3DXBUFFER *adjacency_out,
3282 LPD3DXBUFFER *materials_out,
3283 LPD3DXBUFFER *effects_out,
3284 DWORD *num_materials_out,
3285 LPD3DXSKININFO *skin_info_out,
3286 LPD3DXMESH *mesh_out)
3289 DWORD *index_in_ptr;
3290 struct mesh_data mesh_data;
3291 DWORD total_vertices;
3292 ID3DXMesh *d3dxmesh = NULL;
3293 ID3DXBuffer *adjacency = NULL;
3294 ID3DXBuffer *materials = NULL;
3295 ID3DXBuffer *effects = NULL;
3296 struct vertex_duplication {
3299 } *duplications = NULL;
3301 void *vertices = NULL;
3302 void *indices = NULL;
3304 DWORD provide_flags = 0;
3306 ZeroMemory(&mesh_data, sizeof(mesh_data));
3308 if (num_materials_out || materials_out || effects_out)
3309 provide_flags |= PROVIDE_MATERIALS;
3311 provide_flags |= PROVIDE_SKININFO;
3313 hr = parse_mesh(filedata, &mesh_data, provide_flags);
3314 if (FAILED(hr)) goto cleanup;
3316 total_vertices = mesh_data.num_vertices;
3317 if (mesh_data.fvf & D3DFVF_NORMAL) {
3318 /* duplicate vertices with multiple normals */
3319 DWORD num_face_indices = mesh_data.num_poly_faces * 2 + mesh_data.num_tri_faces;
3320 duplications = HeapAlloc(GetProcessHeap(), 0, (mesh_data.num_vertices + num_face_indices) * sizeof(*duplications));
3321 if (!duplications) {
3325 for (i = 0; i < total_vertices; i++)
3327 duplications[i].normal_index = -1;
3328 list_init(&duplications[i].entry);
3330 for (i = 0; i < num_face_indices; i++) {
3331 DWORD vertex_index = mesh_data.indices[i];
3332 DWORD normal_index = mesh_data.normal_indices[i];
3333 struct vertex_duplication *dup_ptr = &duplications[vertex_index];
3335 if (dup_ptr->normal_index == -1) {
3336 dup_ptr->normal_index = normal_index;
3338 D3DXVECTOR3 *new_normal = &mesh_data.normals[normal_index];
3339 struct list *dup_list = &dup_ptr->entry;
3341 D3DXVECTOR3 *cur_normal = &mesh_data.normals[dup_ptr->normal_index];
3342 if (new_normal->x == cur_normal->x &&
3343 new_normal->y == cur_normal->y &&
3344 new_normal->z == cur_normal->z)
3346 mesh_data.indices[i] = dup_ptr - duplications;
3348 } else if (!list_next(dup_list, &dup_ptr->entry)) {
3349 dup_ptr = &duplications[total_vertices++];
3350 dup_ptr->normal_index = normal_index;
3351 list_add_tail(dup_list, &dup_ptr->entry);
3352 mesh_data.indices[i] = dup_ptr - duplications;
3355 dup_ptr = LIST_ENTRY(list_next(dup_list, &dup_ptr->entry),
3356 struct vertex_duplication, entry);
3363 hr = D3DXCreateMeshFVF(mesh_data.num_tri_faces, total_vertices, options, mesh_data.fvf, device, &d3dxmesh);
3364 if (FAILED(hr)) goto cleanup;
3366 hr = d3dxmesh->lpVtbl->LockVertexBuffer(d3dxmesh, 0, &vertices);
3367 if (FAILED(hr)) goto cleanup;
3370 for (i = 0; i < mesh_data.num_vertices; i++) {
3371 *(D3DXVECTOR3*)out_ptr = mesh_data.vertices[i];
3372 out_ptr += sizeof(D3DXVECTOR3);
3373 if (mesh_data.fvf & D3DFVF_NORMAL) {
3374 if (duplications[i].normal_index == -1)
3375 ZeroMemory(out_ptr, sizeof(D3DXVECTOR3));
3377 *(D3DXVECTOR3*)out_ptr = mesh_data.normals[duplications[i].normal_index];
3378 out_ptr += sizeof(D3DXVECTOR3);
3380 if (mesh_data.fvf & D3DFVF_DIFFUSE) {
3381 *(DWORD*)out_ptr = mesh_data.vertex_colors[i];
3382 out_ptr += sizeof(DWORD);
3384 if (mesh_data.fvf & D3DFVF_TEX1) {
3385 *(D3DXVECTOR2*)out_ptr = mesh_data.tex_coords[i];
3386 out_ptr += sizeof(D3DXVECTOR2);
3389 if (mesh_data.fvf & D3DFVF_NORMAL) {
3390 DWORD vertex_size = D3DXGetFVFVertexSize(mesh_data.fvf);
3392 for (i = 0; i < mesh_data.num_vertices; i++) {
3393 struct vertex_duplication *dup_ptr;
3394 LIST_FOR_EACH_ENTRY(dup_ptr, &duplications[i].entry, struct vertex_duplication, entry)
3396 int j = dup_ptr - duplications;
3397 BYTE *dest_vertex = (BYTE*)vertices + j * vertex_size;
3399 memcpy(dest_vertex, out_ptr, vertex_size);
3400 dest_vertex += sizeof(D3DXVECTOR3);
3401 *(D3DXVECTOR3*)dest_vertex = mesh_data.normals[dup_ptr->normal_index];
3403 out_ptr += vertex_size;
3406 d3dxmesh->lpVtbl->UnlockVertexBuffer(d3dxmesh);
3408 hr = d3dxmesh->lpVtbl->LockIndexBuffer(d3dxmesh, 0, &indices);
3409 if (FAILED(hr)) goto cleanup;
3411 index_in_ptr = mesh_data.indices;
3412 #define FILL_INDEX_BUFFER(indices_var) \
3413 for (i = 0; i < mesh_data.num_poly_faces; i++) \
3415 DWORD count = mesh_data.num_tri_per_face[i]; \
3416 WORD first_index = *index_in_ptr++; \
3418 *indices_var++ = first_index; \
3419 *indices_var++ = *index_in_ptr; \
3421 *indices_var++ = *index_in_ptr; \
3425 if (options & D3DXMESH_32BIT) {
3426 DWORD *dword_indices = indices;
3427 FILL_INDEX_BUFFER(dword_indices)
3429 WORD *word_indices = indices;
3430 FILL_INDEX_BUFFER(word_indices)
3432 #undef FILL_INDEX_BUFFER
3433 d3dxmesh->lpVtbl->UnlockIndexBuffer(d3dxmesh);
3435 if (mesh_data.material_indices) {
3436 DWORD *attrib_buffer = NULL;
3437 hr = d3dxmesh->lpVtbl->LockAttributeBuffer(d3dxmesh, 0, &attrib_buffer);
3438 if (FAILED(hr)) goto cleanup;
3439 for (i = 0; i < mesh_data.num_poly_faces; i++)
3441 DWORD count = mesh_data.num_tri_per_face[i];
3443 *attrib_buffer++ = mesh_data.material_indices[i];
3445 d3dxmesh->lpVtbl->UnlockAttributeBuffer(d3dxmesh);
3447 hr = d3dxmesh->lpVtbl->OptimizeInplace(d3dxmesh,
3448 D3DXMESHOPT_ATTRSORT | D3DXMESHOPT_IGNOREVERTS | D3DXMESHOPT_DONOTSPLIT,
3449 NULL, NULL, NULL, NULL);
3450 if (FAILED(hr)) goto cleanup;
3453 if (mesh_data.num_materials && (materials_out || effects_out)) {
3454 DWORD buffer_size = mesh_data.num_materials * sizeof(D3DXMATERIAL);
3455 char *strings_out_ptr;
3456 D3DXMATERIAL *materials_ptr;
3458 for (i = 0; i < mesh_data.num_materials; i++) {
3459 if (mesh_data.materials[i].pTextureFilename)
3460 buffer_size += strlen(mesh_data.materials[i].pTextureFilename) + 1;
3463 hr = D3DXCreateBuffer(buffer_size, &materials);
3464 if (FAILED(hr)) goto cleanup;
3466 materials_ptr = ID3DXBuffer_GetBufferPointer(materials);
3467 memcpy(materials_ptr, mesh_data.materials, mesh_data.num_materials * sizeof(D3DXMATERIAL));
3468 strings_out_ptr = (char*)(materials_ptr + mesh_data.num_materials);
3469 for (i = 0; i < mesh_data.num_materials; i++) {
3470 if (materials_ptr[i].pTextureFilename) {
3471 strcpy(strings_out_ptr, mesh_data.materials[i].pTextureFilename);
3472 materials_ptr[i].pTextureFilename = strings_out_ptr;
3473 strings_out_ptr += strlen(mesh_data.materials[i].pTextureFilename) + 1;
3478 if (mesh_data.num_materials && effects_out) {
3479 hr = generate_effects(materials, mesh_data.num_materials, &effects);
3480 if (FAILED(hr)) goto cleanup;
3482 if (!materials_out) {
3483 ID3DXBuffer_Release(materials);
3488 if (adjacency_out) {
3489 hr = D3DXCreateBuffer(mesh_data.num_tri_faces * 3 * sizeof(DWORD), &adjacency);
3490 if (FAILED(hr)) goto cleanup;
3491 hr = d3dxmesh->lpVtbl->GenerateAdjacency(d3dxmesh, 0.0f, ID3DXBuffer_GetBufferPointer(adjacency));
3492 if (FAILED(hr)) goto cleanup;
3495 *mesh_out = d3dxmesh;
3496 if (adjacency_out) *adjacency_out = adjacency;
3497 if (num_materials_out) *num_materials_out = mesh_data.num_materials;
3498 if (materials_out) *materials_out = materials;
3499 if (effects_out) *effects_out = effects;
3500 if (skin_info_out) *skin_info_out = NULL;
3505 if (d3dxmesh) IUnknown_Release(d3dxmesh);
3506 if (adjacency) ID3DXBuffer_Release(adjacency);
3507 if (materials) ID3DXBuffer_Release(materials);
3508 if (effects) ID3DXBuffer_Release(effects);
3510 HeapFree(GetProcessHeap(), 0, mesh_data.vertices);
3511 HeapFree(GetProcessHeap(), 0, mesh_data.num_tri_per_face);
3512 HeapFree(GetProcessHeap(), 0, mesh_data.indices);
3513 HeapFree(GetProcessHeap(), 0, mesh_data.normals);
3514 HeapFree(GetProcessHeap(), 0, mesh_data.normal_indices);
3515 destroy_materials(&mesh_data);
3516 HeapFree(GetProcessHeap(), 0, mesh_data.tex_coords);
3517 HeapFree(GetProcessHeap(), 0, mesh_data.vertex_colors);
3518 HeapFree(GetProcessHeap(), 0, duplications);
3522 HRESULT WINAPI D3DXLoadMeshHierarchyFromXA(LPCSTR filename,
3524 LPDIRECT3DDEVICE9 device,
3525 LPD3DXALLOCATEHIERARCHY alloc_hier,
3526 LPD3DXLOADUSERDATA load_user_data,
3527 LPD3DXFRAME *frame_hierarchy,
3528 LPD3DXANIMATIONCONTROLLER *anim_controller)
3534 TRACE("(%s, %x, %p, %p, %p, %p, %p)\n", debugstr_a(filename), options,
3535 device, alloc_hier, load_user_data, frame_hierarchy, anim_controller);
3538 return D3DERR_INVALIDCALL;
3540 len = MultiByteToWideChar(CP_ACP, 0, filename, -1, NULL, 0);
3541 filenameW = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
3542 if (!filenameW) return E_OUTOFMEMORY;
3543 MultiByteToWideChar(CP_ACP, 0, filename, -1, filenameW, len);
3545 hr = D3DXLoadMeshHierarchyFromXW(filenameW, options, device,
3546 alloc_hier, load_user_data, frame_hierarchy, anim_controller);
3547 HeapFree(GetProcessHeap(), 0, filenameW);
3552 HRESULT WINAPI D3DXLoadMeshHierarchyFromXW(LPCWSTR filename,
3554 LPDIRECT3DDEVICE9 device,
3555 LPD3DXALLOCATEHIERARCHY alloc_hier,
3556 LPD3DXLOADUSERDATA load_user_data,
3557 LPD3DXFRAME *frame_hierarchy,
3558 LPD3DXANIMATIONCONTROLLER *anim_controller)
3564 TRACE("(%s, %x, %p, %p, %p, %p, %p)\n", debugstr_w(filename), options,
3565 device, alloc_hier, load_user_data, frame_hierarchy, anim_controller);
3568 return D3DERR_INVALIDCALL;
3570 hr = map_view_of_file(filename, &buffer, &size);
3572 return D3DXERR_INVALIDDATA;
3574 hr = D3DXLoadMeshHierarchyFromXInMemory(buffer, size, options, device,
3575 alloc_hier, load_user_data, frame_hierarchy, anim_controller);
3577 UnmapViewOfFile(buffer);
3582 static HRESULT filedata_get_name(IDirectXFileData *filedata, char **name)
3587 hr = IDirectXFileData_GetName(filedata, NULL, &name_len);
3588 if (FAILED(hr)) return hr;
3592 *name = HeapAlloc(GetProcessHeap(), 0, name_len);
3593 if (!*name) return E_OUTOFMEMORY;
3595 hr = IDirectXFileObject_GetName(filedata, *name, &name_len);
3597 HeapFree(GetProcessHeap(), 0, name);
3604 static HRESULT load_mesh_container(IDirectXFileData *filedata,
3606 LPDIRECT3DDEVICE9 device,
3607 LPD3DXALLOCATEHIERARCHY alloc_hier,
3608 D3DXMESHCONTAINER **mesh_container)
3611 ID3DXBuffer *adjacency = NULL;
3612 ID3DXBuffer *materials = NULL;
3613 ID3DXBuffer *effects = NULL;
3614 ID3DXSkinInfo *skin_info = NULL;
3615 D3DXMESHDATA mesh_data;
3616 DWORD num_materials = 0;
3619 mesh_data.Type = D3DXMESHTYPE_MESH;
3620 mesh_data.u.pMesh = NULL;
3622 hr = load_skin_mesh_from_xof(filedata, options, device,
3623 &adjacency, &materials, &effects, &num_materials,
3624 &skin_info, &mesh_data.u.pMesh);
3625 if (FAILED(hr)) return hr;
3627 hr = filedata_get_name(filedata, &name);
3628 if (FAILED(hr)) goto cleanup;
3630 hr = alloc_hier->lpVtbl->CreateMeshContainer(alloc_hier, name, &mesh_data,
3631 materials ? ID3DXBuffer_GetBufferPointer(materials) : NULL,
3632 effects ? ID3DXBuffer_GetBufferPointer(effects) : NULL,
3634 adjacency ? ID3DXBuffer_GetBufferPointer(adjacency) : NULL,
3635 skin_info, mesh_container);
3638 if (materials) ID3DXBuffer_Release(materials);
3639 if (effects) ID3DXBuffer_Release(effects);
3640 if (adjacency) ID3DXBuffer_Release(adjacency);
3641 if (skin_info) IUnknown_Release(skin_info);
3642 if (mesh_data.u.pMesh) IUnknown_Release(mesh_data.u.pMesh);
3643 HeapFree(GetProcessHeap(), 0, name);
3647 static HRESULT parse_transform_matrix(IDirectXFileData *filedata, D3DXMATRIX *transform)
3653 /* template Matrix4x4 {
3654 * array FLOAT matrix[16];
3656 * template FrameTransformMatrix {
3657 * Matrix4x4 frameMatrix;
3661 hr = IDirectXFileData_GetData(filedata, NULL, &data_size, (void**)&data);
3662 if (FAILED(hr)) return hr;
3664 if (data_size != sizeof(D3DXMATRIX)) {
3665 WARN("incorrect data size (%u bytes)\n", data_size);
3669 memcpy(transform, data, sizeof(D3DXMATRIX));
3674 static HRESULT load_frame(IDirectXFileData *filedata,
3676 LPDIRECT3DDEVICE9 device,
3677 LPD3DXALLOCATEHIERARCHY alloc_hier,
3678 D3DXFRAME **frame_out)
3682 IDirectXFileData *child;
3684 D3DXFRAME *frame = NULL;
3685 D3DXMESHCONTAINER **next_container;
3686 D3DXFRAME **next_child;
3688 hr = filedata_get_name(filedata, &name);
3689 if (FAILED(hr)) return hr;
3691 hr = alloc_hier->lpVtbl->CreateFrame(alloc_hier, name, frame_out);
3692 HeapFree(GetProcessHeap(), 0, name);
3693 if (FAILED(hr)) return E_FAIL;
3696 D3DXMatrixIdentity(&frame->TransformationMatrix);
3697 next_child = &frame->pFrameFirstChild;
3698 next_container = &frame->pMeshContainer;
3700 while (SUCCEEDED(hr = get_next_child(filedata, &child, &type)))
3702 if (IsEqualGUID(type, &TID_D3DRMMesh)) {
3703 hr = load_mesh_container(child, options, device, alloc_hier, next_container);
3705 next_container = &(*next_container)->pNextMeshContainer;
3706 } else if (IsEqualGUID(type, &TID_D3DRMFrameTransformMatrix)) {
3707 hr = parse_transform_matrix(child, &frame->TransformationMatrix);
3708 } else if (IsEqualGUID(type, &TID_D3DRMFrame)) {
3709 hr = load_frame(child, options, device, alloc_hier, next_child);
3711 next_child = &(*next_child)->pFrameSibling;
3713 if (FAILED(hr)) break;
3715 if (hr == DXFILEERR_NOMOREOBJECTS)
3721 HRESULT WINAPI D3DXLoadMeshHierarchyFromXInMemory(LPCVOID memory,
3724 LPDIRECT3DDEVICE9 device,
3725 LPD3DXALLOCATEHIERARCHY alloc_hier,
3726 LPD3DXLOADUSERDATA load_user_data,
3727 LPD3DXFRAME *frame_hierarchy,
3728 LPD3DXANIMATIONCONTROLLER *anim_controller)
3731 IDirectXFile *dxfile = NULL;
3732 IDirectXFileEnumObject *enumobj = NULL;
3733 IDirectXFileData *filedata = NULL;
3734 DXFILELOADMEMORY source;
3735 D3DXFRAME *first_frame = NULL;
3736 D3DXFRAME **next_frame = &first_frame;
3738 TRACE("(%p, %u, %x, %p, %p, %p, %p, %p)\n", memory, memory_size, options,
3739 device, alloc_hier, load_user_data, frame_hierarchy, anim_controller);
3741 if (!memory || !memory_size || !device || !frame_hierarchy || !alloc_hier)
3742 return D3DERR_INVALIDCALL;
3743 if (load_user_data || anim_controller) {
3745 FIXME("Loading user data not implemented\n");
3746 if (anim_controller)
3747 FIXME("Animation controller creation not implemented\n");
3751 hr = DirectXFileCreate(&dxfile);
3752 if (FAILED(hr)) goto cleanup;
3754 hr = IDirectXFile_RegisterTemplates(dxfile, D3DRM_XTEMPLATES, D3DRM_XTEMPLATE_BYTES);
3755 if (FAILED(hr)) goto cleanup;
3757 source.lpMemory = (void*)memory;
3758 source.dSize = memory_size;
3759 hr = IDirectXFile_CreateEnumObject(dxfile, &source, DXFILELOAD_FROMMEMORY, &enumobj);
3760 if (FAILED(hr)) goto cleanup;
3762 while (SUCCEEDED(hr = IDirectXFileEnumObject_GetNextDataObject(enumobj, &filedata)))
3764 const GUID *guid = NULL;
3766 hr = IDirectXFileData_GetType(filedata, &guid);
3767 if (SUCCEEDED(hr)) {
3768 if (IsEqualGUID(guid, &TID_D3DRMMesh)) {
3769 hr = alloc_hier->lpVtbl->CreateFrame(alloc_hier, NULL, next_frame);
3775 D3DXMatrixIdentity(&(*next_frame)->TransformationMatrix);
3777 hr = load_mesh_container(filedata, options, device, alloc_hier, &(*next_frame)->pMeshContainer);
3778 if (FAILED(hr)) goto cleanup;
3779 } else if (IsEqualGUID(guid, &TID_D3DRMFrame)) {
3780 hr = load_frame(filedata, options, device, alloc_hier, next_frame);
3781 if (FAILED(hr)) goto cleanup;
3784 next_frame = &(*next_frame)->pFrameSibling;
3787 IDirectXFileData_Release(filedata);
3792 if (hr != DXFILEERR_NOMOREOBJECTS)
3797 } else if (first_frame->pFrameSibling) {
3798 D3DXFRAME *root_frame = NULL;
3799 hr = alloc_hier->lpVtbl->CreateFrame(alloc_hier, NULL, &root_frame);
3804 D3DXMatrixIdentity(&root_frame->TransformationMatrix);
3805 root_frame->pFrameFirstChild = first_frame;
3806 *frame_hierarchy = root_frame;
3809 *frame_hierarchy = first_frame;
3814 if (FAILED(hr) && first_frame) D3DXFrameDestroy(first_frame, alloc_hier);
3815 if (filedata) IDirectXFileData_Release(filedata);
3816 if (enumobj) IDirectXFileEnumObject_Release(enumobj);
3817 if (dxfile) IDirectXFile_Release(dxfile);
3821 HRESULT WINAPI D3DXCleanMesh(D3DXCLEANTYPE clean_type, LPD3DXMESH mesh_in, const DWORD *adjacency_in,
3822 LPD3DXMESH *mesh_out, DWORD *adjacency_out, LPD3DXBUFFER *errors_and_warnings)
3824 FIXME("(%u, %p, %p, %p, %p, %p)\n", clean_type, mesh_in, adjacency_in, mesh_out, adjacency_out, errors_and_warnings);
3829 HRESULT WINAPI D3DXFrameDestroy(LPD3DXFRAME frame, LPD3DXALLOCATEHIERARCHY alloc_hier)
3834 TRACE("(%p, %p)\n", frame, alloc_hier);
3836 if (!frame || !alloc_hier)
3837 return D3DERR_INVALIDCALL;
3840 D3DXMESHCONTAINER *container;
3841 D3DXFRAME *current_frame;
3843 if (frame->pFrameSibling) {
3844 current_frame = frame->pFrameSibling;
3845 frame->pFrameSibling = current_frame->pFrameSibling;
3846 current_frame->pFrameSibling = NULL;
3848 current_frame = frame;
3852 if (current_frame->pFrameFirstChild) {
3853 hr = D3DXFrameDestroy(current_frame->pFrameFirstChild, alloc_hier);
3854 if (FAILED(hr)) return hr;
3855 current_frame->pFrameFirstChild = NULL;
3858 container = current_frame->pMeshContainer;
3860 D3DXMESHCONTAINER *next_container = container->pNextMeshContainer;
3861 hr = alloc_hier->lpVtbl->DestroyMeshContainer(alloc_hier, container);
3862 if (FAILED(hr)) return hr;
3863 container = next_container;
3865 hr = alloc_hier->lpVtbl->DestroyFrame(alloc_hier, current_frame);
3866 if (FAILED(hr)) return hr;
3871 HRESULT WINAPI D3DXLoadMeshFromXA(LPCSTR filename,
3873 LPDIRECT3DDEVICE9 device,
3874 LPD3DXBUFFER *adjacency,
3875 LPD3DXBUFFER *materials,
3876 LPD3DXBUFFER *effect_instances,
3877 DWORD *num_materials,
3884 TRACE("(%s, %x, %p, %p, %p, %p, %p, %p)\n", debugstr_a(filename), options,
3885 device, adjacency, materials, effect_instances, num_materials, mesh);
3888 return D3DERR_INVALIDCALL;
3890 len = MultiByteToWideChar(CP_ACP, 0, filename, -1, NULL, 0);
3891 filenameW = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
3892 if (!filenameW) return E_OUTOFMEMORY;
3893 MultiByteToWideChar(CP_ACP, 0, filename, -1, filenameW, len);
3895 hr = D3DXLoadMeshFromXW(filenameW, options, device, adjacency, materials,
3896 effect_instances, num_materials, mesh);
3897 HeapFree(GetProcessHeap(), 0, filenameW);
3902 HRESULT WINAPI D3DXLoadMeshFromXW(LPCWSTR filename,
3904 LPDIRECT3DDEVICE9 device,
3905 LPD3DXBUFFER *adjacency,
3906 LPD3DXBUFFER *materials,
3907 LPD3DXBUFFER *effect_instances,
3908 DWORD *num_materials,
3915 TRACE("(%s, %x, %p, %p, %p, %p, %p, %p)\n", debugstr_w(filename), options,
3916 device, adjacency, materials, effect_instances, num_materials, mesh);
3919 return D3DERR_INVALIDCALL;
3921 hr = map_view_of_file(filename, &buffer, &size);
3923 return D3DXERR_INVALIDDATA;
3925 hr = D3DXLoadMeshFromXInMemory(buffer, size, options, device, adjacency,
3926 materials, effect_instances, num_materials, mesh);
3928 UnmapViewOfFile(buffer);
3933 HRESULT WINAPI D3DXLoadMeshFromXResource(HMODULE module,
3937 LPDIRECT3DDEVICE9 device,
3938 LPD3DXBUFFER *adjacency,
3939 LPD3DXBUFFER *materials,
3940 LPD3DXBUFFER *effect_instances,
3941 DWORD *num_materials,
3949 TRACE("(%p, %s, %s, %x, %p, %p, %p, %p, %p, %p)\n",
3950 module, debugstr_a(name), debugstr_a(type), options, device,
3951 adjacency, materials, effect_instances, num_materials, mesh);
3953 resinfo = FindResourceA(module, name, type);
3954 if (!resinfo) return D3DXERR_INVALIDDATA;
3956 hr = load_resource_into_memory(module, resinfo, &buffer, &size);
3957 if (FAILED(hr)) return D3DXERR_INVALIDDATA;
3959 return D3DXLoadMeshFromXInMemory(buffer, size, options, device, adjacency,
3960 materials, effect_instances, num_materials, mesh);
3963 struct mesh_container
3967 ID3DXBuffer *adjacency;
3968 ID3DXBuffer *materials;
3969 ID3DXBuffer *effects;
3970 DWORD num_materials;
3971 D3DXMATRIX transform;
3974 static HRESULT parse_frame(IDirectXFileData *filedata,
3976 LPDIRECT3DDEVICE9 device,
3977 const D3DXMATRIX *parent_transform,
3978 struct list *container_list,
3979 DWORD provide_flags)
3982 D3DXMATRIX transform = *parent_transform;
3983 IDirectXFileData *child;
3986 while (SUCCEEDED(hr = get_next_child(filedata, &child, &type)))
3988 if (IsEqualGUID(type, &TID_D3DRMMesh)) {
3989 struct mesh_container *container = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(*container));
3994 list_add_tail(container_list, &container->entry);
3995 container->transform = transform;
3997 hr = load_skin_mesh_from_xof(child, options, device,
3998 (provide_flags & PROVIDE_ADJACENCY) ? &container->adjacency : NULL,
3999 (provide_flags & PROVIDE_MATERIALS) ? &container->materials : NULL,
4000 NULL, &container->num_materials, NULL, &container->mesh);
4001 } else if (IsEqualGUID(type, &TID_D3DRMFrameTransformMatrix)) {
4002 D3DXMATRIX new_transform;
4003 hr = parse_transform_matrix(child, &new_transform);
4004 D3DXMatrixMultiply(&transform, &transform, &new_transform);
4005 } else if (IsEqualGUID(type, &TID_D3DRMFrame)) {
4006 hr = parse_frame(child, options, device, &transform, container_list, provide_flags);
4008 if (FAILED(hr)) break;
4010 return hr == DXFILEERR_NOMOREOBJECTS ? D3D_OK : hr;
4013 HRESULT WINAPI D3DXLoadMeshFromXInMemory(LPCVOID memory,
4016 LPDIRECT3DDEVICE9 device,
4017 LPD3DXBUFFER *adjacency_out,
4018 LPD3DXBUFFER *materials_out,
4019 LPD3DXBUFFER *effects_out,
4020 DWORD *num_materials_out,
4021 LPD3DXMESH *mesh_out)
4024 IDirectXFile *dxfile = NULL;
4025 IDirectXFileEnumObject *enumobj = NULL;
4026 IDirectXFileData *filedata = NULL;
4027 DXFILELOADMEMORY source;
4028 ID3DXBuffer *materials = NULL;
4029 ID3DXBuffer *effects = NULL;
4030 ID3DXBuffer *adjacency = NULL;
4031 struct list container_list = LIST_INIT(container_list);
4032 struct mesh_container *container_ptr, *next_container_ptr;
4033 DWORD num_materials;
4034 DWORD num_faces, num_vertices;
4035 D3DXMATRIX identity;
4037 DWORD provide_flags = 0;
4039 ID3DXMesh *concat_mesh = NULL;
4040 D3DVERTEXELEMENT9 concat_decl[MAX_FVF_DECL_SIZE];
4041 BYTE *concat_vertices = NULL;
4042 void *concat_indices = NULL;
4044 DWORD concat_vertex_size;
4046 TRACE("(%p, %u, %x, %p, %p, %p, %p, %p, %p)\n", memory, memory_size, options,
4047 device, adjacency_out, materials_out, effects_out, num_materials_out, mesh_out);
4049 if (!memory || !memory_size || !device || !mesh_out)
4050 return D3DERR_INVALIDCALL;
4052 hr = DirectXFileCreate(&dxfile);
4053 if (FAILED(hr)) goto cleanup;
4055 hr = IDirectXFile_RegisterTemplates(dxfile, D3DRM_XTEMPLATES, D3DRM_XTEMPLATE_BYTES);
4056 if (FAILED(hr)) goto cleanup;
4058 source.lpMemory = (void*)memory;
4059 source.dSize = memory_size;
4060 hr = IDirectXFile_CreateEnumObject(dxfile, &source, DXFILELOAD_FROMMEMORY, &enumobj);
4061 if (FAILED(hr)) goto cleanup;
4063 D3DXMatrixIdentity(&identity);
4064 if (adjacency_out) provide_flags |= PROVIDE_ADJACENCY;
4065 if (materials_out || effects_out) provide_flags |= PROVIDE_MATERIALS;
4067 while (SUCCEEDED(hr = IDirectXFileEnumObject_GetNextDataObject(enumobj, &filedata)))
4069 const GUID *guid = NULL;
4071 hr = IDirectXFileData_GetType(filedata, &guid);
4072 if (SUCCEEDED(hr)) {
4073 if (IsEqualGUID(guid, &TID_D3DRMMesh)) {
4074 container_ptr = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(*container_ptr));
4075 if (!container_ptr) {
4079 list_add_tail(&container_list, &container_ptr->entry);
4080 D3DXMatrixIdentity(&container_ptr->transform);
4082 hr = load_skin_mesh_from_xof(filedata, options, device,
4083 (provide_flags & PROVIDE_ADJACENCY) ? &container_ptr->adjacency : NULL,
4084 (provide_flags & PROVIDE_MATERIALS) ? &container_ptr->materials : NULL,
4085 NULL, &container_ptr->num_materials, NULL, &container_ptr->mesh);
4086 } else if (IsEqualGUID(guid, &TID_D3DRMFrame)) {
4087 hr = parse_frame(filedata, options, device, &identity, &container_list, provide_flags);
4089 if (FAILED(hr)) goto cleanup;
4091 IDirectXFileData_Release(filedata);
4096 if (hr != DXFILEERR_NOMOREOBJECTS)
4099 IDirectXFileEnumObject_Release(enumobj);
4101 IDirectXFile_Release(dxfile);
4104 if (list_empty(&container_list)) {
4113 LIST_FOR_EACH_ENTRY(container_ptr, &container_list, struct mesh_container, entry)
4115 ID3DXMesh *mesh = container_ptr->mesh;
4116 fvf |= mesh->lpVtbl->GetFVF(mesh);
4117 num_faces += mesh->lpVtbl->GetNumFaces(mesh);
4118 num_vertices += mesh->lpVtbl->GetNumVertices(mesh);
4119 num_materials += container_ptr->num_materials;
4122 hr = D3DXCreateMeshFVF(num_faces, num_vertices, options, fvf, device, &concat_mesh);
4123 if (FAILED(hr)) goto cleanup;
4125 hr = concat_mesh->lpVtbl->GetDeclaration(concat_mesh, concat_decl);
4126 if (FAILED(hr)) goto cleanup;
4128 concat_vertex_size = D3DXGetDeclVertexSize(concat_decl, 0);
4130 hr = concat_mesh->lpVtbl->LockVertexBuffer(concat_mesh, 0, (void**)&concat_vertices);
4131 if (FAILED(hr)) goto cleanup;
4133 LIST_FOR_EACH_ENTRY(container_ptr, &container_list, struct mesh_container, entry)
4135 D3DVERTEXELEMENT9 mesh_decl[MAX_FVF_DECL_SIZE];
4136 ID3DXMesh *mesh = container_ptr->mesh;
4137 DWORD num_mesh_vertices = mesh->lpVtbl->GetNumVertices(mesh);
4138 DWORD mesh_vertex_size;
4139 const BYTE *mesh_vertices;
4141 hr = mesh->lpVtbl->GetDeclaration(mesh, mesh_decl);
4142 if (FAILED(hr)) goto cleanup;
4144 mesh_vertex_size = D3DXGetDeclVertexSize(mesh_decl, 0);
4146 hr = mesh->lpVtbl->LockVertexBuffer(mesh, D3DLOCK_READONLY, (void**)&mesh_vertices);
4147 if (FAILED(hr)) goto cleanup;
4149 for (i = 0; i < num_mesh_vertices; i++) {
4153 D3DXVec3TransformCoord((D3DXVECTOR3*)concat_vertices,
4154 (D3DXVECTOR3*)mesh_vertices,
4155 &container_ptr->transform);
4156 for (j = 1; concat_decl[j].Stream != 0xff; j++)
4158 if (concat_decl[j].Usage == mesh_decl[k].Usage &&
4159 concat_decl[j].UsageIndex == mesh_decl[k].UsageIndex)
4161 if (concat_decl[j].Usage == D3DDECLUSAGE_NORMAL) {
4162 D3DXVec3TransformCoord((D3DXVECTOR3*)(concat_vertices + concat_decl[j].Offset),
4163 (D3DXVECTOR3*)(mesh_vertices + mesh_decl[k].Offset),
4164 &container_ptr->transform);
4166 memcpy(concat_vertices + concat_decl[j].Offset,
4167 mesh_vertices + mesh_decl[k].Offset,
4168 d3dx_decltype_size[mesh_decl[k].Type]);
4173 mesh_vertices += mesh_vertex_size;
4174 concat_vertices += concat_vertex_size;
4177 mesh->lpVtbl->UnlockVertexBuffer(mesh);
4180 concat_mesh->lpVtbl->UnlockVertexBuffer(concat_mesh);
4181 concat_vertices = NULL;
4183 hr = concat_mesh->lpVtbl->LockIndexBuffer(concat_mesh, 0, &concat_indices);
4184 if (FAILED(hr)) goto cleanup;
4187 LIST_FOR_EACH_ENTRY(container_ptr, &container_list, struct mesh_container, entry)
4189 ID3DXMesh *mesh = container_ptr->mesh;
4190 const void *mesh_indices;
4191 DWORD num_mesh_faces = mesh->lpVtbl->GetNumFaces(mesh);
4194 hr = mesh->lpVtbl->LockIndexBuffer(mesh, D3DLOCK_READONLY, (void**)&mesh_indices);
4195 if (FAILED(hr)) goto cleanup;
4197 if (options & D3DXMESH_32BIT) {
4198 DWORD *dest = concat_indices;
4199 const DWORD *src = mesh_indices;
4200 for (i = 0; i < num_mesh_faces * 3; i++)
4201 *dest++ = index_offset + *src++;
4202 concat_indices = dest;
4204 WORD *dest = concat_indices;
4205 const WORD *src = mesh_indices;
4206 for (i = 0; i < num_mesh_faces * 3; i++)
4207 *dest++ = index_offset + *src++;
4208 concat_indices = dest;
4210 mesh->lpVtbl->UnlockIndexBuffer(mesh);
4212 index_offset += num_mesh_faces * 3;
4215 concat_mesh->lpVtbl->UnlockIndexBuffer(concat_mesh);
4216 concat_indices = NULL;
4218 if (num_materials) {
4219 DWORD *concat_attrib_buffer = NULL;
4222 hr = concat_mesh->lpVtbl->LockAttributeBuffer(concat_mesh, 0, &concat_attrib_buffer);
4223 if (FAILED(hr)) goto cleanup;
4225 LIST_FOR_EACH_ENTRY(container_ptr, &container_list, struct mesh_container, entry)
4227 ID3DXMesh *mesh = container_ptr->mesh;
4228 const DWORD *mesh_attrib_buffer = NULL;
4229 DWORD count = mesh->lpVtbl->GetNumFaces(mesh);
4231 hr = mesh->lpVtbl->LockAttributeBuffer(mesh, D3DLOCK_READONLY, (DWORD**)&mesh_attrib_buffer);
4233 concat_mesh->lpVtbl->UnlockAttributeBuffer(concat_mesh);
4238 *concat_attrib_buffer++ = offset + *mesh_attrib_buffer++;
4240 mesh->lpVtbl->UnlockAttributeBuffer(mesh);
4241 offset += container_ptr->num_materials;
4243 concat_mesh->lpVtbl->UnlockAttributeBuffer(concat_mesh);
4246 if (materials_out || effects_out) {
4247 D3DXMATERIAL *out_ptr;
4248 if (!num_materials) {
4249 /* create default material */
4250 hr = D3DXCreateBuffer(sizeof(D3DXMATERIAL), &materials);
4251 if (FAILED(hr)) goto cleanup;
4253 out_ptr = ID3DXBuffer_GetBufferPointer(materials);
4254 out_ptr->MatD3D.Diffuse.r = 0.5f;
4255 out_ptr->MatD3D.Diffuse.g = 0.5f;
4256 out_ptr->MatD3D.Diffuse.b = 0.5f;
4257 out_ptr->MatD3D.Specular.r = 0.5f;
4258 out_ptr->MatD3D.Specular.g = 0.5f;
4259 out_ptr->MatD3D.Specular.b = 0.5f;
4260 /* D3DXCreateBuffer initializes the rest to zero */
4262 DWORD buffer_size = num_materials * sizeof(D3DXMATERIAL);
4263 char *strings_out_ptr;
4265 LIST_FOR_EACH_ENTRY(container_ptr, &container_list, struct mesh_container, entry)
4267 if (container_ptr->materials) {
4268 const D3DXMATERIAL *in_ptr = ID3DXBuffer_GetBufferPointer(container_ptr->materials);
4269 for (i = 0; i < container_ptr->num_materials; i++)
4271 if (in_ptr->pTextureFilename)
4272 buffer_size += strlen(in_ptr->pTextureFilename) + 1;
4278 hr = D3DXCreateBuffer(buffer_size, &materials);
4279 if (FAILED(hr)) goto cleanup;
4280 out_ptr = ID3DXBuffer_GetBufferPointer(materials);
4281 strings_out_ptr = (char*)(out_ptr + num_materials);
4283 LIST_FOR_EACH_ENTRY(container_ptr, &container_list, struct mesh_container, entry)
4285 if (container_ptr->materials) {
4286 const D3DXMATERIAL *in_ptr = ID3DXBuffer_GetBufferPointer(container_ptr->materials);
4287 for (i = 0; i < container_ptr->num_materials; i++)
4289 out_ptr->MatD3D = in_ptr->MatD3D;
4290 if (in_ptr->pTextureFilename) {
4291 out_ptr->pTextureFilename = strings_out_ptr;
4292 strcpy(out_ptr->pTextureFilename, in_ptr->pTextureFilename);
4293 strings_out_ptr += strlen(in_ptr->pTextureFilename) + 1;
4306 generate_effects(materials, num_materials, &effects);
4307 if (!materials_out) {
4308 ID3DXBuffer_Release(materials);
4313 if (adjacency_out) {
4314 if (!list_next(&container_list, list_head(&container_list))) {
4315 container_ptr = LIST_ENTRY(list_head(&container_list), struct mesh_container, entry);
4316 adjacency = container_ptr->adjacency;
4317 container_ptr->adjacency = NULL;
4322 hr = D3DXCreateBuffer(num_faces * 3 * sizeof(DWORD), &adjacency);
4323 if (FAILED(hr)) goto cleanup;
4325 out_ptr = ID3DXBuffer_GetBufferPointer(adjacency);
4326 LIST_FOR_EACH_ENTRY(container_ptr, &container_list, struct mesh_container, entry)
4328 DWORD *in_ptr = ID3DXBuffer_GetBufferPointer(container_ptr->adjacency);
4329 int count = 3 * container_ptr->mesh->lpVtbl->GetNumFaces(container_ptr->mesh);
4331 for (i = 0; i < count; i++)
4332 *out_ptr++ = offset + *in_ptr++;
4339 *mesh_out = concat_mesh;
4340 if (adjacency_out) *adjacency_out = adjacency;
4341 if (materials_out) *materials_out = materials;
4342 if (effects_out) *effects_out = effects;
4343 if (num_materials_out) *num_materials_out = num_materials;
4347 if (concat_indices) concat_mesh->lpVtbl->UnlockIndexBuffer(concat_mesh);
4348 if (concat_vertices) concat_mesh->lpVtbl->UnlockVertexBuffer(concat_mesh);
4349 if (filedata) IDirectXFileData_Release(filedata);
4350 if (enumobj) IDirectXFileEnumObject_Release(enumobj);
4351 if (dxfile) IDirectXFile_Release(dxfile);
4353 if (concat_mesh) IUnknown_Release(concat_mesh);
4354 if (materials) ID3DXBuffer_Release(materials);
4355 if (effects) ID3DXBuffer_Release(effects);
4356 if (adjacency) ID3DXBuffer_Release(adjacency);
4358 LIST_FOR_EACH_ENTRY_SAFE(container_ptr, next_container_ptr, &container_list, struct mesh_container, entry)
4360 if (container_ptr->mesh) IUnknown_Release(container_ptr->mesh);
4361 if (container_ptr->adjacency) ID3DXBuffer_Release(container_ptr->adjacency);
4362 if (container_ptr->materials) ID3DXBuffer_Release(container_ptr->materials);
4363 if (container_ptr->effects) ID3DXBuffer_Release(container_ptr->effects);
4364 HeapFree(GetProcessHeap(), 0, container_ptr);
4369 HRESULT WINAPI D3DXCreateBox(LPDIRECT3DDEVICE9 device, FLOAT width, FLOAT height,
4370 FLOAT depth, LPD3DXMESH* mesh, LPD3DXBUFFER* adjacency)
4372 FIXME("(%p, %f, %f, %f, %p, %p): stub\n", device, width, height, depth, mesh, adjacency);
4379 D3DXVECTOR3 position;
4383 typedef WORD face[3];
4391 static void free_sincos_table(struct sincos_table *sincos_table)
4393 HeapFree(GetProcessHeap(), 0, sincos_table->cos);
4394 HeapFree(GetProcessHeap(), 0, sincos_table->sin);
4397 /* pre compute sine and cosine tables; caller must free */
4398 static BOOL compute_sincos_table(struct sincos_table *sincos_table, float angle_start, float angle_step, int n)
4403 sincos_table->sin = HeapAlloc(GetProcessHeap(), 0, n * sizeof(*sincos_table->sin));
4404 if (!sincos_table->sin)
4408 sincos_table->cos = HeapAlloc(GetProcessHeap(), 0, n * sizeof(*sincos_table->cos));
4409 if (!sincos_table->cos)
4411 HeapFree(GetProcessHeap(), 0, sincos_table->sin);
4415 angle = angle_start;
4416 for (i = 0; i < n; i++)
4418 sincos_table->sin[i] = sin(angle);
4419 sincos_table->cos[i] = cos(angle);
4420 angle += angle_step;
4426 static WORD vertex_index(UINT slices, int slice, int stack)
4428 return stack*slices+slice+1;
4431 HRESULT WINAPI D3DXCreateSphere(LPDIRECT3DDEVICE9 device, FLOAT radius, UINT slices,
4432 UINT stacks, LPD3DXMESH* mesh, LPD3DXBUFFER* adjacency)
4434 DWORD number_of_vertices, number_of_faces;
4437 struct vertex *vertices;
4439 float phi_step, phi_start;
4440 struct sincos_table phi;
4441 float theta_step, theta, sin_theta, cos_theta;
4445 TRACE("(%p, %f, %u, %u, %p, %p)\n", device, radius, slices, stacks, mesh, adjacency);
4447 if (!device || radius < 0.0f || slices < 2 || stacks < 2 || !mesh)
4449 return D3DERR_INVALIDCALL;
4454 FIXME("Case of adjacency != NULL not implemented.\n");
4458 number_of_vertices = 2 + slices * (stacks-1);
4459 number_of_faces = 2 * slices + (stacks - 2) * (2 * slices);
4461 hr = D3DXCreateMeshFVF(number_of_faces, number_of_vertices, D3DXMESH_MANAGED,
4462 D3DFVF_XYZ | D3DFVF_NORMAL, device, &sphere);
4468 hr = sphere->lpVtbl->LockVertexBuffer(sphere, 0, (LPVOID *)&vertices);
4471 sphere->lpVtbl->Release(sphere);
4475 hr = sphere->lpVtbl->LockIndexBuffer(sphere, 0, (LPVOID *)&faces);
4478 sphere->lpVtbl->UnlockVertexBuffer(sphere);
4479 sphere->lpVtbl->Release(sphere);
4483 /* phi = angle on xz plane wrt z axis */
4484 phi_step = -2 * M_PI / slices;
4485 phi_start = M_PI / 2;
4487 if (!compute_sincos_table(&phi, phi_start, phi_step, slices))
4489 sphere->lpVtbl->UnlockIndexBuffer(sphere);
4490 sphere->lpVtbl->UnlockVertexBuffer(sphere);
4491 sphere->lpVtbl->Release(sphere);
4492 return E_OUTOFMEMORY;
4495 /* theta = angle on xy plane wrt x axis */
4496 theta_step = M_PI / stacks;
4502 vertices[vertex].normal.x = 0.0f;
4503 vertices[vertex].normal.y = 0.0f;
4504 vertices[vertex].normal.z = 1.0f;
4505 vertices[vertex].position.x = 0.0f;
4506 vertices[vertex].position.y = 0.0f;
4507 vertices[vertex].position.z = radius;
4510 for (stack = 0; stack < stacks - 1; stack++)
4512 sin_theta = sin(theta);
4513 cos_theta = cos(theta);
4515 for (slice = 0; slice < slices; slice++)
4517 vertices[vertex].normal.x = sin_theta * phi.cos[slice];
4518 vertices[vertex].normal.y = sin_theta * phi.sin[slice];
4519 vertices[vertex].normal.z = cos_theta;
4520 vertices[vertex].position.x = radius * sin_theta * phi.cos[slice];
4521 vertices[vertex].position.y = radius * sin_theta * phi.sin[slice];
4522 vertices[vertex].position.z = radius * cos_theta;
4529 /* top stack is triangle fan */
4531 faces[face][1] = slice + 1;
4532 faces[face][2] = slice;
4537 /* stacks in between top and bottom are quad strips */
4538 faces[face][0] = vertex_index(slices, slice-1, stack-1);
4539 faces[face][1] = vertex_index(slices, slice, stack-1);
4540 faces[face][2] = vertex_index(slices, slice-1, stack);
4543 faces[face][0] = vertex_index(slices, slice, stack-1);
4544 faces[face][1] = vertex_index(slices, slice, stack);
4545 faces[face][2] = vertex_index(slices, slice-1, stack);
4551 theta += theta_step;
4557 faces[face][2] = slice;
4562 faces[face][0] = vertex_index(slices, slice-1, stack-1);
4563 faces[face][1] = vertex_index(slices, 0, stack-1);
4564 faces[face][2] = vertex_index(slices, slice-1, stack);
4567 faces[face][0] = vertex_index(slices, 0, stack-1);
4568 faces[face][1] = vertex_index(slices, 0, stack);
4569 faces[face][2] = vertex_index(slices, slice-1, stack);
4574 vertices[vertex].position.x = 0.0f;
4575 vertices[vertex].position.y = 0.0f;
4576 vertices[vertex].position.z = -radius;
4577 vertices[vertex].normal.x = 0.0f;
4578 vertices[vertex].normal.y = 0.0f;
4579 vertices[vertex].normal.z = -1.0f;
4581 /* bottom stack is triangle fan */
4582 for (slice = 1; slice < slices; slice++)
4584 faces[face][0] = vertex_index(slices, slice-1, stack-1);
4585 faces[face][1] = vertex_index(slices, slice, stack-1);
4586 faces[face][2] = vertex;
4590 faces[face][0] = vertex_index(slices, slice-1, stack-1);
4591 faces[face][1] = vertex_index(slices, 0, stack-1);
4592 faces[face][2] = vertex;
4594 free_sincos_table(&phi);
4595 sphere->lpVtbl->UnlockIndexBuffer(sphere);
4596 sphere->lpVtbl->UnlockVertexBuffer(sphere);
4602 HRESULT WINAPI D3DXCreateCylinder(LPDIRECT3DDEVICE9 device, FLOAT radius1, FLOAT radius2, FLOAT length, UINT slices,
4603 UINT stacks, LPD3DXMESH* mesh, LPD3DXBUFFER* adjacency)
4605 DWORD number_of_vertices, number_of_faces;
4607 ID3DXMesh *cylinder;
4608 struct vertex *vertices;
4610 float theta_step, theta_start;
4611 struct sincos_table theta;
4612 float delta_radius, radius, radius_step;
4613 float z, z_step, z_normal;
4617 TRACE("(%p, %f, %f, %f, %u, %u, %p, %p)\n", device, radius1, radius2, length, slices, stacks, mesh, adjacency);
4619 if (device == NULL || radius1 < 0.0f || radius2 < 0.0f || length < 0.0f || slices < 2 || stacks < 1 || mesh == NULL)
4621 return D3DERR_INVALIDCALL;
4626 FIXME("Case of adjacency != NULL not implemented.\n");
4630 number_of_vertices = 2 + (slices * (3 + stacks));
4631 number_of_faces = 2 * slices + stacks * (2 * slices);
4633 hr = D3DXCreateMeshFVF(number_of_faces, number_of_vertices, D3DXMESH_MANAGED,
4634 D3DFVF_XYZ | D3DFVF_NORMAL, device, &cylinder);
4640 hr = cylinder->lpVtbl->LockVertexBuffer(cylinder, 0, (LPVOID *)&vertices);
4643 cylinder->lpVtbl->Release(cylinder);
4647 hr = cylinder->lpVtbl->LockIndexBuffer(cylinder, 0, (LPVOID *)&faces);
4650 cylinder->lpVtbl->UnlockVertexBuffer(cylinder);
4651 cylinder->lpVtbl->Release(cylinder);
4655 /* theta = angle on xy plane wrt x axis */
4656 theta_step = -2 * M_PI / slices;
4657 theta_start = M_PI / 2;
4659 if (!compute_sincos_table(&theta, theta_start, theta_step, slices))
4661 cylinder->lpVtbl->UnlockIndexBuffer(cylinder);
4662 cylinder->lpVtbl->UnlockVertexBuffer(cylinder);
4663 cylinder->lpVtbl->Release(cylinder);
4664 return E_OUTOFMEMORY;
4670 delta_radius = radius1 - radius2;
4672 radius_step = delta_radius / stacks;
4675 z_step = length / stacks;
4676 z_normal = delta_radius / length;
4677 if (isnan(z_normal))
4682 vertices[vertex].normal.x = 0.0f;
4683 vertices[vertex].normal.y = 0.0f;
4684 vertices[vertex].normal.z = -1.0f;
4685 vertices[vertex].position.x = 0.0f;
4686 vertices[vertex].position.y = 0.0f;
4687 vertices[vertex++].position.z = z;
4689 for (slice = 0; slice < slices; slice++, vertex++)
4691 vertices[vertex].normal.x = 0.0f;
4692 vertices[vertex].normal.y = 0.0f;
4693 vertices[vertex].normal.z = -1.0f;
4694 vertices[vertex].position.x = radius * theta.cos[slice];
4695 vertices[vertex].position.y = radius * theta.sin[slice];
4696 vertices[vertex].position.z = z;
4701 faces[face][1] = slice;
4702 faces[face++][2] = slice + 1;
4707 faces[face][1] = slice;
4708 faces[face++][2] = 1;
4710 for (stack = 1; stack <= stacks+1; stack++)
4712 for (slice = 0; slice < slices; slice++, vertex++)
4714 vertices[vertex].normal.x = theta.cos[slice];
4715 vertices[vertex].normal.y = theta.sin[slice];
4716 vertices[vertex].normal.z = z_normal;
4717 D3DXVec3Normalize(&vertices[vertex].normal, &vertices[vertex].normal);
4718 vertices[vertex].position.x = radius * theta.cos[slice];
4719 vertices[vertex].position.y = radius * theta.sin[slice];
4720 vertices[vertex].position.z = z;
4722 if (stack > 1 && slice > 0)
4724 faces[face][0] = vertex_index(slices, slice-1, stack-1);
4725 faces[face][1] = vertex_index(slices, slice-1, stack);
4726 faces[face++][2] = vertex_index(slices, slice, stack-1);
4728 faces[face][0] = vertex_index(slices, slice, stack-1);
4729 faces[face][1] = vertex_index(slices, slice-1, stack);
4730 faces[face++][2] = vertex_index(slices, slice, stack);
4736 faces[face][0] = vertex_index(slices, slice-1, stack-1);
4737 faces[face][1] = vertex_index(slices, slice-1, stack);
4738 faces[face++][2] = vertex_index(slices, 0, stack-1);
4740 faces[face][0] = vertex_index(slices, 0, stack-1);
4741 faces[face][1] = vertex_index(slices, slice-1, stack);
4742 faces[face++][2] = vertex_index(slices, 0, stack);
4745 if (stack < stacks + 1)
4748 radius -= radius_step;
4752 for (slice = 0; slice < slices; slice++, vertex++)
4754 vertices[vertex].normal.x = 0.0f;
4755 vertices[vertex].normal.y = 0.0f;
4756 vertices[vertex].normal.z = 1.0f;
4757 vertices[vertex].position.x = radius * theta.cos[slice];
4758 vertices[vertex].position.y = radius * theta.sin[slice];
4759 vertices[vertex].position.z = z;
4763 faces[face][0] = vertex_index(slices, slice-1, stack);
4764 faces[face][1] = number_of_vertices - 1;
4765 faces[face++][2] = vertex_index(slices, slice, stack);
4769 vertices[vertex].position.x = 0.0f;
4770 vertices[vertex].position.y = 0.0f;
4771 vertices[vertex].position.z = z;
4772 vertices[vertex].normal.x = 0.0f;
4773 vertices[vertex].normal.y = 0.0f;
4774 vertices[vertex].normal.z = 1.0f;
4776 faces[face][0] = vertex_index(slices, slice-1, stack);
4777 faces[face][1] = number_of_vertices - 1;
4778 faces[face][2] = vertex_index(slices, 0, stack);
4780 free_sincos_table(&theta);
4781 cylinder->lpVtbl->UnlockIndexBuffer(cylinder);
4782 cylinder->lpVtbl->UnlockVertexBuffer(cylinder);
4788 HRESULT WINAPI D3DXCreateTeapot(LPDIRECT3DDEVICE9 device, LPD3DXMESH *mesh, LPD3DXBUFFER* adjacency)
4790 FIXME("(%p, %p, %p): stub\n", device, mesh, adjacency);
4795 HRESULT WINAPI D3DXCreateTextA(LPDIRECT3DDEVICE9 device,
4796 HDC hdc, LPCSTR text,
4797 FLOAT deviation, FLOAT extrusion,
4798 LPD3DXMESH *mesh, LPD3DXBUFFER *adjacency,
4799 LPGLYPHMETRICSFLOAT glyphmetrics)
4805 TRACE("(%p, %p, %s, %f, %f, %p, %p, %p)\n", device, hdc,
4806 debugstr_a(text), deviation, extrusion, mesh, adjacency, glyphmetrics);
4809 return D3DERR_INVALIDCALL;
4811 len = MultiByteToWideChar(CP_ACP, 0, text, -1, NULL, 0);
4812 textW = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
4813 MultiByteToWideChar(CP_ACP, 0, text, -1, textW, len);
4815 hr = D3DXCreateTextW(device, hdc, textW, deviation, extrusion,
4816 mesh, adjacency, glyphmetrics);
4817 HeapFree(GetProcessHeap(), 0, textW);
4823 POINTTYPE_CURVE = 0,
4825 POINTTYPE_CURVE_START,
4826 POINTTYPE_CURVE_END,
4827 POINTTYPE_CURVE_MIDDLE,
4833 enum pointtype corner;
4836 struct dynamic_array
4838 int count, capacity;
4842 /* is a dynamic_array */
4845 int count, capacity;
4846 struct point2d *items;
4849 /* is a dynamic_array */
4850 struct outline_array
4852 int count, capacity;
4853 struct outline *items;
4862 struct point2d_index
4864 struct outline *outline;
4868 struct point2d_index_array
4871 struct point2d_index *items;
4876 struct outline_array outlines;
4877 struct face_array faces;
4878 struct point2d_index_array ordered_vertices;
4882 /* is an dynamic_array */
4885 int count, capacity;
4889 /* complex polygons are split into monotone polygons, which have
4890 * at most 2 intersections with the vertical sweep line */
4891 struct triangulation
4893 struct word_array vertex_stack;
4894 BOOL last_on_top, merging;
4897 /* is an dynamic_array */
4898 struct triangulation_array
4900 int count, capacity;
4901 struct triangulation *items;
4903 struct glyphinfo *glyph;
4906 static BOOL reserve(struct dynamic_array *array, int count, int itemsize)
4908 if (count > array->capacity) {
4911 if (array->items && array->capacity) {
4912 new_capacity = max(array->capacity * 2, count);
4913 new_buffer = HeapReAlloc(GetProcessHeap(), 0, array->items, new_capacity * itemsize);
4915 new_capacity = max(16, count);
4916 new_buffer = HeapAlloc(GetProcessHeap(), 0, new_capacity * itemsize);
4920 array->items = new_buffer;
4921 array->capacity = new_capacity;
4926 static struct point2d *add_points(struct outline *array, int num)
4928 struct point2d *item;
4930 if (!reserve((struct dynamic_array *)array, array->count + num, sizeof(array->items[0])))
4933 item = &array->items[array->count];
4934 array->count += num;
4938 static struct outline *add_outline(struct outline_array *array)
4940 struct outline *item;
4942 if (!reserve((struct dynamic_array *)array, array->count + 1, sizeof(array->items[0])))
4945 item = &array->items[array->count++];
4946 ZeroMemory(item, sizeof(*item));
4950 static inline face *add_face(struct face_array *array)
4952 return &array->items[array->count++];
4955 static struct triangulation *add_triangulation(struct triangulation_array *array)
4957 struct triangulation *item;
4959 if (!reserve((struct dynamic_array *)array, array->count + 1, sizeof(array->items[0])))
4962 item = &array->items[array->count++];
4963 ZeroMemory(item, sizeof(*item));
4967 static HRESULT add_vertex_index(struct word_array *array, WORD vertex_index)
4969 if (!reserve((struct dynamic_array *)array, array->count + 1, sizeof(array->items[0])))
4970 return E_OUTOFMEMORY;
4972 array->items[array->count++] = vertex_index;
4976 /* assume fixed point numbers can be converted to float point in place */
4977 C_ASSERT(sizeof(FIXED) == sizeof(float));
4978 C_ASSERT(sizeof(POINTFX) == sizeof(D3DXVECTOR2));
4980 static inline D3DXVECTOR2 *convert_fixed_to_float(POINTFX *pt, int count, float emsquare)
4982 D3DXVECTOR2 *ret = (D3DXVECTOR2*)pt;
4984 D3DXVECTOR2 *pt_flt = (D3DXVECTOR2*)pt;
4985 pt_flt->x = (pt->x.value + pt->x.fract / (float)0x10000) / emsquare;
4986 pt_flt->y = (pt->y.value + pt->y.fract / (float)0x10000) / emsquare;
4992 static HRESULT add_bezier_points(struct outline *outline, const D3DXVECTOR2 *p1,
4993 const D3DXVECTOR2 *p2, const D3DXVECTOR2 *p3,
4994 float max_deviation_sq)
4996 D3DXVECTOR2 split1 = {0, 0}, split2 = {0, 0}, middle, vec;
4999 D3DXVec2Scale(&split1, D3DXVec2Add(&split1, p1, p2), 0.5f);
5000 D3DXVec2Scale(&split2, D3DXVec2Add(&split2, p2, p3), 0.5f);
5001 D3DXVec2Scale(&middle, D3DXVec2Add(&middle, &split1, &split2), 0.5f);
5003 deviation_sq = D3DXVec2LengthSq(D3DXVec2Subtract(&vec, &middle, p2));
5004 if (deviation_sq < max_deviation_sq) {
5005 struct point2d *pt = add_points(outline, 1);
5006 if (!pt) return E_OUTOFMEMORY;
5008 pt->corner = POINTTYPE_CURVE;
5009 /* the end point is omitted because the end line merges into the next segment of
5010 * the split bezier curve, and the end of the split bezier curve is added outside
5011 * this recursive function. */
5013 HRESULT hr = add_bezier_points(outline, p1, &split1, &middle, max_deviation_sq);
5014 if (hr != S_OK) return hr;
5015 hr = add_bezier_points(outline, &middle, &split2, p3, max_deviation_sq);
5016 if (hr != S_OK) return hr;
5022 static inline BOOL is_direction_similar(D3DXVECTOR2 *dir1, D3DXVECTOR2 *dir2, float cos_theta)
5024 /* dot product = cos(theta) */
5025 return D3DXVec2Dot(dir1, dir2) > cos_theta;
5028 static inline D3DXVECTOR2 *unit_vec2(D3DXVECTOR2 *dir, const D3DXVECTOR2 *pt1, const D3DXVECTOR2 *pt2)
5030 return D3DXVec2Normalize(D3DXVec2Subtract(dir, pt2, pt1), dir);
5040 static BOOL attempt_line_merge(struct outline *outline,
5042 const D3DXVECTOR2 *nextpt,
5044 const struct cos_table *table)
5046 D3DXVECTOR2 curdir, lastdir;
5047 struct point2d *prevpt, *pt;
5050 pt = &outline->items[pt_index];
5051 pt_index = (pt_index - 1 + outline->count) % outline->count;
5052 prevpt = &outline->items[pt_index];
5055 pt->corner = pt->corner != POINTTYPE_CORNER ? POINTTYPE_CURVE_MIDDLE : POINTTYPE_CURVE_START;
5057 if (outline->count < 2)
5060 /* remove last point if the next line continues the last line */
5061 unit_vec2(&lastdir, &prevpt->pos, &pt->pos);
5062 unit_vec2(&curdir, &pt->pos, nextpt);
5063 if (is_direction_similar(&lastdir, &curdir, table->cos_half))
5066 if (pt->corner == POINTTYPE_CURVE_END)
5067 prevpt->corner = pt->corner;
5068 if (prevpt->corner == POINTTYPE_CURVE_END && to_curve)
5069 prevpt->corner = POINTTYPE_CURVE_MIDDLE;
5073 if (outline->count < 2)
5076 pt_index = (pt_index - 1 + outline->count) % outline->count;
5077 prevpt = &outline->items[pt_index];
5078 unit_vec2(&lastdir, &prevpt->pos, &pt->pos);
5079 unit_vec2(&curdir, &pt->pos, nextpt);
5084 static HRESULT create_outline(struct glyphinfo *glyph, void *raw_outline, int datasize,
5085 float max_deviation_sq, float emsquare, const struct cos_table *cos_table)
5087 TTPOLYGONHEADER *header = (TTPOLYGONHEADER *)raw_outline;
5089 while ((char *)header < (char *)raw_outline + datasize)
5091 TTPOLYCURVE *curve = (TTPOLYCURVE *)(header + 1);
5092 struct point2d *lastpt, *pt;
5093 D3DXVECTOR2 lastdir;
5094 D3DXVECTOR2 *pt_flt;
5096 struct outline *outline = add_outline(&glyph->outlines);
5099 return E_OUTOFMEMORY;
5101 pt = add_points(outline, 1);
5103 return E_OUTOFMEMORY;
5104 pt_flt = convert_fixed_to_float(&header->pfxStart, 1, emsquare);
5106 pt->corner = POINTTYPE_CORNER;
5108 if (header->dwType != TT_POLYGON_TYPE)
5109 FIXME("Unknown header type %d\n", header->dwType);
5111 while ((char *)curve < (char *)header + header->cb)
5113 D3DXVECTOR2 bezier_start = outline->items[outline->count - 1].pos;
5114 BOOL to_curve = curve->wType != TT_PRIM_LINE && curve->cpfx > 1;
5117 curve = (TTPOLYCURVE *)&curve->apfx[curve->cpfx];
5121 pt_flt = convert_fixed_to_float(curve->apfx, curve->cpfx, emsquare);
5123 attempt_line_merge(outline, outline->count - 1, &pt_flt[0], to_curve, cos_table);
5128 int count = curve->cpfx;
5133 D3DXVECTOR2 bezier_end;
5135 D3DXVec2Scale(&bezier_end, D3DXVec2Add(&bezier_end, &pt_flt[j], &pt_flt[j+1]), 0.5f);
5136 hr = add_bezier_points(outline, &bezier_start, &pt_flt[j], &bezier_end, max_deviation_sq);
5139 bezier_start = bezier_end;
5143 hr = add_bezier_points(outline, &bezier_start, &pt_flt[j], &pt_flt[j+1], max_deviation_sq);
5147 pt = add_points(outline, 1);
5149 return E_OUTOFMEMORY;
5151 pt->pos = pt_flt[j];
5152 pt->corner = POINTTYPE_CURVE_END;
5154 pt = add_points(outline, curve->cpfx);
5156 return E_OUTOFMEMORY;
5157 for (j = 0; j < curve->cpfx; j++)
5159 pt->pos = pt_flt[j];
5160 pt->corner = POINTTYPE_CORNER;
5165 curve = (TTPOLYCURVE *)&curve->apfx[curve->cpfx];
5168 /* remove last point if the next line continues the last line */
5169 if (outline->count >= 3) {
5172 lastpt = &outline->items[outline->count - 1];
5173 pt = &outline->items[0];
5174 if (pt->pos.x == lastpt->pos.x && pt->pos.y == lastpt->pos.y) {
5175 if (lastpt->corner == POINTTYPE_CURVE_END)
5177 if (pt->corner == POINTTYPE_CURVE_START)
5178 pt->corner = POINTTYPE_CURVE_MIDDLE;
5180 pt->corner = POINTTYPE_CURVE_END;
5183 lastpt = &outline->items[outline->count - 1];
5185 /* outline closed with a line from end to start point */
5186 attempt_line_merge(outline, outline->count - 1, &pt->pos, FALSE, cos_table);
5188 lastpt = &outline->items[0];
5189 to_curve = lastpt->corner != POINTTYPE_CORNER && lastpt->corner != POINTTYPE_CURVE_END;
5190 if (lastpt->corner == POINTTYPE_CURVE_START)
5191 lastpt->corner = POINTTYPE_CORNER;
5192 pt = &outline->items[1];
5193 if (attempt_line_merge(outline, 0, &pt->pos, to_curve, cos_table))
5194 *lastpt = outline->items[outline->count];
5197 lastpt = &outline->items[outline->count - 1];
5198 pt = &outline->items[0];
5199 unit_vec2(&lastdir, &lastpt->pos, &pt->pos);
5200 for (j = 0; j < outline->count; j++)
5205 pt = &outline->items[(j + 1) % outline->count];
5206 unit_vec2(&curdir, &lastpt->pos, &pt->pos);
5208 switch (lastpt->corner)
5210 case POINTTYPE_CURVE_START:
5211 case POINTTYPE_CURVE_END:
5212 if (!is_direction_similar(&lastdir, &curdir, cos_table->cos_45))
5213 lastpt->corner = POINTTYPE_CORNER;
5215 case POINTTYPE_CURVE_MIDDLE:
5216 if (!is_direction_similar(&lastdir, &curdir, cos_table->cos_90))
5217 lastpt->corner = POINTTYPE_CORNER;
5219 lastpt->corner = POINTTYPE_CURVE;
5227 header = (TTPOLYGONHEADER *)((char *)header + header->cb);
5232 /* Get the y-distance from a line to a point */
5233 static float get_line_to_point_y_distance(D3DXVECTOR2 *line_pt1,
5234 D3DXVECTOR2 *line_pt2,
5237 D3DXVECTOR2 line_vec = {0, 0};
5241 D3DXVec2Subtract(&line_vec, line_pt2, line_pt1);
5242 line_pt_dx = point->x - line_pt1->x;
5243 line_y = line_pt1->y + (line_vec.y * line_pt_dx) / line_vec.x;
5244 return point->y - line_y;
5247 static D3DXVECTOR2 *get_indexed_point(struct point2d_index *pt_idx)
5249 return &pt_idx->outline->items[pt_idx->vertex].pos;
5252 static D3DXVECTOR2 *get_ordered_vertex(struct glyphinfo *glyph, WORD index)
5254 return get_indexed_point(&glyph->ordered_vertices.items[index]);
5257 static void remove_triangulation(struct triangulation_array *array, struct triangulation *item)
5259 HeapFree(GetProcessHeap(), 0, item->vertex_stack.items);
5260 MoveMemory(item, item + 1, (char*)&array->items[array->count] - (char*)(item + 1));
5264 static HRESULT triangulation_add_point(struct triangulation **t_ptr,
5265 struct triangulation_array *triangulations,
5269 struct glyphinfo *glyph = triangulations->glyph;
5270 struct triangulation *t = *t_ptr;
5275 if (t->last_on_top) {
5283 if (t->last_on_top != to_top && t->vertex_stack.count > 1) {
5284 /* consume all vertices on the stack */
5285 WORD last_pt = t->vertex_stack.items[0];
5287 for (i = 1; i < t->vertex_stack.count; i++)
5289 face = add_face(&glyph->faces);
5290 if (!face) return E_OUTOFMEMORY;
5291 (*face)[0] = vtx_idx;
5292 (*face)[f1] = last_pt;
5293 (*face)[f2] = last_pt = t->vertex_stack.items[i];
5295 t->vertex_stack.items[0] = last_pt;
5296 t->vertex_stack.count = 1;
5297 } else if (t->vertex_stack.count > 1) {
5298 int i = t->vertex_stack.count - 1;
5299 D3DXVECTOR2 *point = get_ordered_vertex(glyph, vtx_idx);
5300 WORD top_idx = t->vertex_stack.items[i--];
5301 D3DXVECTOR2 *top_pt = get_ordered_vertex(glyph, top_idx);
5305 WORD prev_idx = t->vertex_stack.items[i--];
5306 D3DXVECTOR2 *prev_pt = get_ordered_vertex(glyph, prev_idx);
5308 if (prev_pt->x != top_pt->x &&
5309 ((to_top && get_line_to_point_y_distance(prev_pt, top_pt, point) > 0) ||
5310 (!to_top && get_line_to_point_y_distance(prev_pt, top_pt, point) < 0)))
5313 face = add_face(&glyph->faces);
5314 if (!face) return E_OUTOFMEMORY;
5315 (*face)[0] = vtx_idx;
5316 (*face)[f1] = prev_idx;
5317 (*face)[f2] = top_idx;
5321 t->vertex_stack.count--;
5324 t->last_on_top = to_top;
5326 hr = add_vertex_index(&t->vertex_stack, vtx_idx);
5328 if (hr == S_OK && t->merging) {
5329 struct triangulation *t2;
5331 t2 = to_top ? t - 1 : t + 1;
5332 t2->merging = FALSE;
5333 hr = triangulation_add_point(&t2, triangulations, vtx_idx, to_top);
5334 if (hr != S_OK) return hr;
5335 remove_triangulation(triangulations, t);
5343 /* check if the point is next on the outline for either the top or bottom */
5344 static D3DXVECTOR2 *triangulation_get_next_point(struct triangulation *t, struct glyphinfo *glyph, BOOL on_top)
5346 int i = t->last_on_top == on_top ? t->vertex_stack.count - 1 : 0;
5347 WORD idx = t->vertex_stack.items[i];
5348 struct point2d_index *pt_idx = &glyph->ordered_vertices.items[idx];
5349 struct outline *outline = pt_idx->outline;
5352 i = (pt_idx->vertex + outline->count - 1) % outline->count;
5354 i = (pt_idx->vertex + 1) % outline->count;
5356 return &outline->items[i].pos;
5359 static int compare_vertex_indices(const void *a, const void *b)
5361 const struct point2d_index *idx1 = a, *idx2 = b;
5362 const D3DXVECTOR2 *p1 = &idx1->outline->items[idx1->vertex].pos;
5363 const D3DXVECTOR2 *p2 = &idx2->outline->items[idx2->vertex].pos;
5364 float diff = p1->x - p2->x;
5367 diff = p1->y - p2->y;
5369 return diff == 0.0f ? 0 : (diff > 0.0f ? -1 : 1);
5372 static HRESULT triangulate(struct triangulation_array *triangulations)
5376 struct glyphinfo *glyph = triangulations->glyph;
5377 int nb_vertices = 0;
5379 struct point2d_index *idx_ptr;
5381 for (i = 0; i < glyph->outlines.count; i++)
5382 nb_vertices += glyph->outlines.items[i].count;
5384 glyph->ordered_vertices.items = HeapAlloc(GetProcessHeap(), 0,
5385 nb_vertices * sizeof(*glyph->ordered_vertices.items));
5386 if (!glyph->ordered_vertices.items)
5387 return E_OUTOFMEMORY;
5389 idx_ptr = glyph->ordered_vertices.items;
5390 for (i = 0; i < glyph->outlines.count; i++)
5392 struct outline *outline = &glyph->outlines.items[i];
5395 idx_ptr->outline = outline;
5396 idx_ptr->vertex = 0;
5398 for (j = outline->count - 1; j > 0; j--)
5400 idx_ptr->outline = outline;
5401 idx_ptr->vertex = j;
5405 glyph->ordered_vertices.count = nb_vertices;
5407 /* Native implementation seems to try to create a triangle fan from
5408 * the first outline point if the glyph only has one outline. */
5409 if (glyph->outlines.count == 1)
5411 struct outline *outline = glyph->outlines.items;
5412 D3DXVECTOR2 *base = &outline->items[0].pos;
5413 D3DXVECTOR2 *last = &outline->items[1].pos;
5416 for (i = 2; i < outline->count; i++)
5418 D3DXVECTOR2 *next = &outline->items[i].pos;
5419 D3DXVECTOR2 v1 = {0.0f, 0.0f};
5420 D3DXVECTOR2 v2 = {0.0f, 0.0f};
5422 D3DXVec2Subtract(&v1, base, last);
5423 D3DXVec2Subtract(&v2, last, next);
5424 ccw = D3DXVec2CCW(&v1, &v2);
5432 glyph->faces.items = HeapAlloc(GetProcessHeap(), 0,
5433 (outline->count - 2) * sizeof(glyph->faces.items[0]));
5434 if (!glyph->faces.items)
5435 return E_OUTOFMEMORY;
5437 glyph->faces.count = outline->count - 2;
5438 for (i = 0; i < glyph->faces.count; i++)
5440 glyph->faces.items[i][0] = 0;
5441 glyph->faces.items[i][1] = i + 1;
5442 glyph->faces.items[i][2] = i + 2;
5448 /* Perform 2D polygon triangulation for complex glyphs.
5449 * Triangulation is performed using a sweep line concept, from right to left,
5450 * by processing vertices in sorted order. Complex polygons are split into
5451 * monotone polygons which are triangulated separately. */
5452 /* FIXME: The order of the faces is not consistent with the native implementation. */
5454 /* Reserve space for maximum possible faces from triangulation.
5455 * # faces for outer outlines = outline->count - 2
5456 * # faces for inner outlines = outline->count + 2
5457 * There must be at least 1 outer outline. */
5458 glyph->faces.items = HeapAlloc(GetProcessHeap(), 0,
5459 (nb_vertices + glyph->outlines.count * 2 - 4) * sizeof(glyph->faces.items[0]));
5460 if (!glyph->faces.items)
5461 return E_OUTOFMEMORY;
5463 qsort(glyph->ordered_vertices.items, nb_vertices,
5464 sizeof(glyph->ordered_vertices.items[0]), compare_vertex_indices);
5465 for (sweep_idx = 0; sweep_idx < glyph->ordered_vertices.count; sweep_idx++)
5468 int end = triangulations->count;
5472 D3DXVECTOR2 *sweep_vtx = get_ordered_vertex(glyph, sweep_idx);
5473 int current = (start + end) / 2;
5474 struct triangulation *t = &triangulations->items[current];
5475 BOOL on_top_outline = FALSE;
5476 D3DXVECTOR2 *top_next, *bottom_next;
5477 WORD top_idx, bottom_idx;
5479 if (t->merging && t->last_on_top)
5480 top_next = triangulation_get_next_point(t + 1, glyph, TRUE);
5482 top_next = triangulation_get_next_point(t, glyph, TRUE);
5483 if (sweep_vtx == top_next)
5485 if (t->merging && t->last_on_top)
5487 hr = triangulation_add_point(&t, triangulations, sweep_idx, TRUE);
5488 if (hr != S_OK) return hr;
5490 if (t + 1 < &triangulations->items[triangulations->count] &&
5491 triangulation_get_next_point(t + 1, glyph, FALSE) == sweep_vtx)
5493 /* point also on bottom outline of higher triangulation */
5494 struct triangulation *t2 = t + 1;
5495 hr = triangulation_add_point(&t2, triangulations, sweep_idx, FALSE);
5496 if (hr != S_OK) return hr;
5501 on_top_outline = TRUE;
5504 if (t->merging && !t->last_on_top)
5505 bottom_next = triangulation_get_next_point(t - 1, glyph, FALSE);
5507 bottom_next = triangulation_get_next_point(t, glyph, FALSE);
5508 if (sweep_vtx == bottom_next)
5510 if (t->merging && !t->last_on_top)
5512 if (on_top_outline) {
5513 /* outline finished */
5514 remove_triangulation(triangulations, t);
5518 hr = triangulation_add_point(&t, triangulations, sweep_idx, FALSE);
5519 if (hr != S_OK) return hr;
5521 if (t > triangulations->items &&
5522 triangulation_get_next_point(t - 1, glyph, TRUE) == sweep_vtx)
5524 struct triangulation *t2 = t - 1;
5525 /* point also on top outline of lower triangulation */
5526 hr = triangulation_add_point(&t2, triangulations, sweep_idx, TRUE);
5527 if (hr != S_OK) return hr;
5528 t = t2 + 1; /* t may be invalidated by triangulation merging */
5538 if (t->last_on_top) {
5539 top_idx = t->vertex_stack.items[t->vertex_stack.count - 1];
5540 bottom_idx = t->vertex_stack.items[0];
5542 top_idx = t->vertex_stack.items[0];
5543 bottom_idx = t->vertex_stack.items[t->vertex_stack.count - 1];
5546 /* check if the point is inside or outside this polygon */
5547 if (get_line_to_point_y_distance(get_ordered_vertex(glyph, top_idx),
5548 top_next, sweep_vtx) > 0)
5550 start = current + 1;
5551 } else if (get_line_to_point_y_distance(get_ordered_vertex(glyph, bottom_idx),
5552 bottom_next, sweep_vtx) < 0)
5555 } else if (t->merging) {
5556 /* inside, so cancel merging */
5557 struct triangulation *t2 = t->last_on_top ? t + 1 : t - 1;
5559 t2->merging = FALSE;
5560 hr = triangulation_add_point(&t, triangulations, sweep_idx, t->last_on_top);
5561 if (hr != S_OK) return hr;
5562 hr = triangulation_add_point(&t2, triangulations, sweep_idx, t2->last_on_top);
5563 if (hr != S_OK) return hr;
5566 /* inside, so split polygon into two monotone parts */
5567 struct triangulation *t2 = add_triangulation(triangulations);
5568 if (!t2) return E_OUTOFMEMORY;
5569 MoveMemory(t + 1, t, (char*)(t2 + 1) - (char*)t);
5570 if (t->last_on_top) {
5577 ZeroMemory(&t2->vertex_stack, sizeof(t2->vertex_stack));
5578 hr = add_vertex_index(&t2->vertex_stack, t->vertex_stack.items[t->vertex_stack.count - 1]);
5579 if (hr != S_OK) return hr;
5580 hr = add_vertex_index(&t2->vertex_stack, sweep_idx);
5581 if (hr != S_OK) return hr;
5582 t2->last_on_top = !t->last_on_top;
5584 hr = triangulation_add_point(&t, triangulations, sweep_idx, t->last_on_top);
5585 if (hr != S_OK) return hr;
5591 struct triangulation *t;
5592 struct triangulation *t2 = add_triangulation(triangulations);
5593 if (!t2) return E_OUTOFMEMORY;
5594 t = &triangulations->items[start];
5595 MoveMemory(t + 1, t, (char*)(t2 + 1) - (char*)t);
5596 ZeroMemory(t, sizeof(*t));
5597 hr = add_vertex_index(&t->vertex_stack, sweep_idx);
5598 if (hr != S_OK) return hr;
5604 HRESULT WINAPI D3DXCreateTextW(LPDIRECT3DDEVICE9 device,
5605 HDC hdc, LPCWSTR text,
5606 FLOAT deviation, FLOAT extrusion,
5607 LPD3DXMESH *mesh_ptr, LPD3DXBUFFER *adjacency,
5608 LPGLYPHMETRICSFLOAT glyphmetrics)
5611 ID3DXMesh *mesh = NULL;
5612 DWORD nb_vertices, nb_faces;
5613 DWORD nb_front_faces, nb_corners, nb_outline_points;
5614 struct vertex *vertices = NULL;
5619 OUTLINETEXTMETRICW otm;
5620 HFONT font = NULL, oldfont = NULL;
5621 const MAT2 identity = {{0, 1}, {0, 0}, {0, 0}, {0, 1}};
5622 void *raw_outline = NULL;
5624 struct glyphinfo *glyphs = NULL;
5626 struct triangulation_array triangulations = {0, 0, NULL};
5628 struct vertex *vertex_ptr;
5630 float max_deviation_sq;
5631 const struct cos_table cos_table = {
5632 cos(D3DXToRadian(0.5f)),
5633 cos(D3DXToRadian(45.0f)),
5634 cos(D3DXToRadian(90.0f)),
5638 TRACE("(%p, %p, %s, %f, %f, %p, %p, %p)\n", device, hdc,
5639 debugstr_w(text), deviation, extrusion, mesh_ptr, adjacency, glyphmetrics);
5641 if (!device || !hdc || !text || !*text || deviation < 0.0f || extrusion < 0.0f || !mesh_ptr)
5642 return D3DERR_INVALIDCALL;
5646 FIXME("Case of adjacency != NULL not implemented.\n");
5650 if (!GetObjectW(GetCurrentObject(hdc, OBJ_FONT), sizeof(lf), &lf) ||
5651 !GetOutlineTextMetricsW(hdc, sizeof(otm), &otm))
5653 return D3DERR_INVALIDCALL;
5656 if (deviation == 0.0f)
5657 deviation = 1.0f / otm.otmEMSquare;
5658 max_deviation_sq = deviation * deviation;
5660 lf.lfHeight = otm.otmEMSquare;
5662 font = CreateFontIndirectW(&lf);
5667 oldfont = SelectObject(hdc, font);
5669 textlen = strlenW(text);
5670 for (i = 0; i < textlen; i++)
5672 int datasize = GetGlyphOutlineW(hdc, text[i], GGO_NATIVE, &gm, 0, NULL, &identity);
5674 return D3DERR_INVALIDCALL;
5675 if (bufsize < datasize)
5678 if (!bufsize) { /* e.g. text == " " */
5679 hr = D3DERR_INVALIDCALL;
5683 glyphs = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, textlen * sizeof(*glyphs));
5684 raw_outline = HeapAlloc(GetProcessHeap(), 0, bufsize);
5685 if (!glyphs || !raw_outline) {
5691 for (i = 0; i < textlen; i++)
5693 /* get outline points from data returned from GetGlyphOutline */
5696 glyphs[i].offset_x = offset_x;
5698 datasize = GetGlyphOutlineW(hdc, text[i], GGO_NATIVE, &gm, bufsize, raw_outline, &identity);
5699 hr = create_outline(&glyphs[i], raw_outline, datasize,
5700 max_deviation_sq, otm.otmEMSquare, &cos_table);
5701 if (hr != S_OK) goto error;
5703 triangulations.glyph = &glyphs[i];
5704 hr = triangulate(&triangulations);
5705 if (hr != S_OK) goto error;
5706 if (triangulations.count) {
5707 ERR("%d incomplete triangulations of glyph (%u).\n", triangulations.count, text[i]);
5708 triangulations.count = 0;
5713 glyphmetrics[i].gmfBlackBoxX = gm.gmBlackBoxX / (float)otm.otmEMSquare;
5714 glyphmetrics[i].gmfBlackBoxY = gm.gmBlackBoxY / (float)otm.otmEMSquare;
5715 glyphmetrics[i].gmfptGlyphOrigin.x = gm.gmptGlyphOrigin.x / (float)otm.otmEMSquare;
5716 glyphmetrics[i].gmfptGlyphOrigin.y = gm.gmptGlyphOrigin.y / (float)otm.otmEMSquare;
5717 glyphmetrics[i].gmfCellIncX = gm.gmCellIncX / (float)otm.otmEMSquare;
5718 glyphmetrics[i].gmfCellIncY = gm.gmCellIncY / (float)otm.otmEMSquare;
5720 offset_x += gm.gmCellIncX / (float)otm.otmEMSquare;
5723 /* corner points need an extra vertex for the different side faces normals */
5725 nb_outline_points = 0;
5727 for (i = 0; i < textlen; i++)
5730 nb_outline_points += glyphs[i].ordered_vertices.count;
5731 nb_front_faces += glyphs[i].faces.count;
5732 for (j = 0; j < glyphs[i].outlines.count; j++)
5735 struct outline *outline = &glyphs[i].outlines.items[j];
5736 nb_corners++; /* first outline point always repeated as a corner */
5737 for (k = 1; k < outline->count; k++)
5738 if (outline->items[k].corner)
5743 nb_vertices = (nb_outline_points + nb_corners) * 2 + nb_outline_points * 2;
5744 nb_faces = nb_outline_points * 2 + nb_front_faces * 2;
5747 hr = D3DXCreateMeshFVF(nb_faces, nb_vertices, D3DXMESH_MANAGED,
5748 D3DFVF_XYZ | D3DFVF_NORMAL, device, &mesh);
5752 hr = mesh->lpVtbl->LockVertexBuffer(mesh, 0, (LPVOID *)&vertices);
5756 hr = mesh->lpVtbl->LockIndexBuffer(mesh, 0, (LPVOID *)&faces);
5760 /* convert 2D vertices and faces into 3D mesh */
5761 vertex_ptr = vertices;
5763 if (extrusion == 0.0f) {
5770 for (i = 0; i < textlen; i++)
5774 struct vertex *back_vertices;
5777 /* side vertices and faces */
5778 for (j = 0; j < glyphs[i].outlines.count; j++)
5780 struct vertex *outline_vertices = vertex_ptr;
5781 struct outline *outline = &glyphs[i].outlines.items[j];
5783 struct point2d *prevpt = &outline->items[outline->count - 1];
5784 struct point2d *pt = &outline->items[0];
5786 for (k = 1; k <= outline->count; k++)
5789 struct point2d *nextpt = &outline->items[k % outline->count];
5790 WORD vtx_idx = vertex_ptr - vertices;
5793 if (pt->corner == POINTTYPE_CURVE_START)
5794 D3DXVec2Subtract(&vec, &pt->pos, &prevpt->pos);
5795 else if (pt->corner)
5796 D3DXVec2Subtract(&vec, &nextpt->pos, &pt->pos);
5798 D3DXVec2Subtract(&vec, &nextpt->pos, &prevpt->pos);
5799 D3DXVec2Normalize(&vec, &vec);
5800 vtx.normal.x = -vec.y;
5801 vtx.normal.y = vec.x;
5804 vtx.position.x = pt->pos.x + glyphs[i].offset_x;
5805 vtx.position.y = pt->pos.y;
5807 *vertex_ptr++ = vtx;
5809 vtx.position.z = -extrusion;
5810 *vertex_ptr++ = vtx;
5812 vtx.position.x = nextpt->pos.x + glyphs[i].offset_x;
5813 vtx.position.y = nextpt->pos.y;
5814 if (pt->corner && nextpt->corner && nextpt->corner != POINTTYPE_CURVE_END) {
5815 vtx.position.z = -extrusion;
5816 *vertex_ptr++ = vtx;
5818 *vertex_ptr++ = vtx;
5820 (*face_ptr)[0] = vtx_idx;
5821 (*face_ptr)[1] = vtx_idx + 2;
5822 (*face_ptr)[2] = vtx_idx + 1;
5825 (*face_ptr)[0] = vtx_idx;
5826 (*face_ptr)[1] = vtx_idx + 3;
5827 (*face_ptr)[2] = vtx_idx + 2;
5830 if (nextpt->corner) {
5831 if (nextpt->corner == POINTTYPE_CURVE_END) {
5832 D3DXVECTOR2 *nextpt2 = &outline->items[(k + 1) % outline->count].pos;
5833 D3DXVec2Subtract(&vec, nextpt2, &nextpt->pos);
5835 D3DXVec2Subtract(&vec, &nextpt->pos, &pt->pos);
5837 D3DXVec2Normalize(&vec, &vec);
5838 vtx.normal.x = -vec.y;
5839 vtx.normal.y = vec.x;
5842 *vertex_ptr++ = vtx;
5843 vtx.position.z = -extrusion;
5844 *vertex_ptr++ = vtx;
5847 (*face_ptr)[0] = vtx_idx;
5848 (*face_ptr)[1] = vtx_idx + 3;
5849 (*face_ptr)[2] = vtx_idx + 1;
5852 (*face_ptr)[0] = vtx_idx;
5853 (*face_ptr)[1] = vtx_idx + 2;
5854 (*face_ptr)[2] = vtx_idx + 3;
5862 *vertex_ptr++ = *outline_vertices++;
5863 *vertex_ptr++ = *outline_vertices++;
5867 /* back vertices and faces */
5868 back_faces = face_ptr;
5869 back_vertices = vertex_ptr;
5870 for (j = 0; j < glyphs[i].ordered_vertices.count; j++)
5872 D3DXVECTOR2 *pt = get_ordered_vertex(&glyphs[i], j);
5873 vertex_ptr->position.x = pt->x + glyphs[i].offset_x;
5874 vertex_ptr->position.y = pt->y;
5875 vertex_ptr->position.z = 0;
5876 vertex_ptr->normal.x = 0;
5877 vertex_ptr->normal.y = 0;
5878 vertex_ptr->normal.z = 1;
5881 count = back_vertices - vertices;
5882 for (j = 0; j < glyphs[i].faces.count; j++)
5884 face *f = &glyphs[i].faces.items[j];
5885 (*face_ptr)[0] = (*f)[0] + count;
5886 (*face_ptr)[1] = (*f)[1] + count;
5887 (*face_ptr)[2] = (*f)[2] + count;
5891 /* front vertices and faces */
5892 j = count = vertex_ptr - back_vertices;
5895 vertex_ptr->position.x = back_vertices->position.x;
5896 vertex_ptr->position.y = back_vertices->position.y;
5897 vertex_ptr->position.z = -extrusion;
5898 vertex_ptr->normal.x = 0;
5899 vertex_ptr->normal.y = 0;
5900 vertex_ptr->normal.z = extrusion == 0.0f ? 1.0f : -1.0f;
5904 j = face_ptr - back_faces;
5907 (*face_ptr)[0] = (*back_faces)[0] + count;
5908 (*face_ptr)[1] = (*back_faces)[f1] + count;
5909 (*face_ptr)[2] = (*back_faces)[f2] + count;
5919 if (faces) mesh->lpVtbl->UnlockIndexBuffer(mesh);
5920 if (vertices) mesh->lpVtbl->UnlockVertexBuffer(mesh);
5921 if (hr != D3D_OK) mesh->lpVtbl->Release(mesh);
5924 for (i = 0; i < textlen; i++)
5927 for (j = 0; j < glyphs[i].outlines.count; j++)
5928 HeapFree(GetProcessHeap(), 0, glyphs[i].outlines.items[j].items);
5929 HeapFree(GetProcessHeap(), 0, glyphs[i].outlines.items);
5930 HeapFree(GetProcessHeap(), 0, glyphs[i].faces.items);
5931 HeapFree(GetProcessHeap(), 0, glyphs[i].ordered_vertices.items);
5933 HeapFree(GetProcessHeap(), 0, glyphs);
5935 if (triangulations.items) {
5937 for (i = 0; i < triangulations.count; i++)
5938 HeapFree(GetProcessHeap(), 0, triangulations.items[i].vertex_stack.items);
5939 HeapFree(GetProcessHeap(), 0, triangulations.items);
5941 HeapFree(GetProcessHeap(), 0, raw_outline);
5942 if (oldfont) SelectObject(hdc, oldfont);
5943 if (font) DeleteObject(font);
5948 HRESULT WINAPI D3DXValidMesh(LPD3DXMESH mesh, CONST DWORD *adjacency, LPD3DXBUFFER *errors_and_warnings)
5950 FIXME("(%p, %p, %p): stub\n", mesh, adjacency, *errors_and_warnings);
5955 static BOOL weld_float1(void *to, void *from, FLOAT epsilon)
5960 if (fabsf(*v1 - *v2) <= epsilon)
5970 static BOOL weld_float2(void *to, void *from, FLOAT epsilon)
5972 D3DXVECTOR2 *v1 = to;
5973 D3DXVECTOR2 *v2 = from;
5974 FLOAT diff_x = fabsf(v1->x - v2->x);
5975 FLOAT diff_y = fabsf(v1->y - v2->y);
5976 FLOAT max_abs_diff = max(diff_x, diff_y);
5978 if (max_abs_diff <= epsilon)
5980 memcpy(to, from, sizeof(D3DXVECTOR2));
5988 static BOOL weld_float3(void *to, void *from, FLOAT epsilon)
5990 D3DXVECTOR3 *v1 = to;
5991 D3DXVECTOR3 *v2 = from;
5992 FLOAT diff_x = fabsf(v1->x - v2->x);
5993 FLOAT diff_y = fabsf(v1->y - v2->y);
5994 FLOAT diff_z = fabsf(v1->z - v2->z);
5995 FLOAT max_abs_diff = max(diff_x, diff_y);
5996 max_abs_diff = max(diff_z, max_abs_diff);
5998 if (max_abs_diff <= epsilon)
6000 memcpy(to, from, sizeof(D3DXVECTOR3));
6008 static BOOL weld_float4(void *to, void *from, FLOAT epsilon)
6010 D3DXVECTOR4 *v1 = to;
6011 D3DXVECTOR4 *v2 = from;
6012 FLOAT diff_x = fabsf(v1->x - v2->x);
6013 FLOAT diff_y = fabsf(v1->y - v2->y);
6014 FLOAT diff_z = fabsf(v1->z - v2->z);
6015 FLOAT diff_w = fabsf(v1->w - v2->w);
6016 FLOAT max_abs_diff = fmax(diff_x, diff_y);
6017 max_abs_diff = max(diff_z, max_abs_diff);
6018 max_abs_diff = max(diff_w, max_abs_diff);
6020 if (max_abs_diff <= epsilon)
6022 memcpy(to, from, sizeof(D3DXVECTOR4));
6030 static BOOL weld_ubyte4(void *to, void *from, FLOAT epsilon)
6034 BYTE truncated_epsilon = (BYTE)epsilon;
6035 BYTE diff_x = b1[0] > b2[0] ? b1[0] - b2[0] : b2[0] - b1[0];
6036 BYTE diff_y = b1[1] > b2[1] ? b1[1] - b2[1] : b2[1] - b1[1];
6037 BYTE diff_z = b1[2] > b2[2] ? b1[2] - b2[2] : b2[2] - b1[2];
6038 BYTE diff_w = b1[3] > b2[3] ? b1[3] - b2[3] : b2[3] - b1[3];
6039 BYTE max_diff = max(diff_x, diff_y);
6040 max_diff = max(diff_z, max_diff);
6041 max_diff = max(diff_w, max_diff);
6043 if (max_diff <= truncated_epsilon)
6045 memcpy(to, from, 4 * sizeof(BYTE));
6053 static BOOL weld_ubyte4n(void *to, void *from, FLOAT epsilon)
6055 return weld_ubyte4(to, from, epsilon * UCHAR_MAX);
6058 static BOOL weld_d3dcolor(void *to, void *from, FLOAT epsilon)
6060 return weld_ubyte4n(to, from, epsilon);
6063 static BOOL weld_short2(void *to, void *from, FLOAT epsilon)
6067 SHORT truncated_epsilon = (SHORT)epsilon;
6068 SHORT diff_x = abs(s1[0] - s2[0]);
6069 SHORT diff_y = abs(s1[1] - s2[1]);
6070 SHORT max_abs_diff = max(diff_x, diff_y);
6072 if (max_abs_diff <= truncated_epsilon)
6074 memcpy(to, from, 2 * sizeof(SHORT));
6082 static BOOL weld_short2n(void *to, void *from, FLOAT epsilon)
6084 return weld_short2(to, from, epsilon * SHRT_MAX);
6087 static BOOL weld_short4(void *to, void *from, FLOAT epsilon)
6091 SHORT truncated_epsilon = (SHORT)epsilon;
6092 SHORT diff_x = abs(s1[0] - s2[0]);
6093 SHORT diff_y = abs(s1[1] - s2[1]);
6094 SHORT diff_z = abs(s1[2] - s2[2]);
6095 SHORT diff_w = abs(s1[3] - s2[3]);
6096 SHORT max_abs_diff = max(diff_x, diff_y);
6097 max_abs_diff = max(diff_z, max_abs_diff);
6098 max_abs_diff = max(diff_w, max_abs_diff);
6100 if (max_abs_diff <= truncated_epsilon)
6102 memcpy(to, from, 4 * sizeof(SHORT));
6110 static BOOL weld_short4n(void *to, void *from, FLOAT epsilon)
6112 return weld_short4(to, from, epsilon * SHRT_MAX);
6115 static BOOL weld_ushort2n(void *to, void *from, FLOAT epsilon)
6119 USHORT scaled_epsilon = (USHORT)(epsilon * USHRT_MAX);
6120 USHORT diff_x = s1[0] > s2[0] ? s1[0] - s2[0] : s2[0] - s1[0];
6121 USHORT diff_y = s1[1] > s2[1] ? s1[1] - s2[1] : s2[1] - s1[1];
6122 USHORT max_diff = max(diff_x, diff_y);
6124 if (max_diff <= scaled_epsilon)
6126 memcpy(to, from, 2 * sizeof(USHORT));
6134 static BOOL weld_ushort4n(void *to, void *from, FLOAT epsilon)
6138 USHORT scaled_epsilon = (USHORT)(epsilon * USHRT_MAX);
6139 USHORT diff_x = s1[0] > s2[0] ? s1[0] - s2[0] : s2[0] - s1[0];
6140 USHORT diff_y = s1[1] > s2[1] ? s1[1] - s2[1] : s2[1] - s1[1];
6141 USHORT diff_z = s1[2] > s2[2] ? s1[2] - s2[2] : s2[2] - s1[2];
6142 USHORT diff_w = s1[3] > s2[3] ? s1[3] - s2[3] : s2[3] - s1[3];
6143 USHORT max_diff = max(diff_x, diff_y);
6144 max_diff = max(diff_z, max_diff);
6145 max_diff = max(diff_w, max_diff);
6147 if (max_diff <= scaled_epsilon)
6149 memcpy(to, from, 4 * sizeof(USHORT));
6165 static struct udec3 dword_to_udec3(DWORD d)
6170 v.y = (d & 0xffc00) >> 10;
6171 v.z = (d & 0x3ff00000) >> 20;
6172 v.w = (d & 0xc0000000) >> 30;
6177 static BOOL weld_udec3(void *to, void *from, FLOAT epsilon)
6181 struct udec3 v1 = dword_to_udec3(*d1);
6182 struct udec3 v2 = dword_to_udec3(*d2);
6183 UINT truncated_epsilon = (UINT)epsilon;
6184 UINT diff_x = v1.x > v2.x ? v1.x - v2.x : v2.x - v1.x;
6185 UINT diff_y = v1.y > v2.y ? v1.y - v2.y : v2.y - v1.y;
6186 UINT diff_z = v1.z > v2.z ? v1.z - v2.z : v2.z - v1.z;
6187 UINT diff_w = v1.w > v2.w ? v1.w - v2.w : v2.w - v1.w;
6188 UINT max_diff = max(diff_x, diff_y);
6189 max_diff = max(diff_z, max_diff);
6190 max_diff = max(diff_w, max_diff);
6192 if (max_diff <= truncated_epsilon)
6194 memcpy(to, from, sizeof(DWORD));
6210 static struct dec3n dword_to_dec3n(DWORD d)
6215 v.y = (d & 0xffc00) >> 10;
6216 v.z = (d & 0x3ff00000) >> 20;
6217 v.w = (d & 0xc0000000) >> 30;
6222 static BOOL weld_dec3n(void *to, void *from, FLOAT epsilon)
6224 const UINT MAX_DEC3N = 511;
6227 struct dec3n v1 = dword_to_dec3n(*d1);
6228 struct dec3n v2 = dword_to_dec3n(*d2);
6229 INT scaled_epsilon = (INT)(epsilon * MAX_DEC3N);
6230 INT diff_x = abs(v1.x - v2.x);
6231 INT diff_y = abs(v1.y - v2.y);
6232 INT diff_z = abs(v1.z - v2.z);
6233 INT diff_w = abs(v1.w - v2.w);
6234 INT max_abs_diff = max(diff_x, diff_y);
6235 max_abs_diff = max(diff_z, max_abs_diff);
6236 max_abs_diff = max(diff_w, max_abs_diff);
6238 if (max_abs_diff <= scaled_epsilon)
6240 memcpy(to, from, sizeof(DWORD));
6248 static BOOL weld_float16_2(void *to, void *from, FLOAT epsilon)
6250 D3DXFLOAT16 *v1_float16 = to;
6251 D3DXFLOAT16 *v2_float16 = from;
6255 const UINT NUM_ELEM = 2;
6259 D3DXFloat16To32Array(v1, v1_float16, NUM_ELEM);
6260 D3DXFloat16To32Array(v2, v2_float16, NUM_ELEM);
6262 diff_x = fabsf(v1[0] - v2[0]);
6263 diff_y = fabsf(v1[1] - v2[1]);
6264 max_abs_diff = max(diff_x, diff_y);
6266 if (max_abs_diff <= epsilon)
6268 memcpy(to, from, NUM_ELEM * sizeof(D3DXFLOAT16));
6276 static BOOL weld_float16_4(void *to, void *from, FLOAT epsilon)
6278 D3DXFLOAT16 *v1_float16 = to;
6279 D3DXFLOAT16 *v2_float16 = from;
6285 const UINT NUM_ELEM = 4;
6289 D3DXFloat16To32Array(v1, v1_float16, NUM_ELEM);
6290 D3DXFloat16To32Array(v2, v2_float16, NUM_ELEM);
6292 diff_x = fabsf(v1[0] - v2[0]);
6293 diff_y = fabsf(v1[1] - v2[1]);
6294 diff_z = fabsf(v1[2] - v2[2]);
6295 diff_w = fabsf(v1[3] - v2[3]);
6296 max_abs_diff = max(diff_x, diff_y);
6297 max_abs_diff = max(diff_z, max_abs_diff);
6298 max_abs_diff = max(diff_w, max_abs_diff);
6300 if (max_abs_diff <= epsilon)
6302 memcpy(to, from, NUM_ELEM * sizeof(D3DXFLOAT16));
6310 /* Sets the vertex components to the same value if they are within epsilon. */
6311 static BOOL weld_component(void *to, void *from, D3DDECLTYPE type, FLOAT epsilon)
6313 /* Quiet FIXMEs as this is in a loop with potentially thousand of iterations. */
6314 BOOL fixme_once_unused = FALSE;
6315 BOOL fixme_once_unknown = FALSE;
6319 case D3DDECLTYPE_FLOAT1:
6320 return weld_float1(to, from, epsilon);
6322 case D3DDECLTYPE_FLOAT2:
6323 return weld_float2(to, from, epsilon);
6325 case D3DDECLTYPE_FLOAT3:
6326 return weld_float3(to, from, epsilon);
6328 case D3DDECLTYPE_FLOAT4:
6329 return weld_float4(to, from, epsilon);
6331 case D3DDECLTYPE_D3DCOLOR:
6332 return weld_d3dcolor(to, from, epsilon);
6334 case D3DDECLTYPE_UBYTE4:
6335 return weld_ubyte4(to, from, epsilon);
6337 case D3DDECLTYPE_SHORT2:
6338 return weld_short2(to, from, epsilon);
6340 case D3DDECLTYPE_SHORT4:
6341 return weld_short4(to, from, epsilon);
6343 case D3DDECLTYPE_UBYTE4N:
6344 return weld_ubyte4n(to, from, epsilon);
6346 case D3DDECLTYPE_SHORT2N:
6347 return weld_short2n(to, from, epsilon);
6349 case D3DDECLTYPE_SHORT4N:
6350 return weld_short4n(to, from, epsilon);
6352 case D3DDECLTYPE_USHORT2N:
6353 return weld_ushort2n(to, from, epsilon);
6355 case D3DDECLTYPE_USHORT4N:
6356 return weld_ushort4n(to, from, epsilon);
6358 case D3DDECLTYPE_UDEC3:
6359 return weld_udec3(to, from, epsilon);
6361 case D3DDECLTYPE_DEC3N:
6362 return weld_dec3n(to, from, epsilon);
6364 case D3DDECLTYPE_FLOAT16_2:
6365 return weld_float16_2(to, from, epsilon);
6367 case D3DDECLTYPE_FLOAT16_4:
6368 return weld_float16_4(to, from, epsilon);
6370 case D3DDECLTYPE_UNUSED:
6371 if (!fixme_once_unused++)
6372 FIXME("D3DDECLTYPE_UNUSED welding not implemented.\n");
6376 if (!fixme_once_unknown++)
6377 FIXME("Welding of unknown declaration type %d is not implemented.\n", type);
6384 static FLOAT get_component_epsilon(const D3DVERTEXELEMENT9 *decl_ptr, const D3DXWELDEPSILONS *epsilons)
6386 FLOAT epsilon = 0.0f;
6387 /* Quiet FIXMEs as this is in a loop with potentially thousand of iterations. */
6388 static BOOL fixme_once_blendindices = FALSE;
6389 static BOOL fixme_once_positiont = FALSE;
6390 static BOOL fixme_once_fog = FALSE;
6391 static BOOL fixme_once_depth = FALSE;
6392 static BOOL fixme_once_sample = FALSE;
6393 static BOOL fixme_once_unknown = FALSE;
6395 switch (decl_ptr->Usage)
6397 case D3DDECLUSAGE_POSITION:
6398 epsilon = epsilons->Position;
6400 case D3DDECLUSAGE_BLENDWEIGHT:
6401 epsilon = epsilons->BlendWeights;
6403 case D3DDECLUSAGE_NORMAL:
6404 epsilon = epsilons->Normals;
6406 case D3DDECLUSAGE_PSIZE:
6407 epsilon = epsilons->PSize;
6409 case D3DDECLUSAGE_TEXCOORD:
6411 BYTE usage_index = decl_ptr->UsageIndex;
6412 if (usage_index > 7)
6414 epsilon = epsilons->Texcoords[usage_index];
6417 case D3DDECLUSAGE_TANGENT:
6418 epsilon = epsilons->Tangent;
6420 case D3DDECLUSAGE_BINORMAL:
6421 epsilon = epsilons->Binormal;
6423 case D3DDECLUSAGE_TESSFACTOR:
6424 epsilon = epsilons->TessFactor;
6426 case D3DDECLUSAGE_COLOR:
6427 if (decl_ptr->UsageIndex == 0)
6428 epsilon = epsilons->Diffuse;
6429 else if (decl_ptr->UsageIndex == 1)
6430 epsilon = epsilons->Specular;
6434 case D3DDECLUSAGE_BLENDINDICES:
6435 if (!fixme_once_blendindices++)
6436 FIXME("D3DDECLUSAGE_BLENDINDICES welding not implemented.\n");
6438 case D3DDECLUSAGE_POSITIONT:
6439 if (!fixme_once_positiont++)
6440 FIXME("D3DDECLUSAGE_POSITIONT welding not implemented.\n");
6442 case D3DDECLUSAGE_FOG:
6443 if (!fixme_once_fog++)
6444 FIXME("D3DDECLUSAGE_FOG welding not implemented.\n");
6446 case D3DDECLUSAGE_DEPTH:
6447 if (!fixme_once_depth++)
6448 FIXME("D3DDECLUSAGE_DEPTH welding not implemented.\n");
6450 case D3DDECLUSAGE_SAMPLE:
6451 if (!fixme_once_sample++)
6452 FIXME("D3DDECLUSAGE_SAMPLE welding not implemented.\n");
6455 if (!fixme_once_unknown++)
6456 FIXME("Unknown usage %x\n", decl_ptr->Usage);
6463 /* Helper function for reading a 32-bit index buffer. */
6464 static inline DWORD read_ib(void *index_buffer, BOOL indices_are_32bit,
6467 if (indices_are_32bit)
6469 DWORD *indices = index_buffer;
6470 return indices[index];
6474 WORD *indices = index_buffer;
6475 return indices[index];
6479 /* Helper function for writing to a 32-bit index buffer. */
6480 static inline void write_ib(void *index_buffer, BOOL indices_are_32bit,
6481 DWORD index, DWORD value)
6483 if (indices_are_32bit)
6485 DWORD *indices = index_buffer;
6486 indices[index] = value;
6490 WORD *indices = index_buffer;
6491 indices[index] = value;
6495 /*************************************************************************
6496 * D3DXWeldVertices (D3DX9_36.@)
6498 * Welds together similar vertices. The similarity between vert-
6499 * ices can be the position and other components such as
6503 * mesh [I] Mesh which vertices will be welded together.
6504 * flags [I] D3DXWELDEPSILONSFLAGS specifying how to weld.
6505 * epsilons [I] How similar a component needs to be for welding.
6506 * adjacency [I] Which faces are adjacent to other faces.
6507 * adjacency_out [O] Updated adjacency after welding.
6508 * face_remap_out [O] Which faces the old faces have been mapped to.
6509 * vertex_remap_out [O] Which vertices the old vertices have been mapped to.
6513 * Failure: D3DERR_INVALIDCALL, E_OUTOFMEMORY.
6516 * Attribute sorting not implemented.
6519 HRESULT WINAPI D3DXWeldVertices(LPD3DXMESH mesh,
6521 CONST D3DXWELDEPSILONS *epsilons,
6522 CONST DWORD *adjacency,
6523 DWORD *adjacency_out,
6524 DWORD *face_remap_out,
6525 LPD3DXBUFFER *vertex_remap_out)
6527 DWORD *adjacency_generated = NULL;
6528 const DWORD *adjacency_ptr;
6529 DWORD *attributes = NULL;
6530 const FLOAT DEFAULT_EPSILON = 1.0e-6f;
6533 void *indices = NULL;
6534 BOOL indices_are_32bit = mesh->lpVtbl->GetOptions(mesh) & D3DXMESH_32BIT;
6535 DWORD optimize_flags;
6536 DWORD *point_reps = NULL;
6537 ID3DXMeshImpl *This = impl_from_ID3DXMesh(mesh);
6538 DWORD *vertex_face_map = NULL;
6539 ID3DXBuffer *vertex_remap = NULL;
6540 BYTE *vertices = NULL;
6542 TRACE("(%p, %x, %p, %p, %p, %p, %p)\n", mesh, flags, epsilons,
6543 adjacency, adjacency_out, face_remap_out, vertex_remap_out);
6547 WARN("No flags is undefined. Using D3DXWELDEPSILONS_WELDPARTIALMATCHES instead.\n");
6548 flags = D3DXWELDEPSILONS_WELDPARTIALMATCHES;
6551 if (adjacency) /* Use supplied adjacency. */
6553 adjacency_ptr = adjacency;
6555 else /* Adjacency has to be generated. */
6557 adjacency_generated = HeapAlloc(GetProcessHeap(), 0, 3 * This->numfaces * sizeof(*adjacency_generated));
6558 if (!adjacency_generated)
6560 ERR("Couldn't allocate memory for adjacency_generated.\n");
6564 hr = mesh->lpVtbl->GenerateAdjacency(mesh, DEFAULT_EPSILON, adjacency_generated);
6567 ERR("Couldn't generate adjacency.\n");
6570 adjacency_ptr = adjacency_generated;
6573 /* Point representation says which vertices can be replaced. */
6574 point_reps = HeapAlloc(GetProcessHeap(), 0, This->numvertices * sizeof(*point_reps));
6578 ERR("Couldn't allocate memory for point_reps.\n");
6581 hr = mesh->lpVtbl->ConvertAdjacencyToPointReps(mesh, adjacency_ptr, point_reps);
6584 ERR("ConvertAdjacencyToPointReps failed.\n");
6588 hr = mesh->lpVtbl->LockIndexBuffer(mesh, 0, &indices);
6591 ERR("Couldn't lock index buffer.\n");
6595 hr = mesh->lpVtbl->LockAttributeBuffer(mesh, 0, &attributes);
6598 ERR("Couldn't lock attribute buffer.\n");
6601 vertex_face_map = HeapAlloc(GetProcessHeap(), 0, This->numvertices * sizeof(*vertex_face_map));
6602 if (!vertex_face_map)
6605 ERR("Couldn't allocate memory for vertex_face_map.\n");
6608 /* Build vertex face map, so that a vertex's face can be looked up. */
6609 for (i = 0; i < This->numfaces; i++)
6612 for (j = 0; j < 3; j++)
6614 DWORD index = read_ib(indices, indices_are_32bit, 3*i + j);
6615 vertex_face_map[index] = i;
6619 if (flags & D3DXWELDEPSILONS_WELDPARTIALMATCHES)
6621 hr = mesh->lpVtbl->LockVertexBuffer(mesh, 0, (void**)&vertices);
6624 ERR("Couldn't lock vertex buffer.\n");
6627 /* For each vertex that can be removed, compare its vertex components
6628 * with the vertex components from the vertex that can replace it. A
6629 * vertex is only fully replaced if all the components match and the
6630 * flag D3DXWELDEPSILONS_DONOTREMOVEVERTICES is not set, and they
6631 * belong to the same attribute group. Otherwise the vertex components
6632 * that are within epsilon are set to the same value.
6634 for (i = 0; i < 3 * This->numfaces; i++)
6636 D3DVERTEXELEMENT9 *decl_ptr;
6637 DWORD vertex_size = mesh->lpVtbl->GetNumBytesPerVertex(mesh);
6638 DWORD num_vertex_components;
6641 DWORD index = read_ib(indices, indices_are_32bit, i);
6643 for (decl_ptr = This->cached_declaration, num_vertex_components = 0; decl_ptr->Stream != 0xFF; decl_ptr++, num_vertex_components++)
6645 BYTE *to = &vertices[vertex_size*index + decl_ptr->Offset];
6646 BYTE *from = &vertices[vertex_size*point_reps[index] + decl_ptr->Offset];
6647 FLOAT epsilon = get_component_epsilon(decl_ptr, epsilons);
6649 /* Don't weld self */
6650 if (index == point_reps[index])
6656 if (weld_component(to, from, decl_ptr->Type, epsilon))
6660 all_match = (num_vertex_components == matches);
6661 if (all_match && !(flags & D3DXWELDEPSILONS_DONOTREMOVEVERTICES))
6663 DWORD to_face = vertex_face_map[index];
6664 DWORD from_face = vertex_face_map[point_reps[index]];
6665 if(attributes[to_face] != attributes[from_face] && !(flags & D3DXWELDEPSILONS_DONOTSPLIT))
6667 write_ib(indices, indices_are_32bit, i, point_reps[index]);
6670 mesh->lpVtbl->UnlockVertexBuffer(mesh);
6673 else if (flags & D3DXWELDEPSILONS_WELDALL)
6675 for (i = 0; i < 3 * This->numfaces; i++)
6677 DWORD index = read_ib(indices, indices_are_32bit, i);
6678 DWORD to_face = vertex_face_map[index];
6679 DWORD from_face = vertex_face_map[point_reps[index]];
6680 if(attributes[to_face] != attributes[from_face] && !(flags & D3DXWELDEPSILONS_DONOTSPLIT))
6682 write_ib(indices, indices_are_32bit, i, point_reps[index]);
6685 mesh->lpVtbl->UnlockAttributeBuffer(mesh);
6687 mesh->lpVtbl->UnlockIndexBuffer(mesh);
6690 /* Compact mesh using OptimizeInplace */
6691 optimize_flags = D3DXMESHOPT_COMPACT;
6692 hr = mesh->lpVtbl->OptimizeInplace(mesh, optimize_flags, adjacency_ptr, adjacency_out, face_remap_out, vertex_remap_out);
6695 ERR("Couldn't compact mesh.\n");
6701 HeapFree(GetProcessHeap(), 0, adjacency_generated);
6702 HeapFree(GetProcessHeap(), 0, point_reps);
6703 HeapFree(GetProcessHeap(), 0, vertex_face_map);
6704 if (attributes) mesh->lpVtbl->UnlockAttributeBuffer(mesh);
6705 if (indices) mesh->lpVtbl->UnlockIndexBuffer(mesh);
6706 if (vertex_remap) ID3DXBuffer_Release(vertex_remap);
6707 if (vertices) mesh->lpVtbl->UnlockVertexBuffer(mesh);
6712 /*************************************************************************
6713 * D3DXOptimizeFaces (D3DX9_36.@)
6715 * Re-orders the faces so the vertex cache is used optimally.
6718 * indices [I] Pointer to an index buffer belonging to a mesh.
6719 * num_faces [I] Number of faces in the mesh.
6720 * num_vertices [I] Number of vertices in the mesh.
6721 * indices_are_32bit [I] Specifies whether indices are 32- or 16-bit.
6722 * face_remap [I/O] The new order the faces should be drawn in.
6726 * Failure: D3DERR_INVALIDCALL.
6729 * The face re-ordering does not use the vertex cache optimally.
6732 HRESULT WINAPI D3DXOptimizeFaces(LPCVOID indices,
6735 BOOL indices_are_32bit,
6739 UINT j = num_faces - 1;
6740 UINT limit_16_bit = 2 << 15; /* According to MSDN */
6741 HRESULT hr = D3D_OK;
6743 FIXME("(%p, %u, %u, %s, %p): semi-stub. Face order will not be optimal.\n",
6744 indices, num_faces, num_vertices,
6745 indices_are_32bit ? "TRUE" : "FALSE", face_remap);
6747 if (!indices_are_32bit && num_faces >= limit_16_bit)
6749 WARN("Number of faces must be less than %d when using 16-bit indices.\n",
6751 hr = D3DERR_INVALIDCALL;
6757 WARN("Face remap pointer is NULL.\n");
6758 hr = D3DERR_INVALIDCALL;
6762 /* The faces are drawn in reverse order for simple meshes. This ordering
6763 * is not optimal for complicated meshes, but will not break anything
6764 * either. The ordering should be changed to take advantage of the vertex
6765 * cache on the graphics card.
6767 * TODO Re-order to take advantage of vertex cache.
6769 for (i = 0; i < num_faces; i++)
6771 face_remap[i] = j--;