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(struct ID3DXMesh *iface,
775 struct IDirect3DVertexBuffer9 **vertex_buffer)
777 ID3DXMeshImpl *This = impl_from_ID3DXMesh(iface);
779 TRACE("(%p)->(%p)\n", This, vertex_buffer);
781 if (vertex_buffer == NULL) return D3DERR_INVALIDCALL;
782 *vertex_buffer = This->vertex_buffer;
783 IDirect3DVertexBuffer9_AddRef(This->vertex_buffer);
788 static HRESULT WINAPI ID3DXMeshImpl_GetIndexBuffer(struct ID3DXMesh *iface,
789 struct IDirect3DIndexBuffer9 **index_buffer)
791 ID3DXMeshImpl *This = impl_from_ID3DXMesh(iface);
793 TRACE("(%p)->(%p)\n", This, index_buffer);
795 if (index_buffer == NULL) return D3DERR_INVALIDCALL;
796 *index_buffer = This->index_buffer;
797 IDirect3DIndexBuffer9_AddRef(This->index_buffer);
802 static HRESULT WINAPI ID3DXMeshImpl_LockVertexBuffer(ID3DXMesh *iface, DWORD flags, LPVOID *data)
804 ID3DXMeshImpl *This = impl_from_ID3DXMesh(iface);
806 TRACE("(%p)->(%u,%p)\n", This, flags, data);
808 return IDirect3DVertexBuffer9_Lock(This->vertex_buffer, 0, 0, data, flags);
811 static HRESULT WINAPI ID3DXMeshImpl_UnlockVertexBuffer(ID3DXMesh *iface)
813 ID3DXMeshImpl *This = impl_from_ID3DXMesh(iface);
815 TRACE("(%p)\n", This);
817 return IDirect3DVertexBuffer9_Unlock(This->vertex_buffer);
820 static HRESULT WINAPI ID3DXMeshImpl_LockIndexBuffer(ID3DXMesh *iface, DWORD flags, LPVOID *data)
822 ID3DXMeshImpl *This = impl_from_ID3DXMesh(iface);
824 TRACE("(%p)->(%u,%p)\n", This, flags, data);
826 return IDirect3DIndexBuffer9_Lock(This->index_buffer, 0, 0, data, flags);
829 static HRESULT WINAPI ID3DXMeshImpl_UnlockIndexBuffer(ID3DXMesh *iface)
831 ID3DXMeshImpl *This = impl_from_ID3DXMesh(iface);
833 TRACE("(%p)\n", This);
835 return IDirect3DIndexBuffer9_Unlock(This->index_buffer);
838 static HRESULT WINAPI ID3DXMeshImpl_GetAttributeTable(ID3DXMesh *iface, D3DXATTRIBUTERANGE *attrib_table, DWORD *attrib_table_size)
840 ID3DXMeshImpl *This = impl_from_ID3DXMesh(iface);
842 TRACE("(%p)->(%p,%p)\n", This, attrib_table, attrib_table_size);
844 if (attrib_table_size)
845 *attrib_table_size = This->attrib_table_size;
848 CopyMemory(attrib_table, This->attrib_table, This->attrib_table_size * sizeof(*attrib_table));
863 struct edge_face *entries;
866 /* Builds up a map of which face a new edge belongs to. That way the adjacency
867 * of another edge can be looked up. An edge has an adjacent face if there
868 * is an edge going in the opposite direction in the map. For example if the
869 * edge (v1, v2) belongs to face 4, and there is a mapping (v2, v1)->7, then
870 * face 4 and 7 are adjacent.
872 * Each edge might have been replaced with another edge, or none at all. There
873 * is at most one edge to face mapping, i.e. an edge can only belong to one
876 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)
881 edge_face_map->lists = HeapAlloc(GetProcessHeap(), 0, 3 * num_faces * sizeof(*edge_face_map->lists));
882 if (!edge_face_map->lists) return E_OUTOFMEMORY;
884 edge_face_map->entries = HeapAlloc(GetProcessHeap(), 0, 3 * num_faces * sizeof(*edge_face_map->entries));
885 if (!edge_face_map->entries) return E_OUTOFMEMORY;
888 /* Initialize all lists */
889 for (i = 0; i < 3 * num_faces; i++)
891 list_init(&edge_face_map->lists[i]);
893 /* Build edge face mapping */
894 for (face = 0; face < num_faces; face++)
896 for (edge = 0; edge < 3; edge++)
898 DWORD v1 = index_buffer[3*face + edge];
899 DWORD v2 = index_buffer[3*face + (edge+1)%3];
900 DWORD new_v1 = point_reps[v1]; /* What v1 has been replaced with */
901 DWORD new_v2 = point_reps[v2];
903 if (v1 != v2) /* Only map non-collapsed edges */
906 edge_face_map->entries[i].v2 = new_v2;
907 edge_face_map->entries[i].face = face;
908 list_add_head(&edge_face_map->lists[new_v1], &edge_face_map->entries[i].entry);
916 static DWORD find_adjacent_face(struct edge_face_map *edge_face_map, DWORD vertex1, DWORD vertex2, CONST DWORD num_faces)
918 struct edge_face *edge_face_ptr;
920 LIST_FOR_EACH_ENTRY(edge_face_ptr, &edge_face_map->lists[vertex2], struct edge_face, entry)
922 if (edge_face_ptr->v2 == vertex1)
923 return edge_face_ptr->face;
929 static DWORD *generate_identity_point_reps(DWORD num_vertices)
931 DWORD *id_point_reps;
934 id_point_reps = HeapAlloc(GetProcessHeap(), 0, num_vertices * sizeof(*id_point_reps));
938 for (i = 0; i < num_vertices; i++)
940 id_point_reps[i] = i;
943 return id_point_reps;
946 static HRESULT WINAPI ID3DXMeshImpl_ConvertPointRepsToAdjacency(ID3DXMesh *iface, CONST DWORD *point_reps, DWORD *adjacency)
948 ID3DXMeshImpl *This = impl_from_ID3DXMesh(iface);
950 DWORD num_faces = iface->lpVtbl->GetNumFaces(iface);
951 DWORD num_vertices = iface->lpVtbl->GetNumVertices(iface);
952 DWORD options = iface->lpVtbl->GetOptions(iface);
953 BOOL indices_are_16_bit = !(options & D3DXMESH_32BIT);
958 struct edge_face_map edge_face_map = {0};
959 CONST DWORD *point_reps_ptr = NULL;
960 DWORD *id_point_reps = NULL;
962 TRACE("(%p)->(%p,%p)\n", This, point_reps, adjacency);
964 if (!adjacency) return D3DERR_INVALIDCALL;
966 if (!point_reps) /* Identity point reps */
968 id_point_reps = generate_identity_point_reps(num_vertices);
975 point_reps_ptr = id_point_reps;
979 point_reps_ptr = point_reps;
982 hr = iface->lpVtbl->LockIndexBuffer(iface, D3DLOCK_READONLY, &ib_ptr);
983 if (FAILED(hr)) goto cleanup;
985 if (indices_are_16_bit)
987 /* Widen 16 bit to 32 bit */
989 WORD *ib_16bit = ib_ptr;
990 ib = HeapAlloc(GetProcessHeap(), 0, 3 * num_faces * sizeof(DWORD));
996 for (i = 0; i < 3 * num_faces; i++)
1006 hr = init_edge_face_map(&edge_face_map, ib, point_reps_ptr, num_faces);
1007 if (FAILED(hr)) goto cleanup;
1009 /* Create adjacency */
1010 for (face = 0; face < num_faces; face++)
1012 for (edge = 0; edge < 3; edge++)
1014 DWORD v1 = ib[3*face + edge];
1015 DWORD v2 = ib[3*face + (edge+1)%3];
1016 DWORD new_v1 = point_reps_ptr[v1];
1017 DWORD new_v2 = point_reps_ptr[v2];
1020 adj_face = find_adjacent_face(&edge_face_map, new_v1, new_v2, num_faces);
1021 adjacency[3*face + edge] = adj_face;
1027 HeapFree(GetProcessHeap(), 0, id_point_reps);
1028 if (indices_are_16_bit) HeapFree(GetProcessHeap(), 0, ib);
1029 HeapFree(GetProcessHeap(), 0, edge_face_map.lists);
1030 HeapFree(GetProcessHeap(), 0, edge_face_map.entries);
1031 if(ib_ptr) iface->lpVtbl->UnlockIndexBuffer(iface);
1035 /* ConvertAdjacencyToPointReps helper function.
1037 * Goes around the edges of each face and replaces the vertices in any adjacent
1038 * face's edge with its own vertices(if its vertices have a lower index). This
1039 * way as few as possible low index vertices are shared among the faces. The
1040 * re-ordered index buffer is stored in new_indices.
1042 * The vertices in a point representation must be ordered sequentially, e.g.
1043 * index 5 holds the index of the vertex that replaces vertex 5, i.e. if
1044 * vertex 5 is replaced by vertex 3 then index 5 would contain 3. If no vertex
1045 * replaces it, then it contains the same number as the index itself, e.g.
1046 * index 5 would contain 5. */
1047 static HRESULT propagate_face_vertices(CONST DWORD *adjacency, DWORD *point_reps,
1048 CONST DWORD *indices, DWORD *new_indices,
1049 CONST DWORD face, CONST DWORD numfaces)
1051 const unsigned int VERTS_PER_FACE = 3;
1052 DWORD edge, opp_edge;
1053 DWORD face_base = VERTS_PER_FACE * face;
1055 for (edge = 0; edge < VERTS_PER_FACE; edge++)
1057 DWORD adj_face = adjacency[face_base + edge];
1058 DWORD adj_face_base;
1060 if (adj_face == -1) /* No adjacent face. */
1062 else if (adj_face >= numfaces)
1064 /* This throws exception on Windows */
1065 WARN("Index out of bounds. Got %d expected less than %d.\n",
1066 adj_face, numfaces);
1067 return D3DERR_INVALIDCALL;
1069 adj_face_base = 3 * adj_face;
1071 /* Find opposite edge in adjacent face. */
1072 for (opp_edge = 0; opp_edge < VERTS_PER_FACE; opp_edge++)
1074 DWORD opp_edge_index = adj_face_base + opp_edge;
1075 if (adjacency[opp_edge_index] == face)
1076 break; /* Found opposite edge. */
1079 /* Replaces vertices in opposite edge with vertices from current edge. */
1080 for (i = 0; i < 2; i++)
1082 DWORD from = face_base + (edge + (1 - i)) % VERTS_PER_FACE;
1083 DWORD to = adj_face_base + (opp_edge + i) % VERTS_PER_FACE;
1085 /* Propagate lowest index. */
1086 if (new_indices[to] > new_indices[from])
1088 new_indices[to] = new_indices[from];
1089 point_reps[indices[to]] = new_indices[from];
1097 static HRESULT WINAPI ID3DXMeshImpl_ConvertAdjacencyToPointReps(ID3DXMesh *iface, CONST DWORD *adjacency, DWORD *point_reps)
1102 DWORD *indices = NULL;
1103 WORD *indices_16bit = NULL;
1104 DWORD *new_indices = NULL;
1105 const unsigned int VERTS_PER_FACE = 3;
1107 ID3DXMeshImpl *This = impl_from_ID3DXMesh(iface);
1109 TRACE("(%p)->(%p,%p)\n", This, adjacency, point_reps);
1113 WARN("NULL adjacency.\n");
1114 hr = D3DERR_INVALIDCALL;
1120 WARN("NULL point_reps.\n");
1121 hr = D3DERR_INVALIDCALL;
1125 /* Should never happen as CreateMesh does not allow meshes with 0 faces */
1126 if (This->numfaces == 0)
1128 ERR("Number of faces was zero.\n");
1129 hr = D3DERR_INVALIDCALL;
1133 new_indices = HeapAlloc(GetProcessHeap(), 0, VERTS_PER_FACE * This->numfaces * sizeof(*indices));
1140 if (This->options & D3DXMESH_32BIT)
1142 hr = iface->lpVtbl->LockIndexBuffer(iface, D3DLOCK_READONLY, (void**)&indices);
1143 if (FAILED(hr)) goto cleanup;
1144 memcpy(new_indices, indices, VERTS_PER_FACE * This->numfaces * sizeof(*indices));
1148 /* Make a widening copy of indices_16bit into indices and new_indices
1149 * in order to re-use the helper function */
1150 hr = iface->lpVtbl->LockIndexBuffer(iface, D3DLOCK_READONLY, (void**)&indices_16bit);
1151 if (FAILED(hr)) goto cleanup;
1152 indices = HeapAlloc(GetProcessHeap(), 0, VERTS_PER_FACE * This->numfaces * sizeof(*indices));
1158 for (i = 0; i < VERTS_PER_FACE * This->numfaces; i++)
1160 new_indices[i] = indices_16bit[i];
1161 indices[i] = indices_16bit[i];
1165 /* Vertices are ordered sequentially in the point representation. */
1166 for (i = 0; i < This->numvertices; i++)
1171 /* Propagate vertices with low indices so as few vertices as possible
1172 * are used in the mesh.
1174 for (face = 0; face < This->numfaces; face++)
1176 hr = propagate_face_vertices(adjacency, point_reps, indices, new_indices, face, This->numfaces);
1177 if (FAILED(hr)) goto cleanup;
1179 /* Go in opposite direction to catch all face orderings */
1180 for (face = 0; face < This->numfaces; face++)
1182 hr = propagate_face_vertices(adjacency, point_reps,
1183 indices, new_indices,
1184 (This->numfaces - 1) - face, This->numfaces);
1185 if (FAILED(hr)) goto cleanup;
1190 if (This->options & D3DXMESH_32BIT)
1192 if (indices) iface->lpVtbl->UnlockIndexBuffer(iface);
1196 if (indices_16bit) iface->lpVtbl->UnlockIndexBuffer(iface);
1197 HeapFree(GetProcessHeap(), 0, indices);
1199 HeapFree(GetProcessHeap(), 0, new_indices);
1203 struct vertex_metadata {
1206 DWORD first_shared_index;
1209 static int compare_vertex_keys(const void *a, const void *b)
1211 const struct vertex_metadata *left = a;
1212 const struct vertex_metadata *right = b;
1213 if (left->key == right->key)
1215 return left->key < right->key ? -1 : 1;
1218 static HRESULT WINAPI ID3DXMeshImpl_GenerateAdjacency(ID3DXMesh *iface, FLOAT epsilon, DWORD *adjacency)
1220 ID3DXMeshImpl *This = impl_from_ID3DXMesh(iface);
1222 BYTE *vertices = NULL;
1223 const DWORD *indices = NULL;
1226 /* sort the vertices by (x + y + z) to quickly find coincident vertices */
1227 struct vertex_metadata *sorted_vertices;
1228 /* shared_indices links together identical indices in the index buffer so
1229 * that adjacency checks can be limited to faces sharing a vertex */
1230 DWORD *shared_indices = NULL;
1231 const FLOAT epsilon_sq = epsilon * epsilon;
1234 TRACE("(%p)->(%f,%p)\n", This, epsilon, adjacency);
1237 return D3DERR_INVALIDCALL;
1239 buffer_size = This->numfaces * 3 * sizeof(*shared_indices) + This->numvertices * sizeof(*sorted_vertices);
1240 if (!(This->options & D3DXMESH_32BIT))
1241 buffer_size += This->numfaces * 3 * sizeof(*indices);
1242 shared_indices = HeapAlloc(GetProcessHeap(), 0, buffer_size);
1243 if (!shared_indices)
1244 return E_OUTOFMEMORY;
1245 sorted_vertices = (struct vertex_metadata*)(shared_indices + This->numfaces * 3);
1247 hr = iface->lpVtbl->LockVertexBuffer(iface, D3DLOCK_READONLY, (void**)&vertices);
1248 if (FAILED(hr)) goto cleanup;
1249 hr = iface->lpVtbl->LockIndexBuffer(iface, D3DLOCK_READONLY, (void**)&indices);
1250 if (FAILED(hr)) goto cleanup;
1252 if (!(This->options & D3DXMESH_32BIT)) {
1253 const WORD *word_indices = (const WORD*)indices;
1254 DWORD *dword_indices = (DWORD*)(sorted_vertices + This->numvertices);
1255 indices = dword_indices;
1256 for (i = 0; i < This->numfaces * 3; i++)
1257 *dword_indices++ = *word_indices++;
1260 vertex_size = iface->lpVtbl->GetNumBytesPerVertex(iface);
1261 for (i = 0; i < This->numvertices; i++) {
1262 D3DXVECTOR3 *vertex = (D3DXVECTOR3*)(vertices + vertex_size * i);
1263 sorted_vertices[i].first_shared_index = -1;
1264 sorted_vertices[i].key = vertex->x + vertex->y + vertex->z;
1265 sorted_vertices[i].vertex_index = i;
1267 for (i = 0; i < This->numfaces * 3; i++) {
1268 DWORD *first_shared_index = &sorted_vertices[indices[i]].first_shared_index;
1269 shared_indices[i] = *first_shared_index;
1270 *first_shared_index = i;
1273 qsort(sorted_vertices, This->numvertices, sizeof(*sorted_vertices), compare_vertex_keys);
1275 for (i = 0; i < This->numvertices; i++) {
1276 struct vertex_metadata *sorted_vertex_a = &sorted_vertices[i];
1277 D3DXVECTOR3 *vertex_a = (D3DXVECTOR3*)(vertices + sorted_vertex_a->vertex_index * vertex_size);
1278 DWORD shared_index_a = sorted_vertex_a->first_shared_index;
1280 while (shared_index_a != -1) {
1282 DWORD shared_index_b = shared_indices[shared_index_a];
1283 struct vertex_metadata *sorted_vertex_b = sorted_vertex_a;
1286 while (shared_index_b != -1) {
1287 /* faces are adjacent if they have another coincident vertex */
1288 DWORD base_a = (shared_index_a / 3) * 3;
1289 DWORD base_b = (shared_index_b / 3) * 3;
1290 BOOL adjacent = FALSE;
1293 for (k = 0; k < 3; k++) {
1294 if (adjacency[base_b + k] == shared_index_a / 3) {
1300 for (k = 1; k <= 2; k++) {
1301 DWORD vertex_index_a = base_a + (shared_index_a + k) % 3;
1302 DWORD vertex_index_b = base_b + (shared_index_b + (3 - k)) % 3;
1303 adjacent = indices[vertex_index_a] == indices[vertex_index_b];
1304 if (!adjacent && epsilon >= 0.0f) {
1305 D3DXVECTOR3 delta = {0.0f, 0.0f, 0.0f};
1308 D3DXVec3Subtract(&delta,
1309 (D3DXVECTOR3*)(vertices + indices[vertex_index_a] * vertex_size),
1310 (D3DXVECTOR3*)(vertices + indices[vertex_index_b] * vertex_size));
1311 length_sq = D3DXVec3LengthSq(&delta);
1312 adjacent = epsilon == 0.0f ? length_sq == 0.0f : length_sq < epsilon_sq;
1315 DWORD adj_a = base_a + 2 - (vertex_index_a + shared_index_a + 1) % 3;
1316 DWORD adj_b = base_b + 2 - (vertex_index_b + shared_index_b + 1) % 3;
1317 if (adjacency[adj_a] == -1 && adjacency[adj_b] == -1) {
1318 adjacency[adj_a] = base_b / 3;
1319 adjacency[adj_b] = base_a / 3;
1326 shared_index_b = shared_indices[shared_index_b];
1328 while (++j < This->numvertices) {
1329 D3DXVECTOR3 *vertex_b;
1332 if (sorted_vertex_b->key - sorted_vertex_a->key > epsilon * 3.0f) {
1333 /* no more coincident vertices to try */
1334 j = This->numvertices;
1337 /* check for coincidence */
1338 vertex_b = (D3DXVECTOR3*)(vertices + sorted_vertex_b->vertex_index * vertex_size);
1339 if (fabsf(vertex_a->x - vertex_b->x) <= epsilon &&
1340 fabsf(vertex_a->y - vertex_b->y) <= epsilon &&
1341 fabsf(vertex_a->z - vertex_b->z) <= epsilon)
1346 if (j >= This->numvertices)
1348 shared_index_b = sorted_vertex_b->first_shared_index;
1351 sorted_vertex_a->first_shared_index = shared_indices[sorted_vertex_a->first_shared_index];
1352 shared_index_a = sorted_vertex_a->first_shared_index;
1358 if (indices) iface->lpVtbl->UnlockIndexBuffer(iface);
1359 if (vertices) iface->lpVtbl->UnlockVertexBuffer(iface);
1360 HeapFree(GetProcessHeap(), 0, shared_indices);
1364 static HRESULT WINAPI ID3DXMeshImpl_UpdateSemantics(ID3DXMesh *iface, D3DVERTEXELEMENT9 declaration[MAX_FVF_DECL_SIZE])
1367 ID3DXMeshImpl *This = impl_from_ID3DXMesh(iface);
1368 UINT vertex_declaration_size;
1371 TRACE("(%p)->(%p)\n", This, declaration);
1375 WARN("Invalid declaration. Can't use NULL declaration.\n");
1376 return D3DERR_INVALIDCALL;
1379 /* New declaration must be same size as original */
1380 vertex_declaration_size = D3DXGetDeclVertexSize(declaration, declaration[0].Stream);
1381 if (vertex_declaration_size != This->vertex_declaration_size)
1383 WARN("Invalid declaration. New vertex size does not match the original vertex size.\n");
1384 return D3DERR_INVALIDCALL;
1387 /* New declaration must not contain non-zero Stream value */
1388 for (i = 0; declaration[i].Stream != 0xff; i++)
1390 if (declaration[i].Stream != 0)
1392 WARN("Invalid declaration. New declaration contains non-zero Stream value.\n");
1393 return D3DERR_INVALIDCALL;
1397 This->num_elem = i + 1;
1398 copy_declaration(This->cached_declaration, declaration, This->num_elem);
1400 if (This->vertex_declaration)
1401 IDirect3DVertexDeclaration9_Release(This->vertex_declaration);
1403 /* An application can pass an invalid declaration to UpdateSemantics and
1404 * still expect D3D_OK (see tests). If the declaration is invalid, then
1405 * subsequent calls to DrawSubset will fail. This is handled by setting the
1406 * vertex declaration to NULL.
1407 * GetDeclaration, GetNumBytesPerVertex must, however, use the new
1408 * invalid declaration. This is handled by them using the cached vertex
1409 * declaration instead of the actual vertex declaration.
1411 hr = IDirect3DDevice9_CreateVertexDeclaration(This->device,
1413 &This->vertex_declaration);
1416 WARN("Using invalid declaration. Calls to DrawSubset will fail.\n");
1417 This->vertex_declaration = NULL;
1424 static HRESULT WINAPI ID3DXMeshImpl_LockAttributeBuffer(ID3DXMesh *iface, DWORD flags, DWORD **data)
1426 ID3DXMeshImpl *This = impl_from_ID3DXMesh(iface);
1428 TRACE("(%p)->(%u,%p)\n", This, flags, data);
1430 InterlockedIncrement(&This->attrib_buffer_lock_count);
1432 if (!(flags & D3DLOCK_READONLY)) {
1433 D3DXATTRIBUTERANGE *attrib_table = This->attrib_table;
1434 This->attrib_table_size = 0;
1435 This->attrib_table = NULL;
1436 HeapFree(GetProcessHeap(), 0, attrib_table);
1439 *data = This->attrib_buffer;
1444 static HRESULT WINAPI ID3DXMeshImpl_UnlockAttributeBuffer(ID3DXMesh *iface)
1446 ID3DXMeshImpl *This = impl_from_ID3DXMesh(iface);
1449 TRACE("(%p)\n", This);
1451 lock_count = InterlockedDecrement(&This->attrib_buffer_lock_count);
1453 if (lock_count < 0) {
1454 InterlockedIncrement(&This->attrib_buffer_lock_count);
1455 return D3DERR_INVALIDCALL;
1461 static HRESULT WINAPI ID3DXMeshImpl_Optimize(ID3DXMesh *iface, DWORD flags, CONST DWORD *adjacency_in, DWORD *adjacency_out,
1462 DWORD *face_remap, LPD3DXBUFFER *vertex_remap, LPD3DXMESH *opt_mesh)
1464 ID3DXMeshImpl *This = impl_from_ID3DXMesh(iface);
1466 D3DVERTEXELEMENT9 declaration[MAX_FVF_DECL_SIZE] = { D3DDECL_END() };
1467 ID3DXMesh *optimized_mesh;
1469 TRACE("(%p)->(%x,%p,%p,%p,%p,%p)\n", This, flags, adjacency_in, adjacency_out, face_remap, vertex_remap, opt_mesh);
1472 return D3DERR_INVALIDCALL;
1474 hr = iface->lpVtbl->GetDeclaration(iface, declaration);
1475 if (FAILED(hr)) return hr;
1477 hr = iface->lpVtbl->CloneMesh(iface, This->options, declaration, This->device, &optimized_mesh);
1478 if (FAILED(hr)) return hr;
1480 hr = optimized_mesh->lpVtbl->OptimizeInplace(optimized_mesh, flags, adjacency_in, adjacency_out, face_remap, vertex_remap);
1482 *opt_mesh = optimized_mesh;
1484 IUnknown_Release(optimized_mesh);
1488 /* Creates a vertex_remap that removes unused vertices.
1489 * Indices are updated according to the vertex_remap. */
1490 static HRESULT compact_mesh(ID3DXMeshImpl *This, DWORD *indices, DWORD *new_num_vertices, ID3DXBuffer **vertex_remap)
1493 DWORD *vertex_remap_ptr;
1494 DWORD num_used_vertices;
1497 hr = D3DXCreateBuffer(This->numvertices * sizeof(DWORD), vertex_remap);
1498 if (FAILED(hr)) return hr;
1499 vertex_remap_ptr = ID3DXBuffer_GetBufferPointer(*vertex_remap);
1501 for (i = 0; i < This->numfaces * 3; i++)
1502 vertex_remap_ptr[indices[i]] = 1;
1504 /* create old->new vertex mapping */
1505 num_used_vertices = 0;
1506 for (i = 0; i < This->numvertices; i++) {
1507 if (vertex_remap_ptr[i])
1508 vertex_remap_ptr[i] = num_used_vertices++;
1510 vertex_remap_ptr[i] = -1;
1512 /* convert indices */
1513 for (i = 0; i < This->numfaces * 3; i++)
1514 indices[i] = vertex_remap_ptr[indices[i]];
1516 /* create new->old vertex mapping */
1517 num_used_vertices = 0;
1518 for (i = 0; i < This->numvertices; i++) {
1519 if (vertex_remap_ptr[i] != -1)
1520 vertex_remap_ptr[num_used_vertices++] = i;
1522 for (i = num_used_vertices; i < This->numvertices; i++)
1523 vertex_remap_ptr[i] = -1;
1525 *new_num_vertices = num_used_vertices;
1530 /* count the number of unique attribute values in a sorted attribute buffer */
1531 static DWORD count_attributes(const DWORD *attrib_buffer, DWORD numfaces)
1533 DWORD last_attribute = attrib_buffer[0];
1534 DWORD attrib_table_size = 1;
1536 for (i = 1; i < numfaces; i++) {
1537 if (attrib_buffer[i] != last_attribute) {
1538 last_attribute = attrib_buffer[i];
1539 attrib_table_size++;
1542 return attrib_table_size;
1545 static void fill_attribute_table(DWORD *attrib_buffer, DWORD numfaces, void *indices,
1546 BOOL is_32bit_indices, D3DXATTRIBUTERANGE *attrib_table)
1548 DWORD attrib_table_size = 0;
1549 DWORD last_attribute = attrib_buffer[0];
1550 DWORD min_vertex, max_vertex;
1553 attrib_table[0].AttribId = last_attribute;
1554 attrib_table[0].FaceStart = 0;
1555 min_vertex = (DWORD)-1;
1557 for (i = 0; i < numfaces; i++) {
1560 if (attrib_buffer[i] != last_attribute) {
1561 last_attribute = attrib_buffer[i];
1562 attrib_table[attrib_table_size].FaceCount = i - attrib_table[attrib_table_size].FaceStart;
1563 attrib_table[attrib_table_size].VertexStart = min_vertex;
1564 attrib_table[attrib_table_size].VertexCount = max_vertex - min_vertex + 1;
1565 attrib_table_size++;
1566 attrib_table[attrib_table_size].AttribId = attrib_buffer[i];
1567 attrib_table[attrib_table_size].FaceStart = i;
1568 min_vertex = (DWORD)-1;
1571 for (j = 0; j < 3; j++) {
1572 DWORD vertex_index = is_32bit_indices ? ((DWORD*)indices)[i * 3 + j] : ((WORD*)indices)[i * 3 + j];
1573 if (vertex_index < min_vertex)
1574 min_vertex = vertex_index;
1575 if (vertex_index > max_vertex)
1576 max_vertex = vertex_index;
1579 attrib_table[attrib_table_size].FaceCount = i - attrib_table[attrib_table_size].FaceStart;
1580 attrib_table[attrib_table_size].VertexStart = min_vertex;
1581 attrib_table[attrib_table_size].VertexCount = max_vertex - min_vertex + 1;
1582 attrib_table_size++;
1585 static int attrib_entry_compare(const DWORD **a, const DWORD **b)
1587 const DWORD *ptr_a = *a;
1588 const DWORD *ptr_b = *b;
1589 int delta = *ptr_a - *ptr_b;
1594 delta = ptr_a - ptr_b; /* for stable sort */
1598 /* Create face_remap, a new attribute buffer for attribute sort optimization. */
1599 static HRESULT remap_faces_for_attrsort(ID3DXMeshImpl *This, const DWORD *indices,
1600 const DWORD *attrib_buffer, DWORD **sorted_attrib_buffer, DWORD **face_remap)
1602 const DWORD **sorted_attrib_ptr_buffer = NULL;
1605 *face_remap = HeapAlloc(GetProcessHeap(), 0, This->numfaces * sizeof(**face_remap));
1606 sorted_attrib_ptr_buffer = HeapAlloc(GetProcessHeap(), 0, This->numfaces * sizeof(*sorted_attrib_ptr_buffer));
1607 if (!*face_remap || !sorted_attrib_ptr_buffer) {
1608 HeapFree(GetProcessHeap(), 0, sorted_attrib_ptr_buffer);
1609 return E_OUTOFMEMORY;
1611 for (i = 0; i < This->numfaces; i++)
1612 sorted_attrib_ptr_buffer[i] = &attrib_buffer[i];
1613 qsort(sorted_attrib_ptr_buffer, This->numfaces, sizeof(*sorted_attrib_ptr_buffer),
1614 (int(*)(const void *, const void *))attrib_entry_compare);
1616 for (i = 0; i < This->numfaces; i++)
1618 DWORD old_face = sorted_attrib_ptr_buffer[i] - attrib_buffer;
1619 (*face_remap)[old_face] = i;
1622 /* overwrite sorted_attrib_ptr_buffer with the values themselves */
1623 *sorted_attrib_buffer = (DWORD*)sorted_attrib_ptr_buffer;
1624 for (i = 0; i < This->numfaces; i++)
1625 (*sorted_attrib_buffer)[(*face_remap)[i]] = attrib_buffer[i];
1630 static HRESULT WINAPI ID3DXMeshImpl_OptimizeInplace(ID3DXMesh *iface, DWORD flags, CONST DWORD *adjacency_in, DWORD *adjacency_out,
1631 DWORD *face_remap_out, LPD3DXBUFFER *vertex_remap_out)
1633 ID3DXMeshImpl *This = impl_from_ID3DXMesh(iface);
1634 void *indices = NULL;
1635 DWORD *attrib_buffer = NULL;
1637 ID3DXBuffer *vertex_remap = NULL;
1638 DWORD *face_remap = NULL; /* old -> new mapping */
1639 DWORD *dword_indices = NULL;
1640 DWORD new_num_vertices = 0;
1641 DWORD new_num_alloc_vertices = 0;
1642 IDirect3DVertexBuffer9 *vertex_buffer = NULL;
1643 DWORD *sorted_attrib_buffer = NULL;
1646 TRACE("(%p)->(%x,%p,%p,%p,%p)\n", This, flags, adjacency_in, adjacency_out, face_remap_out, vertex_remap_out);
1649 return D3DERR_INVALIDCALL;
1650 if (!adjacency_in && (flags & (D3DXMESHOPT_VERTEXCACHE | D3DXMESHOPT_STRIPREORDER)))
1651 return D3DERR_INVALIDCALL;
1652 if ((flags & (D3DXMESHOPT_VERTEXCACHE | D3DXMESHOPT_STRIPREORDER)) == (D3DXMESHOPT_VERTEXCACHE | D3DXMESHOPT_STRIPREORDER))
1653 return D3DERR_INVALIDCALL;
1655 if (flags & (D3DXMESHOPT_VERTEXCACHE | D3DXMESHOPT_STRIPREORDER))
1657 if (flags & D3DXMESHOPT_VERTEXCACHE)
1658 FIXME("D3DXMESHOPT_VERTEXCACHE not implemented.\n");
1659 if (flags & D3DXMESHOPT_STRIPREORDER)
1660 FIXME("D3DXMESHOPT_STRIPREORDER not implemented.\n");
1664 hr = iface->lpVtbl->LockIndexBuffer(iface, 0, &indices);
1665 if (FAILED(hr)) goto cleanup;
1667 dword_indices = HeapAlloc(GetProcessHeap(), 0, This->numfaces * 3 * sizeof(DWORD));
1668 if (!dword_indices) return E_OUTOFMEMORY;
1669 if (This->options & D3DXMESH_32BIT) {
1670 memcpy(dword_indices, indices, This->numfaces * 3 * sizeof(DWORD));
1672 WORD *word_indices = indices;
1673 for (i = 0; i < This->numfaces * 3; i++)
1674 dword_indices[i] = *word_indices++;
1677 if ((flags & (D3DXMESHOPT_COMPACT | D3DXMESHOPT_IGNOREVERTS | D3DXMESHOPT_ATTRSORT)) == D3DXMESHOPT_COMPACT)
1679 new_num_alloc_vertices = This->numvertices;
1680 hr = compact_mesh(This, dword_indices, &new_num_vertices, &vertex_remap);
1681 if (FAILED(hr)) goto cleanup;
1682 } else if (flags & D3DXMESHOPT_ATTRSORT) {
1683 if (!(flags & D3DXMESHOPT_IGNOREVERTS))
1685 FIXME("D3DXMESHOPT_ATTRSORT vertex reordering not implemented.\n");
1690 hr = iface->lpVtbl->LockAttributeBuffer(iface, 0, &attrib_buffer);
1691 if (FAILED(hr)) goto cleanup;
1693 hr = remap_faces_for_attrsort(This, dword_indices, attrib_buffer, &sorted_attrib_buffer, &face_remap);
1694 if (FAILED(hr)) goto cleanup;
1699 /* reorder the vertices using vertex_remap */
1700 D3DVERTEXBUFFER_DESC vertex_desc;
1701 DWORD *vertex_remap_ptr = ID3DXBuffer_GetBufferPointer(vertex_remap);
1702 DWORD vertex_size = iface->lpVtbl->GetNumBytesPerVertex(iface);
1703 BYTE *orig_vertices;
1706 hr = IDirect3DVertexBuffer9_GetDesc(This->vertex_buffer, &vertex_desc);
1707 if (FAILED(hr)) goto cleanup;
1709 hr = IDirect3DDevice9_CreateVertexBuffer(This->device, new_num_alloc_vertices * vertex_size,
1710 vertex_desc.Usage, This->fvf, vertex_desc.Pool, &vertex_buffer, NULL);
1711 if (FAILED(hr)) goto cleanup;
1713 hr = IDirect3DVertexBuffer9_Lock(This->vertex_buffer, 0, 0, (void**)&orig_vertices, D3DLOCK_READONLY);
1714 if (FAILED(hr)) goto cleanup;
1716 hr = IDirect3DVertexBuffer9_Lock(vertex_buffer, 0, 0, (void**)&new_vertices, 0);
1718 IDirect3DVertexBuffer9_Unlock(This->vertex_buffer);
1722 for (i = 0; i < new_num_vertices; i++)
1723 memcpy(new_vertices + i * vertex_size, orig_vertices + vertex_remap_ptr[i] * vertex_size, vertex_size);
1725 IDirect3DVertexBuffer9_Unlock(This->vertex_buffer);
1726 IDirect3DVertexBuffer9_Unlock(vertex_buffer);
1727 } else if (vertex_remap_out) {
1728 DWORD *vertex_remap_ptr;
1730 hr = D3DXCreateBuffer(This->numvertices * sizeof(DWORD), &vertex_remap);
1731 if (FAILED(hr)) goto cleanup;
1732 vertex_remap_ptr = ID3DXBuffer_GetBufferPointer(vertex_remap);
1733 for (i = 0; i < This->numvertices; i++)
1734 *vertex_remap_ptr++ = i;
1737 if (flags & D3DXMESHOPT_ATTRSORT)
1739 D3DXATTRIBUTERANGE *attrib_table;
1740 DWORD attrib_table_size;
1742 attrib_table_size = count_attributes(sorted_attrib_buffer, This->numfaces);
1743 attrib_table = HeapAlloc(GetProcessHeap(), 0, attrib_table_size * sizeof(*attrib_table));
1744 if (!attrib_table) {
1749 memcpy(attrib_buffer, sorted_attrib_buffer, This->numfaces * sizeof(*attrib_buffer));
1751 /* reorder the indices using face_remap */
1752 if (This->options & D3DXMESH_32BIT) {
1753 for (i = 0; i < This->numfaces; i++)
1754 memcpy((DWORD*)indices + face_remap[i] * 3, dword_indices + i * 3, 3 * sizeof(DWORD));
1756 WORD *word_indices = indices;
1757 for (i = 0; i < This->numfaces; i++) {
1758 DWORD new_pos = face_remap[i] * 3;
1759 DWORD old_pos = i * 3;
1760 word_indices[new_pos++] = dword_indices[old_pos++];
1761 word_indices[new_pos++] = dword_indices[old_pos++];
1762 word_indices[new_pos] = dword_indices[old_pos];
1766 fill_attribute_table(attrib_buffer, This->numfaces, indices,
1767 This->options & D3DXMESH_32BIT, attrib_table);
1769 HeapFree(GetProcessHeap(), 0, This->attrib_table);
1770 This->attrib_table = attrib_table;
1771 This->attrib_table_size = attrib_table_size;
1773 if (This->options & D3DXMESH_32BIT) {
1774 memcpy(indices, dword_indices, This->numfaces * 3 * sizeof(DWORD));
1776 WORD *word_indices = indices;
1777 for (i = 0; i < This->numfaces * 3; i++)
1778 *word_indices++ = dword_indices[i];
1782 if (adjacency_out) {
1784 for (i = 0; i < This->numfaces; i++) {
1785 DWORD old_pos = i * 3;
1786 DWORD new_pos = face_remap[i] * 3;
1787 adjacency_out[new_pos++] = face_remap[adjacency_in[old_pos++]];
1788 adjacency_out[new_pos++] = face_remap[adjacency_in[old_pos++]];
1789 adjacency_out[new_pos++] = face_remap[adjacency_in[old_pos++]];
1792 memcpy(adjacency_out, adjacency_in, This->numfaces * 3 * sizeof(*adjacency_out));
1795 if (face_remap_out) {
1797 for (i = 0; i < This->numfaces; i++)
1798 face_remap_out[face_remap[i]] = i;
1800 for (i = 0; i < This->numfaces; i++)
1801 face_remap_out[i] = i;
1804 if (vertex_remap_out)
1805 *vertex_remap_out = vertex_remap;
1806 vertex_remap = NULL;
1808 if (vertex_buffer) {
1809 IDirect3DVertexBuffer9_Release(This->vertex_buffer);
1810 This->vertex_buffer = vertex_buffer;
1811 vertex_buffer = NULL;
1812 This->numvertices = new_num_vertices;
1817 HeapFree(GetProcessHeap(), 0, sorted_attrib_buffer);
1818 HeapFree(GetProcessHeap(), 0, face_remap);
1819 HeapFree(GetProcessHeap(), 0, dword_indices);
1820 if (vertex_remap) ID3DXBuffer_Release(vertex_remap);
1821 if (vertex_buffer) IDirect3DVertexBuffer9_Release(vertex_buffer);
1822 if (attrib_buffer) iface->lpVtbl->UnlockAttributeBuffer(iface);
1823 if (indices) iface->lpVtbl->UnlockIndexBuffer(iface);
1827 static HRESULT WINAPI ID3DXMeshImpl_SetAttributeTable(ID3DXMesh *iface, CONST D3DXATTRIBUTERANGE *attrib_table, DWORD attrib_table_size)
1829 ID3DXMeshImpl *This = impl_from_ID3DXMesh(iface);
1830 D3DXATTRIBUTERANGE *new_table = NULL;
1832 TRACE("(%p)->(%p,%u)\n", This, attrib_table, attrib_table_size);
1834 if (attrib_table_size) {
1835 size_t size = attrib_table_size * sizeof(*attrib_table);
1837 new_table = HeapAlloc(GetProcessHeap(), 0, size);
1839 return E_OUTOFMEMORY;
1841 CopyMemory(new_table, attrib_table, size);
1842 } else if (attrib_table) {
1843 return D3DERR_INVALIDCALL;
1845 HeapFree(GetProcessHeap(), 0, This->attrib_table);
1846 This->attrib_table = new_table;
1847 This->attrib_table_size = attrib_table_size;
1852 static const struct ID3DXMeshVtbl D3DXMesh_Vtbl =
1854 /*** IUnknown methods ***/
1855 ID3DXMeshImpl_QueryInterface,
1856 ID3DXMeshImpl_AddRef,
1857 ID3DXMeshImpl_Release,
1858 /*** ID3DXBaseMesh ***/
1859 ID3DXMeshImpl_DrawSubset,
1860 ID3DXMeshImpl_GetNumFaces,
1861 ID3DXMeshImpl_GetNumVertices,
1862 ID3DXMeshImpl_GetFVF,
1863 ID3DXMeshImpl_GetDeclaration,
1864 ID3DXMeshImpl_GetNumBytesPerVertex,
1865 ID3DXMeshImpl_GetOptions,
1866 ID3DXMeshImpl_GetDevice,
1867 ID3DXMeshImpl_CloneMeshFVF,
1868 ID3DXMeshImpl_CloneMesh,
1869 ID3DXMeshImpl_GetVertexBuffer,
1870 ID3DXMeshImpl_GetIndexBuffer,
1871 ID3DXMeshImpl_LockVertexBuffer,
1872 ID3DXMeshImpl_UnlockVertexBuffer,
1873 ID3DXMeshImpl_LockIndexBuffer,
1874 ID3DXMeshImpl_UnlockIndexBuffer,
1875 ID3DXMeshImpl_GetAttributeTable,
1876 ID3DXMeshImpl_ConvertPointRepsToAdjacency,
1877 ID3DXMeshImpl_ConvertAdjacencyToPointReps,
1878 ID3DXMeshImpl_GenerateAdjacency,
1879 ID3DXMeshImpl_UpdateSemantics,
1881 ID3DXMeshImpl_LockAttributeBuffer,
1882 ID3DXMeshImpl_UnlockAttributeBuffer,
1883 ID3DXMeshImpl_Optimize,
1884 ID3DXMeshImpl_OptimizeInplace,
1885 ID3DXMeshImpl_SetAttributeTable
1888 /*************************************************************************
1891 BOOL WINAPI D3DXBoxBoundProbe(CONST D3DXVECTOR3 *pmin, CONST D3DXVECTOR3 *pmax, CONST D3DXVECTOR3 *prayposition, CONST D3DXVECTOR3 *praydirection)
1893 /* Algorithm taken from the article: An Efficient and Robust Ray-Box Intersection Algorithm
1894 Amy Williams University of Utah
1895 Steve Barrus University of Utah
1896 R. Keith Morley University of Utah
1897 Peter Shirley University of Utah
1899 International Conference on Computer Graphics and Interactive Techniques archive
1900 ACM SIGGRAPH 2005 Courses
1901 Los Angeles, California
1903 This algorithm is free of patents or of copyrights, as confirmed by Peter Shirley himself.
1905 Algorithm: Consider the box as the intersection of three slabs. Clip the ray
1906 against each slab, if there's anything left of the ray after we're
1907 done we've got an intersection of the ray with the box.
1911 FLOAT div, tmin, tmax, tymin, tymax, tzmin, tzmax;
1913 div = 1.0f / praydirection->x;
1916 tmin = ( pmin->x - prayposition->x ) * div;
1917 tmax = ( pmax->x - prayposition->x ) * div;
1921 tmin = ( pmax->x - prayposition->x ) * div;
1922 tmax = ( pmin->x - prayposition->x ) * div;
1925 if ( tmax < 0.0f ) return FALSE;
1927 div = 1.0f / praydirection->y;
1930 tymin = ( pmin->y - prayposition->y ) * div;
1931 tymax = ( pmax->y - prayposition->y ) * div;
1935 tymin = ( pmax->y - prayposition->y ) * div;
1936 tymax = ( pmin->y - prayposition->y ) * div;
1939 if ( ( tymax < 0.0f ) || ( tmin > tymax ) || ( tymin > tmax ) ) return FALSE;
1941 if ( tymin > tmin ) tmin = tymin;
1942 if ( tymax < tmax ) tmax = tymax;
1944 div = 1.0f / praydirection->z;
1947 tzmin = ( pmin->z - prayposition->z ) * div;
1948 tzmax = ( pmax->z - prayposition->z ) * div;
1952 tzmin = ( pmax->z - prayposition->z ) * div;
1953 tzmax = ( pmin->z - prayposition->z ) * div;
1956 if ( (tzmax < 0.0f ) || ( tmin > tzmax ) || ( tzmin > tmax ) ) return FALSE;
1961 /*************************************************************************
1962 * D3DXComputeBoundingBox
1964 HRESULT WINAPI D3DXComputeBoundingBox(CONST D3DXVECTOR3 *pfirstposition, DWORD numvertices, DWORD dwstride, D3DXVECTOR3 *pmin, D3DXVECTOR3 *pmax)
1969 if( !pfirstposition || !pmin || !pmax ) return D3DERR_INVALIDCALL;
1971 *pmin = *pfirstposition;
1974 for(i=0; i<numvertices; i++)
1976 vec = *( (const D3DXVECTOR3*)((const char*)pfirstposition + dwstride * i) );
1978 if ( vec.x < pmin->x ) pmin->x = vec.x;
1979 if ( vec.x > pmax->x ) pmax->x = vec.x;
1981 if ( vec.y < pmin->y ) pmin->y = vec.y;
1982 if ( vec.y > pmax->y ) pmax->y = vec.y;
1984 if ( vec.z < pmin->z ) pmin->z = vec.z;
1985 if ( vec.z > pmax->z ) pmax->z = vec.z;
1991 /*************************************************************************
1992 * D3DXComputeBoundingSphere
1994 HRESULT WINAPI D3DXComputeBoundingSphere(CONST D3DXVECTOR3* pfirstposition, DWORD numvertices, DWORD dwstride, D3DXVECTOR3 *pcenter, FLOAT *pradius)
2000 if( !pfirstposition || !pcenter || !pradius ) return D3DERR_INVALIDCALL;
2007 for(i=0; i<numvertices; i++)
2008 D3DXVec3Add(&temp, &temp, (const D3DXVECTOR3*)((const char*)pfirstposition + dwstride * i));
2010 D3DXVec3Scale(pcenter, &temp, 1.0f / numvertices);
2012 for(i=0; i<numvertices; i++)
2014 d = D3DXVec3Length(D3DXVec3Subtract(&temp, (const D3DXVECTOR3*)((const char*)pfirstposition + dwstride * i), pcenter));
2015 if ( d > *pradius ) *pradius = d;
2020 static void append_decl_element(D3DVERTEXELEMENT9 *declaration, UINT *idx, UINT *offset,
2021 D3DDECLTYPE type, D3DDECLUSAGE usage, UINT usage_idx)
2023 declaration[*idx].Stream = 0;
2024 declaration[*idx].Offset = *offset;
2025 declaration[*idx].Type = type;
2026 declaration[*idx].Method = D3DDECLMETHOD_DEFAULT;
2027 declaration[*idx].Usage = usage;
2028 declaration[*idx].UsageIndex = usage_idx;
2030 *offset += d3dx_decltype_size[type];
2034 /*************************************************************************
2035 * D3DXDeclaratorFromFVF
2037 HRESULT WINAPI D3DXDeclaratorFromFVF(DWORD fvf, D3DVERTEXELEMENT9 declaration[MAX_FVF_DECL_SIZE])
2039 static const D3DVERTEXELEMENT9 end_element = D3DDECL_END();
2040 DWORD tex_count = (fvf & D3DFVF_TEXCOUNT_MASK) >> D3DFVF_TEXCOUNT_SHIFT;
2041 unsigned int offset = 0;
2042 unsigned int idx = 0;
2045 TRACE("fvf %#x, declaration %p.\n", fvf, declaration);
2047 if (fvf & (D3DFVF_RESERVED0 | D3DFVF_RESERVED2)) return D3DERR_INVALIDCALL;
2049 if (fvf & D3DFVF_POSITION_MASK)
2051 BOOL has_blend = (fvf & D3DFVF_XYZB5) >= D3DFVF_XYZB1;
2052 DWORD blend_count = 1 + (((fvf & D3DFVF_XYZB5) - D3DFVF_XYZB1) >> 1);
2053 BOOL has_blend_idx = (fvf & D3DFVF_LASTBETA_D3DCOLOR) || (fvf & D3DFVF_LASTBETA_UBYTE4);
2055 if (has_blend_idx) --blend_count;
2057 if ((fvf & D3DFVF_POSITION_MASK) == D3DFVF_XYZW
2058 || (has_blend && blend_count > 4))
2059 return D3DERR_INVALIDCALL;
2061 if ((fvf & D3DFVF_POSITION_MASK) == D3DFVF_XYZRHW)
2062 append_decl_element(declaration, &idx, &offset, D3DDECLTYPE_FLOAT4, D3DDECLUSAGE_POSITIONT, 0);
2064 append_decl_element(declaration, &idx, &offset, D3DDECLTYPE_FLOAT3, D3DDECLUSAGE_POSITION, 0);
2068 switch (blend_count)
2073 append_decl_element(declaration, &idx, &offset, D3DDECLTYPE_FLOAT1, D3DDECLUSAGE_BLENDWEIGHT, 0);
2076 append_decl_element(declaration, &idx, &offset, D3DDECLTYPE_FLOAT2, D3DDECLUSAGE_BLENDWEIGHT, 0);
2079 append_decl_element(declaration, &idx, &offset, D3DDECLTYPE_FLOAT3, D3DDECLUSAGE_BLENDWEIGHT, 0);
2082 append_decl_element(declaration, &idx, &offset, D3DDECLTYPE_FLOAT4, D3DDECLUSAGE_BLENDWEIGHT, 0);
2085 ERR("Invalid blend count %u.\n", blend_count);
2091 if (fvf & D3DFVF_LASTBETA_UBYTE4)
2092 append_decl_element(declaration, &idx, &offset, D3DDECLTYPE_UBYTE4, D3DDECLUSAGE_BLENDINDICES, 0);
2093 else if (fvf & D3DFVF_LASTBETA_D3DCOLOR)
2094 append_decl_element(declaration, &idx, &offset, D3DDECLTYPE_D3DCOLOR, D3DDECLUSAGE_BLENDINDICES, 0);
2099 if (fvf & D3DFVF_NORMAL)
2100 append_decl_element(declaration, &idx, &offset, D3DDECLTYPE_FLOAT3, D3DDECLUSAGE_NORMAL, 0);
2101 if (fvf & D3DFVF_PSIZE)
2102 append_decl_element(declaration, &idx, &offset, D3DDECLTYPE_FLOAT1, D3DDECLUSAGE_PSIZE, 0);
2103 if (fvf & D3DFVF_DIFFUSE)
2104 append_decl_element(declaration, &idx, &offset, D3DDECLTYPE_D3DCOLOR, D3DDECLUSAGE_COLOR, 0);
2105 if (fvf & D3DFVF_SPECULAR)
2106 append_decl_element(declaration, &idx, &offset, D3DDECLTYPE_D3DCOLOR, D3DDECLUSAGE_COLOR, 1);
2108 for (i = 0; i < tex_count; ++i)
2110 switch ((fvf >> (16 + 2 * i)) & 0x03)
2112 case D3DFVF_TEXTUREFORMAT1:
2113 append_decl_element(declaration, &idx, &offset, D3DDECLTYPE_FLOAT1, D3DDECLUSAGE_TEXCOORD, i);
2115 case D3DFVF_TEXTUREFORMAT2:
2116 append_decl_element(declaration, &idx, &offset, D3DDECLTYPE_FLOAT2, D3DDECLUSAGE_TEXCOORD, i);
2118 case D3DFVF_TEXTUREFORMAT3:
2119 append_decl_element(declaration, &idx, &offset, D3DDECLTYPE_FLOAT3, D3DDECLUSAGE_TEXCOORD, i);
2121 case D3DFVF_TEXTUREFORMAT4:
2122 append_decl_element(declaration, &idx, &offset, D3DDECLTYPE_FLOAT4, D3DDECLUSAGE_TEXCOORD, i);
2127 declaration[idx] = end_element;
2132 /*************************************************************************
2133 * D3DXFVFFromDeclarator
2135 HRESULT WINAPI D3DXFVFFromDeclarator(const D3DVERTEXELEMENT9 *declaration, DWORD *fvf)
2137 unsigned int i = 0, texture, offset;
2139 TRACE("(%p, %p)\n", declaration, fvf);
2142 if (declaration[0].Type == D3DDECLTYPE_FLOAT3 && declaration[0].Usage == D3DDECLUSAGE_POSITION)
2144 if ((declaration[1].Type == D3DDECLTYPE_FLOAT4 && declaration[1].Usage == D3DDECLUSAGE_BLENDWEIGHT &&
2145 declaration[1].UsageIndex == 0) &&
2146 (declaration[2].Type == D3DDECLTYPE_FLOAT1 && declaration[2].Usage == D3DDECLUSAGE_BLENDINDICES &&
2147 declaration[2].UsageIndex == 0))
2149 return D3DERR_INVALIDCALL;
2151 else if ((declaration[1].Type == D3DDECLTYPE_UBYTE4 || declaration[1].Type == D3DDECLTYPE_D3DCOLOR) &&
2152 declaration[1].Usage == D3DDECLUSAGE_BLENDINDICES && declaration[1].UsageIndex == 0)
2154 if (declaration[1].Type == D3DDECLTYPE_UBYTE4)
2156 *fvf |= D3DFVF_XYZB1 | D3DFVF_LASTBETA_UBYTE4;
2160 *fvf |= D3DFVF_XYZB1 | D3DFVF_LASTBETA_D3DCOLOR;
2164 else if (declaration[1].Type <= D3DDECLTYPE_FLOAT4 && declaration[1].Usage == D3DDECLUSAGE_BLENDWEIGHT &&
2165 declaration[1].UsageIndex == 0)
2167 if ((declaration[2].Type == D3DDECLTYPE_UBYTE4 || declaration[2].Type == D3DDECLTYPE_D3DCOLOR) &&
2168 declaration[2].Usage == D3DDECLUSAGE_BLENDINDICES && declaration[2].UsageIndex == 0)
2170 if (declaration[2].Type == D3DDECLTYPE_UBYTE4)
2172 *fvf |= D3DFVF_LASTBETA_UBYTE4;
2176 *fvf |= D3DFVF_LASTBETA_D3DCOLOR;
2178 switch (declaration[1].Type)
2180 case D3DDECLTYPE_FLOAT1: *fvf |= D3DFVF_XYZB2; break;
2181 case D3DDECLTYPE_FLOAT2: *fvf |= D3DFVF_XYZB3; break;
2182 case D3DDECLTYPE_FLOAT3: *fvf |= D3DFVF_XYZB4; break;
2183 case D3DDECLTYPE_FLOAT4: *fvf |= D3DFVF_XYZB5; break;
2189 switch (declaration[1].Type)
2191 case D3DDECLTYPE_FLOAT1: *fvf |= D3DFVF_XYZB1; break;
2192 case D3DDECLTYPE_FLOAT2: *fvf |= D3DFVF_XYZB2; break;
2193 case D3DDECLTYPE_FLOAT3: *fvf |= D3DFVF_XYZB3; break;
2194 case D3DDECLTYPE_FLOAT4: *fvf |= D3DFVF_XYZB4; break;
2205 else if (declaration[0].Type == D3DDECLTYPE_FLOAT4 && declaration[0].Usage == D3DDECLUSAGE_POSITIONT &&
2206 declaration[0].UsageIndex == 0)
2208 *fvf |= D3DFVF_XYZRHW;
2212 if (declaration[i].Type == D3DDECLTYPE_FLOAT3 && declaration[i].Usage == D3DDECLUSAGE_NORMAL)
2214 *fvf |= D3DFVF_NORMAL;
2217 if (declaration[i].Type == D3DDECLTYPE_FLOAT1 && declaration[i].Usage == D3DDECLUSAGE_PSIZE &&
2218 declaration[i].UsageIndex == 0)
2220 *fvf |= D3DFVF_PSIZE;
2223 if (declaration[i].Type == D3DDECLTYPE_D3DCOLOR && declaration[i].Usage == D3DDECLUSAGE_COLOR &&
2224 declaration[i].UsageIndex == 0)
2226 *fvf |= D3DFVF_DIFFUSE;
2229 if (declaration[i].Type == D3DDECLTYPE_D3DCOLOR && declaration[i].Usage == D3DDECLUSAGE_COLOR &&
2230 declaration[i].UsageIndex == 1)
2232 *fvf |= D3DFVF_SPECULAR;
2236 for (texture = 0; texture < D3DDP_MAXTEXCOORD; i++, texture++)
2238 if (declaration[i].Stream == 0xFF)
2242 else if (declaration[i].Type == D3DDECLTYPE_FLOAT1 && declaration[i].Usage == D3DDECLUSAGE_TEXCOORD &&
2243 declaration[i].UsageIndex == texture)
2245 *fvf |= D3DFVF_TEXCOORDSIZE1(declaration[i].UsageIndex);
2247 else if (declaration[i].Type == D3DDECLTYPE_FLOAT2 && declaration[i].Usage == D3DDECLUSAGE_TEXCOORD &&
2248 declaration[i].UsageIndex == texture)
2250 *fvf |= D3DFVF_TEXCOORDSIZE2(declaration[i].UsageIndex);
2252 else if (declaration[i].Type == D3DDECLTYPE_FLOAT3 && declaration[i].Usage == D3DDECLUSAGE_TEXCOORD &&
2253 declaration[i].UsageIndex == texture)
2255 *fvf |= D3DFVF_TEXCOORDSIZE3(declaration[i].UsageIndex);
2257 else if (declaration[i].Type == D3DDECLTYPE_FLOAT4 && declaration[i].Usage == D3DDECLUSAGE_TEXCOORD &&
2258 declaration[i].UsageIndex == texture)
2260 *fvf |= D3DFVF_TEXCOORDSIZE4(declaration[i].UsageIndex);
2264 return D3DERR_INVALIDCALL;
2268 *fvf |= (texture << D3DFVF_TEXCOUNT_SHIFT);
2270 for (offset = 0, i = 0; declaration[i].Stream != 0xFF;
2271 offset += d3dx_decltype_size[declaration[i].Type], i++)
2273 if (declaration[i].Offset != offset)
2275 return D3DERR_INVALIDCALL;
2282 /*************************************************************************
2283 * D3DXGetFVFVertexSize
2285 static UINT Get_TexCoord_Size_From_FVF(DWORD FVF, int tex_num)
2287 return (((((FVF) >> (16 + (2 * (tex_num)))) + 1) & 0x03) + 1);
2290 UINT WINAPI D3DXGetFVFVertexSize(DWORD FVF)
2294 UINT numTextures = (FVF & D3DFVF_TEXCOUNT_MASK) >> D3DFVF_TEXCOUNT_SHIFT;
2296 if (FVF & D3DFVF_NORMAL) size += sizeof(D3DXVECTOR3);
2297 if (FVF & D3DFVF_DIFFUSE) size += sizeof(DWORD);
2298 if (FVF & D3DFVF_SPECULAR) size += sizeof(DWORD);
2299 if (FVF & D3DFVF_PSIZE) size += sizeof(DWORD);
2301 switch (FVF & D3DFVF_POSITION_MASK)
2303 case D3DFVF_XYZ: size += sizeof(D3DXVECTOR3); break;
2304 case D3DFVF_XYZRHW: size += 4 * sizeof(FLOAT); break;
2305 case D3DFVF_XYZB1: size += 4 * sizeof(FLOAT); break;
2306 case D3DFVF_XYZB2: size += 5 * sizeof(FLOAT); break;
2307 case D3DFVF_XYZB3: size += 6 * sizeof(FLOAT); break;
2308 case D3DFVF_XYZB4: size += 7 * sizeof(FLOAT); break;
2309 case D3DFVF_XYZB5: size += 8 * sizeof(FLOAT); break;
2310 case D3DFVF_XYZW: size += 4 * sizeof(FLOAT); break;
2313 for (i = 0; i < numTextures; i++)
2315 size += Get_TexCoord_Size_From_FVF(FVF, i) * sizeof(FLOAT);
2321 /*************************************************************************
2322 * D3DXGetDeclVertexSize
2324 UINT WINAPI D3DXGetDeclVertexSize(const D3DVERTEXELEMENT9 *decl, DWORD stream_idx)
2326 const D3DVERTEXELEMENT9 *element;
2329 TRACE("decl %p, stream_idx %u\n", decl, stream_idx);
2331 if (!decl) return 0;
2333 for (element = decl; element->Stream != 0xff; ++element)
2337 if (element->Stream != stream_idx) continue;
2339 if (element->Type >= sizeof(d3dx_decltype_size) / sizeof(*d3dx_decltype_size))
2341 FIXME("Unhandled element type %#x, size will be incorrect.\n", element->Type);
2345 type_size = d3dx_decltype_size[element->Type];
2346 if (element->Offset + type_size > size) size = element->Offset + type_size;
2352 /*************************************************************************
2355 UINT WINAPI D3DXGetDeclLength(const D3DVERTEXELEMENT9 *decl)
2357 const D3DVERTEXELEMENT9 *element;
2359 TRACE("decl %p\n", decl);
2361 /* null decl results in exception on Windows XP */
2363 for (element = decl; element->Stream != 0xff; ++element);
2365 return element - decl;
2368 /*************************************************************************
2371 BOOL WINAPI D3DXIntersectTri(CONST D3DXVECTOR3 *p0, CONST D3DXVECTOR3 *p1, CONST D3DXVECTOR3 *p2, CONST D3DXVECTOR3 *praypos, CONST D3DXVECTOR3 *praydir, FLOAT *pu, FLOAT *pv, FLOAT *pdist)
2376 m.u.m[0][0] = p1->x - p0->x;
2377 m.u.m[1][0] = p2->x - p0->x;
2378 m.u.m[2][0] = -praydir->x;
2380 m.u.m[0][1] = p1->y - p0->z;
2381 m.u.m[1][1] = p2->y - p0->z;
2382 m.u.m[2][1] = -praydir->y;
2384 m.u.m[0][2] = p1->z - p0->z;
2385 m.u.m[1][2] = p2->z - p0->z;
2386 m.u.m[2][2] = -praydir->z;
2393 vec.x = praypos->x - p0->x;
2394 vec.y = praypos->y - p0->y;
2395 vec.z = praypos->z - p0->z;
2398 if ( D3DXMatrixInverse(&m, NULL, &m) )
2400 D3DXVec4Transform(&vec, &vec, &m);
2401 if ( (vec.x >= 0.0f) && (vec.y >= 0.0f) && (vec.x + vec.y <= 1.0f) && (vec.z >= 0.0f) )
2405 *pdist = fabs( vec.z );
2413 /*************************************************************************
2414 * D3DXSphereBoundProbe
2416 BOOL WINAPI D3DXSphereBoundProbe(CONST D3DXVECTOR3 *pcenter, FLOAT radius, CONST D3DXVECTOR3 *prayposition, CONST D3DXVECTOR3 *praydirection)
2418 D3DXVECTOR3 difference;
2421 a = D3DXVec3LengthSq(praydirection);
2422 if (!D3DXVec3Subtract(&difference, prayposition, pcenter)) return FALSE;
2423 b = D3DXVec3Dot(&difference, praydirection);
2424 c = D3DXVec3LengthSq(&difference) - radius * radius;
2427 if ( ( d <= 0.0f ) || ( sqrt(d) <= b ) ) return FALSE;
2431 /*************************************************************************
2434 HRESULT WINAPI D3DXCreateMesh(DWORD numfaces, DWORD numvertices, DWORD options, CONST D3DVERTEXELEMENT9 *declaration,
2435 LPDIRECT3DDEVICE9 device, LPD3DXMESH *mesh)
2439 IDirect3DVertexDeclaration9 *vertex_declaration;
2440 UINT vertex_declaration_size;
2442 IDirect3DVertexBuffer9 *vertex_buffer;
2443 IDirect3DIndexBuffer9 *index_buffer;
2444 DWORD *attrib_buffer;
2445 ID3DXMeshImpl *object;
2446 DWORD index_usage = 0;
2447 D3DPOOL index_pool = D3DPOOL_DEFAULT;
2448 D3DFORMAT index_format = D3DFMT_INDEX16;
2449 DWORD vertex_usage = 0;
2450 D3DPOOL vertex_pool = D3DPOOL_DEFAULT;
2453 TRACE("(%d, %d, %x, %p, %p, %p)\n", numfaces, numvertices, options, declaration, device, mesh);
2455 if (numfaces == 0 || numvertices == 0 || declaration == NULL || device == NULL || mesh == NULL ||
2456 /* D3DXMESH_VB_SHARE is for cloning, and D3DXMESH_USEHWONLY is for ConvertToBlendedMesh */
2457 (options & (D3DXMESH_VB_SHARE | D3DXMESH_USEHWONLY | 0xfffe0000)))
2459 return D3DERR_INVALIDCALL;
2461 for (i = 0; declaration[i].Stream != 0xff; i++)
2462 if (declaration[i].Stream != 0)
2463 return D3DERR_INVALIDCALL;
2466 if (options & D3DXMESH_32BIT)
2467 index_format = D3DFMT_INDEX32;
2469 if (options & D3DXMESH_DONOTCLIP) {
2470 index_usage |= D3DUSAGE_DONOTCLIP;
2471 vertex_usage |= D3DUSAGE_DONOTCLIP;
2473 if (options & D3DXMESH_POINTS) {
2474 index_usage |= D3DUSAGE_POINTS;
2475 vertex_usage |= D3DUSAGE_POINTS;
2477 if (options & D3DXMESH_RTPATCHES) {
2478 index_usage |= D3DUSAGE_RTPATCHES;
2479 vertex_usage |= D3DUSAGE_RTPATCHES;
2481 if (options & D3DXMESH_NPATCHES) {
2482 index_usage |= D3DUSAGE_NPATCHES;
2483 vertex_usage |= D3DUSAGE_NPATCHES;
2486 if (options & D3DXMESH_VB_SYSTEMMEM)
2487 vertex_pool = D3DPOOL_SYSTEMMEM;
2488 else if (options & D3DXMESH_VB_MANAGED)
2489 vertex_pool = D3DPOOL_MANAGED;
2491 if (options & D3DXMESH_VB_WRITEONLY)
2492 vertex_usage |= D3DUSAGE_WRITEONLY;
2493 if (options & D3DXMESH_VB_DYNAMIC)
2494 vertex_usage |= D3DUSAGE_DYNAMIC;
2495 if (options & D3DXMESH_VB_SOFTWAREPROCESSING)
2496 vertex_usage |= D3DUSAGE_SOFTWAREPROCESSING;
2498 if (options & D3DXMESH_IB_SYSTEMMEM)
2499 index_pool = D3DPOOL_SYSTEMMEM;
2500 else if (options & D3DXMESH_IB_MANAGED)
2501 index_pool = D3DPOOL_MANAGED;
2503 if (options & D3DXMESH_IB_WRITEONLY)
2504 index_usage |= D3DUSAGE_WRITEONLY;
2505 if (options & D3DXMESH_IB_DYNAMIC)
2506 index_usage |= D3DUSAGE_DYNAMIC;
2507 if (options & D3DXMESH_IB_SOFTWAREPROCESSING)
2508 index_usage |= D3DUSAGE_SOFTWAREPROCESSING;
2510 hr = D3DXFVFFromDeclarator(declaration, &fvf);
2516 /* Create vertex declaration */
2517 hr = IDirect3DDevice9_CreateVertexDeclaration(device,
2519 &vertex_declaration);
2522 WARN("Unexpected return value %x from IDirect3DDevice9_CreateVertexDeclaration.\n",hr);
2525 vertex_declaration_size = D3DXGetDeclVertexSize(declaration, declaration[0].Stream);
2527 /* Create vertex buffer */
2528 hr = IDirect3DDevice9_CreateVertexBuffer(device,
2529 numvertices * vertex_declaration_size,
2537 WARN("Unexpected return value %x from IDirect3DDevice9_CreateVertexBuffer.\n",hr);
2538 IDirect3DVertexDeclaration9_Release(vertex_declaration);
2542 /* Create index buffer */
2543 hr = IDirect3DDevice9_CreateIndexBuffer(device,
2544 numfaces * 3 * ((index_format == D3DFMT_INDEX16) ? 2 : 4),
2552 WARN("Unexpected return value %x from IDirect3DDevice9_CreateVertexBuffer.\n",hr);
2553 IDirect3DVertexBuffer9_Release(vertex_buffer);
2554 IDirect3DVertexDeclaration9_Release(vertex_declaration);
2558 attrib_buffer = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, numfaces * sizeof(*attrib_buffer));
2559 object = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(ID3DXMeshImpl));
2560 if (object == NULL || attrib_buffer == NULL)
2562 HeapFree(GetProcessHeap(), 0, attrib_buffer);
2563 IDirect3DIndexBuffer9_Release(index_buffer);
2564 IDirect3DVertexBuffer9_Release(vertex_buffer);
2565 IDirect3DVertexDeclaration9_Release(vertex_declaration);
2567 return E_OUTOFMEMORY;
2569 object->ID3DXMesh_iface.lpVtbl = &D3DXMesh_Vtbl;
2572 object->numfaces = numfaces;
2573 object->numvertices = numvertices;
2574 object->options = options;
2576 object->device = device;
2577 IDirect3DDevice9_AddRef(device);
2579 copy_declaration(object->cached_declaration, declaration, num_elem);
2580 object->vertex_declaration = vertex_declaration;
2581 object->vertex_declaration_size = vertex_declaration_size;
2582 object->num_elem = num_elem;
2583 object->vertex_buffer = vertex_buffer;
2584 object->index_buffer = index_buffer;
2585 object->attrib_buffer = attrib_buffer;
2587 *mesh = &object->ID3DXMesh_iface;
2592 /*************************************************************************
2595 HRESULT WINAPI D3DXCreateMeshFVF(DWORD numfaces, DWORD numvertices, DWORD options, DWORD fvf,
2596 LPDIRECT3DDEVICE9 device, LPD3DXMESH *mesh)
2599 D3DVERTEXELEMENT9 declaration[MAX_FVF_DECL_SIZE];
2601 TRACE("(%u, %u, %u, %u, %p, %p)\n", numfaces, numvertices, options, fvf, device, mesh);
2603 hr = D3DXDeclaratorFromFVF(fvf, declaration);
2604 if (FAILED(hr)) return hr;
2606 return D3DXCreateMesh(numfaces, numvertices, options, declaration, device, mesh);
2612 DWORD num_poly_faces;
2613 DWORD num_tri_faces;
2614 D3DXVECTOR3 *vertices;
2615 DWORD *num_tri_per_face;
2620 /* optional mesh data */
2623 D3DXVECTOR3 *normals;
2624 DWORD *normal_indices;
2626 D3DXVECTOR2 *tex_coords;
2628 DWORD *vertex_colors;
2630 DWORD num_materials;
2631 D3DXMATERIAL *materials;
2632 DWORD *material_indices;
2635 static HRESULT get_next_child(IDirectXFileData *filedata, IDirectXFileData **child, const GUID **type)
2638 IDirectXFileDataReference *child_ref = NULL;
2639 IDirectXFileObject *child_obj = NULL;
2640 IDirectXFileData *child_data = NULL;
2642 hr = IDirectXFileData_GetNextObject(filedata, &child_obj);
2643 if (FAILED(hr)) return hr;
2645 hr = IDirectXFileObject_QueryInterface(child_obj, &IID_IDirectXFileDataReference, (void**)&child_ref);
2646 if (SUCCEEDED(hr)) {
2647 hr = IDirectXFileDataReference_Resolve(child_ref, &child_data);
2648 IDirectXFileDataReference_Release(child_ref);
2650 hr = IDirectXFileObject_QueryInterface(child_obj, &IID_IDirectXFileData, (void**)&child_data);
2652 IDirectXFileObject_Release(child_obj);
2656 hr = IDirectXFileData_GetType(child_data, type);
2658 IDirectXFileData_Release(child_data);
2660 *child = child_data;
2666 static HRESULT parse_texture_filename(IDirectXFileData *filedata, LPSTR *filename_out)
2672 char *filename = NULL;
2674 /* template TextureFilename {
2679 HeapFree(GetProcessHeap(), 0, *filename_out);
2680 *filename_out = NULL;
2682 hr = IDirectXFileData_GetData(filedata, NULL, &data_size, (void**)&data);
2683 if (FAILED(hr)) return hr;
2685 if (data_size < sizeof(LPSTR)) {
2686 WARN("truncated data (%u bytes)\n", data_size);
2689 filename_in = *(LPSTR*)data;
2691 filename = HeapAlloc(GetProcessHeap(), 0, strlen(filename_in) + 1);
2692 if (!filename) return E_OUTOFMEMORY;
2694 strcpy(filename, filename_in);
2695 *filename_out = filename;
2700 static HRESULT parse_material(IDirectXFileData *filedata, D3DXMATERIAL *material)
2706 IDirectXFileData *child;
2708 material->pTextureFilename = NULL;
2710 hr = IDirectXFileData_GetData(filedata, NULL, &data_size, (void**)&data);
2711 if (FAILED(hr)) return hr;
2714 * template ColorRGBA {
2720 * template ColorRGB {
2725 * template Material {
2726 * ColorRGBA faceColor;
2728 * ColorRGB specularColor;
2729 * ColorRGB emissiveColor;
2733 if (data_size != sizeof(FLOAT) * 11) {
2734 WARN("incorrect data size (%u bytes)\n", data_size);
2738 memcpy(&material->MatD3D.Diffuse, data, sizeof(D3DCOLORVALUE));
2739 data += sizeof(D3DCOLORVALUE);
2740 material->MatD3D.Power = *(FLOAT*)data;
2741 data += sizeof(FLOAT);
2742 memcpy(&material->MatD3D.Specular, data, sizeof(FLOAT) * 3);
2743 material->MatD3D.Specular.a = 1.0f;
2744 data += 3 * sizeof(FLOAT);
2745 memcpy(&material->MatD3D.Emissive, data, sizeof(FLOAT) * 3);
2746 material->MatD3D.Emissive.a = 1.0f;
2747 material->MatD3D.Ambient.r = 0.0f;
2748 material->MatD3D.Ambient.g = 0.0f;
2749 material->MatD3D.Ambient.b = 0.0f;
2750 material->MatD3D.Ambient.a = 1.0f;
2752 while (SUCCEEDED(hr = get_next_child(filedata, &child, &type)))
2754 if (IsEqualGUID(type, &TID_D3DRMTextureFilename)) {
2755 hr = parse_texture_filename(child, &material->pTextureFilename);
2756 if (FAILED(hr)) break;
2759 return hr == DXFILEERR_NOMOREOBJECTS ? D3D_OK : hr;
2762 static void destroy_materials(struct mesh_data *mesh)
2765 for (i = 0; i < mesh->num_materials; i++)
2766 HeapFree(GetProcessHeap(), 0, mesh->materials[i].pTextureFilename);
2767 HeapFree(GetProcessHeap(), 0, mesh->materials);
2768 HeapFree(GetProcessHeap(), 0, mesh->material_indices);
2769 mesh->num_materials = 0;
2770 mesh->materials = NULL;
2771 mesh->material_indices = NULL;
2774 static HRESULT parse_material_list(IDirectXFileData *filedata, struct mesh_data *mesh)
2778 DWORD *data, *in_ptr;
2780 IDirectXFileData *child;
2781 DWORD num_materials;
2784 destroy_materials(mesh);
2786 hr = IDirectXFileData_GetData(filedata, NULL, &data_size, (void**)&data);
2787 if (FAILED(hr)) return hr;
2789 /* template MeshMaterialList {
2791 * DWORD nFaceIndexes;
2792 * array DWORD faceIndexes[nFaceIndexes];
2799 if (data_size < sizeof(DWORD))
2800 goto truncated_data_error;
2801 num_materials = *in_ptr++;
2805 if (data_size < 2 * sizeof(DWORD))
2806 goto truncated_data_error;
2807 if (*in_ptr++ != mesh->num_poly_faces) {
2808 WARN("number of material face indices (%u) doesn't match number of faces (%u)\n",
2809 *(in_ptr - 1), mesh->num_poly_faces);
2812 if (data_size < 2 * sizeof(DWORD) + mesh->num_poly_faces * sizeof(DWORD))
2813 goto truncated_data_error;
2814 for (i = 0; i < mesh->num_poly_faces; i++) {
2815 if (*in_ptr++ >= num_materials) {
2816 WARN("face %u: reference to undefined material %u (only %u materials)\n",
2817 i, *(in_ptr - 1), num_materials);
2822 mesh->materials = HeapAlloc(GetProcessHeap(), 0, num_materials * sizeof(*mesh->materials));
2823 mesh->material_indices = HeapAlloc(GetProcessHeap(), 0, mesh->num_poly_faces * sizeof(*mesh->material_indices));
2824 if (!mesh->materials || !mesh->material_indices)
2825 return E_OUTOFMEMORY;
2826 memcpy(mesh->material_indices, data + 2, mesh->num_poly_faces * sizeof(DWORD));
2828 while (SUCCEEDED(hr = get_next_child(filedata, &child, &type)))
2830 if (IsEqualGUID(type, &TID_D3DRMMaterial)) {
2831 if (mesh->num_materials >= num_materials) {
2832 WARN("more materials defined than declared\n");
2835 hr = parse_material(child, &mesh->materials[mesh->num_materials++]);
2836 if (FAILED(hr)) break;
2839 if (hr != DXFILEERR_NOMOREOBJECTS)
2841 if (num_materials != mesh->num_materials) {
2842 WARN("only %u of %u materials defined\n", num_materials, mesh->num_materials);
2847 truncated_data_error:
2848 WARN("truncated data (%u bytes)\n", data_size);
2852 static HRESULT parse_texture_coords(IDirectXFileData *filedata, struct mesh_data *mesh)
2858 HeapFree(GetProcessHeap(), 0, mesh->tex_coords);
2859 mesh->tex_coords = NULL;
2861 hr = IDirectXFileData_GetData(filedata, NULL, &data_size, (void**)&data);
2862 if (FAILED(hr)) return hr;
2864 /* template Coords2d {
2868 * template MeshTextureCoords {
2869 * DWORD nTextureCoords;
2870 * array Coords2d textureCoords[nTextureCoords];
2874 if (data_size < sizeof(DWORD))
2875 goto truncated_data_error;
2876 if (*(DWORD*)data != mesh->num_vertices) {
2877 WARN("number of texture coordinates (%u) doesn't match number of vertices (%u)\n",
2878 *(DWORD*)data, mesh->num_vertices);
2881 data += sizeof(DWORD);
2882 if (data_size < sizeof(DWORD) + mesh->num_vertices * sizeof(*mesh->tex_coords))
2883 goto truncated_data_error;
2885 mesh->tex_coords = HeapAlloc(GetProcessHeap(), 0, mesh->num_vertices * sizeof(*mesh->tex_coords));
2886 if (!mesh->tex_coords) return E_OUTOFMEMORY;
2887 memcpy(mesh->tex_coords, data, mesh->num_vertices * sizeof(*mesh->tex_coords));
2889 mesh->fvf |= D3DFVF_TEX1;
2892 truncated_data_error:
2893 WARN("truncated data (%u bytes)\n", data_size);
2897 static HRESULT parse_vertex_colors(IDirectXFileData *filedata, struct mesh_data *mesh)
2905 HeapFree(GetProcessHeap(), 0, mesh->vertex_colors);
2906 mesh->vertex_colors = NULL;
2908 hr = IDirectXFileData_GetData(filedata, NULL, &data_size, (void**)&data);
2909 if (FAILED(hr)) return hr;
2911 /* template IndexedColor {
2913 * ColorRGBA indexColor;
2915 * template MeshVertexColors {
2916 * DWORD nVertexColors;
2917 * array IndexedColor vertexColors[nVertexColors];
2921 if (data_size < sizeof(DWORD))
2922 goto truncated_data_error;
2923 num_colors = *(DWORD*)data;
2924 data += sizeof(DWORD);
2925 if (data_size < sizeof(DWORD) + num_colors * (sizeof(DWORD) + sizeof(D3DCOLORVALUE)))
2926 goto truncated_data_error;
2928 mesh->vertex_colors = HeapAlloc(GetProcessHeap(), 0, mesh->num_vertices * sizeof(DWORD));
2929 if (!mesh->vertex_colors)
2930 return E_OUTOFMEMORY;
2932 for (i = 0; i < mesh->num_vertices; i++)
2933 mesh->vertex_colors[i] = D3DCOLOR_ARGB(0, 0xff, 0xff, 0xff);
2934 for (i = 0; i < num_colors; i++)
2936 D3DCOLORVALUE color;
2937 DWORD index = *(DWORD*)data;
2938 data += sizeof(DWORD);
2939 if (index >= mesh->num_vertices) {
2940 WARN("vertex color %u references undefined vertex %u (only %u vertices)\n",
2941 i, index, mesh->num_vertices);
2944 memcpy(&color, data, sizeof(color));
2945 data += sizeof(color);
2946 color.r = min(1.0f, max(0.0f, color.r));
2947 color.g = min(1.0f, max(0.0f, color.g));
2948 color.b = min(1.0f, max(0.0f, color.b));
2949 color.a = min(1.0f, max(0.0f, color.a));
2950 mesh->vertex_colors[index] = D3DCOLOR_ARGB((BYTE)(color.a * 255.0f + 0.5f),
2951 (BYTE)(color.r * 255.0f + 0.5f),
2952 (BYTE)(color.g * 255.0f + 0.5f),
2953 (BYTE)(color.b * 255.0f + 0.5f));
2956 mesh->fvf |= D3DFVF_DIFFUSE;
2959 truncated_data_error:
2960 WARN("truncated data (%u bytes)\n", data_size);
2964 static HRESULT parse_normals(IDirectXFileData *filedata, struct mesh_data *mesh)
2969 DWORD *index_out_ptr;
2971 DWORD num_face_indices = mesh->num_poly_faces * 2 + mesh->num_tri_faces;
2973 HeapFree(GetProcessHeap(), 0, mesh->normals);
2974 mesh->num_normals = 0;
2975 mesh->normals = NULL;
2976 mesh->normal_indices = NULL;
2977 mesh->fvf |= D3DFVF_NORMAL;
2979 hr = IDirectXFileData_GetData(filedata, NULL, &data_size, (void**)&data);
2980 if (FAILED(hr)) return hr;
2982 /* template Vector {
2987 * template MeshFace {
2988 * DWORD nFaceVertexIndices;
2989 * array DWORD faceVertexIndices[nFaceVertexIndices];
2991 * template MeshNormals {
2993 * array Vector normals[nNormals];
2994 * DWORD nFaceNormals;
2995 * array MeshFace faceNormals[nFaceNormals];
2999 if (data_size < sizeof(DWORD) * 2)
3000 goto truncated_data_error;
3001 mesh->num_normals = *(DWORD*)data;
3002 data += sizeof(DWORD);
3003 if (data_size < sizeof(DWORD) * 2 + mesh->num_normals * sizeof(D3DXVECTOR3) +
3004 num_face_indices * sizeof(DWORD))
3005 goto truncated_data_error;
3007 mesh->normals = HeapAlloc(GetProcessHeap(), 0, mesh->num_normals * sizeof(D3DXVECTOR3));
3008 mesh->normal_indices = HeapAlloc(GetProcessHeap(), 0, num_face_indices * sizeof(DWORD));
3009 if (!mesh->normals || !mesh->normal_indices)
3010 return E_OUTOFMEMORY;
3012 memcpy(mesh->normals, data, mesh->num_normals * sizeof(D3DXVECTOR3));
3013 data += mesh->num_normals * sizeof(D3DXVECTOR3);
3014 for (i = 0; i < mesh->num_normals; i++)
3015 D3DXVec3Normalize(&mesh->normals[i], &mesh->normals[i]);
3017 if (*(DWORD*)data != mesh->num_poly_faces) {
3018 WARN("number of face normals (%u) doesn't match number of faces (%u)\n",
3019 *(DWORD*)data, mesh->num_poly_faces);
3022 data += sizeof(DWORD);
3023 index_out_ptr = mesh->normal_indices;
3024 for (i = 0; i < mesh->num_poly_faces; i++)
3027 DWORD count = *(DWORD*)data;
3028 if (count != mesh->num_tri_per_face[i] + 2) {
3029 WARN("face %u: number of normals (%u) doesn't match number of vertices (%u)\n",
3030 i, count, mesh->num_tri_per_face[i] + 2);
3033 data += sizeof(DWORD);
3035 for (j = 0; j < count; j++) {
3036 DWORD normal_index = *(DWORD*)data;
3037 if (normal_index >= mesh->num_normals) {
3038 WARN("face %u, normal index %u: reference to undefined normal %u (only %u normals)\n",
3039 i, j, normal_index, mesh->num_normals);
3042 *index_out_ptr++ = normal_index;
3043 data += sizeof(DWORD);
3048 truncated_data_error:
3049 WARN("truncated data (%u bytes)\n", data_size);
3053 /* for provide_flags parameters */
3054 #define PROVIDE_MATERIALS 0x1
3055 #define PROVIDE_SKININFO 0x2
3056 #define PROVIDE_ADJACENCY 0x4
3058 static HRESULT parse_mesh(IDirectXFileData *filedata, struct mesh_data *mesh_data, DWORD provide_flags)
3062 BYTE *data, *in_ptr;
3063 DWORD *index_out_ptr;
3065 IDirectXFileData *child;
3071 * array Vector vertices[nVertices];
3073 * array MeshFace faces[nFaces];
3078 hr = IDirectXFileData_GetData(filedata, NULL, &data_size, (void**)&data);
3079 if (FAILED(hr)) return hr;
3082 if (data_size < sizeof(DWORD) * 2)
3083 goto truncated_data_error;
3084 mesh_data->num_vertices = *(DWORD*)in_ptr;
3085 if (data_size < sizeof(DWORD) * 2 + mesh_data->num_vertices * sizeof(D3DXVECTOR3))
3086 goto truncated_data_error;
3087 in_ptr += sizeof(DWORD) + mesh_data->num_vertices * sizeof(D3DXVECTOR3);
3089 mesh_data->num_poly_faces = *(DWORD*)in_ptr;
3090 in_ptr += sizeof(DWORD);
3092 mesh_data->num_tri_faces = 0;
3093 for (i = 0; i < mesh_data->num_poly_faces; i++)
3095 DWORD num_poly_vertices;
3098 if (data_size - (in_ptr - data) < sizeof(DWORD))
3099 goto truncated_data_error;
3100 num_poly_vertices = *(DWORD*)in_ptr;
3101 in_ptr += sizeof(DWORD);
3102 if (data_size - (in_ptr - data) < num_poly_vertices * sizeof(DWORD))
3103 goto truncated_data_error;
3104 if (num_poly_vertices < 3) {
3105 WARN("face %u has only %u vertices\n", i, num_poly_vertices);
3108 for (j = 0; j < num_poly_vertices; j++) {
3109 if (*(DWORD*)in_ptr >= mesh_data->num_vertices) {
3110 WARN("face %u, index %u: undefined vertex %u (only %u vertices)\n",
3111 i, j, *(DWORD*)in_ptr, mesh_data->num_vertices);
3114 in_ptr += sizeof(DWORD);
3116 mesh_data->num_tri_faces += num_poly_vertices - 2;
3119 mesh_data->fvf = D3DFVF_XYZ;
3121 mesh_data->vertices = HeapAlloc(GetProcessHeap(), 0,
3122 mesh_data->num_vertices * sizeof(*mesh_data->vertices));
3123 mesh_data->num_tri_per_face = HeapAlloc(GetProcessHeap(), 0,
3124 mesh_data->num_poly_faces * sizeof(*mesh_data->num_tri_per_face));
3125 mesh_data->indices = HeapAlloc(GetProcessHeap(), 0,
3126 (mesh_data->num_tri_faces + mesh_data->num_poly_faces * 2) * sizeof(*mesh_data->indices));
3127 if (!mesh_data->vertices || !mesh_data->num_tri_per_face || !mesh_data->indices)
3128 return E_OUTOFMEMORY;
3130 in_ptr = data + sizeof(DWORD);
3131 memcpy(mesh_data->vertices, in_ptr, mesh_data->num_vertices * sizeof(D3DXVECTOR3));
3132 in_ptr += mesh_data->num_vertices * sizeof(D3DXVECTOR3) + sizeof(DWORD);
3134 index_out_ptr = mesh_data->indices;
3135 for (i = 0; i < mesh_data->num_poly_faces; i++)
3139 count = *(DWORD*)in_ptr;
3140 in_ptr += sizeof(DWORD);
3141 mesh_data->num_tri_per_face[i] = count - 2;
3144 *index_out_ptr++ = *(DWORD*)in_ptr;
3145 in_ptr += sizeof(DWORD);
3149 while (SUCCEEDED(hr = get_next_child(filedata, &child, &type)))
3151 if (IsEqualGUID(type, &TID_D3DRMMeshNormals)) {
3152 hr = parse_normals(child, mesh_data);
3153 } else if (IsEqualGUID(type, &TID_D3DRMMeshVertexColors)) {
3154 hr = parse_vertex_colors(child, mesh_data);
3155 } else if (IsEqualGUID(type, &TID_D3DRMMeshTextureCoords)) {
3156 hr = parse_texture_coords(child, mesh_data);
3157 } else if (IsEqualGUID(type, &TID_D3DRMMeshMaterialList) &&
3158 (provide_flags & PROVIDE_MATERIALS))
3160 hr = parse_material_list(child, mesh_data);
3161 } else if (provide_flags & PROVIDE_SKININFO) {
3162 if (IsEqualGUID(type, &DXFILEOBJ_XSkinMeshHeader)) {
3163 FIXME("Skin mesh loading not implemented.\n");
3165 } else if (IsEqualGUID(type, &DXFILEOBJ_SkinWeights)) {
3166 /* ignored without XSkinMeshHeader */
3172 return hr == DXFILEERR_NOMOREOBJECTS ? D3D_OK : hr;
3173 truncated_data_error:
3174 WARN("truncated data (%u bytes)\n", data_size);
3178 static HRESULT generate_effects(ID3DXBuffer *materials, DWORD num_materials,
3179 ID3DXBuffer **effects)
3182 D3DXEFFECTINSTANCE *effect_ptr;
3184 const D3DXMATERIAL *material_ptr = ID3DXBuffer_GetBufferPointer(materials);
3185 static const struct {
3186 const char *param_name;
3190 } material_effects[] = {
3191 #define EFFECT_TABLE_ENTRY(str, field) \
3192 {str, sizeof(str), sizeof(material_ptr->MatD3D.field), offsetof(D3DXMATERIAL, MatD3D.field)}
3193 EFFECT_TABLE_ENTRY("Diffuse", Diffuse),
3194 EFFECT_TABLE_ENTRY("Power", Power),
3195 EFFECT_TABLE_ENTRY("Specular", Specular),
3196 EFFECT_TABLE_ENTRY("Emissive", Emissive),
3197 EFFECT_TABLE_ENTRY("Ambient", Ambient),
3198 #undef EFFECT_TABLE_ENTRY
3200 static const char texture_paramname[] = "Texture0@Name";
3204 /* effects buffer layout:
3206 * D3DXEFFECTINSTANCE effects[num_materials];
3207 * for (effect in effects)
3209 * D3DXEFFECTDEFAULT defaults[effect.NumDefaults];
3210 * for (default in defaults)
3212 * *default.pParamName;
3217 buffer_size = sizeof(D3DXEFFECTINSTANCE);
3218 buffer_size += sizeof(D3DXEFFECTDEFAULT) * ARRAY_SIZE(material_effects);
3219 for (i = 0; i < ARRAY_SIZE(material_effects); i++) {
3220 buffer_size += material_effects[i].name_size;
3221 buffer_size += material_effects[i].num_bytes;
3223 buffer_size *= num_materials;
3224 for (i = 0; i < num_materials; i++) {
3225 if (material_ptr[i].pTextureFilename) {
3226 buffer_size += sizeof(D3DXEFFECTDEFAULT);
3227 buffer_size += sizeof(texture_paramname);
3228 buffer_size += strlen(material_ptr[i].pTextureFilename) + 1;
3232 hr = D3DXCreateBuffer(buffer_size, effects);
3233 if (FAILED(hr)) return hr;
3234 effect_ptr = ID3DXBuffer_GetBufferPointer(*effects);
3235 out_ptr = (BYTE*)(effect_ptr + num_materials);
3237 for (i = 0; i < num_materials; i++)
3240 D3DXEFFECTDEFAULT *defaults = (D3DXEFFECTDEFAULT*)out_ptr;
3242 effect_ptr->pDefaults = defaults;
3243 effect_ptr->NumDefaults = material_ptr->pTextureFilename ? 6 : 5;
3244 out_ptr = (BYTE*)(effect_ptr->pDefaults + effect_ptr->NumDefaults);
3246 for (j = 0; j < ARRAY_SIZE(material_effects); j++)
3248 defaults->pParamName = (LPSTR)out_ptr;
3249 strcpy(defaults->pParamName, material_effects[j].param_name);
3250 defaults->pValue = defaults->pParamName + material_effects[j].name_size;
3251 defaults->Type = D3DXEDT_FLOATS;
3252 defaults->NumBytes = material_effects[j].num_bytes;
3253 memcpy(defaults->pValue, (BYTE*)material_ptr + material_effects[j].value_offset, defaults->NumBytes);
3254 out_ptr = (BYTE*)defaults->pValue + defaults->NumBytes;
3258 if (material_ptr->pTextureFilename) {
3259 defaults->pParamName = (LPSTR)out_ptr;
3260 strcpy(defaults->pParamName, texture_paramname);
3261 defaults->pValue = defaults->pParamName + sizeof(texture_paramname);
3262 defaults->Type = D3DXEDT_STRING;
3263 defaults->NumBytes = strlen(material_ptr->pTextureFilename) + 1;
3264 strcpy(defaults->pValue, material_ptr->pTextureFilename);
3265 out_ptr = (BYTE*)defaults->pValue + defaults->NumBytes;
3270 assert(out_ptr - (BYTE*)ID3DXBuffer_GetBufferPointer(*effects) == buffer_size);
3275 /* change to D3DXLoadSkinMeshFromXof when ID3DXFileData is implemented */
3276 static HRESULT load_skin_mesh_from_xof(IDirectXFileData *filedata,
3278 LPDIRECT3DDEVICE9 device,
3279 LPD3DXBUFFER *adjacency_out,
3280 LPD3DXBUFFER *materials_out,
3281 LPD3DXBUFFER *effects_out,
3282 DWORD *num_materials_out,
3283 LPD3DXSKININFO *skin_info_out,
3284 LPD3DXMESH *mesh_out)
3287 DWORD *index_in_ptr;
3288 struct mesh_data mesh_data;
3289 DWORD total_vertices;
3290 ID3DXMesh *d3dxmesh = NULL;
3291 ID3DXBuffer *adjacency = NULL;
3292 ID3DXBuffer *materials = NULL;
3293 ID3DXBuffer *effects = NULL;
3294 struct vertex_duplication {
3297 } *duplications = NULL;
3299 void *vertices = NULL;
3300 void *indices = NULL;
3302 DWORD provide_flags = 0;
3304 ZeroMemory(&mesh_data, sizeof(mesh_data));
3306 if (num_materials_out || materials_out || effects_out)
3307 provide_flags |= PROVIDE_MATERIALS;
3309 provide_flags |= PROVIDE_SKININFO;
3311 hr = parse_mesh(filedata, &mesh_data, provide_flags);
3312 if (FAILED(hr)) goto cleanup;
3314 total_vertices = mesh_data.num_vertices;
3315 if (mesh_data.fvf & D3DFVF_NORMAL) {
3316 /* duplicate vertices with multiple normals */
3317 DWORD num_face_indices = mesh_data.num_poly_faces * 2 + mesh_data.num_tri_faces;
3318 duplications = HeapAlloc(GetProcessHeap(), 0, (mesh_data.num_vertices + num_face_indices) * sizeof(*duplications));
3319 if (!duplications) {
3323 for (i = 0; i < total_vertices; i++)
3325 duplications[i].normal_index = -1;
3326 list_init(&duplications[i].entry);
3328 for (i = 0; i < num_face_indices; i++) {
3329 DWORD vertex_index = mesh_data.indices[i];
3330 DWORD normal_index = mesh_data.normal_indices[i];
3331 struct vertex_duplication *dup_ptr = &duplications[vertex_index];
3333 if (dup_ptr->normal_index == -1) {
3334 dup_ptr->normal_index = normal_index;
3336 D3DXVECTOR3 *new_normal = &mesh_data.normals[normal_index];
3337 struct list *dup_list = &dup_ptr->entry;
3339 D3DXVECTOR3 *cur_normal = &mesh_data.normals[dup_ptr->normal_index];
3340 if (new_normal->x == cur_normal->x &&
3341 new_normal->y == cur_normal->y &&
3342 new_normal->z == cur_normal->z)
3344 mesh_data.indices[i] = dup_ptr - duplications;
3346 } else if (!list_next(dup_list, &dup_ptr->entry)) {
3347 dup_ptr = &duplications[total_vertices++];
3348 dup_ptr->normal_index = normal_index;
3349 list_add_tail(dup_list, &dup_ptr->entry);
3350 mesh_data.indices[i] = dup_ptr - duplications;
3353 dup_ptr = LIST_ENTRY(list_next(dup_list, &dup_ptr->entry),
3354 struct vertex_duplication, entry);
3361 hr = D3DXCreateMeshFVF(mesh_data.num_tri_faces, total_vertices, options, mesh_data.fvf, device, &d3dxmesh);
3362 if (FAILED(hr)) goto cleanup;
3364 hr = d3dxmesh->lpVtbl->LockVertexBuffer(d3dxmesh, 0, &vertices);
3365 if (FAILED(hr)) goto cleanup;
3368 for (i = 0; i < mesh_data.num_vertices; i++) {
3369 *(D3DXVECTOR3*)out_ptr = mesh_data.vertices[i];
3370 out_ptr += sizeof(D3DXVECTOR3);
3371 if (mesh_data.fvf & D3DFVF_NORMAL) {
3372 if (duplications[i].normal_index == -1)
3373 ZeroMemory(out_ptr, sizeof(D3DXVECTOR3));
3375 *(D3DXVECTOR3*)out_ptr = mesh_data.normals[duplications[i].normal_index];
3376 out_ptr += sizeof(D3DXVECTOR3);
3378 if (mesh_data.fvf & D3DFVF_DIFFUSE) {
3379 *(DWORD*)out_ptr = mesh_data.vertex_colors[i];
3380 out_ptr += sizeof(DWORD);
3382 if (mesh_data.fvf & D3DFVF_TEX1) {
3383 *(D3DXVECTOR2*)out_ptr = mesh_data.tex_coords[i];
3384 out_ptr += sizeof(D3DXVECTOR2);
3387 if (mesh_data.fvf & D3DFVF_NORMAL) {
3388 DWORD vertex_size = D3DXGetFVFVertexSize(mesh_data.fvf);
3390 for (i = 0; i < mesh_data.num_vertices; i++) {
3391 struct vertex_duplication *dup_ptr;
3392 LIST_FOR_EACH_ENTRY(dup_ptr, &duplications[i].entry, struct vertex_duplication, entry)
3394 int j = dup_ptr - duplications;
3395 BYTE *dest_vertex = (BYTE*)vertices + j * vertex_size;
3397 memcpy(dest_vertex, out_ptr, vertex_size);
3398 dest_vertex += sizeof(D3DXVECTOR3);
3399 *(D3DXVECTOR3*)dest_vertex = mesh_data.normals[dup_ptr->normal_index];
3401 out_ptr += vertex_size;
3404 d3dxmesh->lpVtbl->UnlockVertexBuffer(d3dxmesh);
3406 hr = d3dxmesh->lpVtbl->LockIndexBuffer(d3dxmesh, 0, &indices);
3407 if (FAILED(hr)) goto cleanup;
3409 index_in_ptr = mesh_data.indices;
3410 #define FILL_INDEX_BUFFER(indices_var) \
3411 for (i = 0; i < mesh_data.num_poly_faces; i++) \
3413 DWORD count = mesh_data.num_tri_per_face[i]; \
3414 WORD first_index = *index_in_ptr++; \
3416 *indices_var++ = first_index; \
3417 *indices_var++ = *index_in_ptr; \
3419 *indices_var++ = *index_in_ptr; \
3423 if (options & D3DXMESH_32BIT) {
3424 DWORD *dword_indices = indices;
3425 FILL_INDEX_BUFFER(dword_indices)
3427 WORD *word_indices = indices;
3428 FILL_INDEX_BUFFER(word_indices)
3430 #undef FILL_INDEX_BUFFER
3431 d3dxmesh->lpVtbl->UnlockIndexBuffer(d3dxmesh);
3433 if (mesh_data.material_indices) {
3434 DWORD *attrib_buffer = NULL;
3435 hr = d3dxmesh->lpVtbl->LockAttributeBuffer(d3dxmesh, 0, &attrib_buffer);
3436 if (FAILED(hr)) goto cleanup;
3437 for (i = 0; i < mesh_data.num_poly_faces; i++)
3439 DWORD count = mesh_data.num_tri_per_face[i];
3441 *attrib_buffer++ = mesh_data.material_indices[i];
3443 d3dxmesh->lpVtbl->UnlockAttributeBuffer(d3dxmesh);
3445 hr = d3dxmesh->lpVtbl->OptimizeInplace(d3dxmesh,
3446 D3DXMESHOPT_ATTRSORT | D3DXMESHOPT_IGNOREVERTS | D3DXMESHOPT_DONOTSPLIT,
3447 NULL, NULL, NULL, NULL);
3448 if (FAILED(hr)) goto cleanup;
3451 if (mesh_data.num_materials && (materials_out || effects_out)) {
3452 DWORD buffer_size = mesh_data.num_materials * sizeof(D3DXMATERIAL);
3453 char *strings_out_ptr;
3454 D3DXMATERIAL *materials_ptr;
3456 for (i = 0; i < mesh_data.num_materials; i++) {
3457 if (mesh_data.materials[i].pTextureFilename)
3458 buffer_size += strlen(mesh_data.materials[i].pTextureFilename) + 1;
3461 hr = D3DXCreateBuffer(buffer_size, &materials);
3462 if (FAILED(hr)) goto cleanup;
3464 materials_ptr = ID3DXBuffer_GetBufferPointer(materials);
3465 memcpy(materials_ptr, mesh_data.materials, mesh_data.num_materials * sizeof(D3DXMATERIAL));
3466 strings_out_ptr = (char*)(materials_ptr + mesh_data.num_materials);
3467 for (i = 0; i < mesh_data.num_materials; i++) {
3468 if (materials_ptr[i].pTextureFilename) {
3469 strcpy(strings_out_ptr, mesh_data.materials[i].pTextureFilename);
3470 materials_ptr[i].pTextureFilename = strings_out_ptr;
3471 strings_out_ptr += strlen(mesh_data.materials[i].pTextureFilename) + 1;
3476 if (mesh_data.num_materials && effects_out) {
3477 hr = generate_effects(materials, mesh_data.num_materials, &effects);
3478 if (FAILED(hr)) goto cleanup;
3480 if (!materials_out) {
3481 ID3DXBuffer_Release(materials);
3486 if (adjacency_out) {
3487 hr = D3DXCreateBuffer(mesh_data.num_tri_faces * 3 * sizeof(DWORD), &adjacency);
3488 if (FAILED(hr)) goto cleanup;
3489 hr = d3dxmesh->lpVtbl->GenerateAdjacency(d3dxmesh, 0.0f, ID3DXBuffer_GetBufferPointer(adjacency));
3490 if (FAILED(hr)) goto cleanup;
3493 *mesh_out = d3dxmesh;
3494 if (adjacency_out) *adjacency_out = adjacency;
3495 if (num_materials_out) *num_materials_out = mesh_data.num_materials;
3496 if (materials_out) *materials_out = materials;
3497 if (effects_out) *effects_out = effects;
3498 if (skin_info_out) *skin_info_out = NULL;
3503 if (d3dxmesh) IUnknown_Release(d3dxmesh);
3504 if (adjacency) ID3DXBuffer_Release(adjacency);
3505 if (materials) ID3DXBuffer_Release(materials);
3506 if (effects) ID3DXBuffer_Release(effects);
3508 HeapFree(GetProcessHeap(), 0, mesh_data.vertices);
3509 HeapFree(GetProcessHeap(), 0, mesh_data.num_tri_per_face);
3510 HeapFree(GetProcessHeap(), 0, mesh_data.indices);
3511 HeapFree(GetProcessHeap(), 0, mesh_data.normals);
3512 HeapFree(GetProcessHeap(), 0, mesh_data.normal_indices);
3513 destroy_materials(&mesh_data);
3514 HeapFree(GetProcessHeap(), 0, mesh_data.tex_coords);
3515 HeapFree(GetProcessHeap(), 0, mesh_data.vertex_colors);
3516 HeapFree(GetProcessHeap(), 0, duplications);
3520 HRESULT WINAPI D3DXLoadMeshHierarchyFromXA(LPCSTR filename,
3522 LPDIRECT3DDEVICE9 device,
3523 LPD3DXALLOCATEHIERARCHY alloc_hier,
3524 LPD3DXLOADUSERDATA load_user_data,
3525 LPD3DXFRAME *frame_hierarchy,
3526 LPD3DXANIMATIONCONTROLLER *anim_controller)
3532 TRACE("(%s, %x, %p, %p, %p, %p, %p)\n", debugstr_a(filename), options,
3533 device, alloc_hier, load_user_data, frame_hierarchy, anim_controller);
3536 return D3DERR_INVALIDCALL;
3538 len = MultiByteToWideChar(CP_ACP, 0, filename, -1, NULL, 0);
3539 filenameW = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
3540 if (!filenameW) return E_OUTOFMEMORY;
3541 MultiByteToWideChar(CP_ACP, 0, filename, -1, filenameW, len);
3543 hr = D3DXLoadMeshHierarchyFromXW(filenameW, options, device,
3544 alloc_hier, load_user_data, frame_hierarchy, anim_controller);
3545 HeapFree(GetProcessHeap(), 0, filenameW);
3550 HRESULT WINAPI D3DXLoadMeshHierarchyFromXW(LPCWSTR filename,
3552 LPDIRECT3DDEVICE9 device,
3553 LPD3DXALLOCATEHIERARCHY alloc_hier,
3554 LPD3DXLOADUSERDATA load_user_data,
3555 LPD3DXFRAME *frame_hierarchy,
3556 LPD3DXANIMATIONCONTROLLER *anim_controller)
3562 TRACE("(%s, %x, %p, %p, %p, %p, %p)\n", debugstr_w(filename), options,
3563 device, alloc_hier, load_user_data, frame_hierarchy, anim_controller);
3566 return D3DERR_INVALIDCALL;
3568 hr = map_view_of_file(filename, &buffer, &size);
3570 return D3DXERR_INVALIDDATA;
3572 hr = D3DXLoadMeshHierarchyFromXInMemory(buffer, size, options, device,
3573 alloc_hier, load_user_data, frame_hierarchy, anim_controller);
3575 UnmapViewOfFile(buffer);
3580 static HRESULT filedata_get_name(IDirectXFileData *filedata, char **name)
3585 hr = IDirectXFileData_GetName(filedata, NULL, &name_len);
3586 if (FAILED(hr)) return hr;
3590 *name = HeapAlloc(GetProcessHeap(), 0, name_len);
3591 if (!*name) return E_OUTOFMEMORY;
3593 hr = IDirectXFileObject_GetName(filedata, *name, &name_len);
3595 HeapFree(GetProcessHeap(), 0, *name);
3602 static HRESULT load_mesh_container(IDirectXFileData *filedata,
3604 LPDIRECT3DDEVICE9 device,
3605 LPD3DXALLOCATEHIERARCHY alloc_hier,
3606 D3DXMESHCONTAINER **mesh_container)
3609 ID3DXBuffer *adjacency = NULL;
3610 ID3DXBuffer *materials = NULL;
3611 ID3DXBuffer *effects = NULL;
3612 ID3DXSkinInfo *skin_info = NULL;
3613 D3DXMESHDATA mesh_data;
3614 DWORD num_materials = 0;
3617 mesh_data.Type = D3DXMESHTYPE_MESH;
3618 mesh_data.u.pMesh = NULL;
3620 hr = load_skin_mesh_from_xof(filedata, options, device,
3621 &adjacency, &materials, &effects, &num_materials,
3622 &skin_info, &mesh_data.u.pMesh);
3623 if (FAILED(hr)) return hr;
3625 hr = filedata_get_name(filedata, &name);
3626 if (FAILED(hr)) goto cleanup;
3628 hr = alloc_hier->lpVtbl->CreateMeshContainer(alloc_hier, name, &mesh_data,
3629 materials ? ID3DXBuffer_GetBufferPointer(materials) : NULL,
3630 effects ? ID3DXBuffer_GetBufferPointer(effects) : NULL,
3632 adjacency ? ID3DXBuffer_GetBufferPointer(adjacency) : NULL,
3633 skin_info, mesh_container);
3636 if (materials) ID3DXBuffer_Release(materials);
3637 if (effects) ID3DXBuffer_Release(effects);
3638 if (adjacency) ID3DXBuffer_Release(adjacency);
3639 if (skin_info) IUnknown_Release(skin_info);
3640 if (mesh_data.u.pMesh) IUnknown_Release(mesh_data.u.pMesh);
3641 HeapFree(GetProcessHeap(), 0, name);
3645 static HRESULT parse_transform_matrix(IDirectXFileData *filedata, D3DXMATRIX *transform)
3651 /* template Matrix4x4 {
3652 * array FLOAT matrix[16];
3654 * template FrameTransformMatrix {
3655 * Matrix4x4 frameMatrix;
3659 hr = IDirectXFileData_GetData(filedata, NULL, &data_size, (void**)&data);
3660 if (FAILED(hr)) return hr;
3662 if (data_size != sizeof(D3DXMATRIX)) {
3663 WARN("incorrect data size (%u bytes)\n", data_size);
3667 memcpy(transform, data, sizeof(D3DXMATRIX));
3672 static HRESULT load_frame(IDirectXFileData *filedata,
3674 LPDIRECT3DDEVICE9 device,
3675 LPD3DXALLOCATEHIERARCHY alloc_hier,
3676 D3DXFRAME **frame_out)
3680 IDirectXFileData *child;
3682 D3DXFRAME *frame = NULL;
3683 D3DXMESHCONTAINER **next_container;
3684 D3DXFRAME **next_child;
3686 hr = filedata_get_name(filedata, &name);
3687 if (FAILED(hr)) return hr;
3689 hr = alloc_hier->lpVtbl->CreateFrame(alloc_hier, name, frame_out);
3690 HeapFree(GetProcessHeap(), 0, name);
3691 if (FAILED(hr)) return E_FAIL;
3694 D3DXMatrixIdentity(&frame->TransformationMatrix);
3695 next_child = &frame->pFrameFirstChild;
3696 next_container = &frame->pMeshContainer;
3698 while (SUCCEEDED(hr = get_next_child(filedata, &child, &type)))
3700 if (IsEqualGUID(type, &TID_D3DRMMesh)) {
3701 hr = load_mesh_container(child, options, device, alloc_hier, next_container);
3703 next_container = &(*next_container)->pNextMeshContainer;
3704 } else if (IsEqualGUID(type, &TID_D3DRMFrameTransformMatrix)) {
3705 hr = parse_transform_matrix(child, &frame->TransformationMatrix);
3706 } else if (IsEqualGUID(type, &TID_D3DRMFrame)) {
3707 hr = load_frame(child, options, device, alloc_hier, next_child);
3709 next_child = &(*next_child)->pFrameSibling;
3711 if (FAILED(hr)) break;
3713 if (hr == DXFILEERR_NOMOREOBJECTS)
3719 HRESULT WINAPI D3DXLoadMeshHierarchyFromXInMemory(LPCVOID memory,
3722 LPDIRECT3DDEVICE9 device,
3723 LPD3DXALLOCATEHIERARCHY alloc_hier,
3724 LPD3DXLOADUSERDATA load_user_data,
3725 LPD3DXFRAME *frame_hierarchy,
3726 LPD3DXANIMATIONCONTROLLER *anim_controller)
3729 IDirectXFile *dxfile = NULL;
3730 IDirectXFileEnumObject *enumobj = NULL;
3731 IDirectXFileData *filedata = NULL;
3732 DXFILELOADMEMORY source;
3733 D3DXFRAME *first_frame = NULL;
3734 D3DXFRAME **next_frame = &first_frame;
3736 TRACE("(%p, %u, %x, %p, %p, %p, %p, %p)\n", memory, memory_size, options,
3737 device, alloc_hier, load_user_data, frame_hierarchy, anim_controller);
3739 if (!memory || !memory_size || !device || !frame_hierarchy || !alloc_hier)
3740 return D3DERR_INVALIDCALL;
3741 if (load_user_data || anim_controller) {
3743 FIXME("Loading user data not implemented\n");
3744 if (anim_controller)
3745 FIXME("Animation controller creation not implemented\n");
3749 hr = DirectXFileCreate(&dxfile);
3750 if (FAILED(hr)) goto cleanup;
3752 hr = IDirectXFile_RegisterTemplates(dxfile, D3DRM_XTEMPLATES, D3DRM_XTEMPLATE_BYTES);
3753 if (FAILED(hr)) goto cleanup;
3755 source.lpMemory = (void*)memory;
3756 source.dSize = memory_size;
3757 hr = IDirectXFile_CreateEnumObject(dxfile, &source, DXFILELOAD_FROMMEMORY, &enumobj);
3758 if (FAILED(hr)) goto cleanup;
3760 while (SUCCEEDED(hr = IDirectXFileEnumObject_GetNextDataObject(enumobj, &filedata)))
3762 const GUID *guid = NULL;
3764 hr = IDirectXFileData_GetType(filedata, &guid);
3765 if (SUCCEEDED(hr)) {
3766 if (IsEqualGUID(guid, &TID_D3DRMMesh)) {
3767 hr = alloc_hier->lpVtbl->CreateFrame(alloc_hier, NULL, next_frame);
3773 D3DXMatrixIdentity(&(*next_frame)->TransformationMatrix);
3775 hr = load_mesh_container(filedata, options, device, alloc_hier, &(*next_frame)->pMeshContainer);
3776 if (FAILED(hr)) goto cleanup;
3777 } else if (IsEqualGUID(guid, &TID_D3DRMFrame)) {
3778 hr = load_frame(filedata, options, device, alloc_hier, next_frame);
3779 if (FAILED(hr)) goto cleanup;
3782 next_frame = &(*next_frame)->pFrameSibling;
3785 IDirectXFileData_Release(filedata);
3790 if (hr != DXFILEERR_NOMOREOBJECTS)
3795 } else if (first_frame->pFrameSibling) {
3796 D3DXFRAME *root_frame = NULL;
3797 hr = alloc_hier->lpVtbl->CreateFrame(alloc_hier, NULL, &root_frame);
3802 D3DXMatrixIdentity(&root_frame->TransformationMatrix);
3803 root_frame->pFrameFirstChild = first_frame;
3804 *frame_hierarchy = root_frame;
3807 *frame_hierarchy = first_frame;
3812 if (FAILED(hr) && first_frame) D3DXFrameDestroy(first_frame, alloc_hier);
3813 if (filedata) IDirectXFileData_Release(filedata);
3814 if (enumobj) IDirectXFileEnumObject_Release(enumobj);
3815 if (dxfile) IDirectXFile_Release(dxfile);
3819 HRESULT WINAPI D3DXCleanMesh(D3DXCLEANTYPE clean_type, LPD3DXMESH mesh_in, const DWORD *adjacency_in,
3820 LPD3DXMESH *mesh_out, DWORD *adjacency_out, LPD3DXBUFFER *errors_and_warnings)
3822 FIXME("(%u, %p, %p, %p, %p, %p)\n", clean_type, mesh_in, adjacency_in, mesh_out, adjacency_out, errors_and_warnings);
3827 HRESULT WINAPI D3DXFrameDestroy(LPD3DXFRAME frame, LPD3DXALLOCATEHIERARCHY alloc_hier)
3832 TRACE("(%p, %p)\n", frame, alloc_hier);
3834 if (!frame || !alloc_hier)
3835 return D3DERR_INVALIDCALL;
3838 D3DXMESHCONTAINER *container;
3839 D3DXFRAME *current_frame;
3841 if (frame->pFrameSibling) {
3842 current_frame = frame->pFrameSibling;
3843 frame->pFrameSibling = current_frame->pFrameSibling;
3844 current_frame->pFrameSibling = NULL;
3846 current_frame = frame;
3850 if (current_frame->pFrameFirstChild) {
3851 hr = D3DXFrameDestroy(current_frame->pFrameFirstChild, alloc_hier);
3852 if (FAILED(hr)) return hr;
3853 current_frame->pFrameFirstChild = NULL;
3856 container = current_frame->pMeshContainer;
3858 D3DXMESHCONTAINER *next_container = container->pNextMeshContainer;
3859 hr = alloc_hier->lpVtbl->DestroyMeshContainer(alloc_hier, container);
3860 if (FAILED(hr)) return hr;
3861 container = next_container;
3863 hr = alloc_hier->lpVtbl->DestroyFrame(alloc_hier, current_frame);
3864 if (FAILED(hr)) return hr;
3869 HRESULT WINAPI D3DXLoadMeshFromXA(LPCSTR filename,
3871 LPDIRECT3DDEVICE9 device,
3872 LPD3DXBUFFER *adjacency,
3873 LPD3DXBUFFER *materials,
3874 LPD3DXBUFFER *effect_instances,
3875 DWORD *num_materials,
3882 TRACE("(%s, %x, %p, %p, %p, %p, %p, %p)\n", debugstr_a(filename), options,
3883 device, adjacency, materials, effect_instances, num_materials, mesh);
3886 return D3DERR_INVALIDCALL;
3888 len = MultiByteToWideChar(CP_ACP, 0, filename, -1, NULL, 0);
3889 filenameW = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
3890 if (!filenameW) return E_OUTOFMEMORY;
3891 MultiByteToWideChar(CP_ACP, 0, filename, -1, filenameW, len);
3893 hr = D3DXLoadMeshFromXW(filenameW, options, device, adjacency, materials,
3894 effect_instances, num_materials, mesh);
3895 HeapFree(GetProcessHeap(), 0, filenameW);
3900 HRESULT WINAPI D3DXLoadMeshFromXW(LPCWSTR filename,
3902 LPDIRECT3DDEVICE9 device,
3903 LPD3DXBUFFER *adjacency,
3904 LPD3DXBUFFER *materials,
3905 LPD3DXBUFFER *effect_instances,
3906 DWORD *num_materials,
3913 TRACE("(%s, %x, %p, %p, %p, %p, %p, %p)\n", debugstr_w(filename), options,
3914 device, adjacency, materials, effect_instances, num_materials, mesh);
3917 return D3DERR_INVALIDCALL;
3919 hr = map_view_of_file(filename, &buffer, &size);
3921 return D3DXERR_INVALIDDATA;
3923 hr = D3DXLoadMeshFromXInMemory(buffer, size, options, device, adjacency,
3924 materials, effect_instances, num_materials, mesh);
3926 UnmapViewOfFile(buffer);
3931 HRESULT WINAPI D3DXLoadMeshFromXResource(HMODULE module,
3935 LPDIRECT3DDEVICE9 device,
3936 LPD3DXBUFFER *adjacency,
3937 LPD3DXBUFFER *materials,
3938 LPD3DXBUFFER *effect_instances,
3939 DWORD *num_materials,
3947 TRACE("(%p, %s, %s, %x, %p, %p, %p, %p, %p, %p)\n",
3948 module, debugstr_a(name), debugstr_a(type), options, device,
3949 adjacency, materials, effect_instances, num_materials, mesh);
3951 resinfo = FindResourceA(module, name, type);
3952 if (!resinfo) return D3DXERR_INVALIDDATA;
3954 hr = load_resource_into_memory(module, resinfo, &buffer, &size);
3955 if (FAILED(hr)) return D3DXERR_INVALIDDATA;
3957 return D3DXLoadMeshFromXInMemory(buffer, size, options, device, adjacency,
3958 materials, effect_instances, num_materials, mesh);
3961 struct mesh_container
3965 ID3DXBuffer *adjacency;
3966 ID3DXBuffer *materials;
3967 ID3DXBuffer *effects;
3968 DWORD num_materials;
3969 D3DXMATRIX transform;
3972 static HRESULT parse_frame(IDirectXFileData *filedata,
3974 LPDIRECT3DDEVICE9 device,
3975 const D3DXMATRIX *parent_transform,
3976 struct list *container_list,
3977 DWORD provide_flags)
3980 D3DXMATRIX transform = *parent_transform;
3981 IDirectXFileData *child;
3984 while (SUCCEEDED(hr = get_next_child(filedata, &child, &type)))
3986 if (IsEqualGUID(type, &TID_D3DRMMesh)) {
3987 struct mesh_container *container = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(*container));
3992 list_add_tail(container_list, &container->entry);
3993 container->transform = transform;
3995 hr = load_skin_mesh_from_xof(child, options, device,
3996 (provide_flags & PROVIDE_ADJACENCY) ? &container->adjacency : NULL,
3997 (provide_flags & PROVIDE_MATERIALS) ? &container->materials : NULL,
3998 NULL, &container->num_materials, NULL, &container->mesh);
3999 } else if (IsEqualGUID(type, &TID_D3DRMFrameTransformMatrix)) {
4000 D3DXMATRIX new_transform;
4001 hr = parse_transform_matrix(child, &new_transform);
4002 D3DXMatrixMultiply(&transform, &transform, &new_transform);
4003 } else if (IsEqualGUID(type, &TID_D3DRMFrame)) {
4004 hr = parse_frame(child, options, device, &transform, container_list, provide_flags);
4006 if (FAILED(hr)) break;
4008 return hr == DXFILEERR_NOMOREOBJECTS ? D3D_OK : hr;
4011 HRESULT WINAPI D3DXLoadMeshFromXInMemory(LPCVOID memory,
4014 LPDIRECT3DDEVICE9 device,
4015 LPD3DXBUFFER *adjacency_out,
4016 LPD3DXBUFFER *materials_out,
4017 LPD3DXBUFFER *effects_out,
4018 DWORD *num_materials_out,
4019 LPD3DXMESH *mesh_out)
4022 IDirectXFile *dxfile = NULL;
4023 IDirectXFileEnumObject *enumobj = NULL;
4024 IDirectXFileData *filedata = NULL;
4025 DXFILELOADMEMORY source;
4026 ID3DXBuffer *materials = NULL;
4027 ID3DXBuffer *effects = NULL;
4028 ID3DXBuffer *adjacency = NULL;
4029 struct list container_list = LIST_INIT(container_list);
4030 struct mesh_container *container_ptr, *next_container_ptr;
4031 DWORD num_materials;
4032 DWORD num_faces, num_vertices;
4033 D3DXMATRIX identity;
4035 DWORD provide_flags = 0;
4037 ID3DXMesh *concat_mesh = NULL;
4038 D3DVERTEXELEMENT9 concat_decl[MAX_FVF_DECL_SIZE];
4039 BYTE *concat_vertices = NULL;
4040 void *concat_indices = NULL;
4042 DWORD concat_vertex_size;
4044 TRACE("(%p, %u, %x, %p, %p, %p, %p, %p, %p)\n", memory, memory_size, options,
4045 device, adjacency_out, materials_out, effects_out, num_materials_out, mesh_out);
4047 if (!memory || !memory_size || !device || !mesh_out)
4048 return D3DERR_INVALIDCALL;
4050 hr = DirectXFileCreate(&dxfile);
4051 if (FAILED(hr)) goto cleanup;
4053 hr = IDirectXFile_RegisterTemplates(dxfile, D3DRM_XTEMPLATES, D3DRM_XTEMPLATE_BYTES);
4054 if (FAILED(hr)) goto cleanup;
4056 source.lpMemory = (void*)memory;
4057 source.dSize = memory_size;
4058 hr = IDirectXFile_CreateEnumObject(dxfile, &source, DXFILELOAD_FROMMEMORY, &enumobj);
4059 if (FAILED(hr)) goto cleanup;
4061 D3DXMatrixIdentity(&identity);
4062 if (adjacency_out) provide_flags |= PROVIDE_ADJACENCY;
4063 if (materials_out || effects_out) provide_flags |= PROVIDE_MATERIALS;
4065 while (SUCCEEDED(hr = IDirectXFileEnumObject_GetNextDataObject(enumobj, &filedata)))
4067 const GUID *guid = NULL;
4069 hr = IDirectXFileData_GetType(filedata, &guid);
4070 if (SUCCEEDED(hr)) {
4071 if (IsEqualGUID(guid, &TID_D3DRMMesh)) {
4072 container_ptr = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(*container_ptr));
4073 if (!container_ptr) {
4077 list_add_tail(&container_list, &container_ptr->entry);
4078 D3DXMatrixIdentity(&container_ptr->transform);
4080 hr = load_skin_mesh_from_xof(filedata, options, device,
4081 (provide_flags & PROVIDE_ADJACENCY) ? &container_ptr->adjacency : NULL,
4082 (provide_flags & PROVIDE_MATERIALS) ? &container_ptr->materials : NULL,
4083 NULL, &container_ptr->num_materials, NULL, &container_ptr->mesh);
4084 } else if (IsEqualGUID(guid, &TID_D3DRMFrame)) {
4085 hr = parse_frame(filedata, options, device, &identity, &container_list, provide_flags);
4087 if (FAILED(hr)) goto cleanup;
4089 IDirectXFileData_Release(filedata);
4094 if (hr != DXFILEERR_NOMOREOBJECTS)
4097 IDirectXFileEnumObject_Release(enumobj);
4099 IDirectXFile_Release(dxfile);
4102 if (list_empty(&container_list)) {
4111 LIST_FOR_EACH_ENTRY(container_ptr, &container_list, struct mesh_container, entry)
4113 ID3DXMesh *mesh = container_ptr->mesh;
4114 fvf |= mesh->lpVtbl->GetFVF(mesh);
4115 num_faces += mesh->lpVtbl->GetNumFaces(mesh);
4116 num_vertices += mesh->lpVtbl->GetNumVertices(mesh);
4117 num_materials += container_ptr->num_materials;
4120 hr = D3DXCreateMeshFVF(num_faces, num_vertices, options, fvf, device, &concat_mesh);
4121 if (FAILED(hr)) goto cleanup;
4123 hr = concat_mesh->lpVtbl->GetDeclaration(concat_mesh, concat_decl);
4124 if (FAILED(hr)) goto cleanup;
4126 concat_vertex_size = D3DXGetDeclVertexSize(concat_decl, 0);
4128 hr = concat_mesh->lpVtbl->LockVertexBuffer(concat_mesh, 0, (void**)&concat_vertices);
4129 if (FAILED(hr)) goto cleanup;
4131 LIST_FOR_EACH_ENTRY(container_ptr, &container_list, struct mesh_container, entry)
4133 D3DVERTEXELEMENT9 mesh_decl[MAX_FVF_DECL_SIZE];
4134 ID3DXMesh *mesh = container_ptr->mesh;
4135 DWORD num_mesh_vertices = mesh->lpVtbl->GetNumVertices(mesh);
4136 DWORD mesh_vertex_size;
4137 const BYTE *mesh_vertices;
4139 hr = mesh->lpVtbl->GetDeclaration(mesh, mesh_decl);
4140 if (FAILED(hr)) goto cleanup;
4142 mesh_vertex_size = D3DXGetDeclVertexSize(mesh_decl, 0);
4144 hr = mesh->lpVtbl->LockVertexBuffer(mesh, D3DLOCK_READONLY, (void**)&mesh_vertices);
4145 if (FAILED(hr)) goto cleanup;
4147 for (i = 0; i < num_mesh_vertices; i++) {
4151 D3DXVec3TransformCoord((D3DXVECTOR3*)concat_vertices,
4152 (D3DXVECTOR3*)mesh_vertices,
4153 &container_ptr->transform);
4154 for (j = 1; concat_decl[j].Stream != 0xff; j++)
4156 if (concat_decl[j].Usage == mesh_decl[k].Usage &&
4157 concat_decl[j].UsageIndex == mesh_decl[k].UsageIndex)
4159 if (concat_decl[j].Usage == D3DDECLUSAGE_NORMAL) {
4160 D3DXVec3TransformCoord((D3DXVECTOR3*)(concat_vertices + concat_decl[j].Offset),
4161 (D3DXVECTOR3*)(mesh_vertices + mesh_decl[k].Offset),
4162 &container_ptr->transform);
4164 memcpy(concat_vertices + concat_decl[j].Offset,
4165 mesh_vertices + mesh_decl[k].Offset,
4166 d3dx_decltype_size[mesh_decl[k].Type]);
4171 mesh_vertices += mesh_vertex_size;
4172 concat_vertices += concat_vertex_size;
4175 mesh->lpVtbl->UnlockVertexBuffer(mesh);
4178 concat_mesh->lpVtbl->UnlockVertexBuffer(concat_mesh);
4179 concat_vertices = NULL;
4181 hr = concat_mesh->lpVtbl->LockIndexBuffer(concat_mesh, 0, &concat_indices);
4182 if (FAILED(hr)) goto cleanup;
4185 LIST_FOR_EACH_ENTRY(container_ptr, &container_list, struct mesh_container, entry)
4187 ID3DXMesh *mesh = container_ptr->mesh;
4188 const void *mesh_indices;
4189 DWORD num_mesh_faces = mesh->lpVtbl->GetNumFaces(mesh);
4192 hr = mesh->lpVtbl->LockIndexBuffer(mesh, D3DLOCK_READONLY, (void**)&mesh_indices);
4193 if (FAILED(hr)) goto cleanup;
4195 if (options & D3DXMESH_32BIT) {
4196 DWORD *dest = concat_indices;
4197 const DWORD *src = mesh_indices;
4198 for (i = 0; i < num_mesh_faces * 3; i++)
4199 *dest++ = index_offset + *src++;
4200 concat_indices = dest;
4202 WORD *dest = concat_indices;
4203 const WORD *src = mesh_indices;
4204 for (i = 0; i < num_mesh_faces * 3; i++)
4205 *dest++ = index_offset + *src++;
4206 concat_indices = dest;
4208 mesh->lpVtbl->UnlockIndexBuffer(mesh);
4210 index_offset += num_mesh_faces * 3;
4213 concat_mesh->lpVtbl->UnlockIndexBuffer(concat_mesh);
4214 concat_indices = NULL;
4216 if (num_materials) {
4217 DWORD *concat_attrib_buffer = NULL;
4220 hr = concat_mesh->lpVtbl->LockAttributeBuffer(concat_mesh, 0, &concat_attrib_buffer);
4221 if (FAILED(hr)) goto cleanup;
4223 LIST_FOR_EACH_ENTRY(container_ptr, &container_list, struct mesh_container, entry)
4225 ID3DXMesh *mesh = container_ptr->mesh;
4226 const DWORD *mesh_attrib_buffer = NULL;
4227 DWORD count = mesh->lpVtbl->GetNumFaces(mesh);
4229 hr = mesh->lpVtbl->LockAttributeBuffer(mesh, D3DLOCK_READONLY, (DWORD**)&mesh_attrib_buffer);
4231 concat_mesh->lpVtbl->UnlockAttributeBuffer(concat_mesh);
4236 *concat_attrib_buffer++ = offset + *mesh_attrib_buffer++;
4238 mesh->lpVtbl->UnlockAttributeBuffer(mesh);
4239 offset += container_ptr->num_materials;
4241 concat_mesh->lpVtbl->UnlockAttributeBuffer(concat_mesh);
4244 if (materials_out || effects_out) {
4245 D3DXMATERIAL *out_ptr;
4246 if (!num_materials) {
4247 /* create default material */
4248 hr = D3DXCreateBuffer(sizeof(D3DXMATERIAL), &materials);
4249 if (FAILED(hr)) goto cleanup;
4251 out_ptr = ID3DXBuffer_GetBufferPointer(materials);
4252 out_ptr->MatD3D.Diffuse.r = 0.5f;
4253 out_ptr->MatD3D.Diffuse.g = 0.5f;
4254 out_ptr->MatD3D.Diffuse.b = 0.5f;
4255 out_ptr->MatD3D.Specular.r = 0.5f;
4256 out_ptr->MatD3D.Specular.g = 0.5f;
4257 out_ptr->MatD3D.Specular.b = 0.5f;
4258 /* D3DXCreateBuffer initializes the rest to zero */
4260 DWORD buffer_size = num_materials * sizeof(D3DXMATERIAL);
4261 char *strings_out_ptr;
4263 LIST_FOR_EACH_ENTRY(container_ptr, &container_list, struct mesh_container, entry)
4265 if (container_ptr->materials) {
4266 const D3DXMATERIAL *in_ptr = ID3DXBuffer_GetBufferPointer(container_ptr->materials);
4267 for (i = 0; i < container_ptr->num_materials; i++)
4269 if (in_ptr->pTextureFilename)
4270 buffer_size += strlen(in_ptr->pTextureFilename) + 1;
4276 hr = D3DXCreateBuffer(buffer_size, &materials);
4277 if (FAILED(hr)) goto cleanup;
4278 out_ptr = ID3DXBuffer_GetBufferPointer(materials);
4279 strings_out_ptr = (char*)(out_ptr + num_materials);
4281 LIST_FOR_EACH_ENTRY(container_ptr, &container_list, struct mesh_container, entry)
4283 if (container_ptr->materials) {
4284 const D3DXMATERIAL *in_ptr = ID3DXBuffer_GetBufferPointer(container_ptr->materials);
4285 for (i = 0; i < container_ptr->num_materials; i++)
4287 out_ptr->MatD3D = in_ptr->MatD3D;
4288 if (in_ptr->pTextureFilename) {
4289 out_ptr->pTextureFilename = strings_out_ptr;
4290 strcpy(out_ptr->pTextureFilename, in_ptr->pTextureFilename);
4291 strings_out_ptr += strlen(in_ptr->pTextureFilename) + 1;
4304 generate_effects(materials, num_materials, &effects);
4305 if (!materials_out) {
4306 ID3DXBuffer_Release(materials);
4311 if (adjacency_out) {
4312 if (!list_next(&container_list, list_head(&container_list))) {
4313 container_ptr = LIST_ENTRY(list_head(&container_list), struct mesh_container, entry);
4314 adjacency = container_ptr->adjacency;
4315 container_ptr->adjacency = NULL;
4320 hr = D3DXCreateBuffer(num_faces * 3 * sizeof(DWORD), &adjacency);
4321 if (FAILED(hr)) goto cleanup;
4323 out_ptr = ID3DXBuffer_GetBufferPointer(adjacency);
4324 LIST_FOR_EACH_ENTRY(container_ptr, &container_list, struct mesh_container, entry)
4326 DWORD *in_ptr = ID3DXBuffer_GetBufferPointer(container_ptr->adjacency);
4327 int count = 3 * container_ptr->mesh->lpVtbl->GetNumFaces(container_ptr->mesh);
4329 for (i = 0; i < count; i++)
4330 *out_ptr++ = offset + *in_ptr++;
4337 *mesh_out = concat_mesh;
4338 if (adjacency_out) *adjacency_out = adjacency;
4339 if (materials_out) *materials_out = materials;
4340 if (effects_out) *effects_out = effects;
4341 if (num_materials_out) *num_materials_out = num_materials;
4345 if (concat_indices) concat_mesh->lpVtbl->UnlockIndexBuffer(concat_mesh);
4346 if (concat_vertices) concat_mesh->lpVtbl->UnlockVertexBuffer(concat_mesh);
4347 if (filedata) IDirectXFileData_Release(filedata);
4348 if (enumobj) IDirectXFileEnumObject_Release(enumobj);
4349 if (dxfile) IDirectXFile_Release(dxfile);
4351 if (concat_mesh) IUnknown_Release(concat_mesh);
4352 if (materials) ID3DXBuffer_Release(materials);
4353 if (effects) ID3DXBuffer_Release(effects);
4354 if (adjacency) ID3DXBuffer_Release(adjacency);
4356 LIST_FOR_EACH_ENTRY_SAFE(container_ptr, next_container_ptr, &container_list, struct mesh_container, entry)
4358 if (container_ptr->mesh) IUnknown_Release(container_ptr->mesh);
4359 if (container_ptr->adjacency) ID3DXBuffer_Release(container_ptr->adjacency);
4360 if (container_ptr->materials) ID3DXBuffer_Release(container_ptr->materials);
4361 if (container_ptr->effects) ID3DXBuffer_Release(container_ptr->effects);
4362 HeapFree(GetProcessHeap(), 0, container_ptr);
4367 HRESULT WINAPI D3DXCreateBox(LPDIRECT3DDEVICE9 device, FLOAT width, FLOAT height,
4368 FLOAT depth, LPD3DXMESH* mesh, LPD3DXBUFFER* adjacency)
4370 FIXME("(%p, %f, %f, %f, %p, %p): stub\n", device, width, height, depth, mesh, adjacency);
4377 D3DXVECTOR3 position;
4381 typedef WORD face[3];
4389 static void free_sincos_table(struct sincos_table *sincos_table)
4391 HeapFree(GetProcessHeap(), 0, sincos_table->cos);
4392 HeapFree(GetProcessHeap(), 0, sincos_table->sin);
4395 /* pre compute sine and cosine tables; caller must free */
4396 static BOOL compute_sincos_table(struct sincos_table *sincos_table, float angle_start, float angle_step, int n)
4401 sincos_table->sin = HeapAlloc(GetProcessHeap(), 0, n * sizeof(*sincos_table->sin));
4402 if (!sincos_table->sin)
4406 sincos_table->cos = HeapAlloc(GetProcessHeap(), 0, n * sizeof(*sincos_table->cos));
4407 if (!sincos_table->cos)
4409 HeapFree(GetProcessHeap(), 0, sincos_table->sin);
4413 angle = angle_start;
4414 for (i = 0; i < n; i++)
4416 sincos_table->sin[i] = sin(angle);
4417 sincos_table->cos[i] = cos(angle);
4418 angle += angle_step;
4424 static WORD vertex_index(UINT slices, int slice, int stack)
4426 return stack*slices+slice+1;
4429 HRESULT WINAPI D3DXCreateSphere(LPDIRECT3DDEVICE9 device, FLOAT radius, UINT slices,
4430 UINT stacks, LPD3DXMESH* mesh, LPD3DXBUFFER* adjacency)
4432 DWORD number_of_vertices, number_of_faces;
4435 struct vertex *vertices;
4437 float phi_step, phi_start;
4438 struct sincos_table phi;
4439 float theta_step, theta, sin_theta, cos_theta;
4443 TRACE("(%p, %f, %u, %u, %p, %p)\n", device, radius, slices, stacks, mesh, adjacency);
4445 if (!device || radius < 0.0f || slices < 2 || stacks < 2 || !mesh)
4447 return D3DERR_INVALIDCALL;
4452 FIXME("Case of adjacency != NULL not implemented.\n");
4456 number_of_vertices = 2 + slices * (stacks-1);
4457 number_of_faces = 2 * slices + (stacks - 2) * (2 * slices);
4459 hr = D3DXCreateMeshFVF(number_of_faces, number_of_vertices, D3DXMESH_MANAGED,
4460 D3DFVF_XYZ | D3DFVF_NORMAL, device, &sphere);
4466 hr = sphere->lpVtbl->LockVertexBuffer(sphere, 0, (LPVOID *)&vertices);
4469 sphere->lpVtbl->Release(sphere);
4473 hr = sphere->lpVtbl->LockIndexBuffer(sphere, 0, (LPVOID *)&faces);
4476 sphere->lpVtbl->UnlockVertexBuffer(sphere);
4477 sphere->lpVtbl->Release(sphere);
4481 /* phi = angle on xz plane wrt z axis */
4482 phi_step = -2 * M_PI / slices;
4483 phi_start = M_PI / 2;
4485 if (!compute_sincos_table(&phi, phi_start, phi_step, slices))
4487 sphere->lpVtbl->UnlockIndexBuffer(sphere);
4488 sphere->lpVtbl->UnlockVertexBuffer(sphere);
4489 sphere->lpVtbl->Release(sphere);
4490 return E_OUTOFMEMORY;
4493 /* theta = angle on xy plane wrt x axis */
4494 theta_step = M_PI / stacks;
4500 vertices[vertex].normal.x = 0.0f;
4501 vertices[vertex].normal.y = 0.0f;
4502 vertices[vertex].normal.z = 1.0f;
4503 vertices[vertex].position.x = 0.0f;
4504 vertices[vertex].position.y = 0.0f;
4505 vertices[vertex].position.z = radius;
4508 for (stack = 0; stack < stacks - 1; stack++)
4510 sin_theta = sin(theta);
4511 cos_theta = cos(theta);
4513 for (slice = 0; slice < slices; slice++)
4515 vertices[vertex].normal.x = sin_theta * phi.cos[slice];
4516 vertices[vertex].normal.y = sin_theta * phi.sin[slice];
4517 vertices[vertex].normal.z = cos_theta;
4518 vertices[vertex].position.x = radius * sin_theta * phi.cos[slice];
4519 vertices[vertex].position.y = radius * sin_theta * phi.sin[slice];
4520 vertices[vertex].position.z = radius * cos_theta;
4527 /* top stack is triangle fan */
4529 faces[face][1] = slice + 1;
4530 faces[face][2] = slice;
4535 /* stacks in between top and bottom are quad strips */
4536 faces[face][0] = vertex_index(slices, slice-1, stack-1);
4537 faces[face][1] = vertex_index(slices, slice, stack-1);
4538 faces[face][2] = vertex_index(slices, slice-1, stack);
4541 faces[face][0] = vertex_index(slices, slice, stack-1);
4542 faces[face][1] = vertex_index(slices, slice, stack);
4543 faces[face][2] = vertex_index(slices, slice-1, stack);
4549 theta += theta_step;
4555 faces[face][2] = slice;
4560 faces[face][0] = vertex_index(slices, slice-1, stack-1);
4561 faces[face][1] = vertex_index(slices, 0, stack-1);
4562 faces[face][2] = vertex_index(slices, slice-1, stack);
4565 faces[face][0] = vertex_index(slices, 0, stack-1);
4566 faces[face][1] = vertex_index(slices, 0, stack);
4567 faces[face][2] = vertex_index(slices, slice-1, stack);
4572 vertices[vertex].position.x = 0.0f;
4573 vertices[vertex].position.y = 0.0f;
4574 vertices[vertex].position.z = -radius;
4575 vertices[vertex].normal.x = 0.0f;
4576 vertices[vertex].normal.y = 0.0f;
4577 vertices[vertex].normal.z = -1.0f;
4579 /* bottom stack is triangle fan */
4580 for (slice = 1; slice < slices; slice++)
4582 faces[face][0] = vertex_index(slices, slice-1, stack-1);
4583 faces[face][1] = vertex_index(slices, slice, stack-1);
4584 faces[face][2] = vertex;
4588 faces[face][0] = vertex_index(slices, slice-1, stack-1);
4589 faces[face][1] = vertex_index(slices, 0, stack-1);
4590 faces[face][2] = vertex;
4592 free_sincos_table(&phi);
4593 sphere->lpVtbl->UnlockIndexBuffer(sphere);
4594 sphere->lpVtbl->UnlockVertexBuffer(sphere);
4600 HRESULT WINAPI D3DXCreateCylinder(LPDIRECT3DDEVICE9 device, FLOAT radius1, FLOAT radius2, FLOAT length, UINT slices,
4601 UINT stacks, LPD3DXMESH* mesh, LPD3DXBUFFER* adjacency)
4603 DWORD number_of_vertices, number_of_faces;
4605 ID3DXMesh *cylinder;
4606 struct vertex *vertices;
4608 float theta_step, theta_start;
4609 struct sincos_table theta;
4610 float delta_radius, radius, radius_step;
4611 float z, z_step, z_normal;
4615 TRACE("(%p, %f, %f, %f, %u, %u, %p, %p)\n", device, radius1, radius2, length, slices, stacks, mesh, adjacency);
4617 if (device == NULL || radius1 < 0.0f || radius2 < 0.0f || length < 0.0f || slices < 2 || stacks < 1 || mesh == NULL)
4619 return D3DERR_INVALIDCALL;
4624 FIXME("Case of adjacency != NULL not implemented.\n");
4628 number_of_vertices = 2 + (slices * (3 + stacks));
4629 number_of_faces = 2 * slices + stacks * (2 * slices);
4631 hr = D3DXCreateMeshFVF(number_of_faces, number_of_vertices, D3DXMESH_MANAGED,
4632 D3DFVF_XYZ | D3DFVF_NORMAL, device, &cylinder);
4638 hr = cylinder->lpVtbl->LockVertexBuffer(cylinder, 0, (LPVOID *)&vertices);
4641 cylinder->lpVtbl->Release(cylinder);
4645 hr = cylinder->lpVtbl->LockIndexBuffer(cylinder, 0, (LPVOID *)&faces);
4648 cylinder->lpVtbl->UnlockVertexBuffer(cylinder);
4649 cylinder->lpVtbl->Release(cylinder);
4653 /* theta = angle on xy plane wrt x axis */
4654 theta_step = -2 * M_PI / slices;
4655 theta_start = M_PI / 2;
4657 if (!compute_sincos_table(&theta, theta_start, theta_step, slices))
4659 cylinder->lpVtbl->UnlockIndexBuffer(cylinder);
4660 cylinder->lpVtbl->UnlockVertexBuffer(cylinder);
4661 cylinder->lpVtbl->Release(cylinder);
4662 return E_OUTOFMEMORY;
4668 delta_radius = radius1 - radius2;
4670 radius_step = delta_radius / stacks;
4673 z_step = length / stacks;
4674 z_normal = delta_radius / length;
4675 if (isnan(z_normal))
4680 vertices[vertex].normal.x = 0.0f;
4681 vertices[vertex].normal.y = 0.0f;
4682 vertices[vertex].normal.z = -1.0f;
4683 vertices[vertex].position.x = 0.0f;
4684 vertices[vertex].position.y = 0.0f;
4685 vertices[vertex++].position.z = z;
4687 for (slice = 0; slice < slices; slice++, vertex++)
4689 vertices[vertex].normal.x = 0.0f;
4690 vertices[vertex].normal.y = 0.0f;
4691 vertices[vertex].normal.z = -1.0f;
4692 vertices[vertex].position.x = radius * theta.cos[slice];
4693 vertices[vertex].position.y = radius * theta.sin[slice];
4694 vertices[vertex].position.z = z;
4699 faces[face][1] = slice;
4700 faces[face++][2] = slice + 1;
4705 faces[face][1] = slice;
4706 faces[face++][2] = 1;
4708 for (stack = 1; stack <= stacks+1; stack++)
4710 for (slice = 0; slice < slices; slice++, vertex++)
4712 vertices[vertex].normal.x = theta.cos[slice];
4713 vertices[vertex].normal.y = theta.sin[slice];
4714 vertices[vertex].normal.z = z_normal;
4715 D3DXVec3Normalize(&vertices[vertex].normal, &vertices[vertex].normal);
4716 vertices[vertex].position.x = radius * theta.cos[slice];
4717 vertices[vertex].position.y = radius * theta.sin[slice];
4718 vertices[vertex].position.z = z;
4720 if (stack > 1 && slice > 0)
4722 faces[face][0] = vertex_index(slices, slice-1, stack-1);
4723 faces[face][1] = vertex_index(slices, slice-1, stack);
4724 faces[face++][2] = vertex_index(slices, slice, stack-1);
4726 faces[face][0] = vertex_index(slices, slice, stack-1);
4727 faces[face][1] = vertex_index(slices, slice-1, stack);
4728 faces[face++][2] = vertex_index(slices, slice, stack);
4734 faces[face][0] = vertex_index(slices, slice-1, stack-1);
4735 faces[face][1] = vertex_index(slices, slice-1, stack);
4736 faces[face++][2] = vertex_index(slices, 0, stack-1);
4738 faces[face][0] = vertex_index(slices, 0, stack-1);
4739 faces[face][1] = vertex_index(slices, slice-1, stack);
4740 faces[face++][2] = vertex_index(slices, 0, stack);
4743 if (stack < stacks + 1)
4746 radius -= radius_step;
4750 for (slice = 0; slice < slices; slice++, vertex++)
4752 vertices[vertex].normal.x = 0.0f;
4753 vertices[vertex].normal.y = 0.0f;
4754 vertices[vertex].normal.z = 1.0f;
4755 vertices[vertex].position.x = radius * theta.cos[slice];
4756 vertices[vertex].position.y = radius * theta.sin[slice];
4757 vertices[vertex].position.z = z;
4761 faces[face][0] = vertex_index(slices, slice-1, stack);
4762 faces[face][1] = number_of_vertices - 1;
4763 faces[face++][2] = vertex_index(slices, slice, stack);
4767 vertices[vertex].position.x = 0.0f;
4768 vertices[vertex].position.y = 0.0f;
4769 vertices[vertex].position.z = z;
4770 vertices[vertex].normal.x = 0.0f;
4771 vertices[vertex].normal.y = 0.0f;
4772 vertices[vertex].normal.z = 1.0f;
4774 faces[face][0] = vertex_index(slices, slice-1, stack);
4775 faces[face][1] = number_of_vertices - 1;
4776 faces[face][2] = vertex_index(slices, 0, stack);
4778 free_sincos_table(&theta);
4779 cylinder->lpVtbl->UnlockIndexBuffer(cylinder);
4780 cylinder->lpVtbl->UnlockVertexBuffer(cylinder);
4786 HRESULT WINAPI D3DXCreateTeapot(LPDIRECT3DDEVICE9 device, LPD3DXMESH *mesh, LPD3DXBUFFER* adjacency)
4788 FIXME("(%p, %p, %p): stub\n", device, mesh, adjacency);
4793 HRESULT WINAPI D3DXCreateTextA(LPDIRECT3DDEVICE9 device,
4794 HDC hdc, LPCSTR text,
4795 FLOAT deviation, FLOAT extrusion,
4796 LPD3DXMESH *mesh, LPD3DXBUFFER *adjacency,
4797 LPGLYPHMETRICSFLOAT glyphmetrics)
4803 TRACE("(%p, %p, %s, %f, %f, %p, %p, %p)\n", device, hdc,
4804 debugstr_a(text), deviation, extrusion, mesh, adjacency, glyphmetrics);
4807 return D3DERR_INVALIDCALL;
4809 len = MultiByteToWideChar(CP_ACP, 0, text, -1, NULL, 0);
4810 textW = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
4811 MultiByteToWideChar(CP_ACP, 0, text, -1, textW, len);
4813 hr = D3DXCreateTextW(device, hdc, textW, deviation, extrusion,
4814 mesh, adjacency, glyphmetrics);
4815 HeapFree(GetProcessHeap(), 0, textW);
4821 POINTTYPE_CURVE = 0,
4823 POINTTYPE_CURVE_START,
4824 POINTTYPE_CURVE_END,
4825 POINTTYPE_CURVE_MIDDLE,
4831 enum pointtype corner;
4834 struct dynamic_array
4836 int count, capacity;
4840 /* is a dynamic_array */
4843 int count, capacity;
4844 struct point2d *items;
4847 /* is a dynamic_array */
4848 struct outline_array
4850 int count, capacity;
4851 struct outline *items;
4860 struct point2d_index
4862 struct outline *outline;
4866 struct point2d_index_array
4869 struct point2d_index *items;
4874 struct outline_array outlines;
4875 struct face_array faces;
4876 struct point2d_index_array ordered_vertices;
4880 /* is an dynamic_array */
4883 int count, capacity;
4887 /* complex polygons are split into monotone polygons, which have
4888 * at most 2 intersections with the vertical sweep line */
4889 struct triangulation
4891 struct word_array vertex_stack;
4892 BOOL last_on_top, merging;
4895 /* is an dynamic_array */
4896 struct triangulation_array
4898 int count, capacity;
4899 struct triangulation *items;
4901 struct glyphinfo *glyph;
4904 static BOOL reserve(struct dynamic_array *array, int count, int itemsize)
4906 if (count > array->capacity) {
4909 if (array->items && array->capacity) {
4910 new_capacity = max(array->capacity * 2, count);
4911 new_buffer = HeapReAlloc(GetProcessHeap(), 0, array->items, new_capacity * itemsize);
4913 new_capacity = max(16, count);
4914 new_buffer = HeapAlloc(GetProcessHeap(), 0, new_capacity * itemsize);
4918 array->items = new_buffer;
4919 array->capacity = new_capacity;
4924 static struct point2d *add_points(struct outline *array, int num)
4926 struct point2d *item;
4928 if (!reserve((struct dynamic_array *)array, array->count + num, sizeof(array->items[0])))
4931 item = &array->items[array->count];
4932 array->count += num;
4936 static struct outline *add_outline(struct outline_array *array)
4938 struct outline *item;
4940 if (!reserve((struct dynamic_array *)array, array->count + 1, sizeof(array->items[0])))
4943 item = &array->items[array->count++];
4944 ZeroMemory(item, sizeof(*item));
4948 static inline face *add_face(struct face_array *array)
4950 return &array->items[array->count++];
4953 static struct triangulation *add_triangulation(struct triangulation_array *array)
4955 struct triangulation *item;
4957 if (!reserve((struct dynamic_array *)array, array->count + 1, sizeof(array->items[0])))
4960 item = &array->items[array->count++];
4961 ZeroMemory(item, sizeof(*item));
4965 static HRESULT add_vertex_index(struct word_array *array, WORD vertex_index)
4967 if (!reserve((struct dynamic_array *)array, array->count + 1, sizeof(array->items[0])))
4968 return E_OUTOFMEMORY;
4970 array->items[array->count++] = vertex_index;
4974 /* assume fixed point numbers can be converted to float point in place */
4975 C_ASSERT(sizeof(FIXED) == sizeof(float));
4976 C_ASSERT(sizeof(POINTFX) == sizeof(D3DXVECTOR2));
4978 static inline D3DXVECTOR2 *convert_fixed_to_float(POINTFX *pt, int count, float emsquare)
4980 D3DXVECTOR2 *ret = (D3DXVECTOR2*)pt;
4982 D3DXVECTOR2 *pt_flt = (D3DXVECTOR2*)pt;
4983 pt_flt->x = (pt->x.value + pt->x.fract / (float)0x10000) / emsquare;
4984 pt_flt->y = (pt->y.value + pt->y.fract / (float)0x10000) / emsquare;
4990 static HRESULT add_bezier_points(struct outline *outline, const D3DXVECTOR2 *p1,
4991 const D3DXVECTOR2 *p2, const D3DXVECTOR2 *p3,
4992 float max_deviation_sq)
4994 D3DXVECTOR2 split1 = {0, 0}, split2 = {0, 0}, middle, vec;
4997 D3DXVec2Scale(&split1, D3DXVec2Add(&split1, p1, p2), 0.5f);
4998 D3DXVec2Scale(&split2, D3DXVec2Add(&split2, p2, p3), 0.5f);
4999 D3DXVec2Scale(&middle, D3DXVec2Add(&middle, &split1, &split2), 0.5f);
5001 deviation_sq = D3DXVec2LengthSq(D3DXVec2Subtract(&vec, &middle, p2));
5002 if (deviation_sq < max_deviation_sq) {
5003 struct point2d *pt = add_points(outline, 1);
5004 if (!pt) return E_OUTOFMEMORY;
5006 pt->corner = POINTTYPE_CURVE;
5007 /* the end point is omitted because the end line merges into the next segment of
5008 * the split bezier curve, and the end of the split bezier curve is added outside
5009 * this recursive function. */
5011 HRESULT hr = add_bezier_points(outline, p1, &split1, &middle, max_deviation_sq);
5012 if (hr != S_OK) return hr;
5013 hr = add_bezier_points(outline, &middle, &split2, p3, max_deviation_sq);
5014 if (hr != S_OK) return hr;
5020 static inline BOOL is_direction_similar(D3DXVECTOR2 *dir1, D3DXVECTOR2 *dir2, float cos_theta)
5022 /* dot product = cos(theta) */
5023 return D3DXVec2Dot(dir1, dir2) > cos_theta;
5026 static inline D3DXVECTOR2 *unit_vec2(D3DXVECTOR2 *dir, const D3DXVECTOR2 *pt1, const D3DXVECTOR2 *pt2)
5028 return D3DXVec2Normalize(D3DXVec2Subtract(dir, pt2, pt1), dir);
5038 static BOOL attempt_line_merge(struct outline *outline,
5040 const D3DXVECTOR2 *nextpt,
5042 const struct cos_table *table)
5044 D3DXVECTOR2 curdir, lastdir;
5045 struct point2d *prevpt, *pt;
5048 pt = &outline->items[pt_index];
5049 pt_index = (pt_index - 1 + outline->count) % outline->count;
5050 prevpt = &outline->items[pt_index];
5053 pt->corner = pt->corner != POINTTYPE_CORNER ? POINTTYPE_CURVE_MIDDLE : POINTTYPE_CURVE_START;
5055 if (outline->count < 2)
5058 /* remove last point if the next line continues the last line */
5059 unit_vec2(&lastdir, &prevpt->pos, &pt->pos);
5060 unit_vec2(&curdir, &pt->pos, nextpt);
5061 if (is_direction_similar(&lastdir, &curdir, table->cos_half))
5064 if (pt->corner == POINTTYPE_CURVE_END)
5065 prevpt->corner = pt->corner;
5066 if (prevpt->corner == POINTTYPE_CURVE_END && to_curve)
5067 prevpt->corner = POINTTYPE_CURVE_MIDDLE;
5071 if (outline->count < 2)
5074 pt_index = (pt_index - 1 + outline->count) % outline->count;
5075 prevpt = &outline->items[pt_index];
5076 unit_vec2(&lastdir, &prevpt->pos, &pt->pos);
5077 unit_vec2(&curdir, &pt->pos, nextpt);
5082 static HRESULT create_outline(struct glyphinfo *glyph, void *raw_outline, int datasize,
5083 float max_deviation_sq, float emsquare, const struct cos_table *cos_table)
5085 TTPOLYGONHEADER *header = (TTPOLYGONHEADER *)raw_outline;
5087 while ((char *)header < (char *)raw_outline + datasize)
5089 TTPOLYCURVE *curve = (TTPOLYCURVE *)(header + 1);
5090 struct point2d *lastpt, *pt;
5091 D3DXVECTOR2 lastdir;
5092 D3DXVECTOR2 *pt_flt;
5094 struct outline *outline = add_outline(&glyph->outlines);
5097 return E_OUTOFMEMORY;
5099 pt = add_points(outline, 1);
5101 return E_OUTOFMEMORY;
5102 pt_flt = convert_fixed_to_float(&header->pfxStart, 1, emsquare);
5104 pt->corner = POINTTYPE_CORNER;
5106 if (header->dwType != TT_POLYGON_TYPE)
5107 FIXME("Unknown header type %d\n", header->dwType);
5109 while ((char *)curve < (char *)header + header->cb)
5111 D3DXVECTOR2 bezier_start = outline->items[outline->count - 1].pos;
5112 BOOL to_curve = curve->wType != TT_PRIM_LINE && curve->cpfx > 1;
5115 curve = (TTPOLYCURVE *)&curve->apfx[curve->cpfx];
5119 pt_flt = convert_fixed_to_float(curve->apfx, curve->cpfx, emsquare);
5121 attempt_line_merge(outline, outline->count - 1, &pt_flt[0], to_curve, cos_table);
5126 int count = curve->cpfx;
5131 D3DXVECTOR2 bezier_end;
5133 D3DXVec2Scale(&bezier_end, D3DXVec2Add(&bezier_end, &pt_flt[j], &pt_flt[j+1]), 0.5f);
5134 hr = add_bezier_points(outline, &bezier_start, &pt_flt[j], &bezier_end, max_deviation_sq);
5137 bezier_start = bezier_end;
5141 hr = add_bezier_points(outline, &bezier_start, &pt_flt[j], &pt_flt[j+1], max_deviation_sq);
5145 pt = add_points(outline, 1);
5147 return E_OUTOFMEMORY;
5149 pt->pos = pt_flt[j];
5150 pt->corner = POINTTYPE_CURVE_END;
5152 pt = add_points(outline, curve->cpfx);
5154 return E_OUTOFMEMORY;
5155 for (j = 0; j < curve->cpfx; j++)
5157 pt->pos = pt_flt[j];
5158 pt->corner = POINTTYPE_CORNER;
5163 curve = (TTPOLYCURVE *)&curve->apfx[curve->cpfx];
5166 /* remove last point if the next line continues the last line */
5167 if (outline->count >= 3) {
5170 lastpt = &outline->items[outline->count - 1];
5171 pt = &outline->items[0];
5172 if (pt->pos.x == lastpt->pos.x && pt->pos.y == lastpt->pos.y) {
5173 if (lastpt->corner == POINTTYPE_CURVE_END)
5175 if (pt->corner == POINTTYPE_CURVE_START)
5176 pt->corner = POINTTYPE_CURVE_MIDDLE;
5178 pt->corner = POINTTYPE_CURVE_END;
5181 lastpt = &outline->items[outline->count - 1];
5183 /* outline closed with a line from end to start point */
5184 attempt_line_merge(outline, outline->count - 1, &pt->pos, FALSE, cos_table);
5186 lastpt = &outline->items[0];
5187 to_curve = lastpt->corner != POINTTYPE_CORNER && lastpt->corner != POINTTYPE_CURVE_END;
5188 if (lastpt->corner == POINTTYPE_CURVE_START)
5189 lastpt->corner = POINTTYPE_CORNER;
5190 pt = &outline->items[1];
5191 if (attempt_line_merge(outline, 0, &pt->pos, to_curve, cos_table))
5192 *lastpt = outline->items[outline->count];
5195 lastpt = &outline->items[outline->count - 1];
5196 pt = &outline->items[0];
5197 unit_vec2(&lastdir, &lastpt->pos, &pt->pos);
5198 for (j = 0; j < outline->count; j++)
5203 pt = &outline->items[(j + 1) % outline->count];
5204 unit_vec2(&curdir, &lastpt->pos, &pt->pos);
5206 switch (lastpt->corner)
5208 case POINTTYPE_CURVE_START:
5209 case POINTTYPE_CURVE_END:
5210 if (!is_direction_similar(&lastdir, &curdir, cos_table->cos_45))
5211 lastpt->corner = POINTTYPE_CORNER;
5213 case POINTTYPE_CURVE_MIDDLE:
5214 if (!is_direction_similar(&lastdir, &curdir, cos_table->cos_90))
5215 lastpt->corner = POINTTYPE_CORNER;
5217 lastpt->corner = POINTTYPE_CURVE;
5225 header = (TTPOLYGONHEADER *)((char *)header + header->cb);
5230 /* Get the y-distance from a line to a point */
5231 static float get_line_to_point_y_distance(D3DXVECTOR2 *line_pt1,
5232 D3DXVECTOR2 *line_pt2,
5235 D3DXVECTOR2 line_vec = {0, 0};
5239 D3DXVec2Subtract(&line_vec, line_pt2, line_pt1);
5240 line_pt_dx = point->x - line_pt1->x;
5241 line_y = line_pt1->y + (line_vec.y * line_pt_dx) / line_vec.x;
5242 return point->y - line_y;
5245 static D3DXVECTOR2 *get_indexed_point(struct point2d_index *pt_idx)
5247 return &pt_idx->outline->items[pt_idx->vertex].pos;
5250 static D3DXVECTOR2 *get_ordered_vertex(struct glyphinfo *glyph, WORD index)
5252 return get_indexed_point(&glyph->ordered_vertices.items[index]);
5255 static void remove_triangulation(struct triangulation_array *array, struct triangulation *item)
5257 HeapFree(GetProcessHeap(), 0, item->vertex_stack.items);
5258 MoveMemory(item, item + 1, (char*)&array->items[array->count] - (char*)(item + 1));
5262 static HRESULT triangulation_add_point(struct triangulation **t_ptr,
5263 struct triangulation_array *triangulations,
5267 struct glyphinfo *glyph = triangulations->glyph;
5268 struct triangulation *t = *t_ptr;
5273 if (t->last_on_top) {
5281 if (t->last_on_top != to_top && t->vertex_stack.count > 1) {
5282 /* consume all vertices on the stack */
5283 WORD last_pt = t->vertex_stack.items[0];
5285 for (i = 1; i < t->vertex_stack.count; i++)
5287 face = add_face(&glyph->faces);
5288 if (!face) return E_OUTOFMEMORY;
5289 (*face)[0] = vtx_idx;
5290 (*face)[f1] = last_pt;
5291 (*face)[f2] = last_pt = t->vertex_stack.items[i];
5293 t->vertex_stack.items[0] = last_pt;
5294 t->vertex_stack.count = 1;
5295 } else if (t->vertex_stack.count > 1) {
5296 int i = t->vertex_stack.count - 1;
5297 D3DXVECTOR2 *point = get_ordered_vertex(glyph, vtx_idx);
5298 WORD top_idx = t->vertex_stack.items[i--];
5299 D3DXVECTOR2 *top_pt = get_ordered_vertex(glyph, top_idx);
5303 WORD prev_idx = t->vertex_stack.items[i--];
5304 D3DXVECTOR2 *prev_pt = get_ordered_vertex(glyph, prev_idx);
5306 if (prev_pt->x != top_pt->x &&
5307 ((to_top && get_line_to_point_y_distance(prev_pt, top_pt, point) > 0) ||
5308 (!to_top && get_line_to_point_y_distance(prev_pt, top_pt, point) < 0)))
5311 face = add_face(&glyph->faces);
5312 if (!face) return E_OUTOFMEMORY;
5313 (*face)[0] = vtx_idx;
5314 (*face)[f1] = prev_idx;
5315 (*face)[f2] = top_idx;
5319 t->vertex_stack.count--;
5322 t->last_on_top = to_top;
5324 hr = add_vertex_index(&t->vertex_stack, vtx_idx);
5326 if (hr == S_OK && t->merging) {
5327 struct triangulation *t2;
5329 t2 = to_top ? t - 1 : t + 1;
5330 t2->merging = FALSE;
5331 hr = triangulation_add_point(&t2, triangulations, vtx_idx, to_top);
5332 if (hr != S_OK) return hr;
5333 remove_triangulation(triangulations, t);
5341 /* check if the point is next on the outline for either the top or bottom */
5342 static D3DXVECTOR2 *triangulation_get_next_point(struct triangulation *t, struct glyphinfo *glyph, BOOL on_top)
5344 int i = t->last_on_top == on_top ? t->vertex_stack.count - 1 : 0;
5345 WORD idx = t->vertex_stack.items[i];
5346 struct point2d_index *pt_idx = &glyph->ordered_vertices.items[idx];
5347 struct outline *outline = pt_idx->outline;
5350 i = (pt_idx->vertex + outline->count - 1) % outline->count;
5352 i = (pt_idx->vertex + 1) % outline->count;
5354 return &outline->items[i].pos;
5357 static int compare_vertex_indices(const void *a, const void *b)
5359 const struct point2d_index *idx1 = a, *idx2 = b;
5360 const D3DXVECTOR2 *p1 = &idx1->outline->items[idx1->vertex].pos;
5361 const D3DXVECTOR2 *p2 = &idx2->outline->items[idx2->vertex].pos;
5362 float diff = p1->x - p2->x;
5365 diff = p1->y - p2->y;
5367 return diff == 0.0f ? 0 : (diff > 0.0f ? -1 : 1);
5370 static HRESULT triangulate(struct triangulation_array *triangulations)
5374 struct glyphinfo *glyph = triangulations->glyph;
5375 int nb_vertices = 0;
5377 struct point2d_index *idx_ptr;
5379 for (i = 0; i < glyph->outlines.count; i++)
5380 nb_vertices += glyph->outlines.items[i].count;
5382 glyph->ordered_vertices.items = HeapAlloc(GetProcessHeap(), 0,
5383 nb_vertices * sizeof(*glyph->ordered_vertices.items));
5384 if (!glyph->ordered_vertices.items)
5385 return E_OUTOFMEMORY;
5387 idx_ptr = glyph->ordered_vertices.items;
5388 for (i = 0; i < glyph->outlines.count; i++)
5390 struct outline *outline = &glyph->outlines.items[i];
5393 idx_ptr->outline = outline;
5394 idx_ptr->vertex = 0;
5396 for (j = outline->count - 1; j > 0; j--)
5398 idx_ptr->outline = outline;
5399 idx_ptr->vertex = j;
5403 glyph->ordered_vertices.count = nb_vertices;
5405 /* Native implementation seems to try to create a triangle fan from
5406 * the first outline point if the glyph only has one outline. */
5407 if (glyph->outlines.count == 1)
5409 struct outline *outline = glyph->outlines.items;
5410 D3DXVECTOR2 *base = &outline->items[0].pos;
5411 D3DXVECTOR2 *last = &outline->items[1].pos;
5414 for (i = 2; i < outline->count; i++)
5416 D3DXVECTOR2 *next = &outline->items[i].pos;
5417 D3DXVECTOR2 v1 = {0.0f, 0.0f};
5418 D3DXVECTOR2 v2 = {0.0f, 0.0f};
5420 D3DXVec2Subtract(&v1, base, last);
5421 D3DXVec2Subtract(&v2, last, next);
5422 ccw = D3DXVec2CCW(&v1, &v2);
5430 glyph->faces.items = HeapAlloc(GetProcessHeap(), 0,
5431 (outline->count - 2) * sizeof(glyph->faces.items[0]));
5432 if (!glyph->faces.items)
5433 return E_OUTOFMEMORY;
5435 glyph->faces.count = outline->count - 2;
5436 for (i = 0; i < glyph->faces.count; i++)
5438 glyph->faces.items[i][0] = 0;
5439 glyph->faces.items[i][1] = i + 1;
5440 glyph->faces.items[i][2] = i + 2;
5446 /* Perform 2D polygon triangulation for complex glyphs.
5447 * Triangulation is performed using a sweep line concept, from right to left,
5448 * by processing vertices in sorted order. Complex polygons are split into
5449 * monotone polygons which are triangulated separately. */
5450 /* FIXME: The order of the faces is not consistent with the native implementation. */
5452 /* Reserve space for maximum possible faces from triangulation.
5453 * # faces for outer outlines = outline->count - 2
5454 * # faces for inner outlines = outline->count + 2
5455 * There must be at least 1 outer outline. */
5456 glyph->faces.items = HeapAlloc(GetProcessHeap(), 0,
5457 (nb_vertices + glyph->outlines.count * 2 - 4) * sizeof(glyph->faces.items[0]));
5458 if (!glyph->faces.items)
5459 return E_OUTOFMEMORY;
5461 qsort(glyph->ordered_vertices.items, nb_vertices,
5462 sizeof(glyph->ordered_vertices.items[0]), compare_vertex_indices);
5463 for (sweep_idx = 0; sweep_idx < glyph->ordered_vertices.count; sweep_idx++)
5466 int end = triangulations->count;
5470 D3DXVECTOR2 *sweep_vtx = get_ordered_vertex(glyph, sweep_idx);
5471 int current = (start + end) / 2;
5472 struct triangulation *t = &triangulations->items[current];
5473 BOOL on_top_outline = FALSE;
5474 D3DXVECTOR2 *top_next, *bottom_next;
5475 WORD top_idx, bottom_idx;
5477 if (t->merging && t->last_on_top)
5478 top_next = triangulation_get_next_point(t + 1, glyph, TRUE);
5480 top_next = triangulation_get_next_point(t, glyph, TRUE);
5481 if (sweep_vtx == top_next)
5483 if (t->merging && t->last_on_top)
5485 hr = triangulation_add_point(&t, triangulations, sweep_idx, TRUE);
5486 if (hr != S_OK) return hr;
5488 if (t + 1 < &triangulations->items[triangulations->count] &&
5489 triangulation_get_next_point(t + 1, glyph, FALSE) == sweep_vtx)
5491 /* point also on bottom outline of higher triangulation */
5492 struct triangulation *t2 = t + 1;
5493 hr = triangulation_add_point(&t2, triangulations, sweep_idx, FALSE);
5494 if (hr != S_OK) return hr;
5499 on_top_outline = TRUE;
5502 if (t->merging && !t->last_on_top)
5503 bottom_next = triangulation_get_next_point(t - 1, glyph, FALSE);
5505 bottom_next = triangulation_get_next_point(t, glyph, FALSE);
5506 if (sweep_vtx == bottom_next)
5508 if (t->merging && !t->last_on_top)
5510 if (on_top_outline) {
5511 /* outline finished */
5512 remove_triangulation(triangulations, t);
5516 hr = triangulation_add_point(&t, triangulations, sweep_idx, FALSE);
5517 if (hr != S_OK) return hr;
5519 if (t > triangulations->items &&
5520 triangulation_get_next_point(t - 1, glyph, TRUE) == sweep_vtx)
5522 struct triangulation *t2 = t - 1;
5523 /* point also on top outline of lower triangulation */
5524 hr = triangulation_add_point(&t2, triangulations, sweep_idx, TRUE);
5525 if (hr != S_OK) return hr;
5526 t = t2 + 1; /* t may be invalidated by triangulation merging */
5536 if (t->last_on_top) {
5537 top_idx = t->vertex_stack.items[t->vertex_stack.count - 1];
5538 bottom_idx = t->vertex_stack.items[0];
5540 top_idx = t->vertex_stack.items[0];
5541 bottom_idx = t->vertex_stack.items[t->vertex_stack.count - 1];
5544 /* check if the point is inside or outside this polygon */
5545 if (get_line_to_point_y_distance(get_ordered_vertex(glyph, top_idx),
5546 top_next, sweep_vtx) > 0)
5548 start = current + 1;
5549 } else if (get_line_to_point_y_distance(get_ordered_vertex(glyph, bottom_idx),
5550 bottom_next, sweep_vtx) < 0)
5553 } else if (t->merging) {
5554 /* inside, so cancel merging */
5555 struct triangulation *t2 = t->last_on_top ? t + 1 : t - 1;
5557 t2->merging = FALSE;
5558 hr = triangulation_add_point(&t, triangulations, sweep_idx, t->last_on_top);
5559 if (hr != S_OK) return hr;
5560 hr = triangulation_add_point(&t2, triangulations, sweep_idx, t2->last_on_top);
5561 if (hr != S_OK) return hr;
5564 /* inside, so split polygon into two monotone parts */
5565 struct triangulation *t2 = add_triangulation(triangulations);
5566 if (!t2) return E_OUTOFMEMORY;
5567 MoveMemory(t + 1, t, (char*)(t2 + 1) - (char*)t);
5568 if (t->last_on_top) {
5575 ZeroMemory(&t2->vertex_stack, sizeof(t2->vertex_stack));
5576 hr = add_vertex_index(&t2->vertex_stack, t->vertex_stack.items[t->vertex_stack.count - 1]);
5577 if (hr != S_OK) return hr;
5578 hr = add_vertex_index(&t2->vertex_stack, sweep_idx);
5579 if (hr != S_OK) return hr;
5580 t2->last_on_top = !t->last_on_top;
5582 hr = triangulation_add_point(&t, triangulations, sweep_idx, t->last_on_top);
5583 if (hr != S_OK) return hr;
5589 struct triangulation *t;
5590 struct triangulation *t2 = add_triangulation(triangulations);
5591 if (!t2) return E_OUTOFMEMORY;
5592 t = &triangulations->items[start];
5593 MoveMemory(t + 1, t, (char*)(t2 + 1) - (char*)t);
5594 ZeroMemory(t, sizeof(*t));
5595 hr = add_vertex_index(&t->vertex_stack, sweep_idx);
5596 if (hr != S_OK) return hr;
5602 HRESULT WINAPI D3DXCreateTextW(LPDIRECT3DDEVICE9 device,
5603 HDC hdc, LPCWSTR text,
5604 FLOAT deviation, FLOAT extrusion,
5605 LPD3DXMESH *mesh_ptr, LPD3DXBUFFER *adjacency,
5606 LPGLYPHMETRICSFLOAT glyphmetrics)
5609 ID3DXMesh *mesh = NULL;
5610 DWORD nb_vertices, nb_faces;
5611 DWORD nb_front_faces, nb_corners, nb_outline_points;
5612 struct vertex *vertices = NULL;
5617 OUTLINETEXTMETRICW otm;
5618 HFONT font = NULL, oldfont = NULL;
5619 const MAT2 identity = {{0, 1}, {0, 0}, {0, 0}, {0, 1}};
5620 void *raw_outline = NULL;
5622 struct glyphinfo *glyphs = NULL;
5624 struct triangulation_array triangulations = {0, 0, NULL};
5626 struct vertex *vertex_ptr;
5628 float max_deviation_sq;
5629 const struct cos_table cos_table = {
5630 cos(D3DXToRadian(0.5f)),
5631 cos(D3DXToRadian(45.0f)),
5632 cos(D3DXToRadian(90.0f)),
5636 TRACE("(%p, %p, %s, %f, %f, %p, %p, %p)\n", device, hdc,
5637 debugstr_w(text), deviation, extrusion, mesh_ptr, adjacency, glyphmetrics);
5639 if (!device || !hdc || !text || !*text || deviation < 0.0f || extrusion < 0.0f || !mesh_ptr)
5640 return D3DERR_INVALIDCALL;
5644 FIXME("Case of adjacency != NULL not implemented.\n");
5648 if (!GetObjectW(GetCurrentObject(hdc, OBJ_FONT), sizeof(lf), &lf) ||
5649 !GetOutlineTextMetricsW(hdc, sizeof(otm), &otm))
5651 return D3DERR_INVALIDCALL;
5654 if (deviation == 0.0f)
5655 deviation = 1.0f / otm.otmEMSquare;
5656 max_deviation_sq = deviation * deviation;
5658 lf.lfHeight = otm.otmEMSquare;
5660 font = CreateFontIndirectW(&lf);
5665 oldfont = SelectObject(hdc, font);
5667 textlen = strlenW(text);
5668 for (i = 0; i < textlen; i++)
5670 int datasize = GetGlyphOutlineW(hdc, text[i], GGO_NATIVE, &gm, 0, NULL, &identity);
5672 return D3DERR_INVALIDCALL;
5673 if (bufsize < datasize)
5676 if (!bufsize) { /* e.g. text == " " */
5677 hr = D3DERR_INVALIDCALL;
5681 glyphs = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, textlen * sizeof(*glyphs));
5682 raw_outline = HeapAlloc(GetProcessHeap(), 0, bufsize);
5683 if (!glyphs || !raw_outline) {
5689 for (i = 0; i < textlen; i++)
5691 /* get outline points from data returned from GetGlyphOutline */
5694 glyphs[i].offset_x = offset_x;
5696 datasize = GetGlyphOutlineW(hdc, text[i], GGO_NATIVE, &gm, bufsize, raw_outline, &identity);
5697 hr = create_outline(&glyphs[i], raw_outline, datasize,
5698 max_deviation_sq, otm.otmEMSquare, &cos_table);
5699 if (hr != S_OK) goto error;
5701 triangulations.glyph = &glyphs[i];
5702 hr = triangulate(&triangulations);
5703 if (hr != S_OK) goto error;
5704 if (triangulations.count) {
5705 ERR("%d incomplete triangulations of glyph (%u).\n", triangulations.count, text[i]);
5706 triangulations.count = 0;
5711 glyphmetrics[i].gmfBlackBoxX = gm.gmBlackBoxX / (float)otm.otmEMSquare;
5712 glyphmetrics[i].gmfBlackBoxY = gm.gmBlackBoxY / (float)otm.otmEMSquare;
5713 glyphmetrics[i].gmfptGlyphOrigin.x = gm.gmptGlyphOrigin.x / (float)otm.otmEMSquare;
5714 glyphmetrics[i].gmfptGlyphOrigin.y = gm.gmptGlyphOrigin.y / (float)otm.otmEMSquare;
5715 glyphmetrics[i].gmfCellIncX = gm.gmCellIncX / (float)otm.otmEMSquare;
5716 glyphmetrics[i].gmfCellIncY = gm.gmCellIncY / (float)otm.otmEMSquare;
5718 offset_x += gm.gmCellIncX / (float)otm.otmEMSquare;
5721 /* corner points need an extra vertex for the different side faces normals */
5723 nb_outline_points = 0;
5725 for (i = 0; i < textlen; i++)
5728 nb_outline_points += glyphs[i].ordered_vertices.count;
5729 nb_front_faces += glyphs[i].faces.count;
5730 for (j = 0; j < glyphs[i].outlines.count; j++)
5733 struct outline *outline = &glyphs[i].outlines.items[j];
5734 nb_corners++; /* first outline point always repeated as a corner */
5735 for (k = 1; k < outline->count; k++)
5736 if (outline->items[k].corner)
5741 nb_vertices = (nb_outline_points + nb_corners) * 2 + nb_outline_points * 2;
5742 nb_faces = nb_outline_points * 2 + nb_front_faces * 2;
5745 hr = D3DXCreateMeshFVF(nb_faces, nb_vertices, D3DXMESH_MANAGED,
5746 D3DFVF_XYZ | D3DFVF_NORMAL, device, &mesh);
5750 hr = mesh->lpVtbl->LockVertexBuffer(mesh, 0, (LPVOID *)&vertices);
5754 hr = mesh->lpVtbl->LockIndexBuffer(mesh, 0, (LPVOID *)&faces);
5758 /* convert 2D vertices and faces into 3D mesh */
5759 vertex_ptr = vertices;
5761 if (extrusion == 0.0f) {
5768 for (i = 0; i < textlen; i++)
5772 struct vertex *back_vertices;
5775 /* side vertices and faces */
5776 for (j = 0; j < glyphs[i].outlines.count; j++)
5778 struct vertex *outline_vertices = vertex_ptr;
5779 struct outline *outline = &glyphs[i].outlines.items[j];
5781 struct point2d *prevpt = &outline->items[outline->count - 1];
5782 struct point2d *pt = &outline->items[0];
5784 for (k = 1; k <= outline->count; k++)
5787 struct point2d *nextpt = &outline->items[k % outline->count];
5788 WORD vtx_idx = vertex_ptr - vertices;
5791 if (pt->corner == POINTTYPE_CURVE_START)
5792 D3DXVec2Subtract(&vec, &pt->pos, &prevpt->pos);
5793 else if (pt->corner)
5794 D3DXVec2Subtract(&vec, &nextpt->pos, &pt->pos);
5796 D3DXVec2Subtract(&vec, &nextpt->pos, &prevpt->pos);
5797 D3DXVec2Normalize(&vec, &vec);
5798 vtx.normal.x = -vec.y;
5799 vtx.normal.y = vec.x;
5802 vtx.position.x = pt->pos.x + glyphs[i].offset_x;
5803 vtx.position.y = pt->pos.y;
5805 *vertex_ptr++ = vtx;
5807 vtx.position.z = -extrusion;
5808 *vertex_ptr++ = vtx;
5810 vtx.position.x = nextpt->pos.x + glyphs[i].offset_x;
5811 vtx.position.y = nextpt->pos.y;
5812 if (pt->corner && nextpt->corner && nextpt->corner != POINTTYPE_CURVE_END) {
5813 vtx.position.z = -extrusion;
5814 *vertex_ptr++ = vtx;
5816 *vertex_ptr++ = vtx;
5818 (*face_ptr)[0] = vtx_idx;
5819 (*face_ptr)[1] = vtx_idx + 2;
5820 (*face_ptr)[2] = vtx_idx + 1;
5823 (*face_ptr)[0] = vtx_idx;
5824 (*face_ptr)[1] = vtx_idx + 3;
5825 (*face_ptr)[2] = vtx_idx + 2;
5828 if (nextpt->corner) {
5829 if (nextpt->corner == POINTTYPE_CURVE_END) {
5830 D3DXVECTOR2 *nextpt2 = &outline->items[(k + 1) % outline->count].pos;
5831 D3DXVec2Subtract(&vec, nextpt2, &nextpt->pos);
5833 D3DXVec2Subtract(&vec, &nextpt->pos, &pt->pos);
5835 D3DXVec2Normalize(&vec, &vec);
5836 vtx.normal.x = -vec.y;
5837 vtx.normal.y = vec.x;
5840 *vertex_ptr++ = vtx;
5841 vtx.position.z = -extrusion;
5842 *vertex_ptr++ = vtx;
5845 (*face_ptr)[0] = vtx_idx;
5846 (*face_ptr)[1] = vtx_idx + 3;
5847 (*face_ptr)[2] = vtx_idx + 1;
5850 (*face_ptr)[0] = vtx_idx;
5851 (*face_ptr)[1] = vtx_idx + 2;
5852 (*face_ptr)[2] = vtx_idx + 3;
5860 *vertex_ptr++ = *outline_vertices++;
5861 *vertex_ptr++ = *outline_vertices++;
5865 /* back vertices and faces */
5866 back_faces = face_ptr;
5867 back_vertices = vertex_ptr;
5868 for (j = 0; j < glyphs[i].ordered_vertices.count; j++)
5870 D3DXVECTOR2 *pt = get_ordered_vertex(&glyphs[i], j);
5871 vertex_ptr->position.x = pt->x + glyphs[i].offset_x;
5872 vertex_ptr->position.y = pt->y;
5873 vertex_ptr->position.z = 0;
5874 vertex_ptr->normal.x = 0;
5875 vertex_ptr->normal.y = 0;
5876 vertex_ptr->normal.z = 1;
5879 count = back_vertices - vertices;
5880 for (j = 0; j < glyphs[i].faces.count; j++)
5882 face *f = &glyphs[i].faces.items[j];
5883 (*face_ptr)[0] = (*f)[0] + count;
5884 (*face_ptr)[1] = (*f)[1] + count;
5885 (*face_ptr)[2] = (*f)[2] + count;
5889 /* front vertices and faces */
5890 j = count = vertex_ptr - back_vertices;
5893 vertex_ptr->position.x = back_vertices->position.x;
5894 vertex_ptr->position.y = back_vertices->position.y;
5895 vertex_ptr->position.z = -extrusion;
5896 vertex_ptr->normal.x = 0;
5897 vertex_ptr->normal.y = 0;
5898 vertex_ptr->normal.z = extrusion == 0.0f ? 1.0f : -1.0f;
5902 j = face_ptr - back_faces;
5905 (*face_ptr)[0] = (*back_faces)[0] + count;
5906 (*face_ptr)[1] = (*back_faces)[f1] + count;
5907 (*face_ptr)[2] = (*back_faces)[f2] + count;
5917 if (faces) mesh->lpVtbl->UnlockIndexBuffer(mesh);
5918 if (vertices) mesh->lpVtbl->UnlockVertexBuffer(mesh);
5919 if (hr != D3D_OK) mesh->lpVtbl->Release(mesh);
5922 for (i = 0; i < textlen; i++)
5925 for (j = 0; j < glyphs[i].outlines.count; j++)
5926 HeapFree(GetProcessHeap(), 0, glyphs[i].outlines.items[j].items);
5927 HeapFree(GetProcessHeap(), 0, glyphs[i].outlines.items);
5928 HeapFree(GetProcessHeap(), 0, glyphs[i].faces.items);
5929 HeapFree(GetProcessHeap(), 0, glyphs[i].ordered_vertices.items);
5931 HeapFree(GetProcessHeap(), 0, glyphs);
5933 if (triangulations.items) {
5935 for (i = 0; i < triangulations.count; i++)
5936 HeapFree(GetProcessHeap(), 0, triangulations.items[i].vertex_stack.items);
5937 HeapFree(GetProcessHeap(), 0, triangulations.items);
5939 HeapFree(GetProcessHeap(), 0, raw_outline);
5940 if (oldfont) SelectObject(hdc, oldfont);
5941 if (font) DeleteObject(font);
5946 HRESULT WINAPI D3DXValidMesh(LPD3DXMESH mesh, CONST DWORD *adjacency, LPD3DXBUFFER *errors_and_warnings)
5948 FIXME("(%p, %p, %p): stub\n", mesh, adjacency, *errors_and_warnings);
5953 static BOOL weld_float1(void *to, void *from, FLOAT epsilon)
5958 if (fabsf(*v1 - *v2) <= epsilon)
5968 static BOOL weld_float2(void *to, void *from, FLOAT epsilon)
5970 D3DXVECTOR2 *v1 = to;
5971 D3DXVECTOR2 *v2 = from;
5972 FLOAT diff_x = fabsf(v1->x - v2->x);
5973 FLOAT diff_y = fabsf(v1->y - v2->y);
5974 FLOAT max_abs_diff = max(diff_x, diff_y);
5976 if (max_abs_diff <= epsilon)
5978 memcpy(to, from, sizeof(D3DXVECTOR2));
5986 static BOOL weld_float3(void *to, void *from, FLOAT epsilon)
5988 D3DXVECTOR3 *v1 = to;
5989 D3DXVECTOR3 *v2 = from;
5990 FLOAT diff_x = fabsf(v1->x - v2->x);
5991 FLOAT diff_y = fabsf(v1->y - v2->y);
5992 FLOAT diff_z = fabsf(v1->z - v2->z);
5993 FLOAT max_abs_diff = max(diff_x, diff_y);
5994 max_abs_diff = max(diff_z, max_abs_diff);
5996 if (max_abs_diff <= epsilon)
5998 memcpy(to, from, sizeof(D3DXVECTOR3));
6006 static BOOL weld_float4(void *to, void *from, FLOAT epsilon)
6008 D3DXVECTOR4 *v1 = to;
6009 D3DXVECTOR4 *v2 = from;
6010 FLOAT diff_x = fabsf(v1->x - v2->x);
6011 FLOAT diff_y = fabsf(v1->y - v2->y);
6012 FLOAT diff_z = fabsf(v1->z - v2->z);
6013 FLOAT diff_w = fabsf(v1->w - v2->w);
6014 FLOAT max_abs_diff = fmax(diff_x, diff_y);
6015 max_abs_diff = max(diff_z, max_abs_diff);
6016 max_abs_diff = max(diff_w, max_abs_diff);
6018 if (max_abs_diff <= epsilon)
6020 memcpy(to, from, sizeof(D3DXVECTOR4));
6028 static BOOL weld_ubyte4(void *to, void *from, FLOAT epsilon)
6032 BYTE truncated_epsilon = (BYTE)epsilon;
6033 BYTE diff_x = b1[0] > b2[0] ? b1[0] - b2[0] : b2[0] - b1[0];
6034 BYTE diff_y = b1[1] > b2[1] ? b1[1] - b2[1] : b2[1] - b1[1];
6035 BYTE diff_z = b1[2] > b2[2] ? b1[2] - b2[2] : b2[2] - b1[2];
6036 BYTE diff_w = b1[3] > b2[3] ? b1[3] - b2[3] : b2[3] - b1[3];
6037 BYTE max_diff = max(diff_x, diff_y);
6038 max_diff = max(diff_z, max_diff);
6039 max_diff = max(diff_w, max_diff);
6041 if (max_diff <= truncated_epsilon)
6043 memcpy(to, from, 4 * sizeof(BYTE));
6051 static BOOL weld_ubyte4n(void *to, void *from, FLOAT epsilon)
6053 return weld_ubyte4(to, from, epsilon * UCHAR_MAX);
6056 static BOOL weld_d3dcolor(void *to, void *from, FLOAT epsilon)
6058 return weld_ubyte4n(to, from, epsilon);
6061 static BOOL weld_short2(void *to, void *from, FLOAT epsilon)
6065 SHORT truncated_epsilon = (SHORT)epsilon;
6066 SHORT diff_x = abs(s1[0] - s2[0]);
6067 SHORT diff_y = abs(s1[1] - s2[1]);
6068 SHORT max_abs_diff = max(diff_x, diff_y);
6070 if (max_abs_diff <= truncated_epsilon)
6072 memcpy(to, from, 2 * sizeof(SHORT));
6080 static BOOL weld_short2n(void *to, void *from, FLOAT epsilon)
6082 return weld_short2(to, from, epsilon * SHRT_MAX);
6085 static BOOL weld_short4(void *to, void *from, FLOAT epsilon)
6089 SHORT truncated_epsilon = (SHORT)epsilon;
6090 SHORT diff_x = abs(s1[0] - s2[0]);
6091 SHORT diff_y = abs(s1[1] - s2[1]);
6092 SHORT diff_z = abs(s1[2] - s2[2]);
6093 SHORT diff_w = abs(s1[3] - s2[3]);
6094 SHORT max_abs_diff = max(diff_x, diff_y);
6095 max_abs_diff = max(diff_z, max_abs_diff);
6096 max_abs_diff = max(diff_w, max_abs_diff);
6098 if (max_abs_diff <= truncated_epsilon)
6100 memcpy(to, from, 4 * sizeof(SHORT));
6108 static BOOL weld_short4n(void *to, void *from, FLOAT epsilon)
6110 return weld_short4(to, from, epsilon * SHRT_MAX);
6113 static BOOL weld_ushort2n(void *to, void *from, FLOAT epsilon)
6117 USHORT scaled_epsilon = (USHORT)(epsilon * USHRT_MAX);
6118 USHORT diff_x = s1[0] > s2[0] ? s1[0] - s2[0] : s2[0] - s1[0];
6119 USHORT diff_y = s1[1] > s2[1] ? s1[1] - s2[1] : s2[1] - s1[1];
6120 USHORT max_diff = max(diff_x, diff_y);
6122 if (max_diff <= scaled_epsilon)
6124 memcpy(to, from, 2 * sizeof(USHORT));
6132 static BOOL weld_ushort4n(void *to, void *from, FLOAT epsilon)
6136 USHORT scaled_epsilon = (USHORT)(epsilon * USHRT_MAX);
6137 USHORT diff_x = s1[0] > s2[0] ? s1[0] - s2[0] : s2[0] - s1[0];
6138 USHORT diff_y = s1[1] > s2[1] ? s1[1] - s2[1] : s2[1] - s1[1];
6139 USHORT diff_z = s1[2] > s2[2] ? s1[2] - s2[2] : s2[2] - s1[2];
6140 USHORT diff_w = s1[3] > s2[3] ? s1[3] - s2[3] : s2[3] - s1[3];
6141 USHORT max_diff = max(diff_x, diff_y);
6142 max_diff = max(diff_z, max_diff);
6143 max_diff = max(diff_w, max_diff);
6145 if (max_diff <= scaled_epsilon)
6147 memcpy(to, from, 4 * sizeof(USHORT));
6163 static struct udec3 dword_to_udec3(DWORD d)
6168 v.y = (d & 0xffc00) >> 10;
6169 v.z = (d & 0x3ff00000) >> 20;
6170 v.w = (d & 0xc0000000) >> 30;
6175 static BOOL weld_udec3(void *to, void *from, FLOAT epsilon)
6179 struct udec3 v1 = dword_to_udec3(*d1);
6180 struct udec3 v2 = dword_to_udec3(*d2);
6181 UINT truncated_epsilon = (UINT)epsilon;
6182 UINT diff_x = v1.x > v2.x ? v1.x - v2.x : v2.x - v1.x;
6183 UINT diff_y = v1.y > v2.y ? v1.y - v2.y : v2.y - v1.y;
6184 UINT diff_z = v1.z > v2.z ? v1.z - v2.z : v2.z - v1.z;
6185 UINT diff_w = v1.w > v2.w ? v1.w - v2.w : v2.w - v1.w;
6186 UINT max_diff = max(diff_x, diff_y);
6187 max_diff = max(diff_z, max_diff);
6188 max_diff = max(diff_w, max_diff);
6190 if (max_diff <= truncated_epsilon)
6192 memcpy(to, from, sizeof(DWORD));
6208 static struct dec3n dword_to_dec3n(DWORD d)
6213 v.y = (d & 0xffc00) >> 10;
6214 v.z = (d & 0x3ff00000) >> 20;
6215 v.w = (d & 0xc0000000) >> 30;
6220 static BOOL weld_dec3n(void *to, void *from, FLOAT epsilon)
6222 const UINT MAX_DEC3N = 511;
6225 struct dec3n v1 = dword_to_dec3n(*d1);
6226 struct dec3n v2 = dword_to_dec3n(*d2);
6227 INT scaled_epsilon = (INT)(epsilon * MAX_DEC3N);
6228 INT diff_x = abs(v1.x - v2.x);
6229 INT diff_y = abs(v1.y - v2.y);
6230 INT diff_z = abs(v1.z - v2.z);
6231 INT diff_w = abs(v1.w - v2.w);
6232 INT max_abs_diff = max(diff_x, diff_y);
6233 max_abs_diff = max(diff_z, max_abs_diff);
6234 max_abs_diff = max(diff_w, max_abs_diff);
6236 if (max_abs_diff <= scaled_epsilon)
6238 memcpy(to, from, sizeof(DWORD));
6246 static BOOL weld_float16_2(void *to, void *from, FLOAT epsilon)
6248 D3DXFLOAT16 *v1_float16 = to;
6249 D3DXFLOAT16 *v2_float16 = from;
6253 const UINT NUM_ELEM = 2;
6257 D3DXFloat16To32Array(v1, v1_float16, NUM_ELEM);
6258 D3DXFloat16To32Array(v2, v2_float16, NUM_ELEM);
6260 diff_x = fabsf(v1[0] - v2[0]);
6261 diff_y = fabsf(v1[1] - v2[1]);
6262 max_abs_diff = max(diff_x, diff_y);
6264 if (max_abs_diff <= epsilon)
6266 memcpy(to, from, NUM_ELEM * sizeof(D3DXFLOAT16));
6274 static BOOL weld_float16_4(void *to, void *from, FLOAT epsilon)
6276 D3DXFLOAT16 *v1_float16 = to;
6277 D3DXFLOAT16 *v2_float16 = from;
6283 const UINT NUM_ELEM = 4;
6287 D3DXFloat16To32Array(v1, v1_float16, NUM_ELEM);
6288 D3DXFloat16To32Array(v2, v2_float16, NUM_ELEM);
6290 diff_x = fabsf(v1[0] - v2[0]);
6291 diff_y = fabsf(v1[1] - v2[1]);
6292 diff_z = fabsf(v1[2] - v2[2]);
6293 diff_w = fabsf(v1[3] - v2[3]);
6294 max_abs_diff = max(diff_x, diff_y);
6295 max_abs_diff = max(diff_z, max_abs_diff);
6296 max_abs_diff = max(diff_w, max_abs_diff);
6298 if (max_abs_diff <= epsilon)
6300 memcpy(to, from, NUM_ELEM * sizeof(D3DXFLOAT16));
6308 /* Sets the vertex components to the same value if they are within epsilon. */
6309 static BOOL weld_component(void *to, void *from, D3DDECLTYPE type, FLOAT epsilon)
6311 /* Quiet FIXMEs as this is in a loop with potentially thousand of iterations. */
6312 BOOL fixme_once_unused = FALSE;
6313 BOOL fixme_once_unknown = FALSE;
6317 case D3DDECLTYPE_FLOAT1:
6318 return weld_float1(to, from, epsilon);
6320 case D3DDECLTYPE_FLOAT2:
6321 return weld_float2(to, from, epsilon);
6323 case D3DDECLTYPE_FLOAT3:
6324 return weld_float3(to, from, epsilon);
6326 case D3DDECLTYPE_FLOAT4:
6327 return weld_float4(to, from, epsilon);
6329 case D3DDECLTYPE_D3DCOLOR:
6330 return weld_d3dcolor(to, from, epsilon);
6332 case D3DDECLTYPE_UBYTE4:
6333 return weld_ubyte4(to, from, epsilon);
6335 case D3DDECLTYPE_SHORT2:
6336 return weld_short2(to, from, epsilon);
6338 case D3DDECLTYPE_SHORT4:
6339 return weld_short4(to, from, epsilon);
6341 case D3DDECLTYPE_UBYTE4N:
6342 return weld_ubyte4n(to, from, epsilon);
6344 case D3DDECLTYPE_SHORT2N:
6345 return weld_short2n(to, from, epsilon);
6347 case D3DDECLTYPE_SHORT4N:
6348 return weld_short4n(to, from, epsilon);
6350 case D3DDECLTYPE_USHORT2N:
6351 return weld_ushort2n(to, from, epsilon);
6353 case D3DDECLTYPE_USHORT4N:
6354 return weld_ushort4n(to, from, epsilon);
6356 case D3DDECLTYPE_UDEC3:
6357 return weld_udec3(to, from, epsilon);
6359 case D3DDECLTYPE_DEC3N:
6360 return weld_dec3n(to, from, epsilon);
6362 case D3DDECLTYPE_FLOAT16_2:
6363 return weld_float16_2(to, from, epsilon);
6365 case D3DDECLTYPE_FLOAT16_4:
6366 return weld_float16_4(to, from, epsilon);
6368 case D3DDECLTYPE_UNUSED:
6369 if (!fixme_once_unused++)
6370 FIXME("D3DDECLTYPE_UNUSED welding not implemented.\n");
6374 if (!fixme_once_unknown++)
6375 FIXME("Welding of unknown declaration type %d is not implemented.\n", type);
6382 static FLOAT get_component_epsilon(const D3DVERTEXELEMENT9 *decl_ptr, const D3DXWELDEPSILONS *epsilons)
6384 FLOAT epsilon = 0.0f;
6385 /* Quiet FIXMEs as this is in a loop with potentially thousand of iterations. */
6386 static BOOL fixme_once_blendindices = FALSE;
6387 static BOOL fixme_once_positiont = FALSE;
6388 static BOOL fixme_once_fog = FALSE;
6389 static BOOL fixme_once_depth = FALSE;
6390 static BOOL fixme_once_sample = FALSE;
6391 static BOOL fixme_once_unknown = FALSE;
6393 switch (decl_ptr->Usage)
6395 case D3DDECLUSAGE_POSITION:
6396 epsilon = epsilons->Position;
6398 case D3DDECLUSAGE_BLENDWEIGHT:
6399 epsilon = epsilons->BlendWeights;
6401 case D3DDECLUSAGE_NORMAL:
6402 epsilon = epsilons->Normals;
6404 case D3DDECLUSAGE_PSIZE:
6405 epsilon = epsilons->PSize;
6407 case D3DDECLUSAGE_TEXCOORD:
6409 BYTE usage_index = decl_ptr->UsageIndex;
6410 if (usage_index > 7)
6412 epsilon = epsilons->Texcoords[usage_index];
6415 case D3DDECLUSAGE_TANGENT:
6416 epsilon = epsilons->Tangent;
6418 case D3DDECLUSAGE_BINORMAL:
6419 epsilon = epsilons->Binormal;
6421 case D3DDECLUSAGE_TESSFACTOR:
6422 epsilon = epsilons->TessFactor;
6424 case D3DDECLUSAGE_COLOR:
6425 if (decl_ptr->UsageIndex == 0)
6426 epsilon = epsilons->Diffuse;
6427 else if (decl_ptr->UsageIndex == 1)
6428 epsilon = epsilons->Specular;
6432 case D3DDECLUSAGE_BLENDINDICES:
6433 if (!fixme_once_blendindices++)
6434 FIXME("D3DDECLUSAGE_BLENDINDICES welding not implemented.\n");
6436 case D3DDECLUSAGE_POSITIONT:
6437 if (!fixme_once_positiont++)
6438 FIXME("D3DDECLUSAGE_POSITIONT welding not implemented.\n");
6440 case D3DDECLUSAGE_FOG:
6441 if (!fixme_once_fog++)
6442 FIXME("D3DDECLUSAGE_FOG welding not implemented.\n");
6444 case D3DDECLUSAGE_DEPTH:
6445 if (!fixme_once_depth++)
6446 FIXME("D3DDECLUSAGE_DEPTH welding not implemented.\n");
6448 case D3DDECLUSAGE_SAMPLE:
6449 if (!fixme_once_sample++)
6450 FIXME("D3DDECLUSAGE_SAMPLE welding not implemented.\n");
6453 if (!fixme_once_unknown++)
6454 FIXME("Unknown usage %x\n", decl_ptr->Usage);
6461 /* Helper function for reading a 32-bit index buffer. */
6462 static inline DWORD read_ib(void *index_buffer, BOOL indices_are_32bit,
6465 if (indices_are_32bit)
6467 DWORD *indices = index_buffer;
6468 return indices[index];
6472 WORD *indices = index_buffer;
6473 return indices[index];
6477 /* Helper function for writing to a 32-bit index buffer. */
6478 static inline void write_ib(void *index_buffer, BOOL indices_are_32bit,
6479 DWORD index, DWORD value)
6481 if (indices_are_32bit)
6483 DWORD *indices = index_buffer;
6484 indices[index] = value;
6488 WORD *indices = index_buffer;
6489 indices[index] = value;
6493 /*************************************************************************
6494 * D3DXWeldVertices (D3DX9_36.@)
6496 * Welds together similar vertices. The similarity between vert-
6497 * ices can be the position and other components such as
6501 * mesh [I] Mesh which vertices will be welded together.
6502 * flags [I] D3DXWELDEPSILONSFLAGS specifying how to weld.
6503 * epsilons [I] How similar a component needs to be for welding.
6504 * adjacency [I] Which faces are adjacent to other faces.
6505 * adjacency_out [O] Updated adjacency after welding.
6506 * face_remap_out [O] Which faces the old faces have been mapped to.
6507 * vertex_remap_out [O] Which vertices the old vertices have been mapped to.
6511 * Failure: D3DERR_INVALIDCALL, E_OUTOFMEMORY.
6514 * Attribute sorting not implemented.
6517 HRESULT WINAPI D3DXWeldVertices(LPD3DXMESH mesh,
6519 CONST D3DXWELDEPSILONS *epsilons,
6520 CONST DWORD *adjacency,
6521 DWORD *adjacency_out,
6522 DWORD *face_remap_out,
6523 LPD3DXBUFFER *vertex_remap_out)
6525 DWORD *adjacency_generated = NULL;
6526 const DWORD *adjacency_ptr;
6527 DWORD *attributes = NULL;
6528 const FLOAT DEFAULT_EPSILON = 1.0e-6f;
6531 void *indices = NULL;
6532 BOOL indices_are_32bit = mesh->lpVtbl->GetOptions(mesh) & D3DXMESH_32BIT;
6533 DWORD optimize_flags;
6534 DWORD *point_reps = NULL;
6535 ID3DXMeshImpl *This = impl_from_ID3DXMesh(mesh);
6536 DWORD *vertex_face_map = NULL;
6537 ID3DXBuffer *vertex_remap = NULL;
6538 BYTE *vertices = NULL;
6540 TRACE("(%p, %x, %p, %p, %p, %p, %p)\n", mesh, flags, epsilons,
6541 adjacency, adjacency_out, face_remap_out, vertex_remap_out);
6545 WARN("No flags is undefined. Using D3DXWELDEPSILONS_WELDPARTIALMATCHES instead.\n");
6546 flags = D3DXWELDEPSILONS_WELDPARTIALMATCHES;
6549 if (adjacency) /* Use supplied adjacency. */
6551 adjacency_ptr = adjacency;
6553 else /* Adjacency has to be generated. */
6555 adjacency_generated = HeapAlloc(GetProcessHeap(), 0, 3 * This->numfaces * sizeof(*adjacency_generated));
6556 if (!adjacency_generated)
6558 ERR("Couldn't allocate memory for adjacency_generated.\n");
6562 hr = mesh->lpVtbl->GenerateAdjacency(mesh, DEFAULT_EPSILON, adjacency_generated);
6565 ERR("Couldn't generate adjacency.\n");
6568 adjacency_ptr = adjacency_generated;
6571 /* Point representation says which vertices can be replaced. */
6572 point_reps = HeapAlloc(GetProcessHeap(), 0, This->numvertices * sizeof(*point_reps));
6576 ERR("Couldn't allocate memory for point_reps.\n");
6579 hr = mesh->lpVtbl->ConvertAdjacencyToPointReps(mesh, adjacency_ptr, point_reps);
6582 ERR("ConvertAdjacencyToPointReps failed.\n");
6586 hr = mesh->lpVtbl->LockIndexBuffer(mesh, 0, &indices);
6589 ERR("Couldn't lock index buffer.\n");
6593 hr = mesh->lpVtbl->LockAttributeBuffer(mesh, 0, &attributes);
6596 ERR("Couldn't lock attribute buffer.\n");
6599 vertex_face_map = HeapAlloc(GetProcessHeap(), 0, This->numvertices * sizeof(*vertex_face_map));
6600 if (!vertex_face_map)
6603 ERR("Couldn't allocate memory for vertex_face_map.\n");
6606 /* Build vertex face map, so that a vertex's face can be looked up. */
6607 for (i = 0; i < This->numfaces; i++)
6610 for (j = 0; j < 3; j++)
6612 DWORD index = read_ib(indices, indices_are_32bit, 3*i + j);
6613 vertex_face_map[index] = i;
6617 if (flags & D3DXWELDEPSILONS_WELDPARTIALMATCHES)
6619 hr = mesh->lpVtbl->LockVertexBuffer(mesh, 0, (void**)&vertices);
6622 ERR("Couldn't lock vertex buffer.\n");
6625 /* For each vertex that can be removed, compare its vertex components
6626 * with the vertex components from the vertex that can replace it. A
6627 * vertex is only fully replaced if all the components match and the
6628 * flag D3DXWELDEPSILONS_DONOTREMOVEVERTICES is not set, and they
6629 * belong to the same attribute group. Otherwise the vertex components
6630 * that are within epsilon are set to the same value.
6632 for (i = 0; i < 3 * This->numfaces; i++)
6634 D3DVERTEXELEMENT9 *decl_ptr;
6635 DWORD vertex_size = mesh->lpVtbl->GetNumBytesPerVertex(mesh);
6636 DWORD num_vertex_components;
6639 DWORD index = read_ib(indices, indices_are_32bit, i);
6641 for (decl_ptr = This->cached_declaration, num_vertex_components = 0; decl_ptr->Stream != 0xFF; decl_ptr++, num_vertex_components++)
6643 BYTE *to = &vertices[vertex_size*index + decl_ptr->Offset];
6644 BYTE *from = &vertices[vertex_size*point_reps[index] + decl_ptr->Offset];
6645 FLOAT epsilon = get_component_epsilon(decl_ptr, epsilons);
6647 /* Don't weld self */
6648 if (index == point_reps[index])
6654 if (weld_component(to, from, decl_ptr->Type, epsilon))
6658 all_match = (num_vertex_components == matches);
6659 if (all_match && !(flags & D3DXWELDEPSILONS_DONOTREMOVEVERTICES))
6661 DWORD to_face = vertex_face_map[index];
6662 DWORD from_face = vertex_face_map[point_reps[index]];
6663 if(attributes[to_face] != attributes[from_face] && !(flags & D3DXWELDEPSILONS_DONOTSPLIT))
6665 write_ib(indices, indices_are_32bit, i, point_reps[index]);
6668 mesh->lpVtbl->UnlockVertexBuffer(mesh);
6671 else if (flags & D3DXWELDEPSILONS_WELDALL)
6673 for (i = 0; i < 3 * This->numfaces; i++)
6675 DWORD index = read_ib(indices, indices_are_32bit, i);
6676 DWORD to_face = vertex_face_map[index];
6677 DWORD from_face = vertex_face_map[point_reps[index]];
6678 if(attributes[to_face] != attributes[from_face] && !(flags & D3DXWELDEPSILONS_DONOTSPLIT))
6680 write_ib(indices, indices_are_32bit, i, point_reps[index]);
6683 mesh->lpVtbl->UnlockAttributeBuffer(mesh);
6685 mesh->lpVtbl->UnlockIndexBuffer(mesh);
6688 /* Compact mesh using OptimizeInplace */
6689 optimize_flags = D3DXMESHOPT_COMPACT;
6690 hr = mesh->lpVtbl->OptimizeInplace(mesh, optimize_flags, adjacency_ptr, adjacency_out, face_remap_out, vertex_remap_out);
6693 ERR("Couldn't compact mesh.\n");
6699 HeapFree(GetProcessHeap(), 0, adjacency_generated);
6700 HeapFree(GetProcessHeap(), 0, point_reps);
6701 HeapFree(GetProcessHeap(), 0, vertex_face_map);
6702 if (attributes) mesh->lpVtbl->UnlockAttributeBuffer(mesh);
6703 if (indices) mesh->lpVtbl->UnlockIndexBuffer(mesh);
6704 if (vertex_remap) ID3DXBuffer_Release(vertex_remap);
6705 if (vertices) mesh->lpVtbl->UnlockVertexBuffer(mesh);
6710 /*************************************************************************
6711 * D3DXOptimizeFaces (D3DX9_36.@)
6713 * Re-orders the faces so the vertex cache is used optimally.
6716 * indices [I] Pointer to an index buffer belonging to a mesh.
6717 * num_faces [I] Number of faces in the mesh.
6718 * num_vertices [I] Number of vertices in the mesh.
6719 * indices_are_32bit [I] Specifies whether indices are 32- or 16-bit.
6720 * face_remap [I/O] The new order the faces should be drawn in.
6724 * Failure: D3DERR_INVALIDCALL.
6727 * The face re-ordering does not use the vertex cache optimally.
6730 HRESULT WINAPI D3DXOptimizeFaces(LPCVOID indices,
6733 BOOL indices_are_32bit,
6737 UINT j = num_faces - 1;
6738 UINT limit_16_bit = 2 << 15; /* According to MSDN */
6739 HRESULT hr = D3D_OK;
6741 FIXME("(%p, %u, %u, %s, %p): semi-stub. Face order will not be optimal.\n",
6742 indices, num_faces, num_vertices,
6743 indices_are_32bit ? "TRUE" : "FALSE", face_remap);
6745 if (!indices_are_32bit && num_faces >= limit_16_bit)
6747 WARN("Number of faces must be less than %d when using 16-bit indices.\n",
6749 hr = D3DERR_INVALIDCALL;
6755 WARN("Face remap pointer is NULL.\n");
6756 hr = D3DERR_INVALIDCALL;
6760 /* The faces are drawn in reverse order for simple meshes. This ordering
6761 * is not optimal for complicated meshes, but will not break anything
6762 * either. The ordering should be changed to take advantage of the vertex
6763 * cache on the graphics card.
6765 * TODO Re-order to take advantage of vertex cache.
6767 for (i = 0; i < num_faces; i++)
6769 face_remap[i] = j--;