gdi32: Add support for 24 bpp dibs.
[wine/multimedia.git] / dlls / d3dx9_36 / mesh.c
blob48ae20ec0d4f853aa2a53283962cf7ae9ae93a23
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
10 * This library is free software; you can redistribute it and/or
11 * modify it under the terms of the GNU Lesser General Public
12 * License as published by the Free Software Foundation; either
13 * version 2.1 of the License, or (at your option) any later version.
15 * This library is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
18 * Lesser General Public License for more details.
20 * You should have received a copy of the GNU Lesser General Public
21 * License along with this library; if not, write to the Free Software
22 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
25 #include "config.h"
26 #include "wine/port.h"
28 #define COBJMACROS
29 #define NONAMELESSUNION
30 #include "windef.h"
31 #include "wingdi.h"
32 #include "d3dx9.h"
33 #undef MAKE_DDHRESULT
34 #include "dxfile.h"
35 #include "rmxfguid.h"
36 #include "rmxftmpl.h"
37 #include "wine/debug.h"
38 #include "wine/unicode.h"
39 #include "wine/list.h"
40 #include "d3dx9_36_private.h"
42 WINE_DEFAULT_DEBUG_CHANNEL(d3dx);
44 typedef struct ID3DXMeshImpl
46 ID3DXMesh ID3DXMesh_iface;
47 LONG ref;
49 DWORD numfaces;
50 DWORD numvertices;
51 DWORD options;
52 DWORD fvf;
53 IDirect3DDevice9 *device;
54 IDirect3DVertexDeclaration9 *vertex_declaration;
55 IDirect3DVertexBuffer9 *vertex_buffer;
56 IDirect3DIndexBuffer9 *index_buffer;
57 DWORD *attrib_buffer;
58 int attrib_buffer_lock_count;
59 DWORD attrib_table_size;
60 D3DXATTRIBUTERANGE *attrib_table;
61 } ID3DXMeshImpl;
63 static inline ID3DXMeshImpl *impl_from_ID3DXMesh(ID3DXMesh *iface)
65 return CONTAINING_RECORD(iface, ID3DXMeshImpl, ID3DXMesh_iface);
68 static HRESULT WINAPI ID3DXMeshImpl_QueryInterface(ID3DXMesh *iface, REFIID riid, LPVOID *object)
70 ID3DXMeshImpl *This = impl_from_ID3DXMesh(iface);
72 TRACE("(%p)->(%s, %p)\n", This, debugstr_guid(riid), object);
74 if (IsEqualGUID(riid, &IID_IUnknown) ||
75 IsEqualGUID(riid, &IID_ID3DXBaseMesh) ||
76 IsEqualGUID(riid, &IID_ID3DXMesh))
78 iface->lpVtbl->AddRef(iface);
79 *object = This;
80 return S_OK;
83 WARN("Interface %s not found.\n", debugstr_guid(riid));
85 return E_NOINTERFACE;
88 static ULONG WINAPI ID3DXMeshImpl_AddRef(ID3DXMesh *iface)
90 ID3DXMeshImpl *This = impl_from_ID3DXMesh(iface);
92 TRACE("(%p)->(): AddRef from %d\n", This, This->ref);
94 return InterlockedIncrement(&This->ref);
97 static ULONG WINAPI ID3DXMeshImpl_Release(ID3DXMesh *iface)
99 ID3DXMeshImpl *This = impl_from_ID3DXMesh(iface);
100 ULONG ref = InterlockedDecrement(&This->ref);
102 TRACE("(%p)->(): Release from %d\n", This, ref + 1);
104 if (!ref)
106 IDirect3DIndexBuffer9_Release(This->index_buffer);
107 IDirect3DVertexBuffer9_Release(This->vertex_buffer);
108 IDirect3DVertexDeclaration9_Release(This->vertex_declaration);
109 IDirect3DDevice9_Release(This->device);
110 HeapFree(GetProcessHeap(), 0, This->attrib_buffer);
111 HeapFree(GetProcessHeap(), 0, This->attrib_table);
112 HeapFree(GetProcessHeap(), 0, This);
115 return ref;
118 /*** ID3DXBaseMesh ***/
119 static HRESULT WINAPI ID3DXMeshImpl_DrawSubset(ID3DXMesh *iface, DWORD attrib_id)
121 ID3DXMeshImpl *This = impl_from_ID3DXMesh(iface);
122 HRESULT hr;
123 DWORD face_start;
124 DWORD face_end = 0;
125 DWORD vertex_size;
127 TRACE("(%p)->(%u)\n", This, attrib_id);
129 vertex_size = iface->lpVtbl->GetNumBytesPerVertex(iface);
131 hr = IDirect3DDevice9_SetVertexDeclaration(This->device, This->vertex_declaration);
132 if (FAILED(hr)) return hr;
133 hr = IDirect3DDevice9_SetStreamSource(This->device, 0, This->vertex_buffer, 0, vertex_size);
134 if (FAILED(hr)) return hr;
135 hr = IDirect3DDevice9_SetIndices(This->device, This->index_buffer);
136 if (FAILED(hr)) return hr;
138 while (face_end < This->numfaces)
140 for (face_start = face_end; face_start < This->numfaces; face_start++)
142 if (This->attrib_buffer[face_start] == attrib_id)
143 break;
145 if (face_start >= This->numfaces)
146 break;
147 for (face_end = face_start + 1; face_end < This->numfaces; face_end++)
149 if (This->attrib_buffer[face_end] != attrib_id)
150 break;
153 hr = IDirect3DDevice9_DrawIndexedPrimitive(This->device, D3DPT_TRIANGLELIST,
154 0, 0, This->numvertices, face_start * 3, face_end - face_start);
155 if (FAILED(hr)) return hr;
158 return D3D_OK;
161 static DWORD WINAPI ID3DXMeshImpl_GetNumFaces(ID3DXMesh *iface)
163 ID3DXMeshImpl *This = impl_from_ID3DXMesh(iface);
165 TRACE("(%p)\n", This);
167 return This->numfaces;
170 static DWORD WINAPI ID3DXMeshImpl_GetNumVertices(ID3DXMesh *iface)
172 ID3DXMeshImpl *This = impl_from_ID3DXMesh(iface);
174 TRACE("(%p)\n", This);
176 return This->numvertices;
179 static DWORD WINAPI ID3DXMeshImpl_GetFVF(ID3DXMesh *iface)
181 ID3DXMeshImpl *This = impl_from_ID3DXMesh(iface);
183 TRACE("(%p)\n", This);
185 return This->fvf;
188 static HRESULT WINAPI ID3DXMeshImpl_GetDeclaration(ID3DXMesh *iface, D3DVERTEXELEMENT9 declaration[MAX_FVF_DECL_SIZE])
190 ID3DXMeshImpl *This = impl_from_ID3DXMesh(iface);
191 UINT numelements;
193 TRACE("(%p)\n", This);
195 if (declaration == NULL) return D3DERR_INVALIDCALL;
197 return IDirect3DVertexDeclaration9_GetDeclaration(This->vertex_declaration,
198 declaration,
199 &numelements);
202 static DWORD WINAPI ID3DXMeshImpl_GetNumBytesPerVertex(ID3DXMesh *iface)
204 ID3DXMeshImpl *This = impl_from_ID3DXMesh(iface);
205 UINT numelements;
206 D3DVERTEXELEMENT9 declaration[MAX_FVF_DECL_SIZE] = { D3DDECL_END() };
208 TRACE("iface (%p)\n", This);
210 IDirect3DVertexDeclaration9_GetDeclaration(This->vertex_declaration,
211 declaration,
212 &numelements);
213 return D3DXGetDeclVertexSize(declaration, 0);
216 static DWORD WINAPI ID3DXMeshImpl_GetOptions(ID3DXMesh *iface)
218 ID3DXMeshImpl *This = impl_from_ID3DXMesh(iface);
220 TRACE("(%p)\n", This);
222 return This->options;
225 static HRESULT WINAPI ID3DXMeshImpl_GetDevice(ID3DXMesh *iface, LPDIRECT3DDEVICE9 *device)
227 ID3DXMeshImpl *This = impl_from_ID3DXMesh(iface);
229 TRACE("(%p)->(%p)\n", This, device);
231 if (device == NULL) return D3DERR_INVALIDCALL;
232 *device = This->device;
233 IDirect3DDevice9_AddRef(This->device);
235 return D3D_OK;
238 static HRESULT WINAPI ID3DXMeshImpl_CloneMeshFVF(ID3DXMesh *iface, DWORD options, DWORD fvf, LPDIRECT3DDEVICE9 device, LPD3DXMESH *clone_mesh)
240 ID3DXMeshImpl *This = impl_from_ID3DXMesh(iface);
241 HRESULT hr;
242 D3DVERTEXELEMENT9 declaration[MAX_FVF_DECL_SIZE];
244 TRACE("(%p)->(%x,%x,%p,%p)\n", This, options, fvf, device, clone_mesh);
246 hr = D3DXDeclaratorFromFVF(fvf, declaration);
247 if (FAILED(hr)) return hr;
249 return iface->lpVtbl->CloneMesh(iface, options, declaration, device, clone_mesh);
252 static HRESULT WINAPI ID3DXMeshImpl_CloneMesh(ID3DXMesh *iface, DWORD options, CONST D3DVERTEXELEMENT9 *declaration, LPDIRECT3DDEVICE9 device,
253 LPD3DXMESH *clone_mesh_out)
255 ID3DXMeshImpl *This = impl_from_ID3DXMesh(iface);
256 ID3DXMeshImpl *cloned_this;
257 ID3DXMesh *clone_mesh;
258 D3DVERTEXELEMENT9 orig_declaration[MAX_FVF_DECL_SIZE] = { D3DDECL_END() };
259 void *data_in, *data_out;
260 DWORD vertex_size;
261 HRESULT hr;
262 int i;
264 TRACE("(%p)->(%x,%p,%p,%p)\n", This, options, declaration, device, clone_mesh_out);
266 if (!clone_mesh_out)
267 return D3DERR_INVALIDCALL;
269 hr = iface->lpVtbl->GetDeclaration(iface, orig_declaration);
270 if (FAILED(hr)) return hr;
272 for (i = 0; orig_declaration[i].Stream != 0xff; i++) {
273 if (memcmp(&orig_declaration[i], &declaration[i], sizeof(*declaration)))
275 FIXME("Vertex buffer conversion not implemented.\n");
276 return E_NOTIMPL;
280 hr = D3DXCreateMesh(This->numfaces, This->numvertices, options & ~D3DXMESH_VB_SHARE,
281 declaration, device, &clone_mesh);
282 if (FAILED(hr)) return hr;
284 cloned_this = impl_from_ID3DXMesh(clone_mesh);
285 vertex_size = clone_mesh->lpVtbl->GetNumBytesPerVertex(clone_mesh);
287 if (options & D3DXMESH_VB_SHARE) {
288 IDirect3DVertexBuffer9_AddRef(This->vertex_buffer);
289 /* FIXME: refactor to avoid creating a new vertex buffer */
290 IDirect3DVertexBuffer9_Release(cloned_this->vertex_buffer);
291 cloned_this->vertex_buffer = This->vertex_buffer;
292 } else {
293 hr = iface->lpVtbl->LockVertexBuffer(iface, D3DLOCK_READONLY, &data_in);
294 if (FAILED(hr)) goto error;
295 hr = clone_mesh->lpVtbl->LockVertexBuffer(clone_mesh, D3DLOCK_DISCARD, &data_out);
296 if (FAILED(hr)) {
297 iface->lpVtbl->UnlockVertexBuffer(iface);
298 goto error;
300 memcpy(data_out, data_in, This->numvertices * vertex_size);
301 clone_mesh->lpVtbl->UnlockVertexBuffer(clone_mesh);
302 iface->lpVtbl->UnlockVertexBuffer(iface);
305 hr = iface->lpVtbl->LockIndexBuffer(iface, D3DLOCK_READONLY, &data_in);
306 if (FAILED(hr)) goto error;
307 hr = clone_mesh->lpVtbl->LockIndexBuffer(clone_mesh, D3DLOCK_DISCARD, &data_out);
308 if (FAILED(hr)) {
309 iface->lpVtbl->UnlockIndexBuffer(iface);
310 goto error;
312 if ((options ^ This->options) & D3DXMESH_32BIT) {
313 if (options & D3DXMESH_32BIT) {
314 for (i = 0; i < This->numfaces * 3; i++)
315 ((DWORD*)data_out)[i] = ((WORD*)data_in)[i];
316 } else {
317 for (i = 0; i < This->numfaces * 3; i++)
318 ((WORD*)data_out)[i] = ((DWORD*)data_in)[i];
320 } else {
321 memcpy(data_out, data_in, This->numfaces * 3 * (options & D3DXMESH_32BIT ? 4 : 2));
323 clone_mesh->lpVtbl->UnlockIndexBuffer(clone_mesh);
324 iface->lpVtbl->UnlockIndexBuffer(iface);
326 memcpy(cloned_this->attrib_buffer, This->attrib_buffer, This->numfaces * sizeof(*This->attrib_buffer));
328 if (This->attrib_table_size)
330 cloned_this->attrib_table_size = This->attrib_table_size;
331 cloned_this->attrib_table = HeapAlloc(GetProcessHeap(), 0, This->attrib_table_size * sizeof(*This->attrib_table));
332 if (!cloned_this->attrib_table) {
333 hr = E_OUTOFMEMORY;
334 goto error;
336 memcpy(cloned_this->attrib_table, This->attrib_table, This->attrib_table_size * sizeof(*This->attrib_table));
339 *clone_mesh_out = clone_mesh;
341 return D3D_OK;
342 error:
343 IUnknown_Release(clone_mesh);
344 return hr;
347 static HRESULT WINAPI ID3DXMeshImpl_GetVertexBuffer(ID3DXMesh *iface, LPDIRECT3DVERTEXBUFFER9 *vertex_buffer)
349 ID3DXMeshImpl *This = impl_from_ID3DXMesh(iface);
351 TRACE("(%p)->(%p)\n", This, vertex_buffer);
353 if (vertex_buffer == NULL) return D3DERR_INVALIDCALL;
354 *vertex_buffer = This->vertex_buffer;
355 IDirect3DVertexBuffer9_AddRef(This->vertex_buffer);
357 return D3D_OK;
360 static HRESULT WINAPI ID3DXMeshImpl_GetIndexBuffer(ID3DXMesh *iface, LPDIRECT3DINDEXBUFFER9 *index_buffer)
362 ID3DXMeshImpl *This = impl_from_ID3DXMesh(iface);
364 TRACE("(%p)->(%p)\n", This, index_buffer);
366 if (index_buffer == NULL) return D3DERR_INVALIDCALL;
367 *index_buffer = This->index_buffer;
368 IDirect3DIndexBuffer9_AddRef(This->index_buffer);
370 return D3D_OK;
373 static HRESULT WINAPI ID3DXMeshImpl_LockVertexBuffer(ID3DXMesh *iface, DWORD flags, LPVOID *data)
375 ID3DXMeshImpl *This = impl_from_ID3DXMesh(iface);
377 TRACE("(%p)->(%u,%p)\n", This, flags, data);
379 return IDirect3DVertexBuffer9_Lock(This->vertex_buffer, 0, 0, data, flags);
382 static HRESULT WINAPI ID3DXMeshImpl_UnlockVertexBuffer(ID3DXMesh *iface)
384 ID3DXMeshImpl *This = impl_from_ID3DXMesh(iface);
386 TRACE("(%p)\n", This);
388 return IDirect3DVertexBuffer9_Unlock(This->vertex_buffer);
391 static HRESULT WINAPI ID3DXMeshImpl_LockIndexBuffer(ID3DXMesh *iface, DWORD flags, LPVOID *data)
393 ID3DXMeshImpl *This = impl_from_ID3DXMesh(iface);
395 TRACE("(%p)->(%u,%p)\n", This, flags, data);
397 return IDirect3DIndexBuffer9_Lock(This->index_buffer, 0, 0, data, flags);
400 static HRESULT WINAPI ID3DXMeshImpl_UnlockIndexBuffer(ID3DXMesh *iface)
402 ID3DXMeshImpl *This = impl_from_ID3DXMesh(iface);
404 TRACE("(%p)\n", This);
406 return IDirect3DIndexBuffer9_Unlock(This->index_buffer);
409 static HRESULT WINAPI ID3DXMeshImpl_GetAttributeTable(ID3DXMesh *iface, D3DXATTRIBUTERANGE *attrib_table, DWORD *attrib_table_size)
411 ID3DXMeshImpl *This = impl_from_ID3DXMesh(iface);
413 TRACE("(%p)->(%p,%p)\n", This, attrib_table, attrib_table_size);
415 if (attrib_table_size)
416 *attrib_table_size = This->attrib_table_size;
418 if (attrib_table)
419 CopyMemory(attrib_table, This->attrib_table, This->attrib_table_size * sizeof(*attrib_table));
421 return D3D_OK;
424 static HRESULT WINAPI ID3DXMeshImpl_ConvertPointRepsToAdjacency(ID3DXMesh *iface, CONST DWORD *point_reps, DWORD *adjacency)
426 ID3DXMeshImpl *This = impl_from_ID3DXMesh(iface);
428 FIXME("(%p)->(%p,%p): stub\n", This, point_reps, adjacency);
430 return E_NOTIMPL;
433 static HRESULT WINAPI ID3DXMeshImpl_ConvertAdjacencyToPointReps(ID3DXMesh *iface, CONST DWORD *adjacency, DWORD *point_reps)
435 ID3DXMeshImpl *This = impl_from_ID3DXMesh(iface);
437 FIXME("(%p)->(%p,%p): stub\n", This, adjacency, point_reps);
439 return E_NOTIMPL;
442 struct vertex_metadata {
443 float key;
444 DWORD vertex_index;
445 DWORD first_shared_index;
448 static int compare_vertex_keys(const void *a, const void *b)
450 const struct vertex_metadata *left = a;
451 const struct vertex_metadata *right = b;
452 if (left->key == right->key)
453 return 0;
454 return left->key < right->key ? -1 : 1;
457 static HRESULT WINAPI ID3DXMeshImpl_GenerateAdjacency(ID3DXMesh *iface, FLOAT epsilon, DWORD *adjacency)
459 ID3DXMeshImpl *This = impl_from_ID3DXMesh(iface);
460 HRESULT hr;
461 BYTE *vertices = NULL;
462 const DWORD *indices = NULL;
463 DWORD vertex_size;
464 DWORD buffer_size;
465 /* sort the vertices by (x + y + z) to quickly find coincident vertices */
466 struct vertex_metadata *sorted_vertices;
467 /* shared_indices links together identical indices in the index buffer so
468 * that adjacency checks can be limited to faces sharing a vertex */
469 DWORD *shared_indices = NULL;
470 const FLOAT epsilon_sq = epsilon * epsilon;
471 int i;
473 TRACE("(%p)->(%f,%p)\n", This, epsilon, adjacency);
475 if (!adjacency)
476 return D3DERR_INVALIDCALL;
478 buffer_size = This->numfaces * 3 * sizeof(*shared_indices) + This->numvertices * sizeof(*sorted_vertices);
479 if (!(This->options & D3DXMESH_32BIT))
480 buffer_size += This->numfaces * 3 * sizeof(*indices);
481 shared_indices = HeapAlloc(GetProcessHeap(), 0, buffer_size);
482 if (!shared_indices)
483 return E_OUTOFMEMORY;
484 sorted_vertices = (struct vertex_metadata*)(shared_indices + This->numfaces * 3);
486 hr = iface->lpVtbl->LockVertexBuffer(iface, D3DLOCK_READONLY, (void**)&vertices);
487 if (FAILED(hr)) goto cleanup;
488 hr = iface->lpVtbl->LockIndexBuffer(iface, D3DLOCK_READONLY, (void**)&indices);
489 if (FAILED(hr)) goto cleanup;
491 if (!(This->options & D3DXMESH_32BIT)) {
492 const WORD *word_indices = (const WORD*)indices;
493 DWORD *dword_indices = (DWORD*)(sorted_vertices + This->numvertices);
494 indices = dword_indices;
495 for (i = 0; i < This->numfaces * 3; i++)
496 *dword_indices++ = *word_indices++;
499 vertex_size = iface->lpVtbl->GetNumBytesPerVertex(iface);
500 for (i = 0; i < This->numvertices; i++) {
501 D3DXVECTOR3 *vertex = (D3DXVECTOR3*)(vertices + vertex_size * i);
502 sorted_vertices[i].first_shared_index = -1;
503 sorted_vertices[i].key = vertex->x + vertex->y + vertex->z;
504 sorted_vertices[i].vertex_index = i;
506 for (i = 0; i < This->numfaces * 3; i++) {
507 DWORD *first_shared_index = &sorted_vertices[indices[i]].first_shared_index;
508 shared_indices[i] = *first_shared_index;
509 *first_shared_index = i;
510 adjacency[i] = -1;
512 qsort(sorted_vertices, This->numvertices, sizeof(*sorted_vertices), compare_vertex_keys);
514 for (i = 0; i < This->numvertices; i++) {
515 struct vertex_metadata *sorted_vertex_a = &sorted_vertices[i];
516 D3DXVECTOR3 *vertex_a = (D3DXVECTOR3*)(vertices + sorted_vertex_a->vertex_index * vertex_size);
517 DWORD shared_index_a = sorted_vertex_a->first_shared_index;
519 while (shared_index_a != -1) {
520 int j = i;
521 DWORD shared_index_b = shared_indices[shared_index_a];
522 struct vertex_metadata *sorted_vertex_b = sorted_vertex_a;
524 while (TRUE) {
525 while (shared_index_b != -1) {
526 /* faces are adjacent if they have another coincident vertex */
527 DWORD base_a = (shared_index_a / 3) * 3;
528 DWORD base_b = (shared_index_b / 3) * 3;
529 BOOL adjacent = FALSE;
530 int k;
532 for (k = 0; k < 3; k++) {
533 if (adjacency[base_b + k] == shared_index_a / 3) {
534 adjacent = TRUE;
535 break;
538 if (!adjacent) {
539 for (k = 1; k <= 2; k++) {
540 DWORD vertex_index_a = base_a + (shared_index_a + k) % 3;
541 DWORD vertex_index_b = base_b + (shared_index_b + (3 - k)) % 3;
542 adjacent = indices[vertex_index_a] == indices[vertex_index_b];
543 if (!adjacent && epsilon >= 0.0f) {
544 D3DXVECTOR3 delta = {0.0f, 0.0f, 0.0f};
545 FLOAT length_sq;
547 D3DXVec3Subtract(&delta,
548 (D3DXVECTOR3*)(vertices + indices[vertex_index_a] * vertex_size),
549 (D3DXVECTOR3*)(vertices + indices[vertex_index_b] * vertex_size));
550 length_sq = D3DXVec3LengthSq(&delta);
551 adjacent = epsilon == 0.0f ? length_sq == 0.0f : length_sq < epsilon_sq;
553 if (adjacent) {
554 DWORD adj_a = base_a + 2 - (vertex_index_a + shared_index_a + 1) % 3;
555 DWORD adj_b = base_b + 2 - (vertex_index_b + shared_index_b + 1) % 3;
556 if (adjacency[adj_a] == -1 && adjacency[adj_b] == -1) {
557 adjacency[adj_a] = base_b / 3;
558 adjacency[adj_b] = base_a / 3;
559 break;
565 shared_index_b = shared_indices[shared_index_b];
567 while (++j < This->numvertices) {
568 D3DXVECTOR3 *vertex_b;
570 sorted_vertex_b++;
571 if (sorted_vertex_b->key - sorted_vertex_a->key > epsilon * 3.0f) {
572 /* no more coincident vertices to try */
573 j = This->numvertices;
574 break;
576 /* check for coincidence */
577 vertex_b = (D3DXVECTOR3*)(vertices + sorted_vertex_b->vertex_index * vertex_size);
578 if (fabsf(vertex_a->x - vertex_b->x) <= epsilon &&
579 fabsf(vertex_a->y - vertex_b->y) <= epsilon &&
580 fabsf(vertex_a->z - vertex_b->z) <= epsilon)
582 break;
585 if (j >= This->numvertices)
586 break;
587 shared_index_b = sorted_vertex_b->first_shared_index;
590 sorted_vertex_a->first_shared_index = shared_indices[sorted_vertex_a->first_shared_index];
591 shared_index_a = sorted_vertex_a->first_shared_index;
595 hr = D3D_OK;
596 cleanup:
597 if (indices) iface->lpVtbl->UnlockIndexBuffer(iface);
598 if (vertices) iface->lpVtbl->UnlockVertexBuffer(iface);
599 HeapFree(GetProcessHeap(), 0, shared_indices);
600 return hr;
603 static HRESULT WINAPI ID3DXMeshImpl_UpdateSemantics(ID3DXMesh *iface, D3DVERTEXELEMENT9 declaration[MAX_FVF_DECL_SIZE])
605 ID3DXMeshImpl *This = impl_from_ID3DXMesh(iface);
607 FIXME("(%p)->(%p): stub\n", This, declaration);
609 return E_NOTIMPL;
612 /*** ID3DXMesh ***/
613 static HRESULT WINAPI ID3DXMeshImpl_LockAttributeBuffer(ID3DXMesh *iface, DWORD flags, DWORD **data)
615 ID3DXMeshImpl *This = impl_from_ID3DXMesh(iface);
617 TRACE("(%p)->(%u,%p)\n", This, flags, data);
619 InterlockedIncrement(&This->attrib_buffer_lock_count);
621 if (!(flags & D3DLOCK_READONLY)) {
622 D3DXATTRIBUTERANGE *attrib_table = This->attrib_table;
623 This->attrib_table_size = 0;
624 This->attrib_table = NULL;
625 HeapFree(GetProcessHeap(), 0, attrib_table);
628 *data = This->attrib_buffer;
630 return D3D_OK;
633 static HRESULT WINAPI ID3DXMeshImpl_UnlockAttributeBuffer(ID3DXMesh *iface)
635 ID3DXMeshImpl *This = impl_from_ID3DXMesh(iface);
636 int lock_count;
638 TRACE("(%p)\n", This);
640 lock_count = InterlockedDecrement(&This->attrib_buffer_lock_count);
642 if (lock_count < 0) {
643 InterlockedIncrement(&This->attrib_buffer_lock_count);
644 return D3DERR_INVALIDCALL;
647 return D3D_OK;
650 static HRESULT WINAPI ID3DXMeshImpl_Optimize(ID3DXMesh *iface, DWORD flags, CONST DWORD *adjacency_in, DWORD *adjacency_out,
651 DWORD *face_remap, LPD3DXBUFFER *vertex_remap, LPD3DXMESH *opt_mesh)
653 ID3DXMeshImpl *This = impl_from_ID3DXMesh(iface);
654 HRESULT hr;
655 D3DVERTEXELEMENT9 declaration[MAX_FVF_DECL_SIZE] = { D3DDECL_END() };
656 ID3DXMesh *optimized_mesh;
658 TRACE("(%p)->(%x,%p,%p,%p,%p,%p)\n", This, flags, adjacency_in, adjacency_out, face_remap, vertex_remap, opt_mesh);
660 if (!opt_mesh)
661 return D3DERR_INVALIDCALL;
663 hr = iface->lpVtbl->GetDeclaration(iface, declaration);
664 if (FAILED(hr)) return hr;
666 hr = iface->lpVtbl->CloneMesh(iface, This->options, declaration, This->device, &optimized_mesh);
667 if (FAILED(hr)) return hr;
669 hr = optimized_mesh->lpVtbl->OptimizeInplace(optimized_mesh, flags, adjacency_in, adjacency_out, face_remap, vertex_remap);
670 if (SUCCEEDED(hr))
671 *opt_mesh = optimized_mesh;
672 else
673 IUnknown_Release(optimized_mesh);
674 return hr;
677 /* Creates a vertex_remap that removes unused vertices.
678 * Indices are updated according to the vertex_remap. */
679 static HRESULT compact_mesh(ID3DXMeshImpl *This, DWORD *indices, DWORD *new_num_vertices, ID3DXBuffer **vertex_remap)
681 HRESULT hr;
682 DWORD *vertex_remap_ptr;
683 DWORD num_used_vertices;
684 DWORD i;
686 hr = D3DXCreateBuffer(This->numvertices * sizeof(DWORD), vertex_remap);
687 if (FAILED(hr)) return hr;
688 vertex_remap_ptr = ID3DXBuffer_GetBufferPointer(*vertex_remap);
690 for (i = 0; i < This->numfaces * 3; i++)
691 vertex_remap_ptr[indices[i]] = 1;
693 /* create old->new vertex mapping */
694 num_used_vertices = 0;
695 for (i = 0; i < This->numvertices; i++) {
696 if (vertex_remap_ptr[i])
697 vertex_remap_ptr[i] = num_used_vertices++;
698 else
699 vertex_remap_ptr[i] = -1;
701 /* convert indices */
702 for (i = 0; i < This->numfaces * 3; i++)
703 indices[i] = vertex_remap_ptr[indices[i]];
705 /* create new->old vertex mapping */
706 num_used_vertices = 0;
707 for (i = 0; i < This->numvertices; i++) {
708 if (vertex_remap_ptr[i] != -1)
709 vertex_remap_ptr[num_used_vertices++] = i;
711 for (i = num_used_vertices; i < This->numvertices; i++)
712 vertex_remap_ptr[i] = -1;
714 *new_num_vertices = num_used_vertices;
716 return D3D_OK;
719 /* count the number of unique attribute values in a sorted attribute buffer */
720 static DWORD count_attributes(const DWORD *attrib_buffer, DWORD numfaces)
722 DWORD last_attribute = attrib_buffer[0];
723 DWORD attrib_table_size = 1;
724 DWORD i;
725 for (i = 1; i < numfaces; i++) {
726 if (attrib_buffer[i] != last_attribute) {
727 last_attribute = attrib_buffer[i];
728 attrib_table_size++;
731 return attrib_table_size;
734 static void fill_attribute_table(DWORD *attrib_buffer, DWORD numfaces, void *indices,
735 BOOL is_32bit_indices, D3DXATTRIBUTERANGE *attrib_table)
737 DWORD attrib_table_size = 0;
738 DWORD last_attribute = attrib_buffer[0];
739 DWORD min_vertex, max_vertex;
740 DWORD i;
742 attrib_table[0].AttribId = last_attribute;
743 attrib_table[0].FaceStart = 0;
744 min_vertex = (DWORD)-1;
745 max_vertex = 0;
746 for (i = 0; i < numfaces; i++) {
747 DWORD j;
749 if (attrib_buffer[i] != last_attribute) {
750 last_attribute = attrib_buffer[i];
751 attrib_table[attrib_table_size].FaceCount = i - attrib_table[attrib_table_size].FaceStart;
752 attrib_table[attrib_table_size].VertexStart = min_vertex;
753 attrib_table[attrib_table_size].VertexCount = max_vertex - min_vertex + 1;
754 attrib_table_size++;
755 attrib_table[attrib_table_size].AttribId = attrib_buffer[i];
756 attrib_table[attrib_table_size].FaceStart = i;
757 min_vertex = (DWORD)-1;
758 max_vertex = 0;
760 for (j = 0; j < 3; j++) {
761 DWORD vertex_index = is_32bit_indices ? ((DWORD*)indices)[i * 3 + j] : ((WORD*)indices)[i * 3 + j];
762 if (vertex_index < min_vertex)
763 min_vertex = vertex_index;
764 if (vertex_index > max_vertex)
765 max_vertex = vertex_index;
768 attrib_table[attrib_table_size].FaceCount = i - attrib_table[attrib_table_size].FaceStart;
769 attrib_table[attrib_table_size].VertexStart = min_vertex;
770 attrib_table[attrib_table_size].VertexCount = max_vertex - min_vertex + 1;
771 attrib_table_size++;
774 static int attrib_entry_compare(const DWORD **a, const DWORD **b)
776 const DWORD *ptr_a = *a;
777 const DWORD *ptr_b = *b;
778 int delta = *ptr_a - *ptr_b;
780 if (delta)
781 return delta;
783 delta = ptr_a - ptr_b; /* for stable sort */
784 return delta;
787 /* Create face_remap, a new attribute buffer for attribute sort optimization. */
788 static HRESULT remap_faces_for_attrsort(ID3DXMeshImpl *This, const DWORD *indices,
789 const DWORD *attrib_buffer, DWORD **sorted_attrib_buffer, DWORD **face_remap)
791 const DWORD **sorted_attrib_ptr_buffer = NULL;
792 DWORD i;
794 *face_remap = HeapAlloc(GetProcessHeap(), 0, This->numfaces * sizeof(**face_remap));
795 sorted_attrib_ptr_buffer = HeapAlloc(GetProcessHeap(), 0, This->numfaces * sizeof(*sorted_attrib_ptr_buffer));
796 if (!*face_remap || !sorted_attrib_ptr_buffer) {
797 HeapFree(GetProcessHeap(), 0, sorted_attrib_ptr_buffer);
798 return E_OUTOFMEMORY;
800 for (i = 0; i < This->numfaces; i++)
801 sorted_attrib_ptr_buffer[i] = &attrib_buffer[i];
802 qsort(sorted_attrib_ptr_buffer, This->numfaces, sizeof(*sorted_attrib_ptr_buffer),
803 (int(*)(const void *, const void *))attrib_entry_compare);
805 for (i = 0; i < This->numfaces; i++)
807 DWORD old_face = sorted_attrib_ptr_buffer[i] - attrib_buffer;
808 (*face_remap)[old_face] = i;
811 /* overwrite sorted_attrib_ptr_buffer with the values themselves */
812 *sorted_attrib_buffer = (DWORD*)sorted_attrib_ptr_buffer;
813 for (i = 0; i < This->numfaces; i++)
814 (*sorted_attrib_buffer)[(*face_remap)[i]] = attrib_buffer[i];
816 return D3D_OK;
819 static HRESULT WINAPI ID3DXMeshImpl_OptimizeInplace(ID3DXMesh *iface, DWORD flags, CONST DWORD *adjacency_in, DWORD *adjacency_out,
820 DWORD *face_remap_out, LPD3DXBUFFER *vertex_remap_out)
822 ID3DXMeshImpl *This = impl_from_ID3DXMesh(iface);
823 void *indices = NULL;
824 DWORD *attrib_buffer = NULL;
825 HRESULT hr;
826 ID3DXBuffer *vertex_remap = NULL;
827 DWORD *face_remap = NULL; /* old -> new mapping */
828 DWORD *dword_indices = NULL;
829 DWORD new_num_vertices = 0;
830 DWORD new_num_alloc_vertices = 0;
831 IDirect3DVertexBuffer9 *vertex_buffer = NULL;
832 DWORD *sorted_attrib_buffer = NULL;
833 DWORD i;
835 TRACE("(%p)->(%x,%p,%p,%p,%p)\n", This, flags, adjacency_in, adjacency_out, face_remap_out, vertex_remap_out);
837 if (!flags)
838 return D3DERR_INVALIDCALL;
839 if (!adjacency_in && (flags & (D3DXMESHOPT_VERTEXCACHE | D3DXMESHOPT_STRIPREORDER)))
840 return D3DERR_INVALIDCALL;
841 if ((flags & (D3DXMESHOPT_VERTEXCACHE | D3DXMESHOPT_STRIPREORDER)) == (D3DXMESHOPT_VERTEXCACHE | D3DXMESHOPT_STRIPREORDER))
842 return D3DERR_INVALIDCALL;
844 if (flags & (D3DXMESHOPT_VERTEXCACHE | D3DXMESHOPT_STRIPREORDER))
846 if (flags & D3DXMESHOPT_VERTEXCACHE)
847 FIXME("D3DXMESHOPT_VERTEXCACHE not implemented.\n");
848 if (flags & D3DXMESHOPT_STRIPREORDER)
849 FIXME("D3DXMESHOPT_STRIPREORDER not implemented.\n");
850 return E_NOTIMPL;
853 hr = iface->lpVtbl->LockIndexBuffer(iface, 0, (void**)&indices);
854 if (FAILED(hr)) goto cleanup;
856 dword_indices = HeapAlloc(GetProcessHeap(), 0, This->numfaces * 3 * sizeof(DWORD));
857 if (!dword_indices) return E_OUTOFMEMORY;
858 if (This->options & D3DXMESH_32BIT) {
859 memcpy(dword_indices, indices, This->numfaces * 3 * sizeof(DWORD));
860 } else {
861 WORD *word_indices = indices;
862 for (i = 0; i < This->numfaces * 3; i++)
863 dword_indices[i] = *word_indices++;
866 if ((flags & (D3DXMESHOPT_COMPACT | D3DXMESHOPT_IGNOREVERTS | D3DXMESHOPT_ATTRSORT)) == D3DXMESHOPT_COMPACT)
868 new_num_alloc_vertices = This->numvertices;
869 hr = compact_mesh(This, dword_indices, &new_num_vertices, &vertex_remap);
870 if (FAILED(hr)) goto cleanup;
871 } else if (flags & D3DXMESHOPT_ATTRSORT) {
872 if (!(flags & D3DXMESHOPT_IGNOREVERTS))
874 FIXME("D3DXMESHOPT_ATTRSORT vertex reordering not implemented.\n");
875 hr = E_NOTIMPL;
876 goto cleanup;
879 hr = iface->lpVtbl->LockAttributeBuffer(iface, 0, &attrib_buffer);
880 if (FAILED(hr)) goto cleanup;
882 hr = remap_faces_for_attrsort(This, dword_indices, attrib_buffer, &sorted_attrib_buffer, &face_remap);
883 if (FAILED(hr)) goto cleanup;
886 if (vertex_remap)
888 /* reorder the vertices using vertex_remap */
889 D3DVERTEXBUFFER_DESC vertex_desc;
890 DWORD *vertex_remap_ptr = ID3DXBuffer_GetBufferPointer(vertex_remap);
891 DWORD vertex_size = iface->lpVtbl->GetNumBytesPerVertex(iface);
892 BYTE *orig_vertices;
893 BYTE *new_vertices;
895 hr = IDirect3DVertexBuffer9_GetDesc(This->vertex_buffer, &vertex_desc);
896 if (FAILED(hr)) goto cleanup;
898 hr = IDirect3DDevice9_CreateVertexBuffer(This->device, new_num_alloc_vertices * vertex_size,
899 vertex_desc.Usage, This->fvf, vertex_desc.Pool, &vertex_buffer, NULL);
900 if (FAILED(hr)) goto cleanup;
902 hr = IDirect3DVertexBuffer9_Lock(This->vertex_buffer, 0, 0, (void**)&orig_vertices, D3DLOCK_READONLY);
903 if (FAILED(hr)) goto cleanup;
905 hr = IDirect3DVertexBuffer9_Lock(vertex_buffer, 0, 0, (void**)&new_vertices, D3DLOCK_DISCARD);
906 if (FAILED(hr)) {
907 IDirect3DVertexBuffer9_Unlock(This->vertex_buffer);
908 goto cleanup;
911 for (i = 0; i < new_num_vertices; i++)
912 memcpy(new_vertices + i * vertex_size, orig_vertices + vertex_remap_ptr[i] * vertex_size, vertex_size);
914 IDirect3DVertexBuffer9_Unlock(This->vertex_buffer);
915 IDirect3DVertexBuffer9_Unlock(vertex_buffer);
916 } else if (vertex_remap_out) {
917 DWORD *vertex_remap_ptr;
919 hr = D3DXCreateBuffer(This->numvertices * sizeof(DWORD), &vertex_remap);
920 if (FAILED(hr)) goto cleanup;
921 vertex_remap_ptr = ID3DXBuffer_GetBufferPointer(vertex_remap);
922 for (i = 0; i < This->numvertices; i++)
923 *vertex_remap_ptr++ = i;
926 if (flags & D3DXMESHOPT_ATTRSORT)
928 D3DXATTRIBUTERANGE *attrib_table;
929 DWORD attrib_table_size;
931 attrib_table_size = count_attributes(sorted_attrib_buffer, This->numfaces);
932 attrib_table = HeapAlloc(GetProcessHeap(), 0, attrib_table_size * sizeof(*attrib_table));
933 if (!attrib_table) {
934 hr = E_OUTOFMEMORY;
935 goto cleanup;
938 memcpy(attrib_buffer, sorted_attrib_buffer, This->numfaces * sizeof(*attrib_buffer));
940 /* reorder the indices using face_remap */
941 if (This->options & D3DXMESH_32BIT) {
942 for (i = 0; i < This->numfaces; i++)
943 memcpy((DWORD*)indices + face_remap[i] * 3, dword_indices + i * 3, 3 * sizeof(DWORD));
944 } else {
945 WORD *word_indices = indices;
946 for (i = 0; i < This->numfaces; i++) {
947 DWORD new_pos = face_remap[i] * 3;
948 DWORD old_pos = i * 3;
949 word_indices[new_pos++] = dword_indices[old_pos++];
950 word_indices[new_pos++] = dword_indices[old_pos++];
951 word_indices[new_pos] = dword_indices[old_pos];
955 fill_attribute_table(attrib_buffer, This->numfaces, indices,
956 This->options & D3DXMESH_32BIT, attrib_table);
958 HeapFree(GetProcessHeap(), 0, This->attrib_table);
959 This->attrib_table = attrib_table;
960 This->attrib_table_size = attrib_table_size;
961 } else {
962 if (This->options & D3DXMESH_32BIT) {
963 memcpy(indices, dword_indices, This->numfaces * 3 * sizeof(DWORD));
964 } else {
965 WORD *word_indices = indices;
966 for (i = 0; i < This->numfaces * 3; i++)
967 *word_indices++ = dword_indices[i];
971 if (adjacency_out) {
972 if (face_remap) {
973 for (i = 0; i < This->numfaces; i++) {
974 DWORD old_pos = i * 3;
975 DWORD new_pos = face_remap[i] * 3;
976 adjacency_out[new_pos++] = face_remap[adjacency_in[old_pos++]];
977 adjacency_out[new_pos++] = face_remap[adjacency_in[old_pos++]];
978 adjacency_out[new_pos++] = face_remap[adjacency_in[old_pos++]];
980 } else {
981 memcpy(adjacency_out, adjacency_in, This->numfaces * 3 * sizeof(*adjacency_out));
984 if (face_remap_out) {
985 if (face_remap) {
986 for (i = 0; i < This->numfaces; i++)
987 face_remap_out[face_remap[i]] = i;
988 } else {
989 for (i = 0; i < This->numfaces; i++)
990 face_remap_out[i] = i;
993 if (vertex_remap_out)
994 *vertex_remap_out = vertex_remap;
995 vertex_remap = NULL;
997 if (vertex_buffer) {
998 IDirect3DVertexBuffer9_Release(This->vertex_buffer);
999 This->vertex_buffer = vertex_buffer;
1000 vertex_buffer = NULL;
1001 This->numvertices = new_num_vertices;
1004 hr = D3D_OK;
1005 cleanup:
1006 HeapFree(GetProcessHeap(), 0, sorted_attrib_buffer);
1007 HeapFree(GetProcessHeap(), 0, face_remap);
1008 HeapFree(GetProcessHeap(), 0, dword_indices);
1009 if (vertex_remap) ID3DXBuffer_Release(vertex_remap);
1010 if (vertex_buffer) IDirect3DVertexBuffer9_Release(vertex_buffer);
1011 if (attrib_buffer) iface->lpVtbl->UnlockAttributeBuffer(iface);
1012 if (indices) iface->lpVtbl->UnlockIndexBuffer(iface);
1013 return hr;
1016 static HRESULT WINAPI ID3DXMeshImpl_SetAttributeTable(ID3DXMesh *iface, CONST D3DXATTRIBUTERANGE *attrib_table, DWORD attrib_table_size)
1018 ID3DXMeshImpl *This = impl_from_ID3DXMesh(iface);
1019 D3DXATTRIBUTERANGE *new_table = NULL;
1021 TRACE("(%p)->(%p,%u)\n", This, attrib_table, attrib_table_size);
1023 if (attrib_table_size) {
1024 size_t size = attrib_table_size * sizeof(*attrib_table);
1026 new_table = HeapAlloc(GetProcessHeap(), 0, size);
1027 if (!new_table)
1028 return E_OUTOFMEMORY;
1030 CopyMemory(new_table, attrib_table, size);
1031 } else if (attrib_table) {
1032 return D3DERR_INVALIDCALL;
1034 HeapFree(GetProcessHeap(), 0, This->attrib_table);
1035 This->attrib_table = new_table;
1036 This->attrib_table_size = attrib_table_size;
1038 return D3D_OK;
1041 static const struct ID3DXMeshVtbl D3DXMesh_Vtbl =
1043 /*** IUnknown methods ***/
1044 ID3DXMeshImpl_QueryInterface,
1045 ID3DXMeshImpl_AddRef,
1046 ID3DXMeshImpl_Release,
1047 /*** ID3DXBaseMesh ***/
1048 ID3DXMeshImpl_DrawSubset,
1049 ID3DXMeshImpl_GetNumFaces,
1050 ID3DXMeshImpl_GetNumVertices,
1051 ID3DXMeshImpl_GetFVF,
1052 ID3DXMeshImpl_GetDeclaration,
1053 ID3DXMeshImpl_GetNumBytesPerVertex,
1054 ID3DXMeshImpl_GetOptions,
1055 ID3DXMeshImpl_GetDevice,
1056 ID3DXMeshImpl_CloneMeshFVF,
1057 ID3DXMeshImpl_CloneMesh,
1058 ID3DXMeshImpl_GetVertexBuffer,
1059 ID3DXMeshImpl_GetIndexBuffer,
1060 ID3DXMeshImpl_LockVertexBuffer,
1061 ID3DXMeshImpl_UnlockVertexBuffer,
1062 ID3DXMeshImpl_LockIndexBuffer,
1063 ID3DXMeshImpl_UnlockIndexBuffer,
1064 ID3DXMeshImpl_GetAttributeTable,
1065 ID3DXMeshImpl_ConvertPointRepsToAdjacency,
1066 ID3DXMeshImpl_ConvertAdjacencyToPointReps,
1067 ID3DXMeshImpl_GenerateAdjacency,
1068 ID3DXMeshImpl_UpdateSemantics,
1069 /*** ID3DXMesh ***/
1070 ID3DXMeshImpl_LockAttributeBuffer,
1071 ID3DXMeshImpl_UnlockAttributeBuffer,
1072 ID3DXMeshImpl_Optimize,
1073 ID3DXMeshImpl_OptimizeInplace,
1074 ID3DXMeshImpl_SetAttributeTable
1077 /*************************************************************************
1078 * D3DXBoxBoundProbe
1080 BOOL WINAPI D3DXBoxBoundProbe(CONST D3DXVECTOR3 *pmin, CONST D3DXVECTOR3 *pmax, CONST D3DXVECTOR3 *prayposition, CONST D3DXVECTOR3 *praydirection)
1082 /* Algorithm taken from the article: An Efficient and Robust Ray-Box Intersection Algoritm
1083 Amy Williams University of Utah
1084 Steve Barrus University of Utah
1085 R. Keith Morley University of Utah
1086 Peter Shirley University of Utah
1088 International Conference on Computer Graphics and Interactive Techniques archive
1089 ACM SIGGRAPH 2005 Courses
1090 Los Angeles, California
1092 This algorithm is free of patents or of copyrights, as confirmed by Peter Shirley himself.
1094 Algorithm: Consider the box as the intersection of three slabs. Clip the ray
1095 against each slab, if there's anything left of the ray after we're
1096 done we've got an intersection of the ray with the box.
1100 FLOAT div, tmin, tmax, tymin, tymax, tzmin, tzmax;
1102 div = 1.0f / praydirection->x;
1103 if ( div >= 0.0f )
1105 tmin = ( pmin->x - prayposition->x ) * div;
1106 tmax = ( pmax->x - prayposition->x ) * div;
1108 else
1110 tmin = ( pmax->x - prayposition->x ) * div;
1111 tmax = ( pmin->x - prayposition->x ) * div;
1114 if ( tmax < 0.0f ) return FALSE;
1116 div = 1.0f / praydirection->y;
1117 if ( div >= 0.0f )
1119 tymin = ( pmin->y - prayposition->y ) * div;
1120 tymax = ( pmax->y - prayposition->y ) * div;
1122 else
1124 tymin = ( pmax->y - prayposition->y ) * div;
1125 tymax = ( pmin->y - prayposition->y ) * div;
1128 if ( ( tymax < 0.0f ) || ( tmin > tymax ) || ( tymin > tmax ) ) return FALSE;
1130 if ( tymin > tmin ) tmin = tymin;
1131 if ( tymax < tmax ) tmax = tymax;
1133 div = 1.0f / praydirection->z;
1134 if ( div >= 0.0f )
1136 tzmin = ( pmin->z - prayposition->z ) * div;
1137 tzmax = ( pmax->z - prayposition->z ) * div;
1139 else
1141 tzmin = ( pmax->z - prayposition->z ) * div;
1142 tzmax = ( pmin->z - prayposition->z ) * div;
1145 if ( (tzmax < 0.0f ) || ( tmin > tzmax ) || ( tzmin > tmax ) ) return FALSE;
1147 return TRUE;
1150 /*************************************************************************
1151 * D3DXComputeBoundingBox
1153 HRESULT WINAPI D3DXComputeBoundingBox(CONST D3DXVECTOR3 *pfirstposition, DWORD numvertices, DWORD dwstride, D3DXVECTOR3 *pmin, D3DXVECTOR3 *pmax)
1155 D3DXVECTOR3 vec;
1156 unsigned int i;
1158 if( !pfirstposition || !pmin || !pmax ) return D3DERR_INVALIDCALL;
1160 *pmin = *pfirstposition;
1161 *pmax = *pmin;
1163 for(i=0; i<numvertices; i++)
1165 vec = *( (const D3DXVECTOR3*)((const char*)pfirstposition + dwstride * i) );
1167 if ( vec.x < pmin->x ) pmin->x = vec.x;
1168 if ( vec.x > pmax->x ) pmax->x = vec.x;
1170 if ( vec.y < pmin->y ) pmin->y = vec.y;
1171 if ( vec.y > pmax->y ) pmax->y = vec.y;
1173 if ( vec.z < pmin->z ) pmin->z = vec.z;
1174 if ( vec.z > pmax->z ) pmax->z = vec.z;
1177 return D3D_OK;
1180 /*************************************************************************
1181 * D3DXComputeBoundingSphere
1183 HRESULT WINAPI D3DXComputeBoundingSphere(CONST D3DXVECTOR3* pfirstposition, DWORD numvertices, DWORD dwstride, D3DXVECTOR3 *pcenter, FLOAT *pradius)
1185 D3DXVECTOR3 temp, temp1;
1186 FLOAT d;
1187 unsigned int i;
1189 if( !pfirstposition || !pcenter || !pradius ) return D3DERR_INVALIDCALL;
1191 temp.x = 0.0f;
1192 temp.y = 0.0f;
1193 temp.z = 0.0f;
1194 temp1 = temp;
1195 *pradius = 0.0f;
1197 for(i=0; i<numvertices; i++)
1199 D3DXVec3Add(&temp1, &temp, (const D3DXVECTOR3*)((const char*)pfirstposition + dwstride * i));
1200 temp = temp1;
1203 D3DXVec3Scale(pcenter, &temp, 1.0f/((FLOAT)numvertices));
1205 for(i=0; i<numvertices; i++)
1207 d = D3DXVec3Length(D3DXVec3Subtract(&temp, (const D3DXVECTOR3*)((const char*)pfirstposition + dwstride * i), pcenter));
1208 if ( d > *pradius ) *pradius = d;
1210 return D3D_OK;
1213 static const UINT d3dx_decltype_size[D3DDECLTYPE_UNUSED] =
1215 /* D3DDECLTYPE_FLOAT1 */ 1 * 4,
1216 /* D3DDECLTYPE_FLOAT2 */ 2 * 4,
1217 /* D3DDECLTYPE_FLOAT3 */ 3 * 4,
1218 /* D3DDECLTYPE_FLOAT4 */ 4 * 4,
1219 /* D3DDECLTYPE_D3DCOLOR */ 4 * 1,
1220 /* D3DDECLTYPE_UBYTE4 */ 4 * 1,
1221 /* D3DDECLTYPE_SHORT2 */ 2 * 2,
1222 /* D3DDECLTYPE_SHORT4 */ 4 * 2,
1223 /* D3DDECLTYPE_UBYTE4N */ 4 * 1,
1224 /* D3DDECLTYPE_SHORT2N */ 2 * 2,
1225 /* D3DDECLTYPE_SHORT4N */ 4 * 2,
1226 /* D3DDECLTYPE_USHORT2N */ 2 * 2,
1227 /* D3DDECLTYPE_USHORT4N */ 4 * 2,
1228 /* D3DDECLTYPE_UDEC3 */ 4, /* 3 * 10 bits + 2 padding */
1229 /* D3DDECLTYPE_DEC3N */ 4,
1230 /* D3DDECLTYPE_FLOAT16_2 */ 2 * 2,
1231 /* D3DDECLTYPE_FLOAT16_4 */ 4 * 2,
1234 static void append_decl_element(D3DVERTEXELEMENT9 *declaration, UINT *idx, UINT *offset,
1235 D3DDECLTYPE type, D3DDECLUSAGE usage, UINT usage_idx)
1237 declaration[*idx].Stream = 0;
1238 declaration[*idx].Offset = *offset;
1239 declaration[*idx].Type = type;
1240 declaration[*idx].Method = D3DDECLMETHOD_DEFAULT;
1241 declaration[*idx].Usage = usage;
1242 declaration[*idx].UsageIndex = usage_idx;
1244 *offset += d3dx_decltype_size[type];
1245 ++(*idx);
1248 /*************************************************************************
1249 * D3DXDeclaratorFromFVF
1251 HRESULT WINAPI D3DXDeclaratorFromFVF(DWORD fvf, D3DVERTEXELEMENT9 declaration[MAX_FVF_DECL_SIZE])
1253 static const D3DVERTEXELEMENT9 end_element = D3DDECL_END();
1254 DWORD tex_count = (fvf & D3DFVF_TEXCOUNT_MASK) >> D3DFVF_TEXCOUNT_SHIFT;
1255 unsigned int offset = 0;
1256 unsigned int idx = 0;
1257 unsigned int i;
1259 TRACE("fvf %#x, declaration %p.\n", fvf, declaration);
1261 if (fvf & (D3DFVF_RESERVED0 | D3DFVF_RESERVED2)) return D3DERR_INVALIDCALL;
1263 if (fvf & D3DFVF_POSITION_MASK)
1265 BOOL has_blend = (fvf & D3DFVF_XYZB5) >= D3DFVF_XYZB1;
1266 DWORD blend_count = 1 + (((fvf & D3DFVF_XYZB5) - D3DFVF_XYZB1) >> 1);
1267 BOOL has_blend_idx = (fvf & D3DFVF_LASTBETA_D3DCOLOR) || (fvf & D3DFVF_LASTBETA_UBYTE4);
1269 if (has_blend_idx) --blend_count;
1271 if ((fvf & D3DFVF_POSITION_MASK) == D3DFVF_XYZW
1272 || (has_blend && blend_count > 4))
1273 return D3DERR_INVALIDCALL;
1275 if ((fvf & D3DFVF_POSITION_MASK) == D3DFVF_XYZRHW)
1276 append_decl_element(declaration, &idx, &offset, D3DDECLTYPE_FLOAT4, D3DDECLUSAGE_POSITIONT, 0);
1277 else
1278 append_decl_element(declaration, &idx, &offset, D3DDECLTYPE_FLOAT3, D3DDECLUSAGE_POSITION, 0);
1280 if (has_blend)
1282 switch (blend_count)
1284 case 0:
1285 break;
1286 case 1:
1287 append_decl_element(declaration, &idx, &offset, D3DDECLTYPE_FLOAT1, D3DDECLUSAGE_BLENDWEIGHT, 0);
1288 break;
1289 case 2:
1290 append_decl_element(declaration, &idx, &offset, D3DDECLTYPE_FLOAT2, D3DDECLUSAGE_BLENDWEIGHT, 0);
1291 break;
1292 case 3:
1293 append_decl_element(declaration, &idx, &offset, D3DDECLTYPE_FLOAT3, D3DDECLUSAGE_BLENDWEIGHT, 0);
1294 break;
1295 case 4:
1296 append_decl_element(declaration, &idx, &offset, D3DDECLTYPE_FLOAT4, D3DDECLUSAGE_BLENDWEIGHT, 0);
1297 break;
1298 default:
1299 ERR("Invalid blend count %u.\n", blend_count);
1300 break;
1303 if (has_blend_idx)
1305 if (fvf & D3DFVF_LASTBETA_UBYTE4)
1306 append_decl_element(declaration, &idx, &offset, D3DDECLTYPE_UBYTE4, D3DDECLUSAGE_BLENDINDICES, 0);
1307 else if (fvf & D3DFVF_LASTBETA_D3DCOLOR)
1308 append_decl_element(declaration, &idx, &offset, D3DDECLTYPE_D3DCOLOR, D3DDECLUSAGE_BLENDINDICES, 0);
1313 if (fvf & D3DFVF_NORMAL)
1314 append_decl_element(declaration, &idx, &offset, D3DDECLTYPE_FLOAT3, D3DDECLUSAGE_NORMAL, 0);
1315 if (fvf & D3DFVF_PSIZE)
1316 append_decl_element(declaration, &idx, &offset, D3DDECLTYPE_FLOAT1, D3DDECLUSAGE_PSIZE, 0);
1317 if (fvf & D3DFVF_DIFFUSE)
1318 append_decl_element(declaration, &idx, &offset, D3DDECLTYPE_D3DCOLOR, D3DDECLUSAGE_COLOR, 0);
1319 if (fvf & D3DFVF_SPECULAR)
1320 append_decl_element(declaration, &idx, &offset, D3DDECLTYPE_D3DCOLOR, D3DDECLUSAGE_COLOR, 1);
1322 for (i = 0; i < tex_count; ++i)
1324 switch ((fvf >> (16 + 2 * i)) & 0x03)
1326 case D3DFVF_TEXTUREFORMAT1:
1327 append_decl_element(declaration, &idx, &offset, D3DDECLTYPE_FLOAT1, D3DDECLUSAGE_TEXCOORD, i);
1328 break;
1329 case D3DFVF_TEXTUREFORMAT2:
1330 append_decl_element(declaration, &idx, &offset, D3DDECLTYPE_FLOAT2, D3DDECLUSAGE_TEXCOORD, i);
1331 break;
1332 case D3DFVF_TEXTUREFORMAT3:
1333 append_decl_element(declaration, &idx, &offset, D3DDECLTYPE_FLOAT3, D3DDECLUSAGE_TEXCOORD, i);
1334 break;
1335 case D3DFVF_TEXTUREFORMAT4:
1336 append_decl_element(declaration, &idx, &offset, D3DDECLTYPE_FLOAT4, D3DDECLUSAGE_TEXCOORD, i);
1337 break;
1341 declaration[idx] = end_element;
1343 return D3D_OK;
1346 /*************************************************************************
1347 * D3DXFVFFromDeclarator
1349 HRESULT WINAPI D3DXFVFFromDeclarator(const D3DVERTEXELEMENT9 *declaration, DWORD *fvf)
1351 unsigned int i = 0, texture, offset;
1353 TRACE("(%p, %p)\n", declaration, fvf);
1355 *fvf = 0;
1356 if (declaration[0].Type == D3DDECLTYPE_FLOAT3 && declaration[0].Usage == D3DDECLUSAGE_POSITION)
1358 if ((declaration[1].Type == D3DDECLTYPE_FLOAT4 && declaration[1].Usage == D3DDECLUSAGE_BLENDWEIGHT &&
1359 declaration[1].UsageIndex == 0) &&
1360 (declaration[2].Type == D3DDECLTYPE_FLOAT1 && declaration[2].Usage == D3DDECLUSAGE_BLENDINDICES &&
1361 declaration[2].UsageIndex == 0))
1363 return D3DERR_INVALIDCALL;
1365 else if ((declaration[1].Type == D3DDECLTYPE_UBYTE4 || declaration[1].Type == D3DDECLTYPE_D3DCOLOR) &&
1366 declaration[1].Usage == D3DDECLUSAGE_BLENDINDICES && declaration[1].UsageIndex == 0)
1368 if (declaration[1].Type == D3DDECLTYPE_UBYTE4)
1370 *fvf |= D3DFVF_XYZB1 | D3DFVF_LASTBETA_UBYTE4;
1372 else
1374 *fvf |= D3DFVF_XYZB1 | D3DFVF_LASTBETA_D3DCOLOR;
1376 i = 2;
1378 else if (declaration[1].Type <= D3DDECLTYPE_FLOAT4 && declaration[1].Usage == D3DDECLUSAGE_BLENDWEIGHT &&
1379 declaration[1].UsageIndex == 0)
1381 if ((declaration[2].Type == D3DDECLTYPE_UBYTE4 || declaration[2].Type == D3DDECLTYPE_D3DCOLOR) &&
1382 declaration[2].Usage == D3DDECLUSAGE_BLENDINDICES && declaration[2].UsageIndex == 0)
1384 if (declaration[2].Type == D3DDECLTYPE_UBYTE4)
1386 *fvf |= D3DFVF_LASTBETA_UBYTE4;
1388 else
1390 *fvf |= D3DFVF_LASTBETA_D3DCOLOR;
1392 switch (declaration[1].Type)
1394 case D3DDECLTYPE_FLOAT1: *fvf |= D3DFVF_XYZB2; break;
1395 case D3DDECLTYPE_FLOAT2: *fvf |= D3DFVF_XYZB3; break;
1396 case D3DDECLTYPE_FLOAT3: *fvf |= D3DFVF_XYZB4; break;
1397 case D3DDECLTYPE_FLOAT4: *fvf |= D3DFVF_XYZB5; break;
1399 i = 3;
1401 else
1403 switch (declaration[1].Type)
1405 case D3DDECLTYPE_FLOAT1: *fvf |= D3DFVF_XYZB1; break;
1406 case D3DDECLTYPE_FLOAT2: *fvf |= D3DFVF_XYZB2; break;
1407 case D3DDECLTYPE_FLOAT3: *fvf |= D3DFVF_XYZB3; break;
1408 case D3DDECLTYPE_FLOAT4: *fvf |= D3DFVF_XYZB4; break;
1410 i = 2;
1413 else
1415 *fvf |= D3DFVF_XYZ;
1416 i = 1;
1419 else if (declaration[0].Type == D3DDECLTYPE_FLOAT4 && declaration[0].Usage == D3DDECLUSAGE_POSITIONT &&
1420 declaration[0].UsageIndex == 0)
1422 *fvf |= D3DFVF_XYZRHW;
1423 i = 1;
1426 if (declaration[i].Type == D3DDECLTYPE_FLOAT3 && declaration[i].Usage == D3DDECLUSAGE_NORMAL)
1428 *fvf |= D3DFVF_NORMAL;
1429 i++;
1431 if (declaration[i].Type == D3DDECLTYPE_FLOAT1 && declaration[i].Usage == D3DDECLUSAGE_PSIZE &&
1432 declaration[i].UsageIndex == 0)
1434 *fvf |= D3DFVF_PSIZE;
1435 i++;
1437 if (declaration[i].Type == D3DDECLTYPE_D3DCOLOR && declaration[i].Usage == D3DDECLUSAGE_COLOR &&
1438 declaration[i].UsageIndex == 0)
1440 *fvf |= D3DFVF_DIFFUSE;
1441 i++;
1443 if (declaration[i].Type == D3DDECLTYPE_D3DCOLOR && declaration[i].Usage == D3DDECLUSAGE_COLOR &&
1444 declaration[i].UsageIndex == 1)
1446 *fvf |= D3DFVF_SPECULAR;
1447 i++;
1450 for (texture = 0; texture < D3DDP_MAXTEXCOORD; i++, texture++)
1452 if (declaration[i].Stream == 0xFF)
1454 break;
1456 else if (declaration[i].Type == D3DDECLTYPE_FLOAT1 && declaration[i].Usage == D3DDECLUSAGE_TEXCOORD &&
1457 declaration[i].UsageIndex == texture)
1459 *fvf |= D3DFVF_TEXCOORDSIZE1(declaration[i].UsageIndex);
1461 else if (declaration[i].Type == D3DDECLTYPE_FLOAT2 && declaration[i].Usage == D3DDECLUSAGE_TEXCOORD &&
1462 declaration[i].UsageIndex == texture)
1464 *fvf |= D3DFVF_TEXCOORDSIZE2(declaration[i].UsageIndex);
1466 else if (declaration[i].Type == D3DDECLTYPE_FLOAT3 && declaration[i].Usage == D3DDECLUSAGE_TEXCOORD &&
1467 declaration[i].UsageIndex == texture)
1469 *fvf |= D3DFVF_TEXCOORDSIZE3(declaration[i].UsageIndex);
1471 else if (declaration[i].Type == D3DDECLTYPE_FLOAT4 && declaration[i].Usage == D3DDECLUSAGE_TEXCOORD &&
1472 declaration[i].UsageIndex == texture)
1474 *fvf |= D3DFVF_TEXCOORDSIZE4(declaration[i].UsageIndex);
1476 else
1478 return D3DERR_INVALIDCALL;
1482 *fvf |= (texture << D3DFVF_TEXCOUNT_SHIFT);
1484 for (offset = 0, i = 0; declaration[i].Stream != 0xFF;
1485 offset += d3dx_decltype_size[declaration[i].Type], i++)
1487 if (declaration[i].Offset != offset)
1489 return D3DERR_INVALIDCALL;
1493 return D3D_OK;
1496 /*************************************************************************
1497 * D3DXGetFVFVertexSize
1499 static UINT Get_TexCoord_Size_From_FVF(DWORD FVF, int tex_num)
1501 return (((((FVF) >> (16 + (2 * (tex_num)))) + 1) & 0x03) + 1);
1504 UINT WINAPI D3DXGetFVFVertexSize(DWORD FVF)
1506 DWORD size = 0;
1507 UINT i;
1508 UINT numTextures = (FVF & D3DFVF_TEXCOUNT_MASK) >> D3DFVF_TEXCOUNT_SHIFT;
1510 if (FVF & D3DFVF_NORMAL) size += sizeof(D3DXVECTOR3);
1511 if (FVF & D3DFVF_DIFFUSE) size += sizeof(DWORD);
1512 if (FVF & D3DFVF_SPECULAR) size += sizeof(DWORD);
1513 if (FVF & D3DFVF_PSIZE) size += sizeof(DWORD);
1515 switch (FVF & D3DFVF_POSITION_MASK)
1517 case D3DFVF_XYZ: size += sizeof(D3DXVECTOR3); break;
1518 case D3DFVF_XYZRHW: size += 4 * sizeof(FLOAT); break;
1519 case D3DFVF_XYZB1: size += 4 * sizeof(FLOAT); break;
1520 case D3DFVF_XYZB2: size += 5 * sizeof(FLOAT); break;
1521 case D3DFVF_XYZB3: size += 6 * sizeof(FLOAT); break;
1522 case D3DFVF_XYZB4: size += 7 * sizeof(FLOAT); break;
1523 case D3DFVF_XYZB5: size += 8 * sizeof(FLOAT); break;
1524 case D3DFVF_XYZW: size += 4 * sizeof(FLOAT); break;
1527 for (i = 0; i < numTextures; i++)
1529 size += Get_TexCoord_Size_From_FVF(FVF, i) * sizeof(FLOAT);
1532 return size;
1535 /*************************************************************************
1536 * D3DXGetDeclVertexSize
1538 UINT WINAPI D3DXGetDeclVertexSize(const D3DVERTEXELEMENT9 *decl, DWORD stream_idx)
1540 const D3DVERTEXELEMENT9 *element;
1541 UINT size = 0;
1543 TRACE("decl %p, stream_idx %u\n", decl, stream_idx);
1545 if (!decl) return 0;
1547 for (element = decl; element->Stream != 0xff; ++element)
1549 UINT type_size;
1551 if (element->Stream != stream_idx) continue;
1553 if (element->Type >= sizeof(d3dx_decltype_size) / sizeof(*d3dx_decltype_size))
1555 FIXME("Unhandled element type %#x, size will be incorrect.\n", element->Type);
1556 continue;
1559 type_size = d3dx_decltype_size[element->Type];
1560 if (element->Offset + type_size > size) size = element->Offset + type_size;
1563 return size;
1566 /*************************************************************************
1567 * D3DXGetDeclLength
1569 UINT WINAPI D3DXGetDeclLength(const D3DVERTEXELEMENT9 *decl)
1571 const D3DVERTEXELEMENT9 *element;
1573 TRACE("decl %p\n", decl);
1575 /* null decl results in exception on Windows XP */
1577 for (element = decl; element->Stream != 0xff; ++element);
1579 return element - decl;
1582 /*************************************************************************
1583 * D3DXIntersectTri
1585 BOOL WINAPI D3DXIntersectTri(CONST D3DXVECTOR3 *p0, CONST D3DXVECTOR3 *p1, CONST D3DXVECTOR3 *p2, CONST D3DXVECTOR3 *praypos, CONST D3DXVECTOR3 *praydir, FLOAT *pu, FLOAT *pv, FLOAT *pdist)
1587 D3DXMATRIX m;
1588 D3DXVECTOR4 vec;
1590 m.u.m[0][0] = p1->x - p0->x;
1591 m.u.m[1][0] = p2->x - p0->x;
1592 m.u.m[2][0] = -praydir->x;
1593 m.u.m[3][0] = 0.0f;
1594 m.u.m[0][1] = p1->y - p0->z;
1595 m.u.m[1][1] = p2->y - p0->z;
1596 m.u.m[2][1] = -praydir->y;
1597 m.u.m[3][1] = 0.0f;
1598 m.u.m[0][2] = p1->z - p0->z;
1599 m.u.m[1][2] = p2->z - p0->z;
1600 m.u.m[2][2] = -praydir->z;
1601 m.u.m[3][2] = 0.0f;
1602 m.u.m[0][3] = 0.0f;
1603 m.u.m[1][3] = 0.0f;
1604 m.u.m[2][3] = 0.0f;
1605 m.u.m[3][3] = 1.0f;
1607 vec.x = praypos->x - p0->x;
1608 vec.y = praypos->y - p0->y;
1609 vec.z = praypos->z - p0->z;
1610 vec.w = 0.0f;
1612 if ( D3DXMatrixInverse(&m, NULL, &m) )
1614 D3DXVec4Transform(&vec, &vec, &m);
1615 if ( (vec.x >= 0.0f) && (vec.y >= 0.0f) && (vec.x + vec.y <= 1.0f) && (vec.z >= 0.0f) )
1617 *pu = vec.x;
1618 *pv = vec.y;
1619 *pdist = fabs( vec.z );
1620 return TRUE;
1624 return FALSE;
1627 /*************************************************************************
1628 * D3DXSphereBoundProbe
1630 BOOL WINAPI D3DXSphereBoundProbe(CONST D3DXVECTOR3 *pcenter, FLOAT radius, CONST D3DXVECTOR3 *prayposition, CONST D3DXVECTOR3 *praydirection)
1632 D3DXVECTOR3 difference;
1633 FLOAT a, b, c, d;
1635 a = D3DXVec3LengthSq(praydirection);
1636 if (!D3DXVec3Subtract(&difference, prayposition, pcenter)) return FALSE;
1637 b = D3DXVec3Dot(&difference, praydirection);
1638 c = D3DXVec3LengthSq(&difference) - radius * radius;
1639 d = b * b - a * c;
1641 if ( ( d <= 0.0f ) || ( sqrt(d) <= b ) ) return FALSE;
1642 return TRUE;
1645 /*************************************************************************
1646 * D3DXCreateMesh
1648 HRESULT WINAPI D3DXCreateMesh(DWORD numfaces, DWORD numvertices, DWORD options, CONST D3DVERTEXELEMENT9 *declaration,
1649 LPDIRECT3DDEVICE9 device, LPD3DXMESH *mesh)
1651 HRESULT hr;
1652 DWORD fvf;
1653 IDirect3DVertexDeclaration9 *vertex_declaration;
1654 IDirect3DVertexBuffer9 *vertex_buffer;
1655 IDirect3DIndexBuffer9 *index_buffer;
1656 DWORD *attrib_buffer;
1657 ID3DXMeshImpl *object;
1658 DWORD index_usage = 0;
1659 D3DPOOL index_pool = D3DPOOL_DEFAULT;
1660 D3DFORMAT index_format = D3DFMT_INDEX16;
1661 DWORD vertex_usage = 0;
1662 D3DPOOL vertex_pool = D3DPOOL_DEFAULT;
1663 int i;
1665 TRACE("(%d, %d, %x, %p, %p, %p)\n", numfaces, numvertices, options, declaration, device, mesh);
1667 if (numfaces == 0 || numvertices == 0 || declaration == NULL || device == NULL || mesh == NULL ||
1668 /* D3DXMESH_VB_SHARE is for cloning, and D3DXMESH_USEHWONLY is for ConvertToBlendedMesh */
1669 (options & (D3DXMESH_VB_SHARE | D3DXMESH_USEHWONLY | 0xfffe0000)))
1671 return D3DERR_INVALIDCALL;
1673 for (i = 0; declaration[i].Stream != 0xff; i++)
1674 if (declaration[i].Stream != 0)
1675 return D3DERR_INVALIDCALL;
1677 if (options & D3DXMESH_32BIT)
1678 index_format = D3DFMT_INDEX32;
1680 if (options & D3DXMESH_DONOTCLIP) {
1681 index_usage |= D3DUSAGE_DONOTCLIP;
1682 vertex_usage |= D3DUSAGE_DONOTCLIP;
1684 if (options & D3DXMESH_POINTS) {
1685 index_usage |= D3DUSAGE_POINTS;
1686 vertex_usage |= D3DUSAGE_POINTS;
1688 if (options & D3DXMESH_RTPATCHES) {
1689 index_usage |= D3DUSAGE_RTPATCHES;
1690 vertex_usage |= D3DUSAGE_RTPATCHES;
1692 if (options & D3DXMESH_NPATCHES) {
1693 index_usage |= D3DUSAGE_NPATCHES;
1694 vertex_usage |= D3DUSAGE_NPATCHES;
1697 if (options & D3DXMESH_VB_SYSTEMMEM)
1698 vertex_pool = D3DPOOL_SYSTEMMEM;
1699 else if (options & D3DXMESH_VB_MANAGED)
1700 vertex_pool = D3DPOOL_MANAGED;
1702 if (options & D3DXMESH_VB_WRITEONLY)
1703 vertex_usage |= D3DUSAGE_WRITEONLY;
1704 if (options & D3DXMESH_VB_DYNAMIC)
1705 vertex_usage |= D3DUSAGE_DYNAMIC;
1706 if (options & D3DXMESH_VB_SOFTWAREPROCESSING)
1707 vertex_usage |= D3DUSAGE_SOFTWAREPROCESSING;
1709 if (options & D3DXMESH_IB_SYSTEMMEM)
1710 index_pool = D3DPOOL_SYSTEMMEM;
1711 else if (options & D3DXMESH_IB_MANAGED)
1712 index_pool = D3DPOOL_MANAGED;
1714 if (options & D3DXMESH_IB_WRITEONLY)
1715 index_usage |= D3DUSAGE_WRITEONLY;
1716 if (options & D3DXMESH_IB_DYNAMIC)
1717 index_usage |= D3DUSAGE_DYNAMIC;
1718 if (options & D3DXMESH_IB_SOFTWAREPROCESSING)
1719 index_usage |= D3DUSAGE_SOFTWAREPROCESSING;
1721 hr = D3DXFVFFromDeclarator(declaration, &fvf);
1722 if (hr != D3D_OK)
1724 fvf = 0;
1727 /* Create vertex declaration */
1728 hr = IDirect3DDevice9_CreateVertexDeclaration(device,
1729 declaration,
1730 &vertex_declaration);
1731 if (FAILED(hr))
1733 WARN("Unexpected return value %x from IDirect3DDevice9_CreateVertexDeclaration.\n",hr);
1734 return hr;
1737 /* Create vertex buffer */
1738 hr = IDirect3DDevice9_CreateVertexBuffer(device,
1739 numvertices * D3DXGetDeclVertexSize(declaration, declaration[0].Stream),
1740 vertex_usage,
1741 fvf,
1742 vertex_pool,
1743 &vertex_buffer,
1744 NULL);
1745 if (FAILED(hr))
1747 WARN("Unexpected return value %x from IDirect3DDevice9_CreateVertexBuffer.\n",hr);
1748 IDirect3DVertexDeclaration9_Release(vertex_declaration);
1749 return hr;
1752 /* Create index buffer */
1753 hr = IDirect3DDevice9_CreateIndexBuffer(device,
1754 numfaces * 3 * ((index_format == D3DFMT_INDEX16) ? 2 : 4),
1755 index_usage,
1756 index_format,
1757 index_pool,
1758 &index_buffer,
1759 NULL);
1760 if (FAILED(hr))
1762 WARN("Unexpected return value %x from IDirect3DDevice9_CreateVertexBuffer.\n",hr);
1763 IDirect3DVertexBuffer9_Release(vertex_buffer);
1764 IDirect3DVertexDeclaration9_Release(vertex_declaration);
1765 return hr;
1768 attrib_buffer = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, numfaces * sizeof(*attrib_buffer));
1769 object = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(ID3DXMeshImpl));
1770 if (object == NULL || attrib_buffer == NULL)
1772 HeapFree(GetProcessHeap(), 0, attrib_buffer);
1773 IDirect3DIndexBuffer9_Release(index_buffer);
1774 IDirect3DVertexBuffer9_Release(vertex_buffer);
1775 IDirect3DVertexDeclaration9_Release(vertex_declaration);
1776 *mesh = NULL;
1777 return E_OUTOFMEMORY;
1779 object->ID3DXMesh_iface.lpVtbl = &D3DXMesh_Vtbl;
1780 object->ref = 1;
1782 object->numfaces = numfaces;
1783 object->numvertices = numvertices;
1784 object->options = options;
1785 object->fvf = fvf;
1786 object->device = device;
1787 IDirect3DDevice9_AddRef(device);
1789 object->vertex_declaration = vertex_declaration;
1790 object->vertex_buffer = vertex_buffer;
1791 object->index_buffer = index_buffer;
1792 object->attrib_buffer = attrib_buffer;
1794 *mesh = &object->ID3DXMesh_iface;
1796 return D3D_OK;
1799 /*************************************************************************
1800 * D3DXCreateMeshFVF
1802 HRESULT WINAPI D3DXCreateMeshFVF(DWORD numfaces, DWORD numvertices, DWORD options, DWORD fvf,
1803 LPDIRECT3DDEVICE9 device, LPD3DXMESH *mesh)
1805 HRESULT hr;
1806 D3DVERTEXELEMENT9 declaration[MAX_FVF_DECL_SIZE];
1808 TRACE("(%u, %u, %u, %u, %p, %p)\n", numfaces, numvertices, options, fvf, device, mesh);
1810 hr = D3DXDeclaratorFromFVF(fvf, declaration);
1811 if (FAILED(hr)) return hr;
1813 return D3DXCreateMesh(numfaces, numvertices, options, declaration, device, mesh);
1817 struct mesh_data {
1818 DWORD num_vertices;
1819 DWORD num_poly_faces;
1820 DWORD num_tri_faces;
1821 D3DXVECTOR3 *vertices;
1822 DWORD *num_tri_per_face;
1823 DWORD *indices;
1825 DWORD fvf;
1827 /* optional mesh data */
1829 DWORD num_normals;
1830 D3DXVECTOR3 *normals;
1831 DWORD *normal_indices;
1834 static HRESULT get_next_child(IDirectXFileData *filedata, IDirectXFileData **child, const GUID **type)
1836 HRESULT hr;
1837 IDirectXFileDataReference *child_ref = NULL;
1838 IDirectXFileObject *child_obj = NULL;
1839 IDirectXFileData *child_data = NULL;
1841 hr = IDirectXFileData_GetNextObject(filedata, &child_obj);
1842 if (FAILED(hr)) return hr;
1844 hr = IDirectXFileObject_QueryInterface(child_obj, &IID_IDirectXFileDataReference, (void**)&child_ref);
1845 if (SUCCEEDED(hr)) {
1846 hr = IDirectXFileDataReference_Resolve(child_ref, &child_data);
1847 IDirectXFileDataReference_Release(child_ref);
1848 } else {
1849 hr = IDirectXFileObject_QueryInterface(child_obj, &IID_IDirectXFileData, (void**)&child_data);
1851 IDirectXFileObject_Release(child_obj);
1852 if (FAILED(hr))
1853 return hr;
1855 hr = IDirectXFileData_GetType(child_data, type);
1856 if (FAILED(hr)) {
1857 IDirectXFileData_Release(child_data);
1858 } else {
1859 *child = child_data;
1862 return hr;
1865 static HRESULT parse_normals(IDirectXFileData *filedata, struct mesh_data *mesh)
1867 HRESULT hr;
1868 DWORD data_size;
1869 BYTE *data;
1870 DWORD *index_out_ptr;
1871 int i;
1872 DWORD num_face_indices = mesh->num_poly_faces * 2 + mesh->num_tri_faces;
1874 HeapFree(GetProcessHeap(), 0, mesh->normals);
1875 mesh->num_normals = 0;
1876 mesh->normals = NULL;
1877 mesh->normal_indices = NULL;
1878 mesh->fvf |= D3DFVF_NORMAL;
1880 hr = IDirectXFileData_GetData(filedata, NULL, &data_size, (void**)&data);
1881 if (FAILED(hr)) return hr;
1883 /* template Vector {
1884 * FLOAT x;
1885 * FLOAT y;
1886 * FLOAT z;
1888 * template MeshFace {
1889 * DWORD nFaceVertexIndices;
1890 * array DWORD faceVertexIndices[nFaceVertexIndices];
1892 * template MeshNormals {
1893 * DWORD nNormals;
1894 * array Vector normals[nNormals];
1895 * DWORD nFaceNormals;
1896 * array MeshFace faceNormals[nFaceNormals];
1900 if (data_size < sizeof(DWORD) * 2)
1901 goto truncated_data_error;
1902 mesh->num_normals = *(DWORD*)data;
1903 data += sizeof(DWORD);
1904 if (data_size < sizeof(DWORD) * 2 + mesh->num_normals * sizeof(D3DXVECTOR3) +
1905 num_face_indices * sizeof(DWORD))
1906 goto truncated_data_error;
1908 mesh->normals = HeapAlloc(GetProcessHeap(), 0, mesh->num_normals * sizeof(D3DXVECTOR3));
1909 mesh->normal_indices = HeapAlloc(GetProcessHeap(), 0, num_face_indices * sizeof(DWORD));
1910 if (!mesh->normals || !mesh->normal_indices)
1911 return E_OUTOFMEMORY;
1913 memcpy(mesh->normals, data, mesh->num_normals * sizeof(D3DXVECTOR3));
1914 data += mesh->num_normals * sizeof(D3DXVECTOR3);
1915 for (i = 0; i < mesh->num_normals; i++)
1916 D3DXVec3Normalize(&mesh->normals[i], &mesh->normals[i]);
1918 if (*(DWORD*)data != mesh->num_poly_faces) {
1919 WARN("number of face normals (%u) doesn't match number of faces (%u)\n",
1920 *(DWORD*)data, mesh->num_poly_faces);
1921 return E_FAIL;
1923 data += sizeof(DWORD);
1924 index_out_ptr = mesh->normal_indices;
1925 for (i = 0; i < mesh->num_poly_faces; i++)
1927 DWORD j;
1928 DWORD count = *(DWORD*)data;
1929 if (count != mesh->num_tri_per_face[i] + 2) {
1930 WARN("face %u: number of normals (%u) doesn't match number of vertices (%u)\n",
1931 i, count, mesh->num_tri_per_face[i] + 2);
1932 return E_FAIL;
1934 data += sizeof(DWORD);
1936 for (j = 0; j < count; j++) {
1937 DWORD normal_index = *(DWORD*)data;
1938 if (normal_index >= mesh->num_normals) {
1939 WARN("face %u, normal index %u: reference to undefined normal %u (only %u normals)\n",
1940 i, j, normal_index, mesh->num_normals);
1941 return E_FAIL;
1943 *index_out_ptr++ = normal_index;
1944 data += sizeof(DWORD);
1948 return D3D_OK;
1949 truncated_data_error:
1950 WARN("truncated data (%u bytes)\n", data_size);
1951 return E_FAIL;
1954 /* for provide_flags parameters */
1955 #define PROVIDE_MATERIALS 0x1
1956 #define PROVIDE_SKININFO 0x2
1958 static HRESULT parse_mesh(IDirectXFileData *filedata, struct mesh_data *mesh_data, DWORD provide_flags)
1960 HRESULT hr;
1961 DWORD data_size;
1962 BYTE *data, *in_ptr;
1963 DWORD *index_out_ptr;
1964 const GUID *type;
1965 IDirectXFileData *child;
1966 int i;
1969 * template Mesh {
1970 * DWORD nVertices;
1971 * array Vector vertices[nVertices];
1972 * DWORD nFaces;
1973 * array MeshFace faces[nFaces];
1974 * [ ... ]
1978 hr = IDirectXFileData_GetData(filedata, NULL, &data_size, (void**)&data);
1979 if (FAILED(hr)) return hr;
1981 in_ptr = data;
1982 if (data_size < sizeof(DWORD) * 2)
1983 goto truncated_data_error;
1984 mesh_data->num_vertices = *(DWORD*)in_ptr;
1985 if (data_size < sizeof(DWORD) * 2 + mesh_data->num_vertices * sizeof(D3DXVECTOR3))
1986 goto truncated_data_error;
1987 in_ptr += sizeof(DWORD) + mesh_data->num_vertices * sizeof(D3DXVECTOR3);
1989 mesh_data->num_poly_faces = *(DWORD*)in_ptr;
1990 in_ptr += sizeof(DWORD);
1992 mesh_data->num_tri_faces = 0;
1993 for (i = 0; i < mesh_data->num_poly_faces; i++)
1995 DWORD num_poly_vertices;
1996 DWORD j;
1998 if (data_size - (in_ptr - data) < sizeof(DWORD))
1999 goto truncated_data_error;
2000 num_poly_vertices = *(DWORD*)in_ptr;
2001 in_ptr += sizeof(DWORD);
2002 if (data_size - (in_ptr - data) < num_poly_vertices * sizeof(DWORD))
2003 goto truncated_data_error;
2004 if (num_poly_vertices < 3) {
2005 WARN("face %u has only %u vertices\n", i, num_poly_vertices);
2006 return E_FAIL;
2008 for (j = 0; j < num_poly_vertices; j++) {
2009 if (*(DWORD*)in_ptr >= mesh_data->num_vertices) {
2010 WARN("face %u, index %u: undefined vertex %u (only %u vertices)\n",
2011 i, j, *(DWORD*)in_ptr, mesh_data->num_vertices);
2012 return E_FAIL;
2014 in_ptr += sizeof(DWORD);
2016 mesh_data->num_tri_faces += num_poly_vertices - 2;
2019 mesh_data->fvf = D3DFVF_XYZ;
2021 mesh_data->vertices = HeapAlloc(GetProcessHeap(), 0,
2022 mesh_data->num_vertices * sizeof(*mesh_data->vertices));
2023 mesh_data->num_tri_per_face = HeapAlloc(GetProcessHeap(), 0,
2024 mesh_data->num_poly_faces * sizeof(*mesh_data->num_tri_per_face));
2025 mesh_data->indices = HeapAlloc(GetProcessHeap(), 0,
2026 (mesh_data->num_tri_faces + mesh_data->num_poly_faces * 2) * sizeof(*mesh_data->indices));
2027 if (!mesh_data->vertices || !mesh_data->num_tri_per_face || !mesh_data->indices)
2028 return E_OUTOFMEMORY;
2030 in_ptr = data + sizeof(DWORD);
2031 memcpy(mesh_data->vertices, in_ptr, mesh_data->num_vertices * sizeof(D3DXVECTOR3));
2032 in_ptr += mesh_data->num_vertices * sizeof(D3DXVECTOR3) + sizeof(DWORD);
2034 index_out_ptr = mesh_data->indices;
2035 for (i = 0; i < mesh_data->num_poly_faces; i++)
2037 DWORD count;
2039 count = *(DWORD*)in_ptr;
2040 in_ptr += sizeof(DWORD);
2041 mesh_data->num_tri_per_face[i] = count - 2;
2043 while (count--) {
2044 *index_out_ptr++ = *(DWORD*)in_ptr;
2045 in_ptr += sizeof(DWORD);
2049 while (SUCCEEDED(hr = get_next_child(filedata, &child, &type)))
2051 if (IsEqualGUID(type, &TID_D3DRMMeshNormals)) {
2052 hr = parse_normals(child, mesh_data);
2053 } else if (IsEqualGUID(type, &TID_D3DRMMeshVertexColors)) {
2054 FIXME("Mesh vertex color loading not implemented.\n");
2055 hr = E_NOTIMPL;
2056 } else if (IsEqualGUID(type, &TID_D3DRMMeshTextureCoords)) {
2057 FIXME("Mesh texture coordinate loading not implemented.\n");
2058 hr = E_NOTIMPL;
2059 } else if (IsEqualGUID(type, &TID_D3DRMMeshMaterialList) &&
2060 (provide_flags & PROVIDE_MATERIALS))
2062 FIXME("Mesh material list loading not implemented.\n");
2063 hr = E_NOTIMPL;
2064 } else if (provide_flags & PROVIDE_SKININFO) {
2065 if (IsEqualGUID(type, &DXFILEOBJ_XSkinMeshHeader)) {
2066 FIXME("Skin mesh loading not implemented.\n");
2067 hr = E_NOTIMPL;
2068 } else if (IsEqualGUID(type, &DXFILEOBJ_SkinWeights)) {
2069 /* ignored without XSkinMeshHeader */
2072 if (FAILED(hr))
2073 break;
2075 return hr == DXFILEERR_NOMOREOBJECTS ? D3D_OK : hr;
2076 truncated_data_error:
2077 WARN("truncated data (%u bytes)\n", data_size);
2078 return E_FAIL;
2081 /* change to D3DXLoadSkinMeshFromXof when ID3DXFileData is implemented */
2082 static HRESULT load_skin_mesh_from_xof(IDirectXFileData *filedata,
2083 DWORD options,
2084 LPDIRECT3DDEVICE9 device,
2085 LPD3DXBUFFER *adjacency_out,
2086 LPD3DXBUFFER *materials_out,
2087 LPD3DXBUFFER *effects_out,
2088 DWORD *num_materials_out,
2089 LPD3DXSKININFO *skin_info_out,
2090 LPD3DXMESH *mesh_out)
2092 HRESULT hr;
2093 DWORD *index_in_ptr;
2094 struct mesh_data mesh_data;
2095 DWORD total_vertices;
2096 ID3DXMesh *d3dxmesh = NULL;
2097 ID3DXBuffer *adjacency = NULL;
2098 struct vertex_duplication {
2099 DWORD normal_index;
2100 struct list entry;
2101 } *duplications = NULL;
2102 int i;
2103 void *vertices = NULL;
2104 void *indices = NULL;
2105 BYTE *out_ptr;
2106 DWORD provide_flags = 0;
2108 ZeroMemory(&mesh_data, sizeof(mesh_data));
2110 if (num_materials_out || materials_out || effects_out)
2111 provide_flags |= PROVIDE_MATERIALS;
2112 if (skin_info_out)
2113 provide_flags |= PROVIDE_SKININFO;
2115 hr = parse_mesh(filedata, &mesh_data, provide_flags);
2116 if (FAILED(hr)) goto cleanup;
2118 total_vertices = mesh_data.num_vertices;
2119 if (mesh_data.fvf & D3DFVF_NORMAL) {
2120 /* duplicate vertices with multiple normals */
2121 DWORD num_face_indices = mesh_data.num_poly_faces * 2 + mesh_data.num_tri_faces;
2122 duplications = HeapAlloc(GetProcessHeap(), 0, (mesh_data.num_vertices + num_face_indices) * sizeof(*duplications));
2123 if (!duplications) {
2124 hr = E_OUTOFMEMORY;
2125 goto cleanup;
2127 for (i = 0; i < total_vertices; i++)
2129 duplications[i].normal_index = -1;
2130 list_init(&duplications[i].entry);
2132 for (i = 0; i < num_face_indices; i++) {
2133 DWORD vertex_index = mesh_data.indices[i];
2134 DWORD normal_index = mesh_data.normal_indices[i];
2135 struct vertex_duplication *dup_ptr = &duplications[vertex_index];
2137 if (dup_ptr->normal_index == -1) {
2138 dup_ptr->normal_index = normal_index;
2139 } else {
2140 D3DXVECTOR3 *new_normal = &mesh_data.normals[normal_index];
2141 struct list *dup_list = &dup_ptr->entry;
2142 while (TRUE) {
2143 D3DXVECTOR3 *cur_normal = &mesh_data.normals[dup_ptr->normal_index];
2144 if (new_normal->x == cur_normal->x &&
2145 new_normal->y == cur_normal->y &&
2146 new_normal->z == cur_normal->z)
2148 mesh_data.indices[i] = dup_ptr - duplications;
2149 break;
2150 } else if (!list_next(dup_list, &dup_ptr->entry)) {
2151 dup_ptr = &duplications[total_vertices++];
2152 dup_ptr->normal_index = normal_index;
2153 list_add_tail(dup_list, &dup_ptr->entry);
2154 mesh_data.indices[i] = dup_ptr - duplications;
2155 break;
2156 } else {
2157 dup_ptr = LIST_ENTRY(list_next(dup_list, &dup_ptr->entry),
2158 struct vertex_duplication, entry);
2165 hr = D3DXCreateMeshFVF(mesh_data.num_tri_faces, total_vertices, D3DXMESH_MANAGED, mesh_data.fvf, device, &d3dxmesh);
2166 if (FAILED(hr)) goto cleanup;
2168 hr = d3dxmesh->lpVtbl->LockVertexBuffer(d3dxmesh, D3DLOCK_DISCARD, &vertices);
2169 if (FAILED(hr)) goto cleanup;
2171 out_ptr = vertices;
2172 for (i = 0; i < mesh_data.num_vertices; i++) {
2173 *(D3DXVECTOR3*)out_ptr = mesh_data.vertices[i];
2174 out_ptr += sizeof(D3DXVECTOR3);
2175 if (mesh_data.fvf & D3DFVF_NORMAL) {
2176 if (duplications[i].normal_index == -1)
2177 ZeroMemory(out_ptr, sizeof(D3DXVECTOR3));
2178 else
2179 *(D3DXVECTOR3*)out_ptr = mesh_data.normals[duplications[i].normal_index];
2180 out_ptr += sizeof(D3DXVECTOR3);
2183 if (mesh_data.fvf & D3DFVF_NORMAL) {
2184 DWORD vertex_size = D3DXGetFVFVertexSize(mesh_data.fvf);
2185 out_ptr = vertices;
2186 for (i = 0; i < mesh_data.num_vertices; i++) {
2187 struct vertex_duplication *dup_ptr;
2188 LIST_FOR_EACH_ENTRY(dup_ptr, &duplications[i].entry, struct vertex_duplication, entry)
2190 int j = dup_ptr - duplications;
2191 BYTE *dest_vertex = (BYTE*)vertices + j * vertex_size;
2193 memcpy(dest_vertex, out_ptr, vertex_size);
2194 dest_vertex += sizeof(D3DXVECTOR3);
2195 *(D3DXVECTOR3*)dest_vertex = mesh_data.normals[dup_ptr->normal_index];
2197 out_ptr += vertex_size;
2200 d3dxmesh->lpVtbl->UnlockVertexBuffer(d3dxmesh);
2202 hr = d3dxmesh->lpVtbl->LockIndexBuffer(d3dxmesh, D3DLOCK_DISCARD, (void**)&indices);
2203 if (FAILED(hr)) goto cleanup;
2205 index_in_ptr = mesh_data.indices;
2206 #define FILL_INDEX_BUFFER(indices_var) \
2207 for (i = 0; i < mesh_data.num_poly_faces; i++) \
2209 DWORD count = mesh_data.num_tri_per_face[i]; \
2210 WORD first_index = *index_in_ptr++; \
2211 while (count--) { \
2212 *indices_var++ = first_index; \
2213 *indices_var++ = *index_in_ptr; \
2214 index_in_ptr++; \
2215 *indices_var++ = *index_in_ptr; \
2217 index_in_ptr++; \
2219 if (options & D3DXMESH_32BIT) {
2220 DWORD *dword_indices = indices;
2221 FILL_INDEX_BUFFER(dword_indices)
2222 } else {
2223 WORD *word_indices = indices;
2224 FILL_INDEX_BUFFER(word_indices)
2226 #undef FILL_INDEX_BUFFER
2227 d3dxmesh->lpVtbl->UnlockIndexBuffer(d3dxmesh);
2229 if (adjacency_out) {
2230 hr = D3DXCreateBuffer(mesh_data.num_tri_faces * 3 * sizeof(DWORD), &adjacency);
2231 if (FAILED(hr)) goto cleanup;
2232 hr = d3dxmesh->lpVtbl->GenerateAdjacency(d3dxmesh, 0.0f, ID3DXBuffer_GetBufferPointer(adjacency));
2233 if (FAILED(hr)) goto cleanup;
2236 *mesh_out = d3dxmesh;
2237 if (adjacency_out) *adjacency_out = adjacency;
2238 if (num_materials_out) *num_materials_out = 0;
2239 if (materials_out) *materials_out = NULL;
2240 if (effects_out) *effects_out = NULL;
2241 if (skin_info_out) *skin_info_out = NULL;
2243 hr = D3D_OK;
2244 cleanup:
2245 if (FAILED(hr)) {
2246 if (d3dxmesh) IUnknown_Release(d3dxmesh);
2247 if (adjacency) ID3DXBuffer_Release(adjacency);
2249 HeapFree(GetProcessHeap(), 0, mesh_data.vertices);
2250 HeapFree(GetProcessHeap(), 0, mesh_data.num_tri_per_face);
2251 HeapFree(GetProcessHeap(), 0, mesh_data.indices);
2252 HeapFree(GetProcessHeap(), 0, mesh_data.normals);
2253 HeapFree(GetProcessHeap(), 0, mesh_data.normal_indices);
2254 HeapFree(GetProcessHeap(), 0, duplications);
2255 return hr;
2258 static HRESULT filedata_get_name(IDirectXFileData *filedata, char **name)
2260 HRESULT hr;
2261 DWORD name_len;
2263 hr = IDirectXFileData_GetName(filedata, NULL, &name_len);
2264 if (FAILED(hr)) return hr;
2266 if (!name_len)
2267 name_len++;
2268 *name = HeapAlloc(GetProcessHeap(), 0, name_len);
2269 if (!*name) return E_OUTOFMEMORY;
2271 hr = IDirectXFileObject_GetName(filedata, *name, &name_len);
2272 if (FAILED(hr))
2273 HeapFree(GetProcessHeap(), 0, name);
2274 if (!name_len)
2275 (*name)[0] = 0;
2277 return hr;
2280 static HRESULT load_mesh_container(IDirectXFileData *filedata,
2281 DWORD options,
2282 LPDIRECT3DDEVICE9 device,
2283 LPD3DXALLOCATEHIERARCHY alloc_hier,
2284 D3DXMESHCONTAINER **mesh_container)
2286 HRESULT hr;
2287 ID3DXBuffer *adjacency = NULL;
2288 ID3DXBuffer *materials = NULL;
2289 ID3DXBuffer *effects = NULL;
2290 ID3DXSkinInfo *skin_info = NULL;
2291 D3DXMESHDATA mesh_data;
2292 DWORD num_materials = 0;
2293 char *name = NULL;
2295 mesh_data.Type = D3DXMESHTYPE_MESH;
2296 mesh_data.u.pMesh = NULL;
2298 hr = load_skin_mesh_from_xof(filedata, options, device,
2299 &adjacency, &materials, &effects, &num_materials,
2300 &skin_info, &mesh_data.u.pMesh);
2301 if (FAILED(hr)) return hr;
2303 hr = filedata_get_name(filedata, &name);
2304 if (FAILED(hr)) goto cleanup;
2306 hr = alloc_hier->lpVtbl->CreateMeshContainer(alloc_hier, name, &mesh_data,
2307 materials ? ID3DXBuffer_GetBufferPointer(materials) : NULL,
2308 effects ? ID3DXBuffer_GetBufferPointer(effects) : NULL,
2309 num_materials,
2310 adjacency ? ID3DXBuffer_GetBufferPointer(adjacency) : NULL,
2311 skin_info, mesh_container);
2313 cleanup:
2314 if (materials) ID3DXBuffer_Release(materials);
2315 if (effects) ID3DXBuffer_Release(effects);
2316 if (adjacency) ID3DXBuffer_Release(adjacency);
2317 if (skin_info) IUnknown_Release(skin_info);
2318 if (mesh_data.u.pMesh) IUnknown_Release(mesh_data.u.pMesh);
2319 HeapFree(GetProcessHeap(), 0, name);
2320 return hr;
2323 static HRESULT parse_transform_matrix(IDirectXFileData *filedata, D3DXMATRIX *transform)
2325 HRESULT hr;
2326 DWORD data_size;
2327 BYTE *data;
2329 /* template Matrix4x4 {
2330 * array FLOAT matrix[16];
2332 * template FrameTransformMatrix {
2333 * Matrix4x4 frameMatrix;
2337 hr = IDirectXFileData_GetData(filedata, NULL, &data_size, (void**)&data);
2338 if (FAILED(hr)) return hr;
2340 if (data_size != sizeof(D3DXMATRIX)) {
2341 WARN("incorrect data size (%u bytes)\n", data_size);
2342 return E_FAIL;
2345 memcpy(transform, data, sizeof(D3DXMATRIX));
2347 return D3D_OK;
2350 static HRESULT load_frame(IDirectXFileData *filedata,
2351 DWORD options,
2352 LPDIRECT3DDEVICE9 device,
2353 LPD3DXALLOCATEHIERARCHY alloc_hier,
2354 D3DXFRAME **frame_out)
2356 HRESULT hr;
2357 const GUID *type;
2358 IDirectXFileData *child;
2359 char *name = NULL;
2360 D3DXFRAME *frame = NULL;
2361 D3DXMESHCONTAINER **next_container;
2362 D3DXFRAME **next_child;
2364 hr = filedata_get_name(filedata, &name);
2365 if (FAILED(hr)) return hr;
2367 hr = alloc_hier->lpVtbl->CreateFrame(alloc_hier, name, frame_out);
2368 HeapFree(GetProcessHeap(), 0, name);
2369 if (FAILED(hr)) return E_FAIL;
2371 frame = *frame_out;
2372 D3DXMatrixIdentity(&frame->TransformationMatrix);
2373 next_child = &frame->pFrameFirstChild;
2374 next_container = &frame->pMeshContainer;
2376 while (SUCCEEDED(hr = get_next_child(filedata, &child, &type)))
2378 if (IsEqualGUID(type, &TID_D3DRMMesh)) {
2379 hr = load_mesh_container(child, options, device, alloc_hier, next_container);
2380 if (SUCCEEDED(hr))
2381 next_container = &(*next_container)->pNextMeshContainer;
2382 } else if (IsEqualGUID(type, &TID_D3DRMFrameTransformMatrix)) {
2383 hr = parse_transform_matrix(child, &frame->TransformationMatrix);
2384 } else if (IsEqualGUID(type, &TID_D3DRMFrame)) {
2385 hr = load_frame(child, options, device, alloc_hier, next_child);
2386 if (SUCCEEDED(hr))
2387 next_child = &(*next_child)->pFrameSibling;
2389 if (FAILED(hr)) break;
2391 if (hr == DXFILEERR_NOMOREOBJECTS)
2392 hr = D3D_OK;
2394 return hr;
2397 HRESULT WINAPI D3DXLoadMeshHierarchyFromXInMemory(LPCVOID memory,
2398 DWORD memory_size,
2399 DWORD options,
2400 LPDIRECT3DDEVICE9 device,
2401 LPD3DXALLOCATEHIERARCHY alloc_hier,
2402 LPD3DXLOADUSERDATA load_user_data,
2403 LPD3DXFRAME *frame_hierarchy,
2404 LPD3DXANIMATIONCONTROLLER *anim_controller)
2406 HRESULT hr;
2407 IDirectXFile *dxfile = NULL;
2408 IDirectXFileEnumObject *enumobj = NULL;
2409 IDirectXFileData *filedata = NULL;
2410 DXFILELOADMEMORY source;
2411 D3DXFRAME *first_frame = NULL;
2412 D3DXFRAME **next_frame = &first_frame;
2414 TRACE("(%p, %u, %x, %p, %p, %p, %p, %p)\n", memory, memory_size, options,
2415 device, alloc_hier, load_user_data, frame_hierarchy, anim_controller);
2417 if (!memory || !memory_size || !device || !frame_hierarchy || !alloc_hier)
2418 return D3DERR_INVALIDCALL;
2419 if (load_user_data || anim_controller) {
2420 if (load_user_data)
2421 FIXME("Loading user data not implemented\n");
2422 if (anim_controller)
2423 FIXME("Animation controller creation not implemented\n");
2424 return E_NOTIMPL;
2427 hr = DirectXFileCreate(&dxfile);
2428 if (FAILED(hr)) goto cleanup;
2430 hr = IDirectXFile_RegisterTemplates(dxfile, D3DRM_XTEMPLATES, D3DRM_XTEMPLATE_BYTES);
2431 if (FAILED(hr)) goto cleanup;
2433 source.lpMemory = (void*)memory;
2434 source.dSize = memory_size;
2435 hr = IDirectXFile_CreateEnumObject(dxfile, &source, DXFILELOAD_FROMMEMORY, &enumobj);
2436 if (FAILED(hr)) goto cleanup;
2438 while (SUCCEEDED(hr = IDirectXFileEnumObject_GetNextDataObject(enumobj, &filedata)))
2440 const GUID *guid = NULL;
2442 hr = IDirectXFileData_GetType(filedata, &guid);
2443 if (SUCCEEDED(hr)) {
2444 if (IsEqualGUID(guid, &TID_D3DRMMesh)) {
2445 hr = alloc_hier->lpVtbl->CreateFrame(alloc_hier, NULL, next_frame);
2446 if (FAILED(hr)) {
2447 hr = E_FAIL;
2448 goto cleanup;
2451 D3DXMatrixIdentity(&(*next_frame)->TransformationMatrix);
2453 hr = load_mesh_container(filedata, options, device, alloc_hier, &(*next_frame)->pMeshContainer);
2454 if (FAILED(hr)) goto cleanup;
2455 } else if (IsEqualGUID(guid, &TID_D3DRMFrame)) {
2456 hr = load_frame(filedata, options, device, alloc_hier, next_frame);
2457 if (FAILED(hr)) goto cleanup;
2459 while (*next_frame)
2460 next_frame = &(*next_frame)->pFrameSibling;
2463 IDirectXFileData_Release(filedata);
2464 filedata = NULL;
2465 if (FAILED(hr))
2466 goto cleanup;
2468 if (hr != DXFILEERR_NOMOREOBJECTS)
2469 goto cleanup;
2471 if (!first_frame) {
2472 hr = E_FAIL;
2473 } else if (first_frame->pFrameSibling) {
2474 D3DXFRAME *root_frame = NULL;
2475 hr = alloc_hier->lpVtbl->CreateFrame(alloc_hier, NULL, &root_frame);
2476 if (FAILED(hr)) {
2477 hr = E_FAIL;
2478 goto cleanup;
2480 D3DXMatrixIdentity(&root_frame->TransformationMatrix);
2481 root_frame->pFrameFirstChild = first_frame;
2482 *frame_hierarchy = root_frame;
2483 hr = D3D_OK;
2484 } else {
2485 *frame_hierarchy = first_frame;
2486 hr = D3D_OK;
2489 cleanup:
2490 if (FAILED(hr) && first_frame) D3DXFrameDestroy(first_frame, alloc_hier);
2491 if (filedata) IDirectXFileData_Release(filedata);
2492 if (enumobj) IDirectXFileEnumObject_Release(enumobj);
2493 if (dxfile) IDirectXFile_Release(dxfile);
2494 return hr;
2497 HRESULT WINAPI D3DXFrameDestroy(LPD3DXFRAME frame, LPD3DXALLOCATEHIERARCHY alloc_hier)
2499 HRESULT hr;
2500 BOOL last = FALSE;
2502 TRACE("(%p, %p)\n", frame, alloc_hier);
2504 if (!frame || !alloc_hier)
2505 return D3DERR_INVALIDCALL;
2507 while (!last) {
2508 D3DXMESHCONTAINER *container;
2509 D3DXFRAME *current_frame;
2511 if (frame->pFrameSibling) {
2512 current_frame = frame->pFrameSibling;
2513 frame->pFrameSibling = current_frame->pFrameSibling;
2514 current_frame->pFrameSibling = NULL;
2515 } else if (frame) {
2516 current_frame = frame;
2517 last = TRUE;
2520 if (current_frame->pFrameFirstChild) {
2521 hr = D3DXFrameDestroy(current_frame->pFrameFirstChild, alloc_hier);
2522 if (FAILED(hr)) return hr;
2523 current_frame->pFrameFirstChild = NULL;
2526 container = current_frame->pMeshContainer;
2527 while (container) {
2528 D3DXMESHCONTAINER *next_container = container->pNextMeshContainer;
2529 hr = alloc_hier->lpVtbl->DestroyMeshContainer(alloc_hier, container);
2530 if (FAILED(hr)) return hr;
2531 container = next_container;
2533 hr = alloc_hier->lpVtbl->DestroyFrame(alloc_hier, current_frame);
2534 if (FAILED(hr)) return hr;
2536 return D3D_OK;
2539 HRESULT WINAPI D3DXCreateBox(LPDIRECT3DDEVICE9 device, FLOAT width, FLOAT height,
2540 FLOAT depth, LPD3DXMESH* mesh, LPD3DXBUFFER* adjacency)
2542 FIXME("(%p, %f, %f, %f, %p, %p): stub\n", device, width, height, depth, mesh, adjacency);
2544 return E_NOTIMPL;
2547 struct vertex
2549 D3DXVECTOR3 position;
2550 D3DXVECTOR3 normal;
2553 typedef WORD face[3];
2555 struct sincos_table
2557 float *sin;
2558 float *cos;
2561 static void free_sincos_table(struct sincos_table *sincos_table)
2563 HeapFree(GetProcessHeap(), 0, sincos_table->cos);
2564 HeapFree(GetProcessHeap(), 0, sincos_table->sin);
2567 /* pre compute sine and cosine tables; caller must free */
2568 static BOOL compute_sincos_table(struct sincos_table *sincos_table, float angle_start, float angle_step, int n)
2570 float angle;
2571 int i;
2573 sincos_table->sin = HeapAlloc(GetProcessHeap(), 0, n * sizeof(*sincos_table->sin));
2574 if (!sincos_table->sin)
2576 return FALSE;
2578 sincos_table->cos = HeapAlloc(GetProcessHeap(), 0, n * sizeof(*sincos_table->cos));
2579 if (!sincos_table->cos)
2581 HeapFree(GetProcessHeap(), 0, sincos_table->sin);
2582 return FALSE;
2585 angle = angle_start;
2586 for (i = 0; i < n; i++)
2588 sincos_table->sin[i] = sin(angle);
2589 sincos_table->cos[i] = cos(angle);
2590 angle += angle_step;
2593 return TRUE;
2596 static WORD vertex_index(UINT slices, int slice, int stack)
2598 return stack*slices+slice+1;
2601 HRESULT WINAPI D3DXCreateSphere(LPDIRECT3DDEVICE9 device, FLOAT radius, UINT slices,
2602 UINT stacks, LPD3DXMESH* mesh, LPD3DXBUFFER* adjacency)
2604 DWORD number_of_vertices, number_of_faces;
2605 HRESULT hr;
2606 ID3DXMesh *sphere;
2607 struct vertex *vertices;
2608 face *faces;
2609 float phi_step, phi_start;
2610 struct sincos_table phi;
2611 float theta_step, theta, sin_theta, cos_theta;
2612 DWORD vertex, face;
2613 int slice, stack;
2615 TRACE("(%p, %f, %u, %u, %p, %p)\n", device, radius, slices, stacks, mesh, adjacency);
2617 if (!device || radius < 0.0f || slices < 2 || stacks < 2 || !mesh)
2619 return D3DERR_INVALIDCALL;
2622 if (adjacency)
2624 FIXME("Case of adjacency != NULL not implemented.\n");
2625 return E_NOTIMPL;
2628 number_of_vertices = 2 + slices * (stacks-1);
2629 number_of_faces = 2 * slices + (stacks - 2) * (2 * slices);
2631 hr = D3DXCreateMeshFVF(number_of_faces, number_of_vertices, D3DXMESH_MANAGED,
2632 D3DFVF_XYZ | D3DFVF_NORMAL, device, &sphere);
2633 if (FAILED(hr))
2635 return hr;
2638 hr = sphere->lpVtbl->LockVertexBuffer(sphere, D3DLOCK_DISCARD, (LPVOID *)&vertices);
2639 if (FAILED(hr))
2641 sphere->lpVtbl->Release(sphere);
2642 return hr;
2645 hr = sphere->lpVtbl->LockIndexBuffer(sphere, D3DLOCK_DISCARD, (LPVOID *)&faces);
2646 if (FAILED(hr))
2648 sphere->lpVtbl->UnlockVertexBuffer(sphere);
2649 sphere->lpVtbl->Release(sphere);
2650 return hr;
2653 /* phi = angle on xz plane wrt z axis */
2654 phi_step = -2 * M_PI / slices;
2655 phi_start = M_PI / 2;
2657 if (!compute_sincos_table(&phi, phi_start, phi_step, slices))
2659 sphere->lpVtbl->UnlockIndexBuffer(sphere);
2660 sphere->lpVtbl->UnlockVertexBuffer(sphere);
2661 sphere->lpVtbl->Release(sphere);
2662 return E_OUTOFMEMORY;
2665 /* theta = angle on xy plane wrt x axis */
2666 theta_step = M_PI / stacks;
2667 theta = theta_step;
2669 vertex = 0;
2670 face = 0;
2671 stack = 0;
2673 vertices[vertex].normal.x = 0.0f;
2674 vertices[vertex].normal.y = 0.0f;
2675 vertices[vertex].normal.z = 1.0f;
2676 vertices[vertex].position.x = 0.0f;
2677 vertices[vertex].position.y = 0.0f;
2678 vertices[vertex].position.z = radius;
2679 vertex++;
2681 for (stack = 0; stack < stacks - 1; stack++)
2683 sin_theta = sin(theta);
2684 cos_theta = cos(theta);
2686 for (slice = 0; slice < slices; slice++)
2688 vertices[vertex].normal.x = sin_theta * phi.cos[slice];
2689 vertices[vertex].normal.y = sin_theta * phi.sin[slice];
2690 vertices[vertex].normal.z = cos_theta;
2691 vertices[vertex].position.x = radius * sin_theta * phi.cos[slice];
2692 vertices[vertex].position.y = radius * sin_theta * phi.sin[slice];
2693 vertices[vertex].position.z = radius * cos_theta;
2694 vertex++;
2696 if (slice > 0)
2698 if (stack == 0)
2700 /* top stack is triangle fan */
2701 faces[face][0] = 0;
2702 faces[face][1] = slice + 1;
2703 faces[face][2] = slice;
2704 face++;
2706 else
2708 /* stacks in between top and bottom are quad strips */
2709 faces[face][0] = vertex_index(slices, slice-1, stack-1);
2710 faces[face][1] = vertex_index(slices, slice, stack-1);
2711 faces[face][2] = vertex_index(slices, slice-1, stack);
2712 face++;
2714 faces[face][0] = vertex_index(slices, slice, stack-1);
2715 faces[face][1] = vertex_index(slices, slice, stack);
2716 faces[face][2] = vertex_index(slices, slice-1, stack);
2717 face++;
2722 theta += theta_step;
2724 if (stack == 0)
2726 faces[face][0] = 0;
2727 faces[face][1] = 1;
2728 faces[face][2] = slice;
2729 face++;
2731 else
2733 faces[face][0] = vertex_index(slices, slice-1, stack-1);
2734 faces[face][1] = vertex_index(slices, 0, stack-1);
2735 faces[face][2] = vertex_index(slices, slice-1, stack);
2736 face++;
2738 faces[face][0] = vertex_index(slices, 0, stack-1);
2739 faces[face][1] = vertex_index(slices, 0, stack);
2740 faces[face][2] = vertex_index(slices, slice-1, stack);
2741 face++;
2745 vertices[vertex].position.x = 0.0f;
2746 vertices[vertex].position.y = 0.0f;
2747 vertices[vertex].position.z = -radius;
2748 vertices[vertex].normal.x = 0.0f;
2749 vertices[vertex].normal.y = 0.0f;
2750 vertices[vertex].normal.z = -1.0f;
2752 /* bottom stack is triangle fan */
2753 for (slice = 1; slice < slices; slice++)
2755 faces[face][0] = vertex_index(slices, slice-1, stack-1);
2756 faces[face][1] = vertex_index(slices, slice, stack-1);
2757 faces[face][2] = vertex;
2758 face++;
2761 faces[face][0] = vertex_index(slices, slice-1, stack-1);
2762 faces[face][1] = vertex_index(slices, 0, stack-1);
2763 faces[face][2] = vertex;
2765 free_sincos_table(&phi);
2766 sphere->lpVtbl->UnlockIndexBuffer(sphere);
2767 sphere->lpVtbl->UnlockVertexBuffer(sphere);
2768 *mesh = sphere;
2770 return D3D_OK;
2773 HRESULT WINAPI D3DXCreateCylinder(LPDIRECT3DDEVICE9 device, FLOAT radius1, FLOAT radius2, FLOAT length, UINT slices,
2774 UINT stacks, LPD3DXMESH* mesh, LPD3DXBUFFER* adjacency)
2776 DWORD number_of_vertices, number_of_faces;
2777 HRESULT hr;
2778 ID3DXMesh *cylinder;
2779 struct vertex *vertices;
2780 face *faces;
2781 float theta_step, theta_start;
2782 struct sincos_table theta;
2783 float delta_radius, radius, radius_step;
2784 float z, z_step, z_normal;
2785 DWORD vertex, face;
2786 int slice, stack;
2788 TRACE("(%p, %f, %f, %f, %u, %u, %p, %p)\n", device, radius1, radius2, length, slices, stacks, mesh, adjacency);
2790 if (device == NULL || radius1 < 0.0f || radius2 < 0.0f || length < 0.0f || slices < 2 || stacks < 1 || mesh == NULL)
2792 return D3DERR_INVALIDCALL;
2795 if (adjacency)
2797 FIXME("Case of adjacency != NULL not implemented.\n");
2798 return E_NOTIMPL;
2801 number_of_vertices = 2 + (slices * (3 + stacks));
2802 number_of_faces = 2 * slices + stacks * (2 * slices);
2804 hr = D3DXCreateMeshFVF(number_of_faces, number_of_vertices, D3DXMESH_MANAGED,
2805 D3DFVF_XYZ | D3DFVF_NORMAL, device, &cylinder);
2806 if (FAILED(hr))
2808 return hr;
2811 hr = cylinder->lpVtbl->LockVertexBuffer(cylinder, D3DLOCK_DISCARD, (LPVOID *)&vertices);
2812 if (FAILED(hr))
2814 cylinder->lpVtbl->Release(cylinder);
2815 return hr;
2818 hr = cylinder->lpVtbl->LockIndexBuffer(cylinder, D3DLOCK_DISCARD, (LPVOID *)&faces);
2819 if (FAILED(hr))
2821 cylinder->lpVtbl->UnlockVertexBuffer(cylinder);
2822 cylinder->lpVtbl->Release(cylinder);
2823 return hr;
2826 /* theta = angle on xy plane wrt x axis */
2827 theta_step = -2 * M_PI / slices;
2828 theta_start = M_PI / 2;
2830 if (!compute_sincos_table(&theta, theta_start, theta_step, slices))
2832 cylinder->lpVtbl->UnlockIndexBuffer(cylinder);
2833 cylinder->lpVtbl->UnlockVertexBuffer(cylinder);
2834 cylinder->lpVtbl->Release(cylinder);
2835 return E_OUTOFMEMORY;
2838 vertex = 0;
2839 face = 0;
2841 delta_radius = radius1 - radius2;
2842 radius = radius1;
2843 radius_step = delta_radius / stacks;
2845 z = -length / 2;
2846 z_step = length / stacks;
2847 z_normal = delta_radius / length;
2848 if (isnan(z_normal))
2850 z_normal = 0.0f;
2853 vertices[vertex].normal.x = 0.0f;
2854 vertices[vertex].normal.y = 0.0f;
2855 vertices[vertex].normal.z = -1.0f;
2856 vertices[vertex].position.x = 0.0f;
2857 vertices[vertex].position.y = 0.0f;
2858 vertices[vertex++].position.z = z;
2860 for (slice = 0; slice < slices; slice++, vertex++)
2862 vertices[vertex].normal.x = 0.0f;
2863 vertices[vertex].normal.y = 0.0f;
2864 vertices[vertex].normal.z = -1.0f;
2865 vertices[vertex].position.x = radius * theta.cos[slice];
2866 vertices[vertex].position.y = radius * theta.sin[slice];
2867 vertices[vertex].position.z = z;
2869 if (slice > 0)
2871 faces[face][0] = 0;
2872 faces[face][1] = slice;
2873 faces[face++][2] = slice + 1;
2877 faces[face][0] = 0;
2878 faces[face][1] = slice;
2879 faces[face++][2] = 1;
2881 for (stack = 1; stack <= stacks+1; stack++)
2883 for (slice = 0; slice < slices; slice++, vertex++)
2885 vertices[vertex].normal.x = theta.cos[slice];
2886 vertices[vertex].normal.y = theta.sin[slice];
2887 vertices[vertex].normal.z = z_normal;
2888 D3DXVec3Normalize(&vertices[vertex].normal, &vertices[vertex].normal);
2889 vertices[vertex].position.x = radius * theta.cos[slice];
2890 vertices[vertex].position.y = radius * theta.sin[slice];
2891 vertices[vertex].position.z = z;
2893 if (stack > 1 && slice > 0)
2895 faces[face][0] = vertex_index(slices, slice-1, stack-1);
2896 faces[face][1] = vertex_index(slices, slice-1, stack);
2897 faces[face++][2] = vertex_index(slices, slice, stack-1);
2899 faces[face][0] = vertex_index(slices, slice, stack-1);
2900 faces[face][1] = vertex_index(slices, slice-1, stack);
2901 faces[face++][2] = vertex_index(slices, slice, stack);
2905 if (stack > 1)
2907 faces[face][0] = vertex_index(slices, slice-1, stack-1);
2908 faces[face][1] = vertex_index(slices, slice-1, stack);
2909 faces[face++][2] = vertex_index(slices, 0, stack-1);
2911 faces[face][0] = vertex_index(slices, 0, stack-1);
2912 faces[face][1] = vertex_index(slices, slice-1, stack);
2913 faces[face++][2] = vertex_index(slices, 0, stack);
2916 if (stack < stacks + 1)
2918 z += z_step;
2919 radius -= radius_step;
2923 for (slice = 0; slice < slices; slice++, vertex++)
2925 vertices[vertex].normal.x = 0.0f;
2926 vertices[vertex].normal.y = 0.0f;
2927 vertices[vertex].normal.z = 1.0f;
2928 vertices[vertex].position.x = radius * theta.cos[slice];
2929 vertices[vertex].position.y = radius * theta.sin[slice];
2930 vertices[vertex].position.z = z;
2932 if (slice > 0)
2934 faces[face][0] = vertex_index(slices, slice-1, stack);
2935 faces[face][1] = number_of_vertices - 1;
2936 faces[face++][2] = vertex_index(slices, slice, stack);
2940 vertices[vertex].position.x = 0.0f;
2941 vertices[vertex].position.y = 0.0f;
2942 vertices[vertex].position.z = z;
2943 vertices[vertex].normal.x = 0.0f;
2944 vertices[vertex].normal.y = 0.0f;
2945 vertices[vertex].normal.z = 1.0f;
2947 faces[face][0] = vertex_index(slices, slice-1, stack);
2948 faces[face][1] = number_of_vertices - 1;
2949 faces[face][2] = vertex_index(slices, 0, stack);
2951 free_sincos_table(&theta);
2952 cylinder->lpVtbl->UnlockIndexBuffer(cylinder);
2953 cylinder->lpVtbl->UnlockVertexBuffer(cylinder);
2954 *mesh = cylinder;
2956 return D3D_OK;
2959 HRESULT WINAPI D3DXCreateTeapot(LPDIRECT3DDEVICE9 device, LPD3DXMESH *mesh, LPD3DXBUFFER* adjacency)
2961 FIXME("(%p, %p, %p): stub\n", device, mesh, adjacency);
2963 return E_NOTIMPL;
2966 HRESULT WINAPI D3DXCreateTextA(LPDIRECT3DDEVICE9 device,
2967 HDC hdc, LPCSTR text,
2968 FLOAT deviation, FLOAT extrusion,
2969 LPD3DXMESH *mesh, LPD3DXBUFFER *adjacency,
2970 LPGLYPHMETRICSFLOAT glyphmetrics)
2972 HRESULT hr;
2973 int len;
2974 LPWSTR textW;
2976 TRACE("(%p, %p, %s, %f, %f, %p, %p, %p)\n", device, hdc,
2977 debugstr_a(text), deviation, extrusion, mesh, adjacency, glyphmetrics);
2979 if (!text)
2980 return D3DERR_INVALIDCALL;
2982 len = MultiByteToWideChar(CP_ACP, 0, text, -1, NULL, 0);
2983 textW = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
2984 MultiByteToWideChar(CP_ACP, 0, text, -1, textW, len);
2986 hr = D3DXCreateTextW(device, hdc, textW, deviation, extrusion,
2987 mesh, adjacency, glyphmetrics);
2988 HeapFree(GetProcessHeap(), 0, textW);
2990 return hr;
2993 enum pointtype {
2994 POINTTYPE_CURVE = 0,
2995 POINTTYPE_CORNER,
2996 POINTTYPE_CURVE_START,
2997 POINTTYPE_CURVE_END,
2998 POINTTYPE_CURVE_MIDDLE,
3001 struct point2d
3003 D3DXVECTOR2 pos;
3004 enum pointtype corner;
3007 struct dynamic_array
3009 int count, capacity;
3010 void *items;
3013 /* is a dynamic_array */
3014 struct outline
3016 int count, capacity;
3017 struct point2d *items;
3020 /* is a dynamic_array */
3021 struct outline_array
3023 int count, capacity;
3024 struct outline *items;
3027 struct face_array
3029 int count;
3030 face *items;
3033 struct point2d_index
3035 struct outline *outline;
3036 int vertex;
3039 struct point2d_index_array
3041 int count;
3042 struct point2d_index *items;
3045 struct glyphinfo
3047 struct outline_array outlines;
3048 struct face_array faces;
3049 struct point2d_index_array ordered_vertices;
3050 float offset_x;
3053 /* is an dynamic_array */
3054 struct word_array
3056 int count, capacity;
3057 WORD *items;
3060 /* complex polygons are split into monotone polygons, which have
3061 * at most 2 intersections with the vertical sweep line */
3062 struct triangulation
3064 struct word_array vertex_stack;
3065 BOOL last_on_top, merging;
3068 /* is an dynamic_array */
3069 struct triangulation_array
3071 int count, capacity;
3072 struct triangulation *items;
3074 struct glyphinfo *glyph;
3077 static BOOL reserve(struct dynamic_array *array, int count, int itemsize)
3079 if (count > array->capacity) {
3080 void *new_buffer;
3081 int new_capacity;
3082 if (array->items && array->capacity) {
3083 new_capacity = max(array->capacity * 2, count);
3084 new_buffer = HeapReAlloc(GetProcessHeap(), 0, array->items, new_capacity * itemsize);
3085 } else {
3086 new_capacity = max(16, count);
3087 new_buffer = HeapAlloc(GetProcessHeap(), 0, new_capacity * itemsize);
3089 if (!new_buffer)
3090 return FALSE;
3091 array->items = new_buffer;
3092 array->capacity = new_capacity;
3094 return TRUE;
3097 static struct point2d *add_points(struct outline *array, int num)
3099 struct point2d *item;
3101 if (!reserve((struct dynamic_array *)array, array->count + num, sizeof(array->items[0])))
3102 return NULL;
3104 item = &array->items[array->count];
3105 array->count += num;
3106 return item;
3109 static struct outline *add_outline(struct outline_array *array)
3111 struct outline *item;
3113 if (!reserve((struct dynamic_array *)array, array->count + 1, sizeof(array->items[0])))
3114 return NULL;
3116 item = &array->items[array->count++];
3117 ZeroMemory(item, sizeof(*item));
3118 return item;
3121 static inline face *add_face(struct face_array *array)
3123 return &array->items[array->count++];
3126 static struct triangulation *add_triangulation(struct triangulation_array *array)
3128 struct triangulation *item;
3130 if (!reserve((struct dynamic_array *)array, array->count + 1, sizeof(array->items[0])))
3131 return NULL;
3133 item = &array->items[array->count++];
3134 ZeroMemory(item, sizeof(*item));
3135 return item;
3138 static HRESULT add_vertex_index(struct word_array *array, WORD vertex_index)
3140 if (!reserve((struct dynamic_array *)array, array->count + 1, sizeof(array->items[0])))
3141 return E_OUTOFMEMORY;
3143 array->items[array->count++] = vertex_index;
3144 return S_OK;
3147 /* assume fixed point numbers can be converted to float point in place */
3148 C_ASSERT(sizeof(FIXED) == sizeof(float));
3149 C_ASSERT(sizeof(POINTFX) == sizeof(D3DXVECTOR2));
3151 static inline D3DXVECTOR2 *convert_fixed_to_float(POINTFX *pt, int count, float emsquare)
3153 D3DXVECTOR2 *ret = (D3DXVECTOR2*)pt;
3154 while (count--) {
3155 D3DXVECTOR2 *pt_flt = (D3DXVECTOR2*)pt;
3156 pt_flt->x = (pt->x.value + pt->x.fract / (float)0x10000) / emsquare;
3157 pt_flt->y = (pt->y.value + pt->y.fract / (float)0x10000) / emsquare;
3158 pt++;
3160 return ret;
3163 static HRESULT add_bezier_points(struct outline *outline, const D3DXVECTOR2 *p1,
3164 const D3DXVECTOR2 *p2, const D3DXVECTOR2 *p3,
3165 float max_deviation_sq)
3167 D3DXVECTOR2 split1 = {0, 0}, split2 = {0, 0}, middle, vec;
3168 float deviation_sq;
3170 D3DXVec2Scale(&split1, D3DXVec2Add(&split1, p1, p2), 0.5f);
3171 D3DXVec2Scale(&split2, D3DXVec2Add(&split2, p2, p3), 0.5f);
3172 D3DXVec2Scale(&middle, D3DXVec2Add(&middle, &split1, &split2), 0.5f);
3174 deviation_sq = D3DXVec2LengthSq(D3DXVec2Subtract(&vec, &middle, p2));
3175 if (deviation_sq < max_deviation_sq) {
3176 struct point2d *pt = add_points(outline, 1);
3177 if (!pt) return E_OUTOFMEMORY;
3178 pt->pos = *p2;
3179 pt->corner = POINTTYPE_CURVE;
3180 /* the end point is omitted because the end line merges into the next segment of
3181 * the split bezier curve, and the end of the split bezier curve is added outside
3182 * this recursive function. */
3183 } else {
3184 HRESULT hr = add_bezier_points(outline, p1, &split1, &middle, max_deviation_sq);
3185 if (hr != S_OK) return hr;
3186 hr = add_bezier_points(outline, &middle, &split2, p3, max_deviation_sq);
3187 if (hr != S_OK) return hr;
3190 return S_OK;
3193 static inline BOOL is_direction_similar(D3DXVECTOR2 *dir1, D3DXVECTOR2 *dir2, float cos_theta)
3195 /* dot product = cos(theta) */
3196 return D3DXVec2Dot(dir1, dir2) > cos_theta;
3199 static inline D3DXVECTOR2 *unit_vec2(D3DXVECTOR2 *dir, const D3DXVECTOR2 *pt1, const D3DXVECTOR2 *pt2)
3201 return D3DXVec2Normalize(D3DXVec2Subtract(dir, pt2, pt1), dir);
3204 struct cos_table
3206 float cos_half;
3207 float cos_45;
3208 float cos_90;
3211 static BOOL attempt_line_merge(struct outline *outline,
3212 int pt_index,
3213 const D3DXVECTOR2 *nextpt,
3214 BOOL to_curve,
3215 const struct cos_table *table)
3217 D3DXVECTOR2 curdir, lastdir;
3218 struct point2d *prevpt, *pt;
3219 BOOL ret = FALSE;
3221 pt = &outline->items[pt_index];
3222 pt_index = (pt_index - 1 + outline->count) % outline->count;
3223 prevpt = &outline->items[pt_index];
3225 if (to_curve)
3226 pt->corner = pt->corner != POINTTYPE_CORNER ? POINTTYPE_CURVE_MIDDLE : POINTTYPE_CURVE_START;
3228 if (outline->count < 2)
3229 return FALSE;
3231 /* remove last point if the next line continues the last line */
3232 unit_vec2(&lastdir, &prevpt->pos, &pt->pos);
3233 unit_vec2(&curdir, &pt->pos, nextpt);
3234 if (is_direction_similar(&lastdir, &curdir, table->cos_half))
3236 outline->count--;
3237 if (pt->corner == POINTTYPE_CURVE_END)
3238 prevpt->corner = pt->corner;
3239 if (prevpt->corner == POINTTYPE_CURVE_END && to_curve)
3240 prevpt->corner = POINTTYPE_CURVE_MIDDLE;
3241 pt = prevpt;
3243 ret = TRUE;
3244 if (outline->count < 2)
3245 return ret;
3247 pt_index = (pt_index - 1 + outline->count) % outline->count;
3248 prevpt = &outline->items[pt_index];
3249 unit_vec2(&lastdir, &prevpt->pos, &pt->pos);
3250 unit_vec2(&curdir, &pt->pos, nextpt);
3252 return ret;
3255 static HRESULT create_outline(struct glyphinfo *glyph, void *raw_outline, int datasize,
3256 float max_deviation_sq, float emsquare, const struct cos_table *cos_table)
3258 TTPOLYGONHEADER *header = (TTPOLYGONHEADER *)raw_outline;
3260 while ((char *)header < (char *)raw_outline + datasize)
3262 TTPOLYCURVE *curve = (TTPOLYCURVE *)(header + 1);
3263 struct point2d *lastpt, *pt;
3264 D3DXVECTOR2 lastdir;
3265 D3DXVECTOR2 *pt_flt;
3266 int j;
3267 struct outline *outline = add_outline(&glyph->outlines);
3269 if (!outline)
3270 return E_OUTOFMEMORY;
3272 pt = add_points(outline, 1);
3273 if (!pt)
3274 return E_OUTOFMEMORY;
3275 pt_flt = convert_fixed_to_float(&header->pfxStart, 1, emsquare);
3276 pt->pos = *pt_flt;
3277 pt->corner = POINTTYPE_CORNER;
3279 if (header->dwType != TT_POLYGON_TYPE)
3280 FIXME("Unknown header type %d\n", header->dwType);
3282 while ((char *)curve < (char *)header + header->cb)
3284 D3DXVECTOR2 bezier_start = outline->items[outline->count - 1].pos;
3285 BOOL to_curve = curve->wType != TT_PRIM_LINE && curve->cpfx > 1;
3287 if (!curve->cpfx) {
3288 curve = (TTPOLYCURVE *)&curve->apfx[curve->cpfx];
3289 continue;
3292 pt_flt = convert_fixed_to_float(curve->apfx, curve->cpfx, emsquare);
3294 attempt_line_merge(outline, outline->count - 1, &pt_flt[0], to_curve, cos_table);
3296 if (to_curve)
3298 HRESULT hr;
3299 int count = curve->cpfx;
3300 j = 0;
3302 while (count > 2)
3304 D3DXVECTOR2 bezier_end;
3306 D3DXVec2Scale(&bezier_end, D3DXVec2Add(&bezier_end, &pt_flt[j], &pt_flt[j+1]), 0.5f);
3307 hr = add_bezier_points(outline, &bezier_start, &pt_flt[j], &bezier_end, max_deviation_sq);
3308 if (hr != S_OK)
3309 return hr;
3310 bezier_start = bezier_end;
3311 count--;
3312 j++;
3314 hr = add_bezier_points(outline, &bezier_start, &pt_flt[j], &pt_flt[j+1], max_deviation_sq);
3315 if (hr != S_OK)
3316 return hr;
3318 pt = add_points(outline, 1);
3319 if (!pt)
3320 return E_OUTOFMEMORY;
3321 j++;
3322 pt->pos = pt_flt[j];
3323 pt->corner = POINTTYPE_CURVE_END;
3324 } else {
3325 pt = add_points(outline, curve->cpfx);
3326 if (!pt)
3327 return E_OUTOFMEMORY;
3328 for (j = 0; j < curve->cpfx; j++)
3330 pt->pos = pt_flt[j];
3331 pt->corner = POINTTYPE_CORNER;
3332 pt++;
3336 curve = (TTPOLYCURVE *)&curve->apfx[curve->cpfx];
3339 /* remove last point if the next line continues the last line */
3340 if (outline->count >= 3) {
3341 BOOL to_curve;
3343 lastpt = &outline->items[outline->count - 1];
3344 pt = &outline->items[0];
3345 if (pt->pos.x == lastpt->pos.x && pt->pos.y == lastpt->pos.y) {
3346 if (lastpt->corner == POINTTYPE_CURVE_END)
3348 if (pt->corner == POINTTYPE_CURVE_START)
3349 pt->corner = POINTTYPE_CURVE_MIDDLE;
3350 else
3351 pt->corner = POINTTYPE_CURVE_END;
3353 outline->count--;
3354 lastpt = &outline->items[outline->count - 1];
3355 } else {
3356 /* outline closed with a line from end to start point */
3357 attempt_line_merge(outline, outline->count - 1, &pt->pos, FALSE, cos_table);
3359 lastpt = &outline->items[0];
3360 to_curve = lastpt->corner != POINTTYPE_CORNER && lastpt->corner != POINTTYPE_CURVE_END;
3361 if (lastpt->corner == POINTTYPE_CURVE_START)
3362 lastpt->corner = POINTTYPE_CORNER;
3363 pt = &outline->items[1];
3364 if (attempt_line_merge(outline, 0, &pt->pos, to_curve, cos_table))
3365 *lastpt = outline->items[outline->count];
3368 lastpt = &outline->items[outline->count - 1];
3369 pt = &outline->items[0];
3370 unit_vec2(&lastdir, &lastpt->pos, &pt->pos);
3371 for (j = 0; j < outline->count; j++)
3373 D3DXVECTOR2 curdir;
3375 lastpt = pt;
3376 pt = &outline->items[(j + 1) % outline->count];
3377 unit_vec2(&curdir, &lastpt->pos, &pt->pos);
3379 switch (lastpt->corner)
3381 case POINTTYPE_CURVE_START:
3382 case POINTTYPE_CURVE_END:
3383 if (!is_direction_similar(&lastdir, &curdir, cos_table->cos_45))
3384 lastpt->corner = POINTTYPE_CORNER;
3385 break;
3386 case POINTTYPE_CURVE_MIDDLE:
3387 if (!is_direction_similar(&lastdir, &curdir, cos_table->cos_90))
3388 lastpt->corner = POINTTYPE_CORNER;
3389 else
3390 lastpt->corner = POINTTYPE_CURVE;
3391 break;
3392 default:
3393 break;
3395 lastdir = curdir;
3398 header = (TTPOLYGONHEADER *)((char *)header + header->cb);
3400 return S_OK;
3403 /* Get the y-distance from a line to a point */
3404 static float get_line_to_point_y_distance(D3DXVECTOR2 *line_pt1,
3405 D3DXVECTOR2 *line_pt2,
3406 D3DXVECTOR2 *point)
3408 D3DXVECTOR2 line_vec = {0, 0};
3409 float line_pt_dx;
3410 float line_y;
3412 D3DXVec2Subtract(&line_vec, line_pt2, line_pt1);
3413 line_pt_dx = point->x - line_pt1->x;
3414 line_y = line_pt1->y + (line_vec.y * line_pt_dx) / line_vec.x;
3415 return point->y - line_y;
3418 static D3DXVECTOR2 *get_indexed_point(struct point2d_index *pt_idx)
3420 return &pt_idx->outline->items[pt_idx->vertex].pos;
3423 static D3DXVECTOR2 *get_ordered_vertex(struct glyphinfo *glyph, WORD index)
3425 return get_indexed_point(&glyph->ordered_vertices.items[index]);
3428 static void remove_triangulation(struct triangulation_array *array, struct triangulation *item)
3430 HeapFree(GetProcessHeap(), 0, item->vertex_stack.items);
3431 MoveMemory(item, item + 1, (char*)&array->items[array->count] - (char*)(item + 1));
3432 array->count--;
3435 static HRESULT triangulation_add_point(struct triangulation **t_ptr,
3436 struct triangulation_array *triangulations,
3437 WORD vtx_idx,
3438 BOOL to_top)
3440 struct glyphinfo *glyph = triangulations->glyph;
3441 struct triangulation *t = *t_ptr;
3442 HRESULT hr;
3443 face *face;
3444 int f1, f2;
3446 if (t->last_on_top) {
3447 f1 = 1;
3448 f2 = 2;
3449 } else {
3450 f1 = 2;
3451 f2 = 1;
3454 if (t->last_on_top != to_top && t->vertex_stack.count > 1) {
3455 /* consume all vertices on the stack */
3456 WORD last_pt = t->vertex_stack.items[0];
3457 int i;
3458 for (i = 1; i < t->vertex_stack.count; i++)
3460 face = add_face(&glyph->faces);
3461 if (!face) return E_OUTOFMEMORY;
3462 (*face)[0] = vtx_idx;
3463 (*face)[f1] = last_pt;
3464 (*face)[f2] = last_pt = t->vertex_stack.items[i];
3466 t->vertex_stack.items[0] = last_pt;
3467 t->vertex_stack.count = 1;
3468 } else if (t->vertex_stack.count > 1) {
3469 int i = t->vertex_stack.count - 1;
3470 D3DXVECTOR2 *point = get_ordered_vertex(glyph, vtx_idx);
3471 WORD top_idx = t->vertex_stack.items[i--];
3472 D3DXVECTOR2 *top_pt = get_ordered_vertex(glyph, top_idx);
3474 while (i >= 0)
3476 WORD prev_idx = t->vertex_stack.items[i--];
3477 D3DXVECTOR2 *prev_pt = get_ordered_vertex(glyph, prev_idx);
3479 if (prev_pt->x != top_pt->x &&
3480 ((to_top && get_line_to_point_y_distance(prev_pt, top_pt, point) > 0) ||
3481 (!to_top && get_line_to_point_y_distance(prev_pt, top_pt, point) < 0)))
3482 break;
3484 face = add_face(&glyph->faces);
3485 if (!face) return E_OUTOFMEMORY;
3486 (*face)[0] = vtx_idx;
3487 (*face)[f1] = prev_idx;
3488 (*face)[f2] = top_idx;
3490 top_pt = prev_pt;
3491 top_idx = prev_idx;
3492 t->vertex_stack.count--;
3495 t->last_on_top = to_top;
3497 hr = add_vertex_index(&t->vertex_stack, vtx_idx);
3499 if (hr == S_OK && t->merging) {
3500 struct triangulation *t2;
3502 t2 = to_top ? t - 1 : t + 1;
3503 t2->merging = FALSE;
3504 hr = triangulation_add_point(&t2, triangulations, vtx_idx, to_top);
3505 if (hr != S_OK) return hr;
3506 remove_triangulation(triangulations, t);
3507 if (t2 > t)
3508 t2--;
3509 *t_ptr = t2;
3511 return hr;
3514 /* check if the point is next on the outline for either the top or bottom */
3515 static D3DXVECTOR2 *triangulation_get_next_point(struct triangulation *t, struct glyphinfo *glyph, BOOL on_top)
3517 int i = t->last_on_top == on_top ? t->vertex_stack.count - 1 : 0;
3518 WORD idx = t->vertex_stack.items[i];
3519 struct point2d_index *pt_idx = &glyph->ordered_vertices.items[idx];
3520 struct outline *outline = pt_idx->outline;
3522 if (on_top)
3523 i = (pt_idx->vertex + outline->count - 1) % outline->count;
3524 else
3525 i = (pt_idx->vertex + 1) % outline->count;
3527 return &outline->items[i].pos;
3530 static int compare_vertex_indices(const void *a, const void *b)
3532 const struct point2d_index *idx1 = a, *idx2 = b;
3533 const D3DXVECTOR2 *p1 = &idx1->outline->items[idx1->vertex].pos;
3534 const D3DXVECTOR2 *p2 = &idx2->outline->items[idx2->vertex].pos;
3535 float diff = p1->x - p2->x;
3537 if (diff == 0.0f)
3538 diff = p1->y - p2->y;
3540 return diff == 0.0f ? 0 : (diff > 0.0f ? -1 : 1);
3543 static HRESULT triangulate(struct triangulation_array *triangulations)
3545 int sweep_idx;
3546 HRESULT hr;
3547 struct glyphinfo *glyph = triangulations->glyph;
3548 int nb_vertices = 0;
3549 int i;
3550 struct point2d_index *idx_ptr;
3552 for (i = 0; i < glyph->outlines.count; i++)
3553 nb_vertices += glyph->outlines.items[i].count;
3555 glyph->ordered_vertices.items = HeapAlloc(GetProcessHeap(), 0,
3556 nb_vertices * sizeof(*glyph->ordered_vertices.items));
3557 if (!glyph->ordered_vertices.items)
3558 return E_OUTOFMEMORY;
3560 idx_ptr = glyph->ordered_vertices.items;
3561 for (i = 0; i < glyph->outlines.count; i++)
3563 struct outline *outline = &glyph->outlines.items[i];
3564 int j;
3566 idx_ptr->outline = outline;
3567 idx_ptr->vertex = 0;
3568 idx_ptr++;
3569 for (j = outline->count - 1; j > 0; j--)
3571 idx_ptr->outline = outline;
3572 idx_ptr->vertex = j;
3573 idx_ptr++;
3576 glyph->ordered_vertices.count = nb_vertices;
3578 /* Native implementation seems to try to create a triangle fan from
3579 * the first outline point if the glyph only has one outline. */
3580 if (glyph->outlines.count == 1)
3582 struct outline *outline = glyph->outlines.items;
3583 D3DXVECTOR2 *base = &outline->items[0].pos;
3584 D3DXVECTOR2 *last = &outline->items[1].pos;
3585 float ccw = 0;
3587 for (i = 2; i < outline->count; i++)
3589 D3DXVECTOR2 *next = &outline->items[i].pos;
3590 D3DXVECTOR2 v1 = {0.0f, 0.0f};
3591 D3DXVECTOR2 v2 = {0.0f, 0.0f};
3593 D3DXVec2Subtract(&v1, base, last);
3594 D3DXVec2Subtract(&v2, last, next);
3595 ccw = D3DXVec2CCW(&v1, &v2);
3596 if (ccw > 0.0f)
3597 break;
3599 last = next;
3601 if (ccw <= 0)
3603 glyph->faces.items = HeapAlloc(GetProcessHeap(), 0,
3604 (outline->count - 2) * sizeof(glyph->faces.items[0]));
3605 if (!glyph->faces.items)
3606 return E_OUTOFMEMORY;
3608 glyph->faces.count = outline->count - 2;
3609 for (i = 0; i < glyph->faces.count; i++)
3611 glyph->faces.items[i][0] = 0;
3612 glyph->faces.items[i][1] = i + 1;
3613 glyph->faces.items[i][2] = i + 2;
3615 return S_OK;
3619 /* Perform 2D polygon triangulation for complex glyphs.
3620 * Triangulation is performed using a sweep line concept, from right to left,
3621 * by processing vertices in sorted order. Complex polygons are split into
3622 * monotone polygons which are triangulated separately. */
3623 /* FIXME: The order of the faces is not consistent with the native implementation. */
3625 /* Reserve space for maximum possible faces from triangulation.
3626 * # faces for outer outlines = outline->count - 2
3627 * # faces for inner outlines = outline->count + 2
3628 * There must be at least 1 outer outline. */
3629 glyph->faces.items = HeapAlloc(GetProcessHeap(), 0,
3630 (nb_vertices + glyph->outlines.count * 2 - 4) * sizeof(glyph->faces.items[0]));
3631 if (!glyph->faces.items)
3632 return E_OUTOFMEMORY;
3634 qsort(glyph->ordered_vertices.items, nb_vertices,
3635 sizeof(glyph->ordered_vertices.items[0]), compare_vertex_indices);
3636 for (sweep_idx = 0; sweep_idx < glyph->ordered_vertices.count; sweep_idx++)
3638 int start = 0;
3639 int end = triangulations->count;
3641 while (start < end)
3643 D3DXVECTOR2 *sweep_vtx = get_ordered_vertex(glyph, sweep_idx);
3644 int current = (start + end) / 2;
3645 struct triangulation *t = &triangulations->items[current];
3646 BOOL on_top_outline = FALSE;
3647 D3DXVECTOR2 *top_next, *bottom_next;
3648 WORD top_idx, bottom_idx;
3650 if (t->merging && t->last_on_top)
3651 top_next = triangulation_get_next_point(t + 1, glyph, TRUE);
3652 else
3653 top_next = triangulation_get_next_point(t, glyph, TRUE);
3654 if (sweep_vtx == top_next)
3656 if (t->merging && t->last_on_top)
3657 t++;
3658 hr = triangulation_add_point(&t, triangulations, sweep_idx, TRUE);
3659 if (hr != S_OK) return hr;
3661 if (t + 1 < &triangulations->items[triangulations->count] &&
3662 triangulation_get_next_point(t + 1, glyph, FALSE) == sweep_vtx)
3664 /* point also on bottom outline of higher triangulation */
3665 struct triangulation *t2 = t + 1;
3666 hr = triangulation_add_point(&t2, triangulations, sweep_idx, FALSE);
3667 if (hr != S_OK) return hr;
3669 t->merging = TRUE;
3670 t2->merging = TRUE;
3672 on_top_outline = TRUE;
3675 if (t->merging && !t->last_on_top)
3676 bottom_next = triangulation_get_next_point(t - 1, glyph, FALSE);
3677 else
3678 bottom_next = triangulation_get_next_point(t, glyph, FALSE);
3679 if (sweep_vtx == bottom_next)
3681 if (t->merging && !t->last_on_top)
3682 t--;
3683 if (on_top_outline) {
3684 /* outline finished */
3685 remove_triangulation(triangulations, t);
3686 break;
3689 hr = triangulation_add_point(&t, triangulations, sweep_idx, FALSE);
3690 if (hr != S_OK) return hr;
3692 if (t > triangulations->items &&
3693 triangulation_get_next_point(t - 1, glyph, TRUE) == sweep_vtx)
3695 struct triangulation *t2 = t - 1;
3696 /* point also on top outline of lower triangulation */
3697 hr = triangulation_add_point(&t2, triangulations, sweep_idx, TRUE);
3698 if (hr != S_OK) return hr;
3699 t = t2 + 1; /* t may be invalidated by triangulation merging */
3701 t->merging = TRUE;
3702 t2->merging = TRUE;
3704 break;
3706 if (on_top_outline)
3707 break;
3709 if (t->last_on_top) {
3710 top_idx = t->vertex_stack.items[t->vertex_stack.count - 1];
3711 bottom_idx = t->vertex_stack.items[0];
3712 } else {
3713 top_idx = t->vertex_stack.items[0];
3714 bottom_idx = t->vertex_stack.items[t->vertex_stack.count - 1];
3717 /* check if the point is inside or outside this polygon */
3718 if (get_line_to_point_y_distance(get_ordered_vertex(glyph, top_idx),
3719 top_next, sweep_vtx) > 0)
3720 { /* above */
3721 start = current + 1;
3722 } else if (get_line_to_point_y_distance(get_ordered_vertex(glyph, bottom_idx),
3723 bottom_next, sweep_vtx) < 0)
3724 { /* below */
3725 end = current;
3726 } else if (t->merging) {
3727 /* inside, so cancel merging */
3728 struct triangulation *t2 = t->last_on_top ? t + 1 : t - 1;
3729 t->merging = FALSE;
3730 t2->merging = FALSE;
3731 hr = triangulation_add_point(&t, triangulations, sweep_idx, t->last_on_top);
3732 if (hr != S_OK) return hr;
3733 hr = triangulation_add_point(&t2, triangulations, sweep_idx, t2->last_on_top);
3734 if (hr != S_OK) return hr;
3735 break;
3736 } else {
3737 /* inside, so split polygon into two monotone parts */
3738 struct triangulation *t2 = add_triangulation(triangulations);
3739 if (!t2) return E_OUTOFMEMORY;
3740 MoveMemory(t + 1, t, (char*)(t2 + 1) - (char*)t);
3741 if (t->last_on_top) {
3742 t2 = t + 1;
3743 } else {
3744 t2 = t;
3745 t++;
3748 ZeroMemory(&t2->vertex_stack, sizeof(t2->vertex_stack));
3749 hr = add_vertex_index(&t2->vertex_stack, t->vertex_stack.items[t->vertex_stack.count - 1]);
3750 if (hr != S_OK) return hr;
3751 hr = add_vertex_index(&t2->vertex_stack, sweep_idx);
3752 if (hr != S_OK) return hr;
3753 t2->last_on_top = !t->last_on_top;
3755 hr = triangulation_add_point(&t, triangulations, sweep_idx, t->last_on_top);
3756 if (hr != S_OK) return hr;
3757 break;
3760 if (start >= end)
3762 struct triangulation *t;
3763 struct triangulation *t2 = add_triangulation(triangulations);
3764 if (!t2) return E_OUTOFMEMORY;
3765 t = &triangulations->items[start];
3766 MoveMemory(t + 1, t, (char*)(t2 + 1) - (char*)t);
3767 ZeroMemory(t, sizeof(*t));
3768 hr = add_vertex_index(&t->vertex_stack, sweep_idx);
3769 if (hr != S_OK) return hr;
3772 return S_OK;
3775 HRESULT WINAPI D3DXCreateTextW(LPDIRECT3DDEVICE9 device,
3776 HDC hdc, LPCWSTR text,
3777 FLOAT deviation, FLOAT extrusion,
3778 LPD3DXMESH *mesh_ptr, LPD3DXBUFFER *adjacency,
3779 LPGLYPHMETRICSFLOAT glyphmetrics)
3781 HRESULT hr;
3782 ID3DXMesh *mesh = NULL;
3783 DWORD nb_vertices, nb_faces;
3784 DWORD nb_front_faces, nb_corners, nb_outline_points;
3785 struct vertex *vertices = NULL;
3786 face *faces = NULL;
3787 int textlen = 0;
3788 float offset_x;
3789 LOGFONTW lf;
3790 OUTLINETEXTMETRICW otm;
3791 HFONT font = NULL, oldfont = NULL;
3792 const MAT2 identity = {{0, 1}, {0, 0}, {0, 0}, {0, 1}};
3793 void *raw_outline = NULL;
3794 int bufsize = 0;
3795 struct glyphinfo *glyphs = NULL;
3796 GLYPHMETRICS gm;
3797 struct triangulation_array triangulations = {0, 0, NULL};
3798 int i;
3799 struct vertex *vertex_ptr;
3800 face *face_ptr;
3801 float max_deviation_sq;
3802 const struct cos_table cos_table = {
3803 cos(D3DXToRadian(0.5f)),
3804 cos(D3DXToRadian(45.0f)),
3805 cos(D3DXToRadian(90.0f)),
3807 int f1, f2;
3809 TRACE("(%p, %p, %s, %f, %f, %p, %p, %p)\n", device, hdc,
3810 debugstr_w(text), deviation, extrusion, mesh_ptr, adjacency, glyphmetrics);
3812 if (!device || !hdc || !text || !*text || deviation < 0.0f || extrusion < 0.0f || !mesh_ptr)
3813 return D3DERR_INVALIDCALL;
3815 if (adjacency)
3817 FIXME("Case of adjacency != NULL not implemented.\n");
3818 return E_NOTIMPL;
3821 if (!GetObjectW(GetCurrentObject(hdc, OBJ_FONT), sizeof(lf), &lf) ||
3822 !GetOutlineTextMetricsW(hdc, sizeof(otm), &otm))
3824 return D3DERR_INVALIDCALL;
3827 if (deviation == 0.0f)
3828 deviation = 1.0f / otm.otmEMSquare;
3829 max_deviation_sq = deviation * deviation;
3831 lf.lfHeight = otm.otmEMSquare;
3832 lf.lfWidth = 0;
3833 font = CreateFontIndirectW(&lf);
3834 if (!font) {
3835 hr = E_OUTOFMEMORY;
3836 goto error;
3838 oldfont = SelectObject(hdc, font);
3840 textlen = strlenW(text);
3841 for (i = 0; i < textlen; i++)
3843 int datasize = GetGlyphOutlineW(hdc, text[i], GGO_NATIVE, &gm, 0, NULL, &identity);
3844 if (datasize < 0)
3845 return D3DERR_INVALIDCALL;
3846 if (bufsize < datasize)
3847 bufsize = datasize;
3849 if (!bufsize) { /* e.g. text == " " */
3850 hr = D3DERR_INVALIDCALL;
3851 goto error;
3854 glyphs = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, textlen * sizeof(*glyphs));
3855 raw_outline = HeapAlloc(GetProcessHeap(), 0, bufsize);
3856 if (!glyphs || !raw_outline) {
3857 hr = E_OUTOFMEMORY;
3858 goto error;
3861 offset_x = 0.0f;
3862 for (i = 0; i < textlen; i++)
3864 /* get outline points from data returned from GetGlyphOutline */
3865 int datasize;
3867 glyphs[i].offset_x = offset_x;
3869 datasize = GetGlyphOutlineW(hdc, text[i], GGO_NATIVE, &gm, bufsize, raw_outline, &identity);
3870 hr = create_outline(&glyphs[i], raw_outline, datasize,
3871 max_deviation_sq, otm.otmEMSquare, &cos_table);
3872 if (hr != S_OK) goto error;
3874 triangulations.glyph = &glyphs[i];
3875 hr = triangulate(&triangulations);
3876 if (hr != S_OK) goto error;
3877 if (triangulations.count) {
3878 ERR("%d incomplete triangulations of glyph (%u).\n", triangulations.count, text[i]);
3879 triangulations.count = 0;
3882 if (glyphmetrics)
3884 glyphmetrics[i].gmfBlackBoxX = gm.gmBlackBoxX / (float)otm.otmEMSquare;
3885 glyphmetrics[i].gmfBlackBoxY = gm.gmBlackBoxY / (float)otm.otmEMSquare;
3886 glyphmetrics[i].gmfptGlyphOrigin.x = gm.gmptGlyphOrigin.x / (float)otm.otmEMSquare;
3887 glyphmetrics[i].gmfptGlyphOrigin.y = gm.gmptGlyphOrigin.y / (float)otm.otmEMSquare;
3888 glyphmetrics[i].gmfCellIncX = gm.gmCellIncX / (float)otm.otmEMSquare;
3889 glyphmetrics[i].gmfCellIncY = gm.gmCellIncY / (float)otm.otmEMSquare;
3891 offset_x += gm.gmCellIncX / (float)otm.otmEMSquare;
3894 /* corner points need an extra vertex for the different side faces normals */
3895 nb_corners = 0;
3896 nb_outline_points = 0;
3897 nb_front_faces = 0;
3898 for (i = 0; i < textlen; i++)
3900 int j;
3901 nb_outline_points += glyphs[i].ordered_vertices.count;
3902 nb_front_faces += glyphs[i].faces.count;
3903 for (j = 0; j < glyphs[i].outlines.count; j++)
3905 int k;
3906 struct outline *outline = &glyphs[i].outlines.items[j];
3907 nb_corners++; /* first outline point always repeated as a corner */
3908 for (k = 1; k < outline->count; k++)
3909 if (outline->items[k].corner)
3910 nb_corners++;
3914 nb_vertices = (nb_outline_points + nb_corners) * 2 + nb_outline_points * 2;
3915 nb_faces = nb_outline_points * 2 + nb_front_faces * 2;
3918 hr = D3DXCreateMeshFVF(nb_faces, nb_vertices, D3DXMESH_MANAGED,
3919 D3DFVF_XYZ | D3DFVF_NORMAL, device, &mesh);
3920 if (FAILED(hr))
3921 goto error;
3923 hr = mesh->lpVtbl->LockVertexBuffer(mesh, D3DLOCK_DISCARD, (LPVOID *)&vertices);
3924 if (FAILED(hr))
3925 goto error;
3927 hr = mesh->lpVtbl->LockIndexBuffer(mesh, D3DLOCK_DISCARD, (LPVOID *)&faces);
3928 if (FAILED(hr))
3929 goto error;
3931 /* convert 2D vertices and faces into 3D mesh */
3932 vertex_ptr = vertices;
3933 face_ptr = faces;
3934 if (extrusion == 0.0f) {
3935 f1 = 1;
3936 f2 = 2;
3937 } else {
3938 f1 = 2;
3939 f2 = 1;
3941 for (i = 0; i < textlen; i++)
3943 int j;
3944 int count;
3945 struct vertex *back_vertices;
3946 face *back_faces;
3948 /* side vertices and faces */
3949 for (j = 0; j < glyphs[i].outlines.count; j++)
3951 struct vertex *outline_vertices = vertex_ptr;
3952 struct outline *outline = &glyphs[i].outlines.items[j];
3953 int k;
3954 struct point2d *prevpt = &outline->items[outline->count - 1];
3955 struct point2d *pt = &outline->items[0];
3957 for (k = 1; k <= outline->count; k++)
3959 struct vertex vtx;
3960 struct point2d *nextpt = &outline->items[k % outline->count];
3961 WORD vtx_idx = vertex_ptr - vertices;
3962 D3DXVECTOR2 vec;
3964 if (pt->corner == POINTTYPE_CURVE_START)
3965 D3DXVec2Subtract(&vec, &pt->pos, &prevpt->pos);
3966 else if (pt->corner)
3967 D3DXVec2Subtract(&vec, &nextpt->pos, &pt->pos);
3968 else
3969 D3DXVec2Subtract(&vec, &nextpt->pos, &prevpt->pos);
3970 D3DXVec2Normalize(&vec, &vec);
3971 vtx.normal.x = -vec.y;
3972 vtx.normal.y = vec.x;
3973 vtx.normal.z = 0;
3975 vtx.position.x = pt->pos.x + glyphs[i].offset_x;
3976 vtx.position.y = pt->pos.y;
3977 vtx.position.z = 0;
3978 *vertex_ptr++ = vtx;
3980 vtx.position.z = -extrusion;
3981 *vertex_ptr++ = vtx;
3983 vtx.position.x = nextpt->pos.x + glyphs[i].offset_x;
3984 vtx.position.y = nextpt->pos.y;
3985 if (pt->corner && nextpt->corner && nextpt->corner != POINTTYPE_CURVE_END) {
3986 vtx.position.z = -extrusion;
3987 *vertex_ptr++ = vtx;
3988 vtx.position.z = 0;
3989 *vertex_ptr++ = vtx;
3991 (*face_ptr)[0] = vtx_idx;
3992 (*face_ptr)[1] = vtx_idx + 2;
3993 (*face_ptr)[2] = vtx_idx + 1;
3994 face_ptr++;
3996 (*face_ptr)[0] = vtx_idx;
3997 (*face_ptr)[1] = vtx_idx + 3;
3998 (*face_ptr)[2] = vtx_idx + 2;
3999 face_ptr++;
4000 } else {
4001 if (nextpt->corner) {
4002 if (nextpt->corner == POINTTYPE_CURVE_END) {
4003 D3DXVECTOR2 *nextpt2 = &outline->items[(k + 1) % outline->count].pos;
4004 D3DXVec2Subtract(&vec, nextpt2, &nextpt->pos);
4005 } else {
4006 D3DXVec2Subtract(&vec, &nextpt->pos, &pt->pos);
4008 D3DXVec2Normalize(&vec, &vec);
4009 vtx.normal.x = -vec.y;
4010 vtx.normal.y = vec.x;
4012 vtx.position.z = 0;
4013 *vertex_ptr++ = vtx;
4014 vtx.position.z = -extrusion;
4015 *vertex_ptr++ = vtx;
4018 (*face_ptr)[0] = vtx_idx;
4019 (*face_ptr)[1] = vtx_idx + 3;
4020 (*face_ptr)[2] = vtx_idx + 1;
4021 face_ptr++;
4023 (*face_ptr)[0] = vtx_idx;
4024 (*face_ptr)[1] = vtx_idx + 2;
4025 (*face_ptr)[2] = vtx_idx + 3;
4026 face_ptr++;
4029 prevpt = pt;
4030 pt = nextpt;
4032 if (!pt->corner) {
4033 *vertex_ptr++ = *outline_vertices++;
4034 *vertex_ptr++ = *outline_vertices++;
4038 /* back vertices and faces */
4039 back_faces = face_ptr;
4040 back_vertices = vertex_ptr;
4041 for (j = 0; j < glyphs[i].ordered_vertices.count; j++)
4043 D3DXVECTOR2 *pt = get_ordered_vertex(&glyphs[i], j);
4044 vertex_ptr->position.x = pt->x + glyphs[i].offset_x;
4045 vertex_ptr->position.y = pt->y;
4046 vertex_ptr->position.z = 0;
4047 vertex_ptr->normal.x = 0;
4048 vertex_ptr->normal.y = 0;
4049 vertex_ptr->normal.z = 1;
4050 vertex_ptr++;
4052 count = back_vertices - vertices;
4053 for (j = 0; j < glyphs[i].faces.count; j++)
4055 face *f = &glyphs[i].faces.items[j];
4056 (*face_ptr)[0] = (*f)[0] + count;
4057 (*face_ptr)[1] = (*f)[1] + count;
4058 (*face_ptr)[2] = (*f)[2] + count;
4059 face_ptr++;
4062 /* front vertices and faces */
4063 j = count = vertex_ptr - back_vertices;
4064 while (j--)
4066 vertex_ptr->position.x = back_vertices->position.x;
4067 vertex_ptr->position.y = back_vertices->position.y;
4068 vertex_ptr->position.z = -extrusion;
4069 vertex_ptr->normal.x = 0;
4070 vertex_ptr->normal.y = 0;
4071 vertex_ptr->normal.z = extrusion == 0.0f ? 1.0f : -1.0f;
4072 vertex_ptr++;
4073 back_vertices++;
4075 j = face_ptr - back_faces;
4076 while (j--)
4078 (*face_ptr)[0] = (*back_faces)[0] + count;
4079 (*face_ptr)[1] = (*back_faces)[f1] + count;
4080 (*face_ptr)[2] = (*back_faces)[f2] + count;
4081 face_ptr++;
4082 back_faces++;
4086 *mesh_ptr = mesh;
4087 hr = D3D_OK;
4088 error:
4089 if (mesh) {
4090 if (faces) mesh->lpVtbl->UnlockIndexBuffer(mesh);
4091 if (vertices) mesh->lpVtbl->UnlockVertexBuffer(mesh);
4092 if (hr != D3D_OK) mesh->lpVtbl->Release(mesh);
4094 if (glyphs) {
4095 for (i = 0; i < textlen; i++)
4097 int j;
4098 for (j = 0; j < glyphs[i].outlines.count; j++)
4099 HeapFree(GetProcessHeap(), 0, glyphs[i].outlines.items[j].items);
4100 HeapFree(GetProcessHeap(), 0, glyphs[i].outlines.items);
4101 HeapFree(GetProcessHeap(), 0, glyphs[i].faces.items);
4102 HeapFree(GetProcessHeap(), 0, glyphs[i].ordered_vertices.items);
4104 HeapFree(GetProcessHeap(), 0, glyphs);
4106 if (triangulations.items) {
4107 int i;
4108 for (i = 0; i < triangulations.count; i++)
4109 HeapFree(GetProcessHeap(), 0, triangulations.items[i].vertex_stack.items);
4110 HeapFree(GetProcessHeap(), 0, triangulations.items);
4112 HeapFree(GetProcessHeap(), 0, raw_outline);
4113 if (oldfont) SelectObject(hdc, oldfont);
4114 if (font) DeleteObject(font);
4116 return hr;