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