d3dx9: Implement UpdateSemantics mesh method.
[wine/multimedia.git] / dlls / d3dx9_36 / mesh.c
blob0455ea7a6992f1b8d6a40a6b056423aabf44445f
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 #include "windef.h"
33 #include "wingdi.h"
34 #include "d3dx9.h"
35 #undef MAKE_DDHRESULT
36 #include "dxfile.h"
37 #include "rmxfguid.h"
38 #include "rmxftmpl.h"
39 #include "wine/debug.h"
40 #include "wine/unicode.h"
41 #include "wine/list.h"
42 #include "d3dx9_36_private.h"
44 WINE_DEFAULT_DEBUG_CHANNEL(d3dx);
46 typedef struct ID3DXMeshImpl
48 ID3DXMesh ID3DXMesh_iface;
49 LONG ref;
51 DWORD numfaces;
52 DWORD numvertices;
53 DWORD options;
54 DWORD fvf;
55 IDirect3DDevice9 *device;
56 D3DVERTEXELEMENT9 cached_declaration[MAX_FVF_DECL_SIZE];
57 IDirect3DVertexDeclaration9 *vertex_declaration;
58 UINT vertex_declaration_size;
59 UINT num_elem;
60 IDirect3DVertexBuffer9 *vertex_buffer;
61 IDirect3DIndexBuffer9 *index_buffer;
62 DWORD *attrib_buffer;
63 int attrib_buffer_lock_count;
64 DWORD attrib_table_size;
65 D3DXATTRIBUTERANGE *attrib_table;
66 } ID3DXMeshImpl;
68 static inline ID3DXMeshImpl *impl_from_ID3DXMesh(ID3DXMesh *iface)
70 return CONTAINING_RECORD(iface, ID3DXMeshImpl, ID3DXMesh_iface);
73 static HRESULT WINAPI ID3DXMeshImpl_QueryInterface(ID3DXMesh *iface, REFIID riid, LPVOID *object)
75 ID3DXMeshImpl *This = impl_from_ID3DXMesh(iface);
77 TRACE("(%p)->(%s, %p)\n", This, debugstr_guid(riid), object);
79 if (IsEqualGUID(riid, &IID_IUnknown) ||
80 IsEqualGUID(riid, &IID_ID3DXBaseMesh) ||
81 IsEqualGUID(riid, &IID_ID3DXMesh))
83 iface->lpVtbl->AddRef(iface);
84 *object = This;
85 return S_OK;
88 WARN("Interface %s not found.\n", debugstr_guid(riid));
90 return E_NOINTERFACE;
93 static ULONG WINAPI ID3DXMeshImpl_AddRef(ID3DXMesh *iface)
95 ID3DXMeshImpl *This = impl_from_ID3DXMesh(iface);
97 TRACE("(%p)->(): AddRef from %d\n", This, This->ref);
99 return InterlockedIncrement(&This->ref);
102 static ULONG WINAPI ID3DXMeshImpl_Release(ID3DXMesh *iface)
104 ID3DXMeshImpl *This = impl_from_ID3DXMesh(iface);
105 ULONG ref = InterlockedDecrement(&This->ref);
107 TRACE("(%p)->(): Release from %d\n", This, ref + 1);
109 if (!ref)
111 IDirect3DIndexBuffer9_Release(This->index_buffer);
112 IDirect3DVertexBuffer9_Release(This->vertex_buffer);
113 if (This->vertex_declaration)
114 IDirect3DVertexDeclaration9_Release(This->vertex_declaration);
115 IDirect3DDevice9_Release(This->device);
116 HeapFree(GetProcessHeap(), 0, This->attrib_buffer);
117 HeapFree(GetProcessHeap(), 0, This->attrib_table);
118 HeapFree(GetProcessHeap(), 0, This);
121 return ref;
124 /*** ID3DXBaseMesh ***/
125 static HRESULT WINAPI ID3DXMeshImpl_DrawSubset(ID3DXMesh *iface, DWORD attrib_id)
127 ID3DXMeshImpl *This = impl_from_ID3DXMesh(iface);
128 HRESULT hr;
129 DWORD face_start;
130 DWORD face_end = 0;
131 DWORD vertex_size;
133 TRACE("(%p)->(%u)\n", This, attrib_id);
135 if (!This->vertex_declaration)
137 WARN("Can't draw a mesh with an invalid vertex declaration.\n");
138 return E_FAIL;
141 vertex_size = iface->lpVtbl->GetNumBytesPerVertex(iface);
143 hr = IDirect3DDevice9_SetVertexDeclaration(This->device, This->vertex_declaration);
144 if (FAILED(hr)) return hr;
145 hr = IDirect3DDevice9_SetStreamSource(This->device, 0, This->vertex_buffer, 0, vertex_size);
146 if (FAILED(hr)) return hr;
147 hr = IDirect3DDevice9_SetIndices(This->device, This->index_buffer);
148 if (FAILED(hr)) return hr;
150 while (face_end < This->numfaces)
152 for (face_start = face_end; face_start < This->numfaces; face_start++)
154 if (This->attrib_buffer[face_start] == attrib_id)
155 break;
157 if (face_start >= This->numfaces)
158 break;
159 for (face_end = face_start + 1; face_end < This->numfaces; face_end++)
161 if (This->attrib_buffer[face_end] != attrib_id)
162 break;
165 hr = IDirect3DDevice9_DrawIndexedPrimitive(This->device, D3DPT_TRIANGLELIST,
166 0, 0, This->numvertices, face_start * 3, face_end - face_start);
167 if (FAILED(hr)) return hr;
170 return D3D_OK;
173 static DWORD WINAPI ID3DXMeshImpl_GetNumFaces(ID3DXMesh *iface)
175 ID3DXMeshImpl *This = impl_from_ID3DXMesh(iface);
177 TRACE("(%p)\n", This);
179 return This->numfaces;
182 static DWORD WINAPI ID3DXMeshImpl_GetNumVertices(ID3DXMesh *iface)
184 ID3DXMeshImpl *This = impl_from_ID3DXMesh(iface);
186 TRACE("(%p)\n", This);
188 return This->numvertices;
191 static DWORD WINAPI ID3DXMeshImpl_GetFVF(ID3DXMesh *iface)
193 ID3DXMeshImpl *This = impl_from_ID3DXMesh(iface);
195 TRACE("(%p)\n", This);
197 return This->fvf;
200 static void copy_declaration(D3DVERTEXELEMENT9 *dst, const D3DVERTEXELEMENT9 *src, UINT num_elem)
202 memcpy(dst, src, num_elem * sizeof(*src));
205 static HRESULT WINAPI ID3DXMeshImpl_GetDeclaration(ID3DXMesh *iface, D3DVERTEXELEMENT9 declaration[MAX_FVF_DECL_SIZE])
207 ID3DXMeshImpl *This = impl_from_ID3DXMesh(iface);
209 TRACE("(%p)\n", This);
211 if (declaration == NULL) return D3DERR_INVALIDCALL;
213 copy_declaration(declaration, This->cached_declaration, This->num_elem);
215 return D3D_OK;
218 static DWORD WINAPI ID3DXMeshImpl_GetNumBytesPerVertex(ID3DXMesh *iface)
220 ID3DXMeshImpl *This = impl_from_ID3DXMesh(iface);
222 TRACE("iface (%p)\n", This);
224 return This->vertex_declaration_size;
227 static DWORD WINAPI ID3DXMeshImpl_GetOptions(ID3DXMesh *iface)
229 ID3DXMeshImpl *This = impl_from_ID3DXMesh(iface);
231 TRACE("(%p)\n", This);
233 return This->options;
236 static HRESULT WINAPI ID3DXMeshImpl_GetDevice(ID3DXMesh *iface, LPDIRECT3DDEVICE9 *device)
238 ID3DXMeshImpl *This = impl_from_ID3DXMesh(iface);
240 TRACE("(%p)->(%p)\n", This, device);
242 if (device == NULL) return D3DERR_INVALIDCALL;
243 *device = This->device;
244 IDirect3DDevice9_AddRef(This->device);
246 return D3D_OK;
249 static HRESULT WINAPI ID3DXMeshImpl_CloneMeshFVF(ID3DXMesh *iface, DWORD options, DWORD fvf, LPDIRECT3DDEVICE9 device, LPD3DXMESH *clone_mesh)
251 ID3DXMeshImpl *This = impl_from_ID3DXMesh(iface);
252 HRESULT hr;
253 D3DVERTEXELEMENT9 declaration[MAX_FVF_DECL_SIZE];
255 TRACE("(%p)->(%x,%x,%p,%p)\n", This, options, fvf, device, clone_mesh);
257 hr = D3DXDeclaratorFromFVF(fvf, declaration);
258 if (FAILED(hr)) return hr;
260 return iface->lpVtbl->CloneMesh(iface, options, declaration, device, clone_mesh);
263 static HRESULT WINAPI ID3DXMeshImpl_CloneMesh(ID3DXMesh *iface, DWORD options, CONST D3DVERTEXELEMENT9 *declaration, LPDIRECT3DDEVICE9 device,
264 LPD3DXMESH *clone_mesh_out)
266 ID3DXMeshImpl *This = impl_from_ID3DXMesh(iface);
267 ID3DXMeshImpl *cloned_this;
268 ID3DXMesh *clone_mesh;
269 D3DVERTEXELEMENT9 orig_declaration[MAX_FVF_DECL_SIZE] = { D3DDECL_END() };
270 void *data_in, *data_out;
271 DWORD vertex_size;
272 HRESULT hr;
273 int i;
275 TRACE("(%p)->(%x,%p,%p,%p)\n", This, options, declaration, device, clone_mesh_out);
277 if (!clone_mesh_out)
278 return D3DERR_INVALIDCALL;
280 hr = iface->lpVtbl->GetDeclaration(iface, orig_declaration);
281 if (FAILED(hr)) return hr;
283 for (i = 0; orig_declaration[i].Stream != 0xff; i++) {
284 if (memcmp(&orig_declaration[i], &declaration[i], sizeof(*declaration)))
286 FIXME("Vertex buffer conversion not implemented.\n");
287 return E_NOTIMPL;
291 hr = D3DXCreateMesh(This->numfaces, This->numvertices, options & ~D3DXMESH_VB_SHARE,
292 declaration, device, &clone_mesh);
293 if (FAILED(hr)) return hr;
295 cloned_this = impl_from_ID3DXMesh(clone_mesh);
296 vertex_size = clone_mesh->lpVtbl->GetNumBytesPerVertex(clone_mesh);
298 if (options & D3DXMESH_VB_SHARE) {
299 IDirect3DVertexBuffer9_AddRef(This->vertex_buffer);
300 /* FIXME: refactor to avoid creating a new vertex buffer */
301 IDirect3DVertexBuffer9_Release(cloned_this->vertex_buffer);
302 cloned_this->vertex_buffer = This->vertex_buffer;
303 } else {
304 hr = iface->lpVtbl->LockVertexBuffer(iface, D3DLOCK_READONLY, &data_in);
305 if (FAILED(hr)) goto error;
306 hr = clone_mesh->lpVtbl->LockVertexBuffer(clone_mesh, D3DLOCK_DISCARD, &data_out);
307 if (FAILED(hr)) {
308 iface->lpVtbl->UnlockVertexBuffer(iface);
309 goto error;
311 memcpy(data_out, data_in, This->numvertices * vertex_size);
312 clone_mesh->lpVtbl->UnlockVertexBuffer(clone_mesh);
313 iface->lpVtbl->UnlockVertexBuffer(iface);
316 hr = iface->lpVtbl->LockIndexBuffer(iface, D3DLOCK_READONLY, &data_in);
317 if (FAILED(hr)) goto error;
318 hr = clone_mesh->lpVtbl->LockIndexBuffer(clone_mesh, D3DLOCK_DISCARD, &data_out);
319 if (FAILED(hr)) {
320 iface->lpVtbl->UnlockIndexBuffer(iface);
321 goto error;
323 if ((options ^ This->options) & D3DXMESH_32BIT) {
324 if (options & D3DXMESH_32BIT) {
325 for (i = 0; i < This->numfaces * 3; i++)
326 ((DWORD*)data_out)[i] = ((WORD*)data_in)[i];
327 } else {
328 for (i = 0; i < This->numfaces * 3; i++)
329 ((WORD*)data_out)[i] = ((DWORD*)data_in)[i];
331 } else {
332 memcpy(data_out, data_in, This->numfaces * 3 * (options & D3DXMESH_32BIT ? 4 : 2));
334 clone_mesh->lpVtbl->UnlockIndexBuffer(clone_mesh);
335 iface->lpVtbl->UnlockIndexBuffer(iface);
337 memcpy(cloned_this->attrib_buffer, This->attrib_buffer, This->numfaces * sizeof(*This->attrib_buffer));
339 if (This->attrib_table_size)
341 cloned_this->attrib_table_size = This->attrib_table_size;
342 cloned_this->attrib_table = HeapAlloc(GetProcessHeap(), 0, This->attrib_table_size * sizeof(*This->attrib_table));
343 if (!cloned_this->attrib_table) {
344 hr = E_OUTOFMEMORY;
345 goto error;
347 memcpy(cloned_this->attrib_table, This->attrib_table, This->attrib_table_size * sizeof(*This->attrib_table));
350 *clone_mesh_out = clone_mesh;
352 return D3D_OK;
353 error:
354 IUnknown_Release(clone_mesh);
355 return hr;
358 static HRESULT WINAPI ID3DXMeshImpl_GetVertexBuffer(ID3DXMesh *iface, LPDIRECT3DVERTEXBUFFER9 *vertex_buffer)
360 ID3DXMeshImpl *This = impl_from_ID3DXMesh(iface);
362 TRACE("(%p)->(%p)\n", This, vertex_buffer);
364 if (vertex_buffer == NULL) return D3DERR_INVALIDCALL;
365 *vertex_buffer = This->vertex_buffer;
366 IDirect3DVertexBuffer9_AddRef(This->vertex_buffer);
368 return D3D_OK;
371 static HRESULT WINAPI ID3DXMeshImpl_GetIndexBuffer(ID3DXMesh *iface, LPDIRECT3DINDEXBUFFER9 *index_buffer)
373 ID3DXMeshImpl *This = impl_from_ID3DXMesh(iface);
375 TRACE("(%p)->(%p)\n", This, index_buffer);
377 if (index_buffer == NULL) return D3DERR_INVALIDCALL;
378 *index_buffer = This->index_buffer;
379 IDirect3DIndexBuffer9_AddRef(This->index_buffer);
381 return D3D_OK;
384 static HRESULT WINAPI ID3DXMeshImpl_LockVertexBuffer(ID3DXMesh *iface, DWORD flags, LPVOID *data)
386 ID3DXMeshImpl *This = impl_from_ID3DXMesh(iface);
388 TRACE("(%p)->(%u,%p)\n", This, flags, data);
390 return IDirect3DVertexBuffer9_Lock(This->vertex_buffer, 0, 0, data, flags);
393 static HRESULT WINAPI ID3DXMeshImpl_UnlockVertexBuffer(ID3DXMesh *iface)
395 ID3DXMeshImpl *This = impl_from_ID3DXMesh(iface);
397 TRACE("(%p)\n", This);
399 return IDirect3DVertexBuffer9_Unlock(This->vertex_buffer);
402 static HRESULT WINAPI ID3DXMeshImpl_LockIndexBuffer(ID3DXMesh *iface, DWORD flags, LPVOID *data)
404 ID3DXMeshImpl *This = impl_from_ID3DXMesh(iface);
406 TRACE("(%p)->(%u,%p)\n", This, flags, data);
408 return IDirect3DIndexBuffer9_Lock(This->index_buffer, 0, 0, data, flags);
411 static HRESULT WINAPI ID3DXMeshImpl_UnlockIndexBuffer(ID3DXMesh *iface)
413 ID3DXMeshImpl *This = impl_from_ID3DXMesh(iface);
415 TRACE("(%p)\n", This);
417 return IDirect3DIndexBuffer9_Unlock(This->index_buffer);
420 static HRESULT WINAPI ID3DXMeshImpl_GetAttributeTable(ID3DXMesh *iface, D3DXATTRIBUTERANGE *attrib_table, DWORD *attrib_table_size)
422 ID3DXMeshImpl *This = impl_from_ID3DXMesh(iface);
424 TRACE("(%p)->(%p,%p)\n", This, attrib_table, attrib_table_size);
426 if (attrib_table_size)
427 *attrib_table_size = This->attrib_table_size;
429 if (attrib_table)
430 CopyMemory(attrib_table, This->attrib_table, This->attrib_table_size * sizeof(*attrib_table));
432 return D3D_OK;
435 static HRESULT WINAPI ID3DXMeshImpl_ConvertPointRepsToAdjacency(ID3DXMesh *iface, CONST DWORD *point_reps, DWORD *adjacency)
437 ID3DXMeshImpl *This = impl_from_ID3DXMesh(iface);
439 FIXME("(%p)->(%p,%p): stub\n", This, point_reps, adjacency);
441 return E_NOTIMPL;
444 static HRESULT WINAPI ID3DXMeshImpl_ConvertAdjacencyToPointReps(ID3DXMesh *iface, CONST DWORD *adjacency, DWORD *point_reps)
446 ID3DXMeshImpl *This = impl_from_ID3DXMesh(iface);
448 FIXME("(%p)->(%p,%p): stub\n", This, adjacency, point_reps);
450 return E_NOTIMPL;
453 struct vertex_metadata {
454 float key;
455 DWORD vertex_index;
456 DWORD first_shared_index;
459 static int compare_vertex_keys(const void *a, const void *b)
461 const struct vertex_metadata *left = a;
462 const struct vertex_metadata *right = b;
463 if (left->key == right->key)
464 return 0;
465 return left->key < right->key ? -1 : 1;
468 static HRESULT WINAPI ID3DXMeshImpl_GenerateAdjacency(ID3DXMesh *iface, FLOAT epsilon, DWORD *adjacency)
470 ID3DXMeshImpl *This = impl_from_ID3DXMesh(iface);
471 HRESULT hr;
472 BYTE *vertices = NULL;
473 const DWORD *indices = NULL;
474 DWORD vertex_size;
475 DWORD buffer_size;
476 /* sort the vertices by (x + y + z) to quickly find coincident vertices */
477 struct vertex_metadata *sorted_vertices;
478 /* shared_indices links together identical indices in the index buffer so
479 * that adjacency checks can be limited to faces sharing a vertex */
480 DWORD *shared_indices = NULL;
481 const FLOAT epsilon_sq = epsilon * epsilon;
482 int i;
484 TRACE("(%p)->(%f,%p)\n", This, epsilon, adjacency);
486 if (!adjacency)
487 return D3DERR_INVALIDCALL;
489 buffer_size = This->numfaces * 3 * sizeof(*shared_indices) + This->numvertices * sizeof(*sorted_vertices);
490 if (!(This->options & D3DXMESH_32BIT))
491 buffer_size += This->numfaces * 3 * sizeof(*indices);
492 shared_indices = HeapAlloc(GetProcessHeap(), 0, buffer_size);
493 if (!shared_indices)
494 return E_OUTOFMEMORY;
495 sorted_vertices = (struct vertex_metadata*)(shared_indices + This->numfaces * 3);
497 hr = iface->lpVtbl->LockVertexBuffer(iface, D3DLOCK_READONLY, (void**)&vertices);
498 if (FAILED(hr)) goto cleanup;
499 hr = iface->lpVtbl->LockIndexBuffer(iface, D3DLOCK_READONLY, (void**)&indices);
500 if (FAILED(hr)) goto cleanup;
502 if (!(This->options & D3DXMESH_32BIT)) {
503 const WORD *word_indices = (const WORD*)indices;
504 DWORD *dword_indices = (DWORD*)(sorted_vertices + This->numvertices);
505 indices = dword_indices;
506 for (i = 0; i < This->numfaces * 3; i++)
507 *dword_indices++ = *word_indices++;
510 vertex_size = iface->lpVtbl->GetNumBytesPerVertex(iface);
511 for (i = 0; i < This->numvertices; i++) {
512 D3DXVECTOR3 *vertex = (D3DXVECTOR3*)(vertices + vertex_size * i);
513 sorted_vertices[i].first_shared_index = -1;
514 sorted_vertices[i].key = vertex->x + vertex->y + vertex->z;
515 sorted_vertices[i].vertex_index = i;
517 for (i = 0; i < This->numfaces * 3; i++) {
518 DWORD *first_shared_index = &sorted_vertices[indices[i]].first_shared_index;
519 shared_indices[i] = *first_shared_index;
520 *first_shared_index = i;
521 adjacency[i] = -1;
523 qsort(sorted_vertices, This->numvertices, sizeof(*sorted_vertices), compare_vertex_keys);
525 for (i = 0; i < This->numvertices; i++) {
526 struct vertex_metadata *sorted_vertex_a = &sorted_vertices[i];
527 D3DXVECTOR3 *vertex_a = (D3DXVECTOR3*)(vertices + sorted_vertex_a->vertex_index * vertex_size);
528 DWORD shared_index_a = sorted_vertex_a->first_shared_index;
530 while (shared_index_a != -1) {
531 int j = i;
532 DWORD shared_index_b = shared_indices[shared_index_a];
533 struct vertex_metadata *sorted_vertex_b = sorted_vertex_a;
535 while (TRUE) {
536 while (shared_index_b != -1) {
537 /* faces are adjacent if they have another coincident vertex */
538 DWORD base_a = (shared_index_a / 3) * 3;
539 DWORD base_b = (shared_index_b / 3) * 3;
540 BOOL adjacent = FALSE;
541 int k;
543 for (k = 0; k < 3; k++) {
544 if (adjacency[base_b + k] == shared_index_a / 3) {
545 adjacent = TRUE;
546 break;
549 if (!adjacent) {
550 for (k = 1; k <= 2; k++) {
551 DWORD vertex_index_a = base_a + (shared_index_a + k) % 3;
552 DWORD vertex_index_b = base_b + (shared_index_b + (3 - k)) % 3;
553 adjacent = indices[vertex_index_a] == indices[vertex_index_b];
554 if (!adjacent && epsilon >= 0.0f) {
555 D3DXVECTOR3 delta = {0.0f, 0.0f, 0.0f};
556 FLOAT length_sq;
558 D3DXVec3Subtract(&delta,
559 (D3DXVECTOR3*)(vertices + indices[vertex_index_a] * vertex_size),
560 (D3DXVECTOR3*)(vertices + indices[vertex_index_b] * vertex_size));
561 length_sq = D3DXVec3LengthSq(&delta);
562 adjacent = epsilon == 0.0f ? length_sq == 0.0f : length_sq < epsilon_sq;
564 if (adjacent) {
565 DWORD adj_a = base_a + 2 - (vertex_index_a + shared_index_a + 1) % 3;
566 DWORD adj_b = base_b + 2 - (vertex_index_b + shared_index_b + 1) % 3;
567 if (adjacency[adj_a] == -1 && adjacency[adj_b] == -1) {
568 adjacency[adj_a] = base_b / 3;
569 adjacency[adj_b] = base_a / 3;
570 break;
576 shared_index_b = shared_indices[shared_index_b];
578 while (++j < This->numvertices) {
579 D3DXVECTOR3 *vertex_b;
581 sorted_vertex_b++;
582 if (sorted_vertex_b->key - sorted_vertex_a->key > epsilon * 3.0f) {
583 /* no more coincident vertices to try */
584 j = This->numvertices;
585 break;
587 /* check for coincidence */
588 vertex_b = (D3DXVECTOR3*)(vertices + sorted_vertex_b->vertex_index * vertex_size);
589 if (fabsf(vertex_a->x - vertex_b->x) <= epsilon &&
590 fabsf(vertex_a->y - vertex_b->y) <= epsilon &&
591 fabsf(vertex_a->z - vertex_b->z) <= epsilon)
593 break;
596 if (j >= This->numvertices)
597 break;
598 shared_index_b = sorted_vertex_b->first_shared_index;
601 sorted_vertex_a->first_shared_index = shared_indices[sorted_vertex_a->first_shared_index];
602 shared_index_a = sorted_vertex_a->first_shared_index;
606 hr = D3D_OK;
607 cleanup:
608 if (indices) iface->lpVtbl->UnlockIndexBuffer(iface);
609 if (vertices) iface->lpVtbl->UnlockVertexBuffer(iface);
610 HeapFree(GetProcessHeap(), 0, shared_indices);
611 return hr;
614 static HRESULT WINAPI ID3DXMeshImpl_UpdateSemantics(ID3DXMesh *iface, D3DVERTEXELEMENT9 declaration[MAX_FVF_DECL_SIZE])
616 HRESULT hr;
617 ID3DXMeshImpl *This = impl_from_ID3DXMesh(iface);
618 UINT vertex_declaration_size;
619 int i;
621 TRACE("(%p)->(%p)\n", This, declaration);
623 if (!declaration)
625 WARN("Invalid declaration. Can't use NULL declaration.\n");
626 return D3DERR_INVALIDCALL;
629 /* New declaration must be same size as original */
630 vertex_declaration_size = D3DXGetDeclVertexSize(declaration, declaration[0].Stream);
631 if (vertex_declaration_size != This->vertex_declaration_size)
633 WARN("Invalid declaration. New vertex size does not match the orginal vertex size.\n");
634 return D3DERR_INVALIDCALL;
637 /* New declaration must not contain non-zero Stream value */
638 for (i = 0; declaration[i].Stream != 0xff; i++)
640 if (declaration[i].Stream != 0)
642 WARN("Invalid declaration. New declaration contains non-zero Stream value.\n");
643 return D3DERR_INVALIDCALL;
647 This->num_elem = i + 1;
648 copy_declaration(This->cached_declaration, declaration, This->num_elem);
650 if (This->vertex_declaration)
651 IDirect3DVertexDeclaration9_Release(This->vertex_declaration);
653 /* An application can pass an invalid declaration to UpdateSemantics and
654 * still expect D3D_OK (see tests). If the declaration is invalid, then
655 * subsequent calls to DrawSubset will fail. This is handled by setting the
656 * vertex declaration to NULL.
657 * GetDeclaration, GetNumBytesPerVertex must, however, use the new
658 * invalid declaration. This is handled by them using the cached vertex
659 * declaration instead of the actual vertex declaration.
661 hr = IDirect3DDevice9_CreateVertexDeclaration(This->device,
662 declaration,
663 &This->vertex_declaration);
664 if (FAILED(hr))
666 WARN("Using invalid declaration. Calls to DrawSubset will fail.\n");
667 This->vertex_declaration = NULL;
670 return D3D_OK;
673 /*** ID3DXMesh ***/
674 static HRESULT WINAPI ID3DXMeshImpl_LockAttributeBuffer(ID3DXMesh *iface, DWORD flags, DWORD **data)
676 ID3DXMeshImpl *This = impl_from_ID3DXMesh(iface);
678 TRACE("(%p)->(%u,%p)\n", This, flags, data);
680 InterlockedIncrement(&This->attrib_buffer_lock_count);
682 if (!(flags & D3DLOCK_READONLY)) {
683 D3DXATTRIBUTERANGE *attrib_table = This->attrib_table;
684 This->attrib_table_size = 0;
685 This->attrib_table = NULL;
686 HeapFree(GetProcessHeap(), 0, attrib_table);
689 *data = This->attrib_buffer;
691 return D3D_OK;
694 static HRESULT WINAPI ID3DXMeshImpl_UnlockAttributeBuffer(ID3DXMesh *iface)
696 ID3DXMeshImpl *This = impl_from_ID3DXMesh(iface);
697 int lock_count;
699 TRACE("(%p)\n", This);
701 lock_count = InterlockedDecrement(&This->attrib_buffer_lock_count);
703 if (lock_count < 0) {
704 InterlockedIncrement(&This->attrib_buffer_lock_count);
705 return D3DERR_INVALIDCALL;
708 return D3D_OK;
711 static HRESULT WINAPI ID3DXMeshImpl_Optimize(ID3DXMesh *iface, DWORD flags, CONST DWORD *adjacency_in, DWORD *adjacency_out,
712 DWORD *face_remap, LPD3DXBUFFER *vertex_remap, LPD3DXMESH *opt_mesh)
714 ID3DXMeshImpl *This = impl_from_ID3DXMesh(iface);
715 HRESULT hr;
716 D3DVERTEXELEMENT9 declaration[MAX_FVF_DECL_SIZE] = { D3DDECL_END() };
717 ID3DXMesh *optimized_mesh;
719 TRACE("(%p)->(%x,%p,%p,%p,%p,%p)\n", This, flags, adjacency_in, adjacency_out, face_remap, vertex_remap, opt_mesh);
721 if (!opt_mesh)
722 return D3DERR_INVALIDCALL;
724 hr = iface->lpVtbl->GetDeclaration(iface, declaration);
725 if (FAILED(hr)) return hr;
727 hr = iface->lpVtbl->CloneMesh(iface, This->options, declaration, This->device, &optimized_mesh);
728 if (FAILED(hr)) return hr;
730 hr = optimized_mesh->lpVtbl->OptimizeInplace(optimized_mesh, flags, adjacency_in, adjacency_out, face_remap, vertex_remap);
731 if (SUCCEEDED(hr))
732 *opt_mesh = optimized_mesh;
733 else
734 IUnknown_Release(optimized_mesh);
735 return hr;
738 /* Creates a vertex_remap that removes unused vertices.
739 * Indices are updated according to the vertex_remap. */
740 static HRESULT compact_mesh(ID3DXMeshImpl *This, DWORD *indices, DWORD *new_num_vertices, ID3DXBuffer **vertex_remap)
742 HRESULT hr;
743 DWORD *vertex_remap_ptr;
744 DWORD num_used_vertices;
745 DWORD i;
747 hr = D3DXCreateBuffer(This->numvertices * sizeof(DWORD), vertex_remap);
748 if (FAILED(hr)) return hr;
749 vertex_remap_ptr = ID3DXBuffer_GetBufferPointer(*vertex_remap);
751 for (i = 0; i < This->numfaces * 3; i++)
752 vertex_remap_ptr[indices[i]] = 1;
754 /* create old->new vertex mapping */
755 num_used_vertices = 0;
756 for (i = 0; i < This->numvertices; i++) {
757 if (vertex_remap_ptr[i])
758 vertex_remap_ptr[i] = num_used_vertices++;
759 else
760 vertex_remap_ptr[i] = -1;
762 /* convert indices */
763 for (i = 0; i < This->numfaces * 3; i++)
764 indices[i] = vertex_remap_ptr[indices[i]];
766 /* create new->old vertex mapping */
767 num_used_vertices = 0;
768 for (i = 0; i < This->numvertices; i++) {
769 if (vertex_remap_ptr[i] != -1)
770 vertex_remap_ptr[num_used_vertices++] = i;
772 for (i = num_used_vertices; i < This->numvertices; i++)
773 vertex_remap_ptr[i] = -1;
775 *new_num_vertices = num_used_vertices;
777 return D3D_OK;
780 /* count the number of unique attribute values in a sorted attribute buffer */
781 static DWORD count_attributes(const DWORD *attrib_buffer, DWORD numfaces)
783 DWORD last_attribute = attrib_buffer[0];
784 DWORD attrib_table_size = 1;
785 DWORD i;
786 for (i = 1; i < numfaces; i++) {
787 if (attrib_buffer[i] != last_attribute) {
788 last_attribute = attrib_buffer[i];
789 attrib_table_size++;
792 return attrib_table_size;
795 static void fill_attribute_table(DWORD *attrib_buffer, DWORD numfaces, void *indices,
796 BOOL is_32bit_indices, D3DXATTRIBUTERANGE *attrib_table)
798 DWORD attrib_table_size = 0;
799 DWORD last_attribute = attrib_buffer[0];
800 DWORD min_vertex, max_vertex;
801 DWORD i;
803 attrib_table[0].AttribId = last_attribute;
804 attrib_table[0].FaceStart = 0;
805 min_vertex = (DWORD)-1;
806 max_vertex = 0;
807 for (i = 0; i < numfaces; i++) {
808 DWORD j;
810 if (attrib_buffer[i] != last_attribute) {
811 last_attribute = attrib_buffer[i];
812 attrib_table[attrib_table_size].FaceCount = i - attrib_table[attrib_table_size].FaceStart;
813 attrib_table[attrib_table_size].VertexStart = min_vertex;
814 attrib_table[attrib_table_size].VertexCount = max_vertex - min_vertex + 1;
815 attrib_table_size++;
816 attrib_table[attrib_table_size].AttribId = attrib_buffer[i];
817 attrib_table[attrib_table_size].FaceStart = i;
818 min_vertex = (DWORD)-1;
819 max_vertex = 0;
821 for (j = 0; j < 3; j++) {
822 DWORD vertex_index = is_32bit_indices ? ((DWORD*)indices)[i * 3 + j] : ((WORD*)indices)[i * 3 + j];
823 if (vertex_index < min_vertex)
824 min_vertex = vertex_index;
825 if (vertex_index > max_vertex)
826 max_vertex = vertex_index;
829 attrib_table[attrib_table_size].FaceCount = i - attrib_table[attrib_table_size].FaceStart;
830 attrib_table[attrib_table_size].VertexStart = min_vertex;
831 attrib_table[attrib_table_size].VertexCount = max_vertex - min_vertex + 1;
832 attrib_table_size++;
835 static int attrib_entry_compare(const DWORD **a, const DWORD **b)
837 const DWORD *ptr_a = *a;
838 const DWORD *ptr_b = *b;
839 int delta = *ptr_a - *ptr_b;
841 if (delta)
842 return delta;
844 delta = ptr_a - ptr_b; /* for stable sort */
845 return delta;
848 /* Create face_remap, a new attribute buffer for attribute sort optimization. */
849 static HRESULT remap_faces_for_attrsort(ID3DXMeshImpl *This, const DWORD *indices,
850 const DWORD *attrib_buffer, DWORD **sorted_attrib_buffer, DWORD **face_remap)
852 const DWORD **sorted_attrib_ptr_buffer = NULL;
853 DWORD i;
855 *face_remap = HeapAlloc(GetProcessHeap(), 0, This->numfaces * sizeof(**face_remap));
856 sorted_attrib_ptr_buffer = HeapAlloc(GetProcessHeap(), 0, This->numfaces * sizeof(*sorted_attrib_ptr_buffer));
857 if (!*face_remap || !sorted_attrib_ptr_buffer) {
858 HeapFree(GetProcessHeap(), 0, sorted_attrib_ptr_buffer);
859 return E_OUTOFMEMORY;
861 for (i = 0; i < This->numfaces; i++)
862 sorted_attrib_ptr_buffer[i] = &attrib_buffer[i];
863 qsort(sorted_attrib_ptr_buffer, This->numfaces, sizeof(*sorted_attrib_ptr_buffer),
864 (int(*)(const void *, const void *))attrib_entry_compare);
866 for (i = 0; i < This->numfaces; i++)
868 DWORD old_face = sorted_attrib_ptr_buffer[i] - attrib_buffer;
869 (*face_remap)[old_face] = i;
872 /* overwrite sorted_attrib_ptr_buffer with the values themselves */
873 *sorted_attrib_buffer = (DWORD*)sorted_attrib_ptr_buffer;
874 for (i = 0; i < This->numfaces; i++)
875 (*sorted_attrib_buffer)[(*face_remap)[i]] = attrib_buffer[i];
877 return D3D_OK;
880 static HRESULT WINAPI ID3DXMeshImpl_OptimizeInplace(ID3DXMesh *iface, DWORD flags, CONST DWORD *adjacency_in, DWORD *adjacency_out,
881 DWORD *face_remap_out, LPD3DXBUFFER *vertex_remap_out)
883 ID3DXMeshImpl *This = impl_from_ID3DXMesh(iface);
884 void *indices = NULL;
885 DWORD *attrib_buffer = NULL;
886 HRESULT hr;
887 ID3DXBuffer *vertex_remap = NULL;
888 DWORD *face_remap = NULL; /* old -> new mapping */
889 DWORD *dword_indices = NULL;
890 DWORD new_num_vertices = 0;
891 DWORD new_num_alloc_vertices = 0;
892 IDirect3DVertexBuffer9 *vertex_buffer = NULL;
893 DWORD *sorted_attrib_buffer = NULL;
894 DWORD i;
896 TRACE("(%p)->(%x,%p,%p,%p,%p)\n", This, flags, adjacency_in, adjacency_out, face_remap_out, vertex_remap_out);
898 if (!flags)
899 return D3DERR_INVALIDCALL;
900 if (!adjacency_in && (flags & (D3DXMESHOPT_VERTEXCACHE | D3DXMESHOPT_STRIPREORDER)))
901 return D3DERR_INVALIDCALL;
902 if ((flags & (D3DXMESHOPT_VERTEXCACHE | D3DXMESHOPT_STRIPREORDER)) == (D3DXMESHOPT_VERTEXCACHE | D3DXMESHOPT_STRIPREORDER))
903 return D3DERR_INVALIDCALL;
905 if (flags & (D3DXMESHOPT_VERTEXCACHE | D3DXMESHOPT_STRIPREORDER))
907 if (flags & D3DXMESHOPT_VERTEXCACHE)
908 FIXME("D3DXMESHOPT_VERTEXCACHE not implemented.\n");
909 if (flags & D3DXMESHOPT_STRIPREORDER)
910 FIXME("D3DXMESHOPT_STRIPREORDER not implemented.\n");
911 return E_NOTIMPL;
914 hr = iface->lpVtbl->LockIndexBuffer(iface, 0, (void**)&indices);
915 if (FAILED(hr)) goto cleanup;
917 dword_indices = HeapAlloc(GetProcessHeap(), 0, This->numfaces * 3 * sizeof(DWORD));
918 if (!dword_indices) return E_OUTOFMEMORY;
919 if (This->options & D3DXMESH_32BIT) {
920 memcpy(dword_indices, indices, This->numfaces * 3 * sizeof(DWORD));
921 } else {
922 WORD *word_indices = indices;
923 for (i = 0; i < This->numfaces * 3; i++)
924 dword_indices[i] = *word_indices++;
927 if ((flags & (D3DXMESHOPT_COMPACT | D3DXMESHOPT_IGNOREVERTS | D3DXMESHOPT_ATTRSORT)) == D3DXMESHOPT_COMPACT)
929 new_num_alloc_vertices = This->numvertices;
930 hr = compact_mesh(This, dword_indices, &new_num_vertices, &vertex_remap);
931 if (FAILED(hr)) goto cleanup;
932 } else if (flags & D3DXMESHOPT_ATTRSORT) {
933 if (!(flags & D3DXMESHOPT_IGNOREVERTS))
935 FIXME("D3DXMESHOPT_ATTRSORT vertex reordering not implemented.\n");
936 hr = E_NOTIMPL;
937 goto cleanup;
940 hr = iface->lpVtbl->LockAttributeBuffer(iface, 0, &attrib_buffer);
941 if (FAILED(hr)) goto cleanup;
943 hr = remap_faces_for_attrsort(This, dword_indices, attrib_buffer, &sorted_attrib_buffer, &face_remap);
944 if (FAILED(hr)) goto cleanup;
947 if (vertex_remap)
949 /* reorder the vertices using vertex_remap */
950 D3DVERTEXBUFFER_DESC vertex_desc;
951 DWORD *vertex_remap_ptr = ID3DXBuffer_GetBufferPointer(vertex_remap);
952 DWORD vertex_size = iface->lpVtbl->GetNumBytesPerVertex(iface);
953 BYTE *orig_vertices;
954 BYTE *new_vertices;
956 hr = IDirect3DVertexBuffer9_GetDesc(This->vertex_buffer, &vertex_desc);
957 if (FAILED(hr)) goto cleanup;
959 hr = IDirect3DDevice9_CreateVertexBuffer(This->device, new_num_alloc_vertices * vertex_size,
960 vertex_desc.Usage, This->fvf, vertex_desc.Pool, &vertex_buffer, NULL);
961 if (FAILED(hr)) goto cleanup;
963 hr = IDirect3DVertexBuffer9_Lock(This->vertex_buffer, 0, 0, (void**)&orig_vertices, D3DLOCK_READONLY);
964 if (FAILED(hr)) goto cleanup;
966 hr = IDirect3DVertexBuffer9_Lock(vertex_buffer, 0, 0, (void**)&new_vertices, D3DLOCK_DISCARD);
967 if (FAILED(hr)) {
968 IDirect3DVertexBuffer9_Unlock(This->vertex_buffer);
969 goto cleanup;
972 for (i = 0; i < new_num_vertices; i++)
973 memcpy(new_vertices + i * vertex_size, orig_vertices + vertex_remap_ptr[i] * vertex_size, vertex_size);
975 IDirect3DVertexBuffer9_Unlock(This->vertex_buffer);
976 IDirect3DVertexBuffer9_Unlock(vertex_buffer);
977 } else if (vertex_remap_out) {
978 DWORD *vertex_remap_ptr;
980 hr = D3DXCreateBuffer(This->numvertices * sizeof(DWORD), &vertex_remap);
981 if (FAILED(hr)) goto cleanup;
982 vertex_remap_ptr = ID3DXBuffer_GetBufferPointer(vertex_remap);
983 for (i = 0; i < This->numvertices; i++)
984 *vertex_remap_ptr++ = i;
987 if (flags & D3DXMESHOPT_ATTRSORT)
989 D3DXATTRIBUTERANGE *attrib_table;
990 DWORD attrib_table_size;
992 attrib_table_size = count_attributes(sorted_attrib_buffer, This->numfaces);
993 attrib_table = HeapAlloc(GetProcessHeap(), 0, attrib_table_size * sizeof(*attrib_table));
994 if (!attrib_table) {
995 hr = E_OUTOFMEMORY;
996 goto cleanup;
999 memcpy(attrib_buffer, sorted_attrib_buffer, This->numfaces * sizeof(*attrib_buffer));
1001 /* reorder the indices using face_remap */
1002 if (This->options & D3DXMESH_32BIT) {
1003 for (i = 0; i < This->numfaces; i++)
1004 memcpy((DWORD*)indices + face_remap[i] * 3, dword_indices + i * 3, 3 * sizeof(DWORD));
1005 } else {
1006 WORD *word_indices = indices;
1007 for (i = 0; i < This->numfaces; i++) {
1008 DWORD new_pos = face_remap[i] * 3;
1009 DWORD old_pos = i * 3;
1010 word_indices[new_pos++] = dword_indices[old_pos++];
1011 word_indices[new_pos++] = dword_indices[old_pos++];
1012 word_indices[new_pos] = dword_indices[old_pos];
1016 fill_attribute_table(attrib_buffer, This->numfaces, indices,
1017 This->options & D3DXMESH_32BIT, attrib_table);
1019 HeapFree(GetProcessHeap(), 0, This->attrib_table);
1020 This->attrib_table = attrib_table;
1021 This->attrib_table_size = attrib_table_size;
1022 } else {
1023 if (This->options & D3DXMESH_32BIT) {
1024 memcpy(indices, dword_indices, This->numfaces * 3 * sizeof(DWORD));
1025 } else {
1026 WORD *word_indices = indices;
1027 for (i = 0; i < This->numfaces * 3; i++)
1028 *word_indices++ = dword_indices[i];
1032 if (adjacency_out) {
1033 if (face_remap) {
1034 for (i = 0; i < This->numfaces; i++) {
1035 DWORD old_pos = i * 3;
1036 DWORD new_pos = face_remap[i] * 3;
1037 adjacency_out[new_pos++] = face_remap[adjacency_in[old_pos++]];
1038 adjacency_out[new_pos++] = face_remap[adjacency_in[old_pos++]];
1039 adjacency_out[new_pos++] = face_remap[adjacency_in[old_pos++]];
1041 } else {
1042 memcpy(adjacency_out, adjacency_in, This->numfaces * 3 * sizeof(*adjacency_out));
1045 if (face_remap_out) {
1046 if (face_remap) {
1047 for (i = 0; i < This->numfaces; i++)
1048 face_remap_out[face_remap[i]] = i;
1049 } else {
1050 for (i = 0; i < This->numfaces; i++)
1051 face_remap_out[i] = i;
1054 if (vertex_remap_out)
1055 *vertex_remap_out = vertex_remap;
1056 vertex_remap = NULL;
1058 if (vertex_buffer) {
1059 IDirect3DVertexBuffer9_Release(This->vertex_buffer);
1060 This->vertex_buffer = vertex_buffer;
1061 vertex_buffer = NULL;
1062 This->numvertices = new_num_vertices;
1065 hr = D3D_OK;
1066 cleanup:
1067 HeapFree(GetProcessHeap(), 0, sorted_attrib_buffer);
1068 HeapFree(GetProcessHeap(), 0, face_remap);
1069 HeapFree(GetProcessHeap(), 0, dword_indices);
1070 if (vertex_remap) ID3DXBuffer_Release(vertex_remap);
1071 if (vertex_buffer) IDirect3DVertexBuffer9_Release(vertex_buffer);
1072 if (attrib_buffer) iface->lpVtbl->UnlockAttributeBuffer(iface);
1073 if (indices) iface->lpVtbl->UnlockIndexBuffer(iface);
1074 return hr;
1077 static HRESULT WINAPI ID3DXMeshImpl_SetAttributeTable(ID3DXMesh *iface, CONST D3DXATTRIBUTERANGE *attrib_table, DWORD attrib_table_size)
1079 ID3DXMeshImpl *This = impl_from_ID3DXMesh(iface);
1080 D3DXATTRIBUTERANGE *new_table = NULL;
1082 TRACE("(%p)->(%p,%u)\n", This, attrib_table, attrib_table_size);
1084 if (attrib_table_size) {
1085 size_t size = attrib_table_size * sizeof(*attrib_table);
1087 new_table = HeapAlloc(GetProcessHeap(), 0, size);
1088 if (!new_table)
1089 return E_OUTOFMEMORY;
1091 CopyMemory(new_table, attrib_table, size);
1092 } else if (attrib_table) {
1093 return D3DERR_INVALIDCALL;
1095 HeapFree(GetProcessHeap(), 0, This->attrib_table);
1096 This->attrib_table = new_table;
1097 This->attrib_table_size = attrib_table_size;
1099 return D3D_OK;
1102 static const struct ID3DXMeshVtbl D3DXMesh_Vtbl =
1104 /*** IUnknown methods ***/
1105 ID3DXMeshImpl_QueryInterface,
1106 ID3DXMeshImpl_AddRef,
1107 ID3DXMeshImpl_Release,
1108 /*** ID3DXBaseMesh ***/
1109 ID3DXMeshImpl_DrawSubset,
1110 ID3DXMeshImpl_GetNumFaces,
1111 ID3DXMeshImpl_GetNumVertices,
1112 ID3DXMeshImpl_GetFVF,
1113 ID3DXMeshImpl_GetDeclaration,
1114 ID3DXMeshImpl_GetNumBytesPerVertex,
1115 ID3DXMeshImpl_GetOptions,
1116 ID3DXMeshImpl_GetDevice,
1117 ID3DXMeshImpl_CloneMeshFVF,
1118 ID3DXMeshImpl_CloneMesh,
1119 ID3DXMeshImpl_GetVertexBuffer,
1120 ID3DXMeshImpl_GetIndexBuffer,
1121 ID3DXMeshImpl_LockVertexBuffer,
1122 ID3DXMeshImpl_UnlockVertexBuffer,
1123 ID3DXMeshImpl_LockIndexBuffer,
1124 ID3DXMeshImpl_UnlockIndexBuffer,
1125 ID3DXMeshImpl_GetAttributeTable,
1126 ID3DXMeshImpl_ConvertPointRepsToAdjacency,
1127 ID3DXMeshImpl_ConvertAdjacencyToPointReps,
1128 ID3DXMeshImpl_GenerateAdjacency,
1129 ID3DXMeshImpl_UpdateSemantics,
1130 /*** ID3DXMesh ***/
1131 ID3DXMeshImpl_LockAttributeBuffer,
1132 ID3DXMeshImpl_UnlockAttributeBuffer,
1133 ID3DXMeshImpl_Optimize,
1134 ID3DXMeshImpl_OptimizeInplace,
1135 ID3DXMeshImpl_SetAttributeTable
1138 /*************************************************************************
1139 * D3DXBoxBoundProbe
1141 BOOL WINAPI D3DXBoxBoundProbe(CONST D3DXVECTOR3 *pmin, CONST D3DXVECTOR3 *pmax, CONST D3DXVECTOR3 *prayposition, CONST D3DXVECTOR3 *praydirection)
1143 /* Algorithm taken from the article: An Efficient and Robust Ray-Box Intersection Algoritm
1144 Amy Williams University of Utah
1145 Steve Barrus University of Utah
1146 R. Keith Morley University of Utah
1147 Peter Shirley University of Utah
1149 International Conference on Computer Graphics and Interactive Techniques archive
1150 ACM SIGGRAPH 2005 Courses
1151 Los Angeles, California
1153 This algorithm is free of patents or of copyrights, as confirmed by Peter Shirley himself.
1155 Algorithm: Consider the box as the intersection of three slabs. Clip the ray
1156 against each slab, if there's anything left of the ray after we're
1157 done we've got an intersection of the ray with the box.
1161 FLOAT div, tmin, tmax, tymin, tymax, tzmin, tzmax;
1163 div = 1.0f / praydirection->x;
1164 if ( div >= 0.0f )
1166 tmin = ( pmin->x - prayposition->x ) * div;
1167 tmax = ( pmax->x - prayposition->x ) * div;
1169 else
1171 tmin = ( pmax->x - prayposition->x ) * div;
1172 tmax = ( pmin->x - prayposition->x ) * div;
1175 if ( tmax < 0.0f ) return FALSE;
1177 div = 1.0f / praydirection->y;
1178 if ( div >= 0.0f )
1180 tymin = ( pmin->y - prayposition->y ) * div;
1181 tymax = ( pmax->y - prayposition->y ) * div;
1183 else
1185 tymin = ( pmax->y - prayposition->y ) * div;
1186 tymax = ( pmin->y - prayposition->y ) * div;
1189 if ( ( tymax < 0.0f ) || ( tmin > tymax ) || ( tymin > tmax ) ) return FALSE;
1191 if ( tymin > tmin ) tmin = tymin;
1192 if ( tymax < tmax ) tmax = tymax;
1194 div = 1.0f / praydirection->z;
1195 if ( div >= 0.0f )
1197 tzmin = ( pmin->z - prayposition->z ) * div;
1198 tzmax = ( pmax->z - prayposition->z ) * div;
1200 else
1202 tzmin = ( pmax->z - prayposition->z ) * div;
1203 tzmax = ( pmin->z - prayposition->z ) * div;
1206 if ( (tzmax < 0.0f ) || ( tmin > tzmax ) || ( tzmin > tmax ) ) return FALSE;
1208 return TRUE;
1211 /*************************************************************************
1212 * D3DXComputeBoundingBox
1214 HRESULT WINAPI D3DXComputeBoundingBox(CONST D3DXVECTOR3 *pfirstposition, DWORD numvertices, DWORD dwstride, D3DXVECTOR3 *pmin, D3DXVECTOR3 *pmax)
1216 D3DXVECTOR3 vec;
1217 unsigned int i;
1219 if( !pfirstposition || !pmin || !pmax ) return D3DERR_INVALIDCALL;
1221 *pmin = *pfirstposition;
1222 *pmax = *pmin;
1224 for(i=0; i<numvertices; i++)
1226 vec = *( (const D3DXVECTOR3*)((const char*)pfirstposition + dwstride * i) );
1228 if ( vec.x < pmin->x ) pmin->x = vec.x;
1229 if ( vec.x > pmax->x ) pmax->x = vec.x;
1231 if ( vec.y < pmin->y ) pmin->y = vec.y;
1232 if ( vec.y > pmax->y ) pmax->y = vec.y;
1234 if ( vec.z < pmin->z ) pmin->z = vec.z;
1235 if ( vec.z > pmax->z ) pmax->z = vec.z;
1238 return D3D_OK;
1241 /*************************************************************************
1242 * D3DXComputeBoundingSphere
1244 HRESULT WINAPI D3DXComputeBoundingSphere(CONST D3DXVECTOR3* pfirstposition, DWORD numvertices, DWORD dwstride, D3DXVECTOR3 *pcenter, FLOAT *pradius)
1246 D3DXVECTOR3 temp, temp1;
1247 FLOAT d;
1248 unsigned int i;
1250 if( !pfirstposition || !pcenter || !pradius ) return D3DERR_INVALIDCALL;
1252 temp.x = 0.0f;
1253 temp.y = 0.0f;
1254 temp.z = 0.0f;
1255 temp1 = temp;
1256 *pradius = 0.0f;
1258 for(i=0; i<numvertices; i++)
1260 D3DXVec3Add(&temp1, &temp, (const D3DXVECTOR3*)((const char*)pfirstposition + dwstride * i));
1261 temp = temp1;
1264 D3DXVec3Scale(pcenter, &temp, 1.0f/((FLOAT)numvertices));
1266 for(i=0; i<numvertices; i++)
1268 d = D3DXVec3Length(D3DXVec3Subtract(&temp, (const D3DXVECTOR3*)((const char*)pfirstposition + dwstride * i), pcenter));
1269 if ( d > *pradius ) *pradius = d;
1271 return D3D_OK;
1274 static const UINT d3dx_decltype_size[D3DDECLTYPE_UNUSED] =
1276 /* D3DDECLTYPE_FLOAT1 */ 1 * 4,
1277 /* D3DDECLTYPE_FLOAT2 */ 2 * 4,
1278 /* D3DDECLTYPE_FLOAT3 */ 3 * 4,
1279 /* D3DDECLTYPE_FLOAT4 */ 4 * 4,
1280 /* D3DDECLTYPE_D3DCOLOR */ 4 * 1,
1281 /* D3DDECLTYPE_UBYTE4 */ 4 * 1,
1282 /* D3DDECLTYPE_SHORT2 */ 2 * 2,
1283 /* D3DDECLTYPE_SHORT4 */ 4 * 2,
1284 /* D3DDECLTYPE_UBYTE4N */ 4 * 1,
1285 /* D3DDECLTYPE_SHORT2N */ 2 * 2,
1286 /* D3DDECLTYPE_SHORT4N */ 4 * 2,
1287 /* D3DDECLTYPE_USHORT2N */ 2 * 2,
1288 /* D3DDECLTYPE_USHORT4N */ 4 * 2,
1289 /* D3DDECLTYPE_UDEC3 */ 4, /* 3 * 10 bits + 2 padding */
1290 /* D3DDECLTYPE_DEC3N */ 4,
1291 /* D3DDECLTYPE_FLOAT16_2 */ 2 * 2,
1292 /* D3DDECLTYPE_FLOAT16_4 */ 4 * 2,
1295 static void append_decl_element(D3DVERTEXELEMENT9 *declaration, UINT *idx, UINT *offset,
1296 D3DDECLTYPE type, D3DDECLUSAGE usage, UINT usage_idx)
1298 declaration[*idx].Stream = 0;
1299 declaration[*idx].Offset = *offset;
1300 declaration[*idx].Type = type;
1301 declaration[*idx].Method = D3DDECLMETHOD_DEFAULT;
1302 declaration[*idx].Usage = usage;
1303 declaration[*idx].UsageIndex = usage_idx;
1305 *offset += d3dx_decltype_size[type];
1306 ++(*idx);
1309 /*************************************************************************
1310 * D3DXDeclaratorFromFVF
1312 HRESULT WINAPI D3DXDeclaratorFromFVF(DWORD fvf, D3DVERTEXELEMENT9 declaration[MAX_FVF_DECL_SIZE])
1314 static const D3DVERTEXELEMENT9 end_element = D3DDECL_END();
1315 DWORD tex_count = (fvf & D3DFVF_TEXCOUNT_MASK) >> D3DFVF_TEXCOUNT_SHIFT;
1316 unsigned int offset = 0;
1317 unsigned int idx = 0;
1318 unsigned int i;
1320 TRACE("fvf %#x, declaration %p.\n", fvf, declaration);
1322 if (fvf & (D3DFVF_RESERVED0 | D3DFVF_RESERVED2)) return D3DERR_INVALIDCALL;
1324 if (fvf & D3DFVF_POSITION_MASK)
1326 BOOL has_blend = (fvf & D3DFVF_XYZB5) >= D3DFVF_XYZB1;
1327 DWORD blend_count = 1 + (((fvf & D3DFVF_XYZB5) - D3DFVF_XYZB1) >> 1);
1328 BOOL has_blend_idx = (fvf & D3DFVF_LASTBETA_D3DCOLOR) || (fvf & D3DFVF_LASTBETA_UBYTE4);
1330 if (has_blend_idx) --blend_count;
1332 if ((fvf & D3DFVF_POSITION_MASK) == D3DFVF_XYZW
1333 || (has_blend && blend_count > 4))
1334 return D3DERR_INVALIDCALL;
1336 if ((fvf & D3DFVF_POSITION_MASK) == D3DFVF_XYZRHW)
1337 append_decl_element(declaration, &idx, &offset, D3DDECLTYPE_FLOAT4, D3DDECLUSAGE_POSITIONT, 0);
1338 else
1339 append_decl_element(declaration, &idx, &offset, D3DDECLTYPE_FLOAT3, D3DDECLUSAGE_POSITION, 0);
1341 if (has_blend)
1343 switch (blend_count)
1345 case 0:
1346 break;
1347 case 1:
1348 append_decl_element(declaration, &idx, &offset, D3DDECLTYPE_FLOAT1, D3DDECLUSAGE_BLENDWEIGHT, 0);
1349 break;
1350 case 2:
1351 append_decl_element(declaration, &idx, &offset, D3DDECLTYPE_FLOAT2, D3DDECLUSAGE_BLENDWEIGHT, 0);
1352 break;
1353 case 3:
1354 append_decl_element(declaration, &idx, &offset, D3DDECLTYPE_FLOAT3, D3DDECLUSAGE_BLENDWEIGHT, 0);
1355 break;
1356 case 4:
1357 append_decl_element(declaration, &idx, &offset, D3DDECLTYPE_FLOAT4, D3DDECLUSAGE_BLENDWEIGHT, 0);
1358 break;
1359 default:
1360 ERR("Invalid blend count %u.\n", blend_count);
1361 break;
1364 if (has_blend_idx)
1366 if (fvf & D3DFVF_LASTBETA_UBYTE4)
1367 append_decl_element(declaration, &idx, &offset, D3DDECLTYPE_UBYTE4, D3DDECLUSAGE_BLENDINDICES, 0);
1368 else if (fvf & D3DFVF_LASTBETA_D3DCOLOR)
1369 append_decl_element(declaration, &idx, &offset, D3DDECLTYPE_D3DCOLOR, D3DDECLUSAGE_BLENDINDICES, 0);
1374 if (fvf & D3DFVF_NORMAL)
1375 append_decl_element(declaration, &idx, &offset, D3DDECLTYPE_FLOAT3, D3DDECLUSAGE_NORMAL, 0);
1376 if (fvf & D3DFVF_PSIZE)
1377 append_decl_element(declaration, &idx, &offset, D3DDECLTYPE_FLOAT1, D3DDECLUSAGE_PSIZE, 0);
1378 if (fvf & D3DFVF_DIFFUSE)
1379 append_decl_element(declaration, &idx, &offset, D3DDECLTYPE_D3DCOLOR, D3DDECLUSAGE_COLOR, 0);
1380 if (fvf & D3DFVF_SPECULAR)
1381 append_decl_element(declaration, &idx, &offset, D3DDECLTYPE_D3DCOLOR, D3DDECLUSAGE_COLOR, 1);
1383 for (i = 0; i < tex_count; ++i)
1385 switch ((fvf >> (16 + 2 * i)) & 0x03)
1387 case D3DFVF_TEXTUREFORMAT1:
1388 append_decl_element(declaration, &idx, &offset, D3DDECLTYPE_FLOAT1, D3DDECLUSAGE_TEXCOORD, i);
1389 break;
1390 case D3DFVF_TEXTUREFORMAT2:
1391 append_decl_element(declaration, &idx, &offset, D3DDECLTYPE_FLOAT2, D3DDECLUSAGE_TEXCOORD, i);
1392 break;
1393 case D3DFVF_TEXTUREFORMAT3:
1394 append_decl_element(declaration, &idx, &offset, D3DDECLTYPE_FLOAT3, D3DDECLUSAGE_TEXCOORD, i);
1395 break;
1396 case D3DFVF_TEXTUREFORMAT4:
1397 append_decl_element(declaration, &idx, &offset, D3DDECLTYPE_FLOAT4, D3DDECLUSAGE_TEXCOORD, i);
1398 break;
1402 declaration[idx] = end_element;
1404 return D3D_OK;
1407 /*************************************************************************
1408 * D3DXFVFFromDeclarator
1410 HRESULT WINAPI D3DXFVFFromDeclarator(const D3DVERTEXELEMENT9 *declaration, DWORD *fvf)
1412 unsigned int i = 0, texture, offset;
1414 TRACE("(%p, %p)\n", declaration, fvf);
1416 *fvf = 0;
1417 if (declaration[0].Type == D3DDECLTYPE_FLOAT3 && declaration[0].Usage == D3DDECLUSAGE_POSITION)
1419 if ((declaration[1].Type == D3DDECLTYPE_FLOAT4 && declaration[1].Usage == D3DDECLUSAGE_BLENDWEIGHT &&
1420 declaration[1].UsageIndex == 0) &&
1421 (declaration[2].Type == D3DDECLTYPE_FLOAT1 && declaration[2].Usage == D3DDECLUSAGE_BLENDINDICES &&
1422 declaration[2].UsageIndex == 0))
1424 return D3DERR_INVALIDCALL;
1426 else if ((declaration[1].Type == D3DDECLTYPE_UBYTE4 || declaration[1].Type == D3DDECLTYPE_D3DCOLOR) &&
1427 declaration[1].Usage == D3DDECLUSAGE_BLENDINDICES && declaration[1].UsageIndex == 0)
1429 if (declaration[1].Type == D3DDECLTYPE_UBYTE4)
1431 *fvf |= D3DFVF_XYZB1 | D3DFVF_LASTBETA_UBYTE4;
1433 else
1435 *fvf |= D3DFVF_XYZB1 | D3DFVF_LASTBETA_D3DCOLOR;
1437 i = 2;
1439 else if (declaration[1].Type <= D3DDECLTYPE_FLOAT4 && declaration[1].Usage == D3DDECLUSAGE_BLENDWEIGHT &&
1440 declaration[1].UsageIndex == 0)
1442 if ((declaration[2].Type == D3DDECLTYPE_UBYTE4 || declaration[2].Type == D3DDECLTYPE_D3DCOLOR) &&
1443 declaration[2].Usage == D3DDECLUSAGE_BLENDINDICES && declaration[2].UsageIndex == 0)
1445 if (declaration[2].Type == D3DDECLTYPE_UBYTE4)
1447 *fvf |= D3DFVF_LASTBETA_UBYTE4;
1449 else
1451 *fvf |= D3DFVF_LASTBETA_D3DCOLOR;
1453 switch (declaration[1].Type)
1455 case D3DDECLTYPE_FLOAT1: *fvf |= D3DFVF_XYZB2; break;
1456 case D3DDECLTYPE_FLOAT2: *fvf |= D3DFVF_XYZB3; break;
1457 case D3DDECLTYPE_FLOAT3: *fvf |= D3DFVF_XYZB4; break;
1458 case D3DDECLTYPE_FLOAT4: *fvf |= D3DFVF_XYZB5; break;
1460 i = 3;
1462 else
1464 switch (declaration[1].Type)
1466 case D3DDECLTYPE_FLOAT1: *fvf |= D3DFVF_XYZB1; break;
1467 case D3DDECLTYPE_FLOAT2: *fvf |= D3DFVF_XYZB2; break;
1468 case D3DDECLTYPE_FLOAT3: *fvf |= D3DFVF_XYZB3; break;
1469 case D3DDECLTYPE_FLOAT4: *fvf |= D3DFVF_XYZB4; break;
1471 i = 2;
1474 else
1476 *fvf |= D3DFVF_XYZ;
1477 i = 1;
1480 else if (declaration[0].Type == D3DDECLTYPE_FLOAT4 && declaration[0].Usage == D3DDECLUSAGE_POSITIONT &&
1481 declaration[0].UsageIndex == 0)
1483 *fvf |= D3DFVF_XYZRHW;
1484 i = 1;
1487 if (declaration[i].Type == D3DDECLTYPE_FLOAT3 && declaration[i].Usage == D3DDECLUSAGE_NORMAL)
1489 *fvf |= D3DFVF_NORMAL;
1490 i++;
1492 if (declaration[i].Type == D3DDECLTYPE_FLOAT1 && declaration[i].Usage == D3DDECLUSAGE_PSIZE &&
1493 declaration[i].UsageIndex == 0)
1495 *fvf |= D3DFVF_PSIZE;
1496 i++;
1498 if (declaration[i].Type == D3DDECLTYPE_D3DCOLOR && declaration[i].Usage == D3DDECLUSAGE_COLOR &&
1499 declaration[i].UsageIndex == 0)
1501 *fvf |= D3DFVF_DIFFUSE;
1502 i++;
1504 if (declaration[i].Type == D3DDECLTYPE_D3DCOLOR && declaration[i].Usage == D3DDECLUSAGE_COLOR &&
1505 declaration[i].UsageIndex == 1)
1507 *fvf |= D3DFVF_SPECULAR;
1508 i++;
1511 for (texture = 0; texture < D3DDP_MAXTEXCOORD; i++, texture++)
1513 if (declaration[i].Stream == 0xFF)
1515 break;
1517 else if (declaration[i].Type == D3DDECLTYPE_FLOAT1 && declaration[i].Usage == D3DDECLUSAGE_TEXCOORD &&
1518 declaration[i].UsageIndex == texture)
1520 *fvf |= D3DFVF_TEXCOORDSIZE1(declaration[i].UsageIndex);
1522 else if (declaration[i].Type == D3DDECLTYPE_FLOAT2 && declaration[i].Usage == D3DDECLUSAGE_TEXCOORD &&
1523 declaration[i].UsageIndex == texture)
1525 *fvf |= D3DFVF_TEXCOORDSIZE2(declaration[i].UsageIndex);
1527 else if (declaration[i].Type == D3DDECLTYPE_FLOAT3 && declaration[i].Usage == D3DDECLUSAGE_TEXCOORD &&
1528 declaration[i].UsageIndex == texture)
1530 *fvf |= D3DFVF_TEXCOORDSIZE3(declaration[i].UsageIndex);
1532 else if (declaration[i].Type == D3DDECLTYPE_FLOAT4 && declaration[i].Usage == D3DDECLUSAGE_TEXCOORD &&
1533 declaration[i].UsageIndex == texture)
1535 *fvf |= D3DFVF_TEXCOORDSIZE4(declaration[i].UsageIndex);
1537 else
1539 return D3DERR_INVALIDCALL;
1543 *fvf |= (texture << D3DFVF_TEXCOUNT_SHIFT);
1545 for (offset = 0, i = 0; declaration[i].Stream != 0xFF;
1546 offset += d3dx_decltype_size[declaration[i].Type], i++)
1548 if (declaration[i].Offset != offset)
1550 return D3DERR_INVALIDCALL;
1554 return D3D_OK;
1557 /*************************************************************************
1558 * D3DXGetFVFVertexSize
1560 static UINT Get_TexCoord_Size_From_FVF(DWORD FVF, int tex_num)
1562 return (((((FVF) >> (16 + (2 * (tex_num)))) + 1) & 0x03) + 1);
1565 UINT WINAPI D3DXGetFVFVertexSize(DWORD FVF)
1567 DWORD size = 0;
1568 UINT i;
1569 UINT numTextures = (FVF & D3DFVF_TEXCOUNT_MASK) >> D3DFVF_TEXCOUNT_SHIFT;
1571 if (FVF & D3DFVF_NORMAL) size += sizeof(D3DXVECTOR3);
1572 if (FVF & D3DFVF_DIFFUSE) size += sizeof(DWORD);
1573 if (FVF & D3DFVF_SPECULAR) size += sizeof(DWORD);
1574 if (FVF & D3DFVF_PSIZE) size += sizeof(DWORD);
1576 switch (FVF & D3DFVF_POSITION_MASK)
1578 case D3DFVF_XYZ: size += sizeof(D3DXVECTOR3); break;
1579 case D3DFVF_XYZRHW: size += 4 * sizeof(FLOAT); break;
1580 case D3DFVF_XYZB1: size += 4 * sizeof(FLOAT); break;
1581 case D3DFVF_XYZB2: size += 5 * sizeof(FLOAT); break;
1582 case D3DFVF_XYZB3: size += 6 * sizeof(FLOAT); break;
1583 case D3DFVF_XYZB4: size += 7 * sizeof(FLOAT); break;
1584 case D3DFVF_XYZB5: size += 8 * sizeof(FLOAT); break;
1585 case D3DFVF_XYZW: size += 4 * sizeof(FLOAT); break;
1588 for (i = 0; i < numTextures; i++)
1590 size += Get_TexCoord_Size_From_FVF(FVF, i) * sizeof(FLOAT);
1593 return size;
1596 /*************************************************************************
1597 * D3DXGetDeclVertexSize
1599 UINT WINAPI D3DXGetDeclVertexSize(const D3DVERTEXELEMENT9 *decl, DWORD stream_idx)
1601 const D3DVERTEXELEMENT9 *element;
1602 UINT size = 0;
1604 TRACE("decl %p, stream_idx %u\n", decl, stream_idx);
1606 if (!decl) return 0;
1608 for (element = decl; element->Stream != 0xff; ++element)
1610 UINT type_size;
1612 if (element->Stream != stream_idx) continue;
1614 if (element->Type >= sizeof(d3dx_decltype_size) / sizeof(*d3dx_decltype_size))
1616 FIXME("Unhandled element type %#x, size will be incorrect.\n", element->Type);
1617 continue;
1620 type_size = d3dx_decltype_size[element->Type];
1621 if (element->Offset + type_size > size) size = element->Offset + type_size;
1624 return size;
1627 /*************************************************************************
1628 * D3DXGetDeclLength
1630 UINT WINAPI D3DXGetDeclLength(const D3DVERTEXELEMENT9 *decl)
1632 const D3DVERTEXELEMENT9 *element;
1634 TRACE("decl %p\n", decl);
1636 /* null decl results in exception on Windows XP */
1638 for (element = decl; element->Stream != 0xff; ++element);
1640 return element - decl;
1643 /*************************************************************************
1644 * D3DXIntersectTri
1646 BOOL WINAPI D3DXIntersectTri(CONST D3DXVECTOR3 *p0, CONST D3DXVECTOR3 *p1, CONST D3DXVECTOR3 *p2, CONST D3DXVECTOR3 *praypos, CONST D3DXVECTOR3 *praydir, FLOAT *pu, FLOAT *pv, FLOAT *pdist)
1648 D3DXMATRIX m;
1649 D3DXVECTOR4 vec;
1651 m.u.m[0][0] = p1->x - p0->x;
1652 m.u.m[1][0] = p2->x - p0->x;
1653 m.u.m[2][0] = -praydir->x;
1654 m.u.m[3][0] = 0.0f;
1655 m.u.m[0][1] = p1->y - p0->z;
1656 m.u.m[1][1] = p2->y - p0->z;
1657 m.u.m[2][1] = -praydir->y;
1658 m.u.m[3][1] = 0.0f;
1659 m.u.m[0][2] = p1->z - p0->z;
1660 m.u.m[1][2] = p2->z - p0->z;
1661 m.u.m[2][2] = -praydir->z;
1662 m.u.m[3][2] = 0.0f;
1663 m.u.m[0][3] = 0.0f;
1664 m.u.m[1][3] = 0.0f;
1665 m.u.m[2][3] = 0.0f;
1666 m.u.m[3][3] = 1.0f;
1668 vec.x = praypos->x - p0->x;
1669 vec.y = praypos->y - p0->y;
1670 vec.z = praypos->z - p0->z;
1671 vec.w = 0.0f;
1673 if ( D3DXMatrixInverse(&m, NULL, &m) )
1675 D3DXVec4Transform(&vec, &vec, &m);
1676 if ( (vec.x >= 0.0f) && (vec.y >= 0.0f) && (vec.x + vec.y <= 1.0f) && (vec.z >= 0.0f) )
1678 *pu = vec.x;
1679 *pv = vec.y;
1680 *pdist = fabs( vec.z );
1681 return TRUE;
1685 return FALSE;
1688 /*************************************************************************
1689 * D3DXSphereBoundProbe
1691 BOOL WINAPI D3DXSphereBoundProbe(CONST D3DXVECTOR3 *pcenter, FLOAT radius, CONST D3DXVECTOR3 *prayposition, CONST D3DXVECTOR3 *praydirection)
1693 D3DXVECTOR3 difference;
1694 FLOAT a, b, c, d;
1696 a = D3DXVec3LengthSq(praydirection);
1697 if (!D3DXVec3Subtract(&difference, prayposition, pcenter)) return FALSE;
1698 b = D3DXVec3Dot(&difference, praydirection);
1699 c = D3DXVec3LengthSq(&difference) - radius * radius;
1700 d = b * b - a * c;
1702 if ( ( d <= 0.0f ) || ( sqrt(d) <= b ) ) return FALSE;
1703 return TRUE;
1706 /*************************************************************************
1707 * D3DXCreateMesh
1709 HRESULT WINAPI D3DXCreateMesh(DWORD numfaces, DWORD numvertices, DWORD options, CONST D3DVERTEXELEMENT9 *declaration,
1710 LPDIRECT3DDEVICE9 device, LPD3DXMESH *mesh)
1712 HRESULT hr;
1713 DWORD fvf;
1714 IDirect3DVertexDeclaration9 *vertex_declaration;
1715 UINT vertex_declaration_size;
1716 UINT num_elem;
1717 IDirect3DVertexBuffer9 *vertex_buffer;
1718 IDirect3DIndexBuffer9 *index_buffer;
1719 DWORD *attrib_buffer;
1720 ID3DXMeshImpl *object;
1721 DWORD index_usage = 0;
1722 D3DPOOL index_pool = D3DPOOL_DEFAULT;
1723 D3DFORMAT index_format = D3DFMT_INDEX16;
1724 DWORD vertex_usage = 0;
1725 D3DPOOL vertex_pool = D3DPOOL_DEFAULT;
1726 int i;
1728 TRACE("(%d, %d, %x, %p, %p, %p)\n", numfaces, numvertices, options, declaration, device, mesh);
1730 if (numfaces == 0 || numvertices == 0 || declaration == NULL || device == NULL || mesh == NULL ||
1731 /* D3DXMESH_VB_SHARE is for cloning, and D3DXMESH_USEHWONLY is for ConvertToBlendedMesh */
1732 (options & (D3DXMESH_VB_SHARE | D3DXMESH_USEHWONLY | 0xfffe0000)))
1734 return D3DERR_INVALIDCALL;
1736 for (i = 0; declaration[i].Stream != 0xff; i++)
1737 if (declaration[i].Stream != 0)
1738 return D3DERR_INVALIDCALL;
1739 num_elem = i + 1;
1741 if (options & D3DXMESH_32BIT)
1742 index_format = D3DFMT_INDEX32;
1744 if (options & D3DXMESH_DONOTCLIP) {
1745 index_usage |= D3DUSAGE_DONOTCLIP;
1746 vertex_usage |= D3DUSAGE_DONOTCLIP;
1748 if (options & D3DXMESH_POINTS) {
1749 index_usage |= D3DUSAGE_POINTS;
1750 vertex_usage |= D3DUSAGE_POINTS;
1752 if (options & D3DXMESH_RTPATCHES) {
1753 index_usage |= D3DUSAGE_RTPATCHES;
1754 vertex_usage |= D3DUSAGE_RTPATCHES;
1756 if (options & D3DXMESH_NPATCHES) {
1757 index_usage |= D3DUSAGE_NPATCHES;
1758 vertex_usage |= D3DUSAGE_NPATCHES;
1761 if (options & D3DXMESH_VB_SYSTEMMEM)
1762 vertex_pool = D3DPOOL_SYSTEMMEM;
1763 else if (options & D3DXMESH_VB_MANAGED)
1764 vertex_pool = D3DPOOL_MANAGED;
1766 if (options & D3DXMESH_VB_WRITEONLY)
1767 vertex_usage |= D3DUSAGE_WRITEONLY;
1768 if (options & D3DXMESH_VB_DYNAMIC)
1769 vertex_usage |= D3DUSAGE_DYNAMIC;
1770 if (options & D3DXMESH_VB_SOFTWAREPROCESSING)
1771 vertex_usage |= D3DUSAGE_SOFTWAREPROCESSING;
1773 if (options & D3DXMESH_IB_SYSTEMMEM)
1774 index_pool = D3DPOOL_SYSTEMMEM;
1775 else if (options & D3DXMESH_IB_MANAGED)
1776 index_pool = D3DPOOL_MANAGED;
1778 if (options & D3DXMESH_IB_WRITEONLY)
1779 index_usage |= D3DUSAGE_WRITEONLY;
1780 if (options & D3DXMESH_IB_DYNAMIC)
1781 index_usage |= D3DUSAGE_DYNAMIC;
1782 if (options & D3DXMESH_IB_SOFTWAREPROCESSING)
1783 index_usage |= D3DUSAGE_SOFTWAREPROCESSING;
1785 hr = D3DXFVFFromDeclarator(declaration, &fvf);
1786 if (hr != D3D_OK)
1788 fvf = 0;
1791 /* Create vertex declaration */
1792 hr = IDirect3DDevice9_CreateVertexDeclaration(device,
1793 declaration,
1794 &vertex_declaration);
1795 if (FAILED(hr))
1797 WARN("Unexpected return value %x from IDirect3DDevice9_CreateVertexDeclaration.\n",hr);
1798 return hr;
1800 vertex_declaration_size = D3DXGetDeclVertexSize(declaration, declaration[0].Stream);
1802 /* Create vertex buffer */
1803 hr = IDirect3DDevice9_CreateVertexBuffer(device,
1804 numvertices * vertex_declaration_size,
1805 vertex_usage,
1806 fvf,
1807 vertex_pool,
1808 &vertex_buffer,
1809 NULL);
1810 if (FAILED(hr))
1812 WARN("Unexpected return value %x from IDirect3DDevice9_CreateVertexBuffer.\n",hr);
1813 IDirect3DVertexDeclaration9_Release(vertex_declaration);
1814 return hr;
1817 /* Create index buffer */
1818 hr = IDirect3DDevice9_CreateIndexBuffer(device,
1819 numfaces * 3 * ((index_format == D3DFMT_INDEX16) ? 2 : 4),
1820 index_usage,
1821 index_format,
1822 index_pool,
1823 &index_buffer,
1824 NULL);
1825 if (FAILED(hr))
1827 WARN("Unexpected return value %x from IDirect3DDevice9_CreateVertexBuffer.\n",hr);
1828 IDirect3DVertexBuffer9_Release(vertex_buffer);
1829 IDirect3DVertexDeclaration9_Release(vertex_declaration);
1830 return hr;
1833 attrib_buffer = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, numfaces * sizeof(*attrib_buffer));
1834 object = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(ID3DXMeshImpl));
1835 if (object == NULL || attrib_buffer == NULL)
1837 HeapFree(GetProcessHeap(), 0, attrib_buffer);
1838 IDirect3DIndexBuffer9_Release(index_buffer);
1839 IDirect3DVertexBuffer9_Release(vertex_buffer);
1840 IDirect3DVertexDeclaration9_Release(vertex_declaration);
1841 *mesh = NULL;
1842 return E_OUTOFMEMORY;
1844 object->ID3DXMesh_iface.lpVtbl = &D3DXMesh_Vtbl;
1845 object->ref = 1;
1847 object->numfaces = numfaces;
1848 object->numvertices = numvertices;
1849 object->options = options;
1850 object->fvf = fvf;
1851 object->device = device;
1852 IDirect3DDevice9_AddRef(device);
1854 copy_declaration(object->cached_declaration, declaration, num_elem);
1855 object->vertex_declaration = vertex_declaration;
1856 object->vertex_declaration_size = vertex_declaration_size;
1857 object->num_elem = num_elem;
1858 object->vertex_buffer = vertex_buffer;
1859 object->index_buffer = index_buffer;
1860 object->attrib_buffer = attrib_buffer;
1862 *mesh = &object->ID3DXMesh_iface;
1864 return D3D_OK;
1867 /*************************************************************************
1868 * D3DXCreateMeshFVF
1870 HRESULT WINAPI D3DXCreateMeshFVF(DWORD numfaces, DWORD numvertices, DWORD options, DWORD fvf,
1871 LPDIRECT3DDEVICE9 device, LPD3DXMESH *mesh)
1873 HRESULT hr;
1874 D3DVERTEXELEMENT9 declaration[MAX_FVF_DECL_SIZE];
1876 TRACE("(%u, %u, %u, %u, %p, %p)\n", numfaces, numvertices, options, fvf, device, mesh);
1878 hr = D3DXDeclaratorFromFVF(fvf, declaration);
1879 if (FAILED(hr)) return hr;
1881 return D3DXCreateMesh(numfaces, numvertices, options, declaration, device, mesh);
1885 struct mesh_data {
1886 DWORD num_vertices;
1887 DWORD num_poly_faces;
1888 DWORD num_tri_faces;
1889 D3DXVECTOR3 *vertices;
1890 DWORD *num_tri_per_face;
1891 DWORD *indices;
1893 DWORD fvf;
1895 /* optional mesh data */
1897 DWORD num_normals;
1898 D3DXVECTOR3 *normals;
1899 DWORD *normal_indices;
1901 D3DXVECTOR2 *tex_coords;
1903 DWORD *vertex_colors;
1905 DWORD num_materials;
1906 D3DXMATERIAL *materials;
1907 DWORD *material_indices;
1910 static HRESULT get_next_child(IDirectXFileData *filedata, IDirectXFileData **child, const GUID **type)
1912 HRESULT hr;
1913 IDirectXFileDataReference *child_ref = NULL;
1914 IDirectXFileObject *child_obj = NULL;
1915 IDirectXFileData *child_data = NULL;
1917 hr = IDirectXFileData_GetNextObject(filedata, &child_obj);
1918 if (FAILED(hr)) return hr;
1920 hr = IDirectXFileObject_QueryInterface(child_obj, &IID_IDirectXFileDataReference, (void**)&child_ref);
1921 if (SUCCEEDED(hr)) {
1922 hr = IDirectXFileDataReference_Resolve(child_ref, &child_data);
1923 IDirectXFileDataReference_Release(child_ref);
1924 } else {
1925 hr = IDirectXFileObject_QueryInterface(child_obj, &IID_IDirectXFileData, (void**)&child_data);
1927 IDirectXFileObject_Release(child_obj);
1928 if (FAILED(hr))
1929 return hr;
1931 hr = IDirectXFileData_GetType(child_data, type);
1932 if (FAILED(hr)) {
1933 IDirectXFileData_Release(child_data);
1934 } else {
1935 *child = child_data;
1938 return hr;
1941 static HRESULT parse_texture_filename(IDirectXFileData *filedata, LPSTR *filename_out)
1943 HRESULT hr;
1944 DWORD data_size;
1945 BYTE *data;
1946 char *filename_in;
1947 char *filename = NULL;
1949 /* template TextureFilename {
1950 * STRING filename;
1954 HeapFree(GetProcessHeap(), 0, *filename_out);
1955 *filename_out = NULL;
1957 hr = IDirectXFileData_GetData(filedata, NULL, &data_size, (void**)&data);
1958 if (FAILED(hr)) return hr;
1960 if (data_size < sizeof(LPSTR)) {
1961 WARN("truncated data (%u bytes)\n", data_size);
1962 return E_FAIL;
1964 filename_in = *(LPSTR*)data;
1966 filename = HeapAlloc(GetProcessHeap(), 0, strlen(filename_in) + 1);
1967 if (!filename) return E_OUTOFMEMORY;
1969 strcpy(filename, filename_in);
1970 *filename_out = filename;
1972 return D3D_OK;
1975 static HRESULT parse_material(IDirectXFileData *filedata, D3DXMATERIAL *material)
1977 HRESULT hr;
1978 DWORD data_size;
1979 BYTE *data;
1980 const GUID *type;
1981 IDirectXFileData *child;
1983 material->pTextureFilename = NULL;
1985 hr = IDirectXFileData_GetData(filedata, NULL, &data_size, (void**)&data);
1986 if (FAILED(hr)) return hr;
1989 * template ColorRGBA {
1990 * FLOAT red;
1991 * FLOAT green;
1992 * FLOAT blue;
1993 * FLOAT alpha;
1995 * template ColorRGB {
1996 * FLOAT red;
1997 * FLOAT green;
1998 * FLOAT blue;
2000 * template Material {
2001 * ColorRGBA faceColor;
2002 * FLOAT power;
2003 * ColorRGB specularColor;
2004 * ColorRGB emissiveColor;
2005 * [ ... ]
2008 if (data_size != sizeof(FLOAT) * 11) {
2009 WARN("incorrect data size (%u bytes)\n", data_size);
2010 return E_FAIL;
2013 memcpy(&material->MatD3D.Diffuse, data, sizeof(D3DCOLORVALUE));
2014 data += sizeof(D3DCOLORVALUE);
2015 material->MatD3D.Power = *(FLOAT*)data;
2016 data += sizeof(FLOAT);
2017 memcpy(&material->MatD3D.Specular, data, sizeof(FLOAT) * 3);
2018 material->MatD3D.Specular.a = 1.0f;
2019 data += 3 * sizeof(FLOAT);
2020 memcpy(&material->MatD3D.Emissive, data, sizeof(FLOAT) * 3);
2021 material->MatD3D.Emissive.a = 1.0f;
2022 material->MatD3D.Ambient.r = 0.0f;
2023 material->MatD3D.Ambient.g = 0.0f;
2024 material->MatD3D.Ambient.b = 0.0f;
2025 material->MatD3D.Ambient.a = 1.0f;
2027 while (SUCCEEDED(hr = get_next_child(filedata, &child, &type)))
2029 if (IsEqualGUID(type, &TID_D3DRMTextureFilename)) {
2030 hr = parse_texture_filename(child, &material->pTextureFilename);
2031 if (FAILED(hr)) break;
2034 return hr == DXFILEERR_NOMOREOBJECTS ? D3D_OK : hr;
2037 static void destroy_materials(struct mesh_data *mesh)
2039 int i;
2040 for (i = 0; i < mesh->num_materials; i++)
2041 HeapFree(GetProcessHeap(), 0, mesh->materials[i].pTextureFilename);
2042 HeapFree(GetProcessHeap(), 0, mesh->materials);
2043 HeapFree(GetProcessHeap(), 0, mesh->material_indices);
2044 mesh->num_materials = 0;
2045 mesh->materials = NULL;
2046 mesh->material_indices = NULL;
2049 static HRESULT parse_material_list(IDirectXFileData *filedata, struct mesh_data *mesh)
2051 HRESULT hr;
2052 DWORD data_size;
2053 DWORD *data, *in_ptr;
2054 const GUID *type;
2055 IDirectXFileData *child;
2056 DWORD num_materials;
2057 int i;
2059 destroy_materials(mesh);
2061 hr = IDirectXFileData_GetData(filedata, NULL, &data_size, (void**)&data);
2062 if (FAILED(hr)) return hr;
2064 /* template MeshMaterialList {
2065 * DWORD nMaterials;
2066 * DWORD nFaceIndexes;
2067 * array DWORD faceIndexes[nFaceIndexes];
2068 * [ Material ]
2072 in_ptr = data;
2074 if (data_size < sizeof(DWORD))
2075 goto truncated_data_error;
2076 num_materials = *in_ptr++;
2077 if (!num_materials)
2078 return D3D_OK;
2080 if (data_size < 2 * sizeof(DWORD))
2081 goto truncated_data_error;
2082 if (*in_ptr++ != mesh->num_poly_faces) {
2083 WARN("number of material face indices (%u) doesn't match number of faces (%u)\n",
2084 *(in_ptr - 1), mesh->num_poly_faces);
2085 return E_FAIL;
2087 if (data_size < 2 * sizeof(DWORD) + mesh->num_poly_faces * sizeof(DWORD))
2088 goto truncated_data_error;
2089 for (i = 0; i < mesh->num_poly_faces; i++) {
2090 if (*in_ptr++ >= num_materials) {
2091 WARN("face %u: reference to undefined material %u (only %u materials)\n",
2092 i, *(in_ptr - 1), num_materials);
2093 return E_FAIL;
2097 mesh->materials = HeapAlloc(GetProcessHeap(), 0, num_materials * sizeof(*mesh->materials));
2098 mesh->material_indices = HeapAlloc(GetProcessHeap(), 0, mesh->num_poly_faces * sizeof(*mesh->material_indices));
2099 if (!mesh->materials || !mesh->material_indices)
2100 return E_OUTOFMEMORY;
2101 memcpy(mesh->material_indices, data + 2, mesh->num_poly_faces * sizeof(DWORD));
2103 while (SUCCEEDED(hr = get_next_child(filedata, &child, &type)))
2105 if (IsEqualGUID(type, &TID_D3DRMMaterial)) {
2106 if (mesh->num_materials >= num_materials) {
2107 WARN("more materials defined than declared\n");
2108 return E_FAIL;
2110 hr = parse_material(child, &mesh->materials[mesh->num_materials++]);
2111 if (FAILED(hr)) break;
2114 if (hr != DXFILEERR_NOMOREOBJECTS)
2115 return hr;
2116 if (num_materials != mesh->num_materials) {
2117 WARN("only %u of %u materials defined\n", num_materials, mesh->num_materials);
2118 return E_FAIL;
2121 return D3D_OK;
2122 truncated_data_error:
2123 WARN("truncated data (%u bytes)\n", data_size);
2124 return E_FAIL;
2127 static HRESULT parse_texture_coords(IDirectXFileData *filedata, struct mesh_data *mesh)
2129 HRESULT hr;
2130 DWORD data_size;
2131 BYTE *data;
2133 HeapFree(GetProcessHeap(), 0, mesh->tex_coords);
2134 mesh->tex_coords = NULL;
2136 hr = IDirectXFileData_GetData(filedata, NULL, &data_size, (void**)&data);
2137 if (FAILED(hr)) return hr;
2139 /* template Coords2d {
2140 * FLOAT u;
2141 * FLOAT v;
2143 * template MeshTextureCoords {
2144 * DWORD nTextureCoords;
2145 * array Coords2d textureCoords[nTextureCoords];
2149 if (data_size < sizeof(DWORD))
2150 goto truncated_data_error;
2151 if (*(DWORD*)data != mesh->num_vertices) {
2152 WARN("number of texture coordinates (%u) doesn't match number of vertices (%u)\n",
2153 *(DWORD*)data, mesh->num_vertices);
2154 return E_FAIL;
2156 data += sizeof(DWORD);
2157 if (data_size < sizeof(DWORD) + mesh->num_vertices * sizeof(*mesh->tex_coords))
2158 goto truncated_data_error;
2160 mesh->tex_coords = HeapAlloc(GetProcessHeap(), 0, mesh->num_vertices * sizeof(*mesh->tex_coords));
2161 if (!mesh->tex_coords) return E_OUTOFMEMORY;
2162 memcpy(mesh->tex_coords, data, mesh->num_vertices * sizeof(*mesh->tex_coords));
2164 mesh->fvf |= D3DFVF_TEX1;
2166 return D3D_OK;
2167 truncated_data_error:
2168 WARN("truncated data (%u bytes)\n", data_size);
2169 return E_FAIL;
2172 static HRESULT parse_vertex_colors(IDirectXFileData *filedata, struct mesh_data *mesh)
2174 HRESULT hr;
2175 DWORD data_size;
2176 BYTE *data;
2177 DWORD num_colors;
2178 int i;
2180 HeapFree(GetProcessHeap(), 0, mesh->vertex_colors);
2181 mesh->vertex_colors = NULL;
2183 hr = IDirectXFileData_GetData(filedata, NULL, &data_size, (void**)&data);
2184 if (FAILED(hr)) return hr;
2186 /* template IndexedColor {
2187 * DWORD index;
2188 * ColorRGBA indexColor;
2190 * template MeshVertexColors {
2191 * DWORD nVertexColors;
2192 * array IndexedColor vertexColors[nVertexColors];
2196 if (data_size < sizeof(DWORD))
2197 goto truncated_data_error;
2198 num_colors = *(DWORD*)data;
2199 data += sizeof(DWORD);
2200 if (data_size < sizeof(DWORD) + num_colors * (sizeof(DWORD) + sizeof(D3DCOLORVALUE)))
2201 goto truncated_data_error;
2203 mesh->vertex_colors = HeapAlloc(GetProcessHeap(), 0, mesh->num_vertices * sizeof(DWORD));
2204 if (!mesh->vertex_colors)
2205 return E_OUTOFMEMORY;
2207 for (i = 0; i < mesh->num_vertices; i++)
2208 mesh->vertex_colors[i] = D3DCOLOR_ARGB(0, 0xff, 0xff, 0xff);
2209 for (i = 0; i < num_colors; i++)
2211 D3DCOLORVALUE color;
2212 DWORD index = *(DWORD*)data;
2213 data += sizeof(DWORD);
2214 if (index >= mesh->num_vertices) {
2215 WARN("vertex color %u references undefined vertex %u (only %u vertices)\n",
2216 i, index, mesh->num_vertices);
2217 return E_FAIL;
2219 memcpy(&color, data, sizeof(color));
2220 data += sizeof(color);
2221 color.r = fminf(1.0f, fmaxf(0.0f, color.r));
2222 color.g = fminf(1.0f, fmaxf(0.0f, color.g));
2223 color.b = fminf(1.0f, fmaxf(0.0f, color.b));
2224 color.a = fminf(1.0f, fmaxf(0.0f, color.a));
2225 mesh->vertex_colors[index] = D3DCOLOR_ARGB((BYTE)(color.a * 255.0f + 0.5f),
2226 (BYTE)(color.r * 255.0f + 0.5f),
2227 (BYTE)(color.g * 255.0f + 0.5f),
2228 (BYTE)(color.b * 255.0f + 0.5f));
2231 mesh->fvf |= D3DFVF_DIFFUSE;
2233 return D3D_OK;
2234 truncated_data_error:
2235 WARN("truncated data (%u bytes)\n", data_size);
2236 return E_FAIL;
2239 static HRESULT parse_normals(IDirectXFileData *filedata, struct mesh_data *mesh)
2241 HRESULT hr;
2242 DWORD data_size;
2243 BYTE *data;
2244 DWORD *index_out_ptr;
2245 int i;
2246 DWORD num_face_indices = mesh->num_poly_faces * 2 + mesh->num_tri_faces;
2248 HeapFree(GetProcessHeap(), 0, mesh->normals);
2249 mesh->num_normals = 0;
2250 mesh->normals = NULL;
2251 mesh->normal_indices = NULL;
2252 mesh->fvf |= D3DFVF_NORMAL;
2254 hr = IDirectXFileData_GetData(filedata, NULL, &data_size, (void**)&data);
2255 if (FAILED(hr)) return hr;
2257 /* template Vector {
2258 * FLOAT x;
2259 * FLOAT y;
2260 * FLOAT z;
2262 * template MeshFace {
2263 * DWORD nFaceVertexIndices;
2264 * array DWORD faceVertexIndices[nFaceVertexIndices];
2266 * template MeshNormals {
2267 * DWORD nNormals;
2268 * array Vector normals[nNormals];
2269 * DWORD nFaceNormals;
2270 * array MeshFace faceNormals[nFaceNormals];
2274 if (data_size < sizeof(DWORD) * 2)
2275 goto truncated_data_error;
2276 mesh->num_normals = *(DWORD*)data;
2277 data += sizeof(DWORD);
2278 if (data_size < sizeof(DWORD) * 2 + mesh->num_normals * sizeof(D3DXVECTOR3) +
2279 num_face_indices * sizeof(DWORD))
2280 goto truncated_data_error;
2282 mesh->normals = HeapAlloc(GetProcessHeap(), 0, mesh->num_normals * sizeof(D3DXVECTOR3));
2283 mesh->normal_indices = HeapAlloc(GetProcessHeap(), 0, num_face_indices * sizeof(DWORD));
2284 if (!mesh->normals || !mesh->normal_indices)
2285 return E_OUTOFMEMORY;
2287 memcpy(mesh->normals, data, mesh->num_normals * sizeof(D3DXVECTOR3));
2288 data += mesh->num_normals * sizeof(D3DXVECTOR3);
2289 for (i = 0; i < mesh->num_normals; i++)
2290 D3DXVec3Normalize(&mesh->normals[i], &mesh->normals[i]);
2292 if (*(DWORD*)data != mesh->num_poly_faces) {
2293 WARN("number of face normals (%u) doesn't match number of faces (%u)\n",
2294 *(DWORD*)data, mesh->num_poly_faces);
2295 return E_FAIL;
2297 data += sizeof(DWORD);
2298 index_out_ptr = mesh->normal_indices;
2299 for (i = 0; i < mesh->num_poly_faces; i++)
2301 DWORD j;
2302 DWORD count = *(DWORD*)data;
2303 if (count != mesh->num_tri_per_face[i] + 2) {
2304 WARN("face %u: number of normals (%u) doesn't match number of vertices (%u)\n",
2305 i, count, mesh->num_tri_per_face[i] + 2);
2306 return E_FAIL;
2308 data += sizeof(DWORD);
2310 for (j = 0; j < count; j++) {
2311 DWORD normal_index = *(DWORD*)data;
2312 if (normal_index >= mesh->num_normals) {
2313 WARN("face %u, normal index %u: reference to undefined normal %u (only %u normals)\n",
2314 i, j, normal_index, mesh->num_normals);
2315 return E_FAIL;
2317 *index_out_ptr++ = normal_index;
2318 data += sizeof(DWORD);
2322 return D3D_OK;
2323 truncated_data_error:
2324 WARN("truncated data (%u bytes)\n", data_size);
2325 return E_FAIL;
2328 /* for provide_flags parameters */
2329 #define PROVIDE_MATERIALS 0x1
2330 #define PROVIDE_SKININFO 0x2
2331 #define PROVIDE_ADJACENCY 0x4
2333 static HRESULT parse_mesh(IDirectXFileData *filedata, struct mesh_data *mesh_data, DWORD provide_flags)
2335 HRESULT hr;
2336 DWORD data_size;
2337 BYTE *data, *in_ptr;
2338 DWORD *index_out_ptr;
2339 const GUID *type;
2340 IDirectXFileData *child;
2341 int i;
2344 * template Mesh {
2345 * DWORD nVertices;
2346 * array Vector vertices[nVertices];
2347 * DWORD nFaces;
2348 * array MeshFace faces[nFaces];
2349 * [ ... ]
2353 hr = IDirectXFileData_GetData(filedata, NULL, &data_size, (void**)&data);
2354 if (FAILED(hr)) return hr;
2356 in_ptr = data;
2357 if (data_size < sizeof(DWORD) * 2)
2358 goto truncated_data_error;
2359 mesh_data->num_vertices = *(DWORD*)in_ptr;
2360 if (data_size < sizeof(DWORD) * 2 + mesh_data->num_vertices * sizeof(D3DXVECTOR3))
2361 goto truncated_data_error;
2362 in_ptr += sizeof(DWORD) + mesh_data->num_vertices * sizeof(D3DXVECTOR3);
2364 mesh_data->num_poly_faces = *(DWORD*)in_ptr;
2365 in_ptr += sizeof(DWORD);
2367 mesh_data->num_tri_faces = 0;
2368 for (i = 0; i < mesh_data->num_poly_faces; i++)
2370 DWORD num_poly_vertices;
2371 DWORD j;
2373 if (data_size - (in_ptr - data) < sizeof(DWORD))
2374 goto truncated_data_error;
2375 num_poly_vertices = *(DWORD*)in_ptr;
2376 in_ptr += sizeof(DWORD);
2377 if (data_size - (in_ptr - data) < num_poly_vertices * sizeof(DWORD))
2378 goto truncated_data_error;
2379 if (num_poly_vertices < 3) {
2380 WARN("face %u has only %u vertices\n", i, num_poly_vertices);
2381 return E_FAIL;
2383 for (j = 0; j < num_poly_vertices; j++) {
2384 if (*(DWORD*)in_ptr >= mesh_data->num_vertices) {
2385 WARN("face %u, index %u: undefined vertex %u (only %u vertices)\n",
2386 i, j, *(DWORD*)in_ptr, mesh_data->num_vertices);
2387 return E_FAIL;
2389 in_ptr += sizeof(DWORD);
2391 mesh_data->num_tri_faces += num_poly_vertices - 2;
2394 mesh_data->fvf = D3DFVF_XYZ;
2396 mesh_data->vertices = HeapAlloc(GetProcessHeap(), 0,
2397 mesh_data->num_vertices * sizeof(*mesh_data->vertices));
2398 mesh_data->num_tri_per_face = HeapAlloc(GetProcessHeap(), 0,
2399 mesh_data->num_poly_faces * sizeof(*mesh_data->num_tri_per_face));
2400 mesh_data->indices = HeapAlloc(GetProcessHeap(), 0,
2401 (mesh_data->num_tri_faces + mesh_data->num_poly_faces * 2) * sizeof(*mesh_data->indices));
2402 if (!mesh_data->vertices || !mesh_data->num_tri_per_face || !mesh_data->indices)
2403 return E_OUTOFMEMORY;
2405 in_ptr = data + sizeof(DWORD);
2406 memcpy(mesh_data->vertices, in_ptr, mesh_data->num_vertices * sizeof(D3DXVECTOR3));
2407 in_ptr += mesh_data->num_vertices * sizeof(D3DXVECTOR3) + sizeof(DWORD);
2409 index_out_ptr = mesh_data->indices;
2410 for (i = 0; i < mesh_data->num_poly_faces; i++)
2412 DWORD count;
2414 count = *(DWORD*)in_ptr;
2415 in_ptr += sizeof(DWORD);
2416 mesh_data->num_tri_per_face[i] = count - 2;
2418 while (count--) {
2419 *index_out_ptr++ = *(DWORD*)in_ptr;
2420 in_ptr += sizeof(DWORD);
2424 while (SUCCEEDED(hr = get_next_child(filedata, &child, &type)))
2426 if (IsEqualGUID(type, &TID_D3DRMMeshNormals)) {
2427 hr = parse_normals(child, mesh_data);
2428 } else if (IsEqualGUID(type, &TID_D3DRMMeshVertexColors)) {
2429 hr = parse_vertex_colors(child, mesh_data);
2430 } else if (IsEqualGUID(type, &TID_D3DRMMeshTextureCoords)) {
2431 hr = parse_texture_coords(child, mesh_data);
2432 } else if (IsEqualGUID(type, &TID_D3DRMMeshMaterialList) &&
2433 (provide_flags & PROVIDE_MATERIALS))
2435 hr = parse_material_list(child, mesh_data);
2436 } else if (provide_flags & PROVIDE_SKININFO) {
2437 if (IsEqualGUID(type, &DXFILEOBJ_XSkinMeshHeader)) {
2438 FIXME("Skin mesh loading not implemented.\n");
2439 hr = E_NOTIMPL;
2440 } else if (IsEqualGUID(type, &DXFILEOBJ_SkinWeights)) {
2441 /* ignored without XSkinMeshHeader */
2444 if (FAILED(hr))
2445 break;
2447 return hr == DXFILEERR_NOMOREOBJECTS ? D3D_OK : hr;
2448 truncated_data_error:
2449 WARN("truncated data (%u bytes)\n", data_size);
2450 return E_FAIL;
2453 static HRESULT generate_effects(ID3DXBuffer *materials, DWORD num_materials,
2454 ID3DXBuffer **effects)
2456 HRESULT hr;
2457 D3DXEFFECTINSTANCE *effect_ptr;
2458 BYTE *out_ptr;
2459 const D3DXMATERIAL *material_ptr = ID3DXBuffer_GetBufferPointer(materials);
2460 static const struct {
2461 const char *param_name;
2462 DWORD name_size;
2463 DWORD num_bytes;
2464 DWORD value_offset;
2465 } material_effects[] = {
2466 #define EFFECT_TABLE_ENTRY(str, field) \
2467 {str, sizeof(str), sizeof(material_ptr->MatD3D.field), offsetof(D3DXMATERIAL, MatD3D.field)}
2468 EFFECT_TABLE_ENTRY("Diffuse", Diffuse),
2469 EFFECT_TABLE_ENTRY("Power", Power),
2470 EFFECT_TABLE_ENTRY("Specular", Specular),
2471 EFFECT_TABLE_ENTRY("Emissive", Emissive),
2472 EFFECT_TABLE_ENTRY("Ambient", Ambient),
2473 #undef EFFECT_TABLE_ENTRY
2475 static const char texture_paramname[] = "Texture0@Name";
2476 DWORD buffer_size;
2477 int i;
2479 /* effects buffer layout:
2481 * D3DXEFFECTINSTANCE effects[num_materials];
2482 * for (effect in effects)
2484 * D3DXEFFECTDEFAULT defaults[effect.NumDefaults];
2485 * for (default in defaults)
2487 * *default.pParamName;
2488 * *default.pValue;
2492 buffer_size = sizeof(D3DXEFFECTINSTANCE);
2493 buffer_size += sizeof(D3DXEFFECTDEFAULT) * ARRAY_SIZE(material_effects);
2494 for (i = 0; i < ARRAY_SIZE(material_effects); i++) {
2495 buffer_size += material_effects[i].name_size;
2496 buffer_size += material_effects[i].num_bytes;
2498 buffer_size *= num_materials;
2499 for (i = 0; i < num_materials; i++) {
2500 if (material_ptr[i].pTextureFilename) {
2501 buffer_size += sizeof(D3DXEFFECTDEFAULT);
2502 buffer_size += sizeof(texture_paramname);
2503 buffer_size += strlen(material_ptr[i].pTextureFilename) + 1;
2507 hr = D3DXCreateBuffer(buffer_size, effects);
2508 if (FAILED(hr)) return hr;
2509 effect_ptr = ID3DXBuffer_GetBufferPointer(*effects);
2510 out_ptr = (BYTE*)(effect_ptr + num_materials);
2512 for (i = 0; i < num_materials; i++)
2514 int j;
2515 D3DXEFFECTDEFAULT *defaults = (D3DXEFFECTDEFAULT*)out_ptr;
2517 effect_ptr->pDefaults = defaults;
2518 effect_ptr->NumDefaults = material_ptr->pTextureFilename ? 6 : 5;
2519 out_ptr = (BYTE*)(effect_ptr->pDefaults + effect_ptr->NumDefaults);
2521 for (j = 0; j < ARRAY_SIZE(material_effects); j++)
2523 defaults->pParamName = (LPSTR)out_ptr;
2524 strcpy(defaults->pParamName, material_effects[j].param_name);
2525 defaults->pValue = defaults->pParamName + material_effects[j].name_size;
2526 defaults->Type = D3DXEDT_FLOATS;
2527 defaults->NumBytes = material_effects[j].num_bytes;
2528 memcpy(defaults->pValue, (BYTE*)material_ptr + material_effects[j].value_offset, defaults->NumBytes);
2529 out_ptr = (BYTE*)defaults->pValue + defaults->NumBytes;
2530 defaults++;
2533 if (material_ptr->pTextureFilename) {
2534 defaults->pParamName = (LPSTR)out_ptr;
2535 strcpy(defaults->pParamName, texture_paramname);
2536 defaults->pValue = defaults->pParamName + sizeof(texture_paramname);
2537 defaults->Type = D3DXEDT_STRING;
2538 defaults->NumBytes = strlen(material_ptr->pTextureFilename) + 1;
2539 strcpy(defaults->pValue, material_ptr->pTextureFilename);
2540 out_ptr = (BYTE*)defaults->pValue + defaults->NumBytes;
2542 material_ptr++;
2543 effect_ptr++;
2545 assert(out_ptr - (BYTE*)ID3DXBuffer_GetBufferPointer(*effects) == buffer_size);
2547 return D3D_OK;
2550 /* change to D3DXLoadSkinMeshFromXof when ID3DXFileData is implemented */
2551 static HRESULT load_skin_mesh_from_xof(IDirectXFileData *filedata,
2552 DWORD options,
2553 LPDIRECT3DDEVICE9 device,
2554 LPD3DXBUFFER *adjacency_out,
2555 LPD3DXBUFFER *materials_out,
2556 LPD3DXBUFFER *effects_out,
2557 DWORD *num_materials_out,
2558 LPD3DXSKININFO *skin_info_out,
2559 LPD3DXMESH *mesh_out)
2561 HRESULT hr;
2562 DWORD *index_in_ptr;
2563 struct mesh_data mesh_data;
2564 DWORD total_vertices;
2565 ID3DXMesh *d3dxmesh = NULL;
2566 ID3DXBuffer *adjacency = NULL;
2567 ID3DXBuffer *materials = NULL;
2568 ID3DXBuffer *effects = NULL;
2569 struct vertex_duplication {
2570 DWORD normal_index;
2571 struct list entry;
2572 } *duplications = NULL;
2573 int i;
2574 void *vertices = NULL;
2575 void *indices = NULL;
2576 BYTE *out_ptr;
2577 DWORD provide_flags = 0;
2579 ZeroMemory(&mesh_data, sizeof(mesh_data));
2581 if (num_materials_out || materials_out || effects_out)
2582 provide_flags |= PROVIDE_MATERIALS;
2583 if (skin_info_out)
2584 provide_flags |= PROVIDE_SKININFO;
2586 hr = parse_mesh(filedata, &mesh_data, provide_flags);
2587 if (FAILED(hr)) goto cleanup;
2589 total_vertices = mesh_data.num_vertices;
2590 if (mesh_data.fvf & D3DFVF_NORMAL) {
2591 /* duplicate vertices with multiple normals */
2592 DWORD num_face_indices = mesh_data.num_poly_faces * 2 + mesh_data.num_tri_faces;
2593 duplications = HeapAlloc(GetProcessHeap(), 0, (mesh_data.num_vertices + num_face_indices) * sizeof(*duplications));
2594 if (!duplications) {
2595 hr = E_OUTOFMEMORY;
2596 goto cleanup;
2598 for (i = 0; i < total_vertices; i++)
2600 duplications[i].normal_index = -1;
2601 list_init(&duplications[i].entry);
2603 for (i = 0; i < num_face_indices; i++) {
2604 DWORD vertex_index = mesh_data.indices[i];
2605 DWORD normal_index = mesh_data.normal_indices[i];
2606 struct vertex_duplication *dup_ptr = &duplications[vertex_index];
2608 if (dup_ptr->normal_index == -1) {
2609 dup_ptr->normal_index = normal_index;
2610 } else {
2611 D3DXVECTOR3 *new_normal = &mesh_data.normals[normal_index];
2612 struct list *dup_list = &dup_ptr->entry;
2613 while (TRUE) {
2614 D3DXVECTOR3 *cur_normal = &mesh_data.normals[dup_ptr->normal_index];
2615 if (new_normal->x == cur_normal->x &&
2616 new_normal->y == cur_normal->y &&
2617 new_normal->z == cur_normal->z)
2619 mesh_data.indices[i] = dup_ptr - duplications;
2620 break;
2621 } else if (!list_next(dup_list, &dup_ptr->entry)) {
2622 dup_ptr = &duplications[total_vertices++];
2623 dup_ptr->normal_index = normal_index;
2624 list_add_tail(dup_list, &dup_ptr->entry);
2625 mesh_data.indices[i] = dup_ptr - duplications;
2626 break;
2627 } else {
2628 dup_ptr = LIST_ENTRY(list_next(dup_list, &dup_ptr->entry),
2629 struct vertex_duplication, entry);
2636 hr = D3DXCreateMeshFVF(mesh_data.num_tri_faces, total_vertices, options, mesh_data.fvf, device, &d3dxmesh);
2637 if (FAILED(hr)) goto cleanup;
2639 hr = d3dxmesh->lpVtbl->LockVertexBuffer(d3dxmesh, D3DLOCK_DISCARD, &vertices);
2640 if (FAILED(hr)) goto cleanup;
2642 out_ptr = vertices;
2643 for (i = 0; i < mesh_data.num_vertices; i++) {
2644 *(D3DXVECTOR3*)out_ptr = mesh_data.vertices[i];
2645 out_ptr += sizeof(D3DXVECTOR3);
2646 if (mesh_data.fvf & D3DFVF_NORMAL) {
2647 if (duplications[i].normal_index == -1)
2648 ZeroMemory(out_ptr, sizeof(D3DXVECTOR3));
2649 else
2650 *(D3DXVECTOR3*)out_ptr = mesh_data.normals[duplications[i].normal_index];
2651 out_ptr += sizeof(D3DXVECTOR3);
2653 if (mesh_data.fvf & D3DFVF_DIFFUSE) {
2654 *(DWORD*)out_ptr = mesh_data.vertex_colors[i];
2655 out_ptr += sizeof(DWORD);
2657 if (mesh_data.fvf & D3DFVF_TEX1) {
2658 *(D3DXVECTOR2*)out_ptr = mesh_data.tex_coords[i];
2659 out_ptr += sizeof(D3DXVECTOR2);
2662 if (mesh_data.fvf & D3DFVF_NORMAL) {
2663 DWORD vertex_size = D3DXGetFVFVertexSize(mesh_data.fvf);
2664 out_ptr = vertices;
2665 for (i = 0; i < mesh_data.num_vertices; i++) {
2666 struct vertex_duplication *dup_ptr;
2667 LIST_FOR_EACH_ENTRY(dup_ptr, &duplications[i].entry, struct vertex_duplication, entry)
2669 int j = dup_ptr - duplications;
2670 BYTE *dest_vertex = (BYTE*)vertices + j * vertex_size;
2672 memcpy(dest_vertex, out_ptr, vertex_size);
2673 dest_vertex += sizeof(D3DXVECTOR3);
2674 *(D3DXVECTOR3*)dest_vertex = mesh_data.normals[dup_ptr->normal_index];
2676 out_ptr += vertex_size;
2679 d3dxmesh->lpVtbl->UnlockVertexBuffer(d3dxmesh);
2681 hr = d3dxmesh->lpVtbl->LockIndexBuffer(d3dxmesh, D3DLOCK_DISCARD, (void**)&indices);
2682 if (FAILED(hr)) goto cleanup;
2684 index_in_ptr = mesh_data.indices;
2685 #define FILL_INDEX_BUFFER(indices_var) \
2686 for (i = 0; i < mesh_data.num_poly_faces; i++) \
2688 DWORD count = mesh_data.num_tri_per_face[i]; \
2689 WORD first_index = *index_in_ptr++; \
2690 while (count--) { \
2691 *indices_var++ = first_index; \
2692 *indices_var++ = *index_in_ptr; \
2693 index_in_ptr++; \
2694 *indices_var++ = *index_in_ptr; \
2696 index_in_ptr++; \
2698 if (options & D3DXMESH_32BIT) {
2699 DWORD *dword_indices = indices;
2700 FILL_INDEX_BUFFER(dword_indices)
2701 } else {
2702 WORD *word_indices = indices;
2703 FILL_INDEX_BUFFER(word_indices)
2705 #undef FILL_INDEX_BUFFER
2706 d3dxmesh->lpVtbl->UnlockIndexBuffer(d3dxmesh);
2708 if (mesh_data.material_indices) {
2709 DWORD *attrib_buffer = NULL;
2710 hr = d3dxmesh->lpVtbl->LockAttributeBuffer(d3dxmesh, D3DLOCK_DISCARD, &attrib_buffer);
2711 if (FAILED(hr)) goto cleanup;
2712 for (i = 0; i < mesh_data.num_poly_faces; i++)
2714 DWORD count = mesh_data.num_tri_per_face[i];
2715 while (count--)
2716 *attrib_buffer++ = mesh_data.material_indices[i];
2718 d3dxmesh->lpVtbl->UnlockAttributeBuffer(d3dxmesh);
2720 hr = d3dxmesh->lpVtbl->OptimizeInplace(d3dxmesh,
2721 D3DXMESHOPT_ATTRSORT | D3DXMESHOPT_IGNOREVERTS | D3DXMESHOPT_DONOTSPLIT,
2722 NULL, NULL, NULL, NULL);
2723 if (FAILED(hr)) goto cleanup;
2726 if (mesh_data.num_materials && (materials_out || effects_out)) {
2727 DWORD buffer_size = mesh_data.num_materials * sizeof(D3DXMATERIAL);
2728 char *strings_out_ptr;
2729 D3DXMATERIAL *materials_ptr;
2731 for (i = 0; i < mesh_data.num_materials; i++) {
2732 if (mesh_data.materials[i].pTextureFilename)
2733 buffer_size += strlen(mesh_data.materials[i].pTextureFilename) + 1;
2736 hr = D3DXCreateBuffer(buffer_size, &materials);
2737 if (FAILED(hr)) goto cleanup;
2739 materials_ptr = ID3DXBuffer_GetBufferPointer(materials);
2740 memcpy(materials_ptr, mesh_data.materials, mesh_data.num_materials * sizeof(D3DXMATERIAL));
2741 strings_out_ptr = (char*)(materials_ptr + mesh_data.num_materials);
2742 for (i = 0; i < mesh_data.num_materials; i++) {
2743 if (materials_ptr[i].pTextureFilename) {
2744 strcpy(strings_out_ptr, mesh_data.materials[i].pTextureFilename);
2745 materials_ptr[i].pTextureFilename = strings_out_ptr;
2746 strings_out_ptr += strlen(mesh_data.materials[i].pTextureFilename) + 1;
2751 if (mesh_data.num_materials && effects_out) {
2752 hr = generate_effects(materials, mesh_data.num_materials, &effects);
2753 if (FAILED(hr)) goto cleanup;
2755 if (!materials_out) {
2756 ID3DXBuffer_Release(materials);
2757 materials = NULL;
2761 if (adjacency_out) {
2762 hr = D3DXCreateBuffer(mesh_data.num_tri_faces * 3 * sizeof(DWORD), &adjacency);
2763 if (FAILED(hr)) goto cleanup;
2764 hr = d3dxmesh->lpVtbl->GenerateAdjacency(d3dxmesh, 0.0f, ID3DXBuffer_GetBufferPointer(adjacency));
2765 if (FAILED(hr)) goto cleanup;
2768 *mesh_out = d3dxmesh;
2769 if (adjacency_out) *adjacency_out = adjacency;
2770 if (num_materials_out) *num_materials_out = mesh_data.num_materials;
2771 if (materials_out) *materials_out = materials;
2772 if (effects_out) *effects_out = effects;
2773 if (skin_info_out) *skin_info_out = NULL;
2775 hr = D3D_OK;
2776 cleanup:
2777 if (FAILED(hr)) {
2778 if (d3dxmesh) IUnknown_Release(d3dxmesh);
2779 if (adjacency) ID3DXBuffer_Release(adjacency);
2780 if (materials) ID3DXBuffer_Release(materials);
2781 if (effects) ID3DXBuffer_Release(effects);
2783 HeapFree(GetProcessHeap(), 0, mesh_data.vertices);
2784 HeapFree(GetProcessHeap(), 0, mesh_data.num_tri_per_face);
2785 HeapFree(GetProcessHeap(), 0, mesh_data.indices);
2786 HeapFree(GetProcessHeap(), 0, mesh_data.normals);
2787 HeapFree(GetProcessHeap(), 0, mesh_data.normal_indices);
2788 destroy_materials(&mesh_data);
2789 HeapFree(GetProcessHeap(), 0, mesh_data.tex_coords);
2790 HeapFree(GetProcessHeap(), 0, mesh_data.vertex_colors);
2791 HeapFree(GetProcessHeap(), 0, duplications);
2792 return hr;
2795 HRESULT WINAPI D3DXLoadMeshHierarchyFromXA(LPCSTR filename,
2796 DWORD options,
2797 LPDIRECT3DDEVICE9 device,
2798 LPD3DXALLOCATEHIERARCHY alloc_hier,
2799 LPD3DXLOADUSERDATA load_user_data,
2800 LPD3DXFRAME *frame_hierarchy,
2801 LPD3DXANIMATIONCONTROLLER *anim_controller)
2803 HRESULT hr;
2804 int len;
2805 LPWSTR filenameW;
2807 TRACE("(%s, %x, %p, %p, %p, %p, %p)\n", debugstr_a(filename), options,
2808 device, alloc_hier, load_user_data, frame_hierarchy, anim_controller);
2810 if (!filename)
2811 return D3DERR_INVALIDCALL;
2813 len = MultiByteToWideChar(CP_ACP, 0, filename, -1, NULL, 0);
2814 filenameW = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
2815 if (!filenameW) return E_OUTOFMEMORY;
2816 MultiByteToWideChar(CP_ACP, 0, filename, -1, filenameW, len);
2818 hr = D3DXLoadMeshHierarchyFromXW(filenameW, options, device,
2819 alloc_hier, load_user_data, frame_hierarchy, anim_controller);
2820 HeapFree(GetProcessHeap(), 0, filenameW);
2822 return hr;
2825 HRESULT WINAPI D3DXLoadMeshHierarchyFromXW(LPCWSTR filename,
2826 DWORD options,
2827 LPDIRECT3DDEVICE9 device,
2828 LPD3DXALLOCATEHIERARCHY alloc_hier,
2829 LPD3DXLOADUSERDATA load_user_data,
2830 LPD3DXFRAME *frame_hierarchy,
2831 LPD3DXANIMATIONCONTROLLER *anim_controller)
2833 HRESULT hr;
2834 DWORD size;
2835 LPVOID buffer;
2837 TRACE("(%s, %x, %p, %p, %p, %p, %p)\n", debugstr_w(filename), options,
2838 device, alloc_hier, load_user_data, frame_hierarchy, anim_controller);
2840 if (!filename)
2841 return D3DERR_INVALIDCALL;
2843 hr = map_view_of_file(filename, &buffer, &size);
2844 if (FAILED(hr))
2845 return D3DXERR_INVALIDDATA;
2847 hr = D3DXLoadMeshHierarchyFromXInMemory(buffer, size, options, device,
2848 alloc_hier, load_user_data, frame_hierarchy, anim_controller);
2850 UnmapViewOfFile(buffer);
2852 return hr;
2855 static HRESULT filedata_get_name(IDirectXFileData *filedata, char **name)
2857 HRESULT hr;
2858 DWORD name_len;
2860 hr = IDirectXFileData_GetName(filedata, NULL, &name_len);
2861 if (FAILED(hr)) return hr;
2863 if (!name_len)
2864 name_len++;
2865 *name = HeapAlloc(GetProcessHeap(), 0, name_len);
2866 if (!*name) return E_OUTOFMEMORY;
2868 hr = IDirectXFileObject_GetName(filedata, *name, &name_len);
2869 if (FAILED(hr))
2870 HeapFree(GetProcessHeap(), 0, name);
2871 if (!name_len)
2872 (*name)[0] = 0;
2874 return hr;
2877 static HRESULT load_mesh_container(IDirectXFileData *filedata,
2878 DWORD options,
2879 LPDIRECT3DDEVICE9 device,
2880 LPD3DXALLOCATEHIERARCHY alloc_hier,
2881 D3DXMESHCONTAINER **mesh_container)
2883 HRESULT hr;
2884 ID3DXBuffer *adjacency = NULL;
2885 ID3DXBuffer *materials = NULL;
2886 ID3DXBuffer *effects = NULL;
2887 ID3DXSkinInfo *skin_info = NULL;
2888 D3DXMESHDATA mesh_data;
2889 DWORD num_materials = 0;
2890 char *name = NULL;
2892 mesh_data.Type = D3DXMESHTYPE_MESH;
2893 mesh_data.u.pMesh = NULL;
2895 hr = load_skin_mesh_from_xof(filedata, options, device,
2896 &adjacency, &materials, &effects, &num_materials,
2897 &skin_info, &mesh_data.u.pMesh);
2898 if (FAILED(hr)) return hr;
2900 hr = filedata_get_name(filedata, &name);
2901 if (FAILED(hr)) goto cleanup;
2903 hr = alloc_hier->lpVtbl->CreateMeshContainer(alloc_hier, name, &mesh_data,
2904 materials ? ID3DXBuffer_GetBufferPointer(materials) : NULL,
2905 effects ? ID3DXBuffer_GetBufferPointer(effects) : NULL,
2906 num_materials,
2907 adjacency ? ID3DXBuffer_GetBufferPointer(adjacency) : NULL,
2908 skin_info, mesh_container);
2910 cleanup:
2911 if (materials) ID3DXBuffer_Release(materials);
2912 if (effects) ID3DXBuffer_Release(effects);
2913 if (adjacency) ID3DXBuffer_Release(adjacency);
2914 if (skin_info) IUnknown_Release(skin_info);
2915 if (mesh_data.u.pMesh) IUnknown_Release(mesh_data.u.pMesh);
2916 HeapFree(GetProcessHeap(), 0, name);
2917 return hr;
2920 static HRESULT parse_transform_matrix(IDirectXFileData *filedata, D3DXMATRIX *transform)
2922 HRESULT hr;
2923 DWORD data_size;
2924 BYTE *data;
2926 /* template Matrix4x4 {
2927 * array FLOAT matrix[16];
2929 * template FrameTransformMatrix {
2930 * Matrix4x4 frameMatrix;
2934 hr = IDirectXFileData_GetData(filedata, NULL, &data_size, (void**)&data);
2935 if (FAILED(hr)) return hr;
2937 if (data_size != sizeof(D3DXMATRIX)) {
2938 WARN("incorrect data size (%u bytes)\n", data_size);
2939 return E_FAIL;
2942 memcpy(transform, data, sizeof(D3DXMATRIX));
2944 return D3D_OK;
2947 static HRESULT load_frame(IDirectXFileData *filedata,
2948 DWORD options,
2949 LPDIRECT3DDEVICE9 device,
2950 LPD3DXALLOCATEHIERARCHY alloc_hier,
2951 D3DXFRAME **frame_out)
2953 HRESULT hr;
2954 const GUID *type;
2955 IDirectXFileData *child;
2956 char *name = NULL;
2957 D3DXFRAME *frame = NULL;
2958 D3DXMESHCONTAINER **next_container;
2959 D3DXFRAME **next_child;
2961 hr = filedata_get_name(filedata, &name);
2962 if (FAILED(hr)) return hr;
2964 hr = alloc_hier->lpVtbl->CreateFrame(alloc_hier, name, frame_out);
2965 HeapFree(GetProcessHeap(), 0, name);
2966 if (FAILED(hr)) return E_FAIL;
2968 frame = *frame_out;
2969 D3DXMatrixIdentity(&frame->TransformationMatrix);
2970 next_child = &frame->pFrameFirstChild;
2971 next_container = &frame->pMeshContainer;
2973 while (SUCCEEDED(hr = get_next_child(filedata, &child, &type)))
2975 if (IsEqualGUID(type, &TID_D3DRMMesh)) {
2976 hr = load_mesh_container(child, options, device, alloc_hier, next_container);
2977 if (SUCCEEDED(hr))
2978 next_container = &(*next_container)->pNextMeshContainer;
2979 } else if (IsEqualGUID(type, &TID_D3DRMFrameTransformMatrix)) {
2980 hr = parse_transform_matrix(child, &frame->TransformationMatrix);
2981 } else if (IsEqualGUID(type, &TID_D3DRMFrame)) {
2982 hr = load_frame(child, options, device, alloc_hier, next_child);
2983 if (SUCCEEDED(hr))
2984 next_child = &(*next_child)->pFrameSibling;
2986 if (FAILED(hr)) break;
2988 if (hr == DXFILEERR_NOMOREOBJECTS)
2989 hr = D3D_OK;
2991 return hr;
2994 HRESULT WINAPI D3DXLoadMeshHierarchyFromXInMemory(LPCVOID memory,
2995 DWORD memory_size,
2996 DWORD options,
2997 LPDIRECT3DDEVICE9 device,
2998 LPD3DXALLOCATEHIERARCHY alloc_hier,
2999 LPD3DXLOADUSERDATA load_user_data,
3000 LPD3DXFRAME *frame_hierarchy,
3001 LPD3DXANIMATIONCONTROLLER *anim_controller)
3003 HRESULT hr;
3004 IDirectXFile *dxfile = NULL;
3005 IDirectXFileEnumObject *enumobj = NULL;
3006 IDirectXFileData *filedata = NULL;
3007 DXFILELOADMEMORY source;
3008 D3DXFRAME *first_frame = NULL;
3009 D3DXFRAME **next_frame = &first_frame;
3011 TRACE("(%p, %u, %x, %p, %p, %p, %p, %p)\n", memory, memory_size, options,
3012 device, alloc_hier, load_user_data, frame_hierarchy, anim_controller);
3014 if (!memory || !memory_size || !device || !frame_hierarchy || !alloc_hier)
3015 return D3DERR_INVALIDCALL;
3016 if (load_user_data || anim_controller) {
3017 if (load_user_data)
3018 FIXME("Loading user data not implemented\n");
3019 if (anim_controller)
3020 FIXME("Animation controller creation not implemented\n");
3021 return E_NOTIMPL;
3024 hr = DirectXFileCreate(&dxfile);
3025 if (FAILED(hr)) goto cleanup;
3027 hr = IDirectXFile_RegisterTemplates(dxfile, D3DRM_XTEMPLATES, D3DRM_XTEMPLATE_BYTES);
3028 if (FAILED(hr)) goto cleanup;
3030 source.lpMemory = (void*)memory;
3031 source.dSize = memory_size;
3032 hr = IDirectXFile_CreateEnumObject(dxfile, &source, DXFILELOAD_FROMMEMORY, &enumobj);
3033 if (FAILED(hr)) goto cleanup;
3035 while (SUCCEEDED(hr = IDirectXFileEnumObject_GetNextDataObject(enumobj, &filedata)))
3037 const GUID *guid = NULL;
3039 hr = IDirectXFileData_GetType(filedata, &guid);
3040 if (SUCCEEDED(hr)) {
3041 if (IsEqualGUID(guid, &TID_D3DRMMesh)) {
3042 hr = alloc_hier->lpVtbl->CreateFrame(alloc_hier, NULL, next_frame);
3043 if (FAILED(hr)) {
3044 hr = E_FAIL;
3045 goto cleanup;
3048 D3DXMatrixIdentity(&(*next_frame)->TransformationMatrix);
3050 hr = load_mesh_container(filedata, options, device, alloc_hier, &(*next_frame)->pMeshContainer);
3051 if (FAILED(hr)) goto cleanup;
3052 } else if (IsEqualGUID(guid, &TID_D3DRMFrame)) {
3053 hr = load_frame(filedata, options, device, alloc_hier, next_frame);
3054 if (FAILED(hr)) goto cleanup;
3056 while (*next_frame)
3057 next_frame = &(*next_frame)->pFrameSibling;
3060 IDirectXFileData_Release(filedata);
3061 filedata = NULL;
3062 if (FAILED(hr))
3063 goto cleanup;
3065 if (hr != DXFILEERR_NOMOREOBJECTS)
3066 goto cleanup;
3068 if (!first_frame) {
3069 hr = E_FAIL;
3070 } else if (first_frame->pFrameSibling) {
3071 D3DXFRAME *root_frame = NULL;
3072 hr = alloc_hier->lpVtbl->CreateFrame(alloc_hier, NULL, &root_frame);
3073 if (FAILED(hr)) {
3074 hr = E_FAIL;
3075 goto cleanup;
3077 D3DXMatrixIdentity(&root_frame->TransformationMatrix);
3078 root_frame->pFrameFirstChild = first_frame;
3079 *frame_hierarchy = root_frame;
3080 hr = D3D_OK;
3081 } else {
3082 *frame_hierarchy = first_frame;
3083 hr = D3D_OK;
3086 cleanup:
3087 if (FAILED(hr) && first_frame) D3DXFrameDestroy(first_frame, alloc_hier);
3088 if (filedata) IDirectXFileData_Release(filedata);
3089 if (enumobj) IDirectXFileEnumObject_Release(enumobj);
3090 if (dxfile) IDirectXFile_Release(dxfile);
3091 return hr;
3094 HRESULT WINAPI D3DXFrameDestroy(LPD3DXFRAME frame, LPD3DXALLOCATEHIERARCHY alloc_hier)
3096 HRESULT hr;
3097 BOOL last = FALSE;
3099 TRACE("(%p, %p)\n", frame, alloc_hier);
3101 if (!frame || !alloc_hier)
3102 return D3DERR_INVALIDCALL;
3104 while (!last) {
3105 D3DXMESHCONTAINER *container;
3106 D3DXFRAME *current_frame;
3108 if (frame->pFrameSibling) {
3109 current_frame = frame->pFrameSibling;
3110 frame->pFrameSibling = current_frame->pFrameSibling;
3111 current_frame->pFrameSibling = NULL;
3112 } else if (frame) {
3113 current_frame = frame;
3114 last = TRUE;
3117 if (current_frame->pFrameFirstChild) {
3118 hr = D3DXFrameDestroy(current_frame->pFrameFirstChild, alloc_hier);
3119 if (FAILED(hr)) return hr;
3120 current_frame->pFrameFirstChild = NULL;
3123 container = current_frame->pMeshContainer;
3124 while (container) {
3125 D3DXMESHCONTAINER *next_container = container->pNextMeshContainer;
3126 hr = alloc_hier->lpVtbl->DestroyMeshContainer(alloc_hier, container);
3127 if (FAILED(hr)) return hr;
3128 container = next_container;
3130 hr = alloc_hier->lpVtbl->DestroyFrame(alloc_hier, current_frame);
3131 if (FAILED(hr)) return hr;
3133 return D3D_OK;
3136 HRESULT WINAPI D3DXLoadMeshFromXA(LPCSTR filename,
3137 DWORD options,
3138 LPDIRECT3DDEVICE9 device,
3139 LPD3DXBUFFER *adjacency,
3140 LPD3DXBUFFER *materials,
3141 LPD3DXBUFFER *effect_instances,
3142 DWORD *num_materials,
3143 LPD3DXMESH *mesh)
3145 HRESULT hr;
3146 int len;
3147 LPWSTR filenameW;
3149 TRACE("(%s, %x, %p, %p, %p, %p, %p, %p)\n", debugstr_a(filename), options,
3150 device, adjacency, materials, effect_instances, num_materials, mesh);
3152 if (!filename)
3153 return D3DERR_INVALIDCALL;
3155 len = MultiByteToWideChar(CP_ACP, 0, filename, -1, NULL, 0);
3156 filenameW = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
3157 if (!filenameW) return E_OUTOFMEMORY;
3158 MultiByteToWideChar(CP_ACP, 0, filename, -1, filenameW, len);
3160 hr = D3DXLoadMeshFromXW(filenameW, options, device, adjacency, materials,
3161 effect_instances, num_materials, mesh);
3162 HeapFree(GetProcessHeap(), 0, filenameW);
3164 return hr;
3167 HRESULT WINAPI D3DXLoadMeshFromXW(LPCWSTR filename,
3168 DWORD options,
3169 LPDIRECT3DDEVICE9 device,
3170 LPD3DXBUFFER *adjacency,
3171 LPD3DXBUFFER *materials,
3172 LPD3DXBUFFER *effect_instances,
3173 DWORD *num_materials,
3174 LPD3DXMESH *mesh)
3176 HRESULT hr;
3177 DWORD size;
3178 LPVOID buffer;
3180 TRACE("(%s, %x, %p, %p, %p, %p, %p, %p)\n", debugstr_w(filename), options,
3181 device, adjacency, materials, effect_instances, num_materials, mesh);
3183 if (!filename)
3184 return D3DERR_INVALIDCALL;
3186 hr = map_view_of_file(filename, &buffer, &size);
3187 if (FAILED(hr))
3188 return D3DXERR_INVALIDDATA;
3190 hr = D3DXLoadMeshFromXInMemory(buffer, size, options, device, adjacency,
3191 materials, effect_instances, num_materials, mesh);
3193 UnmapViewOfFile(buffer);
3195 return hr;
3198 HRESULT WINAPI D3DXLoadMeshFromXResource(HMODULE module,
3199 LPCSTR name,
3200 LPCSTR type,
3201 DWORD options,
3202 LPDIRECT3DDEVICE9 device,
3203 LPD3DXBUFFER *adjacency,
3204 LPD3DXBUFFER *materials,
3205 LPD3DXBUFFER *effect_instances,
3206 DWORD *num_materials,
3207 LPD3DXMESH *mesh)
3209 HRESULT hr;
3210 HRSRC resinfo;
3211 DWORD size;
3212 LPVOID buffer;
3214 TRACE("(%p, %s, %s, %x, %p, %p, %p, %p, %p, %p)\n",
3215 module, debugstr_a(name), debugstr_a(type), options, device,
3216 adjacency, materials, effect_instances, num_materials, mesh);
3218 resinfo = FindResourceA(module, name, type);
3219 if (!resinfo) return D3DXERR_INVALIDDATA;
3221 hr = load_resource_into_memory(module, resinfo, &buffer, &size);
3222 if (!FAILED(hr)) return D3DXERR_INVALIDDATA;
3224 return D3DXLoadMeshFromXInMemory(buffer, size, options, device, adjacency,
3225 materials, effect_instances, num_materials, mesh);
3228 struct mesh_container
3230 struct list entry;
3231 ID3DXMesh *mesh;
3232 ID3DXBuffer *adjacency;
3233 ID3DXBuffer *materials;
3234 ID3DXBuffer *effects;
3235 DWORD num_materials;
3236 D3DXMATRIX transform;
3239 static HRESULT parse_frame(IDirectXFileData *filedata,
3240 DWORD options,
3241 LPDIRECT3DDEVICE9 device,
3242 const D3DXMATRIX *parent_transform,
3243 struct list *container_list,
3244 DWORD provide_flags)
3246 HRESULT hr;
3247 D3DXMATRIX transform = *parent_transform;
3248 IDirectXFileData *child;
3249 const GUID *type;
3251 while (SUCCEEDED(hr = get_next_child(filedata, &child, &type)))
3253 if (IsEqualGUID(type, &TID_D3DRMMesh)) {
3254 struct mesh_container *container = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(*container));
3255 if (!container) {
3256 hr = E_OUTOFMEMORY;
3257 break;
3259 list_add_tail(container_list, &container->entry);
3260 container->transform = transform;
3262 hr = load_skin_mesh_from_xof(child, options, device,
3263 (provide_flags & PROVIDE_ADJACENCY) ? &container->adjacency : NULL,
3264 (provide_flags & PROVIDE_MATERIALS) ? &container->materials : NULL,
3265 NULL, &container->num_materials, NULL, &container->mesh);
3266 } else if (IsEqualGUID(type, &TID_D3DRMFrameTransformMatrix)) {
3267 D3DXMATRIX new_transform;
3268 hr = parse_transform_matrix(child, &new_transform);
3269 D3DXMatrixMultiply(&transform, &transform, &new_transform);
3270 } else if (IsEqualGUID(type, &TID_D3DRMFrame)) {
3271 hr = parse_frame(child, options, device, &transform, container_list, provide_flags);
3273 if (FAILED(hr)) break;
3275 return hr == DXFILEERR_NOMOREOBJECTS ? D3D_OK : hr;
3278 HRESULT WINAPI D3DXLoadMeshFromXInMemory(LPCVOID memory,
3279 DWORD memory_size,
3280 DWORD options,
3281 LPDIRECT3DDEVICE9 device,
3282 LPD3DXBUFFER *adjacency_out,
3283 LPD3DXBUFFER *materials_out,
3284 LPD3DXBUFFER *effects_out,
3285 DWORD *num_materials_out,
3286 LPD3DXMESH *mesh_out)
3288 HRESULT hr;
3289 IDirectXFile *dxfile = NULL;
3290 IDirectXFileEnumObject *enumobj = NULL;
3291 IDirectXFileData *filedata = NULL;
3292 DXFILELOADMEMORY source;
3293 ID3DXBuffer *materials = NULL;
3294 ID3DXBuffer *effects = NULL;
3295 ID3DXBuffer *adjacency = NULL;
3296 struct list container_list = LIST_INIT(container_list);
3297 struct mesh_container *container_ptr, *next_container_ptr;
3298 DWORD num_materials;
3299 DWORD num_faces, num_vertices;
3300 D3DXMATRIX identity;
3301 int i;
3302 DWORD provide_flags = 0;
3303 DWORD fvf;
3304 ID3DXMesh *concat_mesh = NULL;
3305 D3DVERTEXELEMENT9 concat_decl[MAX_FVF_DECL_SIZE];
3306 BYTE *concat_vertices = NULL;
3307 void *concat_indices = NULL;
3308 DWORD index_offset;
3309 DWORD concat_vertex_size;
3311 TRACE("(%p, %u, %x, %p, %p, %p, %p, %p, %p)\n", memory, memory_size, options,
3312 device, adjacency_out, materials_out, effects_out, num_materials_out, mesh_out);
3314 if (!memory || !memory_size || !device || !mesh_out)
3315 return D3DERR_INVALIDCALL;
3317 hr = DirectXFileCreate(&dxfile);
3318 if (FAILED(hr)) goto cleanup;
3320 hr = IDirectXFile_RegisterTemplates(dxfile, D3DRM_XTEMPLATES, D3DRM_XTEMPLATE_BYTES);
3321 if (FAILED(hr)) goto cleanup;
3323 source.lpMemory = (void*)memory;
3324 source.dSize = memory_size;
3325 hr = IDirectXFile_CreateEnumObject(dxfile, &source, DXFILELOAD_FROMMEMORY, &enumobj);
3326 if (FAILED(hr)) goto cleanup;
3328 D3DXMatrixIdentity(&identity);
3329 if (adjacency_out) provide_flags |= PROVIDE_ADJACENCY;
3330 if (materials_out || effects_out) provide_flags |= PROVIDE_MATERIALS;
3332 while (SUCCEEDED(hr = IDirectXFileEnumObject_GetNextDataObject(enumobj, &filedata)))
3334 const GUID *guid = NULL;
3336 hr = IDirectXFileData_GetType(filedata, &guid);
3337 if (SUCCEEDED(hr)) {
3338 if (IsEqualGUID(guid, &TID_D3DRMMesh)) {
3339 container_ptr = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(*container_ptr));
3340 if (!container_ptr) {
3341 hr = E_OUTOFMEMORY;
3342 goto cleanup;
3344 list_add_tail(&container_list, &container_ptr->entry);
3345 D3DXMatrixIdentity(&container_ptr->transform);
3347 hr = load_skin_mesh_from_xof(filedata, options, device,
3348 (provide_flags & PROVIDE_ADJACENCY) ? &container_ptr->adjacency : NULL,
3349 (provide_flags & PROVIDE_MATERIALS) ? &container_ptr->materials : NULL,
3350 NULL, &container_ptr->num_materials, NULL, &container_ptr->mesh);
3351 } else if (IsEqualGUID(guid, &TID_D3DRMFrame)) {
3352 hr = parse_frame(filedata, options, device, &identity, &container_list, provide_flags);
3354 if (FAILED(hr)) goto cleanup;
3356 IDirectXFileData_Release(filedata);
3357 filedata = NULL;
3358 if (FAILED(hr))
3359 goto cleanup;
3361 if (hr != DXFILEERR_NOMOREOBJECTS)
3362 goto cleanup;
3364 IDirectXFileEnumObject_Release(enumobj);
3365 enumobj = NULL;
3366 IDirectXFile_Release(dxfile);
3367 dxfile = NULL;
3369 if (list_empty(&container_list)) {
3370 hr = E_FAIL;
3371 goto cleanup;
3374 fvf = D3DFVF_XYZ;
3375 num_faces = 0;
3376 num_vertices = 0;
3377 num_materials = 0;
3378 LIST_FOR_EACH_ENTRY(container_ptr, &container_list, struct mesh_container, entry)
3380 ID3DXMesh *mesh = container_ptr->mesh;
3381 fvf |= mesh->lpVtbl->GetFVF(mesh);
3382 num_faces += mesh->lpVtbl->GetNumFaces(mesh);
3383 num_vertices += mesh->lpVtbl->GetNumVertices(mesh);
3384 num_materials += container_ptr->num_materials;
3387 hr = D3DXCreateMeshFVF(num_faces, num_vertices, options, fvf, device, &concat_mesh);
3388 if (FAILED(hr)) goto cleanup;
3390 hr = concat_mesh->lpVtbl->GetDeclaration(concat_mesh, concat_decl);
3391 if (FAILED(hr)) goto cleanup;
3393 concat_vertex_size = D3DXGetDeclVertexSize(concat_decl, 0);
3395 hr = concat_mesh->lpVtbl->LockVertexBuffer(concat_mesh, D3DLOCK_DISCARD, (void**)&concat_vertices);
3396 if (FAILED(hr)) goto cleanup;
3398 LIST_FOR_EACH_ENTRY(container_ptr, &container_list, struct mesh_container, entry)
3400 D3DVERTEXELEMENT9 mesh_decl[MAX_FVF_DECL_SIZE];
3401 ID3DXMesh *mesh = container_ptr->mesh;
3402 DWORD num_mesh_vertices = mesh->lpVtbl->GetNumVertices(mesh);
3403 DWORD mesh_vertex_size;
3404 const BYTE *mesh_vertices;
3406 hr = mesh->lpVtbl->GetDeclaration(mesh, mesh_decl);
3407 if (FAILED(hr)) goto cleanup;
3409 mesh_vertex_size = D3DXGetDeclVertexSize(mesh_decl, 0);
3411 hr = mesh->lpVtbl->LockVertexBuffer(mesh, D3DLOCK_READONLY, (void**)&mesh_vertices);
3412 if (FAILED(hr)) goto cleanup;
3414 for (i = 0; i < num_mesh_vertices; i++) {
3415 int j;
3416 int k = 1;
3418 D3DXVec3TransformCoord((D3DXVECTOR3*)concat_vertices,
3419 (D3DXVECTOR3*)mesh_vertices,
3420 &container_ptr->transform);
3421 for (j = 1; concat_decl[j].Stream != 0xff; j++)
3423 if (concat_decl[j].Usage == mesh_decl[k].Usage &&
3424 concat_decl[j].UsageIndex == mesh_decl[k].UsageIndex)
3426 if (concat_decl[j].Usage == D3DDECLUSAGE_NORMAL) {
3427 D3DXVec3TransformCoord((D3DXVECTOR3*)(concat_vertices + concat_decl[j].Offset),
3428 (D3DXVECTOR3*)(mesh_vertices + mesh_decl[k].Offset),
3429 &container_ptr->transform);
3430 } else {
3431 memcpy(concat_vertices + concat_decl[j].Offset,
3432 mesh_vertices + mesh_decl[k].Offset,
3433 d3dx_decltype_size[mesh_decl[k].Type]);
3435 k++;
3438 mesh_vertices += mesh_vertex_size;
3439 concat_vertices += concat_vertex_size;
3442 mesh->lpVtbl->UnlockVertexBuffer(mesh);
3445 concat_mesh->lpVtbl->UnlockVertexBuffer(concat_mesh);
3446 concat_vertices = NULL;
3448 hr = concat_mesh->lpVtbl->LockIndexBuffer(concat_mesh, D3DLOCK_DISCARD, &concat_indices);
3449 if (FAILED(hr)) goto cleanup;
3451 index_offset = 0;
3452 LIST_FOR_EACH_ENTRY(container_ptr, &container_list, struct mesh_container, entry)
3454 ID3DXMesh *mesh = container_ptr->mesh;
3455 const void *mesh_indices;
3456 DWORD num_mesh_faces = mesh->lpVtbl->GetNumFaces(mesh);
3457 int i;
3459 hr = mesh->lpVtbl->LockIndexBuffer(mesh, D3DLOCK_READONLY, (void**)&mesh_indices);
3460 if (FAILED(hr)) goto cleanup;
3462 if (options & D3DXMESH_32BIT) {
3463 DWORD *dest = concat_indices;
3464 const DWORD *src = mesh_indices;
3465 for (i = 0; i < num_mesh_faces * 3; i++)
3466 *dest++ = index_offset + *src++;
3467 concat_indices = dest;
3468 } else {
3469 WORD *dest = concat_indices;
3470 const WORD *src = mesh_indices;
3471 for (i = 0; i < num_mesh_faces * 3; i++)
3472 *dest++ = index_offset + *src++;
3473 concat_indices = dest;
3475 mesh->lpVtbl->UnlockIndexBuffer(mesh);
3477 index_offset += num_mesh_faces * 3;
3480 concat_mesh->lpVtbl->UnlockIndexBuffer(concat_mesh);
3481 concat_indices = NULL;
3483 if (num_materials) {
3484 DWORD *concat_attrib_buffer = NULL;
3485 DWORD offset = 0;
3487 hr = concat_mesh->lpVtbl->LockAttributeBuffer(concat_mesh, D3DLOCK_DISCARD, &concat_attrib_buffer);
3488 if (FAILED(hr)) goto cleanup;
3490 LIST_FOR_EACH_ENTRY(container_ptr, &container_list, struct mesh_container, entry)
3492 ID3DXMesh *mesh = container_ptr->mesh;
3493 const DWORD *mesh_attrib_buffer = NULL;
3494 DWORD count = mesh->lpVtbl->GetNumFaces(mesh);
3496 hr = mesh->lpVtbl->LockAttributeBuffer(mesh, D3DLOCK_READONLY, (DWORD**)&mesh_attrib_buffer);
3497 if (FAILED(hr)) {
3498 concat_mesh->lpVtbl->UnlockAttributeBuffer(concat_mesh);
3499 goto cleanup;
3502 while (count--)
3503 *concat_attrib_buffer++ = offset + *mesh_attrib_buffer++;
3505 mesh->lpVtbl->UnlockAttributeBuffer(mesh);
3506 offset += container_ptr->num_materials;
3508 concat_mesh->lpVtbl->UnlockAttributeBuffer(concat_mesh);
3511 if (materials_out || effects_out) {
3512 D3DXMATERIAL *out_ptr;
3513 if (!num_materials) {
3514 /* create default material */
3515 hr = D3DXCreateBuffer(sizeof(D3DXMATERIAL), &materials);
3516 if (FAILED(hr)) goto cleanup;
3518 out_ptr = ID3DXBuffer_GetBufferPointer(materials);
3519 out_ptr->MatD3D.Diffuse.r = 0.5f;
3520 out_ptr->MatD3D.Diffuse.g = 0.5f;
3521 out_ptr->MatD3D.Diffuse.b = 0.5f;
3522 out_ptr->MatD3D.Specular.r = 0.5f;
3523 out_ptr->MatD3D.Specular.g = 0.5f;
3524 out_ptr->MatD3D.Specular.b = 0.5f;
3525 /* D3DXCreateBuffer initializes the rest to zero */
3526 } else {
3527 DWORD buffer_size = num_materials * sizeof(D3DXMATERIAL);
3528 char *strings_out_ptr;
3530 LIST_FOR_EACH_ENTRY(container_ptr, &container_list, struct mesh_container, entry)
3532 if (container_ptr->materials) {
3533 const D3DXMATERIAL *in_ptr = ID3DXBuffer_GetBufferPointer(container_ptr->materials);
3534 for (i = 0; i < container_ptr->num_materials; i++)
3536 if (in_ptr->pTextureFilename)
3537 buffer_size += strlen(in_ptr->pTextureFilename) + 1;
3538 in_ptr++;
3543 hr = D3DXCreateBuffer(buffer_size, &materials);
3544 if (FAILED(hr)) goto cleanup;
3545 out_ptr = ID3DXBuffer_GetBufferPointer(materials);
3546 strings_out_ptr = (char*)(out_ptr + num_materials);
3548 LIST_FOR_EACH_ENTRY(container_ptr, &container_list, struct mesh_container, entry)
3550 if (container_ptr->materials) {
3551 const D3DXMATERIAL *in_ptr = ID3DXBuffer_GetBufferPointer(container_ptr->materials);
3552 for (i = 0; i < container_ptr->num_materials; i++)
3554 out_ptr->MatD3D = in_ptr->MatD3D;
3555 if (in_ptr->pTextureFilename) {
3556 out_ptr->pTextureFilename = strings_out_ptr;
3557 strcpy(out_ptr->pTextureFilename, in_ptr->pTextureFilename);
3558 strings_out_ptr += strlen(in_ptr->pTextureFilename) + 1;
3560 in_ptr++;
3561 out_ptr++;
3567 if (!num_materials)
3568 num_materials = 1;
3570 if (effects_out) {
3571 generate_effects(materials, num_materials, &effects);
3572 if (!materials_out) {
3573 ID3DXBuffer_Release(materials);
3574 materials = NULL;
3578 if (adjacency_out) {
3579 if (!list_next(&container_list, list_head(&container_list))) {
3580 container_ptr = LIST_ENTRY(list_head(&container_list), struct mesh_container, entry);
3581 adjacency = container_ptr->adjacency;
3582 container_ptr->adjacency = NULL;
3583 } else {
3584 DWORD offset = 0;
3585 DWORD *out_ptr;
3587 hr = D3DXCreateBuffer(num_faces * 3 * sizeof(DWORD), &adjacency);
3588 if (FAILED(hr)) goto cleanup;
3590 out_ptr = ID3DXBuffer_GetBufferPointer(adjacency);
3591 LIST_FOR_EACH_ENTRY(container_ptr, &container_list, struct mesh_container, entry)
3593 DWORD *in_ptr = ID3DXBuffer_GetBufferPointer(container_ptr->adjacency);
3594 int count = 3 * container_ptr->mesh->lpVtbl->GetNumFaces(container_ptr->mesh);
3596 for (i = 0; i < count; i++)
3597 *out_ptr++ = offset + *in_ptr++;
3599 offset += count;
3604 *mesh_out = concat_mesh;
3605 if (adjacency_out) *adjacency_out = adjacency;
3606 if (materials_out) *materials_out = materials;
3607 if (effects_out) *effects_out = effects;
3608 if (num_materials_out) *num_materials_out = num_materials;
3610 hr = D3D_OK;
3611 cleanup:
3612 if (concat_indices) concat_mesh->lpVtbl->UnlockIndexBuffer(concat_mesh);
3613 if (concat_vertices) concat_mesh->lpVtbl->UnlockVertexBuffer(concat_mesh);
3614 if (filedata) IDirectXFileData_Release(filedata);
3615 if (enumobj) IDirectXFileEnumObject_Release(enumobj);
3616 if (dxfile) IDirectXFile_Release(dxfile);
3617 if (FAILED(hr)) {
3618 if (concat_mesh) IUnknown_Release(concat_mesh);
3619 if (materials) ID3DXBuffer_Release(materials);
3620 if (effects) ID3DXBuffer_Release(effects);
3621 if (adjacency) ID3DXBuffer_Release(adjacency);
3623 LIST_FOR_EACH_ENTRY_SAFE(container_ptr, next_container_ptr, &container_list, struct mesh_container, entry)
3625 if (container_ptr->mesh) IUnknown_Release(container_ptr->mesh);
3626 if (container_ptr->adjacency) ID3DXBuffer_Release(container_ptr->adjacency);
3627 if (container_ptr->materials) ID3DXBuffer_Release(container_ptr->materials);
3628 if (container_ptr->effects) ID3DXBuffer_Release(container_ptr->effects);
3629 HeapFree(GetProcessHeap(), 0, container_ptr);
3631 return hr;
3634 HRESULT WINAPI D3DXCreateBox(LPDIRECT3DDEVICE9 device, FLOAT width, FLOAT height,
3635 FLOAT depth, LPD3DXMESH* mesh, LPD3DXBUFFER* adjacency)
3637 FIXME("(%p, %f, %f, %f, %p, %p): stub\n", device, width, height, depth, mesh, adjacency);
3639 return E_NOTIMPL;
3642 struct vertex
3644 D3DXVECTOR3 position;
3645 D3DXVECTOR3 normal;
3648 typedef WORD face[3];
3650 struct sincos_table
3652 float *sin;
3653 float *cos;
3656 static void free_sincos_table(struct sincos_table *sincos_table)
3658 HeapFree(GetProcessHeap(), 0, sincos_table->cos);
3659 HeapFree(GetProcessHeap(), 0, sincos_table->sin);
3662 /* pre compute sine and cosine tables; caller must free */
3663 static BOOL compute_sincos_table(struct sincos_table *sincos_table, float angle_start, float angle_step, int n)
3665 float angle;
3666 int i;
3668 sincos_table->sin = HeapAlloc(GetProcessHeap(), 0, n * sizeof(*sincos_table->sin));
3669 if (!sincos_table->sin)
3671 return FALSE;
3673 sincos_table->cos = HeapAlloc(GetProcessHeap(), 0, n * sizeof(*sincos_table->cos));
3674 if (!sincos_table->cos)
3676 HeapFree(GetProcessHeap(), 0, sincos_table->sin);
3677 return FALSE;
3680 angle = angle_start;
3681 for (i = 0; i < n; i++)
3683 sincos_table->sin[i] = sin(angle);
3684 sincos_table->cos[i] = cos(angle);
3685 angle += angle_step;
3688 return TRUE;
3691 static WORD vertex_index(UINT slices, int slice, int stack)
3693 return stack*slices+slice+1;
3696 HRESULT WINAPI D3DXCreateSphere(LPDIRECT3DDEVICE9 device, FLOAT radius, UINT slices,
3697 UINT stacks, LPD3DXMESH* mesh, LPD3DXBUFFER* adjacency)
3699 DWORD number_of_vertices, number_of_faces;
3700 HRESULT hr;
3701 ID3DXMesh *sphere;
3702 struct vertex *vertices;
3703 face *faces;
3704 float phi_step, phi_start;
3705 struct sincos_table phi;
3706 float theta_step, theta, sin_theta, cos_theta;
3707 DWORD vertex, face;
3708 int slice, stack;
3710 TRACE("(%p, %f, %u, %u, %p, %p)\n", device, radius, slices, stacks, mesh, adjacency);
3712 if (!device || radius < 0.0f || slices < 2 || stacks < 2 || !mesh)
3714 return D3DERR_INVALIDCALL;
3717 if (adjacency)
3719 FIXME("Case of adjacency != NULL not implemented.\n");
3720 return E_NOTIMPL;
3723 number_of_vertices = 2 + slices * (stacks-1);
3724 number_of_faces = 2 * slices + (stacks - 2) * (2 * slices);
3726 hr = D3DXCreateMeshFVF(number_of_faces, number_of_vertices, D3DXMESH_MANAGED,
3727 D3DFVF_XYZ | D3DFVF_NORMAL, device, &sphere);
3728 if (FAILED(hr))
3730 return hr;
3733 hr = sphere->lpVtbl->LockVertexBuffer(sphere, D3DLOCK_DISCARD, (LPVOID *)&vertices);
3734 if (FAILED(hr))
3736 sphere->lpVtbl->Release(sphere);
3737 return hr;
3740 hr = sphere->lpVtbl->LockIndexBuffer(sphere, D3DLOCK_DISCARD, (LPVOID *)&faces);
3741 if (FAILED(hr))
3743 sphere->lpVtbl->UnlockVertexBuffer(sphere);
3744 sphere->lpVtbl->Release(sphere);
3745 return hr;
3748 /* phi = angle on xz plane wrt z axis */
3749 phi_step = -2 * M_PI / slices;
3750 phi_start = M_PI / 2;
3752 if (!compute_sincos_table(&phi, phi_start, phi_step, slices))
3754 sphere->lpVtbl->UnlockIndexBuffer(sphere);
3755 sphere->lpVtbl->UnlockVertexBuffer(sphere);
3756 sphere->lpVtbl->Release(sphere);
3757 return E_OUTOFMEMORY;
3760 /* theta = angle on xy plane wrt x axis */
3761 theta_step = M_PI / stacks;
3762 theta = theta_step;
3764 vertex = 0;
3765 face = 0;
3766 stack = 0;
3768 vertices[vertex].normal.x = 0.0f;
3769 vertices[vertex].normal.y = 0.0f;
3770 vertices[vertex].normal.z = 1.0f;
3771 vertices[vertex].position.x = 0.0f;
3772 vertices[vertex].position.y = 0.0f;
3773 vertices[vertex].position.z = radius;
3774 vertex++;
3776 for (stack = 0; stack < stacks - 1; stack++)
3778 sin_theta = sin(theta);
3779 cos_theta = cos(theta);
3781 for (slice = 0; slice < slices; slice++)
3783 vertices[vertex].normal.x = sin_theta * phi.cos[slice];
3784 vertices[vertex].normal.y = sin_theta * phi.sin[slice];
3785 vertices[vertex].normal.z = cos_theta;
3786 vertices[vertex].position.x = radius * sin_theta * phi.cos[slice];
3787 vertices[vertex].position.y = radius * sin_theta * phi.sin[slice];
3788 vertices[vertex].position.z = radius * cos_theta;
3789 vertex++;
3791 if (slice > 0)
3793 if (stack == 0)
3795 /* top stack is triangle fan */
3796 faces[face][0] = 0;
3797 faces[face][1] = slice + 1;
3798 faces[face][2] = slice;
3799 face++;
3801 else
3803 /* stacks in between top and bottom are quad strips */
3804 faces[face][0] = vertex_index(slices, slice-1, stack-1);
3805 faces[face][1] = vertex_index(slices, slice, stack-1);
3806 faces[face][2] = vertex_index(slices, slice-1, stack);
3807 face++;
3809 faces[face][0] = vertex_index(slices, slice, stack-1);
3810 faces[face][1] = vertex_index(slices, slice, stack);
3811 faces[face][2] = vertex_index(slices, slice-1, stack);
3812 face++;
3817 theta += theta_step;
3819 if (stack == 0)
3821 faces[face][0] = 0;
3822 faces[face][1] = 1;
3823 faces[face][2] = slice;
3824 face++;
3826 else
3828 faces[face][0] = vertex_index(slices, slice-1, stack-1);
3829 faces[face][1] = vertex_index(slices, 0, stack-1);
3830 faces[face][2] = vertex_index(slices, slice-1, stack);
3831 face++;
3833 faces[face][0] = vertex_index(slices, 0, stack-1);
3834 faces[face][1] = vertex_index(slices, 0, stack);
3835 faces[face][2] = vertex_index(slices, slice-1, stack);
3836 face++;
3840 vertices[vertex].position.x = 0.0f;
3841 vertices[vertex].position.y = 0.0f;
3842 vertices[vertex].position.z = -radius;
3843 vertices[vertex].normal.x = 0.0f;
3844 vertices[vertex].normal.y = 0.0f;
3845 vertices[vertex].normal.z = -1.0f;
3847 /* bottom stack is triangle fan */
3848 for (slice = 1; slice < slices; slice++)
3850 faces[face][0] = vertex_index(slices, slice-1, stack-1);
3851 faces[face][1] = vertex_index(slices, slice, stack-1);
3852 faces[face][2] = vertex;
3853 face++;
3856 faces[face][0] = vertex_index(slices, slice-1, stack-1);
3857 faces[face][1] = vertex_index(slices, 0, stack-1);
3858 faces[face][2] = vertex;
3860 free_sincos_table(&phi);
3861 sphere->lpVtbl->UnlockIndexBuffer(sphere);
3862 sphere->lpVtbl->UnlockVertexBuffer(sphere);
3863 *mesh = sphere;
3865 return D3D_OK;
3868 HRESULT WINAPI D3DXCreateCylinder(LPDIRECT3DDEVICE9 device, FLOAT radius1, FLOAT radius2, FLOAT length, UINT slices,
3869 UINT stacks, LPD3DXMESH* mesh, LPD3DXBUFFER* adjacency)
3871 DWORD number_of_vertices, number_of_faces;
3872 HRESULT hr;
3873 ID3DXMesh *cylinder;
3874 struct vertex *vertices;
3875 face *faces;
3876 float theta_step, theta_start;
3877 struct sincos_table theta;
3878 float delta_radius, radius, radius_step;
3879 float z, z_step, z_normal;
3880 DWORD vertex, face;
3881 int slice, stack;
3883 TRACE("(%p, %f, %f, %f, %u, %u, %p, %p)\n", device, radius1, radius2, length, slices, stacks, mesh, adjacency);
3885 if (device == NULL || radius1 < 0.0f || radius2 < 0.0f || length < 0.0f || slices < 2 || stacks < 1 || mesh == NULL)
3887 return D3DERR_INVALIDCALL;
3890 if (adjacency)
3892 FIXME("Case of adjacency != NULL not implemented.\n");
3893 return E_NOTIMPL;
3896 number_of_vertices = 2 + (slices * (3 + stacks));
3897 number_of_faces = 2 * slices + stacks * (2 * slices);
3899 hr = D3DXCreateMeshFVF(number_of_faces, number_of_vertices, D3DXMESH_MANAGED,
3900 D3DFVF_XYZ | D3DFVF_NORMAL, device, &cylinder);
3901 if (FAILED(hr))
3903 return hr;
3906 hr = cylinder->lpVtbl->LockVertexBuffer(cylinder, D3DLOCK_DISCARD, (LPVOID *)&vertices);
3907 if (FAILED(hr))
3909 cylinder->lpVtbl->Release(cylinder);
3910 return hr;
3913 hr = cylinder->lpVtbl->LockIndexBuffer(cylinder, D3DLOCK_DISCARD, (LPVOID *)&faces);
3914 if (FAILED(hr))
3916 cylinder->lpVtbl->UnlockVertexBuffer(cylinder);
3917 cylinder->lpVtbl->Release(cylinder);
3918 return hr;
3921 /* theta = angle on xy plane wrt x axis */
3922 theta_step = -2 * M_PI / slices;
3923 theta_start = M_PI / 2;
3925 if (!compute_sincos_table(&theta, theta_start, theta_step, slices))
3927 cylinder->lpVtbl->UnlockIndexBuffer(cylinder);
3928 cylinder->lpVtbl->UnlockVertexBuffer(cylinder);
3929 cylinder->lpVtbl->Release(cylinder);
3930 return E_OUTOFMEMORY;
3933 vertex = 0;
3934 face = 0;
3936 delta_radius = radius1 - radius2;
3937 radius = radius1;
3938 radius_step = delta_radius / stacks;
3940 z = -length / 2;
3941 z_step = length / stacks;
3942 z_normal = delta_radius / length;
3943 if (isnan(z_normal))
3945 z_normal = 0.0f;
3948 vertices[vertex].normal.x = 0.0f;
3949 vertices[vertex].normal.y = 0.0f;
3950 vertices[vertex].normal.z = -1.0f;
3951 vertices[vertex].position.x = 0.0f;
3952 vertices[vertex].position.y = 0.0f;
3953 vertices[vertex++].position.z = z;
3955 for (slice = 0; slice < slices; slice++, vertex++)
3957 vertices[vertex].normal.x = 0.0f;
3958 vertices[vertex].normal.y = 0.0f;
3959 vertices[vertex].normal.z = -1.0f;
3960 vertices[vertex].position.x = radius * theta.cos[slice];
3961 vertices[vertex].position.y = radius * theta.sin[slice];
3962 vertices[vertex].position.z = z;
3964 if (slice > 0)
3966 faces[face][0] = 0;
3967 faces[face][1] = slice;
3968 faces[face++][2] = slice + 1;
3972 faces[face][0] = 0;
3973 faces[face][1] = slice;
3974 faces[face++][2] = 1;
3976 for (stack = 1; stack <= stacks+1; stack++)
3978 for (slice = 0; slice < slices; slice++, vertex++)
3980 vertices[vertex].normal.x = theta.cos[slice];
3981 vertices[vertex].normal.y = theta.sin[slice];
3982 vertices[vertex].normal.z = z_normal;
3983 D3DXVec3Normalize(&vertices[vertex].normal, &vertices[vertex].normal);
3984 vertices[vertex].position.x = radius * theta.cos[slice];
3985 vertices[vertex].position.y = radius * theta.sin[slice];
3986 vertices[vertex].position.z = z;
3988 if (stack > 1 && slice > 0)
3990 faces[face][0] = vertex_index(slices, slice-1, stack-1);
3991 faces[face][1] = vertex_index(slices, slice-1, stack);
3992 faces[face++][2] = vertex_index(slices, slice, stack-1);
3994 faces[face][0] = vertex_index(slices, slice, stack-1);
3995 faces[face][1] = vertex_index(slices, slice-1, stack);
3996 faces[face++][2] = vertex_index(slices, slice, stack);
4000 if (stack > 1)
4002 faces[face][0] = vertex_index(slices, slice-1, stack-1);
4003 faces[face][1] = vertex_index(slices, slice-1, stack);
4004 faces[face++][2] = vertex_index(slices, 0, stack-1);
4006 faces[face][0] = vertex_index(slices, 0, stack-1);
4007 faces[face][1] = vertex_index(slices, slice-1, stack);
4008 faces[face++][2] = vertex_index(slices, 0, stack);
4011 if (stack < stacks + 1)
4013 z += z_step;
4014 radius -= radius_step;
4018 for (slice = 0; slice < slices; slice++, vertex++)
4020 vertices[vertex].normal.x = 0.0f;
4021 vertices[vertex].normal.y = 0.0f;
4022 vertices[vertex].normal.z = 1.0f;
4023 vertices[vertex].position.x = radius * theta.cos[slice];
4024 vertices[vertex].position.y = radius * theta.sin[slice];
4025 vertices[vertex].position.z = z;
4027 if (slice > 0)
4029 faces[face][0] = vertex_index(slices, slice-1, stack);
4030 faces[face][1] = number_of_vertices - 1;
4031 faces[face++][2] = vertex_index(slices, slice, stack);
4035 vertices[vertex].position.x = 0.0f;
4036 vertices[vertex].position.y = 0.0f;
4037 vertices[vertex].position.z = z;
4038 vertices[vertex].normal.x = 0.0f;
4039 vertices[vertex].normal.y = 0.0f;
4040 vertices[vertex].normal.z = 1.0f;
4042 faces[face][0] = vertex_index(slices, slice-1, stack);
4043 faces[face][1] = number_of_vertices - 1;
4044 faces[face][2] = vertex_index(slices, 0, stack);
4046 free_sincos_table(&theta);
4047 cylinder->lpVtbl->UnlockIndexBuffer(cylinder);
4048 cylinder->lpVtbl->UnlockVertexBuffer(cylinder);
4049 *mesh = cylinder;
4051 return D3D_OK;
4054 HRESULT WINAPI D3DXCreateTeapot(LPDIRECT3DDEVICE9 device, LPD3DXMESH *mesh, LPD3DXBUFFER* adjacency)
4056 FIXME("(%p, %p, %p): stub\n", device, mesh, adjacency);
4058 return E_NOTIMPL;
4061 HRESULT WINAPI D3DXCreateTextA(LPDIRECT3DDEVICE9 device,
4062 HDC hdc, LPCSTR text,
4063 FLOAT deviation, FLOAT extrusion,
4064 LPD3DXMESH *mesh, LPD3DXBUFFER *adjacency,
4065 LPGLYPHMETRICSFLOAT glyphmetrics)
4067 HRESULT hr;
4068 int len;
4069 LPWSTR textW;
4071 TRACE("(%p, %p, %s, %f, %f, %p, %p, %p)\n", device, hdc,
4072 debugstr_a(text), deviation, extrusion, mesh, adjacency, glyphmetrics);
4074 if (!text)
4075 return D3DERR_INVALIDCALL;
4077 len = MultiByteToWideChar(CP_ACP, 0, text, -1, NULL, 0);
4078 textW = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
4079 MultiByteToWideChar(CP_ACP, 0, text, -1, textW, len);
4081 hr = D3DXCreateTextW(device, hdc, textW, deviation, extrusion,
4082 mesh, adjacency, glyphmetrics);
4083 HeapFree(GetProcessHeap(), 0, textW);
4085 return hr;
4088 enum pointtype {
4089 POINTTYPE_CURVE = 0,
4090 POINTTYPE_CORNER,
4091 POINTTYPE_CURVE_START,
4092 POINTTYPE_CURVE_END,
4093 POINTTYPE_CURVE_MIDDLE,
4096 struct point2d
4098 D3DXVECTOR2 pos;
4099 enum pointtype corner;
4102 struct dynamic_array
4104 int count, capacity;
4105 void *items;
4108 /* is a dynamic_array */
4109 struct outline
4111 int count, capacity;
4112 struct point2d *items;
4115 /* is a dynamic_array */
4116 struct outline_array
4118 int count, capacity;
4119 struct outline *items;
4122 struct face_array
4124 int count;
4125 face *items;
4128 struct point2d_index
4130 struct outline *outline;
4131 int vertex;
4134 struct point2d_index_array
4136 int count;
4137 struct point2d_index *items;
4140 struct glyphinfo
4142 struct outline_array outlines;
4143 struct face_array faces;
4144 struct point2d_index_array ordered_vertices;
4145 float offset_x;
4148 /* is an dynamic_array */
4149 struct word_array
4151 int count, capacity;
4152 WORD *items;
4155 /* complex polygons are split into monotone polygons, which have
4156 * at most 2 intersections with the vertical sweep line */
4157 struct triangulation
4159 struct word_array vertex_stack;
4160 BOOL last_on_top, merging;
4163 /* is an dynamic_array */
4164 struct triangulation_array
4166 int count, capacity;
4167 struct triangulation *items;
4169 struct glyphinfo *glyph;
4172 static BOOL reserve(struct dynamic_array *array, int count, int itemsize)
4174 if (count > array->capacity) {
4175 void *new_buffer;
4176 int new_capacity;
4177 if (array->items && array->capacity) {
4178 new_capacity = max(array->capacity * 2, count);
4179 new_buffer = HeapReAlloc(GetProcessHeap(), 0, array->items, new_capacity * itemsize);
4180 } else {
4181 new_capacity = max(16, count);
4182 new_buffer = HeapAlloc(GetProcessHeap(), 0, new_capacity * itemsize);
4184 if (!new_buffer)
4185 return FALSE;
4186 array->items = new_buffer;
4187 array->capacity = new_capacity;
4189 return TRUE;
4192 static struct point2d *add_points(struct outline *array, int num)
4194 struct point2d *item;
4196 if (!reserve((struct dynamic_array *)array, array->count + num, sizeof(array->items[0])))
4197 return NULL;
4199 item = &array->items[array->count];
4200 array->count += num;
4201 return item;
4204 static struct outline *add_outline(struct outline_array *array)
4206 struct outline *item;
4208 if (!reserve((struct dynamic_array *)array, array->count + 1, sizeof(array->items[0])))
4209 return NULL;
4211 item = &array->items[array->count++];
4212 ZeroMemory(item, sizeof(*item));
4213 return item;
4216 static inline face *add_face(struct face_array *array)
4218 return &array->items[array->count++];
4221 static struct triangulation *add_triangulation(struct triangulation_array *array)
4223 struct triangulation *item;
4225 if (!reserve((struct dynamic_array *)array, array->count + 1, sizeof(array->items[0])))
4226 return NULL;
4228 item = &array->items[array->count++];
4229 ZeroMemory(item, sizeof(*item));
4230 return item;
4233 static HRESULT add_vertex_index(struct word_array *array, WORD vertex_index)
4235 if (!reserve((struct dynamic_array *)array, array->count + 1, sizeof(array->items[0])))
4236 return E_OUTOFMEMORY;
4238 array->items[array->count++] = vertex_index;
4239 return S_OK;
4242 /* assume fixed point numbers can be converted to float point in place */
4243 C_ASSERT(sizeof(FIXED) == sizeof(float));
4244 C_ASSERT(sizeof(POINTFX) == sizeof(D3DXVECTOR2));
4246 static inline D3DXVECTOR2 *convert_fixed_to_float(POINTFX *pt, int count, float emsquare)
4248 D3DXVECTOR2 *ret = (D3DXVECTOR2*)pt;
4249 while (count--) {
4250 D3DXVECTOR2 *pt_flt = (D3DXVECTOR2*)pt;
4251 pt_flt->x = (pt->x.value + pt->x.fract / (float)0x10000) / emsquare;
4252 pt_flt->y = (pt->y.value + pt->y.fract / (float)0x10000) / emsquare;
4253 pt++;
4255 return ret;
4258 static HRESULT add_bezier_points(struct outline *outline, const D3DXVECTOR2 *p1,
4259 const D3DXVECTOR2 *p2, const D3DXVECTOR2 *p3,
4260 float max_deviation_sq)
4262 D3DXVECTOR2 split1 = {0, 0}, split2 = {0, 0}, middle, vec;
4263 float deviation_sq;
4265 D3DXVec2Scale(&split1, D3DXVec2Add(&split1, p1, p2), 0.5f);
4266 D3DXVec2Scale(&split2, D3DXVec2Add(&split2, p2, p3), 0.5f);
4267 D3DXVec2Scale(&middle, D3DXVec2Add(&middle, &split1, &split2), 0.5f);
4269 deviation_sq = D3DXVec2LengthSq(D3DXVec2Subtract(&vec, &middle, p2));
4270 if (deviation_sq < max_deviation_sq) {
4271 struct point2d *pt = add_points(outline, 1);
4272 if (!pt) return E_OUTOFMEMORY;
4273 pt->pos = *p2;
4274 pt->corner = POINTTYPE_CURVE;
4275 /* the end point is omitted because the end line merges into the next segment of
4276 * the split bezier curve, and the end of the split bezier curve is added outside
4277 * this recursive function. */
4278 } else {
4279 HRESULT hr = add_bezier_points(outline, p1, &split1, &middle, max_deviation_sq);
4280 if (hr != S_OK) return hr;
4281 hr = add_bezier_points(outline, &middle, &split2, p3, max_deviation_sq);
4282 if (hr != S_OK) return hr;
4285 return S_OK;
4288 static inline BOOL is_direction_similar(D3DXVECTOR2 *dir1, D3DXVECTOR2 *dir2, float cos_theta)
4290 /* dot product = cos(theta) */
4291 return D3DXVec2Dot(dir1, dir2) > cos_theta;
4294 static inline D3DXVECTOR2 *unit_vec2(D3DXVECTOR2 *dir, const D3DXVECTOR2 *pt1, const D3DXVECTOR2 *pt2)
4296 return D3DXVec2Normalize(D3DXVec2Subtract(dir, pt2, pt1), dir);
4299 struct cos_table
4301 float cos_half;
4302 float cos_45;
4303 float cos_90;
4306 static BOOL attempt_line_merge(struct outline *outline,
4307 int pt_index,
4308 const D3DXVECTOR2 *nextpt,
4309 BOOL to_curve,
4310 const struct cos_table *table)
4312 D3DXVECTOR2 curdir, lastdir;
4313 struct point2d *prevpt, *pt;
4314 BOOL ret = FALSE;
4316 pt = &outline->items[pt_index];
4317 pt_index = (pt_index - 1 + outline->count) % outline->count;
4318 prevpt = &outline->items[pt_index];
4320 if (to_curve)
4321 pt->corner = pt->corner != POINTTYPE_CORNER ? POINTTYPE_CURVE_MIDDLE : POINTTYPE_CURVE_START;
4323 if (outline->count < 2)
4324 return FALSE;
4326 /* remove last point if the next line continues the last line */
4327 unit_vec2(&lastdir, &prevpt->pos, &pt->pos);
4328 unit_vec2(&curdir, &pt->pos, nextpt);
4329 if (is_direction_similar(&lastdir, &curdir, table->cos_half))
4331 outline->count--;
4332 if (pt->corner == POINTTYPE_CURVE_END)
4333 prevpt->corner = pt->corner;
4334 if (prevpt->corner == POINTTYPE_CURVE_END && to_curve)
4335 prevpt->corner = POINTTYPE_CURVE_MIDDLE;
4336 pt = prevpt;
4338 ret = TRUE;
4339 if (outline->count < 2)
4340 return ret;
4342 pt_index = (pt_index - 1 + outline->count) % outline->count;
4343 prevpt = &outline->items[pt_index];
4344 unit_vec2(&lastdir, &prevpt->pos, &pt->pos);
4345 unit_vec2(&curdir, &pt->pos, nextpt);
4347 return ret;
4350 static HRESULT create_outline(struct glyphinfo *glyph, void *raw_outline, int datasize,
4351 float max_deviation_sq, float emsquare, const struct cos_table *cos_table)
4353 TTPOLYGONHEADER *header = (TTPOLYGONHEADER *)raw_outline;
4355 while ((char *)header < (char *)raw_outline + datasize)
4357 TTPOLYCURVE *curve = (TTPOLYCURVE *)(header + 1);
4358 struct point2d *lastpt, *pt;
4359 D3DXVECTOR2 lastdir;
4360 D3DXVECTOR2 *pt_flt;
4361 int j;
4362 struct outline *outline = add_outline(&glyph->outlines);
4364 if (!outline)
4365 return E_OUTOFMEMORY;
4367 pt = add_points(outline, 1);
4368 if (!pt)
4369 return E_OUTOFMEMORY;
4370 pt_flt = convert_fixed_to_float(&header->pfxStart, 1, emsquare);
4371 pt->pos = *pt_flt;
4372 pt->corner = POINTTYPE_CORNER;
4374 if (header->dwType != TT_POLYGON_TYPE)
4375 FIXME("Unknown header type %d\n", header->dwType);
4377 while ((char *)curve < (char *)header + header->cb)
4379 D3DXVECTOR2 bezier_start = outline->items[outline->count - 1].pos;
4380 BOOL to_curve = curve->wType != TT_PRIM_LINE && curve->cpfx > 1;
4382 if (!curve->cpfx) {
4383 curve = (TTPOLYCURVE *)&curve->apfx[curve->cpfx];
4384 continue;
4387 pt_flt = convert_fixed_to_float(curve->apfx, curve->cpfx, emsquare);
4389 attempt_line_merge(outline, outline->count - 1, &pt_flt[0], to_curve, cos_table);
4391 if (to_curve)
4393 HRESULT hr;
4394 int count = curve->cpfx;
4395 j = 0;
4397 while (count > 2)
4399 D3DXVECTOR2 bezier_end;
4401 D3DXVec2Scale(&bezier_end, D3DXVec2Add(&bezier_end, &pt_flt[j], &pt_flt[j+1]), 0.5f);
4402 hr = add_bezier_points(outline, &bezier_start, &pt_flt[j], &bezier_end, max_deviation_sq);
4403 if (hr != S_OK)
4404 return hr;
4405 bezier_start = bezier_end;
4406 count--;
4407 j++;
4409 hr = add_bezier_points(outline, &bezier_start, &pt_flt[j], &pt_flt[j+1], max_deviation_sq);
4410 if (hr != S_OK)
4411 return hr;
4413 pt = add_points(outline, 1);
4414 if (!pt)
4415 return E_OUTOFMEMORY;
4416 j++;
4417 pt->pos = pt_flt[j];
4418 pt->corner = POINTTYPE_CURVE_END;
4419 } else {
4420 pt = add_points(outline, curve->cpfx);
4421 if (!pt)
4422 return E_OUTOFMEMORY;
4423 for (j = 0; j < curve->cpfx; j++)
4425 pt->pos = pt_flt[j];
4426 pt->corner = POINTTYPE_CORNER;
4427 pt++;
4431 curve = (TTPOLYCURVE *)&curve->apfx[curve->cpfx];
4434 /* remove last point if the next line continues the last line */
4435 if (outline->count >= 3) {
4436 BOOL to_curve;
4438 lastpt = &outline->items[outline->count - 1];
4439 pt = &outline->items[0];
4440 if (pt->pos.x == lastpt->pos.x && pt->pos.y == lastpt->pos.y) {
4441 if (lastpt->corner == POINTTYPE_CURVE_END)
4443 if (pt->corner == POINTTYPE_CURVE_START)
4444 pt->corner = POINTTYPE_CURVE_MIDDLE;
4445 else
4446 pt->corner = POINTTYPE_CURVE_END;
4448 outline->count--;
4449 lastpt = &outline->items[outline->count - 1];
4450 } else {
4451 /* outline closed with a line from end to start point */
4452 attempt_line_merge(outline, outline->count - 1, &pt->pos, FALSE, cos_table);
4454 lastpt = &outline->items[0];
4455 to_curve = lastpt->corner != POINTTYPE_CORNER && lastpt->corner != POINTTYPE_CURVE_END;
4456 if (lastpt->corner == POINTTYPE_CURVE_START)
4457 lastpt->corner = POINTTYPE_CORNER;
4458 pt = &outline->items[1];
4459 if (attempt_line_merge(outline, 0, &pt->pos, to_curve, cos_table))
4460 *lastpt = outline->items[outline->count];
4463 lastpt = &outline->items[outline->count - 1];
4464 pt = &outline->items[0];
4465 unit_vec2(&lastdir, &lastpt->pos, &pt->pos);
4466 for (j = 0; j < outline->count; j++)
4468 D3DXVECTOR2 curdir;
4470 lastpt = pt;
4471 pt = &outline->items[(j + 1) % outline->count];
4472 unit_vec2(&curdir, &lastpt->pos, &pt->pos);
4474 switch (lastpt->corner)
4476 case POINTTYPE_CURVE_START:
4477 case POINTTYPE_CURVE_END:
4478 if (!is_direction_similar(&lastdir, &curdir, cos_table->cos_45))
4479 lastpt->corner = POINTTYPE_CORNER;
4480 break;
4481 case POINTTYPE_CURVE_MIDDLE:
4482 if (!is_direction_similar(&lastdir, &curdir, cos_table->cos_90))
4483 lastpt->corner = POINTTYPE_CORNER;
4484 else
4485 lastpt->corner = POINTTYPE_CURVE;
4486 break;
4487 default:
4488 break;
4490 lastdir = curdir;
4493 header = (TTPOLYGONHEADER *)((char *)header + header->cb);
4495 return S_OK;
4498 /* Get the y-distance from a line to a point */
4499 static float get_line_to_point_y_distance(D3DXVECTOR2 *line_pt1,
4500 D3DXVECTOR2 *line_pt2,
4501 D3DXVECTOR2 *point)
4503 D3DXVECTOR2 line_vec = {0, 0};
4504 float line_pt_dx;
4505 float line_y;
4507 D3DXVec2Subtract(&line_vec, line_pt2, line_pt1);
4508 line_pt_dx = point->x - line_pt1->x;
4509 line_y = line_pt1->y + (line_vec.y * line_pt_dx) / line_vec.x;
4510 return point->y - line_y;
4513 static D3DXVECTOR2 *get_indexed_point(struct point2d_index *pt_idx)
4515 return &pt_idx->outline->items[pt_idx->vertex].pos;
4518 static D3DXVECTOR2 *get_ordered_vertex(struct glyphinfo *glyph, WORD index)
4520 return get_indexed_point(&glyph->ordered_vertices.items[index]);
4523 static void remove_triangulation(struct triangulation_array *array, struct triangulation *item)
4525 HeapFree(GetProcessHeap(), 0, item->vertex_stack.items);
4526 MoveMemory(item, item + 1, (char*)&array->items[array->count] - (char*)(item + 1));
4527 array->count--;
4530 static HRESULT triangulation_add_point(struct triangulation **t_ptr,
4531 struct triangulation_array *triangulations,
4532 WORD vtx_idx,
4533 BOOL to_top)
4535 struct glyphinfo *glyph = triangulations->glyph;
4536 struct triangulation *t = *t_ptr;
4537 HRESULT hr;
4538 face *face;
4539 int f1, f2;
4541 if (t->last_on_top) {
4542 f1 = 1;
4543 f2 = 2;
4544 } else {
4545 f1 = 2;
4546 f2 = 1;
4549 if (t->last_on_top != to_top && t->vertex_stack.count > 1) {
4550 /* consume all vertices on the stack */
4551 WORD last_pt = t->vertex_stack.items[0];
4552 int i;
4553 for (i = 1; i < t->vertex_stack.count; i++)
4555 face = add_face(&glyph->faces);
4556 if (!face) return E_OUTOFMEMORY;
4557 (*face)[0] = vtx_idx;
4558 (*face)[f1] = last_pt;
4559 (*face)[f2] = last_pt = t->vertex_stack.items[i];
4561 t->vertex_stack.items[0] = last_pt;
4562 t->vertex_stack.count = 1;
4563 } else if (t->vertex_stack.count > 1) {
4564 int i = t->vertex_stack.count - 1;
4565 D3DXVECTOR2 *point = get_ordered_vertex(glyph, vtx_idx);
4566 WORD top_idx = t->vertex_stack.items[i--];
4567 D3DXVECTOR2 *top_pt = get_ordered_vertex(glyph, top_idx);
4569 while (i >= 0)
4571 WORD prev_idx = t->vertex_stack.items[i--];
4572 D3DXVECTOR2 *prev_pt = get_ordered_vertex(glyph, prev_idx);
4574 if (prev_pt->x != top_pt->x &&
4575 ((to_top && get_line_to_point_y_distance(prev_pt, top_pt, point) > 0) ||
4576 (!to_top && get_line_to_point_y_distance(prev_pt, top_pt, point) < 0)))
4577 break;
4579 face = add_face(&glyph->faces);
4580 if (!face) return E_OUTOFMEMORY;
4581 (*face)[0] = vtx_idx;
4582 (*face)[f1] = prev_idx;
4583 (*face)[f2] = top_idx;
4585 top_pt = prev_pt;
4586 top_idx = prev_idx;
4587 t->vertex_stack.count--;
4590 t->last_on_top = to_top;
4592 hr = add_vertex_index(&t->vertex_stack, vtx_idx);
4594 if (hr == S_OK && t->merging) {
4595 struct triangulation *t2;
4597 t2 = to_top ? t - 1 : t + 1;
4598 t2->merging = FALSE;
4599 hr = triangulation_add_point(&t2, triangulations, vtx_idx, to_top);
4600 if (hr != S_OK) return hr;
4601 remove_triangulation(triangulations, t);
4602 if (t2 > t)
4603 t2--;
4604 *t_ptr = t2;
4606 return hr;
4609 /* check if the point is next on the outline for either the top or bottom */
4610 static D3DXVECTOR2 *triangulation_get_next_point(struct triangulation *t, struct glyphinfo *glyph, BOOL on_top)
4612 int i = t->last_on_top == on_top ? t->vertex_stack.count - 1 : 0;
4613 WORD idx = t->vertex_stack.items[i];
4614 struct point2d_index *pt_idx = &glyph->ordered_vertices.items[idx];
4615 struct outline *outline = pt_idx->outline;
4617 if (on_top)
4618 i = (pt_idx->vertex + outline->count - 1) % outline->count;
4619 else
4620 i = (pt_idx->vertex + 1) % outline->count;
4622 return &outline->items[i].pos;
4625 static int compare_vertex_indices(const void *a, const void *b)
4627 const struct point2d_index *idx1 = a, *idx2 = b;
4628 const D3DXVECTOR2 *p1 = &idx1->outline->items[idx1->vertex].pos;
4629 const D3DXVECTOR2 *p2 = &idx2->outline->items[idx2->vertex].pos;
4630 float diff = p1->x - p2->x;
4632 if (diff == 0.0f)
4633 diff = p1->y - p2->y;
4635 return diff == 0.0f ? 0 : (diff > 0.0f ? -1 : 1);
4638 static HRESULT triangulate(struct triangulation_array *triangulations)
4640 int sweep_idx;
4641 HRESULT hr;
4642 struct glyphinfo *glyph = triangulations->glyph;
4643 int nb_vertices = 0;
4644 int i;
4645 struct point2d_index *idx_ptr;
4647 for (i = 0; i < glyph->outlines.count; i++)
4648 nb_vertices += glyph->outlines.items[i].count;
4650 glyph->ordered_vertices.items = HeapAlloc(GetProcessHeap(), 0,
4651 nb_vertices * sizeof(*glyph->ordered_vertices.items));
4652 if (!glyph->ordered_vertices.items)
4653 return E_OUTOFMEMORY;
4655 idx_ptr = glyph->ordered_vertices.items;
4656 for (i = 0; i < glyph->outlines.count; i++)
4658 struct outline *outline = &glyph->outlines.items[i];
4659 int j;
4661 idx_ptr->outline = outline;
4662 idx_ptr->vertex = 0;
4663 idx_ptr++;
4664 for (j = outline->count - 1; j > 0; j--)
4666 idx_ptr->outline = outline;
4667 idx_ptr->vertex = j;
4668 idx_ptr++;
4671 glyph->ordered_vertices.count = nb_vertices;
4673 /* Native implementation seems to try to create a triangle fan from
4674 * the first outline point if the glyph only has one outline. */
4675 if (glyph->outlines.count == 1)
4677 struct outline *outline = glyph->outlines.items;
4678 D3DXVECTOR2 *base = &outline->items[0].pos;
4679 D3DXVECTOR2 *last = &outline->items[1].pos;
4680 float ccw = 0;
4682 for (i = 2; i < outline->count; i++)
4684 D3DXVECTOR2 *next = &outline->items[i].pos;
4685 D3DXVECTOR2 v1 = {0.0f, 0.0f};
4686 D3DXVECTOR2 v2 = {0.0f, 0.0f};
4688 D3DXVec2Subtract(&v1, base, last);
4689 D3DXVec2Subtract(&v2, last, next);
4690 ccw = D3DXVec2CCW(&v1, &v2);
4691 if (ccw > 0.0f)
4692 break;
4694 last = next;
4696 if (ccw <= 0)
4698 glyph->faces.items = HeapAlloc(GetProcessHeap(), 0,
4699 (outline->count - 2) * sizeof(glyph->faces.items[0]));
4700 if (!glyph->faces.items)
4701 return E_OUTOFMEMORY;
4703 glyph->faces.count = outline->count - 2;
4704 for (i = 0; i < glyph->faces.count; i++)
4706 glyph->faces.items[i][0] = 0;
4707 glyph->faces.items[i][1] = i + 1;
4708 glyph->faces.items[i][2] = i + 2;
4710 return S_OK;
4714 /* Perform 2D polygon triangulation for complex glyphs.
4715 * Triangulation is performed using a sweep line concept, from right to left,
4716 * by processing vertices in sorted order. Complex polygons are split into
4717 * monotone polygons which are triangulated separately. */
4718 /* FIXME: The order of the faces is not consistent with the native implementation. */
4720 /* Reserve space for maximum possible faces from triangulation.
4721 * # faces for outer outlines = outline->count - 2
4722 * # faces for inner outlines = outline->count + 2
4723 * There must be at least 1 outer outline. */
4724 glyph->faces.items = HeapAlloc(GetProcessHeap(), 0,
4725 (nb_vertices + glyph->outlines.count * 2 - 4) * sizeof(glyph->faces.items[0]));
4726 if (!glyph->faces.items)
4727 return E_OUTOFMEMORY;
4729 qsort(glyph->ordered_vertices.items, nb_vertices,
4730 sizeof(glyph->ordered_vertices.items[0]), compare_vertex_indices);
4731 for (sweep_idx = 0; sweep_idx < glyph->ordered_vertices.count; sweep_idx++)
4733 int start = 0;
4734 int end = triangulations->count;
4736 while (start < end)
4738 D3DXVECTOR2 *sweep_vtx = get_ordered_vertex(glyph, sweep_idx);
4739 int current = (start + end) / 2;
4740 struct triangulation *t = &triangulations->items[current];
4741 BOOL on_top_outline = FALSE;
4742 D3DXVECTOR2 *top_next, *bottom_next;
4743 WORD top_idx, bottom_idx;
4745 if (t->merging && t->last_on_top)
4746 top_next = triangulation_get_next_point(t + 1, glyph, TRUE);
4747 else
4748 top_next = triangulation_get_next_point(t, glyph, TRUE);
4749 if (sweep_vtx == top_next)
4751 if (t->merging && t->last_on_top)
4752 t++;
4753 hr = triangulation_add_point(&t, triangulations, sweep_idx, TRUE);
4754 if (hr != S_OK) return hr;
4756 if (t + 1 < &triangulations->items[triangulations->count] &&
4757 triangulation_get_next_point(t + 1, glyph, FALSE) == sweep_vtx)
4759 /* point also on bottom outline of higher triangulation */
4760 struct triangulation *t2 = t + 1;
4761 hr = triangulation_add_point(&t2, triangulations, sweep_idx, FALSE);
4762 if (hr != S_OK) return hr;
4764 t->merging = TRUE;
4765 t2->merging = TRUE;
4767 on_top_outline = TRUE;
4770 if (t->merging && !t->last_on_top)
4771 bottom_next = triangulation_get_next_point(t - 1, glyph, FALSE);
4772 else
4773 bottom_next = triangulation_get_next_point(t, glyph, FALSE);
4774 if (sweep_vtx == bottom_next)
4776 if (t->merging && !t->last_on_top)
4777 t--;
4778 if (on_top_outline) {
4779 /* outline finished */
4780 remove_triangulation(triangulations, t);
4781 break;
4784 hr = triangulation_add_point(&t, triangulations, sweep_idx, FALSE);
4785 if (hr != S_OK) return hr;
4787 if (t > triangulations->items &&
4788 triangulation_get_next_point(t - 1, glyph, TRUE) == sweep_vtx)
4790 struct triangulation *t2 = t - 1;
4791 /* point also on top outline of lower triangulation */
4792 hr = triangulation_add_point(&t2, triangulations, sweep_idx, TRUE);
4793 if (hr != S_OK) return hr;
4794 t = t2 + 1; /* t may be invalidated by triangulation merging */
4796 t->merging = TRUE;
4797 t2->merging = TRUE;
4799 break;
4801 if (on_top_outline)
4802 break;
4804 if (t->last_on_top) {
4805 top_idx = t->vertex_stack.items[t->vertex_stack.count - 1];
4806 bottom_idx = t->vertex_stack.items[0];
4807 } else {
4808 top_idx = t->vertex_stack.items[0];
4809 bottom_idx = t->vertex_stack.items[t->vertex_stack.count - 1];
4812 /* check if the point is inside or outside this polygon */
4813 if (get_line_to_point_y_distance(get_ordered_vertex(glyph, top_idx),
4814 top_next, sweep_vtx) > 0)
4815 { /* above */
4816 start = current + 1;
4817 } else if (get_line_to_point_y_distance(get_ordered_vertex(glyph, bottom_idx),
4818 bottom_next, sweep_vtx) < 0)
4819 { /* below */
4820 end = current;
4821 } else if (t->merging) {
4822 /* inside, so cancel merging */
4823 struct triangulation *t2 = t->last_on_top ? t + 1 : t - 1;
4824 t->merging = FALSE;
4825 t2->merging = FALSE;
4826 hr = triangulation_add_point(&t, triangulations, sweep_idx, t->last_on_top);
4827 if (hr != S_OK) return hr;
4828 hr = triangulation_add_point(&t2, triangulations, sweep_idx, t2->last_on_top);
4829 if (hr != S_OK) return hr;
4830 break;
4831 } else {
4832 /* inside, so split polygon into two monotone parts */
4833 struct triangulation *t2 = add_triangulation(triangulations);
4834 if (!t2) return E_OUTOFMEMORY;
4835 MoveMemory(t + 1, t, (char*)(t2 + 1) - (char*)t);
4836 if (t->last_on_top) {
4837 t2 = t + 1;
4838 } else {
4839 t2 = t;
4840 t++;
4843 ZeroMemory(&t2->vertex_stack, sizeof(t2->vertex_stack));
4844 hr = add_vertex_index(&t2->vertex_stack, t->vertex_stack.items[t->vertex_stack.count - 1]);
4845 if (hr != S_OK) return hr;
4846 hr = add_vertex_index(&t2->vertex_stack, sweep_idx);
4847 if (hr != S_OK) return hr;
4848 t2->last_on_top = !t->last_on_top;
4850 hr = triangulation_add_point(&t, triangulations, sweep_idx, t->last_on_top);
4851 if (hr != S_OK) return hr;
4852 break;
4855 if (start >= end)
4857 struct triangulation *t;
4858 struct triangulation *t2 = add_triangulation(triangulations);
4859 if (!t2) return E_OUTOFMEMORY;
4860 t = &triangulations->items[start];
4861 MoveMemory(t + 1, t, (char*)(t2 + 1) - (char*)t);
4862 ZeroMemory(t, sizeof(*t));
4863 hr = add_vertex_index(&t->vertex_stack, sweep_idx);
4864 if (hr != S_OK) return hr;
4867 return S_OK;
4870 HRESULT WINAPI D3DXCreateTextW(LPDIRECT3DDEVICE9 device,
4871 HDC hdc, LPCWSTR text,
4872 FLOAT deviation, FLOAT extrusion,
4873 LPD3DXMESH *mesh_ptr, LPD3DXBUFFER *adjacency,
4874 LPGLYPHMETRICSFLOAT glyphmetrics)
4876 HRESULT hr;
4877 ID3DXMesh *mesh = NULL;
4878 DWORD nb_vertices, nb_faces;
4879 DWORD nb_front_faces, nb_corners, nb_outline_points;
4880 struct vertex *vertices = NULL;
4881 face *faces = NULL;
4882 int textlen = 0;
4883 float offset_x;
4884 LOGFONTW lf;
4885 OUTLINETEXTMETRICW otm;
4886 HFONT font = NULL, oldfont = NULL;
4887 const MAT2 identity = {{0, 1}, {0, 0}, {0, 0}, {0, 1}};
4888 void *raw_outline = NULL;
4889 int bufsize = 0;
4890 struct glyphinfo *glyphs = NULL;
4891 GLYPHMETRICS gm;
4892 struct triangulation_array triangulations = {0, 0, NULL};
4893 int i;
4894 struct vertex *vertex_ptr;
4895 face *face_ptr;
4896 float max_deviation_sq;
4897 const struct cos_table cos_table = {
4898 cos(D3DXToRadian(0.5f)),
4899 cos(D3DXToRadian(45.0f)),
4900 cos(D3DXToRadian(90.0f)),
4902 int f1, f2;
4904 TRACE("(%p, %p, %s, %f, %f, %p, %p, %p)\n", device, hdc,
4905 debugstr_w(text), deviation, extrusion, mesh_ptr, adjacency, glyphmetrics);
4907 if (!device || !hdc || !text || !*text || deviation < 0.0f || extrusion < 0.0f || !mesh_ptr)
4908 return D3DERR_INVALIDCALL;
4910 if (adjacency)
4912 FIXME("Case of adjacency != NULL not implemented.\n");
4913 return E_NOTIMPL;
4916 if (!GetObjectW(GetCurrentObject(hdc, OBJ_FONT), sizeof(lf), &lf) ||
4917 !GetOutlineTextMetricsW(hdc, sizeof(otm), &otm))
4919 return D3DERR_INVALIDCALL;
4922 if (deviation == 0.0f)
4923 deviation = 1.0f / otm.otmEMSquare;
4924 max_deviation_sq = deviation * deviation;
4926 lf.lfHeight = otm.otmEMSquare;
4927 lf.lfWidth = 0;
4928 font = CreateFontIndirectW(&lf);
4929 if (!font) {
4930 hr = E_OUTOFMEMORY;
4931 goto error;
4933 oldfont = SelectObject(hdc, font);
4935 textlen = strlenW(text);
4936 for (i = 0; i < textlen; i++)
4938 int datasize = GetGlyphOutlineW(hdc, text[i], GGO_NATIVE, &gm, 0, NULL, &identity);
4939 if (datasize < 0)
4940 return D3DERR_INVALIDCALL;
4941 if (bufsize < datasize)
4942 bufsize = datasize;
4944 if (!bufsize) { /* e.g. text == " " */
4945 hr = D3DERR_INVALIDCALL;
4946 goto error;
4949 glyphs = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, textlen * sizeof(*glyphs));
4950 raw_outline = HeapAlloc(GetProcessHeap(), 0, bufsize);
4951 if (!glyphs || !raw_outline) {
4952 hr = E_OUTOFMEMORY;
4953 goto error;
4956 offset_x = 0.0f;
4957 for (i = 0; i < textlen; i++)
4959 /* get outline points from data returned from GetGlyphOutline */
4960 int datasize;
4962 glyphs[i].offset_x = offset_x;
4964 datasize = GetGlyphOutlineW(hdc, text[i], GGO_NATIVE, &gm, bufsize, raw_outline, &identity);
4965 hr = create_outline(&glyphs[i], raw_outline, datasize,
4966 max_deviation_sq, otm.otmEMSquare, &cos_table);
4967 if (hr != S_OK) goto error;
4969 triangulations.glyph = &glyphs[i];
4970 hr = triangulate(&triangulations);
4971 if (hr != S_OK) goto error;
4972 if (triangulations.count) {
4973 ERR("%d incomplete triangulations of glyph (%u).\n", triangulations.count, text[i]);
4974 triangulations.count = 0;
4977 if (glyphmetrics)
4979 glyphmetrics[i].gmfBlackBoxX = gm.gmBlackBoxX / (float)otm.otmEMSquare;
4980 glyphmetrics[i].gmfBlackBoxY = gm.gmBlackBoxY / (float)otm.otmEMSquare;
4981 glyphmetrics[i].gmfptGlyphOrigin.x = gm.gmptGlyphOrigin.x / (float)otm.otmEMSquare;
4982 glyphmetrics[i].gmfptGlyphOrigin.y = gm.gmptGlyphOrigin.y / (float)otm.otmEMSquare;
4983 glyphmetrics[i].gmfCellIncX = gm.gmCellIncX / (float)otm.otmEMSquare;
4984 glyphmetrics[i].gmfCellIncY = gm.gmCellIncY / (float)otm.otmEMSquare;
4986 offset_x += gm.gmCellIncX / (float)otm.otmEMSquare;
4989 /* corner points need an extra vertex for the different side faces normals */
4990 nb_corners = 0;
4991 nb_outline_points = 0;
4992 nb_front_faces = 0;
4993 for (i = 0; i < textlen; i++)
4995 int j;
4996 nb_outline_points += glyphs[i].ordered_vertices.count;
4997 nb_front_faces += glyphs[i].faces.count;
4998 for (j = 0; j < glyphs[i].outlines.count; j++)
5000 int k;
5001 struct outline *outline = &glyphs[i].outlines.items[j];
5002 nb_corners++; /* first outline point always repeated as a corner */
5003 for (k = 1; k < outline->count; k++)
5004 if (outline->items[k].corner)
5005 nb_corners++;
5009 nb_vertices = (nb_outline_points + nb_corners) * 2 + nb_outline_points * 2;
5010 nb_faces = nb_outline_points * 2 + nb_front_faces * 2;
5013 hr = D3DXCreateMeshFVF(nb_faces, nb_vertices, D3DXMESH_MANAGED,
5014 D3DFVF_XYZ | D3DFVF_NORMAL, device, &mesh);
5015 if (FAILED(hr))
5016 goto error;
5018 hr = mesh->lpVtbl->LockVertexBuffer(mesh, D3DLOCK_DISCARD, (LPVOID *)&vertices);
5019 if (FAILED(hr))
5020 goto error;
5022 hr = mesh->lpVtbl->LockIndexBuffer(mesh, D3DLOCK_DISCARD, (LPVOID *)&faces);
5023 if (FAILED(hr))
5024 goto error;
5026 /* convert 2D vertices and faces into 3D mesh */
5027 vertex_ptr = vertices;
5028 face_ptr = faces;
5029 if (extrusion == 0.0f) {
5030 f1 = 1;
5031 f2 = 2;
5032 } else {
5033 f1 = 2;
5034 f2 = 1;
5036 for (i = 0; i < textlen; i++)
5038 int j;
5039 int count;
5040 struct vertex *back_vertices;
5041 face *back_faces;
5043 /* side vertices and faces */
5044 for (j = 0; j < glyphs[i].outlines.count; j++)
5046 struct vertex *outline_vertices = vertex_ptr;
5047 struct outline *outline = &glyphs[i].outlines.items[j];
5048 int k;
5049 struct point2d *prevpt = &outline->items[outline->count - 1];
5050 struct point2d *pt = &outline->items[0];
5052 for (k = 1; k <= outline->count; k++)
5054 struct vertex vtx;
5055 struct point2d *nextpt = &outline->items[k % outline->count];
5056 WORD vtx_idx = vertex_ptr - vertices;
5057 D3DXVECTOR2 vec;
5059 if (pt->corner == POINTTYPE_CURVE_START)
5060 D3DXVec2Subtract(&vec, &pt->pos, &prevpt->pos);
5061 else if (pt->corner)
5062 D3DXVec2Subtract(&vec, &nextpt->pos, &pt->pos);
5063 else
5064 D3DXVec2Subtract(&vec, &nextpt->pos, &prevpt->pos);
5065 D3DXVec2Normalize(&vec, &vec);
5066 vtx.normal.x = -vec.y;
5067 vtx.normal.y = vec.x;
5068 vtx.normal.z = 0;
5070 vtx.position.x = pt->pos.x + glyphs[i].offset_x;
5071 vtx.position.y = pt->pos.y;
5072 vtx.position.z = 0;
5073 *vertex_ptr++ = vtx;
5075 vtx.position.z = -extrusion;
5076 *vertex_ptr++ = vtx;
5078 vtx.position.x = nextpt->pos.x + glyphs[i].offset_x;
5079 vtx.position.y = nextpt->pos.y;
5080 if (pt->corner && nextpt->corner && nextpt->corner != POINTTYPE_CURVE_END) {
5081 vtx.position.z = -extrusion;
5082 *vertex_ptr++ = vtx;
5083 vtx.position.z = 0;
5084 *vertex_ptr++ = vtx;
5086 (*face_ptr)[0] = vtx_idx;
5087 (*face_ptr)[1] = vtx_idx + 2;
5088 (*face_ptr)[2] = vtx_idx + 1;
5089 face_ptr++;
5091 (*face_ptr)[0] = vtx_idx;
5092 (*face_ptr)[1] = vtx_idx + 3;
5093 (*face_ptr)[2] = vtx_idx + 2;
5094 face_ptr++;
5095 } else {
5096 if (nextpt->corner) {
5097 if (nextpt->corner == POINTTYPE_CURVE_END) {
5098 D3DXVECTOR2 *nextpt2 = &outline->items[(k + 1) % outline->count].pos;
5099 D3DXVec2Subtract(&vec, nextpt2, &nextpt->pos);
5100 } else {
5101 D3DXVec2Subtract(&vec, &nextpt->pos, &pt->pos);
5103 D3DXVec2Normalize(&vec, &vec);
5104 vtx.normal.x = -vec.y;
5105 vtx.normal.y = vec.x;
5107 vtx.position.z = 0;
5108 *vertex_ptr++ = vtx;
5109 vtx.position.z = -extrusion;
5110 *vertex_ptr++ = vtx;
5113 (*face_ptr)[0] = vtx_idx;
5114 (*face_ptr)[1] = vtx_idx + 3;
5115 (*face_ptr)[2] = vtx_idx + 1;
5116 face_ptr++;
5118 (*face_ptr)[0] = vtx_idx;
5119 (*face_ptr)[1] = vtx_idx + 2;
5120 (*face_ptr)[2] = vtx_idx + 3;
5121 face_ptr++;
5124 prevpt = pt;
5125 pt = nextpt;
5127 if (!pt->corner) {
5128 *vertex_ptr++ = *outline_vertices++;
5129 *vertex_ptr++ = *outline_vertices++;
5133 /* back vertices and faces */
5134 back_faces = face_ptr;
5135 back_vertices = vertex_ptr;
5136 for (j = 0; j < glyphs[i].ordered_vertices.count; j++)
5138 D3DXVECTOR2 *pt = get_ordered_vertex(&glyphs[i], j);
5139 vertex_ptr->position.x = pt->x + glyphs[i].offset_x;
5140 vertex_ptr->position.y = pt->y;
5141 vertex_ptr->position.z = 0;
5142 vertex_ptr->normal.x = 0;
5143 vertex_ptr->normal.y = 0;
5144 vertex_ptr->normal.z = 1;
5145 vertex_ptr++;
5147 count = back_vertices - vertices;
5148 for (j = 0; j < glyphs[i].faces.count; j++)
5150 face *f = &glyphs[i].faces.items[j];
5151 (*face_ptr)[0] = (*f)[0] + count;
5152 (*face_ptr)[1] = (*f)[1] + count;
5153 (*face_ptr)[2] = (*f)[2] + count;
5154 face_ptr++;
5157 /* front vertices and faces */
5158 j = count = vertex_ptr - back_vertices;
5159 while (j--)
5161 vertex_ptr->position.x = back_vertices->position.x;
5162 vertex_ptr->position.y = back_vertices->position.y;
5163 vertex_ptr->position.z = -extrusion;
5164 vertex_ptr->normal.x = 0;
5165 vertex_ptr->normal.y = 0;
5166 vertex_ptr->normal.z = extrusion == 0.0f ? 1.0f : -1.0f;
5167 vertex_ptr++;
5168 back_vertices++;
5170 j = face_ptr - back_faces;
5171 while (j--)
5173 (*face_ptr)[0] = (*back_faces)[0] + count;
5174 (*face_ptr)[1] = (*back_faces)[f1] + count;
5175 (*face_ptr)[2] = (*back_faces)[f2] + count;
5176 face_ptr++;
5177 back_faces++;
5181 *mesh_ptr = mesh;
5182 hr = D3D_OK;
5183 error:
5184 if (mesh) {
5185 if (faces) mesh->lpVtbl->UnlockIndexBuffer(mesh);
5186 if (vertices) mesh->lpVtbl->UnlockVertexBuffer(mesh);
5187 if (hr != D3D_OK) mesh->lpVtbl->Release(mesh);
5189 if (glyphs) {
5190 for (i = 0; i < textlen; i++)
5192 int j;
5193 for (j = 0; j < glyphs[i].outlines.count; j++)
5194 HeapFree(GetProcessHeap(), 0, glyphs[i].outlines.items[j].items);
5195 HeapFree(GetProcessHeap(), 0, glyphs[i].outlines.items);
5196 HeapFree(GetProcessHeap(), 0, glyphs[i].faces.items);
5197 HeapFree(GetProcessHeap(), 0, glyphs[i].ordered_vertices.items);
5199 HeapFree(GetProcessHeap(), 0, glyphs);
5201 if (triangulations.items) {
5202 int i;
5203 for (i = 0; i < triangulations.count; i++)
5204 HeapFree(GetProcessHeap(), 0, triangulations.items[i].vertex_stack.items);
5205 HeapFree(GetProcessHeap(), 0, triangulations.items);
5207 HeapFree(GetProcessHeap(), 0, raw_outline);
5208 if (oldfont) SelectObject(hdc, oldfont);
5209 if (font) DeleteObject(font);
5211 return hr;