ReactOS  0.4.14-dev-608-gd495a4f
mesh.c
Go to the documentation of this file.
1  /*
2  * Mesh operations specific to D3DX9.
3  *
4  * Copyright (C) 2005 Henri Verbeet
5  * Copyright (C) 2006 Ivan Gyurdiev
6  * Copyright (C) 2009 David Adam
7  * Copyright (C) 2010 Tony Wasserka
8  * Copyright (C) 2011 Dylan Smith
9  * Copyright (C) 2011 Michael Mc Donnell
10  * Copyright (C) 2013 Christian Costa
11  *
12  * This library is free software; you can redistribute it and/or
13  * modify it under the terms of the GNU Lesser General Public
14  * License as published by the Free Software Foundation; either
15  * version 2.1 of the License, or (at your option) any later version.
16  *
17  * This library is distributed in the hope that it will be useful,
18  * but WITHOUT ANY WARRANTY; without even the implied warranty of
19  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
20  * Lesser General Public License for more details.
21  *
22  * You should have received a copy of the GNU Lesser General Public
23  * License along with this library; if not, write to the Free Software
24  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
25  */
26 
27 #include "config.h"
28 #include "wine/port.h"
29 
30 #include <assert.h>
31 #ifdef HAVE_FLOAT_H
32 # include <float.h>
33 #endif
34 
35 #include "d3dx9_private.h"
36 #undef MAKE_DDHRESULT
37 #include "dxfile.h"
38 #include "rmxfguid.h"
39 #include "rmxftmpl.h"
40 #include "wine/unicode.h"
41 #include "wine/list.h"
42 
44 
45 struct d3dx9_mesh
46 {
47  ID3DXMesh ID3DXMesh_iface;
49 
54  IDirect3DDevice9 *device;
56  IDirect3DVertexDeclaration9 *vertex_declaration;
59  IDirect3DVertexBuffer9 *vertex_buffer;
60  IDirect3DIndexBuffer9 *index_buffer;
65 };
66 
67 static const UINT d3dx_decltype_size[] =
68 {
69  /* D3DDECLTYPE_FLOAT1 */ sizeof(FLOAT),
70  /* D3DDECLTYPE_FLOAT2 */ sizeof(D3DXVECTOR2),
71  /* D3DDECLTYPE_FLOAT3 */ sizeof(D3DXVECTOR3),
72  /* D3DDECLTYPE_FLOAT4 */ sizeof(D3DXVECTOR4),
73  /* D3DDECLTYPE_D3DCOLOR */ sizeof(D3DCOLOR),
74  /* D3DDECLTYPE_UBYTE4 */ 4 * sizeof(BYTE),
75  /* D3DDECLTYPE_SHORT2 */ 2 * sizeof(SHORT),
76  /* D3DDECLTYPE_SHORT4 */ 4 * sizeof(SHORT),
77  /* D3DDECLTYPE_UBYTE4N */ 4 * sizeof(BYTE),
78  /* D3DDECLTYPE_SHORT2N */ 2 * sizeof(SHORT),
79  /* D3DDECLTYPE_SHORT4N */ 4 * sizeof(SHORT),
80  /* D3DDECLTYPE_USHORT2N */ 2 * sizeof(USHORT),
81  /* D3DDECLTYPE_USHORT4N */ 4 * sizeof(USHORT),
82  /* D3DDECLTYPE_UDEC3 */ 4, /* 3 * 10 bits + 2 padding */
83  /* D3DDECLTYPE_DEC3N */ 4,
84  /* D3DDECLTYPE_FLOAT16_2 */ 2 * sizeof(D3DXFLOAT16),
85  /* D3DDECLTYPE_FLOAT16_4 */ 4 * sizeof(D3DXFLOAT16),
86 };
87 
88 static inline struct d3dx9_mesh *impl_from_ID3DXMesh(ID3DXMesh *iface)
89 {
90  return CONTAINING_RECORD(iface, struct d3dx9_mesh, ID3DXMesh_iface);
91 }
92 
93 static HRESULT WINAPI d3dx9_mesh_QueryInterface(ID3DXMesh *iface, REFIID riid, void **out)
94 {
95  TRACE("iface %p, riid %s, out %p.\n", iface, debugstr_guid(riid), out);
96 
97  if (IsEqualGUID(riid, &IID_IUnknown) ||
98  IsEqualGUID(riid, &IID_ID3DXBaseMesh) ||
99  IsEqualGUID(riid, &IID_ID3DXMesh))
100  {
101  iface->lpVtbl->AddRef(iface);
102  *out = iface;
103  return S_OK;
104  }
105 
106  WARN("Interface %s not found.\n", debugstr_guid(riid));
107 
108  return E_NOINTERFACE;
109 }
110 
111 static ULONG WINAPI d3dx9_mesh_AddRef(ID3DXMesh *iface)
112 {
113  struct d3dx9_mesh *mesh = impl_from_ID3DXMesh(iface);
114  ULONG refcount = InterlockedIncrement(&mesh->ref);
115 
116  TRACE("%p increasing refcount to %u.\n", mesh, refcount);
117 
118  return refcount;
119 }
120 
121 static ULONG WINAPI d3dx9_mesh_Release(ID3DXMesh *iface)
122 {
123  struct d3dx9_mesh *mesh = impl_from_ID3DXMesh(iface);
124  ULONG refcount = InterlockedDecrement(&mesh->ref);
125 
126  TRACE("%p decreasing refcount to %u.\n", mesh, refcount);
127 
128  if (!refcount)
129  {
132  if (mesh->vertex_declaration)
136  HeapFree(GetProcessHeap(), 0, mesh->attrib_table);
137  HeapFree(GetProcessHeap(), 0, mesh);
138  }
139 
140  return refcount;
141 }
142 
143 static HRESULT WINAPI d3dx9_mesh_DrawSubset(ID3DXMesh *iface, DWORD attrib_id)
144 {
145  struct d3dx9_mesh *This = impl_from_ID3DXMesh(iface);
146  HRESULT hr;
147  DWORD face_start;
148  DWORD face_end = 0;
149  DWORD vertex_size;
150 
151  TRACE("iface %p, attrib_id %u.\n", iface, attrib_id);
152 
153  if (!This->vertex_declaration)
154  {
155  WARN("Can't draw a mesh with an invalid vertex declaration.\n");
156  return E_FAIL;
157  }
158 
159  vertex_size = iface->lpVtbl->GetNumBytesPerVertex(iface);
160 
161  hr = IDirect3DDevice9_SetVertexDeclaration(This->device, This->vertex_declaration);
162  if (FAILED(hr)) return hr;
163  hr = IDirect3DDevice9_SetStreamSource(This->device, 0, This->vertex_buffer, 0, vertex_size);
164  if (FAILED(hr)) return hr;
165  hr = IDirect3DDevice9_SetIndices(This->device, This->index_buffer);
166  if (FAILED(hr)) return hr;
167 
168  while (face_end < This->numfaces)
169  {
170  for (face_start = face_end; face_start < This->numfaces; face_start++)
171  {
172  if (This->attrib_buffer[face_start] == attrib_id)
173  break;
174  }
175  if (face_start >= This->numfaces)
176  break;
177  for (face_end = face_start + 1; face_end < This->numfaces; face_end++)
178  {
179  if (This->attrib_buffer[face_end] != attrib_id)
180  break;
181  }
182 
184  0, 0, This->numvertices, face_start * 3, face_end - face_start);
185  if (FAILED(hr)) return hr;
186  }
187 
188  return D3D_OK;
189 }
190 
191 static DWORD WINAPI d3dx9_mesh_GetNumFaces(ID3DXMesh *iface)
192 {
193  struct d3dx9_mesh *mesh = impl_from_ID3DXMesh(iface);
194 
195  TRACE("iface %p.\n", iface);
196 
197  return mesh->numfaces;
198 }
199 
200 static DWORD WINAPI d3dx9_mesh_GetNumVertices(ID3DXMesh *iface)
201 {
202  struct d3dx9_mesh *mesh = impl_from_ID3DXMesh(iface);
203 
204  TRACE("iface %p.\n", iface);
205 
206  return mesh->numvertices;
207 }
208 
209 static DWORD WINAPI d3dx9_mesh_GetFVF(ID3DXMesh *iface)
210 {
211  struct d3dx9_mesh *mesh = impl_from_ID3DXMesh(iface);
212 
213  TRACE("iface %p.\n", iface);
214 
215  return mesh->fvf;
216 }
217 
219 {
220  memcpy(dst, src, num_elem * sizeof(*src));
221 }
222 
224 {
225  struct d3dx9_mesh *mesh = impl_from_ID3DXMesh(iface);
226 
227  TRACE("iface %p, declaration %p.\n", iface, declaration);
228 
229  if (!declaration)
230  return D3DERR_INVALIDCALL;
231 
233 
234  return D3D_OK;
235 }
236 
238 {
239  struct d3dx9_mesh *mesh = impl_from_ID3DXMesh(iface);
240 
241  TRACE("iface %p.\n", iface);
242 
243  return mesh->vertex_declaration_size;
244 }
245 
246 static DWORD WINAPI d3dx9_mesh_GetOptions(ID3DXMesh *iface)
247 {
248  struct d3dx9_mesh *mesh = impl_from_ID3DXMesh(iface);
249 
250  TRACE("iface %p.\n", iface);
251 
252  return mesh->options;
253 }
254 
255 static HRESULT WINAPI d3dx9_mesh_GetDevice(struct ID3DXMesh *iface, struct IDirect3DDevice9 **device)
256 {
257  struct d3dx9_mesh *mesh = impl_from_ID3DXMesh(iface);
258 
259  TRACE("iface %p, device %p.\n", iface, device);
260 
261  if (!device)
262  return D3DERR_INVALIDCALL;
263  *device = mesh->device;
265 
266  return D3D_OK;
267 }
268 
269 static HRESULT WINAPI d3dx9_mesh_CloneMeshFVF(struct ID3DXMesh *iface, DWORD options, DWORD fvf,
270  struct IDirect3DDevice9 *device, struct ID3DXMesh **clone_mesh)
271 {
272  HRESULT hr;
274 
275  TRACE("iface %p, options %#x, fvf %#x, device %p, clone_mesh %p.\n",
276  iface, options, fvf, device, clone_mesh);
277 
279  return hr;
280 
281  return iface->lpVtbl->CloneMesh(iface, options, declaration, device, clone_mesh);
282 }
283 
285 {
286  value = value * UCHAR_MAX;
287 
288  if (value < 0.0f)
289  {
290  return 0.0f;
291  }
292  else
293  {
294  if (value > UCHAR_MAX) /* Clamp at 255 */
295  return UCHAR_MAX;
296  else
297  return value;
298  }
299 }
300 
302 {
303  value = value * SHRT_MAX;
304 
305  /* The tests show that the range is SHRT_MIN + 1 to SHRT_MAX. */
306  if (value <= SHRT_MIN)
307  {
308  return SHRT_MIN + 1;
309  }
310  else if (value > SHRT_MAX)
311  {
312  return SHRT_MAX;
313  }
314  else
315  {
316  return value;
317  }
318 }
319 
321 {
322  value = value * USHRT_MAX;
323 
324  if (value < 0.0f)
325  {
326  return 0.0f;
327  }
328  else
329  {
330  if (value > USHRT_MAX) /* Clamp at 65535 */
331  return USHRT_MAX;
332  else
333  return value;
334  }
335 }
336 
338 {
339  int res = (INT)(value + 0.5f);
340 
341  return res;
342 }
343 
344 static void convert_float4(BYTE *dst, const D3DXVECTOR4 *src, D3DDECLTYPE type_dst)
345 {
346  BOOL fixme_once = FALSE;
347 
348  switch (type_dst)
349  {
350  case D3DDECLTYPE_FLOAT1:
351  {
352  FLOAT *dst_ptr = (FLOAT*)dst;
353  *dst_ptr = src->x;
354  break;
355  }
356  case D3DDECLTYPE_FLOAT2:
357  {
358  D3DXVECTOR2 *dst_ptr = (D3DXVECTOR2*)dst;
359  dst_ptr->x = src->x;
360  dst_ptr->y = src->y;
361  break;
362  }
363  case D3DDECLTYPE_FLOAT3:
364  {
365  D3DXVECTOR3 *dst_ptr = (D3DXVECTOR3*)dst;
366  dst_ptr->x = src->x;
367  dst_ptr->y = src->y;
368  dst_ptr->z = src->z;
369  break;
370  }
371  case D3DDECLTYPE_FLOAT4:
372  {
373  D3DXVECTOR4 *dst_ptr = (D3DXVECTOR4*)dst;
374  dst_ptr->x = src->x;
375  dst_ptr->y = src->y;
376  dst_ptr->z = src->z;
377  dst_ptr->w = src->w;
378  break;
379  }
381  {
386  break;
387  }
388  case D3DDECLTYPE_UBYTE4:
389  {
390  dst[0] = src->x < 0.0f ? 0 : (BYTE)simple_round(src->x);
391  dst[1] = src->y < 0.0f ? 0 : (BYTE)simple_round(src->y);
392  dst[2] = src->z < 0.0f ? 0 : (BYTE)simple_round(src->z);
393  dst[3] = src->w < 0.0f ? 0 : (BYTE)simple_round(src->w);
394  break;
395  }
396  case D3DDECLTYPE_SHORT2:
397  {
398  SHORT *dst_ptr = (SHORT*)dst;
399  dst_ptr[0] = (SHORT)simple_round(src->x);
400  dst_ptr[1] = (SHORT)simple_round(src->y);
401  break;
402  }
403  case D3DDECLTYPE_SHORT4:
404  {
405  SHORT *dst_ptr = (SHORT*)dst;
406  dst_ptr[0] = (SHORT)simple_round(src->x);
407  dst_ptr[1] = (SHORT)simple_round(src->y);
408  dst_ptr[2] = (SHORT)simple_round(src->z);
409  dst_ptr[3] = (SHORT)simple_round(src->w);
410  break;
411  }
412  case D3DDECLTYPE_UBYTE4N:
413  {
418  break;
419  }
420  case D3DDECLTYPE_SHORT2N:
421  {
422  SHORT *dst_ptr = (SHORT*)dst;
423  dst_ptr[0] = (SHORT)simple_round(scale_clamp_shortn(src->x));
424  dst_ptr[1] = (SHORT)simple_round(scale_clamp_shortn(src->y));
425  break;
426  }
427  case D3DDECLTYPE_SHORT4N:
428  {
429  SHORT *dst_ptr = (SHORT*)dst;
430  dst_ptr[0] = (SHORT)simple_round(scale_clamp_shortn(src->x));
431  dst_ptr[1] = (SHORT)simple_round(scale_clamp_shortn(src->y));
432  dst_ptr[2] = (SHORT)simple_round(scale_clamp_shortn(src->z));
433  dst_ptr[3] = (SHORT)simple_round(scale_clamp_shortn(src->w));
434  break;
435  }
437  {
438  USHORT *dst_ptr = (USHORT*)dst;
439  dst_ptr[0] = (USHORT)simple_round(scale_clamp_ushortn(src->x));
440  dst_ptr[1] = (USHORT)simple_round(scale_clamp_ushortn(src->y));
441  break;
442  }
444  {
445  USHORT *dst_ptr = (USHORT*)dst;
446  dst_ptr[0] = (USHORT)simple_round(scale_clamp_ushortn(src->x));
447  dst_ptr[1] = (USHORT)simple_round(scale_clamp_ushortn(src->y));
448  dst_ptr[2] = (USHORT)simple_round(scale_clamp_ushortn(src->z));
449  dst_ptr[3] = (USHORT)simple_round(scale_clamp_ushortn(src->w));
450  break;
451  }
453  {
455  break;
456  }
458  {
460  break;
461  }
462  default:
463  if (!fixme_once++)
464  FIXME("Conversion from D3DDECLTYPE_FLOAT4 to %d not implemented.\n", type_dst);
465  break;
466  }
467 }
468 
469 static void convert_component(BYTE *dst, BYTE *src, D3DDECLTYPE type_dst, D3DDECLTYPE type_src)
470 {
471  BOOL fixme_once = FALSE;
472 
473  switch (type_src)
474  {
475  case D3DDECLTYPE_FLOAT1:
476  {
477  FLOAT *src_ptr = (FLOAT*)src;
478  D3DXVECTOR4 src_float4 = {*src_ptr, 0.0f, 0.0f, 1.0f};
479  convert_float4(dst, &src_float4, type_dst);
480  break;
481  }
482  case D3DDECLTYPE_FLOAT2:
483  {
484  D3DXVECTOR2 *src_ptr = (D3DXVECTOR2*)src;
485  D3DXVECTOR4 src_float4 = {src_ptr->x, src_ptr->y, 0.0f, 1.0f};
486  convert_float4(dst, &src_float4, type_dst);
487  break;
488  }
489  case D3DDECLTYPE_FLOAT3:
490  {
491  D3DXVECTOR3 *src_ptr = (D3DXVECTOR3*)src;
492  D3DXVECTOR4 src_float4 = {src_ptr->x, src_ptr->y, src_ptr->z, 1.0f};
493  convert_float4(dst, &src_float4, type_dst);
494  break;
495  }
496  case D3DDECLTYPE_FLOAT4:
497  {
498  D3DXVECTOR4 *src_ptr = (D3DXVECTOR4*)src;
499  D3DXVECTOR4 src_float4 = {src_ptr->x, src_ptr->y, src_ptr->z, src_ptr->w};
500  convert_float4(dst, &src_float4, type_dst);
501  break;
502  }
504  {
505  D3DXVECTOR4 src_float4 =
506  {
507  (FLOAT)src[2]/UCHAR_MAX,
508  (FLOAT)src[1]/UCHAR_MAX,
509  (FLOAT)src[0]/UCHAR_MAX,
510  (FLOAT)src[3]/UCHAR_MAX
511  };
512  convert_float4(dst, &src_float4, type_dst);
513  break;
514  }
515  case D3DDECLTYPE_UBYTE4:
516  {
517  D3DXVECTOR4 src_float4 = {src[0], src[1], src[2], src[3]};
518  convert_float4(dst, &src_float4, type_dst);
519  break;
520  }
521  case D3DDECLTYPE_SHORT2:
522  {
523  SHORT *src_ptr = (SHORT*)src;
524  D3DXVECTOR4 src_float4 = {src_ptr[0], src_ptr[1], 0.0f, 1.0f};
525  convert_float4(dst, &src_float4, type_dst);
526  break;
527  }
528  case D3DDECLTYPE_SHORT4:
529  {
530  SHORT *src_ptr = (SHORT*)src;
531  D3DXVECTOR4 src_float4 = {src_ptr[0], src_ptr[1], src_ptr[2], src_ptr[3]};
532  convert_float4(dst, &src_float4, type_dst);
533  break;
534  }
535  case D3DDECLTYPE_UBYTE4N:
536  {
537  D3DXVECTOR4 src_float4 =
538  {
539  (FLOAT)src[0]/UCHAR_MAX,
540  (FLOAT)src[1]/UCHAR_MAX,
541  (FLOAT)src[2]/UCHAR_MAX,
542  (FLOAT)src[3]/UCHAR_MAX
543  };
544  convert_float4(dst, &src_float4, type_dst);
545  break;
546  }
547  case D3DDECLTYPE_SHORT2N:
548  {
549  SHORT *src_ptr = (SHORT*)src;
550  D3DXVECTOR4 src_float4 = {(FLOAT)src_ptr[0]/SHRT_MAX, (FLOAT)src_ptr[1]/SHRT_MAX, 0.0f, 1.0f};
551  convert_float4(dst, &src_float4, type_dst);
552  break;
553  }
554  case D3DDECLTYPE_SHORT4N:
555  {
556  SHORT *src_ptr = (SHORT*)src;
557  D3DXVECTOR4 src_float4 =
558  {
559  (FLOAT)src_ptr[0]/SHRT_MAX,
560  (FLOAT)src_ptr[1]/SHRT_MAX,
561  (FLOAT)src_ptr[2]/SHRT_MAX,
562  (FLOAT)src_ptr[3]/SHRT_MAX
563  };
564  convert_float4(dst, &src_float4, type_dst);
565  break;
566  }
568  {
569  D3DXVECTOR4 src_float4 = {0.0f, 0.0f, 0.0f, 1.0f};
570  D3DXFloat16To32Array((FLOAT*)&src_float4, (D3DXFLOAT16*)src, 2);
571  convert_float4(dst, &src_float4, type_dst);
572  break;
573  }
575  {
576  D3DXVECTOR4 src_float4;
577  D3DXFloat16To32Array((FLOAT*)&src_float4, (D3DXFLOAT16*)src, 4);
578  convert_float4(dst, &src_float4, type_dst);
579  break;
580  }
581  default:
582  if (!fixme_once++)
583  FIXME("Conversion of D3DDECLTYPE %d to %d not implemented.\n", type_src, type_dst);
584  break;
585  }
586 }
587 
589 {
590  INT i;
591 
592  for (i = 0; declaration[i].Stream != 0xff; i++)
593  {
594  if (orig_declaration.Usage == declaration[i].Usage
595  && orig_declaration.UsageIndex == declaration[i].UsageIndex)
596  {
597  return i;
598  }
599  }
600 
601  return -1;
602 }
603 
604 static HRESULT convert_vertex_buffer(ID3DXMesh *mesh_dst, ID3DXMesh *mesh_src)
605 {
606  HRESULT hr;
607  D3DVERTEXELEMENT9 orig_declaration[MAX_FVF_DECL_SIZE] = {D3DDECL_END()};
609  BYTE *vb_dst = NULL;
610  BYTE *vb_src = NULL;
611  UINT i;
612  UINT num_vertices = mesh_src->lpVtbl->GetNumVertices(mesh_src);
613  UINT dst_vertex_size = mesh_dst->lpVtbl->GetNumBytesPerVertex(mesh_dst);
614  UINT src_vertex_size = mesh_src->lpVtbl->GetNumBytesPerVertex(mesh_src);
615 
616  hr = mesh_src->lpVtbl->GetDeclaration(mesh_src, orig_declaration);
617  if (FAILED(hr)) return hr;
618  hr = mesh_dst->lpVtbl->GetDeclaration(mesh_dst, declaration);
619  if (FAILED(hr)) return hr;
620 
621  hr = mesh_src->lpVtbl->LockVertexBuffer(mesh_src, D3DLOCK_READONLY, (void**)&vb_src);
622  if (FAILED(hr)) goto cleanup;
623  hr = mesh_dst->lpVtbl->LockVertexBuffer(mesh_dst, 0, (void**)&vb_dst);
624  if (FAILED(hr)) goto cleanup;
625 
626  /* Clear all new fields by clearing the entire vertex buffer. */
627  memset(vb_dst, 0, num_vertices * dst_vertex_size);
628 
629  for (i = 0; orig_declaration[i].Stream != 0xff; i++)
630  {
631  INT eq_idx = get_equivalent_declaration_index(orig_declaration[i], declaration);
632 
633  if (eq_idx >= 0)
634  {
635  UINT j;
636  for (j = 0; j < num_vertices; j++)
637  {
638  UINT idx_dst = dst_vertex_size * j + declaration[eq_idx].Offset;
639  UINT idx_src = src_vertex_size * j + orig_declaration[i].Offset;
640  UINT type_size = d3dx_decltype_size[orig_declaration[i].Type];
641 
642  if (orig_declaration[i].Type == declaration[eq_idx].Type)
643  memcpy(&vb_dst[idx_dst], &vb_src[idx_src], type_size);
644  else
645  convert_component(&vb_dst[idx_dst], &vb_src[idx_src], declaration[eq_idx].Type, orig_declaration[i].Type);
646  }
647  }
648  }
649 
650  hr = D3D_OK;
651 cleanup:
652  if (vb_dst) mesh_dst->lpVtbl->UnlockVertexBuffer(mesh_dst);
653  if (vb_src) mesh_src->lpVtbl->UnlockVertexBuffer(mesh_src);
654 
655  return hr;
656 }
657 
658 static BOOL declaration_equals(const D3DVERTEXELEMENT9 *declaration1, const D3DVERTEXELEMENT9 *declaration2)
659 {
660  UINT size1 = 0, size2 = 0;
661 
662  /* Find the size of each declaration */
663  while (declaration1[size1].Stream != 0xff) size1++;
664  while (declaration2[size2].Stream != 0xff) size2++;
665 
666  /* If not same size then they are definitely not equal */
667  if (size1 != size2)
668  return FALSE;
669 
670  /* Check that all components are the same */
671  if (memcmp(declaration1, declaration2, size1*sizeof(*declaration1)) == 0)
672  return TRUE;
673 
674  return FALSE;
675 }
676 
677 static HRESULT WINAPI d3dx9_mesh_CloneMesh(struct ID3DXMesh *iface, DWORD options,
678  const D3DVERTEXELEMENT9 *declaration, struct IDirect3DDevice9 *device, struct ID3DXMesh **clone_mesh_out)
679 {
680  struct d3dx9_mesh *This = impl_from_ID3DXMesh(iface);
681  struct d3dx9_mesh *cloned_this;
682  ID3DXMesh *clone_mesh;
683  D3DVERTEXELEMENT9 orig_declaration[MAX_FVF_DECL_SIZE] = { D3DDECL_END() };
684  void *data_in, *data_out;
685  DWORD vertex_size;
686  HRESULT hr;
687  BOOL same_declaration;
688 
689  TRACE("iface %p, options %#x, declaration %p, device %p, clone_mesh_out %p.\n",
690  iface, options, declaration, device, clone_mesh_out);
691 
692  if (!clone_mesh_out)
693  return D3DERR_INVALIDCALL;
694 
695  hr = iface->lpVtbl->GetDeclaration(iface, orig_declaration);
696  if (FAILED(hr)) return hr;
697 
698  hr = D3DXCreateMesh(This->numfaces, This->numvertices, options & ~D3DXMESH_VB_SHARE,
699  declaration, device, &clone_mesh);
700  if (FAILED(hr)) return hr;
701 
702  cloned_this = impl_from_ID3DXMesh(clone_mesh);
703  vertex_size = clone_mesh->lpVtbl->GetNumBytesPerVertex(clone_mesh);
704  same_declaration = declaration_equals(declaration, orig_declaration);
705 
706  if (options & D3DXMESH_VB_SHARE) {
707  if (!same_declaration) {
709  goto error;
710  }
711  IDirect3DVertexBuffer9_AddRef(This->vertex_buffer);
712  /* FIXME: refactor to avoid creating a new vertex buffer */
714  cloned_this->vertex_buffer = This->vertex_buffer;
715  } else if (same_declaration) {
716  hr = iface->lpVtbl->LockVertexBuffer(iface, D3DLOCK_READONLY, &data_in);
717  if (FAILED(hr)) goto error;
718  hr = clone_mesh->lpVtbl->LockVertexBuffer(clone_mesh, 0, &data_out);
719  if (FAILED(hr)) {
720  iface->lpVtbl->UnlockVertexBuffer(iface);
721  goto error;
722  }
723  memcpy(data_out, data_in, This->numvertices * vertex_size);
724  clone_mesh->lpVtbl->UnlockVertexBuffer(clone_mesh);
725  iface->lpVtbl->UnlockVertexBuffer(iface);
726  } else {
727  hr = convert_vertex_buffer(clone_mesh, iface);
728  if (FAILED(hr)) goto error;
729  }
730 
731  hr = iface->lpVtbl->LockIndexBuffer(iface, D3DLOCK_READONLY, &data_in);
732  if (FAILED(hr)) goto error;
733  hr = clone_mesh->lpVtbl->LockIndexBuffer(clone_mesh, 0, &data_out);
734  if (FAILED(hr)) {
735  iface->lpVtbl->UnlockIndexBuffer(iface);
736  goto error;
737  }
738  if ((options ^ This->options) & D3DXMESH_32BIT) {
739  DWORD i;
740  if (options & D3DXMESH_32BIT) {
741  for (i = 0; i < This->numfaces * 3; i++)
742  ((DWORD*)data_out)[i] = ((WORD*)data_in)[i];
743  } else {
744  for (i = 0; i < This->numfaces * 3; i++)
745  ((WORD*)data_out)[i] = ((DWORD*)data_in)[i];
746  }
747  } else {
748  memcpy(data_out, data_in, This->numfaces * 3 * (options & D3DXMESH_32BIT ? 4 : 2));
749  }
750  clone_mesh->lpVtbl->UnlockIndexBuffer(clone_mesh);
751  iface->lpVtbl->UnlockIndexBuffer(iface);
752 
753  memcpy(cloned_this->attrib_buffer, This->attrib_buffer, This->numfaces * sizeof(*This->attrib_buffer));
754 
755  if (This->attrib_table_size)
756  {
757  cloned_this->attrib_table_size = This->attrib_table_size;
758  cloned_this->attrib_table = HeapAlloc(GetProcessHeap(), 0, This->attrib_table_size * sizeof(*This->attrib_table));
759  if (!cloned_this->attrib_table) {
760  hr = E_OUTOFMEMORY;
761  goto error;
762  }
763  memcpy(cloned_this->attrib_table, This->attrib_table, This->attrib_table_size * sizeof(*This->attrib_table));
764  }
765 
766  *clone_mesh_out = clone_mesh;
767 
768  return D3D_OK;
769 error:
770  IUnknown_Release(clone_mesh);
771  return hr;
772 }
773 
774 static HRESULT WINAPI d3dx9_mesh_GetVertexBuffer(struct ID3DXMesh *iface,
775  struct IDirect3DVertexBuffer9 **vertex_buffer)
776 {
777  struct d3dx9_mesh *mesh = impl_from_ID3DXMesh(iface);
778 
779  TRACE("iface %p, vertex_buffer %p.\n", iface, vertex_buffer);
780 
781  if (!vertex_buffer)
782  return D3DERR_INVALIDCALL;
783  *vertex_buffer = mesh->vertex_buffer;
785 
786  return D3D_OK;
787 }
788 
789 static HRESULT WINAPI d3dx9_mesh_GetIndexBuffer(struct ID3DXMesh *iface,
790  struct IDirect3DIndexBuffer9 **index_buffer)
791 {
792  struct d3dx9_mesh *mesh = impl_from_ID3DXMesh(iface);
793 
794  TRACE("iface %p, index_buffer %p.\n", iface, index_buffer);
795 
796  if (!index_buffer)
797  return D3DERR_INVALIDCALL;
798  *index_buffer = mesh->index_buffer;
800 
801  return D3D_OK;
802 }
803 
804 static HRESULT WINAPI d3dx9_mesh_LockVertexBuffer(ID3DXMesh *iface, DWORD flags, void **data)
805 {
806  struct d3dx9_mesh *mesh = impl_from_ID3DXMesh(iface);
807 
808  TRACE("iface %p, flags %#x, data %p.\n", iface, flags, data);
809 
810  return IDirect3DVertexBuffer9_Lock(mesh->vertex_buffer, 0, 0, data, flags);
811 }
812 
814 {
815  struct d3dx9_mesh *mesh = impl_from_ID3DXMesh(iface);
816 
817  TRACE("iface %p.\n", iface);
818 
820 }
821 
822 static HRESULT WINAPI d3dx9_mesh_LockIndexBuffer(ID3DXMesh *iface, DWORD flags, void **data)
823 {
824  struct d3dx9_mesh *mesh = impl_from_ID3DXMesh(iface);
825 
826  TRACE("iface %p, flags %#x, data %p.\n", iface, flags, data);
827 
828  return IDirect3DIndexBuffer9_Lock(mesh->index_buffer, 0, 0, data, flags);
829 }
830 
832 {
833  struct d3dx9_mesh *mesh = impl_from_ID3DXMesh(iface);
834 
835  TRACE("iface %p.\n", iface);
836 
838 }
839 
840 /* FIXME: This looks just wrong, we never check *attrib_table_size before
841  * copying the data. */
844 {
845  struct d3dx9_mesh *mesh = impl_from_ID3DXMesh(iface);
846 
847  TRACE("iface %p, attrib_table %p, attrib_table_size %p.\n",
849 
850  if (attrib_table_size)
852 
853  if (attrib_table)
855 
856  return D3D_OK;
857 }
858 
859 struct edge_face
860 {
861  struct list entry;
864 };
865 
867 {
868  struct list *lists;
870 };
871 
872 /* Builds up a map of which face a new edge belongs to. That way the adjacency
873  * of another edge can be looked up. An edge has an adjacent face if there
874  * is an edge going in the opposite direction in the map. For example if the
875  * edge (v1, v2) belongs to face 4, and there is a mapping (v2, v1)->7, then
876  * face 4 and 7 are adjacent.
877  *
878  * Each edge might have been replaced with another edge, or none at all. There
879  * is at most one edge to face mapping, i.e. an edge can only belong to one
880  * face.
881  */
882 static HRESULT init_edge_face_map(struct edge_face_map *edge_face_map, const DWORD *index_buffer,
883  const DWORD *point_reps, DWORD num_faces)
884 {
885  DWORD face, edge;
886  DWORD i;
887 
888  edge_face_map->lists = HeapAlloc(GetProcessHeap(), 0, 3 * num_faces * sizeof(*edge_face_map->lists));
889  if (!edge_face_map->lists) return E_OUTOFMEMORY;
890 
891  edge_face_map->entries = HeapAlloc(GetProcessHeap(), 0, 3 * num_faces * sizeof(*edge_face_map->entries));
892  if (!edge_face_map->entries) return E_OUTOFMEMORY;
893 
894 
895  /* Initialize all lists */
896  for (i = 0; i < 3 * num_faces; i++)
897  {
899  }
900  /* Build edge face mapping */
901  for (face = 0; face < num_faces; face++)
902  {
903  for (edge = 0; edge < 3; edge++)
904  {
905  DWORD v1 = index_buffer[3*face + edge];
906  DWORD v2 = index_buffer[3*face + (edge+1)%3];
907  DWORD new_v1 = point_reps[v1]; /* What v1 has been replaced with */
908  DWORD new_v2 = point_reps[v2];
909 
910  if (v1 != v2) /* Only map non-collapsed edges */
911  {
912  i = 3*face + edge;
913  edge_face_map->entries[i].v2 = new_v2;
914  edge_face_map->entries[i].face = face;
916  }
917  }
918  }
919 
920  return D3D_OK;
921 }
922 
923 static DWORD find_adjacent_face(struct edge_face_map *edge_face_map, DWORD vertex1, DWORD vertex2, DWORD num_faces)
924 {
925  struct edge_face *edge_face_ptr;
926 
927  LIST_FOR_EACH_ENTRY(edge_face_ptr, &edge_face_map->lists[vertex2], struct edge_face, entry)
928  {
929  if (edge_face_ptr->v2 == vertex1)
930  return edge_face_ptr->face;
931  }
932 
933  return -1;
934 }
935 
937 {
938  DWORD *id_point_reps;
939  DWORD i;
940 
941  id_point_reps = HeapAlloc(GetProcessHeap(), 0, num_vertices * sizeof(*id_point_reps));
942  if (!id_point_reps)
943  return NULL;
944 
945  for (i = 0; i < num_vertices; i++)
946  {
947  id_point_reps[i] = i;
948  }
949 
950  return id_point_reps;
951 }
952 
954  const DWORD *point_reps, DWORD *adjacency)
955 {
956  HRESULT hr;
957  DWORD num_faces = iface->lpVtbl->GetNumFaces(iface);
958  DWORD num_vertices = iface->lpVtbl->GetNumVertices(iface);
959  DWORD options = iface->lpVtbl->GetOptions(iface);
960  BOOL indices_are_16_bit = !(options & D3DXMESH_32BIT);
961  DWORD *ib = NULL;
962  void *ib_ptr = NULL;
963  DWORD face;
964  DWORD edge;
965  struct edge_face_map edge_face_map = {0};
966  const DWORD *point_reps_ptr = NULL;
967  DWORD *id_point_reps = NULL;
968 
969  TRACE("iface %p, point_reps %p, adjacency %p.\n", iface, point_reps, adjacency);
970 
971  if (!adjacency) return D3DERR_INVALIDCALL;
972 
973  if (!point_reps) /* Identity point reps */
974  {
975  id_point_reps = generate_identity_point_reps(num_vertices);
976  if (!id_point_reps)
977  {
978  hr = E_OUTOFMEMORY;
979  goto cleanup;
980  }
981 
982  point_reps_ptr = id_point_reps;
983  }
984  else
985  {
986  point_reps_ptr = point_reps;
987  }
988 
989  hr = iface->lpVtbl->LockIndexBuffer(iface, D3DLOCK_READONLY, &ib_ptr);
990  if (FAILED(hr)) goto cleanup;
991 
992  if (indices_are_16_bit)
993  {
994  /* Widen 16 bit to 32 bit */
995  DWORD i;
996  WORD *ib_16bit = ib_ptr;
997  ib = HeapAlloc(GetProcessHeap(), 0, 3 * num_faces * sizeof(DWORD));
998  if (!ib)
999  {
1000  hr = E_OUTOFMEMORY;
1001  goto cleanup;
1002  }
1003  for (i = 0; i < 3 * num_faces; i++)
1004  {
1005  ib[i] = ib_16bit[i];
1006  }
1007  }
1008  else
1009  {
1010  ib = ib_ptr;
1011  }
1012 
1013  hr = init_edge_face_map(&edge_face_map, ib, point_reps_ptr, num_faces);
1014  if (FAILED(hr)) goto cleanup;
1015 
1016  /* Create adjacency */
1017  for (face = 0; face < num_faces; face++)
1018  {
1019  for (edge = 0; edge < 3; edge++)
1020  {
1021  DWORD v1 = ib[3*face + edge];
1022  DWORD v2 = ib[3*face + (edge+1)%3];
1023  DWORD new_v1 = point_reps_ptr[v1];
1024  DWORD new_v2 = point_reps_ptr[v2];
1025  DWORD adj_face;
1026 
1027  adj_face = find_adjacent_face(&edge_face_map, new_v1, new_v2, num_faces);
1028  adjacency[3*face + edge] = adj_face;
1029  }
1030  }
1031 
1032  hr = D3D_OK;
1033 cleanup:
1034  HeapFree(GetProcessHeap(), 0, id_point_reps);
1035  if (indices_are_16_bit) HeapFree(GetProcessHeap(), 0, ib);
1038  if(ib_ptr) iface->lpVtbl->UnlockIndexBuffer(iface);
1039  return hr;
1040 }
1041 
1042 /* ConvertAdjacencyToPointReps helper function.
1043  *
1044  * Goes around the edges of each face and replaces the vertices in any adjacent
1045  * face's edge with its own vertices(if its vertices have a lower index). This
1046  * way as few as possible low index vertices are shared among the faces. The
1047  * re-ordered index buffer is stored in new_indices.
1048  *
1049  * The vertices in a point representation must be ordered sequentially, e.g.
1050  * index 5 holds the index of the vertex that replaces vertex 5, i.e. if
1051  * vertex 5 is replaced by vertex 3 then index 5 would contain 3. If no vertex
1052  * replaces it, then it contains the same number as the index itself, e.g.
1053  * index 5 would contain 5. */
1054 static HRESULT propagate_face_vertices(const DWORD *adjacency, DWORD *point_reps,
1055  const DWORD *indices, DWORD *new_indices, DWORD face, DWORD numfaces)
1056 {
1057  const unsigned int VERTS_PER_FACE = 3;
1058  DWORD edge, opp_edge;
1059  DWORD face_base = VERTS_PER_FACE * face;
1060 
1061  for (edge = 0; edge < VERTS_PER_FACE; edge++)
1062  {
1063  DWORD adj_face = adjacency[face_base + edge];
1064  DWORD adj_face_base;
1065  DWORD i;
1066  if (adj_face == -1) /* No adjacent face. */
1067  continue;
1068  else if (adj_face >= numfaces)
1069  {
1070  /* This throws exception on Windows */
1071  WARN("Index out of bounds. Got %d expected less than %d.\n",
1072  adj_face, numfaces);
1073  return D3DERR_INVALIDCALL;
1074  }
1075  adj_face_base = 3 * adj_face;
1076 
1077  /* Find opposite edge in adjacent face. */
1078  for (opp_edge = 0; opp_edge < VERTS_PER_FACE; opp_edge++)
1079  {
1080  DWORD opp_edge_index = adj_face_base + opp_edge;
1081  if (adjacency[opp_edge_index] == face)
1082  break; /* Found opposite edge. */
1083  }
1084 
1085  /* Replaces vertices in opposite edge with vertices from current edge. */
1086  for (i = 0; i < 2; i++)
1087  {
1088  DWORD from = face_base + (edge + (1 - i)) % VERTS_PER_FACE;
1089  DWORD to = adj_face_base + (opp_edge + i) % VERTS_PER_FACE;
1090 
1091  /* Propagate lowest index. */
1092  if (new_indices[to] > new_indices[from])
1093  {
1094  new_indices[to] = new_indices[from];
1095  point_reps[indices[to]] = new_indices[from];
1096  }
1097  }
1098  }
1099 
1100  return D3D_OK;
1101 }
1102 
1104  const DWORD *adjacency, DWORD *point_reps)
1105 {
1106  struct d3dx9_mesh *This = impl_from_ID3DXMesh(iface);
1107  HRESULT hr;
1108  DWORD face;
1109  DWORD i;
1110  DWORD *indices = NULL;
1111  WORD *indices_16bit = NULL;
1112  DWORD *new_indices = NULL;
1113  const unsigned int VERTS_PER_FACE = 3;
1114 
1115  TRACE("iface %p, adjacency %p, point_reps %p.\n", iface, adjacency, point_reps);
1116 
1117  if (!adjacency)
1118  {
1119  WARN("NULL adjacency.\n");
1121  goto cleanup;
1122  }
1123 
1124  if (!point_reps)
1125  {
1126  WARN("NULL point_reps.\n");
1128  goto cleanup;
1129  }
1130 
1131  /* Should never happen as CreateMesh does not allow meshes with 0 faces */
1132  if (This->numfaces == 0)
1133  {
1134  ERR("Number of faces was zero.\n");
1136  goto cleanup;
1137  }
1138 
1139  new_indices = HeapAlloc(GetProcessHeap(), 0, VERTS_PER_FACE * This->numfaces * sizeof(*indices));
1140  if (!new_indices)
1141  {
1142  hr = E_OUTOFMEMORY;
1143  goto cleanup;
1144  }
1145 
1146  if (This->options & D3DXMESH_32BIT)
1147  {
1148  hr = iface->lpVtbl->LockIndexBuffer(iface, D3DLOCK_READONLY, (void**)&indices);
1149  if (FAILED(hr)) goto cleanup;
1150  memcpy(new_indices, indices, VERTS_PER_FACE * This->numfaces * sizeof(*indices));
1151  }
1152  else
1153  {
1154  /* Make a widening copy of indices_16bit into indices and new_indices
1155  * in order to re-use the helper function */
1156  hr = iface->lpVtbl->LockIndexBuffer(iface, D3DLOCK_READONLY, (void**)&indices_16bit);
1157  if (FAILED(hr)) goto cleanup;
1158  indices = HeapAlloc(GetProcessHeap(), 0, VERTS_PER_FACE * This->numfaces * sizeof(*indices));
1159  if (!indices)
1160  {
1161  hr = E_OUTOFMEMORY;
1162  goto cleanup;
1163  }
1164  for (i = 0; i < VERTS_PER_FACE * This->numfaces; i++)
1165  {
1166  new_indices[i] = indices_16bit[i];
1167  indices[i] = indices_16bit[i];
1168  }
1169  }
1170 
1171  /* Vertices are ordered sequentially in the point representation. */
1172  for (i = 0; i < This->numvertices; i++)
1173  {
1174  point_reps[i] = i;
1175  }
1176 
1177  /* Propagate vertices with low indices so as few vertices as possible
1178  * are used in the mesh.
1179  */
1180  for (face = 0; face < This->numfaces; face++)
1181  {
1182  hr = propagate_face_vertices(adjacency, point_reps, indices, new_indices, face, This->numfaces);
1183  if (FAILED(hr)) goto cleanup;
1184  }
1185  /* Go in opposite direction to catch all face orderings */
1186  for (face = 0; face < This->numfaces; face++)
1187  {
1188  hr = propagate_face_vertices(adjacency, point_reps,
1189  indices, new_indices,
1190  (This->numfaces - 1) - face, This->numfaces);
1191  if (FAILED(hr)) goto cleanup;
1192  }
1193 
1194  hr = D3D_OK;
1195 cleanup:
1196  if (This->options & D3DXMESH_32BIT)
1197  {
1198  if (indices) iface->lpVtbl->UnlockIndexBuffer(iface);
1199  }
1200  else
1201  {
1202  if (indices_16bit) iface->lpVtbl->UnlockIndexBuffer(iface);
1204  }
1205  HeapFree(GetProcessHeap(), 0, new_indices);
1206  return hr;
1207 }
1208 
1210  float key;
1213 };
1214 
1215 static int compare_vertex_keys(const void *a, const void *b)
1216 {
1217  const struct vertex_metadata *left = a;
1218  const struct vertex_metadata *right = b;
1219  if (left->key == right->key)
1220  return 0;
1221  return left->key < right->key ? -1 : 1;
1222 }
1223 
1224 static HRESULT WINAPI d3dx9_mesh_GenerateAdjacency(ID3DXMesh *iface, float epsilon, DWORD *adjacency)
1225 {
1226  struct d3dx9_mesh *This = impl_from_ID3DXMesh(iface);
1227  HRESULT hr;
1228  BYTE *vertices = NULL;
1229  const DWORD *indices = NULL;
1230  DWORD vertex_size;
1232  /* sort the vertices by (x + y + z) to quickly find coincident vertices */
1233  struct vertex_metadata *sorted_vertices;
1234  /* shared_indices links together identical indices in the index buffer so
1235  * that adjacency checks can be limited to faces sharing a vertex */
1236  DWORD *shared_indices = NULL;
1237  const FLOAT epsilon_sq = epsilon * epsilon;
1238  DWORD i;
1239 
1240  TRACE("iface %p, epsilon %.8e, adjacency %p.\n", iface, epsilon, adjacency);
1241 
1242  if (!adjacency)
1243  return D3DERR_INVALIDCALL;
1244 
1245  buffer_size = This->numfaces * 3 * sizeof(*shared_indices) + This->numvertices * sizeof(*sorted_vertices);
1246  if (!(This->options & D3DXMESH_32BIT))
1247  buffer_size += This->numfaces * 3 * sizeof(*indices);
1248  shared_indices = HeapAlloc(GetProcessHeap(), 0, buffer_size);
1249  if (!shared_indices)
1250  return E_OUTOFMEMORY;
1251  sorted_vertices = (struct vertex_metadata*)(shared_indices + This->numfaces * 3);
1252 
1253  hr = iface->lpVtbl->LockVertexBuffer(iface, D3DLOCK_READONLY, (void**)&vertices);
1254  if (FAILED(hr)) goto cleanup;
1255  hr = iface->lpVtbl->LockIndexBuffer(iface, D3DLOCK_READONLY, (void**)&indices);
1256  if (FAILED(hr)) goto cleanup;
1257 
1258  if (!(This->options & D3DXMESH_32BIT)) {
1259  const WORD *word_indices = (const WORD*)indices;
1260  DWORD *dword_indices = (DWORD*)(sorted_vertices + This->numvertices);
1261  indices = dword_indices;
1262  for (i = 0; i < This->numfaces * 3; i++)
1263  *dword_indices++ = *word_indices++;
1264  }
1265 
1266  vertex_size = iface->lpVtbl->GetNumBytesPerVertex(iface);
1267  for (i = 0; i < This->numvertices; i++) {
1268  D3DXVECTOR3 *vertex = (D3DXVECTOR3*)(vertices + vertex_size * i);
1269  sorted_vertices[i].first_shared_index = -1;
1270  sorted_vertices[i].key = vertex->x + vertex->y + vertex->z;
1271  sorted_vertices[i].vertex_index = i;
1272  }
1273  for (i = 0; i < This->numfaces * 3; i++) {
1274  DWORD *first_shared_index = &sorted_vertices[indices[i]].first_shared_index;
1275  shared_indices[i] = *first_shared_index;
1276  *first_shared_index = i;
1277  adjacency[i] = -1;
1278  }
1279  qsort(sorted_vertices, This->numvertices, sizeof(*sorted_vertices), compare_vertex_keys);
1280 
1281  for (i = 0; i < This->numvertices; i++) {
1282  struct vertex_metadata *sorted_vertex_a = &sorted_vertices[i];
1283  D3DXVECTOR3 *vertex_a = (D3DXVECTOR3*)(vertices + sorted_vertex_a->vertex_index * vertex_size);
1284  DWORD shared_index_a = sorted_vertex_a->first_shared_index;
1285 
1286  while (shared_index_a != -1) {
1287  DWORD j = i;
1288  DWORD shared_index_b = shared_indices[shared_index_a];
1289  struct vertex_metadata *sorted_vertex_b = sorted_vertex_a;
1290 
1291  while (TRUE) {
1292  while (shared_index_b != -1) {
1293  /* faces are adjacent if they have another coincident vertex */
1294  DWORD base_a = (shared_index_a / 3) * 3;
1295  DWORD base_b = (shared_index_b / 3) * 3;
1296  BOOL adjacent = FALSE;
1297  int k;
1298 
1299  for (k = 0; k < 3; k++) {
1300  if (adjacency[base_b + k] == shared_index_a / 3) {
1301  adjacent = TRUE;
1302  break;
1303  }
1304  }
1305  if (!adjacent) {
1306  for (k = 1; k <= 2; k++) {
1307  DWORD vertex_index_a = base_a + (shared_index_a + k) % 3;
1308  DWORD vertex_index_b = base_b + (shared_index_b + (3 - k)) % 3;
1309  adjacent = indices[vertex_index_a] == indices[vertex_index_b];
1310  if (!adjacent && epsilon >= 0.0f) {
1311  D3DXVECTOR3 delta = {0.0f, 0.0f, 0.0f};
1312  FLOAT length_sq;
1313 
1314  D3DXVec3Subtract(&delta,
1315  (D3DXVECTOR3*)(vertices + indices[vertex_index_a] * vertex_size),
1316  (D3DXVECTOR3*)(vertices + indices[vertex_index_b] * vertex_size));
1317  length_sq = D3DXVec3LengthSq(&delta);
1318  adjacent = epsilon == 0.0f ? length_sq == 0.0f : length_sq < epsilon_sq;
1319  }
1320  if (adjacent) {
1321  DWORD adj_a = base_a + 2 - (vertex_index_a + shared_index_a + 1) % 3;
1322  DWORD adj_b = base_b + 2 - (vertex_index_b + shared_index_b + 1) % 3;
1323  if (adjacency[adj_a] == -1 && adjacency[adj_b] == -1) {
1324  adjacency[adj_a] = base_b / 3;
1325  adjacency[adj_b] = base_a / 3;
1326  break;
1327  }
1328  }
1329  }
1330  }
1331 
1332  shared_index_b = shared_indices[shared_index_b];
1333  }
1334  while (++j < This->numvertices) {
1335  D3DXVECTOR3 *vertex_b;
1336 
1337  sorted_vertex_b++;
1338  if (sorted_vertex_b->key - sorted_vertex_a->key > epsilon * 3.0f) {
1339  /* no more coincident vertices to try */
1340  j = This->numvertices;
1341  break;
1342  }
1343  /* check for coincidence */
1344  vertex_b = (D3DXVECTOR3*)(vertices + sorted_vertex_b->vertex_index * vertex_size);
1345  if (fabsf(vertex_a->x - vertex_b->x) <= epsilon &&
1346  fabsf(vertex_a->y - vertex_b->y) <= epsilon &&
1347  fabsf(vertex_a->z - vertex_b->z) <= epsilon)
1348  {
1349  break;
1350  }
1351  }
1352  if (j >= This->numvertices)
1353  break;
1354  shared_index_b = sorted_vertex_b->first_shared_index;
1355  }
1356 
1357  sorted_vertex_a->first_shared_index = shared_indices[sorted_vertex_a->first_shared_index];
1358  shared_index_a = sorted_vertex_a->first_shared_index;
1359  }
1360  }
1361 
1362  hr = D3D_OK;
1363 cleanup:
1364  if (indices) iface->lpVtbl->UnlockIndexBuffer(iface);
1365  if (vertices) iface->lpVtbl->UnlockVertexBuffer(iface);
1366  HeapFree(GetProcessHeap(), 0, shared_indices);
1367  return hr;
1368 }
1369 
1371 {
1372  struct d3dx9_mesh *This = impl_from_ID3DXMesh(iface);
1373  HRESULT hr;
1375  int i;
1376 
1377  TRACE("iface %p, declaration %p.\n", iface, declaration);
1378 
1379  if (!declaration)
1380  {
1381  WARN("Invalid declaration. Can't use NULL declaration.\n");
1382  return D3DERR_INVALIDCALL;
1383  }
1384 
1385  /* New declaration must be same size as original */
1387  if (vertex_declaration_size != This->vertex_declaration_size)
1388  {
1389  WARN("Invalid declaration. New vertex size does not match the original vertex size.\n");
1390  return D3DERR_INVALIDCALL;
1391  }
1392 
1393  /* New declaration must not contain non-zero Stream value */
1394  for (i = 0; declaration[i].Stream != 0xff; i++)
1395  {
1396  if (declaration[i].Stream != 0)
1397  {
1398  WARN("Invalid declaration. New declaration contains non-zero Stream value.\n");
1399  return D3DERR_INVALIDCALL;
1400  }
1401  }
1402 
1403  This->num_elem = i + 1;
1404  copy_declaration(This->cached_declaration, declaration, This->num_elem);
1405 
1406  if (This->vertex_declaration)
1407  IDirect3DVertexDeclaration9_Release(This->vertex_declaration);
1408 
1409  /* An application can pass an invalid declaration to UpdateSemantics and
1410  * still expect D3D_OK (see tests). If the declaration is invalid, then
1411  * subsequent calls to DrawSubset will fail. This is handled by setting the
1412  * vertex declaration to NULL.
1413  * GetDeclaration, GetNumBytesPerVertex must, however, use the new
1414  * invalid declaration. This is handled by them using the cached vertex
1415  * declaration instead of the actual vertex declaration.
1416  */
1418  declaration,
1419  &This->vertex_declaration);
1420  if (FAILED(hr))
1421  {
1422  WARN("Using invalid declaration. Calls to DrawSubset will fail.\n");
1423  This->vertex_declaration = NULL;
1424  }
1425 
1426  return D3D_OK;
1427 }
1428 
1430 {
1431  struct d3dx9_mesh *mesh = impl_from_ID3DXMesh(iface);
1432 
1433  TRACE("iface %p, flags %#x, data %p.\n", iface, flags, data);
1434 
1436 
1437  if (!(flags & D3DLOCK_READONLY))
1438  {
1440  mesh->attrib_table_size = 0;
1441  mesh->attrib_table = NULL;
1443  }
1444 
1445  *data = mesh->attrib_buffer;
1446 
1447  return D3D_OK;
1448 }
1449 
1451 {
1452  struct d3dx9_mesh *mesh = impl_from_ID3DXMesh(iface);
1453  int lock_count;
1454 
1455  TRACE("iface %p.\n", iface);
1456 
1457  lock_count = InterlockedDecrement(&mesh->attrib_buffer_lock_count);
1458  if (lock_count < 0)
1459  {
1461  return D3DERR_INVALIDCALL;
1462  }
1463 
1464  return D3D_OK;
1465 }
1466 
1467 static HRESULT WINAPI d3dx9_mesh_Optimize(ID3DXMesh *iface, DWORD flags, const DWORD *adjacency_in,
1468  DWORD *adjacency_out, DWORD *face_remap, ID3DXBuffer **vertex_remap, ID3DXMesh **opt_mesh)
1469 {
1470  struct d3dx9_mesh *mesh = impl_from_ID3DXMesh(iface);
1471  HRESULT hr;
1473  ID3DXMesh *optimized_mesh;
1474 
1475  TRACE("iface %p, flags %#x, adjacency_in %p, adjacency_out %p, face_remap %p, vertex_remap %p, opt_mesh %p.\n",
1476  iface, flags, adjacency_in, adjacency_out, face_remap, vertex_remap, opt_mesh);
1477 
1478  if (!opt_mesh)
1479  return D3DERR_INVALIDCALL;
1480 
1481  hr = iface->lpVtbl->GetDeclaration(iface, declaration);
1482  if (FAILED(hr)) return hr;
1483 
1484  if (FAILED(hr = iface->lpVtbl->CloneMesh(iface, mesh->options, declaration, mesh->device, &optimized_mesh)))
1485  return hr;
1486 
1487  hr = optimized_mesh->lpVtbl->OptimizeInplace(optimized_mesh, flags, adjacency_in, adjacency_out, face_remap, vertex_remap);
1488  if (SUCCEEDED(hr))
1489  *opt_mesh = optimized_mesh;
1490  else
1491  IUnknown_Release(optimized_mesh);
1492  return hr;
1493 }
1494 
1495 /* Creates a vertex_remap that removes unused vertices.
1496  * Indices are updated according to the vertex_remap. */
1498  DWORD *new_num_vertices, ID3DXBuffer **vertex_remap)
1499 {
1500  HRESULT hr;
1501  DWORD *vertex_remap_ptr;
1502  DWORD num_used_vertices;
1503  DWORD i;
1504 
1505  hr = D3DXCreateBuffer(This->numvertices * sizeof(DWORD), vertex_remap);
1506  if (FAILED(hr)) return hr;
1507  vertex_remap_ptr = ID3DXBuffer_GetBufferPointer(*vertex_remap);
1508 
1509  for (i = 0; i < This->numfaces * 3; i++)
1510  vertex_remap_ptr[indices[i]] = 1;
1511 
1512  /* create old->new vertex mapping */
1513  num_used_vertices = 0;
1514  for (i = 0; i < This->numvertices; i++) {
1515  if (vertex_remap_ptr[i])
1516  vertex_remap_ptr[i] = num_used_vertices++;
1517  else
1518  vertex_remap_ptr[i] = -1;
1519  }
1520  /* convert indices */
1521  for (i = 0; i < This->numfaces * 3; i++)
1522  indices[i] = vertex_remap_ptr[indices[i]];
1523 
1524  /* create new->old vertex mapping */
1525  num_used_vertices = 0;
1526  for (i = 0; i < This->numvertices; i++) {
1527  if (vertex_remap_ptr[i] != -1)
1528  vertex_remap_ptr[num_used_vertices++] = i;
1529  }
1530  for (i = num_used_vertices; i < This->numvertices; i++)
1531  vertex_remap_ptr[i] = -1;
1532 
1533  *new_num_vertices = num_used_vertices;
1534 
1535  return D3D_OK;
1536 }
1537 
1538 /* count the number of unique attribute values in a sorted attribute buffer */
1540 {
1541  DWORD last_attribute = attrib_buffer[0];
1543  DWORD i;
1544  for (i = 1; i < numfaces; i++) {
1545  if (attrib_buffer[i] != last_attribute) {
1546  last_attribute = attrib_buffer[i];
1548  }
1549  }
1550  return attrib_table_size;
1551 }
1552 
1554  BOOL is_32bit_indices, D3DXATTRIBUTERANGE *attrib_table)
1555 {
1557  DWORD last_attribute = attrib_buffer[0];
1558  DWORD min_vertex, max_vertex;
1559  DWORD i;
1560 
1561  attrib_table[0].AttribId = last_attribute;
1562  attrib_table[0].FaceStart = 0;
1563  min_vertex = (DWORD)-1;
1564  max_vertex = 0;
1565  for (i = 0; i < numfaces; i++) {
1566  DWORD j;
1567 
1568  if (attrib_buffer[i] != last_attribute) {
1569  last_attribute = attrib_buffer[i];
1572  attrib_table[attrib_table_size].VertexCount = max_vertex - min_vertex + 1;
1576  min_vertex = (DWORD)-1;
1577  max_vertex = 0;
1578  }
1579  for (j = 0; j < 3; j++) {
1580  DWORD vertex_index = is_32bit_indices ? ((DWORD*)indices)[i * 3 + j] : ((WORD*)indices)[i * 3 + j];
1581  if (vertex_index < min_vertex)
1582  min_vertex = vertex_index;
1583  if (vertex_index > max_vertex)
1584  max_vertex = vertex_index;
1585  }
1586  }
1589  attrib_table[attrib_table_size].VertexCount = max_vertex - min_vertex + 1;
1591 }
1592 
1593 static int attrib_entry_compare(const DWORD **a, const DWORD **b)
1594 {
1595  const DWORD *ptr_a = *a;
1596  const DWORD *ptr_b = *b;
1597  int delta = *ptr_a - *ptr_b;
1598 
1599  if (delta)
1600  return delta;
1601 
1602  delta = ptr_a - ptr_b; /* for stable sort */
1603  return delta;
1604 }
1605 
1606 /* Create face_remap, a new attribute buffer for attribute sort optimization. */
1608  DWORD *attrib_buffer, DWORD **sorted_attrib_buffer, DWORD **face_remap)
1609 {
1610  DWORD **sorted_attrib_ptr_buffer = NULL;
1611  DWORD i;
1612 
1613  sorted_attrib_ptr_buffer = HeapAlloc(GetProcessHeap(), 0, This->numfaces * sizeof(*sorted_attrib_ptr_buffer));
1614  if (!sorted_attrib_ptr_buffer)
1615  return E_OUTOFMEMORY;
1616 
1617  *face_remap = HeapAlloc(GetProcessHeap(), 0, This->numfaces * sizeof(**face_remap));
1618  if (!*face_remap)
1619  {
1620  HeapFree(GetProcessHeap(), 0, sorted_attrib_ptr_buffer);
1621  return E_OUTOFMEMORY;
1622  }
1623 
1624  for (i = 0; i < This->numfaces; i++)
1625  sorted_attrib_ptr_buffer[i] = &attrib_buffer[i];
1626  qsort(sorted_attrib_ptr_buffer, This->numfaces, sizeof(*sorted_attrib_ptr_buffer),
1627  (int(*)(const void *, const void *))attrib_entry_compare);
1628 
1629  for (i = 0; i < This->numfaces; i++)
1630  {
1631  DWORD old_face = sorted_attrib_ptr_buffer[i] - attrib_buffer;
1632  (*face_remap)[old_face] = i;
1633  }
1634 
1635  /* overwrite sorted_attrib_ptr_buffer with the values themselves */
1636  *sorted_attrib_buffer = (DWORD*)sorted_attrib_ptr_buffer;
1637  for (i = 0; i < This->numfaces; i++)
1638  (*sorted_attrib_buffer)[(*face_remap)[i]] = attrib_buffer[i];
1639 
1640  return D3D_OK;
1641 }
1642 
1643 static HRESULT WINAPI d3dx9_mesh_OptimizeInplace(ID3DXMesh *iface, DWORD flags, const DWORD *adjacency_in,
1644  DWORD *adjacency_out, DWORD *face_remap_out, ID3DXBuffer **vertex_remap_out)
1645 {
1646  struct d3dx9_mesh *This = impl_from_ID3DXMesh(iface);
1647  void *indices = NULL;
1649  HRESULT hr;
1650  ID3DXBuffer *vertex_remap = NULL;
1651  DWORD *face_remap = NULL; /* old -> new mapping */
1652  DWORD *dword_indices = NULL;
1653  DWORD new_num_vertices = 0;
1654  DWORD new_num_alloc_vertices = 0;
1655  IDirect3DVertexBuffer9 *vertex_buffer = NULL;
1656  DWORD *sorted_attrib_buffer = NULL;
1657  DWORD i;
1658 
1659  TRACE("iface %p, flags %#x, adjacency_in %p, adjacency_out %p, face_remap_out %p, vertex_remap_out %p.\n",
1660  iface, flags, adjacency_in, adjacency_out, face_remap_out, vertex_remap_out);
1661 
1662  if (!flags)
1663  return D3DERR_INVALIDCALL;
1664  if (!adjacency_in && (flags & (D3DXMESHOPT_VERTEXCACHE | D3DXMESHOPT_STRIPREORDER)))
1665  return D3DERR_INVALIDCALL;
1667  return D3DERR_INVALIDCALL;
1668 
1670  {
1672  FIXME("D3DXMESHOPT_VERTEXCACHE not implemented.\n");
1674  FIXME("D3DXMESHOPT_STRIPREORDER not implemented.\n");
1675  return E_NOTIMPL;
1676  }
1677 
1678  hr = iface->lpVtbl->LockIndexBuffer(iface, 0, &indices);
1679  if (FAILED(hr)) goto cleanup;
1680 
1681  dword_indices = HeapAlloc(GetProcessHeap(), 0, This->numfaces * 3 * sizeof(DWORD));
1682  if (!dword_indices) return E_OUTOFMEMORY;
1683  if (This->options & D3DXMESH_32BIT) {
1684  memcpy(dword_indices, indices, This->numfaces * 3 * sizeof(DWORD));
1685  } else {
1686  WORD *word_indices = indices;
1687  for (i = 0; i < This->numfaces * 3; i++)
1688  dword_indices[i] = *word_indices++;
1689  }
1690 
1692  {
1693  new_num_alloc_vertices = This->numvertices;
1694  hr = compact_mesh(This, dword_indices, &new_num_vertices, &vertex_remap);
1695  if (FAILED(hr)) goto cleanup;
1696  } else if (flags & D3DXMESHOPT_ATTRSORT) {
1697  if (!(flags & D3DXMESHOPT_IGNOREVERTS))
1698  FIXME("D3DXMESHOPT_ATTRSORT vertex reordering not implemented.\n");
1699 
1700  hr = iface->lpVtbl->LockAttributeBuffer(iface, 0, &attrib_buffer);
1701  if (FAILED(hr)) goto cleanup;
1702 
1703  hr = remap_faces_for_attrsort(This, dword_indices, attrib_buffer, &sorted_attrib_buffer, &face_remap);
1704  if (FAILED(hr)) goto cleanup;
1705  }
1706 
1707  if (vertex_remap)
1708  {
1709  /* reorder the vertices using vertex_remap */
1710  D3DVERTEXBUFFER_DESC vertex_desc;
1711  DWORD *vertex_remap_ptr = ID3DXBuffer_GetBufferPointer(vertex_remap);
1712  DWORD vertex_size = iface->lpVtbl->GetNumBytesPerVertex(iface);
1713  BYTE *orig_vertices;
1714  BYTE *new_vertices;
1715 
1716  hr = IDirect3DVertexBuffer9_GetDesc(This->vertex_buffer, &vertex_desc);
1717  if (FAILED(hr)) goto cleanup;
1718 
1719  hr = IDirect3DDevice9_CreateVertexBuffer(This->device, new_num_alloc_vertices * vertex_size,
1720  vertex_desc.Usage, This->fvf, vertex_desc.Pool, &vertex_buffer, NULL);
1721  if (FAILED(hr)) goto cleanup;
1722 
1723  hr = IDirect3DVertexBuffer9_Lock(This->vertex_buffer, 0, 0, (void**)&orig_vertices, D3DLOCK_READONLY);
1724  if (FAILED(hr)) goto cleanup;
1725 
1726  hr = IDirect3DVertexBuffer9_Lock(vertex_buffer, 0, 0, (void**)&new_vertices, 0);
1727  if (FAILED(hr)) {
1728  IDirect3DVertexBuffer9_Unlock(This->vertex_buffer);
1729  goto cleanup;
1730  }
1731 
1732  for (i = 0; i < new_num_vertices; i++)
1733  memcpy(new_vertices + i * vertex_size, orig_vertices + vertex_remap_ptr[i] * vertex_size, vertex_size);
1734 
1735  IDirect3DVertexBuffer9_Unlock(This->vertex_buffer);
1737  } else if (vertex_remap_out) {
1738  DWORD *vertex_remap_ptr;
1739 
1740  hr = D3DXCreateBuffer(This->numvertices * sizeof(DWORD), &vertex_remap);
1741  if (FAILED(hr)) goto cleanup;
1742  vertex_remap_ptr = ID3DXBuffer_GetBufferPointer(vertex_remap);
1743  for (i = 0; i < This->numvertices; i++)
1744  *vertex_remap_ptr++ = i;
1745  }
1746 
1748  {
1751 
1752  attrib_table_size = count_attributes(sorted_attrib_buffer, This->numfaces);
1754  if (!attrib_table) {
1755  hr = E_OUTOFMEMORY;
1756  goto cleanup;
1757  }
1758 
1759  memcpy(attrib_buffer, sorted_attrib_buffer, This->numfaces * sizeof(*attrib_buffer));
1760 
1761  /* reorder the indices using face_remap */
1762  if (This->options & D3DXMESH_32BIT) {
1763  for (i = 0; i < This->numfaces; i++)
1764  memcpy((DWORD*)indices + face_remap[i] * 3, dword_indices + i * 3, 3 * sizeof(DWORD));
1765  } else {
1766  WORD *word_indices = indices;
1767  for (i = 0; i < This->numfaces; i++) {
1768  DWORD new_pos = face_remap[i] * 3;
1769  DWORD old_pos = i * 3;
1770  word_indices[new_pos++] = dword_indices[old_pos++];
1771  word_indices[new_pos++] = dword_indices[old_pos++];
1772  word_indices[new_pos] = dword_indices[old_pos];
1773  }
1774  }
1775 
1777  This->options & D3DXMESH_32BIT, attrib_table);
1778 
1779  HeapFree(GetProcessHeap(), 0, This->attrib_table);
1780  This->attrib_table = attrib_table;
1781  This->attrib_table_size = attrib_table_size;
1782  } else {
1783  if (This->options & D3DXMESH_32BIT) {
1784  memcpy(indices, dword_indices, This->numfaces * 3 * sizeof(DWORD));
1785  } else {
1786  WORD *word_indices = indices;
1787  for (i = 0; i < This->numfaces * 3; i++)
1788  *word_indices++ = dword_indices[i];
1789  }
1790  }
1791 
1792  if (adjacency_out) {
1793  if (face_remap) {
1794  for (i = 0; i < This->numfaces; i++) {
1795  DWORD old_pos = i * 3;
1796  DWORD new_pos = face_remap[i] * 3;
1797  adjacency_out[new_pos++] = face_remap[adjacency_in[old_pos++]];
1798  adjacency_out[new_pos++] = face_remap[adjacency_in[old_pos++]];
1799  adjacency_out[new_pos++] = face_remap[adjacency_in[old_pos++]];
1800  }
1801  } else {
1802  memcpy(adjacency_out, adjacency_in, This->numfaces * 3 * sizeof(*adjacency_out));
1803  }
1804  }
1805  if (face_remap_out) {
1806  if (face_remap) {
1807  for (i = 0; i < This->numfaces; i++)
1808  face_remap_out[face_remap[i]] = i;
1809  } else {
1810  for (i = 0; i < This->numfaces; i++)
1811  face_remap_out[i] = i;
1812  }
1813  }
1814  if (vertex_remap_out)
1815  *vertex_remap_out = vertex_remap;
1816  vertex_remap = NULL;
1817 
1818  if (vertex_buffer) {
1819  IDirect3DVertexBuffer9_Release(This->vertex_buffer);
1820  This->vertex_buffer = vertex_buffer;
1821  vertex_buffer = NULL;
1822  This->numvertices = new_num_vertices;
1823  }
1824 
1825  hr = D3D_OK;
1826 cleanup:
1827  HeapFree(GetProcessHeap(), 0, sorted_attrib_buffer);
1828  HeapFree(GetProcessHeap(), 0, face_remap);
1829  HeapFree(GetProcessHeap(), 0, dword_indices);
1830  if (vertex_remap) ID3DXBuffer_Release(vertex_remap);
1832  if (attrib_buffer) iface->lpVtbl->UnlockAttributeBuffer(iface);
1833  if (indices) iface->lpVtbl->UnlockIndexBuffer(iface);
1834  return hr;
1835 }
1836 
1839 {
1840  struct d3dx9_mesh *mesh = impl_from_ID3DXMesh(iface);
1841  D3DXATTRIBUTERANGE *new_table = NULL;
1842 
1843  TRACE("iface %p, attrib_table %p, attrib_table_size %u.\n", iface, attrib_table, attrib_table_size);
1844 
1845  if (attrib_table_size) {
1846  size_t size = attrib_table_size * sizeof(*attrib_table);
1847 
1848  new_table = HeapAlloc(GetProcessHeap(), 0, size);
1849  if (!new_table)
1850  return E_OUTOFMEMORY;
1851 
1852  CopyMemory(new_table, attrib_table, size);
1853  } else if (attrib_table) {
1854  return D3DERR_INVALIDCALL;
1855  }
1856  HeapFree(GetProcessHeap(), 0, mesh->attrib_table);
1857  mesh->attrib_table = new_table;
1859 
1860  return D3D_OK;
1861 }
1862 
1863 static const struct ID3DXMeshVtbl D3DXMesh_Vtbl =
1864 {
1894 };
1895 
1896 
1897 /* Algorithm taken from the article: An Efficient and Robust Ray-Box Intersection Algorithm
1898 Amy Williams University of Utah
1899 Steve Barrus University of Utah
1900 R. Keith Morley University of Utah
1901 Peter Shirley University of Utah
1902 
1903 International Conference on Computer Graphics and Interactive Techniques archive
1904 ACM SIGGRAPH 2005 Courses
1905 Los Angeles, California
1906 
1907 This algorithm is free of patents or of copyrights, as confirmed by Peter Shirley himself.
1908 
1909 Algorithm: Consider the box as the intersection of three slabs. Clip the ray
1910 against each slab, if there's anything left of the ray after we're
1911 done we've got an intersection of the ray with the box. */
1913  const D3DXVECTOR3 *prayposition, const D3DXVECTOR3 *praydirection)
1914 {
1915  FLOAT div, tmin, tmax, tymin, tymax, tzmin, tzmax;
1916 
1917  div = 1.0f / praydirection->x;
1918  if ( div >= 0.0f )
1919  {
1920  tmin = ( pmin->x - prayposition->x ) * div;
1921  tmax = ( pmax->x - prayposition->x ) * div;
1922  }
1923  else
1924  {
1925  tmin = ( pmax->x - prayposition->x ) * div;
1926  tmax = ( pmin->x - prayposition->x ) * div;
1927  }
1928 
1929  if ( tmax < 0.0f ) return FALSE;
1930 
1931  div = 1.0f / praydirection->y;
1932  if ( div >= 0.0f )
1933  {
1934  tymin = ( pmin->y - prayposition->y ) * div;
1935  tymax = ( pmax->y - prayposition->y ) * div;
1936  }
1937  else
1938  {
1939  tymin = ( pmax->y - prayposition->y ) * div;
1940  tymax = ( pmin->y - prayposition->y ) * div;
1941  }
1942 
1943  if ( ( tymax < 0.0f ) || ( tmin > tymax ) || ( tymin > tmax ) ) return FALSE;
1944 
1945  if ( tymin > tmin ) tmin = tymin;
1946  if ( tymax < tmax ) tmax = tymax;
1947 
1948  div = 1.0f / praydirection->z;
1949  if ( div >= 0.0f )
1950  {
1951  tzmin = ( pmin->z - prayposition->z ) * div;
1952  tzmax = ( pmax->z - prayposition->z ) * div;
1953  }
1954  else
1955  {
1956  tzmin = ( pmax->z - prayposition->z ) * div;
1957  tzmax = ( pmin->z - prayposition->z ) * div;
1958  }
1959 
1960  if ( (tzmax < 0.0f ) || ( tmin > tzmax ) || ( tzmin > tmax ) ) return FALSE;
1961 
1962  return TRUE;
1963 }
1964 
1966  DWORD numvertices, DWORD dwstride, D3DXVECTOR3 *pmin, D3DXVECTOR3 *pmax)
1967 {
1968  D3DXVECTOR3 vec;
1969  unsigned int i;
1970 
1971  if( !pfirstposition || !pmin || !pmax ) return D3DERR_INVALIDCALL;
1972 
1973  *pmin = *pfirstposition;
1974  *pmax = *pmin;
1975 
1976  for(i=0; i<numvertices; i++)
1977  {
1978  vec = *( (const D3DXVECTOR3*)((const char*)pfirstposition + dwstride * i) );
1979 
1980  if ( vec.x < pmin->x ) pmin->x = vec.x;
1981  if ( vec.x > pmax->x ) pmax->x = vec.x;
1982 
1983  if ( vec.y < pmin->y ) pmin->y = vec.y;
1984  if ( vec.y > pmax->y ) pmax->y = vec.y;
1985 
1986  if ( vec.z < pmin->z ) pmin->z = vec.z;
1987  if ( vec.z > pmax->z ) pmax->z = vec.z;
1988  }
1989 
1990  return D3D_OK;
1991 }
1992 
1994  DWORD numvertices, DWORD dwstride, D3DXVECTOR3 *pcenter, float *pradius)
1995 {
1996  D3DXVECTOR3 temp;
1997  FLOAT d;
1998  unsigned int i;
1999 
2000  if( !pfirstposition || !pcenter || !pradius ) return D3DERR_INVALIDCALL;
2001 
2002  temp.x = 0.0f;
2003  temp.y = 0.0f;
2004  temp.z = 0.0f;
2005  *pradius = 0.0f;
2006 
2007  for(i=0; i<numvertices; i++)
2008  D3DXVec3Add(&temp, &temp, (const D3DXVECTOR3*)((const char*)pfirstposition + dwstride * i));
2009 
2010  D3DXVec3Scale(pcenter, &temp, 1.0f / numvertices);
2011 
2012  for(i=0; i<numvertices; i++)
2013  {
2014  d = D3DXVec3Length(D3DXVec3Subtract(&temp, (const D3DXVECTOR3*)((const char*)pfirstposition + dwstride * i), pcenter));
2015  if ( d > *pradius ) *pradius = d;
2016  }
2017  return D3D_OK;
2018 }
2019 
2022 {
2023  declaration[*idx].Stream = 0;
2024  declaration[*idx].Offset = *offset;
2025  declaration[*idx].Type = type;
2027  declaration[*idx].Usage = usage;
2028  declaration[*idx].UsageIndex = usage_idx;
2029 
2031  ++(*idx);
2032 }
2033 
2034 /*************************************************************************
2035  * D3DXDeclaratorFromFVF
2036  */
2038 {
2039  static const D3DVERTEXELEMENT9 end_element = D3DDECL_END();
2040  DWORD tex_count = (fvf & D3DFVF_TEXCOUNT_MASK) >> D3DFVF_TEXCOUNT_SHIFT;
2041  unsigned int offset = 0;
2042  unsigned int idx = 0;
2043  unsigned int i;
2044 
2045  TRACE("fvf %#x, declaration %p.\n", fvf, declaration);
2046 
2048 
2049  if (fvf & D3DFVF_POSITION_MASK)
2050  {
2051  BOOL has_blend = (fvf & D3DFVF_XYZB5) >= D3DFVF_XYZB1;
2052  DWORD blend_count = 1 + (((fvf & D3DFVF_XYZB5) - D3DFVF_XYZB1) >> 1);
2053  BOOL has_blend_idx = (fvf & D3DFVF_LASTBETA_D3DCOLOR) || (fvf & D3DFVF_LASTBETA_UBYTE4);
2054 
2055  if (has_blend_idx) --blend_count;
2056 
2057  if ((fvf & D3DFVF_POSITION_MASK) == D3DFVF_XYZW
2058  || (has_blend && blend_count > 4))
2059  return D3DERR_INVALIDCALL;
2060 
2061  if ((fvf & D3DFVF_POSITION_MASK) == D3DFVF_XYZRHW)
2063  else
2065 
2066  if (has_blend)
2067  {
2068  switch (blend_count)
2069  {
2070  case 0:
2071  break;
2072  case 1:
2074  break;
2075  case 2:
2077  break;
2078  case 3:
2080  break;
2081  case 4:
2083  break;
2084  default:
2085  ERR("Invalid blend count %u.\n", blend_count);
2086  break;
2087  }
2088 
2089  if (has_blend_idx)
2090  {
2091  if (fvf & D3DFVF_LASTBETA_UBYTE4)
2093  else if (fvf & D3DFVF_LASTBETA_D3DCOLOR)
2095  }
2096  }
2097  }
2098 
2099  if (fvf & D3DFVF_NORMAL)
2101  if (fvf & D3DFVF_PSIZE)
2103  if (fvf & D3DFVF_DIFFUSE)
2105  if (fvf & D3DFVF_SPECULAR)
2107 
2108  for (i = 0; i < tex_count; ++i)
2109  {
2110  switch ((fvf >> (16 + 2 * i)) & 0x03)
2111  {
2112  case D3DFVF_TEXTUREFORMAT1:
2114  break;
2115  case D3DFVF_TEXTUREFORMAT2:
2117  break;
2118  case D3DFVF_TEXTUREFORMAT3:
2120  break;
2121  case D3DFVF_TEXTUREFORMAT4:
2123  break;
2124  }
2125  }
2126 
2127  declaration[idx] = end_element;
2128 
2129  return D3D_OK;
2130 }
2131 
2132 /*************************************************************************
2133  * D3DXFVFFromDeclarator
2134  */
2136 {
2137  unsigned int i = 0, texture, offset;
2138 
2139  TRACE("(%p, %p)\n", declaration, fvf);
2140 
2141  *fvf = 0;
2143  {
2145  declaration[1].UsageIndex == 0) &&
2147  declaration[2].UsageIndex == 0))
2148  {
2149  return D3DERR_INVALIDCALL;
2150  }
2152  declaration[1].Usage == D3DDECLUSAGE_BLENDINDICES && declaration[1].UsageIndex == 0)
2153  {
2155  {
2157  }
2158  else
2159  {
2161  }
2162  i = 2;
2163  }
2165  declaration[1].UsageIndex == 0)
2166  {
2168  declaration[2].Usage == D3DDECLUSAGE_BLENDINDICES && declaration[2].UsageIndex == 0)
2169  {
2171  {
2172  *fvf |= D3DFVF_LASTBETA_UBYTE4;
2173  }
2174  else
2175  {
2176  *fvf |= D3DFVF_LASTBETA_D3DCOLOR;
2177  }
2178  switch (declaration[1].Type)
2179  {
2180  case D3DDECLTYPE_FLOAT1: *fvf |= D3DFVF_XYZB2; break;
2181  case D3DDECLTYPE_FLOAT2: *fvf |= D3DFVF_XYZB3; break;
2182  case D3DDECLTYPE_FLOAT3: *fvf |= D3DFVF_XYZB4; break;
2183  case D3DDECLTYPE_FLOAT4: *fvf |= D3DFVF_XYZB5; break;
2184  }
2185  i = 3;
2186  }
2187  else
2188  {
2189  switch (declaration[1].Type)
2190  {
2191  case D3DDECLTYPE_FLOAT1: *fvf |= D3DFVF_XYZB1; break;
2192  case D3DDECLTYPE_FLOAT2: *fvf |= D3DFVF_XYZB2; break;
2193  case D3DDECLTYPE_FLOAT3: *fvf |= D3DFVF_XYZB3; break;
2194  case D3DDECLTYPE_FLOAT4: *fvf |= D3DFVF_XYZB4; break;
2195  }
2196  i = 2;
2197  }
2198  }
2199  else
2200  {
2201  *fvf |= D3DFVF_XYZ;
2202  i = 1;
2203  }
2204  }
2206  declaration[0].UsageIndex == 0)
2207  {
2208  *fvf |= D3DFVF_XYZRHW;
2209  i = 1;
2210  }
2211 
2213  {
2214  *fvf |= D3DFVF_NORMAL;
2215  i++;
2216  }
2218  declaration[i].UsageIndex == 0)
2219  {
2220  *fvf |= D3DFVF_PSIZE;
2221  i++;
2222  }
2224  declaration[i].UsageIndex == 0)
2225  {
2226  *fvf |= D3DFVF_DIFFUSE;
2227  i++;
2228  }
2230  declaration[i].UsageIndex == 1)
2231  {
2232  *fvf |= D3DFVF_SPECULAR;
2233  i++;
2234  }
2235 
2236  for (texture = 0; texture < D3DDP_MAXTEXCOORD; i++, texture++)
2237  {
2238  if (declaration[i].Stream == 0xFF)
2239  {
2240  break;
2241  }
2243  declaration[i].UsageIndex == texture)
2244  {
2245  *fvf |= D3DFVF_TEXCOORDSIZE1(declaration[i].UsageIndex);
2246  }
2248  declaration[i].UsageIndex == texture)
2249  {
2250  *fvf |= D3DFVF_TEXCOORDSIZE2(declaration[i].UsageIndex);
2251  }
2253  declaration[i].UsageIndex == texture)
2254  {
2255  *fvf |= D3DFVF_TEXCOORDSIZE3(declaration[i].UsageIndex);
2256  }
2258  declaration[i].UsageIndex == texture)
2259  {
2260  *fvf |= D3DFVF_TEXCOORDSIZE4(declaration[i].UsageIndex);
2261  }
2262  else
2263  {
2264  return D3DERR_INVALIDCALL;
2265  }
2266  }
2267 
2268  *fvf |= (texture << D3DFVF_TEXCOUNT_SHIFT);
2269 
2270  for (offset = 0, i = 0; declaration[i].Stream != 0xFF;
2271  offset += d3dx_decltype_size[declaration[i].Type], i++)
2272  {
2273  if (declaration[i].Offset != offset)
2274  {
2275  return D3DERR_INVALIDCALL;
2276  }
2277  }
2278 
2279  return D3D_OK;
2280 }
2281 
2282 /*************************************************************************
2283  * D3DXGetFVFVertexSize
2284  */
2285 static UINT Get_TexCoord_Size_From_FVF(DWORD FVF, int tex_num)
2286 {
2287  return (((((FVF) >> (16 + (2 * (tex_num)))) + 1) & 0x03) + 1);
2288 }
2289 
2291 {
2292  DWORD size = 0;
2293  UINT i;
2294  UINT numTextures = (FVF & D3DFVF_TEXCOUNT_MASK) >> D3DFVF_TEXCOUNT_SHIFT;
2295 
2296  if (FVF & D3DFVF_NORMAL) size += sizeof(D3DXVECTOR3);
2297  if (FVF & D3DFVF_DIFFUSE) size += sizeof(DWORD);
2298  if (FVF & D3DFVF_SPECULAR) size += sizeof(DWORD);
2299  if (FVF & D3DFVF_PSIZE) size += sizeof(DWORD);
2300 
2301  switch (FVF & D3DFVF_POSITION_MASK)
2302  {
2303  case D3DFVF_XYZ: size += sizeof(D3DXVECTOR3); break;
2304  case D3DFVF_XYZRHW: size += 4 * sizeof(FLOAT); break;
2305  case D3DFVF_XYZB1: size += 4 * sizeof(FLOAT); break;
2306  case D3DFVF_XYZB2: size += 5 * sizeof(FLOAT); break;
2307  case D3DFVF_XYZB3: size += 6 * sizeof(FLOAT); break;
2308  case D3DFVF_XYZB4: size += 7 * sizeof(FLOAT); break;
2309  case D3DFVF_XYZB5: size += 8 * sizeof(FLOAT); break;
2310  case D3DFVF_XYZW: size += 4 * sizeof(FLOAT); break;
2311  }
2312 
2313  for (i = 0; i < numTextures; i++)
2314  {
2315  size += Get_TexCoord_Size_From_FVF(FVF, i) * sizeof(FLOAT);
2316  }
2317 
2318  return size;
2319 }
2320 
2321 /*************************************************************************
2322  * D3DXGetDeclVertexSize
2323  */
2325 {
2326  const D3DVERTEXELEMENT9 *element;
2327  UINT size = 0;
2328 
2329  TRACE("decl %p, stream_idx %u\n", decl, stream_idx);
2330 
2331  if (!decl) return 0;
2332 
2333  for (element = decl; element->Stream != 0xff; ++element)
2334  {
2335  UINT type_size;
2336 
2337  if (element->Stream != stream_idx) continue;
2338 
2339  if (element->Type >= ARRAY_SIZE(d3dx_decltype_size))
2340  {
2341  FIXME("Unhandled element type %#x, size will be incorrect.\n", element->Type);
2342  continue;
2343  }
2344 
2345  type_size = d3dx_decltype_size[element->Type];
2346  if (element->Offset + type_size > size) size = element->Offset + type_size;
2347  }
2348 
2349  return size;
2350 }
2351 
2352 /*************************************************************************
2353  * D3DXGetDeclLength
2354  */
2356 {
2357  const D3DVERTEXELEMENT9 *element;
2358 
2359  TRACE("decl %p\n", decl);
2360 
2361  /* null decl results in exception on Windows XP */
2362 
2363  for (element = decl; element->Stream != 0xff; ++element);
2364 
2365  return element - decl;
2366 }
2367 
2369  const D3DXVECTOR3 *praypos, const D3DXVECTOR3 *praydir, float *pu, float *pv, float *pdist)
2370 {
2371  D3DXMATRIX m;
2372  D3DXVECTOR4 vec;
2373 
2374  TRACE("p0 %p, p1 %p, p2 %p, praypos %p, praydir %p, pu %p, pv %p, pdist %p.\n",
2375  p0, p1, p2, praypos, praydir, pu, pv, pdist);
2376 
2377  m.u.m[0][0] = p1->x - p0->x;
2378  m.u.m[1][0] = p2->x - p0->x;
2379  m.u.m[2][0] = -praydir->x;
2380  m.u.m[3][0] = 0.0f;
2381  m.u.m[0][1] = p1->y - p0->y;
2382  m.u.m[1][1] = p2->y - p0->y;
2383  m.u.m[2][1] = -praydir->y;
2384  m.u.m[3][1] = 0.0f;
2385  m.u.m[0][2] = p1->z - p0->z;
2386  m.u.m[1][2] = p2->z - p0->z;
2387  m.u.m[2][2] = -praydir->z;
2388  m.u.m[3][2] = 0.0f;
2389  m.u.m[0][3] = 0.0f;
2390  m.u.m[1][3] = 0.0f;
2391  m.u.m[2][3] = 0.0f;
2392  m.u.m[3][3] = 1.0f;
2393 
2394  vec.x = praypos->x - p0->x;
2395  vec.y = praypos->y - p0->y;
2396  vec.z = praypos->z - p0->z;
2397  vec.w = 0.0f;
2398 
2399  if ( D3DXMatrixInverse(&m, NULL, &m) )
2400  {
2401  D3DXVec4Transform(&vec, &vec, &m);
2402  if ( (vec.x >= 0.0f) && (vec.y >= 0.0f) && (vec.x + vec.y <= 1.0f) && (vec.z >= 0.0f) )
2403  {
2404  if (pu) *pu = vec.x;
2405  if (pv) *pv = vec.y;
2406  if (pdist) *pdist = fabsf( vec.z );
2407  return TRUE;
2408  }
2409  }
2410 
2411  return FALSE;
2412 }
2413 
2414 BOOL WINAPI D3DXSphereBoundProbe(const D3DXVECTOR3 *pcenter, float radius,
2415  const D3DXVECTOR3 *prayposition, const D3DXVECTOR3 *praydirection)
2416 {
2417  D3DXVECTOR3 difference;
2418  FLOAT a, b, c, d;
2419 
2420  a = D3DXVec3LengthSq(praydirection);
2421  if (!D3DXVec3Subtract(&difference, prayposition, pcenter)) return FALSE;
2422  b = D3DXVec3Dot(&difference, praydirection);
2423  c = D3DXVec3LengthSq(&difference) - radius * radius;
2424  d = b * b - a * c;
2425 
2426  if ( ( d <= 0.0f ) || ( sqrt(d) <= b ) ) return FALSE;
2427  return TRUE;
2428 }
2429 
2430 /*************************************************************************
2431  * D3DXCreateMesh
2432  */
2434  const D3DVERTEXELEMENT9 *declaration, struct IDirect3DDevice9 *device, struct ID3DXMesh **mesh)
2435 {
2436  HRESULT hr;
2437  DWORD fvf;
2438  IDirect3DVertexDeclaration9 *vertex_declaration;
2439  UINT vertex_declaration_size;
2440  UINT num_elem;
2441  IDirect3DVertexBuffer9 *vertex_buffer;
2442  IDirect3DIndexBuffer9 *index_buffer;
2443  DWORD *attrib_buffer;
2444  struct d3dx9_mesh *object;
2445  DWORD index_usage = 0;
2446  D3DPOOL index_pool = D3DPOOL_DEFAULT;
2447  D3DFORMAT index_format = D3DFMT_INDEX16;
2448  DWORD vertex_usage = 0;
2449  D3DPOOL vertex_pool = D3DPOOL_DEFAULT;
2450  int i;
2451 
2452  TRACE("numfaces %u, numvertices %u, options %#x, declaration %p, device %p, mesh %p.\n",
2454 
2455  if (numfaces == 0 || numvertices == 0 || declaration == NULL || device == NULL || mesh == NULL ||
2456  /* D3DXMESH_VB_SHARE is for cloning, and D3DXMESH_USEHWONLY is for ConvertToBlendedMesh */
2457  (options & (D3DXMESH_VB_SHARE | D3DXMESH_USEHWONLY | 0xfffe0000)))
2458  {
2459  return D3DERR_INVALIDCALL;
2460  }
2461  for (i = 0; declaration[i].Stream != 0xff; i++)
2462  if (declaration[i].Stream != 0)
2463  return D3DERR_INVALIDCALL;
2464  num_elem = i + 1;
2465 
2466  if (options & D3DXMESH_32BIT)
2467  index_format = D3DFMT_INDEX32;
2468 
2469  if (options & D3DXMESH_DONOTCLIP) {
2470  index_usage |= D3DUSAGE_DONOTCLIP;
2471  vertex_usage |= D3DUSAGE_DONOTCLIP;
2472  }
2473  if (options & D3DXMESH_POINTS) {
2474  index_usage |= D3DUSAGE_POINTS;
2475  vertex_usage |= D3DUSAGE_POINTS;
2476  }
2477  if (options & D3DXMESH_RTPATCHES) {
2478  index_usage |= D3DUSAGE_RTPATCHES;
2479  vertex_usage |= D3DUSAGE_RTPATCHES;
2480  }
2481  if (options & D3DXMESH_NPATCHES) {
2482  index_usage |= D3DUSAGE_NPATCHES;
2483  vertex_usage |= D3DUSAGE_NPATCHES;
2484  }
2485 
2487  vertex_pool = D3DPOOL_SYSTEMMEM;
2488  else if (options & D3DXMESH_VB_MANAGED)
2489  vertex_pool = D3DPOOL_MANAGED;
2490 
2492  vertex_usage |= D3DUSAGE_WRITEONLY;
2494  vertex_usage |= D3DUSAGE_DYNAMIC;
2496  vertex_usage |= D3DUSAGE_SOFTWAREPROCESSING;
2497 
2499  index_pool = D3DPOOL_SYSTEMMEM;
2500  else if (options & D3DXMESH_IB_MANAGED)
2501  index_pool = D3DPOOL_MANAGED;
2502 
2504  index_usage |= D3DUSAGE_WRITEONLY;
2506  index_usage |= D3DUSAGE_DYNAMIC;
2508  index_usage |= D3DUSAGE_SOFTWAREPROCESSING;
2509 
2511  if (hr != D3D_OK)
2512  {
2513  fvf = 0;
2514  }
2515 
2516  /* Create vertex declaration */
2518  declaration,
2520  if (FAILED(hr))
2521  {
2522  WARN("Unexpected return value %x from IDirect3DDevice9_CreateVertexDeclaration.\n",hr);
2523  return hr;
2524  }
2526 
2527  /* Create vertex buffer */
2530  vertex_usage,
2531  fvf,
2532  vertex_pool,
2533  &vertex_buffer,
2534  NULL);
2535  if (FAILED(hr))
2536  {
2537  WARN("Unexpected return value %x from IDirect3DDevice9_CreateVertexBuffer.\n",hr);
2539  return hr;
2540  }
2541 
2542  /* Create index buffer */
2544  numfaces * 3 * ((index_format == D3DFMT_INDEX16) ? 2 : 4),
2545  index_usage,
2546  index_format,
2547  index_pool,
2548  &index_buffer,
2549  NULL);
2550  if (FAILED(hr))
2551  {
2552  WARN("Unexpected return value %x from IDirect3DDevice9_CreateVertexBuffer.\n",hr);
2555  return hr;
2556  }
2557 
2559  object = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(*object));
2560  if (object == NULL || attrib_buffer == NULL)
2561  {
2562  HeapFree(GetProcessHeap(), 0, object);
2567  *mesh = NULL;
2568  return E_OUTOFMEMORY;
2569  }
2570  object->ID3DXMesh_iface.lpVtbl = &D3DXMesh_Vtbl;
2571  object->ref = 1;
2572 
2573  object->numfaces = numfaces;
2574  object->numvertices = numvertices;
2575  object->options = options;
2576  object->fvf = fvf;
2577  object->device = device;
2579 
2580  copy_declaration(object->cached_declaration, declaration, num_elem);
2581  object->vertex_declaration = vertex_declaration;
2582  object->vertex_declaration_size = vertex_declaration_size;
2583  object->num_elem = num_elem;
2584  object->vertex_buffer = vertex_buffer;
2585  object->index_buffer = index_buffer;
2586  object->attrib_buffer = attrib_buffer;
2587 
2588  *mesh = &object->ID3DXMesh_iface;
2589 
2590  return D3D_OK;
2591 }
2592 
2593 /*************************************************************************
2594  * D3DXCreateMeshFVF
2595  */
2597  DWORD fvf, struct IDirect3DDevice9 *device, struct ID3DXMesh **mesh)
2598 {
2599  HRESULT hr;
2601 
2602  TRACE("(%u, %u, %u, %u, %p, %p)\n", numfaces, numvertices, options, fvf, device, mesh);
2603 
2605  if (FAILED(hr)) return hr;
2606 
2608 }
2609 
2610 
2611 struct mesh_data {
2618 
2620 
2621  /* optional mesh data */
2622 
2626 
2628 
2630 
2634 
2635  struct ID3DXSkinInfo *skin_info;
2637 };
2638 
2639 static HRESULT parse_texture_filename(ID3DXFileData *filedata, char **filename_out)
2640 {
2641  HRESULT hr;
2642  SIZE_T data_size;
2643  BYTE *data;
2644  char *filename_in;
2645  char *filename = NULL;
2646 
2647  /* template TextureFilename {
2648  * STRING filename;
2649  * }
2650  */
2651 
2652  HeapFree(GetProcessHeap(), 0, *filename_out);
2653  *filename_out = NULL;
2654 
2655  hr = filedata->lpVtbl->Lock(filedata, &data_size, (const void**)&data);
2656  if (FAILED(hr)) return hr;
2657 
2658  /* FIXME: String must be retrieved directly instead of through a pointer once ID3DXFILE is fixed */
2659  if (data_size < sizeof(filename_in))
2660  {
2661  WARN("truncated data (%lu bytes)\n", data_size);
2662  filedata->lpVtbl->Unlock(filedata);
2663  return E_FAIL;
2664  }
2665  filename_in = *(char **)data;
2666 
2667  filename = HeapAlloc(GetProcessHeap(), 0, strlen(filename_in) + 1);
2668  if (!filename) {
2669  filedata->lpVtbl->Unlock(filedata);
2670  return E_OUTOFMEMORY;
2671  }
2672 
2673  strcpy(filename, filename_in);
2674  *filename_out = filename;
2675 
2676  filedata->lpVtbl->Unlock(filedata);
2677 
2678  return D3D_OK;
2679 }
2680 
2681 static HRESULT parse_material(ID3DXFileData *filedata, D3DXMATERIAL *material)
2682 {
2683  HRESULT hr;
2684  SIZE_T data_size;
2685  const BYTE *data;
2686  GUID type;
2687  ID3DXFileData *child;
2688  SIZE_T i, nb_children;
2689 
2690  material->pTextureFilename = NULL;
2691 
2692  hr = filedata->lpVtbl->Lock(filedata, &data_size, (const void**)&data);
2693  if (FAILED(hr)) return hr;
2694 
2695  /*
2696  * template ColorRGBA {
2697  * FLOAT red;
2698  * FLOAT green;
2699  * FLOAT blue;
2700  * FLOAT alpha;
2701  * }
2702  * template ColorRGB {
2703  * FLOAT red;
2704  * FLOAT green;
2705  * FLOAT blue;
2706  * }
2707  * template Material {
2708  * ColorRGBA faceColor;
2709  * FLOAT power;
2710  * ColorRGB specularColor;
2711  * ColorRGB emissiveColor;
2712  * [ ... ]
2713  * }
2714  */
2715  if (data_size != sizeof(FLOAT) * 11) {
2716  WARN("incorrect data size (%ld bytes)\n", data_size);
2717  filedata->lpVtbl->Unlock(filedata);
2718  return E_FAIL;
2719  }
2720 
2721  memcpy(&material->MatD3D.Diffuse, data, sizeof(D3DCOLORVALUE));
2722  data += sizeof(D3DCOLORVALUE);
2723  material->MatD3D.Power = *(FLOAT*)data;
2724  data += sizeof(FLOAT);
2725  memcpy(&material->MatD3D.Specular, data, sizeof(FLOAT) * 3);
2726  material->MatD3D.Specular.a = 1.0f;
2727  data += 3 * sizeof(FLOAT);
2728  memcpy(&material->MatD3D.Emissive, data, sizeof(FLOAT) * 3);
2729  material->MatD3D.Emissive.a = 1.0f;
2730  material->MatD3D.Ambient.r = 0.0f;
2731  material->MatD3D.Ambient.g = 0.0f;
2732  material->MatD3D.Ambient.b = 0.0f;
2733  material->MatD3D.Ambient.a = 1.0f;
2734 
2735  filedata->lpVtbl->Unlock(filedata);
2736 
2737  hr = filedata->lpVtbl->GetChildren(filedata, &nb_children);
2738  if (FAILED(hr))
2739  return hr;
2740 
2741  for (i = 0; i < nb_children; i++)
2742  {
2743  hr = filedata->lpVtbl->GetChild(filedata, i, &child);
2744  if (FAILED(hr))
2745  return hr;
2746  hr = child->lpVtbl->GetType(child, &type);
2747  if (FAILED(hr))
2748  goto err;
2749 
2750  if (IsEqualGUID(&type, &TID_D3DRMTextureFilename)) {
2752  if (FAILED(hr))
2753  goto err;
2754  }
2755  IUnknown_Release(child);
2756  }
2757  return D3D_OK;
2758 
2759 err:
2760  IUnknown_Release(child);
2761  return hr;
2762 }
2763 
2764 static void destroy_materials(struct mesh_data *mesh)
2765 {
2766  DWORD i;
2767  for (i = 0; i < mesh->num_materials; i++)
2769  HeapFree(GetProcessHeap(), 0, mesh->materials);
2771  mesh->num_materials = 0;
2772  mesh->materials = NULL;
2773  mesh->material_indices = NULL;
2774 }
2775 
2776 static HRESULT parse_material_list(ID3DXFileData *filedata, struct mesh_data *mesh)
2777 {
2778  HRESULT hr;
2779  SIZE_T data_size;
2780  const DWORD *data, *in_ptr;
2781  GUID type;
2782  ID3DXFileData *child = NULL;
2783  DWORD num_materials;
2784  DWORD i;
2785  SIZE_T nb_children;
2786 
2787  destroy_materials(mesh);
2788 
2789  hr = filedata->lpVtbl->Lock(filedata, &data_size, (const void**)&data);
2790  if (FAILED(hr)) return hr;
2791 
2792  /* template MeshMaterialList {
2793  * DWORD nMaterials;
2794  * DWORD nFaceIndexes;
2795  * array DWORD faceIndexes[nFaceIndexes];
2796  * [ Material ]
2797  * }
2798  */
2799 
2800  in_ptr = data;
2801  hr = E_FAIL;
2802 
2803  if (data_size < sizeof(DWORD)) {
2804  WARN("truncated data (%ld bytes)\n", data_size);
2805  goto end;
2806  }
2807  num_materials = *in_ptr++;
2808  if (!num_materials) {
2809  hr = D3D_OK;
2810  goto end;
2811  }
2812 
2813  if (data_size < 2 * sizeof(DWORD)) {
2814  WARN("truncated data (%ld bytes)\n", data_size);
2815  goto end;
2816  }
2817  if (*in_ptr++ != mesh->num_poly_faces) {
2818  WARN("number of material face indices (%u) doesn't match number of faces (%u)\n",
2819  *(in_ptr - 1), mesh->num_poly_faces);
2820  goto end;
2821  }
2822  if (data_size < 2 * sizeof(DWORD) + mesh->num_poly_faces * sizeof(DWORD)) {
2823  WARN("truncated data (%ld bytes)\n", data_size);
2824  goto end;
2825  }
2826  for (i = 0; i < mesh->num_poly_faces; i++) {
2827  if (*in_ptr++ >= num_materials) {
2828  WARN("face %u: reference to undefined material %u (only %u materials)\n",
2829  i, *(in_ptr - 1), num_materials);
2830  goto end;
2831  }
2832  }
2833 
2834  mesh->materials = HeapAlloc(GetProcessHeap(), 0, num_materials * sizeof(*mesh->materials));
2835  mesh->material_indices = HeapAlloc(GetProcessHeap(), 0, mesh->num_poly_faces * sizeof(*mesh->material_indices));
2836  if (!mesh->materials || !mesh->material_indices) {
2837  hr = E_OUTOFMEMORY;
2838  goto end;
2839  }
2840  memcpy(mesh->material_indices, data + 2, mesh->num_poly_faces * sizeof(DWORD));
2841 
2842  hr = filedata->lpVtbl->GetChildren(filedata, &nb_children);
2843  if (FAILED(hr))
2844  goto end;
2845 
2846  for (i = 0; i < nb_children; i++)
2847  {
2848  hr = filedata->lpVtbl->GetChild(filedata, i, &child);
2849  if (FAILED(hr))
2850  goto end;
2851  hr = child->lpVtbl->GetType(child, &type);
2852  if (FAILED(hr))
2853  goto end;
2854 
2855  if (IsEqualGUID(&type, &TID_D3DRMMaterial)) {
2856  if (mesh->num_materials >= num_materials) {
2857  WARN("more materials defined than declared\n");
2858  hr = E_FAIL;
2859  goto end;
2860  }
2861  hr = parse_material(child, &mesh->materials[mesh->num_materials++]);
2862  if (FAILED(hr))
2863  goto end;
2864  }
2865 
2866  IUnknown_Release(child);
2867  child = NULL;
2868  }
2869  if (num_materials != mesh->num_materials) {
2870  WARN("only %u of %u materials defined\n", num_materials, mesh->num_materials);
2871  hr = E_FAIL;
2872  }
2873 
2874 end:
2875  if (child)
2876  IUnknown_Release(child);
2877  filedata->lpVtbl->Unlock(filedata);
2878  return hr;
2879 }
2880 
2881 static HRESULT parse_texture_coords(ID3DXFileData *filedata, struct mesh_data *mesh)
2882 {
2883  HRESULT hr;
2884  SIZE_T data_size;
2885  const BYTE *data;
2886 
2887  HeapFree(GetProcessHeap(), 0, mesh->tex_coords);
2888  mesh->tex_coords = NULL;
2889 
2890  hr = filedata->lpVtbl->Lock(filedata, &data_size, (const void**)&data);
2891  if (FAILED(hr)) return hr;
2892 
2893  /* template Coords2d {
2894  * FLOAT u;
2895  * FLOAT v;
2896  * }
2897  * template MeshTextureCoords {
2898  * DWORD nTextureCoords;
2899  * array Coords2d textureCoords[nTextureCoords];
2900  * }
2901  */
2902 
2903  hr = E_FAIL;
2904 
2905  if (data_size < sizeof(DWORD)) {
2906  WARN("truncated data (%ld bytes)\n", data_size);
2907  goto end;
2908  }
2909  if (*(DWORD*)data != mesh->num_vertices) {
2910  WARN("number of texture coordinates (%u) doesn't match number of vertices (%u)\n",
2911  *(DWORD*)data, mesh->num_vertices);
2912  goto end;
2913  }
2914  data += sizeof(DWORD);
2915  if (data_size < sizeof(DWORD) + mesh->num_vertices * sizeof(*mesh->tex_coords)) {
2916  WARN("truncated data (%ld bytes)\n", data_size);
2917  goto end;
2918  }
2919 
2920  mesh->tex_coords = HeapAlloc(GetProcessHeap(), 0, mesh->num_vertices * sizeof(*mesh->tex_coords));
2921  if (!mesh->tex_coords) {
2922  hr = E_OUTOFMEMORY;
2923  goto end;
2924  }
2925  memcpy(mesh->tex_coords, data, mesh->num_vertices * sizeof(*mesh->tex_coords));
2926 
2927  mesh->fvf |= D3DFVF_TEX1;
2928 
2929  hr = D3D_OK;
2930 
2931 end:
2932  filedata->lpVtbl->Unlock(filedata);
2933  return hr;
2934 }
2935 
2936 static HRESULT parse_vertex_colors(ID3DXFileData *filedata, struct mesh_data *mesh)
2937 {
2938  HRESULT hr;
2939  SIZE_T data_size;
2940  const BYTE *data;
2941  DWORD num_colors;
2942  DWORD i;
2943 
2944  HeapFree(GetProcessHeap(), 0, mesh->vertex_colors);
2945  mesh->vertex_colors = NULL;
2946 
2947  hr = filedata->lpVtbl->Lock(filedata, &data_size, (const void**)&data);
2948  if (FAILED(hr)) return hr;
2949 
2950  /* template IndexedColor {
2951  * DWORD index;
2952  * ColorRGBA indexColor;
2953  * }
2954  * template MeshVertexColors {
2955  * DWORD nVertexColors;
2956  * array IndexedColor vertexColors[nVertexColors];
2957  * }
2958  */
2959 
2960  hr = E_FAIL;
2961 
2962  if (data_size < sizeof(DWORD)) {
2963  WARN("truncated data (%ld bytes)\n", data_size);
2964  goto end;
2965  }
2966  num_colors = *(DWORD*)data;
2967  data += sizeof(DWORD);
2968  if (data_size < sizeof(DWORD) + num_colors * (sizeof(DWORD) + sizeof(D3DCOLORVALUE))) {
2969  WARN("truncated data (%ld bytes)\n", data_size);
2970  goto end;
2971  }
2972 
2973  mesh->vertex_colors = HeapAlloc(GetProcessHeap(), 0, mesh->num_vertices * sizeof(DWORD));
2974  if (!mesh->vertex_colors) {
2975  hr = E_OUTOFMEMORY;
2976  goto end;
2977  }
2978 
2979  for (i = 0; i < mesh->num_vertices; i++)
2980  mesh->vertex_colors[i] = D3DCOLOR_ARGB(0, 0xff, 0xff, 0xff);
2981  for (i = 0; i < num_colors; i++)
2982  {
2984  DWORD index = *(DWORD*)data;
2985  data += sizeof(DWORD);
2986  if (index >= mesh->num_vertices) {
2987  WARN("vertex color %u references undefined vertex %u (only %u vertices)\n",
2988  i, index, mesh->num_vertices);
2989  goto end;
2990  }
2991  memcpy(&color, data, sizeof(color));
2992  data += sizeof(color);
2993  color.r = min(1.0f, max(0.0f, color.r));
2994  color.g = min(1.0f, max(0.0f, color.g));
2995  color.b = min(1.0f, max(0.0f, color.b));
2996  color.a = min(1.0f, max(0.0f, color.a));
2997  mesh->vertex_colors[index] = D3DCOLOR_ARGB((BYTE)(color.a * 255.0f + 0.5f),
2998  (BYTE)(color.r * 255.0f + 0.5f),
2999  (BYTE)(color.g * 255.0f + 0.5f),
3000  (BYTE)(color.b * 255.0f + 0.5f));
3001  }
3002 
3003  mesh->fvf |= D3DFVF_DIFFUSE;
3004 
3005  hr = D3D_OK;
3006 
3007 end:
3008  filedata->lpVtbl->Unlock(filedata);
3009  return hr;
3010 }
3011 
3012 static HRESULT parse_normals(ID3DXFileData *filedata, struct mesh_data *mesh)
3013 {
3014  HRESULT hr;
3015  SIZE_T data_size;
3016  const BYTE *data;
3017  DWORD *index_out_ptr;
3018  DWORD i;
3019  DWORD num_face_indices = mesh->num_poly_faces * 2 + mesh->num_tri_faces;
3020 
3021  HeapFree(GetProcessHeap(), 0, mesh->normals);
3022  mesh->num_normals = 0;
3023  mesh->normals = NULL;
3024  mesh->normal_indices = NULL;
3025  mesh->fvf |= D3DFVF_NORMAL;
3026 
3027  hr = filedata->lpVtbl->Lock(filedata, &data_size, (const void**)&data);
3028  if (FAILED(hr)) return hr;
3029 
3030  /* template Vector {
3031  * FLOAT x;
3032  * FLOAT y;
3033  * FLOAT z;
3034  * }
3035  * template MeshFace {
3036  * DWORD nFaceVertexIndices;
3037  * array DWORD faceVertexIndices[nFaceVertexIndices];
3038  * }
3039  * template MeshNormals {
3040  * DWORD nNormals;
3041  * array Vector normals[nNormals];
3042  * DWORD nFaceNormals;
3043  * array MeshFace faceNormals[nFaceNormals];
3044  * }
3045  */
3046 
3047  hr = E_FAIL;
3048 
3049  if (data_size < sizeof(DWORD) * 2) {
3050  WARN("truncated data (%ld bytes)\n", data_size);
3051  goto end;
3052  }
3053  mesh->num_normals = *(DWORD*)data;
3054  data += sizeof(DWORD);
3055  if (data_size < sizeof(DWORD) * 2 + mesh->num_normals * sizeof(D3DXVECTOR3) +
3056  num_face_indices * sizeof(DWORD)) {
3057  WARN("truncated data (%ld bytes)\n", data_size);
3058  goto end;
3059  }
3060 
3061  mesh->normals = HeapAlloc(GetProcessHeap(), 0, mesh->num_normals * sizeof(D3DXVECTOR3));
3062  mesh->normal_indices = HeapAlloc(GetProcessHeap(), 0, num_face_indices * sizeof(DWORD));
3063  if (!mesh->normals || !mesh->normal_indices) {
3064  hr = E_OUTOFMEMORY;
3065  goto end;
3066  }
3067 
3068  memcpy(mesh->normals, data, mesh->num_normals * sizeof(D3DXVECTOR3));
3069  data += mesh->num_normals * sizeof(D3DXVECTOR3);
3070  for (i = 0; i < mesh->num_normals; i++)
3071  D3DXVec3Normalize(&mesh->normals[i], &mesh->normals[i]);
3072 
3073  if (*(DWORD*)data != mesh->num_poly_faces) {
3074  WARN("number of face normals (%u) doesn't match number of faces (%u)\n",
3075  *(DWORD*)data, mesh->num_poly_faces);
3076  goto end;
3077  }
3078  data += sizeof(DWORD);
3079  index_out_ptr = mesh->normal_indices;
3080  for (i = 0; i < mesh->num_poly_faces; i++)
3081  {
3082  DWORD j;
3083  DWORD count = *(DWORD*)data;
3084  if (count != mesh->num_tri_per_face[i] + 2) {
3085  WARN("face %u: number of normals (%u) doesn't match number of vertices (%u)\n",
3086  i, count, mesh->num_tri_per_face[i] + 2);
3087  goto end;
3088  }
3089  data += sizeof(DWORD);
3090 
3091  for (j = 0; j < count; j++) {
3092  DWORD normal_index = *(DWORD*)data;
3093  if (normal_index >= mesh->num_normals) {
3094  WARN("face %u, normal index %u: reference to undefined normal %u (only %u normals)\n",
3095  i, j, normal_index, mesh->num_normals);
3096  goto end;
3097  }
3098  *index_out_ptr++ = normal_index;
3099  data += sizeof(DWORD);
3100  }
3101  }
3102 
3103  hr = D3D_OK;
3104 
3105 end:
3106  filedata->lpVtbl->Unlock(filedata);
3107  return hr;
3108 }
3109 
3110 static HRESULT parse_skin_mesh_info(ID3DXFileData *filedata, struct mesh_data *mesh_data, DWORD index)
3111 {
3112  HRESULT hr;
3113  SIZE_T data_size;
3114  const BYTE *data;
3115 
3116  TRACE("(%p, %p, %u)\n", filedata, mesh_data, index);
3117 
3118  hr = filedata->lpVtbl->Lock(filedata, &data_size, (const void**)&data);
3119  if (FAILED(hr)) return hr;
3120 
3121  hr = E_FAIL;
3122 
3123  if (!mesh_data->skin_info) {
3124  if (data_size < sizeof(WORD) * 3) {
3125  WARN("truncated data (%ld bytes)\n", data_size);
3126  goto end;
3127  }
3128  /* Skip nMaxSkinWeightsPerVertex and nMaxSkinWeightsPerFace */
3129  data += 2 * sizeof(WORD);
3130  mesh_data->nb_bones = *(WORD*)data;
3132  } else {
3133  const char *name;
3134  DWORD nb_influences;
3135 
3136  /* FIXME: String must be retrieved directly instead of through a pointer once ID3DXFILE is fixed */
3137  name = *(const char**)data;
3138  data += sizeof(char*);
3139 
3140  nb_influences = *(DWORD*)data;
3141  data += sizeof(DWORD);
3142 
3143  if (data_size < (sizeof(char*) + sizeof(DWORD) + nb_influences * (sizeof(DWORD) + sizeof(FLOAT)) + 16 * sizeof(FLOAT))) {
3144  WARN("truncated data (%ld bytes)\n", data_size);
3145  goto end;
3146  }
3147 
3148  hr = mesh_data->skin_info->lpVtbl->SetBoneName(mesh_data->skin_info, index, name);
3149  if (SUCCEEDED(hr))
3150  hr = mesh_data->skin_info->lpVtbl->SetBoneInfluence(mesh_data->skin_info, index, nb_influences,
3151  (const DWORD*)data, (const FLOAT*)(data + nb_influences * sizeof(DWORD)));
3152  if (SUCCEEDED(hr))
3153  hr = mesh_data->skin_info->lpVtbl->SetBoneOffsetMatrix(mesh_data->skin_info, index,
3154  (const D3DMATRIX*)(data + nb_influences * (sizeof(DWORD) + sizeof(FLOAT))));
3155  }
3156 
3157 end:
3158  filedata->lpVtbl->Unlock(filedata);
3159  return hr;
3160 }
3161 
3162 /* for provide_flags parameters */
3163 #define PROVIDE_MATERIALS 0x1
3164 #define PROVIDE_SKININFO 0x2
3165 #define PROVIDE_ADJACENCY 0x4
3166 
3167 static HRESULT parse_mesh(ID3DXFileData *filedata, struct mesh_data *mesh_data, DWORD provide_flags)
3168 {
3169  HRESULT hr;
3170  SIZE_T data_size;
3171  const BYTE *data, *in_ptr;
3172  DWORD *index_out_ptr;
3173  GUID type;
3174  ID3DXFileData *child = NULL;
3175  DWORD i;
3176  SIZE_T nb_children;
3177  DWORD nb_skin_weights_info = 0;
3178 
3179  /*
3180  * template Mesh {
3181  * DWORD nVertices;
3182  * array Vector vertices[nVertices];
3183  * DWORD nFaces;
3184  * array MeshFace faces[nFaces];
3185  * [ ... ]
3186  * }
3187  */
3188 
3189  hr = filedata->lpVtbl->Lock(filedata, &data_size, (const void**)&data);
3190  if (FAILED(hr)) return hr;
3191 
3192  in_ptr = data;
3193  hr = E_FAIL;
3194 
3195  if (data_size < sizeof(DWORD) * 2) {
3196  WARN("truncated data (%ld bytes)\n", data_size);
3197  goto end;
3198  }
3199  mesh_data->num_vertices = *(DWORD*)in_ptr;
3200  if (data_size < sizeof(DWORD) * 2 + mesh_data->num_vertices * sizeof(D3DXVECTOR3)) {
3201  WARN("truncated data (%ld bytes)\n", data_size);
3202  goto end;
3203  }
3204  in_ptr += sizeof(DWORD) + mesh_data->num_vertices * sizeof(D3DXVECTOR3);
3205 
3206  mesh_data->num_poly_faces = *(DWORD*)in_ptr;
3207  in_ptr += sizeof(DWORD);
3208 
3209  mesh_data->num_tri_faces = 0;
3210  for (i = 0; i < mesh_data->num_poly_faces; i++)
3211  {
3212  DWORD num_poly_vertices;
3213  DWORD j;
3214 
3215  if (data_size - (in_ptr - data) < sizeof(DWORD)) {
3216  WARN("truncated data (%ld bytes)\n", data_size);
3217  goto end;
3218  }
3219  num_poly_vertices = *(DWORD*)in_ptr;
3220  in_ptr += sizeof(DWORD);
3221  if (data_size - (in_ptr - data) < num_poly_vertices * sizeof(DWORD)) {
3222  WARN("truncated data (%ld bytes)\n", data_size);
3223  goto end;
3224  }
3225  if (num_poly_vertices < 3) {
3226  WARN("face %u has only %u vertices\n", i, num_poly_vertices);
3227  goto end;
3228  }
3229  for (j = 0; j < num_poly_vertices; j++) {
3230  if (*(DWORD*)in_ptr >= mesh_data->num_vertices) {
3231  WARN("face %u, index %u: undefined vertex %u (only %u vertices)\n",
3232  i, j, *(DWORD*)in_ptr, mesh_data->num_vertices);
3233  goto end;
3234  }
3235  in_ptr += sizeof(DWORD);
3236  }
3237  mesh_data->num_tri_faces += num_poly_vertices - 2;
3238  }
3239 
3241 
3243  mesh_data->num_vertices * sizeof(*mesh_data->vertices));
3249  hr = E_OUTOFMEMORY;
3250  goto end;
3251  }
3252 
3253  in_ptr = data + sizeof(DWORD);
3255  in_ptr += mesh_data->num_vertices * sizeof(D3DXVECTOR3) + sizeof(DWORD);
3256 
3257  index_out_ptr = mesh_data->indices;
3258  for (i = 0; i < mesh_data->num_poly_faces; i++)
3259  {
3260  DWORD count;
3261 
3262  count = *(DWORD*)in_ptr;
3263  in_ptr += sizeof(DWORD);
3265 
3266  while (count--) {
3267  *index_out_ptr++ = *(DWORD*)in_ptr;
3268  in_ptr += sizeof(DWORD);
3269  }
3270  }
3271 
3272  hr = filedata->lpVtbl->GetChildren(filedata, &nb_children);
3273  if (FAILED(hr))
3274  goto end;
3275 
3276  for (i = 0; i < nb_children; i++)
3277  {
3278  hr = filedata->lpVtbl->GetChild(filedata, i, &child);
3279  if (FAILED(hr))
3280  goto end;
3281  hr = child->lpVtbl->GetType(child, &type);
3282  if (FAILED(hr))
3283  goto end;
3284 
3285  if (IsEqualGUID(&type, &TID_D3DRMMeshNormals)) {
3287  } else if (IsEqualGUID(&type, &TID_D3DRMMeshVertexColors)) {
3289  } else if (IsEqualGUID(&type, &TID_D3DRMMeshTextureCoords)) {
3291  } else if (IsEqualGUID(&type, &TID_D3DRMMeshMaterialList) &&
3292  (provide_flags & PROVIDE_MATERIALS))
3293  {
3295  } else if (provide_flags & PROVIDE_SKININFO) {
3296  if (IsEqualGUID(&type, &DXFILEOBJ_XSkinMeshHeader)) {
3297  if (mesh_data->skin_info) {
3298  WARN("Skin mesh header already encountered\n");
3299  hr = E_FAIL;
3300  goto end;
3301  }
3303  if (FAILED(hr))
3304  goto end;
3305  } else if (IsEqualGUID(&type, &DXFILEOBJ_SkinWeights)) {
3306  if (!mesh_data->skin_info) {
3307  WARN("Skin weights found but skin mesh header not encountered yet\n");
3308  hr = E_FAIL;
3309  goto end;
3310  }
3311  hr = parse_skin_mesh_info(child, mesh_data, nb_skin_weights_info);
3312  if (FAILED(hr))
3313  goto end;
3314  nb_skin_weights_info++;
3315  }
3316  }
3317  if (FAILED(hr))
3318  goto end;
3319 
3320  IUnknown_Release(child);
3321  child = NULL;
3322  }
3323 
3324  if (mesh_data->skin_info && (nb_skin_weights_info != mesh_data->nb_bones)) {
3325  WARN("Mismatch between nb skin weights info %u encountered and nb bones %u from skin mesh header\n",
3326  nb_skin_weights_info, mesh_data->nb_bones);
3327  hr = E_FAIL;
3328  goto end;
3329  }
3330 
3331  if ((provide_flags & PROVIDE_SKININFO) && !mesh_data->skin_info)
3332  {
3334  if (FAILED(hr))
3335  goto end;
3336  }
3337 
3338  hr = D3D_OK;
3339 
3340 end:
3341  if (child)
3342  IUnknown_Release(child);
3343  filedata->lpVtbl->Unlock(filedata);
3344  return hr;
3345 }
3346 
3347 static HRESULT generate_effects(ID3DXBuffer *materials, DWORD num_materials,
3348  ID3DXBuffer **effects)
3349 {
3350  HRESULT hr;
3351  D3DXEFFECTINSTANCE *effect_ptr;
3352  BYTE *out_ptr;
3353  const D3DXMATERIAL *material_ptr = ID3DXBuffer_GetBufferPointer(materials);
3354  static const struct {
3355  const char *param_name;
3356  DWORD name_size;
3357  DWORD num_bytes;
3358  DWORD value_offset;
3359  } material_effects[] = {
3360 #define EFFECT_TABLE_ENTRY(str, field) \
3361  {str, sizeof(str), sizeof(material_ptr->MatD3D.field), offsetof(D3DXMATERIAL, MatD3D.field)}
3362  EFFECT_TABLE_ENTRY("Diffuse", Diffuse),
3363  EFFECT_TABLE_ENTRY("Power", Power),
3364  EFFECT_TABLE_ENTRY("Specular", Specular),
3365  EFFECT_TABLE_ENTRY("Emissive", Emissive),
3366  EFFECT_TABLE_ENTRY("Ambient", Ambient),
3367 #undef EFFECT_TABLE_ENTRY
3368  };
3369  static const char texture_paramname[] = "Texture0@Name";
3371  DWORD i;
3372 
3373  /* effects buffer layout:
3374  *
3375  * D3DXEFFECTINSTANCE effects[num_materials];
3376  * for (effect in effects)
3377  * {
3378  * D3DXEFFECTDEFAULT defaults[effect.NumDefaults];
3379  * for (default in defaults)
3380  * {
3381  * *default.pParamName;
3382  * *default.pValue;
3383  * }
3384  * }
3385  */
3386  buffer_size = sizeof(D3DXEFFECTINSTANCE);
3387  buffer_size += sizeof(D3DXEFFECTDEFAULT) * ARRAY_SIZE(material_effects);
3388  for (i = 0; i < ARRAY_SIZE(material_effects); i++) {
3389  buffer_size += material_effects[i].name_size;
3390  buffer_size += material_effects[i].num_bytes;
3391  }
3392  buffer_size *= num_materials;
3393  for (i = 0; i < num_materials; i++) {
3394  if (material_ptr[i].pTextureFilename) {
3395  buffer_size += sizeof(D3DXEFFECTDEFAULT);
3396  buffer_size += sizeof(texture_paramname);
3397  buffer_size += strlen(material_ptr[i].pTextureFilename) + 1;
3398  }
3399  }
3400 
3401  hr = D3DXCreateBuffer(buffer_size, effects);
3402  if (FAILED(hr)) return hr;
3403  effect_ptr = ID3DXBuffer_GetBufferPointer(*effects);
3404  out_ptr = (BYTE*)(effect_ptr + num_materials);
3405 
3406  for (i = 0; i < num_materials; i++)
3407  {
3408  DWORD j;
3410 
3411  effect_ptr->pDefaults = defaults;
3412  effect_ptr->NumDefaults = material_ptr->pTextureFilename ? 6 : 5;
3413  out_ptr = (BYTE*)(effect_ptr->pDefaults + effect_ptr->NumDefaults);
3414 
3415  for (j = 0; j < ARRAY_SIZE(material_effects); j++)
3416  {
3417  defaults->pParamName = (char *)out_ptr;
3418  strcpy(defaults->pParamName, material_effects[j].param_name);
3419  defaults->pValue = defaults->pParamName + material_effects[j].name_size;
3420  defaults->Type = D3DXEDT_FLOATS;
3421  defaults->NumBytes = material_effects[j].num_bytes;
3422  memcpy(defaults->pValue, (BYTE*)material_ptr + material_effects[j].value_offset, defaults->NumBytes);
3423  out_ptr = (BYTE*)defaults->pValue + defaults->NumBytes;
3424  defaults++;
3425  }
3426 
3427  if (material_ptr->pTextureFilename)
3428  {
3429  defaults->pParamName = (char *)out_ptr;
3430  strcpy(defaults->pParamName, texture_paramname);
3431  defaults->pValue = defaults->pParamName + sizeof(texture_paramname);
3432  defaults->Type = D3DXEDT_STRING;
3433  defaults->NumBytes = strlen(material_ptr->pTextureFilename) + 1;
3434  strcpy(defaults->pValue, material_ptr->pTextureFilename);
3435  out_ptr = (BYTE*)defaults->pValue + defaults->NumBytes;
3436  }
3437  material_ptr++;
3438  effect_ptr++;
3439  }
3440  assert(out_ptr - (BYTE*)ID3DXBuffer_GetBufferPointer(*effects) == buffer_size);
3441 
3442  return D3D_OK;
3443 }
3444 
3445 HRESULT WINAPI D3DXLoadSkinMeshFromXof(struct ID3DXFileData *filedata, DWORD options,
3446  struct IDirect3DDevice9 *device, struct ID3DXBuffer **adjacency_out, struct ID3DXBuffer **materials_out,
3447  struct ID3DXBuffer **effects_out, DWORD *num_materials_out, struct ID3DXSkinInfo **skin_info_out,
3448  struct ID3DXMesh **mesh_out)
3449 {
3450  HRESULT hr;
3451  DWORD *index_in_ptr;
3452  struct mesh_data mesh_data;
3453  DWORD total_vertices;
3454  ID3DXMesh *d3dxmesh = NULL;
3455  ID3DXBuffer *adjacency = NULL;
3457  ID3DXBuffer *effects = NULL;
3458  struct vertex_duplication {
3459  DWORD normal_index;
3460  struct list entry;
3461  } *duplications = NULL;
3462  DWORD i;
3463  void *vertices = NULL;
3464  void *indices = NULL;
3465  BYTE *out_ptr;
3466  DWORD provide_flags = 0;
3467 
3468  TRACE("(%p, %x, %p, %p, %p, %p, %p, %p, %p)\n", filedata, options, device, adjacency_out, materials_out,
3469  effects_out, num_materials_out, skin_info_out, mesh_out);
3470 
3471  ZeroMemory(&mesh_data, sizeof(mesh_data));
3472 
3473  if (num_materials_out || materials_out || effects_out)
3474  provide_flags |= PROVIDE_MATERIALS;
3475  if (skin_info_out)
3476  provide_flags |= PROVIDE_SKININFO;
3477 
3478  hr = parse_mesh(filedata, &mesh_data, provide_flags);
3479  if (FAILED(hr)) goto cleanup;
3480 
3481  total_vertices = mesh_data.num_vertices;
3482  if (mesh_data.fvf & D3DFVF_NORMAL) {
3483  /* duplicate vertices with multiple normals */
3484  DWORD num_face_indices = mesh_data.num_poly_faces * 2 + mesh_data.num_tri_faces;
3485  duplications = HeapAlloc(GetProcessHeap(), 0, (mesh_data.num_vertices + num_face_indices) * sizeof(*duplications));
3486  if (!duplications) {
3487  hr = E_OUTOFMEMORY;
3488  goto cleanup;
3489  }
3490  for (i = 0; i < total_vertices; i++)
3491  {
3492  duplications[i].normal_index = -1;
3493  list_init(&duplications[i].entry);
3494  }
3495  for (i = 0; i < num_face_indices; i++) {
3497  DWORD normal_index = mesh_data.normal_indices[i];
3498  struct vertex_duplication *dup_ptr = &duplications[vertex_index];
3499 
3500  if (dup_ptr->normal_index == -1) {
3501  dup_ptr->normal_index = normal_index;
3502  } else {
3503  D3DXVECTOR3 *new_normal = &mesh_data.normals[normal_index];
3504  struct list *dup_list = &dup_ptr->entry;
3505  while (TRUE) {
3506  D3DXVECTOR3 *cur_normal = &mesh_data.normals[dup_ptr->normal_index];
3507  if (new_normal->x == cur_normal->x &&
3508  new_normal->y == cur_normal->y &&
3509  new_normal->z == cur_normal->z)
3510  {
3511  mesh_data.indices[i] = dup_ptr - duplications;
3512  break;
3513  } else if (!list_next(dup_list, &dup_ptr->entry)) {
3514  dup_ptr = &duplications[total_vertices++];
3515  dup_ptr->normal_index = normal_index;
3516  list_add_tail(dup_list, &dup_ptr->entry);
3517  mesh_data.indices[i] = dup_ptr - duplications;
3518  break;
3519  } else {
3520  dup_ptr = LIST_ENTRY(list_next(dup_list, &dup_ptr->entry),
3521  struct vertex_duplication, entry);
3522  }
3523  }
3524  }
3525  }
3526  }
3527 
3528  hr = D3DXCreateMeshFVF(mesh_data.num_tri_faces, total_vertices, options, mesh_data.fvf, device, &d3dxmesh);
3529  if (FAILED(hr)) goto cleanup;
3530 
3531  hr = d3dxmesh->lpVtbl->LockVertexBuffer(d3dxmesh, 0, &vertices);
3532  if (FAILED(hr)) goto cleanup;
3533 
3534  out_ptr = vertices;
3535  for (i = 0; i < mesh_data.num_vertices; i++) {
3536  *(D3DXVECTOR3*)out_ptr = mesh_data.vertices[i];
3537  out_ptr += sizeof(D3DXVECTOR3);
3538  if (mesh_data.fvf & D3DFVF_NORMAL) {
3539  if (duplications[i].normal_index == -1)
3540  ZeroMemory(out_ptr, sizeof(D3DXVECTOR3));
3541  else
3542  *(D3DXVECTOR3*)out_ptr = mesh_data.normals[duplications[i].normal_index];
3543  out_ptr += sizeof(D3DXVECTOR3);
3544  }
3545  if (mesh_data.fvf & D3DFVF_DIFFUSE) {
3546  *(DWORD*)out_ptr = mesh_data.vertex_colors[i];
3547  out_ptr += sizeof(DWORD);
3548  }
3549  if (mesh_data.fvf & D3DFVF_TEX1) {
3550  *(D3DXVECTOR2*)out_ptr = mesh_data.tex_coords[i];
3551  out_ptr += sizeof(D3DXVECTOR2);
3552  }
3553  }
3554  if (mesh_data.fvf & D3DFVF_NORMAL) {
3555  DWORD vertex_size = D3DXGetFVFVertexSize(mesh_data.fvf);
3556  out_ptr = vertices;
3557  for (i = 0; i < mesh_data.num_vertices; i++) {
3558  struct vertex_duplication *dup_ptr;
3559  LIST_FOR_EACH_ENTRY(dup_ptr, &duplications[i].entry, struct vertex_duplication, entry)
3560  {
3561  int j = dup_ptr - duplications;
3562  BYTE *dest_vertex = (BYTE*)vertices + j * vertex_size;
3563 
3564  memcpy(dest_vertex, out_ptr, vertex_size);
3565  dest_vertex += sizeof(D3DXVECTOR3);
3566  *(D3DXVECTOR3*)dest_vertex = mesh_data.normals[dup_ptr->normal_index];
3567  }
3568  out_ptr += vertex_size;
3569  }
3570  }
3571  d3dxmesh->lpVtbl->UnlockVertexBuffer(d3dxmesh);
3572 
3573  hr = d3dxmesh->lpVtbl->LockIndexBuffer(d3dxmesh, 0, &indices);
3574  if (FAILED(hr)) goto cleanup;
3575 
3576  index_in_ptr = mesh_data.indices;
3577 #define FILL_INDEX_BUFFER(indices_var) \
3578  for (i = 0; i < mesh_data.num_poly_faces; i++) \
3579  { \
3580  DWORD count = mesh_data.num_tri_per_face[i]; \
3581  WORD first_index = *index_in_ptr++; \
3582  while (count--) { \
3583  *indices_var++ = first_index; \
3584  *indices_var++ = *index_in_ptr; \
3585  index_in_ptr++; \
3586  *indices_var++ = *index_in_ptr; \
3587  } \
3588  index_in_ptr++; \
3589  }
3590  if (options & D3DXMESH_32BIT) {
3591  DWORD *dword_indices = indices;
3592  FILL_INDEX_BUFFER(dword_indices)
3593  } else {
3594  WORD *word_indices = indices;
3595  FILL_INDEX_BUFFER(word_indices)
3596  }
3597 #undef FILL_INDEX_BUFFER
3598  d3dxmesh->lpVtbl->UnlockIndexBuffer(d3dxmesh);
3599 
3601  DWORD *attrib_buffer = NULL;
3602  hr = d3dxmesh->lpVtbl->LockAttributeBuffer(d3dxmesh, 0, &attrib_buffer);
3603  if (FAILED(hr)) goto cleanup;
3604  for (i = 0; i < mesh_data.num_poly_faces; i++)
3605  {
3607  while (count--)
3608  *attrib_buffer++ = mesh_data.material_indices[i];
3609  }
3610  d3dxmesh->lpVtbl->UnlockAttributeBuffer(d3dxmesh);
3611 
3612  hr = d3dxmesh->lpVtbl->OptimizeInplace(d3dxmesh,
3614  NULL, NULL, NULL, NULL);
3615  if (FAILED(hr)) goto cleanup;
3616  }
3617 
3618  if (mesh_data.num_materials && (materials_out || effects_out)) {
3620  char *strings_out_ptr;
3621  D3DXMATERIAL *materials_ptr;
3622 
3623  for (i = 0; i < mesh_data.num_materials; i++) {
3626  }
3627 
3628  hr = D3DXCreateBuffer(buffer_size, &materials);
3629  if (FAILED(hr)) goto cleanup;
3630 
3631  materials_ptr = ID3DXBuffer_GetBufferPointer(materials);
3632  memcpy(materials_ptr, mesh_data.materials, mesh_data.num_materials * sizeof(D3DXMATERIAL));
3633  strings_out_ptr = (char*)(materials_ptr + mesh_data.num_materials);
3634  for (i = 0; i < mesh_data.num_materials; i++) {
3635  if (materials_ptr[i].pTextureFilename) {
3636  strcpy(strings_out_ptr, mesh_data.materials[i].pTextureFilename);
3637  materials_ptr[i].pTextureFilename = strings_out_ptr;
3638  strings_out_ptr += strlen(mesh_data.materials[i].pTextureFilename) + 1;
3639  }
3640  }
3641  }
3642 
3643  if (mesh_data.num_materials && effects_out) {
3644  hr = generate_effects(materials, mesh_data.num_materials, &effects);
3645  if (FAILED(hr)) goto cleanup;
3646 
3647  if (!materials_out) {
3648  ID3DXBuffer_Release(materials);
3649  materials = NULL;
3650  }
3651  }
3652 
3653  if (adjacency_out) {
3654  hr = D3DXCreateBuffer(mesh_data.num_tri_faces * 3 * sizeof(DWORD), &adjacency);
3655  if (FAILED(hr)) goto cleanup;
3656  hr = d3dxmesh->lpVtbl->GenerateAdjacency(d3dxmesh, 0.0f, ID3DXBuffer_GetBufferPointer(adjacency));
3657  if (FAILED(hr)) goto cleanup;
3658  }
3659 
3660  *mesh_out = d3dxmesh;
3661  if (adjacency_out) *adjacency_out = adjacency;
3662  if (num_materials_out) *num_materials_out = mesh_data.num_materials;
3663  if (materials_out) *materials_out = materials;
3664  if (effects_out) *effects_out = effects;
3665  if (skin_info_out) *skin_info_out = mesh_data.skin_info;
3666 
3667  hr = D3D_OK;
3668 cleanup:
3669  if (FAILED(hr)) {
3670  if (d3dxmesh) IUnknown_Release(d3dxmesh);
3671  if (adjacency) ID3DXBuffer_Release(adjacency);
3672  if (materials) ID3DXBuffer_Release(materials);
3673  if (effects) ID3DXBuffer_Release(effects);
3675  if (skin_info_out) *skin_info_out = NULL;
3676  }
3685  HeapFree(GetProcessHeap(), 0, duplications);
3686  return hr;
3687 }
3688 
3689 HRESULT WINAPI D3DXLoadMeshHierarchyFromXA(const char *filename, DWORD options, struct IDirect3DDevice9 *device,
3690  struct ID3DXAllocateHierarchy *alloc_hier, struct ID3DXLoadUserData *load_user_data,
3691  D3DXFRAME **frame_hierarchy, struct ID3DXAnimationController **anim_controller)
3692 {
3693  WCHAR *filenameW;
3694  HRESULT hr;
3695  int len;
3696 
3697  TRACE("filename %s, options %#x, device %p, alloc_hier %p, "
3698  "load_user_data %p, frame_hierarchy %p, anim_controller %p.\n",
3699  debugstr_a(filename), options, device, alloc_hier,
3700  load_user_data, frame_hierarchy, anim_controller);
3701 
3702  if (!filename)
3703  return D3DERR_INVALIDCALL;
3704 
3705  len = MultiByteToWideChar(CP_ACP, 0, filename, -1, NULL, 0);
3706  filenameW = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
3707  if (!filenameW) return E_OUTOFMEMORY;
3709 
3711  alloc_hier, load_user_data, frame_hierarchy, anim_controller);
3713 
3714  return hr;
3715 }
3716 
3718  struct ID3DXAllocateHierarchy *alloc_hier, struct ID3DXLoadUserData *load_user_data,
3719  D3DXFRAME **frame_hierarchy, struct ID3DXAnimationController **anim_controller)
3720 {
3721  void *buffer;
3722  HRESULT hr;
3723  DWORD size;
3724 
3725  TRACE("filename %s, options %#x, device %p, alloc_hier %p, "
3726  "load_user_data %p, frame_hierarchy %p, anim_controller %p.\n",
3727  debugstr_w(filename), options, device, alloc_hier,
3728  load_user_data, frame_hierarchy, anim_controller);
3729 
3730  if (!filename)
3731  return D3DERR_INVALIDCALL;
3732 
3734  if (FAILED(hr))
3735  return D3DXERR_INVALIDDATA;
3736 
3738  alloc_hier, load_user_data, frame_hierarchy, anim_controller);
3739 
3741 
3742  return hr;
3743 }
3744 
3745 static HRESULT filedata_get_name(ID3DXFileData *filedata, char **name)
3746 {
3747  HRESULT hr;
3748  SIZE_T name_len;
3749 
3750  hr = filedata->lpVtbl->GetName(filedata, NULL, &name_len);
3751  if (FAILED(hr)) return hr;
3752 
3753  if (!name_len)
3754  name_len++;
3755  *name = HeapAlloc(GetProcessHeap(), 0, name_len);
3756  if (!*name) return E_OUTOFMEMORY;
3757 
3758  hr = filedata->lpVtbl->GetName(filedata, *name, &name_len);
3759  if (FAILED(hr))
3760  HeapFree(GetProcessHeap(), 0, *name);
3761  else if (!name_len)
3762  (*name)[0] = 0;
3763 
3764  return hr;
3765 }
3766 
3767 static HRESULT load_mesh_container(struct ID3DXFileData *filedata, DWORD options, struct IDirect3DDevice9 *device,
3768  struct ID3DXAllocateHierarchy *alloc_hier, D3DXMESHCONTAINER **mesh_container)
3769 {
3770  HRESULT hr;
3771  ID3DXBuffer *adjacency = NULL;
3772  ID3DXBuffer *materials = NULL;
3773  ID3DXBuffer *effects = NULL;
3774  ID3DXSkinInfo *skin_info = NULL;
3776  DWORD num_materials = 0;
3777  char *name = NULL;
3778 
3780  mesh_data.u.pMesh = NULL;
3781 
3783  &adjacency, &materials, &effects, &num_materials,
3784  &skin_info, &mesh_data.u.pMesh);
3785  if (FAILED(hr)) return hr;
3786 
3787  hr = filedata_get_name(filedata, &name);
3788  if (FAILED(hr)) goto cleanup;
3789 
3790  hr = alloc_hier->lpVtbl->CreateMeshContainer(alloc_hier, name, &mesh_data,
3791  materials ? ID3DXBuffer_GetBufferPointer(materials) : NULL,
3792  effects ? ID3DXBuffer_GetBufferPointer(effects) : NULL,
3793  num_materials,
3794  adjacency ? ID3DXBuffer_GetBufferPointer(adjacency) : NULL,
3795  skin_info, mesh_container);
3796 
3797 cleanup:
3798  if (materials) ID3DXBuffer_Release(materials);
3799  if (effects) ID3DXBuffer_Release(effects);
3800  if (adjacency) ID3DXBuffer_Release(adjacency);
3801  if (skin_info) IUnknown_Release(skin_info);
3802  if (mesh_data.u.pMesh) IUnknown_Release(mesh_data.u.pMesh);
3803  HeapFree(GetProcessHeap(), 0, name);
3804  return hr;
3805 }
3806 
3807 static HRESULT parse_transform_matrix(ID3DXFileData *filedata, D3DXMATRIX *transform)
3808 {
3809  HRESULT hr;
3810  SIZE_T data_size;
3811  const BYTE *data;
3812 
3813  /* template Matrix4x4 {
3814  * array FLOAT matrix[16];
3815  * }
3816  * template FrameTransformMatrix {
3817  * Matrix4x4 frameMatrix;
3818  * }
3819  */
3820 
3821  hr = filedata->lpVtbl->Lock(filedata, &data_size, (const void**)&data);
3822  if (FAILED(hr)) return hr;
3823 
3824  if (data_size != sizeof(D3DXMATRIX)) {
3825  WARN("incorrect data size (%ld bytes)\n", data_size);
3826  filedata->lpVtbl->Unlock(filedata);
3827  return E_FAIL;
3828  }
3829 
3830  memcpy(transform, data, sizeof(D3DXMATRIX));
3831 
3832  filedata->lpVtbl->Unlock(filedata);
3833  return D3D_OK;
3834 }
3835 
3836 static HRESULT load_frame(struct ID3DXFileData *filedata, DWORD options, struct IDirect3DDevice9 *device,
3837  struct ID3DXAllocateHierarchy *alloc_hier, D3DXFRAME **frame_out)
3838 {
3839  HRESULT hr;
3840  GUID type;
3841  ID3DXFileData *child;
3842  char *name = NULL;
3843  D3DXFRAME *frame = NULL;
3844  D3DXMESHCONTAINER **next_container;
3845  D3DXFRAME **next_child;
3846  SIZE_T i, nb_children;
3847 
3848  hr = filedata_get_name(filedata, &name);
3849  if (FAILED(hr)) return hr;
3850 
3851  hr = alloc_hier->lpVtbl->CreateFrame(alloc_hier, name, frame_out);
3852  HeapFree(GetProcessHeap(), 0, name);
3853  if (FAILED(hr)) return E_FAIL;
3854 
3855  frame = *frame_out;
3856  D3DXMatrixIdentity(&frame->TransformationMatrix);
3857  next_child = &frame->pFrameFirstChild;
3858  next_container = &frame->pMeshContainer;
3859 
3860  hr = filedata->lpVtbl->GetChildren(filedata, &nb_children);
3861  if (FAILED(hr))
3862  return hr;
3863 
3864  for (i = 0; i < nb_children; i++)
3865  {
3866  hr = filedata->lpVtbl->GetChild(filedata, i, &child);
3867  if (FAILED(hr))
3868  return hr;
3869  hr = child->lpVtbl->GetType(child, &type);
3870  if (FAILED(hr))
3871  goto err;
3872 
3873  if (IsEqualGUID(&type, &TID_D3DRMMesh)) {
3874  hr = load_mesh_container(child, options, device, alloc_hier, next_container);
3875  if (SUCCEEDED(hr))
3876  next_container = &(*next_container)->pNextMeshContainer;
3877  } else if (IsEqualGUID(&type, &TID_D3DRMFrameTransformMatrix)) {
3879  } else if (IsEqualGUID(&type, &TID_D3DRMFrame)) {
3880  hr = load_frame(child, options, device, alloc_hier, next_child);
3881  if (SUCCEEDED(hr))
3882  next_child = &(*next_child)->pFrameSibling;
3883  }
3884  if (FAILED(hr))
3885  goto err;
3886 
3887  IUnknown_Release(child);
3888  }
3889  return D3D_OK;
3890 
3891 err:
3892  IUnknown_Release(child);
3893  return hr;
3894 }
3895 
3897  struct IDirect3DDevice9 *device, struct ID3DXAllocateHierarchy *alloc_hier,
3898  struct ID3DXLoadUserData *load_user_data, D3DXFRAME **frame_hierarchy,
3899  struct ID3DXAnimationController **anim_controller)
3900 {
3901  HRESULT hr;
3902  ID3DXFile *d3dxfile = NULL;
3903  ID3DXFileEnumObject *enumobj = NULL;
3904  ID3DXFileData *filedata = NULL;
3906  D3DXFRAME *first_frame = NULL;
3907  D3DXFRAME **next_frame = &first_frame;
3908  SIZE_T i, nb_children;
3909  GUID guid;
3910 
3911  TRACE("(%p, %u, %x, %p, %p, %p, %p, %p)\n", memory, memory_size, options,
3912  device, alloc_hier, load_user_data, frame_hierarchy, anim_controller);
3913 
3914  if (!memory || !memory_size || !device || !frame_hierarchy || !alloc_hier)
3915  return D3DERR_INVALIDCALL;
3916  if (load_user_data)
3917  {
3918  FIXME("Loading user data not implemented.\n");
3919  return E_NOTIMPL;
3920  }
3921 
3922  hr = D3DXFileCreate(&d3dxfile);
3923  if (FAILED(hr)) goto cleanup;
3924 
3925  hr = d3dxfile->lpVtbl->RegisterTemplates(d3dxfile, D3DRM_XTEMPLATES, D3DRM_XTEMPLATE_BYTES);
3926  if (FAILED(hr)) goto cleanup;
3927 
3928  source.lpMemory = (void*)memory;
3929  source.dSize = memory_size;
3930  hr = d3dxfile->lpVtbl->CreateEnumObject(d3dxfile, &source, D3DXF_FILELOAD_FROMMEMORY, &enumobj);
3931  if (FAILED(hr)) goto cleanup;
3932 
3933  hr = enumobj->lpVtbl->GetChildren(enumobj, &nb_children);
3934  if (FAILED(hr))
3935  goto cleanup;
3936 
3937  for (i = 0; i < nb_children; i++)
3938  {
3939  hr = enumobj->lpVtbl->GetChild(enumobj, i, &filedata);
3940  if (FAILED(hr))
3941  goto cleanup;
3942 
3943  hr = filedata->lpVtbl->GetType(filedata, &guid);
3944  if (SUCCEEDED(hr)) {
3945  if (IsEqualGUID(&guid, &TID_D3DRMMesh)) {
3946  hr = alloc_hier->lpVtbl->CreateFrame(alloc_hier, NULL, next_frame);
3947  if (FAILED(hr)) {
3948  hr = E_FAIL;
3949  goto cleanup;
3950  }
3951 
3952  D3DXMatrixIdentity(&(*next_frame)->TransformationMatrix);
3953 
3954  hr = load_mesh_container(filedata, options, device, alloc_hier, &(*next_frame)->pMeshContainer);
3955  if (FAILED(hr)) goto cleanup;
3956  } else if (IsEqualGUID(&guid, &TID_D3DRMFrame)) {
3957  hr = load_frame(filedata, options, device, alloc_hier, next_frame);
3958  if (FAILED(hr)) goto cleanup;
3959  }
3960  while (*next_frame)
3961  next_frame = &(*next_frame)->pFrameSibling;
3962  }
3963 
3964  filedata->lpVtbl->Release(filedata);
3965  filedata = NULL;
3966  if (FAILED(hr))
3967  goto cleanup;
3968  }
3969 
3970  if (!first_frame) {
3971  hr = E_FAIL;
3972  } else if (first_frame->pFrameSibling) {
3973  D3DXFRAME *root_frame = NULL;
3974  hr = alloc_hier->lpVtbl->CreateFrame(alloc_hier, NULL, &root_frame);
3975  if (FAILED(hr)) {
3976  hr = E_FAIL;
3977  goto cleanup;
3978  }
3979  D3DXMatrixIdentity(&root_frame->TransformationMatrix);
3980  root_frame->pFrameFirstChild = first_frame;
3981  *frame_hierarchy = root_frame;
3982  hr = D3D_OK;
3983  } else {
3984  *frame_hierarchy = first_frame;
3985  hr = D3D_OK;
3986  }
3987 
3988  if (anim_controller)
3989  {
3990  *anim_controller = NULL;
3991  FIXME("Animation controller creation not implemented.\n");
3992  }
3993 
3994 cleanup:
3995  if (FAILED(hr) && first_frame) D3DXFrameDestroy(first_frame, alloc_hier);
3996  if (filedata) filedata->lpVtbl->Release(filedata);
3997  if (enumobj) enumobj->lpVtbl->Release(enumobj);
3998  if (d3dxfile) d3dxfile->lpVtbl->Release(d3dxfile);
3999  return hr;
4000 }
4001 
4002 HRESULT WINAPI D3DXCleanMesh(D3DXCLEANTYPE clean_type, ID3DXMesh *mesh_in, const DWORD *adjacency_in,
4003  ID3DXMesh **mesh_out, DWORD *adjacency_out, ID3DXBuffer **errors_and_warnings)
4004 {
4005  FIXME("(%u, %p, %p, %p, %p, %p)\n", clean_type, mesh_in, adjacency_in, mesh_out, adjacency_out, errors_and_warnings);
4006 
4007  return E_NOTIMPL;
4008 }
4009 
4010 HRESULT WINAPI D3DXFrameDestroy(D3DXFRAME *frame, ID3DXAllocateHierarchy *alloc_hier)
4011 {
4012  HRESULT hr;
4013  BOOL last = FALSE;
4014 
4015  TRACE("(%p, %p)\n", frame, alloc_hier);
4016 
4017  if (!frame || !alloc_hier)
4018  return D3DERR_INVALIDCALL;
4019 
4020  while (!last) {
4022  D3DXFRAME *current_frame;
4023 
4024  if (frame->pFrameSibling) {
4025  current_frame = frame->pFrameSibling;
4026  frame->pFrameSibling = current_frame->pFrameSibling;
4027  current_frame->pFrameSibling = NULL;
4028  } else {
4029  current_frame = frame;
4030  last = TRUE;
4031  }
4032 
4033  if (current_frame->pFrameFirstChild) {
4034  hr = D3DXFrameDestroy(current_frame->pFrameFirstChild, alloc_hier);
4035  if (FAILED(hr)) return hr;
4036  current_frame->pFrameFirstChild = NULL;
4037  }
4038 
4039  container = current_frame->pMeshContainer;
4040  while (container) {
4041  D3DXMESHCONTAINER *next_container = container->pNextMeshContainer;
4042  hr = alloc_hier->lpVtbl->DestroyMeshContainer(alloc_hier, container);
4043  if (FAILED(hr)) return hr;
4044  container = next_container;
4045  }
4046  hr = alloc_hier->lpVtbl->DestroyFrame(alloc_hier, current_frame);
4047  if (FAILED(hr)) return hr;
4048  }
4049  return D3D_OK;
4050 }
4051 
4052 HRESULT WINAPI D3DXLoadMeshFromXA(const char *filename, DWORD options, struct IDirect3DDevice9 *device,
4053  struct ID3DXBuffer **adjacency, struct ID3DXBuffer **materials, struct ID3DXBuffer **effect_instances,
4054  DWORD *num_materials, struct ID3DXMesh **mesh)
4055 {
4056  WCHAR *filenameW;
4057  HRESULT hr;
4058  int len;
4059 
4060  TRACE("filename %