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
39 #include "wine/debug.h"
40 #include "wine/unicode.h"
41 #include "wine/list.h"
42 #include "d3dx9_36_private.h"
44 WINE_DEFAULT_DEBUG_CHANNEL(d3dx);
46 typedef struct ID3DXMeshImpl
48 ID3DXMesh ID3DXMesh_iface;
55 IDirect3DDevice9 *device;
56 D3DVERTEXELEMENT9 cached_declaration[MAX_FVF_DECL_SIZE];
57 IDirect3DVertexDeclaration9 *vertex_declaration;
58 UINT vertex_declaration_size;
60 IDirect3DVertexBuffer9 *vertex_buffer;
61 IDirect3DIndexBuffer9 *index_buffer;
63 int attrib_buffer_lock_count;
64 DWORD attrib_table_size;
65 D3DXATTRIBUTERANGE *attrib_table;
68 static inline ID3DXMeshImpl *impl_from_ID3DXMesh(ID3DXMesh *iface)
70 return CONTAINING_RECORD(iface, ID3DXMeshImpl, ID3DXMesh_iface);
73 static HRESULT WINAPI ID3DXMeshImpl_QueryInterface(ID3DXMesh *iface, REFIID riid, LPVOID *object)
75 ID3DXMeshImpl *This = impl_from_ID3DXMesh(iface);
77 TRACE("(%p)->(%s, %p)\n", This, debugstr_guid(riid), object);
79 if (IsEqualGUID(riid, &IID_IUnknown) ||
80 IsEqualGUID(riid, &IID_ID3DXBaseMesh) ||
81 IsEqualGUID(riid, &IID_ID3DXMesh))
83 iface->lpVtbl->AddRef(iface);
88 WARN("Interface %s not found.\n", debugstr_guid(riid));
93 static ULONG WINAPI ID3DXMeshImpl_AddRef(ID3DXMesh *iface)
95 ID3DXMeshImpl *This = impl_from_ID3DXMesh(iface);
97 TRACE("(%p)->(): AddRef from %d\n", This, This->ref);
99 return InterlockedIncrement(&This->ref);
102 static ULONG WINAPI ID3DXMeshImpl_Release(ID3DXMesh *iface)
104 ID3DXMeshImpl *This = impl_from_ID3DXMesh(iface);
105 ULONG ref = InterlockedDecrement(&This->ref);
107 TRACE("(%p)->(): Release from %d\n", This, ref + 1);
111 IDirect3DIndexBuffer9_Release(This->index_buffer);
112 IDirect3DVertexBuffer9_Release(This->vertex_buffer);
113 if (This->vertex_declaration)
114 IDirect3DVertexDeclaration9_Release(This->vertex_declaration);
115 IDirect3DDevice9_Release(This->device);
116 HeapFree(GetProcessHeap(), 0, This->attrib_buffer);
117 HeapFree(GetProcessHeap(), 0, This->attrib_table);
118 HeapFree(GetProcessHeap(), 0, This);
124 /*** ID3DXBaseMesh ***/
125 static HRESULT WINAPI ID3DXMeshImpl_DrawSubset(ID3DXMesh *iface, DWORD attrib_id)
127 ID3DXMeshImpl *This = impl_from_ID3DXMesh(iface);
133 TRACE("(%p)->(%u)\n", This, attrib_id);
135 if (!This->vertex_declaration)
137 WARN("Can't draw a mesh with an invalid vertex declaration.\n");
141 vertex_size = iface->lpVtbl->GetNumBytesPerVertex(iface);
143 hr = IDirect3DDevice9_SetVertexDeclaration(This->device, This->vertex_declaration);
144 if (FAILED(hr)) return hr;
145 hr = IDirect3DDevice9_SetStreamSource(This->device, 0, This->vertex_buffer, 0, vertex_size);
146 if (FAILED(hr)) return hr;
147 hr = IDirect3DDevice9_SetIndices(This->device, This->index_buffer);
148 if (FAILED(hr)) return hr;
150 while (face_end < This->numfaces)
152 for (face_start = face_end; face_start < This->numfaces; face_start++)
154 if (This->attrib_buffer[face_start] == attrib_id)
157 if (face_start >= This->numfaces)
159 for (face_end = face_start + 1; face_end < This->numfaces; face_end++)
161 if (This->attrib_buffer[face_end] != attrib_id)
165 hr = IDirect3DDevice9_DrawIndexedPrimitive(This->device, D3DPT_TRIANGLELIST,
166 0, 0, This->numvertices, face_start * 3, face_end - face_start);
167 if (FAILED(hr)) return hr;
173 static DWORD WINAPI ID3DXMeshImpl_GetNumFaces(ID3DXMesh *iface)
175 ID3DXMeshImpl *This = impl_from_ID3DXMesh(iface);
177 TRACE("(%p)\n", This);
179 return This->numfaces;
182 static DWORD WINAPI ID3DXMeshImpl_GetNumVertices(ID3DXMesh *iface)
184 ID3DXMeshImpl *This = impl_from_ID3DXMesh(iface);
186 TRACE("(%p)\n", This);
188 return This->numvertices;
191 static DWORD WINAPI ID3DXMeshImpl_GetFVF(ID3DXMesh *iface)
193 ID3DXMeshImpl *This = impl_from_ID3DXMesh(iface);
195 TRACE("(%p)\n", This);
200 static void copy_declaration(D3DVERTEXELEMENT9 *dst, const D3DVERTEXELEMENT9 *src, UINT num_elem)
202 memcpy(dst, src, num_elem * sizeof(*src));
205 static HRESULT WINAPI ID3DXMeshImpl_GetDeclaration(ID3DXMesh *iface, D3DVERTEXELEMENT9 declaration[MAX_FVF_DECL_SIZE])
207 ID3DXMeshImpl *This = impl_from_ID3DXMesh(iface);
209 TRACE("(%p)\n", This);
211 if (declaration == NULL) return D3DERR_INVALIDCALL;
213 copy_declaration(declaration, This->cached_declaration, This->num_elem);
218 static DWORD WINAPI ID3DXMeshImpl_GetNumBytesPerVertex(ID3DXMesh *iface)
220 ID3DXMeshImpl *This = impl_from_ID3DXMesh(iface);
222 TRACE("iface (%p)\n", This);
224 return This->vertex_declaration_size;
227 static DWORD WINAPI ID3DXMeshImpl_GetOptions(ID3DXMesh *iface)
229 ID3DXMeshImpl *This = impl_from_ID3DXMesh(iface);
231 TRACE("(%p)\n", This);
233 return This->options;
236 static HRESULT WINAPI ID3DXMeshImpl_GetDevice(ID3DXMesh *iface, LPDIRECT3DDEVICE9 *device)
238 ID3DXMeshImpl *This = impl_from_ID3DXMesh(iface);
240 TRACE("(%p)->(%p)\n", This, device);
242 if (device == NULL) return D3DERR_INVALIDCALL;
243 *device = This->device;
244 IDirect3DDevice9_AddRef(This->device);
249 static HRESULT WINAPI ID3DXMeshImpl_CloneMeshFVF(ID3DXMesh *iface, DWORD options, DWORD fvf, LPDIRECT3DDEVICE9 device, LPD3DXMESH *clone_mesh)
251 ID3DXMeshImpl *This = impl_from_ID3DXMesh(iface);
253 D3DVERTEXELEMENT9 declaration[MAX_FVF_DECL_SIZE];
255 TRACE("(%p)->(%x,%x,%p,%p)\n", This, options, fvf, device, clone_mesh);
257 hr = D3DXDeclaratorFromFVF(fvf, declaration);
258 if (FAILED(hr)) return hr;
260 return iface->lpVtbl->CloneMesh(iface, options, declaration, device, clone_mesh);
263 static HRESULT WINAPI ID3DXMeshImpl_CloneMesh(ID3DXMesh *iface, DWORD options, CONST D3DVERTEXELEMENT9 *declaration, LPDIRECT3DDEVICE9 device,
264 LPD3DXMESH *clone_mesh_out)
266 ID3DXMeshImpl *This = impl_from_ID3DXMesh(iface);
267 ID3DXMeshImpl *cloned_this;
268 ID3DXMesh *clone_mesh;
269 D3DVERTEXELEMENT9 orig_declaration[MAX_FVF_DECL_SIZE] = { D3DDECL_END() };
270 void *data_in, *data_out;
275 TRACE("(%p)->(%x,%p,%p,%p)\n", This, options, declaration, device, clone_mesh_out);
278 return D3DERR_INVALIDCALL;
280 hr = iface->lpVtbl->GetDeclaration(iface, orig_declaration);
281 if (FAILED(hr)) return hr;
283 for (i = 0; orig_declaration[i].Stream != 0xff; i++) {
284 if (memcmp(&orig_declaration[i], &declaration[i], sizeof(*declaration)))
286 FIXME("Vertex buffer conversion not implemented.\n");
291 hr = D3DXCreateMesh(This->numfaces, This->numvertices, options & ~D3DXMESH_VB_SHARE,
292 declaration, device, &clone_mesh);
293 if (FAILED(hr)) return hr;
295 cloned_this = impl_from_ID3DXMesh(clone_mesh);
296 vertex_size = clone_mesh->lpVtbl->GetNumBytesPerVertex(clone_mesh);
298 if (options & D3DXMESH_VB_SHARE) {
299 IDirect3DVertexBuffer9_AddRef(This->vertex_buffer);
300 /* FIXME: refactor to avoid creating a new vertex buffer */
301 IDirect3DVertexBuffer9_Release(cloned_this->vertex_buffer);
302 cloned_this->vertex_buffer = This->vertex_buffer;
304 hr = iface->lpVtbl->LockVertexBuffer(iface, D3DLOCK_READONLY, &data_in);
305 if (FAILED(hr)) goto error;
306 hr = clone_mesh->lpVtbl->LockVertexBuffer(clone_mesh, D3DLOCK_DISCARD, &data_out);
308 iface->lpVtbl->UnlockVertexBuffer(iface);
311 memcpy(data_out, data_in, This->numvertices * vertex_size);
312 clone_mesh->lpVtbl->UnlockVertexBuffer(clone_mesh);
313 iface->lpVtbl->UnlockVertexBuffer(iface);
316 hr = iface->lpVtbl->LockIndexBuffer(iface, D3DLOCK_READONLY, &data_in);
317 if (FAILED(hr)) goto error;
318 hr = clone_mesh->lpVtbl->LockIndexBuffer(clone_mesh, D3DLOCK_DISCARD, &data_out);
320 iface->lpVtbl->UnlockIndexBuffer(iface);
323 if ((options ^ This->options) & D3DXMESH_32BIT) {
324 if (options & D3DXMESH_32BIT) {
325 for (i = 0; i < This->numfaces * 3; i++)
326 ((DWORD*)data_out)[i] = ((WORD*)data_in)[i];
328 for (i = 0; i < This->numfaces * 3; i++)
329 ((WORD*)data_out)[i] = ((DWORD*)data_in)[i];
332 memcpy(data_out, data_in, This->numfaces * 3 * (options & D3DXMESH_32BIT ? 4 : 2));
334 clone_mesh->lpVtbl->UnlockIndexBuffer(clone_mesh);
335 iface->lpVtbl->UnlockIndexBuffer(iface);
337 memcpy(cloned_this->attrib_buffer, This->attrib_buffer, This->numfaces * sizeof(*This->attrib_buffer));
339 if (This->attrib_table_size)
341 cloned_this->attrib_table_size = This->attrib_table_size;
342 cloned_this->attrib_table = HeapAlloc(GetProcessHeap(), 0, This->attrib_table_size * sizeof(*This->attrib_table));
343 if (!cloned_this->attrib_table) {
347 memcpy(cloned_this->attrib_table, This->attrib_table, This->attrib_table_size * sizeof(*This->attrib_table));
350 *clone_mesh_out = clone_mesh;
354 IUnknown_Release(clone_mesh);
358 static HRESULT WINAPI ID3DXMeshImpl_GetVertexBuffer(ID3DXMesh *iface, LPDIRECT3DVERTEXBUFFER9 *vertex_buffer)
360 ID3DXMeshImpl *This = impl_from_ID3DXMesh(iface);
362 TRACE("(%p)->(%p)\n", This, vertex_buffer);
364 if (vertex_buffer == NULL) return D3DERR_INVALIDCALL;
365 *vertex_buffer = This->vertex_buffer;
366 IDirect3DVertexBuffer9_AddRef(This->vertex_buffer);
371 static HRESULT WINAPI ID3DXMeshImpl_GetIndexBuffer(ID3DXMesh *iface, LPDIRECT3DINDEXBUFFER9 *index_buffer)
373 ID3DXMeshImpl *This = impl_from_ID3DXMesh(iface);
375 TRACE("(%p)->(%p)\n", This, index_buffer);
377 if (index_buffer == NULL) return D3DERR_INVALIDCALL;
378 *index_buffer = This->index_buffer;
379 IDirect3DIndexBuffer9_AddRef(This->index_buffer);
384 static HRESULT WINAPI ID3DXMeshImpl_LockVertexBuffer(ID3DXMesh *iface, DWORD flags, LPVOID *data)
386 ID3DXMeshImpl *This = impl_from_ID3DXMesh(iface);
388 TRACE("(%p)->(%u,%p)\n", This, flags, data);
390 return IDirect3DVertexBuffer9_Lock(This->vertex_buffer, 0, 0, data, flags);
393 static HRESULT WINAPI ID3DXMeshImpl_UnlockVertexBuffer(ID3DXMesh *iface)
395 ID3DXMeshImpl *This = impl_from_ID3DXMesh(iface);
397 TRACE("(%p)\n", This);
399 return IDirect3DVertexBuffer9_Unlock(This->vertex_buffer);
402 static HRESULT WINAPI ID3DXMeshImpl_LockIndexBuffer(ID3DXMesh *iface, DWORD flags, LPVOID *data)
404 ID3DXMeshImpl *This = impl_from_ID3DXMesh(iface);
406 TRACE("(%p)->(%u,%p)\n", This, flags, data);
408 return IDirect3DIndexBuffer9_Lock(This->index_buffer, 0, 0, data, flags);
411 static HRESULT WINAPI ID3DXMeshImpl_UnlockIndexBuffer(ID3DXMesh *iface)
413 ID3DXMeshImpl *This = impl_from_ID3DXMesh(iface);
415 TRACE("(%p)\n", This);
417 return IDirect3DIndexBuffer9_Unlock(This->index_buffer);
420 static HRESULT WINAPI ID3DXMeshImpl_GetAttributeTable(ID3DXMesh *iface, D3DXATTRIBUTERANGE *attrib_table, DWORD *attrib_table_size)
422 ID3DXMeshImpl *This = impl_from_ID3DXMesh(iface);
424 TRACE("(%p)->(%p,%p)\n", This, attrib_table, attrib_table_size);
426 if (attrib_table_size)
427 *attrib_table_size = This->attrib_table_size;
430 CopyMemory(attrib_table, This->attrib_table, This->attrib_table_size * sizeof(*attrib_table));
445 struct edge_face *entries;
448 /* Builds up a map of which face a new edge belongs to. That way the adjacency
449 * of another edge can be looked up. An edge has an adjacent face if there
450 * is an edge going in the opposite direction in the map. For example if the
451 * edge (v1, v2) belongs to face 4, and there is a mapping (v2, v1)->7, then
452 * face 4 and 7 are adjacent.
454 * Each edge might have been replaced with another edge, or none at all. There
455 * is at most one edge to face mapping, i.e. an edge can only belong to one
458 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)
463 edge_face_map->lists = HeapAlloc(GetProcessHeap(), 0, 3 * num_faces * sizeof(*edge_face_map->lists));
464 if (!edge_face_map->lists) return E_OUTOFMEMORY;
466 edge_face_map->entries = HeapAlloc(GetProcessHeap(), 0, 3 * num_faces * sizeof(*edge_face_map->entries));
467 if (!edge_face_map->entries) return E_OUTOFMEMORY;
470 /* Initialize all lists */
471 for (i = 0; i < 3 * num_faces; i++)
473 list_init(&edge_face_map->lists[i]);
475 /* Build edge face mapping */
476 for (face = 0; face < num_faces; face++)
478 for (edge = 0; edge < 3; edge++)
480 DWORD v1 = index_buffer[3*face + edge];
481 DWORD v2 = index_buffer[3*face + (edge+1)%3];
482 DWORD new_v1 = point_reps[v1]; /* What v1 has been replaced with */
483 DWORD new_v2 = point_reps[v2];
485 if (v1 != v2) /* Only map non-collapsed edges */
488 edge_face_map->entries[i].v2 = new_v2;
489 edge_face_map->entries[i].face = face;
490 list_add_head(&edge_face_map->lists[new_v1], &edge_face_map->entries[i].entry);
498 static DWORD find_adjacent_face(struct edge_face_map *edge_face_map, DWORD vertex1, DWORD vertex2, CONST DWORD num_faces)
500 struct edge_face *edge_face_ptr;
502 LIST_FOR_EACH_ENTRY(edge_face_ptr, &edge_face_map->lists[vertex2], struct edge_face, entry)
504 if (edge_face_ptr->v2 == vertex1)
505 return edge_face_ptr->face;
511 static DWORD *generate_identity_point_reps(DWORD num_vertices)
513 DWORD *id_point_reps;
516 id_point_reps = HeapAlloc(GetProcessHeap(), 0, num_vertices * sizeof(*id_point_reps));
520 for (i = 0; i < num_vertices; i++)
522 id_point_reps[i] = i;
525 return id_point_reps;
528 static HRESULT WINAPI ID3DXMeshImpl_ConvertPointRepsToAdjacency(ID3DXMesh *iface, CONST DWORD *point_reps, DWORD *adjacency)
530 ID3DXMeshImpl *This = impl_from_ID3DXMesh(iface);
532 DWORD num_faces = iface->lpVtbl->GetNumFaces(iface);
533 DWORD num_vertices = iface->lpVtbl->GetNumVertices(iface);
534 DWORD options = iface->lpVtbl->GetOptions(iface);
535 BOOL indices_are_16_bit = !(options & D3DXMESH_32BIT);
540 struct edge_face_map edge_face_map = {0};
541 CONST DWORD *point_reps_ptr = NULL;
542 DWORD *id_point_reps = NULL;
544 TRACE("(%p)->(%p,%p)\n", This, point_reps, adjacency);
546 if (!adjacency) return D3DERR_INVALIDCALL;
548 if (!point_reps) /* Identity point reps */
550 id_point_reps = generate_identity_point_reps(num_vertices);
557 point_reps_ptr = id_point_reps;
561 point_reps_ptr = point_reps;
564 hr = iface->lpVtbl->LockIndexBuffer(iface, D3DLOCK_READONLY, &ib_ptr);
565 if (FAILED(hr)) goto cleanup;
567 if (indices_are_16_bit)
569 /* Widen 16 bit to 32 bit */
571 WORD *ib_16bit = ib_ptr;
572 ib = HeapAlloc(GetProcessHeap(), 0, 3 * num_faces * sizeof(DWORD));
578 for (i = 0; i < 3 * num_faces; i++)
588 hr = init_edge_face_map(&edge_face_map, ib, point_reps_ptr, num_faces);
589 if (FAILED(hr)) goto cleanup;
591 /* Create adjacency */
592 for (face = 0; face < num_faces; face++)
594 for (edge = 0; edge < 3; edge++)
596 DWORD v1 = ib[3*face + edge];
597 DWORD v2 = ib[3*face + (edge+1)%3];
598 DWORD new_v1 = point_reps_ptr[v1];
599 DWORD new_v2 = point_reps_ptr[v2];
602 adj_face = find_adjacent_face(&edge_face_map, new_v1, new_v2, num_faces);
603 adjacency[3*face + edge] = adj_face;
609 HeapFree(GetProcessHeap(), 0, id_point_reps);
610 if (indices_are_16_bit) HeapFree(GetProcessHeap(), 0, ib);
611 HeapFree(GetProcessHeap(), 0, edge_face_map.lists);
612 HeapFree(GetProcessHeap(), 0, edge_face_map.entries);
613 if(ib_ptr) iface->lpVtbl->UnlockIndexBuffer(iface);
617 /* ConvertAdjacencyToPointReps helper function.
619 * Goes around the edges of each face and replaces the vertices in any adjacent
620 * face's edge with its own vertices(if its vertices have a lower index). This
621 * way as few as possible low index vertices are shared among the faces. The
622 * re-ordered index buffer is stored in new_indices.
624 * The vertices in a point representation must be ordered sequentially, e.g.
625 * index 5 holds the index of the vertex that replaces vertex 5, i.e. if
626 * vertex 5 is replaced by vertex 3 then index 5 would contain 3. If no vertex
627 * replaces it, then it contains the same number as the index itself, e.g.
628 * index 5 would contain 5. */
629 static HRESULT propagate_face_vertices(CONST DWORD *adjacency, DWORD *point_reps,
630 CONST DWORD *indices, DWORD *new_indices,
631 CONST DWORD face, CONST DWORD numfaces)
633 const unsigned int VERTS_PER_FACE = 3;
634 DWORD edge, opp_edge;
635 DWORD face_base = VERTS_PER_FACE * face;
637 for (edge = 0; edge < VERTS_PER_FACE; edge++)
639 DWORD adj_face = adjacency[face_base + edge];
642 if (adj_face == -1) /* No adjacent face. */
644 else if (adj_face >= numfaces)
646 /* This throws exception on Windows */
647 WARN("Index out of bounds. Got %d expected less than %d.\n",
649 return D3DERR_INVALIDCALL;
651 adj_face_base = 3 * adj_face;
653 /* Find opposite edge in adjacent face. */
654 for (opp_edge = 0; opp_edge < VERTS_PER_FACE; opp_edge++)
656 DWORD opp_edge_index = adj_face_base + opp_edge;
657 if (adjacency[opp_edge_index] == face)
658 break; /* Found opposite edge. */
661 /* Replaces vertices in opposite edge with vertices from current edge. */
662 for (i = 0; i < 2; i++)
664 DWORD from = face_base + (edge + (1 - i)) % VERTS_PER_FACE;
665 DWORD to = adj_face_base + (opp_edge + i) % VERTS_PER_FACE;
667 /* Propagate lowest index. */
668 if (new_indices[to] > new_indices[from])
670 new_indices[to] = new_indices[from];
671 point_reps[indices[to]] = new_indices[from];
679 static HRESULT WINAPI ID3DXMeshImpl_ConvertAdjacencyToPointReps(ID3DXMesh *iface, CONST DWORD *adjacency, DWORD *point_reps)
684 DWORD *indices = NULL;
685 WORD *indices_16bit = NULL;
686 DWORD *new_indices = NULL;
687 const unsigned int VERTS_PER_FACE = 3;
689 ID3DXMeshImpl *This = impl_from_ID3DXMesh(iface);
691 TRACE("(%p)->(%p,%p)\n", This, adjacency, point_reps);
695 WARN("NULL adjacency.\n");
696 hr = D3DERR_INVALIDCALL;
702 WARN("NULL point_reps.\n");
703 hr = D3DERR_INVALIDCALL;
707 /* Should never happen as CreateMesh does not allow meshes with 0 faces */
708 if (This->numfaces == 0)
710 ERR("Number of faces was zero.\n");
711 hr = D3DERR_INVALIDCALL;
715 new_indices = HeapAlloc(GetProcessHeap(), 0, VERTS_PER_FACE * This->numfaces * sizeof(*indices));
722 if (This->options & D3DXMESH_32BIT)
724 hr = iface->lpVtbl->LockIndexBuffer(iface, D3DLOCK_READONLY, (void**)&indices);
725 if (FAILED(hr)) goto cleanup;
726 memcpy(new_indices, indices, VERTS_PER_FACE * This->numfaces * sizeof(*indices));
730 /* Make a widening copy of indices_16bit into indices and new_indices
731 * in order to re-use the helper function */
732 hr = iface->lpVtbl->LockIndexBuffer(iface, D3DLOCK_READONLY, (void**)&indices_16bit);
733 if (FAILED(hr)) goto cleanup;
734 indices = HeapAlloc(GetProcessHeap(), 0, VERTS_PER_FACE * This->numfaces * sizeof(*indices));
740 for (i = 0; i < VERTS_PER_FACE * This->numfaces; i++)
742 new_indices[i] = indices_16bit[i];
743 indices[i] = indices_16bit[i];
747 /* Vertices are ordered sequentially in the point representation. */
748 for (i = 0; i < This->numvertices; i++)
753 /* Propagate vertices with low indices so as few vertices as possible
754 * are used in the mesh.
756 for (face = 0; face < This->numfaces; face++)
758 hr = propagate_face_vertices(adjacency, point_reps, indices, new_indices, face, This->numfaces);
759 if (FAILED(hr)) goto cleanup;
761 /* Go in opposite direction to catch all face orderings */
762 for (face = 0; face < This->numfaces; face++)
764 hr = propagate_face_vertices(adjacency, point_reps,
765 indices, new_indices,
766 (This->numfaces - 1) - face, This->numfaces);
767 if (FAILED(hr)) goto cleanup;
772 if (This->options & D3DXMESH_32BIT)
774 if (indices) iface->lpVtbl->UnlockIndexBuffer(iface);
778 if (indices_16bit) iface->lpVtbl->UnlockIndexBuffer(iface);
779 HeapFree(GetProcessHeap(), 0, indices);
781 HeapFree(GetProcessHeap(), 0, new_indices);
785 struct vertex_metadata {
788 DWORD first_shared_index;
791 static int compare_vertex_keys(const void *a, const void *b)
793 const struct vertex_metadata *left = a;
794 const struct vertex_metadata *right = b;
795 if (left->key == right->key)
797 return left->key < right->key ? -1 : 1;
800 static HRESULT WINAPI ID3DXMeshImpl_GenerateAdjacency(ID3DXMesh *iface, FLOAT epsilon, DWORD *adjacency)
802 ID3DXMeshImpl *This = impl_from_ID3DXMesh(iface);
804 BYTE *vertices = NULL;
805 const DWORD *indices = NULL;
808 /* sort the vertices by (x + y + z) to quickly find coincident vertices */
809 struct vertex_metadata *sorted_vertices;
810 /* shared_indices links together identical indices in the index buffer so
811 * that adjacency checks can be limited to faces sharing a vertex */
812 DWORD *shared_indices = NULL;
813 const FLOAT epsilon_sq = epsilon * epsilon;
816 TRACE("(%p)->(%f,%p)\n", This, epsilon, adjacency);
819 return D3DERR_INVALIDCALL;
821 buffer_size = This->numfaces * 3 * sizeof(*shared_indices) + This->numvertices * sizeof(*sorted_vertices);
822 if (!(This->options & D3DXMESH_32BIT))
823 buffer_size += This->numfaces * 3 * sizeof(*indices);
824 shared_indices = HeapAlloc(GetProcessHeap(), 0, buffer_size);
826 return E_OUTOFMEMORY;
827 sorted_vertices = (struct vertex_metadata*)(shared_indices + This->numfaces * 3);
829 hr = iface->lpVtbl->LockVertexBuffer(iface, D3DLOCK_READONLY, (void**)&vertices);
830 if (FAILED(hr)) goto cleanup;
831 hr = iface->lpVtbl->LockIndexBuffer(iface, D3DLOCK_READONLY, (void**)&indices);
832 if (FAILED(hr)) goto cleanup;
834 if (!(This->options & D3DXMESH_32BIT)) {
835 const WORD *word_indices = (const WORD*)indices;
836 DWORD *dword_indices = (DWORD*)(sorted_vertices + This->numvertices);
837 indices = dword_indices;
838 for (i = 0; i < This->numfaces * 3; i++)
839 *dword_indices++ = *word_indices++;
842 vertex_size = iface->lpVtbl->GetNumBytesPerVertex(iface);
843 for (i = 0; i < This->numvertices; i++) {
844 D3DXVECTOR3 *vertex = (D3DXVECTOR3*)(vertices + vertex_size * i);
845 sorted_vertices[i].first_shared_index = -1;
846 sorted_vertices[i].key = vertex->x + vertex->y + vertex->z;
847 sorted_vertices[i].vertex_index = i;
849 for (i = 0; i < This->numfaces * 3; i++) {
850 DWORD *first_shared_index = &sorted_vertices[indices[i]].first_shared_index;
851 shared_indices[i] = *first_shared_index;
852 *first_shared_index = i;
855 qsort(sorted_vertices, This->numvertices, sizeof(*sorted_vertices), compare_vertex_keys);
857 for (i = 0; i < This->numvertices; i++) {
858 struct vertex_metadata *sorted_vertex_a = &sorted_vertices[i];
859 D3DXVECTOR3 *vertex_a = (D3DXVECTOR3*)(vertices + sorted_vertex_a->vertex_index * vertex_size);
860 DWORD shared_index_a = sorted_vertex_a->first_shared_index;
862 while (shared_index_a != -1) {
864 DWORD shared_index_b = shared_indices[shared_index_a];
865 struct vertex_metadata *sorted_vertex_b = sorted_vertex_a;
868 while (shared_index_b != -1) {
869 /* faces are adjacent if they have another coincident vertex */
870 DWORD base_a = (shared_index_a / 3) * 3;
871 DWORD base_b = (shared_index_b / 3) * 3;
872 BOOL adjacent = FALSE;
875 for (k = 0; k < 3; k++) {
876 if (adjacency[base_b + k] == shared_index_a / 3) {
882 for (k = 1; k <= 2; k++) {
883 DWORD vertex_index_a = base_a + (shared_index_a + k) % 3;
884 DWORD vertex_index_b = base_b + (shared_index_b + (3 - k)) % 3;
885 adjacent = indices[vertex_index_a] == indices[vertex_index_b];
886 if (!adjacent && epsilon >= 0.0f) {
887 D3DXVECTOR3 delta = {0.0f, 0.0f, 0.0f};
890 D3DXVec3Subtract(&delta,
891 (D3DXVECTOR3*)(vertices + indices[vertex_index_a] * vertex_size),
892 (D3DXVECTOR3*)(vertices + indices[vertex_index_b] * vertex_size));
893 length_sq = D3DXVec3LengthSq(&delta);
894 adjacent = epsilon == 0.0f ? length_sq == 0.0f : length_sq < epsilon_sq;
897 DWORD adj_a = base_a + 2 - (vertex_index_a + shared_index_a + 1) % 3;
898 DWORD adj_b = base_b + 2 - (vertex_index_b + shared_index_b + 1) % 3;
899 if (adjacency[adj_a] == -1 && adjacency[adj_b] == -1) {
900 adjacency[adj_a] = base_b / 3;
901 adjacency[adj_b] = base_a / 3;
908 shared_index_b = shared_indices[shared_index_b];
910 while (++j < This->numvertices) {
911 D3DXVECTOR3 *vertex_b;
914 if (sorted_vertex_b->key - sorted_vertex_a->key > epsilon * 3.0f) {
915 /* no more coincident vertices to try */
916 j = This->numvertices;
919 /* check for coincidence */
920 vertex_b = (D3DXVECTOR3*)(vertices + sorted_vertex_b->vertex_index * vertex_size);
921 if (fabsf(vertex_a->x - vertex_b->x) <= epsilon &&
922 fabsf(vertex_a->y - vertex_b->y) <= epsilon &&
923 fabsf(vertex_a->z - vertex_b->z) <= epsilon)
928 if (j >= This->numvertices)
930 shared_index_b = sorted_vertex_b->first_shared_index;
933 sorted_vertex_a->first_shared_index = shared_indices[sorted_vertex_a->first_shared_index];
934 shared_index_a = sorted_vertex_a->first_shared_index;
940 if (indices) iface->lpVtbl->UnlockIndexBuffer(iface);
941 if (vertices) iface->lpVtbl->UnlockVertexBuffer(iface);
942 HeapFree(GetProcessHeap(), 0, shared_indices);
946 static HRESULT WINAPI ID3DXMeshImpl_UpdateSemantics(ID3DXMesh *iface, D3DVERTEXELEMENT9 declaration[MAX_FVF_DECL_SIZE])
949 ID3DXMeshImpl *This = impl_from_ID3DXMesh(iface);
950 UINT vertex_declaration_size;
953 TRACE("(%p)->(%p)\n", This, declaration);
957 WARN("Invalid declaration. Can't use NULL declaration.\n");
958 return D3DERR_INVALIDCALL;
961 /* New declaration must be same size as original */
962 vertex_declaration_size = D3DXGetDeclVertexSize(declaration, declaration[0].Stream);
963 if (vertex_declaration_size != This->vertex_declaration_size)
965 WARN("Invalid declaration. New vertex size does not match the original vertex size.\n");
966 return D3DERR_INVALIDCALL;
969 /* New declaration must not contain non-zero Stream value */
970 for (i = 0; declaration[i].Stream != 0xff; i++)
972 if (declaration[i].Stream != 0)
974 WARN("Invalid declaration. New declaration contains non-zero Stream value.\n");
975 return D3DERR_INVALIDCALL;
979 This->num_elem = i + 1;
980 copy_declaration(This->cached_declaration, declaration, This->num_elem);
982 if (This->vertex_declaration)
983 IDirect3DVertexDeclaration9_Release(This->vertex_declaration);
985 /* An application can pass an invalid declaration to UpdateSemantics and
986 * still expect D3D_OK (see tests). If the declaration is invalid, then
987 * subsequent calls to DrawSubset will fail. This is handled by setting the
988 * vertex declaration to NULL.
989 * GetDeclaration, GetNumBytesPerVertex must, however, use the new
990 * invalid declaration. This is handled by them using the cached vertex
991 * declaration instead of the actual vertex declaration.
993 hr = IDirect3DDevice9_CreateVertexDeclaration(This->device,
995 &This->vertex_declaration);
998 WARN("Using invalid declaration. Calls to DrawSubset will fail.\n");
999 This->vertex_declaration = NULL;
1006 static HRESULT WINAPI ID3DXMeshImpl_LockAttributeBuffer(ID3DXMesh *iface, DWORD flags, DWORD **data)
1008 ID3DXMeshImpl *This = impl_from_ID3DXMesh(iface);
1010 TRACE("(%p)->(%u,%p)\n", This, flags, data);
1012 InterlockedIncrement(&This->attrib_buffer_lock_count);
1014 if (!(flags & D3DLOCK_READONLY)) {
1015 D3DXATTRIBUTERANGE *attrib_table = This->attrib_table;
1016 This->attrib_table_size = 0;
1017 This->attrib_table = NULL;
1018 HeapFree(GetProcessHeap(), 0, attrib_table);
1021 *data = This->attrib_buffer;
1026 static HRESULT WINAPI ID3DXMeshImpl_UnlockAttributeBuffer(ID3DXMesh *iface)
1028 ID3DXMeshImpl *This = impl_from_ID3DXMesh(iface);
1031 TRACE("(%p)\n", This);
1033 lock_count = InterlockedDecrement(&This->attrib_buffer_lock_count);
1035 if (lock_count < 0) {
1036 InterlockedIncrement(&This->attrib_buffer_lock_count);
1037 return D3DERR_INVALIDCALL;
1043 static HRESULT WINAPI ID3DXMeshImpl_Optimize(ID3DXMesh *iface, DWORD flags, CONST DWORD *adjacency_in, DWORD *adjacency_out,
1044 DWORD *face_remap, LPD3DXBUFFER *vertex_remap, LPD3DXMESH *opt_mesh)
1046 ID3DXMeshImpl *This = impl_from_ID3DXMesh(iface);
1048 D3DVERTEXELEMENT9 declaration[MAX_FVF_DECL_SIZE] = { D3DDECL_END() };
1049 ID3DXMesh *optimized_mesh;
1051 TRACE("(%p)->(%x,%p,%p,%p,%p,%p)\n", This, flags, adjacency_in, adjacency_out, face_remap, vertex_remap, opt_mesh);
1054 return D3DERR_INVALIDCALL;
1056 hr = iface->lpVtbl->GetDeclaration(iface, declaration);
1057 if (FAILED(hr)) return hr;
1059 hr = iface->lpVtbl->CloneMesh(iface, This->options, declaration, This->device, &optimized_mesh);
1060 if (FAILED(hr)) return hr;
1062 hr = optimized_mesh->lpVtbl->OptimizeInplace(optimized_mesh, flags, adjacency_in, adjacency_out, face_remap, vertex_remap);
1064 *opt_mesh = optimized_mesh;
1066 IUnknown_Release(optimized_mesh);
1070 /* Creates a vertex_remap that removes unused vertices.
1071 * Indices are updated according to the vertex_remap. */
1072 static HRESULT compact_mesh(ID3DXMeshImpl *This, DWORD *indices, DWORD *new_num_vertices, ID3DXBuffer **vertex_remap)
1075 DWORD *vertex_remap_ptr;
1076 DWORD num_used_vertices;
1079 hr = D3DXCreateBuffer(This->numvertices * sizeof(DWORD), vertex_remap);
1080 if (FAILED(hr)) return hr;
1081 vertex_remap_ptr = ID3DXBuffer_GetBufferPointer(*vertex_remap);
1083 for (i = 0; i < This->numfaces * 3; i++)
1084 vertex_remap_ptr[indices[i]] = 1;
1086 /* create old->new vertex mapping */
1087 num_used_vertices = 0;
1088 for (i = 0; i < This->numvertices; i++) {
1089 if (vertex_remap_ptr[i])
1090 vertex_remap_ptr[i] = num_used_vertices++;
1092 vertex_remap_ptr[i] = -1;
1094 /* convert indices */
1095 for (i = 0; i < This->numfaces * 3; i++)
1096 indices[i] = vertex_remap_ptr[indices[i]];
1098 /* create new->old vertex mapping */
1099 num_used_vertices = 0;
1100 for (i = 0; i < This->numvertices; i++) {
1101 if (vertex_remap_ptr[i] != -1)
1102 vertex_remap_ptr[num_used_vertices++] = i;
1104 for (i = num_used_vertices; i < This->numvertices; i++)
1105 vertex_remap_ptr[i] = -1;
1107 *new_num_vertices = num_used_vertices;
1112 /* count the number of unique attribute values in a sorted attribute buffer */
1113 static DWORD count_attributes(const DWORD *attrib_buffer, DWORD numfaces)
1115 DWORD last_attribute = attrib_buffer[0];
1116 DWORD attrib_table_size = 1;
1118 for (i = 1; i < numfaces; i++) {
1119 if (attrib_buffer[i] != last_attribute) {
1120 last_attribute = attrib_buffer[i];
1121 attrib_table_size++;
1124 return attrib_table_size;
1127 static void fill_attribute_table(DWORD *attrib_buffer, DWORD numfaces, void *indices,
1128 BOOL is_32bit_indices, D3DXATTRIBUTERANGE *attrib_table)
1130 DWORD attrib_table_size = 0;
1131 DWORD last_attribute = attrib_buffer[0];
1132 DWORD min_vertex, max_vertex;
1135 attrib_table[0].AttribId = last_attribute;
1136 attrib_table[0].FaceStart = 0;
1137 min_vertex = (DWORD)-1;
1139 for (i = 0; i < numfaces; i++) {
1142 if (attrib_buffer[i] != last_attribute) {
1143 last_attribute = attrib_buffer[i];
1144 attrib_table[attrib_table_size].FaceCount = i - attrib_table[attrib_table_size].FaceStart;
1145 attrib_table[attrib_table_size].VertexStart = min_vertex;
1146 attrib_table[attrib_table_size].VertexCount = max_vertex - min_vertex + 1;
1147 attrib_table_size++;
1148 attrib_table[attrib_table_size].AttribId = attrib_buffer[i];
1149 attrib_table[attrib_table_size].FaceStart = i;
1150 min_vertex = (DWORD)-1;
1153 for (j = 0; j < 3; j++) {
1154 DWORD vertex_index = is_32bit_indices ? ((DWORD*)indices)[i * 3 + j] : ((WORD*)indices)[i * 3 + j];
1155 if (vertex_index < min_vertex)
1156 min_vertex = vertex_index;
1157 if (vertex_index > max_vertex)
1158 max_vertex = vertex_index;
1161 attrib_table[attrib_table_size].FaceCount = i - attrib_table[attrib_table_size].FaceStart;
1162 attrib_table[attrib_table_size].VertexStart = min_vertex;
1163 attrib_table[attrib_table_size].VertexCount = max_vertex - min_vertex + 1;
1164 attrib_table_size++;
1167 static int attrib_entry_compare(const DWORD **a, const DWORD **b)
1169 const DWORD *ptr_a = *a;
1170 const DWORD *ptr_b = *b;
1171 int delta = *ptr_a - *ptr_b;
1176 delta = ptr_a - ptr_b; /* for stable sort */
1180 /* Create face_remap, a new attribute buffer for attribute sort optimization. */
1181 static HRESULT remap_faces_for_attrsort(ID3DXMeshImpl *This, const DWORD *indices,
1182 const DWORD *attrib_buffer, DWORD **sorted_attrib_buffer, DWORD **face_remap)
1184 const DWORD **sorted_attrib_ptr_buffer = NULL;
1187 *face_remap = HeapAlloc(GetProcessHeap(), 0, This->numfaces * sizeof(**face_remap));
1188 sorted_attrib_ptr_buffer = HeapAlloc(GetProcessHeap(), 0, This->numfaces * sizeof(*sorted_attrib_ptr_buffer));
1189 if (!*face_remap || !sorted_attrib_ptr_buffer) {
1190 HeapFree(GetProcessHeap(), 0, sorted_attrib_ptr_buffer);
1191 return E_OUTOFMEMORY;
1193 for (i = 0; i < This->numfaces; i++)
1194 sorted_attrib_ptr_buffer[i] = &attrib_buffer[i];
1195 qsort(sorted_attrib_ptr_buffer, This->numfaces, sizeof(*sorted_attrib_ptr_buffer),
1196 (int(*)(const void *, const void *))attrib_entry_compare);
1198 for (i = 0; i < This->numfaces; i++)
1200 DWORD old_face = sorted_attrib_ptr_buffer[i] - attrib_buffer;
1201 (*face_remap)[old_face] = i;
1204 /* overwrite sorted_attrib_ptr_buffer with the values themselves */
1205 *sorted_attrib_buffer = (DWORD*)sorted_attrib_ptr_buffer;
1206 for (i = 0; i < This->numfaces; i++)
1207 (*sorted_attrib_buffer)[(*face_remap)[i]] = attrib_buffer[i];
1212 static HRESULT WINAPI ID3DXMeshImpl_OptimizeInplace(ID3DXMesh *iface, DWORD flags, CONST DWORD *adjacency_in, DWORD *adjacency_out,
1213 DWORD *face_remap_out, LPD3DXBUFFER *vertex_remap_out)
1215 ID3DXMeshImpl *This = impl_from_ID3DXMesh(iface);
1216 void *indices = NULL;
1217 DWORD *attrib_buffer = NULL;
1219 ID3DXBuffer *vertex_remap = NULL;
1220 DWORD *face_remap = NULL; /* old -> new mapping */
1221 DWORD *dword_indices = NULL;
1222 DWORD new_num_vertices = 0;
1223 DWORD new_num_alloc_vertices = 0;
1224 IDirect3DVertexBuffer9 *vertex_buffer = NULL;
1225 DWORD *sorted_attrib_buffer = NULL;
1228 TRACE("(%p)->(%x,%p,%p,%p,%p)\n", This, flags, adjacency_in, adjacency_out, face_remap_out, vertex_remap_out);
1231 return D3DERR_INVALIDCALL;
1232 if (!adjacency_in && (flags & (D3DXMESHOPT_VERTEXCACHE | D3DXMESHOPT_STRIPREORDER)))
1233 return D3DERR_INVALIDCALL;
1234 if ((flags & (D3DXMESHOPT_VERTEXCACHE | D3DXMESHOPT_STRIPREORDER)) == (D3DXMESHOPT_VERTEXCACHE | D3DXMESHOPT_STRIPREORDER))
1235 return D3DERR_INVALIDCALL;
1237 if (flags & (D3DXMESHOPT_VERTEXCACHE | D3DXMESHOPT_STRIPREORDER))
1239 if (flags & D3DXMESHOPT_VERTEXCACHE)
1240 FIXME("D3DXMESHOPT_VERTEXCACHE not implemented.\n");
1241 if (flags & D3DXMESHOPT_STRIPREORDER)
1242 FIXME("D3DXMESHOPT_STRIPREORDER not implemented.\n");
1246 hr = iface->lpVtbl->LockIndexBuffer(iface, 0, (void**)&indices);
1247 if (FAILED(hr)) goto cleanup;
1249 dword_indices = HeapAlloc(GetProcessHeap(), 0, This->numfaces * 3 * sizeof(DWORD));
1250 if (!dword_indices) return E_OUTOFMEMORY;
1251 if (This->options & D3DXMESH_32BIT) {
1252 memcpy(dword_indices, indices, This->numfaces * 3 * sizeof(DWORD));
1254 WORD *word_indices = indices;
1255 for (i = 0; i < This->numfaces * 3; i++)
1256 dword_indices[i] = *word_indices++;
1259 if ((flags & (D3DXMESHOPT_COMPACT | D3DXMESHOPT_IGNOREVERTS | D3DXMESHOPT_ATTRSORT)) == D3DXMESHOPT_COMPACT)
1261 new_num_alloc_vertices = This->numvertices;
1262 hr = compact_mesh(This, dword_indices, &new_num_vertices, &vertex_remap);
1263 if (FAILED(hr)) goto cleanup;
1264 } else if (flags & D3DXMESHOPT_ATTRSORT) {
1265 if (!(flags & D3DXMESHOPT_IGNOREVERTS))
1267 FIXME("D3DXMESHOPT_ATTRSORT vertex reordering not implemented.\n");
1272 hr = iface->lpVtbl->LockAttributeBuffer(iface, 0, &attrib_buffer);
1273 if (FAILED(hr)) goto cleanup;
1275 hr = remap_faces_for_attrsort(This, dword_indices, attrib_buffer, &sorted_attrib_buffer, &face_remap);
1276 if (FAILED(hr)) goto cleanup;
1281 /* reorder the vertices using vertex_remap */
1282 D3DVERTEXBUFFER_DESC vertex_desc;
1283 DWORD *vertex_remap_ptr = ID3DXBuffer_GetBufferPointer(vertex_remap);
1284 DWORD vertex_size = iface->lpVtbl->GetNumBytesPerVertex(iface);
1285 BYTE *orig_vertices;
1288 hr = IDirect3DVertexBuffer9_GetDesc(This->vertex_buffer, &vertex_desc);
1289 if (FAILED(hr)) goto cleanup;
1291 hr = IDirect3DDevice9_CreateVertexBuffer(This->device, new_num_alloc_vertices * vertex_size,
1292 vertex_desc.Usage, This->fvf, vertex_desc.Pool, &vertex_buffer, NULL);
1293 if (FAILED(hr)) goto cleanup;
1295 hr = IDirect3DVertexBuffer9_Lock(This->vertex_buffer, 0, 0, (void**)&orig_vertices, D3DLOCK_READONLY);
1296 if (FAILED(hr)) goto cleanup;
1298 hr = IDirect3DVertexBuffer9_Lock(vertex_buffer, 0, 0, (void**)&new_vertices, 0);
1300 IDirect3DVertexBuffer9_Unlock(This->vertex_buffer);
1304 for (i = 0; i < new_num_vertices; i++)
1305 memcpy(new_vertices + i * vertex_size, orig_vertices + vertex_remap_ptr[i] * vertex_size, vertex_size);
1307 IDirect3DVertexBuffer9_Unlock(This->vertex_buffer);
1308 IDirect3DVertexBuffer9_Unlock(vertex_buffer);
1309 } else if (vertex_remap_out) {
1310 DWORD *vertex_remap_ptr;
1312 hr = D3DXCreateBuffer(This->numvertices * sizeof(DWORD), &vertex_remap);
1313 if (FAILED(hr)) goto cleanup;
1314 vertex_remap_ptr = ID3DXBuffer_GetBufferPointer(vertex_remap);
1315 for (i = 0; i < This->numvertices; i++)
1316 *vertex_remap_ptr++ = i;
1319 if (flags & D3DXMESHOPT_ATTRSORT)
1321 D3DXATTRIBUTERANGE *attrib_table;
1322 DWORD attrib_table_size;
1324 attrib_table_size = count_attributes(sorted_attrib_buffer, This->numfaces);
1325 attrib_table = HeapAlloc(GetProcessHeap(), 0, attrib_table_size * sizeof(*attrib_table));
1326 if (!attrib_table) {
1331 memcpy(attrib_buffer, sorted_attrib_buffer, This->numfaces * sizeof(*attrib_buffer));
1333 /* reorder the indices using face_remap */
1334 if (This->options & D3DXMESH_32BIT) {
1335 for (i = 0; i < This->numfaces; i++)
1336 memcpy((DWORD*)indices + face_remap[i] * 3, dword_indices + i * 3, 3 * sizeof(DWORD));
1338 WORD *word_indices = indices;
1339 for (i = 0; i < This->numfaces; i++) {
1340 DWORD new_pos = face_remap[i] * 3;
1341 DWORD old_pos = i * 3;
1342 word_indices[new_pos++] = dword_indices[old_pos++];
1343 word_indices[new_pos++] = dword_indices[old_pos++];
1344 word_indices[new_pos] = dword_indices[old_pos];
1348 fill_attribute_table(attrib_buffer, This->numfaces, indices,
1349 This->options & D3DXMESH_32BIT, attrib_table);
1351 HeapFree(GetProcessHeap(), 0, This->attrib_table);
1352 This->attrib_table = attrib_table;
1353 This->attrib_table_size = attrib_table_size;
1355 if (This->options & D3DXMESH_32BIT) {
1356 memcpy(indices, dword_indices, This->numfaces * 3 * sizeof(DWORD));
1358 WORD *word_indices = indices;
1359 for (i = 0; i < This->numfaces * 3; i++)
1360 *word_indices++ = dword_indices[i];
1364 if (adjacency_out) {
1366 for (i = 0; i < This->numfaces; i++) {
1367 DWORD old_pos = i * 3;
1368 DWORD new_pos = face_remap[i] * 3;
1369 adjacency_out[new_pos++] = face_remap[adjacency_in[old_pos++]];
1370 adjacency_out[new_pos++] = face_remap[adjacency_in[old_pos++]];
1371 adjacency_out[new_pos++] = face_remap[adjacency_in[old_pos++]];
1374 memcpy(adjacency_out, adjacency_in, This->numfaces * 3 * sizeof(*adjacency_out));
1377 if (face_remap_out) {
1379 for (i = 0; i < This->numfaces; i++)
1380 face_remap_out[face_remap[i]] = i;
1382 for (i = 0; i < This->numfaces; i++)
1383 face_remap_out[i] = i;
1386 if (vertex_remap_out)
1387 *vertex_remap_out = vertex_remap;
1388 vertex_remap = NULL;
1390 if (vertex_buffer) {
1391 IDirect3DVertexBuffer9_Release(This->vertex_buffer);
1392 This->vertex_buffer = vertex_buffer;
1393 vertex_buffer = NULL;
1394 This->numvertices = new_num_vertices;
1399 HeapFree(GetProcessHeap(), 0, sorted_attrib_buffer);
1400 HeapFree(GetProcessHeap(), 0, face_remap);
1401 HeapFree(GetProcessHeap(), 0, dword_indices);
1402 if (vertex_remap) ID3DXBuffer_Release(vertex_remap);
1403 if (vertex_buffer) IDirect3DVertexBuffer9_Release(vertex_buffer);
1404 if (attrib_buffer) iface->lpVtbl->UnlockAttributeBuffer(iface);
1405 if (indices) iface->lpVtbl->UnlockIndexBuffer(iface);
1409 static HRESULT WINAPI ID3DXMeshImpl_SetAttributeTable(ID3DXMesh *iface, CONST D3DXATTRIBUTERANGE *attrib_table, DWORD attrib_table_size)
1411 ID3DXMeshImpl *This = impl_from_ID3DXMesh(iface);
1412 D3DXATTRIBUTERANGE *new_table = NULL;
1414 TRACE("(%p)->(%p,%u)\n", This, attrib_table, attrib_table_size);
1416 if (attrib_table_size) {
1417 size_t size = attrib_table_size * sizeof(*attrib_table);
1419 new_table = HeapAlloc(GetProcessHeap(), 0, size);
1421 return E_OUTOFMEMORY;
1423 CopyMemory(new_table, attrib_table, size);
1424 } else if (attrib_table) {
1425 return D3DERR_INVALIDCALL;
1427 HeapFree(GetProcessHeap(), 0, This->attrib_table);
1428 This->attrib_table = new_table;
1429 This->attrib_table_size = attrib_table_size;
1434 static const struct ID3DXMeshVtbl D3DXMesh_Vtbl =
1436 /*** IUnknown methods ***/
1437 ID3DXMeshImpl_QueryInterface,
1438 ID3DXMeshImpl_AddRef,
1439 ID3DXMeshImpl_Release,
1440 /*** ID3DXBaseMesh ***/
1441 ID3DXMeshImpl_DrawSubset,
1442 ID3DXMeshImpl_GetNumFaces,
1443 ID3DXMeshImpl_GetNumVertices,
1444 ID3DXMeshImpl_GetFVF,
1445 ID3DXMeshImpl_GetDeclaration,
1446 ID3DXMeshImpl_GetNumBytesPerVertex,
1447 ID3DXMeshImpl_GetOptions,
1448 ID3DXMeshImpl_GetDevice,
1449 ID3DXMeshImpl_CloneMeshFVF,
1450 ID3DXMeshImpl_CloneMesh,
1451 ID3DXMeshImpl_GetVertexBuffer,
1452 ID3DXMeshImpl_GetIndexBuffer,
1453 ID3DXMeshImpl_LockVertexBuffer,
1454 ID3DXMeshImpl_UnlockVertexBuffer,
1455 ID3DXMeshImpl_LockIndexBuffer,
1456 ID3DXMeshImpl_UnlockIndexBuffer,
1457 ID3DXMeshImpl_GetAttributeTable,
1458 ID3DXMeshImpl_ConvertPointRepsToAdjacency,
1459 ID3DXMeshImpl_ConvertAdjacencyToPointReps,
1460 ID3DXMeshImpl_GenerateAdjacency,
1461 ID3DXMeshImpl_UpdateSemantics,
1463 ID3DXMeshImpl_LockAttributeBuffer,
1464 ID3DXMeshImpl_UnlockAttributeBuffer,
1465 ID3DXMeshImpl_Optimize,
1466 ID3DXMeshImpl_OptimizeInplace,
1467 ID3DXMeshImpl_SetAttributeTable
1470 /*************************************************************************
1473 BOOL WINAPI D3DXBoxBoundProbe(CONST D3DXVECTOR3 *pmin, CONST D3DXVECTOR3 *pmax, CONST D3DXVECTOR3 *prayposition, CONST D3DXVECTOR3 *praydirection)
1475 /* Algorithm taken from the article: An Efficient and Robust Ray-Box Intersection Algoritm
1476 Amy Williams University of Utah
1477 Steve Barrus University of Utah
1478 R. Keith Morley University of Utah
1479 Peter Shirley University of Utah
1481 International Conference on Computer Graphics and Interactive Techniques archive
1482 ACM SIGGRAPH 2005 Courses
1483 Los Angeles, California
1485 This algorithm is free of patents or of copyrights, as confirmed by Peter Shirley himself.
1487 Algorithm: Consider the box as the intersection of three slabs. Clip the ray
1488 against each slab, if there's anything left of the ray after we're
1489 done we've got an intersection of the ray with the box.
1493 FLOAT div, tmin, tmax, tymin, tymax, tzmin, tzmax;
1495 div = 1.0f / praydirection->x;
1498 tmin = ( pmin->x - prayposition->x ) * div;
1499 tmax = ( pmax->x - prayposition->x ) * div;
1503 tmin = ( pmax->x - prayposition->x ) * div;
1504 tmax = ( pmin->x - prayposition->x ) * div;
1507 if ( tmax < 0.0f ) return FALSE;
1509 div = 1.0f / praydirection->y;
1512 tymin = ( pmin->y - prayposition->y ) * div;
1513 tymax = ( pmax->y - prayposition->y ) * div;
1517 tymin = ( pmax->y - prayposition->y ) * div;
1518 tymax = ( pmin->y - prayposition->y ) * div;
1521 if ( ( tymax < 0.0f ) || ( tmin > tymax ) || ( tymin > tmax ) ) return FALSE;
1523 if ( tymin > tmin ) tmin = tymin;
1524 if ( tymax < tmax ) tmax = tymax;
1526 div = 1.0f / praydirection->z;
1529 tzmin = ( pmin->z - prayposition->z ) * div;
1530 tzmax = ( pmax->z - prayposition->z ) * div;
1534 tzmin = ( pmax->z - prayposition->z ) * div;
1535 tzmax = ( pmin->z - prayposition->z ) * div;
1538 if ( (tzmax < 0.0f ) || ( tmin > tzmax ) || ( tzmin > tmax ) ) return FALSE;
1543 /*************************************************************************
1544 * D3DXComputeBoundingBox
1546 HRESULT WINAPI D3DXComputeBoundingBox(CONST D3DXVECTOR3 *pfirstposition, DWORD numvertices, DWORD dwstride, D3DXVECTOR3 *pmin, D3DXVECTOR3 *pmax)
1551 if( !pfirstposition || !pmin || !pmax ) return D3DERR_INVALIDCALL;
1553 *pmin = *pfirstposition;
1556 for(i=0; i<numvertices; i++)
1558 vec = *( (const D3DXVECTOR3*)((const char*)pfirstposition + dwstride * i) );
1560 if ( vec.x < pmin->x ) pmin->x = vec.x;
1561 if ( vec.x > pmax->x ) pmax->x = vec.x;
1563 if ( vec.y < pmin->y ) pmin->y = vec.y;
1564 if ( vec.y > pmax->y ) pmax->y = vec.y;
1566 if ( vec.z < pmin->z ) pmin->z = vec.z;
1567 if ( vec.z > pmax->z ) pmax->z = vec.z;
1573 /*************************************************************************
1574 * D3DXComputeBoundingSphere
1576 HRESULT WINAPI D3DXComputeBoundingSphere(CONST D3DXVECTOR3* pfirstposition, DWORD numvertices, DWORD dwstride, D3DXVECTOR3 *pcenter, FLOAT *pradius)
1578 D3DXVECTOR3 temp, temp1;
1582 if( !pfirstposition || !pcenter || !pradius ) return D3DERR_INVALIDCALL;
1590 for(i=0; i<numvertices; i++)
1592 D3DXVec3Add(&temp1, &temp, (const D3DXVECTOR3*)((const char*)pfirstposition + dwstride * i));
1596 D3DXVec3Scale(pcenter, &temp, 1.0f/((FLOAT)numvertices));
1598 for(i=0; i<numvertices; i++)
1600 d = D3DXVec3Length(D3DXVec3Subtract(&temp, (const D3DXVECTOR3*)((const char*)pfirstposition + dwstride * i), pcenter));
1601 if ( d > *pradius ) *pradius = d;
1606 static const UINT d3dx_decltype_size[D3DDECLTYPE_UNUSED] =
1608 /* D3DDECLTYPE_FLOAT1 */ 1 * 4,
1609 /* D3DDECLTYPE_FLOAT2 */ 2 * 4,
1610 /* D3DDECLTYPE_FLOAT3 */ 3 * 4,
1611 /* D3DDECLTYPE_FLOAT4 */ 4 * 4,
1612 /* D3DDECLTYPE_D3DCOLOR */ 4 * 1,
1613 /* D3DDECLTYPE_UBYTE4 */ 4 * 1,
1614 /* D3DDECLTYPE_SHORT2 */ 2 * 2,
1615 /* D3DDECLTYPE_SHORT4 */ 4 * 2,
1616 /* D3DDECLTYPE_UBYTE4N */ 4 * 1,
1617 /* D3DDECLTYPE_SHORT2N */ 2 * 2,
1618 /* D3DDECLTYPE_SHORT4N */ 4 * 2,
1619 /* D3DDECLTYPE_USHORT2N */ 2 * 2,
1620 /* D3DDECLTYPE_USHORT4N */ 4 * 2,
1621 /* D3DDECLTYPE_UDEC3 */ 4, /* 3 * 10 bits + 2 padding */
1622 /* D3DDECLTYPE_DEC3N */ 4,
1623 /* D3DDECLTYPE_FLOAT16_2 */ 2 * 2,
1624 /* D3DDECLTYPE_FLOAT16_4 */ 4 * 2,
1627 static void append_decl_element(D3DVERTEXELEMENT9 *declaration, UINT *idx, UINT *offset,
1628 D3DDECLTYPE type, D3DDECLUSAGE usage, UINT usage_idx)
1630 declaration[*idx].Stream = 0;
1631 declaration[*idx].Offset = *offset;
1632 declaration[*idx].Type = type;
1633 declaration[*idx].Method = D3DDECLMETHOD_DEFAULT;
1634 declaration[*idx].Usage = usage;
1635 declaration[*idx].UsageIndex = usage_idx;
1637 *offset += d3dx_decltype_size[type];
1641 /*************************************************************************
1642 * D3DXDeclaratorFromFVF
1644 HRESULT WINAPI D3DXDeclaratorFromFVF(DWORD fvf, D3DVERTEXELEMENT9 declaration[MAX_FVF_DECL_SIZE])
1646 static const D3DVERTEXELEMENT9 end_element = D3DDECL_END();
1647 DWORD tex_count = (fvf & D3DFVF_TEXCOUNT_MASK) >> D3DFVF_TEXCOUNT_SHIFT;
1648 unsigned int offset = 0;
1649 unsigned int idx = 0;
1652 TRACE("fvf %#x, declaration %p.\n", fvf, declaration);
1654 if (fvf & (D3DFVF_RESERVED0 | D3DFVF_RESERVED2)) return D3DERR_INVALIDCALL;
1656 if (fvf & D3DFVF_POSITION_MASK)
1658 BOOL has_blend = (fvf & D3DFVF_XYZB5) >= D3DFVF_XYZB1;
1659 DWORD blend_count = 1 + (((fvf & D3DFVF_XYZB5) - D3DFVF_XYZB1) >> 1);
1660 BOOL has_blend_idx = (fvf & D3DFVF_LASTBETA_D3DCOLOR) || (fvf & D3DFVF_LASTBETA_UBYTE4);
1662 if (has_blend_idx) --blend_count;
1664 if ((fvf & D3DFVF_POSITION_MASK) == D3DFVF_XYZW
1665 || (has_blend && blend_count > 4))
1666 return D3DERR_INVALIDCALL;
1668 if ((fvf & D3DFVF_POSITION_MASK) == D3DFVF_XYZRHW)
1669 append_decl_element(declaration, &idx, &offset, D3DDECLTYPE_FLOAT4, D3DDECLUSAGE_POSITIONT, 0);
1671 append_decl_element(declaration, &idx, &offset, D3DDECLTYPE_FLOAT3, D3DDECLUSAGE_POSITION, 0);
1675 switch (blend_count)
1680 append_decl_element(declaration, &idx, &offset, D3DDECLTYPE_FLOAT1, D3DDECLUSAGE_BLENDWEIGHT, 0);
1683 append_decl_element(declaration, &idx, &offset, D3DDECLTYPE_FLOAT2, D3DDECLUSAGE_BLENDWEIGHT, 0);
1686 append_decl_element(declaration, &idx, &offset, D3DDECLTYPE_FLOAT3, D3DDECLUSAGE_BLENDWEIGHT, 0);
1689 append_decl_element(declaration, &idx, &offset, D3DDECLTYPE_FLOAT4, D3DDECLUSAGE_BLENDWEIGHT, 0);
1692 ERR("Invalid blend count %u.\n", blend_count);
1698 if (fvf & D3DFVF_LASTBETA_UBYTE4)
1699 append_decl_element(declaration, &idx, &offset, D3DDECLTYPE_UBYTE4, D3DDECLUSAGE_BLENDINDICES, 0);
1700 else if (fvf & D3DFVF_LASTBETA_D3DCOLOR)
1701 append_decl_element(declaration, &idx, &offset, D3DDECLTYPE_D3DCOLOR, D3DDECLUSAGE_BLENDINDICES, 0);
1706 if (fvf & D3DFVF_NORMAL)
1707 append_decl_element(declaration, &idx, &offset, D3DDECLTYPE_FLOAT3, D3DDECLUSAGE_NORMAL, 0);
1708 if (fvf & D3DFVF_PSIZE)
1709 append_decl_element(declaration, &idx, &offset, D3DDECLTYPE_FLOAT1, D3DDECLUSAGE_PSIZE, 0);
1710 if (fvf & D3DFVF_DIFFUSE)
1711 append_decl_element(declaration, &idx, &offset, D3DDECLTYPE_D3DCOLOR, D3DDECLUSAGE_COLOR, 0);
1712 if (fvf & D3DFVF_SPECULAR)
1713 append_decl_element(declaration, &idx, &offset, D3DDECLTYPE_D3DCOLOR, D3DDECLUSAGE_COLOR, 1);
1715 for (i = 0; i < tex_count; ++i)
1717 switch ((fvf >> (16 + 2 * i)) & 0x03)
1719 case D3DFVF_TEXTUREFORMAT1:
1720 append_decl_element(declaration, &idx, &offset, D3DDECLTYPE_FLOAT1, D3DDECLUSAGE_TEXCOORD, i);
1722 case D3DFVF_TEXTUREFORMAT2:
1723 append_decl_element(declaration, &idx, &offset, D3DDECLTYPE_FLOAT2, D3DDECLUSAGE_TEXCOORD, i);
1725 case D3DFVF_TEXTUREFORMAT3:
1726 append_decl_element(declaration, &idx, &offset, D3DDECLTYPE_FLOAT3, D3DDECLUSAGE_TEXCOORD, i);
1728 case D3DFVF_TEXTUREFORMAT4:
1729 append_decl_element(declaration, &idx, &offset, D3DDECLTYPE_FLOAT4, D3DDECLUSAGE_TEXCOORD, i);
1734 declaration[idx] = end_element;
1739 /*************************************************************************
1740 * D3DXFVFFromDeclarator
1742 HRESULT WINAPI D3DXFVFFromDeclarator(const D3DVERTEXELEMENT9 *declaration, DWORD *fvf)
1744 unsigned int i = 0, texture, offset;
1746 TRACE("(%p, %p)\n", declaration, fvf);
1749 if (declaration[0].Type == D3DDECLTYPE_FLOAT3 && declaration[0].Usage == D3DDECLUSAGE_POSITION)
1751 if ((declaration[1].Type == D3DDECLTYPE_FLOAT4 && declaration[1].Usage == D3DDECLUSAGE_BLENDWEIGHT &&
1752 declaration[1].UsageIndex == 0) &&
1753 (declaration[2].Type == D3DDECLTYPE_FLOAT1 && declaration[2].Usage == D3DDECLUSAGE_BLENDINDICES &&
1754 declaration[2].UsageIndex == 0))
1756 return D3DERR_INVALIDCALL;
1758 else if ((declaration[1].Type == D3DDECLTYPE_UBYTE4 || declaration[1].Type == D3DDECLTYPE_D3DCOLOR) &&
1759 declaration[1].Usage == D3DDECLUSAGE_BLENDINDICES && declaration[1].UsageIndex == 0)
1761 if (declaration[1].Type == D3DDECLTYPE_UBYTE4)
1763 *fvf |= D3DFVF_XYZB1 | D3DFVF_LASTBETA_UBYTE4;
1767 *fvf |= D3DFVF_XYZB1 | D3DFVF_LASTBETA_D3DCOLOR;
1771 else if (declaration[1].Type <= D3DDECLTYPE_FLOAT4 && declaration[1].Usage == D3DDECLUSAGE_BLENDWEIGHT &&
1772 declaration[1].UsageIndex == 0)
1774 if ((declaration[2].Type == D3DDECLTYPE_UBYTE4 || declaration[2].Type == D3DDECLTYPE_D3DCOLOR) &&
1775 declaration[2].Usage == D3DDECLUSAGE_BLENDINDICES && declaration[2].UsageIndex == 0)
1777 if (declaration[2].Type == D3DDECLTYPE_UBYTE4)
1779 *fvf |= D3DFVF_LASTBETA_UBYTE4;
1783 *fvf |= D3DFVF_LASTBETA_D3DCOLOR;
1785 switch (declaration[1].Type)
1787 case D3DDECLTYPE_FLOAT1: *fvf |= D3DFVF_XYZB2; break;
1788 case D3DDECLTYPE_FLOAT2: *fvf |= D3DFVF_XYZB3; break;
1789 case D3DDECLTYPE_FLOAT3: *fvf |= D3DFVF_XYZB4; break;
1790 case D3DDECLTYPE_FLOAT4: *fvf |= D3DFVF_XYZB5; break;
1796 switch (declaration[1].Type)
1798 case D3DDECLTYPE_FLOAT1: *fvf |= D3DFVF_XYZB1; break;
1799 case D3DDECLTYPE_FLOAT2: *fvf |= D3DFVF_XYZB2; break;
1800 case D3DDECLTYPE_FLOAT3: *fvf |= D3DFVF_XYZB3; break;
1801 case D3DDECLTYPE_FLOAT4: *fvf |= D3DFVF_XYZB4; break;
1812 else if (declaration[0].Type == D3DDECLTYPE_FLOAT4 && declaration[0].Usage == D3DDECLUSAGE_POSITIONT &&
1813 declaration[0].UsageIndex == 0)
1815 *fvf |= D3DFVF_XYZRHW;
1819 if (declaration[i].Type == D3DDECLTYPE_FLOAT3 && declaration[i].Usage == D3DDECLUSAGE_NORMAL)
1821 *fvf |= D3DFVF_NORMAL;
1824 if (declaration[i].Type == D3DDECLTYPE_FLOAT1 && declaration[i].Usage == D3DDECLUSAGE_PSIZE &&
1825 declaration[i].UsageIndex == 0)
1827 *fvf |= D3DFVF_PSIZE;
1830 if (declaration[i].Type == D3DDECLTYPE_D3DCOLOR && declaration[i].Usage == D3DDECLUSAGE_COLOR &&
1831 declaration[i].UsageIndex == 0)
1833 *fvf |= D3DFVF_DIFFUSE;
1836 if (declaration[i].Type == D3DDECLTYPE_D3DCOLOR && declaration[i].Usage == D3DDECLUSAGE_COLOR &&
1837 declaration[i].UsageIndex == 1)
1839 *fvf |= D3DFVF_SPECULAR;
1843 for (texture = 0; texture < D3DDP_MAXTEXCOORD; i++, texture++)
1845 if (declaration[i].Stream == 0xFF)
1849 else if (declaration[i].Type == D3DDECLTYPE_FLOAT1 && declaration[i].Usage == D3DDECLUSAGE_TEXCOORD &&
1850 declaration[i].UsageIndex == texture)
1852 *fvf |= D3DFVF_TEXCOORDSIZE1(declaration[i].UsageIndex);
1854 else if (declaration[i].Type == D3DDECLTYPE_FLOAT2 && declaration[i].Usage == D3DDECLUSAGE_TEXCOORD &&
1855 declaration[i].UsageIndex == texture)
1857 *fvf |= D3DFVF_TEXCOORDSIZE2(declaration[i].UsageIndex);
1859 else if (declaration[i].Type == D3DDECLTYPE_FLOAT3 && declaration[i].Usage == D3DDECLUSAGE_TEXCOORD &&
1860 declaration[i].UsageIndex == texture)
1862 *fvf |= D3DFVF_TEXCOORDSIZE3(declaration[i].UsageIndex);
1864 else if (declaration[i].Type == D3DDECLTYPE_FLOAT4 && declaration[i].Usage == D3DDECLUSAGE_TEXCOORD &&
1865 declaration[i].UsageIndex == texture)
1867 *fvf |= D3DFVF_TEXCOORDSIZE4(declaration[i].UsageIndex);
1871 return D3DERR_INVALIDCALL;
1875 *fvf |= (texture << D3DFVF_TEXCOUNT_SHIFT);
1877 for (offset = 0, i = 0; declaration[i].Stream != 0xFF;
1878 offset += d3dx_decltype_size[declaration[i].Type], i++)
1880 if (declaration[i].Offset != offset)
1882 return D3DERR_INVALIDCALL;
1889 /*************************************************************************
1890 * D3DXGetFVFVertexSize
1892 static UINT Get_TexCoord_Size_From_FVF(DWORD FVF, int tex_num)
1894 return (((((FVF) >> (16 + (2 * (tex_num)))) + 1) & 0x03) + 1);
1897 UINT WINAPI D3DXGetFVFVertexSize(DWORD FVF)
1901 UINT numTextures = (FVF & D3DFVF_TEXCOUNT_MASK) >> D3DFVF_TEXCOUNT_SHIFT;
1903 if (FVF & D3DFVF_NORMAL) size += sizeof(D3DXVECTOR3);
1904 if (FVF & D3DFVF_DIFFUSE) size += sizeof(DWORD);
1905 if (FVF & D3DFVF_SPECULAR) size += sizeof(DWORD);
1906 if (FVF & D3DFVF_PSIZE) size += sizeof(DWORD);
1908 switch (FVF & D3DFVF_POSITION_MASK)
1910 case D3DFVF_XYZ: size += sizeof(D3DXVECTOR3); break;
1911 case D3DFVF_XYZRHW: size += 4 * sizeof(FLOAT); break;
1912 case D3DFVF_XYZB1: size += 4 * sizeof(FLOAT); break;
1913 case D3DFVF_XYZB2: size += 5 * sizeof(FLOAT); break;
1914 case D3DFVF_XYZB3: size += 6 * sizeof(FLOAT); break;
1915 case D3DFVF_XYZB4: size += 7 * sizeof(FLOAT); break;
1916 case D3DFVF_XYZB5: size += 8 * sizeof(FLOAT); break;
1917 case D3DFVF_XYZW: size += 4 * sizeof(FLOAT); break;
1920 for (i = 0; i < numTextures; i++)
1922 size += Get_TexCoord_Size_From_FVF(FVF, i) * sizeof(FLOAT);
1928 /*************************************************************************
1929 * D3DXGetDeclVertexSize
1931 UINT WINAPI D3DXGetDeclVertexSize(const D3DVERTEXELEMENT9 *decl, DWORD stream_idx)
1933 const D3DVERTEXELEMENT9 *element;
1936 TRACE("decl %p, stream_idx %u\n", decl, stream_idx);
1938 if (!decl) return 0;
1940 for (element = decl; element->Stream != 0xff; ++element)
1944 if (element->Stream != stream_idx) continue;
1946 if (element->Type >= sizeof(d3dx_decltype_size) / sizeof(*d3dx_decltype_size))
1948 FIXME("Unhandled element type %#x, size will be incorrect.\n", element->Type);
1952 type_size = d3dx_decltype_size[element->Type];
1953 if (element->Offset + type_size > size) size = element->Offset + type_size;
1959 /*************************************************************************
1962 UINT WINAPI D3DXGetDeclLength(const D3DVERTEXELEMENT9 *decl)
1964 const D3DVERTEXELEMENT9 *element;
1966 TRACE("decl %p\n", decl);
1968 /* null decl results in exception on Windows XP */
1970 for (element = decl; element->Stream != 0xff; ++element);
1972 return element - decl;
1975 /*************************************************************************
1978 BOOL WINAPI D3DXIntersectTri(CONST D3DXVECTOR3 *p0, CONST D3DXVECTOR3 *p1, CONST D3DXVECTOR3 *p2, CONST D3DXVECTOR3 *praypos, CONST D3DXVECTOR3 *praydir, FLOAT *pu, FLOAT *pv, FLOAT *pdist)
1983 m.u.m[0][0] = p1->x - p0->x;
1984 m.u.m[1][0] = p2->x - p0->x;
1985 m.u.m[2][0] = -praydir->x;
1987 m.u.m[0][1] = p1->y - p0->z;
1988 m.u.m[1][1] = p2->y - p0->z;
1989 m.u.m[2][1] = -praydir->y;
1991 m.u.m[0][2] = p1->z - p0->z;
1992 m.u.m[1][2] = p2->z - p0->z;
1993 m.u.m[2][2] = -praydir->z;
2000 vec.x = praypos->x - p0->x;
2001 vec.y = praypos->y - p0->y;
2002 vec.z = praypos->z - p0->z;
2005 if ( D3DXMatrixInverse(&m, NULL, &m) )
2007 D3DXVec4Transform(&vec, &vec, &m);
2008 if ( (vec.x >= 0.0f) && (vec.y >= 0.0f) && (vec.x + vec.y <= 1.0f) && (vec.z >= 0.0f) )
2012 *pdist = fabs( vec.z );
2020 /*************************************************************************
2021 * D3DXSphereBoundProbe
2023 BOOL WINAPI D3DXSphereBoundProbe(CONST D3DXVECTOR3 *pcenter, FLOAT radius, CONST D3DXVECTOR3 *prayposition, CONST D3DXVECTOR3 *praydirection)
2025 D3DXVECTOR3 difference;
2028 a = D3DXVec3LengthSq(praydirection);
2029 if (!D3DXVec3Subtract(&difference, prayposition, pcenter)) return FALSE;
2030 b = D3DXVec3Dot(&difference, praydirection);
2031 c = D3DXVec3LengthSq(&difference) - radius * radius;
2034 if ( ( d <= 0.0f ) || ( sqrt(d) <= b ) ) return FALSE;
2038 /*************************************************************************
2041 HRESULT WINAPI D3DXCreateMesh(DWORD numfaces, DWORD numvertices, DWORD options, CONST D3DVERTEXELEMENT9 *declaration,
2042 LPDIRECT3DDEVICE9 device, LPD3DXMESH *mesh)
2046 IDirect3DVertexDeclaration9 *vertex_declaration;
2047 UINT vertex_declaration_size;
2049 IDirect3DVertexBuffer9 *vertex_buffer;
2050 IDirect3DIndexBuffer9 *index_buffer;
2051 DWORD *attrib_buffer;
2052 ID3DXMeshImpl *object;
2053 DWORD index_usage = 0;
2054 D3DPOOL index_pool = D3DPOOL_DEFAULT;
2055 D3DFORMAT index_format = D3DFMT_INDEX16;
2056 DWORD vertex_usage = 0;
2057 D3DPOOL vertex_pool = D3DPOOL_DEFAULT;
2060 TRACE("(%d, %d, %x, %p, %p, %p)\n", numfaces, numvertices, options, declaration, device, mesh);
2062 if (numfaces == 0 || numvertices == 0 || declaration == NULL || device == NULL || mesh == NULL ||
2063 /* D3DXMESH_VB_SHARE is for cloning, and D3DXMESH_USEHWONLY is for ConvertToBlendedMesh */
2064 (options & (D3DXMESH_VB_SHARE | D3DXMESH_USEHWONLY | 0xfffe0000)))
2066 return D3DERR_INVALIDCALL;
2068 for (i = 0; declaration[i].Stream != 0xff; i++)
2069 if (declaration[i].Stream != 0)
2070 return D3DERR_INVALIDCALL;
2073 if (options & D3DXMESH_32BIT)
2074 index_format = D3DFMT_INDEX32;
2076 if (options & D3DXMESH_DONOTCLIP) {
2077 index_usage |= D3DUSAGE_DONOTCLIP;
2078 vertex_usage |= D3DUSAGE_DONOTCLIP;
2080 if (options & D3DXMESH_POINTS) {
2081 index_usage |= D3DUSAGE_POINTS;
2082 vertex_usage |= D3DUSAGE_POINTS;
2084 if (options & D3DXMESH_RTPATCHES) {
2085 index_usage |= D3DUSAGE_RTPATCHES;
2086 vertex_usage |= D3DUSAGE_RTPATCHES;
2088 if (options & D3DXMESH_NPATCHES) {
2089 index_usage |= D3DUSAGE_NPATCHES;
2090 vertex_usage |= D3DUSAGE_NPATCHES;
2093 if (options & D3DXMESH_VB_SYSTEMMEM)
2094 vertex_pool = D3DPOOL_SYSTEMMEM;
2095 else if (options & D3DXMESH_VB_MANAGED)
2096 vertex_pool = D3DPOOL_MANAGED;
2098 if (options & D3DXMESH_VB_WRITEONLY)
2099 vertex_usage |= D3DUSAGE_WRITEONLY;
2100 if (options & D3DXMESH_VB_DYNAMIC)
2101 vertex_usage |= D3DUSAGE_DYNAMIC;
2102 if (options & D3DXMESH_VB_SOFTWAREPROCESSING)
2103 vertex_usage |= D3DUSAGE_SOFTWAREPROCESSING;
2105 if (options & D3DXMESH_IB_SYSTEMMEM)
2106 index_pool = D3DPOOL_SYSTEMMEM;
2107 else if (options & D3DXMESH_IB_MANAGED)
2108 index_pool = D3DPOOL_MANAGED;
2110 if (options & D3DXMESH_IB_WRITEONLY)
2111 index_usage |= D3DUSAGE_WRITEONLY;
2112 if (options & D3DXMESH_IB_DYNAMIC)
2113 index_usage |= D3DUSAGE_DYNAMIC;
2114 if (options & D3DXMESH_IB_SOFTWAREPROCESSING)
2115 index_usage |= D3DUSAGE_SOFTWAREPROCESSING;
2117 hr = D3DXFVFFromDeclarator(declaration, &fvf);
2123 /* Create vertex declaration */
2124 hr = IDirect3DDevice9_CreateVertexDeclaration(device,
2126 &vertex_declaration);
2129 WARN("Unexpected return value %x from IDirect3DDevice9_CreateVertexDeclaration.\n",hr);
2132 vertex_declaration_size = D3DXGetDeclVertexSize(declaration, declaration[0].Stream);
2134 /* Create vertex buffer */
2135 hr = IDirect3DDevice9_CreateVertexBuffer(device,
2136 numvertices * vertex_declaration_size,
2144 WARN("Unexpected return value %x from IDirect3DDevice9_CreateVertexBuffer.\n",hr);
2145 IDirect3DVertexDeclaration9_Release(vertex_declaration);
2149 /* Create index buffer */
2150 hr = IDirect3DDevice9_CreateIndexBuffer(device,
2151 numfaces * 3 * ((index_format == D3DFMT_INDEX16) ? 2 : 4),
2159 WARN("Unexpected return value %x from IDirect3DDevice9_CreateVertexBuffer.\n",hr);
2160 IDirect3DVertexBuffer9_Release(vertex_buffer);
2161 IDirect3DVertexDeclaration9_Release(vertex_declaration);
2165 attrib_buffer = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, numfaces * sizeof(*attrib_buffer));
2166 object = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(ID3DXMeshImpl));
2167 if (object == NULL || attrib_buffer == NULL)
2169 HeapFree(GetProcessHeap(), 0, attrib_buffer);
2170 IDirect3DIndexBuffer9_Release(index_buffer);
2171 IDirect3DVertexBuffer9_Release(vertex_buffer);
2172 IDirect3DVertexDeclaration9_Release(vertex_declaration);
2174 return E_OUTOFMEMORY;
2176 object->ID3DXMesh_iface.lpVtbl = &D3DXMesh_Vtbl;
2179 object->numfaces = numfaces;
2180 object->numvertices = numvertices;
2181 object->options = options;
2183 object->device = device;
2184 IDirect3DDevice9_AddRef(device);
2186 copy_declaration(object->cached_declaration, declaration, num_elem);
2187 object->vertex_declaration = vertex_declaration;
2188 object->vertex_declaration_size = vertex_declaration_size;
2189 object->num_elem = num_elem;
2190 object->vertex_buffer = vertex_buffer;
2191 object->index_buffer = index_buffer;
2192 object->attrib_buffer = attrib_buffer;
2194 *mesh = &object->ID3DXMesh_iface;
2199 /*************************************************************************
2202 HRESULT WINAPI D3DXCreateMeshFVF(DWORD numfaces, DWORD numvertices, DWORD options, DWORD fvf,
2203 LPDIRECT3DDEVICE9 device, LPD3DXMESH *mesh)
2206 D3DVERTEXELEMENT9 declaration[MAX_FVF_DECL_SIZE];
2208 TRACE("(%u, %u, %u, %u, %p, %p)\n", numfaces, numvertices, options, fvf, device, mesh);
2210 hr = D3DXDeclaratorFromFVF(fvf, declaration);
2211 if (FAILED(hr)) return hr;
2213 return D3DXCreateMesh(numfaces, numvertices, options, declaration, device, mesh);
2219 DWORD num_poly_faces;
2220 DWORD num_tri_faces;
2221 D3DXVECTOR3 *vertices;
2222 DWORD *num_tri_per_face;
2227 /* optional mesh data */
2230 D3DXVECTOR3 *normals;
2231 DWORD *normal_indices;
2233 D3DXVECTOR2 *tex_coords;
2235 DWORD *vertex_colors;
2237 DWORD num_materials;
2238 D3DXMATERIAL *materials;
2239 DWORD *material_indices;
2242 static HRESULT get_next_child(IDirectXFileData *filedata, IDirectXFileData **child, const GUID **type)
2245 IDirectXFileDataReference *child_ref = NULL;
2246 IDirectXFileObject *child_obj = NULL;
2247 IDirectXFileData *child_data = NULL;
2249 hr = IDirectXFileData_GetNextObject(filedata, &child_obj);
2250 if (FAILED(hr)) return hr;
2252 hr = IDirectXFileObject_QueryInterface(child_obj, &IID_IDirectXFileDataReference, (void**)&child_ref);
2253 if (SUCCEEDED(hr)) {
2254 hr = IDirectXFileDataReference_Resolve(child_ref, &child_data);
2255 IDirectXFileDataReference_Release(child_ref);
2257 hr = IDirectXFileObject_QueryInterface(child_obj, &IID_IDirectXFileData, (void**)&child_data);
2259 IDirectXFileObject_Release(child_obj);
2263 hr = IDirectXFileData_GetType(child_data, type);
2265 IDirectXFileData_Release(child_data);
2267 *child = child_data;
2273 static HRESULT parse_texture_filename(IDirectXFileData *filedata, LPSTR *filename_out)
2279 char *filename = NULL;
2281 /* template TextureFilename {
2286 HeapFree(GetProcessHeap(), 0, *filename_out);
2287 *filename_out = NULL;
2289 hr = IDirectXFileData_GetData(filedata, NULL, &data_size, (void**)&data);
2290 if (FAILED(hr)) return hr;
2292 if (data_size < sizeof(LPSTR)) {
2293 WARN("truncated data (%u bytes)\n", data_size);
2296 filename_in = *(LPSTR*)data;
2298 filename = HeapAlloc(GetProcessHeap(), 0, strlen(filename_in) + 1);
2299 if (!filename) return E_OUTOFMEMORY;
2301 strcpy(filename, filename_in);
2302 *filename_out = filename;
2307 static HRESULT parse_material(IDirectXFileData *filedata, D3DXMATERIAL *material)
2313 IDirectXFileData *child;
2315 material->pTextureFilename = NULL;
2317 hr = IDirectXFileData_GetData(filedata, NULL, &data_size, (void**)&data);
2318 if (FAILED(hr)) return hr;
2321 * template ColorRGBA {
2327 * template ColorRGB {
2332 * template Material {
2333 * ColorRGBA faceColor;
2335 * ColorRGB specularColor;
2336 * ColorRGB emissiveColor;
2340 if (data_size != sizeof(FLOAT) * 11) {
2341 WARN("incorrect data size (%u bytes)\n", data_size);
2345 memcpy(&material->MatD3D.Diffuse, data, sizeof(D3DCOLORVALUE));
2346 data += sizeof(D3DCOLORVALUE);
2347 material->MatD3D.Power = *(FLOAT*)data;
2348 data += sizeof(FLOAT);
2349 memcpy(&material->MatD3D.Specular, data, sizeof(FLOAT) * 3);
2350 material->MatD3D.Specular.a = 1.0f;
2351 data += 3 * sizeof(FLOAT);
2352 memcpy(&material->MatD3D.Emissive, data, sizeof(FLOAT) * 3);
2353 material->MatD3D.Emissive.a = 1.0f;
2354 material->MatD3D.Ambient.r = 0.0f;
2355 material->MatD3D.Ambient.g = 0.0f;
2356 material->MatD3D.Ambient.b = 0.0f;
2357 material->MatD3D.Ambient.a = 1.0f;
2359 while (SUCCEEDED(hr = get_next_child(filedata, &child, &type)))
2361 if (IsEqualGUID(type, &TID_D3DRMTextureFilename)) {
2362 hr = parse_texture_filename(child, &material->pTextureFilename);
2363 if (FAILED(hr)) break;
2366 return hr == DXFILEERR_NOMOREOBJECTS ? D3D_OK : hr;
2369 static void destroy_materials(struct mesh_data *mesh)
2372 for (i = 0; i < mesh->num_materials; i++)
2373 HeapFree(GetProcessHeap(), 0, mesh->materials[i].pTextureFilename);
2374 HeapFree(GetProcessHeap(), 0, mesh->materials);
2375 HeapFree(GetProcessHeap(), 0, mesh->material_indices);
2376 mesh->num_materials = 0;
2377 mesh->materials = NULL;
2378 mesh->material_indices = NULL;
2381 static HRESULT parse_material_list(IDirectXFileData *filedata, struct mesh_data *mesh)
2385 DWORD *data, *in_ptr;
2387 IDirectXFileData *child;
2388 DWORD num_materials;
2391 destroy_materials(mesh);
2393 hr = IDirectXFileData_GetData(filedata, NULL, &data_size, (void**)&data);
2394 if (FAILED(hr)) return hr;
2396 /* template MeshMaterialList {
2398 * DWORD nFaceIndexes;
2399 * array DWORD faceIndexes[nFaceIndexes];
2406 if (data_size < sizeof(DWORD))
2407 goto truncated_data_error;
2408 num_materials = *in_ptr++;
2412 if (data_size < 2 * sizeof(DWORD))
2413 goto truncated_data_error;
2414 if (*in_ptr++ != mesh->num_poly_faces) {
2415 WARN("number of material face indices (%u) doesn't match number of faces (%u)\n",
2416 *(in_ptr - 1), mesh->num_poly_faces);
2419 if (data_size < 2 * sizeof(DWORD) + mesh->num_poly_faces * sizeof(DWORD))
2420 goto truncated_data_error;
2421 for (i = 0; i < mesh->num_poly_faces; i++) {
2422 if (*in_ptr++ >= num_materials) {
2423 WARN("face %u: reference to undefined material %u (only %u materials)\n",
2424 i, *(in_ptr - 1), num_materials);
2429 mesh->materials = HeapAlloc(GetProcessHeap(), 0, num_materials * sizeof(*mesh->materials));
2430 mesh->material_indices = HeapAlloc(GetProcessHeap(), 0, mesh->num_poly_faces * sizeof(*mesh->material_indices));
2431 if (!mesh->materials || !mesh->material_indices)
2432 return E_OUTOFMEMORY;
2433 memcpy(mesh->material_indices, data + 2, mesh->num_poly_faces * sizeof(DWORD));
2435 while (SUCCEEDED(hr = get_next_child(filedata, &child, &type)))
2437 if (IsEqualGUID(type, &TID_D3DRMMaterial)) {
2438 if (mesh->num_materials >= num_materials) {
2439 WARN("more materials defined than declared\n");
2442 hr = parse_material(child, &mesh->materials[mesh->num_materials++]);
2443 if (FAILED(hr)) break;
2446 if (hr != DXFILEERR_NOMOREOBJECTS)
2448 if (num_materials != mesh->num_materials) {
2449 WARN("only %u of %u materials defined\n", num_materials, mesh->num_materials);
2454 truncated_data_error:
2455 WARN("truncated data (%u bytes)\n", data_size);
2459 static HRESULT parse_texture_coords(IDirectXFileData *filedata, struct mesh_data *mesh)
2465 HeapFree(GetProcessHeap(), 0, mesh->tex_coords);
2466 mesh->tex_coords = NULL;
2468 hr = IDirectXFileData_GetData(filedata, NULL, &data_size, (void**)&data);
2469 if (FAILED(hr)) return hr;
2471 /* template Coords2d {
2475 * template MeshTextureCoords {
2476 * DWORD nTextureCoords;
2477 * array Coords2d textureCoords[nTextureCoords];
2481 if (data_size < sizeof(DWORD))
2482 goto truncated_data_error;
2483 if (*(DWORD*)data != mesh->num_vertices) {
2484 WARN("number of texture coordinates (%u) doesn't match number of vertices (%u)\n",
2485 *(DWORD*)data, mesh->num_vertices);
2488 data += sizeof(DWORD);
2489 if (data_size < sizeof(DWORD) + mesh->num_vertices * sizeof(*mesh->tex_coords))
2490 goto truncated_data_error;
2492 mesh->tex_coords = HeapAlloc(GetProcessHeap(), 0, mesh->num_vertices * sizeof(*mesh->tex_coords));
2493 if (!mesh->tex_coords) return E_OUTOFMEMORY;
2494 memcpy(mesh->tex_coords, data, mesh->num_vertices * sizeof(*mesh->tex_coords));
2496 mesh->fvf |= D3DFVF_TEX1;
2499 truncated_data_error:
2500 WARN("truncated data (%u bytes)\n", data_size);
2504 static HRESULT parse_vertex_colors(IDirectXFileData *filedata, struct mesh_data *mesh)
2512 HeapFree(GetProcessHeap(), 0, mesh->vertex_colors);
2513 mesh->vertex_colors = NULL;
2515 hr = IDirectXFileData_GetData(filedata, NULL, &data_size, (void**)&data);
2516 if (FAILED(hr)) return hr;
2518 /* template IndexedColor {
2520 * ColorRGBA indexColor;
2522 * template MeshVertexColors {
2523 * DWORD nVertexColors;
2524 * array IndexedColor vertexColors[nVertexColors];
2528 if (data_size < sizeof(DWORD))
2529 goto truncated_data_error;
2530 num_colors = *(DWORD*)data;
2531 data += sizeof(DWORD);
2532 if (data_size < sizeof(DWORD) + num_colors * (sizeof(DWORD) + sizeof(D3DCOLORVALUE)))
2533 goto truncated_data_error;
2535 mesh->vertex_colors = HeapAlloc(GetProcessHeap(), 0, mesh->num_vertices * sizeof(DWORD));
2536 if (!mesh->vertex_colors)
2537 return E_OUTOFMEMORY;
2539 for (i = 0; i < mesh->num_vertices; i++)
2540 mesh->vertex_colors[i] = D3DCOLOR_ARGB(0, 0xff, 0xff, 0xff);
2541 for (i = 0; i < num_colors; i++)
2543 D3DCOLORVALUE color;
2544 DWORD index = *(DWORD*)data;
2545 data += sizeof(DWORD);
2546 if (index >= mesh->num_vertices) {
2547 WARN("vertex color %u references undefined vertex %u (only %u vertices)\n",
2548 i, index, mesh->num_vertices);
2551 memcpy(&color, data, sizeof(color));
2552 data += sizeof(color);
2553 color.r = min(1.0f, max(0.0f, color.r));
2554 color.g = min(1.0f, max(0.0f, color.g));
2555 color.b = min(1.0f, max(0.0f, color.b));
2556 color.a = min(1.0f, max(0.0f, color.a));
2557 mesh->vertex_colors[index] = D3DCOLOR_ARGB((BYTE)(color.a * 255.0f + 0.5f),
2558 (BYTE)(color.r * 255.0f + 0.5f),
2559 (BYTE)(color.g * 255.0f + 0.5f),
2560 (BYTE)(color.b * 255.0f + 0.5f));
2563 mesh->fvf |= D3DFVF_DIFFUSE;
2566 truncated_data_error:
2567 WARN("truncated data (%u bytes)\n", data_size);
2571 static HRESULT parse_normals(IDirectXFileData *filedata, struct mesh_data *mesh)
2576 DWORD *index_out_ptr;
2578 DWORD num_face_indices = mesh->num_poly_faces * 2 + mesh->num_tri_faces;
2580 HeapFree(GetProcessHeap(), 0, mesh->normals);
2581 mesh->num_normals = 0;
2582 mesh->normals = NULL;
2583 mesh->normal_indices = NULL;
2584 mesh->fvf |= D3DFVF_NORMAL;
2586 hr = IDirectXFileData_GetData(filedata, NULL, &data_size, (void**)&data);
2587 if (FAILED(hr)) return hr;
2589 /* template Vector {
2594 * template MeshFace {
2595 * DWORD nFaceVertexIndices;
2596 * array DWORD faceVertexIndices[nFaceVertexIndices];
2598 * template MeshNormals {
2600 * array Vector normals[nNormals];
2601 * DWORD nFaceNormals;
2602 * array MeshFace faceNormals[nFaceNormals];
2606 if (data_size < sizeof(DWORD) * 2)
2607 goto truncated_data_error;
2608 mesh->num_normals = *(DWORD*)data;
2609 data += sizeof(DWORD);
2610 if (data_size < sizeof(DWORD) * 2 + mesh->num_normals * sizeof(D3DXVECTOR3) +
2611 num_face_indices * sizeof(DWORD))
2612 goto truncated_data_error;
2614 mesh->normals = HeapAlloc(GetProcessHeap(), 0, mesh->num_normals * sizeof(D3DXVECTOR3));
2615 mesh->normal_indices = HeapAlloc(GetProcessHeap(), 0, num_face_indices * sizeof(DWORD));
2616 if (!mesh->normals || !mesh->normal_indices)
2617 return E_OUTOFMEMORY;
2619 memcpy(mesh->normals, data, mesh->num_normals * sizeof(D3DXVECTOR3));
2620 data += mesh->num_normals * sizeof(D3DXVECTOR3);
2621 for (i = 0; i < mesh->num_normals; i++)
2622 D3DXVec3Normalize(&mesh->normals[i], &mesh->normals[i]);
2624 if (*(DWORD*)data != mesh->num_poly_faces) {
2625 WARN("number of face normals (%u) doesn't match number of faces (%u)\n",
2626 *(DWORD*)data, mesh->num_poly_faces);
2629 data += sizeof(DWORD);
2630 index_out_ptr = mesh->normal_indices;
2631 for (i = 0; i < mesh->num_poly_faces; i++)
2634 DWORD count = *(DWORD*)data;
2635 if (count != mesh->num_tri_per_face[i] + 2) {
2636 WARN("face %u: number of normals (%u) doesn't match number of vertices (%u)\n",
2637 i, count, mesh->num_tri_per_face[i] + 2);
2640 data += sizeof(DWORD);
2642 for (j = 0; j < count; j++) {
2643 DWORD normal_index = *(DWORD*)data;
2644 if (normal_index >= mesh->num_normals) {
2645 WARN("face %u, normal index %u: reference to undefined normal %u (only %u normals)\n",
2646 i, j, normal_index, mesh->num_normals);
2649 *index_out_ptr++ = normal_index;
2650 data += sizeof(DWORD);
2655 truncated_data_error:
2656 WARN("truncated data (%u bytes)\n", data_size);
2660 /* for provide_flags parameters */
2661 #define PROVIDE_MATERIALS 0x1
2662 #define PROVIDE_SKININFO 0x2
2663 #define PROVIDE_ADJACENCY 0x4
2665 static HRESULT parse_mesh(IDirectXFileData *filedata, struct mesh_data *mesh_data, DWORD provide_flags)
2669 BYTE *data, *in_ptr;
2670 DWORD *index_out_ptr;
2672 IDirectXFileData *child;
2678 * array Vector vertices[nVertices];
2680 * array MeshFace faces[nFaces];
2685 hr = IDirectXFileData_GetData(filedata, NULL, &data_size, (void**)&data);
2686 if (FAILED(hr)) return hr;
2689 if (data_size < sizeof(DWORD) * 2)
2690 goto truncated_data_error;
2691 mesh_data->num_vertices = *(DWORD*)in_ptr;
2692 if (data_size < sizeof(DWORD) * 2 + mesh_data->num_vertices * sizeof(D3DXVECTOR3))
2693 goto truncated_data_error;
2694 in_ptr += sizeof(DWORD) + mesh_data->num_vertices * sizeof(D3DXVECTOR3);
2696 mesh_data->num_poly_faces = *(DWORD*)in_ptr;
2697 in_ptr += sizeof(DWORD);
2699 mesh_data->num_tri_faces = 0;
2700 for (i = 0; i < mesh_data->num_poly_faces; i++)
2702 DWORD num_poly_vertices;
2705 if (data_size - (in_ptr - data) < sizeof(DWORD))
2706 goto truncated_data_error;
2707 num_poly_vertices = *(DWORD*)in_ptr;
2708 in_ptr += sizeof(DWORD);
2709 if (data_size - (in_ptr - data) < num_poly_vertices * sizeof(DWORD))
2710 goto truncated_data_error;
2711 if (num_poly_vertices < 3) {
2712 WARN("face %u has only %u vertices\n", i, num_poly_vertices);
2715 for (j = 0; j < num_poly_vertices; j++) {
2716 if (*(DWORD*)in_ptr >= mesh_data->num_vertices) {
2717 WARN("face %u, index %u: undefined vertex %u (only %u vertices)\n",
2718 i, j, *(DWORD*)in_ptr, mesh_data->num_vertices);
2721 in_ptr += sizeof(DWORD);
2723 mesh_data->num_tri_faces += num_poly_vertices - 2;
2726 mesh_data->fvf = D3DFVF_XYZ;
2728 mesh_data->vertices = HeapAlloc(GetProcessHeap(), 0,
2729 mesh_data->num_vertices * sizeof(*mesh_data->vertices));
2730 mesh_data->num_tri_per_face = HeapAlloc(GetProcessHeap(), 0,
2731 mesh_data->num_poly_faces * sizeof(*mesh_data->num_tri_per_face));
2732 mesh_data->indices = HeapAlloc(GetProcessHeap(), 0,
2733 (mesh_data->num_tri_faces + mesh_data->num_poly_faces * 2) * sizeof(*mesh_data->indices));
2734 if (!mesh_data->vertices || !mesh_data->num_tri_per_face || !mesh_data->indices)
2735 return E_OUTOFMEMORY;
2737 in_ptr = data + sizeof(DWORD);
2738 memcpy(mesh_data->vertices, in_ptr, mesh_data->num_vertices * sizeof(D3DXVECTOR3));
2739 in_ptr += mesh_data->num_vertices * sizeof(D3DXVECTOR3) + sizeof(DWORD);
2741 index_out_ptr = mesh_data->indices;
2742 for (i = 0; i < mesh_data->num_poly_faces; i++)
2746 count = *(DWORD*)in_ptr;
2747 in_ptr += sizeof(DWORD);
2748 mesh_data->num_tri_per_face[i] = count - 2;
2751 *index_out_ptr++ = *(DWORD*)in_ptr;
2752 in_ptr += sizeof(DWORD);
2756 while (SUCCEEDED(hr = get_next_child(filedata, &child, &type)))
2758 if (IsEqualGUID(type, &TID_D3DRMMeshNormals)) {
2759 hr = parse_normals(child, mesh_data);
2760 } else if (IsEqualGUID(type, &TID_D3DRMMeshVertexColors)) {
2761 hr = parse_vertex_colors(child, mesh_data);
2762 } else if (IsEqualGUID(type, &TID_D3DRMMeshTextureCoords)) {
2763 hr = parse_texture_coords(child, mesh_data);
2764 } else if (IsEqualGUID(type, &TID_D3DRMMeshMaterialList) &&
2765 (provide_flags & PROVIDE_MATERIALS))
2767 hr = parse_material_list(child, mesh_data);
2768 } else if (provide_flags & PROVIDE_SKININFO) {
2769 if (IsEqualGUID(type, &DXFILEOBJ_XSkinMeshHeader)) {
2770 FIXME("Skin mesh loading not implemented.\n");
2772 } else if (IsEqualGUID(type, &DXFILEOBJ_SkinWeights)) {
2773 /* ignored without XSkinMeshHeader */
2779 return hr == DXFILEERR_NOMOREOBJECTS ? D3D_OK : hr;
2780 truncated_data_error:
2781 WARN("truncated data (%u bytes)\n", data_size);
2785 static HRESULT generate_effects(ID3DXBuffer *materials, DWORD num_materials,
2786 ID3DXBuffer **effects)
2789 D3DXEFFECTINSTANCE *effect_ptr;
2791 const D3DXMATERIAL *material_ptr = ID3DXBuffer_GetBufferPointer(materials);
2792 static const struct {
2793 const char *param_name;
2797 } material_effects[] = {
2798 #define EFFECT_TABLE_ENTRY(str, field) \
2799 {str, sizeof(str), sizeof(material_ptr->MatD3D.field), offsetof(D3DXMATERIAL, MatD3D.field)}
2800 EFFECT_TABLE_ENTRY("Diffuse", Diffuse),
2801 EFFECT_TABLE_ENTRY("Power", Power),
2802 EFFECT_TABLE_ENTRY("Specular", Specular),
2803 EFFECT_TABLE_ENTRY("Emissive", Emissive),
2804 EFFECT_TABLE_ENTRY("Ambient", Ambient),
2805 #undef EFFECT_TABLE_ENTRY
2807 static const char texture_paramname[] = "Texture0@Name";
2811 /* effects buffer layout:
2813 * D3DXEFFECTINSTANCE effects[num_materials];
2814 * for (effect in effects)
2816 * D3DXEFFECTDEFAULT defaults[effect.NumDefaults];
2817 * for (default in defaults)
2819 * *default.pParamName;
2824 buffer_size = sizeof(D3DXEFFECTINSTANCE);
2825 buffer_size += sizeof(D3DXEFFECTDEFAULT) * ARRAY_SIZE(material_effects);
2826 for (i = 0; i < ARRAY_SIZE(material_effects); i++) {
2827 buffer_size += material_effects[i].name_size;
2828 buffer_size += material_effects[i].num_bytes;
2830 buffer_size *= num_materials;
2831 for (i = 0; i < num_materials; i++) {
2832 if (material_ptr[i].pTextureFilename) {
2833 buffer_size += sizeof(D3DXEFFECTDEFAULT);
2834 buffer_size += sizeof(texture_paramname);
2835 buffer_size += strlen(material_ptr[i].pTextureFilename) + 1;
2839 hr = D3DXCreateBuffer(buffer_size, effects);
2840 if (FAILED(hr)) return hr;
2841 effect_ptr = ID3DXBuffer_GetBufferPointer(*effects);
2842 out_ptr = (BYTE*)(effect_ptr + num_materials);
2844 for (i = 0; i < num_materials; i++)
2847 D3DXEFFECTDEFAULT *defaults = (D3DXEFFECTDEFAULT*)out_ptr;
2849 effect_ptr->pDefaults = defaults;
2850 effect_ptr->NumDefaults = material_ptr->pTextureFilename ? 6 : 5;
2851 out_ptr = (BYTE*)(effect_ptr->pDefaults + effect_ptr->NumDefaults);
2853 for (j = 0; j < ARRAY_SIZE(material_effects); j++)
2855 defaults->pParamName = (LPSTR)out_ptr;
2856 strcpy(defaults->pParamName, material_effects[j].param_name);
2857 defaults->pValue = defaults->pParamName + material_effects[j].name_size;
2858 defaults->Type = D3DXEDT_FLOATS;
2859 defaults->NumBytes = material_effects[j].num_bytes;
2860 memcpy(defaults->pValue, (BYTE*)material_ptr + material_effects[j].value_offset, defaults->NumBytes);
2861 out_ptr = (BYTE*)defaults->pValue + defaults->NumBytes;
2865 if (material_ptr->pTextureFilename) {
2866 defaults->pParamName = (LPSTR)out_ptr;
2867 strcpy(defaults->pParamName, texture_paramname);
2868 defaults->pValue = defaults->pParamName + sizeof(texture_paramname);
2869 defaults->Type = D3DXEDT_STRING;
2870 defaults->NumBytes = strlen(material_ptr->pTextureFilename) + 1;
2871 strcpy(defaults->pValue, material_ptr->pTextureFilename);
2872 out_ptr = (BYTE*)defaults->pValue + defaults->NumBytes;
2877 assert(out_ptr - (BYTE*)ID3DXBuffer_GetBufferPointer(*effects) == buffer_size);
2882 /* change to D3DXLoadSkinMeshFromXof when ID3DXFileData is implemented */
2883 static HRESULT load_skin_mesh_from_xof(IDirectXFileData *filedata,
2885 LPDIRECT3DDEVICE9 device,
2886 LPD3DXBUFFER *adjacency_out,
2887 LPD3DXBUFFER *materials_out,
2888 LPD3DXBUFFER *effects_out,
2889 DWORD *num_materials_out,
2890 LPD3DXSKININFO *skin_info_out,
2891 LPD3DXMESH *mesh_out)
2894 DWORD *index_in_ptr;
2895 struct mesh_data mesh_data;
2896 DWORD total_vertices;
2897 ID3DXMesh *d3dxmesh = NULL;
2898 ID3DXBuffer *adjacency = NULL;
2899 ID3DXBuffer *materials = NULL;
2900 ID3DXBuffer *effects = NULL;
2901 struct vertex_duplication {
2904 } *duplications = NULL;
2906 void *vertices = NULL;
2907 void *indices = NULL;
2909 DWORD provide_flags = 0;
2911 ZeroMemory(&mesh_data, sizeof(mesh_data));
2913 if (num_materials_out || materials_out || effects_out)
2914 provide_flags |= PROVIDE_MATERIALS;
2916 provide_flags |= PROVIDE_SKININFO;
2918 hr = parse_mesh(filedata, &mesh_data, provide_flags);
2919 if (FAILED(hr)) goto cleanup;
2921 total_vertices = mesh_data.num_vertices;
2922 if (mesh_data.fvf & D3DFVF_NORMAL) {
2923 /* duplicate vertices with multiple normals */
2924 DWORD num_face_indices = mesh_data.num_poly_faces * 2 + mesh_data.num_tri_faces;
2925 duplications = HeapAlloc(GetProcessHeap(), 0, (mesh_data.num_vertices + num_face_indices) * sizeof(*duplications));
2926 if (!duplications) {
2930 for (i = 0; i < total_vertices; i++)
2932 duplications[i].normal_index = -1;
2933 list_init(&duplications[i].entry);
2935 for (i = 0; i < num_face_indices; i++) {
2936 DWORD vertex_index = mesh_data.indices[i];
2937 DWORD normal_index = mesh_data.normal_indices[i];
2938 struct vertex_duplication *dup_ptr = &duplications[vertex_index];
2940 if (dup_ptr->normal_index == -1) {
2941 dup_ptr->normal_index = normal_index;
2943 D3DXVECTOR3 *new_normal = &mesh_data.normals[normal_index];
2944 struct list *dup_list = &dup_ptr->entry;
2946 D3DXVECTOR3 *cur_normal = &mesh_data.normals[dup_ptr->normal_index];
2947 if (new_normal->x == cur_normal->x &&
2948 new_normal->y == cur_normal->y &&
2949 new_normal->z == cur_normal->z)
2951 mesh_data.indices[i] = dup_ptr - duplications;
2953 } else if (!list_next(dup_list, &dup_ptr->entry)) {
2954 dup_ptr = &duplications[total_vertices++];
2955 dup_ptr->normal_index = normal_index;
2956 list_add_tail(dup_list, &dup_ptr->entry);
2957 mesh_data.indices[i] = dup_ptr - duplications;
2960 dup_ptr = LIST_ENTRY(list_next(dup_list, &dup_ptr->entry),
2961 struct vertex_duplication, entry);
2968 hr = D3DXCreateMeshFVF(mesh_data.num_tri_faces, total_vertices, options, mesh_data.fvf, device, &d3dxmesh);
2969 if (FAILED(hr)) goto cleanup;
2971 hr = d3dxmesh->lpVtbl->LockVertexBuffer(d3dxmesh, 0, &vertices);
2972 if (FAILED(hr)) goto cleanup;
2975 for (i = 0; i < mesh_data.num_vertices; i++) {
2976 *(D3DXVECTOR3*)out_ptr = mesh_data.vertices[i];
2977 out_ptr += sizeof(D3DXVECTOR3);
2978 if (mesh_data.fvf & D3DFVF_NORMAL) {
2979 if (duplications[i].normal_index == -1)
2980 ZeroMemory(out_ptr, sizeof(D3DXVECTOR3));
2982 *(D3DXVECTOR3*)out_ptr = mesh_data.normals[duplications[i].normal_index];
2983 out_ptr += sizeof(D3DXVECTOR3);
2985 if (mesh_data.fvf & D3DFVF_DIFFUSE) {
2986 *(DWORD*)out_ptr = mesh_data.vertex_colors[i];
2987 out_ptr += sizeof(DWORD);
2989 if (mesh_data.fvf & D3DFVF_TEX1) {
2990 *(D3DXVECTOR2*)out_ptr = mesh_data.tex_coords[i];
2991 out_ptr += sizeof(D3DXVECTOR2);
2994 if (mesh_data.fvf & D3DFVF_NORMAL) {
2995 DWORD vertex_size = D3DXGetFVFVertexSize(mesh_data.fvf);
2997 for (i = 0; i < mesh_data.num_vertices; i++) {
2998 struct vertex_duplication *dup_ptr;
2999 LIST_FOR_EACH_ENTRY(dup_ptr, &duplications[i].entry, struct vertex_duplication, entry)
3001 int j = dup_ptr - duplications;
3002 BYTE *dest_vertex = (BYTE*)vertices + j * vertex_size;
3004 memcpy(dest_vertex, out_ptr, vertex_size);
3005 dest_vertex += sizeof(D3DXVECTOR3);
3006 *(D3DXVECTOR3*)dest_vertex = mesh_data.normals[dup_ptr->normal_index];
3008 out_ptr += vertex_size;
3011 d3dxmesh->lpVtbl->UnlockVertexBuffer(d3dxmesh);
3013 hr = d3dxmesh->lpVtbl->LockIndexBuffer(d3dxmesh, 0, (void**)&indices);
3014 if (FAILED(hr)) goto cleanup;
3016 index_in_ptr = mesh_data.indices;
3017 #define FILL_INDEX_BUFFER(indices_var) \
3018 for (i = 0; i < mesh_data.num_poly_faces; i++) \
3020 DWORD count = mesh_data.num_tri_per_face[i]; \
3021 WORD first_index = *index_in_ptr++; \
3023 *indices_var++ = first_index; \
3024 *indices_var++ = *index_in_ptr; \
3026 *indices_var++ = *index_in_ptr; \
3030 if (options & D3DXMESH_32BIT) {
3031 DWORD *dword_indices = indices;
3032 FILL_INDEX_BUFFER(dword_indices)
3034 WORD *word_indices = indices;
3035 FILL_INDEX_BUFFER(word_indices)
3037 #undef FILL_INDEX_BUFFER
3038 d3dxmesh->lpVtbl->UnlockIndexBuffer(d3dxmesh);
3040 if (mesh_data.material_indices) {
3041 DWORD *attrib_buffer = NULL;
3042 hr = d3dxmesh->lpVtbl->LockAttributeBuffer(d3dxmesh, 0, &attrib_buffer);
3043 if (FAILED(hr)) goto cleanup;
3044 for (i = 0; i < mesh_data.num_poly_faces; i++)
3046 DWORD count = mesh_data.num_tri_per_face[i];
3048 *attrib_buffer++ = mesh_data.material_indices[i];
3050 d3dxmesh->lpVtbl->UnlockAttributeBuffer(d3dxmesh);
3052 hr = d3dxmesh->lpVtbl->OptimizeInplace(d3dxmesh,
3053 D3DXMESHOPT_ATTRSORT | D3DXMESHOPT_IGNOREVERTS | D3DXMESHOPT_DONOTSPLIT,
3054 NULL, NULL, NULL, NULL);
3055 if (FAILED(hr)) goto cleanup;
3058 if (mesh_data.num_materials && (materials_out || effects_out)) {
3059 DWORD buffer_size = mesh_data.num_materials * sizeof(D3DXMATERIAL);
3060 char *strings_out_ptr;
3061 D3DXMATERIAL *materials_ptr;
3063 for (i = 0; i < mesh_data.num_materials; i++) {
3064 if (mesh_data.materials[i].pTextureFilename)
3065 buffer_size += strlen(mesh_data.materials[i].pTextureFilename) + 1;
3068 hr = D3DXCreateBuffer(buffer_size, &materials);
3069 if (FAILED(hr)) goto cleanup;
3071 materials_ptr = ID3DXBuffer_GetBufferPointer(materials);
3072 memcpy(materials_ptr, mesh_data.materials, mesh_data.num_materials * sizeof(D3DXMATERIAL));
3073 strings_out_ptr = (char*)(materials_ptr + mesh_data.num_materials);
3074 for (i = 0; i < mesh_data.num_materials; i++) {
3075 if (materials_ptr[i].pTextureFilename) {
3076 strcpy(strings_out_ptr, mesh_data.materials[i].pTextureFilename);
3077 materials_ptr[i].pTextureFilename = strings_out_ptr;
3078 strings_out_ptr += strlen(mesh_data.materials[i].pTextureFilename) + 1;
3083 if (mesh_data.num_materials && effects_out) {
3084 hr = generate_effects(materials, mesh_data.num_materials, &effects);
3085 if (FAILED(hr)) goto cleanup;
3087 if (!materials_out) {
3088 ID3DXBuffer_Release(materials);
3093 if (adjacency_out) {
3094 hr = D3DXCreateBuffer(mesh_data.num_tri_faces * 3 * sizeof(DWORD), &adjacency);
3095 if (FAILED(hr)) goto cleanup;
3096 hr = d3dxmesh->lpVtbl->GenerateAdjacency(d3dxmesh, 0.0f, ID3DXBuffer_GetBufferPointer(adjacency));
3097 if (FAILED(hr)) goto cleanup;
3100 *mesh_out = d3dxmesh;
3101 if (adjacency_out) *adjacency_out = adjacency;
3102 if (num_materials_out) *num_materials_out = mesh_data.num_materials;
3103 if (materials_out) *materials_out = materials;
3104 if (effects_out) *effects_out = effects;
3105 if (skin_info_out) *skin_info_out = NULL;
3110 if (d3dxmesh) IUnknown_Release(d3dxmesh);
3111 if (adjacency) ID3DXBuffer_Release(adjacency);
3112 if (materials) ID3DXBuffer_Release(materials);
3113 if (effects) ID3DXBuffer_Release(effects);
3115 HeapFree(GetProcessHeap(), 0, mesh_data.vertices);
3116 HeapFree(GetProcessHeap(), 0, mesh_data.num_tri_per_face);
3117 HeapFree(GetProcessHeap(), 0, mesh_data.indices);
3118 HeapFree(GetProcessHeap(), 0, mesh_data.normals);
3119 HeapFree(GetProcessHeap(), 0, mesh_data.normal_indices);
3120 destroy_materials(&mesh_data);
3121 HeapFree(GetProcessHeap(), 0, mesh_data.tex_coords);
3122 HeapFree(GetProcessHeap(), 0, mesh_data.vertex_colors);
3123 HeapFree(GetProcessHeap(), 0, duplications);
3127 HRESULT WINAPI D3DXLoadMeshHierarchyFromXA(LPCSTR filename,
3129 LPDIRECT3DDEVICE9 device,
3130 LPD3DXALLOCATEHIERARCHY alloc_hier,
3131 LPD3DXLOADUSERDATA load_user_data,
3132 LPD3DXFRAME *frame_hierarchy,
3133 LPD3DXANIMATIONCONTROLLER *anim_controller)
3139 TRACE("(%s, %x, %p, %p, %p, %p, %p)\n", debugstr_a(filename), options,
3140 device, alloc_hier, load_user_data, frame_hierarchy, anim_controller);
3143 return D3DERR_INVALIDCALL;
3145 len = MultiByteToWideChar(CP_ACP, 0, filename, -1, NULL, 0);
3146 filenameW = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
3147 if (!filenameW) return E_OUTOFMEMORY;
3148 MultiByteToWideChar(CP_ACP, 0, filename, -1, filenameW, len);
3150 hr = D3DXLoadMeshHierarchyFromXW(filenameW, options, device,
3151 alloc_hier, load_user_data, frame_hierarchy, anim_controller);
3152 HeapFree(GetProcessHeap(), 0, filenameW);
3157 HRESULT WINAPI D3DXLoadMeshHierarchyFromXW(LPCWSTR filename,
3159 LPDIRECT3DDEVICE9 device,
3160 LPD3DXALLOCATEHIERARCHY alloc_hier,
3161 LPD3DXLOADUSERDATA load_user_data,
3162 LPD3DXFRAME *frame_hierarchy,
3163 LPD3DXANIMATIONCONTROLLER *anim_controller)
3169 TRACE("(%s, %x, %p, %p, %p, %p, %p)\n", debugstr_w(filename), options,
3170 device, alloc_hier, load_user_data, frame_hierarchy, anim_controller);
3173 return D3DERR_INVALIDCALL;
3175 hr = map_view_of_file(filename, &buffer, &size);
3177 return D3DXERR_INVALIDDATA;
3179 hr = D3DXLoadMeshHierarchyFromXInMemory(buffer, size, options, device,
3180 alloc_hier, load_user_data, frame_hierarchy, anim_controller);
3182 UnmapViewOfFile(buffer);
3187 static HRESULT filedata_get_name(IDirectXFileData *filedata, char **name)
3192 hr = IDirectXFileData_GetName(filedata, NULL, &name_len);
3193 if (FAILED(hr)) return hr;
3197 *name = HeapAlloc(GetProcessHeap(), 0, name_len);
3198 if (!*name) return E_OUTOFMEMORY;
3200 hr = IDirectXFileObject_GetName(filedata, *name, &name_len);
3202 HeapFree(GetProcessHeap(), 0, name);
3209 static HRESULT load_mesh_container(IDirectXFileData *filedata,
3211 LPDIRECT3DDEVICE9 device,
3212 LPD3DXALLOCATEHIERARCHY alloc_hier,
3213 D3DXMESHCONTAINER **mesh_container)
3216 ID3DXBuffer *adjacency = NULL;
3217 ID3DXBuffer *materials = NULL;
3218 ID3DXBuffer *effects = NULL;
3219 ID3DXSkinInfo *skin_info = NULL;
3220 D3DXMESHDATA mesh_data;
3221 DWORD num_materials = 0;
3224 mesh_data.Type = D3DXMESHTYPE_MESH;
3225 mesh_data.u.pMesh = NULL;
3227 hr = load_skin_mesh_from_xof(filedata, options, device,
3228 &adjacency, &materials, &effects, &num_materials,
3229 &skin_info, &mesh_data.u.pMesh);
3230 if (FAILED(hr)) return hr;
3232 hr = filedata_get_name(filedata, &name);
3233 if (FAILED(hr)) goto cleanup;
3235 hr = alloc_hier->lpVtbl->CreateMeshContainer(alloc_hier, name, &mesh_data,
3236 materials ? ID3DXBuffer_GetBufferPointer(materials) : NULL,
3237 effects ? ID3DXBuffer_GetBufferPointer(effects) : NULL,
3239 adjacency ? ID3DXBuffer_GetBufferPointer(adjacency) : NULL,
3240 skin_info, mesh_container);
3243 if (materials) ID3DXBuffer_Release(materials);
3244 if (effects) ID3DXBuffer_Release(effects);
3245 if (adjacency) ID3DXBuffer_Release(adjacency);
3246 if (skin_info) IUnknown_Release(skin_info);
3247 if (mesh_data.u.pMesh) IUnknown_Release(mesh_data.u.pMesh);
3248 HeapFree(GetProcessHeap(), 0, name);
3252 static HRESULT parse_transform_matrix(IDirectXFileData *filedata, D3DXMATRIX *transform)
3258 /* template Matrix4x4 {
3259 * array FLOAT matrix[16];
3261 * template FrameTransformMatrix {
3262 * Matrix4x4 frameMatrix;
3266 hr = IDirectXFileData_GetData(filedata, NULL, &data_size, (void**)&data);
3267 if (FAILED(hr)) return hr;
3269 if (data_size != sizeof(D3DXMATRIX)) {
3270 WARN("incorrect data size (%u bytes)\n", data_size);
3274 memcpy(transform, data, sizeof(D3DXMATRIX));
3279 static HRESULT load_frame(IDirectXFileData *filedata,
3281 LPDIRECT3DDEVICE9 device,
3282 LPD3DXALLOCATEHIERARCHY alloc_hier,
3283 D3DXFRAME **frame_out)
3287 IDirectXFileData *child;
3289 D3DXFRAME *frame = NULL;
3290 D3DXMESHCONTAINER **next_container;
3291 D3DXFRAME **next_child;
3293 hr = filedata_get_name(filedata, &name);
3294 if (FAILED(hr)) return hr;
3296 hr = alloc_hier->lpVtbl->CreateFrame(alloc_hier, name, frame_out);
3297 HeapFree(GetProcessHeap(), 0, name);
3298 if (FAILED(hr)) return E_FAIL;
3301 D3DXMatrixIdentity(&frame->TransformationMatrix);
3302 next_child = &frame->pFrameFirstChild;
3303 next_container = &frame->pMeshContainer;
3305 while (SUCCEEDED(hr = get_next_child(filedata, &child, &type)))
3307 if (IsEqualGUID(type, &TID_D3DRMMesh)) {
3308 hr = load_mesh_container(child, options, device, alloc_hier, next_container);
3310 next_container = &(*next_container)->pNextMeshContainer;
3311 } else if (IsEqualGUID(type, &TID_D3DRMFrameTransformMatrix)) {
3312 hr = parse_transform_matrix(child, &frame->TransformationMatrix);
3313 } else if (IsEqualGUID(type, &TID_D3DRMFrame)) {
3314 hr = load_frame(child, options, device, alloc_hier, next_child);
3316 next_child = &(*next_child)->pFrameSibling;
3318 if (FAILED(hr)) break;
3320 if (hr == DXFILEERR_NOMOREOBJECTS)
3326 HRESULT WINAPI D3DXLoadMeshHierarchyFromXInMemory(LPCVOID memory,
3329 LPDIRECT3DDEVICE9 device,
3330 LPD3DXALLOCATEHIERARCHY alloc_hier,
3331 LPD3DXLOADUSERDATA load_user_data,
3332 LPD3DXFRAME *frame_hierarchy,
3333 LPD3DXANIMATIONCONTROLLER *anim_controller)
3336 IDirectXFile *dxfile = NULL;
3337 IDirectXFileEnumObject *enumobj = NULL;
3338 IDirectXFileData *filedata = NULL;
3339 DXFILELOADMEMORY source;
3340 D3DXFRAME *first_frame = NULL;
3341 D3DXFRAME **next_frame = &first_frame;
3343 TRACE("(%p, %u, %x, %p, %p, %p, %p, %p)\n", memory, memory_size, options,
3344 device, alloc_hier, load_user_data, frame_hierarchy, anim_controller);
3346 if (!memory || !memory_size || !device || !frame_hierarchy || !alloc_hier)
3347 return D3DERR_INVALIDCALL;
3348 if (load_user_data || anim_controller) {
3350 FIXME("Loading user data not implemented\n");
3351 if (anim_controller)
3352 FIXME("Animation controller creation not implemented\n");
3356 hr = DirectXFileCreate(&dxfile);
3357 if (FAILED(hr)) goto cleanup;
3359 hr = IDirectXFile_RegisterTemplates(dxfile, D3DRM_XTEMPLATES, D3DRM_XTEMPLATE_BYTES);
3360 if (FAILED(hr)) goto cleanup;
3362 source.lpMemory = (void*)memory;
3363 source.dSize = memory_size;
3364 hr = IDirectXFile_CreateEnumObject(dxfile, &source, DXFILELOAD_FROMMEMORY, &enumobj);
3365 if (FAILED(hr)) goto cleanup;
3367 while (SUCCEEDED(hr = IDirectXFileEnumObject_GetNextDataObject(enumobj, &filedata)))
3369 const GUID *guid = NULL;
3371 hr = IDirectXFileData_GetType(filedata, &guid);
3372 if (SUCCEEDED(hr)) {
3373 if (IsEqualGUID(guid, &TID_D3DRMMesh)) {
3374 hr = alloc_hier->lpVtbl->CreateFrame(alloc_hier, NULL, next_frame);
3380 D3DXMatrixIdentity(&(*next_frame)->TransformationMatrix);
3382 hr = load_mesh_container(filedata, options, device, alloc_hier, &(*next_frame)->pMeshContainer);
3383 if (FAILED(hr)) goto cleanup;
3384 } else if (IsEqualGUID(guid, &TID_D3DRMFrame)) {
3385 hr = load_frame(filedata, options, device, alloc_hier, next_frame);
3386 if (FAILED(hr)) goto cleanup;
3389 next_frame = &(*next_frame)->pFrameSibling;
3392 IDirectXFileData_Release(filedata);
3397 if (hr != DXFILEERR_NOMOREOBJECTS)
3402 } else if (first_frame->pFrameSibling) {
3403 D3DXFRAME *root_frame = NULL;
3404 hr = alloc_hier->lpVtbl->CreateFrame(alloc_hier, NULL, &root_frame);
3409 D3DXMatrixIdentity(&root_frame->TransformationMatrix);
3410 root_frame->pFrameFirstChild = first_frame;
3411 *frame_hierarchy = root_frame;
3414 *frame_hierarchy = first_frame;
3419 if (FAILED(hr) && first_frame) D3DXFrameDestroy(first_frame, alloc_hier);
3420 if (filedata) IDirectXFileData_Release(filedata);
3421 if (enumobj) IDirectXFileEnumObject_Release(enumobj);
3422 if (dxfile) IDirectXFile_Release(dxfile);
3426 HRESULT WINAPI D3DXFrameDestroy(LPD3DXFRAME frame, LPD3DXALLOCATEHIERARCHY alloc_hier)
3431 TRACE("(%p, %p)\n", frame, alloc_hier);
3433 if (!frame || !alloc_hier)
3434 return D3DERR_INVALIDCALL;
3437 D3DXMESHCONTAINER *container;
3438 D3DXFRAME *current_frame;
3440 if (frame->pFrameSibling) {
3441 current_frame = frame->pFrameSibling;
3442 frame->pFrameSibling = current_frame->pFrameSibling;
3443 current_frame->pFrameSibling = NULL;
3445 current_frame = frame;
3449 if (current_frame->pFrameFirstChild) {
3450 hr = D3DXFrameDestroy(current_frame->pFrameFirstChild, alloc_hier);
3451 if (FAILED(hr)) return hr;
3452 current_frame->pFrameFirstChild = NULL;
3455 container = current_frame->pMeshContainer;
3457 D3DXMESHCONTAINER *next_container = container->pNextMeshContainer;
3458 hr = alloc_hier->lpVtbl->DestroyMeshContainer(alloc_hier, container);
3459 if (FAILED(hr)) return hr;
3460 container = next_container;
3462 hr = alloc_hier->lpVtbl->DestroyFrame(alloc_hier, current_frame);
3463 if (FAILED(hr)) return hr;
3468 HRESULT WINAPI D3DXLoadMeshFromXA(LPCSTR filename,
3470 LPDIRECT3DDEVICE9 device,
3471 LPD3DXBUFFER *adjacency,
3472 LPD3DXBUFFER *materials,
3473 LPD3DXBUFFER *effect_instances,
3474 DWORD *num_materials,
3481 TRACE("(%s, %x, %p, %p, %p, %p, %p, %p)\n", debugstr_a(filename), options,
3482 device, adjacency, materials, effect_instances, num_materials, mesh);
3485 return D3DERR_INVALIDCALL;
3487 len = MultiByteToWideChar(CP_ACP, 0, filename, -1, NULL, 0);
3488 filenameW = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
3489 if (!filenameW) return E_OUTOFMEMORY;
3490 MultiByteToWideChar(CP_ACP, 0, filename, -1, filenameW, len);
3492 hr = D3DXLoadMeshFromXW(filenameW, options, device, adjacency, materials,
3493 effect_instances, num_materials, mesh);
3494 HeapFree(GetProcessHeap(), 0, filenameW);
3499 HRESULT WINAPI D3DXLoadMeshFromXW(LPCWSTR filename,
3501 LPDIRECT3DDEVICE9 device,
3502 LPD3DXBUFFER *adjacency,
3503 LPD3DXBUFFER *materials,
3504 LPD3DXBUFFER *effect_instances,
3505 DWORD *num_materials,
3512 TRACE("(%s, %x, %p, %p, %p, %p, %p, %p)\n", debugstr_w(filename), options,
3513 device, adjacency, materials, effect_instances, num_materials, mesh);
3516 return D3DERR_INVALIDCALL;
3518 hr = map_view_of_file(filename, &buffer, &size);
3520 return D3DXERR_INVALIDDATA;
3522 hr = D3DXLoadMeshFromXInMemory(buffer, size, options, device, adjacency,
3523 materials, effect_instances, num_materials, mesh);
3525 UnmapViewOfFile(buffer);
3530 HRESULT WINAPI D3DXLoadMeshFromXResource(HMODULE module,
3534 LPDIRECT3DDEVICE9 device,
3535 LPD3DXBUFFER *adjacency,
3536 LPD3DXBUFFER *materials,
3537 LPD3DXBUFFER *effect_instances,
3538 DWORD *num_materials,
3546 TRACE("(%p, %s, %s, %x, %p, %p, %p, %p, %p, %p)\n",
3547 module, debugstr_a(name), debugstr_a(type), options, device,
3548 adjacency, materials, effect_instances, num_materials, mesh);
3550 resinfo = FindResourceA(module, name, type);
3551 if (!resinfo) return D3DXERR_INVALIDDATA;
3553 hr = load_resource_into_memory(module, resinfo, &buffer, &size);
3554 if (FAILED(hr)) return D3DXERR_INVALIDDATA;
3556 return D3DXLoadMeshFromXInMemory(buffer, size, options, device, adjacency,
3557 materials, effect_instances, num_materials, mesh);
3560 struct mesh_container
3564 ID3DXBuffer *adjacency;
3565 ID3DXBuffer *materials;
3566 ID3DXBuffer *effects;
3567 DWORD num_materials;
3568 D3DXMATRIX transform;
3571 static HRESULT parse_frame(IDirectXFileData *filedata,
3573 LPDIRECT3DDEVICE9 device,
3574 const D3DXMATRIX *parent_transform,
3575 struct list *container_list,
3576 DWORD provide_flags)
3579 D3DXMATRIX transform = *parent_transform;
3580 IDirectXFileData *child;
3583 while (SUCCEEDED(hr = get_next_child(filedata, &child, &type)))
3585 if (IsEqualGUID(type, &TID_D3DRMMesh)) {
3586 struct mesh_container *container = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(*container));
3591 list_add_tail(container_list, &container->entry);
3592 container->transform = transform;
3594 hr = load_skin_mesh_from_xof(child, options, device,
3595 (provide_flags & PROVIDE_ADJACENCY) ? &container->adjacency : NULL,
3596 (provide_flags & PROVIDE_MATERIALS) ? &container->materials : NULL,
3597 NULL, &container->num_materials, NULL, &container->mesh);
3598 } else if (IsEqualGUID(type, &TID_D3DRMFrameTransformMatrix)) {
3599 D3DXMATRIX new_transform;
3600 hr = parse_transform_matrix(child, &new_transform);
3601 D3DXMatrixMultiply(&transform, &transform, &new_transform);
3602 } else if (IsEqualGUID(type, &TID_D3DRMFrame)) {
3603 hr = parse_frame(child, options, device, &transform, container_list, provide_flags);
3605 if (FAILED(hr)) break;
3607 return hr == DXFILEERR_NOMOREOBJECTS ? D3D_OK : hr;
3610 HRESULT WINAPI D3DXLoadMeshFromXInMemory(LPCVOID memory,
3613 LPDIRECT3DDEVICE9 device,
3614 LPD3DXBUFFER *adjacency_out,
3615 LPD3DXBUFFER *materials_out,
3616 LPD3DXBUFFER *effects_out,
3617 DWORD *num_materials_out,
3618 LPD3DXMESH *mesh_out)
3621 IDirectXFile *dxfile = NULL;
3622 IDirectXFileEnumObject *enumobj = NULL;
3623 IDirectXFileData *filedata = NULL;
3624 DXFILELOADMEMORY source;
3625 ID3DXBuffer *materials = NULL;
3626 ID3DXBuffer *effects = NULL;
3627 ID3DXBuffer *adjacency = NULL;
3628 struct list container_list = LIST_INIT(container_list);
3629 struct mesh_container *container_ptr, *next_container_ptr;
3630 DWORD num_materials;
3631 DWORD num_faces, num_vertices;
3632 D3DXMATRIX identity;
3634 DWORD provide_flags = 0;
3636 ID3DXMesh *concat_mesh = NULL;
3637 D3DVERTEXELEMENT9 concat_decl[MAX_FVF_DECL_SIZE];
3638 BYTE *concat_vertices = NULL;
3639 void *concat_indices = NULL;
3641 DWORD concat_vertex_size;
3643 TRACE("(%p, %u, %x, %p, %p, %p, %p, %p, %p)\n", memory, memory_size, options,
3644 device, adjacency_out, materials_out, effects_out, num_materials_out, mesh_out);
3646 if (!memory || !memory_size || !device || !mesh_out)
3647 return D3DERR_INVALIDCALL;
3649 hr = DirectXFileCreate(&dxfile);
3650 if (FAILED(hr)) goto cleanup;
3652 hr = IDirectXFile_RegisterTemplates(dxfile, D3DRM_XTEMPLATES, D3DRM_XTEMPLATE_BYTES);
3653 if (FAILED(hr)) goto cleanup;
3655 source.lpMemory = (void*)memory;
3656 source.dSize = memory_size;
3657 hr = IDirectXFile_CreateEnumObject(dxfile, &source, DXFILELOAD_FROMMEMORY, &enumobj);
3658 if (FAILED(hr)) goto cleanup;
3660 D3DXMatrixIdentity(&identity);
3661 if (adjacency_out) provide_flags |= PROVIDE_ADJACENCY;
3662 if (materials_out || effects_out) provide_flags |= PROVIDE_MATERIALS;
3664 while (SUCCEEDED(hr = IDirectXFileEnumObject_GetNextDataObject(enumobj, &filedata)))
3666 const GUID *guid = NULL;
3668 hr = IDirectXFileData_GetType(filedata, &guid);
3669 if (SUCCEEDED(hr)) {
3670 if (IsEqualGUID(guid, &TID_D3DRMMesh)) {
3671 container_ptr = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(*container_ptr));
3672 if (!container_ptr) {
3676 list_add_tail(&container_list, &container_ptr->entry);
3677 D3DXMatrixIdentity(&container_ptr->transform);
3679 hr = load_skin_mesh_from_xof(filedata, options, device,
3680 (provide_flags & PROVIDE_ADJACENCY) ? &container_ptr->adjacency : NULL,
3681 (provide_flags & PROVIDE_MATERIALS) ? &container_ptr->materials : NULL,
3682 NULL, &container_ptr->num_materials, NULL, &container_ptr->mesh);
3683 } else if (IsEqualGUID(guid, &TID_D3DRMFrame)) {
3684 hr = parse_frame(filedata, options, device, &identity, &container_list, provide_flags);
3686 if (FAILED(hr)) goto cleanup;
3688 IDirectXFileData_Release(filedata);
3693 if (hr != DXFILEERR_NOMOREOBJECTS)
3696 IDirectXFileEnumObject_Release(enumobj);
3698 IDirectXFile_Release(dxfile);
3701 if (list_empty(&container_list)) {
3710 LIST_FOR_EACH_ENTRY(container_ptr, &container_list, struct mesh_container, entry)
3712 ID3DXMesh *mesh = container_ptr->mesh;
3713 fvf |= mesh->lpVtbl->GetFVF(mesh);
3714 num_faces += mesh->lpVtbl->GetNumFaces(mesh);
3715 num_vertices += mesh->lpVtbl->GetNumVertices(mesh);
3716 num_materials += container_ptr->num_materials;
3719 hr = D3DXCreateMeshFVF(num_faces, num_vertices, options, fvf, device, &concat_mesh);
3720 if (FAILED(hr)) goto cleanup;
3722 hr = concat_mesh->lpVtbl->GetDeclaration(concat_mesh, concat_decl);
3723 if (FAILED(hr)) goto cleanup;
3725 concat_vertex_size = D3DXGetDeclVertexSize(concat_decl, 0);
3727 hr = concat_mesh->lpVtbl->LockVertexBuffer(concat_mesh, 0, (void**)&concat_vertices);
3728 if (FAILED(hr)) goto cleanup;
3730 LIST_FOR_EACH_ENTRY(container_ptr, &container_list, struct mesh_container, entry)
3732 D3DVERTEXELEMENT9 mesh_decl[MAX_FVF_DECL_SIZE];
3733 ID3DXMesh *mesh = container_ptr->mesh;
3734 DWORD num_mesh_vertices = mesh->lpVtbl->GetNumVertices(mesh);
3735 DWORD mesh_vertex_size;
3736 const BYTE *mesh_vertices;
3738 hr = mesh->lpVtbl->GetDeclaration(mesh, mesh_decl);
3739 if (FAILED(hr)) goto cleanup;
3741 mesh_vertex_size = D3DXGetDeclVertexSize(mesh_decl, 0);
3743 hr = mesh->lpVtbl->LockVertexBuffer(mesh, D3DLOCK_READONLY, (void**)&mesh_vertices);
3744 if (FAILED(hr)) goto cleanup;
3746 for (i = 0; i < num_mesh_vertices; i++) {
3750 D3DXVec3TransformCoord((D3DXVECTOR3*)concat_vertices,
3751 (D3DXVECTOR3*)mesh_vertices,
3752 &container_ptr->transform);
3753 for (j = 1; concat_decl[j].Stream != 0xff; j++)
3755 if (concat_decl[j].Usage == mesh_decl[k].Usage &&
3756 concat_decl[j].UsageIndex == mesh_decl[k].UsageIndex)
3758 if (concat_decl[j].Usage == D3DDECLUSAGE_NORMAL) {
3759 D3DXVec3TransformCoord((D3DXVECTOR3*)(concat_vertices + concat_decl[j].Offset),
3760 (D3DXVECTOR3*)(mesh_vertices + mesh_decl[k].Offset),
3761 &container_ptr->transform);
3763 memcpy(concat_vertices + concat_decl[j].Offset,
3764 mesh_vertices + mesh_decl[k].Offset,
3765 d3dx_decltype_size[mesh_decl[k].Type]);
3770 mesh_vertices += mesh_vertex_size;
3771 concat_vertices += concat_vertex_size;
3774 mesh->lpVtbl->UnlockVertexBuffer(mesh);
3777 concat_mesh->lpVtbl->UnlockVertexBuffer(concat_mesh);
3778 concat_vertices = NULL;
3780 hr = concat_mesh->lpVtbl->LockIndexBuffer(concat_mesh, 0, &concat_indices);
3781 if (FAILED(hr)) goto cleanup;
3784 LIST_FOR_EACH_ENTRY(container_ptr, &container_list, struct mesh_container, entry)
3786 ID3DXMesh *mesh = container_ptr->mesh;
3787 const void *mesh_indices;
3788 DWORD num_mesh_faces = mesh->lpVtbl->GetNumFaces(mesh);
3791 hr = mesh->lpVtbl->LockIndexBuffer(mesh, D3DLOCK_READONLY, (void**)&mesh_indices);
3792 if (FAILED(hr)) goto cleanup;
3794 if (options & D3DXMESH_32BIT) {
3795 DWORD *dest = concat_indices;
3796 const DWORD *src = mesh_indices;
3797 for (i = 0; i < num_mesh_faces * 3; i++)
3798 *dest++ = index_offset + *src++;
3799 concat_indices = dest;
3801 WORD *dest = concat_indices;
3802 const WORD *src = mesh_indices;
3803 for (i = 0; i < num_mesh_faces * 3; i++)
3804 *dest++ = index_offset + *src++;
3805 concat_indices = dest;
3807 mesh->lpVtbl->UnlockIndexBuffer(mesh);
3809 index_offset += num_mesh_faces * 3;
3812 concat_mesh->lpVtbl->UnlockIndexBuffer(concat_mesh);
3813 concat_indices = NULL;
3815 if (num_materials) {
3816 DWORD *concat_attrib_buffer = NULL;
3819 hr = concat_mesh->lpVtbl->LockAttributeBuffer(concat_mesh, 0, &concat_attrib_buffer);
3820 if (FAILED(hr)) goto cleanup;
3822 LIST_FOR_EACH_ENTRY(container_ptr, &container_list, struct mesh_container, entry)
3824 ID3DXMesh *mesh = container_ptr->mesh;
3825 const DWORD *mesh_attrib_buffer = NULL;
3826 DWORD count = mesh->lpVtbl->GetNumFaces(mesh);
3828 hr = mesh->lpVtbl->LockAttributeBuffer(mesh, D3DLOCK_READONLY, (DWORD**)&mesh_attrib_buffer);
3830 concat_mesh->lpVtbl->UnlockAttributeBuffer(concat_mesh);
3835 *concat_attrib_buffer++ = offset + *mesh_attrib_buffer++;
3837 mesh->lpVtbl->UnlockAttributeBuffer(mesh);
3838 offset += container_ptr->num_materials;
3840 concat_mesh->lpVtbl->UnlockAttributeBuffer(concat_mesh);
3843 if (materials_out || effects_out) {
3844 D3DXMATERIAL *out_ptr;
3845 if (!num_materials) {
3846 /* create default material */
3847 hr = D3DXCreateBuffer(sizeof(D3DXMATERIAL), &materials);
3848 if (FAILED(hr)) goto cleanup;
3850 out_ptr = ID3DXBuffer_GetBufferPointer(materials);
3851 out_ptr->MatD3D.Diffuse.r = 0.5f;
3852 out_ptr->MatD3D.Diffuse.g = 0.5f;
3853 out_ptr->MatD3D.Diffuse.b = 0.5f;
3854 out_ptr->MatD3D.Specular.r = 0.5f;
3855 out_ptr->MatD3D.Specular.g = 0.5f;
3856 out_ptr->MatD3D.Specular.b = 0.5f;
3857 /* D3DXCreateBuffer initializes the rest to zero */
3859 DWORD buffer_size = num_materials * sizeof(D3DXMATERIAL);
3860 char *strings_out_ptr;
3862 LIST_FOR_EACH_ENTRY(container_ptr, &container_list, struct mesh_container, entry)
3864 if (container_ptr->materials) {
3865 const D3DXMATERIAL *in_ptr = ID3DXBuffer_GetBufferPointer(container_ptr->materials);
3866 for (i = 0; i < container_ptr->num_materials; i++)
3868 if (in_ptr->pTextureFilename)
3869 buffer_size += strlen(in_ptr->pTextureFilename) + 1;
3875 hr = D3DXCreateBuffer(buffer_size, &materials);
3876 if (FAILED(hr)) goto cleanup;
3877 out_ptr = ID3DXBuffer_GetBufferPointer(materials);
3878 strings_out_ptr = (char*)(out_ptr + num_materials);
3880 LIST_FOR_EACH_ENTRY(container_ptr, &container_list, struct mesh_container, entry)
3882 if (container_ptr->materials) {
3883 const D3DXMATERIAL *in_ptr = ID3DXBuffer_GetBufferPointer(container_ptr->materials);
3884 for (i = 0; i < container_ptr->num_materials; i++)
3886 out_ptr->MatD3D = in_ptr->MatD3D;
3887 if (in_ptr->pTextureFilename) {
3888 out_ptr->pTextureFilename = strings_out_ptr;
3889 strcpy(out_ptr->pTextureFilename, in_ptr->pTextureFilename);
3890 strings_out_ptr += strlen(in_ptr->pTextureFilename) + 1;
3903 generate_effects(materials, num_materials, &effects);
3904 if (!materials_out) {
3905 ID3DXBuffer_Release(materials);
3910 if (adjacency_out) {
3911 if (!list_next(&container_list, list_head(&container_list))) {
3912 container_ptr = LIST_ENTRY(list_head(&container_list), struct mesh_container, entry);
3913 adjacency = container_ptr->adjacency;
3914 container_ptr->adjacency = NULL;
3919 hr = D3DXCreateBuffer(num_faces * 3 * sizeof(DWORD), &adjacency);
3920 if (FAILED(hr)) goto cleanup;
3922 out_ptr = ID3DXBuffer_GetBufferPointer(adjacency);
3923 LIST_FOR_EACH_ENTRY(container_ptr, &container_list, struct mesh_container, entry)
3925 DWORD *in_ptr = ID3DXBuffer_GetBufferPointer(container_ptr->adjacency);
3926 int count = 3 * container_ptr->mesh->lpVtbl->GetNumFaces(container_ptr->mesh);
3928 for (i = 0; i < count; i++)
3929 *out_ptr++ = offset + *in_ptr++;
3936 *mesh_out = concat_mesh;
3937 if (adjacency_out) *adjacency_out = adjacency;
3938 if (materials_out) *materials_out = materials;
3939 if (effects_out) *effects_out = effects;
3940 if (num_materials_out) *num_materials_out = num_materials;
3944 if (concat_indices) concat_mesh->lpVtbl->UnlockIndexBuffer(concat_mesh);
3945 if (concat_vertices) concat_mesh->lpVtbl->UnlockVertexBuffer(concat_mesh);
3946 if (filedata) IDirectXFileData_Release(filedata);
3947 if (enumobj) IDirectXFileEnumObject_Release(enumobj);
3948 if (dxfile) IDirectXFile_Release(dxfile);
3950 if (concat_mesh) IUnknown_Release(concat_mesh);
3951 if (materials) ID3DXBuffer_Release(materials);
3952 if (effects) ID3DXBuffer_Release(effects);
3953 if (adjacency) ID3DXBuffer_Release(adjacency);
3955 LIST_FOR_EACH_ENTRY_SAFE(container_ptr, next_container_ptr, &container_list, struct mesh_container, entry)
3957 if (container_ptr->mesh) IUnknown_Release(container_ptr->mesh);
3958 if (container_ptr->adjacency) ID3DXBuffer_Release(container_ptr->adjacency);
3959 if (container_ptr->materials) ID3DXBuffer_Release(container_ptr->materials);
3960 if (container_ptr->effects) ID3DXBuffer_Release(container_ptr->effects);
3961 HeapFree(GetProcessHeap(), 0, container_ptr);
3966 HRESULT WINAPI D3DXCreateBox(LPDIRECT3DDEVICE9 device, FLOAT width, FLOAT height,
3967 FLOAT depth, LPD3DXMESH* mesh, LPD3DXBUFFER* adjacency)
3969 FIXME("(%p, %f, %f, %f, %p, %p): stub\n", device, width, height, depth, mesh, adjacency);
3976 D3DXVECTOR3 position;
3980 typedef WORD face[3];
3988 static void free_sincos_table(struct sincos_table *sincos_table)
3990 HeapFree(GetProcessHeap(), 0, sincos_table->cos);
3991 HeapFree(GetProcessHeap(), 0, sincos_table->sin);
3994 /* pre compute sine and cosine tables; caller must free */
3995 static BOOL compute_sincos_table(struct sincos_table *sincos_table, float angle_start, float angle_step, int n)
4000 sincos_table->sin = HeapAlloc(GetProcessHeap(), 0, n * sizeof(*sincos_table->sin));
4001 if (!sincos_table->sin)
4005 sincos_table->cos = HeapAlloc(GetProcessHeap(), 0, n * sizeof(*sincos_table->cos));
4006 if (!sincos_table->cos)
4008 HeapFree(GetProcessHeap(), 0, sincos_table->sin);
4012 angle = angle_start;
4013 for (i = 0; i < n; i++)
4015 sincos_table->sin[i] = sin(angle);
4016 sincos_table->cos[i] = cos(angle);
4017 angle += angle_step;
4023 static WORD vertex_index(UINT slices, int slice, int stack)
4025 return stack*slices+slice+1;
4028 HRESULT WINAPI D3DXCreateSphere(LPDIRECT3DDEVICE9 device, FLOAT radius, UINT slices,
4029 UINT stacks, LPD3DXMESH* mesh, LPD3DXBUFFER* adjacency)
4031 DWORD number_of_vertices, number_of_faces;
4034 struct vertex *vertices;
4036 float phi_step, phi_start;
4037 struct sincos_table phi;
4038 float theta_step, theta, sin_theta, cos_theta;
4042 TRACE("(%p, %f, %u, %u, %p, %p)\n", device, radius, slices, stacks, mesh, adjacency);
4044 if (!device || radius < 0.0f || slices < 2 || stacks < 2 || !mesh)
4046 return D3DERR_INVALIDCALL;
4051 FIXME("Case of adjacency != NULL not implemented.\n");
4055 number_of_vertices = 2 + slices * (stacks-1);
4056 number_of_faces = 2 * slices + (stacks - 2) * (2 * slices);
4058 hr = D3DXCreateMeshFVF(number_of_faces, number_of_vertices, D3DXMESH_MANAGED,
4059 D3DFVF_XYZ | D3DFVF_NORMAL, device, &sphere);
4065 hr = sphere->lpVtbl->LockVertexBuffer(sphere, 0, (LPVOID *)&vertices);
4068 sphere->lpVtbl->Release(sphere);
4072 hr = sphere->lpVtbl->LockIndexBuffer(sphere, 0, (LPVOID *)&faces);
4075 sphere->lpVtbl->UnlockVertexBuffer(sphere);
4076 sphere->lpVtbl->Release(sphere);
4080 /* phi = angle on xz plane wrt z axis */
4081 phi_step = -2 * M_PI / slices;
4082 phi_start = M_PI / 2;
4084 if (!compute_sincos_table(&phi, phi_start, phi_step, slices))
4086 sphere->lpVtbl->UnlockIndexBuffer(sphere);
4087 sphere->lpVtbl->UnlockVertexBuffer(sphere);
4088 sphere->lpVtbl->Release(sphere);
4089 return E_OUTOFMEMORY;
4092 /* theta = angle on xy plane wrt x axis */
4093 theta_step = M_PI / stacks;
4099 vertices[vertex].normal.x = 0.0f;
4100 vertices[vertex].normal.y = 0.0f;
4101 vertices[vertex].normal.z = 1.0f;
4102 vertices[vertex].position.x = 0.0f;
4103 vertices[vertex].position.y = 0.0f;
4104 vertices[vertex].position.z = radius;
4107 for (stack = 0; stack < stacks - 1; stack++)
4109 sin_theta = sin(theta);
4110 cos_theta = cos(theta);
4112 for (slice = 0; slice < slices; slice++)
4114 vertices[vertex].normal.x = sin_theta * phi.cos[slice];
4115 vertices[vertex].normal.y = sin_theta * phi.sin[slice];
4116 vertices[vertex].normal.z = cos_theta;
4117 vertices[vertex].position.x = radius * sin_theta * phi.cos[slice];
4118 vertices[vertex].position.y = radius * sin_theta * phi.sin[slice];
4119 vertices[vertex].position.z = radius * cos_theta;
4126 /* top stack is triangle fan */
4128 faces[face][1] = slice + 1;
4129 faces[face][2] = slice;
4134 /* stacks in between top and bottom are quad strips */
4135 faces[face][0] = vertex_index(slices, slice-1, stack-1);
4136 faces[face][1] = vertex_index(slices, slice, stack-1);
4137 faces[face][2] = vertex_index(slices, slice-1, stack);
4140 faces[face][0] = vertex_index(slices, slice, stack-1);
4141 faces[face][1] = vertex_index(slices, slice, stack);
4142 faces[face][2] = vertex_index(slices, slice-1, stack);
4148 theta += theta_step;
4154 faces[face][2] = slice;
4159 faces[face][0] = vertex_index(slices, slice-1, stack-1);
4160 faces[face][1] = vertex_index(slices, 0, stack-1);
4161 faces[face][2] = vertex_index(slices, slice-1, stack);
4164 faces[face][0] = vertex_index(slices, 0, stack-1);
4165 faces[face][1] = vertex_index(slices, 0, stack);
4166 faces[face][2] = vertex_index(slices, slice-1, stack);
4171 vertices[vertex].position.x = 0.0f;
4172 vertices[vertex].position.y = 0.0f;
4173 vertices[vertex].position.z = -radius;
4174 vertices[vertex].normal.x = 0.0f;
4175 vertices[vertex].normal.y = 0.0f;
4176 vertices[vertex].normal.z = -1.0f;
4178 /* bottom stack is triangle fan */
4179 for (slice = 1; slice < slices; slice++)
4181 faces[face][0] = vertex_index(slices, slice-1, stack-1);
4182 faces[face][1] = vertex_index(slices, slice, stack-1);
4183 faces[face][2] = vertex;
4187 faces[face][0] = vertex_index(slices, slice-1, stack-1);
4188 faces[face][1] = vertex_index(slices, 0, stack-1);
4189 faces[face][2] = vertex;
4191 free_sincos_table(&phi);
4192 sphere->lpVtbl->UnlockIndexBuffer(sphere);
4193 sphere->lpVtbl->UnlockVertexBuffer(sphere);
4199 HRESULT WINAPI D3DXCreateCylinder(LPDIRECT3DDEVICE9 device, FLOAT radius1, FLOAT radius2, FLOAT length, UINT slices,
4200 UINT stacks, LPD3DXMESH* mesh, LPD3DXBUFFER* adjacency)
4202 DWORD number_of_vertices, number_of_faces;
4204 ID3DXMesh *cylinder;
4205 struct vertex *vertices;
4207 float theta_step, theta_start;
4208 struct sincos_table theta;
4209 float delta_radius, radius, radius_step;
4210 float z, z_step, z_normal;
4214 TRACE("(%p, %f, %f, %f, %u, %u, %p, %p)\n", device, radius1, radius2, length, slices, stacks, mesh, adjacency);
4216 if (device == NULL || radius1 < 0.0f || radius2 < 0.0f || length < 0.0f || slices < 2 || stacks < 1 || mesh == NULL)
4218 return D3DERR_INVALIDCALL;
4223 FIXME("Case of adjacency != NULL not implemented.\n");
4227 number_of_vertices = 2 + (slices * (3 + stacks));
4228 number_of_faces = 2 * slices + stacks * (2 * slices);
4230 hr = D3DXCreateMeshFVF(number_of_faces, number_of_vertices, D3DXMESH_MANAGED,
4231 D3DFVF_XYZ | D3DFVF_NORMAL, device, &cylinder);
4237 hr = cylinder->lpVtbl->LockVertexBuffer(cylinder, 0, (LPVOID *)&vertices);
4240 cylinder->lpVtbl->Release(cylinder);
4244 hr = cylinder->lpVtbl->LockIndexBuffer(cylinder, 0, (LPVOID *)&faces);
4247 cylinder->lpVtbl->UnlockVertexBuffer(cylinder);
4248 cylinder->lpVtbl->Release(cylinder);
4252 /* theta = angle on xy plane wrt x axis */
4253 theta_step = -2 * M_PI / slices;
4254 theta_start = M_PI / 2;
4256 if (!compute_sincos_table(&theta, theta_start, theta_step, slices))
4258 cylinder->lpVtbl->UnlockIndexBuffer(cylinder);
4259 cylinder->lpVtbl->UnlockVertexBuffer(cylinder);
4260 cylinder->lpVtbl->Release(cylinder);
4261 return E_OUTOFMEMORY;
4267 delta_radius = radius1 - radius2;
4269 radius_step = delta_radius / stacks;
4272 z_step = length / stacks;
4273 z_normal = delta_radius / length;
4274 if (isnan(z_normal))
4279 vertices[vertex].normal.x = 0.0f;
4280 vertices[vertex].normal.y = 0.0f;
4281 vertices[vertex].normal.z = -1.0f;
4282 vertices[vertex].position.x = 0.0f;
4283 vertices[vertex].position.y = 0.0f;
4284 vertices[vertex++].position.z = z;
4286 for (slice = 0; slice < slices; slice++, vertex++)
4288 vertices[vertex].normal.x = 0.0f;
4289 vertices[vertex].normal.y = 0.0f;
4290 vertices[vertex].normal.z = -1.0f;
4291 vertices[vertex].position.x = radius * theta.cos[slice];
4292 vertices[vertex].position.y = radius * theta.sin[slice];
4293 vertices[vertex].position.z = z;
4298 faces[face][1] = slice;
4299 faces[face++][2] = slice + 1;
4304 faces[face][1] = slice;
4305 faces[face++][2] = 1;
4307 for (stack = 1; stack <= stacks+1; stack++)
4309 for (slice = 0; slice < slices; slice++, vertex++)
4311 vertices[vertex].normal.x = theta.cos[slice];
4312 vertices[vertex].normal.y = theta.sin[slice];
4313 vertices[vertex].normal.z = z_normal;
4314 D3DXVec3Normalize(&vertices[vertex].normal, &vertices[vertex].normal);
4315 vertices[vertex].position.x = radius * theta.cos[slice];
4316 vertices[vertex].position.y = radius * theta.sin[slice];
4317 vertices[vertex].position.z = z;
4319 if (stack > 1 && slice > 0)
4321 faces[face][0] = vertex_index(slices, slice-1, stack-1);
4322 faces[face][1] = vertex_index(slices, slice-1, stack);
4323 faces[face++][2] = vertex_index(slices, slice, stack-1);
4325 faces[face][0] = vertex_index(slices, slice, stack-1);
4326 faces[face][1] = vertex_index(slices, slice-1, stack);
4327 faces[face++][2] = vertex_index(slices, slice, stack);
4333 faces[face][0] = vertex_index(slices, slice-1, stack-1);
4334 faces[face][1] = vertex_index(slices, slice-1, stack);
4335 faces[face++][2] = vertex_index(slices, 0, stack-1);
4337 faces[face][0] = vertex_index(slices, 0, stack-1);
4338 faces[face][1] = vertex_index(slices, slice-1, stack);
4339 faces[face++][2] = vertex_index(slices, 0, stack);
4342 if (stack < stacks + 1)
4345 radius -= radius_step;
4349 for (slice = 0; slice < slices; slice++, vertex++)
4351 vertices[vertex].normal.x = 0.0f;
4352 vertices[vertex].normal.y = 0.0f;
4353 vertices[vertex].normal.z = 1.0f;
4354 vertices[vertex].position.x = radius * theta.cos[slice];
4355 vertices[vertex].position.y = radius * theta.sin[slice];
4356 vertices[vertex].position.z = z;
4360 faces[face][0] = vertex_index(slices, slice-1, stack);
4361 faces[face][1] = number_of_vertices - 1;
4362 faces[face++][2] = vertex_index(slices, slice, stack);
4366 vertices[vertex].position.x = 0.0f;
4367 vertices[vertex].position.y = 0.0f;
4368 vertices[vertex].position.z = z;
4369 vertices[vertex].normal.x = 0.0f;
4370 vertices[vertex].normal.y = 0.0f;
4371 vertices[vertex].normal.z = 1.0f;
4373 faces[face][0] = vertex_index(slices, slice-1, stack);
4374 faces[face][1] = number_of_vertices - 1;
4375 faces[face][2] = vertex_index(slices, 0, stack);
4377 free_sincos_table(&theta);
4378 cylinder->lpVtbl->UnlockIndexBuffer(cylinder);
4379 cylinder->lpVtbl->UnlockVertexBuffer(cylinder);
4385 HRESULT WINAPI D3DXCreateTeapot(LPDIRECT3DDEVICE9 device, LPD3DXMESH *mesh, LPD3DXBUFFER* adjacency)
4387 FIXME("(%p, %p, %p): stub\n", device, mesh, adjacency);
4392 HRESULT WINAPI D3DXCreateTextA(LPDIRECT3DDEVICE9 device,
4393 HDC hdc, LPCSTR text,
4394 FLOAT deviation, FLOAT extrusion,
4395 LPD3DXMESH *mesh, LPD3DXBUFFER *adjacency,
4396 LPGLYPHMETRICSFLOAT glyphmetrics)
4402 TRACE("(%p, %p, %s, %f, %f, %p, %p, %p)\n", device, hdc,
4403 debugstr_a(text), deviation, extrusion, mesh, adjacency, glyphmetrics);
4406 return D3DERR_INVALIDCALL;
4408 len = MultiByteToWideChar(CP_ACP, 0, text, -1, NULL, 0);
4409 textW = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
4410 MultiByteToWideChar(CP_ACP, 0, text, -1, textW, len);
4412 hr = D3DXCreateTextW(device, hdc, textW, deviation, extrusion,
4413 mesh, adjacency, glyphmetrics);
4414 HeapFree(GetProcessHeap(), 0, textW);
4420 POINTTYPE_CURVE = 0,
4422 POINTTYPE_CURVE_START,
4423 POINTTYPE_CURVE_END,
4424 POINTTYPE_CURVE_MIDDLE,
4430 enum pointtype corner;
4433 struct dynamic_array
4435 int count, capacity;
4439 /* is a dynamic_array */
4442 int count, capacity;
4443 struct point2d *items;
4446 /* is a dynamic_array */
4447 struct outline_array
4449 int count, capacity;
4450 struct outline *items;
4459 struct point2d_index
4461 struct outline *outline;
4465 struct point2d_index_array
4468 struct point2d_index *items;
4473 struct outline_array outlines;
4474 struct face_array faces;
4475 struct point2d_index_array ordered_vertices;
4479 /* is an dynamic_array */
4482 int count, capacity;
4486 /* complex polygons are split into monotone polygons, which have
4487 * at most 2 intersections with the vertical sweep line */
4488 struct triangulation
4490 struct word_array vertex_stack;
4491 BOOL last_on_top, merging;
4494 /* is an dynamic_array */
4495 struct triangulation_array
4497 int count, capacity;
4498 struct triangulation *items;
4500 struct glyphinfo *glyph;
4503 static BOOL reserve(struct dynamic_array *array, int count, int itemsize)
4505 if (count > array->capacity) {
4508 if (array->items && array->capacity) {
4509 new_capacity = max(array->capacity * 2, count);
4510 new_buffer = HeapReAlloc(GetProcessHeap(), 0, array->items, new_capacity * itemsize);
4512 new_capacity = max(16, count);
4513 new_buffer = HeapAlloc(GetProcessHeap(), 0, new_capacity * itemsize);
4517 array->items = new_buffer;
4518 array->capacity = new_capacity;
4523 static struct point2d *add_points(struct outline *array, int num)
4525 struct point2d *item;
4527 if (!reserve((struct dynamic_array *)array, array->count + num, sizeof(array->items[0])))
4530 item = &array->items[array->count];
4531 array->count += num;
4535 static struct outline *add_outline(struct outline_array *array)
4537 struct outline *item;
4539 if (!reserve((struct dynamic_array *)array, array->count + 1, sizeof(array->items[0])))
4542 item = &array->items[array->count++];
4543 ZeroMemory(item, sizeof(*item));
4547 static inline face *add_face(struct face_array *array)
4549 return &array->items[array->count++];
4552 static struct triangulation *add_triangulation(struct triangulation_array *array)
4554 struct triangulation *item;
4556 if (!reserve((struct dynamic_array *)array, array->count + 1, sizeof(array->items[0])))
4559 item = &array->items[array->count++];
4560 ZeroMemory(item, sizeof(*item));
4564 static HRESULT add_vertex_index(struct word_array *array, WORD vertex_index)
4566 if (!reserve((struct dynamic_array *)array, array->count + 1, sizeof(array->items[0])))
4567 return E_OUTOFMEMORY;
4569 array->items[array->count++] = vertex_index;
4573 /* assume fixed point numbers can be converted to float point in place */
4574 C_ASSERT(sizeof(FIXED) == sizeof(float));
4575 C_ASSERT(sizeof(POINTFX) == sizeof(D3DXVECTOR2));
4577 static inline D3DXVECTOR2 *convert_fixed_to_float(POINTFX *pt, int count, float emsquare)
4579 D3DXVECTOR2 *ret = (D3DXVECTOR2*)pt;
4581 D3DXVECTOR2 *pt_flt = (D3DXVECTOR2*)pt;
4582 pt_flt->x = (pt->x.value + pt->x.fract / (float)0x10000) / emsquare;
4583 pt_flt->y = (pt->y.value + pt->y.fract / (float)0x10000) / emsquare;
4589 static HRESULT add_bezier_points(struct outline *outline, const D3DXVECTOR2 *p1,
4590 const D3DXVECTOR2 *p2, const D3DXVECTOR2 *p3,
4591 float max_deviation_sq)
4593 D3DXVECTOR2 split1 = {0, 0}, split2 = {0, 0}, middle, vec;
4596 D3DXVec2Scale(&split1, D3DXVec2Add(&split1, p1, p2), 0.5f);
4597 D3DXVec2Scale(&split2, D3DXVec2Add(&split2, p2, p3), 0.5f);
4598 D3DXVec2Scale(&middle, D3DXVec2Add(&middle, &split1, &split2), 0.5f);
4600 deviation_sq = D3DXVec2LengthSq(D3DXVec2Subtract(&vec, &middle, p2));
4601 if (deviation_sq < max_deviation_sq) {
4602 struct point2d *pt = add_points(outline, 1);
4603 if (!pt) return E_OUTOFMEMORY;
4605 pt->corner = POINTTYPE_CURVE;
4606 /* the end point is omitted because the end line merges into the next segment of
4607 * the split bezier curve, and the end of the split bezier curve is added outside
4608 * this recursive function. */
4610 HRESULT hr = add_bezier_points(outline, p1, &split1, &middle, max_deviation_sq);
4611 if (hr != S_OK) return hr;
4612 hr = add_bezier_points(outline, &middle, &split2, p3, max_deviation_sq);
4613 if (hr != S_OK) return hr;
4619 static inline BOOL is_direction_similar(D3DXVECTOR2 *dir1, D3DXVECTOR2 *dir2, float cos_theta)
4621 /* dot product = cos(theta) */
4622 return D3DXVec2Dot(dir1, dir2) > cos_theta;
4625 static inline D3DXVECTOR2 *unit_vec2(D3DXVECTOR2 *dir, const D3DXVECTOR2 *pt1, const D3DXVECTOR2 *pt2)
4627 return D3DXVec2Normalize(D3DXVec2Subtract(dir, pt2, pt1), dir);
4637 static BOOL attempt_line_merge(struct outline *outline,
4639 const D3DXVECTOR2 *nextpt,
4641 const struct cos_table *table)
4643 D3DXVECTOR2 curdir, lastdir;
4644 struct point2d *prevpt, *pt;
4647 pt = &outline->items[pt_index];
4648 pt_index = (pt_index - 1 + outline->count) % outline->count;
4649 prevpt = &outline->items[pt_index];
4652 pt->corner = pt->corner != POINTTYPE_CORNER ? POINTTYPE_CURVE_MIDDLE : POINTTYPE_CURVE_START;
4654 if (outline->count < 2)
4657 /* remove last point if the next line continues the last line */
4658 unit_vec2(&lastdir, &prevpt->pos, &pt->pos);
4659 unit_vec2(&curdir, &pt->pos, nextpt);
4660 if (is_direction_similar(&lastdir, &curdir, table->cos_half))
4663 if (pt->corner == POINTTYPE_CURVE_END)
4664 prevpt->corner = pt->corner;
4665 if (prevpt->corner == POINTTYPE_CURVE_END && to_curve)
4666 prevpt->corner = POINTTYPE_CURVE_MIDDLE;
4670 if (outline->count < 2)
4673 pt_index = (pt_index - 1 + outline->count) % outline->count;
4674 prevpt = &outline->items[pt_index];
4675 unit_vec2(&lastdir, &prevpt->pos, &pt->pos);
4676 unit_vec2(&curdir, &pt->pos, nextpt);
4681 static HRESULT create_outline(struct glyphinfo *glyph, void *raw_outline, int datasize,
4682 float max_deviation_sq, float emsquare, const struct cos_table *cos_table)
4684 TTPOLYGONHEADER *header = (TTPOLYGONHEADER *)raw_outline;
4686 while ((char *)header < (char *)raw_outline + datasize)
4688 TTPOLYCURVE *curve = (TTPOLYCURVE *)(header + 1);
4689 struct point2d *lastpt, *pt;
4690 D3DXVECTOR2 lastdir;
4691 D3DXVECTOR2 *pt_flt;
4693 struct outline *outline = add_outline(&glyph->outlines);
4696 return E_OUTOFMEMORY;
4698 pt = add_points(outline, 1);
4700 return E_OUTOFMEMORY;
4701 pt_flt = convert_fixed_to_float(&header->pfxStart, 1, emsquare);
4703 pt->corner = POINTTYPE_CORNER;
4705 if (header->dwType != TT_POLYGON_TYPE)
4706 FIXME("Unknown header type %d\n", header->dwType);
4708 while ((char *)curve < (char *)header + header->cb)
4710 D3DXVECTOR2 bezier_start = outline->items[outline->count - 1].pos;
4711 BOOL to_curve = curve->wType != TT_PRIM_LINE && curve->cpfx > 1;
4714 curve = (TTPOLYCURVE *)&curve->apfx[curve->cpfx];
4718 pt_flt = convert_fixed_to_float(curve->apfx, curve->cpfx, emsquare);
4720 attempt_line_merge(outline, outline->count - 1, &pt_flt[0], to_curve, cos_table);
4725 int count = curve->cpfx;
4730 D3DXVECTOR2 bezier_end;
4732 D3DXVec2Scale(&bezier_end, D3DXVec2Add(&bezier_end, &pt_flt[j], &pt_flt[j+1]), 0.5f);
4733 hr = add_bezier_points(outline, &bezier_start, &pt_flt[j], &bezier_end, max_deviation_sq);
4736 bezier_start = bezier_end;
4740 hr = add_bezier_points(outline, &bezier_start, &pt_flt[j], &pt_flt[j+1], max_deviation_sq);
4744 pt = add_points(outline, 1);
4746 return E_OUTOFMEMORY;
4748 pt->pos = pt_flt[j];
4749 pt->corner = POINTTYPE_CURVE_END;
4751 pt = add_points(outline, curve->cpfx);
4753 return E_OUTOFMEMORY;
4754 for (j = 0; j < curve->cpfx; j++)
4756 pt->pos = pt_flt[j];
4757 pt->corner = POINTTYPE_CORNER;
4762 curve = (TTPOLYCURVE *)&curve->apfx[curve->cpfx];
4765 /* remove last point if the next line continues the last line */
4766 if (outline->count >= 3) {
4769 lastpt = &outline->items[outline->count - 1];
4770 pt = &outline->items[0];
4771 if (pt->pos.x == lastpt->pos.x && pt->pos.y == lastpt->pos.y) {
4772 if (lastpt->corner == POINTTYPE_CURVE_END)
4774 if (pt->corner == POINTTYPE_CURVE_START)
4775 pt->corner = POINTTYPE_CURVE_MIDDLE;
4777 pt->corner = POINTTYPE_CURVE_END;
4780 lastpt = &outline->items[outline->count - 1];
4782 /* outline closed with a line from end to start point */
4783 attempt_line_merge(outline, outline->count - 1, &pt->pos, FALSE, cos_table);
4785 lastpt = &outline->items[0];
4786 to_curve = lastpt->corner != POINTTYPE_CORNER && lastpt->corner != POINTTYPE_CURVE_END;
4787 if (lastpt->corner == POINTTYPE_CURVE_START)
4788 lastpt->corner = POINTTYPE_CORNER;
4789 pt = &outline->items[1];
4790 if (attempt_line_merge(outline, 0, &pt->pos, to_curve, cos_table))
4791 *lastpt = outline->items[outline->count];
4794 lastpt = &outline->items[outline->count - 1];
4795 pt = &outline->items[0];
4796 unit_vec2(&lastdir, &lastpt->pos, &pt->pos);
4797 for (j = 0; j < outline->count; j++)
4802 pt = &outline->items[(j + 1) % outline->count];
4803 unit_vec2(&curdir, &lastpt->pos, &pt->pos);
4805 switch (lastpt->corner)
4807 case POINTTYPE_CURVE_START:
4808 case POINTTYPE_CURVE_END:
4809 if (!is_direction_similar(&lastdir, &curdir, cos_table->cos_45))
4810 lastpt->corner = POINTTYPE_CORNER;
4812 case POINTTYPE_CURVE_MIDDLE:
4813 if (!is_direction_similar(&lastdir, &curdir, cos_table->cos_90))
4814 lastpt->corner = POINTTYPE_CORNER;
4816 lastpt->corner = POINTTYPE_CURVE;
4824 header = (TTPOLYGONHEADER *)((char *)header + header->cb);
4829 /* Get the y-distance from a line to a point */
4830 static float get_line_to_point_y_distance(D3DXVECTOR2 *line_pt1,
4831 D3DXVECTOR2 *line_pt2,
4834 D3DXVECTOR2 line_vec = {0, 0};
4838 D3DXVec2Subtract(&line_vec, line_pt2, line_pt1);
4839 line_pt_dx = point->x - line_pt1->x;
4840 line_y = line_pt1->y + (line_vec.y * line_pt_dx) / line_vec.x;
4841 return point->y - line_y;
4844 static D3DXVECTOR2 *get_indexed_point(struct point2d_index *pt_idx)
4846 return &pt_idx->outline->items[pt_idx->vertex].pos;
4849 static D3DXVECTOR2 *get_ordered_vertex(struct glyphinfo *glyph, WORD index)
4851 return get_indexed_point(&glyph->ordered_vertices.items[index]);
4854 static void remove_triangulation(struct triangulation_array *array, struct triangulation *item)
4856 HeapFree(GetProcessHeap(), 0, item->vertex_stack.items);
4857 MoveMemory(item, item + 1, (char*)&array->items[array->count] - (char*)(item + 1));
4861 static HRESULT triangulation_add_point(struct triangulation **t_ptr,
4862 struct triangulation_array *triangulations,
4866 struct glyphinfo *glyph = triangulations->glyph;
4867 struct triangulation *t = *t_ptr;
4872 if (t->last_on_top) {
4880 if (t->last_on_top != to_top && t->vertex_stack.count > 1) {
4881 /* consume all vertices on the stack */
4882 WORD last_pt = t->vertex_stack.items[0];
4884 for (i = 1; i < t->vertex_stack.count; i++)
4886 face = add_face(&glyph->faces);
4887 if (!face) return E_OUTOFMEMORY;
4888 (*face)[0] = vtx_idx;
4889 (*face)[f1] = last_pt;
4890 (*face)[f2] = last_pt = t->vertex_stack.items[i];
4892 t->vertex_stack.items[0] = last_pt;
4893 t->vertex_stack.count = 1;
4894 } else if (t->vertex_stack.count > 1) {
4895 int i = t->vertex_stack.count - 1;
4896 D3DXVECTOR2 *point = get_ordered_vertex(glyph, vtx_idx);
4897 WORD top_idx = t->vertex_stack.items[i--];
4898 D3DXVECTOR2 *top_pt = get_ordered_vertex(glyph, top_idx);
4902 WORD prev_idx = t->vertex_stack.items[i--];
4903 D3DXVECTOR2 *prev_pt = get_ordered_vertex(glyph, prev_idx);
4905 if (prev_pt->x != top_pt->x &&
4906 ((to_top && get_line_to_point_y_distance(prev_pt, top_pt, point) > 0) ||
4907 (!to_top && get_line_to_point_y_distance(prev_pt, top_pt, point) < 0)))
4910 face = add_face(&glyph->faces);
4911 if (!face) return E_OUTOFMEMORY;
4912 (*face)[0] = vtx_idx;
4913 (*face)[f1] = prev_idx;
4914 (*face)[f2] = top_idx;
4918 t->vertex_stack.count--;
4921 t->last_on_top = to_top;
4923 hr = add_vertex_index(&t->vertex_stack, vtx_idx);
4925 if (hr == S_OK && t->merging) {
4926 struct triangulation *t2;
4928 t2 = to_top ? t - 1 : t + 1;
4929 t2->merging = FALSE;
4930 hr = triangulation_add_point(&t2, triangulations, vtx_idx, to_top);
4931 if (hr != S_OK) return hr;
4932 remove_triangulation(triangulations, t);
4940 /* check if the point is next on the outline for either the top or bottom */
4941 static D3DXVECTOR2 *triangulation_get_next_point(struct triangulation *t, struct glyphinfo *glyph, BOOL on_top)
4943 int i = t->last_on_top == on_top ? t->vertex_stack.count - 1 : 0;
4944 WORD idx = t->vertex_stack.items[i];
4945 struct point2d_index *pt_idx = &glyph->ordered_vertices.items[idx];
4946 struct outline *outline = pt_idx->outline;
4949 i = (pt_idx->vertex + outline->count - 1) % outline->count;
4951 i = (pt_idx->vertex + 1) % outline->count;
4953 return &outline->items[i].pos;
4956 static int compare_vertex_indices(const void *a, const void *b)
4958 const struct point2d_index *idx1 = a, *idx2 = b;
4959 const D3DXVECTOR2 *p1 = &idx1->outline->items[idx1->vertex].pos;
4960 const D3DXVECTOR2 *p2 = &idx2->outline->items[idx2->vertex].pos;
4961 float diff = p1->x - p2->x;
4964 diff = p1->y - p2->y;
4966 return diff == 0.0f ? 0 : (diff > 0.0f ? -1 : 1);
4969 static HRESULT triangulate(struct triangulation_array *triangulations)
4973 struct glyphinfo *glyph = triangulations->glyph;
4974 int nb_vertices = 0;
4976 struct point2d_index *idx_ptr;
4978 for (i = 0; i < glyph->outlines.count; i++)
4979 nb_vertices += glyph->outlines.items[i].count;
4981 glyph->ordered_vertices.items = HeapAlloc(GetProcessHeap(), 0,
4982 nb_vertices * sizeof(*glyph->ordered_vertices.items));
4983 if (!glyph->ordered_vertices.items)
4984 return E_OUTOFMEMORY;
4986 idx_ptr = glyph->ordered_vertices.items;
4987 for (i = 0; i < glyph->outlines.count; i++)
4989 struct outline *outline = &glyph->outlines.items[i];
4992 idx_ptr->outline = outline;
4993 idx_ptr->vertex = 0;
4995 for (j = outline->count - 1; j > 0; j--)
4997 idx_ptr->outline = outline;
4998 idx_ptr->vertex = j;
5002 glyph->ordered_vertices.count = nb_vertices;
5004 /* Native implementation seems to try to create a triangle fan from
5005 * the first outline point if the glyph only has one outline. */
5006 if (glyph->outlines.count == 1)
5008 struct outline *outline = glyph->outlines.items;
5009 D3DXVECTOR2 *base = &outline->items[0].pos;
5010 D3DXVECTOR2 *last = &outline->items[1].pos;
5013 for (i = 2; i < outline->count; i++)
5015 D3DXVECTOR2 *next = &outline->items[i].pos;
5016 D3DXVECTOR2 v1 = {0.0f, 0.0f};
5017 D3DXVECTOR2 v2 = {0.0f, 0.0f};
5019 D3DXVec2Subtract(&v1, base, last);
5020 D3DXVec2Subtract(&v2, last, next);
5021 ccw = D3DXVec2CCW(&v1, &v2);
5029 glyph->faces.items = HeapAlloc(GetProcessHeap(), 0,
5030 (outline->count - 2) * sizeof(glyph->faces.items[0]));
5031 if (!glyph->faces.items)
5032 return E_OUTOFMEMORY;
5034 glyph->faces.count = outline->count - 2;
5035 for (i = 0; i < glyph->faces.count; i++)
5037 glyph->faces.items[i][0] = 0;
5038 glyph->faces.items[i][1] = i + 1;
5039 glyph->faces.items[i][2] = i + 2;
5045 /* Perform 2D polygon triangulation for complex glyphs.
5046 * Triangulation is performed using a sweep line concept, from right to left,
5047 * by processing vertices in sorted order. Complex polygons are split into
5048 * monotone polygons which are triangulated separately. */
5049 /* FIXME: The order of the faces is not consistent with the native implementation. */
5051 /* Reserve space for maximum possible faces from triangulation.
5052 * # faces for outer outlines = outline->count - 2
5053 * # faces for inner outlines = outline->count + 2
5054 * There must be at least 1 outer outline. */
5055 glyph->faces.items = HeapAlloc(GetProcessHeap(), 0,
5056 (nb_vertices + glyph->outlines.count * 2 - 4) * sizeof(glyph->faces.items[0]));
5057 if (!glyph->faces.items)
5058 return E_OUTOFMEMORY;
5060 qsort(glyph->ordered_vertices.items, nb_vertices,
5061 sizeof(glyph->ordered_vertices.items[0]), compare_vertex_indices);
5062 for (sweep_idx = 0; sweep_idx < glyph->ordered_vertices.count; sweep_idx++)
5065 int end = triangulations->count;
5069 D3DXVECTOR2 *sweep_vtx = get_ordered_vertex(glyph, sweep_idx);
5070 int current = (start + end) / 2;
5071 struct triangulation *t = &triangulations->items[current];
5072 BOOL on_top_outline = FALSE;
5073 D3DXVECTOR2 *top_next, *bottom_next;
5074 WORD top_idx, bottom_idx;
5076 if (t->merging && t->last_on_top)
5077 top_next = triangulation_get_next_point(t + 1, glyph, TRUE);
5079 top_next = triangulation_get_next_point(t, glyph, TRUE);
5080 if (sweep_vtx == top_next)
5082 if (t->merging && t->last_on_top)
5084 hr = triangulation_add_point(&t, triangulations, sweep_idx, TRUE);
5085 if (hr != S_OK) return hr;
5087 if (t + 1 < &triangulations->items[triangulations->count] &&
5088 triangulation_get_next_point(t + 1, glyph, FALSE) == sweep_vtx)
5090 /* point also on bottom outline of higher triangulation */
5091 struct triangulation *t2 = t + 1;
5092 hr = triangulation_add_point(&t2, triangulations, sweep_idx, FALSE);
5093 if (hr != S_OK) return hr;
5098 on_top_outline = TRUE;
5101 if (t->merging && !t->last_on_top)
5102 bottom_next = triangulation_get_next_point(t - 1, glyph, FALSE);
5104 bottom_next = triangulation_get_next_point(t, glyph, FALSE);
5105 if (sweep_vtx == bottom_next)
5107 if (t->merging && !t->last_on_top)
5109 if (on_top_outline) {
5110 /* outline finished */
5111 remove_triangulation(triangulations, t);
5115 hr = triangulation_add_point(&t, triangulations, sweep_idx, FALSE);
5116 if (hr != S_OK) return hr;
5118 if (t > triangulations->items &&
5119 triangulation_get_next_point(t - 1, glyph, TRUE) == sweep_vtx)
5121 struct triangulation *t2 = t - 1;
5122 /* point also on top outline of lower triangulation */
5123 hr = triangulation_add_point(&t2, triangulations, sweep_idx, TRUE);
5124 if (hr != S_OK) return hr;
5125 t = t2 + 1; /* t may be invalidated by triangulation merging */
5135 if (t->last_on_top) {
5136 top_idx = t->vertex_stack.items[t->vertex_stack.count - 1];
5137 bottom_idx = t->vertex_stack.items[0];
5139 top_idx = t->vertex_stack.items[0];
5140 bottom_idx = t->vertex_stack.items[t->vertex_stack.count - 1];
5143 /* check if the point is inside or outside this polygon */
5144 if (get_line_to_point_y_distance(get_ordered_vertex(glyph, top_idx),
5145 top_next, sweep_vtx) > 0)
5147 start = current + 1;
5148 } else if (get_line_to_point_y_distance(get_ordered_vertex(glyph, bottom_idx),
5149 bottom_next, sweep_vtx) < 0)
5152 } else if (t->merging) {
5153 /* inside, so cancel merging */
5154 struct triangulation *t2 = t->last_on_top ? t + 1 : t - 1;
5156 t2->merging = FALSE;
5157 hr = triangulation_add_point(&t, triangulations, sweep_idx, t->last_on_top);
5158 if (hr != S_OK) return hr;
5159 hr = triangulation_add_point(&t2, triangulations, sweep_idx, t2->last_on_top);
5160 if (hr != S_OK) return hr;
5163 /* inside, so split polygon into two monotone parts */
5164 struct triangulation *t2 = add_triangulation(triangulations);
5165 if (!t2) return E_OUTOFMEMORY;
5166 MoveMemory(t + 1, t, (char*)(t2 + 1) - (char*)t);
5167 if (t->last_on_top) {
5174 ZeroMemory(&t2->vertex_stack, sizeof(t2->vertex_stack));
5175 hr = add_vertex_index(&t2->vertex_stack, t->vertex_stack.items[t->vertex_stack.count - 1]);
5176 if (hr != S_OK) return hr;
5177 hr = add_vertex_index(&t2->vertex_stack, sweep_idx);
5178 if (hr != S_OK) return hr;
5179 t2->last_on_top = !t->last_on_top;
5181 hr = triangulation_add_point(&t, triangulations, sweep_idx, t->last_on_top);
5182 if (hr != S_OK) return hr;
5188 struct triangulation *t;
5189 struct triangulation *t2 = add_triangulation(triangulations);
5190 if (!t2) return E_OUTOFMEMORY;
5191 t = &triangulations->items[start];
5192 MoveMemory(t + 1, t, (char*)(t2 + 1) - (char*)t);
5193 ZeroMemory(t, sizeof(*t));
5194 hr = add_vertex_index(&t->vertex_stack, sweep_idx);
5195 if (hr != S_OK) return hr;
5201 HRESULT WINAPI D3DXCreateTextW(LPDIRECT3DDEVICE9 device,
5202 HDC hdc, LPCWSTR text,
5203 FLOAT deviation, FLOAT extrusion,
5204 LPD3DXMESH *mesh_ptr, LPD3DXBUFFER *adjacency,
5205 LPGLYPHMETRICSFLOAT glyphmetrics)
5208 ID3DXMesh *mesh = NULL;
5209 DWORD nb_vertices, nb_faces;
5210 DWORD nb_front_faces, nb_corners, nb_outline_points;
5211 struct vertex *vertices = NULL;
5216 OUTLINETEXTMETRICW otm;
5217 HFONT font = NULL, oldfont = NULL;
5218 const MAT2 identity = {{0, 1}, {0, 0}, {0, 0}, {0, 1}};
5219 void *raw_outline = NULL;
5221 struct glyphinfo *glyphs = NULL;
5223 struct triangulation_array triangulations = {0, 0, NULL};
5225 struct vertex *vertex_ptr;
5227 float max_deviation_sq;
5228 const struct cos_table cos_table = {
5229 cos(D3DXToRadian(0.5f)),
5230 cos(D3DXToRadian(45.0f)),
5231 cos(D3DXToRadian(90.0f)),
5235 TRACE("(%p, %p, %s, %f, %f, %p, %p, %p)\n", device, hdc,
5236 debugstr_w(text), deviation, extrusion, mesh_ptr, adjacency, glyphmetrics);
5238 if (!device || !hdc || !text || !*text || deviation < 0.0f || extrusion < 0.0f || !mesh_ptr)
5239 return D3DERR_INVALIDCALL;
5243 FIXME("Case of adjacency != NULL not implemented.\n");
5247 if (!GetObjectW(GetCurrentObject(hdc, OBJ_FONT), sizeof(lf), &lf) ||
5248 !GetOutlineTextMetricsW(hdc, sizeof(otm), &otm))
5250 return D3DERR_INVALIDCALL;
5253 if (deviation == 0.0f)
5254 deviation = 1.0f / otm.otmEMSquare;
5255 max_deviation_sq = deviation * deviation;
5257 lf.lfHeight = otm.otmEMSquare;
5259 font = CreateFontIndirectW(&lf);
5264 oldfont = SelectObject(hdc, font);
5266 textlen = strlenW(text);
5267 for (i = 0; i < textlen; i++)
5269 int datasize = GetGlyphOutlineW(hdc, text[i], GGO_NATIVE, &gm, 0, NULL, &identity);
5271 return D3DERR_INVALIDCALL;
5272 if (bufsize < datasize)
5275 if (!bufsize) { /* e.g. text == " " */
5276 hr = D3DERR_INVALIDCALL;
5280 glyphs = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, textlen * sizeof(*glyphs));
5281 raw_outline = HeapAlloc(GetProcessHeap(), 0, bufsize);
5282 if (!glyphs || !raw_outline) {
5288 for (i = 0; i < textlen; i++)
5290 /* get outline points from data returned from GetGlyphOutline */
5293 glyphs[i].offset_x = offset_x;
5295 datasize = GetGlyphOutlineW(hdc, text[i], GGO_NATIVE, &gm, bufsize, raw_outline, &identity);
5296 hr = create_outline(&glyphs[i], raw_outline, datasize,
5297 max_deviation_sq, otm.otmEMSquare, &cos_table);
5298 if (hr != S_OK) goto error;
5300 triangulations.glyph = &glyphs[i];
5301 hr = triangulate(&triangulations);
5302 if (hr != S_OK) goto error;
5303 if (triangulations.count) {
5304 ERR("%d incomplete triangulations of glyph (%u).\n", triangulations.count, text[i]);
5305 triangulations.count = 0;
5310 glyphmetrics[i].gmfBlackBoxX = gm.gmBlackBoxX / (float)otm.otmEMSquare;
5311 glyphmetrics[i].gmfBlackBoxY = gm.gmBlackBoxY / (float)otm.otmEMSquare;
5312 glyphmetrics[i].gmfptGlyphOrigin.x = gm.gmptGlyphOrigin.x / (float)otm.otmEMSquare;
5313 glyphmetrics[i].gmfptGlyphOrigin.y = gm.gmptGlyphOrigin.y / (float)otm.otmEMSquare;
5314 glyphmetrics[i].gmfCellIncX = gm.gmCellIncX / (float)otm.otmEMSquare;
5315 glyphmetrics[i].gmfCellIncY = gm.gmCellIncY / (float)otm.otmEMSquare;
5317 offset_x += gm.gmCellIncX / (float)otm.otmEMSquare;
5320 /* corner points need an extra vertex for the different side faces normals */
5322 nb_outline_points = 0;
5324 for (i = 0; i < textlen; i++)
5327 nb_outline_points += glyphs[i].ordered_vertices.count;
5328 nb_front_faces += glyphs[i].faces.count;
5329 for (j = 0; j < glyphs[i].outlines.count; j++)
5332 struct outline *outline = &glyphs[i].outlines.items[j];
5333 nb_corners++; /* first outline point always repeated as a corner */
5334 for (k = 1; k < outline->count; k++)
5335 if (outline->items[k].corner)
5340 nb_vertices = (nb_outline_points + nb_corners) * 2 + nb_outline_points * 2;
5341 nb_faces = nb_outline_points * 2 + nb_front_faces * 2;
5344 hr = D3DXCreateMeshFVF(nb_faces, nb_vertices, D3DXMESH_MANAGED,
5345 D3DFVF_XYZ | D3DFVF_NORMAL, device, &mesh);
5349 hr = mesh->lpVtbl->LockVertexBuffer(mesh, 0, (LPVOID *)&vertices);
5353 hr = mesh->lpVtbl->LockIndexBuffer(mesh, 0, (LPVOID *)&faces);
5357 /* convert 2D vertices and faces into 3D mesh */
5358 vertex_ptr = vertices;
5360 if (extrusion == 0.0f) {
5367 for (i = 0; i < textlen; i++)
5371 struct vertex *back_vertices;
5374 /* side vertices and faces */
5375 for (j = 0; j < glyphs[i].outlines.count; j++)
5377 struct vertex *outline_vertices = vertex_ptr;
5378 struct outline *outline = &glyphs[i].outlines.items[j];
5380 struct point2d *prevpt = &outline->items[outline->count - 1];
5381 struct point2d *pt = &outline->items[0];
5383 for (k = 1; k <= outline->count; k++)
5386 struct point2d *nextpt = &outline->items[k % outline->count];
5387 WORD vtx_idx = vertex_ptr - vertices;
5390 if (pt->corner == POINTTYPE_CURVE_START)
5391 D3DXVec2Subtract(&vec, &pt->pos, &prevpt->pos);
5392 else if (pt->corner)
5393 D3DXVec2Subtract(&vec, &nextpt->pos, &pt->pos);
5395 D3DXVec2Subtract(&vec, &nextpt->pos, &prevpt->pos);
5396 D3DXVec2Normalize(&vec, &vec);
5397 vtx.normal.x = -vec.y;
5398 vtx.normal.y = vec.x;
5401 vtx.position.x = pt->pos.x + glyphs[i].offset_x;
5402 vtx.position.y = pt->pos.y;
5404 *vertex_ptr++ = vtx;
5406 vtx.position.z = -extrusion;
5407 *vertex_ptr++ = vtx;
5409 vtx.position.x = nextpt->pos.x + glyphs[i].offset_x;
5410 vtx.position.y = nextpt->pos.y;
5411 if (pt->corner && nextpt->corner && nextpt->corner != POINTTYPE_CURVE_END) {
5412 vtx.position.z = -extrusion;
5413 *vertex_ptr++ = vtx;
5415 *vertex_ptr++ = vtx;
5417 (*face_ptr)[0] = vtx_idx;
5418 (*face_ptr)[1] = vtx_idx + 2;
5419 (*face_ptr)[2] = vtx_idx + 1;
5422 (*face_ptr)[0] = vtx_idx;
5423 (*face_ptr)[1] = vtx_idx + 3;
5424 (*face_ptr)[2] = vtx_idx + 2;
5427 if (nextpt->corner) {
5428 if (nextpt->corner == POINTTYPE_CURVE_END) {
5429 D3DXVECTOR2 *nextpt2 = &outline->items[(k + 1) % outline->count].pos;
5430 D3DXVec2Subtract(&vec, nextpt2, &nextpt->pos);
5432 D3DXVec2Subtract(&vec, &nextpt->pos, &pt->pos);
5434 D3DXVec2Normalize(&vec, &vec);
5435 vtx.normal.x = -vec.y;
5436 vtx.normal.y = vec.x;
5439 *vertex_ptr++ = vtx;
5440 vtx.position.z = -extrusion;
5441 *vertex_ptr++ = vtx;
5444 (*face_ptr)[0] = vtx_idx;
5445 (*face_ptr)[1] = vtx_idx + 3;
5446 (*face_ptr)[2] = vtx_idx + 1;
5449 (*face_ptr)[0] = vtx_idx;
5450 (*face_ptr)[1] = vtx_idx + 2;
5451 (*face_ptr)[2] = vtx_idx + 3;
5459 *vertex_ptr++ = *outline_vertices++;
5460 *vertex_ptr++ = *outline_vertices++;
5464 /* back vertices and faces */
5465 back_faces = face_ptr;
5466 back_vertices = vertex_ptr;
5467 for (j = 0; j < glyphs[i].ordered_vertices.count; j++)
5469 D3DXVECTOR2 *pt = get_ordered_vertex(&glyphs[i], j);
5470 vertex_ptr->position.x = pt->x + glyphs[i].offset_x;
5471 vertex_ptr->position.y = pt->y;
5472 vertex_ptr->position.z = 0;
5473 vertex_ptr->normal.x = 0;
5474 vertex_ptr->normal.y = 0;
5475 vertex_ptr->normal.z = 1;
5478 count = back_vertices - vertices;
5479 for (j = 0; j < glyphs[i].faces.count; j++)
5481 face *f = &glyphs[i].faces.items[j];
5482 (*face_ptr)[0] = (*f)[0] + count;
5483 (*face_ptr)[1] = (*f)[1] + count;
5484 (*face_ptr)[2] = (*f)[2] + count;
5488 /* front vertices and faces */
5489 j = count = vertex_ptr - back_vertices;
5492 vertex_ptr->position.x = back_vertices->position.x;
5493 vertex_ptr->position.y = back_vertices->position.y;
5494 vertex_ptr->position.z = -extrusion;
5495 vertex_ptr->normal.x = 0;
5496 vertex_ptr->normal.y = 0;
5497 vertex_ptr->normal.z = extrusion == 0.0f ? 1.0f : -1.0f;
5501 j = face_ptr - back_faces;
5504 (*face_ptr)[0] = (*back_faces)[0] + count;
5505 (*face_ptr)[1] = (*back_faces)[f1] + count;
5506 (*face_ptr)[2] = (*back_faces)[f2] + count;
5516 if (faces) mesh->lpVtbl->UnlockIndexBuffer(mesh);
5517 if (vertices) mesh->lpVtbl->UnlockVertexBuffer(mesh);
5518 if (hr != D3D_OK) mesh->lpVtbl->Release(mesh);
5521 for (i = 0; i < textlen; i++)
5524 for (j = 0; j < glyphs[i].outlines.count; j++)
5525 HeapFree(GetProcessHeap(), 0, glyphs[i].outlines.items[j].items);
5526 HeapFree(GetProcessHeap(), 0, glyphs[i].outlines.items);
5527 HeapFree(GetProcessHeap(), 0, glyphs[i].faces.items);
5528 HeapFree(GetProcessHeap(), 0, glyphs[i].ordered_vertices.items);
5530 HeapFree(GetProcessHeap(), 0, glyphs);
5532 if (triangulations.items) {
5534 for (i = 0; i < triangulations.count; i++)
5535 HeapFree(GetProcessHeap(), 0, triangulations.items[i].vertex_stack.items);
5536 HeapFree(GetProcessHeap(), 0, triangulations.items);
5538 HeapFree(GetProcessHeap(), 0, raw_outline);
5539 if (oldfont) SelectObject(hdc, oldfont);
5540 if (font) DeleteObject(font);