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