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