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
10 * This library is free software; you can redistribute it and/or
11 * modify it under the terms of the GNU Lesser General Public
12 * License as published by the Free Software Foundation; either
13 * version 2.1 of the License, or (at your option) any later version.
15 * This library is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
18 * Lesser General Public License for more details.
20 * You should have received a copy of the GNU Lesser General Public
21 * License along with this library; if not, write to the Free Software
22 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
26 #include "wine/port.h"
29 #define NONAMELESSUNION
37 #include "wine/debug.h"
38 #include "wine/unicode.h"
39 #include "wine/list.h"
40 #include "d3dx9_36_private.h"
42 WINE_DEFAULT_DEBUG_CHANNEL(d3dx);
44 typedef struct ID3DXMeshImpl
46 ID3DXMesh ID3DXMesh_iface;
53 IDirect3DDevice9 *device;
54 IDirect3DVertexDeclaration9 *vertex_declaration;
55 IDirect3DVertexBuffer9 *vertex_buffer;
56 IDirect3DIndexBuffer9 *index_buffer;
58 int attrib_buffer_lock_count;
59 DWORD attrib_table_size;
60 D3DXATTRIBUTERANGE *attrib_table;
63 static inline ID3DXMeshImpl *impl_from_ID3DXMesh(ID3DXMesh *iface)
65 return CONTAINING_RECORD(iface, ID3DXMeshImpl, ID3DXMesh_iface);
68 static HRESULT WINAPI ID3DXMeshImpl_QueryInterface(ID3DXMesh *iface, REFIID riid, LPVOID *object)
70 ID3DXMeshImpl *This = impl_from_ID3DXMesh(iface);
72 TRACE("(%p)->(%s, %p)\n", This, debugstr_guid(riid), object);
74 if (IsEqualGUID(riid, &IID_IUnknown) ||
75 IsEqualGUID(riid, &IID_ID3DXBaseMesh) ||
76 IsEqualGUID(riid, &IID_ID3DXMesh))
78 iface->lpVtbl->AddRef(iface);
83 WARN("Interface %s not found.\n", debugstr_guid(riid));
88 static ULONG WINAPI ID3DXMeshImpl_AddRef(ID3DXMesh *iface)
90 ID3DXMeshImpl *This = impl_from_ID3DXMesh(iface);
92 TRACE("(%p)->(): AddRef from %d\n", This, This->ref);
94 return InterlockedIncrement(&This->ref);
97 static ULONG WINAPI ID3DXMeshImpl_Release(ID3DXMesh *iface)
99 ID3DXMeshImpl *This = impl_from_ID3DXMesh(iface);
100 ULONG ref = InterlockedDecrement(&This->ref);
102 TRACE("(%p)->(): Release from %d\n", This, ref + 1);
106 IDirect3DIndexBuffer9_Release(This->index_buffer);
107 IDirect3DVertexBuffer9_Release(This->vertex_buffer);
108 IDirect3DVertexDeclaration9_Release(This->vertex_declaration);
109 IDirect3DDevice9_Release(This->device);
110 HeapFree(GetProcessHeap(), 0, This->attrib_buffer);
111 HeapFree(GetProcessHeap(), 0, This->attrib_table);
112 HeapFree(GetProcessHeap(), 0, This);
118 /*** ID3DXBaseMesh ***/
119 static HRESULT WINAPI ID3DXMeshImpl_DrawSubset(ID3DXMesh *iface, DWORD attrib_id)
121 ID3DXMeshImpl *This = impl_from_ID3DXMesh(iface);
127 TRACE("(%p)->(%u)\n", This, attrib_id);
129 vertex_size = iface->lpVtbl->GetNumBytesPerVertex(iface);
131 hr = IDirect3DDevice9_SetVertexDeclaration(This->device, This->vertex_declaration);
132 if (FAILED(hr)) return hr;
133 hr = IDirect3DDevice9_SetStreamSource(This->device, 0, This->vertex_buffer, 0, vertex_size);
134 if (FAILED(hr)) return hr;
135 hr = IDirect3DDevice9_SetIndices(This->device, This->index_buffer);
136 if (FAILED(hr)) return hr;
138 while (face_end < This->numfaces)
140 for (face_start = face_end; face_start < This->numfaces; face_start++)
142 if (This->attrib_buffer[face_start] == attrib_id)
145 if (face_start >= This->numfaces)
147 for (face_end = face_start + 1; face_end < This->numfaces; face_end++)
149 if (This->attrib_buffer[face_end] != attrib_id)
153 hr = IDirect3DDevice9_DrawIndexedPrimitive(This->device, D3DPT_TRIANGLELIST,
154 0, 0, This->numvertices, face_start * 3, face_end - face_start);
155 if (FAILED(hr)) return hr;
161 static DWORD WINAPI ID3DXMeshImpl_GetNumFaces(ID3DXMesh *iface)
163 ID3DXMeshImpl *This = impl_from_ID3DXMesh(iface);
165 TRACE("(%p)\n", This);
167 return This->numfaces;
170 static DWORD WINAPI ID3DXMeshImpl_GetNumVertices(ID3DXMesh *iface)
172 ID3DXMeshImpl *This = impl_from_ID3DXMesh(iface);
174 TRACE("(%p)\n", This);
176 return This->numvertices;
179 static DWORD WINAPI ID3DXMeshImpl_GetFVF(ID3DXMesh *iface)
181 ID3DXMeshImpl *This = impl_from_ID3DXMesh(iface);
183 TRACE("(%p)\n", This);
188 static HRESULT WINAPI ID3DXMeshImpl_GetDeclaration(ID3DXMesh *iface, D3DVERTEXELEMENT9 declaration[MAX_FVF_DECL_SIZE])
190 ID3DXMeshImpl *This = impl_from_ID3DXMesh(iface);
193 TRACE("(%p)\n", This);
195 if (declaration == NULL) return D3DERR_INVALIDCALL;
197 return IDirect3DVertexDeclaration9_GetDeclaration(This->vertex_declaration,
202 static DWORD WINAPI ID3DXMeshImpl_GetNumBytesPerVertex(ID3DXMesh *iface)
204 ID3DXMeshImpl *This = impl_from_ID3DXMesh(iface);
206 D3DVERTEXELEMENT9 declaration[MAX_FVF_DECL_SIZE] = { D3DDECL_END() };
208 TRACE("iface (%p)\n", This);
210 IDirect3DVertexDeclaration9_GetDeclaration(This->vertex_declaration,
213 return D3DXGetDeclVertexSize(declaration, 0);
216 static DWORD WINAPI ID3DXMeshImpl_GetOptions(ID3DXMesh *iface)
218 ID3DXMeshImpl *This = impl_from_ID3DXMesh(iface);
220 TRACE("(%p)\n", This);
222 return This->options;
225 static HRESULT WINAPI ID3DXMeshImpl_GetDevice(ID3DXMesh *iface, LPDIRECT3DDEVICE9 *device)
227 ID3DXMeshImpl *This = impl_from_ID3DXMesh(iface);
229 TRACE("(%p)->(%p)\n", This, device);
231 if (device == NULL) return D3DERR_INVALIDCALL;
232 *device = This->device;
233 IDirect3DDevice9_AddRef(This->device);
238 static HRESULT WINAPI ID3DXMeshImpl_CloneMeshFVF(ID3DXMesh *iface, DWORD options, DWORD fvf, LPDIRECT3DDEVICE9 device, LPD3DXMESH *clone_mesh)
240 ID3DXMeshImpl *This = impl_from_ID3DXMesh(iface);
242 D3DVERTEXELEMENT9 declaration[MAX_FVF_DECL_SIZE];
244 TRACE("(%p)->(%x,%x,%p,%p)\n", This, options, fvf, device, clone_mesh);
246 hr = D3DXDeclaratorFromFVF(fvf, declaration);
247 if (FAILED(hr)) return hr;
249 return iface->lpVtbl->CloneMesh(iface, options, declaration, device, clone_mesh);
252 static HRESULT WINAPI ID3DXMeshImpl_CloneMesh(ID3DXMesh *iface, DWORD options, CONST D3DVERTEXELEMENT9 *declaration, LPDIRECT3DDEVICE9 device,
253 LPD3DXMESH *clone_mesh_out)
255 ID3DXMeshImpl *This = impl_from_ID3DXMesh(iface);
256 ID3DXMeshImpl *cloned_this;
257 ID3DXMesh *clone_mesh;
258 D3DVERTEXELEMENT9 orig_declaration[MAX_FVF_DECL_SIZE] = { D3DDECL_END() };
259 void *data_in, *data_out;
264 TRACE("(%p)->(%x,%p,%p,%p)\n", This, options, declaration, device, clone_mesh_out);
267 return D3DERR_INVALIDCALL;
269 hr = iface->lpVtbl->GetDeclaration(iface, orig_declaration);
270 if (FAILED(hr)) return hr;
272 for (i = 0; orig_declaration[i].Stream != 0xff; i++) {
273 if (memcmp(&orig_declaration[i], &declaration[i], sizeof(*declaration)))
275 FIXME("Vertex buffer conversion not implemented.\n");
280 hr = D3DXCreateMesh(This->numfaces, This->numvertices, options & ~D3DXMESH_VB_SHARE,
281 declaration, device, &clone_mesh);
282 if (FAILED(hr)) return hr;
284 cloned_this = impl_from_ID3DXMesh(clone_mesh);
285 vertex_size = clone_mesh->lpVtbl->GetNumBytesPerVertex(clone_mesh);
287 if (options & D3DXMESH_VB_SHARE) {
288 IDirect3DVertexBuffer9_AddRef(This->vertex_buffer);
289 /* FIXME: refactor to avoid creating a new vertex buffer */
290 IDirect3DVertexBuffer9_Release(cloned_this->vertex_buffer);
291 cloned_this->vertex_buffer = This->vertex_buffer;
293 hr = iface->lpVtbl->LockVertexBuffer(iface, D3DLOCK_READONLY, &data_in);
294 if (FAILED(hr)) goto error;
295 hr = clone_mesh->lpVtbl->LockVertexBuffer(clone_mesh, D3DLOCK_DISCARD, &data_out);
297 iface->lpVtbl->UnlockVertexBuffer(iface);
300 memcpy(data_out, data_in, This->numvertices * vertex_size);
301 clone_mesh->lpVtbl->UnlockVertexBuffer(clone_mesh);
302 iface->lpVtbl->UnlockVertexBuffer(iface);
305 hr = iface->lpVtbl->LockIndexBuffer(iface, D3DLOCK_READONLY, &data_in);
306 if (FAILED(hr)) goto error;
307 hr = clone_mesh->lpVtbl->LockIndexBuffer(clone_mesh, D3DLOCK_DISCARD, &data_out);
309 iface->lpVtbl->UnlockIndexBuffer(iface);
312 if ((options ^ This->options) & D3DXMESH_32BIT) {
313 if (options & D3DXMESH_32BIT) {
314 for (i = 0; i < This->numfaces * 3; i++)
315 ((DWORD*)data_out)[i] = ((WORD*)data_in)[i];
317 for (i = 0; i < This->numfaces * 3; i++)
318 ((WORD*)data_out)[i] = ((DWORD*)data_in)[i];
321 memcpy(data_out, data_in, This->numfaces * 3 * (options & D3DXMESH_32BIT ? 4 : 2));
323 clone_mesh->lpVtbl->UnlockIndexBuffer(clone_mesh);
324 iface->lpVtbl->UnlockIndexBuffer(iface);
326 memcpy(cloned_this->attrib_buffer, This->attrib_buffer, This->numfaces * sizeof(*This->attrib_buffer));
328 if (This->attrib_table_size)
330 cloned_this->attrib_table_size = This->attrib_table_size;
331 cloned_this->attrib_table = HeapAlloc(GetProcessHeap(), 0, This->attrib_table_size * sizeof(*This->attrib_table));
332 if (!cloned_this->attrib_table) {
336 memcpy(cloned_this->attrib_table, This->attrib_table, This->attrib_table_size * sizeof(*This->attrib_table));
339 *clone_mesh_out = clone_mesh;
343 IUnknown_Release(clone_mesh);
347 static HRESULT WINAPI ID3DXMeshImpl_GetVertexBuffer(ID3DXMesh *iface, LPDIRECT3DVERTEXBUFFER9 *vertex_buffer)
349 ID3DXMeshImpl *This = impl_from_ID3DXMesh(iface);
351 TRACE("(%p)->(%p)\n", This, vertex_buffer);
353 if (vertex_buffer == NULL) return D3DERR_INVALIDCALL;
354 *vertex_buffer = This->vertex_buffer;
355 IDirect3DVertexBuffer9_AddRef(This->vertex_buffer);
360 static HRESULT WINAPI ID3DXMeshImpl_GetIndexBuffer(ID3DXMesh *iface, LPDIRECT3DINDEXBUFFER9 *index_buffer)
362 ID3DXMeshImpl *This = impl_from_ID3DXMesh(iface);
364 TRACE("(%p)->(%p)\n", This, index_buffer);
366 if (index_buffer == NULL) return D3DERR_INVALIDCALL;
367 *index_buffer = This->index_buffer;
368 IDirect3DIndexBuffer9_AddRef(This->index_buffer);
373 static HRESULT WINAPI ID3DXMeshImpl_LockVertexBuffer(ID3DXMesh *iface, DWORD flags, LPVOID *data)
375 ID3DXMeshImpl *This = impl_from_ID3DXMesh(iface);
377 TRACE("(%p)->(%u,%p)\n", This, flags, data);
379 return IDirect3DVertexBuffer9_Lock(This->vertex_buffer, 0, 0, data, flags);
382 static HRESULT WINAPI ID3DXMeshImpl_UnlockVertexBuffer(ID3DXMesh *iface)
384 ID3DXMeshImpl *This = impl_from_ID3DXMesh(iface);
386 TRACE("(%p)\n", This);
388 return IDirect3DVertexBuffer9_Unlock(This->vertex_buffer);
391 static HRESULT WINAPI ID3DXMeshImpl_LockIndexBuffer(ID3DXMesh *iface, DWORD flags, LPVOID *data)
393 ID3DXMeshImpl *This = impl_from_ID3DXMesh(iface);
395 TRACE("(%p)->(%u,%p)\n", This, flags, data);
397 return IDirect3DIndexBuffer9_Lock(This->index_buffer, 0, 0, data, flags);
400 static HRESULT WINAPI ID3DXMeshImpl_UnlockIndexBuffer(ID3DXMesh *iface)
402 ID3DXMeshImpl *This = impl_from_ID3DXMesh(iface);
404 TRACE("(%p)\n", This);
406 return IDirect3DIndexBuffer9_Unlock(This->index_buffer);
409 static HRESULT WINAPI ID3DXMeshImpl_GetAttributeTable(ID3DXMesh *iface, D3DXATTRIBUTERANGE *attrib_table, DWORD *attrib_table_size)
411 ID3DXMeshImpl *This = impl_from_ID3DXMesh(iface);
413 TRACE("(%p)->(%p,%p)\n", This, attrib_table, attrib_table_size);
415 if (attrib_table_size)
416 *attrib_table_size = This->attrib_table_size;
419 CopyMemory(attrib_table, This->attrib_table, This->attrib_table_size * sizeof(*attrib_table));
424 static HRESULT WINAPI ID3DXMeshImpl_ConvertPointRepsToAdjacency(ID3DXMesh *iface, CONST DWORD *point_reps, DWORD *adjacency)
426 ID3DXMeshImpl *This = impl_from_ID3DXMesh(iface);
428 FIXME("(%p)->(%p,%p): stub\n", This, point_reps, adjacency);
433 static HRESULT WINAPI ID3DXMeshImpl_ConvertAdjacencyToPointReps(ID3DXMesh *iface, CONST DWORD *adjacency, DWORD *point_reps)
435 ID3DXMeshImpl *This = impl_from_ID3DXMesh(iface);
437 FIXME("(%p)->(%p,%p): stub\n", This, adjacency, point_reps);
442 struct vertex_metadata {
445 DWORD first_shared_index;
448 static int compare_vertex_keys(const void *a, const void *b)
450 const struct vertex_metadata *left = a;
451 const struct vertex_metadata *right = b;
452 if (left->key == right->key)
454 return left->key < right->key ? -1 : 1;
457 static HRESULT WINAPI ID3DXMeshImpl_GenerateAdjacency(ID3DXMesh *iface, FLOAT epsilon, DWORD *adjacency)
459 ID3DXMeshImpl *This = impl_from_ID3DXMesh(iface);
461 BYTE *vertices = NULL;
462 const DWORD *indices = NULL;
465 /* sort the vertices by (x + y + z) to quickly find coincident vertices */
466 struct vertex_metadata *sorted_vertices;
467 /* shared_indices links together identical indices in the index buffer so
468 * that adjacency checks can be limited to faces sharing a vertex */
469 DWORD *shared_indices = NULL;
470 const FLOAT epsilon_sq = epsilon * epsilon;
473 TRACE("(%p)->(%f,%p)\n", This, epsilon, adjacency);
476 return D3DERR_INVALIDCALL;
478 buffer_size = This->numfaces * 3 * sizeof(*shared_indices) + This->numvertices * sizeof(*sorted_vertices);
479 if (!(This->options & D3DXMESH_32BIT))
480 buffer_size += This->numfaces * 3 * sizeof(*indices);
481 shared_indices = HeapAlloc(GetProcessHeap(), 0, buffer_size);
483 return E_OUTOFMEMORY;
484 sorted_vertices = (struct vertex_metadata*)(shared_indices + This->numfaces * 3);
486 hr = iface->lpVtbl->LockVertexBuffer(iface, D3DLOCK_READONLY, (void**)&vertices);
487 if (FAILED(hr)) goto cleanup;
488 hr = iface->lpVtbl->LockIndexBuffer(iface, D3DLOCK_READONLY, (void**)&indices);
489 if (FAILED(hr)) goto cleanup;
491 if (!(This->options & D3DXMESH_32BIT)) {
492 const WORD *word_indices = (const WORD*)indices;
493 DWORD *dword_indices = (DWORD*)(sorted_vertices + This->numvertices);
494 indices = dword_indices;
495 for (i = 0; i < This->numfaces * 3; i++)
496 *dword_indices++ = *word_indices++;
499 vertex_size = iface->lpVtbl->GetNumBytesPerVertex(iface);
500 for (i = 0; i < This->numvertices; i++) {
501 D3DXVECTOR3 *vertex = (D3DXVECTOR3*)(vertices + vertex_size * i);
502 sorted_vertices[i].first_shared_index = -1;
503 sorted_vertices[i].key = vertex->x + vertex->y + vertex->z;
504 sorted_vertices[i].vertex_index = i;
506 for (i = 0; i < This->numfaces * 3; i++) {
507 DWORD *first_shared_index = &sorted_vertices[indices[i]].first_shared_index;
508 shared_indices[i] = *first_shared_index;
509 *first_shared_index = i;
512 qsort(sorted_vertices, This->numvertices, sizeof(*sorted_vertices), compare_vertex_keys);
514 for (i = 0; i < This->numvertices; i++) {
515 struct vertex_metadata *sorted_vertex_a = &sorted_vertices[i];
516 D3DXVECTOR3 *vertex_a = (D3DXVECTOR3*)(vertices + sorted_vertex_a->vertex_index * vertex_size);
517 DWORD shared_index_a = sorted_vertex_a->first_shared_index;
519 while (shared_index_a != -1) {
521 DWORD shared_index_b = shared_indices[shared_index_a];
522 struct vertex_metadata *sorted_vertex_b = sorted_vertex_a;
525 while (shared_index_b != -1) {
526 /* faces are adjacent if they have another coincident vertex */
527 DWORD base_a = (shared_index_a / 3) * 3;
528 DWORD base_b = (shared_index_b / 3) * 3;
529 BOOL adjacent = FALSE;
532 for (k = 0; k < 3; k++) {
533 if (adjacency[base_b + k] == shared_index_a / 3) {
539 for (k = 1; k <= 2; k++) {
540 DWORD vertex_index_a = base_a + (shared_index_a + k) % 3;
541 DWORD vertex_index_b = base_b + (shared_index_b + (3 - k)) % 3;
542 adjacent = indices[vertex_index_a] == indices[vertex_index_b];
543 if (!adjacent && epsilon >= 0.0f) {
544 D3DXVECTOR3 delta = {0.0f, 0.0f, 0.0f};
547 D3DXVec3Subtract(&delta,
548 (D3DXVECTOR3*)(vertices + indices[vertex_index_a] * vertex_size),
549 (D3DXVECTOR3*)(vertices + indices[vertex_index_b] * vertex_size));
550 length_sq = D3DXVec3LengthSq(&delta);
551 adjacent = epsilon == 0.0f ? length_sq == 0.0f : length_sq < epsilon_sq;
554 DWORD adj_a = base_a + 2 - (vertex_index_a + shared_index_a + 1) % 3;
555 DWORD adj_b = base_b + 2 - (vertex_index_b + shared_index_b + 1) % 3;
556 if (adjacency[adj_a] == -1 && adjacency[adj_b] == -1) {
557 adjacency[adj_a] = base_b / 3;
558 adjacency[adj_b] = base_a / 3;
565 shared_index_b = shared_indices[shared_index_b];
567 while (++j < This->numvertices) {
568 D3DXVECTOR3 *vertex_b;
571 if (sorted_vertex_b->key - sorted_vertex_a->key > epsilon * 3.0f) {
572 /* no more coincident vertices to try */
573 j = This->numvertices;
576 /* check for coincidence */
577 vertex_b = (D3DXVECTOR3*)(vertices + sorted_vertex_b->vertex_index * vertex_size);
578 if (fabsf(vertex_a->x - vertex_b->x) <= epsilon &&
579 fabsf(vertex_a->y - vertex_b->y) <= epsilon &&
580 fabsf(vertex_a->z - vertex_b->z) <= epsilon)
585 if (j >= This->numvertices)
587 shared_index_b = sorted_vertex_b->first_shared_index;
590 sorted_vertex_a->first_shared_index = shared_indices[sorted_vertex_a->first_shared_index];
591 shared_index_a = sorted_vertex_a->first_shared_index;
597 if (indices) iface->lpVtbl->UnlockIndexBuffer(iface);
598 if (vertices) iface->lpVtbl->UnlockVertexBuffer(iface);
599 HeapFree(GetProcessHeap(), 0, shared_indices);
603 static HRESULT WINAPI ID3DXMeshImpl_UpdateSemantics(ID3DXMesh *iface, D3DVERTEXELEMENT9 declaration[MAX_FVF_DECL_SIZE])
605 ID3DXMeshImpl *This = impl_from_ID3DXMesh(iface);
607 FIXME("(%p)->(%p): stub\n", This, declaration);
613 static HRESULT WINAPI ID3DXMeshImpl_LockAttributeBuffer(ID3DXMesh *iface, DWORD flags, DWORD **data)
615 ID3DXMeshImpl *This = impl_from_ID3DXMesh(iface);
617 TRACE("(%p)->(%u,%p)\n", This, flags, data);
619 InterlockedIncrement(&This->attrib_buffer_lock_count);
621 if (!(flags & D3DLOCK_READONLY)) {
622 D3DXATTRIBUTERANGE *attrib_table = This->attrib_table;
623 This->attrib_table_size = 0;
624 This->attrib_table = NULL;
625 HeapFree(GetProcessHeap(), 0, attrib_table);
628 *data = This->attrib_buffer;
633 static HRESULT WINAPI ID3DXMeshImpl_UnlockAttributeBuffer(ID3DXMesh *iface)
635 ID3DXMeshImpl *This = impl_from_ID3DXMesh(iface);
638 TRACE("(%p)\n", This);
640 lock_count = InterlockedDecrement(&This->attrib_buffer_lock_count);
642 if (lock_count < 0) {
643 InterlockedIncrement(&This->attrib_buffer_lock_count);
644 return D3DERR_INVALIDCALL;
650 static HRESULT WINAPI ID3DXMeshImpl_Optimize(ID3DXMesh *iface, DWORD flags, CONST DWORD *adjacency_in, DWORD *adjacency_out,
651 DWORD *face_remap, LPD3DXBUFFER *vertex_remap, LPD3DXMESH *opt_mesh)
653 ID3DXMeshImpl *This = impl_from_ID3DXMesh(iface);
655 D3DVERTEXELEMENT9 declaration[MAX_FVF_DECL_SIZE] = { D3DDECL_END() };
656 ID3DXMesh *optimized_mesh;
658 TRACE("(%p)->(%x,%p,%p,%p,%p,%p)\n", This, flags, adjacency_in, adjacency_out, face_remap, vertex_remap, opt_mesh);
661 return D3DERR_INVALIDCALL;
663 hr = iface->lpVtbl->GetDeclaration(iface, declaration);
664 if (FAILED(hr)) return hr;
666 hr = iface->lpVtbl->CloneMesh(iface, This->options, declaration, This->device, &optimized_mesh);
667 if (FAILED(hr)) return hr;
669 hr = optimized_mesh->lpVtbl->OptimizeInplace(optimized_mesh, flags, adjacency_in, adjacency_out, face_remap, vertex_remap);
671 *opt_mesh = optimized_mesh;
673 IUnknown_Release(optimized_mesh);
677 /* Creates a vertex_remap that removes unused vertices.
678 * Indices are updated according to the vertex_remap. */
679 static HRESULT compact_mesh(ID3DXMeshImpl *This, DWORD *indices, DWORD *new_num_vertices, ID3DXBuffer **vertex_remap)
682 DWORD *vertex_remap_ptr;
683 DWORD num_used_vertices;
686 hr = D3DXCreateBuffer(This->numvertices * sizeof(DWORD), vertex_remap);
687 if (FAILED(hr)) return hr;
688 vertex_remap_ptr = ID3DXBuffer_GetBufferPointer(*vertex_remap);
690 for (i = 0; i < This->numfaces * 3; i++)
691 vertex_remap_ptr[indices[i]] = 1;
693 /* create old->new vertex mapping */
694 num_used_vertices = 0;
695 for (i = 0; i < This->numvertices; i++) {
696 if (vertex_remap_ptr[i])
697 vertex_remap_ptr[i] = num_used_vertices++;
699 vertex_remap_ptr[i] = -1;
701 /* convert indices */
702 for (i = 0; i < This->numfaces * 3; i++)
703 indices[i] = vertex_remap_ptr[indices[i]];
705 /* create new->old vertex mapping */
706 num_used_vertices = 0;
707 for (i = 0; i < This->numvertices; i++) {
708 if (vertex_remap_ptr[i] != -1)
709 vertex_remap_ptr[num_used_vertices++] = i;
711 for (i = num_used_vertices; i < This->numvertices; i++)
712 vertex_remap_ptr[i] = -1;
714 *new_num_vertices = num_used_vertices;
719 /* count the number of unique attribute values in a sorted attribute buffer */
720 static DWORD count_attributes(const DWORD *attrib_buffer, DWORD numfaces)
722 DWORD last_attribute = attrib_buffer[0];
723 DWORD attrib_table_size = 1;
725 for (i = 1; i < numfaces; i++) {
726 if (attrib_buffer[i] != last_attribute) {
727 last_attribute = attrib_buffer[i];
731 return attrib_table_size;
734 static void fill_attribute_table(DWORD *attrib_buffer, DWORD numfaces, void *indices,
735 BOOL is_32bit_indices, D3DXATTRIBUTERANGE *attrib_table)
737 DWORD attrib_table_size = 0;
738 DWORD last_attribute = attrib_buffer[0];
739 DWORD min_vertex, max_vertex;
742 attrib_table[0].AttribId = last_attribute;
743 attrib_table[0].FaceStart = 0;
744 min_vertex = (DWORD)-1;
746 for (i = 0; i < numfaces; i++) {
749 if (attrib_buffer[i] != last_attribute) {
750 last_attribute = attrib_buffer[i];
751 attrib_table[attrib_table_size].FaceCount = i - attrib_table[attrib_table_size].FaceStart;
752 attrib_table[attrib_table_size].VertexStart = min_vertex;
753 attrib_table[attrib_table_size].VertexCount = max_vertex - min_vertex + 1;
755 attrib_table[attrib_table_size].AttribId = attrib_buffer[i];
756 attrib_table[attrib_table_size].FaceStart = i;
757 min_vertex = (DWORD)-1;
760 for (j = 0; j < 3; j++) {
761 DWORD vertex_index = is_32bit_indices ? ((DWORD*)indices)[i * 3 + j] : ((WORD*)indices)[i * 3 + j];
762 if (vertex_index < min_vertex)
763 min_vertex = vertex_index;
764 if (vertex_index > max_vertex)
765 max_vertex = vertex_index;
768 attrib_table[attrib_table_size].FaceCount = i - attrib_table[attrib_table_size].FaceStart;
769 attrib_table[attrib_table_size].VertexStart = min_vertex;
770 attrib_table[attrib_table_size].VertexCount = max_vertex - min_vertex + 1;
774 static int attrib_entry_compare(const DWORD **a, const DWORD **b)
776 const DWORD *ptr_a = *a;
777 const DWORD *ptr_b = *b;
778 int delta = *ptr_a - *ptr_b;
783 delta = ptr_a - ptr_b; /* for stable sort */
787 /* Create face_remap, a new attribute buffer for attribute sort optimization. */
788 static HRESULT remap_faces_for_attrsort(ID3DXMeshImpl *This, const DWORD *indices,
789 const DWORD *attrib_buffer, DWORD **sorted_attrib_buffer, DWORD **face_remap)
791 const DWORD **sorted_attrib_ptr_buffer = NULL;
794 *face_remap = HeapAlloc(GetProcessHeap(), 0, This->numfaces * sizeof(**face_remap));
795 sorted_attrib_ptr_buffer = HeapAlloc(GetProcessHeap(), 0, This->numfaces * sizeof(*sorted_attrib_ptr_buffer));
796 if (!*face_remap || !sorted_attrib_ptr_buffer) {
797 HeapFree(GetProcessHeap(), 0, sorted_attrib_ptr_buffer);
798 return E_OUTOFMEMORY;
800 for (i = 0; i < This->numfaces; i++)
801 sorted_attrib_ptr_buffer[i] = &attrib_buffer[i];
802 qsort(sorted_attrib_ptr_buffer, This->numfaces, sizeof(*sorted_attrib_ptr_buffer),
803 (int(*)(const void *, const void *))attrib_entry_compare);
805 for (i = 0; i < This->numfaces; i++)
807 DWORD old_face = sorted_attrib_ptr_buffer[i] - attrib_buffer;
808 (*face_remap)[old_face] = i;
811 /* overwrite sorted_attrib_ptr_buffer with the values themselves */
812 *sorted_attrib_buffer = (DWORD*)sorted_attrib_ptr_buffer;
813 for (i = 0; i < This->numfaces; i++)
814 (*sorted_attrib_buffer)[(*face_remap)[i]] = attrib_buffer[i];
819 static HRESULT WINAPI ID3DXMeshImpl_OptimizeInplace(ID3DXMesh *iface, DWORD flags, CONST DWORD *adjacency_in, DWORD *adjacency_out,
820 DWORD *face_remap_out, LPD3DXBUFFER *vertex_remap_out)
822 ID3DXMeshImpl *This = impl_from_ID3DXMesh(iface);
823 void *indices = NULL;
824 DWORD *attrib_buffer = NULL;
826 ID3DXBuffer *vertex_remap = NULL;
827 DWORD *face_remap = NULL; /* old -> new mapping */
828 DWORD *dword_indices = NULL;
829 DWORD new_num_vertices = 0;
830 DWORD new_num_alloc_vertices = 0;
831 IDirect3DVertexBuffer9 *vertex_buffer = NULL;
832 DWORD *sorted_attrib_buffer = NULL;
835 TRACE("(%p)->(%x,%p,%p,%p,%p)\n", This, flags, adjacency_in, adjacency_out, face_remap_out, vertex_remap_out);
838 return D3DERR_INVALIDCALL;
839 if (!adjacency_in && (flags & (D3DXMESHOPT_VERTEXCACHE | D3DXMESHOPT_STRIPREORDER)))
840 return D3DERR_INVALIDCALL;
841 if ((flags & (D3DXMESHOPT_VERTEXCACHE | D3DXMESHOPT_STRIPREORDER)) == (D3DXMESHOPT_VERTEXCACHE | D3DXMESHOPT_STRIPREORDER))
842 return D3DERR_INVALIDCALL;
844 if (flags & (D3DXMESHOPT_VERTEXCACHE | D3DXMESHOPT_STRIPREORDER))
846 if (flags & D3DXMESHOPT_VERTEXCACHE)
847 FIXME("D3DXMESHOPT_VERTEXCACHE not implemented.\n");
848 if (flags & D3DXMESHOPT_STRIPREORDER)
849 FIXME("D3DXMESHOPT_STRIPREORDER not implemented.\n");
853 hr = iface->lpVtbl->LockIndexBuffer(iface, 0, (void**)&indices);
854 if (FAILED(hr)) goto cleanup;
856 dword_indices = HeapAlloc(GetProcessHeap(), 0, This->numfaces * 3 * sizeof(DWORD));
857 if (!dword_indices) return E_OUTOFMEMORY;
858 if (This->options & D3DXMESH_32BIT) {
859 memcpy(dword_indices, indices, This->numfaces * 3 * sizeof(DWORD));
861 WORD *word_indices = indices;
862 for (i = 0; i < This->numfaces * 3; i++)
863 dword_indices[i] = *word_indices++;
866 if ((flags & (D3DXMESHOPT_COMPACT | D3DXMESHOPT_IGNOREVERTS | D3DXMESHOPT_ATTRSORT)) == D3DXMESHOPT_COMPACT)
868 new_num_alloc_vertices = This->numvertices;
869 hr = compact_mesh(This, dword_indices, &new_num_vertices, &vertex_remap);
870 if (FAILED(hr)) goto cleanup;
871 } else if (flags & D3DXMESHOPT_ATTRSORT) {
872 if (!(flags & D3DXMESHOPT_IGNOREVERTS))
874 FIXME("D3DXMESHOPT_ATTRSORT vertex reordering not implemented.\n");
879 hr = iface->lpVtbl->LockAttributeBuffer(iface, 0, &attrib_buffer);
880 if (FAILED(hr)) goto cleanup;
882 hr = remap_faces_for_attrsort(This, dword_indices, attrib_buffer, &sorted_attrib_buffer, &face_remap);
883 if (FAILED(hr)) goto cleanup;
888 /* reorder the vertices using vertex_remap */
889 D3DVERTEXBUFFER_DESC vertex_desc;
890 DWORD *vertex_remap_ptr = ID3DXBuffer_GetBufferPointer(vertex_remap);
891 DWORD vertex_size = iface->lpVtbl->GetNumBytesPerVertex(iface);
895 hr = IDirect3DVertexBuffer9_GetDesc(This->vertex_buffer, &vertex_desc);
896 if (FAILED(hr)) goto cleanup;
898 hr = IDirect3DDevice9_CreateVertexBuffer(This->device, new_num_alloc_vertices * vertex_size,
899 vertex_desc.Usage, This->fvf, vertex_desc.Pool, &vertex_buffer, NULL);
900 if (FAILED(hr)) goto cleanup;
902 hr = IDirect3DVertexBuffer9_Lock(This->vertex_buffer, 0, 0, (void**)&orig_vertices, D3DLOCK_READONLY);
903 if (FAILED(hr)) goto cleanup;
905 hr = IDirect3DVertexBuffer9_Lock(vertex_buffer, 0, 0, (void**)&new_vertices, D3DLOCK_DISCARD);
907 IDirect3DVertexBuffer9_Unlock(This->vertex_buffer);
911 for (i = 0; i < new_num_vertices; i++)
912 memcpy(new_vertices + i * vertex_size, orig_vertices + vertex_remap_ptr[i] * vertex_size, vertex_size);
914 IDirect3DVertexBuffer9_Unlock(This->vertex_buffer);
915 IDirect3DVertexBuffer9_Unlock(vertex_buffer);
916 } else if (vertex_remap_out) {
917 DWORD *vertex_remap_ptr;
919 hr = D3DXCreateBuffer(This->numvertices * sizeof(DWORD), &vertex_remap);
920 if (FAILED(hr)) goto cleanup;
921 vertex_remap_ptr = ID3DXBuffer_GetBufferPointer(vertex_remap);
922 for (i = 0; i < This->numvertices; i++)
923 *vertex_remap_ptr++ = i;
926 if (flags & D3DXMESHOPT_ATTRSORT)
928 D3DXATTRIBUTERANGE *attrib_table;
929 DWORD attrib_table_size;
931 attrib_table_size = count_attributes(sorted_attrib_buffer, This->numfaces);
932 attrib_table = HeapAlloc(GetProcessHeap(), 0, attrib_table_size * sizeof(*attrib_table));
938 memcpy(attrib_buffer, sorted_attrib_buffer, This->numfaces * sizeof(*attrib_buffer));
940 /* reorder the indices using face_remap */
941 if (This->options & D3DXMESH_32BIT) {
942 for (i = 0; i < This->numfaces; i++)
943 memcpy((DWORD*)indices + face_remap[i] * 3, dword_indices + i * 3, 3 * sizeof(DWORD));
945 WORD *word_indices = indices;
946 for (i = 0; i < This->numfaces; i++) {
947 DWORD new_pos = face_remap[i] * 3;
948 DWORD old_pos = i * 3;
949 word_indices[new_pos++] = dword_indices[old_pos++];
950 word_indices[new_pos++] = dword_indices[old_pos++];
951 word_indices[new_pos] = dword_indices[old_pos];
955 fill_attribute_table(attrib_buffer, This->numfaces, indices,
956 This->options & D3DXMESH_32BIT, attrib_table);
958 HeapFree(GetProcessHeap(), 0, This->attrib_table);
959 This->attrib_table = attrib_table;
960 This->attrib_table_size = attrib_table_size;
962 if (This->options & D3DXMESH_32BIT) {
963 memcpy(indices, dword_indices, This->numfaces * 3 * sizeof(DWORD));
965 WORD *word_indices = indices;
966 for (i = 0; i < This->numfaces * 3; i++)
967 *word_indices++ = dword_indices[i];
973 for (i = 0; i < This->numfaces; i++) {
974 DWORD old_pos = i * 3;
975 DWORD new_pos = face_remap[i] * 3;
976 adjacency_out[new_pos++] = face_remap[adjacency_in[old_pos++]];
977 adjacency_out[new_pos++] = face_remap[adjacency_in[old_pos++]];
978 adjacency_out[new_pos++] = face_remap[adjacency_in[old_pos++]];
981 memcpy(adjacency_out, adjacency_in, This->numfaces * 3 * sizeof(*adjacency_out));
984 if (face_remap_out) {
986 for (i = 0; i < This->numfaces; i++)
987 face_remap_out[face_remap[i]] = i;
989 for (i = 0; i < This->numfaces; i++)
990 face_remap_out[i] = i;
993 if (vertex_remap_out)
994 *vertex_remap_out = vertex_remap;
998 IDirect3DVertexBuffer9_Release(This->vertex_buffer);
999 This->vertex_buffer = vertex_buffer;
1000 vertex_buffer = NULL;
1001 This->numvertices = new_num_vertices;
1006 HeapFree(GetProcessHeap(), 0, sorted_attrib_buffer);
1007 HeapFree(GetProcessHeap(), 0, face_remap);
1008 HeapFree(GetProcessHeap(), 0, dword_indices);
1009 if (vertex_remap) ID3DXBuffer_Release(vertex_remap);
1010 if (vertex_buffer) IDirect3DVertexBuffer9_Release(vertex_buffer);
1011 if (attrib_buffer) iface->lpVtbl->UnlockAttributeBuffer(iface);
1012 if (indices) iface->lpVtbl->UnlockIndexBuffer(iface);
1016 static HRESULT WINAPI ID3DXMeshImpl_SetAttributeTable(ID3DXMesh *iface, CONST D3DXATTRIBUTERANGE *attrib_table, DWORD attrib_table_size)
1018 ID3DXMeshImpl *This = impl_from_ID3DXMesh(iface);
1019 D3DXATTRIBUTERANGE *new_table = NULL;
1021 TRACE("(%p)->(%p,%u)\n", This, attrib_table, attrib_table_size);
1023 if (attrib_table_size) {
1024 size_t size = attrib_table_size * sizeof(*attrib_table);
1026 new_table = HeapAlloc(GetProcessHeap(), 0, size);
1028 return E_OUTOFMEMORY;
1030 CopyMemory(new_table, attrib_table, size);
1031 } else if (attrib_table) {
1032 return D3DERR_INVALIDCALL;
1034 HeapFree(GetProcessHeap(), 0, This->attrib_table);
1035 This->attrib_table = new_table;
1036 This->attrib_table_size = attrib_table_size;
1041 static const struct ID3DXMeshVtbl D3DXMesh_Vtbl =
1043 /*** IUnknown methods ***/
1044 ID3DXMeshImpl_QueryInterface,
1045 ID3DXMeshImpl_AddRef,
1046 ID3DXMeshImpl_Release,
1047 /*** ID3DXBaseMesh ***/
1048 ID3DXMeshImpl_DrawSubset,
1049 ID3DXMeshImpl_GetNumFaces,
1050 ID3DXMeshImpl_GetNumVertices,
1051 ID3DXMeshImpl_GetFVF,
1052 ID3DXMeshImpl_GetDeclaration,
1053 ID3DXMeshImpl_GetNumBytesPerVertex,
1054 ID3DXMeshImpl_GetOptions,
1055 ID3DXMeshImpl_GetDevice,
1056 ID3DXMeshImpl_CloneMeshFVF,
1057 ID3DXMeshImpl_CloneMesh,
1058 ID3DXMeshImpl_GetVertexBuffer,
1059 ID3DXMeshImpl_GetIndexBuffer,
1060 ID3DXMeshImpl_LockVertexBuffer,
1061 ID3DXMeshImpl_UnlockVertexBuffer,
1062 ID3DXMeshImpl_LockIndexBuffer,
1063 ID3DXMeshImpl_UnlockIndexBuffer,
1064 ID3DXMeshImpl_GetAttributeTable,
1065 ID3DXMeshImpl_ConvertPointRepsToAdjacency,
1066 ID3DXMeshImpl_ConvertAdjacencyToPointReps,
1067 ID3DXMeshImpl_GenerateAdjacency,
1068 ID3DXMeshImpl_UpdateSemantics,
1070 ID3DXMeshImpl_LockAttributeBuffer,
1071 ID3DXMeshImpl_UnlockAttributeBuffer,
1072 ID3DXMeshImpl_Optimize,
1073 ID3DXMeshImpl_OptimizeInplace,
1074 ID3DXMeshImpl_SetAttributeTable
1077 /*************************************************************************
1080 BOOL WINAPI D3DXBoxBoundProbe(CONST D3DXVECTOR3 *pmin, CONST D3DXVECTOR3 *pmax, CONST D3DXVECTOR3 *prayposition, CONST D3DXVECTOR3 *praydirection)
1082 /* Algorithm taken from the article: An Efficient and Robust Ray-Box Intersection Algoritm
1083 Amy Williams University of Utah
1084 Steve Barrus University of Utah
1085 R. Keith Morley University of Utah
1086 Peter Shirley University of Utah
1088 International Conference on Computer Graphics and Interactive Techniques archive
1089 ACM SIGGRAPH 2005 Courses
1090 Los Angeles, California
1092 This algorithm is free of patents or of copyrights, as confirmed by Peter Shirley himself.
1094 Algorithm: Consider the box as the intersection of three slabs. Clip the ray
1095 against each slab, if there's anything left of the ray after we're
1096 done we've got an intersection of the ray with the box.
1100 FLOAT div, tmin, tmax, tymin, tymax, tzmin, tzmax;
1102 div = 1.0f / praydirection->x;
1105 tmin = ( pmin->x - prayposition->x ) * div;
1106 tmax = ( pmax->x - prayposition->x ) * div;
1110 tmin = ( pmax->x - prayposition->x ) * div;
1111 tmax = ( pmin->x - prayposition->x ) * div;
1114 if ( tmax < 0.0f ) return FALSE;
1116 div = 1.0f / praydirection->y;
1119 tymin = ( pmin->y - prayposition->y ) * div;
1120 tymax = ( pmax->y - prayposition->y ) * div;
1124 tymin = ( pmax->y - prayposition->y ) * div;
1125 tymax = ( pmin->y - prayposition->y ) * div;
1128 if ( ( tymax < 0.0f ) || ( tmin > tymax ) || ( tymin > tmax ) ) return FALSE;
1130 if ( tymin > tmin ) tmin = tymin;
1131 if ( tymax < tmax ) tmax = tymax;
1133 div = 1.0f / praydirection->z;
1136 tzmin = ( pmin->z - prayposition->z ) * div;
1137 tzmax = ( pmax->z - prayposition->z ) * div;
1141 tzmin = ( pmax->z - prayposition->z ) * div;
1142 tzmax = ( pmin->z - prayposition->z ) * div;
1145 if ( (tzmax < 0.0f ) || ( tmin > tzmax ) || ( tzmin > tmax ) ) return FALSE;
1150 /*************************************************************************
1151 * D3DXComputeBoundingBox
1153 HRESULT WINAPI D3DXComputeBoundingBox(CONST D3DXVECTOR3 *pfirstposition, DWORD numvertices, DWORD dwstride, D3DXVECTOR3 *pmin, D3DXVECTOR3 *pmax)
1158 if( !pfirstposition || !pmin || !pmax ) return D3DERR_INVALIDCALL;
1160 *pmin = *pfirstposition;
1163 for(i=0; i<numvertices; i++)
1165 vec = *( (const D3DXVECTOR3*)((const char*)pfirstposition + dwstride * i) );
1167 if ( vec.x < pmin->x ) pmin->x = vec.x;
1168 if ( vec.x > pmax->x ) pmax->x = vec.x;
1170 if ( vec.y < pmin->y ) pmin->y = vec.y;
1171 if ( vec.y > pmax->y ) pmax->y = vec.y;
1173 if ( vec.z < pmin->z ) pmin->z = vec.z;
1174 if ( vec.z > pmax->z ) pmax->z = vec.z;
1180 /*************************************************************************
1181 * D3DXComputeBoundingSphere
1183 HRESULT WINAPI D3DXComputeBoundingSphere(CONST D3DXVECTOR3* pfirstposition, DWORD numvertices, DWORD dwstride, D3DXVECTOR3 *pcenter, FLOAT *pradius)
1185 D3DXVECTOR3 temp, temp1;
1189 if( !pfirstposition || !pcenter || !pradius ) return D3DERR_INVALIDCALL;
1197 for(i=0; i<numvertices; i++)
1199 D3DXVec3Add(&temp1, &temp, (const D3DXVECTOR3*)((const char*)pfirstposition + dwstride * i));
1203 D3DXVec3Scale(pcenter, &temp, 1.0f/((FLOAT)numvertices));
1205 for(i=0; i<numvertices; i++)
1207 d = D3DXVec3Length(D3DXVec3Subtract(&temp, (const D3DXVECTOR3*)((const char*)pfirstposition + dwstride * i), pcenter));
1208 if ( d > *pradius ) *pradius = d;
1213 static const UINT d3dx_decltype_size[D3DDECLTYPE_UNUSED] =
1215 /* D3DDECLTYPE_FLOAT1 */ 1 * 4,
1216 /* D3DDECLTYPE_FLOAT2 */ 2 * 4,
1217 /* D3DDECLTYPE_FLOAT3 */ 3 * 4,
1218 /* D3DDECLTYPE_FLOAT4 */ 4 * 4,
1219 /* D3DDECLTYPE_D3DCOLOR */ 4 * 1,
1220 /* D3DDECLTYPE_UBYTE4 */ 4 * 1,
1221 /* D3DDECLTYPE_SHORT2 */ 2 * 2,
1222 /* D3DDECLTYPE_SHORT4 */ 4 * 2,
1223 /* D3DDECLTYPE_UBYTE4N */ 4 * 1,
1224 /* D3DDECLTYPE_SHORT2N */ 2 * 2,
1225 /* D3DDECLTYPE_SHORT4N */ 4 * 2,
1226 /* D3DDECLTYPE_USHORT2N */ 2 * 2,
1227 /* D3DDECLTYPE_USHORT4N */ 4 * 2,
1228 /* D3DDECLTYPE_UDEC3 */ 4, /* 3 * 10 bits + 2 padding */
1229 /* D3DDECLTYPE_DEC3N */ 4,
1230 /* D3DDECLTYPE_FLOAT16_2 */ 2 * 2,
1231 /* D3DDECLTYPE_FLOAT16_4 */ 4 * 2,
1234 static void append_decl_element(D3DVERTEXELEMENT9 *declaration, UINT *idx, UINT *offset,
1235 D3DDECLTYPE type, D3DDECLUSAGE usage, UINT usage_idx)
1237 declaration[*idx].Stream = 0;
1238 declaration[*idx].Offset = *offset;
1239 declaration[*idx].Type = type;
1240 declaration[*idx].Method = D3DDECLMETHOD_DEFAULT;
1241 declaration[*idx].Usage = usage;
1242 declaration[*idx].UsageIndex = usage_idx;
1244 *offset += d3dx_decltype_size[type];
1248 /*************************************************************************
1249 * D3DXDeclaratorFromFVF
1251 HRESULT WINAPI D3DXDeclaratorFromFVF(DWORD fvf, D3DVERTEXELEMENT9 declaration[MAX_FVF_DECL_SIZE])
1253 static const D3DVERTEXELEMENT9 end_element = D3DDECL_END();
1254 DWORD tex_count = (fvf & D3DFVF_TEXCOUNT_MASK) >> D3DFVF_TEXCOUNT_SHIFT;
1255 unsigned int offset = 0;
1256 unsigned int idx = 0;
1259 TRACE("fvf %#x, declaration %p.\n", fvf, declaration);
1261 if (fvf & (D3DFVF_RESERVED0 | D3DFVF_RESERVED2)) return D3DERR_INVALIDCALL;
1263 if (fvf & D3DFVF_POSITION_MASK)
1265 BOOL has_blend = (fvf & D3DFVF_XYZB5) >= D3DFVF_XYZB1;
1266 DWORD blend_count = 1 + (((fvf & D3DFVF_XYZB5) - D3DFVF_XYZB1) >> 1);
1267 BOOL has_blend_idx = (fvf & D3DFVF_LASTBETA_D3DCOLOR) || (fvf & D3DFVF_LASTBETA_UBYTE4);
1269 if (has_blend_idx) --blend_count;
1271 if ((fvf & D3DFVF_POSITION_MASK) == D3DFVF_XYZW
1272 || (has_blend && blend_count > 4))
1273 return D3DERR_INVALIDCALL;
1275 if ((fvf & D3DFVF_POSITION_MASK) == D3DFVF_XYZRHW)
1276 append_decl_element(declaration, &idx, &offset, D3DDECLTYPE_FLOAT4, D3DDECLUSAGE_POSITIONT, 0);
1278 append_decl_element(declaration, &idx, &offset, D3DDECLTYPE_FLOAT3, D3DDECLUSAGE_POSITION, 0);
1282 switch (blend_count)
1287 append_decl_element(declaration, &idx, &offset, D3DDECLTYPE_FLOAT1, D3DDECLUSAGE_BLENDWEIGHT, 0);
1290 append_decl_element(declaration, &idx, &offset, D3DDECLTYPE_FLOAT2, D3DDECLUSAGE_BLENDWEIGHT, 0);
1293 append_decl_element(declaration, &idx, &offset, D3DDECLTYPE_FLOAT3, D3DDECLUSAGE_BLENDWEIGHT, 0);
1296 append_decl_element(declaration, &idx, &offset, D3DDECLTYPE_FLOAT4, D3DDECLUSAGE_BLENDWEIGHT, 0);
1299 ERR("Invalid blend count %u.\n", blend_count);
1305 if (fvf & D3DFVF_LASTBETA_UBYTE4)
1306 append_decl_element(declaration, &idx, &offset, D3DDECLTYPE_UBYTE4, D3DDECLUSAGE_BLENDINDICES, 0);
1307 else if (fvf & D3DFVF_LASTBETA_D3DCOLOR)
1308 append_decl_element(declaration, &idx, &offset, D3DDECLTYPE_D3DCOLOR, D3DDECLUSAGE_BLENDINDICES, 0);
1313 if (fvf & D3DFVF_NORMAL)
1314 append_decl_element(declaration, &idx, &offset, D3DDECLTYPE_FLOAT3, D3DDECLUSAGE_NORMAL, 0);
1315 if (fvf & D3DFVF_PSIZE)
1316 append_decl_element(declaration, &idx, &offset, D3DDECLTYPE_FLOAT1, D3DDECLUSAGE_PSIZE, 0);
1317 if (fvf & D3DFVF_DIFFUSE)
1318 append_decl_element(declaration, &idx, &offset, D3DDECLTYPE_D3DCOLOR, D3DDECLUSAGE_COLOR, 0);
1319 if (fvf & D3DFVF_SPECULAR)
1320 append_decl_element(declaration, &idx, &offset, D3DDECLTYPE_D3DCOLOR, D3DDECLUSAGE_COLOR, 1);
1322 for (i = 0; i < tex_count; ++i)
1324 switch ((fvf >> (16 + 2 * i)) & 0x03)
1326 case D3DFVF_TEXTUREFORMAT1:
1327 append_decl_element(declaration, &idx, &offset, D3DDECLTYPE_FLOAT1, D3DDECLUSAGE_TEXCOORD, i);
1329 case D3DFVF_TEXTUREFORMAT2:
1330 append_decl_element(declaration, &idx, &offset, D3DDECLTYPE_FLOAT2, D3DDECLUSAGE_TEXCOORD, i);
1332 case D3DFVF_TEXTUREFORMAT3:
1333 append_decl_element(declaration, &idx, &offset, D3DDECLTYPE_FLOAT3, D3DDECLUSAGE_TEXCOORD, i);
1335 case D3DFVF_TEXTUREFORMAT4:
1336 append_decl_element(declaration, &idx, &offset, D3DDECLTYPE_FLOAT4, D3DDECLUSAGE_TEXCOORD, i);
1341 declaration[idx] = end_element;
1346 /*************************************************************************
1347 * D3DXFVFFromDeclarator
1349 HRESULT WINAPI D3DXFVFFromDeclarator(const D3DVERTEXELEMENT9 *declaration, DWORD *fvf)
1351 unsigned int i = 0, texture, offset;
1353 TRACE("(%p, %p)\n", declaration, fvf);
1356 if (declaration[0].Type == D3DDECLTYPE_FLOAT3 && declaration[0].Usage == D3DDECLUSAGE_POSITION)
1358 if ((declaration[1].Type == D3DDECLTYPE_FLOAT4 && declaration[1].Usage == D3DDECLUSAGE_BLENDWEIGHT &&
1359 declaration[1].UsageIndex == 0) &&
1360 (declaration[2].Type == D3DDECLTYPE_FLOAT1 && declaration[2].Usage == D3DDECLUSAGE_BLENDINDICES &&
1361 declaration[2].UsageIndex == 0))
1363 return D3DERR_INVALIDCALL;
1365 else if ((declaration[1].Type == D3DDECLTYPE_UBYTE4 || declaration[1].Type == D3DDECLTYPE_D3DCOLOR) &&
1366 declaration[1].Usage == D3DDECLUSAGE_BLENDINDICES && declaration[1].UsageIndex == 0)
1368 if (declaration[1].Type == D3DDECLTYPE_UBYTE4)
1370 *fvf |= D3DFVF_XYZB1 | D3DFVF_LASTBETA_UBYTE4;
1374 *fvf |= D3DFVF_XYZB1 | D3DFVF_LASTBETA_D3DCOLOR;
1378 else if (declaration[1].Type <= D3DDECLTYPE_FLOAT4 && declaration[1].Usage == D3DDECLUSAGE_BLENDWEIGHT &&
1379 declaration[1].UsageIndex == 0)
1381 if ((declaration[2].Type == D3DDECLTYPE_UBYTE4 || declaration[2].Type == D3DDECLTYPE_D3DCOLOR) &&
1382 declaration[2].Usage == D3DDECLUSAGE_BLENDINDICES && declaration[2].UsageIndex == 0)
1384 if (declaration[2].Type == D3DDECLTYPE_UBYTE4)
1386 *fvf |= D3DFVF_LASTBETA_UBYTE4;
1390 *fvf |= D3DFVF_LASTBETA_D3DCOLOR;
1392 switch (declaration[1].Type)
1394 case D3DDECLTYPE_FLOAT1: *fvf |= D3DFVF_XYZB2; break;
1395 case D3DDECLTYPE_FLOAT2: *fvf |= D3DFVF_XYZB3; break;
1396 case D3DDECLTYPE_FLOAT3: *fvf |= D3DFVF_XYZB4; break;
1397 case D3DDECLTYPE_FLOAT4: *fvf |= D3DFVF_XYZB5; break;
1403 switch (declaration[1].Type)
1405 case D3DDECLTYPE_FLOAT1: *fvf |= D3DFVF_XYZB1; break;
1406 case D3DDECLTYPE_FLOAT2: *fvf |= D3DFVF_XYZB2; break;
1407 case D3DDECLTYPE_FLOAT3: *fvf |= D3DFVF_XYZB3; break;
1408 case D3DDECLTYPE_FLOAT4: *fvf |= D3DFVF_XYZB4; break;
1419 else if (declaration[0].Type == D3DDECLTYPE_FLOAT4 && declaration[0].Usage == D3DDECLUSAGE_POSITIONT &&
1420 declaration[0].UsageIndex == 0)
1422 *fvf |= D3DFVF_XYZRHW;
1426 if (declaration[i].Type == D3DDECLTYPE_FLOAT3 && declaration[i].Usage == D3DDECLUSAGE_NORMAL)
1428 *fvf |= D3DFVF_NORMAL;
1431 if (declaration[i].Type == D3DDECLTYPE_FLOAT1 && declaration[i].Usage == D3DDECLUSAGE_PSIZE &&
1432 declaration[i].UsageIndex == 0)
1434 *fvf |= D3DFVF_PSIZE;
1437 if (declaration[i].Type == D3DDECLTYPE_D3DCOLOR && declaration[i].Usage == D3DDECLUSAGE_COLOR &&
1438 declaration[i].UsageIndex == 0)
1440 *fvf |= D3DFVF_DIFFUSE;
1443 if (declaration[i].Type == D3DDECLTYPE_D3DCOLOR && declaration[i].Usage == D3DDECLUSAGE_COLOR &&
1444 declaration[i].UsageIndex == 1)
1446 *fvf |= D3DFVF_SPECULAR;
1450 for (texture = 0; texture < D3DDP_MAXTEXCOORD; i++, texture++)
1452 if (declaration[i].Stream == 0xFF)
1456 else if (declaration[i].Type == D3DDECLTYPE_FLOAT1 && declaration[i].Usage == D3DDECLUSAGE_TEXCOORD &&
1457 declaration[i].UsageIndex == texture)
1459 *fvf |= D3DFVF_TEXCOORDSIZE1(declaration[i].UsageIndex);
1461 else if (declaration[i].Type == D3DDECLTYPE_FLOAT2 && declaration[i].Usage == D3DDECLUSAGE_TEXCOORD &&
1462 declaration[i].UsageIndex == texture)
1464 *fvf |= D3DFVF_TEXCOORDSIZE2(declaration[i].UsageIndex);
1466 else if (declaration[i].Type == D3DDECLTYPE_FLOAT3 && declaration[i].Usage == D3DDECLUSAGE_TEXCOORD &&
1467 declaration[i].UsageIndex == texture)
1469 *fvf |= D3DFVF_TEXCOORDSIZE3(declaration[i].UsageIndex);
1471 else if (declaration[i].Type == D3DDECLTYPE_FLOAT4 && declaration[i].Usage == D3DDECLUSAGE_TEXCOORD &&
1472 declaration[i].UsageIndex == texture)
1474 *fvf |= D3DFVF_TEXCOORDSIZE4(declaration[i].UsageIndex);
1478 return D3DERR_INVALIDCALL;
1482 *fvf |= (texture << D3DFVF_TEXCOUNT_SHIFT);
1484 for (offset = 0, i = 0; declaration[i].Stream != 0xFF;
1485 offset += d3dx_decltype_size[declaration[i].Type], i++)
1487 if (declaration[i].Offset != offset)
1489 return D3DERR_INVALIDCALL;
1496 /*************************************************************************
1497 * D3DXGetFVFVertexSize
1499 static UINT Get_TexCoord_Size_From_FVF(DWORD FVF, int tex_num)
1501 return (((((FVF) >> (16 + (2 * (tex_num)))) + 1) & 0x03) + 1);
1504 UINT WINAPI D3DXGetFVFVertexSize(DWORD FVF)
1508 UINT numTextures = (FVF & D3DFVF_TEXCOUNT_MASK) >> D3DFVF_TEXCOUNT_SHIFT;
1510 if (FVF & D3DFVF_NORMAL) size += sizeof(D3DXVECTOR3);
1511 if (FVF & D3DFVF_DIFFUSE) size += sizeof(DWORD);
1512 if (FVF & D3DFVF_SPECULAR) size += sizeof(DWORD);
1513 if (FVF & D3DFVF_PSIZE) size += sizeof(DWORD);
1515 switch (FVF & D3DFVF_POSITION_MASK)
1517 case D3DFVF_XYZ: size += sizeof(D3DXVECTOR3); break;
1518 case D3DFVF_XYZRHW: size += 4 * sizeof(FLOAT); break;
1519 case D3DFVF_XYZB1: size += 4 * sizeof(FLOAT); break;
1520 case D3DFVF_XYZB2: size += 5 * sizeof(FLOAT); break;
1521 case D3DFVF_XYZB3: size += 6 * sizeof(FLOAT); break;
1522 case D3DFVF_XYZB4: size += 7 * sizeof(FLOAT); break;
1523 case D3DFVF_XYZB5: size += 8 * sizeof(FLOAT); break;
1524 case D3DFVF_XYZW: size += 4 * sizeof(FLOAT); break;
1527 for (i = 0; i < numTextures; i++)
1529 size += Get_TexCoord_Size_From_FVF(FVF, i) * sizeof(FLOAT);
1535 /*************************************************************************
1536 * D3DXGetDeclVertexSize
1538 UINT WINAPI D3DXGetDeclVertexSize(const D3DVERTEXELEMENT9 *decl, DWORD stream_idx)
1540 const D3DVERTEXELEMENT9 *element;
1543 TRACE("decl %p, stream_idx %u\n", decl, stream_idx);
1545 if (!decl) return 0;
1547 for (element = decl; element->Stream != 0xff; ++element)
1551 if (element->Stream != stream_idx) continue;
1553 if (element->Type >= sizeof(d3dx_decltype_size) / sizeof(*d3dx_decltype_size))
1555 FIXME("Unhandled element type %#x, size will be incorrect.\n", element->Type);
1559 type_size = d3dx_decltype_size[element->Type];
1560 if (element->Offset + type_size > size) size = element->Offset + type_size;
1566 /*************************************************************************
1569 UINT WINAPI D3DXGetDeclLength(const D3DVERTEXELEMENT9 *decl)
1571 const D3DVERTEXELEMENT9 *element;
1573 TRACE("decl %p\n", decl);
1575 /* null decl results in exception on Windows XP */
1577 for (element = decl; element->Stream != 0xff; ++element);
1579 return element - decl;
1582 /*************************************************************************
1585 BOOL WINAPI D3DXIntersectTri(CONST D3DXVECTOR3 *p0, CONST D3DXVECTOR3 *p1, CONST D3DXVECTOR3 *p2, CONST D3DXVECTOR3 *praypos, CONST D3DXVECTOR3 *praydir, FLOAT *pu, FLOAT *pv, FLOAT *pdist)
1590 m.u.m[0][0] = p1->x - p0->x;
1591 m.u.m[1][0] = p2->x - p0->x;
1592 m.u.m[2][0] = -praydir->x;
1594 m.u.m[0][1] = p1->y - p0->z;
1595 m.u.m[1][1] = p2->y - p0->z;
1596 m.u.m[2][1] = -praydir->y;
1598 m.u.m[0][2] = p1->z - p0->z;
1599 m.u.m[1][2] = p2->z - p0->z;
1600 m.u.m[2][2] = -praydir->z;
1607 vec.x = praypos->x - p0->x;
1608 vec.y = praypos->y - p0->y;
1609 vec.z = praypos->z - p0->z;
1612 if ( D3DXMatrixInverse(&m, NULL, &m) )
1614 D3DXVec4Transform(&vec, &vec, &m);
1615 if ( (vec.x >= 0.0f) && (vec.y >= 0.0f) && (vec.x + vec.y <= 1.0f) && (vec.z >= 0.0f) )
1619 *pdist = fabs( vec.z );
1627 /*************************************************************************
1628 * D3DXSphereBoundProbe
1630 BOOL WINAPI D3DXSphereBoundProbe(CONST D3DXVECTOR3 *pcenter, FLOAT radius, CONST D3DXVECTOR3 *prayposition, CONST D3DXVECTOR3 *praydirection)
1632 D3DXVECTOR3 difference;
1635 a = D3DXVec3LengthSq(praydirection);
1636 if (!D3DXVec3Subtract(&difference, prayposition, pcenter)) return FALSE;
1637 b = D3DXVec3Dot(&difference, praydirection);
1638 c = D3DXVec3LengthSq(&difference) - radius * radius;
1641 if ( ( d <= 0.0f ) || ( sqrt(d) <= b ) ) return FALSE;
1645 /*************************************************************************
1648 HRESULT WINAPI D3DXCreateMesh(DWORD numfaces, DWORD numvertices, DWORD options, CONST D3DVERTEXELEMENT9 *declaration,
1649 LPDIRECT3DDEVICE9 device, LPD3DXMESH *mesh)
1653 IDirect3DVertexDeclaration9 *vertex_declaration;
1654 IDirect3DVertexBuffer9 *vertex_buffer;
1655 IDirect3DIndexBuffer9 *index_buffer;
1656 DWORD *attrib_buffer;
1657 ID3DXMeshImpl *object;
1658 DWORD index_usage = 0;
1659 D3DPOOL index_pool = D3DPOOL_DEFAULT;
1660 D3DFORMAT index_format = D3DFMT_INDEX16;
1661 DWORD vertex_usage = 0;
1662 D3DPOOL vertex_pool = D3DPOOL_DEFAULT;
1665 TRACE("(%d, %d, %x, %p, %p, %p)\n", numfaces, numvertices, options, declaration, device, mesh);
1667 if (numfaces == 0 || numvertices == 0 || declaration == NULL || device == NULL || mesh == NULL ||
1668 /* D3DXMESH_VB_SHARE is for cloning, and D3DXMESH_USEHWONLY is for ConvertToBlendedMesh */
1669 (options & (D3DXMESH_VB_SHARE | D3DXMESH_USEHWONLY | 0xfffe0000)))
1671 return D3DERR_INVALIDCALL;
1673 for (i = 0; declaration[i].Stream != 0xff; i++)
1674 if (declaration[i].Stream != 0)
1675 return D3DERR_INVALIDCALL;
1677 if (options & D3DXMESH_32BIT)
1678 index_format = D3DFMT_INDEX32;
1680 if (options & D3DXMESH_DONOTCLIP) {
1681 index_usage |= D3DUSAGE_DONOTCLIP;
1682 vertex_usage |= D3DUSAGE_DONOTCLIP;
1684 if (options & D3DXMESH_POINTS) {
1685 index_usage |= D3DUSAGE_POINTS;
1686 vertex_usage |= D3DUSAGE_POINTS;
1688 if (options & D3DXMESH_RTPATCHES) {
1689 index_usage |= D3DUSAGE_RTPATCHES;
1690 vertex_usage |= D3DUSAGE_RTPATCHES;
1692 if (options & D3DXMESH_NPATCHES) {
1693 index_usage |= D3DUSAGE_NPATCHES;
1694 vertex_usage |= D3DUSAGE_NPATCHES;
1697 if (options & D3DXMESH_VB_SYSTEMMEM)
1698 vertex_pool = D3DPOOL_SYSTEMMEM;
1699 else if (options & D3DXMESH_VB_MANAGED)
1700 vertex_pool = D3DPOOL_MANAGED;
1702 if (options & D3DXMESH_VB_WRITEONLY)
1703 vertex_usage |= D3DUSAGE_WRITEONLY;
1704 if (options & D3DXMESH_VB_DYNAMIC)
1705 vertex_usage |= D3DUSAGE_DYNAMIC;
1706 if (options & D3DXMESH_VB_SOFTWAREPROCESSING)
1707 vertex_usage |= D3DUSAGE_SOFTWAREPROCESSING;
1709 if (options & D3DXMESH_IB_SYSTEMMEM)
1710 index_pool = D3DPOOL_SYSTEMMEM;
1711 else if (options & D3DXMESH_IB_MANAGED)
1712 index_pool = D3DPOOL_MANAGED;
1714 if (options & D3DXMESH_IB_WRITEONLY)
1715 index_usage |= D3DUSAGE_WRITEONLY;
1716 if (options & D3DXMESH_IB_DYNAMIC)
1717 index_usage |= D3DUSAGE_DYNAMIC;
1718 if (options & D3DXMESH_IB_SOFTWAREPROCESSING)
1719 index_usage |= D3DUSAGE_SOFTWAREPROCESSING;
1721 hr = D3DXFVFFromDeclarator(declaration, &fvf);
1727 /* Create vertex declaration */
1728 hr = IDirect3DDevice9_CreateVertexDeclaration(device,
1730 &vertex_declaration);
1733 WARN("Unexpected return value %x from IDirect3DDevice9_CreateVertexDeclaration.\n",hr);
1737 /* Create vertex buffer */
1738 hr = IDirect3DDevice9_CreateVertexBuffer(device,
1739 numvertices * D3DXGetDeclVertexSize(declaration, declaration[0].Stream),
1747 WARN("Unexpected return value %x from IDirect3DDevice9_CreateVertexBuffer.\n",hr);
1748 IDirect3DVertexDeclaration9_Release(vertex_declaration);
1752 /* Create index buffer */
1753 hr = IDirect3DDevice9_CreateIndexBuffer(device,
1754 numfaces * 3 * ((index_format == D3DFMT_INDEX16) ? 2 : 4),
1762 WARN("Unexpected return value %x from IDirect3DDevice9_CreateVertexBuffer.\n",hr);
1763 IDirect3DVertexBuffer9_Release(vertex_buffer);
1764 IDirect3DVertexDeclaration9_Release(vertex_declaration);
1768 attrib_buffer = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, numfaces * sizeof(*attrib_buffer));
1769 object = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(ID3DXMeshImpl));
1770 if (object == NULL || attrib_buffer == NULL)
1772 HeapFree(GetProcessHeap(), 0, attrib_buffer);
1773 IDirect3DIndexBuffer9_Release(index_buffer);
1774 IDirect3DVertexBuffer9_Release(vertex_buffer);
1775 IDirect3DVertexDeclaration9_Release(vertex_declaration);
1777 return E_OUTOFMEMORY;
1779 object->ID3DXMesh_iface.lpVtbl = &D3DXMesh_Vtbl;
1782 object->numfaces = numfaces;
1783 object->numvertices = numvertices;
1784 object->options = options;
1786 object->device = device;
1787 IDirect3DDevice9_AddRef(device);
1789 object->vertex_declaration = vertex_declaration;
1790 object->vertex_buffer = vertex_buffer;
1791 object->index_buffer = index_buffer;
1792 object->attrib_buffer = attrib_buffer;
1794 *mesh = &object->ID3DXMesh_iface;
1799 /*************************************************************************
1802 HRESULT WINAPI D3DXCreateMeshFVF(DWORD numfaces, DWORD numvertices, DWORD options, DWORD fvf,
1803 LPDIRECT3DDEVICE9 device, LPD3DXMESH *mesh)
1806 D3DVERTEXELEMENT9 declaration[MAX_FVF_DECL_SIZE];
1808 TRACE("(%u, %u, %u, %u, %p, %p)\n", numfaces, numvertices, options, fvf, device, mesh);
1810 hr = D3DXDeclaratorFromFVF(fvf, declaration);
1811 if (FAILED(hr)) return hr;
1813 return D3DXCreateMesh(numfaces, numvertices, options, declaration, device, mesh);
1819 DWORD num_poly_faces;
1820 DWORD num_tri_faces;
1821 D3DXVECTOR3 *vertices;
1822 DWORD *num_tri_per_face;
1827 /* optional mesh data */
1830 D3DXVECTOR3 *normals;
1831 DWORD *normal_indices;
1833 DWORD num_materials;
1834 D3DXMATERIAL *materials;
1835 DWORD *material_indices;
1838 static HRESULT get_next_child(IDirectXFileData *filedata, IDirectXFileData **child, const GUID **type)
1841 IDirectXFileDataReference *child_ref = NULL;
1842 IDirectXFileObject *child_obj = NULL;
1843 IDirectXFileData *child_data = NULL;
1845 hr = IDirectXFileData_GetNextObject(filedata, &child_obj);
1846 if (FAILED(hr)) return hr;
1848 hr = IDirectXFileObject_QueryInterface(child_obj, &IID_IDirectXFileDataReference, (void**)&child_ref);
1849 if (SUCCEEDED(hr)) {
1850 hr = IDirectXFileDataReference_Resolve(child_ref, &child_data);
1851 IDirectXFileDataReference_Release(child_ref);
1853 hr = IDirectXFileObject_QueryInterface(child_obj, &IID_IDirectXFileData, (void**)&child_data);
1855 IDirectXFileObject_Release(child_obj);
1859 hr = IDirectXFileData_GetType(child_data, type);
1861 IDirectXFileData_Release(child_data);
1863 *child = child_data;
1869 static HRESULT parse_texture_filename(IDirectXFileData *filedata, LPSTR *filename_out)
1875 char *filename = NULL;
1877 /* template TextureFilename {
1882 HeapFree(GetProcessHeap(), 0, *filename_out);
1883 *filename_out = NULL;
1885 hr = IDirectXFileData_GetData(filedata, NULL, &data_size, (void**)&data);
1886 if (FAILED(hr)) return hr;
1888 if (data_size < sizeof(LPSTR)) {
1889 WARN("truncated data (%u bytes)\n", data_size);
1892 filename_in = *(LPSTR*)data;
1894 filename = HeapAlloc(GetProcessHeap(), 0, strlen(filename_in) + 1);
1895 if (!filename) return E_OUTOFMEMORY;
1897 strcpy(filename, filename_in);
1898 *filename_out = filename;
1903 static HRESULT parse_material(IDirectXFileData *filedata, D3DXMATERIAL *material)
1909 IDirectXFileData *child;
1911 material->pTextureFilename = NULL;
1913 hr = IDirectXFileData_GetData(filedata, NULL, &data_size, (void**)&data);
1914 if (FAILED(hr)) return hr;
1917 * template ColorRGBA {
1923 * template ColorRGB {
1928 * template Material {
1929 * ColorRGBA faceColor;
1931 * ColorRGB specularColor;
1932 * ColorRGB emissiveColor;
1936 if (data_size != sizeof(FLOAT) * 11) {
1937 WARN("incorrect data size (%u bytes)\n", data_size);
1941 memcpy(&material->MatD3D.Diffuse, data, sizeof(D3DCOLORVALUE));
1942 data += sizeof(D3DCOLORVALUE);
1943 material->MatD3D.Power = *(FLOAT*)data;
1944 data += sizeof(FLOAT);
1945 memcpy(&material->MatD3D.Specular, data, sizeof(FLOAT) * 3);
1946 material->MatD3D.Specular.a = 1.0f;
1947 data += 3 * sizeof(FLOAT);
1948 memcpy(&material->MatD3D.Emissive, data, sizeof(FLOAT) * 3);
1949 material->MatD3D.Emissive.a = 1.0f;
1950 material->MatD3D.Ambient.r = 0.0f;
1951 material->MatD3D.Ambient.g = 0.0f;
1952 material->MatD3D.Ambient.b = 0.0f;
1953 material->MatD3D.Ambient.a = 1.0f;
1955 while (SUCCEEDED(hr = get_next_child(filedata, &child, &type)))
1957 if (IsEqualGUID(type, &TID_D3DRMTextureFilename)) {
1958 hr = parse_texture_filename(child, &material->pTextureFilename);
1959 if (FAILED(hr)) break;
1962 return hr == DXFILEERR_NOMOREOBJECTS ? D3D_OK : hr;
1965 static void destroy_materials(struct mesh_data *mesh)
1968 for (i = 0; i < mesh->num_materials; i++)
1969 HeapFree(GetProcessHeap(), 0, mesh->materials[i].pTextureFilename);
1970 HeapFree(GetProcessHeap(), 0, mesh->materials);
1971 HeapFree(GetProcessHeap(), 0, mesh->material_indices);
1972 mesh->num_materials = 0;
1973 mesh->materials = NULL;
1974 mesh->material_indices = NULL;
1977 static HRESULT parse_material_list(IDirectXFileData *filedata, struct mesh_data *mesh)
1981 DWORD *data, *in_ptr;
1983 IDirectXFileData *child;
1984 DWORD num_materials;
1987 destroy_materials(mesh);
1989 hr = IDirectXFileData_GetData(filedata, NULL, &data_size, (void**)&data);
1990 if (FAILED(hr)) return hr;
1992 /* template MeshMaterialList {
1994 * DWORD nFaceIndexes;
1995 * array DWORD faceIndexes[nFaceIndexes];
2002 if (data_size < sizeof(DWORD))
2003 goto truncated_data_error;
2004 num_materials = *in_ptr++;
2008 if (data_size < 2 * sizeof(DWORD))
2009 goto truncated_data_error;
2010 if (*in_ptr++ != mesh->num_poly_faces) {
2011 WARN("number of material face indices (%u) doesn't match number of faces (%u)\n",
2012 *(in_ptr - 1), mesh->num_poly_faces);
2015 if (data_size < 2 * sizeof(DWORD) + mesh->num_poly_faces * sizeof(DWORD))
2016 goto truncated_data_error;
2017 for (i = 0; i < mesh->num_poly_faces; i++) {
2018 if (*in_ptr++ >= num_materials) {
2019 WARN("face %u: reference to undefined material %u (only %u materials)\n",
2020 i, *(in_ptr - 1), num_materials);
2025 mesh->materials = HeapAlloc(GetProcessHeap(), 0, num_materials * sizeof(*mesh->materials));
2026 mesh->material_indices = HeapAlloc(GetProcessHeap(), 0, mesh->num_poly_faces * sizeof(*mesh->material_indices));
2027 if (!mesh->materials || !mesh->material_indices)
2028 return E_OUTOFMEMORY;
2029 memcpy(mesh->material_indices, data + 2, mesh->num_poly_faces * sizeof(DWORD));
2031 while (SUCCEEDED(hr = get_next_child(filedata, &child, &type)))
2033 if (IsEqualGUID(type, &TID_D3DRMMaterial)) {
2034 if (mesh->num_materials >= num_materials) {
2035 WARN("more materials defined than declared\n");
2038 hr = parse_material(child, &mesh->materials[mesh->num_materials++]);
2039 if (FAILED(hr)) break;
2042 if (hr != DXFILEERR_NOMOREOBJECTS)
2044 if (num_materials != mesh->num_materials) {
2045 WARN("only %u of %u materials defined\n", num_materials, mesh->num_materials);
2050 truncated_data_error:
2051 WARN("truncated data (%u bytes)\n", data_size);
2055 static HRESULT parse_normals(IDirectXFileData *filedata, struct mesh_data *mesh)
2060 DWORD *index_out_ptr;
2062 DWORD num_face_indices = mesh->num_poly_faces * 2 + mesh->num_tri_faces;
2064 HeapFree(GetProcessHeap(), 0, mesh->normals);
2065 mesh->num_normals = 0;
2066 mesh->normals = NULL;
2067 mesh->normal_indices = NULL;
2068 mesh->fvf |= D3DFVF_NORMAL;
2070 hr = IDirectXFileData_GetData(filedata, NULL, &data_size, (void**)&data);
2071 if (FAILED(hr)) return hr;
2073 /* template Vector {
2078 * template MeshFace {
2079 * DWORD nFaceVertexIndices;
2080 * array DWORD faceVertexIndices[nFaceVertexIndices];
2082 * template MeshNormals {
2084 * array Vector normals[nNormals];
2085 * DWORD nFaceNormals;
2086 * array MeshFace faceNormals[nFaceNormals];
2090 if (data_size < sizeof(DWORD) * 2)
2091 goto truncated_data_error;
2092 mesh->num_normals = *(DWORD*)data;
2093 data += sizeof(DWORD);
2094 if (data_size < sizeof(DWORD) * 2 + mesh->num_normals * sizeof(D3DXVECTOR3) +
2095 num_face_indices * sizeof(DWORD))
2096 goto truncated_data_error;
2098 mesh->normals = HeapAlloc(GetProcessHeap(), 0, mesh->num_normals * sizeof(D3DXVECTOR3));
2099 mesh->normal_indices = HeapAlloc(GetProcessHeap(), 0, num_face_indices * sizeof(DWORD));
2100 if (!mesh->normals || !mesh->normal_indices)
2101 return E_OUTOFMEMORY;
2103 memcpy(mesh->normals, data, mesh->num_normals * sizeof(D3DXVECTOR3));
2104 data += mesh->num_normals * sizeof(D3DXVECTOR3);
2105 for (i = 0; i < mesh->num_normals; i++)
2106 D3DXVec3Normalize(&mesh->normals[i], &mesh->normals[i]);
2108 if (*(DWORD*)data != mesh->num_poly_faces) {
2109 WARN("number of face normals (%u) doesn't match number of faces (%u)\n",
2110 *(DWORD*)data, mesh->num_poly_faces);
2113 data += sizeof(DWORD);
2114 index_out_ptr = mesh->normal_indices;
2115 for (i = 0; i < mesh->num_poly_faces; i++)
2118 DWORD count = *(DWORD*)data;
2119 if (count != mesh->num_tri_per_face[i] + 2) {
2120 WARN("face %u: number of normals (%u) doesn't match number of vertices (%u)\n",
2121 i, count, mesh->num_tri_per_face[i] + 2);
2124 data += sizeof(DWORD);
2126 for (j = 0; j < count; j++) {
2127 DWORD normal_index = *(DWORD*)data;
2128 if (normal_index >= mesh->num_normals) {
2129 WARN("face %u, normal index %u: reference to undefined normal %u (only %u normals)\n",
2130 i, j, normal_index, mesh->num_normals);
2133 *index_out_ptr++ = normal_index;
2134 data += sizeof(DWORD);
2139 truncated_data_error:
2140 WARN("truncated data (%u bytes)\n", data_size);
2144 /* for provide_flags parameters */
2145 #define PROVIDE_MATERIALS 0x1
2146 #define PROVIDE_SKININFO 0x2
2148 static HRESULT parse_mesh(IDirectXFileData *filedata, struct mesh_data *mesh_data, DWORD provide_flags)
2152 BYTE *data, *in_ptr;
2153 DWORD *index_out_ptr;
2155 IDirectXFileData *child;
2161 * array Vector vertices[nVertices];
2163 * array MeshFace faces[nFaces];
2168 hr = IDirectXFileData_GetData(filedata, NULL, &data_size, (void**)&data);
2169 if (FAILED(hr)) return hr;
2172 if (data_size < sizeof(DWORD) * 2)
2173 goto truncated_data_error;
2174 mesh_data->num_vertices = *(DWORD*)in_ptr;
2175 if (data_size < sizeof(DWORD) * 2 + mesh_data->num_vertices * sizeof(D3DXVECTOR3))
2176 goto truncated_data_error;
2177 in_ptr += sizeof(DWORD) + mesh_data->num_vertices * sizeof(D3DXVECTOR3);
2179 mesh_data->num_poly_faces = *(DWORD*)in_ptr;
2180 in_ptr += sizeof(DWORD);
2182 mesh_data->num_tri_faces = 0;
2183 for (i = 0; i < mesh_data->num_poly_faces; i++)
2185 DWORD num_poly_vertices;
2188 if (data_size - (in_ptr - data) < sizeof(DWORD))
2189 goto truncated_data_error;
2190 num_poly_vertices = *(DWORD*)in_ptr;
2191 in_ptr += sizeof(DWORD);
2192 if (data_size - (in_ptr - data) < num_poly_vertices * sizeof(DWORD))
2193 goto truncated_data_error;
2194 if (num_poly_vertices < 3) {
2195 WARN("face %u has only %u vertices\n", i, num_poly_vertices);
2198 for (j = 0; j < num_poly_vertices; j++) {
2199 if (*(DWORD*)in_ptr >= mesh_data->num_vertices) {
2200 WARN("face %u, index %u: undefined vertex %u (only %u vertices)\n",
2201 i, j, *(DWORD*)in_ptr, mesh_data->num_vertices);
2204 in_ptr += sizeof(DWORD);
2206 mesh_data->num_tri_faces += num_poly_vertices - 2;
2209 mesh_data->fvf = D3DFVF_XYZ;
2211 mesh_data->vertices = HeapAlloc(GetProcessHeap(), 0,
2212 mesh_data->num_vertices * sizeof(*mesh_data->vertices));
2213 mesh_data->num_tri_per_face = HeapAlloc(GetProcessHeap(), 0,
2214 mesh_data->num_poly_faces * sizeof(*mesh_data->num_tri_per_face));
2215 mesh_data->indices = HeapAlloc(GetProcessHeap(), 0,
2216 (mesh_data->num_tri_faces + mesh_data->num_poly_faces * 2) * sizeof(*mesh_data->indices));
2217 if (!mesh_data->vertices || !mesh_data->num_tri_per_face || !mesh_data->indices)
2218 return E_OUTOFMEMORY;
2220 in_ptr = data + sizeof(DWORD);
2221 memcpy(mesh_data->vertices, in_ptr, mesh_data->num_vertices * sizeof(D3DXVECTOR3));
2222 in_ptr += mesh_data->num_vertices * sizeof(D3DXVECTOR3) + sizeof(DWORD);
2224 index_out_ptr = mesh_data->indices;
2225 for (i = 0; i < mesh_data->num_poly_faces; i++)
2229 count = *(DWORD*)in_ptr;
2230 in_ptr += sizeof(DWORD);
2231 mesh_data->num_tri_per_face[i] = count - 2;
2234 *index_out_ptr++ = *(DWORD*)in_ptr;
2235 in_ptr += sizeof(DWORD);
2239 while (SUCCEEDED(hr = get_next_child(filedata, &child, &type)))
2241 if (IsEqualGUID(type, &TID_D3DRMMeshNormals)) {
2242 hr = parse_normals(child, mesh_data);
2243 } else if (IsEqualGUID(type, &TID_D3DRMMeshVertexColors)) {
2244 FIXME("Mesh vertex color loading not implemented.\n");
2246 } else if (IsEqualGUID(type, &TID_D3DRMMeshTextureCoords)) {
2247 FIXME("Mesh texture coordinate loading not implemented.\n");
2249 } else if (IsEqualGUID(type, &TID_D3DRMMeshMaterialList) &&
2250 (provide_flags & PROVIDE_MATERIALS))
2252 hr = parse_material_list(child, mesh_data);
2253 } else if (provide_flags & PROVIDE_SKININFO) {
2254 if (IsEqualGUID(type, &DXFILEOBJ_XSkinMeshHeader)) {
2255 FIXME("Skin mesh loading not implemented.\n");
2257 } else if (IsEqualGUID(type, &DXFILEOBJ_SkinWeights)) {
2258 /* ignored without XSkinMeshHeader */
2264 return hr == DXFILEERR_NOMOREOBJECTS ? D3D_OK : hr;
2265 truncated_data_error:
2266 WARN("truncated data (%u bytes)\n", data_size);
2270 /* change to D3DXLoadSkinMeshFromXof when ID3DXFileData is implemented */
2271 static HRESULT load_skin_mesh_from_xof(IDirectXFileData *filedata,
2273 LPDIRECT3DDEVICE9 device,
2274 LPD3DXBUFFER *adjacency_out,
2275 LPD3DXBUFFER *materials_out,
2276 LPD3DXBUFFER *effects_out,
2277 DWORD *num_materials_out,
2278 LPD3DXSKININFO *skin_info_out,
2279 LPD3DXMESH *mesh_out)
2282 DWORD *index_in_ptr;
2283 struct mesh_data mesh_data;
2284 DWORD total_vertices;
2285 ID3DXMesh *d3dxmesh = NULL;
2286 ID3DXBuffer *adjacency = NULL;
2287 ID3DXBuffer *materials = NULL;
2288 struct vertex_duplication {
2291 } *duplications = NULL;
2293 void *vertices = NULL;
2294 void *indices = NULL;
2296 DWORD provide_flags = 0;
2298 ZeroMemory(&mesh_data, sizeof(mesh_data));
2300 if (num_materials_out || materials_out || effects_out)
2301 provide_flags |= PROVIDE_MATERIALS;
2303 provide_flags |= PROVIDE_SKININFO;
2305 hr = parse_mesh(filedata, &mesh_data, provide_flags);
2306 if (FAILED(hr)) goto cleanup;
2308 total_vertices = mesh_data.num_vertices;
2309 if (mesh_data.fvf & D3DFVF_NORMAL) {
2310 /* duplicate vertices with multiple normals */
2311 DWORD num_face_indices = mesh_data.num_poly_faces * 2 + mesh_data.num_tri_faces;
2312 duplications = HeapAlloc(GetProcessHeap(), 0, (mesh_data.num_vertices + num_face_indices) * sizeof(*duplications));
2313 if (!duplications) {
2317 for (i = 0; i < total_vertices; i++)
2319 duplications[i].normal_index = -1;
2320 list_init(&duplications[i].entry);
2322 for (i = 0; i < num_face_indices; i++) {
2323 DWORD vertex_index = mesh_data.indices[i];
2324 DWORD normal_index = mesh_data.normal_indices[i];
2325 struct vertex_duplication *dup_ptr = &duplications[vertex_index];
2327 if (dup_ptr->normal_index == -1) {
2328 dup_ptr->normal_index = normal_index;
2330 D3DXVECTOR3 *new_normal = &mesh_data.normals[normal_index];
2331 struct list *dup_list = &dup_ptr->entry;
2333 D3DXVECTOR3 *cur_normal = &mesh_data.normals[dup_ptr->normal_index];
2334 if (new_normal->x == cur_normal->x &&
2335 new_normal->y == cur_normal->y &&
2336 new_normal->z == cur_normal->z)
2338 mesh_data.indices[i] = dup_ptr - duplications;
2340 } else if (!list_next(dup_list, &dup_ptr->entry)) {
2341 dup_ptr = &duplications[total_vertices++];
2342 dup_ptr->normal_index = normal_index;
2343 list_add_tail(dup_list, &dup_ptr->entry);
2344 mesh_data.indices[i] = dup_ptr - duplications;
2347 dup_ptr = LIST_ENTRY(list_next(dup_list, &dup_ptr->entry),
2348 struct vertex_duplication, entry);
2355 hr = D3DXCreateMeshFVF(mesh_data.num_tri_faces, total_vertices, options, mesh_data.fvf, device, &d3dxmesh);
2356 if (FAILED(hr)) goto cleanup;
2358 hr = d3dxmesh->lpVtbl->LockVertexBuffer(d3dxmesh, D3DLOCK_DISCARD, &vertices);
2359 if (FAILED(hr)) goto cleanup;
2362 for (i = 0; i < mesh_data.num_vertices; i++) {
2363 *(D3DXVECTOR3*)out_ptr = mesh_data.vertices[i];
2364 out_ptr += sizeof(D3DXVECTOR3);
2365 if (mesh_data.fvf & D3DFVF_NORMAL) {
2366 if (duplications[i].normal_index == -1)
2367 ZeroMemory(out_ptr, sizeof(D3DXVECTOR3));
2369 *(D3DXVECTOR3*)out_ptr = mesh_data.normals[duplications[i].normal_index];
2370 out_ptr += sizeof(D3DXVECTOR3);
2373 if (mesh_data.fvf & D3DFVF_NORMAL) {
2374 DWORD vertex_size = D3DXGetFVFVertexSize(mesh_data.fvf);
2376 for (i = 0; i < mesh_data.num_vertices; i++) {
2377 struct vertex_duplication *dup_ptr;
2378 LIST_FOR_EACH_ENTRY(dup_ptr, &duplications[i].entry, struct vertex_duplication, entry)
2380 int j = dup_ptr - duplications;
2381 BYTE *dest_vertex = (BYTE*)vertices + j * vertex_size;
2383 memcpy(dest_vertex, out_ptr, vertex_size);
2384 dest_vertex += sizeof(D3DXVECTOR3);
2385 *(D3DXVECTOR3*)dest_vertex = mesh_data.normals[dup_ptr->normal_index];
2387 out_ptr += vertex_size;
2390 d3dxmesh->lpVtbl->UnlockVertexBuffer(d3dxmesh);
2392 hr = d3dxmesh->lpVtbl->LockIndexBuffer(d3dxmesh, D3DLOCK_DISCARD, (void**)&indices);
2393 if (FAILED(hr)) goto cleanup;
2395 index_in_ptr = mesh_data.indices;
2396 #define FILL_INDEX_BUFFER(indices_var) \
2397 for (i = 0; i < mesh_data.num_poly_faces; i++) \
2399 DWORD count = mesh_data.num_tri_per_face[i]; \
2400 WORD first_index = *index_in_ptr++; \
2402 *indices_var++ = first_index; \
2403 *indices_var++ = *index_in_ptr; \
2405 *indices_var++ = *index_in_ptr; \
2409 if (options & D3DXMESH_32BIT) {
2410 DWORD *dword_indices = indices;
2411 FILL_INDEX_BUFFER(dword_indices)
2413 WORD *word_indices = indices;
2414 FILL_INDEX_BUFFER(word_indices)
2416 #undef FILL_INDEX_BUFFER
2417 d3dxmesh->lpVtbl->UnlockIndexBuffer(d3dxmesh);
2419 if (mesh_data.material_indices) {
2420 DWORD *attrib_buffer = NULL;
2421 hr = d3dxmesh->lpVtbl->LockAttributeBuffer(d3dxmesh, D3DLOCK_DISCARD, &attrib_buffer);
2422 if (FAILED(hr)) goto cleanup;
2423 for (i = 0; i < mesh_data.num_poly_faces; i++)
2425 DWORD count = mesh_data.num_tri_per_face[i];
2427 *attrib_buffer++ = mesh_data.material_indices[i];
2429 d3dxmesh->lpVtbl->UnlockAttributeBuffer(d3dxmesh);
2431 hr = d3dxmesh->lpVtbl->OptimizeInplace(d3dxmesh,
2432 D3DXMESHOPT_ATTRSORT | D3DXMESHOPT_IGNOREVERTS | D3DXMESHOPT_DONOTSPLIT,
2433 NULL, NULL, NULL, NULL);
2434 if (FAILED(hr)) goto cleanup;
2437 if (mesh_data.num_materials && materials_out) {
2438 DWORD buffer_size = mesh_data.num_materials * sizeof(D3DXMATERIAL);
2439 char *strings_out_ptr;
2440 D3DXMATERIAL *materials_ptr;
2442 for (i = 0; i < mesh_data.num_materials; i++) {
2443 if (mesh_data.materials[i].pTextureFilename)
2444 buffer_size += strlen(mesh_data.materials[i].pTextureFilename) + 1;
2447 hr = D3DXCreateBuffer(buffer_size, &materials);
2448 if (FAILED(hr)) goto cleanup;
2450 materials_ptr = ID3DXBuffer_GetBufferPointer(materials);
2451 memcpy(materials_ptr, mesh_data.materials, mesh_data.num_materials * sizeof(D3DXMATERIAL));
2452 strings_out_ptr = (char*)(materials_ptr + mesh_data.num_materials);
2453 for (i = 0; i < mesh_data.num_materials; i++) {
2454 if (materials_ptr[i].pTextureFilename) {
2455 strcpy(strings_out_ptr, mesh_data.materials[i].pTextureFilename);
2456 materials_ptr[i].pTextureFilename = strings_out_ptr;
2457 strings_out_ptr += strlen(mesh_data.materials[i].pTextureFilename) + 1;
2462 if (mesh_data.num_materials && effects_out) {
2463 FIXME("Effect instance generation not supported.\n");
2468 if (adjacency_out) {
2469 hr = D3DXCreateBuffer(mesh_data.num_tri_faces * 3 * sizeof(DWORD), &adjacency);
2470 if (FAILED(hr)) goto cleanup;
2471 hr = d3dxmesh->lpVtbl->GenerateAdjacency(d3dxmesh, 0.0f, ID3DXBuffer_GetBufferPointer(adjacency));
2472 if (FAILED(hr)) goto cleanup;
2475 *mesh_out = d3dxmesh;
2476 if (adjacency_out) *adjacency_out = adjacency;
2477 if (num_materials_out) *num_materials_out = mesh_data.num_materials;
2478 if (materials_out) *materials_out = materials;
2479 if (effects_out) *effects_out = NULL;
2480 if (skin_info_out) *skin_info_out = NULL;
2485 if (d3dxmesh) IUnknown_Release(d3dxmesh);
2486 if (adjacency) ID3DXBuffer_Release(adjacency);
2487 if (materials) ID3DXBuffer_Release(materials);
2489 HeapFree(GetProcessHeap(), 0, mesh_data.vertices);
2490 HeapFree(GetProcessHeap(), 0, mesh_data.num_tri_per_face);
2491 HeapFree(GetProcessHeap(), 0, mesh_data.indices);
2492 HeapFree(GetProcessHeap(), 0, mesh_data.normals);
2493 HeapFree(GetProcessHeap(), 0, mesh_data.normal_indices);
2494 destroy_materials(&mesh_data);
2495 HeapFree(GetProcessHeap(), 0, duplications);
2499 static HRESULT filedata_get_name(IDirectXFileData *filedata, char **name)
2504 hr = IDirectXFileData_GetName(filedata, NULL, &name_len);
2505 if (FAILED(hr)) return hr;
2509 *name = HeapAlloc(GetProcessHeap(), 0, name_len);
2510 if (!*name) return E_OUTOFMEMORY;
2512 hr = IDirectXFileObject_GetName(filedata, *name, &name_len);
2514 HeapFree(GetProcessHeap(), 0, name);
2521 static HRESULT load_mesh_container(IDirectXFileData *filedata,
2523 LPDIRECT3DDEVICE9 device,
2524 LPD3DXALLOCATEHIERARCHY alloc_hier,
2525 D3DXMESHCONTAINER **mesh_container)
2528 ID3DXBuffer *adjacency = NULL;
2529 ID3DXBuffer *materials = NULL;
2530 ID3DXBuffer *effects = NULL;
2531 ID3DXSkinInfo *skin_info = NULL;
2532 D3DXMESHDATA mesh_data;
2533 DWORD num_materials = 0;
2536 mesh_data.Type = D3DXMESHTYPE_MESH;
2537 mesh_data.u.pMesh = NULL;
2539 hr = load_skin_mesh_from_xof(filedata, options, device,
2540 &adjacency, &materials, &effects, &num_materials,
2541 &skin_info, &mesh_data.u.pMesh);
2542 if (FAILED(hr)) return hr;
2544 hr = filedata_get_name(filedata, &name);
2545 if (FAILED(hr)) goto cleanup;
2547 hr = alloc_hier->lpVtbl->CreateMeshContainer(alloc_hier, name, &mesh_data,
2548 materials ? ID3DXBuffer_GetBufferPointer(materials) : NULL,
2549 effects ? ID3DXBuffer_GetBufferPointer(effects) : NULL,
2551 adjacency ? ID3DXBuffer_GetBufferPointer(adjacency) : NULL,
2552 skin_info, mesh_container);
2555 if (materials) ID3DXBuffer_Release(materials);
2556 if (effects) ID3DXBuffer_Release(effects);
2557 if (adjacency) ID3DXBuffer_Release(adjacency);
2558 if (skin_info) IUnknown_Release(skin_info);
2559 if (mesh_data.u.pMesh) IUnknown_Release(mesh_data.u.pMesh);
2560 HeapFree(GetProcessHeap(), 0, name);
2564 static HRESULT parse_transform_matrix(IDirectXFileData *filedata, D3DXMATRIX *transform)
2570 /* template Matrix4x4 {
2571 * array FLOAT matrix[16];
2573 * template FrameTransformMatrix {
2574 * Matrix4x4 frameMatrix;
2578 hr = IDirectXFileData_GetData(filedata, NULL, &data_size, (void**)&data);
2579 if (FAILED(hr)) return hr;
2581 if (data_size != sizeof(D3DXMATRIX)) {
2582 WARN("incorrect data size (%u bytes)\n", data_size);
2586 memcpy(transform, data, sizeof(D3DXMATRIX));
2591 static HRESULT load_frame(IDirectXFileData *filedata,
2593 LPDIRECT3DDEVICE9 device,
2594 LPD3DXALLOCATEHIERARCHY alloc_hier,
2595 D3DXFRAME **frame_out)
2599 IDirectXFileData *child;
2601 D3DXFRAME *frame = NULL;
2602 D3DXMESHCONTAINER **next_container;
2603 D3DXFRAME **next_child;
2605 hr = filedata_get_name(filedata, &name);
2606 if (FAILED(hr)) return hr;
2608 hr = alloc_hier->lpVtbl->CreateFrame(alloc_hier, name, frame_out);
2609 HeapFree(GetProcessHeap(), 0, name);
2610 if (FAILED(hr)) return E_FAIL;
2613 D3DXMatrixIdentity(&frame->TransformationMatrix);
2614 next_child = &frame->pFrameFirstChild;
2615 next_container = &frame->pMeshContainer;
2617 while (SUCCEEDED(hr = get_next_child(filedata, &child, &type)))
2619 if (IsEqualGUID(type, &TID_D3DRMMesh)) {
2620 hr = load_mesh_container(child, options, device, alloc_hier, next_container);
2622 next_container = &(*next_container)->pNextMeshContainer;
2623 } else if (IsEqualGUID(type, &TID_D3DRMFrameTransformMatrix)) {
2624 hr = parse_transform_matrix(child, &frame->TransformationMatrix);
2625 } else if (IsEqualGUID(type, &TID_D3DRMFrame)) {
2626 hr = load_frame(child, options, device, alloc_hier, next_child);
2628 next_child = &(*next_child)->pFrameSibling;
2630 if (FAILED(hr)) break;
2632 if (hr == DXFILEERR_NOMOREOBJECTS)
2638 HRESULT WINAPI D3DXLoadMeshHierarchyFromXInMemory(LPCVOID memory,
2641 LPDIRECT3DDEVICE9 device,
2642 LPD3DXALLOCATEHIERARCHY alloc_hier,
2643 LPD3DXLOADUSERDATA load_user_data,
2644 LPD3DXFRAME *frame_hierarchy,
2645 LPD3DXANIMATIONCONTROLLER *anim_controller)
2648 IDirectXFile *dxfile = NULL;
2649 IDirectXFileEnumObject *enumobj = NULL;
2650 IDirectXFileData *filedata = NULL;
2651 DXFILELOADMEMORY source;
2652 D3DXFRAME *first_frame = NULL;
2653 D3DXFRAME **next_frame = &first_frame;
2655 TRACE("(%p, %u, %x, %p, %p, %p, %p, %p)\n", memory, memory_size, options,
2656 device, alloc_hier, load_user_data, frame_hierarchy, anim_controller);
2658 if (!memory || !memory_size || !device || !frame_hierarchy || !alloc_hier)
2659 return D3DERR_INVALIDCALL;
2660 if (load_user_data || anim_controller) {
2662 FIXME("Loading user data not implemented\n");
2663 if (anim_controller)
2664 FIXME("Animation controller creation not implemented\n");
2668 hr = DirectXFileCreate(&dxfile);
2669 if (FAILED(hr)) goto cleanup;
2671 hr = IDirectXFile_RegisterTemplates(dxfile, D3DRM_XTEMPLATES, D3DRM_XTEMPLATE_BYTES);
2672 if (FAILED(hr)) goto cleanup;
2674 source.lpMemory = (void*)memory;
2675 source.dSize = memory_size;
2676 hr = IDirectXFile_CreateEnumObject(dxfile, &source, DXFILELOAD_FROMMEMORY, &enumobj);
2677 if (FAILED(hr)) goto cleanup;
2679 while (SUCCEEDED(hr = IDirectXFileEnumObject_GetNextDataObject(enumobj, &filedata)))
2681 const GUID *guid = NULL;
2683 hr = IDirectXFileData_GetType(filedata, &guid);
2684 if (SUCCEEDED(hr)) {
2685 if (IsEqualGUID(guid, &TID_D3DRMMesh)) {
2686 hr = alloc_hier->lpVtbl->CreateFrame(alloc_hier, NULL, next_frame);
2692 D3DXMatrixIdentity(&(*next_frame)->TransformationMatrix);
2694 hr = load_mesh_container(filedata, options, device, alloc_hier, &(*next_frame)->pMeshContainer);
2695 if (FAILED(hr)) goto cleanup;
2696 } else if (IsEqualGUID(guid, &TID_D3DRMFrame)) {
2697 hr = load_frame(filedata, options, device, alloc_hier, next_frame);
2698 if (FAILED(hr)) goto cleanup;
2701 next_frame = &(*next_frame)->pFrameSibling;
2704 IDirectXFileData_Release(filedata);
2709 if (hr != DXFILEERR_NOMOREOBJECTS)
2714 } else if (first_frame->pFrameSibling) {
2715 D3DXFRAME *root_frame = NULL;
2716 hr = alloc_hier->lpVtbl->CreateFrame(alloc_hier, NULL, &root_frame);
2721 D3DXMatrixIdentity(&root_frame->TransformationMatrix);
2722 root_frame->pFrameFirstChild = first_frame;
2723 *frame_hierarchy = root_frame;
2726 *frame_hierarchy = first_frame;
2731 if (FAILED(hr) && first_frame) D3DXFrameDestroy(first_frame, alloc_hier);
2732 if (filedata) IDirectXFileData_Release(filedata);
2733 if (enumobj) IDirectXFileEnumObject_Release(enumobj);
2734 if (dxfile) IDirectXFile_Release(dxfile);
2738 HRESULT WINAPI D3DXFrameDestroy(LPD3DXFRAME frame, LPD3DXALLOCATEHIERARCHY alloc_hier)
2743 TRACE("(%p, %p)\n", frame, alloc_hier);
2745 if (!frame || !alloc_hier)
2746 return D3DERR_INVALIDCALL;
2749 D3DXMESHCONTAINER *container;
2750 D3DXFRAME *current_frame;
2752 if (frame->pFrameSibling) {
2753 current_frame = frame->pFrameSibling;
2754 frame->pFrameSibling = current_frame->pFrameSibling;
2755 current_frame->pFrameSibling = NULL;
2757 current_frame = frame;
2761 if (current_frame->pFrameFirstChild) {
2762 hr = D3DXFrameDestroy(current_frame->pFrameFirstChild, alloc_hier);
2763 if (FAILED(hr)) return hr;
2764 current_frame->pFrameFirstChild = NULL;
2767 container = current_frame->pMeshContainer;
2769 D3DXMESHCONTAINER *next_container = container->pNextMeshContainer;
2770 hr = alloc_hier->lpVtbl->DestroyMeshContainer(alloc_hier, container);
2771 if (FAILED(hr)) return hr;
2772 container = next_container;
2774 hr = alloc_hier->lpVtbl->DestroyFrame(alloc_hier, current_frame);
2775 if (FAILED(hr)) return hr;
2780 HRESULT WINAPI D3DXCreateBox(LPDIRECT3DDEVICE9 device, FLOAT width, FLOAT height,
2781 FLOAT depth, LPD3DXMESH* mesh, LPD3DXBUFFER* adjacency)
2783 FIXME("(%p, %f, %f, %f, %p, %p): stub\n", device, width, height, depth, mesh, adjacency);
2790 D3DXVECTOR3 position;
2794 typedef WORD face[3];
2802 static void free_sincos_table(struct sincos_table *sincos_table)
2804 HeapFree(GetProcessHeap(), 0, sincos_table->cos);
2805 HeapFree(GetProcessHeap(), 0, sincos_table->sin);
2808 /* pre compute sine and cosine tables; caller must free */
2809 static BOOL compute_sincos_table(struct sincos_table *sincos_table, float angle_start, float angle_step, int n)
2814 sincos_table->sin = HeapAlloc(GetProcessHeap(), 0, n * sizeof(*sincos_table->sin));
2815 if (!sincos_table->sin)
2819 sincos_table->cos = HeapAlloc(GetProcessHeap(), 0, n * sizeof(*sincos_table->cos));
2820 if (!sincos_table->cos)
2822 HeapFree(GetProcessHeap(), 0, sincos_table->sin);
2826 angle = angle_start;
2827 for (i = 0; i < n; i++)
2829 sincos_table->sin[i] = sin(angle);
2830 sincos_table->cos[i] = cos(angle);
2831 angle += angle_step;
2837 static WORD vertex_index(UINT slices, int slice, int stack)
2839 return stack*slices+slice+1;
2842 HRESULT WINAPI D3DXCreateSphere(LPDIRECT3DDEVICE9 device, FLOAT radius, UINT slices,
2843 UINT stacks, LPD3DXMESH* mesh, LPD3DXBUFFER* adjacency)
2845 DWORD number_of_vertices, number_of_faces;
2848 struct vertex *vertices;
2850 float phi_step, phi_start;
2851 struct sincos_table phi;
2852 float theta_step, theta, sin_theta, cos_theta;
2856 TRACE("(%p, %f, %u, %u, %p, %p)\n", device, radius, slices, stacks, mesh, adjacency);
2858 if (!device || radius < 0.0f || slices < 2 || stacks < 2 || !mesh)
2860 return D3DERR_INVALIDCALL;
2865 FIXME("Case of adjacency != NULL not implemented.\n");
2869 number_of_vertices = 2 + slices * (stacks-1);
2870 number_of_faces = 2 * slices + (stacks - 2) * (2 * slices);
2872 hr = D3DXCreateMeshFVF(number_of_faces, number_of_vertices, D3DXMESH_MANAGED,
2873 D3DFVF_XYZ | D3DFVF_NORMAL, device, &sphere);
2879 hr = sphere->lpVtbl->LockVertexBuffer(sphere, D3DLOCK_DISCARD, (LPVOID *)&vertices);
2882 sphere->lpVtbl->Release(sphere);
2886 hr = sphere->lpVtbl->LockIndexBuffer(sphere, D3DLOCK_DISCARD, (LPVOID *)&faces);
2889 sphere->lpVtbl->UnlockVertexBuffer(sphere);
2890 sphere->lpVtbl->Release(sphere);
2894 /* phi = angle on xz plane wrt z axis */
2895 phi_step = -2 * M_PI / slices;
2896 phi_start = M_PI / 2;
2898 if (!compute_sincos_table(&phi, phi_start, phi_step, slices))
2900 sphere->lpVtbl->UnlockIndexBuffer(sphere);
2901 sphere->lpVtbl->UnlockVertexBuffer(sphere);
2902 sphere->lpVtbl->Release(sphere);
2903 return E_OUTOFMEMORY;
2906 /* theta = angle on xy plane wrt x axis */
2907 theta_step = M_PI / stacks;
2914 vertices[vertex].normal.x = 0.0f;
2915 vertices[vertex].normal.y = 0.0f;
2916 vertices[vertex].normal.z = 1.0f;
2917 vertices[vertex].position.x = 0.0f;
2918 vertices[vertex].position.y = 0.0f;
2919 vertices[vertex].position.z = radius;
2922 for (stack = 0; stack < stacks - 1; stack++)
2924 sin_theta = sin(theta);
2925 cos_theta = cos(theta);
2927 for (slice = 0; slice < slices; slice++)
2929 vertices[vertex].normal.x = sin_theta * phi.cos[slice];
2930 vertices[vertex].normal.y = sin_theta * phi.sin[slice];
2931 vertices[vertex].normal.z = cos_theta;
2932 vertices[vertex].position.x = radius * sin_theta * phi.cos[slice];
2933 vertices[vertex].position.y = radius * sin_theta * phi.sin[slice];
2934 vertices[vertex].position.z = radius * cos_theta;
2941 /* top stack is triangle fan */
2943 faces[face][1] = slice + 1;
2944 faces[face][2] = slice;
2949 /* stacks in between top and bottom are quad strips */
2950 faces[face][0] = vertex_index(slices, slice-1, stack-1);
2951 faces[face][1] = vertex_index(slices, slice, stack-1);
2952 faces[face][2] = vertex_index(slices, slice-1, stack);
2955 faces[face][0] = vertex_index(slices, slice, stack-1);
2956 faces[face][1] = vertex_index(slices, slice, stack);
2957 faces[face][2] = vertex_index(slices, slice-1, stack);
2963 theta += theta_step;
2969 faces[face][2] = slice;
2974 faces[face][0] = vertex_index(slices, slice-1, stack-1);
2975 faces[face][1] = vertex_index(slices, 0, stack-1);
2976 faces[face][2] = vertex_index(slices, slice-1, stack);
2979 faces[face][0] = vertex_index(slices, 0, stack-1);
2980 faces[face][1] = vertex_index(slices, 0, stack);
2981 faces[face][2] = vertex_index(slices, slice-1, stack);
2986 vertices[vertex].position.x = 0.0f;
2987 vertices[vertex].position.y = 0.0f;
2988 vertices[vertex].position.z = -radius;
2989 vertices[vertex].normal.x = 0.0f;
2990 vertices[vertex].normal.y = 0.0f;
2991 vertices[vertex].normal.z = -1.0f;
2993 /* bottom stack is triangle fan */
2994 for (slice = 1; slice < slices; slice++)
2996 faces[face][0] = vertex_index(slices, slice-1, stack-1);
2997 faces[face][1] = vertex_index(slices, slice, stack-1);
2998 faces[face][2] = vertex;
3002 faces[face][0] = vertex_index(slices, slice-1, stack-1);
3003 faces[face][1] = vertex_index(slices, 0, stack-1);
3004 faces[face][2] = vertex;
3006 free_sincos_table(&phi);
3007 sphere->lpVtbl->UnlockIndexBuffer(sphere);
3008 sphere->lpVtbl->UnlockVertexBuffer(sphere);
3014 HRESULT WINAPI D3DXCreateCylinder(LPDIRECT3DDEVICE9 device, FLOAT radius1, FLOAT radius2, FLOAT length, UINT slices,
3015 UINT stacks, LPD3DXMESH* mesh, LPD3DXBUFFER* adjacency)
3017 DWORD number_of_vertices, number_of_faces;
3019 ID3DXMesh *cylinder;
3020 struct vertex *vertices;
3022 float theta_step, theta_start;
3023 struct sincos_table theta;
3024 float delta_radius, radius, radius_step;
3025 float z, z_step, z_normal;
3029 TRACE("(%p, %f, %f, %f, %u, %u, %p, %p)\n", device, radius1, radius2, length, slices, stacks, mesh, adjacency);
3031 if (device == NULL || radius1 < 0.0f || radius2 < 0.0f || length < 0.0f || slices < 2 || stacks < 1 || mesh == NULL)
3033 return D3DERR_INVALIDCALL;
3038 FIXME("Case of adjacency != NULL not implemented.\n");
3042 number_of_vertices = 2 + (slices * (3 + stacks));
3043 number_of_faces = 2 * slices + stacks * (2 * slices);
3045 hr = D3DXCreateMeshFVF(number_of_faces, number_of_vertices, D3DXMESH_MANAGED,
3046 D3DFVF_XYZ | D3DFVF_NORMAL, device, &cylinder);
3052 hr = cylinder->lpVtbl->LockVertexBuffer(cylinder, D3DLOCK_DISCARD, (LPVOID *)&vertices);
3055 cylinder->lpVtbl->Release(cylinder);
3059 hr = cylinder->lpVtbl->LockIndexBuffer(cylinder, D3DLOCK_DISCARD, (LPVOID *)&faces);
3062 cylinder->lpVtbl->UnlockVertexBuffer(cylinder);
3063 cylinder->lpVtbl->Release(cylinder);
3067 /* theta = angle on xy plane wrt x axis */
3068 theta_step = -2 * M_PI / slices;
3069 theta_start = M_PI / 2;
3071 if (!compute_sincos_table(&theta, theta_start, theta_step, slices))
3073 cylinder->lpVtbl->UnlockIndexBuffer(cylinder);
3074 cylinder->lpVtbl->UnlockVertexBuffer(cylinder);
3075 cylinder->lpVtbl->Release(cylinder);
3076 return E_OUTOFMEMORY;
3082 delta_radius = radius1 - radius2;
3084 radius_step = delta_radius / stacks;
3087 z_step = length / stacks;
3088 z_normal = delta_radius / length;
3089 if (isnan(z_normal))
3094 vertices[vertex].normal.x = 0.0f;
3095 vertices[vertex].normal.y = 0.0f;
3096 vertices[vertex].normal.z = -1.0f;
3097 vertices[vertex].position.x = 0.0f;
3098 vertices[vertex].position.y = 0.0f;
3099 vertices[vertex++].position.z = z;
3101 for (slice = 0; slice < slices; slice++, vertex++)
3103 vertices[vertex].normal.x = 0.0f;
3104 vertices[vertex].normal.y = 0.0f;
3105 vertices[vertex].normal.z = -1.0f;
3106 vertices[vertex].position.x = radius * theta.cos[slice];
3107 vertices[vertex].position.y = radius * theta.sin[slice];
3108 vertices[vertex].position.z = z;
3113 faces[face][1] = slice;
3114 faces[face++][2] = slice + 1;
3119 faces[face][1] = slice;
3120 faces[face++][2] = 1;
3122 for (stack = 1; stack <= stacks+1; stack++)
3124 for (slice = 0; slice < slices; slice++, vertex++)
3126 vertices[vertex].normal.x = theta.cos[slice];
3127 vertices[vertex].normal.y = theta.sin[slice];
3128 vertices[vertex].normal.z = z_normal;
3129 D3DXVec3Normalize(&vertices[vertex].normal, &vertices[vertex].normal);
3130 vertices[vertex].position.x = radius * theta.cos[slice];
3131 vertices[vertex].position.y = radius * theta.sin[slice];
3132 vertices[vertex].position.z = z;
3134 if (stack > 1 && slice > 0)
3136 faces[face][0] = vertex_index(slices, slice-1, stack-1);
3137 faces[face][1] = vertex_index(slices, slice-1, stack);
3138 faces[face++][2] = vertex_index(slices, slice, stack-1);
3140 faces[face][0] = vertex_index(slices, slice, stack-1);
3141 faces[face][1] = vertex_index(slices, slice-1, stack);
3142 faces[face++][2] = vertex_index(slices, slice, stack);
3148 faces[face][0] = vertex_index(slices, slice-1, stack-1);
3149 faces[face][1] = vertex_index(slices, slice-1, stack);
3150 faces[face++][2] = vertex_index(slices, 0, stack-1);
3152 faces[face][0] = vertex_index(slices, 0, stack-1);
3153 faces[face][1] = vertex_index(slices, slice-1, stack);
3154 faces[face++][2] = vertex_index(slices, 0, stack);
3157 if (stack < stacks + 1)
3160 radius -= radius_step;
3164 for (slice = 0; slice < slices; slice++, vertex++)
3166 vertices[vertex].normal.x = 0.0f;
3167 vertices[vertex].normal.y = 0.0f;
3168 vertices[vertex].normal.z = 1.0f;
3169 vertices[vertex].position.x = radius * theta.cos[slice];
3170 vertices[vertex].position.y = radius * theta.sin[slice];
3171 vertices[vertex].position.z = z;
3175 faces[face][0] = vertex_index(slices, slice-1, stack);
3176 faces[face][1] = number_of_vertices - 1;
3177 faces[face++][2] = vertex_index(slices, slice, stack);
3181 vertices[vertex].position.x = 0.0f;
3182 vertices[vertex].position.y = 0.0f;
3183 vertices[vertex].position.z = z;
3184 vertices[vertex].normal.x = 0.0f;
3185 vertices[vertex].normal.y = 0.0f;
3186 vertices[vertex].normal.z = 1.0f;
3188 faces[face][0] = vertex_index(slices, slice-1, stack);
3189 faces[face][1] = number_of_vertices - 1;
3190 faces[face][2] = vertex_index(slices, 0, stack);
3192 free_sincos_table(&theta);
3193 cylinder->lpVtbl->UnlockIndexBuffer(cylinder);
3194 cylinder->lpVtbl->UnlockVertexBuffer(cylinder);
3200 HRESULT WINAPI D3DXCreateTeapot(LPDIRECT3DDEVICE9 device, LPD3DXMESH *mesh, LPD3DXBUFFER* adjacency)
3202 FIXME("(%p, %p, %p): stub\n", device, mesh, adjacency);
3207 HRESULT WINAPI D3DXCreateTextA(LPDIRECT3DDEVICE9 device,
3208 HDC hdc, LPCSTR text,
3209 FLOAT deviation, FLOAT extrusion,
3210 LPD3DXMESH *mesh, LPD3DXBUFFER *adjacency,
3211 LPGLYPHMETRICSFLOAT glyphmetrics)
3217 TRACE("(%p, %p, %s, %f, %f, %p, %p, %p)\n", device, hdc,
3218 debugstr_a(text), deviation, extrusion, mesh, adjacency, glyphmetrics);
3221 return D3DERR_INVALIDCALL;
3223 len = MultiByteToWideChar(CP_ACP, 0, text, -1, NULL, 0);
3224 textW = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
3225 MultiByteToWideChar(CP_ACP, 0, text, -1, textW, len);
3227 hr = D3DXCreateTextW(device, hdc, textW, deviation, extrusion,
3228 mesh, adjacency, glyphmetrics);
3229 HeapFree(GetProcessHeap(), 0, textW);
3235 POINTTYPE_CURVE = 0,
3237 POINTTYPE_CURVE_START,
3238 POINTTYPE_CURVE_END,
3239 POINTTYPE_CURVE_MIDDLE,
3245 enum pointtype corner;
3248 struct dynamic_array
3250 int count, capacity;
3254 /* is a dynamic_array */
3257 int count, capacity;
3258 struct point2d *items;
3261 /* is a dynamic_array */
3262 struct outline_array
3264 int count, capacity;
3265 struct outline *items;
3274 struct point2d_index
3276 struct outline *outline;
3280 struct point2d_index_array
3283 struct point2d_index *items;
3288 struct outline_array outlines;
3289 struct face_array faces;
3290 struct point2d_index_array ordered_vertices;
3294 /* is an dynamic_array */
3297 int count, capacity;
3301 /* complex polygons are split into monotone polygons, which have
3302 * at most 2 intersections with the vertical sweep line */
3303 struct triangulation
3305 struct word_array vertex_stack;
3306 BOOL last_on_top, merging;
3309 /* is an dynamic_array */
3310 struct triangulation_array
3312 int count, capacity;
3313 struct triangulation *items;
3315 struct glyphinfo *glyph;
3318 static BOOL reserve(struct dynamic_array *array, int count, int itemsize)
3320 if (count > array->capacity) {
3323 if (array->items && array->capacity) {
3324 new_capacity = max(array->capacity * 2, count);
3325 new_buffer = HeapReAlloc(GetProcessHeap(), 0, array->items, new_capacity * itemsize);
3327 new_capacity = max(16, count);
3328 new_buffer = HeapAlloc(GetProcessHeap(), 0, new_capacity * itemsize);
3332 array->items = new_buffer;
3333 array->capacity = new_capacity;
3338 static struct point2d *add_points(struct outline *array, int num)
3340 struct point2d *item;
3342 if (!reserve((struct dynamic_array *)array, array->count + num, sizeof(array->items[0])))
3345 item = &array->items[array->count];
3346 array->count += num;
3350 static struct outline *add_outline(struct outline_array *array)
3352 struct outline *item;
3354 if (!reserve((struct dynamic_array *)array, array->count + 1, sizeof(array->items[0])))
3357 item = &array->items[array->count++];
3358 ZeroMemory(item, sizeof(*item));
3362 static inline face *add_face(struct face_array *array)
3364 return &array->items[array->count++];
3367 static struct triangulation *add_triangulation(struct triangulation_array *array)
3369 struct triangulation *item;
3371 if (!reserve((struct dynamic_array *)array, array->count + 1, sizeof(array->items[0])))
3374 item = &array->items[array->count++];
3375 ZeroMemory(item, sizeof(*item));
3379 static HRESULT add_vertex_index(struct word_array *array, WORD vertex_index)
3381 if (!reserve((struct dynamic_array *)array, array->count + 1, sizeof(array->items[0])))
3382 return E_OUTOFMEMORY;
3384 array->items[array->count++] = vertex_index;
3388 /* assume fixed point numbers can be converted to float point in place */
3389 C_ASSERT(sizeof(FIXED) == sizeof(float));
3390 C_ASSERT(sizeof(POINTFX) == sizeof(D3DXVECTOR2));
3392 static inline D3DXVECTOR2 *convert_fixed_to_float(POINTFX *pt, int count, float emsquare)
3394 D3DXVECTOR2 *ret = (D3DXVECTOR2*)pt;
3396 D3DXVECTOR2 *pt_flt = (D3DXVECTOR2*)pt;
3397 pt_flt->x = (pt->x.value + pt->x.fract / (float)0x10000) / emsquare;
3398 pt_flt->y = (pt->y.value + pt->y.fract / (float)0x10000) / emsquare;
3404 static HRESULT add_bezier_points(struct outline *outline, const D3DXVECTOR2 *p1,
3405 const D3DXVECTOR2 *p2, const D3DXVECTOR2 *p3,
3406 float max_deviation_sq)
3408 D3DXVECTOR2 split1 = {0, 0}, split2 = {0, 0}, middle, vec;
3411 D3DXVec2Scale(&split1, D3DXVec2Add(&split1, p1, p2), 0.5f);
3412 D3DXVec2Scale(&split2, D3DXVec2Add(&split2, p2, p3), 0.5f);
3413 D3DXVec2Scale(&middle, D3DXVec2Add(&middle, &split1, &split2), 0.5f);
3415 deviation_sq = D3DXVec2LengthSq(D3DXVec2Subtract(&vec, &middle, p2));
3416 if (deviation_sq < max_deviation_sq) {
3417 struct point2d *pt = add_points(outline, 1);
3418 if (!pt) return E_OUTOFMEMORY;
3420 pt->corner = POINTTYPE_CURVE;
3421 /* the end point is omitted because the end line merges into the next segment of
3422 * the split bezier curve, and the end of the split bezier curve is added outside
3423 * this recursive function. */
3425 HRESULT hr = add_bezier_points(outline, p1, &split1, &middle, max_deviation_sq);
3426 if (hr != S_OK) return hr;
3427 hr = add_bezier_points(outline, &middle, &split2, p3, max_deviation_sq);
3428 if (hr != S_OK) return hr;
3434 static inline BOOL is_direction_similar(D3DXVECTOR2 *dir1, D3DXVECTOR2 *dir2, float cos_theta)
3436 /* dot product = cos(theta) */
3437 return D3DXVec2Dot(dir1, dir2) > cos_theta;
3440 static inline D3DXVECTOR2 *unit_vec2(D3DXVECTOR2 *dir, const D3DXVECTOR2 *pt1, const D3DXVECTOR2 *pt2)
3442 return D3DXVec2Normalize(D3DXVec2Subtract(dir, pt2, pt1), dir);
3452 static BOOL attempt_line_merge(struct outline *outline,
3454 const D3DXVECTOR2 *nextpt,
3456 const struct cos_table *table)
3458 D3DXVECTOR2 curdir, lastdir;
3459 struct point2d *prevpt, *pt;
3462 pt = &outline->items[pt_index];
3463 pt_index = (pt_index - 1 + outline->count) % outline->count;
3464 prevpt = &outline->items[pt_index];
3467 pt->corner = pt->corner != POINTTYPE_CORNER ? POINTTYPE_CURVE_MIDDLE : POINTTYPE_CURVE_START;
3469 if (outline->count < 2)
3472 /* remove last point if the next line continues the last line */
3473 unit_vec2(&lastdir, &prevpt->pos, &pt->pos);
3474 unit_vec2(&curdir, &pt->pos, nextpt);
3475 if (is_direction_similar(&lastdir, &curdir, table->cos_half))
3478 if (pt->corner == POINTTYPE_CURVE_END)
3479 prevpt->corner = pt->corner;
3480 if (prevpt->corner == POINTTYPE_CURVE_END && to_curve)
3481 prevpt->corner = POINTTYPE_CURVE_MIDDLE;
3485 if (outline->count < 2)
3488 pt_index = (pt_index - 1 + outline->count) % outline->count;
3489 prevpt = &outline->items[pt_index];
3490 unit_vec2(&lastdir, &prevpt->pos, &pt->pos);
3491 unit_vec2(&curdir, &pt->pos, nextpt);
3496 static HRESULT create_outline(struct glyphinfo *glyph, void *raw_outline, int datasize,
3497 float max_deviation_sq, float emsquare, const struct cos_table *cos_table)
3499 TTPOLYGONHEADER *header = (TTPOLYGONHEADER *)raw_outline;
3501 while ((char *)header < (char *)raw_outline + datasize)
3503 TTPOLYCURVE *curve = (TTPOLYCURVE *)(header + 1);
3504 struct point2d *lastpt, *pt;
3505 D3DXVECTOR2 lastdir;
3506 D3DXVECTOR2 *pt_flt;
3508 struct outline *outline = add_outline(&glyph->outlines);
3511 return E_OUTOFMEMORY;
3513 pt = add_points(outline, 1);
3515 return E_OUTOFMEMORY;
3516 pt_flt = convert_fixed_to_float(&header->pfxStart, 1, emsquare);
3518 pt->corner = POINTTYPE_CORNER;
3520 if (header->dwType != TT_POLYGON_TYPE)
3521 FIXME("Unknown header type %d\n", header->dwType);
3523 while ((char *)curve < (char *)header + header->cb)
3525 D3DXVECTOR2 bezier_start = outline->items[outline->count - 1].pos;
3526 BOOL to_curve = curve->wType != TT_PRIM_LINE && curve->cpfx > 1;
3529 curve = (TTPOLYCURVE *)&curve->apfx[curve->cpfx];
3533 pt_flt = convert_fixed_to_float(curve->apfx, curve->cpfx, emsquare);
3535 attempt_line_merge(outline, outline->count - 1, &pt_flt[0], to_curve, cos_table);
3540 int count = curve->cpfx;
3545 D3DXVECTOR2 bezier_end;
3547 D3DXVec2Scale(&bezier_end, D3DXVec2Add(&bezier_end, &pt_flt[j], &pt_flt[j+1]), 0.5f);
3548 hr = add_bezier_points(outline, &bezier_start, &pt_flt[j], &bezier_end, max_deviation_sq);
3551 bezier_start = bezier_end;
3555 hr = add_bezier_points(outline, &bezier_start, &pt_flt[j], &pt_flt[j+1], max_deviation_sq);
3559 pt = add_points(outline, 1);
3561 return E_OUTOFMEMORY;
3563 pt->pos = pt_flt[j];
3564 pt->corner = POINTTYPE_CURVE_END;
3566 pt = add_points(outline, curve->cpfx);
3568 return E_OUTOFMEMORY;
3569 for (j = 0; j < curve->cpfx; j++)
3571 pt->pos = pt_flt[j];
3572 pt->corner = POINTTYPE_CORNER;
3577 curve = (TTPOLYCURVE *)&curve->apfx[curve->cpfx];
3580 /* remove last point if the next line continues the last line */
3581 if (outline->count >= 3) {
3584 lastpt = &outline->items[outline->count - 1];
3585 pt = &outline->items[0];
3586 if (pt->pos.x == lastpt->pos.x && pt->pos.y == lastpt->pos.y) {
3587 if (lastpt->corner == POINTTYPE_CURVE_END)
3589 if (pt->corner == POINTTYPE_CURVE_START)
3590 pt->corner = POINTTYPE_CURVE_MIDDLE;
3592 pt->corner = POINTTYPE_CURVE_END;
3595 lastpt = &outline->items[outline->count - 1];
3597 /* outline closed with a line from end to start point */
3598 attempt_line_merge(outline, outline->count - 1, &pt->pos, FALSE, cos_table);
3600 lastpt = &outline->items[0];
3601 to_curve = lastpt->corner != POINTTYPE_CORNER && lastpt->corner != POINTTYPE_CURVE_END;
3602 if (lastpt->corner == POINTTYPE_CURVE_START)
3603 lastpt->corner = POINTTYPE_CORNER;
3604 pt = &outline->items[1];
3605 if (attempt_line_merge(outline, 0, &pt->pos, to_curve, cos_table))
3606 *lastpt = outline->items[outline->count];
3609 lastpt = &outline->items[outline->count - 1];
3610 pt = &outline->items[0];
3611 unit_vec2(&lastdir, &lastpt->pos, &pt->pos);
3612 for (j = 0; j < outline->count; j++)
3617 pt = &outline->items[(j + 1) % outline->count];
3618 unit_vec2(&curdir, &lastpt->pos, &pt->pos);
3620 switch (lastpt->corner)
3622 case POINTTYPE_CURVE_START:
3623 case POINTTYPE_CURVE_END:
3624 if (!is_direction_similar(&lastdir, &curdir, cos_table->cos_45))
3625 lastpt->corner = POINTTYPE_CORNER;
3627 case POINTTYPE_CURVE_MIDDLE:
3628 if (!is_direction_similar(&lastdir, &curdir, cos_table->cos_90))
3629 lastpt->corner = POINTTYPE_CORNER;
3631 lastpt->corner = POINTTYPE_CURVE;
3639 header = (TTPOLYGONHEADER *)((char *)header + header->cb);
3644 /* Get the y-distance from a line to a point */
3645 static float get_line_to_point_y_distance(D3DXVECTOR2 *line_pt1,
3646 D3DXVECTOR2 *line_pt2,
3649 D3DXVECTOR2 line_vec = {0, 0};
3653 D3DXVec2Subtract(&line_vec, line_pt2, line_pt1);
3654 line_pt_dx = point->x - line_pt1->x;
3655 line_y = line_pt1->y + (line_vec.y * line_pt_dx) / line_vec.x;
3656 return point->y - line_y;
3659 static D3DXVECTOR2 *get_indexed_point(struct point2d_index *pt_idx)
3661 return &pt_idx->outline->items[pt_idx->vertex].pos;
3664 static D3DXVECTOR2 *get_ordered_vertex(struct glyphinfo *glyph, WORD index)
3666 return get_indexed_point(&glyph->ordered_vertices.items[index]);
3669 static void remove_triangulation(struct triangulation_array *array, struct triangulation *item)
3671 HeapFree(GetProcessHeap(), 0, item->vertex_stack.items);
3672 MoveMemory(item, item + 1, (char*)&array->items[array->count] - (char*)(item + 1));
3676 static HRESULT triangulation_add_point(struct triangulation **t_ptr,
3677 struct triangulation_array *triangulations,
3681 struct glyphinfo *glyph = triangulations->glyph;
3682 struct triangulation *t = *t_ptr;
3687 if (t->last_on_top) {
3695 if (t->last_on_top != to_top && t->vertex_stack.count > 1) {
3696 /* consume all vertices on the stack */
3697 WORD last_pt = t->vertex_stack.items[0];
3699 for (i = 1; i < t->vertex_stack.count; i++)
3701 face = add_face(&glyph->faces);
3702 if (!face) return E_OUTOFMEMORY;
3703 (*face)[0] = vtx_idx;
3704 (*face)[f1] = last_pt;
3705 (*face)[f2] = last_pt = t->vertex_stack.items[i];
3707 t->vertex_stack.items[0] = last_pt;
3708 t->vertex_stack.count = 1;
3709 } else if (t->vertex_stack.count > 1) {
3710 int i = t->vertex_stack.count - 1;
3711 D3DXVECTOR2 *point = get_ordered_vertex(glyph, vtx_idx);
3712 WORD top_idx = t->vertex_stack.items[i--];
3713 D3DXVECTOR2 *top_pt = get_ordered_vertex(glyph, top_idx);
3717 WORD prev_idx = t->vertex_stack.items[i--];
3718 D3DXVECTOR2 *prev_pt = get_ordered_vertex(glyph, prev_idx);
3720 if (prev_pt->x != top_pt->x &&
3721 ((to_top && get_line_to_point_y_distance(prev_pt, top_pt, point) > 0) ||
3722 (!to_top && get_line_to_point_y_distance(prev_pt, top_pt, point) < 0)))
3725 face = add_face(&glyph->faces);
3726 if (!face) return E_OUTOFMEMORY;
3727 (*face)[0] = vtx_idx;
3728 (*face)[f1] = prev_idx;
3729 (*face)[f2] = top_idx;
3733 t->vertex_stack.count--;
3736 t->last_on_top = to_top;
3738 hr = add_vertex_index(&t->vertex_stack, vtx_idx);
3740 if (hr == S_OK && t->merging) {
3741 struct triangulation *t2;
3743 t2 = to_top ? t - 1 : t + 1;
3744 t2->merging = FALSE;
3745 hr = triangulation_add_point(&t2, triangulations, vtx_idx, to_top);
3746 if (hr != S_OK) return hr;
3747 remove_triangulation(triangulations, t);
3755 /* check if the point is next on the outline for either the top or bottom */
3756 static D3DXVECTOR2 *triangulation_get_next_point(struct triangulation *t, struct glyphinfo *glyph, BOOL on_top)
3758 int i = t->last_on_top == on_top ? t->vertex_stack.count - 1 : 0;
3759 WORD idx = t->vertex_stack.items[i];
3760 struct point2d_index *pt_idx = &glyph->ordered_vertices.items[idx];
3761 struct outline *outline = pt_idx->outline;
3764 i = (pt_idx->vertex + outline->count - 1) % outline->count;
3766 i = (pt_idx->vertex + 1) % outline->count;
3768 return &outline->items[i].pos;
3771 static int compare_vertex_indices(const void *a, const void *b)
3773 const struct point2d_index *idx1 = a, *idx2 = b;
3774 const D3DXVECTOR2 *p1 = &idx1->outline->items[idx1->vertex].pos;
3775 const D3DXVECTOR2 *p2 = &idx2->outline->items[idx2->vertex].pos;
3776 float diff = p1->x - p2->x;
3779 diff = p1->y - p2->y;
3781 return diff == 0.0f ? 0 : (diff > 0.0f ? -1 : 1);
3784 static HRESULT triangulate(struct triangulation_array *triangulations)
3788 struct glyphinfo *glyph = triangulations->glyph;
3789 int nb_vertices = 0;
3791 struct point2d_index *idx_ptr;
3793 for (i = 0; i < glyph->outlines.count; i++)
3794 nb_vertices += glyph->outlines.items[i].count;
3796 glyph->ordered_vertices.items = HeapAlloc(GetProcessHeap(), 0,
3797 nb_vertices * sizeof(*glyph->ordered_vertices.items));
3798 if (!glyph->ordered_vertices.items)
3799 return E_OUTOFMEMORY;
3801 idx_ptr = glyph->ordered_vertices.items;
3802 for (i = 0; i < glyph->outlines.count; i++)
3804 struct outline *outline = &glyph->outlines.items[i];
3807 idx_ptr->outline = outline;
3808 idx_ptr->vertex = 0;
3810 for (j = outline->count - 1; j > 0; j--)
3812 idx_ptr->outline = outline;
3813 idx_ptr->vertex = j;
3817 glyph->ordered_vertices.count = nb_vertices;
3819 /* Native implementation seems to try to create a triangle fan from
3820 * the first outline point if the glyph only has one outline. */
3821 if (glyph->outlines.count == 1)
3823 struct outline *outline = glyph->outlines.items;
3824 D3DXVECTOR2 *base = &outline->items[0].pos;
3825 D3DXVECTOR2 *last = &outline->items[1].pos;
3828 for (i = 2; i < outline->count; i++)
3830 D3DXVECTOR2 *next = &outline->items[i].pos;
3831 D3DXVECTOR2 v1 = {0.0f, 0.0f};
3832 D3DXVECTOR2 v2 = {0.0f, 0.0f};
3834 D3DXVec2Subtract(&v1, base, last);
3835 D3DXVec2Subtract(&v2, last, next);
3836 ccw = D3DXVec2CCW(&v1, &v2);
3844 glyph->faces.items = HeapAlloc(GetProcessHeap(), 0,
3845 (outline->count - 2) * sizeof(glyph->faces.items[0]));
3846 if (!glyph->faces.items)
3847 return E_OUTOFMEMORY;
3849 glyph->faces.count = outline->count - 2;
3850 for (i = 0; i < glyph->faces.count; i++)
3852 glyph->faces.items[i][0] = 0;
3853 glyph->faces.items[i][1] = i + 1;
3854 glyph->faces.items[i][2] = i + 2;
3860 /* Perform 2D polygon triangulation for complex glyphs.
3861 * Triangulation is performed using a sweep line concept, from right to left,
3862 * by processing vertices in sorted order. Complex polygons are split into
3863 * monotone polygons which are triangulated separately. */
3864 /* FIXME: The order of the faces is not consistent with the native implementation. */
3866 /* Reserve space for maximum possible faces from triangulation.
3867 * # faces for outer outlines = outline->count - 2
3868 * # faces for inner outlines = outline->count + 2
3869 * There must be at least 1 outer outline. */
3870 glyph->faces.items = HeapAlloc(GetProcessHeap(), 0,
3871 (nb_vertices + glyph->outlines.count * 2 - 4) * sizeof(glyph->faces.items[0]));
3872 if (!glyph->faces.items)
3873 return E_OUTOFMEMORY;
3875 qsort(glyph->ordered_vertices.items, nb_vertices,
3876 sizeof(glyph->ordered_vertices.items[0]), compare_vertex_indices);
3877 for (sweep_idx = 0; sweep_idx < glyph->ordered_vertices.count; sweep_idx++)
3880 int end = triangulations->count;
3884 D3DXVECTOR2 *sweep_vtx = get_ordered_vertex(glyph, sweep_idx);
3885 int current = (start + end) / 2;
3886 struct triangulation *t = &triangulations->items[current];
3887 BOOL on_top_outline = FALSE;
3888 D3DXVECTOR2 *top_next, *bottom_next;
3889 WORD top_idx, bottom_idx;
3891 if (t->merging && t->last_on_top)
3892 top_next = triangulation_get_next_point(t + 1, glyph, TRUE);
3894 top_next = triangulation_get_next_point(t, glyph, TRUE);
3895 if (sweep_vtx == top_next)
3897 if (t->merging && t->last_on_top)
3899 hr = triangulation_add_point(&t, triangulations, sweep_idx, TRUE);
3900 if (hr != S_OK) return hr;
3902 if (t + 1 < &triangulations->items[triangulations->count] &&
3903 triangulation_get_next_point(t + 1, glyph, FALSE) == sweep_vtx)
3905 /* point also on bottom outline of higher triangulation */
3906 struct triangulation *t2 = t + 1;
3907 hr = triangulation_add_point(&t2, triangulations, sweep_idx, FALSE);
3908 if (hr != S_OK) return hr;
3913 on_top_outline = TRUE;
3916 if (t->merging && !t->last_on_top)
3917 bottom_next = triangulation_get_next_point(t - 1, glyph, FALSE);
3919 bottom_next = triangulation_get_next_point(t, glyph, FALSE);
3920 if (sweep_vtx == bottom_next)
3922 if (t->merging && !t->last_on_top)
3924 if (on_top_outline) {
3925 /* outline finished */
3926 remove_triangulation(triangulations, t);
3930 hr = triangulation_add_point(&t, triangulations, sweep_idx, FALSE);
3931 if (hr != S_OK) return hr;
3933 if (t > triangulations->items &&
3934 triangulation_get_next_point(t - 1, glyph, TRUE) == sweep_vtx)
3936 struct triangulation *t2 = t - 1;
3937 /* point also on top outline of lower triangulation */
3938 hr = triangulation_add_point(&t2, triangulations, sweep_idx, TRUE);
3939 if (hr != S_OK) return hr;
3940 t = t2 + 1; /* t may be invalidated by triangulation merging */
3950 if (t->last_on_top) {
3951 top_idx = t->vertex_stack.items[t->vertex_stack.count - 1];
3952 bottom_idx = t->vertex_stack.items[0];
3954 top_idx = t->vertex_stack.items[0];
3955 bottom_idx = t->vertex_stack.items[t->vertex_stack.count - 1];
3958 /* check if the point is inside or outside this polygon */
3959 if (get_line_to_point_y_distance(get_ordered_vertex(glyph, top_idx),
3960 top_next, sweep_vtx) > 0)
3962 start = current + 1;
3963 } else if (get_line_to_point_y_distance(get_ordered_vertex(glyph, bottom_idx),
3964 bottom_next, sweep_vtx) < 0)
3967 } else if (t->merging) {
3968 /* inside, so cancel merging */
3969 struct triangulation *t2 = t->last_on_top ? t + 1 : t - 1;
3971 t2->merging = FALSE;
3972 hr = triangulation_add_point(&t, triangulations, sweep_idx, t->last_on_top);
3973 if (hr != S_OK) return hr;
3974 hr = triangulation_add_point(&t2, triangulations, sweep_idx, t2->last_on_top);
3975 if (hr != S_OK) return hr;
3978 /* inside, so split polygon into two monotone parts */
3979 struct triangulation *t2 = add_triangulation(triangulations);
3980 if (!t2) return E_OUTOFMEMORY;
3981 MoveMemory(t + 1, t, (char*)(t2 + 1) - (char*)t);
3982 if (t->last_on_top) {
3989 ZeroMemory(&t2->vertex_stack, sizeof(t2->vertex_stack));
3990 hr = add_vertex_index(&t2->vertex_stack, t->vertex_stack.items[t->vertex_stack.count - 1]);
3991 if (hr != S_OK) return hr;
3992 hr = add_vertex_index(&t2->vertex_stack, sweep_idx);
3993 if (hr != S_OK) return hr;
3994 t2->last_on_top = !t->last_on_top;
3996 hr = triangulation_add_point(&t, triangulations, sweep_idx, t->last_on_top);
3997 if (hr != S_OK) return hr;
4003 struct triangulation *t;
4004 struct triangulation *t2 = add_triangulation(triangulations);
4005 if (!t2) return E_OUTOFMEMORY;
4006 t = &triangulations->items[start];
4007 MoveMemory(t + 1, t, (char*)(t2 + 1) - (char*)t);
4008 ZeroMemory(t, sizeof(*t));
4009 hr = add_vertex_index(&t->vertex_stack, sweep_idx);
4010 if (hr != S_OK) return hr;
4016 HRESULT WINAPI D3DXCreateTextW(LPDIRECT3DDEVICE9 device,
4017 HDC hdc, LPCWSTR text,
4018 FLOAT deviation, FLOAT extrusion,
4019 LPD3DXMESH *mesh_ptr, LPD3DXBUFFER *adjacency,
4020 LPGLYPHMETRICSFLOAT glyphmetrics)
4023 ID3DXMesh *mesh = NULL;
4024 DWORD nb_vertices, nb_faces;
4025 DWORD nb_front_faces, nb_corners, nb_outline_points;
4026 struct vertex *vertices = NULL;
4031 OUTLINETEXTMETRICW otm;
4032 HFONT font = NULL, oldfont = NULL;
4033 const MAT2 identity = {{0, 1}, {0, 0}, {0, 0}, {0, 1}};
4034 void *raw_outline = NULL;
4036 struct glyphinfo *glyphs = NULL;
4038 struct triangulation_array triangulations = {0, 0, NULL};
4040 struct vertex *vertex_ptr;
4042 float max_deviation_sq;
4043 const struct cos_table cos_table = {
4044 cos(D3DXToRadian(0.5f)),
4045 cos(D3DXToRadian(45.0f)),
4046 cos(D3DXToRadian(90.0f)),
4050 TRACE("(%p, %p, %s, %f, %f, %p, %p, %p)\n", device, hdc,
4051 debugstr_w(text), deviation, extrusion, mesh_ptr, adjacency, glyphmetrics);
4053 if (!device || !hdc || !text || !*text || deviation < 0.0f || extrusion < 0.0f || !mesh_ptr)
4054 return D3DERR_INVALIDCALL;
4058 FIXME("Case of adjacency != NULL not implemented.\n");
4062 if (!GetObjectW(GetCurrentObject(hdc, OBJ_FONT), sizeof(lf), &lf) ||
4063 !GetOutlineTextMetricsW(hdc, sizeof(otm), &otm))
4065 return D3DERR_INVALIDCALL;
4068 if (deviation == 0.0f)
4069 deviation = 1.0f / otm.otmEMSquare;
4070 max_deviation_sq = deviation * deviation;
4072 lf.lfHeight = otm.otmEMSquare;
4074 font = CreateFontIndirectW(&lf);
4079 oldfont = SelectObject(hdc, font);
4081 textlen = strlenW(text);
4082 for (i = 0; i < textlen; i++)
4084 int datasize = GetGlyphOutlineW(hdc, text[i], GGO_NATIVE, &gm, 0, NULL, &identity);
4086 return D3DERR_INVALIDCALL;
4087 if (bufsize < datasize)
4090 if (!bufsize) { /* e.g. text == " " */
4091 hr = D3DERR_INVALIDCALL;
4095 glyphs = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, textlen * sizeof(*glyphs));
4096 raw_outline = HeapAlloc(GetProcessHeap(), 0, bufsize);
4097 if (!glyphs || !raw_outline) {
4103 for (i = 0; i < textlen; i++)
4105 /* get outline points from data returned from GetGlyphOutline */
4108 glyphs[i].offset_x = offset_x;
4110 datasize = GetGlyphOutlineW(hdc, text[i], GGO_NATIVE, &gm, bufsize, raw_outline, &identity);
4111 hr = create_outline(&glyphs[i], raw_outline, datasize,
4112 max_deviation_sq, otm.otmEMSquare, &cos_table);
4113 if (hr != S_OK) goto error;
4115 triangulations.glyph = &glyphs[i];
4116 hr = triangulate(&triangulations);
4117 if (hr != S_OK) goto error;
4118 if (triangulations.count) {
4119 ERR("%d incomplete triangulations of glyph (%u).\n", triangulations.count, text[i]);
4120 triangulations.count = 0;
4125 glyphmetrics[i].gmfBlackBoxX = gm.gmBlackBoxX / (float)otm.otmEMSquare;
4126 glyphmetrics[i].gmfBlackBoxY = gm.gmBlackBoxY / (float)otm.otmEMSquare;
4127 glyphmetrics[i].gmfptGlyphOrigin.x = gm.gmptGlyphOrigin.x / (float)otm.otmEMSquare;
4128 glyphmetrics[i].gmfptGlyphOrigin.y = gm.gmptGlyphOrigin.y / (float)otm.otmEMSquare;
4129 glyphmetrics[i].gmfCellIncX = gm.gmCellIncX / (float)otm.otmEMSquare;
4130 glyphmetrics[i].gmfCellIncY = gm.gmCellIncY / (float)otm.otmEMSquare;
4132 offset_x += gm.gmCellIncX / (float)otm.otmEMSquare;
4135 /* corner points need an extra vertex for the different side faces normals */
4137 nb_outline_points = 0;
4139 for (i = 0; i < textlen; i++)
4142 nb_outline_points += glyphs[i].ordered_vertices.count;
4143 nb_front_faces += glyphs[i].faces.count;
4144 for (j = 0; j < glyphs[i].outlines.count; j++)
4147 struct outline *outline = &glyphs[i].outlines.items[j];
4148 nb_corners++; /* first outline point always repeated as a corner */
4149 for (k = 1; k < outline->count; k++)
4150 if (outline->items[k].corner)
4155 nb_vertices = (nb_outline_points + nb_corners) * 2 + nb_outline_points * 2;
4156 nb_faces = nb_outline_points * 2 + nb_front_faces * 2;
4159 hr = D3DXCreateMeshFVF(nb_faces, nb_vertices, D3DXMESH_MANAGED,
4160 D3DFVF_XYZ | D3DFVF_NORMAL, device, &mesh);
4164 hr = mesh->lpVtbl->LockVertexBuffer(mesh, D3DLOCK_DISCARD, (LPVOID *)&vertices);
4168 hr = mesh->lpVtbl->LockIndexBuffer(mesh, D3DLOCK_DISCARD, (LPVOID *)&faces);
4172 /* convert 2D vertices and faces into 3D mesh */
4173 vertex_ptr = vertices;
4175 if (extrusion == 0.0f) {
4182 for (i = 0; i < textlen; i++)
4186 struct vertex *back_vertices;
4189 /* side vertices and faces */
4190 for (j = 0; j < glyphs[i].outlines.count; j++)
4192 struct vertex *outline_vertices = vertex_ptr;
4193 struct outline *outline = &glyphs[i].outlines.items[j];
4195 struct point2d *prevpt = &outline->items[outline->count - 1];
4196 struct point2d *pt = &outline->items[0];
4198 for (k = 1; k <= outline->count; k++)
4201 struct point2d *nextpt = &outline->items[k % outline->count];
4202 WORD vtx_idx = vertex_ptr - vertices;
4205 if (pt->corner == POINTTYPE_CURVE_START)
4206 D3DXVec2Subtract(&vec, &pt->pos, &prevpt->pos);
4207 else if (pt->corner)
4208 D3DXVec2Subtract(&vec, &nextpt->pos, &pt->pos);
4210 D3DXVec2Subtract(&vec, &nextpt->pos, &prevpt->pos);
4211 D3DXVec2Normalize(&vec, &vec);
4212 vtx.normal.x = -vec.y;
4213 vtx.normal.y = vec.x;
4216 vtx.position.x = pt->pos.x + glyphs[i].offset_x;
4217 vtx.position.y = pt->pos.y;
4219 *vertex_ptr++ = vtx;
4221 vtx.position.z = -extrusion;
4222 *vertex_ptr++ = vtx;
4224 vtx.position.x = nextpt->pos.x + glyphs[i].offset_x;
4225 vtx.position.y = nextpt->pos.y;
4226 if (pt->corner && nextpt->corner && nextpt->corner != POINTTYPE_CURVE_END) {
4227 vtx.position.z = -extrusion;
4228 *vertex_ptr++ = vtx;
4230 *vertex_ptr++ = vtx;
4232 (*face_ptr)[0] = vtx_idx;
4233 (*face_ptr)[1] = vtx_idx + 2;
4234 (*face_ptr)[2] = vtx_idx + 1;
4237 (*face_ptr)[0] = vtx_idx;
4238 (*face_ptr)[1] = vtx_idx + 3;
4239 (*face_ptr)[2] = vtx_idx + 2;
4242 if (nextpt->corner) {
4243 if (nextpt->corner == POINTTYPE_CURVE_END) {
4244 D3DXVECTOR2 *nextpt2 = &outline->items[(k + 1) % outline->count].pos;
4245 D3DXVec2Subtract(&vec, nextpt2, &nextpt->pos);
4247 D3DXVec2Subtract(&vec, &nextpt->pos, &pt->pos);
4249 D3DXVec2Normalize(&vec, &vec);
4250 vtx.normal.x = -vec.y;
4251 vtx.normal.y = vec.x;
4254 *vertex_ptr++ = vtx;
4255 vtx.position.z = -extrusion;
4256 *vertex_ptr++ = vtx;
4259 (*face_ptr)[0] = vtx_idx;
4260 (*face_ptr)[1] = vtx_idx + 3;
4261 (*face_ptr)[2] = vtx_idx + 1;
4264 (*face_ptr)[0] = vtx_idx;
4265 (*face_ptr)[1] = vtx_idx + 2;
4266 (*face_ptr)[2] = vtx_idx + 3;
4274 *vertex_ptr++ = *outline_vertices++;
4275 *vertex_ptr++ = *outline_vertices++;
4279 /* back vertices and faces */
4280 back_faces = face_ptr;
4281 back_vertices = vertex_ptr;
4282 for (j = 0; j < glyphs[i].ordered_vertices.count; j++)
4284 D3DXVECTOR2 *pt = get_ordered_vertex(&glyphs[i], j);
4285 vertex_ptr->position.x = pt->x + glyphs[i].offset_x;
4286 vertex_ptr->position.y = pt->y;
4287 vertex_ptr->position.z = 0;
4288 vertex_ptr->normal.x = 0;
4289 vertex_ptr->normal.y = 0;
4290 vertex_ptr->normal.z = 1;
4293 count = back_vertices - vertices;
4294 for (j = 0; j < glyphs[i].faces.count; j++)
4296 face *f = &glyphs[i].faces.items[j];
4297 (*face_ptr)[0] = (*f)[0] + count;
4298 (*face_ptr)[1] = (*f)[1] + count;
4299 (*face_ptr)[2] = (*f)[2] + count;
4303 /* front vertices and faces */
4304 j = count = vertex_ptr - back_vertices;
4307 vertex_ptr->position.x = back_vertices->position.x;
4308 vertex_ptr->position.y = back_vertices->position.y;
4309 vertex_ptr->position.z = -extrusion;
4310 vertex_ptr->normal.x = 0;
4311 vertex_ptr->normal.y = 0;
4312 vertex_ptr->normal.z = extrusion == 0.0f ? 1.0f : -1.0f;
4316 j = face_ptr - back_faces;
4319 (*face_ptr)[0] = (*back_faces)[0] + count;
4320 (*face_ptr)[1] = (*back_faces)[f1] + count;
4321 (*face_ptr)[2] = (*back_faces)[f2] + count;
4331 if (faces) mesh->lpVtbl->UnlockIndexBuffer(mesh);
4332 if (vertices) mesh->lpVtbl->UnlockVertexBuffer(mesh);
4333 if (hr != D3D_OK) mesh->lpVtbl->Release(mesh);
4336 for (i = 0; i < textlen; i++)
4339 for (j = 0; j < glyphs[i].outlines.count; j++)
4340 HeapFree(GetProcessHeap(), 0, glyphs[i].outlines.items[j].items);
4341 HeapFree(GetProcessHeap(), 0, glyphs[i].outlines.items);
4342 HeapFree(GetProcessHeap(), 0, glyphs[i].faces.items);
4343 HeapFree(GetProcessHeap(), 0, glyphs[i].ordered_vertices.items);
4345 HeapFree(GetProcessHeap(), 0, glyphs);
4347 if (triangulations.items) {
4349 for (i = 0; i < triangulations.count; i++)
4350 HeapFree(GetProcessHeap(), 0, triangulations.items[i].vertex_stack.items);
4351 HeapFree(GetProcessHeap(), 0, triangulations.items);
4353 HeapFree(GetProcessHeap(), 0, raw_outline);
4354 if (oldfont) SelectObject(hdc, oldfont);
4355 if (font) DeleteObject(font);