d3dx9: D3DXCreateMesh rejects declarations with non-zero streams.
[wine/multimedia.git] / dlls / d3dx9_36 / mesh.c
blob432db1d501240d7a00dc38f14b143abd0fbd9fec
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 NONAMELESSUNION
29 #include "windef.h"
30 #include "wingdi.h"
31 #include "d3dx9.h"
32 #include "wine/debug.h"
33 #include "wine/unicode.h"
34 #include "d3dx9_36_private.h"
36 WINE_DEFAULT_DEBUG_CHANNEL(d3dx);
38 typedef struct ID3DXMeshImpl
40 ID3DXMesh ID3DXMesh_iface;
41 LONG ref;
43 DWORD numfaces;
44 DWORD numvertices;
45 DWORD options;
46 DWORD fvf;
47 IDirect3DDevice9 *device;
48 IDirect3DVertexDeclaration9 *vertex_declaration;
49 IDirect3DVertexBuffer9 *vertex_buffer;
50 IDirect3DIndexBuffer9 *index_buffer;
51 } ID3DXMeshImpl;
53 static inline ID3DXMeshImpl *impl_from_ID3DXMesh(ID3DXMesh *iface)
55 return CONTAINING_RECORD(iface, ID3DXMeshImpl, ID3DXMesh_iface);
58 static HRESULT WINAPI ID3DXMeshImpl_QueryInterface(ID3DXMesh *iface, REFIID riid, LPVOID *object)
60 ID3DXMeshImpl *This = impl_from_ID3DXMesh(iface);
62 TRACE("(%p)->(%s, %p)\n", This, debugstr_guid(riid), object);
64 if (IsEqualGUID(riid, &IID_IUnknown) ||
65 IsEqualGUID(riid, &IID_ID3DXBaseMesh) ||
66 IsEqualGUID(riid, &IID_ID3DXMesh))
68 iface->lpVtbl->AddRef(iface);
69 *object = This;
70 return S_OK;
73 WARN("Interface %s not found.\n", debugstr_guid(riid));
75 return E_NOINTERFACE;
78 static ULONG WINAPI ID3DXMeshImpl_AddRef(ID3DXMesh *iface)
80 ID3DXMeshImpl *This = impl_from_ID3DXMesh(iface);
82 TRACE("(%p)->(): AddRef from %d\n", This, This->ref);
84 return InterlockedIncrement(&This->ref);
87 static ULONG WINAPI ID3DXMeshImpl_Release(ID3DXMesh *iface)
89 ID3DXMeshImpl *This = impl_from_ID3DXMesh(iface);
90 ULONG ref = InterlockedDecrement(&This->ref);
92 TRACE("(%p)->(): Release from %d\n", This, ref + 1);
94 if (!ref)
96 IDirect3DIndexBuffer9_Release(This->index_buffer);
97 IDirect3DVertexBuffer9_Release(This->vertex_buffer);
98 IDirect3DVertexDeclaration9_Release(This->vertex_declaration);
99 IDirect3DDevice9_Release(This->device);
100 HeapFree(GetProcessHeap(), 0, This);
103 return ref;
106 /*** ID3DXBaseMesh ***/
107 static HRESULT WINAPI ID3DXMeshImpl_DrawSubset(ID3DXMesh *iface, DWORD attrib_id)
109 ID3DXMeshImpl *This = impl_from_ID3DXMesh(iface);
111 FIXME("(%p)->(%u): stub\n", This, attrib_id);
113 return E_NOTIMPL;
116 static DWORD WINAPI ID3DXMeshImpl_GetNumFaces(ID3DXMesh *iface)
118 ID3DXMeshImpl *This = impl_from_ID3DXMesh(iface);
120 TRACE("(%p)\n", This);
122 return This->numfaces;
125 static DWORD WINAPI ID3DXMeshImpl_GetNumVertices(ID3DXMesh *iface)
127 ID3DXMeshImpl *This = impl_from_ID3DXMesh(iface);
129 TRACE("(%p)\n", This);
131 return This->numvertices;
134 static DWORD WINAPI ID3DXMeshImpl_GetFVF(ID3DXMesh *iface)
136 ID3DXMeshImpl *This = impl_from_ID3DXMesh(iface);
138 TRACE("(%p)\n", This);
140 return This->fvf;
143 static HRESULT WINAPI ID3DXMeshImpl_GetDeclaration(ID3DXMesh *iface, D3DVERTEXELEMENT9 declaration[MAX_FVF_DECL_SIZE])
145 ID3DXMeshImpl *This = impl_from_ID3DXMesh(iface);
146 UINT numelements;
148 TRACE("(%p)\n", This);
150 if (declaration == NULL) return D3DERR_INVALIDCALL;
152 return IDirect3DVertexDeclaration9_GetDeclaration(This->vertex_declaration,
153 declaration,
154 &numelements);
157 static DWORD WINAPI ID3DXMeshImpl_GetNumBytesPerVertex(ID3DXMesh *iface)
159 ID3DXMeshImpl *This = impl_from_ID3DXMesh(iface);
161 TRACE("iface (%p)\n", This);
163 return D3DXGetFVFVertexSize(This->fvf);
166 static DWORD WINAPI ID3DXMeshImpl_GetOptions(ID3DXMesh *iface)
168 ID3DXMeshImpl *This = impl_from_ID3DXMesh(iface);
170 TRACE("(%p)\n", This);
172 return This->options;
175 static HRESULT WINAPI ID3DXMeshImpl_GetDevice(ID3DXMesh *iface, LPDIRECT3DDEVICE9 *device)
177 ID3DXMeshImpl *This = impl_from_ID3DXMesh(iface);
179 TRACE("(%p)->(%p)\n", This, device);
181 if (device == NULL) return D3DERR_INVALIDCALL;
182 *device = This->device;
183 IDirect3DDevice9_AddRef(This->device);
185 return D3D_OK;
188 static HRESULT WINAPI ID3DXMeshImpl_CloneMeshFVF(ID3DXMesh *iface, DWORD options, DWORD fvf, LPDIRECT3DDEVICE9 device, LPD3DXMESH *clone_mesh)
190 ID3DXMeshImpl *This = impl_from_ID3DXMesh(iface);
192 FIXME("(%p)->(%u,%u,%p,%p): stub\n", This, options, fvf, device, clone_mesh);
194 return E_NOTIMPL;
197 static HRESULT WINAPI ID3DXMeshImpl_CloneMesh(ID3DXMesh *iface, DWORD options, CONST D3DVERTEXELEMENT9 *declaration, LPDIRECT3DDEVICE9 device,
198 LPD3DXMESH *clone_mesh)
200 ID3DXMeshImpl *This = impl_from_ID3DXMesh(iface);
202 FIXME("(%p)->(%u,%p,%p,%p): stub\n", This, options, declaration, device, clone_mesh);
204 return E_NOTIMPL;
207 static HRESULT WINAPI ID3DXMeshImpl_GetVertexBuffer(ID3DXMesh *iface, LPDIRECT3DVERTEXBUFFER9 *vertex_buffer)
209 ID3DXMeshImpl *This = impl_from_ID3DXMesh(iface);
211 TRACE("(%p)->(%p)\n", This, vertex_buffer);
213 if (vertex_buffer == NULL) return D3DERR_INVALIDCALL;
214 *vertex_buffer = This->vertex_buffer;
215 IDirect3DVertexBuffer9_AddRef(This->vertex_buffer);
217 return D3D_OK;
220 static HRESULT WINAPI ID3DXMeshImpl_GetIndexBuffer(ID3DXMesh *iface, LPDIRECT3DINDEXBUFFER9 *index_buffer)
222 ID3DXMeshImpl *This = impl_from_ID3DXMesh(iface);
224 TRACE("(%p)->(%p)\n", This, index_buffer);
226 if (index_buffer == NULL) return D3DERR_INVALIDCALL;
227 *index_buffer = This->index_buffer;
228 IDirect3DIndexBuffer9_AddRef(This->index_buffer);
230 return D3D_OK;
233 static HRESULT WINAPI ID3DXMeshImpl_LockVertexBuffer(ID3DXMesh *iface, DWORD flags, LPVOID *data)
235 ID3DXMeshImpl *This = impl_from_ID3DXMesh(iface);
237 TRACE("(%p)->(%u,%p)\n", This, flags, data);
239 return IDirect3DVertexBuffer9_Lock(This->vertex_buffer, 0, 0, data, flags);
242 static HRESULT WINAPI ID3DXMeshImpl_UnlockVertexBuffer(ID3DXMesh *iface)
244 ID3DXMeshImpl *This = impl_from_ID3DXMesh(iface);
246 TRACE("(%p)\n", This);
248 return IDirect3DVertexBuffer9_Unlock(This->vertex_buffer);
251 static HRESULT WINAPI ID3DXMeshImpl_LockIndexBuffer(ID3DXMesh *iface, DWORD flags, LPVOID *data)
253 ID3DXMeshImpl *This = impl_from_ID3DXMesh(iface);
255 TRACE("(%p)->(%u,%p)\n", This, flags, data);
257 return IDirect3DIndexBuffer9_Lock(This->index_buffer, 0, 0, data, flags);
260 static HRESULT WINAPI ID3DXMeshImpl_UnlockIndexBuffer(ID3DXMesh *iface)
262 ID3DXMeshImpl *This = impl_from_ID3DXMesh(iface);
264 TRACE("(%p)\n", This);
266 return IDirect3DIndexBuffer9_Unlock(This->index_buffer);
269 static HRESULT WINAPI ID3DXMeshImpl_GetAttributeTable(ID3DXMesh *iface, D3DXATTRIBUTERANGE *attrib_table, DWORD *attrib_table_size)
271 ID3DXMeshImpl *This = impl_from_ID3DXMesh(iface);
273 FIXME("(%p)->(%p,%p): stub\n", This, attrib_table, attrib_table_size);
275 return E_NOTIMPL;
278 static HRESULT WINAPI ID3DXMeshImpl_ConvertPointRepsToAdjacency(ID3DXMesh *iface, CONST DWORD *point_reps, DWORD *adjacency)
280 ID3DXMeshImpl *This = impl_from_ID3DXMesh(iface);
282 FIXME("(%p)->(%p,%p): stub\n", This, point_reps, adjacency);
284 return E_NOTIMPL;
287 static HRESULT WINAPI ID3DXMeshImpl_ConvertAdjacencyToPointReps(ID3DXMesh *iface, CONST DWORD *adjacency, DWORD *point_reps)
289 ID3DXMeshImpl *This = impl_from_ID3DXMesh(iface);
291 FIXME("(%p)->(%p,%p): stub\n", This, adjacency, point_reps);
293 return E_NOTIMPL;
296 struct vertex_metadata {
297 float key;
298 DWORD vertex_index;
299 DWORD first_shared_index;
302 static int compare_vertex_keys(const void *a, const void *b)
304 const struct vertex_metadata *left = a;
305 const struct vertex_metadata *right = b;
306 if (left->key == right->key)
307 return 0;
308 return left->key < right->key ? -1 : 1;
311 static HRESULT WINAPI ID3DXMeshImpl_GenerateAdjacency(ID3DXMesh *iface, FLOAT epsilon, DWORD *adjacency)
313 ID3DXMeshImpl *This = impl_from_ID3DXMesh(iface);
314 HRESULT hr;
315 BYTE *vertices = NULL;
316 const DWORD *indices = NULL;
317 DWORD vertex_size;
318 DWORD buffer_size;
319 /* sort the vertices by (x + y + z) to quickly find coincident vertices */
320 struct vertex_metadata *sorted_vertices;
321 /* shared_indices links together identical indices in the index buffer so
322 * that adjacency checks can be limited to faces sharing a vertex */
323 DWORD *shared_indices = NULL;
324 const FLOAT epsilon_sq = epsilon * epsilon;
325 int i;
327 TRACE("(%p)->(%f,%p)\n", This, epsilon, adjacency);
329 if (!adjacency)
330 return D3DERR_INVALIDCALL;
332 buffer_size = This->numfaces * 3 * sizeof(*shared_indices) + This->numvertices * sizeof(*sorted_vertices);
333 if (!(This->options & D3DXMESH_32BIT))
334 buffer_size += This->numfaces * 3 * sizeof(*indices);
335 shared_indices = HeapAlloc(GetProcessHeap(), 0, buffer_size);
336 if (!shared_indices)
337 return E_OUTOFMEMORY;
338 sorted_vertices = (struct vertex_metadata*)(shared_indices + This->numfaces * 3);
340 hr = iface->lpVtbl->LockVertexBuffer(iface, D3DLOCK_READONLY, (void**)&vertices);
341 if (FAILED(hr)) goto cleanup;
342 hr = iface->lpVtbl->LockIndexBuffer(iface, D3DLOCK_READONLY, (void**)&indices);
343 if (FAILED(hr)) goto cleanup;
345 if (!(This->options & D3DXMESH_32BIT)) {
346 const WORD *word_indices = (const WORD*)indices;
347 DWORD *dword_indices = (DWORD*)(sorted_vertices + This->numvertices);
348 indices = dword_indices;
349 for (i = 0; i < This->numfaces * 3; i++)
350 *dword_indices++ = *word_indices++;
353 vertex_size = iface->lpVtbl->GetNumBytesPerVertex(iface);
354 for (i = 0; i < This->numvertices; i++) {
355 D3DXVECTOR3 *vertex = (D3DXVECTOR3*)(vertices + vertex_size * i);
356 sorted_vertices[i].first_shared_index = -1;
357 sorted_vertices[i].key = vertex->x + vertex->y + vertex->z;
358 sorted_vertices[i].vertex_index = i;
360 for (i = 0; i < This->numfaces * 3; i++) {
361 DWORD *first_shared_index = &sorted_vertices[indices[i]].first_shared_index;
362 shared_indices[i] = *first_shared_index;
363 *first_shared_index = i;
364 adjacency[i] = -1;
366 qsort(sorted_vertices, This->numvertices, sizeof(*sorted_vertices), compare_vertex_keys);
368 for (i = 0; i < This->numvertices; i++) {
369 struct vertex_metadata *sorted_vertex_a = &sorted_vertices[i];
370 D3DXVECTOR3 *vertex_a = (D3DXVECTOR3*)(vertices + sorted_vertex_a->vertex_index * vertex_size);
371 DWORD shared_index_a = sorted_vertex_a->first_shared_index;
373 while (shared_index_a != -1) {
374 int j = i;
375 DWORD shared_index_b = shared_indices[shared_index_a];
376 struct vertex_metadata *sorted_vertex_b = sorted_vertex_a;
378 while (TRUE) {
379 while (shared_index_b != -1) {
380 /* faces are adjacent if they have another coincident vertex */
381 DWORD base_a = (shared_index_a / 3) * 3;
382 DWORD base_b = (shared_index_b / 3) * 3;
383 BOOL adjacent = FALSE;
384 int k;
386 for (k = 0; k < 3; k++) {
387 if (adjacency[base_b + k] == shared_index_a / 3) {
388 adjacent = TRUE;
389 break;
392 if (!adjacent) {
393 for (k = 1; k <= 2; k++) {
394 DWORD vertex_index_a = base_a + (shared_index_a + k) % 3;
395 DWORD vertex_index_b = base_b + (shared_index_b + (3 - k)) % 3;
396 adjacent = indices[vertex_index_a] == indices[vertex_index_b];
397 if (!adjacent && epsilon >= 0.0f) {
398 D3DXVECTOR3 delta = {0.0f, 0.0f, 0.0f};
399 FLOAT length_sq;
401 D3DXVec3Subtract(&delta,
402 (D3DXVECTOR3*)(vertices + indices[vertex_index_a] * vertex_size),
403 (D3DXVECTOR3*)(vertices + indices[vertex_index_b] * vertex_size));
404 length_sq = D3DXVec3LengthSq(&delta);
405 adjacent = epsilon == 0.0f ? length_sq == 0.0f : length_sq < epsilon_sq;
407 if (adjacent) {
408 DWORD adj_a = base_a + 2 - (vertex_index_a + shared_index_a + 1) % 3;
409 DWORD adj_b = base_b + 2 - (vertex_index_b + shared_index_b + 1) % 3;
410 if (adjacency[adj_a] == -1 && adjacency[adj_b] == -1) {
411 adjacency[adj_a] = base_b / 3;
412 adjacency[adj_b] = base_a / 3;
413 break;
419 shared_index_b = shared_indices[shared_index_b];
421 while (++j < This->numvertices) {
422 D3DXVECTOR3 *vertex_b;
424 sorted_vertex_b++;
425 if (sorted_vertex_b->key - sorted_vertex_a->key > epsilon * 3.0f) {
426 /* no more coincident vertices to try */
427 j = This->numvertices;
428 break;
430 /* check for coincidence */
431 vertex_b = (D3DXVECTOR3*)(vertices + sorted_vertex_b->vertex_index * vertex_size);
432 if (fabsf(vertex_a->x - vertex_b->x) <= epsilon &&
433 fabsf(vertex_a->y - vertex_b->y) <= epsilon &&
434 fabsf(vertex_a->z - vertex_b->z) <= epsilon)
436 break;
439 if (j >= This->numvertices)
440 break;
441 shared_index_b = sorted_vertex_b->first_shared_index;
444 sorted_vertex_a->first_shared_index = shared_indices[sorted_vertex_a->first_shared_index];
445 shared_index_a = sorted_vertex_a->first_shared_index;
449 hr = D3D_OK;
450 cleanup:
451 if (indices) iface->lpVtbl->UnlockIndexBuffer(iface);
452 if (vertices) iface->lpVtbl->UnlockVertexBuffer(iface);
453 HeapFree(GetProcessHeap(), 0, shared_indices);
454 return hr;
457 static HRESULT WINAPI ID3DXMeshImpl_UpdateSemantics(ID3DXMesh *iface, D3DVERTEXELEMENT9 declaration[MAX_FVF_DECL_SIZE])
459 ID3DXMeshImpl *This = impl_from_ID3DXMesh(iface);
461 FIXME("(%p)->(%p): stub\n", This, declaration);
463 return E_NOTIMPL;
466 /*** ID3DXMesh ***/
467 static HRESULT WINAPI ID3DXMeshImpl_LockAttributeBuffer(ID3DXMesh *iface, DWORD flags, DWORD **data)
469 ID3DXMeshImpl *This = impl_from_ID3DXMesh(iface);
471 FIXME("(%p)->(%u,%p): stub\n", This, flags, data);
473 return E_NOTIMPL;
476 static HRESULT WINAPI ID3DXMeshImpl_UnlockAttributeBuffer(ID3DXMesh *iface)
478 ID3DXMeshImpl *This = impl_from_ID3DXMesh(iface);
480 FIXME("(%p): stub\n", This);
482 return E_NOTIMPL;
485 static HRESULT WINAPI ID3DXMeshImpl_Optimize(ID3DXMesh *iface, DWORD flags, CONST DWORD *adjacency_in, DWORD *adjacency_out,
486 DWORD *face_remap, LPD3DXBUFFER *vertex_remap, LPD3DXMESH *opt_mesh)
488 ID3DXMeshImpl *This = impl_from_ID3DXMesh(iface);
490 FIXME("(%p)->(%u,%p,%p,%p,%p,%p): stub\n", This, flags, adjacency_in, adjacency_out, face_remap, vertex_remap, opt_mesh);
492 return E_NOTIMPL;
495 static HRESULT WINAPI ID3DXMeshImpl_OptimizeInplace(ID3DXMesh *iface, DWORD flags, CONST DWORD *adjacency_in, DWORD *adjacency_out,
496 DWORD *face_remap, LPD3DXBUFFER *vertex_remap)
498 ID3DXMeshImpl *This = impl_from_ID3DXMesh(iface);
500 FIXME("(%p)->(%u,%p,%p,%p,%p): stub\n", This, flags, adjacency_in, adjacency_out, face_remap, vertex_remap);
502 return E_NOTIMPL;
505 static HRESULT WINAPI ID3DXMeshImpl_SetAttributeTable(ID3DXMesh *iface, CONST D3DXATTRIBUTERANGE *attrib_table, DWORD attrib_table_size)
507 ID3DXMeshImpl *This = impl_from_ID3DXMesh(iface);
509 FIXME("(%p)->(%p,%u): stub\n", This, attrib_table, attrib_table_size);
511 return E_NOTIMPL;
514 static const struct ID3DXMeshVtbl D3DXMesh_Vtbl =
516 /*** IUnknown methods ***/
517 ID3DXMeshImpl_QueryInterface,
518 ID3DXMeshImpl_AddRef,
519 ID3DXMeshImpl_Release,
520 /*** ID3DXBaseMesh ***/
521 ID3DXMeshImpl_DrawSubset,
522 ID3DXMeshImpl_GetNumFaces,
523 ID3DXMeshImpl_GetNumVertices,
524 ID3DXMeshImpl_GetFVF,
525 ID3DXMeshImpl_GetDeclaration,
526 ID3DXMeshImpl_GetNumBytesPerVertex,
527 ID3DXMeshImpl_GetOptions,
528 ID3DXMeshImpl_GetDevice,
529 ID3DXMeshImpl_CloneMeshFVF,
530 ID3DXMeshImpl_CloneMesh,
531 ID3DXMeshImpl_GetVertexBuffer,
532 ID3DXMeshImpl_GetIndexBuffer,
533 ID3DXMeshImpl_LockVertexBuffer,
534 ID3DXMeshImpl_UnlockVertexBuffer,
535 ID3DXMeshImpl_LockIndexBuffer,
536 ID3DXMeshImpl_UnlockIndexBuffer,
537 ID3DXMeshImpl_GetAttributeTable,
538 ID3DXMeshImpl_ConvertPointRepsToAdjacency,
539 ID3DXMeshImpl_ConvertAdjacencyToPointReps,
540 ID3DXMeshImpl_GenerateAdjacency,
541 ID3DXMeshImpl_UpdateSemantics,
542 /*** ID3DXMesh ***/
543 ID3DXMeshImpl_LockAttributeBuffer,
544 ID3DXMeshImpl_UnlockAttributeBuffer,
545 ID3DXMeshImpl_Optimize,
546 ID3DXMeshImpl_OptimizeInplace,
547 ID3DXMeshImpl_SetAttributeTable
550 /*************************************************************************
551 * D3DXBoxBoundProbe
553 BOOL WINAPI D3DXBoxBoundProbe(CONST D3DXVECTOR3 *pmin, CONST D3DXVECTOR3 *pmax, CONST D3DXVECTOR3 *prayposition, CONST D3DXVECTOR3 *praydirection)
555 /* Algorithm taken from the article: An Efficient and Robust Ray-Box Intersection Algoritm
556 Amy Williams University of Utah
557 Steve Barrus University of Utah
558 R. Keith Morley University of Utah
559 Peter Shirley University of Utah
561 International Conference on Computer Graphics and Interactive Techniques archive
562 ACM SIGGRAPH 2005 Courses
563 Los Angeles, California
565 This algorithm is free of patents or of copyrights, as confirmed by Peter Shirley himself.
567 Algorithm: Consider the box as the intersection of three slabs. Clip the ray
568 against each slab, if there's anything left of the ray after we're
569 done we've got an intersection of the ray with the box.
573 FLOAT div, tmin, tmax, tymin, tymax, tzmin, tzmax;
575 div = 1.0f / praydirection->x;
576 if ( div >= 0.0f )
578 tmin = ( pmin->x - prayposition->x ) * div;
579 tmax = ( pmax->x - prayposition->x ) * div;
581 else
583 tmin = ( pmax->x - prayposition->x ) * div;
584 tmax = ( pmin->x - prayposition->x ) * div;
587 if ( tmax < 0.0f ) return FALSE;
589 div = 1.0f / praydirection->y;
590 if ( div >= 0.0f )
592 tymin = ( pmin->y - prayposition->y ) * div;
593 tymax = ( pmax->y - prayposition->y ) * div;
595 else
597 tymin = ( pmax->y - prayposition->y ) * div;
598 tymax = ( pmin->y - prayposition->y ) * div;
601 if ( ( tymax < 0.0f ) || ( tmin > tymax ) || ( tymin > tmax ) ) return FALSE;
603 if ( tymin > tmin ) tmin = tymin;
604 if ( tymax < tmax ) tmax = tymax;
606 div = 1.0f / praydirection->z;
607 if ( div >= 0.0f )
609 tzmin = ( pmin->z - prayposition->z ) * div;
610 tzmax = ( pmax->z - prayposition->z ) * div;
612 else
614 tzmin = ( pmax->z - prayposition->z ) * div;
615 tzmax = ( pmin->z - prayposition->z ) * div;
618 if ( (tzmax < 0.0f ) || ( tmin > tzmax ) || ( tzmin > tmax ) ) return FALSE;
620 return TRUE;
623 /*************************************************************************
624 * D3DXComputeBoundingBox
626 HRESULT WINAPI D3DXComputeBoundingBox(CONST D3DXVECTOR3 *pfirstposition, DWORD numvertices, DWORD dwstride, D3DXVECTOR3 *pmin, D3DXVECTOR3 *pmax)
628 D3DXVECTOR3 vec;
629 unsigned int i;
631 if( !pfirstposition || !pmin || !pmax ) return D3DERR_INVALIDCALL;
633 *pmin = *pfirstposition;
634 *pmax = *pmin;
636 for(i=0; i<numvertices; i++)
638 vec = *( (const D3DXVECTOR3*)((const char*)pfirstposition + dwstride * i) );
640 if ( vec.x < pmin->x ) pmin->x = vec.x;
641 if ( vec.x > pmax->x ) pmax->x = vec.x;
643 if ( vec.y < pmin->y ) pmin->y = vec.y;
644 if ( vec.y > pmax->y ) pmax->y = vec.y;
646 if ( vec.z < pmin->z ) pmin->z = vec.z;
647 if ( vec.z > pmax->z ) pmax->z = vec.z;
650 return D3D_OK;
653 /*************************************************************************
654 * D3DXComputeBoundingSphere
656 HRESULT WINAPI D3DXComputeBoundingSphere(CONST D3DXVECTOR3* pfirstposition, DWORD numvertices, DWORD dwstride, D3DXVECTOR3 *pcenter, FLOAT *pradius)
658 D3DXVECTOR3 temp, temp1;
659 FLOAT d;
660 unsigned int i;
662 if( !pfirstposition || !pcenter || !pradius ) return D3DERR_INVALIDCALL;
664 temp.x = 0.0f;
665 temp.y = 0.0f;
666 temp.z = 0.0f;
667 temp1 = temp;
668 *pradius = 0.0f;
670 for(i=0; i<numvertices; i++)
672 D3DXVec3Add(&temp1, &temp, (const D3DXVECTOR3*)((const char*)pfirstposition + dwstride * i));
673 temp = temp1;
676 D3DXVec3Scale(pcenter, &temp, 1.0f/((FLOAT)numvertices));
678 for(i=0; i<numvertices; i++)
680 d = D3DXVec3Length(D3DXVec3Subtract(&temp, (const D3DXVECTOR3*)((const char*)pfirstposition + dwstride * i), pcenter));
681 if ( d > *pradius ) *pradius = d;
683 return D3D_OK;
686 static const UINT d3dx_decltype_size[D3DDECLTYPE_UNUSED] =
688 /* D3DDECLTYPE_FLOAT1 */ 1 * 4,
689 /* D3DDECLTYPE_FLOAT2 */ 2 * 4,
690 /* D3DDECLTYPE_FLOAT3 */ 3 * 4,
691 /* D3DDECLTYPE_FLOAT4 */ 4 * 4,
692 /* D3DDECLTYPE_D3DCOLOR */ 4 * 1,
693 /* D3DDECLTYPE_UBYTE4 */ 4 * 1,
694 /* D3DDECLTYPE_SHORT2 */ 2 * 2,
695 /* D3DDECLTYPE_SHORT4 */ 4 * 2,
696 /* D3DDECLTYPE_UBYTE4N */ 4 * 1,
697 /* D3DDECLTYPE_SHORT2N */ 2 * 2,
698 /* D3DDECLTYPE_SHORT4N */ 4 * 2,
699 /* D3DDECLTYPE_USHORT2N */ 2 * 2,
700 /* D3DDECLTYPE_USHORT4N */ 4 * 2,
701 /* D3DDECLTYPE_UDEC3 */ 4, /* 3 * 10 bits + 2 padding */
702 /* D3DDECLTYPE_DEC3N */ 4,
703 /* D3DDECLTYPE_FLOAT16_2 */ 2 * 2,
704 /* D3DDECLTYPE_FLOAT16_4 */ 4 * 2,
707 static void append_decl_element(D3DVERTEXELEMENT9 *declaration, UINT *idx, UINT *offset,
708 D3DDECLTYPE type, D3DDECLUSAGE usage, UINT usage_idx)
710 declaration[*idx].Stream = 0;
711 declaration[*idx].Offset = *offset;
712 declaration[*idx].Type = type;
713 declaration[*idx].Method = D3DDECLMETHOD_DEFAULT;
714 declaration[*idx].Usage = usage;
715 declaration[*idx].UsageIndex = usage_idx;
717 *offset += d3dx_decltype_size[type];
718 ++(*idx);
721 /*************************************************************************
722 * D3DXDeclaratorFromFVF
724 HRESULT WINAPI D3DXDeclaratorFromFVF(DWORD fvf, D3DVERTEXELEMENT9 declaration[MAX_FVF_DECL_SIZE])
726 static const D3DVERTEXELEMENT9 end_element = D3DDECL_END();
727 DWORD tex_count = (fvf & D3DFVF_TEXCOUNT_MASK) >> D3DFVF_TEXCOUNT_SHIFT;
728 unsigned int offset = 0;
729 unsigned int idx = 0;
730 unsigned int i;
732 TRACE("fvf %#x, declaration %p.\n", fvf, declaration);
734 if (fvf & (D3DFVF_RESERVED0 | D3DFVF_RESERVED2)) return D3DERR_INVALIDCALL;
736 if (fvf & D3DFVF_POSITION_MASK)
738 BOOL has_blend = (fvf & D3DFVF_XYZB5) >= D3DFVF_XYZB1;
739 DWORD blend_count = 1 + (((fvf & D3DFVF_XYZB5) - D3DFVF_XYZB1) >> 1);
740 BOOL has_blend_idx = (fvf & D3DFVF_LASTBETA_D3DCOLOR) || (fvf & D3DFVF_LASTBETA_UBYTE4);
742 if (has_blend_idx) --blend_count;
744 if ((fvf & D3DFVF_POSITION_MASK) == D3DFVF_XYZW
745 || (has_blend && blend_count > 4))
746 return D3DERR_INVALIDCALL;
748 if ((fvf & D3DFVF_POSITION_MASK) == D3DFVF_XYZRHW)
749 append_decl_element(declaration, &idx, &offset, D3DDECLTYPE_FLOAT4, D3DDECLUSAGE_POSITIONT, 0);
750 else
751 append_decl_element(declaration, &idx, &offset, D3DDECLTYPE_FLOAT3, D3DDECLUSAGE_POSITION, 0);
753 if (has_blend)
755 switch (blend_count)
757 case 0:
758 break;
759 case 1:
760 append_decl_element(declaration, &idx, &offset, D3DDECLTYPE_FLOAT1, D3DDECLUSAGE_BLENDWEIGHT, 0);
761 break;
762 case 2:
763 append_decl_element(declaration, &idx, &offset, D3DDECLTYPE_FLOAT2, D3DDECLUSAGE_BLENDWEIGHT, 0);
764 break;
765 case 3:
766 append_decl_element(declaration, &idx, &offset, D3DDECLTYPE_FLOAT3, D3DDECLUSAGE_BLENDWEIGHT, 0);
767 break;
768 case 4:
769 append_decl_element(declaration, &idx, &offset, D3DDECLTYPE_FLOAT4, D3DDECLUSAGE_BLENDWEIGHT, 0);
770 break;
771 default:
772 ERR("Invalid blend count %u.\n", blend_count);
773 break;
776 if (has_blend_idx)
778 if (fvf & D3DFVF_LASTBETA_UBYTE4)
779 append_decl_element(declaration, &idx, &offset, D3DDECLTYPE_UBYTE4, D3DDECLUSAGE_BLENDINDICES, 0);
780 else if (fvf & D3DFVF_LASTBETA_D3DCOLOR)
781 append_decl_element(declaration, &idx, &offset, D3DDECLTYPE_D3DCOLOR, D3DDECLUSAGE_BLENDINDICES, 0);
786 if (fvf & D3DFVF_NORMAL)
787 append_decl_element(declaration, &idx, &offset, D3DDECLTYPE_FLOAT3, D3DDECLUSAGE_NORMAL, 0);
788 if (fvf & D3DFVF_PSIZE)
789 append_decl_element(declaration, &idx, &offset, D3DDECLTYPE_FLOAT1, D3DDECLUSAGE_PSIZE, 0);
790 if (fvf & D3DFVF_DIFFUSE)
791 append_decl_element(declaration, &idx, &offset, D3DDECLTYPE_D3DCOLOR, D3DDECLUSAGE_COLOR, 0);
792 if (fvf & D3DFVF_SPECULAR)
793 append_decl_element(declaration, &idx, &offset, D3DDECLTYPE_D3DCOLOR, D3DDECLUSAGE_COLOR, 1);
795 for (i = 0; i < tex_count; ++i)
797 switch ((fvf >> (16 + 2 * i)) & 0x03)
799 case D3DFVF_TEXTUREFORMAT1:
800 append_decl_element(declaration, &idx, &offset, D3DDECLTYPE_FLOAT1, D3DDECLUSAGE_TEXCOORD, i);
801 break;
802 case D3DFVF_TEXTUREFORMAT2:
803 append_decl_element(declaration, &idx, &offset, D3DDECLTYPE_FLOAT2, D3DDECLUSAGE_TEXCOORD, i);
804 break;
805 case D3DFVF_TEXTUREFORMAT3:
806 append_decl_element(declaration, &idx, &offset, D3DDECLTYPE_FLOAT3, D3DDECLUSAGE_TEXCOORD, i);
807 break;
808 case D3DFVF_TEXTUREFORMAT4:
809 append_decl_element(declaration, &idx, &offset, D3DDECLTYPE_FLOAT4, D3DDECLUSAGE_TEXCOORD, i);
810 break;
814 declaration[idx] = end_element;
816 return D3D_OK;
819 /*************************************************************************
820 * D3DXFVFFromDeclarator
822 HRESULT WINAPI D3DXFVFFromDeclarator(const D3DVERTEXELEMENT9 *declaration, DWORD *fvf)
824 unsigned int i = 0, texture, offset;
826 TRACE("(%p, %p)\n", declaration, fvf);
828 *fvf = 0;
829 if (declaration[0].Type == D3DDECLTYPE_FLOAT3 && declaration[0].Usage == D3DDECLUSAGE_POSITION)
831 if ((declaration[1].Type == D3DDECLTYPE_FLOAT4 && declaration[1].Usage == D3DDECLUSAGE_BLENDWEIGHT &&
832 declaration[1].UsageIndex == 0) &&
833 (declaration[2].Type == D3DDECLTYPE_FLOAT1 && declaration[2].Usage == D3DDECLUSAGE_BLENDINDICES &&
834 declaration[2].UsageIndex == 0))
836 return D3DERR_INVALIDCALL;
838 else if ((declaration[1].Type == D3DDECLTYPE_UBYTE4 || declaration[1].Type == D3DDECLTYPE_D3DCOLOR) &&
839 declaration[1].Usage == D3DDECLUSAGE_BLENDINDICES && declaration[1].UsageIndex == 0)
841 if (declaration[1].Type == D3DDECLTYPE_UBYTE4)
843 *fvf |= D3DFVF_XYZB1 | D3DFVF_LASTBETA_UBYTE4;
845 else
847 *fvf |= D3DFVF_XYZB1 | D3DFVF_LASTBETA_D3DCOLOR;
849 i = 2;
851 else if (declaration[1].Type <= D3DDECLTYPE_FLOAT4 && declaration[1].Usage == D3DDECLUSAGE_BLENDWEIGHT &&
852 declaration[1].UsageIndex == 0)
854 if ((declaration[2].Type == D3DDECLTYPE_UBYTE4 || declaration[2].Type == D3DDECLTYPE_D3DCOLOR) &&
855 declaration[2].Usage == D3DDECLUSAGE_BLENDINDICES && declaration[2].UsageIndex == 0)
857 if (declaration[2].Type == D3DDECLTYPE_UBYTE4)
859 *fvf |= D3DFVF_LASTBETA_UBYTE4;
861 else
863 *fvf |= D3DFVF_LASTBETA_D3DCOLOR;
865 switch (declaration[1].Type)
867 case D3DDECLTYPE_FLOAT1: *fvf |= D3DFVF_XYZB2; break;
868 case D3DDECLTYPE_FLOAT2: *fvf |= D3DFVF_XYZB3; break;
869 case D3DDECLTYPE_FLOAT3: *fvf |= D3DFVF_XYZB4; break;
870 case D3DDECLTYPE_FLOAT4: *fvf |= D3DFVF_XYZB5; break;
872 i = 3;
874 else
876 switch (declaration[1].Type)
878 case D3DDECLTYPE_FLOAT1: *fvf |= D3DFVF_XYZB1; break;
879 case D3DDECLTYPE_FLOAT2: *fvf |= D3DFVF_XYZB2; break;
880 case D3DDECLTYPE_FLOAT3: *fvf |= D3DFVF_XYZB3; break;
881 case D3DDECLTYPE_FLOAT4: *fvf |= D3DFVF_XYZB4; break;
883 i = 2;
886 else
888 *fvf |= D3DFVF_XYZ;
889 i = 1;
892 else if (declaration[0].Type == D3DDECLTYPE_FLOAT4 && declaration[0].Usage == D3DDECLUSAGE_POSITIONT &&
893 declaration[0].UsageIndex == 0)
895 *fvf |= D3DFVF_XYZRHW;
896 i = 1;
899 if (declaration[i].Type == D3DDECLTYPE_FLOAT3 && declaration[i].Usage == D3DDECLUSAGE_NORMAL)
901 *fvf |= D3DFVF_NORMAL;
902 i++;
904 if (declaration[i].Type == D3DDECLTYPE_FLOAT1 && declaration[i].Usage == D3DDECLUSAGE_PSIZE &&
905 declaration[i].UsageIndex == 0)
907 *fvf |= D3DFVF_PSIZE;
908 i++;
910 if (declaration[i].Type == D3DDECLTYPE_D3DCOLOR && declaration[i].Usage == D3DDECLUSAGE_COLOR &&
911 declaration[i].UsageIndex == 0)
913 *fvf |= D3DFVF_DIFFUSE;
914 i++;
916 if (declaration[i].Type == D3DDECLTYPE_D3DCOLOR && declaration[i].Usage == D3DDECLUSAGE_COLOR &&
917 declaration[i].UsageIndex == 1)
919 *fvf |= D3DFVF_SPECULAR;
920 i++;
923 for (texture = 0; texture < D3DDP_MAXTEXCOORD; i++, texture++)
925 if (declaration[i].Stream == 0xFF)
927 break;
929 else if (declaration[i].Type == D3DDECLTYPE_FLOAT1 && declaration[i].Usage == D3DDECLUSAGE_TEXCOORD &&
930 declaration[i].UsageIndex == texture)
932 *fvf |= D3DFVF_TEXCOORDSIZE1(declaration[i].UsageIndex);
934 else if (declaration[i].Type == D3DDECLTYPE_FLOAT2 && declaration[i].Usage == D3DDECLUSAGE_TEXCOORD &&
935 declaration[i].UsageIndex == texture)
937 *fvf |= D3DFVF_TEXCOORDSIZE2(declaration[i].UsageIndex);
939 else if (declaration[i].Type == D3DDECLTYPE_FLOAT3 && declaration[i].Usage == D3DDECLUSAGE_TEXCOORD &&
940 declaration[i].UsageIndex == texture)
942 *fvf |= D3DFVF_TEXCOORDSIZE3(declaration[i].UsageIndex);
944 else if (declaration[i].Type == D3DDECLTYPE_FLOAT4 && declaration[i].Usage == D3DDECLUSAGE_TEXCOORD &&
945 declaration[i].UsageIndex == texture)
947 *fvf |= D3DFVF_TEXCOORDSIZE4(declaration[i].UsageIndex);
949 else
951 return D3DERR_INVALIDCALL;
955 *fvf |= (texture << D3DFVF_TEXCOUNT_SHIFT);
957 for (offset = 0, i = 0; declaration[i].Stream != 0xFF;
958 offset += d3dx_decltype_size[declaration[i].Type], i++)
960 if (declaration[i].Offset != offset)
962 return D3DERR_INVALIDCALL;
966 return D3D_OK;
969 /*************************************************************************
970 * D3DXGetFVFVertexSize
972 static UINT Get_TexCoord_Size_From_FVF(DWORD FVF, int tex_num)
974 return (((((FVF) >> (16 + (2 * (tex_num)))) + 1) & 0x03) + 1);
977 UINT WINAPI D3DXGetFVFVertexSize(DWORD FVF)
979 DWORD size = 0;
980 UINT i;
981 UINT numTextures = (FVF & D3DFVF_TEXCOUNT_MASK) >> D3DFVF_TEXCOUNT_SHIFT;
983 if (FVF & D3DFVF_NORMAL) size += sizeof(D3DXVECTOR3);
984 if (FVF & D3DFVF_DIFFUSE) size += sizeof(DWORD);
985 if (FVF & D3DFVF_SPECULAR) size += sizeof(DWORD);
986 if (FVF & D3DFVF_PSIZE) size += sizeof(DWORD);
988 switch (FVF & D3DFVF_POSITION_MASK)
990 case D3DFVF_XYZ: size += sizeof(D3DXVECTOR3); break;
991 case D3DFVF_XYZRHW: size += 4 * sizeof(FLOAT); break;
992 case D3DFVF_XYZB1: size += 4 * sizeof(FLOAT); break;
993 case D3DFVF_XYZB2: size += 5 * sizeof(FLOAT); break;
994 case D3DFVF_XYZB3: size += 6 * sizeof(FLOAT); break;
995 case D3DFVF_XYZB4: size += 7 * sizeof(FLOAT); break;
996 case D3DFVF_XYZB5: size += 8 * sizeof(FLOAT); break;
997 case D3DFVF_XYZW: size += 4 * sizeof(FLOAT); break;
1000 for (i = 0; i < numTextures; i++)
1002 size += Get_TexCoord_Size_From_FVF(FVF, i) * sizeof(FLOAT);
1005 return size;
1008 /*************************************************************************
1009 * D3DXGetDeclVertexSize
1011 UINT WINAPI D3DXGetDeclVertexSize(const D3DVERTEXELEMENT9 *decl, DWORD stream_idx)
1013 const D3DVERTEXELEMENT9 *element;
1014 UINT size = 0;
1016 TRACE("decl %p, stream_idx %u\n", decl, stream_idx);
1018 if (!decl) return 0;
1020 for (element = decl; element->Stream != 0xff; ++element)
1022 UINT type_size;
1024 if (element->Stream != stream_idx) continue;
1026 if (element->Type >= sizeof(d3dx_decltype_size) / sizeof(*d3dx_decltype_size))
1028 FIXME("Unhandled element type %#x, size will be incorrect.\n", element->Type);
1029 continue;
1032 type_size = d3dx_decltype_size[element->Type];
1033 if (element->Offset + type_size > size) size = element->Offset + type_size;
1036 return size;
1039 /*************************************************************************
1040 * D3DXGetDeclLength
1042 UINT WINAPI D3DXGetDeclLength(const D3DVERTEXELEMENT9 *decl)
1044 const D3DVERTEXELEMENT9 *element;
1046 TRACE("decl %p\n", decl);
1048 /* null decl results in exception on Windows XP */
1050 for (element = decl; element->Stream != 0xff; ++element);
1052 return element - decl;
1055 /*************************************************************************
1056 * D3DXIntersectTri
1058 BOOL WINAPI D3DXIntersectTri(CONST D3DXVECTOR3 *p0, CONST D3DXVECTOR3 *p1, CONST D3DXVECTOR3 *p2, CONST D3DXVECTOR3 *praypos, CONST D3DXVECTOR3 *praydir, FLOAT *pu, FLOAT *pv, FLOAT *pdist)
1060 D3DXMATRIX m;
1061 D3DXVECTOR4 vec;
1063 m.u.m[0][0] = p1->x - p0->x;
1064 m.u.m[1][0] = p2->x - p0->x;
1065 m.u.m[2][0] = -praydir->x;
1066 m.u.m[3][0] = 0.0f;
1067 m.u.m[0][1] = p1->y - p0->z;
1068 m.u.m[1][1] = p2->y - p0->z;
1069 m.u.m[2][1] = -praydir->y;
1070 m.u.m[3][1] = 0.0f;
1071 m.u.m[0][2] = p1->z - p0->z;
1072 m.u.m[1][2] = p2->z - p0->z;
1073 m.u.m[2][2] = -praydir->z;
1074 m.u.m[3][2] = 0.0f;
1075 m.u.m[0][3] = 0.0f;
1076 m.u.m[1][3] = 0.0f;
1077 m.u.m[2][3] = 0.0f;
1078 m.u.m[3][3] = 1.0f;
1080 vec.x = praypos->x - p0->x;
1081 vec.y = praypos->y - p0->y;
1082 vec.z = praypos->z - p0->z;
1083 vec.w = 0.0f;
1085 if ( D3DXMatrixInverse(&m, NULL, &m) )
1087 D3DXVec4Transform(&vec, &vec, &m);
1088 if ( (vec.x >= 0.0f) && (vec.y >= 0.0f) && (vec.x + vec.y <= 1.0f) && (vec.z >= 0.0f) )
1090 *pu = vec.x;
1091 *pv = vec.y;
1092 *pdist = fabs( vec.z );
1093 return TRUE;
1097 return FALSE;
1100 /*************************************************************************
1101 * D3DXSphereBoundProbe
1103 BOOL WINAPI D3DXSphereBoundProbe(CONST D3DXVECTOR3 *pcenter, FLOAT radius, CONST D3DXVECTOR3 *prayposition, CONST D3DXVECTOR3 *praydirection)
1105 D3DXVECTOR3 difference;
1106 FLOAT a, b, c, d;
1108 a = D3DXVec3LengthSq(praydirection);
1109 if (!D3DXVec3Subtract(&difference, prayposition, pcenter)) return FALSE;
1110 b = D3DXVec3Dot(&difference, praydirection);
1111 c = D3DXVec3LengthSq(&difference) - radius * radius;
1112 d = b * b - a * c;
1114 if ( ( d <= 0.0f ) || ( sqrt(d) <= b ) ) return FALSE;
1115 return TRUE;
1118 /*************************************************************************
1119 * D3DXCreateMesh
1121 HRESULT WINAPI D3DXCreateMesh(DWORD numfaces, DWORD numvertices, DWORD options, CONST D3DVERTEXELEMENT9 *declaration,
1122 LPDIRECT3DDEVICE9 device, LPD3DXMESH *mesh)
1124 HRESULT hr;
1125 DWORD fvf;
1126 IDirect3DVertexDeclaration9 *vertex_declaration;
1127 IDirect3DVertexBuffer9 *vertex_buffer;
1128 IDirect3DIndexBuffer9 *index_buffer;
1129 ID3DXMeshImpl *object;
1130 DWORD index_usage = 0;
1131 D3DPOOL index_pool = D3DPOOL_DEFAULT;
1132 D3DFORMAT index_format = D3DFMT_INDEX16;
1133 DWORD vertex_usage = 0;
1134 D3DPOOL vertex_pool = D3DPOOL_DEFAULT;
1135 int i;
1137 TRACE("(%d, %d, %x, %p, %p, %p)\n", numfaces, numvertices, options, declaration, device, mesh);
1139 if (numfaces == 0 || numvertices == 0 || declaration == NULL || device == NULL || mesh == NULL ||
1140 /* D3DXMESH_VB_SHARE is for cloning, and D3DXMESH_USEHWONLY is for ConvertToBlendedMesh */
1141 (options & (D3DXMESH_VB_SHARE | D3DXMESH_USEHWONLY | 0xfffe0000)))
1143 return D3DERR_INVALIDCALL;
1145 for (i = 0; declaration[i].Stream != 0xff; i++)
1146 if (declaration[i].Stream != 0)
1147 return D3DERR_INVALIDCALL;
1149 if (options & D3DXMESH_32BIT)
1150 index_format = D3DFMT_INDEX32;
1152 if (options & D3DXMESH_DONOTCLIP) {
1153 index_usage |= D3DUSAGE_DONOTCLIP;
1154 vertex_usage |= D3DUSAGE_DONOTCLIP;
1156 if (options & D3DXMESH_POINTS) {
1157 index_usage |= D3DUSAGE_POINTS;
1158 vertex_usage |= D3DUSAGE_POINTS;
1160 if (options & D3DXMESH_RTPATCHES) {
1161 index_usage |= D3DUSAGE_RTPATCHES;
1162 vertex_usage |= D3DUSAGE_RTPATCHES;
1164 if (options & D3DXMESH_NPATCHES) {
1165 index_usage |= D3DUSAGE_NPATCHES;
1166 vertex_usage |= D3DUSAGE_NPATCHES;
1169 if (options & D3DXMESH_VB_SYSTEMMEM)
1170 vertex_pool = D3DPOOL_SYSTEMMEM;
1171 else if (options & D3DXMESH_VB_MANAGED)
1172 vertex_pool = D3DPOOL_MANAGED;
1174 if (options & D3DXMESH_VB_WRITEONLY)
1175 vertex_usage |= D3DUSAGE_WRITEONLY;
1176 if (options & D3DXMESH_VB_DYNAMIC)
1177 vertex_usage |= D3DUSAGE_DYNAMIC;
1178 if (options & D3DXMESH_VB_SOFTWAREPROCESSING)
1179 vertex_usage |= D3DUSAGE_SOFTWAREPROCESSING;
1181 if (options & D3DXMESH_IB_SYSTEMMEM)
1182 index_pool = D3DPOOL_SYSTEMMEM;
1183 else if (options & D3DXMESH_IB_MANAGED)
1184 index_pool = D3DPOOL_MANAGED;
1186 if (options & D3DXMESH_IB_WRITEONLY)
1187 index_usage |= D3DUSAGE_WRITEONLY;
1188 if (options & D3DXMESH_IB_DYNAMIC)
1189 index_usage |= D3DUSAGE_DYNAMIC;
1190 if (options & D3DXMESH_IB_SOFTWAREPROCESSING)
1191 index_usage |= D3DUSAGE_SOFTWAREPROCESSING;
1193 hr = D3DXFVFFromDeclarator(declaration, &fvf);
1194 if (hr != D3D_OK)
1196 fvf = 0;
1199 /* Create vertex declaration */
1200 hr = IDirect3DDevice9_CreateVertexDeclaration(device,
1201 declaration,
1202 &vertex_declaration);
1203 if (FAILED(hr))
1205 WARN("Unexpected return value %x from IDirect3DDevice9_CreateVertexDeclaration.\n",hr);
1206 return hr;
1209 /* Create vertex buffer */
1210 hr = IDirect3DDevice9_CreateVertexBuffer(device,
1211 numvertices * D3DXGetDeclVertexSize(declaration, declaration[0].Stream),
1212 vertex_usage,
1213 fvf,
1214 vertex_pool,
1215 &vertex_buffer,
1216 NULL);
1217 if (FAILED(hr))
1219 WARN("Unexpected return value %x from IDirect3DDevice9_CreateVertexBuffer.\n",hr);
1220 IDirect3DVertexDeclaration9_Release(vertex_declaration);
1221 return hr;
1224 /* Create index buffer */
1225 hr = IDirect3DDevice9_CreateIndexBuffer(device,
1226 numfaces * 3 * ((index_format == D3DFMT_INDEX16) ? 2 : 4),
1227 index_usage,
1228 index_format,
1229 index_pool,
1230 &index_buffer,
1231 NULL);
1232 if (FAILED(hr))
1234 WARN("Unexpected return value %x from IDirect3DDevice9_CreateVertexBuffer.\n",hr);
1235 IDirect3DVertexBuffer9_Release(vertex_buffer);
1236 IDirect3DVertexDeclaration9_Release(vertex_declaration);
1237 return hr;
1240 object = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(ID3DXMeshImpl));
1241 if (object == NULL)
1243 IDirect3DIndexBuffer9_Release(index_buffer);
1244 IDirect3DVertexBuffer9_Release(vertex_buffer);
1245 IDirect3DVertexDeclaration9_Release(vertex_declaration);
1246 *mesh = NULL;
1247 return E_OUTOFMEMORY;
1249 object->ID3DXMesh_iface.lpVtbl = &D3DXMesh_Vtbl;
1250 object->ref = 1;
1252 object->numfaces = numfaces;
1253 object->numvertices = numvertices;
1254 object->options = options;
1255 object->fvf = fvf;
1256 object->device = device;
1257 IDirect3DDevice9_AddRef(device);
1259 object->vertex_declaration = vertex_declaration;
1260 object->vertex_buffer = vertex_buffer;
1261 object->index_buffer = index_buffer;
1263 *mesh = &object->ID3DXMesh_iface;
1265 return D3D_OK;
1268 /*************************************************************************
1269 * D3DXCreateMeshFVF
1271 HRESULT WINAPI D3DXCreateMeshFVF(DWORD numfaces, DWORD numvertices, DWORD options, DWORD fvf,
1272 LPDIRECT3DDEVICE9 device, LPD3DXMESH *mesh)
1274 HRESULT hr;
1275 D3DVERTEXELEMENT9 declaration[MAX_FVF_DECL_SIZE];
1277 TRACE("(%u, %u, %u, %u, %p, %p)\n", numfaces, numvertices, options, fvf, device, mesh);
1279 hr = D3DXDeclaratorFromFVF(fvf, declaration);
1280 if (FAILED(hr)) return hr;
1282 return D3DXCreateMesh(numfaces, numvertices, options, declaration, device, mesh);
1285 HRESULT WINAPI D3DXCreateBox(LPDIRECT3DDEVICE9 device, FLOAT width, FLOAT height,
1286 FLOAT depth, LPD3DXMESH* mesh, LPD3DXBUFFER* adjacency)
1288 FIXME("(%p, %f, %f, %f, %p, %p): stub\n", device, width, height, depth, mesh, adjacency);
1290 return E_NOTIMPL;
1293 struct vertex
1295 D3DXVECTOR3 position;
1296 D3DXVECTOR3 normal;
1299 typedef WORD face[3];
1301 struct sincos_table
1303 float *sin;
1304 float *cos;
1307 static void free_sincos_table(struct sincos_table *sincos_table)
1309 HeapFree(GetProcessHeap(), 0, sincos_table->cos);
1310 HeapFree(GetProcessHeap(), 0, sincos_table->sin);
1313 /* pre compute sine and cosine tables; caller must free */
1314 static BOOL compute_sincos_table(struct sincos_table *sincos_table, float angle_start, float angle_step, int n)
1316 float angle;
1317 int i;
1319 sincos_table->sin = HeapAlloc(GetProcessHeap(), 0, n * sizeof(*sincos_table->sin));
1320 if (!sincos_table->sin)
1322 return FALSE;
1324 sincos_table->cos = HeapAlloc(GetProcessHeap(), 0, n * sizeof(*sincos_table->cos));
1325 if (!sincos_table->cos)
1327 HeapFree(GetProcessHeap(), 0, sincos_table->sin);
1328 return FALSE;
1331 angle = angle_start;
1332 for (i = 0; i < n; i++)
1334 sincos_table->sin[i] = sin(angle);
1335 sincos_table->cos[i] = cos(angle);
1336 angle += angle_step;
1339 return TRUE;
1342 static WORD vertex_index(UINT slices, int slice, int stack)
1344 return stack*slices+slice+1;
1347 HRESULT WINAPI D3DXCreateSphere(LPDIRECT3DDEVICE9 device, FLOAT radius, UINT slices,
1348 UINT stacks, LPD3DXMESH* mesh, LPD3DXBUFFER* adjacency)
1350 DWORD number_of_vertices, number_of_faces;
1351 HRESULT hr;
1352 ID3DXMesh *sphere;
1353 struct vertex *vertices;
1354 face *faces;
1355 float phi_step, phi_start;
1356 struct sincos_table phi;
1357 float theta_step, theta, sin_theta, cos_theta;
1358 DWORD vertex, face;
1359 int slice, stack;
1361 TRACE("(%p, %f, %u, %u, %p, %p)\n", device, radius, slices, stacks, mesh, adjacency);
1363 if (!device || radius < 0.0f || slices < 2 || stacks < 2 || !mesh)
1365 return D3DERR_INVALIDCALL;
1368 if (adjacency)
1370 FIXME("Case of adjacency != NULL not implemented.\n");
1371 return E_NOTIMPL;
1374 number_of_vertices = 2 + slices * (stacks-1);
1375 number_of_faces = 2 * slices + (stacks - 2) * (2 * slices);
1377 hr = D3DXCreateMeshFVF(number_of_faces, number_of_vertices, D3DXMESH_MANAGED,
1378 D3DFVF_XYZ | D3DFVF_NORMAL, device, &sphere);
1379 if (FAILED(hr))
1381 return hr;
1384 hr = sphere->lpVtbl->LockVertexBuffer(sphere, D3DLOCK_DISCARD, (LPVOID *)&vertices);
1385 if (FAILED(hr))
1387 sphere->lpVtbl->Release(sphere);
1388 return hr;
1391 hr = sphere->lpVtbl->LockIndexBuffer(sphere, D3DLOCK_DISCARD, (LPVOID *)&faces);
1392 if (FAILED(hr))
1394 sphere->lpVtbl->UnlockVertexBuffer(sphere);
1395 sphere->lpVtbl->Release(sphere);
1396 return hr;
1399 /* phi = angle on xz plane wrt z axis */
1400 phi_step = -2 * M_PI / slices;
1401 phi_start = M_PI / 2;
1403 if (!compute_sincos_table(&phi, phi_start, phi_step, slices))
1405 sphere->lpVtbl->UnlockIndexBuffer(sphere);
1406 sphere->lpVtbl->UnlockVertexBuffer(sphere);
1407 sphere->lpVtbl->Release(sphere);
1408 return E_OUTOFMEMORY;
1411 /* theta = angle on xy plane wrt x axis */
1412 theta_step = M_PI / stacks;
1413 theta = theta_step;
1415 vertex = 0;
1416 face = 0;
1417 stack = 0;
1419 vertices[vertex].normal.x = 0.0f;
1420 vertices[vertex].normal.y = 0.0f;
1421 vertices[vertex].normal.z = 1.0f;
1422 vertices[vertex].position.x = 0.0f;
1423 vertices[vertex].position.y = 0.0f;
1424 vertices[vertex].position.z = radius;
1425 vertex++;
1427 for (stack = 0; stack < stacks - 1; stack++)
1429 sin_theta = sin(theta);
1430 cos_theta = cos(theta);
1432 for (slice = 0; slice < slices; slice++)
1434 vertices[vertex].normal.x = sin_theta * phi.cos[slice];
1435 vertices[vertex].normal.y = sin_theta * phi.sin[slice];
1436 vertices[vertex].normal.z = cos_theta;
1437 vertices[vertex].position.x = radius * sin_theta * phi.cos[slice];
1438 vertices[vertex].position.y = radius * sin_theta * phi.sin[slice];
1439 vertices[vertex].position.z = radius * cos_theta;
1440 vertex++;
1442 if (slice > 0)
1444 if (stack == 0)
1446 /* top stack is triangle fan */
1447 faces[face][0] = 0;
1448 faces[face][1] = slice + 1;
1449 faces[face][2] = slice;
1450 face++;
1452 else
1454 /* stacks in between top and bottom are quad strips */
1455 faces[face][0] = vertex_index(slices, slice-1, stack-1);
1456 faces[face][1] = vertex_index(slices, slice, stack-1);
1457 faces[face][2] = vertex_index(slices, slice-1, stack);
1458 face++;
1460 faces[face][0] = vertex_index(slices, slice, stack-1);
1461 faces[face][1] = vertex_index(slices, slice, stack);
1462 faces[face][2] = vertex_index(slices, slice-1, stack);
1463 face++;
1468 theta += theta_step;
1470 if (stack == 0)
1472 faces[face][0] = 0;
1473 faces[face][1] = 1;
1474 faces[face][2] = slice;
1475 face++;
1477 else
1479 faces[face][0] = vertex_index(slices, slice-1, stack-1);
1480 faces[face][1] = vertex_index(slices, 0, stack-1);
1481 faces[face][2] = vertex_index(slices, slice-1, stack);
1482 face++;
1484 faces[face][0] = vertex_index(slices, 0, stack-1);
1485 faces[face][1] = vertex_index(slices, 0, stack);
1486 faces[face][2] = vertex_index(slices, slice-1, stack);
1487 face++;
1491 vertices[vertex].position.x = 0.0f;
1492 vertices[vertex].position.y = 0.0f;
1493 vertices[vertex].position.z = -radius;
1494 vertices[vertex].normal.x = 0.0f;
1495 vertices[vertex].normal.y = 0.0f;
1496 vertices[vertex].normal.z = -1.0f;
1498 /* bottom stack is triangle fan */
1499 for (slice = 1; slice < slices; slice++)
1501 faces[face][0] = vertex_index(slices, slice-1, stack-1);
1502 faces[face][1] = vertex_index(slices, slice, stack-1);
1503 faces[face][2] = vertex;
1504 face++;
1507 faces[face][0] = vertex_index(slices, slice-1, stack-1);
1508 faces[face][1] = vertex_index(slices, 0, stack-1);
1509 faces[face][2] = vertex;
1511 free_sincos_table(&phi);
1512 sphere->lpVtbl->UnlockIndexBuffer(sphere);
1513 sphere->lpVtbl->UnlockVertexBuffer(sphere);
1514 *mesh = sphere;
1516 return D3D_OK;
1519 HRESULT WINAPI D3DXCreateCylinder(LPDIRECT3DDEVICE9 device, FLOAT radius1, FLOAT radius2, FLOAT length, UINT slices,
1520 UINT stacks, LPD3DXMESH* mesh, LPD3DXBUFFER* adjacency)
1522 DWORD number_of_vertices, number_of_faces;
1523 HRESULT hr;
1524 ID3DXMesh *cylinder;
1525 struct vertex *vertices;
1526 face *faces;
1527 float theta_step, theta_start;
1528 struct sincos_table theta;
1529 float delta_radius, radius, radius_step;
1530 float z, z_step, z_normal;
1531 DWORD vertex, face;
1532 int slice, stack;
1534 TRACE("(%p, %f, %f, %f, %u, %u, %p, %p)\n", device, radius1, radius2, length, slices, stacks, mesh, adjacency);
1536 if (device == NULL || radius1 < 0.0f || radius2 < 0.0f || length < 0.0f || slices < 2 || stacks < 1 || mesh == NULL)
1538 return D3DERR_INVALIDCALL;
1541 if (adjacency)
1543 FIXME("Case of adjacency != NULL not implemented.\n");
1544 return E_NOTIMPL;
1547 number_of_vertices = 2 + (slices * (3 + stacks));
1548 number_of_faces = 2 * slices + stacks * (2 * slices);
1550 hr = D3DXCreateMeshFVF(number_of_faces, number_of_vertices, D3DXMESH_MANAGED,
1551 D3DFVF_XYZ | D3DFVF_NORMAL, device, &cylinder);
1552 if (FAILED(hr))
1554 return hr;
1557 hr = cylinder->lpVtbl->LockVertexBuffer(cylinder, D3DLOCK_DISCARD, (LPVOID *)&vertices);
1558 if (FAILED(hr))
1560 cylinder->lpVtbl->Release(cylinder);
1561 return hr;
1564 hr = cylinder->lpVtbl->LockIndexBuffer(cylinder, D3DLOCK_DISCARD, (LPVOID *)&faces);
1565 if (FAILED(hr))
1567 cylinder->lpVtbl->UnlockVertexBuffer(cylinder);
1568 cylinder->lpVtbl->Release(cylinder);
1569 return hr;
1572 /* theta = angle on xy plane wrt x axis */
1573 theta_step = -2 * M_PI / slices;
1574 theta_start = M_PI / 2;
1576 if (!compute_sincos_table(&theta, theta_start, theta_step, slices))
1578 cylinder->lpVtbl->UnlockIndexBuffer(cylinder);
1579 cylinder->lpVtbl->UnlockVertexBuffer(cylinder);
1580 cylinder->lpVtbl->Release(cylinder);
1581 return E_OUTOFMEMORY;
1584 vertex = 0;
1585 face = 0;
1587 delta_radius = radius1 - radius2;
1588 radius = radius1;
1589 radius_step = delta_radius / stacks;
1591 z = -length / 2;
1592 z_step = length / stacks;
1593 z_normal = delta_radius / length;
1594 if (isnan(z_normal))
1596 z_normal = 0.0f;
1599 vertices[vertex].normal.x = 0.0f;
1600 vertices[vertex].normal.y = 0.0f;
1601 vertices[vertex].normal.z = -1.0f;
1602 vertices[vertex].position.x = 0.0f;
1603 vertices[vertex].position.y = 0.0f;
1604 vertices[vertex++].position.z = z;
1606 for (slice = 0; slice < slices; slice++, vertex++)
1608 vertices[vertex].normal.x = 0.0f;
1609 vertices[vertex].normal.y = 0.0f;
1610 vertices[vertex].normal.z = -1.0f;
1611 vertices[vertex].position.x = radius * theta.cos[slice];
1612 vertices[vertex].position.y = radius * theta.sin[slice];
1613 vertices[vertex].position.z = z;
1615 if (slice > 0)
1617 faces[face][0] = 0;
1618 faces[face][1] = slice;
1619 faces[face++][2] = slice + 1;
1623 faces[face][0] = 0;
1624 faces[face][1] = slice;
1625 faces[face++][2] = 1;
1627 for (stack = 1; stack <= stacks+1; stack++)
1629 for (slice = 0; slice < slices; slice++, vertex++)
1631 vertices[vertex].normal.x = theta.cos[slice];
1632 vertices[vertex].normal.y = theta.sin[slice];
1633 vertices[vertex].normal.z = z_normal;
1634 D3DXVec3Normalize(&vertices[vertex].normal, &vertices[vertex].normal);
1635 vertices[vertex].position.x = radius * theta.cos[slice];
1636 vertices[vertex].position.y = radius * theta.sin[slice];
1637 vertices[vertex].position.z = z;
1639 if (stack > 1 && slice > 0)
1641 faces[face][0] = vertex_index(slices, slice-1, stack-1);
1642 faces[face][1] = vertex_index(slices, slice-1, stack);
1643 faces[face++][2] = vertex_index(slices, slice, stack-1);
1645 faces[face][0] = vertex_index(slices, slice, stack-1);
1646 faces[face][1] = vertex_index(slices, slice-1, stack);
1647 faces[face++][2] = vertex_index(slices, slice, stack);
1651 if (stack > 1)
1653 faces[face][0] = vertex_index(slices, slice-1, stack-1);
1654 faces[face][1] = vertex_index(slices, slice-1, stack);
1655 faces[face++][2] = vertex_index(slices, 0, stack-1);
1657 faces[face][0] = vertex_index(slices, 0, stack-1);
1658 faces[face][1] = vertex_index(slices, slice-1, stack);
1659 faces[face++][2] = vertex_index(slices, 0, stack);
1662 if (stack < stacks + 1)
1664 z += z_step;
1665 radius -= radius_step;
1669 for (slice = 0; slice < slices; slice++, vertex++)
1671 vertices[vertex].normal.x = 0.0f;
1672 vertices[vertex].normal.y = 0.0f;
1673 vertices[vertex].normal.z = 1.0f;
1674 vertices[vertex].position.x = radius * theta.cos[slice];
1675 vertices[vertex].position.y = radius * theta.sin[slice];
1676 vertices[vertex].position.z = z;
1678 if (slice > 0)
1680 faces[face][0] = vertex_index(slices, slice-1, stack);
1681 faces[face][1] = number_of_vertices - 1;
1682 faces[face++][2] = vertex_index(slices, slice, stack);
1686 vertices[vertex].position.x = 0.0f;
1687 vertices[vertex].position.y = 0.0f;
1688 vertices[vertex].position.z = z;
1689 vertices[vertex].normal.x = 0.0f;
1690 vertices[vertex].normal.y = 0.0f;
1691 vertices[vertex].normal.z = 1.0f;
1693 faces[face][0] = vertex_index(slices, slice-1, stack);
1694 faces[face][1] = number_of_vertices - 1;
1695 faces[face][2] = vertex_index(slices, 0, stack);
1697 free_sincos_table(&theta);
1698 cylinder->lpVtbl->UnlockIndexBuffer(cylinder);
1699 cylinder->lpVtbl->UnlockVertexBuffer(cylinder);
1700 *mesh = cylinder;
1702 return D3D_OK;
1705 HRESULT WINAPI D3DXCreateTeapot(LPDIRECT3DDEVICE9 device, LPD3DXMESH *mesh, LPD3DXBUFFER* adjacency)
1707 FIXME("(%p, %p, %p): stub\n", device, mesh, adjacency);
1709 return E_NOTIMPL;
1712 HRESULT WINAPI D3DXCreateTextA(LPDIRECT3DDEVICE9 device,
1713 HDC hdc, LPCSTR text,
1714 FLOAT deviation, FLOAT extrusion,
1715 LPD3DXMESH *mesh, LPD3DXBUFFER *adjacency,
1716 LPGLYPHMETRICSFLOAT glyphmetrics)
1718 HRESULT hr;
1719 int len;
1720 LPWSTR textW;
1722 TRACE("(%p, %p, %s, %f, %f, %p, %p, %p)\n", device, hdc,
1723 debugstr_a(text), deviation, extrusion, mesh, adjacency, glyphmetrics);
1725 if (!text)
1726 return D3DERR_INVALIDCALL;
1728 len = MultiByteToWideChar(CP_ACP, 0, text, -1, NULL, 0);
1729 textW = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
1730 MultiByteToWideChar(CP_ACP, 0, text, -1, textW, len);
1732 hr = D3DXCreateTextW(device, hdc, textW, deviation, extrusion,
1733 mesh, adjacency, glyphmetrics);
1734 HeapFree(GetProcessHeap(), 0, textW);
1736 return hr;
1739 enum pointtype {
1740 POINTTYPE_CURVE = 0,
1741 POINTTYPE_CORNER,
1742 POINTTYPE_CURVE_START,
1743 POINTTYPE_CURVE_END,
1744 POINTTYPE_CURVE_MIDDLE,
1747 struct point2d
1749 D3DXVECTOR2 pos;
1750 enum pointtype corner;
1753 struct dynamic_array
1755 int count, capacity;
1756 void *items;
1759 /* is a dynamic_array */
1760 struct outline
1762 int count, capacity;
1763 struct point2d *items;
1766 /* is a dynamic_array */
1767 struct outline_array
1769 int count, capacity;
1770 struct outline *items;
1773 struct face_array
1775 int count;
1776 face *items;
1779 struct point2d_index
1781 struct outline *outline;
1782 int vertex;
1785 struct point2d_index_array
1787 int count;
1788 struct point2d_index *items;
1791 struct glyphinfo
1793 struct outline_array outlines;
1794 struct face_array faces;
1795 struct point2d_index_array ordered_vertices;
1796 float offset_x;
1799 /* is an dynamic_array */
1800 struct word_array
1802 int count, capacity;
1803 WORD *items;
1806 /* complex polygons are split into monotone polygons, which have
1807 * at most 2 intersections with the vertical sweep line */
1808 struct triangulation
1810 struct word_array vertex_stack;
1811 BOOL last_on_top, merging;
1814 /* is an dynamic_array */
1815 struct triangulation_array
1817 int count, capacity;
1818 struct triangulation *items;
1820 struct glyphinfo *glyph;
1823 static BOOL reserve(struct dynamic_array *array, int count, int itemsize)
1825 if (count > array->capacity) {
1826 void *new_buffer;
1827 int new_capacity;
1828 if (array->items && array->capacity) {
1829 new_capacity = max(array->capacity * 2, count);
1830 new_buffer = HeapReAlloc(GetProcessHeap(), 0, array->items, new_capacity * itemsize);
1831 } else {
1832 new_capacity = max(16, count);
1833 new_buffer = HeapAlloc(GetProcessHeap(), 0, new_capacity * itemsize);
1835 if (!new_buffer)
1836 return FALSE;
1837 array->items = new_buffer;
1838 array->capacity = new_capacity;
1840 return TRUE;
1843 static struct point2d *add_points(struct outline *array, int num)
1845 struct point2d *item;
1847 if (!reserve((struct dynamic_array *)array, array->count + num, sizeof(array->items[0])))
1848 return NULL;
1850 item = &array->items[array->count];
1851 array->count += num;
1852 return item;
1855 static struct outline *add_outline(struct outline_array *array)
1857 struct outline *item;
1859 if (!reserve((struct dynamic_array *)array, array->count + 1, sizeof(array->items[0])))
1860 return NULL;
1862 item = &array->items[array->count++];
1863 ZeroMemory(item, sizeof(*item));
1864 return item;
1867 static inline face *add_face(struct face_array *array)
1869 return &array->items[array->count++];
1872 static struct triangulation *add_triangulation(struct triangulation_array *array)
1874 struct triangulation *item;
1876 if (!reserve((struct dynamic_array *)array, array->count + 1, sizeof(array->items[0])))
1877 return NULL;
1879 item = &array->items[array->count++];
1880 ZeroMemory(item, sizeof(*item));
1881 return item;
1884 static HRESULT add_vertex_index(struct word_array *array, WORD vertex_index)
1886 if (!reserve((struct dynamic_array *)array, array->count + 1, sizeof(array->items[0])))
1887 return E_OUTOFMEMORY;
1889 array->items[array->count++] = vertex_index;
1890 return S_OK;
1893 /* assume fixed point numbers can be converted to float point in place */
1894 C_ASSERT(sizeof(FIXED) == sizeof(float));
1895 C_ASSERT(sizeof(POINTFX) == sizeof(D3DXVECTOR2));
1897 static inline D3DXVECTOR2 *convert_fixed_to_float(POINTFX *pt, int count, float emsquare)
1899 D3DXVECTOR2 *ret = (D3DXVECTOR2*)pt;
1900 while (count--) {
1901 D3DXVECTOR2 *pt_flt = (D3DXVECTOR2*)pt;
1902 pt_flt->x = (pt->x.value + pt->x.fract / (float)0x10000) / emsquare;
1903 pt_flt->y = (pt->y.value + pt->y.fract / (float)0x10000) / emsquare;
1904 pt++;
1906 return ret;
1909 static HRESULT add_bezier_points(struct outline *outline, const D3DXVECTOR2 *p1,
1910 const D3DXVECTOR2 *p2, const D3DXVECTOR2 *p3,
1911 float max_deviation_sq)
1913 D3DXVECTOR2 split1 = {0, 0}, split2 = {0, 0}, middle, vec;
1914 float deviation_sq;
1916 D3DXVec2Scale(&split1, D3DXVec2Add(&split1, p1, p2), 0.5f);
1917 D3DXVec2Scale(&split2, D3DXVec2Add(&split2, p2, p3), 0.5f);
1918 D3DXVec2Scale(&middle, D3DXVec2Add(&middle, &split1, &split2), 0.5f);
1920 deviation_sq = D3DXVec2LengthSq(D3DXVec2Subtract(&vec, &middle, p2));
1921 if (deviation_sq < max_deviation_sq) {
1922 struct point2d *pt = add_points(outline, 1);
1923 if (!pt) return E_OUTOFMEMORY;
1924 pt->pos = *p2;
1925 pt->corner = POINTTYPE_CURVE;
1926 /* the end point is omitted because the end line merges into the next segment of
1927 * the split bezier curve, and the end of the split bezier curve is added outside
1928 * this recursive function. */
1929 } else {
1930 HRESULT hr = add_bezier_points(outline, p1, &split1, &middle, max_deviation_sq);
1931 if (hr != S_OK) return hr;
1932 hr = add_bezier_points(outline, &middle, &split2, p3, max_deviation_sq);
1933 if (hr != S_OK) return hr;
1936 return S_OK;
1939 static inline BOOL is_direction_similar(D3DXVECTOR2 *dir1, D3DXVECTOR2 *dir2, float cos_theta)
1941 /* dot product = cos(theta) */
1942 return D3DXVec2Dot(dir1, dir2) > cos_theta;
1945 static inline D3DXVECTOR2 *unit_vec2(D3DXVECTOR2 *dir, const D3DXVECTOR2 *pt1, const D3DXVECTOR2 *pt2)
1947 return D3DXVec2Normalize(D3DXVec2Subtract(dir, pt2, pt1), dir);
1950 struct cos_table
1952 float cos_half;
1953 float cos_45;
1954 float cos_90;
1957 static BOOL attempt_line_merge(struct outline *outline,
1958 int pt_index,
1959 const D3DXVECTOR2 *nextpt,
1960 BOOL to_curve,
1961 const struct cos_table *table)
1963 D3DXVECTOR2 curdir, lastdir;
1964 struct point2d *prevpt, *pt;
1965 BOOL ret = FALSE;
1967 pt = &outline->items[pt_index];
1968 pt_index = (pt_index - 1 + outline->count) % outline->count;
1969 prevpt = &outline->items[pt_index];
1971 if (to_curve)
1972 pt->corner = pt->corner != POINTTYPE_CORNER ? POINTTYPE_CURVE_MIDDLE : POINTTYPE_CURVE_START;
1974 if (outline->count < 2)
1975 return FALSE;
1977 /* remove last point if the next line continues the last line */
1978 unit_vec2(&lastdir, &prevpt->pos, &pt->pos);
1979 unit_vec2(&curdir, &pt->pos, nextpt);
1980 if (is_direction_similar(&lastdir, &curdir, table->cos_half))
1982 outline->count--;
1983 if (pt->corner == POINTTYPE_CURVE_END)
1984 prevpt->corner = pt->corner;
1985 if (prevpt->corner == POINTTYPE_CURVE_END && to_curve)
1986 prevpt->corner = POINTTYPE_CURVE_MIDDLE;
1987 pt = prevpt;
1989 ret = TRUE;
1990 if (outline->count < 2)
1991 return ret;
1993 pt_index = (pt_index - 1 + outline->count) % outline->count;
1994 prevpt = &outline->items[pt_index];
1995 unit_vec2(&lastdir, &prevpt->pos, &pt->pos);
1996 unit_vec2(&curdir, &pt->pos, nextpt);
1998 return ret;
2001 static HRESULT create_outline(struct glyphinfo *glyph, void *raw_outline, int datasize,
2002 float max_deviation_sq, float emsquare, const struct cos_table *cos_table)
2004 TTPOLYGONHEADER *header = (TTPOLYGONHEADER *)raw_outline;
2006 while ((char *)header < (char *)raw_outline + datasize)
2008 TTPOLYCURVE *curve = (TTPOLYCURVE *)(header + 1);
2009 struct point2d *lastpt, *pt;
2010 D3DXVECTOR2 lastdir;
2011 D3DXVECTOR2 *pt_flt;
2012 int j;
2013 struct outline *outline = add_outline(&glyph->outlines);
2015 if (!outline)
2016 return E_OUTOFMEMORY;
2018 pt = add_points(outline, 1);
2019 if (!pt)
2020 return E_OUTOFMEMORY;
2021 pt_flt = convert_fixed_to_float(&header->pfxStart, 1, emsquare);
2022 pt->pos = *pt_flt;
2023 pt->corner = POINTTYPE_CORNER;
2025 if (header->dwType != TT_POLYGON_TYPE)
2026 FIXME("Unknown header type %d\n", header->dwType);
2028 while ((char *)curve < (char *)header + header->cb)
2030 D3DXVECTOR2 bezier_start = outline->items[outline->count - 1].pos;
2031 BOOL to_curve = curve->wType != TT_PRIM_LINE && curve->cpfx > 1;
2033 if (!curve->cpfx) {
2034 curve = (TTPOLYCURVE *)&curve->apfx[curve->cpfx];
2035 continue;
2038 pt_flt = convert_fixed_to_float(curve->apfx, curve->cpfx, emsquare);
2040 attempt_line_merge(outline, outline->count - 1, &pt_flt[0], to_curve, cos_table);
2042 if (to_curve)
2044 HRESULT hr;
2045 int count = curve->cpfx;
2046 j = 0;
2048 while (count > 2)
2050 D3DXVECTOR2 bezier_end;
2052 D3DXVec2Scale(&bezier_end, D3DXVec2Add(&bezier_end, &pt_flt[j], &pt_flt[j+1]), 0.5f);
2053 hr = add_bezier_points(outline, &bezier_start, &pt_flt[j], &bezier_end, max_deviation_sq);
2054 if (hr != S_OK)
2055 return hr;
2056 bezier_start = bezier_end;
2057 count--;
2058 j++;
2060 hr = add_bezier_points(outline, &bezier_start, &pt_flt[j], &pt_flt[j+1], max_deviation_sq);
2061 if (hr != S_OK)
2062 return hr;
2064 pt = add_points(outline, 1);
2065 if (!pt)
2066 return E_OUTOFMEMORY;
2067 j++;
2068 pt->pos = pt_flt[j];
2069 pt->corner = POINTTYPE_CURVE_END;
2070 } else {
2071 pt = add_points(outline, curve->cpfx);
2072 if (!pt)
2073 return E_OUTOFMEMORY;
2074 for (j = 0; j < curve->cpfx; j++)
2076 pt->pos = pt_flt[j];
2077 pt->corner = POINTTYPE_CORNER;
2078 pt++;
2082 curve = (TTPOLYCURVE *)&curve->apfx[curve->cpfx];
2085 /* remove last point if the next line continues the last line */
2086 if (outline->count >= 3) {
2087 BOOL to_curve;
2089 lastpt = &outline->items[outline->count - 1];
2090 pt = &outline->items[0];
2091 if (pt->pos.x == lastpt->pos.x && pt->pos.y == lastpt->pos.y) {
2092 if (lastpt->corner == POINTTYPE_CURVE_END)
2094 if (pt->corner == POINTTYPE_CURVE_START)
2095 pt->corner = POINTTYPE_CURVE_MIDDLE;
2096 else
2097 pt->corner = POINTTYPE_CURVE_END;
2099 outline->count--;
2100 lastpt = &outline->items[outline->count - 1];
2101 } else {
2102 /* outline closed with a line from end to start point */
2103 attempt_line_merge(outline, outline->count - 1, &pt->pos, FALSE, cos_table);
2105 lastpt = &outline->items[0];
2106 to_curve = lastpt->corner != POINTTYPE_CORNER && lastpt->corner != POINTTYPE_CURVE_END;
2107 if (lastpt->corner == POINTTYPE_CURVE_START)
2108 lastpt->corner = POINTTYPE_CORNER;
2109 pt = &outline->items[1];
2110 if (attempt_line_merge(outline, 0, &pt->pos, to_curve, cos_table))
2111 *lastpt = outline->items[outline->count];
2114 lastpt = &outline->items[outline->count - 1];
2115 pt = &outline->items[0];
2116 unit_vec2(&lastdir, &lastpt->pos, &pt->pos);
2117 for (j = 0; j < outline->count; j++)
2119 D3DXVECTOR2 curdir;
2121 lastpt = pt;
2122 pt = &outline->items[(j + 1) % outline->count];
2123 unit_vec2(&curdir, &lastpt->pos, &pt->pos);
2125 switch (lastpt->corner)
2127 case POINTTYPE_CURVE_START:
2128 case POINTTYPE_CURVE_END:
2129 if (!is_direction_similar(&lastdir, &curdir, cos_table->cos_45))
2130 lastpt->corner = POINTTYPE_CORNER;
2131 break;
2132 case POINTTYPE_CURVE_MIDDLE:
2133 if (!is_direction_similar(&lastdir, &curdir, cos_table->cos_90))
2134 lastpt->corner = POINTTYPE_CORNER;
2135 else
2136 lastpt->corner = POINTTYPE_CURVE;
2137 break;
2138 default:
2139 break;
2141 lastdir = curdir;
2144 header = (TTPOLYGONHEADER *)((char *)header + header->cb);
2146 return S_OK;
2149 /* Get the y-distance from a line to a point */
2150 static float get_line_to_point_y_distance(D3DXVECTOR2 *line_pt1,
2151 D3DXVECTOR2 *line_pt2,
2152 D3DXVECTOR2 *point)
2154 D3DXVECTOR2 line_vec = {0, 0};
2155 float line_pt_dx;
2156 float line_y;
2158 D3DXVec2Subtract(&line_vec, line_pt2, line_pt1);
2159 line_pt_dx = point->x - line_pt1->x;
2160 line_y = line_pt1->y + (line_vec.y * line_pt_dx) / line_vec.x;
2161 return point->y - line_y;
2164 static D3DXVECTOR2 *get_indexed_point(struct point2d_index *pt_idx)
2166 return &pt_idx->outline->items[pt_idx->vertex].pos;
2169 static D3DXVECTOR2 *get_ordered_vertex(struct glyphinfo *glyph, WORD index)
2171 return get_indexed_point(&glyph->ordered_vertices.items[index]);
2174 static void remove_triangulation(struct triangulation_array *array, struct triangulation *item)
2176 HeapFree(GetProcessHeap(), 0, item->vertex_stack.items);
2177 MoveMemory(item, item + 1, (char*)&array->items[array->count] - (char*)(item + 1));
2178 array->count--;
2181 static HRESULT triangulation_add_point(struct triangulation **t_ptr,
2182 struct triangulation_array *triangulations,
2183 WORD vtx_idx,
2184 BOOL to_top)
2186 struct glyphinfo *glyph = triangulations->glyph;
2187 struct triangulation *t = *t_ptr;
2188 HRESULT hr;
2189 face *face;
2190 int f1, f2;
2192 if (t->last_on_top) {
2193 f1 = 1;
2194 f2 = 2;
2195 } else {
2196 f1 = 2;
2197 f2 = 1;
2200 if (t->last_on_top != to_top && t->vertex_stack.count > 1) {
2201 /* consume all vertices on the stack */
2202 WORD last_pt = t->vertex_stack.items[0];
2203 int i;
2204 for (i = 1; i < t->vertex_stack.count; i++)
2206 face = add_face(&glyph->faces);
2207 if (!face) return E_OUTOFMEMORY;
2208 (*face)[0] = vtx_idx;
2209 (*face)[f1] = last_pt;
2210 (*face)[f2] = last_pt = t->vertex_stack.items[i];
2212 t->vertex_stack.items[0] = last_pt;
2213 t->vertex_stack.count = 1;
2214 } else if (t->vertex_stack.count > 1) {
2215 int i = t->vertex_stack.count - 1;
2216 D3DXVECTOR2 *point = get_ordered_vertex(glyph, vtx_idx);
2217 WORD top_idx = t->vertex_stack.items[i--];
2218 D3DXVECTOR2 *top_pt = get_ordered_vertex(glyph, top_idx);
2220 while (i >= 0)
2222 WORD prev_idx = t->vertex_stack.items[i--];
2223 D3DXVECTOR2 *prev_pt = get_ordered_vertex(glyph, prev_idx);
2225 if (prev_pt->x != top_pt->x &&
2226 ((to_top && get_line_to_point_y_distance(prev_pt, top_pt, point) > 0) ||
2227 (!to_top && get_line_to_point_y_distance(prev_pt, top_pt, point) < 0)))
2228 break;
2230 face = add_face(&glyph->faces);
2231 if (!face) return E_OUTOFMEMORY;
2232 (*face)[0] = vtx_idx;
2233 (*face)[f1] = prev_idx;
2234 (*face)[f2] = top_idx;
2236 top_pt = prev_pt;
2237 top_idx = prev_idx;
2238 t->vertex_stack.count--;
2241 t->last_on_top = to_top;
2243 hr = add_vertex_index(&t->vertex_stack, vtx_idx);
2245 if (hr == S_OK && t->merging) {
2246 struct triangulation *t2;
2248 t2 = to_top ? t - 1 : t + 1;
2249 t2->merging = FALSE;
2250 hr = triangulation_add_point(&t2, triangulations, vtx_idx, to_top);
2251 if (hr != S_OK) return hr;
2252 remove_triangulation(triangulations, t);
2253 if (t2 > t)
2254 t2--;
2255 *t_ptr = t2;
2257 return hr;
2260 /* check if the point is next on the outline for either the top or bottom */
2261 static D3DXVECTOR2 *triangulation_get_next_point(struct triangulation *t, struct glyphinfo *glyph, BOOL on_top)
2263 int i = t->last_on_top == on_top ? t->vertex_stack.count - 1 : 0;
2264 WORD idx = t->vertex_stack.items[i];
2265 struct point2d_index *pt_idx = &glyph->ordered_vertices.items[idx];
2266 struct outline *outline = pt_idx->outline;
2268 if (on_top)
2269 i = (pt_idx->vertex + outline->count - 1) % outline->count;
2270 else
2271 i = (pt_idx->vertex + 1) % outline->count;
2273 return &outline->items[i].pos;
2276 static int compare_vertex_indices(const void *a, const void *b)
2278 const struct point2d_index *idx1 = a, *idx2 = b;
2279 const D3DXVECTOR2 *p1 = &idx1->outline->items[idx1->vertex].pos;
2280 const D3DXVECTOR2 *p2 = &idx2->outline->items[idx2->vertex].pos;
2281 float diff = p1->x - p2->x;
2283 if (diff == 0.0f)
2284 diff = p1->y - p2->y;
2286 return diff == 0.0f ? 0 : (diff > 0.0f ? -1 : 1);
2289 static HRESULT triangulate(struct triangulation_array *triangulations)
2291 int sweep_idx;
2292 HRESULT hr;
2293 struct glyphinfo *glyph = triangulations->glyph;
2294 int nb_vertices = 0;
2295 int i;
2296 struct point2d_index *idx_ptr;
2298 for (i = 0; i < glyph->outlines.count; i++)
2299 nb_vertices += glyph->outlines.items[i].count;
2301 glyph->ordered_vertices.items = HeapAlloc(GetProcessHeap(), 0,
2302 nb_vertices * sizeof(*glyph->ordered_vertices.items));
2303 if (!glyph->ordered_vertices.items)
2304 return E_OUTOFMEMORY;
2306 idx_ptr = glyph->ordered_vertices.items;
2307 for (i = 0; i < glyph->outlines.count; i++)
2309 struct outline *outline = &glyph->outlines.items[i];
2310 int j;
2312 idx_ptr->outline = outline;
2313 idx_ptr->vertex = 0;
2314 idx_ptr++;
2315 for (j = outline->count - 1; j > 0; j--)
2317 idx_ptr->outline = outline;
2318 idx_ptr->vertex = j;
2319 idx_ptr++;
2322 glyph->ordered_vertices.count = nb_vertices;
2324 /* Native implementation seems to try to create a triangle fan from
2325 * the first outline point if the glyph only has one outline. */
2326 if (glyph->outlines.count == 1)
2328 struct outline *outline = glyph->outlines.items;
2329 D3DXVECTOR2 *base = &outline->items[0].pos;
2330 D3DXVECTOR2 *last = &outline->items[1].pos;
2331 float ccw = 0;
2333 for (i = 2; i < outline->count; i++)
2335 D3DXVECTOR2 *next = &outline->items[i].pos;
2336 D3DXVECTOR2 v1 = {0.0f, 0.0f};
2337 D3DXVECTOR2 v2 = {0.0f, 0.0f};
2339 D3DXVec2Subtract(&v1, base, last);
2340 D3DXVec2Subtract(&v2, last, next);
2341 ccw = D3DXVec2CCW(&v1, &v2);
2342 if (ccw > 0.0f)
2343 break;
2345 last = next;
2347 if (ccw <= 0)
2349 glyph->faces.items = HeapAlloc(GetProcessHeap(), 0,
2350 (outline->count - 2) * sizeof(glyph->faces.items[0]));
2351 if (!glyph->faces.items)
2352 return E_OUTOFMEMORY;
2354 glyph->faces.count = outline->count - 2;
2355 for (i = 0; i < glyph->faces.count; i++)
2357 glyph->faces.items[i][0] = 0;
2358 glyph->faces.items[i][1] = i + 1;
2359 glyph->faces.items[i][2] = i + 2;
2361 return S_OK;
2365 /* Perform 2D polygon triangulation for complex glyphs.
2366 * Triangulation is performed using a sweep line concept, from right to left,
2367 * by processing vertices in sorted order. Complex polygons are split into
2368 * monotone polygons which are triangulated seperately. */
2369 /* FIXME: The order of the faces is not consistent with the native implementation. */
2371 /* Reserve space for maximum possible faces from triangulation.
2372 * # faces for outer outlines = outline->count - 2
2373 * # faces for inner outlines = outline->count + 2
2374 * There must be at least 1 outer outline. */
2375 glyph->faces.items = HeapAlloc(GetProcessHeap(), 0,
2376 (nb_vertices + glyph->outlines.count * 2 - 4) * sizeof(glyph->faces.items[0]));
2377 if (!glyph->faces.items)
2378 return E_OUTOFMEMORY;
2380 qsort(glyph->ordered_vertices.items, nb_vertices,
2381 sizeof(glyph->ordered_vertices.items[0]), compare_vertex_indices);
2382 for (sweep_idx = 0; sweep_idx < glyph->ordered_vertices.count; sweep_idx++)
2384 int start = 0;
2385 int end = triangulations->count;
2387 while (start < end)
2389 D3DXVECTOR2 *sweep_vtx = get_ordered_vertex(glyph, sweep_idx);
2390 int current = (start + end) / 2;
2391 struct triangulation *t = &triangulations->items[current];
2392 BOOL on_top_outline = FALSE;
2393 D3DXVECTOR2 *top_next, *bottom_next;
2394 WORD top_idx, bottom_idx;
2396 if (t->merging && t->last_on_top)
2397 top_next = triangulation_get_next_point(t + 1, glyph, TRUE);
2398 else
2399 top_next = triangulation_get_next_point(t, glyph, TRUE);
2400 if (sweep_vtx == top_next)
2402 if (t->merging && t->last_on_top)
2403 t++;
2404 hr = triangulation_add_point(&t, triangulations, sweep_idx, TRUE);
2405 if (hr != S_OK) return hr;
2407 if (t + 1 < &triangulations->items[triangulations->count] &&
2408 triangulation_get_next_point(t + 1, glyph, FALSE) == sweep_vtx)
2410 /* point also on bottom outline of higher triangulation */
2411 struct triangulation *t2 = t + 1;
2412 hr = triangulation_add_point(&t2, triangulations, sweep_idx, FALSE);
2413 if (hr != S_OK) return hr;
2415 t->merging = TRUE;
2416 t2->merging = TRUE;
2418 on_top_outline = TRUE;
2421 if (t->merging && !t->last_on_top)
2422 bottom_next = triangulation_get_next_point(t - 1, glyph, FALSE);
2423 else
2424 bottom_next = triangulation_get_next_point(t, glyph, FALSE);
2425 if (sweep_vtx == bottom_next)
2427 if (t->merging && !t->last_on_top)
2428 t--;
2429 if (on_top_outline) {
2430 /* outline finished */
2431 remove_triangulation(triangulations, t);
2432 break;
2435 hr = triangulation_add_point(&t, triangulations, sweep_idx, FALSE);
2436 if (hr != S_OK) return hr;
2438 if (t > triangulations->items &&
2439 triangulation_get_next_point(t - 1, glyph, TRUE) == sweep_vtx)
2441 struct triangulation *t2 = t - 1;
2442 /* point also on top outline of lower triangulation */
2443 hr = triangulation_add_point(&t2, triangulations, sweep_idx, TRUE);
2444 if (hr != S_OK) return hr;
2445 t = t2 + 1; /* t may be invalidated by triangulation merging */
2447 t->merging = TRUE;
2448 t2->merging = TRUE;
2450 break;
2452 if (on_top_outline)
2453 break;
2455 if (t->last_on_top) {
2456 top_idx = t->vertex_stack.items[t->vertex_stack.count - 1];
2457 bottom_idx = t->vertex_stack.items[0];
2458 } else {
2459 top_idx = t->vertex_stack.items[0];
2460 bottom_idx = t->vertex_stack.items[t->vertex_stack.count - 1];
2463 /* check if the point is inside or outside this polygon */
2464 if (get_line_to_point_y_distance(get_ordered_vertex(glyph, top_idx),
2465 top_next, sweep_vtx) > 0)
2466 { /* above */
2467 start = current + 1;
2468 } else if (get_line_to_point_y_distance(get_ordered_vertex(glyph, bottom_idx),
2469 bottom_next, sweep_vtx) < 0)
2470 { /* below */
2471 end = current;
2472 } else if (t->merging) {
2473 /* inside, so cancel merging */
2474 struct triangulation *t2 = t->last_on_top ? t + 1 : t - 1;
2475 t->merging = FALSE;
2476 t2->merging = FALSE;
2477 hr = triangulation_add_point(&t, triangulations, sweep_idx, t->last_on_top);
2478 if (hr != S_OK) return hr;
2479 hr = triangulation_add_point(&t2, triangulations, sweep_idx, t2->last_on_top);
2480 if (hr != S_OK) return hr;
2481 break;
2482 } else {
2483 /* inside, so split polygon into two monotone parts */
2484 struct triangulation *t2 = add_triangulation(triangulations);
2485 if (!t2) return E_OUTOFMEMORY;
2486 MoveMemory(t + 1, t, (char*)(t2 + 1) - (char*)t);
2487 if (t->last_on_top) {
2488 t2 = t + 1;
2489 } else {
2490 t2 = t;
2491 t++;
2494 ZeroMemory(&t2->vertex_stack, sizeof(t2->vertex_stack));
2495 hr = add_vertex_index(&t2->vertex_stack, t->vertex_stack.items[t->vertex_stack.count - 1]);
2496 if (hr != S_OK) return hr;
2497 hr = add_vertex_index(&t2->vertex_stack, sweep_idx);
2498 if (hr != S_OK) return hr;
2499 t2->last_on_top = !t->last_on_top;
2501 hr = triangulation_add_point(&t, triangulations, sweep_idx, t->last_on_top);
2502 if (hr != S_OK) return hr;
2503 break;
2506 if (start >= end)
2508 struct triangulation *t;
2509 struct triangulation *t2 = add_triangulation(triangulations);
2510 if (!t2) return E_OUTOFMEMORY;
2511 t = &triangulations->items[start];
2512 MoveMemory(t + 1, t, (char*)(t2 + 1) - (char*)t);
2513 ZeroMemory(t, sizeof(*t));
2514 hr = add_vertex_index(&t->vertex_stack, sweep_idx);
2515 if (hr != S_OK) return hr;
2518 return S_OK;
2521 HRESULT WINAPI D3DXCreateTextW(LPDIRECT3DDEVICE9 device,
2522 HDC hdc, LPCWSTR text,
2523 FLOAT deviation, FLOAT extrusion,
2524 LPD3DXMESH *mesh_ptr, LPD3DXBUFFER *adjacency,
2525 LPGLYPHMETRICSFLOAT glyphmetrics)
2527 HRESULT hr;
2528 ID3DXMesh *mesh = NULL;
2529 DWORD nb_vertices, nb_faces;
2530 DWORD nb_front_faces, nb_corners, nb_outline_points;
2531 struct vertex *vertices = NULL;
2532 face *faces = NULL;
2533 int textlen = 0;
2534 float offset_x;
2535 LOGFONTW lf;
2536 OUTLINETEXTMETRICW otm;
2537 HFONT font = NULL, oldfont = NULL;
2538 const MAT2 identity = {{0, 1}, {0, 0}, {0, 0}, {0, 1}};
2539 void *raw_outline = NULL;
2540 int bufsize = 0;
2541 struct glyphinfo *glyphs = NULL;
2542 GLYPHMETRICS gm;
2543 struct triangulation_array triangulations = {0, 0, NULL};
2544 int i;
2545 struct vertex *vertex_ptr;
2546 face *face_ptr;
2547 float max_deviation_sq;
2548 const struct cos_table cos_table = {
2549 cos(D3DXToRadian(0.5f)),
2550 cos(D3DXToRadian(45.0f)),
2551 cos(D3DXToRadian(90.0f)),
2553 int f1, f2;
2555 TRACE("(%p, %p, %s, %f, %f, %p, %p, %p)\n", device, hdc,
2556 debugstr_w(text), deviation, extrusion, mesh_ptr, adjacency, glyphmetrics);
2558 if (!device || !hdc || !text || !*text || deviation < 0.0f || extrusion < 0.0f || !mesh_ptr)
2559 return D3DERR_INVALIDCALL;
2561 if (adjacency)
2563 FIXME("Case of adjacency != NULL not implemented.\n");
2564 return E_NOTIMPL;
2567 if (!GetObjectW(GetCurrentObject(hdc, OBJ_FONT), sizeof(lf), &lf) ||
2568 !GetOutlineTextMetricsW(hdc, sizeof(otm), &otm))
2570 return D3DERR_INVALIDCALL;
2573 if (deviation == 0.0f)
2574 deviation = 1.0f / otm.otmEMSquare;
2575 max_deviation_sq = deviation * deviation;
2577 lf.lfHeight = otm.otmEMSquare;
2578 lf.lfWidth = 0;
2579 font = CreateFontIndirectW(&lf);
2580 if (!font) {
2581 hr = E_OUTOFMEMORY;
2582 goto error;
2584 oldfont = SelectObject(hdc, font);
2586 textlen = strlenW(text);
2587 for (i = 0; i < textlen; i++)
2589 int datasize = GetGlyphOutlineW(hdc, text[i], GGO_NATIVE, &gm, 0, NULL, &identity);
2590 if (datasize < 0)
2591 return D3DERR_INVALIDCALL;
2592 if (bufsize < datasize)
2593 bufsize = datasize;
2595 if (!bufsize) { /* e.g. text == " " */
2596 hr = D3DERR_INVALIDCALL;
2597 goto error;
2600 glyphs = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, textlen * sizeof(*glyphs));
2601 raw_outline = HeapAlloc(GetProcessHeap(), 0, bufsize);
2602 if (!glyphs || !raw_outline) {
2603 hr = E_OUTOFMEMORY;
2604 goto error;
2607 offset_x = 0.0f;
2608 for (i = 0; i < textlen; i++)
2610 /* get outline points from data returned from GetGlyphOutline */
2611 int datasize;
2613 glyphs[i].offset_x = offset_x;
2615 datasize = GetGlyphOutlineW(hdc, text[i], GGO_NATIVE, &gm, bufsize, raw_outline, &identity);
2616 hr = create_outline(&glyphs[i], raw_outline, datasize,
2617 max_deviation_sq, otm.otmEMSquare, &cos_table);
2618 if (hr != S_OK) goto error;
2620 triangulations.glyph = &glyphs[i];
2621 hr = triangulate(&triangulations);
2622 if (hr != S_OK) goto error;
2623 if (triangulations.count) {
2624 ERR("%d incomplete triangulations of glyph (%u).\n", triangulations.count, text[i]);
2625 triangulations.count = 0;
2628 if (glyphmetrics)
2630 glyphmetrics[i].gmfBlackBoxX = gm.gmBlackBoxX / (float)otm.otmEMSquare;
2631 glyphmetrics[i].gmfBlackBoxY = gm.gmBlackBoxY / (float)otm.otmEMSquare;
2632 glyphmetrics[i].gmfptGlyphOrigin.x = gm.gmptGlyphOrigin.x / (float)otm.otmEMSquare;
2633 glyphmetrics[i].gmfptGlyphOrigin.y = gm.gmptGlyphOrigin.y / (float)otm.otmEMSquare;
2634 glyphmetrics[i].gmfCellIncX = gm.gmCellIncX / (float)otm.otmEMSquare;
2635 glyphmetrics[i].gmfCellIncY = gm.gmCellIncY / (float)otm.otmEMSquare;
2637 offset_x += gm.gmCellIncX / (float)otm.otmEMSquare;
2640 /* corner points need an extra vertex for the different side faces normals */
2641 nb_corners = 0;
2642 nb_outline_points = 0;
2643 nb_front_faces = 0;
2644 for (i = 0; i < textlen; i++)
2646 int j;
2647 nb_outline_points += glyphs[i].ordered_vertices.count;
2648 nb_front_faces += glyphs[i].faces.count;
2649 for (j = 0; j < glyphs[i].outlines.count; j++)
2651 int k;
2652 struct outline *outline = &glyphs[i].outlines.items[j];
2653 nb_corners++; /* first outline point always repeated as a corner */
2654 for (k = 1; k < outline->count; k++)
2655 if (outline->items[k].corner)
2656 nb_corners++;
2660 nb_vertices = (nb_outline_points + nb_corners) * 2 + nb_outline_points * 2;
2661 nb_faces = nb_outline_points * 2 + nb_front_faces * 2;
2664 hr = D3DXCreateMeshFVF(nb_faces, nb_vertices, D3DXMESH_MANAGED,
2665 D3DFVF_XYZ | D3DFVF_NORMAL, device, &mesh);
2666 if (FAILED(hr))
2667 goto error;
2669 hr = mesh->lpVtbl->LockVertexBuffer(mesh, D3DLOCK_DISCARD, (LPVOID *)&vertices);
2670 if (FAILED(hr))
2671 goto error;
2673 hr = mesh->lpVtbl->LockIndexBuffer(mesh, D3DLOCK_DISCARD, (LPVOID *)&faces);
2674 if (FAILED(hr))
2675 goto error;
2677 /* convert 2D vertices and faces into 3D mesh */
2678 vertex_ptr = vertices;
2679 face_ptr = faces;
2680 if (extrusion == 0.0f) {
2681 f1 = 1;
2682 f2 = 2;
2683 } else {
2684 f1 = 2;
2685 f2 = 1;
2687 for (i = 0; i < textlen; i++)
2689 int j;
2690 int count;
2691 struct vertex *back_vertices;
2692 face *back_faces;
2694 /* side vertices and faces */
2695 for (j = 0; j < glyphs[i].outlines.count; j++)
2697 struct vertex *outline_vertices = vertex_ptr;
2698 struct outline *outline = &glyphs[i].outlines.items[j];
2699 int k;
2700 struct point2d *prevpt = &outline->items[outline->count - 1];
2701 struct point2d *pt = &outline->items[0];
2703 for (k = 1; k <= outline->count; k++)
2705 struct vertex vtx;
2706 struct point2d *nextpt = &outline->items[k % outline->count];
2707 WORD vtx_idx = vertex_ptr - vertices;
2708 D3DXVECTOR2 vec;
2710 if (pt->corner == POINTTYPE_CURVE_START)
2711 D3DXVec2Subtract(&vec, &pt->pos, &prevpt->pos);
2712 else if (pt->corner)
2713 D3DXVec2Subtract(&vec, &nextpt->pos, &pt->pos);
2714 else
2715 D3DXVec2Subtract(&vec, &nextpt->pos, &prevpt->pos);
2716 D3DXVec2Normalize(&vec, &vec);
2717 vtx.normal.x = -vec.y;
2718 vtx.normal.y = vec.x;
2719 vtx.normal.z = 0;
2721 vtx.position.x = pt->pos.x + glyphs[i].offset_x;
2722 vtx.position.y = pt->pos.y;
2723 vtx.position.z = 0;
2724 *vertex_ptr++ = vtx;
2726 vtx.position.z = -extrusion;
2727 *vertex_ptr++ = vtx;
2729 vtx.position.x = nextpt->pos.x + glyphs[i].offset_x;
2730 vtx.position.y = nextpt->pos.y;
2731 if (pt->corner && nextpt->corner && nextpt->corner != POINTTYPE_CURVE_END) {
2732 vtx.position.z = -extrusion;
2733 *vertex_ptr++ = vtx;
2734 vtx.position.z = 0;
2735 *vertex_ptr++ = vtx;
2737 (*face_ptr)[0] = vtx_idx;
2738 (*face_ptr)[1] = vtx_idx + 2;
2739 (*face_ptr)[2] = vtx_idx + 1;
2740 face_ptr++;
2742 (*face_ptr)[0] = vtx_idx;
2743 (*face_ptr)[1] = vtx_idx + 3;
2744 (*face_ptr)[2] = vtx_idx + 2;
2745 face_ptr++;
2746 } else {
2747 if (nextpt->corner) {
2748 if (nextpt->corner == POINTTYPE_CURVE_END) {
2749 D3DXVECTOR2 *nextpt2 = &outline->items[(k + 1) % outline->count].pos;
2750 D3DXVec2Subtract(&vec, nextpt2, &nextpt->pos);
2751 } else {
2752 D3DXVec2Subtract(&vec, &nextpt->pos, &pt->pos);
2754 D3DXVec2Normalize(&vec, &vec);
2755 vtx.normal.x = -vec.y;
2756 vtx.normal.y = vec.x;
2758 vtx.position.z = 0;
2759 *vertex_ptr++ = vtx;
2760 vtx.position.z = -extrusion;
2761 *vertex_ptr++ = vtx;
2764 (*face_ptr)[0] = vtx_idx;
2765 (*face_ptr)[1] = vtx_idx + 3;
2766 (*face_ptr)[2] = vtx_idx + 1;
2767 face_ptr++;
2769 (*face_ptr)[0] = vtx_idx;
2770 (*face_ptr)[1] = vtx_idx + 2;
2771 (*face_ptr)[2] = vtx_idx + 3;
2772 face_ptr++;
2775 prevpt = pt;
2776 pt = nextpt;
2778 if (!pt->corner) {
2779 *vertex_ptr++ = *outline_vertices++;
2780 *vertex_ptr++ = *outline_vertices++;
2784 /* back vertices and faces */
2785 back_faces = face_ptr;
2786 back_vertices = vertex_ptr;
2787 for (j = 0; j < glyphs[i].ordered_vertices.count; j++)
2789 D3DXVECTOR2 *pt = get_ordered_vertex(&glyphs[i], j);
2790 vertex_ptr->position.x = pt->x + glyphs[i].offset_x;
2791 vertex_ptr->position.y = pt->y;
2792 vertex_ptr->position.z = 0;
2793 vertex_ptr->normal.x = 0;
2794 vertex_ptr->normal.y = 0;
2795 vertex_ptr->normal.z = 1;
2796 vertex_ptr++;
2798 count = back_vertices - vertices;
2799 for (j = 0; j < glyphs[i].faces.count; j++)
2801 face *f = &glyphs[i].faces.items[j];
2802 (*face_ptr)[0] = (*f)[0] + count;
2803 (*face_ptr)[1] = (*f)[1] + count;
2804 (*face_ptr)[2] = (*f)[2] + count;
2805 face_ptr++;
2808 /* front vertices and faces */
2809 j = count = vertex_ptr - back_vertices;
2810 while (j--)
2812 vertex_ptr->position.x = back_vertices->position.x;
2813 vertex_ptr->position.y = back_vertices->position.y;
2814 vertex_ptr->position.z = -extrusion;
2815 vertex_ptr->normal.x = 0;
2816 vertex_ptr->normal.y = 0;
2817 vertex_ptr->normal.z = extrusion == 0.0f ? 1.0f : -1.0f;
2818 vertex_ptr++;
2819 back_vertices++;
2821 j = face_ptr - back_faces;
2822 while (j--)
2824 (*face_ptr)[0] = (*back_faces)[0] + count;
2825 (*face_ptr)[1] = (*back_faces)[f1] + count;
2826 (*face_ptr)[2] = (*back_faces)[f2] + count;
2827 face_ptr++;
2828 back_faces++;
2832 *mesh_ptr = mesh;
2833 hr = D3D_OK;
2834 error:
2835 if (mesh) {
2836 if (faces) mesh->lpVtbl->UnlockIndexBuffer(mesh);
2837 if (vertices) mesh->lpVtbl->UnlockVertexBuffer(mesh);
2838 if (hr != D3D_OK) mesh->lpVtbl->Release(mesh);
2840 if (glyphs) {
2841 for (i = 0; i < textlen; i++)
2843 int j;
2844 for (j = 0; j < glyphs[i].outlines.count; j++)
2845 HeapFree(GetProcessHeap(), 0, glyphs[i].outlines.items[j].items);
2846 HeapFree(GetProcessHeap(), 0, glyphs[i].outlines.items);
2847 HeapFree(GetProcessHeap(), 0, glyphs[i].faces.items);
2848 HeapFree(GetProcessHeap(), 0, glyphs[i].ordered_vertices.items);
2850 HeapFree(GetProcessHeap(), 0, glyphs);
2852 if (triangulations.items) {
2853 int i;
2854 for (i = 0; i < triangulations.count; i++)
2855 HeapFree(GetProcessHeap(), 0, triangulations.items[i].vertex_stack.items);
2856 HeapFree(GetProcessHeap(), 0, triangulations.items);
2858 HeapFree(GetProcessHeap(), 0, raw_outline);
2859 if (oldfont) SelectObject(hdc, oldfont);
2860 if (font) DeleteObject(font);
2862 return hr;