2 * Mesh operations specific to D3DX9.
4 * Copyright (C) 2005 Henri Verbeet
5 * Copyright (C) 2006 Ivan Gyurdiev
6 * Copyright (C) 2009 David Adam
7 * Copyright (C) 2010 Tony Wasserka
8 * Copyright (C) 2011 Dylan Smith
9 * Copyright (C) 2011 Michael Mc Donnell
11 * This library is free software; you can redistribute it and/or
12 * modify it under the terms of the GNU Lesser General Public
13 * License as published by the Free Software Foundation; either
14 * version 2.1 of the License, or (at your option) any later version.
16 * This library is distributed in the hope that it will be useful,
17 * but WITHOUT ANY WARRANTY; without even the implied warranty of
18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
19 * Lesser General Public License for more details.
21 * You should have received a copy of the GNU Lesser General Public
22 * License along with this library; if not, write to the Free Software
23 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
27 #include "wine/port.h"
30 #define NONAMELESSUNION
42 #include "wine/debug.h"
43 #include "wine/unicode.h"
44 #include "wine/list.h"
45 #include "d3dx9_36_private.h"
47 WINE_DEFAULT_DEBUG_CHANNEL(d3dx);
49 typedef struct ID3DXMeshImpl
51 ID3DXMesh ID3DXMesh_iface;
58 IDirect3DDevice9 *device;
59 D3DVERTEXELEMENT9 cached_declaration[MAX_FVF_DECL_SIZE];
60 IDirect3DVertexDeclaration9 *vertex_declaration;
61 UINT vertex_declaration_size;
63 IDirect3DVertexBuffer9 *vertex_buffer;
64 IDirect3DIndexBuffer9 *index_buffer;
66 int attrib_buffer_lock_count;
67 DWORD attrib_table_size;
68 D3DXATTRIBUTERANGE *attrib_table;
71 const UINT d3dx_decltype_size[] =
73 /* D3DDECLTYPE_FLOAT1 */ sizeof(FLOAT),
74 /* D3DDECLTYPE_FLOAT2 */ sizeof(D3DXVECTOR2),
75 /* D3DDECLTYPE_FLOAT3 */ sizeof(D3DXVECTOR3),
76 /* D3DDECLTYPE_FLOAT4 */ sizeof(D3DXVECTOR4),
77 /* D3DDECLTYPE_D3DCOLOR */ sizeof(D3DCOLOR),
78 /* D3DDECLTYPE_UBYTE4 */ 4 * sizeof(BYTE),
79 /* D3DDECLTYPE_SHORT2 */ 2 * sizeof(SHORT),
80 /* D3DDECLTYPE_SHORT4 */ 4 * sizeof(SHORT),
81 /* D3DDECLTYPE_UBYTE4N */ 4 * sizeof(BYTE),
82 /* D3DDECLTYPE_SHORT2N */ 2 * sizeof(SHORT),
83 /* D3DDECLTYPE_SHORT4N */ 4 * sizeof(SHORT),
84 /* D3DDECLTYPE_USHORT2N */ 2 * sizeof(USHORT),
85 /* D3DDECLTYPE_USHORT4N */ 4 * sizeof(USHORT),
86 /* D3DDECLTYPE_UDEC3 */ 4, /* 3 * 10 bits + 2 padding */
87 /* D3DDECLTYPE_DEC3N */ 4,
88 /* D3DDECLTYPE_FLOAT16_2 */ 2 * sizeof(D3DXFLOAT16),
89 /* D3DDECLTYPE_FLOAT16_4 */ 4 * sizeof(D3DXFLOAT16),
92 static inline ID3DXMeshImpl *impl_from_ID3DXMesh(ID3DXMesh *iface)
94 return CONTAINING_RECORD(iface, ID3DXMeshImpl, ID3DXMesh_iface);
97 static HRESULT WINAPI ID3DXMeshImpl_QueryInterface(ID3DXMesh *iface, REFIID riid, LPVOID *object)
99 TRACE("(%p)->(%s, %p)\n", iface, debugstr_guid(riid), object);
101 if (IsEqualGUID(riid, &IID_IUnknown) ||
102 IsEqualGUID(riid, &IID_ID3DXBaseMesh) ||
103 IsEqualGUID(riid, &IID_ID3DXMesh))
105 iface->lpVtbl->AddRef(iface);
110 WARN("Interface %s not found.\n", debugstr_guid(riid));
112 return E_NOINTERFACE;
115 static ULONG WINAPI ID3DXMeshImpl_AddRef(ID3DXMesh *iface)
117 ID3DXMeshImpl *This = impl_from_ID3DXMesh(iface);
119 TRACE("(%p)->(): AddRef from %d\n", This, This->ref);
121 return InterlockedIncrement(&This->ref);
124 static ULONG WINAPI ID3DXMeshImpl_Release(ID3DXMesh *iface)
126 ID3DXMeshImpl *This = impl_from_ID3DXMesh(iface);
127 ULONG ref = InterlockedDecrement(&This->ref);
129 TRACE("(%p)->(): Release from %d\n", This, ref + 1);
133 IDirect3DIndexBuffer9_Release(This->index_buffer);
134 IDirect3DVertexBuffer9_Release(This->vertex_buffer);
135 if (This->vertex_declaration)
136 IDirect3DVertexDeclaration9_Release(This->vertex_declaration);
137 IDirect3DDevice9_Release(This->device);
138 HeapFree(GetProcessHeap(), 0, This->attrib_buffer);
139 HeapFree(GetProcessHeap(), 0, This->attrib_table);
140 HeapFree(GetProcessHeap(), 0, This);
146 /*** ID3DXBaseMesh ***/
147 static HRESULT WINAPI ID3DXMeshImpl_DrawSubset(ID3DXMesh *iface, DWORD attrib_id)
149 ID3DXMeshImpl *This = impl_from_ID3DXMesh(iface);
155 TRACE("(%p)->(%u)\n", This, attrib_id);
157 if (!This->vertex_declaration)
159 WARN("Can't draw a mesh with an invalid vertex declaration.\n");
163 vertex_size = iface->lpVtbl->GetNumBytesPerVertex(iface);
165 hr = IDirect3DDevice9_SetVertexDeclaration(This->device, This->vertex_declaration);
166 if (FAILED(hr)) return hr;
167 hr = IDirect3DDevice9_SetStreamSource(This->device, 0, This->vertex_buffer, 0, vertex_size);
168 if (FAILED(hr)) return hr;
169 hr = IDirect3DDevice9_SetIndices(This->device, This->index_buffer);
170 if (FAILED(hr)) return hr;
172 while (face_end < This->numfaces)
174 for (face_start = face_end; face_start < This->numfaces; face_start++)
176 if (This->attrib_buffer[face_start] == attrib_id)
179 if (face_start >= This->numfaces)
181 for (face_end = face_start + 1; face_end < This->numfaces; face_end++)
183 if (This->attrib_buffer[face_end] != attrib_id)
187 hr = IDirect3DDevice9_DrawIndexedPrimitive(This->device, D3DPT_TRIANGLELIST,
188 0, 0, This->numvertices, face_start * 3, face_end - face_start);
189 if (FAILED(hr)) return hr;
195 static DWORD WINAPI ID3DXMeshImpl_GetNumFaces(ID3DXMesh *iface)
197 ID3DXMeshImpl *This = impl_from_ID3DXMesh(iface);
199 TRACE("(%p)\n", This);
201 return This->numfaces;
204 static DWORD WINAPI ID3DXMeshImpl_GetNumVertices(ID3DXMesh *iface)
206 ID3DXMeshImpl *This = impl_from_ID3DXMesh(iface);
208 TRACE("(%p)\n", This);
210 return This->numvertices;
213 static DWORD WINAPI ID3DXMeshImpl_GetFVF(ID3DXMesh *iface)
215 ID3DXMeshImpl *This = impl_from_ID3DXMesh(iface);
217 TRACE("(%p)\n", This);
222 static void copy_declaration(D3DVERTEXELEMENT9 *dst, const D3DVERTEXELEMENT9 *src, UINT num_elem)
224 memcpy(dst, src, num_elem * sizeof(*src));
227 static HRESULT WINAPI ID3DXMeshImpl_GetDeclaration(ID3DXMesh *iface, D3DVERTEXELEMENT9 declaration[MAX_FVF_DECL_SIZE])
229 ID3DXMeshImpl *This = impl_from_ID3DXMesh(iface);
231 TRACE("(%p)\n", This);
233 if (declaration == NULL) return D3DERR_INVALIDCALL;
235 copy_declaration(declaration, This->cached_declaration, This->num_elem);
240 static DWORD WINAPI ID3DXMeshImpl_GetNumBytesPerVertex(ID3DXMesh *iface)
242 ID3DXMeshImpl *This = impl_from_ID3DXMesh(iface);
244 TRACE("iface (%p)\n", This);
246 return This->vertex_declaration_size;
249 static DWORD WINAPI ID3DXMeshImpl_GetOptions(ID3DXMesh *iface)
251 ID3DXMeshImpl *This = impl_from_ID3DXMesh(iface);
253 TRACE("(%p)\n", This);
255 return This->options;
258 static HRESULT WINAPI ID3DXMeshImpl_GetDevice(ID3DXMesh *iface, LPDIRECT3DDEVICE9 *device)
260 ID3DXMeshImpl *This = impl_from_ID3DXMesh(iface);
262 TRACE("(%p)->(%p)\n", This, device);
264 if (device == NULL) return D3DERR_INVALIDCALL;
265 *device = This->device;
266 IDirect3DDevice9_AddRef(This->device);
271 static HRESULT WINAPI ID3DXMeshImpl_CloneMeshFVF(ID3DXMesh *iface, DWORD options, DWORD fvf, LPDIRECT3DDEVICE9 device, LPD3DXMESH *clone_mesh)
273 ID3DXMeshImpl *This = impl_from_ID3DXMesh(iface);
275 D3DVERTEXELEMENT9 declaration[MAX_FVF_DECL_SIZE];
277 TRACE("(%p)->(%x,%x,%p,%p)\n", This, options, fvf, device, clone_mesh);
279 hr = D3DXDeclaratorFromFVF(fvf, declaration);
280 if (FAILED(hr)) return hr;
282 return iface->lpVtbl->CloneMesh(iface, options, declaration, device, clone_mesh);
285 static FLOAT scale_clamp_ubyten(FLOAT value)
287 value = value * UCHAR_MAX;
295 if (value > UCHAR_MAX) /* Clamp at 255 */
302 static FLOAT scale_clamp_shortn(FLOAT value)
304 value = value * SHRT_MAX;
306 /* The tests show that the range is SHRT_MIN + 1 to SHRT_MAX. */
307 if (value <= SHRT_MIN)
311 else if (value > SHRT_MAX)
321 static FLOAT scale_clamp_ushortn(FLOAT value)
323 value = value * USHRT_MAX;
331 if (value > USHRT_MAX) /* Clamp at 65535 */
338 static INT simple_round(FLOAT value)
340 int res = (INT)(value + 0.5f);
345 static void convert_float4(BYTE *dst, CONST D3DXVECTOR4 *src, D3DDECLTYPE type_dst)
347 BOOL fixme_once = FALSE;
351 case D3DDECLTYPE_FLOAT1:
353 FLOAT *dst_ptr = (FLOAT*)dst;
357 case D3DDECLTYPE_FLOAT2:
359 D3DXVECTOR2 *dst_ptr = (D3DXVECTOR2*)dst;
364 case D3DDECLTYPE_FLOAT3:
366 D3DXVECTOR3 *dst_ptr = (D3DXVECTOR3*)dst;
372 case D3DDECLTYPE_FLOAT4:
374 D3DXVECTOR4 *dst_ptr = (D3DXVECTOR4*)dst;
381 case D3DDECLTYPE_D3DCOLOR:
383 dst[0] = (BYTE)simple_round(scale_clamp_ubyten(src->z));
384 dst[1] = (BYTE)simple_round(scale_clamp_ubyten(src->y));
385 dst[2] = (BYTE)simple_round(scale_clamp_ubyten(src->x));
386 dst[3] = (BYTE)simple_round(scale_clamp_ubyten(src->w));
389 case D3DDECLTYPE_UBYTE4:
391 dst[0] = src->x < 0.0f ? 0 : (BYTE)simple_round(src->x);
392 dst[1] = src->y < 0.0f ? 0 : (BYTE)simple_round(src->y);
393 dst[2] = src->z < 0.0f ? 0 : (BYTE)simple_round(src->z);
394 dst[3] = src->w < 0.0f ? 0 : (BYTE)simple_round(src->w);
397 case D3DDECLTYPE_SHORT2:
399 SHORT *dst_ptr = (SHORT*)dst;
400 dst_ptr[0] = (SHORT)simple_round(src->x);
401 dst_ptr[1] = (SHORT)simple_round(src->y);
404 case D3DDECLTYPE_SHORT4:
406 SHORT *dst_ptr = (SHORT*)dst;
407 dst_ptr[0] = (SHORT)simple_round(src->x);
408 dst_ptr[1] = (SHORT)simple_round(src->y);
409 dst_ptr[2] = (SHORT)simple_round(src->z);
410 dst_ptr[3] = (SHORT)simple_round(src->w);
413 case D3DDECLTYPE_UBYTE4N:
415 dst[0] = (BYTE)simple_round(scale_clamp_ubyten(src->x));
416 dst[1] = (BYTE)simple_round(scale_clamp_ubyten(src->y));
417 dst[2] = (BYTE)simple_round(scale_clamp_ubyten(src->z));
418 dst[3] = (BYTE)simple_round(scale_clamp_ubyten(src->w));
421 case D3DDECLTYPE_SHORT2N:
423 SHORT *dst_ptr = (SHORT*)dst;
424 dst_ptr[0] = (SHORT)simple_round(scale_clamp_shortn(src->x));
425 dst_ptr[1] = (SHORT)simple_round(scale_clamp_shortn(src->y));
428 case D3DDECLTYPE_SHORT4N:
430 SHORT *dst_ptr = (SHORT*)dst;
431 dst_ptr[0] = (SHORT)simple_round(scale_clamp_shortn(src->x));
432 dst_ptr[1] = (SHORT)simple_round(scale_clamp_shortn(src->y));
433 dst_ptr[2] = (SHORT)simple_round(scale_clamp_shortn(src->z));
434 dst_ptr[3] = (SHORT)simple_round(scale_clamp_shortn(src->w));
437 case D3DDECLTYPE_USHORT2N:
439 USHORT *dst_ptr = (USHORT*)dst;
440 dst_ptr[0] = (USHORT)simple_round(scale_clamp_ushortn(src->x));
441 dst_ptr[1] = (USHORT)simple_round(scale_clamp_ushortn(src->y));
444 case D3DDECLTYPE_USHORT4N:
446 USHORT *dst_ptr = (USHORT*)dst;
447 dst_ptr[0] = (USHORT)simple_round(scale_clamp_ushortn(src->x));
448 dst_ptr[1] = (USHORT)simple_round(scale_clamp_ushortn(src->y));
449 dst_ptr[2] = (USHORT)simple_round(scale_clamp_ushortn(src->z));
450 dst_ptr[3] = (USHORT)simple_round(scale_clamp_ushortn(src->w));
453 case D3DDECLTYPE_FLOAT16_2:
455 D3DXFloat32To16Array((D3DXFLOAT16*)dst, (FLOAT*)src, 2);
458 case D3DDECLTYPE_FLOAT16_4:
460 D3DXFloat32To16Array((D3DXFLOAT16*)dst, (FLOAT*)src, 4);
465 FIXME("Conversion from D3DDECLTYPE_FLOAT4 to %d not implemented.\n", type_dst);
470 static void convert_component(BYTE *dst, BYTE *src, D3DDECLTYPE type_dst, D3DDECLTYPE type_src)
472 BOOL fixme_once = FALSE;
476 case D3DDECLTYPE_FLOAT1:
478 FLOAT *src_ptr = (FLOAT*)src;
479 D3DXVECTOR4 src_float4 = {*src_ptr, 0.0f, 0.0f, 1.0f};
480 convert_float4(dst, &src_float4, type_dst);
483 case D3DDECLTYPE_FLOAT2:
485 D3DXVECTOR2 *src_ptr = (D3DXVECTOR2*)src;
486 D3DXVECTOR4 src_float4 = {src_ptr->x, src_ptr->y, 0.0f, 1.0f};
487 convert_float4(dst, &src_float4, type_dst);
490 case D3DDECLTYPE_FLOAT3:
492 D3DXVECTOR3 *src_ptr = (D3DXVECTOR3*)src;
493 D3DXVECTOR4 src_float4 = {src_ptr->x, src_ptr->y, src_ptr->z, 1.0f};
494 convert_float4(dst, &src_float4, type_dst);
497 case D3DDECLTYPE_FLOAT4:
499 D3DXVECTOR4 *src_ptr = (D3DXVECTOR4*)src;
500 D3DXVECTOR4 src_float4 = {src_ptr->x, src_ptr->y, src_ptr->z, src_ptr->w};
501 convert_float4(dst, &src_float4, type_dst);
504 case D3DDECLTYPE_D3DCOLOR:
506 D3DXVECTOR4 src_float4 =
508 (FLOAT)src[2]/UCHAR_MAX,
509 (FLOAT)src[1]/UCHAR_MAX,
510 (FLOAT)src[0]/UCHAR_MAX,
511 (FLOAT)src[3]/UCHAR_MAX
513 convert_float4(dst, &src_float4, type_dst);
516 case D3DDECLTYPE_UBYTE4:
518 D3DXVECTOR4 src_float4 = {src[0], src[1], src[2], src[3]};
519 convert_float4(dst, &src_float4, type_dst);
522 case D3DDECLTYPE_SHORT2:
524 SHORT *src_ptr = (SHORT*)src;
525 D3DXVECTOR4 src_float4 = {src_ptr[0], src_ptr[1], 0.0f, 1.0f};
526 convert_float4(dst, &src_float4, type_dst);
529 case D3DDECLTYPE_SHORT4:
531 SHORT *src_ptr = (SHORT*)src;
532 D3DXVECTOR4 src_float4 = {src_ptr[0], src_ptr[1], src_ptr[2], src_ptr[3]};
533 convert_float4(dst, &src_float4, type_dst);
536 case D3DDECLTYPE_UBYTE4N:
538 D3DXVECTOR4 src_float4 =
540 (FLOAT)src[0]/UCHAR_MAX,
541 (FLOAT)src[1]/UCHAR_MAX,
542 (FLOAT)src[2]/UCHAR_MAX,
543 (FLOAT)src[3]/UCHAR_MAX
545 convert_float4(dst, &src_float4, type_dst);
548 case D3DDECLTYPE_SHORT2N:
550 SHORT *src_ptr = (SHORT*)src;
551 D3DXVECTOR4 src_float4 = {(FLOAT)src_ptr[0]/SHRT_MAX, (FLOAT)src_ptr[1]/SHRT_MAX, 0.0f, 1.0f};
552 convert_float4(dst, &src_float4, type_dst);
555 case D3DDECLTYPE_SHORT4N:
557 SHORT *src_ptr = (SHORT*)src;
558 D3DXVECTOR4 src_float4 =
560 (FLOAT)src_ptr[0]/SHRT_MAX,
561 (FLOAT)src_ptr[1]/SHRT_MAX,
562 (FLOAT)src_ptr[2]/SHRT_MAX,
563 (FLOAT)src_ptr[3]/SHRT_MAX
565 convert_float4(dst, &src_float4, type_dst);
568 case D3DDECLTYPE_FLOAT16_2:
570 D3DXVECTOR4 src_float4 = {0.0f, 0.0f, 0.0f, 1.0f};
571 D3DXFloat16To32Array((FLOAT*)&src_float4, (D3DXFLOAT16*)src, 2);
572 convert_float4(dst, &src_float4, type_dst);
575 case D3DDECLTYPE_FLOAT16_4:
577 D3DXVECTOR4 src_float4;
578 D3DXFloat16To32Array((FLOAT*)&src_float4, (D3DXFLOAT16*)src, 4);
579 convert_float4(dst, &src_float4, type_dst);
584 FIXME("Conversion of D3DDECLTYPE %d to %d not implemented.\n", type_src, type_dst);
589 static INT get_equivalent_declaration_index(D3DVERTEXELEMENT9 orig_declaration, D3DVERTEXELEMENT9 *declaration)
593 for (i = 0; declaration[i].Stream != 0xff; i++)
595 if (orig_declaration.Usage == declaration[i].Usage
596 && orig_declaration.UsageIndex == declaration[i].UsageIndex)
605 static HRESULT convert_vertex_buffer(ID3DXMesh *mesh_dst, ID3DXMesh *mesh_src)
608 D3DVERTEXELEMENT9 orig_declaration[MAX_FVF_DECL_SIZE] = {D3DDECL_END()};
609 D3DVERTEXELEMENT9 declaration[MAX_FVF_DECL_SIZE] = {D3DDECL_END()};
613 UINT num_vertices = mesh_src->lpVtbl->GetNumVertices(mesh_src);
614 UINT dst_vertex_size = mesh_dst->lpVtbl->GetNumBytesPerVertex(mesh_dst);
615 UINT src_vertex_size = mesh_src->lpVtbl->GetNumBytesPerVertex(mesh_src);
617 hr = mesh_src->lpVtbl->GetDeclaration(mesh_src, orig_declaration);
618 if (FAILED(hr)) return hr;
619 hr = mesh_dst->lpVtbl->GetDeclaration(mesh_dst, declaration);
620 if (FAILED(hr)) return hr;
622 hr = mesh_src->lpVtbl->LockVertexBuffer(mesh_src, D3DLOCK_READONLY, (void**)&vb_src);
623 if (FAILED(hr)) goto cleanup;
624 hr = mesh_dst->lpVtbl->LockVertexBuffer(mesh_dst, 0, (void**)&vb_dst);
625 if (FAILED(hr)) goto cleanup;
627 /* Clear all new fields by clearing the entire vertex buffer. */
628 memset(vb_dst, 0, num_vertices * dst_vertex_size);
630 for (i = 0; orig_declaration[i].Stream != 0xff; i++)
632 INT eq_idx = get_equivalent_declaration_index(orig_declaration[i], declaration);
637 for (j = 0; j < num_vertices; j++)
639 UINT idx_dst = dst_vertex_size * j + declaration[eq_idx].Offset;
640 UINT idx_src = src_vertex_size * j + orig_declaration[i].Offset;
641 UINT type_size = d3dx_decltype_size[orig_declaration[i].Type];
643 if (orig_declaration[i].Type == declaration[eq_idx].Type)
644 memcpy(&vb_dst[idx_dst], &vb_src[idx_src], type_size);
646 convert_component(&vb_dst[idx_dst], &vb_src[idx_src], declaration[eq_idx].Type, orig_declaration[i].Type);
653 if (vb_dst) mesh_dst->lpVtbl->UnlockVertexBuffer(mesh_dst);
654 if (vb_src) mesh_src->lpVtbl->UnlockVertexBuffer(mesh_src);
659 static BOOL declaration_equals(CONST D3DVERTEXELEMENT9 *declaration1, CONST D3DVERTEXELEMENT9 *declaration2)
661 UINT size1 = 0, size2 = 0;
663 /* Find the size of each declaration */
664 while (declaration1[size1].Stream != 0xff) size1++;
665 while (declaration2[size2].Stream != 0xff) size2++;
667 /* If not same size then they are definitely not equal */
671 /* Check that all components are the same */
672 if (memcmp(declaration1, declaration2, size1*sizeof(*declaration1)) == 0)
678 static HRESULT WINAPI ID3DXMeshImpl_CloneMesh(ID3DXMesh *iface, DWORD options, CONST D3DVERTEXELEMENT9 *declaration, LPDIRECT3DDEVICE9 device,
679 LPD3DXMESH *clone_mesh_out)
681 ID3DXMeshImpl *This = impl_from_ID3DXMesh(iface);
682 ID3DXMeshImpl *cloned_this;
683 ID3DXMesh *clone_mesh;
684 D3DVERTEXELEMENT9 orig_declaration[MAX_FVF_DECL_SIZE] = { D3DDECL_END() };
685 void *data_in, *data_out;
689 BOOL same_declaration;
691 TRACE("(%p)->(%x,%p,%p,%p)\n", This, options, declaration, device, clone_mesh_out);
694 return D3DERR_INVALIDCALL;
696 hr = iface->lpVtbl->GetDeclaration(iface, orig_declaration);
697 if (FAILED(hr)) return hr;
699 hr = D3DXCreateMesh(This->numfaces, This->numvertices, options & ~D3DXMESH_VB_SHARE,
700 declaration, device, &clone_mesh);
701 if (FAILED(hr)) return hr;
703 cloned_this = impl_from_ID3DXMesh(clone_mesh);
704 vertex_size = clone_mesh->lpVtbl->GetNumBytesPerVertex(clone_mesh);
705 same_declaration = declaration_equals(declaration, orig_declaration);
707 if (options & D3DXMESH_VB_SHARE) {
708 if (!same_declaration) {
709 hr = D3DERR_INVALIDCALL;
712 IDirect3DVertexBuffer9_AddRef(This->vertex_buffer);
713 /* FIXME: refactor to avoid creating a new vertex buffer */
714 IDirect3DVertexBuffer9_Release(cloned_this->vertex_buffer);
715 cloned_this->vertex_buffer = This->vertex_buffer;
716 } else if (same_declaration) {
717 hr = iface->lpVtbl->LockVertexBuffer(iface, D3DLOCK_READONLY, &data_in);
718 if (FAILED(hr)) goto error;
719 hr = clone_mesh->lpVtbl->LockVertexBuffer(clone_mesh, 0, &data_out);
721 iface->lpVtbl->UnlockVertexBuffer(iface);
724 memcpy(data_out, data_in, This->numvertices * vertex_size);
725 clone_mesh->lpVtbl->UnlockVertexBuffer(clone_mesh);
726 iface->lpVtbl->UnlockVertexBuffer(iface);
728 hr = convert_vertex_buffer(clone_mesh, iface);
729 if (FAILED(hr)) goto error;
732 hr = iface->lpVtbl->LockIndexBuffer(iface, D3DLOCK_READONLY, &data_in);
733 if (FAILED(hr)) goto error;
734 hr = clone_mesh->lpVtbl->LockIndexBuffer(clone_mesh, 0, &data_out);
736 iface->lpVtbl->UnlockIndexBuffer(iface);
739 if ((options ^ This->options) & D3DXMESH_32BIT) {
740 if (options & D3DXMESH_32BIT) {
741 for (i = 0; i < This->numfaces * 3; i++)
742 ((DWORD*)data_out)[i] = ((WORD*)data_in)[i];
744 for (i = 0; i < This->numfaces * 3; i++)
745 ((WORD*)data_out)[i] = ((DWORD*)data_in)[i];
748 memcpy(data_out, data_in, This->numfaces * 3 * (options & D3DXMESH_32BIT ? 4 : 2));
750 clone_mesh->lpVtbl->UnlockIndexBuffer(clone_mesh);
751 iface->lpVtbl->UnlockIndexBuffer(iface);
753 memcpy(cloned_this->attrib_buffer, This->attrib_buffer, This->numfaces * sizeof(*This->attrib_buffer));
755 if (This->attrib_table_size)
757 cloned_this->attrib_table_size = This->attrib_table_size;
758 cloned_this->attrib_table = HeapAlloc(GetProcessHeap(), 0, This->attrib_table_size * sizeof(*This->attrib_table));
759 if (!cloned_this->attrib_table) {
763 memcpy(cloned_this->attrib_table, This->attrib_table, This->attrib_table_size * sizeof(*This->attrib_table));
766 *clone_mesh_out = clone_mesh;
770 IUnknown_Release(clone_mesh);
774 static HRESULT WINAPI ID3DXMeshImpl_GetVertexBuffer(ID3DXMesh *iface, LPDIRECT3DVERTEXBUFFER9 *vertex_buffer)
776 ID3DXMeshImpl *This = impl_from_ID3DXMesh(iface);
778 TRACE("(%p)->(%p)\n", This, vertex_buffer);
780 if (vertex_buffer == NULL) return D3DERR_INVALIDCALL;
781 *vertex_buffer = This->vertex_buffer;
782 IDirect3DVertexBuffer9_AddRef(This->vertex_buffer);
787 static HRESULT WINAPI ID3DXMeshImpl_GetIndexBuffer(ID3DXMesh *iface, LPDIRECT3DINDEXBUFFER9 *index_buffer)
789 ID3DXMeshImpl *This = impl_from_ID3DXMesh(iface);
791 TRACE("(%p)->(%p)\n", This, index_buffer);
793 if (index_buffer == NULL) return D3DERR_INVALIDCALL;
794 *index_buffer = This->index_buffer;
795 IDirect3DIndexBuffer9_AddRef(This->index_buffer);
800 static HRESULT WINAPI ID3DXMeshImpl_LockVertexBuffer(ID3DXMesh *iface, DWORD flags, LPVOID *data)
802 ID3DXMeshImpl *This = impl_from_ID3DXMesh(iface);
804 TRACE("(%p)->(%u,%p)\n", This, flags, data);
806 return IDirect3DVertexBuffer9_Lock(This->vertex_buffer, 0, 0, data, flags);
809 static HRESULT WINAPI ID3DXMeshImpl_UnlockVertexBuffer(ID3DXMesh *iface)
811 ID3DXMeshImpl *This = impl_from_ID3DXMesh(iface);
813 TRACE("(%p)\n", This);
815 return IDirect3DVertexBuffer9_Unlock(This->vertex_buffer);
818 static HRESULT WINAPI ID3DXMeshImpl_LockIndexBuffer(ID3DXMesh *iface, DWORD flags, LPVOID *data)
820 ID3DXMeshImpl *This = impl_from_ID3DXMesh(iface);
822 TRACE("(%p)->(%u,%p)\n", This, flags, data);
824 return IDirect3DIndexBuffer9_Lock(This->index_buffer, 0, 0, data, flags);
827 static HRESULT WINAPI ID3DXMeshImpl_UnlockIndexBuffer(ID3DXMesh *iface)
829 ID3DXMeshImpl *This = impl_from_ID3DXMesh(iface);
831 TRACE("(%p)\n", This);
833 return IDirect3DIndexBuffer9_Unlock(This->index_buffer);
836 static HRESULT WINAPI ID3DXMeshImpl_GetAttributeTable(ID3DXMesh *iface, D3DXATTRIBUTERANGE *attrib_table, DWORD *attrib_table_size)
838 ID3DXMeshImpl *This = impl_from_ID3DXMesh(iface);
840 TRACE("(%p)->(%p,%p)\n", This, attrib_table, attrib_table_size);
842 if (attrib_table_size)
843 *attrib_table_size = This->attrib_table_size;
846 CopyMemory(attrib_table, This->attrib_table, This->attrib_table_size * sizeof(*attrib_table));
861 struct edge_face *entries;
864 /* Builds up a map of which face a new edge belongs to. That way the adjacency
865 * of another edge can be looked up. An edge has an adjacent face if there
866 * is an edge going in the opposite direction in the map. For example if the
867 * edge (v1, v2) belongs to face 4, and there is a mapping (v2, v1)->7, then
868 * face 4 and 7 are adjacent.
870 * Each edge might have been replaced with another edge, or none at all. There
871 * is at most one edge to face mapping, i.e. an edge can only belong to one
874 static HRESULT init_edge_face_map(struct edge_face_map *edge_face_map, CONST DWORD *index_buffer, CONST DWORD *point_reps, CONST DWORD num_faces)
879 edge_face_map->lists = HeapAlloc(GetProcessHeap(), 0, 3 * num_faces * sizeof(*edge_face_map->lists));
880 if (!edge_face_map->lists) return E_OUTOFMEMORY;
882 edge_face_map->entries = HeapAlloc(GetProcessHeap(), 0, 3 * num_faces * sizeof(*edge_face_map->entries));
883 if (!edge_face_map->entries) return E_OUTOFMEMORY;
886 /* Initialize all lists */
887 for (i = 0; i < 3 * num_faces; i++)
889 list_init(&edge_face_map->lists[i]);
891 /* Build edge face mapping */
892 for (face = 0; face < num_faces; face++)
894 for (edge = 0; edge < 3; edge++)
896 DWORD v1 = index_buffer[3*face + edge];
897 DWORD v2 = index_buffer[3*face + (edge+1)%3];
898 DWORD new_v1 = point_reps[v1]; /* What v1 has been replaced with */
899 DWORD new_v2 = point_reps[v2];
901 if (v1 != v2) /* Only map non-collapsed edges */
904 edge_face_map->entries[i].v2 = new_v2;
905 edge_face_map->entries[i].face = face;
906 list_add_head(&edge_face_map->lists[new_v1], &edge_face_map->entries[i].entry);
914 static DWORD find_adjacent_face(struct edge_face_map *edge_face_map, DWORD vertex1, DWORD vertex2, CONST DWORD num_faces)
916 struct edge_face *edge_face_ptr;
918 LIST_FOR_EACH_ENTRY(edge_face_ptr, &edge_face_map->lists[vertex2], struct edge_face, entry)
920 if (edge_face_ptr->v2 == vertex1)
921 return edge_face_ptr->face;
927 static DWORD *generate_identity_point_reps(DWORD num_vertices)
929 DWORD *id_point_reps;
932 id_point_reps = HeapAlloc(GetProcessHeap(), 0, num_vertices * sizeof(*id_point_reps));
936 for (i = 0; i < num_vertices; i++)
938 id_point_reps[i] = i;
941 return id_point_reps;
944 static HRESULT WINAPI ID3DXMeshImpl_ConvertPointRepsToAdjacency(ID3DXMesh *iface, CONST DWORD *point_reps, DWORD *adjacency)
946 ID3DXMeshImpl *This = impl_from_ID3DXMesh(iface);
948 DWORD num_faces = iface->lpVtbl->GetNumFaces(iface);
949 DWORD num_vertices = iface->lpVtbl->GetNumVertices(iface);
950 DWORD options = iface->lpVtbl->GetOptions(iface);
951 BOOL indices_are_16_bit = !(options & D3DXMESH_32BIT);
956 struct edge_face_map edge_face_map = {0};
957 CONST DWORD *point_reps_ptr = NULL;
958 DWORD *id_point_reps = NULL;
960 TRACE("(%p)->(%p,%p)\n", This, point_reps, adjacency);
962 if (!adjacency) return D3DERR_INVALIDCALL;
964 if (!point_reps) /* Identity point reps */
966 id_point_reps = generate_identity_point_reps(num_vertices);
973 point_reps_ptr = id_point_reps;
977 point_reps_ptr = point_reps;
980 hr = iface->lpVtbl->LockIndexBuffer(iface, D3DLOCK_READONLY, &ib_ptr);
981 if (FAILED(hr)) goto cleanup;
983 if (indices_are_16_bit)
985 /* Widen 16 bit to 32 bit */
987 WORD *ib_16bit = ib_ptr;
988 ib = HeapAlloc(GetProcessHeap(), 0, 3 * num_faces * sizeof(DWORD));
994 for (i = 0; i < 3 * num_faces; i++)
1004 hr = init_edge_face_map(&edge_face_map, ib, point_reps_ptr, num_faces);
1005 if (FAILED(hr)) goto cleanup;
1007 /* Create adjacency */
1008 for (face = 0; face < num_faces; face++)
1010 for (edge = 0; edge < 3; edge++)
1012 DWORD v1 = ib[3*face + edge];
1013 DWORD v2 = ib[3*face + (edge+1)%3];
1014 DWORD new_v1 = point_reps_ptr[v1];
1015 DWORD new_v2 = point_reps_ptr[v2];
1018 adj_face = find_adjacent_face(&edge_face_map, new_v1, new_v2, num_faces);
1019 adjacency[3*face + edge] = adj_face;
1025 HeapFree(GetProcessHeap(), 0, id_point_reps);
1026 if (indices_are_16_bit) HeapFree(GetProcessHeap(), 0, ib);
1027 HeapFree(GetProcessHeap(), 0, edge_face_map.lists);
1028 HeapFree(GetProcessHeap(), 0, edge_face_map.entries);
1029 if(ib_ptr) iface->lpVtbl->UnlockIndexBuffer(iface);
1033 /* ConvertAdjacencyToPointReps helper function.
1035 * Goes around the edges of each face and replaces the vertices in any adjacent
1036 * face's edge with its own vertices(if its vertices have a lower index). This
1037 * way as few as possible low index vertices are shared among the faces. The
1038 * re-ordered index buffer is stored in new_indices.
1040 * The vertices in a point representation must be ordered sequentially, e.g.
1041 * index 5 holds the index of the vertex that replaces vertex 5, i.e. if
1042 * vertex 5 is replaced by vertex 3 then index 5 would contain 3. If no vertex
1043 * replaces it, then it contains the same number as the index itself, e.g.
1044 * index 5 would contain 5. */
1045 static HRESULT propagate_face_vertices(CONST DWORD *adjacency, DWORD *point_reps,
1046 CONST DWORD *indices, DWORD *new_indices,
1047 CONST DWORD face, CONST DWORD numfaces)
1049 const unsigned int VERTS_PER_FACE = 3;
1050 DWORD edge, opp_edge;
1051 DWORD face_base = VERTS_PER_FACE * face;
1053 for (edge = 0; edge < VERTS_PER_FACE; edge++)
1055 DWORD adj_face = adjacency[face_base + edge];
1056 DWORD adj_face_base;
1058 if (adj_face == -1) /* No adjacent face. */
1060 else if (adj_face >= numfaces)
1062 /* This throws exception on Windows */
1063 WARN("Index out of bounds. Got %d expected less than %d.\n",
1064 adj_face, numfaces);
1065 return D3DERR_INVALIDCALL;
1067 adj_face_base = 3 * adj_face;
1069 /* Find opposite edge in adjacent face. */
1070 for (opp_edge = 0; opp_edge < VERTS_PER_FACE; opp_edge++)
1072 DWORD opp_edge_index = adj_face_base + opp_edge;
1073 if (adjacency[opp_edge_index] == face)
1074 break; /* Found opposite edge. */
1077 /* Replaces vertices in opposite edge with vertices from current edge. */
1078 for (i = 0; i < 2; i++)
1080 DWORD from = face_base + (edge + (1 - i)) % VERTS_PER_FACE;
1081 DWORD to = adj_face_base + (opp_edge + i) % VERTS_PER_FACE;
1083 /* Propagate lowest index. */
1084 if (new_indices[to] > new_indices[from])
1086 new_indices[to] = new_indices[from];
1087 point_reps[indices[to]] = new_indices[from];
1095 static HRESULT WINAPI ID3DXMeshImpl_ConvertAdjacencyToPointReps(ID3DXMesh *iface, CONST DWORD *adjacency, DWORD *point_reps)
1100 DWORD *indices = NULL;
1101 WORD *indices_16bit = NULL;
1102 DWORD *new_indices = NULL;
1103 const unsigned int VERTS_PER_FACE = 3;
1105 ID3DXMeshImpl *This = impl_from_ID3DXMesh(iface);
1107 TRACE("(%p)->(%p,%p)\n", This, adjacency, point_reps);
1111 WARN("NULL adjacency.\n");
1112 hr = D3DERR_INVALIDCALL;
1118 WARN("NULL point_reps.\n");
1119 hr = D3DERR_INVALIDCALL;
1123 /* Should never happen as CreateMesh does not allow meshes with 0 faces */
1124 if (This->numfaces == 0)
1126 ERR("Number of faces was zero.\n");
1127 hr = D3DERR_INVALIDCALL;
1131 new_indices = HeapAlloc(GetProcessHeap(), 0, VERTS_PER_FACE * This->numfaces * sizeof(*indices));
1138 if (This->options & D3DXMESH_32BIT)
1140 hr = iface->lpVtbl->LockIndexBuffer(iface, D3DLOCK_READONLY, (void**)&indices);
1141 if (FAILED(hr)) goto cleanup;
1142 memcpy(new_indices, indices, VERTS_PER_FACE * This->numfaces * sizeof(*indices));
1146 /* Make a widening copy of indices_16bit into indices and new_indices
1147 * in order to re-use the helper function */
1148 hr = iface->lpVtbl->LockIndexBuffer(iface, D3DLOCK_READONLY, (void**)&indices_16bit);
1149 if (FAILED(hr)) goto cleanup;
1150 indices = HeapAlloc(GetProcessHeap(), 0, VERTS_PER_FACE * This->numfaces * sizeof(*indices));
1156 for (i = 0; i < VERTS_PER_FACE * This->numfaces; i++)
1158 new_indices[i] = indices_16bit[i];
1159 indices[i] = indices_16bit[i];
1163 /* Vertices are ordered sequentially in the point representation. */
1164 for (i = 0; i < This->numvertices; i++)
1169 /* Propagate vertices with low indices so as few vertices as possible
1170 * are used in the mesh.
1172 for (face = 0; face < This->numfaces; face++)
1174 hr = propagate_face_vertices(adjacency, point_reps, indices, new_indices, face, This->numfaces);
1175 if (FAILED(hr)) goto cleanup;
1177 /* Go in opposite direction to catch all face orderings */
1178 for (face = 0; face < This->numfaces; face++)
1180 hr = propagate_face_vertices(adjacency, point_reps,
1181 indices, new_indices,
1182 (This->numfaces - 1) - face, This->numfaces);
1183 if (FAILED(hr)) goto cleanup;
1188 if (This->options & D3DXMESH_32BIT)
1190 if (indices) iface->lpVtbl->UnlockIndexBuffer(iface);
1194 if (indices_16bit) iface->lpVtbl->UnlockIndexBuffer(iface);
1195 HeapFree(GetProcessHeap(), 0, indices);
1197 HeapFree(GetProcessHeap(), 0, new_indices);
1201 struct vertex_metadata {
1204 DWORD first_shared_index;
1207 static int compare_vertex_keys(const void *a, const void *b)
1209 const struct vertex_metadata *left = a;
1210 const struct vertex_metadata *right = b;
1211 if (left->key == right->key)
1213 return left->key < right->key ? -1 : 1;
1216 static HRESULT WINAPI ID3DXMeshImpl_GenerateAdjacency(ID3DXMesh *iface, FLOAT epsilon, DWORD *adjacency)
1218 ID3DXMeshImpl *This = impl_from_ID3DXMesh(iface);
1220 BYTE *vertices = NULL;
1221 const DWORD *indices = NULL;
1224 /* sort the vertices by (x + y + z) to quickly find coincident vertices */
1225 struct vertex_metadata *sorted_vertices;
1226 /* shared_indices links together identical indices in the index buffer so
1227 * that adjacency checks can be limited to faces sharing a vertex */
1228 DWORD *shared_indices = NULL;
1229 const FLOAT epsilon_sq = epsilon * epsilon;
1232 TRACE("(%p)->(%f,%p)\n", This, epsilon, adjacency);
1235 return D3DERR_INVALIDCALL;
1237 buffer_size = This->numfaces * 3 * sizeof(*shared_indices) + This->numvertices * sizeof(*sorted_vertices);
1238 if (!(This->options & D3DXMESH_32BIT))
1239 buffer_size += This->numfaces * 3 * sizeof(*indices);
1240 shared_indices = HeapAlloc(GetProcessHeap(), 0, buffer_size);
1241 if (!shared_indices)
1242 return E_OUTOFMEMORY;
1243 sorted_vertices = (struct vertex_metadata*)(shared_indices + This->numfaces * 3);
1245 hr = iface->lpVtbl->LockVertexBuffer(iface, D3DLOCK_READONLY, (void**)&vertices);
1246 if (FAILED(hr)) goto cleanup;
1247 hr = iface->lpVtbl->LockIndexBuffer(iface, D3DLOCK_READONLY, (void**)&indices);
1248 if (FAILED(hr)) goto cleanup;
1250 if (!(This->options & D3DXMESH_32BIT)) {
1251 const WORD *word_indices = (const WORD*)indices;
1252 DWORD *dword_indices = (DWORD*)(sorted_vertices + This->numvertices);
1253 indices = dword_indices;
1254 for (i = 0; i < This->numfaces * 3; i++)
1255 *dword_indices++ = *word_indices++;
1258 vertex_size = iface->lpVtbl->GetNumBytesPerVertex(iface);
1259 for (i = 0; i < This->numvertices; i++) {
1260 D3DXVECTOR3 *vertex = (D3DXVECTOR3*)(vertices + vertex_size * i);
1261 sorted_vertices[i].first_shared_index = -1;
1262 sorted_vertices[i].key = vertex->x + vertex->y + vertex->z;
1263 sorted_vertices[i].vertex_index = i;
1265 for (i = 0; i < This->numfaces * 3; i++) {
1266 DWORD *first_shared_index = &sorted_vertices[indices[i]].first_shared_index;
1267 shared_indices[i] = *first_shared_index;
1268 *first_shared_index = i;
1271 qsort(sorted_vertices, This->numvertices, sizeof(*sorted_vertices), compare_vertex_keys);
1273 for (i = 0; i < This->numvertices; i++) {
1274 struct vertex_metadata *sorted_vertex_a = &sorted_vertices[i];
1275 D3DXVECTOR3 *vertex_a = (D3DXVECTOR3*)(vertices + sorted_vertex_a->vertex_index * vertex_size);
1276 DWORD shared_index_a = sorted_vertex_a->first_shared_index;
1278 while (shared_index_a != -1) {
1280 DWORD shared_index_b = shared_indices[shared_index_a];
1281 struct vertex_metadata *sorted_vertex_b = sorted_vertex_a;
1284 while (shared_index_b != -1) {
1285 /* faces are adjacent if they have another coincident vertex */
1286 DWORD base_a = (shared_index_a / 3) * 3;
1287 DWORD base_b = (shared_index_b / 3) * 3;
1288 BOOL adjacent = FALSE;
1291 for (k = 0; k < 3; k++) {
1292 if (adjacency[base_b + k] == shared_index_a / 3) {
1298 for (k = 1; k <= 2; k++) {
1299 DWORD vertex_index_a = base_a + (shared_index_a + k) % 3;
1300 DWORD vertex_index_b = base_b + (shared_index_b + (3 - k)) % 3;
1301 adjacent = indices[vertex_index_a] == indices[vertex_index_b];
1302 if (!adjacent && epsilon >= 0.0f) {
1303 D3DXVECTOR3 delta = {0.0f, 0.0f, 0.0f};
1306 D3DXVec3Subtract(&delta,
1307 (D3DXVECTOR3*)(vertices + indices[vertex_index_a] * vertex_size),
1308 (D3DXVECTOR3*)(vertices + indices[vertex_index_b] * vertex_size));
1309 length_sq = D3DXVec3LengthSq(&delta);
1310 adjacent = epsilon == 0.0f ? length_sq == 0.0f : length_sq < epsilon_sq;
1313 DWORD adj_a = base_a + 2 - (vertex_index_a + shared_index_a + 1) % 3;
1314 DWORD adj_b = base_b + 2 - (vertex_index_b + shared_index_b + 1) % 3;
1315 if (adjacency[adj_a] == -1 && adjacency[adj_b] == -1) {
1316 adjacency[adj_a] = base_b / 3;
1317 adjacency[adj_b] = base_a / 3;
1324 shared_index_b = shared_indices[shared_index_b];
1326 while (++j < This->numvertices) {
1327 D3DXVECTOR3 *vertex_b;
1330 if (sorted_vertex_b->key - sorted_vertex_a->key > epsilon * 3.0f) {
1331 /* no more coincident vertices to try */
1332 j = This->numvertices;
1335 /* check for coincidence */
1336 vertex_b = (D3DXVECTOR3*)(vertices + sorted_vertex_b->vertex_index * vertex_size);
1337 if (fabsf(vertex_a->x - vertex_b->x) <= epsilon &&
1338 fabsf(vertex_a->y - vertex_b->y) <= epsilon &&
1339 fabsf(vertex_a->z - vertex_b->z) <= epsilon)
1344 if (j >= This->numvertices)
1346 shared_index_b = sorted_vertex_b->first_shared_index;
1349 sorted_vertex_a->first_shared_index = shared_indices[sorted_vertex_a->first_shared_index];
1350 shared_index_a = sorted_vertex_a->first_shared_index;
1356 if (indices) iface->lpVtbl->UnlockIndexBuffer(iface);
1357 if (vertices) iface->lpVtbl->UnlockVertexBuffer(iface);
1358 HeapFree(GetProcessHeap(), 0, shared_indices);
1362 static HRESULT WINAPI ID3DXMeshImpl_UpdateSemantics(ID3DXMesh *iface, D3DVERTEXELEMENT9 declaration[MAX_FVF_DECL_SIZE])
1365 ID3DXMeshImpl *This = impl_from_ID3DXMesh(iface);
1366 UINT vertex_declaration_size;
1369 TRACE("(%p)->(%p)\n", This, declaration);
1373 WARN("Invalid declaration. Can't use NULL declaration.\n");
1374 return D3DERR_INVALIDCALL;
1377 /* New declaration must be same size as original */
1378 vertex_declaration_size = D3DXGetDeclVertexSize(declaration, declaration[0].Stream);
1379 if (vertex_declaration_size != This->vertex_declaration_size)
1381 WARN("Invalid declaration. New vertex size does not match the original vertex size.\n");
1382 return D3DERR_INVALIDCALL;
1385 /* New declaration must not contain non-zero Stream value */
1386 for (i = 0; declaration[i].Stream != 0xff; i++)
1388 if (declaration[i].Stream != 0)
1390 WARN("Invalid declaration. New declaration contains non-zero Stream value.\n");
1391 return D3DERR_INVALIDCALL;
1395 This->num_elem = i + 1;
1396 copy_declaration(This->cached_declaration, declaration, This->num_elem);
1398 if (This->vertex_declaration)
1399 IDirect3DVertexDeclaration9_Release(This->vertex_declaration);
1401 /* An application can pass an invalid declaration to UpdateSemantics and
1402 * still expect D3D_OK (see tests). If the declaration is invalid, then
1403 * subsequent calls to DrawSubset will fail. This is handled by setting the
1404 * vertex declaration to NULL.
1405 * GetDeclaration, GetNumBytesPerVertex must, however, use the new
1406 * invalid declaration. This is handled by them using the cached vertex
1407 * declaration instead of the actual vertex declaration.
1409 hr = IDirect3DDevice9_CreateVertexDeclaration(This->device,
1411 &This->vertex_declaration);
1414 WARN("Using invalid declaration. Calls to DrawSubset will fail.\n");
1415 This->vertex_declaration = NULL;
1422 static HRESULT WINAPI ID3DXMeshImpl_LockAttributeBuffer(ID3DXMesh *iface, DWORD flags, DWORD **data)
1424 ID3DXMeshImpl *This = impl_from_ID3DXMesh(iface);
1426 TRACE("(%p)->(%u,%p)\n", This, flags, data);
1428 InterlockedIncrement(&This->attrib_buffer_lock_count);
1430 if (!(flags & D3DLOCK_READONLY)) {
1431 D3DXATTRIBUTERANGE *attrib_table = This->attrib_table;
1432 This->attrib_table_size = 0;
1433 This->attrib_table = NULL;
1434 HeapFree(GetProcessHeap(), 0, attrib_table);
1437 *data = This->attrib_buffer;
1442 static HRESULT WINAPI ID3DXMeshImpl_UnlockAttributeBuffer(ID3DXMesh *iface)
1444 ID3DXMeshImpl *This = impl_from_ID3DXMesh(iface);
1447 TRACE("(%p)\n", This);
1449 lock_count = InterlockedDecrement(&This->attrib_buffer_lock_count);
1451 if (lock_count < 0) {
1452 InterlockedIncrement(&This->attrib_buffer_lock_count);
1453 return D3DERR_INVALIDCALL;
1459 static HRESULT WINAPI ID3DXMeshImpl_Optimize(ID3DXMesh *iface, DWORD flags, CONST DWORD *adjacency_in, DWORD *adjacency_out,
1460 DWORD *face_remap, LPD3DXBUFFER *vertex_remap, LPD3DXMESH *opt_mesh)
1462 ID3DXMeshImpl *This = impl_from_ID3DXMesh(iface);
1464 D3DVERTEXELEMENT9 declaration[MAX_FVF_DECL_SIZE] = { D3DDECL_END() };
1465 ID3DXMesh *optimized_mesh;
1467 TRACE("(%p)->(%x,%p,%p,%p,%p,%p)\n", This, flags, adjacency_in, adjacency_out, face_remap, vertex_remap, opt_mesh);
1470 return D3DERR_INVALIDCALL;
1472 hr = iface->lpVtbl->GetDeclaration(iface, declaration);
1473 if (FAILED(hr)) return hr;
1475 hr = iface->lpVtbl->CloneMesh(iface, This->options, declaration, This->device, &optimized_mesh);
1476 if (FAILED(hr)) return hr;
1478 hr = optimized_mesh->lpVtbl->OptimizeInplace(optimized_mesh, flags, adjacency_in, adjacency_out, face_remap, vertex_remap);
1480 *opt_mesh = optimized_mesh;
1482 IUnknown_Release(optimized_mesh);
1486 /* Creates a vertex_remap that removes unused vertices.
1487 * Indices are updated according to the vertex_remap. */
1488 static HRESULT compact_mesh(ID3DXMeshImpl *This, DWORD *indices, DWORD *new_num_vertices, ID3DXBuffer **vertex_remap)
1491 DWORD *vertex_remap_ptr;
1492 DWORD num_used_vertices;
1495 hr = D3DXCreateBuffer(This->numvertices * sizeof(DWORD), vertex_remap);
1496 if (FAILED(hr)) return hr;
1497 vertex_remap_ptr = ID3DXBuffer_GetBufferPointer(*vertex_remap);
1499 for (i = 0; i < This->numfaces * 3; i++)
1500 vertex_remap_ptr[indices[i]] = 1;
1502 /* create old->new vertex mapping */
1503 num_used_vertices = 0;
1504 for (i = 0; i < This->numvertices; i++) {
1505 if (vertex_remap_ptr[i])
1506 vertex_remap_ptr[i] = num_used_vertices++;
1508 vertex_remap_ptr[i] = -1;
1510 /* convert indices */
1511 for (i = 0; i < This->numfaces * 3; i++)
1512 indices[i] = vertex_remap_ptr[indices[i]];
1514 /* create new->old vertex mapping */
1515 num_used_vertices = 0;
1516 for (i = 0; i < This->numvertices; i++) {
1517 if (vertex_remap_ptr[i] != -1)
1518 vertex_remap_ptr[num_used_vertices++] = i;
1520 for (i = num_used_vertices; i < This->numvertices; i++)
1521 vertex_remap_ptr[i] = -1;
1523 *new_num_vertices = num_used_vertices;
1528 /* count the number of unique attribute values in a sorted attribute buffer */
1529 static DWORD count_attributes(const DWORD *attrib_buffer, DWORD numfaces)
1531 DWORD last_attribute = attrib_buffer[0];
1532 DWORD attrib_table_size = 1;
1534 for (i = 1; i < numfaces; i++) {
1535 if (attrib_buffer[i] != last_attribute) {
1536 last_attribute = attrib_buffer[i];
1537 attrib_table_size++;
1540 return attrib_table_size;
1543 static void fill_attribute_table(DWORD *attrib_buffer, DWORD numfaces, void *indices,
1544 BOOL is_32bit_indices, D3DXATTRIBUTERANGE *attrib_table)
1546 DWORD attrib_table_size = 0;
1547 DWORD last_attribute = attrib_buffer[0];
1548 DWORD min_vertex, max_vertex;
1551 attrib_table[0].AttribId = last_attribute;
1552 attrib_table[0].FaceStart = 0;
1553 min_vertex = (DWORD)-1;
1555 for (i = 0; i < numfaces; i++) {
1558 if (attrib_buffer[i] != last_attribute) {
1559 last_attribute = attrib_buffer[i];
1560 attrib_table[attrib_table_size].FaceCount = i - attrib_table[attrib_table_size].FaceStart;
1561 attrib_table[attrib_table_size].VertexStart = min_vertex;
1562 attrib_table[attrib_table_size].VertexCount = max_vertex - min_vertex + 1;
1563 attrib_table_size++;
1564 attrib_table[attrib_table_size].AttribId = attrib_buffer[i];
1565 attrib_table[attrib_table_size].FaceStart = i;
1566 min_vertex = (DWORD)-1;
1569 for (j = 0; j < 3; j++) {
1570 DWORD vertex_index = is_32bit_indices ? ((DWORD*)indices)[i * 3 + j] : ((WORD*)indices)[i * 3 + j];
1571 if (vertex_index < min_vertex)
1572 min_vertex = vertex_index;
1573 if (vertex_index > max_vertex)
1574 max_vertex = vertex_index;
1577 attrib_table[attrib_table_size].FaceCount = i - attrib_table[attrib_table_size].FaceStart;
1578 attrib_table[attrib_table_size].VertexStart = min_vertex;
1579 attrib_table[attrib_table_size].VertexCount = max_vertex - min_vertex + 1;
1580 attrib_table_size++;
1583 static int attrib_entry_compare(const DWORD **a, const DWORD **b)
1585 const DWORD *ptr_a = *a;
1586 const DWORD *ptr_b = *b;
1587 int delta = *ptr_a - *ptr_b;
1592 delta = ptr_a - ptr_b; /* for stable sort */
1596 /* Create face_remap, a new attribute buffer for attribute sort optimization. */
1597 static HRESULT remap_faces_for_attrsort(ID3DXMeshImpl *This, const DWORD *indices,
1598 const DWORD *attrib_buffer, DWORD **sorted_attrib_buffer, DWORD **face_remap)
1600 const DWORD **sorted_attrib_ptr_buffer = NULL;
1603 *face_remap = HeapAlloc(GetProcessHeap(), 0, This->numfaces * sizeof(**face_remap));
1604 sorted_attrib_ptr_buffer = HeapAlloc(GetProcessHeap(), 0, This->numfaces * sizeof(*sorted_attrib_ptr_buffer));
1605 if (!*face_remap || !sorted_attrib_ptr_buffer) {
1606 HeapFree(GetProcessHeap(), 0, sorted_attrib_ptr_buffer);
1607 return E_OUTOFMEMORY;
1609 for (i = 0; i < This->numfaces; i++)
1610 sorted_attrib_ptr_buffer[i] = &attrib_buffer[i];
1611 qsort(sorted_attrib_ptr_buffer, This->numfaces, sizeof(*sorted_attrib_ptr_buffer),
1612 (int(*)(const void *, const void *))attrib_entry_compare);
1614 for (i = 0; i < This->numfaces; i++)
1616 DWORD old_face = sorted_attrib_ptr_buffer[i] - attrib_buffer;
1617 (*face_remap)[old_face] = i;
1620 /* overwrite sorted_attrib_ptr_buffer with the values themselves */
1621 *sorted_attrib_buffer = (DWORD*)sorted_attrib_ptr_buffer;
1622 for (i = 0; i < This->numfaces; i++)
1623 (*sorted_attrib_buffer)[(*face_remap)[i]] = attrib_buffer[i];
1628 static HRESULT WINAPI ID3DXMeshImpl_OptimizeInplace(ID3DXMesh *iface, DWORD flags, CONST DWORD *adjacency_in, DWORD *adjacency_out,
1629 DWORD *face_remap_out, LPD3DXBUFFER *vertex_remap_out)
1631 ID3DXMeshImpl *This = impl_from_ID3DXMesh(iface);
1632 void *indices = NULL;
1633 DWORD *attrib_buffer = NULL;
1635 ID3DXBuffer *vertex_remap = NULL;
1636 DWORD *face_remap = NULL; /* old -> new mapping */
1637 DWORD *dword_indices = NULL;
1638 DWORD new_num_vertices = 0;
1639 DWORD new_num_alloc_vertices = 0;
1640 IDirect3DVertexBuffer9 *vertex_buffer = NULL;
1641 DWORD *sorted_attrib_buffer = NULL;
1644 TRACE("(%p)->(%x,%p,%p,%p,%p)\n", This, flags, adjacency_in, adjacency_out, face_remap_out, vertex_remap_out);
1647 return D3DERR_INVALIDCALL;
1648 if (!adjacency_in && (flags & (D3DXMESHOPT_VERTEXCACHE | D3DXMESHOPT_STRIPREORDER)))
1649 return D3DERR_INVALIDCALL;
1650 if ((flags & (D3DXMESHOPT_VERTEXCACHE | D3DXMESHOPT_STRIPREORDER)) == (D3DXMESHOPT_VERTEXCACHE | D3DXMESHOPT_STRIPREORDER))
1651 return D3DERR_INVALIDCALL;
1653 if (flags & (D3DXMESHOPT_VERTEXCACHE | D3DXMESHOPT_STRIPREORDER))
1655 if (flags & D3DXMESHOPT_VERTEXCACHE)
1656 FIXME("D3DXMESHOPT_VERTEXCACHE not implemented.\n");
1657 if (flags & D3DXMESHOPT_STRIPREORDER)
1658 FIXME("D3DXMESHOPT_STRIPREORDER not implemented.\n");
1662 hr = iface->lpVtbl->LockIndexBuffer(iface, 0, &indices);
1663 if (FAILED(hr)) goto cleanup;
1665 dword_indices = HeapAlloc(GetProcessHeap(), 0, This->numfaces * 3 * sizeof(DWORD));
1666 if (!dword_indices) return E_OUTOFMEMORY;
1667 if (This->options & D3DXMESH_32BIT) {
1668 memcpy(dword_indices, indices, This->numfaces * 3 * sizeof(DWORD));
1670 WORD *word_indices = indices;
1671 for (i = 0; i < This->numfaces * 3; i++)
1672 dword_indices[i] = *word_indices++;
1675 if ((flags & (D3DXMESHOPT_COMPACT | D3DXMESHOPT_IGNOREVERTS | D3DXMESHOPT_ATTRSORT)) == D3DXMESHOPT_COMPACT)
1677 new_num_alloc_vertices = This->numvertices;
1678 hr = compact_mesh(This, dword_indices, &new_num_vertices, &vertex_remap);
1679 if (FAILED(hr)) goto cleanup;
1680 } else if (flags & D3DXMESHOPT_ATTRSORT) {
1681 if (!(flags & D3DXMESHOPT_IGNOREVERTS))
1683 FIXME("D3DXMESHOPT_ATTRSORT vertex reordering not implemented.\n");
1688 hr = iface->lpVtbl->LockAttributeBuffer(iface, 0, &attrib_buffer);
1689 if (FAILED(hr)) goto cleanup;
1691 hr = remap_faces_for_attrsort(This, dword_indices, attrib_buffer, &sorted_attrib_buffer, &face_remap);
1692 if (FAILED(hr)) goto cleanup;
1697 /* reorder the vertices using vertex_remap */
1698 D3DVERTEXBUFFER_DESC vertex_desc;
1699 DWORD *vertex_remap_ptr = ID3DXBuffer_GetBufferPointer(vertex_remap);
1700 DWORD vertex_size = iface->lpVtbl->GetNumBytesPerVertex(iface);
1701 BYTE *orig_vertices;
1704 hr = IDirect3DVertexBuffer9_GetDesc(This->vertex_buffer, &vertex_desc);
1705 if (FAILED(hr)) goto cleanup;
1707 hr = IDirect3DDevice9_CreateVertexBuffer(This->device, new_num_alloc_vertices * vertex_size,
1708 vertex_desc.Usage, This->fvf, vertex_desc.Pool, &vertex_buffer, NULL);
1709 if (FAILED(hr)) goto cleanup;
1711 hr = IDirect3DVertexBuffer9_Lock(This->vertex_buffer, 0, 0, (void**)&orig_vertices, D3DLOCK_READONLY);
1712 if (FAILED(hr)) goto cleanup;
1714 hr = IDirect3DVertexBuffer9_Lock(vertex_buffer, 0, 0, (void**)&new_vertices, 0);
1716 IDirect3DVertexBuffer9_Unlock(This->vertex_buffer);
1720 for (i = 0; i < new_num_vertices; i++)
1721 memcpy(new_vertices + i * vertex_size, orig_vertices + vertex_remap_ptr[i] * vertex_size, vertex_size);
1723 IDirect3DVertexBuffer9_Unlock(This->vertex_buffer);
1724 IDirect3DVertexBuffer9_Unlock(vertex_buffer);
1725 } else if (vertex_remap_out) {
1726 DWORD *vertex_remap_ptr;
1728 hr = D3DXCreateBuffer(This->numvertices * sizeof(DWORD), &vertex_remap);
1729 if (FAILED(hr)) goto cleanup;
1730 vertex_remap_ptr = ID3DXBuffer_GetBufferPointer(vertex_remap);
1731 for (i = 0; i < This->numvertices; i++)
1732 *vertex_remap_ptr++ = i;
1735 if (flags & D3DXMESHOPT_ATTRSORT)
1737 D3DXATTRIBUTERANGE *attrib_table;
1738 DWORD attrib_table_size;
1740 attrib_table_size = count_attributes(sorted_attrib_buffer, This->numfaces);
1741 attrib_table = HeapAlloc(GetProcessHeap(), 0, attrib_table_size * sizeof(*attrib_table));
1742 if (!attrib_table) {
1747 memcpy(attrib_buffer, sorted_attrib_buffer, This->numfaces * sizeof(*attrib_buffer));
1749 /* reorder the indices using face_remap */
1750 if (This->options & D3DXMESH_32BIT) {
1751 for (i = 0; i < This->numfaces; i++)
1752 memcpy((DWORD*)indices + face_remap[i] * 3, dword_indices + i * 3, 3 * sizeof(DWORD));
1754 WORD *word_indices = indices;
1755 for (i = 0; i < This->numfaces; i++) {
1756 DWORD new_pos = face_remap[i] * 3;
1757 DWORD old_pos = i * 3;
1758 word_indices[new_pos++] = dword_indices[old_pos++];
1759 word_indices[new_pos++] = dword_indices[old_pos++];
1760 word_indices[new_pos] = dword_indices[old_pos];
1764 fill_attribute_table(attrib_buffer, This->numfaces, indices,
1765 This->options & D3DXMESH_32BIT, attrib_table);
1767 HeapFree(GetProcessHeap(), 0, This->attrib_table);
1768 This->attrib_table = attrib_table;
1769 This->attrib_table_size = attrib_table_size;
1771 if (This->options & D3DXMESH_32BIT) {
1772 memcpy(indices, dword_indices, This->numfaces * 3 * sizeof(DWORD));
1774 WORD *word_indices = indices;
1775 for (i = 0; i < This->numfaces * 3; i++)
1776 *word_indices++ = dword_indices[i];
1780 if (adjacency_out) {
1782 for (i = 0; i < This->numfaces; i++) {
1783 DWORD old_pos = i * 3;
1784 DWORD new_pos = face_remap[i] * 3;
1785 adjacency_out[new_pos++] = face_remap[adjacency_in[old_pos++]];
1786 adjacency_out[new_pos++] = face_remap[adjacency_in[old_pos++]];
1787 adjacency_out[new_pos++] = face_remap[adjacency_in[old_pos++]];
1790 memcpy(adjacency_out, adjacency_in, This->numfaces * 3 * sizeof(*adjacency_out));
1793 if (face_remap_out) {
1795 for (i = 0; i < This->numfaces; i++)
1796 face_remap_out[face_remap[i]] = i;
1798 for (i = 0; i < This->numfaces; i++)
1799 face_remap_out[i] = i;
1802 if (vertex_remap_out)
1803 *vertex_remap_out = vertex_remap;
1804 vertex_remap = NULL;
1806 if (vertex_buffer) {
1807 IDirect3DVertexBuffer9_Release(This->vertex_buffer);
1808 This->vertex_buffer = vertex_buffer;
1809 vertex_buffer = NULL;
1810 This->numvertices = new_num_vertices;
1815 HeapFree(GetProcessHeap(), 0, sorted_attrib_buffer);
1816 HeapFree(GetProcessHeap(), 0, face_remap);
1817 HeapFree(GetProcessHeap(), 0, dword_indices);
1818 if (vertex_remap) ID3DXBuffer_Release(vertex_remap);
1819 if (vertex_buffer) IDirect3DVertexBuffer9_Release(vertex_buffer);
1820 if (attrib_buffer) iface->lpVtbl->UnlockAttributeBuffer(iface);
1821 if (indices) iface->lpVtbl->UnlockIndexBuffer(iface);
1825 static HRESULT WINAPI ID3DXMeshImpl_SetAttributeTable(ID3DXMesh *iface, CONST D3DXATTRIBUTERANGE *attrib_table, DWORD attrib_table_size)
1827 ID3DXMeshImpl *This = impl_from_ID3DXMesh(iface);
1828 D3DXATTRIBUTERANGE *new_table = NULL;
1830 TRACE("(%p)->(%p,%u)\n", This, attrib_table, attrib_table_size);
1832 if (attrib_table_size) {
1833 size_t size = attrib_table_size * sizeof(*attrib_table);
1835 new_table = HeapAlloc(GetProcessHeap(), 0, size);
1837 return E_OUTOFMEMORY;
1839 CopyMemory(new_table, attrib_table, size);
1840 } else if (attrib_table) {
1841 return D3DERR_INVALIDCALL;
1843 HeapFree(GetProcessHeap(), 0, This->attrib_table);
1844 This->attrib_table = new_table;
1845 This->attrib_table_size = attrib_table_size;
1850 static const struct ID3DXMeshVtbl D3DXMesh_Vtbl =
1852 /*** IUnknown methods ***/
1853 ID3DXMeshImpl_QueryInterface,
1854 ID3DXMeshImpl_AddRef,
1855 ID3DXMeshImpl_Release,
1856 /*** ID3DXBaseMesh ***/
1857 ID3DXMeshImpl_DrawSubset,
1858 ID3DXMeshImpl_GetNumFaces,
1859 ID3DXMeshImpl_GetNumVertices,
1860 ID3DXMeshImpl_GetFVF,
1861 ID3DXMeshImpl_GetDeclaration,
1862 ID3DXMeshImpl_GetNumBytesPerVertex,
1863 ID3DXMeshImpl_GetOptions,
1864 ID3DXMeshImpl_GetDevice,
1865 ID3DXMeshImpl_CloneMeshFVF,
1866 ID3DXMeshImpl_CloneMesh,
1867 ID3DXMeshImpl_GetVertexBuffer,
1868 ID3DXMeshImpl_GetIndexBuffer,
1869 ID3DXMeshImpl_LockVertexBuffer,
1870 ID3DXMeshImpl_UnlockVertexBuffer,
1871 ID3DXMeshImpl_LockIndexBuffer,
1872 ID3DXMeshImpl_UnlockIndexBuffer,
1873 ID3DXMeshImpl_GetAttributeTable,
1874 ID3DXMeshImpl_ConvertPointRepsToAdjacency,
1875 ID3DXMeshImpl_ConvertAdjacencyToPointReps,
1876 ID3DXMeshImpl_GenerateAdjacency,
1877 ID3DXMeshImpl_UpdateSemantics,
1879 ID3DXMeshImpl_LockAttributeBuffer,
1880 ID3DXMeshImpl_UnlockAttributeBuffer,
1881 ID3DXMeshImpl_Optimize,
1882 ID3DXMeshImpl_OptimizeInplace,
1883 ID3DXMeshImpl_SetAttributeTable
1886 /*************************************************************************
1889 BOOL WINAPI D3DXBoxBoundProbe(CONST D3DXVECTOR3 *pmin, CONST D3DXVECTOR3 *pmax, CONST D3DXVECTOR3 *prayposition, CONST D3DXVECTOR3 *praydirection)
1891 /* Algorithm taken from the article: An Efficient and Robust Ray-Box Intersection Algorithm
1892 Amy Williams University of Utah
1893 Steve Barrus University of Utah
1894 R. Keith Morley University of Utah
1895 Peter Shirley University of Utah
1897 International Conference on Computer Graphics and Interactive Techniques archive
1898 ACM SIGGRAPH 2005 Courses
1899 Los Angeles, California
1901 This algorithm is free of patents or of copyrights, as confirmed by Peter Shirley himself.
1903 Algorithm: Consider the box as the intersection of three slabs. Clip the ray
1904 against each slab, if there's anything left of the ray after we're
1905 done we've got an intersection of the ray with the box.
1909 FLOAT div, tmin, tmax, tymin, tymax, tzmin, tzmax;
1911 div = 1.0f / praydirection->x;
1914 tmin = ( pmin->x - prayposition->x ) * div;
1915 tmax = ( pmax->x - prayposition->x ) * div;
1919 tmin = ( pmax->x - prayposition->x ) * div;
1920 tmax = ( pmin->x - prayposition->x ) * div;
1923 if ( tmax < 0.0f ) return FALSE;
1925 div = 1.0f / praydirection->y;
1928 tymin = ( pmin->y - prayposition->y ) * div;
1929 tymax = ( pmax->y - prayposition->y ) * div;
1933 tymin = ( pmax->y - prayposition->y ) * div;
1934 tymax = ( pmin->y - prayposition->y ) * div;
1937 if ( ( tymax < 0.0f ) || ( tmin > tymax ) || ( tymin > tmax ) ) return FALSE;
1939 if ( tymin > tmin ) tmin = tymin;
1940 if ( tymax < tmax ) tmax = tymax;
1942 div = 1.0f / praydirection->z;
1945 tzmin = ( pmin->z - prayposition->z ) * div;
1946 tzmax = ( pmax->z - prayposition->z ) * div;
1950 tzmin = ( pmax->z - prayposition->z ) * div;
1951 tzmax = ( pmin->z - prayposition->z ) * div;
1954 if ( (tzmax < 0.0f ) || ( tmin > tzmax ) || ( tzmin > tmax ) ) return FALSE;
1959 /*************************************************************************
1960 * D3DXComputeBoundingBox
1962 HRESULT WINAPI D3DXComputeBoundingBox(CONST D3DXVECTOR3 *pfirstposition, DWORD numvertices, DWORD dwstride, D3DXVECTOR3 *pmin, D3DXVECTOR3 *pmax)
1967 if( !pfirstposition || !pmin || !pmax ) return D3DERR_INVALIDCALL;
1969 *pmin = *pfirstposition;
1972 for(i=0; i<numvertices; i++)
1974 vec = *( (const D3DXVECTOR3*)((const char*)pfirstposition + dwstride * i) );
1976 if ( vec.x < pmin->x ) pmin->x = vec.x;
1977 if ( vec.x > pmax->x ) pmax->x = vec.x;
1979 if ( vec.y < pmin->y ) pmin->y = vec.y;
1980 if ( vec.y > pmax->y ) pmax->y = vec.y;
1982 if ( vec.z < pmin->z ) pmin->z = vec.z;
1983 if ( vec.z > pmax->z ) pmax->z = vec.z;
1989 /*************************************************************************
1990 * D3DXComputeBoundingSphere
1992 HRESULT WINAPI D3DXComputeBoundingSphere(CONST D3DXVECTOR3* pfirstposition, DWORD numvertices, DWORD dwstride, D3DXVECTOR3 *pcenter, FLOAT *pradius)
1998 if( !pfirstposition || !pcenter || !pradius ) return D3DERR_INVALIDCALL;
2005 for(i=0; i<numvertices; i++)
2006 D3DXVec3Add(&temp, &temp, (const D3DXVECTOR3*)((const char*)pfirstposition + dwstride * i));
2008 D3DXVec3Scale(pcenter, &temp, 1.0f / numvertices);
2010 for(i=0; i<numvertices; i++)
2012 d = D3DXVec3Length(D3DXVec3Subtract(&temp, (const D3DXVECTOR3*)((const char*)pfirstposition + dwstride * i), pcenter));
2013 if ( d > *pradius ) *pradius = d;
2018 static void append_decl_element(D3DVERTEXELEMENT9 *declaration, UINT *idx, UINT *offset,
2019 D3DDECLTYPE type, D3DDECLUSAGE usage, UINT usage_idx)
2021 declaration[*idx].Stream = 0;
2022 declaration[*idx].Offset = *offset;
2023 declaration[*idx].Type = type;
2024 declaration[*idx].Method = D3DDECLMETHOD_DEFAULT;
2025 declaration[*idx].Usage = usage;
2026 declaration[*idx].UsageIndex = usage_idx;
2028 *offset += d3dx_decltype_size[type];
2032 /*************************************************************************
2033 * D3DXDeclaratorFromFVF
2035 HRESULT WINAPI D3DXDeclaratorFromFVF(DWORD fvf, D3DVERTEXELEMENT9 declaration[MAX_FVF_DECL_SIZE])
2037 static const D3DVERTEXELEMENT9 end_element = D3DDECL_END();
2038 DWORD tex_count = (fvf & D3DFVF_TEXCOUNT_MASK) >> D3DFVF_TEXCOUNT_SHIFT;
2039 unsigned int offset = 0;
2040 unsigned int idx = 0;
2043 TRACE("fvf %#x, declaration %p.\n", fvf, declaration);
2045 if (fvf & (D3DFVF_RESERVED0 | D3DFVF_RESERVED2)) return D3DERR_INVALIDCALL;
2047 if (fvf & D3DFVF_POSITION_MASK)
2049 BOOL has_blend = (fvf & D3DFVF_XYZB5) >= D3DFVF_XYZB1;
2050 DWORD blend_count = 1 + (((fvf & D3DFVF_XYZB5) - D3DFVF_XYZB1) >> 1);
2051 BOOL has_blend_idx = (fvf & D3DFVF_LASTBETA_D3DCOLOR) || (fvf & D3DFVF_LASTBETA_UBYTE4);
2053 if (has_blend_idx) --blend_count;
2055 if ((fvf & D3DFVF_POSITION_MASK) == D3DFVF_XYZW
2056 || (has_blend && blend_count > 4))
2057 return D3DERR_INVALIDCALL;
2059 if ((fvf & D3DFVF_POSITION_MASK) == D3DFVF_XYZRHW)
2060 append_decl_element(declaration, &idx, &offset, D3DDECLTYPE_FLOAT4, D3DDECLUSAGE_POSITIONT, 0);
2062 append_decl_element(declaration, &idx, &offset, D3DDECLTYPE_FLOAT3, D3DDECLUSAGE_POSITION, 0);
2066 switch (blend_count)
2071 append_decl_element(declaration, &idx, &offset, D3DDECLTYPE_FLOAT1, D3DDECLUSAGE_BLENDWEIGHT, 0);
2074 append_decl_element(declaration, &idx, &offset, D3DDECLTYPE_FLOAT2, D3DDECLUSAGE_BLENDWEIGHT, 0);
2077 append_decl_element(declaration, &idx, &offset, D3DDECLTYPE_FLOAT3, D3DDECLUSAGE_BLENDWEIGHT, 0);
2080 append_decl_element(declaration, &idx, &offset, D3DDECLTYPE_FLOAT4, D3DDECLUSAGE_BLENDWEIGHT, 0);
2083 ERR("Invalid blend count %u.\n", blend_count);
2089 if (fvf & D3DFVF_LASTBETA_UBYTE4)
2090 append_decl_element(declaration, &idx, &offset, D3DDECLTYPE_UBYTE4, D3DDECLUSAGE_BLENDINDICES, 0);
2091 else if (fvf & D3DFVF_LASTBETA_D3DCOLOR)
2092 append_decl_element(declaration, &idx, &offset, D3DDECLTYPE_D3DCOLOR, D3DDECLUSAGE_BLENDINDICES, 0);
2097 if (fvf & D3DFVF_NORMAL)
2098 append_decl_element(declaration, &idx, &offset, D3DDECLTYPE_FLOAT3, D3DDECLUSAGE_NORMAL, 0);
2099 if (fvf & D3DFVF_PSIZE)
2100 append_decl_element(declaration, &idx, &offset, D3DDECLTYPE_FLOAT1, D3DDECLUSAGE_PSIZE, 0);
2101 if (fvf & D3DFVF_DIFFUSE)
2102 append_decl_element(declaration, &idx, &offset, D3DDECLTYPE_D3DCOLOR, D3DDECLUSAGE_COLOR, 0);
2103 if (fvf & D3DFVF_SPECULAR)
2104 append_decl_element(declaration, &idx, &offset, D3DDECLTYPE_D3DCOLOR, D3DDECLUSAGE_COLOR, 1);
2106 for (i = 0; i < tex_count; ++i)
2108 switch ((fvf >> (16 + 2 * i)) & 0x03)
2110 case D3DFVF_TEXTUREFORMAT1:
2111 append_decl_element(declaration, &idx, &offset, D3DDECLTYPE_FLOAT1, D3DDECLUSAGE_TEXCOORD, i);
2113 case D3DFVF_TEXTUREFORMAT2:
2114 append_decl_element(declaration, &idx, &offset, D3DDECLTYPE_FLOAT2, D3DDECLUSAGE_TEXCOORD, i);
2116 case D3DFVF_TEXTUREFORMAT3:
2117 append_decl_element(declaration, &idx, &offset, D3DDECLTYPE_FLOAT3, D3DDECLUSAGE_TEXCOORD, i);
2119 case D3DFVF_TEXTUREFORMAT4:
2120 append_decl_element(declaration, &idx, &offset, D3DDECLTYPE_FLOAT4, D3DDECLUSAGE_TEXCOORD, i);
2125 declaration[idx] = end_element;
2130 /*************************************************************************
2131 * D3DXFVFFromDeclarator
2133 HRESULT WINAPI D3DXFVFFromDeclarator(const D3DVERTEXELEMENT9 *declaration, DWORD *fvf)
2135 unsigned int i = 0, texture, offset;
2137 TRACE("(%p, %p)\n", declaration, fvf);
2140 if (declaration[0].Type == D3DDECLTYPE_FLOAT3 && declaration[0].Usage == D3DDECLUSAGE_POSITION)
2142 if ((declaration[1].Type == D3DDECLTYPE_FLOAT4 && declaration[1].Usage == D3DDECLUSAGE_BLENDWEIGHT &&
2143 declaration[1].UsageIndex == 0) &&
2144 (declaration[2].Type == D3DDECLTYPE_FLOAT1 && declaration[2].Usage == D3DDECLUSAGE_BLENDINDICES &&
2145 declaration[2].UsageIndex == 0))
2147 return D3DERR_INVALIDCALL;
2149 else if ((declaration[1].Type == D3DDECLTYPE_UBYTE4 || declaration[1].Type == D3DDECLTYPE_D3DCOLOR) &&
2150 declaration[1].Usage == D3DDECLUSAGE_BLENDINDICES && declaration[1].UsageIndex == 0)
2152 if (declaration[1].Type == D3DDECLTYPE_UBYTE4)
2154 *fvf |= D3DFVF_XYZB1 | D3DFVF_LASTBETA_UBYTE4;
2158 *fvf |= D3DFVF_XYZB1 | D3DFVF_LASTBETA_D3DCOLOR;
2162 else if (declaration[1].Type <= D3DDECLTYPE_FLOAT4 && declaration[1].Usage == D3DDECLUSAGE_BLENDWEIGHT &&
2163 declaration[1].UsageIndex == 0)
2165 if ((declaration[2].Type == D3DDECLTYPE_UBYTE4 || declaration[2].Type == D3DDECLTYPE_D3DCOLOR) &&
2166 declaration[2].Usage == D3DDECLUSAGE_BLENDINDICES && declaration[2].UsageIndex == 0)
2168 if (declaration[2].Type == D3DDECLTYPE_UBYTE4)
2170 *fvf |= D3DFVF_LASTBETA_UBYTE4;
2174 *fvf |= D3DFVF_LASTBETA_D3DCOLOR;
2176 switch (declaration[1].Type)
2178 case D3DDECLTYPE_FLOAT1: *fvf |= D3DFVF_XYZB2; break;
2179 case D3DDECLTYPE_FLOAT2: *fvf |= D3DFVF_XYZB3; break;
2180 case D3DDECLTYPE_FLOAT3: *fvf |= D3DFVF_XYZB4; break;
2181 case D3DDECLTYPE_FLOAT4: *fvf |= D3DFVF_XYZB5; break;
2187 switch (declaration[1].Type)
2189 case D3DDECLTYPE_FLOAT1: *fvf |= D3DFVF_XYZB1; break;
2190 case D3DDECLTYPE_FLOAT2: *fvf |= D3DFVF_XYZB2; break;
2191 case D3DDECLTYPE_FLOAT3: *fvf |= D3DFVF_XYZB3; break;
2192 case D3DDECLTYPE_FLOAT4: *fvf |= D3DFVF_XYZB4; break;
2203 else if (declaration[0].Type == D3DDECLTYPE_FLOAT4 && declaration[0].Usage == D3DDECLUSAGE_POSITIONT &&
2204 declaration[0].UsageIndex == 0)
2206 *fvf |= D3DFVF_XYZRHW;
2210 if (declaration[i].Type == D3DDECLTYPE_FLOAT3 && declaration[i].Usage == D3DDECLUSAGE_NORMAL)
2212 *fvf |= D3DFVF_NORMAL;
2215 if (declaration[i].Type == D3DDECLTYPE_FLOAT1 && declaration[i].Usage == D3DDECLUSAGE_PSIZE &&
2216 declaration[i].UsageIndex == 0)
2218 *fvf |= D3DFVF_PSIZE;
2221 if (declaration[i].Type == D3DDECLTYPE_D3DCOLOR && declaration[i].Usage == D3DDECLUSAGE_COLOR &&
2222 declaration[i].UsageIndex == 0)
2224 *fvf |= D3DFVF_DIFFUSE;
2227 if (declaration[i].Type == D3DDECLTYPE_D3DCOLOR && declaration[i].Usage == D3DDECLUSAGE_COLOR &&
2228 declaration[i].UsageIndex == 1)
2230 *fvf |= D3DFVF_SPECULAR;
2234 for (texture = 0; texture < D3DDP_MAXTEXCOORD; i++, texture++)
2236 if (declaration[i].Stream == 0xFF)
2240 else if (declaration[i].Type == D3DDECLTYPE_FLOAT1 && declaration[i].Usage == D3DDECLUSAGE_TEXCOORD &&
2241 declaration[i].UsageIndex == texture)
2243 *fvf |= D3DFVF_TEXCOORDSIZE1(declaration[i].UsageIndex);
2245 else if (declaration[i].Type == D3DDECLTYPE_FLOAT2 && declaration[i].Usage == D3DDECLUSAGE_TEXCOORD &&
2246 declaration[i].UsageIndex == texture)
2248 *fvf |= D3DFVF_TEXCOORDSIZE2(declaration[i].UsageIndex);
2250 else if (declaration[i].Type == D3DDECLTYPE_FLOAT3 && declaration[i].Usage == D3DDECLUSAGE_TEXCOORD &&
2251 declaration[i].UsageIndex == texture)
2253 *fvf |= D3DFVF_TEXCOORDSIZE3(declaration[i].UsageIndex);
2255 else if (declaration[i].Type == D3DDECLTYPE_FLOAT4 && declaration[i].Usage == D3DDECLUSAGE_TEXCOORD &&
2256 declaration[i].UsageIndex == texture)
2258 *fvf |= D3DFVF_TEXCOORDSIZE4(declaration[i].UsageIndex);
2262 return D3DERR_INVALIDCALL;
2266 *fvf |= (texture << D3DFVF_TEXCOUNT_SHIFT);
2268 for (offset = 0, i = 0; declaration[i].Stream != 0xFF;
2269 offset += d3dx_decltype_size[declaration[i].Type], i++)
2271 if (declaration[i].Offset != offset)
2273 return D3DERR_INVALIDCALL;
2280 /*************************************************************************
2281 * D3DXGetFVFVertexSize
2283 static UINT Get_TexCoord_Size_From_FVF(DWORD FVF, int tex_num)
2285 return (((((FVF) >> (16 + (2 * (tex_num)))) + 1) & 0x03) + 1);
2288 UINT WINAPI D3DXGetFVFVertexSize(DWORD FVF)
2292 UINT numTextures = (FVF & D3DFVF_TEXCOUNT_MASK) >> D3DFVF_TEXCOUNT_SHIFT;
2294 if (FVF & D3DFVF_NORMAL) size += sizeof(D3DXVECTOR3);
2295 if (FVF & D3DFVF_DIFFUSE) size += sizeof(DWORD);
2296 if (FVF & D3DFVF_SPECULAR) size += sizeof(DWORD);
2297 if (FVF & D3DFVF_PSIZE) size += sizeof(DWORD);
2299 switch (FVF & D3DFVF_POSITION_MASK)
2301 case D3DFVF_XYZ: size += sizeof(D3DXVECTOR3); break;
2302 case D3DFVF_XYZRHW: size += 4 * sizeof(FLOAT); break;
2303 case D3DFVF_XYZB1: size += 4 * sizeof(FLOAT); break;
2304 case D3DFVF_XYZB2: size += 5 * sizeof(FLOAT); break;
2305 case D3DFVF_XYZB3: size += 6 * sizeof(FLOAT); break;
2306 case D3DFVF_XYZB4: size += 7 * sizeof(FLOAT); break;
2307 case D3DFVF_XYZB5: size += 8 * sizeof(FLOAT); break;
2308 case D3DFVF_XYZW: size += 4 * sizeof(FLOAT); break;
2311 for (i = 0; i < numTextures; i++)
2313 size += Get_TexCoord_Size_From_FVF(FVF, i) * sizeof(FLOAT);
2319 /*************************************************************************
2320 * D3DXGetDeclVertexSize
2322 UINT WINAPI D3DXGetDeclVertexSize(const D3DVERTEXELEMENT9 *decl, DWORD stream_idx)
2324 const D3DVERTEXELEMENT9 *element;
2327 TRACE("decl %p, stream_idx %u\n", decl, stream_idx);
2329 if (!decl) return 0;
2331 for (element = decl; element->Stream != 0xff; ++element)
2335 if (element->Stream != stream_idx) continue;
2337 if (element->Type >= sizeof(d3dx_decltype_size) / sizeof(*d3dx_decltype_size))
2339 FIXME("Unhandled element type %#x, size will be incorrect.\n", element->Type);
2343 type_size = d3dx_decltype_size[element->Type];
2344 if (element->Offset + type_size > size) size = element->Offset + type_size;
2350 /*************************************************************************
2353 UINT WINAPI D3DXGetDeclLength(const D3DVERTEXELEMENT9 *decl)
2355 const D3DVERTEXELEMENT9 *element;
2357 TRACE("decl %p\n", decl);
2359 /* null decl results in exception on Windows XP */
2361 for (element = decl; element->Stream != 0xff; ++element);
2363 return element - decl;
2366 /*************************************************************************
2369 BOOL WINAPI D3DXIntersectTri(CONST D3DXVECTOR3 *p0, CONST D3DXVECTOR3 *p1, CONST D3DXVECTOR3 *p2, CONST D3DXVECTOR3 *praypos, CONST D3DXVECTOR3 *praydir, FLOAT *pu, FLOAT *pv, FLOAT *pdist)
2374 m.u.m[0][0] = p1->x - p0->x;
2375 m.u.m[1][0] = p2->x - p0->x;
2376 m.u.m[2][0] = -praydir->x;
2378 m.u.m[0][1] = p1->y - p0->z;
2379 m.u.m[1][1] = p2->y - p0->z;
2380 m.u.m[2][1] = -praydir->y;
2382 m.u.m[0][2] = p1->z - p0->z;
2383 m.u.m[1][2] = p2->z - p0->z;
2384 m.u.m[2][2] = -praydir->z;
2391 vec.x = praypos->x - p0->x;
2392 vec.y = praypos->y - p0->y;
2393 vec.z = praypos->z - p0->z;
2396 if ( D3DXMatrixInverse(&m, NULL, &m) )
2398 D3DXVec4Transform(&vec, &vec, &m);
2399 if ( (vec.x >= 0.0f) && (vec.y >= 0.0f) && (vec.x + vec.y <= 1.0f) && (vec.z >= 0.0f) )
2403 *pdist = fabs( vec.z );
2411 /*************************************************************************
2412 * D3DXSphereBoundProbe
2414 BOOL WINAPI D3DXSphereBoundProbe(CONST D3DXVECTOR3 *pcenter, FLOAT radius, CONST D3DXVECTOR3 *prayposition, CONST D3DXVECTOR3 *praydirection)
2416 D3DXVECTOR3 difference;
2419 a = D3DXVec3LengthSq(praydirection);
2420 if (!D3DXVec3Subtract(&difference, prayposition, pcenter)) return FALSE;
2421 b = D3DXVec3Dot(&difference, praydirection);
2422 c = D3DXVec3LengthSq(&difference) - radius * radius;
2425 if ( ( d <= 0.0f ) || ( sqrt(d) <= b ) ) return FALSE;
2429 /*************************************************************************
2432 HRESULT WINAPI D3DXCreateMesh(DWORD numfaces, DWORD numvertices, DWORD options, CONST D3DVERTEXELEMENT9 *declaration,
2433 LPDIRECT3DDEVICE9 device, LPD3DXMESH *mesh)
2437 IDirect3DVertexDeclaration9 *vertex_declaration;
2438 UINT vertex_declaration_size;
2440 IDirect3DVertexBuffer9 *vertex_buffer;
2441 IDirect3DIndexBuffer9 *index_buffer;
2442 DWORD *attrib_buffer;
2443 ID3DXMeshImpl *object;
2444 DWORD index_usage = 0;
2445 D3DPOOL index_pool = D3DPOOL_DEFAULT;
2446 D3DFORMAT index_format = D3DFMT_INDEX16;
2447 DWORD vertex_usage = 0;
2448 D3DPOOL vertex_pool = D3DPOOL_DEFAULT;
2451 TRACE("(%d, %d, %x, %p, %p, %p)\n", numfaces, numvertices, options, declaration, device, mesh);
2453 if (numfaces == 0 || numvertices == 0 || declaration == NULL || device == NULL || mesh == NULL ||
2454 /* D3DXMESH_VB_SHARE is for cloning, and D3DXMESH_USEHWONLY is for ConvertToBlendedMesh */
2455 (options & (D3DXMESH_VB_SHARE | D3DXMESH_USEHWONLY | 0xfffe0000)))
2457 return D3DERR_INVALIDCALL;
2459 for (i = 0; declaration[i].Stream != 0xff; i++)
2460 if (declaration[i].Stream != 0)
2461 return D3DERR_INVALIDCALL;
2464 if (options & D3DXMESH_32BIT)
2465 index_format = D3DFMT_INDEX32;
2467 if (options & D3DXMESH_DONOTCLIP) {
2468 index_usage |= D3DUSAGE_DONOTCLIP;
2469 vertex_usage |= D3DUSAGE_DONOTCLIP;
2471 if (options & D3DXMESH_POINTS) {
2472 index_usage |= D3DUSAGE_POINTS;
2473 vertex_usage |= D3DUSAGE_POINTS;
2475 if (options & D3DXMESH_RTPATCHES) {
2476 index_usage |= D3DUSAGE_RTPATCHES;
2477 vertex_usage |= D3DUSAGE_RTPATCHES;
2479 if (options & D3DXMESH_NPATCHES) {
2480 index_usage |= D3DUSAGE_NPATCHES;
2481 vertex_usage |= D3DUSAGE_NPATCHES;
2484 if (options & D3DXMESH_VB_SYSTEMMEM)
2485 vertex_pool = D3DPOOL_SYSTEMMEM;
2486 else if (options & D3DXMESH_VB_MANAGED)
2487 vertex_pool = D3DPOOL_MANAGED;
2489 if (options & D3DXMESH_VB_WRITEONLY)
2490 vertex_usage |= D3DUSAGE_WRITEONLY;
2491 if (options & D3DXMESH_VB_DYNAMIC)
2492 vertex_usage |= D3DUSAGE_DYNAMIC;
2493 if (options & D3DXMESH_VB_SOFTWAREPROCESSING)
2494 vertex_usage |= D3DUSAGE_SOFTWAREPROCESSING;
2496 if (options & D3DXMESH_IB_SYSTEMMEM)
2497 index_pool = D3DPOOL_SYSTEMMEM;
2498 else if (options & D3DXMESH_IB_MANAGED)
2499 index_pool = D3DPOOL_MANAGED;
2501 if (options & D3DXMESH_IB_WRITEONLY)
2502 index_usage |= D3DUSAGE_WRITEONLY;
2503 if (options & D3DXMESH_IB_DYNAMIC)
2504 index_usage |= D3DUSAGE_DYNAMIC;
2505 if (options & D3DXMESH_IB_SOFTWAREPROCESSING)
2506 index_usage |= D3DUSAGE_SOFTWAREPROCESSING;
2508 hr = D3DXFVFFromDeclarator(declaration, &fvf);
2514 /* Create vertex declaration */
2515 hr = IDirect3DDevice9_CreateVertexDeclaration(device,
2517 &vertex_declaration);
2520 WARN("Unexpected return value %x from IDirect3DDevice9_CreateVertexDeclaration.\n",hr);
2523 vertex_declaration_size = D3DXGetDeclVertexSize(declaration, declaration[0].Stream);
2525 /* Create vertex buffer */
2526 hr = IDirect3DDevice9_CreateVertexBuffer(device,
2527 numvertices * vertex_declaration_size,
2535 WARN("Unexpected return value %x from IDirect3DDevice9_CreateVertexBuffer.\n",hr);
2536 IDirect3DVertexDeclaration9_Release(vertex_declaration);
2540 /* Create index buffer */
2541 hr = IDirect3DDevice9_CreateIndexBuffer(device,
2542 numfaces * 3 * ((index_format == D3DFMT_INDEX16) ? 2 : 4),
2550 WARN("Unexpected return value %x from IDirect3DDevice9_CreateVertexBuffer.\n",hr);
2551 IDirect3DVertexBuffer9_Release(vertex_buffer);
2552 IDirect3DVertexDeclaration9_Release(vertex_declaration);
2556 attrib_buffer = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, numfaces * sizeof(*attrib_buffer));
2557 object = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(ID3DXMeshImpl));
2558 if (object == NULL || attrib_buffer == NULL)
2560 HeapFree(GetProcessHeap(), 0, attrib_buffer);
2561 IDirect3DIndexBuffer9_Release(index_buffer);
2562 IDirect3DVertexBuffer9_Release(vertex_buffer);
2563 IDirect3DVertexDeclaration9_Release(vertex_declaration);
2565 return E_OUTOFMEMORY;
2567 object->ID3DXMesh_iface.lpVtbl = &D3DXMesh_Vtbl;
2570 object->numfaces = numfaces;
2571 object->numvertices = numvertices;
2572 object->options = options;
2574 object->device = device;
2575 IDirect3DDevice9_AddRef(device);
2577 copy_declaration(object->cached_declaration, declaration, num_elem);
2578 object->vertex_declaration = vertex_declaration;
2579 object->vertex_declaration_size = vertex_declaration_size;
2580 object->num_elem = num_elem;
2581 object->vertex_buffer = vertex_buffer;
2582 object->index_buffer = index_buffer;
2583 object->attrib_buffer = attrib_buffer;
2585 *mesh = &object->ID3DXMesh_iface;
2590 /*************************************************************************
2593 HRESULT WINAPI D3DXCreateMeshFVF(DWORD numfaces, DWORD numvertices, DWORD options, DWORD fvf,
2594 LPDIRECT3DDEVICE9 device, LPD3DXMESH *mesh)
2597 D3DVERTEXELEMENT9 declaration[MAX_FVF_DECL_SIZE];
2599 TRACE("(%u, %u, %u, %u, %p, %p)\n", numfaces, numvertices, options, fvf, device, mesh);
2601 hr = D3DXDeclaratorFromFVF(fvf, declaration);
2602 if (FAILED(hr)) return hr;
2604 return D3DXCreateMesh(numfaces, numvertices, options, declaration, device, mesh);
2610 DWORD num_poly_faces;
2611 DWORD num_tri_faces;
2612 D3DXVECTOR3 *vertices;
2613 DWORD *num_tri_per_face;
2618 /* optional mesh data */
2621 D3DXVECTOR3 *normals;
2622 DWORD *normal_indices;
2624 D3DXVECTOR2 *tex_coords;
2626 DWORD *vertex_colors;
2628 DWORD num_materials;
2629 D3DXMATERIAL *materials;
2630 DWORD *material_indices;
2633 static HRESULT get_next_child(IDirectXFileData *filedata, IDirectXFileData **child, const GUID **type)
2636 IDirectXFileDataReference *child_ref = NULL;
2637 IDirectXFileObject *child_obj = NULL;
2638 IDirectXFileData *child_data = NULL;
2640 hr = IDirectXFileData_GetNextObject(filedata, &child_obj);
2641 if (FAILED(hr)) return hr;
2643 hr = IDirectXFileObject_QueryInterface(child_obj, &IID_IDirectXFileDataReference, (void**)&child_ref);
2644 if (SUCCEEDED(hr)) {
2645 hr = IDirectXFileDataReference_Resolve(child_ref, &child_data);
2646 IDirectXFileDataReference_Release(child_ref);
2648 hr = IDirectXFileObject_QueryInterface(child_obj, &IID_IDirectXFileData, (void**)&child_data);
2650 IDirectXFileObject_Release(child_obj);
2654 hr = IDirectXFileData_GetType(child_data, type);
2656 IDirectXFileData_Release(child_data);
2658 *child = child_data;
2664 static HRESULT parse_texture_filename(IDirectXFileData *filedata, LPSTR *filename_out)
2670 char *filename = NULL;
2672 /* template TextureFilename {
2677 HeapFree(GetProcessHeap(), 0, *filename_out);
2678 *filename_out = NULL;
2680 hr = IDirectXFileData_GetData(filedata, NULL, &data_size, (void**)&data);
2681 if (FAILED(hr)) return hr;
2683 if (data_size < sizeof(LPSTR)) {
2684 WARN("truncated data (%u bytes)\n", data_size);
2687 filename_in = *(LPSTR*)data;
2689 filename = HeapAlloc(GetProcessHeap(), 0, strlen(filename_in) + 1);
2690 if (!filename) return E_OUTOFMEMORY;
2692 strcpy(filename, filename_in);
2693 *filename_out = filename;
2698 static HRESULT parse_material(IDirectXFileData *filedata, D3DXMATERIAL *material)
2704 IDirectXFileData *child;
2706 material->pTextureFilename = NULL;
2708 hr = IDirectXFileData_GetData(filedata, NULL, &data_size, (void**)&data);
2709 if (FAILED(hr)) return hr;
2712 * template ColorRGBA {
2718 * template ColorRGB {
2723 * template Material {
2724 * ColorRGBA faceColor;
2726 * ColorRGB specularColor;
2727 * ColorRGB emissiveColor;
2731 if (data_size != sizeof(FLOAT) * 11) {
2732 WARN("incorrect data size (%u bytes)\n", data_size);
2736 memcpy(&material->MatD3D.Diffuse, data, sizeof(D3DCOLORVALUE));
2737 data += sizeof(D3DCOLORVALUE);
2738 material->MatD3D.Power = *(FLOAT*)data;
2739 data += sizeof(FLOAT);
2740 memcpy(&material->MatD3D.Specular, data, sizeof(FLOAT) * 3);
2741 material->MatD3D.Specular.a = 1.0f;
2742 data += 3 * sizeof(FLOAT);
2743 memcpy(&material->MatD3D.Emissive, data, sizeof(FLOAT) * 3);
2744 material->MatD3D.Emissive.a = 1.0f;
2745 material->MatD3D.Ambient.r = 0.0f;
2746 material->MatD3D.Ambient.g = 0.0f;
2747 material->MatD3D.Ambient.b = 0.0f;
2748 material->MatD3D.Ambient.a = 1.0f;
2750 while (SUCCEEDED(hr = get_next_child(filedata, &child, &type)))
2752 if (IsEqualGUID(type, &TID_D3DRMTextureFilename)) {
2753 hr = parse_texture_filename(child, &material->pTextureFilename);
2754 if (FAILED(hr)) break;
2757 return hr == DXFILEERR_NOMOREOBJECTS ? D3D_OK : hr;
2760 static void destroy_materials(struct mesh_data *mesh)
2763 for (i = 0; i < mesh->num_materials; i++)
2764 HeapFree(GetProcessHeap(), 0, mesh->materials[i].pTextureFilename);
2765 HeapFree(GetProcessHeap(), 0, mesh->materials);
2766 HeapFree(GetProcessHeap(), 0, mesh->material_indices);
2767 mesh->num_materials = 0;
2768 mesh->materials = NULL;
2769 mesh->material_indices = NULL;
2772 static HRESULT parse_material_list(IDirectXFileData *filedata, struct mesh_data *mesh)
2776 DWORD *data, *in_ptr;
2778 IDirectXFileData *child;
2779 DWORD num_materials;
2782 destroy_materials(mesh);
2784 hr = IDirectXFileData_GetData(filedata, NULL, &data_size, (void**)&data);
2785 if (FAILED(hr)) return hr;
2787 /* template MeshMaterialList {
2789 * DWORD nFaceIndexes;
2790 * array DWORD faceIndexes[nFaceIndexes];
2797 if (data_size < sizeof(DWORD))
2798 goto truncated_data_error;
2799 num_materials = *in_ptr++;
2803 if (data_size < 2 * sizeof(DWORD))
2804 goto truncated_data_error;
2805 if (*in_ptr++ != mesh->num_poly_faces) {
2806 WARN("number of material face indices (%u) doesn't match number of faces (%u)\n",
2807 *(in_ptr - 1), mesh->num_poly_faces);
2810 if (data_size < 2 * sizeof(DWORD) + mesh->num_poly_faces * sizeof(DWORD))
2811 goto truncated_data_error;
2812 for (i = 0; i < mesh->num_poly_faces; i++) {
2813 if (*in_ptr++ >= num_materials) {
2814 WARN("face %u: reference to undefined material %u (only %u materials)\n",
2815 i, *(in_ptr - 1), num_materials);
2820 mesh->materials = HeapAlloc(GetProcessHeap(), 0, num_materials * sizeof(*mesh->materials));
2821 mesh->material_indices = HeapAlloc(GetProcessHeap(), 0, mesh->num_poly_faces * sizeof(*mesh->material_indices));
2822 if (!mesh->materials || !mesh->material_indices)
2823 return E_OUTOFMEMORY;
2824 memcpy(mesh->material_indices, data + 2, mesh->num_poly_faces * sizeof(DWORD));
2826 while (SUCCEEDED(hr = get_next_child(filedata, &child, &type)))
2828 if (IsEqualGUID(type, &TID_D3DRMMaterial)) {
2829 if (mesh->num_materials >= num_materials) {
2830 WARN("more materials defined than declared\n");
2833 hr = parse_material(child, &mesh->materials[mesh->num_materials++]);
2834 if (FAILED(hr)) break;
2837 if (hr != DXFILEERR_NOMOREOBJECTS)
2839 if (num_materials != mesh->num_materials) {
2840 WARN("only %u of %u materials defined\n", num_materials, mesh->num_materials);
2845 truncated_data_error:
2846 WARN("truncated data (%u bytes)\n", data_size);
2850 static HRESULT parse_texture_coords(IDirectXFileData *filedata, struct mesh_data *mesh)
2856 HeapFree(GetProcessHeap(), 0, mesh->tex_coords);
2857 mesh->tex_coords = NULL;
2859 hr = IDirectXFileData_GetData(filedata, NULL, &data_size, (void**)&data);
2860 if (FAILED(hr)) return hr;
2862 /* template Coords2d {
2866 * template MeshTextureCoords {
2867 * DWORD nTextureCoords;
2868 * array Coords2d textureCoords[nTextureCoords];
2872 if (data_size < sizeof(DWORD))
2873 goto truncated_data_error;
2874 if (*(DWORD*)data != mesh->num_vertices) {
2875 WARN("number of texture coordinates (%u) doesn't match number of vertices (%u)\n",
2876 *(DWORD*)data, mesh->num_vertices);
2879 data += sizeof(DWORD);
2880 if (data_size < sizeof(DWORD) + mesh->num_vertices * sizeof(*mesh->tex_coords))
2881 goto truncated_data_error;
2883 mesh->tex_coords = HeapAlloc(GetProcessHeap(), 0, mesh->num_vertices * sizeof(*mesh->tex_coords));
2884 if (!mesh->tex_coords) return E_OUTOFMEMORY;
2885 memcpy(mesh->tex_coords, data, mesh->num_vertices * sizeof(*mesh->tex_coords));
2887 mesh->fvf |= D3DFVF_TEX1;
2890 truncated_data_error:
2891 WARN("truncated data (%u bytes)\n", data_size);
2895 static HRESULT parse_vertex_colors(IDirectXFileData *filedata, struct mesh_data *mesh)
2903 HeapFree(GetProcessHeap(), 0, mesh->vertex_colors);
2904 mesh->vertex_colors = NULL;
2906 hr = IDirectXFileData_GetData(filedata, NULL, &data_size, (void**)&data);
2907 if (FAILED(hr)) return hr;
2909 /* template IndexedColor {
2911 * ColorRGBA indexColor;
2913 * template MeshVertexColors {
2914 * DWORD nVertexColors;
2915 * array IndexedColor vertexColors[nVertexColors];
2919 if (data_size < sizeof(DWORD))
2920 goto truncated_data_error;
2921 num_colors = *(DWORD*)data;
2922 data += sizeof(DWORD);
2923 if (data_size < sizeof(DWORD) + num_colors * (sizeof(DWORD) + sizeof(D3DCOLORVALUE)))
2924 goto truncated_data_error;
2926 mesh->vertex_colors = HeapAlloc(GetProcessHeap(), 0, mesh->num_vertices * sizeof(DWORD));
2927 if (!mesh->vertex_colors)
2928 return E_OUTOFMEMORY;
2930 for (i = 0; i < mesh->num_vertices; i++)
2931 mesh->vertex_colors[i] = D3DCOLOR_ARGB(0, 0xff, 0xff, 0xff);
2932 for (i = 0; i < num_colors; i++)
2934 D3DCOLORVALUE color;
2935 DWORD index = *(DWORD*)data;
2936 data += sizeof(DWORD);
2937 if (index >= mesh->num_vertices) {
2938 WARN("vertex color %u references undefined vertex %u (only %u vertices)\n",
2939 i, index, mesh->num_vertices);
2942 memcpy(&color, data, sizeof(color));
2943 data += sizeof(color);
2944 color.r = min(1.0f, max(0.0f, color.r));
2945 color.g = min(1.0f, max(0.0f, color.g));
2946 color.b = min(1.0f, max(0.0f, color.b));
2947 color.a = min(1.0f, max(0.0f, color.a));
2948 mesh->vertex_colors[index] = D3DCOLOR_ARGB((BYTE)(color.a * 255.0f + 0.5f),
2949 (BYTE)(color.r * 255.0f + 0.5f),
2950 (BYTE)(color.g * 255.0f + 0.5f),
2951 (BYTE)(color.b * 255.0f + 0.5f));
2954 mesh->fvf |= D3DFVF_DIFFUSE;
2957 truncated_data_error:
2958 WARN("truncated data (%u bytes)\n", data_size);
2962 static HRESULT parse_normals(IDirectXFileData *filedata, struct mesh_data *mesh)
2967 DWORD *index_out_ptr;
2969 DWORD num_face_indices = mesh->num_poly_faces * 2 + mesh->num_tri_faces;
2971 HeapFree(GetProcessHeap(), 0, mesh->normals);
2972 mesh->num_normals = 0;
2973 mesh->normals = NULL;
2974 mesh->normal_indices = NULL;
2975 mesh->fvf |= D3DFVF_NORMAL;
2977 hr = IDirectXFileData_GetData(filedata, NULL, &data_size, (void**)&data);
2978 if (FAILED(hr)) return hr;
2980 /* template Vector {
2985 * template MeshFace {
2986 * DWORD nFaceVertexIndices;
2987 * array DWORD faceVertexIndices[nFaceVertexIndices];
2989 * template MeshNormals {
2991 * array Vector normals[nNormals];
2992 * DWORD nFaceNormals;
2993 * array MeshFace faceNormals[nFaceNormals];
2997 if (data_size < sizeof(DWORD) * 2)
2998 goto truncated_data_error;
2999 mesh->num_normals = *(DWORD*)data;
3000 data += sizeof(DWORD);
3001 if (data_size < sizeof(DWORD) * 2 + mesh->num_normals * sizeof(D3DXVECTOR3) +
3002 num_face_indices * sizeof(DWORD))
3003 goto truncated_data_error;
3005 mesh->normals = HeapAlloc(GetProcessHeap(), 0, mesh->num_normals * sizeof(D3DXVECTOR3));
3006 mesh->normal_indices = HeapAlloc(GetProcessHeap(), 0, num_face_indices * sizeof(DWORD));
3007 if (!mesh->normals || !mesh->normal_indices)
3008 return E_OUTOFMEMORY;
3010 memcpy(mesh->normals, data, mesh->num_normals * sizeof(D3DXVECTOR3));
3011 data += mesh->num_normals * sizeof(D3DXVECTOR3);
3012 for (i = 0; i < mesh->num_normals; i++)
3013 D3DXVec3Normalize(&mesh->normals[i], &mesh->normals[i]);
3015 if (*(DWORD*)data != mesh->num_poly_faces) {
3016 WARN("number of face normals (%u) doesn't match number of faces (%u)\n",
3017 *(DWORD*)data, mesh->num_poly_faces);
3020 data += sizeof(DWORD);
3021 index_out_ptr = mesh->normal_indices;
3022 for (i = 0; i < mesh->num_poly_faces; i++)
3025 DWORD count = *(DWORD*)data;
3026 if (count != mesh->num_tri_per_face[i] + 2) {
3027 WARN("face %u: number of normals (%u) doesn't match number of vertices (%u)\n",
3028 i, count, mesh->num_tri_per_face[i] + 2);
3031 data += sizeof(DWORD);
3033 for (j = 0; j < count; j++) {
3034 DWORD normal_index = *(DWORD*)data;
3035 if (normal_index >= mesh->num_normals) {
3036 WARN("face %u, normal index %u: reference to undefined normal %u (only %u normals)\n",
3037 i, j, normal_index, mesh->num_normals);
3040 *index_out_ptr++ = normal_index;
3041 data += sizeof(DWORD);
3046 truncated_data_error:
3047 WARN("truncated data (%u bytes)\n", data_size);
3051 /* for provide_flags parameters */
3052 #define PROVIDE_MATERIALS 0x1
3053 #define PROVIDE_SKININFO 0x2
3054 #define PROVIDE_ADJACENCY 0x4
3056 static HRESULT parse_mesh(IDirectXFileData *filedata, struct mesh_data *mesh_data, DWORD provide_flags)
3060 BYTE *data, *in_ptr;
3061 DWORD *index_out_ptr;
3063 IDirectXFileData *child;
3069 * array Vector vertices[nVertices];
3071 * array MeshFace faces[nFaces];
3076 hr = IDirectXFileData_GetData(filedata, NULL, &data_size, (void**)&data);
3077 if (FAILED(hr)) return hr;
3080 if (data_size < sizeof(DWORD) * 2)
3081 goto truncated_data_error;
3082 mesh_data->num_vertices = *(DWORD*)in_ptr;
3083 if (data_size < sizeof(DWORD) * 2 + mesh_data->num_vertices * sizeof(D3DXVECTOR3))
3084 goto truncated_data_error;
3085 in_ptr += sizeof(DWORD) + mesh_data->num_vertices * sizeof(D3DXVECTOR3);
3087 mesh_data->num_poly_faces = *(DWORD*)in_ptr;
3088 in_ptr += sizeof(DWORD);
3090 mesh_data->num_tri_faces = 0;
3091 for (i = 0; i < mesh_data->num_poly_faces; i++)
3093 DWORD num_poly_vertices;
3096 if (data_size - (in_ptr - data) < sizeof(DWORD))
3097 goto truncated_data_error;
3098 num_poly_vertices = *(DWORD*)in_ptr;
3099 in_ptr += sizeof(DWORD);
3100 if (data_size - (in_ptr - data) < num_poly_vertices * sizeof(DWORD))
3101 goto truncated_data_error;
3102 if (num_poly_vertices < 3) {
3103 WARN("face %u has only %u vertices\n", i, num_poly_vertices);
3106 for (j = 0; j < num_poly_vertices; j++) {
3107 if (*(DWORD*)in_ptr >= mesh_data->num_vertices) {
3108 WARN("face %u, index %u: undefined vertex %u (only %u vertices)\n",
3109 i, j, *(DWORD*)in_ptr, mesh_data->num_vertices);
3112 in_ptr += sizeof(DWORD);
3114 mesh_data->num_tri_faces += num_poly_vertices - 2;
3117 mesh_data->fvf = D3DFVF_XYZ;
3119 mesh_data->vertices = HeapAlloc(GetProcessHeap(), 0,
3120 mesh_data->num_vertices * sizeof(*mesh_data->vertices));
3121 mesh_data->num_tri_per_face = HeapAlloc(GetProcessHeap(), 0,
3122 mesh_data->num_poly_faces * sizeof(*mesh_data->num_tri_per_face));
3123 mesh_data->indices = HeapAlloc(GetProcessHeap(), 0,
3124 (mesh_data->num_tri_faces + mesh_data->num_poly_faces * 2) * sizeof(*mesh_data->indices));
3125 if (!mesh_data->vertices || !mesh_data->num_tri_per_face || !mesh_data->indices)
3126 return E_OUTOFMEMORY;
3128 in_ptr = data + sizeof(DWORD);
3129 memcpy(mesh_data->vertices, in_ptr, mesh_data->num_vertices * sizeof(D3DXVECTOR3));
3130 in_ptr += mesh_data->num_vertices * sizeof(D3DXVECTOR3) + sizeof(DWORD);
3132 index_out_ptr = mesh_data->indices;
3133 for (i = 0; i < mesh_data->num_poly_faces; i++)
3137 count = *(DWORD*)in_ptr;
3138 in_ptr += sizeof(DWORD);
3139 mesh_data->num_tri_per_face[i] = count - 2;
3142 *index_out_ptr++ = *(DWORD*)in_ptr;
3143 in_ptr += sizeof(DWORD);
3147 while (SUCCEEDED(hr = get_next_child(filedata, &child, &type)))
3149 if (IsEqualGUID(type, &TID_D3DRMMeshNormals)) {
3150 hr = parse_normals(child, mesh_data);
3151 } else if (IsEqualGUID(type, &TID_D3DRMMeshVertexColors)) {
3152 hr = parse_vertex_colors(child, mesh_data);
3153 } else if (IsEqualGUID(type, &TID_D3DRMMeshTextureCoords)) {
3154 hr = parse_texture_coords(child, mesh_data);
3155 } else if (IsEqualGUID(type, &TID_D3DRMMeshMaterialList) &&
3156 (provide_flags & PROVIDE_MATERIALS))
3158 hr = parse_material_list(child, mesh_data);
3159 } else if (provide_flags & PROVIDE_SKININFO) {
3160 if (IsEqualGUID(type, &DXFILEOBJ_XSkinMeshHeader)) {
3161 FIXME("Skin mesh loading not implemented.\n");
3163 } else if (IsEqualGUID(type, &DXFILEOBJ_SkinWeights)) {
3164 /* ignored without XSkinMeshHeader */
3170 return hr == DXFILEERR_NOMOREOBJECTS ? D3D_OK : hr;
3171 truncated_data_error:
3172 WARN("truncated data (%u bytes)\n", data_size);
3176 static HRESULT generate_effects(ID3DXBuffer *materials, DWORD num_materials,
3177 ID3DXBuffer **effects)
3180 D3DXEFFECTINSTANCE *effect_ptr;
3182 const D3DXMATERIAL *material_ptr = ID3DXBuffer_GetBufferPointer(materials);
3183 static const struct {
3184 const char *param_name;
3188 } material_effects[] = {
3189 #define EFFECT_TABLE_ENTRY(str, field) \
3190 {str, sizeof(str), sizeof(material_ptr->MatD3D.field), offsetof(D3DXMATERIAL, MatD3D.field)}
3191 EFFECT_TABLE_ENTRY("Diffuse", Diffuse),
3192 EFFECT_TABLE_ENTRY("Power", Power),
3193 EFFECT_TABLE_ENTRY("Specular", Specular),
3194 EFFECT_TABLE_ENTRY("Emissive", Emissive),
3195 EFFECT_TABLE_ENTRY("Ambient", Ambient),
3196 #undef EFFECT_TABLE_ENTRY
3198 static const char texture_paramname[] = "Texture0@Name";
3202 /* effects buffer layout:
3204 * D3DXEFFECTINSTANCE effects[num_materials];
3205 * for (effect in effects)
3207 * D3DXEFFECTDEFAULT defaults[effect.NumDefaults];
3208 * for (default in defaults)
3210 * *default.pParamName;
3215 buffer_size = sizeof(D3DXEFFECTINSTANCE);
3216 buffer_size += sizeof(D3DXEFFECTDEFAULT) * ARRAY_SIZE(material_effects);
3217 for (i = 0; i < ARRAY_SIZE(material_effects); i++) {
3218 buffer_size += material_effects[i].name_size;
3219 buffer_size += material_effects[i].num_bytes;
3221 buffer_size *= num_materials;
3222 for (i = 0; i < num_materials; i++) {
3223 if (material_ptr[i].pTextureFilename) {
3224 buffer_size += sizeof(D3DXEFFECTDEFAULT);
3225 buffer_size += sizeof(texture_paramname);
3226 buffer_size += strlen(material_ptr[i].pTextureFilename) + 1;
3230 hr = D3DXCreateBuffer(buffer_size, effects);
3231 if (FAILED(hr)) return hr;
3232 effect_ptr = ID3DXBuffer_GetBufferPointer(*effects);
3233 out_ptr = (BYTE*)(effect_ptr + num_materials);
3235 for (i = 0; i < num_materials; i++)
3238 D3DXEFFECTDEFAULT *defaults = (D3DXEFFECTDEFAULT*)out_ptr;
3240 effect_ptr->pDefaults = defaults;
3241 effect_ptr->NumDefaults = material_ptr->pTextureFilename ? 6 : 5;
3242 out_ptr = (BYTE*)(effect_ptr->pDefaults + effect_ptr->NumDefaults);
3244 for (j = 0; j < ARRAY_SIZE(material_effects); j++)
3246 defaults->pParamName = (LPSTR)out_ptr;
3247 strcpy(defaults->pParamName, material_effects[j].param_name);
3248 defaults->pValue = defaults->pParamName + material_effects[j].name_size;
3249 defaults->Type = D3DXEDT_FLOATS;
3250 defaults->NumBytes = material_effects[j].num_bytes;
3251 memcpy(defaults->pValue, (BYTE*)material_ptr + material_effects[j].value_offset, defaults->NumBytes);
3252 out_ptr = (BYTE*)defaults->pValue + defaults->NumBytes;
3256 if (material_ptr->pTextureFilename) {
3257 defaults->pParamName = (LPSTR)out_ptr;
3258 strcpy(defaults->pParamName, texture_paramname);
3259 defaults->pValue = defaults->pParamName + sizeof(texture_paramname);
3260 defaults->Type = D3DXEDT_STRING;
3261 defaults->NumBytes = strlen(material_ptr->pTextureFilename) + 1;
3262 strcpy(defaults->pValue, material_ptr->pTextureFilename);
3263 out_ptr = (BYTE*)defaults->pValue + defaults->NumBytes;
3268 assert(out_ptr - (BYTE*)ID3DXBuffer_GetBufferPointer(*effects) == buffer_size);
3273 /* change to D3DXLoadSkinMeshFromXof when ID3DXFileData is implemented */
3274 static HRESULT load_skin_mesh_from_xof(IDirectXFileData *filedata,
3276 LPDIRECT3DDEVICE9 device,
3277 LPD3DXBUFFER *adjacency_out,
3278 LPD3DXBUFFER *materials_out,
3279 LPD3DXBUFFER *effects_out,
3280 DWORD *num_materials_out,
3281 LPD3DXSKININFO *skin_info_out,
3282 LPD3DXMESH *mesh_out)
3285 DWORD *index_in_ptr;
3286 struct mesh_data mesh_data;
3287 DWORD total_vertices;
3288 ID3DXMesh *d3dxmesh = NULL;
3289 ID3DXBuffer *adjacency = NULL;
3290 ID3DXBuffer *materials = NULL;
3291 ID3DXBuffer *effects = NULL;
3292 struct vertex_duplication {
3295 } *duplications = NULL;
3297 void *vertices = NULL;
3298 void *indices = NULL;
3300 DWORD provide_flags = 0;
3302 ZeroMemory(&mesh_data, sizeof(mesh_data));
3304 if (num_materials_out || materials_out || effects_out)
3305 provide_flags |= PROVIDE_MATERIALS;
3307 provide_flags |= PROVIDE_SKININFO;
3309 hr = parse_mesh(filedata, &mesh_data, provide_flags);
3310 if (FAILED(hr)) goto cleanup;
3312 total_vertices = mesh_data.num_vertices;
3313 if (mesh_data.fvf & D3DFVF_NORMAL) {
3314 /* duplicate vertices with multiple normals */
3315 DWORD num_face_indices = mesh_data.num_poly_faces * 2 + mesh_data.num_tri_faces;
3316 duplications = HeapAlloc(GetProcessHeap(), 0, (mesh_data.num_vertices + num_face_indices) * sizeof(*duplications));
3317 if (!duplications) {
3321 for (i = 0; i < total_vertices; i++)
3323 duplications[i].normal_index = -1;
3324 list_init(&duplications[i].entry);
3326 for (i = 0; i < num_face_indices; i++) {
3327 DWORD vertex_index = mesh_data.indices[i];
3328 DWORD normal_index = mesh_data.normal_indices[i];
3329 struct vertex_duplication *dup_ptr = &duplications[vertex_index];
3331 if (dup_ptr->normal_index == -1) {
3332 dup_ptr->normal_index = normal_index;
3334 D3DXVECTOR3 *new_normal = &mesh_data.normals[normal_index];
3335 struct list *dup_list = &dup_ptr->entry;
3337 D3DXVECTOR3 *cur_normal = &mesh_data.normals[dup_ptr->normal_index];
3338 if (new_normal->x == cur_normal->x &&
3339 new_normal->y == cur_normal->y &&
3340 new_normal->z == cur_normal->z)
3342 mesh_data.indices[i] = dup_ptr - duplications;
3344 } else if (!list_next(dup_list, &dup_ptr->entry)) {
3345 dup_ptr = &duplications[total_vertices++];
3346 dup_ptr->normal_index = normal_index;
3347 list_add_tail(dup_list, &dup_ptr->entry);
3348 mesh_data.indices[i] = dup_ptr - duplications;
3351 dup_ptr = LIST_ENTRY(list_next(dup_list, &dup_ptr->entry),
3352 struct vertex_duplication, entry);
3359 hr = D3DXCreateMeshFVF(mesh_data.num_tri_faces, total_vertices, options, mesh_data.fvf, device, &d3dxmesh);
3360 if (FAILED(hr)) goto cleanup;
3362 hr = d3dxmesh->lpVtbl->LockVertexBuffer(d3dxmesh, 0, &vertices);
3363 if (FAILED(hr)) goto cleanup;
3366 for (i = 0; i < mesh_data.num_vertices; i++) {
3367 *(D3DXVECTOR3*)out_ptr = mesh_data.vertices[i];
3368 out_ptr += sizeof(D3DXVECTOR3);
3369 if (mesh_data.fvf & D3DFVF_NORMAL) {
3370 if (duplications[i].normal_index == -1)
3371 ZeroMemory(out_ptr, sizeof(D3DXVECTOR3));
3373 *(D3DXVECTOR3*)out_ptr = mesh_data.normals[duplications[i].normal_index];
3374 out_ptr += sizeof(D3DXVECTOR3);
3376 if (mesh_data.fvf & D3DFVF_DIFFUSE) {
3377 *(DWORD*)out_ptr = mesh_data.vertex_colors[i];
3378 out_ptr += sizeof(DWORD);
3380 if (mesh_data.fvf & D3DFVF_TEX1) {
3381 *(D3DXVECTOR2*)out_ptr = mesh_data.tex_coords[i];
3382 out_ptr += sizeof(D3DXVECTOR2);
3385 if (mesh_data.fvf & D3DFVF_NORMAL) {
3386 DWORD vertex_size = D3DXGetFVFVertexSize(mesh_data.fvf);
3388 for (i = 0; i < mesh_data.num_vertices; i++) {
3389 struct vertex_duplication *dup_ptr;
3390 LIST_FOR_EACH_ENTRY(dup_ptr, &duplications[i].entry, struct vertex_duplication, entry)
3392 int j = dup_ptr - duplications;
3393 BYTE *dest_vertex = (BYTE*)vertices + j * vertex_size;
3395 memcpy(dest_vertex, out_ptr, vertex_size);
3396 dest_vertex += sizeof(D3DXVECTOR3);
3397 *(D3DXVECTOR3*)dest_vertex = mesh_data.normals[dup_ptr->normal_index];
3399 out_ptr += vertex_size;
3402 d3dxmesh->lpVtbl->UnlockVertexBuffer(d3dxmesh);
3404 hr = d3dxmesh->lpVtbl->LockIndexBuffer(d3dxmesh, 0, &indices);
3405 if (FAILED(hr)) goto cleanup;
3407 index_in_ptr = mesh_data.indices;
3408 #define FILL_INDEX_BUFFER(indices_var) \
3409 for (i = 0; i < mesh_data.num_poly_faces; i++) \
3411 DWORD count = mesh_data.num_tri_per_face[i]; \
3412 WORD first_index = *index_in_ptr++; \
3414 *indices_var++ = first_index; \
3415 *indices_var++ = *index_in_ptr; \
3417 *indices_var++ = *index_in_ptr; \
3421 if (options & D3DXMESH_32BIT) {
3422 DWORD *dword_indices = indices;
3423 FILL_INDEX_BUFFER(dword_indices)
3425 WORD *word_indices = indices;
3426 FILL_INDEX_BUFFER(word_indices)
3428 #undef FILL_INDEX_BUFFER
3429 d3dxmesh->lpVtbl->UnlockIndexBuffer(d3dxmesh);
3431 if (mesh_data.material_indices) {
3432 DWORD *attrib_buffer = NULL;
3433 hr = d3dxmesh->lpVtbl->LockAttributeBuffer(d3dxmesh, 0, &attrib_buffer);
3434 if (FAILED(hr)) goto cleanup;
3435 for (i = 0; i < mesh_data.num_poly_faces; i++)
3437 DWORD count = mesh_data.num_tri_per_face[i];
3439 *attrib_buffer++ = mesh_data.material_indices[i];
3441 d3dxmesh->lpVtbl->UnlockAttributeBuffer(d3dxmesh);
3443 hr = d3dxmesh->lpVtbl->OptimizeInplace(d3dxmesh,
3444 D3DXMESHOPT_ATTRSORT | D3DXMESHOPT_IGNOREVERTS | D3DXMESHOPT_DONOTSPLIT,
3445 NULL, NULL, NULL, NULL);
3446 if (FAILED(hr)) goto cleanup;
3449 if (mesh_data.num_materials && (materials_out || effects_out)) {
3450 DWORD buffer_size = mesh_data.num_materials * sizeof(D3DXMATERIAL);
3451 char *strings_out_ptr;
3452 D3DXMATERIAL *materials_ptr;
3454 for (i = 0; i < mesh_data.num_materials; i++) {
3455 if (mesh_data.materials[i].pTextureFilename)
3456 buffer_size += strlen(mesh_data.materials[i].pTextureFilename) + 1;
3459 hr = D3DXCreateBuffer(buffer_size, &materials);
3460 if (FAILED(hr)) goto cleanup;
3462 materials_ptr = ID3DXBuffer_GetBufferPointer(materials);
3463 memcpy(materials_ptr, mesh_data.materials, mesh_data.num_materials * sizeof(D3DXMATERIAL));
3464 strings_out_ptr = (char*)(materials_ptr + mesh_data.num_materials);
3465 for (i = 0; i < mesh_data.num_materials; i++) {
3466 if (materials_ptr[i].pTextureFilename) {
3467 strcpy(strings_out_ptr, mesh_data.materials[i].pTextureFilename);
3468 materials_ptr[i].pTextureFilename = strings_out_ptr;
3469 strings_out_ptr += strlen(mesh_data.materials[i].pTextureFilename) + 1;
3474 if (mesh_data.num_materials && effects_out) {
3475 hr = generate_effects(materials, mesh_data.num_materials, &effects);
3476 if (FAILED(hr)) goto cleanup;
3478 if (!materials_out) {
3479 ID3DXBuffer_Release(materials);
3484 if (adjacency_out) {
3485 hr = D3DXCreateBuffer(mesh_data.num_tri_faces * 3 * sizeof(DWORD), &adjacency);
3486 if (FAILED(hr)) goto cleanup;
3487 hr = d3dxmesh->lpVtbl->GenerateAdjacency(d3dxmesh, 0.0f, ID3DXBuffer_GetBufferPointer(adjacency));
3488 if (FAILED(hr)) goto cleanup;
3491 *mesh_out = d3dxmesh;
3492 if (adjacency_out) *adjacency_out = adjacency;
3493 if (num_materials_out) *num_materials_out = mesh_data.num_materials;
3494 if (materials_out) *materials_out = materials;
3495 if (effects_out) *effects_out = effects;
3496 if (skin_info_out) *skin_info_out = NULL;
3501 if (d3dxmesh) IUnknown_Release(d3dxmesh);
3502 if (adjacency) ID3DXBuffer_Release(adjacency);
3503 if (materials) ID3DXBuffer_Release(materials);
3504 if (effects) ID3DXBuffer_Release(effects);
3506 HeapFree(GetProcessHeap(), 0, mesh_data.vertices);
3507 HeapFree(GetProcessHeap(), 0, mesh_data.num_tri_per_face);
3508 HeapFree(GetProcessHeap(), 0, mesh_data.indices);
3509 HeapFree(GetProcessHeap(), 0, mesh_data.normals);
3510 HeapFree(GetProcessHeap(), 0, mesh_data.normal_indices);
3511 destroy_materials(&mesh_data);
3512 HeapFree(GetProcessHeap(), 0, mesh_data.tex_coords);
3513 HeapFree(GetProcessHeap(), 0, mesh_data.vertex_colors);
3514 HeapFree(GetProcessHeap(), 0, duplications);
3518 HRESULT WINAPI D3DXLoadMeshHierarchyFromXA(LPCSTR filename,
3520 LPDIRECT3DDEVICE9 device,
3521 LPD3DXALLOCATEHIERARCHY alloc_hier,
3522 LPD3DXLOADUSERDATA load_user_data,
3523 LPD3DXFRAME *frame_hierarchy,
3524 LPD3DXANIMATIONCONTROLLER *anim_controller)
3530 TRACE("(%s, %x, %p, %p, %p, %p, %p)\n", debugstr_a(filename), options,
3531 device, alloc_hier, load_user_data, frame_hierarchy, anim_controller);
3534 return D3DERR_INVALIDCALL;
3536 len = MultiByteToWideChar(CP_ACP, 0, filename, -1, NULL, 0);
3537 filenameW = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
3538 if (!filenameW) return E_OUTOFMEMORY;
3539 MultiByteToWideChar(CP_ACP, 0, filename, -1, filenameW, len);
3541 hr = D3DXLoadMeshHierarchyFromXW(filenameW, options, device,
3542 alloc_hier, load_user_data, frame_hierarchy, anim_controller);
3543 HeapFree(GetProcessHeap(), 0, filenameW);
3548 HRESULT WINAPI D3DXLoadMeshHierarchyFromXW(LPCWSTR filename,
3550 LPDIRECT3DDEVICE9 device,
3551 LPD3DXALLOCATEHIERARCHY alloc_hier,
3552 LPD3DXLOADUSERDATA load_user_data,
3553 LPD3DXFRAME *frame_hierarchy,
3554 LPD3DXANIMATIONCONTROLLER *anim_controller)
3560 TRACE("(%s, %x, %p, %p, %p, %p, %p)\n", debugstr_w(filename), options,
3561 device, alloc_hier, load_user_data, frame_hierarchy, anim_controller);
3564 return D3DERR_INVALIDCALL;
3566 hr = map_view_of_file(filename, &buffer, &size);
3568 return D3DXERR_INVALIDDATA;
3570 hr = D3DXLoadMeshHierarchyFromXInMemory(buffer, size, options, device,
3571 alloc_hier, load_user_data, frame_hierarchy, anim_controller);
3573 UnmapViewOfFile(buffer);
3578 static HRESULT filedata_get_name(IDirectXFileData *filedata, char **name)
3583 hr = IDirectXFileData_GetName(filedata, NULL, &name_len);
3584 if (FAILED(hr)) return hr;
3588 *name = HeapAlloc(GetProcessHeap(), 0, name_len);
3589 if (!*name) return E_OUTOFMEMORY;
3591 hr = IDirectXFileObject_GetName(filedata, *name, &name_len);
3593 HeapFree(GetProcessHeap(), 0, name);
3600 static HRESULT load_mesh_container(IDirectXFileData *filedata,
3602 LPDIRECT3DDEVICE9 device,
3603 LPD3DXALLOCATEHIERARCHY alloc_hier,
3604 D3DXMESHCONTAINER **mesh_container)
3607 ID3DXBuffer *adjacency = NULL;
3608 ID3DXBuffer *materials = NULL;
3609 ID3DXBuffer *effects = NULL;
3610 ID3DXSkinInfo *skin_info = NULL;
3611 D3DXMESHDATA mesh_data;
3612 DWORD num_materials = 0;
3615 mesh_data.Type = D3DXMESHTYPE_MESH;
3616 mesh_data.u.pMesh = NULL;
3618 hr = load_skin_mesh_from_xof(filedata, options, device,
3619 &adjacency, &materials, &effects, &num_materials,
3620 &skin_info, &mesh_data.u.pMesh);
3621 if (FAILED(hr)) return hr;
3623 hr = filedata_get_name(filedata, &name);
3624 if (FAILED(hr)) goto cleanup;
3626 hr = alloc_hier->lpVtbl->CreateMeshContainer(alloc_hier, name, &mesh_data,
3627 materials ? ID3DXBuffer_GetBufferPointer(materials) : NULL,
3628 effects ? ID3DXBuffer_GetBufferPointer(effects) : NULL,
3630 adjacency ? ID3DXBuffer_GetBufferPointer(adjacency) : NULL,
3631 skin_info, mesh_container);
3634 if (materials) ID3DXBuffer_Release(materials);
3635 if (effects) ID3DXBuffer_Release(effects);
3636 if (adjacency) ID3DXBuffer_Release(adjacency);
3637 if (skin_info) IUnknown_Release(skin_info);
3638 if (mesh_data.u.pMesh) IUnknown_Release(mesh_data.u.pMesh);
3639 HeapFree(GetProcessHeap(), 0, name);
3643 static HRESULT parse_transform_matrix(IDirectXFileData *filedata, D3DXMATRIX *transform)
3649 /* template Matrix4x4 {
3650 * array FLOAT matrix[16];
3652 * template FrameTransformMatrix {
3653 * Matrix4x4 frameMatrix;
3657 hr = IDirectXFileData_GetData(filedata, NULL, &data_size, (void**)&data);
3658 if (FAILED(hr)) return hr;
3660 if (data_size != sizeof(D3DXMATRIX)) {
3661 WARN("incorrect data size (%u bytes)\n", data_size);
3665 memcpy(transform, data, sizeof(D3DXMATRIX));
3670 static HRESULT load_frame(IDirectXFileData *filedata,
3672 LPDIRECT3DDEVICE9 device,
3673 LPD3DXALLOCATEHIERARCHY alloc_hier,
3674 D3DXFRAME **frame_out)
3678 IDirectXFileData *child;
3680 D3DXFRAME *frame = NULL;
3681 D3DXMESHCONTAINER **next_container;
3682 D3DXFRAME **next_child;
3684 hr = filedata_get_name(filedata, &name);
3685 if (FAILED(hr)) return hr;
3687 hr = alloc_hier->lpVtbl->CreateFrame(alloc_hier, name, frame_out);
3688 HeapFree(GetProcessHeap(), 0, name);
3689 if (FAILED(hr)) return E_FAIL;
3692 D3DXMatrixIdentity(&frame->TransformationMatrix);
3693 next_child = &frame->pFrameFirstChild;
3694 next_container = &frame->pMeshContainer;
3696 while (SUCCEEDED(hr = get_next_child(filedata, &child, &type)))
3698 if (IsEqualGUID(type, &TID_D3DRMMesh)) {
3699 hr = load_mesh_container(child, options, device, alloc_hier, next_container);
3701 next_container = &(*next_container)->pNextMeshContainer;
3702 } else if (IsEqualGUID(type, &TID_D3DRMFrameTransformMatrix)) {
3703 hr = parse_transform_matrix(child, &frame->TransformationMatrix);
3704 } else if (IsEqualGUID(type, &TID_D3DRMFrame)) {
3705 hr = load_frame(child, options, device, alloc_hier, next_child);
3707 next_child = &(*next_child)->pFrameSibling;
3709 if (FAILED(hr)) break;
3711 if (hr == DXFILEERR_NOMOREOBJECTS)
3717 HRESULT WINAPI D3DXLoadMeshHierarchyFromXInMemory(LPCVOID memory,
3720 LPDIRECT3DDEVICE9 device,
3721 LPD3DXALLOCATEHIERARCHY alloc_hier,
3722 LPD3DXLOADUSERDATA load_user_data,
3723 LPD3DXFRAME *frame_hierarchy,
3724 LPD3DXANIMATIONCONTROLLER *anim_controller)
3727 IDirectXFile *dxfile = NULL;
3728 IDirectXFileEnumObject *enumobj = NULL;
3729 IDirectXFileData *filedata = NULL;
3730 DXFILELOADMEMORY source;
3731 D3DXFRAME *first_frame = NULL;
3732 D3DXFRAME **next_frame = &first_frame;
3734 TRACE("(%p, %u, %x, %p, %p, %p, %p, %p)\n", memory, memory_size, options,
3735 device, alloc_hier, load_user_data, frame_hierarchy, anim_controller);
3737 if (!memory || !memory_size || !device || !frame_hierarchy || !alloc_hier)
3738 return D3DERR_INVALIDCALL;
3739 if (load_user_data || anim_controller) {
3741 FIXME("Loading user data not implemented\n");
3742 if (anim_controller)
3743 FIXME("Animation controller creation not implemented\n");
3747 hr = DirectXFileCreate(&dxfile);
3748 if (FAILED(hr)) goto cleanup;
3750 hr = IDirectXFile_RegisterTemplates(dxfile, D3DRM_XTEMPLATES, D3DRM_XTEMPLATE_BYTES);
3751 if (FAILED(hr)) goto cleanup;
3753 source.lpMemory = (void*)memory;
3754 source.dSize = memory_size;
3755 hr = IDirectXFile_CreateEnumObject(dxfile, &source, DXFILELOAD_FROMMEMORY, &enumobj);
3756 if (FAILED(hr)) goto cleanup;
3758 while (SUCCEEDED(hr = IDirectXFileEnumObject_GetNextDataObject(enumobj, &filedata)))
3760 const GUID *guid = NULL;
3762 hr = IDirectXFileData_GetType(filedata, &guid);
3763 if (SUCCEEDED(hr)) {
3764 if (IsEqualGUID(guid, &TID_D3DRMMesh)) {
3765 hr = alloc_hier->lpVtbl->CreateFrame(alloc_hier, NULL, next_frame);
3771 D3DXMatrixIdentity(&(*next_frame)->TransformationMatrix);
3773 hr = load_mesh_container(filedata, options, device, alloc_hier, &(*next_frame)->pMeshContainer);
3774 if (FAILED(hr)) goto cleanup;
3775 } else if (IsEqualGUID(guid, &TID_D3DRMFrame)) {
3776 hr = load_frame(filedata, options, device, alloc_hier, next_frame);
3777 if (FAILED(hr)) goto cleanup;
3780 next_frame = &(*next_frame)->pFrameSibling;
3783 IDirectXFileData_Release(filedata);
3788 if (hr != DXFILEERR_NOMOREOBJECTS)
3793 } else if (first_frame->pFrameSibling) {
3794 D3DXFRAME *root_frame = NULL;
3795 hr = alloc_hier->lpVtbl->CreateFrame(alloc_hier, NULL, &root_frame);
3800 D3DXMatrixIdentity(&root_frame->TransformationMatrix);
3801 root_frame->pFrameFirstChild = first_frame;
3802 *frame_hierarchy = root_frame;
3805 *frame_hierarchy = first_frame;
3810 if (FAILED(hr) && first_frame) D3DXFrameDestroy(first_frame, alloc_hier);
3811 if (filedata) IDirectXFileData_Release(filedata);
3812 if (enumobj) IDirectXFileEnumObject_Release(enumobj);
3813 if (dxfile) IDirectXFile_Release(dxfile);
3817 HRESULT WINAPI D3DXCleanMesh(D3DXCLEANTYPE clean_type, LPD3DXMESH mesh_in, const DWORD *adjacency_in,
3818 LPD3DXMESH *mesh_out, DWORD *adjacency_out, LPD3DXBUFFER *errors_and_warnings)
3820 FIXME("(%u, %p, %p, %p, %p, %p)\n", clean_type, mesh_in, adjacency_in, mesh_out, adjacency_out, errors_and_warnings);
3825 HRESULT WINAPI D3DXFrameDestroy(LPD3DXFRAME frame, LPD3DXALLOCATEHIERARCHY alloc_hier)
3830 TRACE("(%p, %p)\n", frame, alloc_hier);
3832 if (!frame || !alloc_hier)
3833 return D3DERR_INVALIDCALL;
3836 D3DXMESHCONTAINER *container;
3837 D3DXFRAME *current_frame;
3839 if (frame->pFrameSibling) {
3840 current_frame = frame->pFrameSibling;
3841 frame->pFrameSibling = current_frame->pFrameSibling;
3842 current_frame->pFrameSibling = NULL;
3844 current_frame = frame;
3848 if (current_frame->pFrameFirstChild) {
3849 hr = D3DXFrameDestroy(current_frame->pFrameFirstChild, alloc_hier);
3850 if (FAILED(hr)) return hr;
3851 current_frame->pFrameFirstChild = NULL;
3854 container = current_frame->pMeshContainer;
3856 D3DXMESHCONTAINER *next_container = container->pNextMeshContainer;
3857 hr = alloc_hier->lpVtbl->DestroyMeshContainer(alloc_hier, container);
3858 if (FAILED(hr)) return hr;
3859 container = next_container;
3861 hr = alloc_hier->lpVtbl->DestroyFrame(alloc_hier, current_frame);
3862 if (FAILED(hr)) return hr;
3867 HRESULT WINAPI D3DXLoadMeshFromXA(LPCSTR filename,
3869 LPDIRECT3DDEVICE9 device,
3870 LPD3DXBUFFER *adjacency,
3871 LPD3DXBUFFER *materials,
3872 LPD3DXBUFFER *effect_instances,
3873 DWORD *num_materials,
3880 TRACE("(%s, %x, %p, %p, %p, %p, %p, %p)\n", debugstr_a(filename), options,
3881 device, adjacency, materials, effect_instances, num_materials, mesh);
3884 return D3DERR_INVALIDCALL;
3886 len = MultiByteToWideChar(CP_ACP, 0, filename, -1, NULL, 0);
3887 filenameW = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
3888 if (!filenameW) return E_OUTOFMEMORY;
3889 MultiByteToWideChar(CP_ACP, 0, filename, -1, filenameW, len);
3891 hr = D3DXLoadMeshFromXW(filenameW, options, device, adjacency, materials,
3892 effect_instances, num_materials, mesh);
3893 HeapFree(GetProcessHeap(), 0, filenameW);
3898 HRESULT WINAPI D3DXLoadMeshFromXW(LPCWSTR filename,
3900 LPDIRECT3DDEVICE9 device,
3901 LPD3DXBUFFER *adjacency,
3902 LPD3DXBUFFER *materials,
3903 LPD3DXBUFFER *effect_instances,
3904 DWORD *num_materials,
3911 TRACE("(%s, %x, %p, %p, %p, %p, %p, %p)\n", debugstr_w(filename), options,
3912 device, adjacency, materials, effect_instances, num_materials, mesh);
3915 return D3DERR_INVALIDCALL;
3917 hr = map_view_of_file(filename, &buffer, &size);
3919 return D3DXERR_INVALIDDATA;
3921 hr = D3DXLoadMeshFromXInMemory(buffer, size, options, device, adjacency,
3922 materials, effect_instances, num_materials, mesh);
3924 UnmapViewOfFile(buffer);
3929 HRESULT WINAPI D3DXLoadMeshFromXResource(HMODULE module,
3933 LPDIRECT3DDEVICE9 device,
3934 LPD3DXBUFFER *adjacency,
3935 LPD3DXBUFFER *materials,
3936 LPD3DXBUFFER *effect_instances,
3937 DWORD *num_materials,
3945 TRACE("(%p, %s, %s, %x, %p, %p, %p, %p, %p, %p)\n",
3946 module, debugstr_a(name), debugstr_a(type), options, device,
3947 adjacency, materials, effect_instances, num_materials, mesh);
3949 resinfo = FindResourceA(module, name, type);
3950 if (!resinfo) return D3DXERR_INVALIDDATA;
3952 hr = load_resource_into_memory(module, resinfo, &buffer, &size);
3953 if (FAILED(hr)) return D3DXERR_INVALIDDATA;
3955 return D3DXLoadMeshFromXInMemory(buffer, size, options, device, adjacency,
3956 materials, effect_instances, num_materials, mesh);
3959 struct mesh_container
3963 ID3DXBuffer *adjacency;
3964 ID3DXBuffer *materials;
3965 ID3DXBuffer *effects;
3966 DWORD num_materials;
3967 D3DXMATRIX transform;
3970 static HRESULT parse_frame(IDirectXFileData *filedata,
3972 LPDIRECT3DDEVICE9 device,
3973 const D3DXMATRIX *parent_transform,
3974 struct list *container_list,
3975 DWORD provide_flags)
3978 D3DXMATRIX transform = *parent_transform;
3979 IDirectXFileData *child;
3982 while (SUCCEEDED(hr = get_next_child(filedata, &child, &type)))
3984 if (IsEqualGUID(type, &TID_D3DRMMesh)) {
3985 struct mesh_container *container = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(*container));
3990 list_add_tail(container_list, &container->entry);
3991 container->transform = transform;
3993 hr = load_skin_mesh_from_xof(child, options, device,
3994 (provide_flags & PROVIDE_ADJACENCY) ? &container->adjacency : NULL,
3995 (provide_flags & PROVIDE_MATERIALS) ? &container->materials : NULL,
3996 NULL, &container->num_materials, NULL, &container->mesh);
3997 } else if (IsEqualGUID(type, &TID_D3DRMFrameTransformMatrix)) {
3998 D3DXMATRIX new_transform;
3999 hr = parse_transform_matrix(child, &new_transform);
4000 D3DXMatrixMultiply(&transform, &transform, &new_transform);
4001 } else if (IsEqualGUID(type, &TID_D3DRMFrame)) {
4002 hr = parse_frame(child, options, device, &transform, container_list, provide_flags);
4004 if (FAILED(hr)) break;
4006 return hr == DXFILEERR_NOMOREOBJECTS ? D3D_OK : hr;
4009 HRESULT WINAPI D3DXLoadMeshFromXInMemory(LPCVOID memory,
4012 LPDIRECT3DDEVICE9 device,
4013 LPD3DXBUFFER *adjacency_out,
4014 LPD3DXBUFFER *materials_out,
4015 LPD3DXBUFFER *effects_out,
4016 DWORD *num_materials_out,
4017 LPD3DXMESH *mesh_out)
4020 IDirectXFile *dxfile = NULL;
4021 IDirectXFileEnumObject *enumobj = NULL;
4022 IDirectXFileData *filedata = NULL;
4023 DXFILELOADMEMORY source;
4024 ID3DXBuffer *materials = NULL;
4025 ID3DXBuffer *effects = NULL;
4026 ID3DXBuffer *adjacency = NULL;
4027 struct list container_list = LIST_INIT(container_list);
4028 struct mesh_container *container_ptr, *next_container_ptr;
4029 DWORD num_materials;
4030 DWORD num_faces, num_vertices;
4031 D3DXMATRIX identity;
4033 DWORD provide_flags = 0;
4035 ID3DXMesh *concat_mesh = NULL;
4036 D3DVERTEXELEMENT9 concat_decl[MAX_FVF_DECL_SIZE];
4037 BYTE *concat_vertices = NULL;
4038 void *concat_indices = NULL;
4040 DWORD concat_vertex_size;
4042 TRACE("(%p, %u, %x, %p, %p, %p, %p, %p, %p)\n", memory, memory_size, options,
4043 device, adjacency_out, materials_out, effects_out, num_materials_out, mesh_out);
4045 if (!memory || !memory_size || !device || !mesh_out)
4046 return D3DERR_INVALIDCALL;
4048 hr = DirectXFileCreate(&dxfile);
4049 if (FAILED(hr)) goto cleanup;
4051 hr = IDirectXFile_RegisterTemplates(dxfile, D3DRM_XTEMPLATES, D3DRM_XTEMPLATE_BYTES);
4052 if (FAILED(hr)) goto cleanup;
4054 source.lpMemory = (void*)memory;
4055 source.dSize = memory_size;
4056 hr = IDirectXFile_CreateEnumObject(dxfile, &source, DXFILELOAD_FROMMEMORY, &enumobj);
4057 if (FAILED(hr)) goto cleanup;
4059 D3DXMatrixIdentity(&identity);
4060 if (adjacency_out) provide_flags |= PROVIDE_ADJACENCY;
4061 if (materials_out || effects_out) provide_flags |= PROVIDE_MATERIALS;
4063 while (SUCCEEDED(hr = IDirectXFileEnumObject_GetNextDataObject(enumobj, &filedata)))
4065 const GUID *guid = NULL;
4067 hr = IDirectXFileData_GetType(filedata, &guid);
4068 if (SUCCEEDED(hr)) {
4069 if (IsEqualGUID(guid, &TID_D3DRMMesh)) {
4070 container_ptr = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(*container_ptr));
4071 if (!container_ptr) {
4075 list_add_tail(&container_list, &container_ptr->entry);
4076 D3DXMatrixIdentity(&container_ptr->transform);
4078 hr = load_skin_mesh_from_xof(filedata, options, device,
4079 (provide_flags & PROVIDE_ADJACENCY) ? &container_ptr->adjacency : NULL,
4080 (provide_flags & PROVIDE_MATERIALS) ? &container_ptr->materials : NULL,
4081 NULL, &container_ptr->num_materials, NULL, &container_ptr->mesh);
4082 } else if (IsEqualGUID(guid, &TID_D3DRMFrame)) {
4083 hr = parse_frame(filedata, options, device, &identity, &container_list, provide_flags);
4085 if (FAILED(hr)) goto cleanup;
4087 IDirectXFileData_Release(filedata);
4092 if (hr != DXFILEERR_NOMOREOBJECTS)
4095 IDirectXFileEnumObject_Release(enumobj);
4097 IDirectXFile_Release(dxfile);
4100 if (list_empty(&container_list)) {
4109 LIST_FOR_EACH_ENTRY(container_ptr, &container_list, struct mesh_container, entry)
4111 ID3DXMesh *mesh = container_ptr->mesh;
4112 fvf |= mesh->lpVtbl->GetFVF(mesh);
4113 num_faces += mesh->lpVtbl->GetNumFaces(mesh);
4114 num_vertices += mesh->lpVtbl->GetNumVertices(mesh);
4115 num_materials += container_ptr->num_materials;
4118 hr = D3DXCreateMeshFVF(num_faces, num_vertices, options, fvf, device, &concat_mesh);
4119 if (FAILED(hr)) goto cleanup;
4121 hr = concat_mesh->lpVtbl->GetDeclaration(concat_mesh, concat_decl);
4122 if (FAILED(hr)) goto cleanup;
4124 concat_vertex_size = D3DXGetDeclVertexSize(concat_decl, 0);
4126 hr = concat_mesh->lpVtbl->LockVertexBuffer(concat_mesh, 0, (void**)&concat_vertices);
4127 if (FAILED(hr)) goto cleanup;
4129 LIST_FOR_EACH_ENTRY(container_ptr, &container_list, struct mesh_container, entry)
4131 D3DVERTEXELEMENT9 mesh_decl[MAX_FVF_DECL_SIZE];
4132 ID3DXMesh *mesh = container_ptr->mesh;
4133 DWORD num_mesh_vertices = mesh->lpVtbl->GetNumVertices(mesh);
4134 DWORD mesh_vertex_size;
4135 const BYTE *mesh_vertices;
4137 hr = mesh->lpVtbl->GetDeclaration(mesh, mesh_decl);
4138 if (FAILED(hr)) goto cleanup;
4140 mesh_vertex_size = D3DXGetDeclVertexSize(mesh_decl, 0);
4142 hr = mesh->lpVtbl->LockVertexBuffer(mesh, D3DLOCK_READONLY, (void**)&mesh_vertices);
4143 if (FAILED(hr)) goto cleanup;
4145 for (i = 0; i < num_mesh_vertices; i++) {
4149 D3DXVec3TransformCoord((D3DXVECTOR3*)concat_vertices,
4150 (D3DXVECTOR3*)mesh_vertices,
4151 &container_ptr->transform);
4152 for (j = 1; concat_decl[j].Stream != 0xff; j++)
4154 if (concat_decl[j].Usage == mesh_decl[k].Usage &&
4155 concat_decl[j].UsageIndex == mesh_decl[k].UsageIndex)
4157 if (concat_decl[j].Usage == D3DDECLUSAGE_NORMAL) {
4158 D3DXVec3TransformCoord((D3DXVECTOR3*)(concat_vertices + concat_decl[j].Offset),
4159 (D3DXVECTOR3*)(mesh_vertices + mesh_decl[k].Offset),
4160 &container_ptr->transform);
4162 memcpy(concat_vertices + concat_decl[j].Offset,
4163 mesh_vertices + mesh_decl[k].Offset,
4164 d3dx_decltype_size[mesh_decl[k].Type]);
4169 mesh_vertices += mesh_vertex_size;
4170 concat_vertices += concat_vertex_size;
4173 mesh->lpVtbl->UnlockVertexBuffer(mesh);
4176 concat_mesh->lpVtbl->UnlockVertexBuffer(concat_mesh);
4177 concat_vertices = NULL;
4179 hr = concat_mesh->lpVtbl->LockIndexBuffer(concat_mesh, 0, &concat_indices);
4180 if (FAILED(hr)) goto cleanup;
4183 LIST_FOR_EACH_ENTRY(container_ptr, &container_list, struct mesh_container, entry)
4185 ID3DXMesh *mesh = container_ptr->mesh;
4186 const void *mesh_indices;
4187 DWORD num_mesh_faces = mesh->lpVtbl->GetNumFaces(mesh);
4190 hr = mesh->lpVtbl->LockIndexBuffer(mesh, D3DLOCK_READONLY, (void**)&mesh_indices);
4191 if (FAILED(hr)) goto cleanup;
4193 if (options & D3DXMESH_32BIT) {
4194 DWORD *dest = concat_indices;
4195 const DWORD *src = mesh_indices;
4196 for (i = 0; i < num_mesh_faces * 3; i++)
4197 *dest++ = index_offset + *src++;
4198 concat_indices = dest;
4200 WORD *dest = concat_indices;
4201 const WORD *src = mesh_indices;
4202 for (i = 0; i < num_mesh_faces * 3; i++)
4203 *dest++ = index_offset + *src++;
4204 concat_indices = dest;
4206 mesh->lpVtbl->UnlockIndexBuffer(mesh);
4208 index_offset += num_mesh_faces * 3;
4211 concat_mesh->lpVtbl->UnlockIndexBuffer(concat_mesh);
4212 concat_indices = NULL;
4214 if (num_materials) {
4215 DWORD *concat_attrib_buffer = NULL;
4218 hr = concat_mesh->lpVtbl->LockAttributeBuffer(concat_mesh, 0, &concat_attrib_buffer);
4219 if (FAILED(hr)) goto cleanup;
4221 LIST_FOR_EACH_ENTRY(container_ptr, &container_list, struct mesh_container, entry)
4223 ID3DXMesh *mesh = container_ptr->mesh;
4224 const DWORD *mesh_attrib_buffer = NULL;
4225 DWORD count = mesh->lpVtbl->GetNumFaces(mesh);
4227 hr = mesh->lpVtbl->LockAttributeBuffer(mesh, D3DLOCK_READONLY, (DWORD**)&mesh_attrib_buffer);
4229 concat_mesh->lpVtbl->UnlockAttributeBuffer(concat_mesh);
4234 *concat_attrib_buffer++ = offset + *mesh_attrib_buffer++;
4236 mesh->lpVtbl->UnlockAttributeBuffer(mesh);
4237 offset += container_ptr->num_materials;
4239 concat_mesh->lpVtbl->UnlockAttributeBuffer(concat_mesh);
4242 if (materials_out || effects_out) {
4243 D3DXMATERIAL *out_ptr;
4244 if (!num_materials) {
4245 /* create default material */
4246 hr = D3DXCreateBuffer(sizeof(D3DXMATERIAL), &materials);
4247 if (FAILED(hr)) goto cleanup;
4249 out_ptr = ID3DXBuffer_GetBufferPointer(materials);
4250 out_ptr->MatD3D.Diffuse.r = 0.5f;
4251 out_ptr->MatD3D.Diffuse.g = 0.5f;
4252 out_ptr->MatD3D.Diffuse.b = 0.5f;
4253 out_ptr->MatD3D.Specular.r = 0.5f;
4254 out_ptr->MatD3D.Specular.g = 0.5f;
4255 out_ptr->MatD3D.Specular.b = 0.5f;
4256 /* D3DXCreateBuffer initializes the rest to zero */
4258 DWORD buffer_size = num_materials * sizeof(D3DXMATERIAL);
4259 char *strings_out_ptr;
4261 LIST_FOR_EACH_ENTRY(container_ptr, &container_list, struct mesh_container, entry)
4263 if (container_ptr->materials) {
4264 const D3DXMATERIAL *in_ptr = ID3DXBuffer_GetBufferPointer(container_ptr->materials);
4265 for (i = 0; i < container_ptr->num_materials; i++)
4267 if (in_ptr->pTextureFilename)
4268 buffer_size += strlen(in_ptr->pTextureFilename) + 1;
4274 hr = D3DXCreateBuffer(buffer_size, &materials);
4275 if (FAILED(hr)) goto cleanup;
4276 out_ptr = ID3DXBuffer_GetBufferPointer(materials);
4277 strings_out_ptr = (char*)(out_ptr + num_materials);
4279 LIST_FOR_EACH_ENTRY(container_ptr, &container_list, struct mesh_container, entry)
4281 if (container_ptr->materials) {
4282 const D3DXMATERIAL *in_ptr = ID3DXBuffer_GetBufferPointer(container_ptr->materials);
4283 for (i = 0; i < container_ptr->num_materials; i++)
4285 out_ptr->MatD3D = in_ptr->MatD3D;
4286 if (in_ptr->pTextureFilename) {
4287 out_ptr->pTextureFilename = strings_out_ptr;
4288 strcpy(out_ptr->pTextureFilename, in_ptr->pTextureFilename);
4289 strings_out_ptr += strlen(in_ptr->pTextureFilename) + 1;
4302 generate_effects(materials, num_materials, &effects);
4303 if (!materials_out) {
4304 ID3DXBuffer_Release(materials);
4309 if (adjacency_out) {
4310 if (!list_next(&container_list, list_head(&container_list))) {
4311 container_ptr = LIST_ENTRY(list_head(&container_list), struct mesh_container, entry);
4312 adjacency = container_ptr->adjacency;
4313 container_ptr->adjacency = NULL;
4318 hr = D3DXCreateBuffer(num_faces * 3 * sizeof(DWORD), &adjacency);
4319 if (FAILED(hr)) goto cleanup;
4321 out_ptr = ID3DXBuffer_GetBufferPointer(adjacency);
4322 LIST_FOR_EACH_ENTRY(container_ptr, &container_list, struct mesh_container, entry)
4324 DWORD *in_ptr = ID3DXBuffer_GetBufferPointer(container_ptr->adjacency);
4325 int count = 3 * container_ptr->mesh->lpVtbl->GetNumFaces(container_ptr->mesh);
4327 for (i = 0; i < count; i++)
4328 *out_ptr++ = offset + *in_ptr++;
4335 *mesh_out = concat_mesh;
4336 if (adjacency_out) *adjacency_out = adjacency;
4337 if (materials_out) *materials_out = materials;
4338 if (effects_out) *effects_out = effects;
4339 if (num_materials_out) *num_materials_out = num_materials;
4343 if (concat_indices) concat_mesh->lpVtbl->UnlockIndexBuffer(concat_mesh);
4344 if (concat_vertices) concat_mesh->lpVtbl->UnlockVertexBuffer(concat_mesh);
4345 if (filedata) IDirectXFileData_Release(filedata);
4346 if (enumobj) IDirectXFileEnumObject_Release(enumobj);
4347 if (dxfile) IDirectXFile_Release(dxfile);
4349 if (concat_mesh) IUnknown_Release(concat_mesh);
4350 if (materials) ID3DXBuffer_Release(materials);
4351 if (effects) ID3DXBuffer_Release(effects);
4352 if (adjacency) ID3DXBuffer_Release(adjacency);
4354 LIST_FOR_EACH_ENTRY_SAFE(container_ptr, next_container_ptr, &container_list, struct mesh_container, entry)
4356 if (container_ptr->mesh) IUnknown_Release(container_ptr->mesh);
4357 if (container_ptr->adjacency) ID3DXBuffer_Release(container_ptr->adjacency);
4358 if (container_ptr->materials) ID3DXBuffer_Release(container_ptr->materials);
4359 if (container_ptr->effects) ID3DXBuffer_Release(container_ptr->effects);
4360 HeapFree(GetProcessHeap(), 0, container_ptr);
4365 HRESULT WINAPI D3DXCreateBox(LPDIRECT3DDEVICE9 device, FLOAT width, FLOAT height,
4366 FLOAT depth, LPD3DXMESH* mesh, LPD3DXBUFFER* adjacency)
4368 FIXME("(%p, %f, %f, %f, %p, %p): stub\n", device, width, height, depth, mesh, adjacency);
4375 D3DXVECTOR3 position;
4379 typedef WORD face[3];
4387 static void free_sincos_table(struct sincos_table *sincos_table)
4389 HeapFree(GetProcessHeap(), 0, sincos_table->cos);
4390 HeapFree(GetProcessHeap(), 0, sincos_table->sin);
4393 /* pre compute sine and cosine tables; caller must free */
4394 static BOOL compute_sincos_table(struct sincos_table *sincos_table, float angle_start, float angle_step, int n)
4399 sincos_table->sin = HeapAlloc(GetProcessHeap(), 0, n * sizeof(*sincos_table->sin));
4400 if (!sincos_table->sin)
4404 sincos_table->cos = HeapAlloc(GetProcessHeap(), 0, n * sizeof(*sincos_table->cos));
4405 if (!sincos_table->cos)
4407 HeapFree(GetProcessHeap(), 0, sincos_table->sin);
4411 angle = angle_start;
4412 for (i = 0; i < n; i++)
4414 sincos_table->sin[i] = sin(angle);
4415 sincos_table->cos[i] = cos(angle);
4416 angle += angle_step;
4422 static WORD vertex_index(UINT slices, int slice, int stack)
4424 return stack*slices+slice+1;
4427 HRESULT WINAPI D3DXCreateSphere(LPDIRECT3DDEVICE9 device, FLOAT radius, UINT slices,
4428 UINT stacks, LPD3DXMESH* mesh, LPD3DXBUFFER* adjacency)
4430 DWORD number_of_vertices, number_of_faces;
4433 struct vertex *vertices;
4435 float phi_step, phi_start;
4436 struct sincos_table phi;
4437 float theta_step, theta, sin_theta, cos_theta;
4441 TRACE("(%p, %f, %u, %u, %p, %p)\n", device, radius, slices, stacks, mesh, adjacency);
4443 if (!device || radius < 0.0f || slices < 2 || stacks < 2 || !mesh)
4445 return D3DERR_INVALIDCALL;
4450 FIXME("Case of adjacency != NULL not implemented.\n");
4454 number_of_vertices = 2 + slices * (stacks-1);
4455 number_of_faces = 2 * slices + (stacks - 2) * (2 * slices);
4457 hr = D3DXCreateMeshFVF(number_of_faces, number_of_vertices, D3DXMESH_MANAGED,
4458 D3DFVF_XYZ | D3DFVF_NORMAL, device, &sphere);
4464 hr = sphere->lpVtbl->LockVertexBuffer(sphere, 0, (LPVOID *)&vertices);
4467 sphere->lpVtbl->Release(sphere);
4471 hr = sphere->lpVtbl->LockIndexBuffer(sphere, 0, (LPVOID *)&faces);
4474 sphere->lpVtbl->UnlockVertexBuffer(sphere);
4475 sphere->lpVtbl->Release(sphere);
4479 /* phi = angle on xz plane wrt z axis */
4480 phi_step = -2 * M_PI / slices;
4481 phi_start = M_PI / 2;
4483 if (!compute_sincos_table(&phi, phi_start, phi_step, slices))
4485 sphere->lpVtbl->UnlockIndexBuffer(sphere);
4486 sphere->lpVtbl->UnlockVertexBuffer(sphere);
4487 sphere->lpVtbl->Release(sphere);
4488 return E_OUTOFMEMORY;
4491 /* theta = angle on xy plane wrt x axis */
4492 theta_step = M_PI / stacks;
4498 vertices[vertex].normal.x = 0.0f;
4499 vertices[vertex].normal.y = 0.0f;
4500 vertices[vertex].normal.z = 1.0f;
4501 vertices[vertex].position.x = 0.0f;
4502 vertices[vertex].position.y = 0.0f;
4503 vertices[vertex].position.z = radius;
4506 for (stack = 0; stack < stacks - 1; stack++)
4508 sin_theta = sin(theta);
4509 cos_theta = cos(theta);
4511 for (slice = 0; slice < slices; slice++)
4513 vertices[vertex].normal.x = sin_theta * phi.cos[slice];
4514 vertices[vertex].normal.y = sin_theta * phi.sin[slice];
4515 vertices[vertex].normal.z = cos_theta;
4516 vertices[vertex].position.x = radius * sin_theta * phi.cos[slice];
4517 vertices[vertex].position.y = radius * sin_theta * phi.sin[slice];
4518 vertices[vertex].position.z = radius * cos_theta;
4525 /* top stack is triangle fan */
4527 faces[face][1] = slice + 1;
4528 faces[face][2] = slice;
4533 /* stacks in between top and bottom are quad strips */
4534 faces[face][0] = vertex_index(slices, slice-1, stack-1);
4535 faces[face][1] = vertex_index(slices, slice, stack-1);
4536 faces[face][2] = vertex_index(slices, slice-1, stack);
4539 faces[face][0] = vertex_index(slices, slice, stack-1);
4540 faces[face][1] = vertex_index(slices, slice, stack);
4541 faces[face][2] = vertex_index(slices, slice-1, stack);
4547 theta += theta_step;
4553 faces[face][2] = slice;
4558 faces[face][0] = vertex_index(slices, slice-1, stack-1);
4559 faces[face][1] = vertex_index(slices, 0, stack-1);
4560 faces[face][2] = vertex_index(slices, slice-1, stack);
4563 faces[face][0] = vertex_index(slices, 0, stack-1);
4564 faces[face][1] = vertex_index(slices, 0, stack);
4565 faces[face][2] = vertex_index(slices, slice-1, stack);
4570 vertices[vertex].position.x = 0.0f;
4571 vertices[vertex].position.y = 0.0f;
4572 vertices[vertex].position.z = -radius;
4573 vertices[vertex].normal.x = 0.0f;
4574 vertices[vertex].normal.y = 0.0f;
4575 vertices[vertex].normal.z = -1.0f;
4577 /* bottom stack is triangle fan */
4578 for (slice = 1; slice < slices; slice++)
4580 faces[face][0] = vertex_index(slices, slice-1, stack-1);
4581 faces[face][1] = vertex_index(slices, slice, stack-1);
4582 faces[face][2] = vertex;
4586 faces[face][0] = vertex_index(slices, slice-1, stack-1);
4587 faces[face][1] = vertex_index(slices, 0, stack-1);
4588 faces[face][2] = vertex;
4590 free_sincos_table(&phi);
4591 sphere->lpVtbl->UnlockIndexBuffer(sphere);
4592 sphere->lpVtbl->UnlockVertexBuffer(sphere);
4598 HRESULT WINAPI D3DXCreateCylinder(LPDIRECT3DDEVICE9 device, FLOAT radius1, FLOAT radius2, FLOAT length, UINT slices,
4599 UINT stacks, LPD3DXMESH* mesh, LPD3DXBUFFER* adjacency)
4601 DWORD number_of_vertices, number_of_faces;
4603 ID3DXMesh *cylinder;
4604 struct vertex *vertices;
4606 float theta_step, theta_start;
4607 struct sincos_table theta;
4608 float delta_radius, radius, radius_step;
4609 float z, z_step, z_normal;
4613 TRACE("(%p, %f, %f, %f, %u, %u, %p, %p)\n", device, radius1, radius2, length, slices, stacks, mesh, adjacency);
4615 if (device == NULL || radius1 < 0.0f || radius2 < 0.0f || length < 0.0f || slices < 2 || stacks < 1 || mesh == NULL)
4617 return D3DERR_INVALIDCALL;
4622 FIXME("Case of adjacency != NULL not implemented.\n");
4626 number_of_vertices = 2 + (slices * (3 + stacks));
4627 number_of_faces = 2 * slices + stacks * (2 * slices);
4629 hr = D3DXCreateMeshFVF(number_of_faces, number_of_vertices, D3DXMESH_MANAGED,
4630 D3DFVF_XYZ | D3DFVF_NORMAL, device, &cylinder);
4636 hr = cylinder->lpVtbl->LockVertexBuffer(cylinder, 0, (LPVOID *)&vertices);
4639 cylinder->lpVtbl->Release(cylinder);
4643 hr = cylinder->lpVtbl->LockIndexBuffer(cylinder, 0, (LPVOID *)&faces);
4646 cylinder->lpVtbl->UnlockVertexBuffer(cylinder);
4647 cylinder->lpVtbl->Release(cylinder);
4651 /* theta = angle on xy plane wrt x axis */
4652 theta_step = -2 * M_PI / slices;
4653 theta_start = M_PI / 2;
4655 if (!compute_sincos_table(&theta, theta_start, theta_step, slices))
4657 cylinder->lpVtbl->UnlockIndexBuffer(cylinder);
4658 cylinder->lpVtbl->UnlockVertexBuffer(cylinder);
4659 cylinder->lpVtbl->Release(cylinder);
4660 return E_OUTOFMEMORY;
4666 delta_radius = radius1 - radius2;
4668 radius_step = delta_radius / stacks;
4671 z_step = length / stacks;
4672 z_normal = delta_radius / length;
4673 if (isnan(z_normal))
4678 vertices[vertex].normal.x = 0.0f;
4679 vertices[vertex].normal.y = 0.0f;
4680 vertices[vertex].normal.z = -1.0f;
4681 vertices[vertex].position.x = 0.0f;
4682 vertices[vertex].position.y = 0.0f;
4683 vertices[vertex++].position.z = z;
4685 for (slice = 0; slice < slices; slice++, vertex++)
4687 vertices[vertex].normal.x = 0.0f;
4688 vertices[vertex].normal.y = 0.0f;
4689 vertices[vertex].normal.z = -1.0f;
4690 vertices[vertex].position.x = radius * theta.cos[slice];
4691 vertices[vertex].position.y = radius * theta.sin[slice];
4692 vertices[vertex].position.z = z;
4697 faces[face][1] = slice;
4698 faces[face++][2] = slice + 1;
4703 faces[face][1] = slice;
4704 faces[face++][2] = 1;
4706 for (stack = 1; stack <= stacks+1; stack++)
4708 for (slice = 0; slice < slices; slice++, vertex++)
4710 vertices[vertex].normal.x = theta.cos[slice];
4711 vertices[vertex].normal.y = theta.sin[slice];
4712 vertices[vertex].normal.z = z_normal;
4713 D3DXVec3Normalize(&vertices[vertex].normal, &vertices[vertex].normal);
4714 vertices[vertex].position.x = radius * theta.cos[slice];
4715 vertices[vertex].position.y = radius * theta.sin[slice];
4716 vertices[vertex].position.z = z;
4718 if (stack > 1 && slice > 0)
4720 faces[face][0] = vertex_index(slices, slice-1, stack-1);
4721 faces[face][1] = vertex_index(slices, slice-1, stack);
4722 faces[face++][2] = vertex_index(slices, slice, stack-1);
4724 faces[face][0] = vertex_index(slices, slice, stack-1);
4725 faces[face][1] = vertex_index(slices, slice-1, stack);
4726 faces[face++][2] = vertex_index(slices, slice, stack);
4732 faces[face][0] = vertex_index(slices, slice-1, stack-1);
4733 faces[face][1] = vertex_index(slices, slice-1, stack);
4734 faces[face++][2] = vertex_index(slices, 0, stack-1);
4736 faces[face][0] = vertex_index(slices, 0, stack-1);
4737 faces[face][1] = vertex_index(slices, slice-1, stack);
4738 faces[face++][2] = vertex_index(slices, 0, stack);
4741 if (stack < stacks + 1)
4744 radius -= radius_step;
4748 for (slice = 0; slice < slices; slice++, vertex++)
4750 vertices[vertex].normal.x = 0.0f;
4751 vertices[vertex].normal.y = 0.0f;
4752 vertices[vertex].normal.z = 1.0f;
4753 vertices[vertex].position.x = radius * theta.cos[slice];
4754 vertices[vertex].position.y = radius * theta.sin[slice];
4755 vertices[vertex].position.z = z;
4759 faces[face][0] = vertex_index(slices, slice-1, stack);
4760 faces[face][1] = number_of_vertices - 1;
4761 faces[face++][2] = vertex_index(slices, slice, stack);
4765 vertices[vertex].position.x = 0.0f;
4766 vertices[vertex].position.y = 0.0f;
4767 vertices[vertex].position.z = z;
4768 vertices[vertex].normal.x = 0.0f;
4769 vertices[vertex].normal.y = 0.0f;
4770 vertices[vertex].normal.z = 1.0f;
4772 faces[face][0] = vertex_index(slices, slice-1, stack);
4773 faces[face][1] = number_of_vertices - 1;
4774 faces[face][2] = vertex_index(slices, 0, stack);
4776 free_sincos_table(&theta);
4777 cylinder->lpVtbl->UnlockIndexBuffer(cylinder);
4778 cylinder->lpVtbl->UnlockVertexBuffer(cylinder);
4784 HRESULT WINAPI D3DXCreateTeapot(LPDIRECT3DDEVICE9 device, LPD3DXMESH *mesh, LPD3DXBUFFER* adjacency)
4786 FIXME("(%p, %p, %p): stub\n", device, mesh, adjacency);
4791 HRESULT WINAPI D3DXCreateTextA(LPDIRECT3DDEVICE9 device,
4792 HDC hdc, LPCSTR text,
4793 FLOAT deviation, FLOAT extrusion,
4794 LPD3DXMESH *mesh, LPD3DXBUFFER *adjacency,
4795 LPGLYPHMETRICSFLOAT glyphmetrics)
4801 TRACE("(%p, %p, %s, %f, %f, %p, %p, %p)\n", device, hdc,
4802 debugstr_a(text), deviation, extrusion, mesh, adjacency, glyphmetrics);
4805 return D3DERR_INVALIDCALL;
4807 len = MultiByteToWideChar(CP_ACP, 0, text, -1, NULL, 0);
4808 textW = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
4809 MultiByteToWideChar(CP_ACP, 0, text, -1, textW, len);
4811 hr = D3DXCreateTextW(device, hdc, textW, deviation, extrusion,
4812 mesh, adjacency, glyphmetrics);
4813 HeapFree(GetProcessHeap(), 0, textW);
4819 POINTTYPE_CURVE = 0,
4821 POINTTYPE_CURVE_START,
4822 POINTTYPE_CURVE_END,
4823 POINTTYPE_CURVE_MIDDLE,
4829 enum pointtype corner;
4832 struct dynamic_array
4834 int count, capacity;
4838 /* is a dynamic_array */
4841 int count, capacity;
4842 struct point2d *items;
4845 /* is a dynamic_array */
4846 struct outline_array
4848 int count, capacity;
4849 struct outline *items;
4858 struct point2d_index
4860 struct outline *outline;
4864 struct point2d_index_array
4867 struct point2d_index *items;
4872 struct outline_array outlines;
4873 struct face_array faces;
4874 struct point2d_index_array ordered_vertices;
4878 /* is an dynamic_array */
4881 int count, capacity;
4885 /* complex polygons are split into monotone polygons, which have
4886 * at most 2 intersections with the vertical sweep line */
4887 struct triangulation
4889 struct word_array vertex_stack;
4890 BOOL last_on_top, merging;
4893 /* is an dynamic_array */
4894 struct triangulation_array
4896 int count, capacity;
4897 struct triangulation *items;
4899 struct glyphinfo *glyph;
4902 static BOOL reserve(struct dynamic_array *array, int count, int itemsize)
4904 if (count > array->capacity) {
4907 if (array->items && array->capacity) {
4908 new_capacity = max(array->capacity * 2, count);
4909 new_buffer = HeapReAlloc(GetProcessHeap(), 0, array->items, new_capacity * itemsize);
4911 new_capacity = max(16, count);
4912 new_buffer = HeapAlloc(GetProcessHeap(), 0, new_capacity * itemsize);
4916 array->items = new_buffer;
4917 array->capacity = new_capacity;
4922 static struct point2d *add_points(struct outline *array, int num)
4924 struct point2d *item;
4926 if (!reserve((struct dynamic_array *)array, array->count + num, sizeof(array->items[0])))
4929 item = &array->items[array->count];
4930 array->count += num;
4934 static struct outline *add_outline(struct outline_array *array)
4936 struct outline *item;
4938 if (!reserve((struct dynamic_array *)array, array->count + 1, sizeof(array->items[0])))
4941 item = &array->items[array->count++];
4942 ZeroMemory(item, sizeof(*item));
4946 static inline face *add_face(struct face_array *array)
4948 return &array->items[array->count++];
4951 static struct triangulation *add_triangulation(struct triangulation_array *array)
4953 struct triangulation *item;
4955 if (!reserve((struct dynamic_array *)array, array->count + 1, sizeof(array->items[0])))
4958 item = &array->items[array->count++];
4959 ZeroMemory(item, sizeof(*item));
4963 static HRESULT add_vertex_index(struct word_array *array, WORD vertex_index)
4965 if (!reserve((struct dynamic_array *)array, array->count + 1, sizeof(array->items[0])))
4966 return E_OUTOFMEMORY;
4968 array->items[array->count++] = vertex_index;
4972 /* assume fixed point numbers can be converted to float point in place */
4973 C_ASSERT(sizeof(FIXED) == sizeof(float));
4974 C_ASSERT(sizeof(POINTFX) == sizeof(D3DXVECTOR2));
4976 static inline D3DXVECTOR2 *convert_fixed_to_float(POINTFX *pt, int count, float emsquare)
4978 D3DXVECTOR2 *ret = (D3DXVECTOR2*)pt;
4980 D3DXVECTOR2 *pt_flt = (D3DXVECTOR2*)pt;
4981 pt_flt->x = (pt->x.value + pt->x.fract / (float)0x10000) / emsquare;
4982 pt_flt->y = (pt->y.value + pt->y.fract / (float)0x10000) / emsquare;
4988 static HRESULT add_bezier_points(struct outline *outline, const D3DXVECTOR2 *p1,
4989 const D3DXVECTOR2 *p2, const D3DXVECTOR2 *p3,
4990 float max_deviation_sq)
4992 D3DXVECTOR2 split1 = {0, 0}, split2 = {0, 0}, middle, vec;
4995 D3DXVec2Scale(&split1, D3DXVec2Add(&split1, p1, p2), 0.5f);
4996 D3DXVec2Scale(&split2, D3DXVec2Add(&split2, p2, p3), 0.5f);
4997 D3DXVec2Scale(&middle, D3DXVec2Add(&middle, &split1, &split2), 0.5f);
4999 deviation_sq = D3DXVec2LengthSq(D3DXVec2Subtract(&vec, &middle, p2));
5000 if (deviation_sq < max_deviation_sq) {
5001 struct point2d *pt = add_points(outline, 1);
5002 if (!pt) return E_OUTOFMEMORY;
5004 pt->corner = POINTTYPE_CURVE;
5005 /* the end point is omitted because the end line merges into the next segment of
5006 * the split bezier curve, and the end of the split bezier curve is added outside
5007 * this recursive function. */
5009 HRESULT hr = add_bezier_points(outline, p1, &split1, &middle, max_deviation_sq);
5010 if (hr != S_OK) return hr;
5011 hr = add_bezier_points(outline, &middle, &split2, p3, max_deviation_sq);
5012 if (hr != S_OK) return hr;
5018 static inline BOOL is_direction_similar(D3DXVECTOR2 *dir1, D3DXVECTOR2 *dir2, float cos_theta)
5020 /* dot product = cos(theta) */
5021 return D3DXVec2Dot(dir1, dir2) > cos_theta;
5024 static inline D3DXVECTOR2 *unit_vec2(D3DXVECTOR2 *dir, const D3DXVECTOR2 *pt1, const D3DXVECTOR2 *pt2)
5026 return D3DXVec2Normalize(D3DXVec2Subtract(dir, pt2, pt1), dir);
5036 static BOOL attempt_line_merge(struct outline *outline,
5038 const D3DXVECTOR2 *nextpt,
5040 const struct cos_table *table)
5042 D3DXVECTOR2 curdir, lastdir;
5043 struct point2d *prevpt, *pt;
5046 pt = &outline->items[pt_index];
5047 pt_index = (pt_index - 1 + outline->count) % outline->count;
5048 prevpt = &outline->items[pt_index];
5051 pt->corner = pt->corner != POINTTYPE_CORNER ? POINTTYPE_CURVE_MIDDLE : POINTTYPE_CURVE_START;
5053 if (outline->count < 2)
5056 /* remove last point if the next line continues the last line */
5057 unit_vec2(&lastdir, &prevpt->pos, &pt->pos);
5058 unit_vec2(&curdir, &pt->pos, nextpt);
5059 if (is_direction_similar(&lastdir, &curdir, table->cos_half))
5062 if (pt->corner == POINTTYPE_CURVE_END)
5063 prevpt->corner = pt->corner;
5064 if (prevpt->corner == POINTTYPE_CURVE_END && to_curve)
5065 prevpt->corner = POINTTYPE_CURVE_MIDDLE;
5069 if (outline->count < 2)
5072 pt_index = (pt_index - 1 + outline->count) % outline->count;
5073 prevpt = &outline->items[pt_index];
5074 unit_vec2(&lastdir, &prevpt->pos, &pt->pos);
5075 unit_vec2(&curdir, &pt->pos, nextpt);
5080 static HRESULT create_outline(struct glyphinfo *glyph, void *raw_outline, int datasize,
5081 float max_deviation_sq, float emsquare, const struct cos_table *cos_table)
5083 TTPOLYGONHEADER *header = (TTPOLYGONHEADER *)raw_outline;
5085 while ((char *)header < (char *)raw_outline + datasize)
5087 TTPOLYCURVE *curve = (TTPOLYCURVE *)(header + 1);
5088 struct point2d *lastpt, *pt;
5089 D3DXVECTOR2 lastdir;
5090 D3DXVECTOR2 *pt_flt;
5092 struct outline *outline = add_outline(&glyph->outlines);
5095 return E_OUTOFMEMORY;
5097 pt = add_points(outline, 1);
5099 return E_OUTOFMEMORY;
5100 pt_flt = convert_fixed_to_float(&header->pfxStart, 1, emsquare);
5102 pt->corner = POINTTYPE_CORNER;
5104 if (header->dwType != TT_POLYGON_TYPE)
5105 FIXME("Unknown header type %d\n", header->dwType);
5107 while ((char *)curve < (char *)header + header->cb)
5109 D3DXVECTOR2 bezier_start = outline->items[outline->count - 1].pos;
5110 BOOL to_curve = curve->wType != TT_PRIM_LINE && curve->cpfx > 1;
5113 curve = (TTPOLYCURVE *)&curve->apfx[curve->cpfx];
5117 pt_flt = convert_fixed_to_float(curve->apfx, curve->cpfx, emsquare);
5119 attempt_line_merge(outline, outline->count - 1, &pt_flt[0], to_curve, cos_table);
5124 int count = curve->cpfx;
5129 D3DXVECTOR2 bezier_end;
5131 D3DXVec2Scale(&bezier_end, D3DXVec2Add(&bezier_end, &pt_flt[j], &pt_flt[j+1]), 0.5f);
5132 hr = add_bezier_points(outline, &bezier_start, &pt_flt[j], &bezier_end, max_deviation_sq);
5135 bezier_start = bezier_end;
5139 hr = add_bezier_points(outline, &bezier_start, &pt_flt[j], &pt_flt[j+1], max_deviation_sq);
5143 pt = add_points(outline, 1);
5145 return E_OUTOFMEMORY;
5147 pt->pos = pt_flt[j];
5148 pt->corner = POINTTYPE_CURVE_END;
5150 pt = add_points(outline, curve->cpfx);
5152 return E_OUTOFMEMORY;
5153 for (j = 0; j < curve->cpfx; j++)
5155 pt->pos = pt_flt[j];
5156 pt->corner = POINTTYPE_CORNER;
5161 curve = (TTPOLYCURVE *)&curve->apfx[curve->cpfx];
5164 /* remove last point if the next line continues the last line */
5165 if (outline->count >= 3) {
5168 lastpt = &outline->items[outline->count - 1];
5169 pt = &outline->items[0];
5170 if (pt->pos.x == lastpt->pos.x && pt->pos.y == lastpt->pos.y) {
5171 if (lastpt->corner == POINTTYPE_CURVE_END)
5173 if (pt->corner == POINTTYPE_CURVE_START)
5174 pt->corner = POINTTYPE_CURVE_MIDDLE;
5176 pt->corner = POINTTYPE_CURVE_END;
5179 lastpt = &outline->items[outline->count - 1];
5181 /* outline closed with a line from end to start point */
5182 attempt_line_merge(outline, outline->count - 1, &pt->pos, FALSE, cos_table);
5184 lastpt = &outline->items[0];
5185 to_curve = lastpt->corner != POINTTYPE_CORNER && lastpt->corner != POINTTYPE_CURVE_END;
5186 if (lastpt->corner == POINTTYPE_CURVE_START)
5187 lastpt->corner = POINTTYPE_CORNER;
5188 pt = &outline->items[1];
5189 if (attempt_line_merge(outline, 0, &pt->pos, to_curve, cos_table))
5190 *lastpt = outline->items[outline->count];
5193 lastpt = &outline->items[outline->count - 1];
5194 pt = &outline->items[0];
5195 unit_vec2(&lastdir, &lastpt->pos, &pt->pos);
5196 for (j = 0; j < outline->count; j++)
5201 pt = &outline->items[(j + 1) % outline->count];
5202 unit_vec2(&curdir, &lastpt->pos, &pt->pos);
5204 switch (lastpt->corner)
5206 case POINTTYPE_CURVE_START:
5207 case POINTTYPE_CURVE_END:
5208 if (!is_direction_similar(&lastdir, &curdir, cos_table->cos_45))
5209 lastpt->corner = POINTTYPE_CORNER;
5211 case POINTTYPE_CURVE_MIDDLE:
5212 if (!is_direction_similar(&lastdir, &curdir, cos_table->cos_90))
5213 lastpt->corner = POINTTYPE_CORNER;
5215 lastpt->corner = POINTTYPE_CURVE;
5223 header = (TTPOLYGONHEADER *)((char *)header + header->cb);
5228 /* Get the y-distance from a line to a point */
5229 static float get_line_to_point_y_distance(D3DXVECTOR2 *line_pt1,
5230 D3DXVECTOR2 *line_pt2,
5233 D3DXVECTOR2 line_vec = {0, 0};
5237 D3DXVec2Subtract(&line_vec, line_pt2, line_pt1);
5238 line_pt_dx = point->x - line_pt1->x;
5239 line_y = line_pt1->y + (line_vec.y * line_pt_dx) / line_vec.x;
5240 return point->y - line_y;
5243 static D3DXVECTOR2 *get_indexed_point(struct point2d_index *pt_idx)
5245 return &pt_idx->outline->items[pt_idx->vertex].pos;
5248 static D3DXVECTOR2 *get_ordered_vertex(struct glyphinfo *glyph, WORD index)
5250 return get_indexed_point(&glyph->ordered_vertices.items[index]);
5253 static void remove_triangulation(struct triangulation_array *array, struct triangulation *item)
5255 HeapFree(GetProcessHeap(), 0, item->vertex_stack.items);
5256 MoveMemory(item, item + 1, (char*)&array->items[array->count] - (char*)(item + 1));
5260 static HRESULT triangulation_add_point(struct triangulation **t_ptr,
5261 struct triangulation_array *triangulations,
5265 struct glyphinfo *glyph = triangulations->glyph;
5266 struct triangulation *t = *t_ptr;
5271 if (t->last_on_top) {
5279 if (t->last_on_top != to_top && t->vertex_stack.count > 1) {
5280 /* consume all vertices on the stack */
5281 WORD last_pt = t->vertex_stack.items[0];
5283 for (i = 1; i < t->vertex_stack.count; i++)
5285 face = add_face(&glyph->faces);
5286 if (!face) return E_OUTOFMEMORY;
5287 (*face)[0] = vtx_idx;
5288 (*face)[f1] = last_pt;
5289 (*face)[f2] = last_pt = t->vertex_stack.items[i];
5291 t->vertex_stack.items[0] = last_pt;
5292 t->vertex_stack.count = 1;
5293 } else if (t->vertex_stack.count > 1) {
5294 int i = t->vertex_stack.count - 1;
5295 D3DXVECTOR2 *point = get_ordered_vertex(glyph, vtx_idx);
5296 WORD top_idx = t->vertex_stack.items[i--];
5297 D3DXVECTOR2 *top_pt = get_ordered_vertex(glyph, top_idx);
5301 WORD prev_idx = t->vertex_stack.items[i--];
5302 D3DXVECTOR2 *prev_pt = get_ordered_vertex(glyph, prev_idx);
5304 if (prev_pt->x != top_pt->x &&
5305 ((to_top && get_line_to_point_y_distance(prev_pt, top_pt, point) > 0) ||
5306 (!to_top && get_line_to_point_y_distance(prev_pt, top_pt, point) < 0)))
5309 face = add_face(&glyph->faces);
5310 if (!face) return E_OUTOFMEMORY;
5311 (*face)[0] = vtx_idx;
5312 (*face)[f1] = prev_idx;
5313 (*face)[f2] = top_idx;
5317 t->vertex_stack.count--;
5320 t->last_on_top = to_top;
5322 hr = add_vertex_index(&t->vertex_stack, vtx_idx);
5324 if (hr == S_OK && t->merging) {
5325 struct triangulation *t2;
5327 t2 = to_top ? t - 1 : t + 1;
5328 t2->merging = FALSE;
5329 hr = triangulation_add_point(&t2, triangulations, vtx_idx, to_top);
5330 if (hr != S_OK) return hr;
5331 remove_triangulation(triangulations, t);
5339 /* check if the point is next on the outline for either the top or bottom */
5340 static D3DXVECTOR2 *triangulation_get_next_point(struct triangulation *t, struct glyphinfo *glyph, BOOL on_top)
5342 int i = t->last_on_top == on_top ? t->vertex_stack.count - 1 : 0;
5343 WORD idx = t->vertex_stack.items[i];
5344 struct point2d_index *pt_idx = &glyph->ordered_vertices.items[idx];
5345 struct outline *outline = pt_idx->outline;
5348 i = (pt_idx->vertex + outline->count - 1) % outline->count;
5350 i = (pt_idx->vertex + 1) % outline->count;
5352 return &outline->items[i].pos;
5355 static int compare_vertex_indices(const void *a, const void *b)
5357 const struct point2d_index *idx1 = a, *idx2 = b;
5358 const D3DXVECTOR2 *p1 = &idx1->outline->items[idx1->vertex].pos;
5359 const D3DXVECTOR2 *p2 = &idx2->outline->items[idx2->vertex].pos;
5360 float diff = p1->x - p2->x;
5363 diff = p1->y - p2->y;
5365 return diff == 0.0f ? 0 : (diff > 0.0f ? -1 : 1);
5368 static HRESULT triangulate(struct triangulation_array *triangulations)
5372 struct glyphinfo *glyph = triangulations->glyph;
5373 int nb_vertices = 0;
5375 struct point2d_index *idx_ptr;
5377 for (i = 0; i < glyph->outlines.count; i++)
5378 nb_vertices += glyph->outlines.items[i].count;
5380 glyph->ordered_vertices.items = HeapAlloc(GetProcessHeap(), 0,
5381 nb_vertices * sizeof(*glyph->ordered_vertices.items));
5382 if (!glyph->ordered_vertices.items)
5383 return E_OUTOFMEMORY;
5385 idx_ptr = glyph->ordered_vertices.items;
5386 for (i = 0; i < glyph->outlines.count; i++)
5388 struct outline *outline = &glyph->outlines.items[i];
5391 idx_ptr->outline = outline;
5392 idx_ptr->vertex = 0;
5394 for (j = outline->count - 1; j > 0; j--)
5396 idx_ptr->outline = outline;
5397 idx_ptr->vertex = j;
5401 glyph->ordered_vertices.count = nb_vertices;
5403 /* Native implementation seems to try to create a triangle fan from
5404 * the first outline point if the glyph only has one outline. */
5405 if (glyph->outlines.count == 1)
5407 struct outline *outline = glyph->outlines.items;
5408 D3DXVECTOR2 *base = &outline->items[0].pos;
5409 D3DXVECTOR2 *last = &outline->items[1].pos;
5412 for (i = 2; i < outline->count; i++)
5414 D3DXVECTOR2 *next = &outline->items[i].pos;
5415 D3DXVECTOR2 v1 = {0.0f, 0.0f};
5416 D3DXVECTOR2 v2 = {0.0f, 0.0f};
5418 D3DXVec2Subtract(&v1, base, last);
5419 D3DXVec2Subtract(&v2, last, next);
5420 ccw = D3DXVec2CCW(&v1, &v2);
5428 glyph->faces.items = HeapAlloc(GetProcessHeap(), 0,
5429 (outline->count - 2) * sizeof(glyph->faces.items[0]));
5430 if (!glyph->faces.items)
5431 return E_OUTOFMEMORY;
5433 glyph->faces.count = outline->count - 2;
5434 for (i = 0; i < glyph->faces.count; i++)
5436 glyph->faces.items[i][0] = 0;
5437 glyph->faces.items[i][1] = i + 1;
5438 glyph->faces.items[i][2] = i + 2;
5444 /* Perform 2D polygon triangulation for complex glyphs.
5445 * Triangulation is performed using a sweep line concept, from right to left,
5446 * by processing vertices in sorted order. Complex polygons are split into
5447 * monotone polygons which are triangulated separately. */
5448 /* FIXME: The order of the faces is not consistent with the native implementation. */
5450 /* Reserve space for maximum possible faces from triangulation.
5451 * # faces for outer outlines = outline->count - 2
5452 * # faces for inner outlines = outline->count + 2
5453 * There must be at least 1 outer outline. */
5454 glyph->faces.items = HeapAlloc(GetProcessHeap(), 0,
5455 (nb_vertices + glyph->outlines.count * 2 - 4) * sizeof(glyph->faces.items[0]));
5456 if (!glyph->faces.items)
5457 return E_OUTOFMEMORY;
5459 qsort(glyph->ordered_vertices.items, nb_vertices,
5460 sizeof(glyph->ordered_vertices.items[0]), compare_vertex_indices);
5461 for (sweep_idx = 0; sweep_idx < glyph->ordered_vertices.count; sweep_idx++)
5464 int end = triangulations->count;
5468 D3DXVECTOR2 *sweep_vtx = get_ordered_vertex(glyph, sweep_idx);
5469 int current = (start + end) / 2;
5470 struct triangulation *t = &triangulations->items[current];
5471 BOOL on_top_outline = FALSE;
5472 D3DXVECTOR2 *top_next, *bottom_next;
5473 WORD top_idx, bottom_idx;
5475 if (t->merging && t->last_on_top)
5476 top_next = triangulation_get_next_point(t + 1, glyph, TRUE);
5478 top_next = triangulation_get_next_point(t, glyph, TRUE);
5479 if (sweep_vtx == top_next)
5481 if (t->merging && t->last_on_top)
5483 hr = triangulation_add_point(&t, triangulations, sweep_idx, TRUE);
5484 if (hr != S_OK) return hr;
5486 if (t + 1 < &triangulations->items[triangulations->count] &&
5487 triangulation_get_next_point(t + 1, glyph, FALSE) == sweep_vtx)
5489 /* point also on bottom outline of higher triangulation */
5490 struct triangulation *t2 = t + 1;
5491 hr = triangulation_add_point(&t2, triangulations, sweep_idx, FALSE);
5492 if (hr != S_OK) return hr;
5497 on_top_outline = TRUE;
5500 if (t->merging && !t->last_on_top)
5501 bottom_next = triangulation_get_next_point(t - 1, glyph, FALSE);
5503 bottom_next = triangulation_get_next_point(t, glyph, FALSE);
5504 if (sweep_vtx == bottom_next)
5506 if (t->merging && !t->last_on_top)
5508 if (on_top_outline) {
5509 /* outline finished */
5510 remove_triangulation(triangulations, t);
5514 hr = triangulation_add_point(&t, triangulations, sweep_idx, FALSE);
5515 if (hr != S_OK) return hr;
5517 if (t > triangulations->items &&
5518 triangulation_get_next_point(t - 1, glyph, TRUE) == sweep_vtx)
5520 struct triangulation *t2 = t - 1;
5521 /* point also on top outline of lower triangulation */
5522 hr = triangulation_add_point(&t2, triangulations, sweep_idx, TRUE);
5523 if (hr != S_OK) return hr;
5524 t = t2 + 1; /* t may be invalidated by triangulation merging */
5534 if (t->last_on_top) {
5535 top_idx = t->vertex_stack.items[t->vertex_stack.count - 1];
5536 bottom_idx = t->vertex_stack.items[0];
5538 top_idx = t->vertex_stack.items[0];
5539 bottom_idx = t->vertex_stack.items[t->vertex_stack.count - 1];
5542 /* check if the point is inside or outside this polygon */
5543 if (get_line_to_point_y_distance(get_ordered_vertex(glyph, top_idx),
5544 top_next, sweep_vtx) > 0)
5546 start = current + 1;
5547 } else if (get_line_to_point_y_distance(get_ordered_vertex(glyph, bottom_idx),
5548 bottom_next, sweep_vtx) < 0)
5551 } else if (t->merging) {
5552 /* inside, so cancel merging */
5553 struct triangulation *t2 = t->last_on_top ? t + 1 : t - 1;
5555 t2->merging = FALSE;
5556 hr = triangulation_add_point(&t, triangulations, sweep_idx, t->last_on_top);
5557 if (hr != S_OK) return hr;
5558 hr = triangulation_add_point(&t2, triangulations, sweep_idx, t2->last_on_top);
5559 if (hr != S_OK) return hr;
5562 /* inside, so split polygon into two monotone parts */
5563 struct triangulation *t2 = add_triangulation(triangulations);
5564 if (!t2) return E_OUTOFMEMORY;
5565 MoveMemory(t + 1, t, (char*)(t2 + 1) - (char*)t);
5566 if (t->last_on_top) {
5573 ZeroMemory(&t2->vertex_stack, sizeof(t2->vertex_stack));
5574 hr = add_vertex_index(&t2->vertex_stack, t->vertex_stack.items[t->vertex_stack.count - 1]);
5575 if (hr != S_OK) return hr;
5576 hr = add_vertex_index(&t2->vertex_stack, sweep_idx);
5577 if (hr != S_OK) return hr;
5578 t2->last_on_top = !t->last_on_top;
5580 hr = triangulation_add_point(&t, triangulations, sweep_idx, t->last_on_top);
5581 if (hr != S_OK) return hr;
5587 struct triangulation *t;
5588 struct triangulation *t2 = add_triangulation(triangulations);
5589 if (!t2) return E_OUTOFMEMORY;
5590 t = &triangulations->items[start];
5591 MoveMemory(t + 1, t, (char*)(t2 + 1) - (char*)t);
5592 ZeroMemory(t, sizeof(*t));
5593 hr = add_vertex_index(&t->vertex_stack, sweep_idx);
5594 if (hr != S_OK) return hr;
5600 HRESULT WINAPI D3DXCreateTextW(LPDIRECT3DDEVICE9 device,
5601 HDC hdc, LPCWSTR text,
5602 FLOAT deviation, FLOAT extrusion,
5603 LPD3DXMESH *mesh_ptr, LPD3DXBUFFER *adjacency,
5604 LPGLYPHMETRICSFLOAT glyphmetrics)
5607 ID3DXMesh *mesh = NULL;
5608 DWORD nb_vertices, nb_faces;
5609 DWORD nb_front_faces, nb_corners, nb_outline_points;
5610 struct vertex *vertices = NULL;
5615 OUTLINETEXTMETRICW otm;
5616 HFONT font = NULL, oldfont = NULL;
5617 const MAT2 identity = {{0, 1}, {0, 0}, {0, 0}, {0, 1}};
5618 void *raw_outline = NULL;
5620 struct glyphinfo *glyphs = NULL;
5622 struct triangulation_array triangulations = {0, 0, NULL};
5624 struct vertex *vertex_ptr;
5626 float max_deviation_sq;
5627 const struct cos_table cos_table = {
5628 cos(D3DXToRadian(0.5f)),
5629 cos(D3DXToRadian(45.0f)),
5630 cos(D3DXToRadian(90.0f)),
5634 TRACE("(%p, %p, %s, %f, %f, %p, %p, %p)\n", device, hdc,
5635 debugstr_w(text), deviation, extrusion, mesh_ptr, adjacency, glyphmetrics);
5637 if (!device || !hdc || !text || !*text || deviation < 0.0f || extrusion < 0.0f || !mesh_ptr)
5638 return D3DERR_INVALIDCALL;
5642 FIXME("Case of adjacency != NULL not implemented.\n");
5646 if (!GetObjectW(GetCurrentObject(hdc, OBJ_FONT), sizeof(lf), &lf) ||
5647 !GetOutlineTextMetricsW(hdc, sizeof(otm), &otm))
5649 return D3DERR_INVALIDCALL;
5652 if (deviation == 0.0f)
5653 deviation = 1.0f / otm.otmEMSquare;
5654 max_deviation_sq = deviation * deviation;
5656 lf.lfHeight = otm.otmEMSquare;
5658 font = CreateFontIndirectW(&lf);
5663 oldfont = SelectObject(hdc, font);
5665 textlen = strlenW(text);
5666 for (i = 0; i < textlen; i++)
5668 int datasize = GetGlyphOutlineW(hdc, text[i], GGO_NATIVE, &gm, 0, NULL, &identity);
5670 return D3DERR_INVALIDCALL;
5671 if (bufsize < datasize)
5674 if (!bufsize) { /* e.g. text == " " */
5675 hr = D3DERR_INVALIDCALL;
5679 glyphs = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, textlen * sizeof(*glyphs));
5680 raw_outline = HeapAlloc(GetProcessHeap(), 0, bufsize);
5681 if (!glyphs || !raw_outline) {
5687 for (i = 0; i < textlen; i++)
5689 /* get outline points from data returned from GetGlyphOutline */
5692 glyphs[i].offset_x = offset_x;
5694 datasize = GetGlyphOutlineW(hdc, text[i], GGO_NATIVE, &gm, bufsize, raw_outline, &identity);
5695 hr = create_outline(&glyphs[i], raw_outline, datasize,
5696 max_deviation_sq, otm.otmEMSquare, &cos_table);
5697 if (hr != S_OK) goto error;
5699 triangulations.glyph = &glyphs[i];
5700 hr = triangulate(&triangulations);
5701 if (hr != S_OK) goto error;
5702 if (triangulations.count) {
5703 ERR("%d incomplete triangulations of glyph (%u).\n", triangulations.count, text[i]);
5704 triangulations.count = 0;
5709 glyphmetrics[i].gmfBlackBoxX = gm.gmBlackBoxX / (float)otm.otmEMSquare;
5710 glyphmetrics[i].gmfBlackBoxY = gm.gmBlackBoxY / (float)otm.otmEMSquare;
5711 glyphmetrics[i].gmfptGlyphOrigin.x = gm.gmptGlyphOrigin.x / (float)otm.otmEMSquare;
5712 glyphmetrics[i].gmfptGlyphOrigin.y = gm.gmptGlyphOrigin.y / (float)otm.otmEMSquare;
5713 glyphmetrics[i].gmfCellIncX = gm.gmCellIncX / (float)otm.otmEMSquare;
5714 glyphmetrics[i].gmfCellIncY = gm.gmCellIncY / (float)otm.otmEMSquare;
5716 offset_x += gm.gmCellIncX / (float)otm.otmEMSquare;
5719 /* corner points need an extra vertex for the different side faces normals */
5721 nb_outline_points = 0;
5723 for (i = 0; i < textlen; i++)
5726 nb_outline_points += glyphs[i].ordered_vertices.count;
5727 nb_front_faces += glyphs[i].faces.count;
5728 for (j = 0; j < glyphs[i].outlines.count; j++)
5731 struct outline *outline = &glyphs[i].outlines.items[j];
5732 nb_corners++; /* first outline point always repeated as a corner */
5733 for (k = 1; k < outline->count; k++)
5734 if (outline->items[k].corner)
5739 nb_vertices = (nb_outline_points + nb_corners) * 2 + nb_outline_points * 2;
5740 nb_faces = nb_outline_points * 2 + nb_front_faces * 2;
5743 hr = D3DXCreateMeshFVF(nb_faces, nb_vertices, D3DXMESH_MANAGED,
5744 D3DFVF_XYZ | D3DFVF_NORMAL, device, &mesh);
5748 hr = mesh->lpVtbl->LockVertexBuffer(mesh, 0, (LPVOID *)&vertices);
5752 hr = mesh->lpVtbl->LockIndexBuffer(mesh, 0, (LPVOID *)&faces);
5756 /* convert 2D vertices and faces into 3D mesh */
5757 vertex_ptr = vertices;
5759 if (extrusion == 0.0f) {
5766 for (i = 0; i < textlen; i++)
5770 struct vertex *back_vertices;
5773 /* side vertices and faces */
5774 for (j = 0; j < glyphs[i].outlines.count; j++)
5776 struct vertex *outline_vertices = vertex_ptr;
5777 struct outline *outline = &glyphs[i].outlines.items[j];
5779 struct point2d *prevpt = &outline->items[outline->count - 1];
5780 struct point2d *pt = &outline->items[0];
5782 for (k = 1; k <= outline->count; k++)
5785 struct point2d *nextpt = &outline->items[k % outline->count];
5786 WORD vtx_idx = vertex_ptr - vertices;
5789 if (pt->corner == POINTTYPE_CURVE_START)
5790 D3DXVec2Subtract(&vec, &pt->pos, &prevpt->pos);
5791 else if (pt->corner)
5792 D3DXVec2Subtract(&vec, &nextpt->pos, &pt->pos);
5794 D3DXVec2Subtract(&vec, &nextpt->pos, &prevpt->pos);
5795 D3DXVec2Normalize(&vec, &vec);
5796 vtx.normal.x = -vec.y;
5797 vtx.normal.y = vec.x;
5800 vtx.position.x = pt->pos.x + glyphs[i].offset_x;
5801 vtx.position.y = pt->pos.y;
5803 *vertex_ptr++ = vtx;
5805 vtx.position.z = -extrusion;
5806 *vertex_ptr++ = vtx;
5808 vtx.position.x = nextpt->pos.x + glyphs[i].offset_x;
5809 vtx.position.y = nextpt->pos.y;
5810 if (pt->corner && nextpt->corner && nextpt->corner != POINTTYPE_CURVE_END) {
5811 vtx.position.z = -extrusion;
5812 *vertex_ptr++ = vtx;
5814 *vertex_ptr++ = vtx;
5816 (*face_ptr)[0] = vtx_idx;
5817 (*face_ptr)[1] = vtx_idx + 2;
5818 (*face_ptr)[2] = vtx_idx + 1;
5821 (*face_ptr)[0] = vtx_idx;
5822 (*face_ptr)[1] = vtx_idx + 3;
5823 (*face_ptr)[2] = vtx_idx + 2;
5826 if (nextpt->corner) {
5827 if (nextpt->corner == POINTTYPE_CURVE_END) {
5828 D3DXVECTOR2 *nextpt2 = &outline->items[(k + 1) % outline->count].pos;
5829 D3DXVec2Subtract(&vec, nextpt2, &nextpt->pos);
5831 D3DXVec2Subtract(&vec, &nextpt->pos, &pt->pos);
5833 D3DXVec2Normalize(&vec, &vec);
5834 vtx.normal.x = -vec.y;
5835 vtx.normal.y = vec.x;
5838 *vertex_ptr++ = vtx;
5839 vtx.position.z = -extrusion;
5840 *vertex_ptr++ = vtx;
5843 (*face_ptr)[0] = vtx_idx;
5844 (*face_ptr)[1] = vtx_idx + 3;
5845 (*face_ptr)[2] = vtx_idx + 1;
5848 (*face_ptr)[0] = vtx_idx;
5849 (*face_ptr)[1] = vtx_idx + 2;
5850 (*face_ptr)[2] = vtx_idx + 3;
5858 *vertex_ptr++ = *outline_vertices++;
5859 *vertex_ptr++ = *outline_vertices++;
5863 /* back vertices and faces */
5864 back_faces = face_ptr;
5865 back_vertices = vertex_ptr;
5866 for (j = 0; j < glyphs[i].ordered_vertices.count; j++)
5868 D3DXVECTOR2 *pt = get_ordered_vertex(&glyphs[i], j);
5869 vertex_ptr->position.x = pt->x + glyphs[i].offset_x;
5870 vertex_ptr->position.y = pt->y;
5871 vertex_ptr->position.z = 0;
5872 vertex_ptr->normal.x = 0;
5873 vertex_ptr->normal.y = 0;
5874 vertex_ptr->normal.z = 1;
5877 count = back_vertices - vertices;
5878 for (j = 0; j < glyphs[i].faces.count; j++)
5880 face *f = &glyphs[i].faces.items[j];
5881 (*face_ptr)[0] = (*f)[0] + count;
5882 (*face_ptr)[1] = (*f)[1] + count;
5883 (*face_ptr)[2] = (*f)[2] + count;
5887 /* front vertices and faces */
5888 j = count = vertex_ptr - back_vertices;
5891 vertex_ptr->position.x = back_vertices->position.x;
5892 vertex_ptr->position.y = back_vertices->position.y;
5893 vertex_ptr->position.z = -extrusion;
5894 vertex_ptr->normal.x = 0;
5895 vertex_ptr->normal.y = 0;
5896 vertex_ptr->normal.z = extrusion == 0.0f ? 1.0f : -1.0f;
5900 j = face_ptr - back_faces;
5903 (*face_ptr)[0] = (*back_faces)[0] + count;
5904 (*face_ptr)[1] = (*back_faces)[f1] + count;
5905 (*face_ptr)[2] = (*back_faces)[f2] + count;
5915 if (faces) mesh->lpVtbl->UnlockIndexBuffer(mesh);
5916 if (vertices) mesh->lpVtbl->UnlockVertexBuffer(mesh);
5917 if (hr != D3D_OK) mesh->lpVtbl->Release(mesh);
5920 for (i = 0; i < textlen; i++)
5923 for (j = 0; j < glyphs[i].outlines.count; j++)
5924 HeapFree(GetProcessHeap(), 0, glyphs[i].outlines.items[j].items);
5925 HeapFree(GetProcessHeap(), 0, glyphs[i].outlines.items);
5926 HeapFree(GetProcessHeap(), 0, glyphs[i].faces.items);
5927 HeapFree(GetProcessHeap(), 0, glyphs[i].ordered_vertices.items);
5929 HeapFree(GetProcessHeap(), 0, glyphs);
5931 if (triangulations.items) {
5933 for (i = 0; i < triangulations.count; i++)
5934 HeapFree(GetProcessHeap(), 0, triangulations.items[i].vertex_stack.items);
5935 HeapFree(GetProcessHeap(), 0, triangulations.items);
5937 HeapFree(GetProcessHeap(), 0, raw_outline);
5938 if (oldfont) SelectObject(hdc, oldfont);
5939 if (font) DeleteObject(font);
5944 HRESULT WINAPI D3DXValidMesh(LPD3DXMESH mesh, CONST DWORD *adjacency, LPD3DXBUFFER *errors_and_warnings)
5946 FIXME("(%p, %p, %p): stub\n", mesh, adjacency, *errors_and_warnings);
5951 static BOOL weld_float1(void *to, void *from, FLOAT epsilon)
5956 if (fabsf(*v1 - *v2) <= epsilon)
5966 static BOOL weld_float2(void *to, void *from, FLOAT epsilon)
5968 D3DXVECTOR2 *v1 = to;
5969 D3DXVECTOR2 *v2 = from;
5970 FLOAT diff_x = fabsf(v1->x - v2->x);
5971 FLOAT diff_y = fabsf(v1->y - v2->y);
5972 FLOAT max_abs_diff = max(diff_x, diff_y);
5974 if (max_abs_diff <= epsilon)
5976 memcpy(to, from, sizeof(D3DXVECTOR2));
5984 static BOOL weld_float3(void *to, void *from, FLOAT epsilon)
5986 D3DXVECTOR3 *v1 = to;
5987 D3DXVECTOR3 *v2 = from;
5988 FLOAT diff_x = fabsf(v1->x - v2->x);
5989 FLOAT diff_y = fabsf(v1->y - v2->y);
5990 FLOAT diff_z = fabsf(v1->z - v2->z);
5991 FLOAT max_abs_diff = max(diff_x, diff_y);
5992 max_abs_diff = max(diff_z, max_abs_diff);
5994 if (max_abs_diff <= epsilon)
5996 memcpy(to, from, sizeof(D3DXVECTOR3));
6004 static BOOL weld_float4(void *to, void *from, FLOAT epsilon)
6006 D3DXVECTOR4 *v1 = to;
6007 D3DXVECTOR4 *v2 = from;
6008 FLOAT diff_x = fabsf(v1->x - v2->x);
6009 FLOAT diff_y = fabsf(v1->y - v2->y);
6010 FLOAT diff_z = fabsf(v1->z - v2->z);
6011 FLOAT diff_w = fabsf(v1->w - v2->w);
6012 FLOAT max_abs_diff = fmax(diff_x, diff_y);
6013 max_abs_diff = max(diff_z, max_abs_diff);
6014 max_abs_diff = max(diff_w, max_abs_diff);
6016 if (max_abs_diff <= epsilon)
6018 memcpy(to, from, sizeof(D3DXVECTOR4));
6026 static BOOL weld_ubyte4(void *to, void *from, FLOAT epsilon)
6030 BYTE truncated_epsilon = (BYTE)epsilon;
6031 BYTE diff_x = b1[0] > b2[0] ? b1[0] - b2[0] : b2[0] - b1[0];
6032 BYTE diff_y = b1[1] > b2[1] ? b1[1] - b2[1] : b2[1] - b1[1];
6033 BYTE diff_z = b1[2] > b2[2] ? b1[2] - b2[2] : b2[2] - b1[2];
6034 BYTE diff_w = b1[3] > b2[3] ? b1[3] - b2[3] : b2[3] - b1[3];
6035 BYTE max_diff = max(diff_x, diff_y);
6036 max_diff = max(diff_z, max_diff);
6037 max_diff = max(diff_w, max_diff);
6039 if (max_diff <= truncated_epsilon)
6041 memcpy(to, from, 4 * sizeof(BYTE));
6049 static BOOL weld_ubyte4n(void *to, void *from, FLOAT epsilon)
6051 return weld_ubyte4(to, from, epsilon * UCHAR_MAX);
6054 static BOOL weld_d3dcolor(void *to, void *from, FLOAT epsilon)
6056 return weld_ubyte4n(to, from, epsilon);
6059 static BOOL weld_short2(void *to, void *from, FLOAT epsilon)
6063 SHORT truncated_epsilon = (SHORT)epsilon;
6064 SHORT diff_x = abs(s1[0] - s2[0]);
6065 SHORT diff_y = abs(s1[1] - s2[1]);
6066 SHORT max_abs_diff = max(diff_x, diff_y);
6068 if (max_abs_diff <= truncated_epsilon)
6070 memcpy(to, from, 2 * sizeof(SHORT));
6078 static BOOL weld_short2n(void *to, void *from, FLOAT epsilon)
6080 return weld_short2(to, from, epsilon * SHRT_MAX);
6083 static BOOL weld_short4(void *to, void *from, FLOAT epsilon)
6087 SHORT truncated_epsilon = (SHORT)epsilon;
6088 SHORT diff_x = abs(s1[0] - s2[0]);
6089 SHORT diff_y = abs(s1[1] - s2[1]);
6090 SHORT diff_z = abs(s1[2] - s2[2]);
6091 SHORT diff_w = abs(s1[3] - s2[3]);
6092 SHORT max_abs_diff = max(diff_x, diff_y);
6093 max_abs_diff = max(diff_z, max_abs_diff);
6094 max_abs_diff = max(diff_w, max_abs_diff);
6096 if (max_abs_diff <= truncated_epsilon)
6098 memcpy(to, from, 4 * sizeof(SHORT));
6106 static BOOL weld_short4n(void *to, void *from, FLOAT epsilon)
6108 return weld_short4(to, from, epsilon * SHRT_MAX);
6111 static BOOL weld_ushort2n(void *to, void *from, FLOAT epsilon)
6115 USHORT scaled_epsilon = (USHORT)(epsilon * USHRT_MAX);
6116 USHORT diff_x = s1[0] > s2[0] ? s1[0] - s2[0] : s2[0] - s1[0];
6117 USHORT diff_y = s1[1] > s2[1] ? s1[1] - s2[1] : s2[1] - s1[1];
6118 USHORT max_diff = max(diff_x, diff_y);
6120 if (max_diff <= scaled_epsilon)
6122 memcpy(to, from, 2 * sizeof(USHORT));
6130 static BOOL weld_ushort4n(void *to, void *from, FLOAT epsilon)
6134 USHORT scaled_epsilon = (USHORT)(epsilon * USHRT_MAX);
6135 USHORT diff_x = s1[0] > s2[0] ? s1[0] - s2[0] : s2[0] - s1[0];
6136 USHORT diff_y = s1[1] > s2[1] ? s1[1] - s2[1] : s2[1] - s1[1];
6137 USHORT diff_z = s1[2] > s2[2] ? s1[2] - s2[2] : s2[2] - s1[2];
6138 USHORT diff_w = s1[3] > s2[3] ? s1[3] - s2[3] : s2[3] - s1[3];
6139 USHORT max_diff = max(diff_x, diff_y);
6140 max_diff = max(diff_z, max_diff);
6141 max_diff = max(diff_w, max_diff);
6143 if (max_diff <= scaled_epsilon)
6145 memcpy(to, from, 4 * sizeof(USHORT));
6161 static struct udec3 dword_to_udec3(DWORD d)
6166 v.y = (d & 0xffc00) >> 10;
6167 v.z = (d & 0x3ff00000) >> 20;
6168 v.w = (d & 0xc0000000) >> 30;
6173 static BOOL weld_udec3(void *to, void *from, FLOAT epsilon)
6177 struct udec3 v1 = dword_to_udec3(*d1);
6178 struct udec3 v2 = dword_to_udec3(*d2);
6179 UINT truncated_epsilon = (UINT)epsilon;
6180 UINT diff_x = v1.x > v2.x ? v1.x - v2.x : v2.x - v1.x;
6181 UINT diff_y = v1.y > v2.y ? v1.y - v2.y : v2.y - v1.y;
6182 UINT diff_z = v1.z > v2.z ? v1.z - v2.z : v2.z - v1.z;
6183 UINT diff_w = v1.w > v2.w ? v1.w - v2.w : v2.w - v1.w;
6184 UINT max_diff = max(diff_x, diff_y);
6185 max_diff = max(diff_z, max_diff);
6186 max_diff = max(diff_w, max_diff);
6188 if (max_diff <= truncated_epsilon)
6190 memcpy(to, from, sizeof(DWORD));
6206 static struct dec3n dword_to_dec3n(DWORD d)
6211 v.y = (d & 0xffc00) >> 10;
6212 v.z = (d & 0x3ff00000) >> 20;
6213 v.w = (d & 0xc0000000) >> 30;
6218 static BOOL weld_dec3n(void *to, void *from, FLOAT epsilon)
6220 const UINT MAX_DEC3N = 511;
6223 struct dec3n v1 = dword_to_dec3n(*d1);
6224 struct dec3n v2 = dword_to_dec3n(*d2);
6225 INT scaled_epsilon = (INT)(epsilon * MAX_DEC3N);
6226 INT diff_x = abs(v1.x - v2.x);
6227 INT diff_y = abs(v1.y - v2.y);
6228 INT diff_z = abs(v1.z - v2.z);
6229 INT diff_w = abs(v1.w - v2.w);
6230 INT max_abs_diff = max(diff_x, diff_y);
6231 max_abs_diff = max(diff_z, max_abs_diff);
6232 max_abs_diff = max(diff_w, max_abs_diff);
6234 if (max_abs_diff <= scaled_epsilon)
6236 memcpy(to, from, sizeof(DWORD));
6244 static BOOL weld_float16_2(void *to, void *from, FLOAT epsilon)
6246 D3DXFLOAT16 *v1_float16 = to;
6247 D3DXFLOAT16 *v2_float16 = from;
6251 const UINT NUM_ELEM = 2;
6255 D3DXFloat16To32Array(v1, v1_float16, NUM_ELEM);
6256 D3DXFloat16To32Array(v2, v2_float16, NUM_ELEM);
6258 diff_x = fabsf(v1[0] - v2[0]);
6259 diff_y = fabsf(v1[1] - v2[1]);
6260 max_abs_diff = max(diff_x, diff_y);
6262 if (max_abs_diff <= epsilon)
6264 memcpy(to, from, NUM_ELEM * sizeof(D3DXFLOAT16));
6272 static BOOL weld_float16_4(void *to, void *from, FLOAT epsilon)
6274 D3DXFLOAT16 *v1_float16 = to;
6275 D3DXFLOAT16 *v2_float16 = from;
6281 const UINT NUM_ELEM = 4;
6285 D3DXFloat16To32Array(v1, v1_float16, NUM_ELEM);
6286 D3DXFloat16To32Array(v2, v2_float16, NUM_ELEM);
6288 diff_x = fabsf(v1[0] - v2[0]);
6289 diff_y = fabsf(v1[1] - v2[1]);
6290 diff_z = fabsf(v1[2] - v2[2]);
6291 diff_w = fabsf(v1[3] - v2[3]);
6292 max_abs_diff = max(diff_x, diff_y);
6293 max_abs_diff = max(diff_z, max_abs_diff);
6294 max_abs_diff = max(diff_w, max_abs_diff);
6296 if (max_abs_diff <= epsilon)
6298 memcpy(to, from, NUM_ELEM * sizeof(D3DXFLOAT16));
6306 /* Sets the vertex components to the same value if they are within epsilon. */
6307 static BOOL weld_component(void *to, void *from, D3DDECLTYPE type, FLOAT epsilon)
6309 /* Quiet FIXMEs as this is in a loop with potentially thousand of iterations. */
6310 BOOL fixme_once_unused = FALSE;
6311 BOOL fixme_once_unknown = FALSE;
6315 case D3DDECLTYPE_FLOAT1:
6316 return weld_float1(to, from, epsilon);
6318 case D3DDECLTYPE_FLOAT2:
6319 return weld_float2(to, from, epsilon);
6321 case D3DDECLTYPE_FLOAT3:
6322 return weld_float3(to, from, epsilon);
6324 case D3DDECLTYPE_FLOAT4:
6325 return weld_float4(to, from, epsilon);
6327 case D3DDECLTYPE_D3DCOLOR:
6328 return weld_d3dcolor(to, from, epsilon);
6330 case D3DDECLTYPE_UBYTE4:
6331 return weld_ubyte4(to, from, epsilon);
6333 case D3DDECLTYPE_SHORT2:
6334 return weld_short2(to, from, epsilon);
6336 case D3DDECLTYPE_SHORT4:
6337 return weld_short4(to, from, epsilon);
6339 case D3DDECLTYPE_UBYTE4N:
6340 return weld_ubyte4n(to, from, epsilon);
6342 case D3DDECLTYPE_SHORT2N:
6343 return weld_short2n(to, from, epsilon);
6345 case D3DDECLTYPE_SHORT4N:
6346 return weld_short4n(to, from, epsilon);
6348 case D3DDECLTYPE_USHORT2N:
6349 return weld_ushort2n(to, from, epsilon);
6351 case D3DDECLTYPE_USHORT4N:
6352 return weld_ushort4n(to, from, epsilon);
6354 case D3DDECLTYPE_UDEC3:
6355 return weld_udec3(to, from, epsilon);
6357 case D3DDECLTYPE_DEC3N:
6358 return weld_dec3n(to, from, epsilon);
6360 case D3DDECLTYPE_FLOAT16_2:
6361 return weld_float16_2(to, from, epsilon);
6363 case D3DDECLTYPE_FLOAT16_4:
6364 return weld_float16_4(to, from, epsilon);
6366 case D3DDECLTYPE_UNUSED:
6367 if (!fixme_once_unused++)
6368 FIXME("D3DDECLTYPE_UNUSED welding not implemented.\n");
6372 if (!fixme_once_unknown++)
6373 FIXME("Welding of unknown declaration type %d is not implemented.\n", type);
6380 static FLOAT get_component_epsilon(const D3DVERTEXELEMENT9 *decl_ptr, const D3DXWELDEPSILONS *epsilons)
6382 FLOAT epsilon = 0.0f;
6383 /* Quiet FIXMEs as this is in a loop with potentially thousand of iterations. */
6384 static BOOL fixme_once_blendindices = FALSE;
6385 static BOOL fixme_once_positiont = FALSE;
6386 static BOOL fixme_once_fog = FALSE;
6387 static BOOL fixme_once_depth = FALSE;
6388 static BOOL fixme_once_sample = FALSE;
6389 static BOOL fixme_once_unknown = FALSE;
6391 switch (decl_ptr->Usage)
6393 case D3DDECLUSAGE_POSITION:
6394 epsilon = epsilons->Position;
6396 case D3DDECLUSAGE_BLENDWEIGHT:
6397 epsilon = epsilons->BlendWeights;
6399 case D3DDECLUSAGE_NORMAL:
6400 epsilon = epsilons->Normals;
6402 case D3DDECLUSAGE_PSIZE:
6403 epsilon = epsilons->PSize;
6405 case D3DDECLUSAGE_TEXCOORD:
6407 BYTE usage_index = decl_ptr->UsageIndex;
6408 if (usage_index > 7)
6410 epsilon = epsilons->Texcoords[usage_index];
6413 case D3DDECLUSAGE_TANGENT:
6414 epsilon = epsilons->Tangent;
6416 case D3DDECLUSAGE_BINORMAL:
6417 epsilon = epsilons->Binormal;
6419 case D3DDECLUSAGE_TESSFACTOR:
6420 epsilon = epsilons->TessFactor;
6422 case D3DDECLUSAGE_COLOR:
6423 if (decl_ptr->UsageIndex == 0)
6424 epsilon = epsilons->Diffuse;
6425 else if (decl_ptr->UsageIndex == 1)
6426 epsilon = epsilons->Specular;
6430 case D3DDECLUSAGE_BLENDINDICES:
6431 if (!fixme_once_blendindices++)
6432 FIXME("D3DDECLUSAGE_BLENDINDICES welding not implemented.\n");
6434 case D3DDECLUSAGE_POSITIONT:
6435 if (!fixme_once_positiont++)
6436 FIXME("D3DDECLUSAGE_POSITIONT welding not implemented.\n");
6438 case D3DDECLUSAGE_FOG:
6439 if (!fixme_once_fog++)
6440 FIXME("D3DDECLUSAGE_FOG welding not implemented.\n");
6442 case D3DDECLUSAGE_DEPTH:
6443 if (!fixme_once_depth++)
6444 FIXME("D3DDECLUSAGE_DEPTH welding not implemented.\n");
6446 case D3DDECLUSAGE_SAMPLE:
6447 if (!fixme_once_sample++)
6448 FIXME("D3DDECLUSAGE_SAMPLE welding not implemented.\n");
6451 if (!fixme_once_unknown++)
6452 FIXME("Unknown usage %x\n", decl_ptr->Usage);
6459 /* Helper function for reading a 32-bit index buffer. */
6460 static inline DWORD read_ib(void *index_buffer, BOOL indices_are_32bit,
6463 if (indices_are_32bit)
6465 DWORD *indices = index_buffer;
6466 return indices[index];
6470 WORD *indices = index_buffer;
6471 return indices[index];
6475 /* Helper function for writing to a 32-bit index buffer. */
6476 static inline void write_ib(void *index_buffer, BOOL indices_are_32bit,
6477 DWORD index, DWORD value)
6479 if (indices_are_32bit)
6481 DWORD *indices = index_buffer;
6482 indices[index] = value;
6486 WORD *indices = index_buffer;
6487 indices[index] = value;
6491 /*************************************************************************
6492 * D3DXWeldVertices (D3DX9_36.@)
6494 * Welds together similar vertices. The similarity between vert-
6495 * ices can be the position and other components such as
6499 * mesh [I] Mesh which vertices will be welded together.
6500 * flags [I] D3DXWELDEPSILONSFLAGS specifying how to weld.
6501 * epsilons [I] How similar a component needs to be for welding.
6502 * adjacency [I] Which faces are adjacent to other faces.
6503 * adjacency_out [O] Updated adjacency after welding.
6504 * face_remap_out [O] Which faces the old faces have been mapped to.
6505 * vertex_remap_out [O] Which vertices the old vertices have been mapped to.
6509 * Failure: D3DERR_INVALIDCALL, E_OUTOFMEMORY.
6512 * Attribute sorting not implemented.
6515 HRESULT WINAPI D3DXWeldVertices(LPD3DXMESH mesh,
6517 CONST D3DXWELDEPSILONS *epsilons,
6518 CONST DWORD *adjacency,
6519 DWORD *adjacency_out,
6520 DWORD *face_remap_out,
6521 LPD3DXBUFFER *vertex_remap_out)
6523 DWORD *adjacency_generated = NULL;
6524 const DWORD *adjacency_ptr;
6525 DWORD *attributes = NULL;
6526 const FLOAT DEFAULT_EPSILON = 1.0e-6f;
6529 void *indices = NULL;
6530 BOOL indices_are_32bit = mesh->lpVtbl->GetOptions(mesh) & D3DXMESH_32BIT;
6531 DWORD optimize_flags;
6532 DWORD *point_reps = NULL;
6533 ID3DXMeshImpl *This = impl_from_ID3DXMesh(mesh);
6534 DWORD *vertex_face_map = NULL;
6535 ID3DXBuffer *vertex_remap = NULL;
6536 BYTE *vertices = NULL;
6538 TRACE("(%p, %x, %p, %p, %p, %p, %p)\n", mesh, flags, epsilons,
6539 adjacency, adjacency_out, face_remap_out, vertex_remap_out);
6543 WARN("No flags is undefined. Using D3DXWELDEPSILONS_WELDPARTIALMATCHES instead.\n");
6544 flags = D3DXWELDEPSILONS_WELDPARTIALMATCHES;
6547 if (adjacency) /* Use supplied adjacency. */
6549 adjacency_ptr = adjacency;
6551 else /* Adjacency has to be generated. */
6553 adjacency_generated = HeapAlloc(GetProcessHeap(), 0, 3 * This->numfaces * sizeof(*adjacency_generated));
6554 if (!adjacency_generated)
6556 ERR("Couldn't allocate memory for adjacency_generated.\n");
6560 hr = mesh->lpVtbl->GenerateAdjacency(mesh, DEFAULT_EPSILON, adjacency_generated);
6563 ERR("Couldn't generate adjacency.\n");
6566 adjacency_ptr = adjacency_generated;
6569 /* Point representation says which vertices can be replaced. */
6570 point_reps = HeapAlloc(GetProcessHeap(), 0, This->numvertices * sizeof(*point_reps));
6574 ERR("Couldn't allocate memory for point_reps.\n");
6577 hr = mesh->lpVtbl->ConvertAdjacencyToPointReps(mesh, adjacency_ptr, point_reps);
6580 ERR("ConvertAdjacencyToPointReps failed.\n");
6584 hr = mesh->lpVtbl->LockIndexBuffer(mesh, 0, &indices);
6587 ERR("Couldn't lock index buffer.\n");
6591 hr = mesh->lpVtbl->LockAttributeBuffer(mesh, 0, &attributes);
6594 ERR("Couldn't lock attribute buffer.\n");
6597 vertex_face_map = HeapAlloc(GetProcessHeap(), 0, This->numvertices * sizeof(*vertex_face_map));
6598 if (!vertex_face_map)
6601 ERR("Couldn't allocate memory for vertex_face_map.\n");
6604 /* Build vertex face map, so that a vertex's face can be looked up. */
6605 for (i = 0; i < This->numfaces; i++)
6608 for (j = 0; j < 3; j++)
6610 DWORD index = read_ib(indices, indices_are_32bit, 3*i + j);
6611 vertex_face_map[index] = i;
6615 if (flags & D3DXWELDEPSILONS_WELDPARTIALMATCHES)
6617 hr = mesh->lpVtbl->LockVertexBuffer(mesh, 0, (void**)&vertices);
6620 ERR("Couldn't lock vertex buffer.\n");
6623 /* For each vertex that can be removed, compare its vertex components
6624 * with the vertex components from the vertex that can replace it. A
6625 * vertex is only fully replaced if all the components match and the
6626 * flag D3DXWELDEPSILONS_DONOTREMOVEVERTICES is not set, and they
6627 * belong to the same attribute group. Otherwise the vertex components
6628 * that are within epsilon are set to the same value.
6630 for (i = 0; i < 3 * This->numfaces; i++)
6632 D3DVERTEXELEMENT9 *decl_ptr;
6633 DWORD vertex_size = mesh->lpVtbl->GetNumBytesPerVertex(mesh);
6634 DWORD num_vertex_components;
6637 DWORD index = read_ib(indices, indices_are_32bit, i);
6639 for (decl_ptr = This->cached_declaration, num_vertex_components = 0; decl_ptr->Stream != 0xFF; decl_ptr++, num_vertex_components++)
6641 BYTE *to = &vertices[vertex_size*index + decl_ptr->Offset];
6642 BYTE *from = &vertices[vertex_size*point_reps[index] + decl_ptr->Offset];
6643 FLOAT epsilon = get_component_epsilon(decl_ptr, epsilons);
6645 /* Don't weld self */
6646 if (index == point_reps[index])
6652 if (weld_component(to, from, decl_ptr->Type, epsilon))
6656 all_match = (num_vertex_components == matches);
6657 if (all_match && !(flags & D3DXWELDEPSILONS_DONOTREMOVEVERTICES))
6659 DWORD to_face = vertex_face_map[index];
6660 DWORD from_face = vertex_face_map[point_reps[index]];
6661 if(attributes[to_face] != attributes[from_face] && !(flags & D3DXWELDEPSILONS_DONOTSPLIT))
6663 write_ib(indices, indices_are_32bit, i, point_reps[index]);
6666 mesh->lpVtbl->UnlockVertexBuffer(mesh);
6669 else if (flags & D3DXWELDEPSILONS_WELDALL)
6671 for (i = 0; i < 3 * This->numfaces; i++)
6673 DWORD index = read_ib(indices, indices_are_32bit, i);
6674 DWORD to_face = vertex_face_map[index];
6675 DWORD from_face = vertex_face_map[point_reps[index]];
6676 if(attributes[to_face] != attributes[from_face] && !(flags & D3DXWELDEPSILONS_DONOTSPLIT))
6678 write_ib(indices, indices_are_32bit, i, point_reps[index]);
6681 mesh->lpVtbl->UnlockAttributeBuffer(mesh);
6683 mesh->lpVtbl->UnlockIndexBuffer(mesh);
6686 /* Compact mesh using OptimizeInplace */
6687 optimize_flags = D3DXMESHOPT_COMPACT;
6688 hr = mesh->lpVtbl->OptimizeInplace(mesh, optimize_flags, adjacency_ptr, adjacency_out, face_remap_out, vertex_remap_out);
6691 ERR("Couldn't compact mesh.\n");
6697 HeapFree(GetProcessHeap(), 0, adjacency_generated);
6698 HeapFree(GetProcessHeap(), 0, point_reps);
6699 HeapFree(GetProcessHeap(), 0, vertex_face_map);
6700 if (attributes) mesh->lpVtbl->UnlockAttributeBuffer(mesh);
6701 if (indices) mesh->lpVtbl->UnlockIndexBuffer(mesh);
6702 if (vertex_remap) ID3DXBuffer_Release(vertex_remap);
6703 if (vertices) mesh->lpVtbl->UnlockVertexBuffer(mesh);
6708 /*************************************************************************
6709 * D3DXOptimizeFaces (D3DX9_36.@)
6711 * Re-orders the faces so the vertex cache is used optimally.
6714 * indices [I] Pointer to an index buffer belonging to a mesh.
6715 * num_faces [I] Number of faces in the mesh.
6716 * num_vertices [I] Number of vertices in the mesh.
6717 * indices_are_32bit [I] Specifies whether indices are 32- or 16-bit.
6718 * face_remap [I/O] The new order the faces should be drawn in.
6722 * Failure: D3DERR_INVALIDCALL.
6725 * The face re-ordering does not use the vertex cache optimally.
6728 HRESULT WINAPI D3DXOptimizeFaces(LPCVOID indices,
6731 BOOL indices_are_32bit,
6735 UINT j = num_faces - 1;
6736 UINT limit_16_bit = 2 << 15; /* According to MSDN */
6737 HRESULT hr = D3D_OK;
6739 FIXME("(%p, %u, %u, %s, %p): semi-stub. Face order will not be optimal.\n",
6740 indices, num_faces, num_vertices,
6741 indices_are_32bit ? "TRUE" : "FALSE", face_remap);
6743 if (!indices_are_32bit && num_faces >= limit_16_bit)
6745 WARN("Number of faces must be less than %d when using 16-bit indices.\n",
6747 hr = D3DERR_INVALIDCALL;
6753 WARN("Face remap pointer is NULL.\n");
6754 hr = D3DERR_INVALIDCALL;
6758 /* The faces are drawn in reverse order for simple meshes. This ordering
6759 * is not optimal for complicated meshes, but will not break anything
6760 * either. The ordering should be changed to take advantage of the vertex
6761 * cache on the graphics card.
6763 * TODO Re-order to take advantage of vertex cache.
6765 for (i = 0; i < num_faces; i++)
6767 face_remap[i] = j--;