1 /* 2 * Mesh operations specific to D3DX9. 3 * 4 * Copyright (C) 2005 Henri Verbeet 5 * Copyright (C) 2006 Ivan Gyurdiev 6 * Copyright (C) 2009 David Adam 7 * Copyright (C) 2010 Tony Wasserka 8 * Copyright (C) 2011 Dylan Smith 9 * Copyright (C) 2011 Michael Mc Donnell 10 * Copyright (C) 2013 Christian Costa 11 * 12 * This library is free software; you can redistribute it and/or 13 * modify it under the terms of the GNU Lesser General Public 14 * License as published by the Free Software Foundation; either 15 * version 2.1 of the License, or (at your option) any later version. 16 * 17 * This library is distributed in the hope that it will be useful, 18 * but WITHOUT ANY WARRANTY; without even the implied warranty of 19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 20 * Lesser General Public License for more details. 21 * 22 * You should have received a copy of the GNU Lesser General Public 23 * License along with this library; if not, write to the Free Software 24 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA 25 */ 26 27 #include "config.h" 28 #include "wine/port.h" 29 30 #include <assert.h> 31 #ifdef HAVE_FLOAT_H 32 # include <float.h> 33 #endif 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/unicode.h" 41 #include "wine/list.h" 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 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 attrib_entry_compare(const DWORD **a, const DWORD **b) 1594 { 1595 const DWORD *ptr_a = *a; 1596 const DWORD *ptr_b = *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), 1627 (int(*)(const void *, const void *))attrib_entry_compare); 1628 1629 for (i = 0; i < This->numfaces; i++) 1630 { 1631 DWORD old_face = sorted_attrib_ptr_buffer[i] - attrib_buffer; 1632 (*face_remap)[old_face] = i; 1633 } 1634 1635 /* overwrite sorted_attrib_ptr_buffer with the values themselves */ 1636 *sorted_attrib_buffer = (DWORD*)sorted_attrib_ptr_buffer; 1637 for (i = 0; i < This->numfaces; i++) 1638 (*sorted_attrib_buffer)[(*face_remap)[i]] = attrib_buffer[i]; 1639 1640 return D3D_OK; 1641 } 1642 1643 static HRESULT WINAPI d3dx9_mesh_OptimizeInplace(ID3DXMesh *iface, DWORD flags, const DWORD *adjacency_in, 1644 DWORD *adjacency_out, DWORD *face_remap_out, ID3DXBuffer **vertex_remap_out) 1645 { 1646 struct d3dx9_mesh *This = impl_from_ID3DXMesh(iface); 1647 void *indices = NULL; 1648 DWORD *attrib_buffer = NULL; 1649 HRESULT hr; 1650 ID3DXBuffer *vertex_remap = NULL; 1651 DWORD *face_remap = NULL; /* old -> new mapping */ 1652 DWORD *dword_indices = NULL; 1653 DWORD new_num_vertices = 0; 1654 DWORD new_num_alloc_vertices = 0; 1655 IDirect3DVertexBuffer9 *vertex_buffer = NULL; 1656 DWORD *sorted_attrib_buffer = NULL; 1657 DWORD i; 1658 1659 TRACE("iface %p, flags %#x, adjacency_in %p, adjacency_out %p, face_remap_out %p, vertex_remap_out %p.\n", 1660 iface, flags, adjacency_in, adjacency_out, face_remap_out, vertex_remap_out); 1661 1662 if (!flags) 1663 return D3DERR_INVALIDCALL; 1664 if (!adjacency_in && (flags & (D3DXMESHOPT_VERTEXCACHE | D3DXMESHOPT_STRIPREORDER))) 1665 return D3DERR_INVALIDCALL; 1666 if ((flags & (D3DXMESHOPT_VERTEXCACHE | D3DXMESHOPT_STRIPREORDER)) == (D3DXMESHOPT_VERTEXCACHE | D3DXMESHOPT_STRIPREORDER)) 1667 return D3DERR_INVALIDCALL; 1668 1669 if (flags & (D3DXMESHOPT_VERTEXCACHE | D3DXMESHOPT_STRIPREORDER)) 1670 { 1671 if (flags & D3DXMESHOPT_VERTEXCACHE) 1672 FIXME("D3DXMESHOPT_VERTEXCACHE not implemented.\n"); 1673 if (flags & D3DXMESHOPT_STRIPREORDER) 1674 FIXME("D3DXMESHOPT_STRIPREORDER not implemented.\n"); 1675 return E_NOTIMPL; 1676 } 1677 1678 hr = iface->lpVtbl->LockIndexBuffer(iface, 0, &indices); 1679 if (FAILED(hr)) goto cleanup; 1680 1681 dword_indices = HeapAlloc(GetProcessHeap(), 0, This->numfaces * 3 * sizeof(DWORD)); 1682 if (!dword_indices) return E_OUTOFMEMORY; 1683 if (This->options & D3DXMESH_32BIT) { 1684 memcpy(dword_indices, indices, This->numfaces * 3 * sizeof(DWORD)); 1685 } else { 1686 WORD *word_indices = indices; 1687 for (i = 0; i < This->numfaces * 3; i++) 1688 dword_indices[i] = *word_indices++; 1689 } 1690 1691 if ((flags & (D3DXMESHOPT_COMPACT | D3DXMESHOPT_IGNOREVERTS | D3DXMESHOPT_ATTRSORT)) == D3DXMESHOPT_COMPACT) 1692 { 1693 new_num_alloc_vertices = This->numvertices; 1694 hr = compact_mesh(This, dword_indices, &new_num_vertices, &vertex_remap); 1695 if (FAILED(hr)) goto cleanup; 1696 } else if (flags & D3DXMESHOPT_ATTRSORT) { 1697 if (!(flags & D3DXMESHOPT_IGNOREVERTS)) 1698 FIXME("D3DXMESHOPT_ATTRSORT vertex reordering not implemented.\n"); 1699 1700 hr = iface->lpVtbl->LockAttributeBuffer(iface, 0, &attrib_buffer); 1701 if (FAILED(hr)) goto cleanup; 1702 1703 hr = remap_faces_for_attrsort(This, dword_indices, attrib_buffer, &sorted_attrib_buffer, &face_remap); 1704 if (FAILED(hr)) goto cleanup; 1705 } 1706 1707 if (vertex_remap) 1708 { 1709 /* reorder the vertices using vertex_remap */ 1710 D3DVERTEXBUFFER_DESC vertex_desc; 1711 DWORD *vertex_remap_ptr = ID3DXBuffer_GetBufferPointer(vertex_remap); 1712 DWORD vertex_size = iface->lpVtbl->GetNumBytesPerVertex(iface); 1713 BYTE *orig_vertices; 1714 BYTE *new_vertices; 1715 1716 hr = IDirect3DVertexBuffer9_GetDesc(This->vertex_buffer, &vertex_desc); 1717 if (FAILED(hr)) goto cleanup; 1718 1719 hr = IDirect3DDevice9_CreateVertexBuffer(This->device, new_num_alloc_vertices * vertex_size, 1720 vertex_desc.Usage, This->fvf, vertex_desc.Pool, &vertex_buffer, NULL); 1721 if (FAILED(hr)) goto cleanup; 1722 1723 hr = IDirect3DVertexBuffer9_Lock(This->vertex_buffer, 0, 0, (void**)&orig_vertices, D3DLOCK_READONLY); 1724 if (FAILED(hr)) goto cleanup; 1725 1726 hr = IDirect3DVertexBuffer9_Lock(vertex_buffer, 0, 0, (void**)&new_vertices, 0); 1727 if (FAILED(hr)) { 1728 IDirect3DVertexBuffer9_Unlock(This->vertex_buffer); 1729 goto cleanup; 1730 } 1731 1732 for (i = 0; i < new_num_vertices; i++) 1733 memcpy(new_vertices + i * vertex_size, orig_vertices + vertex_remap_ptr[i] * vertex_size, vertex_size); 1734 1735 IDirect3DVertexBuffer9_Unlock(This->vertex_buffer); 1736 IDirect3DVertexBuffer9_Unlock(vertex_buffer); 1737 } else if (vertex_remap_out) { 1738 DWORD *vertex_remap_ptr; 1739 1740 hr = D3DXCreateBuffer(This->numvertices * sizeof(DWORD), &vertex_remap); 1741 if (FAILED(hr)) goto cleanup; 1742 vertex_remap_ptr = ID3DXBuffer_GetBufferPointer(vertex_remap); 1743 for (i = 0; i < This->numvertices; i++) 1744 *vertex_remap_ptr++ = i; 1745 } 1746 1747 if (flags & D3DXMESHOPT_ATTRSORT) 1748 { 1749 D3DXATTRIBUTERANGE *attrib_table; 1750 DWORD attrib_table_size; 1751 1752 attrib_table_size = count_attributes(sorted_attrib_buffer, This->numfaces); 1753 attrib_table = HeapAlloc(GetProcessHeap(), 0, attrib_table_size * sizeof(*attrib_table)); 1754 if (!attrib_table) { 1755 hr = E_OUTOFMEMORY; 1756 goto cleanup; 1757 } 1758 1759 memcpy(attrib_buffer, sorted_attrib_buffer, This->numfaces * sizeof(*attrib_buffer)); 1760 1761 /* reorder the indices using face_remap */ 1762 if (This->options & D3DXMESH_32BIT) { 1763 for (i = 0; i < This->numfaces; i++) 1764 memcpy((DWORD*)indices + face_remap[i] * 3, dword_indices + i * 3, 3 * sizeof(DWORD)); 1765 } else { 1766 WORD *word_indices = indices; 1767 for (i = 0; i < This->numfaces; i++) { 1768 DWORD new_pos = face_remap[i] * 3; 1769 DWORD old_pos = i * 3; 1770 word_indices[new_pos++] = dword_indices[old_pos++]; 1771 word_indices[new_pos++] = dword_indices[old_pos++]; 1772 word_indices[new_pos] = dword_indices[old_pos]; 1773 } 1774 } 1775 1776 fill_attribute_table(attrib_buffer, This->numfaces, indices, 1777 This->options & D3DXMESH_32BIT, attrib_table); 1778 1779 HeapFree(GetProcessHeap(), 0, This->attrib_table); 1780 This->attrib_table = attrib_table; 1781 This->attrib_table_size = attrib_table_size; 1782 } else { 1783 if (This->options & D3DXMESH_32BIT) { 1784 memcpy(indices, dword_indices, This->numfaces * 3 * sizeof(DWORD)); 1785 } else { 1786 WORD *word_indices = indices; 1787 for (i = 0; i < This->numfaces * 3; i++) 1788 *word_indices++ = dword_indices[i]; 1789 } 1790 } 1791 1792 if (adjacency_out) { 1793 if (face_remap) { 1794 for (i = 0; i < This->numfaces; i++) { 1795 DWORD old_pos = i * 3; 1796 DWORD new_pos = face_remap[i] * 3; 1797 adjacency_out[new_pos++] = face_remap[adjacency_in[old_pos++]]; 1798 adjacency_out[new_pos++] = face_remap[adjacency_in[old_pos++]]; 1799 adjacency_out[new_pos++] = face_remap[adjacency_in[old_pos++]]; 1800 } 1801 } else { 1802 memcpy(adjacency_out, adjacency_in, This->numfaces * 3 * sizeof(*adjacency_out)); 1803 } 1804 } 1805 if (face_remap_out) { 1806 if (face_remap) { 1807 for (i = 0; i < This->numfaces; i++) 1808 face_remap_out[face_remap[i]] = i; 1809 } else { 1810 for (i = 0; i < This->numfaces; i++) 1811 face_remap_out[i] = i; 1812 } 1813 } 1814 if (vertex_remap_out) 1815 *vertex_remap_out = vertex_remap; 1816 vertex_remap = NULL; 1817 1818 if (vertex_buffer) { 1819 IDirect3DVertexBuffer9_Release(This->vertex_buffer); 1820 This->vertex_buffer = vertex_buffer; 1821 vertex_buffer = NULL; 1822 This->numvertices = new_num_vertices; 1823 } 1824 1825 hr = D3D_OK; 1826 cleanup: 1827 HeapFree(GetProcessHeap(), 0, sorted_attrib_buffer); 1828 HeapFree(GetProcessHeap(), 0, face_remap); 1829 HeapFree(GetProcessHeap(), 0, dword_indices); 1830 if (vertex_remap) ID3DXBuffer_Release(vertex_remap); 1831 if (vertex_buffer) IDirect3DVertexBuffer9_Release(vertex_buffer); 1832 if (attrib_buffer) iface->lpVtbl->UnlockAttributeBuffer(iface); 1833 if (indices) iface->lpVtbl->UnlockIndexBuffer(iface); 1834 return hr; 1835 } 1836 1837 static HRESULT WINAPI d3dx9_mesh_SetAttributeTable(ID3DXMesh *iface, 1838 const D3DXATTRIBUTERANGE *attrib_table, DWORD attrib_table_size) 1839 { 1840 struct d3dx9_mesh *mesh = impl_from_ID3DXMesh(iface); 1841 D3DXATTRIBUTERANGE *new_table = NULL; 1842 1843 TRACE("iface %p, attrib_table %p, attrib_table_size %u.\n", iface, attrib_table, attrib_table_size); 1844 1845 if (attrib_table_size) { 1846 size_t size = attrib_table_size * sizeof(*attrib_table); 1847 1848 new_table = HeapAlloc(GetProcessHeap(), 0, size); 1849 if (!new_table) 1850 return E_OUTOFMEMORY; 1851 1852 CopyMemory(new_table, attrib_table, size); 1853 } else if (attrib_table) { 1854 return D3DERR_INVALIDCALL; 1855 } 1856 HeapFree(GetProcessHeap(), 0, mesh->attrib_table); 1857 mesh->attrib_table = new_table; 1858 mesh->attrib_table_size = attrib_table_size; 1859 1860 return D3D_OK; 1861 } 1862 1863 static const struct ID3DXMeshVtbl D3DXMesh_Vtbl = 1864 { 1865 d3dx9_mesh_QueryInterface, 1866 d3dx9_mesh_AddRef, 1867 d3dx9_mesh_Release, 1868 d3dx9_mesh_DrawSubset, 1869 d3dx9_mesh_GetNumFaces, 1870 d3dx9_mesh_GetNumVertices, 1871 d3dx9_mesh_GetFVF, 1872 d3dx9_mesh_GetDeclaration, 1873 d3dx9_mesh_GetNumBytesPerVertex, 1874 d3dx9_mesh_GetOptions, 1875 d3dx9_mesh_GetDevice, 1876 d3dx9_mesh_CloneMeshFVF, 1877 d3dx9_mesh_CloneMesh, 1878 d3dx9_mesh_GetVertexBuffer, 1879 d3dx9_mesh_GetIndexBuffer, 1880 d3dx9_mesh_LockVertexBuffer, 1881 d3dx9_mesh_UnlockVertexBuffer, 1882 d3dx9_mesh_LockIndexBuffer, 1883 d3dx9_mesh_UnlockIndexBuffer, 1884 d3dx9_mesh_GetAttributeTable, 1885 d3dx9_mesh_ConvertPointRepsToAdjacency, 1886 d3dx9_mesh_ConvertAdjacencyToPointReps, 1887 d3dx9_mesh_GenerateAdjacency, 1888 d3dx9_mesh_UpdateSemantics, 1889 d3dx9_mesh_LockAttributeBuffer, 1890 d3dx9_mesh_UnlockAttributeBuffer, 1891 d3dx9_mesh_Optimize, 1892 d3dx9_mesh_OptimizeInplace, 1893 d3dx9_mesh_SetAttributeTable, 1894 }; 1895 1896 1897 /* Algorithm taken from the article: An Efficient and Robust Ray-Box Intersection Algorithm 1898 Amy Williams University of Utah 1899 Steve Barrus University of Utah 1900 R. Keith Morley University of Utah 1901 Peter Shirley University of Utah 1902 1903 International Conference on Computer Graphics and Interactive Techniques archive 1904 ACM SIGGRAPH 2005 Courses 1905 Los Angeles, California 1906 1907 This algorithm is free of patents or of copyrights, as confirmed by Peter Shirley himself. 1908 1909 Algorithm: Consider the box as the intersection of three slabs. Clip the ray 1910 against each slab, if there's anything left of the ray after we're 1911 done we've got an intersection of the ray with the box. */ 1912 BOOL WINAPI D3DXBoxBoundProbe(const D3DXVECTOR3 *pmin, const D3DXVECTOR3 *pmax, 1913 const D3DXVECTOR3 *prayposition, const D3DXVECTOR3 *praydirection) 1914 { 1915 FLOAT div, tmin, tmax, tymin, tymax, tzmin, tzmax; 1916 1917 div = 1.0f / praydirection->x; 1918 if ( div >= 0.0f ) 1919 { 1920 tmin = ( pmin->x - prayposition->x ) * div; 1921 tmax = ( pmax->x - prayposition->x ) * div; 1922 } 1923 else 1924 { 1925 tmin = ( pmax->x - prayposition->x ) * div; 1926 tmax = ( pmin->x - prayposition->x ) * div; 1927 } 1928 1929 if ( tmax < 0.0f ) return FALSE; 1930 1931 div = 1.0f / praydirection->y; 1932 if ( div >= 0.0f ) 1933 { 1934 tymin = ( pmin->y - prayposition->y ) * div; 1935 tymax = ( pmax->y - prayposition->y ) * div; 1936 } 1937 else 1938 { 1939 tymin = ( pmax->y - prayposition->y ) * div; 1940 tymax = ( pmin->y - prayposition->y ) * div; 1941 } 1942 1943 if ( ( tymax < 0.0f ) || ( tmin > tymax ) || ( tymin > tmax ) ) return FALSE; 1944 1945 if ( tymin > tmin ) tmin = tymin; 1946 if ( tymax < tmax ) tmax = tymax; 1947 1948 div = 1.0f / praydirection->z; 1949 if ( div >= 0.0f ) 1950 { 1951 tzmin = ( pmin->z - prayposition->z ) * div; 1952 tzmax = ( pmax->z - prayposition->z ) * div; 1953 } 1954 else 1955 { 1956 tzmin = ( pmax->z - prayposition->z ) * div; 1957 tzmax = ( pmin->z - prayposition->z ) * div; 1958 } 1959 1960 if ( (tzmax < 0.0f ) || ( tmin > tzmax ) || ( tzmin > tmax ) ) return FALSE; 1961 1962 return TRUE; 1963 } 1964 1965 HRESULT WINAPI D3DXComputeBoundingBox(const D3DXVECTOR3 *pfirstposition, 1966 DWORD numvertices, DWORD dwstride, D3DXVECTOR3 *pmin, D3DXVECTOR3 *pmax) 1967 { 1968 D3DXVECTOR3 vec; 1969 unsigned int i; 1970 1971 if( !pfirstposition || !pmin || !pmax ) return D3DERR_INVALIDCALL; 1972 1973 *pmin = *pfirstposition; 1974 *pmax = *pmin; 1975 1976 for(i=0; i<numvertices; i++) 1977 { 1978 vec = *( (const D3DXVECTOR3*)((const char*)pfirstposition + dwstride * i) ); 1979 1980 if ( vec.x < pmin->x ) pmin->x = vec.x; 1981 if ( vec.x > pmax->x ) pmax->x = vec.x; 1982 1983 if ( vec.y < pmin->y ) pmin->y = vec.y; 1984 if ( vec.y > pmax->y ) pmax->y = vec.y; 1985 1986 if ( vec.z < pmin->z ) pmin->z = vec.z; 1987 if ( vec.z > pmax->z ) pmax->z = vec.z; 1988 } 1989 1990 return D3D_OK; 1991 } 1992 1993 HRESULT WINAPI D3DXComputeBoundingSphere(const D3DXVECTOR3 *pfirstposition, 1994 DWORD numvertices, DWORD dwstride, D3DXVECTOR3 *pcenter, float *pradius) 1995 { 1996 D3DXVECTOR3 temp; 1997 FLOAT d; 1998 unsigned int i; 1999 2000 if( !pfirstposition || !pcenter || !pradius ) return D3DERR_INVALIDCALL; 2001 2002 temp.x = 0.0f; 2003 temp.y = 0.0f; 2004 temp.z = 0.0f; 2005 *pradius = 0.0f; 2006 2007 for(i=0; i<numvertices; i++) 2008 D3DXVec3Add(&temp, &temp, (const D3DXVECTOR3*)((const char*)pfirstposition + dwstride * i)); 2009 2010 D3DXVec3Scale(pcenter, &temp, 1.0f / numvertices); 2011 2012 for(i=0; i<numvertices; i++) 2013 { 2014 d = D3DXVec3Length(D3DXVec3Subtract(&temp, (const D3DXVECTOR3*)((const char*)pfirstposition + dwstride * i), pcenter)); 2015 if ( d > *pradius ) *pradius = d; 2016 } 2017 return D3D_OK; 2018 } 2019 2020 static void append_decl_element(D3DVERTEXELEMENT9 *declaration, UINT *idx, UINT *offset, 2021 D3DDECLTYPE type, D3DDECLUSAGE usage, UINT usage_idx) 2022 { 2023 declaration[*idx].Stream = 0; 2024 declaration[*idx].Offset = *offset; 2025 declaration[*idx].Type = type; 2026 declaration[*idx].Method = D3DDECLMETHOD_DEFAULT; 2027 declaration[*idx].Usage = usage; 2028 declaration[*idx].UsageIndex = usage_idx; 2029 2030 *offset += d3dx_decltype_size[type]; 2031 ++(*idx); 2032 } 2033 2034 /************************************************************************* 2035 * D3DXDeclaratorFromFVF 2036 */ 2037 HRESULT WINAPI D3DXDeclaratorFromFVF(DWORD fvf, D3DVERTEXELEMENT9 declaration[MAX_FVF_DECL_SIZE]) 2038 { 2039 static const D3DVERTEXELEMENT9 end_element = D3DDECL_END(); 2040 DWORD tex_count = (fvf & D3DFVF_TEXCOUNT_MASK) >> D3DFVF_TEXCOUNT_SHIFT; 2041 unsigned int offset = 0; 2042 unsigned int idx = 0; 2043 unsigned int i; 2044 2045 TRACE("fvf %#x, declaration %p.\n", fvf, declaration); 2046 2047 if (fvf & (D3DFVF_RESERVED0 | D3DFVF_RESERVED2)) return D3DERR_INVALIDCALL; 2048 2049 if (fvf & D3DFVF_POSITION_MASK) 2050 { 2051 BOOL has_blend = (fvf & D3DFVF_XYZB5) >= D3DFVF_XYZB1; 2052 DWORD blend_count = 1 + (((fvf & D3DFVF_XYZB5) - D3DFVF_XYZB1) >> 1); 2053 BOOL has_blend_idx = (fvf & D3DFVF_LASTBETA_D3DCOLOR) || (fvf & D3DFVF_LASTBETA_UBYTE4); 2054 2055 if (has_blend_idx) --blend_count; 2056 2057 if ((fvf & D3DFVF_POSITION_MASK) == D3DFVF_XYZW 2058 || (has_blend && blend_count > 4)) 2059 return D3DERR_INVALIDCALL; 2060 2061 if ((fvf & D3DFVF_POSITION_MASK) == D3DFVF_XYZRHW) 2062 append_decl_element(declaration, &idx, &offset, D3DDECLTYPE_FLOAT4, D3DDECLUSAGE_POSITIONT, 0); 2063 else 2064 append_decl_element(declaration, &idx, &offset, D3DDECLTYPE_FLOAT3, D3DDECLUSAGE_POSITION, 0); 2065 2066 if (has_blend) 2067 { 2068 switch (blend_count) 2069 { 2070 case 0: 2071 break; 2072 case 1: 2073 append_decl_element(declaration, &idx, &offset, D3DDECLTYPE_FLOAT1, D3DDECLUSAGE_BLENDWEIGHT, 0); 2074 break; 2075 case 2: 2076 append_decl_element(declaration, &idx, &offset, D3DDECLTYPE_FLOAT2, D3DDECLUSAGE_BLENDWEIGHT, 0); 2077 break; 2078 case 3: 2079 append_decl_element(declaration, &idx, &offset, D3DDECLTYPE_FLOAT3, D3DDECLUSAGE_BLENDWEIGHT, 0); 2080 break; 2081 case 4: 2082 append_decl_element(declaration, &idx, &offset, D3DDECLTYPE_FLOAT4, D3DDECLUSAGE_BLENDWEIGHT, 0); 2083 break; 2084 default: 2085 ERR("Invalid blend count %u.\n", blend_count); 2086 break; 2087 } 2088 2089 if (has_blend_idx) 2090 { 2091 if (fvf & D3DFVF_LASTBETA_UBYTE4) 2092 append_decl_element(declaration, &idx, &offset, D3DDECLTYPE_UBYTE4, D3DDECLUSAGE_BLENDINDICES, 0); 2093 else if (fvf & D3DFVF_LASTBETA_D3DCOLOR) 2094 append_decl_element(declaration, &idx, &offset, D3DDECLTYPE_D3DCOLOR, D3DDECLUSAGE_BLENDINDICES, 0); 2095 } 2096 } 2097 } 2098 2099 if (fvf & D3DFVF_NORMAL) 2100 append_decl_element(declaration, &idx, &offset, D3DDECLTYPE_FLOAT3, D3DDECLUSAGE_NORMAL, 0); 2101 if (fvf & D3DFVF_PSIZE) 2102 append_decl_element(declaration, &idx, &offset, D3DDECLTYPE_FLOAT1, D3DDECLUSAGE_PSIZE, 0); 2103 if (fvf & D3DFVF_DIFFUSE) 2104 append_decl_element(declaration, &idx, &offset, D3DDECLTYPE_D3DCOLOR, D3DDECLUSAGE_COLOR, 0); 2105 if (fvf & D3DFVF_SPECULAR) 2106 append_decl_element(declaration, &idx, &offset, D3DDECLTYPE_D3DCOLOR, D3DDECLUSAGE_COLOR, 1); 2107 2108 for (i = 0; i < tex_count; ++i) 2109 { 2110 switch ((fvf >> (16 + 2 * i)) & 0x03) 2111 { 2112 case D3DFVF_TEXTUREFORMAT1: 2113 append_decl_element(declaration, &idx, &offset, D3DDECLTYPE_FLOAT1, D3DDECLUSAGE_TEXCOORD, i); 2114 break; 2115 case D3DFVF_TEXTUREFORMAT2: 2116 append_decl_element(declaration, &idx, &offset, D3DDECLTYPE_FLOAT2, D3DDECLUSAGE_TEXCOORD, i); 2117 break; 2118 case D3DFVF_TEXTUREFORMAT3: 2119 append_decl_element(declaration, &idx, &offset, D3DDECLTYPE_FLOAT3, D3DDECLUSAGE_TEXCOORD, i); 2120 break; 2121 case D3DFVF_TEXTUREFORMAT4: 2122 append_decl_element(declaration, &idx, &offset, D3DDECLTYPE_FLOAT4, D3DDECLUSAGE_TEXCOORD, i); 2123 break; 2124 } 2125 } 2126 2127 declaration[idx] = end_element; 2128 2129 return D3D_OK; 2130 } 2131 2132 /************************************************************************* 2133 * D3DXFVFFromDeclarator 2134 */ 2135 HRESULT WINAPI D3DXFVFFromDeclarator(const D3DVERTEXELEMENT9 *declaration, DWORD *fvf) 2136 { 2137 unsigned int i = 0, texture, offset; 2138 2139 TRACE("(%p, %p)\n", declaration, fvf); 2140 2141 *fvf = 0; 2142 if (declaration[0].Type == D3DDECLTYPE_FLOAT3 && declaration[0].Usage == D3DDECLUSAGE_POSITION) 2143 { 2144 if ((declaration[1].Type == D3DDECLTYPE_FLOAT4 && declaration[1].Usage == D3DDECLUSAGE_BLENDWEIGHT && 2145 declaration[1].UsageIndex == 0) && 2146 (declaration[2].Type == D3DDECLTYPE_FLOAT1 && declaration[2].Usage == D3DDECLUSAGE_BLENDINDICES && 2147 declaration[2].UsageIndex == 0)) 2148 { 2149 return D3DERR_INVALIDCALL; 2150 } 2151 else if ((declaration[1].Type == D3DDECLTYPE_UBYTE4 || declaration[1].Type == D3DDECLTYPE_D3DCOLOR) && 2152 declaration[1].Usage == D3DDECLUSAGE_BLENDINDICES && declaration[1].UsageIndex == 0) 2153 { 2154 if (declaration[1].Type == D3DDECLTYPE_UBYTE4) 2155 { 2156 *fvf |= D3DFVF_XYZB1 | D3DFVF_LASTBETA_UBYTE4; 2157 } 2158 else 2159 { 2160 *fvf |= D3DFVF_XYZB1 | D3DFVF_LASTBETA_D3DCOLOR; 2161 } 2162 i = 2; 2163 } 2164 else if (declaration[1].Type <= D3DDECLTYPE_FLOAT4 && declaration[1].Usage == D3DDECLUSAGE_BLENDWEIGHT && 2165 declaration[1].UsageIndex == 0) 2166 { 2167 if ((declaration[2].Type == D3DDECLTYPE_UBYTE4 || declaration[2].Type == D3DDECLTYPE_D3DCOLOR) && 2168 declaration[2].Usage == D3DDECLUSAGE_BLENDINDICES && declaration[2].UsageIndex == 0) 2169 { 2170 if (declaration[2].Type == D3DDECLTYPE_UBYTE4) 2171 { 2172 *fvf |= D3DFVF_LASTBETA_UBYTE4; 2173 } 2174 else 2175 { 2176 *fvf |= D3DFVF_LASTBETA_D3DCOLOR; 2177 } 2178 switch (declaration[1].Type) 2179 { 2180 case D3DDECLTYPE_FLOAT1: *fvf |= D3DFVF_XYZB2; break; 2181 case D3DDECLTYPE_FLOAT2: *fvf |= D3DFVF_XYZB3; break; 2182 case D3DDECLTYPE_FLOAT3: *fvf |= D3DFVF_XYZB4; break; 2183 case D3DDECLTYPE_FLOAT4: *fvf |= D3DFVF_XYZB5; break; 2184 } 2185 i = 3; 2186 } 2187 else 2188 { 2189 switch (declaration[1].Type) 2190 { 2191 case D3DDECLTYPE_FLOAT1: *fvf |= D3DFVF_XYZB1; break; 2192 case D3DDECLTYPE_FLOAT2: *fvf |= D3DFVF_XYZB2; break; 2193 case D3DDECLTYPE_FLOAT3: *fvf |= D3DFVF_XYZB3; break; 2194 case D3DDECLTYPE_FLOAT4: *fvf |= D3DFVF_XYZB4; break; 2195 } 2196 i = 2; 2197 } 2198 } 2199 else 2200 { 2201 *fvf |= D3DFVF_XYZ; 2202 i = 1; 2203 } 2204 } 2205 else if (declaration[0].Type == D3DDECLTYPE_FLOAT4 && declaration[0].Usage == D3DDECLUSAGE_POSITIONT && 2206 declaration[0].UsageIndex == 0) 2207 { 2208 *fvf |= D3DFVF_XYZRHW; 2209 i = 1; 2210 } 2211 2212 if (declaration[i].Type == D3DDECLTYPE_FLOAT3 && declaration[i].Usage == D3DDECLUSAGE_NORMAL) 2213 { 2214 *fvf |= D3DFVF_NORMAL; 2215 i++; 2216 } 2217 if (declaration[i].Type == D3DDECLTYPE_FLOAT1 && declaration[i].Usage == D3DDECLUSAGE_PSIZE && 2218 declaration[i].UsageIndex == 0) 2219 { 2220 *fvf |= D3DFVF_PSIZE; 2221 i++; 2222 } 2223 if (declaration[i].Type == D3DDECLTYPE_D3DCOLOR && declaration[i].Usage == D3DDECLUSAGE_COLOR && 2224 declaration[i].UsageIndex == 0) 2225 { 2226 *fvf |= D3DFVF_DIFFUSE; 2227 i++; 2228 } 2229 if (declaration[i].Type == D3DDECLTYPE_D3DCOLOR && declaration[i].Usage == D3DDECLUSAGE_COLOR && 2230 declaration[i].UsageIndex == 1) 2231 { 2232 *fvf |= D3DFVF_SPECULAR; 2233 i++; 2234 } 2235 2236 for (texture = 0; texture < D3DDP_MAXTEXCOORD; i++, texture++) 2237 { 2238 if (declaration[i].Stream == 0xFF) 2239 { 2240 break; 2241 } 2242 else if (declaration[i].Type == D3DDECLTYPE_FLOAT1 && declaration[i].Usage == D3DDECLUSAGE_TEXCOORD && 2243 declaration[i].UsageIndex == texture) 2244 { 2245 *fvf |= D3DFVF_TEXCOORDSIZE1(declaration[i].UsageIndex); 2246 } 2247 else if (declaration[i].Type == D3DDECLTYPE_FLOAT2 && declaration[i].Usage == D3DDECLUSAGE_TEXCOORD && 2248 declaration[i].UsageIndex == texture) 2249 { 2250 *fvf |= D3DFVF_TEXCOORDSIZE2(declaration[i].UsageIndex); 2251 } 2252 else if (declaration[i].Type == D3DDECLTYPE_FLOAT3 && declaration[i].Usage == D3DDECLUSAGE_TEXCOORD && 2253 declaration[i].UsageIndex == texture) 2254 { 2255 *fvf |= D3DFVF_TEXCOORDSIZE3(declaration[i].UsageIndex); 2256 } 2257 else if (declaration[i].Type == D3DDECLTYPE_FLOAT4 && declaration[i].Usage == D3DDECLUSAGE_TEXCOORD && 2258 declaration[i].UsageIndex == texture) 2259 { 2260 *fvf |= D3DFVF_TEXCOORDSIZE4(declaration[i].UsageIndex); 2261 } 2262 else 2263 { 2264 return D3DERR_INVALIDCALL; 2265 } 2266 } 2267 2268 *fvf |= (texture << D3DFVF_TEXCOUNT_SHIFT); 2269 2270 for (offset = 0, i = 0; declaration[i].Stream != 0xFF; 2271 offset += d3dx_decltype_size[declaration[i].Type], i++) 2272 { 2273 if (declaration[i].Offset != offset) 2274 { 2275 return D3DERR_INVALIDCALL; 2276 } 2277 } 2278 2279 return D3D_OK; 2280 } 2281 2282 /************************************************************************* 2283 * D3DXGetFVFVertexSize 2284 */ 2285 static UINT Get_TexCoord_Size_From_FVF(DWORD FVF, int tex_num) 2286 { 2287 return (((((FVF) >> (16 + (2 * (tex_num)))) + 1) & 0x03) + 1); 2288 } 2289 2290 UINT WINAPI D3DXGetFVFVertexSize(DWORD FVF) 2291 { 2292 DWORD size = 0; 2293 UINT i; 2294 UINT numTextures = (FVF & D3DFVF_TEXCOUNT_MASK) >> D3DFVF_TEXCOUNT_SHIFT; 2295 2296 if (FVF & D3DFVF_NORMAL) size += sizeof(D3DXVECTOR3); 2297 if (FVF & D3DFVF_DIFFUSE) size += sizeof(DWORD); 2298 if (FVF & D3DFVF_SPECULAR) size += sizeof(DWORD); 2299 if (FVF & D3DFVF_PSIZE) size += sizeof(DWORD); 2300 2301 switch (FVF & D3DFVF_POSITION_MASK) 2302 { 2303 case D3DFVF_XYZ: size += sizeof(D3DXVECTOR3); break; 2304 case D3DFVF_XYZRHW: size += 4 * sizeof(FLOAT); break; 2305 case D3DFVF_XYZB1: size += 4 * sizeof(FLOAT); break; 2306 case D3DFVF_XYZB2: size += 5 * sizeof(FLOAT); break; 2307 case D3DFVF_XYZB3: size += 6 * sizeof(FLOAT); break; 2308 case D3DFVF_XYZB4: size += 7 * sizeof(FLOAT); break; 2309 case D3DFVF_XYZB5: size += 8 * sizeof(FLOAT); break; 2310 case D3DFVF_XYZW: size += 4 * sizeof(FLOAT); break; 2311 } 2312 2313 for (i = 0; i < numTextures; i++) 2314 { 2315 size += Get_TexCoord_Size_From_FVF(FVF, i) * sizeof(FLOAT); 2316 } 2317 2318 return size; 2319 } 2320 2321 /************************************************************************* 2322 * D3DXGetDeclVertexSize 2323 */ 2324 UINT WINAPI D3DXGetDeclVertexSize(const D3DVERTEXELEMENT9 *decl, DWORD stream_idx) 2325 { 2326 const D3DVERTEXELEMENT9 *element; 2327 UINT size = 0; 2328 2329 TRACE("decl %p, stream_idx %u\n", decl, stream_idx); 2330 2331 if (!decl) return 0; 2332 2333 for (element = decl; element->Stream != 0xff; ++element) 2334 { 2335 UINT type_size; 2336 2337 if (element->Stream != stream_idx) continue; 2338 2339 if (element->Type >= ARRAY_SIZE(d3dx_decltype_size)) 2340 { 2341 FIXME("Unhandled element type %#x, size will be incorrect.\n", element->Type); 2342 continue; 2343 } 2344 2345 type_size = d3dx_decltype_size[element->Type]; 2346 if (element->Offset + type_size > size) size = element->Offset + type_size; 2347 } 2348 2349 return size; 2350 } 2351 2352 /************************************************************************* 2353 * D3DXGetDeclLength 2354 */ 2355 UINT WINAPI D3DXGetDeclLength(const D3DVERTEXELEMENT9 *decl) 2356 { 2357 const D3DVERTEXELEMENT9 *element; 2358 2359 TRACE("decl %p\n", decl); 2360 2361 /* null decl results in exception on Windows XP */ 2362 2363 for (element = decl; element->Stream != 0xff; ++element); 2364 2365 return element - decl; 2366 } 2367 2368 BOOL WINAPI D3DXIntersectTri(const D3DXVECTOR3 *p0, const D3DXVECTOR3 *p1, const D3DXVECTOR3 *p2, 2369 const D3DXVECTOR3 *praypos, const D3DXVECTOR3 *praydir, float *pu, float *pv, float *pdist) 2370 { 2371 D3DXMATRIX m; 2372 D3DXVECTOR4 vec; 2373 2374 TRACE("p0 %p, p1 %p, p2 %p, praypos %p, praydir %p, pu %p, pv %p, pdist %p.\n", 2375 p0, p1, p2, praypos, praydir, pu, pv, pdist); 2376 2377 m.u.m[0][0] = p1->x - p0->x; 2378 m.u.m[1][0] = p2->x - p0->x; 2379 m.u.m[2][0] = -praydir->x; 2380 m.u.m[3][0] = 0.0f; 2381 m.u.m[0][1] = p1->y - p0->y; 2382 m.u.m[1][1] = p2->y - p0->y; 2383 m.u.m[2][1] = -praydir->y; 2384 m.u.m[3][1] = 0.0f; 2385 m.u.m[0][2] = p1->z - p0->z; 2386 m.u.m[1][2] = p2->z - p0->z; 2387 m.u.m[2][2] = -praydir->z; 2388 m.u.m[3][2] = 0.0f; 2389 m.u.m[0][3] = 0.0f; 2390 m.u.m[1][3] = 0.0f; 2391 m.u.m[2][3] = 0.0f; 2392 m.u.m[3][3] = 1.0f; 2393 2394 vec.x = praypos->x - p0->x; 2395 vec.y = praypos->y - p0->y; 2396 vec.z = praypos->z - p0->z; 2397 vec.w = 0.0f; 2398 2399 if ( D3DXMatrixInverse(&m, NULL, &m) ) 2400 { 2401 D3DXVec4Transform(&vec, &vec, &m); 2402 if ( (vec.x >= 0.0f) && (vec.y >= 0.0f) && (vec.x + vec.y <= 1.0f) && (vec.z >= 0.0f) ) 2403 { 2404 if (pu) *pu = vec.x; 2405 if (pv) *pv = vec.y; 2406 if (pdist) *pdist = fabsf( vec.z ); 2407 return TRUE; 2408 } 2409 } 2410 2411 return FALSE; 2412 } 2413 2414 BOOL WINAPI D3DXSphereBoundProbe(const D3DXVECTOR3 *pcenter, float radius, 2415 const D3DXVECTOR3 *prayposition, const D3DXVECTOR3 *praydirection) 2416 { 2417 D3DXVECTOR3 difference; 2418 FLOAT a, b, c, d; 2419 2420 a = D3DXVec3LengthSq(praydirection); 2421 if (!D3DXVec3Subtract(&difference, prayposition, pcenter)) return FALSE; 2422 b = D3DXVec3Dot(&difference, praydirection); 2423 c = D3DXVec3LengthSq(&difference) - radius * radius; 2424 d = b * b - a * c; 2425 2426 if ( ( d <= 0.0f ) || ( sqrt(d) <= b ) ) return FALSE; 2427 return TRUE; 2428 } 2429 2430 /************************************************************************* 2431 * D3DXCreateMesh 2432 */ 2433 HRESULT WINAPI D3DXCreateMesh(DWORD numfaces, DWORD numvertices, DWORD options, 2434 const D3DVERTEXELEMENT9 *declaration, struct IDirect3DDevice9 *device, struct ID3DXMesh **mesh) 2435 { 2436 HRESULT hr; 2437 DWORD fvf; 2438 IDirect3DVertexDeclaration9 *vertex_declaration; 2439 UINT vertex_declaration_size; 2440 UINT num_elem; 2441 IDirect3DVertexBuffer9 *vertex_buffer; 2442 IDirect3DIndexBuffer9 *index_buffer; 2443 DWORD *attrib_buffer; 2444 struct d3dx9_mesh *object; 2445 DWORD index_usage = 0; 2446 D3DPOOL index_pool = D3DPOOL_DEFAULT; 2447 D3DFORMAT index_format = D3DFMT_INDEX16; 2448 DWORD vertex_usage = 0; 2449 D3DPOOL vertex_pool = D3DPOOL_DEFAULT; 2450 int i; 2451 2452 TRACE("numfaces %u, numvertices %u, options %#x, declaration %p, device %p, mesh %p.\n", 2453 numfaces, numvertices, options, declaration, device, mesh); 2454 2455 if (numfaces == 0 || numvertices == 0 || declaration == NULL || device == NULL || mesh == NULL || 2456 /* D3DXMESH_VB_SHARE is for cloning, and D3DXMESH_USEHWONLY is for ConvertToBlendedMesh */ 2457 (options & (D3DXMESH_VB_SHARE | D3DXMESH_USEHWONLY | 0xfffe0000))) 2458 { 2459 return D3DERR_INVALIDCALL; 2460 } 2461 for (i = 0; declaration[i].Stream != 0xff; i++) 2462 if (declaration[i].Stream != 0) 2463 return D3DERR_INVALIDCALL; 2464 num_elem = i + 1; 2465 2466 if (options & D3DXMESH_32BIT) 2467 index_format = D3DFMT_INDEX32; 2468 2469 if (options & D3DXMESH_DONOTCLIP) { 2470 index_usage |= D3DUSAGE_DONOTCLIP; 2471 vertex_usage |= D3DUSAGE_DONOTCLIP; 2472 } 2473 if (options & D3DXMESH_POINTS) { 2474 index_usage |= D3DUSAGE_POINTS; 2475 vertex_usage |= D3DUSAGE_POINTS; 2476 } 2477 if (options & D3DXMESH_RTPATCHES) { 2478 index_usage |= D3DUSAGE_RTPATCHES; 2479 vertex_usage |= D3DUSAGE_RTPATCHES; 2480 } 2481 if (options & D3DXMESH_NPATCHES) { 2482 index_usage |= D3DUSAGE_NPATCHES; 2483 vertex_usage |= D3DUSAGE_NPATCHES; 2484 } 2485 2486 if (options & D3DXMESH_VB_SYSTEMMEM) 2487 vertex_pool = D3DPOOL_SYSTEMMEM; 2488 else if (options & D3DXMESH_VB_MANAGED) 2489 vertex_pool = D3DPOOL_MANAGED; 2490 2491 if (options & D3DXMESH_VB_WRITEONLY) 2492 vertex_usage |= D3DUSAGE_WRITEONLY; 2493 if (options & D3DXMESH_VB_DYNAMIC) 2494 vertex_usage |= D3DUSAGE_DYNAMIC; 2495 if (options & D3DXMESH_VB_SOFTWAREPROCESSING) 2496 vertex_usage |= D3DUSAGE_SOFTWAREPROCESSING; 2497 2498 if (options & D3DXMESH_IB_SYSTEMMEM) 2499 index_pool = D3DPOOL_SYSTEMMEM; 2500 else if (options & D3DXMESH_IB_MANAGED) 2501 index_pool = D3DPOOL_MANAGED; 2502 2503 if (options & D3DXMESH_IB_WRITEONLY) 2504 index_usage |= D3DUSAGE_WRITEONLY; 2505 if (options & D3DXMESH_IB_DYNAMIC) 2506 index_usage |= D3DUSAGE_DYNAMIC; 2507 if (options & D3DXMESH_IB_SOFTWAREPROCESSING) 2508 index_usage |= D3DUSAGE_SOFTWAREPROCESSING; 2509 2510 hr = D3DXFVFFromDeclarator(declaration, &fvf); 2511 if (hr != D3D_OK) 2512 { 2513 fvf = 0; 2514 } 2515 2516 /* Create vertex declaration */ 2517 hr = IDirect3DDevice9_CreateVertexDeclaration(device, 2518 declaration, 2519 &vertex_declaration); 2520 if (FAILED(hr)) 2521 { 2522 WARN("Unexpected return value %x from IDirect3DDevice9_CreateVertexDeclaration.\n",hr); 2523 return hr; 2524 } 2525 vertex_declaration_size = D3DXGetDeclVertexSize(declaration, declaration[0].Stream); 2526 2527 /* Create vertex buffer */ 2528 hr = IDirect3DDevice9_CreateVertexBuffer(device, 2529 numvertices * vertex_declaration_size, 2530 vertex_usage, 2531 fvf, 2532 vertex_pool, 2533 &vertex_buffer, 2534 NULL); 2535 if (FAILED(hr)) 2536 { 2537 WARN("Unexpected return value %x from IDirect3DDevice9_CreateVertexBuffer.\n",hr); 2538 IDirect3DVertexDeclaration9_Release(vertex_declaration); 2539 return hr; 2540 } 2541 2542 /* Create index buffer */ 2543 hr = IDirect3DDevice9_CreateIndexBuffer(device, 2544 numfaces * 3 * ((index_format == D3DFMT_INDEX16) ? 2 : 4), 2545 index_usage, 2546 index_format, 2547 index_pool, 2548 &index_buffer, 2549 NULL); 2550 if (FAILED(hr)) 2551 { 2552 WARN("Unexpected return value %x from IDirect3DDevice9_CreateVertexBuffer.\n",hr); 2553 IDirect3DVertexBuffer9_Release(vertex_buffer); 2554 IDirect3DVertexDeclaration9_Release(vertex_declaration); 2555 return hr; 2556 } 2557 2558 attrib_buffer = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, numfaces * sizeof(*attrib_buffer)); 2559 object = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(*object)); 2560 if (object == NULL || attrib_buffer == NULL) 2561 { 2562 HeapFree(GetProcessHeap(), 0, object); 2563 HeapFree(GetProcessHeap(), 0, attrib_buffer); 2564 IDirect3DIndexBuffer9_Release(index_buffer); 2565 IDirect3DVertexBuffer9_Release(vertex_buffer); 2566 IDirect3DVertexDeclaration9_Release(vertex_declaration); 2567 *mesh = NULL; 2568 return E_OUTOFMEMORY; 2569 } 2570 object->ID3DXMesh_iface.lpVtbl = &D3DXMesh_Vtbl; 2571 object->ref = 1; 2572 2573 object->numfaces = numfaces; 2574 object->numvertices = numvertices; 2575 object->options = options; 2576 object->fvf = fvf; 2577 object->device = device; 2578 IDirect3DDevice9_AddRef(device); 2579 2580 copy_declaration(object->cached_declaration, declaration, num_elem); 2581 object->vertex_declaration = vertex_declaration; 2582 object->vertex_declaration_size = vertex_declaration_size; 2583 object->num_elem = num_elem; 2584 object->vertex_buffer = vertex_buffer; 2585 object->index_buffer = index_buffer; 2586 object->attrib_buffer = attrib_buffer; 2587 2588 *mesh = &object->ID3DXMesh_iface; 2589 2590 return D3D_OK; 2591 } 2592 2593 /************************************************************************* 2594 * D3DXCreateMeshFVF 2595 */ 2596 HRESULT WINAPI D3DXCreateMeshFVF(DWORD numfaces, DWORD numvertices, DWORD options, 2597 DWORD fvf, struct IDirect3DDevice9 *device, struct ID3DXMesh **mesh) 2598 { 2599 HRESULT hr; 2600 D3DVERTEXELEMENT9 declaration[MAX_FVF_DECL_SIZE]; 2601 2602 TRACE("(%u, %u, %u, %u, %p, %p)\n", numfaces, numvertices, options, fvf, device, mesh); 2603 2604 hr = D3DXDeclaratorFromFVF(fvf, declaration); 2605 if (FAILED(hr)) return hr; 2606 2607 return D3DXCreateMesh(numfaces, numvertices, options, declaration, device, mesh); 2608 } 2609 2610 2611 struct mesh_data { 2612 DWORD num_vertices; 2613 DWORD num_poly_faces; 2614 DWORD num_tri_faces; 2615 D3DXVECTOR3 *vertices; 2616 DWORD *num_tri_per_face; 2617 DWORD *indices; 2618 2619 DWORD fvf; 2620 2621 /* optional mesh data */ 2622 2623 DWORD num_normals; 2624 D3DXVECTOR3 *normals; 2625 DWORD *normal_indices; 2626 2627 D3DXVECTOR2 *tex_coords; 2628 2629 DWORD *vertex_colors; 2630 2631 DWORD num_materials; 2632 D3DXMATERIAL *materials; 2633 DWORD *material_indices; 2634 2635 struct ID3DXSkinInfo *skin_info; 2636 DWORD nb_bones; 2637 }; 2638 2639 static HRESULT parse_texture_filename(ID3DXFileData *filedata, char **filename_out) 2640 { 2641 HRESULT hr; 2642 SIZE_T data_size; 2643 BYTE *data; 2644 char *filename_in; 2645 char *filename = NULL; 2646 2647 /* template TextureFilename { 2648 * STRING filename; 2649 * } 2650 */ 2651 2652 HeapFree(GetProcessHeap(), 0, *filename_out); 2653 *filename_out = NULL; 2654 2655 hr = filedata->lpVtbl->Lock(filedata, &data_size, (const void**)&data); 2656 if (FAILED(hr)) return hr; 2657 2658 /* FIXME: String must be retrieved directly instead of through a pointer once ID3DXFILE is fixed */ 2659 if (data_size < sizeof(filename_in)) 2660 { 2661 WARN("truncated data (%lu bytes)\n", data_size); 2662 filedata->lpVtbl->Unlock(filedata); 2663 return E_FAIL; 2664 } 2665 filename_in = *(char **)data; 2666 2667 filename = HeapAlloc(GetProcessHeap(), 0, strlen(filename_in) + 1); 2668 if (!filename) { 2669 filedata->lpVtbl->Unlock(filedata); 2670 return E_OUTOFMEMORY; 2671 } 2672 2673 strcpy(filename, filename_in); 2674 *filename_out = filename; 2675 2676 filedata->lpVtbl->Unlock(filedata); 2677 2678 return D3D_OK; 2679 } 2680 2681 static HRESULT parse_material(ID3DXFileData *filedata, D3DXMATERIAL *material) 2682 { 2683 HRESULT hr; 2684 SIZE_T data_size; 2685 const BYTE *data; 2686 GUID type; 2687 ID3DXFileData *child; 2688 SIZE_T i, nb_children; 2689 2690 material->pTextureFilename = NULL; 2691 2692 hr = filedata->lpVtbl->Lock(filedata, &data_size, (const void**)&data); 2693 if (FAILED(hr)) return hr; 2694 2695 /* 2696 * template ColorRGBA { 2697 * FLOAT red; 2698 * FLOAT green; 2699 * FLOAT blue; 2700 * FLOAT alpha; 2701 * } 2702 * template ColorRGB { 2703 * FLOAT red; 2704 * FLOAT green; 2705 * FLOAT blue; 2706 * } 2707 * template Material { 2708 * ColorRGBA faceColor; 2709 * FLOAT power; 2710 * ColorRGB specularColor; 2711 * ColorRGB emissiveColor; 2712 * [ ... ] 2713 * } 2714 */ 2715 if (data_size != sizeof(FLOAT) * 11) { 2716 WARN("incorrect data size (%ld bytes)\n", data_size); 2717 filedata->lpVtbl->Unlock(filedata); 2718 return E_FAIL; 2719 } 2720 2721 memcpy(&material->MatD3D.Diffuse, data, sizeof(D3DCOLORVALUE)); 2722 data += sizeof(D3DCOLORVALUE); 2723 material->MatD3D.Power = *(FLOAT*)data; 2724 data += sizeof(FLOAT); 2725 memcpy(&material->MatD3D.Specular, data, sizeof(FLOAT) * 3); 2726 material->MatD3D.Specular.a = 1.0f; 2727 data += 3 * sizeof(FLOAT); 2728 memcpy(&material->MatD3D.Emissive, data, sizeof(FLOAT) * 3); 2729 material->MatD3D.Emissive.a = 1.0f; 2730 material->MatD3D.Ambient.r = 0.0f; 2731 material->MatD3D.Ambient.g = 0.0f; 2732 material->MatD3D.Ambient.b = 0.0f; 2733 material->MatD3D.Ambient.a = 1.0f; 2734 2735 filedata->lpVtbl->Unlock(filedata); 2736 2737 hr = filedata->lpVtbl->GetChildren(filedata, &nb_children); 2738 if (FAILED(hr)) 2739 return hr; 2740 2741 for (i = 0; i < nb_children; i++) 2742 { 2743 hr = filedata->lpVtbl->GetChild(filedata, i, &child); 2744 if (FAILED(hr)) 2745 return hr; 2746 hr = child->lpVtbl->GetType(child, &type); 2747 if (FAILED(hr)) 2748 goto err; 2749 2750 if (IsEqualGUID(&type, &TID_D3DRMTextureFilename)) { 2751 hr = parse_texture_filename(child, &material->pTextureFilename); 2752 if (FAILED(hr)) 2753 goto err; 2754 } 2755 IUnknown_Release(child); 2756 } 2757 return D3D_OK; 2758 2759 err: 2760 IUnknown_Release(child); 2761 return hr; 2762 } 2763 2764 static void destroy_materials(struct mesh_data *mesh) 2765 { 2766 DWORD i; 2767 for (i = 0; i < mesh->num_materials; i++) 2768 HeapFree(GetProcessHeap(), 0, mesh->materials[i].pTextureFilename); 2769 HeapFree(GetProcessHeap(), 0, mesh->materials); 2770 HeapFree(GetProcessHeap(), 0, mesh->material_indices); 2771 mesh->num_materials = 0; 2772 mesh->materials = NULL; 2773 mesh->material_indices = NULL; 2774 } 2775 2776 static HRESULT parse_material_list(ID3DXFileData *filedata, struct mesh_data *mesh) 2777 { 2778 HRESULT hr; 2779 SIZE_T data_size; 2780 const DWORD *data, *in_ptr; 2781 GUID type; 2782 ID3DXFileData *child = NULL; 2783 DWORD num_materials; 2784 DWORD i; 2785 SIZE_T nb_children; 2786 2787 destroy_materials(mesh); 2788 2789 hr = filedata->lpVtbl->Lock(filedata, &data_size, (const void**)&data); 2790 if (FAILED(hr)) return hr; 2791 2792 /* template MeshMaterialList { 2793 * DWORD nMaterials; 2794 * DWORD nFaceIndexes; 2795 * array DWORD faceIndexes[nFaceIndexes]; 2796 * [ Material ] 2797 * } 2798 */ 2799 2800 in_ptr = data; 2801 hr = E_FAIL; 2802 2803 if (data_size < sizeof(DWORD)) { 2804 WARN("truncated data (%ld bytes)\n", data_size); 2805 goto end; 2806 } 2807 num_materials = *in_ptr++; 2808 if (!num_materials) { 2809 hr = D3D_OK; 2810 goto end; 2811 } 2812 2813 if (data_size < 2 * sizeof(DWORD)) { 2814 WARN("truncated data (%ld bytes)\n", data_size); 2815 goto end; 2816 } 2817 if (*in_ptr++ != mesh->num_poly_faces) { 2818 WARN("number of material face indices (%u) doesn't match number of faces (%u)\n", 2819 *(in_ptr - 1), mesh->num_poly_faces); 2820 goto end; 2821 } 2822 if (data_size < 2 * sizeof(DWORD) + mesh->num_poly_faces * sizeof(DWORD)) { 2823 WARN("truncated data (%ld bytes)\n", data_size); 2824 goto end; 2825 } 2826 for (i = 0; i < mesh->num_poly_faces; i++) { 2827 if (*in_ptr++ >= num_materials) { 2828 WARN("face %u: reference to undefined material %u (only %u materials)\n", 2829 i, *(in_ptr - 1), num_materials); 2830 goto end; 2831 } 2832 } 2833 2834 mesh->materials = HeapAlloc(GetProcessHeap(), 0, num_materials * sizeof(*mesh->materials)); 2835 mesh->material_indices = HeapAlloc(GetProcessHeap(), 0, mesh->num_poly_faces * sizeof(*mesh->material_indices)); 2836 if (!mesh->materials || !mesh->material_indices) { 2837 hr = E_OUTOFMEMORY; 2838 goto end; 2839 } 2840 memcpy(mesh->material_indices, data + 2, mesh->num_poly_faces * sizeof(DWORD)); 2841 2842 hr = filedata->lpVtbl->GetChildren(filedata, &nb_children); 2843 if (FAILED(hr)) 2844 goto end; 2845 2846 for (i = 0; i < nb_children; i++) 2847 { 2848 hr = filedata->lpVtbl->GetChild(filedata, i, &child); 2849 if (FAILED(hr)) 2850 goto end; 2851 hr = child->lpVtbl->GetType(child, &type); 2852 if (FAILED(hr)) 2853 goto end; 2854 2855 if (IsEqualGUID(&type, &TID_D3DRMMaterial)) { 2856 if (mesh->num_materials >= num_materials) { 2857 WARN("more materials defined than declared\n"); 2858 hr = E_FAIL; 2859 goto end; 2860 } 2861 hr = parse_material(child, &mesh->materials[mesh->num_materials++]); 2862 if (FAILED(hr)) 2863 goto end; 2864 } 2865 2866 IUnknown_Release(child); 2867 child = NULL; 2868 } 2869 if (num_materials != mesh->num_materials) { 2870 WARN("only %u of %u materials defined\n", num_materials, mesh->num_materials); 2871 hr = E_FAIL; 2872 } 2873 2874 end: 2875 if (child) 2876 IUnknown_Release(child); 2877 filedata->lpVtbl->Unlock(filedata); 2878 return hr; 2879 } 2880 2881 static HRESULT parse_texture_coords(ID3DXFileData *filedata, struct mesh_data *mesh) 2882 { 2883 HRESULT hr; 2884 SIZE_T data_size; 2885 const BYTE *data; 2886 2887 HeapFree(GetProcessHeap(), 0, mesh->tex_coords); 2888 mesh->tex_coords = NULL; 2889 2890 hr = filedata->lpVtbl->Lock(filedata, &data_size, (const void**)&data); 2891 if (FAILED(hr)) return hr; 2892 2893 /* template Coords2d { 2894 * FLOAT u; 2895 * FLOAT v; 2896 * } 2897 * template MeshTextureCoords { 2898 * DWORD nTextureCoords; 2899 * array Coords2d textureCoords[nTextureCoords]; 2900 * } 2901 */ 2902 2903 hr = E_FAIL; 2904 2905 if (data_size < sizeof(DWORD)) { 2906 WARN("truncated data (%ld bytes)\n", data_size); 2907 goto end; 2908 } 2909 if (*(DWORD*)data != mesh->num_vertices) { 2910 WARN("number of texture coordinates (%u) doesn't match number of vertices (%u)\n", 2911 *(DWORD*)data, mesh->num_vertices); 2912 goto end; 2913 } 2914 data += sizeof(DWORD); 2915 if (data_size < sizeof(DWORD) + mesh->num_vertices * sizeof(*mesh->tex_coords)) { 2916 WARN("truncated data (%ld bytes)\n", data_size); 2917 goto end; 2918 } 2919 2920 mesh->tex_coords = HeapAlloc(GetProcessHeap(), 0, mesh->num_vertices * sizeof(*mesh->tex_coords)); 2921 if (!mesh->tex_coords) { 2922 hr = E_OUTOFMEMORY; 2923 goto end; 2924 } 2925 memcpy(mesh->tex_coords, data, mesh->num_vertices * sizeof(*mesh->tex_coords)); 2926 2927 mesh->fvf |= D3DFVF_TEX1; 2928 2929 hr = D3D_OK; 2930 2931 end: 2932 filedata->lpVtbl->Unlock(filedata); 2933 return hr; 2934 } 2935 2936 static HRESULT parse_vertex_colors(ID3DXFileData *filedata, struct mesh_data *mesh) 2937 { 2938 HRESULT hr; 2939 SIZE_T data_size; 2940 const BYTE *data; 2941 DWORD num_colors; 2942 DWORD i; 2943 2944 HeapFree(GetProcessHeap(), 0, mesh->vertex_colors); 2945 mesh->vertex_colors = NULL; 2946 2947 hr = filedata->lpVtbl->Lock(filedata, &data_size, (const void**)&data); 2948 if (FAILED(hr)) return hr; 2949 2950 /* template IndexedColor { 2951 * DWORD index; 2952 * ColorRGBA indexColor; 2953 * } 2954 * template MeshVertexColors { 2955 * DWORD nVertexColors; 2956 * array IndexedColor vertexColors[nVertexColors]; 2957 * } 2958 */ 2959 2960 hr = E_FAIL; 2961 2962 if (data_size < sizeof(DWORD)) { 2963 WARN("truncated data (%ld bytes)\n", data_size); 2964 goto end; 2965 } 2966 num_colors = *(DWORD*)data; 2967 data += sizeof(DWORD); 2968 if (data_size < sizeof(DWORD) + num_colors * (sizeof(DWORD) + sizeof(D3DCOLORVALUE))) { 2969 WARN("truncated data (%ld bytes)\n", data_size); 2970 goto end; 2971 } 2972 2973 mesh->vertex_colors = HeapAlloc(GetProcessHeap(), 0, mesh->num_vertices * sizeof(DWORD)); 2974 if (!mesh->vertex_colors) { 2975 hr = E_OUTOFMEMORY; 2976 goto end; 2977 } 2978 2979 for (i = 0; i < mesh->num_vertices; i++) 2980 mesh->vertex_colors[i] = D3DCOLOR_ARGB(0, 0xff, 0xff, 0xff); 2981 for (i = 0; i < num_colors; i++) 2982 { 2983 D3DCOLORVALUE color; 2984 DWORD index = *(DWORD*)data; 2985 data += sizeof(DWORD); 2986 if (index >= mesh->num_vertices) { 2987 WARN("vertex color %u references undefined vertex %u (only %u vertices)\n", 2988 i, index, mesh->num_vertices); 2989 goto end; 2990 } 2991 memcpy(&color, data, sizeof(color)); 2992 data += sizeof(color); 2993 color.r = min(1.0f, max(0.0f, color.r)); 2994 color.g = min(1.0f, max(0.0f, color.g)); 2995 color.b = min(1.0f, max(0.0f, color.b)); 2996 color.a = min(1.0f, max(0.0f, color.a)); 2997 mesh->vertex_colors[index] = D3DCOLOR_ARGB((BYTE)(color.a * 255.0f + 0.5f), 2998 (BYTE)(color.r * 255.0f + 0.5f), 2999 (BYTE)(color.g * 255.0f + 0.5f), 3000 (BYTE)(color.b * 255.0f + 0.5f)); 3001 } 3002 3003 mesh->fvf |= D3DFVF_DIFFUSE; 3004 3005 hr = D3D_OK; 3006 3007 end: 3008 filedata->lpVtbl->Unlock(filedata); 3009 return hr; 3010 } 3011 3012 static HRESULT parse_normals(ID3DXFileData *filedata, struct mesh_data *mesh) 3013 { 3014 HRESULT hr; 3015 SIZE_T data_size; 3016 const BYTE *data; 3017 DWORD *index_out_ptr; 3018 DWORD i; 3019 DWORD num_face_indices = mesh->num_poly_faces * 2 + mesh->num_tri_faces; 3020 3021 HeapFree(GetProcessHeap(), 0, mesh->normals); 3022 mesh->num_normals = 0; 3023 mesh->normals = NULL; 3024 mesh->normal_indices = NULL; 3025 mesh->fvf |= D3DFVF_NORMAL; 3026 3027 hr = filedata->lpVtbl->Lock(filedata, &data_size, (const void**)&data); 3028 if (FAILED(hr)) return hr; 3029 3030 /* template Vector { 3031 * FLOAT x; 3032 * FLOAT y; 3033 * FLOAT z; 3034 * } 3035 * template MeshFace { 3036 * DWORD nFaceVertexIndices; 3037 * array DWORD faceVertexIndices[nFaceVertexIndices]; 3038 * } 3039 * template MeshNormals { 3040 * DWORD nNormals; 3041 * array Vector normals[nNormals]; 3042 * DWORD nFaceNormals; 3043 * array MeshFace faceNormals[nFaceNormals]; 3044 * } 3045 */ 3046 3047 hr = E_FAIL; 3048 3049 if (data_size < sizeof(DWORD) * 2) { 3050 WARN("truncated data (%ld bytes)\n", data_size); 3051 goto end; 3052 } 3053 mesh->num_normals = *(DWORD*)data; 3054 data += sizeof(DWORD); 3055 if (data_size < sizeof(DWORD) * 2 + mesh->num_normals * sizeof(D3DXVECTOR3) + 3056 num_face_indices * sizeof(DWORD)) { 3057 WARN("truncated data (%ld bytes)\n", data_size); 3058 goto end; 3059 } 3060 3061 mesh->normals = HeapAlloc(GetProcessHeap(), 0, mesh->num_normals * sizeof(D3DXVECTOR3)); 3062 mesh->normal_indices = HeapAlloc(GetProcessHeap(), 0, num_face_indices * sizeof(DWORD)); 3063 if (!mesh->normals || !mesh->normal_indices) { 3064 hr = E_OUTOFMEMORY; 3065 goto end; 3066 } 3067 3068 memcpy(mesh->normals, data, mesh->num_normals * sizeof(D3DXVECTOR3)); 3069 data += mesh->num_normals * sizeof(D3DXVECTOR3); 3070 for (i = 0; i < mesh->num_normals; i++) 3071 D3DXVec3Normalize(&mesh->normals[i], &mesh->normals[i]); 3072 3073 if (*(DWORD*)data != mesh->num_poly_faces) { 3074 WARN("number of face normals (%u) doesn't match number of faces (%u)\n", 3075 *(DWORD*)data, mesh->num_poly_faces); 3076 goto end; 3077 } 3078 data += sizeof(DWORD); 3079 index_out_ptr = mesh->normal_indices; 3080 for (i = 0; i < mesh->num_poly_faces; i++) 3081 { 3082 DWORD j; 3083 DWORD count = *(DWORD*)data; 3084 if (count != mesh->num_tri_per_face[i] + 2) { 3085 WARN("face %u: number of normals (%u) doesn't match number of vertices (%u)\n", 3086 i, count, mesh->num_tri_per_face[i] + 2); 3087 goto end; 3088 } 3089 data += sizeof(DWORD); 3090 3091 for (j = 0; j < count; j++) { 3092 DWORD normal_index = *(DWORD*)data; 3093 if (normal_index >= mesh->num_normals) { 3094 WARN("face %u, normal index %u: reference to undefined normal %u (only %u normals)\n", 3095 i, j, normal_index, mesh->num_normals); 3096 goto end; 3097 } 3098 *index_out_ptr++ = normal_index; 3099 data += sizeof(DWORD); 3100 } 3101 } 3102 3103 hr = D3D_OK; 3104 3105 end: 3106 filedata->lpVtbl->Unlock(filedata); 3107 return hr; 3108 } 3109 3110 static HRESULT parse_skin_mesh_info(ID3DXFileData *filedata, struct mesh_data *mesh_data, DWORD index) 3111 { 3112 HRESULT hr; 3113 SIZE_T data_size; 3114 const BYTE *data; 3115 3116 TRACE("(%p, %p, %u)\n", filedata, mesh_data, index); 3117 3118 hr = filedata->lpVtbl->Lock(filedata, &data_size, (const void**)&data); 3119 if (FAILED(hr)) return hr; 3120 3121 hr = E_FAIL; 3122 3123 if (!mesh_data->skin_info) { 3124 if (data_size < sizeof(WORD) * 3) { 3125 WARN("truncated data (%ld bytes)\n", data_size); 3126 goto end; 3127 } 3128 /* Skip nMaxSkinWeightsPerVertex and nMaxSkinWeightsPerFace */ 3129 data += 2 * sizeof(WORD); 3130 mesh_data->nb_bones = *(WORD*)data; 3131 hr = D3DXCreateSkinInfoFVF(mesh_data->num_vertices, mesh_data->fvf, mesh_data->nb_bones, &mesh_data->skin_info); 3132 } else { 3133 const char *name; 3134 DWORD nb_influences; 3135 3136 /* FIXME: String must be retrieved directly instead of through a pointer once ID3DXFILE is fixed */ 3137 name = *(const char**)data; 3138 data += sizeof(char*); 3139 3140 nb_influences = *(DWORD*)data; 3141 data += sizeof(DWORD); 3142 3143 if (data_size < (sizeof(char*) + sizeof(DWORD) + nb_influences * (sizeof(DWORD) + sizeof(FLOAT)) + 16 * sizeof(FLOAT))) { 3144 WARN("truncated data (%ld bytes)\n", data_size); 3145 goto end; 3146 } 3147 3148 hr = mesh_data->skin_info->lpVtbl->SetBoneName(mesh_data->skin_info, index, name); 3149 if (SUCCEEDED(hr)) 3150 hr = mesh_data->skin_info->lpVtbl->SetBoneInfluence(mesh_data->skin_info, index, nb_influences, 3151 (const DWORD*)data, (const FLOAT*)(data + nb_influences * sizeof(DWORD))); 3152 if (SUCCEEDED(hr)) 3153 hr = mesh_data->skin_info->lpVtbl->SetBoneOffsetMatrix(mesh_data->skin_info, index, 3154 (const D3DMATRIX*)(data + nb_influences * (sizeof(DWORD) + sizeof(FLOAT)))); 3155 } 3156 3157 end: 3158 filedata->lpVtbl->Unlock(filedata); 3159 return hr; 3160 } 3161 3162 /* for provide_flags parameters */ 3163 #define PROVIDE_MATERIALS 0x1 3164 #define PROVIDE_SKININFO 0x2 3165 #define PROVIDE_ADJACENCY 0x4 3166 3167 static HRESULT parse_mesh(ID3DXFileData *filedata, struct mesh_data *mesh_data, DWORD provide_flags) 3168 { 3169 HRESULT hr; 3170 SIZE_T data_size; 3171 const BYTE *data, *in_ptr; 3172 DWORD *index_out_ptr; 3173 GUID type; 3174 ID3DXFileData *child = NULL; 3175 DWORD i; 3176 SIZE_T nb_children; 3177 DWORD nb_skin_weights_info = 0; 3178 3179 /* 3180 * template Mesh { 3181 * DWORD nVertices; 3182 * array Vector vertices[nVertices]; 3183 * DWORD nFaces; 3184 * array MeshFace faces[nFaces]; 3185 * [ ... ] 3186 * } 3187 */ 3188 3189 hr = filedata->lpVtbl->Lock(filedata, &data_size, (const void**)&data); 3190 if (FAILED(hr)) return hr; 3191 3192 in_ptr = data; 3193 hr = E_FAIL; 3194 3195 if (data_size < sizeof(DWORD) * 2) { 3196 WARN("truncated data (%ld bytes)\n", data_size); 3197 goto end; 3198 } 3199 mesh_data->num_vertices = *(DWORD*)in_ptr; 3200 if (data_size < sizeof(DWORD) * 2 + mesh_data->num_vertices * sizeof(D3DXVECTOR3)) { 3201 WARN("truncated data (%ld bytes)\n", data_size); 3202 goto end; 3203 } 3204 in_ptr += sizeof(DWORD) + mesh_data->num_vertices * sizeof(D3DXVECTOR3); 3205 3206 mesh_data->num_poly_faces = *(DWORD*)in_ptr; 3207 in_ptr += sizeof(DWORD); 3208 3209 mesh_data->num_tri_faces = 0; 3210 for (i = 0; i < mesh_data->num_poly_faces; i++) 3211 { 3212 DWORD num_poly_vertices; 3213 DWORD j; 3214 3215 if (data_size - (in_ptr - data) < sizeof(DWORD)) { 3216 WARN("truncated data (%ld bytes)\n", data_size); 3217 goto end; 3218 } 3219 num_poly_vertices = *(DWORD*)in_ptr; 3220 in_ptr += sizeof(DWORD); 3221 if (data_size - (in_ptr - data) < num_poly_vertices * sizeof(DWORD)) { 3222 WARN("truncated data (%ld bytes)\n", data_size); 3223 goto end; 3224 } 3225 if (num_poly_vertices < 3) { 3226 WARN("face %u has only %u vertices\n", i, num_poly_vertices); 3227 goto end; 3228 } 3229 for (j = 0; j < num_poly_vertices; j++) { 3230 if (*(DWORD*)in_ptr >= mesh_data->num_vertices) { 3231 WARN("face %u, index %u: undefined vertex %u (only %u vertices)\n", 3232 i, j, *(DWORD*)in_ptr, mesh_data->num_vertices); 3233 goto end; 3234 } 3235 in_ptr += sizeof(DWORD); 3236 } 3237 mesh_data->num_tri_faces += num_poly_vertices - 2; 3238 } 3239 3240 mesh_data->fvf = D3DFVF_XYZ; 3241 3242 mesh_data->vertices = HeapAlloc(GetProcessHeap(), 0, 3243 mesh_data->num_vertices * sizeof(*mesh_data->vertices)); 3244 mesh_data->num_tri_per_face = HeapAlloc(GetProcessHeap(), 0, 3245 mesh_data->num_poly_faces * sizeof(*mesh_data->num_tri_per_face)); 3246 mesh_data->indices = HeapAlloc(GetProcessHeap(), 0, 3247 (mesh_data->num_tri_faces + mesh_data->num_poly_faces * 2) * sizeof(*mesh_data->indices)); 3248 if (!mesh_data->vertices || !mesh_data->num_tri_per_face || !mesh_data->indices) { 3249 hr = E_OUTOFMEMORY; 3250 goto end; 3251 } 3252 3253 in_ptr = data + sizeof(DWORD); 3254 memcpy(mesh_data->vertices, in_ptr, mesh_data->num_vertices * sizeof(D3DXVECTOR3)); 3255 in_ptr += mesh_data->num_vertices * sizeof(D3DXVECTOR3) + sizeof(DWORD); 3256 3257 index_out_ptr = mesh_data->indices; 3258 for (i = 0; i < mesh_data->num_poly_faces; i++) 3259 { 3260 DWORD count; 3261 3262 count = *(DWORD*)in_ptr; 3263 in_ptr += sizeof(DWORD); 3264 mesh_data->num_tri_per_face[i] = count - 2; 3265 3266 while (count--) { 3267 *index_out_ptr++ = *(DWORD*)in_ptr; 3268 in_ptr += sizeof(DWORD); 3269 } 3270 } 3271 3272 hr = filedata->lpVtbl->GetChildren(filedata, &nb_children); 3273 if (FAILED(hr)) 3274 goto end; 3275 3276 for (i = 0; i < nb_children; i++) 3277 { 3278 hr = filedata->lpVtbl->GetChild(filedata, i, &child); 3279 if (FAILED(hr)) 3280 goto end; 3281 hr = child->lpVtbl->GetType(child, &type); 3282 if (FAILED(hr)) 3283 goto end; 3284 3285 if (IsEqualGUID(&type, &TID_D3DRMMeshNormals)) { 3286 hr = parse_normals(child, mesh_data); 3287 } else if (IsEqualGUID(&type, &TID_D3DRMMeshVertexColors)) { 3288 hr = parse_vertex_colors(child, mesh_data); 3289 } else if (IsEqualGUID(&type, &TID_D3DRMMeshTextureCoords)) { 3290 hr = parse_texture_coords(child, mesh_data); 3291 } else if (IsEqualGUID(&type, &TID_D3DRMMeshMaterialList) && 3292 (provide_flags & PROVIDE_MATERIALS)) 3293 { 3294 hr = parse_material_list(child, mesh_data); 3295 } else if (provide_flags & PROVIDE_SKININFO) { 3296 if (IsEqualGUID(&type, &DXFILEOBJ_XSkinMeshHeader)) { 3297 if (mesh_data->skin_info) { 3298 WARN("Skin mesh header already encountered\n"); 3299 hr = E_FAIL; 3300 goto end; 3301 } 3302 hr = parse_skin_mesh_info(child, mesh_data, 0); 3303 if (FAILED(hr)) 3304 goto end; 3305 } else if (IsEqualGUID(&type, &DXFILEOBJ_SkinWeights)) { 3306 if (!mesh_data->skin_info) { 3307 WARN("Skin weights found but skin mesh header not encountered yet\n"); 3308 hr = E_FAIL; 3309 goto end; 3310 } 3311 hr = parse_skin_mesh_info(child, mesh_data, nb_skin_weights_info); 3312 if (FAILED(hr)) 3313 goto end; 3314 nb_skin_weights_info++; 3315 } 3316 } 3317 if (FAILED(hr)) 3318 goto end; 3319 3320 IUnknown_Release(child); 3321 child = NULL; 3322 } 3323 3324 if (mesh_data->skin_info && (nb_skin_weights_info != mesh_data->nb_bones)) { 3325 WARN("Mismatch between nb skin weights info %u encountered and nb bones %u from skin mesh header\n", 3326 nb_skin_weights_info, mesh_data->nb_bones); 3327 hr = E_FAIL; 3328 goto end; 3329 } 3330 3331 if ((provide_flags & PROVIDE_SKININFO) && !mesh_data->skin_info) 3332 { 3333 hr = create_dummy_skin(&mesh_data->skin_info); 3334 if (FAILED(hr)) 3335 goto end; 3336 } 3337 3338 hr = D3D_OK; 3339 3340 end: 3341 if (child) 3342 IUnknown_Release(child); 3343 filedata->lpVtbl->Unlock(filedata); 3344 return hr; 3345 } 3346 3347 static HRESULT generate_effects(ID3DXBuffer *materials, DWORD num_materials, 3348 ID3DXBuffer **effects) 3349 { 3350 HRESULT hr; 3351 D3DXEFFECTINSTANCE *effect_ptr; 3352 BYTE *out_ptr; 3353 const D3DXMATERIAL *material_ptr = ID3DXBuffer_GetBufferPointer(materials); 3354 static const struct { 3355 const char *param_name; 3356 DWORD name_size; 3357 DWORD num_bytes; 3358 DWORD value_offset; 3359 } material_effects[] = { 3360 #define EFFECT_TABLE_ENTRY(str, field) \ 3361 {str, sizeof(str), sizeof(material_ptr->MatD3D.field), offsetof(D3DXMATERIAL, MatD3D.field)} 3362 EFFECT_TABLE_ENTRY("Diffuse", Diffuse), 3363 EFFECT_TABLE_ENTRY("Power", Power), 3364 EFFECT_TABLE_ENTRY("Specular", Specular), 3365 EFFECT_TABLE_ENTRY("Emissive", Emissive), 3366 EFFECT_TABLE_ENTRY("Ambient", Ambient), 3367 #undef EFFECT_TABLE_ENTRY 3368 }; 3369 static const char texture_paramname[] = "Texture0@Name"; 3370 DWORD buffer_size; 3371 DWORD i; 3372 3373 /* effects buffer layout: 3374 * 3375 * D3DXEFFECTINSTANCE effects[num_materials]; 3376 * for (effect in effects) 3377 * { 3378 * D3DXEFFECTDEFAULT defaults[effect.NumDefaults]; 3379 * for (default in defaults) 3380 * { 3381 * *default.pParamName; 3382 * *default.pValue; 3383 * } 3384 * } 3385 */ 3386 buffer_size = sizeof(D3DXEFFECTINSTANCE); 3387 buffer_size += sizeof(D3DXEFFECTDEFAULT) * ARRAY_SIZE(material_effects); 3388 for (i = 0; i < ARRAY_SIZE(material_effects); i++) { 3389 buffer_size += material_effects[i].name_size; 3390 buffer_size += material_effects[i].num_bytes; 3391 } 3392 buffer_size *= num_materials; 3393 for (i = 0; i < num_materials; i++) { 3394 if (material_ptr[i].pTextureFilename) { 3395 buffer_size += sizeof(D3DXEFFECTDEFAULT); 3396 buffer_size += sizeof(texture_paramname); 3397 buffer_size += strlen(material_ptr[i].pTextureFilename) + 1; 3398 } 3399 } 3400 3401 hr = D3DXCreateBuffer(buffer_size, effects); 3402 if (FAILED(hr)) return hr; 3403 effect_ptr = ID3DXBuffer_GetBufferPointer(*effects); 3404 out_ptr = (BYTE*)(effect_ptr + num_materials); 3405 3406 for (i = 0; i < num_materials; i++) 3407 { 3408 DWORD j; 3409 D3DXEFFECTDEFAULT *defaults = (D3DXEFFECTDEFAULT*)out_ptr; 3410 3411 effect_ptr->pDefaults = defaults; 3412 effect_ptr->NumDefaults = material_ptr->pTextureFilename ? 6 : 5; 3413 out_ptr = (BYTE*)(effect_ptr->pDefaults + effect_ptr->NumDefaults); 3414 3415 for (j = 0; j < ARRAY_SIZE(material_effects); j++) 3416 { 3417 defaults->pParamName = (char *)out_ptr; 3418 strcpy(defaults->pParamName, material_effects[j].param_name); 3419 defaults->pValue = defaults->pParamName + material_effects[j].name_size; 3420 defaults->Type = D3DXEDT_FLOATS; 3421 defaults->NumBytes = material_effects[j].num_bytes; 3422 memcpy(defaults->pValue, (BYTE*)material_ptr + material_effects[j].value_offset, defaults->NumBytes); 3423 out_ptr = (BYTE*)defaults->pValue + defaults->NumBytes; 3424 defaults++; 3425 } 3426 3427 if (material_ptr->pTextureFilename) 3428 { 3429 defaults->pParamName = (char *)out_ptr; 3430 strcpy(defaults->pParamName, texture_paramname); 3431 defaults->pValue = defaults->pParamName + sizeof(texture_paramname); 3432 defaults->Type = D3DXEDT_STRING; 3433 defaults->NumBytes = strlen(material_ptr->pTextureFilename) + 1; 3434 strcpy(defaults->pValue, material_ptr->pTextureFilename); 3435 out_ptr = (BYTE*)defaults->pValue + defaults->NumBytes; 3436 } 3437 material_ptr++; 3438 effect_ptr++; 3439 } 3440 assert(out_ptr - (BYTE*)ID3DXBuffer_GetBufferPointer(*effects) == buffer_size); 3441 3442 return D3D_OK; 3443 } 3444 3445 HRESULT WINAPI D3DXLoadSkinMeshFromXof(struct ID3DXFileData *filedata, DWORD options, 3446 struct IDirect3DDevice9 *device, struct ID3DXBuffer **adjacency_out, struct ID3DXBuffer **materials_out, 3447 struct ID3DXBuffer **effects_out, DWORD *num_materials_out, struct ID3DXSkinInfo **skin_info_out, 3448 struct ID3DXMesh **mesh_out) 3449 { 3450 HRESULT hr; 3451 DWORD *index_in_ptr; 3452 struct mesh_data mesh_data; 3453 DWORD total_vertices; 3454 ID3DXMesh *d3dxmesh = NULL; 3455 ID3DXBuffer *adjacency = NULL; 3456 ID3DXBuffer *materials = NULL; 3457 ID3DXBuffer *effects = NULL; 3458 struct vertex_duplication { 3459 DWORD normal_index; 3460 struct list entry; 3461 } *duplications = NULL; 3462 DWORD i; 3463 void *vertices = NULL; 3464 void *indices = NULL; 3465 BYTE *out_ptr; 3466 DWORD provide_flags = 0; 3467 3468 TRACE("(%p, %x, %p, %p, %p, %p, %p, %p, %p)\n", filedata, options, device, adjacency_out, materials_out, 3469 effects_out, num_materials_out, skin_info_out, mesh_out); 3470 3471 ZeroMemory(&mesh_data, sizeof(mesh_data)); 3472 3473 if (num_materials_out || materials_out || effects_out) 3474 provide_flags |= PROVIDE_MATERIALS; 3475 if (skin_info_out) 3476 provide_flags |= PROVIDE_SKININFO; 3477 3478 hr = parse_mesh(filedata, &mesh_data, provide_flags); 3479 if (FAILED(hr)) goto cleanup; 3480 3481 total_vertices = mesh_data.num_vertices; 3482 if (mesh_data.fvf & D3DFVF_NORMAL) { 3483 /* duplicate vertices with multiple normals */ 3484 DWORD num_face_indices = mesh_data.num_poly_faces * 2 + mesh_data.num_tri_faces; 3485 duplications = HeapAlloc(GetProcessHeap(), 0, (mesh_data.num_vertices + num_face_indices) * sizeof(*duplications)); 3486 if (!duplications) { 3487 hr = E_OUTOFMEMORY; 3488 goto cleanup; 3489 } 3490 for (i = 0; i < total_vertices; i++) 3491 { 3492 duplications[i].normal_index = -1; 3493 list_init(&duplications[i].entry); 3494 } 3495 for (i = 0; i < num_face_indices; i++) { 3496 DWORD vertex_index = mesh_data.indices[i]; 3497 DWORD normal_index = mesh_data.normal_indices[i]; 3498 struct vertex_duplication *dup_ptr = &duplications[vertex_index]; 3499 3500 if (dup_ptr->normal_index == -1) { 3501 dup_ptr->normal_index = normal_index; 3502 } else { 3503 D3DXVECTOR3 *new_normal = &mesh_data.normals[normal_index]; 3504 struct list *dup_list = &dup_ptr->entry; 3505 while (TRUE) { 3506 D3DXVECTOR3 *cur_normal = &mesh_data.normals[dup_ptr->normal_index]; 3507 if (new_normal->x == cur_normal->x && 3508 new_normal->y == cur_normal->y && 3509 new_normal->z == cur_normal->z) 3510 { 3511 mesh_data.indices[i] = dup_ptr - duplications; 3512 break; 3513 } else if (!list_next(dup_list, &dup_ptr->entry)) { 3514 dup_ptr = &duplications[total_vertices++]; 3515 dup_ptr->normal_index = normal_index; 3516 list_add_tail(dup_list, &dup_ptr->entry); 3517 mesh_data.indices[i] = dup_ptr - duplications; 3518 break; 3519 } else { 3520 dup_ptr = LIST_ENTRY(list_next(dup_list, &dup_ptr->entry), 3521 struct vertex_duplication, entry); 3522 } 3523 } 3524 } 3525 } 3526 } 3527 3528 hr = D3DXCreateMeshFVF(mesh_data.num_tri_faces, total_vertices, options, mesh_data.fvf, device, &d3dxmesh); 3529 if (FAILED(hr)) goto cleanup; 3530 3531 hr = d3dxmesh->lpVtbl->LockVertexBuffer(d3dxmesh, 0, &vertices); 3532 if (FAILED(hr)) goto cleanup; 3533 3534 out_ptr = vertices; 3535 for (i = 0; i < mesh_data.num_vertices; i++) { 3536 *(D3DXVECTOR3*)out_ptr = mesh_data.vertices[i]; 3537 out_ptr += sizeof(D3DXVECTOR3); 3538 if (mesh_data.fvf & D3DFVF_NORMAL) { 3539 if (duplications[i].normal_index == -1) 3540 ZeroMemory(out_ptr, sizeof(D3DXVECTOR3)); 3541 else 3542 *(D3DXVECTOR3*)out_ptr = mesh_data.normals[duplications[i].normal_index]; 3543 out_ptr += sizeof(D3DXVECTOR3); 3544 } 3545 if (mesh_data.fvf & D3DFVF_DIFFUSE) { 3546 *(DWORD*)out_ptr = mesh_data.vertex_colors[i]; 3547 out_ptr += sizeof(DWORD); 3548 } 3549 if (mesh_data.fvf & D3DFVF_TEX1) { 3550 *(D3DXVECTOR2*)out_ptr = mesh_data.tex_coords[i]; 3551 out_ptr += sizeof(D3DXVECTOR2); 3552 } 3553 } 3554 if (mesh_data.fvf & D3DFVF_NORMAL) { 3555 DWORD vertex_size = D3DXGetFVFVertexSize(mesh_data.fvf); 3556 out_ptr = vertices; 3557 for (i = 0; i < mesh_data.num_vertices; i++) { 3558 struct vertex_duplication *dup_ptr; 3559 LIST_FOR_EACH_ENTRY(dup_ptr, &duplications[i].entry, struct vertex_duplication, entry) 3560 { 3561 int j = dup_ptr - duplications; 3562 BYTE *dest_vertex = (BYTE*)vertices + j * vertex_size; 3563 3564 memcpy(dest_vertex, out_ptr, vertex_size); 3565 dest_vertex += sizeof(D3DXVECTOR3); 3566 *(D3DXVECTOR3*)dest_vertex = mesh_data.normals[dup_ptr->normal_index]; 3567 } 3568 out_ptr += vertex_size; 3569 } 3570 } 3571 d3dxmesh->lpVtbl->UnlockVertexBuffer(d3dxmesh); 3572 3573 hr = d3dxmesh->lpVtbl->LockIndexBuffer(d3dxmesh, 0, &indices); 3574 if (FAILED(hr)) goto cleanup; 3575 3576 index_in_ptr = mesh_data.indices; 3577 #define FILL_INDEX_BUFFER(indices_var) \ 3578 for (i = 0; i < mesh_data.num_poly_faces; i++) \ 3579 { \ 3580 DWORD count = mesh_data.num_tri_per_face[i]; \ 3581 WORD first_index = *index_in_ptr++; \ 3582 while (count--) { \ 3583 *indices_var++ = first_index; \ 3584 *indices_var++ = *index_in_ptr; \ 3585 index_in_ptr++; \ 3586 *indices_var++ = *index_in_ptr; \ 3587 } \ 3588 index_in_ptr++; \ 3589 } 3590 if (options & D3DXMESH_32BIT) { 3591 DWORD *dword_indices = indices; 3592 FILL_INDEX_BUFFER(dword_indices) 3593 } else { 3594 WORD *word_indices = indices; 3595 FILL_INDEX_BUFFER(word_indices) 3596 } 3597 #undef FILL_INDEX_BUFFER 3598 d3dxmesh->lpVtbl->UnlockIndexBuffer(d3dxmesh); 3599 3600 if (mesh_data.material_indices) { 3601 DWORD *attrib_buffer = NULL; 3602 hr = d3dxmesh->lpVtbl->LockAttributeBuffer(d3dxmesh, 0, &attrib_buffer); 3603 if (FAILED(hr)) goto cleanup; 3604 for (i = 0; i < mesh_data.num_poly_faces; i++) 3605 { 3606 DWORD count = mesh_data.num_tri_per_face[i]; 3607 while (count--) 3608 *attrib_buffer++ = mesh_data.material_indices[i]; 3609 } 3610 d3dxmesh->lpVtbl->UnlockAttributeBuffer(d3dxmesh); 3611 3612 hr = d3dxmesh->lpVtbl->OptimizeInplace(d3dxmesh, 3613 D3DXMESHOPT_ATTRSORT | D3DXMESHOPT_IGNOREVERTS | D3DXMESHOPT_DONOTSPLIT, 3614 NULL, NULL, NULL, NULL); 3615 if (FAILED(hr)) goto cleanup; 3616 } 3617 3618 if (mesh_data.num_materials && (materials_out || effects_out)) { 3619 DWORD buffer_size = mesh_data.num_materials * sizeof(D3DXMATERIAL); 3620 char *strings_out_ptr; 3621 D3DXMATERIAL *materials_ptr; 3622 3623 for (i = 0; i < mesh_data.num_materials; i++) { 3624 if (mesh_data.materials[i].pTextureFilename) 3625 buffer_size += strlen(mesh_data.materials[i].pTextureFilename) + 1; 3626 } 3627 3628 hr = D3DXCreateBuffer(buffer_size, &materials); 3629 if (FAILED(hr)) goto cleanup; 3630 3631 materials_ptr = ID3DXBuffer_GetBufferPointer(materials); 3632 memcpy(materials_ptr, mesh_data.materials, mesh_data.num_materials * sizeof(D3DXMATERIAL)); 3633 strings_out_ptr = (char*)(materials_ptr + mesh_data.num_materials); 3634 for (i = 0; i < mesh_data.num_materials; i++) { 3635 if (materials_ptr[i].pTextureFilename) { 3636 strcpy(strings_out_ptr, mesh_data.materials[i].pTextureFilename); 3637 materials_ptr[i].pTextureFilename = strings_out_ptr; 3638 strings_out_ptr += strlen(mesh_data.materials[i].pTextureFilename) + 1; 3639 } 3640 } 3641 } 3642 3643 if (mesh_data.num_materials && effects_out) { 3644 hr = generate_effects(materials, mesh_data.num_materials, &effects); 3645 if (FAILED(hr)) goto cleanup; 3646 3647 if (!materials_out) { 3648 ID3DXBuffer_Release(materials); 3649 materials = NULL; 3650 } 3651 } 3652 3653 if (adjacency_out) { 3654 hr = D3DXCreateBuffer(mesh_data.num_tri_faces * 3 * sizeof(DWORD), &adjacency); 3655 if (FAILED(hr)) goto cleanup; 3656 hr = d3dxmesh->lpVtbl->GenerateAdjacency(d3dxmesh, 0.0f, ID3DXBuffer_GetBufferPointer(adjacency)); 3657 if (FAILED(hr)) goto cleanup; 3658 } 3659 3660 *mesh_out = d3dxmesh; 3661 if (adjacency_out) *adjacency_out = adjacency; 3662 if (num_materials_out) *num_materials_out = mesh_data.num_materials; 3663 if (materials_out) *materials_out = materials; 3664 if (effects_out) *effects_out = effects; 3665 if (skin_info_out) *skin_info_out = mesh_data.skin_info; 3666 3667 hr = D3D_OK; 3668 cleanup: 3669 if (FAILED(hr)) { 3670 if (d3dxmesh) IUnknown_Release(d3dxmesh); 3671 if (adjacency) ID3DXBuffer_Release(adjacency); 3672 if (materials) ID3DXBuffer_Release(materials); 3673 if (effects) ID3DXBuffer_Release(effects); 3674 if (mesh_data.skin_info) mesh_data.skin_info->lpVtbl->Release(mesh_data.skin_info); 3675 if (skin_info_out) *skin_info_out = NULL; 3676 } 3677 HeapFree(GetProcessHeap(), 0, mesh_data.vertices); 3678 HeapFree(GetProcessHeap(), 0, mesh_data.num_tri_per_face); 3679 HeapFree(GetProcessHeap(), 0, mesh_data.indices); 3680 HeapFree(GetProcessHeap(), 0, mesh_data.normals); 3681 HeapFree(GetProcessHeap(), 0, mesh_data.normal_indices); 3682 destroy_materials(&mesh_data); 3683 HeapFree(GetProcessHeap(), 0, mesh_data.tex_coords); 3684 HeapFree(GetProcessHeap(), 0, mesh_data.vertex_colors); 3685 HeapFree(GetProcessHeap(), 0, duplications); 3686 return hr; 3687 } 3688 3689 HRESULT WINAPI D3DXLoadMeshHierarchyFromXA(const char *filename, DWORD options, struct IDirect3DDevice9 *device, 3690 struct ID3DXAllocateHierarchy *alloc_hier, struct ID3DXLoadUserData *load_user_data, 3691 D3DXFRAME **frame_hierarchy, struct ID3DXAnimationController **anim_controller) 3692 { 3693 WCHAR *filenameW; 3694 HRESULT hr; 3695 int len; 3696 3697 TRACE("filename %s, options %#x, device %p, alloc_hier %p, " 3698 "load_user_data %p, frame_hierarchy %p, anim_controller %p.\n", 3699 debugstr_a(filename), options, device, alloc_hier, 3700 load_user_data, frame_hierarchy, anim_controller); 3701 3702 if (!filename) 3703 return D3DERR_INVALIDCALL; 3704 3705 len = MultiByteToWideChar(CP_ACP, 0, filename, -1, NULL, 0); 3706 filenameW = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR)); 3707 if (!filenameW) return E_OUTOFMEMORY; 3708 MultiByteToWideChar(CP_ACP, 0, filename, -1, filenameW, len); 3709 3710 hr = D3DXLoadMeshHierarchyFromXW(filenameW, options, device, 3711 alloc_hier, load_user_data, frame_hierarchy, anim_controller); 3712 HeapFree(GetProcessHeap(), 0, filenameW); 3713 3714 return hr; 3715 } 3716 3717 HRESULT WINAPI D3DXLoadMeshHierarchyFromXW(const WCHAR *filename, DWORD options, struct IDirect3DDevice9 *device, 3718 struct ID3DXAllocateHierarchy *alloc_hier, struct ID3DXLoadUserData *load_user_data, 3719 D3DXFRAME **frame_hierarchy, struct ID3DXAnimationController **anim_controller) 3720 { 3721 void *buffer; 3722 HRESULT hr; 3723 DWORD size; 3724 3725 TRACE("filename %s, options %#x, device %p, alloc_hier %p, " 3726 "load_user_data %p, frame_hierarchy %p, anim_controller %p.\n", 3727 debugstr_w(filename), options, device, alloc_hier, 3728 load_user_data, frame_hierarchy, anim_controller); 3729 3730 if (!filename) 3731 return D3DERR_INVALIDCALL; 3732 3733 hr = map_view_of_file(filename, &buffer, &size); 3734 if (FAILED(hr)) 3735 return D3DXERR_INVALIDDATA; 3736 3737 hr = D3DXLoadMeshHierarchyFromXInMemory(buffer, size, options, device, 3738 alloc_hier, load_user_data, frame_hierarchy, anim_controller); 3739 3740 UnmapViewOfFile(buffer); 3741 3742 return hr; 3743 } 3744 3745 static HRESULT filedata_get_name(ID3DXFileData *filedata, char **name) 3746 { 3747 HRESULT hr; 3748 SIZE_T name_len; 3749 3750 hr = filedata->lpVtbl->GetName(filedata, NULL, &name_len); 3751 if (FAILED(hr)) return hr; 3752 3753 if (!name_len) 3754 name_len++; 3755 *name = HeapAlloc(GetProcessHeap(), 0, name_len); 3756 if (!*name) return E_OUTOFMEMORY; 3757 3758 hr = filedata->lpVtbl->GetName(filedata, *name, &name_len); 3759 if (FAILED(hr)) 3760 HeapFree(GetProcessHeap(), 0, *name); 3761 else if (!name_len) 3762 (*name)[0] = 0; 3763 3764 return hr; 3765 } 3766 3767 static HRESULT load_mesh_container(struct ID3DXFileData *filedata, DWORD options, struct IDirect3DDevice9 *device, 3768 struct ID3DXAllocateHierarchy *alloc_hier, D3DXMESHCONTAINER **mesh_container) 3769 { 3770 HRESULT hr; 3771 ID3DXBuffer *adjacency = NULL; 3772 ID3DXBuffer *materials = NULL; 3773 ID3DXBuffer *effects = NULL; 3774 ID3DXSkinInfo *skin_info = NULL; 3775 D3DXMESHDATA mesh_data; 3776 DWORD num_materials = 0; 3777 char *name = NULL; 3778 3779 mesh_data.Type = D3DXMESHTYPE_MESH; 3780 mesh_data.u.pMesh = NULL; 3781 3782 hr = D3DXLoadSkinMeshFromXof(filedata, options, device, 3783 &adjacency, &materials, &effects, &num_materials, 3784 &skin_info, &mesh_data.u.pMesh); 3785 if (FAILED(hr)) return hr; 3786 3787 hr = filedata_get_name(filedata, &name); 3788 if (FAILED(hr)) goto cleanup; 3789 3790 hr = alloc_hier->lpVtbl->CreateMeshContainer(alloc_hier, name, &mesh_data, 3791 materials ? ID3DXBuffer_GetBufferPointer(materials) : NULL, 3792 effects ? ID3DXBuffer_GetBufferPointer(effects) : NULL, 3793 num_materials, 3794 adjacency ? ID3DXBuffer_GetBufferPointer(adjacency) : NULL, 3795 skin_info, mesh_container); 3796 3797 cleanup: 3798 if (materials) ID3DXBuffer_Release(materials); 3799 if (effects) ID3DXBuffer_Release(effects); 3800 if (adjacency) ID3DXBuffer_Release(adjacency); 3801 if (skin_info) IUnknown_Release(skin_info); 3802 if (mesh_data.u.pMesh) IUnknown_Release(mesh_data.u.pMesh); 3803 HeapFree(GetProcessHeap(), 0, name); 3804 return hr; 3805 } 3806 3807 static HRESULT parse_transform_matrix(ID3DXFileData *filedata, D3DXMATRIX *transform) 3808 { 3809 HRESULT hr; 3810 SIZE_T data_size; 3811 const BYTE *data; 3812 3813 /* template Matrix4x4 { 3814 * array FLOAT matrix[16]; 3815 * } 3816 * template FrameTransformMatrix { 3817 * Matrix4x4 frameMatrix; 3818 * } 3819 */ 3820 3821 hr = filedata->lpVtbl->Lock(filedata, &data_size, (const void**)&data); 3822 if (FAILED(hr)) return hr; 3823 3824 if (data_size != sizeof(D3DXMATRIX)) { 3825 WARN("incorrect data size (%ld bytes)\n", data_size); 3826 filedata->lpVtbl->Unlock(filedata); 3827 return E_FAIL; 3828 } 3829 3830 memcpy(transform, data, sizeof(D3DXMATRIX)); 3831 3832 filedata->lpVtbl->Unlock(filedata); 3833 return D3D_OK; 3834 } 3835 3836 static HRESULT load_frame(struct ID3DXFileData *filedata, DWORD options, struct IDirect3DDevice9 *device, 3837 struct ID3DXAllocateHierarchy *alloc_hier, D3DXFRAME **frame_out) 3838 { 3839 HRESULT hr; 3840 GUID type; 3841 ID3DXFileData *child; 3842 char *name = NULL; 3843 D3DXFRAME *frame = NULL; 3844 D3DXMESHCONTAINER **next_container; 3845 D3DXFRAME **next_child; 3846 SIZE_T i, nb_children; 3847 3848 hr = filedata_get_name(filedata, &name); 3849 if (FAILED(hr)) return hr; 3850 3851 hr = alloc_hier->lpVtbl->CreateFrame(alloc_hier, name, frame_out); 3852 HeapFree(GetProcessHeap(), 0, name); 3853 if (FAILED(hr)) return E_FAIL; 3854 3855 frame = *frame_out; 3856 D3DXMatrixIdentity(&frame->TransformationMatrix); 3857 next_child = &frame->pFrameFirstChild; 3858 next_container = &frame->pMeshContainer; 3859 3860 hr = filedata->lpVtbl->GetChildren(filedata, &nb_children); 3861 if (FAILED(hr)) 3862 return hr; 3863 3864 for (i = 0; i < nb_children; i++) 3865 { 3866 hr = filedata->lpVtbl->GetChild(filedata, i, &child); 3867 if (FAILED(hr)) 3868 return hr; 3869 hr = child->lpVtbl->GetType(child, &type); 3870 if (FAILED(hr)) 3871 goto err; 3872 3873 if (IsEqualGUID(&type, &TID_D3DRMMesh)) { 3874 hr = load_mesh_container(child, options, device, alloc_hier, next_container); 3875 if (SUCCEEDED(hr)) 3876 next_container = &(*next_container)->pNextMeshContainer; 3877 } else if (IsEqualGUID(&type, &TID_D3DRMFrameTransformMatrix)) { 3878 hr = parse_transform_matrix(child, &frame->TransformationMatrix); 3879 } else if (IsEqualGUID(&type, &TID_D3DRMFrame)) { 3880 hr = load_frame(child, options, device, alloc_hier, next_child); 3881 if (SUCCEEDED(hr)) 3882 next_child = &(*next_child)->pFrameSibling; 3883 } 3884 if (FAILED(hr)) 3885 goto err; 3886 3887 IUnknown_Release(child); 3888 } 3889 return D3D_OK; 3890 3891 err: 3892 IUnknown_Release(child); 3893 return hr; 3894 } 3895 3896 HRESULT WINAPI D3DXLoadMeshHierarchyFromXInMemory(const void *memory, DWORD memory_size, DWORD options, 3897 struct IDirect3DDevice9 *device, struct ID3DXAllocateHierarchy *alloc_hier, 3898 struct ID3DXLoadUserData *load_user_data, D3DXFRAME **frame_hierarchy, 3899 struct ID3DXAnimationController **anim_controller) 3900 { 3901 HRESULT hr; 3902 ID3DXFile *d3dxfile = NULL; 3903 ID3DXFileEnumObject *enumobj = NULL; 3904 ID3DXFileData *filedata = NULL; 3905 D3DXF_FILELOADMEMORY source; 3906 D3DXFRAME *first_frame = NULL; 3907 D3DXFRAME **next_frame = &first_frame; 3908 SIZE_T i, nb_children; 3909 GUID guid; 3910 3911 TRACE("(%p, %u, %x, %p, %p, %p, %p, %p)\n", memory, memory_size, options, 3912 device, alloc_hier, load_user_data, frame_hierarchy, anim_controller); 3913 3914 if (!memory || !memory_size || !device || !frame_hierarchy || !alloc_hier) 3915 return D3DERR_INVALIDCALL; 3916 if (load_user_data) 3917 { 3918 FIXME("Loading user data not implemented.\n"); 3919 return E_NOTIMPL; 3920 } 3921 3922 hr = D3DXFileCreate(&d3dxfile); 3923 if (FAILED(hr)) goto cleanup; 3924 3925 hr = d3dxfile->lpVtbl->RegisterTemplates(d3dxfile, D3DRM_XTEMPLATES, D3DRM_XTEMPLATE_BYTES); 3926 if (FAILED(hr)) goto cleanup; 3927 3928 source.lpMemory = (void*)memory; 3929 source.dSize = memory_size; 3930 hr = d3dxfile->lpVtbl->CreateEnumObject(d3dxfile, &source, D3DXF_FILELOAD_FROMMEMORY, &enumobj); 3931 if (FAILED(hr)) goto cleanup; 3932 3933 hr = enumobj->lpVtbl->GetChildren(enumobj, &nb_children); 3934 if (FAILED(hr)) 3935 goto cleanup; 3936 3937 for (i = 0; i < nb_children; i++) 3938 { 3939 hr = enumobj->lpVtbl->GetChild(enumobj, i, &filedata); 3940 if (FAILED(hr)) 3941 goto cleanup; 3942 3943 hr = filedata->lpVtbl->GetType(filedata, &guid); 3944 if (SUCCEEDED(hr)) { 3945 if (IsEqualGUID(&guid, &TID_D3DRMMesh)) { 3946 hr = alloc_hier->lpVtbl->CreateFrame(alloc_hier, NULL, next_frame); 3947 if (FAILED(hr)) { 3948 hr = E_FAIL; 3949 goto cleanup; 3950 } 3951 3952 D3DXMatrixIdentity(&(*next_frame)->TransformationMatrix); 3953 3954 hr = load_mesh_container(filedata, options, device, alloc_hier, &(*next_frame)->pMeshContainer); 3955 if (FAILED(hr)) goto cleanup; 3956 } else if (IsEqualGUID(&guid, &TID_D3DRMFrame)) { 3957 hr = load_frame(filedata, options, device, alloc_hier, next_frame); 3958 if (FAILED(hr)) goto cleanup; 3959 } 3960 while (*next_frame) 3961 next_frame = &(*next_frame)->pFrameSibling; 3962 } 3963 3964 filedata->lpVtbl->Release(filedata); 3965 filedata = NULL; 3966 if (FAILED(hr)) 3967 goto cleanup; 3968 } 3969 3970 if (!first_frame) { 3971 hr = E_FAIL; 3972 } else if (first_frame->pFrameSibling) { 3973 D3DXFRAME *root_frame = NULL; 3974 hr = alloc_hier->lpVtbl->CreateFrame(alloc_hier, NULL, &root_frame); 3975 if (FAILED(hr)) { 3976 hr = E_FAIL; 3977 goto cleanup; 3978 } 3979 D3DXMatrixIdentity(&root_frame->TransformationMatrix); 3980 root_frame->pFrameFirstChild = first_frame; 3981 *frame_hierarchy = root_frame; 3982 hr = D3D_OK; 3983 } else { 3984 *frame_hierarchy = first_frame; 3985 hr = D3D_OK; 3986 } 3987 3988 if (anim_controller) 3989 { 3990 *anim_controller = NULL; 3991 FIXME("Animation controller creation not implemented.\n"); 3992 } 3993 3994 cleanup: 3995 if (FAILED(hr) && first_frame) D3DXFrameDestroy(first_frame, alloc_hier); 3996 if (filedata) filedata->lpVtbl->Release(filedata); 3997 if (enumobj) enumobj->lpVtbl->Release(enumobj); 3998 if (d3dxfile) d3dxfile->lpVtbl->Release(d3dxfile); 3999 return hr; 4000 } 4001 4002 HRESULT WINAPI D3DXCleanMesh(D3DXCLEANTYPE clean_type, ID3DXMesh *mesh_in, const DWORD *adjacency_in, 4003 ID3DXMesh **mesh_out, DWORD *adjacency_out, ID3DXBuffer **errors_and_warnings) 4004 { 4005 FIXME("(%u, %p, %p, %p, %p, %p)\n", clean_type, mesh_in, adjacency_in, mesh_out, adjacency_out, errors_and_warnings); 4006 4007 return E_NOTIMPL; 4008 } 4009 4010 HRESULT WINAPI D3DXFrameDestroy(D3DXFRAME *frame, ID3DXAllocateHierarchy *alloc_hier) 4011 { 4012 HRESULT hr; 4013 BOOL last = FALSE; 4014 4015 TRACE("(%p, %p)\n", frame, alloc_hier); 4016 4017 if (!frame || !alloc_hier) 4018 return D3DERR_INVALIDCALL; 4019 4020 while (!last) { 4021 D3DXMESHCONTAINER *container; 4022 D3DXFRAME *current_frame; 4023 4024 if (frame->pFrameSibling) { 4025 current_frame = frame->pFrameSibling; 4026 frame->pFrameSibling = current_frame->pFrameSibling; 4027 current_frame->pFrameSibling = NULL; 4028 } else { 4029 current_frame = frame; 4030 last = TRUE; 4031 } 4032 4033 if (current_frame->pFrameFirstChild) { 4034 hr = D3DXFrameDestroy(current_frame->pFrameFirstChild, alloc_hier); 4035 if (FAILED(hr)) return hr; 4036 current_frame->pFrameFirstChild = NULL; 4037 } 4038 4039 container = current_frame->pMeshContainer; 4040 while (container) { 4041 D3DXMESHCONTAINER *next_container = container->pNextMeshContainer; 4042 hr = alloc_hier->lpVtbl->DestroyMeshContainer(alloc_hier, container); 4043 if (FAILED(hr)) return hr; 4044 container = next_container; 4045 } 4046 hr = alloc_hier->lpVtbl->DestroyFrame(alloc_hier, current_frame); 4047 if (FAILED(hr)) return hr; 4048 } 4049 return D3D_OK; 4050 } 4051 4052 HRESULT WINAPI D3DXLoadMeshFromXA(const char *filename, DWORD options, struct IDirect3DDevice9 *device, 4053 struct ID3DXBuffer **adjacency, struct ID3DXBuffer **materials, struct ID3DXBuffer **effect_instances, 4054 DWORD *num_materials, struct ID3DXMesh **mesh) 4055 { 4056 WCHAR *filenameW; 4057 HRESULT hr; 4058 int len; 4059 4060 TRACE("filename %s, options %#x, device %p, adjacency %p, materials %p, " 4061 "effect_instances %p, num_materials %p, mesh %p.\n", 4062 debugstr_a(filename), options, device, adjacency, materials, 4063 effect_instances, num_materials, mesh); 4064 4065 if (!filename) 4066 return D3DERR_INVALIDCALL; 4067 4068 len = MultiByteToWideChar(CP_ACP, 0, filename, -1, NULL, 0); 4069 filenameW = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR)); 4070 if (!filenameW) return E_OUTOFMEMORY; 4071 MultiByteToWideChar(CP_ACP, 0, filename, -1, filenameW, len); 4072 4073 hr = D3DXLoadMeshFromXW(filenameW, options, device, adjacency, materials, 4074 effect_instances, num_materials, mesh); 4075 HeapFree(GetProcessHeap(), 0, filenameW); 4076 4077 return hr; 4078 } 4079 4080 HRESULT WINAPI D3DXLoadMeshFromXW(const WCHAR *filename, DWORD options, struct IDirect3DDevice9 *device, 4081 struct ID3DXBuffer **adjacency, struct ID3DXBuffer **materials, struct ID3DXBuffer **effect_instances, 4082 DWORD *num_materials, struct ID3DXMesh **mesh) 4083 { 4084 void *buffer; 4085 HRESULT hr; 4086 DWORD size; 4087 4088 TRACE("filename %s, options %#x, device %p, adjacency %p, materials %p, " 4089 "effect_instances %p, num_materials %p, mesh %p.\n", 4090 debugstr_w(filename), options, device, adjacency, materials, 4091 effect_instances, num_materials, mesh); 4092 4093 if (!filename) 4094 return D3DERR_INVALIDCALL; 4095 4096 hr = map_view_of_file(filename, &buffer, &size); 4097 if (FAILED(hr)) 4098 return D3DXERR_INVALIDDATA; 4099 4100 hr = D3DXLoadMeshFromXInMemory(buffer, size, options, device, adjacency, 4101 materials, effect_instances, num_materials, mesh); 4102 4103 UnmapViewOfFile(buffer); 4104 4105 return hr; 4106 } 4107 4108 HRESULT WINAPI D3DXLoadMeshFromXResource(HMODULE module, const char *name, const char *type, DWORD options, 4109 struct IDirect3DDevice9 *device, struct ID3DXBuffer **adjacency, struct ID3DXBuffer **materials, 4110 struct ID3DXBuffer **effect_instances, DWORD *num_materials, struct ID3DXMesh **mesh) 4111 { 4112 HRESULT hr; 4113 HRSRC resinfo; 4114 void *buffer; 4115 DWORD size; 4116 4117 TRACE("module %p, name %s, type %s, options %#x, device %p, adjacency %p, " 4118 "materials %p, effect_instances %p, num_materials %p, mesh %p.\n", 4119 module, debugstr_a(name), debugstr_a(type), options, device, adjacency, 4120 materials, effect_instances, num_materials, mesh); 4121 4122 resinfo = FindResourceA(module, name, type); 4123 if (!resinfo) return D3DXERR_INVALIDDATA; 4124 4125 hr = load_resource_into_memory(module, resinfo, &buffer, &size); 4126 if (FAILED(hr)) return D3DXERR_INVALIDDATA; 4127 4128 return D3DXLoadMeshFromXInMemory(buffer, size, options, device, adjacency, 4129 materials, effect_instances, num_materials, mesh); 4130 } 4131 4132 struct mesh_container 4133 { 4134 struct list entry; 4135 ID3DXMesh *mesh; 4136 ID3DXBuffer *adjacency; 4137 ID3DXBuffer *materials; 4138 ID3DXBuffer *effects; 4139 DWORD num_materials; 4140 D3DXMATRIX transform; 4141 }; 4142 4143 static HRESULT parse_frame(struct ID3DXFileData *filedata, DWORD options, struct IDirect3DDevice9 *device, 4144 const D3DXMATRIX *parent_transform, struct list *container_list, DWORD provide_flags) 4145 { 4146 HRESULT hr; 4147 D3DXMATRIX transform = *parent_transform; 4148 ID3DXFileData *child; 4149 GUID type; 4150 SIZE_T i, nb_children; 4151 4152 hr = filedata->lpVtbl->GetChildren(filedata, &nb_children); 4153 if (FAILED(hr)) 4154 return hr; 4155 4156 for (i = 0; i < nb_children; i++) 4157 { 4158 hr = filedata->lpVtbl->GetChild(filedata, i, &child); 4159 if (FAILED(hr)) 4160 return hr; 4161 hr = child->lpVtbl->GetType(child, &type); 4162 if (FAILED(hr)) 4163 goto err; 4164 4165 if (IsEqualGUID(&type, &TID_D3DRMMesh)) { 4166 struct mesh_container *container = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(*container)); 4167 if (!container) 4168 { 4169 hr = E_OUTOFMEMORY; 4170 goto err; 4171 } 4172 list_add_tail(container_list, &container->entry); 4173 container->transform = transform; 4174 4175 hr = D3DXLoadSkinMeshFromXof(child, options, device, 4176 (provide_flags & PROVIDE_ADJACENCY) ? &container->adjacency : NULL, 4177 (provide_flags & PROVIDE_MATERIALS) ? &container->materials : NULL, 4178 NULL, &container->num_materials, NULL, &container->mesh); 4179 } else if (IsEqualGUID(&type, &TID_D3DRMFrameTransformMatrix)) { 4180 D3DXMATRIX new_transform; 4181 hr = parse_transform_matrix(child, &new_transform); 4182 D3DXMatrixMultiply(&transform, &transform, &new_transform); 4183 } else if (IsEqualGUID(&type, &TID_D3DRMFrame)) { 4184 hr = parse_frame(child, options, device, &transform, container_list, provide_flags); 4185 } 4186 if (FAILED(hr)) 4187 goto err; 4188 4189 IUnknown_Release(child); 4190 } 4191 return D3D_OK; 4192 4193 err: 4194 IUnknown_Release(child); 4195 return hr; 4196 } 4197 4198 HRESULT WINAPI D3DXLoadMeshFromXInMemory(const void *memory, DWORD memory_size, DWORD options, 4199 struct IDirect3DDevice9 *device, struct ID3DXBuffer **adjacency_out, struct ID3DXBuffer **materials_out, 4200 struct ID3DXBuffer **effects_out, DWORD *num_materials_out, struct ID3DXMesh **mesh_out) 4201 { 4202 HRESULT hr; 4203 ID3DXFile *d3dxfile = NULL; 4204 ID3DXFileEnumObject *enumobj = NULL; 4205 ID3DXFileData *filedata = NULL; 4206 D3DXF_FILELOADMEMORY source; 4207 ID3DXBuffer *materials = NULL; 4208 ID3DXBuffer *effects = NULL; 4209 ID3DXBuffer *adjacency = NULL; 4210 struct list container_list = LIST_INIT(container_list); 4211 struct mesh_container *container_ptr, *next_container_ptr; 4212 DWORD num_materials; 4213 DWORD num_faces, num_vertices; 4214 D3DXMATRIX identity; 4215 DWORD provide_flags = 0; 4216 DWORD fvf; 4217 ID3DXMesh *concat_mesh = NULL; 4218 D3DVERTEXELEMENT9 concat_decl[MAX_FVF_DECL_SIZE]; 4219 BYTE *concat_vertices = NULL; 4220 void *concat_indices = NULL; 4221 DWORD index_offset; 4222 DWORD concat_vertex_size; 4223 SIZE_T i, nb_children; 4224 GUID guid; 4225 4226 TRACE("(%p, %u, %x, %p, %p, %p, %p, %p, %p)\n", memory, memory_size, options, 4227 device, adjacency_out, materials_out, effects_out, num_materials_out, mesh_out); 4228 4229 if (!memory || !memory_size || !device || !mesh_out) 4230 return D3DERR_INVALIDCALL; 4231 4232 hr = D3DXFileCreate(&d3dxfile); 4233 if (FAILED(hr)) goto cleanup; 4234 4235 hr = d3dxfile->lpVtbl->RegisterTemplates(d3dxfile, D3DRM_XTEMPLATES, D3DRM_XTEMPLATE_BYTES); 4236 if (FAILED(hr)) goto cleanup; 4237 4238 source.lpMemory = (void*)memory; 4239 source.dSize = memory_size; 4240 hr = d3dxfile->lpVtbl->CreateEnumObject(d3dxfile, &source, D3DXF_FILELOAD_FROMMEMORY, &enumobj); 4241 if (FAILED(hr)) goto cleanup; 4242 4243 D3DXMatrixIdentity(&identity); 4244 if (adjacency_out) provide_flags |= PROVIDE_ADJACENCY; 4245 if (materials_out || effects_out) provide_flags |= PROVIDE_MATERIALS; 4246 4247 hr = enumobj->lpVtbl->GetChildren(enumobj, &nb_children); 4248 if (FAILED(hr)) 4249 goto cleanup; 4250 4251 for (i = 0; i < nb_children; i++) 4252 { 4253 hr = enumobj->lpVtbl->GetChild(enumobj, i, &filedata); 4254 if (FAILED(hr)) 4255 goto cleanup; 4256 4257 hr = filedata->lpVtbl->GetType(filedata, &guid); 4258 if (SUCCEEDED(hr)) { 4259 if (IsEqualGUID(&guid, &TID_D3DRMMesh)) { 4260 container_ptr = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(*container_ptr)); 4261 if (!container_ptr) { 4262 hr = E_OUTOFMEMORY; 4263 goto cleanup; 4264 } 4265 list_add_tail(&container_list, &container_ptr->entry); 4266 D3DXMatrixIdentity(&container_ptr->transform); 4267 4268 hr = D3DXLoadSkinMeshFromXof(filedata, options, device, 4269 (provide_flags & PROVIDE_ADJACENCY) ? &container_ptr->adjacency : NULL, 4270 (provide_flags & PROVIDE_MATERIALS) ? &container_ptr->materials : NULL, 4271 NULL, &container_ptr->num_materials, NULL, &container_ptr->mesh); 4272 } else if (IsEqualGUID(&guid, &TID_D3DRMFrame)) { 4273 hr = parse_frame(filedata, options, device, &identity, &container_list, provide_flags); 4274 } 4275 if (FAILED(hr)) goto cleanup; 4276 } 4277 filedata->lpVtbl->Release(filedata); 4278 filedata = NULL; 4279 if (FAILED(hr)) 4280 goto cleanup; 4281 } 4282 4283 enumobj->lpVtbl->Release(enumobj); 4284 enumobj = NULL; 4285 d3dxfile->lpVtbl->Release(d3dxfile); 4286 d3dxfile = NULL; 4287 4288 if (list_empty(&container_list)) { 4289 hr = E_FAIL; 4290 goto cleanup; 4291 } 4292 4293 fvf = D3DFVF_XYZ; 4294 num_faces = 0; 4295 num_vertices = 0; 4296 num_materials = 0; 4297 LIST_FOR_EACH_ENTRY(container_ptr, &container_list, struct mesh_container, entry) 4298 { 4299 ID3DXMesh *mesh = container_ptr->mesh; 4300 fvf |= mesh->lpVtbl->GetFVF(mesh); 4301 num_faces += mesh->lpVtbl->GetNumFaces(mesh); 4302 num_vertices += mesh->lpVtbl->GetNumVertices(mesh); 4303 num_materials += container_ptr->num_materials; 4304 } 4305 4306 hr = D3DXCreateMeshFVF(num_faces, num_vertices, options, fvf, device, &concat_mesh); 4307 if (FAILED(hr)) goto cleanup; 4308 4309 hr = concat_mesh->lpVtbl->GetDeclaration(concat_mesh, concat_decl); 4310 if (FAILED(hr)) goto cleanup; 4311 4312 concat_vertex_size = D3DXGetDeclVertexSize(concat_decl, 0); 4313 4314 hr = concat_mesh->lpVtbl->LockVertexBuffer(concat_mesh, 0, (void**)&concat_vertices); 4315 if (FAILED(hr)) goto cleanup; 4316 4317 LIST_FOR_EACH_ENTRY(container_ptr, &container_list, struct mesh_container, entry) 4318 { 4319 D3DVERTEXELEMENT9 mesh_decl[MAX_FVF_DECL_SIZE]; 4320 ID3DXMesh *mesh = container_ptr->mesh; 4321 DWORD num_mesh_vertices = mesh->lpVtbl->GetNumVertices(mesh); 4322 DWORD mesh_vertex_size; 4323 const BYTE *mesh_vertices; 4324 DWORD i; 4325 4326 hr = mesh->lpVtbl->GetDeclaration(mesh, mesh_decl); 4327 if (FAILED(hr)) goto cleanup; 4328 4329 mesh_vertex_size = D3DXGetDeclVertexSize(mesh_decl, 0); 4330 4331 hr = mesh->lpVtbl->LockVertexBuffer(mesh, D3DLOCK_READONLY, (void**)&mesh_vertices); 4332 if (FAILED(hr)) goto cleanup; 4333 4334 for (i = 0; i < num_mesh_vertices; i++) { 4335 int j; 4336 int k = 1; 4337 4338 D3DXVec3TransformCoord((D3DXVECTOR3*)concat_vertices, 4339 (D3DXVECTOR3*)mesh_vertices, 4340 &container_ptr->transform); 4341 for (j = 1; concat_decl[j].Stream != 0xff; j++) 4342 { 4343 if (concat_decl[j].Usage == mesh_decl[k].Usage && 4344 concat_decl[j].UsageIndex == mesh_decl[k].UsageIndex) 4345 { 4346 if (concat_decl[j].Usage == D3DDECLUSAGE_NORMAL) { 4347 D3DXVec3TransformCoord((D3DXVECTOR3*)(concat_vertices + concat_decl[j].Offset), 4348 (D3DXVECTOR3*)(mesh_vertices + mesh_decl[k].Offset), 4349 &container_ptr->transform); 4350 } else { 4351 memcpy(concat_vertices + concat_decl[j].Offset, 4352 mesh_vertices + mesh_decl[k].Offset, 4353 d3dx_decltype_size[mesh_decl[k].Type]); 4354 } 4355 k++; 4356 } 4357 } 4358 mesh_vertices += mesh_vertex_size; 4359 concat_vertices += concat_vertex_size; 4360 } 4361 4362 mesh->lpVtbl->UnlockVertexBuffer(mesh); 4363 } 4364 4365 concat_mesh->lpVtbl->UnlockVertexBuffer(concat_mesh); 4366 concat_vertices = NULL; 4367 4368 hr = concat_mesh->lpVtbl->LockIndexBuffer(concat_mesh, 0, &concat_indices); 4369 if (FAILED(hr)) goto cleanup; 4370 4371 index_offset = 0; 4372 LIST_FOR_EACH_ENTRY(container_ptr, &container_list, struct mesh_container, entry) 4373 { 4374 ID3DXMesh *mesh = container_ptr->mesh; 4375 const void *mesh_indices; 4376 DWORD num_mesh_faces = mesh->lpVtbl->GetNumFaces(mesh); 4377 DWORD i; 4378 4379 hr = mesh->lpVtbl->LockIndexBuffer(mesh, D3DLOCK_READONLY, (void**)&mesh_indices); 4380 if (FAILED(hr)) goto cleanup; 4381 4382 if (options & D3DXMESH_32BIT) { 4383 DWORD *dest = concat_indices; 4384 const DWORD *src = mesh_indices; 4385 for (i = 0; i < num_mesh_faces * 3; i++) 4386 *dest++ = index_offset + *src++; 4387 concat_indices = dest; 4388 } else { 4389 WORD *dest = concat_indices; 4390 const WORD *src = mesh_indices; 4391 for (i = 0; i < num_mesh_faces * 3; i++) 4392 *dest++ = index_offset + *src++; 4393 concat_indices = dest; 4394 } 4395 mesh->lpVtbl->UnlockIndexBuffer(mesh); 4396 4397 index_offset += num_mesh_faces * 3; 4398 } 4399 4400 concat_mesh->lpVtbl->UnlockIndexBuffer(concat_mesh); 4401 concat_indices = NULL; 4402 4403 if (num_materials) { 4404 DWORD *concat_attrib_buffer = NULL; 4405 DWORD offset = 0; 4406 4407 hr = concat_mesh->lpVtbl->LockAttributeBuffer(concat_mesh, 0, &concat_attrib_buffer); 4408 if (FAILED(hr)) goto cleanup; 4409 4410 LIST_FOR_EACH_ENTRY(container_ptr, &container_list, struct mesh_container, entry) 4411 { 4412 ID3DXMesh *mesh = container_ptr->mesh; 4413 const DWORD *mesh_attrib_buffer = NULL; 4414 DWORD count = mesh->lpVtbl->GetNumFaces(mesh); 4415 4416 hr = mesh->lpVtbl->LockAttributeBuffer(mesh, D3DLOCK_READONLY, (DWORD**)&mesh_attrib_buffer); 4417 if (FAILED(hr)) { 4418 concat_mesh->lpVtbl->UnlockAttributeBuffer(concat_mesh); 4419 goto cleanup; 4420 } 4421 4422 while (count--) 4423 *concat_attrib_buffer++ = offset + *mesh_attrib_buffer++; 4424 4425 mesh->lpVtbl->UnlockAttributeBuffer(mesh); 4426 offset += container_ptr->num_materials; 4427 } 4428 concat_mesh->lpVtbl->UnlockAttributeBuffer(concat_mesh); 4429 } 4430 4431 if (materials_out || effects_out) { 4432 D3DXMATERIAL *out_ptr; 4433 if (!num_materials) { 4434 /* create default material */ 4435 hr = D3DXCreateBuffer(sizeof(D3DXMATERIAL), &materials); 4436 if (FAILED(hr)) goto cleanup; 4437 4438 out_ptr = ID3DXBuffer_GetBufferPointer(materials); 4439 out_ptr->MatD3D.Diffuse.r = 0.5f; 4440 out_ptr->MatD3D.Diffuse.g = 0.5f; 4441 out_ptr->MatD3D.Diffuse.b = 0.5f; 4442 out_ptr->MatD3D.Specular.r = 0.5f; 4443 out_ptr->MatD3D.Specular.g = 0.5f; 4444 out_ptr->MatD3D.Specular.b = 0.5f; 4445 /* D3DXCreateBuffer initializes the rest to zero */ 4446 } else { 4447 DWORD buffer_size = num_materials * sizeof(D3DXMATERIAL); 4448 char *strings_out_ptr; 4449 4450 LIST_FOR_EACH_ENTRY(container_ptr, &container_list, struct mesh_container, entry) 4451 { 4452 if (container_ptr->materials) { 4453 DWORD i; 4454 const D3DXMATERIAL *in_ptr = ID3DXBuffer_GetBufferPointer(container_ptr->materials); 4455 for (i = 0; i < container_ptr->num_materials; i++) 4456 { 4457 if (in_ptr->pTextureFilename) 4458 buffer_size += strlen(in_ptr->pTextureFilename) + 1; 4459 in_ptr++; 4460 } 4461 } 4462 } 4463 4464 hr = D3DXCreateBuffer(buffer_size, &materials); 4465 if (FAILED(hr)) goto cleanup; 4466 out_ptr = ID3DXBuffer_GetBufferPointer(materials); 4467 strings_out_ptr = (char*)(out_ptr + num_materials); 4468 4469 LIST_FOR_EACH_ENTRY(container_ptr, &container_list, struct mesh_container, entry) 4470 { 4471 if (container_ptr->materials) { 4472 DWORD i; 4473 const D3DXMATERIAL *in_ptr = ID3DXBuffer_GetBufferPointer(container_ptr->materials); 4474 for (i = 0; i < container_ptr->num_materials; i++) 4475 { 4476 out_ptr->MatD3D = in_ptr->MatD3D; 4477 if (in_ptr->pTextureFilename) { 4478 out_ptr->pTextureFilename = strings_out_ptr; 4479 strcpy(out_ptr->pTextureFilename, in_ptr->pTextureFilename); 4480 strings_out_ptr += strlen(in_ptr->pTextureFilename) + 1; 4481 } 4482 in_ptr++; 4483 out_ptr++; 4484 } 4485 } 4486 } 4487 } 4488 } 4489 if (!num_materials) 4490 num_materials = 1; 4491 4492 if (effects_out) { 4493 generate_effects(materials, num_materials, &effects); 4494 if (!materials_out) { 4495 ID3DXBuffer_Release(materials); 4496 materials = NULL; 4497 } 4498 } 4499 4500 if (adjacency_out) { 4501 if (!list_next(&container_list, list_head(&container_list))) { 4502 container_ptr = LIST_ENTRY(list_head(&container_list), struct mesh_container, entry); 4503 adjacency = container_ptr->adjacency; 4504 container_ptr->adjacency = NULL; 4505 } else { 4506 DWORD offset = 0; 4507 DWORD *out_ptr; 4508 4509 hr = D3DXCreateBuffer(num_faces * 3 * sizeof(DWORD), &adjacency); 4510 if (FAILED(hr)) goto cleanup; 4511 4512 out_ptr = ID3DXBuffer_GetBufferPointer(adjacency); 4513 LIST_FOR_EACH_ENTRY(container_ptr, &container_list, struct mesh_container, entry) 4514 { 4515 DWORD i; 4516 DWORD count = 3 * container_ptr->mesh->lpVtbl->GetNumFaces(container_ptr->mesh); 4517 DWORD *in_ptr = ID3DXBuffer_GetBufferPointer(container_ptr->adjacency); 4518 4519 for (i = 0; i < count; i++) 4520 *out_ptr++ = offset + *in_ptr++; 4521 4522 offset += count; 4523 } 4524 } 4525 } 4526 4527 *mesh_out = concat_mesh; 4528 if (adjacency_out) *adjacency_out = adjacency; 4529 if (materials_out) *materials_out = materials; 4530 if (effects_out) *effects_out = effects; 4531 if (num_materials_out) *num_materials_out = num_materials; 4532 4533 hr = D3D_OK; 4534 cleanup: 4535 if (concat_indices) concat_mesh->lpVtbl->UnlockIndexBuffer(concat_mesh); 4536 if (concat_vertices) concat_mesh->lpVtbl->UnlockVertexBuffer(concat_mesh); 4537 if (filedata) filedata->lpVtbl->Release(filedata); 4538 if (enumobj) enumobj->lpVtbl->Release(enumobj); 4539 if (d3dxfile) d3dxfile->lpVtbl->Release(d3dxfile); 4540 if (FAILED(hr)) { 4541 if (concat_mesh) IUnknown_Release(concat_mesh); 4542 if (materials) ID3DXBuffer_Release(materials); 4543 if (effects) ID3DXBuffer_Release(effects); 4544 if (adjacency) ID3DXBuffer_Release(adjacency); 4545 } 4546 LIST_FOR_EACH_ENTRY_SAFE(container_ptr, next_container_ptr, &container_list, struct mesh_container, entry) 4547 { 4548 if (container_ptr->mesh) IUnknown_Release(container_ptr->mesh); 4549 if (container_ptr->adjacency) ID3DXBuffer_Release(container_ptr->adjacency); 4550 if (container_ptr->materials) ID3DXBuffer_Release(container_ptr->materials); 4551 if (container_ptr->effects) ID3DXBuffer_Release(container_ptr->effects); 4552 HeapFree(GetProcessHeap(), 0, container_ptr); 4553 } 4554 return hr; 4555 } 4556 4557 struct vertex 4558 { 4559 D3DXVECTOR3 position; 4560 D3DXVECTOR3 normal; 4561 }; 4562 4563 HRESULT WINAPI D3DXCreatePolygon(struct IDirect3DDevice9 *device, float length, UINT sides, 4564 struct ID3DXMesh **mesh, struct ID3DXBuffer **adjacency) 4565 { 4566 HRESULT hr; 4567 ID3DXMesh *polygon; 4568 struct vertex *vertices; 4569 WORD (*faces)[3]; 4570 DWORD (*adjacency_buf)[3]; 4571 float scale; 4572 unsigned int i; 4573 4574 TRACE("device %p, length %f, sides %u, mesh %p, adjacency %p.\n", 4575 device, length, sides, mesh, adjacency); 4576 4577 if (!device || length < 0.0f || sides < 3 || !mesh) 4578 return D3DERR_INVALIDCALL; 4579 4580 if (FAILED(hr = D3DXCreateMeshFVF(sides, sides + 1, D3DXMESH_MANAGED, 4581 D3DFVF_XYZ | D3DFVF_NORMAL, device, &polygon))) 4582 { 4583 return hr; 4584 } 4585 4586 if (FAILED(hr = polygon->lpVtbl->LockVertexBuffer(polygon, 0, (void **)&vertices))) 4587 { 4588 polygon->lpVtbl->Release(polygon); 4589 return hr; 4590 } 4591 4592 if (FAILED(hr = polygon->lpVtbl->LockIndexBuffer(polygon, 0, (void **)&faces))) 4593 { 4594 polygon->lpVtbl->UnlockVertexBuffer(polygon); 4595 polygon->lpVtbl->Release(polygon); 4596 return hr; 4597 } 4598 4599 scale = 0.5f * length / sinf(D3DX_PI / sides); 4600 4601 vertices[0].position.x = 0.0f; 4602 vertices[0].position.y = 0.0f; 4603 vertices[0].position.z = 0.0f; 4604 vertices[0].normal.x = 0.0f; 4605 vertices[0].normal.y = 0.0f; 4606 vertices[0].normal.z = 1.0f; 4607 4608 for (i = 0; i < sides; ++i) 4609 { 4610 vertices[i + 1].position.x = cosf(2.0f * D3DX_PI * i / sides) * scale; 4611 vertices[i + 1].position.y = sinf(2.0f * D3DX_PI * i / sides) * scale; 4612 vertices[i + 1].position.z = 0.0f; 4613 vertices[i + 1].normal.x = 0.0f; 4614 vertices[i + 1].normal.y = 0.0f; 4615 vertices[i + 1].normal.z = 1.0f; 4616 4617 faces[i][0] = 0; 4618 faces[i][1] = i + 1; 4619 faces[i][2] = i + 2; 4620 } 4621 4622 faces[sides - 1][2] = 1; 4623 4624 polygon->lpVtbl->UnlockVertexBuffer(polygon); 4625 polygon->lpVtbl->UnlockIndexBuffer(polygon); 4626 4627 if (adjacency) 4628 { 4629 if (FAILED(hr = D3DXCreateBuffer(sides * sizeof(DWORD) * 3, adjacency))) 4630 { 4631 polygon->lpVtbl->Release(polygon); 4632 return hr; 4633 } 4634 4635 adjacency_buf = ID3DXBuffer_GetBufferPointer(*adjacency); 4636 for (i = 0; i < sides; ++i) 4637 { 4638 adjacency_buf[i][0] = i - 1; 4639 adjacency_buf[i][1] = ~0U; 4640 adjacency_buf[i][2] = i + 1; 4641 } 4642 adjacency_buf[0][0] = sides - 1; 4643 adjacency_buf[sides - 1][2] = 0; 4644 } 4645 4646 *mesh = polygon; 4647 4648 return D3D_OK; 4649 } 4650 4651 HRESULT WINAPI D3DXCreateBox(struct IDirect3DDevice9 *device, float width, float height, 4652 float depth, struct ID3DXMesh **mesh, struct ID3DXBuffer **adjacency) 4653 { 4654 HRESULT hr; 4655 ID3DXMesh *box; 4656 struct vertex *vertices; 4657 WORD (*faces)[3]; 4658 DWORD *adjacency_buf; 4659 unsigned int i, face; 4660 static const D3DXVECTOR3 unit_box[] = 4661 { 4662 {-0.5f, -0.5f, -0.5f}, {-0.5f, -0.5f, 0.5f}, {-0.5f, 0.5f, 0.5f}, {-0.5f, 0.5f, -0.5f}, 4663 {-0.5f, 0.5f, -0.5f}, {-0.5f, 0.5f, 0.5f}, { 0.5f, 0.5f, 0.5f}, { 0.5f, 0.5f, -0.5f}, 4664 { 0.5f, 0.5f, -0.5f}, { 0.5f, 0.5f, 0.5f}, { 0.5f, -0.5f, 0.5f}, { 0.5f, -0.5f, -0.5f}, 4665 {-0.5f, -0.5f, 0.5f}, {-0.5f, -0.5f, -0.5f}, { 0.5f, -0.5f, -0.5f}, { 0.5f, -0.5f, 0.5f}, 4666 {-0.5f, -0.5f, 0.5f}, { 0.5f, -0.5f, 0.5f}, { 0.5f, 0.5f, 0.5f}, {-0.5f, 0.5f, 0.5f}, 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 }; 4669 static const D3DXVECTOR3 normals[] = 4670 { 4671 {-1.0f, 0.0f, 0.0f}, { 0.0f, 1.0f, 0.0f}, { 1.0f, 0.0f, 0.0f}, 4672 { 0.0f, -1.0f, 0.0f}, { 0.0f, 0.0f, 1.0f}, { 0.0f, 0.0f, -1.0f} 4673 }; 4674 static const DWORD adjacency_table[] = 4675 { 4676 6, 9, 1, 2, 10, 0, 1, 9, 3, 4, 10, 2, 4677 3, 8, 5, 7, 11, 4, 0, 11, 7, 5, 8, 6, 4678 7, 4, 9, 2, 0, 8, 1, 3, 11, 5, 6, 10 4679 }; 4680 4681 TRACE("device %p, width %f, height %f, depth %f, mesh %p, adjacency %p\n", 4682 device, width, height, depth, mesh, adjacency); 4683 4684 if (!device || width < 0.0f || height < 0.0f || depth < 0.0f || !mesh) 4685 { 4686 return D3DERR_INVALIDCALL; 4687 } 4688 4689 if (FAILED(hr = D3DXCreateMeshFVF(12, 24, D3DXMESH_MANAGED, D3DFVF_XYZ | D3DFVF_NORMAL, device, &box))) 4690 { 4691 return hr; 4692 } 4693 4694 if (FAILED(hr = box->lpVtbl->LockVertexBuffer(box, 0, (void **)&vertices))) 4695 { 4696 box->lpVtbl->Release(box); 4697 return hr; 4698 } 4699 4700 if (FAILED(hr = box->lpVtbl->LockIndexBuffer(box, 0, (void **)&faces))) 4701 { 4702 box->lpVtbl->UnlockVertexBuffer(box); 4703 box->lpVtbl->Release(box); 4704 return hr; 4705 } 4706 4707 for (i = 0; i < 24; i++) 4708 { 4709 vertices[i].position.x = width * unit_box[i].x; 4710 vertices[i].position.y = height * unit_box[i].y; 4711 vertices[i].position.z = depth * unit_box[i].z; 4712 vertices[i].normal.x = normals[i / 4].x; 4713 vertices[i].normal.y = normals[i / 4].y; 4714 vertices[i].normal.z = normals[i / 4].z; 4715 } 4716 4717 face = 0; 4718 for (i = 0; i < 12; i++) 4719 { 4720 faces[i][0] = face++; 4721 faces[i][1] = face++; 4722 faces[i][2] = (i % 2) ? face - 4 : face; 4723 } 4724 4725 box->lpVtbl->UnlockIndexBuffer(box); 4726 box->lpVtbl->UnlockVertexBuffer(box); 4727 4728 if (adjacency) 4729 { 4730 if (FAILED(hr = D3DXCreateBuffer(sizeof(adjacency_table), adjacency))) 4731 { 4732 box->lpVtbl->Release(box); 4733 return hr; 4734 } 4735 4736 adjacency_buf = ID3DXBuffer_GetBufferPointer(*adjacency); 4737 memcpy(adjacency_buf, adjacency_table, sizeof(adjacency_table)); 4738 } 4739 4740 *mesh = box; 4741 4742 return D3D_OK; 4743 } 4744 4745 typedef WORD face[3]; 4746 4747 struct sincos_table 4748 { 4749 float *sin; 4750 float *cos; 4751 }; 4752 4753 static void free_sincos_table(struct sincos_table *sincos_table) 4754 { 4755 HeapFree(GetProcessHeap(), 0, sincos_table->cos); 4756 HeapFree(GetProcessHeap(), 0, sincos_table->sin); 4757 } 4758 4759 /* pre compute sine and cosine tables; caller must free */ 4760 static BOOL compute_sincos_table(struct sincos_table *sincos_table, float angle_start, float angle_step, int n) 4761 { 4762 float angle; 4763 int i; 4764 4765 sincos_table->sin = HeapAlloc(GetProcessHeap(), 0, n * sizeof(*sincos_table->sin)); 4766 if (!sincos_table->sin) 4767 { 4768 return FALSE; 4769 } 4770 sincos_table->cos = HeapAlloc(GetProcessHeap(), 0, n * sizeof(*sincos_table->cos)); 4771 if (!sincos_table->cos) 4772 { 4773 HeapFree(GetProcessHeap(), 0, sincos_table->sin); 4774 return FALSE; 4775 } 4776 4777 angle = angle_start; 4778 for (i = 0; i < n; i++) 4779 { 4780 sincos_table->sin[i] = sinf(angle); 4781 sincos_table->cos[i] = cosf(angle); 4782 angle += angle_step; 4783 } 4784 4785 return TRUE; 4786 } 4787 4788 static WORD vertex_index(UINT slices, int slice, int stack) 4789 { 4790 return stack*slices+slice+1; 4791 } 4792 4793 HRESULT WINAPI D3DXCreateSphere(struct IDirect3DDevice9 *device, float radius, UINT slices, 4794 UINT stacks, struct ID3DXMesh **mesh, struct ID3DXBuffer **adjacency) 4795 { 4796 DWORD number_of_vertices, number_of_faces; 4797 HRESULT hr; 4798 ID3DXMesh *sphere; 4799 struct vertex *vertices; 4800 face *faces; 4801 float phi_step, phi_start; 4802 struct sincos_table phi; 4803 float theta_step, theta, sin_theta, cos_theta; 4804 DWORD vertex, face, stack, slice; 4805 4806 TRACE("(%p, %f, %u, %u, %p, %p)\n", device, radius, slices, stacks, mesh, adjacency); 4807 4808 if (!device || radius < 0.0f || slices < 2 || stacks < 2 || !mesh) 4809 { 4810 return D3DERR_INVALIDCALL; 4811 } 4812 4813 number_of_vertices = 2 + slices * (stacks-1); 4814 number_of_faces = 2 * slices + (stacks - 2) * (2 * slices); 4815 4816 hr = D3DXCreateMeshFVF(number_of_faces, number_of_vertices, D3DXMESH_MANAGED, 4817 D3DFVF_XYZ | D3DFVF_NORMAL, device, &sphere); 4818 if (FAILED(hr)) 4819 { 4820 return hr; 4821 } 4822 4823 if (FAILED(hr = sphere->lpVtbl->LockVertexBuffer(sphere, 0, (void **)&vertices))) 4824 { 4825 sphere->lpVtbl->Release(sphere); 4826 return hr; 4827 } 4828 4829 if (FAILED(hr = sphere->lpVtbl->LockIndexBuffer(sphere, 0, (void **)&faces))) 4830 { 4831 sphere->lpVtbl->UnlockVertexBuffer(sphere); 4832 sphere->lpVtbl->Release(sphere); 4833 return hr; 4834 } 4835 4836 /* phi = angle on xz plane wrt z axis */ 4837 phi_step = -2.0f * D3DX_PI / slices; 4838 phi_start = D3DX_PI / 2.0f; 4839 4840 if (!compute_sincos_table(&phi, phi_start, phi_step, slices)) 4841 { 4842 sphere->lpVtbl->UnlockIndexBuffer(sphere); 4843 sphere->lpVtbl->UnlockVertexBuffer(sphere); 4844 sphere->lpVtbl->Release(sphere); 4845 return E_OUTOFMEMORY; 4846 } 4847 4848 /* theta = angle on xy plane wrt x axis */ 4849 theta_step = D3DX_PI / stacks; 4850 theta = theta_step; 4851 4852 vertex = 0; 4853 face = 0; 4854 4855 vertices[vertex].normal.x = 0.0f; 4856 vertices[vertex].normal.y = 0.0f; 4857 vertices[vertex].normal.z = 1.0f; 4858 vertices[vertex].position.x = 0.0f; 4859 vertices[vertex].position.y = 0.0f; 4860 vertices[vertex].position.z = radius; 4861 vertex++; 4862 4863 for (stack = 0; stack < stacks - 1; stack++) 4864 { 4865 sin_theta = sinf(theta); 4866 cos_theta = cosf(theta); 4867 4868 for (slice = 0; slice < slices; slice++) 4869 { 4870 vertices[vertex].normal.x = sin_theta * phi.cos[slice]; 4871 vertices[vertex].normal.y = sin_theta * phi.sin[slice]; 4872 vertices[vertex].normal.z = cos_theta; 4873 vertices[vertex].position.x = radius * sin_theta * phi.cos[slice]; 4874 vertices[vertex].position.y = radius * sin_theta * phi.sin[slice]; 4875 vertices[vertex].position.z = radius * cos_theta; 4876 vertex++; 4877 4878 if (slice > 0) 4879 { 4880 if (stack == 0) 4881 { 4882 /* top stack is triangle fan */ 4883 faces[face][0] = 0; 4884 faces[face][1] = slice + 1; 4885 faces[face][2] = slice; 4886 face++; 4887 } 4888 else 4889 { 4890 /* stacks in between top and bottom are quad strips */ 4891 faces[face][0] = vertex_index(slices, slice-1, stack-1); 4892 faces[face][1] = vertex_index(slices, slice, stack-1); 4893 faces[face][2] = vertex_index(slices, slice-1, stack); 4894 face++; 4895 4896 faces[face][0] = vertex_index(slices, slice, stack-1); 4897 faces[face][1] = vertex_index(slices, slice, stack); 4898 faces[face][2] = vertex_index(slices, slice-1, stack); 4899 face++; 4900 } 4901 } 4902 } 4903 4904 theta += theta_step; 4905 4906 if (stack == 0) 4907 { 4908 faces[face][0] = 0; 4909 faces[face][1] = 1; 4910 faces[face][2] = slice; 4911 face++; 4912 } 4913 else 4914 { 4915 faces[face][0] = vertex_index(slices, slice-1, stack-1); 4916 faces[face][1] = vertex_index(slices, 0, stack-1); 4917 faces[face][2] = vertex_index(slices, slice-1, stack); 4918 face++; 4919 4920 faces[face][0] = vertex_index(slices, 0, stack-1); 4921 faces[face][1] = vertex_index(slices, 0, stack); 4922 faces[face][2] = vertex_index(slices, slice-1, stack); 4923 face++; 4924 } 4925 } 4926 4927 vertices[vertex].position.x = 0.0f; 4928 vertices[vertex].position.y = 0.0f; 4929 vertices[vertex].position.z = -radius; 4930 vertices[vertex].normal.x = 0.0f; 4931 vertices[vertex].normal.y = 0.0f; 4932 vertices[vertex].normal.z = -1.0f; 4933 4934 /* bottom stack is triangle fan */ 4935 for (slice = 1; slice < slices; slice++) 4936 { 4937 faces[face][0] = vertex_index(slices, slice-1, stack-1); 4938 faces[face][1] = vertex_index(slices, slice, stack-1); 4939 faces[face][2] = vertex; 4940 face++; 4941 } 4942 4943 faces[face][0] = vertex_index(slices, slice-1, stack-1); 4944 faces[face][1] = vertex_index(slices, 0, stack-1); 4945 faces[face][2] = vertex; 4946 4947 free_sincos_table(&phi); 4948 sphere->lpVtbl->UnlockIndexBuffer(sphere); 4949 sphere->lpVtbl->UnlockVertexBuffer(sphere); 4950 4951 4952 if (adjacency) 4953 { 4954 if (FAILED(hr = D3DXCreateBuffer(number_of_faces * sizeof(DWORD) * 3, adjacency))) 4955 { 4956 sphere->lpVtbl->Release(sphere); 4957 return hr; 4958 } 4959 4960 if (FAILED(hr = sphere->lpVtbl->GenerateAdjacency(sphere, 0.0f, (*adjacency)->lpVtbl->GetBufferPointer(*adjacency)))) 4961 { 4962 (*adjacency)->lpVtbl->Release(*adjacency); 4963 sphere->lpVtbl->Release(sphere); 4964 return hr; 4965 } 4966 } 4967 4968 *mesh = sphere; 4969 4970 return D3D_OK; 4971 } 4972 4973 HRESULT WINAPI D3DXCreateCylinder(struct IDirect3DDevice9 *device, float radius1, float radius2, 4974 float length, UINT slices, UINT stacks, struct ID3DXMesh **mesh, struct ID3DXBuffer **adjacency) 4975 { 4976 DWORD number_of_vertices, number_of_faces; 4977 HRESULT hr; 4978 ID3DXMesh *cylinder; 4979 struct vertex *vertices; 4980 face *faces; 4981 float theta_step, theta_start; 4982 struct sincos_table theta; 4983 float delta_radius, radius, radius_step; 4984 float z, z_step, z_normal; 4985 DWORD vertex, face, slice, stack; 4986 4987 TRACE("(%p, %f, %f, %f, %u, %u, %p, %p)\n", device, radius1, radius2, length, slices, stacks, mesh, adjacency); 4988 4989 if (device == NULL || radius1 < 0.0f || radius2 < 0.0f || length < 0.0f || slices < 2 || stacks < 1 || mesh == NULL) 4990 { 4991 return D3DERR_INVALIDCALL; 4992 } 4993 4994 number_of_vertices = 2 + (slices * (3 + stacks)); 4995 number_of_faces = 2 * slices + stacks * (2 * slices); 4996 4997 hr = D3DXCreateMeshFVF(number_of_faces, number_of_vertices, D3DXMESH_MANAGED, 4998 D3DFVF_XYZ | D3DFVF_NORMAL, device, &cylinder); 4999 if (FAILED(hr)) 5000 { 5001 return hr; 5002 } 5003 5004 if (FAILED(hr = cylinder->lpVtbl->LockVertexBuffer(cylinder, 0, (void **)&vertices))) 5005 { 5006 cylinder->lpVtbl->Release(cylinder); 5007 return hr; 5008 } 5009 5010 if (FAILED(hr = cylinder->lpVtbl->LockIndexBuffer(cylinder, 0, (void **)&faces))) 5011 { 5012 cylinder->lpVtbl->UnlockVertexBuffer(cylinder); 5013 cylinder->lpVtbl->Release(cylinder); 5014 return hr; 5015 } 5016 5017 /* theta = angle on xy plane wrt x axis */ 5018 theta_step = -2.0f * D3DX_PI / slices; 5019 theta_start = D3DX_PI / 2.0f; 5020 5021 if (!compute_sincos_table(&theta, theta_start, theta_step, slices)) 5022 { 5023 cylinder->lpVtbl->UnlockIndexBuffer(cylinder); 5024 cylinder->lpVtbl->UnlockVertexBuffer(cylinder); 5025 cylinder->lpVtbl->Release(cylinder); 5026 return E_OUTOFMEMORY; 5027 } 5028 5029 vertex = 0; 5030 face = 0; 5031 5032 delta_radius = radius1 - radius2; 5033 radius = radius1; 5034 radius_step = delta_radius / stacks; 5035 5036 z = -length / 2; 5037 z_step = length / stacks; 5038 z_normal = delta_radius / length; 5039 if (isnan(z_normal)) 5040 { 5041 z_normal = 0.0f; 5042 } 5043 5044 vertices[vertex].normal.x = 0.0f; 5045 vertices[vertex].normal.y = 0.0f; 5046 vertices[vertex].normal.z = -1.0f; 5047 vertices[vertex].position.x = 0.0f; 5048 vertices[vertex].position.y = 0.0f; 5049 vertices[vertex++].position.z = z; 5050 5051 for (slice = 0; slice < slices; slice++, vertex++) 5052 { 5053 vertices[vertex].normal.x = 0.0f; 5054 vertices[vertex].normal.y = 0.0f; 5055 vertices[vertex].normal.z = -1.0f; 5056 vertices[vertex].position.x = radius * theta.cos[slice]; 5057 vertices[vertex].position.y = radius * theta.sin[slice]; 5058 vertices[vertex].position.z = z; 5059 5060 if (slice > 0) 5061 { 5062 faces[face][0] = 0; 5063 faces[face][1] = slice; 5064 faces[face++][2] = slice + 1; 5065 } 5066 } 5067 5068 faces[face][0] = 0; 5069 faces[face][1] = slice; 5070 faces[face++][2] = 1; 5071 5072 for (stack = 1; stack <= stacks+1; stack++) 5073 { 5074 for (slice = 0; slice < slices; slice++, vertex++) 5075 { 5076 vertices[vertex].normal.x = theta.cos[slice]; 5077 vertices[vertex].normal.y = theta.sin[slice]; 5078 vertices[vertex].normal.z = z_normal; 5079 D3DXVec3Normalize(&vertices[vertex].normal, &vertices[vertex].normal); 5080 vertices[vertex].position.x = radius * theta.cos[slice]; 5081 vertices[vertex].position.y = radius * theta.sin[slice]; 5082 vertices[vertex].position.z = z; 5083 5084 if (stack > 1 && slice > 0) 5085 { 5086 faces[face][0] = vertex_index(slices, slice-1, stack-1); 5087 faces[face][1] = vertex_index(slices, slice-1, stack); 5088 faces[face++][2] = vertex_index(slices, slice, stack-1); 5089 5090 faces[face][0] = vertex_index(slices, slice, stack-1); 5091 faces[face][1] = vertex_index(slices, slice-1, stack); 5092 faces[face++][2] = vertex_index(slices, slice, stack); 5093 } 5094 } 5095 5096 if (stack > 1) 5097 { 5098 faces[face][0] = vertex_index(slices, slice-1, stack-1); 5099 faces[face][1] = vertex_index(slices, slice-1, stack); 5100 faces[face++][2] = vertex_index(slices, 0, stack-1); 5101 5102 faces[face][0] = vertex_index(slices, 0, stack-1); 5103 faces[face][1] = vertex_index(slices, slice-1, stack); 5104 faces[face++][2] = vertex_index(slices, 0, stack); 5105 } 5106 5107 if (stack < stacks + 1) 5108 { 5109 z += z_step; 5110 radius -= radius_step; 5111 } 5112 } 5113 5114 for (slice = 0; slice < slices; slice++, vertex++) 5115 { 5116 vertices[vertex].normal.x = 0.0f; 5117 vertices[vertex].normal.y = 0.0f; 5118 vertices[vertex].normal.z = 1.0f; 5119 vertices[vertex].position.x = radius * theta.cos[slice]; 5120 vertices[vertex].position.y = radius * theta.sin[slice]; 5121 vertices[vertex].position.z = z; 5122 5123 if (slice > 0) 5124 { 5125 faces[face][0] = vertex_index(slices, slice-1, stack); 5126 faces[face][1] = number_of_vertices - 1; 5127 faces[face++][2] = vertex_index(slices, slice, stack); 5128 } 5129 } 5130 5131 vertices[vertex].position.x = 0.0f; 5132 vertices[vertex].position.y = 0.0f; 5133 vertices[vertex].position.z = z; 5134 vertices[vertex].normal.x = 0.0f; 5135 vertices[vertex].normal.y = 0.0f; 5136 vertices[vertex].normal.z = 1.0f; 5137 5138 faces[face][0] = vertex_index(slices, slice-1, stack); 5139 faces[face][1] = number_of_vertices - 1; 5140 faces[face][2] = vertex_index(slices, 0, stack); 5141 5142 free_sincos_table(&theta); 5143 cylinder->lpVtbl->UnlockIndexBuffer(cylinder); 5144 cylinder->lpVtbl->UnlockVertexBuffer(cylinder); 5145 5146 if (adjacency) 5147 { 5148 if (FAILED(hr = D3DXCreateBuffer(number_of_faces * sizeof(DWORD) * 3, adjacency))) 5149 { 5150 cylinder->lpVtbl->Release(cylinder); 5151 return hr; 5152 } 5153 5154 if (FAILED(hr = cylinder->lpVtbl->GenerateAdjacency(cylinder, 0.0f, (*adjacency)->lpVtbl->GetBufferPointer(*adjacency)))) 5155 { 5156 (*adjacency)->lpVtbl->Release(*adjacency); 5157 cylinder->lpVtbl->Release(cylinder); 5158 return hr; 5159 } 5160 } 5161 5162 *mesh = cylinder; 5163 5164 return D3D_OK; 5165 } 5166 5167 HRESULT WINAPI D3DXCreateTeapot(struct IDirect3DDevice9 *device, 5168 struct ID3DXMesh **mesh, struct ID3DXBuffer **adjacency) 5169 { 5170 FIXME("(%p, %p, %p): stub\n", device, mesh, adjacency); 5171 5172 return D3DXCreateSphere(device, 1.0f, 4, 4, mesh, adjacency); 5173 } 5174 5175 HRESULT WINAPI D3DXCreateTextA(struct IDirect3DDevice9 *device, HDC hdc, const char *text, float deviation, 5176 float extrusion, struct ID3DXMesh **mesh, struct ID3DXBuffer **adjacency, GLYPHMETRICSFLOAT *glyphmetrics) 5177 { 5178 WCHAR *textW; 5179 HRESULT hr; 5180 int len; 5181 5182 TRACE("device %p, hdc %p, text %s, deviation %.8e, extrusion %.8e, mesh %p, adjacency %p, glyphmetrics %p.\n", 5183 device, hdc, debugstr_a(text), deviation, extrusion, mesh, adjacency, glyphmetrics); 5184 5185 if (!text) 5186 return D3DERR_INVALIDCALL; 5187 5188 len = MultiByteToWideChar(CP_ACP, 0, text, -1, NULL, 0); 5189 textW = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR)); 5190 MultiByteToWideChar(CP_ACP, 0, text, -1, textW, len); 5191 5192 hr = D3DXCreateTextW(device, hdc, textW, deviation, extrusion, 5193 mesh, adjacency, glyphmetrics); 5194 HeapFree(GetProcessHeap(), 0, textW); 5195 5196 return hr; 5197 } 5198 5199 HRESULT WINAPI D3DXCreateTorus(struct IDirect3DDevice9 *device, 5200 float innerradius, float outerradius, UINT sides, UINT rings, struct ID3DXMesh **mesh, ID3DXBuffer **adjacency) 5201 { 5202 HRESULT hr; 5203 ID3DXMesh *torus; 5204 WORD (*faces)[3]; 5205 struct vertex *vertices; 5206 float phi, phi_step, sin_phi, cos_phi; 5207 float theta, theta_step, sin_theta, cos_theta; 5208 unsigned int i, j, numvert, numfaces; 5209 5210 TRACE("device %p, innerradius %.8e, outerradius %.8e, sides %u, rings %u, mesh %p, adjacency %p.\n", 5211 device, innerradius, outerradius, sides, rings, mesh, adjacency); 5212 5213 numvert = sides * rings; 5214 numfaces = numvert * 2; 5215 5216 if (!device || innerradius < 0.0f || outerradius < 0.0f || sides < 3 || rings < 3 || !mesh) 5217 { 5218 WARN("Invalid arguments.\n"); 5219 return D3DERR_INVALIDCALL; 5220 } 5221 5222 if (FAILED(hr = D3DXCreateMeshFVF(numfaces, numvert, D3DXMESH_MANAGED, D3DFVF_XYZ | D3DFVF_NORMAL, device, &torus))) 5223 return hr; 5224 5225 if (FAILED(hr = torus->lpVtbl->LockVertexBuffer(torus, 0, (void **)&vertices))) 5226 { 5227 torus->lpVtbl->Release(torus); 5228 return hr; 5229 } 5230 5231 if (FAILED(hr = torus->lpVtbl->LockIndexBuffer(torus, 0, (void **)&faces))) 5232 { 5233 torus->lpVtbl->UnlockVertexBuffer(torus); 5234 torus->lpVtbl->Release(torus); 5235 return hr; 5236 } 5237 5238 phi_step = D3DX_PI / sides * 2.0f; 5239 theta_step = D3DX_PI / rings * -2.0f; 5240 5241 theta = 0.0f; 5242 5243 for (i = 0; i < rings; ++i) 5244 { 5245 phi = 0.0f; 5246 5247 sin_theta = sinf(theta); 5248 cos_theta = cosf(theta); 5249 5250 for (j = 0; j < sides; ++j) 5251 { 5252 sin_phi = sinf(phi); 5253 cos_phi = cosf(phi); 5254 5255 vertices[i * sides + j].position.x = (innerradius * cos_phi + outerradius) * cos_theta; 5256 vertices[i * sides + j].position.y = (innerradius * cos_phi + outerradius) * sin_theta; 5257 vertices[i * sides + j].position.z = innerradius * sin_phi; 5258 vertices[i * sides + j].normal.x = cos_phi * cos_theta; 5259 vertices[i * sides + j].normal.y = cos_phi * sin_theta; 5260 vertices[i * sides + j].normal.z = sin_phi; 5261 5262 phi += phi_step; 5263 } 5264 5265 theta += theta_step; 5266 } 5267 5268 for (i = 0; i < numfaces - sides * 2; ++i) 5269 { 5270 faces[i][0] = i % 2 ? i / 2 + sides : i / 2; 5271 faces[i][1] = (i / 2 + 1) % sides ? i / 2 + 1 : i / 2 + 1 - sides; 5272 faces[i][2] = (i + 1) % (sides * 2) ? (i + 1) / 2 + sides : (i + 1) / 2; 5273 } 5274 5275 for (j = 0; i < numfaces; ++i, ++j) 5276 { 5277 faces[i][0] = i % 2 ? j / 2 : i / 2; 5278 faces[i][1] = (i / 2 + 1) % sides ? i / 2 + 1 : i / 2 + 1 - sides; 5279 faces[i][2] = i == numfaces - 1 ? 0 : (j + 1) / 2; 5280 } 5281 5282 torus->lpVtbl->UnlockIndexBuffer(torus); 5283 torus->lpVtbl->UnlockVertexBuffer(torus); 5284 5285 if (adjacency) 5286 { 5287 if (FAILED(hr = D3DXCreateBuffer(numfaces * sizeof(DWORD) * 3, adjacency))) 5288 { 5289 torus->lpVtbl->Release(torus); 5290 return hr; 5291 } 5292 5293 if (FAILED(hr = torus->lpVtbl->GenerateAdjacency(torus, 0.0f, (*adjacency)->lpVtbl->GetBufferPointer(*adjacency)))) 5294 { 5295 (*adjacency)->lpVtbl->Release(*adjacency); 5296 torus->lpVtbl->Release(torus); 5297 return hr; 5298 } 5299 } 5300 5301 *mesh = torus; 5302 5303 return D3D_OK; 5304 } 5305 5306 enum pointtype { 5307 POINTTYPE_CURVE = 0, 5308 POINTTYPE_CORNER, 5309 POINTTYPE_CURVE_START, 5310 POINTTYPE_CURVE_END, 5311 POINTTYPE_CURVE_MIDDLE, 5312 }; 5313 5314 struct point2d 5315 { 5316 D3DXVECTOR2 pos; 5317 enum pointtype corner; 5318 }; 5319 5320 struct dynamic_array 5321 { 5322 int count, capacity; 5323 void *items; 5324 }; 5325 5326 /* is a dynamic_array */ 5327 struct outline 5328 { 5329 int count, capacity; 5330 struct point2d *items; 5331 }; 5332 5333 /* is a dynamic_array */ 5334 struct outline_array 5335 { 5336 int count, capacity; 5337 struct outline *items; 5338 }; 5339 5340 struct face_array 5341 { 5342 int count; 5343 face *items; 5344 }; 5345 5346 struct point2d_index 5347 { 5348 struct outline *outline; 5349 int vertex; 5350 }; 5351 5352 struct point2d_index_array 5353 { 5354 int count; 5355 struct point2d_index *items; 5356 }; 5357 5358 struct glyphinfo 5359 { 5360 struct outline_array outlines; 5361 struct face_array faces; 5362 struct point2d_index_array ordered_vertices; 5363 float offset_x; 5364 }; 5365 5366 /* is an dynamic_array */ 5367 struct word_array 5368 { 5369 int count, capacity; 5370 WORD *items; 5371 }; 5372 5373 /* complex polygons are split into monotone polygons, which have 5374 * at most 2 intersections with the vertical sweep line */ 5375 struct triangulation 5376 { 5377 struct word_array vertex_stack; 5378 BOOL last_on_top, merging; 5379 }; 5380 5381 /* is an dynamic_array */ 5382 struct triangulation_array 5383 { 5384 int count, capacity; 5385 struct triangulation *items; 5386 5387 struct glyphinfo *glyph; 5388 }; 5389 5390 static BOOL reserve(struct dynamic_array *array, int count, int itemsize) 5391 { 5392 if (count > array->capacity) { 5393 void *new_buffer; 5394 int new_capacity; 5395 if (array->items && array->capacity) { 5396 new_capacity = max(array->capacity * 2, count); 5397 new_buffer = HeapReAlloc(GetProcessHeap(), 0, array->items, new_capacity * itemsize); 5398 } else { 5399 new_capacity = max(16, count); 5400 new_buffer = HeapAlloc(GetProcessHeap(), 0, new_capacity * itemsize); 5401 } 5402 if (!new_buffer) 5403 return FALSE; 5404 array->items = new_buffer; 5405 array->capacity = new_capacity; 5406 } 5407 return TRUE; 5408 } 5409 5410 static struct point2d *add_points(struct outline *array, int num) 5411 { 5412 struct point2d *item; 5413 5414 if (!reserve((struct dynamic_array *)array, array->count + num, sizeof(array->items[0]))) 5415 return NULL; 5416 5417 item = &array->items[array->count]; 5418 array->count += num; 5419 return item; 5420 } 5421 5422 static struct outline *add_outline(struct outline_array *array) 5423 { 5424 struct outline *item; 5425 5426 if (!reserve((struct dynamic_array *)array, array->count + 1, sizeof(array->items[0]))) 5427 return NULL; 5428 5429 item = &array->items[array->count++]; 5430 ZeroMemory(item, sizeof(*item)); 5431 return item; 5432 } 5433 5434 static inline face *add_face(struct face_array *array) 5435 { 5436 return &array->items[array->count++]; 5437 } 5438 5439 static struct triangulation *add_triangulation(struct triangulation_array *array) 5440 { 5441 struct triangulation *item; 5442 5443 if (!reserve((struct dynamic_array *)array, array->count + 1, sizeof(array->items[0]))) 5444 return NULL; 5445 5446 item = &array->items[array->count++]; 5447 ZeroMemory(item, sizeof(*item)); 5448 return item; 5449 } 5450 5451 static HRESULT add_vertex_index(struct word_array *array, WORD vertex_index) 5452 { 5453 if (!reserve((struct dynamic_array *)array, array->count + 1, sizeof(array->items[0]))) 5454 return E_OUTOFMEMORY; 5455 5456 array->items[array->count++] = vertex_index; 5457 return S_OK; 5458 } 5459 5460 /* assume fixed point numbers can be converted to float point in place */ 5461 C_ASSERT(sizeof(FIXED) == sizeof(float)); 5462 C_ASSERT(sizeof(POINTFX) == sizeof(D3DXVECTOR2)); 5463 5464 static inline D3DXVECTOR2 *convert_fixed_to_float(POINTFX *pt, int count, unsigned int emsquare) 5465 { 5466 D3DXVECTOR2 *ret = (D3DXVECTOR2*)pt; 5467 while (count--) { 5468 D3DXVECTOR2 *pt_flt = (D3DXVECTOR2*)pt; 5469 pt_flt->x = (pt->x.value + pt->x.fract / (float)0x10000) / emsquare; 5470 pt_flt->y = (pt->y.value + pt->y.fract / (float)0x10000) / emsquare; 5471 pt++; 5472 } 5473 return ret; 5474 } 5475 5476 static HRESULT add_bezier_points(struct outline *outline, const D3DXVECTOR2 *p1, 5477 const D3DXVECTOR2 *p2, const D3DXVECTOR2 *p3, 5478 float max_deviation_sq) 5479 { 5480 D3DXVECTOR2 split1 = {0, 0}, split2 = {0, 0}, middle, vec; 5481 float deviation_sq; 5482 5483 D3DXVec2Scale(&split1, D3DXVec2Add(&split1, p1, p2), 0.5f); 5484 D3DXVec2Scale(&split2, D3DXVec2Add(&split2, p2, p3), 0.5f); 5485 D3DXVec2Scale(&middle, D3DXVec2Add(&middle, &split1, &split2), 0.5f); 5486 5487 deviation_sq = D3DXVec2LengthSq(D3DXVec2Subtract(&vec, &middle, p2)); 5488 if (deviation_sq < max_deviation_sq) { 5489 struct point2d *pt = add_points(outline, 1); 5490 if (!pt) return E_OUTOFMEMORY; 5491 pt->pos = *p2; 5492 pt->corner = POINTTYPE_CURVE; 5493 /* the end point is omitted because the end line merges into the next segment of 5494 * the split bezier curve, and the end of the split bezier curve is added outside 5495 * this recursive function. */ 5496 } else { 5497 HRESULT hr = add_bezier_points(outline, p1, &split1, &middle, max_deviation_sq); 5498 if (hr != S_OK) return hr; 5499 hr = add_bezier_points(outline, &middle, &split2, p3, max_deviation_sq); 5500 if (hr != S_OK) return hr; 5501 } 5502 5503 return S_OK; 5504 } 5505 5506 static inline BOOL is_direction_similar(D3DXVECTOR2 *dir1, D3DXVECTOR2 *dir2, float cos_theta) 5507 { 5508 /* dot product = cos(theta) */ 5509 return D3DXVec2Dot(dir1, dir2) > cos_theta; 5510 } 5511 5512 static inline D3DXVECTOR2 *unit_vec2(D3DXVECTOR2 *dir, const D3DXVECTOR2 *pt1, const D3DXVECTOR2 *pt2) 5513 { 5514 return D3DXVec2Normalize(D3DXVec2Subtract(dir, pt2, pt1), dir); 5515 } 5516 5517 struct cos_table 5518 { 5519 float cos_half; 5520 float cos_45; 5521 float cos_90; 5522 }; 5523 5524 static BOOL attempt_line_merge(struct outline *outline, 5525 int pt_index, 5526 const D3DXVECTOR2 *nextpt, 5527 BOOL to_curve, 5528 const struct cos_table *table) 5529 { 5530 D3DXVECTOR2 curdir, lastdir; 5531 struct point2d *prevpt, *pt; 5532 BOOL ret = FALSE; 5533 5534 pt = &outline->items[pt_index]; 5535 pt_index = (pt_index - 1 + outline->count) % outline->count; 5536 prevpt = &outline->items[pt_index]; 5537 5538 if (to_curve) 5539 pt->corner = pt->corner != POINTTYPE_CORNER ? POINTTYPE_CURVE_MIDDLE : POINTTYPE_CURVE_START; 5540 5541 if (outline->count < 2) 5542 return FALSE; 5543 5544 /* remove last point if the next line continues the last line */ 5545 unit_vec2(&lastdir, &prevpt->pos, &pt->pos); 5546 unit_vec2(&curdir, &pt->pos, nextpt); 5547 if (is_direction_similar(&lastdir, &curdir, table->cos_half)) 5548 { 5549 outline->count--; 5550 if (pt->corner == POINTTYPE_CURVE_END) 5551 prevpt->corner = pt->corner; 5552 if (prevpt->corner == POINTTYPE_CURVE_END && to_curve) 5553 prevpt->corner = POINTTYPE_CURVE_MIDDLE; 5554 pt = prevpt; 5555 5556 ret = TRUE; 5557 if (outline->count < 2) 5558 return ret; 5559 5560 pt_index = (pt_index - 1 + outline->count) % outline->count; 5561 prevpt = &outline->items[pt_index]; 5562 unit_vec2(&lastdir, &prevpt->pos, &pt->pos); 5563 unit_vec2(&curdir, &pt->pos, nextpt); 5564 } 5565 return ret; 5566 } 5567 5568 static HRESULT create_outline(struct glyphinfo *glyph, void *raw_outline, int datasize, 5569 float max_deviation_sq, unsigned int emsquare, 5570 const struct cos_table *cos_table) 5571 { 5572 TTPOLYGONHEADER *header = (TTPOLYGONHEADER *)raw_outline; 5573 5574 while ((char *)header < (char *)raw_outline + datasize) 5575 { 5576 TTPOLYCURVE *curve = (TTPOLYCURVE *)(header + 1); 5577 struct point2d *lastpt, *pt; 5578 D3DXVECTOR2 lastdir; 5579 D3DXVECTOR2 *pt_flt; 5580 int j; 5581 struct outline *outline = add_outline(&glyph->outlines); 5582 5583 if (!outline) 5584 return E_OUTOFMEMORY; 5585 5586 pt = add_points(outline, 1); 5587 if (!pt) 5588 return E_OUTOFMEMORY; 5589 pt_flt = convert_fixed_to_float(&header->pfxStart, 1, emsquare); 5590 pt->pos = *pt_flt; 5591 pt->corner = POINTTYPE_CORNER; 5592 5593 if (header->dwType != TT_POLYGON_TYPE) 5594 FIXME("Unknown header type %d\n", header->dwType); 5595 5596 while ((char *)curve < (char *)header + header->cb) 5597 { 5598 D3DXVECTOR2 bezier_start = outline->items[outline->count - 1].pos; 5599 BOOL to_curve = curve->wType != TT_PRIM_LINE && curve->cpfx > 1; 5600 unsigned int j2 = 0; 5601 5602 if (!curve->cpfx) { 5603 curve = (TTPOLYCURVE *)&curve->apfx[curve->cpfx]; 5604 continue; 5605 } 5606 5607 pt_flt = convert_fixed_to_float(curve->apfx, curve->cpfx, emsquare); 5608 5609 attempt_line_merge(outline, outline->count - 1, &pt_flt[0], to_curve, cos_table); 5610 5611 if (to_curve) 5612 { 5613 HRESULT hr; 5614 int count = curve->cpfx; 5615 5616 while (count > 2) 5617 { 5618 D3DXVECTOR2 bezier_end; 5619 5620 D3DXVec2Scale(&bezier_end, D3DXVec2Add(&bezier_end, &pt_flt[j2], &pt_flt[j2+1]), 0.5f); 5621 hr = add_bezier_points(outline, &bezier_start, &pt_flt[j2], &bezier_end, max_deviation_sq); 5622 if (hr != S_OK) 5623 return hr; 5624 bezier_start = bezier_end; 5625 count--; 5626 j2++; 5627 } 5628 hr = add_bezier_points(outline, &bezier_start, &pt_flt[j2], &pt_flt[j2+1], max_deviation_sq); 5629 if (hr != S_OK) 5630 return hr; 5631 5632 pt = add_points(outline, 1); 5633 if (!pt) 5634 return E_OUTOFMEMORY; 5635 j2++; 5636 pt->pos = pt_flt[j2]; 5637 pt->corner = POINTTYPE_CURVE_END; 5638 } else { 5639 pt = add_points(outline, curve->cpfx); 5640 if (!pt) 5641 return E_OUTOFMEMORY; 5642 for (j2 = 0; j2 < curve->cpfx; j2++) 5643 { 5644 pt->pos = pt_flt[j2]; 5645 pt->corner = POINTTYPE_CORNER; 5646 pt++; 5647 } 5648 } 5649 5650 curve = (TTPOLYCURVE *)&curve->apfx[curve->cpfx]; 5651 } 5652 5653 /* remove last point if the next line continues the last line */ 5654 if (outline->count >= 3) { 5655 BOOL to_curve; 5656 5657 lastpt = &outline->items[outline->count - 1]; 5658 pt = &outline->items[0]; 5659 if (pt->pos.x == lastpt->pos.x && pt->pos.y == lastpt->pos.y) { 5660 if (lastpt->corner == POINTTYPE_CURVE_END) 5661 { 5662 if (pt->corner == POINTTYPE_CURVE_START) 5663 pt->corner = POINTTYPE_CURVE_MIDDLE; 5664 else 5665 pt->corner = POINTTYPE_CURVE_END; 5666 } 5667 outline->count--; 5668 } else { 5669 /* outline closed with a line from end to start point */ 5670 attempt_line_merge(outline, outline->count - 1, &pt->pos, FALSE, cos_table); 5671 } 5672 lastpt = &outline->items[0]; 5673 to_curve = lastpt->corner != POINTTYPE_CORNER && lastpt->corner != POINTTYPE_CURVE_END; 5674 if (lastpt->corner == POINTTYPE_CURVE_START) 5675 lastpt->corner = POINTTYPE_CORNER; 5676 pt = &outline->items[1]; 5677 if (attempt_line_merge(outline, 0, &pt->pos, to_curve, cos_table)) 5678 *lastpt = outline->items[outline->count]; 5679 } 5680 5681 lastpt = &outline->items[outline->count - 1]; 5682 pt = &outline->items[0]; 5683 unit_vec2(&lastdir, &lastpt->pos, &pt->pos); 5684 for (j = 0; j < outline->count; j++) 5685 { 5686 D3DXVECTOR2 curdir; 5687 5688 lastpt = pt; 5689 pt = &outline->items[(j + 1) % outline->count]; 5690 unit_vec2(&curdir, &lastpt->pos, &pt->pos); 5691 5692 switch (lastpt->corner) 5693 { 5694 case POINTTYPE_CURVE_START: 5695 case POINTTYPE_CURVE_END: 5696 if (!is_direction_similar(&lastdir, &curdir, cos_table->cos_45)) 5697 lastpt->corner = POINTTYPE_CORNER; 5698 break; 5699 case POINTTYPE_CURVE_MIDDLE: 5700 if (!is_direction_similar(&lastdir, &curdir, cos_table->cos_90)) 5701 lastpt->corner = POINTTYPE_CORNER; 5702 else 5703 lastpt->corner = POINTTYPE_CURVE; 5704 break; 5705 default: 5706 break; 5707 } 5708 lastdir = curdir; 5709 } 5710 5711 header = (TTPOLYGONHEADER *)((char *)header + header->cb); 5712 } 5713 return S_OK; 5714 } 5715 5716 /* Get the y-distance from a line to a point */ 5717 static float get_line_to_point_y_distance(D3DXVECTOR2 *line_pt1, 5718 D3DXVECTOR2 *line_pt2, 5719 D3DXVECTOR2 *point) 5720 { 5721 D3DXVECTOR2 line_vec = {0, 0}; 5722 float line_pt_dx; 5723 float line_y; 5724 5725 D3DXVec2Subtract(&line_vec, line_pt2, line_pt1); 5726 line_pt_dx = point->x - line_pt1->x; 5727 line_y = line_pt1->y + (line_vec.y * line_pt_dx) / line_vec.x; 5728 return point->y - line_y; 5729 } 5730 5731 static D3DXVECTOR2 *get_indexed_point(struct point2d_index *pt_idx) 5732 { 5733 return &pt_idx->outline->items[pt_idx->vertex].pos; 5734 } 5735 5736 static D3DXVECTOR2 *get_ordered_vertex(struct glyphinfo *glyph, WORD index) 5737 { 5738 return get_indexed_point(&glyph->ordered_vertices.items[index]); 5739 } 5740 5741 static void remove_triangulation(struct triangulation_array *array, struct triangulation *item) 5742 { 5743 HeapFree(GetProcessHeap(), 0, item->vertex_stack.items); 5744 MoveMemory(item, item + 1, (char*)&array->items[array->count] - (char*)(item + 1)); 5745 array->count--; 5746 } 5747 5748 static HRESULT triangulation_add_point(struct triangulation **t_ptr, 5749 struct triangulation_array *triangulations, 5750 WORD vtx_idx, 5751 BOOL to_top) 5752 { 5753 struct glyphinfo *glyph = triangulations->glyph; 5754 struct triangulation *t = *t_ptr; 5755 HRESULT hr; 5756 face *face; 5757 int f1, f2; 5758 5759 if (t->last_on_top) { 5760 f1 = 1; 5761 f2 = 2; 5762 } else { 5763 f1 = 2; 5764 f2 = 1; 5765 } 5766 5767 if (t->last_on_top != to_top && t->vertex_stack.count > 1) { 5768 /* consume all vertices on the stack */ 5769 WORD last_pt = t->vertex_stack.items[0]; 5770 int i; 5771 for (i = 1; i < t->vertex_stack.count; i++) 5772 { 5773 face = add_face(&glyph->faces); 5774 if (!face) return E_OUTOFMEMORY; 5775 (*face)[0] = vtx_idx; 5776 (*face)[f1] = last_pt; 5777 (*face)[f2] = last_pt = t->vertex_stack.items[i]; 5778 } 5779 t->vertex_stack.items[0] = last_pt; 5780 t->vertex_stack.count = 1; 5781 } else if (t->vertex_stack.count > 1) { 5782 int i = t->vertex_stack.count - 1; 5783 D3DXVECTOR2 *point = get_ordered_vertex(glyph, vtx_idx); 5784 WORD top_idx = t->vertex_stack.items[i--]; 5785 D3DXVECTOR2 *top_pt = get_ordered_vertex(glyph, top_idx); 5786 5787 while (i >= 0) 5788 { 5789 WORD prev_idx = t->vertex_stack.items[i--]; 5790 D3DXVECTOR2 *prev_pt = get_ordered_vertex(glyph, prev_idx); 5791 5792 if (prev_pt->x != top_pt->x && 5793 ((to_top && get_line_to_point_y_distance(prev_pt, top_pt, point) > 0) || 5794 (!to_top && get_line_to_point_y_distance(prev_pt, top_pt, point) < 0))) 5795 break; 5796 5797 face = add_face(&glyph->faces); 5798 if (!face) return E_OUTOFMEMORY; 5799 (*face)[0] = vtx_idx; 5800 (*face)[f1] = prev_idx; 5801 (*face)[f2] = top_idx; 5802 5803 top_pt = prev_pt; 5804 top_idx = prev_idx; 5805 t->vertex_stack.count--; 5806 } 5807 } 5808 t->last_on_top = to_top; 5809 5810 hr = add_vertex_index(&t->vertex_stack, vtx_idx); 5811 5812 if (hr == S_OK && t->merging) { 5813 struct triangulation *t2; 5814 5815 t2 = to_top ? t - 1 : t + 1; 5816 t2->merging = FALSE; 5817 hr = triangulation_add_point(&t2, triangulations, vtx_idx, to_top); 5818 if (hr != S_OK) return hr; 5819 remove_triangulation(triangulations, t); 5820 if (t2 > t) 5821 t2--; 5822 *t_ptr = t2; 5823 } 5824 return hr; 5825 } 5826 5827 /* check if the point is next on the outline for either the top or bottom */ 5828 static D3DXVECTOR2 *triangulation_get_next_point(struct triangulation *t, struct glyphinfo *glyph, BOOL on_top) 5829 { 5830 int i = t->last_on_top == on_top ? t->vertex_stack.count - 1 : 0; 5831 WORD idx = t->vertex_stack.items[i]; 5832 struct point2d_index *pt_idx = &glyph->ordered_vertices.items[idx]; 5833 struct outline *outline = pt_idx->outline; 5834 5835 if (on_top) 5836 i = (pt_idx->vertex + outline->count - 1) % outline->count; 5837 else 5838 i = (pt_idx->vertex + 1) % outline->count; 5839 5840 return &outline->items[i].pos; 5841 } 5842 5843 static int compare_vertex_indices(const void *a, const void *b) 5844 { 5845 const struct point2d_index *idx1 = a, *idx2 = b; 5846 const D3DXVECTOR2 *p1 = &idx1->outline->items[idx1->vertex].pos; 5847 const D3DXVECTOR2 *p2 = &idx2->outline->items[idx2->vertex].pos; 5848 float diff = p1->x - p2->x; 5849 5850 if (diff == 0.0f) 5851 diff = p1->y - p2->y; 5852 5853 return diff == 0.0f ? 0 : (diff > 0.0f ? -1 : 1); 5854 } 5855 5856 static HRESULT triangulate(struct triangulation_array *triangulations) 5857 { 5858 int sweep_idx; 5859 HRESULT hr; 5860 struct glyphinfo *glyph = triangulations->glyph; 5861 int nb_vertices = 0; 5862 int i; 5863 struct point2d_index *idx_ptr; 5864 5865 /* Glyphs without outlines do not generate any vertices. */ 5866 if (!glyph->outlines.count) 5867 return D3D_OK; 5868 5869 for (i = 0; i < glyph->outlines.count; i++) 5870 nb_vertices += glyph->outlines.items[i].count; 5871 5872 glyph->ordered_vertices.items = HeapAlloc(GetProcessHeap(), 0, 5873 nb_vertices * sizeof(*glyph->ordered_vertices.items)); 5874 if (!glyph->ordered_vertices.items) 5875 return E_OUTOFMEMORY; 5876 5877 idx_ptr = glyph->ordered_vertices.items; 5878 for (i = 0; i < glyph->outlines.count; i++) 5879 { 5880 struct outline *outline = &glyph->outlines.items[i]; 5881 int j; 5882 5883 idx_ptr->outline = outline; 5884 idx_ptr->vertex = 0; 5885 idx_ptr++; 5886 for (j = outline->count - 1; j > 0; j--) 5887 { 5888 idx_ptr->outline = outline; 5889 idx_ptr->vertex = j; 5890 idx_ptr++; 5891 } 5892 } 5893 glyph->ordered_vertices.count = nb_vertices; 5894 5895 /* Native implementation seems to try to create a triangle fan from 5896 * the first outline point if the glyph only has one outline. */ 5897 if (glyph->outlines.count == 1) 5898 { 5899 struct outline *outline = glyph->outlines.items; 5900 D3DXVECTOR2 *base = &outline->items[0].pos; 5901 D3DXVECTOR2 *last = &outline->items[1].pos; 5902 float ccw = 0; 5903 5904 for (i = 2; i < outline->count; i++) 5905 { 5906 D3DXVECTOR2 *next = &outline->items[i].pos; 5907 D3DXVECTOR2 v1 = {0.0f, 0.0f}; 5908 D3DXVECTOR2 v2 = {0.0f, 0.0f}; 5909 5910 D3DXVec2Subtract(&v1, base, last); 5911 D3DXVec2Subtract(&v2, last, next); 5912 ccw = D3DXVec2CCW(&v1, &v2); 5913 if (ccw > 0.0f) 5914 break; 5915 5916 last = next; 5917 } 5918 if (ccw <= 0) 5919 { 5920 glyph->faces.items = HeapAlloc(GetProcessHeap(), 0, 5921 (outline->count - 2) * sizeof(glyph->faces.items[0])); 5922 if (!glyph->faces.items) 5923 return E_OUTOFMEMORY; 5924 5925 glyph->faces.count = outline->count - 2; 5926 for (i = 0; i < glyph->faces.count; i++) 5927 { 5928 glyph->faces.items[i][0] = 0; 5929 glyph->faces.items[i][1] = i + 1; 5930 glyph->faces.items[i][2] = i + 2; 5931 } 5932 return S_OK; 5933 } 5934 } 5935 5936 /* Perform 2D polygon triangulation for complex glyphs. 5937 * Triangulation is performed using a sweep line concept, from right to left, 5938 * by processing vertices in sorted order. Complex polygons are split into 5939 * monotone polygons which are triangulated separately. */ 5940 /* FIXME: The order of the faces is not consistent with the native implementation. */ 5941 5942 /* Reserve space for maximum possible faces from triangulation. 5943 * # faces for outer outlines = outline->count - 2 5944 * # faces for inner outlines = outline->count + 2 5945 * There must be at least 1 outer outline. */ 5946 glyph->faces.items = HeapAlloc(GetProcessHeap(), 0, 5947 (nb_vertices + glyph->outlines.count * 2 - 4) * sizeof(glyph->faces.items[0])); 5948 if (!glyph->faces.items) 5949 return E_OUTOFMEMORY; 5950 5951 qsort(glyph->ordered_vertices.items, nb_vertices, 5952 sizeof(glyph->ordered_vertices.items[0]), compare_vertex_indices); 5953 for (sweep_idx = 0; sweep_idx < glyph->ordered_vertices.count; sweep_idx++) 5954 { 5955 int start = 0; 5956 int end = triangulations->count; 5957 5958 while (start < end) 5959 { 5960 D3DXVECTOR2 *sweep_vtx = get_ordered_vertex(glyph, sweep_idx); 5961 int current = (start + end) / 2; 5962 struct triangulation *t = &triangulations->items[current]; 5963 BOOL on_top_outline = FALSE; 5964 D3DXVECTOR2 *top_next, *bottom_next; 5965 WORD top_idx, bottom_idx; 5966 5967 if (t->merging && t->last_on_top) 5968 top_next = triangulation_get_next_point(t + 1, glyph, TRUE); 5969 else 5970 top_next = triangulation_get_next_point(t, glyph, TRUE); 5971 if (sweep_vtx == top_next) 5972 { 5973 if (t->merging && t->last_on_top) 5974 t++; 5975 hr = triangulation_add_point(&t, triangulations, sweep_idx, TRUE); 5976 if (hr != S_OK) return hr; 5977 5978 if (t + 1 < &triangulations->items[triangulations->count] && 5979 triangulation_get_next_point(t + 1, glyph, FALSE) == sweep_vtx) 5980 { 5981 /* point also on bottom outline of higher triangulation */ 5982 struct triangulation *t2 = t + 1; 5983 hr = triangulation_add_point(&t2, triangulations, sweep_idx, FALSE); 5984 if (hr != S_OK) return hr; 5985 5986 t->merging = TRUE; 5987 t2->merging = TRUE; 5988 } 5989 on_top_outline = TRUE; 5990 } 5991 5992 if (t->merging && !t->last_on_top) 5993 bottom_next = triangulation_get_next_point(t - 1, glyph, FALSE); 5994 else 5995 bottom_next = triangulation_get_next_point(t, glyph, FALSE); 5996 if (sweep_vtx == bottom_next) 5997 { 5998 if (t->merging && !t->last_on_top) 5999 t--; 6000 if (on_top_outline) { 6001 /* outline finished */ 6002 remove_triangulation(triangulations, t); 6003 break; 6004 } 6005 6006 hr = triangulation_add_point(&t, triangulations, sweep_idx, FALSE); 6007 if (hr != S_OK) return hr; 6008 6009 if (t > triangulations->items && 6010 triangulation_get_next_point(t - 1, glyph, TRUE) == sweep_vtx) 6011 { 6012 struct triangulation *t2 = t - 1; 6013 /* point also on top outline of lower triangulation */ 6014 hr = triangulation_add_point(&t2, triangulations, sweep_idx, TRUE); 6015 if (hr != S_OK) return hr; 6016 t = t2 + 1; /* t may be invalidated by triangulation merging */ 6017 6018 t->merging = TRUE; 6019 t2->merging = TRUE; 6020 } 6021 break; 6022 } 6023 if (on_top_outline) 6024 break; 6025 6026 if (t->last_on_top) { 6027 top_idx = t->vertex_stack.items[t->vertex_stack.count - 1]; 6028 bottom_idx = t->vertex_stack.items[0]; 6029 } else { 6030 top_idx = t->vertex_stack.items[0]; 6031 bottom_idx = t->vertex_stack.items[t->vertex_stack.count - 1]; 6032 } 6033 6034 /* check if the point is inside or outside this polygon */ 6035 if (get_line_to_point_y_distance(get_ordered_vertex(glyph, top_idx), 6036 top_next, sweep_vtx) > 0) 6037 { /* above */ 6038 start = current + 1; 6039 } else if (get_line_to_point_y_distance(get_ordered_vertex(glyph, bottom_idx), 6040 bottom_next, sweep_vtx) < 0) 6041 { /* below */ 6042 end = current; 6043 } else if (t->merging) { 6044 /* inside, so cancel merging */ 6045 struct triangulation *t2 = t->last_on_top ? t + 1 : t - 1; 6046 t->merging = FALSE; 6047 t2->merging = FALSE; 6048 hr = triangulation_add_point(&t, triangulations, sweep_idx, t->last_on_top); 6049 if (hr != S_OK) return hr; 6050 hr = triangulation_add_point(&t2, triangulations, sweep_idx, t2->last_on_top); 6051 if (hr != S_OK) return hr; 6052 break; 6053 } else { 6054 /* inside, so split polygon into two monotone parts */ 6055 struct triangulation *t2 = add_triangulation(triangulations); 6056 if (!t2) return E_OUTOFMEMORY; 6057 MoveMemory(t + 1, t, (char*)(t2 + 1) - (char*)t); 6058 if (t->last_on_top) { 6059 t2 = t + 1; 6060 } else { 6061 t2 = t; 6062 t++; 6063 } 6064 6065 ZeroMemory(&t2->vertex_stack, sizeof(t2->vertex_stack)); 6066 hr = add_vertex_index(&t2->vertex_stack, t->vertex_stack.items[t->vertex_stack.count - 1]); 6067 if (hr != S_OK) return hr; 6068 hr = add_vertex_index(&t2->vertex_stack, sweep_idx); 6069 if (hr != S_OK) return hr; 6070 t2->last_on_top = !t->last_on_top; 6071 6072 hr = triangulation_add_point(&t, triangulations, sweep_idx, t->last_on_top); 6073 if (hr != S_OK) return hr; 6074 break; 6075 } 6076 } 6077 if (start >= end) 6078 { 6079 struct triangulation *t; 6080 struct triangulation *t2 = add_triangulation(triangulations); 6081 if (!t2) return E_OUTOFMEMORY; 6082 t = &triangulations->items[start]; 6083 MoveMemory(t + 1, t, (char*)(t2 + 1) - (char*)t); 6084 ZeroMemory(t, sizeof(*t)); 6085 hr = add_vertex_index(&t->vertex_stack, sweep_idx); 6086 if (hr != S_OK) return hr; 6087 } 6088 } 6089 return S_OK; 6090 } 6091 6092 HRESULT WINAPI D3DXCreateTextW(struct IDirect3DDevice9 *device, HDC hdc, const WCHAR *text, float deviation, 6093 float extrusion, struct ID3DXMesh **mesh_ptr, struct ID3DXBuffer **adjacency, GLYPHMETRICSFLOAT *glyphmetrics) 6094 { 6095 HRESULT hr; 6096 ID3DXMesh *mesh = NULL; 6097 DWORD nb_vertices, nb_faces; 6098 DWORD nb_front_faces, nb_corners, nb_outline_points; 6099 struct vertex *vertices = NULL; 6100 face *faces = NULL; 6101 int textlen = 0; 6102 float offset_x; 6103 LOGFONTW lf; 6104 OUTLINETEXTMETRICW otm; 6105 HFONT font = NULL, oldfont = NULL; 6106 const MAT2 identity = {{0, 1}, {0, 0}, {0, 0}, {0, 1}}; 6107 void *raw_outline = NULL; 6108 int bufsize = 0; 6109 struct glyphinfo *glyphs = NULL; 6110 GLYPHMETRICS gm; 6111 struct triangulation_array triangulations = {0, 0, NULL}; 6112 int i; 6113 struct vertex *vertex_ptr; 6114 face *face_ptr; 6115 float max_deviation_sq; 6116 const struct cos_table cos_table = { 6117 cosf(D3DXToRadian(0.5f)), 6118 cosf(D3DXToRadian(45.0f)), 6119 cosf(D3DXToRadian(90.0f)), 6120 }; 6121 int f1, f2; 6122 6123 TRACE("(%p, %p, %s, %f, %f, %p, %p, %p)\n", device, hdc, 6124 debugstr_w(text), deviation, extrusion, mesh_ptr, adjacency, glyphmetrics); 6125 6126 if (!device || !hdc || !text || !*text || deviation < 0.0f || extrusion < 0.0f || !mesh_ptr) 6127 return D3DERR_INVALIDCALL; 6128 6129 if (adjacency) 6130 { 6131 FIXME("Case of adjacency != NULL not implemented.\n"); 6132 return E_NOTIMPL; 6133 } 6134 6135 if (!GetObjectW(GetCurrentObject(hdc, OBJ_FONT), sizeof(lf), &lf) || 6136 !GetOutlineTextMetricsW(hdc, sizeof(otm), &otm)) 6137 { 6138 return D3DERR_INVALIDCALL; 6139 } 6140 6141 if (deviation == 0.0f) 6142 deviation = 1.0f / otm.otmEMSquare; 6143 max_deviation_sq = deviation * deviation; 6144 6145 lf.lfHeight = otm.otmEMSquare; 6146 lf.lfWidth = 0; 6147 font = CreateFontIndirectW(&lf); 6148 if (!font) { 6149 hr = E_OUTOFMEMORY; 6150 goto error; 6151 } 6152 oldfont = SelectObject(hdc, font); 6153 6154 textlen = strlenW(text); 6155 for (i = 0; i < textlen; i++) 6156 { 6157 int datasize = GetGlyphOutlineW(hdc, text[i], GGO_NATIVE, &gm, 0, NULL, &identity); 6158 if (datasize < 0) 6159 return D3DERR_INVALIDCALL; 6160 if (bufsize < datasize) 6161 bufsize = datasize; 6162 } 6163 if (!bufsize) { /* e.g. text == " " */ 6164 hr = D3DERR_INVALIDCALL; 6165 goto error; 6166 } 6167 6168 glyphs = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, textlen * sizeof(*glyphs)); 6169 raw_outline = HeapAlloc(GetProcessHeap(), 0, bufsize); 6170 if (!glyphs || !raw_outline) { 6171 hr = E_OUTOFMEMORY; 6172 goto error; 6173 } 6174 6175 offset_x = 0.0f; 6176 for (i = 0; i < textlen; i++) 6177 { 6178 /* get outline points from data returned from GetGlyphOutline */ 6179 int datasize; 6180 6181 glyphs[i].offset_x = offset_x; 6182 6183 datasize = GetGlyphOutlineW(hdc, text[i], GGO_NATIVE, &gm, bufsize, raw_outline, &identity); 6184 hr = create_outline(&glyphs[i], raw_outline, datasize, 6185 max_deviation_sq, otm.otmEMSquare, &cos_table); 6186 if (hr != S_OK) goto error; 6187 6188 triangulations.glyph = &glyphs[i]; 6189 hr = triangulate(&triangulations); 6190 if (hr != S_OK) goto error; 6191 if (triangulations.count) { 6192 ERR("%d incomplete triangulations of glyph (%u).\n", triangulations.count, text[i]); 6193 triangulations.count = 0; 6194 } 6195 6196 if (glyphmetrics) 6197 { 6198 glyphmetrics[i].gmfBlackBoxX = gm.gmBlackBoxX / (float)otm.otmEMSquare; 6199 glyphmetrics[i].gmfBlackBoxY = gm.gmBlackBoxY / (float)otm.otmEMSquare; 6200 glyphmetrics[i].gmfptGlyphOrigin.x = gm.gmptGlyphOrigin.x / (float)otm.otmEMSquare; 6201 glyphmetrics[i].gmfptGlyphOrigin.y = gm.gmptGlyphOrigin.y / (float)otm.otmEMSquare; 6202 glyphmetrics[i].gmfCellIncX = gm.gmCellIncX / (float)otm.otmEMSquare; 6203 glyphmetrics[i].gmfCellIncY = gm.gmCellIncY / (float)otm.otmEMSquare; 6204 } 6205 offset_x += gm.gmCellIncX / (float)otm.otmEMSquare; 6206 } 6207 6208 /* corner points need an extra vertex for the different side faces normals */ 6209 nb_corners = 0; 6210 nb_outline_points = 0; 6211 nb_front_faces = 0; 6212 for (i = 0; i < textlen; i++) 6213 { 6214 int j; 6215 nb_outline_points += glyphs[i].ordered_vertices.count; 6216 nb_front_faces += glyphs[i].faces.count; 6217 for (j = 0; j < glyphs[i].outlines.count; j++) 6218 { 6219 int k; 6220 struct outline *outline = &glyphs[i].outlines.items[j]; 6221 nb_corners++; /* first outline point always repeated as a corner */ 6222 for (k = 1; k < outline->count; k++) 6223 if (outline->items[k].corner) 6224 nb_corners++; 6225 } 6226 } 6227 6228 nb_vertices = (nb_outline_points + nb_corners) * 2 + nb_outline_points * 2; 6229 nb_faces = nb_outline_points * 2 + nb_front_faces * 2; 6230 6231 6232 hr = D3DXCreateMeshFVF(nb_faces, nb_vertices, D3DXMESH_MANAGED, 6233 D3DFVF_XYZ | D3DFVF_NORMAL, device, &mesh); 6234 if (FAILED(hr)) 6235 goto error; 6236 6237 if (FAILED(hr = mesh->lpVtbl->LockVertexBuffer(mesh, 0, (void **)&vertices))) 6238 goto error; 6239 6240 if (FAILED(hr = mesh->lpVtbl->LockIndexBuffer(mesh, 0, (void **)&faces))) 6241 goto error; 6242 6243 /* convert 2D vertices and faces into 3D mesh */ 6244 vertex_ptr = vertices; 6245 face_ptr = faces; 6246 if (extrusion == 0.0f) { 6247 f1 = 1; 6248 f2 = 2; 6249 } else { 6250 f1 = 2; 6251 f2 = 1; 6252 } 6253 for (i = 0; i < textlen; i++) 6254 { 6255 int j; 6256 int count; 6257 struct vertex *back_vertices; 6258 face *back_faces; 6259 6260 /* side vertices and faces */ 6261 for (j = 0; j < glyphs[i].outlines.count; j++) 6262 { 6263 struct vertex *outline_vertices = vertex_ptr; 6264 struct outline *outline = &glyphs[i].outlines.items[j]; 6265 int k; 6266 struct point2d *prevpt = &outline->items[outline->count - 1]; 6267 struct point2d *pt = &outline->items[0]; 6268 6269 for (k = 1; k <= outline->count; k++) 6270 { 6271 struct vertex vtx; 6272 struct point2d *nextpt = &outline->items[k % outline->count]; 6273 WORD vtx_idx = vertex_ptr - vertices; 6274 D3DXVECTOR2 vec; 6275 6276 if (pt->corner == POINTTYPE_CURVE_START) 6277 D3DXVec2Subtract(&vec, &pt->pos, &prevpt->pos); 6278 else if (pt->corner) 6279 D3DXVec2Subtract(&vec, &nextpt->pos, &pt->pos); 6280 else 6281 D3DXVec2Subtract(&vec, &nextpt->pos, &prevpt->pos); 6282 D3DXVec2Normalize(&vec, &vec); 6283 vtx.normal.x = -vec.y; 6284 vtx.normal.y = vec.x; 6285 vtx.normal.z = 0; 6286 6287 vtx.position.x = pt->pos.x + glyphs[i].offset_x; 6288 vtx.position.y = pt->pos.y; 6289 vtx.position.z = 0; 6290 *vertex_ptr++ = vtx; 6291 6292 vtx.position.z = -extrusion; 6293 *vertex_ptr++ = vtx; 6294 6295 vtx.position.x = nextpt->pos.x + glyphs[i].offset_x; 6296 vtx.position.y = nextpt->pos.y; 6297 if (pt->corner && nextpt->corner && nextpt->corner != POINTTYPE_CURVE_END) { 6298 vtx.position.z = -extrusion; 6299 *vertex_ptr++ = vtx; 6300 vtx.position.z = 0; 6301 *vertex_ptr++ = vtx; 6302 6303 (*face_ptr)[0] = vtx_idx; 6304 (*face_ptr)[1] = vtx_idx + 2; 6305 (*face_ptr)[2] = vtx_idx + 1; 6306 face_ptr++; 6307 6308 (*face_ptr)[0] = vtx_idx; 6309 (*face_ptr)[1] = vtx_idx + 3; 6310 (*face_ptr)[2] = vtx_idx + 2; 6311 face_ptr++; 6312 } else { 6313 if (nextpt->corner) { 6314 if (nextpt->corner == POINTTYPE_CURVE_END) { 6315 D3DXVECTOR2 *nextpt2 = &outline->items[(k + 1) % outline->count].pos; 6316 D3DXVec2Subtract(&vec, nextpt2, &nextpt->pos); 6317 } else { 6318 D3DXVec2Subtract(&vec, &nextpt->pos, &pt->pos); 6319 } 6320 D3DXVec2Normalize(&vec, &vec); 6321 vtx.normal.x = -vec.y; 6322 vtx.normal.y = vec.x; 6323 6324 vtx.position.z = 0; 6325 *vertex_ptr++ = vtx; 6326 vtx.position.z = -extrusion; 6327 *vertex_ptr++ = vtx; 6328 } 6329 6330 (*face_ptr)[0] = vtx_idx; 6331 (*face_ptr)[1] = vtx_idx + 3; 6332 (*face_ptr)[2] = vtx_idx + 1; 6333 face_ptr++; 6334 6335 (*face_ptr)[0] = vtx_idx; 6336 (*face_ptr)[1] = vtx_idx + 2; 6337 (*face_ptr)[2] = vtx_idx + 3; 6338 face_ptr++; 6339 } 6340 6341 prevpt = pt; 6342 pt = nextpt; 6343 } 6344 if (!pt->corner) { 6345 *vertex_ptr++ = *outline_vertices++; 6346 *vertex_ptr++ = *outline_vertices++; 6347 } 6348 } 6349 6350 /* back vertices and faces */ 6351 back_faces = face_ptr; 6352 back_vertices = vertex_ptr; 6353 for (j = 0; j < glyphs[i].ordered_vertices.count; j++) 6354 { 6355 D3DXVECTOR2 *pt = get_ordered_vertex(&glyphs[i], j); 6356 vertex_ptr->position.x = pt->x + glyphs[i].offset_x; 6357 vertex_ptr->position.y = pt->y; 6358 vertex_ptr->position.z = 0; 6359 vertex_ptr->normal.x = 0; 6360 vertex_ptr->normal.y = 0; 6361 vertex_ptr->normal.z = 1; 6362 vertex_ptr++; 6363 } 6364 count = back_vertices - vertices; 6365 for (j = 0; j < glyphs[i].faces.count; j++) 6366 { 6367 face *f = &glyphs[i].faces.items[j]; 6368 (*face_ptr)[0] = (*f)[0] + count; 6369 (*face_ptr)[1] = (*f)[1] + count; 6370 (*face_ptr)[2] = (*f)[2] + count; 6371 face_ptr++; 6372 } 6373 6374 /* front vertices and faces */ 6375 j = count = vertex_ptr - back_vertices; 6376 while (j--) 6377 { 6378 vertex_ptr->position.x = back_vertices->position.x; 6379 vertex_ptr->position.y = back_vertices->position.y; 6380 vertex_ptr->position.z = -extrusion; 6381 vertex_ptr->normal.x = 0; 6382 vertex_ptr->normal.y = 0; 6383 vertex_ptr->normal.z = extrusion == 0.0f ? 1.0f : -1.0f; 6384 vertex_ptr++; 6385 back_vertices++; 6386 } 6387 j = face_ptr - back_faces; 6388 while (j--) 6389 { 6390 (*face_ptr)[0] = (*back_faces)[0] + count; 6391 (*face_ptr)[1] = (*back_faces)[f1] + count; 6392 (*face_ptr)[2] = (*back_faces)[f2] + count; 6393 face_ptr++; 6394 back_faces++; 6395 } 6396 } 6397 6398 *mesh_ptr = mesh; 6399 hr = D3D_OK; 6400 error: 6401 if (mesh) { 6402 if (faces) mesh->lpVtbl->UnlockIndexBuffer(mesh); 6403 if (vertices) mesh->lpVtbl->UnlockVertexBuffer(mesh); 6404 if (hr != D3D_OK) mesh->lpVtbl->Release(mesh); 6405 } 6406 if (glyphs) { 6407 for (i = 0; i < textlen; i++) 6408 { 6409 int j; 6410 for (j = 0; j < glyphs[i].outlines.count; j++) 6411 HeapFree(GetProcessHeap(), 0, glyphs[i].outlines.items[j].items); 6412 HeapFree(GetProcessHeap(), 0, glyphs[i].outlines.items); 6413 HeapFree(GetProcessHeap(), 0, glyphs[i].faces.items); 6414 HeapFree(GetProcessHeap(), 0, glyphs[i].ordered_vertices.items); 6415 } 6416 HeapFree(GetProcessHeap(), 0, glyphs); 6417 } 6418 if (triangulations.items) { 6419 int i; 6420 for (i = 0; i < triangulations.count; i++) 6421 HeapFree(GetProcessHeap(), 0, triangulations.items[i].vertex_stack.items); 6422 HeapFree(GetProcessHeap(), 0, triangulations.items); 6423 } 6424 HeapFree(GetProcessHeap(), 0, raw_outline); 6425 if (oldfont) SelectObject(hdc, oldfont); 6426 if (font) DeleteObject(font); 6427 6428 return hr; 6429 } 6430 6431 HRESULT WINAPI D3DXValidMesh(ID3DXMesh *mesh, const DWORD *adjacency, ID3DXBuffer **errors_and_warnings) 6432 { 6433 FIXME("(%p, %p, %p): stub\n", mesh, adjacency, *errors_and_warnings); 6434 6435 return E_NOTIMPL; 6436 } 6437 6438 static BOOL weld_float1(void *to, void *from, FLOAT epsilon) 6439 { 6440 FLOAT *v1 = to; 6441 FLOAT *v2 = from; 6442 6443 if (fabsf(*v1 - *v2) <= epsilon) 6444 { 6445 *v1 = *v2; 6446 6447 return TRUE; 6448 } 6449 6450 return FALSE; 6451 } 6452 6453 static BOOL weld_float2(void *to, void *from, FLOAT epsilon) 6454 { 6455 D3DXVECTOR2 *v1 = to; 6456 D3DXVECTOR2 *v2 = from; 6457 FLOAT diff_x = fabsf(v1->x - v2->x); 6458 FLOAT diff_y = fabsf(v1->y - v2->y); 6459 FLOAT max_abs_diff = max(diff_x, diff_y); 6460 6461 if (max_abs_diff <= epsilon) 6462 { 6463 memcpy(to, from, sizeof(D3DXVECTOR2)); 6464 6465 return TRUE; 6466 } 6467 6468 return FALSE; 6469 } 6470 6471 static BOOL weld_float3(void *to, void *from, FLOAT epsilon) 6472 { 6473 D3DXVECTOR3 *v1 = to; 6474 D3DXVECTOR3 *v2 = from; 6475 FLOAT diff_x = fabsf(v1->x - v2->x); 6476 FLOAT diff_y = fabsf(v1->y - v2->y); 6477 FLOAT diff_z = fabsf(v1->z - v2->z); 6478 FLOAT max_abs_diff = max(diff_x, diff_y); 6479 max_abs_diff = max(diff_z, max_abs_diff); 6480 6481 if (max_abs_diff <= epsilon) 6482 { 6483 memcpy(to, from, sizeof(D3DXVECTOR3)); 6484 6485 return TRUE; 6486 } 6487 6488 return FALSE; 6489 } 6490 6491 static BOOL weld_float4(void *to, void *from, FLOAT epsilon) 6492 { 6493 D3DXVECTOR4 *v1 = to; 6494 D3DXVECTOR4 *v2 = from; 6495 FLOAT diff_x = fabsf(v1->x - v2->x); 6496 FLOAT diff_y = fabsf(v1->y - v2->y); 6497 FLOAT diff_z = fabsf(v1->z - v2->z); 6498 FLOAT diff_w = fabsf(v1->w - v2->w); 6499 FLOAT max_abs_diff = max(diff_x, diff_y); 6500 max_abs_diff = max(diff_z, max_abs_diff); 6501 max_abs_diff = max(diff_w, max_abs_diff); 6502 6503 if (max_abs_diff <= epsilon) 6504 { 6505 memcpy(to, from, sizeof(D3DXVECTOR4)); 6506 6507 return TRUE; 6508 } 6509 6510 return FALSE; 6511 } 6512 6513 static BOOL weld_ubyte4(void *to, void *from, FLOAT epsilon) 6514 { 6515 BYTE *b1 = to; 6516 BYTE *b2 = from; 6517 BYTE truncated_epsilon = (BYTE)epsilon; 6518 BYTE diff_x = b1[0] > b2[0] ? b1[0] - b2[0] : b2[0] - b1[0]; 6519 BYTE diff_y = b1[1] > b2[1] ? b1[1] - b2[1] : b2[1] - b1[1]; 6520 BYTE diff_z = b1[2] > b2[2] ? b1[2] - b2[2] : b2[2] - b1[2]; 6521 BYTE diff_w = b1[3] > b2[3] ? b1[3] - b2[3] : b2[3] - b1[3]; 6522 BYTE max_diff = max(diff_x, diff_y); 6523 max_diff = max(diff_z, max_diff); 6524 max_diff = max(diff_w, max_diff); 6525 6526 if (max_diff <= truncated_epsilon) 6527 { 6528 memcpy(to, from, 4 * sizeof(BYTE)); 6529 6530 return TRUE; 6531 } 6532 6533 return FALSE; 6534 } 6535 6536 static BOOL weld_ubyte4n(void *to, void *from, FLOAT epsilon) 6537 { 6538 return weld_ubyte4(to, from, epsilon * UCHAR_MAX); 6539 } 6540 6541 static BOOL weld_d3dcolor(void *to, void *from, FLOAT epsilon) 6542 { 6543 return weld_ubyte4n(to, from, epsilon); 6544 } 6545 6546 static BOOL weld_short2(void *to, void *from, FLOAT epsilon) 6547 { 6548 SHORT *s1 = to; 6549 SHORT *s2 = from; 6550 SHORT truncated_epsilon = (SHORT)epsilon; 6551 SHORT diff_x = abs(s1[0] - s2[0]); 6552 SHORT diff_y = abs(s1[1] - s2[1]); 6553 SHORT max_abs_diff = max(diff_x, diff_y); 6554 6555 if (max_abs_diff <= truncated_epsilon) 6556 { 6557 memcpy(to, from, 2 * sizeof(SHORT)); 6558 6559 return TRUE; 6560 } 6561 6562 return FALSE; 6563 } 6564 6565 static BOOL weld_short2n(void *to, void *from, FLOAT epsilon) 6566 { 6567 return weld_short2(to, from, epsilon * SHRT_MAX); 6568 } 6569 6570 static BOOL weld_short4(void *to, void *from, FLOAT epsilon) 6571 { 6572 SHORT *s1 = to; 6573 SHORT *s2 = from; 6574 SHORT truncated_epsilon = (SHORT)epsilon; 6575 SHORT diff_x = abs(s1[0] - s2[0]); 6576 SHORT diff_y = abs(s1[1] - s2[1]); 6577 SHORT diff_z = abs(s1[2] - s2[2]); 6578 SHORT diff_w = abs(s1[3] - s2[3]); 6579 SHORT max_abs_diff = max(diff_x, diff_y); 6580 max_abs_diff = max(diff_z, max_abs_diff); 6581 max_abs_diff = max(diff_w, max_abs_diff); 6582 6583 if (max_abs_diff <= truncated_epsilon) 6584 { 6585 memcpy(to, from, 4 * sizeof(SHORT)); 6586 6587 return TRUE; 6588 } 6589 6590 return FALSE; 6591 } 6592 6593 static BOOL weld_short4n(void *to, void *from, FLOAT epsilon) 6594 { 6595 return weld_short4(to, from, epsilon * SHRT_MAX); 6596 } 6597 6598 static BOOL weld_ushort2n(void *to, void *from, FLOAT epsilon) 6599 { 6600 USHORT *s1 = to; 6601 USHORT *s2 = from; 6602 USHORT scaled_epsilon = (USHORT)(epsilon * USHRT_MAX); 6603 USHORT diff_x = s1[0] > s2[0] ? s1[0] - s2[0] : s2[0] - s1[0]; 6604 USHORT diff_y = s1[1] > s2[1] ? s1[1] - s2[1] : s2[1] - s1[1]; 6605 USHORT max_diff = max(diff_x, diff_y); 6606 6607 if (max_diff <= scaled_epsilon) 6608 { 6609 memcpy(to, from, 2 * sizeof(USHORT)); 6610 6611 return TRUE; 6612 } 6613 6614 return FALSE; 6615 } 6616 6617 static BOOL weld_ushort4n(void *to, void *from, FLOAT epsilon) 6618 { 6619 USHORT *s1 = to; 6620 USHORT *s2 = from; 6621 USHORT scaled_epsilon = (USHORT)(epsilon * USHRT_MAX); 6622 USHORT diff_x = s1[0] > s2[0] ? s1[0] - s2[0] : s2[0] - s1[0]; 6623 USHORT diff_y = s1[1] > s2[1] ? s1[1] - s2[1] : s2[1] - s1[1]; 6624 USHORT diff_z = s1[2] > s2[2] ? s1[2] - s2[2] : s2[2] - s1[2]; 6625 USHORT diff_w = s1[3] > s2[3] ? s1[3] - s2[3] : s2[3] - s1[3]; 6626 USHORT max_diff = max(diff_x, diff_y); 6627 max_diff = max(diff_z, max_diff); 6628 max_diff = max(diff_w, max_diff); 6629 6630 if (max_diff <= scaled_epsilon) 6631 { 6632 memcpy(to, from, 4 * sizeof(USHORT)); 6633 6634 return TRUE; 6635 } 6636 6637 return FALSE; 6638 } 6639 6640 struct udec3 6641 { 6642 UINT x; 6643 UINT y; 6644 UINT z; 6645 UINT w; 6646 }; 6647 6648 static struct udec3 dword_to_udec3(DWORD d) 6649 { 6650 struct udec3 v; 6651 6652 v.x = d & 0x3ff; 6653 v.y = (d & 0xffc00) >> 10; 6654 v.z = (d & 0x3ff00000) >> 20; 6655 v.w = (d & 0xc0000000) >> 30; 6656 6657 return v; 6658 } 6659 6660 static BOOL weld_udec3(void *to, void *from, FLOAT epsilon) 6661 { 6662 DWORD *d1 = to; 6663 DWORD *d2 = from; 6664 struct udec3 v1 = dword_to_udec3(*d1); 6665 struct udec3 v2 = dword_to_udec3(*d2); 6666 UINT truncated_epsilon = (UINT)epsilon; 6667 UINT diff_x = v1.x > v2.x ? v1.x - v2.x : v2.x - v1.x; 6668 UINT diff_y = v1.y > v2.y ? v1.y - v2.y : v2.y - v1.y; 6669 UINT diff_z = v1.z > v2.z ? v1.z - v2.z : v2.z - v1.z; 6670 UINT diff_w = v1.w > v2.w ? v1.w - v2.w : v2.w - v1.w; 6671 UINT max_diff = max(diff_x, diff_y); 6672 max_diff = max(diff_z, max_diff); 6673 max_diff = max(diff_w, max_diff); 6674 6675 if (max_diff <= truncated_epsilon) 6676 { 6677 memcpy(to, from, sizeof(DWORD)); 6678 6679 return TRUE; 6680 } 6681 6682 return FALSE; 6683 } 6684 6685 struct dec3n 6686 { 6687 INT x; 6688 INT y; 6689 INT z; 6690 INT w; 6691 }; 6692 6693 static struct dec3n dword_to_dec3n(DWORD d) 6694 { 6695 struct dec3n v; 6696 6697 v.x = d & 0x3ff; 6698 v.y = (d & 0xffc00) >> 10; 6699 v.z = (d & 0x3ff00000) >> 20; 6700 v.w = (d & 0xc0000000) >> 30; 6701 6702 return v; 6703 } 6704 6705 static BOOL weld_dec3n(void *to, void *from, FLOAT epsilon) 6706 { 6707 const UINT MAX_DEC3N = 511; 6708 DWORD *d1 = to; 6709 DWORD *d2 = from; 6710 struct dec3n v1 = dword_to_dec3n(*d1); 6711 struct dec3n v2 = dword_to_dec3n(*d2); 6712 INT scaled_epsilon = (INT)(epsilon * MAX_DEC3N); 6713 INT diff_x = abs(v1.x - v2.x); 6714 INT diff_y = abs(v1.y - v2.y); 6715 INT diff_z = abs(v1.z - v2.z); 6716 INT diff_w = abs(v1.w - v2.w); 6717 INT max_abs_diff = max(diff_x, diff_y); 6718 max_abs_diff = max(diff_z, max_abs_diff); 6719 max_abs_diff = max(diff_w, max_abs_diff); 6720 6721 if (max_abs_diff <= scaled_epsilon) 6722 { 6723 memcpy(to, from, sizeof(DWORD)); 6724 6725 return TRUE; 6726 } 6727 6728 return FALSE; 6729 } 6730 6731 static BOOL weld_float16_2(void *to, void *from, FLOAT epsilon) 6732 { 6733 D3DXFLOAT16 *v1_float16 = to; 6734 D3DXFLOAT16 *v2_float16 = from; 6735 FLOAT diff_x; 6736 FLOAT diff_y; 6737 FLOAT max_abs_diff; 6738 #define NUM_ELEM 2 6739 FLOAT v1[NUM_ELEM]; 6740 FLOAT v2[NUM_ELEM]; 6741 6742 D3DXFloat16To32Array(v1, v1_float16, NUM_ELEM); 6743 D3DXFloat16To32Array(v2, v2_float16, NUM_ELEM); 6744 6745 diff_x = fabsf(v1[0] - v2[0]); 6746 diff_y = fabsf(v1[1] - v2[1]); 6747 max_abs_diff = max(diff_x, diff_y); 6748 6749 if (max_abs_diff <= epsilon) 6750 { 6751 memcpy(to, from, NUM_ELEM * sizeof(D3DXFLOAT16)); 6752 6753 return TRUE; 6754 } 6755 6756 return FALSE; 6757 #undef NUM_ELEM 6758 } 6759 6760 static BOOL weld_float16_4(void *to, void *from, FLOAT epsilon) 6761 { 6762 D3DXFLOAT16 *v1_float16 = to; 6763 D3DXFLOAT16 *v2_float16 = from; 6764 FLOAT diff_x; 6765 FLOAT diff_y; 6766 FLOAT diff_z; 6767 FLOAT diff_w; 6768 FLOAT max_abs_diff; 6769 #define NUM_ELEM 4 6770 FLOAT v1[NUM_ELEM]; 6771 FLOAT v2[NUM_ELEM]; 6772 6773 D3DXFloat16To32Array(v1, v1_float16, NUM_ELEM); 6774 D3DXFloat16To32Array(v2, v2_float16, NUM_ELEM); 6775 6776 diff_x = fabsf(v1[0] - v2[0]); 6777 diff_y = fabsf(v1[1] - v2[1]); 6778 diff_z = fabsf(v1[2] - v2[2]); 6779 diff_w = fabsf(v1[3] - v2[3]); 6780 max_abs_diff = max(diff_x, diff_y); 6781 max_abs_diff = max(diff_z, max_abs_diff); 6782 max_abs_diff = max(diff_w, max_abs_diff); 6783 6784 if (max_abs_diff <= epsilon) 6785 { 6786 memcpy(to, from, NUM_ELEM * sizeof(D3DXFLOAT16)); 6787 6788 return TRUE; 6789 } 6790 6791 return FALSE; 6792 #undef NUM_ELEM 6793 } 6794 6795 /* Sets the vertex components to the same value if they are within epsilon. */ 6796 static BOOL weld_component(void *to, void *from, D3DDECLTYPE type, FLOAT epsilon) 6797 { 6798 /* Quiet FIXMEs as this is in a loop with potentially thousand of iterations. */ 6799 BOOL fixme_once_unused = FALSE; 6800 BOOL fixme_once_unknown = FALSE; 6801 6802 switch (type) 6803 { 6804 case D3DDECLTYPE_FLOAT1: 6805 return weld_float1(to, from, epsilon); 6806 6807 case D3DDECLTYPE_FLOAT2: 6808 return weld_float2(to, from, epsilon); 6809 6810 case D3DDECLTYPE_FLOAT3: 6811 return weld_float3(to, from, epsilon); 6812 6813 case D3DDECLTYPE_FLOAT4: 6814 return weld_float4(to, from, epsilon); 6815 6816 case D3DDECLTYPE_D3DCOLOR: 6817 return weld_d3dcolor(to, from, epsilon); 6818 6819 case D3DDECLTYPE_UBYTE4: 6820 return weld_ubyte4(to, from, epsilon); 6821 6822 case D3DDECLTYPE_SHORT2: 6823 return weld_short2(to, from, epsilon); 6824 6825 case D3DDECLTYPE_SHORT4: 6826 return weld_short4(to, from, epsilon); 6827 6828 case D3DDECLTYPE_UBYTE4N: 6829 return weld_ubyte4n(to, from, epsilon); 6830 6831 case D3DDECLTYPE_SHORT2N: 6832 return weld_short2n(to, from, epsilon); 6833 6834 case D3DDECLTYPE_SHORT4N: 6835 return weld_short4n(to, from, epsilon); 6836 6837 case D3DDECLTYPE_USHORT2N: 6838 return weld_ushort2n(to, from, epsilon); 6839 6840 case D3DDECLTYPE_USHORT4N: 6841 return weld_ushort4n(to, from, epsilon); 6842 6843 case D3DDECLTYPE_UDEC3: 6844 return weld_udec3(to, from, epsilon); 6845 6846 case D3DDECLTYPE_DEC3N: 6847 return weld_dec3n(to, from, epsilon); 6848 6849 case D3DDECLTYPE_FLOAT16_2: 6850 return weld_float16_2(to, from, epsilon); 6851 6852 case D3DDECLTYPE_FLOAT16_4: 6853 return weld_float16_4(to, from, epsilon); 6854 6855 case D3DDECLTYPE_UNUSED: 6856 if (!fixme_once_unused++) 6857 FIXME("D3DDECLTYPE_UNUSED welding not implemented.\n"); 6858 break; 6859 6860 default: 6861 if (!fixme_once_unknown++) 6862 FIXME("Welding of unknown declaration type %d is not implemented.\n", type); 6863 break; 6864 } 6865 6866 return FALSE; 6867 } 6868 6869 static FLOAT get_component_epsilon(const D3DVERTEXELEMENT9 *decl_ptr, const D3DXWELDEPSILONS *epsilons) 6870 { 6871 FLOAT epsilon = 0.0f; 6872 /* Quiet FIXMEs as this is in a loop with potentially thousand of iterations. */ 6873 static BOOL fixme_once_blendindices = FALSE; 6874 static BOOL fixme_once_positiont = FALSE; 6875 static BOOL fixme_once_fog = FALSE; 6876 static BOOL fixme_once_depth = FALSE; 6877 static BOOL fixme_once_sample = FALSE; 6878 static BOOL fixme_once_unknown = FALSE; 6879 6880 switch (decl_ptr->Usage) 6881 { 6882 case D3DDECLUSAGE_POSITION: 6883 epsilon = epsilons->Position; 6884 break; 6885 case D3DDECLUSAGE_BLENDWEIGHT: 6886 epsilon = epsilons->BlendWeights; 6887 break; 6888 case D3DDECLUSAGE_NORMAL: 6889 epsilon = epsilons->Normals; 6890 break; 6891 case D3DDECLUSAGE_PSIZE: 6892 epsilon = epsilons->PSize; 6893 break; 6894 case D3DDECLUSAGE_TEXCOORD: 6895 { 6896 BYTE usage_index = decl_ptr->UsageIndex; 6897 if (usage_index > 7) 6898 usage_index = 7; 6899 epsilon = epsilons->Texcoords[usage_index]; 6900 break; 6901 } 6902 case D3DDECLUSAGE_TANGENT: 6903 epsilon = epsilons->Tangent; 6904 break; 6905 case D3DDECLUSAGE_BINORMAL: 6906 epsilon = epsilons->Binormal; 6907 break; 6908 case D3DDECLUSAGE_TESSFACTOR: 6909 epsilon = epsilons->TessFactor; 6910 break; 6911 case D3DDECLUSAGE_COLOR: 6912 if (decl_ptr->UsageIndex == 0) 6913 epsilon = epsilons->Diffuse; 6914 else if (decl_ptr->UsageIndex == 1) 6915 epsilon = epsilons->Specular; 6916 else 6917 epsilon = 1e-6f; 6918 break; 6919 case D3DDECLUSAGE_BLENDINDICES: 6920 if (!fixme_once_blendindices++) 6921 FIXME("D3DDECLUSAGE_BLENDINDICES welding not implemented.\n"); 6922 break; 6923 case D3DDECLUSAGE_POSITIONT: 6924 if (!fixme_once_positiont++) 6925 FIXME("D3DDECLUSAGE_POSITIONT welding not implemented.\n"); 6926 break; 6927 case D3DDECLUSAGE_FOG: 6928 if (!fixme_once_fog++) 6929 FIXME("D3DDECLUSAGE_FOG welding not implemented.\n"); 6930 break; 6931 case D3DDECLUSAGE_DEPTH: 6932 if (!fixme_once_depth++) 6933 FIXME("D3DDECLUSAGE_DEPTH welding not implemented.\n"); 6934 break; 6935 case D3DDECLUSAGE_SAMPLE: 6936 if (!fixme_once_sample++) 6937 FIXME("D3DDECLUSAGE_SAMPLE welding not implemented.\n"); 6938 break; 6939 default: 6940 if (!fixme_once_unknown++) 6941 FIXME("Unknown usage %x\n", decl_ptr->Usage); 6942 break; 6943 } 6944 6945 return epsilon; 6946 } 6947 6948 /* Helper function for reading a 32-bit index buffer. */ 6949 static inline DWORD read_ib(void *index_buffer, BOOL indices_are_32bit, 6950 DWORD index) 6951 { 6952 if (indices_are_32bit) 6953 { 6954 DWORD *indices = index_buffer; 6955 return indices[index]; 6956 } 6957 else 6958 { 6959 WORD *indices = index_buffer; 6960 return indices[index]; 6961 } 6962 } 6963 6964 /* Helper function for writing to a 32-bit index buffer. */ 6965 static inline void write_ib(void *index_buffer, BOOL indices_are_32bit, 6966 DWORD index, DWORD value) 6967 { 6968 if (indices_are_32bit) 6969 { 6970 DWORD *indices = index_buffer; 6971 indices[index] = value; 6972 } 6973 else 6974 { 6975 WORD *indices = index_buffer; 6976 indices[index] = value; 6977 } 6978 } 6979 6980 /************************************************************************* 6981 * D3DXWeldVertices (D3DX9_36.@) 6982 * 6983 * Welds together similar vertices. The similarity between vert- 6984 * ices can be the position and other components such as 6985 * normal and color. 6986 * 6987 * PARAMS 6988 * mesh [I] Mesh which vertices will be welded together. 6989 * flags [I] D3DXWELDEPSILONSFLAGS specifying how to weld. 6990 * epsilons [I] How similar a component needs to be for welding. 6991 * adjacency [I] Which faces are adjacent to other faces. 6992 * adjacency_out [O] Updated adjacency after welding. 6993 * face_remap_out [O] Which faces the old faces have been mapped to. 6994 * vertex_remap_out [O] Which vertices the old vertices have been mapped to. 6995 * 6996 * RETURNS 6997 * Success: D3D_OK. 6998 * Failure: D3DERR_INVALIDCALL, E_OUTOFMEMORY. 6999 * 7000 * BUGS 7001 * Attribute sorting not implemented. 7002 * 7003 */ 7004 HRESULT WINAPI D3DXWeldVertices(ID3DXMesh *mesh, DWORD flags, const D3DXWELDEPSILONS *epsilons, 7005 const DWORD *adjacency, DWORD *adjacency_out, DWORD *face_remap_out, ID3DXBuffer **vertex_remap_out) 7006 { 7007 DWORD *adjacency_generated = NULL; 7008 const DWORD *adjacency_ptr; 7009 DWORD *attributes = NULL; 7010 const FLOAT DEFAULT_EPSILON = 1.0e-6f; 7011 HRESULT hr; 7012 DWORD i; 7013 void *indices = NULL; 7014 BOOL indices_are_32bit = mesh->lpVtbl->GetOptions(mesh) & D3DXMESH_32BIT; 7015 DWORD optimize_flags; 7016 DWORD *point_reps = NULL; 7017 struct d3dx9_mesh *This = impl_from_ID3DXMesh(mesh); 7018 DWORD *vertex_face_map = NULL; 7019 BYTE *vertices = NULL; 7020 7021 TRACE("mesh %p, flags %#x, epsilons %p, adjacency %p, adjacency_out %p, face_remap_out %p, vertex_remap_out %p.\n", 7022 mesh, flags, epsilons, adjacency, adjacency_out, face_remap_out, vertex_remap_out); 7023 7024 if (flags == 0) 7025 { 7026 WARN("No flags are undefined. Using D3DXWELDEPSILONS_WELDPARTIALMATCHES instead.\n"); 7027 flags = D3DXWELDEPSILONS_WELDPARTIALMATCHES; 7028 } 7029 7030 if (adjacency) /* Use supplied adjacency. */ 7031 { 7032 adjacency_ptr = adjacency; 7033 } 7034 else /* Adjacency has to be generated. */ 7035 { 7036 adjacency_generated = HeapAlloc(GetProcessHeap(), 0, 3 * This->numfaces * sizeof(*adjacency_generated)); 7037 if (!adjacency_generated) 7038 { 7039 ERR("Couldn't allocate memory for adjacency_generated.\n"); 7040 hr = E_OUTOFMEMORY; 7041 goto cleanup; 7042 } 7043 hr = mesh->lpVtbl->GenerateAdjacency(mesh, DEFAULT_EPSILON, adjacency_generated); 7044 if (FAILED(hr)) 7045 { 7046 ERR("Couldn't generate adjacency.\n"); 7047 goto cleanup; 7048 } 7049 adjacency_ptr = adjacency_generated; 7050 } 7051 7052 /* Point representation says which vertices can be replaced. */ 7053 point_reps = HeapAlloc(GetProcessHeap(), 0, This->numvertices * sizeof(*point_reps)); 7054 if (!point_reps) 7055 { 7056 hr = E_OUTOFMEMORY; 7057 ERR("Couldn't allocate memory for point_reps.\n"); 7058 goto cleanup; 7059 } 7060 hr = mesh->lpVtbl->ConvertAdjacencyToPointReps(mesh, adjacency_ptr, point_reps); 7061 if (FAILED(hr)) 7062 { 7063 ERR("ConvertAdjacencyToPointReps failed.\n"); 7064 goto cleanup; 7065 } 7066 7067 hr = mesh->lpVtbl->LockIndexBuffer(mesh, 0, &indices); 7068 if (FAILED(hr)) 7069 { 7070 ERR("Couldn't lock index buffer.\n"); 7071 goto cleanup; 7072 } 7073 7074 hr = mesh->lpVtbl->LockAttributeBuffer(mesh, 0, &attributes); 7075 if (FAILED(hr)) 7076 { 7077 ERR("Couldn't lock attribute buffer.\n"); 7078 goto cleanup; 7079 } 7080 vertex_face_map = HeapAlloc(GetProcessHeap(), 0, This->numvertices * sizeof(*vertex_face_map)); 7081 if (!vertex_face_map) 7082 { 7083 hr = E_OUTOFMEMORY; 7084 ERR("Couldn't allocate memory for vertex_face_map.\n"); 7085 goto cleanup; 7086 } 7087 /* Build vertex face map, so that a vertex's face can be looked up. */ 7088 for (i = 0; i < This->numfaces; i++) 7089 { 7090 DWORD j; 7091 for (j = 0; j < 3; j++) 7092 { 7093 DWORD index = read_ib(indices, indices_are_32bit, 3*i + j); 7094 vertex_face_map[index] = i; 7095 } 7096 } 7097 7098 if (flags & D3DXWELDEPSILONS_WELDPARTIALMATCHES) 7099 { 7100 hr = mesh->lpVtbl->LockVertexBuffer(mesh, 0, (void**)&vertices); 7101 if (FAILED(hr)) 7102 { 7103 ERR("Couldn't lock vertex buffer.\n"); 7104 goto cleanup; 7105 } 7106 /* For each vertex that can be removed, compare its vertex components 7107 * with the vertex components from the vertex that can replace it. A 7108 * vertex is only fully replaced if all the components match and the 7109 * flag D3DXWELDEPSILONS_DONOTREMOVEVERTICES is not set, and they 7110 * belong to the same attribute group. Otherwise the vertex components 7111 * that are within epsilon are set to the same value. 7112 */ 7113 for (i = 0; i < 3 * This->numfaces; i++) 7114 { 7115 D3DVERTEXELEMENT9 *decl_ptr; 7116 DWORD vertex_size = mesh->lpVtbl->GetNumBytesPerVertex(mesh); 7117 DWORD num_vertex_components; 7118 INT matches = 0; 7119 BOOL all_match; 7120 DWORD index = read_ib(indices, indices_are_32bit, i); 7121 7122 for (decl_ptr = This->cached_declaration, num_vertex_components = 0; decl_ptr->Stream != 0xFF; decl_ptr++, num_vertex_components++) 7123 { 7124 BYTE *to = &vertices[vertex_size*index + decl_ptr->Offset]; 7125 BYTE *from = &vertices[vertex_size*point_reps[index] + decl_ptr->Offset]; 7126 FLOAT epsilon = get_component_epsilon(decl_ptr, epsilons); 7127 7128 /* Don't weld self */ 7129 if (index == point_reps[index]) 7130 { 7131 matches++; 7132 continue; 7133 } 7134 7135 if (weld_component(to, from, decl_ptr->Type, epsilon)) 7136 matches++; 7137 } 7138 7139 all_match = (num_vertex_components == matches); 7140 if (all_match && !(flags & D3DXWELDEPSILONS_DONOTREMOVEVERTICES)) 7141 { 7142 DWORD to_face = vertex_face_map[index]; 7143 DWORD from_face = vertex_face_map[point_reps[index]]; 7144 if(attributes[to_face] != attributes[from_face] && !(flags & D3DXWELDEPSILONS_DONOTSPLIT)) 7145 continue; 7146 write_ib(indices, indices_are_32bit, i, point_reps[index]); 7147 } 7148 } 7149 mesh->lpVtbl->UnlockVertexBuffer(mesh); 7150 vertices = NULL; 7151 } 7152 else if (flags & D3DXWELDEPSILONS_WELDALL) 7153 { 7154 for (i = 0; i < 3 * This->numfaces; i++) 7155 { 7156 DWORD index = read_ib(indices, indices_are_32bit, i); 7157 DWORD to_face = vertex_face_map[index]; 7158 DWORD from_face = vertex_face_map[point_reps[index]]; 7159 if(attributes[to_face] != attributes[from_face] && !(flags & D3DXWELDEPSILONS_DONOTSPLIT)) 7160 continue; 7161 write_ib(indices, indices_are_32bit, i, point_reps[index]); 7162 } 7163 } 7164 mesh->lpVtbl->UnlockAttributeBuffer(mesh); 7165 attributes = NULL; 7166 mesh->lpVtbl->UnlockIndexBuffer(mesh); 7167 indices = NULL; 7168 7169 /* Compact mesh using OptimizeInplace */ 7170 optimize_flags = D3DXMESHOPT_COMPACT; 7171 hr = mesh->lpVtbl->OptimizeInplace(mesh, optimize_flags, adjacency_ptr, adjacency_out, face_remap_out, vertex_remap_out); 7172 if (FAILED(hr)) 7173 { 7174 ERR("Couldn't compact mesh.\n"); 7175 goto cleanup; 7176 } 7177 7178 hr = D3D_OK; 7179 cleanup: 7180 HeapFree(GetProcessHeap(), 0, adjacency_generated); 7181 HeapFree(GetProcessHeap(), 0, point_reps); 7182 HeapFree(GetProcessHeap(), 0, vertex_face_map); 7183 if (attributes) mesh->lpVtbl->UnlockAttributeBuffer(mesh); 7184 if (indices) mesh->lpVtbl->UnlockIndexBuffer(mesh); 7185 if (vertices) mesh->lpVtbl->UnlockVertexBuffer(mesh); 7186 7187 return hr; 7188 } 7189 7190 7191 /************************************************************************* 7192 * D3DXOptimizeVertices (D3DX9_36.@) 7193 */ 7194 HRESULT WINAPI D3DXOptimizeVertices(const void *indices, UINT num_faces, 7195 UINT num_vertices, BOOL indices_are_32bit, DWORD *vertex_remap) 7196 { 7197 UINT i; 7198 7199 FIXME("indices %p, num_faces %u, num_vertices %u, indices_are_32bit %#x, vertex_remap %p semi-stub.\n", 7200 indices, num_faces, num_vertices, indices_are_32bit, vertex_remap); 7201 7202 if (!vertex_remap) 7203 { 7204 WARN("vertex remap pointer is NULL.\n"); 7205 return D3DERR_INVALIDCALL; 7206 } 7207 7208 for (i = 0; i < num_vertices; i++) 7209 { 7210 vertex_remap[i] = i; 7211 } 7212 7213 return D3D_OK; 7214 } 7215 7216 7217 /************************************************************************* 7218 * D3DXOptimizeFaces (D3DX9_36.@) 7219 * 7220 * Re-orders the faces so the vertex cache is used optimally. 7221 * 7222 * PARAMS 7223 * indices [I] Pointer to an index buffer belonging to a mesh. 7224 * num_faces [I] Number of faces in the mesh. 7225 * num_vertices [I] Number of vertices in the mesh. 7226 * indices_are_32bit [I] Specifies whether indices are 32- or 16-bit. 7227 * face_remap [I/O] The new order the faces should be drawn in. 7228 * 7229 * RETURNS 7230 * Success: D3D_OK. 7231 * Failure: D3DERR_INVALIDCALL. 7232 * 7233 * BUGS 7234 * The face re-ordering does not use the vertex cache optimally. 7235 * 7236 */ 7237 HRESULT WINAPI D3DXOptimizeFaces(const void *indices, UINT num_faces, 7238 UINT num_vertices, BOOL indices_are_32bit, DWORD *face_remap) 7239 { 7240 UINT i; 7241 UINT j = num_faces - 1; 7242 UINT limit_16_bit = 2 << 15; /* According to MSDN */ 7243 HRESULT hr = D3D_OK; 7244 7245 FIXME("indices %p, num_faces %u, num_vertices %u, indices_are_32bit %#x, face_remap %p semi-stub. " 7246 "Face order will not be optimal.\n", 7247 indices, num_faces, num_vertices, indices_are_32bit, face_remap); 7248 7249 if (!indices_are_32bit && num_faces >= limit_16_bit) 7250 { 7251 WARN("Number of faces must be less than %d when using 16-bit indices.\n", 7252 limit_16_bit); 7253 hr = D3DERR_INVALIDCALL; 7254 goto error; 7255 } 7256 7257 if (!face_remap) 7258 { 7259 WARN("Face remap pointer is NULL.\n"); 7260 hr = D3DERR_INVALIDCALL; 7261 goto error; 7262 } 7263 7264 /* The faces are drawn in reverse order for simple meshes. This ordering 7265 * is not optimal for complicated meshes, but will not break anything 7266 * either. The ordering should be changed to take advantage of the vertex 7267 * cache on the graphics card. 7268 * 7269 * TODO Re-order to take advantage of vertex cache. 7270 */ 7271 for (i = 0; i < num_faces; i++) 7272 { 7273 face_remap[i] = j--; 7274 } 7275 7276 return D3D_OK; 7277 7278 error: 7279 return hr; 7280 } 7281 7282 static D3DXVECTOR3 *vertex_element_vec3(BYTE *vertices, const D3DVERTEXELEMENT9 *declaration, 7283 DWORD vertex_stride, DWORD index) 7284 { 7285 return (D3DXVECTOR3 *)(vertices + declaration->Offset + index * vertex_stride); 7286 } 7287 7288 static D3DXVECTOR3 read_vec3(BYTE *vertices, const D3DVERTEXELEMENT9 *declaration, 7289 DWORD vertex_stride, DWORD index) 7290 { 7291 D3DXVECTOR3 vec3 = {0}; 7292 const D3DXVECTOR3 *src = vertex_element_vec3(vertices, declaration, vertex_stride, index); 7293 7294 switch (declaration->Type) 7295 { 7296 case D3DDECLTYPE_FLOAT1: 7297 vec3.x = src->x; 7298 break; 7299 case D3DDECLTYPE_FLOAT2: 7300 vec3.x = src->x; 7301 vec3.y = src->y; 7302 break; 7303 case D3DDECLTYPE_FLOAT3: 7304 case D3DDECLTYPE_FLOAT4: 7305 vec3 = *src; 7306 break; 7307 default: 7308 ERR("Cannot read vec3\n"); 7309 break; 7310 } 7311 7312 return vec3; 7313 } 7314 7315 /************************************************************************* 7316 * D3DXComputeTangentFrameEx (D3DX9_36.@) 7317 */ 7318 HRESULT WINAPI D3DXComputeTangentFrameEx(ID3DXMesh *mesh, DWORD texture_in_semantic, DWORD texture_in_index, 7319 DWORD u_partial_out_semantic, DWORD u_partial_out_index, DWORD v_partial_out_semantic, 7320 DWORD v_partial_out_index, DWORD normal_out_semantic, DWORD normal_out_index, DWORD options, 7321 const DWORD *adjacency, float partial_edge_threshold, float singular_point_threshold, 7322 float normal_edge_threshold, ID3DXMesh **mesh_out, ID3DXBuffer **vertex_mapping) 7323 { 7324 HRESULT hr; 7325 void *indices = NULL; 7326 BYTE *vertices = NULL; 7327 DWORD *point_reps = NULL; 7328 size_t normal_size; 7329 BOOL indices_are_32bit; 7330 DWORD i, j, num_faces, num_vertices, vertex_stride; 7331 D3DVERTEXELEMENT9 declaration[MAX_FVF_DECL_SIZE] = {D3DDECL_END()}; 7332 D3DVERTEXELEMENT9 *position_declaration = NULL, *normal_declaration = NULL; 7333 DWORD weighting_method = options & (D3DXTANGENT_WEIGHT_EQUAL | D3DXTANGENT_WEIGHT_BY_AREA); 7334 7335 TRACE("mesh %p, texture_in_semantic %u, texture_in_index %u, u_partial_out_semantic %u, u_partial_out_index %u, " 7336 "v_partial_out_semantic %u, v_partial_out_index %u, normal_out_semantic %u, normal_out_index %u, " 7337 "options %#x, adjacency %p, partial_edge_threshold %f, singular_point_threshold %f, " 7338 "normal_edge_threshold %f, mesh_out %p, vertex_mapping %p\n", 7339 mesh, texture_in_semantic, texture_in_index, u_partial_out_semantic, u_partial_out_index, 7340 v_partial_out_semantic, v_partial_out_index, normal_out_semantic, normal_out_index, options, adjacency, 7341 partial_edge_threshold, singular_point_threshold, normal_edge_threshold, mesh_out, vertex_mapping); 7342 7343 if (!mesh) 7344 { 7345 WARN("mesh is NULL\n"); 7346 return D3DERR_INVALIDCALL; 7347 } 7348 7349 if (weighting_method == (D3DXTANGENT_WEIGHT_EQUAL | D3DXTANGENT_WEIGHT_BY_AREA)) 7350 { 7351 WARN("D3DXTANGENT_WEIGHT_BY_AREA and D3DXTANGENT_WEIGHT_EQUAL are mutally exclusive\n"); 7352 return D3DERR_INVALIDCALL; 7353 } 7354 7355 if (u_partial_out_semantic != D3DX_DEFAULT) 7356 { 7357 FIXME("tangent vectors computation is not supported\n"); 7358 return E_NOTIMPL; 7359 } 7360 7361 if (v_partial_out_semantic != D3DX_DEFAULT) 7362 { 7363 FIXME("binormal vectors computation is not supported\n"); 7364 return E_NOTIMPL; 7365 } 7366 7367 if (options & ~(D3DXTANGENT_GENERATE_IN_PLACE | D3DXTANGENT_CALCULATE_NORMALS | D3DXTANGENT_WEIGHT_EQUAL | D3DXTANGENT_WEIGHT_BY_AREA)) 7368 { 7369 FIXME("unsupported options %#x\n", options); 7370 return E_NOTIMPL; 7371 } 7372 7373 if (!(options & D3DXTANGENT_CALCULATE_NORMALS)) 7374 { 7375 FIXME("only normals computation is supported\n"); 7376 return E_NOTIMPL; 7377 } 7378 7379 if (!(options & D3DXTANGENT_GENERATE_IN_PLACE) || mesh_out || vertex_mapping) 7380 { 7381 FIXME("only D3DXTANGENT_GENERATE_IN_PLACE is supported\n"); 7382 return E_NOTIMPL; 7383 } 7384 7385 if (FAILED(hr = mesh->lpVtbl->GetDeclaration(mesh, declaration))) 7386 return hr; 7387 7388 for (i = 0; declaration[i].Stream != 0xff; i++) 7389 { 7390 if (declaration[i].Usage == D3DDECLUSAGE_POSITION && !declaration[i].UsageIndex) 7391 position_declaration = &declaration[i]; 7392 if (declaration[i].Usage == normal_out_semantic && declaration[i].UsageIndex == normal_out_index) 7393 normal_declaration = &declaration[i]; 7394 } 7395 7396 if (!position_declaration || !normal_declaration) 7397 return D3DERR_INVALIDCALL; 7398 7399 if (normal_declaration->Type == D3DDECLTYPE_FLOAT3) 7400 { 7401 normal_size = sizeof(D3DXVECTOR3); 7402 } 7403 else if (normal_declaration->Type == D3DDECLTYPE_FLOAT4) 7404 { 7405 normal_size = sizeof(D3DXVECTOR4); 7406 } 7407 else 7408 { 7409 WARN("unsupported normals type %u\n", normal_declaration->Type); 7410 return D3DERR_INVALIDCALL; 7411 } 7412 7413 num_faces = mesh->lpVtbl->GetNumFaces(mesh); 7414 num_vertices = mesh->lpVtbl->GetNumVertices(mesh); 7415 vertex_stride = mesh->lpVtbl->GetNumBytesPerVertex(mesh); 7416 indices_are_32bit = mesh->lpVtbl->GetOptions(mesh) & D3DXMESH_32BIT; 7417 7418 point_reps = HeapAlloc(GetProcessHeap(), 0, num_vertices * sizeof(*point_reps)); 7419 if (!point_reps) 7420 { 7421 hr = E_OUTOFMEMORY; 7422 goto done; 7423 } 7424 7425 if (adjacency) 7426 { 7427 if (FAILED(hr = mesh->lpVtbl->ConvertAdjacencyToPointReps(mesh, adjacency, point_reps))) 7428 goto done; 7429 } 7430 else 7431 { 7432 for (i = 0; i < num_vertices; i++) 7433 point_reps[i] = i; 7434 } 7435 7436 if (FAILED(hr = mesh->lpVtbl->LockIndexBuffer(mesh, 0, &indices))) 7437 goto done; 7438 7439 if (FAILED(hr = mesh->lpVtbl->LockVertexBuffer(mesh, 0, (void **)&vertices))) 7440 goto done; 7441 7442 for (i = 0; i < num_vertices; i++) 7443 { 7444 static const D3DXVECTOR4 default_vector = {0.0f, 0.0f, 0.0f, 1.0f}; 7445 void *normal = vertices + normal_declaration->Offset + i * vertex_stride; 7446 7447 memcpy(normal, &default_vector, normal_size); 7448 } 7449 7450 for (i = 0; i < num_faces; i++) 7451 { 7452 float denominator, weights[3]; 7453 D3DXVECTOR3 a, b, cross, face_normal; 7454 const DWORD face_indices[3] = 7455 { 7456 read_ib(indices, indices_are_32bit, 3 * i + 0), 7457 read_ib(indices, indices_are_32bit, 3 * i + 1), 7458 read_ib(indices, indices_are_32bit, 3 * i + 2) 7459 }; 7460 const D3DXVECTOR3 v0 = read_vec3(vertices, position_declaration, vertex_stride, face_indices[0]); 7461 const D3DXVECTOR3 v1 = read_vec3(vertices, position_declaration, vertex_stride, face_indices[1]); 7462 const D3DXVECTOR3 v2 = read_vec3(vertices, position_declaration, vertex_stride, face_indices[2]); 7463 7464 D3DXVec3Cross(&cross, D3DXVec3Subtract(&a, &v0, &v1), D3DXVec3Subtract(&b, &v0, &v2)); 7465 7466 switch (weighting_method) 7467 { 7468 case D3DXTANGENT_WEIGHT_EQUAL: 7469 weights[0] = weights[1] = weights[2] = 1.0f; 7470 break; 7471 case D3DXTANGENT_WEIGHT_BY_AREA: 7472 weights[0] = weights[1] = weights[2] = D3DXVec3Length(&cross); 7473 break; 7474 default: 7475 /* weight by angle */ 7476 denominator = D3DXVec3Length(&a) * D3DXVec3Length(&b); 7477 if (!denominator) 7478 weights[0] = 0.0f; 7479 else 7480 weights[0] = acosf(D3DXVec3Dot(&a, &b) / denominator); 7481 7482 D3DXVec3Subtract(&a, &v1, &v0); 7483 D3DXVec3Subtract(&b, &v1, &v2); 7484 denominator = D3DXVec3Length(&a) * D3DXVec3Length(&b); 7485 if (!denominator) 7486 weights[1] = 0.0f; 7487 else 7488 weights[1] = acosf(D3DXVec3Dot(&a, &b) / denominator); 7489 7490 D3DXVec3Subtract(&a, &v2, &v0); 7491 D3DXVec3Subtract(&b, &v2, &v1); 7492 denominator = D3DXVec3Length(&a) * D3DXVec3Length(&b); 7493 if (!denominator) 7494 weights[2] = 0.0f; 7495 else 7496 weights[2] = acosf(D3DXVec3Dot(&a, &b) / denominator); 7497 7498 break; 7499 } 7500 7501 D3DXVec3Normalize(&face_normal, &cross); 7502 7503 for (j = 0; j < 3; j++) 7504 { 7505 D3DXVECTOR3 normal; 7506 DWORD rep_index = point_reps[face_indices[j]]; 7507 D3DXVECTOR3 *rep_normal = vertex_element_vec3(vertices, normal_declaration, vertex_stride, rep_index); 7508 7509 D3DXVec3Scale(&normal, &face_normal, weights[j]); 7510 D3DXVec3Add(rep_normal, rep_normal, &normal); 7511 } 7512 } 7513 7514 for (i = 0; i < num_vertices; i++) 7515 { 7516 DWORD rep_index = point_reps[i]; 7517 D3DXVECTOR3 *normal = vertex_element_vec3(vertices, normal_declaration, vertex_stride, i); 7518 D3DXVECTOR3 *rep_normal = vertex_element_vec3(vertices, normal_declaration, vertex_stride, rep_index); 7519 7520 if (i == rep_index) 7521 D3DXVec3Normalize(rep_normal, rep_normal); 7522 else 7523 *normal = *rep_normal; 7524 } 7525 7526 hr = D3D_OK; 7527 7528 done: 7529 if (vertices) 7530 mesh->lpVtbl->UnlockVertexBuffer(mesh); 7531 7532 if (indices) 7533 mesh->lpVtbl->UnlockIndexBuffer(mesh); 7534 7535 HeapFree(GetProcessHeap(), 0, point_reps); 7536 7537 return hr; 7538 } 7539 7540 /************************************************************************* 7541 * D3DXComputeTangent (D3DX9_36.@) 7542 */ 7543 HRESULT WINAPI D3DXComputeTangent(ID3DXMesh *mesh, DWORD stage_idx, DWORD tangent_idx, 7544 DWORD binorm_idx, DWORD wrap, const DWORD *adjacency) 7545 { 7546 TRACE("mesh %p, stage_idx %d, tangent_idx %d, binorm_idx %d, wrap %d, adjacency %p.\n", 7547 mesh, stage_idx, tangent_idx, binorm_idx, wrap, adjacency); 7548 7549 return D3DXComputeTangentFrameEx( mesh, D3DDECLUSAGE_TEXCOORD, stage_idx, 7550 ( binorm_idx == D3DX_DEFAULT ) ? D3DX_DEFAULT : D3DDECLUSAGE_BINORMAL, 7551 binorm_idx, 7552 ( tangent_idx == D3DX_DEFAULT ) ? D3DX_DEFAULT : D3DDECLUSAGE_TANGENT, 7553 tangent_idx, D3DX_DEFAULT, 0, 7554 ( wrap ? D3DXTANGENT_WRAP_UV : 0 ) | D3DXTANGENT_GENERATE_IN_PLACE | D3DXTANGENT_ORTHOGONALIZE_FROM_U, 7555 adjacency, -1.01f, -0.01f, -1.01f, NULL, NULL); 7556 } 7557 7558 /************************************************************************* 7559 * D3DXComputeNormals (D3DX9_36.@) 7560 */ 7561 HRESULT WINAPI D3DXComputeNormals(struct ID3DXBaseMesh *mesh, const DWORD *adjacency) 7562 { 7563 TRACE("mesh %p, adjacency %p\n", mesh, adjacency); 7564 7565 if (mesh && (ID3DXMeshVtbl *)mesh->lpVtbl != &D3DXMesh_Vtbl) 7566 { 7567 ERR("Invalid virtual table\n"); 7568 return D3DERR_INVALIDCALL; 7569 } 7570 7571 return D3DXComputeTangentFrameEx((ID3DXMesh *)mesh, D3DX_DEFAULT, 0, 7572 D3DX_DEFAULT, 0, D3DX_DEFAULT, 0, D3DDECLUSAGE_NORMAL, 0, 7573 D3DXTANGENT_GENERATE_IN_PLACE | D3DXTANGENT_CALCULATE_NORMALS, 7574 adjacency, -1.01f, -0.01f, -1.01f, NULL, NULL); 7575 } 7576 7577 /************************************************************************* 7578 * D3DXComputeNormalMap (D3DX9_36.@) 7579 */ 7580 HRESULT WINAPI D3DXComputeNormalMap(IDirect3DTexture9 *texture, IDirect3DTexture9 *src_texture, 7581 const PALETTEENTRY *src_palette, DWORD flags, DWORD channel, FLOAT amplitude) 7582 { 7583 FIXME("texture %p, src_texture %p, src_palette %p, flags %#x, channel %u, amplitude %f stub.\n", 7584 texture, src_texture, src_palette, flags, channel, amplitude); 7585 7586 return D3D_OK; 7587 } 7588 7589 /************************************************************************* 7590 * D3DXIntersect (D3DX9_36.@) 7591 */ 7592 HRESULT WINAPI D3DXIntersect(ID3DXBaseMesh *mesh, const D3DXVECTOR3 *ray_pos, const D3DXVECTOR3 *ray_dir, 7593 BOOL *hit, DWORD *face_index, float *u, float *v, float *distance, ID3DXBuffer **all_hits, DWORD *count_of_hits) 7594 { 7595 FIXME("mesh %p, ray_pos %p, ray_dir %p, hit %p, face_index %p, u %p, v %p, distance %p, all_hits %p, " 7596 "count_of_hits %p stub!\n", mesh, ray_pos, ray_dir, hit, face_index, u, v, distance, all_hits, count_of_hits); 7597 7598 return E_NOTIMPL; 7599 } 7600 7601 HRESULT WINAPI D3DXTessellateNPatches(ID3DXMesh *mesh, const DWORD *adjacency_in, float num_segs, 7602 BOOL quadratic_normals, ID3DXMesh **mesh_out, ID3DXBuffer **adjacency_out) 7603 { 7604 FIXME("mesh %p, adjacency_in %p, num_segs %f, quadratic_normals %d, mesh_out %p, adjacency_out %p stub.\n", 7605 mesh, adjacency_in, num_segs, quadratic_normals, mesh_out, adjacency_out); 7606 7607 return E_NOTIMPL; 7608 } 7609 7610 HRESULT WINAPI D3DXConvertMeshSubsetToSingleStrip(struct ID3DXBaseMesh *mesh_in, DWORD attribute_id, 7611 DWORD ib_flags, struct IDirect3DIndexBuffer9 **index_buffer, DWORD *index_count) 7612 { 7613 FIXME("mesh_in %p, attribute_id %u, ib_flags %u, index_buffer %p, index_count %p stub.\n", 7614 mesh_in, attribute_id, ib_flags, index_buffer, index_count); 7615 7616 return E_NOTIMPL; 7617 } 7618 7619 struct frame_node 7620 { 7621 struct list entry; 7622 D3DXFRAME *frame; 7623 }; 7624 7625 static BOOL queue_frame_node(struct list *queue, D3DXFRAME *frame) 7626 { 7627 struct frame_node *node; 7628 7629 if (!frame->pFrameFirstChild) 7630 return TRUE; 7631 7632 node = HeapAlloc(GetProcessHeap(), 0, sizeof(*node)); 7633 if (!node) 7634 return FALSE; 7635 7636 node->frame = frame; 7637 list_add_tail(queue, &node->entry); 7638 7639 return TRUE; 7640 } 7641 7642 static void empty_frame_queue(struct list *queue) 7643 { 7644 struct frame_node *cur, *cur2; 7645 LIST_FOR_EACH_ENTRY_SAFE(cur, cur2, queue, struct frame_node, entry) 7646 { 7647 list_remove(&cur->entry); 7648 HeapFree(GetProcessHeap(), 0, cur); 7649 } 7650 } 7651 7652 D3DXFRAME * WINAPI D3DXFrameFind(const D3DXFRAME *root, const char *name) 7653 { 7654 D3DXFRAME *found = NULL, *frame; 7655 struct list queue; 7656 7657 TRACE("root frame %p, name %s.\n", root, debugstr_a(name)); 7658 7659 if (!root) 7660 return NULL; 7661 7662 list_init(&queue); 7663 7664 frame = (D3DXFRAME *)root; 7665 7666 for (;;) 7667 { 7668 struct frame_node *node; 7669 7670 while (frame) 7671 { 7672 if ((name && frame->Name && !strcmp(frame->Name, name)) || (!name && !frame->Name)) 7673 { 7674 found = frame; 7675 goto cleanup; 7676 } 7677 7678 if (!queue_frame_node(&queue, frame)) 7679 goto cleanup; 7680 7681 frame = frame->pFrameSibling; 7682 } 7683 7684 if (list_empty(&queue)) 7685 break; 7686 7687 node = LIST_ENTRY(list_head(&queue), struct frame_node, entry); 7688 list_remove(&node->entry); 7689 frame = node->frame->pFrameFirstChild; 7690 HeapFree(GetProcessHeap(), 0, node); 7691 } 7692 7693 cleanup: 7694 empty_frame_queue(&queue); 7695 7696 return found; 7697 } 7698