d3dx9: Generate effect instances from materials for mesh loading.
[wine/multimedia.git] / dlls / d3dx9_36 / mesh.c
blob1a4964ea68f8d08879f9268017edfce1f7401b6c
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 <assert.h>
31 #include "windef.h"
32 #include "wingdi.h"
33 #include "d3dx9.h"
34 #undef MAKE_DDHRESULT
35 #include "dxfile.h"
36 #include "rmxfguid.h"
37 #include "rmxftmpl.h"
38 #include "wine/debug.h"
39 #include "wine/unicode.h"
40 #include "wine/list.h"
41 #include "d3dx9_36_private.h"
43 WINE_DEFAULT_DEBUG_CHANNEL(d3dx);
45 typedef struct ID3DXMeshImpl
47 ID3DXMesh ID3DXMesh_iface;
48 LONG ref;
50 DWORD numfaces;
51 DWORD numvertices;
52 DWORD options;
53 DWORD fvf;
54 IDirect3DDevice9 *device;
55 IDirect3DVertexDeclaration9 *vertex_declaration;
56 IDirect3DVertexBuffer9 *vertex_buffer;
57 IDirect3DIndexBuffer9 *index_buffer;
58 DWORD *attrib_buffer;
59 int attrib_buffer_lock_count;
60 DWORD attrib_table_size;
61 D3DXATTRIBUTERANGE *attrib_table;
62 } ID3DXMeshImpl;
64 static inline ID3DXMeshImpl *impl_from_ID3DXMesh(ID3DXMesh *iface)
66 return CONTAINING_RECORD(iface, ID3DXMeshImpl, ID3DXMesh_iface);
69 static HRESULT WINAPI ID3DXMeshImpl_QueryInterface(ID3DXMesh *iface, REFIID riid, LPVOID *object)
71 ID3DXMeshImpl *This = impl_from_ID3DXMesh(iface);
73 TRACE("(%p)->(%s, %p)\n", This, debugstr_guid(riid), object);
75 if (IsEqualGUID(riid, &IID_IUnknown) ||
76 IsEqualGUID(riid, &IID_ID3DXBaseMesh) ||
77 IsEqualGUID(riid, &IID_ID3DXMesh))
79 iface->lpVtbl->AddRef(iface);
80 *object = This;
81 return S_OK;
84 WARN("Interface %s not found.\n", debugstr_guid(riid));
86 return E_NOINTERFACE;
89 static ULONG WINAPI ID3DXMeshImpl_AddRef(ID3DXMesh *iface)
91 ID3DXMeshImpl *This = impl_from_ID3DXMesh(iface);
93 TRACE("(%p)->(): AddRef from %d\n", This, This->ref);
95 return InterlockedIncrement(&This->ref);
98 static ULONG WINAPI ID3DXMeshImpl_Release(ID3DXMesh *iface)
100 ID3DXMeshImpl *This = impl_from_ID3DXMesh(iface);
101 ULONG ref = InterlockedDecrement(&This->ref);
103 TRACE("(%p)->(): Release from %d\n", This, ref + 1);
105 if (!ref)
107 IDirect3DIndexBuffer9_Release(This->index_buffer);
108 IDirect3DVertexBuffer9_Release(This->vertex_buffer);
109 IDirect3DVertexDeclaration9_Release(This->vertex_declaration);
110 IDirect3DDevice9_Release(This->device);
111 HeapFree(GetProcessHeap(), 0, This->attrib_buffer);
112 HeapFree(GetProcessHeap(), 0, This->attrib_table);
113 HeapFree(GetProcessHeap(), 0, This);
116 return ref;
119 /*** ID3DXBaseMesh ***/
120 static HRESULT WINAPI ID3DXMeshImpl_DrawSubset(ID3DXMesh *iface, DWORD attrib_id)
122 ID3DXMeshImpl *This = impl_from_ID3DXMesh(iface);
123 HRESULT hr;
124 DWORD face_start;
125 DWORD face_end = 0;
126 DWORD vertex_size;
128 TRACE("(%p)->(%u)\n", This, attrib_id);
130 vertex_size = iface->lpVtbl->GetNumBytesPerVertex(iface);
132 hr = IDirect3DDevice9_SetVertexDeclaration(This->device, This->vertex_declaration);
133 if (FAILED(hr)) return hr;
134 hr = IDirect3DDevice9_SetStreamSource(This->device, 0, This->vertex_buffer, 0, vertex_size);
135 if (FAILED(hr)) return hr;
136 hr = IDirect3DDevice9_SetIndices(This->device, This->index_buffer);
137 if (FAILED(hr)) return hr;
139 while (face_end < This->numfaces)
141 for (face_start = face_end; face_start < This->numfaces; face_start++)
143 if (This->attrib_buffer[face_start] == attrib_id)
144 break;
146 if (face_start >= This->numfaces)
147 break;
148 for (face_end = face_start + 1; face_end < This->numfaces; face_end++)
150 if (This->attrib_buffer[face_end] != attrib_id)
151 break;
154 hr = IDirect3DDevice9_DrawIndexedPrimitive(This->device, D3DPT_TRIANGLELIST,
155 0, 0, This->numvertices, face_start * 3, face_end - face_start);
156 if (FAILED(hr)) return hr;
159 return D3D_OK;
162 static DWORD WINAPI ID3DXMeshImpl_GetNumFaces(ID3DXMesh *iface)
164 ID3DXMeshImpl *This = impl_from_ID3DXMesh(iface);
166 TRACE("(%p)\n", This);
168 return This->numfaces;
171 static DWORD WINAPI ID3DXMeshImpl_GetNumVertices(ID3DXMesh *iface)
173 ID3DXMeshImpl *This = impl_from_ID3DXMesh(iface);
175 TRACE("(%p)\n", This);
177 return This->numvertices;
180 static DWORD WINAPI ID3DXMeshImpl_GetFVF(ID3DXMesh *iface)
182 ID3DXMeshImpl *This = impl_from_ID3DXMesh(iface);
184 TRACE("(%p)\n", This);
186 return This->fvf;
189 static HRESULT WINAPI ID3DXMeshImpl_GetDeclaration(ID3DXMesh *iface, D3DVERTEXELEMENT9 declaration[MAX_FVF_DECL_SIZE])
191 ID3DXMeshImpl *This = impl_from_ID3DXMesh(iface);
192 UINT numelements;
194 TRACE("(%p)\n", This);
196 if (declaration == NULL) return D3DERR_INVALIDCALL;
198 return IDirect3DVertexDeclaration9_GetDeclaration(This->vertex_declaration,
199 declaration,
200 &numelements);
203 static DWORD WINAPI ID3DXMeshImpl_GetNumBytesPerVertex(ID3DXMesh *iface)
205 ID3DXMeshImpl *This = impl_from_ID3DXMesh(iface);
206 UINT numelements;
207 D3DVERTEXELEMENT9 declaration[MAX_FVF_DECL_SIZE] = { D3DDECL_END() };
209 TRACE("iface (%p)\n", This);
211 IDirect3DVertexDeclaration9_GetDeclaration(This->vertex_declaration,
212 declaration,
213 &numelements);
214 return D3DXGetDeclVertexSize(declaration, 0);
217 static DWORD WINAPI ID3DXMeshImpl_GetOptions(ID3DXMesh *iface)
219 ID3DXMeshImpl *This = impl_from_ID3DXMesh(iface);
221 TRACE("(%p)\n", This);
223 return This->options;
226 static HRESULT WINAPI ID3DXMeshImpl_GetDevice(ID3DXMesh *iface, LPDIRECT3DDEVICE9 *device)
228 ID3DXMeshImpl *This = impl_from_ID3DXMesh(iface);
230 TRACE("(%p)->(%p)\n", This, device);
232 if (device == NULL) return D3DERR_INVALIDCALL;
233 *device = This->device;
234 IDirect3DDevice9_AddRef(This->device);
236 return D3D_OK;
239 static HRESULT WINAPI ID3DXMeshImpl_CloneMeshFVF(ID3DXMesh *iface, DWORD options, DWORD fvf, LPDIRECT3DDEVICE9 device, LPD3DXMESH *clone_mesh)
241 ID3DXMeshImpl *This = impl_from_ID3DXMesh(iface);
242 HRESULT hr;
243 D3DVERTEXELEMENT9 declaration[MAX_FVF_DECL_SIZE];
245 TRACE("(%p)->(%x,%x,%p,%p)\n", This, options, fvf, device, clone_mesh);
247 hr = D3DXDeclaratorFromFVF(fvf, declaration);
248 if (FAILED(hr)) return hr;
250 return iface->lpVtbl->CloneMesh(iface, options, declaration, device, clone_mesh);
253 static HRESULT WINAPI ID3DXMeshImpl_CloneMesh(ID3DXMesh *iface, DWORD options, CONST D3DVERTEXELEMENT9 *declaration, LPDIRECT3DDEVICE9 device,
254 LPD3DXMESH *clone_mesh_out)
256 ID3DXMeshImpl *This = impl_from_ID3DXMesh(iface);
257 ID3DXMeshImpl *cloned_this;
258 ID3DXMesh *clone_mesh;
259 D3DVERTEXELEMENT9 orig_declaration[MAX_FVF_DECL_SIZE] = { D3DDECL_END() };
260 void *data_in, *data_out;
261 DWORD vertex_size;
262 HRESULT hr;
263 int i;
265 TRACE("(%p)->(%x,%p,%p,%p)\n", This, options, declaration, device, clone_mesh_out);
267 if (!clone_mesh_out)
268 return D3DERR_INVALIDCALL;
270 hr = iface->lpVtbl->GetDeclaration(iface, orig_declaration);
271 if (FAILED(hr)) return hr;
273 for (i = 0; orig_declaration[i].Stream != 0xff; i++) {
274 if (memcmp(&orig_declaration[i], &declaration[i], sizeof(*declaration)))
276 FIXME("Vertex buffer conversion not implemented.\n");
277 return E_NOTIMPL;
281 hr = D3DXCreateMesh(This->numfaces, This->numvertices, options & ~D3DXMESH_VB_SHARE,
282 declaration, device, &clone_mesh);
283 if (FAILED(hr)) return hr;
285 cloned_this = impl_from_ID3DXMesh(clone_mesh);
286 vertex_size = clone_mesh->lpVtbl->GetNumBytesPerVertex(clone_mesh);
288 if (options & D3DXMESH_VB_SHARE) {
289 IDirect3DVertexBuffer9_AddRef(This->vertex_buffer);
290 /* FIXME: refactor to avoid creating a new vertex buffer */
291 IDirect3DVertexBuffer9_Release(cloned_this->vertex_buffer);
292 cloned_this->vertex_buffer = This->vertex_buffer;
293 } else {
294 hr = iface->lpVtbl->LockVertexBuffer(iface, D3DLOCK_READONLY, &data_in);
295 if (FAILED(hr)) goto error;
296 hr = clone_mesh->lpVtbl->LockVertexBuffer(clone_mesh, D3DLOCK_DISCARD, &data_out);
297 if (FAILED(hr)) {
298 iface->lpVtbl->UnlockVertexBuffer(iface);
299 goto error;
301 memcpy(data_out, data_in, This->numvertices * vertex_size);
302 clone_mesh->lpVtbl->UnlockVertexBuffer(clone_mesh);
303 iface->lpVtbl->UnlockVertexBuffer(iface);
306 hr = iface->lpVtbl->LockIndexBuffer(iface, D3DLOCK_READONLY, &data_in);
307 if (FAILED(hr)) goto error;
308 hr = clone_mesh->lpVtbl->LockIndexBuffer(clone_mesh, D3DLOCK_DISCARD, &data_out);
309 if (FAILED(hr)) {
310 iface->lpVtbl->UnlockIndexBuffer(iface);
311 goto error;
313 if ((options ^ This->options) & D3DXMESH_32BIT) {
314 if (options & D3DXMESH_32BIT) {
315 for (i = 0; i < This->numfaces * 3; i++)
316 ((DWORD*)data_out)[i] = ((WORD*)data_in)[i];
317 } else {
318 for (i = 0; i < This->numfaces * 3; i++)
319 ((WORD*)data_out)[i] = ((DWORD*)data_in)[i];
321 } else {
322 memcpy(data_out, data_in, This->numfaces * 3 * (options & D3DXMESH_32BIT ? 4 : 2));
324 clone_mesh->lpVtbl->UnlockIndexBuffer(clone_mesh);
325 iface->lpVtbl->UnlockIndexBuffer(iface);
327 memcpy(cloned_this->attrib_buffer, This->attrib_buffer, This->numfaces * sizeof(*This->attrib_buffer));
329 if (This->attrib_table_size)
331 cloned_this->attrib_table_size = This->attrib_table_size;
332 cloned_this->attrib_table = HeapAlloc(GetProcessHeap(), 0, This->attrib_table_size * sizeof(*This->attrib_table));
333 if (!cloned_this->attrib_table) {
334 hr = E_OUTOFMEMORY;
335 goto error;
337 memcpy(cloned_this->attrib_table, This->attrib_table, This->attrib_table_size * sizeof(*This->attrib_table));
340 *clone_mesh_out = clone_mesh;
342 return D3D_OK;
343 error:
344 IUnknown_Release(clone_mesh);
345 return hr;
348 static HRESULT WINAPI ID3DXMeshImpl_GetVertexBuffer(ID3DXMesh *iface, LPDIRECT3DVERTEXBUFFER9 *vertex_buffer)
350 ID3DXMeshImpl *This = impl_from_ID3DXMesh(iface);
352 TRACE("(%p)->(%p)\n", This, vertex_buffer);
354 if (vertex_buffer == NULL) return D3DERR_INVALIDCALL;
355 *vertex_buffer = This->vertex_buffer;
356 IDirect3DVertexBuffer9_AddRef(This->vertex_buffer);
358 return D3D_OK;
361 static HRESULT WINAPI ID3DXMeshImpl_GetIndexBuffer(ID3DXMesh *iface, LPDIRECT3DINDEXBUFFER9 *index_buffer)
363 ID3DXMeshImpl *This = impl_from_ID3DXMesh(iface);
365 TRACE("(%p)->(%p)\n", This, index_buffer);
367 if (index_buffer == NULL) return D3DERR_INVALIDCALL;
368 *index_buffer = This->index_buffer;
369 IDirect3DIndexBuffer9_AddRef(This->index_buffer);
371 return D3D_OK;
374 static HRESULT WINAPI ID3DXMeshImpl_LockVertexBuffer(ID3DXMesh *iface, DWORD flags, LPVOID *data)
376 ID3DXMeshImpl *This = impl_from_ID3DXMesh(iface);
378 TRACE("(%p)->(%u,%p)\n", This, flags, data);
380 return IDirect3DVertexBuffer9_Lock(This->vertex_buffer, 0, 0, data, flags);
383 static HRESULT WINAPI ID3DXMeshImpl_UnlockVertexBuffer(ID3DXMesh *iface)
385 ID3DXMeshImpl *This = impl_from_ID3DXMesh(iface);
387 TRACE("(%p)\n", This);
389 return IDirect3DVertexBuffer9_Unlock(This->vertex_buffer);
392 static HRESULT WINAPI ID3DXMeshImpl_LockIndexBuffer(ID3DXMesh *iface, DWORD flags, LPVOID *data)
394 ID3DXMeshImpl *This = impl_from_ID3DXMesh(iface);
396 TRACE("(%p)->(%u,%p)\n", This, flags, data);
398 return IDirect3DIndexBuffer9_Lock(This->index_buffer, 0, 0, data, flags);
401 static HRESULT WINAPI ID3DXMeshImpl_UnlockIndexBuffer(ID3DXMesh *iface)
403 ID3DXMeshImpl *This = impl_from_ID3DXMesh(iface);
405 TRACE("(%p)\n", This);
407 return IDirect3DIndexBuffer9_Unlock(This->index_buffer);
410 static HRESULT WINAPI ID3DXMeshImpl_GetAttributeTable(ID3DXMesh *iface, D3DXATTRIBUTERANGE *attrib_table, DWORD *attrib_table_size)
412 ID3DXMeshImpl *This = impl_from_ID3DXMesh(iface);
414 TRACE("(%p)->(%p,%p)\n", This, attrib_table, attrib_table_size);
416 if (attrib_table_size)
417 *attrib_table_size = This->attrib_table_size;
419 if (attrib_table)
420 CopyMemory(attrib_table, This->attrib_table, This->attrib_table_size * sizeof(*attrib_table));
422 return D3D_OK;
425 static HRESULT WINAPI ID3DXMeshImpl_ConvertPointRepsToAdjacency(ID3DXMesh *iface, CONST DWORD *point_reps, DWORD *adjacency)
427 ID3DXMeshImpl *This = impl_from_ID3DXMesh(iface);
429 FIXME("(%p)->(%p,%p): stub\n", This, point_reps, adjacency);
431 return E_NOTIMPL;
434 static HRESULT WINAPI ID3DXMeshImpl_ConvertAdjacencyToPointReps(ID3DXMesh *iface, CONST DWORD *adjacency, DWORD *point_reps)
436 ID3DXMeshImpl *This = impl_from_ID3DXMesh(iface);
438 FIXME("(%p)->(%p,%p): stub\n", This, adjacency, point_reps);
440 return E_NOTIMPL;
443 struct vertex_metadata {
444 float key;
445 DWORD vertex_index;
446 DWORD first_shared_index;
449 static int compare_vertex_keys(const void *a, const void *b)
451 const struct vertex_metadata *left = a;
452 const struct vertex_metadata *right = b;
453 if (left->key == right->key)
454 return 0;
455 return left->key < right->key ? -1 : 1;
458 static HRESULT WINAPI ID3DXMeshImpl_GenerateAdjacency(ID3DXMesh *iface, FLOAT epsilon, DWORD *adjacency)
460 ID3DXMeshImpl *This = impl_from_ID3DXMesh(iface);
461 HRESULT hr;
462 BYTE *vertices = NULL;
463 const DWORD *indices = NULL;
464 DWORD vertex_size;
465 DWORD buffer_size;
466 /* sort the vertices by (x + y + z) to quickly find coincident vertices */
467 struct vertex_metadata *sorted_vertices;
468 /* shared_indices links together identical indices in the index buffer so
469 * that adjacency checks can be limited to faces sharing a vertex */
470 DWORD *shared_indices = NULL;
471 const FLOAT epsilon_sq = epsilon * epsilon;
472 int i;
474 TRACE("(%p)->(%f,%p)\n", This, epsilon, adjacency);
476 if (!adjacency)
477 return D3DERR_INVALIDCALL;
479 buffer_size = This->numfaces * 3 * sizeof(*shared_indices) + This->numvertices * sizeof(*sorted_vertices);
480 if (!(This->options & D3DXMESH_32BIT))
481 buffer_size += This->numfaces * 3 * sizeof(*indices);
482 shared_indices = HeapAlloc(GetProcessHeap(), 0, buffer_size);
483 if (!shared_indices)
484 return E_OUTOFMEMORY;
485 sorted_vertices = (struct vertex_metadata*)(shared_indices + This->numfaces * 3);
487 hr = iface->lpVtbl->LockVertexBuffer(iface, D3DLOCK_READONLY, (void**)&vertices);
488 if (FAILED(hr)) goto cleanup;
489 hr = iface->lpVtbl->LockIndexBuffer(iface, D3DLOCK_READONLY, (void**)&indices);
490 if (FAILED(hr)) goto cleanup;
492 if (!(This->options & D3DXMESH_32BIT)) {
493 const WORD *word_indices = (const WORD*)indices;
494 DWORD *dword_indices = (DWORD*)(sorted_vertices + This->numvertices);
495 indices = dword_indices;
496 for (i = 0; i < This->numfaces * 3; i++)
497 *dword_indices++ = *word_indices++;
500 vertex_size = iface->lpVtbl->GetNumBytesPerVertex(iface);
501 for (i = 0; i < This->numvertices; i++) {
502 D3DXVECTOR3 *vertex = (D3DXVECTOR3*)(vertices + vertex_size * i);
503 sorted_vertices[i].first_shared_index = -1;
504 sorted_vertices[i].key = vertex->x + vertex->y + vertex->z;
505 sorted_vertices[i].vertex_index = i;
507 for (i = 0; i < This->numfaces * 3; i++) {
508 DWORD *first_shared_index = &sorted_vertices[indices[i]].first_shared_index;
509 shared_indices[i] = *first_shared_index;
510 *first_shared_index = i;
511 adjacency[i] = -1;
513 qsort(sorted_vertices, This->numvertices, sizeof(*sorted_vertices), compare_vertex_keys);
515 for (i = 0; i < This->numvertices; i++) {
516 struct vertex_metadata *sorted_vertex_a = &sorted_vertices[i];
517 D3DXVECTOR3 *vertex_a = (D3DXVECTOR3*)(vertices + sorted_vertex_a->vertex_index * vertex_size);
518 DWORD shared_index_a = sorted_vertex_a->first_shared_index;
520 while (shared_index_a != -1) {
521 int j = i;
522 DWORD shared_index_b = shared_indices[shared_index_a];
523 struct vertex_metadata *sorted_vertex_b = sorted_vertex_a;
525 while (TRUE) {
526 while (shared_index_b != -1) {
527 /* faces are adjacent if they have another coincident vertex */
528 DWORD base_a = (shared_index_a / 3) * 3;
529 DWORD base_b = (shared_index_b / 3) * 3;
530 BOOL adjacent = FALSE;
531 int k;
533 for (k = 0; k < 3; k++) {
534 if (adjacency[base_b + k] == shared_index_a / 3) {
535 adjacent = TRUE;
536 break;
539 if (!adjacent) {
540 for (k = 1; k <= 2; k++) {
541 DWORD vertex_index_a = base_a + (shared_index_a + k) % 3;
542 DWORD vertex_index_b = base_b + (shared_index_b + (3 - k)) % 3;
543 adjacent = indices[vertex_index_a] == indices[vertex_index_b];
544 if (!adjacent && epsilon >= 0.0f) {
545 D3DXVECTOR3 delta = {0.0f, 0.0f, 0.0f};
546 FLOAT length_sq;
548 D3DXVec3Subtract(&delta,
549 (D3DXVECTOR3*)(vertices + indices[vertex_index_a] * vertex_size),
550 (D3DXVECTOR3*)(vertices + indices[vertex_index_b] * vertex_size));
551 length_sq = D3DXVec3LengthSq(&delta);
552 adjacent = epsilon == 0.0f ? length_sq == 0.0f : length_sq < epsilon_sq;
554 if (adjacent) {
555 DWORD adj_a = base_a + 2 - (vertex_index_a + shared_index_a + 1) % 3;
556 DWORD adj_b = base_b + 2 - (vertex_index_b + shared_index_b + 1) % 3;
557 if (adjacency[adj_a] == -1 && adjacency[adj_b] == -1) {
558 adjacency[adj_a] = base_b / 3;
559 adjacency[adj_b] = base_a / 3;
560 break;
566 shared_index_b = shared_indices[shared_index_b];
568 while (++j < This->numvertices) {
569 D3DXVECTOR3 *vertex_b;
571 sorted_vertex_b++;
572 if (sorted_vertex_b->key - sorted_vertex_a->key > epsilon * 3.0f) {
573 /* no more coincident vertices to try */
574 j = This->numvertices;
575 break;
577 /* check for coincidence */
578 vertex_b = (D3DXVECTOR3*)(vertices + sorted_vertex_b->vertex_index * vertex_size);
579 if (fabsf(vertex_a->x - vertex_b->x) <= epsilon &&
580 fabsf(vertex_a->y - vertex_b->y) <= epsilon &&
581 fabsf(vertex_a->z - vertex_b->z) <= epsilon)
583 break;
586 if (j >= This->numvertices)
587 break;
588 shared_index_b = sorted_vertex_b->first_shared_index;
591 sorted_vertex_a->first_shared_index = shared_indices[sorted_vertex_a->first_shared_index];
592 shared_index_a = sorted_vertex_a->first_shared_index;
596 hr = D3D_OK;
597 cleanup:
598 if (indices) iface->lpVtbl->UnlockIndexBuffer(iface);
599 if (vertices) iface->lpVtbl->UnlockVertexBuffer(iface);
600 HeapFree(GetProcessHeap(), 0, shared_indices);
601 return hr;
604 static HRESULT WINAPI ID3DXMeshImpl_UpdateSemantics(ID3DXMesh *iface, D3DVERTEXELEMENT9 declaration[MAX_FVF_DECL_SIZE])
606 ID3DXMeshImpl *This = impl_from_ID3DXMesh(iface);
608 FIXME("(%p)->(%p): stub\n", This, declaration);
610 return E_NOTIMPL;
613 /*** ID3DXMesh ***/
614 static HRESULT WINAPI ID3DXMeshImpl_LockAttributeBuffer(ID3DXMesh *iface, DWORD flags, DWORD **data)
616 ID3DXMeshImpl *This = impl_from_ID3DXMesh(iface);
618 TRACE("(%p)->(%u,%p)\n", This, flags, data);
620 InterlockedIncrement(&This->attrib_buffer_lock_count);
622 if (!(flags & D3DLOCK_READONLY)) {
623 D3DXATTRIBUTERANGE *attrib_table = This->attrib_table;
624 This->attrib_table_size = 0;
625 This->attrib_table = NULL;
626 HeapFree(GetProcessHeap(), 0, attrib_table);
629 *data = This->attrib_buffer;
631 return D3D_OK;
634 static HRESULT WINAPI ID3DXMeshImpl_UnlockAttributeBuffer(ID3DXMesh *iface)
636 ID3DXMeshImpl *This = impl_from_ID3DXMesh(iface);
637 int lock_count;
639 TRACE("(%p)\n", This);
641 lock_count = InterlockedDecrement(&This->attrib_buffer_lock_count);
643 if (lock_count < 0) {
644 InterlockedIncrement(&This->attrib_buffer_lock_count);
645 return D3DERR_INVALIDCALL;
648 return D3D_OK;
651 static HRESULT WINAPI ID3DXMeshImpl_Optimize(ID3DXMesh *iface, DWORD flags, CONST DWORD *adjacency_in, DWORD *adjacency_out,
652 DWORD *face_remap, LPD3DXBUFFER *vertex_remap, LPD3DXMESH *opt_mesh)
654 ID3DXMeshImpl *This = impl_from_ID3DXMesh(iface);
655 HRESULT hr;
656 D3DVERTEXELEMENT9 declaration[MAX_FVF_DECL_SIZE] = { D3DDECL_END() };
657 ID3DXMesh *optimized_mesh;
659 TRACE("(%p)->(%x,%p,%p,%p,%p,%p)\n", This, flags, adjacency_in, adjacency_out, face_remap, vertex_remap, opt_mesh);
661 if (!opt_mesh)
662 return D3DERR_INVALIDCALL;
664 hr = iface->lpVtbl->GetDeclaration(iface, declaration);
665 if (FAILED(hr)) return hr;
667 hr = iface->lpVtbl->CloneMesh(iface, This->options, declaration, This->device, &optimized_mesh);
668 if (FAILED(hr)) return hr;
670 hr = optimized_mesh->lpVtbl->OptimizeInplace(optimized_mesh, flags, adjacency_in, adjacency_out, face_remap, vertex_remap);
671 if (SUCCEEDED(hr))
672 *opt_mesh = optimized_mesh;
673 else
674 IUnknown_Release(optimized_mesh);
675 return hr;
678 /* Creates a vertex_remap that removes unused vertices.
679 * Indices are updated according to the vertex_remap. */
680 static HRESULT compact_mesh(ID3DXMeshImpl *This, DWORD *indices, DWORD *new_num_vertices, ID3DXBuffer **vertex_remap)
682 HRESULT hr;
683 DWORD *vertex_remap_ptr;
684 DWORD num_used_vertices;
685 DWORD i;
687 hr = D3DXCreateBuffer(This->numvertices * sizeof(DWORD), vertex_remap);
688 if (FAILED(hr)) return hr;
689 vertex_remap_ptr = ID3DXBuffer_GetBufferPointer(*vertex_remap);
691 for (i = 0; i < This->numfaces * 3; i++)
692 vertex_remap_ptr[indices[i]] = 1;
694 /* create old->new vertex mapping */
695 num_used_vertices = 0;
696 for (i = 0; i < This->numvertices; i++) {
697 if (vertex_remap_ptr[i])
698 vertex_remap_ptr[i] = num_used_vertices++;
699 else
700 vertex_remap_ptr[i] = -1;
702 /* convert indices */
703 for (i = 0; i < This->numfaces * 3; i++)
704 indices[i] = vertex_remap_ptr[indices[i]];
706 /* create new->old vertex mapping */
707 num_used_vertices = 0;
708 for (i = 0; i < This->numvertices; i++) {
709 if (vertex_remap_ptr[i] != -1)
710 vertex_remap_ptr[num_used_vertices++] = i;
712 for (i = num_used_vertices; i < This->numvertices; i++)
713 vertex_remap_ptr[i] = -1;
715 *new_num_vertices = num_used_vertices;
717 return D3D_OK;
720 /* count the number of unique attribute values in a sorted attribute buffer */
721 static DWORD count_attributes(const DWORD *attrib_buffer, DWORD numfaces)
723 DWORD last_attribute = attrib_buffer[0];
724 DWORD attrib_table_size = 1;
725 DWORD i;
726 for (i = 1; i < numfaces; i++) {
727 if (attrib_buffer[i] != last_attribute) {
728 last_attribute = attrib_buffer[i];
729 attrib_table_size++;
732 return attrib_table_size;
735 static void fill_attribute_table(DWORD *attrib_buffer, DWORD numfaces, void *indices,
736 BOOL is_32bit_indices, D3DXATTRIBUTERANGE *attrib_table)
738 DWORD attrib_table_size = 0;
739 DWORD last_attribute = attrib_buffer[0];
740 DWORD min_vertex, max_vertex;
741 DWORD i;
743 attrib_table[0].AttribId = last_attribute;
744 attrib_table[0].FaceStart = 0;
745 min_vertex = (DWORD)-1;
746 max_vertex = 0;
747 for (i = 0; i < numfaces; i++) {
748 DWORD j;
750 if (attrib_buffer[i] != last_attribute) {
751 last_attribute = attrib_buffer[i];
752 attrib_table[attrib_table_size].FaceCount = i - attrib_table[attrib_table_size].FaceStart;
753 attrib_table[attrib_table_size].VertexStart = min_vertex;
754 attrib_table[attrib_table_size].VertexCount = max_vertex - min_vertex + 1;
755 attrib_table_size++;
756 attrib_table[attrib_table_size].AttribId = attrib_buffer[i];
757 attrib_table[attrib_table_size].FaceStart = i;
758 min_vertex = (DWORD)-1;
759 max_vertex = 0;
761 for (j = 0; j < 3; j++) {
762 DWORD vertex_index = is_32bit_indices ? ((DWORD*)indices)[i * 3 + j] : ((WORD*)indices)[i * 3 + j];
763 if (vertex_index < min_vertex)
764 min_vertex = vertex_index;
765 if (vertex_index > max_vertex)
766 max_vertex = vertex_index;
769 attrib_table[attrib_table_size].FaceCount = i - attrib_table[attrib_table_size].FaceStart;
770 attrib_table[attrib_table_size].VertexStart = min_vertex;
771 attrib_table[attrib_table_size].VertexCount = max_vertex - min_vertex + 1;
772 attrib_table_size++;
775 static int attrib_entry_compare(const DWORD **a, const DWORD **b)
777 const DWORD *ptr_a = *a;
778 const DWORD *ptr_b = *b;
779 int delta = *ptr_a - *ptr_b;
781 if (delta)
782 return delta;
784 delta = ptr_a - ptr_b; /* for stable sort */
785 return delta;
788 /* Create face_remap, a new attribute buffer for attribute sort optimization. */
789 static HRESULT remap_faces_for_attrsort(ID3DXMeshImpl *This, const DWORD *indices,
790 const DWORD *attrib_buffer, DWORD **sorted_attrib_buffer, DWORD **face_remap)
792 const DWORD **sorted_attrib_ptr_buffer = NULL;
793 DWORD i;
795 *face_remap = HeapAlloc(GetProcessHeap(), 0, This->numfaces * sizeof(**face_remap));
796 sorted_attrib_ptr_buffer = HeapAlloc(GetProcessHeap(), 0, This->numfaces * sizeof(*sorted_attrib_ptr_buffer));
797 if (!*face_remap || !sorted_attrib_ptr_buffer) {
798 HeapFree(GetProcessHeap(), 0, sorted_attrib_ptr_buffer);
799 return E_OUTOFMEMORY;
801 for (i = 0; i < This->numfaces; i++)
802 sorted_attrib_ptr_buffer[i] = &attrib_buffer[i];
803 qsort(sorted_attrib_ptr_buffer, This->numfaces, sizeof(*sorted_attrib_ptr_buffer),
804 (int(*)(const void *, const void *))attrib_entry_compare);
806 for (i = 0; i < This->numfaces; i++)
808 DWORD old_face = sorted_attrib_ptr_buffer[i] - attrib_buffer;
809 (*face_remap)[old_face] = i;
812 /* overwrite sorted_attrib_ptr_buffer with the values themselves */
813 *sorted_attrib_buffer = (DWORD*)sorted_attrib_ptr_buffer;
814 for (i = 0; i < This->numfaces; i++)
815 (*sorted_attrib_buffer)[(*face_remap)[i]] = attrib_buffer[i];
817 return D3D_OK;
820 static HRESULT WINAPI ID3DXMeshImpl_OptimizeInplace(ID3DXMesh *iface, DWORD flags, CONST DWORD *adjacency_in, DWORD *adjacency_out,
821 DWORD *face_remap_out, LPD3DXBUFFER *vertex_remap_out)
823 ID3DXMeshImpl *This = impl_from_ID3DXMesh(iface);
824 void *indices = NULL;
825 DWORD *attrib_buffer = NULL;
826 HRESULT hr;
827 ID3DXBuffer *vertex_remap = NULL;
828 DWORD *face_remap = NULL; /* old -> new mapping */
829 DWORD *dword_indices = NULL;
830 DWORD new_num_vertices = 0;
831 DWORD new_num_alloc_vertices = 0;
832 IDirect3DVertexBuffer9 *vertex_buffer = NULL;
833 DWORD *sorted_attrib_buffer = NULL;
834 DWORD i;
836 TRACE("(%p)->(%x,%p,%p,%p,%p)\n", This, flags, adjacency_in, adjacency_out, face_remap_out, vertex_remap_out);
838 if (!flags)
839 return D3DERR_INVALIDCALL;
840 if (!adjacency_in && (flags & (D3DXMESHOPT_VERTEXCACHE | D3DXMESHOPT_STRIPREORDER)))
841 return D3DERR_INVALIDCALL;
842 if ((flags & (D3DXMESHOPT_VERTEXCACHE | D3DXMESHOPT_STRIPREORDER)) == (D3DXMESHOPT_VERTEXCACHE | D3DXMESHOPT_STRIPREORDER))
843 return D3DERR_INVALIDCALL;
845 if (flags & (D3DXMESHOPT_VERTEXCACHE | D3DXMESHOPT_STRIPREORDER))
847 if (flags & D3DXMESHOPT_VERTEXCACHE)
848 FIXME("D3DXMESHOPT_VERTEXCACHE not implemented.\n");
849 if (flags & D3DXMESHOPT_STRIPREORDER)
850 FIXME("D3DXMESHOPT_STRIPREORDER not implemented.\n");
851 return E_NOTIMPL;
854 hr = iface->lpVtbl->LockIndexBuffer(iface, 0, (void**)&indices);
855 if (FAILED(hr)) goto cleanup;
857 dword_indices = HeapAlloc(GetProcessHeap(), 0, This->numfaces * 3 * sizeof(DWORD));
858 if (!dword_indices) return E_OUTOFMEMORY;
859 if (This->options & D3DXMESH_32BIT) {
860 memcpy(dword_indices, indices, This->numfaces * 3 * sizeof(DWORD));
861 } else {
862 WORD *word_indices = indices;
863 for (i = 0; i < This->numfaces * 3; i++)
864 dword_indices[i] = *word_indices++;
867 if ((flags & (D3DXMESHOPT_COMPACT | D3DXMESHOPT_IGNOREVERTS | D3DXMESHOPT_ATTRSORT)) == D3DXMESHOPT_COMPACT)
869 new_num_alloc_vertices = This->numvertices;
870 hr = compact_mesh(This, dword_indices, &new_num_vertices, &vertex_remap);
871 if (FAILED(hr)) goto cleanup;
872 } else if (flags & D3DXMESHOPT_ATTRSORT) {
873 if (!(flags & D3DXMESHOPT_IGNOREVERTS))
875 FIXME("D3DXMESHOPT_ATTRSORT vertex reordering not implemented.\n");
876 hr = E_NOTIMPL;
877 goto cleanup;
880 hr = iface->lpVtbl->LockAttributeBuffer(iface, 0, &attrib_buffer);
881 if (FAILED(hr)) goto cleanup;
883 hr = remap_faces_for_attrsort(This, dword_indices, attrib_buffer, &sorted_attrib_buffer, &face_remap);
884 if (FAILED(hr)) goto cleanup;
887 if (vertex_remap)
889 /* reorder the vertices using vertex_remap */
890 D3DVERTEXBUFFER_DESC vertex_desc;
891 DWORD *vertex_remap_ptr = ID3DXBuffer_GetBufferPointer(vertex_remap);
892 DWORD vertex_size = iface->lpVtbl->GetNumBytesPerVertex(iface);
893 BYTE *orig_vertices;
894 BYTE *new_vertices;
896 hr = IDirect3DVertexBuffer9_GetDesc(This->vertex_buffer, &vertex_desc);
897 if (FAILED(hr)) goto cleanup;
899 hr = IDirect3DDevice9_CreateVertexBuffer(This->device, new_num_alloc_vertices * vertex_size,
900 vertex_desc.Usage, This->fvf, vertex_desc.Pool, &vertex_buffer, NULL);
901 if (FAILED(hr)) goto cleanup;
903 hr = IDirect3DVertexBuffer9_Lock(This->vertex_buffer, 0, 0, (void**)&orig_vertices, D3DLOCK_READONLY);
904 if (FAILED(hr)) goto cleanup;
906 hr = IDirect3DVertexBuffer9_Lock(vertex_buffer, 0, 0, (void**)&new_vertices, D3DLOCK_DISCARD);
907 if (FAILED(hr)) {
908 IDirect3DVertexBuffer9_Unlock(This->vertex_buffer);
909 goto cleanup;
912 for (i = 0; i < new_num_vertices; i++)
913 memcpy(new_vertices + i * vertex_size, orig_vertices + vertex_remap_ptr[i] * vertex_size, vertex_size);
915 IDirect3DVertexBuffer9_Unlock(This->vertex_buffer);
916 IDirect3DVertexBuffer9_Unlock(vertex_buffer);
917 } else if (vertex_remap_out) {
918 DWORD *vertex_remap_ptr;
920 hr = D3DXCreateBuffer(This->numvertices * sizeof(DWORD), &vertex_remap);
921 if (FAILED(hr)) goto cleanup;
922 vertex_remap_ptr = ID3DXBuffer_GetBufferPointer(vertex_remap);
923 for (i = 0; i < This->numvertices; i++)
924 *vertex_remap_ptr++ = i;
927 if (flags & D3DXMESHOPT_ATTRSORT)
929 D3DXATTRIBUTERANGE *attrib_table;
930 DWORD attrib_table_size;
932 attrib_table_size = count_attributes(sorted_attrib_buffer, This->numfaces);
933 attrib_table = HeapAlloc(GetProcessHeap(), 0, attrib_table_size * sizeof(*attrib_table));
934 if (!attrib_table) {
935 hr = E_OUTOFMEMORY;
936 goto cleanup;
939 memcpy(attrib_buffer, sorted_attrib_buffer, This->numfaces * sizeof(*attrib_buffer));
941 /* reorder the indices using face_remap */
942 if (This->options & D3DXMESH_32BIT) {
943 for (i = 0; i < This->numfaces; i++)
944 memcpy((DWORD*)indices + face_remap[i] * 3, dword_indices + i * 3, 3 * sizeof(DWORD));
945 } else {
946 WORD *word_indices = indices;
947 for (i = 0; i < This->numfaces; i++) {
948 DWORD new_pos = face_remap[i] * 3;
949 DWORD old_pos = i * 3;
950 word_indices[new_pos++] = dword_indices[old_pos++];
951 word_indices[new_pos++] = dword_indices[old_pos++];
952 word_indices[new_pos] = dword_indices[old_pos];
956 fill_attribute_table(attrib_buffer, This->numfaces, indices,
957 This->options & D3DXMESH_32BIT, attrib_table);
959 HeapFree(GetProcessHeap(), 0, This->attrib_table);
960 This->attrib_table = attrib_table;
961 This->attrib_table_size = attrib_table_size;
962 } else {
963 if (This->options & D3DXMESH_32BIT) {
964 memcpy(indices, dword_indices, This->numfaces * 3 * sizeof(DWORD));
965 } else {
966 WORD *word_indices = indices;
967 for (i = 0; i < This->numfaces * 3; i++)
968 *word_indices++ = dword_indices[i];
972 if (adjacency_out) {
973 if (face_remap) {
974 for (i = 0; i < This->numfaces; i++) {
975 DWORD old_pos = i * 3;
976 DWORD new_pos = face_remap[i] * 3;
977 adjacency_out[new_pos++] = face_remap[adjacency_in[old_pos++]];
978 adjacency_out[new_pos++] = face_remap[adjacency_in[old_pos++]];
979 adjacency_out[new_pos++] = face_remap[adjacency_in[old_pos++]];
981 } else {
982 memcpy(adjacency_out, adjacency_in, This->numfaces * 3 * sizeof(*adjacency_out));
985 if (face_remap_out) {
986 if (face_remap) {
987 for (i = 0; i < This->numfaces; i++)
988 face_remap_out[face_remap[i]] = i;
989 } else {
990 for (i = 0; i < This->numfaces; i++)
991 face_remap_out[i] = i;
994 if (vertex_remap_out)
995 *vertex_remap_out = vertex_remap;
996 vertex_remap = NULL;
998 if (vertex_buffer) {
999 IDirect3DVertexBuffer9_Release(This->vertex_buffer);
1000 This->vertex_buffer = vertex_buffer;
1001 vertex_buffer = NULL;
1002 This->numvertices = new_num_vertices;
1005 hr = D3D_OK;
1006 cleanup:
1007 HeapFree(GetProcessHeap(), 0, sorted_attrib_buffer);
1008 HeapFree(GetProcessHeap(), 0, face_remap);
1009 HeapFree(GetProcessHeap(), 0, dword_indices);
1010 if (vertex_remap) ID3DXBuffer_Release(vertex_remap);
1011 if (vertex_buffer) IDirect3DVertexBuffer9_Release(vertex_buffer);
1012 if (attrib_buffer) iface->lpVtbl->UnlockAttributeBuffer(iface);
1013 if (indices) iface->lpVtbl->UnlockIndexBuffer(iface);
1014 return hr;
1017 static HRESULT WINAPI ID3DXMeshImpl_SetAttributeTable(ID3DXMesh *iface, CONST D3DXATTRIBUTERANGE *attrib_table, DWORD attrib_table_size)
1019 ID3DXMeshImpl *This = impl_from_ID3DXMesh(iface);
1020 D3DXATTRIBUTERANGE *new_table = NULL;
1022 TRACE("(%p)->(%p,%u)\n", This, attrib_table, attrib_table_size);
1024 if (attrib_table_size) {
1025 size_t size = attrib_table_size * sizeof(*attrib_table);
1027 new_table = HeapAlloc(GetProcessHeap(), 0, size);
1028 if (!new_table)
1029 return E_OUTOFMEMORY;
1031 CopyMemory(new_table, attrib_table, size);
1032 } else if (attrib_table) {
1033 return D3DERR_INVALIDCALL;
1035 HeapFree(GetProcessHeap(), 0, This->attrib_table);
1036 This->attrib_table = new_table;
1037 This->attrib_table_size = attrib_table_size;
1039 return D3D_OK;
1042 static const struct ID3DXMeshVtbl D3DXMesh_Vtbl =
1044 /*** IUnknown methods ***/
1045 ID3DXMeshImpl_QueryInterface,
1046 ID3DXMeshImpl_AddRef,
1047 ID3DXMeshImpl_Release,
1048 /*** ID3DXBaseMesh ***/
1049 ID3DXMeshImpl_DrawSubset,
1050 ID3DXMeshImpl_GetNumFaces,
1051 ID3DXMeshImpl_GetNumVertices,
1052 ID3DXMeshImpl_GetFVF,
1053 ID3DXMeshImpl_GetDeclaration,
1054 ID3DXMeshImpl_GetNumBytesPerVertex,
1055 ID3DXMeshImpl_GetOptions,
1056 ID3DXMeshImpl_GetDevice,
1057 ID3DXMeshImpl_CloneMeshFVF,
1058 ID3DXMeshImpl_CloneMesh,
1059 ID3DXMeshImpl_GetVertexBuffer,
1060 ID3DXMeshImpl_GetIndexBuffer,
1061 ID3DXMeshImpl_LockVertexBuffer,
1062 ID3DXMeshImpl_UnlockVertexBuffer,
1063 ID3DXMeshImpl_LockIndexBuffer,
1064 ID3DXMeshImpl_UnlockIndexBuffer,
1065 ID3DXMeshImpl_GetAttributeTable,
1066 ID3DXMeshImpl_ConvertPointRepsToAdjacency,
1067 ID3DXMeshImpl_ConvertAdjacencyToPointReps,
1068 ID3DXMeshImpl_GenerateAdjacency,
1069 ID3DXMeshImpl_UpdateSemantics,
1070 /*** ID3DXMesh ***/
1071 ID3DXMeshImpl_LockAttributeBuffer,
1072 ID3DXMeshImpl_UnlockAttributeBuffer,
1073 ID3DXMeshImpl_Optimize,
1074 ID3DXMeshImpl_OptimizeInplace,
1075 ID3DXMeshImpl_SetAttributeTable
1078 /*************************************************************************
1079 * D3DXBoxBoundProbe
1081 BOOL WINAPI D3DXBoxBoundProbe(CONST D3DXVECTOR3 *pmin, CONST D3DXVECTOR3 *pmax, CONST D3DXVECTOR3 *prayposition, CONST D3DXVECTOR3 *praydirection)
1083 /* Algorithm taken from the article: An Efficient and Robust Ray-Box Intersection Algoritm
1084 Amy Williams University of Utah
1085 Steve Barrus University of Utah
1086 R. Keith Morley University of Utah
1087 Peter Shirley University of Utah
1089 International Conference on Computer Graphics and Interactive Techniques archive
1090 ACM SIGGRAPH 2005 Courses
1091 Los Angeles, California
1093 This algorithm is free of patents or of copyrights, as confirmed by Peter Shirley himself.
1095 Algorithm: Consider the box as the intersection of three slabs. Clip the ray
1096 against each slab, if there's anything left of the ray after we're
1097 done we've got an intersection of the ray with the box.
1101 FLOAT div, tmin, tmax, tymin, tymax, tzmin, tzmax;
1103 div = 1.0f / praydirection->x;
1104 if ( div >= 0.0f )
1106 tmin = ( pmin->x - prayposition->x ) * div;
1107 tmax = ( pmax->x - prayposition->x ) * div;
1109 else
1111 tmin = ( pmax->x - prayposition->x ) * div;
1112 tmax = ( pmin->x - prayposition->x ) * div;
1115 if ( tmax < 0.0f ) return FALSE;
1117 div = 1.0f / praydirection->y;
1118 if ( div >= 0.0f )
1120 tymin = ( pmin->y - prayposition->y ) * div;
1121 tymax = ( pmax->y - prayposition->y ) * div;
1123 else
1125 tymin = ( pmax->y - prayposition->y ) * div;
1126 tymax = ( pmin->y - prayposition->y ) * div;
1129 if ( ( tymax < 0.0f ) || ( tmin > tymax ) || ( tymin > tmax ) ) return FALSE;
1131 if ( tymin > tmin ) tmin = tymin;
1132 if ( tymax < tmax ) tmax = tymax;
1134 div = 1.0f / praydirection->z;
1135 if ( div >= 0.0f )
1137 tzmin = ( pmin->z - prayposition->z ) * div;
1138 tzmax = ( pmax->z - prayposition->z ) * div;
1140 else
1142 tzmin = ( pmax->z - prayposition->z ) * div;
1143 tzmax = ( pmin->z - prayposition->z ) * div;
1146 if ( (tzmax < 0.0f ) || ( tmin > tzmax ) || ( tzmin > tmax ) ) return FALSE;
1148 return TRUE;
1151 /*************************************************************************
1152 * D3DXComputeBoundingBox
1154 HRESULT WINAPI D3DXComputeBoundingBox(CONST D3DXVECTOR3 *pfirstposition, DWORD numvertices, DWORD dwstride, D3DXVECTOR3 *pmin, D3DXVECTOR3 *pmax)
1156 D3DXVECTOR3 vec;
1157 unsigned int i;
1159 if( !pfirstposition || !pmin || !pmax ) return D3DERR_INVALIDCALL;
1161 *pmin = *pfirstposition;
1162 *pmax = *pmin;
1164 for(i=0; i<numvertices; i++)
1166 vec = *( (const D3DXVECTOR3*)((const char*)pfirstposition + dwstride * i) );
1168 if ( vec.x < pmin->x ) pmin->x = vec.x;
1169 if ( vec.x > pmax->x ) pmax->x = vec.x;
1171 if ( vec.y < pmin->y ) pmin->y = vec.y;
1172 if ( vec.y > pmax->y ) pmax->y = vec.y;
1174 if ( vec.z < pmin->z ) pmin->z = vec.z;
1175 if ( vec.z > pmax->z ) pmax->z = vec.z;
1178 return D3D_OK;
1181 /*************************************************************************
1182 * D3DXComputeBoundingSphere
1184 HRESULT WINAPI D3DXComputeBoundingSphere(CONST D3DXVECTOR3* pfirstposition, DWORD numvertices, DWORD dwstride, D3DXVECTOR3 *pcenter, FLOAT *pradius)
1186 D3DXVECTOR3 temp, temp1;
1187 FLOAT d;
1188 unsigned int i;
1190 if( !pfirstposition || !pcenter || !pradius ) return D3DERR_INVALIDCALL;
1192 temp.x = 0.0f;
1193 temp.y = 0.0f;
1194 temp.z = 0.0f;
1195 temp1 = temp;
1196 *pradius = 0.0f;
1198 for(i=0; i<numvertices; i++)
1200 D3DXVec3Add(&temp1, &temp, (const D3DXVECTOR3*)((const char*)pfirstposition + dwstride * i));
1201 temp = temp1;
1204 D3DXVec3Scale(pcenter, &temp, 1.0f/((FLOAT)numvertices));
1206 for(i=0; i<numvertices; i++)
1208 d = D3DXVec3Length(D3DXVec3Subtract(&temp, (const D3DXVECTOR3*)((const char*)pfirstposition + dwstride * i), pcenter));
1209 if ( d > *pradius ) *pradius = d;
1211 return D3D_OK;
1214 static const UINT d3dx_decltype_size[D3DDECLTYPE_UNUSED] =
1216 /* D3DDECLTYPE_FLOAT1 */ 1 * 4,
1217 /* D3DDECLTYPE_FLOAT2 */ 2 * 4,
1218 /* D3DDECLTYPE_FLOAT3 */ 3 * 4,
1219 /* D3DDECLTYPE_FLOAT4 */ 4 * 4,
1220 /* D3DDECLTYPE_D3DCOLOR */ 4 * 1,
1221 /* D3DDECLTYPE_UBYTE4 */ 4 * 1,
1222 /* D3DDECLTYPE_SHORT2 */ 2 * 2,
1223 /* D3DDECLTYPE_SHORT4 */ 4 * 2,
1224 /* D3DDECLTYPE_UBYTE4N */ 4 * 1,
1225 /* D3DDECLTYPE_SHORT2N */ 2 * 2,
1226 /* D3DDECLTYPE_SHORT4N */ 4 * 2,
1227 /* D3DDECLTYPE_USHORT2N */ 2 * 2,
1228 /* D3DDECLTYPE_USHORT4N */ 4 * 2,
1229 /* D3DDECLTYPE_UDEC3 */ 4, /* 3 * 10 bits + 2 padding */
1230 /* D3DDECLTYPE_DEC3N */ 4,
1231 /* D3DDECLTYPE_FLOAT16_2 */ 2 * 2,
1232 /* D3DDECLTYPE_FLOAT16_4 */ 4 * 2,
1235 static void append_decl_element(D3DVERTEXELEMENT9 *declaration, UINT *idx, UINT *offset,
1236 D3DDECLTYPE type, D3DDECLUSAGE usage, UINT usage_idx)
1238 declaration[*idx].Stream = 0;
1239 declaration[*idx].Offset = *offset;
1240 declaration[*idx].Type = type;
1241 declaration[*idx].Method = D3DDECLMETHOD_DEFAULT;
1242 declaration[*idx].Usage = usage;
1243 declaration[*idx].UsageIndex = usage_idx;
1245 *offset += d3dx_decltype_size[type];
1246 ++(*idx);
1249 /*************************************************************************
1250 * D3DXDeclaratorFromFVF
1252 HRESULT WINAPI D3DXDeclaratorFromFVF(DWORD fvf, D3DVERTEXELEMENT9 declaration[MAX_FVF_DECL_SIZE])
1254 static const D3DVERTEXELEMENT9 end_element = D3DDECL_END();
1255 DWORD tex_count = (fvf & D3DFVF_TEXCOUNT_MASK) >> D3DFVF_TEXCOUNT_SHIFT;
1256 unsigned int offset = 0;
1257 unsigned int idx = 0;
1258 unsigned int i;
1260 TRACE("fvf %#x, declaration %p.\n", fvf, declaration);
1262 if (fvf & (D3DFVF_RESERVED0 | D3DFVF_RESERVED2)) return D3DERR_INVALIDCALL;
1264 if (fvf & D3DFVF_POSITION_MASK)
1266 BOOL has_blend = (fvf & D3DFVF_XYZB5) >= D3DFVF_XYZB1;
1267 DWORD blend_count = 1 + (((fvf & D3DFVF_XYZB5) - D3DFVF_XYZB1) >> 1);
1268 BOOL has_blend_idx = (fvf & D3DFVF_LASTBETA_D3DCOLOR) || (fvf & D3DFVF_LASTBETA_UBYTE4);
1270 if (has_blend_idx) --blend_count;
1272 if ((fvf & D3DFVF_POSITION_MASK) == D3DFVF_XYZW
1273 || (has_blend && blend_count > 4))
1274 return D3DERR_INVALIDCALL;
1276 if ((fvf & D3DFVF_POSITION_MASK) == D3DFVF_XYZRHW)
1277 append_decl_element(declaration, &idx, &offset, D3DDECLTYPE_FLOAT4, D3DDECLUSAGE_POSITIONT, 0);
1278 else
1279 append_decl_element(declaration, &idx, &offset, D3DDECLTYPE_FLOAT3, D3DDECLUSAGE_POSITION, 0);
1281 if (has_blend)
1283 switch (blend_count)
1285 case 0:
1286 break;
1287 case 1:
1288 append_decl_element(declaration, &idx, &offset, D3DDECLTYPE_FLOAT1, D3DDECLUSAGE_BLENDWEIGHT, 0);
1289 break;
1290 case 2:
1291 append_decl_element(declaration, &idx, &offset, D3DDECLTYPE_FLOAT2, D3DDECLUSAGE_BLENDWEIGHT, 0);
1292 break;
1293 case 3:
1294 append_decl_element(declaration, &idx, &offset, D3DDECLTYPE_FLOAT3, D3DDECLUSAGE_BLENDWEIGHT, 0);
1295 break;
1296 case 4:
1297 append_decl_element(declaration, &idx, &offset, D3DDECLTYPE_FLOAT4, D3DDECLUSAGE_BLENDWEIGHT, 0);
1298 break;
1299 default:
1300 ERR("Invalid blend count %u.\n", blend_count);
1301 break;
1304 if (has_blend_idx)
1306 if (fvf & D3DFVF_LASTBETA_UBYTE4)
1307 append_decl_element(declaration, &idx, &offset, D3DDECLTYPE_UBYTE4, D3DDECLUSAGE_BLENDINDICES, 0);
1308 else if (fvf & D3DFVF_LASTBETA_D3DCOLOR)
1309 append_decl_element(declaration, &idx, &offset, D3DDECLTYPE_D3DCOLOR, D3DDECLUSAGE_BLENDINDICES, 0);
1314 if (fvf & D3DFVF_NORMAL)
1315 append_decl_element(declaration, &idx, &offset, D3DDECLTYPE_FLOAT3, D3DDECLUSAGE_NORMAL, 0);
1316 if (fvf & D3DFVF_PSIZE)
1317 append_decl_element(declaration, &idx, &offset, D3DDECLTYPE_FLOAT1, D3DDECLUSAGE_PSIZE, 0);
1318 if (fvf & D3DFVF_DIFFUSE)
1319 append_decl_element(declaration, &idx, &offset, D3DDECLTYPE_D3DCOLOR, D3DDECLUSAGE_COLOR, 0);
1320 if (fvf & D3DFVF_SPECULAR)
1321 append_decl_element(declaration, &idx, &offset, D3DDECLTYPE_D3DCOLOR, D3DDECLUSAGE_COLOR, 1);
1323 for (i = 0; i < tex_count; ++i)
1325 switch ((fvf >> (16 + 2 * i)) & 0x03)
1327 case D3DFVF_TEXTUREFORMAT1:
1328 append_decl_element(declaration, &idx, &offset, D3DDECLTYPE_FLOAT1, D3DDECLUSAGE_TEXCOORD, i);
1329 break;
1330 case D3DFVF_TEXTUREFORMAT2:
1331 append_decl_element(declaration, &idx, &offset, D3DDECLTYPE_FLOAT2, D3DDECLUSAGE_TEXCOORD, i);
1332 break;
1333 case D3DFVF_TEXTUREFORMAT3:
1334 append_decl_element(declaration, &idx, &offset, D3DDECLTYPE_FLOAT3, D3DDECLUSAGE_TEXCOORD, i);
1335 break;
1336 case D3DFVF_TEXTUREFORMAT4:
1337 append_decl_element(declaration, &idx, &offset, D3DDECLTYPE_FLOAT4, D3DDECLUSAGE_TEXCOORD, i);
1338 break;
1342 declaration[idx] = end_element;
1344 return D3D_OK;
1347 /*************************************************************************
1348 * D3DXFVFFromDeclarator
1350 HRESULT WINAPI D3DXFVFFromDeclarator(const D3DVERTEXELEMENT9 *declaration, DWORD *fvf)
1352 unsigned int i = 0, texture, offset;
1354 TRACE("(%p, %p)\n", declaration, fvf);
1356 *fvf = 0;
1357 if (declaration[0].Type == D3DDECLTYPE_FLOAT3 && declaration[0].Usage == D3DDECLUSAGE_POSITION)
1359 if ((declaration[1].Type == D3DDECLTYPE_FLOAT4 && declaration[1].Usage == D3DDECLUSAGE_BLENDWEIGHT &&
1360 declaration[1].UsageIndex == 0) &&
1361 (declaration[2].Type == D3DDECLTYPE_FLOAT1 && declaration[2].Usage == D3DDECLUSAGE_BLENDINDICES &&
1362 declaration[2].UsageIndex == 0))
1364 return D3DERR_INVALIDCALL;
1366 else if ((declaration[1].Type == D3DDECLTYPE_UBYTE4 || declaration[1].Type == D3DDECLTYPE_D3DCOLOR) &&
1367 declaration[1].Usage == D3DDECLUSAGE_BLENDINDICES && declaration[1].UsageIndex == 0)
1369 if (declaration[1].Type == D3DDECLTYPE_UBYTE4)
1371 *fvf |= D3DFVF_XYZB1 | D3DFVF_LASTBETA_UBYTE4;
1373 else
1375 *fvf |= D3DFVF_XYZB1 | D3DFVF_LASTBETA_D3DCOLOR;
1377 i = 2;
1379 else if (declaration[1].Type <= D3DDECLTYPE_FLOAT4 && declaration[1].Usage == D3DDECLUSAGE_BLENDWEIGHT &&
1380 declaration[1].UsageIndex == 0)
1382 if ((declaration[2].Type == D3DDECLTYPE_UBYTE4 || declaration[2].Type == D3DDECLTYPE_D3DCOLOR) &&
1383 declaration[2].Usage == D3DDECLUSAGE_BLENDINDICES && declaration[2].UsageIndex == 0)
1385 if (declaration[2].Type == D3DDECLTYPE_UBYTE4)
1387 *fvf |= D3DFVF_LASTBETA_UBYTE4;
1389 else
1391 *fvf |= D3DFVF_LASTBETA_D3DCOLOR;
1393 switch (declaration[1].Type)
1395 case D3DDECLTYPE_FLOAT1: *fvf |= D3DFVF_XYZB2; break;
1396 case D3DDECLTYPE_FLOAT2: *fvf |= D3DFVF_XYZB3; break;
1397 case D3DDECLTYPE_FLOAT3: *fvf |= D3DFVF_XYZB4; break;
1398 case D3DDECLTYPE_FLOAT4: *fvf |= D3DFVF_XYZB5; break;
1400 i = 3;
1402 else
1404 switch (declaration[1].Type)
1406 case D3DDECLTYPE_FLOAT1: *fvf |= D3DFVF_XYZB1; break;
1407 case D3DDECLTYPE_FLOAT2: *fvf |= D3DFVF_XYZB2; break;
1408 case D3DDECLTYPE_FLOAT3: *fvf |= D3DFVF_XYZB3; break;
1409 case D3DDECLTYPE_FLOAT4: *fvf |= D3DFVF_XYZB4; break;
1411 i = 2;
1414 else
1416 *fvf |= D3DFVF_XYZ;
1417 i = 1;
1420 else if (declaration[0].Type == D3DDECLTYPE_FLOAT4 && declaration[0].Usage == D3DDECLUSAGE_POSITIONT &&
1421 declaration[0].UsageIndex == 0)
1423 *fvf |= D3DFVF_XYZRHW;
1424 i = 1;
1427 if (declaration[i].Type == D3DDECLTYPE_FLOAT3 && declaration[i].Usage == D3DDECLUSAGE_NORMAL)
1429 *fvf |= D3DFVF_NORMAL;
1430 i++;
1432 if (declaration[i].Type == D3DDECLTYPE_FLOAT1 && declaration[i].Usage == D3DDECLUSAGE_PSIZE &&
1433 declaration[i].UsageIndex == 0)
1435 *fvf |= D3DFVF_PSIZE;
1436 i++;
1438 if (declaration[i].Type == D3DDECLTYPE_D3DCOLOR && declaration[i].Usage == D3DDECLUSAGE_COLOR &&
1439 declaration[i].UsageIndex == 0)
1441 *fvf |= D3DFVF_DIFFUSE;
1442 i++;
1444 if (declaration[i].Type == D3DDECLTYPE_D3DCOLOR && declaration[i].Usage == D3DDECLUSAGE_COLOR &&
1445 declaration[i].UsageIndex == 1)
1447 *fvf |= D3DFVF_SPECULAR;
1448 i++;
1451 for (texture = 0; texture < D3DDP_MAXTEXCOORD; i++, texture++)
1453 if (declaration[i].Stream == 0xFF)
1455 break;
1457 else if (declaration[i].Type == D3DDECLTYPE_FLOAT1 && declaration[i].Usage == D3DDECLUSAGE_TEXCOORD &&
1458 declaration[i].UsageIndex == texture)
1460 *fvf |= D3DFVF_TEXCOORDSIZE1(declaration[i].UsageIndex);
1462 else if (declaration[i].Type == D3DDECLTYPE_FLOAT2 && declaration[i].Usage == D3DDECLUSAGE_TEXCOORD &&
1463 declaration[i].UsageIndex == texture)
1465 *fvf |= D3DFVF_TEXCOORDSIZE2(declaration[i].UsageIndex);
1467 else if (declaration[i].Type == D3DDECLTYPE_FLOAT3 && declaration[i].Usage == D3DDECLUSAGE_TEXCOORD &&
1468 declaration[i].UsageIndex == texture)
1470 *fvf |= D3DFVF_TEXCOORDSIZE3(declaration[i].UsageIndex);
1472 else if (declaration[i].Type == D3DDECLTYPE_FLOAT4 && declaration[i].Usage == D3DDECLUSAGE_TEXCOORD &&
1473 declaration[i].UsageIndex == texture)
1475 *fvf |= D3DFVF_TEXCOORDSIZE4(declaration[i].UsageIndex);
1477 else
1479 return D3DERR_INVALIDCALL;
1483 *fvf |= (texture << D3DFVF_TEXCOUNT_SHIFT);
1485 for (offset = 0, i = 0; declaration[i].Stream != 0xFF;
1486 offset += d3dx_decltype_size[declaration[i].Type], i++)
1488 if (declaration[i].Offset != offset)
1490 return D3DERR_INVALIDCALL;
1494 return D3D_OK;
1497 /*************************************************************************
1498 * D3DXGetFVFVertexSize
1500 static UINT Get_TexCoord_Size_From_FVF(DWORD FVF, int tex_num)
1502 return (((((FVF) >> (16 + (2 * (tex_num)))) + 1) & 0x03) + 1);
1505 UINT WINAPI D3DXGetFVFVertexSize(DWORD FVF)
1507 DWORD size = 0;
1508 UINT i;
1509 UINT numTextures = (FVF & D3DFVF_TEXCOUNT_MASK) >> D3DFVF_TEXCOUNT_SHIFT;
1511 if (FVF & D3DFVF_NORMAL) size += sizeof(D3DXVECTOR3);
1512 if (FVF & D3DFVF_DIFFUSE) size += sizeof(DWORD);
1513 if (FVF & D3DFVF_SPECULAR) size += sizeof(DWORD);
1514 if (FVF & D3DFVF_PSIZE) size += sizeof(DWORD);
1516 switch (FVF & D3DFVF_POSITION_MASK)
1518 case D3DFVF_XYZ: size += sizeof(D3DXVECTOR3); break;
1519 case D3DFVF_XYZRHW: size += 4 * sizeof(FLOAT); break;
1520 case D3DFVF_XYZB1: size += 4 * sizeof(FLOAT); break;
1521 case D3DFVF_XYZB2: size += 5 * sizeof(FLOAT); break;
1522 case D3DFVF_XYZB3: size += 6 * sizeof(FLOAT); break;
1523 case D3DFVF_XYZB4: size += 7 * sizeof(FLOAT); break;
1524 case D3DFVF_XYZB5: size += 8 * sizeof(FLOAT); break;
1525 case D3DFVF_XYZW: size += 4 * sizeof(FLOAT); break;
1528 for (i = 0; i < numTextures; i++)
1530 size += Get_TexCoord_Size_From_FVF(FVF, i) * sizeof(FLOAT);
1533 return size;
1536 /*************************************************************************
1537 * D3DXGetDeclVertexSize
1539 UINT WINAPI D3DXGetDeclVertexSize(const D3DVERTEXELEMENT9 *decl, DWORD stream_idx)
1541 const D3DVERTEXELEMENT9 *element;
1542 UINT size = 0;
1544 TRACE("decl %p, stream_idx %u\n", decl, stream_idx);
1546 if (!decl) return 0;
1548 for (element = decl; element->Stream != 0xff; ++element)
1550 UINT type_size;
1552 if (element->Stream != stream_idx) continue;
1554 if (element->Type >= sizeof(d3dx_decltype_size) / sizeof(*d3dx_decltype_size))
1556 FIXME("Unhandled element type %#x, size will be incorrect.\n", element->Type);
1557 continue;
1560 type_size = d3dx_decltype_size[element->Type];
1561 if (element->Offset + type_size > size) size = element->Offset + type_size;
1564 return size;
1567 /*************************************************************************
1568 * D3DXGetDeclLength
1570 UINT WINAPI D3DXGetDeclLength(const D3DVERTEXELEMENT9 *decl)
1572 const D3DVERTEXELEMENT9 *element;
1574 TRACE("decl %p\n", decl);
1576 /* null decl results in exception on Windows XP */
1578 for (element = decl; element->Stream != 0xff; ++element);
1580 return element - decl;
1583 /*************************************************************************
1584 * D3DXIntersectTri
1586 BOOL WINAPI D3DXIntersectTri(CONST D3DXVECTOR3 *p0, CONST D3DXVECTOR3 *p1, CONST D3DXVECTOR3 *p2, CONST D3DXVECTOR3 *praypos, CONST D3DXVECTOR3 *praydir, FLOAT *pu, FLOAT *pv, FLOAT *pdist)
1588 D3DXMATRIX m;
1589 D3DXVECTOR4 vec;
1591 m.u.m[0][0] = p1->x - p0->x;
1592 m.u.m[1][0] = p2->x - p0->x;
1593 m.u.m[2][0] = -praydir->x;
1594 m.u.m[3][0] = 0.0f;
1595 m.u.m[0][1] = p1->y - p0->z;
1596 m.u.m[1][1] = p2->y - p0->z;
1597 m.u.m[2][1] = -praydir->y;
1598 m.u.m[3][1] = 0.0f;
1599 m.u.m[0][2] = p1->z - p0->z;
1600 m.u.m[1][2] = p2->z - p0->z;
1601 m.u.m[2][2] = -praydir->z;
1602 m.u.m[3][2] = 0.0f;
1603 m.u.m[0][3] = 0.0f;
1604 m.u.m[1][3] = 0.0f;
1605 m.u.m[2][3] = 0.0f;
1606 m.u.m[3][3] = 1.0f;
1608 vec.x = praypos->x - p0->x;
1609 vec.y = praypos->y - p0->y;
1610 vec.z = praypos->z - p0->z;
1611 vec.w = 0.0f;
1613 if ( D3DXMatrixInverse(&m, NULL, &m) )
1615 D3DXVec4Transform(&vec, &vec, &m);
1616 if ( (vec.x >= 0.0f) && (vec.y >= 0.0f) && (vec.x + vec.y <= 1.0f) && (vec.z >= 0.0f) )
1618 *pu = vec.x;
1619 *pv = vec.y;
1620 *pdist = fabs( vec.z );
1621 return TRUE;
1625 return FALSE;
1628 /*************************************************************************
1629 * D3DXSphereBoundProbe
1631 BOOL WINAPI D3DXSphereBoundProbe(CONST D3DXVECTOR3 *pcenter, FLOAT radius, CONST D3DXVECTOR3 *prayposition, CONST D3DXVECTOR3 *praydirection)
1633 D3DXVECTOR3 difference;
1634 FLOAT a, b, c, d;
1636 a = D3DXVec3LengthSq(praydirection);
1637 if (!D3DXVec3Subtract(&difference, prayposition, pcenter)) return FALSE;
1638 b = D3DXVec3Dot(&difference, praydirection);
1639 c = D3DXVec3LengthSq(&difference) - radius * radius;
1640 d = b * b - a * c;
1642 if ( ( d <= 0.0f ) || ( sqrt(d) <= b ) ) return FALSE;
1643 return TRUE;
1646 /*************************************************************************
1647 * D3DXCreateMesh
1649 HRESULT WINAPI D3DXCreateMesh(DWORD numfaces, DWORD numvertices, DWORD options, CONST D3DVERTEXELEMENT9 *declaration,
1650 LPDIRECT3DDEVICE9 device, LPD3DXMESH *mesh)
1652 HRESULT hr;
1653 DWORD fvf;
1654 IDirect3DVertexDeclaration9 *vertex_declaration;
1655 IDirect3DVertexBuffer9 *vertex_buffer;
1656 IDirect3DIndexBuffer9 *index_buffer;
1657 DWORD *attrib_buffer;
1658 ID3DXMeshImpl *object;
1659 DWORD index_usage = 0;
1660 D3DPOOL index_pool = D3DPOOL_DEFAULT;
1661 D3DFORMAT index_format = D3DFMT_INDEX16;
1662 DWORD vertex_usage = 0;
1663 D3DPOOL vertex_pool = D3DPOOL_DEFAULT;
1664 int i;
1666 TRACE("(%d, %d, %x, %p, %p, %p)\n", numfaces, numvertices, options, declaration, device, mesh);
1668 if (numfaces == 0 || numvertices == 0 || declaration == NULL || device == NULL || mesh == NULL ||
1669 /* D3DXMESH_VB_SHARE is for cloning, and D3DXMESH_USEHWONLY is for ConvertToBlendedMesh */
1670 (options & (D3DXMESH_VB_SHARE | D3DXMESH_USEHWONLY | 0xfffe0000)))
1672 return D3DERR_INVALIDCALL;
1674 for (i = 0; declaration[i].Stream != 0xff; i++)
1675 if (declaration[i].Stream != 0)
1676 return D3DERR_INVALIDCALL;
1678 if (options & D3DXMESH_32BIT)
1679 index_format = D3DFMT_INDEX32;
1681 if (options & D3DXMESH_DONOTCLIP) {
1682 index_usage |= D3DUSAGE_DONOTCLIP;
1683 vertex_usage |= D3DUSAGE_DONOTCLIP;
1685 if (options & D3DXMESH_POINTS) {
1686 index_usage |= D3DUSAGE_POINTS;
1687 vertex_usage |= D3DUSAGE_POINTS;
1689 if (options & D3DXMESH_RTPATCHES) {
1690 index_usage |= D3DUSAGE_RTPATCHES;
1691 vertex_usage |= D3DUSAGE_RTPATCHES;
1693 if (options & D3DXMESH_NPATCHES) {
1694 index_usage |= D3DUSAGE_NPATCHES;
1695 vertex_usage |= D3DUSAGE_NPATCHES;
1698 if (options & D3DXMESH_VB_SYSTEMMEM)
1699 vertex_pool = D3DPOOL_SYSTEMMEM;
1700 else if (options & D3DXMESH_VB_MANAGED)
1701 vertex_pool = D3DPOOL_MANAGED;
1703 if (options & D3DXMESH_VB_WRITEONLY)
1704 vertex_usage |= D3DUSAGE_WRITEONLY;
1705 if (options & D3DXMESH_VB_DYNAMIC)
1706 vertex_usage |= D3DUSAGE_DYNAMIC;
1707 if (options & D3DXMESH_VB_SOFTWAREPROCESSING)
1708 vertex_usage |= D3DUSAGE_SOFTWAREPROCESSING;
1710 if (options & D3DXMESH_IB_SYSTEMMEM)
1711 index_pool = D3DPOOL_SYSTEMMEM;
1712 else if (options & D3DXMESH_IB_MANAGED)
1713 index_pool = D3DPOOL_MANAGED;
1715 if (options & D3DXMESH_IB_WRITEONLY)
1716 index_usage |= D3DUSAGE_WRITEONLY;
1717 if (options & D3DXMESH_IB_DYNAMIC)
1718 index_usage |= D3DUSAGE_DYNAMIC;
1719 if (options & D3DXMESH_IB_SOFTWAREPROCESSING)
1720 index_usage |= D3DUSAGE_SOFTWAREPROCESSING;
1722 hr = D3DXFVFFromDeclarator(declaration, &fvf);
1723 if (hr != D3D_OK)
1725 fvf = 0;
1728 /* Create vertex declaration */
1729 hr = IDirect3DDevice9_CreateVertexDeclaration(device,
1730 declaration,
1731 &vertex_declaration);
1732 if (FAILED(hr))
1734 WARN("Unexpected return value %x from IDirect3DDevice9_CreateVertexDeclaration.\n",hr);
1735 return hr;
1738 /* Create vertex buffer */
1739 hr = IDirect3DDevice9_CreateVertexBuffer(device,
1740 numvertices * D3DXGetDeclVertexSize(declaration, declaration[0].Stream),
1741 vertex_usage,
1742 fvf,
1743 vertex_pool,
1744 &vertex_buffer,
1745 NULL);
1746 if (FAILED(hr))
1748 WARN("Unexpected return value %x from IDirect3DDevice9_CreateVertexBuffer.\n",hr);
1749 IDirect3DVertexDeclaration9_Release(vertex_declaration);
1750 return hr;
1753 /* Create index buffer */
1754 hr = IDirect3DDevice9_CreateIndexBuffer(device,
1755 numfaces * 3 * ((index_format == D3DFMT_INDEX16) ? 2 : 4),
1756 index_usage,
1757 index_format,
1758 index_pool,
1759 &index_buffer,
1760 NULL);
1761 if (FAILED(hr))
1763 WARN("Unexpected return value %x from IDirect3DDevice9_CreateVertexBuffer.\n",hr);
1764 IDirect3DVertexBuffer9_Release(vertex_buffer);
1765 IDirect3DVertexDeclaration9_Release(vertex_declaration);
1766 return hr;
1769 attrib_buffer = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, numfaces * sizeof(*attrib_buffer));
1770 object = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(ID3DXMeshImpl));
1771 if (object == NULL || attrib_buffer == NULL)
1773 HeapFree(GetProcessHeap(), 0, attrib_buffer);
1774 IDirect3DIndexBuffer9_Release(index_buffer);
1775 IDirect3DVertexBuffer9_Release(vertex_buffer);
1776 IDirect3DVertexDeclaration9_Release(vertex_declaration);
1777 *mesh = NULL;
1778 return E_OUTOFMEMORY;
1780 object->ID3DXMesh_iface.lpVtbl = &D3DXMesh_Vtbl;
1781 object->ref = 1;
1783 object->numfaces = numfaces;
1784 object->numvertices = numvertices;
1785 object->options = options;
1786 object->fvf = fvf;
1787 object->device = device;
1788 IDirect3DDevice9_AddRef(device);
1790 object->vertex_declaration = vertex_declaration;
1791 object->vertex_buffer = vertex_buffer;
1792 object->index_buffer = index_buffer;
1793 object->attrib_buffer = attrib_buffer;
1795 *mesh = &object->ID3DXMesh_iface;
1797 return D3D_OK;
1800 /*************************************************************************
1801 * D3DXCreateMeshFVF
1803 HRESULT WINAPI D3DXCreateMeshFVF(DWORD numfaces, DWORD numvertices, DWORD options, DWORD fvf,
1804 LPDIRECT3DDEVICE9 device, LPD3DXMESH *mesh)
1806 HRESULT hr;
1807 D3DVERTEXELEMENT9 declaration[MAX_FVF_DECL_SIZE];
1809 TRACE("(%u, %u, %u, %u, %p, %p)\n", numfaces, numvertices, options, fvf, device, mesh);
1811 hr = D3DXDeclaratorFromFVF(fvf, declaration);
1812 if (FAILED(hr)) return hr;
1814 return D3DXCreateMesh(numfaces, numvertices, options, declaration, device, mesh);
1818 struct mesh_data {
1819 DWORD num_vertices;
1820 DWORD num_poly_faces;
1821 DWORD num_tri_faces;
1822 D3DXVECTOR3 *vertices;
1823 DWORD *num_tri_per_face;
1824 DWORD *indices;
1826 DWORD fvf;
1828 /* optional mesh data */
1830 DWORD num_normals;
1831 D3DXVECTOR3 *normals;
1832 DWORD *normal_indices;
1834 DWORD num_materials;
1835 D3DXMATERIAL *materials;
1836 DWORD *material_indices;
1839 static HRESULT get_next_child(IDirectXFileData *filedata, IDirectXFileData **child, const GUID **type)
1841 HRESULT hr;
1842 IDirectXFileDataReference *child_ref = NULL;
1843 IDirectXFileObject *child_obj = NULL;
1844 IDirectXFileData *child_data = NULL;
1846 hr = IDirectXFileData_GetNextObject(filedata, &child_obj);
1847 if (FAILED(hr)) return hr;
1849 hr = IDirectXFileObject_QueryInterface(child_obj, &IID_IDirectXFileDataReference, (void**)&child_ref);
1850 if (SUCCEEDED(hr)) {
1851 hr = IDirectXFileDataReference_Resolve(child_ref, &child_data);
1852 IDirectXFileDataReference_Release(child_ref);
1853 } else {
1854 hr = IDirectXFileObject_QueryInterface(child_obj, &IID_IDirectXFileData, (void**)&child_data);
1856 IDirectXFileObject_Release(child_obj);
1857 if (FAILED(hr))
1858 return hr;
1860 hr = IDirectXFileData_GetType(child_data, type);
1861 if (FAILED(hr)) {
1862 IDirectXFileData_Release(child_data);
1863 } else {
1864 *child = child_data;
1867 return hr;
1870 static HRESULT parse_texture_filename(IDirectXFileData *filedata, LPSTR *filename_out)
1872 HRESULT hr;
1873 DWORD data_size;
1874 BYTE *data;
1875 char *filename_in;
1876 char *filename = NULL;
1878 /* template TextureFilename {
1879 * STRING filename;
1883 HeapFree(GetProcessHeap(), 0, *filename_out);
1884 *filename_out = NULL;
1886 hr = IDirectXFileData_GetData(filedata, NULL, &data_size, (void**)&data);
1887 if (FAILED(hr)) return hr;
1889 if (data_size < sizeof(LPSTR)) {
1890 WARN("truncated data (%u bytes)\n", data_size);
1891 return E_FAIL;
1893 filename_in = *(LPSTR*)data;
1895 filename = HeapAlloc(GetProcessHeap(), 0, strlen(filename_in) + 1);
1896 if (!filename) return E_OUTOFMEMORY;
1898 strcpy(filename, filename_in);
1899 *filename_out = filename;
1901 return D3D_OK;
1904 static HRESULT parse_material(IDirectXFileData *filedata, D3DXMATERIAL *material)
1906 HRESULT hr;
1907 DWORD data_size;
1908 BYTE *data;
1909 const GUID *type;
1910 IDirectXFileData *child;
1912 material->pTextureFilename = NULL;
1914 hr = IDirectXFileData_GetData(filedata, NULL, &data_size, (void**)&data);
1915 if (FAILED(hr)) return hr;
1918 * template ColorRGBA {
1919 * FLOAT red;
1920 * FLOAT green;
1921 * FLOAT blue;
1922 * FLOAT alpha;
1924 * template ColorRGB {
1925 * FLOAT red;
1926 * FLOAT green;
1927 * FLOAT blue;
1929 * template Material {
1930 * ColorRGBA faceColor;
1931 * FLOAT power;
1932 * ColorRGB specularColor;
1933 * ColorRGB emissiveColor;
1934 * [ ... ]
1937 if (data_size != sizeof(FLOAT) * 11) {
1938 WARN("incorrect data size (%u bytes)\n", data_size);
1939 return E_FAIL;
1942 memcpy(&material->MatD3D.Diffuse, data, sizeof(D3DCOLORVALUE));
1943 data += sizeof(D3DCOLORVALUE);
1944 material->MatD3D.Power = *(FLOAT*)data;
1945 data += sizeof(FLOAT);
1946 memcpy(&material->MatD3D.Specular, data, sizeof(FLOAT) * 3);
1947 material->MatD3D.Specular.a = 1.0f;
1948 data += 3 * sizeof(FLOAT);
1949 memcpy(&material->MatD3D.Emissive, data, sizeof(FLOAT) * 3);
1950 material->MatD3D.Emissive.a = 1.0f;
1951 material->MatD3D.Ambient.r = 0.0f;
1952 material->MatD3D.Ambient.g = 0.0f;
1953 material->MatD3D.Ambient.b = 0.0f;
1954 material->MatD3D.Ambient.a = 1.0f;
1956 while (SUCCEEDED(hr = get_next_child(filedata, &child, &type)))
1958 if (IsEqualGUID(type, &TID_D3DRMTextureFilename)) {
1959 hr = parse_texture_filename(child, &material->pTextureFilename);
1960 if (FAILED(hr)) break;
1963 return hr == DXFILEERR_NOMOREOBJECTS ? D3D_OK : hr;
1966 static void destroy_materials(struct mesh_data *mesh)
1968 int i;
1969 for (i = 0; i < mesh->num_materials; i++)
1970 HeapFree(GetProcessHeap(), 0, mesh->materials[i].pTextureFilename);
1971 HeapFree(GetProcessHeap(), 0, mesh->materials);
1972 HeapFree(GetProcessHeap(), 0, mesh->material_indices);
1973 mesh->num_materials = 0;
1974 mesh->materials = NULL;
1975 mesh->material_indices = NULL;
1978 static HRESULT parse_material_list(IDirectXFileData *filedata, struct mesh_data *mesh)
1980 HRESULT hr;
1981 DWORD data_size;
1982 DWORD *data, *in_ptr;
1983 const GUID *type;
1984 IDirectXFileData *child;
1985 DWORD num_materials;
1986 int i;
1988 destroy_materials(mesh);
1990 hr = IDirectXFileData_GetData(filedata, NULL, &data_size, (void**)&data);
1991 if (FAILED(hr)) return hr;
1993 /* template MeshMaterialList {
1994 * DWORD nMaterials;
1995 * DWORD nFaceIndexes;
1996 * array DWORD faceIndexes[nFaceIndexes];
1997 * [ Material ]
2001 in_ptr = data;
2003 if (data_size < sizeof(DWORD))
2004 goto truncated_data_error;
2005 num_materials = *in_ptr++;
2006 if (!num_materials)
2007 return D3D_OK;
2009 if (data_size < 2 * sizeof(DWORD))
2010 goto truncated_data_error;
2011 if (*in_ptr++ != mesh->num_poly_faces) {
2012 WARN("number of material face indices (%u) doesn't match number of faces (%u)\n",
2013 *(in_ptr - 1), mesh->num_poly_faces);
2014 return E_FAIL;
2016 if (data_size < 2 * sizeof(DWORD) + mesh->num_poly_faces * sizeof(DWORD))
2017 goto truncated_data_error;
2018 for (i = 0; i < mesh->num_poly_faces; i++) {
2019 if (*in_ptr++ >= num_materials) {
2020 WARN("face %u: reference to undefined material %u (only %u materials)\n",
2021 i, *(in_ptr - 1), num_materials);
2022 return E_FAIL;
2026 mesh->materials = HeapAlloc(GetProcessHeap(), 0, num_materials * sizeof(*mesh->materials));
2027 mesh->material_indices = HeapAlloc(GetProcessHeap(), 0, mesh->num_poly_faces * sizeof(*mesh->material_indices));
2028 if (!mesh->materials || !mesh->material_indices)
2029 return E_OUTOFMEMORY;
2030 memcpy(mesh->material_indices, data + 2, mesh->num_poly_faces * sizeof(DWORD));
2032 while (SUCCEEDED(hr = get_next_child(filedata, &child, &type)))
2034 if (IsEqualGUID(type, &TID_D3DRMMaterial)) {
2035 if (mesh->num_materials >= num_materials) {
2036 WARN("more materials defined than declared\n");
2037 return E_FAIL;
2039 hr = parse_material(child, &mesh->materials[mesh->num_materials++]);
2040 if (FAILED(hr)) break;
2043 if (hr != DXFILEERR_NOMOREOBJECTS)
2044 return hr;
2045 if (num_materials != mesh->num_materials) {
2046 WARN("only %u of %u materials defined\n", num_materials, mesh->num_materials);
2047 return E_FAIL;
2050 return D3D_OK;
2051 truncated_data_error:
2052 WARN("truncated data (%u bytes)\n", data_size);
2053 return E_FAIL;
2056 static HRESULT parse_normals(IDirectXFileData *filedata, struct mesh_data *mesh)
2058 HRESULT hr;
2059 DWORD data_size;
2060 BYTE *data;
2061 DWORD *index_out_ptr;
2062 int i;
2063 DWORD num_face_indices = mesh->num_poly_faces * 2 + mesh->num_tri_faces;
2065 HeapFree(GetProcessHeap(), 0, mesh->normals);
2066 mesh->num_normals = 0;
2067 mesh->normals = NULL;
2068 mesh->normal_indices = NULL;
2069 mesh->fvf |= D3DFVF_NORMAL;
2071 hr = IDirectXFileData_GetData(filedata, NULL, &data_size, (void**)&data);
2072 if (FAILED(hr)) return hr;
2074 /* template Vector {
2075 * FLOAT x;
2076 * FLOAT y;
2077 * FLOAT z;
2079 * template MeshFace {
2080 * DWORD nFaceVertexIndices;
2081 * array DWORD faceVertexIndices[nFaceVertexIndices];
2083 * template MeshNormals {
2084 * DWORD nNormals;
2085 * array Vector normals[nNormals];
2086 * DWORD nFaceNormals;
2087 * array MeshFace faceNormals[nFaceNormals];
2091 if (data_size < sizeof(DWORD) * 2)
2092 goto truncated_data_error;
2093 mesh->num_normals = *(DWORD*)data;
2094 data += sizeof(DWORD);
2095 if (data_size < sizeof(DWORD) * 2 + mesh->num_normals * sizeof(D3DXVECTOR3) +
2096 num_face_indices * sizeof(DWORD))
2097 goto truncated_data_error;
2099 mesh->normals = HeapAlloc(GetProcessHeap(), 0, mesh->num_normals * sizeof(D3DXVECTOR3));
2100 mesh->normal_indices = HeapAlloc(GetProcessHeap(), 0, num_face_indices * sizeof(DWORD));
2101 if (!mesh->normals || !mesh->normal_indices)
2102 return E_OUTOFMEMORY;
2104 memcpy(mesh->normals, data, mesh->num_normals * sizeof(D3DXVECTOR3));
2105 data += mesh->num_normals * sizeof(D3DXVECTOR3);
2106 for (i = 0; i < mesh->num_normals; i++)
2107 D3DXVec3Normalize(&mesh->normals[i], &mesh->normals[i]);
2109 if (*(DWORD*)data != mesh->num_poly_faces) {
2110 WARN("number of face normals (%u) doesn't match number of faces (%u)\n",
2111 *(DWORD*)data, mesh->num_poly_faces);
2112 return E_FAIL;
2114 data += sizeof(DWORD);
2115 index_out_ptr = mesh->normal_indices;
2116 for (i = 0; i < mesh->num_poly_faces; i++)
2118 DWORD j;
2119 DWORD count = *(DWORD*)data;
2120 if (count != mesh->num_tri_per_face[i] + 2) {
2121 WARN("face %u: number of normals (%u) doesn't match number of vertices (%u)\n",
2122 i, count, mesh->num_tri_per_face[i] + 2);
2123 return E_FAIL;
2125 data += sizeof(DWORD);
2127 for (j = 0; j < count; j++) {
2128 DWORD normal_index = *(DWORD*)data;
2129 if (normal_index >= mesh->num_normals) {
2130 WARN("face %u, normal index %u: reference to undefined normal %u (only %u normals)\n",
2131 i, j, normal_index, mesh->num_normals);
2132 return E_FAIL;
2134 *index_out_ptr++ = normal_index;
2135 data += sizeof(DWORD);
2139 return D3D_OK;
2140 truncated_data_error:
2141 WARN("truncated data (%u bytes)\n", data_size);
2142 return E_FAIL;
2145 /* for provide_flags parameters */
2146 #define PROVIDE_MATERIALS 0x1
2147 #define PROVIDE_SKININFO 0x2
2149 static HRESULT parse_mesh(IDirectXFileData *filedata, struct mesh_data *mesh_data, DWORD provide_flags)
2151 HRESULT hr;
2152 DWORD data_size;
2153 BYTE *data, *in_ptr;
2154 DWORD *index_out_ptr;
2155 const GUID *type;
2156 IDirectXFileData *child;
2157 int i;
2160 * template Mesh {
2161 * DWORD nVertices;
2162 * array Vector vertices[nVertices];
2163 * DWORD nFaces;
2164 * array MeshFace faces[nFaces];
2165 * [ ... ]
2169 hr = IDirectXFileData_GetData(filedata, NULL, &data_size, (void**)&data);
2170 if (FAILED(hr)) return hr;
2172 in_ptr = data;
2173 if (data_size < sizeof(DWORD) * 2)
2174 goto truncated_data_error;
2175 mesh_data->num_vertices = *(DWORD*)in_ptr;
2176 if (data_size < sizeof(DWORD) * 2 + mesh_data->num_vertices * sizeof(D3DXVECTOR3))
2177 goto truncated_data_error;
2178 in_ptr += sizeof(DWORD) + mesh_data->num_vertices * sizeof(D3DXVECTOR3);
2180 mesh_data->num_poly_faces = *(DWORD*)in_ptr;
2181 in_ptr += sizeof(DWORD);
2183 mesh_data->num_tri_faces = 0;
2184 for (i = 0; i < mesh_data->num_poly_faces; i++)
2186 DWORD num_poly_vertices;
2187 DWORD j;
2189 if (data_size - (in_ptr - data) < sizeof(DWORD))
2190 goto truncated_data_error;
2191 num_poly_vertices = *(DWORD*)in_ptr;
2192 in_ptr += sizeof(DWORD);
2193 if (data_size - (in_ptr - data) < num_poly_vertices * sizeof(DWORD))
2194 goto truncated_data_error;
2195 if (num_poly_vertices < 3) {
2196 WARN("face %u has only %u vertices\n", i, num_poly_vertices);
2197 return E_FAIL;
2199 for (j = 0; j < num_poly_vertices; j++) {
2200 if (*(DWORD*)in_ptr >= mesh_data->num_vertices) {
2201 WARN("face %u, index %u: undefined vertex %u (only %u vertices)\n",
2202 i, j, *(DWORD*)in_ptr, mesh_data->num_vertices);
2203 return E_FAIL;
2205 in_ptr += sizeof(DWORD);
2207 mesh_data->num_tri_faces += num_poly_vertices - 2;
2210 mesh_data->fvf = D3DFVF_XYZ;
2212 mesh_data->vertices = HeapAlloc(GetProcessHeap(), 0,
2213 mesh_data->num_vertices * sizeof(*mesh_data->vertices));
2214 mesh_data->num_tri_per_face = HeapAlloc(GetProcessHeap(), 0,
2215 mesh_data->num_poly_faces * sizeof(*mesh_data->num_tri_per_face));
2216 mesh_data->indices = HeapAlloc(GetProcessHeap(), 0,
2217 (mesh_data->num_tri_faces + mesh_data->num_poly_faces * 2) * sizeof(*mesh_data->indices));
2218 if (!mesh_data->vertices || !mesh_data->num_tri_per_face || !mesh_data->indices)
2219 return E_OUTOFMEMORY;
2221 in_ptr = data + sizeof(DWORD);
2222 memcpy(mesh_data->vertices, in_ptr, mesh_data->num_vertices * sizeof(D3DXVECTOR3));
2223 in_ptr += mesh_data->num_vertices * sizeof(D3DXVECTOR3) + sizeof(DWORD);
2225 index_out_ptr = mesh_data->indices;
2226 for (i = 0; i < mesh_data->num_poly_faces; i++)
2228 DWORD count;
2230 count = *(DWORD*)in_ptr;
2231 in_ptr += sizeof(DWORD);
2232 mesh_data->num_tri_per_face[i] = count - 2;
2234 while (count--) {
2235 *index_out_ptr++ = *(DWORD*)in_ptr;
2236 in_ptr += sizeof(DWORD);
2240 while (SUCCEEDED(hr = get_next_child(filedata, &child, &type)))
2242 if (IsEqualGUID(type, &TID_D3DRMMeshNormals)) {
2243 hr = parse_normals(child, mesh_data);
2244 } else if (IsEqualGUID(type, &TID_D3DRMMeshVertexColors)) {
2245 FIXME("Mesh vertex color loading not implemented.\n");
2246 hr = E_NOTIMPL;
2247 } else if (IsEqualGUID(type, &TID_D3DRMMeshTextureCoords)) {
2248 FIXME("Mesh texture coordinate loading not implemented.\n");
2249 hr = E_NOTIMPL;
2250 } else if (IsEqualGUID(type, &TID_D3DRMMeshMaterialList) &&
2251 (provide_flags & PROVIDE_MATERIALS))
2253 hr = parse_material_list(child, mesh_data);
2254 } else if (provide_flags & PROVIDE_SKININFO) {
2255 if (IsEqualGUID(type, &DXFILEOBJ_XSkinMeshHeader)) {
2256 FIXME("Skin mesh loading not implemented.\n");
2257 hr = E_NOTIMPL;
2258 } else if (IsEqualGUID(type, &DXFILEOBJ_SkinWeights)) {
2259 /* ignored without XSkinMeshHeader */
2262 if (FAILED(hr))
2263 break;
2265 return hr == DXFILEERR_NOMOREOBJECTS ? D3D_OK : hr;
2266 truncated_data_error:
2267 WARN("truncated data (%u bytes)\n", data_size);
2268 return E_FAIL;
2271 static HRESULT generate_effects(ID3DXBuffer *materials, DWORD num_materials,
2272 ID3DXBuffer **effects)
2274 HRESULT hr;
2275 D3DXEFFECTINSTANCE *effect_ptr;
2276 BYTE *out_ptr;
2277 const D3DXMATERIAL *material_ptr = ID3DXBuffer_GetBufferPointer(materials);
2278 static const struct {
2279 const char *param_name;
2280 DWORD name_size;
2281 DWORD num_bytes;
2282 DWORD value_offset;
2283 } material_effects[] = {
2284 #define EFFECT_TABLE_ENTRY(str, field) \
2285 {str, sizeof(str), sizeof(material_ptr->MatD3D.field), offsetof(D3DXMATERIAL, MatD3D.field)}
2286 EFFECT_TABLE_ENTRY("Diffuse", Diffuse),
2287 EFFECT_TABLE_ENTRY("Power", Power),
2288 EFFECT_TABLE_ENTRY("Specular", Specular),
2289 EFFECT_TABLE_ENTRY("Emissive", Emissive),
2290 EFFECT_TABLE_ENTRY("Ambient", Ambient),
2291 #undef EFFECT_TABLE_ENTRY
2293 static const char texture_paramname[] = "Texture0@Name";
2294 DWORD buffer_size;
2295 int i;
2297 /* effects buffer layout:
2299 * D3DXEFFECTINSTANCE effects[num_materials];
2300 * for (effect in effects)
2302 * D3DXEFFECTDEFAULT defaults[effect.NumDefaults];
2303 * for (default in defaults)
2305 * *default.pParamName;
2306 * *default.pValue;
2310 buffer_size = sizeof(D3DXEFFECTINSTANCE);
2311 buffer_size += sizeof(D3DXEFFECTDEFAULT) * ARRAY_SIZE(material_effects);
2312 for (i = 0; i < ARRAY_SIZE(material_effects); i++) {
2313 buffer_size += material_effects[i].name_size;
2314 buffer_size += material_effects[i].num_bytes;
2316 buffer_size *= num_materials;
2317 for (i = 0; i < num_materials; i++) {
2318 if (material_ptr[i].pTextureFilename) {
2319 buffer_size += sizeof(D3DXEFFECTDEFAULT);
2320 buffer_size += sizeof(texture_paramname);
2321 buffer_size += strlen(material_ptr[i].pTextureFilename) + 1;
2325 hr = D3DXCreateBuffer(buffer_size, effects);
2326 if (FAILED(hr)) return hr;
2327 effect_ptr = ID3DXBuffer_GetBufferPointer(*effects);
2328 out_ptr = (BYTE*)(effect_ptr + num_materials);
2330 for (i = 0; i < num_materials; i++)
2332 int j;
2333 D3DXEFFECTDEFAULT *defaults = (D3DXEFFECTDEFAULT*)out_ptr;
2335 effect_ptr->pDefaults = defaults;
2336 effect_ptr->NumDefaults = material_ptr->pTextureFilename ? 6 : 5;
2337 out_ptr = (BYTE*)(effect_ptr->pDefaults + effect_ptr->NumDefaults);
2339 for (j = 0; j < ARRAY_SIZE(material_effects); j++)
2341 defaults->pParamName = (LPSTR)out_ptr;
2342 strcpy(defaults->pParamName, material_effects[j].param_name);
2343 defaults->pValue = defaults->pParamName + material_effects[j].name_size;
2344 defaults->Type = D3DXEDT_FLOATS;
2345 defaults->NumBytes = material_effects[j].num_bytes;
2346 memcpy(defaults->pValue, (BYTE*)material_ptr + material_effects[j].value_offset, defaults->NumBytes);
2347 out_ptr = (BYTE*)defaults->pValue + defaults->NumBytes;
2348 defaults++;
2351 if (material_ptr->pTextureFilename) {
2352 defaults->pParamName = (LPSTR)out_ptr;
2353 strcpy(defaults->pParamName, texture_paramname);
2354 defaults->pValue = defaults->pParamName + sizeof(texture_paramname);
2355 defaults->Type = D3DXEDT_STRING;
2356 defaults->NumBytes = strlen(material_ptr->pTextureFilename) + 1;
2357 strcpy(defaults->pValue, material_ptr->pTextureFilename);
2358 out_ptr = (BYTE*)defaults->pValue + defaults->NumBytes;
2360 material_ptr++;
2361 effect_ptr++;
2363 assert(out_ptr - (BYTE*)ID3DXBuffer_GetBufferPointer(*effects) == buffer_size);
2365 return D3D_OK;
2368 /* change to D3DXLoadSkinMeshFromXof when ID3DXFileData is implemented */
2369 static HRESULT load_skin_mesh_from_xof(IDirectXFileData *filedata,
2370 DWORD options,
2371 LPDIRECT3DDEVICE9 device,
2372 LPD3DXBUFFER *adjacency_out,
2373 LPD3DXBUFFER *materials_out,
2374 LPD3DXBUFFER *effects_out,
2375 DWORD *num_materials_out,
2376 LPD3DXSKININFO *skin_info_out,
2377 LPD3DXMESH *mesh_out)
2379 HRESULT hr;
2380 DWORD *index_in_ptr;
2381 struct mesh_data mesh_data;
2382 DWORD total_vertices;
2383 ID3DXMesh *d3dxmesh = NULL;
2384 ID3DXBuffer *adjacency = NULL;
2385 ID3DXBuffer *materials = NULL;
2386 ID3DXBuffer *effects = NULL;
2387 struct vertex_duplication {
2388 DWORD normal_index;
2389 struct list entry;
2390 } *duplications = NULL;
2391 int i;
2392 void *vertices = NULL;
2393 void *indices = NULL;
2394 BYTE *out_ptr;
2395 DWORD provide_flags = 0;
2397 ZeroMemory(&mesh_data, sizeof(mesh_data));
2399 if (num_materials_out || materials_out || effects_out)
2400 provide_flags |= PROVIDE_MATERIALS;
2401 if (skin_info_out)
2402 provide_flags |= PROVIDE_SKININFO;
2404 hr = parse_mesh(filedata, &mesh_data, provide_flags);
2405 if (FAILED(hr)) goto cleanup;
2407 total_vertices = mesh_data.num_vertices;
2408 if (mesh_data.fvf & D3DFVF_NORMAL) {
2409 /* duplicate vertices with multiple normals */
2410 DWORD num_face_indices = mesh_data.num_poly_faces * 2 + mesh_data.num_tri_faces;
2411 duplications = HeapAlloc(GetProcessHeap(), 0, (mesh_data.num_vertices + num_face_indices) * sizeof(*duplications));
2412 if (!duplications) {
2413 hr = E_OUTOFMEMORY;
2414 goto cleanup;
2416 for (i = 0; i < total_vertices; i++)
2418 duplications[i].normal_index = -1;
2419 list_init(&duplications[i].entry);
2421 for (i = 0; i < num_face_indices; i++) {
2422 DWORD vertex_index = mesh_data.indices[i];
2423 DWORD normal_index = mesh_data.normal_indices[i];
2424 struct vertex_duplication *dup_ptr = &duplications[vertex_index];
2426 if (dup_ptr->normal_index == -1) {
2427 dup_ptr->normal_index = normal_index;
2428 } else {
2429 D3DXVECTOR3 *new_normal = &mesh_data.normals[normal_index];
2430 struct list *dup_list = &dup_ptr->entry;
2431 while (TRUE) {
2432 D3DXVECTOR3 *cur_normal = &mesh_data.normals[dup_ptr->normal_index];
2433 if (new_normal->x == cur_normal->x &&
2434 new_normal->y == cur_normal->y &&
2435 new_normal->z == cur_normal->z)
2437 mesh_data.indices[i] = dup_ptr - duplications;
2438 break;
2439 } else if (!list_next(dup_list, &dup_ptr->entry)) {
2440 dup_ptr = &duplications[total_vertices++];
2441 dup_ptr->normal_index = normal_index;
2442 list_add_tail(dup_list, &dup_ptr->entry);
2443 mesh_data.indices[i] = dup_ptr - duplications;
2444 break;
2445 } else {
2446 dup_ptr = LIST_ENTRY(list_next(dup_list, &dup_ptr->entry),
2447 struct vertex_duplication, entry);
2454 hr = D3DXCreateMeshFVF(mesh_data.num_tri_faces, total_vertices, options, mesh_data.fvf, device, &d3dxmesh);
2455 if (FAILED(hr)) goto cleanup;
2457 hr = d3dxmesh->lpVtbl->LockVertexBuffer(d3dxmesh, D3DLOCK_DISCARD, &vertices);
2458 if (FAILED(hr)) goto cleanup;
2460 out_ptr = vertices;
2461 for (i = 0; i < mesh_data.num_vertices; i++) {
2462 *(D3DXVECTOR3*)out_ptr = mesh_data.vertices[i];
2463 out_ptr += sizeof(D3DXVECTOR3);
2464 if (mesh_data.fvf & D3DFVF_NORMAL) {
2465 if (duplications[i].normal_index == -1)
2466 ZeroMemory(out_ptr, sizeof(D3DXVECTOR3));
2467 else
2468 *(D3DXVECTOR3*)out_ptr = mesh_data.normals[duplications[i].normal_index];
2469 out_ptr += sizeof(D3DXVECTOR3);
2472 if (mesh_data.fvf & D3DFVF_NORMAL) {
2473 DWORD vertex_size = D3DXGetFVFVertexSize(mesh_data.fvf);
2474 out_ptr = vertices;
2475 for (i = 0; i < mesh_data.num_vertices; i++) {
2476 struct vertex_duplication *dup_ptr;
2477 LIST_FOR_EACH_ENTRY(dup_ptr, &duplications[i].entry, struct vertex_duplication, entry)
2479 int j = dup_ptr - duplications;
2480 BYTE *dest_vertex = (BYTE*)vertices + j * vertex_size;
2482 memcpy(dest_vertex, out_ptr, vertex_size);
2483 dest_vertex += sizeof(D3DXVECTOR3);
2484 *(D3DXVECTOR3*)dest_vertex = mesh_data.normals[dup_ptr->normal_index];
2486 out_ptr += vertex_size;
2489 d3dxmesh->lpVtbl->UnlockVertexBuffer(d3dxmesh);
2491 hr = d3dxmesh->lpVtbl->LockIndexBuffer(d3dxmesh, D3DLOCK_DISCARD, (void**)&indices);
2492 if (FAILED(hr)) goto cleanup;
2494 index_in_ptr = mesh_data.indices;
2495 #define FILL_INDEX_BUFFER(indices_var) \
2496 for (i = 0; i < mesh_data.num_poly_faces; i++) \
2498 DWORD count = mesh_data.num_tri_per_face[i]; \
2499 WORD first_index = *index_in_ptr++; \
2500 while (count--) { \
2501 *indices_var++ = first_index; \
2502 *indices_var++ = *index_in_ptr; \
2503 index_in_ptr++; \
2504 *indices_var++ = *index_in_ptr; \
2506 index_in_ptr++; \
2508 if (options & D3DXMESH_32BIT) {
2509 DWORD *dword_indices = indices;
2510 FILL_INDEX_BUFFER(dword_indices)
2511 } else {
2512 WORD *word_indices = indices;
2513 FILL_INDEX_BUFFER(word_indices)
2515 #undef FILL_INDEX_BUFFER
2516 d3dxmesh->lpVtbl->UnlockIndexBuffer(d3dxmesh);
2518 if (mesh_data.material_indices) {
2519 DWORD *attrib_buffer = NULL;
2520 hr = d3dxmesh->lpVtbl->LockAttributeBuffer(d3dxmesh, D3DLOCK_DISCARD, &attrib_buffer);
2521 if (FAILED(hr)) goto cleanup;
2522 for (i = 0; i < mesh_data.num_poly_faces; i++)
2524 DWORD count = mesh_data.num_tri_per_face[i];
2525 while (count--)
2526 *attrib_buffer++ = mesh_data.material_indices[i];
2528 d3dxmesh->lpVtbl->UnlockAttributeBuffer(d3dxmesh);
2530 hr = d3dxmesh->lpVtbl->OptimizeInplace(d3dxmesh,
2531 D3DXMESHOPT_ATTRSORT | D3DXMESHOPT_IGNOREVERTS | D3DXMESHOPT_DONOTSPLIT,
2532 NULL, NULL, NULL, NULL);
2533 if (FAILED(hr)) goto cleanup;
2536 if (mesh_data.num_materials && (materials_out || effects_out)) {
2537 DWORD buffer_size = mesh_data.num_materials * sizeof(D3DXMATERIAL);
2538 char *strings_out_ptr;
2539 D3DXMATERIAL *materials_ptr;
2541 for (i = 0; i < mesh_data.num_materials; i++) {
2542 if (mesh_data.materials[i].pTextureFilename)
2543 buffer_size += strlen(mesh_data.materials[i].pTextureFilename) + 1;
2546 hr = D3DXCreateBuffer(buffer_size, &materials);
2547 if (FAILED(hr)) goto cleanup;
2549 materials_ptr = ID3DXBuffer_GetBufferPointer(materials);
2550 memcpy(materials_ptr, mesh_data.materials, mesh_data.num_materials * sizeof(D3DXMATERIAL));
2551 strings_out_ptr = (char*)(materials_ptr + mesh_data.num_materials);
2552 for (i = 0; i < mesh_data.num_materials; i++) {
2553 if (materials_ptr[i].pTextureFilename) {
2554 strcpy(strings_out_ptr, mesh_data.materials[i].pTextureFilename);
2555 materials_ptr[i].pTextureFilename = strings_out_ptr;
2556 strings_out_ptr += strlen(mesh_data.materials[i].pTextureFilename) + 1;
2561 if (mesh_data.num_materials && effects_out) {
2562 hr = generate_effects(materials, mesh_data.num_materials, &effects);
2563 if (FAILED(hr)) goto cleanup;
2565 if (!materials_out) {
2566 ID3DXBuffer_Release(materials);
2567 materials = NULL;
2571 if (adjacency_out) {
2572 hr = D3DXCreateBuffer(mesh_data.num_tri_faces * 3 * sizeof(DWORD), &adjacency);
2573 if (FAILED(hr)) goto cleanup;
2574 hr = d3dxmesh->lpVtbl->GenerateAdjacency(d3dxmesh, 0.0f, ID3DXBuffer_GetBufferPointer(adjacency));
2575 if (FAILED(hr)) goto cleanup;
2578 *mesh_out = d3dxmesh;
2579 if (adjacency_out) *adjacency_out = adjacency;
2580 if (num_materials_out) *num_materials_out = mesh_data.num_materials;
2581 if (materials_out) *materials_out = materials;
2582 if (effects_out) *effects_out = effects;
2583 if (skin_info_out) *skin_info_out = NULL;
2585 hr = D3D_OK;
2586 cleanup:
2587 if (FAILED(hr)) {
2588 if (d3dxmesh) IUnknown_Release(d3dxmesh);
2589 if (adjacency) ID3DXBuffer_Release(adjacency);
2590 if (materials) ID3DXBuffer_Release(materials);
2591 if (effects) ID3DXBuffer_Release(effects);
2593 HeapFree(GetProcessHeap(), 0, mesh_data.vertices);
2594 HeapFree(GetProcessHeap(), 0, mesh_data.num_tri_per_face);
2595 HeapFree(GetProcessHeap(), 0, mesh_data.indices);
2596 HeapFree(GetProcessHeap(), 0, mesh_data.normals);
2597 HeapFree(GetProcessHeap(), 0, mesh_data.normal_indices);
2598 destroy_materials(&mesh_data);
2599 HeapFree(GetProcessHeap(), 0, duplications);
2600 return hr;
2603 static HRESULT filedata_get_name(IDirectXFileData *filedata, char **name)
2605 HRESULT hr;
2606 DWORD name_len;
2608 hr = IDirectXFileData_GetName(filedata, NULL, &name_len);
2609 if (FAILED(hr)) return hr;
2611 if (!name_len)
2612 name_len++;
2613 *name = HeapAlloc(GetProcessHeap(), 0, name_len);
2614 if (!*name) return E_OUTOFMEMORY;
2616 hr = IDirectXFileObject_GetName(filedata, *name, &name_len);
2617 if (FAILED(hr))
2618 HeapFree(GetProcessHeap(), 0, name);
2619 if (!name_len)
2620 (*name)[0] = 0;
2622 return hr;
2625 static HRESULT load_mesh_container(IDirectXFileData *filedata,
2626 DWORD options,
2627 LPDIRECT3DDEVICE9 device,
2628 LPD3DXALLOCATEHIERARCHY alloc_hier,
2629 D3DXMESHCONTAINER **mesh_container)
2631 HRESULT hr;
2632 ID3DXBuffer *adjacency = NULL;
2633 ID3DXBuffer *materials = NULL;
2634 ID3DXBuffer *effects = NULL;
2635 ID3DXSkinInfo *skin_info = NULL;
2636 D3DXMESHDATA mesh_data;
2637 DWORD num_materials = 0;
2638 char *name = NULL;
2640 mesh_data.Type = D3DXMESHTYPE_MESH;
2641 mesh_data.u.pMesh = NULL;
2643 hr = load_skin_mesh_from_xof(filedata, options, device,
2644 &adjacency, &materials, &effects, &num_materials,
2645 &skin_info, &mesh_data.u.pMesh);
2646 if (FAILED(hr)) return hr;
2648 hr = filedata_get_name(filedata, &name);
2649 if (FAILED(hr)) goto cleanup;
2651 hr = alloc_hier->lpVtbl->CreateMeshContainer(alloc_hier, name, &mesh_data,
2652 materials ? ID3DXBuffer_GetBufferPointer(materials) : NULL,
2653 effects ? ID3DXBuffer_GetBufferPointer(effects) : NULL,
2654 num_materials,
2655 adjacency ? ID3DXBuffer_GetBufferPointer(adjacency) : NULL,
2656 skin_info, mesh_container);
2658 cleanup:
2659 if (materials) ID3DXBuffer_Release(materials);
2660 if (effects) ID3DXBuffer_Release(effects);
2661 if (adjacency) ID3DXBuffer_Release(adjacency);
2662 if (skin_info) IUnknown_Release(skin_info);
2663 if (mesh_data.u.pMesh) IUnknown_Release(mesh_data.u.pMesh);
2664 HeapFree(GetProcessHeap(), 0, name);
2665 return hr;
2668 static HRESULT parse_transform_matrix(IDirectXFileData *filedata, D3DXMATRIX *transform)
2670 HRESULT hr;
2671 DWORD data_size;
2672 BYTE *data;
2674 /* template Matrix4x4 {
2675 * array FLOAT matrix[16];
2677 * template FrameTransformMatrix {
2678 * Matrix4x4 frameMatrix;
2682 hr = IDirectXFileData_GetData(filedata, NULL, &data_size, (void**)&data);
2683 if (FAILED(hr)) return hr;
2685 if (data_size != sizeof(D3DXMATRIX)) {
2686 WARN("incorrect data size (%u bytes)\n", data_size);
2687 return E_FAIL;
2690 memcpy(transform, data, sizeof(D3DXMATRIX));
2692 return D3D_OK;
2695 static HRESULT load_frame(IDirectXFileData *filedata,
2696 DWORD options,
2697 LPDIRECT3DDEVICE9 device,
2698 LPD3DXALLOCATEHIERARCHY alloc_hier,
2699 D3DXFRAME **frame_out)
2701 HRESULT hr;
2702 const GUID *type;
2703 IDirectXFileData *child;
2704 char *name = NULL;
2705 D3DXFRAME *frame = NULL;
2706 D3DXMESHCONTAINER **next_container;
2707 D3DXFRAME **next_child;
2709 hr = filedata_get_name(filedata, &name);
2710 if (FAILED(hr)) return hr;
2712 hr = alloc_hier->lpVtbl->CreateFrame(alloc_hier, name, frame_out);
2713 HeapFree(GetProcessHeap(), 0, name);
2714 if (FAILED(hr)) return E_FAIL;
2716 frame = *frame_out;
2717 D3DXMatrixIdentity(&frame->TransformationMatrix);
2718 next_child = &frame->pFrameFirstChild;
2719 next_container = &frame->pMeshContainer;
2721 while (SUCCEEDED(hr = get_next_child(filedata, &child, &type)))
2723 if (IsEqualGUID(type, &TID_D3DRMMesh)) {
2724 hr = load_mesh_container(child, options, device, alloc_hier, next_container);
2725 if (SUCCEEDED(hr))
2726 next_container = &(*next_container)->pNextMeshContainer;
2727 } else if (IsEqualGUID(type, &TID_D3DRMFrameTransformMatrix)) {
2728 hr = parse_transform_matrix(child, &frame->TransformationMatrix);
2729 } else if (IsEqualGUID(type, &TID_D3DRMFrame)) {
2730 hr = load_frame(child, options, device, alloc_hier, next_child);
2731 if (SUCCEEDED(hr))
2732 next_child = &(*next_child)->pFrameSibling;
2734 if (FAILED(hr)) break;
2736 if (hr == DXFILEERR_NOMOREOBJECTS)
2737 hr = D3D_OK;
2739 return hr;
2742 HRESULT WINAPI D3DXLoadMeshHierarchyFromXInMemory(LPCVOID memory,
2743 DWORD memory_size,
2744 DWORD options,
2745 LPDIRECT3DDEVICE9 device,
2746 LPD3DXALLOCATEHIERARCHY alloc_hier,
2747 LPD3DXLOADUSERDATA load_user_data,
2748 LPD3DXFRAME *frame_hierarchy,
2749 LPD3DXANIMATIONCONTROLLER *anim_controller)
2751 HRESULT hr;
2752 IDirectXFile *dxfile = NULL;
2753 IDirectXFileEnumObject *enumobj = NULL;
2754 IDirectXFileData *filedata = NULL;
2755 DXFILELOADMEMORY source;
2756 D3DXFRAME *first_frame = NULL;
2757 D3DXFRAME **next_frame = &first_frame;
2759 TRACE("(%p, %u, %x, %p, %p, %p, %p, %p)\n", memory, memory_size, options,
2760 device, alloc_hier, load_user_data, frame_hierarchy, anim_controller);
2762 if (!memory || !memory_size || !device || !frame_hierarchy || !alloc_hier)
2763 return D3DERR_INVALIDCALL;
2764 if (load_user_data || anim_controller) {
2765 if (load_user_data)
2766 FIXME("Loading user data not implemented\n");
2767 if (anim_controller)
2768 FIXME("Animation controller creation not implemented\n");
2769 return E_NOTIMPL;
2772 hr = DirectXFileCreate(&dxfile);
2773 if (FAILED(hr)) goto cleanup;
2775 hr = IDirectXFile_RegisterTemplates(dxfile, D3DRM_XTEMPLATES, D3DRM_XTEMPLATE_BYTES);
2776 if (FAILED(hr)) goto cleanup;
2778 source.lpMemory = (void*)memory;
2779 source.dSize = memory_size;
2780 hr = IDirectXFile_CreateEnumObject(dxfile, &source, DXFILELOAD_FROMMEMORY, &enumobj);
2781 if (FAILED(hr)) goto cleanup;
2783 while (SUCCEEDED(hr = IDirectXFileEnumObject_GetNextDataObject(enumobj, &filedata)))
2785 const GUID *guid = NULL;
2787 hr = IDirectXFileData_GetType(filedata, &guid);
2788 if (SUCCEEDED(hr)) {
2789 if (IsEqualGUID(guid, &TID_D3DRMMesh)) {
2790 hr = alloc_hier->lpVtbl->CreateFrame(alloc_hier, NULL, next_frame);
2791 if (FAILED(hr)) {
2792 hr = E_FAIL;
2793 goto cleanup;
2796 D3DXMatrixIdentity(&(*next_frame)->TransformationMatrix);
2798 hr = load_mesh_container(filedata, options, device, alloc_hier, &(*next_frame)->pMeshContainer);
2799 if (FAILED(hr)) goto cleanup;
2800 } else if (IsEqualGUID(guid, &TID_D3DRMFrame)) {
2801 hr = load_frame(filedata, options, device, alloc_hier, next_frame);
2802 if (FAILED(hr)) goto cleanup;
2804 while (*next_frame)
2805 next_frame = &(*next_frame)->pFrameSibling;
2808 IDirectXFileData_Release(filedata);
2809 filedata = NULL;
2810 if (FAILED(hr))
2811 goto cleanup;
2813 if (hr != DXFILEERR_NOMOREOBJECTS)
2814 goto cleanup;
2816 if (!first_frame) {
2817 hr = E_FAIL;
2818 } else if (first_frame->pFrameSibling) {
2819 D3DXFRAME *root_frame = NULL;
2820 hr = alloc_hier->lpVtbl->CreateFrame(alloc_hier, NULL, &root_frame);
2821 if (FAILED(hr)) {
2822 hr = E_FAIL;
2823 goto cleanup;
2825 D3DXMatrixIdentity(&root_frame->TransformationMatrix);
2826 root_frame->pFrameFirstChild = first_frame;
2827 *frame_hierarchy = root_frame;
2828 hr = D3D_OK;
2829 } else {
2830 *frame_hierarchy = first_frame;
2831 hr = D3D_OK;
2834 cleanup:
2835 if (FAILED(hr) && first_frame) D3DXFrameDestroy(first_frame, alloc_hier);
2836 if (filedata) IDirectXFileData_Release(filedata);
2837 if (enumobj) IDirectXFileEnumObject_Release(enumobj);
2838 if (dxfile) IDirectXFile_Release(dxfile);
2839 return hr;
2842 HRESULT WINAPI D3DXFrameDestroy(LPD3DXFRAME frame, LPD3DXALLOCATEHIERARCHY alloc_hier)
2844 HRESULT hr;
2845 BOOL last = FALSE;
2847 TRACE("(%p, %p)\n", frame, alloc_hier);
2849 if (!frame || !alloc_hier)
2850 return D3DERR_INVALIDCALL;
2852 while (!last) {
2853 D3DXMESHCONTAINER *container;
2854 D3DXFRAME *current_frame;
2856 if (frame->pFrameSibling) {
2857 current_frame = frame->pFrameSibling;
2858 frame->pFrameSibling = current_frame->pFrameSibling;
2859 current_frame->pFrameSibling = NULL;
2860 } else if (frame) {
2861 current_frame = frame;
2862 last = TRUE;
2865 if (current_frame->pFrameFirstChild) {
2866 hr = D3DXFrameDestroy(current_frame->pFrameFirstChild, alloc_hier);
2867 if (FAILED(hr)) return hr;
2868 current_frame->pFrameFirstChild = NULL;
2871 container = current_frame->pMeshContainer;
2872 while (container) {
2873 D3DXMESHCONTAINER *next_container = container->pNextMeshContainer;
2874 hr = alloc_hier->lpVtbl->DestroyMeshContainer(alloc_hier, container);
2875 if (FAILED(hr)) return hr;
2876 container = next_container;
2878 hr = alloc_hier->lpVtbl->DestroyFrame(alloc_hier, current_frame);
2879 if (FAILED(hr)) return hr;
2881 return D3D_OK;
2884 HRESULT WINAPI D3DXCreateBox(LPDIRECT3DDEVICE9 device, FLOAT width, FLOAT height,
2885 FLOAT depth, LPD3DXMESH* mesh, LPD3DXBUFFER* adjacency)
2887 FIXME("(%p, %f, %f, %f, %p, %p): stub\n", device, width, height, depth, mesh, adjacency);
2889 return E_NOTIMPL;
2892 struct vertex
2894 D3DXVECTOR3 position;
2895 D3DXVECTOR3 normal;
2898 typedef WORD face[3];
2900 struct sincos_table
2902 float *sin;
2903 float *cos;
2906 static void free_sincos_table(struct sincos_table *sincos_table)
2908 HeapFree(GetProcessHeap(), 0, sincos_table->cos);
2909 HeapFree(GetProcessHeap(), 0, sincos_table->sin);
2912 /* pre compute sine and cosine tables; caller must free */
2913 static BOOL compute_sincos_table(struct sincos_table *sincos_table, float angle_start, float angle_step, int n)
2915 float angle;
2916 int i;
2918 sincos_table->sin = HeapAlloc(GetProcessHeap(), 0, n * sizeof(*sincos_table->sin));
2919 if (!sincos_table->sin)
2921 return FALSE;
2923 sincos_table->cos = HeapAlloc(GetProcessHeap(), 0, n * sizeof(*sincos_table->cos));
2924 if (!sincos_table->cos)
2926 HeapFree(GetProcessHeap(), 0, sincos_table->sin);
2927 return FALSE;
2930 angle = angle_start;
2931 for (i = 0; i < n; i++)
2933 sincos_table->sin[i] = sin(angle);
2934 sincos_table->cos[i] = cos(angle);
2935 angle += angle_step;
2938 return TRUE;
2941 static WORD vertex_index(UINT slices, int slice, int stack)
2943 return stack*slices+slice+1;
2946 HRESULT WINAPI D3DXCreateSphere(LPDIRECT3DDEVICE9 device, FLOAT radius, UINT slices,
2947 UINT stacks, LPD3DXMESH* mesh, LPD3DXBUFFER* adjacency)
2949 DWORD number_of_vertices, number_of_faces;
2950 HRESULT hr;
2951 ID3DXMesh *sphere;
2952 struct vertex *vertices;
2953 face *faces;
2954 float phi_step, phi_start;
2955 struct sincos_table phi;
2956 float theta_step, theta, sin_theta, cos_theta;
2957 DWORD vertex, face;
2958 int slice, stack;
2960 TRACE("(%p, %f, %u, %u, %p, %p)\n", device, radius, slices, stacks, mesh, adjacency);
2962 if (!device || radius < 0.0f || slices < 2 || stacks < 2 || !mesh)
2964 return D3DERR_INVALIDCALL;
2967 if (adjacency)
2969 FIXME("Case of adjacency != NULL not implemented.\n");
2970 return E_NOTIMPL;
2973 number_of_vertices = 2 + slices * (stacks-1);
2974 number_of_faces = 2 * slices + (stacks - 2) * (2 * slices);
2976 hr = D3DXCreateMeshFVF(number_of_faces, number_of_vertices, D3DXMESH_MANAGED,
2977 D3DFVF_XYZ | D3DFVF_NORMAL, device, &sphere);
2978 if (FAILED(hr))
2980 return hr;
2983 hr = sphere->lpVtbl->LockVertexBuffer(sphere, D3DLOCK_DISCARD, (LPVOID *)&vertices);
2984 if (FAILED(hr))
2986 sphere->lpVtbl->Release(sphere);
2987 return hr;
2990 hr = sphere->lpVtbl->LockIndexBuffer(sphere, D3DLOCK_DISCARD, (LPVOID *)&faces);
2991 if (FAILED(hr))
2993 sphere->lpVtbl->UnlockVertexBuffer(sphere);
2994 sphere->lpVtbl->Release(sphere);
2995 return hr;
2998 /* phi = angle on xz plane wrt z axis */
2999 phi_step = -2 * M_PI / slices;
3000 phi_start = M_PI / 2;
3002 if (!compute_sincos_table(&phi, phi_start, phi_step, slices))
3004 sphere->lpVtbl->UnlockIndexBuffer(sphere);
3005 sphere->lpVtbl->UnlockVertexBuffer(sphere);
3006 sphere->lpVtbl->Release(sphere);
3007 return E_OUTOFMEMORY;
3010 /* theta = angle on xy plane wrt x axis */
3011 theta_step = M_PI / stacks;
3012 theta = theta_step;
3014 vertex = 0;
3015 face = 0;
3016 stack = 0;
3018 vertices[vertex].normal.x = 0.0f;
3019 vertices[vertex].normal.y = 0.0f;
3020 vertices[vertex].normal.z = 1.0f;
3021 vertices[vertex].position.x = 0.0f;
3022 vertices[vertex].position.y = 0.0f;
3023 vertices[vertex].position.z = radius;
3024 vertex++;
3026 for (stack = 0; stack < stacks - 1; stack++)
3028 sin_theta = sin(theta);
3029 cos_theta = cos(theta);
3031 for (slice = 0; slice < slices; slice++)
3033 vertices[vertex].normal.x = sin_theta * phi.cos[slice];
3034 vertices[vertex].normal.y = sin_theta * phi.sin[slice];
3035 vertices[vertex].normal.z = cos_theta;
3036 vertices[vertex].position.x = radius * sin_theta * phi.cos[slice];
3037 vertices[vertex].position.y = radius * sin_theta * phi.sin[slice];
3038 vertices[vertex].position.z = radius * cos_theta;
3039 vertex++;
3041 if (slice > 0)
3043 if (stack == 0)
3045 /* top stack is triangle fan */
3046 faces[face][0] = 0;
3047 faces[face][1] = slice + 1;
3048 faces[face][2] = slice;
3049 face++;
3051 else
3053 /* stacks in between top and bottom are quad strips */
3054 faces[face][0] = vertex_index(slices, slice-1, stack-1);
3055 faces[face][1] = vertex_index(slices, slice, stack-1);
3056 faces[face][2] = vertex_index(slices, slice-1, stack);
3057 face++;
3059 faces[face][0] = vertex_index(slices, slice, stack-1);
3060 faces[face][1] = vertex_index(slices, slice, stack);
3061 faces[face][2] = vertex_index(slices, slice-1, stack);
3062 face++;
3067 theta += theta_step;
3069 if (stack == 0)
3071 faces[face][0] = 0;
3072 faces[face][1] = 1;
3073 faces[face][2] = slice;
3074 face++;
3076 else
3078 faces[face][0] = vertex_index(slices, slice-1, stack-1);
3079 faces[face][1] = vertex_index(slices, 0, stack-1);
3080 faces[face][2] = vertex_index(slices, slice-1, stack);
3081 face++;
3083 faces[face][0] = vertex_index(slices, 0, stack-1);
3084 faces[face][1] = vertex_index(slices, 0, stack);
3085 faces[face][2] = vertex_index(slices, slice-1, stack);
3086 face++;
3090 vertices[vertex].position.x = 0.0f;
3091 vertices[vertex].position.y = 0.0f;
3092 vertices[vertex].position.z = -radius;
3093 vertices[vertex].normal.x = 0.0f;
3094 vertices[vertex].normal.y = 0.0f;
3095 vertices[vertex].normal.z = -1.0f;
3097 /* bottom stack is triangle fan */
3098 for (slice = 1; slice < slices; slice++)
3100 faces[face][0] = vertex_index(slices, slice-1, stack-1);
3101 faces[face][1] = vertex_index(slices, slice, stack-1);
3102 faces[face][2] = vertex;
3103 face++;
3106 faces[face][0] = vertex_index(slices, slice-1, stack-1);
3107 faces[face][1] = vertex_index(slices, 0, stack-1);
3108 faces[face][2] = vertex;
3110 free_sincos_table(&phi);
3111 sphere->lpVtbl->UnlockIndexBuffer(sphere);
3112 sphere->lpVtbl->UnlockVertexBuffer(sphere);
3113 *mesh = sphere;
3115 return D3D_OK;
3118 HRESULT WINAPI D3DXCreateCylinder(LPDIRECT3DDEVICE9 device, FLOAT radius1, FLOAT radius2, FLOAT length, UINT slices,
3119 UINT stacks, LPD3DXMESH* mesh, LPD3DXBUFFER* adjacency)
3121 DWORD number_of_vertices, number_of_faces;
3122 HRESULT hr;
3123 ID3DXMesh *cylinder;
3124 struct vertex *vertices;
3125 face *faces;
3126 float theta_step, theta_start;
3127 struct sincos_table theta;
3128 float delta_radius, radius, radius_step;
3129 float z, z_step, z_normal;
3130 DWORD vertex, face;
3131 int slice, stack;
3133 TRACE("(%p, %f, %f, %f, %u, %u, %p, %p)\n", device, radius1, radius2, length, slices, stacks, mesh, adjacency);
3135 if (device == NULL || radius1 < 0.0f || radius2 < 0.0f || length < 0.0f || slices < 2 || stacks < 1 || mesh == NULL)
3137 return D3DERR_INVALIDCALL;
3140 if (adjacency)
3142 FIXME("Case of adjacency != NULL not implemented.\n");
3143 return E_NOTIMPL;
3146 number_of_vertices = 2 + (slices * (3 + stacks));
3147 number_of_faces = 2 * slices + stacks * (2 * slices);
3149 hr = D3DXCreateMeshFVF(number_of_faces, number_of_vertices, D3DXMESH_MANAGED,
3150 D3DFVF_XYZ | D3DFVF_NORMAL, device, &cylinder);
3151 if (FAILED(hr))
3153 return hr;
3156 hr = cylinder->lpVtbl->LockVertexBuffer(cylinder, D3DLOCK_DISCARD, (LPVOID *)&vertices);
3157 if (FAILED(hr))
3159 cylinder->lpVtbl->Release(cylinder);
3160 return hr;
3163 hr = cylinder->lpVtbl->LockIndexBuffer(cylinder, D3DLOCK_DISCARD, (LPVOID *)&faces);
3164 if (FAILED(hr))
3166 cylinder->lpVtbl->UnlockVertexBuffer(cylinder);
3167 cylinder->lpVtbl->Release(cylinder);
3168 return hr;
3171 /* theta = angle on xy plane wrt x axis */
3172 theta_step = -2 * M_PI / slices;
3173 theta_start = M_PI / 2;
3175 if (!compute_sincos_table(&theta, theta_start, theta_step, slices))
3177 cylinder->lpVtbl->UnlockIndexBuffer(cylinder);
3178 cylinder->lpVtbl->UnlockVertexBuffer(cylinder);
3179 cylinder->lpVtbl->Release(cylinder);
3180 return E_OUTOFMEMORY;
3183 vertex = 0;
3184 face = 0;
3186 delta_radius = radius1 - radius2;
3187 radius = radius1;
3188 radius_step = delta_radius / stacks;
3190 z = -length / 2;
3191 z_step = length / stacks;
3192 z_normal = delta_radius / length;
3193 if (isnan(z_normal))
3195 z_normal = 0.0f;
3198 vertices[vertex].normal.x = 0.0f;
3199 vertices[vertex].normal.y = 0.0f;
3200 vertices[vertex].normal.z = -1.0f;
3201 vertices[vertex].position.x = 0.0f;
3202 vertices[vertex].position.y = 0.0f;
3203 vertices[vertex++].position.z = z;
3205 for (slice = 0; slice < slices; slice++, vertex++)
3207 vertices[vertex].normal.x = 0.0f;
3208 vertices[vertex].normal.y = 0.0f;
3209 vertices[vertex].normal.z = -1.0f;
3210 vertices[vertex].position.x = radius * theta.cos[slice];
3211 vertices[vertex].position.y = radius * theta.sin[slice];
3212 vertices[vertex].position.z = z;
3214 if (slice > 0)
3216 faces[face][0] = 0;
3217 faces[face][1] = slice;
3218 faces[face++][2] = slice + 1;
3222 faces[face][0] = 0;
3223 faces[face][1] = slice;
3224 faces[face++][2] = 1;
3226 for (stack = 1; stack <= stacks+1; stack++)
3228 for (slice = 0; slice < slices; slice++, vertex++)
3230 vertices[vertex].normal.x = theta.cos[slice];
3231 vertices[vertex].normal.y = theta.sin[slice];
3232 vertices[vertex].normal.z = z_normal;
3233 D3DXVec3Normalize(&vertices[vertex].normal, &vertices[vertex].normal);
3234 vertices[vertex].position.x = radius * theta.cos[slice];
3235 vertices[vertex].position.y = radius * theta.sin[slice];
3236 vertices[vertex].position.z = z;
3238 if (stack > 1 && slice > 0)
3240 faces[face][0] = vertex_index(slices, slice-1, stack-1);
3241 faces[face][1] = vertex_index(slices, slice-1, stack);
3242 faces[face++][2] = vertex_index(slices, slice, stack-1);
3244 faces[face][0] = vertex_index(slices, slice, stack-1);
3245 faces[face][1] = vertex_index(slices, slice-1, stack);
3246 faces[face++][2] = vertex_index(slices, slice, stack);
3250 if (stack > 1)
3252 faces[face][0] = vertex_index(slices, slice-1, stack-1);
3253 faces[face][1] = vertex_index(slices, slice-1, stack);
3254 faces[face++][2] = vertex_index(slices, 0, stack-1);
3256 faces[face][0] = vertex_index(slices, 0, stack-1);
3257 faces[face][1] = vertex_index(slices, slice-1, stack);
3258 faces[face++][2] = vertex_index(slices, 0, stack);
3261 if (stack < stacks + 1)
3263 z += z_step;
3264 radius -= radius_step;
3268 for (slice = 0; slice < slices; slice++, vertex++)
3270 vertices[vertex].normal.x = 0.0f;
3271 vertices[vertex].normal.y = 0.0f;
3272 vertices[vertex].normal.z = 1.0f;
3273 vertices[vertex].position.x = radius * theta.cos[slice];
3274 vertices[vertex].position.y = radius * theta.sin[slice];
3275 vertices[vertex].position.z = z;
3277 if (slice > 0)
3279 faces[face][0] = vertex_index(slices, slice-1, stack);
3280 faces[face][1] = number_of_vertices - 1;
3281 faces[face++][2] = vertex_index(slices, slice, stack);
3285 vertices[vertex].position.x = 0.0f;
3286 vertices[vertex].position.y = 0.0f;
3287 vertices[vertex].position.z = z;
3288 vertices[vertex].normal.x = 0.0f;
3289 vertices[vertex].normal.y = 0.0f;
3290 vertices[vertex].normal.z = 1.0f;
3292 faces[face][0] = vertex_index(slices, slice-1, stack);
3293 faces[face][1] = number_of_vertices - 1;
3294 faces[face][2] = vertex_index(slices, 0, stack);
3296 free_sincos_table(&theta);
3297 cylinder->lpVtbl->UnlockIndexBuffer(cylinder);
3298 cylinder->lpVtbl->UnlockVertexBuffer(cylinder);
3299 *mesh = cylinder;
3301 return D3D_OK;
3304 HRESULT WINAPI D3DXCreateTeapot(LPDIRECT3DDEVICE9 device, LPD3DXMESH *mesh, LPD3DXBUFFER* adjacency)
3306 FIXME("(%p, %p, %p): stub\n", device, mesh, adjacency);
3308 return E_NOTIMPL;
3311 HRESULT WINAPI D3DXCreateTextA(LPDIRECT3DDEVICE9 device,
3312 HDC hdc, LPCSTR text,
3313 FLOAT deviation, FLOAT extrusion,
3314 LPD3DXMESH *mesh, LPD3DXBUFFER *adjacency,
3315 LPGLYPHMETRICSFLOAT glyphmetrics)
3317 HRESULT hr;
3318 int len;
3319 LPWSTR textW;
3321 TRACE("(%p, %p, %s, %f, %f, %p, %p, %p)\n", device, hdc,
3322 debugstr_a(text), deviation, extrusion, mesh, adjacency, glyphmetrics);
3324 if (!text)
3325 return D3DERR_INVALIDCALL;
3327 len = MultiByteToWideChar(CP_ACP, 0, text, -1, NULL, 0);
3328 textW = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
3329 MultiByteToWideChar(CP_ACP, 0, text, -1, textW, len);
3331 hr = D3DXCreateTextW(device, hdc, textW, deviation, extrusion,
3332 mesh, adjacency, glyphmetrics);
3333 HeapFree(GetProcessHeap(), 0, textW);
3335 return hr;
3338 enum pointtype {
3339 POINTTYPE_CURVE = 0,
3340 POINTTYPE_CORNER,
3341 POINTTYPE_CURVE_START,
3342 POINTTYPE_CURVE_END,
3343 POINTTYPE_CURVE_MIDDLE,
3346 struct point2d
3348 D3DXVECTOR2 pos;
3349 enum pointtype corner;
3352 struct dynamic_array
3354 int count, capacity;
3355 void *items;
3358 /* is a dynamic_array */
3359 struct outline
3361 int count, capacity;
3362 struct point2d *items;
3365 /* is a dynamic_array */
3366 struct outline_array
3368 int count, capacity;
3369 struct outline *items;
3372 struct face_array
3374 int count;
3375 face *items;
3378 struct point2d_index
3380 struct outline *outline;
3381 int vertex;
3384 struct point2d_index_array
3386 int count;
3387 struct point2d_index *items;
3390 struct glyphinfo
3392 struct outline_array outlines;
3393 struct face_array faces;
3394 struct point2d_index_array ordered_vertices;
3395 float offset_x;
3398 /* is an dynamic_array */
3399 struct word_array
3401 int count, capacity;
3402 WORD *items;
3405 /* complex polygons are split into monotone polygons, which have
3406 * at most 2 intersections with the vertical sweep line */
3407 struct triangulation
3409 struct word_array vertex_stack;
3410 BOOL last_on_top, merging;
3413 /* is an dynamic_array */
3414 struct triangulation_array
3416 int count, capacity;
3417 struct triangulation *items;
3419 struct glyphinfo *glyph;
3422 static BOOL reserve(struct dynamic_array *array, int count, int itemsize)
3424 if (count > array->capacity) {
3425 void *new_buffer;
3426 int new_capacity;
3427 if (array->items && array->capacity) {
3428 new_capacity = max(array->capacity * 2, count);
3429 new_buffer = HeapReAlloc(GetProcessHeap(), 0, array->items, new_capacity * itemsize);
3430 } else {
3431 new_capacity = max(16, count);
3432 new_buffer = HeapAlloc(GetProcessHeap(), 0, new_capacity * itemsize);
3434 if (!new_buffer)
3435 return FALSE;
3436 array->items = new_buffer;
3437 array->capacity = new_capacity;
3439 return TRUE;
3442 static struct point2d *add_points(struct outline *array, int num)
3444 struct point2d *item;
3446 if (!reserve((struct dynamic_array *)array, array->count + num, sizeof(array->items[0])))
3447 return NULL;
3449 item = &array->items[array->count];
3450 array->count += num;
3451 return item;
3454 static struct outline *add_outline(struct outline_array *array)
3456 struct outline *item;
3458 if (!reserve((struct dynamic_array *)array, array->count + 1, sizeof(array->items[0])))
3459 return NULL;
3461 item = &array->items[array->count++];
3462 ZeroMemory(item, sizeof(*item));
3463 return item;
3466 static inline face *add_face(struct face_array *array)
3468 return &array->items[array->count++];
3471 static struct triangulation *add_triangulation(struct triangulation_array *array)
3473 struct triangulation *item;
3475 if (!reserve((struct dynamic_array *)array, array->count + 1, sizeof(array->items[0])))
3476 return NULL;
3478 item = &array->items[array->count++];
3479 ZeroMemory(item, sizeof(*item));
3480 return item;
3483 static HRESULT add_vertex_index(struct word_array *array, WORD vertex_index)
3485 if (!reserve((struct dynamic_array *)array, array->count + 1, sizeof(array->items[0])))
3486 return E_OUTOFMEMORY;
3488 array->items[array->count++] = vertex_index;
3489 return S_OK;
3492 /* assume fixed point numbers can be converted to float point in place */
3493 C_ASSERT(sizeof(FIXED) == sizeof(float));
3494 C_ASSERT(sizeof(POINTFX) == sizeof(D3DXVECTOR2));
3496 static inline D3DXVECTOR2 *convert_fixed_to_float(POINTFX *pt, int count, float emsquare)
3498 D3DXVECTOR2 *ret = (D3DXVECTOR2*)pt;
3499 while (count--) {
3500 D3DXVECTOR2 *pt_flt = (D3DXVECTOR2*)pt;
3501 pt_flt->x = (pt->x.value + pt->x.fract / (float)0x10000) / emsquare;
3502 pt_flt->y = (pt->y.value + pt->y.fract / (float)0x10000) / emsquare;
3503 pt++;
3505 return ret;
3508 static HRESULT add_bezier_points(struct outline *outline, const D3DXVECTOR2 *p1,
3509 const D3DXVECTOR2 *p2, const D3DXVECTOR2 *p3,
3510 float max_deviation_sq)
3512 D3DXVECTOR2 split1 = {0, 0}, split2 = {0, 0}, middle, vec;
3513 float deviation_sq;
3515 D3DXVec2Scale(&split1, D3DXVec2Add(&split1, p1, p2), 0.5f);
3516 D3DXVec2Scale(&split2, D3DXVec2Add(&split2, p2, p3), 0.5f);
3517 D3DXVec2Scale(&middle, D3DXVec2Add(&middle, &split1, &split2), 0.5f);
3519 deviation_sq = D3DXVec2LengthSq(D3DXVec2Subtract(&vec, &middle, p2));
3520 if (deviation_sq < max_deviation_sq) {
3521 struct point2d *pt = add_points(outline, 1);
3522 if (!pt) return E_OUTOFMEMORY;
3523 pt->pos = *p2;
3524 pt->corner = POINTTYPE_CURVE;
3525 /* the end point is omitted because the end line merges into the next segment of
3526 * the split bezier curve, and the end of the split bezier curve is added outside
3527 * this recursive function. */
3528 } else {
3529 HRESULT hr = add_bezier_points(outline, p1, &split1, &middle, max_deviation_sq);
3530 if (hr != S_OK) return hr;
3531 hr = add_bezier_points(outline, &middle, &split2, p3, max_deviation_sq);
3532 if (hr != S_OK) return hr;
3535 return S_OK;
3538 static inline BOOL is_direction_similar(D3DXVECTOR2 *dir1, D3DXVECTOR2 *dir2, float cos_theta)
3540 /* dot product = cos(theta) */
3541 return D3DXVec2Dot(dir1, dir2) > cos_theta;
3544 static inline D3DXVECTOR2 *unit_vec2(D3DXVECTOR2 *dir, const D3DXVECTOR2 *pt1, const D3DXVECTOR2 *pt2)
3546 return D3DXVec2Normalize(D3DXVec2Subtract(dir, pt2, pt1), dir);
3549 struct cos_table
3551 float cos_half;
3552 float cos_45;
3553 float cos_90;
3556 static BOOL attempt_line_merge(struct outline *outline,
3557 int pt_index,
3558 const D3DXVECTOR2 *nextpt,
3559 BOOL to_curve,
3560 const struct cos_table *table)
3562 D3DXVECTOR2 curdir, lastdir;
3563 struct point2d *prevpt, *pt;
3564 BOOL ret = FALSE;
3566 pt = &outline->items[pt_index];
3567 pt_index = (pt_index - 1 + outline->count) % outline->count;
3568 prevpt = &outline->items[pt_index];
3570 if (to_curve)
3571 pt->corner = pt->corner != POINTTYPE_CORNER ? POINTTYPE_CURVE_MIDDLE : POINTTYPE_CURVE_START;
3573 if (outline->count < 2)
3574 return FALSE;
3576 /* remove last point if the next line continues the last line */
3577 unit_vec2(&lastdir, &prevpt->pos, &pt->pos);
3578 unit_vec2(&curdir, &pt->pos, nextpt);
3579 if (is_direction_similar(&lastdir, &curdir, table->cos_half))
3581 outline->count--;
3582 if (pt->corner == POINTTYPE_CURVE_END)
3583 prevpt->corner = pt->corner;
3584 if (prevpt->corner == POINTTYPE_CURVE_END && to_curve)
3585 prevpt->corner = POINTTYPE_CURVE_MIDDLE;
3586 pt = prevpt;
3588 ret = TRUE;
3589 if (outline->count < 2)
3590 return ret;
3592 pt_index = (pt_index - 1 + outline->count) % outline->count;
3593 prevpt = &outline->items[pt_index];
3594 unit_vec2(&lastdir, &prevpt->pos, &pt->pos);
3595 unit_vec2(&curdir, &pt->pos, nextpt);
3597 return ret;
3600 static HRESULT create_outline(struct glyphinfo *glyph, void *raw_outline, int datasize,
3601 float max_deviation_sq, float emsquare, const struct cos_table *cos_table)
3603 TTPOLYGONHEADER *header = (TTPOLYGONHEADER *)raw_outline;
3605 while ((char *)header < (char *)raw_outline + datasize)
3607 TTPOLYCURVE *curve = (TTPOLYCURVE *)(header + 1);
3608 struct point2d *lastpt, *pt;
3609 D3DXVECTOR2 lastdir;
3610 D3DXVECTOR2 *pt_flt;
3611 int j;
3612 struct outline *outline = add_outline(&glyph->outlines);
3614 if (!outline)
3615 return E_OUTOFMEMORY;
3617 pt = add_points(outline, 1);
3618 if (!pt)
3619 return E_OUTOFMEMORY;
3620 pt_flt = convert_fixed_to_float(&header->pfxStart, 1, emsquare);
3621 pt->pos = *pt_flt;
3622 pt->corner = POINTTYPE_CORNER;
3624 if (header->dwType != TT_POLYGON_TYPE)
3625 FIXME("Unknown header type %d\n", header->dwType);
3627 while ((char *)curve < (char *)header + header->cb)
3629 D3DXVECTOR2 bezier_start = outline->items[outline->count - 1].pos;
3630 BOOL to_curve = curve->wType != TT_PRIM_LINE && curve->cpfx > 1;
3632 if (!curve->cpfx) {
3633 curve = (TTPOLYCURVE *)&curve->apfx[curve->cpfx];
3634 continue;
3637 pt_flt = convert_fixed_to_float(curve->apfx, curve->cpfx, emsquare);
3639 attempt_line_merge(outline, outline->count - 1, &pt_flt[0], to_curve, cos_table);
3641 if (to_curve)
3643 HRESULT hr;
3644 int count = curve->cpfx;
3645 j = 0;
3647 while (count > 2)
3649 D3DXVECTOR2 bezier_end;
3651 D3DXVec2Scale(&bezier_end, D3DXVec2Add(&bezier_end, &pt_flt[j], &pt_flt[j+1]), 0.5f);
3652 hr = add_bezier_points(outline, &bezier_start, &pt_flt[j], &bezier_end, max_deviation_sq);
3653 if (hr != S_OK)
3654 return hr;
3655 bezier_start = bezier_end;
3656 count--;
3657 j++;
3659 hr = add_bezier_points(outline, &bezier_start, &pt_flt[j], &pt_flt[j+1], max_deviation_sq);
3660 if (hr != S_OK)
3661 return hr;
3663 pt = add_points(outline, 1);
3664 if (!pt)
3665 return E_OUTOFMEMORY;
3666 j++;
3667 pt->pos = pt_flt[j];
3668 pt->corner = POINTTYPE_CURVE_END;
3669 } else {
3670 pt = add_points(outline, curve->cpfx);
3671 if (!pt)
3672 return E_OUTOFMEMORY;
3673 for (j = 0; j < curve->cpfx; j++)
3675 pt->pos = pt_flt[j];
3676 pt->corner = POINTTYPE_CORNER;
3677 pt++;
3681 curve = (TTPOLYCURVE *)&curve->apfx[curve->cpfx];
3684 /* remove last point if the next line continues the last line */
3685 if (outline->count >= 3) {
3686 BOOL to_curve;
3688 lastpt = &outline->items[outline->count - 1];
3689 pt = &outline->items[0];
3690 if (pt->pos.x == lastpt->pos.x && pt->pos.y == lastpt->pos.y) {
3691 if (lastpt->corner == POINTTYPE_CURVE_END)
3693 if (pt->corner == POINTTYPE_CURVE_START)
3694 pt->corner = POINTTYPE_CURVE_MIDDLE;
3695 else
3696 pt->corner = POINTTYPE_CURVE_END;
3698 outline->count--;
3699 lastpt = &outline->items[outline->count - 1];
3700 } else {
3701 /* outline closed with a line from end to start point */
3702 attempt_line_merge(outline, outline->count - 1, &pt->pos, FALSE, cos_table);
3704 lastpt = &outline->items[0];
3705 to_curve = lastpt->corner != POINTTYPE_CORNER && lastpt->corner != POINTTYPE_CURVE_END;
3706 if (lastpt->corner == POINTTYPE_CURVE_START)
3707 lastpt->corner = POINTTYPE_CORNER;
3708 pt = &outline->items[1];
3709 if (attempt_line_merge(outline, 0, &pt->pos, to_curve, cos_table))
3710 *lastpt = outline->items[outline->count];
3713 lastpt = &outline->items[outline->count - 1];
3714 pt = &outline->items[0];
3715 unit_vec2(&lastdir, &lastpt->pos, &pt->pos);
3716 for (j = 0; j < outline->count; j++)
3718 D3DXVECTOR2 curdir;
3720 lastpt = pt;
3721 pt = &outline->items[(j + 1) % outline->count];
3722 unit_vec2(&curdir, &lastpt->pos, &pt->pos);
3724 switch (lastpt->corner)
3726 case POINTTYPE_CURVE_START:
3727 case POINTTYPE_CURVE_END:
3728 if (!is_direction_similar(&lastdir, &curdir, cos_table->cos_45))
3729 lastpt->corner = POINTTYPE_CORNER;
3730 break;
3731 case POINTTYPE_CURVE_MIDDLE:
3732 if (!is_direction_similar(&lastdir, &curdir, cos_table->cos_90))
3733 lastpt->corner = POINTTYPE_CORNER;
3734 else
3735 lastpt->corner = POINTTYPE_CURVE;
3736 break;
3737 default:
3738 break;
3740 lastdir = curdir;
3743 header = (TTPOLYGONHEADER *)((char *)header + header->cb);
3745 return S_OK;
3748 /* Get the y-distance from a line to a point */
3749 static float get_line_to_point_y_distance(D3DXVECTOR2 *line_pt1,
3750 D3DXVECTOR2 *line_pt2,
3751 D3DXVECTOR2 *point)
3753 D3DXVECTOR2 line_vec = {0, 0};
3754 float line_pt_dx;
3755 float line_y;
3757 D3DXVec2Subtract(&line_vec, line_pt2, line_pt1);
3758 line_pt_dx = point->x - line_pt1->x;
3759 line_y = line_pt1->y + (line_vec.y * line_pt_dx) / line_vec.x;
3760 return point->y - line_y;
3763 static D3DXVECTOR2 *get_indexed_point(struct point2d_index *pt_idx)
3765 return &pt_idx->outline->items[pt_idx->vertex].pos;
3768 static D3DXVECTOR2 *get_ordered_vertex(struct glyphinfo *glyph, WORD index)
3770 return get_indexed_point(&glyph->ordered_vertices.items[index]);
3773 static void remove_triangulation(struct triangulation_array *array, struct triangulation *item)
3775 HeapFree(GetProcessHeap(), 0, item->vertex_stack.items);
3776 MoveMemory(item, item + 1, (char*)&array->items[array->count] - (char*)(item + 1));
3777 array->count--;
3780 static HRESULT triangulation_add_point(struct triangulation **t_ptr,
3781 struct triangulation_array *triangulations,
3782 WORD vtx_idx,
3783 BOOL to_top)
3785 struct glyphinfo *glyph = triangulations->glyph;
3786 struct triangulation *t = *t_ptr;
3787 HRESULT hr;
3788 face *face;
3789 int f1, f2;
3791 if (t->last_on_top) {
3792 f1 = 1;
3793 f2 = 2;
3794 } else {
3795 f1 = 2;
3796 f2 = 1;
3799 if (t->last_on_top != to_top && t->vertex_stack.count > 1) {
3800 /* consume all vertices on the stack */
3801 WORD last_pt = t->vertex_stack.items[0];
3802 int i;
3803 for (i = 1; i < t->vertex_stack.count; i++)
3805 face = add_face(&glyph->faces);
3806 if (!face) return E_OUTOFMEMORY;
3807 (*face)[0] = vtx_idx;
3808 (*face)[f1] = last_pt;
3809 (*face)[f2] = last_pt = t->vertex_stack.items[i];
3811 t->vertex_stack.items[0] = last_pt;
3812 t->vertex_stack.count = 1;
3813 } else if (t->vertex_stack.count > 1) {
3814 int i = t->vertex_stack.count - 1;
3815 D3DXVECTOR2 *point = get_ordered_vertex(glyph, vtx_idx);
3816 WORD top_idx = t->vertex_stack.items[i--];
3817 D3DXVECTOR2 *top_pt = get_ordered_vertex(glyph, top_idx);
3819 while (i >= 0)
3821 WORD prev_idx = t->vertex_stack.items[i--];
3822 D3DXVECTOR2 *prev_pt = get_ordered_vertex(glyph, prev_idx);
3824 if (prev_pt->x != top_pt->x &&
3825 ((to_top && get_line_to_point_y_distance(prev_pt, top_pt, point) > 0) ||
3826 (!to_top && get_line_to_point_y_distance(prev_pt, top_pt, point) < 0)))
3827 break;
3829 face = add_face(&glyph->faces);
3830 if (!face) return E_OUTOFMEMORY;
3831 (*face)[0] = vtx_idx;
3832 (*face)[f1] = prev_idx;
3833 (*face)[f2] = top_idx;
3835 top_pt = prev_pt;
3836 top_idx = prev_idx;
3837 t->vertex_stack.count--;
3840 t->last_on_top = to_top;
3842 hr = add_vertex_index(&t->vertex_stack, vtx_idx);
3844 if (hr == S_OK && t->merging) {
3845 struct triangulation *t2;
3847 t2 = to_top ? t - 1 : t + 1;
3848 t2->merging = FALSE;
3849 hr = triangulation_add_point(&t2, triangulations, vtx_idx, to_top);
3850 if (hr != S_OK) return hr;
3851 remove_triangulation(triangulations, t);
3852 if (t2 > t)
3853 t2--;
3854 *t_ptr = t2;
3856 return hr;
3859 /* check if the point is next on the outline for either the top or bottom */
3860 static D3DXVECTOR2 *triangulation_get_next_point(struct triangulation *t, struct glyphinfo *glyph, BOOL on_top)
3862 int i = t->last_on_top == on_top ? t->vertex_stack.count - 1 : 0;
3863 WORD idx = t->vertex_stack.items[i];
3864 struct point2d_index *pt_idx = &glyph->ordered_vertices.items[idx];
3865 struct outline *outline = pt_idx->outline;
3867 if (on_top)
3868 i = (pt_idx->vertex + outline->count - 1) % outline->count;
3869 else
3870 i = (pt_idx->vertex + 1) % outline->count;
3872 return &outline->items[i].pos;
3875 static int compare_vertex_indices(const void *a, const void *b)
3877 const struct point2d_index *idx1 = a, *idx2 = b;
3878 const D3DXVECTOR2 *p1 = &idx1->outline->items[idx1->vertex].pos;
3879 const D3DXVECTOR2 *p2 = &idx2->outline->items[idx2->vertex].pos;
3880 float diff = p1->x - p2->x;
3882 if (diff == 0.0f)
3883 diff = p1->y - p2->y;
3885 return diff == 0.0f ? 0 : (diff > 0.0f ? -1 : 1);
3888 static HRESULT triangulate(struct triangulation_array *triangulations)
3890 int sweep_idx;
3891 HRESULT hr;
3892 struct glyphinfo *glyph = triangulations->glyph;
3893 int nb_vertices = 0;
3894 int i;
3895 struct point2d_index *idx_ptr;
3897 for (i = 0; i < glyph->outlines.count; i++)
3898 nb_vertices += glyph->outlines.items[i].count;
3900 glyph->ordered_vertices.items = HeapAlloc(GetProcessHeap(), 0,
3901 nb_vertices * sizeof(*glyph->ordered_vertices.items));
3902 if (!glyph->ordered_vertices.items)
3903 return E_OUTOFMEMORY;
3905 idx_ptr = glyph->ordered_vertices.items;
3906 for (i = 0; i < glyph->outlines.count; i++)
3908 struct outline *outline = &glyph->outlines.items[i];
3909 int j;
3911 idx_ptr->outline = outline;
3912 idx_ptr->vertex = 0;
3913 idx_ptr++;
3914 for (j = outline->count - 1; j > 0; j--)
3916 idx_ptr->outline = outline;
3917 idx_ptr->vertex = j;
3918 idx_ptr++;
3921 glyph->ordered_vertices.count = nb_vertices;
3923 /* Native implementation seems to try to create a triangle fan from
3924 * the first outline point if the glyph only has one outline. */
3925 if (glyph->outlines.count == 1)
3927 struct outline *outline = glyph->outlines.items;
3928 D3DXVECTOR2 *base = &outline->items[0].pos;
3929 D3DXVECTOR2 *last = &outline->items[1].pos;
3930 float ccw = 0;
3932 for (i = 2; i < outline->count; i++)
3934 D3DXVECTOR2 *next = &outline->items[i].pos;
3935 D3DXVECTOR2 v1 = {0.0f, 0.0f};
3936 D3DXVECTOR2 v2 = {0.0f, 0.0f};
3938 D3DXVec2Subtract(&v1, base, last);
3939 D3DXVec2Subtract(&v2, last, next);
3940 ccw = D3DXVec2CCW(&v1, &v2);
3941 if (ccw > 0.0f)
3942 break;
3944 last = next;
3946 if (ccw <= 0)
3948 glyph->faces.items = HeapAlloc(GetProcessHeap(), 0,
3949 (outline->count - 2) * sizeof(glyph->faces.items[0]));
3950 if (!glyph->faces.items)
3951 return E_OUTOFMEMORY;
3953 glyph->faces.count = outline->count - 2;
3954 for (i = 0; i < glyph->faces.count; i++)
3956 glyph->faces.items[i][0] = 0;
3957 glyph->faces.items[i][1] = i + 1;
3958 glyph->faces.items[i][2] = i + 2;
3960 return S_OK;
3964 /* Perform 2D polygon triangulation for complex glyphs.
3965 * Triangulation is performed using a sweep line concept, from right to left,
3966 * by processing vertices in sorted order. Complex polygons are split into
3967 * monotone polygons which are triangulated separately. */
3968 /* FIXME: The order of the faces is not consistent with the native implementation. */
3970 /* Reserve space for maximum possible faces from triangulation.
3971 * # faces for outer outlines = outline->count - 2
3972 * # faces for inner outlines = outline->count + 2
3973 * There must be at least 1 outer outline. */
3974 glyph->faces.items = HeapAlloc(GetProcessHeap(), 0,
3975 (nb_vertices + glyph->outlines.count * 2 - 4) * sizeof(glyph->faces.items[0]));
3976 if (!glyph->faces.items)
3977 return E_OUTOFMEMORY;
3979 qsort(glyph->ordered_vertices.items, nb_vertices,
3980 sizeof(glyph->ordered_vertices.items[0]), compare_vertex_indices);
3981 for (sweep_idx = 0; sweep_idx < glyph->ordered_vertices.count; sweep_idx++)
3983 int start = 0;
3984 int end = triangulations->count;
3986 while (start < end)
3988 D3DXVECTOR2 *sweep_vtx = get_ordered_vertex(glyph, sweep_idx);
3989 int current = (start + end) / 2;
3990 struct triangulation *t = &triangulations->items[current];
3991 BOOL on_top_outline = FALSE;
3992 D3DXVECTOR2 *top_next, *bottom_next;
3993 WORD top_idx, bottom_idx;
3995 if (t->merging && t->last_on_top)
3996 top_next = triangulation_get_next_point(t + 1, glyph, TRUE);
3997 else
3998 top_next = triangulation_get_next_point(t, glyph, TRUE);
3999 if (sweep_vtx == top_next)
4001 if (t->merging && t->last_on_top)
4002 t++;
4003 hr = triangulation_add_point(&t, triangulations, sweep_idx, TRUE);
4004 if (hr != S_OK) return hr;
4006 if (t + 1 < &triangulations->items[triangulations->count] &&
4007 triangulation_get_next_point(t + 1, glyph, FALSE) == sweep_vtx)
4009 /* point also on bottom outline of higher triangulation */
4010 struct triangulation *t2 = t + 1;
4011 hr = triangulation_add_point(&t2, triangulations, sweep_idx, FALSE);
4012 if (hr != S_OK) return hr;
4014 t->merging = TRUE;
4015 t2->merging = TRUE;
4017 on_top_outline = TRUE;
4020 if (t->merging && !t->last_on_top)
4021 bottom_next = triangulation_get_next_point(t - 1, glyph, FALSE);
4022 else
4023 bottom_next = triangulation_get_next_point(t, glyph, FALSE);
4024 if (sweep_vtx == bottom_next)
4026 if (t->merging && !t->last_on_top)
4027 t--;
4028 if (on_top_outline) {
4029 /* outline finished */
4030 remove_triangulation(triangulations, t);
4031 break;
4034 hr = triangulation_add_point(&t, triangulations, sweep_idx, FALSE);
4035 if (hr != S_OK) return hr;
4037 if (t > triangulations->items &&
4038 triangulation_get_next_point(t - 1, glyph, TRUE) == sweep_vtx)
4040 struct triangulation *t2 = t - 1;
4041 /* point also on top outline of lower triangulation */
4042 hr = triangulation_add_point(&t2, triangulations, sweep_idx, TRUE);
4043 if (hr != S_OK) return hr;
4044 t = t2 + 1; /* t may be invalidated by triangulation merging */
4046 t->merging = TRUE;
4047 t2->merging = TRUE;
4049 break;
4051 if (on_top_outline)
4052 break;
4054 if (t->last_on_top) {
4055 top_idx = t->vertex_stack.items[t->vertex_stack.count - 1];
4056 bottom_idx = t->vertex_stack.items[0];
4057 } else {
4058 top_idx = t->vertex_stack.items[0];
4059 bottom_idx = t->vertex_stack.items[t->vertex_stack.count - 1];
4062 /* check if the point is inside or outside this polygon */
4063 if (get_line_to_point_y_distance(get_ordered_vertex(glyph, top_idx),
4064 top_next, sweep_vtx) > 0)
4065 { /* above */
4066 start = current + 1;
4067 } else if (get_line_to_point_y_distance(get_ordered_vertex(glyph, bottom_idx),
4068 bottom_next, sweep_vtx) < 0)
4069 { /* below */
4070 end = current;
4071 } else if (t->merging) {
4072 /* inside, so cancel merging */
4073 struct triangulation *t2 = t->last_on_top ? t + 1 : t - 1;
4074 t->merging = FALSE;
4075 t2->merging = FALSE;
4076 hr = triangulation_add_point(&t, triangulations, sweep_idx, t->last_on_top);
4077 if (hr != S_OK) return hr;
4078 hr = triangulation_add_point(&t2, triangulations, sweep_idx, t2->last_on_top);
4079 if (hr != S_OK) return hr;
4080 break;
4081 } else {
4082 /* inside, so split polygon into two monotone parts */
4083 struct triangulation *t2 = add_triangulation(triangulations);
4084 if (!t2) return E_OUTOFMEMORY;
4085 MoveMemory(t + 1, t, (char*)(t2 + 1) - (char*)t);
4086 if (t->last_on_top) {
4087 t2 = t + 1;
4088 } else {
4089 t2 = t;
4090 t++;
4093 ZeroMemory(&t2->vertex_stack, sizeof(t2->vertex_stack));
4094 hr = add_vertex_index(&t2->vertex_stack, t->vertex_stack.items[t->vertex_stack.count - 1]);
4095 if (hr != S_OK) return hr;
4096 hr = add_vertex_index(&t2->vertex_stack, sweep_idx);
4097 if (hr != S_OK) return hr;
4098 t2->last_on_top = !t->last_on_top;
4100 hr = triangulation_add_point(&t, triangulations, sweep_idx, t->last_on_top);
4101 if (hr != S_OK) return hr;
4102 break;
4105 if (start >= end)
4107 struct triangulation *t;
4108 struct triangulation *t2 = add_triangulation(triangulations);
4109 if (!t2) return E_OUTOFMEMORY;
4110 t = &triangulations->items[start];
4111 MoveMemory(t + 1, t, (char*)(t2 + 1) - (char*)t);
4112 ZeroMemory(t, sizeof(*t));
4113 hr = add_vertex_index(&t->vertex_stack, sweep_idx);
4114 if (hr != S_OK) return hr;
4117 return S_OK;
4120 HRESULT WINAPI D3DXCreateTextW(LPDIRECT3DDEVICE9 device,
4121 HDC hdc, LPCWSTR text,
4122 FLOAT deviation, FLOAT extrusion,
4123 LPD3DXMESH *mesh_ptr, LPD3DXBUFFER *adjacency,
4124 LPGLYPHMETRICSFLOAT glyphmetrics)
4126 HRESULT hr;
4127 ID3DXMesh *mesh = NULL;
4128 DWORD nb_vertices, nb_faces;
4129 DWORD nb_front_faces, nb_corners, nb_outline_points;
4130 struct vertex *vertices = NULL;
4131 face *faces = NULL;
4132 int textlen = 0;
4133 float offset_x;
4134 LOGFONTW lf;
4135 OUTLINETEXTMETRICW otm;
4136 HFONT font = NULL, oldfont = NULL;
4137 const MAT2 identity = {{0, 1}, {0, 0}, {0, 0}, {0, 1}};
4138 void *raw_outline = NULL;
4139 int bufsize = 0;
4140 struct glyphinfo *glyphs = NULL;
4141 GLYPHMETRICS gm;
4142 struct triangulation_array triangulations = {0, 0, NULL};
4143 int i;
4144 struct vertex *vertex_ptr;
4145 face *face_ptr;
4146 float max_deviation_sq;
4147 const struct cos_table cos_table = {
4148 cos(D3DXToRadian(0.5f)),
4149 cos(D3DXToRadian(45.0f)),
4150 cos(D3DXToRadian(90.0f)),
4152 int f1, f2;
4154 TRACE("(%p, %p, %s, %f, %f, %p, %p, %p)\n", device, hdc,
4155 debugstr_w(text), deviation, extrusion, mesh_ptr, adjacency, glyphmetrics);
4157 if (!device || !hdc || !text || !*text || deviation < 0.0f || extrusion < 0.0f || !mesh_ptr)
4158 return D3DERR_INVALIDCALL;
4160 if (adjacency)
4162 FIXME("Case of adjacency != NULL not implemented.\n");
4163 return E_NOTIMPL;
4166 if (!GetObjectW(GetCurrentObject(hdc, OBJ_FONT), sizeof(lf), &lf) ||
4167 !GetOutlineTextMetricsW(hdc, sizeof(otm), &otm))
4169 return D3DERR_INVALIDCALL;
4172 if (deviation == 0.0f)
4173 deviation = 1.0f / otm.otmEMSquare;
4174 max_deviation_sq = deviation * deviation;
4176 lf.lfHeight = otm.otmEMSquare;
4177 lf.lfWidth = 0;
4178 font = CreateFontIndirectW(&lf);
4179 if (!font) {
4180 hr = E_OUTOFMEMORY;
4181 goto error;
4183 oldfont = SelectObject(hdc, font);
4185 textlen = strlenW(text);
4186 for (i = 0; i < textlen; i++)
4188 int datasize = GetGlyphOutlineW(hdc, text[i], GGO_NATIVE, &gm, 0, NULL, &identity);
4189 if (datasize < 0)
4190 return D3DERR_INVALIDCALL;
4191 if (bufsize < datasize)
4192 bufsize = datasize;
4194 if (!bufsize) { /* e.g. text == " " */
4195 hr = D3DERR_INVALIDCALL;
4196 goto error;
4199 glyphs = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, textlen * sizeof(*glyphs));
4200 raw_outline = HeapAlloc(GetProcessHeap(), 0, bufsize);
4201 if (!glyphs || !raw_outline) {
4202 hr = E_OUTOFMEMORY;
4203 goto error;
4206 offset_x = 0.0f;
4207 for (i = 0; i < textlen; i++)
4209 /* get outline points from data returned from GetGlyphOutline */
4210 int datasize;
4212 glyphs[i].offset_x = offset_x;
4214 datasize = GetGlyphOutlineW(hdc, text[i], GGO_NATIVE, &gm, bufsize, raw_outline, &identity);
4215 hr = create_outline(&glyphs[i], raw_outline, datasize,
4216 max_deviation_sq, otm.otmEMSquare, &cos_table);
4217 if (hr != S_OK) goto error;
4219 triangulations.glyph = &glyphs[i];
4220 hr = triangulate(&triangulations);
4221 if (hr != S_OK) goto error;
4222 if (triangulations.count) {
4223 ERR("%d incomplete triangulations of glyph (%u).\n", triangulations.count, text[i]);
4224 triangulations.count = 0;
4227 if (glyphmetrics)
4229 glyphmetrics[i].gmfBlackBoxX = gm.gmBlackBoxX / (float)otm.otmEMSquare;
4230 glyphmetrics[i].gmfBlackBoxY = gm.gmBlackBoxY / (float)otm.otmEMSquare;
4231 glyphmetrics[i].gmfptGlyphOrigin.x = gm.gmptGlyphOrigin.x / (float)otm.otmEMSquare;
4232 glyphmetrics[i].gmfptGlyphOrigin.y = gm.gmptGlyphOrigin.y / (float)otm.otmEMSquare;
4233 glyphmetrics[i].gmfCellIncX = gm.gmCellIncX / (float)otm.otmEMSquare;
4234 glyphmetrics[i].gmfCellIncY = gm.gmCellIncY / (float)otm.otmEMSquare;
4236 offset_x += gm.gmCellIncX / (float)otm.otmEMSquare;
4239 /* corner points need an extra vertex for the different side faces normals */
4240 nb_corners = 0;
4241 nb_outline_points = 0;
4242 nb_front_faces = 0;
4243 for (i = 0; i < textlen; i++)
4245 int j;
4246 nb_outline_points += glyphs[i].ordered_vertices.count;
4247 nb_front_faces += glyphs[i].faces.count;
4248 for (j = 0; j < glyphs[i].outlines.count; j++)
4250 int k;
4251 struct outline *outline = &glyphs[i].outlines.items[j];
4252 nb_corners++; /* first outline point always repeated as a corner */
4253 for (k = 1; k < outline->count; k++)
4254 if (outline->items[k].corner)
4255 nb_corners++;
4259 nb_vertices = (nb_outline_points + nb_corners) * 2 + nb_outline_points * 2;
4260 nb_faces = nb_outline_points * 2 + nb_front_faces * 2;
4263 hr = D3DXCreateMeshFVF(nb_faces, nb_vertices, D3DXMESH_MANAGED,
4264 D3DFVF_XYZ | D3DFVF_NORMAL, device, &mesh);
4265 if (FAILED(hr))
4266 goto error;
4268 hr = mesh->lpVtbl->LockVertexBuffer(mesh, D3DLOCK_DISCARD, (LPVOID *)&vertices);
4269 if (FAILED(hr))
4270 goto error;
4272 hr = mesh->lpVtbl->LockIndexBuffer(mesh, D3DLOCK_DISCARD, (LPVOID *)&faces);
4273 if (FAILED(hr))
4274 goto error;
4276 /* convert 2D vertices and faces into 3D mesh */
4277 vertex_ptr = vertices;
4278 face_ptr = faces;
4279 if (extrusion == 0.0f) {
4280 f1 = 1;
4281 f2 = 2;
4282 } else {
4283 f1 = 2;
4284 f2 = 1;
4286 for (i = 0; i < textlen; i++)
4288 int j;
4289 int count;
4290 struct vertex *back_vertices;
4291 face *back_faces;
4293 /* side vertices and faces */
4294 for (j = 0; j < glyphs[i].outlines.count; j++)
4296 struct vertex *outline_vertices = vertex_ptr;
4297 struct outline *outline = &glyphs[i].outlines.items[j];
4298 int k;
4299 struct point2d *prevpt = &outline->items[outline->count - 1];
4300 struct point2d *pt = &outline->items[0];
4302 for (k = 1; k <= outline->count; k++)
4304 struct vertex vtx;
4305 struct point2d *nextpt = &outline->items[k % outline->count];
4306 WORD vtx_idx = vertex_ptr - vertices;
4307 D3DXVECTOR2 vec;
4309 if (pt->corner == POINTTYPE_CURVE_START)
4310 D3DXVec2Subtract(&vec, &pt->pos, &prevpt->pos);
4311 else if (pt->corner)
4312 D3DXVec2Subtract(&vec, &nextpt->pos, &pt->pos);
4313 else
4314 D3DXVec2Subtract(&vec, &nextpt->pos, &prevpt->pos);
4315 D3DXVec2Normalize(&vec, &vec);
4316 vtx.normal.x = -vec.y;
4317 vtx.normal.y = vec.x;
4318 vtx.normal.z = 0;
4320 vtx.position.x = pt->pos.x + glyphs[i].offset_x;
4321 vtx.position.y = pt->pos.y;
4322 vtx.position.z = 0;
4323 *vertex_ptr++ = vtx;
4325 vtx.position.z = -extrusion;
4326 *vertex_ptr++ = vtx;
4328 vtx.position.x = nextpt->pos.x + glyphs[i].offset_x;
4329 vtx.position.y = nextpt->pos.y;
4330 if (pt->corner && nextpt->corner && nextpt->corner != POINTTYPE_CURVE_END) {
4331 vtx.position.z = -extrusion;
4332 *vertex_ptr++ = vtx;
4333 vtx.position.z = 0;
4334 *vertex_ptr++ = vtx;
4336 (*face_ptr)[0] = vtx_idx;
4337 (*face_ptr)[1] = vtx_idx + 2;
4338 (*face_ptr)[2] = vtx_idx + 1;
4339 face_ptr++;
4341 (*face_ptr)[0] = vtx_idx;
4342 (*face_ptr)[1] = vtx_idx + 3;
4343 (*face_ptr)[2] = vtx_idx + 2;
4344 face_ptr++;
4345 } else {
4346 if (nextpt->corner) {
4347 if (nextpt->corner == POINTTYPE_CURVE_END) {
4348 D3DXVECTOR2 *nextpt2 = &outline->items[(k + 1) % outline->count].pos;
4349 D3DXVec2Subtract(&vec, nextpt2, &nextpt->pos);
4350 } else {
4351 D3DXVec2Subtract(&vec, &nextpt->pos, &pt->pos);
4353 D3DXVec2Normalize(&vec, &vec);
4354 vtx.normal.x = -vec.y;
4355 vtx.normal.y = vec.x;
4357 vtx.position.z = 0;
4358 *vertex_ptr++ = vtx;
4359 vtx.position.z = -extrusion;
4360 *vertex_ptr++ = vtx;
4363 (*face_ptr)[0] = vtx_idx;
4364 (*face_ptr)[1] = vtx_idx + 3;
4365 (*face_ptr)[2] = vtx_idx + 1;
4366 face_ptr++;
4368 (*face_ptr)[0] = vtx_idx;
4369 (*face_ptr)[1] = vtx_idx + 2;
4370 (*face_ptr)[2] = vtx_idx + 3;
4371 face_ptr++;
4374 prevpt = pt;
4375 pt = nextpt;
4377 if (!pt->corner) {
4378 *vertex_ptr++ = *outline_vertices++;
4379 *vertex_ptr++ = *outline_vertices++;
4383 /* back vertices and faces */
4384 back_faces = face_ptr;
4385 back_vertices = vertex_ptr;
4386 for (j = 0; j < glyphs[i].ordered_vertices.count; j++)
4388 D3DXVECTOR2 *pt = get_ordered_vertex(&glyphs[i], j);
4389 vertex_ptr->position.x = pt->x + glyphs[i].offset_x;
4390 vertex_ptr->position.y = pt->y;
4391 vertex_ptr->position.z = 0;
4392 vertex_ptr->normal.x = 0;
4393 vertex_ptr->normal.y = 0;
4394 vertex_ptr->normal.z = 1;
4395 vertex_ptr++;
4397 count = back_vertices - vertices;
4398 for (j = 0; j < glyphs[i].faces.count; j++)
4400 face *f = &glyphs[i].faces.items[j];
4401 (*face_ptr)[0] = (*f)[0] + count;
4402 (*face_ptr)[1] = (*f)[1] + count;
4403 (*face_ptr)[2] = (*f)[2] + count;
4404 face_ptr++;
4407 /* front vertices and faces */
4408 j = count = vertex_ptr - back_vertices;
4409 while (j--)
4411 vertex_ptr->position.x = back_vertices->position.x;
4412 vertex_ptr->position.y = back_vertices->position.y;
4413 vertex_ptr->position.z = -extrusion;
4414 vertex_ptr->normal.x = 0;
4415 vertex_ptr->normal.y = 0;
4416 vertex_ptr->normal.z = extrusion == 0.0f ? 1.0f : -1.0f;
4417 vertex_ptr++;
4418 back_vertices++;
4420 j = face_ptr - back_faces;
4421 while (j--)
4423 (*face_ptr)[0] = (*back_faces)[0] + count;
4424 (*face_ptr)[1] = (*back_faces)[f1] + count;
4425 (*face_ptr)[2] = (*back_faces)[f2] + count;
4426 face_ptr++;
4427 back_faces++;
4431 *mesh_ptr = mesh;
4432 hr = D3D_OK;
4433 error:
4434 if (mesh) {
4435 if (faces) mesh->lpVtbl->UnlockIndexBuffer(mesh);
4436 if (vertices) mesh->lpVtbl->UnlockVertexBuffer(mesh);
4437 if (hr != D3D_OK) mesh->lpVtbl->Release(mesh);
4439 if (glyphs) {
4440 for (i = 0; i < textlen; i++)
4442 int j;
4443 for (j = 0; j < glyphs[i].outlines.count; j++)
4444 HeapFree(GetProcessHeap(), 0, glyphs[i].outlines.items[j].items);
4445 HeapFree(GetProcessHeap(), 0, glyphs[i].outlines.items);
4446 HeapFree(GetProcessHeap(), 0, glyphs[i].faces.items);
4447 HeapFree(GetProcessHeap(), 0, glyphs[i].ordered_vertices.items);
4449 HeapFree(GetProcessHeap(), 0, glyphs);
4451 if (triangulations.items) {
4452 int i;
4453 for (i = 0; i < triangulations.count; i++)
4454 HeapFree(GetProcessHeap(), 0, triangulations.items[i].vertex_stack.items);
4455 HeapFree(GetProcessHeap(), 0, triangulations.items);
4457 HeapFree(GetProcessHeap(), 0, raw_outline);
4458 if (oldfont) SelectObject(hdc, oldfont);
4459 if (font) DeleteObject(font);
4461 return hr;