Dear All:

I have worked hard today with Henri on my D3DXCreateSphere patch and
appreciate all his help (and all of your help and patience with me and
my inability to use email programs and IRC programs ;) ).

I am now moving on to implementing D3DXCreateMeshFVF and ID3DXMesh so
that we can actually get D3DXCreateSphere, Box, and Cylinder going (and
teapot... or a stub).

I attached my current versions. I expect a lot of comments/changes:
* patch 1 - same patch as I submitted with Henri's blessing. Hopefully
this will go in tomorrow. This is just for reference.
* patch 2 - add stub for D3DXCreateMeshFVF and basic test. I'm assuming
this should go in too if I sent it by itself tomorrow.
* patch 3 - enumeration. Also fairly simple.
* patch 4 - complete D3DXCreateMeshFVF test. Any criticisms? At the very
least, I would like to send patches 2-4 tomorrow, provided patch 1 goes
in (I'm assuming so per the Henri blessing part ... fingers crossed)

* patch 5 - this is clearly not complete and I hope to get some good
feedback on this patch. I've already gotten some feedback from Tony
Wasserka (thank you). Specifically, I expect per his comment I must
reimplement using declarators
http://bugs.winehq.org/show_bug.cgi?id=22918#c39

This means that I must first get a patch committed that implements
D3DXDeclaratorFromFVF which is currently a stub in git.

Additionally, I realize Tony has, in his private repo, some more work on
ID3DXMesh that is somewhat more complete I believe than mine:
http://repo.or.cz/w/wine/d3dx9TW.git/blob/Merge:/dlls/d3dx9_36/basemesh.c

I have not yet given this detailed scrutiny as, for me, it is easier to
start with my own code, and then work from there, potentially
integrating Tony's code (thank you) based on your feedback about what is
necessary, etc.

However, I have currently limited my code to the features that are
necessary for ID3DXMesh for D3DXCreateSphere, Cylinder, and Box, as the
aim was to fix this bug:
http://bugs.winehq.org/show_bug.cgi?id=22918

In any case, I appreciate your attention to these patches and good
feedback.

I am specifically quite interested in feedback to patches 2-4, as I'd
like to (ideally) submit versions of these that go through tomorrow.

However, as the bulk of my work tomorrow (and probably for quite a bit
afterwards) I expect on patch 5, I'd appreciate, it possible a list of
things that are necessary to get such a patch committed. (Please correct
me if I am wrong about having to implement D3DXDeclaratorFromFVF.)

I chose to put this hard stuff first.

Then, quite honestly, I believe adding tests for D3DXCreateBox and
Cylinder, as well as the actual implementations, should be fairly simple
compared to this part.

In any case, I eagerly (anxiously) await constructive
criticism/feedback/suggestions.

Thank you
Misha
>From 1c7a93a3e18fcf117eef2b9dd7c0086b51872f52 Mon Sep 17 00:00:00 2001
From: Misha Koshelev <[email protected]>
Date: Mon, 26 Jul 2010 20:16:43 -0500
Subject: d3dx9: Implement D3DXCreateMeshFVF and ID3DXMesh.
To: wine-patches <[email protected]>
Reply-To: wine-devel <[email protected]>

---
 dlls/d3dx9_36/d3dx9_36_private.h |   18 ++
 dlls/d3dx9_36/mesh.c             |  381 +++++++++++++++++++++++++++++++++++++-
 dlls/d3dx9_36/tests/mesh.c       |   18 +-
 3 files changed, 406 insertions(+), 11 deletions(-)

diff --git a/dlls/d3dx9_36/d3dx9_36_private.h b/dlls/d3dx9_36/d3dx9_36_private.h
index 7488307..a1f7544 100644
--- a/dlls/d3dx9_36/d3dx9_36_private.h
+++ b/dlls/d3dx9_36/d3dx9_36_private.h
@@ -95,6 +95,24 @@ typedef struct ID3DXMatrixStackImpl
   D3DXMATRIX *stack;
 } ID3DXMatrixStackImpl;
 
