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, j = 1; i < 2 && (j+1) > 0; i++, j--)
664 DWORD from = face_base + (edge + j) % 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 = This->numfaces - 1; face + 1 > 0; face--)
764 hr = propagate_face_vertices(adjacency, point_reps, indices, new_indices, face, This->numfaces);
765 if (FAILED(hr)) goto cleanup;
770 if (This->options & D3DXMESH_32BIT)
772 if (indices) iface->lpVtbl->UnlockIndexBuffer(iface);
776 if (indices_16bit) iface->lpVtbl->UnlockIndexBuffer(iface);
777 HeapFree(GetProcessHeap(), 0, indices);
779 HeapFree(GetProcessHeap(), 0, new_indices);
783 struct vertex_metadata {
786 DWORD first_shared_index;
789 static int compare_vertex_keys(const void *a, const void *b)
791 const struct vertex_metadata *left = a;
792 const struct vertex_metadata *right = b;
793 if (left->key == right->key)
795 return left->key < right->key ? -1 : 1;
798 static HRESULT WINAPI ID3DXMeshImpl_GenerateAdjacency(ID3DXMesh *iface, FLOAT epsilon, DWORD *adjacency)
800 ID3DXMeshImpl *This = impl_from_ID3DXMesh(iface);
802 BYTE *vertices = NULL;
803 const DWORD *indices = NULL;
806 /* sort the vertices by (x + y + z) to quickly find coincident vertices */
807 struct vertex_metadata *sorted_vertices;
808 /* shared_indices links together identical indices in the index buffer so
809 * that adjacency checks can be limited to faces sharing a vertex */
810 DWORD *shared_indices = NULL;
811 const FLOAT epsilon_sq = epsilon * epsilon;
814 TRACE("(%p)->(%f,%p)\n", This, epsilon, adjacency);
817 return D3DERR_INVALIDCALL;
819 buffer_size = This->numfaces * 3 * sizeof(*shared_indices) + This->numvertices * sizeof(*sorted_vertices);
820 if (!(This->options & D3DXMESH_32BIT))
821 buffer_size += This->numfaces * 3 * sizeof(*indices);
822 shared_indices = HeapAlloc(GetProcessHeap(), 0, buffer_size);
824 return E_OUTOFMEMORY;
825 sorted_vertices = (struct vertex_metadata*)(shared_indices + This->numfaces * 3);
827 hr = iface->lpVtbl->LockVertexBuffer(iface, D3DLOCK_READONLY, (void**)&vertices);
828 if (FAILED(hr)) goto cleanup;
829 hr = iface->lpVtbl->LockIndexBuffer(iface, D3DLOCK_READONLY, (void**)&indices);
830 if (FAILED(hr)) goto cleanup;
832 if (!(This->options & D3DXMESH_32BIT)) {
833 const WORD *word_indices = (const WORD*)indices;
834 DWORD *dword_indices = (DWORD*)(sorted_vertices + This->numvertices);
835 indices = dword_indices;
836 for (i = 0; i < This->numfaces * 3; i++)
837 *dword_indices++ = *word_indices++;
840 vertex_size = iface->lpVtbl->GetNumBytesPerVertex(iface);
841 for (i = 0; i < This->numvertices; i++) {
842 D3DXVECTOR3 *vertex = (D3DXVECTOR3*)(vertices + vertex_size * i);
843 sorted_vertices[i].first_shared_index = -1;
844 sorted_vertices[i].key = vertex->x + vertex->y + vertex->z;
845 sorted_vertices[i].vertex_index = i;
847 for (i = 0; i < This->numfaces * 3; i++) {
848 DWORD *first_shared_index = &sorted_vertices[indices[i]].first_shared_index;
849 shared_indices[i] = *first_shared_index;
850 *first_shared_index = i;
853 qsort(sorted_vertices, This->numvertices, sizeof(*sorted_vertices), compare_vertex_keys);
855 for (i = 0; i < This->numvertices; i++) {
856 struct vertex_metadata *sorted_vertex_a = &sorted_vertices[i];
857 D3DXVECTOR3 *vertex_a = (D3DXVECTOR3*)(vertices + sorted_vertex_a->vertex_index * vertex_size);
858 DWORD shared_index_a = sorted_vertex_a->first_shared_index;
860 while (shared_index_a != -1) {
862 DWORD shared_index_b = shared_indices[shared_index_a];
863 struct vertex_metadata *sorted_vertex_b = sorted_vertex_a;
866 while (shared_index_b != -1) {
867 /* faces are adjacent if they have another coincident vertex */
868 DWORD base_a = (shared_index_a / 3) * 3;
869 DWORD base_b = (shared_index_b / 3) * 3;
870 BOOL adjacent = FALSE;
873 for (k = 0; k < 3; k++) {
874 if (adjacency[base_b + k] == shared_index_a / 3) {
880 for (k = 1; k <= 2; k++) {
881 DWORD vertex_index_a = base_a + (shared_index_a + k) % 3;
882 DWORD vertex_index_b = base_b + (shared_index_b + (3 - k)) % 3;
883 adjacent = indices[vertex_index_a] == indices[vertex_index_b];
884 if (!adjacent && epsilon >= 0.0f) {
885 D3DXVECTOR3 delta = {0.0f, 0.0f, 0.0f};
888 D3DXVec3Subtract(&delta,
889 (D3DXVECTOR3*)(vertices + indices[vertex_index_a] * vertex_size),
890 (D3DXVECTOR3*)(vertices + indices[vertex_index_b] * vertex_size));
891 length_sq = D3DXVec3LengthSq(&delta);
892 adjacent = epsilon == 0.0f ? length_sq == 0.0f : length_sq < epsilon_sq;
895 DWORD adj_a = base_a + 2 - (vertex_index_a + shared_index_a + 1) % 3;
896 DWORD adj_b = base_b + 2 - (vertex_index_b + shared_index_b + 1) % 3;
897 if (adjacency[adj_a] == -1 && adjacency[adj_b] == -1) {
898 adjacency[adj_a] = base_b / 3;
899 adjacency[adj_b] = base_a / 3;
906 shared_index_b = shared_indices[shared_index_b];
908 while (++j < This->numvertices) {
909 D3DXVECTOR3 *vertex_b;
912 if (sorted_vertex_b->key - sorted_vertex_a->key > epsilon * 3.0f) {
913 /* no more coincident vertices to try */
914 j = This->numvertices;
917 /* check for coincidence */
918 vertex_b = (D3DXVECTOR3*)(vertices + sorted_vertex_b->vertex_index * vertex_size);
919 if (fabsf(vertex_a->x - vertex_b->x) <= epsilon &&
920 fabsf(vertex_a->y - vertex_b->y) <= epsilon &&
921 fabsf(vertex_a->z - vertex_b->z) <= epsilon)
926 if (j >= This->numvertices)
928 shared_index_b = sorted_vertex_b->first_shared_index;
931 sorted_vertex_a->first_shared_index = shared_indices[sorted_vertex_a->first_shared_index];
932 shared_index_a = sorted_vertex_a->first_shared_index;
938 if (indices) iface->lpVtbl->UnlockIndexBuffer(iface);
939 if (vertices) iface->lpVtbl->UnlockVertexBuffer(iface);
940 HeapFree(GetProcessHeap(), 0, shared_indices);
944 static HRESULT WINAPI ID3DXMeshImpl_UpdateSemantics(ID3DXMesh *iface, D3DVERTEXELEMENT9 declaration[MAX_FVF_DECL_SIZE])
947 ID3DXMeshImpl *This = impl_from_ID3DXMesh(iface);
948 UINT vertex_declaration_size;
951 TRACE("(%p)->(%p)\n", This, declaration);
955 WARN("Invalid declaration. Can't use NULL declaration.\n");
956 return D3DERR_INVALIDCALL;
959 /* New declaration must be same size as original */
960 vertex_declaration_size = D3DXGetDeclVertexSize(declaration, declaration[0].Stream);
961 if (vertex_declaration_size != This->vertex_declaration_size)
963 WARN("Invalid declaration. New vertex size does not match the original vertex size.\n");
964 return D3DERR_INVALIDCALL;
967 /* New declaration must not contain non-zero Stream value */
968 for (i = 0; declaration[i].Stream != 0xff; i++)
970 if (declaration[i].Stream != 0)
972 WARN("Invalid declaration. New declaration contains non-zero Stream value.\n");
973 return D3DERR_INVALIDCALL;
977 This->num_elem = i + 1;
978 copy_declaration(This->cached_declaration, declaration, This->num_elem);
980 if (This->vertex_declaration)
981 IDirect3DVertexDeclaration9_Release(This->vertex_declaration);
983 /* An application can pass an invalid declaration to UpdateSemantics and
984 * still expect D3D_OK (see tests). If the declaration is invalid, then
985 * subsequent calls to DrawSubset will fail. This is handled by setting the
986 * vertex declaration to NULL.
987 * GetDeclaration, GetNumBytesPerVertex must, however, use the new
988 * invalid declaration. This is handled by them using the cached vertex
989 * declaration instead of the actual vertex declaration.
991 hr = IDirect3DDevice9_CreateVertexDeclaration(This->device,
993 &This->vertex_declaration);
996 WARN("Using invalid declaration. Calls to DrawSubset will fail.\n");
997 This->vertex_declaration = NULL;
1004 static HRESULT WINAPI ID3DXMeshImpl_LockAttributeBuffer(ID3DXMesh *iface, DWORD flags, DWORD **data)
1006 ID3DXMeshImpl *This = impl_from_ID3DXMesh(iface);
1008 TRACE("(%p)->(%u,%p)\n", This, flags, data);
1010 InterlockedIncrement(&This->attrib_buffer_lock_count);
1012 if (!(flags & D3DLOCK_READONLY)) {
1013 D3DXATTRIBUTERANGE *attrib_table = This->attrib_table;
1014 This->attrib_table_size = 0;
1015 This->attrib_table = NULL;
1016 HeapFree(GetProcessHeap(), 0, attrib_table);
1019 *data = This->attrib_buffer;
1024 static HRESULT WINAPI ID3DXMeshImpl_UnlockAttributeBuffer(ID3DXMesh *iface)
1026 ID3DXMeshImpl *This = impl_from_ID3DXMesh(iface);
1029 TRACE("(%p)\n", This);
1031 lock_count = InterlockedDecrement(&This->attrib_buffer_lock_count);
1033 if (lock_count < 0) {
1034 InterlockedIncrement(&This->attrib_buffer_lock_count);
1035 return D3DERR_INVALIDCALL;
1041 static HRESULT WINAPI ID3DXMeshImpl_Optimize(ID3DXMesh *iface, DWORD flags, CONST DWORD *adjacency_in, DWORD *adjacency_out,
1042 DWORD *face_remap, LPD3DXBUFFER *vertex_remap, LPD3DXMESH *opt_mesh)
1044 ID3DXMeshImpl *This = impl_from_ID3DXMesh(iface);
1046 D3DVERTEXELEMENT9 declaration[MAX_FVF_DECL_SIZE] = { D3DDECL_END() };
1047 ID3DXMesh *optimized_mesh;
1049 TRACE("(%p)->(%x,%p,%p,%p,%p,%p)\n", This, flags, adjacency_in, adjacency_out, face_remap, vertex_remap, opt_mesh);
1052 return D3DERR_INVALIDCALL;
1054 hr = iface->lpVtbl->GetDeclaration(iface, declaration);
1055 if (FAILED(hr)) return hr;
1057 hr = iface->lpVtbl->CloneMesh(iface, This->options, declaration, This->device, &optimized_mesh);
1058 if (FAILED(hr)) return hr;
1060 hr = optimized_mesh->lpVtbl->OptimizeInplace(optimized_mesh, flags, adjacency_in, adjacency_out, face_remap, vertex_remap);
1062 *opt_mesh = optimized_mesh;
1064 IUnknown_Release(optimized_mesh);
1068 /* Creates a vertex_remap that removes unused vertices.
1069 * Indices are updated according to the vertex_remap. */
1070 static HRESULT compact_mesh(ID3DXMeshImpl *This, DWORD *indices, DWORD *new_num_vertices, ID3DXBuffer **vertex_remap)
1073 DWORD *vertex_remap_ptr;
1074 DWORD num_used_vertices;
1077 hr = D3DXCreateBuffer(This->numvertices * sizeof(DWORD), vertex_remap);
1078 if (FAILED(hr)) return hr;
1079 vertex_remap_ptr = ID3DXBuffer_GetBufferPointer(*vertex_remap);
1081 for (i = 0; i < This->numfaces * 3; i++)
1082 vertex_remap_ptr[indices[i]] = 1;
1084 /* create old->new vertex mapping */
1085 num_used_vertices = 0;
1086 for (i = 0; i < This->numvertices; i++) {
1087 if (vertex_remap_ptr[i])
1088 vertex_remap_ptr[i] = num_used_vertices++;
1090 vertex_remap_ptr[i] = -1;
1092 /* convert indices */
1093 for (i = 0; i < This->numfaces * 3; i++)
1094 indices[i] = vertex_remap_ptr[indices[i]];
1096 /* create new->old vertex mapping */
1097 num_used_vertices = 0;
1098 for (i = 0; i < This->numvertices; i++) {
1099 if (vertex_remap_ptr[i] != -1)
1100 vertex_remap_ptr[num_used_vertices++] = i;
1102 for (i = num_used_vertices; i < This->numvertices; i++)
1103 vertex_remap_ptr[i] = -1;
1105 *new_num_vertices = num_used_vertices;
1110 /* count the number of unique attribute values in a sorted attribute buffer */
1111 static DWORD count_attributes(const DWORD *attrib_buffer, DWORD numfaces)
1113 DWORD last_attribute = attrib_buffer[0];
1114 DWORD attrib_table_size = 1;
1116 for (i = 1; i < numfaces; i++) {
1117 if (attrib_buffer[i] != last_attribute) {
1118 last_attribute = attrib_buffer[i];
1119 attrib_table_size++;
1122 return attrib_table_size;
1125 static void fill_attribute_table(DWORD *attrib_buffer, DWORD numfaces, void *indices,
1126 BOOL is_32bit_indices, D3DXATTRIBUTERANGE *attrib_table)
1128 DWORD attrib_table_size = 0;
1129 DWORD last_attribute = attrib_buffer[0];
1130 DWORD min_vertex, max_vertex;
1133 attrib_table[0].AttribId = last_attribute;
1134 attrib_table[0].FaceStart = 0;
1135 min_vertex = (DWORD)-1;
1137 for (i = 0; i < numfaces; i++) {
1140 if (attrib_buffer[i] != last_attribute) {
1141 last_attribute = attrib_buffer[i];
1142 attrib_table[attrib_table_size].FaceCount = i - attrib_table[attrib_table_size].FaceStart;
1143 attrib_table[attrib_table_size].VertexStart = min_vertex;
1144 attrib_table[attrib_table_size].VertexCount = max_vertex - min_vertex + 1;
1145 attrib_table_size++;
1146 attrib_table[attrib_table_size].AttribId = attrib_buffer[i];
1147 attrib_table[attrib_table_size].FaceStart = i;
1148 min_vertex = (DWORD)-1;
1151 for (j = 0; j < 3; j++) {
1152 DWORD vertex_index = is_32bit_indices ? ((DWORD*)indices)[i * 3 + j] : ((WORD*)indices)[i * 3 + j];
1153 if (vertex_index < min_vertex)
1154 min_vertex = vertex_index;
1155 if (vertex_index > max_vertex)
1156 max_vertex = vertex_index;
1159 attrib_table[attrib_table_size].FaceCount = i - attrib_table[attrib_table_size].FaceStart;
1160 attrib_table[attrib_table_size].VertexStart = min_vertex;
1161 attrib_table[attrib_table_size].VertexCount = max_vertex - min_vertex + 1;
1162 attrib_table_size++;
1165 static int attrib_entry_compare(const DWORD **a, const DWORD **b)
1167 const DWORD *ptr_a = *a;
1168 const DWORD *ptr_b = *b;
1169 int delta = *ptr_a - *ptr_b;
1174 delta = ptr_a - ptr_b; /* for stable sort */
1178 /* Create face_remap, a new attribute buffer for attribute sort optimization. */
1179 static HRESULT remap_faces_for_attrsort(ID3DXMeshImpl *This, const DWORD *indices,
1180 const DWORD *attrib_buffer, DWORD **sorted_attrib_buffer, DWORD **face_remap)
1182 const DWORD **sorted_attrib_ptr_buffer = NULL;
1185 *face_remap = HeapAlloc(GetProcessHeap(), 0, This->numfaces * sizeof(**face_remap));
1186 sorted_attrib_ptr_buffer = HeapAlloc(GetProcessHeap(), 0, This->numfaces * sizeof(*sorted_attrib_ptr_buffer));
1187 if (!*face_remap || !sorted_attrib_ptr_buffer) {
1188 HeapFree(GetProcessHeap(), 0, sorted_attrib_ptr_buffer);
1189 return E_OUTOFMEMORY;
1191 for (i = 0; i < This->numfaces; i++)
1192 sorted_attrib_ptr_buffer[i] = &attrib_buffer[i];
1193 qsort(sorted_attrib_ptr_buffer, This->numfaces, sizeof(*sorted_attrib_ptr_buffer),
1194 (int(*)(const void *, const void *))attrib_entry_compare);
1196 for (i = 0; i < This->numfaces; i++)
1198 DWORD old_face = sorted_attrib_ptr_buffer[i] - attrib_buffer;
1199 (*face_remap)[old_face] = i;
1202 /* overwrite sorted_attrib_ptr_buffer with the values themselves */
1203 *sorted_attrib_buffer = (DWORD*)sorted_attrib_ptr_buffer;
1204 for (i = 0; i < This->numfaces; i++)
1205 (*sorted_attrib_buffer)[(*face_remap)[i]] = attrib_buffer[i];
1210 static HRESULT WINAPI ID3DXMeshImpl_OptimizeInplace(ID3DXMesh *iface, DWORD flags, CONST DWORD *adjacency_in, DWORD *adjacency_out,
1211 DWORD *face_remap_out, LPD3DXBUFFER *vertex_remap_out)
1213 ID3DXMeshImpl *This = impl_from_ID3DXMesh(iface);
1214 void *indices = NULL;
1215 DWORD *attrib_buffer = NULL;
1217 ID3DXBuffer *vertex_remap = NULL;
1218 DWORD *face_remap = NULL; /* old -> new mapping */
1219 DWORD *dword_indices = NULL;
1220 DWORD new_num_vertices = 0;
1221 DWORD new_num_alloc_vertices = 0;
1222 IDirect3DVertexBuffer9 *vertex_buffer = NULL;
1223 DWORD *sorted_attrib_buffer = NULL;
1226 TRACE("(%p)->(%x,%p,%p,%p,%p)\n", This, flags, adjacency_in, adjacency_out, face_remap_out, vertex_remap_out);
1229 return D3DERR_INVALIDCALL;
1230 if (!adjacency_in && (flags & (D3DXMESHOPT_VERTEXCACHE | D3DXMESHOPT_STRIPREORDER)))
1231 return D3DERR_INVALIDCALL;
1232 if ((flags & (D3DXMESHOPT_VERTEXCACHE | D3DXMESHOPT_STRIPREORDER)) == (D3DXMESHOPT_VERTEXCACHE | D3DXMESHOPT_STRIPREORDER))
1233 return D3DERR_INVALIDCALL;
1235 if (flags & (D3DXMESHOPT_VERTEXCACHE | D3DXMESHOPT_STRIPREORDER))
1237 if (flags & D3DXMESHOPT_VERTEXCACHE)
1238 FIXME("D3DXMESHOPT_VERTEXCACHE not implemented.\n");
1239 if (flags & D3DXMESHOPT_STRIPREORDER)
1240 FIXME("D3DXMESHOPT_STRIPREORDER not implemented.\n");
1244 hr = iface->lpVtbl->LockIndexBuffer(iface, 0, (void**)&indices);
1245 if (FAILED(hr)) goto cleanup;
1247 dword_indices = HeapAlloc(GetProcessHeap(), 0, This->numfaces * 3 * sizeof(DWORD));
1248 if (!dword_indices) return E_OUTOFMEMORY;
1249 if (This->options & D3DXMESH_32BIT) {
1250 memcpy(dword_indices, indices, This->numfaces * 3 * sizeof(DWORD));
1252 WORD *word_indices = indices;
1253 for (i = 0; i < This->numfaces * 3; i++)
1254 dword_indices[i] = *word_indices++;
1257 if ((flags & (D3DXMESHOPT_COMPACT | D3DXMESHOPT_IGNOREVERTS | D3DXMESHOPT_ATTRSORT)) == D3DXMESHOPT_COMPACT)
1259 new_num_alloc_vertices = This->numvertices;
1260 hr = compact_mesh(This, dword_indices, &new_num_vertices, &vertex_remap);
1261 if (FAILED(hr)) goto cleanup;
1262 } else if (flags & D3DXMESHOPT_ATTRSORT) {
1263 if (!(flags & D3DXMESHOPT_IGNOREVERTS))
1265 FIXME("D3DXMESHOPT_ATTRSORT vertex reordering not implemented.\n");
1270 hr = iface->lpVtbl->LockAttributeBuffer(iface, 0, &attrib_buffer);
1271 if (FAILED(hr)) goto cleanup;
1273 hr = remap_faces_for_attrsort(This, dword_indices, attrib_buffer, &sorted_attrib_buffer, &face_remap);
1274 if (FAILED(hr)) goto cleanup;
1279 /* reorder the vertices using vertex_remap */
1280 D3DVERTEXBUFFER_DESC vertex_desc;
1281 DWORD *vertex_remap_ptr = ID3DXBuffer_GetBufferPointer(vertex_remap);
1282 DWORD vertex_size = iface->lpVtbl->GetNumBytesPerVertex(iface);
1283 BYTE *orig_vertices;
1286 hr = IDirect3DVertexBuffer9_GetDesc(This->vertex_buffer, &vertex_desc);
1287 if (FAILED(hr)) goto cleanup;
1289 hr = IDirect3DDevice9_CreateVertexBuffer(This->device, new_num_alloc_vertices * vertex_size,
1290 vertex_desc.Usage, This->fvf, vertex_desc.Pool, &vertex_buffer, NULL);
1291 if (FAILED(hr)) goto cleanup;
1293 hr = IDirect3DVertexBuffer9_Lock(This->vertex_buffer, 0, 0, (void**)&orig_vertices, D3DLOCK_READONLY);
1294 if (FAILED(hr)) goto cleanup;
1296 hr = IDirect3DVertexBuffer9_Lock(vertex_buffer, 0, 0, (void**)&new_vertices, D3DLOCK_DISCARD);
1298 IDirect3DVertexBuffer9_Unlock(This->vertex_buffer);
1302 for (i = 0; i < new_num_vertices; i++)
1303 memcpy(new_vertices + i * vertex_size, orig_vertices + vertex_remap_ptr[i] * vertex_size, vertex_size);
1305 IDirect3DVertexBuffer9_Unlock(This->vertex_buffer);
1306 IDirect3DVertexBuffer9_Unlock(vertex_buffer);
1307 } else if (vertex_remap_out) {
1308 DWORD *vertex_remap_ptr;
1310 hr = D3DXCreateBuffer(This->numvertices * sizeof(DWORD), &vertex_remap);
1311 if (FAILED(hr)) goto cleanup;
1312 vertex_remap_ptr = ID3DXBuffer_GetBufferPointer(vertex_remap);
1313 for (i = 0; i < This->numvertices; i++)
1314 *vertex_remap_ptr++ = i;
1317 if (flags & D3DXMESHOPT_ATTRSORT)
1319 D3DXATTRIBUTERANGE *attrib_table;
1320 DWORD attrib_table_size;
1322 attrib_table_size = count_attributes(sorted_attrib_buffer, This->numfaces);
1323 attrib_table = HeapAlloc(GetProcessHeap(), 0, attrib_table_size * sizeof(*attrib_table));
1324 if (!attrib_table) {
1329 memcpy(attrib_buffer, sorted_attrib_buffer, This->numfaces * sizeof(*attrib_buffer));
1331 /* reorder the indices using face_remap */
1332 if (This->options & D3DXMESH_32BIT) {
1333 for (i = 0; i < This->numfaces; i++)
1334 memcpy((DWORD*)indices + face_remap[i] * 3, dword_indices + i * 3, 3 * sizeof(DWORD));
1336 WORD *word_indices = indices;
1337 for (i = 0; i < This->numfaces; i++) {
1338 DWORD new_pos = face_remap[i] * 3;
1339 DWORD old_pos = i * 3;
1340 word_indices[new_pos++] = dword_indices[old_pos++];
1341 word_indices[new_pos++] = dword_indices[old_pos++];
1342 word_indices[new_pos] = dword_indices[old_pos];
1346 fill_attribute_table(attrib_buffer, This->numfaces, indices,
1347 This->options & D3DXMESH_32BIT, attrib_table);
1349 HeapFree(GetProcessHeap(), 0, This->attrib_table);
1350 This->attrib_table = attrib_table;
1351 This->attrib_table_size = attrib_table_size;
1353 if (This->options & D3DXMESH_32BIT) {
1354 memcpy(indices, dword_indices, This->numfaces * 3 * sizeof(DWORD));
1356 WORD *word_indices = indices;
1357 for (i = 0; i < This->numfaces * 3; i++)
1358 *word_indices++ = dword_indices[i];
1362 if (adjacency_out) {
1364 for (i = 0; i < This->numfaces; i++) {
1365 DWORD old_pos = i * 3;
1366 DWORD new_pos = face_remap[i] * 3;
1367 adjacency_out[new_pos++] = face_remap[adjacency_in[old_pos++]];
1368 adjacency_out[new_pos++] = face_remap[adjacency_in[old_pos++]];
1369 adjacency_out[new_pos++] = face_remap[adjacency_in[old_pos++]];
1372 memcpy(adjacency_out, adjacency_in, This->numfaces * 3 * sizeof(*adjacency_out));
1375 if (face_remap_out) {
1377 for (i = 0; i < This->numfaces; i++)
1378 face_remap_out[face_remap[i]] = i;
1380 for (i = 0; i < This->numfaces; i++)
1381 face_remap_out[i] = i;
1384 if (vertex_remap_out)
1385 *vertex_remap_out = vertex_remap;
1386 vertex_remap = NULL;
1388 if (vertex_buffer) {
1389 IDirect3DVertexBuffer9_Release(This->vertex_buffer);
1390 This->vertex_buffer = vertex_buffer;
1391 vertex_buffer = NULL;
1392 This->numvertices = new_num_vertices;
1397 HeapFree(GetProcessHeap(), 0, sorted_attrib_buffer);
1398 HeapFree(GetProcessHeap(), 0, face_remap);
1399 HeapFree(GetProcessHeap(), 0, dword_indices);
1400 if (vertex_remap) ID3DXBuffer_Release(vertex_remap);
1401 if (vertex_buffer) IDirect3DVertexBuffer9_Release(vertex_buffer);
1402 if (attrib_buffer) iface->lpVtbl->UnlockAttributeBuffer(iface);
1403 if (indices) iface->lpVtbl->UnlockIndexBuffer(iface);
1407 static HRESULT WINAPI ID3DXMeshImpl_SetAttributeTable(ID3DXMesh *iface, CONST D3DXATTRIBUTERANGE *attrib_table, DWORD attrib_table_size)
1409 ID3DXMeshImpl *This = impl_from_ID3DXMesh(iface);
1410 D3DXATTRIBUTERANGE *new_table = NULL;
1412 TRACE("(%p)->(%p,%u)\n", This, attrib_table, attrib_table_size);
1414 if (attrib_table_size) {
1415 size_t size = attrib_table_size * sizeof(*attrib_table);
1417 new_table = HeapAlloc(GetProcessHeap(), 0, size);
1419 return E_OUTOFMEMORY;
1421 CopyMemory(new_table, attrib_table, size);
1422 } else if (attrib_table) {
1423 return D3DERR_INVALIDCALL;
1425 HeapFree(GetProcessHeap(), 0, This->attrib_table);
1426 This->attrib_table = new_table;
1427 This->attrib_table_size = attrib_table_size;
1432 static const struct ID3DXMeshVtbl D3DXMesh_Vtbl =
1434 /*** IUnknown methods ***/
1435 ID3DXMeshImpl_QueryInterface,
1436 ID3DXMeshImpl_AddRef,
1437 ID3DXMeshImpl_Release,
1438 /*** ID3DXBaseMesh ***/
1439 ID3DXMeshImpl_DrawSubset,
1440 ID3DXMeshImpl_GetNumFaces,
1441 ID3DXMeshImpl_GetNumVertices,
1442 ID3DXMeshImpl_GetFVF,
1443 ID3DXMeshImpl_GetDeclaration,
1444 ID3DXMeshImpl_GetNumBytesPerVertex,
1445 ID3DXMeshImpl_GetOptions,
1446 ID3DXMeshImpl_GetDevice,
1447 ID3DXMeshImpl_CloneMeshFVF,
1448 ID3DXMeshImpl_CloneMesh,
1449 ID3DXMeshImpl_GetVertexBuffer,
1450 ID3DXMeshImpl_GetIndexBuffer,
1451 ID3DXMeshImpl_LockVertexBuffer,
1452 ID3DXMeshImpl_UnlockVertexBuffer,
1453 ID3DXMeshImpl_LockIndexBuffer,
1454 ID3DXMeshImpl_UnlockIndexBuffer,
1455 ID3DXMeshImpl_GetAttributeTable,
1456 ID3DXMeshImpl_ConvertPointRepsToAdjacency,
1457 ID3DXMeshImpl_ConvertAdjacencyToPointReps,
1458 ID3DXMeshImpl_GenerateAdjacency,
1459 ID3DXMeshImpl_UpdateSemantics,
1461 ID3DXMeshImpl_LockAttributeBuffer,
1462 ID3DXMeshImpl_UnlockAttributeBuffer,
1463 ID3DXMeshImpl_Optimize,
1464 ID3DXMeshImpl_OptimizeInplace,
1465 ID3DXMeshImpl_SetAttributeTable
1468 /*************************************************************************
1471 BOOL WINAPI D3DXBoxBoundProbe(CONST D3DXVECTOR3 *pmin, CONST D3DXVECTOR3 *pmax, CONST D3DXVECTOR3 *prayposition, CONST D3DXVECTOR3 *praydirection)
1473 /* Algorithm taken from the article: An Efficient and Robust Ray-Box Intersection Algoritm
1474 Amy Williams University of Utah
1475 Steve Barrus University of Utah
1476 R. Keith Morley University of Utah
1477 Peter Shirley University of Utah
1479 International Conference on Computer Graphics and Interactive Techniques archive
1480 ACM SIGGRAPH 2005 Courses
1481 Los Angeles, California
1483 This algorithm is free of patents or of copyrights, as confirmed by Peter Shirley himself.
1485 Algorithm: Consider the box as the intersection of three slabs. Clip the ray
1486 against each slab, if there's anything left of the ray after we're
1487 done we've got an intersection of the ray with the box.
1491 FLOAT div, tmin, tmax, tymin, tymax, tzmin, tzmax;
1493 div = 1.0f / praydirection->x;
1496 tmin = ( pmin->x - prayposition->x ) * div;
1497 tmax = ( pmax->x - prayposition->x ) * div;
1501 tmin = ( pmax->x - prayposition->x ) * div;
1502 tmax = ( pmin->x - prayposition->x ) * div;
1505 if ( tmax < 0.0f ) return FALSE;
1507 div = 1.0f / praydirection->y;
1510 tymin = ( pmin->y - prayposition->y ) * div;
1511 tymax = ( pmax->y - prayposition->y ) * div;
1515 tymin = ( pmax->y - prayposition->y ) * div;
1516 tymax = ( pmin->y - prayposition->y ) * div;
1519 if ( ( tymax < 0.0f ) || ( tmin > tymax ) || ( tymin > tmax ) ) return FALSE;
1521 if ( tymin > tmin ) tmin = tymin;
1522 if ( tymax < tmax ) tmax = tymax;
1524 div = 1.0f / praydirection->z;
1527 tzmin = ( pmin->z - prayposition->z ) * div;
1528 tzmax = ( pmax->z - prayposition->z ) * div;
1532 tzmin = ( pmax->z - prayposition->z ) * div;
1533 tzmax = ( pmin->z - prayposition->z ) * div;
1536 if ( (tzmax < 0.0f ) || ( tmin > tzmax ) || ( tzmin > tmax ) ) return FALSE;
1541 /*************************************************************************
1542 * D3DXComputeBoundingBox
1544 HRESULT WINAPI D3DXComputeBoundingBox(CONST D3DXVECTOR3 *pfirstposition, DWORD numvertices, DWORD dwstride, D3DXVECTOR3 *pmin, D3DXVECTOR3 *pmax)
1549 if( !pfirstposition || !pmin || !pmax ) return D3DERR_INVALIDCALL;
1551 *pmin = *pfirstposition;
1554 for(i=0; i<numvertices; i++)
1556 vec = *( (const D3DXVECTOR3*)((const char*)pfirstposition + dwstride * i) );
1558 if ( vec.x < pmin->x ) pmin->x = vec.x;
1559 if ( vec.x > pmax->x ) pmax->x = vec.x;
1561 if ( vec.y < pmin->y ) pmin->y = vec.y;
1562 if ( vec.y > pmax->y ) pmax->y = vec.y;
1564 if ( vec.z < pmin->z ) pmin->z = vec.z;
1565 if ( vec.z > pmax->z ) pmax->z = vec.z;
1571 /*************************************************************************
1572 * D3DXComputeBoundingSphere
1574 HRESULT WINAPI D3DXComputeBoundingSphere(CONST D3DXVECTOR3* pfirstposition, DWORD numvertices, DWORD dwstride, D3DXVECTOR3 *pcenter, FLOAT *pradius)
1576 D3DXVECTOR3 temp, temp1;
1580 if( !pfirstposition || !pcenter || !pradius ) return D3DERR_INVALIDCALL;
1588 for(i=0; i<numvertices; i++)
1590 D3DXVec3Add(&temp1, &temp, (const D3DXVECTOR3*)((const char*)pfirstposition + dwstride * i));
1594 D3DXVec3Scale(pcenter, &temp, 1.0f/((FLOAT)numvertices));
1596 for(i=0; i<numvertices; i++)
1598 d = D3DXVec3Length(D3DXVec3Subtract(&temp, (const D3DXVECTOR3*)((const char*)pfirstposition + dwstride * i), pcenter));
1599 if ( d > *pradius ) *pradius = d;
1604 static const UINT d3dx_decltype_size[D3DDECLTYPE_UNUSED] =
1606 /* D3DDECLTYPE_FLOAT1 */ 1 * 4,
1607 /* D3DDECLTYPE_FLOAT2 */ 2 * 4,
1608 /* D3DDECLTYPE_FLOAT3 */ 3 * 4,
1609 /* D3DDECLTYPE_FLOAT4 */ 4 * 4,
1610 /* D3DDECLTYPE_D3DCOLOR */ 4 * 1,
1611 /* D3DDECLTYPE_UBYTE4 */ 4 * 1,
1612 /* D3DDECLTYPE_SHORT2 */ 2 * 2,
1613 /* D3DDECLTYPE_SHORT4 */ 4 * 2,
1614 /* D3DDECLTYPE_UBYTE4N */ 4 * 1,
1615 /* D3DDECLTYPE_SHORT2N */ 2 * 2,
1616 /* D3DDECLTYPE_SHORT4N */ 4 * 2,
1617 /* D3DDECLTYPE_USHORT2N */ 2 * 2,
1618 /* D3DDECLTYPE_USHORT4N */ 4 * 2,
1619 /* D3DDECLTYPE_UDEC3 */ 4, /* 3 * 10 bits + 2 padding */
1620 /* D3DDECLTYPE_DEC3N */ 4,
1621 /* D3DDECLTYPE_FLOAT16_2 */ 2 * 2,
1622 /* D3DDECLTYPE_FLOAT16_4 */ 4 * 2,
1625 static void append_decl_element(D3DVERTEXELEMENT9 *declaration, UINT *idx, UINT *offset,
1626 D3DDECLTYPE type, D3DDECLUSAGE usage, UINT usage_idx)
1628 declaration[*idx].Stream = 0;
1629 declaration[*idx].Offset = *offset;
1630 declaration[*idx].Type = type;
1631 declaration[*idx].Method = D3DDECLMETHOD_DEFAULT;
1632 declaration[*idx].Usage = usage;
1633 declaration[*idx].UsageIndex = usage_idx;
1635 *offset += d3dx_decltype_size[type];
1639 /*************************************************************************
1640 * D3DXDeclaratorFromFVF
1642 HRESULT WINAPI D3DXDeclaratorFromFVF(DWORD fvf, D3DVERTEXELEMENT9 declaration[MAX_FVF_DECL_SIZE])
1644 static const D3DVERTEXELEMENT9 end_element = D3DDECL_END();
1645 DWORD tex_count = (fvf & D3DFVF_TEXCOUNT_MASK) >> D3DFVF_TEXCOUNT_SHIFT;
1646 unsigned int offset = 0;
1647 unsigned int idx = 0;
1650 TRACE("fvf %#x, declaration %p.\n", fvf, declaration);
1652 if (fvf & (D3DFVF_RESERVED0 | D3DFVF_RESERVED2)) return D3DERR_INVALIDCALL;
1654 if (fvf & D3DFVF_POSITION_MASK)
1656 BOOL has_blend = (fvf & D3DFVF_XYZB5) >= D3DFVF_XYZB1;
1657 DWORD blend_count = 1 + (((fvf & D3DFVF_XYZB5) - D3DFVF_XYZB1) >> 1);
1658 BOOL has_blend_idx = (fvf & D3DFVF_LASTBETA_D3DCOLOR) || (fvf & D3DFVF_LASTBETA_UBYTE4);
1660 if (has_blend_idx) --blend_count;
1662 if ((fvf & D3DFVF_POSITION_MASK) == D3DFVF_XYZW
1663 || (has_blend && blend_count > 4))
1664 return D3DERR_INVALIDCALL;
1666 if ((fvf & D3DFVF_POSITION_MASK) == D3DFVF_XYZRHW)
1667 append_decl_element(declaration, &idx, &offset, D3DDECLTYPE_FLOAT4, D3DDECLUSAGE_POSITIONT, 0);
1669 append_decl_element(declaration, &idx, &offset, D3DDECLTYPE_FLOAT3, D3DDECLUSAGE_POSITION, 0);
1673 switch (blend_count)
1678 append_decl_element(declaration, &idx, &offset, D3DDECLTYPE_FLOAT1, D3DDECLUSAGE_BLENDWEIGHT, 0);
1681 append_decl_element(declaration, &idx, &offset, D3DDECLTYPE_FLOAT2, D3DDECLUSAGE_BLENDWEIGHT, 0);
1684 append_decl_element(declaration, &idx, &offset, D3DDECLTYPE_FLOAT3, D3DDECLUSAGE_BLENDWEIGHT, 0);
1687 append_decl_element(declaration, &idx, &offset, D3DDECLTYPE_FLOAT4, D3DDECLUSAGE_BLENDWEIGHT, 0);
1690 ERR("Invalid blend count %u.\n", blend_count);
1696 if (fvf & D3DFVF_LASTBETA_UBYTE4)
1697 append_decl_element(declaration, &idx, &offset, D3DDECLTYPE_UBYTE4, D3DDECLUSAGE_BLENDINDICES, 0);
1698 else if (fvf & D3DFVF_LASTBETA_D3DCOLOR)
1699 append_decl_element(declaration, &idx, &offset, D3DDECLTYPE_D3DCOLOR, D3DDECLUSAGE_BLENDINDICES, 0);
1704 if (fvf & D3DFVF_NORMAL)
1705 append_decl_element(declaration, &idx, &offset, D3DDECLTYPE_FLOAT3, D3DDECLUSAGE_NORMAL, 0);
1706 if (fvf & D3DFVF_PSIZE)
1707 append_decl_element(declaration, &idx, &offset, D3DDECLTYPE_FLOAT1, D3DDECLUSAGE_PSIZE, 0);
1708 if (fvf & D3DFVF_DIFFUSE)
1709 append_decl_element(declaration, &idx, &offset, D3DDECLTYPE_D3DCOLOR, D3DDECLUSAGE_COLOR, 0);
1710 if (fvf & D3DFVF_SPECULAR)
1711 append_decl_element(declaration, &idx, &offset, D3DDECLTYPE_D3DCOLOR, D3DDECLUSAGE_COLOR, 1);
1713 for (i = 0; i < tex_count; ++i)
1715 switch ((fvf >> (16 + 2 * i)) & 0x03)
1717 case D3DFVF_TEXTUREFORMAT1:
1718 append_decl_element(declaration, &idx, &offset, D3DDECLTYPE_FLOAT1, D3DDECLUSAGE_TEXCOORD, i);
1720 case D3DFVF_TEXTUREFORMAT2:
1721 append_decl_element(declaration, &idx, &offset, D3DDECLTYPE_FLOAT2, D3DDECLUSAGE_TEXCOORD, i);
1723 case D3DFVF_TEXTUREFORMAT3:
1724 append_decl_element(declaration, &idx, &offset, D3DDECLTYPE_FLOAT3, D3DDECLUSAGE_TEXCOORD, i);
1726 case D3DFVF_TEXTUREFORMAT4:
1727 append_decl_element(declaration, &idx, &offset, D3DDECLTYPE_FLOAT4, D3DDECLUSAGE_TEXCOORD, i);
1732 declaration[idx] = end_element;
1737 /*************************************************************************
1738 * D3DXFVFFromDeclarator
1740 HRESULT WINAPI D3DXFVFFromDeclarator(const D3DVERTEXELEMENT9 *declaration, DWORD *fvf)
1742 unsigned int i = 0, texture, offset;
1744 TRACE("(%p, %p)\n", declaration, fvf);
1747 if (declaration[0].Type == D3DDECLTYPE_FLOAT3 && declaration[0].Usage == D3DDECLUSAGE_POSITION)
1749 if ((declaration[1].Type == D3DDECLTYPE_FLOAT4 && declaration[1].Usage == D3DDECLUSAGE_BLENDWEIGHT &&
1750 declaration[1].UsageIndex == 0) &&
1751 (declaration[2].Type == D3DDECLTYPE_FLOAT1 && declaration[2].Usage == D3DDECLUSAGE_BLENDINDICES &&
1752 declaration[2].UsageIndex == 0))
1754 return D3DERR_INVALIDCALL;
1756 else if ((declaration[1].Type == D3DDECLTYPE_UBYTE4 || declaration[1].Type == D3DDECLTYPE_D3DCOLOR) &&
1757 declaration[1].Usage == D3DDECLUSAGE_BLENDINDICES && declaration[1].UsageIndex == 0)
1759 if (declaration[1].Type == D3DDECLTYPE_UBYTE4)
1761 *fvf |= D3DFVF_XYZB1 | D3DFVF_LASTBETA_UBYTE4;
1765 *fvf |= D3DFVF_XYZB1 | D3DFVF_LASTBETA_D3DCOLOR;
1769 else if (declaration[1].Type <= D3DDECLTYPE_FLOAT4 && declaration[1].Usage == D3DDECLUSAGE_BLENDWEIGHT &&
1770 declaration[1].UsageIndex == 0)
1772 if ((declaration[2].Type == D3DDECLTYPE_UBYTE4 || declaration[2].Type == D3DDECLTYPE_D3DCOLOR) &&
1773 declaration[2].Usage == D3DDECLUSAGE_BLENDINDICES && declaration[2].UsageIndex == 0)
1775 if (declaration[2].Type == D3DDECLTYPE_UBYTE4)
1777 *fvf |= D3DFVF_LASTBETA_UBYTE4;
1781 *fvf |= D3DFVF_LASTBETA_D3DCOLOR;
1783 switch (declaration[1].Type)
1785 case D3DDECLTYPE_FLOAT1: *fvf |= D3DFVF_XYZB2; break;
1786 case D3DDECLTYPE_FLOAT2: *fvf |= D3DFVF_XYZB3; break;
1787 case D3DDECLTYPE_FLOAT3: *fvf |= D3DFVF_XYZB4; break;
1788 case D3DDECLTYPE_FLOAT4: *fvf |= D3DFVF_XYZB5; break;
1794 switch (declaration[1].Type)
1796 case D3DDECLTYPE_FLOAT1: *fvf |= D3DFVF_XYZB1; break;
1797 case D3DDECLTYPE_FLOAT2: *fvf |= D3DFVF_XYZB2; break;
1798 case D3DDECLTYPE_FLOAT3: *fvf |= D3DFVF_XYZB3; break;
1799 case D3DDECLTYPE_FLOAT4: *fvf |= D3DFVF_XYZB4; break;
1810 else if (declaration[0].Type == D3DDECLTYPE_FLOAT4 && declaration[0].Usage == D3DDECLUSAGE_POSITIONT &&
1811 declaration[0].UsageIndex == 0)
1813 *fvf |= D3DFVF_XYZRHW;
1817 if (declaration[i].Type == D3DDECLTYPE_FLOAT3 && declaration[i].Usage == D3DDECLUSAGE_NORMAL)
1819 *fvf |= D3DFVF_NORMAL;
1822 if (declaration[i].Type == D3DDECLTYPE_FLOAT1 && declaration[i].Usage == D3DDECLUSAGE_PSIZE &&
1823 declaration[i].UsageIndex == 0)
1825 *fvf |= D3DFVF_PSIZE;
1828 if (declaration[i].Type == D3DDECLTYPE_D3DCOLOR && declaration[i].Usage == D3DDECLUSAGE_COLOR &&
1829 declaration[i].UsageIndex == 0)
1831 *fvf |= D3DFVF_DIFFUSE;
1834 if (declaration[i].Type == D3DDECLTYPE_D3DCOLOR && declaration[i].Usage == D3DDECLUSAGE_COLOR &&
1835 declaration[i].UsageIndex == 1)
1837 *fvf |= D3DFVF_SPECULAR;
1841 for (texture = 0; texture < D3DDP_MAXTEXCOORD; i++, texture++)
1843 if (declaration[i].Stream == 0xFF)
1847 else if (declaration[i].Type == D3DDECLTYPE_FLOAT1 && declaration[i].Usage == D3DDECLUSAGE_TEXCOORD &&
1848 declaration[i].UsageIndex == texture)
1850 *fvf |= D3DFVF_TEXCOORDSIZE1(declaration[i].UsageIndex);
1852 else if (declaration[i].Type == D3DDECLTYPE_FLOAT2 && declaration[i].Usage == D3DDECLUSAGE_TEXCOORD &&
1853 declaration[i].UsageIndex == texture)
1855 *fvf |= D3DFVF_TEXCOORDSIZE2(declaration[i].UsageIndex);
1857 else if (declaration[i].Type == D3DDECLTYPE_FLOAT3 && declaration[i].Usage == D3DDECLUSAGE_TEXCOORD &&
1858 declaration[i].UsageIndex == texture)
1860 *fvf |= D3DFVF_TEXCOORDSIZE3(declaration[i].UsageIndex);
1862 else if (declaration[i].Type == D3DDECLTYPE_FLOAT4 && declaration[i].Usage == D3DDECLUSAGE_TEXCOORD &&
1863 declaration[i].UsageIndex == texture)
1865 *fvf |= D3DFVF_TEXCOORDSIZE4(declaration[i].UsageIndex);
1869 return D3DERR_INVALIDCALL;
1873 *fvf |= (texture << D3DFVF_TEXCOUNT_SHIFT);
1875 for (offset = 0, i = 0; declaration[i].Stream != 0xFF;
1876 offset += d3dx_decltype_size[declaration[i].Type], i++)
1878 if (declaration[i].Offset != offset)
1880 return D3DERR_INVALIDCALL;
1887 /*************************************************************************
1888 * D3DXGetFVFVertexSize
1890 static UINT Get_TexCoord_Size_From_FVF(DWORD FVF, int tex_num)
1892 return (((((FVF) >> (16 + (2 * (tex_num)))) + 1) & 0x03) + 1);
1895 UINT WINAPI D3DXGetFVFVertexSize(DWORD FVF)
1899 UINT numTextures = (FVF & D3DFVF_TEXCOUNT_MASK) >> D3DFVF_TEXCOUNT_SHIFT;
1901 if (FVF & D3DFVF_NORMAL) size += sizeof(D3DXVECTOR3);
1902 if (FVF & D3DFVF_DIFFUSE) size += sizeof(DWORD);
1903 if (FVF & D3DFVF_SPECULAR) size += sizeof(DWORD);
1904 if (FVF & D3DFVF_PSIZE) size += sizeof(DWORD);
1906 switch (FVF & D3DFVF_POSITION_MASK)
1908 case D3DFVF_XYZ: size += sizeof(D3DXVECTOR3); break;
1909 case D3DFVF_XYZRHW: size += 4 * sizeof(FLOAT); break;
1910 case D3DFVF_XYZB1: size += 4 * sizeof(FLOAT); break;
1911 case D3DFVF_XYZB2: size += 5 * sizeof(FLOAT); break;
1912 case D3DFVF_XYZB3: size += 6 * sizeof(FLOAT); break;
1913 case D3DFVF_XYZB4: size += 7 * sizeof(FLOAT); break;
1914 case D3DFVF_XYZB5: size += 8 * sizeof(FLOAT); break;
1915 case D3DFVF_XYZW: size += 4 * sizeof(FLOAT); break;
1918 for (i = 0; i < numTextures; i++)
1920 size += Get_TexCoord_Size_From_FVF(FVF, i) * sizeof(FLOAT);
1926 /*************************************************************************
1927 * D3DXGetDeclVertexSize
1929 UINT WINAPI D3DXGetDeclVertexSize(const D3DVERTEXELEMENT9 *decl, DWORD stream_idx)
1931 const D3DVERTEXELEMENT9 *element;
1934 TRACE("decl %p, stream_idx %u\n", decl, stream_idx);
1936 if (!decl) return 0;
1938 for (element = decl; element->Stream != 0xff; ++element)
1942 if (element->Stream != stream_idx) continue;
1944 if (element->Type >= sizeof(d3dx_decltype_size) / sizeof(*d3dx_decltype_size))
1946 FIXME("Unhandled element type %#x, size will be incorrect.\n", element->Type);
1950 type_size = d3dx_decltype_size[element->Type];
1951 if (element->Offset + type_size > size) size = element->Offset + type_size;
1957 /*************************************************************************
1960 UINT WINAPI D3DXGetDeclLength(const D3DVERTEXELEMENT9 *decl)
1962 const D3DVERTEXELEMENT9 *element;
1964 TRACE("decl %p\n", decl);
1966 /* null decl results in exception on Windows XP */
1968 for (element = decl; element->Stream != 0xff; ++element);
1970 return element - decl;
1973 /*************************************************************************
1976 BOOL WINAPI D3DXIntersectTri(CONST D3DXVECTOR3 *p0, CONST D3DXVECTOR3 *p1, CONST D3DXVECTOR3 *p2, CONST D3DXVECTOR3 *praypos, CONST D3DXVECTOR3 *praydir, FLOAT *pu, FLOAT *pv, FLOAT *pdist)
1981 m.u.m[0][0] = p1->x - p0->x;
1982 m.u.m[1][0] = p2->x - p0->x;
1983 m.u.m[2][0] = -praydir->x;
1985 m.u.m[0][1] = p1->y - p0->z;
1986 m.u.m[1][1] = p2->y - p0->z;
1987 m.u.m[2][1] = -praydir->y;
1989 m.u.m[0][2] = p1->z - p0->z;
1990 m.u.m[1][2] = p2->z - p0->z;
1991 m.u.m[2][2] = -praydir->z;
1998 vec.x = praypos->x - p0->x;
1999 vec.y = praypos->y - p0->y;
2000 vec.z = praypos->z - p0->z;
2003 if ( D3DXMatrixInverse(&m, NULL, &m) )
2005 D3DXVec4Transform(&vec, &vec, &m);
2006 if ( (vec.x >= 0.0f) && (vec.y >= 0.0f) && (vec.x + vec.y <= 1.0f) && (vec.z >= 0.0f) )
2010 *pdist = fabs( vec.z );
2018 /*************************************************************************
2019 * D3DXSphereBoundProbe
2021 BOOL WINAPI D3DXSphereBoundProbe(CONST D3DXVECTOR3 *pcenter, FLOAT radius, CONST D3DXVECTOR3 *prayposition, CONST D3DXVECTOR3 *praydirection)
2023 D3DXVECTOR3 difference;
2026 a = D3DXVec3LengthSq(praydirection);
2027 if (!D3DXVec3Subtract(&difference, prayposition, pcenter)) return FALSE;
2028 b = D3DXVec3Dot(&difference, praydirection);
2029 c = D3DXVec3LengthSq(&difference) - radius * radius;
2032 if ( ( d <= 0.0f ) || ( sqrt(d) <= b ) ) return FALSE;
2036 /*************************************************************************
2039 HRESULT WINAPI D3DXCreateMesh(DWORD numfaces, DWORD numvertices, DWORD options, CONST D3DVERTEXELEMENT9 *declaration,
2040 LPDIRECT3DDEVICE9 device, LPD3DXMESH *mesh)
2044 IDirect3DVertexDeclaration9 *vertex_declaration;
2045 UINT vertex_declaration_size;
2047 IDirect3DVertexBuffer9 *vertex_buffer;
2048 IDirect3DIndexBuffer9 *index_buffer;
2049 DWORD *attrib_buffer;
2050 ID3DXMeshImpl *object;
2051 DWORD index_usage = 0;
2052 D3DPOOL index_pool = D3DPOOL_DEFAULT;
2053 D3DFORMAT index_format = D3DFMT_INDEX16;
2054 DWORD vertex_usage = 0;
2055 D3DPOOL vertex_pool = D3DPOOL_DEFAULT;
2058 TRACE("(%d, %d, %x, %p, %p, %p)\n", numfaces, numvertices, options, declaration, device, mesh);
2060 if (numfaces == 0 || numvertices == 0 || declaration == NULL || device == NULL || mesh == NULL ||
2061 /* D3DXMESH_VB_SHARE is for cloning, and D3DXMESH_USEHWONLY is for ConvertToBlendedMesh */
2062 (options & (D3DXMESH_VB_SHARE | D3DXMESH_USEHWONLY | 0xfffe0000)))
2064 return D3DERR_INVALIDCALL;
2066 for (i = 0; declaration[i].Stream != 0xff; i++)
2067 if (declaration[i].Stream != 0)
2068 return D3DERR_INVALIDCALL;
2071 if (options & D3DXMESH_32BIT)
2072 index_format = D3DFMT_INDEX32;
2074 if (options & D3DXMESH_DONOTCLIP) {
2075 index_usage |= D3DUSAGE_DONOTCLIP;
2076 vertex_usage |= D3DUSAGE_DONOTCLIP;
2078 if (options & D3DXMESH_POINTS) {
2079 index_usage |= D3DUSAGE_POINTS;
2080 vertex_usage |= D3DUSAGE_POINTS;
2082 if (options & D3DXMESH_RTPATCHES) {
2083 index_usage |= D3DUSAGE_RTPATCHES;
2084 vertex_usage |= D3DUSAGE_RTPATCHES;
2086 if (options & D3DXMESH_NPATCHES) {
2087 index_usage |= D3DUSAGE_NPATCHES;
2088 vertex_usage |= D3DUSAGE_NPATCHES;
2091 if (options & D3DXMESH_VB_SYSTEMMEM)
2092 vertex_pool = D3DPOOL_SYSTEMMEM;
2093 else if (options & D3DXMESH_VB_MANAGED)
2094 vertex_pool = D3DPOOL_MANAGED;
2096 if (options & D3DXMESH_VB_WRITEONLY)
2097 vertex_usage |= D3DUSAGE_WRITEONLY;
2098 if (options & D3DXMESH_VB_DYNAMIC)
2099 vertex_usage |= D3DUSAGE_DYNAMIC;
2100 if (options & D3DXMESH_VB_SOFTWAREPROCESSING)
2101 vertex_usage |= D3DUSAGE_SOFTWAREPROCESSING;
2103 if (options & D3DXMESH_IB_SYSTEMMEM)
2104 index_pool = D3DPOOL_SYSTEMMEM;
2105 else if (options & D3DXMESH_IB_MANAGED)
2106 index_pool = D3DPOOL_MANAGED;
2108 if (options & D3DXMESH_IB_WRITEONLY)
2109 index_usage |= D3DUSAGE_WRITEONLY;
2110 if (options & D3DXMESH_IB_DYNAMIC)
2111 index_usage |= D3DUSAGE_DYNAMIC;
2112 if (options & D3DXMESH_IB_SOFTWAREPROCESSING)
2113 index_usage |= D3DUSAGE_SOFTWAREPROCESSING;
2115 hr = D3DXFVFFromDeclarator(declaration, &fvf);
2121 /* Create vertex declaration */
2122 hr = IDirect3DDevice9_CreateVertexDeclaration(device,
2124 &vertex_declaration);
2127 WARN("Unexpected return value %x from IDirect3DDevice9_CreateVertexDeclaration.\n",hr);
2130 vertex_declaration_size = D3DXGetDeclVertexSize(declaration, declaration[0].Stream);
2132 /* Create vertex buffer */
2133 hr = IDirect3DDevice9_CreateVertexBuffer(device,
2134 numvertices * vertex_declaration_size,
2142 WARN("Unexpected return value %x from IDirect3DDevice9_CreateVertexBuffer.\n",hr);
2143 IDirect3DVertexDeclaration9_Release(vertex_declaration);
2147 /* Create index buffer */
2148 hr = IDirect3DDevice9_CreateIndexBuffer(device,
2149 numfaces * 3 * ((index_format == D3DFMT_INDEX16) ? 2 : 4),
2157 WARN("Unexpected return value %x from IDirect3DDevice9_CreateVertexBuffer.\n",hr);
2158 IDirect3DVertexBuffer9_Release(vertex_buffer);
2159 IDirect3DVertexDeclaration9_Release(vertex_declaration);
2163 attrib_buffer = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, numfaces * sizeof(*attrib_buffer));
2164 object = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(ID3DXMeshImpl));
2165 if (object == NULL || attrib_buffer == NULL)
2167 HeapFree(GetProcessHeap(), 0, attrib_buffer);
2168 IDirect3DIndexBuffer9_Release(index_buffer);
2169 IDirect3DVertexBuffer9_Release(vertex_buffer);
2170 IDirect3DVertexDeclaration9_Release(vertex_declaration);
2172 return E_OUTOFMEMORY;
2174 object->ID3DXMesh_iface.lpVtbl = &D3DXMesh_Vtbl;
2177 object->numfaces = numfaces;
2178 object->numvertices = numvertices;
2179 object->options = options;
2181 object->device = device;
2182 IDirect3DDevice9_AddRef(device);
2184 copy_declaration(object->cached_declaration, declaration, num_elem);
2185 object->vertex_declaration = vertex_declaration;
2186 object->vertex_declaration_size = vertex_declaration_size;
2187 object->num_elem = num_elem;
2188 object->vertex_buffer = vertex_buffer;
2189 object->index_buffer = index_buffer;
2190 object->attrib_buffer = attrib_buffer;
2192 *mesh = &object->ID3DXMesh_iface;
2197 /*************************************************************************
2200 HRESULT WINAPI D3DXCreateMeshFVF(DWORD numfaces, DWORD numvertices, DWORD options, DWORD fvf,
2201 LPDIRECT3DDEVICE9 device, LPD3DXMESH *mesh)
2204 D3DVERTEXELEMENT9 declaration[MAX_FVF_DECL_SIZE];
2206 TRACE("(%u, %u, %u, %u, %p, %p)\n", numfaces, numvertices, options, fvf, device, mesh);
2208 hr = D3DXDeclaratorFromFVF(fvf, declaration);
2209 if (FAILED(hr)) return hr;
2211 return D3DXCreateMesh(numfaces, numvertices, options, declaration, device, mesh);
2217 DWORD num_poly_faces;
2218 DWORD num_tri_faces;
2219 D3DXVECTOR3 *vertices;
2220 DWORD *num_tri_per_face;
2225 /* optional mesh data */
2228 D3DXVECTOR3 *normals;
2229 DWORD *normal_indices;
2231 D3DXVECTOR2 *tex_coords;
2233 DWORD *vertex_colors;
2235 DWORD num_materials;
2236 D3DXMATERIAL *materials;
2237 DWORD *material_indices;
2240 static HRESULT get_next_child(IDirectXFileData *filedata, IDirectXFileData **child, const GUID **type)
2243 IDirectXFileDataReference *child_ref = NULL;
2244 IDirectXFileObject *child_obj = NULL;
2245 IDirectXFileData *child_data = NULL;
2247 hr = IDirectXFileData_GetNextObject(filedata, &child_obj);
2248 if (FAILED(hr)) return hr;
2250 hr = IDirectXFileObject_QueryInterface(child_obj, &IID_IDirectXFileDataReference, (void**)&child_ref);
2251 if (SUCCEEDED(hr)) {
2252 hr = IDirectXFileDataReference_Resolve(child_ref, &child_data);
2253 IDirectXFileDataReference_Release(child_ref);
2255 hr = IDirectXFileObject_QueryInterface(child_obj, &IID_IDirectXFileData, (void**)&child_data);
2257 IDirectXFileObject_Release(child_obj);
2261 hr = IDirectXFileData_GetType(child_data, type);
2263 IDirectXFileData_Release(child_data);
2265 *child = child_data;
2271 static HRESULT parse_texture_filename(IDirectXFileData *filedata, LPSTR *filename_out)
2277 char *filename = NULL;
2279 /* template TextureFilename {
2284 HeapFree(GetProcessHeap(), 0, *filename_out);
2285 *filename_out = NULL;
2287 hr = IDirectXFileData_GetData(filedata, NULL, &data_size, (void**)&data);
2288 if (FAILED(hr)) return hr;
2290 if (data_size < sizeof(LPSTR)) {
2291 WARN("truncated data (%u bytes)\n", data_size);
2294 filename_in = *(LPSTR*)data;
2296 filename = HeapAlloc(GetProcessHeap(), 0, strlen(filename_in) + 1);
2297 if (!filename) return E_OUTOFMEMORY;
2299 strcpy(filename, filename_in);
2300 *filename_out = filename;
2305 static HRESULT parse_material(IDirectXFileData *filedata, D3DXMATERIAL *material)
2311 IDirectXFileData *child;
2313 material->pTextureFilename = NULL;
2315 hr = IDirectXFileData_GetData(filedata, NULL, &data_size, (void**)&data);
2316 if (FAILED(hr)) return hr;
2319 * template ColorRGBA {
2325 * template ColorRGB {
2330 * template Material {
2331 * ColorRGBA faceColor;
2333 * ColorRGB specularColor;
2334 * ColorRGB emissiveColor;
2338 if (data_size != sizeof(FLOAT) * 11) {
2339 WARN("incorrect data size (%u bytes)\n", data_size);
2343 memcpy(&material->MatD3D.Diffuse, data, sizeof(D3DCOLORVALUE));
2344 data += sizeof(D3DCOLORVALUE);
2345 material->MatD3D.Power = *(FLOAT*)data;
2346 data += sizeof(FLOAT);
2347 memcpy(&material->MatD3D.Specular, data, sizeof(FLOAT) * 3);
2348 material->MatD3D.Specular.a = 1.0f;
2349 data += 3 * sizeof(FLOAT);
2350 memcpy(&material->MatD3D.Emissive, data, sizeof(FLOAT) * 3);
2351 material->MatD3D.Emissive.a = 1.0f;
2352 material->MatD3D.Ambient.r = 0.0f;
2353 material->MatD3D.Ambient.g = 0.0f;
2354 material->MatD3D.Ambient.b = 0.0f;
2355 material->MatD3D.Ambient.a = 1.0f;
2357 while (SUCCEEDED(hr = get_next_child(filedata, &child, &type)))
2359 if (IsEqualGUID(type, &TID_D3DRMTextureFilename)) {
2360 hr = parse_texture_filename(child, &material->pTextureFilename);
2361 if (FAILED(hr)) break;
2364 return hr == DXFILEERR_NOMOREOBJECTS ? D3D_OK : hr;
2367 static void destroy_materials(struct mesh_data *mesh)
2370 for (i = 0; i < mesh->num_materials; i++)
2371 HeapFree(GetProcessHeap(), 0, mesh->materials[i].pTextureFilename);
2372 HeapFree(GetProcessHeap(), 0, mesh->materials);
2373 HeapFree(GetProcessHeap(), 0, mesh->material_indices);
2374 mesh->num_materials = 0;
2375 mesh->materials = NULL;
2376 mesh->material_indices = NULL;
2379 static HRESULT parse_material_list(IDirectXFileData *filedata, struct mesh_data *mesh)
2383 DWORD *data, *in_ptr;
2385 IDirectXFileData *child;
2386 DWORD num_materials;
2389 destroy_materials(mesh);
2391 hr = IDirectXFileData_GetData(filedata, NULL, &data_size, (void**)&data);
2392 if (FAILED(hr)) return hr;
2394 /* template MeshMaterialList {
2396 * DWORD nFaceIndexes;
2397 * array DWORD faceIndexes[nFaceIndexes];
2404 if (data_size < sizeof(DWORD))
2405 goto truncated_data_error;
2406 num_materials = *in_ptr++;
2410 if (data_size < 2 * sizeof(DWORD))
2411 goto truncated_data_error;
2412 if (*in_ptr++ != mesh->num_poly_faces) {
2413 WARN("number of material face indices (%u) doesn't match number of faces (%u)\n",
2414 *(in_ptr - 1), mesh->num_poly_faces);
2417 if (data_size < 2 * sizeof(DWORD) + mesh->num_poly_faces * sizeof(DWORD))
2418 goto truncated_data_error;
2419 for (i = 0; i < mesh->num_poly_faces; i++) {
2420 if (*in_ptr++ >= num_materials) {
2421 WARN("face %u: reference to undefined material %u (only %u materials)\n",
2422 i, *(in_ptr - 1), num_materials);
2427 mesh->materials = HeapAlloc(GetProcessHeap(), 0, num_materials * sizeof(*mesh->materials));
2428 mesh->material_indices = HeapAlloc(GetProcessHeap(), 0, mesh->num_poly_faces * sizeof(*mesh->material_indices));
2429 if (!mesh->materials || !mesh->material_indices)
2430 return E_OUTOFMEMORY;
2431 memcpy(mesh->material_indices, data + 2, mesh->num_poly_faces * sizeof(DWORD));
2433 while (SUCCEEDED(hr = get_next_child(filedata, &child, &type)))
2435 if (IsEqualGUID(type, &TID_D3DRMMaterial)) {
2436 if (mesh->num_materials >= num_materials) {
2437 WARN("more materials defined than declared\n");
2440 hr = parse_material(child, &mesh->materials[mesh->num_materials++]);
2441 if (FAILED(hr)) break;
2444 if (hr != DXFILEERR_NOMOREOBJECTS)
2446 if (num_materials != mesh->num_materials) {
2447 WARN("only %u of %u materials defined\n", num_materials, mesh->num_materials);
2452 truncated_data_error:
2453 WARN("truncated data (%u bytes)\n", data_size);
2457 static HRESULT parse_texture_coords(IDirectXFileData *filedata, struct mesh_data *mesh)
2463 HeapFree(GetProcessHeap(), 0, mesh->tex_coords);
2464 mesh->tex_coords = NULL;
2466 hr = IDirectXFileData_GetData(filedata, NULL, &data_size, (void**)&data);
2467 if (FAILED(hr)) return hr;
2469 /* template Coords2d {
2473 * template MeshTextureCoords {
2474 * DWORD nTextureCoords;
2475 * array Coords2d textureCoords[nTextureCoords];
2479 if (data_size < sizeof(DWORD))
2480 goto truncated_data_error;
2481 if (*(DWORD*)data != mesh->num_vertices) {
2482 WARN("number of texture coordinates (%u) doesn't match number of vertices (%u)\n",
2483 *(DWORD*)data, mesh->num_vertices);
2486 data += sizeof(DWORD);
2487 if (data_size < sizeof(DWORD) + mesh->num_vertices * sizeof(*mesh->tex_coords))
2488 goto truncated_data_error;
2490 mesh->tex_coords = HeapAlloc(GetProcessHeap(), 0, mesh->num_vertices * sizeof(*mesh->tex_coords));
2491 if (!mesh->tex_coords) return E_OUTOFMEMORY;
2492 memcpy(mesh->tex_coords, data, mesh->num_vertices * sizeof(*mesh->tex_coords));
2494 mesh->fvf |= D3DFVF_TEX1;
2497 truncated_data_error:
2498 WARN("truncated data (%u bytes)\n", data_size);
2502 static HRESULT parse_vertex_colors(IDirectXFileData *filedata, struct mesh_data *mesh)
2510 HeapFree(GetProcessHeap(), 0, mesh->vertex_colors);
2511 mesh->vertex_colors = NULL;
2513 hr = IDirectXFileData_GetData(filedata, NULL, &data_size, (void**)&data);
2514 if (FAILED(hr)) return hr;
2516 /* template IndexedColor {
2518 * ColorRGBA indexColor;
2520 * template MeshVertexColors {
2521 * DWORD nVertexColors;
2522 * array IndexedColor vertexColors[nVertexColors];
2526 if (data_size < sizeof(DWORD))
2527 goto truncated_data_error;
2528 num_colors = *(DWORD*)data;
2529 data += sizeof(DWORD);
2530 if (data_size < sizeof(DWORD) + num_colors * (sizeof(DWORD) + sizeof(D3DCOLORVALUE)))
2531 goto truncated_data_error;
2533 mesh->vertex_colors = HeapAlloc(GetProcessHeap(), 0, mesh->num_vertices * sizeof(DWORD));
2534 if (!mesh->vertex_colors)
2535 return E_OUTOFMEMORY;
2537 for (i = 0; i < mesh->num_vertices; i++)
2538 mesh->vertex_colors[i] = D3DCOLOR_ARGB(0, 0xff, 0xff, 0xff);
2539 for (i = 0; i < num_colors; i++)
2541 D3DCOLORVALUE color;
2542 DWORD index = *(DWORD*)data;
2543 data += sizeof(DWORD);
2544 if (index >= mesh->num_vertices) {
2545 WARN("vertex color %u references undefined vertex %u (only %u vertices)\n",
2546 i, index, mesh->num_vertices);
2549 memcpy(&color, data, sizeof(color));
2550 data += sizeof(color);
2551 color.r = fminf(1.0f, fmaxf(0.0f, color.r));
2552 color.g = fminf(1.0f, fmaxf(0.0f, color.g));
2553 color.b = fminf(1.0f, fmaxf(0.0f, color.b));
2554 color.a = fminf(1.0f, fmaxf(0.0f, color.a));
2555 mesh->vertex_colors[index] = D3DCOLOR_ARGB((BYTE)(color.a * 255.0f + 0.5f),
2556 (BYTE)(color.r * 255.0f + 0.5f),
2557 (BYTE)(color.g * 255.0f + 0.5f),
2558 (BYTE)(color.b * 255.0f + 0.5f));
2561 mesh->fvf |= D3DFVF_DIFFUSE;
2564 truncated_data_error:
2565 WARN("truncated data (%u bytes)\n", data_size);
2569 static HRESULT parse_normals(IDirectXFileData *filedata, struct mesh_data *mesh)
2574 DWORD *index_out_ptr;
2576 DWORD num_face_indices = mesh->num_poly_faces * 2 + mesh->num_tri_faces;
2578 HeapFree(GetProcessHeap(), 0, mesh->normals);
2579 mesh->num_normals = 0;
2580 mesh->normals = NULL;
2581 mesh->normal_indices = NULL;
2582 mesh->fvf |= D3DFVF_NORMAL;
2584 hr = IDirectXFileData_GetData(filedata, NULL, &data_size, (void**)&data);
2585 if (FAILED(hr)) return hr;
2587 /* template Vector {
2592 * template MeshFace {
2593 * DWORD nFaceVertexIndices;
2594 * array DWORD faceVertexIndices[nFaceVertexIndices];
2596 * template MeshNormals {
2598 * array Vector normals[nNormals];
2599 * DWORD nFaceNormals;
2600 * array MeshFace faceNormals[nFaceNormals];
2604 if (data_size < sizeof(DWORD) * 2)
2605 goto truncated_data_error;
2606 mesh->num_normals = *(DWORD*)data;
2607 data += sizeof(DWORD);
2608 if (data_size < sizeof(DWORD) * 2 + mesh->num_normals * sizeof(D3DXVECTOR3) +
2609 num_face_indices * sizeof(DWORD))
2610 goto truncated_data_error;
2612 mesh->normals = HeapAlloc(GetProcessHeap(), 0, mesh->num_normals * sizeof(D3DXVECTOR3));
2613 mesh->normal_indices = HeapAlloc(GetProcessHeap(), 0, num_face_indices * sizeof(DWORD));
2614 if (!mesh->normals || !mesh->normal_indices)
2615 return E_OUTOFMEMORY;
2617 memcpy(mesh->normals, data, mesh->num_normals * sizeof(D3DXVECTOR3));
2618 data += mesh->num_normals * sizeof(D3DXVECTOR3);
2619 for (i = 0; i < mesh->num_normals; i++)
2620 D3DXVec3Normalize(&mesh->normals[i], &mesh->normals[i]);
2622 if (*(DWORD*)data != mesh->num_poly_faces) {
2623 WARN("number of face normals (%u) doesn't match number of faces (%u)\n",
2624 *(DWORD*)data, mesh->num_poly_faces);
2627 data += sizeof(DWORD);
2628 index_out_ptr = mesh->normal_indices;
2629 for (i = 0; i < mesh->num_poly_faces; i++)
2632 DWORD count = *(DWORD*)data;
2633 if (count != mesh->num_tri_per_face[i] + 2) {
2634 WARN("face %u: number of normals (%u) doesn't match number of vertices (%u)\n",
2635 i, count, mesh->num_tri_per_face[i] + 2);
2638 data += sizeof(DWORD);
2640 for (j = 0; j < count; j++) {
2641 DWORD normal_index = *(DWORD*)data;
2642 if (normal_index >= mesh->num_normals) {
2643 WARN("face %u, normal index %u: reference to undefined normal %u (only %u normals)\n",
2644 i, j, normal_index, mesh->num_normals);
2647 *index_out_ptr++ = normal_index;
2648 data += sizeof(DWORD);
2653 truncated_data_error:
2654 WARN("truncated data (%u bytes)\n", data_size);
2658 /* for provide_flags parameters */
2659 #define PROVIDE_MATERIALS 0x1
2660 #define PROVIDE_SKININFO 0x2
2661 #define PROVIDE_ADJACENCY 0x4
2663 static HRESULT parse_mesh(IDirectXFileData *filedata, struct mesh_data *mesh_data, DWORD provide_flags)
2667 BYTE *data, *in_ptr;
2668 DWORD *index_out_ptr;
2670 IDirectXFileData *child;
2676 * array Vector vertices[nVertices];
2678 * array MeshFace faces[nFaces];
2683 hr = IDirectXFileData_GetData(filedata, NULL, &data_size, (void**)&data);
2684 if (FAILED(hr)) return hr;
2687 if (data_size < sizeof(DWORD) * 2)
2688 goto truncated_data_error;
2689 mesh_data->num_vertices = *(DWORD*)in_ptr;
2690 if (data_size < sizeof(DWORD) * 2 + mesh_data->num_vertices * sizeof(D3DXVECTOR3))
2691 goto truncated_data_error;
2692 in_ptr += sizeof(DWORD) + mesh_data->num_vertices * sizeof(D3DXVECTOR3);
2694 mesh_data->num_poly_faces = *(DWORD*)in_ptr;
2695 in_ptr += sizeof(DWORD);
2697 mesh_data->num_tri_faces = 0;
2698 for (i = 0; i < mesh_data->num_poly_faces; i++)
2700 DWORD num_poly_vertices;
2703 if (data_size - (in_ptr - data) < sizeof(DWORD))
2704 goto truncated_data_error;
2705 num_poly_vertices = *(DWORD*)in_ptr;
2706 in_ptr += sizeof(DWORD);
2707 if (data_size - (in_ptr - data) < num_poly_vertices * sizeof(DWORD))
2708 goto truncated_data_error;
2709 if (num_poly_vertices < 3) {
2710 WARN("face %u has only %u vertices\n", i, num_poly_vertices);
2713 for (j = 0; j < num_poly_vertices; j++) {
2714 if (*(DWORD*)in_ptr >= mesh_data->num_vertices) {
2715 WARN("face %u, index %u: undefined vertex %u (only %u vertices)\n",
2716 i, j, *(DWORD*)in_ptr, mesh_data->num_vertices);
2719 in_ptr += sizeof(DWORD);
2721 mesh_data->num_tri_faces += num_poly_vertices - 2;
2724 mesh_data->fvf = D3DFVF_XYZ;
2726 mesh_data->vertices = HeapAlloc(GetProcessHeap(), 0,
2727 mesh_data->num_vertices * sizeof(*mesh_data->vertices));
2728 mesh_data->num_tri_per_face = HeapAlloc(GetProcessHeap(), 0,
2729 mesh_data->num_poly_faces * sizeof(*mesh_data->num_tri_per_face));
2730 mesh_data->indices = HeapAlloc(GetProcessHeap(), 0,
2731 (mesh_data->num_tri_faces + mesh_data->num_poly_faces * 2) * sizeof(*mesh_data->indices));
2732 if (!mesh_data->vertices || !mesh_data->num_tri_per_face || !mesh_data->indices)
2733 return E_OUTOFMEMORY;
2735 in_ptr = data + sizeof(DWORD);
2736 memcpy(mesh_data->vertices, in_ptr, mesh_data->num_vertices * sizeof(D3DXVECTOR3));
2737 in_ptr += mesh_data->num_vertices * sizeof(D3DXVECTOR3) + sizeof(DWORD);
2739 index_out_ptr = mesh_data->indices;
2740 for (i = 0; i < mesh_data->num_poly_faces; i++)
2744 count = *(DWORD*)in_ptr;
2745 in_ptr += sizeof(DWORD);
2746 mesh_data->num_tri_per_face[i] = count - 2;
2749 *index_out_ptr++ = *(DWORD*)in_ptr;
2750 in_ptr += sizeof(DWORD);
2754 while (SUCCEEDED(hr = get_next_child(filedata, &child, &type)))
2756 if (IsEqualGUID(type, &TID_D3DRMMeshNormals)) {
2757 hr = parse_normals(child, mesh_data);
2758 } else if (IsEqualGUID(type, &TID_D3DRMMeshVertexColors)) {
2759 hr = parse_vertex_colors(child, mesh_data);
2760 } else if (IsEqualGUID(type, &TID_D3DRMMeshTextureCoords)) {
2761 hr = parse_texture_coords(child, mesh_data);
2762 } else if (IsEqualGUID(type, &TID_D3DRMMeshMaterialList) &&
2763 (provide_flags & PROVIDE_MATERIALS))
2765 hr = parse_material_list(child, mesh_data);
2766 } else if (provide_flags & PROVIDE_SKININFO) {
2767 if (IsEqualGUID(type, &DXFILEOBJ_XSkinMeshHeader)) {
2768 FIXME("Skin mesh loading not implemented.\n");
2770 } else if (IsEqualGUID(type, &DXFILEOBJ_SkinWeights)) {
2771 /* ignored without XSkinMeshHeader */
2777 return hr == DXFILEERR_NOMOREOBJECTS ? D3D_OK : hr;
2778 truncated_data_error:
2779 WARN("truncated data (%u bytes)\n", data_size);
2783 static HRESULT generate_effects(ID3DXBuffer *materials, DWORD num_materials,
2784 ID3DXBuffer **effects)
2787 D3DXEFFECTINSTANCE *effect_ptr;
2789 const D3DXMATERIAL *material_ptr = ID3DXBuffer_GetBufferPointer(materials);
2790 static const struct {
2791 const char *param_name;
2795 } material_effects[] = {
2796 #define EFFECT_TABLE_ENTRY(str, field) \
2797 {str, sizeof(str), sizeof(material_ptr->MatD3D.field), offsetof(D3DXMATERIAL, MatD3D.field)}
2798 EFFECT_TABLE_ENTRY("Diffuse", Diffuse),
2799 EFFECT_TABLE_ENTRY("Power", Power),
2800 EFFECT_TABLE_ENTRY("Specular", Specular),
2801 EFFECT_TABLE_ENTRY("Emissive", Emissive),
2802 EFFECT_TABLE_ENTRY("Ambient", Ambient),
2803 #undef EFFECT_TABLE_ENTRY
2805 static const char texture_paramname[] = "Texture0@Name";
2809 /* effects buffer layout:
2811 * D3DXEFFECTINSTANCE effects[num_materials];
2812 * for (effect in effects)
2814 * D3DXEFFECTDEFAULT defaults[effect.NumDefaults];
2815 * for (default in defaults)
2817 * *default.pParamName;
2822 buffer_size = sizeof(D3DXEFFECTINSTANCE);
2823 buffer_size += sizeof(D3DXEFFECTDEFAULT) * ARRAY_SIZE(material_effects);
2824 for (i = 0; i < ARRAY_SIZE(material_effects); i++) {
2825 buffer_size += material_effects[i].name_size;
2826 buffer_size += material_effects[i].num_bytes;
2828 buffer_size *= num_materials;
2829 for (i = 0; i < num_materials; i++) {
2830 if (material_ptr[i].pTextureFilename) {
2831 buffer_size += sizeof(D3DXEFFECTDEFAULT);
2832 buffer_size += sizeof(texture_paramname);
2833 buffer_size += strlen(material_ptr[i].pTextureFilename) + 1;
2837 hr = D3DXCreateBuffer(buffer_size, effects);
2838 if (FAILED(hr)) return hr;
2839 effect_ptr = ID3DXBuffer_GetBufferPointer(*effects);
2840 out_ptr = (BYTE*)(effect_ptr + num_materials);
2842 for (i = 0; i < num_materials; i++)
2845 D3DXEFFECTDEFAULT *defaults = (D3DXEFFECTDEFAULT*)out_ptr;
2847 effect_ptr->pDefaults = defaults;
2848 effect_ptr->NumDefaults = material_ptr->pTextureFilename ? 6 : 5;
2849 out_ptr = (BYTE*)(effect_ptr->pDefaults + effect_ptr->NumDefaults);
2851 for (j = 0; j < ARRAY_SIZE(material_effects); j++)
2853 defaults->pParamName = (LPSTR)out_ptr;
2854 strcpy(defaults->pParamName, material_effects[j].param_name);
2855 defaults->pValue = defaults->pParamName + material_effects[j].name_size;
2856 defaults->Type = D3DXEDT_FLOATS;
2857 defaults->NumBytes = material_effects[j].num_bytes;
2858 memcpy(defaults->pValue, (BYTE*)material_ptr + material_effects[j].value_offset, defaults->NumBytes);
2859 out_ptr = (BYTE*)defaults->pValue + defaults->NumBytes;
2863 if (material_ptr->pTextureFilename) {
2864 defaults->pParamName = (LPSTR)out_ptr;
2865 strcpy(defaults->pParamName, texture_paramname);
2866 defaults->pValue = defaults->pParamName + sizeof(texture_paramname);
2867 defaults->Type = D3DXEDT_STRING;
2868 defaults->NumBytes = strlen(material_ptr->pTextureFilename) + 1;
2869 strcpy(defaults->pValue, material_ptr->pTextureFilename);
2870 out_ptr = (BYTE*)defaults->pValue + defaults->NumBytes;
2875 assert(out_ptr - (BYTE*)ID3DXBuffer_GetBufferPointer(*effects) == buffer_size);
2880 /* change to D3DXLoadSkinMeshFromXof when ID3DXFileData is implemented */
2881 static HRESULT load_skin_mesh_from_xof(IDirectXFileData *filedata,
2883 LPDIRECT3DDEVICE9 device,
2884 LPD3DXBUFFER *adjacency_out,
2885 LPD3DXBUFFER *materials_out,
2886 LPD3DXBUFFER *effects_out,
2887 DWORD *num_materials_out,
2888 LPD3DXSKININFO *skin_info_out,
2889 LPD3DXMESH *mesh_out)
2892 DWORD *index_in_ptr;
2893 struct mesh_data mesh_data;
2894 DWORD total_vertices;
2895 ID3DXMesh *d3dxmesh = NULL;
2896 ID3DXBuffer *adjacency = NULL;
2897 ID3DXBuffer *materials = NULL;
2898 ID3DXBuffer *effects = NULL;
2899 struct vertex_duplication {
2902 } *duplications = NULL;
2904 void *vertices = NULL;
2905 void *indices = NULL;
2907 DWORD provide_flags = 0;
2909 ZeroMemory(&mesh_data, sizeof(mesh_data));
2911 if (num_materials_out || materials_out || effects_out)
2912 provide_flags |= PROVIDE_MATERIALS;
2914 provide_flags |= PROVIDE_SKININFO;
2916 hr = parse_mesh(filedata, &mesh_data, provide_flags);
2917 if (FAILED(hr)) goto cleanup;
2919 total_vertices = mesh_data.num_vertices;
2920 if (mesh_data.fvf & D3DFVF_NORMAL) {
2921 /* duplicate vertices with multiple normals */
2922 DWORD num_face_indices = mesh_data.num_poly_faces * 2 + mesh_data.num_tri_faces;
2923 duplications = HeapAlloc(GetProcessHeap(), 0, (mesh_data.num_vertices + num_face_indices) * sizeof(*duplications));
2924 if (!duplications) {
2928 for (i = 0; i < total_vertices; i++)
2930 duplications[i].normal_index = -1;
2931 list_init(&duplications[i].entry);
2933 for (i = 0; i < num_face_indices; i++) {
2934 DWORD vertex_index = mesh_data.indices[i];
2935 DWORD normal_index = mesh_data.normal_indices[i];
2936 struct vertex_duplication *dup_ptr = &duplications[vertex_index];
2938 if (dup_ptr->normal_index == -1) {
2939 dup_ptr->normal_index = normal_index;
2941 D3DXVECTOR3 *new_normal = &mesh_data.normals[normal_index];
2942 struct list *dup_list = &dup_ptr->entry;
2944 D3DXVECTOR3 *cur_normal = &mesh_data.normals[dup_ptr->normal_index];
2945 if (new_normal->x == cur_normal->x &&
2946 new_normal->y == cur_normal->y &&
2947 new_normal->z == cur_normal->z)
2949 mesh_data.indices[i] = dup_ptr - duplications;
2951 } else if (!list_next(dup_list, &dup_ptr->entry)) {
2952 dup_ptr = &duplications[total_vertices++];
2953 dup_ptr->normal_index = normal_index;
2954 list_add_tail(dup_list, &dup_ptr->entry);
2955 mesh_data.indices[i] = dup_ptr - duplications;
2958 dup_ptr = LIST_ENTRY(list_next(dup_list, &dup_ptr->entry),
2959 struct vertex_duplication, entry);
2966 hr = D3DXCreateMeshFVF(mesh_data.num_tri_faces, total_vertices, options, mesh_data.fvf, device, &d3dxmesh);
2967 if (FAILED(hr)) goto cleanup;
2969 hr = d3dxmesh->lpVtbl->LockVertexBuffer(d3dxmesh, D3DLOCK_DISCARD, &vertices);
2970 if (FAILED(hr)) goto cleanup;
2973 for (i = 0; i < mesh_data.num_vertices; i++) {
2974 *(D3DXVECTOR3*)out_ptr = mesh_data.vertices[i];
2975 out_ptr += sizeof(D3DXVECTOR3);
2976 if (mesh_data.fvf & D3DFVF_NORMAL) {
2977 if (duplications[i].normal_index == -1)
2978 ZeroMemory(out_ptr, sizeof(D3DXVECTOR3));
2980 *(D3DXVECTOR3*)out_ptr = mesh_data.normals[duplications[i].normal_index];
2981 out_ptr += sizeof(D3DXVECTOR3);
2983 if (mesh_data.fvf & D3DFVF_DIFFUSE) {
2984 *(DWORD*)out_ptr = mesh_data.vertex_colors[i];
2985 out_ptr += sizeof(DWORD);
2987 if (mesh_data.fvf & D3DFVF_TEX1) {
2988 *(D3DXVECTOR2*)out_ptr = mesh_data.tex_coords[i];
2989 out_ptr += sizeof(D3DXVECTOR2);
2992 if (mesh_data.fvf & D3DFVF_NORMAL) {
2993 DWORD vertex_size = D3DXGetFVFVertexSize(mesh_data.fvf);
2995 for (i = 0; i < mesh_data.num_vertices; i++) {
2996 struct vertex_duplication *dup_ptr;
2997 LIST_FOR_EACH_ENTRY(dup_ptr, &duplications[i].entry, struct vertex_duplication, entry)
2999 int j = dup_ptr - duplications;
3000 BYTE *dest_vertex = (BYTE*)vertices + j * vertex_size;
3002 memcpy(dest_vertex, out_ptr, vertex_size);
3003 dest_vertex += sizeof(D3DXVECTOR3);
3004 *(D3DXVECTOR3*)dest_vertex = mesh_data.normals[dup_ptr->normal_index];
3006 out_ptr += vertex_size;
3009 d3dxmesh->lpVtbl->UnlockVertexBuffer(d3dxmesh);
3011 hr = d3dxmesh->lpVtbl->LockIndexBuffer(d3dxmesh, D3DLOCK_DISCARD, (void**)&indices);
3012 if (FAILED(hr)) goto cleanup;
3014 index_in_ptr = mesh_data.indices;
3015 #define FILL_INDEX_BUFFER(indices_var) \
3016 for (i = 0; i < mesh_data.num_poly_faces; i++) \
3018 DWORD count = mesh_data.num_tri_per_face[i]; \
3019 WORD first_index = *index_in_ptr++; \
3021 *indices_var++ = first_index; \
3022 *indices_var++ = *index_in_ptr; \
3024 *indices_var++ = *index_in_ptr; \
3028 if (options & D3DXMESH_32BIT) {
3029 DWORD *dword_indices = indices;
3030 FILL_INDEX_BUFFER(dword_indices)
3032 WORD *word_indices = indices;
3033 FILL_INDEX_BUFFER(word_indices)
3035 #undef FILL_INDEX_BUFFER
3036 d3dxmesh->lpVtbl->UnlockIndexBuffer(d3dxmesh);
3038 if (mesh_data.material_indices) {
3039 DWORD *attrib_buffer = NULL;
3040 hr = d3dxmesh->lpVtbl->LockAttributeBuffer(d3dxmesh, D3DLOCK_DISCARD, &attrib_buffer);
3041 if (FAILED(hr)) goto cleanup;
3042 for (i = 0; i < mesh_data.num_poly_faces; i++)
3044 DWORD count = mesh_data.num_tri_per_face[i];
3046 *attrib_buffer++ = mesh_data.material_indices[i];
3048 d3dxmesh->lpVtbl->UnlockAttributeBuffer(d3dxmesh);
3050 hr = d3dxmesh->lpVtbl->OptimizeInplace(d3dxmesh,
3051 D3DXMESHOPT_ATTRSORT | D3DXMESHOPT_IGNOREVERTS | D3DXMESHOPT_DONOTSPLIT,
3052 NULL, NULL, NULL, NULL);
3053 if (FAILED(hr)) goto cleanup;
3056 if (mesh_data.num_materials && (materials_out || effects_out)) {
3057 DWORD buffer_size = mesh_data.num_materials * sizeof(D3DXMATERIAL);
3058 char *strings_out_ptr;
3059 D3DXMATERIAL *materials_ptr;
3061 for (i = 0; i < mesh_data.num_materials; i++) {
3062 if (mesh_data.materials[i].pTextureFilename)
3063 buffer_size += strlen(mesh_data.materials[i].pTextureFilename) + 1;
3066 hr = D3DXCreateBuffer(buffer_size, &materials);
3067 if (FAILED(hr)) goto cleanup;
3069 materials_ptr = ID3DXBuffer_GetBufferPointer(materials);
3070 memcpy(materials_ptr, mesh_data.materials, mesh_data.num_materials * sizeof(D3DXMATERIAL));
3071 strings_out_ptr = (char*)(materials_ptr + mesh_data.num_materials);
3072 for (i = 0; i < mesh_data.num_materials; i++) {
3073 if (materials_ptr[i].pTextureFilename) {
3074 strcpy(strings_out_ptr, mesh_data.materials[i].pTextureFilename);
3075 materials_ptr[i].pTextureFilename = strings_out_ptr;
3076 strings_out_ptr += strlen(mesh_data.materials[i].pTextureFilename) + 1;
3081 if (mesh_data.num_materials && effects_out) {
3082 hr = generate_effects(materials, mesh_data.num_materials, &effects);
3083 if (FAILED(hr)) goto cleanup;
3085 if (!materials_out) {
3086 ID3DXBuffer_Release(materials);
3091 if (adjacency_out) {
3092 hr = D3DXCreateBuffer(mesh_data.num_tri_faces * 3 * sizeof(DWORD), &adjacency);
3093 if (FAILED(hr)) goto cleanup;
3094 hr = d3dxmesh->lpVtbl->GenerateAdjacency(d3dxmesh, 0.0f, ID3DXBuffer_GetBufferPointer(adjacency));
3095 if (FAILED(hr)) goto cleanup;
3098 *mesh_out = d3dxmesh;
3099 if (adjacency_out) *adjacency_out = adjacency;
3100 if (num_materials_out) *num_materials_out = mesh_data.num_materials;
3101 if (materials_out) *materials_out = materials;
3102 if (effects_out) *effects_out = effects;
3103 if (skin_info_out) *skin_info_out = NULL;
3108 if (d3dxmesh) IUnknown_Release(d3dxmesh);
3109 if (adjacency) ID3DXBuffer_Release(adjacency);
3110 if (materials) ID3DXBuffer_Release(materials);
3111 if (effects) ID3DXBuffer_Release(effects);
3113 HeapFree(GetProcessHeap(), 0, mesh_data.vertices);
3114 HeapFree(GetProcessHeap(), 0, mesh_data.num_tri_per_face);
3115 HeapFree(GetProcessHeap(), 0, mesh_data.indices);
3116 HeapFree(GetProcessHeap(), 0, mesh_data.normals);
3117 HeapFree(GetProcessHeap(), 0, mesh_data.normal_indices);
3118 destroy_materials(&mesh_data);
3119 HeapFree(GetProcessHeap(), 0, mesh_data.tex_coords);
3120 HeapFree(GetProcessHeap(), 0, mesh_data.vertex_colors);
3121 HeapFree(GetProcessHeap(), 0, duplications);
3125 HRESULT WINAPI D3DXLoadMeshHierarchyFromXA(LPCSTR filename,
3127 LPDIRECT3DDEVICE9 device,
3128 LPD3DXALLOCATEHIERARCHY alloc_hier,
3129 LPD3DXLOADUSERDATA load_user_data,
3130 LPD3DXFRAME *frame_hierarchy,
3131 LPD3DXANIMATIONCONTROLLER *anim_controller)
3137 TRACE("(%s, %x, %p, %p, %p, %p, %p)\n", debugstr_a(filename), options,
3138 device, alloc_hier, load_user_data, frame_hierarchy, anim_controller);
3141 return D3DERR_INVALIDCALL;
3143 len = MultiByteToWideChar(CP_ACP, 0, filename, -1, NULL, 0);
3144 filenameW = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
3145 if (!filenameW) return E_OUTOFMEMORY;
3146 MultiByteToWideChar(CP_ACP, 0, filename, -1, filenameW, len);
3148 hr = D3DXLoadMeshHierarchyFromXW(filenameW, options, device,
3149 alloc_hier, load_user_data, frame_hierarchy, anim_controller);
3150 HeapFree(GetProcessHeap(), 0, filenameW);
3155 HRESULT WINAPI D3DXLoadMeshHierarchyFromXW(LPCWSTR filename,
3157 LPDIRECT3DDEVICE9 device,
3158 LPD3DXALLOCATEHIERARCHY alloc_hier,
3159 LPD3DXLOADUSERDATA load_user_data,
3160 LPD3DXFRAME *frame_hierarchy,
3161 LPD3DXANIMATIONCONTROLLER *anim_controller)
3167 TRACE("(%s, %x, %p, %p, %p, %p, %p)\n", debugstr_w(filename), options,
3168 device, alloc_hier, load_user_data, frame_hierarchy, anim_controller);
3171 return D3DERR_INVALIDCALL;
3173 hr = map_view_of_file(filename, &buffer, &size);
3175 return D3DXERR_INVALIDDATA;
3177 hr = D3DXLoadMeshHierarchyFromXInMemory(buffer, size, options, device,
3178 alloc_hier, load_user_data, frame_hierarchy, anim_controller);
3180 UnmapViewOfFile(buffer);
3185 static HRESULT filedata_get_name(IDirectXFileData *filedata, char **name)
3190 hr = IDirectXFileData_GetName(filedata, NULL, &name_len);
3191 if (FAILED(hr)) return hr;
3195 *name = HeapAlloc(GetProcessHeap(), 0, name_len);
3196 if (!*name) return E_OUTOFMEMORY;
3198 hr = IDirectXFileObject_GetName(filedata, *name, &name_len);
3200 HeapFree(GetProcessHeap(), 0, name);
3207 static HRESULT load_mesh_container(IDirectXFileData *filedata,
3209 LPDIRECT3DDEVICE9 device,
3210 LPD3DXALLOCATEHIERARCHY alloc_hier,
3211 D3DXMESHCONTAINER **mesh_container)
3214 ID3DXBuffer *adjacency = NULL;
3215 ID3DXBuffer *materials = NULL;
3216 ID3DXBuffer *effects = NULL;
3217 ID3DXSkinInfo *skin_info = NULL;
3218 D3DXMESHDATA mesh_data;
3219 DWORD num_materials = 0;
3222 mesh_data.Type = D3DXMESHTYPE_MESH;
3223 mesh_data.u.pMesh = NULL;
3225 hr = load_skin_mesh_from_xof(filedata, options, device,
3226 &adjacency, &materials, &effects, &num_materials,
3227 &skin_info, &mesh_data.u.pMesh);
3228 if (FAILED(hr)) return hr;
3230 hr = filedata_get_name(filedata, &name);
3231 if (FAILED(hr)) goto cleanup;
3233 hr = alloc_hier->lpVtbl->CreateMeshContainer(alloc_hier, name, &mesh_data,
3234 materials ? ID3DXBuffer_GetBufferPointer(materials) : NULL,
3235 effects ? ID3DXBuffer_GetBufferPointer(effects) : NULL,
3237 adjacency ? ID3DXBuffer_GetBufferPointer(adjacency) : NULL,
3238 skin_info, mesh_container);
3241 if (materials) ID3DXBuffer_Release(materials);
3242 if (effects) ID3DXBuffer_Release(effects);
3243 if (adjacency) ID3DXBuffer_Release(adjacency);
3244 if (skin_info) IUnknown_Release(skin_info);
3245 if (mesh_data.u.pMesh) IUnknown_Release(mesh_data.u.pMesh);
3246 HeapFree(GetProcessHeap(), 0, name);
3250 static HRESULT parse_transform_matrix(IDirectXFileData *filedata, D3DXMATRIX *transform)
3256 /* template Matrix4x4 {
3257 * array FLOAT matrix[16];
3259 * template FrameTransformMatrix {
3260 * Matrix4x4 frameMatrix;
3264 hr = IDirectXFileData_GetData(filedata, NULL, &data_size, (void**)&data);
3265 if (FAILED(hr)) return hr;
3267 if (data_size != sizeof(D3DXMATRIX)) {
3268 WARN("incorrect data size (%u bytes)\n", data_size);
3272 memcpy(transform, data, sizeof(D3DXMATRIX));
3277 static HRESULT load_frame(IDirectXFileData *filedata,
3279 LPDIRECT3DDEVICE9 device,
3280 LPD3DXALLOCATEHIERARCHY alloc_hier,
3281 D3DXFRAME **frame_out)
3285 IDirectXFileData *child;
3287 D3DXFRAME *frame = NULL;
3288 D3DXMESHCONTAINER **next_container;
3289 D3DXFRAME **next_child;
3291 hr = filedata_get_name(filedata, &name);
3292 if (FAILED(hr)) return hr;
3294 hr = alloc_hier->lpVtbl->CreateFrame(alloc_hier, name, frame_out);
3295 HeapFree(GetProcessHeap(), 0, name);
3296 if (FAILED(hr)) return E_FAIL;
3299 D3DXMatrixIdentity(&frame->TransformationMatrix);
3300 next_child = &frame->pFrameFirstChild;
3301 next_container = &frame->pMeshContainer;
3303 while (SUCCEEDED(hr = get_next_child(filedata, &child, &type)))
3305 if (IsEqualGUID(type, &TID_D3DRMMesh)) {
3306 hr = load_mesh_container(child, options, device, alloc_hier, next_container);
3308 next_container = &(*next_container)->pNextMeshContainer;
3309 } else if (IsEqualGUID(type, &TID_D3DRMFrameTransformMatrix)) {
3310 hr = parse_transform_matrix(child, &frame->TransformationMatrix);
3311 } else if (IsEqualGUID(type, &TID_D3DRMFrame)) {
3312 hr = load_frame(child, options, device, alloc_hier, next_child);
3314 next_child = &(*next_child)->pFrameSibling;
3316 if (FAILED(hr)) break;
3318 if (hr == DXFILEERR_NOMOREOBJECTS)
3324 HRESULT WINAPI D3DXLoadMeshHierarchyFromXInMemory(LPCVOID memory,
3327 LPDIRECT3DDEVICE9 device,
3328 LPD3DXALLOCATEHIERARCHY alloc_hier,
3329 LPD3DXLOADUSERDATA load_user_data,
3330 LPD3DXFRAME *frame_hierarchy,
3331 LPD3DXANIMATIONCONTROLLER *anim_controller)
3334 IDirectXFile *dxfile = NULL;
3335 IDirectXFileEnumObject *enumobj = NULL;
3336 IDirectXFileData *filedata = NULL;
3337 DXFILELOADMEMORY source;
3338 D3DXFRAME *first_frame = NULL;
3339 D3DXFRAME **next_frame = &first_frame;
3341 TRACE("(%p, %u, %x, %p, %p, %p, %p, %p)\n", memory, memory_size, options,
3342 device, alloc_hier, load_user_data, frame_hierarchy, anim_controller);
3344 if (!memory || !memory_size || !device || !frame_hierarchy || !alloc_hier)
3345 return D3DERR_INVALIDCALL;
3346 if (load_user_data || anim_controller) {
3348 FIXME("Loading user data not implemented\n");
3349 if (anim_controller)
3350 FIXME("Animation controller creation not implemented\n");
3354 hr = DirectXFileCreate(&dxfile);
3355 if (FAILED(hr)) goto cleanup;
3357 hr = IDirectXFile_RegisterTemplates(dxfile, D3DRM_XTEMPLATES, D3DRM_XTEMPLATE_BYTES);
3358 if (FAILED(hr)) goto cleanup;
3360 source.lpMemory = (void*)memory;
3361 source.dSize = memory_size;
3362 hr = IDirectXFile_CreateEnumObject(dxfile, &source, DXFILELOAD_FROMMEMORY, &enumobj);
3363 if (FAILED(hr)) goto cleanup;
3365 while (SUCCEEDED(hr = IDirectXFileEnumObject_GetNextDataObject(enumobj, &filedata)))
3367 const GUID *guid = NULL;
3369 hr = IDirectXFileData_GetType(filedata, &guid);
3370 if (SUCCEEDED(hr)) {
3371 if (IsEqualGUID(guid, &TID_D3DRMMesh)) {
3372 hr = alloc_hier->lpVtbl->CreateFrame(alloc_hier, NULL, next_frame);
3378 D3DXMatrixIdentity(&(*next_frame)->TransformationMatrix);
3380 hr = load_mesh_container(filedata, options, device, alloc_hier, &(*next_frame)->pMeshContainer);
3381 if (FAILED(hr)) goto cleanup;
3382 } else if (IsEqualGUID(guid, &TID_D3DRMFrame)) {
3383 hr = load_frame(filedata, options, device, alloc_hier, next_frame);
3384 if (FAILED(hr)) goto cleanup;
3387 next_frame = &(*next_frame)->pFrameSibling;
3390 IDirectXFileData_Release(filedata);
3395 if (hr != DXFILEERR_NOMOREOBJECTS)
3400 } else if (first_frame->pFrameSibling) {
3401 D3DXFRAME *root_frame = NULL;
3402 hr = alloc_hier->lpVtbl->CreateFrame(alloc_hier, NULL, &root_frame);
3407 D3DXMatrixIdentity(&root_frame->TransformationMatrix);
3408 root_frame->pFrameFirstChild = first_frame;
3409 *frame_hierarchy = root_frame;
3412 *frame_hierarchy = first_frame;
3417 if (FAILED(hr) && first_frame) D3DXFrameDestroy(first_frame, alloc_hier);
3418 if (filedata) IDirectXFileData_Release(filedata);
3419 if (enumobj) IDirectXFileEnumObject_Release(enumobj);
3420 if (dxfile) IDirectXFile_Release(dxfile);
3424 HRESULT WINAPI D3DXFrameDestroy(LPD3DXFRAME frame, LPD3DXALLOCATEHIERARCHY alloc_hier)
3429 TRACE("(%p, %p)\n", frame, alloc_hier);
3431 if (!frame || !alloc_hier)
3432 return D3DERR_INVALIDCALL;
3435 D3DXMESHCONTAINER *container;
3436 D3DXFRAME *current_frame;
3438 if (frame->pFrameSibling) {
3439 current_frame = frame->pFrameSibling;
3440 frame->pFrameSibling = current_frame->pFrameSibling;
3441 current_frame->pFrameSibling = NULL;
3443 current_frame = frame;
3447 if (current_frame->pFrameFirstChild) {
3448 hr = D3DXFrameDestroy(current_frame->pFrameFirstChild, alloc_hier);
3449 if (FAILED(hr)) return hr;
3450 current_frame->pFrameFirstChild = NULL;
3453 container = current_frame->pMeshContainer;
3455 D3DXMESHCONTAINER *next_container = container->pNextMeshContainer;
3456 hr = alloc_hier->lpVtbl->DestroyMeshContainer(alloc_hier, container);
3457 if (FAILED(hr)) return hr;
3458 container = next_container;
3460 hr = alloc_hier->lpVtbl->DestroyFrame(alloc_hier, current_frame);
3461 if (FAILED(hr)) return hr;
3466 HRESULT WINAPI D3DXLoadMeshFromXA(LPCSTR filename,
3468 LPDIRECT3DDEVICE9 device,
3469 LPD3DXBUFFER *adjacency,
3470 LPD3DXBUFFER *materials,
3471 LPD3DXBUFFER *effect_instances,
3472 DWORD *num_materials,
3479 TRACE("(%s, %x, %p, %p, %p, %p, %p, %p)\n", debugstr_a(filename), options,
3480 device, adjacency, materials, effect_instances, num_materials, mesh);
3483 return D3DERR_INVALIDCALL;
3485 len = MultiByteToWideChar(CP_ACP, 0, filename, -1, NULL, 0);
3486 filenameW = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
3487 if (!filenameW) return E_OUTOFMEMORY;
3488 MultiByteToWideChar(CP_ACP, 0, filename, -1, filenameW, len);
3490 hr = D3DXLoadMeshFromXW(filenameW, options, device, adjacency, materials,
3491 effect_instances, num_materials, mesh);
3492 HeapFree(GetProcessHeap(), 0, filenameW);
3497 HRESULT WINAPI D3DXLoadMeshFromXW(LPCWSTR filename,
3499 LPDIRECT3DDEVICE9 device,
3500 LPD3DXBUFFER *adjacency,
3501 LPD3DXBUFFER *materials,
3502 LPD3DXBUFFER *effect_instances,
3503 DWORD *num_materials,
3510 TRACE("(%s, %x, %p, %p, %p, %p, %p, %p)\n", debugstr_w(filename), options,
3511 device, adjacency, materials, effect_instances, num_materials, mesh);
3514 return D3DERR_INVALIDCALL;
3516 hr = map_view_of_file(filename, &buffer, &size);
3518 return D3DXERR_INVALIDDATA;
3520 hr = D3DXLoadMeshFromXInMemory(buffer, size, options, device, adjacency,
3521 materials, effect_instances, num_materials, mesh);
3523 UnmapViewOfFile(buffer);
3528 HRESULT WINAPI D3DXLoadMeshFromXResource(HMODULE module,
3532 LPDIRECT3DDEVICE9 device,
3533 LPD3DXBUFFER *adjacency,
3534 LPD3DXBUFFER *materials,
3535 LPD3DXBUFFER *effect_instances,
3536 DWORD *num_materials,
3544 TRACE("(%p, %s, %s, %x, %p, %p, %p, %p, %p, %p)\n",
3545 module, debugstr_a(name), debugstr_a(type), options, device,
3546 adjacency, materials, effect_instances, num_materials, mesh);
3548 resinfo = FindResourceA(module, name, type);
3549 if (!resinfo) return D3DXERR_INVALIDDATA;
3551 hr = load_resource_into_memory(module, resinfo, &buffer, &size);
3552 if (FAILED(hr)) return D3DXERR_INVALIDDATA;
3554 return D3DXLoadMeshFromXInMemory(buffer, size, options, device, adjacency,
3555 materials, effect_instances, num_materials, mesh);
3558 struct mesh_container
3562 ID3DXBuffer *adjacency;
3563 ID3DXBuffer *materials;
3564 ID3DXBuffer *effects;
3565 DWORD num_materials;
3566 D3DXMATRIX transform;
3569 static HRESULT parse_frame(IDirectXFileData *filedata,
3571 LPDIRECT3DDEVICE9 device,
3572 const D3DXMATRIX *parent_transform,
3573 struct list *container_list,
3574 DWORD provide_flags)
3577 D3DXMATRIX transform = *parent_transform;
3578 IDirectXFileData *child;
3581 while (SUCCEEDED(hr = get_next_child(filedata, &child, &type)))
3583 if (IsEqualGUID(type, &TID_D3DRMMesh)) {
3584 struct mesh_container *container = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(*container));
3589 list_add_tail(container_list, &container->entry);
3590 container->transform = transform;
3592 hr = load_skin_mesh_from_xof(child, options, device,
3593 (provide_flags & PROVIDE_ADJACENCY) ? &container->adjacency : NULL,
3594 (provide_flags & PROVIDE_MATERIALS) ? &container->materials : NULL,
3595 NULL, &container->num_materials, NULL, &container->mesh);
3596 } else if (IsEqualGUID(type, &TID_D3DRMFrameTransformMatrix)) {
3597 D3DXMATRIX new_transform;
3598 hr = parse_transform_matrix(child, &new_transform);
3599 D3DXMatrixMultiply(&transform, &transform, &new_transform);
3600 } else if (IsEqualGUID(type, &TID_D3DRMFrame)) {
3601 hr = parse_frame(child, options, device, &transform, container_list, provide_flags);
3603 if (FAILED(hr)) break;
3605 return hr == DXFILEERR_NOMOREOBJECTS ? D3D_OK : hr;
3608 HRESULT WINAPI D3DXLoadMeshFromXInMemory(LPCVOID memory,
3611 LPDIRECT3DDEVICE9 device,
3612 LPD3DXBUFFER *adjacency_out,
3613 LPD3DXBUFFER *materials_out,
3614 LPD3DXBUFFER *effects_out,
3615 DWORD *num_materials_out,
3616 LPD3DXMESH *mesh_out)
3619 IDirectXFile *dxfile = NULL;
3620 IDirectXFileEnumObject *enumobj = NULL;
3621 IDirectXFileData *filedata = NULL;
3622 DXFILELOADMEMORY source;
3623 ID3DXBuffer *materials = NULL;
3624 ID3DXBuffer *effects = NULL;
3625 ID3DXBuffer *adjacency = NULL;
3626 struct list container_list = LIST_INIT(container_list);
3627 struct mesh_container *container_ptr, *next_container_ptr;
3628 DWORD num_materials;
3629 DWORD num_faces, num_vertices;
3630 D3DXMATRIX identity;
3632 DWORD provide_flags = 0;
3634 ID3DXMesh *concat_mesh = NULL;
3635 D3DVERTEXELEMENT9 concat_decl[MAX_FVF_DECL_SIZE];
3636 BYTE *concat_vertices = NULL;
3637 void *concat_indices = NULL;
3639 DWORD concat_vertex_size;
3641 TRACE("(%p, %u, %x, %p, %p, %p, %p, %p, %p)\n", memory, memory_size, options,
3642 device, adjacency_out, materials_out, effects_out, num_materials_out, mesh_out);
3644 if (!memory || !memory_size || !device || !mesh_out)
3645 return D3DERR_INVALIDCALL;
3647 hr = DirectXFileCreate(&dxfile);
3648 if (FAILED(hr)) goto cleanup;
3650 hr = IDirectXFile_RegisterTemplates(dxfile, D3DRM_XTEMPLATES, D3DRM_XTEMPLATE_BYTES);
3651 if (FAILED(hr)) goto cleanup;
3653 source.lpMemory = (void*)memory;
3654 source.dSize = memory_size;
3655 hr = IDirectXFile_CreateEnumObject(dxfile, &source, DXFILELOAD_FROMMEMORY, &enumobj);
3656 if (FAILED(hr)) goto cleanup;
3658 D3DXMatrixIdentity(&identity);
3659 if (adjacency_out) provide_flags |= PROVIDE_ADJACENCY;
3660 if (materials_out || effects_out) provide_flags |= PROVIDE_MATERIALS;
3662 while (SUCCEEDED(hr = IDirectXFileEnumObject_GetNextDataObject(enumobj, &filedata)))
3664 const GUID *guid = NULL;
3666 hr = IDirectXFileData_GetType(filedata, &guid);
3667 if (SUCCEEDED(hr)) {
3668 if (IsEqualGUID(guid, &TID_D3DRMMesh)) {
3669 container_ptr = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(*container_ptr));
3670 if (!container_ptr) {
3674 list_add_tail(&container_list, &container_ptr->entry);
3675 D3DXMatrixIdentity(&container_ptr->transform);
3677 hr = load_skin_mesh_from_xof(filedata, options, device,
3678 (provide_flags & PROVIDE_ADJACENCY) ? &container_ptr->adjacency : NULL,
3679 (provide_flags & PROVIDE_MATERIALS) ? &container_ptr->materials : NULL,
3680 NULL, &container_ptr->num_materials, NULL, &container_ptr->mesh);
3681 } else if (IsEqualGUID(guid, &TID_D3DRMFrame)) {
3682 hr = parse_frame(filedata, options, device, &identity, &container_list, provide_flags);
3684 if (FAILED(hr)) goto cleanup;
3686 IDirectXFileData_Release(filedata);
3691 if (hr != DXFILEERR_NOMOREOBJECTS)
3694 IDirectXFileEnumObject_Release(enumobj);
3696 IDirectXFile_Release(dxfile);
3699 if (list_empty(&container_list)) {
3708 LIST_FOR_EACH_ENTRY(container_ptr, &container_list, struct mesh_container, entry)
3710 ID3DXMesh *mesh = container_ptr->mesh;
3711 fvf |= mesh->lpVtbl->GetFVF(mesh);
3712 num_faces += mesh->lpVtbl->GetNumFaces(mesh);
3713 num_vertices += mesh->lpVtbl->GetNumVertices(mesh);
3714 num_materials += container_ptr->num_materials;
3717 hr = D3DXCreateMeshFVF(num_faces, num_vertices, options, fvf, device, &concat_mesh);
3718 if (FAILED(hr)) goto cleanup;
3720 hr = concat_mesh->lpVtbl->GetDeclaration(concat_mesh, concat_decl);
3721 if (FAILED(hr)) goto cleanup;
3723 concat_vertex_size = D3DXGetDeclVertexSize(concat_decl, 0);
3725 hr = concat_mesh->lpVtbl->LockVertexBuffer(concat_mesh, D3DLOCK_DISCARD, (void**)&concat_vertices);
3726 if (FAILED(hr)) goto cleanup;
3728 LIST_FOR_EACH_ENTRY(container_ptr, &container_list, struct mesh_container, entry)
3730 D3DVERTEXELEMENT9 mesh_decl[MAX_FVF_DECL_SIZE];
3731 ID3DXMesh *mesh = container_ptr->mesh;
3732 DWORD num_mesh_vertices = mesh->lpVtbl->GetNumVertices(mesh);
3733 DWORD mesh_vertex_size;
3734 const BYTE *mesh_vertices;
3736 hr = mesh->lpVtbl->GetDeclaration(mesh, mesh_decl);
3737 if (FAILED(hr)) goto cleanup;
3739 mesh_vertex_size = D3DXGetDeclVertexSize(mesh_decl, 0);
3741 hr = mesh->lpVtbl->LockVertexBuffer(mesh, D3DLOCK_READONLY, (void**)&mesh_vertices);
3742 if (FAILED(hr)) goto cleanup;
3744 for (i = 0; i < num_mesh_vertices; i++) {
3748 D3DXVec3TransformCoord((D3DXVECTOR3*)concat_vertices,
3749 (D3DXVECTOR3*)mesh_vertices,
3750 &container_ptr->transform);
3751 for (j = 1; concat_decl[j].Stream != 0xff; j++)
3753 if (concat_decl[j].Usage == mesh_decl[k].Usage &&
3754 concat_decl[j].UsageIndex == mesh_decl[k].UsageIndex)
3756 if (concat_decl[j].Usage == D3DDECLUSAGE_NORMAL) {
3757 D3DXVec3TransformCoord((D3DXVECTOR3*)(concat_vertices + concat_decl[j].Offset),
3758 (D3DXVECTOR3*)(mesh_vertices + mesh_decl[k].Offset),
3759 &container_ptr->transform);
3761 memcpy(concat_vertices + concat_decl[j].Offset,
3762 mesh_vertices + mesh_decl[k].Offset,
3763 d3dx_decltype_size[mesh_decl[k].Type]);
3768 mesh_vertices += mesh_vertex_size;
3769 concat_vertices += concat_vertex_size;
3772 mesh->lpVtbl->UnlockVertexBuffer(mesh);
3775 concat_mesh->lpVtbl->UnlockVertexBuffer(concat_mesh);
3776 concat_vertices = NULL;
3778 hr = concat_mesh->lpVtbl->LockIndexBuffer(concat_mesh, D3DLOCK_DISCARD, &concat_indices);
3779 if (FAILED(hr)) goto cleanup;
3782 LIST_FOR_EACH_ENTRY(container_ptr, &container_list, struct mesh_container, entry)
3784 ID3DXMesh *mesh = container_ptr->mesh;
3785 const void *mesh_indices;
3786 DWORD num_mesh_faces = mesh->lpVtbl->GetNumFaces(mesh);
3789 hr = mesh->lpVtbl->LockIndexBuffer(mesh, D3DLOCK_READONLY, (void**)&mesh_indices);
3790 if (FAILED(hr)) goto cleanup;
3792 if (options & D3DXMESH_32BIT) {
3793 DWORD *dest = concat_indices;
3794 const DWORD *src = mesh_indices;
3795 for (i = 0; i < num_mesh_faces * 3; i++)
3796 *dest++ = index_offset + *src++;
3797 concat_indices = dest;
3799 WORD *dest = concat_indices;
3800 const WORD *src = mesh_indices;
3801 for (i = 0; i < num_mesh_faces * 3; i++)
3802 *dest++ = index_offset + *src++;
3803 concat_indices = dest;
3805 mesh->lpVtbl->UnlockIndexBuffer(mesh);
3807 index_offset += num_mesh_faces * 3;
3810 concat_mesh->lpVtbl->UnlockIndexBuffer(concat_mesh);
3811 concat_indices = NULL;
3813 if (num_materials) {
3814 DWORD *concat_attrib_buffer = NULL;
3817 hr = concat_mesh->lpVtbl->LockAttributeBuffer(concat_mesh, D3DLOCK_DISCARD, &concat_attrib_buffer);
3818 if (FAILED(hr)) goto cleanup;
3820 LIST_FOR_EACH_ENTRY(container_ptr, &container_list, struct mesh_container, entry)
3822 ID3DXMesh *mesh = container_ptr->mesh;
3823 const DWORD *mesh_attrib_buffer = NULL;
3824 DWORD count = mesh->lpVtbl->GetNumFaces(mesh);
3826 hr = mesh->lpVtbl->LockAttributeBuffer(mesh, D3DLOCK_READONLY, (DWORD**)&mesh_attrib_buffer);
3828 concat_mesh->lpVtbl->UnlockAttributeBuffer(concat_mesh);
3833 *concat_attrib_buffer++ = offset + *mesh_attrib_buffer++;
3835 mesh->lpVtbl->UnlockAttributeBuffer(mesh);
3836 offset += container_ptr->num_materials;
3838 concat_mesh->lpVtbl->UnlockAttributeBuffer(concat_mesh);
3841 if (materials_out || effects_out) {
3842 D3DXMATERIAL *out_ptr;
3843 if (!num_materials) {
3844 /* create default material */
3845 hr = D3DXCreateBuffer(sizeof(D3DXMATERIAL), &materials);
3846 if (FAILED(hr)) goto cleanup;
3848 out_ptr = ID3DXBuffer_GetBufferPointer(materials);
3849 out_ptr->MatD3D.Diffuse.r = 0.5f;
3850 out_ptr->MatD3D.Diffuse.g = 0.5f;
3851 out_ptr->MatD3D.Diffuse.b = 0.5f;
3852 out_ptr->MatD3D.Specular.r = 0.5f;
3853 out_ptr->MatD3D.Specular.g = 0.5f;
3854 out_ptr->MatD3D.Specular.b = 0.5f;
3855 /* D3DXCreateBuffer initializes the rest to zero */
3857 DWORD buffer_size = num_materials * sizeof(D3DXMATERIAL);
3858 char *strings_out_ptr;
3860 LIST_FOR_EACH_ENTRY(container_ptr, &container_list, struct mesh_container, entry)
3862 if (container_ptr->materials) {
3863 const D3DXMATERIAL *in_ptr = ID3DXBuffer_GetBufferPointer(container_ptr->materials);
3864 for (i = 0; i < container_ptr->num_materials; i++)
3866 if (in_ptr->pTextureFilename)
3867 buffer_size += strlen(in_ptr->pTextureFilename) + 1;
3873 hr = D3DXCreateBuffer(buffer_size, &materials);
3874 if (FAILED(hr)) goto cleanup;
3875 out_ptr = ID3DXBuffer_GetBufferPointer(materials);
3876 strings_out_ptr = (char*)(out_ptr + num_materials);
3878 LIST_FOR_EACH_ENTRY(container_ptr, &container_list, struct mesh_container, entry)
3880 if (container_ptr->materials) {
3881 const D3DXMATERIAL *in_ptr = ID3DXBuffer_GetBufferPointer(container_ptr->materials);
3882 for (i = 0; i < container_ptr->num_materials; i++)
3884 out_ptr->MatD3D = in_ptr->MatD3D;
3885 if (in_ptr->pTextureFilename) {
3886 out_ptr->pTextureFilename = strings_out_ptr;
3887 strcpy(out_ptr->pTextureFilename, in_ptr->pTextureFilename);
3888 strings_out_ptr += strlen(in_ptr->pTextureFilename) + 1;
3901 generate_effects(materials, num_materials, &effects);
3902 if (!materials_out) {
3903 ID3DXBuffer_Release(materials);
3908 if (adjacency_out) {
3909 if (!list_next(&container_list, list_head(&container_list))) {
3910 container_ptr = LIST_ENTRY(list_head(&container_list), struct mesh_container, entry);
3911 adjacency = container_ptr->adjacency;
3912 container_ptr->adjacency = NULL;
3917 hr = D3DXCreateBuffer(num_faces * 3 * sizeof(DWORD), &adjacency);
3918 if (FAILED(hr)) goto cleanup;
3920 out_ptr = ID3DXBuffer_GetBufferPointer(adjacency);
3921 LIST_FOR_EACH_ENTRY(container_ptr, &container_list, struct mesh_container, entry)
3923 DWORD *in_ptr = ID3DXBuffer_GetBufferPointer(container_ptr->adjacency);
3924 int count = 3 * container_ptr->mesh->lpVtbl->GetNumFaces(container_ptr->mesh);
3926 for (i = 0; i < count; i++)
3927 *out_ptr++ = offset + *in_ptr++;
3934 *mesh_out = concat_mesh;
3935 if (adjacency_out) *adjacency_out = adjacency;
3936 if (materials_out) *materials_out = materials;
3937 if (effects_out) *effects_out = effects;
3938 if (num_materials_out) *num_materials_out = num_materials;
3942 if (concat_indices) concat_mesh->lpVtbl->UnlockIndexBuffer(concat_mesh);
3943 if (concat_vertices) concat_mesh->lpVtbl->UnlockVertexBuffer(concat_mesh);
3944 if (filedata) IDirectXFileData_Release(filedata);
3945 if (enumobj) IDirectXFileEnumObject_Release(enumobj);
3946 if (dxfile) IDirectXFile_Release(dxfile);
3948 if (concat_mesh) IUnknown_Release(concat_mesh);
3949 if (materials) ID3DXBuffer_Release(materials);
3950 if (effects) ID3DXBuffer_Release(effects);
3951 if (adjacency) ID3DXBuffer_Release(adjacency);
3953 LIST_FOR_EACH_ENTRY_SAFE(container_ptr, next_container_ptr, &container_list, struct mesh_container, entry)
3955 if (container_ptr->mesh) IUnknown_Release(container_ptr->mesh);
3956 if (container_ptr->adjacency) ID3DXBuffer_Release(container_ptr->adjacency);
3957 if (container_ptr->materials) ID3DXBuffer_Release(container_ptr->materials);
3958 if (container_ptr->effects) ID3DXBuffer_Release(container_ptr->effects);
3959 HeapFree(GetProcessHeap(), 0, container_ptr);
3964 HRESULT WINAPI D3DXCreateBox(LPDIRECT3DDEVICE9 device, FLOAT width, FLOAT height,
3965 FLOAT depth, LPD3DXMESH* mesh, LPD3DXBUFFER* adjacency)
3967 FIXME("(%p, %f, %f, %f, %p, %p): stub\n", device, width, height, depth, mesh, adjacency);
3974 D3DXVECTOR3 position;
3978 typedef WORD face[3];
3986 static void free_sincos_table(struct sincos_table *sincos_table)
3988 HeapFree(GetProcessHeap(), 0, sincos_table->cos);
3989 HeapFree(GetProcessHeap(), 0, sincos_table->sin);
3992 /* pre compute sine and cosine tables; caller must free */
3993 static BOOL compute_sincos_table(struct sincos_table *sincos_table, float angle_start, float angle_step, int n)
3998 sincos_table->sin = HeapAlloc(GetProcessHeap(), 0, n * sizeof(*sincos_table->sin));
3999 if (!sincos_table->sin)
4003 sincos_table->cos = HeapAlloc(GetProcessHeap(), 0, n * sizeof(*sincos_table->cos));
4004 if (!sincos_table->cos)
4006 HeapFree(GetProcessHeap(), 0, sincos_table->sin);
4010 angle = angle_start;
4011 for (i = 0; i < n; i++)
4013 sincos_table->sin[i] = sin(angle);
4014 sincos_table->cos[i] = cos(angle);
4015 angle += angle_step;
4021 static WORD vertex_index(UINT slices, int slice, int stack)
4023 return stack*slices+slice+1;
4026 HRESULT WINAPI D3DXCreateSphere(LPDIRECT3DDEVICE9 device, FLOAT radius, UINT slices,
4027 UINT stacks, LPD3DXMESH* mesh, LPD3DXBUFFER* adjacency)
4029 DWORD number_of_vertices, number_of_faces;
4032 struct vertex *vertices;
4034 float phi_step, phi_start;
4035 struct sincos_table phi;
4036 float theta_step, theta, sin_theta, cos_theta;
4040 TRACE("(%p, %f, %u, %u, %p, %p)\n", device, radius, slices, stacks, mesh, adjacency);
4042 if (!device || radius < 0.0f || slices < 2 || stacks < 2 || !mesh)
4044 return D3DERR_INVALIDCALL;
4049 FIXME("Case of adjacency != NULL not implemented.\n");
4053 number_of_vertices = 2 + slices * (stacks-1);
4054 number_of_faces = 2 * slices + (stacks - 2) * (2 * slices);
4056 hr = D3DXCreateMeshFVF(number_of_faces, number_of_vertices, D3DXMESH_MANAGED,
4057 D3DFVF_XYZ | D3DFVF_NORMAL, device, &sphere);
4063 hr = sphere->lpVtbl->LockVertexBuffer(sphere, D3DLOCK_DISCARD, (LPVOID *)&vertices);
4066 sphere->lpVtbl->Release(sphere);
4070 hr = sphere->lpVtbl->LockIndexBuffer(sphere, D3DLOCK_DISCARD, (LPVOID *)&faces);
4073 sphere->lpVtbl->UnlockVertexBuffer(sphere);
4074 sphere->lpVtbl->Release(sphere);
4078 /* phi = angle on xz plane wrt z axis */
4079 phi_step = -2 * M_PI / slices;
4080 phi_start = M_PI / 2;
4082 if (!compute_sincos_table(&phi, phi_start, phi_step, slices))
4084 sphere->lpVtbl->UnlockIndexBuffer(sphere);
4085 sphere->lpVtbl->UnlockVertexBuffer(sphere);
4086 sphere->lpVtbl->Release(sphere);
4087 return E_OUTOFMEMORY;
4090 /* theta = angle on xy plane wrt x axis */
4091 theta_step = M_PI / stacks;
4097 vertices[vertex].normal.x = 0.0f;
4098 vertices[vertex].normal.y = 0.0f;
4099 vertices[vertex].normal.z = 1.0f;
4100 vertices[vertex].position.x = 0.0f;
4101 vertices[vertex].position.y = 0.0f;
4102 vertices[vertex].position.z = radius;
4105 for (stack = 0; stack < stacks - 1; stack++)
4107 sin_theta = sin(theta);
4108 cos_theta = cos(theta);
4110 for (slice = 0; slice < slices; slice++)
4112 vertices[vertex].normal.x = sin_theta * phi.cos[slice];
4113 vertices[vertex].normal.y = sin_theta * phi.sin[slice];
4114 vertices[vertex].normal.z = cos_theta;
4115 vertices[vertex].position.x = radius * sin_theta * phi.cos[slice];
4116 vertices[vertex].position.y = radius * sin_theta * phi.sin[slice];
4117 vertices[vertex].position.z = radius * cos_theta;
4124 /* top stack is triangle fan */
4126 faces[face][1] = slice + 1;
4127 faces[face][2] = slice;
4132 /* stacks in between top and bottom are quad strips */
4133 faces[face][0] = vertex_index(slices, slice-1, stack-1);
4134 faces[face][1] = vertex_index(slices, slice, stack-1);
4135 faces[face][2] = vertex_index(slices, slice-1, stack);
4138 faces[face][0] = vertex_index(slices, slice, stack-1);
4139 faces[face][1] = vertex_index(slices, slice, stack);
4140 faces[face][2] = vertex_index(slices, slice-1, stack);
4146 theta += theta_step;
4152 faces[face][2] = slice;
4157 faces[face][0] = vertex_index(slices, slice-1, stack-1);
4158 faces[face][1] = vertex_index(slices, 0, stack-1);
4159 faces[face][2] = vertex_index(slices, slice-1, stack);
4162 faces[face][0] = vertex_index(slices, 0, stack-1);
4163 faces[face][1] = vertex_index(slices, 0, stack);
4164 faces[face][2] = vertex_index(slices, slice-1, stack);
4169 vertices[vertex].position.x = 0.0f;
4170 vertices[vertex].position.y = 0.0f;
4171 vertices[vertex].position.z = -radius;
4172 vertices[vertex].normal.x = 0.0f;
4173 vertices[vertex].normal.y = 0.0f;
4174 vertices[vertex].normal.z = -1.0f;
4176 /* bottom stack is triangle fan */
4177 for (slice = 1; slice < slices; slice++)
4179 faces[face][0] = vertex_index(slices, slice-1, stack-1);
4180 faces[face][1] = vertex_index(slices, slice, stack-1);
4181 faces[face][2] = vertex;
4185 faces[face][0] = vertex_index(slices, slice-1, stack-1);
4186 faces[face][1] = vertex_index(slices, 0, stack-1);
4187 faces[face][2] = vertex;
4189 free_sincos_table(&phi);
4190 sphere->lpVtbl->UnlockIndexBuffer(sphere);
4191 sphere->lpVtbl->UnlockVertexBuffer(sphere);
4197 HRESULT WINAPI D3DXCreateCylinder(LPDIRECT3DDEVICE9 device, FLOAT radius1, FLOAT radius2, FLOAT length, UINT slices,
4198 UINT stacks, LPD3DXMESH* mesh, LPD3DXBUFFER* adjacency)
4200 DWORD number_of_vertices, number_of_faces;
4202 ID3DXMesh *cylinder;
4203 struct vertex *vertices;
4205 float theta_step, theta_start;
4206 struct sincos_table theta;
4207 float delta_radius, radius, radius_step;
4208 float z, z_step, z_normal;
4212 TRACE("(%p, %f, %f, %f, %u, %u, %p, %p)\n", device, radius1, radius2, length, slices, stacks, mesh, adjacency);
4214 if (device == NULL || radius1 < 0.0f || radius2 < 0.0f || length < 0.0f || slices < 2 || stacks < 1 || mesh == NULL)
4216 return D3DERR_INVALIDCALL;
4221 FIXME("Case of adjacency != NULL not implemented.\n");
4225 number_of_vertices = 2 + (slices * (3 + stacks));
4226 number_of_faces = 2 * slices + stacks * (2 * slices);
4228 hr = D3DXCreateMeshFVF(number_of_faces, number_of_vertices, D3DXMESH_MANAGED,
4229 D3DFVF_XYZ | D3DFVF_NORMAL, device, &cylinder);
4235 hr = cylinder->lpVtbl->LockVertexBuffer(cylinder, D3DLOCK_DISCARD, (LPVOID *)&vertices);
4238 cylinder->lpVtbl->Release(cylinder);
4242 hr = cylinder->lpVtbl->LockIndexBuffer(cylinder, D3DLOCK_DISCARD, (LPVOID *)&faces);
4245 cylinder->lpVtbl->UnlockVertexBuffer(cylinder);
4246 cylinder->lpVtbl->Release(cylinder);
4250 /* theta = angle on xy plane wrt x axis */
4251 theta_step = -2 * M_PI / slices;
4252 theta_start = M_PI / 2;
4254 if (!compute_sincos_table(&theta, theta_start, theta_step, slices))
4256 cylinder->lpVtbl->UnlockIndexBuffer(cylinder);
4257 cylinder->lpVtbl->UnlockVertexBuffer(cylinder);
4258 cylinder->lpVtbl->Release(cylinder);
4259 return E_OUTOFMEMORY;
4265 delta_radius = radius1 - radius2;
4267 radius_step = delta_radius / stacks;
4270 z_step = length / stacks;
4271 z_normal = delta_radius / length;
4272 if (isnan(z_normal))
4277 vertices[vertex].normal.x = 0.0f;
4278 vertices[vertex].normal.y = 0.0f;
4279 vertices[vertex].normal.z = -1.0f;
4280 vertices[vertex].position.x = 0.0f;
4281 vertices[vertex].position.y = 0.0f;
4282 vertices[vertex++].position.z = z;
4284 for (slice = 0; slice < slices; slice++, vertex++)
4286 vertices[vertex].normal.x = 0.0f;
4287 vertices[vertex].normal.y = 0.0f;
4288 vertices[vertex].normal.z = -1.0f;
4289 vertices[vertex].position.x = radius * theta.cos[slice];
4290 vertices[vertex].position.y = radius * theta.sin[slice];
4291 vertices[vertex].position.z = z;
4296 faces[face][1] = slice;
4297 faces[face++][2] = slice + 1;
4302 faces[face][1] = slice;
4303 faces[face++][2] = 1;
4305 for (stack = 1; stack <= stacks+1; stack++)
4307 for (slice = 0; slice < slices; slice++, vertex++)
4309 vertices[vertex].normal.x = theta.cos[slice];
4310 vertices[vertex].normal.y = theta.sin[slice];
4311 vertices[vertex].normal.z = z_normal;
4312 D3DXVec3Normalize(&vertices[vertex].normal, &vertices[vertex].normal);
4313 vertices[vertex].position.x = radius * theta.cos[slice];
4314 vertices[vertex].position.y = radius * theta.sin[slice];
4315 vertices[vertex].position.z = z;
4317 if (stack > 1 && slice > 0)
4319 faces[face][0] = vertex_index(slices, slice-1, stack-1);
4320 faces[face][1] = vertex_index(slices, slice-1, stack);
4321 faces[face++][2] = vertex_index(slices, slice, stack-1);
4323 faces[face][0] = vertex_index(slices, slice, stack-1);
4324 faces[face][1] = vertex_index(slices, slice-1, stack);
4325 faces[face++][2] = vertex_index(slices, slice, stack);
4331 faces[face][0] = vertex_index(slices, slice-1, stack-1);
4332 faces[face][1] = vertex_index(slices, slice-1, stack);
4333 faces[face++][2] = vertex_index(slices, 0, stack-1);
4335 faces[face][0] = vertex_index(slices, 0, stack-1);
4336 faces[face][1] = vertex_index(slices, slice-1, stack);
4337 faces[face++][2] = vertex_index(slices, 0, stack);
4340 if (stack < stacks + 1)
4343 radius -= radius_step;
4347 for (slice = 0; slice < slices; slice++, vertex++)
4349 vertices[vertex].normal.x = 0.0f;
4350 vertices[vertex].normal.y = 0.0f;
4351 vertices[vertex].normal.z = 1.0f;
4352 vertices[vertex].position.x = radius * theta.cos[slice];
4353 vertices[vertex].position.y = radius * theta.sin[slice];
4354 vertices[vertex].position.z = z;
4358 faces[face][0] = vertex_index(slices, slice-1, stack);
4359 faces[face][1] = number_of_vertices - 1;
4360 faces[face++][2] = vertex_index(slices, slice, stack);
4364 vertices[vertex].position.x = 0.0f;
4365 vertices[vertex].position.y = 0.0f;
4366 vertices[vertex].position.z = z;
4367 vertices[vertex].normal.x = 0.0f;
4368 vertices[vertex].normal.y = 0.0f;
4369 vertices[vertex].normal.z = 1.0f;
4371 faces[face][0] = vertex_index(slices, slice-1, stack);
4372 faces[face][1] = number_of_vertices - 1;
4373 faces[face][2] = vertex_index(slices, 0, stack);
4375 free_sincos_table(&theta);
4376 cylinder->lpVtbl->UnlockIndexBuffer(cylinder);
4377 cylinder->lpVtbl->UnlockVertexBuffer(cylinder);
4383 HRESULT WINAPI D3DXCreateTeapot(LPDIRECT3DDEVICE9 device, LPD3DXMESH *mesh, LPD3DXBUFFER* adjacency)
4385 FIXME("(%p, %p, %p): stub\n", device, mesh, adjacency);
4390 HRESULT WINAPI D3DXCreateTextA(LPDIRECT3DDEVICE9 device,
4391 HDC hdc, LPCSTR text,
4392 FLOAT deviation, FLOAT extrusion,
4393 LPD3DXMESH *mesh, LPD3DXBUFFER *adjacency,
4394 LPGLYPHMETRICSFLOAT glyphmetrics)
4400 TRACE("(%p, %p, %s, %f, %f, %p, %p, %p)\n", device, hdc,
4401 debugstr_a(text), deviation, extrusion, mesh, adjacency, glyphmetrics);
4404 return D3DERR_INVALIDCALL;
4406 len = MultiByteToWideChar(CP_ACP, 0, text, -1, NULL, 0);
4407 textW = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
4408 MultiByteToWideChar(CP_ACP, 0, text, -1, textW, len);
4410 hr = D3DXCreateTextW(device, hdc, textW, deviation, extrusion,
4411 mesh, adjacency, glyphmetrics);
4412 HeapFree(GetProcessHeap(), 0, textW);
4418 POINTTYPE_CURVE = 0,
4420 POINTTYPE_CURVE_START,
4421 POINTTYPE_CURVE_END,
4422 POINTTYPE_CURVE_MIDDLE,
4428 enum pointtype corner;
4431 struct dynamic_array
4433 int count, capacity;
4437 /* is a dynamic_array */
4440 int count, capacity;
4441 struct point2d *items;
4444 /* is a dynamic_array */
4445 struct outline_array
4447 int count, capacity;
4448 struct outline *items;
4457 struct point2d_index
4459 struct outline *outline;
4463 struct point2d_index_array
4466 struct point2d_index *items;
4471 struct outline_array outlines;
4472 struct face_array faces;
4473 struct point2d_index_array ordered_vertices;
4477 /* is an dynamic_array */
4480 int count, capacity;
4484 /* complex polygons are split into monotone polygons, which have
4485 * at most 2 intersections with the vertical sweep line */
4486 struct triangulation
4488 struct word_array vertex_stack;
4489 BOOL last_on_top, merging;
4492 /* is an dynamic_array */
4493 struct triangulation_array
4495 int count, capacity;
4496 struct triangulation *items;
4498 struct glyphinfo *glyph;
4501 static BOOL reserve(struct dynamic_array *array, int count, int itemsize)
4503 if (count > array->capacity) {
4506 if (array->items && array->capacity) {
4507 new_capacity = max(array->capacity * 2, count);
4508 new_buffer = HeapReAlloc(GetProcessHeap(), 0, array->items, new_capacity * itemsize);
4510 new_capacity = max(16, count);
4511 new_buffer = HeapAlloc(GetProcessHeap(), 0, new_capacity * itemsize);
4515 array->items = new_buffer;
4516 array->capacity = new_capacity;
4521 static struct point2d *add_points(struct outline *array, int num)
4523 struct point2d *item;
4525 if (!reserve((struct dynamic_array *)array, array->count + num, sizeof(array->items[0])))
4528 item = &array->items[array->count];
4529 array->count += num;
4533 static struct outline *add_outline(struct outline_array *array)
4535 struct outline *item;
4537 if (!reserve((struct dynamic_array *)array, array->count + 1, sizeof(array->items[0])))
4540 item = &array->items[array->count++];
4541 ZeroMemory(item, sizeof(*item));
4545 static inline face *add_face(struct face_array *array)
4547 return &array->items[array->count++];
4550 static struct triangulation *add_triangulation(struct triangulation_array *array)
4552 struct triangulation *item;
4554 if (!reserve((struct dynamic_array *)array, array->count + 1, sizeof(array->items[0])))
4557 item = &array->items[array->count++];
4558 ZeroMemory(item, sizeof(*item));
4562 static HRESULT add_vertex_index(struct word_array *array, WORD vertex_index)
4564 if (!reserve((struct dynamic_array *)array, array->count + 1, sizeof(array->items[0])))
4565 return E_OUTOFMEMORY;
4567 array->items[array->count++] = vertex_index;
4571 /* assume fixed point numbers can be converted to float point in place */
4572 C_ASSERT(sizeof(FIXED) == sizeof(float));
4573 C_ASSERT(sizeof(POINTFX) == sizeof(D3DXVECTOR2));
4575 static inline D3DXVECTOR2 *convert_fixed_to_float(POINTFX *pt, int count, float emsquare)
4577 D3DXVECTOR2 *ret = (D3DXVECTOR2*)pt;
4579 D3DXVECTOR2 *pt_flt = (D3DXVECTOR2*)pt;
4580 pt_flt->x = (pt->x.value + pt->x.fract / (float)0x10000) / emsquare;
4581 pt_flt->y = (pt->y.value + pt->y.fract / (float)0x10000) / emsquare;
4587 static HRESULT add_bezier_points(struct outline *outline, const D3DXVECTOR2 *p1,
4588 const D3DXVECTOR2 *p2, const D3DXVECTOR2 *p3,
4589 float max_deviation_sq)
4591 D3DXVECTOR2 split1 = {0, 0}, split2 = {0, 0}, middle, vec;
4594 D3DXVec2Scale(&split1, D3DXVec2Add(&split1, p1, p2), 0.5f);
4595 D3DXVec2Scale(&split2, D3DXVec2Add(&split2, p2, p3), 0.5f);
4596 D3DXVec2Scale(&middle, D3DXVec2Add(&middle, &split1, &split2), 0.5f);
4598 deviation_sq = D3DXVec2LengthSq(D3DXVec2Subtract(&vec, &middle, p2));
4599 if (deviation_sq < max_deviation_sq) {
4600 struct point2d *pt = add_points(outline, 1);
4601 if (!pt) return E_OUTOFMEMORY;
4603 pt->corner = POINTTYPE_CURVE;
4604 /* the end point is omitted because the end line merges into the next segment of
4605 * the split bezier curve, and the end of the split bezier curve is added outside
4606 * this recursive function. */
4608 HRESULT hr = add_bezier_points(outline, p1, &split1, &middle, max_deviation_sq);
4609 if (hr != S_OK) return hr;
4610 hr = add_bezier_points(outline, &middle, &split2, p3, max_deviation_sq);
4611 if (hr != S_OK) return hr;
4617 static inline BOOL is_direction_similar(D3DXVECTOR2 *dir1, D3DXVECTOR2 *dir2, float cos_theta)
4619 /* dot product = cos(theta) */
4620 return D3DXVec2Dot(dir1, dir2) > cos_theta;
4623 static inline D3DXVECTOR2 *unit_vec2(D3DXVECTOR2 *dir, const D3DXVECTOR2 *pt1, const D3DXVECTOR2 *pt2)
4625 return D3DXVec2Normalize(D3DXVec2Subtract(dir, pt2, pt1), dir);
4635 static BOOL attempt_line_merge(struct outline *outline,
4637 const D3DXVECTOR2 *nextpt,
4639 const struct cos_table *table)
4641 D3DXVECTOR2 curdir, lastdir;
4642 struct point2d *prevpt, *pt;
4645 pt = &outline->items[pt_index];
4646 pt_index = (pt_index - 1 + outline->count) % outline->count;
4647 prevpt = &outline->items[pt_index];
4650 pt->corner = pt->corner != POINTTYPE_CORNER ? POINTTYPE_CURVE_MIDDLE : POINTTYPE_CURVE_START;
4652 if (outline->count < 2)
4655 /* remove last point if the next line continues the last line */
4656 unit_vec2(&lastdir, &prevpt->pos, &pt->pos);
4657 unit_vec2(&curdir, &pt->pos, nextpt);
4658 if (is_direction_similar(&lastdir, &curdir, table->cos_half))
4661 if (pt->corner == POINTTYPE_CURVE_END)
4662 prevpt->corner = pt->corner;
4663 if (prevpt->corner == POINTTYPE_CURVE_END && to_curve)
4664 prevpt->corner = POINTTYPE_CURVE_MIDDLE;
4668 if (outline->count < 2)
4671 pt_index = (pt_index - 1 + outline->count) % outline->count;
4672 prevpt = &outline->items[pt_index];
4673 unit_vec2(&lastdir, &prevpt->pos, &pt->pos);
4674 unit_vec2(&curdir, &pt->pos, nextpt);
4679 static HRESULT create_outline(struct glyphinfo *glyph, void *raw_outline, int datasize,
4680 float max_deviation_sq, float emsquare, const struct cos_table *cos_table)
4682 TTPOLYGONHEADER *header = (TTPOLYGONHEADER *)raw_outline;
4684 while ((char *)header < (char *)raw_outline + datasize)
4686 TTPOLYCURVE *curve = (TTPOLYCURVE *)(header + 1);
4687 struct point2d *lastpt, *pt;
4688 D3DXVECTOR2 lastdir;
4689 D3DXVECTOR2 *pt_flt;
4691 struct outline *outline = add_outline(&glyph->outlines);
4694 return E_OUTOFMEMORY;
4696 pt = add_points(outline, 1);
4698 return E_OUTOFMEMORY;
4699 pt_flt = convert_fixed_to_float(&header->pfxStart, 1, emsquare);
4701 pt->corner = POINTTYPE_CORNER;
4703 if (header->dwType != TT_POLYGON_TYPE)
4704 FIXME("Unknown header type %d\n", header->dwType);
4706 while ((char *)curve < (char *)header + header->cb)
4708 D3DXVECTOR2 bezier_start = outline->items[outline->count - 1].pos;
4709 BOOL to_curve = curve->wType != TT_PRIM_LINE && curve->cpfx > 1;
4712 curve = (TTPOLYCURVE *)&curve->apfx[curve->cpfx];
4716 pt_flt = convert_fixed_to_float(curve->apfx, curve->cpfx, emsquare);
4718 attempt_line_merge(outline, outline->count - 1, &pt_flt[0], to_curve, cos_table);
4723 int count = curve->cpfx;
4728 D3DXVECTOR2 bezier_end;
4730 D3DXVec2Scale(&bezier_end, D3DXVec2Add(&bezier_end, &pt_flt[j], &pt_flt[j+1]), 0.5f);
4731 hr = add_bezier_points(outline, &bezier_start, &pt_flt[j], &bezier_end, max_deviation_sq);
4734 bezier_start = bezier_end;
4738 hr = add_bezier_points(outline, &bezier_start, &pt_flt[j], &pt_flt[j+1], max_deviation_sq);
4742 pt = add_points(outline, 1);
4744 return E_OUTOFMEMORY;
4746 pt->pos = pt_flt[j];
4747 pt->corner = POINTTYPE_CURVE_END;
4749 pt = add_points(outline, curve->cpfx);
4751 return E_OUTOFMEMORY;
4752 for (j = 0; j < curve->cpfx; j++)
4754 pt->pos = pt_flt[j];
4755 pt->corner = POINTTYPE_CORNER;
4760 curve = (TTPOLYCURVE *)&curve->apfx[curve->cpfx];
4763 /* remove last point if the next line continues the last line */
4764 if (outline->count >= 3) {
4767 lastpt = &outline->items[outline->count - 1];
4768 pt = &outline->items[0];
4769 if (pt->pos.x == lastpt->pos.x && pt->pos.y == lastpt->pos.y) {
4770 if (lastpt->corner == POINTTYPE_CURVE_END)
4772 if (pt->corner == POINTTYPE_CURVE_START)
4773 pt->corner = POINTTYPE_CURVE_MIDDLE;
4775 pt->corner = POINTTYPE_CURVE_END;
4778 lastpt = &outline->items[outline->count - 1];
4780 /* outline closed with a line from end to start point */
4781 attempt_line_merge(outline, outline->count - 1, &pt->pos, FALSE, cos_table);
4783 lastpt = &outline->items[0];
4784 to_curve = lastpt->corner != POINTTYPE_CORNER && lastpt->corner != POINTTYPE_CURVE_END;
4785 if (lastpt->corner == POINTTYPE_CURVE_START)
4786 lastpt->corner = POINTTYPE_CORNER;
4787 pt = &outline->items[1];
4788 if (attempt_line_merge(outline, 0, &pt->pos, to_curve, cos_table))
4789 *lastpt = outline->items[outline->count];
4792 lastpt = &outline->items[outline->count - 1];
4793 pt = &outline->items[0];
4794 unit_vec2(&lastdir, &lastpt->pos, &pt->pos);
4795 for (j = 0; j < outline->count; j++)
4800 pt = &outline->items[(j + 1) % outline->count];
4801 unit_vec2(&curdir, &lastpt->pos, &pt->pos);
4803 switch (lastpt->corner)
4805 case POINTTYPE_CURVE_START:
4806 case POINTTYPE_CURVE_END:
4807 if (!is_direction_similar(&lastdir, &curdir, cos_table->cos_45))
4808 lastpt->corner = POINTTYPE_CORNER;
4810 case POINTTYPE_CURVE_MIDDLE:
4811 if (!is_direction_similar(&lastdir, &curdir, cos_table->cos_90))
4812 lastpt->corner = POINTTYPE_CORNER;
4814 lastpt->corner = POINTTYPE_CURVE;
4822 header = (TTPOLYGONHEADER *)((char *)header + header->cb);
4827 /* Get the y-distance from a line to a point */
4828 static float get_line_to_point_y_distance(D3DXVECTOR2 *line_pt1,
4829 D3DXVECTOR2 *line_pt2,
4832 D3DXVECTOR2 line_vec = {0, 0};
4836 D3DXVec2Subtract(&line_vec, line_pt2, line_pt1);
4837 line_pt_dx = point->x - line_pt1->x;
4838 line_y = line_pt1->y + (line_vec.y * line_pt_dx) / line_vec.x;
4839 return point->y - line_y;
4842 static D3DXVECTOR2 *get_indexed_point(struct point2d_index *pt_idx)
4844 return &pt_idx->outline->items[pt_idx->vertex].pos;
4847 static D3DXVECTOR2 *get_ordered_vertex(struct glyphinfo *glyph, WORD index)
4849 return get_indexed_point(&glyph->ordered_vertices.items[index]);
4852 static void remove_triangulation(struct triangulation_array *array, struct triangulation *item)
4854 HeapFree(GetProcessHeap(), 0, item->vertex_stack.items);
4855 MoveMemory(item, item + 1, (char*)&array->items[array->count] - (char*)(item + 1));
4859 static HRESULT triangulation_add_point(struct triangulation **t_ptr,
4860 struct triangulation_array *triangulations,
4864 struct glyphinfo *glyph = triangulations->glyph;
4865 struct triangulation *t = *t_ptr;
4870 if (t->last_on_top) {
4878 if (t->last_on_top != to_top && t->vertex_stack.count > 1) {
4879 /* consume all vertices on the stack */
4880 WORD last_pt = t->vertex_stack.items[0];
4882 for (i = 1; i < t->vertex_stack.count; i++)
4884 face = add_face(&glyph->faces);
4885 if (!face) return E_OUTOFMEMORY;
4886 (*face)[0] = vtx_idx;
4887 (*face)[f1] = last_pt;
4888 (*face)[f2] = last_pt = t->vertex_stack.items[i];
4890 t->vertex_stack.items[0] = last_pt;
4891 t->vertex_stack.count = 1;
4892 } else if (t->vertex_stack.count > 1) {
4893 int i = t->vertex_stack.count - 1;
4894 D3DXVECTOR2 *point = get_ordered_vertex(glyph, vtx_idx);
4895 WORD top_idx = t->vertex_stack.items[i--];
4896 D3DXVECTOR2 *top_pt = get_ordered_vertex(glyph, top_idx);
4900 WORD prev_idx = t->vertex_stack.items[i--];
4901 D3DXVECTOR2 *prev_pt = get_ordered_vertex(glyph, prev_idx);
4903 if (prev_pt->x != top_pt->x &&
4904 ((to_top && get_line_to_point_y_distance(prev_pt, top_pt, point) > 0) ||
4905 (!to_top && get_line_to_point_y_distance(prev_pt, top_pt, point) < 0)))
4908 face = add_face(&glyph->faces);
4909 if (!face) return E_OUTOFMEMORY;
4910 (*face)[0] = vtx_idx;
4911 (*face)[f1] = prev_idx;
4912 (*face)[f2] = top_idx;
4916 t->vertex_stack.count--;
4919 t->last_on_top = to_top;
4921 hr = add_vertex_index(&t->vertex_stack, vtx_idx);
4923 if (hr == S_OK && t->merging) {
4924 struct triangulation *t2;
4926 t2 = to_top ? t - 1 : t + 1;
4927 t2->merging = FALSE;
4928 hr = triangulation_add_point(&t2, triangulations, vtx_idx, to_top);
4929 if (hr != S_OK) return hr;
4930 remove_triangulation(triangulations, t);
4938 /* check if the point is next on the outline for either the top or bottom */
4939 static D3DXVECTOR2 *triangulation_get_next_point(struct triangulation *t, struct glyphinfo *glyph, BOOL on_top)
4941 int i = t->last_on_top == on_top ? t->vertex_stack.count - 1 : 0;
4942 WORD idx = t->vertex_stack.items[i];
4943 struct point2d_index *pt_idx = &glyph->ordered_vertices.items[idx];
4944 struct outline *outline = pt_idx->outline;
4947 i = (pt_idx->vertex + outline->count - 1) % outline->count;
4949 i = (pt_idx->vertex + 1) % outline->count;
4951 return &outline->items[i].pos;
4954 static int compare_vertex_indices(const void *a, const void *b)
4956 const struct point2d_index *idx1 = a, *idx2 = b;
4957 const D3DXVECTOR2 *p1 = &idx1->outline->items[idx1->vertex].pos;
4958 const D3DXVECTOR2 *p2 = &idx2->outline->items[idx2->vertex].pos;
4959 float diff = p1->x - p2->x;
4962 diff = p1->y - p2->y;
4964 return diff == 0.0f ? 0 : (diff > 0.0f ? -1 : 1);
4967 static HRESULT triangulate(struct triangulation_array *triangulations)
4971 struct glyphinfo *glyph = triangulations->glyph;
4972 int nb_vertices = 0;
4974 struct point2d_index *idx_ptr;
4976 for (i = 0; i < glyph->outlines.count; i++)
4977 nb_vertices += glyph->outlines.items[i].count;
4979 glyph->ordered_vertices.items = HeapAlloc(GetProcessHeap(), 0,
4980 nb_vertices * sizeof(*glyph->ordered_vertices.items));
4981 if (!glyph->ordered_vertices.items)
4982 return E_OUTOFMEMORY;
4984 idx_ptr = glyph->ordered_vertices.items;
4985 for (i = 0; i < glyph->outlines.count; i++)
4987 struct outline *outline = &glyph->outlines.items[i];
4990 idx_ptr->outline = outline;
4991 idx_ptr->vertex = 0;
4993 for (j = outline->count - 1; j > 0; j--)
4995 idx_ptr->outline = outline;
4996 idx_ptr->vertex = j;
5000 glyph->ordered_vertices.count = nb_vertices;
5002 /* Native implementation seems to try to create a triangle fan from
5003 * the first outline point if the glyph only has one outline. */
5004 if (glyph->outlines.count == 1)
5006 struct outline *outline = glyph->outlines.items;
5007 D3DXVECTOR2 *base = &outline->items[0].pos;
5008 D3DXVECTOR2 *last = &outline->items[1].pos;
5011 for (i = 2; i < outline->count; i++)
5013 D3DXVECTOR2 *next = &outline->items[i].pos;
5014 D3DXVECTOR2 v1 = {0.0f, 0.0f};
5015 D3DXVECTOR2 v2 = {0.0f, 0.0f};
5017 D3DXVec2Subtract(&v1, base, last);
5018 D3DXVec2Subtract(&v2, last, next);
5019 ccw = D3DXVec2CCW(&v1, &v2);
5027 glyph->faces.items = HeapAlloc(GetProcessHeap(), 0,
5028 (outline->count - 2) * sizeof(glyph->faces.items[0]));
5029 if (!glyph->faces.items)
5030 return E_OUTOFMEMORY;
5032 glyph->faces.count = outline->count - 2;
5033 for (i = 0; i < glyph->faces.count; i++)
5035 glyph->faces.items[i][0] = 0;
5036 glyph->faces.items[i][1] = i + 1;
5037 glyph->faces.items[i][2] = i + 2;
5043 /* Perform 2D polygon triangulation for complex glyphs.
5044 * Triangulation is performed using a sweep line concept, from right to left,
5045 * by processing vertices in sorted order. Complex polygons are split into
5046 * monotone polygons which are triangulated separately. */
5047 /* FIXME: The order of the faces is not consistent with the native implementation. */
5049 /* Reserve space for maximum possible faces from triangulation.
5050 * # faces for outer outlines = outline->count - 2
5051 * # faces for inner outlines = outline->count + 2
5052 * There must be at least 1 outer outline. */
5053 glyph->faces.items = HeapAlloc(GetProcessHeap(), 0,
5054 (nb_vertices + glyph->outlines.count * 2 - 4) * sizeof(glyph->faces.items[0]));
5055 if (!glyph->faces.items)
5056 return E_OUTOFMEMORY;
5058 qsort(glyph->ordered_vertices.items, nb_vertices,
5059 sizeof(glyph->ordered_vertices.items[0]), compare_vertex_indices);
5060 for (sweep_idx = 0; sweep_idx < glyph->ordered_vertices.count; sweep_idx++)
5063 int end = triangulations->count;
5067 D3DXVECTOR2 *sweep_vtx = get_ordered_vertex(glyph, sweep_idx);
5068 int current = (start + end) / 2;
5069 struct triangulation *t = &triangulations->items[current];
5070 BOOL on_top_outline = FALSE;
5071 D3DXVECTOR2 *top_next, *bottom_next;
5072 WORD top_idx, bottom_idx;
5074 if (t->merging && t->last_on_top)
5075 top_next = triangulation_get_next_point(t + 1, glyph, TRUE);
5077 top_next = triangulation_get_next_point(t, glyph, TRUE);
5078 if (sweep_vtx == top_next)
5080 if (t->merging && t->last_on_top)
5082 hr = triangulation_add_point(&t, triangulations, sweep_idx, TRUE);
5083 if (hr != S_OK) return hr;
5085 if (t + 1 < &triangulations->items[triangulations->count] &&
5086 triangulation_get_next_point(t + 1, glyph, FALSE) == sweep_vtx)
5088 /* point also on bottom outline of higher triangulation */
5089 struct triangulation *t2 = t + 1;
5090 hr = triangulation_add_point(&t2, triangulations, sweep_idx, FALSE);
5091 if (hr != S_OK) return hr;
5096 on_top_outline = TRUE;
5099 if (t->merging && !t->last_on_top)
5100 bottom_next = triangulation_get_next_point(t - 1, glyph, FALSE);
5102 bottom_next = triangulation_get_next_point(t, glyph, FALSE);
5103 if (sweep_vtx == bottom_next)
5105 if (t->merging && !t->last_on_top)
5107 if (on_top_outline) {
5108 /* outline finished */
5109 remove_triangulation(triangulations, t);
5113 hr = triangulation_add_point(&t, triangulations, sweep_idx, FALSE);
5114 if (hr != S_OK) return hr;
5116 if (t > triangulations->items &&
5117 triangulation_get_next_point(t - 1, glyph, TRUE) == sweep_vtx)
5119 struct triangulation *t2 = t - 1;
5120 /* point also on top outline of lower triangulation */
5121 hr = triangulation_add_point(&t2, triangulations, sweep_idx, TRUE);
5122 if (hr != S_OK) return hr;
5123 t = t2 + 1; /* t may be invalidated by triangulation merging */
5133 if (t->last_on_top) {
5134 top_idx = t->vertex_stack.items[t->vertex_stack.count - 1];
5135 bottom_idx = t->vertex_stack.items[0];
5137 top_idx = t->vertex_stack.items[0];
5138 bottom_idx = t->vertex_stack.items[t->vertex_stack.count - 1];
5141 /* check if the point is inside or outside this polygon */
5142 if (get_line_to_point_y_distance(get_ordered_vertex(glyph, top_idx),
5143 top_next, sweep_vtx) > 0)
5145 start = current + 1;
5146 } else if (get_line_to_point_y_distance(get_ordered_vertex(glyph, bottom_idx),
5147 bottom_next, sweep_vtx) < 0)
5150 } else if (t->merging) {
5151 /* inside, so cancel merging */
5152 struct triangulation *t2 = t->last_on_top ? t + 1 : t - 1;
5154 t2->merging = FALSE;
5155 hr = triangulation_add_point(&t, triangulations, sweep_idx, t->last_on_top);
5156 if (hr != S_OK) return hr;
5157 hr = triangulation_add_point(&t2, triangulations, sweep_idx, t2->last_on_top);
5158 if (hr != S_OK) return hr;
5161 /* inside, so split polygon into two monotone parts */
5162 struct triangulation *t2 = add_triangulation(triangulations);
5163 if (!t2) return E_OUTOFMEMORY;
5164 MoveMemory(t + 1, t, (char*)(t2 + 1) - (char*)t);
5165 if (t->last_on_top) {
5172 ZeroMemory(&t2->vertex_stack, sizeof(t2->vertex_stack));
5173 hr = add_vertex_index(&t2->vertex_stack, t->vertex_stack.items[t->vertex_stack.count - 1]);
5174 if (hr != S_OK) return hr;
5175 hr = add_vertex_index(&t2->vertex_stack, sweep_idx);
5176 if (hr != S_OK) return hr;
5177 t2->last_on_top = !t->last_on_top;
5179 hr = triangulation_add_point(&t, triangulations, sweep_idx, t->last_on_top);
5180 if (hr != S_OK) return hr;
5186 struct triangulation *t;
5187 struct triangulation *t2 = add_triangulation(triangulations);
5188 if (!t2) return E_OUTOFMEMORY;
5189 t = &triangulations->items[start];
5190 MoveMemory(t + 1, t, (char*)(t2 + 1) - (char*)t);
5191 ZeroMemory(t, sizeof(*t));
5192 hr = add_vertex_index(&t->vertex_stack, sweep_idx);
5193 if (hr != S_OK) return hr;
5199 HRESULT WINAPI D3DXCreateTextW(LPDIRECT3DDEVICE9 device,
5200 HDC hdc, LPCWSTR text,
5201 FLOAT deviation, FLOAT extrusion,
5202 LPD3DXMESH *mesh_ptr, LPD3DXBUFFER *adjacency,
5203 LPGLYPHMETRICSFLOAT glyphmetrics)
5206 ID3DXMesh *mesh = NULL;
5207 DWORD nb_vertices, nb_faces;
5208 DWORD nb_front_faces, nb_corners, nb_outline_points;
5209 struct vertex *vertices = NULL;
5214 OUTLINETEXTMETRICW otm;
5215 HFONT font = NULL, oldfont = NULL;
5216 const MAT2 identity = {{0, 1}, {0, 0}, {0, 0}, {0, 1}};
5217 void *raw_outline = NULL;
5219 struct glyphinfo *glyphs = NULL;
5221 struct triangulation_array triangulations = {0, 0, NULL};
5223 struct vertex *vertex_ptr;
5225 float max_deviation_sq;
5226 const struct cos_table cos_table = {
5227 cos(D3DXToRadian(0.5f)),
5228 cos(D3DXToRadian(45.0f)),
5229 cos(D3DXToRadian(90.0f)),
5233 TRACE("(%p, %p, %s, %f, %f, %p, %p, %p)\n", device, hdc,
5234 debugstr_w(text), deviation, extrusion, mesh_ptr, adjacency, glyphmetrics);
5236 if (!device || !hdc || !text || !*text || deviation < 0.0f || extrusion < 0.0f || !mesh_ptr)
5237 return D3DERR_INVALIDCALL;
5241 FIXME("Case of adjacency != NULL not implemented.\n");
5245 if (!GetObjectW(GetCurrentObject(hdc, OBJ_FONT), sizeof(lf), &lf) ||
5246 !GetOutlineTextMetricsW(hdc, sizeof(otm), &otm))
5248 return D3DERR_INVALIDCALL;
5251 if (deviation == 0.0f)
5252 deviation = 1.0f / otm.otmEMSquare;
5253 max_deviation_sq = deviation * deviation;
5255 lf.lfHeight = otm.otmEMSquare;
5257 font = CreateFontIndirectW(&lf);
5262 oldfont = SelectObject(hdc, font);
5264 textlen = strlenW(text);
5265 for (i = 0; i < textlen; i++)
5267 int datasize = GetGlyphOutlineW(hdc, text[i], GGO_NATIVE, &gm, 0, NULL, &identity);
5269 return D3DERR_INVALIDCALL;
5270 if (bufsize < datasize)
5273 if (!bufsize) { /* e.g. text == " " */
5274 hr = D3DERR_INVALIDCALL;
5278 glyphs = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, textlen * sizeof(*glyphs));
5279 raw_outline = HeapAlloc(GetProcessHeap(), 0, bufsize);
5280 if (!glyphs || !raw_outline) {
5286 for (i = 0; i < textlen; i++)
5288 /* get outline points from data returned from GetGlyphOutline */
5291 glyphs[i].offset_x = offset_x;
5293 datasize = GetGlyphOutlineW(hdc, text[i], GGO_NATIVE, &gm, bufsize, raw_outline, &identity);
5294 hr = create_outline(&glyphs[i], raw_outline, datasize,
5295 max_deviation_sq, otm.otmEMSquare, &cos_table);
5296 if (hr != S_OK) goto error;
5298 triangulations.glyph = &glyphs[i];
5299 hr = triangulate(&triangulations);
5300 if (hr != S_OK) goto error;
5301 if (triangulations.count) {
5302 ERR("%d incomplete triangulations of glyph (%u).\n", triangulations.count, text[i]);
5303 triangulations.count = 0;
5308 glyphmetrics[i].gmfBlackBoxX = gm.gmBlackBoxX / (float)otm.otmEMSquare;
5309 glyphmetrics[i].gmfBlackBoxY = gm.gmBlackBoxY / (float)otm.otmEMSquare;
5310 glyphmetrics[i].gmfptGlyphOrigin.x = gm.gmptGlyphOrigin.x / (float)otm.otmEMSquare;
5311 glyphmetrics[i].gmfptGlyphOrigin.y = gm.gmptGlyphOrigin.y / (float)otm.otmEMSquare;
5312 glyphmetrics[i].gmfCellIncX = gm.gmCellIncX / (float)otm.otmEMSquare;
5313 glyphmetrics[i].gmfCellIncY = gm.gmCellIncY / (float)otm.otmEMSquare;
5315 offset_x += gm.gmCellIncX / (float)otm.otmEMSquare;
5318 /* corner points need an extra vertex for the different side faces normals */
5320 nb_outline_points = 0;
5322 for (i = 0; i < textlen; i++)
5325 nb_outline_points += glyphs[i].ordered_vertices.count;
5326 nb_front_faces += glyphs[i].faces.count;
5327 for (j = 0; j < glyphs[i].outlines.count; j++)
5330 struct outline *outline = &glyphs[i].outlines.items[j];
5331 nb_corners++; /* first outline point always repeated as a corner */
5332 for (k = 1; k < outline->count; k++)
5333 if (outline->items[k].corner)
5338 nb_vertices = (nb_outline_points + nb_corners) * 2 + nb_outline_points * 2;
5339 nb_faces = nb_outline_points * 2 + nb_front_faces * 2;
5342 hr = D3DXCreateMeshFVF(nb_faces, nb_vertices, D3DXMESH_MANAGED,
5343 D3DFVF_XYZ | D3DFVF_NORMAL, device, &mesh);
5347 hr = mesh->lpVtbl->LockVertexBuffer(mesh, D3DLOCK_DISCARD, (LPVOID *)&vertices);
5351 hr = mesh->lpVtbl->LockIndexBuffer(mesh, D3DLOCK_DISCARD, (LPVOID *)&faces);
5355 /* convert 2D vertices and faces into 3D mesh */
5356 vertex_ptr = vertices;
5358 if (extrusion == 0.0f) {
5365 for (i = 0; i < textlen; i++)
5369 struct vertex *back_vertices;
5372 /* side vertices and faces */
5373 for (j = 0; j < glyphs[i].outlines.count; j++)
5375 struct vertex *outline_vertices = vertex_ptr;
5376 struct outline *outline = &glyphs[i].outlines.items[j];
5378 struct point2d *prevpt = &outline->items[outline->count - 1];
5379 struct point2d *pt = &outline->items[0];
5381 for (k = 1; k <= outline->count; k++)
5384 struct point2d *nextpt = &outline->items[k % outline->count];
5385 WORD vtx_idx = vertex_ptr - vertices;
5388 if (pt->corner == POINTTYPE_CURVE_START)
5389 D3DXVec2Subtract(&vec, &pt->pos, &prevpt->pos);
5390 else if (pt->corner)
5391 D3DXVec2Subtract(&vec, &nextpt->pos, &pt->pos);
5393 D3DXVec2Subtract(&vec, &nextpt->pos, &prevpt->pos);
5394 D3DXVec2Normalize(&vec, &vec);
5395 vtx.normal.x = -vec.y;
5396 vtx.normal.y = vec.x;
5399 vtx.position.x = pt->pos.x + glyphs[i].offset_x;
5400 vtx.position.y = pt->pos.y;
5402 *vertex_ptr++ = vtx;
5404 vtx.position.z = -extrusion;
5405 *vertex_ptr++ = vtx;
5407 vtx.position.x = nextpt->pos.x + glyphs[i].offset_x;
5408 vtx.position.y = nextpt->pos.y;
5409 if (pt->corner && nextpt->corner && nextpt->corner != POINTTYPE_CURVE_END) {
5410 vtx.position.z = -extrusion;
5411 *vertex_ptr++ = vtx;
5413 *vertex_ptr++ = vtx;
5415 (*face_ptr)[0] = vtx_idx;
5416 (*face_ptr)[1] = vtx_idx + 2;
5417 (*face_ptr)[2] = vtx_idx + 1;
5420 (*face_ptr)[0] = vtx_idx;
5421 (*face_ptr)[1] = vtx_idx + 3;
5422 (*face_ptr)[2] = vtx_idx + 2;
5425 if (nextpt->corner) {
5426 if (nextpt->corner == POINTTYPE_CURVE_END) {
5427 D3DXVECTOR2 *nextpt2 = &outline->items[(k + 1) % outline->count].pos;
5428 D3DXVec2Subtract(&vec, nextpt2, &nextpt->pos);
5430 D3DXVec2Subtract(&vec, &nextpt->pos, &pt->pos);
5432 D3DXVec2Normalize(&vec, &vec);
5433 vtx.normal.x = -vec.y;
5434 vtx.normal.y = vec.x;
5437 *vertex_ptr++ = vtx;
5438 vtx.position.z = -extrusion;
5439 *vertex_ptr++ = vtx;
5442 (*face_ptr)[0] = vtx_idx;
5443 (*face_ptr)[1] = vtx_idx + 3;
5444 (*face_ptr)[2] = vtx_idx + 1;
5447 (*face_ptr)[0] = vtx_idx;
5448 (*face_ptr)[1] = vtx_idx + 2;
5449 (*face_ptr)[2] = vtx_idx + 3;
5457 *vertex_ptr++ = *outline_vertices++;
5458 *vertex_ptr++ = *outline_vertices++;
5462 /* back vertices and faces */
5463 back_faces = face_ptr;
5464 back_vertices = vertex_ptr;
5465 for (j = 0; j < glyphs[i].ordered_vertices.count; j++)
5467 D3DXVECTOR2 *pt = get_ordered_vertex(&glyphs[i], j);
5468 vertex_ptr->position.x = pt->x + glyphs[i].offset_x;
5469 vertex_ptr->position.y = pt->y;
5470 vertex_ptr->position.z = 0;
5471 vertex_ptr->normal.x = 0;
5472 vertex_ptr->normal.y = 0;
5473 vertex_ptr->normal.z = 1;
5476 count = back_vertices - vertices;
5477 for (j = 0; j < glyphs[i].faces.count; j++)
5479 face *f = &glyphs[i].faces.items[j];
5480 (*face_ptr)[0] = (*f)[0] + count;
5481 (*face_ptr)[1] = (*f)[1] + count;
5482 (*face_ptr)[2] = (*f)[2] + count;
5486 /* front vertices and faces */
5487 j = count = vertex_ptr - back_vertices;
5490 vertex_ptr->position.x = back_vertices->position.x;
5491 vertex_ptr->position.y = back_vertices->position.y;
5492 vertex_ptr->position.z = -extrusion;
5493 vertex_ptr->normal.x = 0;
5494 vertex_ptr->normal.y = 0;
5495 vertex_ptr->normal.z = extrusion == 0.0f ? 1.0f : -1.0f;
5499 j = face_ptr - back_faces;
5502 (*face_ptr)[0] = (*back_faces)[0] + count;
5503 (*face_ptr)[1] = (*back_faces)[f1] + count;
5504 (*face_ptr)[2] = (*back_faces)[f2] + count;
5514 if (faces) mesh->lpVtbl->UnlockIndexBuffer(mesh);
5515 if (vertices) mesh->lpVtbl->UnlockVertexBuffer(mesh);
5516 if (hr != D3D_OK) mesh->lpVtbl->Release(mesh);
5519 for (i = 0; i < textlen; i++)
5522 for (j = 0; j < glyphs[i].outlines.count; j++)
5523 HeapFree(GetProcessHeap(), 0, glyphs[i].outlines.items[j].items);
5524 HeapFree(GetProcessHeap(), 0, glyphs[i].outlines.items);
5525 HeapFree(GetProcessHeap(), 0, glyphs[i].faces.items);
5526 HeapFree(GetProcessHeap(), 0, glyphs[i].ordered_vertices.items);
5528 HeapFree(GetProcessHeap(), 0, glyphs);
5530 if (triangulations.items) {
5532 for (i = 0; i < triangulations.count; i++)
5533 HeapFree(GetProcessHeap(), 0, triangulations.items[i].vertex_stack.items);
5534 HeapFree(GetProcessHeap(), 0, triangulations.items);
5536 HeapFree(GetProcessHeap(), 0, raw_outline);
5537 if (oldfont) SelectObject(hdc, oldfont);
5538 if (font) DeleteObject(font);