2 * Mesh operations specific to D3DX9.
4 * Copyright (C) 2005 Henri Verbeet
5 * Copyright (C) 2006 Ivan Gyurdiev
6 * Copyright (C) 2009 David Adam
7 * Copyright (C) 2010 Tony Wasserka
8 * Copyright (C) 2011 Dylan Smith
9 * Copyright (C) 2011 Michael Mc Donnell
11 * This library is free software; you can redistribute it and/or
12 * modify it under the terms of the GNU Lesser General Public
13 * License as published by the Free Software Foundation; either
14 * version 2.1 of the License, or (at your option) any later version.
16 * This library is distributed in the hope that it will be useful,
17 * but WITHOUT ANY WARRANTY; without even the implied warranty of
18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
19 * Lesser General Public License for more details.
21 * You should have received a copy of the GNU Lesser General Public
22 * License along with this library; if not, write to the Free Software
23 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
27 #include "wine/port.h"
30 #define NONAMELESSUNION
42 #include "wine/debug.h"
43 #include "wine/unicode.h"
44 #include "wine/list.h"
45 #include "d3dx9_36_private.h"
47 WINE_DEFAULT_DEBUG_CHANNEL(d3dx);
49 typedef struct ID3DXMeshImpl
51 ID3DXMesh ID3DXMesh_iface;
58 IDirect3DDevice9 *device;
59 D3DVERTEXELEMENT9 cached_declaration[MAX_FVF_DECL_SIZE];
60 IDirect3DVertexDeclaration9 *vertex_declaration;
61 UINT vertex_declaration_size;
63 IDirect3DVertexBuffer9 *vertex_buffer;
64 IDirect3DIndexBuffer9 *index_buffer;
66 int attrib_buffer_lock_count;
67 DWORD attrib_table_size;
68 D3DXATTRIBUTERANGE *attrib_table;
71 static inline ID3DXMeshImpl *impl_from_ID3DXMesh(ID3DXMesh *iface)
73 return CONTAINING_RECORD(iface, ID3DXMeshImpl, ID3DXMesh_iface);
76 static HRESULT WINAPI ID3DXMeshImpl_QueryInterface(ID3DXMesh *iface, REFIID riid, LPVOID *object)
78 ID3DXMeshImpl *This = impl_from_ID3DXMesh(iface);
80 TRACE("(%p)->(%s, %p)\n", This, debugstr_guid(riid), object);
82 if (IsEqualGUID(riid, &IID_IUnknown) ||
83 IsEqualGUID(riid, &IID_ID3DXBaseMesh) ||
84 IsEqualGUID(riid, &IID_ID3DXMesh))
86 iface->lpVtbl->AddRef(iface);
91 WARN("Interface %s not found.\n", debugstr_guid(riid));
96 static ULONG WINAPI ID3DXMeshImpl_AddRef(ID3DXMesh *iface)
98 ID3DXMeshImpl *This = impl_from_ID3DXMesh(iface);
100 TRACE("(%p)->(): AddRef from %d\n", This, This->ref);
102 return InterlockedIncrement(&This->ref);
105 static ULONG WINAPI ID3DXMeshImpl_Release(ID3DXMesh *iface)
107 ID3DXMeshImpl *This = impl_from_ID3DXMesh(iface);
108 ULONG ref = InterlockedDecrement(&This->ref);
110 TRACE("(%p)->(): Release from %d\n", This, ref + 1);
114 IDirect3DIndexBuffer9_Release(This->index_buffer);
115 IDirect3DVertexBuffer9_Release(This->vertex_buffer);
116 if (This->vertex_declaration)
117 IDirect3DVertexDeclaration9_Release(This->vertex_declaration);
118 IDirect3DDevice9_Release(This->device);
119 HeapFree(GetProcessHeap(), 0, This->attrib_buffer);
120 HeapFree(GetProcessHeap(), 0, This->attrib_table);
121 HeapFree(GetProcessHeap(), 0, This);
127 /*** ID3DXBaseMesh ***/
128 static HRESULT WINAPI ID3DXMeshImpl_DrawSubset(ID3DXMesh *iface, DWORD attrib_id)
130 ID3DXMeshImpl *This = impl_from_ID3DXMesh(iface);
136 TRACE("(%p)->(%u)\n", This, attrib_id);
138 if (!This->vertex_declaration)
140 WARN("Can't draw a mesh with an invalid vertex declaration.\n");
144 vertex_size = iface->lpVtbl->GetNumBytesPerVertex(iface);
146 hr = IDirect3DDevice9_SetVertexDeclaration(This->device, This->vertex_declaration);
147 if (FAILED(hr)) return hr;
148 hr = IDirect3DDevice9_SetStreamSource(This->device, 0, This->vertex_buffer, 0, vertex_size);
149 if (FAILED(hr)) return hr;
150 hr = IDirect3DDevice9_SetIndices(This->device, This->index_buffer);
151 if (FAILED(hr)) return hr;
153 while (face_end < This->numfaces)
155 for (face_start = face_end; face_start < This->numfaces; face_start++)
157 if (This->attrib_buffer[face_start] == attrib_id)
160 if (face_start >= This->numfaces)
162 for (face_end = face_start + 1; face_end < This->numfaces; face_end++)
164 if (This->attrib_buffer[face_end] != attrib_id)
168 hr = IDirect3DDevice9_DrawIndexedPrimitive(This->device, D3DPT_TRIANGLELIST,
169 0, 0, This->numvertices, face_start * 3, face_end - face_start);
170 if (FAILED(hr)) return hr;
176 static DWORD WINAPI ID3DXMeshImpl_GetNumFaces(ID3DXMesh *iface)
178 ID3DXMeshImpl *This = impl_from_ID3DXMesh(iface);
180 TRACE("(%p)\n", This);
182 return This->numfaces;
185 static DWORD WINAPI ID3DXMeshImpl_GetNumVertices(ID3DXMesh *iface)
187 ID3DXMeshImpl *This = impl_from_ID3DXMesh(iface);
189 TRACE("(%p)\n", This);
191 return This->numvertices;
194 static DWORD WINAPI ID3DXMeshImpl_GetFVF(ID3DXMesh *iface)
196 ID3DXMeshImpl *This = impl_from_ID3DXMesh(iface);
198 TRACE("(%p)\n", This);
203 static void copy_declaration(D3DVERTEXELEMENT9 *dst, const D3DVERTEXELEMENT9 *src, UINT num_elem)
205 memcpy(dst, src, num_elem * sizeof(*src));
208 static HRESULT WINAPI ID3DXMeshImpl_GetDeclaration(ID3DXMesh *iface, D3DVERTEXELEMENT9 declaration[MAX_FVF_DECL_SIZE])
210 ID3DXMeshImpl *This = impl_from_ID3DXMesh(iface);
212 TRACE("(%p)\n", This);
214 if (declaration == NULL) return D3DERR_INVALIDCALL;
216 copy_declaration(declaration, This->cached_declaration, This->num_elem);
221 static DWORD WINAPI ID3DXMeshImpl_GetNumBytesPerVertex(ID3DXMesh *iface)
223 ID3DXMeshImpl *This = impl_from_ID3DXMesh(iface);
225 TRACE("iface (%p)\n", This);
227 return This->vertex_declaration_size;
230 static DWORD WINAPI ID3DXMeshImpl_GetOptions(ID3DXMesh *iface)
232 ID3DXMeshImpl *This = impl_from_ID3DXMesh(iface);
234 TRACE("(%p)\n", This);
236 return This->options;
239 static HRESULT WINAPI ID3DXMeshImpl_GetDevice(ID3DXMesh *iface, LPDIRECT3DDEVICE9 *device)
241 ID3DXMeshImpl *This = impl_from_ID3DXMesh(iface);
243 TRACE("(%p)->(%p)\n", This, device);
245 if (device == NULL) return D3DERR_INVALIDCALL;
246 *device = This->device;
247 IDirect3DDevice9_AddRef(This->device);
252 static HRESULT WINAPI ID3DXMeshImpl_CloneMeshFVF(ID3DXMesh *iface, DWORD options, DWORD fvf, LPDIRECT3DDEVICE9 device, LPD3DXMESH *clone_mesh)
254 ID3DXMeshImpl *This = impl_from_ID3DXMesh(iface);
256 D3DVERTEXELEMENT9 declaration[MAX_FVF_DECL_SIZE];
258 TRACE("(%p)->(%x,%x,%p,%p)\n", This, options, fvf, device, clone_mesh);
260 hr = D3DXDeclaratorFromFVF(fvf, declaration);
261 if (FAILED(hr)) return hr;
263 return iface->lpVtbl->CloneMesh(iface, options, declaration, device, clone_mesh);
266 static HRESULT WINAPI ID3DXMeshImpl_CloneMesh(ID3DXMesh *iface, DWORD options, CONST D3DVERTEXELEMENT9 *declaration, LPDIRECT3DDEVICE9 device,
267 LPD3DXMESH *clone_mesh_out)
269 ID3DXMeshImpl *This = impl_from_ID3DXMesh(iface);
270 ID3DXMeshImpl *cloned_this;
271 ID3DXMesh *clone_mesh;
272 D3DVERTEXELEMENT9 orig_declaration[MAX_FVF_DECL_SIZE] = { D3DDECL_END() };
273 void *data_in, *data_out;
278 TRACE("(%p)->(%x,%p,%p,%p)\n", This, options, declaration, device, clone_mesh_out);
281 return D3DERR_INVALIDCALL;
283 hr = iface->lpVtbl->GetDeclaration(iface, orig_declaration);
284 if (FAILED(hr)) return hr;
286 for (i = 0; orig_declaration[i].Stream != 0xff; i++) {
287 if (memcmp(&orig_declaration[i], &declaration[i], sizeof(*declaration)))
289 FIXME("Vertex buffer conversion not implemented.\n");
294 hr = D3DXCreateMesh(This->numfaces, This->numvertices, options & ~D3DXMESH_VB_SHARE,
295 declaration, device, &clone_mesh);
296 if (FAILED(hr)) return hr;
298 cloned_this = impl_from_ID3DXMesh(clone_mesh);
299 vertex_size = clone_mesh->lpVtbl->GetNumBytesPerVertex(clone_mesh);
301 if (options & D3DXMESH_VB_SHARE) {
302 IDirect3DVertexBuffer9_AddRef(This->vertex_buffer);
303 /* FIXME: refactor to avoid creating a new vertex buffer */
304 IDirect3DVertexBuffer9_Release(cloned_this->vertex_buffer);
305 cloned_this->vertex_buffer = This->vertex_buffer;
307 hr = iface->lpVtbl->LockVertexBuffer(iface, D3DLOCK_READONLY, &data_in);
308 if (FAILED(hr)) goto error;
309 hr = clone_mesh->lpVtbl->LockVertexBuffer(clone_mesh, D3DLOCK_DISCARD, &data_out);
311 iface->lpVtbl->UnlockVertexBuffer(iface);
314 memcpy(data_out, data_in, This->numvertices * vertex_size);
315 clone_mesh->lpVtbl->UnlockVertexBuffer(clone_mesh);
316 iface->lpVtbl->UnlockVertexBuffer(iface);
319 hr = iface->lpVtbl->LockIndexBuffer(iface, D3DLOCK_READONLY, &data_in);
320 if (FAILED(hr)) goto error;
321 hr = clone_mesh->lpVtbl->LockIndexBuffer(clone_mesh, D3DLOCK_DISCARD, &data_out);
323 iface->lpVtbl->UnlockIndexBuffer(iface);
326 if ((options ^ This->options) & D3DXMESH_32BIT) {
327 if (options & D3DXMESH_32BIT) {
328 for (i = 0; i < This->numfaces * 3; i++)
329 ((DWORD*)data_out)[i] = ((WORD*)data_in)[i];
331 for (i = 0; i < This->numfaces * 3; i++)
332 ((WORD*)data_out)[i] = ((DWORD*)data_in)[i];
335 memcpy(data_out, data_in, This->numfaces * 3 * (options & D3DXMESH_32BIT ? 4 : 2));
337 clone_mesh->lpVtbl->UnlockIndexBuffer(clone_mesh);
338 iface->lpVtbl->UnlockIndexBuffer(iface);
340 memcpy(cloned_this->attrib_buffer, This->attrib_buffer, This->numfaces * sizeof(*This->attrib_buffer));
342 if (This->attrib_table_size)
344 cloned_this->attrib_table_size = This->attrib_table_size;
345 cloned_this->attrib_table = HeapAlloc(GetProcessHeap(), 0, This->attrib_table_size * sizeof(*This->attrib_table));
346 if (!cloned_this->attrib_table) {
350 memcpy(cloned_this->attrib_table, This->attrib_table, This->attrib_table_size * sizeof(*This->attrib_table));
353 *clone_mesh_out = clone_mesh;
357 IUnknown_Release(clone_mesh);
361 static HRESULT WINAPI ID3DXMeshImpl_GetVertexBuffer(ID3DXMesh *iface, LPDIRECT3DVERTEXBUFFER9 *vertex_buffer)
363 ID3DXMeshImpl *This = impl_from_ID3DXMesh(iface);
365 TRACE("(%p)->(%p)\n", This, vertex_buffer);
367 if (vertex_buffer == NULL) return D3DERR_INVALIDCALL;
368 *vertex_buffer = This->vertex_buffer;
369 IDirect3DVertexBuffer9_AddRef(This->vertex_buffer);
374 static HRESULT WINAPI ID3DXMeshImpl_GetIndexBuffer(ID3DXMesh *iface, LPDIRECT3DINDEXBUFFER9 *index_buffer)
376 ID3DXMeshImpl *This = impl_from_ID3DXMesh(iface);
378 TRACE("(%p)->(%p)\n", This, index_buffer);
380 if (index_buffer == NULL) return D3DERR_INVALIDCALL;
381 *index_buffer = This->index_buffer;
382 IDirect3DIndexBuffer9_AddRef(This->index_buffer);
387 static HRESULT WINAPI ID3DXMeshImpl_LockVertexBuffer(ID3DXMesh *iface, DWORD flags, LPVOID *data)
389 ID3DXMeshImpl *This = impl_from_ID3DXMesh(iface);
391 TRACE("(%p)->(%u,%p)\n", This, flags, data);
393 return IDirect3DVertexBuffer9_Lock(This->vertex_buffer, 0, 0, data, flags);
396 static HRESULT WINAPI ID3DXMeshImpl_UnlockVertexBuffer(ID3DXMesh *iface)
398 ID3DXMeshImpl *This = impl_from_ID3DXMesh(iface);
400 TRACE("(%p)\n", This);
402 return IDirect3DVertexBuffer9_Unlock(This->vertex_buffer);
405 static HRESULT WINAPI ID3DXMeshImpl_LockIndexBuffer(ID3DXMesh *iface, DWORD flags, LPVOID *data)
407 ID3DXMeshImpl *This = impl_from_ID3DXMesh(iface);
409 TRACE("(%p)->(%u,%p)\n", This, flags, data);
411 return IDirect3DIndexBuffer9_Lock(This->index_buffer, 0, 0, data, flags);
414 static HRESULT WINAPI ID3DXMeshImpl_UnlockIndexBuffer(ID3DXMesh *iface)
416 ID3DXMeshImpl *This = impl_from_ID3DXMesh(iface);
418 TRACE("(%p)\n", This);
420 return IDirect3DIndexBuffer9_Unlock(This->index_buffer);
423 static HRESULT WINAPI ID3DXMeshImpl_GetAttributeTable(ID3DXMesh *iface, D3DXATTRIBUTERANGE *attrib_table, DWORD *attrib_table_size)
425 ID3DXMeshImpl *This = impl_from_ID3DXMesh(iface);
427 TRACE("(%p)->(%p,%p)\n", This, attrib_table, attrib_table_size);
429 if (attrib_table_size)
430 *attrib_table_size = This->attrib_table_size;
433 CopyMemory(attrib_table, This->attrib_table, This->attrib_table_size * sizeof(*attrib_table));
448 struct edge_face *entries;
451 /* Builds up a map of which face a new edge belongs to. That way the adjacency
452 * of another edge can be looked up. An edge has an adjacent face if there
453 * is an edge going in the opposite direction in the map. For example if the
454 * edge (v1, v2) belongs to face 4, and there is a mapping (v2, v1)->7, then
455 * face 4 and 7 are adjacent.
457 * Each edge might have been replaced with another edge, or none at all. There
458 * is at most one edge to face mapping, i.e. an edge can only belong to one
461 static HRESULT init_edge_face_map(struct edge_face_map *edge_face_map, CONST DWORD *index_buffer, CONST DWORD *point_reps, CONST DWORD num_faces)
466 edge_face_map->lists = HeapAlloc(GetProcessHeap(), 0, 3 * num_faces * sizeof(*edge_face_map->lists));
467 if (!edge_face_map->lists) return E_OUTOFMEMORY;
469 edge_face_map->entries = HeapAlloc(GetProcessHeap(), 0, 3 * num_faces * sizeof(*edge_face_map->entries));
470 if (!edge_face_map->entries) return E_OUTOFMEMORY;
473 /* Initialize all lists */
474 for (i = 0; i < 3 * num_faces; i++)
476 list_init(&edge_face_map->lists[i]);
478 /* Build edge face mapping */
479 for (face = 0; face < num_faces; face++)
481 for (edge = 0; edge < 3; edge++)
483 DWORD v1 = index_buffer[3*face + edge];
484 DWORD v2 = index_buffer[3*face + (edge+1)%3];
485 DWORD new_v1 = point_reps[v1]; /* What v1 has been replaced with */
486 DWORD new_v2 = point_reps[v2];
488 if (v1 != v2) /* Only map non-collapsed edges */
491 edge_face_map->entries[i].v2 = new_v2;
492 edge_face_map->entries[i].face = face;
493 list_add_head(&edge_face_map->lists[new_v1], &edge_face_map->entries[i].entry);
501 static DWORD find_adjacent_face(struct edge_face_map *edge_face_map, DWORD vertex1, DWORD vertex2, CONST DWORD num_faces)
503 struct edge_face *edge_face_ptr;
505 LIST_FOR_EACH_ENTRY(edge_face_ptr, &edge_face_map->lists[vertex2], struct edge_face, entry)
507 if (edge_face_ptr->v2 == vertex1)
508 return edge_face_ptr->face;
514 static DWORD *generate_identity_point_reps(DWORD num_vertices)
516 DWORD *id_point_reps;
519 id_point_reps = HeapAlloc(GetProcessHeap(), 0, num_vertices * sizeof(*id_point_reps));
523 for (i = 0; i < num_vertices; i++)
525 id_point_reps[i] = i;
528 return id_point_reps;
531 static HRESULT WINAPI ID3DXMeshImpl_ConvertPointRepsToAdjacency(ID3DXMesh *iface, CONST DWORD *point_reps, DWORD *adjacency)
533 ID3DXMeshImpl *This = impl_from_ID3DXMesh(iface);
535 DWORD num_faces = iface->lpVtbl->GetNumFaces(iface);
536 DWORD num_vertices = iface->lpVtbl->GetNumVertices(iface);
537 DWORD options = iface->lpVtbl->GetOptions(iface);
538 BOOL indices_are_16_bit = !(options & D3DXMESH_32BIT);
543 struct edge_face_map edge_face_map = {0};
544 CONST DWORD *point_reps_ptr = NULL;
545 DWORD *id_point_reps = NULL;
547 TRACE("(%p)->(%p,%p)\n", This, point_reps, adjacency);
549 if (!adjacency) return D3DERR_INVALIDCALL;
551 if (!point_reps) /* Identity point reps */
553 id_point_reps = generate_identity_point_reps(num_vertices);
560 point_reps_ptr = id_point_reps;
564 point_reps_ptr = point_reps;
567 hr = iface->lpVtbl->LockIndexBuffer(iface, D3DLOCK_READONLY, &ib_ptr);
568 if (FAILED(hr)) goto cleanup;
570 if (indices_are_16_bit)
572 /* Widen 16 bit to 32 bit */
574 WORD *ib_16bit = ib_ptr;
575 ib = HeapAlloc(GetProcessHeap(), 0, 3 * num_faces * sizeof(DWORD));
581 for (i = 0; i < 3 * num_faces; i++)
591 hr = init_edge_face_map(&edge_face_map, ib, point_reps_ptr, num_faces);
592 if (FAILED(hr)) goto cleanup;
594 /* Create adjacency */
595 for (face = 0; face < num_faces; face++)
597 for (edge = 0; edge < 3; edge++)
599 DWORD v1 = ib[3*face + edge];
600 DWORD v2 = ib[3*face + (edge+1)%3];
601 DWORD new_v1 = point_reps_ptr[v1];
602 DWORD new_v2 = point_reps_ptr[v2];
605 adj_face = find_adjacent_face(&edge_face_map, new_v1, new_v2, num_faces);
606 adjacency[3*face + edge] = adj_face;
612 HeapFree(GetProcessHeap(), 0, id_point_reps);
613 if (indices_are_16_bit) HeapFree(GetProcessHeap(), 0, ib);
614 HeapFree(GetProcessHeap(), 0, edge_face_map.lists);
615 HeapFree(GetProcessHeap(), 0, edge_face_map.entries);
616 if(ib_ptr) iface->lpVtbl->UnlockIndexBuffer(iface);
620 /* ConvertAdjacencyToPointReps helper function.
622 * Goes around the edges of each face and replaces the vertices in any adjacent
623 * face's edge with its own vertices(if its vertices have a lower index). This
624 * way as few as possible low index vertices are shared among the faces. The
625 * re-ordered index buffer is stored in new_indices.
627 * The vertices in a point representation must be ordered sequentially, e.g.
628 * index 5 holds the index of the vertex that replaces vertex 5, i.e. if
629 * vertex 5 is replaced by vertex 3 then index 5 would contain 3. If no vertex
630 * replaces it, then it contains the same number as the index itself, e.g.
631 * index 5 would contain 5. */
632 static HRESULT propagate_face_vertices(CONST DWORD *adjacency, DWORD *point_reps,
633 CONST DWORD *indices, DWORD *new_indices,
634 CONST DWORD face, CONST DWORD numfaces)
636 const unsigned int VERTS_PER_FACE = 3;
637 DWORD edge, opp_edge;
638 DWORD face_base = VERTS_PER_FACE * face;
640 for (edge = 0; edge < VERTS_PER_FACE; edge++)
642 DWORD adj_face = adjacency[face_base + edge];
645 if (adj_face == -1) /* No adjacent face. */
647 else if (adj_face >= numfaces)
649 /* This throws exception on Windows */
650 WARN("Index out of bounds. Got %d expected less than %d.\n",
652 return D3DERR_INVALIDCALL;
654 adj_face_base = 3 * adj_face;
656 /* Find opposite edge in adjacent face. */
657 for (opp_edge = 0; opp_edge < VERTS_PER_FACE; opp_edge++)
659 DWORD opp_edge_index = adj_face_base + opp_edge;
660 if (adjacency[opp_edge_index] == face)
661 break; /* Found opposite edge. */
664 /* Replaces vertices in opposite edge with vertices from current edge. */
665 for (i = 0; i < 2; i++)
667 DWORD from = face_base + (edge + (1 - i)) % VERTS_PER_FACE;
668 DWORD to = adj_face_base + (opp_edge + i) % VERTS_PER_FACE;
670 /* Propagate lowest index. */
671 if (new_indices[to] > new_indices[from])
673 new_indices[to] = new_indices[from];
674 point_reps[indices[to]] = new_indices[from];
682 static HRESULT WINAPI ID3DXMeshImpl_ConvertAdjacencyToPointReps(ID3DXMesh *iface, CONST DWORD *adjacency, DWORD *point_reps)
687 DWORD *indices = NULL;
688 WORD *indices_16bit = NULL;
689 DWORD *new_indices = NULL;
690 const unsigned int VERTS_PER_FACE = 3;
692 ID3DXMeshImpl *This = impl_from_ID3DXMesh(iface);
694 TRACE("(%p)->(%p,%p)\n", This, adjacency, point_reps);
698 WARN("NULL adjacency.\n");
699 hr = D3DERR_INVALIDCALL;
705 WARN("NULL point_reps.\n");
706 hr = D3DERR_INVALIDCALL;
710 /* Should never happen as CreateMesh does not allow meshes with 0 faces */
711 if (This->numfaces == 0)
713 ERR("Number of faces was zero.\n");
714 hr = D3DERR_INVALIDCALL;
718 new_indices = HeapAlloc(GetProcessHeap(), 0, VERTS_PER_FACE * This->numfaces * sizeof(*indices));
725 if (This->options & D3DXMESH_32BIT)
727 hr = iface->lpVtbl->LockIndexBuffer(iface, D3DLOCK_READONLY, (void**)&indices);
728 if (FAILED(hr)) goto cleanup;
729 memcpy(new_indices, indices, VERTS_PER_FACE * This->numfaces * sizeof(*indices));
733 /* Make a widening copy of indices_16bit into indices and new_indices
734 * in order to re-use the helper function */
735 hr = iface->lpVtbl->LockIndexBuffer(iface, D3DLOCK_READONLY, (void**)&indices_16bit);
736 if (FAILED(hr)) goto cleanup;
737 indices = HeapAlloc(GetProcessHeap(), 0, VERTS_PER_FACE * This->numfaces * sizeof(*indices));
743 for (i = 0; i < VERTS_PER_FACE * This->numfaces; i++)
745 new_indices[i] = indices_16bit[i];
746 indices[i] = indices_16bit[i];
750 /* Vertices are ordered sequentially in the point representation. */
751 for (i = 0; i < This->numvertices; i++)
756 /* Propagate vertices with low indices so as few vertices as possible
757 * are used in the mesh.
759 for (face = 0; face < This->numfaces; face++)
761 hr = propagate_face_vertices(adjacency, point_reps, indices, new_indices, face, This->numfaces);
762 if (FAILED(hr)) goto cleanup;
764 /* Go in opposite direction to catch all face orderings */
765 for (face = 0; face < This->numfaces; face++)
767 hr = propagate_face_vertices(adjacency, point_reps,
768 indices, new_indices,
769 (This->numfaces - 1) - face, This->numfaces);
770 if (FAILED(hr)) goto cleanup;
775 if (This->options & D3DXMESH_32BIT)
777 if (indices) iface->lpVtbl->UnlockIndexBuffer(iface);
781 if (indices_16bit) iface->lpVtbl->UnlockIndexBuffer(iface);
782 HeapFree(GetProcessHeap(), 0, indices);
784 HeapFree(GetProcessHeap(), 0, new_indices);
788 struct vertex_metadata {
791 DWORD first_shared_index;
794 static int compare_vertex_keys(const void *a, const void *b)
796 const struct vertex_metadata *left = a;
797 const struct vertex_metadata *right = b;
798 if (left->key == right->key)
800 return left->key < right->key ? -1 : 1;
803 static HRESULT WINAPI ID3DXMeshImpl_GenerateAdjacency(ID3DXMesh *iface, FLOAT epsilon, DWORD *adjacency)
805 ID3DXMeshImpl *This = impl_from_ID3DXMesh(iface);
807 BYTE *vertices = NULL;
808 const DWORD *indices = NULL;
811 /* sort the vertices by (x + y + z) to quickly find coincident vertices */
812 struct vertex_metadata *sorted_vertices;
813 /* shared_indices links together identical indices in the index buffer so
814 * that adjacency checks can be limited to faces sharing a vertex */
815 DWORD *shared_indices = NULL;
816 const FLOAT epsilon_sq = epsilon * epsilon;
819 TRACE("(%p)->(%f,%p)\n", This, epsilon, adjacency);
822 return D3DERR_INVALIDCALL;
824 buffer_size = This->numfaces * 3 * sizeof(*shared_indices) + This->numvertices * sizeof(*sorted_vertices);
825 if (!(This->options & D3DXMESH_32BIT))
826 buffer_size += This->numfaces * 3 * sizeof(*indices);
827 shared_indices = HeapAlloc(GetProcessHeap(), 0, buffer_size);
829 return E_OUTOFMEMORY;
830 sorted_vertices = (struct vertex_metadata*)(shared_indices + This->numfaces * 3);
832 hr = iface->lpVtbl->LockVertexBuffer(iface, D3DLOCK_READONLY, (void**)&vertices);
833 if (FAILED(hr)) goto cleanup;
834 hr = iface->lpVtbl->LockIndexBuffer(iface, D3DLOCK_READONLY, (void**)&indices);
835 if (FAILED(hr)) goto cleanup;
837 if (!(This->options & D3DXMESH_32BIT)) {
838 const WORD *word_indices = (const WORD*)indices;
839 DWORD *dword_indices = (DWORD*)(sorted_vertices + This->numvertices);
840 indices = dword_indices;
841 for (i = 0; i < This->numfaces * 3; i++)
842 *dword_indices++ = *word_indices++;
845 vertex_size = iface->lpVtbl->GetNumBytesPerVertex(iface);
846 for (i = 0; i < This->numvertices; i++) {
847 D3DXVECTOR3 *vertex = (D3DXVECTOR3*)(vertices + vertex_size * i);
848 sorted_vertices[i].first_shared_index = -1;
849 sorted_vertices[i].key = vertex->x + vertex->y + vertex->z;
850 sorted_vertices[i].vertex_index = i;
852 for (i = 0; i < This->numfaces * 3; i++) {
853 DWORD *first_shared_index = &sorted_vertices[indices[i]].first_shared_index;
854 shared_indices[i] = *first_shared_index;
855 *first_shared_index = i;
858 qsort(sorted_vertices, This->numvertices, sizeof(*sorted_vertices), compare_vertex_keys);
860 for (i = 0; i < This->numvertices; i++) {
861 struct vertex_metadata *sorted_vertex_a = &sorted_vertices[i];
862 D3DXVECTOR3 *vertex_a = (D3DXVECTOR3*)(vertices + sorted_vertex_a->vertex_index * vertex_size);
863 DWORD shared_index_a = sorted_vertex_a->first_shared_index;
865 while (shared_index_a != -1) {
867 DWORD shared_index_b = shared_indices[shared_index_a];
868 struct vertex_metadata *sorted_vertex_b = sorted_vertex_a;
871 while (shared_index_b != -1) {
872 /* faces are adjacent if they have another coincident vertex */
873 DWORD base_a = (shared_index_a / 3) * 3;
874 DWORD base_b = (shared_index_b / 3) * 3;
875 BOOL adjacent = FALSE;
878 for (k = 0; k < 3; k++) {
879 if (adjacency[base_b + k] == shared_index_a / 3) {
885 for (k = 1; k <= 2; k++) {
886 DWORD vertex_index_a = base_a + (shared_index_a + k) % 3;
887 DWORD vertex_index_b = base_b + (shared_index_b + (3 - k)) % 3;
888 adjacent = indices[vertex_index_a] == indices[vertex_index_b];
889 if (!adjacent && epsilon >= 0.0f) {
890 D3DXVECTOR3 delta = {0.0f, 0.0f, 0.0f};
893 D3DXVec3Subtract(&delta,
894 (D3DXVECTOR3*)(vertices + indices[vertex_index_a] * vertex_size),
895 (D3DXVECTOR3*)(vertices + indices[vertex_index_b] * vertex_size));
896 length_sq = D3DXVec3LengthSq(&delta);
897 adjacent = epsilon == 0.0f ? length_sq == 0.0f : length_sq < epsilon_sq;
900 DWORD adj_a = base_a + 2 - (vertex_index_a + shared_index_a + 1) % 3;
901 DWORD adj_b = base_b + 2 - (vertex_index_b + shared_index_b + 1) % 3;
902 if (adjacency[adj_a] == -1 && adjacency[adj_b] == -1) {
903 adjacency[adj_a] = base_b / 3;
904 adjacency[adj_b] = base_a / 3;
911 shared_index_b = shared_indices[shared_index_b];
913 while (++j < This->numvertices) {
914 D3DXVECTOR3 *vertex_b;
917 if (sorted_vertex_b->key - sorted_vertex_a->key > epsilon * 3.0f) {
918 /* no more coincident vertices to try */
919 j = This->numvertices;
922 /* check for coincidence */
923 vertex_b = (D3DXVECTOR3*)(vertices + sorted_vertex_b->vertex_index * vertex_size);
924 if (fabsf(vertex_a->x - vertex_b->x) <= epsilon &&
925 fabsf(vertex_a->y - vertex_b->y) <= epsilon &&
926 fabsf(vertex_a->z - vertex_b->z) <= epsilon)
931 if (j >= This->numvertices)
933 shared_index_b = sorted_vertex_b->first_shared_index;
936 sorted_vertex_a->first_shared_index = shared_indices[sorted_vertex_a->first_shared_index];
937 shared_index_a = sorted_vertex_a->first_shared_index;
943 if (indices) iface->lpVtbl->UnlockIndexBuffer(iface);
944 if (vertices) iface->lpVtbl->UnlockVertexBuffer(iface);
945 HeapFree(GetProcessHeap(), 0, shared_indices);
949 static HRESULT WINAPI ID3DXMeshImpl_UpdateSemantics(ID3DXMesh *iface, D3DVERTEXELEMENT9 declaration[MAX_FVF_DECL_SIZE])
952 ID3DXMeshImpl *This = impl_from_ID3DXMesh(iface);
953 UINT vertex_declaration_size;
956 TRACE("(%p)->(%p)\n", This, declaration);
960 WARN("Invalid declaration. Can't use NULL declaration.\n");
961 return D3DERR_INVALIDCALL;
964 /* New declaration must be same size as original */
965 vertex_declaration_size = D3DXGetDeclVertexSize(declaration, declaration[0].Stream);
966 if (vertex_declaration_size != This->vertex_declaration_size)
968 WARN("Invalid declaration. New vertex size does not match the original vertex size.\n");
969 return D3DERR_INVALIDCALL;
972 /* New declaration must not contain non-zero Stream value */
973 for (i = 0; declaration[i].Stream != 0xff; i++)
975 if (declaration[i].Stream != 0)
977 WARN("Invalid declaration. New declaration contains non-zero Stream value.\n");
978 return D3DERR_INVALIDCALL;
982 This->num_elem = i + 1;
983 copy_declaration(This->cached_declaration, declaration, This->num_elem);
985 if (This->vertex_declaration)
986 IDirect3DVertexDeclaration9_Release(This->vertex_declaration);
988 /* An application can pass an invalid declaration to UpdateSemantics and
989 * still expect D3D_OK (see tests). If the declaration is invalid, then
990 * subsequent calls to DrawSubset will fail. This is handled by setting the
991 * vertex declaration to NULL.
992 * GetDeclaration, GetNumBytesPerVertex must, however, use the new
993 * invalid declaration. This is handled by them using the cached vertex
994 * declaration instead of the actual vertex declaration.
996 hr = IDirect3DDevice9_CreateVertexDeclaration(This->device,
998 &This->vertex_declaration);
1001 WARN("Using invalid declaration. Calls to DrawSubset will fail.\n");
1002 This->vertex_declaration = NULL;
1009 static HRESULT WINAPI ID3DXMeshImpl_LockAttributeBuffer(ID3DXMesh *iface, DWORD flags, DWORD **data)
1011 ID3DXMeshImpl *This = impl_from_ID3DXMesh(iface);
1013 TRACE("(%p)->(%u,%p)\n", This, flags, data);
1015 InterlockedIncrement(&This->attrib_buffer_lock_count);
1017 if (!(flags & D3DLOCK_READONLY)) {
1018 D3DXATTRIBUTERANGE *attrib_table = This->attrib_table;
1019 This->attrib_table_size = 0;
1020 This->attrib_table = NULL;
1021 HeapFree(GetProcessHeap(), 0, attrib_table);
1024 *data = This->attrib_buffer;
1029 static HRESULT WINAPI ID3DXMeshImpl_UnlockAttributeBuffer(ID3DXMesh *iface)
1031 ID3DXMeshImpl *This = impl_from_ID3DXMesh(iface);
1034 TRACE("(%p)\n", This);
1036 lock_count = InterlockedDecrement(&This->attrib_buffer_lock_count);
1038 if (lock_count < 0) {
1039 InterlockedIncrement(&This->attrib_buffer_lock_count);
1040 return D3DERR_INVALIDCALL;
1046 static HRESULT WINAPI ID3DXMeshImpl_Optimize(ID3DXMesh *iface, DWORD flags, CONST DWORD *adjacency_in, DWORD *adjacency_out,
1047 DWORD *face_remap, LPD3DXBUFFER *vertex_remap, LPD3DXMESH *opt_mesh)
1049 ID3DXMeshImpl *This = impl_from_ID3DXMesh(iface);
1051 D3DVERTEXELEMENT9 declaration[MAX_FVF_DECL_SIZE] = { D3DDECL_END() };
1052 ID3DXMesh *optimized_mesh;
1054 TRACE("(%p)->(%x,%p,%p,%p,%p,%p)\n", This, flags, adjacency_in, adjacency_out, face_remap, vertex_remap, opt_mesh);
1057 return D3DERR_INVALIDCALL;
1059 hr = iface->lpVtbl->GetDeclaration(iface, declaration);
1060 if (FAILED(hr)) return hr;
1062 hr = iface->lpVtbl->CloneMesh(iface, This->options, declaration, This->device, &optimized_mesh);
1063 if (FAILED(hr)) return hr;
1065 hr = optimized_mesh->lpVtbl->OptimizeInplace(optimized_mesh, flags, adjacency_in, adjacency_out, face_remap, vertex_remap);
1067 *opt_mesh = optimized_mesh;
1069 IUnknown_Release(optimized_mesh);
1073 /* Creates a vertex_remap that removes unused vertices.
1074 * Indices are updated according to the vertex_remap. */
1075 static HRESULT compact_mesh(ID3DXMeshImpl *This, DWORD *indices, DWORD *new_num_vertices, ID3DXBuffer **vertex_remap)
1078 DWORD *vertex_remap_ptr;
1079 DWORD num_used_vertices;
1082 hr = D3DXCreateBuffer(This->numvertices * sizeof(DWORD), vertex_remap);
1083 if (FAILED(hr)) return hr;
1084 vertex_remap_ptr = ID3DXBuffer_GetBufferPointer(*vertex_remap);
1086 for (i = 0; i < This->numfaces * 3; i++)
1087 vertex_remap_ptr[indices[i]] = 1;
1089 /* create old->new vertex mapping */
1090 num_used_vertices = 0;
1091 for (i = 0; i < This->numvertices; i++) {
1092 if (vertex_remap_ptr[i])
1093 vertex_remap_ptr[i] = num_used_vertices++;
1095 vertex_remap_ptr[i] = -1;
1097 /* convert indices */
1098 for (i = 0; i < This->numfaces * 3; i++)
1099 indices[i] = vertex_remap_ptr[indices[i]];
1101 /* create new->old vertex mapping */
1102 num_used_vertices = 0;
1103 for (i = 0; i < This->numvertices; i++) {
1104 if (vertex_remap_ptr[i] != -1)
1105 vertex_remap_ptr[num_used_vertices++] = i;
1107 for (i = num_used_vertices; i < This->numvertices; i++)
1108 vertex_remap_ptr[i] = -1;
1110 *new_num_vertices = num_used_vertices;
1115 /* count the number of unique attribute values in a sorted attribute buffer */
1116 static DWORD count_attributes(const DWORD *attrib_buffer, DWORD numfaces)
1118 DWORD last_attribute = attrib_buffer[0];
1119 DWORD attrib_table_size = 1;
1121 for (i = 1; i < numfaces; i++) {
1122 if (attrib_buffer[i] != last_attribute) {
1123 last_attribute = attrib_buffer[i];
1124 attrib_table_size++;
1127 return attrib_table_size;
1130 static void fill_attribute_table(DWORD *attrib_buffer, DWORD numfaces, void *indices,
1131 BOOL is_32bit_indices, D3DXATTRIBUTERANGE *attrib_table)
1133 DWORD attrib_table_size = 0;
1134 DWORD last_attribute = attrib_buffer[0];
1135 DWORD min_vertex, max_vertex;
1138 attrib_table[0].AttribId = last_attribute;
1139 attrib_table[0].FaceStart = 0;
1140 min_vertex = (DWORD)-1;
1142 for (i = 0; i < numfaces; i++) {
1145 if (attrib_buffer[i] != last_attribute) {
1146 last_attribute = attrib_buffer[i];
1147 attrib_table[attrib_table_size].FaceCount = i - attrib_table[attrib_table_size].FaceStart;
1148 attrib_table[attrib_table_size].VertexStart = min_vertex;
1149 attrib_table[attrib_table_size].VertexCount = max_vertex - min_vertex + 1;
1150 attrib_table_size++;
1151 attrib_table[attrib_table_size].AttribId = attrib_buffer[i];
1152 attrib_table[attrib_table_size].FaceStart = i;
1153 min_vertex = (DWORD)-1;
1156 for (j = 0; j < 3; j++) {
1157 DWORD vertex_index = is_32bit_indices ? ((DWORD*)indices)[i * 3 + j] : ((WORD*)indices)[i * 3 + j];
1158 if (vertex_index < min_vertex)
1159 min_vertex = vertex_index;
1160 if (vertex_index > max_vertex)
1161 max_vertex = vertex_index;
1164 attrib_table[attrib_table_size].FaceCount = i - attrib_table[attrib_table_size].FaceStart;
1165 attrib_table[attrib_table_size].VertexStart = min_vertex;
1166 attrib_table[attrib_table_size].VertexCount = max_vertex - min_vertex + 1;
1167 attrib_table_size++;
1170 static int attrib_entry_compare(const DWORD **a, const DWORD **b)
1172 const DWORD *ptr_a = *a;
1173 const DWORD *ptr_b = *b;
1174 int delta = *ptr_a - *ptr_b;
1179 delta = ptr_a - ptr_b; /* for stable sort */
1183 /* Create face_remap, a new attribute buffer for attribute sort optimization. */
1184 static HRESULT remap_faces_for_attrsort(ID3DXMeshImpl *This, const DWORD *indices,
1185 const DWORD *attrib_buffer, DWORD **sorted_attrib_buffer, DWORD **face_remap)
1187 const DWORD **sorted_attrib_ptr_buffer = NULL;
1190 *face_remap = HeapAlloc(GetProcessHeap(), 0, This->numfaces * sizeof(**face_remap));
1191 sorted_attrib_ptr_buffer = HeapAlloc(GetProcessHeap(), 0, This->numfaces * sizeof(*sorted_attrib_ptr_buffer));
1192 if (!*face_remap || !sorted_attrib_ptr_buffer) {
1193 HeapFree(GetProcessHeap(), 0, sorted_attrib_ptr_buffer);
1194 return E_OUTOFMEMORY;
1196 for (i = 0; i < This->numfaces; i++)
1197 sorted_attrib_ptr_buffer[i] = &attrib_buffer[i];
1198 qsort(sorted_attrib_ptr_buffer, This->numfaces, sizeof(*sorted_attrib_ptr_buffer),
1199 (int(*)(const void *, const void *))attrib_entry_compare);
1201 for (i = 0; i < This->numfaces; i++)
1203 DWORD old_face = sorted_attrib_ptr_buffer[i] - attrib_buffer;
1204 (*face_remap)[old_face] = i;
1207 /* overwrite sorted_attrib_ptr_buffer with the values themselves */
1208 *sorted_attrib_buffer = (DWORD*)sorted_attrib_ptr_buffer;
1209 for (i = 0; i < This->numfaces; i++)
1210 (*sorted_attrib_buffer)[(*face_remap)[i]] = attrib_buffer[i];
1215 static HRESULT WINAPI ID3DXMeshImpl_OptimizeInplace(ID3DXMesh *iface, DWORD flags, CONST DWORD *adjacency_in, DWORD *adjacency_out,
1216 DWORD *face_remap_out, LPD3DXBUFFER *vertex_remap_out)
1218 ID3DXMeshImpl *This = impl_from_ID3DXMesh(iface);
1219 void *indices = NULL;
1220 DWORD *attrib_buffer = NULL;
1222 ID3DXBuffer *vertex_remap = NULL;
1223 DWORD *face_remap = NULL; /* old -> new mapping */
1224 DWORD *dword_indices = NULL;
1225 DWORD new_num_vertices = 0;
1226 DWORD new_num_alloc_vertices = 0;
1227 IDirect3DVertexBuffer9 *vertex_buffer = NULL;
1228 DWORD *sorted_attrib_buffer = NULL;
1231 TRACE("(%p)->(%x,%p,%p,%p,%p)\n", This, flags, adjacency_in, adjacency_out, face_remap_out, vertex_remap_out);
1234 return D3DERR_INVALIDCALL;
1235 if (!adjacency_in && (flags & (D3DXMESHOPT_VERTEXCACHE | D3DXMESHOPT_STRIPREORDER)))
1236 return D3DERR_INVALIDCALL;
1237 if ((flags & (D3DXMESHOPT_VERTEXCACHE | D3DXMESHOPT_STRIPREORDER)) == (D3DXMESHOPT_VERTEXCACHE | D3DXMESHOPT_STRIPREORDER))
1238 return D3DERR_INVALIDCALL;
1240 if (flags & (D3DXMESHOPT_VERTEXCACHE | D3DXMESHOPT_STRIPREORDER))
1242 if (flags & D3DXMESHOPT_VERTEXCACHE)
1243 FIXME("D3DXMESHOPT_VERTEXCACHE not implemented.\n");
1244 if (flags & D3DXMESHOPT_STRIPREORDER)
1245 FIXME("D3DXMESHOPT_STRIPREORDER not implemented.\n");
1249 hr = iface->lpVtbl->LockIndexBuffer(iface, 0, &indices);
1250 if (FAILED(hr)) goto cleanup;
1252 dword_indices = HeapAlloc(GetProcessHeap(), 0, This->numfaces * 3 * sizeof(DWORD));
1253 if (!dword_indices) return E_OUTOFMEMORY;
1254 if (This->options & D3DXMESH_32BIT) {
1255 memcpy(dword_indices, indices, This->numfaces * 3 * sizeof(DWORD));
1257 WORD *word_indices = indices;
1258 for (i = 0; i < This->numfaces * 3; i++)
1259 dword_indices[i] = *word_indices++;
1262 if ((flags & (D3DXMESHOPT_COMPACT | D3DXMESHOPT_IGNOREVERTS | D3DXMESHOPT_ATTRSORT)) == D3DXMESHOPT_COMPACT)
1264 new_num_alloc_vertices = This->numvertices;
1265 hr = compact_mesh(This, dword_indices, &new_num_vertices, &vertex_remap);
1266 if (FAILED(hr)) goto cleanup;
1267 } else if (flags & D3DXMESHOPT_ATTRSORT) {
1268 if (!(flags & D3DXMESHOPT_IGNOREVERTS))
1270 FIXME("D3DXMESHOPT_ATTRSORT vertex reordering not implemented.\n");
1275 hr = iface->lpVtbl->LockAttributeBuffer(iface, 0, &attrib_buffer);
1276 if (FAILED(hr)) goto cleanup;
1278 hr = remap_faces_for_attrsort(This, dword_indices, attrib_buffer, &sorted_attrib_buffer, &face_remap);
1279 if (FAILED(hr)) goto cleanup;
1284 /* reorder the vertices using vertex_remap */
1285 D3DVERTEXBUFFER_DESC vertex_desc;
1286 DWORD *vertex_remap_ptr = ID3DXBuffer_GetBufferPointer(vertex_remap);
1287 DWORD vertex_size = iface->lpVtbl->GetNumBytesPerVertex(iface);
1288 BYTE *orig_vertices;
1291 hr = IDirect3DVertexBuffer9_GetDesc(This->vertex_buffer, &vertex_desc);
1292 if (FAILED(hr)) goto cleanup;
1294 hr = IDirect3DDevice9_CreateVertexBuffer(This->device, new_num_alloc_vertices * vertex_size,
1295 vertex_desc.Usage, This->fvf, vertex_desc.Pool, &vertex_buffer, NULL);
1296 if (FAILED(hr)) goto cleanup;
1298 hr = IDirect3DVertexBuffer9_Lock(This->vertex_buffer, 0, 0, (void**)&orig_vertices, D3DLOCK_READONLY);
1299 if (FAILED(hr)) goto cleanup;
1301 hr = IDirect3DVertexBuffer9_Lock(vertex_buffer, 0, 0, (void**)&new_vertices, 0);
1303 IDirect3DVertexBuffer9_Unlock(This->vertex_buffer);
1307 for (i = 0; i < new_num_vertices; i++)
1308 memcpy(new_vertices + i * vertex_size, orig_vertices + vertex_remap_ptr[i] * vertex_size, vertex_size);
1310 IDirect3DVertexBuffer9_Unlock(This->vertex_buffer);
1311 IDirect3DVertexBuffer9_Unlock(vertex_buffer);
1312 } else if (vertex_remap_out) {
1313 DWORD *vertex_remap_ptr;
1315 hr = D3DXCreateBuffer(This->numvertices * sizeof(DWORD), &vertex_remap);
1316 if (FAILED(hr)) goto cleanup;
1317 vertex_remap_ptr = ID3DXBuffer_GetBufferPointer(vertex_remap);
1318 for (i = 0; i < This->numvertices; i++)
1319 *vertex_remap_ptr++ = i;
1322 if (flags & D3DXMESHOPT_ATTRSORT)
1324 D3DXATTRIBUTERANGE *attrib_table;
1325 DWORD attrib_table_size;
1327 attrib_table_size = count_attributes(sorted_attrib_buffer, This->numfaces);
1328 attrib_table = HeapAlloc(GetProcessHeap(), 0, attrib_table_size * sizeof(*attrib_table));
1329 if (!attrib_table) {
1334 memcpy(attrib_buffer, sorted_attrib_buffer, This->numfaces * sizeof(*attrib_buffer));
1336 /* reorder the indices using face_remap */
1337 if (This->options & D3DXMESH_32BIT) {
1338 for (i = 0; i < This->numfaces; i++)
1339 memcpy((DWORD*)indices + face_remap[i] * 3, dword_indices + i * 3, 3 * sizeof(DWORD));
1341 WORD *word_indices = indices;
1342 for (i = 0; i < This->numfaces; i++) {
1343 DWORD new_pos = face_remap[i] * 3;
1344 DWORD old_pos = i * 3;
1345 word_indices[new_pos++] = dword_indices[old_pos++];
1346 word_indices[new_pos++] = dword_indices[old_pos++];
1347 word_indices[new_pos] = dword_indices[old_pos];
1351 fill_attribute_table(attrib_buffer, This->numfaces, indices,
1352 This->options & D3DXMESH_32BIT, attrib_table);
1354 HeapFree(GetProcessHeap(), 0, This->attrib_table);
1355 This->attrib_table = attrib_table;
1356 This->attrib_table_size = attrib_table_size;
1358 if (This->options & D3DXMESH_32BIT) {
1359 memcpy(indices, dword_indices, This->numfaces * 3 * sizeof(DWORD));
1361 WORD *word_indices = indices;
1362 for (i = 0; i < This->numfaces * 3; i++)
1363 *word_indices++ = dword_indices[i];
1367 if (adjacency_out) {
1369 for (i = 0; i < This->numfaces; i++) {
1370 DWORD old_pos = i * 3;
1371 DWORD new_pos = face_remap[i] * 3;
1372 adjacency_out[new_pos++] = face_remap[adjacency_in[old_pos++]];
1373 adjacency_out[new_pos++] = face_remap[adjacency_in[old_pos++]];
1374 adjacency_out[new_pos++] = face_remap[adjacency_in[old_pos++]];
1377 memcpy(adjacency_out, adjacency_in, This->numfaces * 3 * sizeof(*adjacency_out));
1380 if (face_remap_out) {
1382 for (i = 0; i < This->numfaces; i++)
1383 face_remap_out[face_remap[i]] = i;
1385 for (i = 0; i < This->numfaces; i++)
1386 face_remap_out[i] = i;
1389 if (vertex_remap_out)
1390 *vertex_remap_out = vertex_remap;
1391 vertex_remap = NULL;
1393 if (vertex_buffer) {
1394 IDirect3DVertexBuffer9_Release(This->vertex_buffer);
1395 This->vertex_buffer = vertex_buffer;
1396 vertex_buffer = NULL;
1397 This->numvertices = new_num_vertices;
1402 HeapFree(GetProcessHeap(), 0, sorted_attrib_buffer);
1403 HeapFree(GetProcessHeap(), 0, face_remap);
1404 HeapFree(GetProcessHeap(), 0, dword_indices);
1405 if (vertex_remap) ID3DXBuffer_Release(vertex_remap);
1406 if (vertex_buffer) IDirect3DVertexBuffer9_Release(vertex_buffer);
1407 if (attrib_buffer) iface->lpVtbl->UnlockAttributeBuffer(iface);
1408 if (indices) iface->lpVtbl->UnlockIndexBuffer(iface);
1412 static HRESULT WINAPI ID3DXMeshImpl_SetAttributeTable(ID3DXMesh *iface, CONST D3DXATTRIBUTERANGE *attrib_table, DWORD attrib_table_size)
1414 ID3DXMeshImpl *This = impl_from_ID3DXMesh(iface);
1415 D3DXATTRIBUTERANGE *new_table = NULL;
1417 TRACE("(%p)->(%p,%u)\n", This, attrib_table, attrib_table_size);
1419 if (attrib_table_size) {
1420 size_t size = attrib_table_size * sizeof(*attrib_table);
1422 new_table = HeapAlloc(GetProcessHeap(), 0, size);
1424 return E_OUTOFMEMORY;
1426 CopyMemory(new_table, attrib_table, size);
1427 } else if (attrib_table) {
1428 return D3DERR_INVALIDCALL;
1430 HeapFree(GetProcessHeap(), 0, This->attrib_table);
1431 This->attrib_table = new_table;
1432 This->attrib_table_size = attrib_table_size;
1437 static const struct ID3DXMeshVtbl D3DXMesh_Vtbl =
1439 /*** IUnknown methods ***/
1440 ID3DXMeshImpl_QueryInterface,
1441 ID3DXMeshImpl_AddRef,
1442 ID3DXMeshImpl_Release,
1443 /*** ID3DXBaseMesh ***/
1444 ID3DXMeshImpl_DrawSubset,
1445 ID3DXMeshImpl_GetNumFaces,
1446 ID3DXMeshImpl_GetNumVertices,
1447 ID3DXMeshImpl_GetFVF,
1448 ID3DXMeshImpl_GetDeclaration,
1449 ID3DXMeshImpl_GetNumBytesPerVertex,
1450 ID3DXMeshImpl_GetOptions,
1451 ID3DXMeshImpl_GetDevice,
1452 ID3DXMeshImpl_CloneMeshFVF,
1453 ID3DXMeshImpl_CloneMesh,
1454 ID3DXMeshImpl_GetVertexBuffer,
1455 ID3DXMeshImpl_GetIndexBuffer,
1456 ID3DXMeshImpl_LockVertexBuffer,
1457 ID3DXMeshImpl_UnlockVertexBuffer,
1458 ID3DXMeshImpl_LockIndexBuffer,
1459 ID3DXMeshImpl_UnlockIndexBuffer,
1460 ID3DXMeshImpl_GetAttributeTable,
1461 ID3DXMeshImpl_ConvertPointRepsToAdjacency,
1462 ID3DXMeshImpl_ConvertAdjacencyToPointReps,
1463 ID3DXMeshImpl_GenerateAdjacency,
1464 ID3DXMeshImpl_UpdateSemantics,
1466 ID3DXMeshImpl_LockAttributeBuffer,
1467 ID3DXMeshImpl_UnlockAttributeBuffer,
1468 ID3DXMeshImpl_Optimize,
1469 ID3DXMeshImpl_OptimizeInplace,
1470 ID3DXMeshImpl_SetAttributeTable
1473 /*************************************************************************
1476 BOOL WINAPI D3DXBoxBoundProbe(CONST D3DXVECTOR3 *pmin, CONST D3DXVECTOR3 *pmax, CONST D3DXVECTOR3 *prayposition, CONST D3DXVECTOR3 *praydirection)
1478 /* Algorithm taken from the article: An Efficient and Robust Ray-Box Intersection Algoritm
1479 Amy Williams University of Utah
1480 Steve Barrus University of Utah
1481 R. Keith Morley University of Utah
1482 Peter Shirley University of Utah
1484 International Conference on Computer Graphics and Interactive Techniques archive
1485 ACM SIGGRAPH 2005 Courses
1486 Los Angeles, California
1488 This algorithm is free of patents or of copyrights, as confirmed by Peter Shirley himself.
1490 Algorithm: Consider the box as the intersection of three slabs. Clip the ray
1491 against each slab, if there's anything left of the ray after we're
1492 done we've got an intersection of the ray with the box.
1496 FLOAT div, tmin, tmax, tymin, tymax, tzmin, tzmax;
1498 div = 1.0f / praydirection->x;
1501 tmin = ( pmin->x - prayposition->x ) * div;
1502 tmax = ( pmax->x - prayposition->x ) * div;
1506 tmin = ( pmax->x - prayposition->x ) * div;
1507 tmax = ( pmin->x - prayposition->x ) * div;
1510 if ( tmax < 0.0f ) return FALSE;
1512 div = 1.0f / praydirection->y;
1515 tymin = ( pmin->y - prayposition->y ) * div;
1516 tymax = ( pmax->y - prayposition->y ) * div;
1520 tymin = ( pmax->y - prayposition->y ) * div;
1521 tymax = ( pmin->y - prayposition->y ) * div;
1524 if ( ( tymax < 0.0f ) || ( tmin > tymax ) || ( tymin > tmax ) ) return FALSE;
1526 if ( tymin > tmin ) tmin = tymin;
1527 if ( tymax < tmax ) tmax = tymax;
1529 div = 1.0f / praydirection->z;
1532 tzmin = ( pmin->z - prayposition->z ) * div;
1533 tzmax = ( pmax->z - prayposition->z ) * div;
1537 tzmin = ( pmax->z - prayposition->z ) * div;
1538 tzmax = ( pmin->z - prayposition->z ) * div;
1541 if ( (tzmax < 0.0f ) || ( tmin > tzmax ) || ( tzmin > tmax ) ) return FALSE;
1546 /*************************************************************************
1547 * D3DXComputeBoundingBox
1549 HRESULT WINAPI D3DXComputeBoundingBox(CONST D3DXVECTOR3 *pfirstposition, DWORD numvertices, DWORD dwstride, D3DXVECTOR3 *pmin, D3DXVECTOR3 *pmax)
1554 if( !pfirstposition || !pmin || !pmax ) return D3DERR_INVALIDCALL;
1556 *pmin = *pfirstposition;
1559 for(i=0; i<numvertices; i++)
1561 vec = *( (const D3DXVECTOR3*)((const char*)pfirstposition + dwstride * i) );
1563 if ( vec.x < pmin->x ) pmin->x = vec.x;
1564 if ( vec.x > pmax->x ) pmax->x = vec.x;
1566 if ( vec.y < pmin->y ) pmin->y = vec.y;
1567 if ( vec.y > pmax->y ) pmax->y = vec.y;
1569 if ( vec.z < pmin->z ) pmin->z = vec.z;
1570 if ( vec.z > pmax->z ) pmax->z = vec.z;
1576 /*************************************************************************
1577 * D3DXComputeBoundingSphere
1579 HRESULT WINAPI D3DXComputeBoundingSphere(CONST D3DXVECTOR3* pfirstposition, DWORD numvertices, DWORD dwstride, D3DXVECTOR3 *pcenter, FLOAT *pradius)
1581 D3DXVECTOR3 temp, temp1;
1585 if( !pfirstposition || !pcenter || !pradius ) return D3DERR_INVALIDCALL;
1593 for(i=0; i<numvertices; i++)
1595 D3DXVec3Add(&temp1, &temp, (const D3DXVECTOR3*)((const char*)pfirstposition + dwstride * i));
1599 D3DXVec3Scale(pcenter, &temp, 1.0f/((FLOAT)numvertices));
1601 for(i=0; i<numvertices; i++)
1603 d = D3DXVec3Length(D3DXVec3Subtract(&temp, (const D3DXVECTOR3*)((const char*)pfirstposition + dwstride * i), pcenter));
1604 if ( d > *pradius ) *pradius = d;
1609 static const UINT d3dx_decltype_size[D3DDECLTYPE_UNUSED] =
1611 /* D3DDECLTYPE_FLOAT1 */ 1 * 4,
1612 /* D3DDECLTYPE_FLOAT2 */ 2 * 4,
1613 /* D3DDECLTYPE_FLOAT3 */ 3 * 4,
1614 /* D3DDECLTYPE_FLOAT4 */ 4 * 4,
1615 /* D3DDECLTYPE_D3DCOLOR */ 4 * 1,
1616 /* D3DDECLTYPE_UBYTE4 */ 4 * 1,
1617 /* D3DDECLTYPE_SHORT2 */ 2 * 2,
1618 /* D3DDECLTYPE_SHORT4 */ 4 * 2,
1619 /* D3DDECLTYPE_UBYTE4N */ 4 * 1,
1620 /* D3DDECLTYPE_SHORT2N */ 2 * 2,
1621 /* D3DDECLTYPE_SHORT4N */ 4 * 2,
1622 /* D3DDECLTYPE_USHORT2N */ 2 * 2,
1623 /* D3DDECLTYPE_USHORT4N */ 4 * 2,
1624 /* D3DDECLTYPE_UDEC3 */ 4, /* 3 * 10 bits + 2 padding */
1625 /* D3DDECLTYPE_DEC3N */ 4,
1626 /* D3DDECLTYPE_FLOAT16_2 */ 2 * 2,
1627 /* D3DDECLTYPE_FLOAT16_4 */ 4 * 2,
1630 static void append_decl_element(D3DVERTEXELEMENT9 *declaration, UINT *idx, UINT *offset,
1631 D3DDECLTYPE type, D3DDECLUSAGE usage, UINT usage_idx)
1633 declaration[*idx].Stream = 0;
1634 declaration[*idx].Offset = *offset;
1635 declaration[*idx].Type = type;
1636 declaration[*idx].Method = D3DDECLMETHOD_DEFAULT;
1637 declaration[*idx].Usage = usage;
1638 declaration[*idx].UsageIndex = usage_idx;
1640 *offset += d3dx_decltype_size[type];
1644 /*************************************************************************
1645 * D3DXDeclaratorFromFVF
1647 HRESULT WINAPI D3DXDeclaratorFromFVF(DWORD fvf, D3DVERTEXELEMENT9 declaration[MAX_FVF_DECL_SIZE])
1649 static const D3DVERTEXELEMENT9 end_element = D3DDECL_END();
1650 DWORD tex_count = (fvf & D3DFVF_TEXCOUNT_MASK) >> D3DFVF_TEXCOUNT_SHIFT;
1651 unsigned int offset = 0;
1652 unsigned int idx = 0;
1655 TRACE("fvf %#x, declaration %p.\n", fvf, declaration);
1657 if (fvf & (D3DFVF_RESERVED0 | D3DFVF_RESERVED2)) return D3DERR_INVALIDCALL;
1659 if (fvf & D3DFVF_POSITION_MASK)
1661 BOOL has_blend = (fvf & D3DFVF_XYZB5) >= D3DFVF_XYZB1;
1662 DWORD blend_count = 1 + (((fvf & D3DFVF_XYZB5) - D3DFVF_XYZB1) >> 1);
1663 BOOL has_blend_idx = (fvf & D3DFVF_LASTBETA_D3DCOLOR) || (fvf & D3DFVF_LASTBETA_UBYTE4);
1665 if (has_blend_idx) --blend_count;
1667 if ((fvf & D3DFVF_POSITION_MASK) == D3DFVF_XYZW
1668 || (has_blend && blend_count > 4))
1669 return D3DERR_INVALIDCALL;
1671 if ((fvf & D3DFVF_POSITION_MASK) == D3DFVF_XYZRHW)
1672 append_decl_element(declaration, &idx, &offset, D3DDECLTYPE_FLOAT4, D3DDECLUSAGE_POSITIONT, 0);
1674 append_decl_element(declaration, &idx, &offset, D3DDECLTYPE_FLOAT3, D3DDECLUSAGE_POSITION, 0);
1678 switch (blend_count)
1683 append_decl_element(declaration, &idx, &offset, D3DDECLTYPE_FLOAT1, D3DDECLUSAGE_BLENDWEIGHT, 0);
1686 append_decl_element(declaration, &idx, &offset, D3DDECLTYPE_FLOAT2, D3DDECLUSAGE_BLENDWEIGHT, 0);
1689 append_decl_element(declaration, &idx, &offset, D3DDECLTYPE_FLOAT3, D3DDECLUSAGE_BLENDWEIGHT, 0);
1692 append_decl_element(declaration, &idx, &offset, D3DDECLTYPE_FLOAT4, D3DDECLUSAGE_BLENDWEIGHT, 0);
1695 ERR("Invalid blend count %u.\n", blend_count);
1701 if (fvf & D3DFVF_LASTBETA_UBYTE4)
1702 append_decl_element(declaration, &idx, &offset, D3DDECLTYPE_UBYTE4, D3DDECLUSAGE_BLENDINDICES, 0);
1703 else if (fvf & D3DFVF_LASTBETA_D3DCOLOR)
1704 append_decl_element(declaration, &idx, &offset, D3DDECLTYPE_D3DCOLOR, D3DDECLUSAGE_BLENDINDICES, 0);
1709 if (fvf & D3DFVF_NORMAL)
1710 append_decl_element(declaration, &idx, &offset, D3DDECLTYPE_FLOAT3, D3DDECLUSAGE_NORMAL, 0);
1711 if (fvf & D3DFVF_PSIZE)
1712 append_decl_element(declaration, &idx, &offset, D3DDECLTYPE_FLOAT1, D3DDECLUSAGE_PSIZE, 0);
1713 if (fvf & D3DFVF_DIFFUSE)
1714 append_decl_element(declaration, &idx, &offset, D3DDECLTYPE_D3DCOLOR, D3DDECLUSAGE_COLOR, 0);
1715 if (fvf & D3DFVF_SPECULAR)
1716 append_decl_element(declaration, &idx, &offset, D3DDECLTYPE_D3DCOLOR, D3DDECLUSAGE_COLOR, 1);
1718 for (i = 0; i < tex_count; ++i)
1720 switch ((fvf >> (16 + 2 * i)) & 0x03)
1722 case D3DFVF_TEXTUREFORMAT1:
1723 append_decl_element(declaration, &idx, &offset, D3DDECLTYPE_FLOAT1, D3DDECLUSAGE_TEXCOORD, i);
1725 case D3DFVF_TEXTUREFORMAT2:
1726 append_decl_element(declaration, &idx, &offset, D3DDECLTYPE_FLOAT2, D3DDECLUSAGE_TEXCOORD, i);
1728 case D3DFVF_TEXTUREFORMAT3:
1729 append_decl_element(declaration, &idx, &offset, D3DDECLTYPE_FLOAT3, D3DDECLUSAGE_TEXCOORD, i);
1731 case D3DFVF_TEXTUREFORMAT4:
1732 append_decl_element(declaration, &idx, &offset, D3DDECLTYPE_FLOAT4, D3DDECLUSAGE_TEXCOORD, i);
1737 declaration[idx] = end_element;
1742 /*************************************************************************
1743 * D3DXFVFFromDeclarator
1745 HRESULT WINAPI D3DXFVFFromDeclarator(const D3DVERTEXELEMENT9 *declaration, DWORD *fvf)
1747 unsigned int i = 0, texture, offset;
1749 TRACE("(%p, %p)\n", declaration, fvf);
1752 if (declaration[0].Type == D3DDECLTYPE_FLOAT3 && declaration[0].Usage == D3DDECLUSAGE_POSITION)
1754 if ((declaration[1].Type == D3DDECLTYPE_FLOAT4 && declaration[1].Usage == D3DDECLUSAGE_BLENDWEIGHT &&
1755 declaration[1].UsageIndex == 0) &&
1756 (declaration[2].Type == D3DDECLTYPE_FLOAT1 && declaration[2].Usage == D3DDECLUSAGE_BLENDINDICES &&
1757 declaration[2].UsageIndex == 0))
1759 return D3DERR_INVALIDCALL;
1761 else if ((declaration[1].Type == D3DDECLTYPE_UBYTE4 || declaration[1].Type == D3DDECLTYPE_D3DCOLOR) &&
1762 declaration[1].Usage == D3DDECLUSAGE_BLENDINDICES && declaration[1].UsageIndex == 0)
1764 if (declaration[1].Type == D3DDECLTYPE_UBYTE4)
1766 *fvf |= D3DFVF_XYZB1 | D3DFVF_LASTBETA_UBYTE4;
1770 *fvf |= D3DFVF_XYZB1 | D3DFVF_LASTBETA_D3DCOLOR;
1774 else if (declaration[1].Type <= D3DDECLTYPE_FLOAT4 && declaration[1].Usage == D3DDECLUSAGE_BLENDWEIGHT &&
1775 declaration[1].UsageIndex == 0)
1777 if ((declaration[2].Type == D3DDECLTYPE_UBYTE4 || declaration[2].Type == D3DDECLTYPE_D3DCOLOR) &&
1778 declaration[2].Usage == D3DDECLUSAGE_BLENDINDICES && declaration[2].UsageIndex == 0)
1780 if (declaration[2].Type == D3DDECLTYPE_UBYTE4)
1782 *fvf |= D3DFVF_LASTBETA_UBYTE4;
1786 *fvf |= D3DFVF_LASTBETA_D3DCOLOR;
1788 switch (declaration[1].Type)
1790 case D3DDECLTYPE_FLOAT1: *fvf |= D3DFVF_XYZB2; break;
1791 case D3DDECLTYPE_FLOAT2: *fvf |= D3DFVF_XYZB3; break;
1792 case D3DDECLTYPE_FLOAT3: *fvf |= D3DFVF_XYZB4; break;
1793 case D3DDECLTYPE_FLOAT4: *fvf |= D3DFVF_XYZB5; break;
1799 switch (declaration[1].Type)
1801 case D3DDECLTYPE_FLOAT1: *fvf |= D3DFVF_XYZB1; break;
1802 case D3DDECLTYPE_FLOAT2: *fvf |= D3DFVF_XYZB2; break;
1803 case D3DDECLTYPE_FLOAT3: *fvf |= D3DFVF_XYZB3; break;
1804 case D3DDECLTYPE_FLOAT4: *fvf |= D3DFVF_XYZB4; break;
1815 else if (declaration[0].Type == D3DDECLTYPE_FLOAT4 && declaration[0].Usage == D3DDECLUSAGE_POSITIONT &&
1816 declaration[0].UsageIndex == 0)
1818 *fvf |= D3DFVF_XYZRHW;
1822 if (declaration[i].Type == D3DDECLTYPE_FLOAT3 && declaration[i].Usage == D3DDECLUSAGE_NORMAL)
1824 *fvf |= D3DFVF_NORMAL;
1827 if (declaration[i].Type == D3DDECLTYPE_FLOAT1 && declaration[i].Usage == D3DDECLUSAGE_PSIZE &&
1828 declaration[i].UsageIndex == 0)
1830 *fvf |= D3DFVF_PSIZE;
1833 if (declaration[i].Type == D3DDECLTYPE_D3DCOLOR && declaration[i].Usage == D3DDECLUSAGE_COLOR &&
1834 declaration[i].UsageIndex == 0)
1836 *fvf |= D3DFVF_DIFFUSE;
1839 if (declaration[i].Type == D3DDECLTYPE_D3DCOLOR && declaration[i].Usage == D3DDECLUSAGE_COLOR &&
1840 declaration[i].UsageIndex == 1)
1842 *fvf |= D3DFVF_SPECULAR;
1846 for (texture = 0; texture < D3DDP_MAXTEXCOORD; i++, texture++)
1848 if (declaration[i].Stream == 0xFF)
1852 else if (declaration[i].Type == D3DDECLTYPE_FLOAT1 && declaration[i].Usage == D3DDECLUSAGE_TEXCOORD &&
1853 declaration[i].UsageIndex == texture)
1855 *fvf |= D3DFVF_TEXCOORDSIZE1(declaration[i].UsageIndex);
1857 else if (declaration[i].Type == D3DDECLTYPE_FLOAT2 && declaration[i].Usage == D3DDECLUSAGE_TEXCOORD &&
1858 declaration[i].UsageIndex == texture)
1860 *fvf |= D3DFVF_TEXCOORDSIZE2(declaration[i].UsageIndex);
1862 else if (declaration[i].Type == D3DDECLTYPE_FLOAT3 && declaration[i].Usage == D3DDECLUSAGE_TEXCOORD &&
1863 declaration[i].UsageIndex == texture)
1865 *fvf |= D3DFVF_TEXCOORDSIZE3(declaration[i].UsageIndex);
1867 else if (declaration[i].Type == D3DDECLTYPE_FLOAT4 && declaration[i].Usage == D3DDECLUSAGE_TEXCOORD &&
1868 declaration[i].UsageIndex == texture)
1870 *fvf |= D3DFVF_TEXCOORDSIZE4(declaration[i].UsageIndex);
1874 return D3DERR_INVALIDCALL;
1878 *fvf |= (texture << D3DFVF_TEXCOUNT_SHIFT);
1880 for (offset = 0, i = 0; declaration[i].Stream != 0xFF;
1881 offset += d3dx_decltype_size[declaration[i].Type], i++)
1883 if (declaration[i].Offset != offset)
1885 return D3DERR_INVALIDCALL;
1892 /*************************************************************************
1893 * D3DXGetFVFVertexSize
1895 static UINT Get_TexCoord_Size_From_FVF(DWORD FVF, int tex_num)
1897 return (((((FVF) >> (16 + (2 * (tex_num)))) + 1) & 0x03) + 1);
1900 UINT WINAPI D3DXGetFVFVertexSize(DWORD FVF)
1904 UINT numTextures = (FVF & D3DFVF_TEXCOUNT_MASK) >> D3DFVF_TEXCOUNT_SHIFT;
1906 if (FVF & D3DFVF_NORMAL) size += sizeof(D3DXVECTOR3);
1907 if (FVF & D3DFVF_DIFFUSE) size += sizeof(DWORD);
1908 if (FVF & D3DFVF_SPECULAR) size += sizeof(DWORD);
1909 if (FVF & D3DFVF_PSIZE) size += sizeof(DWORD);
1911 switch (FVF & D3DFVF_POSITION_MASK)
1913 case D3DFVF_XYZ: size += sizeof(D3DXVECTOR3); break;
1914 case D3DFVF_XYZRHW: size += 4 * sizeof(FLOAT); break;
1915 case D3DFVF_XYZB1: size += 4 * sizeof(FLOAT); break;
1916 case D3DFVF_XYZB2: size += 5 * sizeof(FLOAT); break;
1917 case D3DFVF_XYZB3: size += 6 * sizeof(FLOAT); break;
1918 case D3DFVF_XYZB4: size += 7 * sizeof(FLOAT); break;
1919 case D3DFVF_XYZB5: size += 8 * sizeof(FLOAT); break;
1920 case D3DFVF_XYZW: size += 4 * sizeof(FLOAT); break;
1923 for (i = 0; i < numTextures; i++)
1925 size += Get_TexCoord_Size_From_FVF(FVF, i) * sizeof(FLOAT);
1931 /*************************************************************************
1932 * D3DXGetDeclVertexSize
1934 UINT WINAPI D3DXGetDeclVertexSize(const D3DVERTEXELEMENT9 *decl, DWORD stream_idx)
1936 const D3DVERTEXELEMENT9 *element;
1939 TRACE("decl %p, stream_idx %u\n", decl, stream_idx);
1941 if (!decl) return 0;
1943 for (element = decl; element->Stream != 0xff; ++element)
1947 if (element->Stream != stream_idx) continue;
1949 if (element->Type >= sizeof(d3dx_decltype_size) / sizeof(*d3dx_decltype_size))
1951 FIXME("Unhandled element type %#x, size will be incorrect.\n", element->Type);
1955 type_size = d3dx_decltype_size[element->Type];
1956 if (element->Offset + type_size > size) size = element->Offset + type_size;
1962 /*************************************************************************
1965 UINT WINAPI D3DXGetDeclLength(const D3DVERTEXELEMENT9 *decl)
1967 const D3DVERTEXELEMENT9 *element;
1969 TRACE("decl %p\n", decl);
1971 /* null decl results in exception on Windows XP */
1973 for (element = decl; element->Stream != 0xff; ++element);
1975 return element - decl;
1978 /*************************************************************************
1981 BOOL WINAPI D3DXIntersectTri(CONST D3DXVECTOR3 *p0, CONST D3DXVECTOR3 *p1, CONST D3DXVECTOR3 *p2, CONST D3DXVECTOR3 *praypos, CONST D3DXVECTOR3 *praydir, FLOAT *pu, FLOAT *pv, FLOAT *pdist)
1986 m.u.m[0][0] = p1->x - p0->x;
1987 m.u.m[1][0] = p2->x - p0->x;
1988 m.u.m[2][0] = -praydir->x;
1990 m.u.m[0][1] = p1->y - p0->z;
1991 m.u.m[1][1] = p2->y - p0->z;
1992 m.u.m[2][1] = -praydir->y;
1994 m.u.m[0][2] = p1->z - p0->z;
1995 m.u.m[1][2] = p2->z - p0->z;
1996 m.u.m[2][2] = -praydir->z;
2003 vec.x = praypos->x - p0->x;
2004 vec.y = praypos->y - p0->y;
2005 vec.z = praypos->z - p0->z;
2008 if ( D3DXMatrixInverse(&m, NULL, &m) )
2010 D3DXVec4Transform(&vec, &vec, &m);
2011 if ( (vec.x >= 0.0f) && (vec.y >= 0.0f) && (vec.x + vec.y <= 1.0f) && (vec.z >= 0.0f) )
2015 *pdist = fabs( vec.z );
2023 /*************************************************************************
2024 * D3DXSphereBoundProbe
2026 BOOL WINAPI D3DXSphereBoundProbe(CONST D3DXVECTOR3 *pcenter, FLOAT radius, CONST D3DXVECTOR3 *prayposition, CONST D3DXVECTOR3 *praydirection)
2028 D3DXVECTOR3 difference;
2031 a = D3DXVec3LengthSq(praydirection);
2032 if (!D3DXVec3Subtract(&difference, prayposition, pcenter)) return FALSE;
2033 b = D3DXVec3Dot(&difference, praydirection);
2034 c = D3DXVec3LengthSq(&difference) - radius * radius;
2037 if ( ( d <= 0.0f ) || ( sqrt(d) <= b ) ) return FALSE;
2041 /*************************************************************************
2044 HRESULT WINAPI D3DXCreateMesh(DWORD numfaces, DWORD numvertices, DWORD options, CONST D3DVERTEXELEMENT9 *declaration,
2045 LPDIRECT3DDEVICE9 device, LPD3DXMESH *mesh)
2049 IDirect3DVertexDeclaration9 *vertex_declaration;
2050 UINT vertex_declaration_size;
2052 IDirect3DVertexBuffer9 *vertex_buffer;
2053 IDirect3DIndexBuffer9 *index_buffer;
2054 DWORD *attrib_buffer;
2055 ID3DXMeshImpl *object;
2056 DWORD index_usage = 0;
2057 D3DPOOL index_pool = D3DPOOL_DEFAULT;
2058 D3DFORMAT index_format = D3DFMT_INDEX16;
2059 DWORD vertex_usage = 0;
2060 D3DPOOL vertex_pool = D3DPOOL_DEFAULT;
2063 TRACE("(%d, %d, %x, %p, %p, %p)\n", numfaces, numvertices, options, declaration, device, mesh);
2065 if (numfaces == 0 || numvertices == 0 || declaration == NULL || device == NULL || mesh == NULL ||
2066 /* D3DXMESH_VB_SHARE is for cloning, and D3DXMESH_USEHWONLY is for ConvertToBlendedMesh */
2067 (options & (D3DXMESH_VB_SHARE | D3DXMESH_USEHWONLY | 0xfffe0000)))
2069 return D3DERR_INVALIDCALL;
2071 for (i = 0; declaration[i].Stream != 0xff; i++)
2072 if (declaration[i].Stream != 0)
2073 return D3DERR_INVALIDCALL;
2076 if (options & D3DXMESH_32BIT)
2077 index_format = D3DFMT_INDEX32;
2079 if (options & D3DXMESH_DONOTCLIP) {
2080 index_usage |= D3DUSAGE_DONOTCLIP;
2081 vertex_usage |= D3DUSAGE_DONOTCLIP;
2083 if (options & D3DXMESH_POINTS) {
2084 index_usage |= D3DUSAGE_POINTS;
2085 vertex_usage |= D3DUSAGE_POINTS;
2087 if (options & D3DXMESH_RTPATCHES) {
2088 index_usage |= D3DUSAGE_RTPATCHES;
2089 vertex_usage |= D3DUSAGE_RTPATCHES;
2091 if (options & D3DXMESH_NPATCHES) {
2092 index_usage |= D3DUSAGE_NPATCHES;
2093 vertex_usage |= D3DUSAGE_NPATCHES;
2096 if (options & D3DXMESH_VB_SYSTEMMEM)
2097 vertex_pool = D3DPOOL_SYSTEMMEM;
2098 else if (options & D3DXMESH_VB_MANAGED)
2099 vertex_pool = D3DPOOL_MANAGED;
2101 if (options & D3DXMESH_VB_WRITEONLY)
2102 vertex_usage |= D3DUSAGE_WRITEONLY;
2103 if (options & D3DXMESH_VB_DYNAMIC)
2104 vertex_usage |= D3DUSAGE_DYNAMIC;
2105 if (options & D3DXMESH_VB_SOFTWAREPROCESSING)
2106 vertex_usage |= D3DUSAGE_SOFTWAREPROCESSING;
2108 if (options & D3DXMESH_IB_SYSTEMMEM)
2109 index_pool = D3DPOOL_SYSTEMMEM;
2110 else if (options & D3DXMESH_IB_MANAGED)
2111 index_pool = D3DPOOL_MANAGED;
2113 if (options & D3DXMESH_IB_WRITEONLY)
2114 index_usage |= D3DUSAGE_WRITEONLY;
2115 if (options & D3DXMESH_IB_DYNAMIC)
2116 index_usage |= D3DUSAGE_DYNAMIC;
2117 if (options & D3DXMESH_IB_SOFTWAREPROCESSING)
2118 index_usage |= D3DUSAGE_SOFTWAREPROCESSING;
2120 hr = D3DXFVFFromDeclarator(declaration, &fvf);
2126 /* Create vertex declaration */
2127 hr = IDirect3DDevice9_CreateVertexDeclaration(device,
2129 &vertex_declaration);
2132 WARN("Unexpected return value %x from IDirect3DDevice9_CreateVertexDeclaration.\n",hr);
2135 vertex_declaration_size = D3DXGetDeclVertexSize(declaration, declaration[0].Stream);
2137 /* Create vertex buffer */
2138 hr = IDirect3DDevice9_CreateVertexBuffer(device,
2139 numvertices * vertex_declaration_size,
2147 WARN("Unexpected return value %x from IDirect3DDevice9_CreateVertexBuffer.\n",hr);
2148 IDirect3DVertexDeclaration9_Release(vertex_declaration);
2152 /* Create index buffer */
2153 hr = IDirect3DDevice9_CreateIndexBuffer(device,
2154 numfaces * 3 * ((index_format == D3DFMT_INDEX16) ? 2 : 4),
2162 WARN("Unexpected return value %x from IDirect3DDevice9_CreateVertexBuffer.\n",hr);
2163 IDirect3DVertexBuffer9_Release(vertex_buffer);
2164 IDirect3DVertexDeclaration9_Release(vertex_declaration);
2168 attrib_buffer = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, numfaces * sizeof(*attrib_buffer));
2169 object = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(ID3DXMeshImpl));
2170 if (object == NULL || attrib_buffer == NULL)
2172 HeapFree(GetProcessHeap(), 0, attrib_buffer);
2173 IDirect3DIndexBuffer9_Release(index_buffer);
2174 IDirect3DVertexBuffer9_Release(vertex_buffer);
2175 IDirect3DVertexDeclaration9_Release(vertex_declaration);
2177 return E_OUTOFMEMORY;
2179 object->ID3DXMesh_iface.lpVtbl = &D3DXMesh_Vtbl;
2182 object->numfaces = numfaces;
2183 object->numvertices = numvertices;
2184 object->options = options;
2186 object->device = device;
2187 IDirect3DDevice9_AddRef(device);
2189 copy_declaration(object->cached_declaration, declaration, num_elem);
2190 object->vertex_declaration = vertex_declaration;
2191 object->vertex_declaration_size = vertex_declaration_size;
2192 object->num_elem = num_elem;
2193 object->vertex_buffer = vertex_buffer;
2194 object->index_buffer = index_buffer;
2195 object->attrib_buffer = attrib_buffer;
2197 *mesh = &object->ID3DXMesh_iface;
2202 /*************************************************************************
2205 HRESULT WINAPI D3DXCreateMeshFVF(DWORD numfaces, DWORD numvertices, DWORD options, DWORD fvf,
2206 LPDIRECT3DDEVICE9 device, LPD3DXMESH *mesh)
2209 D3DVERTEXELEMENT9 declaration[MAX_FVF_DECL_SIZE];
2211 TRACE("(%u, %u, %u, %u, %p, %p)\n", numfaces, numvertices, options, fvf, device, mesh);
2213 hr = D3DXDeclaratorFromFVF(fvf, declaration);
2214 if (FAILED(hr)) return hr;
2216 return D3DXCreateMesh(numfaces, numvertices, options, declaration, device, mesh);
2222 DWORD num_poly_faces;
2223 DWORD num_tri_faces;
2224 D3DXVECTOR3 *vertices;
2225 DWORD *num_tri_per_face;
2230 /* optional mesh data */
2233 D3DXVECTOR3 *normals;
2234 DWORD *normal_indices;
2236 D3DXVECTOR2 *tex_coords;
2238 DWORD *vertex_colors;
2240 DWORD num_materials;
2241 D3DXMATERIAL *materials;
2242 DWORD *material_indices;
2245 static HRESULT get_next_child(IDirectXFileData *filedata, IDirectXFileData **child, const GUID **type)
2248 IDirectXFileDataReference *child_ref = NULL;
2249 IDirectXFileObject *child_obj = NULL;
2250 IDirectXFileData *child_data = NULL;
2252 hr = IDirectXFileData_GetNextObject(filedata, &child_obj);
2253 if (FAILED(hr)) return hr;
2255 hr = IDirectXFileObject_QueryInterface(child_obj, &IID_IDirectXFileDataReference, (void**)&child_ref);
2256 if (SUCCEEDED(hr)) {
2257 hr = IDirectXFileDataReference_Resolve(child_ref, &child_data);
2258 IDirectXFileDataReference_Release(child_ref);
2260 hr = IDirectXFileObject_QueryInterface(child_obj, &IID_IDirectXFileData, (void**)&child_data);
2262 IDirectXFileObject_Release(child_obj);
2266 hr = IDirectXFileData_GetType(child_data, type);
2268 IDirectXFileData_Release(child_data);
2270 *child = child_data;
2276 static HRESULT parse_texture_filename(IDirectXFileData *filedata, LPSTR *filename_out)
2282 char *filename = NULL;
2284 /* template TextureFilename {
2289 HeapFree(GetProcessHeap(), 0, *filename_out);
2290 *filename_out = NULL;
2292 hr = IDirectXFileData_GetData(filedata, NULL, &data_size, (void**)&data);
2293 if (FAILED(hr)) return hr;
2295 if (data_size < sizeof(LPSTR)) {
2296 WARN("truncated data (%u bytes)\n", data_size);
2299 filename_in = *(LPSTR*)data;
2301 filename = HeapAlloc(GetProcessHeap(), 0, strlen(filename_in) + 1);
2302 if (!filename) return E_OUTOFMEMORY;
2304 strcpy(filename, filename_in);
2305 *filename_out = filename;
2310 static HRESULT parse_material(IDirectXFileData *filedata, D3DXMATERIAL *material)
2316 IDirectXFileData *child;
2318 material->pTextureFilename = NULL;
2320 hr = IDirectXFileData_GetData(filedata, NULL, &data_size, (void**)&data);
2321 if (FAILED(hr)) return hr;
2324 * template ColorRGBA {
2330 * template ColorRGB {
2335 * template Material {
2336 * ColorRGBA faceColor;
2338 * ColorRGB specularColor;
2339 * ColorRGB emissiveColor;
2343 if (data_size != sizeof(FLOAT) * 11) {
2344 WARN("incorrect data size (%u bytes)\n", data_size);
2348 memcpy(&material->MatD3D.Diffuse, data, sizeof(D3DCOLORVALUE));
2349 data += sizeof(D3DCOLORVALUE);
2350 material->MatD3D.Power = *(FLOAT*)data;
2351 data += sizeof(FLOAT);
2352 memcpy(&material->MatD3D.Specular, data, sizeof(FLOAT) * 3);
2353 material->MatD3D.Specular.a = 1.0f;
2354 data += 3 * sizeof(FLOAT);
2355 memcpy(&material->MatD3D.Emissive, data, sizeof(FLOAT) * 3);
2356 material->MatD3D.Emissive.a = 1.0f;
2357 material->MatD3D.Ambient.r = 0.0f;
2358 material->MatD3D.Ambient.g = 0.0f;
2359 material->MatD3D.Ambient.b = 0.0f;
2360 material->MatD3D.Ambient.a = 1.0f;
2362 while (SUCCEEDED(hr = get_next_child(filedata, &child, &type)))
2364 if (IsEqualGUID(type, &TID_D3DRMTextureFilename)) {
2365 hr = parse_texture_filename(child, &material->pTextureFilename);
2366 if (FAILED(hr)) break;
2369 return hr == DXFILEERR_NOMOREOBJECTS ? D3D_OK : hr;
2372 static void destroy_materials(struct mesh_data *mesh)
2375 for (i = 0; i < mesh->num_materials; i++)
2376 HeapFree(GetProcessHeap(), 0, mesh->materials[i].pTextureFilename);
2377 HeapFree(GetProcessHeap(), 0, mesh->materials);
2378 HeapFree(GetProcessHeap(), 0, mesh->material_indices);
2379 mesh->num_materials = 0;
2380 mesh->materials = NULL;
2381 mesh->material_indices = NULL;
2384 static HRESULT parse_material_list(IDirectXFileData *filedata, struct mesh_data *mesh)
2388 DWORD *data, *in_ptr;
2390 IDirectXFileData *child;
2391 DWORD num_materials;
2394 destroy_materials(mesh);
2396 hr = IDirectXFileData_GetData(filedata, NULL, &data_size, (void**)&data);
2397 if (FAILED(hr)) return hr;
2399 /* template MeshMaterialList {
2401 * DWORD nFaceIndexes;
2402 * array DWORD faceIndexes[nFaceIndexes];
2409 if (data_size < sizeof(DWORD))
2410 goto truncated_data_error;
2411 num_materials = *in_ptr++;
2415 if (data_size < 2 * sizeof(DWORD))
2416 goto truncated_data_error;
2417 if (*in_ptr++ != mesh->num_poly_faces) {
2418 WARN("number of material face indices (%u) doesn't match number of faces (%u)\n",
2419 *(in_ptr - 1), mesh->num_poly_faces);
2422 if (data_size < 2 * sizeof(DWORD) + mesh->num_poly_faces * sizeof(DWORD))
2423 goto truncated_data_error;
2424 for (i = 0; i < mesh->num_poly_faces; i++) {
2425 if (*in_ptr++ >= num_materials) {
2426 WARN("face %u: reference to undefined material %u (only %u materials)\n",
2427 i, *(in_ptr - 1), num_materials);
2432 mesh->materials = HeapAlloc(GetProcessHeap(), 0, num_materials * sizeof(*mesh->materials));
2433 mesh->material_indices = HeapAlloc(GetProcessHeap(), 0, mesh->num_poly_faces * sizeof(*mesh->material_indices));
2434 if (!mesh->materials || !mesh->material_indices)
2435 return E_OUTOFMEMORY;
2436 memcpy(mesh->material_indices, data + 2, mesh->num_poly_faces * sizeof(DWORD));
2438 while (SUCCEEDED(hr = get_next_child(filedata, &child, &type)))
2440 if (IsEqualGUID(type, &TID_D3DRMMaterial)) {
2441 if (mesh->num_materials >= num_materials) {
2442 WARN("more materials defined than declared\n");
2445 hr = parse_material(child, &mesh->materials[mesh->num_materials++]);
2446 if (FAILED(hr)) break;
2449 if (hr != DXFILEERR_NOMOREOBJECTS)
2451 if (num_materials != mesh->num_materials) {
2452 WARN("only %u of %u materials defined\n", num_materials, mesh->num_materials);
2457 truncated_data_error:
2458 WARN("truncated data (%u bytes)\n", data_size);
2462 static HRESULT parse_texture_coords(IDirectXFileData *filedata, struct mesh_data *mesh)
2468 HeapFree(GetProcessHeap(), 0, mesh->tex_coords);
2469 mesh->tex_coords = NULL;
2471 hr = IDirectXFileData_GetData(filedata, NULL, &data_size, (void**)&data);
2472 if (FAILED(hr)) return hr;
2474 /* template Coords2d {
2478 * template MeshTextureCoords {
2479 * DWORD nTextureCoords;
2480 * array Coords2d textureCoords[nTextureCoords];
2484 if (data_size < sizeof(DWORD))
2485 goto truncated_data_error;
2486 if (*(DWORD*)data != mesh->num_vertices) {
2487 WARN("number of texture coordinates (%u) doesn't match number of vertices (%u)\n",
2488 *(DWORD*)data, mesh->num_vertices);
2491 data += sizeof(DWORD);
2492 if (data_size < sizeof(DWORD) + mesh->num_vertices * sizeof(*mesh->tex_coords))
2493 goto truncated_data_error;
2495 mesh->tex_coords = HeapAlloc(GetProcessHeap(), 0, mesh->num_vertices * sizeof(*mesh->tex_coords));
2496 if (!mesh->tex_coords) return E_OUTOFMEMORY;
2497 memcpy(mesh->tex_coords, data, mesh->num_vertices * sizeof(*mesh->tex_coords));
2499 mesh->fvf |= D3DFVF_TEX1;
2502 truncated_data_error:
2503 WARN("truncated data (%u bytes)\n", data_size);
2507 static HRESULT parse_vertex_colors(IDirectXFileData *filedata, struct mesh_data *mesh)
2515 HeapFree(GetProcessHeap(), 0, mesh->vertex_colors);
2516 mesh->vertex_colors = NULL;
2518 hr = IDirectXFileData_GetData(filedata, NULL, &data_size, (void**)&data);
2519 if (FAILED(hr)) return hr;
2521 /* template IndexedColor {
2523 * ColorRGBA indexColor;
2525 * template MeshVertexColors {
2526 * DWORD nVertexColors;
2527 * array IndexedColor vertexColors[nVertexColors];
2531 if (data_size < sizeof(DWORD))
2532 goto truncated_data_error;
2533 num_colors = *(DWORD*)data;
2534 data += sizeof(DWORD);
2535 if (data_size < sizeof(DWORD) + num_colors * (sizeof(DWORD) + sizeof(D3DCOLORVALUE)))
2536 goto truncated_data_error;
2538 mesh->vertex_colors = HeapAlloc(GetProcessHeap(), 0, mesh->num_vertices * sizeof(DWORD));
2539 if (!mesh->vertex_colors)
2540 return E_OUTOFMEMORY;
2542 for (i = 0; i < mesh->num_vertices; i++)
2543 mesh->vertex_colors[i] = D3DCOLOR_ARGB(0, 0xff, 0xff, 0xff);
2544 for (i = 0; i < num_colors; i++)
2546 D3DCOLORVALUE color;
2547 DWORD index = *(DWORD*)data;
2548 data += sizeof(DWORD);
2549 if (index >= mesh->num_vertices) {
2550 WARN("vertex color %u references undefined vertex %u (only %u vertices)\n",
2551 i, index, mesh->num_vertices);
2554 memcpy(&color, data, sizeof(color));
2555 data += sizeof(color);
2556 color.r = min(1.0f, max(0.0f, color.r));
2557 color.g = min(1.0f, max(0.0f, color.g));
2558 color.b = min(1.0f, max(0.0f, color.b));
2559 color.a = min(1.0f, max(0.0f, color.a));
2560 mesh->vertex_colors[index] = D3DCOLOR_ARGB((BYTE)(color.a * 255.0f + 0.5f),
2561 (BYTE)(color.r * 255.0f + 0.5f),
2562 (BYTE)(color.g * 255.0f + 0.5f),
2563 (BYTE)(color.b * 255.0f + 0.5f));
2566 mesh->fvf |= D3DFVF_DIFFUSE;
2569 truncated_data_error:
2570 WARN("truncated data (%u bytes)\n", data_size);
2574 static HRESULT parse_normals(IDirectXFileData *filedata, struct mesh_data *mesh)
2579 DWORD *index_out_ptr;
2581 DWORD num_face_indices = mesh->num_poly_faces * 2 + mesh->num_tri_faces;
2583 HeapFree(GetProcessHeap(), 0, mesh->normals);
2584 mesh->num_normals = 0;
2585 mesh->normals = NULL;
2586 mesh->normal_indices = NULL;
2587 mesh->fvf |= D3DFVF_NORMAL;
2589 hr = IDirectXFileData_GetData(filedata, NULL, &data_size, (void**)&data);
2590 if (FAILED(hr)) return hr;
2592 /* template Vector {
2597 * template MeshFace {
2598 * DWORD nFaceVertexIndices;
2599 * array DWORD faceVertexIndices[nFaceVertexIndices];
2601 * template MeshNormals {
2603 * array Vector normals[nNormals];
2604 * DWORD nFaceNormals;
2605 * array MeshFace faceNormals[nFaceNormals];
2609 if (data_size < sizeof(DWORD) * 2)
2610 goto truncated_data_error;
2611 mesh->num_normals = *(DWORD*)data;
2612 data += sizeof(DWORD);
2613 if (data_size < sizeof(DWORD) * 2 + mesh->num_normals * sizeof(D3DXVECTOR3) +
2614 num_face_indices * sizeof(DWORD))
2615 goto truncated_data_error;
2617 mesh->normals = HeapAlloc(GetProcessHeap(), 0, mesh->num_normals * sizeof(D3DXVECTOR3));
2618 mesh->normal_indices = HeapAlloc(GetProcessHeap(), 0, num_face_indices * sizeof(DWORD));
2619 if (!mesh->normals || !mesh->normal_indices)
2620 return E_OUTOFMEMORY;
2622 memcpy(mesh->normals, data, mesh->num_normals * sizeof(D3DXVECTOR3));
2623 data += mesh->num_normals * sizeof(D3DXVECTOR3);
2624 for (i = 0; i < mesh->num_normals; i++)
2625 D3DXVec3Normalize(&mesh->normals[i], &mesh->normals[i]);
2627 if (*(DWORD*)data != mesh->num_poly_faces) {
2628 WARN("number of face normals (%u) doesn't match number of faces (%u)\n",
2629 *(DWORD*)data, mesh->num_poly_faces);
2632 data += sizeof(DWORD);
2633 index_out_ptr = mesh->normal_indices;
2634 for (i = 0; i < mesh->num_poly_faces; i++)
2637 DWORD count = *(DWORD*)data;
2638 if (count != mesh->num_tri_per_face[i] + 2) {
2639 WARN("face %u: number of normals (%u) doesn't match number of vertices (%u)\n",
2640 i, count, mesh->num_tri_per_face[i] + 2);
2643 data += sizeof(DWORD);
2645 for (j = 0; j < count; j++) {
2646 DWORD normal_index = *(DWORD*)data;
2647 if (normal_index >= mesh->num_normals) {
2648 WARN("face %u, normal index %u: reference to undefined normal %u (only %u normals)\n",
2649 i, j, normal_index, mesh->num_normals);
2652 *index_out_ptr++ = normal_index;
2653 data += sizeof(DWORD);
2658 truncated_data_error:
2659 WARN("truncated data (%u bytes)\n", data_size);
2663 /* for provide_flags parameters */
2664 #define PROVIDE_MATERIALS 0x1
2665 #define PROVIDE_SKININFO 0x2
2666 #define PROVIDE_ADJACENCY 0x4
2668 static HRESULT parse_mesh(IDirectXFileData *filedata, struct mesh_data *mesh_data, DWORD provide_flags)
2672 BYTE *data, *in_ptr;
2673 DWORD *index_out_ptr;
2675 IDirectXFileData *child;
2681 * array Vector vertices[nVertices];
2683 * array MeshFace faces[nFaces];
2688 hr = IDirectXFileData_GetData(filedata, NULL, &data_size, (void**)&data);
2689 if (FAILED(hr)) return hr;
2692 if (data_size < sizeof(DWORD) * 2)
2693 goto truncated_data_error;
2694 mesh_data->num_vertices = *(DWORD*)in_ptr;
2695 if (data_size < sizeof(DWORD) * 2 + mesh_data->num_vertices * sizeof(D3DXVECTOR3))
2696 goto truncated_data_error;
2697 in_ptr += sizeof(DWORD) + mesh_data->num_vertices * sizeof(D3DXVECTOR3);
2699 mesh_data->num_poly_faces = *(DWORD*)in_ptr;
2700 in_ptr += sizeof(DWORD);
2702 mesh_data->num_tri_faces = 0;
2703 for (i = 0; i < mesh_data->num_poly_faces; i++)
2705 DWORD num_poly_vertices;
2708 if (data_size - (in_ptr - data) < sizeof(DWORD))
2709 goto truncated_data_error;
2710 num_poly_vertices = *(DWORD*)in_ptr;
2711 in_ptr += sizeof(DWORD);
2712 if (data_size - (in_ptr - data) < num_poly_vertices * sizeof(DWORD))
2713 goto truncated_data_error;
2714 if (num_poly_vertices < 3) {
2715 WARN("face %u has only %u vertices\n", i, num_poly_vertices);
2718 for (j = 0; j < num_poly_vertices; j++) {
2719 if (*(DWORD*)in_ptr >= mesh_data->num_vertices) {
2720 WARN("face %u, index %u: undefined vertex %u (only %u vertices)\n",
2721 i, j, *(DWORD*)in_ptr, mesh_data->num_vertices);
2724 in_ptr += sizeof(DWORD);
2726 mesh_data->num_tri_faces += num_poly_vertices - 2;
2729 mesh_data->fvf = D3DFVF_XYZ;
2731 mesh_data->vertices = HeapAlloc(GetProcessHeap(), 0,
2732 mesh_data->num_vertices * sizeof(*mesh_data->vertices));
2733 mesh_data->num_tri_per_face = HeapAlloc(GetProcessHeap(), 0,
2734 mesh_data->num_poly_faces * sizeof(*mesh_data->num_tri_per_face));
2735 mesh_data->indices = HeapAlloc(GetProcessHeap(), 0,
2736 (mesh_data->num_tri_faces + mesh_data->num_poly_faces * 2) * sizeof(*mesh_data->indices));
2737 if (!mesh_data->vertices || !mesh_data->num_tri_per_face || !mesh_data->indices)
2738 return E_OUTOFMEMORY;
2740 in_ptr = data + sizeof(DWORD);
2741 memcpy(mesh_data->vertices, in_ptr, mesh_data->num_vertices * sizeof(D3DXVECTOR3));
2742 in_ptr += mesh_data->num_vertices * sizeof(D3DXVECTOR3) + sizeof(DWORD);
2744 index_out_ptr = mesh_data->indices;
2745 for (i = 0; i < mesh_data->num_poly_faces; i++)
2749 count = *(DWORD*)in_ptr;
2750 in_ptr += sizeof(DWORD);
2751 mesh_data->num_tri_per_face[i] = count - 2;
2754 *index_out_ptr++ = *(DWORD*)in_ptr;
2755 in_ptr += sizeof(DWORD);
2759 while (SUCCEEDED(hr = get_next_child(filedata, &child, &type)))
2761 if (IsEqualGUID(type, &TID_D3DRMMeshNormals)) {
2762 hr = parse_normals(child, mesh_data);
2763 } else if (IsEqualGUID(type, &TID_D3DRMMeshVertexColors)) {
2764 hr = parse_vertex_colors(child, mesh_data);
2765 } else if (IsEqualGUID(type, &TID_D3DRMMeshTextureCoords)) {
2766 hr = parse_texture_coords(child, mesh_data);
2767 } else if (IsEqualGUID(type, &TID_D3DRMMeshMaterialList) &&
2768 (provide_flags & PROVIDE_MATERIALS))
2770 hr = parse_material_list(child, mesh_data);
2771 } else if (provide_flags & PROVIDE_SKININFO) {
2772 if (IsEqualGUID(type, &DXFILEOBJ_XSkinMeshHeader)) {
2773 FIXME("Skin mesh loading not implemented.\n");
2775 } else if (IsEqualGUID(type, &DXFILEOBJ_SkinWeights)) {
2776 /* ignored without XSkinMeshHeader */
2782 return hr == DXFILEERR_NOMOREOBJECTS ? D3D_OK : hr;
2783 truncated_data_error:
2784 WARN("truncated data (%u bytes)\n", data_size);
2788 static HRESULT generate_effects(ID3DXBuffer *materials, DWORD num_materials,
2789 ID3DXBuffer **effects)
2792 D3DXEFFECTINSTANCE *effect_ptr;
2794 const D3DXMATERIAL *material_ptr = ID3DXBuffer_GetBufferPointer(materials);
2795 static const struct {
2796 const char *param_name;
2800 } material_effects[] = {
2801 #define EFFECT_TABLE_ENTRY(str, field) \
2802 {str, sizeof(str), sizeof(material_ptr->MatD3D.field), offsetof(D3DXMATERIAL, MatD3D.field)}
2803 EFFECT_TABLE_ENTRY("Diffuse", Diffuse),
2804 EFFECT_TABLE_ENTRY("Power", Power),
2805 EFFECT_TABLE_ENTRY("Specular", Specular),
2806 EFFECT_TABLE_ENTRY("Emissive", Emissive),
2807 EFFECT_TABLE_ENTRY("Ambient", Ambient),
2808 #undef EFFECT_TABLE_ENTRY
2810 static const char texture_paramname[] = "Texture0@Name";
2814 /* effects buffer layout:
2816 * D3DXEFFECTINSTANCE effects[num_materials];
2817 * for (effect in effects)
2819 * D3DXEFFECTDEFAULT defaults[effect.NumDefaults];
2820 * for (default in defaults)
2822 * *default.pParamName;
2827 buffer_size = sizeof(D3DXEFFECTINSTANCE);
2828 buffer_size += sizeof(D3DXEFFECTDEFAULT) * ARRAY_SIZE(material_effects);
2829 for (i = 0; i < ARRAY_SIZE(material_effects); i++) {
2830 buffer_size += material_effects[i].name_size;
2831 buffer_size += material_effects[i].num_bytes;
2833 buffer_size *= num_materials;
2834 for (i = 0; i < num_materials; i++) {
2835 if (material_ptr[i].pTextureFilename) {
2836 buffer_size += sizeof(D3DXEFFECTDEFAULT);
2837 buffer_size += sizeof(texture_paramname);
2838 buffer_size += strlen(material_ptr[i].pTextureFilename) + 1;
2842 hr = D3DXCreateBuffer(buffer_size, effects);
2843 if (FAILED(hr)) return hr;
2844 effect_ptr = ID3DXBuffer_GetBufferPointer(*effects);
2845 out_ptr = (BYTE*)(effect_ptr + num_materials);
2847 for (i = 0; i < num_materials; i++)
2850 D3DXEFFECTDEFAULT *defaults = (D3DXEFFECTDEFAULT*)out_ptr;
2852 effect_ptr->pDefaults = defaults;
2853 effect_ptr->NumDefaults = material_ptr->pTextureFilename ? 6 : 5;
2854 out_ptr = (BYTE*)(effect_ptr->pDefaults + effect_ptr->NumDefaults);
2856 for (j = 0; j < ARRAY_SIZE(material_effects); j++)
2858 defaults->pParamName = (LPSTR)out_ptr;
2859 strcpy(defaults->pParamName, material_effects[j].param_name);
2860 defaults->pValue = defaults->pParamName + material_effects[j].name_size;
2861 defaults->Type = D3DXEDT_FLOATS;
2862 defaults->NumBytes = material_effects[j].num_bytes;
2863 memcpy(defaults->pValue, (BYTE*)material_ptr + material_effects[j].value_offset, defaults->NumBytes);
2864 out_ptr = (BYTE*)defaults->pValue + defaults->NumBytes;
2868 if (material_ptr->pTextureFilename) {
2869 defaults->pParamName = (LPSTR)out_ptr;
2870 strcpy(defaults->pParamName, texture_paramname);
2871 defaults->pValue = defaults->pParamName + sizeof(texture_paramname);
2872 defaults->Type = D3DXEDT_STRING;
2873 defaults->NumBytes = strlen(material_ptr->pTextureFilename) + 1;
2874 strcpy(defaults->pValue, material_ptr->pTextureFilename);
2875 out_ptr = (BYTE*)defaults->pValue + defaults->NumBytes;
2880 assert(out_ptr - (BYTE*)ID3DXBuffer_GetBufferPointer(*effects) == buffer_size);
2885 /* change to D3DXLoadSkinMeshFromXof when ID3DXFileData is implemented */
2886 static HRESULT load_skin_mesh_from_xof(IDirectXFileData *filedata,
2888 LPDIRECT3DDEVICE9 device,
2889 LPD3DXBUFFER *adjacency_out,
2890 LPD3DXBUFFER *materials_out,
2891 LPD3DXBUFFER *effects_out,
2892 DWORD *num_materials_out,
2893 LPD3DXSKININFO *skin_info_out,
2894 LPD3DXMESH *mesh_out)
2897 DWORD *index_in_ptr;
2898 struct mesh_data mesh_data;
2899 DWORD total_vertices;
2900 ID3DXMesh *d3dxmesh = NULL;
2901 ID3DXBuffer *adjacency = NULL;
2902 ID3DXBuffer *materials = NULL;
2903 ID3DXBuffer *effects = NULL;
2904 struct vertex_duplication {
2907 } *duplications = NULL;
2909 void *vertices = NULL;
2910 void *indices = NULL;
2912 DWORD provide_flags = 0;
2914 ZeroMemory(&mesh_data, sizeof(mesh_data));
2916 if (num_materials_out || materials_out || effects_out)
2917 provide_flags |= PROVIDE_MATERIALS;
2919 provide_flags |= PROVIDE_SKININFO;
2921 hr = parse_mesh(filedata, &mesh_data, provide_flags);
2922 if (FAILED(hr)) goto cleanup;
2924 total_vertices = mesh_data.num_vertices;
2925 if (mesh_data.fvf & D3DFVF_NORMAL) {
2926 /* duplicate vertices with multiple normals */
2927 DWORD num_face_indices = mesh_data.num_poly_faces * 2 + mesh_data.num_tri_faces;
2928 duplications = HeapAlloc(GetProcessHeap(), 0, (mesh_data.num_vertices + num_face_indices) * sizeof(*duplications));
2929 if (!duplications) {
2933 for (i = 0; i < total_vertices; i++)
2935 duplications[i].normal_index = -1;
2936 list_init(&duplications[i].entry);
2938 for (i = 0; i < num_face_indices; i++) {
2939 DWORD vertex_index = mesh_data.indices[i];
2940 DWORD normal_index = mesh_data.normal_indices[i];
2941 struct vertex_duplication *dup_ptr = &duplications[vertex_index];
2943 if (dup_ptr->normal_index == -1) {
2944 dup_ptr->normal_index = normal_index;
2946 D3DXVECTOR3 *new_normal = &mesh_data.normals[normal_index];
2947 struct list *dup_list = &dup_ptr->entry;
2949 D3DXVECTOR3 *cur_normal = &mesh_data.normals[dup_ptr->normal_index];
2950 if (new_normal->x == cur_normal->x &&
2951 new_normal->y == cur_normal->y &&
2952 new_normal->z == cur_normal->z)
2954 mesh_data.indices[i] = dup_ptr - duplications;
2956 } else if (!list_next(dup_list, &dup_ptr->entry)) {
2957 dup_ptr = &duplications[total_vertices++];
2958 dup_ptr->normal_index = normal_index;
2959 list_add_tail(dup_list, &dup_ptr->entry);
2960 mesh_data.indices[i] = dup_ptr - duplications;
2963 dup_ptr = LIST_ENTRY(list_next(dup_list, &dup_ptr->entry),
2964 struct vertex_duplication, entry);
2971 hr = D3DXCreateMeshFVF(mesh_data.num_tri_faces, total_vertices, options, mesh_data.fvf, device, &d3dxmesh);
2972 if (FAILED(hr)) goto cleanup;
2974 hr = d3dxmesh->lpVtbl->LockVertexBuffer(d3dxmesh, 0, &vertices);
2975 if (FAILED(hr)) goto cleanup;
2978 for (i = 0; i < mesh_data.num_vertices; i++) {
2979 *(D3DXVECTOR3*)out_ptr = mesh_data.vertices[i];
2980 out_ptr += sizeof(D3DXVECTOR3);
2981 if (mesh_data.fvf & D3DFVF_NORMAL) {
2982 if (duplications[i].normal_index == -1)
2983 ZeroMemory(out_ptr, sizeof(D3DXVECTOR3));
2985 *(D3DXVECTOR3*)out_ptr = mesh_data.normals[duplications[i].normal_index];
2986 out_ptr += sizeof(D3DXVECTOR3);
2988 if (mesh_data.fvf & D3DFVF_DIFFUSE) {
2989 *(DWORD*)out_ptr = mesh_data.vertex_colors[i];
2990 out_ptr += sizeof(DWORD);
2992 if (mesh_data.fvf & D3DFVF_TEX1) {
2993 *(D3DXVECTOR2*)out_ptr = mesh_data.tex_coords[i];
2994 out_ptr += sizeof(D3DXVECTOR2);
2997 if (mesh_data.fvf & D3DFVF_NORMAL) {
2998 DWORD vertex_size = D3DXGetFVFVertexSize(mesh_data.fvf);
3000 for (i = 0; i < mesh_data.num_vertices; i++) {
3001 struct vertex_duplication *dup_ptr;
3002 LIST_FOR_EACH_ENTRY(dup_ptr, &duplications[i].entry, struct vertex_duplication, entry)
3004 int j = dup_ptr - duplications;
3005 BYTE *dest_vertex = (BYTE*)vertices + j * vertex_size;
3007 memcpy(dest_vertex, out_ptr, vertex_size);
3008 dest_vertex += sizeof(D3DXVECTOR3);
3009 *(D3DXVECTOR3*)dest_vertex = mesh_data.normals[dup_ptr->normal_index];
3011 out_ptr += vertex_size;
3014 d3dxmesh->lpVtbl->UnlockVertexBuffer(d3dxmesh);
3016 hr = d3dxmesh->lpVtbl->LockIndexBuffer(d3dxmesh, 0, &indices);
3017 if (FAILED(hr)) goto cleanup;
3019 index_in_ptr = mesh_data.indices;
3020 #define FILL_INDEX_BUFFER(indices_var) \
3021 for (i = 0; i < mesh_data.num_poly_faces; i++) \
3023 DWORD count = mesh_data.num_tri_per_face[i]; \
3024 WORD first_index = *index_in_ptr++; \
3026 *indices_var++ = first_index; \
3027 *indices_var++ = *index_in_ptr; \
3029 *indices_var++ = *index_in_ptr; \
3033 if (options & D3DXMESH_32BIT) {
3034 DWORD *dword_indices = indices;
3035 FILL_INDEX_BUFFER(dword_indices)
3037 WORD *word_indices = indices;
3038 FILL_INDEX_BUFFER(word_indices)
3040 #undef FILL_INDEX_BUFFER
3041 d3dxmesh->lpVtbl->UnlockIndexBuffer(d3dxmesh);
3043 if (mesh_data.material_indices) {
3044 DWORD *attrib_buffer = NULL;
3045 hr = d3dxmesh->lpVtbl->LockAttributeBuffer(d3dxmesh, 0, &attrib_buffer);
3046 if (FAILED(hr)) goto cleanup;
3047 for (i = 0; i < mesh_data.num_poly_faces; i++)
3049 DWORD count = mesh_data.num_tri_per_face[i];
3051 *attrib_buffer++ = mesh_data.material_indices[i];
3053 d3dxmesh->lpVtbl->UnlockAttributeBuffer(d3dxmesh);
3055 hr = d3dxmesh->lpVtbl->OptimizeInplace(d3dxmesh,
3056 D3DXMESHOPT_ATTRSORT | D3DXMESHOPT_IGNOREVERTS | D3DXMESHOPT_DONOTSPLIT,
3057 NULL, NULL, NULL, NULL);
3058 if (FAILED(hr)) goto cleanup;
3061 if (mesh_data.num_materials && (materials_out || effects_out)) {
3062 DWORD buffer_size = mesh_data.num_materials * sizeof(D3DXMATERIAL);
3063 char *strings_out_ptr;
3064 D3DXMATERIAL *materials_ptr;
3066 for (i = 0; i < mesh_data.num_materials; i++) {
3067 if (mesh_data.materials[i].pTextureFilename)
3068 buffer_size += strlen(mesh_data.materials[i].pTextureFilename) + 1;
3071 hr = D3DXCreateBuffer(buffer_size, &materials);
3072 if (FAILED(hr)) goto cleanup;
3074 materials_ptr = ID3DXBuffer_GetBufferPointer(materials);
3075 memcpy(materials_ptr, mesh_data.materials, mesh_data.num_materials * sizeof(D3DXMATERIAL));
3076 strings_out_ptr = (char*)(materials_ptr + mesh_data.num_materials);
3077 for (i = 0; i < mesh_data.num_materials; i++) {
3078 if (materials_ptr[i].pTextureFilename) {
3079 strcpy(strings_out_ptr, mesh_data.materials[i].pTextureFilename);
3080 materials_ptr[i].pTextureFilename = strings_out_ptr;
3081 strings_out_ptr += strlen(mesh_data.materials[i].pTextureFilename) + 1;
3086 if (mesh_data.num_materials && effects_out) {
3087 hr = generate_effects(materials, mesh_data.num_materials, &effects);
3088 if (FAILED(hr)) goto cleanup;
3090 if (!materials_out) {
3091 ID3DXBuffer_Release(materials);
3096 if (adjacency_out) {
3097 hr = D3DXCreateBuffer(mesh_data.num_tri_faces * 3 * sizeof(DWORD), &adjacency);
3098 if (FAILED(hr)) goto cleanup;
3099 hr = d3dxmesh->lpVtbl->GenerateAdjacency(d3dxmesh, 0.0f, ID3DXBuffer_GetBufferPointer(adjacency));
3100 if (FAILED(hr)) goto cleanup;
3103 *mesh_out = d3dxmesh;
3104 if (adjacency_out) *adjacency_out = adjacency;
3105 if (num_materials_out) *num_materials_out = mesh_data.num_materials;
3106 if (materials_out) *materials_out = materials;
3107 if (effects_out) *effects_out = effects;
3108 if (skin_info_out) *skin_info_out = NULL;
3113 if (d3dxmesh) IUnknown_Release(d3dxmesh);
3114 if (adjacency) ID3DXBuffer_Release(adjacency);
3115 if (materials) ID3DXBuffer_Release(materials);
3116 if (effects) ID3DXBuffer_Release(effects);
3118 HeapFree(GetProcessHeap(), 0, mesh_data.vertices);
3119 HeapFree(GetProcessHeap(), 0, mesh_data.num_tri_per_face);
3120 HeapFree(GetProcessHeap(), 0, mesh_data.indices);
3121 HeapFree(GetProcessHeap(), 0, mesh_data.normals);
3122 HeapFree(GetProcessHeap(), 0, mesh_data.normal_indices);
3123 destroy_materials(&mesh_data);
3124 HeapFree(GetProcessHeap(), 0, mesh_data.tex_coords);
3125 HeapFree(GetProcessHeap(), 0, mesh_data.vertex_colors);
3126 HeapFree(GetProcessHeap(), 0, duplications);
3130 HRESULT WINAPI D3DXLoadMeshHierarchyFromXA(LPCSTR filename,
3132 LPDIRECT3DDEVICE9 device,
3133 LPD3DXALLOCATEHIERARCHY alloc_hier,
3134 LPD3DXLOADUSERDATA load_user_data,
3135 LPD3DXFRAME *frame_hierarchy,
3136 LPD3DXANIMATIONCONTROLLER *anim_controller)
3142 TRACE("(%s, %x, %p, %p, %p, %p, %p)\n", debugstr_a(filename), options,
3143 device, alloc_hier, load_user_data, frame_hierarchy, anim_controller);
3146 return D3DERR_INVALIDCALL;
3148 len = MultiByteToWideChar(CP_ACP, 0, filename, -1, NULL, 0);
3149 filenameW = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
3150 if (!filenameW) return E_OUTOFMEMORY;
3151 MultiByteToWideChar(CP_ACP, 0, filename, -1, filenameW, len);
3153 hr = D3DXLoadMeshHierarchyFromXW(filenameW, options, device,
3154 alloc_hier, load_user_data, frame_hierarchy, anim_controller);
3155 HeapFree(GetProcessHeap(), 0, filenameW);
3160 HRESULT WINAPI D3DXLoadMeshHierarchyFromXW(LPCWSTR filename,
3162 LPDIRECT3DDEVICE9 device,
3163 LPD3DXALLOCATEHIERARCHY alloc_hier,
3164 LPD3DXLOADUSERDATA load_user_data,
3165 LPD3DXFRAME *frame_hierarchy,
3166 LPD3DXANIMATIONCONTROLLER *anim_controller)
3172 TRACE("(%s, %x, %p, %p, %p, %p, %p)\n", debugstr_w(filename), options,
3173 device, alloc_hier, load_user_data, frame_hierarchy, anim_controller);
3176 return D3DERR_INVALIDCALL;
3178 hr = map_view_of_file(filename, &buffer, &size);
3180 return D3DXERR_INVALIDDATA;
3182 hr = D3DXLoadMeshHierarchyFromXInMemory(buffer, size, options, device,
3183 alloc_hier, load_user_data, frame_hierarchy, anim_controller);
3185 UnmapViewOfFile(buffer);
3190 static HRESULT filedata_get_name(IDirectXFileData *filedata, char **name)
3195 hr = IDirectXFileData_GetName(filedata, NULL, &name_len);
3196 if (FAILED(hr)) return hr;
3200 *name = HeapAlloc(GetProcessHeap(), 0, name_len);
3201 if (!*name) return E_OUTOFMEMORY;
3203 hr = IDirectXFileObject_GetName(filedata, *name, &name_len);
3205 HeapFree(GetProcessHeap(), 0, name);
3212 static HRESULT load_mesh_container(IDirectXFileData *filedata,
3214 LPDIRECT3DDEVICE9 device,
3215 LPD3DXALLOCATEHIERARCHY alloc_hier,
3216 D3DXMESHCONTAINER **mesh_container)
3219 ID3DXBuffer *adjacency = NULL;
3220 ID3DXBuffer *materials = NULL;
3221 ID3DXBuffer *effects = NULL;
3222 ID3DXSkinInfo *skin_info = NULL;
3223 D3DXMESHDATA mesh_data;
3224 DWORD num_materials = 0;
3227 mesh_data.Type = D3DXMESHTYPE_MESH;
3228 mesh_data.u.pMesh = NULL;
3230 hr = load_skin_mesh_from_xof(filedata, options, device,
3231 &adjacency, &materials, &effects, &num_materials,
3232 &skin_info, &mesh_data.u.pMesh);
3233 if (FAILED(hr)) return hr;
3235 hr = filedata_get_name(filedata, &name);
3236 if (FAILED(hr)) goto cleanup;
3238 hr = alloc_hier->lpVtbl->CreateMeshContainer(alloc_hier, name, &mesh_data,
3239 materials ? ID3DXBuffer_GetBufferPointer(materials) : NULL,
3240 effects ? ID3DXBuffer_GetBufferPointer(effects) : NULL,
3242 adjacency ? ID3DXBuffer_GetBufferPointer(adjacency) : NULL,
3243 skin_info, mesh_container);
3246 if (materials) ID3DXBuffer_Release(materials);
3247 if (effects) ID3DXBuffer_Release(effects);
3248 if (adjacency) ID3DXBuffer_Release(adjacency);
3249 if (skin_info) IUnknown_Release(skin_info);
3250 if (mesh_data.u.pMesh) IUnknown_Release(mesh_data.u.pMesh);
3251 HeapFree(GetProcessHeap(), 0, name);
3255 static HRESULT parse_transform_matrix(IDirectXFileData *filedata, D3DXMATRIX *transform)
3261 /* template Matrix4x4 {
3262 * array FLOAT matrix[16];
3264 * template FrameTransformMatrix {
3265 * Matrix4x4 frameMatrix;
3269 hr = IDirectXFileData_GetData(filedata, NULL, &data_size, (void**)&data);
3270 if (FAILED(hr)) return hr;
3272 if (data_size != sizeof(D3DXMATRIX)) {
3273 WARN("incorrect data size (%u bytes)\n", data_size);
3277 memcpy(transform, data, sizeof(D3DXMATRIX));
3282 static HRESULT load_frame(IDirectXFileData *filedata,
3284 LPDIRECT3DDEVICE9 device,
3285 LPD3DXALLOCATEHIERARCHY alloc_hier,
3286 D3DXFRAME **frame_out)
3290 IDirectXFileData *child;
3292 D3DXFRAME *frame = NULL;
3293 D3DXMESHCONTAINER **next_container;
3294 D3DXFRAME **next_child;
3296 hr = filedata_get_name(filedata, &name);
3297 if (FAILED(hr)) return hr;
3299 hr = alloc_hier->lpVtbl->CreateFrame(alloc_hier, name, frame_out);
3300 HeapFree(GetProcessHeap(), 0, name);
3301 if (FAILED(hr)) return E_FAIL;
3304 D3DXMatrixIdentity(&frame->TransformationMatrix);
3305 next_child = &frame->pFrameFirstChild;
3306 next_container = &frame->pMeshContainer;
3308 while (SUCCEEDED(hr = get_next_child(filedata, &child, &type)))
3310 if (IsEqualGUID(type, &TID_D3DRMMesh)) {
3311 hr = load_mesh_container(child, options, device, alloc_hier, next_container);
3313 next_container = &(*next_container)->pNextMeshContainer;
3314 } else if (IsEqualGUID(type, &TID_D3DRMFrameTransformMatrix)) {
3315 hr = parse_transform_matrix(child, &frame->TransformationMatrix);
3316 } else if (IsEqualGUID(type, &TID_D3DRMFrame)) {
3317 hr = load_frame(child, options, device, alloc_hier, next_child);
3319 next_child = &(*next_child)->pFrameSibling;
3321 if (FAILED(hr)) break;
3323 if (hr == DXFILEERR_NOMOREOBJECTS)
3329 HRESULT WINAPI D3DXLoadMeshHierarchyFromXInMemory(LPCVOID memory,
3332 LPDIRECT3DDEVICE9 device,
3333 LPD3DXALLOCATEHIERARCHY alloc_hier,
3334 LPD3DXLOADUSERDATA load_user_data,
3335 LPD3DXFRAME *frame_hierarchy,
3336 LPD3DXANIMATIONCONTROLLER *anim_controller)
3339 IDirectXFile *dxfile = NULL;
3340 IDirectXFileEnumObject *enumobj = NULL;
3341 IDirectXFileData *filedata = NULL;
3342 DXFILELOADMEMORY source;
3343 D3DXFRAME *first_frame = NULL;
3344 D3DXFRAME **next_frame = &first_frame;
3346 TRACE("(%p, %u, %x, %p, %p, %p, %p, %p)\n", memory, memory_size, options,
3347 device, alloc_hier, load_user_data, frame_hierarchy, anim_controller);
3349 if (!memory || !memory_size || !device || !frame_hierarchy || !alloc_hier)
3350 return D3DERR_INVALIDCALL;
3351 if (load_user_data || anim_controller) {
3353 FIXME("Loading user data not implemented\n");
3354 if (anim_controller)
3355 FIXME("Animation controller creation not implemented\n");
3359 hr = DirectXFileCreate(&dxfile);
3360 if (FAILED(hr)) goto cleanup;
3362 hr = IDirectXFile_RegisterTemplates(dxfile, D3DRM_XTEMPLATES, D3DRM_XTEMPLATE_BYTES);
3363 if (FAILED(hr)) goto cleanup;
3365 source.lpMemory = (void*)memory;
3366 source.dSize = memory_size;
3367 hr = IDirectXFile_CreateEnumObject(dxfile, &source, DXFILELOAD_FROMMEMORY, &enumobj);
3368 if (FAILED(hr)) goto cleanup;
3370 while (SUCCEEDED(hr = IDirectXFileEnumObject_GetNextDataObject(enumobj, &filedata)))
3372 const GUID *guid = NULL;
3374 hr = IDirectXFileData_GetType(filedata, &guid);
3375 if (SUCCEEDED(hr)) {
3376 if (IsEqualGUID(guid, &TID_D3DRMMesh)) {
3377 hr = alloc_hier->lpVtbl->CreateFrame(alloc_hier, NULL, next_frame);
3383 D3DXMatrixIdentity(&(*next_frame)->TransformationMatrix);
3385 hr = load_mesh_container(filedata, options, device, alloc_hier, &(*next_frame)->pMeshContainer);
3386 if (FAILED(hr)) goto cleanup;
3387 } else if (IsEqualGUID(guid, &TID_D3DRMFrame)) {
3388 hr = load_frame(filedata, options, device, alloc_hier, next_frame);
3389 if (FAILED(hr)) goto cleanup;
3392 next_frame = &(*next_frame)->pFrameSibling;
3395 IDirectXFileData_Release(filedata);
3400 if (hr != DXFILEERR_NOMOREOBJECTS)
3405 } else if (first_frame->pFrameSibling) {
3406 D3DXFRAME *root_frame = NULL;
3407 hr = alloc_hier->lpVtbl->CreateFrame(alloc_hier, NULL, &root_frame);
3412 D3DXMatrixIdentity(&root_frame->TransformationMatrix);
3413 root_frame->pFrameFirstChild = first_frame;
3414 *frame_hierarchy = root_frame;
3417 *frame_hierarchy = first_frame;
3422 if (FAILED(hr) && first_frame) D3DXFrameDestroy(first_frame, alloc_hier);
3423 if (filedata) IDirectXFileData_Release(filedata);
3424 if (enumobj) IDirectXFileEnumObject_Release(enumobj);
3425 if (dxfile) IDirectXFile_Release(dxfile);
3429 HRESULT WINAPI D3DXFrameDestroy(LPD3DXFRAME frame, LPD3DXALLOCATEHIERARCHY alloc_hier)
3434 TRACE("(%p, %p)\n", frame, alloc_hier);
3436 if (!frame || !alloc_hier)
3437 return D3DERR_INVALIDCALL;
3440 D3DXMESHCONTAINER *container;
3441 D3DXFRAME *current_frame;
3443 if (frame->pFrameSibling) {
3444 current_frame = frame->pFrameSibling;
3445 frame->pFrameSibling = current_frame->pFrameSibling;
3446 current_frame->pFrameSibling = NULL;
3448 current_frame = frame;
3452 if (current_frame->pFrameFirstChild) {
3453 hr = D3DXFrameDestroy(current_frame->pFrameFirstChild, alloc_hier);
3454 if (FAILED(hr)) return hr;
3455 current_frame->pFrameFirstChild = NULL;
3458 container = current_frame->pMeshContainer;
3460 D3DXMESHCONTAINER *next_container = container->pNextMeshContainer;
3461 hr = alloc_hier->lpVtbl->DestroyMeshContainer(alloc_hier, container);
3462 if (FAILED(hr)) return hr;
3463 container = next_container;
3465 hr = alloc_hier->lpVtbl->DestroyFrame(alloc_hier, current_frame);
3466 if (FAILED(hr)) return hr;
3471 HRESULT WINAPI D3DXLoadMeshFromXA(LPCSTR filename,
3473 LPDIRECT3DDEVICE9 device,
3474 LPD3DXBUFFER *adjacency,
3475 LPD3DXBUFFER *materials,
3476 LPD3DXBUFFER *effect_instances,
3477 DWORD *num_materials,
3484 TRACE("(%s, %x, %p, %p, %p, %p, %p, %p)\n", debugstr_a(filename), options,
3485 device, adjacency, materials, effect_instances, num_materials, mesh);
3488 return D3DERR_INVALIDCALL;
3490 len = MultiByteToWideChar(CP_ACP, 0, filename, -1, NULL, 0);
3491 filenameW = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
3492 if (!filenameW) return E_OUTOFMEMORY;
3493 MultiByteToWideChar(CP_ACP, 0, filename, -1, filenameW, len);
3495 hr = D3DXLoadMeshFromXW(filenameW, options, device, adjacency, materials,
3496 effect_instances, num_materials, mesh);
3497 HeapFree(GetProcessHeap(), 0, filenameW);
3502 HRESULT WINAPI D3DXLoadMeshFromXW(LPCWSTR filename,
3504 LPDIRECT3DDEVICE9 device,
3505 LPD3DXBUFFER *adjacency,
3506 LPD3DXBUFFER *materials,
3507 LPD3DXBUFFER *effect_instances,
3508 DWORD *num_materials,
3515 TRACE("(%s, %x, %p, %p, %p, %p, %p, %p)\n", debugstr_w(filename), options,
3516 device, adjacency, materials, effect_instances, num_materials, mesh);
3519 return D3DERR_INVALIDCALL;
3521 hr = map_view_of_file(filename, &buffer, &size);
3523 return D3DXERR_INVALIDDATA;
3525 hr = D3DXLoadMeshFromXInMemory(buffer, size, options, device, adjacency,
3526 materials, effect_instances, num_materials, mesh);
3528 UnmapViewOfFile(buffer);
3533 HRESULT WINAPI D3DXLoadMeshFromXResource(HMODULE module,
3537 LPDIRECT3DDEVICE9 device,
3538 LPD3DXBUFFER *adjacency,
3539 LPD3DXBUFFER *materials,
3540 LPD3DXBUFFER *effect_instances,
3541 DWORD *num_materials,
3549 TRACE("(%p, %s, %s, %x, %p, %p, %p, %p, %p, %p)\n",
3550 module, debugstr_a(name), debugstr_a(type), options, device,
3551 adjacency, materials, effect_instances, num_materials, mesh);
3553 resinfo = FindResourceA(module, name, type);
3554 if (!resinfo) return D3DXERR_INVALIDDATA;
3556 hr = load_resource_into_memory(module, resinfo, &buffer, &size);
3557 if (FAILED(hr)) return D3DXERR_INVALIDDATA;
3559 return D3DXLoadMeshFromXInMemory(buffer, size, options, device, adjacency,
3560 materials, effect_instances, num_materials, mesh);
3563 struct mesh_container
3567 ID3DXBuffer *adjacency;
3568 ID3DXBuffer *materials;
3569 ID3DXBuffer *effects;
3570 DWORD num_materials;
3571 D3DXMATRIX transform;
3574 static HRESULT parse_frame(IDirectXFileData *filedata,
3576 LPDIRECT3DDEVICE9 device,
3577 const D3DXMATRIX *parent_transform,
3578 struct list *container_list,
3579 DWORD provide_flags)
3582 D3DXMATRIX transform = *parent_transform;
3583 IDirectXFileData *child;
3586 while (SUCCEEDED(hr = get_next_child(filedata, &child, &type)))
3588 if (IsEqualGUID(type, &TID_D3DRMMesh)) {
3589 struct mesh_container *container = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(*container));
3594 list_add_tail(container_list, &container->entry);
3595 container->transform = transform;
3597 hr = load_skin_mesh_from_xof(child, options, device,
3598 (provide_flags & PROVIDE_ADJACENCY) ? &container->adjacency : NULL,
3599 (provide_flags & PROVIDE_MATERIALS) ? &container->materials : NULL,
3600 NULL, &container->num_materials, NULL, &container->mesh);
3601 } else if (IsEqualGUID(type, &TID_D3DRMFrameTransformMatrix)) {
3602 D3DXMATRIX new_transform;
3603 hr = parse_transform_matrix(child, &new_transform);
3604 D3DXMatrixMultiply(&transform, &transform, &new_transform);
3605 } else if (IsEqualGUID(type, &TID_D3DRMFrame)) {
3606 hr = parse_frame(child, options, device, &transform, container_list, provide_flags);
3608 if (FAILED(hr)) break;
3610 return hr == DXFILEERR_NOMOREOBJECTS ? D3D_OK : hr;
3613 HRESULT WINAPI D3DXLoadMeshFromXInMemory(LPCVOID memory,
3616 LPDIRECT3DDEVICE9 device,
3617 LPD3DXBUFFER *adjacency_out,
3618 LPD3DXBUFFER *materials_out,
3619 LPD3DXBUFFER *effects_out,
3620 DWORD *num_materials_out,
3621 LPD3DXMESH *mesh_out)
3624 IDirectXFile *dxfile = NULL;
3625 IDirectXFileEnumObject *enumobj = NULL;
3626 IDirectXFileData *filedata = NULL;
3627 DXFILELOADMEMORY source;
3628 ID3DXBuffer *materials = NULL;
3629 ID3DXBuffer *effects = NULL;
3630 ID3DXBuffer *adjacency = NULL;
3631 struct list container_list = LIST_INIT(container_list);
3632 struct mesh_container *container_ptr, *next_container_ptr;
3633 DWORD num_materials;
3634 DWORD num_faces, num_vertices;
3635 D3DXMATRIX identity;
3637 DWORD provide_flags = 0;
3639 ID3DXMesh *concat_mesh = NULL;
3640 D3DVERTEXELEMENT9 concat_decl[MAX_FVF_DECL_SIZE];
3641 BYTE *concat_vertices = NULL;
3642 void *concat_indices = NULL;
3644 DWORD concat_vertex_size;
3646 TRACE("(%p, %u, %x, %p, %p, %p, %p, %p, %p)\n", memory, memory_size, options,
3647 device, adjacency_out, materials_out, effects_out, num_materials_out, mesh_out);
3649 if (!memory || !memory_size || !device || !mesh_out)
3650 return D3DERR_INVALIDCALL;
3652 hr = DirectXFileCreate(&dxfile);
3653 if (FAILED(hr)) goto cleanup;
3655 hr = IDirectXFile_RegisterTemplates(dxfile, D3DRM_XTEMPLATES, D3DRM_XTEMPLATE_BYTES);
3656 if (FAILED(hr)) goto cleanup;
3658 source.lpMemory = (void*)memory;
3659 source.dSize = memory_size;
3660 hr = IDirectXFile_CreateEnumObject(dxfile, &source, DXFILELOAD_FROMMEMORY, &enumobj);
3661 if (FAILED(hr)) goto cleanup;
3663 D3DXMatrixIdentity(&identity);
3664 if (adjacency_out) provide_flags |= PROVIDE_ADJACENCY;
3665 if (materials_out || effects_out) provide_flags |= PROVIDE_MATERIALS;
3667 while (SUCCEEDED(hr = IDirectXFileEnumObject_GetNextDataObject(enumobj, &filedata)))
3669 const GUID *guid = NULL;
3671 hr = IDirectXFileData_GetType(filedata, &guid);
3672 if (SUCCEEDED(hr)) {
3673 if (IsEqualGUID(guid, &TID_D3DRMMesh)) {
3674 container_ptr = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(*container_ptr));
3675 if (!container_ptr) {
3679 list_add_tail(&container_list, &container_ptr->entry);
3680 D3DXMatrixIdentity(&container_ptr->transform);
3682 hr = load_skin_mesh_from_xof(filedata, options, device,
3683 (provide_flags & PROVIDE_ADJACENCY) ? &container_ptr->adjacency : NULL,
3684 (provide_flags & PROVIDE_MATERIALS) ? &container_ptr->materials : NULL,
3685 NULL, &container_ptr->num_materials, NULL, &container_ptr->mesh);
3686 } else if (IsEqualGUID(guid, &TID_D3DRMFrame)) {
3687 hr = parse_frame(filedata, options, device, &identity, &container_list, provide_flags);
3689 if (FAILED(hr)) goto cleanup;
3691 IDirectXFileData_Release(filedata);
3696 if (hr != DXFILEERR_NOMOREOBJECTS)
3699 IDirectXFileEnumObject_Release(enumobj);
3701 IDirectXFile_Release(dxfile);
3704 if (list_empty(&container_list)) {
3713 LIST_FOR_EACH_ENTRY(container_ptr, &container_list, struct mesh_container, entry)
3715 ID3DXMesh *mesh = container_ptr->mesh;
3716 fvf |= mesh->lpVtbl->GetFVF(mesh);
3717 num_faces += mesh->lpVtbl->GetNumFaces(mesh);
3718 num_vertices += mesh->lpVtbl->GetNumVertices(mesh);
3719 num_materials += container_ptr->num_materials;
3722 hr = D3DXCreateMeshFVF(num_faces, num_vertices, options, fvf, device, &concat_mesh);
3723 if (FAILED(hr)) goto cleanup;
3725 hr = concat_mesh->lpVtbl->GetDeclaration(concat_mesh, concat_decl);
3726 if (FAILED(hr)) goto cleanup;
3728 concat_vertex_size = D3DXGetDeclVertexSize(concat_decl, 0);
3730 hr = concat_mesh->lpVtbl->LockVertexBuffer(concat_mesh, 0, (void**)&concat_vertices);
3731 if (FAILED(hr)) goto cleanup;
3733 LIST_FOR_EACH_ENTRY(container_ptr, &container_list, struct mesh_container, entry)
3735 D3DVERTEXELEMENT9 mesh_decl[MAX_FVF_DECL_SIZE];
3736 ID3DXMesh *mesh = container_ptr->mesh;
3737 DWORD num_mesh_vertices = mesh->lpVtbl->GetNumVertices(mesh);
3738 DWORD mesh_vertex_size;
3739 const BYTE *mesh_vertices;
3741 hr = mesh->lpVtbl->GetDeclaration(mesh, mesh_decl);
3742 if (FAILED(hr)) goto cleanup;
3744 mesh_vertex_size = D3DXGetDeclVertexSize(mesh_decl, 0);
3746 hr = mesh->lpVtbl->LockVertexBuffer(mesh, D3DLOCK_READONLY, (void**)&mesh_vertices);
3747 if (FAILED(hr)) goto cleanup;
3749 for (i = 0; i < num_mesh_vertices; i++) {
3753 D3DXVec3TransformCoord((D3DXVECTOR3*)concat_vertices,
3754 (D3DXVECTOR3*)mesh_vertices,
3755 &container_ptr->transform);
3756 for (j = 1; concat_decl[j].Stream != 0xff; j++)
3758 if (concat_decl[j].Usage == mesh_decl[k].Usage &&
3759 concat_decl[j].UsageIndex == mesh_decl[k].UsageIndex)
3761 if (concat_decl[j].Usage == D3DDECLUSAGE_NORMAL) {
3762 D3DXVec3TransformCoord((D3DXVECTOR3*)(concat_vertices + concat_decl[j].Offset),
3763 (D3DXVECTOR3*)(mesh_vertices + mesh_decl[k].Offset),
3764 &container_ptr->transform);
3766 memcpy(concat_vertices + concat_decl[j].Offset,
3767 mesh_vertices + mesh_decl[k].Offset,
3768 d3dx_decltype_size[mesh_decl[k].Type]);
3773 mesh_vertices += mesh_vertex_size;
3774 concat_vertices += concat_vertex_size;
3777 mesh->lpVtbl->UnlockVertexBuffer(mesh);
3780 concat_mesh->lpVtbl->UnlockVertexBuffer(concat_mesh);
3781 concat_vertices = NULL;
3783 hr = concat_mesh->lpVtbl->LockIndexBuffer(concat_mesh, 0, &concat_indices);
3784 if (FAILED(hr)) goto cleanup;
3787 LIST_FOR_EACH_ENTRY(container_ptr, &container_list, struct mesh_container, entry)
3789 ID3DXMesh *mesh = container_ptr->mesh;
3790 const void *mesh_indices;
3791 DWORD num_mesh_faces = mesh->lpVtbl->GetNumFaces(mesh);
3794 hr = mesh->lpVtbl->LockIndexBuffer(mesh, D3DLOCK_READONLY, (void**)&mesh_indices);
3795 if (FAILED(hr)) goto cleanup;
3797 if (options & D3DXMESH_32BIT) {
3798 DWORD *dest = concat_indices;
3799 const DWORD *src = mesh_indices;
3800 for (i = 0; i < num_mesh_faces * 3; i++)
3801 *dest++ = index_offset + *src++;
3802 concat_indices = dest;
3804 WORD *dest = concat_indices;
3805 const WORD *src = mesh_indices;
3806 for (i = 0; i < num_mesh_faces * 3; i++)
3807 *dest++ = index_offset + *src++;
3808 concat_indices = dest;
3810 mesh->lpVtbl->UnlockIndexBuffer(mesh);
3812 index_offset += num_mesh_faces * 3;
3815 concat_mesh->lpVtbl->UnlockIndexBuffer(concat_mesh);
3816 concat_indices = NULL;
3818 if (num_materials) {
3819 DWORD *concat_attrib_buffer = NULL;
3822 hr = concat_mesh->lpVtbl->LockAttributeBuffer(concat_mesh, 0, &concat_attrib_buffer);
3823 if (FAILED(hr)) goto cleanup;
3825 LIST_FOR_EACH_ENTRY(container_ptr, &container_list, struct mesh_container, entry)
3827 ID3DXMesh *mesh = container_ptr->mesh;
3828 const DWORD *mesh_attrib_buffer = NULL;
3829 DWORD count = mesh->lpVtbl->GetNumFaces(mesh);
3831 hr = mesh->lpVtbl->LockAttributeBuffer(mesh, D3DLOCK_READONLY, (DWORD**)&mesh_attrib_buffer);
3833 concat_mesh->lpVtbl->UnlockAttributeBuffer(concat_mesh);
3838 *concat_attrib_buffer++ = offset + *mesh_attrib_buffer++;
3840 mesh->lpVtbl->UnlockAttributeBuffer(mesh);
3841 offset += container_ptr->num_materials;
3843 concat_mesh->lpVtbl->UnlockAttributeBuffer(concat_mesh);
3846 if (materials_out || effects_out) {
3847 D3DXMATERIAL *out_ptr;
3848 if (!num_materials) {
3849 /* create default material */
3850 hr = D3DXCreateBuffer(sizeof(D3DXMATERIAL), &materials);
3851 if (FAILED(hr)) goto cleanup;
3853 out_ptr = ID3DXBuffer_GetBufferPointer(materials);
3854 out_ptr->MatD3D.Diffuse.r = 0.5f;
3855 out_ptr->MatD3D.Diffuse.g = 0.5f;
3856 out_ptr->MatD3D.Diffuse.b = 0.5f;
3857 out_ptr->MatD3D.Specular.r = 0.5f;
3858 out_ptr->MatD3D.Specular.g = 0.5f;
3859 out_ptr->MatD3D.Specular.b = 0.5f;
3860 /* D3DXCreateBuffer initializes the rest to zero */
3862 DWORD buffer_size = num_materials * sizeof(D3DXMATERIAL);
3863 char *strings_out_ptr;
3865 LIST_FOR_EACH_ENTRY(container_ptr, &container_list, struct mesh_container, entry)
3867 if (container_ptr->materials) {
3868 const D3DXMATERIAL *in_ptr = ID3DXBuffer_GetBufferPointer(container_ptr->materials);
3869 for (i = 0; i < container_ptr->num_materials; i++)
3871 if (in_ptr->pTextureFilename)
3872 buffer_size += strlen(in_ptr->pTextureFilename) + 1;
3878 hr = D3DXCreateBuffer(buffer_size, &materials);
3879 if (FAILED(hr)) goto cleanup;
3880 out_ptr = ID3DXBuffer_GetBufferPointer(materials);
3881 strings_out_ptr = (char*)(out_ptr + num_materials);
3883 LIST_FOR_EACH_ENTRY(container_ptr, &container_list, struct mesh_container, entry)
3885 if (container_ptr->materials) {
3886 const D3DXMATERIAL *in_ptr = ID3DXBuffer_GetBufferPointer(container_ptr->materials);
3887 for (i = 0; i < container_ptr->num_materials; i++)
3889 out_ptr->MatD3D = in_ptr->MatD3D;
3890 if (in_ptr->pTextureFilename) {
3891 out_ptr->pTextureFilename = strings_out_ptr;
3892 strcpy(out_ptr->pTextureFilename, in_ptr->pTextureFilename);
3893 strings_out_ptr += strlen(in_ptr->pTextureFilename) + 1;
3906 generate_effects(materials, num_materials, &effects);
3907 if (!materials_out) {
3908 ID3DXBuffer_Release(materials);
3913 if (adjacency_out) {
3914 if (!list_next(&container_list, list_head(&container_list))) {
3915 container_ptr = LIST_ENTRY(list_head(&container_list), struct mesh_container, entry);
3916 adjacency = container_ptr->adjacency;
3917 container_ptr->adjacency = NULL;
3922 hr = D3DXCreateBuffer(num_faces * 3 * sizeof(DWORD), &adjacency);
3923 if (FAILED(hr)) goto cleanup;
3925 out_ptr = ID3DXBuffer_GetBufferPointer(adjacency);
3926 LIST_FOR_EACH_ENTRY(container_ptr, &container_list, struct mesh_container, entry)
3928 DWORD *in_ptr = ID3DXBuffer_GetBufferPointer(container_ptr->adjacency);
3929 int count = 3 * container_ptr->mesh->lpVtbl->GetNumFaces(container_ptr->mesh);
3931 for (i = 0; i < count; i++)
3932 *out_ptr++ = offset + *in_ptr++;
3939 *mesh_out = concat_mesh;
3940 if (adjacency_out) *adjacency_out = adjacency;
3941 if (materials_out) *materials_out = materials;
3942 if (effects_out) *effects_out = effects;
3943 if (num_materials_out) *num_materials_out = num_materials;
3947 if (concat_indices) concat_mesh->lpVtbl->UnlockIndexBuffer(concat_mesh);
3948 if (concat_vertices) concat_mesh->lpVtbl->UnlockVertexBuffer(concat_mesh);
3949 if (filedata) IDirectXFileData_Release(filedata);
3950 if (enumobj) IDirectXFileEnumObject_Release(enumobj);
3951 if (dxfile) IDirectXFile_Release(dxfile);
3953 if (concat_mesh) IUnknown_Release(concat_mesh);
3954 if (materials) ID3DXBuffer_Release(materials);
3955 if (effects) ID3DXBuffer_Release(effects);
3956 if (adjacency) ID3DXBuffer_Release(adjacency);
3958 LIST_FOR_EACH_ENTRY_SAFE(container_ptr, next_container_ptr, &container_list, struct mesh_container, entry)
3960 if (container_ptr->mesh) IUnknown_Release(container_ptr->mesh);
3961 if (container_ptr->adjacency) ID3DXBuffer_Release(container_ptr->adjacency);
3962 if (container_ptr->materials) ID3DXBuffer_Release(container_ptr->materials);
3963 if (container_ptr->effects) ID3DXBuffer_Release(container_ptr->effects);
3964 HeapFree(GetProcessHeap(), 0, container_ptr);
3969 HRESULT WINAPI D3DXCreateBox(LPDIRECT3DDEVICE9 device, FLOAT width, FLOAT height,
3970 FLOAT depth, LPD3DXMESH* mesh, LPD3DXBUFFER* adjacency)
3972 FIXME("(%p, %f, %f, %f, %p, %p): stub\n", device, width, height, depth, mesh, adjacency);
3979 D3DXVECTOR3 position;
3983 typedef WORD face[3];
3991 static void free_sincos_table(struct sincos_table *sincos_table)
3993 HeapFree(GetProcessHeap(), 0, sincos_table->cos);
3994 HeapFree(GetProcessHeap(), 0, sincos_table->sin);
3997 /* pre compute sine and cosine tables; caller must free */
3998 static BOOL compute_sincos_table(struct sincos_table *sincos_table, float angle_start, float angle_step, int n)
4003 sincos_table->sin = HeapAlloc(GetProcessHeap(), 0, n * sizeof(*sincos_table->sin));
4004 if (!sincos_table->sin)
4008 sincos_table->cos = HeapAlloc(GetProcessHeap(), 0, n * sizeof(*sincos_table->cos));
4009 if (!sincos_table->cos)
4011 HeapFree(GetProcessHeap(), 0, sincos_table->sin);
4015 angle = angle_start;
4016 for (i = 0; i < n; i++)
4018 sincos_table->sin[i] = sin(angle);
4019 sincos_table->cos[i] = cos(angle);
4020 angle += angle_step;
4026 static WORD vertex_index(UINT slices, int slice, int stack)
4028 return stack*slices+slice+1;
4031 HRESULT WINAPI D3DXCreateSphere(LPDIRECT3DDEVICE9 device, FLOAT radius, UINT slices,
4032 UINT stacks, LPD3DXMESH* mesh, LPD3DXBUFFER* adjacency)
4034 DWORD number_of_vertices, number_of_faces;
4037 struct vertex *vertices;
4039 float phi_step, phi_start;
4040 struct sincos_table phi;
4041 float theta_step, theta, sin_theta, cos_theta;
4045 TRACE("(%p, %f, %u, %u, %p, %p)\n", device, radius, slices, stacks, mesh, adjacency);
4047 if (!device || radius < 0.0f || slices < 2 || stacks < 2 || !mesh)
4049 return D3DERR_INVALIDCALL;
4054 FIXME("Case of adjacency != NULL not implemented.\n");
4058 number_of_vertices = 2 + slices * (stacks-1);
4059 number_of_faces = 2 * slices + (stacks - 2) * (2 * slices);
4061 hr = D3DXCreateMeshFVF(number_of_faces, number_of_vertices, D3DXMESH_MANAGED,
4062 D3DFVF_XYZ | D3DFVF_NORMAL, device, &sphere);
4068 hr = sphere->lpVtbl->LockVertexBuffer(sphere, 0, (LPVOID *)&vertices);
4071 sphere->lpVtbl->Release(sphere);
4075 hr = sphere->lpVtbl->LockIndexBuffer(sphere, 0, (LPVOID *)&faces);
4078 sphere->lpVtbl->UnlockVertexBuffer(sphere);
4079 sphere->lpVtbl->Release(sphere);
4083 /* phi = angle on xz plane wrt z axis */
4084 phi_step = -2 * M_PI / slices;
4085 phi_start = M_PI / 2;
4087 if (!compute_sincos_table(&phi, phi_start, phi_step, slices))
4089 sphere->lpVtbl->UnlockIndexBuffer(sphere);
4090 sphere->lpVtbl->UnlockVertexBuffer(sphere);
4091 sphere->lpVtbl->Release(sphere);
4092 return E_OUTOFMEMORY;
4095 /* theta = angle on xy plane wrt x axis */
4096 theta_step = M_PI / stacks;
4102 vertices[vertex].normal.x = 0.0f;
4103 vertices[vertex].normal.y = 0.0f;
4104 vertices[vertex].normal.z = 1.0f;
4105 vertices[vertex].position.x = 0.0f;
4106 vertices[vertex].position.y = 0.0f;
4107 vertices[vertex].position.z = radius;
4110 for (stack = 0; stack < stacks - 1; stack++)
4112 sin_theta = sin(theta);
4113 cos_theta = cos(theta);
4115 for (slice = 0; slice < slices; slice++)
4117 vertices[vertex].normal.x = sin_theta * phi.cos[slice];
4118 vertices[vertex].normal.y = sin_theta * phi.sin[slice];
4119 vertices[vertex].normal.z = cos_theta;
4120 vertices[vertex].position.x = radius * sin_theta * phi.cos[slice];
4121 vertices[vertex].position.y = radius * sin_theta * phi.sin[slice];
4122 vertices[vertex].position.z = radius * cos_theta;
4129 /* top stack is triangle fan */
4131 faces[face][1] = slice + 1;
4132 faces[face][2] = slice;
4137 /* stacks in between top and bottom are quad strips */
4138 faces[face][0] = vertex_index(slices, slice-1, stack-1);
4139 faces[face][1] = vertex_index(slices, slice, stack-1);
4140 faces[face][2] = vertex_index(slices, slice-1, stack);
4143 faces[face][0] = vertex_index(slices, slice, stack-1);
4144 faces[face][1] = vertex_index(slices, slice, stack);
4145 faces[face][2] = vertex_index(slices, slice-1, stack);
4151 theta += theta_step;
4157 faces[face][2] = slice;
4162 faces[face][0] = vertex_index(slices, slice-1, stack-1);
4163 faces[face][1] = vertex_index(slices, 0, stack-1);
4164 faces[face][2] = vertex_index(slices, slice-1, stack);
4167 faces[face][0] = vertex_index(slices, 0, stack-1);
4168 faces[face][1] = vertex_index(slices, 0, stack);
4169 faces[face][2] = vertex_index(slices, slice-1, stack);
4174 vertices[vertex].position.x = 0.0f;
4175 vertices[vertex].position.y = 0.0f;
4176 vertices[vertex].position.z = -radius;
4177 vertices[vertex].normal.x = 0.0f;
4178 vertices[vertex].normal.y = 0.0f;
4179 vertices[vertex].normal.z = -1.0f;
4181 /* bottom stack is triangle fan */
4182 for (slice = 1; slice < slices; slice++)
4184 faces[face][0] = vertex_index(slices, slice-1, stack-1);
4185 faces[face][1] = vertex_index(slices, slice, stack-1);
4186 faces[face][2] = vertex;
4190 faces[face][0] = vertex_index(slices, slice-1, stack-1);
4191 faces[face][1] = vertex_index(slices, 0, stack-1);
4192 faces[face][2] = vertex;
4194 free_sincos_table(&phi);
4195 sphere->lpVtbl->UnlockIndexBuffer(sphere);
4196 sphere->lpVtbl->UnlockVertexBuffer(sphere);
4202 HRESULT WINAPI D3DXCreateCylinder(LPDIRECT3DDEVICE9 device, FLOAT radius1, FLOAT radius2, FLOAT length, UINT slices,
4203 UINT stacks, LPD3DXMESH* mesh, LPD3DXBUFFER* adjacency)
4205 DWORD number_of_vertices, number_of_faces;
4207 ID3DXMesh *cylinder;
4208 struct vertex *vertices;
4210 float theta_step, theta_start;
4211 struct sincos_table theta;
4212 float delta_radius, radius, radius_step;
4213 float z, z_step, z_normal;
4217 TRACE("(%p, %f, %f, %f, %u, %u, %p, %p)\n", device, radius1, radius2, length, slices, stacks, mesh, adjacency);
4219 if (device == NULL || radius1 < 0.0f || radius2 < 0.0f || length < 0.0f || slices < 2 || stacks < 1 || mesh == NULL)
4221 return D3DERR_INVALIDCALL;
4226 FIXME("Case of adjacency != NULL not implemented.\n");
4230 number_of_vertices = 2 + (slices * (3 + stacks));
4231 number_of_faces = 2 * slices + stacks * (2 * slices);
4233 hr = D3DXCreateMeshFVF(number_of_faces, number_of_vertices, D3DXMESH_MANAGED,
4234 D3DFVF_XYZ | D3DFVF_NORMAL, device, &cylinder);
4240 hr = cylinder->lpVtbl->LockVertexBuffer(cylinder, 0, (LPVOID *)&vertices);
4243 cylinder->lpVtbl->Release(cylinder);
4247 hr = cylinder->lpVtbl->LockIndexBuffer(cylinder, 0, (LPVOID *)&faces);
4250 cylinder->lpVtbl->UnlockVertexBuffer(cylinder);
4251 cylinder->lpVtbl->Release(cylinder);
4255 /* theta = angle on xy plane wrt x axis */
4256 theta_step = -2 * M_PI / slices;
4257 theta_start = M_PI / 2;
4259 if (!compute_sincos_table(&theta, theta_start, theta_step, slices))
4261 cylinder->lpVtbl->UnlockIndexBuffer(cylinder);
4262 cylinder->lpVtbl->UnlockVertexBuffer(cylinder);
4263 cylinder->lpVtbl->Release(cylinder);
4264 return E_OUTOFMEMORY;
4270 delta_radius = radius1 - radius2;
4272 radius_step = delta_radius / stacks;
4275 z_step = length / stacks;
4276 z_normal = delta_radius / length;
4277 if (isnan(z_normal))
4282 vertices[vertex].normal.x = 0.0f;
4283 vertices[vertex].normal.y = 0.0f;
4284 vertices[vertex].normal.z = -1.0f;
4285 vertices[vertex].position.x = 0.0f;
4286 vertices[vertex].position.y = 0.0f;
4287 vertices[vertex++].position.z = z;
4289 for (slice = 0; slice < slices; slice++, vertex++)
4291 vertices[vertex].normal.x = 0.0f;
4292 vertices[vertex].normal.y = 0.0f;
4293 vertices[vertex].normal.z = -1.0f;
4294 vertices[vertex].position.x = radius * theta.cos[slice];
4295 vertices[vertex].position.y = radius * theta.sin[slice];
4296 vertices[vertex].position.z = z;
4301 faces[face][1] = slice;
4302 faces[face++][2] = slice + 1;
4307 faces[face][1] = slice;
4308 faces[face++][2] = 1;
4310 for (stack = 1; stack <= stacks+1; stack++)
4312 for (slice = 0; slice < slices; slice++, vertex++)
4314 vertices[vertex].normal.x = theta.cos[slice];
4315 vertices[vertex].normal.y = theta.sin[slice];
4316 vertices[vertex].normal.z = z_normal;
4317 D3DXVec3Normalize(&vertices[vertex].normal, &vertices[vertex].normal);
4318 vertices[vertex].position.x = radius * theta.cos[slice];
4319 vertices[vertex].position.y = radius * theta.sin[slice];
4320 vertices[vertex].position.z = z;
4322 if (stack > 1 && slice > 0)
4324 faces[face][0] = vertex_index(slices, slice-1, stack-1);
4325 faces[face][1] = vertex_index(slices, slice-1, stack);
4326 faces[face++][2] = vertex_index(slices, slice, stack-1);
4328 faces[face][0] = vertex_index(slices, slice, stack-1);
4329 faces[face][1] = vertex_index(slices, slice-1, stack);
4330 faces[face++][2] = vertex_index(slices, slice, stack);
4336 faces[face][0] = vertex_index(slices, slice-1, stack-1);
4337 faces[face][1] = vertex_index(slices, slice-1, stack);
4338 faces[face++][2] = vertex_index(slices, 0, stack-1);
4340 faces[face][0] = vertex_index(slices, 0, stack-1);
4341 faces[face][1] = vertex_index(slices, slice-1, stack);
4342 faces[face++][2] = vertex_index(slices, 0, stack);
4345 if (stack < stacks + 1)
4348 radius -= radius_step;
4352 for (slice = 0; slice < slices; slice++, vertex++)
4354 vertices[vertex].normal.x = 0.0f;
4355 vertices[vertex].normal.y = 0.0f;
4356 vertices[vertex].normal.z = 1.0f;
4357 vertices[vertex].position.x = radius * theta.cos[slice];
4358 vertices[vertex].position.y = radius * theta.sin[slice];
4359 vertices[vertex].position.z = z;
4363 faces[face][0] = vertex_index(slices, slice-1, stack);
4364 faces[face][1] = number_of_vertices - 1;
4365 faces[face++][2] = vertex_index(slices, slice, stack);
4369 vertices[vertex].position.x = 0.0f;
4370 vertices[vertex].position.y = 0.0f;
4371 vertices[vertex].position.z = z;
4372 vertices[vertex].normal.x = 0.0f;
4373 vertices[vertex].normal.y = 0.0f;
4374 vertices[vertex].normal.z = 1.0f;
4376 faces[face][0] = vertex_index(slices, slice-1, stack);
4377 faces[face][1] = number_of_vertices - 1;
4378 faces[face][2] = vertex_index(slices, 0, stack);
4380 free_sincos_table(&theta);
4381 cylinder->lpVtbl->UnlockIndexBuffer(cylinder);
4382 cylinder->lpVtbl->UnlockVertexBuffer(cylinder);
4388 HRESULT WINAPI D3DXCreateTeapot(LPDIRECT3DDEVICE9 device, LPD3DXMESH *mesh, LPD3DXBUFFER* adjacency)
4390 FIXME("(%p, %p, %p): stub\n", device, mesh, adjacency);
4395 HRESULT WINAPI D3DXCreateTextA(LPDIRECT3DDEVICE9 device,
4396 HDC hdc, LPCSTR text,
4397 FLOAT deviation, FLOAT extrusion,
4398 LPD3DXMESH *mesh, LPD3DXBUFFER *adjacency,
4399 LPGLYPHMETRICSFLOAT glyphmetrics)
4405 TRACE("(%p, %p, %s, %f, %f, %p, %p, %p)\n", device, hdc,
4406 debugstr_a(text), deviation, extrusion, mesh, adjacency, glyphmetrics);
4409 return D3DERR_INVALIDCALL;
4411 len = MultiByteToWideChar(CP_ACP, 0, text, -1, NULL, 0);
4412 textW = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
4413 MultiByteToWideChar(CP_ACP, 0, text, -1, textW, len);
4415 hr = D3DXCreateTextW(device, hdc, textW, deviation, extrusion,
4416 mesh, adjacency, glyphmetrics);
4417 HeapFree(GetProcessHeap(), 0, textW);
4423 POINTTYPE_CURVE = 0,
4425 POINTTYPE_CURVE_START,
4426 POINTTYPE_CURVE_END,
4427 POINTTYPE_CURVE_MIDDLE,
4433 enum pointtype corner;
4436 struct dynamic_array
4438 int count, capacity;
4442 /* is a dynamic_array */
4445 int count, capacity;
4446 struct point2d *items;
4449 /* is a dynamic_array */
4450 struct outline_array
4452 int count, capacity;
4453 struct outline *items;
4462 struct point2d_index
4464 struct outline *outline;
4468 struct point2d_index_array
4471 struct point2d_index *items;
4476 struct outline_array outlines;
4477 struct face_array faces;
4478 struct point2d_index_array ordered_vertices;
4482 /* is an dynamic_array */
4485 int count, capacity;
4489 /* complex polygons are split into monotone polygons, which have
4490 * at most 2 intersections with the vertical sweep line */
4491 struct triangulation
4493 struct word_array vertex_stack;
4494 BOOL last_on_top, merging;
4497 /* is an dynamic_array */
4498 struct triangulation_array
4500 int count, capacity;
4501 struct triangulation *items;
4503 struct glyphinfo *glyph;
4506 static BOOL reserve(struct dynamic_array *array, int count, int itemsize)
4508 if (count > array->capacity) {
4511 if (array->items && array->capacity) {
4512 new_capacity = max(array->capacity * 2, count);
4513 new_buffer = HeapReAlloc(GetProcessHeap(), 0, array->items, new_capacity * itemsize);
4515 new_capacity = max(16, count);
4516 new_buffer = HeapAlloc(GetProcessHeap(), 0, new_capacity * itemsize);
4520 array->items = new_buffer;
4521 array->capacity = new_capacity;
4526 static struct point2d *add_points(struct outline *array, int num)
4528 struct point2d *item;
4530 if (!reserve((struct dynamic_array *)array, array->count + num, sizeof(array->items[0])))
4533 item = &array->items[array->count];
4534 array->count += num;
4538 static struct outline *add_outline(struct outline_array *array)
4540 struct outline *item;
4542 if (!reserve((struct dynamic_array *)array, array->count + 1, sizeof(array->items[0])))
4545 item = &array->items[array->count++];
4546 ZeroMemory(item, sizeof(*item));
4550 static inline face *add_face(struct face_array *array)
4552 return &array->items[array->count++];
4555 static struct triangulation *add_triangulation(struct triangulation_array *array)
4557 struct triangulation *item;
4559 if (!reserve((struct dynamic_array *)array, array->count + 1, sizeof(array->items[0])))
4562 item = &array->items[array->count++];
4563 ZeroMemory(item, sizeof(*item));
4567 static HRESULT add_vertex_index(struct word_array *array, WORD vertex_index)
4569 if (!reserve((struct dynamic_array *)array, array->count + 1, sizeof(array->items[0])))
4570 return E_OUTOFMEMORY;
4572 array->items[array->count++] = vertex_index;
4576 /* assume fixed point numbers can be converted to float point in place */
4577 C_ASSERT(sizeof(FIXED) == sizeof(float));
4578 C_ASSERT(sizeof(POINTFX) == sizeof(D3DXVECTOR2));
4580 static inline D3DXVECTOR2 *convert_fixed_to_float(POINTFX *pt, int count, float emsquare)
4582 D3DXVECTOR2 *ret = (D3DXVECTOR2*)pt;
4584 D3DXVECTOR2 *pt_flt = (D3DXVECTOR2*)pt;
4585 pt_flt->x = (pt->x.value + pt->x.fract / (float)0x10000) / emsquare;
4586 pt_flt->y = (pt->y.value + pt->y.fract / (float)0x10000) / emsquare;
4592 static HRESULT add_bezier_points(struct outline *outline, const D3DXVECTOR2 *p1,
4593 const D3DXVECTOR2 *p2, const D3DXVECTOR2 *p3,
4594 float max_deviation_sq)
4596 D3DXVECTOR2 split1 = {0, 0}, split2 = {0, 0}, middle, vec;
4599 D3DXVec2Scale(&split1, D3DXVec2Add(&split1, p1, p2), 0.5f);
4600 D3DXVec2Scale(&split2, D3DXVec2Add(&split2, p2, p3), 0.5f);
4601 D3DXVec2Scale(&middle, D3DXVec2Add(&middle, &split1, &split2), 0.5f);
4603 deviation_sq = D3DXVec2LengthSq(D3DXVec2Subtract(&vec, &middle, p2));
4604 if (deviation_sq < max_deviation_sq) {
4605 struct point2d *pt = add_points(outline, 1);
4606 if (!pt) return E_OUTOFMEMORY;
4608 pt->corner = POINTTYPE_CURVE;
4609 /* the end point is omitted because the end line merges into the next segment of
4610 * the split bezier curve, and the end of the split bezier curve is added outside
4611 * this recursive function. */
4613 HRESULT hr = add_bezier_points(outline, p1, &split1, &middle, max_deviation_sq);
4614 if (hr != S_OK) return hr;
4615 hr = add_bezier_points(outline, &middle, &split2, p3, max_deviation_sq);
4616 if (hr != S_OK) return hr;
4622 static inline BOOL is_direction_similar(D3DXVECTOR2 *dir1, D3DXVECTOR2 *dir2, float cos_theta)
4624 /* dot product = cos(theta) */
4625 return D3DXVec2Dot(dir1, dir2) > cos_theta;
4628 static inline D3DXVECTOR2 *unit_vec2(D3DXVECTOR2 *dir, const D3DXVECTOR2 *pt1, const D3DXVECTOR2 *pt2)
4630 return D3DXVec2Normalize(D3DXVec2Subtract(dir, pt2, pt1), dir);
4640 static BOOL attempt_line_merge(struct outline *outline,
4642 const D3DXVECTOR2 *nextpt,
4644 const struct cos_table *table)
4646 D3DXVECTOR2 curdir, lastdir;
4647 struct point2d *prevpt, *pt;
4650 pt = &outline->items[pt_index];
4651 pt_index = (pt_index - 1 + outline->count) % outline->count;
4652 prevpt = &outline->items[pt_index];
4655 pt->corner = pt->corner != POINTTYPE_CORNER ? POINTTYPE_CURVE_MIDDLE : POINTTYPE_CURVE_START;
4657 if (outline->count < 2)
4660 /* remove last point if the next line continues the last line */
4661 unit_vec2(&lastdir, &prevpt->pos, &pt->pos);
4662 unit_vec2(&curdir, &pt->pos, nextpt);
4663 if (is_direction_similar(&lastdir, &curdir, table->cos_half))
4666 if (pt->corner == POINTTYPE_CURVE_END)
4667 prevpt->corner = pt->corner;
4668 if (prevpt->corner == POINTTYPE_CURVE_END && to_curve)
4669 prevpt->corner = POINTTYPE_CURVE_MIDDLE;
4673 if (outline->count < 2)
4676 pt_index = (pt_index - 1 + outline->count) % outline->count;
4677 prevpt = &outline->items[pt_index];
4678 unit_vec2(&lastdir, &prevpt->pos, &pt->pos);
4679 unit_vec2(&curdir, &pt->pos, nextpt);
4684 static HRESULT create_outline(struct glyphinfo *glyph, void *raw_outline, int datasize,
4685 float max_deviation_sq, float emsquare, const struct cos_table *cos_table)
4687 TTPOLYGONHEADER *header = (TTPOLYGONHEADER *)raw_outline;
4689 while ((char *)header < (char *)raw_outline + datasize)
4691 TTPOLYCURVE *curve = (TTPOLYCURVE *)(header + 1);
4692 struct point2d *lastpt, *pt;
4693 D3DXVECTOR2 lastdir;
4694 D3DXVECTOR2 *pt_flt;
4696 struct outline *outline = add_outline(&glyph->outlines);
4699 return E_OUTOFMEMORY;
4701 pt = add_points(outline, 1);
4703 return E_OUTOFMEMORY;
4704 pt_flt = convert_fixed_to_float(&header->pfxStart, 1, emsquare);
4706 pt->corner = POINTTYPE_CORNER;
4708 if (header->dwType != TT_POLYGON_TYPE)
4709 FIXME("Unknown header type %d\n", header->dwType);
4711 while ((char *)curve < (char *)header + header->cb)
4713 D3DXVECTOR2 bezier_start = outline->items[outline->count - 1].pos;
4714 BOOL to_curve = curve->wType != TT_PRIM_LINE && curve->cpfx > 1;
4717 curve = (TTPOLYCURVE *)&curve->apfx[curve->cpfx];
4721 pt_flt = convert_fixed_to_float(curve->apfx, curve->cpfx, emsquare);
4723 attempt_line_merge(outline, outline->count - 1, &pt_flt[0], to_curve, cos_table);
4728 int count = curve->cpfx;
4733 D3DXVECTOR2 bezier_end;
4735 D3DXVec2Scale(&bezier_end, D3DXVec2Add(&bezier_end, &pt_flt[j], &pt_flt[j+1]), 0.5f);
4736 hr = add_bezier_points(outline, &bezier_start, &pt_flt[j], &bezier_end, max_deviation_sq);
4739 bezier_start = bezier_end;
4743 hr = add_bezier_points(outline, &bezier_start, &pt_flt[j], &pt_flt[j+1], max_deviation_sq);
4747 pt = add_points(outline, 1);
4749 return E_OUTOFMEMORY;
4751 pt->pos = pt_flt[j];
4752 pt->corner = POINTTYPE_CURVE_END;
4754 pt = add_points(outline, curve->cpfx);
4756 return E_OUTOFMEMORY;
4757 for (j = 0; j < curve->cpfx; j++)
4759 pt->pos = pt_flt[j];
4760 pt->corner = POINTTYPE_CORNER;
4765 curve = (TTPOLYCURVE *)&curve->apfx[curve->cpfx];
4768 /* remove last point if the next line continues the last line */
4769 if (outline->count >= 3) {
4772 lastpt = &outline->items[outline->count - 1];
4773 pt = &outline->items[0];
4774 if (pt->pos.x == lastpt->pos.x && pt->pos.y == lastpt->pos.y) {
4775 if (lastpt->corner == POINTTYPE_CURVE_END)
4777 if (pt->corner == POINTTYPE_CURVE_START)
4778 pt->corner = POINTTYPE_CURVE_MIDDLE;
4780 pt->corner = POINTTYPE_CURVE_END;
4783 lastpt = &outline->items[outline->count - 1];
4785 /* outline closed with a line from end to start point */
4786 attempt_line_merge(outline, outline->count - 1, &pt->pos, FALSE, cos_table);
4788 lastpt = &outline->items[0];
4789 to_curve = lastpt->corner != POINTTYPE_CORNER && lastpt->corner != POINTTYPE_CURVE_END;
4790 if (lastpt->corner == POINTTYPE_CURVE_START)
4791 lastpt->corner = POINTTYPE_CORNER;
4792 pt = &outline->items[1];
4793 if (attempt_line_merge(outline, 0, &pt->pos, to_curve, cos_table))
4794 *lastpt = outline->items[outline->count];
4797 lastpt = &outline->items[outline->count - 1];
4798 pt = &outline->items[0];
4799 unit_vec2(&lastdir, &lastpt->pos, &pt->pos);
4800 for (j = 0; j < outline->count; j++)
4805 pt = &outline->items[(j + 1) % outline->count];
4806 unit_vec2(&curdir, &lastpt->pos, &pt->pos);
4808 switch (lastpt->corner)
4810 case POINTTYPE_CURVE_START:
4811 case POINTTYPE_CURVE_END:
4812 if (!is_direction_similar(&lastdir, &curdir, cos_table->cos_45))
4813 lastpt->corner = POINTTYPE_CORNER;
4815 case POINTTYPE_CURVE_MIDDLE:
4816 if (!is_direction_similar(&lastdir, &curdir, cos_table->cos_90))
4817 lastpt->corner = POINTTYPE_CORNER;
4819 lastpt->corner = POINTTYPE_CURVE;
4827 header = (TTPOLYGONHEADER *)((char *)header + header->cb);
4832 /* Get the y-distance from a line to a point */
4833 static float get_line_to_point_y_distance(D3DXVECTOR2 *line_pt1,
4834 D3DXVECTOR2 *line_pt2,
4837 D3DXVECTOR2 line_vec = {0, 0};
4841 D3DXVec2Subtract(&line_vec, line_pt2, line_pt1);
4842 line_pt_dx = point->x - line_pt1->x;
4843 line_y = line_pt1->y + (line_vec.y * line_pt_dx) / line_vec.x;
4844 return point->y - line_y;
4847 static D3DXVECTOR2 *get_indexed_point(struct point2d_index *pt_idx)
4849 return &pt_idx->outline->items[pt_idx->vertex].pos;
4852 static D3DXVECTOR2 *get_ordered_vertex(struct glyphinfo *glyph, WORD index)
4854 return get_indexed_point(&glyph->ordered_vertices.items[index]);
4857 static void remove_triangulation(struct triangulation_array *array, struct triangulation *item)
4859 HeapFree(GetProcessHeap(), 0, item->vertex_stack.items);
4860 MoveMemory(item, item + 1, (char*)&array->items[array->count] - (char*)(item + 1));
4864 static HRESULT triangulation_add_point(struct triangulation **t_ptr,
4865 struct triangulation_array *triangulations,
4869 struct glyphinfo *glyph = triangulations->glyph;
4870 struct triangulation *t = *t_ptr;
4875 if (t->last_on_top) {
4883 if (t->last_on_top != to_top && t->vertex_stack.count > 1) {
4884 /* consume all vertices on the stack */
4885 WORD last_pt = t->vertex_stack.items[0];
4887 for (i = 1; i < t->vertex_stack.count; i++)
4889 face = add_face(&glyph->faces);
4890 if (!face) return E_OUTOFMEMORY;
4891 (*face)[0] = vtx_idx;
4892 (*face)[f1] = last_pt;
4893 (*face)[f2] = last_pt = t->vertex_stack.items[i];
4895 t->vertex_stack.items[0] = last_pt;
4896 t->vertex_stack.count = 1;
4897 } else if (t->vertex_stack.count > 1) {
4898 int i = t->vertex_stack.count - 1;
4899 D3DXVECTOR2 *point = get_ordered_vertex(glyph, vtx_idx);
4900 WORD top_idx = t->vertex_stack.items[i--];
4901 D3DXVECTOR2 *top_pt = get_ordered_vertex(glyph, top_idx);
4905 WORD prev_idx = t->vertex_stack.items[i--];
4906 D3DXVECTOR2 *prev_pt = get_ordered_vertex(glyph, prev_idx);
4908 if (prev_pt->x != top_pt->x &&
4909 ((to_top && get_line_to_point_y_distance(prev_pt, top_pt, point) > 0) ||
4910 (!to_top && get_line_to_point_y_distance(prev_pt, top_pt, point) < 0)))
4913 face = add_face(&glyph->faces);
4914 if (!face) return E_OUTOFMEMORY;
4915 (*face)[0] = vtx_idx;
4916 (*face)[f1] = prev_idx;
4917 (*face)[f2] = top_idx;
4921 t->vertex_stack.count--;
4924 t->last_on_top = to_top;
4926 hr = add_vertex_index(&t->vertex_stack, vtx_idx);
4928 if (hr == S_OK && t->merging) {
4929 struct triangulation *t2;
4931 t2 = to_top ? t - 1 : t + 1;
4932 t2->merging = FALSE;
4933 hr = triangulation_add_point(&t2, triangulations, vtx_idx, to_top);
4934 if (hr != S_OK) return hr;
4935 remove_triangulation(triangulations, t);
4943 /* check if the point is next on the outline for either the top or bottom */
4944 static D3DXVECTOR2 *triangulation_get_next_point(struct triangulation *t, struct glyphinfo *glyph, BOOL on_top)
4946 int i = t->last_on_top == on_top ? t->vertex_stack.count - 1 : 0;
4947 WORD idx = t->vertex_stack.items[i];
4948 struct point2d_index *pt_idx = &glyph->ordered_vertices.items[idx];
4949 struct outline *outline = pt_idx->outline;
4952 i = (pt_idx->vertex + outline->count - 1) % outline->count;
4954 i = (pt_idx->vertex + 1) % outline->count;
4956 return &outline->items[i].pos;
4959 static int compare_vertex_indices(const void *a, const void *b)
4961 const struct point2d_index *idx1 = a, *idx2 = b;
4962 const D3DXVECTOR2 *p1 = &idx1->outline->items[idx1->vertex].pos;
4963 const D3DXVECTOR2 *p2 = &idx2->outline->items[idx2->vertex].pos;
4964 float diff = p1->x - p2->x;
4967 diff = p1->y - p2->y;
4969 return diff == 0.0f ? 0 : (diff > 0.0f ? -1 : 1);
4972 static HRESULT triangulate(struct triangulation_array *triangulations)
4976 struct glyphinfo *glyph = triangulations->glyph;
4977 int nb_vertices = 0;
4979 struct point2d_index *idx_ptr;
4981 for (i = 0; i < glyph->outlines.count; i++)
4982 nb_vertices += glyph->outlines.items[i].count;
4984 glyph->ordered_vertices.items = HeapAlloc(GetProcessHeap(), 0,
4985 nb_vertices * sizeof(*glyph->ordered_vertices.items));
4986 if (!glyph->ordered_vertices.items)
4987 return E_OUTOFMEMORY;
4989 idx_ptr = glyph->ordered_vertices.items;
4990 for (i = 0; i < glyph->outlines.count; i++)
4992 struct outline *outline = &glyph->outlines.items[i];
4995 idx_ptr->outline = outline;
4996 idx_ptr->vertex = 0;
4998 for (j = outline->count - 1; j > 0; j--)
5000 idx_ptr->outline = outline;
5001 idx_ptr->vertex = j;
5005 glyph->ordered_vertices.count = nb_vertices;
5007 /* Native implementation seems to try to create a triangle fan from
5008 * the first outline point if the glyph only has one outline. */
5009 if (glyph->outlines.count == 1)
5011 struct outline *outline = glyph->outlines.items;
5012 D3DXVECTOR2 *base = &outline->items[0].pos;
5013 D3DXVECTOR2 *last = &outline->items[1].pos;
5016 for (i = 2; i < outline->count; i++)
5018 D3DXVECTOR2 *next = &outline->items[i].pos;
5019 D3DXVECTOR2 v1 = {0.0f, 0.0f};
5020 D3DXVECTOR2 v2 = {0.0f, 0.0f};
5022 D3DXVec2Subtract(&v1, base, last);
5023 D3DXVec2Subtract(&v2, last, next);
5024 ccw = D3DXVec2CCW(&v1, &v2);
5032 glyph->faces.items = HeapAlloc(GetProcessHeap(), 0,
5033 (outline->count - 2) * sizeof(glyph->faces.items[0]));
5034 if (!glyph->faces.items)
5035 return E_OUTOFMEMORY;
5037 glyph->faces.count = outline->count - 2;
5038 for (i = 0; i < glyph->faces.count; i++)
5040 glyph->faces.items[i][0] = 0;
5041 glyph->faces.items[i][1] = i + 1;
5042 glyph->faces.items[i][2] = i + 2;
5048 /* Perform 2D polygon triangulation for complex glyphs.
5049 * Triangulation is performed using a sweep line concept, from right to left,
5050 * by processing vertices in sorted order. Complex polygons are split into
5051 * monotone polygons which are triangulated separately. */
5052 /* FIXME: The order of the faces is not consistent with the native implementation. */
5054 /* Reserve space for maximum possible faces from triangulation.
5055 * # faces for outer outlines = outline->count - 2
5056 * # faces for inner outlines = outline->count + 2
5057 * There must be at least 1 outer outline. */
5058 glyph->faces.items = HeapAlloc(GetProcessHeap(), 0,
5059 (nb_vertices + glyph->outlines.count * 2 - 4) * sizeof(glyph->faces.items[0]));
5060 if (!glyph->faces.items)
5061 return E_OUTOFMEMORY;
5063 qsort(glyph->ordered_vertices.items, nb_vertices,
5064 sizeof(glyph->ordered_vertices.items[0]), compare_vertex_indices);
5065 for (sweep_idx = 0; sweep_idx < glyph->ordered_vertices.count; sweep_idx++)
5068 int end = triangulations->count;
5072 D3DXVECTOR2 *sweep_vtx = get_ordered_vertex(glyph, sweep_idx);
5073 int current = (start + end) / 2;
5074 struct triangulation *t = &triangulations->items[current];
5075 BOOL on_top_outline = FALSE;
5076 D3DXVECTOR2 *top_next, *bottom_next;
5077 WORD top_idx, bottom_idx;
5079 if (t->merging && t->last_on_top)
5080 top_next = triangulation_get_next_point(t + 1, glyph, TRUE);
5082 top_next = triangulation_get_next_point(t, glyph, TRUE);
5083 if (sweep_vtx == top_next)
5085 if (t->merging && t->last_on_top)
5087 hr = triangulation_add_point(&t, triangulations, sweep_idx, TRUE);
5088 if (hr != S_OK) return hr;
5090 if (t + 1 < &triangulations->items[triangulations->count] &&
5091 triangulation_get_next_point(t + 1, glyph, FALSE) == sweep_vtx)
5093 /* point also on bottom outline of higher triangulation */
5094 struct triangulation *t2 = t + 1;
5095 hr = triangulation_add_point(&t2, triangulations, sweep_idx, FALSE);
5096 if (hr != S_OK) return hr;
5101 on_top_outline = TRUE;
5104 if (t->merging && !t->last_on_top)
5105 bottom_next = triangulation_get_next_point(t - 1, glyph, FALSE);
5107 bottom_next = triangulation_get_next_point(t, glyph, FALSE);
5108 if (sweep_vtx == bottom_next)
5110 if (t->merging && !t->last_on_top)
5112 if (on_top_outline) {
5113 /* outline finished */
5114 remove_triangulation(triangulations, t);
5118 hr = triangulation_add_point(&t, triangulations, sweep_idx, FALSE);
5119 if (hr != S_OK) return hr;
5121 if (t > triangulations->items &&
5122 triangulation_get_next_point(t - 1, glyph, TRUE) == sweep_vtx)
5124 struct triangulation *t2 = t - 1;
5125 /* point also on top outline of lower triangulation */
5126 hr = triangulation_add_point(&t2, triangulations, sweep_idx, TRUE);
5127 if (hr != S_OK) return hr;
5128 t = t2 + 1; /* t may be invalidated by triangulation merging */
5138 if (t->last_on_top) {
5139 top_idx = t->vertex_stack.items[t->vertex_stack.count - 1];
5140 bottom_idx = t->vertex_stack.items[0];
5142 top_idx = t->vertex_stack.items[0];
5143 bottom_idx = t->vertex_stack.items[t->vertex_stack.count - 1];
5146 /* check if the point is inside or outside this polygon */
5147 if (get_line_to_point_y_distance(get_ordered_vertex(glyph, top_idx),
5148 top_next, sweep_vtx) > 0)
5150 start = current + 1;
5151 } else if (get_line_to_point_y_distance(get_ordered_vertex(glyph, bottom_idx),
5152 bottom_next, sweep_vtx) < 0)
5155 } else if (t->merging) {
5156 /* inside, so cancel merging */
5157 struct triangulation *t2 = t->last_on_top ? t + 1 : t - 1;
5159 t2->merging = FALSE;
5160 hr = triangulation_add_point(&t, triangulations, sweep_idx, t->last_on_top);
5161 if (hr != S_OK) return hr;
5162 hr = triangulation_add_point(&t2, triangulations, sweep_idx, t2->last_on_top);
5163 if (hr != S_OK) return hr;
5166 /* inside, so split polygon into two monotone parts */
5167 struct triangulation *t2 = add_triangulation(triangulations);
5168 if (!t2) return E_OUTOFMEMORY;
5169 MoveMemory(t + 1, t, (char*)(t2 + 1) - (char*)t);
5170 if (t->last_on_top) {
5177 ZeroMemory(&t2->vertex_stack, sizeof(t2->vertex_stack));
5178 hr = add_vertex_index(&t2->vertex_stack, t->vertex_stack.items[t->vertex_stack.count - 1]);
5179 if (hr != S_OK) return hr;
5180 hr = add_vertex_index(&t2->vertex_stack, sweep_idx);
5181 if (hr != S_OK) return hr;
5182 t2->last_on_top = !t->last_on_top;
5184 hr = triangulation_add_point(&t, triangulations, sweep_idx, t->last_on_top);
5185 if (hr != S_OK) return hr;
5191 struct triangulation *t;
5192 struct triangulation *t2 = add_triangulation(triangulations);
5193 if (!t2) return E_OUTOFMEMORY;
5194 t = &triangulations->items[start];
5195 MoveMemory(t + 1, t, (char*)(t2 + 1) - (char*)t);
5196 ZeroMemory(t, sizeof(*t));
5197 hr = add_vertex_index(&t->vertex_stack, sweep_idx);
5198 if (hr != S_OK) return hr;
5204 HRESULT WINAPI D3DXCreateTextW(LPDIRECT3DDEVICE9 device,
5205 HDC hdc, LPCWSTR text,
5206 FLOAT deviation, FLOAT extrusion,
5207 LPD3DXMESH *mesh_ptr, LPD3DXBUFFER *adjacency,
5208 LPGLYPHMETRICSFLOAT glyphmetrics)
5211 ID3DXMesh *mesh = NULL;
5212 DWORD nb_vertices, nb_faces;
5213 DWORD nb_front_faces, nb_corners, nb_outline_points;
5214 struct vertex *vertices = NULL;
5219 OUTLINETEXTMETRICW otm;
5220 HFONT font = NULL, oldfont = NULL;
5221 const MAT2 identity = {{0, 1}, {0, 0}, {0, 0}, {0, 1}};
5222 void *raw_outline = NULL;
5224 struct glyphinfo *glyphs = NULL;
5226 struct triangulation_array triangulations = {0, 0, NULL};
5228 struct vertex *vertex_ptr;
5230 float max_deviation_sq;
5231 const struct cos_table cos_table = {
5232 cos(D3DXToRadian(0.5f)),
5233 cos(D3DXToRadian(45.0f)),
5234 cos(D3DXToRadian(90.0f)),
5238 TRACE("(%p, %p, %s, %f, %f, %p, %p, %p)\n", device, hdc,
5239 debugstr_w(text), deviation, extrusion, mesh_ptr, adjacency, glyphmetrics);
5241 if (!device || !hdc || !text || !*text || deviation < 0.0f || extrusion < 0.0f || !mesh_ptr)
5242 return D3DERR_INVALIDCALL;
5246 FIXME("Case of adjacency != NULL not implemented.\n");
5250 if (!GetObjectW(GetCurrentObject(hdc, OBJ_FONT), sizeof(lf), &lf) ||
5251 !GetOutlineTextMetricsW(hdc, sizeof(otm), &otm))
5253 return D3DERR_INVALIDCALL;
5256 if (deviation == 0.0f)
5257 deviation = 1.0f / otm.otmEMSquare;
5258 max_deviation_sq = deviation * deviation;
5260 lf.lfHeight = otm.otmEMSquare;
5262 font = CreateFontIndirectW(&lf);
5267 oldfont = SelectObject(hdc, font);
5269 textlen = strlenW(text);
5270 for (i = 0; i < textlen; i++)
5272 int datasize = GetGlyphOutlineW(hdc, text[i], GGO_NATIVE, &gm, 0, NULL, &identity);
5274 return D3DERR_INVALIDCALL;
5275 if (bufsize < datasize)
5278 if (!bufsize) { /* e.g. text == " " */
5279 hr = D3DERR_INVALIDCALL;
5283 glyphs = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, textlen * sizeof(*glyphs));
5284 raw_outline = HeapAlloc(GetProcessHeap(), 0, bufsize);
5285 if (!glyphs || !raw_outline) {
5291 for (i = 0; i < textlen; i++)
5293 /* get outline points from data returned from GetGlyphOutline */
5296 glyphs[i].offset_x = offset_x;
5298 datasize = GetGlyphOutlineW(hdc, text[i], GGO_NATIVE, &gm, bufsize, raw_outline, &identity);
5299 hr = create_outline(&glyphs[i], raw_outline, datasize,
5300 max_deviation_sq, otm.otmEMSquare, &cos_table);
5301 if (hr != S_OK) goto error;
5303 triangulations.glyph = &glyphs[i];
5304 hr = triangulate(&triangulations);
5305 if (hr != S_OK) goto error;
5306 if (triangulations.count) {
5307 ERR("%d incomplete triangulations of glyph (%u).\n", triangulations.count, text[i]);
5308 triangulations.count = 0;
5313 glyphmetrics[i].gmfBlackBoxX = gm.gmBlackBoxX / (float)otm.otmEMSquare;
5314 glyphmetrics[i].gmfBlackBoxY = gm.gmBlackBoxY / (float)otm.otmEMSquare;
5315 glyphmetrics[i].gmfptGlyphOrigin.x = gm.gmptGlyphOrigin.x / (float)otm.otmEMSquare;
5316 glyphmetrics[i].gmfptGlyphOrigin.y = gm.gmptGlyphOrigin.y / (float)otm.otmEMSquare;
5317 glyphmetrics[i].gmfCellIncX = gm.gmCellIncX / (float)otm.otmEMSquare;
5318 glyphmetrics[i].gmfCellIncY = gm.gmCellIncY / (float)otm.otmEMSquare;
5320 offset_x += gm.gmCellIncX / (float)otm.otmEMSquare;
5323 /* corner points need an extra vertex for the different side faces normals */
5325 nb_outline_points = 0;
5327 for (i = 0; i < textlen; i++)
5330 nb_outline_points += glyphs[i].ordered_vertices.count;
5331 nb_front_faces += glyphs[i].faces.count;
5332 for (j = 0; j < glyphs[i].outlines.count; j++)
5335 struct outline *outline = &glyphs[i].outlines.items[j];
5336 nb_corners++; /* first outline point always repeated as a corner */
5337 for (k = 1; k < outline->count; k++)
5338 if (outline->items[k].corner)
5343 nb_vertices = (nb_outline_points + nb_corners) * 2 + nb_outline_points * 2;
5344 nb_faces = nb_outline_points * 2 + nb_front_faces * 2;
5347 hr = D3DXCreateMeshFVF(nb_faces, nb_vertices, D3DXMESH_MANAGED,
5348 D3DFVF_XYZ | D3DFVF_NORMAL, device, &mesh);
5352 hr = mesh->lpVtbl->LockVertexBuffer(mesh, 0, (LPVOID *)&vertices);
5356 hr = mesh->lpVtbl->LockIndexBuffer(mesh, 0, (LPVOID *)&faces);
5360 /* convert 2D vertices and faces into 3D mesh */
5361 vertex_ptr = vertices;
5363 if (extrusion == 0.0f) {
5370 for (i = 0; i < textlen; i++)
5374 struct vertex *back_vertices;
5377 /* side vertices and faces */
5378 for (j = 0; j < glyphs[i].outlines.count; j++)
5380 struct vertex *outline_vertices = vertex_ptr;
5381 struct outline *outline = &glyphs[i].outlines.items[j];
5383 struct point2d *prevpt = &outline->items[outline->count - 1];
5384 struct point2d *pt = &outline->items[0];
5386 for (k = 1; k <= outline->count; k++)
5389 struct point2d *nextpt = &outline->items[k % outline->count];
5390 WORD vtx_idx = vertex_ptr - vertices;
5393 if (pt->corner == POINTTYPE_CURVE_START)
5394 D3DXVec2Subtract(&vec, &pt->pos, &prevpt->pos);
5395 else if (pt->corner)
5396 D3DXVec2Subtract(&vec, &nextpt->pos, &pt->pos);
5398 D3DXVec2Subtract(&vec, &nextpt->pos, &prevpt->pos);
5399 D3DXVec2Normalize(&vec, &vec);
5400 vtx.normal.x = -vec.y;
5401 vtx.normal.y = vec.x;
5404 vtx.position.x = pt->pos.x + glyphs[i].offset_x;
5405 vtx.position.y = pt->pos.y;
5407 *vertex_ptr++ = vtx;
5409 vtx.position.z = -extrusion;
5410 *vertex_ptr++ = vtx;
5412 vtx.position.x = nextpt->pos.x + glyphs[i].offset_x;
5413 vtx.position.y = nextpt->pos.y;
5414 if (pt->corner && nextpt->corner && nextpt->corner != POINTTYPE_CURVE_END) {
5415 vtx.position.z = -extrusion;
5416 *vertex_ptr++ = vtx;
5418 *vertex_ptr++ = vtx;
5420 (*face_ptr)[0] = vtx_idx;
5421 (*face_ptr)[1] = vtx_idx + 2;
5422 (*face_ptr)[2] = vtx_idx + 1;
5425 (*face_ptr)[0] = vtx_idx;
5426 (*face_ptr)[1] = vtx_idx + 3;
5427 (*face_ptr)[2] = vtx_idx + 2;
5430 if (nextpt->corner) {
5431 if (nextpt->corner == POINTTYPE_CURVE_END) {
5432 D3DXVECTOR2 *nextpt2 = &outline->items[(k + 1) % outline->count].pos;
5433 D3DXVec2Subtract(&vec, nextpt2, &nextpt->pos);
5435 D3DXVec2Subtract(&vec, &nextpt->pos, &pt->pos);
5437 D3DXVec2Normalize(&vec, &vec);
5438 vtx.normal.x = -vec.y;
5439 vtx.normal.y = vec.x;
5442 *vertex_ptr++ = vtx;
5443 vtx.position.z = -extrusion;
5444 *vertex_ptr++ = vtx;
5447 (*face_ptr)[0] = vtx_idx;
5448 (*face_ptr)[1] = vtx_idx + 3;
5449 (*face_ptr)[2] = vtx_idx + 1;
5452 (*face_ptr)[0] = vtx_idx;
5453 (*face_ptr)[1] = vtx_idx + 2;
5454 (*face_ptr)[2] = vtx_idx + 3;
5462 *vertex_ptr++ = *outline_vertices++;
5463 *vertex_ptr++ = *outline_vertices++;
5467 /* back vertices and faces */
5468 back_faces = face_ptr;
5469 back_vertices = vertex_ptr;
5470 for (j = 0; j < glyphs[i].ordered_vertices.count; j++)
5472 D3DXVECTOR2 *pt = get_ordered_vertex(&glyphs[i], j);
5473 vertex_ptr->position.x = pt->x + glyphs[i].offset_x;
5474 vertex_ptr->position.y = pt->y;
5475 vertex_ptr->position.z = 0;
5476 vertex_ptr->normal.x = 0;
5477 vertex_ptr->normal.y = 0;
5478 vertex_ptr->normal.z = 1;
5481 count = back_vertices - vertices;
5482 for (j = 0; j < glyphs[i].faces.count; j++)
5484 face *f = &glyphs[i].faces.items[j];
5485 (*face_ptr)[0] = (*f)[0] + count;
5486 (*face_ptr)[1] = (*f)[1] + count;
5487 (*face_ptr)[2] = (*f)[2] + count;
5491 /* front vertices and faces */
5492 j = count = vertex_ptr - back_vertices;
5495 vertex_ptr->position.x = back_vertices->position.x;
5496 vertex_ptr->position.y = back_vertices->position.y;
5497 vertex_ptr->position.z = -extrusion;
5498 vertex_ptr->normal.x = 0;
5499 vertex_ptr->normal.y = 0;
5500 vertex_ptr->normal.z = extrusion == 0.0f ? 1.0f : -1.0f;
5504 j = face_ptr - back_faces;
5507 (*face_ptr)[0] = (*back_faces)[0] + count;
5508 (*face_ptr)[1] = (*back_faces)[f1] + count;
5509 (*face_ptr)[2] = (*back_faces)[f2] + count;
5519 if (faces) mesh->lpVtbl->UnlockIndexBuffer(mesh);
5520 if (vertices) mesh->lpVtbl->UnlockVertexBuffer(mesh);
5521 if (hr != D3D_OK) mesh->lpVtbl->Release(mesh);
5524 for (i = 0; i < textlen; i++)
5527 for (j = 0; j < glyphs[i].outlines.count; j++)
5528 HeapFree(GetProcessHeap(), 0, glyphs[i].outlines.items[j].items);
5529 HeapFree(GetProcessHeap(), 0, glyphs[i].outlines.items);
5530 HeapFree(GetProcessHeap(), 0, glyphs[i].faces.items);
5531 HeapFree(GetProcessHeap(), 0, glyphs[i].ordered_vertices.items);
5533 HeapFree(GetProcessHeap(), 0, glyphs);
5535 if (triangulations.items) {
5537 for (i = 0; i < triangulations.count; i++)
5538 HeapFree(GetProcessHeap(), 0, triangulations.items[i].vertex_stack.items);
5539 HeapFree(GetProcessHeap(), 0, triangulations.items);
5541 HeapFree(GetProcessHeap(), 0, raw_outline);
5542 if (oldfont) SelectObject(hdc, oldfont);
5543 if (font) DeleteObject(font);
5548 static BOOL weld_float1(void *to, void *from, FLOAT epsilon)
5553 if (fabsf(*v1 - *v2) <= epsilon)
5563 static BOOL weld_float2(void *to, void *from, FLOAT epsilon)
5565 D3DXVECTOR2 *v1 = to;
5566 D3DXVECTOR2 *v2 = from;
5567 FLOAT diff_x = fabsf(v1->x - v2->x);
5568 FLOAT diff_y = fabsf(v1->y - v2->y);
5569 FLOAT max_abs_diff = max(diff_x, diff_y);
5571 if (max_abs_diff <= epsilon)
5573 memcpy(to, from, sizeof(D3DXVECTOR2));
5581 static BOOL weld_float3(void *to, void *from, FLOAT epsilon)
5583 D3DXVECTOR3 *v1 = to;
5584 D3DXVECTOR3 *v2 = from;
5585 FLOAT diff_x = fabsf(v1->x - v2->x);
5586 FLOAT diff_y = fabsf(v1->y - v2->y);
5587 FLOAT diff_z = fabsf(v1->z - v2->z);
5588 FLOAT max_abs_diff = max(diff_x, diff_y);
5589 max_abs_diff = max(diff_z, max_abs_diff);
5591 if (max_abs_diff <= epsilon)
5593 memcpy(to, from, sizeof(D3DXVECTOR3));
5601 static BOOL weld_float4(void *to, void *from, FLOAT epsilon)
5603 D3DXVECTOR4 *v1 = to;
5604 D3DXVECTOR4 *v2 = from;
5605 FLOAT diff_x = fabsf(v1->x - v2->x);
5606 FLOAT diff_y = fabsf(v1->y - v2->y);
5607 FLOAT diff_z = fabsf(v1->z - v2->z);
5608 FLOAT diff_w = fabsf(v1->w - v2->w);
5609 FLOAT max_abs_diff = fmax(diff_x, diff_y);
5610 max_abs_diff = max(diff_z, max_abs_diff);
5611 max_abs_diff = max(diff_w, max_abs_diff);
5613 if (max_abs_diff <= epsilon)
5615 memcpy(to, from, sizeof(D3DXVECTOR4));
5623 static BOOL weld_ubyte4(void *to, void *from, FLOAT epsilon)
5627 BYTE truncated_epsilon = (BYTE)epsilon;
5628 BYTE diff_x = b1[0] > b2[0] ? b1[0] - b2[0] : b2[0] - b1[0];
5629 BYTE diff_y = b1[1] > b2[1] ? b1[1] - b2[1] : b2[1] - b1[1];
5630 BYTE diff_z = b1[2] > b2[2] ? b1[2] - b2[2] : b2[2] - b1[2];
5631 BYTE diff_w = b1[3] > b2[3] ? b1[3] - b2[3] : b2[3] - b1[3];
5632 BYTE max_diff = max(diff_x, diff_y);
5633 max_diff = max(diff_z, max_diff);
5634 max_diff = max(diff_w, max_diff);
5636 if (max_diff <= truncated_epsilon)
5638 memcpy(to, from, 4 * sizeof(BYTE));
5646 static BOOL weld_ubyte4n(void *to, void *from, FLOAT epsilon)
5648 return weld_ubyte4(to, from, epsilon * UCHAR_MAX);
5651 static BOOL weld_d3dcolor(void *to, void *from, FLOAT epsilon)
5653 return weld_ubyte4n(to, from, epsilon);
5656 static BOOL weld_short2(void *to, void *from, FLOAT epsilon)
5660 SHORT truncated_epsilon = (SHORT)epsilon;
5661 SHORT diff_x = abs(s1[0] - s2[0]);
5662 SHORT diff_y = abs(s1[1] - s2[1]);
5663 SHORT max_abs_diff = max(diff_x, diff_y);
5665 if (max_abs_diff <= truncated_epsilon)
5667 memcpy(to, from, 2 * sizeof(SHORT));
5675 static BOOL weld_short2n(void *to, void *from, FLOAT epsilon)
5677 return weld_short2(to, from, epsilon * SHRT_MAX);
5680 static BOOL weld_short4(void *to, void *from, FLOAT epsilon)
5684 SHORT truncated_epsilon = (SHORT)epsilon;
5685 SHORT diff_x = abs(s1[0] - s2[0]);
5686 SHORT diff_y = abs(s1[1] - s2[1]);
5687 SHORT diff_z = abs(s1[2] - s2[2]);
5688 SHORT diff_w = abs(s1[3] - s2[3]);
5689 SHORT max_abs_diff = max(diff_x, diff_y);
5690 max_abs_diff = max(diff_z, max_abs_diff);
5691 max_abs_diff = max(diff_w, max_abs_diff);
5693 if (max_abs_diff <= truncated_epsilon)
5695 memcpy(to, from, 4 * sizeof(SHORT));
5703 static BOOL weld_short4n(void *to, void *from, FLOAT epsilon)
5705 return weld_short4(to, from, epsilon * SHRT_MAX);
5708 static BOOL weld_ushort2n(void *to, void *from, FLOAT epsilon)
5712 USHORT scaled_epsilon = (USHORT)(epsilon * USHRT_MAX);
5713 USHORT diff_x = s1[0] > s2[0] ? s1[0] - s2[0] : s2[0] - s1[0];
5714 USHORT diff_y = s1[1] > s2[1] ? s1[1] - s2[1] : s2[1] - s1[1];
5715 USHORT max_diff = max(diff_x, diff_y);
5717 if (max_diff <= scaled_epsilon)
5719 memcpy(to, from, 2 * sizeof(USHORT));
5727 static BOOL weld_ushort4n(void *to, void *from, FLOAT epsilon)
5731 USHORT scaled_epsilon = (USHORT)(epsilon * USHRT_MAX);
5732 USHORT diff_x = s1[0] > s2[0] ? s1[0] - s2[0] : s2[0] - s1[0];
5733 USHORT diff_y = s1[1] > s2[1] ? s1[1] - s2[1] : s2[1] - s1[1];
5734 USHORT diff_z = s1[2] > s2[2] ? s1[2] - s2[2] : s2[2] - s1[2];
5735 USHORT diff_w = s1[3] > s2[3] ? s1[3] - s2[3] : s2[3] - s1[3];
5736 USHORT max_diff = max(diff_x, diff_y);
5737 max_diff = max(diff_z, max_diff);
5738 max_diff = max(diff_w, max_diff);
5740 if (max_diff <= scaled_epsilon)
5742 memcpy(to, from, 4 * sizeof(USHORT));
5758 static struct udec3 dword_to_udec3(DWORD d)
5763 v.y = (d & 0xffc00) >> 10;
5764 v.z = (d & 0x3ff00000) >> 20;
5765 v.w = (d & 0xc0000000) >> 30;
5770 static BOOL weld_udec3(void *to, void *from, FLOAT epsilon)
5774 struct udec3 v1 = dword_to_udec3(*d1);
5775 struct udec3 v2 = dword_to_udec3(*d2);
5776 UINT truncated_epsilon = (UINT)epsilon;
5777 UINT diff_x = v1.x > v2.x ? v1.x - v2.x : v2.x - v1.x;
5778 UINT diff_y = v1.y > v2.y ? v1.y - v2.y : v2.y - v1.y;
5779 UINT diff_z = v1.z > v2.z ? v1.z - v2.z : v2.z - v1.z;
5780 UINT diff_w = v1.w > v2.w ? v1.w - v2.w : v2.w - v1.w;
5781 UINT max_diff = max(diff_x, diff_y);
5782 max_diff = max(diff_z, max_diff);
5783 max_diff = max(diff_w, max_diff);
5785 if (max_diff <= truncated_epsilon)
5787 memcpy(to, from, sizeof(DWORD));
5803 static struct dec3n dword_to_dec3n(DWORD d)
5808 v.y = (d & 0xffc00) >> 10;
5809 v.z = (d & 0x3ff00000) >> 20;
5810 v.w = (d & 0xc0000000) >> 30;
5815 static BOOL weld_dec3n(void *to, void *from, FLOAT epsilon)
5817 const UINT MAX_DEC3N = 511;
5820 struct dec3n v1 = dword_to_dec3n(*d1);
5821 struct dec3n v2 = dword_to_dec3n(*d2);
5822 INT scaled_epsilon = (INT)(epsilon * MAX_DEC3N);
5823 INT diff_x = abs(v1.x - v2.x);
5824 INT diff_y = abs(v1.y - v2.y);
5825 INT diff_z = abs(v1.z - v2.z);
5826 INT diff_w = abs(v1.w - v2.w);
5827 INT max_abs_diff = max(diff_x, diff_y);
5828 max_abs_diff = max(diff_z, max_abs_diff);
5829 max_abs_diff = max(diff_w, max_abs_diff);
5831 if (max_abs_diff <= scaled_epsilon)
5833 memcpy(to, from, sizeof(DWORD));
5841 static BOOL weld_float16_2(void *to, void *from, FLOAT epsilon)
5843 D3DXFLOAT16 *v1_float16 = to;
5844 D3DXFLOAT16 *v2_float16 = from;
5848 const UINT NUM_ELEM = 2;
5852 D3DXFloat16To32Array(v1, v1_float16, NUM_ELEM);
5853 D3DXFloat16To32Array(v2, v2_float16, NUM_ELEM);
5855 diff_x = fabsf(v1[0] - v2[0]);
5856 diff_y = fabsf(v1[1] - v2[1]);
5857 max_abs_diff = max(diff_x, diff_y);
5859 if (max_abs_diff <= epsilon)
5861 memcpy(to, from, NUM_ELEM * sizeof(D3DXFLOAT16));
5869 static BOOL weld_float16_4(void *to, void *from, FLOAT epsilon)
5871 D3DXFLOAT16 *v1_float16 = to;
5872 D3DXFLOAT16 *v2_float16 = from;
5878 const UINT NUM_ELEM = 4;
5882 D3DXFloat16To32Array(v1, v1_float16, NUM_ELEM);
5883 D3DXFloat16To32Array(v2, v2_float16, NUM_ELEM);
5885 diff_x = fabsf(v1[0] - v2[0]);
5886 diff_y = fabsf(v1[1] - v2[1]);
5887 diff_z = fabsf(v1[2] - v2[2]);
5888 diff_w = fabsf(v1[3] - v2[3]);
5889 max_abs_diff = max(diff_x, diff_y);
5890 max_abs_diff = max(diff_z, max_abs_diff);
5891 max_abs_diff = max(diff_w, max_abs_diff);
5893 if (max_abs_diff <= epsilon)
5895 memcpy(to, from, NUM_ELEM * sizeof(D3DXFLOAT16));
5903 /* Sets the vertex components to the same value if they are within epsilon. */
5904 static BOOL weld_component(void *to, void *from, D3DDECLTYPE type, FLOAT epsilon)
5906 /* Quiet FIXMEs as this is in a loop with potentially thousand of iterations. */
5907 BOOL fixme_once_unused = FALSE;
5908 BOOL fixme_once_unknown = FALSE;
5912 case D3DDECLTYPE_FLOAT1:
5913 return weld_float1(to, from, epsilon);
5915 case D3DDECLTYPE_FLOAT2:
5916 return weld_float2(to, from, epsilon);
5918 case D3DDECLTYPE_FLOAT3:
5919 return weld_float3(to, from, epsilon);
5921 case D3DDECLTYPE_FLOAT4:
5922 return weld_float4(to, from, epsilon);
5924 case D3DDECLTYPE_D3DCOLOR:
5925 return weld_d3dcolor(to, from, epsilon);
5927 case D3DDECLTYPE_UBYTE4:
5928 return weld_ubyte4(to, from, epsilon);
5930 case D3DDECLTYPE_SHORT2:
5931 return weld_short2(to, from, epsilon);
5933 case D3DDECLTYPE_SHORT4:
5934 return weld_short4(to, from, epsilon);
5936 case D3DDECLTYPE_UBYTE4N:
5937 return weld_ubyte4n(to, from, epsilon);
5939 case D3DDECLTYPE_SHORT2N:
5940 return weld_short2n(to, from, epsilon);
5942 case D3DDECLTYPE_SHORT4N:
5943 return weld_short4n(to, from, epsilon);
5945 case D3DDECLTYPE_USHORT2N:
5946 return weld_ushort2n(to, from, epsilon);
5948 case D3DDECLTYPE_USHORT4N:
5949 return weld_ushort4n(to, from, epsilon);
5951 case D3DDECLTYPE_UDEC3:
5952 return weld_udec3(to, from, epsilon);
5954 case D3DDECLTYPE_DEC3N:
5955 return weld_dec3n(to, from, epsilon);
5957 case D3DDECLTYPE_FLOAT16_2:
5958 return weld_float16_2(to, from, epsilon);
5960 case D3DDECLTYPE_FLOAT16_4:
5961 return weld_float16_4(to, from, epsilon);
5963 case D3DDECLTYPE_UNUSED:
5964 if (!fixme_once_unused++)
5965 FIXME("D3DDECLTYPE_UNUSED welding not implemented.\n");
5969 if (!fixme_once_unknown++)
5970 FIXME("Welding of unknown declaration type %d is not implemented.\n", type);
5977 static FLOAT get_component_epsilon(const D3DVERTEXELEMENT9 *decl_ptr, const D3DXWELDEPSILONS *epsilons)
5979 FLOAT epsilon = 0.0f;
5980 /* Quiet FIXMEs as this is in a loop with potentially thousand of iterations. */
5981 static BOOL fixme_once_blendindices = FALSE;
5982 static BOOL fixme_once_positiont = FALSE;
5983 static BOOL fixme_once_fog = FALSE;
5984 static BOOL fixme_once_depth = FALSE;
5985 static BOOL fixme_once_sample = FALSE;
5986 static BOOL fixme_once_unknown = FALSE;
5988 switch (decl_ptr->Usage)
5990 case D3DDECLUSAGE_POSITION:
5991 epsilon = epsilons->Position;
5993 case D3DDECLUSAGE_BLENDWEIGHT:
5994 epsilon = epsilons->BlendWeights;
5996 case D3DDECLUSAGE_NORMAL:
5997 epsilon = epsilons->Normals;
5999 case D3DDECLUSAGE_PSIZE:
6000 epsilon = epsilons->PSize;
6002 case D3DDECLUSAGE_TEXCOORD:
6004 BYTE usage_index = decl_ptr->UsageIndex;
6005 if (usage_index > 7)
6007 epsilon = epsilons->Texcoords[usage_index];
6010 case D3DDECLUSAGE_TANGENT:
6011 epsilon = epsilons->Tangent;
6013 case D3DDECLUSAGE_BINORMAL:
6014 epsilon = epsilons->Binormal;
6016 case D3DDECLUSAGE_TESSFACTOR:
6017 epsilon = epsilons->TessFactor;
6019 case D3DDECLUSAGE_COLOR:
6020 if (decl_ptr->UsageIndex == 0)
6021 epsilon = epsilons->Diffuse;
6022 else if (decl_ptr->UsageIndex == 1)
6023 epsilon = epsilons->Specular;
6027 case D3DDECLUSAGE_BLENDINDICES:
6028 if (!fixme_once_blendindices++)
6029 FIXME("D3DDECLUSAGE_BLENDINDICES welding not implemented.\n");
6031 case D3DDECLUSAGE_POSITIONT:
6032 if (!fixme_once_positiont++)
6033 FIXME("D3DDECLUSAGE_POSITIONT welding not implemented.\n");
6035 case D3DDECLUSAGE_FOG:
6036 if (!fixme_once_fog++)
6037 FIXME("D3DDECLUSAGE_FOG welding not implemented.\n");
6039 case D3DDECLUSAGE_DEPTH:
6040 if (!fixme_once_depth++)
6041 FIXME("D3DDECLUSAGE_DEPTH welding not implemented.\n");
6043 case D3DDECLUSAGE_SAMPLE:
6044 if (!fixme_once_sample++)
6045 FIXME("D3DDECLUSAGE_SAMPLE welding not implemented.\n");
6048 if (!fixme_once_unknown++)
6049 FIXME("Unknown usage %x\n", decl_ptr->Usage);
6056 /* Helper function for reading a 32-bit index buffer. */
6057 static inline DWORD read_ib(void *index_buffer, BOOL indices_are_32bit,
6060 if (indices_are_32bit)
6062 DWORD *indices = index_buffer;
6063 return indices[index];
6067 WORD *indices = index_buffer;
6068 return indices[index];
6072 /* Helper function for writing to a 32-bit index buffer. */
6073 static inline void write_ib(void *index_buffer, BOOL indices_are_32bit,
6074 DWORD index, DWORD value)
6076 if (indices_are_32bit)
6078 DWORD *indices = index_buffer;
6079 indices[index] = value;
6083 WORD *indices = index_buffer;
6084 indices[index] = value;
6088 /*************************************************************************
6089 * D3DXWeldVertices (D3DX9_36.@)
6091 * Welds together similar vertices. The similarity between vert-
6092 * ices can be the position and other components such as
6096 * mesh [I] Mesh which vertices will be welded together.
6097 * flags [I] D3DXWELDEPSILONSFLAGS specifying how to weld.
6098 * epsilons [I] How similar a component needs to be for welding.
6099 * adjacency [I] Which faces are adjacent to other faces.
6100 * adjacency_out [O] Updated adjacency after welding.
6101 * face_remap_out [O] Which faces the old faces have been mapped to.
6102 * vertex_remap_out [O] Which vertices the old vertices have been mapped to.
6106 * Failure: D3DERR_INVALIDCALL, E_OUTOFMEMORY.
6109 * Attribute sorting not implemented.
6112 HRESULT WINAPI D3DXWeldVertices(LPD3DXMESH mesh,
6114 CONST D3DXWELDEPSILONS *epsilons,
6115 CONST DWORD *adjacency,
6116 DWORD *adjacency_out,
6117 DWORD *face_remap_out,
6118 LPD3DXBUFFER *vertex_remap_out)
6120 DWORD *adjacency_generated = NULL;
6121 const DWORD *adjacency_ptr;
6122 DWORD *attributes = NULL;
6123 const FLOAT DEFAULT_EPSILON = 1.0e-6f;
6126 void *indices = NULL;
6127 BOOL indices_are_32bit = mesh->lpVtbl->GetOptions(mesh) & D3DXMESH_32BIT;
6128 DWORD optimize_flags;
6129 DWORD *point_reps = NULL;
6130 ID3DXMeshImpl *This = impl_from_ID3DXMesh(mesh);
6131 DWORD *vertex_face_map = NULL;
6132 ID3DXBuffer *vertex_remap = NULL;
6133 BYTE *vertices = NULL;
6135 TRACE("(%p, %x, %p, %p, %p, %p, %p)\n", mesh, flags, epsilons,
6136 adjacency, adjacency_out, face_remap_out, vertex_remap_out);
6140 WARN("No flags is undefined. Using D3DXWELDEPSILONS_WELDPARTIALMATCHES instead.\n");
6141 flags = D3DXWELDEPSILONS_WELDPARTIALMATCHES;
6144 if (adjacency) /* Use supplied adjacency. */
6146 adjacency_ptr = adjacency;
6148 else /* Adjacency has to be generated. */
6150 adjacency_generated = HeapAlloc(GetProcessHeap(), 0, 3 * This->numfaces * sizeof(*adjacency_generated));
6151 if (!adjacency_generated)
6153 ERR("Couldn't allocate memory for adjacency_generated.\n");
6157 hr = mesh->lpVtbl->GenerateAdjacency(mesh, DEFAULT_EPSILON, adjacency_generated);
6160 ERR("Couldn't generate adjacency.\n");
6163 adjacency_ptr = adjacency_generated;
6166 /* Point representation says which vertices can be replaced. */
6167 point_reps = HeapAlloc(GetProcessHeap(), 0, This->numvertices * sizeof(*point_reps));
6171 ERR("Couldn't allocate memory for point_reps.\n");
6174 hr = mesh->lpVtbl->ConvertAdjacencyToPointReps(mesh, adjacency_ptr, point_reps);
6177 ERR("ConvertAdjacencyToPointReps failed.\n");
6181 hr = mesh->lpVtbl->LockIndexBuffer(mesh, 0, &indices);
6184 ERR("Couldn't lock index buffer.\n");
6188 hr = mesh->lpVtbl->LockAttributeBuffer(mesh, 0, &attributes);
6191 ERR("Couldn't lock attribute buffer.\n");
6194 vertex_face_map = HeapAlloc(GetProcessHeap(), 0, This->numvertices * sizeof(*vertex_face_map));
6195 if (!vertex_face_map)
6198 ERR("Couldn't allocate memory for vertex_face_map.\n");
6201 /* Build vertex face map, so that a vertex's face can be looked up. */
6202 for (i = 0; i < This->numfaces; i++)
6205 for (j = 0; j < 3; j++)
6207 DWORD index = read_ib(indices, indices_are_32bit, 3*i + j);
6208 vertex_face_map[index] = i;
6212 if (flags & D3DXWELDEPSILONS_WELDPARTIALMATCHES)
6214 hr = mesh->lpVtbl->LockVertexBuffer(mesh, 0, (void**)&vertices);
6217 ERR("Couldn't lock vertex buffer.\n");
6220 /* For each vertex that can be removed, compare its vertex components
6221 * with the vertex components from the vertex that can replace it. A
6222 * vertex is only fully replaced if all the components match and the
6223 * flag D3DXWELDEPSILONS_DONOTREMOVEVERTICES is not set, and they
6224 * belong to the same attribute group. Otherwise the vertex components
6225 * that are within epsilon are set to the same value.
6227 for (i = 0; i < 3 * This->numfaces; i++)
6229 D3DVERTEXELEMENT9 *decl_ptr;
6230 DWORD vertex_size = mesh->lpVtbl->GetNumBytesPerVertex(mesh);
6231 DWORD num_vertex_components;
6234 DWORD index = read_ib(indices, indices_are_32bit, i);
6236 for (decl_ptr = This->cached_declaration, num_vertex_components = 0; decl_ptr->Stream != 0xFF; decl_ptr++, num_vertex_components++)
6238 BYTE *to = &vertices[vertex_size*index + decl_ptr->Offset];
6239 BYTE *from = &vertices[vertex_size*point_reps[index] + decl_ptr->Offset];
6240 FLOAT epsilon = get_component_epsilon(decl_ptr, epsilons);
6242 if (weld_component(to, from, decl_ptr->Type, epsilon))
6246 all_match = (num_vertex_components == matches);
6247 if (all_match && !(flags & D3DXWELDEPSILONS_DONOTREMOVEVERTICES))
6249 DWORD to_face = vertex_face_map[index];
6250 DWORD from_face = vertex_face_map[point_reps[index]];
6251 if(attributes[to_face] != attributes[from_face] && !(flags & D3DXWELDEPSILONS_DONOTSPLIT))
6253 write_ib(indices, indices_are_32bit, i, point_reps[index]);
6256 mesh->lpVtbl->UnlockVertexBuffer(mesh);
6259 else if (flags & D3DXWELDEPSILONS_WELDALL)
6261 for (i = 0; i < 3 * This->numfaces; i++)
6263 DWORD index = read_ib(indices, indices_are_32bit, i);
6264 DWORD to_face = vertex_face_map[index];
6265 DWORD from_face = vertex_face_map[point_reps[index]];
6266 if(attributes[to_face] != attributes[from_face] && !(flags & D3DXWELDEPSILONS_DONOTSPLIT))
6268 write_ib(indices, indices_are_32bit, i, point_reps[index]);
6271 mesh->lpVtbl->UnlockAttributeBuffer(mesh);
6273 mesh->lpVtbl->UnlockIndexBuffer(mesh);
6276 /* Compact mesh using OptimizeInplace */
6277 optimize_flags = D3DXMESHOPT_COMPACT;
6278 hr = mesh->lpVtbl->OptimizeInplace(mesh, optimize_flags, adjacency_ptr, adjacency_out, face_remap_out, vertex_remap_out);
6281 ERR("Couldn't compact mesh.\n");
6287 HeapFree(GetProcessHeap(), 0, adjacency_generated);
6288 HeapFree(GetProcessHeap(), 0, point_reps);
6289 HeapFree(GetProcessHeap(), 0, vertex_face_map);
6290 if (attributes) mesh->lpVtbl->UnlockAttributeBuffer(mesh);
6291 if (indices) mesh->lpVtbl->UnlockIndexBuffer(mesh);
6292 if (vertex_remap) ID3DXBuffer_Release(vertex_remap);
6293 if (vertices) mesh->lpVtbl->UnlockVertexBuffer(mesh);