+/* ID3DXMesh */
+typedef struct ID3DXMeshImpl
+{
+    /* IUnknown fields */
+    const ID3DXMeshVtbl *lpVtbl;
+    LONG ref;
+
+    /* ID3DXMesh fields */
+    DWORD numfaces;
+    DWORD numvertices;
+    DWORD options;
+    DWORD FVF;
+
+    IDirect3DDevice9 *device;
+    IDirect3DVertexBuffer9 *vertex_buffer;
+    IDirect3DIndexBuffer9 *index_buffer;
+} ID3DXMeshImpl;
+
 /*ID3DXSprite */
 typedef struct _SPRITE {
     LPDIRECT3DTEXTURE9 texture;
diff --git a/dlls/d3dx9_36/mesh.c b/dlls/d3dx9_36/mesh.c
index 5ff257c..78b62f8 100644
--- a/dlls/d3dx9_36/mesh.c
+++ b/dlls/d3dx9_36/mesh.c
@@ -27,9 +27,307 @@
 #include "wingdi.h"
 #include "d3dx9.h"
 #include "wine/debug.h"
+#include "d3dx9_36_private.h"
 
 WINE_DEFAULT_DEBUG_CHANNEL(d3dx);
 
+/*** IUnknown methods ***/
+static HRESULT WINAPI ID3DXMeshImpl_QueryInterface(ID3DXMesh *iface, REFIID 
riid, LPVOID *object)
+{
+    ID3DXMeshImpl *This = (ID3DXMeshImpl *)iface;
+
+    TRACE("(%p)->(%s, %p)\n", This, debugstr_guid(riid), object);
+
+    if (IsEqualGUID(riid, &IID_IUnknown) ||
+        IsEqualGUID(riid, &IID_ID3DXBaseMesh) ||
+        IsEqualGUID(riid, &IID_ID3DXMesh))
+    {
+        iface->lpVtbl->AddRef(iface);
+        *object = This;
+        return S_OK;
+    }
+
+    WARN("Interface %s not found.\n", debugstr_guid(riid));
+
+    return E_NOINTERFACE;
+}
+
+static ULONG WINAPI ID3DXMeshImpl_AddRef(ID3DXMesh *iface)
+{
+    ID3DXMeshImpl *This = (ID3DXMeshImpl *)iface;
+
+    TRACE("(%p)->(): AddRef from %d\n", This, This->ref);
+
+    return InterlockedIncrement(&This->ref);
+}
+
+static ULONG WINAPI ID3DXMeshImpl_Release(ID3DXMesh *iface)
+{
+    ID3DXMeshImpl *This = (ID3DXMeshImpl *)iface;
+    ULONG ref = InterlockedDecrement(&This->ref);
+
+    TRACE("(%p)->(): Release from %d\n", This, ref + 1);
+
+    if (!ref)
+    {
+        if (This->index_buffer) 
IDirect3DIndexBuffer9_Release(This->index_buffer);
+        if (This->vertex_buffer) 
IDirect3DVertexBuffer9_Release(This->vertex_buffer);
+        if (This->device) IDirect3DDevice9_Release(This->device);
+        HeapFree(GetProcessHeap(), 0, This);
+    }
+
+    return ref;
+}
+
+/*** ID3DXBaseMesh ***/
+static HRESULT WINAPI ID3DXMeshImpl_DrawSubset(ID3DXMesh *iface, DWORD 
attrib_id)
+{
+    ID3DXMeshImpl *This = (ID3DXMeshImpl *)iface;
+    FIXME("(%p)->(%u): stub\n", This, attrib_id);
+    return E_NOTIMPL;
+}
+
+static DWORD WINAPI ID3DXMeshImpl_GetNumFaces(ID3DXMesh *iface)
+{
+    ID3DXMeshImpl *This = (ID3DXMeshImpl *)iface;
+
+    TRACE("(%p)\n", This);
+
+    return This->numfaces;
+}
+
+static DWORD WINAPI ID3DXMeshImpl_GetNumVertices(ID3DXMesh *iface)
+{
+    ID3DXMeshImpl *This = (ID3DXMeshImpl *)iface;
+
+    TRACE("(%p)\n", This);
+
+    return This->numvertices;
+}
+
+static DWORD WINAPI ID3DXMeshImpl_GetFVF(ID3DXMesh *iface)
+{
+    ID3DXMeshImpl *This = (ID3DXMeshImpl *)iface;
+
+    TRACE("(%p)\n", This);
+
+    return This->FVF;
+}
+
+static HRESULT WINAPI ID3DXMeshImpl_GetDeclaration(ID3DXMesh *iface, 
D3DVERTEXELEMENT9 declaration[MAX_FVF_DECL_SIZE])
+{
+    ID3DXMeshImpl *This = (ID3DXMeshImpl *)iface;
+    FIXME("(%p)->(%p): stub\n", This, declaration);
+    return E_NOTIMPL;
+}
+
+static DWORD WINAPI ID3DXMeshImpl_GetNumBytesPerVertex(ID3DXMesh *iface)
+{
+    ID3DXMeshImpl *This = (ID3DXMeshImpl *)iface;
+    FIXME("(%p): stub\n", This);
+    return 0; /* arbitrary since we cannot return E_NOTIMPL */
+}
+
+static DWORD WINAPI ID3DXMeshImpl_GetOptions(ID3DXMesh *iface)
+{
+    ID3DXMeshImpl *This = (ID3DXMeshImpl *)iface;
+
+    TRACE("(%p)\n", This);
+
+    return This->options;
+}
+
+static HRESULT WINAPI ID3DXMeshImpl_GetDevice(ID3DXMesh *iface, 
LPDIRECT3DDEVICE9* device)
+{
+    ID3DXMeshImpl *This = (ID3DXMeshImpl *)iface;
+
+    TRACE("(%p)->(%p): stub\n", This, device);
+
+    if (device == NULL) return D3DERR_INVALIDCALL;
+    *device = This->device;
+    IDirect3DDevice9_AddRef(This->device);
+
+    return D3D_OK;
+}
+
+static HRESULT WINAPI ID3DXMeshImpl_CloneMeshFVF(ID3DXMesh *iface, DWORD 
options, DWORD fvf, LPDIRECT3DDEVICE9 device, LPD3DXMESH* clone_mesh)
+{
+    ID3DXMeshImpl *This = (ID3DXMeshImpl *)iface;
+    FIXME("(%p)->(%u,%u,%p,%p): stub\n", This, options, fvf, device, 
clone_mesh);
+    return E_NOTIMPL;
+}
+
+static HRESULT WINAPI ID3DXMeshImpl_CloneMesh(ID3DXMesh *iface, DWORD options, 
CONST D3DVERTEXELEMENT9* declaration, LPDIRECT3DDEVICE9 device,
+                                              LPD3DXMESH* clone_mesh)
+{
+    ID3DXMeshImpl *This = (ID3DXMeshImpl *)iface;
+    FIXME("(%p)->(%u,%p,%p,%p): stub\n", This, options, declaration, device, 
clone_mesh);
+    return E_NOTIMPL;
+}
+
+static HRESULT WINAPI ID3DXMeshImpl_GetVertexBuffer(ID3DXMesh *iface, 
LPDIRECT3DVERTEXBUFFER9* vertex_buffer)
+{
+    ID3DXMeshImpl *This = (ID3DXMeshImpl *)iface;
+
+    TRACE("(%p)->(%p)\n", This, vertex_buffer);
+
+    if (vertex_buffer == NULL) return D3DERR_INVALIDCALL;
+    *vertex_buffer = This->vertex_buffer;
+    if (This->vertex_buffer) 
IDirect3DVertexBuffer9_AddRef(This->vertex_buffer);
+
+    return D3D_OK;
+}
+
+static HRESULT WINAPI ID3DXMeshImpl_GetIndexBuffer(ID3DXMesh *iface, 
LPDIRECT3DINDEXBUFFER9* index_buffer)
+{
+    ID3DXMeshImpl *This = (ID3DXMeshImpl *)iface;
+
+    TRACE("(%p)->(%p)\n", This, index_buffer);
+
+    if (index_buffer == NULL) return D3DERR_INVALIDCALL;
+    *index_buffer = This->index_buffer;
+    if (This->index_buffer) IDirect3DIndexBuffer9_AddRef(This->index_buffer);
+
+    return D3D_OK;
+}
+
+static HRESULT WINAPI ID3DXMeshImpl_LockVertexBuffer(ID3DXMesh *iface, DWORD 
flags, LPVOID* data)
+{
+    ID3DXMeshImpl *This = (ID3DXMeshImpl *)iface;
+    FIXME("(%p)->(%u,%p): stub\n", This, flags, data);
+    return E_NOTIMPL;
+}
+
+static HRESULT WINAPI ID3DXMeshImpl_UnlockVertexBuffer(ID3DXMesh *iface)
+{
+    ID3DXMeshImpl *This = (ID3DXMeshImpl *)iface;
+    FIXME("(%p): stub\n", This);
+    return E_NOTIMPL;
+}
+
+static HRESULT WINAPI ID3DXMeshImpl_LockIndexBuffer(ID3DXMesh *iface, DWORD 
flags, LPVOID* data)
+{
+    ID3DXMeshImpl *This = (ID3DXMeshImpl *)iface;
+    FIXME("(%p)->(%u,%p): stub\n", This, flags, data);
+    return E_NOTIMPL;
+}
+
+static HRESULT WINAPI ID3DXMeshImpl_UnlockIndexBuffer(ID3DXMesh *iface)
+{
+    ID3DXMeshImpl *This = (ID3DXMeshImpl *)iface;
+    FIXME("(%p): stub\n", This);
+    return E_NOTIMPL;
+}
+
+static HRESULT WINAPI ID3DXMeshImpl_GetAttributeTable(ID3DXMesh *iface, 
D3DXATTRIBUTERANGE* attrib_table, DWORD* attrib_table_size)
+{
+    ID3DXMeshImpl *This = (ID3DXMeshImpl *)iface;
+    FIXME("(%p)->(%p,%p): stub\n", This, attrib_table, attrib_table_size);
+    return E_NOTIMPL;
+}
+
+static HRESULT WINAPI ID3DXMeshImpl_ConvertPointRepsToAdjacency(ID3DXMesh 
*iface, CONST DWORD* point_reps, DWORD* adjacency)
+{
+    ID3DXMeshImpl *This = (ID3DXMeshImpl *)iface;
+    FIXME("(%p)->(%p,%p): stub\n", This, point_reps, adjacency);
+    return E_NOTIMPL;
+}
+
+static HRESULT WINAPI ID3DXMeshImpl_ConvertAdjacencyToPointReps(ID3DXMesh 
*iface, CONST DWORD* adjacency, DWORD* point_reps)
+{
+    ID3DXMeshImpl *This = (ID3DXMeshImpl *)iface;
+    FIXME("(%p)->(%p,%p): stub\n", This, adjacency, point_reps);
+    return E_NOTIMPL;
+}
+
+static HRESULT WINAPI ID3DXMeshImpl_GenerateAdjacency(ID3DXMesh *iface, FLOAT 
epsilon, DWORD* adjacency)
+{
+    ID3DXMeshImpl *This = (ID3DXMeshImpl *)iface;
+    FIXME("(%p)->(%f,%p): stub\n", This, epsilon, adjacency);
+    return E_NOTIMPL;
+}
+
+static HRESULT WINAPI ID3DXMeshImpl_UpdateSemantics(ID3DXMesh *iface, 
D3DVERTEXELEMENT9 declaration[MAX_FVF_DECL_SIZE])
+{
+    ID3DXMeshImpl *This = (ID3DXMeshImpl *)iface;
+    FIXME("(%p)->(%p): stub\n", This, declaration);
+    return E_NOTIMPL;
+}
+
+/*** ID3DXMesh ***/
+static HRESULT WINAPI ID3DXMeshImpl_LockAttributeBuffer(ID3DXMesh *iface, 
DWORD flags, DWORD** data)
+{
+    ID3DXMeshImpl *This = (ID3DXMeshImpl *)iface;
+    FIXME("(%p)->(%u,%p): stub\n", This, flags, data);
+    return E_NOTIMPL;
+}
+
+static HRESULT WINAPI ID3DXMeshImpl_UnlockAttributeBuffer(ID3DXMesh *iface)
+{
+    ID3DXMeshImpl *This = (ID3DXMeshImpl *)iface;
+    FIXME("(%p): stub\n", This);
+    return E_NOTIMPL;
+}
+
+static HRESULT WINAPI ID3DXMeshImpl_Optimize(ID3DXMesh *iface, DWORD flags, 
CONST DWORD* adjacency_in, DWORD* adjacency_out,
+                                             DWORD* face_remap, LPD3DXBUFFER* 
vertex_remap, LPD3DXMESH* opt_mesh)
+{
+    ID3DXMeshImpl *This = (ID3DXMeshImpl *)iface;
+    FIXME("(%p)->(%u,%p,%p,%p,%p,%p): stub\n", This, flags, adjacency_in, 
adjacency_out, face_remap, vertex_remap, opt_mesh);
+    return E_NOTIMPL;
+}
+
+static HRESULT WINAPI ID3DXMeshImpl_OptimizeInplace(ID3DXMesh *iface, DWORD 
flags, CONST DWORD* adjacency_in, DWORD* adjacency_out,
+                                                    DWORD* face_remap, 
LPD3DXBUFFER* vertex_remap)
+{
+    ID3DXMeshImpl *This = (ID3DXMeshImpl *)iface;
+    FIXME("(%p)->(%u,%p,%p,%p,%p): stub\n", This, flags, adjacency_in, 
adjacency_out, face_remap, vertex_remap);
+    return E_NOTIMPL;
+}
+
+static HRESULT WINAPI ID3DXMeshImpl_SetAttributeTable(ID3DXMesh *iface, CONST 
D3DXATTRIBUTERANGE* attrib_table, DWORD attrib_table_size)
+{
+    ID3DXMeshImpl *This = (ID3DXMeshImpl *)iface;
+    FIXME("(%p)->(%p,%u): stub\n", This, attrib_table, attrib_table_size);
+    return E_NOTIMPL;
+}
+
+static const struct ID3DXMeshVtbl D3DXMesh_Vtbl =
+{
+    /*** IUnknown methods ***/
+    ID3DXMeshImpl_QueryInterface,
+    ID3DXMeshImpl_AddRef,
+    ID3DXMeshImpl_Release,
+    /*** ID3DXBaseMesh ***/
+    ID3DXMeshImpl_DrawSubset,
+    ID3DXMeshImpl_GetNumFaces,
+    ID3DXMeshImpl_GetNumVertices,
+    ID3DXMeshImpl_GetFVF,
+    ID3DXMeshImpl_GetDeclaration,
+    ID3DXMeshImpl_GetNumBytesPerVertex,
+    ID3DXMeshImpl_GetOptions,
+    ID3DXMeshImpl_GetDevice,
+    ID3DXMeshImpl_CloneMeshFVF,
+    ID3DXMeshImpl_CloneMesh,
+    ID3DXMeshImpl_GetVertexBuffer,
+    ID3DXMeshImpl_GetIndexBuffer,
+    ID3DXMeshImpl_LockVertexBuffer,
+    ID3DXMeshImpl_UnlockVertexBuffer,
+    ID3DXMeshImpl_LockIndexBuffer,
+    ID3DXMeshImpl_UnlockIndexBuffer,
+    ID3DXMeshImpl_GetAttributeTable,
+    ID3DXMeshImpl_ConvertPointRepsToAdjacency,
+    ID3DXMeshImpl_ConvertAdjacencyToPointReps,
+    ID3DXMeshImpl_GenerateAdjacency,
+    ID3DXMeshImpl_UpdateSemantics,
+    /*** ID3DXMesh ***/
+    ID3DXMeshImpl_LockAttributeBuffer,
+    ID3DXMeshImpl_UnlockAttributeBuffer,
+    ID3DXMeshImpl_Optimize,
+    ID3DXMeshImpl_OptimizeInplace,
+    ID3DXMeshImpl_SetAttributeTable
+};
+
 /*************************************************************************
  * D3DXBoxBoundProbe
  */
@@ -331,9 +629,88 @@ BOOL WINAPI D3DXSphereBoundProbe(CONST D3DXVECTOR3 
*pcenter, FLOAT radius, CONST
 HRESULT WINAPI D3DXCreateMeshFVF(DWORD numfaces, DWORD numvertices, DWORD 
options, DWORD FVF,
                                  LPDIRECT3DDEVICE9 device, LPD3DXMESH *mesh)
 {
-    FIXME("(%d, %d, %d, %d, %p, %p): stub\n", numfaces, numvertices, options, 
FVF, device, mesh);
+    HRESULT hr;
+    IDirect3DVertexBuffer9 *vertex_buffer;
+    IDirect3DIndexBuffer9 *index_buffer;
+    ID3DXMeshImpl *object;
 
-    return E_NOTIMPL;
+    TRACE("(%d, %d, %d, %d, %p, %p)\n", numfaces, numvertices, options, FVF, 
device, mesh);
+
+    if (numfaces == 0 || numvertices == 0 || FVF == 0 || !device || !mesh)
+    {
+        return D3DERR_INVALIDCALL;
+    }
+
+    if (options != D3DXMESH_MANAGED)
+    {
+        FIXME("Only implemented for options == D3DXMESH_MANAGED.\n");
+        return E_NOTIMPL;
+    }
+
+    /* Create vertex buffer */
+    hr = IDirect3DDevice9_CreateVertexBuffer(device,
+                                             numvertices * sizeof(D3DXVECTOR3) 
* 2,
+                                             0,
+                                             FVF,
+                                             D3DPOOL_MANAGED,
+                                             &vertex_buffer,
+                                             NULL);
+    if (hr != D3D_OK)
+    {
+        if (hr == D3DERR_INVALIDCALL)
+        {
+            ERR("Parameters incorrect in call to 
IDirect3DDevice9_CreateVertexBuffer.\n");
+        }
+        else if (hr != E_OUTOFMEMORY && hr != D3DERR_OUTOFVIDEOMEMORY)
+        {
+            ERR("Unexpected return value %x from 
IDirect3DDevice9_CreateVertexBuffer.\n", hr);
+        }
+        return hr;
+    }
+
+    /* Create index buffer */
+    hr = IDirect3DDevice9_CreateIndexBuffer(device,
+                                            numfaces * sizeof(WORD) * 3,
+                                            0,
+                                            D3DFMT_INDEX16,
+                                            D3DPOOL_MANAGED,
+                                            &index_buffer,
+                                            NULL);
+    if (hr != D3D_OK)
+    {
+        if (hr == D3DERR_INVALIDCALL || hr == D3DXERR_INVALIDDATA)
+        {
+            ERR("Parameters incorrect in call to 
IDirect3DDevice9_CreateVertexBuffer, return value was %x.\n", hr);
+        }
+        else if (hr != E_OUTOFMEMORY && hr != D3DERR_OUTOFVIDEOMEMORY)
+        {
+            ERR("Unexpected return value %x from 
IDirect3DDevice9_CreateVertexBuffer.\n", hr);
+        }
+        return hr;
+    }
+
+    object = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, 
sizeof(ID3DXMeshImpl));
+    if (object == NULL)
+    {
+        *mesh = NULL;
+        return E_OUTOFMEMORY;
+    }
+    object->lpVtbl = &D3DXMesh_Vtbl;
+    object->ref = 1;
+
+    object->numfaces = numfaces;
+    object->numvertices = numvertices;
+    object->options = options;
+    object->FVF = FVF;
+    object->device = device;
+    IDirect3DDevice9_AddRef(device);
+
+    object->vertex_buffer = vertex_buffer;
+    object->index_buffer = index_buffer;
+
+    *mesh = (ID3DXMesh*)object;
+
+    return D3D_OK;
 }
 
 HRESULT WINAPI D3DXCreateBox(LPDIRECT3DDEVICE9 device, FLOAT width, FLOAT 
height,
diff --git a/dlls/d3dx9_36/tests/mesh.c b/dlls/d3dx9_36/tests/mesh.c
index 1e0fb98..0caa28a 100644
--- a/dlls/d3dx9_36/tests/mesh.c
+++ b/dlls/d3dx9_36/tests/mesh.c
@@ -191,7 +191,7 @@ static void compare_mesh(const char *name, ID3DXMesh 
*d3dxmesh, struct mesh *mes
         {
             ok(index_buffer_description.Format == D3DFMT_INDEX16, "Test %s, 
result %x, expected %x (D3DFMT_INDEX16)\n", name, 
index_buffer_description.Format, D3DFMT_INDEX16);
             ok(index_buffer_description.Type == D3DRTYPE_INDEXBUFFER, "Test 
%s, result %x, expected %x (D3DRTYPE_INDEXBUFFER)\n", name, 
index_buffer_description.Type, D3DRTYPE_INDEXBUFFER);
-            ok(index_buffer_description.Usage == 0, "Test %s, result %x, 
expected %x\n", name, index_buffer_description.Usage, 0);
+            todo_wine ok(index_buffer_description.Usage == 0, "Test %s, result 
%x, expected %x\n", name, index_buffer_description.Usage, 0); /* 
WINED3DUSAGE_MASK */
             ok(index_buffer_description.Pool == D3DPOOL_MANAGED, "Test %s, 
result %x, expected %x (D3DPOOL_DEFAULT)\n", name, 
index_buffer_description.Pool, D3DPOOL_DEFAULT);
             expected = number_of_faces * sizeof(WORD) * 3;
             ok(index_buffer_description.Size == expected, "Test %s, result %x, 
expected %x\n", name, index_buffer_description.Size, expected);
@@ -680,10 +680,10 @@ static void D3DXCreateMeshFVFTest(void)
         D3DDECL_END(), };
 
     hr = D3DXCreateMeshFVF(0, 0, 0, 0, NULL, NULL);
-    todo_wine ok(hr == D3DERR_INVALIDCALL, "Got result %x, expected %x 
(D3DERR_INVALIDCALL)\n", hr, D3DERR_INVALIDCALL);
+    ok(hr == D3DERR_INVALIDCALL, "Got result %x, expected %x 
(D3DERR_INVALIDCALL)\n", hr, D3DERR_INVALIDCALL);
 
     hr = D3DXCreateMeshFVF(1, 3, D3DXMESH_MANAGED, D3DFVF_XYZ | D3DFVF_NORMAL, 
NULL, &d3dxmesh);
-    todo_wine ok(hr == D3DERR_INVALIDCALL, "Got result %x, expected %x 
(D3DERR_INVALIDCALL)\n", hr, D3DERR_INVALIDCALL);
+    ok(hr == D3DERR_INVALIDCALL, "Got result %x, expected %x 
(D3DERR_INVALIDCALL)\n", hr, D3DERR_INVALIDCALL);
 
     wnd = CreateWindow("static", "d3dx9_test", 0, 0, 0, 0, 0, NULL, NULL, 
NULL, NULL);
     d3d = Direct3DCreate9(D3D_SDK_VERSION);
@@ -712,10 +712,10 @@ static void D3DXCreateMeshFVFTest(void)
     }
 
     hr = D3DXCreateMeshFVF(0, 3, D3DXMESH_MANAGED, D3DFVF_XYZ | D3DFVF_NORMAL, 
device, &d3dxmesh);
-    todo_wine ok(hr == D3DERR_INVALIDCALL, "Got result %x, expected %x 
(D3DERR_INVALIDCALL)\n", hr, D3DERR_INVALIDCALL);
+    ok(hr == D3DERR_INVALIDCALL, "Got result %x, expected %x 
(D3DERR_INVALIDCALL)\n", hr, D3DERR_INVALIDCALL);
 
     hr = D3DXCreateMeshFVF(1, 0, D3DXMESH_MANAGED, D3DFVF_XYZ | D3DFVF_NORMAL, 
device, &d3dxmesh);
-    todo_wine ok(hr == D3DERR_INVALIDCALL, "Got result %x, expected %x 
(D3DERR_INVALIDCALL)\n", hr, D3DERR_INVALIDCALL);
+    ok(hr == D3DERR_INVALIDCALL, "Got result %x, expected %x 
(D3DERR_INVALIDCALL)\n", hr, D3DERR_INVALIDCALL);
 
     hr = D3DXCreateMeshFVF(1, 3, 0, D3DFVF_XYZ | D3DFVF_NORMAL, device, 
&d3dxmesh);
     todo_wine ok(hr == D3D_OK, "Got result %x, expected %x (D3D_OK)\n", hr, 
D3D_OK);
@@ -726,13 +726,13 @@ static void D3DXCreateMeshFVFTest(void)
     }
 
     hr = D3DXCreateMeshFVF(1, 3, D3DXMESH_MANAGED, 0, device, &d3dxmesh);
-    todo_wine ok(hr == D3DERR_INVALIDCALL, "Got result %x, expected %x 
(D3DERR_INVALIDCALL)\n", hr, D3DERR_INVALIDCALL);
+    ok(hr == D3DERR_INVALIDCALL, "Got result %x, expected %x 
(D3DERR_INVALIDCALL)\n", hr, D3DERR_INVALIDCALL);
 
     hr = D3DXCreateMeshFVF(1, 3, D3DXMESH_MANAGED, D3DFVF_XYZ | D3DFVF_NORMAL, 
device, NULL);
-    todo_wine ok(hr == D3DERR_INVALIDCALL, "Got result %x, expected %x 
(D3DERR_INVALIDCALL)\n", hr, D3DERR_INVALIDCALL);
+    ok(hr == D3DERR_INVALIDCALL, "Got result %x, expected %x 
(D3DERR_INVALIDCALL)\n", hr, D3DERR_INVALIDCALL);
 
     hr = D3DXCreateMeshFVF(1, 3, D3DXMESH_MANAGED, D3DFVF_XYZ | D3DFVF_NORMAL, 
device, &d3dxmesh);
-    todo_wine ok(hr == D3D_OK, "Got result %x, expected 0 (D3D_OK)\n", hr);
+    ok(hr == D3D_OK, "Got result %x, expected 0 (D3D_OK)\n", hr);
 
     if (hr == D3D_OK)
     {
@@ -751,7 +751,7 @@ static void D3DXCreateMeshFVFTest(void)
 
         /* declaration */
         hr = d3dxmesh->lpVtbl->GetDeclaration(d3dxmesh, decl);
-        ok(hr == D3D_OK, "Got result %x, expected 0 (D3D_OK)\n", hr);
+        todo_wine ok(hr == D3D_OK, "Got result %x, expected 0 (D3D_OK)\n", hr);
 
         if (hr == D3D_OK)
         {
-- 
1.7.1

>From eed3628b53ab676eb840ae80d9040390d48d4795 Mon Sep 17 00:00:00 2001
From: Misha Koshelev <[email protected]>
Date: Mon, 26 Jul 2010 20:01:51 -0500
Subject: d3dx9: Complete test for D3DXCreateMeshFVF.
To: wine-patches <[email protected]>
Reply-To: wine-devel <[email protected]>

---
 dlls/d3dx9_36/tests/mesh.c |  130 ++++++++++++++++++++++++++++++++++++++++++++
 1 files changed, 130 insertions(+), 0 deletions(-)

diff --git a/dlls/d3dx9_36/tests/mesh.c b/dlls/d3dx9_36/tests/mesh.c
index 4bbc768..1e0fb98 100644
--- a/dlls/d3dx9_36/tests/mesh.c
+++ b/dlls/d3dx9_36/tests/mesh.c
@@ -664,9 +664,139 @@ static void D3DXIntersectTriTest(void)
 static void D3DXCreateMeshFVFTest(void)
 {
     HRESULT hr;
+    HWND wnd;
+    IDirect3D9 *d3d;
+    IDirect3DDevice9 *device, *test_device;
+    D3DPRESENT_PARAMETERS d3dpp;
+    ID3DXMesh *d3dxmesh;
+    int i, size;
+    D3DVERTEXELEMENT9 decl[MAX_FVF_DECL_SIZE];
+    DWORD fvf, options;
+    struct mesh mesh;
+
+    static const D3DVERTEXELEMENT9 exp[3] = {
+        {0, 0, D3DDECLTYPE_FLOAT3, D3DDECLMETHOD_DEFAULT, 
D3DDECLUSAGE_POSITION, 0},
+        {0, 0xC, D3DDECLTYPE_FLOAT3, D3DDECLMETHOD_DEFAULT, 
D3DDECLUSAGE_NORMAL, 0},
+        D3DDECL_END(), };
 
     hr = D3DXCreateMeshFVF(0, 0, 0, 0, NULL, NULL);
     todo_wine ok(hr == D3DERR_INVALIDCALL, "Got result %x, expected %x 
(D3DERR_INVALIDCALL)\n", hr, D3DERR_INVALIDCALL);
+
+    hr = D3DXCreateMeshFVF(1, 3, D3DXMESH_MANAGED, D3DFVF_XYZ | D3DFVF_NORMAL, 
NULL, &d3dxmesh);
+    todo_wine ok(hr == D3DERR_INVALIDCALL, "Got result %x, expected %x 
(D3DERR_INVALIDCALL)\n", hr, D3DERR_INVALIDCALL);
+
+    wnd = CreateWindow("static", "d3dx9_test", 0, 0, 0, 0, 0, NULL, NULL, 
NULL, NULL);
+    d3d = Direct3DCreate9(D3D_SDK_VERSION);
+    if (!wnd)
+    {
+        skip("Couldn't create application window\n");
+        return;
+    }
+    if (!d3d)
+    {
+        skip("Couldn't create IDirect3D9 object\n");
+        DestroyWindow(wnd);
+        return;
+    }
+
+    ZeroMemory(&d3dpp, sizeof(d3dpp));
+    d3dpp.Windowed = TRUE;
+    d3dpp.SwapEffect = D3DSWAPEFFECT_DISCARD;
+    hr = IDirect3D9_CreateDevice(d3d, D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, wnd, 
D3DCREATE_MIXED_VERTEXPROCESSING, &d3dpp, &device);
+    if (FAILED(hr))
+    {
+        skip("Failed to create IDirect3DDevice9 object %#x\n", hr);
+        IDirect3D9_Release(d3d);
+        DestroyWindow(wnd);
+        return;
+    }
+
+    hr = D3DXCreateMeshFVF(0, 3, D3DXMESH_MANAGED, D3DFVF_XYZ | D3DFVF_NORMAL, 
device, &d3dxmesh);
+    todo_wine ok(hr == D3DERR_INVALIDCALL, "Got result %x, expected %x 
(D3DERR_INVALIDCALL)\n", hr, D3DERR_INVALIDCALL);
+
+    hr = D3DXCreateMeshFVF(1, 0, D3DXMESH_MANAGED, D3DFVF_XYZ | D3DFVF_NORMAL, 
device, &d3dxmesh);
+    todo_wine ok(hr == D3DERR_INVALIDCALL, "Got result %x, expected %x 
(D3DERR_INVALIDCALL)\n", hr, D3DERR_INVALIDCALL);
+
+    hr = D3DXCreateMeshFVF(1, 3, 0, D3DFVF_XYZ | D3DFVF_NORMAL, device, 
&d3dxmesh);
+    todo_wine ok(hr == D3D_OK, "Got result %x, expected %x (D3D_OK)\n", hr, 
D3D_OK);
+
+    if (hr == D3D_OK)
+    {
+        d3dxmesh->lpVtbl->Release(d3dxmesh);
+    }
+
+    hr = D3DXCreateMeshFVF(1, 3, D3DXMESH_MANAGED, 0, device, &d3dxmesh);
+    todo_wine ok(hr == D3DERR_INVALIDCALL, "Got result %x, expected %x 
(D3DERR_INVALIDCALL)\n", hr, D3DERR_INVALIDCALL);
+
+    hr = D3DXCreateMeshFVF(1, 3, D3DXMESH_MANAGED, D3DFVF_XYZ | D3DFVF_NORMAL, 
device, NULL);
+    todo_wine ok(hr == D3DERR_INVALIDCALL, "Got result %x, expected %x 
(D3DERR_INVALIDCALL)\n", hr, D3DERR_INVALIDCALL);
+
+    hr = D3DXCreateMeshFVF(1, 3, D3DXMESH_MANAGED, D3DFVF_XYZ | D3DFVF_NORMAL, 
device, &d3dxmesh);
+    todo_wine ok(hr == D3D_OK, "Got result %x, expected 0 (D3D_OK)\n", hr);
+
+    if (hr == D3D_OK)
+    {
+        /* device */
+        hr = d3dxmesh->lpVtbl->GetDevice(d3dxmesh, NULL);
+        ok(hr == D3DERR_INVALIDCALL, "Got result %x, expected %x 
(D3DERR_INVALIDCALL)\n", hr, D3DERR_INVALIDCALL);
+
+        hr = d3dxmesh->lpVtbl->GetDevice(d3dxmesh, &test_device);
+        ok(hr == D3D_OK, "Got result %x, expected %x (D3D_OK)\n", hr, D3D_OK);
+        ok(test_device == device, "Got result %p, expected %p\n", test_device, 
device);
+
+        if (hr == D3D_OK)
+        {
+            IDirect3DDevice9_Release(device);
+        }
+
+        /* declaration */
+        hr = d3dxmesh->lpVtbl->GetDeclaration(d3dxmesh, decl);
+        ok(hr == D3D_OK, "Got result %x, expected 0 (D3D_OK)\n", hr);
+
+        if (hr == D3D_OK)
+        {
+            size = sizeof(exp) / sizeof(exp[0]);
+            for (i = 0; i < size - 1; i++)
+            {
+                ok(decl[i].Stream == exp[i].Stream, "Returned stream %d, 
expected %d\n", decl[i].Stream, exp[i].Stream);
+                ok(decl[i].Type == exp[i].Type, "Returned type %d, expected 
%d\n", decl[i].Type, exp[i].Type);
+                ok(decl[i].Method == exp[i].Method, "Returned method %d, 
expected %d\n", decl[i].Method, exp[i].Method);
+                ok(decl[i].Usage == exp[i].Usage, "Returned usage %d, expected 
%d\n", decl[i].Usage, exp[i].Usage);
+                ok(decl[i].UsageIndex == exp[i].UsageIndex, "Returned usage 
index %d, expected %d\n", decl[i].UsageIndex, exp[i].UsageIndex);
+                ok(decl[i].Offset == exp[i].Offset, "Returned offset %d, 
expected %d\n", decl[1].Offset, exp[i].Offset);
+            }
+            ok(decl[size-1].Stream == 0xFF, "Returned too long vertex 
declaration\n"); /* end element */
+        }
+
+        /* FVF */
+        fvf = d3dxmesh->lpVtbl->GetFVF(d3dxmesh);
+        ok(fvf == (D3DFVF_XYZ | D3DFVF_NORMAL), "Got result %x, expected %x 
(D3DFVF_XYZ | D3DFVF_NORMAL)\n", fvf, D3DFVF_XYZ | D3DFVF_NORMAL);
+
+        /* options */
+        options = d3dxmesh->lpVtbl->GetOptions(d3dxmesh);
+        ok(options == D3DXMESH_MANAGED, "Got result %x, expected %x 
(D3DXMESH_MANAGED)\n", options, D3DXMESH_MANAGED);
+
+        /* rest */
+        if (!new_mesh(&mesh, 3, 1))
+        {
+            skip("Couldn't create mesh\n");
+        }
+        else
+        {
+            memset(mesh.vertices, 0, mesh.number_of_vertices * 
sizeof(*mesh.vertices));
+            memset(mesh.faces, 0, mesh.number_of_faces * sizeof(*mesh.faces));
+
+            compare_mesh("createmeshfvf", d3dxmesh, &mesh);
+
+            free_mesh(&mesh);
+        }
+
+        d3dxmesh->lpVtbl->Release(d3dxmesh);
+    }
+
+    IDirect3DDevice9_Release(device);
+    IDirect3D9_Release(d3d);
+    DestroyWindow(wnd);
 }
 
 struct sincos_table
-- 
1.7.1

>From 90ab08120326f0a1a8481d3c9dd6063f81d67291 Mon Sep 17 00:00:00 2001
From: Misha Koshelev <[email protected]>
Date: Wed, 7 Jul 2010 09:59:53 -0500
Subject: d3dx9: Add _D3DXMESH enumeration.
To: wine-patches <[email protected]>
Reply-To: wine-devel <[email protected]>

---
 include/d3dx9mesh.h |   26 ++++++++++++++++++++++++++
 1 files changed, 26 insertions(+), 0 deletions(-)

diff --git a/include/d3dx9mesh.h b/include/d3dx9mesh.h
index 13629aa..b3858b4 100644
--- a/include/d3dx9mesh.h
+++ b/include/d3dx9mesh.h
@@ -30,6 +30,32 @@ enum _MAX_FVF_DECL_SIZE
     MAX_FVF_DECL_SIZE = MAXD3DDECLLENGTH + 1
 };
 
+enum _D3DXMESH
+{
+    D3DXMESH_32BIT                 = 0x001,
+    D3DXMESH_DONOTCLIP             = 0x002,
+    D3DXMESH_POINTS                = 0x004,
+    D3DXMESH_RTPATCHES             = 0x008,
+    D3DXMESH_NPATCHES              = 0x4000,
+    D3DXMESH_VB_SYSTEMMEM          = 0x010,
+    D3DXMESH_VB_MANAGED            = 0x020,
+    D3DXMESH_VB_WRITEONLY          = 0x040,
+    D3DXMESH_VB_DYNAMIC            = 0x080,
+    D3DXMESH_VB_SOFTWAREPROCESSING = 0x8000,
+    D3DXMESH_IB_SYSTEMMEM          = 0x100,
+    D3DXMESH_IB_MANAGED            = 0x200,
+    D3DXMESH_IB_WRITEONLY          = 0x400,
+    D3DXMESH_IB_DYNAMIC            = 0x800,
+    D3DXMESH_IB_SOFTWAREPROCESSING = 0x10000,
+    D3DXMESH_VB_SHARE              = 0x1000,
+    D3DXMESH_USEHWONLY             = 0x2000,
+    D3DXMESH_SYSTEMMEM             = 0x110,
+    D3DXMESH_MANAGED               = 0x220,
+    D3DXMESH_WRITEONLY             = 0x440,
+    D3DXMESH_DYNAMIC               = 0x880,
+    D3DXMESH_SOFTWAREPROCESSING    = 0x18000
+};
+
 typedef struct ID3DXBaseMesh* LPD3DXBASEMESH;
 typedef struct ID3DXMesh* LPD3DXMESH;
 
-- 
1.7.1

>From d0a2b14b33bb572c99005d8510ef0bd1b581d455 Mon Sep 17 00:00:00 2001
From: Misha Koshelev <[email protected]>
Date: Mon, 26 Jul 2010 17:43:48 -0500
Subject: d3dx9: Add stub and basic test for D3DXCreateMeshFVF.
To: wine-patches <[email protected]>
Reply-To: wine-devel <[email protected]>

---
 dlls/d3dx9_36/d3dx9_36.spec |    2 +-
 dlls/d3dx9_36/mesh.c        |    8 ++++++++
 dlls/d3dx9_36/tests/mesh.c  |    9 +++++++++
 include/d3dx9mesh.h         |    1 +
 4 files changed, 19 insertions(+), 1 deletions(-)

diff --git a/dlls/d3dx9_36/d3dx9_36.spec b/dlls/d3dx9_36/d3dx9_36.spec
index 622a03c..ca925d9 100644
--- a/dlls/d3dx9_36/d3dx9_36.spec
+++ b/dlls/d3dx9_36/d3dx9_36.spec
@@ -72,7 +72,7 @@
 @ stdcall D3DXCreateLine(ptr ptr)
 @ stdcall D3DXCreateMatrixStack(long ptr)
 @ stub D3DXCreateMesh
-@ stub D3DXCreateMeshFVF
+@ stdcall D3DXCreateMeshFVF(long long long long ptr ptr)
 @ stub D3DXCreateNPatchMesh
 @ stub D3DXCreatePMeshFromStream
 @ stub D3DXCreatePatchMesh
diff --git a/dlls/d3dx9_36/mesh.c b/dlls/d3dx9_36/mesh.c
index bfa1c23..5ff257c 100644
--- a/dlls/d3dx9_36/mesh.c
+++ b/dlls/d3dx9_36/mesh.c
@@ -328,6 +328,14 @@ BOOL WINAPI D3DXSphereBoundProbe(CONST D3DXVECTOR3 
*pcenter, FLOAT radius, CONST
     return TRUE;
 }
 
+HRESULT WINAPI D3DXCreateMeshFVF(DWORD numfaces, DWORD numvertices, DWORD 
options, DWORD FVF,
+                                 LPDIRECT3DDEVICE9 device, LPD3DXMESH *mesh)
+{
+    FIXME("(%d, %d, %d, %d, %p, %p): stub\n", numfaces, numvertices, options, 
FVF, device, mesh);
+
+    return E_NOTIMPL;
+}
+
 HRESULT WINAPI D3DXCreateBox(LPDIRECT3DDEVICE9 device, FLOAT width, FLOAT 
height,
                              FLOAT depth, LPD3DXMESH* mesh, LPD3DXBUFFER* 
adjacency)
 {
diff --git a/dlls/d3dx9_36/tests/mesh.c b/dlls/d3dx9_36/tests/mesh.c
index 940e39e..4bbc768 100644
--- a/dlls/d3dx9_36/tests/mesh.c
+++ b/dlls/d3dx9_36/tests/mesh.c
@@ -661,6 +661,14 @@ static void D3DXIntersectTriTest(void)
     ok( got_res == exp_res, "Expected result = %d, got %d\n",exp_res,got_res);
 }
 
+static void D3DXCreateMeshFVFTest(void)
+{
+    HRESULT hr;
+
+    hr = D3DXCreateMeshFVF(0, 0, 0, 0, NULL, NULL);
+    todo_wine ok(hr == D3DERR_INVALIDCALL, "Got result %x, expected %x 
(D3DERR_INVALIDCALL)\n", hr, D3DERR_INVALIDCALL);
+}
+
 struct sincos_table
 {
     float *sin;
@@ -1041,6 +1049,7 @@ START_TEST(mesh)
     D3DXDeclaratorFromFVFTest();
     D3DXGetFVFVertexSizeTest();
     D3DXIntersectTriTest();
+    D3DXCreateMeshFVFTest();
     D3DXCreateSphereTest();
     test_get_decl_vertex_size();
 }
diff --git a/include/d3dx9mesh.h b/include/d3dx9mesh.h
index 5773d24..13629aa 100644
--- a/include/d3dx9mesh.h
+++ b/include/d3dx9mesh.h
@@ -124,6 +124,7 @@ DECLARE_INTERFACE_(ID3DXMesh, ID3DXBaseMesh)
 extern "C" {
 #endif
 
+HRESULT WINAPI D3DXCreateMeshFVF(DWORD, DWORD, DWORD, DWORD, 
LPDIRECT3DDEVICE9, LPD3DXMESH *);
 HRESULT WINAPI D3DXCreateBuffer(DWORD, LPD3DXBUFFER*);
 UINT    WINAPI D3DXGetDeclVertexSize(const D3DVERTEXELEMENT9 *decl, DWORD 
stream_idx);
 UINT    WINAPI D3DXGetFVFVertexSize(DWORD);
-- 
1.7.1

>From 10f81a0f0492442f08038b82bb578efbae16d47e Mon Sep 17 00:00:00 2001
From: Misha Koshelev <[email protected]>
Date: Mon, 26 Jul 2010 17:05:00 -0500
Subject: d3dx9: Complete test for D3DXCreateSphere.
To: wine-patches <[email protected]>
Reply-To: wine-devel <[email protected]>

---
 dlls/d3dx9_36/tests/mesh.c |  429 +++++++++++++++++++++++++++++++++++++++++++-
 1 files changed, 425 insertions(+), 4 deletions(-)

diff --git a/dlls/d3dx9_36/tests/mesh.c b/dlls/d3dx9_36/tests/mesh.c
index f3a629d..940e39e 100644
--- a/dlls/d3dx9_36/tests/mesh.c
+++ b/dlls/d3dx9_36/tests/mesh.c
@@ -18,6 +18,7 @@
  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
  */
 
+#include <stdio.h>
 #include "wine/test.h"
 #include "d3dx9.h"
 
@@ -37,6 +38,187 @@ static BOOL compare_vec3(D3DXVECTOR3 u, D3DXVECTOR3 v)
     return ( compare(u.x, v.x) && compare(u.y, v.y) && compare(u.z, v.z) );
 }
 
+struct vertex
+{
+    D3DXVECTOR3 position;
+    D3DXVECTOR3 normal;
+};
+
+typedef WORD face[3];
+
+static BOOL compare_face(face a, face b)
+{
+    return (a[0]==b[0] && a[1] == b[1] && a[2] == b[2]);
+}
+
+struct mesh
+{
+    DWORD number_of_vertices;
+    struct vertex *vertices;
+
+    DWORD number_of_faces;
+    face *faces;
+};
+
+static void free_mesh(struct mesh *mesh)
+{
+    HeapFree(GetProcessHeap(), 0, mesh->faces);
+    HeapFree(GetProcessHeap(), 0, mesh->vertices);
+}
+
+static BOOL new_mesh(struct mesh *mesh, DWORD number_of_vertices, DWORD 
number_of_faces)
+{
+    int i;
+
+    /* vertices */
+    mesh->vertices = HeapAlloc(GetProcessHeap(), 0, number_of_vertices * 
sizeof(*mesh->vertices));
+    if (!mesh->vertices)
+    {
+        return FALSE;
+    }
+    mesh->number_of_vertices = number_of_vertices;
+
+    /* faces */
+    mesh->faces = HeapAlloc(GetProcessHeap(), 0, number_of_faces * 
sizeof(*mesh->faces));
+    if (!mesh->faces)
+    {
+        HeapFree(GetProcessHeap(), 0, mesh->vertices);
+        return FALSE;
+    }
+    mesh->number_of_faces = number_of_faces;
+
+    /* fill with nonsense data to make sure no comparison succeed by chance */
+    for (i = 0; i < number_of_vertices; i++)
+    {
+        mesh->vertices[i].position.x = NAN; mesh->vertices[i].position.y = 
NAN; mesh->vertices[i].position.z = NAN;
+        mesh->vertices[i].normal.x = NAN; mesh->vertices[i].normal.y = NAN; 
mesh->vertices[i].normal.z = NAN;
+    }
+    for (i = 0; i < number_of_faces; i++)
+    {
+        mesh->faces[i][0] = -1; mesh->faces[i][1] = -1; mesh->faces[i][2] = -1;
+    }
+
+    /* success */
+    return TRUE;
+}
+
+static void compare_mesh(const char *name, ID3DXMesh *d3dxmesh, struct mesh 
*mesh)
+{
+    HRESULT hr;
+    DWORD number_of_vertices, number_of_faces;
+    IDirect3DVertexBuffer9 *vertex_buffer;
+    IDirect3DIndexBuffer9 *index_buffer;
+    D3DVERTEXBUFFER_DESC vertex_buffer_description;
+    D3DINDEXBUFFER_DESC index_buffer_description;
+    struct vertex *vertices;
+    face *faces;
+    int expected, i;
+
+    number_of_vertices = d3dxmesh->lpVtbl->GetNumVertices(d3dxmesh);
+    ok(number_of_vertices == mesh->number_of_vertices, "Test %s, result %u, 
expected %d\n", name, number_of_vertices, mesh->number_of_vertices);
+
+    number_of_faces = d3dxmesh->lpVtbl->GetNumFaces(d3dxmesh);
+    ok(number_of_faces == mesh->number_of_faces, "Test %s, result %u, expected 
%d\n", name, number_of_faces, mesh->number_of_faces);
+
+    /* vertex buffer */
+    hr = d3dxmesh->lpVtbl->GetVertexBuffer(d3dxmesh, &vertex_buffer);
+    ok(hr == D3D_OK, "Test %s, result %x, expected 0 (D3D_OK)\n", name, hr);
+
+    if (hr != D3D_OK)
+    {
+        skip("Couldn't get vertex buffer\n");
+    }
+    else
+    {
+        hr = IDirect3DVertexBuffer9_GetDesc(vertex_buffer, 
&vertex_buffer_description);
+        ok(hr == D3D_OK, "Test %s, result %x, expected 0 (D3D_OK)\n", name, 
hr);
+
+        if (hr != D3D_OK)
+        {
+            skip("Couldn't get vertex buffer description\n");
+        }
+        else
+        {
+            ok(vertex_buffer_description.Format == D3DFMT_VERTEXDATA, "Test 
%s, result %x, expected %x (D3DFMT_VERTEXDATA)\n", name, 
vertex_buffer_description.Format, D3DFMT_VERTEXDATA);
+            ok(vertex_buffer_description.Type == D3DRTYPE_VERTEXBUFFER, "Test 
%s, result %x, expected %x (D3DRTYPE_VERTEXBUFFER)\n", name, 
vertex_buffer_description.Type, D3DRTYPE_VERTEXBUFFER);
+            ok(vertex_buffer_description.Usage == 0, "Test %s, result %x, 
expected %x\n", name, vertex_buffer_description.Usage, 0);
+            ok(vertex_buffer_description.Pool == D3DPOOL_MANAGED, "Test %s, 
result %x, expected %x (D3DPOOL_DEFAULT)\n", name, 
vertex_buffer_description.Pool, D3DPOOL_DEFAULT);
+            expected = number_of_vertices * sizeof(D3DXVECTOR3) * 2;
+            ok(vertex_buffer_description.Size == expected, "Test %s, result 
%x, expected %x\n", name, vertex_buffer_description.Size, expected);
+            ok(vertex_buffer_description.FVF == (D3DFVF_XYZ | D3DFVF_NORMAL), 
"Test %s, result %x, expected %x (D3DFVF_XYZ | D3DFVF_NORMAL)\n", name, 
vertex_buffer_description.FVF, D3DFVF_XYZ | D3DFVF_NORMAL);
+        }
+
+        /* specify offset and size to avoid potential overruns */
+        hr = IDirect3DVertexBuffer9_Lock(vertex_buffer, 0, number_of_vertices 
* sizeof(D3DXVECTOR3) * 2, (LPVOID *)&vertices, D3DLOCK_DISCARD);
+        ok(hr == D3D_OK, "Test %s, result %x, expected 0 (D3D_OK)\n", name, 
hr);
+
+        if (hr != D3D_OK)
+        {
+            skip("Couldn't lock vertex buffer\n");
+        }
+        else
+        {
+            for (i = 0; i < number_of_vertices; i++)
+            {
+                ok(compare_vec3(vertices[i].position, 
mesh->vertices[i].position), "Test %s, vertex position %d, result (%g, %g, %g), 
expected (%g, %g, %g)\n", name, i, vertices[i].position.x, 
vertices[i].position.y, vertices[i].position.z, mesh->vertices[i].position.x, 
mesh->vertices[i].position.y, mesh->vertices[i].position.z);
+                ok(compare_vec3(vertices[i].normal, mesh->vertices[i].normal), 
"Test %s, vertex normal %d, result (%g, %g, %g), expected (%g, %g, %g)\n", 
name, i, vertices[i].normal.x, vertices[i].normal.y, vertices[i].normal.z, 
mesh->vertices[i].normal.x, mesh->vertices[i].normal.y, 
mesh->vertices[i].normal.z);
+            }
+
+            IDirect3DVertexBuffer9_Unlock(vertex_buffer);
+        }
+
+        IDirect3DVertexBuffer9_Release(vertex_buffer);
+    }
+
+    /* index buffer */
+    hr = d3dxmesh->lpVtbl->GetIndexBuffer(d3dxmesh, &index_buffer);
+    ok(hr == D3D_OK, "Test %s, result %x, expected 0 (D3D_OK)\n", name, hr);
+
+    if (!index_buffer)
+    {
+        skip("Couldn't get index buffer\n");
+    }
+    else
+    {
+        hr = IDirect3DIndexBuffer9_GetDesc(index_buffer, 
&index_buffer_description);
+        ok(hr == D3D_OK, "Test %s, result %x, expected 0 (D3D_OK)\n", name, 
hr);
+
+        if (hr != D3D_OK)
+        {
+            skip("Couldn't get index buffer description\n");
+        }
+        else
+        {
+            ok(index_buffer_description.Format == D3DFMT_INDEX16, "Test %s, 
result %x, expected %x (D3DFMT_INDEX16)\n", name, 
index_buffer_description.Format, D3DFMT_INDEX16);
+            ok(index_buffer_description.Type == D3DRTYPE_INDEXBUFFER, "Test 
%s, result %x, expected %x (D3DRTYPE_INDEXBUFFER)\n", name, 
index_buffer_description.Type, D3DRTYPE_INDEXBUFFER);
+            ok(index_buffer_description.Usage == 0, "Test %s, result %x, 
expected %x\n", name, index_buffer_description.Usage, 0);
+            ok(index_buffer_description.Pool == D3DPOOL_MANAGED, "Test %s, 
result %x, expected %x (D3DPOOL_DEFAULT)\n", name, 
index_buffer_description.Pool, D3DPOOL_DEFAULT);
+            expected = number_of_faces * sizeof(WORD) * 3;
+            ok(index_buffer_description.Size == expected, "Test %s, result %x, 
expected %x\n", name, index_buffer_description.Size, expected);
+        }
+
+        /* specify offset and size to avoid potential overruns */
+        hr = IDirect3DIndexBuffer9_Lock(index_buffer, 0, number_of_faces * 
sizeof(WORD) * 3, (LPVOID *)&faces, D3DLOCK_DISCARD);
+        ok(hr == D3D_OK, "Test %s, result %x, expected 0 (D3D_OK)\n", name, 
hr);
+
+        if (hr != D3D_OK)
+        {
+            skip("Couldn't lock index buffer\n");
+        }
+        else
+        {
+            for (i = 0; i < number_of_faces; i++)
+            {
+                ok(compare_face(faces[i], mesh->faces[i]), "Test %s, face %d, 
result (%u, %u, %u), expected (%u, %u, %u)\n", name, i, faces[i][0], 
faces[i][1], faces[i][2], mesh->faces[i][0], mesh->faces[i][1], 
mesh->faces[i][2]);
+            }
+
+            IDirect3DIndexBuffer9_Unlock(index_buffer);
+        }
+
+        IDirect3DIndexBuffer9_Release(index_buffer);
+    }
+}
+
 static void D3DXBoundProbeTest(void)
 {
     BOOL result;
@@ -479,6 +661,234 @@ static void D3DXIntersectTriTest(void)
     ok( got_res == exp_res, "Expected result = %d, got %d\n",exp_res,got_res);
 }
 
+struct sincos_table
+{
+    float *sin;
+    float *cos;
+};
+
+static void free_sincos_table(struct sincos_table *sincos_table)
+{
+    HeapFree(GetProcessHeap(), 0, sincos_table->cos);
+    HeapFree(GetProcessHeap(), 0, sincos_table->sin);
+}
+
+/* pre compute sine and cosine tables; caller must free */
+static BOOL compute_sincos_table(struct sincos_table *sincos_table, float 
angle_start, float angle_step, int n)
+{
+    float angle;
+    int i;
+
+    /* allocate */
+    sincos_table->sin = HeapAlloc(GetProcessHeap(), 0, n * 
sizeof(*sincos_table->sin));
+    if (!sincos_table->sin)
+    {
+        return FALSE;
+    }
+    sincos_table->cos = HeapAlloc(GetProcessHeap(), 0, n * 
sizeof(*sincos_table->cos));
+    if (!sincos_table->cos)
+    {
+        HeapFree(GetProcessHeap(), 0, sincos_table->sin);
+        return FALSE;
+    }
+
+    /* fill tables */
+    angle = angle_start;
+    for (i = 0; i < n; i++)
+    {
+        sincos_table->sin[i] = sin(angle);
+        sincos_table->cos[i] = cos(angle);
+        angle += angle_step;
+    }
+
+    /* success */
+    return TRUE;
+}
+
+/* index of given sphere vertex */
+static WORD sphere_vertex(UINT slices, int slice, int stack)
+{
+    return stack*slices+slice+1;
+}
+
+/* slices = subdivisions along xy plane, stacks = subdivisions along z axis */
+static BOOL compute_sphere(struct mesh *mesh, FLOAT radius, UINT slices, UINT 
stacks)
+{
+    float theta_step, theta_start;
+    struct sincos_table theta;
+    float phi_step, phi_start;
+    struct sincos_table phi;
+    DWORD number_of_vertices, number_of_faces;
+    DWORD vertex, face;
+    int slice, stack;
+
+    /* theta = angle on xy plane wrt x axis */
+    theta_step = M_PI / stacks;
+    theta_start = theta_step;
+
+    /* phi = angle on xz plane wrt z axis */
+    phi_step = -2 * M_PI / slices;
+    phi_start = M_PI / 2;
+
+    /* create lookup tables */
+    if (!compute_sincos_table(&theta, theta_start, theta_step, stacks))
+    {
+        return FALSE;
+    }
+    if (!compute_sincos_table(&phi, phi_start, phi_step, slices))
+    {
+        free_sincos_table(&theta);
+        return FALSE;
+    }
+
+    /* top and bottom centers, slices vertices for stacks - 1 circles */
+    number_of_vertices = 2 + slices * (stacks-1);
+
+    /* slices faces for top and bottom, 2 * slices faces for each stack not
+     * counting top and bottom (z axis) */
+    number_of_faces = 2 * slices + (stacks - 2) * (2 * slices);
+
+    /* create mesh */
+    if (!new_mesh(mesh, number_of_vertices, number_of_faces))
+    {
+        free_sincos_table(&phi);
+        free_sincos_table(&theta);
+        return FALSE;
+    }
+
+    /* initialize */
+    vertex = 0;
+    face = 0;
+    stack = 0;
+
+    /* first vertex and normal */
+    mesh->vertices[vertex].normal.x = 0.0f;
+    mesh->vertices[vertex].normal.y = 0.0f;
+    mesh->vertices[vertex].normal.z = 1.0f;
+    mesh->vertices[vertex].position.x = 0.0f;
+    mesh->vertices[vertex].position.y = 0.0f;
+    mesh->vertices[vertex].position.z = radius;
+    vertex++;
+
+    /* middle */
+    for (stack = 0; stack < stacks - 1; stack++)
+    {
+        for (slice = 0; slice < slices; slice++)
+        {
+            mesh->vertices[vertex].normal.x = theta.sin[stack] * 
phi.cos[slice];
+            mesh->vertices[vertex].normal.y = theta.sin[stack] * 
phi.sin[slice];
+            mesh->vertices[vertex].normal.z = theta.cos[stack];
+            mesh->vertices[vertex].position.x = radius * theta.sin[stack] * 
phi.cos[slice];
+            mesh->vertices[vertex].position.y = radius * theta.sin[stack] * 
phi.sin[slice];
+            mesh->vertices[vertex].position.z = radius * theta.cos[stack];
+            vertex++;
+
+            if (slice > 0)
+            {
+                if (stack == 0)
+                {
+                    /* top stack is triangle fan */
+                    mesh->faces[face][0] = 0;
+                    mesh->faces[face][1] = slice + 1;
+                    mesh->faces[face][2] = slice;
+                    face++;
+                }
+                else
+                {
+                    /* stacks in between top and bottom are quad strips */
+                    mesh->faces[face][0] = sphere_vertex(slices, slice-1, 
stack-1);
+                    mesh->faces[face][1] = sphere_vertex(slices, slice, 
stack-1);
+                    mesh->faces[face][2] = sphere_vertex(slices, slice-1, 
stack);
+                    face++;
+
+                    mesh->faces[face][0] = sphere_vertex(slices, slice, 
stack-1);
+                    mesh->faces[face][1] = sphere_vertex(slices, slice, stack);
+                    mesh->faces[face][2] = sphere_vertex(slices, slice-1, 
stack);
+                    face++;
+                }
+            }
+        }
+
+        if (stack == 0)
+        {
+            mesh->faces[face][0] = 0;
+            mesh->faces[face][1] = 1;
+            mesh->faces[face][2] = slice;
+            face++;
+        }
+        else
+        {
+            mesh->faces[face][0] = sphere_vertex(slices, slice-1, stack-1);
+            mesh->faces[face][1] = sphere_vertex(slices, 0, stack-1);
+            mesh->faces[face][2] = sphere_vertex(slices, slice-1, stack);
+            face++;
+
+            mesh->faces[face][0] = sphere_vertex(slices, 0, stack-1);
+            mesh->faces[face][1] = sphere_vertex(slices, 0, stack);
+            mesh->faces[face][2] = sphere_vertex(slices, slice-1, stack);
+            face++;
+        }
+    }
+
+    /* last vertex and normal */
+    mesh->vertices[vertex].position.x = 0.0f;
+    mesh->vertices[vertex].position.y = 0.0f;
+    mesh->vertices[vertex].position.z = -radius;
+    mesh->vertices[vertex].normal.x = 0.0f;
+    mesh->vertices[vertex].normal.y = 0.0f;
+    mesh->vertices[vertex].normal.z = -1.0f;
+
+    /* bottom stack is triangle fan */
+    for (slice = 1; slice < slices; slice++)
+    {
+        mesh->faces[face][0] = sphere_vertex(slices, slice-1, stack-1);
+        mesh->faces[face][1] = sphere_vertex(slices, slice, stack-1);
+        mesh->faces[face][2] = vertex;
+        face++;
+    }
+
+    mesh->faces[face][0] = sphere_vertex(slices, slice-1, stack-1);
+    mesh->faces[face][1] = sphere_vertex(slices, 0, stack-1);
+    mesh->faces[face][2] = vertex;
+
+    /* free lookup tables */
+    free_sincos_table(&phi);
+    free_sincos_table(&theta);
+
+    /* success */
+    return TRUE;
+}
+
+static void test_sphere(IDirect3DDevice9 *device, FLOAT radius, UINT slices, 
UINT stacks)
+{
+    HRESULT hr;
+    ID3DXMesh *sphere;
+    struct mesh mesh;
+    char name[256];
+
+    hr = D3DXCreateSphere(device, radius, slices, stacks, &sphere, NULL);
+    todo_wine ok(hr == D3D_OK, "Got result %x, expected 0 (D3D_OK)\n", hr);
+    if (hr != D3D_OK)
+    {
+        skip("Couldn't create sphere\n");
+        return;
+    }
+
+    if (!compute_sphere(&mesh, radius, slices, stacks))
+    {
+        skip("Couldn't create mesh\n");
+        sphere->lpVtbl->Release(sphere);
+        return;
+    }
+
+    sprintf(name, "sphere (%g, %u, %u)", radius, slices, stacks);
+    compare_mesh(name, sphere, &mesh);
+
+    free_mesh(&mesh);
+
+    sphere->lpVtbl->Release(sphere);
+}
+
 static void D3DXCreateSphereTest(void)
 {
     HRESULT hr;
@@ -529,11 +939,22 @@ static void D3DXCreateSphereTest(void)
     hr = D3DXCreateSphere(device, 1.0f, 1, 1, &sphere, NULL);
     todo_wine ok( hr == D3DERR_INVALIDCALL, "Got result %x, expected %x 
(D3DERR_INVALIDCALL)\n",hr,D3DERR_INVALIDCALL);
 
-    hr = D3DXCreateSphere(device, 1.0f, 2, 2, &sphere, NULL);
-    todo_wine ok( hr == D3D_OK, "Got result %x, expected 0 (D3D_OK)\n",hr);
+    hr = D3DXCreateSphere(device, 1.0f, 2, 1, &sphere, NULL);
+    todo_wine ok(hr == D3DERR_INVALIDCALL, "Got result %x, expected %x 
(D3DERR_INVALIDCALL)\n", hr, D3DERR_INVALIDCALL);
 
-    if (sphere)
-        sphere->lpVtbl->Release(sphere);
+    hr = D3DXCreateSphere(device, 1.0f, 1, 2, &sphere, NULL);
+    todo_wine ok(hr == D3DERR_INVALIDCALL, "Got result %x, expected %x 
(D3DERR_INVALIDCALL)\n", hr, D3DERR_INVALIDCALL);
+
+    hr = D3DXCreateSphere(device, -0.1f, 1, 2, &sphere, NULL);
+    todo_wine ok(hr == D3DERR_INVALIDCALL, "Got result %x, expected %x 
(D3DERR_INVALIDCALL)\n", hr, D3DERR_INVALIDCALL);
+
+    test_sphere(device, 0.0f, 2, 2);
+    test_sphere(device, 1.0f, 2, 2);
+    test_sphere(device, 1.0f, 3, 2);
+    test_sphere(device, 1.0f, 4, 4);
+    test_sphere(device, 1.0f, 3, 4);
+    test_sphere(device, 5.0f, 6, 7);
+    test_sphere(device, 10.0f, 11, 12);
 
     IDirect3DDevice9_Release(device);
     IDirect3D9_Release(d3d);
-- 
1.7.1



Reply via email to