1 #ifdef __REACTOS__ 2 #include "precomp.h" 3 #include <rmxftmpl.h> 4 #else 5 /* 6 * Mesh operations specific to D3DX9. 7 * 8 * Copyright (C) 2005 Henri Verbeet 9 * Copyright (C) 2006 Ivan Gyurdiev 10 * Copyright (C) 2009 David Adam 11 * Copyright (C) 2010 Tony Wasserka 12 * Copyright (C) 2011 Dylan Smith 13 * Copyright (C) 2011 Michael Mc Donnell 14 * Copyright (C) 2013 Christian Costa 15 * 16 * This library is free software; you can redistribute it and/or 17 * modify it under the terms of the GNU Lesser General Public 18 * License as published by the Free Software Foundation; either 19 * version 2.1 of the License, or (at your option) any later version. 20 * 21 * This library is distributed in the hope that it will be useful, 22 * but WITHOUT ANY WARRANTY; without even the implied warranty of 23 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 24 * Lesser General Public License for more details. 25 * 26 * You should have received a copy of the GNU Lesser General Public 27 * License along with this library; if not, write to the Free Software 28 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA 29 */ 30 31 32 #include <assert.h> 33 #include <float.h> 34 35 #include "d3dx9_private.h" 36 #undef MAKE_DDHRESULT 37 #include "dxfile.h" 38 #include "rmxfguid.h" 39 #include "rmxftmpl.h" 40 #include "wine/list.h" 41 #endif /* __REACTOS__ */ 42 43 WINE_DEFAULT_DEBUG_CHANNEL(d3dx); 44 45 struct d3dx9_mesh 46 { 47 ID3DXMesh ID3DXMesh_iface; 48 LONG ref; 49 50 DWORD numfaces; 51 DWORD numvertices; 52 DWORD options; 53 DWORD fvf; 54 IDirect3DDevice9 *device; 55 D3DVERTEXELEMENT9 cached_declaration[MAX_FVF_DECL_SIZE]; 56 IDirect3DVertexDeclaration9 *vertex_declaration; 57 UINT vertex_declaration_size; 58 UINT num_elem; 59 IDirect3DVertexBuffer9 *vertex_buffer; 60 IDirect3DIndexBuffer9 *index_buffer; 61 DWORD *attrib_buffer; 62 int attrib_buffer_lock_count; 63 DWORD attrib_table_size; 64 D3DXATTRIBUTERANGE *attrib_table; 65 }; 66 67 static const UINT d3dx_decltype_size[] = 68 { 69 /* D3DDECLTYPE_FLOAT1 */ sizeof(FLOAT), 70 /* D3DDECLTYPE_FLOAT2 */ sizeof(D3DXVECTOR2), 71 /* D3DDECLTYPE_FLOAT3 */ sizeof(D3DXVECTOR3), 72 /* D3DDECLTYPE_FLOAT4 */ sizeof(D3DXVECTOR4), 73 /* D3DDECLTYPE_D3DCOLOR */ sizeof(D3DCOLOR), 74 /* D3DDECLTYPE_UBYTE4 */ 4 * sizeof(BYTE), 75 /* D3DDECLTYPE_SHORT2 */ 2 * sizeof(SHORT), 76 /* D3DDECLTYPE_SHORT4 */ 4 * sizeof(SHORT), 77 /* D3DDECLTYPE_UBYTE4N */ 4 * sizeof(BYTE), 78 /* D3DDECLTYPE_SHORT2N */ 2 * sizeof(SHORT), 79 /* D3DDECLTYPE_SHORT4N */ 4 * sizeof(SHORT), 80 /* D3DDECLTYPE_USHORT2N */ 2 * sizeof(USHORT), 81 /* D3DDECLTYPE_USHORT4N */ 4 * sizeof(USHORT), 82 /* D3DDECLTYPE_UDEC3 */ 4, /* 3 * 10 bits + 2 padding */ 83 /* D3DDECLTYPE_DEC3N */ 4, 84 /* D3DDECLTYPE_FLOAT16_2 */ 2 * sizeof(D3DXFLOAT16), 85 /* D3DDECLTYPE_FLOAT16_4 */ 4 * sizeof(D3DXFLOAT16), 86 }; 87 88 static inline struct d3dx9_mesh *impl_from_ID3DXMesh(ID3DXMesh *iface) 89 { 90 return CONTAINING_RECORD(iface, struct d3dx9_mesh, ID3DXMesh_iface); 91 } 92 93 static HRESULT WINAPI d3dx9_mesh_QueryInterface(ID3DXMesh *iface, REFIID riid, void **out) 94 { 95 TRACE("iface %p, riid %s, out %p.\n", iface, debugstr_guid(riid), out); 96 97 if (IsEqualGUID(riid, &IID_IUnknown) || 98 IsEqualGUID(riid, &IID_ID3DXBaseMesh) || 99 IsEqualGUID(riid, &IID_ID3DXMesh)) 100 { 101 iface->lpVtbl->AddRef(iface); 102 *out = iface; 103 return S_OK; 104 } 105 106 WARN("Interface %s not found.\n", debugstr_guid(riid)); 107 108 return E_NOINTERFACE; 109 } 110 111 static ULONG WINAPI d3dx9_mesh_AddRef(ID3DXMesh *iface) 112 { 113 struct d3dx9_mesh *mesh = impl_from_ID3DXMesh(iface); 114 ULONG refcount = InterlockedIncrement(&mesh->ref); 115 116 TRACE("%p increasing refcount to %u.\n", mesh, refcount); 117 118 return refcount; 119 } 120 121 static ULONG WINAPI d3dx9_mesh_Release(ID3DXMesh *iface) 122 { 123 struct d3dx9_mesh *mesh = impl_from_ID3DXMesh(iface); 124 ULONG refcount = InterlockedDecrement(&mesh->ref); 125 126 TRACE("%p decreasing refcount to %u.\n", mesh, refcount); 127 128 if (!refcount) 129 { 130 IDirect3DIndexBuffer9_Release(mesh->index_buffer); 131 IDirect3DVertexBuffer9_Release(mesh->vertex_buffer); 132 if (mesh->vertex_declaration) 133 IDirect3DVertexDeclaration9_Release(mesh->vertex_declaration); 134 IDirect3DDevice9_Release(mesh->device); 135 HeapFree(GetProcessHeap(), 0, mesh->attrib_buffer); 136 HeapFree(GetProcessHeap(), 0, mesh->attrib_table); 137 HeapFree(GetProcessHeap(), 0, mesh); 138 } 139 140 return refcount; 141 } 142 143 static HRESULT WINAPI d3dx9_mesh_DrawSubset(ID3DXMesh *iface, DWORD attrib_id) 144 { 145 struct d3dx9_mesh *This = impl_from_ID3DXMesh(iface); 146 HRESULT hr; 147 DWORD face_start; 148 DWORD face_end = 0; 149 DWORD vertex_size; 150 151 TRACE("iface %p, attrib_id %u.\n", iface, attrib_id); 152 153 if (!This->vertex_declaration) 154 { 155 WARN("Can't draw a mesh with an invalid vertex declaration.\n"); 156 return E_FAIL; 157 } 158 159 vertex_size = iface->lpVtbl->GetNumBytesPerVertex(iface); 160 161 hr = IDirect3DDevice9_SetVertexDeclaration(This->device, This->vertex_declaration); 162 if (FAILED(hr)) return hr; 163 hr = IDirect3DDevice9_SetStreamSource(This->device, 0, This->vertex_buffer, 0, vertex_size); 164 if (FAILED(hr)) return hr; 165 hr = IDirect3DDevice9_SetIndices(This->device, This->index_buffer); 166 if (FAILED(hr)) return hr; 167 168 while (face_end < This->numfaces) 169 { 170 for (face_start = face_end; face_start < This->numfaces; face_start++) 171 { 172 if (This->attrib_buffer[face_start] == attrib_id) 173 break; 174 } 175 if (face_start >= This->numfaces) 176 break; 177 for (face_end = face_start + 1; face_end < This->numfaces; face_end++) 178 { 179 if (This->attrib_buffer[face_end] != attrib_id) 180 break; 181 } 182 183 hr = IDirect3DDevice9_DrawIndexedPrimitive(This->device, D3DPT_TRIANGLELIST, 184 0, 0, This->numvertices, face_start * 3, face_end - face_start); 185 if (FAILED(hr)) return hr; 186 } 187 188 return D3D_OK; 189 } 190 191 static DWORD WINAPI d3dx9_mesh_GetNumFaces(ID3DXMesh *iface) 192 { 193 struct d3dx9_mesh *mesh = impl_from_ID3DXMesh(iface); 194 195 TRACE("iface %p.\n", iface); 196 197 return mesh->numfaces; 198 } 199 200 static DWORD WINAPI d3dx9_mesh_GetNumVertices(ID3DXMesh *iface) 201 { 202 struct d3dx9_mesh *mesh = impl_from_ID3DXMesh(iface); 203 204 TRACE("iface %p.\n", iface); 205 206 return mesh->numvertices; 207 } 208 209 static DWORD WINAPI d3dx9_mesh_GetFVF(ID3DXMesh *iface) 210 { 211 struct d3dx9_mesh *mesh = impl_from_ID3DXMesh(iface); 212 213 TRACE("iface %p.\n", iface); 214 215 return mesh->fvf; 216 } 217 218 static void copy_declaration(D3DVERTEXELEMENT9 *dst, const D3DVERTEXELEMENT9 *src, UINT num_elem) 219 { 220 memcpy(dst, src, num_elem * sizeof(*src)); 221 } 222 223 static HRESULT WINAPI d3dx9_mesh_GetDeclaration(ID3DXMesh *iface, D3DVERTEXELEMENT9 declaration[MAX_FVF_DECL_SIZE]) 224 { 225 struct d3dx9_mesh *mesh = impl_from_ID3DXMesh(iface); 226 227 TRACE("iface %p, declaration %p.\n", iface, declaration); 228 229 if (!declaration) 230 return D3DERR_INVALIDCALL; 231 232 copy_declaration(declaration, mesh->cached_declaration, mesh->num_elem); 233 234 return D3D_OK; 235 } 236 237 static DWORD WINAPI d3dx9_mesh_GetNumBytesPerVertex(ID3DXMesh *iface) 238 { 239 struct d3dx9_mesh *mesh = impl_from_ID3DXMesh(iface); 240 241 TRACE("iface %p.\n", iface); 242 243 return mesh->vertex_declaration_size; 244 } 245 246 static DWORD WINAPI d3dx9_mesh_GetOptions(ID3DXMesh *iface) 247 { 248 struct d3dx9_mesh *mesh = impl_from_ID3DXMesh(iface); 249 250 TRACE("iface %p.\n", iface); 251 252 return mesh->options; 253 } 254 255 static HRESULT WINAPI d3dx9_mesh_GetDevice(struct ID3DXMesh *iface, struct IDirect3DDevice9 **device) 256 { 257 struct d3dx9_mesh *mesh = impl_from_ID3DXMesh(iface); 258 259 TRACE("iface %p, device %p.\n", iface, device); 260 261 if (!device) 262 return D3DERR_INVALIDCALL; 263 *device = mesh->device; 264 IDirect3DDevice9_AddRef(mesh->device); 265 266 return D3D_OK; 267 } 268 269 static HRESULT WINAPI d3dx9_mesh_CloneMeshFVF(struct ID3DXMesh *iface, DWORD options, DWORD fvf, 270 struct IDirect3DDevice9 *device, struct ID3DXMesh **clone_mesh) 271 { 272 HRESULT hr; 273 D3DVERTEXELEMENT9 declaration[MAX_FVF_DECL_SIZE]; 274 275 TRACE("iface %p, options %#x, fvf %#x, device %p, clone_mesh %p.\n", 276 iface, options, fvf, device, clone_mesh); 277 278 if (FAILED(hr = D3DXDeclaratorFromFVF(fvf, declaration))) 279 return hr; 280 281 return iface->lpVtbl->CloneMesh(iface, options, declaration, device, clone_mesh); 282 } 283 284 static FLOAT scale_clamp_ubyten(FLOAT value) 285 { 286 value = value * UCHAR_MAX; 287 288 if (value < 0.0f) 289 { 290 return 0.0f; 291 } 292 else 293 { 294 if (value > UCHAR_MAX) /* Clamp at 255 */ 295 return UCHAR_MAX; 296 else 297 return value; 298 } 299 } 300 301 static FLOAT scale_clamp_shortn(FLOAT value) 302 { 303 value = value * SHRT_MAX; 304 305 /* The tests show that the range is SHRT_MIN + 1 to SHRT_MAX. */ 306 if (value <= SHRT_MIN) 307 { 308 return SHRT_MIN + 1; 309 } 310 else if (value > SHRT_MAX) 311 { 312 return SHRT_MAX; 313 } 314 else 315 { 316 return value; 317 } 318 } 319 320 static FLOAT scale_clamp_ushortn(FLOAT value) 321 { 322 value = value * USHRT_MAX; 323 324 if (value < 0.0f) 325 { 326 return 0.0f; 327 } 328 else 329 { 330 if (value > USHRT_MAX) /* Clamp at 65535 */ 331 return USHRT_MAX; 332 else 333 return value; 334 } 335 } 336 337 static INT simple_round(FLOAT value) 338 { 339 int res = (INT)(value + 0.5f); 340 341 return res; 342 } 343 344 static void convert_float4(BYTE *dst, const D3DXVECTOR4 *src, D3DDECLTYPE type_dst) 345 { 346 BOOL fixme_once = FALSE; 347 348 switch (type_dst) 349 { 350 case D3DDECLTYPE_FLOAT1: 351 { 352 FLOAT *dst_ptr = (FLOAT*)dst; 353 *dst_ptr = src->x; 354 break; 355 } 356 case D3DDECLTYPE_FLOAT2: 357 { 358 D3DXVECTOR2 *dst_ptr = (D3DXVECTOR2*)dst; 359 dst_ptr->x = src->x; 360 dst_ptr->y = src->y; 361 break; 362 } 363 case D3DDECLTYPE_FLOAT3: 364 { 365 D3DXVECTOR3 *dst_ptr = (D3DXVECTOR3*)dst; 366 dst_ptr->x = src->x; 367 dst_ptr->y = src->y; 368 dst_ptr->z = src->z; 369 break; 370 } 371 case D3DDECLTYPE_FLOAT4: 372 { 373 D3DXVECTOR4 *dst_ptr = (D3DXVECTOR4*)dst; 374 dst_ptr->x = src->x; 375 dst_ptr->y = src->y; 376 dst_ptr->z = src->z; 377 dst_ptr->w = src->w; 378 break; 379 } 380 case D3DDECLTYPE_D3DCOLOR: 381 { 382 dst[0] = (BYTE)simple_round(scale_clamp_ubyten(src->z)); 383 dst[1] = (BYTE)simple_round(scale_clamp_ubyten(src->y)); 384 dst[2] = (BYTE)simple_round(scale_clamp_ubyten(src->x)); 385 dst[3] = (BYTE)simple_round(scale_clamp_ubyten(src->w)); 386 break; 387 } 388 case D3DDECLTYPE_UBYTE4: 389 { 390 dst[0] = src->x < 0.0f ? 0 : (BYTE)simple_round(src->x); 391 dst[1] = src->y < 0.0f ? 0 : (BYTE)simple_round(src->y); 392 dst[2] = src->z < 0.0f ? 0 : (BYTE)simple_round(src->z); 393 dst[3] = src->w < 0.0f ? 0 : (BYTE)simple_round(src->w); 394 break; 395 } 396 case D3DDECLTYPE_SHORT2: 397 { 398 SHORT *dst_ptr = (SHORT*)dst; 399 dst_ptr[0] = (SHORT)simple_round(src->x); 400 dst_ptr[1] = (SHORT)simple_round(src->y); 401 break; 402 } 403 case D3DDECLTYPE_SHORT4: 404 { 405 SHORT *dst_ptr = (SHORT*)dst; 406 dst_ptr[0] = (SHORT)simple_round(src->x); 407 dst_ptr[1] = (SHORT)simple_round(src->y); 408 dst_ptr[2] = (SHORT)simple_round(src->z); 409 dst_ptr[3] = (SHORT)simple_round(src->w); 410 break; 411 } 412 case D3DDECLTYPE_UBYTE4N: 413 { 414 dst[0] = (BYTE)simple_round(scale_clamp_ubyten(src->x)); 415 dst[1] = (BYTE)simple_round(scale_clamp_ubyten(src->y)); 416 dst[2] = (BYTE)simple_round(scale_clamp_ubyten(src->z)); 417 dst[3] = (BYTE)simple_round(scale_clamp_ubyten(src->w)); 418 break; 419 } 420 case D3DDECLTYPE_SHORT2N: 421 { 422 SHORT *dst_ptr = (SHORT*)dst; 423 dst_ptr[0] = (SHORT)simple_round(scale_clamp_shortn(src->x)); 424 dst_ptr[1] = (SHORT)simple_round(scale_clamp_shortn(src->y)); 425 break; 426 } 427 case D3DDECLTYPE_SHORT4N: 428 { 429 SHORT *dst_ptr = (SHORT*)dst; 430 dst_ptr[0] = (SHORT)simple_round(scale_clamp_shortn(src->x)); 431 dst_ptr[1] = (SHORT)simple_round(scale_clamp_shortn(src->y)); 432 dst_ptr[2] = (SHORT)simple_round(scale_clamp_shortn(src->z)); 433 dst_ptr[3] = (SHORT)simple_round(scale_clamp_shortn(src->w)); 434 break; 435 } 436 case D3DDECLTYPE_USHORT2N: 437 { 438 USHORT *dst_ptr = (USHORT*)dst; 439 dst_ptr[0] = (USHORT)simple_round(scale_clamp_ushortn(src->x)); 440 dst_ptr[1] = (USHORT)simple_round(scale_clamp_ushortn(src->y)); 441 break; 442 } 443 case D3DDECLTYPE_USHORT4N: 444 { 445 USHORT *dst_ptr = (USHORT*)dst; 446 dst_ptr[0] = (USHORT)simple_round(scale_clamp_ushortn(src->x)); 447 dst_ptr[1] = (USHORT)simple_round(scale_clamp_ushortn(src->y)); 448 dst_ptr[2] = (USHORT)simple_round(scale_clamp_ushortn(src->z)); 449 dst_ptr[3] = (USHORT)simple_round(scale_clamp_ushortn(src->w)); 450 break; 451 } 452 case D3DDECLTYPE_FLOAT16_2: 453 { 454 D3DXFloat32To16Array((D3DXFLOAT16*)dst, (FLOAT*)src, 2); 455 break; 456 } 457 case D3DDECLTYPE_FLOAT16_4: 458 { 459 D3DXFloat32To16Array((D3DXFLOAT16*)dst, (FLOAT*)src, 4); 460 break; 461 } 462 default: 463 if (!fixme_once++) 464 FIXME("Conversion from D3DDECLTYPE_FLOAT4 to %d not implemented.\n", type_dst); 465 break; 466 } 467 } 468 469 static void convert_component(BYTE *dst, BYTE *src, D3DDECLTYPE type_dst, D3DDECLTYPE type_src) 470 { 471 BOOL fixme_once = FALSE; 472 473 switch (type_src) 474 { 475 case D3DDECLTYPE_FLOAT1: 476 { 477 FLOAT *src_ptr = (FLOAT*)src; 478 D3DXVECTOR4 src_float4 = {*src_ptr, 0.0f, 0.0f, 1.0f}; 479 convert_float4(dst, &src_float4, type_dst); 480 break; 481 } 482 case D3DDECLTYPE_FLOAT2: 483 { 484 D3DXVECTOR2 *src_ptr = (D3DXVECTOR2*)src; 485 D3DXVECTOR4 src_float4 = {src_ptr->x, src_ptr->y, 0.0f, 1.0f}; 486 convert_float4(dst, &src_float4, type_dst); 487 break; 488 } 489 case D3DDECLTYPE_FLOAT3: 490 { 491 D3DXVECTOR3 *src_ptr = (D3DXVECTOR3*)src; 492 D3DXVECTOR4 src_float4 = {src_ptr->x, src_ptr->y, src_ptr->z, 1.0f}; 493 convert_float4(dst, &src_float4, type_dst); 494 break; 495 } 496 case D3DDECLTYPE_FLOAT4: 497 { 498 D3DXVECTOR4 *src_ptr = (D3DXVECTOR4*)src; 499 D3DXVECTOR4 src_float4 = {src_ptr->x, src_ptr->y, src_ptr->z, src_ptr->w}; 500 convert_float4(dst, &src_float4, type_dst); 501 break; 502 } 503 case D3DDECLTYPE_D3DCOLOR: 504 { 505 D3DXVECTOR4 src_float4 = 506 { 507 (FLOAT)src[2]/UCHAR_MAX, 508 (FLOAT)src[1]/UCHAR_MAX, 509 (FLOAT)src[0]/UCHAR_MAX, 510 (FLOAT)src[3]/UCHAR_MAX 511 }; 512 convert_float4(dst, &src_float4, type_dst); 513 break; 514 } 515 case D3DDECLTYPE_UBYTE4: 516 { 517 D3DXVECTOR4 src_float4 = {src[0], src[1], src[2], src[3]}; 518 convert_float4(dst, &src_float4, type_dst); 519 break; 520 } 521 case D3DDECLTYPE_SHORT2: 522 { 523 SHORT *src_ptr = (SHORT*)src; 524 D3DXVECTOR4 src_float4 = {src_ptr[0], src_ptr[1], 0.0f, 1.0f}; 525 convert_float4(dst, &src_float4, type_dst); 526 break; 527 } 528 case D3DDECLTYPE_SHORT4: 529 { 530 SHORT *src_ptr = (SHORT*)src; 531 D3DXVECTOR4 src_float4 = {src_ptr[0], src_ptr[1], src_ptr[2], src_ptr[3]}; 532 convert_float4(dst, &src_float4, type_dst); 533 break; 534 } 535 case D3DDECLTYPE_UBYTE4N: 536 { 537 D3DXVECTOR4 src_float4 = 538 { 539 (FLOAT)src[0]/UCHAR_MAX, 540 (FLOAT)src[1]/UCHAR_MAX, 541 (FLOAT)src[2]/UCHAR_MAX, 542 (FLOAT)src[3]/UCHAR_MAX 543 }; 544 convert_float4(dst, &src_float4, type_dst); 545 break; 546 } 547 case D3DDECLTYPE_SHORT2N: 548 { 549 SHORT *src_ptr = (SHORT*)src; 550 D3DXVECTOR4 src_float4 = {(FLOAT)src_ptr[0]/SHRT_MAX, (FLOAT)src_ptr[1]/SHRT_MAX, 0.0f, 1.0f}; 551 convert_float4(dst, &src_float4, type_dst); 552 break; 553 } 554 case D3DDECLTYPE_SHORT4N: 555 { 556 SHORT *src_ptr = (SHORT*)src; 557 D3DXVECTOR4 src_float4 = 558 { 559 (FLOAT)src_ptr[0]/SHRT_MAX, 560 (FLOAT)src_ptr[1]/SHRT_MAX, 561 (FLOAT)src_ptr[2]/SHRT_MAX, 562 (FLOAT)src_ptr[3]/SHRT_MAX 563 }; 564 convert_float4(dst, &src_float4, type_dst); 565 break; 566 } 567 case D3DDECLTYPE_FLOAT16_2: 568 { 569 D3DXVECTOR4 src_float4 = {0.0f, 0.0f, 0.0f, 1.0f}; 570 D3DXFloat16To32Array((FLOAT*)&src_float4, (D3DXFLOAT16*)src, 2); 571 convert_float4(dst, &src_float4, type_dst); 572 break; 573 } 574 case D3DDECLTYPE_FLOAT16_4: 575 { 576 D3DXVECTOR4 src_float4; 577 D3DXFloat16To32Array((FLOAT*)&src_float4, (D3DXFLOAT16*)src, 4); 578 convert_float4(dst, &src_float4, type_dst); 579 break; 580 } 581 default: 582 if (!fixme_once++) 583 FIXME("Conversion of D3DDECLTYPE %d to %d not implemented.\n", type_src, type_dst); 584 break; 585 } 586 } 587 588 static INT get_equivalent_declaration_index(D3DVERTEXELEMENT9 orig_declaration, D3DVERTEXELEMENT9 *declaration) 589 { 590 INT i; 591 592 for (i = 0; declaration[i].Stream != 0xff; i++) 593 { 594 if (orig_declaration.Usage == declaration[i].Usage 595 && orig_declaration.UsageIndex == declaration[i].UsageIndex) 596 { 597 return i; 598 } 599 } 600 601 return -1; 602 } 603 604 static HRESULT convert_vertex_buffer(ID3DXMesh *mesh_dst, ID3DXMesh *mesh_src) 605 { 606 HRESULT hr; 607 D3DVERTEXELEMENT9 orig_declaration[MAX_FVF_DECL_SIZE] = {D3DDECL_END()}; 608 D3DVERTEXELEMENT9 declaration[MAX_FVF_DECL_SIZE] = {D3DDECL_END()}; 609 BYTE *vb_dst = NULL; 610 BYTE *vb_src = NULL; 611 UINT i; 612 UINT num_vertices = mesh_src->lpVtbl->GetNumVertices(mesh_src); 613 UINT dst_vertex_size = mesh_dst->lpVtbl->GetNumBytesPerVertex(mesh_dst); 614 UINT src_vertex_size = mesh_src->lpVtbl->GetNumBytesPerVertex(mesh_src); 615 616 hr = mesh_src->lpVtbl->GetDeclaration(mesh_src, orig_declaration); 617 if (FAILED(hr)) return hr; 618 hr = mesh_dst->lpVtbl->GetDeclaration(mesh_dst, declaration); 619 if (FAILED(hr)) return hr; 620 621 hr = mesh_src->lpVtbl->LockVertexBuffer(mesh_src, D3DLOCK_READONLY, (void**)&vb_src); 622 if (FAILED(hr)) goto cleanup; 623 hr = mesh_dst->lpVtbl->LockVertexBuffer(mesh_dst, 0, (void**)&vb_dst); 624 if (FAILED(hr)) goto cleanup; 625 626 /* Clear all new fields by clearing the entire vertex buffer. */ 627 memset(vb_dst, 0, num_vertices * dst_vertex_size); 628 629 for (i = 0; orig_declaration[i].Stream != 0xff; i++) 630 { 631 INT eq_idx = get_equivalent_declaration_index(orig_declaration[i], declaration); 632 633 if (eq_idx >= 0) 634 { 635 UINT j; 636 for (j = 0; j < num_vertices; j++) 637 { 638 UINT idx_dst = dst_vertex_size * j + declaration[eq_idx].Offset; 639 UINT idx_src = src_vertex_size * j + orig_declaration[i].Offset; 640 UINT type_size = d3dx_decltype_size[orig_declaration[i].Type]; 641 642 if (orig_declaration[i].Type == declaration[eq_idx].Type) 643 memcpy(&vb_dst[idx_dst], &vb_src[idx_src], type_size); 644 else 645 convert_component(&vb_dst[idx_dst], &vb_src[idx_src], declaration[eq_idx].Type, orig_declaration[i].Type); 646 } 647 } 648 } 649 650 hr = D3D_OK; 651 cleanup: 652 if (vb_dst) mesh_dst->lpVtbl->UnlockVertexBuffer(mesh_dst); 653 if (vb_src) mesh_src->lpVtbl->UnlockVertexBuffer(mesh_src); 654 655 return hr; 656 } 657 658 static BOOL declaration_equals(const D3DVERTEXELEMENT9 *declaration1, const D3DVERTEXELEMENT9 *declaration2) 659 { 660 UINT size1 = 0, size2 = 0; 661 662 /* Find the size of each declaration */ 663 while (declaration1[size1].Stream != 0xff) size1++; 664 while (declaration2[size2].Stream != 0xff) size2++; 665 666 /* If not same size then they are definitely not equal */ 667 if (size1 != size2) 668 return FALSE; 669 670 /* Check that all components are the same */ 671 if (memcmp(declaration1, declaration2, size1*sizeof(*declaration1)) == 0) 672 return TRUE; 673 674 return FALSE; 675 } 676 677 static HRESULT WINAPI d3dx9_mesh_CloneMesh(struct ID3DXMesh *iface, DWORD options, 678 const D3DVERTEXELEMENT9 *declaration, struct IDirect3DDevice9 *device, struct ID3DXMesh **clone_mesh_out) 679 { 680 struct d3dx9_mesh *This = impl_from_ID3DXMesh(iface); 681 struct d3dx9_mesh *cloned_this; 682 ID3DXMesh *clone_mesh; 683 D3DVERTEXELEMENT9 orig_declaration[MAX_FVF_DECL_SIZE] = { D3DDECL_END() }; 684 void *data_in, *data_out; 685 DWORD vertex_size; 686 HRESULT hr; 687 BOOL same_declaration; 688 689 TRACE("iface %p, options %#x, declaration %p, device %p, clone_mesh_out %p.\n", 690 iface, options, declaration, device, clone_mesh_out); 691 692 if (!clone_mesh_out) 693 return D3DERR_INVALIDCALL; 694 695 hr = iface->lpVtbl->GetDeclaration(iface, orig_declaration); 696 if (FAILED(hr)) return hr; 697 698 hr = D3DXCreateMesh(This->numfaces, This->numvertices, options & ~D3DXMESH_VB_SHARE, 699 declaration, device, &clone_mesh); 700 if (FAILED(hr)) return hr; 701 702 cloned_this = impl_from_ID3DXMesh(clone_mesh); 703 vertex_size = clone_mesh->lpVtbl->GetNumBytesPerVertex(clone_mesh); 704 same_declaration = declaration_equals(declaration, orig_declaration); 705 706 if (options & D3DXMESH_VB_SHARE) { 707 if (!same_declaration) { 708 hr = D3DERR_INVALIDCALL; 709 goto error; 710 } 711 IDirect3DVertexBuffer9_AddRef(This->vertex_buffer); 712 /* FIXME: refactor to avoid creating a new vertex buffer */ 713 IDirect3DVertexBuffer9_Release(cloned_this->vertex_buffer); 714 cloned_this->vertex_buffer = This->vertex_buffer; 715 } else if (same_declaration) { 716 hr = iface->lpVtbl->LockVertexBuffer(iface, D3DLOCK_READONLY, &data_in); 717 if (FAILED(hr)) goto error; 718 hr = clone_mesh->lpVtbl->LockVertexBuffer(clone_mesh, 0, &data_out); 719 if (FAILED(hr)) { 720 iface->lpVtbl->UnlockVertexBuffer(iface); 721 goto error; 722 } 723 memcpy(data_out, data_in, This->numvertices * vertex_size); 724 clone_mesh->lpVtbl->UnlockVertexBuffer(clone_mesh); 725 iface->lpVtbl->UnlockVertexBuffer(iface); 726 } else { 727 hr = convert_vertex_buffer(clone_mesh, iface); 728 if (FAILED(hr)) goto error; 729 } 730 731 hr = iface->lpVtbl->LockIndexBuffer(iface, D3DLOCK_READONLY, &data_in); 732 if (FAILED(hr)) goto error; 733 hr = clone_mesh->lpVtbl->LockIndexBuffer(clone_mesh, 0, &data_out); 734 if (FAILED(hr)) { 735 iface->lpVtbl->UnlockIndexBuffer(iface); 736 goto error; 737 } 738 if ((options ^ This->options) & D3DXMESH_32BIT) { 739 DWORD i; 740 if (options & D3DXMESH_32BIT) { 741 for (i = 0; i < This->numfaces * 3; i++) 742 ((DWORD*)data_out)[i] = ((WORD*)data_in)[i]; 743 } else { 744 for (i = 0; i < This->numfaces * 3; i++) 745 ((WORD*)data_out)[i] = ((DWORD*)data_in)[i]; 746 } 747 } else { 748 memcpy(data_out, data_in, This->numfaces * 3 * (options & D3DXMESH_32BIT ? 4 : 2)); 749 } 750 clone_mesh->lpVtbl->UnlockIndexBuffer(clone_mesh); 751 iface->lpVtbl->UnlockIndexBuffer(iface); 752 753 memcpy(cloned_this->attrib_buffer, This->attrib_buffer, This->numfaces * sizeof(*This->attrib_buffer)); 754 755 if (This->attrib_table_size) 756 { 757 cloned_this->attrib_table_size = This->attrib_table_size; 758 cloned_this->attrib_table = HeapAlloc(GetProcessHeap(), 0, This->attrib_table_size * sizeof(*This->attrib_table)); 759 if (!cloned_this->attrib_table) { 760 hr = E_OUTOFMEMORY; 761 goto error; 762 } 763 memcpy(cloned_this->attrib_table, This->attrib_table, This->attrib_table_size * sizeof(*This->attrib_table)); 764 } 765 766 *clone_mesh_out = clone_mesh; 767 768 return D3D_OK; 769 error: 770 IUnknown_Release(clone_mesh); 771 return hr; 772 } 773 774 static HRESULT WINAPI d3dx9_mesh_GetVertexBuffer(struct ID3DXMesh *iface, 775 struct IDirect3DVertexBuffer9 **vertex_buffer) 776 { 777 struct d3dx9_mesh *mesh = impl_from_ID3DXMesh(iface); 778 779 TRACE("iface %p, vertex_buffer %p.\n", iface, vertex_buffer); 780 781 if (!vertex_buffer) 782 return D3DERR_INVALIDCALL; 783 *vertex_buffer = mesh->vertex_buffer; 784 IDirect3DVertexBuffer9_AddRef(mesh->vertex_buffer); 785 786 return D3D_OK; 787 } 788 789 static HRESULT WINAPI d3dx9_mesh_GetIndexBuffer(struct ID3DXMesh *iface, 790 struct IDirect3DIndexBuffer9 **index_buffer) 791 { 792 struct d3dx9_mesh *mesh = impl_from_ID3DXMesh(iface); 793 794 TRACE("iface %p, index_buffer %p.\n", iface, index_buffer); 795 796 if (!index_buffer) 797 return D3DERR_INVALIDCALL; 798 *index_buffer = mesh->index_buffer; 799 IDirect3DIndexBuffer9_AddRef(mesh->index_buffer); 800 801 return D3D_OK; 802 } 803 804 static HRESULT WINAPI d3dx9_mesh_LockVertexBuffer(ID3DXMesh *iface, DWORD flags, void **data) 805 { 806 struct d3dx9_mesh *mesh = impl_from_ID3DXMesh(iface); 807 808 TRACE("iface %p, flags %#x, data %p.\n", iface, flags, data); 809 810 return IDirect3DVertexBuffer9_Lock(mesh->vertex_buffer, 0, 0, data, flags); 811 } 812 813 static HRESULT WINAPI d3dx9_mesh_UnlockVertexBuffer(ID3DXMesh *iface) 814 { 815 struct d3dx9_mesh *mesh = impl_from_ID3DXMesh(iface); 816 817 TRACE("iface %p.\n", iface); 818 819 return IDirect3DVertexBuffer9_Unlock(mesh->vertex_buffer); 820 } 821 822 static HRESULT WINAPI d3dx9_mesh_LockIndexBuffer(ID3DXMesh *iface, DWORD flags, void **data) 823 { 824 struct d3dx9_mesh *mesh = impl_from_ID3DXMesh(iface); 825 826 TRACE("iface %p, flags %#x, data %p.\n", iface, flags, data); 827 828 return IDirect3DIndexBuffer9_Lock(mesh->index_buffer, 0, 0, data, flags); 829 } 830 831 static HRESULT WINAPI d3dx9_mesh_UnlockIndexBuffer(ID3DXMesh *iface) 832 { 833 struct d3dx9_mesh *mesh = impl_from_ID3DXMesh(iface); 834 835 TRACE("iface %p.\n", iface); 836 837 return IDirect3DIndexBuffer9_Unlock(mesh->index_buffer); 838 } 839 840 /* FIXME: This looks just wrong, we never check *attrib_table_size before 841 * copying the data. */ 842 static HRESULT WINAPI d3dx9_mesh_GetAttributeTable(ID3DXMesh *iface, 843 D3DXATTRIBUTERANGE *attrib_table, DWORD *attrib_table_size) 844 { 845 struct d3dx9_mesh *mesh = impl_from_ID3DXMesh(iface); 846 847 TRACE("iface %p, attrib_table %p, attrib_table_size %p.\n", 848 iface, attrib_table, attrib_table_size); 849 850 if (attrib_table_size) 851 *attrib_table_size = mesh->attrib_table_size; 852 853 if (attrib_table) 854 memcpy(attrib_table, mesh->attrib_table, mesh->attrib_table_size * sizeof(*attrib_table)); 855 856 return D3D_OK; 857 } 858 859 struct edge_face 860 { 861 struct list entry; 862 DWORD v2; 863 DWORD face; 864 }; 865 866 struct edge_face_map 867 { 868 struct list *lists; 869 struct edge_face *entries; 870 }; 871 872 /* Builds up a map of which face a new edge belongs to. That way the adjacency 873 * of another edge can be looked up. An edge has an adjacent face if there 874 * is an edge going in the opposite direction in the map. For example if the 875 * edge (v1, v2) belongs to face 4, and there is a mapping (v2, v1)->7, then 876 * face 4 and 7 are adjacent. 877 * 878 * Each edge might have been replaced with another edge, or none at all. There 879 * is at most one edge to face mapping, i.e. an edge can only belong to one 880 * face. 881 */ 882 static HRESULT init_edge_face_map(struct edge_face_map *edge_face_map, const DWORD *index_buffer, 883 const DWORD *point_reps, DWORD num_faces) 884 { 885 DWORD face, edge; 886 DWORD i; 887 888 edge_face_map->lists = HeapAlloc(GetProcessHeap(), 0, 3 * num_faces * sizeof(*edge_face_map->lists)); 889 if (!edge_face_map->lists) return E_OUTOFMEMORY; 890 891 edge_face_map->entries = HeapAlloc(GetProcessHeap(), 0, 3 * num_faces * sizeof(*edge_face_map->entries)); 892 if (!edge_face_map->entries) return E_OUTOFMEMORY; 893 894 895 /* Initialize all lists */ 896 for (i = 0; i < 3 * num_faces; i++) 897 { 898 list_init(&edge_face_map->lists[i]); 899 } 900 /* Build edge face mapping */ 901 for (face = 0; face < num_faces; face++) 902 { 903 for (edge = 0; edge < 3; edge++) 904 { 905 DWORD v1 = index_buffer[3*face + edge]; 906 DWORD v2 = index_buffer[3*face + (edge+1)%3]; 907 DWORD new_v1 = point_reps[v1]; /* What v1 has been replaced with */ 908 DWORD new_v2 = point_reps[v2]; 909 910 if (v1 != v2) /* Only map non-collapsed edges */ 911 { 912 i = 3*face + edge; 913 edge_face_map->entries[i].v2 = new_v2; 914 edge_face_map->entries[i].face = face; 915 list_add_head(&edge_face_map->lists[new_v1], &edge_face_map->entries[i].entry); 916 } 917 } 918 } 919 920 return D3D_OK; 921 } 922 923 static DWORD find_adjacent_face(struct edge_face_map *edge_face_map, DWORD vertex1, DWORD vertex2, DWORD num_faces) 924 { 925 struct edge_face *edge_face_ptr; 926 927 LIST_FOR_EACH_ENTRY(edge_face_ptr, &edge_face_map->lists[vertex2], struct edge_face, entry) 928 { 929 if (edge_face_ptr->v2 == vertex1) 930 return edge_face_ptr->face; 931 } 932 933 return -1; 934 } 935 936 static DWORD *generate_identity_point_reps(DWORD num_vertices) 937 { 938 DWORD *id_point_reps; 939 DWORD i; 940 941 id_point_reps = HeapAlloc(GetProcessHeap(), 0, num_vertices * sizeof(*id_point_reps)); 942 if (!id_point_reps) 943 return NULL; 944 945 for (i = 0; i < num_vertices; i++) 946 { 947 id_point_reps[i] = i; 948 } 949 950 return id_point_reps; 951 } 952 953 static HRESULT WINAPI d3dx9_mesh_ConvertPointRepsToAdjacency(ID3DXMesh *iface, 954 const DWORD *point_reps, DWORD *adjacency) 955 { 956 HRESULT hr; 957 DWORD num_faces = iface->lpVtbl->GetNumFaces(iface); 958 DWORD num_vertices = iface->lpVtbl->GetNumVertices(iface); 959 DWORD options = iface->lpVtbl->GetOptions(iface); 960 BOOL indices_are_16_bit = !(options & D3DXMESH_32BIT); 961 DWORD *ib = NULL; 962 void *ib_ptr = NULL; 963 DWORD face; 964 DWORD edge; 965 struct edge_face_map edge_face_map = {0}; 966 const DWORD *point_reps_ptr = NULL; 967 DWORD *id_point_reps = NULL; 968 969 TRACE("iface %p, point_reps %p, adjacency %p.\n", iface, point_reps, adjacency); 970 971 if (!adjacency) return D3DERR_INVALIDCALL; 972 973 if (!point_reps) /* Identity point reps */ 974 { 975 id_point_reps = generate_identity_point_reps(num_vertices); 976 if (!id_point_reps) 977 { 978 hr = E_OUTOFMEMORY; 979 goto cleanup; 980 } 981 982 point_reps_ptr = id_point_reps; 983 } 984 else 985 { 986 point_reps_ptr = point_reps; 987 } 988 989 hr = iface->lpVtbl->LockIndexBuffer(iface, D3DLOCK_READONLY, &ib_ptr); 990 if (FAILED(hr)) goto cleanup; 991 992 if (indices_are_16_bit) 993 { 994 /* Widen 16 bit to 32 bit */ 995 DWORD i; 996 WORD *ib_16bit = ib_ptr; 997 ib = HeapAlloc(GetProcessHeap(), 0, 3 * num_faces * sizeof(DWORD)); 998 if (!ib) 999 { 1000 hr = E_OUTOFMEMORY; 1001 goto cleanup; 1002 } 1003 for (i = 0; i < 3 * num_faces; i++) 1004 { 1005 ib[i] = ib_16bit[i]; 1006 } 1007 } 1008 else 1009 { 1010 ib = ib_ptr; 1011 } 1012 1013 hr = init_edge_face_map(&edge_face_map, ib, point_reps_ptr, num_faces); 1014 if (FAILED(hr)) goto cleanup; 1015 1016 /* Create adjacency */ 1017 for (face = 0; face < num_faces; face++) 1018 { 1019 for (edge = 0; edge < 3; edge++) 1020 { 1021 DWORD v1 = ib[3*face + edge]; 1022 DWORD v2 = ib[3*face + (edge+1)%3]; 1023 DWORD new_v1 = point_reps_ptr[v1]; 1024 DWORD new_v2 = point_reps_ptr[v2]; 1025 DWORD adj_face; 1026 1027 adj_face = find_adjacent_face(&edge_face_map, new_v1, new_v2, num_faces); 1028 adjacency[3*face + edge] = adj_face; 1029 } 1030 } 1031 1032 hr = D3D_OK; 1033 cleanup: 1034 HeapFree(GetProcessHeap(), 0, id_point_reps); 1035 if (indices_are_16_bit) HeapFree(GetProcessHeap(), 0, ib); 1036 HeapFree(GetProcessHeap(), 0, edge_face_map.lists); 1037 HeapFree(GetProcessHeap(), 0, edge_face_map.entries); 1038 if(ib_ptr) iface->lpVtbl->UnlockIndexBuffer(iface); 1039 return hr; 1040 } 1041 1042 /* ConvertAdjacencyToPointReps helper function. 1043 * 1044 * Goes around the edges of each face and replaces the vertices in any adjacent 1045 * face's edge with its own vertices(if its vertices have a lower index). This 1046 * way as few as possible low index vertices are shared among the faces. The 1047 * re-ordered index buffer is stored in new_indices. 1048 * 1049 * The vertices in a point representation must be ordered sequentially, e.g. 1050 * index 5 holds the index of the vertex that replaces vertex 5, i.e. if 1051 * vertex 5 is replaced by vertex 3 then index 5 would contain 3. If no vertex 1052 * replaces it, then it contains the same number as the index itself, e.g. 1053 * index 5 would contain 5. */ 1054 static HRESULT propagate_face_vertices(const DWORD *adjacency, DWORD *point_reps, 1055 const DWORD *indices, DWORD *new_indices, DWORD face, DWORD numfaces) 1056 { 1057 const unsigned int VERTS_PER_FACE = 3; 1058 DWORD edge, opp_edge; 1059 DWORD face_base = VERTS_PER_FACE * face; 1060 1061 for (edge = 0; edge < VERTS_PER_FACE; edge++) 1062 { 1063 DWORD adj_face = adjacency[face_base + edge]; 1064 DWORD adj_face_base; 1065 DWORD i; 1066 if (adj_face == -1) /* No adjacent face. */ 1067 continue; 1068 else if (adj_face >= numfaces) 1069 { 1070 /* This throws exception on Windows */ 1071 WARN("Index out of bounds. Got %d expected less than %d.\n", 1072 adj_face, numfaces); 1073 return D3DERR_INVALIDCALL; 1074 } 1075 adj_face_base = 3 * adj_face; 1076 1077 /* Find opposite edge in adjacent face. */ 1078 for (opp_edge = 0; opp_edge < VERTS_PER_FACE; opp_edge++) 1079 { 1080 DWORD opp_edge_index = adj_face_base + opp_edge; 1081 if (adjacency[opp_edge_index] == face) 1082 break; /* Found opposite edge. */ 1083 } 1084 1085 /* Replaces vertices in opposite edge with vertices from current edge. */ 1086 for (i = 0; i < 2; i++) 1087 { 1088 DWORD from = face_base + (edge + (1 - i)) % VERTS_PER_FACE; 1089 DWORD to = adj_face_base + (opp_edge + i) % VERTS_PER_FACE; 1090 1091 /* Propagate lowest index. */ 1092 if (new_indices[to] > new_indices[from]) 1093 { 1094 new_indices[to] = new_indices[from]; 1095 point_reps[indices[to]] = new_indices[from]; 1096 } 1097 } 1098 } 1099 1100 return D3D_OK; 1101 } 1102 1103 static HRESULT WINAPI d3dx9_mesh_ConvertAdjacencyToPointReps(ID3DXMesh *iface, 1104 const DWORD *adjacency, DWORD *point_reps) 1105 { 1106 struct d3dx9_mesh *This = impl_from_ID3DXMesh(iface); 1107 HRESULT hr; 1108 DWORD face; 1109 DWORD i; 1110 DWORD *indices = NULL; 1111 WORD *indices_16bit = NULL; 1112 DWORD *new_indices = NULL; 1113 const unsigned int VERTS_PER_FACE = 3; 1114 1115 TRACE("iface %p, adjacency %p, point_reps %p.\n", iface, adjacency, point_reps); 1116 1117 if (!adjacency) 1118 { 1119 WARN("NULL adjacency.\n"); 1120 hr = D3DERR_INVALIDCALL; 1121 goto cleanup; 1122 } 1123 1124 if (!point_reps) 1125 { 1126 WARN("NULL point_reps.\n"); 1127 hr = D3DERR_INVALIDCALL; 1128 goto cleanup; 1129 } 1130 1131 /* Should never happen as CreateMesh does not allow meshes with 0 faces */ 1132 if (This->numfaces == 0) 1133 { 1134 ERR("Number of faces was zero.\n"); 1135 hr = D3DERR_INVALIDCALL; 1136 goto cleanup; 1137 } 1138 1139 new_indices = HeapAlloc(GetProcessHeap(), 0, VERTS_PER_FACE * This->numfaces * sizeof(*indices)); 1140 if (!new_indices) 1141 { 1142 hr = E_OUTOFMEMORY; 1143 goto cleanup; 1144 } 1145 1146 if (This->options & D3DXMESH_32BIT) 1147 { 1148 hr = iface->lpVtbl->LockIndexBuffer(iface, D3DLOCK_READONLY, (void**)&indices); 1149 if (FAILED(hr)) goto cleanup; 1150 memcpy(new_indices, indices, VERTS_PER_FACE * This->numfaces * sizeof(*indices)); 1151 } 1152 else 1153 { 1154 /* Make a widening copy of indices_16bit into indices and new_indices 1155 * in order to re-use the helper function */ 1156 hr = iface->lpVtbl->LockIndexBuffer(iface, D3DLOCK_READONLY, (void**)&indices_16bit); 1157 if (FAILED(hr)) goto cleanup; 1158 indices = HeapAlloc(GetProcessHeap(), 0, VERTS_PER_FACE * This->numfaces * sizeof(*indices)); 1159 if (!indices) 1160 { 1161 hr = E_OUTOFMEMORY; 1162 goto cleanup; 1163 } 1164 for (i = 0; i < VERTS_PER_FACE * This->numfaces; i++) 1165 { 1166 new_indices[i] = indices_16bit[i]; 1167 indices[i] = indices_16bit[i]; 1168 } 1169 } 1170 1171 /* Vertices are ordered sequentially in the point representation. */ 1172 for (i = 0; i < This->numvertices; i++) 1173 { 1174 point_reps[i] = i; 1175 } 1176 1177 /* Propagate vertices with low indices so as few vertices as possible 1178 * are used in the mesh. 1179 */ 1180 for (face = 0; face < This->numfaces; face++) 1181 { 1182 hr = propagate_face_vertices(adjacency, point_reps, indices, new_indices, face, This->numfaces); 1183 if (FAILED(hr)) goto cleanup; 1184 } 1185 /* Go in opposite direction to catch all face orderings */ 1186 for (face = 0; face < This->numfaces; face++) 1187 { 1188 hr = propagate_face_vertices(adjacency, point_reps, 1189 indices, new_indices, 1190 (This->numfaces - 1) - face, This->numfaces); 1191 if (FAILED(hr)) goto cleanup; 1192 } 1193 1194 hr = D3D_OK; 1195 cleanup: 1196 if (This->options & D3DXMESH_32BIT) 1197 { 1198 if (indices) iface->lpVtbl->UnlockIndexBuffer(iface); 1199 } 1200 else 1201 { 1202 if (indices_16bit) iface->lpVtbl->UnlockIndexBuffer(iface); 1203 HeapFree(GetProcessHeap(), 0, indices); 1204 } 1205 HeapFree(GetProcessHeap(), 0, new_indices); 1206 return hr; 1207 } 1208 1209 struct vertex_metadata { 1210 float key; 1211 DWORD vertex_index; 1212 DWORD first_shared_index; 1213 }; 1214 1215 static int __cdecl compare_vertex_keys(const void *a, const void *b) 1216 { 1217 const struct vertex_metadata *left = a; 1218 const struct vertex_metadata *right = b; 1219 if (left->key == right->key) 1220 return 0; 1221 return left->key < right->key ? -1 : 1; 1222 } 1223 1224 static HRESULT WINAPI d3dx9_mesh_GenerateAdjacency(ID3DXMesh *iface, float epsilon, DWORD *adjacency) 1225 { 1226 struct d3dx9_mesh *This = impl_from_ID3DXMesh(iface); 1227 HRESULT hr; 1228 BYTE *vertices = NULL; 1229 const DWORD *indices = NULL; 1230 DWORD vertex_size; 1231 DWORD buffer_size; 1232 /* sort the vertices by (x + y + z) to quickly find coincident vertices */ 1233 struct vertex_metadata *sorted_vertices; 1234 /* shared_indices links together identical indices in the index buffer so 1235 * that adjacency checks can be limited to faces sharing a vertex */ 1236 DWORD *shared_indices = NULL; 1237 const FLOAT epsilon_sq = epsilon * epsilon; 1238 DWORD i; 1239 1240 TRACE("iface %p, epsilon %.8e, adjacency %p.\n", iface, epsilon, adjacency); 1241 1242 if (!adjacency) 1243 return D3DERR_INVALIDCALL; 1244 1245 buffer_size = This->numfaces * 3 * sizeof(*shared_indices) + This->numvertices * sizeof(*sorted_vertices); 1246 if (!(This->options & D3DXMESH_32BIT)) 1247 buffer_size += This->numfaces * 3 * sizeof(*indices); 1248 shared_indices = HeapAlloc(GetProcessHeap(), 0, buffer_size); 1249 if (!shared_indices) 1250 return E_OUTOFMEMORY; 1251 sorted_vertices = (struct vertex_metadata*)(shared_indices + This->numfaces * 3); 1252 1253 hr = iface->lpVtbl->LockVertexBuffer(iface, D3DLOCK_READONLY, (void**)&vertices); 1254 if (FAILED(hr)) goto cleanup; 1255 hr = iface->lpVtbl->LockIndexBuffer(iface, D3DLOCK_READONLY, (void**)&indices); 1256 if (FAILED(hr)) goto cleanup; 1257 1258 if (!(This->options & D3DXMESH_32BIT)) { 1259 const WORD *word_indices = (const WORD*)indices; 1260 DWORD *dword_indices = (DWORD*)(sorted_vertices + This->numvertices); 1261 indices = dword_indices; 1262 for (i = 0; i < This->numfaces * 3; i++) 1263 *dword_indices++ = *word_indices++; 1264 } 1265 1266 vertex_size = iface->lpVtbl->GetNumBytesPerVertex(iface); 1267 for (i = 0; i < This->numvertices; i++) { 1268 D3DXVECTOR3 *vertex = (D3DXVECTOR3*)(vertices + vertex_size * i); 1269 sorted_vertices[i].first_shared_index = -1; 1270 sorted_vertices[i].key = vertex->x + vertex->y + vertex->z; 1271 sorted_vertices[i].vertex_index = i; 1272 } 1273 for (i = 0; i < This->numfaces * 3; i++) { 1274 DWORD *first_shared_index = &sorted_vertices[indices[i]].first_shared_index; 1275 shared_indices[i] = *first_shared_index; 1276 *first_shared_index = i; 1277 adjacency[i] = -1; 1278 } 1279 qsort(sorted_vertices, This->numvertices, sizeof(*sorted_vertices), compare_vertex_keys); 1280 1281 for (i = 0; i < This->numvertices; i++) { 1282 struct vertex_metadata *sorted_vertex_a = &sorted_vertices[i]; 1283 D3DXVECTOR3 *vertex_a = (D3DXVECTOR3*)(vertices + sorted_vertex_a->vertex_index * vertex_size); 1284 DWORD shared_index_a = sorted_vertex_a->first_shared_index; 1285 1286 while (shared_index_a != -1) { 1287 DWORD j = i; 1288 DWORD shared_index_b = shared_indices[shared_index_a]; 1289 struct vertex_metadata *sorted_vertex_b = sorted_vertex_a; 1290 1291 while (TRUE) { 1292 while (shared_index_b != -1) { 1293 /* faces are adjacent if they have another coincident vertex */ 1294 DWORD base_a = (shared_index_a / 3) * 3; 1295 DWORD base_b = (shared_index_b / 3) * 3; 1296 BOOL adjacent = FALSE; 1297 int k; 1298 1299 for (k = 0; k < 3; k++) { 1300 if (adjacency[base_b + k] == shared_index_a / 3) { 1301 adjacent = TRUE; 1302 break; 1303 } 1304 } 1305 if (!adjacent) { 1306 for (k = 1; k <= 2; k++) { 1307 DWORD vertex_index_a = base_a + (shared_index_a + k) % 3; 1308 DWORD vertex_index_b = base_b + (shared_index_b + (3 - k)) % 3; 1309 adjacent = indices[vertex_index_a] == indices[vertex_index_b]; 1310 if (!adjacent && epsilon >= 0.0f) { 1311 D3DXVECTOR3 delta = {0.0f, 0.0f, 0.0f}; 1312 FLOAT length_sq; 1313 1314 D3DXVec3Subtract(&delta, 1315 (D3DXVECTOR3*)(vertices + indices[vertex_index_a] * vertex_size), 1316 (D3DXVECTOR3*)(vertices + indices[vertex_index_b] * vertex_size)); 1317 length_sq = D3DXVec3LengthSq(&delta); 1318 adjacent = epsilon == 0.0f ? length_sq == 0.0f : length_sq < epsilon_sq; 1319 } 1320 if (adjacent) { 1321 DWORD adj_a = base_a + 2 - (vertex_index_a + shared_index_a + 1) % 3; 1322 DWORD adj_b = base_b + 2 - (vertex_index_b + shared_index_b + 1) % 3; 1323 if (adjacency[adj_a] == -1 && adjacency[adj_b] == -1) { 1324 adjacency[adj_a] = base_b / 3; 1325 adjacency[adj_b] = base_a / 3; 1326 break; 1327 } 1328 } 1329 } 1330 } 1331 1332 shared_index_b = shared_indices[shared_index_b]; 1333 } 1334 while (++j < This->numvertices) { 1335 D3DXVECTOR3 *vertex_b; 1336 1337 sorted_vertex_b++; 1338 if (sorted_vertex_b->key - sorted_vertex_a->key > epsilon * 3.0f) { 1339 /* no more coincident vertices to try */ 1340 j = This->numvertices; 1341 break; 1342 } 1343 /* check for coincidence */ 1344 vertex_b = (D3DXVECTOR3*)(vertices + sorted_vertex_b->vertex_index * vertex_size); 1345 if (fabsf(vertex_a->x - vertex_b->x) <= epsilon && 1346 fabsf(vertex_a->y - vertex_b->y) <= epsilon && 1347 fabsf(vertex_a->z - vertex_b->z) <= epsilon) 1348 { 1349 break; 1350 } 1351 } 1352 if (j >= This->numvertices) 1353 break; 1354 shared_index_b = sorted_vertex_b->first_shared_index; 1355 } 1356 1357 sorted_vertex_a->first_shared_index = shared_indices[sorted_vertex_a->first_shared_index]; 1358 shared_index_a = sorted_vertex_a->first_shared_index; 1359 } 1360 } 1361 1362 hr = D3D_OK; 1363 cleanup: 1364 if (indices) iface->lpVtbl->UnlockIndexBuffer(iface); 1365 if (vertices) iface->lpVtbl->UnlockVertexBuffer(iface); 1366 HeapFree(GetProcessHeap(), 0, shared_indices); 1367 return hr; 1368 } 1369 1370 static HRESULT WINAPI d3dx9_mesh_UpdateSemantics(ID3DXMesh *iface, D3DVERTEXELEMENT9 declaration[MAX_FVF_DECL_SIZE]) 1371 { 1372 struct d3dx9_mesh *This = impl_from_ID3DXMesh(iface); 1373 HRESULT hr; 1374 UINT vertex_declaration_size; 1375 int i; 1376 1377 TRACE("iface %p, declaration %p.\n", iface, declaration); 1378 1379 if (!declaration) 1380 { 1381 WARN("Invalid declaration. Can't use NULL declaration.\n"); 1382 return D3DERR_INVALIDCALL; 1383 } 1384 1385 /* New declaration must be same size as original */ 1386 vertex_declaration_size = D3DXGetDeclVertexSize(declaration, declaration[0].Stream); 1387 if (vertex_declaration_size != This->vertex_declaration_size) 1388 { 1389 WARN("Invalid declaration. New vertex size does not match the original vertex size.\n"); 1390 return D3DERR_INVALIDCALL; 1391 } 1392 1393 /* New declaration must not contain non-zero Stream value */ 1394 for (i = 0; declaration[i].Stream != 0xff; i++) 1395 { 1396 if (declaration[i].Stream != 0) 1397 { 1398 WARN("Invalid declaration. New declaration contains non-zero Stream value.\n"); 1399 return D3DERR_INVALIDCALL; 1400 } 1401 } 1402 1403 This->num_elem = i + 1; 1404 copy_declaration(This->cached_declaration, declaration, This->num_elem); 1405 1406 if (This->vertex_declaration) 1407 IDirect3DVertexDeclaration9_Release(This->vertex_declaration); 1408 1409 /* An application can pass an invalid declaration to UpdateSemantics and 1410 * still expect D3D_OK (see tests). If the declaration is invalid, then 1411 * subsequent calls to DrawSubset will fail. This is handled by setting the 1412 * vertex declaration to NULL. 1413 * GetDeclaration, GetNumBytesPerVertex must, however, use the new 1414 * invalid declaration. This is handled by them using the cached vertex 1415 * declaration instead of the actual vertex declaration. 1416 */ 1417 hr = IDirect3DDevice9_CreateVertexDeclaration(This->device, 1418 declaration, 1419 &This->vertex_declaration); 1420 if (FAILED(hr)) 1421 { 1422 WARN("Using invalid declaration. Calls to DrawSubset will fail.\n"); 1423 This->vertex_declaration = NULL; 1424 } 1425 1426 return D3D_OK; 1427 } 1428 1429 static HRESULT WINAPI d3dx9_mesh_LockAttributeBuffer(ID3DXMesh *iface, DWORD flags, DWORD **data) 1430 { 1431 struct d3dx9_mesh *mesh = impl_from_ID3DXMesh(iface); 1432 1433 TRACE("iface %p, flags %#x, data %p.\n", iface, flags, data); 1434 1435 InterlockedIncrement(&mesh->attrib_buffer_lock_count); 1436 1437 if (!(flags & D3DLOCK_READONLY)) 1438 { 1439 D3DXATTRIBUTERANGE *attrib_table = mesh->attrib_table; 1440 mesh->attrib_table_size = 0; 1441 mesh->attrib_table = NULL; 1442 HeapFree(GetProcessHeap(), 0, attrib_table); 1443 } 1444 1445 *data = mesh->attrib_buffer; 1446 1447 return D3D_OK; 1448 } 1449 1450 static HRESULT WINAPI d3dx9_mesh_UnlockAttributeBuffer(ID3DXMesh *iface) 1451 { 1452 struct d3dx9_mesh *mesh = impl_from_ID3DXMesh(iface); 1453 int lock_count; 1454 1455 TRACE("iface %p.\n", iface); 1456 1457 lock_count = InterlockedDecrement(&mesh->attrib_buffer_lock_count); 1458 if (lock_count < 0) 1459 { 1460 InterlockedIncrement(&mesh->attrib_buffer_lock_count); 1461 return D3DERR_INVALIDCALL; 1462 } 1463 1464 return D3D_OK; 1465 } 1466 1467 static HRESULT WINAPI d3dx9_mesh_Optimize(ID3DXMesh *iface, DWORD flags, const DWORD *adjacency_in, 1468 DWORD *adjacency_out, DWORD *face_remap, ID3DXBuffer **vertex_remap, ID3DXMesh **opt_mesh) 1469 { 1470 struct d3dx9_mesh *mesh = impl_from_ID3DXMesh(iface); 1471 HRESULT hr; 1472 D3DVERTEXELEMENT9 declaration[MAX_FVF_DECL_SIZE] = { D3DDECL_END() }; 1473 ID3DXMesh *optimized_mesh; 1474 1475 TRACE("iface %p, flags %#x, adjacency_in %p, adjacency_out %p, face_remap %p, vertex_remap %p, opt_mesh %p.\n", 1476 iface, flags, adjacency_in, adjacency_out, face_remap, vertex_remap, opt_mesh); 1477 1478 if (!opt_mesh) 1479 return D3DERR_INVALIDCALL; 1480 1481 hr = iface->lpVtbl->GetDeclaration(iface, declaration); 1482 if (FAILED(hr)) return hr; 1483 1484 if (FAILED(hr = iface->lpVtbl->CloneMesh(iface, mesh->options, declaration, mesh->device, &optimized_mesh))) 1485 return hr; 1486 1487 hr = optimized_mesh->lpVtbl->OptimizeInplace(optimized_mesh, flags, adjacency_in, adjacency_out, face_remap, vertex_remap); 1488 if (SUCCEEDED(hr)) 1489 *opt_mesh = optimized_mesh; 1490 else 1491 IUnknown_Release(optimized_mesh); 1492 return hr; 1493 } 1494 1495 /* Creates a vertex_remap that removes unused vertices. 1496 * Indices are updated according to the vertex_remap. */ 1497 static HRESULT compact_mesh(struct d3dx9_mesh *This, DWORD *indices, 1498 DWORD *new_num_vertices, ID3DXBuffer **vertex_remap) 1499 { 1500 HRESULT hr; 1501 DWORD *vertex_remap_ptr; 1502 DWORD num_used_vertices; 1503 DWORD i; 1504 1505 hr = D3DXCreateBuffer(This->numvertices * sizeof(DWORD), vertex_remap); 1506 if (FAILED(hr)) return hr; 1507 vertex_remap_ptr = ID3DXBuffer_GetBufferPointer(*vertex_remap); 1508 1509 for (i = 0; i < This->numfaces * 3; i++) 1510 vertex_remap_ptr[indices[i]] = 1; 1511 1512 /* create old->new vertex mapping */ 1513 num_used_vertices = 0; 1514 for (i = 0; i < This->numvertices; i++) { 1515 if (vertex_remap_ptr[i]) 1516 vertex_remap_ptr[i] = num_used_vertices++; 1517 else 1518 vertex_remap_ptr[i] = -1; 1519 } 1520 /* convert indices */ 1521 for (i = 0; i < This->numfaces * 3; i++) 1522 indices[i] = vertex_remap_ptr[indices[i]]; 1523 1524 /* create new->old vertex mapping */ 1525 num_used_vertices = 0; 1526 for (i = 0; i < This->numvertices; i++) { 1527 if (vertex_remap_ptr[i] != -1) 1528 vertex_remap_ptr[num_used_vertices++] = i; 1529 } 1530 for (i = num_used_vertices; i < This->numvertices; i++) 1531 vertex_remap_ptr[i] = -1; 1532 1533 *new_num_vertices = num_used_vertices; 1534 1535 return D3D_OK; 1536 } 1537 1538 /* count the number of unique attribute values in a sorted attribute buffer */ 1539 static DWORD count_attributes(const DWORD *attrib_buffer, DWORD numfaces) 1540 { 1541 DWORD last_attribute = attrib_buffer[0]; 1542 DWORD attrib_table_size = 1; 1543 DWORD i; 1544 for (i = 1; i < numfaces; i++) { 1545 if (attrib_buffer[i] != last_attribute) { 1546 last_attribute = attrib_buffer[i]; 1547 attrib_table_size++; 1548 } 1549 } 1550 return attrib_table_size; 1551 } 1552 1553 static void fill_attribute_table(DWORD *attrib_buffer, DWORD numfaces, void *indices, 1554 BOOL is_32bit_indices, D3DXATTRIBUTERANGE *attrib_table) 1555 { 1556 DWORD attrib_table_size = 0; 1557 DWORD last_attribute = attrib_buffer[0]; 1558 DWORD min_vertex, max_vertex; 1559 DWORD i; 1560 1561 attrib_table[0].AttribId = last_attribute; 1562 attrib_table[0].FaceStart = 0; 1563 min_vertex = (DWORD)-1; 1564 max_vertex = 0; 1565 for (i = 0; i < numfaces; i++) { 1566 DWORD j; 1567 1568 if (attrib_buffer[i] != last_attribute) { 1569 last_attribute = attrib_buffer[i]; 1570 attrib_table[attrib_table_size].FaceCount = i - attrib_table[attrib_table_size].FaceStart; 1571 attrib_table[attrib_table_size].VertexStart = min_vertex; 1572 attrib_table[attrib_table_size].VertexCount = max_vertex - min_vertex + 1; 1573 attrib_table_size++; 1574 attrib_table[attrib_table_size].AttribId = attrib_buffer[i]; 1575 attrib_table[attrib_table_size].FaceStart = i; 1576 min_vertex = (DWORD)-1; 1577 max_vertex = 0; 1578 } 1579 for (j = 0; j < 3; j++) { 1580 DWORD vertex_index = is_32bit_indices ? ((DWORD*)indices)[i * 3 + j] : ((WORD*)indices)[i * 3 + j]; 1581 if (vertex_index < min_vertex) 1582 min_vertex = vertex_index; 1583 if (vertex_index > max_vertex) 1584 max_vertex = vertex_index; 1585 } 1586 } 1587 attrib_table[attrib_table_size].FaceCount = i - attrib_table[attrib_table_size].FaceStart; 1588 attrib_table[attrib_table_size].VertexStart = min_vertex; 1589 attrib_table[attrib_table_size].VertexCount = max_vertex - min_vertex + 1; 1590 attrib_table_size++; 1591 } 1592 1593 static int __cdecl attrib_entry_compare(const void *a, const void *b) 1594 { 1595 const DWORD *ptr_a = *(const DWORD **)a; 1596 const DWORD *ptr_b = *(const DWORD **)b; 1597 int delta = *ptr_a - *ptr_b; 1598 1599 if (delta) 1600 return delta; 1601 1602 delta = ptr_a - ptr_b; /* for stable sort */ 1603 return delta; 1604 } 1605 1606 /* Create face_remap, a new attribute buffer for attribute sort optimization. */ 1607 static HRESULT remap_faces_for_attrsort(struct d3dx9_mesh *This, const DWORD *indices, 1608 DWORD *attrib_buffer, DWORD **sorted_attrib_buffer, DWORD **face_remap) 1609 { 1610 DWORD **sorted_attrib_ptr_buffer = NULL; 1611 DWORD i; 1612 1613 sorted_attrib_ptr_buffer = HeapAlloc(GetProcessHeap(), 0, This->numfaces * sizeof(*sorted_attrib_ptr_buffer)); 1614 if (!sorted_attrib_ptr_buffer) 1615 return E_OUTOFMEMORY; 1616 1617 *face_remap = HeapAlloc(GetProcessHeap(), 0, This->numfaces * sizeof(**face_remap)); 1618 if (!*face_remap) 1619 { 1620 HeapFree(GetProcessHeap(), 0, sorted_attrib_ptr_buffer); 1621 return E_OUTOFMEMORY; 1622 } 1623 1624 for (i = 0; i < This->numfaces; i++) 1625 sorted_attrib_ptr_buffer[i] = &attrib_buffer[i]; 1626 qsort(sorted_attrib_ptr_buffer, This->numfaces, sizeof(*sorted_attrib_ptr_buffer), attrib_entry_compare); 1627 1628 for (i = 0; i < This->numfaces; i++) 1629 { 1630 DWORD old_face = sorted_attrib_ptr_buffer[i] - attrib_buffer; 1631 (*face_remap)[old_face] = i; 1632 } 1633 1634 /* overwrite sorted_attrib_ptr_buffer with the values themselves */ 1635 *sorted_attrib_buffer = (DWORD*)sorted_attrib_ptr_buffer; 1636 for (i = 0; i < This->numfaces; i++) 1637 (*sorted_attrib_buffer)[(*face_remap)[i]] = attrib_buffer[i]; 1638 1639 return D3D_OK; 1640 } 1641 1642 static HRESULT WINAPI d3dx9_mesh_OptimizeInplace(ID3DXMesh *iface, DWORD flags, const DWORD *adjacency_in, 1643 DWORD *adjacency_out, DWORD *face_remap_out, ID3DXBuffer **vertex_remap_out) 1644 { 1645 struct d3dx9_mesh *This = impl_from_ID3DXMesh(iface); 1646 void *indices = NULL; 1647 DWORD *attrib_buffer = NULL; 1648 HRESULT hr; 1649 ID3DXBuffer *vertex_remap = NULL; 1650 DWORD *face_remap = NULL; /* old -> new mapping */ 1651 DWORD *dword_indices = NULL; 1652 DWORD new_num_vertices = 0; 1653 DWORD new_num_alloc_vertices = 0; 1654 IDirect3DVertexBuffer9 *vertex_buffer = NULL; 1655 DWORD *sorted_attrib_buffer = NULL; 1656 DWORD i; 1657 1658 TRACE("iface %p, flags %#x, adjacency_in %p, adjacency_out %p, face_remap_out %p, vertex_remap_out %p.\n", 1659 iface, flags, adjacency_in, adjacency_out, face_remap_out, vertex_remap_out); 1660 1661 if (!flags) 1662 return D3DERR_INVALIDCALL; 1663 if (!adjacency_in && (flags & (D3DXMESHOPT_VERTEXCACHE | D3DXMESHOPT_STRIPREORDER))) 1664 return D3DERR_INVALIDCALL; 1665 if ((flags & (D3DXMESHOPT_VERTEXCACHE | D3DXMESHOPT_STRIPREORDER)) == (D3DXMESHOPT_VERTEXCACHE | D3DXMESHOPT_STRIPREORDER)) 1666 return D3DERR_INVALIDCALL; 1667 1668 if (flags & (D3DXMESHOPT_VERTEXCACHE | D3DXMESHOPT_STRIPREORDER)) 1669 { 1670 if (flags & D3DXMESHOPT_VERTEXCACHE) 1671 FIXME("D3DXMESHOPT_VERTEXCACHE not implemented.\n"); 1672 if (flags & D3DXMESHOPT_STRIPREORDER) 1673 FIXME("D3DXMESHOPT_STRIPREORDER not implemented.\n"); 1674 return E_NOTIMPL; 1675 } 1676 1677 hr = iface->lpVtbl->LockIndexBuffer(iface, 0, &indices); 1678 if (FAILED(hr)) goto cleanup; 1679 1680 dword_indices = HeapAlloc(GetProcessHeap(), 0, This->numfaces * 3 * sizeof(DWORD)); 1681 if (!dword_indices) return E_OUTOFMEMORY; 1682 if (This->options & D3DXMESH_32BIT) { 1683 memcpy(dword_indices, indices, This->numfaces * 3 * sizeof(DWORD)); 1684 } else { 1685 WORD *word_indices = indices; 1686 for (i = 0; i < This->numfaces * 3; i++) 1687 dword_indices[i] = *word_indices++; 1688 } 1689 1690 if ((flags & (D3DXMESHOPT_COMPACT | D3DXMESHOPT_IGNOREVERTS | D3DXMESHOPT_ATTRSORT)) == D3DXMESHOPT_COMPACT) 1691 { 1692 new_num_alloc_vertices = This->numvertices; 1693 hr = compact_mesh(This, dword_indices, &new_num_vertices, &vertex_remap); 1694 if (FAILED(hr)) goto cleanup; 1695 } else if (flags & D3DXMESHOPT_ATTRSORT) { 1696 if (!(flags & D3DXMESHOPT_IGNOREVERTS)) 1697 { 1698 FIXME("D3DXMESHOPT_ATTRSORT vertex reordering not implemented.\n"); 1699 hr = E_NOTIMPL; 1700 goto cleanup; 1701 } 1702 1703 hr = iface->lpVtbl->LockAttributeBuffer(iface, 0, &attrib_buffer); 1704 if (FAILED(hr)) goto cleanup; 1705 1706 hr = remap_faces_for_attrsort(This, dword_indices, attrib_buffer, &sorted_attrib_buffer, &face_remap); 1707 if (FAILED(hr)) goto cleanup; 1708 } 1709 1710 if (vertex_remap) 1711 { 1712 /* reorder the vertices using vertex_remap */ 1713 D3DVERTEXBUFFER_DESC vertex_desc; 1714 DWORD *vertex_remap_ptr = ID3DXBuffer_GetBufferPointer(vertex_remap); 1715 DWORD vertex_size = iface->lpVtbl->GetNumBytesPerVertex(iface); 1716 BYTE *orig_vertices; 1717 BYTE *new_vertices; 1718 1719 hr = IDirect3DVertexBuffer9_GetDesc(This->vertex_buffer, &vertex_desc); 1720 if (FAILED(hr)) goto cleanup; 1721 1722 hr = IDirect3DDevice9_CreateVertexBuffer(This->device, new_num_alloc_vertices * vertex_size, 1723 vertex_desc.Usage, This->fvf, vertex_desc.Pool, &vertex_buffer, NULL); 1724 if (FAILED(hr)) goto cleanup; 1725 1726 hr = IDirect3DVertexBuffer9_Lock(This->vertex_buffer, 0, 0, (void**)&orig_vertices, D3DLOCK_READONLY); 1727 if (FAILED(hr)) goto cleanup; 1728 1729 hr = IDirect3DVertexBuffer9_Lock(vertex_buffer, 0, 0, (void**)&new_vertices, 0); 1730 if (FAILED(hr)) { 1731 IDirect3DVertexBuffer9_Unlock(This->vertex_buffer); 1732 goto cleanup; 1733 } 1734 1735 for (i = 0; i < new_num_vertices; i++) 1736 memcpy(new_vertices + i * vertex_size, orig_vertices + vertex_remap_ptr[i] * vertex_size, vertex_size); 1737 1738 IDirect3DVertexBuffer9_Unlock(This->vertex_buffer); 1739 IDirect3DVertexBuffer9_Unlock(vertex_buffer); 1740 } else if (vertex_remap_out) { 1741 DWORD *vertex_remap_ptr; 1742 1743 hr = D3DXCreateBuffer(This->numvertices * sizeof(DWORD), &vertex_remap); 1744 if (FAILED(hr)) goto cleanup; 1745 vertex_remap_ptr = ID3DXBuffer_GetBufferPointer(vertex_remap); 1746 for (i = 0; i < This->numvertices; i++) 1747 *vertex_remap_ptr++ = i; 1748 } 1749 1750 if (flags & D3DXMESHOPT_ATTRSORT) 1751 { 1752 D3DXATTRIBUTERANGE *attrib_table; 1753 DWORD attrib_table_size; 1754 1755 attrib_table_size = count_attributes(sorted_attrib_buffer, This->numfaces); 1756 attrib_table = HeapAlloc(GetProcessHeap(), 0, attrib_table_size * sizeof(*attrib_table)); 1757 if (!attrib_table) { 1758 hr = E_OUTOFMEMORY; 1759 goto cleanup; 1760 } 1761 1762 memcpy(attrib_buffer, sorted_attrib_buffer, This->numfaces * sizeof(*attrib_buffer)); 1763 1764 /* reorder the indices using face_remap */ 1765 if (This->options & D3DXMESH_32BIT) { 1766 for (i = 0; i < This->numfaces; i++) 1767 memcpy((DWORD*)indices + face_remap[i] * 3, dword_indices + i * 3, 3 * sizeof(DWORD)); 1768 } else { 1769 WORD *word_indices = indices; 1770 for (i = 0; i < This->numfaces; i++) { 1771 DWORD new_pos = face_remap[i] * 3; 1772 DWORD old_pos = i * 3; 1773 word_indices[new_pos++] = dword_indices[old_pos++]; 1774 word_indices[new_pos++] = dword_indices[old_pos++]; 1775 word_indices[new_pos] = dword_indices[old_pos]; 1776 } 1777 } 1778 1779 fill_attribute_table(attrib_buffer, This->numfaces, indices, 1780 This->options & D3DXMESH_32BIT, attrib_table); 1781 1782 HeapFree(GetProcessHeap(), 0, This->attrib_table); 1783 This->attrib_table = attrib_table; 1784 This->attrib_table_size = attrib_table_size; 1785 } else { 1786 if (This->options & D3DXMESH_32BIT) { 1787 memcpy(indices, dword_indices, This->numfaces * 3 * sizeof(DWORD)); 1788 } else { 1789 WORD *word_indices = indices; 1790 for (i = 0; i < This->numfaces * 3; i++) 1791 *word_indices++ = dword_indices[i]; 1792 } 1793 } 1794 1795 if (adjacency_out) { 1796 if (face_remap) { 1797 for (i = 0; i < This->numfaces; i++) { 1798 DWORD old_pos = i * 3; 1799 DWORD new_pos = face_remap[i] * 3; 1800 adjacency_out[new_pos++] = face_remap[adjacency_in[old_pos++]]; 1801 adjacency_out[new_pos++] = face_remap[adjacency_in[old_pos++]]; 1802 adjacency_out[new_pos++] = face_remap[adjacency_in[old_pos++]]; 1803 } 1804 } else { 1805 memcpy(adjacency_out, adjacency_in, This->numfaces * 3 * sizeof(*adjacency_out)); 1806 } 1807 } 1808 if (face_remap_out) { 1809 if (face_remap) { 1810 for (i = 0; i < This->numfaces; i++) 1811 face_remap_out[face_remap[i]] = i; 1812 } else { 1813 for (i = 0; i < This->numfaces; i++) 1814 face_remap_out[i] = i; 1815 } 1816 } 1817 if (vertex_remap_out) 1818 *vertex_remap_out = vertex_remap; 1819 vertex_remap = NULL; 1820 1821 if (vertex_buffer) { 1822 IDirect3DVertexBuffer9_Release(This->vertex_buffer); 1823 This->vertex_buffer = vertex_buffer; 1824 vertex_buffer = NULL; 1825 This->numvertices = new_num_vertices; 1826 } 1827 1828 hr = D3D_OK; 1829 cleanup: 1830 HeapFree(GetProcessHeap(), 0, sorted_attrib_buffer); 1831 HeapFree(GetProcessHeap(), 0, face_remap); 1832 HeapFree(GetProcessHeap(), 0, dword_indices); 1833 if (vertex_remap) ID3DXBuffer_Release(vertex_remap); 1834 if (vertex_buffer) IDirect3DVertexBuffer9_Release(vertex_buffer); 1835 if (attrib_buffer) iface->lpVtbl->UnlockAttributeBuffer(iface); 1836 if (indices) iface->lpVtbl->UnlockIndexBuffer(iface); 1837 return hr; 1838 } 1839 1840 static HRESULT WINAPI d3dx9_mesh_SetAttributeTable(ID3DXMesh *iface, 1841 const D3DXATTRIBUTERANGE *attrib_table, DWORD attrib_table_size) 1842 { 1843 struct d3dx9_mesh *mesh = impl_from_ID3DXMesh(iface); 1844 D3DXATTRIBUTERANGE *new_table = NULL; 1845 1846 TRACE("iface %p, attrib_table %p, attrib_table_size %u.\n", iface, attrib_table, attrib_table_size); 1847 1848 if (attrib_table_size) { 1849 size_t size = attrib_table_size * sizeof(*attrib_table); 1850 1851 new_table = HeapAlloc(GetProcessHeap(), 0, size); 1852 if (!new_table) 1853 return E_OUTOFMEMORY; 1854 1855 CopyMemory(new_table, attrib_table, size); 1856 } else if (attrib_table) { 1857 return D3DERR_INVALIDCALL; 1858 } 1859 HeapFree(GetProcessHeap(), 0, mesh->attrib_table); 1860 mesh->attrib_table = new_table; 1861 mesh->attrib_table_size = attrib_table_size; 1862 1863 return D3D_OK; 1864 } 1865 1866 static const struct ID3DXMeshVtbl D3DXMesh_Vtbl = 1867 { 1868 d3dx9_mesh_QueryInterface, 1869 d3dx9_mesh_AddRef, 1870 d3dx9_mesh_Release, 1871 d3dx9_mesh_DrawSubset, 1872 d3dx9_mesh_GetNumFaces, 1873 d3dx9_mesh_GetNumVertices, 1874 d3dx9_mesh_GetFVF, 1875 d3dx9_mesh_GetDeclaration, 1876 d3dx9_mesh_GetNumBytesPerVertex, 1877 d3dx9_mesh_GetOptions, 1878 d3dx9_mesh_GetDevice, 1879 d3dx9_mesh_CloneMeshFVF, 1880 d3dx9_mesh_CloneMesh, 1881 d3dx9_mesh_GetVertexBuffer, 1882 d3dx9_mesh_GetIndexBuffer, 1883 d3dx9_mesh_LockVertexBuffer, 1884 d3dx9_mesh_UnlockVertexBuffer, 1885 d3dx9_mesh_LockIndexBuffer, 1886 d3dx9_mesh_UnlockIndexBuffer, 1887 d3dx9_mesh_GetAttributeTable, 1888 d3dx9_mesh_ConvertPointRepsToAdjacency, 1889 d3dx9_mesh_ConvertAdjacencyToPointReps, 1890 d3dx9_mesh_GenerateAdjacency, 1891 d3dx9_mesh_UpdateSemantics, 1892 d3dx9_mesh_LockAttributeBuffer, 1893 d3dx9_mesh_UnlockAttributeBuffer, 1894 d3dx9_mesh_Optimize, 1895 d3dx9_mesh_OptimizeInplace, 1896 d3dx9_mesh_SetAttributeTable, 1897 }; 1898 1899 1900 /* Algorithm taken from the article: An Efficient and Robust Ray-Box Intersection Algorithm 1901 Amy Williams University of Utah 1902 Steve Barrus University of Utah 1903 R. Keith Morley University of Utah 1904 Peter Shirley University of Utah 1905 1906 International Conference on Computer Graphics and Interactive Techniques archive 1907 ACM SIGGRAPH 2005 Courses 1908 Los Angeles, California 1909 1910 This algorithm is free of patents or of copyrights, as confirmed by Peter Shirley himself. 1911 1912 Algorithm: Consider the box as the intersection of three slabs. Clip the ray 1913 against each slab, if there's anything left of the ray after we're 1914 done we've got an intersection of the ray with the box. */ 1915 BOOL WINAPI D3DXBoxBoundProbe(const D3DXVECTOR3 *pmin, const D3DXVECTOR3 *pmax, 1916 const D3DXVECTOR3 *prayposition, const D3DXVECTOR3 *praydirection) 1917 { 1918 FLOAT div, tmin, tmax, tymin, tymax, tzmin, tzmax; 1919 1920 div = 1.0f / praydirection->x; 1921 if ( div >= 0.0f ) 1922 { 1923 tmin = ( pmin->x - prayposition->x ) * div; 1924 tmax = ( pmax->x - prayposition->x ) * div; 1925 } 1926 else 1927 { 1928 tmin = ( pmax->x - prayposition->x ) * div; 1929 tmax = ( pmin->x - prayposition->x ) * div; 1930 } 1931 1932 if ( tmax < 0.0f ) return FALSE; 1933 1934 div = 1.0f / praydirection->y; 1935 if ( div >= 0.0f ) 1936 { 1937 tymin = ( pmin->y - prayposition->y ) * div; 1938 tymax = ( pmax->y - prayposition->y ) * div; 1939 } 1940 else 1941 { 1942 tymin = ( pmax->y - prayposition->y ) * div; 1943 tymax = ( pmin->y - prayposition->y ) * div; 1944 } 1945 1946 if ( ( tymax < 0.0f ) || ( tmin > tymax ) || ( tymin > tmax ) ) return FALSE; 1947 1948 if ( tymin > tmin ) tmin = tymin; 1949 if ( tymax < tmax ) tmax = tymax; 1950 1951 div = 1.0f / praydirection->z; 1952 if ( div >= 0.0f ) 1953 { 1954 tzmin = ( pmin->z - prayposition->z ) * div; 1955 tzmax = ( pmax->z - prayposition->z ) * div; 1956 } 1957 else 1958 { 1959 tzmin = ( pmax->z - prayposition->z ) * div; 1960 tzmax = ( pmin->z - prayposition->z ) * div; 1961 } 1962 1963 if ( (tzmax < 0.0f ) || ( tmin > tzmax ) || ( tzmin > tmax ) ) return FALSE; 1964 1965 return TRUE; 1966 } 1967 1968 HRESULT WINAPI D3DXComputeBoundingBox(const D3DXVECTOR3 *pfirstposition, 1969 DWORD numvertices, DWORD dwstride, D3DXVECTOR3 *pmin, D3DXVECTOR3 *pmax) 1970 { 1971 D3DXVECTOR3 vec; 1972 unsigned int i; 1973 1974 if( !pfirstposition || !pmin || !pmax ) return D3DERR_INVALIDCALL; 1975 1976 *pmin = *pfirstposition; 1977 *pmax = *pmin; 1978 1979 for(i=0; i<numvertices; i++) 1980 { 1981 vec = *( (const D3DXVECTOR3*)((const char*)pfirstposition + dwstride * i) ); 1982 1983 if ( vec.x < pmin->x ) pmin->x = vec.x; 1984 if ( vec.x > pmax->x ) pmax->x = vec.x; 1985 1986 if ( vec.y < pmin->y ) pmin->y = vec.y; 1987 if ( vec.y > pmax->y ) pmax->y = vec.y; 1988 1989 if ( vec.z < pmin->z ) pmin->z = vec.z; 1990 if ( vec.z > pmax->z ) pmax->z = vec.z; 1991 } 1992 1993 return D3D_OK; 1994 } 1995 1996 HRESULT WINAPI D3DXComputeBoundingSphere(const D3DXVECTOR3 *pfirstposition, 1997 DWORD numvertices, DWORD dwstride, D3DXVECTOR3 *pcenter, float *pradius) 1998 { 1999 D3DXVECTOR3 temp; 2000 FLOAT d; 2001 unsigned int i; 2002 2003 if( !pfirstposition || !pcenter || !pradius ) return D3DERR_INVALIDCALL; 2004 2005 temp.x = 0.0f; 2006 temp.y = 0.0f; 2007 temp.z = 0.0f; 2008 *pradius = 0.0f; 2009 2010 for(i=0; i<numvertices; i++) 2011 D3DXVec3Add(&temp, &temp, (const D3DXVECTOR3*)((const char*)pfirstposition + dwstride * i)); 2012 2013 D3DXVec3Scale(pcenter, &temp, 1.0f / numvertices); 2014 2015 for(i=0; i<numvertices; i++) 2016 { 2017 d = D3DXVec3Length(D3DXVec3Subtract(&temp, (const D3DXVECTOR3*)((const char*)pfirstposition + dwstride * i), pcenter)); 2018 if ( d > *pradius ) *pradius = d; 2019 } 2020 return D3D_OK; 2021 } 2022 2023 static void append_decl_element(D3DVERTEXELEMENT9 *declaration, UINT *idx, UINT *offset, 2024 D3DDECLTYPE type, D3DDECLUSAGE usage, UINT usage_idx) 2025 { 2026 declaration[*idx].Stream = 0; 2027 declaration[*idx].Offset = *offset; 2028 declaration[*idx].Type = type; 2029 declaration[*idx].Method = D3DDECLMETHOD_DEFAULT; 2030 declaration[*idx].Usage = usage; 2031 declaration[*idx].UsageIndex = usage_idx; 2032 2033 *offset += d3dx_decltype_size[type]; 2034 ++(*idx); 2035 } 2036 2037 /************************************************************************* 2038 * D3DXDeclaratorFromFVF 2039 */ 2040 HRESULT WINAPI D3DXDeclaratorFromFVF(DWORD fvf, D3DVERTEXELEMENT9 declaration[MAX_FVF_DECL_SIZE]) 2041 { 2042 static const D3DVERTEXELEMENT9 end_element = D3DDECL_END(); 2043 DWORD tex_count = (fvf & D3DFVF_TEXCOUNT_MASK) >> D3DFVF_TEXCOUNT_SHIFT; 2044 unsigned int offset = 0; 2045 unsigned int idx = 0; 2046 unsigned int i; 2047 2048 TRACE("fvf %#x, declaration %p.\n", fvf, declaration); 2049 2050 if (fvf & (D3DFVF_RESERVED0 | D3DFVF_RESERVED2)) return D3DERR_INVALIDCALL; 2051 2052 if (fvf & D3DFVF_POSITION_MASK) 2053 { 2054 BOOL has_blend = (fvf & D3DFVF_XYZB5) >= D3DFVF_XYZB1; 2055 DWORD blend_count = 1 + (((fvf & D3DFVF_XYZB5) - D3DFVF_XYZB1) >> 1); 2056 BOOL has_blend_idx = (fvf & D3DFVF_LASTBETA_D3DCOLOR) || (fvf & D3DFVF_LASTBETA_UBYTE4); 2057 2058 if (has_blend_idx) --blend_count; 2059 2060 if ((fvf & D3DFVF_POSITION_MASK) == D3DFVF_XYZW 2061 || (has_blend && blend_count > 4)) 2062 return D3DERR_INVALIDCALL; 2063 2064 if ((fvf & D3DFVF_POSITION_MASK) == D3DFVF_XYZRHW) 2065 append_decl_element(declaration, &idx, &offset, D3DDECLTYPE_FLOAT4, D3DDECLUSAGE_POSITIONT, 0); 2066 else 2067 append_decl_element(declaration, &idx, &offset, D3DDECLTYPE_FLOAT3, D3DDECLUSAGE_POSITION, 0); 2068 2069 if (has_blend) 2070 { 2071 switch (blend_count) 2072 { 2073 case 0: 2074 break; 2075 case 1: 2076 append_decl_element(declaration, &idx, &offset, D3DDECLTYPE_FLOAT1, D3DDECLUSAGE_BLENDWEIGHT, 0); 2077 break; 2078 case 2: 2079 append_decl_element(declaration, &idx, &offset, D3DDECLTYPE_FLOAT2, D3DDECLUSAGE_BLENDWEIGHT, 0); 2080 break; 2081 case 3: 2082 append_decl_element(declaration, &idx, &offset, D3DDECLTYPE_FLOAT3, D3DDECLUSAGE_BLENDWEIGHT, 0); 2083 break; 2084 case 4: 2085 append_decl_element(declaration, &idx, &offset, D3DDECLTYPE_FLOAT4, D3DDECLUSAGE_BLENDWEIGHT, 0); 2086 break; 2087 default: 2088 ERR("Invalid blend count %u.\n", blend_count); 2089 break; 2090 } 2091 2092 if (has_blend_idx) 2093 { 2094 if (fvf & D3DFVF_LASTBETA_UBYTE4) 2095 append_decl_element(declaration, &idx, &offset, D3DDECLTYPE_UBYTE4, D3DDECLUSAGE_BLENDINDICES, 0); 2096 else if (fvf & D3DFVF_LASTBETA_D3DCOLOR) 2097 append_decl_element(declaration, &idx, &offset, D3DDECLTYPE_D3DCOLOR, D3DDECLUSAGE_BLENDINDICES, 0); 2098 } 2099 } 2100 } 2101 2102 if (fvf & D3DFVF_NORMAL) 2103 append_decl_element(declaration, &idx, &offset, D3DDECLTYPE_FLOAT3, D3DDECLUSAGE_NORMAL, 0); 2104 if (fvf & D3DFVF_PSIZE) 2105 append_decl_element(declaration, &idx, &offset, D3DDECLTYPE_FLOAT1, D3DDECLUSAGE_PSIZE, 0); 2106 if (fvf & D3DFVF_DIFFUSE) 2107 append_decl_element(declaration, &idx, &offset, D3DDECLTYPE_D3DCOLOR, D3DDECLUSAGE_COLOR, 0); 2108 if (fvf & D3DFVF_SPECULAR) 2109 append_decl_element(declaration, &idx, &offset, D3DDECLTYPE_D3DCOLOR, D3DDECLUSAGE_COLOR, 1); 2110 2111 for (i = 0; i < tex_count; ++i) 2112 { 2113 switch ((fvf >> (16 + 2 * i)) & 0x03) 2114 { 2115 case D3DFVF_TEXTUREFORMAT1: 2116 append_decl_element(declaration, &idx, &offset, D3DDECLTYPE_FLOAT1, D3DDECLUSAGE_TEXCOORD, i); 2117 break; 2118 case D3DFVF_TEXTUREFORMAT2: 2119 append_decl_element(declaration, &idx, &offset, D3DDECLTYPE_FLOAT2, D3DDECLUSAGE_TEXCOORD, i); 2120 break; 2121 case D3DFVF_TEXTUREFORMAT3: 2122 append_decl_element(declaration, &idx, &offset, D3DDECLTYPE_FLOAT3, D3DDECLUSAGE_TEXCOORD, i); 2123 break; 2124 case D3DFVF_TEXTUREFORMAT4: 2125 append_decl_element(declaration, &idx, &offset, D3DDECLTYPE_FLOAT4, D3DDECLUSAGE_TEXCOORD, i); 2126 break; 2127 } 2128 } 2129 2130 declaration[idx] = end_element; 2131 2132 return D3D_OK; 2133 } 2134 2135 /************************************************************************* 2136 * D3DXFVFFromDeclarator 2137 */ 2138 HRESULT WINAPI D3DXFVFFromDeclarator(const D3DVERTEXELEMENT9 *declaration, DWORD *fvf) 2139 { 2140 unsigned int i = 0, texture, offset; 2141 2142 TRACE("(%p, %p)\n", declaration, fvf); 2143 2144 *fvf = 0; 2145 if (declaration[0].Type == D3DDECLTYPE_FLOAT3 && declaration[0].Usage == D3DDECLUSAGE_POSITION) 2146 { 2147 if ((declaration[1].Type == D3DDECLTYPE_FLOAT4 && declaration[1].Usage == D3DDECLUSAGE_BLENDWEIGHT && 2148 declaration[1].UsageIndex == 0) && 2149 (declaration[2].Type == D3DDECLTYPE_FLOAT1 && declaration[2].Usage == D3DDECLUSAGE_BLENDINDICES && 2150 declaration[2].UsageIndex == 0)) 2151 { 2152 return D3DERR_INVALIDCALL; 2153 } 2154 else if ((declaration[1].Type == D3DDECLTYPE_UBYTE4 || declaration[1].Type == D3DDECLTYPE_D3DCOLOR) && 2155 declaration[1].Usage == D3DDECLUSAGE_BLENDINDICES && declaration[1].UsageIndex == 0) 2156 { 2157 if (declaration[1].Type == D3DDECLTYPE_UBYTE4) 2158 { 2159 *fvf |= D3DFVF_XYZB1 | D3DFVF_LASTBETA_UBYTE4; 2160 } 2161 else 2162 { 2163 *fvf |= D3DFVF_XYZB1 | D3DFVF_LASTBETA_D3DCOLOR; 2164 } 2165 i = 2; 2166 } 2167 else if (declaration[1].Type <= D3DDECLTYPE_FLOAT4 && declaration[1].Usage == D3DDECLUSAGE_BLENDWEIGHT && 2168 declaration[1].UsageIndex == 0) 2169 { 2170 if ((declaration[2].Type == D3DDECLTYPE_UBYTE4 || declaration[2].Type == D3DDECLTYPE_D3DCOLOR) && 2171 declaration[2].Usage == D3DDECLUSAGE_BLENDINDICES && declaration[2].UsageIndex == 0) 2172 { 2173 if (declaration[2].Type == D3DDECLTYPE_UBYTE4) 2174 { 2175 *fvf |= D3DFVF_LASTBETA_UBYTE4; 2176 } 2177 else 2178 { 2179 *fvf |= D3DFVF_LASTBETA_D3DCOLOR; 2180 } 2181 switch (declaration[1].Type) 2182 { 2183 case D3DDECLTYPE_FLOAT1: *fvf |= D3DFVF_XYZB2; break; 2184 case D3DDECLTYPE_FLOAT2: *fvf |= D3DFVF_XYZB3; break; 2185 case D3DDECLTYPE_FLOAT3: *fvf |= D3DFVF_XYZB4; break; 2186 case D3DDECLTYPE_FLOAT4: *fvf |= D3DFVF_XYZB5; break; 2187 } 2188 i = 3; 2189 } 2190 else 2191 { 2192 switch (declaration[1].Type) 2193 { 2194 case D3DDECLTYPE_FLOAT1: *fvf |= D3DFVF_XYZB1; break; 2195 case D3DDECLTYPE_FLOAT2: *fvf |= D3DFVF_XYZB2; break; 2196 case D3DDECLTYPE_FLOAT3: *fvf |= D3DFVF_XYZB3; break; 2197 case D3DDECLTYPE_FLOAT4: *fvf |= D3DFVF_XYZB4; break; 2198 } 2199 i = 2; 2200 } 2201 } 2202 else 2203 { 2204 *fvf |= D3DFVF_XYZ; 2205 i = 1; 2206 } 2207 } 2208 else if (declaration[0].Type == D3DDECLTYPE_FLOAT4 && declaration[0].Usage == D3DDECLUSAGE_POSITIONT && 2209 declaration[0].UsageIndex == 0) 2210 { 2211 *fvf |= D3DFVF_XYZRHW; 2212 i = 1; 2213 } 2214 2215 if (declaration[i].Type == D3DDECLTYPE_FLOAT3 && declaration[i].Usage == D3DDECLUSAGE_NORMAL) 2216 { 2217 *fvf |= D3DFVF_NORMAL; 2218 i++; 2219 } 2220 if (declaration[i].Type == D3DDECLTYPE_FLOAT1 && declaration[i].Usage == D3DDECLUSAGE_PSIZE && 2221 declaration[i].UsageIndex == 0) 2222 { 2223 *fvf |= D3DFVF_PSIZE; 2224 i++; 2225 } 2226 if (declaration[i].Type == D3DDECLTYPE_D3DCOLOR && declaration[i].Usage == D3DDECLUSAGE_COLOR && 2227 declaration[i].UsageIndex == 0) 2228 { 2229 *fvf |= D3DFVF_DIFFUSE; 2230 i++; 2231 } 2232 if (declaration[i].Type == D3DDECLTYPE_D3DCOLOR && declaration[i].Usage == D3DDECLUSAGE_COLOR && 2233 declaration[i].UsageIndex == 1) 2234 { 2235 *fvf |= D3DFVF_SPECULAR; 2236 i++; 2237 } 2238 2239 for (texture = 0; texture < D3DDP_MAXTEXCOORD; i++, texture++) 2240 { 2241 if (declaration[i].Stream == 0xFF) 2242 { 2243 break; 2244 } 2245 else if (declaration[i].Type == D3DDECLTYPE_FLOAT1 && declaration[i].Usage == D3DDECLUSAGE_TEXCOORD && 2246 declaration[i].UsageIndex == texture) 2247 { 2248 *fvf |= D3DFVF_TEXCOORDSIZE1(declaration[i].UsageIndex); 2249 } 2250 else if (declaration[i].Type == D3DDECLTYPE_FLOAT2 && declaration[i].Usage == D3DDECLUSAGE_TEXCOORD && 2251 declaration[i].UsageIndex == texture) 2252 { 2253 *fvf |= D3DFVF_TEXCOORDSIZE2(declaration[i].UsageIndex); 2254 } 2255 else if (declaration[i].Type == D3DDECLTYPE_FLOAT3 && declaration[i].Usage == D3DDECLUSAGE_TEXCOORD && 2256 declaration[i].UsageIndex == texture) 2257 { 2258 *fvf |= D3DFVF_TEXCOORDSIZE3(declaration[i].UsageIndex); 2259 } 2260 else if (declaration[i].Type == D3DDECLTYPE_FLOAT4 && declaration[i].Usage == D3DDECLUSAGE_TEXCOORD && 2261 declaration[i].UsageIndex == texture) 2262 { 2263 *fvf |= D3DFVF_TEXCOORDSIZE4(declaration[i].UsageIndex); 2264 } 2265 else 2266 { 2267 return D3DERR_INVALIDCALL; 2268 } 2269 } 2270 2271 *fvf |= (texture << D3DFVF_TEXCOUNT_SHIFT); 2272 2273 for (offset = 0, i = 0; declaration[i].Stream != 0xFF; 2274 offset += d3dx_decltype_size[declaration[i].Type], i++) 2275 { 2276 if (declaration[i].Offset != offset) 2277 { 2278 return D3DERR_INVALIDCALL; 2279 } 2280 } 2281 2282 return D3D_OK; 2283 } 2284 2285 /************************************************************************* 2286 * D3DXGetFVFVertexSize 2287 */ 2288 static UINT Get_TexCoord_Size_From_FVF(DWORD FVF, int tex_num) 2289 { 2290 return (((((FVF) >> (16 + (2 * (tex_num)))) + 1) & 0x03) + 1); 2291 } 2292 2293 UINT WINAPI D3DXGetFVFVertexSize(DWORD FVF) 2294 { 2295 DWORD size = 0; 2296 UINT i; 2297 UINT numTextures = (FVF & D3DFVF_TEXCOUNT_MASK) >> D3DFVF_TEXCOUNT_SHIFT; 2298 2299 if (FVF & D3DFVF_NORMAL) size += sizeof(D3DXVECTOR3); 2300 if (FVF & D3DFVF_DIFFUSE) size += sizeof(DWORD); 2301 if (FVF & D3DFVF_SPECULAR) size += sizeof(DWORD); 2302 if (FVF & D3DFVF_PSIZE) size += sizeof(DWORD); 2303 2304 switch (FVF & D3DFVF_POSITION_MASK) 2305 { 2306 case D3DFVF_XYZ: size += sizeof(D3DXVECTOR3); break; 2307 case D3DFVF_XYZRHW: size += 4 * sizeof(FLOAT); break; 2308 case D3DFVF_XYZB1: size += 4 * sizeof(FLOAT); break; 2309 case D3DFVF_XYZB2: size += 5 * sizeof(FLOAT); break; 2310 case D3DFVF_XYZB3: size += 6 * sizeof(FLOAT); break; 2311 case D3DFVF_XYZB4: size += 7 * sizeof(FLOAT); break; 2312 case D3DFVF_XYZB5: size += 8 * sizeof(FLOAT); break; 2313 case D3DFVF_XYZW: size += 4 * sizeof(FLOAT); break; 2314 } 2315 2316 for (i = 0; i < numTextures; i++) 2317 { 2318 size += Get_TexCoord_Size_From_FVF(FVF, i) * sizeof(FLOAT); 2319 } 2320 2321 return size; 2322 } 2323 2324 /************************************************************************* 2325 * D3DXGetDeclVertexSize 2326 */ 2327 UINT WINAPI D3DXGetDeclVertexSize(const D3DVERTEXELEMENT9 *decl, DWORD stream_idx) 2328 { 2329 const D3DVERTEXELEMENT9 *element; 2330 UINT size = 0; 2331 2332 TRACE("decl %p, stream_idx %u\n", decl, stream_idx); 2333 2334 if (!decl) return 0; 2335 2336 for (element = decl; element->Stream != 0xff; ++element) 2337 { 2338 UINT type_size; 2339 2340 if (element->Stream != stream_idx) continue; 2341 2342 if (element->Type >= ARRAY_SIZE(d3dx_decltype_size)) 2343 { 2344 FIXME("Unhandled element type %#x, size will be incorrect.\n", element->Type); 2345 continue; 2346 } 2347 2348 type_size = d3dx_decltype_size[element->Type]; 2349 if (element->Offset + type_size > size) size = element->Offset + type_size; 2350 } 2351 2352 return size; 2353 } 2354 2355 /************************************************************************* 2356 * D3DXGetDeclLength 2357 */ 2358 UINT WINAPI D3DXGetDeclLength(const D3DVERTEXELEMENT9 *decl) 2359 { 2360 const D3DVERTEXELEMENT9 *element; 2361 2362 TRACE("decl %p\n", decl); 2363 2364 /* null decl results in exception on Windows XP */ 2365 2366 for (element = decl; element->Stream != 0xff; ++element); 2367 2368 return element - decl; 2369 } 2370 2371 BOOL WINAPI D3DXIntersectTri(const D3DXVECTOR3 *p0, const D3DXVECTOR3 *p1, const D3DXVECTOR3 *p2, 2372 const D3DXVECTOR3 *praypos, const D3DXVECTOR3 *praydir, float *pu, float *pv, float *pdist) 2373 { 2374 D3DXMATRIX m; 2375 D3DXVECTOR4 vec; 2376 2377 TRACE("p0 %p, p1 %p, p2 %p, praypos %p, praydir %p, pu %p, pv %p, pdist %p.\n", 2378 p0, p1, p2, praypos, praydir, pu, pv, pdist); 2379 2380 m.u.m[0][0] = p1->x - p0->x; 2381 m.u.m[1][0] = p2->x - p0->x; 2382 m.u.m[2][0] = -praydir->x; 2383 m.u.m[3][0] = 0.0f; 2384 m.u.m[0][1] = p1->y - p0->y; 2385 m.u.m[1][1] = p2->y - p0->y; 2386 m.u.m[2][1] = -praydir->y; 2387 m.u.m[3][1] = 0.0f; 2388 m.u.m[0][2] = p1->z - p0->z; 2389 m.u.m[1][2] = p2->z - p0->z; 2390 m.u.m[2][2] = -praydir->z; 2391 m.u.m[3][2] = 0.0f; 2392 m.u.m[0][3] = 0.0f; 2393 m.u.m[1][3] = 0.0f; 2394 m.u.m[2][3] = 0.0f; 2395 m.u.m[3][3] = 1.0f; 2396 2397 vec.x = praypos->x - p0->x; 2398 vec.y = praypos->y - p0->y; 2399 vec.z = praypos->z - p0->z; 2400 vec.w = 0.0f; 2401 2402 if ( D3DXMatrixInverse(&m, NULL, &m) ) 2403 { 2404 D3DXVec4Transform(&vec, &vec, &m); 2405 if ( (vec.x >= 0.0f) && (vec.y >= 0.0f) && (vec.x + vec.y <= 1.0f) && (vec.z >= 0.0f) ) 2406 { 2407 if (pu) *pu = vec.x; 2408 if (pv) *pv = vec.y; 2409 if (pdist) *pdist = fabsf( vec.z ); 2410 return TRUE; 2411 } 2412 } 2413 2414 return FALSE; 2415 } 2416 2417 BOOL WINAPI D3DXSphereBoundProbe(const D3DXVECTOR3 *pcenter, float radius, 2418 const D3DXVECTOR3 *prayposition, const D3DXVECTOR3 *praydirection) 2419 { 2420 D3DXVECTOR3 difference; 2421 FLOAT a, b, c, d; 2422 2423 a = D3DXVec3LengthSq(praydirection); 2424 if (!D3DXVec3Subtract(&difference, prayposition, pcenter)) return FALSE; 2425 b = D3DXVec3Dot(&difference, praydirection); 2426 c = D3DXVec3LengthSq(&difference) - radius * radius; 2427 d = b * b - a * c; 2428 2429 if ( ( d <= 0.0f ) || ( sqrt(d) <= b ) ) return FALSE; 2430 return TRUE; 2431 } 2432 2433 /************************************************************************* 2434 * D3DXCreateMesh 2435 */ 2436 HRESULT WINAPI D3DXCreateMesh(DWORD numfaces, DWORD numvertices, DWORD options, 2437 const D3DVERTEXELEMENT9 *declaration, struct IDirect3DDevice9 *device, struct ID3DXMesh **mesh) 2438 { 2439 HRESULT hr; 2440 DWORD fvf; 2441 IDirect3DVertexDeclaration9 *vertex_declaration; 2442 UINT vertex_declaration_size; 2443 UINT num_elem; 2444 IDirect3DVertexBuffer9 *vertex_buffer; 2445 IDirect3DIndexBuffer9 *index_buffer; 2446 DWORD *attrib_buffer; 2447 struct d3dx9_mesh *object; 2448 DWORD index_usage = 0; 2449 D3DPOOL index_pool = D3DPOOL_DEFAULT; 2450 D3DFORMAT index_format = D3DFMT_INDEX16; 2451 DWORD vertex_usage = 0; 2452 D3DPOOL vertex_pool = D3DPOOL_DEFAULT; 2453 int i; 2454 2455 TRACE("numfaces %u, numvertices %u, options %#x, declaration %p, device %p, mesh %p.\n", 2456 numfaces, numvertices, options, declaration, device, mesh); 2457 2458 if (numfaces == 0 || numvertices == 0 || declaration == NULL || device == NULL || mesh == NULL || 2459 /* D3DXMESH_VB_SHARE is for cloning, and D3DXMESH_USEHWONLY is for ConvertToBlendedMesh */ 2460 (options & (D3DXMESH_VB_SHARE | D3DXMESH_USEHWONLY | 0xfffe0000))) 2461 { 2462 return D3DERR_INVALIDCALL; 2463 } 2464 for (i = 0; declaration[i].Stream != 0xff; i++) 2465 if (declaration[i].Stream != 0) 2466 return D3DERR_INVALIDCALL; 2467 num_elem = i + 1; 2468 2469 if (options & D3DXMESH_32BIT) 2470 index_format = D3DFMT_INDEX32; 2471 2472 if (options & D3DXMESH_DONOTCLIP) { 2473 index_usage |= D3DUSAGE_DONOTCLIP; 2474 vertex_usage |= D3DUSAGE_DONOTCLIP; 2475 } 2476 if (options & D3DXMESH_POINTS) { 2477 index_usage |= D3DUSAGE_POINTS; 2478 vertex_usage |= D3DUSAGE_POINTS; 2479 } 2480 if (options & D3DXMESH_RTPATCHES) { 2481 index_usage |= D3DUSAGE_RTPATCHES; 2482 vertex_usage |= D3DUSAGE_RTPATCHES; 2483 } 2484 if (options & D3DXMESH_NPATCHES) { 2485 index_usage |= D3DUSAGE_NPATCHES; 2486 vertex_usage |= D3DUSAGE_NPATCHES; 2487 } 2488 2489 if (options & D3DXMESH_VB_SYSTEMMEM) 2490 vertex_pool = D3DPOOL_SYSTEMMEM; 2491 else if (options & D3DXMESH_VB_MANAGED) 2492 vertex_pool = D3DPOOL_MANAGED; 2493 2494 if (options & D3DXMESH_VB_WRITEONLY) 2495 vertex_usage |= D3DUSAGE_WRITEONLY; 2496 if (options & D3DXMESH_VB_DYNAMIC) 2497 vertex_usage |= D3DUSAGE_DYNAMIC; 2498 if (options & D3DXMESH_VB_SOFTWAREPROCESSING) 2499 vertex_usage |= D3DUSAGE_SOFTWAREPROCESSING; 2500 2501 if (options & D3DXMESH_IB_SYSTEMMEM) 2502 index_pool = D3DPOOL_SYSTEMMEM; 2503 else if (options & D3DXMESH_IB_MANAGED) 2504 index_pool = D3DPOOL_MANAGED; 2505 2506 if (options & D3DXMESH_IB_WRITEONLY) 2507 index_usage |= D3DUSAGE_WRITEONLY; 2508 if (options & D3DXMESH_IB_DYNAMIC) 2509 index_usage |= D3DUSAGE_DYNAMIC; 2510 if (options & D3DXMESH_IB_SOFTWAREPROCESSING) 2511 index_usage |= D3DUSAGE_SOFTWAREPROCESSING; 2512 2513 hr = D3DXFVFFromDeclarator(declaration, &fvf); 2514 if (hr != D3D_OK) 2515 { 2516 fvf = 0; 2517 } 2518 2519 /* Create vertex declaration */ 2520 hr = IDirect3DDevice9_CreateVertexDeclaration(device, 2521 declaration, 2522 &vertex_declaration); 2523 if (FAILED(hr)) 2524 { 2525 WARN("Unexpected return value %x from IDirect3DDevice9_CreateVertexDeclaration.\n",hr); 2526 return hr; 2527 } 2528 vertex_declaration_size = D3DXGetDeclVertexSize(declaration, declaration[0].Stream); 2529 2530 /* Create vertex buffer */ 2531 hr = IDirect3DDevice9_CreateVertexBuffer(device, 2532 numvertices * vertex_declaration_size, 2533 vertex_usage, 2534 fvf, 2535 vertex_pool, 2536 &vertex_buffer, 2537 NULL); 2538 if (FAILED(hr)) 2539 { 2540 WARN("Unexpected return value %x from IDirect3DDevice9_CreateVertexBuffer.\n",hr); 2541 IDirect3DVertexDeclaration9_Release(vertex_declaration); 2542 return hr; 2543 } 2544 2545 /* Create index buffer */ 2546 hr = IDirect3DDevice9_CreateIndexBuffer(device, 2547 numfaces * 3 * ((index_format == D3DFMT_INDEX16) ? 2 : 4), 2548 index_usage, 2549 index_format, 2550 index_pool, 2551 &index_buffer, 2552 NULL); 2553 if (FAILED(hr)) 2554 { 2555 WARN("Unexpected return value %x from IDirect3DDevice9_CreateVertexBuffer.\n",hr); 2556 IDirect3DVertexBuffer9_Release(vertex_buffer); 2557 IDirect3DVertexDeclaration9_Release(vertex_declaration); 2558 return hr; 2559 } 2560 2561 attrib_buffer = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, numfaces * sizeof(*attrib_buffer)); 2562 object = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(*object)); 2563 if (object == NULL || attrib_buffer == NULL) 2564 { 2565 HeapFree(GetProcessHeap(), 0, object); 2566 HeapFree(GetProcessHeap(), 0, attrib_buffer); 2567 IDirect3DIndexBuffer9_Release(index_buffer); 2568 IDirect3DVertexBuffer9_Release(vertex_buffer); 2569 IDirect3DVertexDeclaration9_Release(vertex_declaration); 2570 *mesh = NULL; 2571 return E_OUTOFMEMORY; 2572 } 2573 object->ID3DXMesh_iface.lpVtbl = &D3DXMesh_Vtbl; 2574 object->ref = 1; 2575 2576 object->numfaces = numfaces; 2577 object->numvertices = numvertices; 2578 object->options = options; 2579 object->fvf = fvf; 2580 object->device = device; 2581 IDirect3DDevice9_AddRef(device); 2582 2583 copy_declaration(object->cached_declaration, declaration, num_elem); 2584 object->vertex_declaration = vertex_declaration; 2585 object->vertex_declaration_size = vertex_declaration_size; 2586 object->num_elem = num_elem; 2587 object->vertex_buffer = vertex_buffer; 2588 object->index_buffer = index_buffer; 2589 object->attrib_buffer = attrib_buffer; 2590 2591 *mesh = &object->ID3DXMesh_iface; 2592 2593 return D3D_OK; 2594 } 2595 2596 /************************************************************************* 2597 * D3DXCreateMeshFVF 2598 */ 2599 HRESULT WINAPI D3DXCreateMeshFVF(DWORD numfaces, DWORD numvertices, DWORD options, 2600 DWORD fvf, struct IDirect3DDevice9 *device, struct ID3DXMesh **mesh) 2601 { 2602 HRESULT hr; 2603 D3DVERTEXELEMENT9 declaration[MAX_FVF_DECL_SIZE]; 2604 2605 TRACE("(%u, %u, %u, %u, %p, %p)\n", numfaces, numvertices, options, fvf, device, mesh); 2606 2607 hr = D3DXDeclaratorFromFVF(fvf, declaration); 2608 if (FAILED(hr)) return hr; 2609 2610 return D3DXCreateMesh(numfaces, numvertices, options, declaration, device, mesh); 2611 } 2612 2613 2614 struct mesh_data { 2615 DWORD num_vertices; 2616 DWORD num_poly_faces; 2617 DWORD num_tri_faces; 2618 D3DXVECTOR3 *vertices; 2619 DWORD *num_tri_per_face; 2620 DWORD *indices; 2621 2622 DWORD fvf; 2623 2624 /* optional mesh data */ 2625 2626 DWORD num_normals; 2627 D3DXVECTOR3 *normals; 2628 DWORD *normal_indices; 2629 2630 D3DXVECTOR2 *tex_coords; 2631 2632 DWORD *vertex_colors; 2633 2634 DWORD num_materials; 2635 D3DXMATERIAL *materials; 2636 DWORD *material_indices; 2637 2638 struct ID3DXSkinInfo *skin_info; 2639 DWORD nb_bones; 2640 }; 2641 2642 static HRESULT parse_texture_filename(ID3DXFileData *filedata, char **filename_out) 2643 { 2644 HRESULT hr; 2645 SIZE_T data_size; 2646 BYTE *data; 2647 char *filename_in; 2648 char *filename = NULL; 2649 2650 /* template TextureFilename { 2651 * STRING filename; 2652 * } 2653 */ 2654 2655 HeapFree(GetProcessHeap(), 0, *filename_out); 2656 *filename_out = NULL; 2657 2658 hr = filedata->lpVtbl->Lock(filedata, &data_size, (const void**)&data); 2659 if (FAILED(hr)) return hr; 2660 2661 /* FIXME: String must be retrieved directly instead of through a pointer once ID3DXFILE is fixed */ 2662 if (data_size < sizeof(filename_in)) 2663 { 2664 WARN("truncated data (%lu bytes)\n", data_size); 2665 filedata->lpVtbl->Unlock(filedata); 2666 return E_FAIL; 2667 } 2668 filename_in = *(char **)data; 2669 2670 filename = HeapAlloc(GetProcessHeap(), 0, strlen(filename_in) + 1); 2671 if (!filename) { 2672 filedata->lpVtbl->Unlock(filedata); 2673 return E_OUTOFMEMORY; 2674 } 2675 2676 strcpy(filename, filename_in); 2677 *filename_out = filename; 2678 2679 filedata->lpVtbl->Unlock(filedata); 2680 2681 return D3D_OK; 2682 } 2683 2684 static HRESULT parse_material(ID3DXFileData *filedata, D3DXMATERIAL *material) 2685 { 2686 HRESULT hr; 2687 SIZE_T data_size; 2688 const BYTE *data; 2689 GUID type; 2690 ID3DXFileData *child; 2691 SIZE_T i, nb_children; 2692 2693 material->pTextureFilename = NULL; 2694 2695 hr = filedata->lpVtbl->Lock(filedata, &data_size, (const void**)&data); 2696 if (FAILED(hr)) return hr; 2697 2698 /* 2699 * template ColorRGBA { 2700 * FLOAT red; 2701 * FLOAT green; 2702 * FLOAT blue; 2703 * FLOAT alpha; 2704 * } 2705 * template ColorRGB { 2706 * FLOAT red; 2707 * FLOAT green; 2708 * FLOAT blue; 2709 * } 2710 * template Material { 2711 * ColorRGBA faceColor; 2712 * FLOAT power; 2713 * ColorRGB specularColor; 2714 * ColorRGB emissiveColor; 2715 * [ ... ] 2716 * } 2717 */ 2718 if (data_size != sizeof(FLOAT) * 11) { 2719 WARN("incorrect data size (%ld bytes)\n", data_size); 2720 filedata->lpVtbl->Unlock(filedata); 2721 return E_FAIL; 2722 } 2723 2724 memcpy(&material->MatD3D.Diffuse, data, sizeof(D3DCOLORVALUE)); 2725 data += sizeof(D3DCOLORVALUE); 2726 material->MatD3D.Power = *(FLOAT*)data; 2727 data += sizeof(FLOAT); 2728 memcpy(&material->MatD3D.Specular, data, sizeof(FLOAT) * 3); 2729 material->MatD3D.Specular.a = 1.0f; 2730 data += 3 * sizeof(FLOAT); 2731 memcpy(&material->MatD3D.Emissive, data, sizeof(FLOAT) * 3); 2732 material->MatD3D.Emissive.a = 1.0f; 2733 material->MatD3D.Ambient.r = 0.0f; 2734 material->MatD3D.Ambient.g = 0.0f; 2735 material->MatD3D.Ambient.b = 0.0f; 2736 material->MatD3D.Ambient.a = 1.0f; 2737 2738 filedata->lpVtbl->Unlock(filedata); 2739 2740 hr = filedata->lpVtbl->GetChildren(filedata, &nb_children); 2741 if (FAILED(hr)) 2742 return hr; 2743 2744 for (i = 0; i < nb_children; i++) 2745 { 2746 hr = filedata->lpVtbl->GetChild(filedata, i, &child); 2747 if (FAILED(hr)) 2748 return hr; 2749 hr = child->lpVtbl->GetType(child, &type); 2750 if (FAILED(hr)) 2751 goto err; 2752 2753 if (IsEqualGUID(&type, &TID_D3DRMTextureFilename)) { 2754 hr = parse_texture_filename(child, &material->pTextureFilename); 2755 if (FAILED(hr)) 2756 goto err; 2757 } 2758 IUnknown_Release(child); 2759 } 2760 return D3D_OK; 2761 2762 err: 2763 IUnknown_Release(child); 2764 return hr; 2765 } 2766 2767 static void destroy_materials(struct mesh_data *mesh) 2768 { 2769 DWORD i; 2770 for (i = 0; i < mesh->num_materials; i++) 2771 HeapFree(GetProcessHeap(), 0, mesh->materials[i].pTextureFilename); 2772 HeapFree(GetProcessHeap(), 0, mesh->materials); 2773 HeapFree(GetProcessHeap(), 0, mesh->material_indices); 2774 mesh->num_materials = 0; 2775 mesh->materials = NULL; 2776 mesh->material_indices = NULL; 2777 } 2778 2779 static HRESULT parse_material_list(ID3DXFileData *filedata, struct mesh_data *mesh) 2780 { 2781 HRESULT hr; 2782 SIZE_T data_size; 2783 const DWORD *data, *in_ptr; 2784 GUID type; 2785 ID3DXFileData *child = NULL; 2786 DWORD num_materials; 2787 DWORD i; 2788 SIZE_T nb_children; 2789 2790 destroy_materials(mesh); 2791 2792 hr = filedata->lpVtbl->Lock(filedata, &data_size, (const void**)&data); 2793 if (FAILED(hr)) return hr; 2794 2795 /* template MeshMaterialList { 2796 * DWORD nMaterials; 2797 * DWORD nFaceIndexes; 2798 * array DWORD faceIndexes[nFaceIndexes]; 2799 * [ Material ] 2800 * } 2801 */ 2802 2803 in_ptr = data; 2804 hr = E_FAIL; 2805 2806 if (data_size < sizeof(DWORD)) { 2807 WARN("truncated data (%ld bytes)\n", data_size); 2808 goto end; 2809 } 2810 num_materials = *in_ptr++; 2811 if (!num_materials) { 2812 hr = D3D_OK; 2813 goto end; 2814 } 2815 2816 if (data_size < 2 * sizeof(DWORD)) { 2817 WARN("truncated data (%ld bytes)\n", data_size); 2818 goto end; 2819 } 2820 if (*in_ptr++ != mesh->num_poly_faces) { 2821 WARN("number of material face indices (%u) doesn't match number of faces (%u)\n", 2822 *(in_ptr - 1), mesh->num_poly_faces); 2823 goto end; 2824 } 2825 if (data_size < 2 * sizeof(DWORD) + mesh->num_poly_faces * sizeof(DWORD)) { 2826 WARN("truncated data (%ld bytes)\n", data_size); 2827 goto end; 2828 } 2829 for (i = 0; i < mesh->num_poly_faces; i++) { 2830 if (*in_ptr++ >= num_materials) { 2831 WARN("face %u: reference to undefined material %u (only %u materials)\n", 2832 i, *(in_ptr - 1), num_materials); 2833 goto end; 2834 } 2835 } 2836 2837 mesh->materials = HeapAlloc(GetProcessHeap(), 0, num_materials * sizeof(*mesh->materials)); 2838 mesh->material_indices = HeapAlloc(GetProcessHeap(), 0, mesh->num_poly_faces * sizeof(*mesh->material_indices)); 2839 if (!mesh->materials || !mesh->material_indices) { 2840 hr = E_OUTOFMEMORY; 2841 goto end; 2842 } 2843 memcpy(mesh->material_indices, data + 2, mesh->num_poly_faces * sizeof(DWORD)); 2844 2845 hr = filedata->lpVtbl->GetChildren(filedata, &nb_children); 2846 if (FAILED(hr)) 2847 goto end; 2848 2849 for (i = 0; i < nb_children; i++) 2850 { 2851 hr = filedata->lpVtbl->GetChild(filedata, i, &child); 2852 if (FAILED(hr)) 2853 goto end; 2854 hr = child->lpVtbl->GetType(child, &type); 2855 if (FAILED(hr)) 2856 goto end; 2857 2858 if (IsEqualGUID(&type, &TID_D3DRMMaterial)) { 2859 if (mesh->num_materials >= num_materials) { 2860 WARN("more materials defined than declared\n"); 2861 hr = E_FAIL; 2862 goto end; 2863 } 2864 hr = parse_material(child, &mesh->materials[mesh->num_materials++]); 2865 if (FAILED(hr)) 2866 goto end; 2867 } 2868 2869 IUnknown_Release(child); 2870 child = NULL; 2871 } 2872 if (num_materials != mesh->num_materials) { 2873 WARN("only %u of %u materials defined\n", num_materials, mesh->num_materials); 2874 hr = E_FAIL; 2875 } 2876 2877 end: 2878 if (child) 2879 IUnknown_Release(child); 2880 filedata->lpVtbl->Unlock(filedata); 2881 return hr; 2882 } 2883 2884 static HRESULT parse_texture_coords(ID3DXFileData *filedata, struct mesh_data *mesh) 2885 { 2886 HRESULT hr; 2887 SIZE_T data_size; 2888 const BYTE *data; 2889 2890 HeapFree(GetProcessHeap(), 0, mesh->tex_coords); 2891 mesh->tex_coords = NULL; 2892 2893 hr = filedata->lpVtbl->Lock(filedata, &data_size, (const void**)&data); 2894 if (FAILED(hr)) return hr; 2895 2896 /* template Coords2d { 2897 * FLOAT u; 2898 * FLOAT v; 2899 * } 2900 * template MeshTextureCoords { 2901 * DWORD nTextureCoords; 2902 * array Coords2d textureCoords[nTextureCoords]; 2903 * } 2904 */ 2905 2906 hr = E_FAIL; 2907 2908 if (data_size < sizeof(DWORD)) { 2909 WARN("truncated data (%ld bytes)\n", data_size); 2910 goto end; 2911 } 2912 if (*(DWORD*)data != mesh->num_vertices) { 2913 WARN("number of texture coordinates (%u) doesn't match number of vertices (%u)\n", 2914 *(DWORD*)data, mesh->num_vertices); 2915 goto end; 2916 } 2917 data += sizeof(DWORD); 2918 if (data_size < sizeof(DWORD) + mesh->num_vertices * sizeof(*mesh->tex_coords)) { 2919 WARN("truncated data (%ld bytes)\n", data_size); 2920 goto end; 2921 } 2922 2923 mesh->tex_coords = HeapAlloc(GetProcessHeap(), 0, mesh->num_vertices * sizeof(*mesh->tex_coords)); 2924 if (!mesh->tex_coords) { 2925 hr = E_OUTOFMEMORY; 2926 goto end; 2927 } 2928 memcpy(mesh->tex_coords, data, mesh->num_vertices * sizeof(*mesh->tex_coords)); 2929 2930 mesh->fvf |= D3DFVF_TEX1; 2931 2932 hr = D3D_OK; 2933 2934 end: 2935 filedata->lpVtbl->Unlock(filedata); 2936 return hr; 2937 } 2938 2939 static HRESULT parse_vertex_colors(ID3DXFileData *filedata, struct mesh_data *mesh) 2940 { 2941 HRESULT hr; 2942 SIZE_T data_size; 2943 const BYTE *data; 2944 DWORD num_colors; 2945 DWORD i; 2946 2947 HeapFree(GetProcessHeap(), 0, mesh->vertex_colors); 2948 mesh->vertex_colors = NULL; 2949 2950 hr = filedata->lpVtbl->Lock(filedata, &data_size, (const void**)&data); 2951 if (FAILED(hr)) return hr; 2952 2953 /* template IndexedColor { 2954 * DWORD index; 2955 * ColorRGBA indexColor; 2956 * } 2957 * template MeshVertexColors { 2958 * DWORD nVertexColors; 2959 * array IndexedColor vertexColors[nVertexColors]; 2960 * } 2961 */ 2962 2963 hr = E_FAIL; 2964 2965 if (data_size < sizeof(DWORD)) { 2966 WARN("truncated data (%ld bytes)\n", data_size); 2967 goto end; 2968 } 2969 num_colors = *(DWORD*)data; 2970 data += sizeof(DWORD); 2971 if (data_size < sizeof(DWORD) + num_colors * (sizeof(DWORD) + sizeof(D3DCOLORVALUE))) { 2972 WARN("truncated data (%ld bytes)\n", data_size); 2973 goto end; 2974 } 2975 2976 mesh->vertex_colors = HeapAlloc(GetProcessHeap(), 0, mesh->num_vertices * sizeof(DWORD)); 2977 if (!mesh->vertex_colors) { 2978 hr = E_OUTOFMEMORY; 2979 goto end; 2980 } 2981 2982 for (i = 0; i < mesh->num_vertices; i++) 2983 mesh->vertex_colors[i] = D3DCOLOR_ARGB(0, 0xff, 0xff, 0xff); 2984 for (i = 0; i < num_colors; i++) 2985 { 2986 D3DCOLORVALUE color; 2987 DWORD index = *(DWORD*)data; 2988 data += sizeof(DWORD); 2989 if (index >= mesh->num_vertices) { 2990 WARN("vertex color %u references undefined vertex %u (only %u vertices)\n", 2991 i, index, mesh->num_vertices); 2992 goto end; 2993 } 2994 memcpy(&color, data, sizeof(color)); 2995 data += sizeof(color); 2996 color.r = min(1.0f, max(0.0f, color.r)); 2997 color.g = min(1.0f, max(0.0f, color.g)); 2998 color.b = min(1.0f, max(0.0f, color.b)); 2999 color.a = min(1.0f, max(0.0f, color.a)); 3000 mesh->vertex_colors[index] = D3DCOLOR_ARGB((BYTE)(color.a * 255.0f + 0.5f), 3001 (BYTE)(color.r * 255.0f + 0.5f), 3002 (BYTE)(color.g * 255.0f + 0.5f), 3003 (BYTE)(color.b * 255.0f + 0.5f)); 3004 } 3005 3006 mesh->fvf |= D3DFVF_DIFFUSE; 3007 3008 hr = D3D_OK; 3009 3010 end: 3011 filedata->lpVtbl->Unlock(filedata); 3012 return hr; 3013 } 3014 3015 static HRESULT parse_normals(ID3DXFileData *filedata, struct mesh_data *mesh) 3016 { 3017 HRESULT hr; 3018 SIZE_T data_size; 3019 const BYTE *data; 3020 DWORD *index_out_ptr; 3021 DWORD i; 3022 DWORD num_face_indices = mesh->num_poly_faces * 2 + mesh->num_tri_faces; 3023 3024 HeapFree(GetProcessHeap(), 0, mesh->normals); 3025 mesh->num_normals = 0; 3026 mesh->normals = NULL; 3027 mesh->normal_indices = NULL; 3028 mesh->fvf |= D3DFVF_NORMAL; 3029 3030 hr = filedata->lpVtbl->Lock(filedata, &data_size, (const void**)&data); 3031 if (FAILED(hr)) return hr; 3032 3033 /* template Vector { 3034 * FLOAT x; 3035 * FLOAT y; 3036 * FLOAT z; 3037 * } 3038 * template MeshFace { 3039 * DWORD nFaceVertexIndices; 3040 * array DWORD faceVertexIndices[nFaceVertexIndices]; 3041 * } 3042 * template MeshNormals { 3043 * DWORD nNormals; 3044 * array Vector normals[nNormals]; 3045 * DWORD nFaceNormals; 3046 * array MeshFace faceNormals[nFaceNormals]; 3047 * } 3048 */ 3049 3050 hr = E_FAIL; 3051 3052 if (data_size < sizeof(DWORD) * 2) { 3053 WARN("truncated data (%ld bytes)\n", data_size); 3054 goto end; 3055 } 3056 mesh->num_normals = *(DWORD*)data; 3057 data += sizeof(DWORD); 3058 if (data_size < sizeof(DWORD) * 2 + mesh->num_normals * sizeof(D3DXVECTOR3) + 3059 num_face_indices * sizeof(DWORD)) { 3060 WARN("truncated data (%ld bytes)\n", data_size); 3061 goto end; 3062 } 3063 3064 mesh->normals = HeapAlloc(GetProcessHeap(), 0, mesh->num_normals * sizeof(D3DXVECTOR3)); 3065 mesh->normal_indices = HeapAlloc(GetProcessHeap(), 0, num_face_indices * sizeof(DWORD)); 3066 if (!mesh->normals || !mesh->normal_indices) { 3067 hr = E_OUTOFMEMORY; 3068 goto end; 3069 } 3070 3071 memcpy(mesh->normals, data, mesh->num_normals * sizeof(D3DXVECTOR3)); 3072 data += mesh->num_normals * sizeof(D3DXVECTOR3); 3073 for (i = 0; i < mesh->num_normals; i++) 3074 D3DXVec3Normalize(&mesh->normals[i], &mesh->normals[i]); 3075 3076 if (*(DWORD*)data != mesh->num_poly_faces) { 3077 WARN("number of face normals (%u) doesn't match number of faces (%u)\n", 3078 *(DWORD*)data, mesh->num_poly_faces); 3079 goto end; 3080 } 3081 data += sizeof(DWORD); 3082 index_out_ptr = mesh->normal_indices; 3083 for (i = 0; i < mesh->num_poly_faces; i++) 3084 { 3085 DWORD j; 3086 DWORD count = *(DWORD*)data; 3087 if (count != mesh->num_tri_per_face[i] + 2) { 3088 WARN("face %u: number of normals (%u) doesn't match number of vertices (%u)\n", 3089 i, count, mesh->num_tri_per_face[i] + 2); 3090 goto end; 3091 } 3092 data += sizeof(DWORD); 3093 3094 for (j = 0; j < count; j++) { 3095 DWORD normal_index = *(DWORD*)data; 3096 if (normal_index >= mesh->num_normals) { 3097 WARN("face %u, normal index %u: reference to undefined normal %u (only %u normals)\n", 3098 i, j, normal_index, mesh->num_normals); 3099 goto end; 3100 } 3101 *index_out_ptr++ = normal_index; 3102 data += sizeof(DWORD); 3103 } 3104 } 3105 3106 hr = D3D_OK; 3107 3108 end: 3109 filedata->lpVtbl->Unlock(filedata); 3110 return hr; 3111 } 3112 3113 static HRESULT parse_skin_mesh_info(ID3DXFileData *filedata, struct mesh_data *mesh_data, DWORD index) 3114 { 3115 HRESULT hr; 3116 SIZE_T data_size; 3117 const BYTE *data; 3118 3119 TRACE("(%p, %p, %u)\n", filedata, mesh_data, index); 3120 3121 hr = filedata->lpVtbl->Lock(filedata, &data_size, (const void**)&data); 3122 if (FAILED(hr)) return hr; 3123 3124 hr = E_FAIL; 3125 3126 if (!mesh_data->skin_info) { 3127 if (data_size < sizeof(WORD) * 3) { 3128 WARN("truncated data (%ld bytes)\n", data_size); 3129 goto end; 3130 } 3131 /* Skip nMaxSkinWeightsPerVertex and nMaxSkinWeightsPerFace */ 3132 data += 2 * sizeof(WORD); 3133 mesh_data->nb_bones = *(WORD*)data; 3134 hr = D3DXCreateSkinInfoFVF(mesh_data->num_vertices, mesh_data->fvf, mesh_data->nb_bones, &mesh_data->skin_info); 3135 } else { 3136 const char *name; 3137 DWORD nb_influences; 3138 3139 /* FIXME: String must be retrieved directly instead of through a pointer once ID3DXFILE is fixed */ 3140 name = *(const char**)data; 3141 data += sizeof(char*); 3142 3143 nb_influences = *(DWORD*)data; 3144 data += sizeof(DWORD); 3145 3146 if (data_size < (sizeof(char*) + sizeof(DWORD) + nb_influences * (sizeof(DWORD) + sizeof(FLOAT)) + 16 * sizeof(FLOAT))) { 3147 WARN("truncated data (%ld bytes)\n", data_size); 3148 goto end; 3149 } 3150 3151 hr = mesh_data->skin_info->lpVtbl->SetBoneName(mesh_data->skin_info, index, name); 3152 if (SUCCEEDED(hr)) 3153 hr = mesh_data->skin_info->lpVtbl->SetBoneInfluence(mesh_data->skin_info, index, nb_influences, 3154 (const DWORD*)data, (const FLOAT*)(data + nb_influences * sizeof(DWORD))); 3155 if (SUCCEEDED(hr)) 3156 hr = mesh_data->skin_info->lpVtbl->SetBoneOffsetMatrix(mesh_data->skin_info, index, 3157 (const D3DMATRIX*)(data + nb_influences * (sizeof(DWORD) + sizeof(FLOAT)))); 3158 } 3159 3160 end: 3161 filedata->lpVtbl->Unlock(filedata); 3162 return hr; 3163 } 3164 3165 /* for provide_flags parameters */ 3166 #define PROVIDE_MATERIALS 0x1 3167 #define PROVIDE_SKININFO 0x2 3168 #define PROVIDE_ADJACENCY 0x4 3169 3170 static HRESULT parse_mesh(ID3DXFileData *filedata, struct mesh_data *mesh_data, DWORD provide_flags) 3171 { 3172 HRESULT hr; 3173 SIZE_T data_size; 3174 const BYTE *data, *in_ptr; 3175 DWORD *index_out_ptr; 3176 GUID type; 3177 ID3DXFileData *child = NULL; 3178 DWORD i; 3179 SIZE_T nb_children; 3180 DWORD nb_skin_weights_info = 0; 3181 3182 /* 3183 * template Mesh { 3184 * DWORD nVertices; 3185 * array Vector vertices[nVertices]; 3186 * DWORD nFaces; 3187 * array MeshFace faces[nFaces]; 3188 * [ ... ] 3189 * } 3190 */ 3191 3192 hr = filedata->lpVtbl->Lock(filedata, &data_size, (const void**)&data); 3193 if (FAILED(hr)) return hr; 3194 3195 in_ptr = data; 3196 hr = E_FAIL; 3197 3198 if (data_size < sizeof(DWORD) * 2) { 3199 WARN("truncated data (%ld bytes)\n", data_size); 3200 goto end; 3201 } 3202 mesh_data->num_vertices = *(DWORD*)in_ptr; 3203 if (data_size < sizeof(DWORD) * 2 + mesh_data->num_vertices * sizeof(D3DXVECTOR3)) { 3204 WARN("truncated data (%ld bytes)\n", data_size); 3205 goto end; 3206 } 3207 in_ptr += sizeof(DWORD) + mesh_data->num_vertices * sizeof(D3DXVECTOR3); 3208 3209 mesh_data->num_poly_faces = *(DWORD*)in_ptr; 3210 in_ptr += sizeof(DWORD); 3211 3212 mesh_data->num_tri_faces = 0; 3213 for (i = 0; i < mesh_data->num_poly_faces; i++) 3214 { 3215 DWORD num_poly_vertices; 3216 DWORD j; 3217 3218 if (data_size - (in_ptr - data) < sizeof(DWORD)) { 3219 WARN("truncated data (%ld bytes)\n", data_size); 3220 goto end; 3221 } 3222 num_poly_vertices = *(DWORD*)in_ptr; 3223 in_ptr += sizeof(DWORD); 3224 if (data_size - (in_ptr - data) < num_poly_vertices * sizeof(DWORD)) { 3225 WARN("truncated data (%ld bytes)\n", data_size); 3226 goto end; 3227 } 3228 if (num_poly_vertices < 3) { 3229 WARN("face %u has only %u vertices\n", i, num_poly_vertices); 3230 goto end; 3231 } 3232 for (j = 0; j < num_poly_vertices; j++) { 3233 if (*(DWORD*)in_ptr >= mesh_data->num_vertices) { 3234 WARN("face %u, index %u: undefined vertex %u (only %u vertices)\n", 3235 i, j, *(DWORD*)in_ptr, mesh_data->num_vertices); 3236 goto end; 3237 } 3238 in_ptr += sizeof(DWORD); 3239 } 3240 mesh_data->num_tri_faces += num_poly_vertices - 2; 3241 } 3242 3243 mesh_data->fvf = D3DFVF_XYZ; 3244 3245 mesh_data->vertices = HeapAlloc(GetProcessHeap(), 0, 3246 mesh_data->num_vertices * sizeof(*mesh_data->vertices)); 3247 mesh_data->num_tri_per_face = HeapAlloc(GetProcessHeap(), 0, 3248 mesh_data->num_poly_faces * sizeof(*mesh_data->num_tri_per_face)); 3249 mesh_data->indices = HeapAlloc(GetProcessHeap(), 0, 3250 (mesh_data->num_tri_faces + mesh_data->num_poly_faces * 2) * sizeof(*mesh_data->indices)); 3251 if (!mesh_data->vertices || !mesh_data->num_tri_per_face || !mesh_data->indices) { 3252 hr = E_OUTOFMEMORY; 3253 goto end; 3254 } 3255 3256 in_ptr = data + sizeof(DWORD); 3257 memcpy(mesh_data->vertices, in_ptr, mesh_data->num_vertices * sizeof(D3DXVECTOR3)); 3258 in_ptr += mesh_data->num_vertices * sizeof(D3DXVECTOR3) + sizeof(DWORD); 3259 3260 index_out_ptr = mesh_data->indices; 3261 for (i = 0; i < mesh_data->num_poly_faces; i++) 3262 { 3263 DWORD count; 3264 3265 count = *(DWORD*)in_ptr; 3266 in_ptr += sizeof(DWORD); 3267 mesh_data->num_tri_per_face[i] = count - 2; 3268 3269 while (count--) { 3270 *index_out_ptr++ = *(DWORD*)in_ptr; 3271 in_ptr += sizeof(DWORD); 3272 } 3273 } 3274 3275 hr = filedata->lpVtbl->GetChildren(filedata, &nb_children); 3276 if (FAILED(hr)) 3277 goto end; 3278 3279 for (i = 0; i < nb_children; i++) 3280 { 3281 hr = filedata->lpVtbl->GetChild(filedata, i, &child); 3282 if (FAILED(hr)) 3283 goto end; 3284 hr = child->lpVtbl->GetType(child, &type); 3285 if (FAILED(hr)) 3286 goto end; 3287 3288 if (IsEqualGUID(&type, &TID_D3DRMMeshNormals)) { 3289 hr = parse_normals(child, mesh_data); 3290 } else if (IsEqualGUID(&type, &TID_D3DRMMeshVertexColors)) { 3291 hr = parse_vertex_colors(child, mesh_data); 3292 } else if (IsEqualGUID(&type, &TID_D3DRMMeshTextureCoords)) { 3293 hr = parse_texture_coords(child, mesh_data); 3294 } else if (IsEqualGUID(&type, &TID_D3DRMMeshMaterialList) && 3295 (provide_flags & PROVIDE_MATERIALS)) 3296 { 3297 hr = parse_material_list(child, mesh_data); 3298 } else if (provide_flags & PROVIDE_SKININFO) { 3299 if (IsEqualGUID(&type, &DXFILEOBJ_XSkinMeshHeader)) { 3300 if (mesh_data->skin_info) { 3301 WARN("Skin mesh header already encountered\n"); 3302 hr = E_FAIL; 3303 goto end; 3304 } 3305 hr = parse_skin_mesh_info(child, mesh_data, 0); 3306 if (FAILED(hr)) 3307 goto end; 3308 } else if (IsEqualGUID(&type, &DXFILEOBJ_SkinWeights)) { 3309 if (!mesh_data->skin_info) { 3310 WARN("Skin weights found but skin mesh header not encountered yet\n"); 3311 hr = E_FAIL; 3312 goto end; 3313 } 3314 hr = parse_skin_mesh_info(child, mesh_data, nb_skin_weights_info); 3315 if (FAILED(hr)) 3316 goto end; 3317 nb_skin_weights_info++; 3318 } 3319 } 3320 if (FAILED(hr)) 3321 goto end; 3322 3323 IUnknown_Release(child); 3324 child = NULL; 3325 } 3326 3327 if (mesh_data->skin_info && (nb_skin_weights_info != mesh_data->nb_bones)) { 3328 WARN("Mismatch between nb skin weights info %u encountered and nb bones %u from skin mesh header\n", 3329 nb_skin_weights_info, mesh_data->nb_bones); 3330 hr = E_FAIL; 3331 goto end; 3332 } 3333 3334 if ((provide_flags & PROVIDE_SKININFO) && !mesh_data->skin_info) 3335 { 3336 if (FAILED(hr = D3DXCreateSkinInfoFVF(mesh_data->num_vertices, mesh_data->fvf, 3337 mesh_data->nb_bones, &mesh_data->skin_info))) 3338 goto end; 3339 } 3340 3341 hr = D3D_OK; 3342 3343 end: 3344 if (child) 3345 IUnknown_Release(child); 3346 filedata->lpVtbl->Unlock(filedata); 3347 return hr; 3348 } 3349 3350 static HRESULT generate_effects(ID3DXBuffer *materials, DWORD num_materials, 3351 ID3DXBuffer **effects) 3352 { 3353 HRESULT hr; 3354 D3DXEFFECTINSTANCE *effect_ptr; 3355 BYTE *out_ptr; 3356 const D3DXMATERIAL *material_ptr = ID3DXBuffer_GetBufferPointer(materials); 3357 static const struct { 3358 const char *param_name; 3359 DWORD name_size; 3360 DWORD num_bytes; 3361 DWORD value_offset; 3362 } material_effects[] = { 3363 #define EFFECT_TABLE_ENTRY(str, field) \ 3364 {str, sizeof(str), sizeof(material_ptr->MatD3D.field), offsetof(D3DXMATERIAL, MatD3D.field)} 3365 EFFECT_TABLE_ENTRY("Diffuse", Diffuse), 3366 EFFECT_TABLE_ENTRY("Power", Power), 3367 EFFECT_TABLE_ENTRY("Specular", Specular), 3368 EFFECT_TABLE_ENTRY("Emissive", Emissive), 3369 EFFECT_TABLE_ENTRY("Ambient", Ambient), 3370 #undef EFFECT_TABLE_ENTRY 3371 }; 3372 static const char texture_paramname[] = "Texture0@Name"; 3373 DWORD buffer_size; 3374 DWORD i; 3375 3376 /* effects buffer layout: 3377 * 3378 * D3DXEFFECTINSTANCE effects[num_materials]; 3379 * for (effect in effects) 3380 * { 3381 * D3DXEFFECTDEFAULT defaults[effect.NumDefaults]; 3382 * for (default in defaults) 3383 * { 3384 * *default.pParamName; 3385 * *default.pValue; 3386 * } 3387 * } 3388 */ 3389 buffer_size = sizeof(D3DXEFFECTINSTANCE); 3390 buffer_size += sizeof(D3DXEFFECTDEFAULT) * ARRAY_SIZE(material_effects); 3391 for (i = 0; i < ARRAY_SIZE(material_effects); i++) { 3392 buffer_size += material_effects[i].name_size; 3393 buffer_size += material_effects[i].num_bytes; 3394 } 3395 buffer_size *= num_materials; 3396 for (i = 0; i < num_materials; i++) { 3397 if (material_ptr[i].pTextureFilename) { 3398 buffer_size += sizeof(D3DXEFFECTDEFAULT); 3399 buffer_size += sizeof(texture_paramname); 3400 buffer_size += strlen(material_ptr[i].pTextureFilename) + 1; 3401 } 3402 } 3403 3404 hr = D3DXCreateBuffer(buffer_size, effects); 3405 if (FAILED(hr)) return hr; 3406 effect_ptr = ID3DXBuffer_GetBufferPointer(*effects); 3407 out_ptr = (BYTE*)(effect_ptr + num_materials); 3408 3409 for (i = 0; i < num_materials; i++) 3410 { 3411 DWORD j; 3412 D3DXEFFECTDEFAULT *defaults = (D3DXEFFECTDEFAULT*)out_ptr; 3413 3414 effect_ptr->pDefaults = defaults; 3415 effect_ptr->NumDefaults = material_ptr->pTextureFilename ? 6 : 5; 3416 out_ptr = (BYTE*)(effect_ptr->pDefaults + effect_ptr->NumDefaults); 3417 3418 for (j = 0; j < ARRAY_SIZE(material_effects); j++) 3419 { 3420 defaults->pParamName = (char *)out_ptr; 3421 strcpy(defaults->pParamName, material_effects[j].param_name); 3422 defaults->pValue = defaults->pParamName + material_effects[j].name_size; 3423 defaults->Type = D3DXEDT_FLOATS; 3424 defaults->NumBytes = material_effects[j].num_bytes; 3425 memcpy(defaults->pValue, (BYTE*)material_ptr + material_effects[j].value_offset, defaults->NumBytes); 3426 out_ptr = (BYTE*)defaults->pValue + defaults->NumBytes; 3427 defaults++; 3428 } 3429 3430 if (material_ptr->pTextureFilename) 3431 { 3432 defaults->pParamName = (char *)out_ptr; 3433 strcpy(defaults->pParamName, texture_paramname); 3434 defaults->pValue = defaults->pParamName + sizeof(texture_paramname); 3435 defaults->Type = D3DXEDT_STRING; 3436 defaults->NumBytes = strlen(material_ptr->pTextureFilename) + 1; 3437 strcpy(defaults->pValue, material_ptr->pTextureFilename); 3438 out_ptr = (BYTE*)defaults->pValue + defaults->NumBytes; 3439 } 3440 material_ptr++; 3441 effect_ptr++; 3442 } 3443 assert(out_ptr - (BYTE*)ID3DXBuffer_GetBufferPointer(*effects) == buffer_size); 3444 3445 return D3D_OK; 3446 } 3447 3448 HRESULT WINAPI D3DXLoadSkinMeshFromXof(struct ID3DXFileData *filedata, DWORD options, 3449 struct IDirect3DDevice9 *device, struct ID3DXBuffer **adjacency_out, struct ID3DXBuffer **materials_out, 3450 struct ID3DXBuffer **effects_out, DWORD *num_materials_out, struct ID3DXSkinInfo **skin_info_out, 3451 struct ID3DXMesh **mesh_out) 3452 { 3453 HRESULT hr; 3454 DWORD *index_in_ptr; 3455 struct mesh_data mesh_data; 3456 DWORD total_vertices; 3457 ID3DXMesh *d3dxmesh = NULL; 3458 ID3DXBuffer *adjacency = NULL; 3459 ID3DXBuffer *materials = NULL; 3460 ID3DXBuffer *effects = NULL; 3461 struct vertex_duplication { 3462 DWORD normal_index; 3463 struct list entry; 3464 } *duplications = NULL; 3465 DWORD i; 3466 void *vertices = NULL; 3467 void *indices = NULL; 3468 BYTE *out_ptr; 3469 DWORD provide_flags = 0; 3470 3471 TRACE("(%p, %x, %p, %p, %p, %p, %p, %p, %p)\n", filedata, options, device, adjacency_out, materials_out, 3472 effects_out, num_materials_out, skin_info_out, mesh_out); 3473 3474 ZeroMemory(&mesh_data, sizeof(mesh_data)); 3475 3476 if (num_materials_out || materials_out || effects_out) 3477 provide_flags |= PROVIDE_MATERIALS; 3478 if (skin_info_out) 3479 provide_flags |= PROVIDE_SKININFO; 3480 3481 hr = parse_mesh(filedata, &mesh_data, provide_flags); 3482 if (FAILED(hr)) goto cleanup; 3483 3484 total_vertices = mesh_data.num_vertices; 3485 if (mesh_data.fvf & D3DFVF_NORMAL) { 3486 /* duplicate vertices with multiple normals */ 3487 DWORD num_face_indices = mesh_data.num_poly_faces * 2 + mesh_data.num_tri_faces; 3488 duplications = HeapAlloc(GetProcessHeap(), 0, (mesh_data.num_vertices + num_face_indices) * sizeof(*duplications)); 3489 if (!duplications) { 3490 hr = E_OUTOFMEMORY; 3491 goto cleanup; 3492 } 3493 for (i = 0; i < total_vertices; i++) 3494 { 3495 duplications[i].normal_index = -1; 3496 list_init(&duplications[i].entry); 3497 } 3498 for (i = 0; i < num_face_indices; i++) { 3499 DWORD vertex_index = mesh_data.indices[i]; 3500 DWORD normal_index = mesh_data.normal_indices[i]; 3501 struct vertex_duplication *dup_ptr = &duplications[vertex_index]; 3502 3503 if (dup_ptr->normal_index == -1) { 3504 dup_ptr->normal_index = normal_index; 3505 } else { 3506 D3DXVECTOR3 *new_normal = &mesh_data.normals[normal_index]; 3507 struct list *dup_list = &dup_ptr->entry; 3508 while (TRUE) { 3509 D3DXVECTOR3 *cur_normal = &mesh_data.normals[dup_ptr->normal_index]; 3510 if (new_normal->x == cur_normal->x && 3511 new_normal->y == cur_normal->y && 3512 new_normal->z == cur_normal->z) 3513 { 3514 mesh_data.indices[i] = dup_ptr - duplications; 3515 break; 3516 } else if (!list_next(dup_list, &dup_ptr->entry)) { 3517 dup_ptr = &duplications[total_vertices++]; 3518 dup_ptr->normal_index = normal_index; 3519 list_add_tail(dup_list, &dup_ptr->entry); 3520 mesh_data.indices[i] = dup_ptr - duplications; 3521 break; 3522 } else { 3523 dup_ptr = LIST_ENTRY(list_next(dup_list, &dup_ptr->entry), 3524 struct vertex_duplication, entry); 3525 } 3526 } 3527 } 3528 } 3529 } 3530 3531 hr = D3DXCreateMeshFVF(mesh_data.num_tri_faces, total_vertices, options, mesh_data.fvf, device, &d3dxmesh); 3532 if (FAILED(hr)) goto cleanup; 3533 3534 hr = d3dxmesh->lpVtbl->LockVertexBuffer(d3dxmesh, 0, &vertices); 3535 if (FAILED(hr)) goto cleanup; 3536 3537 out_ptr = vertices; 3538 for (i = 0; i < mesh_data.num_vertices; i++) { 3539 *(D3DXVECTOR3*)out_ptr = mesh_data.vertices[i]; 3540 out_ptr += sizeof(D3DXVECTOR3); 3541 if (mesh_data.fvf & D3DFVF_NORMAL) { 3542 if (duplications[i].normal_index == -1) 3543 ZeroMemory(out_ptr, sizeof(D3DXVECTOR3)); 3544 else 3545 *(D3DXVECTOR3*)out_ptr = mesh_data.normals[duplications[i].normal_index]; 3546 out_ptr += sizeof(D3DXVECTOR3); 3547 } 3548 if (mesh_data.fvf & D3DFVF_DIFFUSE) { 3549 *(DWORD*)out_ptr = mesh_data.vertex_colors[i]; 3550 out_ptr += sizeof(DWORD); 3551 } 3552 if (mesh_data.fvf & D3DFVF_TEX1) { 3553 *(D3DXVECTOR2*)out_ptr = mesh_data.tex_coords[i]; 3554 out_ptr += sizeof(D3DXVECTOR2); 3555 } 3556 } 3557 if (mesh_data.fvf & D3DFVF_NORMAL) { 3558 DWORD vertex_size = D3DXGetFVFVertexSize(mesh_data.fvf); 3559 out_ptr = vertices; 3560 for (i = 0; i < mesh_data.num_vertices; i++) { 3561 struct vertex_duplication *dup_ptr; 3562 LIST_FOR_EACH_ENTRY(dup_ptr, &duplications[i].entry, struct vertex_duplication, entry) 3563 { 3564 int j = dup_ptr - duplications; 3565 BYTE *dest_vertex = (BYTE*)vertices + j * vertex_size; 3566 3567 memcpy(dest_vertex, out_ptr, vertex_size); 3568 dest_vertex += sizeof(D3DXVECTOR3); 3569 *(D3DXVECTOR3*)dest_vertex = mesh_data.normals[dup_ptr->normal_index]; 3570 } 3571 out_ptr += vertex_size; 3572 } 3573 } 3574 d3dxmesh->lpVtbl->UnlockVertexBuffer(d3dxmesh); 3575 3576 hr = d3dxmesh->lpVtbl->LockIndexBuffer(d3dxmesh, 0, &indices); 3577 if (FAILED(hr)) goto cleanup; 3578 3579 index_in_ptr = mesh_data.indices; 3580 #define FILL_INDEX_BUFFER(indices_var) \ 3581 for (i = 0; i < mesh_data.num_poly_faces; i++) \ 3582 { \ 3583 DWORD count = mesh_data.num_tri_per_face[i]; \ 3584 WORD first_index = *index_in_ptr++; \ 3585 while (count--) { \ 3586 *indices_var++ = first_index; \ 3587 *indices_var++ = *index_in_ptr; \ 3588 index_in_ptr++; \ 3589 *indices_var++ = *index_in_ptr; \ 3590 } \ 3591 index_in_ptr++; \ 3592 } 3593 if (options & D3DXMESH_32BIT) { 3594 DWORD *dword_indices = indices; 3595 FILL_INDEX_BUFFER(dword_indices) 3596 } else { 3597 WORD *word_indices = indices; 3598 FILL_INDEX_BUFFER(word_indices) 3599 } 3600 #undef FILL_INDEX_BUFFER 3601 d3dxmesh->lpVtbl->UnlockIndexBuffer(d3dxmesh); 3602 3603 if (mesh_data.material_indices) { 3604 DWORD *attrib_buffer = NULL; 3605 hr = d3dxmesh->lpVtbl->LockAttributeBuffer(d3dxmesh, 0, &attrib_buffer); 3606 if (FAILED(hr)) goto cleanup; 3607 for (i = 0; i < mesh_data.num_poly_faces; i++) 3608 { 3609 DWORD count = mesh_data.num_tri_per_face[i]; 3610 while (count--) 3611 *attrib_buffer++ = mesh_data.material_indices[i]; 3612 } 3613 d3dxmesh->lpVtbl->UnlockAttributeBuffer(d3dxmesh); 3614 3615 hr = d3dxmesh->lpVtbl->OptimizeInplace(d3dxmesh, 3616 D3DXMESHOPT_ATTRSORT | D3DXMESHOPT_IGNOREVERTS | D3DXMESHOPT_DONOTSPLIT, 3617 NULL, NULL, NULL, NULL); 3618 if (FAILED(hr)) goto cleanup; 3619 } 3620 3621 if (mesh_data.num_materials && (materials_out || effects_out)) { 3622 DWORD buffer_size = mesh_data.num_materials * sizeof(D3DXMATERIAL); 3623 char *strings_out_ptr; 3624 D3DXMATERIAL *materials_ptr; 3625 3626 for (i = 0; i < mesh_data.num_materials; i++) { 3627 if (mesh_data.materials[i].pTextureFilename) 3628 buffer_size += strlen(mesh_data.materials[i].pTextureFilename) + 1; 3629 } 3630 3631 hr = D3DXCreateBuffer(buffer_size, &materials); 3632 if (FAILED(hr)) goto cleanup; 3633 3634 materials_ptr = ID3DXBuffer_GetBufferPointer(materials); 3635 memcpy(materials_ptr, mesh_data.materials, mesh_data.num_materials * sizeof(D3DXMATERIAL)); 3636 strings_out_ptr = (char*)(materials_ptr + mesh_data.num_materials); 3637 for (i = 0; i < mesh_data.num_materials; i++) { 3638 if (materials_ptr[i].pTextureFilename) { 3639 strcpy(strings_out_ptr, mesh_data.materials[i].pTextureFilename); 3640 materials_ptr[i].pTextureFilename = strings_out_ptr; 3641 strings_out_ptr += strlen(mesh_data.materials[i].pTextureFilename) + 1; 3642 } 3643 } 3644 } 3645 3646 if (mesh_data.num_materials && effects_out) { 3647 hr = generate_effects(materials, mesh_data.num_materials, &effects); 3648 if (FAILED(hr)) goto cleanup; 3649 3650 if (!materials_out) { 3651 ID3DXBuffer_Release(materials); 3652 materials = NULL; 3653 } 3654 } 3655 3656 if (adjacency_out) { 3657 hr = D3DXCreateBuffer(mesh_data.num_tri_faces * 3 * sizeof(DWORD), &adjacency); 3658 if (FAILED(hr)) goto cleanup; 3659 hr = d3dxmesh->lpVtbl->GenerateAdjacency(d3dxmesh, 0.0f, ID3DXBuffer_GetBufferPointer(adjacency)); 3660 if (FAILED(hr)) goto cleanup; 3661 } 3662 3663 *mesh_out = d3dxmesh; 3664 if (adjacency_out) *adjacency_out = adjacency; 3665 if (num_materials_out) *num_materials_out = mesh_data.num_materials; 3666 if (materials_out) *materials_out = materials; 3667 if (effects_out) *effects_out = effects; 3668 if (skin_info_out) *skin_info_out = mesh_data.skin_info; 3669 3670 hr = D3D_OK; 3671 cleanup: 3672 if (FAILED(hr)) { 3673 if (d3dxmesh) IUnknown_Release(d3dxmesh); 3674 if (adjacency) ID3DXBuffer_Release(adjacency); 3675 if (materials) ID3DXBuffer_Release(materials); 3676 if (effects) ID3DXBuffer_Release(effects); 3677 if (mesh_data.skin_info) mesh_data.skin_info->lpVtbl->Release(mesh_data.skin_info); 3678 if (skin_info_out) *skin_info_out = NULL; 3679 } 3680 HeapFree(GetProcessHeap(), 0, mesh_data.vertices); 3681 HeapFree(GetProcessHeap(), 0, mesh_data.num_tri_per_face); 3682 HeapFree(GetProcessHeap(), 0, mesh_data.indices); 3683 HeapFree(GetProcessHeap(), 0, mesh_data.normals); 3684 HeapFree(GetProcessHeap(), 0, mesh_data.normal_indices); 3685 destroy_materials(&mesh_data); 3686 HeapFree(GetProcessHeap(), 0, mesh_data.tex_coords); 3687 HeapFree(GetProcessHeap(), 0, mesh_data.vertex_colors); 3688 HeapFree(GetProcessHeap(), 0, duplications); 3689 return hr; 3690 } 3691 3692 HRESULT WINAPI D3DXLoadMeshHierarchyFromXA(const char *filename, DWORD options, struct IDirect3DDevice9 *device, 3693 struct ID3DXAllocateHierarchy *alloc_hier, struct ID3DXLoadUserData *load_user_data, 3694 D3DXFRAME **frame_hierarchy, struct ID3DXAnimationController **anim_controller) 3695 { 3696 WCHAR *filenameW; 3697 HRESULT hr; 3698 int len; 3699 3700 TRACE("filename %s, options %#x, device %p, alloc_hier %p, " 3701 "load_user_data %p, frame_hierarchy %p, anim_controller %p.\n", 3702 debugstr_a(filename), options, device, alloc_hier, 3703 load_user_data, frame_hierarchy, anim_controller); 3704 3705 if (!filename) 3706 return D3DERR_INVALIDCALL; 3707 3708 len = MultiByteToWideChar(CP_ACP, 0, filename, -1, NULL, 0); 3709 filenameW = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR)); 3710 if (!filenameW) return E_OUTOFMEMORY; 3711 MultiByteToWideChar(CP_ACP, 0, filename, -1, filenameW, len); 3712 3713 hr = D3DXLoadMeshHierarchyFromXW(filenameW, options, device, 3714 alloc_hier, load_user_data, frame_hierarchy, anim_controller); 3715 HeapFree(GetProcessHeap(), 0, filenameW); 3716 3717 return hr; 3718 } 3719 3720 HRESULT WINAPI D3DXLoadMeshHierarchyFromXW(const WCHAR *filename, DWORD options, struct IDirect3DDevice9 *device, 3721 struct ID3DXAllocateHierarchy *alloc_hier, struct ID3DXLoadUserData *load_user_data, 3722 D3DXFRAME **frame_hierarchy, struct ID3DXAnimationController **anim_controller) 3723 { 3724 void *buffer; 3725 HRESULT hr; 3726 DWORD size; 3727 3728 TRACE("filename %s, options %#x, device %p, alloc_hier %p, " 3729 "load_user_data %p, frame_hierarchy %p, anim_controller %p.\n", 3730 debugstr_w(filename), options, device, alloc_hier, 3731 load_user_data, frame_hierarchy, anim_controller); 3732 3733 if (!filename) 3734 return D3DERR_INVALIDCALL; 3735 3736 hr = map_view_of_file(filename, &buffer, &size); 3737 if (FAILED(hr)) 3738 return D3DXERR_INVALIDDATA; 3739 3740 hr = D3DXLoadMeshHierarchyFromXInMemory(buffer, size, options, device, 3741 alloc_hier, load_user_data, frame_hierarchy, anim_controller); 3742 3743 UnmapViewOfFile(buffer); 3744 3745 return hr; 3746 } 3747 3748 static HRESULT filedata_get_name(ID3DXFileData *filedata, char **name) 3749 { 3750 HRESULT hr; 3751 SIZE_T name_len; 3752 3753 hr = filedata->lpVtbl->GetName(filedata, NULL, &name_len); 3754 if (FAILED(hr)) return hr; 3755 3756 if (!name_len) 3757 name_len++; 3758 *name = HeapAlloc(GetProcessHeap(), 0, name_len); 3759 if (!*name) return E_OUTOFMEMORY; 3760 3761 hr = filedata->lpVtbl->GetName(filedata, *name, &name_len); 3762 if (FAILED(hr)) 3763 HeapFree(GetProcessHeap(), 0, *name); 3764 else if (!name_len) 3765 (*name)[0] = 0; 3766 3767 return hr; 3768 } 3769 3770 static HRESULT load_mesh_container(struct ID3DXFileData *filedata, DWORD options, struct IDirect3DDevice9 *device, 3771 struct ID3DXAllocateHierarchy *alloc_hier, D3DXMESHCONTAINER **mesh_container) 3772 { 3773 HRESULT hr; 3774 ID3DXBuffer *adjacency = NULL; 3775 ID3DXBuffer *materials = NULL; 3776 ID3DXBuffer *effects = NULL; 3777 ID3DXSkinInfo *skin_info = NULL; 3778 D3DXMESHDATA mesh_data; 3779 DWORD num_materials = 0; 3780 char *name = NULL; 3781 3782 mesh_data.Type = D3DXMESHTYPE_MESH; 3783 mesh_data.u.pMesh = NULL; 3784 3785 hr = D3DXLoadSkinMeshFromXof(filedata, options, device, 3786 &adjacency, &materials, &effects, &num_materials, 3787 &skin_info, &mesh_data.u.pMesh); 3788 if (FAILED(hr)) return hr; 3789 3790 hr = filedata_get_name(filedata, &name); 3791 if (FAILED(hr)) goto cleanup; 3792 3793 hr = alloc_hier->lpVtbl->CreateMeshContainer(alloc_hier, name, &mesh_data, 3794 materials ? ID3DXBuffer_GetBufferPointer(materials) : NULL, 3795 effects ? ID3DXBuffer_GetBufferPointer(effects) : NULL, 3796 num_materials, 3797 adjacency ? ID3DXBuffer_GetBufferPointer(adjacency) : NULL, 3798 skin_info, mesh_container); 3799 3800 cleanup: 3801 if (materials) ID3DXBuffer_Release(materials); 3802 if (effects) ID3DXBuffer_Release(effects); 3803 if (adjacency) ID3DXBuffer_Release(adjacency); 3804 if (skin_info) IUnknown_Release(skin_info); 3805 if (mesh_data.u.pMesh) IUnknown_Release(mesh_data.u.pMesh); 3806 HeapFree(GetProcessHeap(), 0, name); 3807 return hr; 3808 } 3809 3810 static HRESULT parse_transform_matrix(ID3DXFileData *filedata, D3DXMATRIX *transform) 3811 { 3812 HRESULT hr; 3813 SIZE_T data_size; 3814 const BYTE *data; 3815 3816 /* template Matrix4x4 { 3817 * array FLOAT matrix[16]; 3818 * } 3819 * template FrameTransformMatrix { 3820 * Matrix4x4 frameMatrix; 3821 * } 3822 */ 3823 3824 hr = filedata->lpVtbl->Lock(filedata, &data_size, (const void**)&data); 3825 if (FAILED(hr)) return hr; 3826 3827 if (data_size != sizeof(D3DXMATRIX)) { 3828 WARN("incorrect data size (%ld bytes)\n", data_size); 3829 filedata->lpVtbl->Unlock(filedata); 3830 return E_FAIL; 3831 } 3832 3833 memcpy(transform, data, sizeof(D3DXMATRIX)); 3834 3835 filedata->lpVtbl->Unlock(filedata); 3836 return D3D_OK; 3837 } 3838 3839 static HRESULT load_frame(struct ID3DXFileData *filedata, DWORD options, struct IDirect3DDevice9 *device, 3840 struct ID3DXAllocateHierarchy *alloc_hier, D3DXFRAME **frame_out) 3841 { 3842 HRESULT hr; 3843 GUID type; 3844 ID3DXFileData *child; 3845 char *name = NULL; 3846 D3DXFRAME *frame = NULL; 3847 D3DXMESHCONTAINER **next_container; 3848 D3DXFRAME **next_child; 3849 SIZE_T i, nb_children; 3850 3851 hr = filedata_get_name(filedata, &name); 3852 if (FAILED(hr)) return hr; 3853 3854 hr = alloc_hier->lpVtbl->CreateFrame(alloc_hier, name, frame_out); 3855 HeapFree(GetProcessHeap(), 0, name); 3856 if (FAILED(hr)) return E_FAIL; 3857 3858 frame = *frame_out; 3859 D3DXMatrixIdentity(&frame->TransformationMatrix); 3860 next_child = &frame->pFrameFirstChild; 3861 next_container = &frame->pMeshContainer; 3862 3863 hr = filedata->lpVtbl->GetChildren(filedata, &nb_children); 3864 if (FAILED(hr)) 3865 return hr; 3866 3867 for (i = 0; i < nb_children; i++) 3868 { 3869 hr = filedata->lpVtbl->GetChild(filedata, i, &child); 3870 if (FAILED(hr)) 3871 return hr; 3872 hr = child->lpVtbl->GetType(child, &type); 3873 if (FAILED(hr)) 3874 goto err; 3875 3876 if (IsEqualGUID(&type, &TID_D3DRMMesh)) { 3877 hr = load_mesh_container(child, options, device, alloc_hier, next_container); 3878 if (SUCCEEDED(hr)) 3879 next_container = &(*next_container)->pNextMeshContainer; 3880 } else if (IsEqualGUID(&type, &TID_D3DRMFrameTransformMatrix)) { 3881 hr = parse_transform_matrix(child, &frame->TransformationMatrix); 3882 } else if (IsEqualGUID(&type, &TID_D3DRMFrame)) { 3883 hr = load_frame(child, options, device, alloc_hier, next_child); 3884 if (SUCCEEDED(hr)) 3885 next_child = &(*next_child)->pFrameSibling; 3886 } 3887 if (FAILED(hr)) 3888 goto err; 3889 3890 IUnknown_Release(child); 3891 } 3892 return D3D_OK; 3893 3894 err: 3895 IUnknown_Release(child); 3896 return hr; 3897 } 3898 3899 HRESULT WINAPI D3DXLoadMeshHierarchyFromXInMemory(const void *memory, DWORD memory_size, DWORD options, 3900 struct IDirect3DDevice9 *device, struct ID3DXAllocateHierarchy *alloc_hier, 3901 struct ID3DXLoadUserData *load_user_data, D3DXFRAME **frame_hierarchy, 3902 struct ID3DXAnimationController **anim_controller) 3903 { 3904 HRESULT hr; 3905 ID3DXFile *d3dxfile = NULL; 3906 ID3DXFileEnumObject *enumobj = NULL; 3907 ID3DXFileData *filedata = NULL; 3908 D3DXF_FILELOADMEMORY source; 3909 D3DXFRAME *first_frame = NULL; 3910 D3DXFRAME **next_frame = &first_frame; 3911 SIZE_T i, nb_children; 3912 GUID guid; 3913 3914 TRACE("(%p, %u, %x, %p, %p, %p, %p, %p)\n", memory, memory_size, options, 3915 device, alloc_hier, load_user_data, frame_hierarchy, anim_controller); 3916 3917 if (!memory || !memory_size || !device || !frame_hierarchy || !alloc_hier) 3918 return D3DERR_INVALIDCALL; 3919 if (load_user_data) 3920 { 3921 FIXME("Loading user data not implemented.\n"); 3922 return E_NOTIMPL; 3923 } 3924 3925 hr = D3DXFileCreate(&d3dxfile); 3926 if (FAILED(hr)) goto cleanup; 3927 3928 hr = d3dxfile->lpVtbl->RegisterTemplates(d3dxfile, D3DRM_XTEMPLATES, D3DRM_XTEMPLATE_BYTES); 3929 if (FAILED(hr)) goto cleanup; 3930 3931 source.lpMemory = (void*)memory; 3932 source.dSize = memory_size; 3933 hr = d3dxfile->lpVtbl->CreateEnumObject(d3dxfile, &source, D3DXF_FILELOAD_FROMMEMORY, &enumobj); 3934 if (FAILED(hr)) goto cleanup; 3935 3936 hr = enumobj->lpVtbl->GetChildren(enumobj, &nb_children); 3937 if (FAILED(hr)) 3938 goto cleanup; 3939 3940 for (i = 0; i < nb_children; i++) 3941 { 3942 hr = enumobj->lpVtbl->GetChild(enumobj, i, &filedata); 3943 if (FAILED(hr)) 3944 goto cleanup; 3945 3946 hr = filedata->lpVtbl->GetType(filedata, &guid); 3947 if (SUCCEEDED(hr)) { 3948 if (IsEqualGUID(&guid, &TID_D3DRMMesh)) { 3949 hr = alloc_hier->lpVtbl->CreateFrame(alloc_hier, NULL, next_frame); 3950 if (FAILED(hr)) { 3951 hr = E_FAIL; 3952 goto cleanup; 3953 } 3954 3955 D3DXMatrixIdentity(&(*next_frame)->TransformationMatrix); 3956 3957 hr = load_mesh_container(filedata, options, device, alloc_hier, &(*next_frame)->pMeshContainer); 3958 if (FAILED(hr)) goto cleanup; 3959 } else if (IsEqualGUID(&guid, &TID_D3DRMFrame)) { 3960 hr = load_frame(filedata, options, device, alloc_hier, next_frame); 3961 if (FAILED(hr)) goto cleanup; 3962 } 3963 while (*next_frame) 3964 next_frame = &(*next_frame)->pFrameSibling; 3965 } 3966 3967 filedata->lpVtbl->Release(filedata); 3968 filedata = NULL; 3969 if (FAILED(hr)) 3970 goto cleanup; 3971 } 3972 3973 if (!first_frame) { 3974 hr = E_FAIL; 3975 } else if (first_frame->pFrameSibling) { 3976 D3DXFRAME *root_frame = NULL; 3977 hr = alloc_hier->lpVtbl->CreateFrame(alloc_hier, NULL, &root_frame); 3978 if (FAILED(hr)) { 3979 hr = E_FAIL; 3980 goto cleanup; 3981 } 3982 D3DXMatrixIdentity(&root_frame->TransformationMatrix); 3983 root_frame->pFrameFirstChild = first_frame; 3984 *frame_hierarchy = root_frame; 3985 hr = D3D_OK; 3986 } else { 3987 *frame_hierarchy = first_frame; 3988 hr = D3D_OK; 3989 } 3990 3991 if (anim_controller) 3992 { 3993 *anim_controller = NULL; 3994 FIXME("Animation controller creation not implemented.\n"); 3995 } 3996 3997 cleanup: 3998 if (FAILED(hr) && first_frame) D3DXFrameDestroy(first_frame, alloc_hier); 3999 if (filedata) filedata->lpVtbl->Release(filedata); 4000 if (enumobj) enumobj->lpVtbl->Release(enumobj); 4001 if (d3dxfile) d3dxfile->lpVtbl->Release(d3dxfile); 4002 return hr; 4003 } 4004 4005 HRESULT WINAPI D3DXCleanMesh(D3DXCLEANTYPE clean_type, ID3DXMesh *mesh_in, const DWORD *adjacency_in, 4006 ID3DXMesh **mesh_out, DWORD *adjacency_out, ID3DXBuffer **errors_and_warnings) 4007 { 4008 FIXME("(%u, %p, %p, %p, %p, %p)\n", clean_type, mesh_in, adjacency_in, mesh_out, adjacency_out, errors_and_warnings); 4009 4010 return E_NOTIMPL; 4011 } 4012 4013 HRESULT WINAPI D3DXFrameDestroy(D3DXFRAME *frame, ID3DXAllocateHierarchy *alloc_hier) 4014 { 4015 HRESULT hr; 4016 BOOL last = FALSE; 4017 4018 TRACE("(%p, %p)\n", frame, alloc_hier); 4019 4020 if (!frame || !alloc_hier) 4021 return D3DERR_INVALIDCALL; 4022 4023 while (!last) { 4024 D3DXMESHCONTAINER *container; 4025 D3DXFRAME *current_frame; 4026 4027 if (frame->pFrameSibling) { 4028 current_frame = frame->pFrameSibling; 4029 frame->pFrameSibling = current_frame->pFrameSibling; 4030 current_frame->pFrameSibling = NULL; 4031 } else { 4032 current_frame = frame; 4033 last = TRUE; 4034 } 4035 4036 if (current_frame->pFrameFirstChild) { 4037 hr = D3DXFrameDestroy(current_frame->pFrameFirstChild, alloc_hier); 4038 if (FAILED(hr)) return hr; 4039 current_frame->pFrameFirstChild = NULL; 4040 } 4041 4042 container = current_frame->pMeshContainer; 4043 while (container) { 4044 D3DXMESHCONTAINER *next_container = container->pNextMeshContainer; 4045 hr = alloc_hier->lpVtbl->DestroyMeshContainer(alloc_hier, container); 4046 if (FAILED(hr)) return hr; 4047 container = next_container; 4048 } 4049 hr = alloc_hier->lpVtbl->DestroyFrame(alloc_hier, current_frame); 4050 if (FAILED(hr)) return hr; 4051 } 4052 return D3D_OK; 4053 } 4054 4055 HRESULT WINAPI D3DXLoadMeshFromXA(const char *filename, DWORD options, struct IDirect3DDevice9 *device, 4056 struct ID3DXBuffer **adjacency, struct ID3DXBuffer **materials, struct ID3DXBuffer **effect_instances, 4057 DWORD *num_materials, struct ID3DXMesh **mesh) 4058 { 4059 WCHAR *filenameW; 4060 HRESULT hr; 4061 int len; 4062 4063 TRACE("filename %s, options %#x, device %p, adjacency %p, materials %p, " 4064 "effect_instances %p, num_materials %p, mesh %p.\n", 4065 debugstr_a(filename), options, device, adjacency, materials, 4066 effect_instances, num_materials, mesh); 4067 4068 if (!filename) 4069 return D3DERR_INVALIDCALL; 4070 4071 len = MultiByteToWideChar(CP_ACP, 0, filename, -1, NULL, 0); 4072 filenameW = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR)); 4073 if (!filenameW) return E_OUTOFMEMORY; 4074 MultiByteToWideChar(CP_ACP, 0, filename, -1, filenameW, len); 4075 4076 hr = D3DXLoadMeshFromXW(filenameW, options, device, adjacency, materials, 4077 effect_instances, num_materials, mesh); 4078 HeapFree(GetProcessHeap(), 0, filenameW); 4079 4080 return hr; 4081 } 4082 4083 HRESULT WINAPI D3DXLoadMeshFromXW(const WCHAR *filename, DWORD options, struct IDirect3DDevice9 *device, 4084 struct ID3DXBuffer **adjacency, struct ID3DXBuffer **materials, struct ID3DXBuffer **effect_instances, 4085 DWORD *num_materials, struct ID3DXMesh **mesh) 4086 { 4087 void *buffer; 4088 HRESULT hr; 4089 DWORD size; 4090 4091 TRACE("filename %s, options %#x, device %p, adjacency %p, materials %p, " 4092 "effect_instances %p, num_materials %p, mesh %p.\n", 4093 debugstr_w(filename), options, device, adjacency, materials, 4094 effect_instances, num_materials, mesh); 4095 4096 if (!filename) 4097 return D3DERR_INVALIDCALL; 4098 4099 hr = map_view_of_file(filename, &buffer, &size); 4100 if (FAILED(hr)) 4101 return D3DXERR_INVALIDDATA; 4102 4103 hr = D3DXLoadMeshFromXInMemory(buffer, size, options, device, adjacency, 4104 materials, effect_instances, num_materials, mesh); 4105 4106 UnmapViewOfFile(buffer); 4107 4108 return hr; 4109 } 4110 4111 HRESULT WINAPI D3DXLoadMeshFromXResource(HMODULE module, const char *name, const char *type, DWORD options, 4112 struct IDirect3DDevice9 *device, struct ID3DXBuffer **adjacency, struct ID3DXBuffer **materials, 4113 struct ID3DXBuffer **effect_instances, DWORD *num_materials, struct ID3DXMesh **mesh) 4114 { 4115 HRESULT hr; 4116 HRSRC resinfo; 4117 void *buffer; 4118 DWORD size; 4119 4120 TRACE("module %p, name %s, type %s, options %#x, device %p, adjacency %p, " 4121 "materials %p, effect_instances %p, num_materials %p, mesh %p.\n", 4122 module, debugstr_a(name), debugstr_a(type), options, device, adjacency, 4123 materials, effect_instances, num_materials, mesh); 4124 4125 resinfo = FindResourceA(module, name, type); 4126 if (!resinfo) return D3DXERR_INVALIDDATA; 4127 4128 hr = load_resource_into_memory(module, resinfo, &buffer, &size); 4129 if (FAILED(hr)) return D3DXERR_INVALIDDATA; 4130 4131 return D3DXLoadMeshFromXInMemory(buffer, size, options, device, adjacency, 4132 materials, effect_instances, num_materials, mesh); 4133 } 4134 4135 struct mesh_container 4136 { 4137 struct list entry; 4138 ID3DXMesh *mesh; 4139 ID3DXBuffer *adjacency; 4140 ID3DXBuffer *materials; 4141 ID3DXBuffer *effects; 4142 DWORD num_materials; 4143 D3DXMATRIX transform; 4144 }; 4145 4146 static HRESULT parse_frame(struct ID3DXFileData *filedata, DWORD options, struct IDirect3DDevice9 *device, 4147 const D3DXMATRIX *parent_transform, struct list *container_list, DWORD provide_flags) 4148 { 4149 HRESULT hr; 4150 D3DXMATRIX transform = *parent_transform; 4151 ID3DXFileData *child; 4152 GUID type; 4153 SIZE_T i, nb_children; 4154 4155 hr = filedata->lpVtbl->GetChildren(filedata, &nb_children); 4156 if (FAILED(hr)) 4157 return hr; 4158 4159 for (i = 0; i < nb_children; i++) 4160 { 4161 hr = filedata->lpVtbl->GetChild(filedata, i, &child); 4162 if (FAILED(hr)) 4163 return hr; 4164 hr = child->lpVtbl->GetType(child, &type); 4165 if (FAILED(hr)) 4166 goto err; 4167 4168 if (IsEqualGUID(&type, &TID_D3DRMMesh)) { 4169 struct mesh_container *container = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(*container)); 4170 if (!container) 4171 { 4172 hr = E_OUTOFMEMORY; 4173 goto err; 4174 } 4175 list_add_tail(container_list, &container->entry); 4176 container->transform = transform; 4177 4178 hr = D3DXLoadSkinMeshFromXof(child, options, device, 4179 (provide_flags & PROVIDE_ADJACENCY) ? &container->adjacency : NULL, 4180 (provide_flags & PROVIDE_MATERIALS) ? &container->materials : NULL, 4181 NULL, &container->num_materials, NULL, &container->mesh); 4182 } else if (IsEqualGUID(&type, &TID_D3DRMFrameTransformMatrix)) { 4183 D3DXMATRIX new_transform; 4184 hr = parse_transform_matrix(child, &new_transform); 4185 D3DXMatrixMultiply(&transform, &transform, &new_transform); 4186 } else if (IsEqualGUID(&type, &TID_D3DRMFrame)) { 4187 hr = parse_frame(child, options, device, &transform, container_list, provide_flags); 4188 } 4189 if (FAILED(hr)) 4190 goto err; 4191 4192 IUnknown_Release(child); 4193 } 4194 return D3D_OK; 4195 4196 err: 4197 IUnknown_Release(child); 4198 return hr; 4199 } 4200 4201 HRESULT WINAPI D3DXLoadMeshFromXInMemory(const void *memory, DWORD memory_size, DWORD options, 4202 struct IDirect3DDevice9 *device, struct ID3DXBuffer **adjacency_out, struct ID3DXBuffer **materials_out, 4203 struct ID3DXBuffer **effects_out, DWORD *num_materials_out, struct ID3DXMesh **mesh_out) 4204 { 4205 HRESULT hr; 4206 ID3DXFile *d3dxfile = NULL; 4207 ID3DXFileEnumObject *enumobj = NULL; 4208 ID3DXFileData *filedata = NULL; 4209 D3DXF_FILELOADMEMORY source; 4210 ID3DXBuffer *materials = NULL; 4211 ID3DXBuffer *effects = NULL; 4212 ID3DXBuffer *adjacency = NULL; 4213 struct list container_list = LIST_INIT(container_list); 4214 struct mesh_container *container_ptr, *next_container_ptr; 4215 DWORD num_materials; 4216 DWORD num_faces, num_vertices; 4217 D3DXMATRIX identity; 4218 DWORD provide_flags = 0; 4219 DWORD fvf; 4220 ID3DXMesh *concat_mesh = NULL; 4221 D3DVERTEXELEMENT9 concat_decl[MAX_FVF_DECL_SIZE]; 4222 BYTE *concat_vertices = NULL; 4223 void *concat_indices = NULL; 4224 DWORD index_offset; 4225 DWORD concat_vertex_size; 4226 SIZE_T i, nb_children; 4227 GUID guid; 4228 4229 TRACE("(%p, %u, %x, %p, %p, %p, %p, %p, %p)\n", memory, memory_size, options, 4230 device, adjacency_out, materials_out, effects_out, num_materials_out, mesh_out); 4231 4232 if (!memory || !memory_size || !device || !mesh_out) 4233 return D3DERR_INVALIDCALL; 4234 4235 hr = D3DXFileCreate(&d3dxfile); 4236 if (FAILED(hr)) goto cleanup; 4237 4238 hr = d3dxfile->lpVtbl->RegisterTemplates(d3dxfile, D3DRM_XTEMPLATES, D3DRM_XTEMPLATE_BYTES); 4239 if (FAILED(hr)) goto cleanup; 4240 4241 source.lpMemory = (void*)memory; 4242 source.dSize = memory_size; 4243 hr = d3dxfile->lpVtbl->CreateEnumObject(d3dxfile, &source, D3DXF_FILELOAD_FROMMEMORY, &enumobj); 4244 if (FAILED(hr)) goto cleanup; 4245 4246 D3DXMatrixIdentity(&identity); 4247 if (adjacency_out) provide_flags |= PROVIDE_ADJACENCY; 4248 if (materials_out || effects_out) provide_flags |= PROVIDE_MATERIALS; 4249 4250 hr = enumobj->lpVtbl->GetChildren(enumobj, &nb_children); 4251 if (FAILED(hr)) 4252 goto cleanup; 4253 4254 for (i = 0; i < nb_children; i++) 4255 { 4256 hr = enumobj->lpVtbl->GetChild(enumobj, i, &filedata); 4257 if (FAILED(hr)) 4258 goto cleanup; 4259 4260 hr = filedata->lpVtbl->GetType(filedata, &guid); 4261 if (SUCCEEDED(hr)) { 4262 if (IsEqualGUID(&guid, &TID_D3DRMMesh)) { 4263 container_ptr = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(*container_ptr)); 4264 if (!container_ptr) { 4265 hr = E_OUTOFMEMORY; 4266 goto cleanup; 4267 } 4268 list_add_tail(&container_list, &container_ptr->entry); 4269 D3DXMatrixIdentity(&container_ptr->transform); 4270 4271 hr = D3DXLoadSkinMeshFromXof(filedata, options, device, 4272 (provide_flags & PROVIDE_ADJACENCY) ? &container_ptr->adjacency : NULL, 4273 (provide_flags & PROVIDE_MATERIALS) ? &container_ptr->materials : NULL, 4274 NULL, &container_ptr->num_materials, NULL, &container_ptr->mesh); 4275 } else if (IsEqualGUID(&guid, &TID_D3DRMFrame)) { 4276 hr = parse_frame(filedata, options, device, &identity, &container_list, provide_flags); 4277 } 4278 if (FAILED(hr)) goto cleanup; 4279 } 4280 filedata->lpVtbl->Release(filedata); 4281 filedata = NULL; 4282 if (FAILED(hr)) 4283 goto cleanup; 4284 } 4285 4286 enumobj->lpVtbl->Release(enumobj); 4287 enumobj = NULL; 4288 d3dxfile->lpVtbl->Release(d3dxfile); 4289 d3dxfile = NULL; 4290 4291 if (list_empty(&container_list)) { 4292 hr = E_FAIL; 4293 goto cleanup; 4294 } 4295 4296 fvf = D3DFVF_XYZ; 4297 num_faces = 0; 4298 num_vertices = 0; 4299 num_materials = 0; 4300 LIST_FOR_EACH_ENTRY(container_ptr, &container_list, struct mesh_container, entry) 4301 { 4302 ID3DXMesh *mesh = container_ptr->mesh; 4303 fvf |= mesh->lpVtbl->GetFVF(mesh); 4304 num_faces += mesh->lpVtbl->GetNumFaces(mesh); 4305 num_vertices += mesh->lpVtbl->GetNumVertices(mesh); 4306 num_materials += container_ptr->num_materials; 4307 } 4308 4309 hr = D3DXCreateMeshFVF(num_faces, num_vertices, options, fvf, device, &concat_mesh); 4310 if (FAILED(hr)) goto cleanup; 4311 4312 hr = concat_mesh->lpVtbl->GetDeclaration(concat_mesh, concat_decl); 4313 if (FAILED(hr)) goto cleanup; 4314 4315 concat_vertex_size = D3DXGetDeclVertexSize(concat_decl, 0); 4316 4317 hr = concat_mesh->lpVtbl->LockVertexBuffer(concat_mesh, 0, (void**)&concat_vertices); 4318 if (FAILED(hr)) goto cleanup; 4319 4320 LIST_FOR_EACH_ENTRY(container_ptr, &container_list, struct mesh_container, entry) 4321 { 4322 D3DVERTEXELEMENT9 mesh_decl[MAX_FVF_DECL_SIZE]; 4323 ID3DXMesh *mesh = container_ptr->mesh; 4324 DWORD num_mesh_vertices = mesh->lpVtbl->GetNumVertices(mesh); 4325 DWORD mesh_vertex_size; 4326 const BYTE *mesh_vertices; 4327 DWORD i; 4328 4329 hr = mesh->lpVtbl->GetDeclaration(mesh, mesh_decl); 4330 if (FAILED(hr)) goto cleanup; 4331 4332 mesh_vertex_size = D3DXGetDeclVertexSize(mesh_decl, 0); 4333 4334 hr = mesh->lpVtbl->LockVertexBuffer(mesh, D3DLOCK_READONLY, (void**)&mesh_vertices); 4335 if (FAILED(hr)) goto cleanup; 4336 4337 for (i = 0; i < num_mesh_vertices; i++) { 4338 int j; 4339 int k = 1; 4340 4341 D3DXVec3TransformCoord((D3DXVECTOR3*)concat_vertices, 4342 (D3DXVECTOR3*)mesh_vertices, 4343 &container_ptr->transform); 4344 for (j = 1; concat_decl[j].Stream != 0xff; j++) 4345 { 4346 if (concat_decl[j].Usage == mesh_decl[k].Usage && 4347 concat_decl[j].UsageIndex == mesh_decl[k].UsageIndex) 4348 { 4349 if (concat_decl[j].Usage == D3DDECLUSAGE_NORMAL) { 4350 D3DXVec3TransformCoord((D3DXVECTOR3*)(concat_vertices + concat_decl[j].Offset), 4351 (D3DXVECTOR3*)(mesh_vertices + mesh_decl[k].Offset), 4352 &container_ptr->transform); 4353 } else { 4354 memcpy(concat_vertices + concat_decl[j].Offset, 4355 mesh_vertices + mesh_decl[k].Offset, 4356 d3dx_decltype_size[mesh_decl[k].Type]); 4357 } 4358 k++; 4359 } 4360 } 4361 mesh_vertices += mesh_vertex_size; 4362 concat_vertices += concat_vertex_size; 4363 } 4364 4365 mesh->lpVtbl->UnlockVertexBuffer(mesh); 4366 } 4367 4368 concat_mesh->lpVtbl->UnlockVertexBuffer(concat_mesh); 4369 concat_vertices = NULL; 4370 4371 hr = concat_mesh->lpVtbl->LockIndexBuffer(concat_mesh, 0, &concat_indices); 4372 if (FAILED(hr)) goto cleanup; 4373 4374 index_offset = 0; 4375 LIST_FOR_EACH_ENTRY(container_ptr, &container_list, struct mesh_container, entry) 4376 { 4377 ID3DXMesh *mesh = container_ptr->mesh; 4378 const void *mesh_indices; 4379 DWORD num_mesh_faces = mesh->lpVtbl->GetNumFaces(mesh); 4380 DWORD i; 4381 4382 hr = mesh->lpVtbl->LockIndexBuffer(mesh, D3DLOCK_READONLY, (void**)&mesh_indices); 4383 if (FAILED(hr)) goto cleanup; 4384 4385 if (options & D3DXMESH_32BIT) { 4386 DWORD *dest = concat_indices; 4387 const DWORD *src = mesh_indices; 4388 for (i = 0; i < num_mesh_faces * 3; i++) 4389 *dest++ = index_offset + *src++; 4390 concat_indices = dest; 4391 } else { 4392 WORD *dest = concat_indices; 4393 const WORD *src = mesh_indices; 4394 for (i = 0; i < num_mesh_faces * 3; i++) 4395 *dest++ = index_offset + *src++; 4396 concat_indices = dest; 4397 } 4398 mesh->lpVtbl->UnlockIndexBuffer(mesh); 4399 4400 index_offset += num_mesh_faces * 3; 4401 } 4402 4403 concat_mesh->lpVtbl->UnlockIndexBuffer(concat_mesh); 4404 concat_indices = NULL; 4405 4406 if (num_materials) { 4407 DWORD *concat_attrib_buffer = NULL; 4408 DWORD offset = 0; 4409 4410 hr = concat_mesh->lpVtbl->LockAttributeBuffer(concat_mesh, 0, &concat_attrib_buffer); 4411 if (FAILED(hr)) goto cleanup; 4412 4413 LIST_FOR_EACH_ENTRY(container_ptr, &container_list, struct mesh_container, entry) 4414 { 4415 ID3DXMesh *mesh = container_ptr->mesh; 4416 const DWORD *mesh_attrib_buffer = NULL; 4417 DWORD count = mesh->lpVtbl->GetNumFaces(mesh); 4418 4419 hr = mesh->lpVtbl->LockAttributeBuffer(mesh, D3DLOCK_READONLY, (DWORD**)&mesh_attrib_buffer); 4420 if (FAILED(hr)) { 4421 concat_mesh->lpVtbl->UnlockAttributeBuffer(concat_mesh); 4422 goto cleanup; 4423 } 4424 4425 while (count--) 4426 *concat_attrib_buffer++ = offset + *mesh_attrib_buffer++; 4427 4428 mesh->lpVtbl->UnlockAttributeBuffer(mesh); 4429 offset += container_ptr->num_materials; 4430 } 4431 concat_mesh->lpVtbl->UnlockAttributeBuffer(concat_mesh); 4432 } 4433 4434 if (materials_out || effects_out) { 4435 D3DXMATERIAL *out_ptr; 4436 if (!num_materials) { 4437 /* create default material */ 4438 hr = D3DXCreateBuffer(sizeof(D3DXMATERIAL), &materials); 4439 if (FAILED(hr)) goto cleanup; 4440 4441 out_ptr = ID3DXBuffer_GetBufferPointer(materials); 4442 out_ptr->MatD3D.Diffuse.r = 0.5f; 4443 out_ptr->MatD3D.Diffuse.g = 0.5f; 4444 out_ptr->MatD3D.Diffuse.b = 0.5f; 4445 out_ptr->MatD3D.Specular.r = 0.5f; 4446 out_ptr->MatD3D.Specular.g = 0.5f; 4447 out_ptr->MatD3D.Specular.b = 0.5f; 4448 /* D3DXCreateBuffer initializes the rest to zero */ 4449 } else { 4450 DWORD buffer_size = num_materials * sizeof(D3DXMATERIAL); 4451 char *strings_out_ptr; 4452 4453 LIST_FOR_EACH_ENTRY(container_ptr, &container_list, struct mesh_container, entry) 4454 { 4455 if (container_ptr->materials) { 4456 DWORD i; 4457 const D3DXMATERIAL *in_ptr = ID3DXBuffer_GetBufferPointer(container_ptr->materials); 4458 for (i = 0; i < container_ptr->num_materials; i++) 4459 { 4460 if (in_ptr->pTextureFilename) 4461 buffer_size += strlen(in_ptr->pTextureFilename) + 1; 4462 in_ptr++; 4463 } 4464 } 4465 } 4466 4467 hr = D3DXCreateBuffer(buffer_size, &materials); 4468 if (FAILED(hr)) goto cleanup; 4469 out_ptr = ID3DXBuffer_GetBufferPointer(materials); 4470 strings_out_ptr = (char*)(out_ptr + num_materials); 4471 4472 LIST_FOR_EACH_ENTRY(container_ptr, &container_list, struct mesh_container, entry) 4473 { 4474 if (container_ptr->materials) { 4475 DWORD i; 4476 const D3DXMATERIAL *in_ptr = ID3DXBuffer_GetBufferPointer(container_ptr->materials); 4477 for (i = 0; i < container_ptr->num_materials; i++) 4478 { 4479 out_ptr->MatD3D = in_ptr->MatD3D; 4480 if (in_ptr->pTextureFilename) { 4481 out_ptr->pTextureFilename = strings_out_ptr; 4482 strcpy(out_ptr->pTextureFilename, in_ptr->pTextureFilename); 4483 strings_out_ptr += strlen(in_ptr->pTextureFilename) + 1; 4484 } 4485 in_ptr++; 4486 out_ptr++; 4487 } 4488 } 4489 } 4490 } 4491 } 4492 if (!num_materials) 4493 num_materials = 1; 4494 4495 if (effects_out) { 4496 generate_effects(materials, num_materials, &effects); 4497 if (!materials_out) { 4498 ID3DXBuffer_Release(materials); 4499 materials = NULL; 4500 } 4501 } 4502 4503 if (adjacency_out) { 4504 if (!list_next(&container_list, list_head(&container_list))) { 4505 container_ptr = LIST_ENTRY(list_head(&container_list), struct mesh_container, entry); 4506 adjacency = container_ptr->adjacency; 4507 container_ptr->adjacency = NULL; 4508 } else { 4509 DWORD offset = 0; 4510 DWORD *out_ptr; 4511 4512 hr = D3DXCreateBuffer(num_faces * 3 * sizeof(DWORD), &adjacency); 4513 if (FAILED(hr)) goto cleanup; 4514 4515 out_ptr = ID3DXBuffer_GetBufferPointer(adjacency); 4516 LIST_FOR_EACH_ENTRY(container_ptr, &container_list, struct mesh_container, entry) 4517 { 4518 DWORD i; 4519 DWORD count = 3 * container_ptr->mesh->lpVtbl->GetNumFaces(container_ptr->mesh); 4520 DWORD *in_ptr = ID3DXBuffer_GetBufferPointer(container_ptr->adjacency); 4521 4522 for (i = 0; i < count; i++) 4523 *out_ptr++ = offset + *in_ptr++; 4524 4525 offset += count; 4526 } 4527 } 4528 } 4529 4530 *mesh_out = concat_mesh; 4531 if (adjacency_out) *adjacency_out = adjacency; 4532 if (materials_out) *materials_out = materials; 4533 if (effects_out) *effects_out = effects; 4534 if (num_materials_out) *num_materials_out = num_materials; 4535 4536 hr = D3D_OK; 4537 cleanup: 4538 if (concat_indices) concat_mesh->lpVtbl->UnlockIndexBuffer(concat_mesh); 4539 if (concat_vertices) concat_mesh->lpVtbl->UnlockVertexBuffer(concat_mesh); 4540 if (filedata) filedata->lpVtbl->Release(filedata); 4541 if (enumobj) enumobj->lpVtbl->Release(enumobj); 4542 if (d3dxfile) d3dxfile->lpVtbl->Release(d3dxfile); 4543 if (FAILED(hr)) { 4544 if (concat_mesh) IUnknown_Release(concat_mesh); 4545 if (materials) ID3DXBuffer_Release(materials); 4546 if (effects) ID3DXBuffer_Release(effects); 4547 if (adjacency) ID3DXBuffer_Release(adjacency); 4548 } 4549 LIST_FOR_EACH_ENTRY_SAFE(container_ptr, next_container_ptr, &container_list, struct mesh_container, entry) 4550 { 4551 if (container_ptr->mesh) IUnknown_Release(container_ptr->mesh); 4552 if (container_ptr->adjacency) ID3DXBuffer_Release(container_ptr->adjacency); 4553 if (container_ptr->materials) ID3DXBuffer_Release(container_ptr->materials); 4554 if (container_ptr->effects) ID3DXBuffer_Release(container_ptr->effects); 4555 HeapFree(GetProcessHeap(), 0, container_ptr); 4556 } 4557 return hr; 4558 } 4559 4560 struct vertex 4561 { 4562 D3DXVECTOR3 position; 4563 D3DXVECTOR3 normal; 4564 }; 4565 4566 HRESULT WINAPI D3DXCreatePolygon(struct IDirect3DDevice9 *device, float length, UINT sides, 4567 struct ID3DXMesh **mesh, struct ID3DXBuffer **adjacency) 4568 { 4569 HRESULT hr; 4570 ID3DXMesh *polygon; 4571 struct vertex *vertices; 4572 WORD (*faces)[3]; 4573 DWORD (*adjacency_buf)[3]; 4574 float angle, scale; 4575 unsigned int i; 4576 4577 TRACE("device %p, length %f, sides %u, mesh %p, adjacency %p.\n", 4578 device, length, sides, mesh, adjacency); 4579 4580 if (!device || length < 0.0f || sides < 3 || !mesh) 4581 return D3DERR_INVALIDCALL; 4582 4583 if (FAILED(hr = D3DXCreateMeshFVF(sides, sides + 1, D3DXMESH_MANAGED, 4584 D3DFVF_XYZ | D3DFVF_NORMAL, device, &polygon))) 4585 { 4586 return hr; 4587 } 4588 4589 if (FAILED(hr = polygon->lpVtbl->LockVertexBuffer(polygon, 0, (void **)&vertices))) 4590 { 4591 polygon->lpVtbl->Release(polygon); 4592 return hr; 4593 } 4594 4595 if (FAILED(hr = polygon->lpVtbl->LockIndexBuffer(polygon, 0, (void **)&faces))) 4596 { 4597 polygon->lpVtbl->UnlockVertexBuffer(polygon); 4598 polygon->lpVtbl->Release(polygon); 4599 return hr; 4600 } 4601 4602 angle = D3DX_PI / sides; 4603 scale = 0.5f * length / sinf(angle); 4604 angle *= 2.0f; 4605 4606 vertices[0].position.x = 0.0f; 4607 vertices[0].position.y = 0.0f; 4608 vertices[0].position.z = 0.0f; 4609 vertices[0].normal.x = 0.0f; 4610 vertices[0].normal.y = 0.0f; 4611 vertices[0].normal.z = 1.0f; 4612 4613 for (i = 0; i < sides; ++i) 4614 { 4615 vertices[i + 1].position.x = cosf(angle * i) * scale; 4616 vertices[i + 1].position.y = sinf(angle * i) * scale; 4617 vertices[i + 1].position.z = 0.0f; 4618 vertices[i + 1].normal.x = 0.0f; 4619 vertices[i + 1].normal.y = 0.0f; 4620 vertices[i + 1].normal.z = 1.0f; 4621 4622 faces[i][0] = 0; 4623 faces[i][1] = i + 1; 4624 faces[i][2] = i + 2; 4625 } 4626 4627 faces[sides - 1][2] = 1; 4628 4629 polygon->lpVtbl->UnlockVertexBuffer(polygon); 4630 polygon->lpVtbl->UnlockIndexBuffer(polygon); 4631 4632 if (adjacency) 4633 { 4634 if (FAILED(hr = D3DXCreateBuffer(sides * sizeof(DWORD) * 3, adjacency))) 4635 { 4636 polygon->lpVtbl->Release(polygon); 4637 return hr; 4638 } 4639 4640 adjacency_buf = ID3DXBuffer_GetBufferPointer(*adjacency); 4641 for (i = 0; i < sides; ++i) 4642 { 4643 adjacency_buf[i][0] = i - 1; 4644 adjacency_buf[i][1] = ~0U; 4645 adjacency_buf[i][2] = i + 1; 4646 } 4647 adjacency_buf[0][0] = sides - 1; 4648 adjacency_buf[sides - 1][2] = 0; 4649 } 4650 4651 *mesh = polygon; 4652 4653 return D3D_OK; 4654 } 4655 4656 HRESULT WINAPI D3DXCreateBox(struct IDirect3DDevice9 *device, float width, float height, 4657 float depth, struct ID3DXMesh **mesh, struct ID3DXBuffer **adjacency) 4658 { 4659 HRESULT hr; 4660 ID3DXMesh *box; 4661 struct vertex *vertices; 4662 WORD (*faces)[3]; 4663 DWORD *adjacency_buf; 4664 unsigned int i, face; 4665 static const D3DXVECTOR3 unit_box[] = 4666 { 4667 {-0.5f, -0.5f, -0.5f}, {-0.5f, -0.5f, 0.5f}, {-0.5f, 0.5f, 0.5f}, {-0.5f, 0.5f, -0.5f}, 4668 {-0.5f, 0.5f, -0.5f}, {-0.5f, 0.5f, 0.5f}, { 0.5f, 0.5f, 0.5f}, { 0.5f, 0.5f, -0.5f}, 4669 { 0.5f, 0.5f, -0.5f}, { 0.5f, 0.5f, 0.5f}, { 0.5f, -0.5f, 0.5f}, { 0.5f, -0.5f, -0.5f}, 4670 {-0.5f, -0.5f, 0.5f}, {-0.5f, -0.5f, -0.5f}, { 0.5f, -0.5f, -0.5f}, { 0.5f, -0.5f, 0.5f}, 4671 {-0.5f, -0.5f, 0.5f}, { 0.5f, -0.5f, 0.5f}, { 0.5f, 0.5f, 0.5f}, {-0.5f, 0.5f, 0.5f}, 4672 {-0.5f, -0.5f, -0.5f}, {-0.5f, 0.5f, -0.5f}, { 0.5f, 0.5f, -0.5f}, { 0.5f, -0.5f, -0.5f} 4673 }; 4674 static const D3DXVECTOR3 normals[] = 4675 { 4676 {-1.0f, 0.0f, 0.0f}, { 0.0f, 1.0f, 0.0f}, { 1.0f, 0.0f, 0.0f}, 4677 { 0.0f, -1.0f, 0.0f}, { 0.0f, 0.0f, 1.0f}, { 0.0f, 0.0f, -1.0f} 4678 }; 4679 static const DWORD adjacency_table[] = 4680 { 4681 6, 9, 1, 2, 10, 0, 1, 9, 3, 4, 10, 2, 4682 3, 8, 5, 7, 11, 4, 0, 11, 7, 5, 8, 6, 4683 7, 4, 9, 2, 0, 8, 1, 3, 11, 5, 6, 10 4684 }; 4685 4686 TRACE("device %p, width %f, height %f, depth %f, mesh %p, adjacency %p\n", 4687 device, width, height, depth, mesh, adjacency); 4688 4689 if (!device || width < 0.0f || height < 0.0f || depth < 0.0f || !mesh) 4690 { 4691 return D3DERR_INVALIDCALL; 4692 } 4693 4694 if (FAILED(hr = D3DXCreateMeshFVF(12, 24, D3DXMESH_MANAGED, D3DFVF_XYZ | D3DFVF_NORMAL, device, &box))) 4695 { 4696 return hr; 4697 } 4698 4699 if (FAILED(hr = box->lpVtbl->LockVertexBuffer(box, 0, (void **)&vertices))) 4700 { 4701 box->lpVtbl->Release(box); 4702 return hr; 4703 } 4704 4705 if (FAILED(hr = box->lpVtbl->LockIndexBuffer(box, 0, (void **)&faces))) 4706 { 4707 box->lpVtbl->UnlockVertexBuffer(box); 4708 box->lpVtbl->Release(box); 4709 return hr; 4710 } 4711 4712 for (i = 0; i < 24; i++) 4713 { 4714 vertices[i].position.x = width * unit_box[i].x; 4715 vertices[i].position.y = height * unit_box[i].y; 4716 vertices[i].position.z = depth * unit_box[i].z; 4717 vertices[i].normal.x = normals[i / 4].x; 4718 vertices[i].normal.y = normals[i / 4].y; 4719 vertices[i].normal.z = normals[i / 4].z; 4720 } 4721 4722 face = 0; 4723 for (i = 0; i < 12; i++) 4724 { 4725 faces[i][0] = face++; 4726 faces[i][1] = face++; 4727 faces[i][2] = (i % 2) ? face - 4 : face; 4728 } 4729 4730 box->lpVtbl->UnlockIndexBuffer(box); 4731 box->lpVtbl->UnlockVertexBuffer(box); 4732 4733 if (adjacency) 4734 { 4735 if (FAILED(hr = D3DXCreateBuffer(sizeof(adjacency_table), adjacency))) 4736 { 4737 box->lpVtbl->Release(box); 4738 return hr; 4739 } 4740 4741 adjacency_buf = ID3DXBuffer_GetBufferPointer(*adjacency); 4742 memcpy(adjacency_buf, adjacency_table, sizeof(adjacency_table)); 4743 } 4744 4745 *mesh = box; 4746 4747 return D3D_OK; 4748 } 4749 4750 typedef WORD face[3]; 4751 4752 struct sincos_table 4753 { 4754 float *sin; 4755 float *cos; 4756 }; 4757 4758 static void free_sincos_table(struct sincos_table *sincos_table) 4759 { 4760 HeapFree(GetProcessHeap(), 0, sincos_table->cos); 4761 HeapFree(GetProcessHeap(), 0, sincos_table->sin); 4762 } 4763 4764 /* pre compute sine and cosine tables; caller must free */ 4765 static BOOL compute_sincos_table(struct sincos_table *sincos_table, float angle_start, float angle_step, int n) 4766 { 4767 float angle; 4768 int i; 4769 4770 sincos_table->sin = HeapAlloc(GetProcessHeap(), 0, n * sizeof(*sincos_table->sin)); 4771 if (!sincos_table->sin) 4772 { 4773 return FALSE; 4774 } 4775 sincos_table->cos = HeapAlloc(GetProcessHeap(), 0, n * sizeof(*sincos_table->cos)); 4776 if (!sincos_table->cos) 4777 { 4778 HeapFree(GetProcessHeap(), 0, sincos_table->sin); 4779 return FALSE; 4780 } 4781 4782 angle = angle_start; 4783 for (i = 0; i < n; i++) 4784 { 4785 sincos_table->sin[i] = sinf(angle); 4786 sincos_table->cos[i] = cosf(angle); 4787 angle += angle_step; 4788 } 4789 4790 return TRUE; 4791 } 4792 4793 static WORD vertex_index(UINT slices, int slice, int stack) 4794 { 4795 return stack*slices+slice+1; 4796 } 4797 4798 HRESULT WINAPI D3DXCreateSphere(struct IDirect3DDevice9 *device, float radius, UINT slices, 4799 UINT stacks, struct ID3DXMesh **mesh, struct ID3DXBuffer **adjacency) 4800 { 4801 DWORD number_of_vertices, number_of_faces; 4802 HRESULT hr; 4803 ID3DXMesh *sphere; 4804 struct vertex *vertices; 4805 face *faces; 4806 float phi_step, phi_start; 4807 struct sincos_table phi; 4808 float theta_step, theta, sin_theta, cos_theta; 4809 DWORD vertex, face, stack, slice; 4810 4811 TRACE("(%p, %f, %u, %u, %p, %p)\n", device, radius, slices, stacks, mesh, adjacency); 4812 4813 if (!device || radius < 0.0f || slices < 2 || stacks < 2 || !mesh) 4814 { 4815 return D3DERR_INVALIDCALL; 4816 } 4817 4818 number_of_vertices = 2 + slices * (stacks-1); 4819 number_of_faces = 2 * slices + (stacks - 2) * (2 * slices); 4820 4821 hr = D3DXCreateMeshFVF(number_of_faces, number_of_vertices, D3DXMESH_MANAGED, 4822 D3DFVF_XYZ | D3DFVF_NORMAL, device, &sphere); 4823 if (FAILED(hr)) 4824 { 4825 return hr; 4826 } 4827 4828 if (FAILED(hr = sphere->lpVtbl->LockVertexBuffer(sphere, 0, (void **)&vertices))) 4829 { 4830 sphere->lpVtbl->Release(sphere); 4831 return hr; 4832 } 4833 4834 if (FAILED(hr = sphere->lpVtbl->LockIndexBuffer(sphere, 0, (void **)&faces))) 4835 { 4836 sphere->lpVtbl->UnlockVertexBuffer(sphere); 4837 sphere->lpVtbl->Release(sphere); 4838 return hr; 4839 } 4840 4841 /* phi = angle on xz plane wrt z axis */ 4842 phi_step = -2.0f * D3DX_PI / slices; 4843 phi_start = D3DX_PI / 2.0f; 4844 4845 if (!compute_sincos_table(&phi, phi_start, phi_step, slices)) 4846 { 4847 sphere->lpVtbl->UnlockIndexBuffer(sphere); 4848 sphere->lpVtbl->UnlockVertexBuffer(sphere); 4849 sphere->lpVtbl->Release(sphere); 4850 return E_OUTOFMEMORY; 4851 } 4852 4853 /* theta = angle on xy plane wrt x axis */ 4854 theta_step = D3DX_PI / stacks; 4855 theta = theta_step; 4856 4857 vertex = 0; 4858 face = 0; 4859 4860 vertices[vertex].normal.x = 0.0f; 4861 vertices[vertex].normal.y = 0.0f; 4862 vertices[vertex].normal.z = 1.0f; 4863 vertices[vertex].position.x = 0.0f; 4864 vertices[vertex].position.y = 0.0f; 4865 vertices[vertex].position.z = radius; 4866 vertex++; 4867 4868 for (stack = 0; stack < stacks - 1; stack++) 4869 { 4870 sin_theta = sinf(theta); 4871 cos_theta = cosf(theta); 4872 4873 for (slice = 0; slice < slices; slice++) 4874 { 4875 vertices[vertex].normal.x = sin_theta * phi.cos[slice]; 4876 vertices[vertex].normal.y = sin_theta * phi.sin[slice]; 4877 vertices[vertex].normal.z = cos_theta; 4878 vertices[vertex].position.x = radius * sin_theta * phi.cos[slice]; 4879 vertices[vertex].position.y = radius * sin_theta * phi.sin[slice]; 4880 vertices[vertex].position.z = radius * cos_theta; 4881 vertex++; 4882 4883 if (slice > 0) 4884 { 4885 if (stack == 0) 4886 { 4887 /* top stack is triangle fan */ 4888 faces[face][0] = 0; 4889 faces[face][1] = slice + 1; 4890 faces[face][2] = slice; 4891 face++; 4892 } 4893 else 4894 { 4895 /* stacks in between top and bottom are quad strips */ 4896 faces[face][0] = vertex_index(slices, slice-1, stack-1); 4897 faces[face][1] = vertex_index(slices, slice, stack-1); 4898 faces[face][2] = vertex_index(slices, slice-1, stack); 4899 face++; 4900 4901 faces[face][0] = vertex_index(slices, slice, stack-1); 4902 faces[face][1] = vertex_index(slices, slice, stack); 4903 faces[face][2] = vertex_index(slices, slice-1, stack); 4904 face++; 4905 } 4906 } 4907 } 4908 4909 theta += theta_step; 4910 4911 if (stack == 0) 4912 { 4913 faces[face][0] = 0; 4914 faces[face][1] = 1; 4915 faces[face][2] = slice; 4916 face++; 4917 } 4918 else 4919 { 4920 faces[face][0] = vertex_index(slices, slice-1, stack-1); 4921 faces[face][1] = vertex_index(slices, 0, stack-1); 4922 faces[face][2] = vertex_index(slices, slice-1, stack); 4923 face++; 4924 4925 faces[face][0] = vertex_index(slices, 0, stack-1); 4926 faces[face][1] = vertex_index(slices, 0, stack); 4927 faces[face][2] = vertex_index(slices, slice-1, stack); 4928 face++; 4929 } 4930 } 4931 4932 vertices[vertex].position.x = 0.0f; 4933 vertices[vertex].position.y = 0.0f; 4934 vertices[vertex].position.z = -radius; 4935 vertices[vertex].normal.x = 0.0f; 4936 vertices[vertex].normal.y = 0.0f; 4937 vertices[vertex].normal.z = -1.0f; 4938 4939 /* bottom stack is triangle fan */ 4940 for (slice = 1; slice < slices; slice++) 4941 { 4942 faces[face][0] = vertex_index(slices, slice-1, stack-1); 4943 faces[face][1] = vertex_index(slices, slice, stack-1); 4944 faces[face][2] = vertex; 4945 face++; 4946 } 4947 4948 faces[face][0] = vertex_index(slices, slice-1, stack-1); 4949 faces[face][1] = vertex_index(slices, 0, stack-1); 4950 faces[face][2] = vertex; 4951 4952 free_sincos_table(&phi); 4953 sphere->lpVtbl->UnlockIndexBuffer(sphere); 4954 sphere->lpVtbl->UnlockVertexBuffer(sphere); 4955 4956 4957 if (adjacency) 4958 { 4959 if (FAILED(hr = D3DXCreateBuffer(number_of_faces * sizeof(DWORD) * 3, adjacency))) 4960 { 4961 sphere->lpVtbl->Release(sphere); 4962 return hr; 4963 } 4964 4965 if (FAILED(hr = sphere->lpVtbl->GenerateAdjacency(sphere, 0.0f, (*adjacency)->lpVtbl->GetBufferPointer(*adjacency)))) 4966 { 4967 (*adjacency)->lpVtbl->Release(*adjacency); 4968 sphere->lpVtbl->Release(sphere); 4969 return hr; 4970 } 4971 } 4972 4973 *mesh = sphere; 4974 4975 return D3D_OK; 4976 } 4977 4978 HRESULT WINAPI D3DXCreateCylinder(struct IDirect3DDevice9 *device, float radius1, float radius2, 4979 float length, UINT slices, UINT stacks, struct ID3DXMesh **mesh, struct ID3DXBuffer **adjacency) 4980 { 4981 DWORD number_of_vertices, number_of_faces; 4982 HRESULT hr; 4983 ID3DXMesh *cylinder; 4984 struct vertex *vertices; 4985 face *faces; 4986 float theta_step, theta_start; 4987 struct sincos_table theta; 4988 float delta_radius, radius, radius_step; 4989 float z, z_step, z_normal; 4990 DWORD vertex, face, slice, stack; 4991 4992 TRACE("(%p, %f, %f, %f, %u, %u, %p, %p)\n", device, radius1, radius2, length, slices, stacks, mesh, adjacency); 4993 4994 if (device == NULL || radius1 < 0.0f || radius2 < 0.0f || length < 0.0f || slices < 2 || stacks < 1 || mesh == NULL) 4995 { 4996 return D3DERR_INVALIDCALL; 4997 } 4998 4999 number_of_vertices = 2 + (slices * (3 + stacks)); 5000 number_of_faces = 2 * slices + stacks * (2 * slices); 5001 5002 hr = D3DXCreateMeshFVF(number_of_faces, number_of_vertices, D3DXMESH_MANAGED, 5003 D3DFVF_XYZ | D3DFVF_NORMAL, device, &cylinder); 5004 if (FAILED(hr)) 5005 { 5006 return hr; 5007 } 5008 5009 if (FAILED(hr = cylinder->lpVtbl->LockVertexBuffer(cylinder, 0, (void **)&vertices))) 5010 { 5011 cylinder->lpVtbl->Release(cylinder); 5012 return hr; 5013 } 5014 5015 if (FAILED(hr = cylinder->lpVtbl->LockIndexBuffer(cylinder, 0, (void **)&faces))) 5016 { 5017 cylinder->lpVtbl->UnlockVertexBuffer(cylinder); 5018 cylinder->lpVtbl->Release(cylinder); 5019 return hr; 5020 } 5021 5022 /* theta = angle on xy plane wrt x axis */ 5023 theta_step = -2.0f * D3DX_PI / slices; 5024 theta_start = D3DX_PI / 2.0f; 5025 5026 if (!compute_sincos_table(&theta, theta_start, theta_step, slices)) 5027 { 5028 cylinder->lpVtbl->UnlockIndexBuffer(cylinder); 5029 cylinder->lpVtbl->UnlockVertexBuffer(cylinder); 5030 cylinder->lpVtbl->Release(cylinder); 5031 return E_OUTOFMEMORY; 5032 } 5033 5034 vertex = 0; 5035 face = 0; 5036 5037 delta_radius = radius1 - radius2; 5038 radius = radius1; 5039 radius_step = delta_radius / stacks; 5040 5041 z = -length / 2; 5042 z_step = length / stacks; 5043 z_normal = delta_radius / length; 5044 if (isnan(z_normal)) 5045 { 5046 z_normal = 0.0f; 5047 } 5048 5049 vertices[vertex].normal.x = 0.0f; 5050 vertices[vertex].normal.y = 0.0f; 5051 vertices[vertex].normal.z = -1.0f; 5052 vertices[vertex].position.x = 0.0f; 5053 vertices[vertex].position.y = 0.0f; 5054 vertices[vertex++].position.z = z; 5055 5056 for (slice = 0; slice < slices; slice++, vertex++) 5057 { 5058 vertices[vertex].normal.x = 0.0f; 5059 vertices[vertex].normal.y = 0.0f; 5060 vertices[vertex].normal.z = -1.0f; 5061 vertices[vertex].position.x = radius * theta.cos[slice]; 5062 vertices[vertex].position.y = radius * theta.sin[slice]; 5063 vertices[vertex].position.z = z; 5064 5065 if (slice > 0) 5066 { 5067 faces[face][0] = 0; 5068 faces[face][1] = slice; 5069 faces[face++][2] = slice + 1; 5070 } 5071 } 5072 5073 faces[face][0] = 0; 5074 faces[face][1] = slice; 5075 faces[face++][2] = 1; 5076 5077 for (stack = 1; stack <= stacks+1; stack++) 5078 { 5079 for (slice = 0; slice < slices; slice++, vertex++) 5080 { 5081 vertices[vertex].normal.x = theta.cos[slice]; 5082 vertices[vertex].normal.y = theta.sin[slice]; 5083 vertices[vertex].normal.z = z_normal; 5084 D3DXVec3Normalize(&vertices[vertex].normal, &vertices[vertex].normal); 5085 vertices[vertex].position.x = radius * theta.cos[slice]; 5086 vertices[vertex].position.y = radius * theta.sin[slice]; 5087 vertices[vertex].position.z = z; 5088 5089 if (stack > 1 && slice > 0) 5090 { 5091 faces[face][0] = vertex_index(slices, slice-1, stack-1); 5092 faces[face][1] = vertex_index(slices, slice-1, stack); 5093 faces[face++][2] = vertex_index(slices, slice, stack-1); 5094 5095 faces[face][0] = vertex_index(slices, slice, stack-1); 5096 faces[face][1] = vertex_index(slices, slice-1, stack); 5097 faces[face++][2] = vertex_index(slices, slice, stack); 5098 } 5099 } 5100 5101 if (stack > 1) 5102 { 5103 faces[face][0] = vertex_index(slices, slice-1, stack-1); 5104 faces[face][1] = vertex_index(slices, slice-1, stack); 5105 faces[face++][2] = vertex_index(slices, 0, stack-1); 5106 5107 faces[face][0] = vertex_index(slices, 0, stack-1); 5108 faces[face][1] = vertex_index(slices, slice-1, stack); 5109 faces[face++][2] = vertex_index(slices, 0, stack); 5110 } 5111 5112 if (stack < stacks + 1) 5113 { 5114 z += z_step; 5115 radius -= radius_step; 5116 } 5117 } 5118 5119 for (slice = 0; slice < slices; slice++, vertex++) 5120 { 5121 vertices[vertex].normal.x = 0.0f; 5122 vertices[vertex].normal.y = 0.0f; 5123 vertices[vertex].normal.z = 1.0f; 5124 vertices[vertex].position.x = radius * theta.cos[slice]; 5125 vertices[vertex].position.y = radius * theta.sin[slice]; 5126 vertices[vertex].position.z = z; 5127 5128 if (slice > 0) 5129 { 5130 faces[face][0] = vertex_index(slices, slice-1, stack); 5131 faces[face][1] = number_of_vertices - 1; 5132 faces[face++][2] = vertex_index(slices, slice, stack); 5133 } 5134 } 5135 5136 vertices[vertex].position.x = 0.0f; 5137 vertices[vertex].position.y = 0.0f; 5138 vertices[vertex].position.z = z; 5139 vertices[vertex].normal.x = 0.0f; 5140 vertices[vertex].normal.y = 0.0f; 5141 vertices[vertex].normal.z = 1.0f; 5142 5143 faces[face][0] = vertex_index(slices, slice-1, stack); 5144 faces[face][1] = number_of_vertices - 1; 5145 faces[face][2] = vertex_index(slices, 0, stack); 5146 5147 free_sincos_table(&theta); 5148 cylinder->lpVtbl->UnlockIndexBuffer(cylinder); 5149 cylinder->lpVtbl->UnlockVertexBuffer(cylinder); 5150 5151 if (adjacency) 5152 { 5153 if (FAILED(hr = D3DXCreateBuffer(number_of_faces * sizeof(DWORD) * 3, adjacency))) 5154 { 5155 cylinder->lpVtbl->Release(cylinder); 5156 return hr; 5157 } 5158 5159 if (FAILED(hr = cylinder->lpVtbl->GenerateAdjacency(cylinder, 0.0f, (*adjacency)->lpVtbl->GetBufferPointer(*adjacency)))) 5160 { 5161 (*adjacency)->lpVtbl->Release(*adjacency); 5162 cylinder->lpVtbl->Release(cylinder); 5163 return hr; 5164 } 5165 } 5166 5167 *mesh = cylinder; 5168 5169 return D3D_OK; 5170 } 5171 5172 HRESULT WINAPI D3DXCreateTeapot(struct IDirect3DDevice9 *device, 5173 struct ID3DXMesh **mesh, struct ID3DXBuffer **adjacency) 5174 { 5175 FIXME("device %p, mesh %p, adjacency %p semi-stub.\n", device, mesh, adjacency); 5176 5177 return D3DXCreateSphere(device, 1.0f, 4, 4, mesh, adjacency); 5178 } 5179 5180 HRESULT WINAPI D3DXCreateTextA(struct IDirect3DDevice9 *device, HDC hdc, const char *text, float deviation, 5181 float extrusion, struct ID3DXMesh **mesh, struct ID3DXBuffer **adjacency, GLYPHMETRICSFLOAT *glyphmetrics) 5182 { 5183 WCHAR *textW; 5184 HRESULT hr; 5185 int len; 5186 5187 TRACE("device %p, hdc %p, text %s, deviation %.8e, extrusion %.8e, mesh %p, adjacency %p, glyphmetrics %p.\n", 5188 device, hdc, debugstr_a(text), deviation, extrusion, mesh, adjacency, glyphmetrics); 5189 5190 if (!text) 5191 return D3DERR_INVALIDCALL; 5192 5193 len = MultiByteToWideChar(CP_ACP, 0, text, -1, NULL, 0); 5194 textW = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR)); 5195 MultiByteToWideChar(CP_ACP, 0, text, -1, textW, len); 5196 5197 hr = D3DXCreateTextW(device, hdc, textW, deviation, extrusion, 5198 mesh, adjacency, glyphmetrics); 5199 HeapFree(GetProcessHeap(), 0, textW); 5200 5201 return hr; 5202 } 5203 5204 HRESULT WINAPI D3DXCreateTorus(struct IDirect3DDevice9 *device, 5205 float innerradius, float outerradius, UINT sides, UINT rings, struct ID3DXMesh **mesh, ID3DXBuffer **adjacency) 5206 { 5207 HRESULT hr; 5208 ID3DXMesh *torus; 5209 WORD (*faces)[3]; 5210 struct vertex *vertices; 5211 float phi, phi_step, sin_phi, cos_phi; 5212 float theta, theta_step, sin_theta, cos_theta; 5213 unsigned int i, j, numvert, numfaces; 5214 5215 TRACE("device %p, innerradius %.8e, outerradius %.8e, sides %u, rings %u, mesh %p, adjacency %p.\n", 5216 device, innerradius, outerradius, sides, rings, mesh, adjacency); 5217 5218 numvert = sides * rings; 5219 numfaces = numvert * 2; 5220 5221 if (!device || innerradius < 0.0f || outerradius < 0.0f || sides < 3 || rings < 3 || !mesh) 5222 { 5223 WARN("Invalid arguments.\n"); 5224 return D3DERR_INVALIDCALL; 5225 } 5226 5227 if (FAILED(hr = D3DXCreateMeshFVF(numfaces, numvert, D3DXMESH_MANAGED, D3DFVF_XYZ | D3DFVF_NORMAL, device, &torus))) 5228 return hr; 5229 5230 if (FAILED(hr = torus->lpVtbl->LockVertexBuffer(torus, 0, (void **)&vertices))) 5231 { 5232 torus->lpVtbl->Release(torus); 5233 return hr; 5234 } 5235 5236 if (FAILED(hr = torus->lpVtbl->LockIndexBuffer(torus, 0, (void **)&faces))) 5237 { 5238 torus->lpVtbl->UnlockVertexBuffer(torus); 5239 torus->lpVtbl->Release(torus); 5240 return hr; 5241 } 5242 5243 phi_step = D3DX_PI / sides * 2.0f; 5244 theta_step = D3DX_PI / rings * -2.0f; 5245 5246 theta = 0.0f; 5247 5248 for (i = 0; i < rings; ++i) 5249 { 5250 phi = 0.0f; 5251 5252 sin_theta = sinf(theta); 5253 cos_theta = cosf(theta); 5254 5255 for (j = 0; j < sides; ++j) 5256 { 5257 sin_phi = sinf(phi); 5258 cos_phi = cosf(phi); 5259 5260 vertices[i * sides + j].position.x = (innerradius * cos_phi + outerradius) * cos_theta; 5261 vertices[i * sides + j].position.y = (innerradius * cos_phi + outerradius) * sin_theta; 5262 vertices[i * sides + j].position.z = innerradius * sin_phi; 5263 vertices[i * sides + j].normal.x = cos_phi * cos_theta; 5264 vertices[i * sides + j].normal.y = cos_phi * sin_theta; 5265 vertices[i * sides + j].normal.z = sin_phi; 5266 5267 phi += phi_step; 5268 } 5269 5270 theta += theta_step; 5271 } 5272 5273 for (i = 0; i < numfaces - sides * 2; ++i) 5274 { 5275 faces[i][0] = i % 2 ? i / 2 + sides : i / 2; 5276 faces[i][1] = (i / 2 + 1) % sides ? i / 2 + 1 : i / 2 + 1 - sides; 5277 faces[i][2] = (i + 1) % (sides * 2) ? (i + 1) / 2 + sides : (i + 1) / 2; 5278 } 5279 5280 for (j = 0; i < numfaces; ++i, ++j) 5281 { 5282 faces[i][0] = i % 2 ? j / 2 : i / 2; 5283 faces[i][1] = (i / 2 + 1) % sides ? i / 2 + 1 : i / 2 + 1 - sides; 5284 faces[i][2] = i == numfaces - 1 ? 0 : (j + 1) / 2; 5285 } 5286 5287 torus->lpVtbl->UnlockIndexBuffer(torus); 5288 torus->lpVtbl->UnlockVertexBuffer(torus); 5289 5290 if (adjacency) 5291 { 5292 if (FAILED(hr = D3DXCreateBuffer(numfaces * sizeof(DWORD) * 3, adjacency))) 5293 { 5294 torus->lpVtbl->Release(torus); 5295 return hr; 5296 } 5297 5298 if (FAILED(hr = torus->lpVtbl->GenerateAdjacency(torus, 0.0f, (*adjacency)->lpVtbl->GetBufferPointer(*adjacency)))) 5299 { 5300 (*adjacency)->lpVtbl->Release(*adjacency); 5301 torus->lpVtbl->Release(torus); 5302 return hr; 5303 } 5304 } 5305 5306 *mesh = torus; 5307 5308 return D3D_OK; 5309 } 5310 5311 enum pointtype { 5312 POINTTYPE_CURVE = 0, 5313 POINTTYPE_CORNER, 5314 POINTTYPE_CURVE_START, 5315 POINTTYPE_CURVE_END, 5316 POINTTYPE_CURVE_MIDDLE, 5317 }; 5318 5319 struct point2d 5320 { 5321 D3DXVECTOR2 pos; 5322 enum pointtype corner; 5323 }; 5324 5325 struct dynamic_array 5326 { 5327 int count, capacity; 5328 void *items; 5329 }; 5330 5331 /* is a dynamic_array */ 5332 struct outline 5333 { 5334 int count, capacity; 5335 struct point2d *items; 5336 }; 5337 5338 /* is a dynamic_array */ 5339 struct outline_array 5340 { 5341 int count, capacity; 5342 struct outline *items; 5343 }; 5344 5345 struct face_array 5346 { 5347 int count; 5348 face *items; 5349 }; 5350 5351 struct point2d_index 5352 { 5353 struct outline *outline; 5354 int vertex; 5355 }; 5356 5357 struct point2d_index_array 5358 { 5359 int count; 5360 struct point2d_index *items; 5361 }; 5362 5363 struct glyphinfo 5364 { 5365 struct outline_array outlines; 5366 struct face_array faces; 5367 struct point2d_index_array ordered_vertices; 5368 float offset_x; 5369 }; 5370 5371 /* is an dynamic_array */ 5372 struct word_array 5373 { 5374 int count, capacity; 5375 WORD *items; 5376 }; 5377 5378 /* complex polygons are split into monotone polygons, which have 5379 * at most 2 intersections with the vertical sweep line */ 5380 struct triangulation 5381 { 5382 struct word_array vertex_stack; 5383 BOOL last_on_top, merging; 5384 }; 5385 5386 /* is an dynamic_array */ 5387 struct triangulation_array 5388 { 5389 int count, capacity; 5390 struct triangulation *items; 5391 5392 struct glyphinfo *glyph; 5393 }; 5394 5395 static BOOL reserve(struct dynamic_array *array, int count, int itemsize) 5396 { 5397 if (count > array->capacity) { 5398 void *new_buffer; 5399 int new_capacity; 5400 if (array->items && array->capacity) { 5401 new_capacity = max(array->capacity * 2, count); 5402 new_buffer = HeapReAlloc(GetProcessHeap(), 0, array->items, new_capacity * itemsize); 5403 } else { 5404 new_capacity = max(16, count); 5405 new_buffer = HeapAlloc(GetProcessHeap(), 0, new_capacity * itemsize); 5406 } 5407 if (!new_buffer) 5408 return FALSE; 5409 array->items = new_buffer; 5410 array->capacity = new_capacity; 5411 } 5412 return TRUE; 5413 } 5414 5415 static struct point2d *add_points(struct outline *array, int num) 5416 { 5417 struct point2d *item; 5418 5419 if (!reserve((struct dynamic_array *)array, array->count + num, sizeof(array->items[0]))) 5420 return NULL; 5421 5422 item = &array->items[array->count]; 5423 array->count += num; 5424 return item; 5425 } 5426 5427 static struct outline *add_outline(struct outline_array *array) 5428 { 5429 struct outline *item; 5430 5431 if (!reserve((struct dynamic_array *)array, array->count + 1, sizeof(array->items[0]))) 5432 return NULL; 5433 5434 item = &array->items[array->count++]; 5435 ZeroMemory(item, sizeof(*item)); 5436 return item; 5437 } 5438 5439 static inline face *add_face(struct face_array *array) 5440 { 5441 return &array->items[array->count++]; 5442 } 5443 5444 static struct triangulation *add_triangulation(struct triangulation_array *array) 5445 { 5446 struct triangulation *item; 5447 5448 if (!reserve((struct dynamic_array *)array, array->count + 1, sizeof(array->items[0]))) 5449 return NULL; 5450 5451 item = &array->items[array->count++]; 5452 ZeroMemory(item, sizeof(*item)); 5453 return item; 5454 } 5455 5456 static HRESULT add_vertex_index(struct word_array *array, WORD vertex_index) 5457 { 5458 if (!reserve((struct dynamic_array *)array, array->count + 1, sizeof(array->items[0]))) 5459 return E_OUTOFMEMORY; 5460 5461 array->items[array->count++] = vertex_index; 5462 return S_OK; 5463 } 5464 5465 /* assume fixed point numbers can be converted to float point in place */ 5466 C_ASSERT(sizeof(FIXED) == sizeof(float)); 5467 C_ASSERT(sizeof(POINTFX) == sizeof(D3DXVECTOR2)); 5468 5469 static inline D3DXVECTOR2 *convert_fixed_to_float(POINTFX *pt, int count, unsigned int emsquare) 5470 { 5471 D3DXVECTOR2 *ret = (D3DXVECTOR2*)pt; 5472 while (count--) { 5473 D3DXVECTOR2 *pt_flt = (D3DXVECTOR2*)pt; 5474 pt_flt->x = (pt->x.value + pt->x.fract / (float)0x10000) / emsquare; 5475 pt_flt->y = (pt->y.value + pt->y.fract / (float)0x10000) / emsquare; 5476 pt++; 5477 } 5478 return ret; 5479 } 5480 5481 static HRESULT add_bezier_points(struct outline *outline, const D3DXVECTOR2 *p1, 5482 const D3DXVECTOR2 *p2, const D3DXVECTOR2 *p3, 5483 float max_deviation_sq) 5484 { 5485 D3DXVECTOR2 split1 = {0, 0}, split2 = {0, 0}, middle, vec; 5486 float deviation_sq; 5487 5488 D3DXVec2Scale(&split1, D3DXVec2Add(&split1, p1, p2), 0.5f); 5489 D3DXVec2Scale(&split2, D3DXVec2Add(&split2, p2, p3), 0.5f); 5490 D3DXVec2Scale(&middle, D3DXVec2Add(&middle, &split1, &split2), 0.5f); 5491 5492 deviation_sq = D3DXVec2LengthSq(D3DXVec2Subtract(&vec, &middle, p2)); 5493 if (deviation_sq < max_deviation_sq) { 5494 struct point2d *pt = add_points(outline, 1); 5495 if (!pt) return E_OUTOFMEMORY; 5496 pt->pos = *p2; 5497 pt->corner = POINTTYPE_CURVE; 5498 /* the end point is omitted because the end line merges into the next segment of 5499 * the split bezier curve, and the end of the split bezier curve is added outside 5500 * this recursive function. */ 5501 } else { 5502 HRESULT hr = add_bezier_points(outline, p1, &split1, &middle, max_deviation_sq); 5503 if (hr != S_OK) return hr; 5504 hr = add_bezier_points(outline, &middle, &split2, p3, max_deviation_sq); 5505 if (hr != S_OK) return hr; 5506 } 5507 5508 return S_OK; 5509 } 5510 5511 static inline BOOL is_direction_similar(D3DXVECTOR2 *dir1, D3DXVECTOR2 *dir2, float cos_theta) 5512 { 5513 /* dot product = cos(theta) */ 5514 return D3DXVec2Dot(dir1, dir2) > cos_theta; 5515 } 5516 5517 static inline D3DXVECTOR2 *unit_vec2(D3DXVECTOR2 *dir, const D3DXVECTOR2 *pt1, const D3DXVECTOR2 *pt2) 5518 { 5519 return D3DXVec2Normalize(D3DXVec2Subtract(dir, pt2, pt1), dir); 5520 } 5521 5522 struct cos_table 5523 { 5524 float cos_half; 5525 float cos_45; 5526 float cos_90; 5527 }; 5528 5529 static BOOL attempt_line_merge(struct outline *outline, 5530 int pt_index, 5531 const D3DXVECTOR2 *nextpt, 5532 BOOL to_curve, 5533 const struct cos_table *table) 5534 { 5535 D3DXVECTOR2 curdir, lastdir; 5536 struct point2d *prevpt, *pt; 5537 BOOL ret = FALSE; 5538 5539 pt = &outline->items[pt_index]; 5540 pt_index = (pt_index - 1 + outline->count) % outline->count; 5541 prevpt = &outline->items[pt_index]; 5542 5543 if (to_curve) 5544 pt->corner = pt->corner != POINTTYPE_CORNER ? POINTTYPE_CURVE_MIDDLE : POINTTYPE_CURVE_START; 5545 5546 if (outline->count < 2) 5547 return FALSE; 5548 5549 /* remove last point if the next line continues the last line */ 5550 unit_vec2(&lastdir, &prevpt->pos, &pt->pos); 5551 unit_vec2(&curdir, &pt->pos, nextpt); 5552 if (is_direction_similar(&lastdir, &curdir, table->cos_half)) 5553 { 5554 outline->count--; 5555 if (pt->corner == POINTTYPE_CURVE_END) 5556 prevpt->corner = pt->corner; 5557 if (prevpt->corner == POINTTYPE_CURVE_END && to_curve) 5558 prevpt->corner = POINTTYPE_CURVE_MIDDLE; 5559 pt = prevpt; 5560 5561 ret = TRUE; 5562 if (outline->count < 2) 5563 return ret; 5564 5565 pt_index = (pt_index - 1 + outline->count) % outline->count; 5566 prevpt = &outline->items[pt_index]; 5567 unit_vec2(&lastdir, &prevpt->pos, &pt->pos); 5568 unit_vec2(&curdir, &pt->pos, nextpt); 5569 } 5570 return ret; 5571 } 5572 5573 static HRESULT create_outline(struct glyphinfo *glyph, void *raw_outline, int datasize, 5574 float max_deviation_sq, unsigned int emsquare, 5575 const struct cos_table *cos_table) 5576 { 5577 TTPOLYGONHEADER *header = (TTPOLYGONHEADER *)raw_outline; 5578 5579 while ((char *)header < (char *)raw_outline + datasize) 5580 { 5581 TTPOLYCURVE *curve = (TTPOLYCURVE *)(header + 1); 5582 struct point2d *lastpt, *pt; 5583 D3DXVECTOR2 lastdir; 5584 D3DXVECTOR2 *pt_flt; 5585 int j; 5586 struct outline *outline = add_outline(&glyph->outlines); 5587 5588 if (!outline) 5589 return E_OUTOFMEMORY; 5590 5591 pt = add_points(outline, 1); 5592 if (!pt) 5593 return E_OUTOFMEMORY; 5594 pt_flt = convert_fixed_to_float(&header->pfxStart, 1, emsquare); 5595 pt->pos = *pt_flt; 5596 pt->corner = POINTTYPE_CORNER; 5597 5598 if (header->dwType != TT_POLYGON_TYPE) 5599 FIXME("Unknown header type %d\n", header->dwType); 5600 5601 while ((char *)curve < (char *)header + header->cb) 5602 { 5603 D3DXVECTOR2 bezier_start = outline->items[outline->count - 1].pos; 5604 BOOL to_curve = curve->wType != TT_PRIM_LINE && curve->cpfx > 1; 5605 unsigned int j2 = 0; 5606 5607 if (!curve->cpfx) { 5608 curve = (TTPOLYCURVE *)&curve->apfx[curve->cpfx]; 5609 continue; 5610 } 5611 5612 pt_flt = convert_fixed_to_float(curve->apfx, curve->cpfx, emsquare); 5613 5614 attempt_line_merge(outline, outline->count - 1, &pt_flt[0], to_curve, cos_table); 5615 5616 if (to_curve) 5617 { 5618 HRESULT hr; 5619 int count = curve->cpfx; 5620 5621 while (count > 2) 5622 { 5623 D3DXVECTOR2 bezier_end; 5624 5625 D3DXVec2Scale(&bezier_end, D3DXVec2Add(&bezier_end, &pt_flt[j2], &pt_flt[j2+1]), 0.5f); 5626 hr = add_bezier_points(outline, &bezier_start, &pt_flt[j2], &bezier_end, max_deviation_sq); 5627 if (hr != S_OK) 5628 return hr; 5629 bezier_start = bezier_end; 5630 count--; 5631 j2++; 5632 } 5633 hr = add_bezier_points(outline, &bezier_start, &pt_flt[j2], &pt_flt[j2+1], max_deviation_sq); 5634 if (hr != S_OK) 5635 return hr; 5636 5637 pt = add_points(outline, 1); 5638 if (!pt) 5639 return E_OUTOFMEMORY; 5640 j2++; 5641 pt->pos = pt_flt[j2]; 5642 pt->corner = POINTTYPE_CURVE_END; 5643 } else { 5644 pt = add_points(outline, curve->cpfx); 5645 if (!pt) 5646 return E_OUTOFMEMORY; 5647 for (j2 = 0; j2 < curve->cpfx; j2++) 5648 { 5649 pt->pos = pt_flt[j2]; 5650 pt->corner = POINTTYPE_CORNER; 5651 pt++; 5652 } 5653 } 5654 5655 curve = (TTPOLYCURVE *)&curve->apfx[curve->cpfx]; 5656 } 5657 5658 /* remove last point if the next line continues the last line */ 5659 if (outline->count >= 3) { 5660 BOOL to_curve; 5661 5662 lastpt = &outline->items[outline->count - 1]; 5663 pt = &outline->items[0]; 5664 if (pt->pos.x == lastpt->pos.x && pt->pos.y == lastpt->pos.y) { 5665 if (lastpt->corner == POINTTYPE_CURVE_END) 5666 { 5667 if (pt->corner == POINTTYPE_CURVE_START) 5668 pt->corner = POINTTYPE_CURVE_MIDDLE; 5669 else 5670 pt->corner = POINTTYPE_CURVE_END; 5671 } 5672 outline->count--; 5673 } else { 5674 /* outline closed with a line from end to start point */ 5675 attempt_line_merge(outline, outline->count - 1, &pt->pos, FALSE, cos_table); 5676 } 5677 lastpt = &outline->items[0]; 5678 to_curve = lastpt->corner != POINTTYPE_CORNER && lastpt->corner != POINTTYPE_CURVE_END; 5679 if (lastpt->corner == POINTTYPE_CURVE_START) 5680 lastpt->corner = POINTTYPE_CORNER; 5681 pt = &outline->items[1]; 5682 if (attempt_line_merge(outline, 0, &pt->pos, to_curve, cos_table)) 5683 *lastpt = outline->items[outline->count]; 5684 } 5685 5686 lastpt = &outline->items[outline->count - 1]; 5687 pt = &outline->items[0]; 5688 unit_vec2(&lastdir, &lastpt->pos, &pt->pos); 5689 for (j = 0; j < outline->count; j++) 5690 { 5691 D3DXVECTOR2 curdir; 5692 5693 lastpt = pt; 5694 pt = &outline->items[(j + 1) % outline->count]; 5695 unit_vec2(&curdir, &lastpt->pos, &pt->pos); 5696 5697 switch (lastpt->corner) 5698 { 5699 case POINTTYPE_CURVE_START: 5700 case POINTTYPE_CURVE_END: 5701 if (!is_direction_similar(&lastdir, &curdir, cos_table->cos_45)) 5702 lastpt->corner = POINTTYPE_CORNER; 5703 break; 5704 case POINTTYPE_CURVE_MIDDLE: 5705 if (!is_direction_similar(&lastdir, &curdir, cos_table->cos_90)) 5706 lastpt->corner = POINTTYPE_CORNER; 5707 else 5708 lastpt->corner = POINTTYPE_CURVE; 5709 break; 5710 default: 5711 break; 5712 } 5713 lastdir = curdir; 5714 } 5715 5716 header = (TTPOLYGONHEADER *)((char *)header + header->cb); 5717 } 5718 return S_OK; 5719 } 5720 5721 /* Get the y-distance from a line to a point */ 5722 static float get_line_to_point_y_distance(D3DXVECTOR2 *line_pt1, 5723 D3DXVECTOR2 *line_pt2, 5724 D3DXVECTOR2 *point) 5725 { 5726 D3DXVECTOR2 line_vec = {0, 0}; 5727 float line_pt_dx; 5728 float line_y; 5729 5730 D3DXVec2Subtract(&line_vec, line_pt2, line_pt1); 5731 line_pt_dx = point->x - line_pt1->x; 5732 line_y = line_pt1->y + (line_vec.y * line_pt_dx) / line_vec.x; 5733 return point->y - line_y; 5734 } 5735 5736 static D3DXVECTOR2 *get_indexed_point(struct point2d_index *pt_idx) 5737 { 5738 return &pt_idx->outline->items[pt_idx->vertex].pos; 5739 } 5740 5741 static D3DXVECTOR2 *get_ordered_vertex(struct glyphinfo *glyph, WORD index) 5742 { 5743 return get_indexed_point(&glyph->ordered_vertices.items[index]); 5744 } 5745 5746 static void remove_triangulation(struct triangulation_array *array, struct triangulation *item) 5747 { 5748 HeapFree(GetProcessHeap(), 0, item->vertex_stack.items); 5749 MoveMemory(item, item + 1, (char*)&array->items[array->count] - (char*)(item + 1)); 5750 array->count--; 5751 } 5752 5753 static HRESULT triangulation_add_point(struct triangulation **t_ptr, 5754 struct triangulation_array *triangulations, 5755 WORD vtx_idx, 5756 BOOL to_top) 5757 { 5758 struct glyphinfo *glyph = triangulations->glyph; 5759 struct triangulation *t = *t_ptr; 5760 HRESULT hr; 5761 face *face; 5762 int f1, f2; 5763 5764 if (t->last_on_top) { 5765 f1 = 1; 5766 f2 = 2; 5767 } else { 5768 f1 = 2; 5769 f2 = 1; 5770 } 5771 5772 if (t->last_on_top != to_top && t->vertex_stack.count > 1) { 5773 /* consume all vertices on the stack */ 5774 WORD last_pt = t->vertex_stack.items[0]; 5775 int i; 5776 for (i = 1; i < t->vertex_stack.count; i++) 5777 { 5778 face = add_face(&glyph->faces); 5779 if (!face) return E_OUTOFMEMORY; 5780 (*face)[0] = vtx_idx; 5781 (*face)[f1] = last_pt; 5782 (*face)[f2] = last_pt = t->vertex_stack.items[i]; 5783 } 5784 t->vertex_stack.items[0] = last_pt; 5785 t->vertex_stack.count = 1; 5786 } else if (t->vertex_stack.count > 1) { 5787 int i = t->vertex_stack.count - 1; 5788 D3DXVECTOR2 *point = get_ordered_vertex(glyph, vtx_idx); 5789 WORD top_idx = t->vertex_stack.items[i--]; 5790 D3DXVECTOR2 *top_pt = get_ordered_vertex(glyph, top_idx); 5791 5792 while (i >= 0) 5793 { 5794 WORD prev_idx = t->vertex_stack.items[i--]; 5795 D3DXVECTOR2 *prev_pt = get_ordered_vertex(glyph, prev_idx); 5796 5797 if (prev_pt->x != top_pt->x && 5798 ((to_top && get_line_to_point_y_distance(prev_pt, top_pt, point) > 0) || 5799 (!to_top && get_line_to_point_y_distance(prev_pt, top_pt, point) < 0))) 5800 break; 5801 5802 face = add_face(&glyph->faces); 5803 if (!face) return E_OUTOFMEMORY; 5804 (*face)[0] = vtx_idx; 5805 (*face)[f1] = prev_idx; 5806 (*face)[f2] = top_idx; 5807 5808 top_pt = prev_pt; 5809 top_idx = prev_idx; 5810 t->vertex_stack.count--; 5811 } 5812 } 5813 t->last_on_top = to_top; 5814 5815 hr = add_vertex_index(&t->vertex_stack, vtx_idx); 5816 5817 if (hr == S_OK && t->merging) { 5818 struct triangulation *t2; 5819 5820 t2 = to_top ? t - 1 : t + 1; 5821 t2->merging = FALSE; 5822 hr = triangulation_add_point(&t2, triangulations, vtx_idx, to_top); 5823 if (hr != S_OK) return hr; 5824 remove_triangulation(triangulations, t); 5825 if (t2 > t) 5826 t2--; 5827 *t_ptr = t2; 5828 } 5829 return hr; 5830 } 5831 5832 /* check if the point is next on the outline for either the top or bottom */ 5833 static D3DXVECTOR2 *triangulation_get_next_point(struct triangulation *t, struct glyphinfo *glyph, BOOL on_top) 5834 { 5835 int i = t->last_on_top == on_top ? t->vertex_stack.count - 1 : 0; 5836 WORD idx = t->vertex_stack.items[i]; 5837 struct point2d_index *pt_idx = &glyph->ordered_vertices.items[idx]; 5838 struct outline *outline = pt_idx->outline; 5839 5840 if (on_top) 5841 i = (pt_idx->vertex + outline->count - 1) % outline->count; 5842 else 5843 i = (pt_idx->vertex + 1) % outline->count; 5844 5845 return &outline->items[i].pos; 5846 } 5847 5848 static int __cdecl compare_vertex_indices(const void *a, const void *b) 5849 { 5850 const struct point2d_index *idx1 = a, *idx2 = b; 5851 const D3DXVECTOR2 *p1 = &idx1->outline->items[idx1->vertex].pos; 5852 const D3DXVECTOR2 *p2 = &idx2->outline->items[idx2->vertex].pos; 5853 float diff = p1->x - p2->x; 5854 5855 if (diff == 0.0f) 5856 diff = p1->y - p2->y; 5857 5858 return diff == 0.0f ? 0 : (diff > 0.0f ? -1 : 1); 5859 } 5860 5861 static HRESULT triangulate(struct triangulation_array *triangulations) 5862 { 5863 int sweep_idx; 5864 HRESULT hr; 5865 struct glyphinfo *glyph = triangulations->glyph; 5866 int nb_vertices = 0; 5867 int i; 5868 struct point2d_index *idx_ptr; 5869 5870 /* Glyphs without outlines do not generate any vertices. */ 5871 if (!glyph->outlines.count) 5872 return D3D_OK; 5873 5874 for (i = 0; i < glyph->outlines.count; i++) 5875 nb_vertices += glyph->outlines.items[i].count; 5876 5877 glyph->ordered_vertices.items = HeapAlloc(GetProcessHeap(), 0, 5878 nb_vertices * sizeof(*glyph->ordered_vertices.items)); 5879 if (!glyph->ordered_vertices.items) 5880 return E_OUTOFMEMORY; 5881 5882 idx_ptr = glyph->ordered_vertices.items; 5883 for (i = 0; i < glyph->outlines.count; i++) 5884 { 5885 struct outline *outline = &glyph->outlines.items[i]; 5886 int j; 5887 5888 idx_ptr->outline = outline; 5889 idx_ptr->vertex = 0; 5890 idx_ptr++; 5891 for (j = outline->count - 1; j > 0; j--) 5892 { 5893 idx_ptr->outline = outline; 5894 idx_ptr->vertex = j; 5895 idx_ptr++; 5896 } 5897 } 5898 glyph->ordered_vertices.count = nb_vertices; 5899 5900 /* Native implementation seems to try to create a triangle fan from 5901 * the first outline point if the glyph only has one outline. */ 5902 if (glyph->outlines.count == 1) 5903 { 5904 struct outline *outline = glyph->outlines.items; 5905 D3DXVECTOR2 *base = &outline->items[0].pos; 5906 D3DXVECTOR2 *last = &outline->items[1].pos; 5907 float ccw = 0; 5908 5909 for (i = 2; i < outline->count; i++) 5910 { 5911 D3DXVECTOR2 *next = &outline->items[i].pos; 5912 D3DXVECTOR2 v1 = {0.0f, 0.0f}; 5913 D3DXVECTOR2 v2 = {0.0f, 0.0f}; 5914 5915 D3DXVec2Subtract(&v1, base, last); 5916 D3DXVec2Subtract(&v2, last, next); 5917 ccw = D3DXVec2CCW(&v1, &v2); 5918 if (ccw > 0.0f) 5919 break; 5920 5921 last = next; 5922 } 5923 if (ccw <= 0) 5924 { 5925 glyph->faces.items = HeapAlloc(GetProcessHeap(), 0, 5926 (outline->count - 2) * sizeof(glyph->faces.items[0])); 5927 if (!glyph->faces.items) 5928 return E_OUTOFMEMORY; 5929 5930 glyph->faces.count = outline->count - 2; 5931 for (i = 0; i < glyph->faces.count; i++) 5932 { 5933 glyph->faces.items[i][0] = 0; 5934 glyph->faces.items[i][1] = i + 1; 5935 glyph->faces.items[i][2] = i + 2; 5936 } 5937 return S_OK; 5938 } 5939 } 5940 5941 /* Perform 2D polygon triangulation for complex glyphs. 5942 * Triangulation is performed using a sweep line concept, from right to left, 5943 * by processing vertices in sorted order. Complex polygons are split into 5944 * monotone polygons which are triangulated separately. */ 5945 /* FIXME: The order of the faces is not consistent with the native implementation. */ 5946 5947 /* Reserve space for maximum possible faces from triangulation. 5948 * # faces for outer outlines = outline->count - 2 5949 * # faces for inner outlines = outline->count + 2 5950 * There must be at least 1 outer outline. */ 5951 glyph->faces.items = HeapAlloc(GetProcessHeap(), 0, 5952 (nb_vertices + glyph->outlines.count * 2 - 4) * sizeof(glyph->faces.items[0])); 5953 if (!glyph->faces.items) 5954 return E_OUTOFMEMORY; 5955 5956 qsort(glyph->ordered_vertices.items, nb_vertices, 5957 sizeof(glyph->ordered_vertices.items[0]), compare_vertex_indices); 5958 for (sweep_idx = 0; sweep_idx < glyph->ordered_vertices.count; sweep_idx++) 5959 { 5960 int start = 0; 5961 int end = triangulations->count; 5962 5963 while (start < end) 5964 { 5965 D3DXVECTOR2 *sweep_vtx = get_ordered_vertex(glyph, sweep_idx); 5966 int current = (start + end) / 2; 5967 struct triangulation *t = &triangulations->items[current]; 5968 BOOL on_top_outline = FALSE; 5969 D3DXVECTOR2 *top_next, *bottom_next; 5970 WORD top_idx, bottom_idx; 5971 5972 if (t->merging && t->last_on_top) 5973 top_next = triangulation_get_next_point(t + 1, glyph, TRUE); 5974 else 5975 top_next = triangulation_get_next_point(t, glyph, TRUE); 5976 if (sweep_vtx == top_next) 5977 { 5978 if (t->merging && t->last_on_top) 5979 t++; 5980 hr = triangulation_add_point(&t, triangulations, sweep_idx, TRUE); 5981 if (hr != S_OK) return hr; 5982 5983 if (t + 1 < &triangulations->items[triangulations->count] && 5984 triangulation_get_next_point(t + 1, glyph, FALSE) == sweep_vtx) 5985 { 5986 /* point also on bottom outline of higher triangulation */ 5987 struct triangulation *t2 = t + 1; 5988 hr = triangulation_add_point(&t2, triangulations, sweep_idx, FALSE); 5989 if (hr != S_OK) return hr; 5990 5991 t->merging = TRUE; 5992 t2->merging = TRUE; 5993 } 5994 on_top_outline = TRUE; 5995 } 5996 5997 if (t->merging && !t->last_on_top) 5998 bottom_next = triangulation_get_next_point(t - 1, glyph, FALSE); 5999 else 6000 bottom_next = triangulation_get_next_point(t, glyph, FALSE); 6001 if (sweep_vtx == bottom_next) 6002 { 6003 if (t->merging && !t->last_on_top) 6004 t--; 6005 if (on_top_outline) { 6006 /* outline finished */ 6007 remove_triangulation(triangulations, t); 6008 break; 6009 } 6010 6011 hr = triangulation_add_point(&t, triangulations, sweep_idx, FALSE); 6012 if (hr != S_OK) return hr; 6013 6014 if (t > triangulations->items && 6015 triangulation_get_next_point(t - 1, glyph, TRUE) == sweep_vtx) 6016 { 6017 struct triangulation *t2 = t - 1; 6018 /* point also on top outline of lower triangulation */ 6019 hr = triangulation_add_point(&t2, triangulations, sweep_idx, TRUE); 6020 if (hr != S_OK) return hr; 6021 t = t2 + 1; /* t may be invalidated by triangulation merging */ 6022 6023 t->merging = TRUE; 6024 t2->merging = TRUE; 6025 } 6026 break; 6027 } 6028 if (on_top_outline) 6029 break; 6030 6031 if (t->last_on_top) { 6032 top_idx = t->vertex_stack.items[t->vertex_stack.count - 1]; 6033 bottom_idx = t->vertex_stack.items[0]; 6034 } else { 6035 top_idx = t->vertex_stack.items[0]; 6036 bottom_idx = t->vertex_stack.items[t->vertex_stack.count - 1]; 6037 } 6038 6039 /* check if the point is inside or outside this polygon */ 6040 if (get_line_to_point_y_distance(get_ordered_vertex(glyph, top_idx), 6041 top_next, sweep_vtx) > 0) 6042 { /* above */ 6043 start = current + 1; 6044 } else if (get_line_to_point_y_distance(get_ordered_vertex(glyph, bottom_idx), 6045 bottom_next, sweep_vtx) < 0) 6046 { /* below */ 6047 end = current; 6048 } else if (t->merging) { 6049 /* inside, so cancel merging */ 6050 struct triangulation *t2 = t->last_on_top ? t + 1 : t - 1; 6051 t->merging = FALSE; 6052 t2->merging = FALSE; 6053 hr = triangulation_add_point(&t, triangulations, sweep_idx, t->last_on_top); 6054 if (hr != S_OK) return hr; 6055 hr = triangulation_add_point(&t2, triangulations, sweep_idx, t2->last_on_top); 6056 if (hr != S_OK) return hr; 6057 break; 6058 } else { 6059 /* inside, so split polygon into two monotone parts */ 6060 struct triangulation *t2 = add_triangulation(triangulations); 6061 if (!t2) return E_OUTOFMEMORY; 6062 MoveMemory(t + 1, t, (char*)(t2 + 1) - (char*)t); 6063 if (t->last_on_top) { 6064 t2 = t + 1; 6065 } else { 6066 t2 = t; 6067 t++; 6068 } 6069 6070 ZeroMemory(&t2->vertex_stack, sizeof(t2->vertex_stack)); 6071 hr = add_vertex_index(&t2->vertex_stack, t->vertex_stack.items[t->vertex_stack.count - 1]); 6072 if (hr != S_OK) return hr; 6073 hr = add_vertex_index(&t2->vertex_stack, sweep_idx); 6074 if (hr != S_OK) return hr; 6075 t2->last_on_top = !t->last_on_top; 6076 6077 hr = triangulation_add_point(&t, triangulations, sweep_idx, t->last_on_top); 6078 if (hr != S_OK) return hr; 6079 break; 6080 } 6081 } 6082 if (start >= end) 6083 { 6084 struct triangulation *t; 6085 struct triangulation *t2 = add_triangulation(triangulations); 6086 if (!t2) return E_OUTOFMEMORY; 6087 t = &triangulations->items[start]; 6088 MoveMemory(t + 1, t, (char*)(t2 + 1) - (char*)t); 6089 ZeroMemory(t, sizeof(*t)); 6090 hr = add_vertex_index(&t->vertex_stack, sweep_idx); 6091 if (hr != S_OK) return hr; 6092 } 6093 } 6094 return S_OK; 6095 } 6096 6097 HRESULT WINAPI D3DXCreateTextW(struct IDirect3DDevice9 *device, HDC hdc, const WCHAR *text, float deviation, 6098 float extrusion, struct ID3DXMesh **mesh_ptr, struct ID3DXBuffer **adjacency, GLYPHMETRICSFLOAT *glyphmetrics) 6099 { 6100 HRESULT hr; 6101 ID3DXMesh *mesh = NULL; 6102 DWORD nb_vertices, nb_faces; 6103 DWORD nb_front_faces, nb_corners, nb_outline_points; 6104 struct vertex *vertices = NULL; 6105 face *faces = NULL; 6106 int textlen = 0; 6107 float offset_x; 6108 LOGFONTW lf; 6109 OUTLINETEXTMETRICW otm; 6110 HFONT font = NULL, oldfont = NULL; 6111 const MAT2 identity = {{0, 1}, {0, 0}, {0, 0}, {0, 1}}; 6112 void *raw_outline = NULL; 6113 int bufsize = 0; 6114 struct glyphinfo *glyphs = NULL; 6115 GLYPHMETRICS gm; 6116 struct triangulation_array triangulations = {0, 0, NULL}; 6117 int i; 6118 struct vertex *vertex_ptr; 6119 face *face_ptr; 6120 float max_deviation_sq; 6121 const struct cos_table cos_table = { 6122 cosf(D3DXToRadian(0.5f)), 6123 cosf(D3DXToRadian(45.0f)), 6124 cosf(D3DXToRadian(90.0f)), 6125 }; 6126 int f1, f2; 6127 6128 TRACE("(%p, %p, %s, %f, %f, %p, %p, %p)\n", device, hdc, 6129 debugstr_w(text), deviation, extrusion, mesh_ptr, adjacency, glyphmetrics); 6130 6131 if (!device || !hdc || !text || !*text || deviation < 0.0f || extrusion < 0.0f || !mesh_ptr) 6132 return D3DERR_INVALIDCALL; 6133 6134 if (adjacency) 6135 { 6136 FIXME("Case of adjacency != NULL not implemented.\n"); 6137 return E_NOTIMPL; 6138 } 6139 6140 if (!GetObjectW(GetCurrentObject(hdc, OBJ_FONT), sizeof(lf), &lf) || 6141 !GetOutlineTextMetricsW(hdc, sizeof(otm), &otm)) 6142 { 6143 return D3DERR_INVALIDCALL; 6144 } 6145 6146 if (deviation == 0.0f) 6147 deviation = 1.0f / otm.otmEMSquare; 6148 max_deviation_sq = deviation * deviation; 6149 6150 lf.lfHeight = otm.otmEMSquare; 6151 lf.lfWidth = 0; 6152 font = CreateFontIndirectW(&lf); 6153 if (!font) { 6154 hr = E_OUTOFMEMORY; 6155 goto error; 6156 } 6157 oldfont = SelectObject(hdc, font); 6158 6159 textlen = lstrlenW(text); 6160 for (i = 0; i < textlen; i++) 6161 { 6162 int datasize = GetGlyphOutlineW(hdc, text[i], GGO_NATIVE, &gm, 0, NULL, &identity); 6163 if (datasize < 0) 6164 return D3DERR_INVALIDCALL; 6165 if (bufsize < datasize) 6166 bufsize = datasize; 6167 } 6168 if (!bufsize) { /* e.g. text == " " */ 6169 hr = D3DERR_INVALIDCALL; 6170 goto error; 6171 } 6172 6173 glyphs = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, textlen * sizeof(*glyphs)); 6174 raw_outline = HeapAlloc(GetProcessHeap(), 0, bufsize); 6175 if (!glyphs || !raw_outline) { 6176 hr = E_OUTOFMEMORY; 6177 goto error; 6178 } 6179 6180 offset_x = 0.0f; 6181 for (i = 0; i < textlen; i++) 6182 { 6183 /* get outline points from data returned from GetGlyphOutline */ 6184 int datasize; 6185 6186 glyphs[i].offset_x = offset_x; 6187 6188 datasize = GetGlyphOutlineW(hdc, text[i], GGO_NATIVE, &gm, bufsize, raw_outline, &identity); 6189 hr = create_outline(&glyphs[i], raw_outline, datasize, 6190 max_deviation_sq, otm.otmEMSquare, &cos_table); 6191 if (hr != S_OK) goto error; 6192 6193 triangulations.glyph = &glyphs[i]; 6194 hr = triangulate(&triangulations); 6195 if (hr != S_OK) goto error; 6196 if (triangulations.count) { 6197 ERR("%d incomplete triangulations of glyph (%u).\n", triangulations.count, text[i]); 6198 triangulations.count = 0; 6199 } 6200 6201 if (glyphmetrics) 6202 { 6203 glyphmetrics[i].gmfBlackBoxX = gm.gmBlackBoxX / (float)otm.otmEMSquare; 6204 glyphmetrics[i].gmfBlackBoxY = gm.gmBlackBoxY / (float)otm.otmEMSquare; 6205 glyphmetrics[i].gmfptGlyphOrigin.x = gm.gmptGlyphOrigin.x / (float)otm.otmEMSquare; 6206 glyphmetrics[i].gmfptGlyphOrigin.y = gm.gmptGlyphOrigin.y / (float)otm.otmEMSquare; 6207 glyphmetrics[i].gmfCellIncX = gm.gmCellIncX / (float)otm.otmEMSquare; 6208 glyphmetrics[i].gmfCellIncY = gm.gmCellIncY / (float)otm.otmEMSquare; 6209 } 6210 offset_x += gm.gmCellIncX / (float)otm.otmEMSquare; 6211 } 6212 6213 /* corner points need an extra vertex for the different side faces normals */ 6214 nb_corners = 0; 6215 nb_outline_points = 0; 6216 nb_front_faces = 0; 6217 for (i = 0; i < textlen; i++) 6218 { 6219 int j; 6220 nb_outline_points += glyphs[i].ordered_vertices.count; 6221 nb_front_faces += glyphs[i].faces.count; 6222 for (j = 0; j < glyphs[i].outlines.count; j++) 6223 { 6224 int k; 6225 struct outline *outline = &glyphs[i].outlines.items[j]; 6226 nb_corners++; /* first outline point always repeated as a corner */ 6227 for (k = 1; k < outline->count; k++) 6228 if (outline->items[k].corner) 6229 nb_corners++; 6230 } 6231 } 6232 6233 nb_vertices = (nb_outline_points + nb_corners) * 2 + nb_outline_points * 2; 6234 nb_faces = nb_outline_points * 2 + nb_front_faces * 2; 6235 6236 6237 hr = D3DXCreateMeshFVF(nb_faces, nb_vertices, D3DXMESH_MANAGED, 6238 D3DFVF_XYZ | D3DFVF_NORMAL, device, &mesh); 6239 if (FAILED(hr)) 6240 goto error; 6241 6242 if (FAILED(hr = mesh->lpVtbl->LockVertexBuffer(mesh, 0, (void **)&vertices))) 6243 goto error; 6244 6245 if (FAILED(hr = mesh->lpVtbl->LockIndexBuffer(mesh, 0, (void **)&faces))) 6246 goto error; 6247 6248 /* convert 2D vertices and faces into 3D mesh */ 6249 vertex_ptr = vertices; 6250 face_ptr = faces; 6251 if (extrusion == 0.0f) { 6252 f1 = 1; 6253 f2 = 2; 6254 } else { 6255 f1 = 2; 6256 f2 = 1; 6257 } 6258 for (i = 0; i < textlen; i++) 6259 { 6260 int j; 6261 int count; 6262 struct vertex *back_vertices; 6263 face *back_faces; 6264 6265 /* side vertices and faces */ 6266 for (j = 0; j < glyphs[i].outlines.count; j++) 6267 { 6268 struct vertex *outline_vertices = vertex_ptr; 6269 struct outline *outline = &glyphs[i].outlines.items[j]; 6270 int k; 6271 struct point2d *prevpt = &outline->items[outline->count - 1]; 6272 struct point2d *pt = &outline->items[0]; 6273 6274 for (k = 1; k <= outline->count; k++) 6275 { 6276 struct vertex vtx; 6277 struct point2d *nextpt = &outline->items[k % outline->count]; 6278 WORD vtx_idx = vertex_ptr - vertices; 6279 D3DXVECTOR2 vec; 6280 6281 if (pt->corner == POINTTYPE_CURVE_START) 6282 D3DXVec2Subtract(&vec, &pt->pos, &prevpt->pos); 6283 else if (pt->corner) 6284 D3DXVec2Subtract(&vec, &nextpt->pos, &pt->pos); 6285 else 6286 D3DXVec2Subtract(&vec, &nextpt->pos, &prevpt->pos); 6287 D3DXVec2Normalize(&vec, &vec); 6288 vtx.normal.x = -vec.y; 6289 vtx.normal.y = vec.x; 6290 vtx.normal.z = 0; 6291 6292 vtx.position.x = pt->pos.x + glyphs[i].offset_x; 6293 vtx.position.y = pt->pos.y; 6294 vtx.position.z = 0; 6295 *vertex_ptr++ = vtx; 6296 6297 vtx.position.z = -extrusion; 6298 *vertex_ptr++ = vtx; 6299 6300 vtx.position.x = nextpt->pos.x + glyphs[i].offset_x; 6301 vtx.position.y = nextpt->pos.y; 6302 if (pt->corner && nextpt->corner && nextpt->corner != POINTTYPE_CURVE_END) { 6303 vtx.position.z = -extrusion; 6304 *vertex_ptr++ = vtx; 6305 vtx.position.z = 0; 6306 *vertex_ptr++ = vtx; 6307 6308 (*face_ptr)[0] = vtx_idx; 6309 (*face_ptr)[1] = vtx_idx + 2; 6310 (*face_ptr)[2] = vtx_idx + 1; 6311 face_ptr++; 6312 6313 (*face_ptr)[0] = vtx_idx; 6314 (*face_ptr)[1] = vtx_idx + 3; 6315 (*face_ptr)[2] = vtx_idx + 2; 6316 face_ptr++; 6317 } else { 6318 if (nextpt->corner) { 6319 if (nextpt->corner == POINTTYPE_CURVE_END) { 6320 D3DXVECTOR2 *nextpt2 = &outline->items[(k + 1) % outline->count].pos; 6321 D3DXVec2Subtract(&vec, nextpt2, &nextpt->pos); 6322 } else { 6323 D3DXVec2Subtract(&vec, &nextpt->pos, &pt->pos); 6324 } 6325 D3DXVec2Normalize(&vec, &vec); 6326 vtx.normal.x = -vec.y; 6327 vtx.normal.y = vec.x; 6328 6329 vtx.position.z = 0; 6330 *vertex_ptr++ = vtx; 6331 vtx.position.z = -extrusion; 6332 *vertex_ptr++ = vtx; 6333 } 6334 6335 (*face_ptr)[0] = vtx_idx; 6336 (*face_ptr)[1] = vtx_idx + 3; 6337 (*face_ptr)[2] = vtx_idx + 1; 6338 face_ptr++; 6339 6340 (*face_ptr)[0] = vtx_idx; 6341 (*face_ptr)[1] = vtx_idx + 2; 6342 (*face_ptr)[2] = vtx_idx + 3; 6343 face_ptr++; 6344 } 6345 6346 prevpt = pt; 6347 pt = nextpt; 6348 } 6349 if (!pt->corner) { 6350 *vertex_ptr++ = *outline_vertices++; 6351 *vertex_ptr++ = *outline_vertices++; 6352 } 6353 } 6354 6355 /* back vertices and faces */ 6356 back_faces = face_ptr; 6357 back_vertices = vertex_ptr; 6358 for (j = 0; j < glyphs[i].ordered_vertices.count; j++) 6359 { 6360 D3DXVECTOR2 *pt = get_ordered_vertex(&glyphs[i], j); 6361 vertex_ptr->position.x = pt->x + glyphs[i].offset_x; 6362 vertex_ptr->position.y = pt->y; 6363 vertex_ptr->position.z = 0; 6364 vertex_ptr->normal.x = 0; 6365 vertex_ptr->normal.y = 0; 6366 vertex_ptr->normal.z = 1; 6367 vertex_ptr++; 6368 } 6369 count = back_vertices - vertices; 6370 for (j = 0; j < glyphs[i].faces.count; j++) 6371 { 6372 face *f = &glyphs[i].faces.items[j]; 6373 (*face_ptr)[0] = (*f)[0] + count; 6374 (*face_ptr)[1] = (*f)[1] + count; 6375 (*face_ptr)[2] = (*f)[2] + count; 6376 face_ptr++; 6377 } 6378 6379 /* front vertices and faces */ 6380 j = count = vertex_ptr - back_vertices; 6381 while (j--) 6382 { 6383 vertex_ptr->position.x = back_vertices->position.x; 6384 vertex_ptr->position.y = back_vertices->position.y; 6385 vertex_ptr->position.z = -extrusion; 6386 vertex_ptr->normal.x = 0; 6387 vertex_ptr->normal.y = 0; 6388 vertex_ptr->normal.z = extrusion == 0.0f ? 1.0f : -1.0f; 6389 vertex_ptr++; 6390 back_vertices++; 6391 } 6392 j = face_ptr - back_faces; 6393 while (j--) 6394 { 6395 (*face_ptr)[0] = (*back_faces)[0] + count; 6396 (*face_ptr)[1] = (*back_faces)[f1] + count; 6397 (*face_ptr)[2] = (*back_faces)[f2] + count; 6398 face_ptr++; 6399 back_faces++; 6400 } 6401 } 6402 6403 *mesh_ptr = mesh; 6404 hr = D3D_OK; 6405 error: 6406 if (mesh) { 6407 if (faces) mesh->lpVtbl->UnlockIndexBuffer(mesh); 6408 if (vertices) mesh->lpVtbl->UnlockVertexBuffer(mesh); 6409 if (hr != D3D_OK) mesh->lpVtbl->Release(mesh); 6410 } 6411 if (glyphs) { 6412 for (i = 0; i < textlen; i++) 6413 { 6414 int j; 6415 for (j = 0; j < glyphs[i].outlines.count; j++) 6416 HeapFree(GetProcessHeap(), 0, glyphs[i].outlines.items[j].items); 6417 HeapFree(GetProcessHeap(), 0, glyphs[i].outlines.items); 6418 HeapFree(GetProcessHeap(), 0, glyphs[i].faces.items); 6419 HeapFree(GetProcessHeap(), 0, glyphs[i].ordered_vertices.items); 6420 } 6421 HeapFree(GetProcessHeap(), 0, glyphs); 6422 } 6423 if (triangulations.items) { 6424 int i; 6425 for (i = 0; i < triangulations.count; i++) 6426 HeapFree(GetProcessHeap(), 0, triangulations.items[i].vertex_stack.items); 6427 HeapFree(GetProcessHeap(), 0, triangulations.items); 6428 } 6429 HeapFree(GetProcessHeap(), 0, raw_outline); 6430 if (oldfont) SelectObject(hdc, oldfont); 6431 if (font) DeleteObject(font); 6432 6433 return hr; 6434 } 6435 6436 HRESULT WINAPI D3DXValidMesh(ID3DXMesh *mesh, const DWORD *adjacency, ID3DXBuffer **errors_and_warnings) 6437 { 6438 FIXME("(%p, %p, %p): stub\n", mesh, adjacency, *errors_and_warnings); 6439 6440 return E_NOTIMPL; 6441 } 6442 6443 static BOOL weld_float1(void *to, void *from, FLOAT epsilon) 6444 { 6445 FLOAT *v1 = to; 6446 FLOAT *v2 = from; 6447 6448 if (fabsf(*v1 - *v2) <= epsilon) 6449 { 6450 *v1 = *v2; 6451 6452 return TRUE; 6453 } 6454 6455 return FALSE; 6456 } 6457 6458 static BOOL weld_float2(void *to, void *from, FLOAT epsilon) 6459 { 6460 D3DXVECTOR2 *v1 = to; 6461 D3DXVECTOR2 *v2 = from; 6462 FLOAT diff_x = fabsf(v1->x - v2->x); 6463 FLOAT diff_y = fabsf(v1->y - v2->y); 6464 FLOAT max_abs_diff = max(diff_x, diff_y); 6465 6466 if (max_abs_diff <= epsilon) 6467 { 6468 memcpy(to, from, sizeof(D3DXVECTOR2)); 6469 6470 return TRUE; 6471 } 6472 6473 return FALSE; 6474 } 6475 6476 static BOOL weld_float3(void *to, void *from, FLOAT epsilon) 6477 { 6478 D3DXVECTOR3 *v1 = to; 6479 D3DXVECTOR3 *v2 = from; 6480 FLOAT diff_x = fabsf(v1->x - v2->x); 6481 FLOAT diff_y = fabsf(v1->y - v2->y); 6482 FLOAT diff_z = fabsf(v1->z - v2->z); 6483 FLOAT max_abs_diff = max(diff_x, diff_y); 6484 max_abs_diff = max(diff_z, max_abs_diff); 6485 6486 if (max_abs_diff <= epsilon) 6487 { 6488 memcpy(to, from, sizeof(D3DXVECTOR3)); 6489 6490 return TRUE; 6491 } 6492 6493 return FALSE; 6494 } 6495 6496 static BOOL weld_float4(void *to, void *from, FLOAT epsilon) 6497 { 6498 D3DXVECTOR4 *v1 = to; 6499 D3DXVECTOR4 *v2 = from; 6500 FLOAT diff_x = fabsf(v1->x - v2->x); 6501 FLOAT diff_y = fabsf(v1->y - v2->y); 6502 FLOAT diff_z = fabsf(v1->z - v2->z); 6503 FLOAT diff_w = fabsf(v1->w - v2->w); 6504 FLOAT max_abs_diff = max(diff_x, diff_y); 6505 max_abs_diff = max(diff_z, max_abs_diff); 6506 max_abs_diff = max(diff_w, max_abs_diff); 6507 6508 if (max_abs_diff <= epsilon) 6509 { 6510 memcpy(to, from, sizeof(D3DXVECTOR4)); 6511 6512 return TRUE; 6513 } 6514 6515 return FALSE; 6516 } 6517 6518 static BOOL weld_ubyte4(void *to, void *from, FLOAT epsilon) 6519 { 6520 BYTE *b1 = to; 6521 BYTE *b2 = from; 6522 BYTE truncated_epsilon = (BYTE)epsilon; 6523 BYTE diff_x = b1[0] > b2[0] ? b1[0] - b2[0] : b2[0] - b1[0]; 6524 BYTE diff_y = b1[1] > b2[1] ? b1[1] - b2[1] : b2[1] - b1[1]; 6525 BYTE diff_z = b1[2] > b2[2] ? b1[2] - b2[2] : b2[2] - b1[2]; 6526 BYTE diff_w = b1[3] > b2[3] ? b1[3] - b2[3] : b2[3] - b1[3]; 6527 BYTE max_diff = max(diff_x, diff_y); 6528 max_diff = max(diff_z, max_diff); 6529 max_diff = max(diff_w, max_diff); 6530 6531 if (max_diff <= truncated_epsilon) 6532 { 6533 memcpy(to, from, 4 * sizeof(BYTE)); 6534 6535 return TRUE; 6536 } 6537 6538 return FALSE; 6539 } 6540 6541 static BOOL weld_ubyte4n(void *to, void *from, FLOAT epsilon) 6542 { 6543 return weld_ubyte4(to, from, epsilon * UCHAR_MAX); 6544 } 6545 6546 static BOOL weld_d3dcolor(void *to, void *from, FLOAT epsilon) 6547 { 6548 return weld_ubyte4n(to, from, epsilon); 6549 } 6550 6551 static BOOL weld_short2(void *to, void *from, FLOAT epsilon) 6552 { 6553 SHORT *s1 = to; 6554 SHORT *s2 = from; 6555 SHORT truncated_epsilon = (SHORT)epsilon; 6556 SHORT diff_x = abs(s1[0] - s2[0]); 6557 SHORT diff_y = abs(s1[1] - s2[1]); 6558 SHORT max_abs_diff = max(diff_x, diff_y); 6559 6560 if (max_abs_diff <= truncated_epsilon) 6561 { 6562 memcpy(to, from, 2 * sizeof(SHORT)); 6563 6564 return TRUE; 6565 } 6566 6567 return FALSE; 6568 } 6569 6570 static BOOL weld_short2n(void *to, void *from, FLOAT epsilon) 6571 { 6572 return weld_short2(to, from, epsilon * SHRT_MAX); 6573 } 6574 6575 static BOOL weld_short4(void *to, void *from, FLOAT epsilon) 6576 { 6577 SHORT *s1 = to; 6578 SHORT *s2 = from; 6579 SHORT truncated_epsilon = (SHORT)epsilon; 6580 SHORT diff_x = abs(s1[0] - s2[0]); 6581 SHORT diff_y = abs(s1[1] - s2[1]); 6582 SHORT diff_z = abs(s1[2] - s2[2]); 6583 SHORT diff_w = abs(s1[3] - s2[3]); 6584 SHORT max_abs_diff = max(diff_x, diff_y); 6585 max_abs_diff = max(diff_z, max_abs_diff); 6586 max_abs_diff = max(diff_w, max_abs_diff); 6587 6588 if (max_abs_diff <= truncated_epsilon) 6589 { 6590 memcpy(to, from, 4 * sizeof(SHORT)); 6591 6592 return TRUE; 6593 } 6594 6595 return FALSE; 6596 } 6597 6598 static BOOL weld_short4n(void *to, void *from, FLOAT epsilon) 6599 { 6600 return weld_short4(to, from, epsilon * SHRT_MAX); 6601 } 6602 6603 static BOOL weld_ushort2n(void *to, void *from, FLOAT epsilon) 6604 { 6605 USHORT *s1 = to; 6606 USHORT *s2 = from; 6607 USHORT scaled_epsilon = (USHORT)(epsilon * USHRT_MAX); 6608 USHORT diff_x = s1[0] > s2[0] ? s1[0] - s2[0] : s2[0] - s1[0]; 6609 USHORT diff_y = s1[1] > s2[1] ? s1[1] - s2[1] : s2[1] - s1[1]; 6610 USHORT max_diff = max(diff_x, diff_y); 6611 6612 if (max_diff <= scaled_epsilon) 6613 { 6614 memcpy(to, from, 2 * sizeof(USHORT)); 6615 6616 return TRUE; 6617 } 6618 6619 return FALSE; 6620 } 6621 6622 static BOOL weld_ushort4n(void *to, void *from, FLOAT epsilon) 6623 { 6624 USHORT *s1 = to; 6625 USHORT *s2 = from; 6626 USHORT scaled_epsilon = (USHORT)(epsilon * USHRT_MAX); 6627 USHORT diff_x = s1[0] > s2[0] ? s1[0] - s2[0] : s2[0] - s1[0]; 6628 USHORT diff_y = s1[1] > s2[1] ? s1[1] - s2[1] : s2[1] - s1[1]; 6629 USHORT diff_z = s1[2] > s2[2] ? s1[2] - s2[2] : s2[2] - s1[2]; 6630 USHORT diff_w = s1[3] > s2[3] ? s1[3] - s2[3] : s2[3] - s1[3]; 6631 USHORT max_diff = max(diff_x, diff_y); 6632 max_diff = max(diff_z, max_diff); 6633 max_diff = max(diff_w, max_diff); 6634 6635 if (max_diff <= scaled_epsilon) 6636 { 6637 memcpy(to, from, 4 * sizeof(USHORT)); 6638 6639 return TRUE; 6640 } 6641 6642 return FALSE; 6643 } 6644 6645 struct udec3 6646 { 6647 UINT x; 6648 UINT y; 6649 UINT z; 6650 UINT w; 6651 }; 6652 6653 static struct udec3 dword_to_udec3(DWORD d) 6654 { 6655 struct udec3 v; 6656 6657 v.x = d & 0x3ff; 6658 v.y = (d & 0xffc00) >> 10; 6659 v.z = (d & 0x3ff00000) >> 20; 6660 v.w = (d & 0xc0000000) >> 30; 6661 6662 return v; 6663 } 6664 6665 static BOOL weld_udec3(void *to, void *from, FLOAT epsilon) 6666 { 6667 DWORD *d1 = to; 6668 DWORD *d2 = from; 6669 struct udec3 v1 = dword_to_udec3(*d1); 6670 struct udec3 v2 = dword_to_udec3(*d2); 6671 UINT truncated_epsilon = (UINT)epsilon; 6672 UINT diff_x = v1.x > v2.x ? v1.x - v2.x : v2.x - v1.x; 6673 UINT diff_y = v1.y > v2.y ? v1.y - v2.y : v2.y - v1.y; 6674 UINT diff_z = v1.z > v2.z ? v1.z - v2.z : v2.z - v1.z; 6675 UINT diff_w = v1.w > v2.w ? v1.w - v2.w : v2.w - v1.w; 6676 UINT max_diff = max(diff_x, diff_y); 6677 max_diff = max(diff_z, max_diff); 6678 max_diff = max(diff_w, max_diff); 6679 6680 if (max_diff <= truncated_epsilon) 6681 { 6682 memcpy(to, from, sizeof(DWORD)); 6683 6684 return TRUE; 6685 } 6686 6687 return FALSE; 6688 } 6689 6690 struct dec3n 6691 { 6692 INT x; 6693 INT y; 6694 INT z; 6695 INT w; 6696 }; 6697 6698 static struct dec3n dword_to_dec3n(DWORD d) 6699 { 6700 struct dec3n v; 6701 6702 v.x = d & 0x3ff; 6703 v.y = (d & 0xffc00) >> 10; 6704 v.z = (d & 0x3ff00000) >> 20; 6705 v.w = (d & 0xc0000000) >> 30; 6706 6707 return v; 6708 } 6709 6710 static BOOL weld_dec3n(void *to, void *from, FLOAT epsilon) 6711 { 6712 const UINT MAX_DEC3N = 511; 6713 DWORD *d1 = to; 6714 DWORD *d2 = from; 6715 struct dec3n v1 = dword_to_dec3n(*d1); 6716 struct dec3n v2 = dword_to_dec3n(*d2); 6717 INT scaled_epsilon = (INT)(epsilon * MAX_DEC3N); 6718 INT diff_x = abs(v1.x - v2.x); 6719 INT diff_y = abs(v1.y - v2.y); 6720 INT diff_z = abs(v1.z - v2.z); 6721 INT diff_w = abs(v1.w - v2.w); 6722 INT max_abs_diff = max(diff_x, diff_y); 6723 max_abs_diff = max(diff_z, max_abs_diff); 6724 max_abs_diff = max(diff_w, max_abs_diff); 6725 6726 if (max_abs_diff <= scaled_epsilon) 6727 { 6728 memcpy(to, from, sizeof(DWORD)); 6729 6730 return TRUE; 6731 } 6732 6733 return FALSE; 6734 } 6735 6736 static BOOL weld_float16_2(void *to, void *from, FLOAT epsilon) 6737 { 6738 D3DXFLOAT16 *v1_float16 = to; 6739 D3DXFLOAT16 *v2_float16 = from; 6740 FLOAT diff_x; 6741 FLOAT diff_y; 6742 FLOAT max_abs_diff; 6743 #define NUM_ELEM 2 6744 FLOAT v1[NUM_ELEM]; 6745 FLOAT v2[NUM_ELEM]; 6746 6747 D3DXFloat16To32Array(v1, v1_float16, NUM_ELEM); 6748 D3DXFloat16To32Array(v2, v2_float16, NUM_ELEM); 6749 6750 diff_x = fabsf(v1[0] - v2[0]); 6751 diff_y = fabsf(v1[1] - v2[1]); 6752 max_abs_diff = max(diff_x, diff_y); 6753 6754 if (max_abs_diff <= epsilon) 6755 { 6756 memcpy(to, from, NUM_ELEM * sizeof(D3DXFLOAT16)); 6757 6758 return TRUE; 6759 } 6760 6761 return FALSE; 6762 #undef NUM_ELEM 6763 } 6764 6765 static BOOL weld_float16_4(void *to, void *from, FLOAT epsilon) 6766 { 6767 D3DXFLOAT16 *v1_float16 = to; 6768 D3DXFLOAT16 *v2_float16 = from; 6769 FLOAT diff_x; 6770 FLOAT diff_y; 6771 FLOAT diff_z; 6772 FLOAT diff_w; 6773 FLOAT max_abs_diff; 6774 #define NUM_ELEM 4 6775 FLOAT v1[NUM_ELEM]; 6776 FLOAT v2[NUM_ELEM]; 6777 6778 D3DXFloat16To32Array(v1, v1_float16, NUM_ELEM); 6779 D3DXFloat16To32Array(v2, v2_float16, NUM_ELEM); 6780 6781 diff_x = fabsf(v1[0] - v2[0]); 6782 diff_y = fabsf(v1[1] - v2[1]); 6783 diff_z = fabsf(v1[2] - v2[2]); 6784 diff_w = fabsf(v1[3] - v2[3]); 6785 max_abs_diff = max(diff_x, diff_y); 6786 max_abs_diff = max(diff_z, max_abs_diff); 6787 max_abs_diff = max(diff_w, max_abs_diff); 6788 6789 if (max_abs_diff <= epsilon) 6790 { 6791 memcpy(to, from, NUM_ELEM * sizeof(D3DXFLOAT16)); 6792 6793 return TRUE; 6794 } 6795 6796 return FALSE; 6797 #undef NUM_ELEM 6798 } 6799 6800 /* Sets the vertex components to the same value if they are within epsilon. */ 6801 static BOOL weld_component(void *to, void *from, D3DDECLTYPE type, FLOAT epsilon) 6802 { 6803 /* Quiet FIXMEs as this is in a loop with potentially thousand of iterations. */ 6804 BOOL fixme_once_unused = FALSE; 6805 BOOL fixme_once_unknown = FALSE; 6806 6807 switch (type) 6808 { 6809 case D3DDECLTYPE_FLOAT1: 6810 return weld_float1(to, from, epsilon); 6811 6812 case D3DDECLTYPE_FLOAT2: 6813 return weld_float2(to, from, epsilon); 6814 6815 case D3DDECLTYPE_FLOAT3: 6816 return weld_float3(to, from, epsilon); 6817 6818 case D3DDECLTYPE_FLOAT4: 6819 return weld_float4(to, from, epsilon); 6820 6821 case D3DDECLTYPE_D3DCOLOR: 6822 return weld_d3dcolor(to, from, epsilon); 6823 6824 case D3DDECLTYPE_UBYTE4: 6825 return weld_ubyte4(to, from, epsilon); 6826 6827 case D3DDECLTYPE_SHORT2: 6828 return weld_short2(to, from, epsilon); 6829 6830 case D3DDECLTYPE_SHORT4: 6831 return weld_short4(to, from, epsilon); 6832 6833 case D3DDECLTYPE_UBYTE4N: 6834 return weld_ubyte4n(to, from, epsilon); 6835 6836 case D3DDECLTYPE_SHORT2N: 6837 return weld_short2n(to, from, epsilon); 6838 6839 case D3DDECLTYPE_SHORT4N: 6840 return weld_short4n(to, from, epsilon); 6841 6842 case D3DDECLTYPE_USHORT2N: 6843 return weld_ushort2n(to, from, epsilon); 6844 6845 case D3DDECLTYPE_USHORT4N: 6846 return weld_ushort4n(to, from, epsilon); 6847 6848 case D3DDECLTYPE_UDEC3: 6849 return weld_udec3(to, from, epsilon); 6850 6851 case D3DDECLTYPE_DEC3N: 6852 return weld_dec3n(to, from, epsilon); 6853 6854 case D3DDECLTYPE_FLOAT16_2: 6855 return weld_float16_2(to, from, epsilon); 6856 6857 case D3DDECLTYPE_FLOAT16_4: 6858 return weld_float16_4(to, from, epsilon); 6859 6860 case D3DDECLTYPE_UNUSED: 6861 if (!fixme_once_unused++) 6862 FIXME("D3DDECLTYPE_UNUSED welding not implemented.\n"); 6863 break; 6864 6865 default: 6866 if (!fixme_once_unknown++) 6867 FIXME("Welding of unknown declaration type %d is not implemented.\n", type); 6868 break; 6869 } 6870 6871 return FALSE; 6872 } 6873 6874 static FLOAT get_component_epsilon(const D3DVERTEXELEMENT9 *decl_ptr, const D3DXWELDEPSILONS *epsilons) 6875 { 6876 FLOAT epsilon = 0.0f; 6877 /* Quiet FIXMEs as this is in a loop with potentially thousand of iterations. */ 6878 static BOOL fixme_once_blendindices = FALSE; 6879 static BOOL fixme_once_positiont = FALSE; 6880 static BOOL fixme_once_fog = FALSE; 6881 static BOOL fixme_once_depth = FALSE; 6882 static BOOL fixme_once_sample = FALSE; 6883 static BOOL fixme_once_unknown = FALSE; 6884 6885 switch (decl_ptr->Usage) 6886 { 6887 case D3DDECLUSAGE_POSITION: 6888 epsilon = epsilons->Position; 6889 break; 6890 case D3DDECLUSAGE_BLENDWEIGHT: 6891 epsilon = epsilons->BlendWeights; 6892 break; 6893 case D3DDECLUSAGE_NORMAL: 6894 epsilon = epsilons->Normals; 6895 break; 6896 case D3DDECLUSAGE_PSIZE: 6897 epsilon = epsilons->PSize; 6898 break; 6899 case D3DDECLUSAGE_TEXCOORD: 6900 { 6901 BYTE usage_index = decl_ptr->UsageIndex; 6902 if (usage_index > 7) 6903 usage_index = 7; 6904 epsilon = epsilons->Texcoords[usage_index]; 6905 break; 6906 } 6907 case D3DDECLUSAGE_TANGENT: 6908 epsilon = epsilons->Tangent; 6909 break; 6910 case D3DDECLUSAGE_BINORMAL: 6911 epsilon = epsilons->Binormal; 6912 break; 6913 case D3DDECLUSAGE_TESSFACTOR: 6914 epsilon = epsilons->TessFactor; 6915 break; 6916 case D3DDECLUSAGE_COLOR: 6917 if (decl_ptr->UsageIndex == 0) 6918 epsilon = epsilons->Diffuse; 6919 else if (decl_ptr->UsageIndex == 1) 6920 epsilon = epsilons->Specular; 6921 else 6922 epsilon = 1e-6f; 6923 break; 6924 case D3DDECLUSAGE_BLENDINDICES: 6925 if (!fixme_once_blendindices++) 6926 FIXME("D3DDECLUSAGE_BLENDINDICES welding not implemented.\n"); 6927 break; 6928 case D3DDECLUSAGE_POSITIONT: 6929 if (!fixme_once_positiont++) 6930 FIXME("D3DDECLUSAGE_POSITIONT welding not implemented.\n"); 6931 break; 6932 case D3DDECLUSAGE_FOG: 6933 if (!fixme_once_fog++) 6934 FIXME("D3DDECLUSAGE_FOG welding not implemented.\n"); 6935 break; 6936 case D3DDECLUSAGE_DEPTH: 6937 if (!fixme_once_depth++) 6938 FIXME("D3DDECLUSAGE_DEPTH welding not implemented.\n"); 6939 break; 6940 case D3DDECLUSAGE_SAMPLE: 6941 if (!fixme_once_sample++) 6942 FIXME("D3DDECLUSAGE_SAMPLE welding not implemented.\n"); 6943 break; 6944 default: 6945 if (!fixme_once_unknown++) 6946 FIXME("Unknown usage %x\n", decl_ptr->Usage); 6947 break; 6948 } 6949 6950 return epsilon; 6951 } 6952 6953 /* Helper function for reading a 32-bit index buffer. */ 6954 static inline DWORD read_ib(void *index_buffer, BOOL indices_are_32bit, 6955 DWORD index) 6956 { 6957 if (indices_are_32bit) 6958 { 6959 DWORD *indices = index_buffer; 6960 return indices[index]; 6961 } 6962 else 6963 { 6964 WORD *indices = index_buffer; 6965 return indices[index]; 6966 } 6967 } 6968 6969 /* Helper function for writing to a 32-bit index buffer. */ 6970 static inline void write_ib(void *index_buffer, BOOL indices_are_32bit, 6971 DWORD index, DWORD value) 6972 { 6973 if (indices_are_32bit) 6974 { 6975 DWORD *indices = index_buffer; 6976 indices[index] = value; 6977 } 6978 else 6979 { 6980 WORD *indices = index_buffer; 6981 indices[index] = value; 6982 } 6983 } 6984 6985 /************************************************************************* 6986 * D3DXWeldVertices (D3DX9_36.@) 6987 * 6988 * Welds together similar vertices. The similarity between vert- 6989 * ices can be the position and other components such as 6990 * normal and color. 6991 * 6992 * PARAMS 6993 * mesh [I] Mesh which vertices will be welded together. 6994 * flags [I] D3DXWELDEPSILONSFLAGS specifying how to weld. 6995 * epsilons [I] How similar a component needs to be for welding. 6996 * adjacency [I] Which faces are adjacent to other faces. 6997 * adjacency_out [O] Updated adjacency after welding. 6998 * face_remap_out [O] Which faces the old faces have been mapped to. 6999 * vertex_remap_out [O] Which vertices the old vertices have been mapped to. 7000 * 7001 * RETURNS 7002 * Success: D3D_OK. 7003 * Failure: D3DERR_INVALIDCALL, E_OUTOFMEMORY. 7004 * 7005 * BUGS 7006 * Attribute sorting not implemented. 7007 * 7008 */ 7009 HRESULT WINAPI D3DXWeldVertices(ID3DXMesh *mesh, DWORD flags, const D3DXWELDEPSILONS *epsilons, 7010 const DWORD *adjacency, DWORD *adjacency_out, DWORD *face_remap_out, ID3DXBuffer **vertex_remap_out) 7011 { 7012 DWORD *adjacency_generated = NULL; 7013 const DWORD *adjacency_ptr; 7014 DWORD *attributes = NULL; 7015 const FLOAT DEFAULT_EPSILON = 1.0e-6f; 7016 HRESULT hr; 7017 DWORD i; 7018 void *indices = NULL; 7019 BOOL indices_are_32bit = mesh->lpVtbl->GetOptions(mesh) & D3DXMESH_32BIT; 7020 DWORD optimize_flags; 7021 DWORD *point_reps = NULL; 7022 struct d3dx9_mesh *This = impl_from_ID3DXMesh(mesh); 7023 DWORD *vertex_face_map = NULL; 7024 BYTE *vertices = NULL; 7025 7026 TRACE("mesh %p, flags %#x, epsilons %p, adjacency %p, adjacency_out %p, face_remap_out %p, vertex_remap_out %p.\n", 7027 mesh, flags, epsilons, adjacency, adjacency_out, face_remap_out, vertex_remap_out); 7028 7029 if (flags == 0) 7030 { 7031 WARN("No flags are undefined. Using D3DXWELDEPSILONS_WELDPARTIALMATCHES instead.\n"); 7032 flags = D3DXWELDEPSILONS_WELDPARTIALMATCHES; 7033 } 7034 7035 if (adjacency) /* Use supplied adjacency. */ 7036 { 7037 adjacency_ptr = adjacency; 7038 } 7039 else /* Adjacency has to be generated. */ 7040 { 7041 adjacency_generated = HeapAlloc(GetProcessHeap(), 0, 3 * This->numfaces * sizeof(*adjacency_generated)); 7042 if (!adjacency_generated) 7043 { 7044 ERR("Couldn't allocate memory for adjacency_generated.\n"); 7045 hr = E_OUTOFMEMORY; 7046 goto cleanup; 7047 } 7048 hr = mesh->lpVtbl->GenerateAdjacency(mesh, DEFAULT_EPSILON, adjacency_generated); 7049 if (FAILED(hr)) 7050 { 7051 ERR("Couldn't generate adjacency.\n"); 7052 goto cleanup; 7053 } 7054 adjacency_ptr = adjacency_generated; 7055 } 7056 7057 /* Point representation says which vertices can be replaced. */ 7058 point_reps = HeapAlloc(GetProcessHeap(), 0, This->numvertices * sizeof(*point_reps)); 7059 if (!point_reps) 7060 { 7061 hr = E_OUTOFMEMORY; 7062 ERR("Couldn't allocate memory for point_reps.\n"); 7063 goto cleanup; 7064 } 7065 hr = mesh->lpVtbl->ConvertAdjacencyToPointReps(mesh, adjacency_ptr, point_reps); 7066 if (FAILED(hr)) 7067 { 7068 ERR("ConvertAdjacencyToPointReps failed.\n"); 7069 goto cleanup; 7070 } 7071 7072 hr = mesh->lpVtbl->LockIndexBuffer(mesh, 0, &indices); 7073 if (FAILED(hr)) 7074 { 7075 ERR("Couldn't lock index buffer.\n"); 7076 goto cleanup; 7077 } 7078 7079 hr = mesh->lpVtbl->LockAttributeBuffer(mesh, 0, &attributes); 7080 if (FAILED(hr)) 7081 { 7082 ERR("Couldn't lock attribute buffer.\n"); 7083 goto cleanup; 7084 } 7085 vertex_face_map = HeapAlloc(GetProcessHeap(), 0, This->numvertices * sizeof(*vertex_face_map)); 7086 if (!vertex_face_map) 7087 { 7088 hr = E_OUTOFMEMORY; 7089 ERR("Couldn't allocate memory for vertex_face_map.\n"); 7090 goto cleanup; 7091 } 7092 /* Build vertex face map, so that a vertex's face can be looked up. */ 7093 for (i = 0; i < This->numfaces; i++) 7094 { 7095 DWORD j; 7096 for (j = 0; j < 3; j++) 7097 { 7098 DWORD index = read_ib(indices, indices_are_32bit, 3*i + j); 7099 vertex_face_map[index] = i; 7100 } 7101 } 7102 7103 if (flags & D3DXWELDEPSILONS_WELDPARTIALMATCHES) 7104 { 7105 hr = mesh->lpVtbl->LockVertexBuffer(mesh, 0, (void**)&vertices); 7106 if (FAILED(hr)) 7107 { 7108 ERR("Couldn't lock vertex buffer.\n"); 7109 goto cleanup; 7110 } 7111 /* For each vertex that can be removed, compare its vertex components 7112 * with the vertex components from the vertex that can replace it. A 7113 * vertex is only fully replaced if all the components match and the 7114 * flag D3DXWELDEPSILONS_DONOTREMOVEVERTICES is not set, and they 7115 * belong to the same attribute group. Otherwise the vertex components 7116 * that are within epsilon are set to the same value. 7117 */ 7118 for (i = 0; i < 3 * This->numfaces; i++) 7119 { 7120 D3DVERTEXELEMENT9 *decl_ptr; 7121 DWORD vertex_size = mesh->lpVtbl->GetNumBytesPerVertex(mesh); 7122 DWORD num_vertex_components; 7123 INT matches = 0; 7124 BOOL all_match; 7125 DWORD index = read_ib(indices, indices_are_32bit, i); 7126 7127 for (decl_ptr = This->cached_declaration, num_vertex_components = 0; decl_ptr->Stream != 0xFF; decl_ptr++, num_vertex_components++) 7128 { 7129 BYTE *to = &vertices[vertex_size*index + decl_ptr->Offset]; 7130 BYTE *from = &vertices[vertex_size*point_reps[index] + decl_ptr->Offset]; 7131 FLOAT epsilon = get_component_epsilon(decl_ptr, epsilons); 7132 7133 /* Don't weld self */ 7134 if (index == point_reps[index]) 7135 { 7136 matches++; 7137 continue; 7138 } 7139 7140 if (weld_component(to, from, decl_ptr->Type, epsilon)) 7141 matches++; 7142 } 7143 7144 all_match = (num_vertex_components == matches); 7145 if (all_match && !(flags & D3DXWELDEPSILONS_DONOTREMOVEVERTICES)) 7146 { 7147 DWORD to_face = vertex_face_map[index]; 7148 DWORD from_face = vertex_face_map[point_reps[index]]; 7149 if(attributes[to_face] != attributes[from_face] && !(flags & D3DXWELDEPSILONS_DONOTSPLIT)) 7150 continue; 7151 write_ib(indices, indices_are_32bit, i, point_reps[index]); 7152 } 7153 } 7154 mesh->lpVtbl->UnlockVertexBuffer(mesh); 7155 vertices = NULL; 7156 } 7157 else if (flags & D3DXWELDEPSILONS_WELDALL) 7158 { 7159 for (i = 0; i < 3 * This->numfaces; i++) 7160 { 7161 DWORD index = read_ib(indices, indices_are_32bit, i); 7162 DWORD to_face = vertex_face_map[index]; 7163 DWORD from_face = vertex_face_map[point_reps[index]]; 7164 if(attributes[to_face] != attributes[from_face] && !(flags & D3DXWELDEPSILONS_DONOTSPLIT)) 7165 continue; 7166 write_ib(indices, indices_are_32bit, i, point_reps[index]); 7167 } 7168 } 7169 mesh->lpVtbl->UnlockAttributeBuffer(mesh); 7170 attributes = NULL; 7171 mesh->lpVtbl->UnlockIndexBuffer(mesh); 7172 indices = NULL; 7173 7174 /* Compact mesh using OptimizeInplace */ 7175 optimize_flags = D3DXMESHOPT_COMPACT; 7176 hr = mesh->lpVtbl->OptimizeInplace(mesh, optimize_flags, adjacency_ptr, adjacency_out, face_remap_out, vertex_remap_out); 7177 if (FAILED(hr)) 7178 { 7179 ERR("Couldn't compact mesh.\n"); 7180 goto cleanup; 7181 } 7182 7183 hr = D3D_OK; 7184 cleanup: 7185 HeapFree(GetProcessHeap(), 0, adjacency_generated); 7186 HeapFree(GetProcessHeap(), 0, point_reps); 7187 HeapFree(GetProcessHeap(), 0, vertex_face_map); 7188 if (attributes) mesh->lpVtbl->UnlockAttributeBuffer(mesh); 7189 if (indices) mesh->lpVtbl->UnlockIndexBuffer(mesh); 7190 if (vertices) mesh->lpVtbl->UnlockVertexBuffer(mesh); 7191 7192 return hr; 7193 } 7194 7195 /************************************************************************* 7196 * D3DXOptimizeFaces (D3DX9_36.@) 7197 * 7198 * Re-orders the faces so the vertex cache is used optimally. 7199 * 7200 * PARAMS 7201 * indices [I] Pointer to an index buffer belonging to a mesh. 7202 * num_faces [I] Number of faces in the mesh. 7203 * num_vertices [I] Number of vertices in the mesh. 7204 * indices_are_32bit [I] Specifies whether indices are 32- or 16-bit. 7205 * face_remap [I/O] The new order the faces should be drawn in. 7206 * 7207 * RETURNS 7208 * Success: D3D_OK. 7209 * Failure: D3DERR_INVALIDCALL. 7210 * 7211 * BUGS 7212 * The face re-ordering does not use the vertex cache optimally. 7213 * 7214 */ 7215 HRESULT WINAPI D3DXOptimizeFaces(const void *indices, UINT num_faces, 7216 UINT num_vertices, BOOL indices_are_32bit, DWORD *face_remap) 7217 { 7218 UINT i; 7219 UINT j = num_faces - 1; 7220 UINT limit_16_bit = 2 << 15; /* According to MSDN */ 7221 HRESULT hr = D3D_OK; 7222 7223 FIXME("indices %p, num_faces %u, num_vertices %u, indices_are_32bit %#x, face_remap %p semi-stub. " 7224 "Face order will not be optimal.\n", 7225 indices, num_faces, num_vertices, indices_are_32bit, face_remap); 7226 7227 if (!indices_are_32bit && num_faces >= limit_16_bit) 7228 { 7229 WARN("Number of faces must be less than %d when using 16-bit indices.\n", 7230 limit_16_bit); 7231 hr = D3DERR_INVALIDCALL; 7232 goto error; 7233 } 7234 7235 if (!face_remap) 7236 { 7237 WARN("Face remap pointer is NULL.\n"); 7238 hr = D3DERR_INVALIDCALL; 7239 goto error; 7240 } 7241 7242 /* The faces are drawn in reverse order for simple meshes. This ordering 7243 * is not optimal for complicated meshes, but will not break anything 7244 * either. The ordering should be changed to take advantage of the vertex 7245 * cache on the graphics card. 7246 * 7247 * TODO Re-order to take advantage of vertex cache. 7248 */ 7249 for (i = 0; i < num_faces; i++) 7250 { 7251 face_remap[i] = j--; 7252 } 7253 7254 return D3D_OK; 7255 7256 error: 7257 return hr; 7258 } 7259 7260 static D3DXVECTOR3 *vertex_element_vec3(BYTE *vertices, const D3DVERTEXELEMENT9 *declaration, 7261 DWORD vertex_stride, DWORD index) 7262 { 7263 return (D3DXVECTOR3 *)(vertices + declaration->Offset + index * vertex_stride); 7264 } 7265 7266 static D3DXVECTOR3 read_vec3(BYTE *vertices, const D3DVERTEXELEMENT9 *declaration, 7267 DWORD vertex_stride, DWORD index) 7268 { 7269 D3DXVECTOR3 vec3 = {0}; 7270 const D3DXVECTOR3 *src = vertex_element_vec3(vertices, declaration, vertex_stride, index); 7271 7272 switch (declaration->Type) 7273 { 7274 case D3DDECLTYPE_FLOAT1: 7275 vec3.x = src->x; 7276 break; 7277 case D3DDECLTYPE_FLOAT2: 7278 vec3.x = src->x; 7279 vec3.y = src->y; 7280 break; 7281 case D3DDECLTYPE_FLOAT3: 7282 case D3DDECLTYPE_FLOAT4: 7283 vec3 = *src; 7284 break; 7285 default: 7286 ERR("Cannot read vec3\n"); 7287 break; 7288 } 7289 7290 return vec3; 7291 } 7292 7293 /************************************************************************* 7294 * D3DXComputeTangentFrameEx (D3DX9_36.@) 7295 */ 7296 HRESULT WINAPI D3DXComputeTangentFrameEx(ID3DXMesh *mesh, DWORD texture_in_semantic, DWORD texture_in_index, 7297 DWORD u_partial_out_semantic, DWORD u_partial_out_index, DWORD v_partial_out_semantic, 7298 DWORD v_partial_out_index, DWORD normal_out_semantic, DWORD normal_out_index, DWORD options, 7299 const DWORD *adjacency, float partial_edge_threshold, float singular_point_threshold, 7300 float normal_edge_threshold, ID3DXMesh **mesh_out, ID3DXBuffer **vertex_mapping) 7301 { 7302 HRESULT hr; 7303 void *indices = NULL; 7304 BYTE *vertices = NULL; 7305 DWORD *point_reps = NULL; 7306 size_t normal_size; 7307 BOOL indices_are_32bit; 7308 DWORD i, j, num_faces, num_vertices, vertex_stride; 7309 D3DVERTEXELEMENT9 declaration[MAX_FVF_DECL_SIZE] = {D3DDECL_END()}; 7310 D3DVERTEXELEMENT9 *position_declaration = NULL, *normal_declaration = NULL; 7311 DWORD weighting_method = options & (D3DXTANGENT_WEIGHT_EQUAL | D3DXTANGENT_WEIGHT_BY_AREA); 7312 7313 TRACE("mesh %p, texture_in_semantic %u, texture_in_index %u, u_partial_out_semantic %u, u_partial_out_index %u, " 7314 "v_partial_out_semantic %u, v_partial_out_index %u, normal_out_semantic %u, normal_out_index %u, " 7315 "options %#x, adjacency %p, partial_edge_threshold %f, singular_point_threshold %f, " 7316 "normal_edge_threshold %f, mesh_out %p, vertex_mapping %p\n", 7317 mesh, texture_in_semantic, texture_in_index, u_partial_out_semantic, u_partial_out_index, 7318 v_partial_out_semantic, v_partial_out_index, normal_out_semantic, normal_out_index, options, adjacency, 7319 partial_edge_threshold, singular_point_threshold, normal_edge_threshold, mesh_out, vertex_mapping); 7320 7321 if (!mesh) 7322 { 7323 WARN("mesh is NULL\n"); 7324 return D3DERR_INVALIDCALL; 7325 } 7326 7327 if (weighting_method == (D3DXTANGENT_WEIGHT_EQUAL | D3DXTANGENT_WEIGHT_BY_AREA)) 7328 { 7329 WARN("D3DXTANGENT_WEIGHT_BY_AREA and D3DXTANGENT_WEIGHT_EQUAL are mutally exclusive\n"); 7330 return D3DERR_INVALIDCALL; 7331 } 7332 7333 if (u_partial_out_semantic != D3DX_DEFAULT) 7334 { 7335 FIXME("tangent vectors computation is not supported\n"); 7336 return E_NOTIMPL; 7337 } 7338 7339 if (v_partial_out_semantic != D3DX_DEFAULT) 7340 { 7341 FIXME("binormal vectors computation is not supported\n"); 7342 return E_NOTIMPL; 7343 } 7344 7345 if (options & ~(D3DXTANGENT_GENERATE_IN_PLACE | D3DXTANGENT_CALCULATE_NORMALS | D3DXTANGENT_WEIGHT_EQUAL | D3DXTANGENT_WEIGHT_BY_AREA)) 7346 { 7347 FIXME("unsupported options %#x\n", options); 7348 return E_NOTIMPL; 7349 } 7350 7351 if (!(options & D3DXTANGENT_CALCULATE_NORMALS)) 7352 { 7353 FIXME("only normals computation is supported\n"); 7354 return E_NOTIMPL; 7355 } 7356 7357 if (!(options & D3DXTANGENT_GENERATE_IN_PLACE) || mesh_out || vertex_mapping) 7358 { 7359 FIXME("only D3DXTANGENT_GENERATE_IN_PLACE is supported\n"); 7360 return E_NOTIMPL; 7361 } 7362 7363 if (FAILED(hr = mesh->lpVtbl->GetDeclaration(mesh, declaration))) 7364 return hr; 7365 7366 for (i = 0; declaration[i].Stream != 0xff; i++) 7367 { 7368 if (declaration[i].Usage == D3DDECLUSAGE_POSITION && !declaration[i].UsageIndex) 7369 position_declaration = &declaration[i]; 7370 if (declaration[i].Usage == normal_out_semantic && declaration[i].UsageIndex == normal_out_index) 7371 normal_declaration = &declaration[i]; 7372 } 7373 7374 if (!position_declaration || !normal_declaration) 7375 return D3DERR_INVALIDCALL; 7376 7377 if (normal_declaration->Type == D3DDECLTYPE_FLOAT3) 7378 { 7379 normal_size = sizeof(D3DXVECTOR3); 7380 } 7381 else if (normal_declaration->Type == D3DDECLTYPE_FLOAT4) 7382 { 7383 normal_size = sizeof(D3DXVECTOR4); 7384 } 7385 else 7386 { 7387 WARN("unsupported normals type %u\n", normal_declaration->Type); 7388 return D3DERR_INVALIDCALL; 7389 } 7390 7391 num_faces = mesh->lpVtbl->GetNumFaces(mesh); 7392 num_vertices = mesh->lpVtbl->GetNumVertices(mesh); 7393 vertex_stride = mesh->lpVtbl->GetNumBytesPerVertex(mesh); 7394 indices_are_32bit = mesh->lpVtbl->GetOptions(mesh) & D3DXMESH_32BIT; 7395 7396 point_reps = HeapAlloc(GetProcessHeap(), 0, num_vertices * sizeof(*point_reps)); 7397 if (!point_reps) 7398 { 7399 hr = E_OUTOFMEMORY; 7400 goto done; 7401 } 7402 7403 if (adjacency) 7404 { 7405 if (FAILED(hr = mesh->lpVtbl->ConvertAdjacencyToPointReps(mesh, adjacency, point_reps))) 7406 goto done; 7407 } 7408 else 7409 { 7410 for (i = 0; i < num_vertices; i++) 7411 point_reps[i] = i; 7412 } 7413 7414 if (FAILED(hr = mesh->lpVtbl->LockIndexBuffer(mesh, 0, &indices))) 7415 goto done; 7416 7417 if (FAILED(hr = mesh->lpVtbl->LockVertexBuffer(mesh, 0, (void **)&vertices))) 7418 goto done; 7419 7420 for (i = 0; i < num_vertices; i++) 7421 { 7422 static const D3DXVECTOR4 default_vector = {0.0f, 0.0f, 0.0f, 1.0f}; 7423 void *normal = vertices + normal_declaration->Offset + i * vertex_stride; 7424 7425 memcpy(normal, &default_vector, normal_size); 7426 } 7427 7428 for (i = 0; i < num_faces; i++) 7429 { 7430 float denominator, weights[3]; 7431 D3DXVECTOR3 a, b, cross, face_normal; 7432 const DWORD face_indices[3] = 7433 { 7434 read_ib(indices, indices_are_32bit, 3 * i + 0), 7435 read_ib(indices, indices_are_32bit, 3 * i + 1), 7436 read_ib(indices, indices_are_32bit, 3 * i + 2) 7437 }; 7438 const D3DXVECTOR3 v0 = read_vec3(vertices, position_declaration, vertex_stride, face_indices[0]); 7439 const D3DXVECTOR3 v1 = read_vec3(vertices, position_declaration, vertex_stride, face_indices[1]); 7440 const D3DXVECTOR3 v2 = read_vec3(vertices, position_declaration, vertex_stride, face_indices[2]); 7441 7442 D3DXVec3Cross(&cross, D3DXVec3Subtract(&a, &v0, &v1), D3DXVec3Subtract(&b, &v0, &v2)); 7443 7444 switch (weighting_method) 7445 { 7446 case D3DXTANGENT_WEIGHT_EQUAL: 7447 weights[0] = weights[1] = weights[2] = 1.0f; 7448 break; 7449 case D3DXTANGENT_WEIGHT_BY_AREA: 7450 weights[0] = weights[1] = weights[2] = D3DXVec3Length(&cross); 7451 break; 7452 default: 7453 /* weight by angle */ 7454 denominator = D3DXVec3Length(&a) * D3DXVec3Length(&b); 7455 if (!denominator) 7456 weights[0] = 0.0f; 7457 else 7458 weights[0] = acosf(D3DXVec3Dot(&a, &b) / denominator); 7459 7460 D3DXVec3Subtract(&a, &v1, &v0); 7461 D3DXVec3Subtract(&b, &v1, &v2); 7462 denominator = D3DXVec3Length(&a) * D3DXVec3Length(&b); 7463 if (!denominator) 7464 weights[1] = 0.0f; 7465 else 7466 weights[1] = acosf(D3DXVec3Dot(&a, &b) / denominator); 7467 7468 D3DXVec3Subtract(&a, &v2, &v0); 7469 D3DXVec3Subtract(&b, &v2, &v1); 7470 denominator = D3DXVec3Length(&a) * D3DXVec3Length(&b); 7471 if (!denominator) 7472 weights[2] = 0.0f; 7473 else 7474 weights[2] = acosf(D3DXVec3Dot(&a, &b) / denominator); 7475 7476 break; 7477 } 7478 7479 D3DXVec3Normalize(&face_normal, &cross); 7480 7481 for (j = 0; j < 3; j++) 7482 { 7483 D3DXVECTOR3 normal; 7484 DWORD rep_index = point_reps[face_indices[j]]; 7485 D3DXVECTOR3 *rep_normal = vertex_element_vec3(vertices, normal_declaration, vertex_stride, rep_index); 7486 7487 D3DXVec3Scale(&normal, &face_normal, weights[j]); 7488 D3DXVec3Add(rep_normal, rep_normal, &normal); 7489 } 7490 } 7491 7492 for (i = 0; i < num_vertices; i++) 7493 { 7494 DWORD rep_index = point_reps[i]; 7495 D3DXVECTOR3 *normal = vertex_element_vec3(vertices, normal_declaration, vertex_stride, i); 7496 D3DXVECTOR3 *rep_normal = vertex_element_vec3(vertices, normal_declaration, vertex_stride, rep_index); 7497 7498 if (i == rep_index) 7499 D3DXVec3Normalize(rep_normal, rep_normal); 7500 else 7501 *normal = *rep_normal; 7502 } 7503 7504 hr = D3D_OK; 7505 7506 done: 7507 if (vertices) 7508 mesh->lpVtbl->UnlockVertexBuffer(mesh); 7509 7510 if (indices) 7511 mesh->lpVtbl->UnlockIndexBuffer(mesh); 7512 7513 HeapFree(GetProcessHeap(), 0, point_reps); 7514 7515 return hr; 7516 } 7517 7518 /************************************************************************* 7519 * D3DXComputeNormals (D3DX9_36.@) 7520 */ 7521 HRESULT WINAPI D3DXComputeNormals(struct ID3DXBaseMesh *mesh, const DWORD *adjacency) 7522 { 7523 TRACE("mesh %p, adjacency %p\n", mesh, adjacency); 7524 7525 if (mesh && (ID3DXMeshVtbl *)mesh->lpVtbl != &D3DXMesh_Vtbl) 7526 { 7527 ERR("Invalid virtual table\n"); 7528 return D3DERR_INVALIDCALL; 7529 } 7530 7531 return D3DXComputeTangentFrameEx((ID3DXMesh *)mesh, D3DX_DEFAULT, 0, 7532 D3DX_DEFAULT, 0, D3DX_DEFAULT, 0, D3DDECLUSAGE_NORMAL, 0, 7533 D3DXTANGENT_GENERATE_IN_PLACE | D3DXTANGENT_CALCULATE_NORMALS, 7534 adjacency, -1.01f, -0.01f, -1.01f, NULL, NULL); 7535 } 7536 7537 /************************************************************************* 7538 * D3DXIntersect (D3DX9_36.@) 7539 */ 7540 HRESULT WINAPI D3DXIntersect(ID3DXBaseMesh *mesh, const D3DXVECTOR3 *ray_pos, const D3DXVECTOR3 *ray_dir, 7541 BOOL *hit, DWORD *face_index, float *u, float *v, float *distance, ID3DXBuffer **all_hits, DWORD *count_of_hits) 7542 { 7543 FIXME("mesh %p, ray_pos %p, ray_dir %p, hit %p, face_index %p, u %p, v %p, distance %p, all_hits %p, " 7544 "count_of_hits %p stub!\n", mesh, ray_pos, ray_dir, hit, face_index, u, v, distance, all_hits, count_of_hits); 7545 7546 return E_NOTIMPL; 7547 } 7548 7549 HRESULT WINAPI D3DXTessellateNPatches(ID3DXMesh *mesh, const DWORD *adjacency_in, float num_segs, 7550 BOOL quadratic_normals, ID3DXMesh **mesh_out, ID3DXBuffer **adjacency_out) 7551 { 7552 FIXME("mesh %p, adjacency_in %p, num_segs %f, quadratic_normals %d, mesh_out %p, adjacency_out %p stub.\n", 7553 mesh, adjacency_in, num_segs, quadratic_normals, mesh_out, adjacency_out); 7554 7555 return E_NOTIMPL; 7556 } 7557 7558 HRESULT WINAPI D3DXConvertMeshSubsetToSingleStrip(struct ID3DXBaseMesh *mesh_in, DWORD attribute_id, 7559 DWORD ib_flags, struct IDirect3DIndexBuffer9 **index_buffer, DWORD *index_count) 7560 { 7561 FIXME("mesh_in %p, attribute_id %u, ib_flags %u, index_buffer %p, index_count %p stub.\n", 7562 mesh_in, attribute_id, ib_flags, index_buffer, index_count); 7563 7564 return E_NOTIMPL; 7565 } 7566 7567 struct frame_node 7568 { 7569 struct list entry; 7570 D3DXFRAME *frame; 7571 }; 7572 7573 static BOOL queue_frame_node(struct list *queue, D3DXFRAME *frame) 7574 { 7575 struct frame_node *node; 7576 7577 if (!frame->pFrameFirstChild) 7578 return TRUE; 7579 7580 node = HeapAlloc(GetProcessHeap(), 0, sizeof(*node)); 7581 if (!node) 7582 return FALSE; 7583 7584 node->frame = frame; 7585 list_add_tail(queue, &node->entry); 7586 7587 return TRUE; 7588 } 7589 7590 static void empty_frame_queue(struct list *queue) 7591 { 7592 struct frame_node *cur, *cur2; 7593 LIST_FOR_EACH_ENTRY_SAFE(cur, cur2, queue, struct frame_node, entry) 7594 { 7595 list_remove(&cur->entry); 7596 HeapFree(GetProcessHeap(), 0, cur); 7597 } 7598 } 7599 7600 D3DXFRAME * WINAPI D3DXFrameFind(const D3DXFRAME *root, const char *name) 7601 { 7602 D3DXFRAME *found = NULL, *frame; 7603 struct list queue; 7604 7605 TRACE("root frame %p, name %s.\n", root, debugstr_a(name)); 7606 7607 if (!root) 7608 return NULL; 7609 7610 list_init(&queue); 7611 7612 frame = (D3DXFRAME *)root; 7613 7614 for (;;) 7615 { 7616 struct frame_node *node; 7617 7618 while (frame) 7619 { 7620 if ((name && frame->Name && !strcmp(frame->Name, name)) || (!name && !frame->Name)) 7621 { 7622 found = frame; 7623 goto cleanup; 7624 } 7625 7626 if (!queue_frame_node(&queue, frame)) 7627 goto cleanup; 7628 7629 frame = frame->pFrameSibling; 7630 } 7631 7632 if (list_empty(&queue)) 7633 break; 7634 7635 node = LIST_ENTRY(list_head(&queue), struct frame_node, entry); 7636 list_remove(&node->entry); 7637 frame = node->frame->pFrameFirstChild; 7638 HeapFree(GetProcessHeap(), 0, node); 7639 } 7640 7641 cleanup: 7642 empty_frame_queue(&queue); 7643 7644 return found; 7645 } 7646